diff --git a/app/scripts/controllers/swaps.js b/app/scripts/controllers/swaps.js index 476d020ee..3bafff418 100644 --- a/app/scripts/controllers/swaps.js +++ b/app/scripts/controllers/swaps.js @@ -17,6 +17,7 @@ import { SWAPS_CHAINID_CONTRACT_ADDRESS_MAP, } from '../../../shared/constants/swaps'; import { GAS_ESTIMATE_TYPES } from '../../../shared/constants/gas'; +import { CHAIN_IDS } from '../../../shared/constants/network'; import { FALLBACK_SMART_TRANSACTIONS_REFRESH_TIME, FALLBACK_SMART_TRANSACTIONS_DEADLINE, @@ -24,6 +25,7 @@ import { } from '../../../shared/constants/smartTransactions'; import { isSwapsDefaultTokenAddress } from '../../../shared/modules/swaps.utils'; +import { sumHexes } from '../../../ui/helpers/utils/transactions.util'; import { fetchTradesInfo as defaultFetchTradesInfo, @@ -36,6 +38,7 @@ import { calcGasTotal, calcTokenAmount, } from '../../../shared/lib/transactions-controller-utils'; +import fetchEstimatedL1Fee from '../../../ui/helpers/utils/optimism/fetchEstimatedL1Fee'; import { NETWORK_EVENTS } from './network'; @@ -291,6 +294,24 @@ export default class SwapsController { destinationTokenInfo: fetchParamsMetaData.destinationTokenInfo, })); + if (chainId === CHAIN_IDS.OPTIMISM && Object.values(newQuotes).length > 0) { + await Promise.all( + Object.values(newQuotes).map(async (quote) => { + if (quote.trade) { + const multiLayerL1TradeFeeTotal = await fetchEstimatedL1Fee( + { + txParams: quote.trade, + chainId, + }, + this.ethersProvider, + ); + quote.multiLayerL1TradeFeeTotal = multiLayerL1TradeFeeTotal; + } + return quote; + }), + ); + } + const quotesLastFetched = Date.now(); let approvalRequired = false; @@ -700,6 +721,7 @@ export default class SwapsController { sourceToken, trade, fee: metaMaskFee, + multiLayerL1TradeFeeTotal, } = quote; const tradeGasLimitForCalculation = gasEstimateWithRefund @@ -710,10 +732,16 @@ export default class SwapsController { .plus(approvalNeeded?.gas || '0x0', 16) .toString(16); - const gasTotalInWeiHex = calcGasTotal( + let gasTotalInWeiHex = calcGasTotal( totalGasLimitForCalculation, usedGasPrice, ); + if (multiLayerL1TradeFeeTotal !== null) { + gasTotalInWeiHex = sumHexes( + gasTotalInWeiHex || '0x0', + multiLayerL1TradeFeeTotal || '0x0', + ); + } // trade.value is a sum of different values depending on the transaction. // It always includes any external fees charged by the quote source. In diff --git a/ui/helpers/utils/optimism/fetchEstimatedL1Fee.js b/ui/helpers/utils/optimism/fetchEstimatedL1Fee.js index da9477fd2..0265200c2 100644 --- a/ui/helpers/utils/optimism/fetchEstimatedL1Fee.js +++ b/ui/helpers/utils/optimism/fetchEstimatedL1Fee.js @@ -19,8 +19,10 @@ const OPTIMISM_GAS_PRICE_ORACLE_ABI = [ const OPTIMISM_GAS_PRICE_ORACLE_ADDRESS = '0x420000000000000000000000000000000000000F'; -export default async function fetchEstimatedL1Fee(txMeta) { - const provider = new Web3Provider(global.ethereumProvider, 10); +export default async function fetchEstimatedL1Fee(txMeta, ethersProvider) { + const provider = global.ethereumProvider + ? new Web3Provider(global.ethereumProvider, 10) + : ethersProvider; if (process.env.IN_TEST) { provider.detectNetwork = async () => ({ name: 'optimism', diff --git a/ui/pages/swaps/swaps.util.js b/ui/pages/swaps/swaps.util.js index 15a4966cd..3f5227fb9 100644 --- a/ui/pages/swaps/swaps.util.js +++ b/ui/pages/swaps/swaps.util.js @@ -361,7 +361,7 @@ export function quotesToRenderableData( chainId, smartTransactionEstimatedGas, nativeCurrencySymbol, - multiLayerL1FeeTotal, + multiLayerL1ApprovalFeeTotal, ) { return Object.values(quotes).map((quote) => { const { @@ -376,7 +376,20 @@ export function quotesToRenderableData( averageGas, fee, trade, + multiLayerL1TradeFeeTotal, } = quote; + let multiLayerL1FeeTotal = null; + if ( + multiLayerL1TradeFeeTotal !== null && + multiLayerL1ApprovalFeeTotal !== null + ) { + multiLayerL1FeeTotal = sumHexes( + multiLayerL1TradeFeeTotal || '0x0', + multiLayerL1ApprovalFeeTotal || '0x0', + ); + } else if (multiLayerL1TradeFeeTotal !== null) { + multiLayerL1FeeTotal = multiLayerL1TradeFeeTotal; + } const sourceValue = calcTokenAmount( sourceAmount, sourceTokenInfo.decimals, diff --git a/ui/pages/swaps/view-quote/view-quote.js b/ui/pages/swaps/view-quote/view-quote.js index 178ad6587..f5d4e256a 100644 --- a/ui/pages/swaps/view-quote/view-quote.js +++ b/ui/pages/swaps/view-quote/view-quote.js @@ -133,6 +133,8 @@ export default function ViewQuote() { const [warningHidden, setWarningHidden] = useState(false); const [originalApproveAmount, setOriginalApproveAmount] = useState(null); const [multiLayerL1FeeTotal, setMultiLayerL1FeeTotal] = useState(null); + const [multiLayerL1ApprovalFeeTotal, setMultiLayerL1ApprovalFeeTotal] = + useState(null); // We need to have currentTimestamp in state, otherwise it would change with each rerender. const [currentTimestamp] = useState(Date.now()); @@ -314,7 +316,7 @@ export default function ViewQuote() { smartTransactionsOptInStatus && smartTransactionFees?.tradeTxFees, nativeCurrencySymbol, - multiLayerL1FeeTotal, + multiLayerL1ApprovalFeeTotal, ); }, [ quotes, @@ -330,7 +332,7 @@ export default function ViewQuote() { nativeCurrencySymbol, smartTransactionsEnabled, smartTransactionsOptInStatus, - multiLayerL1FeeTotal, + multiLayerL1ApprovalFeeTotal, ]); const renderableDataForUsedQuote = renderablePopoverData.find( @@ -891,15 +893,11 @@ export default function ViewQuote() { ]); useEffect(() => { - if (!isMultiLayerFeeNetwork) { + if (!isMultiLayerFeeNetwork || !usedQuote?.multiLayerL1TradeFeeTotal) { return; } - const getEstimatedL1Fee = async () => { + const getEstimatedL1Fees = async () => { try { - const l1TradeFeeTotal = await fetchEstimatedL1Fee({ - txParams: unsignedTransaction, - chainId, - }); let l1ApprovalFeeTotal = '0x0'; if (approveTxParams) { l1ApprovalFeeTotal = await fetchEstimatedL1Fee({ @@ -910,16 +908,27 @@ export default function ViewQuote() { }, chainId, }); + setMultiLayerL1ApprovalFeeTotal(l1ApprovalFeeTotal); } - const l1FeeTotal = sumHexes(l1TradeFeeTotal, l1ApprovalFeeTotal); + const l1FeeTotal = sumHexes( + usedQuote.multiLayerL1TradeFeeTotal, + l1ApprovalFeeTotal, + ); setMultiLayerL1FeeTotal(l1FeeTotal); } catch (e) { captureException(e); setMultiLayerL1FeeTotal(null); + setMultiLayerL1ApprovalFeeTotal(null); } }; - getEstimatedL1Fee(); - }, [unsignedTransaction, approveTxParams, isMultiLayerFeeNetwork, chainId]); + getEstimatedL1Fees(); + }, [ + unsignedTransaction, + approveTxParams, + isMultiLayerFeeNetwork, + chainId, + usedQuote, + ]); useEffect(() => { if (currentSmartTransactionsEnabled && smartTransactionsOptInStatus) {