Skip to content

Commit

Permalink
Merge pull request #332 from pendulum-chain/fix-offramp-broken
Browse files Browse the repository at this point in the history
Fix offramp process getting stuck after entering details on anchor page
  • Loading branch information
ebma authored Dec 18, 2024
2 parents 418f5e1 + f2f06ed commit fd5a5d9
Show file tree
Hide file tree
Showing 6 changed files with 65 additions and 20 deletions.
1 change: 1 addition & 0 deletions src/constants/constants.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ import { config } from '../config';

export const HORIZON_URL = 'https://horizon.stellar.org';
export const STELLAR_BASE_FEE = '1000000';
export const STELLAR_EPHEMERAL_STARTING_BALANCE_UNITS = '2.5'; // Amount to send to the new stellar ephemeral account created
export const PENDULUM_WSS = 'wss://rpc-pendulum.prd.pendulumchain.tech';
export const ASSETHUB_WSS = 'wss://polkadot-asset-hub-rpc.polkadot.io';
export const WALLETCONNECT_ASSETHUB_ID = 'polkadot:68d56f15f85d3136970ec16946040bc1';
Expand Down
20 changes: 10 additions & 10 deletions src/main.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -41,17 +41,17 @@ render(
<BrowserRouter>
<WagmiProvider config={wagmiConfig}>
<QueryClientProvider client={queryClient}>
<PolkadotNodeProvider>
<PolkadotWalletStateProvider>
<EventsProvider>
<SiweProvider>
<NetworkProvider>
<NetworkProvider>
<PolkadotNodeProvider>
<PolkadotWalletStateProvider>
<EventsProvider>
<SiweProvider>
<App />
</NetworkProvider>
</SiweProvider>
</EventsProvider>
</PolkadotWalletStateProvider>
</PolkadotNodeProvider>
</SiweProvider>
</EventsProvider>
</PolkadotWalletStateProvider>
</PolkadotNodeProvider>
</NetworkProvider>
</QueryClientProvider>
</WagmiProvider>
</BrowserRouter>
Expand Down
2 changes: 1 addition & 1 deletion src/pages/swap/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -433,7 +433,7 @@ export const SwapPage = () => {
) : (
<SwapSubmitButton
text={isInitiating ? 'Confirming' : offrampingStarted ? 'Processing Details' : 'Confirm'}
disabled={Boolean(getCurrentErrorMessage()) || !inputAmountIsStable}
disabled={Boolean(getCurrentErrorMessage()) || !inputAmountIsStable || initializeFailed}
pending={isInitiating || offrampingStarted || offrampingState !== undefined}
/>
)}
Expand Down
2 changes: 2 additions & 0 deletions src/services/anchor/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -282,6 +282,8 @@ export async function sep24Second(
const { sep24Url } = tomlValues;

if (config.test.mockSep24) {
// sleep 10 secs
await new Promise((resolve) => setTimeout(resolve, 10000));
return {
amount: sessionParams.offrampAmount,
memo: 'MYK1722323689',
Expand Down
1 change: 1 addition & 0 deletions src/services/phases/signedTransactions.ts
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,7 @@ export async function prepareTransactions(state: OfframpingState, context: Execu
const stellarFundingAccountId = (await fetchSigningServiceAccountId()).stellar.public;
const stellarEphemeralKeypair = Keypair.fromSecret(stellarEphemeralSecret);
const stellarEphemeralPublicKey = stellarEphemeralKeypair.publicKey();

const { offrampingTransaction, mergeAccountTransaction } = await setUpAccountAndOperations(
stellarFundingAccountId,
stellarEphemeralKeypair,
Expand Down
59 changes: 50 additions & 9 deletions src/services/phases/stellar/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -14,14 +14,51 @@ import {
} from 'stellar-sdk';

import { OUTPUT_TOKEN_CONFIG, OutputTokenDetails, OutputTokenType } from '../../../constants/tokenConfig';
import { HORIZON_URL, SIGNING_SERVICE_URL, STELLAR_BASE_FEE } from '../../../constants/constants';
import {
HORIZON_URL,
SIGNING_SERVICE_URL,
STELLAR_BASE_FEE,
STELLAR_EPHEMERAL_STARTING_BALANCE_UNITS,
} from '../../../constants/constants';
import { fetchSigningServiceAccountId } from '../../signingService';
import { OfframpingState } from '../../offrampingFlow';
import { SepResult } from '../../anchor';

const horizonServer = new Horizon.Server(HORIZON_URL);
const NETWORK_PASSPHRASE = Networks.PUBLIC;

// This is a helper function to add a timeout to the loadAccount function and retry it.
// The Horizon.Server class does not allow defining a custom timeout for network queries.
async function loadAccountWithRetry(
ephemeralAccountId: string,
retries = 3,
timeout = 15000,
): Promise<Horizon.AccountResponse | null> {
let lastError: Error | null = null;

const loadAccountWithTimeout = (accountId: string, timeout: number): Promise<Horizon.AccountResponse> => {
return Promise.race([
horizonServer.loadAccount(accountId),
new Promise<never>((_, reject) => setTimeout(() => reject(new Error('Timeout')), timeout)),
]);
};

for (let i = 0; i < retries; i++) {
try {
return await loadAccountWithTimeout(ephemeralAccountId, timeout);
} catch (err: any) {
if (err?.toString().includes('NotFoundError')) {
// The account does not exist
return null;
}
console.log(`Attempt ${i + 1} to load account ${ephemeralAccountId} failed: ${err}`);
lastError = err;
}
}

throw new Error(`Failed to load account ${ephemeralAccountId} after ${retries} attempts: ` + lastError?.toString());
}

export interface StellarOperations {
offrampingTransaction: Transaction;
mergeAccountTransaction: Transaction;
Expand Down Expand Up @@ -58,10 +95,11 @@ async function isEphemeralCreated(stellarEphemeralSecret: string): Promise<boole
const ephemeralAccountId = ephemeralKeypair.publicKey();

try {
await horizonServer.loadAccount(ephemeralAccountId);
return true;
} catch {
return false;
const accountOrNull = await loadAccountWithRetry(ephemeralAccountId);
// If result is null, the account does not exist yet meaning
return accountOrNull !== null;
} catch (error) {
throw new Error('Error while checking if ephemeral account exists: ' + error?.toString());
}
}

Expand All @@ -71,7 +109,11 @@ export async function setUpAccountAndOperations(
sepResult: SepResult,
outputTokenType: OutputTokenType,
): Promise<StellarOperations> {
const ephemeralAccount = await horizonServer.loadAccount(ephemeralKeypair.publicKey());
const ephemeralAccount = await loadAccountWithRetry(ephemeralKeypair.publicKey());
if (!ephemeralAccount) {
throw new Error('Ephemeral account does not exist on network.');
}

const { offrampingTransaction, mergeAccountTransaction } = await createOfframpAndMergeTransaction(
fundingAccountId,
sepResult,
Expand Down Expand Up @@ -131,7 +173,7 @@ async function setupStellarAccount(
.addOperation(
Operation.createAccount({
destination: ephemeralAccountId,
startingBalance: '2.5',
startingBalance: STELLAR_EPHEMERAL_STARTING_BALANCE_UNITS,
}),
)
.addOperation(
Expand Down Expand Up @@ -163,8 +205,7 @@ async function setupStellarAccount(
await horizonServer.submitTransaction(createAccountTransaction);
} catch (error: unknown) {
const horizonError = error as { response: { data: { extras: any } } };
console.log(horizonError.response.data.extras);
console.error(horizonError.response.data.extras.toString());
console.error('Transaction submission to horizon failed', horizonError.toString());
throw new Error('Could not submit the account creation transaction');
}
}
Expand Down

0 comments on commit fd5a5d9

Please sign in to comment.