diff --git a/app/components/Views/confirmations/components/confirmation-asset-polling-provider/confirmation-asset-polling-provider.test.tsx b/app/components/Views/confirmations/components/confirmation-asset-polling-provider/confirmation-asset-polling-provider.test.tsx
index 7dd989deb650..030cacef7948 100644
--- a/app/components/Views/confirmations/components/confirmation-asset-polling-provider/confirmation-asset-polling-provider.test.tsx
+++ b/app/components/Views/confirmations/components/confirmation-asset-polling-provider/confirmation-asset-polling-provider.test.tsx
@@ -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';
@@ -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
@@ -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 = () => Test Child;
+
+ renderWithProvider(
+
+
+ ,
+ { 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 = () => Test Child;
+
+ renderWithProvider(
+
+
+ ,
+ { 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 = () => Test Child;
+
+ renderWithProvider(
+
+
+ ,
+ { state: {} },
+ );
+
+ // Should only include EVM chains (0x1, 0x2), not 'SolanaChainId'
expect(mockAssetPollingProvider).toHaveBeenCalledWith(
{
chainIds: [CHAIN_ID_MOCK, CHAIN_ID_2_MOCK],
@@ -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 = () => Test Child;
+
+ renderWithProvider(
+
+
+ ,
+ { 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(),
+ );
+ });
});
});
diff --git a/app/components/Views/confirmations/components/confirmation-asset-polling-provider/confirmation-asset-polling-provider.tsx b/app/components/Views/confirmations/components/confirmation-asset-polling-provider/confirmation-asset-polling-provider.tsx
index dc1e01e2a096..3ef96566b759 100644
--- a/app/components/Views/confirmations/components/confirmation-asset-polling-provider/confirmation-asset-polling-provider.tsx
+++ b/app/components/Views/confirmations/components/confirmation-asset-polling-provider/confirmation-asset-polling-provider.tsx
@@ -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;