From 734dfcf8d64f6f36c53f28133135fc4852a5af47 Mon Sep 17 00:00:00 2001 From: Maxime OUAIRY Date: Fri, 3 Jul 2026 16:37:36 +0200 Subject: [PATCH 1/3] feat: assets-controllers extract and export getAssetId --- packages/assets-controllers/src/index.ts | 1 + .../src/token-prices-service/codefi-v2.ts | 74 +++++++++++++------ .../src/token-prices-service/index.test.ts | 1 + .../src/token-prices-service/index.ts | 1 + 4 files changed, 55 insertions(+), 22 deletions(-) diff --git a/packages/assets-controllers/src/index.ts b/packages/assets-controllers/src/index.ts index 70cadfb015..a005d08481 100644 --- a/packages/assets-controllers/src/index.ts +++ b/packages/assets-controllers/src/index.ts @@ -176,6 +176,7 @@ export { SUPPORTED_CHAIN_IDS, getNativeTokenAddress, SPOT_PRICES_SUPPORT_INFO, + getAssetId, } from './token-prices-service'; export { fetchRwas, diff --git a/packages/assets-controllers/src/token-prices-service/codefi-v2.ts b/packages/assets-controllers/src/token-prices-service/codefi-v2.ts index 712f92011c..b0f6238af4 100644 --- a/packages/assets-controllers/src/token-prices-service/codefi-v2.ts +++ b/packages/assets-controllers/src/token-prices-service/codefi-v2.ts @@ -228,7 +228,6 @@ export const ZERO_ADDRESS: Hex = */ const chainIdToNativeTokenAddress: Record = { '0x89': '0x0000000000000000000000000000000000001010', // Polygon - '0x1e': '0x542fda317318ebf1d3deaf76e0b632741a7e677d', // Rootstock Mainnet - Native symbol: RBTC '0x64': '0xe91d153e0b41518a2ce8dd3d7944fa863463a97d', // Gnosis '0x3dc': '0x779ded0c9e1022225f8e0630b35a9b54be713736', // Stable - Native symbol: USDT0 '0x440': '0xdeaddeaddeaddeaddeaddeaddeaddeaddead0000', // Metis Andromeda @@ -571,6 +570,52 @@ export function resetSupportedCurrenciesCache(): void { lastFetchedCurrencies = null; } +/** + * Derives the CAIP-19 asset ID used to query the Price API for a token on a + * given chain. + * + * For native tokens, uses the hardcoded {@link SPOT_PRICES_SUPPORT_INFO} entry + * when defined, otherwise falls back to the provided native asset identifiers + * (sourced from NetworkEnablementController). For ERC20 tokens, constructs the + * CAIP-19 ID dynamically. + * + * @param args - The arguments to this function. + * @param args.chainId - The hexadecimal chain ID the token lives on. + * @param args.tokenAddress - The token's address. + * @param args.nativeAssetIdentifiers - Map of CAIP-2 chain IDs to native asset + * identifiers, used as a fallback for native tokens. + * @returns The CAIP-19 asset ID, or undefined if it cannot be determined. + */ +export function getAssetId({ + chainId, + tokenAddress, + nativeAssetIdentifiers, +}: { + chainId: Hex; + tokenAddress: string; + nativeAssetIdentifiers: NativeAssetIdentifiersMap; +}): CaipAssetType | undefined { + const caipChainId = toCaipChainId( + KnownCaipNamespace.Eip155, + hexToNumber(chainId).toString(), + ); + + const nativeAddress = getNativeTokenAddress(chainId); + const isNativeToken = + nativeAddress.toLowerCase() === tokenAddress.toLowerCase(); + + if (isNativeToken) { + const hardcodedId = ( + SPOT_PRICES_SUPPORT_INFO as Partial> + )[chainId]; + return (hardcodedId ?? nativeAssetIdentifiers[caipChainId]) as + | CaipAssetType + | undefined; + } + + return `${caipChainId}/erc20:${tokenAddress.toLowerCase()}` as CaipAssetType; +} + /** * This version of the token prices service uses V2 of the Codefi Price API to * fetch token prices. @@ -744,26 +789,11 @@ export class CodefiTokenPricesServiceV2 implements AbstractTokenPricesService< // Filter out assets that are not supported by V3 of the Price API. .filter((asset) => supportedChainIdsV3.includes(asset.chainId)) .map((asset) => { - const caipChainId = toCaipChainId( - KnownCaipNamespace.Eip155, - hexToNumber(asset.chainId).toString(), - ); - - const nativeAddress = getNativeTokenAddress(asset.chainId); - const isNativeToken = - nativeAddress.toLowerCase() === asset.tokenAddress.toLowerCase(); - - let assetId: string | undefined; - if (isNativeToken) { - // For native tokens, use hardcoded SPOT_PRICES_SUPPORT_INFO when defined, - // otherwise use nativeAssetIdentifiers from NetworkEnablementController by default. - assetId = - SPOT_PRICES_SUPPORT_INFO[asset.chainId] ?? - this.#nativeAssetIdentifiers[caipChainId]; - } else { - // For ERC20 tokens, construct the CAIP-19 ID dynamically - assetId = `${caipChainId}/erc20:${asset.tokenAddress.toLowerCase()}`; - } + const assetId = getAssetId({ + chainId: asset.chainId, + tokenAddress: asset.tokenAddress, + nativeAssetIdentifiers: this.#nativeAssetIdentifiers, + }); if (!assetId) { return undefined; @@ -771,7 +801,7 @@ export class CodefiTokenPricesServiceV2 implements AbstractTokenPricesService< return { ...asset, - assetId: assetId as CaipAssetType, + assetId, }; }) .filter( diff --git a/packages/assets-controllers/src/token-prices-service/index.test.ts b/packages/assets-controllers/src/token-prices-service/index.test.ts index d67c48e53f..687fe8b673 100644 --- a/packages/assets-controllers/src/token-prices-service/index.test.ts +++ b/packages/assets-controllers/src/token-prices-service/index.test.ts @@ -11,6 +11,7 @@ describe('token-prices-service', () => { "getSupportedNetworks", "resetSupportedNetworksCache", "SPOT_PRICES_SUPPORT_INFO", + "getAssetId", ] `); }); diff --git a/packages/assets-controllers/src/token-prices-service/index.ts b/packages/assets-controllers/src/token-prices-service/index.ts index 3d32fe3618..da2ee6adff 100644 --- a/packages/assets-controllers/src/token-prices-service/index.ts +++ b/packages/assets-controllers/src/token-prices-service/index.ts @@ -10,4 +10,5 @@ export { getSupportedNetworks, resetSupportedNetworksCache, SPOT_PRICES_SUPPORT_INFO, + getAssetId, } from './codefi-v2'; From c6bc2893eeea41657e24666e4bcccb9d77546e6f Mon Sep 17 00:00:00 2001 From: Maxime OUAIRY Date: Mon, 6 Jul 2026 09:24:47 +0200 Subject: [PATCH 2/3] feat: assets-controllers polish getAssetId and unit tests --- packages/assets-controllers/CHANGELOG.md | 4 ++ .../token-prices-service/codefi-v2.test.ts | 69 +++++++++++++++++++ .../src/token-prices-service/codefi-v2.ts | 43 +++++++----- 3 files changed, 97 insertions(+), 19 deletions(-) diff --git a/packages/assets-controllers/CHANGELOG.md b/packages/assets-controllers/CHANGELOG.md index c800175a48..37a6baa88d 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] +### Added + +- Add exported `getAssetId` in `codefi-v2.ts` for CAIP-19 asset ID derivation - extracted from existing `fetchTokenPrices` ([#9386](https://github.com/MetaMask/core/pull/9386)) + ## [109.3.0] ### Added diff --git a/packages/assets-controllers/src/token-prices-service/codefi-v2.test.ts b/packages/assets-controllers/src/token-prices-service/codefi-v2.test.ts index ad25d83a78..47aec3556f 100644 --- a/packages/assets-controllers/src/token-prices-service/codefi-v2.test.ts +++ b/packages/assets-controllers/src/token-prices-service/codefi-v2.test.ts @@ -15,6 +15,7 @@ import { fetchSupportedNetworks, getSupportedNetworks, resetSupportedNetworksCache, + getAssetId, } from './codefi-v2'; // We're not customizing the default max delay @@ -2183,6 +2184,74 @@ describe('CodefiTokenPricesServiceV2', () => { expect(service.validateChainIdSupported('0xdeadbeef')).toBe(false); }); }); + + describe('getAssetId', () => { + it('returns a CAIP-19 erc20 id with a lowercased address for ERC20 tokens', () => { + expect(getAssetId({ chainId: '0x1', tokenAddress: '0xABCDEF' })).toBe( + 'eip155:1/erc20:0xabcdef', + ); + }); + + it('returns the hardcoded id for native tokens on supported chains', () => { + expect(getAssetId({ chainId: '0x1', tokenAddress: ZERO_ADDRESS })).toBe( + 'eip155:1/slip44:60', + ); + }); + + it('detects native tokens on chains with a custom native address', () => { + expect( + getAssetId({ + chainId: '0x89', + tokenAddress: '0x0000000000000000000000000000000000001010', + }), + ).toBe('eip155:137/slip44:966'); + }); + + it('matches native token addresses case-insensitively', () => { + expect( + getAssetId({ + chainId: '0x89', + tokenAddress: '0x0000000000000000000000000000000000001010' + .toUpperCase() + .replace('0X', '0x'), + }), + ).toBe('eip155:137/slip44:966'); + }); + + it('falls back to nativeAssetIdentifiers when the chain has no hardcoded entry', () => { + // 0x42 (OKXChain) is not in SPOT_PRICES_SUPPORT_INFO + expect( + getAssetId({ + chainId: '0x42', + tokenAddress: ZERO_ADDRESS, + nativeAssetIdentifiers: { 'eip155:66': 'eip155:66/slip44:996' }, + }), + ).toBe('eip155:66/slip44:996'); + }); + + it('prefers the hardcoded entry over nativeAssetIdentifiers', () => { + expect( + getAssetId({ + chainId: '0x1', + tokenAddress: ZERO_ADDRESS, + nativeAssetIdentifiers: { 'eip155:1': 'eip155:1/erc20:0xwrong' }, + }), + ).toBe('eip155:1/slip44:60'); + }); + + it('returns undefined for a native token with no hardcoded entry and no identifier', () => { + expect( + getAssetId({ chainId: '0x42', tokenAddress: ZERO_ADDRESS }), + ).toBeUndefined(); + }); + + it('returns undefined instead of throwing when given a malformed chain ID', () => { + expect( + // @ts-expect-error Testing runtime behavior with invalid input + getAssetId({ chainId: 'not-a-hex-string', tokenAddress: '0xAAA' }), + ).toBeUndefined(); + }); + }); }); /** diff --git a/packages/assets-controllers/src/token-prices-service/codefi-v2.ts b/packages/assets-controllers/src/token-prices-service/codefi-v2.ts index b0f6238af4..82178e1970 100644 --- a/packages/assets-controllers/src/token-prices-service/codefi-v2.ts +++ b/packages/assets-controllers/src/token-prices-service/codefi-v2.ts @@ -589,31 +589,36 @@ export function resetSupportedCurrenciesCache(): void { export function getAssetId({ chainId, tokenAddress, - nativeAssetIdentifiers, + nativeAssetIdentifiers = {}, }: { chainId: Hex; tokenAddress: string; - nativeAssetIdentifiers: NativeAssetIdentifiersMap; + nativeAssetIdentifiers?: NativeAssetIdentifiersMap; }): CaipAssetType | undefined { - const caipChainId = toCaipChainId( - KnownCaipNamespace.Eip155, - hexToNumber(chainId).toString(), - ); + try { + const caipChainId = toCaipChainId( + KnownCaipNamespace.Eip155, + hexToNumber(chainId).toString(), + ); - const nativeAddress = getNativeTokenAddress(chainId); - const isNativeToken = - nativeAddress.toLowerCase() === tokenAddress.toLowerCase(); - - if (isNativeToken) { - const hardcodedId = ( - SPOT_PRICES_SUPPORT_INFO as Partial> - )[chainId]; - return (hardcodedId ?? nativeAssetIdentifiers[caipChainId]) as - | CaipAssetType - | undefined; - } + const nativeAddress = getNativeTokenAddress(chainId); + const isNativeToken = + nativeAddress.toLowerCase() === tokenAddress.toLowerCase(); + + if (isNativeToken) { + const hardcodedId = ( + SPOT_PRICES_SUPPORT_INFO as Partial> + )[chainId]; + return (hardcodedId ?? nativeAssetIdentifiers[caipChainId]) as + | CaipAssetType + | undefined; + } - return `${caipChainId}/erc20:${tokenAddress.toLowerCase()}` as CaipAssetType; + return `${caipChainId}/erc20:${tokenAddress.toLowerCase()}` as CaipAssetType; + } catch { + // This block should never be reached as long as using Typescript, but added for safety. + return undefined; + } } /** From 87a315e03dd77e60487328eaabf691095ca0d224 Mon Sep 17 00:00:00 2001 From: Maxime OUAIRY Date: Mon, 6 Jul 2026 15:52:28 +0200 Subject: [PATCH 3/3] feat: change native asset Stable and Gnosis --- packages/assets-controllers/CHANGELOG.md | 4 ++++ .../src/token-prices-service/codefi-v2.ts | 6 ++---- 2 files changed, 6 insertions(+), 4 deletions(-) diff --git a/packages/assets-controllers/CHANGELOG.md b/packages/assets-controllers/CHANGELOG.md index 37a6baa88d..fec5c2d488 100644 --- a/packages/assets-controllers/CHANGELOG.md +++ b/packages/assets-controllers/CHANGELOG.md @@ -11,6 +11,10 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 - Add exported `getAssetId` in `codefi-v2.ts` for CAIP-19 asset ID derivation - extracted from existing `fetchTokenPrices` ([#9386](https://github.com/MetaMask/core/pull/9386)) +### Changed + +- Modified native asset addresses for Stable (`988`) and Gnosis (`100`) in `codefi-v2.ts` ([#9386](https://github.com/MetaMask/core/pull/9386)) + ## [109.3.0] ### Added diff --git a/packages/assets-controllers/src/token-prices-service/codefi-v2.ts b/packages/assets-controllers/src/token-prices-service/codefi-v2.ts index 82178e1970..b2a1c2d0f1 100644 --- a/packages/assets-controllers/src/token-prices-service/codefi-v2.ts +++ b/packages/assets-controllers/src/token-prices-service/codefi-v2.ts @@ -228,8 +228,6 @@ export const ZERO_ADDRESS: Hex = */ const chainIdToNativeTokenAddress: Record = { '0x89': '0x0000000000000000000000000000000000001010', // Polygon - '0x64': '0xe91d153e0b41518a2ce8dd3d7944fa863463a97d', // Gnosis - '0x3dc': '0x779ded0c9e1022225f8e0630b35a9b54be713736', // Stable - Native symbol: USDT0 '0x440': '0xdeaddeaddeaddeaddeaddeaddeaddeaddead0000', // Metis Andromeda '0x1388': '0xdeaddeaddeaddeaddeaddeaddeaddeaddead0000', // Mantle }; @@ -261,7 +259,7 @@ export const SPOT_PRICES_SUPPORT_INFO = { '0x39': 'eip155:57/slip44:57', // Syscoin Mainnet - Native symbol: SYS '0x52': 'eip155:82/slip44:18000', // Meter Mainnet - Native symbol: MTR '0x58': 'eip155:88/slip44:889', // TomoChain - Native symbol: TOMO - '0x64': 'eip155:100/slip44:700', // Gnosis (formerly xDAI Chain) - Native symbol: xDAI + '0x64': 'eip155:100/erc20:0x0000000000000000000000000000000000000000', // Gnosis (formerly xDAI Chain) - Native symbol: xDAI '0x6a': 'eip155:106/slip44:5655640', // Velas EVM Mainnet - Native symbol: VLX '0x7a': 'eip155:122/erc20:0x0000000000000000000000000000000000000000', // Fuse Mainnet - Native symbol: FUSE '0x80': 'eip155:128/slip44:1010', // Huobi ECO Chain Mainnet - Native symbol: HT @@ -278,7 +276,7 @@ export const SPOT_PRICES_SUPPORT_INFO = { '0x150': 'eip155:336/slip44:809', // Shiden - Native symbol: SDN '0x169': 'eip155:361/slip44:589', // Theta Mainnet - Native symbol: TFUEL '0x2eb': 'eip155:747/slip44:539', // Flow evm - Native symbol: Flow - '0x3dc': 'eip155:988/erc20:0x779ded0c9e1022225f8e0630b35a9b54be713736', // Stable - Native symbol: USDT0 + '0x3dc': 'eip155:988/erc20:0x0000000000000000000000000000000000000000', // Stable - Native symbol: USDT0 '0x3e7': 'eip155:999/slip44:2457', // HyperEVM - Native symbol: HYPE '0x440': 'eip155:1088/erc20:0xdeaddeaddeaddeaddeaddeaddeaddeaddead0000', // Metis Andromeda Mainnet (Ethereum L2) - Native symbol: METIS '0x44d': 'eip155:1101/slip44:60', // Polygon zkEVM mainnet - Native symbol: ETH