-
Notifications
You must be signed in to change notification settings - Fork 5.4k
feat: for quoted swap use data from quote to populate estimated changes #37574
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
Changes from all commits
0355e59
89d8619
1a991a3
9528a13
75538f8
3bb477c
cc37af1
a338213
033a8d0
c8fedc9
b7ae186
933ebfb
ec93043
dddc1c5
ac73f16
056ea61
cacbbec
a049e99
6a06b53
12c0d94
f50ae83
25b8695
aded29b
5ef409a
a4ae3c5
548d49a
d147390
eaae3e6
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -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<string, string> = {}) { | ||
| const state = getMockConfirmStateForTransaction({ | ||
| ...mockSwapConfirmation, | ||
| ...args, | ||
| } as Confirmation); | ||
|
|
||
| const mockStore = configureMockStore()(state); | ||
|
|
||
| return renderWithConfirmContextProvider( | ||
| <QuoteSwapSimulationDetails | ||
| fiatRates={{ | ||
| '0x0000000000000000000000000000000000000000': 3377.19, | ||
| '0x833589fCD6eDb6E08f4c7C32D4f71b54bdA02913': 0.999877, | ||
| '0xfde4C96c8593536E31F229EA8f37b2ADa2699bb2': 0.999578, | ||
| }} | ||
| quote={quote as unknown as QuoteResponse} | ||
| sourceTokenAmount={'0x186a0'} | ||
| tokenDetails={{ | ||
| '0xfde4c96c8593536e31f229ea8f37b2ada2699bb2': { | ||
| decimals: '6', | ||
| symbol: 'USDT', | ||
| standard: 'ERC20', | ||
| }, | ||
| '0x833589fcd6edb6e08f4c7c32d4f71b54bda02913': { | ||
| decimals: '6', | ||
| symbol: 'USDC', | ||
| standard: 'ERC20', | ||
| }, | ||
| }} | ||
| />, | ||
| mockStore, | ||
| ); | ||
| } | ||
|
|
||
| describe('<QuoteSwapSimulationDetails />', () => { | ||
| 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(); | ||
| }); | ||
| }); |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,108 @@ | ||
| import React, { useMemo } from 'react'; | ||
| 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 { 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 { TokenAssetIdentifier } from '../../simulation-details/types'; | ||
|
|
||
| export const QuoteSwapSimulationDetails = ({ | ||
| fiatRates, | ||
| quote, | ||
| sourceTokenAmount, | ||
| tokenDetails, | ||
| }: { | ||
| fiatRates?: Record<Hex, number | undefined>; | ||
| quote?: QuoteResponse; | ||
| sourceTokenAmount?: string; | ||
| tokenDetails?: Record<Hex, TokenStandAndDetails>; | ||
| }) => { | ||
| const t = useI18nContext(); | ||
| const { currentConfirmation } = useConfirmContext<TransactionMeta>(); | ||
| const { id: transactionId } = currentConfirmation; | ||
|
|
||
| const { srcAssetBalanceChange, destAssetBalanceChange } = useMemo(() => { | ||
| if (!quote || !tokenDetails || !fiatRates) { | ||
| return {}; | ||
| } | ||
| const { srcAsset, destAsset, destTokenAmount } = quote.quote; | ||
| return { | ||
| srcAssetBalanceChange: { | ||
| asset: { | ||
| ...tokenDetails[srcAsset.address as Hex], | ||
| chainId: toHex(srcAsset.chainId), | ||
| address: srcAsset.address as Hex, | ||
| } as unknown as TokenAssetIdentifier, | ||
| amount: new BigNumber(sourceTokenAmount ?? '0x0', 16) | ||
| .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.toLowerCase() as Hex] ?? 0) | ||
| .toNumber(), | ||
jpuri marked this conversation as resolved.
Show resolved
Hide resolved
|
||
| usdAmount: 0, | ||
| }, | ||
| destAssetBalanceChange: { | ||
| asset: { | ||
| ...tokenDetails[destAsset.address as Hex], | ||
| chainId: toHex(destAsset.chainId), | ||
| address: destAsset.address as Hex, | ||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Bug: Case-Sensitive Token Detail Lookup MismatchThe |
||
| } as unknown as TokenAssetIdentifier, | ||
| 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.toLowerCase() as Hex] ?? 0) | ||
| .toNumber(), | ||
jpuri marked this conversation as resolved.
Show resolved
Hide resolved
|
||
| usdAmount: 0, | ||
| }, | ||
| }; | ||
| }, [fiatRates, quote, sourceTokenAmount, tokenDetails]); | ||
|
|
||
| if ( | ||
| !quote || | ||
| !tokenDetails || | ||
| !fiatRates || | ||
| !srcAssetBalanceChange || | ||
| !destAssetBalanceChange | ||
| ) { | ||
| return null; | ||
| } | ||
|
|
||
| return ( | ||
| <SimulationDetailsLayout | ||
| isTransactionsRedesign | ||
| transactionId={transactionId} | ||
| > | ||
| <Box | ||
| flexDirection={BoxFlexDirection.Column} | ||
| gap={3} | ||
| data-testid="quote-swap-simulation-details" | ||
| > | ||
| <BalanceChangeRow | ||
| balanceChange={srcAssetBalanceChange} | ||
| confirmationId={currentConfirmation?.id} | ||
| isFirstRow | ||
| label={t('simulationDetailsOutgoingHeading')} | ||
| showFiat | ||
| /> | ||
jpuri marked this conversation as resolved.
Show resolved
Hide resolved
|
||
| <BalanceChangeRow | ||
| balanceChange={destAssetBalanceChange} | ||
| hasIncomingTokens | ||
| confirmationId={currentConfirmation?.id} | ||
| label={t('simulationDetailsIncomingHeading')} | ||
| showFiat | ||
| /> | ||
| </Box> | ||
| </SimulationDetailsLayout> | ||
| ); | ||
| }; | ||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Bug: Case-Sensitive Token Detail Lookup Mismatch Failure
The
tokenDetailslookup usessrcAsset.addressdirectly without lowercasing, butfetchAllTokenDetailsreturns keys normalized to lowercase. This causes the spread operation to fail silently when the address has mixed case, resulting in missing token details. The same code correctly lowercases addresses when accessingfiatRateson line 49.