From 54ae12f9d4842b0f0df04684e266cb4697dfda76 Mon Sep 17 00:00:00 2001 From: Matthew Walsh Date: Tue, 29 Oct 2024 14:09:39 +0000 Subject: [PATCH] refactor: remove global network usage from transaction simulation (#27895) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit ## **Description** Remove any usage of the global network from transaction simulation. This requires using the `chainId` from the `TransactionMeta`, and so the `SimulationDetails` properties have been simplified to accept `TransactionMeta` directly to minimise the number of tightly coupled properties. [![Open in GitHub Codespaces](https://github.com/codespaces/badge.svg)](https://codespaces.new/MetaMask/metamask-extension/pull/27895?quickstart=1) ## **Related issues** Fixes: [#3376](https://github.com/MetaMask/MetaMask-planning/issues/3376) ## **Manual testing steps** Regression of all simulation usages, with specific attention to fiat value. - Legacy Confirmations - Redesigned Confirmations - Smart Transaction Status ## **Screenshots/Recordings** ### **Before** ### **After** ## **Pre-merge author checklist** - [x] I've followed [MetaMask Contributor Docs](https://github.com/MetaMask/contributor-docs) and [MetaMask Extension Coding Standards](https://github.com/MetaMask/metamask-extension/blob/develop/.github/guidelines/CODING_GUIDELINES.md). - [x] I've completed the PR template to the best of my ability - [x] I’ve included tests if applicable - [x] I’ve documented my code using [JSDoc](https://jsdoc.app/) format if applicable - [x] I’ve applied the right labels on the PR (see [labeling guidelines](https://github.com/MetaMask/metamask-extension/blob/develop/.github/guidelines/LABELING_GUIDELINES.md)). Not required for external contributors. ## **Pre-merge reviewer checklist** - [ ] I've manually tested the PR (e.g. pull and build branch, run the app, test code being changed). - [ ] I confirm that this PR addresses all acceptance criteria described in the ticket it closes and includes the necessary testing evidence such as recordings and or screenshots. --- .storybook/main.js | 3 + .../base-transaction-info.tsx | 3 +- .../nft-token-transfer/nft-token-transfer.tsx | 3 +- .../info/token-transfer/token-transfer.tsx | 3 +- .../simulation-details/amount-pill.test.tsx | 15 +- .../simulation-details/asset-pill.test.tsx | 15 +- .../simulation-details/asset-pill.tsx | 34 +- .../simulation-details.stories.tsx | 338 ++++++++++-------- .../simulation-details.test.tsx | 6 +- .../simulation-details/simulation-details.tsx | 14 +- .../components/simulation-details/types.ts | 6 +- .../useBalanceChanges.test.ts | 26 +- .../simulation-details/useBalanceChanges.ts | 30 +- .../useSimulationMetrics.ts | 7 +- .../confirm-transaction-base.component.js | 6 +- .../smart-transaction-status-page.tsx | 5 +- 16 files changed, 290 insertions(+), 224 deletions(-) diff --git a/.storybook/main.js b/.storybook/main.js index de856adf8857..2b4384250c9c 100644 --- a/.storybook/main.js +++ b/.storybook/main.js @@ -52,6 +52,9 @@ module.exports = { config.resolve.alias['../../../../../../store/actions'] = require.resolve( '../ui/__mocks__/actions.js', ); + config.resolve.alias['../../../store/actions'] = require.resolve( + '../ui/__mocks__/actions.js', + ); // Import within controller-utils crashes storybook. config.resolve.alias['@ethereumjs/util'] = require.resolve( '../ui/__mocks__/ethereumjs-util.js', diff --git a/ui/pages/confirmations/components/confirm/info/base-transaction-info/base-transaction-info.tsx b/ui/pages/confirmations/components/confirm/info/base-transaction-info/base-transaction-info.tsx index fc58008d7784..08329536b524 100644 --- a/ui/pages/confirmations/components/confirm/info/base-transaction-info/base-transaction-info.tsx +++ b/ui/pages/confirmations/components/confirm/info/base-transaction-info/base-transaction-info.tsx @@ -20,8 +20,7 @@ const BaseTransactionInfo = () => { <> diff --git a/ui/pages/confirmations/components/confirm/info/nft-token-transfer/nft-token-transfer.tsx b/ui/pages/confirmations/components/confirm/info/nft-token-transfer/nft-token-transfer.tsx index a2e6a73b4353..b5d579994ef9 100644 --- a/ui/pages/confirmations/components/confirm/info/nft-token-transfer/nft-token-transfer.tsx +++ b/ui/pages/confirmations/components/confirm/info/nft-token-transfer/nft-token-transfer.tsx @@ -22,8 +22,7 @@ const NFTTokenTransferInfo = () => { {!isWalletInitiated && ( diff --git a/ui/pages/confirmations/components/confirm/info/token-transfer/token-transfer.tsx b/ui/pages/confirmations/components/confirm/info/token-transfer/token-transfer.tsx index b89e87350a36..df1136ec5213 100644 --- a/ui/pages/confirmations/components/confirm/info/token-transfer/token-transfer.tsx +++ b/ui/pages/confirmations/components/confirm/info/token-transfer/token-transfer.tsx @@ -22,8 +22,7 @@ const TokenTransferInfo = () => { {!isWalletInitiated && ( diff --git a/ui/pages/confirmations/components/simulation-details/amount-pill.test.tsx b/ui/pages/confirmations/components/simulation-details/amount-pill.test.tsx index 044eca8d887c..d08f9d7d1c52 100644 --- a/ui/pages/confirmations/components/simulation-details/amount-pill.test.tsx +++ b/ui/pages/confirmations/components/simulation-details/amount-pill.test.tsx @@ -6,7 +6,7 @@ import Tooltip from '../../../../components/ui/tooltip'; import { AmountPill } from './amount-pill'; import { AssetIdentifier, - NATIVE_ASSET_IDENTIFIER, + NativeAssetIdentifier, TokenAssetIdentifier, } from './types'; @@ -24,17 +24,28 @@ jest.mock('../../../../components/ui/tooltip', () => ({ })); const TOKEN_ID_MOCK = '0xabc'; +const CHAIN_ID_MOCK = '0x1'; + +const NATIVE_ASSET_MOCK: NativeAssetIdentifier = { + chainId: CHAIN_ID_MOCK, + standard: TokenStandard.none, +}; const ERC20_ASSET_MOCK: TokenAssetIdentifier = { + chainId: CHAIN_ID_MOCK, standard: TokenStandard.ERC20, address: '0x456', }; + const ERC721_ASSET_MOCK: TokenAssetIdentifier = { + chainId: CHAIN_ID_MOCK, standard: TokenStandard.ERC721, address: '0x123', tokenId: TOKEN_ID_MOCK, }; + const ERC1155_ASSET_MOCK: TokenAssetIdentifier = { + chainId: CHAIN_ID_MOCK, standard: TokenStandard.ERC1155, address: '0x789', tokenId: TOKEN_ID_MOCK, @@ -114,7 +125,7 @@ describe('AmountPill', () => { amount: BigNumber; expected: { text: string; tooltip: string }; }) => { - renderAndExpect(NATIVE_ASSET_IDENTIFIER, amount, expected); + renderAndExpect(NATIVE_ASSET_MOCK, amount, expected); }, ); }); diff --git a/ui/pages/confirmations/components/simulation-details/asset-pill.test.tsx b/ui/pages/confirmations/components/simulation-details/asset-pill.test.tsx index 477119b02284..4b11adf0e17b 100644 --- a/ui/pages/confirmations/components/simulation-details/asset-pill.test.tsx +++ b/ui/pages/confirmations/components/simulation-details/asset-pill.test.tsx @@ -10,7 +10,7 @@ import { AvatarNetwork } from '../../../../components/component-library/avatar-n import { mockNetworkState } from '../../../../../test/stub/networks'; import mockState from '../../../../../test/data/mock-state.json'; import { AssetPill } from './asset-pill'; -import { NATIVE_ASSET_IDENTIFIER, TokenAssetIdentifier } from './types'; +import { NativeAssetIdentifier, TokenAssetIdentifier } from './types'; jest.mock('../../../../components/component-library/avatar-network', () => ({ AvatarNetworkSize: { Sm: 'Sm' }, @@ -22,6 +22,8 @@ jest.mock('../../../../components/app/name', () => ({ default: jest.fn(() => null), })); +const CHAIN_ID_MOCK = '0x1'; + describe('AssetPill', () => { beforeEach(() => { jest.clearAllMocks(); @@ -61,10 +63,12 @@ describe('AssetPill', () => { }, }); - renderWithProvider( - , - store, - ); + const asset: NativeAssetIdentifier = { + chainId, + standard: TokenStandard.none, + }; + + renderWithProvider(, store); expect(screen.getByText(expected.ticker)).toBeInTheDocument(); @@ -81,6 +85,7 @@ describe('AssetPill', () => { it('renders Name component with correct props when asset standard is not none', () => { const asset: TokenAssetIdentifier = { + chainId: CHAIN_ID_MOCK, standard: TokenStandard.ERC20, address: '0x1234567890123456789012345678901234567890', }; diff --git a/ui/pages/confirmations/components/simulation-details/asset-pill.tsx b/ui/pages/confirmations/components/simulation-details/asset-pill.tsx index 1545c86e452c..99bd2b3af8ef 100644 --- a/ui/pages/confirmations/components/simulation-details/asset-pill.tsx +++ b/ui/pages/confirmations/components/simulation-details/asset-pill.tsx @@ -1,6 +1,7 @@ import React from 'react'; import { NameType } from '@metamask/name-controller'; import { useSelector } from 'react-redux'; +import { Hex } from '@metamask/utils'; import { AvatarNetwork, AvatarNetworkSize, @@ -18,16 +19,20 @@ import { } from '../../../../helpers/constants/design-system'; import Name from '../../../../components/app/name'; import { TokenStandard } from '../../../../../shared/constants/transaction'; -import { - getCurrentChainId, - getNativeCurrencyImage, -} from '../../../../selectors'; -import { getNativeCurrency } from '../../../../ducks/metamask/metamask'; +import { getNetworkConfigurationsByChainId } from '../../../../selectors'; +import { CHAIN_ID_TOKEN_IMAGE_MAP } from '../../../../../shared/constants/network'; import { AssetIdentifier } from './types'; -const NativeAssetPill: React.FC = () => { - const ticker = useSelector(getNativeCurrency); - const imgSrc = useSelector(getNativeCurrencyImage); +const NativeAssetPill: React.FC<{ chainId: Hex }> = ({ chainId }) => { + const imgSrc = + CHAIN_ID_TOKEN_IMAGE_MAP[chainId as keyof typeof CHAIN_ID_TOKEN_IMAGE_MAP]; + + const networkConfigurationsByChainId = useSelector( + getNetworkConfigurationsByChainId, + ); + + const network = networkConfigurationsByChainId?.[chainId]; + const { nativeCurrency } = network; return ( { }} > - {ticker} + {nativeCurrency} ); @@ -60,9 +65,10 @@ const NativeAssetPill: React.FC = () => { * @param props * @param props.asset */ -export const AssetPill: React.FC<{ asset: AssetIdentifier }> = ({ asset }) => { - // TODO: Temporary pending multi-chain support in simulations; - const chainId = useSelector(getCurrentChainId); +export const AssetPill: React.FC<{ + asset: AssetIdentifier; +}> = ({ asset }) => { + const { chainId } = asset; return ( = ({ asset }) => { }} > {asset.standard === TokenStandard.none ? ( - + ) : ( , +): TransactionMeta { + return metadata as TransactionMeta; +} const meta: Meta = { title: 'Components/App/SimulationDetails', @@ -96,180 +97,207 @@ type Story = StoryObj; export const MultipleTokens: Story = { args: { - simulationData: { - nativeBalanceChange: { - ...DUMMY_BALANCE_CHANGE, - difference: '0x12345678912345678', - isDecrease: true, - }, - tokenBalanceChanges: [ - { - ...DUMMY_BALANCE_CHANGE, - address: ERC20_TOKEN_1_MOCK, - difference: '0x123456', - isDecrease: false, - standard: SimulationTokenStandard.erc20, - }, - { - ...DUMMY_BALANCE_CHANGE, - address: ERC20_TOKEN_2_MOCK, - difference: '0x123456901', - isDecrease: false, - standard: SimulationTokenStandard.erc20, - }, - { + transaction: createTransactionMeta({ + chainId: CHAIN_ID_MOCK, + simulationData: { + nativeBalanceChange: { ...DUMMY_BALANCE_CHANGE, - address: ERC721_TOKEN_MOCK, - difference: '0x1', - isDecrease: false, - id: '0x721', - standard: SimulationTokenStandard.erc721, - }, - { - ...DUMMY_BALANCE_CHANGE, - address: ERC1155_TOKEN_MOCK, - difference: '0x13', - isDecrease: false, - id: '0x1155', - standard: SimulationTokenStandard.erc1155, + difference: '0x12345678912345678', + isDecrease: true, }, - ], - }, + tokenBalanceChanges: [ + { + ...DUMMY_BALANCE_CHANGE, + address: ERC20_TOKEN_1_MOCK, + difference: '0x123456', + isDecrease: false, + standard: SimulationTokenStandard.erc20, + }, + { + ...DUMMY_BALANCE_CHANGE, + address: ERC20_TOKEN_2_MOCK, + difference: '0x123456901', + isDecrease: false, + standard: SimulationTokenStandard.erc20, + }, + { + ...DUMMY_BALANCE_CHANGE, + address: ERC721_TOKEN_MOCK, + difference: '0x1', + isDecrease: false, + id: '0x721', + standard: SimulationTokenStandard.erc721, + }, + { + ...DUMMY_BALANCE_CHANGE, + address: ERC1155_TOKEN_MOCK, + difference: '0x13', + isDecrease: false, + id: '0x1155', + standard: SimulationTokenStandard.erc1155, + }, + ], + }, + }), }, }; export const SendSmallAmount: Story = { args: { - simulationData: { - nativeBalanceChange: { - ...DUMMY_BALANCE_CHANGE, - difference: '0x123', - isDecrease: true, + transaction: createTransactionMeta({ + chainId: CHAIN_ID_MOCK, + simulationData: { + nativeBalanceChange: { + ...DUMMY_BALANCE_CHANGE, + difference: '0x123', + isDecrease: true, + }, + tokenBalanceChanges: [], }, - tokenBalanceChanges: [], - }, + }), }, }; export const LongValuesAndNames: Story = { args: { - simulationData: { - nativeBalanceChange: { - ...DUMMY_BALANCE_CHANGE, - difference: '0x12345678912345678', - isDecrease: true, - }, - tokenBalanceChanges: [ - { - ...DUMMY_BALANCE_CHANGE, - address: ERC20_TOKEN_1_MOCK, - difference: '0x42345909', - isDecrease: false, - standard: SimulationTokenStandard.erc20, - }, - { + transaction: createTransactionMeta({ + chainId: CHAIN_ID_MOCK, + simulationData: { + nativeBalanceChange: { ...DUMMY_BALANCE_CHANGE, - address: ERC20_TOKEN_2_MOCK, - difference: '0x123456901', - isDecrease: false, - standard: SimulationTokenStandard.erc20, + difference: '0x12345678912345678', + isDecrease: true, }, - ], - }, + tokenBalanceChanges: [ + { + ...DUMMY_BALANCE_CHANGE, + address: ERC20_TOKEN_1_MOCK, + difference: '0x42345909', + isDecrease: false, + standard: SimulationTokenStandard.erc20, + }, + { + ...DUMMY_BALANCE_CHANGE, + address: ERC20_TOKEN_2_MOCK, + difference: '0x123456901', + isDecrease: false, + standard: SimulationTokenStandard.erc20, + }, + ], + }, + }), }, }; export const PolygonNativeAsset: Story = { args: { - simulationData: { - nativeBalanceChange: { - ...DUMMY_BALANCE_CHANGE, - difference: '0x9345678923456789', - isDecrease: true, + transaction: createTransactionMeta({ + chainId: CHAIN_IDS.POLYGON, + simulationData: { + nativeBalanceChange: { + ...DUMMY_BALANCE_CHANGE, + difference: '0x9345678923456789', + isDecrease: true, + }, + tokenBalanceChanges: [], }, - tokenBalanceChanges: [], - }, + }), }, - decorators: [ - (story) => {story()}, - ], }; export const ArbitrumNativeAsset: Story = { args: { - simulationData: { - nativeBalanceChange: { - ...DUMMY_BALANCE_CHANGE, - difference: '0x9345678923456789', - isDecrease: true, + transaction: createTransactionMeta({ + chainId: CHAIN_IDS.ARBITRUM, + simulationData: { + nativeBalanceChange: { + ...DUMMY_BALANCE_CHANGE, + difference: '0x9345678923456789', + isDecrease: true, + }, + tokenBalanceChanges: [], }, - tokenBalanceChanges: [], - }, + }), }, - decorators: [ - (story) => {story()}, - ], }; export const ReceiveOnly: Story = { args: { - simulationData: { - nativeBalanceChange: { - previousBalance: '0x2', - newBalance: '0x1', - difference: '0x12345678912345678', - isDecrease: false, + transaction: createTransactionMeta({ + chainId: CHAIN_ID_MOCK, + simulationData: { + nativeBalanceChange: { + previousBalance: '0x2', + newBalance: '0x1', + difference: '0x12345678912345678', + isDecrease: false, + }, + tokenBalanceChanges: [], }, - tokenBalanceChanges: [], - }, + }), }, }; export const SendOnly: Story = { args: { - simulationData: { - nativeBalanceChange: { - previousBalance: '0x1', - newBalance: '0x2', - difference: '0x12345678912345678', - isDecrease: true, + transaction: createTransactionMeta({ + chainId: CHAIN_ID_MOCK, + simulationData: { + nativeBalanceChange: { + previousBalance: '0x1', + newBalance: '0x2', + difference: '0x12345678912345678', + isDecrease: true, + }, + tokenBalanceChanges: [], }, - tokenBalanceChanges: [], - }, + }), }, }; export const NoBalanceChanges: Story = { args: { - simulationData: { - nativeBalanceChange: undefined, - tokenBalanceChanges: [], - }, + transaction: createTransactionMeta({ + chainId: CHAIN_ID_MOCK, + simulationData: { + nativeBalanceChange: undefined, + tokenBalanceChanges: [], + }, + }), }, }; export const Loading: Story = { args: { - simulationData: undefined, + transaction: createTransactionMeta({ + chainId: CHAIN_ID_MOCK, + simulationData: undefined, + }), }, }; export const TransactionReverted: Story = { args: { - simulationData: { - error: { code: SimulationErrorCode.Reverted }, - nativeBalanceChange: undefined, - tokenBalanceChanges: [], - }, + transaction: createTransactionMeta({ + chainId: CHAIN_ID_MOCK, + simulationData: { + error: { code: SimulationErrorCode.Reverted }, + nativeBalanceChange: undefined, + tokenBalanceChanges: [], + }, + }), }, }; export const GenericError: Story = { args: { - simulationData: { - error: {}, - nativeBalanceChange: undefined, - tokenBalanceChanges: [], - }, + transaction: createTransactionMeta({ + chainId: CHAIN_ID_MOCK, + simulationData: { + error: {}, + nativeBalanceChange: undefined, + tokenBalanceChanges: [], + }, + }), }, }; diff --git a/ui/pages/confirmations/components/simulation-details/simulation-details.test.tsx b/ui/pages/confirmations/components/simulation-details/simulation-details.test.tsx index 5971e513e3a1..5bb182ac7e66 100644 --- a/ui/pages/confirmations/components/simulation-details/simulation-details.test.tsx +++ b/ui/pages/confirmations/components/simulation-details/simulation-details.test.tsx @@ -4,6 +4,7 @@ import { screen } from '@testing-library/react'; import { SimulationData, SimulationErrorCode, + TransactionMeta, } from '@metamask/transaction-controller'; import { BigNumber } from 'bignumber.js'; import { renderWithProvider } from '../../../../../test/lib/render-helpers'; @@ -28,8 +29,9 @@ jest.mock('./useSimulationMetrics'); const renderSimulationDetails = (simulationData?: Partial) => renderWithProvider( , store, ); diff --git a/ui/pages/confirmations/components/simulation-details/simulation-details.tsx b/ui/pages/confirmations/components/simulation-details/simulation-details.tsx index 79d5c2105a9c..0ad01e921cff 100644 --- a/ui/pages/confirmations/components/simulation-details/simulation-details.tsx +++ b/ui/pages/confirmations/components/simulation-details/simulation-details.tsx @@ -1,7 +1,7 @@ import { - SimulationData, SimulationError, SimulationErrorCode, + TransactionMeta, } from '@metamask/transaction-controller'; import React from 'react'; import { @@ -30,10 +30,9 @@ import { useBalanceChanges } from './useBalanceChanges'; import { useSimulationMetrics } from './useSimulationMetrics'; export type SimulationDetailsProps = { - simulationData?: SimulationData; - transactionId: string; enableMetrics?: boolean; isTransactionsRedesign?: boolean; + transaction: TransactionMeta; }; /** @@ -172,20 +171,19 @@ const SimulationDetailsLayout: React.FC<{ * Preview of a transaction's effects using simulation data. * * @param props - * @param props.simulationData - The simulation data to display. - * @param props.transactionId - The ID of the transaction being simulated. + * @param props.transaction - Metadata of the transaction that was simulated. * @param props.enableMetrics - Whether to enable simulation metrics. * @param props.isTransactionsRedesign - Whether or not the component is being * used inside the transaction redesign flow. */ export const SimulationDetails: React.FC = ({ - simulationData, - transactionId, + transaction, enableMetrics = false, isTransactionsRedesign = false, }: SimulationDetailsProps) => { const t = useI18nContext(); - const balanceChangesResult = useBalanceChanges(simulationData); + const { chainId, id: transactionId, simulationData } = transaction; + const balanceChangesResult = useBalanceChanges({ chainId, simulationData }); const loading = !simulationData || balanceChangesResult.pending; useSimulationMetrics({ diff --git a/ui/pages/confirmations/components/simulation-details/types.ts b/ui/pages/confirmations/components/simulation-details/types.ts index 7db6a30357cd..c1a3290f6838 100644 --- a/ui/pages/confirmations/components/simulation-details/types.ts +++ b/ui/pages/confirmations/components/simulation-details/types.ts @@ -2,10 +2,6 @@ import { Hex } from '@metamask/utils'; import { BigNumber } from 'bignumber.js'; import { TokenStandard } from '../../../../../shared/constants/transaction'; -export const NATIVE_ASSET_IDENTIFIER: NativeAssetIdentifier = { - standard: TokenStandard.none, -}; - /** * Describes an amount of fiat. */ @@ -17,6 +13,7 @@ export type FiatAmount = FiatAmountAvailable | typeof FIAT_UNAVAILABLE; * Identifies the native asset of a chain. */ export type NativeAssetIdentifier = { + chainId: Hex; standard: TokenStandard.none; address?: undefined; tokenId?: undefined; @@ -26,6 +23,7 @@ export type NativeAssetIdentifier = { * Uniquely identifies a token asset on a chain. */ export type TokenAssetIdentifier = { + chainId: Hex; standard: Exclude; address: Hex; tokenId?: Hex; diff --git a/ui/pages/confirmations/components/simulation-details/useBalanceChanges.test.ts b/ui/pages/confirmations/components/simulation-details/useBalanceChanges.test.ts index 5dc0be870538..f77774c6ec4a 100644 --- a/ui/pages/confirmations/components/simulation-details/useBalanceChanges.test.ts +++ b/ui/pages/confirmations/components/simulation-details/useBalanceChanges.test.ts @@ -57,6 +57,8 @@ const DIFFERENCE_1_MOCK: Hex = '0x11'; const DIFFERENCE_2_MOCK: Hex = '0x2'; const DIFFERENCE_ETH_MOCK: Hex = '0x1234567890123456789'; +const CHAIN_ID_MOCK = '0x123'; + const dummyBalanceChange = { previousBalance: '0xIGNORE' as Hex, newBalance: '0xIGNORE' as Hex, @@ -98,7 +100,10 @@ describe('useBalanceChanges', () => { describe('pending states', () => { it('returns pending=true if no simulation data', async () => { const { result, waitForNextUpdate } = renderHook(() => - useBalanceChanges(undefined), + useBalanceChanges({ + chainId: CHAIN_ID_MOCK, + simulationData: undefined, + }), ); expect(result.current).toEqual({ pending: true, value: [] }); await waitForNextUpdate(); @@ -119,7 +124,7 @@ describe('useBalanceChanges', () => { ], }; const { result, unmount, waitForNextUpdate } = renderHook(() => - useBalanceChanges(simulationData), + useBalanceChanges({ chainId: CHAIN_ID_MOCK, simulationData }), ); await waitForNextUpdate(); @@ -143,7 +148,7 @@ describe('useBalanceChanges', () => { ], }; const { result, unmount, waitForNextUpdate } = renderHook(() => - useBalanceChanges(simulationData), + useBalanceChanges({ chainId: CHAIN_ID_MOCK, simulationData }), ); await waitForNextUpdate(); @@ -161,7 +166,9 @@ describe('useBalanceChanges', () => { nativeBalanceChange: undefined, tokenBalanceChanges, }; - return renderHook(() => useBalanceChanges(simulationData)); + return renderHook(() => + useBalanceChanges({ chainId: CHAIN_ID_MOCK, simulationData }), + ); }; it('maps token balance changes correctly', async () => { @@ -181,6 +188,7 @@ describe('useBalanceChanges', () => { expect(changes).toEqual([ { asset: { + chainId: CHAIN_ID_MOCK, address: ERC20_TOKEN_ADDRESS_1_MOCK, standard: TokenStandard.ERC20, tokenId: undefined, @@ -237,6 +245,7 @@ describe('useBalanceChanges', () => { expect(result.current.value).toEqual([ { asset: { + chainId: CHAIN_ID_MOCK, address: NFT_TOKEN_ADDRESS_MOCK, standard: TokenStandard.ERC721, tokenId: TOKEN_ID_1_MOCK, @@ -307,7 +316,9 @@ describe('useBalanceChanges', () => { nativeBalanceChange, tokenBalanceChanges: [], }; - return renderHook(() => useBalanceChanges(simulationData)); + return renderHook(() => + useBalanceChanges({ chainId: CHAIN_ID_MOCK, simulationData }), + ); }; it('maps native balance change correctly', async () => { @@ -323,6 +334,7 @@ describe('useBalanceChanges', () => { expect(changes).toEqual([ { asset: { + chainId: CHAIN_ID_MOCK, standard: TokenStandard.none, }, amount: new BigNumber('-5373.003641998677469065'), @@ -382,7 +394,7 @@ describe('useBalanceChanges', () => { ], }; const { result, waitForNextUpdate } = renderHook(() => - useBalanceChanges(simulationData), + useBalanceChanges({ chainId: CHAIN_ID_MOCK, simulationData }), ); await waitForNextUpdate(); @@ -390,6 +402,7 @@ describe('useBalanceChanges', () => { const changes = result.current.value; expect(changes).toHaveLength(2); expect(changes[0].asset).toEqual({ + chainId: CHAIN_ID_MOCK, standard: TokenStandard.none, }); expect(changes[0].amount).toEqual( @@ -397,6 +410,7 @@ describe('useBalanceChanges', () => { ); expect(changes[0].fiatAmount).toBe(Number('-16119.010925996032')); expect(changes[1].asset).toEqual({ + chainId: CHAIN_ID_MOCK, address: ERC20_TOKEN_ADDRESS_1_MOCK, standard: TokenStandard.ERC20, }); diff --git a/ui/pages/confirmations/components/simulation-details/useBalanceChanges.ts b/ui/pages/confirmations/components/simulation-details/useBalanceChanges.ts index 2a198d76ea36..1bbc9cb5eec8 100644 --- a/ui/pages/confirmations/components/simulation-details/useBalanceChanges.ts +++ b/ui/pages/confirmations/components/simulation-details/useBalanceChanges.ts @@ -11,14 +11,14 @@ import { ContractExchangeRates } from '@metamask/assets-controllers'; import { useAsyncResultOrThrow } from '../../../../hooks/useAsyncResult'; import { TokenStandard } from '../../../../../shared/constants/transaction'; import { getConversionRate } from '../../../../ducks/metamask/metamask'; -import { getCurrentChainId, getCurrentCurrency } from '../../../../selectors'; +import { getCurrentCurrency } from '../../../../selectors'; import { fetchTokenExchangeRates } from '../../../../helpers/utils/util'; import { ERC20_DEFAULT_DECIMALS, fetchErc20Decimals } from '../../utils/token'; import { BalanceChange, FIAT_UNAVAILABLE, - NATIVE_ASSET_IDENTIFIER, + NativeAssetIdentifier, TokenAssetIdentifier, } from './types'; @@ -94,17 +94,25 @@ async function fetchTokenFiatRates( function getNativeBalanceChange( nativeBalanceChange: SimulationBalanceChange | undefined, nativeFiatRate: number | undefined, + chainId: Hex, ): BalanceChange | undefined { if (!nativeBalanceChange) { return undefined; } - const asset = NATIVE_ASSET_IDENTIFIER; + + const asset: NativeAssetIdentifier = { + chainId, + standard: TokenStandard.none, + }; + const amount = getAssetAmount(nativeBalanceChange, NATIVE_DECIMALS); + const fiatAmount = nativeFiatRate ? amount .times(convertNumberToStringWithPrecisionWarning(nativeFiatRate)) .toNumber() : FIAT_UNAVAILABLE; + return { asset, amount, fiatAmount }; } @@ -113,9 +121,11 @@ function getTokenBalanceChanges( tokenBalanceChanges: SimulationTokenBalanceChange[], erc20Decimals: Record, erc20FiatRates: Partial>, + chainId: Hex, ): BalanceChange[] { return tokenBalanceChanges.map((tokenBc) => { const asset: TokenAssetIdentifier = { + chainId, standard: convertStandard(tokenBc.standard), address: tokenBc.address.toLowerCase() as Hex, tokenId: tokenBc.id, @@ -140,10 +150,13 @@ function getTokenBalanceChanges( } // Compiles a list of balance changes from simulation data -export const useBalanceChanges = ( - simulationData: SimulationData | undefined, -): { pending: boolean; value: BalanceChange[] } => { - const chainId = useSelector(getCurrentChainId); +export const useBalanceChanges = ({ + chainId, + simulationData, +}: { + chainId: Hex; + simulationData?: SimulationData; +}): { pending: boolean; value: BalanceChange[] } => { const fiatCurrency = useSelector(getCurrentCurrency); const nativeFiatRate = useSelector(getConversionRate); @@ -171,11 +184,14 @@ export const useBalanceChanges = ( const nativeChange = getNativeBalanceChange( nativeBalanceChange, nativeFiatRate, + chainId, ); + const tokenChanges = getTokenBalanceChanges( tokenBalanceChanges, erc20Decimals.value, erc20FiatRates.value, + chainId, ); const balanceChanges: BalanceChange[] = [ diff --git a/ui/pages/confirmations/components/simulation-details/useSimulationMetrics.ts b/ui/pages/confirmations/components/simulation-details/useSimulationMetrics.ts index 1f5c349d0050..15e8327176ea 100644 --- a/ui/pages/confirmations/components/simulation-details/useSimulationMetrics.ts +++ b/ui/pages/confirmations/components/simulation-details/useSimulationMetrics.ts @@ -4,7 +4,6 @@ import { } from '@metamask/transaction-controller'; import { useContext, useEffect, useState } from 'react'; import { NameType } from '@metamask/name-controller'; -import { useSelector } from 'react-redux'; import { useTransactionEventFragment } from '../../hooks/useTransactionEventFragment'; import { UseDisplayNameRequest, @@ -17,7 +16,6 @@ import { MetaMetricsEventCategory, MetaMetricsEventName, } from '../../../../../shared/constants/metametrics'; -import { getCurrentChainId } from '../../../../selectors'; import { calculateTotalFiat } from './fiat-display'; import { BalanceChange } from './types'; import { useLoadingTime } from './useLoadingTime'; @@ -65,9 +63,6 @@ export function useSimulationMetrics({ }: UseSimulationMetricsProps) { const { loadingTime, setLoadingComplete } = useLoadingTime(); - // TODO: Temporary pending multi-chain support in simulations. - const chainId = useSelector(getCurrentChainId); - if (!loading) { setLoadingComplete(); } @@ -79,7 +74,7 @@ export function useSimulationMetrics({ value: asset.address as string, type: NameType.ETHEREUM_ADDRESS, preferContractSymbol: true, - variation: chainId, + variation: asset.chainId, })); const displayNames = useDisplayNames(displayNameRequests); diff --git a/ui/pages/confirmations/confirm-transaction-base/confirm-transaction-base.component.js b/ui/pages/confirmations/confirm-transaction-base/confirm-transaction-base.component.js index 1ae7eaaeb33d..c07b8a263f8a 100644 --- a/ui/pages/confirmations/confirm-transaction-base/confirm-transaction-base.component.js +++ b/ui/pages/confirmations/confirm-transaction-base/confirm-transaction-base.component.js @@ -543,11 +543,7 @@ export default class ConfirmTransactionBase extends Component { const { simulationData } = txData; const simulationDetails = ( - + ); const showTotals = Boolean(simulationData?.error); diff --git a/ui/pages/smart-transactions/smart-transaction-status-page/smart-transaction-status-page.tsx b/ui/pages/smart-transactions/smart-transaction-status-page/smart-transaction-status-page.tsx index 4492ed4e4844..5e4ef2511a51 100644 --- a/ui/pages/smart-transactions/smart-transaction-status-page/smart-transaction-status-page.tsx +++ b/ui/pages/smart-transactions/smart-transaction-status-page/smart-transaction-status-page.tsx @@ -366,10 +366,7 @@ export const SmartTransactionStatusPage = ({ {canShowSimulationDetails && ( - + )}