From 0e05267e437b478a677a667e5f15b9eba3918595 Mon Sep 17 00:00:00 2001 From: nickkelly1 Date: Wed, 13 Nov 2024 12:14:03 -0600 Subject: [PATCH 01/30] 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 02/30] 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 03/30] 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 04/30] 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 05/30] 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 06/30] 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, { From a28a95a6f9b580f9743e12e606b7774505467627 Mon Sep 17 00:00:00 2001 From: nickkelly1 Date: Fri, 15 Nov 2024 13:49:47 -0600 Subject: [PATCH 07/30] feat: add base to rango and changelly --- .../swap/src/providers/changelly/supported.ts | 4 + packages/swap/src/providers/rango/index.ts | 180 +---------------- .../swap/src/providers/rango/supported.ts | 182 ++++++++++++++++++ 3 files changed, 191 insertions(+), 175 deletions(-) create mode 100644 packages/swap/src/providers/rango/supported.ts diff --git a/packages/swap/src/providers/changelly/supported.ts b/packages/swap/src/providers/changelly/supported.ts index 0ff55132d..143e5cc1a 100644 --- a/packages/swap/src/providers/changelly/supported.ts +++ b/packages/swap/src/providers/changelly/supported.ts @@ -27,6 +27,10 @@ const supportedNetworks: { changellyName: "binance_smart_chain", isAddress: (address: string) => Promise.resolve(isEVMAddress(address)), }, + [SupportedNetworkName.Base]: { + changellyName: "BASE", + isAddress: (address: string) => Promise.resolve(isEVMAddress(address)), + }, [SupportedNetworkName.Matic]: { changellyName: "polygon", isAddress: (address: string) => Promise.resolve(isEVMAddress(address)), diff --git a/packages/swap/src/providers/rango/index.ts b/packages/swap/src/providers/rango/index.ts index adc5ea683..49a30944a 100644 --- a/packages/swap/src/providers/rango/index.ts +++ b/packages/swap/src/providers/rango/index.ts @@ -63,6 +63,11 @@ import { RangoNetworkedTransactions, RangoSwapResponse } from "./types"; import { TOKEN_AMOUNT_INFINITY_AND_BEYOND } from "../../utils/approvals"; import estimateEVMGasList from "../../common/estimateGasList"; import { isEVMAddress } from "../../utils/common"; +import { + supportedNetworkByRangoBlockchain, + SupportedNetworkInfo, + supportedNetworkInfoByName, +} from "./supported"; /** Enables debug logging in this file */ const DEBUG = false; @@ -103,181 +108,6 @@ if (DEBUG) { debug = () => {}; } -type SupportedNetworkInfo = { - /** Standard base10 chain ID, can be obtained from `https://chainlist.org` */ - realChainId: string; - /** Rango's chainId for Solana is "mainnet-beta" */ - rangoChainId: string; - /** Rango blockchain name (Rango's identifier for the chain) of a network */ - rangoBlockchain: string; -}; - -/** - * `name` is the blockchain id on Rango - * - * You can use the Rango API to get a list of tokens to figure out the Rango name of a network - * - * @see https://rango-api.readme.io/reference/meta - * - * ```sh - * # Rango token meta (list of all tokens with token metadata, blockchain info, etc) - * curl 'https://api.rango.exchange/basic/meta?apiKey=c6381a79-2817-4602-83bf-6a641a409e32' -sL -H 'Accept:application/json' | jq . - * # { - * # "tokens": [ - * # { - * # "blockchain": "ETH", - * # "symbol": "USDT", - * # "name": "USD Tether", - * # "isPopular": true, - * # "chainId": "1", - * # "address": "0xdac17f958d2ee523a2206206994597c13d831ec7", - * # "decimals": 6, - * # "image": "https://rango.vip/i/r3Oex6", - * # "blockchainImage": "https://raw.githubusercontent.com/rango-exchange/assets/main/blockchains/ETH/icon.svg", - * # "usdPrice": 1.001, - * # "supportedSwappers": [ - * # "Arbitrum Bridge", - * # "ThorChain", - * # ... - * - * # Rango token count per blockchain - * curl 'https://api.rango.exchange/basic/meta?apiKey=c6381a79-2817-4602-83bf-6a641a409e32' -sL -H 'Accept:application/json' | jq --raw-output .tokens[].blockchain | sort | uniq -c | sort -n - * # count blockchain - * # ... - * # 36 MOONBEAM - * # 42 CELO - * # 48 OKC - * # 50 MOONRIVER - * # 55 AURORA - * # 56 LINEA - * # 58 ZKSYNC - * # 61 BLAST - * # 146 OSMOSIS - * # 147 HECO - * # 158 CRONOS - * # 301 OPTIMISM - * # 368 AVAX_CCHAIN - * # 437 BASE - * # 594 POLYGON - * # 596 ARBITRUM - * # 833 BSC - * # 1509 SOLANA - * # 5610 ETH - * - * # Rango token count per blockchain & chain id - * curl 'https://api.rango.exchange/basic/meta?apiKey=c6381a79-2817-4602-83bf-6a641a409e32' -sL -H 'Accept:application/json' | jq -r '.tokens[] | "\(.blockchain)\t\(.chainId)"' | sort | uniq -c | sort -n | sed 's/^ *\([0-9]*\) *\(.*\)/\1\t\2/' | column -s $'\t' -t - * # count blockchain chain id - * # ... - * # 50 MOONRIVER 1285 - * # 55 AURORA 1313161554 - * # 56 LINEA 59144 - * # 58 ZKSYNC 324 - * # 61 BLAST 81457 - * # 146 OSMOSIS osmosis-1 - * # 147 HECO 128 - * # 158 CRONOS 25 - * # 301 OPTIMISM 10 - * # 368 AVAX_CCHAIN 43114 - * # 437 BASE 8453 - * # 594 POLYGON 137 - * # 596 ARBITRUM 42161 - * # 833 BSC 56 - * # 1509 SOLANA mainnet-beta - * # 5610 ETH 1 - * ``` - */ -const supportedNetworks: Readonly<{ - [key in SupportedNetworkName]?: SupportedNetworkInfo; -}> = { - [SupportedNetworkName.Ethereum]: { - realChainId: "1", - rangoChainId: "1", - rangoBlockchain: "ETH", - }, - [SupportedNetworkName.Binance]: { - realChainId: "56", - rangoChainId: "56", - rangoBlockchain: "BSC", - }, - [SupportedNetworkName.Matic]: { - realChainId: "137", - rangoChainId: "137", - rangoBlockchain: "POLYGON", - }, - [SupportedNetworkName.Optimism]: { - realChainId: "10", - rangoChainId: "10", - rangoBlockchain: "OPTIMISM", - }, - [SupportedNetworkName.Avalanche]: { - realChainId: "43114", - rangoChainId: "43114", - rangoBlockchain: "AVAX_CCHAIN", - }, - [SupportedNetworkName.Fantom]: { - realChainId: "250", - rangoChainId: "250", - rangoBlockchain: "FANTOM", - }, - [SupportedNetworkName.Aurora]: { - realChainId: "1313161554", - rangoChainId: "1313161554", - rangoBlockchain: "AURORA", - }, - [SupportedNetworkName.Gnosis]: { - realChainId: "100", - rangoChainId: "100", - rangoBlockchain: "GNOSIS", - }, - [SupportedNetworkName.Arbitrum]: { - realChainId: "42161", - rangoChainId: "42161", - rangoBlockchain: "ARBITRUM", - }, - [SupportedNetworkName.Moonbeam]: { - realChainId: "1284", - rangoChainId: "1284", - rangoBlockchain: "MOONBEAM", - }, - [SupportedNetworkName.Solana]: { - realChainId: "900", - rangoChainId: "mainnet-beta", - rangoBlockchain: "SOLANA", - }, - [SupportedNetworkName.Blast]: { - realChainId: "81457", - rangoChainId: "81457", - rangoBlockchain: "BLAST", - }, - [SupportedNetworkName.Telos]: { - realChainId: "40", - rangoChainId: "40", - rangoBlockchain: "TELOS", - }, -}; - -// Freeze because we index below so modifications would make the indexes stale -Object.freeze(supportedNetworks); - -/** Enkrypt supported network name -> network info */ -const supportedNetworkInfoByName = new Map( - Object.entries(supportedNetworks), -) as unknown as Map; - -/** Rango blockchain name -> network info & enkrypt network name */ -const supportedNetworkByRangoBlockchain = new Map< - string, - { info: SupportedNetworkInfo; name: SupportedNetworkName } ->( - Object.entries(supportedNetworks).map(([supportedNetwork, networkInfo]) => [ - networkInfo.rangoBlockchain, - { - info: networkInfo, - name: supportedNetwork as unknown as SupportedNetworkName, - }, - ]), -); - type RangoEnkryptToken = { rangoMeta: RangoToken; token?: TokenType; diff --git a/packages/swap/src/providers/rango/supported.ts b/packages/swap/src/providers/rango/supported.ts new file mode 100644 index 000000000..0b4f5fb10 --- /dev/null +++ b/packages/swap/src/providers/rango/supported.ts @@ -0,0 +1,182 @@ +import { SupportedNetworkName, } from '../../types' + +export type SupportedNetworkInfo = { + /** Standard base10 chain ID, can be obtained from `https://chainlist.org` */ + realChainId: string; + /** Rango's chainId for Solana is "mainnet-beta" */ + rangoChainId: string; + /** Rango blockchain name (Rango's identifier for the chain) of a network */ + rangoBlockchain: string; +}; + +/** + * `name` is the blockchain id on Rango + * + * You can use the Rango API to get a list of tokens to figure out the Rango name of a network + * + * @see https://rango-api.readme.io/reference/meta + * + * ```sh + * # Rango token meta (list of all tokens with token metadata, blockchain info, etc) + * curl 'https://api.rango.exchange/basic/meta?apiKey=c6381a79-2817-4602-83bf-6a641a409e32' -sL -H 'Accept:application/json' | jq . + * # { + * # "tokens": [ + * # { + * # "blockchain": "ETH", + * # "symbol": "USDT", + * # "name": "USD Tether", + * # "isPopular": true, + * # "chainId": "1", + * # "address": "0xdac17f958d2ee523a2206206994597c13d831ec7", + * # "decimals": 6, + * # "image": "https://rango.vip/i/r3Oex6", + * # "blockchainImage": "https://raw.githubusercontent.com/rango-exchange/assets/main/blockchains/ETH/icon.svg", + * # "usdPrice": 1.001, + * # "supportedSwappers": [ + * # "Arbitrum Bridge", + * # "ThorChain", + * # ... + * + * # Rango token count per blockchain + * curl 'https://api.rango.exchange/basic/meta?apiKey=c6381a79-2817-4602-83bf-6a641a409e32' -sL -H 'Accept:application/json' | jq --raw-output .tokens[].blockchain | sort | uniq -c | sort -n + * # count blockchain + * # ... + * # 36 MOONBEAM + * # 42 CELO + * # 48 OKC + * # 50 MOONRIVER + * # 55 AURORA + * # 56 LINEA + * # 58 ZKSYNC + * # 61 BLAST + * # 146 OSMOSIS + * # 147 HECO + * # 158 CRONOS + * # 301 OPTIMISM + * # 368 AVAX_CCHAIN + * # 437 BASE + * # 594 POLYGON + * # 596 ARBITRUM + * # 833 BSC + * # 1509 SOLANA + * # 5610 ETH + * + * # Rango token count per blockchain & chain id + * curl 'https://api.rango.exchange/basic/meta?apiKey=c6381a79-2817-4602-83bf-6a641a409e32' -sL -H 'Accept:application/json' | jq -r '.tokens[] | "\(.blockchain)\t\(.chainId)"' | sort | uniq -c | sort -n | sed 's/^ *\([0-9]*\) *\(.*\)/\1\t\2/' | column -s $'\t' -t + * # count blockchain chain id + * # ... + * # 50 MOONRIVER 1285 + * # 55 AURORA 1313161554 + * # 56 LINEA 59144 + * # 58 ZKSYNC 324 + * # 61 BLAST 81457 + * # 146 OSMOSIS osmosis-1 + * # 147 HECO 128 + * # 158 CRONOS 25 + * # 301 OPTIMISM 10 + * # 368 AVAX_CCHAIN 43114 + * # 437 BASE 8453 + * # 594 POLYGON 137 + * # 596 ARBITRUM 42161 + * # 833 BSC 56 + * # 1509 SOLANA mainnet-beta + * # 5610 ETH 1 + * ``` + */ +const supportedNetworks: Readonly<{ + [key in SupportedNetworkName]?: SupportedNetworkInfo; +}> = { + [SupportedNetworkName.Ethereum]: { + realChainId: "1", + rangoChainId: "1", + rangoBlockchain: "ETH", + }, + [SupportedNetworkName.Binance]: { + realChainId: "56", + rangoChainId: "56", + rangoBlockchain: "BSC", + }, + [SupportedNetworkName.Base]: { + realChainId: "8453", + rangoChainId: "8453", + rangoBlockchain: "BASE", + }, + [SupportedNetworkName.Matic]: { + realChainId: "137", + rangoChainId: "137", + rangoBlockchain: "POLYGON", + }, + [SupportedNetworkName.Optimism]: { + realChainId: "10", + rangoChainId: "10", + rangoBlockchain: "OPTIMISM", + }, + [SupportedNetworkName.Avalanche]: { + realChainId: "43114", + rangoChainId: "43114", + rangoBlockchain: "AVAX_CCHAIN", + }, + [SupportedNetworkName.Fantom]: { + realChainId: "250", + rangoChainId: "250", + rangoBlockchain: "FANTOM", + }, + [SupportedNetworkName.Aurora]: { + realChainId: "1313161554", + rangoChainId: "1313161554", + rangoBlockchain: "AURORA", + }, + [SupportedNetworkName.Gnosis]: { + realChainId: "100", + rangoChainId: "100", + rangoBlockchain: "GNOSIS", + }, + [SupportedNetworkName.Arbitrum]: { + realChainId: "42161", + rangoChainId: "42161", + rangoBlockchain: "ARBITRUM", + }, + [SupportedNetworkName.Moonbeam]: { + realChainId: "1284", + rangoChainId: "1284", + rangoBlockchain: "MOONBEAM", + }, + [SupportedNetworkName.Solana]: { + realChainId: "900", + rangoChainId: "mainnet-beta", + rangoBlockchain: "SOLANA", + }, + [SupportedNetworkName.Blast]: { + realChainId: "81457", + rangoChainId: "81457", + rangoBlockchain: "BLAST", + }, + [SupportedNetworkName.Telos]: { + realChainId: "40", + rangoChainId: "40", + rangoBlockchain: "TELOS", + }, +}; + +// Freeze because we index below so modifications would make the indexes stale +Object.freeze(supportedNetworks); + +/** Enkrypt supported network name -> network info */ +export const supportedNetworkInfoByName = new Map( + Object.entries(supportedNetworks), +) as unknown as Map; + +/** Rango blockchain name -> network info & enkrypt network name */ +export const supportedNetworkByRangoBlockchain = new Map< + string, + { info: SupportedNetworkInfo; name: SupportedNetworkName } +>( + Object.entries(supportedNetworks).map(([supportedNetwork, networkInfo]) => [ + networkInfo.rangoBlockchain, + { + info: networkInfo, + name: supportedNetwork as unknown as SupportedNetworkName, + }, + ]), +); + From 71749a226262e97b75e72002c8ad185c7695d1e5 Mon Sep 17 00:00:00 2001 From: nickkelly1 Date: Wed, 20 Nov 2024 20:39:36 -0600 Subject: [PATCH 08/30] fix: dropped jupiter sol swap transactions --- packages/swap/src/providers/jupiter/index.ts | 15 ++++-- packages/swap/src/providers/jupiter/types.ts | 9 +++- packages/swap/src/utils/solana.ts | 52 ++++++++++++++++++++ 3 files changed, 71 insertions(+), 5 deletions(-) diff --git a/packages/swap/src/providers/jupiter/index.ts b/packages/swap/src/providers/jupiter/index.ts index ef92eb65d..50ff8b9cf 100644 --- a/packages/swap/src/providers/jupiter/index.ts +++ b/packages/swap/src/providers/jupiter/index.ts @@ -616,7 +616,7 @@ async function getJupiterTokens(abortable?: { const url = JUPITER_TOKENS_URL; let failed = false; let tokens: JupiterTokenInfo[]; - const backoff = [0, 100, 500, 1000, 2_500, 5_000]; + const backoff = [0, 100, 500, 1000, 2_000, 4_000]; let backoffi = 0; let errRef: undefined | { err: Error }; @@ -795,7 +795,7 @@ async function getJupiterQuote( let failed = false; let quote: JupiterQuoteResponse; - const backoff = [0, 100, 500, 1000, 2_500, 5_000]; + const backoff = [0, 100, 500, 1000, 2_000, 4_000]; let backoffi = 0; let errRef: undefined | { err: Error }; @@ -936,12 +936,19 @@ async function getJupiterSwap( feeAccount: referrerATAPubkey?.toBase58(), quoteResponse: quote, destinationTokenAccount: dstATAPubkey?.toBase58(), + prioritizationFeeLamports: { + /** + * The automatic fee seems low and frequently causes transactions + * to be dropped when traffic is high + */ + autoMultiplier: 3, + }, }; const url = `${JUPITER_API_URL}swap`; let failed = false; let swap: JupiterSwapResponse; - const backoff = [0, 100, 500, 1000, 2_500, 5_000]; + const backoff = [0, 100, 500, 1000, 2_000, 4_000]; let backoffi = 0; let errRef: undefined | { err: Error }; @@ -1197,6 +1204,6 @@ function sleep( clearTimeout(timeout); } abortable?.signal?.addEventListener("abort", onAbortDuringSleep); - const timeout = setTimeout(onTimeout); + const timeout = setTimeout(onTimeout, duration); }); } diff --git a/packages/swap/src/providers/jupiter/types.ts b/packages/swap/src/providers/jupiter/types.ts index c776f6c14..614ac217d 100644 --- a/packages/swap/src/providers/jupiter/types.ts +++ b/packages/swap/src/providers/jupiter/types.ts @@ -87,7 +87,14 @@ export type JupiterSwapParams = { trackingAccount?: string; /** Integer */ computeUnitPriceMicroLamports?: number; - prioritizationFeeLamports?: number; + /** Integer */ + prioritizationFeeLamports?: + | number + | "auto" + | { + /** Integer */ + autoMultiplier: number; + }; /** Default: false */ asLegacyTransaction?: boolean; /** Default: false */ diff --git a/packages/swap/src/utils/solana.ts b/packages/swap/src/utils/solana.ts index e0e43aa3f..863d22671 100644 --- a/packages/swap/src/utils/solana.ts +++ b/packages/swap/src/utils/solana.ts @@ -118,6 +118,58 @@ export function extractComputeBudget( ); } +/** + * @see https://solana.com/docs/core/fees#prioritization-fees + */ +export function extractComputeUnitPriceMicroLamports( + tx: VersionedTransaction, +): undefined | number | bigint { + /** Compute unit price */ + let computeUnitPriceMicroLamports: undefined | number | bigint; + + // eslint-disable-next-line no-restricted-syntax, no-labels + instructionLoop: for ( + let i = 0, len = tx.message.compiledInstructions.length; + i < len; + i++ + ) { + const instr = tx.message.compiledInstructions[i]; + const program = tx.message.staticAccountKeys[instr.programIdIndex]; + if (!ComputeBudgetProgram.programId.equals(program)) continue; + + const keys = instr.accountKeyIndexes.map( + (accountKeyIndex): AccountMeta => ({ + pubkey: tx.message.staticAccountKeys[accountKeyIndex], + isSigner: tx.message.isAccountSigner(accountKeyIndex), + isWritable: tx.message.isAccountWritable(accountKeyIndex), + }), + ); + + // Decompile the instruction + const instruction = new TransactionInstruction({ + keys, + programId: program, + data: Buffer.from(instr.data), + }); + + const type = ComputeBudgetInstruction.decodeInstructionType(instruction); + switch (type) { + case "SetComputeUnitPrice": { + // Compute limit + const command = + ComputeBudgetInstruction.decodeSetComputeUnitPrice(instruction); + computeUnitPriceMicroLamports = command.microLamports; + // eslint-disable-next-line no-labels + break instructionLoop; + } + default: /** noop */ + break; + } + } + + return computeUnitPriceMicroLamports; +} + /** * Insert new instructions at the start of a transaction, after compute budget and compute limit instructions */ From 10dfeb06fead876f0cccc5d201765b7c467bba65 Mon Sep 17 00:00:00 2001 From: nickkelly1 Date: Thu, 21 Nov 2024 14:01:08 -0600 Subject: [PATCH 09/30] fix: increase jupiter swap priority fee multiplier --- packages/swap/src/providers/jupiter/index.ts | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/packages/swap/src/providers/jupiter/index.ts b/packages/swap/src/providers/jupiter/index.ts index 50ff8b9cf..2e1023006 100644 --- a/packages/swap/src/providers/jupiter/index.ts +++ b/packages/swap/src/providers/jupiter/index.ts @@ -936,12 +936,16 @@ async function getJupiterSwap( feeAccount: referrerATAPubkey?.toBase58(), quoteResponse: quote, destinationTokenAccount: dstATAPubkey?.toBase58(), + /** @see https://station.jup.ag/api-v6/post-swap */ prioritizationFeeLamports: { /** * The automatic fee seems low and frequently causes transactions * to be dropped when traffic is high + * + * This number has been arbitrary selected from manual testing @ 2024-11-21 + * where there's been a bunch of network activity causing dropped transactions */ - autoMultiplier: 3, + autoMultiplier: 6, }, }; From 4e7071a572c94b1d2af08520528bc817e90490e1 Mon Sep 17 00:00:00 2001 From: kvhnuke <10602065+kvhnuke@users.noreply.github.com> Date: Thu, 21 Nov 2024 15:11:03 -0800 Subject: [PATCH 10/30] fix: base max fee --- .../ethereum/libs/transaction/index.ts | 113 +++++++++++------- 1 file changed, 67 insertions(+), 46 deletions(-) diff --git a/packages/extension/src/providers/ethereum/libs/transaction/index.ts b/packages/extension/src/providers/ethereum/libs/transaction/index.ts index a0e740936..8936a04d0 100644 --- a/packages/extension/src/providers/ethereum/libs/transaction/index.ts +++ b/packages/extension/src/providers/ethereum/libs/transaction/index.ts @@ -38,14 +38,13 @@ class Transaction { value: this.tx.value || '0x0', }); } - async getOPfees(): Promise { + async getOPfees( + fTx: LegacyTransaction | FeeMarketEIP1559Transaction, + ): Promise { const OPContract = new this.web3.Contract( OPTIMISM_PRICE_ORACLE_ABI as any, OPTIMISM_PRICE_ORACLE, ); - const fTx = await this.getFinalizedTransaction({ - gasPriceType: GasPriceTypes.ECONOMY, - }); const serializedTx = fTx.serialize(); return OPContract.methods .getL1Fee(bufferToHex(serializedTx)) @@ -87,6 +86,7 @@ class Transaction { maxFeePerGas?: string; gasLimit: string; formattedFeeHistory?: FormattedFeeHistory; + finalizedTransaction: LegacyTransaction | FeeMarketEIP1559Transaction; }> { const latestBlock = await this.web3.getBlock('latest', false); const { isFeeMarketNetwork, feeHistory } = await this.web3 @@ -123,10 +123,20 @@ class Transaction { nonce: this.tx.nonce || (numberToHex(nonce) as `0x${string}`), value: this.tx.value || '0x0', }; + const common = Common.custom({ + chainId: BigInt(this.tx.chainId), + }); + const finalizedTransaction = LegacyTransaction.fromTxData( + legacyTx as FinalizedLegacyEthereumTransaction, + { + common, + }, + ); return { transaction: legacyTx, gasPrice: gasPrice, gasLimit: legacyTx.gasLimit, + finalizedTransaction, }; } else { // Fee market transaction (post EIP1559) @@ -141,7 +151,7 @@ class Transaction { const gasLimit = this.tx.gasLimit || (numberToHex(await this.estimateGas()) as `0x${string}`); - const maxFeePerGas = !options.totalGasPrice + let maxFeePerGas = !options.totalGasPrice ? feeMarket.maxFeePerGas : options.totalGasPrice.div(toBN(gasLimit)); const maxPriorityFeePerGas = feeMarket.maxPriorityFeePerGas; @@ -162,6 +172,35 @@ class Transaction { type: '0x02', accessList: this.tx.accessList || [], }; + const common = Common.custom({ + chainId: BigInt(this.tx.chainId), + defaultHardfork: Hardfork.London, + }); + let finalizedTransaction = FeeMarketEIP1559Transaction.fromTxData( + feeMarketTx as FinalizedFeeMarketEthereumTransaction, + { + common, + }, + ); + if (options.totalGasPrice) { + const opFee = await this.getOPfees(finalizedTransaction); + if (opFee.gtn(0)) { + const gasFeeWithoutOPFee = options.totalGasPrice.sub(opFee); + maxFeePerGas = gasFeeWithoutOPFee.div(toBN(gasLimit)); + feeMarketTx.maxFeePerGas = numberToHex(maxFeePerGas) as `0x${string}`; + feeMarketTx.maxPriorityFeePerGas = numberToHex( + maxPriorityFeePerGas.gt(maxFeePerGas) + ? maxFeePerGas + : maxPriorityFeePerGas, + ) as `0x${string}`; + finalizedTransaction = FeeMarketEIP1559Transaction.fromTxData( + feeMarketTx as FinalizedFeeMarketEthereumTransaction, + { + common, + }, + ); + } + } return { transaction: feeMarketTx, gasLimit: feeMarketTx.gasLimit, @@ -169,6 +208,7 @@ class Transaction { maxFeePerGas: numberToHex(feeMarket.maxFeePerGas), maxPriorityFeePerGas: numberToHex(feeMarket.maxPriorityFeePerGas), formattedFeeHistory, + finalizedTransaction, }; } } @@ -182,30 +222,8 @@ class Transaction { async getFinalizedTransaction( options: TransactionOptions, ): Promise { - const { transaction } = await this.finalizeTransaction(options); - - if (!transaction.maxFeePerGas) { - const common = Common.custom({ - chainId: BigInt(transaction.chainId), - }); - return LegacyTransaction.fromTxData( - transaction as FinalizedLegacyEthereumTransaction, - { - common, - }, - ); - } else { - const common = Common.custom({ - chainId: BigInt(transaction.chainId), - defaultHardfork: Hardfork.London, - }); - return FeeMarketEIP1559Transaction.fromTxData( - transaction as FinalizedFeeMarketEthereumTransaction, - { - common, - }, - ); - } + const { finalizedTransaction } = await this.finalizeTransaction(options); + return finalizedTransaction; } async getMessageToSign(options: TransactionOptions): Promise { @@ -214,35 +232,38 @@ class Transaction { } async getGasCosts(): Promise { - const { gasLimit, gasPrice, baseFeePerGas, formattedFeeHistory } = - await this.finalizeTransaction({ - gasPriceType: GasPriceTypes.ECONOMY, - }); - const opFee = await this.getOPfees(); + const { + gasLimit, + gasPrice, + baseFeePerGas, + formattedFeeHistory, + finalizedTransaction, + } = await this.finalizeTransaction({ + gasPriceType: GasPriceTypes.ECONOMY, + }); if (gasPrice) { return { [GasPriceTypes.ECONOMY]: numberToHex( - getGasBasedOnType(gasPrice, GasPriceTypes.ECONOMY) - .mul(toBN(gasLimit)) - .add(opFee), + getGasBasedOnType(gasPrice, GasPriceTypes.ECONOMY).mul( + toBN(gasLimit), + ), ), [GasPriceTypes.REGULAR]: numberToHex( - getGasBasedOnType(gasPrice, GasPriceTypes.REGULAR) - .mul(toBN(gasLimit)) - .add(opFee), + getGasBasedOnType(gasPrice, GasPriceTypes.REGULAR).mul( + toBN(gasLimit), + ), ), [GasPriceTypes.FAST]: numberToHex( - getGasBasedOnType(gasPrice, GasPriceTypes.FAST) - .mul(toBN(gasLimit)) - .add(opFee), + getGasBasedOnType(gasPrice, GasPriceTypes.FAST).mul(toBN(gasLimit)), ), [GasPriceTypes.FASTEST]: numberToHex( - getGasBasedOnType(gasPrice, GasPriceTypes.FASTEST) - .mul(toBN(gasLimit)) - .add(opFee), + getGasBasedOnType(gasPrice, GasPriceTypes.FASTEST).mul( + toBN(gasLimit), + ), ), }; } else { + const opFee = await this.getOPfees(finalizedTransaction); return { [GasPriceTypes.ECONOMY]: numberToHex( this.getFeeMarketGasInfo( From aa5c165be99d5bdc194c207409f6ff29a73d11aa Mon Sep 17 00:00:00 2001 From: kvhnuke <10602065+kvhnuke@users.noreply.github.com> Date: Thu, 21 Nov 2024 15:48:03 -0800 Subject: [PATCH 11/30] fix: hw wallet shaded area --- .../ui/action/views/swap/components/swap-error/index.vue | 2 +- .../ui/action/views/swap/components/swap-loading/index.vue | 2 +- .../ui/action/views/swap/views/swap-best-offer/index.vue | 6 +----- 3 files changed, 3 insertions(+), 7 deletions(-) diff --git a/packages/extension/src/ui/action/views/swap/components/swap-error/index.vue b/packages/extension/src/ui/action/views/swap/components/swap-error/index.vue index 5890dc1d7..3d48193ec 100644 --- a/packages/extension/src/ui/action/views/swap/components/swap-error/index.vue +++ b/packages/extension/src/ui/action/views/swap/components/swap-error/index.vue @@ -130,7 +130,7 @@ const supportedNets = getSupportedNetworks() &__container { width: 100%; - height: 600px; + height: 100%; left: 0px; top: 0px; position: fixed; diff --git a/packages/extension/src/ui/action/views/swap/components/swap-loading/index.vue b/packages/extension/src/ui/action/views/swap/components/swap-loading/index.vue index 3310af9ee..68a0a5968 100644 --- a/packages/extension/src/ui/action/views/swap/components/swap-loading/index.vue +++ b/packages/extension/src/ui/action/views/swap/components/swap-loading/index.vue @@ -84,7 +84,7 @@ withDefaults(defineProps(), { &__container { width: 100%; - height: 600px; + height: 100%; left: 0px; top: 0px; position: fixed; 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..24b04f7d3 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 @@ -11,11 +11,7 @@

- + Date: Thu, 21 Nov 2024 19:01:33 -0600 Subject: [PATCH 12/30] fix: send input ui breaking with multiple periods --- .../providers/bitcoin/ui/send-transaction/index.vue | 5 ++++- .../common/ui/send-transaction/send-input-amount.vue | 11 +++++++++-- .../providers/ethereum/ui/send-transaction/index.vue | 4 +++- .../providers/solana/ui/send-transaction/index.vue | 4 +++- 4 files changed, 19 insertions(+), 5 deletions(-) diff --git a/packages/extension/src/providers/bitcoin/ui/send-transaction/index.vue b/packages/extension/src/providers/bitcoin/ui/send-transaction/index.vue index 476e459be..0019b36de 100644 --- a/packages/extension/src/providers/bitcoin/ui/send-transaction/index.vue +++ b/packages/extension/src/providers/bitcoin/ui/send-transaction/index.vue @@ -305,7 +305,10 @@ const isInputsValid = computed(() => { isSendToken.value ) return false; - if (new BigNumber(sendAmount.value).gt(assetMaxValue.value)) return false; + + const sendAmountBigNumber = new BigNumber(sendAmount.value) + if (sendAmountBigNumber.isNaN()) return false + if (sendAmountBigNumber.gt(assetMaxValue.value)) return false; return true; }); diff --git a/packages/extension/src/providers/common/ui/send-transaction/send-input-amount.vue b/packages/extension/src/providers/common/ui/send-transaction/send-input-amount.vue index 206a5fafd..d444b943a 100644 --- a/packages/extension/src/providers/common/ui/send-transaction/send-input-amount.vue +++ b/packages/extension/src/providers/common/ui/send-transaction/send-input-amount.vue @@ -73,9 +73,16 @@ const amount = computed({ const onlyNumber = ($event: KeyboardEvent) => { const keyCode = $event.keyCode ? $event.keyCode : $event.which; - if ((keyCode < 48 || keyCode > 57) && keyCode !== 46) { - $event.preventDefault(); + // Numeric + if (keyCode >= /* 0 */ 48 && keyCode <= /* 9 */ 57) { + return; } + // Only allow a single period + if (keyCode === /* '.' */ 46 && amount.value.indexOf('.') === -1) { + return; + } + // Alphabetical (/non-numeric) or mulitple periods. Don't propagate change + $event.preventDefault(); }; const changeFocus = () => { isFocus.value = !isFocus.value; diff --git a/packages/extension/src/providers/ethereum/ui/send-transaction/index.vue b/packages/extension/src/providers/ethereum/ui/send-transaction/index.vue index d042451b8..74929b82e 100644 --- a/packages/extension/src/providers/ethereum/ui/send-transaction/index.vue +++ b/packages/extension/src/providers/ethereum/ui/send-transaction/index.vue @@ -486,7 +486,9 @@ const isInputsValid = computed(() => { if (!isSendToken.value && !selectedNft.value.id) { return false; } - if (new BigNumber(sendAmount.value).gt(assetMaxValue.value)) return false; + const sendAmountBigNumber = new BigNumber(sendAmount.value) + if (sendAmountBigNumber.isNaN()) return false + if (sendAmountBigNumber.gt(assetMaxValue.value)) return false; if (gasCostValues.value.REGULAR.nativeValue === '0') return false; return true; }); diff --git a/packages/extension/src/providers/solana/ui/send-transaction/index.vue b/packages/extension/src/providers/solana/ui/send-transaction/index.vue index 12b74be95..55eaedd24 100644 --- a/packages/extension/src/providers/solana/ui/send-transaction/index.vue +++ b/packages/extension/src/providers/solana/ui/send-transaction/index.vue @@ -399,7 +399,9 @@ const isInputsValid = computed(() => { if (!isSendToken.value && !selectedNft.value.id) { return false; } - if (new BigNumber(sendAmount.value).gt(assetMaxValue.value)) return false; + const sendAmountBigNumber = new BigNumber(sendAmount.value); + if (sendAmountBigNumber.isNaN()) return false; + if (sendAmountBigNumber.gt(assetMaxValue.value)) return false; return true; }); From 3e027535d67640a616656ee8050341cd614ce215 Mon Sep 17 00:00:00 2001 From: nickkelly1 Date: Fri, 22 Nov 2024 13:15:58 -0600 Subject: [PATCH 13/30] feat: show dst network in swap activity desc when src and dst symbols are the same --- .../network-activity-transaction.vue | 87 ++++++++++++++++++- 1 file changed, 83 insertions(+), 4 deletions(-) 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..a423f128d 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 @@ -81,9 +81,7 @@

- Swap from - {{ (activity.rawInfo as SwapRawInfo).fromToken.symbol }} to - {{ (activity.rawInfo as SwapRawInfo).toToken.symbol }} + {{ swapMessage }}