diff --git a/src/__swaps__/screens/Swap/hooks/useEstimatedGasFee.ts b/src/__swaps__/screens/Swap/hooks/useEstimatedGasFee.ts index 9f8af50f901..17fb0d613a8 100644 --- a/src/__swaps__/screens/Swap/hooks/useEstimatedGasFee.ts +++ b/src/__swaps__/screens/Swap/hooks/useEstimatedGasFee.ts @@ -19,9 +19,22 @@ function safeBigInt(value: string) { } } +const isFeeNaN = (value: string | undefined) => isNaN(Number(value)) || typeof value === 'undefined'; + export function calculateGasFee(gasSettings: GasSettings, gasLimit: string) { - const amount = gasSettings.isEIP1559 ? add(gasSettings.maxBaseFee, gasSettings.maxPriorityFee || '0') : gasSettings.gasPrice; - return multiply(gasLimit, amount); + if (gasSettings.isEIP1559) { + if (isFeeNaN(gasSettings.maxBaseFee) || isFeeNaN(gasSettings.maxPriorityFee)) { + return null; + } + + return add(gasSettings.maxBaseFee, gasSettings.maxPriorityFee); + } + + if (isFeeNaN(gasSettings.gasPrice)) { + return null; + } + + return multiply(gasLimit, gasSettings.gasPrice); } export function useEstimatedGasFee({ @@ -40,6 +53,7 @@ export function useEstimatedGasFee({ if (!gasLimit || !gasSettings || !nativeNetworkAsset?.price) return; const fee = calculateGasFee(gasSettings, gasLimit); + if (!fee) return; const networkAssetPrice = nativeNetworkAsset.price.value?.toString(); if (!networkAssetPrice) return `${formatNumber(weiToGwei(fee))} Gwei`; diff --git a/src/__swaps__/screens/Swap/providers/SyncSwapStateAndSharedValues.tsx b/src/__swaps__/screens/Swap/providers/SyncSwapStateAndSharedValues.tsx index 843fcc616d6..fa047483948 100644 --- a/src/__swaps__/screens/Swap/providers/SyncSwapStateAndSharedValues.tsx +++ b/src/__swaps__/screens/Swap/providers/SyncSwapStateAndSharedValues.tsx @@ -79,10 +79,28 @@ export const SyncQuoteSharedValuesToState = () => { return null; }; +const isFeeNaNWorklet = (value: string | undefined) => { + 'worklet'; + + return isNaN(Number(value)) || typeof value === 'undefined'; +}; + export function calculateGasFeeWorklet(gasSettings: GasSettings, gasLimit: string) { 'worklet'; - const amount = gasSettings.isEIP1559 ? sumWorklet(gasSettings.maxBaseFee, gasSettings.maxPriorityFee || '0') : gasSettings.gasPrice; - return mulWorklet(gasLimit, amount); + + if (gasSettings.isEIP1559) { + if (isFeeNaNWorklet(gasSettings.maxBaseFee) || isFeeNaNWorklet(gasSettings.maxPriorityFee)) { + return null; + } + + return sumWorklet(gasSettings.maxBaseFee || '0', gasSettings.maxPriorityFee || '0'); + } + + if (isFeeNaNWorklet(gasSettings.gasPrice)) { + return null; + } + + return mulWorklet(gasLimit, gasSettings.gasPrice); } export function formatUnitsWorklet(value: string, decimals: number) { @@ -166,12 +184,23 @@ export function SyncGasStateToSharedValues() { hasEnoughFundsForGas.value = undefined; if (!gasSettings || !estimatedGasLimit || !quote || 'error' in quote || isLoadingNativeNetworkAsset) return; + // NOTE: if we don't have a gas price or max base fee or max priority fee, we can't calculate the gas fee + if ( + (gasSettings.isEIP1559 && !(gasSettings.maxBaseFee || gasSettings.maxPriorityFee)) || + (!gasSettings.isEIP1559 && !gasSettings.gasPrice) + ) { + return; + } + if (!userNativeNetworkAsset) { hasEnoughFundsForGas.value = false; return; } const gasFee = calculateGasFeeWorklet(gasSettings, estimatedGasLimit); + if (gasFee === null || isNaN(Number(gasFee))) { + return; + } const nativeGasFee = divWorklet(gasFee, powWorklet(10, userNativeNetworkAsset.decimals)); diff --git a/src/handlers/web3.ts b/src/handlers/web3.ts index 13416a8aceb..015e75eea6d 100644 --- a/src/handlers/web3.ts +++ b/src/handlers/web3.ts @@ -370,6 +370,10 @@ export async function estimateGasWithPadding( ? contractCallEstimateGas(...(callArguments ?? []), txPayloadToEstimate) : p.estimateGas(cleanTxPayload)); + if (!BigNumber.isBigNumber(estimatedGas)) { + throw new Error('Invalid gas limit type'); + } + const lastBlockGasLimit = addBuffer(gasLimit.toString(), 0.9); const paddedGas = addBuffer(estimatedGas.toString(), paddingFactor.toString()); logger.debug('[web3]: ⛽ GAS CALCULATIONS!', { diff --git a/src/raps/actions/crosschainSwap.ts b/src/raps/actions/crosschainSwap.ts index ddbd4789be3..eeca43b96e2 100644 --- a/src/raps/actions/crosschainSwap.ts +++ b/src/raps/actions/crosschainSwap.ts @@ -71,9 +71,13 @@ export const estimateCrosschainSwapGasLimit = async ({ SWAP_GAS_PADDING ); - return gasLimit || getCrosschainSwapDefaultGasLimit(quote); + if (gasLimit === null || gasLimit === undefined || isNaN(Number(gasLimit))) { + return getCrosschainSwapDefaultGasLimit(quote) || getDefaultGasLimitForTrade(quote, chainId); + } + + return gasLimit; } catch (error) { - return getCrosschainSwapDefaultGasLimit(quote); + return getCrosschainSwapDefaultGasLimit(quote) || getDefaultGasLimitForTrade(quote, chainId); } }; diff --git a/src/raps/actions/swap.ts b/src/raps/actions/swap.ts index 2b1eddb690c..179f8ae7d27 100644 --- a/src/raps/actions/swap.ts +++ b/src/raps/actions/swap.ts @@ -93,9 +93,13 @@ export const estimateSwapGasLimit = async ({ WRAP_GAS_PADDING ); - return gasLimit || String(quote?.defaultGasLimit) || String(default_estimate); + if (gasLimit === null || gasLimit === undefined || isNaN(Number(gasLimit))) { + return quote?.defaultGasLimit || default_estimate; + } + + return gasLimit; } catch (e) { - return String(quote?.defaultGasLimit) || String(default_estimate); + return quote?.defaultGasLimit || default_estimate; } // Swap } else { @@ -116,8 +120,11 @@ export const estimateSwapGasLimit = async ({ } const gasLimit = await estimateGasWithPadding(params, method, methodArgs, provider, SWAP_GAS_PADDING); + if (gasLimit === null || gasLimit === undefined || isNaN(Number(gasLimit))) { + return getDefaultGasLimitForTrade(quote, chainId); + } - return gasLimit || getDefaultGasLimitForTrade(quote, chainId); + return gasLimit; } catch (error) { return getDefaultGasLimitForTrade(quote, chainId); } diff --git a/src/raps/actions/unlock.ts b/src/raps/actions/unlock.ts index b4377935b99..b2de3ff610c 100644 --- a/src/raps/actions/unlock.ts +++ b/src/raps/actions/unlock.ts @@ -91,7 +91,12 @@ export const estimateApprove = async ({ const gasLimit = await tokenContract.estimateGas.approve(spender, MaxUint256, { from: owner, }); - return gasLimit ? gasLimit.toString() : `${gasUnits.basic_approval}`; + + if (gasLimit === null || gasLimit === undefined || isNaN(Number(gasLimit.toString()))) { + return `${gasUnits.basic_approval}`; + } + + return gasLimit.toString(); } catch (error) { logger.error(new RainbowError('[raps/unlock]: error estimateApprove'), { message: (error as Error)?.message, diff --git a/src/raps/unlockAndCrosschainSwap.ts b/src/raps/unlockAndCrosschainSwap.ts index d1ff8fa64f3..a5d3b20ed21 100644 --- a/src/raps/unlockAndCrosschainSwap.ts +++ b/src/raps/unlockAndCrosschainSwap.ts @@ -53,10 +53,8 @@ export const estimateUnlockAndCrosschainSwap = async ({ }); } - let unlockGasLimit; - if (swapAssetNeedsUnlocking) { - unlockGasLimit = await estimateApprove({ + const unlockGasLimit = await estimateApprove({ owner: accountAddress, tokenAddress: sellTokenAddress, spender: allowanceTarget, @@ -71,7 +69,14 @@ export const estimateUnlockAndCrosschainSwap = async ({ quote, }); + if (swapGasLimit === null || swapGasLimit === undefined || isNaN(Number(swapGasLimit))) { + return null; + } + const gasLimit = gasLimits.concat(swapGasLimit).reduce((acc, limit) => add(acc, limit), '0'); + if (isNaN(Number(gasLimit))) { + return null; + } return gasLimit.toString(); }; diff --git a/src/raps/unlockAndSwap.ts b/src/raps/unlockAndSwap.ts index cdf9349adcb..769546efec6 100644 --- a/src/raps/unlockAndSwap.ts +++ b/src/raps/unlockAndSwap.ts @@ -43,7 +43,6 @@ export const estimateUnlockAndSwap = async ({ let swapAssetNeedsUnlocking = false; const nativeAsset = isLowerCaseMatch(ETH_ADDRESS_AGGREGATOR, sellTokenAddress) || isNativeAsset(sellTokenAddress, chainId); - if (!isNativeAssetUnwrapping && !nativeAsset) { swapAssetNeedsUnlocking = await assetNeedsUnlocking({ owner: accountAddress, @@ -65,12 +64,8 @@ export const estimateUnlockAndSwap = async ({ if (gasLimitFromMetadata) { return gasLimitFromMetadata; } - } - let unlockGasLimit; - - if (swapAssetNeedsUnlocking) { - unlockGasLimit = await estimateApprove({ + const unlockGasLimit = await estimateApprove({ owner: accountAddress, tokenAddress: sellTokenAddress, spender: getRainbowRouterContractAddress(chainId), @@ -85,7 +80,14 @@ export const estimateUnlockAndSwap = async ({ quote, }); + if (swapGasLimit === null || swapGasLimit === undefined || isNaN(Number(swapGasLimit))) { + return null; + } + const gasLimit = gasLimits.concat(swapGasLimit).reduce((acc, limit) => add(acc, limit), '0'); + if (isNaN(Number(gasLimit))) { + return null; + } return gasLimit.toString(); };