From f2e889606c9c9eebefd010265e2ec66f77d459d2 Mon Sep 17 00:00:00 2001 From: gomes <17035424+gomesalexandre@users.noreply.github.com> Date: Fri, 21 Feb 2025 14:03:42 +0100 Subject: [PATCH 1/4] [skip ci] wip: swapper no-non-null-asserts --- .eslintrc | 4 +- .../ChainflipSwapper/utils/getQuoteOrRate.ts | 59 +++++++++++-------- .../ChainflipSwapper/utils/helpers.ts | 8 ++- .../swappers/CowSwapper/CowSwapper.test.ts | 2 +- .../src/swappers/CowSwapper/endpoints.ts | 8 +-- .../swappers/JupiterSwapper/utils/helpers.ts | 6 +- .../getTradeQuote/getTradeQuote.ts | 21 +++++-- .../swappers/ThorchainSwapper/endpoints.ts | 4 +- .../ThorchainSwapper/utils/getL1Rate.ts | 6 +- .../utils/getL1ToLongtailQuote.ts | 12 +++- .../utils/getL1ToLongtailRate.ts | 12 +++- .../ThorchainSwapper/utils/getL1quote.ts | 4 +- .../helpers/getInputOutputRatioFromQuote.ts | 47 +++++++-------- .../swapper/helpers/validateTradeQuote.ts | 32 +++++----- 14 files changed, 134 insertions(+), 91 deletions(-) diff --git a/.eslintrc b/.eslintrc index 5046fae8852..b591c375e56 100644 --- a/.eslintrc +++ b/.eslintrc @@ -97,8 +97,8 @@ "react/jsx-handler-names": "error", "require-await": "error", "import/no-duplicates": "error", - "react-memo/require-usememo": "error" - // "@typescript-eslint/no-non-null-assertion": "warn" + "react-memo/require-usememo": "error", + "@typescript-eslint/no-non-null-assertion": "warn" }, "overrides": [ { diff --git a/packages/swapper/src/swappers/ChainflipSwapper/utils/getQuoteOrRate.ts b/packages/swapper/src/swappers/ChainflipSwapper/utils/getQuoteOrRate.ts index 5d597076659..36fb06be91e 100644 --- a/packages/swapper/src/swappers/ChainflipSwapper/utils/getQuoteOrRate.ts +++ b/packages/swapper/src/swappers/ChainflipSwapper/utils/getQuoteOrRate.ts @@ -128,12 +128,12 @@ export const getQuoteOrRate = async ( if ( cause.message.includes('code 400') && - cause.response!.data.detail.includes('Amount outside asset bounds') + cause.response?.data.detail.includes('Amount outside asset bounds') ) { return Err( createTradeAmountTooSmallErr({ assetId: sellAsset.assetId, - minAmountCryptoBaseUnit: cause.response!.data.errors.minimalAmountNative[0], + minAmountCryptoBaseUnit: cause.response?.data.errors.minimalAmountNative[0], }), ) } @@ -215,10 +215,13 @@ export const getQuoteOrRate = async ( const getProtocolFees = (singleQuoteResponse: ChainflipBaasQuoteQuote) => { const protocolFees: Record = {} - for (const fee of singleQuoteResponse.includedFees!) { + for (const fee of singleQuoteResponse.includedFees ?? []) { if (fee.type === 'broker') continue - const asset = getFeeAsset(fee)! + const asset = getFeeAsset(fee) + + if (!asset) continue + if (!(asset.assetId in protocolFees)) { protocolFees[asset.assetId] = { amountCryptoBaseUnit: '0', @@ -228,7 +231,7 @@ export const getQuoteOrRate = async ( } protocolFees[asset.assetId].amountCryptoBaseUnit = ( - BigInt(protocolFees[asset.assetId].amountCryptoBaseUnit) + BigInt(fee.amountNative!) + BigInt(protocolFees[asset.assetId].amountCryptoBaseUnit) + BigInt(fee.amountNative ?? '0') ).toString() } @@ -291,17 +294,21 @@ export const getQuoteOrRate = async ( if (!singleQuoteResponse.type) throw new Error('Missing quote type') - if (singleQuoteResponse.boostQuote) { + if ( + singleQuoteResponse.boostQuote && + singleQuoteResponse.boostQuote.ingressAmountNative && + singleQuoteResponse.boostQuote.egressAmountNative + ) { const boostRate = getChainflipQuoteRate( - singleQuoteResponse.boostQuote.ingressAmountNative!, - singleQuoteResponse.boostQuote.egressAmountNative!, + singleQuoteResponse.boostQuote.ingressAmountNative, + singleQuoteResponse.boostQuote.egressAmountNative, ) // This is not really a buyAmount before fees but rather an input/output calculation to get the sell amount // prorated to the buy asset price to determine price impact const buyAmountBeforeFeesCryptoBaseUnit = toBaseUnit( - bnOrZero(singleQuoteResponse.boostQuote.ingressAmount!).times( - singleQuoteResponse.estimatedPrice!, + bnOrZero(singleQuoteResponse.boostQuote.ingressAmount).times( + bnOrZero(singleQuoteResponse.estimatedPrice), ), buyAsset.precision, ) @@ -321,9 +328,9 @@ export const getQuoteOrRate = async ( steps: [ { buyAmountBeforeFeesCryptoBaseUnit, - buyAmountAfterFeesCryptoBaseUnit: singleQuoteResponse.boostQuote.egressAmountNative!, + buyAmountAfterFeesCryptoBaseUnit: singleQuoteResponse.boostQuote.egressAmountNative, sellAmountIncludingProtocolFeesCryptoBaseUnit: - singleQuoteResponse.boostQuote.ingressAmountNative!, + singleQuoteResponse.boostQuote.ingressAmountNative, feeData: { protocolFees: getProtocolFees(singleQuoteResponse.boostQuote), ...feeData, @@ -335,9 +342,8 @@ export const getQuoteOrRate = async ( accountNumber, allowanceContract: '0x0', // Chainflip does not use contracts estimatedExecutionTimeMs: - (singleQuoteResponse.boostQuote.estimatedDurationsSeconds!.deposit! + - singleQuoteResponse.boostQuote.estimatedDurationsSeconds!.swap!) * - 1000, + (singleQuoteResponse.boostQuote.estimatedDurationsSeconds?.deposit ?? 0) + + (singleQuoteResponse.boostQuote.estimatedDurationsSeconds?.swap ?? 0) * 1000, chainflipSpecific: { chainflipNumberOfChunks: isStreaming ? singleQuoteResponse.boostQuote.numberOfChunks ?? undefined @@ -354,15 +360,20 @@ export const getQuoteOrRate = async ( ratesOrQuotes.push(boostTradeRateOrQuote) } - const rate = getChainflipQuoteRate( - singleQuoteResponse.ingressAmountNative!, - singleQuoteResponse.egressAmountNative!, - ) + const rate = + singleQuoteResponse.ingressAmountNative && singleQuoteResponse.egressAmountNative + ? getChainflipQuoteRate( + singleQuoteResponse.ingressAmountNative, + singleQuoteResponse.egressAmountNative, + ) + : '0' // This is not really a buyAmount before fees but rather an input/output calculation to get the sell amount // prorated to the buy asset price to determine price impact const buyAmountBeforeFeesCryptoBaseUnit = toBaseUnit( - bnOrZero(singleQuoteResponse.ingressAmount!).times(singleQuoteResponse.estimatedPrice!), + bnOrZero(singleQuoteResponse.ingressAmount).times( + bnOrZero(singleQuoteResponse.estimatedPrice), + ), buyAsset.precision, ) @@ -381,8 +392,8 @@ export const getQuoteOrRate = async ( steps: [ { buyAmountBeforeFeesCryptoBaseUnit, - buyAmountAfterFeesCryptoBaseUnit: singleQuoteResponse.egressAmountNative!, - sellAmountIncludingProtocolFeesCryptoBaseUnit: singleQuoteResponse.ingressAmountNative!, + buyAmountAfterFeesCryptoBaseUnit: singleQuoteResponse.egressAmountNative, + sellAmountIncludingProtocolFeesCryptoBaseUnit: singleQuoteResponse.ingressAmountNative, feeData: { protocolFees: getProtocolFees(singleQuoteResponse), ...feeData, @@ -394,8 +405,8 @@ export const getQuoteOrRate = async ( accountNumber, allowanceContract: '0x0', // Chainflip does not use contracts - all Txs are sends estimatedExecutionTimeMs: - (singleQuoteResponse.estimatedDurationsSeconds!.deposit! + - singleQuoteResponse.estimatedDurationsSeconds!.swap!) * + ((singleQuoteResponse.estimatedDurationsSeconds?.deposit ?? 0) + + (singleQuoteResponse.estimatedDurationsSeconds?.swap ?? 0)) * 1000, chainflipSpecific: { chainflipNumberOfChunks: isStreaming diff --git a/packages/swapper/src/swappers/ChainflipSwapper/utils/helpers.ts b/packages/swapper/src/swappers/ChainflipSwapper/utils/helpers.ts index e5aef7eab31..6acb4d93001 100644 --- a/packages/swapper/src/swappers/ChainflipSwapper/utils/helpers.ts +++ b/packages/swapper/src/swappers/ChainflipSwapper/utils/helpers.ts @@ -51,9 +51,11 @@ export const isSupportedAssetId = ( chainId: ChainId, assetId: AssetId, ): chainId is ChainflipSupportedChainId => { - return ChainflipSupportedAssetIdsByChainId[chainId as ChainflipSupportedChainId]!.includes( - assetId, - ) + const supportedAssetIds = + ChainflipSupportedAssetIdsByChainId[chainId as ChainflipSupportedChainId] + if (!supportedAssetIds) return false + + return supportedAssetIds.includes(assetId) } export const calculateChainflipMinPrice = ({ diff --git a/packages/swapper/src/swappers/CowSwapper/CowSwapper.test.ts b/packages/swapper/src/swappers/CowSwapper/CowSwapper.test.ts index a2322ed5cee..e8fb243c5ad 100644 --- a/packages/swapper/src/swappers/CowSwapper/CowSwapper.test.ts +++ b/packages/swapper/src/swappers/CowSwapper/CowSwapper.test.ts @@ -288,7 +288,7 @@ describe('cowApi', () => { Ok({ data: cowswapQuoteResponse } as unknown as AxiosResponse), ), ) - const actual = await cowApi.getUnsignedEvmMessage!({ + const actual = await cowApi.getUnsignedEvmMessage?.({ from, slippageTolerancePercentageDecimal, tradeQuote, diff --git a/packages/swapper/src/swappers/CowSwapper/endpoints.ts b/packages/swapper/src/swappers/CowSwapper/endpoints.ts index b4c927c889f..d1b69801b79 100644 --- a/packages/swapper/src/swappers/CowSwapper/endpoints.ts +++ b/packages/swapper/src/swappers/CowSwapper/endpoints.ts @@ -44,9 +44,9 @@ export const cowApi: SwapperApi = { return tradeQuoteResult.map(tradeQuote => { // A quote always has a first step - const firstStep = getHopByIndex(tradeQuote, 0)! + const firstStep = getHopByIndex(tradeQuote, 0) const id = uuid() - tradeQuoteMetadata.set(id, { chainId: firstStep.sellAsset.chainId as EvmChainId }) + tradeQuoteMetadata.set(id, { chainId: firstStep?.sellAsset.chainId as EvmChainId }) return [tradeQuote] }) }, @@ -58,9 +58,9 @@ export const cowApi: SwapperApi = { return tradeRateResult.map(tradeRate => { // A rate always has a first step - const firstStep = getHopByIndex(tradeRate, 0)! + const firstStep = getHopByIndex(tradeRate, 0) const id = uuid() - tradeQuoteMetadata.set(id, { chainId: firstStep.sellAsset.chainId as EvmChainId }) + tradeQuoteMetadata.set(id, { chainId: firstStep?.sellAsset.chainId as EvmChainId }) return [tradeRate] }) }, diff --git a/packages/swapper/src/swappers/JupiterSwapper/utils/helpers.ts b/packages/swapper/src/swappers/JupiterSwapper/utils/helpers.ts index 0a861a56200..adb2c15f38d 100644 --- a/packages/swapper/src/swappers/JupiterSwapper/utils/helpers.ts +++ b/packages/swapper/src/swappers/JupiterSwapper/utils/helpers.ts @@ -223,10 +223,10 @@ export const createSwapInstructions = async ({ buyAsset.assetId === solAssetId ? undefined : fromAssetId(buyAsset.assetId).assetReference const { instruction: createTokenAccountInstruction, destinationTokenAccount } = - contractAddress && isCrossAccountTrade + contractAddress && isCrossAccountTrade && receiveAddress ? await adapter.createAssociatedTokenAccountInstruction({ from: sendAddress, - to: receiveAddress!, + to: receiveAddress, tokenId: contractAddress, }) : { instruction: undefined, destinationTokenAccount: undefined } @@ -277,7 +277,7 @@ export const createSwapInstructions = async ({ if (maybeSwapResponse.isErr()) { const error = maybeSwapResponse.unwrapErr() const cause = error.cause as AxiosError - throw Error(cause.response!.data.detail) + throw Error(cause.response?.data.detail) } const { data: swapResponse } = maybeSwapResponse.unwrap() diff --git a/packages/swapper/src/swappers/LifiSwapper/getTradeQuote/getTradeQuote.ts b/packages/swapper/src/swappers/LifiSwapper/getTradeQuote/getTradeQuote.ts index d1be319a8d2..d9f8f6aaa42 100644 --- a/packages/swapper/src/swappers/LifiSwapper/getTradeQuote/getTradeQuote.ts +++ b/packages/swapper/src/swappers/LifiSwapper/getTradeQuote/getTradeQuote.ts @@ -9,6 +9,7 @@ import { convertPrecision, isFulfilled, isRejected, + isSome, } from '@shapeshiftoss/utils' import type { Result } from '@sniptt/monads' import { Err, Ok } from '@sniptt/monads' @@ -350,11 +351,19 @@ export const getTradeQuote = async ( const quotesResult = await getTrade({ input, deps, lifiChainMap }) return quotesResult.map(quotes => - quotes.map(quote => ({ - ...quote, - quoteOrRate: 'quote' as const, - receiveAddress: quote.receiveAddress!, - steps: quote.steps.map(step => step) as [TradeQuoteStep] | [TradeQuoteStep, TradeQuoteStep], - })), + quotes + .map(quote => { + if (!quote.receiveAddress) return undefined + + return { + ...quote, + quoteOrRate: 'quote' as const, + receiveAddress: quote.receiveAddress, + steps: quote.steps.map(step => step) as + | [TradeQuoteStep] + | [TradeQuoteStep, TradeQuoteStep], + } + }) + .filter(isSome), ) } diff --git a/packages/swapper/src/swappers/ThorchainSwapper/endpoints.ts b/packages/swapper/src/swappers/ThorchainSwapper/endpoints.ts index 488166e33e5..668c56fc30e 100644 --- a/packages/swapper/src/swappers/ThorchainSwapper/endpoints.ts +++ b/packages/swapper/src/swappers/ThorchainSwapper/endpoints.ts @@ -434,7 +434,7 @@ export const thorchainApi: SwapperApi = { return assertGetUtxoChainAdapter(sellAsset.chainId).buildSendApiTransaction({ value: sellAmountIncludingProtocolFeesCryptoBaseUnit, - xpub: xpub!, + xpub, to: vault, accountNumber, // skip address validation for thorchain vault addresses as they may exceed the risk score threshold, but are still valid for use @@ -475,7 +475,7 @@ export const thorchainApi: SwapperApi = { to: vault, value: firstStep.sellAmountIncludingProtocolFeesCryptoBaseUnit, chainSpecific: { - pubkey: xpub!, + pubkey: xpub, opReturnData, }, sendMax: false, diff --git a/packages/swapper/src/swappers/ThorchainSwapper/utils/getL1Rate.ts b/packages/swapper/src/swappers/ThorchainSwapper/utils/getL1Rate.ts index 6410cdc9e9b..e7d0b0ae4f1 100644 --- a/packages/swapper/src/swappers/ThorchainSwapper/utils/getL1Rate.ts +++ b/packages/swapper/src/swappers/ThorchainSwapper/utils/getL1Rate.ts @@ -280,7 +280,7 @@ export const getL1Rate = async ( source, buyAsset, sellAsset, - accountNumber: accountNumber!, + accountNumber, allowanceContract: router, feeData: { networkFeeCryptoBaseUnit, @@ -342,7 +342,7 @@ export const getL1Rate = async ( const { vault, opReturnData, pubkey } = await getUtxoThorTxInfo({ sellAsset, - xpub: (input as unknown as GetUtxoTradeQuoteInput).xpub!, + xpub: (input as unknown as GetUtxoTradeQuoteInput).xpub, memo, config: deps.config, }) @@ -392,7 +392,7 @@ export const getL1Rate = async ( sellAsset, // TODO(gomes): when we actually split between TradeQuote and TradeRate in https://github.com/shapeshift/web/issues/7941, // this won't be an issue anymore - for now this is tackled at runtime with the isConnected check above - accountNumber: accountNumber!, + accountNumber, allowanceContract: '0x0', // not applicable to UTXOs feeData, }, diff --git a/packages/swapper/src/swappers/ThorchainSwapper/utils/getL1ToLongtailQuote.ts b/packages/swapper/src/swappers/ThorchainSwapper/utils/getL1ToLongtailQuote.ts index 13cdb20094e..9e27917a051 100644 --- a/packages/swapper/src/swappers/ThorchainSwapper/utils/getL1ToLongtailQuote.ts +++ b/packages/swapper/src/swappers/ThorchainSwapper/utils/getL1ToLongtailQuote.ts @@ -118,7 +118,17 @@ export const getL1ToLongtailQuote = async ( const promises = await Promise.allSettled( thorchainQuotes.map(async quote => { // A quote always has a first step - const onlyStep = getHopByIndex(quote, 0)! + const onlyStep = getHopByIndex(quote, 0) + + // Or well... it should. + if (!onlyStep) { + return Err( + makeSwapErrorRight({ + message: `[getL1ToLongtailQuote] - First hop not found`, + code: TradeQuoteError.InternalError, + }), + ) + } const maybeBestAggregator = await getBestAggregator( buyAssetFeeAsset, diff --git a/packages/swapper/src/swappers/ThorchainSwapper/utils/getL1ToLongtailRate.ts b/packages/swapper/src/swappers/ThorchainSwapper/utils/getL1ToLongtailRate.ts index 3ee3ca7e629..661b77c678b 100644 --- a/packages/swapper/src/swappers/ThorchainSwapper/utils/getL1ToLongtailRate.ts +++ b/packages/swapper/src/swappers/ThorchainSwapper/utils/getL1ToLongtailRate.ts @@ -109,7 +109,17 @@ export const getL1ToLongtailRate = async ( const promises = await Promise.allSettled( thorchainRates.map(async quote => { // A quote always has a first step - const onlyStep = getHopByIndex(quote, 0)! + const onlyStep = getHopByIndex(quote, 0) + + // Or well... it should. + if (!onlyStep) { + return Err( + makeSwapErrorRight({ + message: `[getL1ToLongtailRate] - First hop not found`, + code: TradeQuoteError.InternalError, + }), + ) + } const maybeBestAggregator = await getBestAggregator( buyAssetFeeAsset, diff --git a/packages/swapper/src/swappers/ThorchainSwapper/utils/getL1quote.ts b/packages/swapper/src/swappers/ThorchainSwapper/utils/getL1quote.ts index d834837d6e0..a280ad1ccfa 100644 --- a/packages/swapper/src/swappers/ThorchainSwapper/utils/getL1quote.ts +++ b/packages/swapper/src/swappers/ThorchainSwapper/utils/getL1quote.ts @@ -294,7 +294,7 @@ export const getL1Quote = async ( source, buyAsset, sellAsset, - accountNumber: accountNumber!, + accountNumber, allowanceContract: router, feeData: { networkFeeCryptoBaseUnit, @@ -360,7 +360,7 @@ export const getL1Quote = async ( const { vault, opReturnData, pubkey } = await getUtxoThorTxInfo({ sellAsset, - xpub: (input as GetUtxoTradeQuoteInput).xpub!, + xpub: (input as GetUtxoTradeQuoteInput).xpub, memo, config: deps.config, }) diff --git a/src/state/apis/swapper/helpers/getInputOutputRatioFromQuote.ts b/src/state/apis/swapper/helpers/getInputOutputRatioFromQuote.ts index a6bfdb2a3bc..dfc32841b89 100644 --- a/src/state/apis/swapper/helpers/getInputOutputRatioFromQuote.ts +++ b/src/state/apis/swapper/helpers/getInputOutputRatioFromQuote.ts @@ -103,38 +103,39 @@ export const getInputOutputRatioFromQuote = ({ swapperName: SwapperName }): number => { // A quote always has a first step - const firstStep = getHopByIndex(quote, 0)! - const { sellAmountIncludingProtocolFeesCryptoBaseUnit, sellAsset } = firstStep + const firstStep = getHopByIndex(quote, 0) + const { sellAmountIncludingProtocolFeesCryptoBaseUnit, sellAsset } = firstStep ?? {} const lastStepIndex = (quote.steps.length - 1) as SupportedTradeQuoteStepIndex // A quote always has a last step since it always has a first - const lastStep = getHopByIndex(quote, lastStepIndex)! - const { buyAsset, buyAmountAfterFeesCryptoBaseUnit: netReceiveAmountCryptoBaseUnit } = lastStep + const lastStep = getHopByIndex(quote, lastStepIndex) + const { buyAsset, buyAmountAfterFeesCryptoBaseUnit: netReceiveAmountCryptoBaseUnit } = + lastStep ?? {} // If we are trading custom assets we might not have USD rates, so we cannot determine a ratio - const hasSellAssetUsdRate = selectUsdRateByAssetId(state, sellAsset.assetId) !== undefined - const hasBuyAssetUsdRate = selectUsdRateByAssetId(state, buyAsset.assetId) !== undefined + const hasSellAssetUsdRate = selectUsdRateByAssetId(state, sellAsset?.assetId ?? '') !== undefined + const hasBuyAssetUsdRate = selectUsdRateByAssetId(state, buyAsset?.assetId ?? '') !== undefined if (!hasSellAssetUsdRate || !hasBuyAssetUsdRate) return 0 // TODO: implement this when we do multi-hop const buySideNetworkFeeCryptoBaseUnit = bn(0) - const netReceiveAmountUsdPrecision = _convertCryptoBaseUnitToUsdPrecision( - state, - buyAsset, - netReceiveAmountCryptoBaseUnit, - ) - - const buySideNetworkFeeUsdPrecision = _convertCryptoBaseUnitToUsdPrecision( - state, - buyAsset, - buySideNetworkFeeCryptoBaseUnit, - ) - - const sellAmountCryptoBaseUnit = _convertCryptoBaseUnitToUsdPrecision( - state, - sellAsset, - sellAmountIncludingProtocolFeesCryptoBaseUnit, - ) + const netReceiveAmountUsdPrecision = + buyAsset && netReceiveAmountCryptoBaseUnit + ? _convertCryptoBaseUnitToUsdPrecision(state, buyAsset, netReceiveAmountCryptoBaseUnit) + : bn(0) + + const buySideNetworkFeeUsdPrecision = buyAsset + ? _convertCryptoBaseUnitToUsdPrecision(state, buyAsset, buySideNetworkFeeCryptoBaseUnit) + : bn(0) + + const sellAmountCryptoBaseUnit = + sellAsset && sellAmountIncludingProtocolFeesCryptoBaseUnit + ? _convertCryptoBaseUnitToUsdPrecision( + state, + sellAsset, + sellAmountIncludingProtocolFeesCryptoBaseUnit, + ) + : bn(0) const totalNetworkFeeUsdPrecision = _getTotalNetworkFeeUsdPrecision(state, quote) diff --git a/src/state/apis/swapper/helpers/validateTradeQuote.ts b/src/state/apis/swapper/helpers/validateTradeQuote.ts index baf4166316b..832f38fcf6c 100644 --- a/src/state/apis/swapper/helpers/validateTradeQuote.ts +++ b/src/state/apis/swapper/helpers/validateTradeQuote.ts @@ -131,21 +131,21 @@ export const validateTradeQuote = ( const walletId = selectWalletId(state) // A quote always consists of at least one hop - const firstHop = getHopByIndex(quote, 0)! + const firstHop = getHopByIndex(quote, 0) const secondHop = getHopByIndex(quote, 1) const isMultiHopTrade = isExecutableTradeQuote(quote) ? isMultiHopTradeQuote(quote) : isMultiHopTradeRate(quote) - const lastHop = (isMultiHopTrade ? secondHop : firstHop)! + const lastHop = isMultiHopTrade ? secondHop : firstHop const walletConnectedChainIds = selectWalletConnectedChainIds(state) const sellAmountCryptoPrecision = selectInputSellAmountCryptoPrecision(state) - const sellAmountCryptoBaseUnit = firstHop.sellAmountIncludingProtocolFeesCryptoBaseUnit - const buyAmountCryptoBaseUnit = lastHop.buyAmountBeforeFeesCryptoBaseUnit + const sellAmountCryptoBaseUnit = firstHop?.sellAmountIncludingProtocolFeesCryptoBaseUnit + const buyAmountCryptoBaseUnit = lastHop?.buyAmountBeforeFeesCryptoBaseUnit // the network fee asset for the first hop in the trade - const firstHopSellFeeAsset = selectFeeAssetById(state, firstHop.sellAsset.assetId) + const firstHopSellFeeAsset = selectFeeAssetById(state, firstHop?.sellAsset.assetId ?? '') // the network fee asset for the second hop in the trade const secondHopSellFeeAsset = @@ -174,7 +174,7 @@ export const validateTradeQuote = ( const firstHopNetworkFeeCryptoPrecision = networkFeeRequiresBalance && firstHopSellFeeAsset ? fromBaseUnit( - bnOrZero(firstHop.feeData.networkFeeCryptoBaseUnit), + bnOrZero(firstHop?.feeData.networkFeeCryptoBaseUnit), firstHopSellFeeAsset.precision, ) : bn(0).toFixed() @@ -188,12 +188,12 @@ export const validateTradeQuote = ( : bn(0).toFixed() const firstHopTradeDeductionCryptoPrecision = - firstHopSellFeeAsset?.assetId === firstHop.sellAsset.assetId + firstHopSellFeeAsset?.assetId === firstHop?.sellAsset.assetId ? bnOrZero(sellAmountCryptoPrecision).toFixed() : bn(0).toFixed() const walletSupportsIntermediaryAssetChain = - !isMultiHopTrade || walletConnectedChainIds.includes(firstHop.buyAsset.chainId) + !isMultiHopTrade || walletConnectedChainIds.includes(firstHop?.buyAsset.chainId ?? '') const firstHopHasSufficientBalanceForGas = bnOrZero(firstHopFeeAssetBalancePrecision) .minus(firstHopNetworkFeeCryptoPrecision ?? 0) @@ -212,8 +212,8 @@ export const validateTradeQuote = ( const portfolioAccountIdByNumberByChainId = selectPortfolioAccountIdByNumberByChainId(state) const portfolioAccountBalancesBaseUnit = selectPortfolioAccountBalancesBaseUnit(state) - const sellAssetAccountNumber = firstHop.accountNumber - const totalProtocolFeesByAsset = getTotalProtocolFeeByAssetForStep(firstHop) + const sellAssetAccountNumber = firstHop?.accountNumber + const totalProtocolFeesByAsset = firstHop ? getTotalProtocolFeeByAssetForStep(firstHop) : {} // This is an oversimplification where protocol fees are assumed to be only deducted from // account IDs corresponding to the sell asset account number and protocol fee asset chain ID. @@ -233,11 +233,11 @@ export const validateTradeQuote = ( // them kick the swapperName bit out of the condition if ( firstHopSellFeeAsset?.assetId === assetId && - firstHop.sellAsset.assetId === assetId && + firstHop?.sellAsset.assetId === assetId && swapperName === SwapperName.Jupiter ) { return bnOrZero(balanceCryptoBaseUnit) - .minus(sellAmountCryptoBaseUnit) + .minus(bnOrZero(sellAmountCryptoBaseUnit)) .minus(protocolFee.amountCryptoBaseUnit) .lt(0) } @@ -267,7 +267,7 @@ export const validateTradeQuote = ( // Ensure the trade is not selling an amount higher than the user input, within a very safe threshold. // Threshold is required because cowswap sometimes quotes a sell amount a teeny-tiny bit more than you input. const invalidQuoteSellAmount = bn(inputSellAmountCryptoBaseUnit).lt( - firstHop.sellAmountIncludingProtocolFeesCryptoBaseUnit, + bnOrZero(firstHop?.sellAmountIncludingProtocolFeesCryptoBaseUnit), ) return { @@ -276,7 +276,7 @@ export const validateTradeQuote = ( error: TradeQuoteValidationError.TradingInactiveOnSellChain, meta: { chainName: assertGetChainAdapter( - firstHop.sellAsset.chainId as KnownChainIds, + firstHop?.sellAsset.chainId as KnownChainIds, ).getDisplayName(), }, }, @@ -284,7 +284,7 @@ export const validateTradeQuote = ( error: TradeQuoteValidationError.TradingInactiveOnBuyChain, meta: { chainName: assertGetChainAdapter( - firstHop.buyAsset.chainId as KnownChainIds, + firstHop?.buyAsset.chainId as KnownChainIds, ).getDisplayName(), }, }, @@ -300,7 +300,7 @@ export const validateTradeQuote = ( walletId && !firstHopHasSufficientBalanceForGas && { error: - firstHopSellFeeAsset?.assetId === firstHop.sellAsset.assetId + firstHopSellFeeAsset?.assetId === firstHop?.sellAsset.assetId ? TradeQuoteValidationError.InsufficientFirstHopAssetBalance : TradeQuoteValidationError.InsufficientFirstHopFeeAssetBalance, meta: { From 563602b3fb1dab6625d0296fe88d12ee181c6e50 Mon Sep 17 00:00:00 2001 From: gomes <17035424+gomesalexandre@users.noreply.github.com> Date: Fri, 21 Feb 2025 14:33:42 +0100 Subject: [PATCH 2/4] [skip ci] wip: wip --- .../ArbitrumBridgeSwapper/endpoints.ts | 13 ++++++-- .../utils/fetchArbitrumBridgeSwap.ts | 23 +++++++++----- .../swappers/ChainflipSwapper/endpoints.ts | 12 ++++--- .../swapperApi/getTradeQuote.ts | 31 +++++++++++++------ .../TradeInput/components/ConfirmSummary.tsx | 3 +- .../components/TradeSuccess/TradeSuccess.tsx | 8 ++--- .../hooks/quoteValidation/usePriceImpact.tsx | 8 ++--- src/state/apis/snapshot/selectors.ts | 3 +- .../createTradeInputBaseSlice.ts | 2 +- 9 files changed, 67 insertions(+), 36 deletions(-) diff --git a/packages/swapper/src/swappers/ArbitrumBridgeSwapper/endpoints.ts b/packages/swapper/src/swappers/ArbitrumBridgeSwapper/endpoints.ts index c1a7c8bb7a0..0e11d639816 100644 --- a/packages/swapper/src/swappers/ArbitrumBridgeSwapper/endpoints.ts +++ b/packages/swapper/src/swappers/ArbitrumBridgeSwapper/endpoints.ts @@ -95,7 +95,11 @@ export const arbitrumBridgeApi: SwapperApi = { return tradeQuoteResult.map(tradeQuote => { const id = tradeQuote.id - const firstHop = getHopByIndex(tradeQuote, 0)! + const firstHop = getHopByIndex(tradeQuote, 0) + if (!firstHop) { + console.error('No first hop found in trade quote') + return [] + } tradeQuoteMetadata.set(id, { sellAssetId: firstHop.sellAsset.assetId, chainId: firstHop.sellAsset.chainId as EvmChainId, @@ -111,7 +115,12 @@ export const arbitrumBridgeApi: SwapperApi = { return tradeRateResult.map(tradeQuote => { const id = tradeQuote.id - const firstHop = getHopByIndex(tradeQuote, 0)! + const firstHop = getHopByIndex(tradeQuote, 0) + if (!firstHop) { + console.error('No first hop found in trade rate') + return [] + } + tradeQuoteMetadata.set(id, { sellAssetId: firstHop.sellAsset.assetId, chainId: firstHop.sellAsset.chainId as EvmChainId, diff --git a/packages/swapper/src/swappers/ArbitrumBridgeSwapper/utils/fetchArbitrumBridgeSwap.ts b/packages/swapper/src/swappers/ArbitrumBridgeSwapper/utils/fetchArbitrumBridgeSwap.ts index fc24b75a33d..444ab10749e 100644 --- a/packages/swapper/src/swappers/ArbitrumBridgeSwapper/utils/fetchArbitrumBridgeSwap.ts +++ b/packages/swapper/src/swappers/ArbitrumBridgeSwapper/utils/fetchArbitrumBridgeSwap.ts @@ -99,8 +99,10 @@ const fetchArbitrumBridgeSwap = async ({ parentProvider, childProvider, amount: BigNumber.from(sellAmountIncludingProtocolFeesCryptoBaseUnit), - from: sendAddress!, - destinationAddress: receiveAddress!, + // This is actually guarded against in if `(quoteOrRate === 'quote' && !sendAddress)` above + from: sendAddress ?? '', + // This is actually guarded against in if `(quoteOrRate === 'quote' && !receiveAddress)` above + destinationAddress: receiveAddress ?? '', }) .catch(e => { console.error('Error getting ETH deposit request', e) @@ -125,8 +127,10 @@ const fetchArbitrumBridgeSwap = async ({ ? await bridger .getWithdrawalRequest({ amount: BigNumber.from(sellAmountIncludingProtocolFeesCryptoBaseUnit), - from: sendAddress!, - destinationAddress: receiveAddress!, + // This is actually guarded against in if `(quoteOrRate === 'quote' && !sendAddress)` above + from: sendAddress ?? '', + // This is actually guarded against in if `(quoteOrRate === 'quote' && !receiveAddress)` above + destinationAddress: receiveAddress ?? '', }) .catch(e => { console.error('Error getting ETH withdraw request', e) @@ -159,8 +163,9 @@ const fetchArbitrumBridgeSwap = async ({ parentProvider, childProvider, erc20ParentAddress, - from: sendAddress!, - destinationAddress: receiveAddress!, + // This is actually guarded against in if `(quoteOrRate === 'quote' && !sendAddress)` above + from: sendAddress ?? '', + destinationAddress: receiveAddress, retryableGasOverrides: { // https://github.com/OffchainLabs/arbitrum-token-bridge/blob/d17c88ef3eef3f4ffc61a04d34d50406039f045d/packages/arb-token-bridge-ui/src/util/TokenDepositUtils.ts#L159 // the gas limit may vary by about 20k due to SSTORE (zero vs nonzero) @@ -193,8 +198,10 @@ const fetchArbitrumBridgeSwap = async ({ .getWithdrawalRequest({ amount: BigNumber.from(sellAmountIncludingProtocolFeesCryptoBaseUnit), erc20ParentAddress, - from: sendAddress!, - destinationAddress: receiveAddress!, + // This is actually guarded against in if `(quoteOrRate === 'quote' && !sendAddress)` above + from: sendAddress ?? '', + // This is actually guarded against in if `(quoteOrRate === 'quote' && !receiveAddress)` above + destinationAddress: receiveAddress ?? '', }) .catch(e => { console.error('Error getting ERC20 withdraw request', e) diff --git a/packages/swapper/src/swappers/ChainflipSwapper/endpoints.ts b/packages/swapper/src/swappers/ChainflipSwapper/endpoints.ts index 059f4b10bff..dfa4a070296 100644 --- a/packages/swapper/src/swappers/ChainflipSwapper/endpoints.ts +++ b/packages/swapper/src/swappers/ChainflipSwapper/endpoints.ts @@ -73,10 +73,12 @@ export const chainflipApi: SwapperApi = { chainSpecific: { gasLimit: fees.chainSpecific.gasLimit, contractAddress: isTokenSend ? assetReference : undefined, - ...(supportsEIP1559 + ...(supportsEIP1559 && + fees.chainSpecific.maxFeePerGas && + fees.chainSpecific.maxPriorityFeePerGas ? { - maxFeePerGas: fees.chainSpecific.maxFeePerGas!, - maxPriorityFeePerGas: fees.chainSpecific.maxPriorityFeePerGas!, + maxFeePerGas: fees.chainSpecific.maxFeePerGas, + maxPriorityFeePerGas: fees.chainSpecific.maxPriorityFeePerGas, } : { gasPrice: fees.chainSpecific.gasPrice, @@ -150,7 +152,7 @@ export const chainflipApi: SwapperApi = { return adapter.buildSendApiTransaction({ value: step.sellAmountIncludingProtocolFeesCryptoBaseUnit, - xpub: xpub!, + xpub, to: step.chainflipSpecific.chainflipDepositAddress, accountNumber: step.accountNumber, skipToAddressValidation: true, @@ -177,7 +179,7 @@ export const chainflipApi: SwapperApi = { to: step.chainflipSpecific.chainflipDepositAddress, value: step.sellAmountIncludingProtocolFeesCryptoBaseUnit, chainSpecific: { - pubkey: xpub!, + pubkey: xpub, }, sendMax: false, } diff --git a/packages/swapper/src/swappers/ChainflipSwapper/swapperApi/getTradeQuote.ts b/packages/swapper/src/swappers/ChainflipSwapper/swapperApi/getTradeQuote.ts index 368b191502f..b0fcd5669c1 100644 --- a/packages/swapper/src/swappers/ChainflipSwapper/swapperApi/getTradeQuote.ts +++ b/packages/swapper/src/swappers/ChainflipSwapper/swapperApi/getTradeQuote.ts @@ -1,6 +1,7 @@ import { CHAIN_NAMESPACE, fromAssetId, solAssetId } from '@shapeshiftoss/caip' import type { GetFeeDataInput } from '@shapeshiftoss/chain-adapters' import type { KnownChainIds } from '@shapeshiftoss/types' +import { isSome } from '@shapeshiftoss/utils' import type { Result } from '@sniptt/monads' import { Err, Ok } from '@sniptt/monads' import type { AxiosError } from 'axios' @@ -103,8 +104,8 @@ export const getTradeQuote = async ( sourceAsset, minimumPrice, destinationAsset, - destinationAddress: input.receiveAddress, - refundAddress: input.sendAddress!, + destinationAddress: receiveAddress, + refundAddress: sendAddress, maxBoostFee: step.chainflipSpecific?.chainflipMaxBoostFee, numberOfChunks: step.chainflipSpecific?.chainflipNumberOfChunks, chunkIntervalBlocks: step.chainflipSpecific?.chainflipChunkIntervalBlocks, @@ -114,7 +115,7 @@ export const getTradeQuote = async ( if (maybeSwapResponse.isErr()) { const error = maybeSwapResponse.unwrapErr() const cause = error.cause as AxiosError - throw Error(cause.response!.data.detail) + throw Error(cause.response?.data.detail) } const { data: swapResponse } = maybeSwapResponse.unwrap() @@ -135,7 +136,7 @@ export const getTradeQuote = async ( to: depositAddress, value: sellAmount, chainSpecific: { - from: input.sendAddress!, + from: sendAddress, tokenId: sellAsset.assetId === solAssetId ? undefined @@ -169,11 +170,21 @@ export const getTradeQuote = async ( const quotesResult = Ok(tradeQuotes) return quotesResult.map(quotes => - quotes.map(quote => ({ - ...quote, - quoteOrRate: 'quote' as const, - receiveAddress: receiveAddress!, - steps: quote.steps.map(step => step) as [TradeQuoteStep] | [TradeQuoteStep, TradeQuoteStep], - })), + quotes + .map(quote => { + if (!quote.receiveAddress) { + console.error('receiveAddress is required') + return undefined + } + return { + ...quote, + quoteOrRate: 'quote' as const, + receiveAddress, + steps: quote.steps.map(step => step) as + | [TradeQuoteStep] + | [TradeQuoteStep, TradeQuoteStep], + } + }) + .filter(isSome), ) } diff --git a/src/components/MultiHopTrade/components/TradeInput/components/ConfirmSummary.tsx b/src/components/MultiHopTrade/components/TradeInput/components/ConfirmSummary.tsx index 36dda4b9f3a..4e1dde4d269 100644 --- a/src/components/MultiHopTrade/components/TradeInput/components/ConfirmSummary.tsx +++ b/src/components/MultiHopTrade/components/TradeInput/components/ConfirmSummary.tsx @@ -254,7 +254,8 @@ export const ConfirmSummary = ({ tradeQuoteError.error === TradeQuoteError.FinalQuoteExecutionReverted): return 'trade.previewTrade' case !!tradeQuoteError: - return getQuoteErrorTranslation(tradeQuoteError!) + // TS pls + return tradeQuoteError ? getQuoteErrorTranslation(tradeQuoteError) : 'common.error' case !isAnyTradeQuoteLoading && !isAnySwapperQuoteAvailable: return 'trade.noRateAvailable' case !isConnected || isDemoWallet: diff --git a/src/components/MultiHopTrade/components/TradeSuccess/TradeSuccess.tsx b/src/components/MultiHopTrade/components/TradeSuccess/TradeSuccess.tsx index 23dc443ad6b..ffd67685fab 100644 --- a/src/components/MultiHopTrade/components/TradeSuccess/TradeSuccess.tsx +++ b/src/components/MultiHopTrade/components/TradeSuccess/TradeSuccess.tsx @@ -195,11 +195,11 @@ export const TradeSuccess = ({ - {enableFoxDiscountSummary && hasFeeSaving && ( - + {enableFoxDiscountSummary && hasFeeSaving && feeSavingUserCurrency && ( + )} - {couldHaveReducedFee && ( - + {couldHaveReducedFee && affiliateFeeUserCurrency && ( + )} diff --git a/src/components/MultiHopTrade/hooks/quoteValidation/usePriceImpact.tsx b/src/components/MultiHopTrade/hooks/quoteValidation/usePriceImpact.tsx index 15d066fc10c..b82105cdf89 100644 --- a/src/components/MultiHopTrade/hooks/quoteValidation/usePriceImpact.tsx +++ b/src/components/MultiHopTrade/hooks/quoteValidation/usePriceImpact.tsx @@ -26,9 +26,9 @@ export const usePriceImpact = (tradeQuote: TradeQuote | TradeRate | undefined) = if (!tradeQuote || !sellAsset || !sellAssetUsdRate) return // A quote always has a first hop - const firstHop = getHopByIndex(tradeQuote, 0)! + const firstHop = getHopByIndex(tradeQuote, 0) const sellAmountIncludingProtocolFeesCryptoBaseUnit = - firstHop.sellAmountIncludingProtocolFeesCryptoBaseUnit + firstHop?.sellAmountIncludingProtocolFeesCryptoBaseUnit const sellAmountIncludingProtocolFeesCryptoPrecision = fromBaseUnit( sellAmountIncludingProtocolFeesCryptoBaseUnit, @@ -50,9 +50,9 @@ export const usePriceImpact = (tradeQuote: TradeQuote | TradeRate | undefined) = const lastHopIndex = (numSteps - 1) as SupportedTradeQuoteStepIndex // A quote always has a last hop since it always has a first hop - const lastHop = getHopByIndex(tradeQuote, lastHopIndex)! + const lastHop = getHopByIndex(tradeQuote, lastHopIndex) const buyAmountBeforeFeesCryptoPrecision = fromBaseUnit( - lastHop.buyAmountBeforeFeesCryptoBaseUnit, + lastHop?.buyAmountBeforeFeesCryptoBaseUnit, buyAsset.precision, ) diff --git a/src/state/apis/snapshot/selectors.ts b/src/state/apis/snapshot/selectors.ts index c49c9077f2d..3c7933a67d9 100644 --- a/src/state/apis/snapshot/selectors.ts +++ b/src/state/apis/snapshot/selectors.ts @@ -33,12 +33,13 @@ export const selectVotingPower = createSelector( selectAccountIdsByChainId, selectIsSnapshotApiQueriesRejected, (votingPowerByModel, feeModel, accountIdsbyChainId, isSnapshotApiQueriesRejected) => { + if (!feeModel) return '0' if (isSnapshotApiQueriesRejected) return '0' const ethAccountIds = accountIdsbyChainId[ethChainId] if (!ethAccountIds?.length) return '0' - return votingPowerByModel[feeModel!] + return votingPowerByModel[feeModel] }, ) diff --git a/src/state/slices/common/tradeInputBase/createTradeInputBaseSlice.ts b/src/state/slices/common/tradeInputBase/createTradeInputBaseSlice.ts index 6e0adea182d..ee530f4f532 100644 --- a/src/state/slices/common/tradeInputBase/createTradeInputBaseSlice.ts +++ b/src/state/slices/common/tradeInputBase/createTradeInputBaseSlice.ts @@ -74,7 +74,7 @@ const getBaseReducers = (initialState: T) => ({ // Avoid division by zero state.sellAmountCryptoPrecision = bnOrZero(buyAssetUsdRate).isZero() ? '0' - : sellAmountUsd.div(buyAssetUsdRate!).toFixed() + : sellAmountUsd.div(bnOrZero(buyAssetUsdRate)).toFixed() const buyAsset = state.sellAsset state.sellAsset = state.buyAsset From e6f5ea4d858d9b8a7ef20fdd3fc9fd7e4f046776 Mon Sep 17 00:00:00 2001 From: gomes <17035424+gomesalexandre@users.noreply.github.com> Date: Fri, 21 Feb 2025 15:42:05 +0100 Subject: [PATCH 3/4] feat: done --- .../hooks/useChainflipStreamingProgress.tsx | 6 ++--- .../VerifyAddresses/VerifyAddresses.tsx | 23 +++++++++++-------- .../hooks/useInputOutputDifference.ts | 12 +++++----- .../hooks/useQuoteEstimatedFeesQuery.ts | 23 ++++++++++--------- 4 files changed, 35 insertions(+), 29 deletions(-) diff --git a/src/components/MultiHopTrade/components/TradeConfirm/hooks/useChainflipStreamingProgress.tsx b/src/components/MultiHopTrade/components/TradeConfirm/hooks/useChainflipStreamingProgress.tsx index e532ba14729..895d0d7c2ed 100644 --- a/src/components/MultiHopTrade/components/TradeConfirm/hooks/useChainflipStreamingProgress.tsx +++ b/src/components/MultiHopTrade/components/TradeConfirm/hooks/useChainflipStreamingProgress.tsx @@ -56,14 +56,14 @@ const getChainflipStreamingSwap = async ( ) { // It's finished! return { - executedChunks: dcaStatus!.executedChunks!, + executedChunks: dcaStatus.executedChunks ?? 0, remainingChunks: 0, } } return { - executedChunks: dcaStatus!.executedChunks!, - remainingChunks: dcaStatus!.remainingChunks!, + executedChunks: dcaStatus?.executedChunks ?? 0, + remainingChunks: dcaStatus?.remainingChunks ?? 0, } } diff --git a/src/components/MultiHopTrade/components/VerifyAddresses/VerifyAddresses.tsx b/src/components/MultiHopTrade/components/VerifyAddresses/VerifyAddresses.tsx index c52de7115ad..5181a7ecd81 100644 --- a/src/components/MultiHopTrade/components/VerifyAddresses/VerifyAddresses.tsx +++ b/src/components/MultiHopTrade/components/VerifyAddresses/VerifyAddresses.tsx @@ -149,15 +149,20 @@ export const VerifyAddresses = () => { accountMetadata: sellAccountMetadata, pubKey: fromAccountId(sellAssetAccountId).account, }) - const fetchedOrManualBuyAddress = shouldVerifyBuyAddress - ? await getReceiveAddress({ - asset: buyAsset, - wallet, - deviceId, - accountMetadata: buyAccountMetadata!, - pubKey: fromAccountId(buyAssetAccountId!).account, - }) - : maybeManualReceiveAddress + + const fetchedOrManualBuyAddress = await (() => { + if (!shouldVerifyBuyAddress) return maybeManualReceiveAddress + + if (!buyAssetAccountId || !buyAccountMetadata) throw new Error('Missing buy account metadata') + + return getReceiveAddress({ + asset: buyAsset, + wallet, + deviceId, + accountMetadata: buyAccountMetadata, + pubKey: fromAccountId(buyAssetAccountId).account, + }) + })() setSellAddress(fetchedSellAddress) setBuyAddress(fetchedOrManualBuyAddress) diff --git a/src/components/MultiHopTrade/hooks/useInputOutputDifference.ts b/src/components/MultiHopTrade/hooks/useInputOutputDifference.ts index bd0fe30e282..e08b78f42a0 100644 --- a/src/components/MultiHopTrade/hooks/useInputOutputDifference.ts +++ b/src/components/MultiHopTrade/hooks/useInputOutputDifference.ts @@ -24,12 +24,12 @@ export const useInputOutputDifferenceDecimalPercentage = ( if (!tradeQuote || !sellAsset || !sellAssetUsdRate) return // A quote always has a first hop - const firstHop = getHopByIndex(tradeQuote, 0)! + const firstHop = getHopByIndex(tradeQuote, 0) const sellAmountIncludingProtocolFeesCryptoBaseUnit = - firstHop.sellAmountIncludingProtocolFeesCryptoBaseUnit + firstHop?.sellAmountIncludingProtocolFeesCryptoBaseUnit const sellAmountIncludingProtocolFeesCryptoPrecision = fromBaseUnit( - sellAmountIncludingProtocolFeesCryptoBaseUnit, + sellAmountIncludingProtocolFeesCryptoBaseUnit ?? '0', sellAsset.precision, ) @@ -41,11 +41,11 @@ export const useInputOutputDifferenceDecimalPercentage = ( const lastHopIndex = (numSteps - 1) as SupportedTradeQuoteStepIndex // A quote always has a last hop since it always has a first hop - const lastHop = getHopByIndex(tradeQuote, lastHopIndex)! - const buyAmountAfterProtocolFeesCryptoBaseUnit = lastHop.buyAmountAfterFeesCryptoBaseUnit + const lastHop = getHopByIndex(tradeQuote, lastHopIndex) + const buyAmountAfterProtocolFeesCryptoBaseUnit = lastHop?.buyAmountAfterFeesCryptoBaseUnit const buyAmountAfterProtocolFeesCryptoPrecision = fromBaseUnit( - buyAmountAfterProtocolFeesCryptoBaseUnit, + buyAmountAfterProtocolFeesCryptoBaseUnit ?? '0', buyAsset.precision, ) diff --git a/src/react-queries/hooks/useQuoteEstimatedFeesQuery.ts b/src/react-queries/hooks/useQuoteEstimatedFeesQuery.ts index 8d65434e9fc..34eb1c00839 100644 --- a/src/react-queries/hooks/useQuoteEstimatedFeesQuery.ts +++ b/src/react-queries/hooks/useQuoteEstimatedFeesQuery.ts @@ -1,7 +1,7 @@ import type { AccountId, AssetId } from '@shapeshiftoss/caip' import { fromAssetId } from '@shapeshiftoss/caip' import type { Asset, KnownChainIds } from '@shapeshiftoss/types' -import { useQuery } from '@tanstack/react-query' +import { skipToken, useQuery } from '@tanstack/react-query' import { useMemo } from 'react' import { toHex } from 'viem' import { estimateFees } from 'components/Modals/Send/utils' @@ -111,7 +111,6 @@ export const useQuoteEstimatedFeesQuery = ({ collateralAccountId, ]) - // TODO(gomes): this is wrong, we should use a proper generated query for this const quoteEstimatedFeesQueryKey = useMemo( () => ['thorchainLendingQuoteEstimatedFees', estimateFeesArgs], [estimateFeesArgs], @@ -139,15 +138,17 @@ export const useQuoteEstimatedFeesQuery = ({ const useQuoteEstimatedFeesQuery = useQuery({ queryKey: quoteEstimatedFeesQueryKey, - queryFn: async () => { - const estimatedFees = await estimateFees(estimateFeesArgs) - const txFeeFiat = bnOrZero(estimatedFees.fast.txFee) - .div(bn(10).pow(feeAsset!.precision)) // actually defined at runtime, see "enabled" below - .times(feeAssetMarketData.price) - .toString() - return { estimatedFees, txFeeFiat, txFeeCryptoBaseUnit: estimatedFees.fast.txFee } - }, - enabled, + queryFn: + enabled && feeAsset + ? async () => { + const estimatedFees = await estimateFees(estimateFeesArgs) + const txFeeFiat = bnOrZero(estimatedFees.fast.txFee) + .div(bn(10).pow(feeAsset.precision)) + .times(feeAssetMarketData.price) + .toString() + return { estimatedFees, txFeeFiat, txFeeCryptoBaseUnit: estimatedFees.fast.txFee } + } + : skipToken, retry: false, }) From 4ea47a3632fc81af03c3769a77f7e77a42c8384f Mon Sep 17 00:00:00 2001 From: gomes <17035424+gomesalexandre@users.noreply.github.com> Date: Fri, 21 Feb 2025 15:42:21 +0100 Subject: [PATCH 4/4] feat: re-comment --- .eslintrc | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.eslintrc b/.eslintrc index b591c375e56..5046fae8852 100644 --- a/.eslintrc +++ b/.eslintrc @@ -97,8 +97,8 @@ "react/jsx-handler-names": "error", "require-await": "error", "import/no-duplicates": "error", - "react-memo/require-usememo": "error", - "@typescript-eslint/no-non-null-assertion": "warn" + "react-memo/require-usememo": "error" + // "@typescript-eslint/no-non-null-assertion": "warn" }, "overrides": [ {