11import {
2+ ChainId ,
23 createServicePolicy ,
34 DEFAULT_CIRCUIT_BREAK_DURATION ,
45 DEFAULT_DEGRADED_THRESHOLD ,
@@ -7,15 +8,29 @@ import {
78 handleFetch ,
89} from '@metamask/controller-utils' ;
910import type { ServicePolicy } from '@metamask/controller-utils' ;
10- import type { Hex } from '@metamask/utils' ;
11- import { hexToNumber } from '@metamask/utils' ;
11+ import type {
12+ CaipAssetReference ,
13+ CaipAssetType ,
14+ CaipChainId ,
15+ Hex ,
16+ } from '@metamask/utils' ;
17+ import {
18+ hexToNumber ,
19+ isCaipAssetId ,
20+ isCaipAssetType ,
21+ isHexString ,
22+ isStrictHexString ,
23+ KnownCaipNamespace ,
24+ toCaipChainId ,
25+ } from '@metamask/utils' ;
1226
1327import type {
1428 AbstractTokenPricesService ,
1529 ExchangeRatesByCurrency ,
1630 TokenPrice ,
1731 TokenPricesByTokenAddress ,
1832} from './abstract-token-prices-service' ;
33+ import { accountAddressToCaipReference } from 'src/assetsUtil' ;
1934
2035/**
2136 * The list of currencies that can be supplied as the `vsCurrency` parameter to
@@ -174,6 +189,50 @@ const chainIdToNativeTokenAddress: Record<Hex, Hex> = {
174189export const getNativeTokenAddress = ( chainId : Hex ) : Hex =>
175190 chainIdToNativeTokenAddress [ chainId ] ?? ZERO_ADDRESS ;
176191
192+ // We can only support PricesAPI V3 for EVM chains that have a CAIP-19 native asset mapping.
193+ export const HEX_CHAIN_ID_TO_CAIP19_NATIVE_ASSET_MAP : Record <
194+ Hex ,
195+ CaipAssetType
196+ > = {
197+ '0x1' : 'eip155:1/slip44:60' , // Ethereum Mainnet
198+ '0xa' : 'eip155:10/slip44:60' , // OP Mainnet
199+ '0x19' : 'eip155:25/slip44:394' , // Cronos Mainnet
200+ '0x38' : 'eip155:56/slip44:714' , // BNB Smart Chain Mainnet
201+ '0x39' : 'eip155:57/slip44:57' , // Syscoin Mainnet
202+ // '0x42': 'eip155:1/slip44:60', // OKXChain Mainnet
203+ // '0x46': 'eip155:1/slip44:60', // Hoo Smart Chain
204+ // '0x52': 'eip155:1/slip44:60', // Meter Mainnet
205+ // '0x58': 'eip155:1/slip44:60', // TomoChain
206+ // '0x64': 'eip155:1/slip44:60', // Gnosis
207+ // '0x6a': 'eip155:1/slip44:60', // Velas EVM Mainnet
208+ // '0x7a': 'eip155:1/slip44:60', // Fuse Mainnet
209+ // '0x80': 'eip155:1/slip44:60', // Huobi ECO Chain Mainnet
210+ '0x89' : 'eip155:137/slip44:966' , // Polygon Mainnet
211+ '0x8f' : 'eip155:143/slip44:268435779' , // Monad Mainnet
212+ // '0x92': 'eip155:1/slip44:60', // Sonic Mainnet
213+ // '0xfa': 'eip155:1/slip44:60', // Fantom Opera
214+ // '0x120': 'eip155:1/slip44:60', // Boba Network
215+ // '0x141': 'eip155:1/slip44:60', // KCC Mainnet
216+ // '0x144': 'eip155:1/slip44:60', // zkSync Era Mainnet
217+ // '0x150': 'eip155:1/slip44:60', // Shiden
218+ // '0x169': 'eip155:1/slip44:60', // Theta Mainnet
219+ // '0x440': 'eip155:1/slip44:60', // Metis Andromeda Mainnet
220+ // '0x504': 'eip155:1/slip44:60', // Moonbeam
221+ // '0x505': 'eip155:1/slip44:60', // Moonriver
222+ '0x531' : 'eip155:1329/slip44:19000118' , // Sei Mainnet
223+ // '0x1388': 'eip155:1/slip44:60', // Mantle
224+ '0x2105' : 'eip155:8453/slip44:60' , // Base
225+ // '0x2710': 'eip155:1/slip44:60', // Smart Bitcoin Cash
226+ '0xa4b1' : 'eip155:42161/slip44:60' , // Arbitrum One
227+ // '0xa4ec': 'eip155:1/slip44:60', // Celo Mainnet
228+ // '0xa516': 'eip155:1/slip44:60', // Oasis Emerald
229+ '0xa86a' : 'eip155:43114/slip44:9000' , // Avalanche C-Chain
230+ '0xe708' : 'eip155:59144/slip44:60' , // Linea Mainnet
231+ // '0x518af': 'eip155:1/slip44:60', // Polis Mainnet
232+ // '0x4e454152': 'eip155:1/slip44:60', // Aurora Mainnet
233+ // '0x63564c40': 'eip155:1/slip44:60', // Harmony Mainnet Shard 0
234+ } ;
235+
177236/**
178237 * A currency that can be supplied as the `vsCurrency` parameter to
179238 * the `/spot-prices` endpoint. Covers both uppercase and lowercase versions.
@@ -273,13 +332,25 @@ export const SUPPORTED_CHAIN_IDS = [
273332 */
274333type SupportedChainId = ( typeof SUPPORTED_CHAIN_IDS ) [ number ] ;
275334
335+ const SLIP44_CHAIN_MAP : Record < string , CaipAssetReference > = {
336+ ETH : 'slip44:60' ,
337+ POL : 'slip44:966' ,
338+ BNB : 'slip44:714' ,
339+ AVAX : 'slip44:9000' ,
340+ TESTETH : 'slip44:60' ,
341+ SEI : 'slip44:19000118' ,
342+ MON : 'slip44:268435779' ,
343+ } ;
344+
276345/**
277346 * All requests to V2 of the Price API start with this.
278347 */
279348const BASE_URL = 'https://price.api.cx.metamask.io/v2' ;
280349
281350const BASE_URL_V1 = 'https://price.api.cx.metamask.io/v1' ;
282351
352+ const BASE_URL_V3 = 'https://price.api.cx.metamask.io/v3' ;
353+
283354/**
284355 * The shape of the data that the /spot-prices endpoint returns.
285356 */
@@ -534,6 +605,85 @@ export class CodefiTokenPricesServiceV2
534605 ) as Partial < TokenPricesByTokenAddress < Hex , SupportedCurrency > > ;
535606 }
536607
608+ async fetchTokenPricesV3 ( {
609+ assets,
610+ currency,
611+ } : {
612+ assets : (
613+ | { address : Hex ; chainId : Hex }
614+ | { address : CaipAssetType ; chainId : CaipChainId }
615+ ) [ ] ;
616+ currency : SupportedCurrency ;
617+ } ) {
618+ const assetsWithIds : ( { assetId : CaipAssetType } & (
619+ | { address : Hex ; chainId : Hex }
620+ | { address : CaipAssetType ; chainId : CaipChainId }
621+ ) ) [ ] = assets . map ( ( asset ) => {
622+ if ( isStrictHexString ( asset . address ) ) {
623+ const caipChainId = toCaipChainId (
624+ KnownCaipNamespace . Eip155 ,
625+ hexToNumber ( asset . chainId ) . toString ( ) ,
626+ ) ;
627+
628+ const nativeAsset =
629+ HEX_CHAIN_ID_TO_CAIP19_NATIVE_ASSET_MAP [ asset . chainId as Hex ] ;
630+
631+ return {
632+ assetId : nativeAsset ?? `${ caipChainId } /erc20:${ asset . address } ` ,
633+ address : asset . address ,
634+ chainId : asset . chainId as Hex ,
635+ } ;
636+ }
637+
638+ return {
639+ assetId : asset . address ,
640+ address : asset . address ,
641+ chainId : asset . chainId as CaipChainId ,
642+ } ;
643+ } ) ;
644+
645+ const url = new URL ( `${ BASE_URL_V3 } /spot-prices` ) ;
646+ url . searchParams . append (
647+ 'assetIds' ,
648+ assetsWithIds . map ( ( asset ) => asset . assetId ) . join ( ',' ) ,
649+ ) ;
650+ url . searchParams . append ( 'vsCurrency' , currency ) ;
651+ url . searchParams . append ( 'includeMarketData' , 'true' ) ;
652+
653+ const addressCryptoDataMap : { [ assetId : CaipAssetType ] : MarketData } =
654+ await this . #policy. execute ( ( ) =>
655+ handleFetch ( url , { headers : { 'Cache-Control' : 'no-cache' } } ) ,
656+ ) ;
657+
658+ return assetsWithIds
659+ . map ( ( assetWithId ) => {
660+ const marketData = addressCryptoDataMap [ assetWithId . assetId ] ;
661+
662+ if ( ! marketData ) {
663+ return undefined ;
664+ }
665+
666+ return {
667+ ...assetWithId ,
668+ currency,
669+ ...marketData ,
670+ } ;
671+ } )
672+ . filter ( Boolean ) as ( MarketData & {
673+ assetId : CaipAssetType ;
674+ currency : SupportedCurrency ;
675+ } & (
676+ | {
677+ address : Hex ;
678+ chainId : Hex ;
679+ }
680+ | {
681+ address : CaipAssetType ;
682+ chainId : CaipChainId ;
683+ }
684+ ) ) [ ] ;
685+ }
686+
537687 /**
538688 * Retrieves exchange rates in the given base currency.
539689 *
0 commit comments