From db64700cbffc260cf09c5603fdbe4024f9b8415a Mon Sep 17 00:00:00 2001 From: Florin Dzeladini Date: Tue, 10 Feb 2026 02:39:49 +0100 Subject: [PATCH 01/10] fix: hide tempo native tokens --- .../assets-controller/src/AssetsController.ts | 37 ++++++++++++++ .../src/selectors/token-selectors.ts | 49 +++++++++++++++++++ 2 files changed, 86 insertions(+) diff --git a/packages/assets-controller/src/AssetsController.ts b/packages/assets-controller/src/AssetsController.ts index 976f727feda..e7469c05e73 100644 --- a/packages/assets-controller/src/AssetsController.ts +++ b/packages/assets-controller/src/AssetsController.ts @@ -1346,6 +1346,11 @@ export class AssetsController extends BaseController< const assetChainId = extractChainId(typedAssetId); + // Skip native tokens on Tempo networks + if (this.#shouldHideNativeToken(assetChainId, typedAssetId, metadata)) { + continue; + } + if (!chainIdSet.has(assetChainId)) { continue; } @@ -1387,6 +1392,38 @@ export class AssetsController extends BaseController< return result; } + /** + * Determines if a native token should be hidden on specific networks. + * + * @param chainId - The CAIP-2 chain ID (e.g., "eip155:42431"). + * @param assetId - The CAIP-19 asset ID (e.g., "eip155:42431/slip44:60"). + * @param metadata - The asset metadata. + * @returns True if the token should be hidden, false otherwise. + */ + #shouldHideNativeToken( + chainId: ChainId, + assetId: Caip19AssetId, + metadata: AssetMetadata, + ): boolean { + const TEMPO_CHAIN_IDS = ['eip155:42431', 'eip155:4217']; // Tempo Testnet (42431) and Mainnet (4217) + + // Check if it's a Tempo network + if (!TEMPO_CHAIN_IDS.includes(chainId)) { + return false; + } + + // Check if it's a native token (either by metadata type or assetId format) + const isNative = + metadata.type === 'native' || assetId.includes('/slip44:'); + + if (isNative) { + return true; + } + + + return false; + } + /** * Maps a token standard to its corresponding asset type. * diff --git a/packages/assets-controllers/src/selectors/token-selectors.ts b/packages/assets-controllers/src/selectors/token-selectors.ts index 61716fc7fdb..dd943d26668 100644 --- a/packages/assets-controllers/src/selectors/token-selectors.ts +++ b/packages/assets-controllers/src/selectors/token-selectors.ts @@ -43,6 +43,34 @@ export const TRON_RESOURCE_SYMBOLS = Object.values( export const TRON_RESOURCE_SYMBOLS_SET: ReadonlySet = new Set(TRON_RESOURCE_SYMBOLS); +/** + * Determines if a native token should be hidden on Tempo networks. + * + * @param chainId - The hex chain ID (e.g., "0xa5bd"). + * @param isNative - Whether the token is a native token. + * @param symbol - The token symbol. + * @returns True if the token should be hidden, false otherwise. + */ +function shouldHideNativeToken( + chainId: Hex | string, + isNative: boolean, + symbol?: string, +): boolean { + const TEMPO_CHAIN_IDS = ['0xa5bf', '0x1079']; // Tempo Testnet (42431) and Mainnet (4217) + + // Check if it's a Tempo network + if (!TEMPO_CHAIN_IDS.includes(chainId.toLowerCase())) { + return false; + } + + // Hide native tokens on Tempo + if (isNative) { + return true; + } + + return false; +} + export type AssetsByAccountGroup = { [accountGroupId: AccountGroupId]: AccountGroupAssets; }; @@ -182,6 +210,11 @@ const selectAllEvmAccountNativeBalances = createAssetListSelector( const { accountGroupId, type, accountId } = account; + // Skip native tokens on Tempo networks + if (shouldHideNativeToken(chainId, true)) { + continue; + } + groupAssets[accountGroupId] ??= {}; groupAssets[accountGroupId][chainId] ??= []; const groupChainAssets = groupAssets[accountGroupId][chainId]; @@ -384,6 +417,22 @@ const selectAllMultichainAssets = createAssetListSelector( continue; } + // Skip native tokens on Tempo networks + const isNative = caipAsset.assetNamespace === 'slip44'; + // Convert CAIP-2 chain ID to hex for comparison + const chainIdHex = chainId.startsWith('eip155:') + ? `0x${parseInt(chainId.split(':')[1], 10).toString(16)}` + : chainId; + if ( + shouldHideNativeToken( + chainIdHex, + isNative, + assetMetadata.symbol, + ) + ) { + continue; + } + groupAssets[accountGroupId] ??= {}; groupAssets[accountGroupId][chainId] ??= []; const groupChainAssets = groupAssets[accountGroupId][chainId]; From eeec144a478dc853f6625a18ed501cc5d574ccc7 Mon Sep 17 00:00:00 2001 From: Florin Dzeladini Date: Tue, 10 Feb 2026 02:43:57 +0100 Subject: [PATCH 02/10] fix: hide tempo native token --- .../src/AssetsController.test.ts | 174 ++++++++++++++++++ .../src/selectors/token-selectors.test.ts | 125 +++++++++++++ 2 files changed, 299 insertions(+) diff --git a/packages/assets-controller/src/AssetsController.test.ts b/packages/assets-controller/src/AssetsController.test.ts index d38ba319203..0d6cfac2077 100644 --- a/packages/assets-controller/src/AssetsController.test.ts +++ b/packages/assets-controller/src/AssetsController.test.ts @@ -542,6 +542,180 @@ describe('AssetsController', () => { expect(assets).toBeDefined(); }); }); + + it('hides native tokens on Tempo testnet (eip155:42431)', async () => { + await withController( + { + state: { + assetsMetadata: { + 'eip155:42431/slip44:60': { + type: 'native', + symbol: 'ETH', + name: 'Ethereum', + decimals: 18, + }, + 'eip155:42431/erc20:0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48': { + type: 'erc20', + symbol: 'USDC', + name: 'USD Coin', + decimals: 6, + }, + }, + assetsBalance: { + [MOCK_ACCOUNT_ID]: { + 'eip155:42431/slip44:60': { + amount: '1', + unit: 'ETH', + }, + 'eip155:42431/erc20:0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48': { + amount: '100', + unit: 'USDC', + }, + }, + }, + assetsPrice: {}, + customAssets: {}, + assetPreferences: {}, + }, + }, + async ({ controller }) => { + const accounts = [createMockInternalAccount()]; + const assets = await controller.getAssets(accounts, { + chainIds: ['eip155:42431'], + }); + + // Native token should be hidden + expect( + assets[MOCK_ACCOUNT_ID]['eip155:42431/slip44:60'], + ).toBeUndefined(); + + // ERC20 token should still be visible + expect( + assets[MOCK_ACCOUNT_ID][ + 'eip155:42431/erc20:0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48' + ], + ).toBeDefined(); + }, + ); + }); + + it('hides native tokens on Tempo mainnet (eip155:4217)', async () => { + await withController( + { + state: { + assetsMetadata: { + 'eip155:4217/slip44:60': { + type: 'native', + symbol: 'ETH', + name: 'Ethereum', + decimals: 18, + }, + }, + assetsBalance: { + [MOCK_ACCOUNT_ID]: { + 'eip155:4217/slip44:60': { + amount: '1', + unit: 'ETH', + }, + }, + }, + assetsPrice: {}, + customAssets: {}, + assetPreferences: {}, + }, + }, + async ({ controller }) => { + const accounts = [createMockInternalAccount()]; + const assets = await controller.getAssets(accounts, { + chainIds: ['eip155:4217'], + }); + + // Native token should be hidden + expect( + assets[MOCK_ACCOUNT_ID]['eip155:4217/slip44:60'], + ).toBeUndefined(); + }, + ); + }); + + it('does not hide native tokens on non-Tempo networks', async () => { + await withController( + { + state: { + assetsMetadata: { + 'eip155:1/slip44:60': { + type: 'native', + symbol: 'ETH', + name: 'Ethereum', + decimals: 18, + }, + }, + assetsBalance: { + [MOCK_ACCOUNT_ID]: { + 'eip155:1/slip44:60': { + amount: '1', + unit: 'ETH', + }, + }, + }, + assetsPrice: {}, + customAssets: {}, + assetPreferences: {}, + }, + }, + async ({ controller }) => { + const accounts = [createMockInternalAccount()]; + const assets = await controller.getAssets(accounts, { + chainIds: ['eip155:1'], + }); + + // Native token should still be visible on Ethereum + expect(assets[MOCK_ACCOUNT_ID]['eip155:1/slip44:60']).toBeDefined(); + expect( + assets[MOCK_ACCOUNT_ID]['eip155:1/slip44:60'].metadata.symbol, + ).toBe('ETH'); + }, + ); + }); + + it('hides native tokens identified by metadata type', async () => { + await withController( + { + state: { + assetsMetadata: { + 'eip155:42431/some:other': { + type: 'native', + symbol: 'ETH', + name: 'Ethereum', + decimals: 18, + }, + }, + assetsBalance: { + [MOCK_ACCOUNT_ID]: { + 'eip155:42431/some:other': { + amount: '1', + unit: 'ETH', + }, + }, + }, + assetsPrice: {}, + customAssets: {}, + assetPreferences: {}, + }, + }, + async ({ controller }) => { + const accounts = [createMockInternalAccount()]; + const assets = await controller.getAssets(accounts, { + chainIds: ['eip155:42431'], + }); + + // Native token should be hidden even if assetId doesn't have slip44 + expect( + assets[MOCK_ACCOUNT_ID]['eip155:42431/some:other'], + ).toBeUndefined(); + }, + ); + }); }); describe('getAssetsBalance', () => { diff --git a/packages/assets-controllers/src/selectors/token-selectors.test.ts b/packages/assets-controllers/src/selectors/token-selectors.test.ts index 6b3126d8c08..1d874a9d009 100644 --- a/packages/assets-controllers/src/selectors/token-selectors.test.ts +++ b/packages/assets-controllers/src/selectors/token-selectors.test.ts @@ -1111,5 +1111,130 @@ describe('token-selectors', () => { // Should have undefined fiat since there's no currency rate for 'INK' expect(inkNativeToken?.fiat).toBeUndefined(); }); + + it('hides native tokens on Tempo testnet (0xa5bf)', () => { + const tempoTestnetChainId = '0xa5bf' as Hex; // 42431 in decimal + const stateWithTempoTestnet = { + ...mockedMergedState, + networkConfigurationsByChainId: { + ...mockNetworkControllerState.networkConfigurationsByChainId, + [tempoTestnetChainId]: { + nativeCurrency: 'ETH', + }, + }, + accountsByChainId: { + ...mockAccountsTrackerControllerState.accountsByChainId, + [tempoTestnetChainId]: { + '0x2bd63233fe369b0f13eaf25292af5a9b63d2b7ab': { + balance: '0xDE0B6B3A7640000', // 1 ETH + }, + }, + }, + }; + + const result = selectAssetsBySelectedAccountGroup(stateWithTempoTestnet); + + // Native token should be hidden on Tempo testnet + const nativeToken = result[tempoTestnetChainId]?.find( + (asset) => asset.isNative, + ); + expect(nativeToken).toBeUndefined(); + }); + + it('hides native tokens on Tempo mainnet (0x1079)', () => { + const tempoMainnetChainId = '0x1079' as Hex; // 4217 in decimal + const stateWithTempoMainnet = { + ...mockedMergedState, + networkConfigurationsByChainId: { + ...mockNetworkControllerState.networkConfigurationsByChainId, + [tempoMainnetChainId]: { + nativeCurrency: 'ETH', + }, + }, + accountsByChainId: { + ...mockAccountsTrackerControllerState.accountsByChainId, + [tempoMainnetChainId]: { + '0x2bd63233fe369b0f13eaf25292af5a9b63d2b7ab': { + balance: '0xDE0B6B3A7640000', // 1 ETH + }, + }, + }, + }; + + const result = selectAssetsBySelectedAccountGroup(stateWithTempoMainnet); + + // Native token should be hidden on Tempo mainnet + const nativeToken = result[tempoMainnetChainId]?.find( + (asset) => asset.isNative, + ); + expect(nativeToken).toBeUndefined(); + }); + + it('does not hide native tokens on non-Tempo networks', () => { + const ethereumChainId = '0x1' as Hex; + const result = selectAssetsBySelectedAccountGroup(mockedMergedState); + + // Native token should still be visible on Ethereum + const nativeToken = result[ethereumChainId]?.find( + (asset) => asset.isNative, + ); + expect(nativeToken).toBeDefined(); + expect(nativeToken?.symbol).toBe('ETH'); + }); + + it('hides native multichain tokens on Tempo networks', () => { + const tempoTestnetChainId = '0xa5bf' as Hex; // 42431 in decimal + const tempoCaipChainId = 'eip155:42431'; + const tempoNativeAssetId = `${tempoCaipChainId}/slip44:60` as const; + + const stateWithTempoMultichain = { + ...mockedMergedState, + accountsAssets: { + ...mockMultichainAssetsControllerState.accountsAssets, + '2d89e6a0-b4e6-45a8-a707-f10cef143b42': [ + ...mockMultichainAssetsControllerState.accountsAssets[ + '2d89e6a0-b4e6-45a8-a707-f10cef143b42' + ], + tempoNativeAssetId, + ], + }, + assetsMetadata: { + ...mockMultichainAssetsControllerState.assetsMetadata, + [tempoNativeAssetId]: { + fungible: true, + iconUrl: '', + name: 'Ethereum', + symbol: 'ETH', + units: [ + { + decimals: 18, + name: 'Ethereum', + symbol: 'ETH', + }, + ], + }, + }, + balances: { + ...mockMultichainBalancesControllerState.balances, + '2d89e6a0-b4e6-45a8-a707-f10cef143b42': { + ...mockMultichainBalancesControllerState.balances[ + '2d89e6a0-b4e6-45a8-a707-f10cef143b42' + ], + [tempoNativeAssetId]: { + amount: '1', + unit: 'ETH', + }, + }, + }, + }; + + const result = selectAssetsBySelectedAccountGroup(stateWithTempoMultichain); + + // Native token should be hidden on Tempo testnet + const nativeToken = result[tempoCaipChainId]?.find( + (asset) => asset.isNative, + ); + expect(nativeToken).toBeUndefined(); + }); }); }); From 1962e8cee0d602eaaa7877c5e5a8d7e1de2b2ffb Mon Sep 17 00:00:00 2001 From: Florin Dzeladini Date: Tue, 10 Feb 2026 02:47:53 +0100 Subject: [PATCH 03/10] chore: changelog --- packages/assets-controller/CHANGELOG.md | 4 ++++ packages/assets-controllers/CHANGELOG.md | 4 ++++ 2 files changed, 8 insertions(+) diff --git a/packages/assets-controller/CHANGELOG.md b/packages/assets-controller/CHANGELOG.md index e05a11a9599..22303d46d6a 100644 --- a/packages/assets-controller/CHANGELOG.md +++ b/packages/assets-controller/CHANGELOG.md @@ -7,6 +7,10 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ## [Unreleased] +### Changed + +- Hide native tokens on Tempo networks (testnet and mainnet) in `getAssets` method ([#7882](https://github.com/MetaMask/core/pull/7882)) + ## [0.2.0] ### Added diff --git a/packages/assets-controllers/CHANGELOG.md b/packages/assets-controllers/CHANGELOG.md index 437fcb7e322..2c5c06299c4 100644 --- a/packages/assets-controllers/CHANGELOG.md +++ b/packages/assets-controllers/CHANGELOG.md @@ -7,6 +7,10 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ## [Unreleased] +### Changed + +- Hide native tokens on Tempo networks (testnet and mainnet) in asset selectors ([#7882](https://github.com/MetaMask/core/pull/7882)) + ## [99.3.0] ### Added From 343fff616d585a988154ec09cd3b59d321a9188c Mon Sep 17 00:00:00 2001 From: Michele Esposito Date: Fri, 13 Feb 2026 15:14:30 +0100 Subject: [PATCH 04/10] fix tests --- .../src/selectors/token-selectors.test.ts | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) diff --git a/packages/assets-controllers/src/selectors/token-selectors.test.ts b/packages/assets-controllers/src/selectors/token-selectors.test.ts index 1d874a9d009..ed9eb8c8153 100644 --- a/packages/assets-controllers/src/selectors/token-selectors.test.ts +++ b/packages/assets-controllers/src/selectors/token-selectors.test.ts @@ -11,7 +11,10 @@ import type { Hex } from '@metamask/utils'; import { cloneDeep } from 'lodash'; import { MOCK_TRON_TOKENS } from './__fixtures__/arrange-tron-state'; -import { selectAssetsBySelectedAccountGroup } from './token-selectors'; +import { + AssetListState, + selectAssetsBySelectedAccountGroup, +} from './token-selectors'; import type { AccountGroupMultichainAccountObject } from '../../../account-tree-controller/src/group'; import type { CurrencyRateState } from '../CurrencyRateController'; import type { MultichainAssetsControllerState } from '../MultichainAssetsController'; @@ -1183,11 +1186,10 @@ describe('token-selectors', () => { }); it('hides native multichain tokens on Tempo networks', () => { - const tempoTestnetChainId = '0xa5bf' as Hex; // 42431 in decimal const tempoCaipChainId = 'eip155:42431'; const tempoNativeAssetId = `${tempoCaipChainId}/slip44:60` as const; - const stateWithTempoMultichain = { + const stateWithTempoMultichain: AssetListState = { ...mockedMergedState, accountsAssets: { ...mockMultichainAssetsControllerState.accountsAssets, @@ -1228,7 +1230,9 @@ describe('token-selectors', () => { }, }; - const result = selectAssetsBySelectedAccountGroup(stateWithTempoMultichain); + const result = selectAssetsBySelectedAccountGroup( + stateWithTempoMultichain, + ); // Native token should be hidden on Tempo testnet const nativeToken = result[tempoCaipChainId]?.find( From a925ab5dd3237170b9d586a10663a96ea685dc28 Mon Sep 17 00:00:00 2001 From: Florin Dzeladini Date: Thu, 19 Feb 2026 12:18:31 +0100 Subject: [PATCH 05/10] fix: skip tempo native for aggregated balance --- .../assets-controller/src/AssetsController.ts | 24 +++++---- .../src/AccountTrackerController.ts | 16 +++++- packages/assets-controllers/src/constants.ts | 49 +++++++++++++++++++ .../src/rpc-service/rpc-balance-fetcher.ts | 6 ++- .../src/selectors/token-selectors.ts | 35 +++---------- 5 files changed, 90 insertions(+), 40 deletions(-) diff --git a/packages/assets-controller/src/AssetsController.ts b/packages/assets-controller/src/AssetsController.ts index e00404163a4..2ce304fa056 100644 --- a/packages/assets-controller/src/AssetsController.ts +++ b/packages/assets-controller/src/AssetsController.ts @@ -1456,10 +1456,19 @@ export class AssetsController extends BaseController< assetId: Caip19AssetId, metadata: AssetMetadata, ): boolean { - const TEMPO_CHAIN_IDS = ['eip155:42431', 'eip155:4217']; // Tempo Testnet (42431) and Mainnet (4217) - - // Check if it's a Tempo network - if (!TEMPO_CHAIN_IDS.includes(chainId)) { + // Chain IDs where native tokens should be skipped (Tempo networks) + // These networks return arbitrary large numbers for native token balances via eth_getBalance + const CHAIN_IDS_TO_SKIP_NATIVE_TOKEN = [ + 'eip155:42431', // Tempo Testnet + 'eip155:4217', // Tempo Mainnet + ] as const; + + // Check if it's a chain that should skip native tokens + if ( + !CHAIN_IDS_TO_SKIP_NATIVE_TOKEN.includes( + chainId as (typeof CHAIN_IDS_TO_SKIP_NATIVE_TOKEN)[number], + ) + ) { return false; } @@ -1467,12 +1476,7 @@ export class AssetsController extends BaseController< const isNative = metadata.type === 'native' || assetId.includes('/slip44:'); - if (isNative) { - return true; - } - - - return false; + return isNative; } /** diff --git a/packages/assets-controllers/src/AccountTrackerController.ts b/packages/assets-controllers/src/AccountTrackerController.ts index f9c6f0e7780..47864d1179c 100644 --- a/packages/assets-controllers/src/AccountTrackerController.ts +++ b/packages/assets-controllers/src/AccountTrackerController.ts @@ -40,6 +40,8 @@ import type { Hex } from '@metamask/utils'; import { Mutex } from 'async-mutex'; import { cloneDeep, isEqual } from 'lodash'; +import { shouldIncludeNativeToken } from './constants'; + import { STAKING_CONTRACT_ADDRESS_BY_CHAINID } from './AssetsContractController'; import type { AssetsContractController, @@ -855,7 +857,19 @@ export class AccountTrackerController extends StaticIntervalPollingController { + acc[address] = { balance: '0x0' }; + return acc; + }, + {} as Record, + ); + } // TODO: This should use multicall when enabled by the user. return await Promise.all( diff --git a/packages/assets-controllers/src/constants.ts b/packages/assets-controllers/src/constants.ts index 96340d3aac1..6cc8e94efd5 100644 --- a/packages/assets-controllers/src/constants.ts +++ b/packages/assets-controllers/src/constants.ts @@ -18,3 +18,52 @@ export const SUPPORTED_NETWORKS_ACCOUNTS_API_V4 = [ '0x8f', // 143 '0x3e7', // 999 HyperEVM ]; + +/** + * Chain IDs where native tokens should be skipped. + * These networks return arbitrary large numbers for native token balances via eth_getBalance. + * Currently includes: Tempo Testnet (eip155:42431) and Tempo Mainnet (eip155:4217). + */ +const CHAIN_IDS_TO_SKIP_NATIVE_TOKEN = [ + 'eip155:42431', // Tempo Testnet + 'eip155:4217', // Tempo Mainnet +] as const; + +/** + * Determines if native token fetching should be included for the given chain. + * Returns false for chains that return arbitrary large numbers (e.g., Tempo networks). + * + * @param chainId - Chain ID in hex format (e.g., "0xa5bf") or CAIP-2 format (e.g., "eip155:42431"). + * @returns True if native token should be included, false if it should be skipped. + */ +export function shouldIncludeNativeToken(chainId: string): boolean { + // Convert hex format to CAIP-2 for comparison + if (chainId.startsWith('0x')) { + try { + const decimal = parseInt(chainId, 16); + const caipChainId = `eip155:${decimal}`; + if ( + CHAIN_IDS_TO_SKIP_NATIVE_TOKEN.includes( + caipChainId as (typeof CHAIN_IDS_TO_SKIP_NATIVE_TOKEN)[number], + ) + ) { + return false; + } + } catch { + // If conversion fails, assume it should be included + return true; + } + return true; + } + + // Check CAIP-2 format directly + if ( + CHAIN_IDS_TO_SKIP_NATIVE_TOKEN.includes( + chainId as (typeof CHAIN_IDS_TO_SKIP_NATIVE_TOKEN)[number], + ) + ) { + return false; + } + + return true; +} diff --git a/packages/assets-controllers/src/rpc-service/rpc-balance-fetcher.ts b/packages/assets-controllers/src/rpc-service/rpc-balance-fetcher.ts index 8cfa0936a83..a46d626f760 100644 --- a/packages/assets-controllers/src/rpc-service/rpc-balance-fetcher.ts +++ b/packages/assets-controllers/src/rpc-service/rpc-balance-fetcher.ts @@ -9,6 +9,7 @@ import type { Hex } from '@metamask/utils'; import BN from 'bn.js'; import { STAKING_CONTRACT_ADDRESS_BY_CHAINID } from '../AssetsContractController'; +import { shouldIncludeNativeToken } from '../constants'; import { getTokenBalancesForMultipleAddresses } from '../multicall'; import type { TokensControllerState } from '../TokensController'; @@ -101,13 +102,16 @@ export class RpcBalanceFetcher implements BalanceFetcher { const provider = this.#getProvider(chainId); await this.#ensureFreshBlockData(chainId); + // Skip native token fetching for chains that return arbitrary large numbers + const includeNative = shouldIncludeNativeToken(chainId); + const balanceResult = await safelyExecuteWithTimeout( async () => { return await getTokenBalancesForMultipleAddresses( accountTokenGroups, chainId, provider, - true, // include native + includeNative, // Skip native for Tempo chains true, // include staked ); }, diff --git a/packages/assets-controllers/src/selectors/token-selectors.ts b/packages/assets-controllers/src/selectors/token-selectors.ts index dd943d26668..3835e6250f4 100644 --- a/packages/assets-controllers/src/selectors/token-selectors.ts +++ b/packages/assets-controllers/src/selectors/token-selectors.ts @@ -14,6 +14,7 @@ import { parseBalanceWithDecimals, stringifyBalanceWithDecimals, } from './stringify-balance'; +import { shouldIncludeNativeToken } from '../constants'; import type { CurrencyRateState } from '../CurrencyRateController'; import type { MultichainAssetsControllerState } from '../MultichainAssetsController'; import type { MultichainAssetsRatesControllerState } from '../MultichainAssetsRatesController'; @@ -44,31 +45,19 @@ export const TRON_RESOURCE_SYMBOLS_SET: ReadonlySet = new Set(TRON_RESOURCE_SYMBOLS); /** - * Determines if a native token should be hidden on Tempo networks. + * Determines if a native token should be hidden. + * Returns true if the token is native and should be excluded for the given chain. * - * @param chainId - The hex chain ID (e.g., "0xa5bd"). + * @param chainId - The chain ID in hex format (e.g., "0xa5bf") or CAIP-2 format (e.g., "eip155:42431"). * @param isNative - Whether the token is a native token. - * @param symbol - The token symbol. * @returns True if the token should be hidden, false otherwise. */ function shouldHideNativeToken( chainId: Hex | string, isNative: boolean, - symbol?: string, ): boolean { - const TEMPO_CHAIN_IDS = ['0xa5bf', '0x1079']; // Tempo Testnet (42431) and Mainnet (4217) - - // Check if it's a Tempo network - if (!TEMPO_CHAIN_IDS.includes(chainId.toLowerCase())) { - return false; - } - - // Hide native tokens on Tempo - if (isNative) { - return true; - } - - return false; + // Only hide native tokens on chains that should skip native token fetching + return isNative && !shouldIncludeNativeToken(chainId); } export type AssetsByAccountGroup = { @@ -419,17 +408,7 @@ const selectAllMultichainAssets = createAssetListSelector( // Skip native tokens on Tempo networks const isNative = caipAsset.assetNamespace === 'slip44'; - // Convert CAIP-2 chain ID to hex for comparison - const chainIdHex = chainId.startsWith('eip155:') - ? `0x${parseInt(chainId.split(':')[1], 10).toString(16)}` - : chainId; - if ( - shouldHideNativeToken( - chainIdHex, - isNative, - assetMetadata.symbol, - ) - ) { + if (shouldHideNativeToken(chainId, isNative)) { continue; } From 9cb836a40a6198484a46c1c7e86903cda5cdbbc2 Mon Sep 17 00:00:00 2001 From: Florin Dzeladini Date: Fri, 20 Feb 2026 09:44:59 +0100 Subject: [PATCH 06/10] chore: fix changelog --- packages/assets-controller/CHANGELOG.md | 1 - packages/assets-controllers/CHANGELOG.md | 4 ---- 2 files changed, 5 deletions(-) diff --git a/packages/assets-controller/CHANGELOG.md b/packages/assets-controller/CHANGELOG.md index ea54a533336..a347e666de9 100644 --- a/packages/assets-controller/CHANGELOG.md +++ b/packages/assets-controller/CHANGELOG.md @@ -30,7 +30,6 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 - Refactor `AssetsControllerMessenger` type safety: remove `as unknown as` casts, import types instead of locally defining them, and add missing allowed actions/events ([#7952](https://github.com/MetaMask/core/pull/7952)) - **BREAKING:** `AccountsApiDataSourceConfig.tokenDetectionEnabled` is now a getter `() => boolean` (was `boolean`) so the Accounts API data source reacts when the user toggles token detection at runtime, consistent with `RpcDataSourceConfig.tokenDetectionEnabled`. Pass a function, e.g. `tokenDetectionEnabled: () => preferenceController.state.useTokenDetection`. - **BREAKING:** Rename state and `DataResponse` property from `assetsMetadata` to `assetsInfo`. Update consumers that read `state.assetsMetadata` or set `response.assetsMetadata` to use `assetsInfo` instead ([#7902](https://github.com/MetaMask/core/pull/7902)) -- Hide native tokens on Tempo networks (testnet and mainnet) in `getAssets` method ([#7882](https://github.com/MetaMask/core/pull/7882)) - Bump `@metamask/keyring-api` from `^21.0.0` to `^21.5.0` ([#7857](https://github.com/MetaMask/core/pull/7857)) - Bump `@metamask/keyring-internal-api` from `^9.0.0` to `^10.0.0` ([#7857](https://github.com/MetaMask/core/pull/7857)) - Bump `@metamask/keyring-snap-client` from `^8.0.0` to `^8.2.0` ([#7857](https://github.com/MetaMask/core/pull/7857)) diff --git a/packages/assets-controllers/CHANGELOG.md b/packages/assets-controllers/CHANGELOG.md index 411a0a7f587..179bc327d2c 100644 --- a/packages/assets-controllers/CHANGELOG.md +++ b/packages/assets-controllers/CHANGELOG.md @@ -33,10 +33,6 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 - Changed Plasma native token ([#7939](https://github.com/MetaMask/core/pull/7939)) - `searchTokens` now returns an optional `error` field when requests fail, allowing consumers to detect and handle search failures instead of silently receiving empty results ([#7938](https://github.com/MetaMask/core/pull/7938)) -### Changed - -- Hide native tokens on Tempo networks (testnet and mainnet) in asset selectors ([#7882](https://github.com/MetaMask/core/pull/7882)) - ## [99.3.2] ### Changed From 41cf37d13764d9be64559866781573a7fe8c7665 Mon Sep 17 00:00:00 2001 From: Florin Dzeladini Date: Fri, 20 Feb 2026 09:52:46 +0100 Subject: [PATCH 07/10] chore: fix changelog --- packages/assets-controllers/CHANGELOG.md | 2 -- 1 file changed, 2 deletions(-) diff --git a/packages/assets-controllers/CHANGELOG.md b/packages/assets-controllers/CHANGELOG.md index 9ef30f026fb..d0fc56e7412 100644 --- a/packages/assets-controllers/CHANGELOG.md +++ b/packages/assets-controllers/CHANGELOG.md @@ -10,8 +10,6 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ### Changed - Hide native tokens on Tempo networks (testnet and mainnet) in asset selectors ([#7882](https://github.com/MetaMask/core/pull/7882)) -- Bump `@metamask/phishing-controller` from `^16.2.0` to `^16.3.0` ([#7979](https://github.com/MetaMask/core/pull/7979)) -- Bump `@metamask/network-enablement-controller` from `^4.1.0` to `^4.1.1` ([#7984](https://github.com/MetaMask/core/pull/7984)) - Blockaid token filtering in `MultichainAssetsController` now only removes tokens flagged as `Malicious` ([#8003](https://github.com/MetaMask/core/pull/8003)) - `Spam`, `Warning`, and `Benign` tokens are no longer filtered out From bd4ad958bccba36ed345f3339d2022b2a01fa974 Mon Sep 17 00:00:00 2001 From: Florin Dzeladini Date: Fri, 20 Feb 2026 10:20:51 +0100 Subject: [PATCH 08/10] chore: lint --- .../src/AssetsController.test.ts | 9 +++++---- .../assets-controller/src/AssetsController.ts | 3 +-- .../src/AccountTrackerController.ts | 19 +++++++++---------- 3 files changed, 15 insertions(+), 16 deletions(-) diff --git a/packages/assets-controller/src/AssetsController.test.ts b/packages/assets-controller/src/AssetsController.test.ts index caa36d7f34d..6d77f240582 100644 --- a/packages/assets-controller/src/AssetsController.test.ts +++ b/packages/assets-controller/src/AssetsController.test.ts @@ -594,10 +594,11 @@ describe('AssetsController', () => { amount: '1', unit: 'ETH', }, - 'eip155:42431/erc20:0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48': { - amount: '100', - unit: 'USDC', - }, + 'eip155:42431/erc20:0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48': + { + amount: '100', + unit: 'USDC', + }, }, }, assetsPrice: {}, diff --git a/packages/assets-controller/src/AssetsController.ts b/packages/assets-controller/src/AssetsController.ts index 2ce304fa056..403185187bc 100644 --- a/packages/assets-controller/src/AssetsController.ts +++ b/packages/assets-controller/src/AssetsController.ts @@ -1473,8 +1473,7 @@ export class AssetsController extends BaseController< } // Check if it's a native token (either by metadata type or assetId format) - const isNative = - metadata.type === 'native' || assetId.includes('/slip44:'); + const isNative = metadata.type === 'native' || assetId.includes('/slip44:'); return isNative; } diff --git a/packages/assets-controllers/src/AccountTrackerController.ts b/packages/assets-controllers/src/AccountTrackerController.ts index 47864d1179c..1da2b0866b3 100644 --- a/packages/assets-controllers/src/AccountTrackerController.ts +++ b/packages/assets-controllers/src/AccountTrackerController.ts @@ -40,13 +40,12 @@ import type { Hex } from '@metamask/utils'; import { Mutex } from 'async-mutex'; import { cloneDeep, isEqual } from 'lodash'; -import { shouldIncludeNativeToken } from './constants'; - import { STAKING_CONTRACT_ADDRESS_BY_CHAINID } from './AssetsContractController'; import type { AssetsContractController, StakedBalance, } from './AssetsContractController'; +import { shouldIncludeNativeToken } from './constants'; import { AccountsApiBalanceFetcher } from './multi-chain-accounts-service/api-balance-fetcher'; import type { BalanceFetcher, @@ -857,18 +856,18 @@ export class AccountTrackerController extends StaticIntervalPollingController { - acc[address] = { balance: '0x0' }; - return acc; - }, - {} as Record, - ); + return addresses.reduce< + Record + >((acc, address) => { + acc[address] = { balance: '0x0' }; + return acc; + }, {}); } // TODO: This should use multicall when enabled by the user. From 722b53795f544a2e288f5f45a1bc3d6422205c21 Mon Sep 17 00:00:00 2001 From: Florin Dzeladini Date: Fri, 20 Feb 2026 10:31:53 +0100 Subject: [PATCH 09/10] chore: fix changelog --- packages/assets-controller/CHANGELOG.md | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/packages/assets-controller/CHANGELOG.md b/packages/assets-controller/CHANGELOG.md index e009fb23354..9d771f3f105 100644 --- a/packages/assets-controller/CHANGELOG.md +++ b/packages/assets-controller/CHANGELOG.md @@ -7,6 +7,10 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ## [Unreleased] +### Changed + +- Hide native tokens on Tempo networks (testnet and mainnet) in `getAssets` method ([#7882](https://github.com/MetaMask/core/pull/7882)) + ## [2.0.2] ### Changed @@ -21,7 +25,6 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ### Changed -- Hide native tokens on Tempo networks (testnet and mainnet) in `getAssets` method ([#7882](https://github.com/MetaMask/core/pull/7882)) - Refactor data source tests to use shared `MockAssetControllerMessenger` fixture ([#7958](https://github.com/MetaMask/core/pull/7958)) - Export `STAKING_INTERFACE` from the staked balance fetcher for use with the staking contract ABI. - `StakedBalanceDataSource` teardown now uses the messenger's `clearEventSubscriptions`; custom messenger implementations must support it for correct cleanup. From 44c7a9283e462b45d0cb5c87dfb2285cb5145a41 Mon Sep 17 00:00:00 2001 From: Florin Dzeladini Date: Fri, 20 Feb 2026 11:19:19 +0100 Subject: [PATCH 10/10] chore: unit tests update --- packages/assets-controller/src/AssetsController.test.ts | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/packages/assets-controller/src/AssetsController.test.ts b/packages/assets-controller/src/AssetsController.test.ts index 6d77f240582..2f2e114ab10 100644 --- a/packages/assets-controller/src/AssetsController.test.ts +++ b/packages/assets-controller/src/AssetsController.test.ts @@ -574,7 +574,7 @@ describe('AssetsController', () => { await withController( { state: { - assetsMetadata: { + assetsInfo: { 'eip155:42431/slip44:60': { type: 'native', symbol: 'ETH', @@ -631,7 +631,7 @@ describe('AssetsController', () => { await withController( { state: { - assetsMetadata: { + assetsInfo: { 'eip155:4217/slip44:60': { type: 'native', symbol: 'ETH', @@ -670,7 +670,7 @@ describe('AssetsController', () => { await withController( { state: { - assetsMetadata: { + assetsInfo: { 'eip155:1/slip44:60': { type: 'native', symbol: 'ETH', @@ -710,7 +710,7 @@ describe('AssetsController', () => { await withController( { state: { - assetsMetadata: { + assetsInfo: { 'eip155:42431/some:other': { type: 'native', symbol: 'ETH',