diff --git a/src/constants/constants.ts b/src/constants/constants.ts index f4097194..1d266731 100644 --- a/src/constants/constants.ts +++ b/src/constants/constants.ts @@ -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'; diff --git a/src/main.tsx b/src/main.tsx index db1ba3f8..f8bd8ca4 100644 --- a/src/main.tsx +++ b/src/main.tsx @@ -41,17 +41,17 @@ render( - - - - - + + + + + - - - - - + + + + + diff --git a/src/pages/swap/index.tsx b/src/pages/swap/index.tsx index 3d7916a9..d6df7dd2 100644 --- a/src/pages/swap/index.tsx +++ b/src/pages/swap/index.tsx @@ -433,7 +433,7 @@ export const SwapPage = () => { ) : ( )} diff --git a/src/services/anchor/index.ts b/src/services/anchor/index.ts index 98f1248f..9044da05 100644 --- a/src/services/anchor/index.ts +++ b/src/services/anchor/index.ts @@ -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', diff --git a/src/services/phases/signedTransactions.ts b/src/services/phases/signedTransactions.ts index 745f7874..4bfd21da 100644 --- a/src/services/phases/signedTransactions.ts +++ b/src/services/phases/signedTransactions.ts @@ -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, diff --git a/src/services/phases/stellar/index.tsx b/src/services/phases/stellar/index.tsx index 73559ac9..c233fff1 100644 --- a/src/services/phases/stellar/index.tsx +++ b/src/services/phases/stellar/index.tsx @@ -14,7 +14,12 @@ 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'; @@ -22,6 +27,38 @@ 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 { + let lastError: Error | null = null; + + const loadAccountWithTimeout = (accountId: string, timeout: number): Promise => { + return Promise.race([ + horizonServer.loadAccount(accountId), + new Promise((_, 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; @@ -58,10 +95,11 @@ async function isEphemeralCreated(stellarEphemeralSecret: string): Promise { - 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, @@ -131,7 +173,7 @@ async function setupStellarAccount( .addOperation( Operation.createAccount({ destination: ephemeralAccountId, - startingBalance: '2.5', + startingBalance: STELLAR_EPHEMERAL_STARTING_BALANCE_UNITS, }), ) .addOperation( @@ -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'); } }