Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import React from 'react';
import { Hex } from '@metamask/utils';
import { Text } from 'react-native';
import { TransactionMeta } from '@metamask/transaction-controller';
import renderWithProvider from '../../../../../util/test/renderWithProvider';
Expand Down Expand Up @@ -204,7 +205,7 @@ describe('ConfirmationAssetPollingProvider', () => {
it('handles different chainId formats correctly', () => {
const customTransactionMetadata = {
...mockTransactionMetadata,
chainId: '0x89' as `0x${string}`,
chainId: '0x89' as Hex,
};

jest
Expand All @@ -220,6 +221,96 @@ describe('ConfirmationAssetPollingProvider', () => {
{ state: {} },
);

// The transaction's chainId should be included even if it's not in bridge chains
expect(mockAssetPollingProvider).toHaveBeenCalledWith(
{
chainIds: [CHAIN_ID_MOCK, CHAIN_ID_2_MOCK, '0x89'],
address: '0x935e73edb9ff52e23bac7f7e043a1ecd06d05477',
},
expect.anything(),
);
});

it('does not duplicate chainId when transaction chainId is already in bridge chains', () => {
const customTransactionMetadata = {
...mockTransactionMetadata,
chainId: CHAIN_ID_MOCK as Hex,
};

jest
.spyOn(TransactionMetadataRequestHook, 'useTransactionMetadataRequest')
.mockReturnValue(customTransactionMetadata);

const TestChild = () => <Text testID="test-child">Test Child</Text>;

renderWithProvider(
<ConfirmationAssetPollingProvider>
<TestChild />
</ConfirmationAssetPollingProvider>,
{ state: {} },
);

// Should not duplicate CHAIN_ID_MOCK since it's already in bridge chains
expect(mockAssetPollingProvider).toHaveBeenCalledWith(
{
chainIds: [CHAIN_ID_MOCK, CHAIN_ID_2_MOCK],
address: '0x935e73edb9ff52e23bac7f7e043a1ecd06d05477',
},
expect.anything(),
);
});

it('handles empty bridge chains list', () => {
selectEnabledSourceChainsMock.mockReturnValue([]);

const customTransactionMetadata = {
...mockTransactionMetadata,
chainId: '0x89' as Hex,
};

jest
.spyOn(TransactionMetadataRequestHook, 'useTransactionMetadataRequest')
.mockReturnValue(customTransactionMetadata);

const TestChild = () => <Text testID="test-child">Test Child</Text>;

renderWithProvider(
<ConfirmationAssetPollingProvider>
<TestChild />
</ConfirmationAssetPollingProvider>,
{ state: {} },
);

// Should still include transaction chainId even with no bridge chains
expect(mockAssetPollingProvider).toHaveBeenCalledWith(
{
chainIds: ['0x89'],
address: '0x935e73edb9ff52e23bac7f7e043a1ecd06d05477',
},
expect.anything(),
);
});

it('filters out non-EVM chains from bridge chains', () => {
const customTransactionMetadata = {
...mockTransactionMetadata,
chainId: CHAIN_ID_MOCK as Hex,
};

jest
.spyOn(TransactionMetadataRequestHook, 'useTransactionMetadataRequest')
.mockReturnValue(customTransactionMetadata);

const TestChild = () => <Text testID="test-child">Test Child</Text>;

renderWithProvider(
<ConfirmationAssetPollingProvider>
<TestChild />
</ConfirmationAssetPollingProvider>,
{ state: {} },
);

// Should only include EVM chains (0x1, 0x2), not 'SolanaChainId'
expect(mockAssetPollingProvider).toHaveBeenCalledWith(
{
chainIds: [CHAIN_ID_MOCK, CHAIN_ID_2_MOCK],
Expand All @@ -228,5 +319,36 @@ describe('ConfirmationAssetPollingProvider', () => {
expect.anything(),
);
});

it('handles newly added network from dapp scenario', () => {
// Simulate a newly added custom network that is not in bridge chains
const newCustomChainId = '0xa4b1'; // Arbitrum One
const customTransactionMetadata = {
...mockTransactionMetadata,
chainId: newCustomChainId as Hex,
};

jest
.spyOn(TransactionMetadataRequestHook, 'useTransactionMetadataRequest')
.mockReturnValue(customTransactionMetadata);

const TestChild = () => <Text testID="test-child">Test Child</Text>;

renderWithProvider(
<ConfirmationAssetPollingProvider>
<TestChild />
</ConfirmationAssetPollingProvider>,
{ state: {} },
);

// The newly added network should be included for polling
expect(mockAssetPollingProvider).toHaveBeenCalledWith(
{
chainIds: [CHAIN_ID_MOCK, CHAIN_ID_2_MOCK, newCustomChainId],
address: '0x935e73edb9ff52e23bac7f7e043a1ecd06d05477',
},
expect.anything(),
);
});
});
});
Original file line number Diff line number Diff line change
Expand Up @@ -23,11 +23,21 @@ export const ConfirmationAssetPollingProvider = ({
const transactionMeta = useTransactionMetadataRequest();
const bridgeChains = useSelector(selectEnabledSourceChains);

const pollChainIds = useMemo(
() =>
bridgeChains.filter((chain) => chain.isEvm).map((chain) => chain.chainId),
[bridgeChains],
) as Hex[];
const pollChainIds = useMemo(() => {
const bridgeChainIds = bridgeChains
.filter((chain) => chain.isEvm)
.map((chain) => chain.chainId as Hex);

// Always include the transaction's chain to ensure balance is available
// This is critical when a user adds a new network from a dapp and immediately
// attempts to transact - the new network may not be in the bridge chains list
const txChainId = transactionMeta?.chainId as Hex | undefined;
if (txChainId && !bridgeChainIds.includes(txChainId)) {
return [...bridgeChainIds, txChainId];
}

return bridgeChainIds;
}, [bridgeChains, transactionMeta?.chainId]);

if (!transactionMeta) {
return children;
Expand Down
Loading