From f529ec012c6c8ea9033b15e443614502fca8bfb7 Mon Sep 17 00:00:00 2001 From: Bernardo Garces Chapero Date: Thu, 5 Mar 2026 14:31:06 +0000 Subject: [PATCH 1/8] fetch both selectedcurrency and usd prices --- .../src/data-sources/PriceDataSource.ts | 96 ++++++++++++------- packages/assets-controller/src/types.ts | 21 ++-- 2 files changed, 70 insertions(+), 47 deletions(-) diff --git a/packages/assets-controller/src/data-sources/PriceDataSource.ts b/packages/assets-controller/src/data-sources/PriceDataSource.ts index c534815e981..0d1508b8595 100644 --- a/packages/assets-controller/src/data-sources/PriceDataSource.ts +++ b/packages/assets-controller/src/data-sources/PriceDataSource.ts @@ -15,6 +15,7 @@ import type { FungibleAssetPrice, Middleware, AssetsControllerStateInternal, + AssetPrice, } from '../types'; // ============================================================================ @@ -189,21 +190,11 @@ export class PriceDataSource { } try { - const priceResponse = await this.#fetchSpotPrices(priceableAssetIds); - - response.assetsPrice ??= {}; - - for (const [assetId, marketData] of Object.entries(priceResponse)) { - if (!isValidMarketData(marketData)) { - continue; - } - - const caipAssetId = assetId as Caip19AssetId; - response.assetsPrice[caipAssetId] = { - ...marketData, - lastUpdated: Date.now(), - }; - } + const spotPrices = await this.#fetchSpotPrices(priceableAssetIds); + response.assetsPrice = { + ...(response.assetsPrice ?? {}), + ...spotPrices, + }; } catch (error) { log('Failed to fetch prices via middleware', { error }); } @@ -223,11 +214,56 @@ export class PriceDataSource { * @param assetIds - Array of CAIP-19 asset IDs * @returns Spot prices response */ - async #fetchSpotPrices(assetIds: string[]): Promise { - return this.#apiClient.prices.fetchV3SpotPrices(assetIds, { - currency: this.#getSelectedCurrency(), - includeMarketData: true, - }); + async #fetchSpotPrices( + assetIds: string[], + ): Promise> { + const selectedCurrency = this.#getSelectedCurrency(); + + let selectedCurrencyPrices: V3SpotPricesResponse; + let usdPrices: V3SpotPricesResponse; + if (selectedCurrency === 'usd') { + selectedCurrencyPrices = await this.#apiClient.prices.fetchV3SpotPrices( + assetIds, + { + currency: selectedCurrency, + includeMarketData: true, + }, + ); + usdPrices = selectedCurrencyPrices; + } else { + [selectedCurrencyPrices, usdPrices] = await Promise.all([ + this.#apiClient.prices.fetchV3SpotPrices(assetIds, { + currency: selectedCurrency, + includeMarketData: true, + }), + this.#apiClient.prices.fetchV3SpotPrices(assetIds, { + currency: 'usd', + includeMarketData: true, + }), + ]); + } + + const prices: Record = {}; + + for (const [assetId, marketData] of Object.entries( + selectedCurrencyPrices, + )) { + const usdMarketData = usdPrices[assetId]; + + // Skip assets with invalid market data (API doesn't have price for this asset is selected currency or USD) + if (!isValidMarketData(marketData) || !isValidMarketData(usdMarketData)) { + continue; + } + + const caipAssetId = assetId as Caip19AssetId; + prices[caipAssetId] = { + ...marketData, + usdPrice: usdMarketData.price, + lastUpdated: Date.now(), + }; + } + + return prices; } /** @@ -330,22 +366,12 @@ export class PriceDataSource { } try { - const priceResponse = await this.#fetchSpotPrices([...assetIds]); + const spotPrices = await this.#fetchSpotPrices([...assetIds]); - response.assetsPrice = {}; - - for (const [assetId, marketData] of Object.entries(priceResponse)) { - // Skip assets with invalid market data (API doesn't have price for this asset) - if (!isValidMarketData(marketData)) { - continue; - } - - const caipAssetId = assetId as Caip19AssetId; - response.assetsPrice[caipAssetId] = { - ...marketData, - lastUpdated: Date.now(), - }; - } + response.assetsPrice = { + ...(response.assetsPrice ?? {}), + ...spotPrices, + }; } catch (error) { log('Failed to fetch prices', { error }); } diff --git a/packages/assets-controller/src/types.ts b/packages/assets-controller/src/types.ts index 2dbaa885032..d97ba51db9c 100644 --- a/packages/assets-controller/src/types.ts +++ b/packages/assets-controller/src/types.ts @@ -190,7 +190,7 @@ export type AssetMetadata = * Base price attributes. */ export type BaseAssetPrice = { - /** Current price in USD */ + /** Current price in selected currency */ price: number; /** Timestamp of last price update */ lastUpdated: number; @@ -200,11 +200,10 @@ export type BaseAssetPrice = { * Price data for fungible tokens (native, ERC20, SPL) * Matches V3SpotPricesResponse from the Price API. */ -export type FungibleAssetPrice = { +export type FungibleAssetPrice = BaseAssetPrice & { + assetPriceType: 'fungible'; /** CoinGecko ID */ id?: string; - /** Current price in USD */ - price: number; /** Market capitalization */ marketCap?: number; /** All-time high price */ @@ -239,14 +238,15 @@ export type FungibleAssetPrice = { pricePercentChange200d?: number; /** 1y price change percentage */ pricePercentChange1y?: number; - /** Timestamp of last price update (added by client) */ - lastUpdated: number; + /** Current price in USD */ + usdPrice: number; }; /** * Price data for NFT collections */ -export type NFTAssetPrice = { +export type NFTAssetPrice = BaseAssetPrice & { + assetPriceType: 'nft'; /** Floor price */ floorPrice?: number; /** Last sale price */ @@ -257,16 +257,13 @@ export type NFTAssetPrice = { averagePrice?: number; /** Number of sales in 24h */ sales24h?: number; -} & BaseAssetPrice; +}; /** * Union type representing all possible asset price types. * All types must be JSON-serializable. */ -export type AssetPrice = - | FungibleAssetPrice - | NFTAssetPrice - | (BaseAssetPrice & { [key: string]: Json }); +export type AssetPrice = FungibleAssetPrice | NFTAssetPrice; // ============================================================================ // BALANCE TYPES (vary by asset type) From 95ab8f481c82c9dbc3a78ef6f81200dcc25b884d Mon Sep 17 00:00:00 2001 From: Bernardo Garces Chapero Date: Thu, 5 Mar 2026 16:13:12 +0000 Subject: [PATCH 2/8] fix types --- .../src/data-sources/PriceDataSource.ts | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) diff --git a/packages/assets-controller/src/data-sources/PriceDataSource.ts b/packages/assets-controller/src/data-sources/PriceDataSource.ts index 0d1508b8595..f511d8188a2 100644 --- a/packages/assets-controller/src/data-sources/PriceDataSource.ts +++ b/packages/assets-controller/src/data-sources/PriceDataSource.ts @@ -15,7 +15,6 @@ import type { FungibleAssetPrice, Middleware, AssetsControllerStateInternal, - AssetPrice, } from '../types'; // ============================================================================ @@ -73,7 +72,10 @@ function isPriceableAsset(assetId: Caip19AssetId): boolean { } /** Market data item from spot prices response (same as FungibleAssetPrice without lastUpdated) */ -type SpotPriceMarketData = Omit; +type SpotPriceMarketData = Omit< + FungibleAssetPrice, + 'lastUpdated' | 'assetPriceType' +>; /** * Type guard to check if market data has a valid price @@ -216,7 +218,7 @@ export class PriceDataSource { */ async #fetchSpotPrices( assetIds: string[], - ): Promise> { + ): Promise> { const selectedCurrency = this.#getSelectedCurrency(); let selectedCurrencyPrices: V3SpotPricesResponse; @@ -243,7 +245,7 @@ export class PriceDataSource { ]); } - const prices: Record = {}; + const prices: Record = {}; for (const [assetId, marketData] of Object.entries( selectedCurrencyPrices, @@ -258,6 +260,7 @@ export class PriceDataSource { const caipAssetId = assetId as Caip19AssetId; prices[caipAssetId] = { ...marketData, + assetPriceType: 'fungible', usdPrice: usdMarketData.price, lastUpdated: Date.now(), }; From 883a837dfddaafe0edc117fdc6a32fc7528d34ed Mon Sep 17 00:00:00 2001 From: Bernardo Garces Chapero Date: Mon, 9 Mar 2026 09:56:10 +0000 Subject: [PATCH 3/8] use usdPrice --- packages/assets-controller/CHANGELOG.md | 2 +- .../AssetsController-method-action-types.ts | 4 - .../src/AssetsController.test.ts | 34 +++-- .../assets-controller/src/AssetsController.ts | 14 +- .../formatExchangeRatesForBridge.test.ts | 40 +++--- .../src/utils/formatExchangeRatesForBridge.ts | 122 ++++++++---------- .../formatStateForTransactionPay.test.ts | 26 ++-- .../src/utils/formatStateForTransactionPay.ts | 17 +-- 8 files changed, 124 insertions(+), 135 deletions(-) diff --git a/packages/assets-controller/CHANGELOG.md b/packages/assets-controller/CHANGELOG.md index c7722bfa3f6..a69f02906bc 100644 --- a/packages/assets-controller/CHANGELOG.md +++ b/packages/assets-controller/CHANGELOG.md @@ -19,7 +19,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ### Fixed -- `getStateForTransactionPay()` and `formatStateForTransactionPay()` now accept optional `usdToSelectedCurrencyRate` (rate: 1 USD = N units of selected currency). When `selectedCurrency` is not USD, passing this rate ensures `currencyRates` and `marketData` use correct user-currency vs USD values; previously they incorrectly used USD price for both +- `formatExchangeRatesForBridge` and `formatStateForTransactionPay` now read both `price` (selected currency) and `usdPrice` (USD) directly from asset price data ([#]()) ## [2.2.0] diff --git a/packages/assets-controller/src/AssetsController-method-action-types.ts b/packages/assets-controller/src/AssetsController-method-action-types.ts index c7918f48073..fb600425209 100644 --- a/packages/assets-controller/src/AssetsController-method-action-types.ts +++ b/packages/assets-controller/src/AssetsController-method-action-types.ts @@ -32,8 +32,6 @@ export type AssetsControllerGetAssetsPriceAction = { * action instead of MultichainAssetsRatesController, TokenRatesController, * and CurrencyRateController. * - * @param options - Optional options for bridge rate conversion. - * @param options.usdToSelectedCurrencyRate - When selectedCurrency is not 'usd', pass 1 USD = this many units of selected currency so that currencyRates and conversionRates use correct user-currency vs USD values; otherwise the bridge USD conversion will be wrong for non-USD currencies. * @returns Bridge-compatible exchange rate state derived from assetsPrice and selectedCurrency. */ export type AssetsControllerGetExchangeRatesForBridgeAction = { @@ -48,8 +46,6 @@ export type AssetsControllerGetExchangeRatesForBridgeAction = { * useAssetsController is true the transaction-pay-controller can use a * single action instead of five separate getState calls. * - * @param options - Optional options for legacy rate conversion. - * @param options.usdToSelectedCurrencyRate - When selectedCurrency is not 'usd', pass 1 USD = this many units of selected currency so that currencyRates and marketData use correct user-currency vs USD values; otherwise conversion rates will be wrong for non-USD currencies. * @returns Legacy-compatible state for transaction-pay-controller. */ export type AssetsControllerGetStateForTransactionPayAction = { diff --git a/packages/assets-controller/src/AssetsController.test.ts b/packages/assets-controller/src/AssetsController.test.ts index 3ee9a856f85..5b03650fd54 100644 --- a/packages/assets-controller/src/AssetsController.test.ts +++ b/packages/assets-controller/src/AssetsController.test.ts @@ -642,7 +642,9 @@ describe('AssetsController', () => { const initialState: Partial = { assetsPrice: { [bitcoinAssetId]: { + assetPriceType: 'fungible', price: 50000, + usdPrice: 50000, lastUpdated: 1_600_000_000, }, }, @@ -674,45 +676,51 @@ describe('AssetsController', () => { }); }); - it('passes usdToSelectedCurrencyRate so currencyRates have distinct conversionRate and usdConversionRate', async () => { + it('reads price and usdPrice directly so currencyRates have distinct conversionRate and usdConversionRate', async () => { const ethNativeId = 'eip155:1/slip44:60' as Caip19AssetId; const initialState: Partial = { assetsPrice: { - [ethNativeId]: { price: 2000, lastUpdated: 1_700_000_000_000 }, + [ethNativeId]: { + assetPriceType: 'fungible', + price: 1840, + usdPrice: 2000, + lastUpdated: 1_700_000_000_000, + }, }, selectedCurrency: 'eur', }; await withController({ state: initialState }, ({ controller }) => { - const result = controller.getExchangeRatesForBridge({ - usdToSelectedCurrencyRate: 0.92, - }); + const result = controller.getExchangeRatesForBridge(); expect(result.currencyRates.ETH).toBeDefined(); - expect(result.currencyRates.ETH?.conversionRate).toBe(2000 * 0.92); + expect(result.currencyRates.ETH?.conversionRate).toBe(1840); expect(result.currencyRates.ETH?.usdConversionRate).toBe(2000); }); }); }); describe('getStateForTransactionPay', () => { - it('passes usdToSelectedCurrencyRate so currencyRates have distinct conversionRate and usdConversionRate', async () => { + it('reads price and usdPrice directly so currencyRates have distinct conversionRate and usdConversionRate', async () => { const ethNativeId = 'eip155:1/slip44:60' as Caip19AssetId; const initialState: Partial = { assetsPrice: { - [ethNativeId]: { price: 2000, lastUpdated: 1_700_000_000_000 }, + [ethNativeId]: { + assetPriceType: 'fungible', + price: 1840, + usdPrice: 2000, + lastUpdated: 1_700_000_000_000, + }, }, selectedCurrency: 'eur', }; await withController({ state: initialState }, ({ controller }) => { - const result = controller.getStateForTransactionPay({ - usdToSelectedCurrencyRate: 0.92, - }); + const result = controller.getStateForTransactionPay(); expect(result.currentCurrency).toBe('eur'); expect(result.currencyRates.ETH).toBeDefined(); - expect(result.currencyRates.ETH?.conversionRate).toBe(2000 * 0.92); + expect(result.currencyRates.ETH?.conversionRate).toBe(1840); expect(result.currencyRates.ETH?.usdConversionRate).toBe(2000); }); }); @@ -976,7 +984,9 @@ describe('AssetsController', () => { { assetsPrice: { [MOCK_ASSET_ID]: { + assetPriceType: 'fungible', price: 1.0, + usdPrice: 1.0, pricePercentChange1d: 0.5, lastUpdated: Date.now(), }, diff --git a/packages/assets-controller/src/AssetsController.ts b/packages/assets-controller/src/AssetsController.ts index dbce4103354..e6178116e4b 100644 --- a/packages/assets-controller/src/AssetsController.ts +++ b/packages/assets-controller/src/AssetsController.ts @@ -1138,13 +1138,9 @@ export class AssetsController extends BaseController< * action instead of MultichainAssetsRatesController, TokenRatesController, * and CurrencyRateController. * - * @param options - Optional options for bridge rate conversion. - * @param options.usdToSelectedCurrencyRate - When selectedCurrency is not 'usd', pass 1 USD = this many units of selected currency so that currencyRates and conversionRates use correct user-currency vs USD values; otherwise the bridge USD conversion will be wrong for non-USD currencies. * @returns Bridge-compatible exchange rate state derived from assetsPrice and selectedCurrency. */ - getExchangeRatesForBridge(options?: { - usdToSelectedCurrencyRate?: number; - }): BridgeExchangeRatesFormat { + getExchangeRatesForBridge(): BridgeExchangeRatesFormat { const { nativeAssetIdentifiers } = this.messenger.call( 'NetworkEnablementController:getState', ); @@ -1156,7 +1152,6 @@ export class AssetsController extends BaseController< selectedCurrency: this.state.selectedCurrency, nativeAssetIdentifiers, networkConfigurationsByChainId, - usdToSelectedCurrencyRate: options?.usdToSelectedCurrencyRate, }); } @@ -1167,13 +1162,9 @@ export class AssetsController extends BaseController< * useAssetsController is true the transaction-pay-controller can use a * single action instead of five separate getState calls. * - * @param options - Optional options for legacy rate conversion. - * @param options.usdToSelectedCurrencyRate - When selectedCurrency is not 'usd', pass 1 USD = this many units of selected currency so that currencyRates and marketData use correct user-currency vs USD values; otherwise conversion rates will be wrong for non-USD currencies. * @returns Legacy-compatible state for transaction-pay-controller. */ - getStateForTransactionPay(options?: { - usdToSelectedCurrencyRate?: number; - }): TransactionPayLegacyFormat { + getStateForTransactionPay(): TransactionPayLegacyFormat { const accounts = this.#selectedAccounts; const { nativeAssetIdentifiers } = this.messenger.call( 'NetworkEnablementController:getState', @@ -1189,7 +1180,6 @@ export class AssetsController extends BaseController< accounts: accounts.map((a) => ({ id: a.id, address: a.address })), nativeAssetIdentifiers, networkConfigurationsByChainId, - usdToSelectedCurrencyRate: options?.usdToSelectedCurrencyRate, }); } diff --git a/packages/assets-controller/src/utils/formatExchangeRatesForBridge.test.ts b/packages/assets-controller/src/utils/formatExchangeRatesForBridge.test.ts index 44408517d44..057daa9806c 100644 --- a/packages/assets-controller/src/utils/formatExchangeRatesForBridge.test.ts +++ b/packages/assets-controller/src/utils/formatExchangeRatesForBridge.test.ts @@ -1,14 +1,22 @@ import { formatExchangeRatesForBridge } from './formatExchangeRatesForBridge'; -import type { AssetPrice } from '../types'; +import type { FungibleAssetPrice } from '../types'; /** - * Builds minimal AssetPrice for tests (lastUpdated required by BaseAssetPrice). + * Builds minimal AssetPrice for tests. Defaults usdPrice to the same + * value as price (convenient for USD-denominated tests). * * @param overrides - Price fields; must include price. * @returns AssetPrice suitable for formatExchangeRatesForBridge input. */ -function price(overrides: Partial & { price: number }): AssetPrice { - return { lastUpdated: 0, ...overrides } as AssetPrice; +function price( + overrides: Partial & { price: number }, +): FungibleAssetPrice { + return { + assetPriceType: 'fungible', + lastUpdated: 0, + usdPrice: overrides.price, + ...overrides, + }; } /** Minimal nativeAssetIdentifiers for EVM tests (from NetworkEnablementController shape). */ @@ -222,14 +230,12 @@ describe('formatExchangeRatesForBridge', () => { expect(usdcEntry.low1d).toBe(0.99); }); - it('skips entries with invalid or negative price', () => { + it('skips entries with negative price', () => { const validId = 'bip122:000000000019d6689c085ae165831e93/slip44:0'; const result = formatExchangeRatesForBridge({ assetsPrice: { [validId]: price({ price: 100 }), 'eip155:1/slip44:60': price({ price: -1 }), - 'solana:5eykt4UsFv8P8NJdTREpY1vzqKqZKvdp/slip44:501': - {} as unknown as AssetPrice, }, selectedCurrency: 'usd', }); @@ -327,38 +333,38 @@ describe('formatExchangeRatesForBridge', () => { ); }); - it('uses usdToSelectedCurrencyRate for currencyRates when selectedCurrency is not usd', () => { + it('uses price for currencyRates conversionRate and usdPrice for usdConversionRate when selectedCurrency is not usd', () => { const ethNativeId = 'eip155:1/slip44:60'; const result = formatExchangeRatesForBridge({ assetsPrice: { - [ethNativeId]: price({ price: 2000, lastUpdated: 1_700_000_000_000 }), + [ethNativeId]: price({ + price: 1840, + usdPrice: 2000, + lastUpdated: 1_700_000_000_000, + }), }, selectedCurrency: 'eur', nativeAssetIdentifiers: { 'eip155:1': ethNativeId }, networkConfigurationsByChainId: EVM_NETWORK_CONFIGS, - usdToSelectedCurrencyRate: 0.92, }); expect(result.currencyRates.ETH).toStrictEqual({ conversionDate: 1_700_000_000, - conversionRate: 2000 * 0.92, + conversionRate: 1840, usdConversionRate: 2000, }); }); - it('uses usdToSelectedCurrencyRate for non-EVM conversionRates rate in selected currency', () => { + it('uses price directly for non-EVM conversionRates rate in selected currency', () => { const bitcoinAssetId = 'bip122:000000000019d6689c085ae165831e93/slip44:0'; const result = formatExchangeRatesForBridge({ assetsPrice: { - [bitcoinAssetId]: price({ price: 50000 }), + [bitcoinAssetId]: price({ price: 46000, usdPrice: 50000 }), }, selectedCurrency: 'eur', - usdToSelectedCurrencyRate: 0.92, }); - expect(result.conversionRates[bitcoinAssetId].rate).toBe( - String(50000 * 0.92), - ); + expect(result.conversionRates[bitcoinAssetId].rate).toBe(String(46000)); expect(result.conversionRates[bitcoinAssetId].currency).toBe( 'swift:0/iso4217:EUR', ); diff --git a/packages/assets-controller/src/utils/formatExchangeRatesForBridge.ts b/packages/assets-controller/src/utils/formatExchangeRatesForBridge.ts index f33241ce537..9b442b6172a 100644 --- a/packages/assets-controller/src/utils/formatExchangeRatesForBridge.ts +++ b/packages/assets-controller/src/utils/formatExchangeRatesForBridge.ts @@ -1,9 +1,9 @@ import { toChecksumAddress } from '@ethereumjs/util'; import { MAP_CAIP_CURRENCIES } from '@metamask/assets-controllers'; -import { numberToHex } from '@metamask/utils'; +import { KnownCaipNamespace, numberToHex } from '@metamask/utils'; import { parseCaipAssetType, parseCaipChainId } from '@metamask/utils'; -import type { AssetPrice, Caip19AssetId } from '../types'; +import type { AssetPrice, FungibleAssetPrice, Caip19AssetId } from '../types'; /** * Bridge-compatible conversion rate entry (MultichainAssetsRatesController shape). @@ -49,12 +49,6 @@ export type BridgeExchangeRatesFormat = { currentCurrency: string; }; -function getPriceNumber(price: AssetPrice): number { - return typeof price === 'object' && price !== null && 'price' in price - ? Number((price as { price: number }).price) - : Number.NaN; -} - /** * Converts AssetsController state (assetsPrice, selectedCurrency) into the * same format the bridge expects from MultichainAssetsRatesController, @@ -62,11 +56,10 @@ function getPriceNumber(price: AssetPrice): number { * a single action when useAssetsControllerForRates is true. * * @param params - Conversion parameters. - * @param params.assetsPrice - Map of CAIP-19 asset ID to price data. Prices are in USD. + * @param params.assetsPrice - Map of CAIP-19 asset ID to price data (must include both `price` and `usdPrice`). * @param params.selectedCurrency - ISO 4217 currency code (e.g. 'usd'). * @param params.nativeAssetIdentifiers - Optional map of CAIP-2 chain ID to native asset ID (e.g. from NetworkEnablementController state). When provided, used for EVM native lookups. * @param params.networkConfigurationsByChainId - Optional map of Hex chain ID to network config (e.g. from NetworkController state). Used to resolve native currency symbol via `nativeCurrency`; keys are Hex (e.g. '0x1'). - * @param params.usdToSelectedCurrencyRate - Optional rate: 1 USD = this many units of selected currency. Required for correct bridge USD conversion when selectedCurrency is not 'usd'. When omitted and selectedCurrency !== 'usd', currencyRates will use the same value for conversionRate and usdConversionRate (ratio 1), which produces incorrect USD rates. * @returns Bridge-compatible conversionRates, currencyRates, marketData, currentCurrency. */ export function formatExchangeRatesForBridge(params: { @@ -74,42 +67,40 @@ export function formatExchangeRatesForBridge(params: { selectedCurrency: string; nativeAssetIdentifiers?: Record; networkConfigurationsByChainId?: Record; - usdToSelectedCurrencyRate?: number; }): BridgeExchangeRatesFormat { const { assetsPrice, selectedCurrency, nativeAssetIdentifiers = {}, networkConfigurationsByChainId = {}, - usdToSelectedCurrencyRate, } = params; const conversionRates: Record = {}; const currencyRates: Record = {}; const marketData: Record> = {}; - // Same as MultichainAssetsRatesController: resolve CAIP currency from selectedCurrency, default to USD - const currencyCaip = - MAP_CAIP_CURRENCIES[selectedCurrency.toLowerCase()] ?? - MAP_CAIP_CURRENCIES.usd; + const currencyCaip = MAP_CAIP_CURRENCIES[selectedCurrency.toLowerCase()]; + + // TODO Protect against undefined by returning early + // TODO Check if this is a correct offset const expirationOffset = 60; - // Price in assetsPrice is in USD. For bridge: conversionRate = in user's currency, usdConversionRate = in USD. - const isUsd = selectedCurrency.toLowerCase() === 'usd'; - const rateUserCurrencyPerUsd = isUsd ? 1 : (usdToSelectedCurrencyRate ?? 1); + const fungibleAssetsPrice = Object.entries(assetsPrice).reduce< + Record + >((acc, [assetId, priceData]) => { + if (priceData.assetPriceType === 'fungible') { + acc[assetId as Caip19AssetId] = priceData; + } + return acc; + }, {}); - for (const [assetId, priceData] of Object.entries(assetsPrice)) { - const price = getPriceNumber(priceData); - if (Number.isNaN(price) || price < 0) { + for (const [assetId, priceData] of Object.entries(fungibleAssetsPrice)) { + const { price, usdPrice, lastUpdated } = priceData; + if (price < 0) { continue; } - const lastUpdated = - typeof priceData === 'object' && - priceData !== null && - 'lastUpdated' in priceData - ? Number((priceData as { lastUpdated: number }).lastUpdated) - : Date.now(); + // TODO Remove this branching, lastUpdated always has the same format const conversionTime = lastUpdated > 1e12 ? lastUpdated / 1000 : lastUpdated; const expirationTime = conversionTime + expirationOffset; @@ -117,34 +108,30 @@ export function formatExchangeRatesForBridge(params: { try { const parsed = parseCaipAssetType(assetId as Caip19AssetId); const chainIdParsed = parseCaipChainId(parsed.chainId); - const chainRef = chainIdParsed.reference; - // conversionRates: only non-EVM assets (bridge uses this for non-EVM chains). Rate in selected currency. - if (chainIdParsed.namespace !== 'eip155') { - const priceInUserCurrency = price * rateUserCurrencyPerUsd; - conversionRates[assetId] = { - rate: String(priceInUserCurrency), - currency: currencyCaip, - conversionTime, - expirationTime, - marketData: - typeof priceData === 'object' && priceData !== null - ? (priceData as Record) - : undefined, - }; - } + if (chainIdParsed.namespace === KnownCaipNamespace.Eip155) { + const chainIdHex = numberToHex(parseInt(chainIdParsed.reference, 10)); - if (chainIdParsed.namespace === 'eip155') { - const chainIdHex = numberToHex(parseInt(chainRef, 10)); - const nativeAssetId = nativeAssetIdentifiers[parsed.chainId] ?? null; - const symbol = - networkConfigurationsByChainId[chainIdHex]?.nativeCurrency ?? 'ETH'; - const nativePrice = - nativeAssetId && assetsPrice[nativeAssetId] - ? getPriceNumber(assetsPrice[nativeAssetId]) - : Number.NaN; - - let tokenAddress: string | null = null; + const nativeAssetId = nativeAssetIdentifiers[parsed.chainId] as + | Caip19AssetId + | undefined; + + const nativeCurrencySymbol = + networkConfigurationsByChainId[chainIdHex]?.nativeCurrency; + + const nativeAssetUsdPrice = + nativeAssetId && fungibleAssetsPrice[nativeAssetId]?.usdPrice; + + if ( + !nativeAssetId || + !nativeCurrencySymbol || + nativeAssetUsdPrice === undefined + ) { + // If we do not have a native asset for that chain or a price for it, the asset needs to be skipped + continue; + } + + let tokenAddress: string | undefined; if (parsed.assetNamespace === 'erc20') { tokenAddress = toChecksumAddress(String(parsed.assetReference)); } else if (parsed.assetNamespace === 'slip44') { @@ -153,20 +140,14 @@ export function formatExchangeRatesForBridge(params: { if (tokenAddress && nativeAssetId) { const priceInNative = - !Number.isNaN(nativePrice) && nativePrice > 0 - ? price / nativePrice - : price; + nativeAssetUsdPrice > 0 ? usdPrice / nativeAssetUsdPrice : usdPrice; if (!marketData[chainIdHex]) { marketData[chainIdHex] = {}; } - const baseMarketData = - typeof priceData === 'object' && priceData !== null - ? (priceData as Record) - : {}; marketData[chainIdHex][tokenAddress] = { - ...baseMarketData, + ...priceData, price: priceInNative, - currency: symbol, + currency: nativeCurrencySymbol, assetId, chainId: chainIdHex, tokenAddress, @@ -174,13 +155,20 @@ export function formatExchangeRatesForBridge(params: { } if (parsed.assetNamespace === 'slip44' && nativeAssetId) { - // conversionRate = native price in user's currency; usdConversionRate = native price in USD (bridge uses ratio for USD conversion) - currencyRates[symbol] = { + currencyRates[nativeCurrencySymbol] = { conversionDate: conversionTime, - conversionRate: price * rateUserCurrencyPerUsd, - usdConversionRate: price, + conversionRate: price, + usdConversionRate: usdPrice, }; } + } else { + conversionRates[assetId] = { + rate: String(price), + currency: currencyCaip, + conversionTime, + expirationTime, + marketData: priceData, + }; } } catch { // Skip malformed asset IDs diff --git a/packages/assets-controller/src/utils/formatStateForTransactionPay.test.ts b/packages/assets-controller/src/utils/formatStateForTransactionPay.test.ts index 270f66de873..8c69c10b8ab 100644 --- a/packages/assets-controller/src/utils/formatStateForTransactionPay.test.ts +++ b/packages/assets-controller/src/utils/formatStateForTransactionPay.test.ts @@ -2,10 +2,17 @@ import { formatStateForTransactionPay, AccountForLegacyFormat, } from './formatStateForTransactionPay'; -import type { AssetBalance, AssetMetadata, AssetPrice } from '../types'; - -function price(overrides: Partial & { price: number }): AssetPrice { - return { lastUpdated: 0, ...overrides } as AssetPrice; +import type { AssetBalance, AssetMetadata, FungibleAssetPrice } from '../types'; + +function price( + overrides: Partial & { price: number }, +): FungibleAssetPrice { + return { + assetPriceType: 'fungible', + lastUpdated: 0, + usdPrice: overrides.price, + ...overrides, + }; } const ETH_NATIVE_ID = 'eip155:1/slip44:60'; @@ -259,24 +266,27 @@ describe('formatStateForTransactionPay', () => { expect(result.marketData['0x1']?.[NATIVE_ADDRESS]).toBeDefined(); }); - it('uses usdToSelectedCurrencyRate for currencyRates when selectedCurrency is not usd', () => { + it('uses price for conversionRate and usdPrice for usdConversionRate when selectedCurrency is not usd', () => { const result = formatStateForTransactionPay({ accounts: [], assetsBalance: {}, assetsInfo: {}, assetsPrice: { - [ETH_NATIVE_ID]: price({ price: 2000, lastUpdated: 1_700_000_000_000 }), + [ETH_NATIVE_ID]: price({ + price: 1840, + usdPrice: 2000, + lastUpdated: 1_700_000_000_000, + }), }, selectedCurrency: 'eur', nativeAssetIdentifiers: EVM_NATIVE_IDS, networkConfigurationsByChainId: EVM_NETWORK_CONFIGS, - usdToSelectedCurrencyRate: 0.92, }); expect(result.currentCurrency).toBe('eur'); expect(result.currencyRates.ETH).toStrictEqual({ conversionDate: 1_700_000_000, - conversionRate: 2000 * 0.92, + conversionRate: 1840, usdConversionRate: 2000, }); }); diff --git a/packages/assets-controller/src/utils/formatStateForTransactionPay.ts b/packages/assets-controller/src/utils/formatStateForTransactionPay.ts index f9165955edb..9abd63e814b 100644 --- a/packages/assets-controller/src/utils/formatStateForTransactionPay.ts +++ b/packages/assets-controller/src/utils/formatStateForTransactionPay.ts @@ -72,7 +72,6 @@ function getAmountFromBalance(balance: AssetBalance): string { * @param params.accounts - List of accounts (id + address) to map state for. * @param params.nativeAssetIdentifiers - Optional CAIP-2 chain ID to native asset ID. * @param params.networkConfigurationsByChainId - Optional chain ID to network config (for native symbol). - * @param params.usdToSelectedCurrencyRate - Optional rate: 1 USD = this many units of selected currency. Required for correct currencyRates when selectedCurrency is not 'usd'; when omitted, conversionRate and usdConversionRate will be equal (incorrect for non-USD). * @returns Legacy-compatible state for transaction-pay-controller. */ export function formatStateForTransactionPay(params: { @@ -83,7 +82,6 @@ export function formatStateForTransactionPay(params: { accounts: AccountForLegacyFormat[]; nativeAssetIdentifiers?: Record; networkConfigurationsByChainId?: Record; - usdToSelectedCurrencyRate?: number; }): TransactionPayLegacyFormat { const { assetsBalance, @@ -93,7 +91,6 @@ export function formatStateForTransactionPay(params: { accounts, nativeAssetIdentifiers = {}, networkConfigurationsByChainId = {}, - usdToSelectedCurrencyRate, } = params; const tokenBalances: TransactionPayLegacyFormat['tokenBalances'] = {}; @@ -151,22 +148,15 @@ export function formatStateForTransactionPay(params: { continue; } const chainIdHex = numberToHex(parseInt(chainIdParsed.reference, 10)); - const baseMeta = - metadata && typeof metadata === 'object' && 'decimals' in metadata - ? (metadata as { decimals: number; symbol: string; name?: string }) - : null; - if (!baseMeta) { - continue; - } const address = parsed.assetNamespace === 'slip44' ? '0x0000000000000000000000000000000000000000' : toChecksumAddress(String(parsed.assetReference)); const token: LegacyToken = { address, - decimals: Number(baseMeta.decimals), - symbol: baseMeta.symbol ?? '', - name: baseMeta.name, + decimals: metadata.decimals, + symbol: metadata.symbol, + name: metadata.name, }; allTokensByChain[chainIdHex] ??= []; if ( @@ -192,7 +182,6 @@ export function formatStateForTransactionPay(params: { selectedCurrency, nativeAssetIdentifiers, networkConfigurationsByChainId, - usdToSelectedCurrencyRate, }); return { From 635bdf93450cacc327cabf0361f23cf7441ea9dc Mon Sep 17 00:00:00 2001 From: Bernardo Garces Chapero Date: Mon, 9 Mar 2026 09:58:36 +0000 Subject: [PATCH 4/8] changelog update --- packages/assets-controller/CHANGELOG.md | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/packages/assets-controller/CHANGELOG.md b/packages/assets-controller/CHANGELOG.md index a69f02906bc..13e3692c330 100644 --- a/packages/assets-controller/CHANGELOG.md +++ b/packages/assets-controller/CHANGELOG.md @@ -9,6 +9,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ### Added +- `usdPrice` is not included in asset price data ([#8123](https://github.com/MetaMask/core/pull/8123)) - Add `getStateForTransactionPay()` method and `AssetsController:getStateForTransactionPay` messenger action. Returns state in the legacy format expected by transaction-pay-controller (TokenBalancesController, AccountTrackerController, TokensController, TokenRatesController, CurrencyRateController shapes) so that when `useAssetsController` is true the transaction-pay-controller can use a single action instead of five separate getState calls. Also export `formatStateForTransactionPay` and types `TransactionPayLegacyFormat`, `AccountForLegacyFormat`, `LegacyToken` from utils ([#8094](https://github.com/MetaMask/core/pull/8094)) ### Changed @@ -19,7 +20,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ### Fixed -- `formatExchangeRatesForBridge` and `formatStateForTransactionPay` now read both `price` (selected currency) and `usdPrice` (USD) directly from asset price data ([#]()) +- `formatExchangeRatesForBridge` and `formatStateForTransactionPay` now read both `price` (selected currency) and `usdPrice` (USD) directly from asset price data ([#8123](https://github.com/MetaMask/core/pull/8123)) ## [2.2.0] From 077dbd743d9007fb64557d298d7de36e086dad1c Mon Sep 17 00:00:00 2001 From: Bernardo Garces Chapero Date: Mon, 9 Mar 2026 10:02:30 +0000 Subject: [PATCH 5/8] remove unnecessary casting --- packages/assets-controller/src/data-sources/PriceDataSource.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/assets-controller/src/data-sources/PriceDataSource.ts b/packages/assets-controller/src/data-sources/PriceDataSource.ts index f511d8188a2..cd98b9ebe2f 100644 --- a/packages/assets-controller/src/data-sources/PriceDataSource.ts +++ b/packages/assets-controller/src/data-sources/PriceDataSource.ts @@ -88,7 +88,7 @@ function isValidMarketData(data: unknown): data is SpotPriceMarketData { typeof data === 'object' && data !== null && 'price' in data && - typeof (data as SpotPriceMarketData).price === 'number' + typeof data.price === 'number' ); } From c701159e7d12d140bb7cee56e4dd004601735994 Mon Sep 17 00:00:00 2001 From: Bernardo Garces Chapero Date: Mon, 9 Mar 2026 10:13:09 +0000 Subject: [PATCH 6/8] Solve TODOs --- .../src/utils/formatExchangeRatesForBridge.ts | 24 ++++++++++--------- 1 file changed, 13 insertions(+), 11 deletions(-) diff --git a/packages/assets-controller/src/utils/formatExchangeRatesForBridge.ts b/packages/assets-controller/src/utils/formatExchangeRatesForBridge.ts index 9b442b6172a..47d597b567d 100644 --- a/packages/assets-controller/src/utils/formatExchangeRatesForBridge.ts +++ b/packages/assets-controller/src/utils/formatExchangeRatesForBridge.ts @@ -79,11 +79,14 @@ export function formatExchangeRatesForBridge(params: { const marketData: Record> = {}; const currencyCaip = MAP_CAIP_CURRENCIES[selectedCurrency.toLowerCase()]; - - // TODO Protect against undefined by returning early - - // TODO Check if this is a correct offset - const expirationOffset = 60; + if (!currencyCaip) { + return { + conversionRates: {}, + currencyRates: {}, + marketData: {}, + currentCurrency: selectedCurrency, + }; + } const fungibleAssetsPrice = Object.entries(assetsPrice).reduce< Record @@ -100,10 +103,9 @@ export function formatExchangeRatesForBridge(params: { continue; } - // TODO Remove this branching, lastUpdated always has the same format - const conversionTime = - lastUpdated > 1e12 ? lastUpdated / 1000 : lastUpdated; - const expirationTime = conversionTime + expirationOffset; + const lastUpdatedInSeconds = lastUpdated / 1000; + const expirationOffsetInSeconds = 60; + const expirationTime = lastUpdatedInSeconds + expirationOffsetInSeconds; try { const parsed = parseCaipAssetType(assetId as Caip19AssetId); @@ -156,7 +158,7 @@ export function formatExchangeRatesForBridge(params: { if (parsed.assetNamespace === 'slip44' && nativeAssetId) { currencyRates[nativeCurrencySymbol] = { - conversionDate: conversionTime, + conversionDate: lastUpdatedInSeconds, conversionRate: price, usdConversionRate: usdPrice, }; @@ -165,7 +167,7 @@ export function formatExchangeRatesForBridge(params: { conversionRates[assetId] = { rate: String(price), currency: currencyCaip, - conversionTime, + conversionTime: lastUpdatedInSeconds, expirationTime, marketData: priceData, }; From 01d82d730ee7e36fd9e958d6fa23646601a5c5ca Mon Sep 17 00:00:00 2001 From: Bernardo Garces Chapero Date: Mon, 9 Mar 2026 10:59:11 +0000 Subject: [PATCH 7/8] fix tests --- .../src/AssetsController.test.ts | 113 ++++-------------- .../formatExchangeRatesForBridge.test.ts | 27 +---- .../formatStateForTransactionPay.test.ts | 14 --- 3 files changed, 27 insertions(+), 127 deletions(-) diff --git a/packages/assets-controller/src/AssetsController.test.ts b/packages/assets-controller/src/AssetsController.test.ts index 5b03650fd54..23c1ce55378 100644 --- a/packages/assets-controller/src/AssetsController.test.ts +++ b/packages/assets-controller/src/AssetsController.test.ts @@ -19,6 +19,19 @@ import type { } from './AssetsController'; import type { PriceDataSourceConfig } from './data-sources/PriceDataSource'; import type { Caip19AssetId, AccountId } from './types'; +import { formatExchangeRatesForBridge } from './utils'; + +jest.mock('./utils', () => { + const actual = jest.requireActual('./utils'); + return { + ...actual, + formatExchangeRatesForBridge: jest.fn(actual.formatExchangeRatesForBridge), + }; +}); + +const formatExchangeRatesForBridgeMock = jest.mocked( + formatExchangeRatesForBridge, +); function createMockQueryApiClient(): ApiPlatformClient { return { fetch: jest.fn() } as unknown as ApiPlatformClient; @@ -621,68 +634,12 @@ describe('AssetsController', () => { }); describe('getExchangeRatesForBridge', () => { - it('returns bridge format via action with empty state', async () => { - await withController(({ messenger }) => { - const result = (messenger.call as CallableFunction)( - 'AssetsController:getExchangeRatesForBridge', - ); - - expect(result).toStrictEqual({ - conversionRates: {}, - currencyRates: {}, - marketData: {}, - currentCurrency: 'usd', - }); - }); - }); - - it('returns bridge format derived from assetsPrice and selectedCurrency', async () => { - const bitcoinAssetId = - 'bip122:000000000019d6689c085ae165831e93/slip44:0' as Caip19AssetId; + it('calls formatExchangeRatesForBridge with state and network data', async () => { const initialState: Partial = { assetsPrice: { - [bitcoinAssetId]: { + [MOCK_NATIVE_ASSET_ID]: { assetPriceType: 'fungible', - price: 50000, - usdPrice: 50000, - lastUpdated: 1_600_000_000, - }, - }, - selectedCurrency: 'eur', - }; - - await withController({ state: initialState }, ({ messenger }) => { - const result = (messenger.call as CallableFunction)( - 'AssetsController:getExchangeRatesForBridge', - ); - - expect(result.currentCurrency).toBe('eur'); - expect(result.conversionRates[bitcoinAssetId]).toBeDefined(); - expect(result.conversionRates[bitcoinAssetId].rate).toBe('50000'); - expect(result.conversionRates[bitcoinAssetId].currency).toBe( - 'swift:0/iso4217:EUR', - ); - }); - }); - - it('returns same result as controller.getExchangeRatesForBridge()', async () => { - await withController(({ controller, messenger }) => { - const viaAction = (messenger.call as CallableFunction)( - 'AssetsController:getExchangeRatesForBridge', - ); - const viaMethod = controller.getExchangeRatesForBridge(); - - expect(viaAction).toStrictEqual(viaMethod); - }); - }); - - it('reads price and usdPrice directly so currencyRates have distinct conversionRate and usdConversionRate', async () => { - const ethNativeId = 'eip155:1/slip44:60' as Caip19AssetId; - const initialState: Partial = { - assetsPrice: { - [ethNativeId]: { - assetPriceType: 'fungible', - price: 1840, + price: 2000, usdPrice: 2000, lastUpdated: 1_700_000_000_000, }, @@ -691,37 +648,19 @@ describe('AssetsController', () => { }; await withController({ state: initialState }, ({ controller }) => { - const result = controller.getExchangeRatesForBridge(); + formatExchangeRatesForBridgeMock.mockClear(); - expect(result.currencyRates.ETH).toBeDefined(); - expect(result.currencyRates.ETH?.conversionRate).toBe(1840); - expect(result.currencyRates.ETH?.usdConversionRate).toBe(2000); - }); - }); - }); + controller.getExchangeRatesForBridge(); - describe('getStateForTransactionPay', () => { - it('reads price and usdPrice directly so currencyRates have distinct conversionRate and usdConversionRate', async () => { - const ethNativeId = 'eip155:1/slip44:60' as Caip19AssetId; - const initialState: Partial = { - assetsPrice: { - [ethNativeId]: { - assetPriceType: 'fungible', - price: 1840, - usdPrice: 2000, - lastUpdated: 1_700_000_000_000, + expect(formatExchangeRatesForBridgeMock).toHaveBeenCalledTimes(1); + expect(formatExchangeRatesForBridgeMock).toHaveBeenCalledWith({ + assetsPrice: initialState.assetsPrice, + selectedCurrency: 'eur', + nativeAssetIdentifiers: { + 'eip155:1': 'eip155:1/slip44:60', }, - }, - selectedCurrency: 'eur', - }; - - await withController({ state: initialState }, ({ controller }) => { - const result = controller.getStateForTransactionPay(); - - expect(result.currentCurrency).toBe('eur'); - expect(result.currencyRates.ETH).toBeDefined(); - expect(result.currencyRates.ETH?.conversionRate).toBe(1840); - expect(result.currencyRates.ETH?.usdConversionRate).toBe(2000); + networkConfigurationsByChainId: {}, + }); }); }); }); diff --git a/packages/assets-controller/src/utils/formatExchangeRatesForBridge.test.ts b/packages/assets-controller/src/utils/formatExchangeRatesForBridge.test.ts index 057daa9806c..e85fcc908f1 100644 --- a/packages/assets-controller/src/utils/formatExchangeRatesForBridge.test.ts +++ b/packages/assets-controller/src/utils/formatExchangeRatesForBridge.test.ts @@ -48,7 +48,7 @@ describe('formatExchangeRatesForBridge', () => { const bitcoinAssetId = 'bip122:000000000019d6689c085ae165831e93/slip44:0'; const result = formatExchangeRatesForBridge({ assetsPrice: { - [bitcoinAssetId]: price({ price: 50000, lastUpdated: 1000000 }), + [bitcoinAssetId]: price({ price: 50000, lastUpdated: 1000000000 }), }, selectedCurrency: 'usd', }); @@ -78,18 +78,6 @@ describe('formatExchangeRatesForBridge', () => { expect(result.currentCurrency).toBe('eur'); }); - it('falls back to USD when selectedCurrency is not in MAP_CAIP_CURRENCIES', () => { - const assetId = 'bip122:000000000019d6689c085ae165831e93/slip44:0'; - const result = formatExchangeRatesForBridge({ - assetsPrice: { [assetId]: price({ price: 1 }) }, - selectedCurrency: 'unknown-currency', - }); - - expect(result.conversionRates[assetId].currency).toBe( - 'swift:0/iso4217:USD', - ); - }); - it('does not include EVM assets in conversionRates', () => { const ethNativeId = 'eip155:1/slip44:60'; const result = formatExchangeRatesForBridge({ @@ -139,19 +127,6 @@ describe('formatExchangeRatesForBridge', () => { expect(result.marketData['0x89']?.[nativeAddress]?.currency).toBe('POL'); }); - it('falls back to ETH when networkConfigurationsByChainId has no nativeCurrency for chain', () => { - const ethNativeId = 'eip155:1/slip44:60'; - const result = formatExchangeRatesForBridge({ - assetsPrice: { [ethNativeId]: price({ price: 2000 }) }, - selectedCurrency: 'usd', - nativeAssetIdentifiers: { 'eip155:1': ethNativeId }, - networkConfigurationsByChainId: {}, - }); - - expect(result.currencyRates.ETH).toBeDefined(); - expect(result.currencyRates.ETH?.conversionRate).toBe(2000); - }); - it('includes EVM native asset in marketData and currencyRates', () => { const ethNativeId = 'eip155:1/slip44:60'; const result = formatExchangeRatesForBridge({ diff --git a/packages/assets-controller/src/utils/formatStateForTransactionPay.test.ts b/packages/assets-controller/src/utils/formatStateForTransactionPay.test.ts index 8c69c10b8ab..70afca5d416 100644 --- a/packages/assets-controller/src/utils/formatStateForTransactionPay.test.ts +++ b/packages/assets-controller/src/utils/formatStateForTransactionPay.test.ts @@ -171,20 +171,6 @@ describe('formatStateForTransactionPay', () => { expect(result.allTokens).toStrictEqual({}); }); - it('skips metadata without decimals for allTokens', () => { - const result = formatStateForTransactionPay({ - accounts: [], - assetsBalance: {}, - assetsInfo: { - [USDC_ASSET_ID]: { type: 'erc721' } as AssetMetadata, - }, - assetsPrice: {}, - selectedCurrency: 'usd', - }); - - expect(result.allTokens).toStrictEqual({}); - }); - it('de-duplicates tokens by address (case-insensitive) in allTokens', () => { const lowerUsdcId = 'eip155:1/erc20:0xa0b86991c6218b36c1d19d4a2e9eb0ce3606eb48'; From 0c1e5279360fe7fc14d88696b070a78cbd337caf Mon Sep 17 00:00:00 2001 From: Bernardo Garces Chapero Date: Mon, 9 Mar 2026 11:02:30 +0000 Subject: [PATCH 8/8] more test fixes --- .../src/data-sources/PriceDataSource.test.ts | 15 ++++++++++++++- 1 file changed, 14 insertions(+), 1 deletion(-) diff --git a/packages/assets-controller/src/data-sources/PriceDataSource.test.ts b/packages/assets-controller/src/data-sources/PriceDataSource.test.ts index bf8def6eab1..03c2ddf2623 100644 --- a/packages/assets-controller/src/data-sources/PriceDataSource.test.ts +++ b/packages/assets-controller/src/data-sources/PriceDataSource.test.ts @@ -208,7 +208,9 @@ describe('PriceDataSource', () => { { currency: 'usd', includeMarketData: true }, ); expect(response.assetsPrice?.[MOCK_NATIVE_ASSET]).toStrictEqual({ + assetPriceType: 'fungible', price: 2500, + usdPrice: 2500, pricePercentChange1d: 2.5, lastUpdated: expect.any(Number), marketCap: 1000000000, @@ -241,7 +243,9 @@ describe('PriceDataSource', () => { { currency: 'usd', includeMarketData: true }, ); expect(response.assetsPrice?.[MOCK_NATIVE_ASSET]).toStrictEqual({ + assetPriceType: 'fungible', price: 2500, + usdPrice: 2500, pricePercentChange1d: 2.5, lastUpdated: expect.any(Number), marketCap: 1000000000, @@ -680,7 +684,9 @@ describe('PriceDataSource', () => { { currency: 'usd', includeMarketData: true }, ); expect(context.response.assetsPrice?.[MOCK_TOKEN_ASSET]).toStrictEqual({ + assetPriceType: 'fungible', price: 1.0, + usdPrice: 1.0, pricePercentChange1d: 2.5, lastUpdated: expect.any(Number), marketCap: 1000000000, @@ -714,7 +720,9 @@ describe('PriceDataSource', () => { { currency: 'usd', includeMarketData: true }, ); expect(context.response.assetsPrice?.[MOCK_TOKEN_ASSET]).toStrictEqual({ + assetPriceType: 'fungible', price: 1.0, + usdPrice: 1.0, pricePercentChange1d: 2.5, lastUpdated: expect.any(Number), marketCap: 1000000000, @@ -787,7 +795,12 @@ describe('PriceDataSource', () => { const context = createMiddlewareContext({ response: { assetsPrice: { - [anotherAsset]: { price: 1.0, lastUpdated: Date.now() }, + [anotherAsset]: { + assetPriceType: 'fungible', + price: 1.0, + usdPrice: 1.0, + lastUpdated: Date.now(), + }, }, detectedAssets: { 'mock-account-id': [MOCK_TOKEN_ASSET],