From 0355e59143d646a27fe3c50807380aaef286572e Mon Sep 17 00:00:00 2001 From: Jyoti Puri Date: Wed, 5 Nov 2025 13:43:24 +0530 Subject: [PATCH 01/18] Update swap transaction depending on user selection --- .../dapp-swap-comparison-banner.tsx | 80 +++++++++++++++++-- .../dapp-swap-comparison-banner/index.scss | 2 +- .../edit-gas-fees-row/edit-gas-fees-row.tsx | 4 +- .../useDappSwapComparisonInfo.ts | 1 + .../dapp-swap-comparison/useSwapCheck.ts | 16 ++++ 5 files changed, 96 insertions(+), 7 deletions(-) create mode 100644 ui/pages/confirmations/hooks/transactions/dapp-swap-comparison/useSwapCheck.ts diff --git a/ui/pages/confirmations/components/confirm/dapp-swap-comparison-banner/dapp-swap-comparison-banner.tsx b/ui/pages/confirmations/components/confirm/dapp-swap-comparison-banner/dapp-swap-comparison-banner.tsx index 6ffa22d0faae..ba0234eee04d 100644 --- a/ui/pages/confirmations/components/confirm/dapp-swap-comparison-banner/dapp-swap-comparison-banner.tsx +++ b/ui/pages/confirmations/components/confirm/dapp-swap-comparison-banner/dapp-swap-comparison-banner.tsx @@ -14,17 +14,28 @@ import { TextColor, TextVariant, } from '@metamask/design-system-react'; -import { TransactionMeta } from '@metamask/transaction-controller'; -import { useSelector } from 'react-redux'; +import { + BatchTransaction, + TransactionMeta, +} from '@metamask/transaction-controller'; +import { TxData } from '@metamask/bridge-controller'; +import { toHex } from '@metamask/controller-utils'; +import { useDispatch, useSelector } from 'react-redux'; import { getRemoteFeatureFlags } from '../../../../../selectors/remote-feature-flags'; import { useI18nContext } from '../../../../../hooks/useI18nContext'; +import { updateTransaction } from '../../../../../store/actions'; import { useConfirmContext } from '../../../context/confirm'; import { useDappSwapComparisonInfo } from '../../../hooks/transactions/dapp-swap-comparison/useDappSwapComparisonInfo'; const DAPP_SWAP_COMPARISON_ORIGIN = 'https://app.uniswap.org'; const DAPP_SWAP_THRESHOLD = 0.01; +type DappSwapUiFlag = { + enabled: boolean; + threshold: number; +}; + const enum SwapType { Current = 'current', Metamask = 'metamask', @@ -66,11 +77,19 @@ const SwapButton = ({ const DappSwapComparisonInner = () => { const t = useI18nContext(); const { + selectedQuote, selectedQuoteValueDifference, gasDifference, tokenAmountDifference, destinationTokenSymbol, } = useDappSwapComparisonInfo(); + const dispatch = useDispatch(); + const { currentConfirmation } = useConfirmContext(); + const { dappSwapUi } = useSelector(getRemoteFeatureFlags) as { + dappSwapUi: DappSwapUiFlag; + }; + + // update selectedSwapType depending on data const [selectedSwapType, setSelectedSwapType] = useState( SwapType.Current, ); @@ -81,9 +100,60 @@ const DappSwapComparisonInner = () => { setShowDappSwapComparisonBanner(false); }, [setShowDappSwapComparisonBanner]); + const updateSwapToCurrent = useCallback(() => { + setSelectedSwapType(SwapType.Current); + setShowDappSwapComparisonBanner(true); + if (currentConfirmation.txParamsOriginal) { + dispatch( + updateTransaction( + { + ...currentConfirmation, + txParams: currentConfirmation.txParamsOriginal, + }, + false, + ), + ); + } + }, [ + currentConfirmation, + dispatch, + setSelectedSwapType, + setShowDappSwapComparisonBanner, + ]); + + const updateSwapToSelectedQuote = useCallback(() => { + setSelectedSwapType(SwapType.Metamask); + setShowDappSwapComparisonBanner(true); + const { value, gasLimit, data } = selectedQuote?.trade as TxData; + dispatch( + updateTransaction( + { + ...currentConfirmation, + txParams: { + ...currentConfirmation.txParams, + value, + gas: toHex(gasLimit ?? 0), + data, + }, + txParamsOriginal: currentConfirmation.txParams, + batchTransactions: [selectedQuote?.approval as BatchTransaction], + }, + false, + ), + ); + }, [ + currentConfirmation, + dispatch, + setSelectedSwapType, + setShowDappSwapComparisonBanner, + selectedQuote, + ]); + if ( process.env.DAPP_SWAP_SHIELD_ENABLED?.toString() !== 'true' || - selectedQuoteValueDifference < DAPP_SWAP_THRESHOLD + !dappSwapUi?.enabled || + selectedQuoteValueDifference < + (dappSwapUi?.threshold ?? DAPP_SWAP_THRESHOLD) ) { return null; } @@ -106,7 +176,7 @@ const DappSwapComparisonInner = () => { ? SwapButtonType.ButtonType : SwapButtonType.Text } - onClick={() => setSelectedSwapType(SwapType.Current)} + onClick={updateSwapToCurrent} label={t('current')} /> { ? SwapButtonType.ButtonType : SwapButtonType.Text } - onClick={() => setSelectedSwapType(SwapType.Metamask)} + onClick={updateSwapToSelectedQuote} label={t('saveAndEarn')} /> diff --git a/ui/pages/confirmations/components/confirm/dapp-swap-comparison-banner/index.scss b/ui/pages/confirmations/components/confirm/dapp-swap-comparison-banner/index.scss index 78d4f2a7191d..ba6ac735b77f 100644 --- a/ui/pages/confirmations/components/confirm/dapp-swap-comparison-banner/index.scss +++ b/ui/pages/confirmations/components/confirm/dapp-swap-comparison-banner/index.scss @@ -40,7 +40,7 @@ Disabling Stylelint's hex color rule here because the TypeScript migration dashb } &_text-save { - color: #c9f570; + color: var(--color-success-default-hover); margin-bottom: 4px; } diff --git a/ui/pages/confirmations/components/confirm/info/shared/edit-gas-fees-row/edit-gas-fees-row.tsx b/ui/pages/confirmations/components/confirm/info/shared/edit-gas-fees-row/edit-gas-fees-row.tsx index bd07e01c272e..0d6da4f01abf 100644 --- a/ui/pages/confirmations/components/confirm/info/shared/edit-gas-fees-row/edit-gas-fees-row.tsx +++ b/ui/pages/confirmations/components/confirm/info/shared/edit-gas-fees-row/edit-gas-fees-row.tsx @@ -22,6 +22,7 @@ import { getPreferences } from '../../../../../../../selectors'; import { useConfirmContext } from '../../../../../context/confirm'; import { useIsGaslessSupported } from '../../../../../hooks/gas/useIsGaslessSupported'; import { selectConfirmationAdvancedDetailsOpen } from '../../../../../selectors/preferences'; +import { useSwapCheck } from '../../../../../hooks/transactions/dapp-swap-comparison/useSwapCheck'; import { useBalanceChanges } from '../../../../simulation-details/useBalanceChanges'; import { useSelectedGasFeeToken } from '../../hooks/useGasFeeToken'; import { EditGasIconButton } from '../edit-gas-icon/edit-gas-icon-button'; @@ -58,6 +59,7 @@ export const EditGasFeesRow = ({ const fiatValue = gasFeeToken ? gasFeeToken.amountFiat : fiatFee; const tokenValue = gasFeeToken ? gasFeeToken.amountFormatted : nativeFee; const metamaskFeeFiat = gasFeeToken?.metamaskFeeFiat; + const { isQuotedSwap } = useSwapCheck(); const tooltip = gasFeeToken?.metaMaskFee && gasFeeToken.metaMaskFee !== '0x0' @@ -101,7 +103,7 @@ export const EditGasFeesRow = ({ {t('paidByMetaMask')} )} - {!gasFeeToken && !isGasFeeSponsored && ( + {!isQuotedSwap && !gasFeeToken && !isGasFeeSponsored && ( (); + const { txParamsOriginal, txParams } = currentConfirmation ?? { + txParams: { data: '' }, + }; + const { data: txParamsOriginalData } = txParamsOriginal ?? { data: '' }; + const { data: txParamsData } = txParams ?? {}; + + return { + isQuotedSwap: txParamsOriginal && txParamsOriginalData !== txParamsData, + }; +} From 89d86192698d227573c69b9f36bea4876aca01ad Mon Sep 17 00:00:00 2001 From: Jyoti Puri Date: Wed, 5 Nov 2025 16:46:12 +0530 Subject: [PATCH 02/18] Adding unit test coverage --- .../dapp-swap-comparison-banner.test.tsx | 84 +++++ .../dapp-swap-comparison-banner.tsx | 10 +- .../edit-gas-fees-row.test.tsx | 22 ++ .../useDappSwapComparisonInfo.test.ts | 303 +++++++++--------- .../dapp-swap-comparison/useSwapCheck.test.ts | 42 +++ .../dapp-swap-comparison/useSwapCheck.ts | 3 +- 6 files changed, 312 insertions(+), 152 deletions(-) create mode 100644 ui/pages/confirmations/hooks/transactions/dapp-swap-comparison/useSwapCheck.test.ts diff --git a/ui/pages/confirmations/components/confirm/dapp-swap-comparison-banner/dapp-swap-comparison-banner.test.tsx b/ui/pages/confirmations/components/confirm/dapp-swap-comparison-banner/dapp-swap-comparison-banner.test.tsx index 0c2ad609fed5..86987323303c 100644 --- a/ui/pages/confirmations/components/confirm/dapp-swap-comparison-banner/dapp-swap-comparison-banner.test.tsx +++ b/ui/pages/confirmations/components/confirm/dapp-swap-comparison-banner/dapp-swap-comparison-banner.test.tsx @@ -1,5 +1,7 @@ import React from 'react'; import configureMockStore from 'redux-mock-store'; +import { QuoteResponse } from '@metamask/bridge-controller'; +import { fireEvent } from '@testing-library/react'; import { getMockConfirmStateForTransaction } from '../../../../../../test/data/confirmations/helper'; import { mockSwapConfirmation } from '../../../../../../test/data/confirmations/contract-interaction'; @@ -7,8 +9,21 @@ import { renderWithConfirmContextProvider } from '../../../../../../test/lib/con import { getRemoteFeatureFlags } from '../../../../../selectors/remote-feature-flags'; import { Confirmation } from '../../../types/confirm'; import { useDappSwapComparisonInfo } from '../../../hooks/transactions/dapp-swap-comparison/useDappSwapComparisonInfo'; +import * as SwapCheckHook from '../../../hooks/transactions/dapp-swap-comparison/useSwapCheck'; import { DappSwapComparisonBanner } from './dapp-swap-comparison-banner'; +const mockDispatch = jest.fn(); +jest.mock('react-redux', () => ({ + ...jest.requireActual('react-redux'), + useDispatch: () => mockDispatch, +})); + +const mockUpdateTransaction = jest.fn(); +jest.mock('../../../../../store/actions', () => ({ + ...jest.requireActual('../../../../../store/actions'), + updateTransaction: () => mockUpdateTransaction, +})); + jest.mock( '../../../hooks/transactions/dapp-swap-comparison/useDappSwapComparisonInfo', () => ({ @@ -20,6 +35,43 @@ jest.mock( jest.mock('../../../../../selectors/remote-feature-flags'); +const quote = { + quote: { + aggregator: 'openocean', + requestId: + '0xf5fe1ea0c87b44825dfc89cc60c3398f1cf83eb49a07e491029e00cb72090ef2', + bridgeId: 'okx', + srcChainId: 42161, + destChainId: 42161, + srcTokenAmount: '9913', + destTokenAmount: '1004000', + minDestTokenAmount: '972870', + walletAddress: '0x178239802520a9C99DCBD791f81326B70298d629', + destWalletAddress: '0x178239802520a9C99DCBD791f81326B70298d629', + bridges: ['okx'], + protocols: ['okx'], + steps: [], + slippage: 2, + }, + approval: { + chainId: 42161, + to: '0xaf88d065e77c8cC2239327C5EDb3A432268e5831', + from: '0x178239802520a9C99DCBD791f81326B70298d629', + value: '0x0', + data: '', + gasLimit: 62000, + }, + trade: { + chainId: 42161, + to: '0x9dDA6Ef3D919c9bC8885D5560999A3640431e8e6', + from: '0x178239802520a9C99DCBD791f81326B70298d629', + value: '0x0', + data: '', + gasLimit: 80000, + }, + estimatedProcessingTimeInSeconds: 0, +}; + function render(args: Record = {}) { const state = getMockConfirmStateForTransaction({ ...mockSwapConfirmation, @@ -41,6 +93,7 @@ describe('', () => { beforeEach(() => { mockGetRemoteFeatureFlags.mockReturnValue({ dappSwapMetrics: { enabled: true }, + dappSwapUi: { enabled: true, threshold: 0.01 }, }); }); @@ -75,4 +128,35 @@ describe('', () => { const { container } = render(); expect(container).toBeEmptyDOMElement(); }); + + it('call function to update confirmation when user clicks on Save + Earn button', () => { + mockUseDappSwapComparisonInfo.mockReturnValue({ + selectedQuote: quote as unknown as QuoteResponse, + selectedQuoteValueDifference: 0.1, + gasDifference: 0.01, + tokenAmountDifference: 0.01, + destinationTokenSymbol: 'TEST', + } as ReturnType); + const { getByText } = render(); + const quoteSwapButton = getByText('Save + Earn'); + fireEvent.click(quoteSwapButton); + expect(mockDispatch).toHaveBeenCalledTimes(1); + }); + + it('call function to update confirmation when user clicks on Current button', () => { + jest.spyOn(SwapCheckHook, 'useSwapCheck').mockReturnValue({ + isQuotedSwap: true, + }); + mockUseDappSwapComparisonInfo.mockReturnValue({ + selectedQuote: quote as unknown as QuoteResponse, + selectedQuoteValueDifference: 0.1, + gasDifference: 0.01, + tokenAmountDifference: 0.01, + destinationTokenSymbol: 'TEST', + } as ReturnType); + const { getByText } = render(); + const quoteSwapButton = getByText('Current'); + fireEvent.click(quoteSwapButton); + expect(mockDispatch).toHaveBeenCalledTimes(1); + }); }); diff --git a/ui/pages/confirmations/components/confirm/dapp-swap-comparison-banner/dapp-swap-comparison-banner.tsx b/ui/pages/confirmations/components/confirm/dapp-swap-comparison-banner/dapp-swap-comparison-banner.tsx index ba0234eee04d..84bddaf6fac4 100644 --- a/ui/pages/confirmations/components/confirm/dapp-swap-comparison-banner/dapp-swap-comparison-banner.tsx +++ b/ui/pages/confirmations/components/confirm/dapp-swap-comparison-banner/dapp-swap-comparison-banner.tsx @@ -1,4 +1,4 @@ -import React, { useCallback, useState } from 'react'; +import React, { useCallback, useEffect, useState } from 'react'; import { Box, BoxBackgroundColor, @@ -27,6 +27,7 @@ import { useI18nContext } from '../../../../../hooks/useI18nContext'; import { updateTransaction } from '../../../../../store/actions'; import { useConfirmContext } from '../../../context/confirm'; import { useDappSwapComparisonInfo } from '../../../hooks/transactions/dapp-swap-comparison/useDappSwapComparisonInfo'; +import { useSwapCheck } from '../../../hooks/transactions/dapp-swap-comparison/useSwapCheck'; const DAPP_SWAP_COMPARISON_ORIGIN = 'https://app.uniswap.org'; const DAPP_SWAP_THRESHOLD = 0.01; @@ -83,6 +84,7 @@ const DappSwapComparisonInner = () => { tokenAmountDifference, destinationTokenSymbol, } = useDappSwapComparisonInfo(); + const { isQuotedSwap } = useSwapCheck(); const dispatch = useDispatch(); const { currentConfirmation } = useConfirmContext(); const { dappSwapUi } = useSelector(getRemoteFeatureFlags) as { @@ -96,6 +98,12 @@ const DappSwapComparisonInner = () => { const [showDappSwapComparisonBanner, setShowDappSwapComparisonBanner] = useState(true); + useEffect(() => { + if (isQuotedSwap && selectedSwapType !== SwapType.Metamask) { + setSelectedSwapType(SwapType.Metamask); + } + }, [isQuotedSwap, selectedSwapType]); + const hideDappSwapComparisonBanner = useCallback(() => { setShowDappSwapComparisonBanner(false); }, [setShowDappSwapComparisonBanner]); diff --git a/ui/pages/confirmations/components/confirm/info/shared/edit-gas-fees-row/edit-gas-fees-row.test.tsx b/ui/pages/confirmations/components/confirm/info/shared/edit-gas-fees-row/edit-gas-fees-row.test.tsx index 076484d57f64..4f0824bd1ab7 100644 --- a/ui/pages/confirmations/components/confirm/info/shared/edit-gas-fees-row/edit-gas-fees-row.test.tsx +++ b/ui/pages/confirmations/components/confirm/info/shared/edit-gas-fees-row/edit-gas-fees-row.test.tsx @@ -7,6 +7,7 @@ import { getMockConfirmStateForTransaction } from '../../../../../../../../test/ import { renderWithConfirmContextProvider } from '../../../../../../../../test/lib/confirmations/render-helpers'; import { GAS_FEE_TOKEN_MOCK } from '../../../../../../../../test/data/confirmations/gas'; import { genUnapprovedContractInteractionConfirmation } from '../../../../../../../../test/data/confirmations/contract-interaction'; +import * as SwapCheckHook from '../../../../../hooks/transactions/dapp-swap-comparison/useSwapCheck'; import { EditGasFeesRow } from './edit-gas-fees-row'; jest.mock('../../../../simulation-details/useBalanceChanges', () => ({ @@ -62,4 +63,25 @@ describe('', () => { expect(getByTestId('gas-fee-token-fee')).toBeInTheDocument(); }); + + it('renders edit gas fee button', () => { + const { getByTestId } = render({ + gasFeeTokens: undefined, + selectedGasFeeToken: undefined, + }); + + expect(getByTestId('edit-gas-fee-icon')).toBeInTheDocument(); + }); + + it('does not renders edit gas fee button for quote suggested swap', () => { + jest.spyOn(SwapCheckHook, 'useSwapCheck').mockReturnValue({ + isQuotedSwap: true, + }); + const { queryByTestId } = render({ + gasFeeTokens: undefined, + selectedGasFeeToken: undefined, + }); + + expect(queryByTestId('edit-gas-fee-icon')).toBeNull(); + }); }); diff --git a/ui/pages/confirmations/hooks/transactions/dapp-swap-comparison/useDappSwapComparisonInfo.test.ts b/ui/pages/confirmations/hooks/transactions/dapp-swap-comparison/useDappSwapComparisonInfo.test.ts index a8ed6830076f..818f74d49d0c 100644 --- a/ui/pages/confirmations/hooks/transactions/dapp-swap-comparison/useDappSwapComparisonInfo.test.ts +++ b/ui/pages/confirmations/hooks/transactions/dapp-swap-comparison/useDappSwapComparisonInfo.test.ts @@ -55,161 +55,162 @@ async function runHook() { return response.result.current; } +const quotes = [ + { + quote: { + requestId: + '0xe00805b32ac0b8ef9a2cb1d2b18a3c3eed1a9190fdfa8e99b718df90d9070026', + bridgeId: 'kyberswap', + srcChainId: 42161, + destChainId: 42161, + aggregator: 'kyberswap', + aggregatorType: 'AGG', + srcTokenAmount: '9913', + destTokenAmount: '9902', + minDestTokenAmount: '9703', + walletAddress: '0x178239802520a9C99DCBD791f81326B70298d629', + destWalletAddress: '0x178239802520a9C99DCBD791f81326B70298d629', + bridges: ['kyberswap'], + protocols: ['kyberswap'], + steps: [], + slippage: 2, + }, + approval: { + chainId: 42161, + to: '0xaf88d065e77c8cC2239327C5EDb3A432268e5831', + from: '0x178239802520a9C99DCBD791f81326B70298d629', + value: '0x0', + data: '', + gasLimit: 63103, + }, + trade: { + chainId: 42161, + to: '0x9dDA6Ef3D919c9bC8885D5560999A3640431e8e6', + from: '0x178239802520a9C99DCBD791f81326B70298d629', + value: '0x0', + data: '', + gasLimit: 399863, + }, + estimatedProcessingTimeInSeconds: 0, + }, + { + quote: { + requestId: + '0xb7e0cdb746800056208ae5408a1755d9c8c10970c067a5e1fbbe768d2f6f626c', + bridgeId: '0x', + srcChainId: 42161, + destChainId: 42161, + aggregator: 'openocean', + aggregatorType: 'AGG', + srcTokenAmount: '9913', + destTokenAmount: '11104', + minDestTokenAmount: '10881', + walletAddress: '0x178239802520a9C99DCBD791f81326B70298d629', + destWalletAddress: '0x178239802520a9C99DCBD791f81326B70298d629', + bridges: ['0x'], + protocols: ['0x'], + steps: [], + slippage: 2, + }, + approval: { + chainId: 42161, + to: '0xaf88d065e77c8cC2239327C5EDb3A432268e5831', + from: '0x178239802520a9C99DCBD791f81326B70298d629', + value: '0x0', + data: '', + gasLimit: 63109, + }, + trade: { + chainId: 42161, + to: '0x9dDA6Ef3D919c9bC8885D5560999A3640431e8e6', + from: '0x178239802520a9C99DCBD791f81326B70298d629', + value: '0x0', + data: '', + gasLimit: 596053, + }, + estimatedProcessingTimeInSeconds: 0, + }, + { + quote: { + requestId: + '0xea157f969eb2bf91ae3f78f461788f3cc789617978e94a56326a94607674e7cf', + bridgeId: 'openocean', + srcChainId: 42161, + destChainId: 42161, + aggregator: 'openocean', + aggregatorType: 'AGG', + srcTokenAmount: '9913', + destTokenAmount: '10200', + minDestTokenAmount: '9996', + walletAddress: '0x178239802520a9C99DCBD791f81326B70298d629', + destWalletAddress: '0x178239802520a9C99DCBD791f81326B70298d629', + bridges: ['openocean'], + protocols: ['openocean'], + steps: [], + slippage: 2, + }, + approval: { + chainId: 42161, + to: '0xaf88d065e77c8cC2239327C5EDb3A432268e5831', + from: '0x178239802520a9C99DCBD791f81326B70298d629', + value: '0x0', + data: '', + gasLimit: 63109, + }, + trade: { + chainId: 42161, + to: '0x9dDA6Ef3D919c9bC8885D5560999A3640431e8e6', + from: '0x178239802520a9C99DCBD791f81326B70298d629', + value: '0x0', + data: '', + gasLimit: 679877, + }, + estimatedProcessingTimeInSeconds: 0, + }, + { + quote: { + aggregator: 'openocean', + requestId: + '0xf5fe1ea0c87b44825dfc89cc60c3398f1cf83eb49a07e491029e00cb72090ef2', + bridgeId: 'okx', + srcChainId: 42161, + destChainId: 42161, + srcTokenAmount: '9913', + destTokenAmount: '1004000', + minDestTokenAmount: '972870', + walletAddress: '0x178239802520a9C99DCBD791f81326B70298d629', + destWalletAddress: '0x178239802520a9C99DCBD791f81326B70298d629', + bridges: ['okx'], + protocols: ['okx'], + steps: [], + slippage: 2, + }, + approval: { + chainId: 42161, + to: '0xaf88d065e77c8cC2239327C5EDb3A432268e5831', + from: '0x178239802520a9C99DCBD791f81326B70298d629', + value: '0x0', + data: '', + gasLimit: 62000, + }, + trade: { + chainId: 42161, + to: '0x9dDA6Ef3D919c9bC8885D5560999A3640431e8e6', + from: '0x178239802520a9C99DCBD791f81326B70298d629', + value: '0x0', + data: '', + gasLimit: 80000, + }, + estimatedProcessingTimeInSeconds: 0, + }, +] as unknown as QuoteResponse[]; + describe('useDappSwapComparisonInfo', () => { const fetchQuotesMock = jest.mocked(fetchQuotes); beforeEach(() => { jest.resetAllMocks(); - - fetchQuotesMock.mockResolvedValue([ - { - quote: { - requestId: - '0xe00805b32ac0b8ef9a2cb1d2b18a3c3eed1a9190fdfa8e99b718df90d9070026', - bridgeId: 'kyberswap', - srcChainId: 42161, - destChainId: 42161, - aggregator: 'kyberswap', - aggregatorType: 'AGG', - srcTokenAmount: '9913', - destTokenAmount: '9902', - minDestTokenAmount: '9703', - walletAddress: '0x178239802520a9C99DCBD791f81326B70298d629', - destWalletAddress: '0x178239802520a9C99DCBD791f81326B70298d629', - bridges: ['kyberswap'], - protocols: ['kyberswap'], - steps: [], - slippage: 2, - }, - approval: { - chainId: 42161, - to: '0xaf88d065e77c8cC2239327C5EDb3A432268e5831', - from: '0x178239802520a9C99DCBD791f81326B70298d629', - value: '0x0', - data: '', - gasLimit: 63103, - }, - trade: { - chainId: 42161, - to: '0x9dDA6Ef3D919c9bC8885D5560999A3640431e8e6', - from: '0x178239802520a9C99DCBD791f81326B70298d629', - value: '0x0', - data: '', - gasLimit: 399863, - }, - estimatedProcessingTimeInSeconds: 0, - }, - { - quote: { - requestId: - '0xb7e0cdb746800056208ae5408a1755d9c8c10970c067a5e1fbbe768d2f6f626c', - bridgeId: '0x', - srcChainId: 42161, - destChainId: 42161, - aggregator: 'openocean', - aggregatorType: 'AGG', - srcTokenAmount: '9913', - destTokenAmount: '11104', - minDestTokenAmount: '10881', - walletAddress: '0x178239802520a9C99DCBD791f81326B70298d629', - destWalletAddress: '0x178239802520a9C99DCBD791f81326B70298d629', - bridges: ['0x'], - protocols: ['0x'], - steps: [], - slippage: 2, - }, - approval: { - chainId: 42161, - to: '0xaf88d065e77c8cC2239327C5EDb3A432268e5831', - from: '0x178239802520a9C99DCBD791f81326B70298d629', - value: '0x0', - data: '', - gasLimit: 63109, - }, - trade: { - chainId: 42161, - to: '0x9dDA6Ef3D919c9bC8885D5560999A3640431e8e6', - from: '0x178239802520a9C99DCBD791f81326B70298d629', - value: '0x0', - data: '', - gasLimit: 596053, - }, - estimatedProcessingTimeInSeconds: 0, - }, - { - quote: { - requestId: - '0xea157f969eb2bf91ae3f78f461788f3cc789617978e94a56326a94607674e7cf', - bridgeId: 'openocean', - srcChainId: 42161, - destChainId: 42161, - aggregator: 'openocean', - aggregatorType: 'AGG', - srcTokenAmount: '9913', - destTokenAmount: '10200', - minDestTokenAmount: '9996', - walletAddress: '0x178239802520a9C99DCBD791f81326B70298d629', - destWalletAddress: '0x178239802520a9C99DCBD791f81326B70298d629', - bridges: ['openocean'], - protocols: ['openocean'], - steps: [], - slippage: 2, - }, - approval: { - chainId: 42161, - to: '0xaf88d065e77c8cC2239327C5EDb3A432268e5831', - from: '0x178239802520a9C99DCBD791f81326B70298d629', - value: '0x0', - data: '', - gasLimit: 63109, - }, - trade: { - chainId: 42161, - to: '0x9dDA6Ef3D919c9bC8885D5560999A3640431e8e6', - from: '0x178239802520a9C99DCBD791f81326B70298d629', - value: '0x0', - data: '', - gasLimit: 679877, - }, - estimatedProcessingTimeInSeconds: 0, - }, - { - quote: { - aggregator: 'openocean', - requestId: - '0xf5fe1ea0c87b44825dfc89cc60c3398f1cf83eb49a07e491029e00cb72090ef2', - bridgeId: 'okx', - srcChainId: 42161, - destChainId: 42161, - srcTokenAmount: '9913', - destTokenAmount: '1004000', - minDestTokenAmount: '972870', - walletAddress: '0x178239802520a9C99DCBD791f81326B70298d629', - destWalletAddress: '0x178239802520a9C99DCBD791f81326B70298d629', - bridges: ['okx'], - protocols: ['okx'], - steps: [], - slippage: 2, - }, - approval: { - chainId: 42161, - to: '0xaf88d065e77c8cC2239327C5EDb3A432268e5831', - from: '0x178239802520a9C99DCBD791f81326B70298d629', - value: '0x0', - data: '', - gasLimit: 62000, - }, - trade: { - chainId: 42161, - to: '0x9dDA6Ef3D919c9bC8885D5560999A3640431e8e6', - from: '0x178239802520a9C99DCBD791f81326B70298d629', - value: '0x0', - data: '', - gasLimit: 80000, - }, - estimatedProcessingTimeInSeconds: 0, - }, - ] as unknown as QuoteResponse[]); + fetchQuotesMock.mockResolvedValue(quotes); }); it('initially call updateTransactionEventFragment with loading', async () => { @@ -315,11 +316,13 @@ describe('useDappSwapComparisonInfo', () => { }); const { + selectedQuote, selectedQuoteValueDifference, gasDifference, tokenAmountDifference, destinationTokenSymbol, } = await runHook(); + expect(selectedQuote).toEqual(quotes[3]); expect(selectedQuoteValueDifference).toBe(0.012494042894187605); expect(gasDifference).toBe(0.005686377458187605); expect(tokenAmountDifference).toBe(0.006807665436); diff --git a/ui/pages/confirmations/hooks/transactions/dapp-swap-comparison/useSwapCheck.test.ts b/ui/pages/confirmations/hooks/transactions/dapp-swap-comparison/useSwapCheck.test.ts new file mode 100644 index 000000000000..33625bb944dc --- /dev/null +++ b/ui/pages/confirmations/hooks/transactions/dapp-swap-comparison/useSwapCheck.test.ts @@ -0,0 +1,42 @@ +import { act } from '@testing-library/react'; + +import { getMockConfirmStateForTransaction } from '../../../../../../test/data/confirmations/helper'; +import { mockSwapConfirmation } from '../../../../../../test/data/confirmations/contract-interaction'; +import { renderHookWithConfirmContextProvider } from '../../../../../../test/lib/confirmations/render-helpers'; +import { Confirmation } from '../../../types/confirm'; +import { useSwapCheck } from './useSwapCheck'; + +async function runHook(mockConfirmation?: Confirmation) { + const response = renderHookWithConfirmContextProvider( + useSwapCheck, + getMockConfirmStateForTransaction( + mockConfirmation ?? (mockSwapConfirmation as Confirmation), + ), + ); + + await act(async () => { + // Ignore + }); + + return response.result.current; +} + +describe('useSwapCheck', () => { + it('return isQuotedSwap false for dapp suggested swap', async () => { + const { isQuotedSwap } = await runHook(); + expect(isQuotedSwap).toBe(false); + }); + + it('return isQuotedSwap true for quoted swap', async () => { + const mockConfirmation = { + ...mockSwapConfirmation, + txParamsOriginal: mockSwapConfirmation.txParams, + txParams: { + ...mockSwapConfirmation.txParams, + data: '0x1234567890', + }, + }; + const { isQuotedSwap } = await runHook(mockConfirmation as Confirmation); + expect(isQuotedSwap).toBe(true); + }); +}); diff --git a/ui/pages/confirmations/hooks/transactions/dapp-swap-comparison/useSwapCheck.ts b/ui/pages/confirmations/hooks/transactions/dapp-swap-comparison/useSwapCheck.ts index 67c286c48aa8..e48d55f5b74b 100644 --- a/ui/pages/confirmations/hooks/transactions/dapp-swap-comparison/useSwapCheck.ts +++ b/ui/pages/confirmations/hooks/transactions/dapp-swap-comparison/useSwapCheck.ts @@ -11,6 +11,7 @@ export function useSwapCheck() { const { data: txParamsData } = txParams ?? {}; return { - isQuotedSwap: txParamsOriginal && txParamsOriginalData !== txParamsData, + isQuotedSwap: + (txParamsOriginal && txParamsOriginalData !== txParamsData) ?? false, }; } From 9528a13c4851abaf8b9cae0fe21d6b763fb2c4ef Mon Sep 17 00:00:00 2001 From: Jyoti Puri Date: Wed, 5 Nov 2025 16:47:36 +0530 Subject: [PATCH 03/18] Adding unit test coverage --- builds.yml | 3 --- test/env.js | 1 - .../dapp-swap-comparison-banner.tsx | 1 - 3 files changed, 5 deletions(-) diff --git a/builds.yml b/builds.yml index f814d96e57d1..2f9356ac9a12 100644 --- a/builds.yml +++ b/builds.yml @@ -455,6 +455,3 @@ env: # This should only be used for local testing, and should not be enabled in any # production builds (including beta and Flask). - FORCE_PREINSTALLED_SNAPS: 'false' - - # Enable dapp swap shield implementation - - DAPP_SWAP_SHIELD_ENABLED: false diff --git a/test/env.js b/test/env.js index b2e5a770cfad..715dfe22f0e9 100644 --- a/test/env.js +++ b/test/env.js @@ -18,4 +18,3 @@ process.env.METAMASK_VERSION = 'MOCK_VERSION'; process.env.TZ = 'UTC'; process.env.SEEDLESS_ONBOARDING_ENABLED = 'true'; process.env.METAMASK_SHIELD_ENABLED = 'false'; -process.env.DAPP_SWAP_SHIELD_ENABLED = 'true'; diff --git a/ui/pages/confirmations/components/confirm/dapp-swap-comparison-banner/dapp-swap-comparison-banner.tsx b/ui/pages/confirmations/components/confirm/dapp-swap-comparison-banner/dapp-swap-comparison-banner.tsx index 84bddaf6fac4..d7057cbd6ac9 100644 --- a/ui/pages/confirmations/components/confirm/dapp-swap-comparison-banner/dapp-swap-comparison-banner.tsx +++ b/ui/pages/confirmations/components/confirm/dapp-swap-comparison-banner/dapp-swap-comparison-banner.tsx @@ -158,7 +158,6 @@ const DappSwapComparisonInner = () => { ]); if ( - process.env.DAPP_SWAP_SHIELD_ENABLED?.toString() !== 'true' || !dappSwapUi?.enabled || selectedQuoteValueDifference < (dappSwapUi?.threshold ?? DAPP_SWAP_THRESHOLD) From 3bb477c3e6b33e2ec245d50cc7d0d963643e4658 Mon Sep 17 00:00:00 2001 From: Jyoti Puri Date: Wed, 5 Nov 2025 17:20:07 +0530 Subject: [PATCH 04/18] update --- .../dapp-swap-comparison/useDappSwapComparisonInfo.ts | 3 +-- .../useDappSwapComparisonLatencyMetrics.ts | 5 ++++- 2 files changed, 5 insertions(+), 3 deletions(-) diff --git a/ui/pages/confirmations/hooks/transactions/dapp-swap-comparison/useDappSwapComparisonInfo.ts b/ui/pages/confirmations/hooks/transactions/dapp-swap-comparison/useDappSwapComparisonInfo.ts index 46061d8a728d..535cc7df30e8 100644 --- a/ui/pages/confirmations/hooks/transactions/dapp-swap-comparison/useDappSwapComparisonInfo.ts +++ b/ui/pages/confirmations/hooks/transactions/dapp-swap-comparison/useDappSwapComparisonInfo.ts @@ -111,11 +111,10 @@ export function useDappSwapComparisonInfo() { }, }); - const startTime = new Date().getTime(); updateQuoteRequestLatency(); + const startTime = new Date().getTime(); const quotesList = await fetchQuotes(quotesInput); - updateQuoteResponseLatency(startTime); return quotesList; }, [ diff --git a/ui/pages/confirmations/hooks/transactions/dapp-swap-comparison/useDappSwapComparisonLatencyMetrics.ts b/ui/pages/confirmations/hooks/transactions/dapp-swap-comparison/useDappSwapComparisonLatencyMetrics.ts index 49827cc822a2..8f78355d818c 100644 --- a/ui/pages/confirmations/hooks/transactions/dapp-swap-comparison/useDappSwapComparisonLatencyMetrics.ts +++ b/ui/pages/confirmations/hooks/transactions/dapp-swap-comparison/useDappSwapComparisonLatencyMetrics.ts @@ -11,17 +11,20 @@ export function useDappSwapComparisonLatencyMetrics() { const [quoteRequestLatency, setQuoteRequestLatency] = useState(N_A); const [quoteResponseLatency, setQuoteResponseLatency] = useState(N_A); const [swapComparisonLatency, setSwapComparisonLatency] = useState(N_A); + const [time2, setTime2] = useState(0); const updateRequestDetectionLatency = useCallback(() => { if (requestDetectionLatency !== N_A) { return; } + setTime2(new Date().getTime()); setRequestDetectionLatency( (new Date().getTime() - currentConfirmation?.time).toString(), ); }, [ currentConfirmation?.time, requestDetectionLatency, + setTime2, setRequestDetectionLatency, ]); @@ -32,7 +35,7 @@ export function useDappSwapComparisonLatencyMetrics() { setQuoteRequestLatency( ( new Date().getTime() - - currentConfirmation?.time + + currentConfirmation?.time - parseInt(requestDetectionLatency, 10) ).toString(), ); From a33821378921518d99975819d82920487a4653a4 Mon Sep 17 00:00:00 2001 From: Jyoti Puri Date: Wed, 5 Nov 2025 17:26:06 +0530 Subject: [PATCH 05/18] update --- .../dapp-swap-comparison-banner/dapp-swap-comparison-banner.tsx | 1 + 1 file changed, 1 insertion(+) diff --git a/ui/pages/confirmations/components/confirm/dapp-swap-comparison-banner/dapp-swap-comparison-banner.tsx b/ui/pages/confirmations/components/confirm/dapp-swap-comparison-banner/dapp-swap-comparison-banner.tsx index d7057cbd6ac9..2b42ee0be7f2 100644 --- a/ui/pages/confirmations/components/confirm/dapp-swap-comparison-banner/dapp-swap-comparison-banner.tsx +++ b/ui/pages/confirmations/components/confirm/dapp-swap-comparison-banner/dapp-swap-comparison-banner.tsx @@ -117,6 +117,7 @@ const DappSwapComparisonInner = () => { { ...currentConfirmation, txParams: currentConfirmation.txParamsOriginal, + batchTransactions: undefined, }, false, ), From c8fedc95004fe20a64d00886c7d38a65766c3e81 Mon Sep 17 00:00:00 2001 From: Jyoti Puri Date: Wed, 5 Nov 2025 17:37:46 +0530 Subject: [PATCH 06/18] update --- .../useDappSwapComparisonLatencyMetrics.ts | 3 --- 1 file changed, 3 deletions(-) diff --git a/ui/pages/confirmations/hooks/transactions/dapp-swap-comparison/useDappSwapComparisonLatencyMetrics.ts b/ui/pages/confirmations/hooks/transactions/dapp-swap-comparison/useDappSwapComparisonLatencyMetrics.ts index 8f78355d818c..5c6539acd646 100644 --- a/ui/pages/confirmations/hooks/transactions/dapp-swap-comparison/useDappSwapComparisonLatencyMetrics.ts +++ b/ui/pages/confirmations/hooks/transactions/dapp-swap-comparison/useDappSwapComparisonLatencyMetrics.ts @@ -11,20 +11,17 @@ export function useDappSwapComparisonLatencyMetrics() { const [quoteRequestLatency, setQuoteRequestLatency] = useState(N_A); const [quoteResponseLatency, setQuoteResponseLatency] = useState(N_A); const [swapComparisonLatency, setSwapComparisonLatency] = useState(N_A); - const [time2, setTime2] = useState(0); const updateRequestDetectionLatency = useCallback(() => { if (requestDetectionLatency !== N_A) { return; } - setTime2(new Date().getTime()); setRequestDetectionLatency( (new Date().getTime() - currentConfirmation?.time).toString(), ); }, [ currentConfirmation?.time, requestDetectionLatency, - setTime2, setRequestDetectionLatency, ]); From b7ae1869b8cf525f00b4b5f3d3b5de48ebd3e9e8 Mon Sep 17 00:00:00 2001 From: Jyoti Puri Date: Thu, 6 Nov 2025 10:05:48 +0530 Subject: [PATCH 07/18] update --- .../dapp-swap-comparison-banner.tsx | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/ui/pages/confirmations/components/confirm/dapp-swap-comparison-banner/dapp-swap-comparison-banner.tsx b/ui/pages/confirmations/components/confirm/dapp-swap-comparison-banner/dapp-swap-comparison-banner.tsx index 2b42ee0be7f2..b95d2d6530fd 100644 --- a/ui/pages/confirmations/components/confirm/dapp-swap-comparison-banner/dapp-swap-comparison-banner.tsx +++ b/ui/pages/confirmations/components/confirm/dapp-swap-comparison-banner/dapp-swap-comparison-banner.tsx @@ -30,6 +30,7 @@ import { useDappSwapComparisonInfo } from '../../../hooks/transactions/dapp-swap import { useSwapCheck } from '../../../hooks/transactions/dapp-swap-comparison/useSwapCheck'; const DAPP_SWAP_COMPARISON_ORIGIN = 'https://app.uniswap.org'; +const TEST_DAPP_ORIGIN = 'https://metamask.github.io'; const DAPP_SWAP_THRESHOLD = 0.01; type DappSwapUiFlag = { @@ -256,7 +257,8 @@ export const DappSwapComparisonBanner = () => { const dappSwapMetricsEnabled = (dappSwapMetrics as { enabled: boolean })?.enabled === true && - transactionMeta.origin === DAPP_SWAP_COMPARISON_ORIGIN; + (transactionMeta.origin === DAPP_SWAP_COMPARISON_ORIGIN || + transactionMeta.origin === TEST_DAPP_ORIGIN); if (!dappSwapMetricsEnabled) { return null; From 933ebfb4f5a74ba2cece3742e3485fa633ff6bd7 Mon Sep 17 00:00:00 2001 From: Jyoti Puri Date: Thu, 6 Nov 2025 10:56:45 +0530 Subject: [PATCH 08/18] update --- .../dapp-swap-comparison/useDappSwapComparisonInfo.ts | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/ui/pages/confirmations/hooks/transactions/dapp-swap-comparison/useDappSwapComparisonInfo.ts b/ui/pages/confirmations/hooks/transactions/dapp-swap-comparison/useDappSwapComparisonInfo.ts index 535cc7df30e8..7cd5f767ce8c 100644 --- a/ui/pages/confirmations/hooks/transactions/dapp-swap-comparison/useDappSwapComparisonInfo.ts +++ b/ui/pages/confirmations/hooks/transactions/dapp-swap-comparison/useDappSwapComparisonInfo.ts @@ -30,11 +30,12 @@ export function useDappSwapComparisonInfo() { id: transactionId, simulationData, txParams, + txParamsOriginal, nestedTransactions, } = currentConfirmation ?? { txParams: {}, }; - const { data, gas } = txParams ?? {}; + const { data, gas } = txParamsOriginal ?? txParams ?? {}; const { updateTransactionEventFragment } = useTransactionEventFragment(); const { requestDetectionLatency, From ec93043d5748d303d025289ac514c0dea4548070 Mon Sep 17 00:00:00 2001 From: Jyoti Puri Date: Thu, 6 Nov 2025 09:28:17 +0530 Subject: [PATCH 09/18] For quoted swap use data from quote to populate estimated changes --- .../dapp-swap-comparison-banner.tsx | 20 +++- .../batch-simulation-details.tsx | 17 +-- .../quote-swap-simulation-details.tsx | 109 ++++++++++++++++++ .../simulation-details/simulation-details.tsx | 2 +- .../useDappSwapComparisonInfo.ts | 8 +- .../useDappSwapUSDValues.ts | 1 + 6 files changed, 144 insertions(+), 13 deletions(-) create mode 100644 ui/pages/confirmations/components/confirm/quote-swap-simulation-details/quote-swap-simulation-details.tsx diff --git a/ui/pages/confirmations/components/confirm/dapp-swap-comparison-banner/dapp-swap-comparison-banner.tsx b/ui/pages/confirmations/components/confirm/dapp-swap-comparison-banner/dapp-swap-comparison-banner.tsx index b95d2d6530fd..3f1c6f609463 100644 --- a/ui/pages/confirmations/components/confirm/dapp-swap-comparison-banner/dapp-swap-comparison-banner.tsx +++ b/ui/pages/confirmations/components/confirm/dapp-swap-comparison-banner/dapp-swap-comparison-banner.tsx @@ -18,7 +18,7 @@ import { BatchTransaction, TransactionMeta, } from '@metamask/transaction-controller'; -import { TxData } from '@metamask/bridge-controller'; +import { QuoteResponse, TxData } from '@metamask/bridge-controller'; import { toHex } from '@metamask/controller-utils'; import { useDispatch, useSelector } from 'react-redux'; @@ -28,6 +28,8 @@ import { updateTransaction } from '../../../../../store/actions'; import { useConfirmContext } from '../../../context/confirm'; import { useDappSwapComparisonInfo } from '../../../hooks/transactions/dapp-swap-comparison/useDappSwapComparisonInfo'; import { useSwapCheck } from '../../../hooks/transactions/dapp-swap-comparison/useSwapCheck'; +import { QuoteSwapSimulationDetails } from '../quote-swap-simulation-details/quote-swap-simulation-details'; +import { parseTransactionData } from '../../../utils/dapp-swap-comparison-utils'; const DAPP_SWAP_COMPARISON_ORIGIN = 'https://app.uniswap.org'; const TEST_DAPP_ORIGIN = 'https://metamask.github.io'; @@ -79,12 +81,16 @@ const SwapButton = ({ const DappSwapComparisonInner = () => { const t = useI18nContext(); const { + fiatRates, + destinationTokenSymbol, + gasDifference, selectedQuote, selectedQuoteValueDifference, - gasDifference, + sourceTokenAmount, tokenAmountDifference, - destinationTokenSymbol, + tokenDetails, } = useDappSwapComparisonInfo(); + const { isQuotedSwap } = useSwapCheck(); const dispatch = useDispatch(); const { currentConfirmation } = useConfirmContext(); @@ -246,6 +252,14 @@ const DappSwapComparisonInner = () => { )} + {selectedSwapType === SwapType.Metamask && ( + + )} ); }; diff --git a/ui/pages/confirmations/components/confirm/info/batch/batch-simulation-details/batch-simulation-details.tsx b/ui/pages/confirmations/components/confirm/info/batch/batch-simulation-details/batch-simulation-details.tsx index 67341bec5e58..4c0c16b2bcfe 100644 --- a/ui/pages/confirmations/components/confirm/info/batch/batch-simulation-details/batch-simulation-details.tsx +++ b/ui/pages/confirmations/components/confirm/info/batch/batch-simulation-details/batch-simulation-details.tsx @@ -17,6 +17,7 @@ import { EditSpendingCapModal } from '../../approve/edit-spending-cap-modal/edit import { TokenStandard } from '../../../../../../../../shared/constants/transaction'; import { useI18nContext } from '../../../../../../../hooks/useI18nContext'; import { updateAtomicBatchData } from '../../../../../../../store/controller-actions/transaction-controller'; +import { useSwapCheck } from '../../../../../hooks/transactions/dapp-swap-comparison/useSwapCheck'; import { useIsUpgradeTransaction } from '../../hooks/useIsUpgradeTransaction'; // TODO: Fix in https://github.com/MetaMask/metamask-extension/issues/31860 @@ -24,6 +25,7 @@ import { useIsUpgradeTransaction } from '../../hooks/useIsUpgradeTransaction'; export function BatchSimulationDetails() { const t = useI18nContext(); const { isUpgradeOnly } = useIsUpgradeTransaction(); + const { isQuotedSwap } = useSwapCheck(); const { currentConfirmation: transactionMeta } = useConfirmContext(); @@ -58,13 +60,6 @@ export function BatchSimulationDetails() { [id, nestedTransactionIndexToEdit], ); - if ( - transactionMeta?.type === TransactionType.revokeDelegation || - isUpgradeOnly - ) { - return null; - } - const approveRows: StaticRow[] = useMemo(() => { const finalBalanceChanges = approveBalanceChanges?.map((change) => ({ ...change, @@ -82,6 +77,14 @@ export function BatchSimulationDetails() { ]; }, [approveBalanceChanges, handleEdit]); + if ( + transactionMeta?.type === TransactionType.revokeDelegation || + isUpgradeOnly || + isQuotedSwap + ) { + return null; + } + const nestedTransactionToEdit = nestedTransactionIndexToEdit === undefined ? undefined diff --git a/ui/pages/confirmations/components/confirm/quote-swap-simulation-details/quote-swap-simulation-details.tsx b/ui/pages/confirmations/components/confirm/quote-swap-simulation-details/quote-swap-simulation-details.tsx new file mode 100644 index 000000000000..fc6feeeb3256 --- /dev/null +++ b/ui/pages/confirmations/components/confirm/quote-swap-simulation-details/quote-swap-simulation-details.tsx @@ -0,0 +1,109 @@ +import React, { useMemo } from 'react'; +import BigNumber from 'bignumber.js'; +import { + Box, + BoxFlexDirection, + TextColor, +} from '@metamask/design-system-react'; +import { Hex } from '@metamask/utils'; +import { QuoteResponse } from '@metamask/bridge-controller'; +import { TransactionMeta } from '@metamask/transaction-controller'; +import { toHex } from '@metamask/controller-utils'; + +import { TokenStandAndDetails } from '../../../../../store/actions'; +import { useI18nContext } from '../../../../../hooks/useI18nContext'; +import { useConfirmContext } from '../../../context/confirm'; +import { SimulationDetailsLayout } from '../../simulation-details/simulation-details'; +import { BalanceChangeRow } from '../../simulation-details/balance-change-row'; + +export const QuoteSwapSimulationDetails = ({ + quote, + fiatRates, + sourceTokenAmount, + tokenDetails, +}: { + quote?: QuoteResponse; + fiatRates?: Record; + sourceTokenAmount?: string; + tokenDetails?: Record; +}) => { + const t = useI18nContext(); + const { currentConfirmation } = useConfirmContext(); + const { id: transactionId } = currentConfirmation; + + const { srcAssetBalanceChange, destAssetBalanceChange } = useMemo(() => { + if (!quote || !tokenDetails || !fiatRates) { + return {}; + } + const { srcAsset, srcTokenAmount, destAsset, destTokenAmount } = + quote.quote; + return { + srcAssetBalanceChange: { + asset: { + ...tokenDetails[srcAsset.address as Hex], + chainId: toHex(srcAsset.chainId), + address: srcAsset.address as Hex, + } as any, + amount: new BigNumber(sourceTokenAmount ?? '0x0', 16).dividedBy( + new BigNumber(10).pow(srcAsset.decimals), + ), + fiatAmount: new BigNumber(srcTokenAmount) + .dividedBy(new BigNumber(10).pow(srcAsset.decimals)) + .times(fiatRates[srcAsset.address as Hex] ?? 0) + .toNumber(), + usdAmount: 0, + color: TextColor.ErrorAlternative, + }, + destAssetBalanceChange: { + asset: { + ...tokenDetails[destAsset.address as Hex], + chainId: toHex(destAsset.chainId), + address: destAsset.address as Hex, + } as any, + amount: new BigNumber(destTokenAmount).dividedBy( + new BigNumber(10).pow(destAsset.decimals), + ), + fiatAmount: new BigNumber(destTokenAmount) + .dividedBy(new BigNumber(10).pow(destAsset.decimals)) + .times(fiatRates[destAsset.address as Hex] ?? 0) + .toNumber(), + usdAmount: 0, + color: TextColor.SuccessDefault, + }, + }; + }, [fiatRates, quote, tokenDetails]); + + if ( + !quote || + !tokenDetails || + !fiatRates || + !srcAssetBalanceChange || + !destAssetBalanceChange + ) { + return null; + } + + return ( + + + + + + + ); +}; diff --git a/ui/pages/confirmations/components/simulation-details/simulation-details.tsx b/ui/pages/confirmations/components/simulation-details/simulation-details.tsx index 56f0d772e2df..1b93a92b2ec0 100644 --- a/ui/pages/confirmations/components/simulation-details/simulation-details.tsx +++ b/ui/pages/confirmations/components/simulation-details/simulation-details.tsx @@ -266,7 +266,7 @@ const HeaderLayout: React.FC<{ * @param props.children * @param props.transactionId */ -const SimulationDetailsLayout: React.FC<{ +export const SimulationDetailsLayout: React.FC<{ inHeader?: React.ReactNode; isTransactionsRedesign: boolean; transactionId: string; diff --git a/ui/pages/confirmations/hooks/transactions/dapp-swap-comparison/useDappSwapComparisonInfo.ts b/ui/pages/confirmations/hooks/transactions/dapp-swap-comparison/useDappSwapComparisonInfo.ts index 7cd5f767ce8c..d908e9a719e5 100644 --- a/ui/pages/confirmations/hooks/transactions/dapp-swap-comparison/useDappSwapComparisonInfo.ts +++ b/ui/pages/confirmations/hooks/transactions/dapp-swap-comparison/useDappSwapComparisonInfo.ts @@ -86,6 +86,7 @@ export function useDappSwapComparisonInfo() { }, [chainId, data, nestedTransactions, updateRequestDetectionLatency]); const { + fiatRates, getGasUSDValue, getTokenUSDValue, getDestinationTokenUSDValue, @@ -369,10 +370,13 @@ export function useDappSwapComparisonInfo() { ]); return { + fiatRates, + destinationTokenSymbol, + gasDifference, selectedQuote, selectedQuoteValueDifference, - gasDifference, + sourceTokenAmount: quotesInput?.srcTokenAmount, tokenAmountDifference, - destinationTokenSymbol, + tokenDetails, }; } diff --git a/ui/pages/confirmations/hooks/transactions/dapp-swap-comparison/useDappSwapUSDValues.ts b/ui/pages/confirmations/hooks/transactions/dapp-swap-comparison/useDappSwapUSDValues.ts index 28b3760b2845..1c167d7a9147 100644 --- a/ui/pages/confirmations/hooks/transactions/dapp-swap-comparison/useDappSwapUSDValues.ts +++ b/ui/pages/confirmations/hooks/transactions/dapp-swap-comparison/useDappSwapUSDValues.ts @@ -106,6 +106,7 @@ export function useDappSwapUSDValues({ getTokenUSDValue, getDestinationTokenUSDValue, tokenDetails, + fiatRates, tokenInfoPending: fiatRatesPending || tokenDetailsPending, }; } From dddc1c5a9525bec358759f322ee988928f3cb5ad Mon Sep 17 00:00:00 2001 From: Jyoti Puri Date: Thu, 6 Nov 2025 18:33:22 +0530 Subject: [PATCH 10/18] adding test coverage --- .../dapp-swap-comparison-banner.tsx | 1 - .../batch-simulation-details.test.tsx | 9 ++ .../quote-swap-simulation-details.test.tsx | 93 +++++++++++++++++++ .../quote-swap-simulation-details.tsx | 33 +++---- .../useDappSwapComparisonInfo.test.ts | 23 ++++- .../useDappSwapUSDValues.test.ts | 5 + 6 files changed, 145 insertions(+), 19 deletions(-) create mode 100644 ui/pages/confirmations/components/confirm/quote-swap-simulation-details/quote-swap-simulation-details.test.tsx diff --git a/ui/pages/confirmations/components/confirm/dapp-swap-comparison-banner/dapp-swap-comparison-banner.tsx b/ui/pages/confirmations/components/confirm/dapp-swap-comparison-banner/dapp-swap-comparison-banner.tsx index 3f1c6f609463..bc63db607f87 100644 --- a/ui/pages/confirmations/components/confirm/dapp-swap-comparison-banner/dapp-swap-comparison-banner.tsx +++ b/ui/pages/confirmations/components/confirm/dapp-swap-comparison-banner/dapp-swap-comparison-banner.tsx @@ -29,7 +29,6 @@ import { useConfirmContext } from '../../../context/confirm'; import { useDappSwapComparisonInfo } from '../../../hooks/transactions/dapp-swap-comparison/useDappSwapComparisonInfo'; import { useSwapCheck } from '../../../hooks/transactions/dapp-swap-comparison/useSwapCheck'; import { QuoteSwapSimulationDetails } from '../quote-swap-simulation-details/quote-swap-simulation-details'; -import { parseTransactionData } from '../../../utils/dapp-swap-comparison-utils'; const DAPP_SWAP_COMPARISON_ORIGIN = 'https://app.uniswap.org'; const TEST_DAPP_ORIGIN = 'https://metamask.github.io'; diff --git a/ui/pages/confirmations/components/confirm/info/batch/batch-simulation-details/batch-simulation-details.test.tsx b/ui/pages/confirmations/components/confirm/info/batch/batch-simulation-details/batch-simulation-details.test.tsx index 8eb5bbb82cba..907f37481795 100644 --- a/ui/pages/confirmations/components/confirm/info/batch/batch-simulation-details/batch-simulation-details.test.tsx +++ b/ui/pages/confirmations/components/confirm/info/batch/batch-simulation-details/batch-simulation-details.test.tsx @@ -21,6 +21,7 @@ import { import { updateAtomicBatchData } from '../../../../../../../store/controller-actions/transaction-controller'; import { Confirmation } from '../../../../../types/confirm'; import { updateApprovalAmount } from '../../../../../../../../shared/lib/transactions/approvals'; +import * as SwapCheckHook from '../../../../../hooks/transactions/dapp-swap-comparison/useSwapCheck'; import { BatchSimulationDetails } from './batch-simulation-details'; jest.mock('../../../../../../../../shared/lib/transactions/approvals'); @@ -291,6 +292,14 @@ describe('BatchSimulationDetails', () => { expect(container.firstChild).toBeNull(); }); + it('return null for MetaMask Swap transaction', () => { + jest.spyOn(SwapCheckHook, 'useSwapCheck').mockReturnValue({ + isQuotedSwap: true, + }); + const { container } = render(); + expect(container.firstChild).toBeNull(); + }); + it('return null for upgrade transaction if there are no nested transactions', () => { const { container } = render(upgradeAccountConfirmationOnly); expect(container.firstChild).toBeNull(); diff --git a/ui/pages/confirmations/components/confirm/quote-swap-simulation-details/quote-swap-simulation-details.test.tsx b/ui/pages/confirmations/components/confirm/quote-swap-simulation-details/quote-swap-simulation-details.test.tsx new file mode 100644 index 000000000000..04fcce0fce28 --- /dev/null +++ b/ui/pages/confirmations/components/confirm/quote-swap-simulation-details/quote-swap-simulation-details.test.tsx @@ -0,0 +1,93 @@ +import React from 'react'; +import configureMockStore from 'redux-mock-store'; +import { QuoteResponse } from '@metamask/bridge-controller'; + +import { getMockConfirmStateForTransaction } from '../../../../../../test/data/confirmations/helper'; +import { mockSwapConfirmation } from '../../../../../../test/data/confirmations/contract-interaction'; +import { renderWithConfirmContextProvider } from '../../../../../../test/lib/confirmations/render-helpers'; +import { Confirmation } from '../../../types/confirm'; +import { QuoteSwapSimulationDetails } from './quote-swap-simulation-details'; + +jest.mock( + '../../../../../components/app/alert-system/contexts/alertMetricsContext', + () => ({ + useAlertMetrics: jest.fn(() => ({ + trackAlertActionClicked: jest.fn(), + trackInlineAlertClicked: jest.fn(), + trackAlertRender: jest.fn(), + })), + }), +); + +const quote = { + quote: { + srcAsset: { + address: '0x833589fCD6eDb6E08f4c7C32D4f71b54bdA02913', + chainId: 8453, + assetId: 'eip155:8453/erc20:0x833589fcd6edb6e08f4c7c32d4f71b54bda02913', + symbol: 'USDC', + decimals: 6, + iconUrl: + 'https://static.cx.metamask.io/api/v2/tokenIcons/assets/eip155/8453/erc20/0x833589fcd6edb6e08f4c7c32d4f71b54bda02913.png', + metadata: { storage: { balance: 9, approval: 10 } }, + }, + srcTokenAmount: '99125', + destAsset: { + address: '0xfde4C96c8593536E31F229EA8f37b2ADa2699bb2', + chainId: 8453, + assetId: 'eip155:8453/erc20:0xfde4c96c8593536e31f229ea8f37b2ada2699bb2', + symbol: 'USDT', + decimals: 6, + iconUrl: + 'https://static.cx.metamask.io/api/v2/tokenIcons/assets/eip155/8453/erc20/0xfde4c96c8593536e31f229ea8f37b2ada2699bb2.png', + metadata: { storage: { balance: 0, approval: 1 } }, + }, + destTokenAmount: '99132', + minDestTokenAmount: '97149', + }, +}; + +function render(args: Record = {}) { + const state = getMockConfirmStateForTransaction({ + ...mockSwapConfirmation, + ...args, + } as Confirmation); + + const mockStore = configureMockStore()(state); + + return renderWithConfirmContextProvider( + , + mockStore, + ); +} + +describe('', () => { + it('renders component without errors', () => { + const { getByText } = render(); + expect(getByText('Estimated changes')).toBeInTheDocument(); + expect(getByText('You send')).toBeInTheDocument(); + expect(getByText('You receive')).toBeInTheDocument(); + expect(getByText('+ 0.1')).toBeInTheDocument(); + expect(getByText('+ 0.0991')).toBeInTheDocument(); + }); +}); diff --git a/ui/pages/confirmations/components/confirm/quote-swap-simulation-details/quote-swap-simulation-details.tsx b/ui/pages/confirmations/components/confirm/quote-swap-simulation-details/quote-swap-simulation-details.tsx index fc6feeeb3256..816c8e6dcda8 100644 --- a/ui/pages/confirmations/components/confirm/quote-swap-simulation-details/quote-swap-simulation-details.tsx +++ b/ui/pages/confirmations/components/confirm/quote-swap-simulation-details/quote-swap-simulation-details.tsx @@ -1,10 +1,6 @@ import React, { useMemo } from 'react'; import BigNumber from 'bignumber.js'; -import { - Box, - BoxFlexDirection, - TextColor, -} from '@metamask/design-system-react'; +import { Box, BoxFlexDirection } from '@metamask/design-system-react'; import { Hex } from '@metamask/utils'; import { QuoteResponse } from '@metamask/bridge-controller'; import { TransactionMeta } from '@metamask/transaction-controller'; @@ -15,15 +11,16 @@ import { useI18nContext } from '../../../../../hooks/useI18nContext'; import { useConfirmContext } from '../../../context/confirm'; import { SimulationDetailsLayout } from '../../simulation-details/simulation-details'; import { BalanceChangeRow } from '../../simulation-details/balance-change-row'; +import { TextColor } from '../../../../../helpers/constants/design-system'; export const QuoteSwapSimulationDetails = ({ - quote, fiatRates, + quote, sourceTokenAmount, tokenDetails, }: { - quote?: QuoteResponse; fiatRates?: Record; + quote?: QuoteResponse; sourceTokenAmount?: string; tokenDetails?: Record; }) => { @@ -52,7 +49,6 @@ export const QuoteSwapSimulationDetails = ({ .times(fiatRates[srcAsset.address as Hex] ?? 0) .toNumber(), usdAmount: 0, - color: TextColor.ErrorAlternative, }, destAssetBalanceChange: { asset: { @@ -68,7 +64,6 @@ export const QuoteSwapSimulationDetails = ({ .times(fiatRates[destAsset.address as Hex] ?? 0) .toNumber(), usdAmount: 0, - color: TextColor.SuccessDefault, }, }; }, [fiatRates, quote, tokenDetails]); @@ -88,19 +83,25 @@ export const QuoteSwapSimulationDetails = ({ isTransactionsRedesign transactionId={transactionId} > - + diff --git a/ui/pages/confirmations/hooks/transactions/dapp-swap-comparison/useDappSwapComparisonInfo.test.ts b/ui/pages/confirmations/hooks/transactions/dapp-swap-comparison/useDappSwapComparisonInfo.test.ts index 818f74d49d0c..7323600c982c 100644 --- a/ui/pages/confirmations/hooks/transactions/dapp-swap-comparison/useDappSwapComparisonInfo.test.ts +++ b/ui/pages/confirmations/hooks/transactions/dapp-swap-comparison/useDappSwapComparisonInfo.test.ts @@ -316,16 +316,35 @@ describe('useDappSwapComparisonInfo', () => { }); const { + fiatRates, + destinationTokenSymbol, + gasDifference, selectedQuote, selectedQuoteValueDifference, - gasDifference, + sourceTokenAmount, tokenAmountDifference, - destinationTokenSymbol, + tokenDetails, } = await runHook(); expect(selectedQuote).toEqual(quotes[3]); expect(selectedQuoteValueDifference).toBe(0.012494042894187605); expect(gasDifference).toBe(0.005686377458187605); expect(tokenAmountDifference).toBe(0.006807665436); expect(destinationTokenSymbol).toBe('USDC'); + expect(sourceTokenAmount).toBe('0xde0b6b3a7640000'); + expect(tokenDetails).toEqual({ + '0x833589fcd6edb6e08f4c7c32d4f71b54bda02913': { + symbol: 'USDC', + decimals: '6', + }, + '0xfd086bc7cd5c481dcc9c85ebe478a1c0b69fcbb9': { + decimals: '6', + symbol: 'USDT', + }, + }); + expect(fiatRates).toEqual({ + '0x0000000000000000000000000000000000000000': 4052.27, + '0x833589fcd6edb6e08f4c7c32d4f71b54bda02913': 0.999804, + '0xfdcc3dd6671eab0709a4c0f3f53de9a333d80798': 1, + }); }); }); diff --git a/ui/pages/confirmations/hooks/transactions/dapp-swap-comparison/useDappSwapUSDValues.test.ts b/ui/pages/confirmations/hooks/transactions/dapp-swap-comparison/useDappSwapUSDValues.test.ts index e9885bce8580..5872d9900d5f 100644 --- a/ui/pages/confirmations/hooks/transactions/dapp-swap-comparison/useDappSwapUSDValues.test.ts +++ b/ui/pages/confirmations/hooks/transactions/dapp-swap-comparison/useDappSwapUSDValues.test.ts @@ -92,5 +92,10 @@ describe('useDappSwapUSDValues', () => { expect(result.getGasUSDValue(new BigNumber('1'))).toBe( '0.00000003879076790654', ); + expect(result.fiatRates).toEqual({ + '0x0000000000000000000000000000000000000000': 4052.27, + '0x833589fcd6edb6e08f4c7c32d4f71b54bda02913': 0.999804, + '0xfdcc3dd6671eab0709a4c0f3f53de9a333d80798': 1, + }); }); }); From ac73f16c345f8948540db46dbab17311bb35c137 Mon Sep 17 00:00:00 2001 From: Jyoti Puri Date: Thu, 6 Nov 2025 18:46:53 +0530 Subject: [PATCH 11/18] update --- .../quote-swap-simulation-details.tsx | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/ui/pages/confirmations/components/confirm/quote-swap-simulation-details/quote-swap-simulation-details.tsx b/ui/pages/confirmations/components/confirm/quote-swap-simulation-details/quote-swap-simulation-details.tsx index 816c8e6dcda8..44496ae5ceee 100644 --- a/ui/pages/confirmations/components/confirm/quote-swap-simulation-details/quote-swap-simulation-details.tsx +++ b/ui/pages/confirmations/components/confirm/quote-swap-simulation-details/quote-swap-simulation-details.tsx @@ -1,17 +1,18 @@ import React, { useMemo } from 'react'; -import BigNumber from 'bignumber.js'; +import { BigNumber } from 'bignumber.js'; import { Box, BoxFlexDirection } from '@metamask/design-system-react'; import { Hex } from '@metamask/utils'; import { QuoteResponse } from '@metamask/bridge-controller'; import { TransactionMeta } from '@metamask/transaction-controller'; import { toHex } from '@metamask/controller-utils'; +import { TextColor } from '../../../../../helpers/constants/design-system'; import { TokenStandAndDetails } from '../../../../../store/actions'; import { useI18nContext } from '../../../../../hooks/useI18nContext'; import { useConfirmContext } from '../../../context/confirm'; import { SimulationDetailsLayout } from '../../simulation-details/simulation-details'; import { BalanceChangeRow } from '../../simulation-details/balance-change-row'; -import { TextColor } from '../../../../../helpers/constants/design-system'; +import { TokenAssetIdentifier } from '../../simulation-details/types'; export const QuoteSwapSimulationDetails = ({ fiatRates, @@ -40,7 +41,7 @@ export const QuoteSwapSimulationDetails = ({ ...tokenDetails[srcAsset.address as Hex], chainId: toHex(srcAsset.chainId), address: srcAsset.address as Hex, - } as any, + } as unknown as TokenAssetIdentifier, amount: new BigNumber(sourceTokenAmount ?? '0x0', 16).dividedBy( new BigNumber(10).pow(srcAsset.decimals), ), @@ -55,7 +56,7 @@ export const QuoteSwapSimulationDetails = ({ ...tokenDetails[destAsset.address as Hex], chainId: toHex(destAsset.chainId), address: destAsset.address as Hex, - } as any, + } as unknown as TokenAssetIdentifier, amount: new BigNumber(destTokenAmount).dividedBy( new BigNumber(10).pow(destAsset.decimals), ), From 056ea612b83ce0b60e9d153bd119e72e33c197fa Mon Sep 17 00:00:00 2001 From: Jyoti Puri Date: Thu, 6 Nov 2025 18:53:45 +0530 Subject: [PATCH 12/18] update --- .../quote-swap-simulation-details.tsx | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/ui/pages/confirmations/components/confirm/quote-swap-simulation-details/quote-swap-simulation-details.tsx b/ui/pages/confirmations/components/confirm/quote-swap-simulation-details/quote-swap-simulation-details.tsx index 44496ae5ceee..50179a8210ee 100644 --- a/ui/pages/confirmations/components/confirm/quote-swap-simulation-details/quote-swap-simulation-details.tsx +++ b/ui/pages/confirmations/components/confirm/quote-swap-simulation-details/quote-swap-simulation-details.tsx @@ -33,8 +33,7 @@ export const QuoteSwapSimulationDetails = ({ if (!quote || !tokenDetails || !fiatRates) { return {}; } - const { srcAsset, srcTokenAmount, destAsset, destTokenAmount } = - quote.quote; + const { srcAsset, destAsset, destTokenAmount } = quote.quote; return { srcAssetBalanceChange: { asset: { @@ -45,7 +44,7 @@ export const QuoteSwapSimulationDetails = ({ amount: new BigNumber(sourceTokenAmount ?? '0x0', 16).dividedBy( new BigNumber(10).pow(srcAsset.decimals), ), - fiatAmount: new BigNumber(srcTokenAmount) + fiatAmount: new BigNumber(sourceTokenAmount ?? '0x0', 16) .dividedBy(new BigNumber(10).pow(srcAsset.decimals)) .times(fiatRates[srcAsset.address as Hex] ?? 0) .toNumber(), @@ -67,7 +66,7 @@ export const QuoteSwapSimulationDetails = ({ usdAmount: 0, }, }; - }, [fiatRates, quote, tokenDetails]); + }, [fiatRates, quote, sourceTokenAmount, tokenDetails]); if ( !quote || From cacbbec7707881563d55d345fc50c85b30bfdf70 Mon Sep 17 00:00:00 2001 From: Jyoti Puri Date: Thu, 6 Nov 2025 19:40:50 +0530 Subject: [PATCH 13/18] update --- .../quote-swap-simulation-details.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ui/pages/confirmations/components/confirm/quote-swap-simulation-details/quote-swap-simulation-details.tsx b/ui/pages/confirmations/components/confirm/quote-swap-simulation-details/quote-swap-simulation-details.tsx index 50179a8210ee..657047be4b9a 100644 --- a/ui/pages/confirmations/components/confirm/quote-swap-simulation-details/quote-swap-simulation-details.tsx +++ b/ui/pages/confirmations/components/confirm/quote-swap-simulation-details/quote-swap-simulation-details.tsx @@ -91,7 +91,6 @@ export const QuoteSwapSimulationDetails = ({ Date: Fri, 7 Nov 2025 12:17:22 +0530 Subject: [PATCH 14/18] update --- .../quote-swap-simulation-details.tsx | 8 +++----- 1 file changed, 3 insertions(+), 5 deletions(-) diff --git a/ui/pages/confirmations/components/confirm/quote-swap-simulation-details/quote-swap-simulation-details.tsx b/ui/pages/confirmations/components/confirm/quote-swap-simulation-details/quote-swap-simulation-details.tsx index 657047be4b9a..7ef02d1d1c71 100644 --- a/ui/pages/confirmations/components/confirm/quote-swap-simulation-details/quote-swap-simulation-details.tsx +++ b/ui/pages/confirmations/components/confirm/quote-swap-simulation-details/quote-swap-simulation-details.tsx @@ -41,9 +41,9 @@ export const QuoteSwapSimulationDetails = ({ chainId: toHex(srcAsset.chainId), address: srcAsset.address as Hex, } as unknown as TokenAssetIdentifier, - amount: new BigNumber(sourceTokenAmount ?? '0x0', 16).dividedBy( - new BigNumber(10).pow(srcAsset.decimals), - ), + amount: new BigNumber(sourceTokenAmount ?? '0x0', 16) + .negated() + .dividedBy(new BigNumber(10).pow(srcAsset.decimals)), fiatAmount: new BigNumber(sourceTokenAmount ?? '0x0', 16) .dividedBy(new BigNumber(10).pow(srcAsset.decimals)) .times(fiatRates[srcAsset.address as Hex] ?? 0) @@ -93,7 +93,6 @@ export const QuoteSwapSimulationDetails = ({ confirmationId={currentConfirmation?.id} isFirstRow label={t('simulationDetailsOutgoingHeading')} - labelColor={TextColor.errorAlternative} showFiat /> From f50ae83c51c43b29dbceb808aff750e12fc26dee Mon Sep 17 00:00:00 2001 From: Jyoti Puri Date: Fri, 7 Nov 2025 13:58:50 +0530 Subject: [PATCH 15/18] update --- .../quote-swap-simulation-details.tsx | 1 - 1 file changed, 1 deletion(-) diff --git a/ui/pages/confirmations/components/confirm/quote-swap-simulation-details/quote-swap-simulation-details.tsx b/ui/pages/confirmations/components/confirm/quote-swap-simulation-details/quote-swap-simulation-details.tsx index 7ef02d1d1c71..62c66cc0cae7 100644 --- a/ui/pages/confirmations/components/confirm/quote-swap-simulation-details/quote-swap-simulation-details.tsx +++ b/ui/pages/confirmations/components/confirm/quote-swap-simulation-details/quote-swap-simulation-details.tsx @@ -6,7 +6,6 @@ import { QuoteResponse } from '@metamask/bridge-controller'; import { TransactionMeta } from '@metamask/transaction-controller'; import { toHex } from '@metamask/controller-utils'; -import { TextColor } from '../../../../../helpers/constants/design-system'; import { TokenStandAndDetails } from '../../../../../store/actions'; import { useI18nContext } from '../../../../../hooks/useI18nContext'; import { useConfirmContext } from '../../../context/confirm'; From a4ae3c5bbe6d576557075fc1989fc5a24580bdc5 Mon Sep 17 00:00:00 2001 From: Jyoti Puri Date: Fri, 7 Nov 2025 18:25:51 +0530 Subject: [PATCH 16/18] update --- .../quote-swap-simulation-details.tsx | 1 + 1 file changed, 1 insertion(+) diff --git a/ui/pages/confirmations/components/confirm/quote-swap-simulation-details/quote-swap-simulation-details.tsx b/ui/pages/confirmations/components/confirm/quote-swap-simulation-details/quote-swap-simulation-details.tsx index 62c66cc0cae7..12ed428e6005 100644 --- a/ui/pages/confirmations/components/confirm/quote-swap-simulation-details/quote-swap-simulation-details.tsx +++ b/ui/pages/confirmations/components/confirm/quote-swap-simulation-details/quote-swap-simulation-details.tsx @@ -44,6 +44,7 @@ export const QuoteSwapSimulationDetails = ({ .negated() .dividedBy(new BigNumber(10).pow(srcAsset.decimals)), fiatAmount: new BigNumber(sourceTokenAmount ?? '0x0', 16) + .negated() .dividedBy(new BigNumber(10).pow(srcAsset.decimals)) .times(fiatRates[srcAsset.address as Hex] ?? 0) .toNumber(), From d147390577bb4c83adc5aec5de8568ca72e7a143 Mon Sep 17 00:00:00 2001 From: Jyoti Puri Date: Fri, 7 Nov 2025 18:27:50 +0530 Subject: [PATCH 17/18] update --- .../quote-swap-simulation-details.tsx | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/ui/pages/confirmations/components/confirm/quote-swap-simulation-details/quote-swap-simulation-details.tsx b/ui/pages/confirmations/components/confirm/quote-swap-simulation-details/quote-swap-simulation-details.tsx index 12ed428e6005..41958b996be4 100644 --- a/ui/pages/confirmations/components/confirm/quote-swap-simulation-details/quote-swap-simulation-details.tsx +++ b/ui/pages/confirmations/components/confirm/quote-swap-simulation-details/quote-swap-simulation-details.tsx @@ -46,7 +46,7 @@ export const QuoteSwapSimulationDetails = ({ fiatAmount: new BigNumber(sourceTokenAmount ?? '0x0', 16) .negated() .dividedBy(new BigNumber(10).pow(srcAsset.decimals)) - .times(fiatRates[srcAsset.address as Hex] ?? 0) + .times(fiatRates[srcAsset.address.toLowerCase() as Hex] ?? 0) .toNumber(), usdAmount: 0, }, @@ -61,7 +61,7 @@ export const QuoteSwapSimulationDetails = ({ ), fiatAmount: new BigNumber(destTokenAmount) .dividedBy(new BigNumber(10).pow(destAsset.decimals)) - .times(fiatRates[destAsset.address as Hex] ?? 0) + .times(fiatRates[destAsset.address.toLowerCase() as Hex] ?? 0) .toNumber(), usdAmount: 0, }, From eaae3e6082a1fa20f46ed118017618b38a5ce4e9 Mon Sep 17 00:00:00 2001 From: Jyoti Puri Date: Fri, 7 Nov 2025 18:48:02 +0530 Subject: [PATCH 18/18] update --- .../quote-swap-simulation-details.test.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ui/pages/confirmations/components/confirm/quote-swap-simulation-details/quote-swap-simulation-details.test.tsx b/ui/pages/confirmations/components/confirm/quote-swap-simulation-details/quote-swap-simulation-details.test.tsx index 04fcce0fce28..148652c38323 100644 --- a/ui/pages/confirmations/components/confirm/quote-swap-simulation-details/quote-swap-simulation-details.test.tsx +++ b/ui/pages/confirmations/components/confirm/quote-swap-simulation-details/quote-swap-simulation-details.test.tsx @@ -87,7 +87,7 @@ describe('', () => { expect(getByText('Estimated changes')).toBeInTheDocument(); expect(getByText('You send')).toBeInTheDocument(); expect(getByText('You receive')).toBeInTheDocument(); - expect(getByText('+ 0.1')).toBeInTheDocument(); + expect(getByText('- 0.1')).toBeInTheDocument(); expect(getByText('+ 0.0991')).toBeInTheDocument(); }); });