From 0e05267e437b478a677a667e5f15b9eba3918595 Mon Sep 17 00:00:00 2001 From: nickkelly1 Date: Wed, 13 Nov 2024 12:14:03 -0600 Subject: [PATCH 1/6] feat: fail dropped jupiter transactions --- .../src/ui/action/views/swap/index.vue | 2 +- .../views/swap/libs/send-transactions.ts | 42 +-- .../swap/views/swap-best-offer/index.vue | 6 +- .../swap/src/providers/changelly/index.ts | 267 +++++++++--------- packages/swap/src/providers/jupiter/index.ts | 62 ++-- packages/swap/src/providers/oneInch/index.ts | 5 +- packages/swap/src/providers/paraswap/index.ts | 5 +- packages/swap/src/providers/rango/index.ts | 103 +++---- packages/swap/src/providers/zerox/index.ts | 5 +- packages/swap/src/types/index.ts | 10 +- packages/swap/tests/changelly.test.ts | 2 +- 11 files changed, 261 insertions(+), 248 deletions(-) diff --git a/packages/extension/src/ui/action/views/swap/index.vue b/packages/extension/src/ui/action/views/swap/index.vue index 9681d940a..8607f92a3 100644 --- a/packages/extension/src/ui/action/views/swap/index.vue +++ b/packages/extension/src/ui/action/views/swap/index.vue @@ -847,7 +847,7 @@ const sendAction = async () => { const tradeStatusOptions = trades.map(t => t!.getStatusObject({ - transactionHashes: [], + transactions: [], }), ); diff --git a/packages/extension/src/ui/action/views/swap/libs/send-transactions.ts b/packages/extension/src/ui/action/views/swap/libs/send-transactions.ts index d5cef90db..f7d2e8d31 100644 --- a/packages/extension/src/ui/action/views/swap/libs/send-transactions.ts +++ b/packages/extension/src/ui/action/views/swap/libs/send-transactions.ts @@ -75,7 +75,7 @@ const getBaseActivity = (options: ExecuteSwapOptions): Activity => { */ export const executeSwap = async ( options: ExecuteSwapOptions, -): Promise => { +): Promise<{ hash: string, sentAt: number }[]> => { const activityState = new ActivityState(); const api = await options.network.api(); if (options.networkType === NetworkType.Bitcoin) { @@ -116,7 +116,7 @@ export const executeSwap = async ( network: options.network.name, }); }); - return [signedTx.getId() as `0x${string}`]; + return [{ hash: signedTx.getId() as `0x${string}`, sentAt: Date.now() }]; } else if (options.networkType === NetworkType.Substrate) { const substrateTx = await getSubstrateNativeTransation( options.network as SubstrateNetwork, @@ -173,12 +173,12 @@ export const executeSwap = async ( { address: txActivity.from, network: options.network.name }, ); }); - return [hash]; + return [{ hash, sentAt: Date.now(), }]; } else if (options.networkType === NetworkType.Solana) { // Execute the swap on Solana const conn = (api as SolanaAPI).api.web3; - const solTxHashes: string[] = []; + const solTxs: { hash: string, sentAt: number, }[] = []; /** Enkrypt representation of the swap transactions */ const enkSolTxs = options.swap.transactions as EnkryptSolanaTransaction[]; @@ -219,7 +219,7 @@ export const executeSwap = async ( // Might need to update the block hash console.warn( `Failed to get fee for legacy transaction while checking` + - ` whether to update block hash: ${String(err)}`, + ` whether to update block hash: ${String(err)}`, ); shouldUpdateBlockHash = true; } @@ -228,7 +228,7 @@ export const executeSwap = async ( if (shouldUpdateBlockHash) { console.warn( `Unsigned legacy transaction might have an` + - ` out-of-date block hash, trying to update it...`, + ` out-of-date block hash, trying to update it...`, ); const backoff = [0, 500, 1_000, 2_000]; let backoffi = 0; @@ -238,7 +238,7 @@ export const executeSwap = async ( // Just continue and hope for the best with old block hash... console.warn( `Failed to get latest blockhash after ${backoffi} attempts,` + - ` continuing with old block hash for legacy transaction...`, + ` continuing with old block hash for legacy transaction...`, ); break update_block_hash; } @@ -246,7 +246,7 @@ export const executeSwap = async ( if (backoffMs > 0) { console.warn( `Waiting ${backoffMs}ms before retrying latest block` + - ` hash for legacy transaction...`, + ` hash for legacy transaction...`, ); await new Promise(res => setTimeout(res, backoffMs)); } @@ -257,7 +257,7 @@ export const executeSwap = async ( } catch (err) { console.warn( `Failed to get latest blockhash on attempt` + - ` ${backoffi + 1}: ${String(err)}`, + ` ${backoffi + 1}: ${String(err)}`, ); } backoffi++; @@ -330,7 +330,7 @@ export const executeSwap = async ( // Might need to update the block hash console.warn( `Failed to get fee for versioned transaction while checking` + - ` whether to update block hash: ${String(err)}`, + ` whether to update block hash: ${String(err)}`, ); shouldUpdateBlockHash = true; } @@ -339,7 +339,7 @@ export const executeSwap = async ( if (shouldUpdateBlockHash) { console.warn( `Unsigned versioned transaction might have an` + - ` out-of-date block hash, trying to update it...`, + ` out-of-date block hash, trying to update it...`, ); const backoff = [0, 500, 1_000, 2_000]; let backoffi = 0; @@ -349,7 +349,7 @@ export const executeSwap = async ( // Just continue and hope for the best with old block hash... console.warn( `Failed to get latest blockhash after ${backoffi} attempts,` + - ` continuing with old block hash for versioned transaction...`, + ` continuing with old block hash for versioned transaction...`, ); break update_block_hash; } @@ -357,7 +357,7 @@ export const executeSwap = async ( if (backoffMs > 0) { console.warn( `Waiting ${backoffMs}ms before retrying latest block` + - ` hash for versioned transaction...`, + ` hash for versioned transaction...`, ); await new Promise(res => setTimeout(res, backoffMs)); } @@ -368,7 +368,7 @@ export const executeSwap = async ( } catch (err) { console.warn( `Failed to get latest blockhash on attempt` + - ` ${backoffi + 1}: ${String(err)}`, + ` ${backoffi + 1}: ${String(err)}`, ); } backoffi++; @@ -434,8 +434,8 @@ export const executeSwap = async ( ); throw new Error( 'Failed to send Solana swap transaction: blockhash not found.' + - ' Too much time may have passed between the creation and sending' + - ' of the transaction', + ' Too much time may have passed between the creation and sending' + + ' of the transaction', ); } @@ -454,7 +454,7 @@ export const executeSwap = async ( } else { console.error( `Failed to send Solana swap transaction,` + - ` unhandled error ${(err as Error).name}`, + ` unhandled error ${(err as Error).name}`, ); } // Solana transactions can have big errors @@ -475,12 +475,12 @@ export const executeSwap = async ( network: options.network.name, }); - solTxHashes.push(txHash); + solTxs.push({ hash: txHash, sentAt: Date.now(), }); } // Finished executing the swap on Solana - return solTxHashes; + return solTxs; } else if (options.networkType === NetworkType.EVM) { const web3 = (api as EvmAPI).web3; const nonce = await web3.getTransactionCount(options.from.address); @@ -504,7 +504,7 @@ export const executeSwap = async ( ); const txs = await Promise.all(txsPromises); /** Hashes of transactions successfully sent & mined, in order of execution */ - const txPromises: `0x${string}`[] = []; + const txPromises: { hash: `0x${string}`, sentAt: number, }[] = []; for (const txInfo of txs) { // Submit each transaction, in-order one-by-one @@ -566,7 +566,7 @@ export const executeSwap = async ( ); }), ); - txPromises.push(hash); + txPromises.push({ hash, sentAt: Date.now(), }); } return txPromises; } else { diff --git a/packages/extension/src/ui/action/views/swap/views/swap-best-offer/index.vue b/packages/extension/src/ui/action/views/swap/views/swap-best-offer/index.vue index 83090a272..329e08956 100644 --- a/packages/extension/src/ui/action/views/swap/views/swap-best-offer/index.vue +++ b/packages/extension/src/ui/action/views/swap/views/swap-best-offer/index.vue @@ -395,8 +395,8 @@ const sendAction = async () => { swap: pickedTrade.value, toToken: swapData.toToken, }) - .then(hashes => { - pickedTrade.value.status!.options.transactionHashes = hashes; + .then(txs => { + pickedTrade.value.status!.options.transactions = txs; const swapRaw: SwapRawInfo = { fromToken: swapData.fromToken, toToken: swapData.toToken, @@ -421,7 +421,7 @@ const sendAction = async () => { timestamp: new Date().getTime(), type: ActivityType.swap, value: pickedTrade.value.toTokenAmount.toString(), - transactionHash: `${hashes[0]}-swap`, + transactionHash: `${txs[0]}-swap`, rawInfo: JSON.parse(JSON.stringify(swapRaw)), }; const activityState = new ActivityState(); diff --git a/packages/swap/src/providers/changelly/index.ts b/packages/swap/src/providers/changelly/index.ts index 926630f0a..da1f6bfec 100644 --- a/packages/swap/src/providers/changelly/index.ts +++ b/packages/swap/src/providers/changelly/index.ts @@ -99,7 +99,7 @@ if (DEBUG) { }; } else { // eslint-disable-next-line @typescript-eslint/no-unused-vars, @typescript-eslint/no-empty-function - debug = () => {}; + debug = () => { }; } class Changelly extends ProviderClass { @@ -286,8 +286,8 @@ class Changelly extends ProviderClass { if (response.error) { console.warn( `Error validating address with via Changelly` + - ` code=${String(response.error.code)}` + - ` message=${String(response.error.message)}` + ` code=${String(response.error.code)}` + + ` message=${String(response.error.message)}` ); return false; } @@ -295,8 +295,8 @@ class Changelly extends ProviderClass { if (typeof response.result.result !== "boolean") { console.warn( 'Unexpected response to "validateAddress" call to Changelly.' + - ` Expected a response.result.result to be a boolean` + - ` but received response: ${JSON.stringify(response)}` + ` Expected a response.result.result to be a boolean` + + ` but received response: ${JSON.stringify(response)}` ); return false; } @@ -305,9 +305,9 @@ class Changelly extends ProviderClass { debug( "isValidAddress", `Changelly validateAddress result` + - ` address=${address}` + - ` ticker=${ticker}` + - ` isValid=${isValid}` + ` address=${address}` + + ` ticker=${ticker}` + + ` isValid=${isValid}` ); return isValid; } @@ -356,11 +356,11 @@ class Changelly extends ProviderClass { // JsonRPC ERR response console.warn( `Changelly "getFixRate" returned JSONRPC error response` + - ` fromToken=${fromToken.symbol} (${params.from})` + - ` toToken=${toToken.symbol} (${params.to})` + - ` took=${(Date.now() - startedAt).toLocaleString()}ms` + - ` code=${String(response.error.code)}` + - ` message=${String(response.error.message)}` + ` fromToken=${fromToken.symbol} (${params.from})` + + ` toToken=${toToken.symbol} (${params.to})` + + ` took=${(Date.now() - startedAt).toLocaleString()}ms` + + ` code=${String(response.error.code)}` + + ` message=${String(response.error.message)}` ); return emptyResponse; } @@ -376,19 +376,19 @@ class Changelly extends ProviderClass { debug( "getMinMaxAmount", `Successfully got min and max of swap pair` + - ` fromToken=${fromToken.symbol} (${params.from})` + - ` toToken=${toToken.symbol} (${params.to})` + - ` took=${(Date.now() - startedAt).toLocaleString()}ms` + ` fromToken=${fromToken.symbol} (${params.from})` + + ` toToken=${toToken.symbol} (${params.to})` + + ` took=${(Date.now() - startedAt).toLocaleString()}ms` ); return minMax; } catch (err) { // HTTP request failed console.warn( `Errored calling Changelly JSONRPC HTTP API "getFixRate"` + - ` fromToken=${fromToken.symbol}` + - ` toToken=${toToken.symbol}` + - ` took=${(Date.now() - startedAt).toLocaleString()}ms` + - ` err=${String(err)}` + ` fromToken=${fromToken.symbol}` + + ` toToken=${toToken.symbol}` + + ` took=${(Date.now() - startedAt).toLocaleString()}ms` + + ` err=${String(err)}` ); return emptyResponse; } @@ -406,12 +406,12 @@ class Changelly extends ProviderClass { debug( "getQuote", `Getting Changelly quote` + - ` srcToken=${options.fromToken.symbol}` + - ` dstToken=${options.toToken.symbol}` + - ` fromAddress=${options.fromAddress}` + - ` toAddress=${options.toAddress}` + - ` fromNetwork=${this.network}` + - ` toNetwork=${options.toToken.networkInfo.name}` + ` srcToken=${options.fromToken.symbol}` + + ` dstToken=${options.toToken.symbol}` + + ` fromAddress=${options.fromAddress}` + + ` toAddress=${options.toAddress}` + + ` fromNetwork=${this.network}` + + ` toNetwork=${options.toToken.networkInfo.name}` ); if ( @@ -422,7 +422,7 @@ class Changelly extends ProviderClass { debug( "getQuote", `No swap: Enkrypt does not support Changelly on the destination network` + - ` dstNetwork=${options.toToken.networkInfo.name}` + ` dstNetwork=${options.toToken.networkInfo.name}` ); return null; } @@ -431,7 +431,7 @@ class Changelly extends ProviderClass { debug( "getQuote", `No swap: Enkrypt does not support Changelly on the source network` + - ` srcNetwork=${this.network}` + ` srcNetwork=${this.network}` ); return null; } @@ -440,8 +440,8 @@ class Changelly extends ProviderClass { debug( "getQuote", `No swap: Failed to find ticker for src token` + - ` srcToken=${options.fromToken.symbol}` + - ` srcNetwork=${this.network}` + ` srcToken=${options.fromToken.symbol}` + + ` srcNetwork=${this.network}` ); return null; } @@ -455,8 +455,8 @@ class Changelly extends ProviderClass { debug( "getQuote", `No swap: Failed to find ticker for dst token` + - ` dstToken=${options.toToken.symbol}` + - ` dstNetwork=${options.toToken.networkInfo.name}` + ` dstToken=${options.toToken.symbol}` + + ` dstNetwork=${options.toToken.networkInfo.name}` ); return null; } @@ -478,10 +478,10 @@ class Changelly extends ProviderClass { debug( "getQuote", `No swap: Quote request amount is zero` + - ` fromToken=${options.fromToken.symbol}` + - ` toToken=${options.toToken.symbol}` + - ` minimumFrom=${minMax.minimumFrom.toString()}` + - ` maximumFrom=${minMax.maximumFrom.toString()}` + ` fromToken=${options.fromToken.symbol}` + + ` toToken=${options.toToken.symbol}` + + ` minimumFrom=${minMax.minimumFrom.toString()}` + + ` maximumFrom=${minMax.maximumFrom.toString()}` ); return null; } @@ -513,12 +513,12 @@ class Changelly extends ProviderClass { if (response.error) { console.warn( `Changelly "getFixRateForAmount" returned JSONRPC error response,` + - ` returning no quotes` + - ` fromToken=${options.fromToken.symbol} (${params.from})` + - ` toToken=${options.toToken.symbol} (${params.to})` + - ` took=${(Date.now() - startedAt).toLocaleString()}ms` + - ` code=${String(response.error.code)}` + - ` message=${String(response.error.message)}` + ` returning no quotes` + + ` fromToken=${options.fromToken.symbol} (${params.from})` + + ` toToken=${options.toToken.symbol} (${params.to})` + + ` took=${(Date.now() - startedAt).toLocaleString()}ms` + + ` code=${String(response.error.code)}` + + ` message=${String(response.error.message)}` ); return null; } @@ -526,12 +526,12 @@ class Changelly extends ProviderClass { if (!response.result || !response.result[0]?.id) { console.warn( `Changelly "getFixRateForAmount" response contains no quotes,` + - ` returning no quotes` + - ` fromToken=${options.fromToken.symbol} (${params.from})` + - ` toToken=${options.toToken.symbol} (${params.to})` + - ` took=${(Date.now() - startedAt).toLocaleString()}ms` + - ` code=${String(response.error.code)}` + - ` message=${String(response.error.message)}` + ` returning no quotes` + + ` fromToken=${options.fromToken.symbol} (${params.from})` + + ` toToken=${options.toToken.symbol} (${params.to})` + + ` took=${(Date.now() - startedAt).toLocaleString()}ms` + + ` code=${String(response.error.code)}` + + ` message=${String(response.error.message)}` ); return null; } @@ -540,10 +540,10 @@ class Changelly extends ProviderClass { if (response.result.length > 1) { console.warn( `Changelly "getFixRateForAmount" returned more than one quote, continuing with first quote` + - ` fromToken=${options.fromToken.symbol} (${params.from})` + - ` toToken=${options.toToken.symbol} (${params.to})` + - ` took=${(Date.now() - startedAt).toLocaleString()}ms` + - ` count=${response.result.length}ms` + ` fromToken=${options.fromToken.symbol} (${params.from})` + + ` toToken=${options.toToken.symbol} (${params.to})` + + ` took=${(Date.now() - startedAt).toLocaleString()}ms` + + ` count=${response.result.length}ms` ); } @@ -551,7 +551,7 @@ class Changelly extends ProviderClass { const evmGasLimit = options.fromToken.address === NATIVE_TOKEN_ADDRESS && - options.fromToken.type === NetworkType.EVM + options.fromToken.type === NetworkType.EVM ? 21000 : toBN(GAS_LIMITS.transferToken).toNumber(); @@ -565,10 +565,10 @@ class Changelly extends ProviderClass { } catch (err) { console.warn( `Changelly "getFixRateForAmount" "amountTo" possibly returned more` + - ` decimals than the token has, attempting to trim trailing decimals...` + - ` amountTo=${firstChangellyFixRateQuote.amountTo}` + - ` toTokenDecimals=${options.toToken.decimals}` + - ` err=${String(err)}` + ` decimals than the token has, attempting to trim trailing decimals...` + + ` amountTo=${firstChangellyFixRateQuote.amountTo}` + + ` toTokenDecimals=${options.toToken.decimals}` + + ` err=${String(err)}` ); const original = firstChangellyFixRateQuote.amountTo; // eslint-disable-next-line no-use-before-define @@ -583,12 +583,12 @@ class Changelly extends ProviderClass { debug( "getQuote", `Fixed amountTo` + - ` firstChangellyFixRateQuote.amountTo=${firstChangellyFixRateQuote.amountTo}` + - ` toTokenAmountBase=${toTokenAmountBase}` + - ` options.toToken.decimals=${options.toToken.decimals}` + - ` options.toToken.symbol=${options.toToken.symbol}` + - ` options.toToken.name=${options.toToken.name}` + - ` options.toToken.address=${options.toToken.address}` + ` firstChangellyFixRateQuote.amountTo=${firstChangellyFixRateQuote.amountTo}` + + ` toTokenAmountBase=${toTokenAmountBase}` + + ` options.toToken.decimals=${options.toToken.decimals}` + + ` options.toToken.symbol=${options.toToken.symbol}` + + ` options.toToken.name=${options.toToken.name}` + + ` options.toToken.address=${options.toToken.address}` ); } @@ -602,10 +602,10 @@ class Changelly extends ProviderClass { } catch (err) { console.warn( `Changelly "getFixRateForAmount" "networkFee" possibly returned more` + - ` decimals than the token has, attempting to trim trailing decimals...` + - ` networkFee=${firstChangellyFixRateQuote.networkFee}` + - ` toTokenDecimals=${options.toToken.decimals}` + - ` err=${String(err)}` + ` decimals than the token has, attempting to trim trailing decimals...` + + ` networkFee=${firstChangellyFixRateQuote.networkFee}` + + ` toTokenDecimals=${options.toToken.decimals}` + + ` err=${String(err)}` ); const original = firstChangellyFixRateQuote.networkFee; // eslint-disable-next-line no-use-before-define @@ -620,12 +620,12 @@ class Changelly extends ProviderClass { debug( "getQuote", `Fixed networkFee` + - ` firstChangellyFixRateQuote.networkFee=${firstChangellyFixRateQuote.networkFee}` + - ` networkFeeBase=${networkFeeBase}` + - ` options.toToken.decimals=${options.toToken.decimals}` + - ` options.toToken.symbol=${options.toToken.symbol}` + - ` options.toToken.name=${options.toToken.name}` + - ` options.toToken.address=${options.toToken.address}` + ` firstChangellyFixRateQuote.networkFee=${firstChangellyFixRateQuote.networkFee}` + + ` networkFeeBase=${networkFeeBase}` + + ` options.toToken.decimals=${options.toToken.decimals}` + + ` options.toToken.symbol=${options.toToken.symbol}` + + ` options.toToken.name=${options.toToken.name}` + + ` options.toToken.address=${options.toToken.address}` ); } @@ -654,16 +654,16 @@ class Changelly extends ProviderClass { debug( "getQuote", `Successfully retrieved quote from Changelly via "getFixRateForAmount"` + - ` took=${(Date.now() - startedAt).toLocaleString()}ms` + ` took=${(Date.now() - startedAt).toLocaleString()}ms` ); return providerQuoteResponse; } catch (err) { console.warn( `Errored getting quotes from Changelly via "getFixRateForAmount",` + - ` returning no quotes` + - ` took=${(Date.now() - startedAt).toLocaleString()}ms` + - ` err=${String(err)}` + ` returning no quotes` + + ` took=${(Date.now() - startedAt).toLocaleString()}ms` + + ` err=${String(err)}` ); return null; } @@ -682,7 +682,7 @@ class Changelly extends ProviderClass { debug( "getSwap", `Enkrypt does not support Changelly on the source network, returning no swap` + - ` srcNetwork=${this.network}` + ` srcNetwork=${this.network}` ); return null; } @@ -695,7 +695,7 @@ class Changelly extends ProviderClass { debug( "getSwap", `Enkrypt does not support Changelly on the destination network, returning no swap` + - ` dstNetwork=${quote.options.toToken.networkInfo.name}` + ` dstNetwork=${quote.options.toToken.networkInfo.name}` ); return null; } @@ -726,11 +726,11 @@ class Changelly extends ProviderClass { if (response.error) { console.warn( `Changelly "createFixTransaction" returned JSONRPC error response, returning no swap` + - ` fromToken=${quote.options.fromToken.symbol} (${params.from})` + - ` toToken=${quote.options.toToken.symbol} (${params.to})` + - ` took=${(Date.now() - startedAt).toLocaleString()}ms` + - ` code=${String(response.error.code)}` + - ` message=${String(response.error.message)}` + ` fromToken=${quote.options.fromToken.symbol} (${params.from})` + + ` toToken=${quote.options.toToken.symbol} (${params.to})` + + ` took=${(Date.now() - startedAt).toLocaleString()}ms` + + ` code=${String(response.error.code)}` + + ` message=${String(response.error.message)}` ); return null; } @@ -738,9 +738,9 @@ class Changelly extends ProviderClass { if (!response.result.id) { console.warn( `Changelly "createFixTransaction" response contains no id, returning no swap` + - ` fromToken=${quote.options.fromToken.symbol} (${params.from})` + - ` toToken=${quote.options.toToken.symbol} (${params.to})` + - ` took=${(Date.now() - startedAt).toLocaleString()}ms` + ` fromToken=${quote.options.fromToken.symbol} (${params.from})` + + ` toToken=${quote.options.toToken.symbol} (${params.to})` + + ` took=${(Date.now() - startedAt).toLocaleString()}ms` ); return null; } @@ -794,11 +794,11 @@ class Changelly extends ProviderClass { debug( "getSwap", `Preparing Solana Changelly SOL swap transaction` + - ` quote.options.fromAddress=${quote.options.fromAddress}` + - ` latestBlockHash=${latestBlockHash.blockhash}` + - ` lastValidBlockHeight=${latestBlockHash.lastValidBlockHeight}` + - ` payinAddress=${changellyFixedRateTx.payinAddress}` + - ` lamports=${BigInt(quote.options.amount.toString())}` + ` quote.options.fromAddress=${quote.options.fromAddress}` + + ` latestBlockHash=${latestBlockHash.blockhash}` + + ` lastValidBlockHeight=${latestBlockHash.lastValidBlockHeight}` + + ` payinAddress=${changellyFixedRateTx.payinAddress}` + + ` lamports=${BigInt(quote.options.amount.toString())}` ); versionedTx = new VersionedTransaction( new TransactionMessage({ @@ -838,15 +838,15 @@ class Changelly extends ProviderClass { debug( "getSwap", `Preparing Solana Changelly SPL token swap transaction` + - ` srcMint=${mint.toBase58()}` + - ` srcTokenProgramId=${tokenProgramId.toBase58()}` + - ` wallet=${wallet.toBase58()}` + - ` walletAta=${tokenProgramId.toBase58()}` + - ` payin=${payin.toBase58()}` + - ` payinAta=${payinAta.toBase58()}` + - ` latestBlockHash=${latestBlockHash.blockhash}` + - ` lastValidBlockHeight=${latestBlockHash.lastValidBlockHeight}` + - ` amount=${amount}` + ` srcMint=${mint.toBase58()}` + + ` srcTokenProgramId=${tokenProgramId.toBase58()}` + + ` wallet=${wallet.toBase58()}` + + ` walletAta=${tokenProgramId.toBase58()}` + + ` payin=${payin.toBase58()}` + + ` payinAta=${payinAta.toBase58()}` + + ` latestBlockHash=${latestBlockHash.blockhash}` + + ` lastValidBlockHeight=${latestBlockHash.lastValidBlockHeight}` + + ` amount=${amount}` ); // If the ATA account doesn't exist we need create it @@ -925,28 +925,28 @@ class Changelly extends ProviderClass { debug( "getSwap", `No recent fees, not setting priority fee` + - ` recentFeeCount=${recentFeeCount}` + - ` recentFeeCountWithoutZeroes=${recentFeeCountWithoutZeroes}` + - ` recentFeeSum=${recentFeeSum}` + - ` recentFeeMin=${recentFeeMin}` + - ` recentFeeMax=${recentFeeMax}` + - ` recentFeeMean=${recentFeeMean}` + - ` recentFeeMedian=${recentFeeMedian}` + - ` recentFeeMinAvg=${recentFeeMinAvg}` + ` recentFeeCount=${recentFeeCount}` + + ` recentFeeCountWithoutZeroes=${recentFeeCountWithoutZeroes}` + + ` recentFeeSum=${recentFeeSum}` + + ` recentFeeMin=${recentFeeMin}` + + ` recentFeeMax=${recentFeeMax}` + + ` recentFeeMean=${recentFeeMean}` + + ` recentFeeMedian=${recentFeeMedian}` + + ` recentFeeMinAvg=${recentFeeMinAvg}` ); } else { debug( "getSwap", `Setting priority fee` + - ` priority_fee=${recentFeeMinAvg} micro_lamports/compute_unit` + - ` recentFeeCount=${recentFeeCount}` + - ` recentFeeCountWithoutZeroes=${recentFeeCountWithoutZeroes}` + - ` recentFeeSum=${recentFeeSum}` + - ` recentFeeMin=${recentFeeMin}` + - ` recentFeeMax=${recentFeeMax}` + - ` recentFeeMean=${recentFeeMean}` + - ` recentFeeMedian=${recentFeeMedian}` + - ` recentFeeMinAvg=${recentFeeMinAvg}` + ` priority_fee=${recentFeeMinAvg} micro_lamports/compute_unit` + + ` recentFeeCount=${recentFeeCount}` + + ` recentFeeCountWithoutZeroes=${recentFeeCountWithoutZeroes}` + + ` recentFeeSum=${recentFeeSum}` + + ` recentFeeMin=${recentFeeMin}` + + ` recentFeeMax=${recentFeeMax}` + + ` recentFeeMean=${recentFeeMean}` + + ` recentFeeMedian=${recentFeeMedian}` + + ` recentFeeMinAvg=${recentFeeMinAvg}` ); instructions.unshift( ComputeBudgetProgram.setComputeUnitPrice({ @@ -961,7 +961,7 @@ class Changelly extends ProviderClass { /** destination */ payinAta, /** owner */ wallet, /** amount */ amount, - /** multiSigners */ [], + /** multiSigners */[], /** programId */ tokenProgramId ) ); @@ -1009,10 +1009,10 @@ class Changelly extends ProviderClass { } catch (err) { console.warn( `Changelly "createFixTransaction" "amountExpectedTo" possibly returned more` + - ` decimals than the token has, attempting to trim trailing decimals...` + - ` amountExpectedTo=${changellyFixedRateTx.amountExpectedTo}` + - ` toTokenDecimals=${quote.options.toToken.decimals}` + - ` err=${String(err)}` + ` decimals than the token has, attempting to trim trailing decimals...` + + ` amountExpectedTo=${changellyFixedRateTx.amountExpectedTo}` + + ` toTokenDecimals=${quote.options.toToken.decimals}` + + ` err=${String(err)}` ); const original = changellyFixedRateTx.amountExpectedTo; // eslint-disable-next-line no-use-before-define @@ -1027,12 +1027,12 @@ class Changelly extends ProviderClass { debug( "getQuote", `Fixed amountExpectedTo` + - ` changellyFixedRateTx.amountExpectedTo=${changellyFixedRateTx.amountExpectedTo}` + - ` baseToAmount=${baseToAmount}` + - ` quote.options.toToken.decimals=${quote.options.toToken.decimals}` + - ` quote.options.toToken.symbol=${quote.options.toToken.symbol}` + - ` quote.options.toToken.name=${quote.options.toToken.name}` + - ` quote.options.toToken.address=${quote.options.toToken.address}` + ` changellyFixedRateTx.amountExpectedTo=${changellyFixedRateTx.amountExpectedTo}` + + ` baseToAmount=${baseToAmount}` + + ` quote.options.toToken.decimals=${quote.options.toToken.decimals}` + + ` quote.options.toToken.symbol=${quote.options.toToken.symbol}` + + ` quote.options.toToken.name=${quote.options.toToken.name}` + + ` quote.options.toToken.address=${quote.options.toToken.address}` ); } @@ -1057,20 +1057,23 @@ class Changelly extends ProviderClass { debug( "getSwap", `Successfully extracted Changelly swap transaction via "createFixTransaction"` + - ` took=${(Date.now() - startedAt).toLocaleString()}ms` + ` took=${(Date.now() - startedAt).toLocaleString()}ms` ); return retResponse; } catch (err) { console.warn( `Errored processing Changelly swap response, returning no swap` + - ` took=${(Date.now() - startedAt).toLocaleString()}ms` + - ` err=${String(err)}` + ` took=${(Date.now() - startedAt).toLocaleString()}ms` + + ` err=${String(err)}` ); return null; } } async getStatus(options: StatusOptions): Promise { + // TODO: If a Solana transaction hasn't been found after 3 minutes then consider dropping it + // I'm not sure how Rango's API handles Solana transactions being dropped... + const params: ChangellyApiGetStatusParams = { id: options.swapId, }; diff --git a/packages/swap/src/providers/jupiter/index.ts b/packages/swap/src/providers/jupiter/index.ts index ed2753b2a..16d3a3ab9 100644 --- a/packages/swap/src/providers/jupiter/index.ts +++ b/packages/swap/src/providers/jupiter/index.ts @@ -160,7 +160,7 @@ if (DEBUG) { }; } else { // eslint-disable-next-line @typescript-eslint/no-unused-vars, @typescript-eslint/no-empty-function - debug = () => {}; + debug = () => { }; } // Jupiter API Tokens @@ -373,8 +373,8 @@ export class Jupiter extends ProviderClass { debug( "querySwapInfo", `Referrer ATA already exists. No need to record additional rent fees.` + - ` ATA pubkey: ${referrerATAPubkey.toBase58()},` + - ` Source mint: ${srcMint.toBase58()}` + ` ATA pubkey: ${referrerATAPubkey.toBase58()},` + + ` Source mint: ${srcMint.toBase58()}` ); } else { // The referral fee ATA account needs to be created or else we can't receive fees for this transaction @@ -400,9 +400,9 @@ export class Jupiter extends ProviderClass { debug( "querySwapInfo", `Referrer ATA does not exist. Updating transaction with instruction to create it.` + - ` Referral ATA pubkey: ${referrerATAPubkey.toBase58()},` + - ` Rent: ${extraRentFees} lamports,` + - ` Total Rent: ${extraRentFees} lamports` + ` Referral ATA pubkey: ${referrerATAPubkey.toBase58()},` + + ` Rent: ${extraRentFees} lamports,` + + ` Total Rent: ${extraRentFees} lamports` ); } @@ -413,8 +413,8 @@ export class Jupiter extends ProviderClass { debug( "querySwapInfo", `Wallet destination mint ATA already exists. No need to record additional rent fees.` + - ` ATA pubkey: ${dstATAPubkey.toBase58()},` + - ` Destination mint: ${dstMint.toBase58()}` + ` ATA pubkey: ${dstATAPubkey.toBase58()},` + + ` Destination mint: ${dstMint.toBase58()}` ); } else { const extraRentFee = await this.conn.getMinimumBalanceForRentExemption( @@ -437,11 +437,11 @@ export class Jupiter extends ProviderClass { debug( "querySwapInfo", `Wallet destination mint ATA does not exist, registering custom instruction to create it.` + - ` Adding ATA rent to extra transaction fees.` + - ` ATA pubkey: ${dstATAPubkey.toBase58()},` + - ` Destination mint: ${dstMint.toBase58()},` + - ` Rent: ${extraRentFee} lamports,` + - ` Total rent: ${rentFees} lamports` + ` Adding ATA rent to extra transaction fees.` + + ` ATA pubkey: ${dstATAPubkey.toBase58()},` + + ` Destination mint: ${dstMint.toBase58()},` + + ` Rent: ${extraRentFee} lamports,` + + ` Total rent: ${rentFees} lamports` ); } @@ -475,7 +475,7 @@ export class Jupiter extends ProviderClass { debug( "getQuote", `ignoring quote request to network ${options.toToken.networkInfo.name},` + - ` cross network swaps not supported` + ` cross network swaps not supported` ); return null; } @@ -574,17 +574,25 @@ export class Jupiter extends ProviderClass { } async getStatus(options: StatusOptions): Promise { - if (options.transactionHashes.length !== 1) { + if (options.transactions.length !== 1) { throw new TypeError( - `JupiterSwap.getStatus: Expected one transaction hash but got ${options.transactionHashes.length}` + `JupiterSwap.getStatus: Expected one transaction hash but got ${options.transactions.length}` ); } - const [txhash] = options.transactionHashes; - const txResponse = await this.conn.getTransaction(txhash, { + const [{ sentAt, hash, }] = options.transactions; + const txResponse = await this.conn.getTransaction(hash, { maxSupportedTransactionVersion: 0, }); + if (txResponse == null) { + // Consider dropped (/failed) if it's still null after 3 minutes + // (block hashes expire after 2 minutes so 3 minutes gives 1 minute of leeway) + if (Date.now() > sentAt + 3 * 60_000) { + // TODO: consider adding a "dropped" status? + return TransactionStatus.failed + } + // Transaction hasn't been picked up by the node yet return TransactionStatus.pending; } @@ -698,8 +706,7 @@ async function getJupiterTokens(abortable?: { default: /* noop */ } throw new Error( - `Failed to get Jupiter tokens, HTTP response returned not-ok status ${ - res.status + `Failed to get Jupiter tokens, HTTP response returned not-ok status ${res.status } ${res.statusText || ""}: ${msg}` ); } @@ -717,8 +724,7 @@ async function getJupiterTokens(abortable?: { if (failed) throw err; debug( "getJupiterTokens", - `Failed to get Jupiter tokens on attempt ${backoffi + 1}/${ - backoff.length + `Failed to get Jupiter tokens on attempt ${backoffi + 1}/${backoff.length }: ${String(err)}` ); errRef ??= { err: err as Error }; @@ -877,8 +883,7 @@ async function getJupiterQuote( default: /* noop */ } throw new Error( - `Failed to get Jupiter quote, HTTP response returned not-ok status ${ - res.status + `Failed to get Jupiter quote, HTTP response returned not-ok status ${res.status } ${res.statusText || ""} at url ${url}: ${msg}` ); } @@ -895,8 +900,7 @@ async function getJupiterQuote( if (abortable?.signal?.aborted) throw abortable?.signal.reason; if (failed) throw err; console.warn( - `[getJupiterQuote] Failed to get Jupiter quote on attempt ${ - backoffi + 1 + `[getJupiterQuote] Failed to get Jupiter quote on attempt ${backoffi + 1 }/${backoff.length}: ${String(err)}` ); errRef ??= { err: err as Error }; @@ -1028,8 +1032,7 @@ async function getJupiterSwap( default: /* noop */ } throw new Error( - `Failed to get Jupiter swap, HTTP response returned not-ok status ${ - res.status + `Failed to get Jupiter swap, HTTP response returned not-ok status ${res.status } ${res.statusText || ""} at url ${url}: ${msg}` ); } @@ -1047,8 +1050,7 @@ async function getJupiterSwap( if (failed) throw err; debug( "getJupiterSwap", - `Failed to get Jupiter swap on attempt ${backoffi + 1}/${ - backoff.length + `Failed to get Jupiter swap on attempt ${backoffi + 1}/${backoff.length }: ${String(err)}` ); errRef ??= { err: err as Error }; diff --git a/packages/swap/src/providers/oneInch/index.ts b/packages/swap/src/providers/oneInch/index.ts index 231ce0ca9..e31f88f2b 100644 --- a/packages/swap/src/providers/oneInch/index.ts +++ b/packages/swap/src/providers/oneInch/index.ts @@ -177,8 +177,7 @@ class OneInch extends ProviderClass { disableEstimate: "true", }); return fetch( - `${BASE_URL}${ - supportedNetworks[this.network].chainId + `${BASE_URL}${supportedNetworks[this.network].chainId }/swap?${params.toString()}` ) .then((res) => res.json()) @@ -295,7 +294,7 @@ class OneInch extends ProviderClass { } getStatus(options: StatusOptions): Promise { - const promises = options.transactionHashes.map((hash) => + const promises = options.transactions.map(({ hash }) => this.web3eth.getTransactionReceipt(hash) ); return Promise.all(promises).then((receipts) => { diff --git a/packages/swap/src/providers/paraswap/index.ts b/packages/swap/src/providers/paraswap/index.ts index a1170502b..ad0c9bd82 100644 --- a/packages/swap/src/providers/paraswap/index.ts +++ b/packages/swap/src/providers/paraswap/index.ts @@ -183,8 +183,7 @@ class ParaSwap extends ProviderClass { isDirectFeeTransfer: true, }); return fetch( - `${BASE_URL}transactions/${ - supportedNetworks[this.network].chainId + `${BASE_URL}transactions/${supportedNetworks[this.network].chainId }?${params.toString()}`, { method: "POST", @@ -350,7 +349,7 @@ class ParaSwap extends ProviderClass { } getStatus(options: StatusOptions): Promise { - const promises = options.transactionHashes.map((hash) => + const promises = options.transactions.map(({ hash }) => this.web3eth.getTransactionReceipt(hash) ); return Promise.all(promises).then((receipts) => { diff --git a/packages/swap/src/providers/rango/index.ts b/packages/swap/src/providers/rango/index.ts index 3218d963e..6908acb4b 100644 --- a/packages/swap/src/providers/rango/index.ts +++ b/packages/swap/src/providers/rango/index.ts @@ -100,7 +100,7 @@ if (DEBUG) { ); }; } else { - debug = () => {}; + debug = () => { }; } type SupportedNetworkInfo = { @@ -403,8 +403,8 @@ class Rango extends ProviderClass { debug( "init", "Rango meta" + - ` tokens.length=${rangoMeta.tokens.length}` + - ` blockchains.length=${rangoMeta.blockchains.length}` + ` tokens.length=${rangoMeta.tokens.length}` + + ` blockchains.length=${rangoMeta.blockchains.length}` ); const supportedNetworkInfo = supportedNetworkInfoByName.get(this.network); @@ -573,7 +573,7 @@ class Rango extends ProviderClass { if (token.address == null) { console.warn( `Cannot get Rango token symbol: Token address is not defined` + - ` for token ${token.name} (${token.symbol}) - ${token.address}` + ` for token ${token.name} (${token.symbol}) - ${token.address}` ); return undefined; } @@ -607,12 +607,12 @@ class Rango extends ProviderClass { debug( "getRangoSwap", `Getting swap` + - ` fromNetwork=${this.network}` + - ` toNetwork=${options.toToken.networkInfo.name}` + - ` fromToken=${options.fromToken.symbol}` + - ` toToken=${options.toToken.symbol}` + - ` fromAddress=${options.fromAddress}` + - ` toAddress=${options.toAddress}` + ` fromNetwork=${this.network}` + + ` toNetwork=${options.toToken.networkInfo.name}` + + ` fromToken=${options.fromToken.symbol}` + + ` toToken=${options.toToken.symbol}` + + ` fromAddress=${options.fromAddress}` + + ` toAddress=${options.toAddress}` ); try { @@ -626,8 +626,8 @@ class Rango extends ProviderClass { debug( "getRangoSwap", "No swap:" + - ` Enkrypt does not support Rango swap on the source network` + - ` fromNetwork=${this.network}` + ` Enkrypt does not support Rango swap on the source network` + + ` fromNetwork=${this.network}` ); } @@ -639,8 +639,8 @@ class Rango extends ProviderClass { debug( "getRangoSwap", "No swap:" + - ` Enkrypt does not support Rango swap on the destination network` + - ` fromNetwork=${this.network}` + ` Enkrypt does not support Rango swap on the destination network` + + ` fromNetwork=${this.network}` ); } @@ -653,10 +653,10 @@ class Rango extends ProviderClass { debug( "getRangoSwap", `No swap:` + - ` Rango does not support swap on the source network` + - ` fromNetwork=${this.network}` + - ` fromBlockchain=${fromRangoBlockchain.name}` + - ` enabled=${fromRangoBlockchain.enabled}` + ` Rango does not support swap on the source network` + + ` fromNetwork=${this.network}` + + ` fromBlockchain=${fromRangoBlockchain.name}` + + ` enabled=${fromRangoBlockchain.enabled}` ); return null; } @@ -671,10 +671,10 @@ class Rango extends ProviderClass { debug( "getRangoSwap", `No swap:` + - ` Rango does not support swap on the destination network` + - ` toNetwork=${options.toToken.networkInfo.name}` + - ` toBlockchain=${toRangoBlockchain.name}` + - ` enabled=${toRangoBlockchain.enabled}` + ` Rango does not support swap on the destination network` + + ` toNetwork=${options.toToken.networkInfo.name}` + + ` toBlockchain=${toRangoBlockchain.name}` + + ` enabled=${toRangoBlockchain.enabled}` ); return null; } @@ -686,8 +686,8 @@ class Rango extends ProviderClass { debug( "getRangoSwap", `Rango block chains ids` + - ` fromRangoBlockchain=${fromRangoBlockchainName}` + - ` toRangoBlockchain=${toRangoBlockchainName}` + ` fromRangoBlockchain=${fromRangoBlockchainName}` + + ` toRangoBlockchain=${toRangoBlockchainName}` ); const fromTokenAddress = options.fromToken.address; @@ -710,8 +710,8 @@ class Rango extends ProviderClass { debug( "getRangoSwap", `No swap: No symbol for src token or dst token` + - ` fromTokenSymbol=${fromRangoTokenSymbol}` + - ` toTokenSymbol=${toRangoTokenSymbol}` + ` fromTokenSymbol=${fromRangoTokenSymbol}` + + ` toTokenSymbol=${toRangoTokenSymbol}` ); return null; } @@ -749,15 +749,15 @@ class Rango extends ProviderClass { debug( "getRangoSwap", `Requesting quote from rango sdk...` + - ` fromRangoBlockchain=${fromRangoBlockchainName}` + - ` toRangoBlockchain=${toRangoBlockchainName}` + - ` fromToken=${fromRangoTokenSymbol}` + - ` toToken=${toRangoTokenSymbol}` + - ` fromAddress=${options.fromAddress}` + - ` toAddress=${options.toAddress}` + - ` amount=${options.amount.toString()}` + - ` slippage=${slippage}` + - ` referrerFee=${params.referrerFee}` + ` fromRangoBlockchain=${fromRangoBlockchainName}` + + ` toRangoBlockchain=${toRangoBlockchainName}` + + ` fromToken=${fromRangoTokenSymbol}` + + ` toToken=${toRangoTokenSymbol}` + + ` fromAddress=${options.fromAddress}` + + ` toAddress=${options.toAddress}` + + ` amount=${options.amount.toString()}` + + ` slippage=${slippage}` + + ` referrerFee=${params.referrerFee}` ); const rangoSwapResponse = await rangoClient.swap(params, abortable); debug("getRangoSwap", `Received quote from rango sdk`); @@ -1009,7 +1009,7 @@ class Rango extends ProviderClass { debug( "getRangoSwap", `Checking Rango signatures...` + - ` signatures=${thirdPartySignatures.length}`, + ` signatures=${thirdPartySignatures.length}`, ` pubkeys=${thirdPartySignatures .map(({ pubkey }) => pubkey) .join(",")}` @@ -1233,30 +1233,33 @@ class Rango extends ProviderClass { } async getStatus(options: StatusOptions): Promise { - const { requestId, transactionHashes } = options; + const { requestId, transactions, } = options; + + // TODO: If a Solana transaction hasn't been found after 3 minutes then consider dropping it + // I'm not sure how Rango's API handles Solana transactions being dropped... - const transactionHash = - transactionHashes.length > 0 - ? transactionHashes[transactionHashes.length - 1] - : transactionHashes[0]; + const mostRecentTransactionHash = + transactions.length > 0 + ? transactions[transactions.length - 1].hash + : transactions[0].hash; const isAlreadySuccessOrFailed = [ RangoTransactionStatus.FAILED, RangoTransactionStatus.SUCCESS, ].includes( - this.transactionsStatus.find((t) => t.hash === transactionHash)?.status + this.transactionsStatus.find((t) => t.hash === mostRecentTransactionHash)?.status ); if (requestId && !isAlreadySuccessOrFailed) { const res = await rangoClient.status({ - txId: transactionHash, + txId: mostRecentTransactionHash, requestId, }); if (res.error || res.status === RangoTransactionStatus.FAILED) { this.transactionsStatus.push({ status: RangoTransactionStatus.FAILED, - hash: transactionHash, + hash: mostRecentTransactionHash, }); return TransactionStatus.failed; } @@ -1265,7 +1268,7 @@ class Rango extends ProviderClass { } this.transactionsStatus.push({ status: RangoTransactionStatus.SUCCESS, - hash: transactionHash, + hash: mostRecentTransactionHash, }); return TransactionStatus.success; } @@ -1276,7 +1279,7 @@ class Rango extends ProviderClass { // Get status of Solana transactions const sigStatuses = await ( this.web3 as Connection - ).getSignatureStatuses(transactionHashes); + ).getSignatureStatuses(transactions.map(({ hash }) => hash)); for (let i = 0, len = sigStatuses.value.length; i < len; i++) { const sigStatus = sigStatuses.value[i]; if (sigStatus == null) { @@ -1293,7 +1296,7 @@ class Rango extends ProviderClass { default: { // Get status of EVM transactions const receipts = await Promise.all( - transactionHashes.map((hash) => + transactions.map(({ hash }) => (this.web3 as Web3Eth).getTransactionReceipt(hash) ) ); @@ -1387,7 +1390,7 @@ async function fetchRangoSwaplist(abortable?: { if (retryidx >= retries.length) { throw new Error( `Failed to fetch Rango swaplists after ${retries.length}` + - ` retries: ${String(errref?.err ?? "???")}` + ` retries: ${String(errref?.err ?? "???")}` ); } const waitMs = retries[retryidx]; @@ -1612,7 +1615,7 @@ async function checkExpectedSolanaLegacyTransactionStatus( debug( "checkExpectedSolanaLegacyTransactionStatus", `Retrying Rango Solana unsigned legacy transaction simulation` + - ` with updated block hash ${latestBlockHash.blockhash}...` + ` with updated block hash ${latestBlockHash.blockhash}...` ); legacyTx.recentBlockhash = latestBlockHash.blockhash; abortable?.signal?.throwIfAborted(); @@ -1668,7 +1671,7 @@ async function checkExpectedSolanaVersionedTransactionStatus( debug( "checkExpectedSolanaVersionedTransactionStatus", `Retrying Rango Solana unsigned versioned transaction simulation` + - ` with updated block hash ${latestBlockHash.blockhash}...` + ` with updated block hash ${latestBlockHash.blockhash}...` ); versionedTx.message.recentBlockhash = latestBlockHash.blockhash; } diff --git a/packages/swap/src/providers/zerox/index.ts b/packages/swap/src/providers/zerox/index.ts index 24706a365..20cc1e72f 100644 --- a/packages/swap/src/providers/zerox/index.ts +++ b/packages/swap/src/providers/zerox/index.ts @@ -165,8 +165,7 @@ class ZeroX extends ProviderClass { affiliateAddress: feeConfig ? feeConfig.referrer : "", }); return fetch( - `${BASE_URL}${ - supportedNetworks[this.network].chainId + `${BASE_URL}${supportedNetworks[this.network].chainId }/swap/v1/quote?${params.toString()}` ) .then((res) => res.json()) @@ -272,7 +271,7 @@ class ZeroX extends ProviderClass { } getStatus(options: StatusOptions): Promise { - const promises = options.transactionHashes.map((hash) => + const promises = options.transactions.map(({ hash }) => this.web3eth.getTransactionReceipt(hash) ); return Promise.all(promises).then((receipts) => { diff --git a/packages/swap/src/types/index.ts b/packages/swap/src/types/index.ts index 3f3d63b07..09e3da2ab 100644 --- a/packages/swap/src/types/index.ts +++ b/packages/swap/src/types/index.ts @@ -220,9 +220,17 @@ export interface ProviderQuoteResponse { quote: SwapQuote; minMax: MinMaxResponse; } + +export type StatusOptionTransaction = { + /** Transaction hash */ + hash: string, + /** Unix epoch milliseconds `Date.now()` */ + sentAt: number, +} + export interface StatusOptions { [key: string]: any; - transactionHashes: string[]; + transactions: StatusOptionTransaction[]; } export interface StatusOptionsResponse { diff --git a/packages/swap/tests/changelly.test.ts b/packages/swap/tests/changelly.test.ts index 103c690f2..da1298ff5 100644 --- a/packages/swap/tests/changelly.test.ts +++ b/packages/swap/tests/changelly.test.ts @@ -52,7 +52,7 @@ describe("Changelly Provider", () => { ).to.be.eq(true); const status = await changelly.getStatus( ( - await swap!.getStatusObject({ transactionHashes: [] }) + await swap!.getStatusObject({ transactions: [] }) ).options ); expect(status).to.be.eq("pending"); From 3ade3200d4f8fcb0a5b33d816e21787585991972 Mon Sep 17 00:00:00 2001 From: nickkelly1 Date: Wed, 13 Nov 2024 12:16:11 -0600 Subject: [PATCH 2/6] chore: lint --- .../swap/src/providers/changelly/index.ts | 264 +++++++++--------- packages/swap/src/providers/jupiter/index.ts | 51 ++-- packages/swap/src/providers/oneInch/index.ts | 3 +- packages/swap/src/providers/paraswap/index.ts | 3 +- packages/swap/src/providers/rango/index.ts | 83 +++--- packages/swap/src/providers/zerox/index.ts | 3 +- packages/swap/src/types/index.ts | 6 +- packages/swap/tests/zerox.test.ts | 8 +- 8 files changed, 215 insertions(+), 206 deletions(-) diff --git a/packages/swap/src/providers/changelly/index.ts b/packages/swap/src/providers/changelly/index.ts index da1f6bfec..b2ec52a6a 100644 --- a/packages/swap/src/providers/changelly/index.ts +++ b/packages/swap/src/providers/changelly/index.ts @@ -99,7 +99,7 @@ if (DEBUG) { }; } else { // eslint-disable-next-line @typescript-eslint/no-unused-vars, @typescript-eslint/no-empty-function - debug = () => { }; + debug = () => {}; } class Changelly extends ProviderClass { @@ -286,8 +286,8 @@ class Changelly extends ProviderClass { if (response.error) { console.warn( `Error validating address with via Changelly` + - ` code=${String(response.error.code)}` + - ` message=${String(response.error.message)}` + ` code=${String(response.error.code)}` + + ` message=${String(response.error.message)}` ); return false; } @@ -295,8 +295,8 @@ class Changelly extends ProviderClass { if (typeof response.result.result !== "boolean") { console.warn( 'Unexpected response to "validateAddress" call to Changelly.' + - ` Expected a response.result.result to be a boolean` + - ` but received response: ${JSON.stringify(response)}` + ` Expected a response.result.result to be a boolean` + + ` but received response: ${JSON.stringify(response)}` ); return false; } @@ -305,9 +305,9 @@ class Changelly extends ProviderClass { debug( "isValidAddress", `Changelly validateAddress result` + - ` address=${address}` + - ` ticker=${ticker}` + - ` isValid=${isValid}` + ` address=${address}` + + ` ticker=${ticker}` + + ` isValid=${isValid}` ); return isValid; } @@ -356,11 +356,11 @@ class Changelly extends ProviderClass { // JsonRPC ERR response console.warn( `Changelly "getFixRate" returned JSONRPC error response` + - ` fromToken=${fromToken.symbol} (${params.from})` + - ` toToken=${toToken.symbol} (${params.to})` + - ` took=${(Date.now() - startedAt).toLocaleString()}ms` + - ` code=${String(response.error.code)}` + - ` message=${String(response.error.message)}` + ` fromToken=${fromToken.symbol} (${params.from})` + + ` toToken=${toToken.symbol} (${params.to})` + + ` took=${(Date.now() - startedAt).toLocaleString()}ms` + + ` code=${String(response.error.code)}` + + ` message=${String(response.error.message)}` ); return emptyResponse; } @@ -376,19 +376,19 @@ class Changelly extends ProviderClass { debug( "getMinMaxAmount", `Successfully got min and max of swap pair` + - ` fromToken=${fromToken.symbol} (${params.from})` + - ` toToken=${toToken.symbol} (${params.to})` + - ` took=${(Date.now() - startedAt).toLocaleString()}ms` + ` fromToken=${fromToken.symbol} (${params.from})` + + ` toToken=${toToken.symbol} (${params.to})` + + ` took=${(Date.now() - startedAt).toLocaleString()}ms` ); return minMax; } catch (err) { // HTTP request failed console.warn( `Errored calling Changelly JSONRPC HTTP API "getFixRate"` + - ` fromToken=${fromToken.symbol}` + - ` toToken=${toToken.symbol}` + - ` took=${(Date.now() - startedAt).toLocaleString()}ms` + - ` err=${String(err)}` + ` fromToken=${fromToken.symbol}` + + ` toToken=${toToken.symbol}` + + ` took=${(Date.now() - startedAt).toLocaleString()}ms` + + ` err=${String(err)}` ); return emptyResponse; } @@ -406,12 +406,12 @@ class Changelly extends ProviderClass { debug( "getQuote", `Getting Changelly quote` + - ` srcToken=${options.fromToken.symbol}` + - ` dstToken=${options.toToken.symbol}` + - ` fromAddress=${options.fromAddress}` + - ` toAddress=${options.toAddress}` + - ` fromNetwork=${this.network}` + - ` toNetwork=${options.toToken.networkInfo.name}` + ` srcToken=${options.fromToken.symbol}` + + ` dstToken=${options.toToken.symbol}` + + ` fromAddress=${options.fromAddress}` + + ` toAddress=${options.toAddress}` + + ` fromNetwork=${this.network}` + + ` toNetwork=${options.toToken.networkInfo.name}` ); if ( @@ -422,7 +422,7 @@ class Changelly extends ProviderClass { debug( "getQuote", `No swap: Enkrypt does not support Changelly on the destination network` + - ` dstNetwork=${options.toToken.networkInfo.name}` + ` dstNetwork=${options.toToken.networkInfo.name}` ); return null; } @@ -431,7 +431,7 @@ class Changelly extends ProviderClass { debug( "getQuote", `No swap: Enkrypt does not support Changelly on the source network` + - ` srcNetwork=${this.network}` + ` srcNetwork=${this.network}` ); return null; } @@ -440,8 +440,8 @@ class Changelly extends ProviderClass { debug( "getQuote", `No swap: Failed to find ticker for src token` + - ` srcToken=${options.fromToken.symbol}` + - ` srcNetwork=${this.network}` + ` srcToken=${options.fromToken.symbol}` + + ` srcNetwork=${this.network}` ); return null; } @@ -455,8 +455,8 @@ class Changelly extends ProviderClass { debug( "getQuote", `No swap: Failed to find ticker for dst token` + - ` dstToken=${options.toToken.symbol}` + - ` dstNetwork=${options.toToken.networkInfo.name}` + ` dstToken=${options.toToken.symbol}` + + ` dstNetwork=${options.toToken.networkInfo.name}` ); return null; } @@ -478,10 +478,10 @@ class Changelly extends ProviderClass { debug( "getQuote", `No swap: Quote request amount is zero` + - ` fromToken=${options.fromToken.symbol}` + - ` toToken=${options.toToken.symbol}` + - ` minimumFrom=${minMax.minimumFrom.toString()}` + - ` maximumFrom=${minMax.maximumFrom.toString()}` + ` fromToken=${options.fromToken.symbol}` + + ` toToken=${options.toToken.symbol}` + + ` minimumFrom=${minMax.minimumFrom.toString()}` + + ` maximumFrom=${minMax.maximumFrom.toString()}` ); return null; } @@ -513,12 +513,12 @@ class Changelly extends ProviderClass { if (response.error) { console.warn( `Changelly "getFixRateForAmount" returned JSONRPC error response,` + - ` returning no quotes` + - ` fromToken=${options.fromToken.symbol} (${params.from})` + - ` toToken=${options.toToken.symbol} (${params.to})` + - ` took=${(Date.now() - startedAt).toLocaleString()}ms` + - ` code=${String(response.error.code)}` + - ` message=${String(response.error.message)}` + ` returning no quotes` + + ` fromToken=${options.fromToken.symbol} (${params.from})` + + ` toToken=${options.toToken.symbol} (${params.to})` + + ` took=${(Date.now() - startedAt).toLocaleString()}ms` + + ` code=${String(response.error.code)}` + + ` message=${String(response.error.message)}` ); return null; } @@ -526,12 +526,12 @@ class Changelly extends ProviderClass { if (!response.result || !response.result[0]?.id) { console.warn( `Changelly "getFixRateForAmount" response contains no quotes,` + - ` returning no quotes` + - ` fromToken=${options.fromToken.symbol} (${params.from})` + - ` toToken=${options.toToken.symbol} (${params.to})` + - ` took=${(Date.now() - startedAt).toLocaleString()}ms` + - ` code=${String(response.error.code)}` + - ` message=${String(response.error.message)}` + ` returning no quotes` + + ` fromToken=${options.fromToken.symbol} (${params.from})` + + ` toToken=${options.toToken.symbol} (${params.to})` + + ` took=${(Date.now() - startedAt).toLocaleString()}ms` + + ` code=${String(response.error.code)}` + + ` message=${String(response.error.message)}` ); return null; } @@ -540,10 +540,10 @@ class Changelly extends ProviderClass { if (response.result.length > 1) { console.warn( `Changelly "getFixRateForAmount" returned more than one quote, continuing with first quote` + - ` fromToken=${options.fromToken.symbol} (${params.from})` + - ` toToken=${options.toToken.symbol} (${params.to})` + - ` took=${(Date.now() - startedAt).toLocaleString()}ms` + - ` count=${response.result.length}ms` + ` fromToken=${options.fromToken.symbol} (${params.from})` + + ` toToken=${options.toToken.symbol} (${params.to})` + + ` took=${(Date.now() - startedAt).toLocaleString()}ms` + + ` count=${response.result.length}ms` ); } @@ -551,7 +551,7 @@ class Changelly extends ProviderClass { const evmGasLimit = options.fromToken.address === NATIVE_TOKEN_ADDRESS && - options.fromToken.type === NetworkType.EVM + options.fromToken.type === NetworkType.EVM ? 21000 : toBN(GAS_LIMITS.transferToken).toNumber(); @@ -565,10 +565,10 @@ class Changelly extends ProviderClass { } catch (err) { console.warn( `Changelly "getFixRateForAmount" "amountTo" possibly returned more` + - ` decimals than the token has, attempting to trim trailing decimals...` + - ` amountTo=${firstChangellyFixRateQuote.amountTo}` + - ` toTokenDecimals=${options.toToken.decimals}` + - ` err=${String(err)}` + ` decimals than the token has, attempting to trim trailing decimals...` + + ` amountTo=${firstChangellyFixRateQuote.amountTo}` + + ` toTokenDecimals=${options.toToken.decimals}` + + ` err=${String(err)}` ); const original = firstChangellyFixRateQuote.amountTo; // eslint-disable-next-line no-use-before-define @@ -583,12 +583,12 @@ class Changelly extends ProviderClass { debug( "getQuote", `Fixed amountTo` + - ` firstChangellyFixRateQuote.amountTo=${firstChangellyFixRateQuote.amountTo}` + - ` toTokenAmountBase=${toTokenAmountBase}` + - ` options.toToken.decimals=${options.toToken.decimals}` + - ` options.toToken.symbol=${options.toToken.symbol}` + - ` options.toToken.name=${options.toToken.name}` + - ` options.toToken.address=${options.toToken.address}` + ` firstChangellyFixRateQuote.amountTo=${firstChangellyFixRateQuote.amountTo}` + + ` toTokenAmountBase=${toTokenAmountBase}` + + ` options.toToken.decimals=${options.toToken.decimals}` + + ` options.toToken.symbol=${options.toToken.symbol}` + + ` options.toToken.name=${options.toToken.name}` + + ` options.toToken.address=${options.toToken.address}` ); } @@ -602,10 +602,10 @@ class Changelly extends ProviderClass { } catch (err) { console.warn( `Changelly "getFixRateForAmount" "networkFee" possibly returned more` + - ` decimals than the token has, attempting to trim trailing decimals...` + - ` networkFee=${firstChangellyFixRateQuote.networkFee}` + - ` toTokenDecimals=${options.toToken.decimals}` + - ` err=${String(err)}` + ` decimals than the token has, attempting to trim trailing decimals...` + + ` networkFee=${firstChangellyFixRateQuote.networkFee}` + + ` toTokenDecimals=${options.toToken.decimals}` + + ` err=${String(err)}` ); const original = firstChangellyFixRateQuote.networkFee; // eslint-disable-next-line no-use-before-define @@ -620,12 +620,12 @@ class Changelly extends ProviderClass { debug( "getQuote", `Fixed networkFee` + - ` firstChangellyFixRateQuote.networkFee=${firstChangellyFixRateQuote.networkFee}` + - ` networkFeeBase=${networkFeeBase}` + - ` options.toToken.decimals=${options.toToken.decimals}` + - ` options.toToken.symbol=${options.toToken.symbol}` + - ` options.toToken.name=${options.toToken.name}` + - ` options.toToken.address=${options.toToken.address}` + ` firstChangellyFixRateQuote.networkFee=${firstChangellyFixRateQuote.networkFee}` + + ` networkFeeBase=${networkFeeBase}` + + ` options.toToken.decimals=${options.toToken.decimals}` + + ` options.toToken.symbol=${options.toToken.symbol}` + + ` options.toToken.name=${options.toToken.name}` + + ` options.toToken.address=${options.toToken.address}` ); } @@ -654,16 +654,16 @@ class Changelly extends ProviderClass { debug( "getQuote", `Successfully retrieved quote from Changelly via "getFixRateForAmount"` + - ` took=${(Date.now() - startedAt).toLocaleString()}ms` + ` took=${(Date.now() - startedAt).toLocaleString()}ms` ); return providerQuoteResponse; } catch (err) { console.warn( `Errored getting quotes from Changelly via "getFixRateForAmount",` + - ` returning no quotes` + - ` took=${(Date.now() - startedAt).toLocaleString()}ms` + - ` err=${String(err)}` + ` returning no quotes` + + ` took=${(Date.now() - startedAt).toLocaleString()}ms` + + ` err=${String(err)}` ); return null; } @@ -682,7 +682,7 @@ class Changelly extends ProviderClass { debug( "getSwap", `Enkrypt does not support Changelly on the source network, returning no swap` + - ` srcNetwork=${this.network}` + ` srcNetwork=${this.network}` ); return null; } @@ -695,7 +695,7 @@ class Changelly extends ProviderClass { debug( "getSwap", `Enkrypt does not support Changelly on the destination network, returning no swap` + - ` dstNetwork=${quote.options.toToken.networkInfo.name}` + ` dstNetwork=${quote.options.toToken.networkInfo.name}` ); return null; } @@ -726,11 +726,11 @@ class Changelly extends ProviderClass { if (response.error) { console.warn( `Changelly "createFixTransaction" returned JSONRPC error response, returning no swap` + - ` fromToken=${quote.options.fromToken.symbol} (${params.from})` + - ` toToken=${quote.options.toToken.symbol} (${params.to})` + - ` took=${(Date.now() - startedAt).toLocaleString()}ms` + - ` code=${String(response.error.code)}` + - ` message=${String(response.error.message)}` + ` fromToken=${quote.options.fromToken.symbol} (${params.from})` + + ` toToken=${quote.options.toToken.symbol} (${params.to})` + + ` took=${(Date.now() - startedAt).toLocaleString()}ms` + + ` code=${String(response.error.code)}` + + ` message=${String(response.error.message)}` ); return null; } @@ -738,9 +738,9 @@ class Changelly extends ProviderClass { if (!response.result.id) { console.warn( `Changelly "createFixTransaction" response contains no id, returning no swap` + - ` fromToken=${quote.options.fromToken.symbol} (${params.from})` + - ` toToken=${quote.options.toToken.symbol} (${params.to})` + - ` took=${(Date.now() - startedAt).toLocaleString()}ms` + ` fromToken=${quote.options.fromToken.symbol} (${params.from})` + + ` toToken=${quote.options.toToken.symbol} (${params.to})` + + ` took=${(Date.now() - startedAt).toLocaleString()}ms` ); return null; } @@ -794,11 +794,11 @@ class Changelly extends ProviderClass { debug( "getSwap", `Preparing Solana Changelly SOL swap transaction` + - ` quote.options.fromAddress=${quote.options.fromAddress}` + - ` latestBlockHash=${latestBlockHash.blockhash}` + - ` lastValidBlockHeight=${latestBlockHash.lastValidBlockHeight}` + - ` payinAddress=${changellyFixedRateTx.payinAddress}` + - ` lamports=${BigInt(quote.options.amount.toString())}` + ` quote.options.fromAddress=${quote.options.fromAddress}` + + ` latestBlockHash=${latestBlockHash.blockhash}` + + ` lastValidBlockHeight=${latestBlockHash.lastValidBlockHeight}` + + ` payinAddress=${changellyFixedRateTx.payinAddress}` + + ` lamports=${BigInt(quote.options.amount.toString())}` ); versionedTx = new VersionedTransaction( new TransactionMessage({ @@ -838,15 +838,15 @@ class Changelly extends ProviderClass { debug( "getSwap", `Preparing Solana Changelly SPL token swap transaction` + - ` srcMint=${mint.toBase58()}` + - ` srcTokenProgramId=${tokenProgramId.toBase58()}` + - ` wallet=${wallet.toBase58()}` + - ` walletAta=${tokenProgramId.toBase58()}` + - ` payin=${payin.toBase58()}` + - ` payinAta=${payinAta.toBase58()}` + - ` latestBlockHash=${latestBlockHash.blockhash}` + - ` lastValidBlockHeight=${latestBlockHash.lastValidBlockHeight}` + - ` amount=${amount}` + ` srcMint=${mint.toBase58()}` + + ` srcTokenProgramId=${tokenProgramId.toBase58()}` + + ` wallet=${wallet.toBase58()}` + + ` walletAta=${tokenProgramId.toBase58()}` + + ` payin=${payin.toBase58()}` + + ` payinAta=${payinAta.toBase58()}` + + ` latestBlockHash=${latestBlockHash.blockhash}` + + ` lastValidBlockHeight=${latestBlockHash.lastValidBlockHeight}` + + ` amount=${amount}` ); // If the ATA account doesn't exist we need create it @@ -925,28 +925,28 @@ class Changelly extends ProviderClass { debug( "getSwap", `No recent fees, not setting priority fee` + - ` recentFeeCount=${recentFeeCount}` + - ` recentFeeCountWithoutZeroes=${recentFeeCountWithoutZeroes}` + - ` recentFeeSum=${recentFeeSum}` + - ` recentFeeMin=${recentFeeMin}` + - ` recentFeeMax=${recentFeeMax}` + - ` recentFeeMean=${recentFeeMean}` + - ` recentFeeMedian=${recentFeeMedian}` + - ` recentFeeMinAvg=${recentFeeMinAvg}` + ` recentFeeCount=${recentFeeCount}` + + ` recentFeeCountWithoutZeroes=${recentFeeCountWithoutZeroes}` + + ` recentFeeSum=${recentFeeSum}` + + ` recentFeeMin=${recentFeeMin}` + + ` recentFeeMax=${recentFeeMax}` + + ` recentFeeMean=${recentFeeMean}` + + ` recentFeeMedian=${recentFeeMedian}` + + ` recentFeeMinAvg=${recentFeeMinAvg}` ); } else { debug( "getSwap", `Setting priority fee` + - ` priority_fee=${recentFeeMinAvg} micro_lamports/compute_unit` + - ` recentFeeCount=${recentFeeCount}` + - ` recentFeeCountWithoutZeroes=${recentFeeCountWithoutZeroes}` + - ` recentFeeSum=${recentFeeSum}` + - ` recentFeeMin=${recentFeeMin}` + - ` recentFeeMax=${recentFeeMax}` + - ` recentFeeMean=${recentFeeMean}` + - ` recentFeeMedian=${recentFeeMedian}` + - ` recentFeeMinAvg=${recentFeeMinAvg}` + ` priority_fee=${recentFeeMinAvg} micro_lamports/compute_unit` + + ` recentFeeCount=${recentFeeCount}` + + ` recentFeeCountWithoutZeroes=${recentFeeCountWithoutZeroes}` + + ` recentFeeSum=${recentFeeSum}` + + ` recentFeeMin=${recentFeeMin}` + + ` recentFeeMax=${recentFeeMax}` + + ` recentFeeMean=${recentFeeMean}` + + ` recentFeeMedian=${recentFeeMedian}` + + ` recentFeeMinAvg=${recentFeeMinAvg}` ); instructions.unshift( ComputeBudgetProgram.setComputeUnitPrice({ @@ -961,7 +961,7 @@ class Changelly extends ProviderClass { /** destination */ payinAta, /** owner */ wallet, /** amount */ amount, - /** multiSigners */[], + /** multiSigners */ [], /** programId */ tokenProgramId ) ); @@ -1009,10 +1009,10 @@ class Changelly extends ProviderClass { } catch (err) { console.warn( `Changelly "createFixTransaction" "amountExpectedTo" possibly returned more` + - ` decimals than the token has, attempting to trim trailing decimals...` + - ` amountExpectedTo=${changellyFixedRateTx.amountExpectedTo}` + - ` toTokenDecimals=${quote.options.toToken.decimals}` + - ` err=${String(err)}` + ` decimals than the token has, attempting to trim trailing decimals...` + + ` amountExpectedTo=${changellyFixedRateTx.amountExpectedTo}` + + ` toTokenDecimals=${quote.options.toToken.decimals}` + + ` err=${String(err)}` ); const original = changellyFixedRateTx.amountExpectedTo; // eslint-disable-next-line no-use-before-define @@ -1027,12 +1027,12 @@ class Changelly extends ProviderClass { debug( "getQuote", `Fixed amountExpectedTo` + - ` changellyFixedRateTx.amountExpectedTo=${changellyFixedRateTx.amountExpectedTo}` + - ` baseToAmount=${baseToAmount}` + - ` quote.options.toToken.decimals=${quote.options.toToken.decimals}` + - ` quote.options.toToken.symbol=${quote.options.toToken.symbol}` + - ` quote.options.toToken.name=${quote.options.toToken.name}` + - ` quote.options.toToken.address=${quote.options.toToken.address}` + ` changellyFixedRateTx.amountExpectedTo=${changellyFixedRateTx.amountExpectedTo}` + + ` baseToAmount=${baseToAmount}` + + ` quote.options.toToken.decimals=${quote.options.toToken.decimals}` + + ` quote.options.toToken.symbol=${quote.options.toToken.symbol}` + + ` quote.options.toToken.name=${quote.options.toToken.name}` + + ` quote.options.toToken.address=${quote.options.toToken.address}` ); } @@ -1057,14 +1057,14 @@ class Changelly extends ProviderClass { debug( "getSwap", `Successfully extracted Changelly swap transaction via "createFixTransaction"` + - ` took=${(Date.now() - startedAt).toLocaleString()}ms` + ` took=${(Date.now() - startedAt).toLocaleString()}ms` ); return retResponse; } catch (err) { console.warn( `Errored processing Changelly swap response, returning no swap` + - ` took=${(Date.now() - startedAt).toLocaleString()}ms` + - ` err=${String(err)}` + ` took=${(Date.now() - startedAt).toLocaleString()}ms` + + ` err=${String(err)}` ); return null; } diff --git a/packages/swap/src/providers/jupiter/index.ts b/packages/swap/src/providers/jupiter/index.ts index 16d3a3ab9..692f7f081 100644 --- a/packages/swap/src/providers/jupiter/index.ts +++ b/packages/swap/src/providers/jupiter/index.ts @@ -160,7 +160,7 @@ if (DEBUG) { }; } else { // eslint-disable-next-line @typescript-eslint/no-unused-vars, @typescript-eslint/no-empty-function - debug = () => { }; + debug = () => {}; } // Jupiter API Tokens @@ -373,8 +373,8 @@ export class Jupiter extends ProviderClass { debug( "querySwapInfo", `Referrer ATA already exists. No need to record additional rent fees.` + - ` ATA pubkey: ${referrerATAPubkey.toBase58()},` + - ` Source mint: ${srcMint.toBase58()}` + ` ATA pubkey: ${referrerATAPubkey.toBase58()},` + + ` Source mint: ${srcMint.toBase58()}` ); } else { // The referral fee ATA account needs to be created or else we can't receive fees for this transaction @@ -400,9 +400,9 @@ export class Jupiter extends ProviderClass { debug( "querySwapInfo", `Referrer ATA does not exist. Updating transaction with instruction to create it.` + - ` Referral ATA pubkey: ${referrerATAPubkey.toBase58()},` + - ` Rent: ${extraRentFees} lamports,` + - ` Total Rent: ${extraRentFees} lamports` + ` Referral ATA pubkey: ${referrerATAPubkey.toBase58()},` + + ` Rent: ${extraRentFees} lamports,` + + ` Total Rent: ${extraRentFees} lamports` ); } @@ -413,8 +413,8 @@ export class Jupiter extends ProviderClass { debug( "querySwapInfo", `Wallet destination mint ATA already exists. No need to record additional rent fees.` + - ` ATA pubkey: ${dstATAPubkey.toBase58()},` + - ` Destination mint: ${dstMint.toBase58()}` + ` ATA pubkey: ${dstATAPubkey.toBase58()},` + + ` Destination mint: ${dstMint.toBase58()}` ); } else { const extraRentFee = await this.conn.getMinimumBalanceForRentExemption( @@ -437,11 +437,11 @@ export class Jupiter extends ProviderClass { debug( "querySwapInfo", `Wallet destination mint ATA does not exist, registering custom instruction to create it.` + - ` Adding ATA rent to extra transaction fees.` + - ` ATA pubkey: ${dstATAPubkey.toBase58()},` + - ` Destination mint: ${dstMint.toBase58()},` + - ` Rent: ${extraRentFee} lamports,` + - ` Total rent: ${rentFees} lamports` + ` Adding ATA rent to extra transaction fees.` + + ` ATA pubkey: ${dstATAPubkey.toBase58()},` + + ` Destination mint: ${dstMint.toBase58()},` + + ` Rent: ${extraRentFee} lamports,` + + ` Total rent: ${rentFees} lamports` ); } @@ -475,7 +475,7 @@ export class Jupiter extends ProviderClass { debug( "getQuote", `ignoring quote request to network ${options.toToken.networkInfo.name},` + - ` cross network swaps not supported` + ` cross network swaps not supported` ); return null; } @@ -579,18 +579,17 @@ export class Jupiter extends ProviderClass { `JupiterSwap.getStatus: Expected one transaction hash but got ${options.transactions.length}` ); } - const [{ sentAt, hash, }] = options.transactions; + const [{ sentAt, hash }] = options.transactions; const txResponse = await this.conn.getTransaction(hash, { maxSupportedTransactionVersion: 0, }); - if (txResponse == null) { // Consider dropped (/failed) if it's still null after 3 minutes // (block hashes expire after 2 minutes so 3 minutes gives 1 minute of leeway) if (Date.now() > sentAt + 3 * 60_000) { // TODO: consider adding a "dropped" status? - return TransactionStatus.failed + return TransactionStatus.failed; } // Transaction hasn't been picked up by the node yet @@ -706,7 +705,8 @@ async function getJupiterTokens(abortable?: { default: /* noop */ } throw new Error( - `Failed to get Jupiter tokens, HTTP response returned not-ok status ${res.status + `Failed to get Jupiter tokens, HTTP response returned not-ok status ${ + res.status } ${res.statusText || ""}: ${msg}` ); } @@ -724,7 +724,8 @@ async function getJupiterTokens(abortable?: { if (failed) throw err; debug( "getJupiterTokens", - `Failed to get Jupiter tokens on attempt ${backoffi + 1}/${backoff.length + `Failed to get Jupiter tokens on attempt ${backoffi + 1}/${ + backoff.length }: ${String(err)}` ); errRef ??= { err: err as Error }; @@ -883,7 +884,8 @@ async function getJupiterQuote( default: /* noop */ } throw new Error( - `Failed to get Jupiter quote, HTTP response returned not-ok status ${res.status + `Failed to get Jupiter quote, HTTP response returned not-ok status ${ + res.status } ${res.statusText || ""} at url ${url}: ${msg}` ); } @@ -900,7 +902,8 @@ async function getJupiterQuote( if (abortable?.signal?.aborted) throw abortable?.signal.reason; if (failed) throw err; console.warn( - `[getJupiterQuote] Failed to get Jupiter quote on attempt ${backoffi + 1 + `[getJupiterQuote] Failed to get Jupiter quote on attempt ${ + backoffi + 1 }/${backoff.length}: ${String(err)}` ); errRef ??= { err: err as Error }; @@ -1032,7 +1035,8 @@ async function getJupiterSwap( default: /* noop */ } throw new Error( - `Failed to get Jupiter swap, HTTP response returned not-ok status ${res.status + `Failed to get Jupiter swap, HTTP response returned not-ok status ${ + res.status } ${res.statusText || ""} at url ${url}: ${msg}` ); } @@ -1050,7 +1054,8 @@ async function getJupiterSwap( if (failed) throw err; debug( "getJupiterSwap", - `Failed to get Jupiter swap on attempt ${backoffi + 1}/${backoff.length + `Failed to get Jupiter swap on attempt ${backoffi + 1}/${ + backoff.length }: ${String(err)}` ); errRef ??= { err: err as Error }; diff --git a/packages/swap/src/providers/oneInch/index.ts b/packages/swap/src/providers/oneInch/index.ts index e31f88f2b..28d5c37e1 100644 --- a/packages/swap/src/providers/oneInch/index.ts +++ b/packages/swap/src/providers/oneInch/index.ts @@ -177,7 +177,8 @@ class OneInch extends ProviderClass { disableEstimate: "true", }); return fetch( - `${BASE_URL}${supportedNetworks[this.network].chainId + `${BASE_URL}${ + supportedNetworks[this.network].chainId }/swap?${params.toString()}` ) .then((res) => res.json()) diff --git a/packages/swap/src/providers/paraswap/index.ts b/packages/swap/src/providers/paraswap/index.ts index ad0c9bd82..40d2e7ab7 100644 --- a/packages/swap/src/providers/paraswap/index.ts +++ b/packages/swap/src/providers/paraswap/index.ts @@ -183,7 +183,8 @@ class ParaSwap extends ProviderClass { isDirectFeeTransfer: true, }); return fetch( - `${BASE_URL}transactions/${supportedNetworks[this.network].chainId + `${BASE_URL}transactions/${ + supportedNetworks[this.network].chainId }?${params.toString()}`, { method: "POST", diff --git a/packages/swap/src/providers/rango/index.ts b/packages/swap/src/providers/rango/index.ts index 6908acb4b..5ace8b771 100644 --- a/packages/swap/src/providers/rango/index.ts +++ b/packages/swap/src/providers/rango/index.ts @@ -100,7 +100,7 @@ if (DEBUG) { ); }; } else { - debug = () => { }; + debug = () => {}; } type SupportedNetworkInfo = { @@ -403,8 +403,8 @@ class Rango extends ProviderClass { debug( "init", "Rango meta" + - ` tokens.length=${rangoMeta.tokens.length}` + - ` blockchains.length=${rangoMeta.blockchains.length}` + ` tokens.length=${rangoMeta.tokens.length}` + + ` blockchains.length=${rangoMeta.blockchains.length}` ); const supportedNetworkInfo = supportedNetworkInfoByName.get(this.network); @@ -573,7 +573,7 @@ class Rango extends ProviderClass { if (token.address == null) { console.warn( `Cannot get Rango token symbol: Token address is not defined` + - ` for token ${token.name} (${token.symbol}) - ${token.address}` + ` for token ${token.name} (${token.symbol}) - ${token.address}` ); return undefined; } @@ -607,12 +607,12 @@ class Rango extends ProviderClass { debug( "getRangoSwap", `Getting swap` + - ` fromNetwork=${this.network}` + - ` toNetwork=${options.toToken.networkInfo.name}` + - ` fromToken=${options.fromToken.symbol}` + - ` toToken=${options.toToken.symbol}` + - ` fromAddress=${options.fromAddress}` + - ` toAddress=${options.toAddress}` + ` fromNetwork=${this.network}` + + ` toNetwork=${options.toToken.networkInfo.name}` + + ` fromToken=${options.fromToken.symbol}` + + ` toToken=${options.toToken.symbol}` + + ` fromAddress=${options.fromAddress}` + + ` toAddress=${options.toAddress}` ); try { @@ -626,8 +626,8 @@ class Rango extends ProviderClass { debug( "getRangoSwap", "No swap:" + - ` Enkrypt does not support Rango swap on the source network` + - ` fromNetwork=${this.network}` + ` Enkrypt does not support Rango swap on the source network` + + ` fromNetwork=${this.network}` ); } @@ -639,8 +639,8 @@ class Rango extends ProviderClass { debug( "getRangoSwap", "No swap:" + - ` Enkrypt does not support Rango swap on the destination network` + - ` fromNetwork=${this.network}` + ` Enkrypt does not support Rango swap on the destination network` + + ` fromNetwork=${this.network}` ); } @@ -653,10 +653,10 @@ class Rango extends ProviderClass { debug( "getRangoSwap", `No swap:` + - ` Rango does not support swap on the source network` + - ` fromNetwork=${this.network}` + - ` fromBlockchain=${fromRangoBlockchain.name}` + - ` enabled=${fromRangoBlockchain.enabled}` + ` Rango does not support swap on the source network` + + ` fromNetwork=${this.network}` + + ` fromBlockchain=${fromRangoBlockchain.name}` + + ` enabled=${fromRangoBlockchain.enabled}` ); return null; } @@ -671,10 +671,10 @@ class Rango extends ProviderClass { debug( "getRangoSwap", `No swap:` + - ` Rango does not support swap on the destination network` + - ` toNetwork=${options.toToken.networkInfo.name}` + - ` toBlockchain=${toRangoBlockchain.name}` + - ` enabled=${toRangoBlockchain.enabled}` + ` Rango does not support swap on the destination network` + + ` toNetwork=${options.toToken.networkInfo.name}` + + ` toBlockchain=${toRangoBlockchain.name}` + + ` enabled=${toRangoBlockchain.enabled}` ); return null; } @@ -686,8 +686,8 @@ class Rango extends ProviderClass { debug( "getRangoSwap", `Rango block chains ids` + - ` fromRangoBlockchain=${fromRangoBlockchainName}` + - ` toRangoBlockchain=${toRangoBlockchainName}` + ` fromRangoBlockchain=${fromRangoBlockchainName}` + + ` toRangoBlockchain=${toRangoBlockchainName}` ); const fromTokenAddress = options.fromToken.address; @@ -710,8 +710,8 @@ class Rango extends ProviderClass { debug( "getRangoSwap", `No swap: No symbol for src token or dst token` + - ` fromTokenSymbol=${fromRangoTokenSymbol}` + - ` toTokenSymbol=${toRangoTokenSymbol}` + ` fromTokenSymbol=${fromRangoTokenSymbol}` + + ` toTokenSymbol=${toRangoTokenSymbol}` ); return null; } @@ -749,15 +749,15 @@ class Rango extends ProviderClass { debug( "getRangoSwap", `Requesting quote from rango sdk...` + - ` fromRangoBlockchain=${fromRangoBlockchainName}` + - ` toRangoBlockchain=${toRangoBlockchainName}` + - ` fromToken=${fromRangoTokenSymbol}` + - ` toToken=${toRangoTokenSymbol}` + - ` fromAddress=${options.fromAddress}` + - ` toAddress=${options.toAddress}` + - ` amount=${options.amount.toString()}` + - ` slippage=${slippage}` + - ` referrerFee=${params.referrerFee}` + ` fromRangoBlockchain=${fromRangoBlockchainName}` + + ` toRangoBlockchain=${toRangoBlockchainName}` + + ` fromToken=${fromRangoTokenSymbol}` + + ` toToken=${toRangoTokenSymbol}` + + ` fromAddress=${options.fromAddress}` + + ` toAddress=${options.toAddress}` + + ` amount=${options.amount.toString()}` + + ` slippage=${slippage}` + + ` referrerFee=${params.referrerFee}` ); const rangoSwapResponse = await rangoClient.swap(params, abortable); debug("getRangoSwap", `Received quote from rango sdk`); @@ -1009,7 +1009,7 @@ class Rango extends ProviderClass { debug( "getRangoSwap", `Checking Rango signatures...` + - ` signatures=${thirdPartySignatures.length}`, + ` signatures=${thirdPartySignatures.length}`, ` pubkeys=${thirdPartySignatures .map(({ pubkey }) => pubkey) .join(",")}` @@ -1233,7 +1233,7 @@ class Rango extends ProviderClass { } async getStatus(options: StatusOptions): Promise { - const { requestId, transactions, } = options; + const { requestId, transactions } = options; // TODO: If a Solana transaction hasn't been found after 3 minutes then consider dropping it // I'm not sure how Rango's API handles Solana transactions being dropped... @@ -1247,7 +1247,8 @@ class Rango extends ProviderClass { RangoTransactionStatus.FAILED, RangoTransactionStatus.SUCCESS, ].includes( - this.transactionsStatus.find((t) => t.hash === mostRecentTransactionHash)?.status + this.transactionsStatus.find((t) => t.hash === mostRecentTransactionHash) + ?.status ); if (requestId && !isAlreadySuccessOrFailed) { @@ -1390,7 +1391,7 @@ async function fetchRangoSwaplist(abortable?: { if (retryidx >= retries.length) { throw new Error( `Failed to fetch Rango swaplists after ${retries.length}` + - ` retries: ${String(errref?.err ?? "???")}` + ` retries: ${String(errref?.err ?? "???")}` ); } const waitMs = retries[retryidx]; @@ -1615,7 +1616,7 @@ async function checkExpectedSolanaLegacyTransactionStatus( debug( "checkExpectedSolanaLegacyTransactionStatus", `Retrying Rango Solana unsigned legacy transaction simulation` + - ` with updated block hash ${latestBlockHash.blockhash}...` + ` with updated block hash ${latestBlockHash.blockhash}...` ); legacyTx.recentBlockhash = latestBlockHash.blockhash; abortable?.signal?.throwIfAborted(); @@ -1671,7 +1672,7 @@ async function checkExpectedSolanaVersionedTransactionStatus( debug( "checkExpectedSolanaVersionedTransactionStatus", `Retrying Rango Solana unsigned versioned transaction simulation` + - ` with updated block hash ${latestBlockHash.blockhash}...` + ` with updated block hash ${latestBlockHash.blockhash}...` ); versionedTx.message.recentBlockhash = latestBlockHash.blockhash; } diff --git a/packages/swap/src/providers/zerox/index.ts b/packages/swap/src/providers/zerox/index.ts index 20cc1e72f..aecbf2d1a 100644 --- a/packages/swap/src/providers/zerox/index.ts +++ b/packages/swap/src/providers/zerox/index.ts @@ -165,7 +165,8 @@ class ZeroX extends ProviderClass { affiliateAddress: feeConfig ? feeConfig.referrer : "", }); return fetch( - `${BASE_URL}${supportedNetworks[this.network].chainId + `${BASE_URL}${ + supportedNetworks[this.network].chainId }/swap/v1/quote?${params.toString()}` ) .then((res) => res.json()) diff --git a/packages/swap/src/types/index.ts b/packages/swap/src/types/index.ts index 09e3da2ab..d8e0aa312 100644 --- a/packages/swap/src/types/index.ts +++ b/packages/swap/src/types/index.ts @@ -223,10 +223,10 @@ export interface ProviderQuoteResponse { export type StatusOptionTransaction = { /** Transaction hash */ - hash: string, + hash: string; /** Unix epoch milliseconds `Date.now()` */ - sentAt: number, -} + sentAt: number; +}; export interface StatusOptions { [key: string]: any; diff --git a/packages/swap/tests/zerox.test.ts b/packages/swap/tests/zerox.test.ts index e7373be80..bedc9f507 100644 --- a/packages/swap/tests/zerox.test.ts +++ b/packages/swap/tests/zerox.test.ts @@ -25,10 +25,10 @@ describe("Zerox Provider", () => { if (process.env.CI) { // We need at-least one test otherwise vitest reports failure - it('No ZeroX swap tests in CI', function() { - expect(true).toBeTruthy() - }) - return + it("No ZeroX swap tests in CI", function () { + expect(true).toBeTruthy(); + }); + return; } it( From 87d808c505dd8a3cceae4b24d4927199f05cba75 Mon Sep 17 00:00:00 2001 From: nickkelly1 Date: Wed, 13 Nov 2024 12:21:34 -0600 Subject: [PATCH 3/6] fix: swap activity --- .../src/ui/action/views/swap/views/swap-best-offer/index.vue | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/extension/src/ui/action/views/swap/views/swap-best-offer/index.vue b/packages/extension/src/ui/action/views/swap/views/swap-best-offer/index.vue index 329e08956..6ef68a35b 100644 --- a/packages/extension/src/ui/action/views/swap/views/swap-best-offer/index.vue +++ b/packages/extension/src/ui/action/views/swap/views/swap-best-offer/index.vue @@ -421,7 +421,7 @@ const sendAction = async () => { timestamp: new Date().getTime(), type: ActivityType.swap, value: pickedTrade.value.toTokenAmount.toString(), - transactionHash: `${txs[0]}-swap`, + transactionHash: `${txs[0].hash}-swap`, rawInfo: JSON.parse(JSON.stringify(swapRaw)), }; const activityState = new ActivityState(); From 35775ee489312677459667370d229630fc0bfc2e Mon Sep 17 00:00:00 2001 From: nickkelly1 Date: Thu, 14 Nov 2024 14:08:38 -0600 Subject: [PATCH 4/6] feat: transaction and swap dropped status --- .../src/libs/activity-state/index.ts | 7 + .../src/providers/solana/libs/api.ts | 47 +++-- packages/extension/src/types/activity.ts | 15 +- .../network-activity-transaction.vue | 31 ++-- .../action/views/network-activity/index.vue | 160 ++++++++++++------ .../views/swap/libs/send-transactions.ts | 2 +- packages/swap/src/providers/jupiter/index.ts | 49 +++--- packages/swap/src/types/index.ts | 1 + 8 files changed, 194 insertions(+), 118 deletions(-) diff --git a/packages/extension/src/libs/activity-state/index.ts b/packages/extension/src/libs/activity-state/index.ts index ff9c339c8..735c91e29 100644 --- a/packages/extension/src/libs/activity-state/index.ts +++ b/packages/extension/src/libs/activity-state/index.ts @@ -61,6 +61,7 @@ class ActivityState { this.getActivityId(options), ); } + async updateActivity( activity: Activity, options: ActivityOptions, @@ -75,11 +76,13 @@ class ActivityState { }); await this.setActivitiesById(clone, this.getActivityId(options)); } + async setCacheTime(options: ActivityOptions): Promise { await this.#storage.set(this.getActivityCacheId(options), { [STORAGE_KEY]: new Date().getTime(), }); } + async getCacheTime(options: ActivityOptions): Promise { const cacheTime: Record = await this.#storage.get( this.getActivityCacheId(options), @@ -87,12 +90,15 @@ class ActivityState { if (!cacheTime || !cacheTime[STORAGE_KEY]) return 0; return cacheTime[STORAGE_KEY]; } + async getAllActivities(options: ActivityOptions): Promise { return this.getActivitiesById(this.getActivityId(options)); } + async deleteAllActivities(options: ActivityOptions): Promise { this.setActivitiesById([], this.getActivityId(options)); } + private async setActivitiesById( activities: Activity[], id: string, @@ -101,6 +107,7 @@ class ActivityState { [STORAGE_KEY]: activities, }); } + private async getActivitiesById(id: string): Promise { const allStates: Record = await this.#storage.get(id); if (!allStates || !allStates[STORAGE_KEY]) return []; diff --git a/packages/extension/src/providers/solana/libs/api.ts b/packages/extension/src/providers/solana/libs/api.ts index e7d404a93..0aac4ed25 100644 --- a/packages/extension/src/providers/solana/libs/api.ts +++ b/packages/extension/src/providers/solana/libs/api.ts @@ -23,36 +23,51 @@ class API implements ProviderAPIInterface { return getSolAddress(pubkey); } - async init(): Promise {} + async init(): Promise { } + + /** + * Returns null if the transaction hasn't been received by the node + * or has been dropped + * + * Sometimes Solana transactions get dropped because there's too much + * network activity, in which case + */ async getTransactionStatus(hash: string): Promise { - return this.web3 - .getTransaction(hash, { - maxSupportedTransactionVersion: 0, - commitment: 'confirmed', - }) - .then(tx => { - if (!tx) return null; - const retVal: SOLRawInfo = { - blockNumber: tx.slot, - timestamp: tx.blockTime, - transactionHash: hash, - status: tx.meta?.err ? false : true, - }; - return retVal; - }); + const tx = await this.web3.getTransaction(hash, { + maxSupportedTransactionVersion: 0, + commitment: 'confirmed', + }) + + if (!tx) { + // Transaction hasn't been picked up by the node + // (maybe it's too soon, or maybe node is behind, or maybe it's been dropped) + return null; + } + + const retVal: SOLRawInfo = { + blockNumber: tx.slot, + timestamp: tx.blockTime, + transactionHash: hash, + status: tx.meta?.err ? false : true, + }; + + return retVal; } + async getBalance(pubkey: string): Promise { const balance = await this.web3.getBalance( new PublicKey(this.getAddress(pubkey)), ); return numberToHex(balance); } + async broadcastTx(rawtx: string): Promise { return this.web3 .sendRawTransaction(hexToBuffer(rawtx)) .then(() => true) .catch(() => false); } + getTokenInfo = async (contractAddress: string): Promise => { interface TokenDetails { address: string; diff --git a/packages/extension/src/types/activity.ts b/packages/extension/src/types/activity.ts index 89db8af7d..1c7511af8 100644 --- a/packages/extension/src/types/activity.ts +++ b/packages/extension/src/types/activity.ts @@ -95,6 +95,7 @@ enum ActivityStatus { pending = 'pending', success = 'success', failed = 'failed', + dropped = 'dropped', } enum ActivityType { @@ -121,13 +122,13 @@ interface Activity { status: ActivityStatus; type: ActivityType; rawInfo?: - | EthereumRawInfo - | SubstrateRawInfo - | SubscanExtrinsicInfo - | BTCRawInfo - | SwapRawInfo - | KadenaRawInfo - | SOLRawInfo; + | EthereumRawInfo + | SubstrateRawInfo + | SubscanExtrinsicInfo + | BTCRawInfo + | SwapRawInfo + | KadenaRawInfo + | SOLRawInfo; } export { diff --git a/packages/extension/src/ui/action/views/network-activity/components/network-activity-transaction.vue b/packages/extension/src/ui/action/views/network-activity/components/network-activity-transaction.vue index 1f4795de8..23a389038 100644 --- a/packages/extension/src/ui/action/views/network-activity/components/network-activity-transaction.vue +++ b/packages/extension/src/ui/action/views/network-activity/components/network-activity-transaction.vue @@ -37,7 +37,10 @@

{{ status }} {{ status }} { if ( props.activity.status === ActivityStatus.success && props.activity.isIncoming - ) + ) { status.value = props.activity.type === ActivityType.transaction ? 'Received' : 'Swapped'; - else if ( + } else if ( props.activity.status === ActivityStatus.success && !props.activity.isIncoming - ) + ) { status.value = props.activity.type === ActivityType.transaction ? 'Sent' : 'Swapped'; - else if ( + } else if ( props.activity.status === ActivityStatus.pending && props.activity.isIncoming - ) + ) { status.value = props.activity.type === ActivityType.transaction ? 'Receiving' : 'Swapping'; - else if ( + } else if ( props.activity.status === ActivityStatus.pending && !props.activity.isIncoming - ) + ) { status.value = props.activity.type === ActivityType.transaction ? 'Sending' : 'Swapping'; - else { + } else if (props.activity.status === ActivityStatus.dropped) { + status.value = 'Dropped'; + } else { status.value = 'Failed'; } }); @@ -256,6 +264,9 @@ onMounted(() => { .error { color: @error; } + .dropped { + /* TODO: Consider different color */ + } } } diff --git a/packages/extension/src/ui/action/views/network-activity/index.vue b/packages/extension/src/ui/action/views/network-activity/index.vue index e15b39ada..5a9b9f243 100644 --- a/packages/extension/src/ui/action/views/network-activity/index.vue +++ b/packages/extension/src/ui/action/views/network-activity/index.vue @@ -116,10 +116,13 @@ apiPromise.then(api => { }); }); -const activityCheckTimers: any[] = []; +/** Intervals that trigger calls to check for updates in transaction activity */ +const activityCheckTimers: ReturnType[] = []; + const activityAddress = computed(() => props.network.displayAddress(props.accountInfo.selectedAccount!.address), ); + const updateVisibleActivity = (activity: Activity): void => { activities.value.forEach((act, idx) => { if (act.transactionHash === activity.transactionHash) { @@ -129,96 +132,135 @@ const updateVisibleActivity = (activity: Activity): void => { forceUpdateVal.value++; }; +/** Set a timer to periodically check (and update) the status of an activity item (transaction) */ const checkActivity = (activity: Activity): void => { activity = toRaw(activity); const timer = setInterval(() => { apiPromise.then(api => { api.getTransactionStatus(activity.transactionHash).then(info => { - getInfo(activity, info, timer); + handleActivityUpdate(activity, info, timer); }); }); - }, 5000); + }, 5_000); + + // Register the interval timer so we can destroy it on component teardown activityCheckTimers.push(timer); }; -const getInfo = (activity: Activity, info: any, timer: any) => { - if (info) { - if (props.network.provider === ProviderName.ethereum) { - const evmInfo = info as EthereumRawInfo; - activity.status = evmInfo.status + +const handleActivityUpdate = (activity: Activity, info: any, timer: any) => { + if (props.network.provider === ProviderName.ethereum) { + if (!info) return; + const evmInfo = info as EthereumRawInfo; + activity.status = evmInfo.status + ? ActivityStatus.success + : ActivityStatus.failed; + activity.rawInfo = evmInfo; + activityState + .updateActivity(activity, { + address: activityAddress.value, + network: props.network.name, + }) + .then(() => updateVisibleActivity(activity)); + } else if (props.network.provider === ProviderName.polkadot) { + if (!info) return; + const subInfo = info as SubscanExtrinsicInfo; + if (!subInfo.pending) { + activity.status = subInfo.success ? ActivityStatus.success : ActivityStatus.failed; - activity.rawInfo = evmInfo; + activity.rawInfo = subInfo; activityState .updateActivity(activity, { address: activityAddress.value, network: props.network.name, }) .then(() => updateVisibleActivity(activity)); - } else if (props.network.provider === ProviderName.polkadot) { - const subInfo = info as SubscanExtrinsicInfo; - if (!subInfo.pending) { - activity.status = subInfo.success - ? ActivityStatus.success - : ActivityStatus.failed; - activity.rawInfo = subInfo; - activityState - .updateActivity(activity, { - address: activityAddress.value, - network: props.network.name, - }) - .then(() => updateVisibleActivity(activity)); - } - } else if (props.network.provider === ProviderName.bitcoin) { - const btcInfo = info as BTCRawInfo; - activity.status = ActivityStatus.success; - activity.rawInfo = btcInfo; - activityState - .updateActivity(activity, { - address: activityAddress.value, - network: props.network.name, - }) - .then(() => updateVisibleActivity(activity)); - } else if (props.network.provider === ProviderName.kadena) { - const kadenaInfo = info as KadenaRawInfo; - activity.status = - kadenaInfo.result.status == 'success' - ? ActivityStatus.success - : ActivityStatus.failed; - activity.rawInfo = kadenaInfo as KadenaRawInfo; + } + } else if (props.network.provider === ProviderName.bitcoin) { + if (!info) return; + const btcInfo = info as BTCRawInfo; + activity.status = ActivityStatus.success; + activity.rawInfo = btcInfo; + activityState + .updateActivity(activity, { + address: activityAddress.value, + network: props.network.name, + }) + .then(() => updateVisibleActivity(activity)); + } else if (props.network.provider === ProviderName.kadena) { + if (!info) return; + const kadenaInfo = info as KadenaRawInfo; + activity.status = + kadenaInfo.result.status == 'success' + ? ActivityStatus.success + : ActivityStatus.failed; + activity.rawInfo = kadenaInfo as KadenaRawInfo; + activityState + .updateActivity(activity, { + address: activityAddress.value, + network: props.network.name, + }) + .then(() => updateVisibleActivity(activity)); + } else if (props.network.provider === ProviderName.solana) { + if (info) { + console.log('[[ ??? doing something ??? ]]', info); + const solInfo = info as SOLRawInfo; + activity.status = info.status + ? ActivityStatus.success + : ActivityStatus.failed; + activity.rawInfo = solInfo; activityState .updateActivity(activity, { address: activityAddress.value, network: props.network.name, }) .then(() => updateVisibleActivity(activity)); - } else if (props.network.provider === ProviderName.solana) { - const solInfo = info as SOLRawInfo; - activity.status = info.status - ? ActivityStatus.success - : ActivityStatus.failed; - activity.rawInfo = solInfo; + } else if (Date.now() > activity.timestamp + 3 * 60_000) { + // Either our node is behind or the transaction was dropped + // Consider the transaction expired + activity.status = ActivityStatus.dropped; activityState .updateActivity(activity, { address: activityAddress.value, network: props.network.name, }) .then(() => updateVisibleActivity(activity)); + } else { + return; /* Give the transaction more time to be mined */ } - clearInterval(timer); } + + // If we're this far in then the transaction has reached a terminal status + // No longer need to check this activity + clearInterval(timer); }; + const checkSwap = (activity: Activity): void => { activity = toRaw(activity); const timer = setInterval(() => { if (swap) { swap.initPromise.then(() => { swap.getStatus((activity.rawInfo as SwapRawInfo).status).then(info => { - if (info === TransactionStatus.pending) return; - activity.status = - info === TransactionStatus.success - ? ActivityStatus.success - : ActivityStatus.failed; + switch (info) { + case TransactionStatus.pending: + // noop + return; + case TransactionStatus.success: + activity.status = ActivityStatus.success; + break; + case TransactionStatus.failed: + case null: + activity.status = ActivityStatus.failed; + break; + case TransactionStatus.dropped: + activity.status = ActivityStatus.dropped; + break; + default: + info satisfies never; + console.error('Unknown swap status:', info); + return; + } activityState .updateActivity(activity, { address: activityAddress.value, @@ -241,15 +283,19 @@ const setActivities = () => { isNoActivity.value = all.length === 0; activities.value.forEach(act => { if ( - act.status === ActivityStatus.pending && + (act.status === ActivityStatus.pending || + act.status === ActivityStatus.dropped) && act.type === ActivityType.transaction - ) + ) { checkActivity(act); + } if ( - act.status === ActivityStatus.pending && + (act.status === ActivityStatus.pending || + act.status === ActivityStatus.dropped) && act.type === ActivityType.swap - ) + ) { checkSwap(act); + } }); }); else activities.value = []; @@ -259,9 +305,11 @@ watch([selectedAddress, selectedNetworkName], setActivities); onMounted(() => { setActivities(); activityCheckTimers.forEach(timer => clearInterval(timer)); + activityCheckTimers.length = 0; }); onUnmounted(() => { activityCheckTimers.forEach(timer => clearInterval(timer)); + activityCheckTimers.length = 0; }); diff --git a/packages/extension/src/ui/action/views/swap/libs/send-transactions.ts b/packages/extension/src/ui/action/views/swap/libs/send-transactions.ts index f7d2e8d31..192357bb7 100644 --- a/packages/extension/src/ui/action/views/swap/libs/send-transactions.ts +++ b/packages/extension/src/ui/action/views/swap/libs/send-transactions.ts @@ -200,7 +200,7 @@ export const executeSwap = async ( const hasThirdPartySignatures = // Serialized versioned transaction was already signed legacyTx.signatures.length > 1 || - // Need to apply third aprty signatures to the transaction + // Need to apply third party signatures to the transaction thirdPartySignatures.length > 0; if (hasThirdPartySignatures) { diff --git a/packages/swap/src/providers/jupiter/index.ts b/packages/swap/src/providers/jupiter/index.ts index 692f7f081..2e0cdc407 100644 --- a/packages/swap/src/providers/jupiter/index.ts +++ b/packages/swap/src/providers/jupiter/index.ts @@ -160,7 +160,7 @@ if (DEBUG) { }; } else { // eslint-disable-next-line @typescript-eslint/no-unused-vars, @typescript-eslint/no-empty-function - debug = () => {}; + debug = () => { }; } // Jupiter API Tokens @@ -373,8 +373,8 @@ export class Jupiter extends ProviderClass { debug( "querySwapInfo", `Referrer ATA already exists. No need to record additional rent fees.` + - ` ATA pubkey: ${referrerATAPubkey.toBase58()},` + - ` Source mint: ${srcMint.toBase58()}` + ` ATA pubkey: ${referrerATAPubkey.toBase58()},` + + ` Source mint: ${srcMint.toBase58()}` ); } else { // The referral fee ATA account needs to be created or else we can't receive fees for this transaction @@ -400,9 +400,9 @@ export class Jupiter extends ProviderClass { debug( "querySwapInfo", `Referrer ATA does not exist. Updating transaction with instruction to create it.` + - ` Referral ATA pubkey: ${referrerATAPubkey.toBase58()},` + - ` Rent: ${extraRentFees} lamports,` + - ` Total Rent: ${extraRentFees} lamports` + ` Referral ATA pubkey: ${referrerATAPubkey.toBase58()},` + + ` Rent: ${extraRentFees} lamports,` + + ` Total Rent: ${extraRentFees} lamports` ); } @@ -413,8 +413,8 @@ export class Jupiter extends ProviderClass { debug( "querySwapInfo", `Wallet destination mint ATA already exists. No need to record additional rent fees.` + - ` ATA pubkey: ${dstATAPubkey.toBase58()},` + - ` Destination mint: ${dstMint.toBase58()}` + ` ATA pubkey: ${dstATAPubkey.toBase58()},` + + ` Destination mint: ${dstMint.toBase58()}` ); } else { const extraRentFee = await this.conn.getMinimumBalanceForRentExemption( @@ -437,11 +437,11 @@ export class Jupiter extends ProviderClass { debug( "querySwapInfo", `Wallet destination mint ATA does not exist, registering custom instruction to create it.` + - ` Adding ATA rent to extra transaction fees.` + - ` ATA pubkey: ${dstATAPubkey.toBase58()},` + - ` Destination mint: ${dstMint.toBase58()},` + - ` Rent: ${extraRentFee} lamports,` + - ` Total rent: ${rentFees} lamports` + ` Adding ATA rent to extra transaction fees.` + + ` ATA pubkey: ${dstATAPubkey.toBase58()},` + + ` Destination mint: ${dstMint.toBase58()},` + + ` Rent: ${extraRentFee} lamports,` + + ` Total rent: ${rentFees} lamports` ); } @@ -475,7 +475,7 @@ export class Jupiter extends ProviderClass { debug( "getQuote", `ignoring quote request to network ${options.toToken.networkInfo.name},` + - ` cross network swaps not supported` + ` cross network swaps not supported` ); return null; } @@ -588,8 +588,7 @@ export class Jupiter extends ProviderClass { // Consider dropped (/failed) if it's still null after 3 minutes // (block hashes expire after 2 minutes so 3 minutes gives 1 minute of leeway) if (Date.now() > sentAt + 3 * 60_000) { - // TODO: consider adding a "dropped" status? - return TransactionStatus.failed; + return TransactionStatus.dropped; } // Transaction hasn't been picked up by the node yet @@ -705,8 +704,7 @@ async function getJupiterTokens(abortable?: { default: /* noop */ } throw new Error( - `Failed to get Jupiter tokens, HTTP response returned not-ok status ${ - res.status + `Failed to get Jupiter tokens, HTTP response returned not-ok status ${res.status } ${res.statusText || ""}: ${msg}` ); } @@ -724,8 +722,7 @@ async function getJupiterTokens(abortable?: { if (failed) throw err; debug( "getJupiterTokens", - `Failed to get Jupiter tokens on attempt ${backoffi + 1}/${ - backoff.length + `Failed to get Jupiter tokens on attempt ${backoffi + 1}/${backoff.length }: ${String(err)}` ); errRef ??= { err: err as Error }; @@ -884,8 +881,7 @@ async function getJupiterQuote( default: /* noop */ } throw new Error( - `Failed to get Jupiter quote, HTTP response returned not-ok status ${ - res.status + `Failed to get Jupiter quote, HTTP response returned not-ok status ${res.status } ${res.statusText || ""} at url ${url}: ${msg}` ); } @@ -902,8 +898,7 @@ async function getJupiterQuote( if (abortable?.signal?.aborted) throw abortable?.signal.reason; if (failed) throw err; console.warn( - `[getJupiterQuote] Failed to get Jupiter quote on attempt ${ - backoffi + 1 + `[getJupiterQuote] Failed to get Jupiter quote on attempt ${backoffi + 1 }/${backoff.length}: ${String(err)}` ); errRef ??= { err: err as Error }; @@ -1035,8 +1030,7 @@ async function getJupiterSwap( default: /* noop */ } throw new Error( - `Failed to get Jupiter swap, HTTP response returned not-ok status ${ - res.status + `Failed to get Jupiter swap, HTTP response returned not-ok status ${res.status } ${res.statusText || ""} at url ${url}: ${msg}` ); } @@ -1054,8 +1048,7 @@ async function getJupiterSwap( if (failed) throw err; debug( "getJupiterSwap", - `Failed to get Jupiter swap on attempt ${backoffi + 1}/${ - backoff.length + `Failed to get Jupiter swap on attempt ${backoffi + 1}/${backoff.length }: ${String(err)}` ); errRef ??= { err: err as Error }; diff --git a/packages/swap/src/types/index.ts b/packages/swap/src/types/index.ts index d8e0aa312..bb7173394 100644 --- a/packages/swap/src/types/index.ts +++ b/packages/swap/src/types/index.ts @@ -135,6 +135,7 @@ export enum TransactionStatus { pending = "pending", failed = "failed", success = "success", + dropped = "dropped", } export interface getQuoteOptions { From 41931cd498530624fae2509e2a786dede2ed664c Mon Sep 17 00:00:00 2001 From: nickkelly1 Date: Thu, 14 Nov 2024 14:45:16 -0600 Subject: [PATCH 5/6] chore: lint --- packages/swap/src/providers/jupiter/index.ts | 46 +++++++++++--------- 1 file changed, 26 insertions(+), 20 deletions(-) diff --git a/packages/swap/src/providers/jupiter/index.ts b/packages/swap/src/providers/jupiter/index.ts index 2e0cdc407..728e36753 100644 --- a/packages/swap/src/providers/jupiter/index.ts +++ b/packages/swap/src/providers/jupiter/index.ts @@ -160,7 +160,7 @@ if (DEBUG) { }; } else { // eslint-disable-next-line @typescript-eslint/no-unused-vars, @typescript-eslint/no-empty-function - debug = () => { }; + debug = () => {}; } // Jupiter API Tokens @@ -373,8 +373,8 @@ export class Jupiter extends ProviderClass { debug( "querySwapInfo", `Referrer ATA already exists. No need to record additional rent fees.` + - ` ATA pubkey: ${referrerATAPubkey.toBase58()},` + - ` Source mint: ${srcMint.toBase58()}` + ` ATA pubkey: ${referrerATAPubkey.toBase58()},` + + ` Source mint: ${srcMint.toBase58()}` ); } else { // The referral fee ATA account needs to be created or else we can't receive fees for this transaction @@ -400,9 +400,9 @@ export class Jupiter extends ProviderClass { debug( "querySwapInfo", `Referrer ATA does not exist. Updating transaction with instruction to create it.` + - ` Referral ATA pubkey: ${referrerATAPubkey.toBase58()},` + - ` Rent: ${extraRentFees} lamports,` + - ` Total Rent: ${extraRentFees} lamports` + ` Referral ATA pubkey: ${referrerATAPubkey.toBase58()},` + + ` Rent: ${extraRentFees} lamports,` + + ` Total Rent: ${extraRentFees} lamports` ); } @@ -413,8 +413,8 @@ export class Jupiter extends ProviderClass { debug( "querySwapInfo", `Wallet destination mint ATA already exists. No need to record additional rent fees.` + - ` ATA pubkey: ${dstATAPubkey.toBase58()},` + - ` Destination mint: ${dstMint.toBase58()}` + ` ATA pubkey: ${dstATAPubkey.toBase58()},` + + ` Destination mint: ${dstMint.toBase58()}` ); } else { const extraRentFee = await this.conn.getMinimumBalanceForRentExemption( @@ -437,11 +437,11 @@ export class Jupiter extends ProviderClass { debug( "querySwapInfo", `Wallet destination mint ATA does not exist, registering custom instruction to create it.` + - ` Adding ATA rent to extra transaction fees.` + - ` ATA pubkey: ${dstATAPubkey.toBase58()},` + - ` Destination mint: ${dstMint.toBase58()},` + - ` Rent: ${extraRentFee} lamports,` + - ` Total rent: ${rentFees} lamports` + ` Adding ATA rent to extra transaction fees.` + + ` ATA pubkey: ${dstATAPubkey.toBase58()},` + + ` Destination mint: ${dstMint.toBase58()},` + + ` Rent: ${extraRentFee} lamports,` + + ` Total rent: ${rentFees} lamports` ); } @@ -475,7 +475,7 @@ export class Jupiter extends ProviderClass { debug( "getQuote", `ignoring quote request to network ${options.toToken.networkInfo.name},` + - ` cross network swaps not supported` + ` cross network swaps not supported` ); return null; } @@ -704,7 +704,8 @@ async function getJupiterTokens(abortable?: { default: /* noop */ } throw new Error( - `Failed to get Jupiter tokens, HTTP response returned not-ok status ${res.status + `Failed to get Jupiter tokens, HTTP response returned not-ok status ${ + res.status } ${res.statusText || ""}: ${msg}` ); } @@ -722,7 +723,8 @@ async function getJupiterTokens(abortable?: { if (failed) throw err; debug( "getJupiterTokens", - `Failed to get Jupiter tokens on attempt ${backoffi + 1}/${backoff.length + `Failed to get Jupiter tokens on attempt ${backoffi + 1}/${ + backoff.length }: ${String(err)}` ); errRef ??= { err: err as Error }; @@ -881,7 +883,8 @@ async function getJupiterQuote( default: /* noop */ } throw new Error( - `Failed to get Jupiter quote, HTTP response returned not-ok status ${res.status + `Failed to get Jupiter quote, HTTP response returned not-ok status ${ + res.status } ${res.statusText || ""} at url ${url}: ${msg}` ); } @@ -898,7 +901,8 @@ async function getJupiterQuote( if (abortable?.signal?.aborted) throw abortable?.signal.reason; if (failed) throw err; console.warn( - `[getJupiterQuote] Failed to get Jupiter quote on attempt ${backoffi + 1 + `[getJupiterQuote] Failed to get Jupiter quote on attempt ${ + backoffi + 1 }/${backoff.length}: ${String(err)}` ); errRef ??= { err: err as Error }; @@ -1030,7 +1034,8 @@ async function getJupiterSwap( default: /* noop */ } throw new Error( - `Failed to get Jupiter swap, HTTP response returned not-ok status ${res.status + `Failed to get Jupiter swap, HTTP response returned not-ok status ${ + res.status } ${res.statusText || ""} at url ${url}: ${msg}` ); } @@ -1048,7 +1053,8 @@ async function getJupiterSwap( if (failed) throw err; debug( "getJupiterSwap", - `Failed to get Jupiter swap on attempt ${backoffi + 1}/${backoff.length + `Failed to get Jupiter swap on attempt ${backoffi + 1}/${ + backoff.length }: ${String(err)}` ); errRef ??= { err: err as Error }; From c0bbe9a4e3d5555b1272d07e10447f265f9f1c8f Mon Sep 17 00:00:00 2001 From: nickkelly1 Date: Thu, 14 Nov 2024 14:46:11 -0600 Subject: [PATCH 6/6] chore: cleanup --- packages/extension/src/providers/solana/libs/api.ts | 3 --- 1 file changed, 3 deletions(-) diff --git a/packages/extension/src/providers/solana/libs/api.ts b/packages/extension/src/providers/solana/libs/api.ts index 0aac4ed25..f4a18588c 100644 --- a/packages/extension/src/providers/solana/libs/api.ts +++ b/packages/extension/src/providers/solana/libs/api.ts @@ -28,9 +28,6 @@ class API implements ProviderAPIInterface { /** * Returns null if the transaction hasn't been received by the node * or has been dropped - * - * Sometimes Solana transactions get dropped because there's too much - * network activity, in which case */ async getTransactionStatus(hash: string): Promise { const tx = await this.web3.getTransaction(hash, {