From 65f968551a198ed99c0e938be8c406c8c2dc709f Mon Sep 17 00:00:00 2001 From: Kriys94 Date: Tue, 29 Sep 2026 21:43:27 +0200 Subject: [PATCH] fix(core-backend): price supportedNetwork response --- packages/assets-controller/CHANGELOG.md | 4 + .../src/data-sources/PriceDataSource.test.ts | 154 +++++++++++++++--- .../src/data-sources/PriceDataSource.ts | 42 ++++- packages/core-backend/CHANGELOG.md | 5 + packages/core-backend/README.md | 4 +- .../src/api/ApiPlatformClient.test.ts | 2 +- packages/core-backend/src/api/index.ts | 2 + .../src/api/prices/client.test.ts | 28 +++- .../core-backend/src/api/prices/client.ts | 18 +- packages/core-backend/src/api/prices/index.ts | 2 + packages/core-backend/src/api/prices/types.ts | 37 ++++- packages/core-backend/src/index.ts | 2 + 12 files changed, 248 insertions(+), 52 deletions(-) diff --git a/packages/assets-controller/CHANGELOG.md b/packages/assets-controller/CHANGELOG.md index e9a3faee0e7..8dd9bc6c3e4 100644 --- a/packages/assets-controller/CHANGELOG.md +++ b/packages/assets-controller/CHANGELOG.md @@ -12,6 +12,10 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 - Bump `lodash-es` from `^4.17.21` to `^4.18.1` ([#10447](https://github.com/MetaMask/core/pull/10447)) - Bump `@ethersproject/providers` from `^5.7.0` to `^5.8.0` ([#10482](https://github.com/MetaMask/core/pull/10482)) +### Fixed + +- Fix `PriceDataSource` supported-network filtering so it reads the object-shaped `partialSupport` returned by the Price API `/v2/supportedNetworks` endpoint ([#10582](https://github.com/MetaMask/core/pull/10582)) + ## [17.0.0] ### Added diff --git a/packages/assets-controller/src/data-sources/PriceDataSource.test.ts b/packages/assets-controller/src/data-sources/PriceDataSource.test.ts index 3d8fa7eb401..c7415a37446 100644 --- a/packages/assets-controller/src/data-sources/PriceDataSource.test.ts +++ b/packages/assets-controller/src/data-sources/PriceDataSource.test.ts @@ -83,13 +83,17 @@ function createMiddlewareContext(overrides?: Partial): Context { function createMockApiClient( priceResponse: Record = {}, supportedNetworks: string[] = ['eip155:1'], + partialSupportNetworks: string[] = [], ): MockApiClient { return { prices: { fetchV3SpotPrices: jest.fn().mockResolvedValue(priceResponse), fetchPriceV2SupportedNetworks: jest.fn().mockResolvedValue({ fullSupport: supportedNetworks, - partialSupport: [], + partialSupport: { + spotPricesV2: [], + spotPricesV3: partialSupportNetworks, + }, }), }, }; @@ -118,6 +122,7 @@ function setupController( getSelectedCurrency?: () => SupportedCurrency; pollInterval?: number; supportedNetworks?: string[]; + partialSupportNetworks?: string[]; getAssetsState?: () => AssetsControllerState; } = {}, ): SetupResult { @@ -127,10 +132,15 @@ function setupController( getSelectedCurrency = (): SupportedCurrency => 'usd', pollInterval, supportedNetworks = ['eip155:1'], + partialSupportNetworks = [], getAssetsState: getAssetsStateOverride, } = options; - const apiClient = createMockApiClient(priceResponse, supportedNetworks); + const apiClient = createMockApiClient( + priceResponse, + supportedNetworks, + partialSupportNetworks, + ); const getAssetsState = getAssetsStateOverride ?? @@ -201,7 +211,7 @@ describe('PriceDataSource', () => { const { controller, apiClient } = setupController({ balanceState: { 'mock-account-id': { - [MOCK_NATIVE_ASSET]: { amount: '1000000000000000000' }, + [MOCK_NATIVE_ASSET]: { amount: '1.5' }, }, }, priceResponse: { @@ -233,7 +243,7 @@ describe('PriceDataSource', () => { balanceState: { 'mock-account-id': { 'not-a-valid-caip19': { amount: '999' }, - [MOCK_NATIVE_ASSET]: { amount: '1000000000000000000' }, + [MOCK_NATIVE_ASSET]: { amount: '1.5' }, }, }, priceResponse: { @@ -304,7 +314,7 @@ describe('PriceDataSource', () => { getSelectedCurrency: () => 'eur', balanceState: { 'mock-account-id': { - [MOCK_NATIVE_ASSET]: { amount: '1000000000000000000' }, + [MOCK_NATIVE_ASSET]: { amount: '1.5' }, }, }, priceResponse: { @@ -326,10 +336,10 @@ describe('PriceDataSource', () => { const { controller, apiClient } = setupController({ balanceState: { 'mock-account-id': { - [MOCK_NATIVE_ASSET]: { amount: '1000000000000000000' }, + [MOCK_NATIVE_ASSET]: { amount: '1.5' }, }, 'other-account-id': { - [MOCK_TOKEN_ASSET]: { amount: '1000000' }, + [MOCK_TOKEN_ASSET]: { amount: '1000' }, }, }, priceResponse: { @@ -353,8 +363,8 @@ describe('PriceDataSource', () => { const { controller, apiClient } = setupController({ balanceState: { 'mock-account-id': { - [MOCK_NATIVE_ASSET]: { amount: '1000000000000000000' }, - [polygonAsset]: { amount: '5000000000000000000' }, + [MOCK_NATIVE_ASSET]: { amount: '1.5' }, + [polygonAsset]: { amount: '250' }, }, }, priceResponse: { @@ -373,6 +383,98 @@ describe('PriceDataSource', () => { controller.destroy(); }); + it('fetch includes assets on partialSupport.spotPricesV3 networks', async () => { + const polygonAsset = + 'eip155:137/erc20:0x0000000000000000000000000000000000001010' as Caip19AssetId; + const { controller, apiClient } = setupController({ + balanceState: { + 'mock-account-id': { + [MOCK_NATIVE_ASSET]: { amount: '1.5' }, + [polygonAsset]: { amount: '250' }, + }, + }, + priceResponse: { + [MOCK_NATIVE_ASSET]: createMockPriceData(2500), + [polygonAsset]: createMockPriceData(0.5), + }, + supportedNetworks: ['eip155:1'], + partialSupportNetworks: ['eip155:137'], + }); + + await controller.fetch( + createDataRequest({ chainIds: [CHAIN_MAINNET, CHAIN_POLYGON] }), + ); + + expect(apiClient.prices.fetchV3SpotPrices).toHaveBeenCalledWith( + expect.arrayContaining([MOCK_NATIVE_ASSET, polygonAsset]), + expect.anything(), + ); + + controller.destroy(); + }); + + it('fetch excludes assets only listed under partialSupport.spotPricesV2', async () => { + const polygonAsset = + 'eip155:137/erc20:0x0000000000000000000000000000000000001010' as Caip19AssetId; + const { controller, apiClient } = setupController({ + balanceState: { + 'mock-account-id': { + [MOCK_NATIVE_ASSET]: { amount: '1.5' }, + [polygonAsset]: { amount: '250' }, + }, + }, + priceResponse: { + [MOCK_NATIVE_ASSET]: createMockPriceData(2500), + }, + }); + apiClient.prices.fetchPriceV2SupportedNetworks.mockResolvedValue({ + fullSupport: ['eip155:1'], + partialSupport: { + spotPricesV2: ['eip155:137'], + spotPricesV3: [], + }, + }); + + await controller.fetch( + createDataRequest({ chainIds: [CHAIN_MAINNET, CHAIN_POLYGON] }), + ); + + expect(apiClient.prices.fetchV3SpotPrices).toHaveBeenCalledWith( + [MOCK_NATIVE_ASSET], + expect.anything(), + ); + + controller.destroy(); + }); + + it('fetch tolerates a legacy array-shaped partialSupport', async () => { + const polygonAsset = + 'eip155:137/erc20:0x0000000000000000000000000000000000001010' as Caip19AssetId; + const { controller, apiClient } = setupController({ + balanceState: { + 'mock-account-id': { + [polygonAsset]: { amount: '250' }, + }, + }, + priceResponse: { + [polygonAsset]: createMockPriceData(0.5), + }, + }); + apiClient.prices.fetchPriceV2SupportedNetworks.mockResolvedValue({ + fullSupport: ['eip155:1'], + partialSupport: ['eip155:137'], + }); + + await controller.fetch(createDataRequest({ chainIds: [CHAIN_POLYGON] })); + + expect(apiClient.prices.fetchV3SpotPrices).toHaveBeenCalledWith( + [polygonAsset], + expect.anything(), + ); + + controller.destroy(); + }); + it('fetch filters out non-priceable assets', async () => { const tronBandwidthAsset = 'tron:0x2b6653dc/slip44:bandwidth' as Caip19AssetId; @@ -383,7 +485,7 @@ describe('PriceDataSource', () => { const { controller, apiClient } = setupController({ balanceState: { 'mock-account-id': { - [MOCK_NATIVE_ASSET]: { amount: '1000000000000000000' }, + [MOCK_NATIVE_ASSET]: { amount: '1.5' }, [tronBandwidthAsset]: { amount: '1000' }, [tronEnergyAsset]: { amount: '5000' }, [tronStakedAsset]: { amount: '10000' }, @@ -408,8 +510,8 @@ describe('PriceDataSource', () => { const { controller } = setupController({ balanceState: { 'mock-account-id': { - [MOCK_NATIVE_ASSET]: { amount: '1000000000000000000' }, - [MOCK_TOKEN_ASSET]: { amount: '1000000' }, + [MOCK_NATIVE_ASSET]: { amount: '1.5' }, + [MOCK_TOKEN_ASSET]: { amount: '1000' }, }, }, priceResponse: { @@ -432,7 +534,7 @@ describe('PriceDataSource', () => { const { controller, apiClient } = setupController({ balanceState: { 'mock-account-id': { - [MOCK_NATIVE_ASSET]: { amount: '1000000000000000000' }, + [MOCK_NATIVE_ASSET]: { amount: '1.5' }, }, }, }); @@ -466,7 +568,7 @@ describe('PriceDataSource', () => { const { controller, assetsUpdateHandler } = setupController({ balanceState: { 'mock-account-id': { - [MOCK_NATIVE_ASSET]: { amount: '1000000000000000000' }, + [MOCK_NATIVE_ASSET]: { amount: '1.5' }, }, }, priceResponse: { @@ -498,7 +600,7 @@ describe('PriceDataSource', () => { pollInterval: 5000, balanceState: { 'mock-account-id': { - [MOCK_NATIVE_ASSET]: { amount: '1000000000000000000' }, + [MOCK_NATIVE_ASSET]: { amount: '1.5' }, }, }, priceResponse: { @@ -526,7 +628,7 @@ describe('PriceDataSource', () => { pollInterval: 60000, balanceState: { 'mock-account-id': { - [MOCK_NATIVE_ASSET]: { amount: '1000000000000000000' }, + [MOCK_NATIVE_ASSET]: { amount: '1.5' }, }, }, priceResponse: { @@ -559,7 +661,7 @@ describe('PriceDataSource', () => { const { controller, apiClient } = setupController({ balanceState: { 'mock-account-id': { - [MOCK_NATIVE_ASSET]: { amount: '1000000000000000000' }, + [MOCK_NATIVE_ASSET]: { amount: '1.5' }, }, }, priceResponse: { @@ -713,7 +815,7 @@ describe('PriceDataSource', () => { pollInterval: 5000, balanceState: { 'mock-account-id': { - [MOCK_NATIVE_ASSET]: { amount: '1000000000000000000' }, + [MOCK_NATIVE_ASSET]: { amount: '1.5' }, }, }, priceResponse: { @@ -942,7 +1044,7 @@ describe('PriceDataSource', () => { const { controller, apiClient } = setupController({ balanceState: { 'mock-account-id': { - [MOCK_NATIVE_ASSET]: { amount: '1000000000000000000' }, + [MOCK_NATIVE_ASSET]: { amount: '1.5' }, }, }, priceResponse: { @@ -966,7 +1068,7 @@ describe('PriceDataSource', () => { pollInterval: 10_000, balanceState: { 'mock-account-id': { - [MOCK_NATIVE_ASSET]: { amount: '1000000000000000000' }, + [MOCK_NATIVE_ASSET]: { amount: '1.5' }, }, }, priceResponse: { @@ -1001,7 +1103,7 @@ describe('PriceDataSource', () => { pollInterval, balanceState: { 'mock-account-id': { - [MOCK_NATIVE_ASSET]: { amount: '1000000000000000000' }, + [MOCK_NATIVE_ASSET]: { amount: '1.5' }, }, }, priceResponse: { @@ -1122,7 +1224,7 @@ describe('PriceDataSource', () => { const { controller, apiClient } = setupController({ balanceState: { 'mock-account-id': { - [MOCK_NATIVE_ASSET]: { amount: '1000000000000000000' }, + [MOCK_NATIVE_ASSET]: { amount: '1.5' }, }, }, priceResponse: { @@ -1163,7 +1265,7 @@ describe('PriceDataSource', () => { const { controller, apiClient } = setupController({ balanceState: { 'mock-account-id': { - [MOCK_NATIVE_ASSET]: { amount: '1000000000000000000' }, + [MOCK_NATIVE_ASSET]: { amount: '1.5' }, }, }, priceResponse: { @@ -1186,7 +1288,7 @@ describe('PriceDataSource', () => { ({ assetsBalance: { 'mock-account-id': { - [MOCK_NATIVE_ASSET]: { amount: '1000000000000000000' }, + [MOCK_NATIVE_ASSET]: { amount: '1.5' }, }, }, }) as AssetsControllerState, @@ -1206,8 +1308,8 @@ describe('PriceDataSource', () => { pollInterval: 5000, balanceState: { 'mock-account-id': { - [MOCK_NATIVE_ASSET]: { amount: '1000000000000000000' }, - [polygonAsset]: { amount: '5000000000000000000' }, + [MOCK_NATIVE_ASSET]: { amount: '1.5' }, + [polygonAsset]: { amount: '250' }, }, }, priceResponse: { diff --git a/packages/assets-controller/src/data-sources/PriceDataSource.ts b/packages/assets-controller/src/data-sources/PriceDataSource.ts index 3d10d12d758..bd00a6eef92 100644 --- a/packages/assets-controller/src/data-sources/PriceDataSource.ts +++ b/packages/assets-controller/src/data-sources/PriceDataSource.ts @@ -1,5 +1,5 @@ import type { - PriceSupportedNetworksResponse, + PriceV2SupportedNetworksResponse, SupportedCurrency, V3SpotPricesResponse, } from '@metamask/core-backend'; @@ -42,6 +42,38 @@ const PRICE_API_BATCH_SIZE = 50; const log = createModuleLogger(projectLogger, CONTROLLER_NAME); +// ============================================================================ +// HELPERS +// ============================================================================ + +/** + * Extracts the CAIP-2 chain IDs the Price API v3 spot-prices endpoint + * supports from a `/v2/supportedNetworks` response. + * + * The endpoint returns `partialSupport` as an object keyed by spot-price + * endpoint (`{ spotPricesV2, spotPricesV3 }`). Since this data source only + * calls `/v3/spot-prices`, only `spotPricesV3` is relevant. A legacy + * array-shaped `partialSupport` is still accepted so a transient schema + * change does not disable price fetching. + * + * @param response - The supported networks response. + * @param response.fullSupport - Chains supported by every endpoint. + * @param response.partialSupport - Chains supported by specific endpoints. + * @returns Unique CAIP-2 chain IDs supported by v3 spot prices. + */ +function getV3SpotPriceSupportedChainIds(response: { + fullSupport?: string[]; + partialSupport?: + | string[] + | Partial; +}): Set { + const { fullSupport, partialSupport } = response; + const partial = Array.isArray(partialSupport) + ? partialSupport + : (partialSupport?.spotPricesV3 ?? []); + return new Set([...(fullSupport ?? []), ...partial]); +} + // ============================================================================ // OPTIONS // ============================================================================ @@ -473,20 +505,20 @@ export class PriceDataSource { } /** - * Price API supported networks. + * Price API networks supported by the v3 spot-prices endpoint + * (`fullSupport` plus `partialSupport.spotPricesV3`). * * @returns CAIP-2 chain IDs; empty on error. */ async #getSupportedNetworks(): Promise> { try { const response = await fetchWithTimeout( - (): Promise => + (): Promise => this.#apiClient.prices.fetchPriceV2SupportedNetworks(), this.#fetchTimeoutMs, ); - const allNetworks = [...response.fullSupport, ...response.partialSupport]; - return new Set(allNetworks); + return getV3SpotPriceSupportedChainIds(response); } catch (error) { log('Failed to fetch price supported networks', { error }); return new Set(); diff --git a/packages/core-backend/CHANGELOG.md b/packages/core-backend/CHANGELOG.md index aa1e495e33c..fb06613e7a8 100644 --- a/packages/core-backend/CHANGELOG.md +++ b/packages/core-backend/CHANGELOG.md @@ -9,6 +9,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ### Changed +- **BREAKING:** `PriceSupportedNetworksResponse` now matches the real `/v2/supportedNetworks` response, with `partialSupport` typed as `{ spotPricesV2: string[]; spotPricesV3: string[] }` instead of `string[]` ([#10582](https://github.com/MetaMask/core/pull/10582)) - Bump `uuid` from `^9.0.1` to `^11.1.1` ([#10243](https://github.com/MetaMask/core/pull/10243)) - Bump `@metamask/profile-sync-controller` from `^32.1.1` to `^33.0.0` ([#10348](https://github.com/MetaMask/core/pull/10348), [#10409](https://github.com/MetaMask/core/pull/10409), [#10418](https://github.com/MetaMask/core/pull/10418), [#10459](https://github.com/MetaMask/core/pull/10459)) - Bump `@tanstack/query-core` from `^5.62.16` to `^5.103.2` ([#9324](https://github.com/MetaMask/core/pull/9324), [#10511](https://github.com/MetaMask/core/pull/10511)) @@ -16,6 +17,10 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 - Bump `cockatiel` from `^3.1.2` to `^3.2.1` ([#10436](https://github.com/MetaMask/core/pull/10436)) - Bump `@metamask/account-tree-controller` from `^10.0.1` to `^11.0.0` ([#10459](https://github.com/MetaMask/core/pull/10459)) +### Deprecated + +- Deprecate `PriceSupportedNetworksResponse` in favor of `PriceV2SupportedNetworksResponse` ([#10582](https://github.com/MetaMask/core/pull/10582)) + ## [11.0.0] ### Added diff --git a/packages/core-backend/README.md b/packages/core-backend/README.md index c42d3a7aaf9..6384fb88ff2 100644 --- a/packages/core-backend/README.md +++ b/packages/core-backend/README.md @@ -480,7 +480,7 @@ Handles price-related operations including spot prices, exchange rates, and hist | Method | Description | | ---------------------------------------------------------------------------------------------------------------- | ------------------------------------------------ | -| `fetchPriceV1SupportedNetworks(options?)` | Get price-supported networks (v1) | +| `fetchPriceV1SupportedNetworks(options?)` | Get price-supported networks (v1, decimal IDs) | | `fetchPriceV2SupportedNetworks(options?)` | Get price-supported networks in CAIP format (v2) | | `fetchV1ExchangeRates(baseCurrency, options?)` | Get exchange rates for base currency | | `fetchV1FiatExchangeRates(options?)` | Get fiat exchange rates | @@ -500,6 +500,8 @@ Handles price-related operations including spot prices, exchange rates, and hist | `getPriceV1SupportedNetworksQueryOptions(options?)` … `getV1HistoricalPriceGraphByTokenAddressQueryOptions(...)` | Return TanStack Query options for each fetch | | `invalidatePrices()` | Invalidate all price cache | +> **Note:** For both supported-networks endpoints, `partialSupport` is an object keyed by spot-price endpoint, not a flat array: `/v1` returns `{ spotPricesV2: number[] }` and `/v2` returns `{ spotPricesV2: string[]; spotPricesV3: string[] }`. Pick the key that matches the spot-prices endpoint you call (e.g. `fullSupport` + `partialSupport.spotPricesV3` for `/v3/spot-prices`). + #### TokenApiClient Handles token metadata, lists, and trending/popular token discovery. diff --git a/packages/core-backend/src/api/ApiPlatformClient.test.ts b/packages/core-backend/src/api/ApiPlatformClient.test.ts index de2a9761559..4de6244a88a 100644 --- a/packages/core-backend/src/api/ApiPlatformClient.test.ts +++ b/packages/core-backend/src/api/ApiPlatformClient.test.ts @@ -289,7 +289,7 @@ describe('ApiPlatformClient', () => { const queryKey = ['prices', 'v1SupportedNetworks']; client.setCachedData(queryKey, { fullSupport: [], - partialSupport: [], + partialSupport: { spotPricesV2: [] }, }); await client.prices.invalidatePrices(); diff --git a/packages/core-backend/src/api/index.ts b/packages/core-backend/src/api/index.ts index a69fd10e355..b84cf0648b1 100644 --- a/packages/core-backend/src/api/index.ts +++ b/packages/core-backend/src/api/index.ts @@ -61,6 +61,8 @@ export type { ExchangeRateInfo, V1ExchangeRatesResponse, PriceSupportedNetworksResponse, + PriceV1SupportedNetworksResponse, + PriceV2SupportedNetworksResponse, V1HistoricalPricesResponse, V3HistoricalPricesResponse, } from './prices/index.js'; diff --git a/packages/core-backend/src/api/prices/client.test.ts b/packages/core-backend/src/api/prices/client.test.ts index 237d4ed5a5a..6f4e3ad8c9a 100644 --- a/packages/core-backend/src/api/prices/client.test.ts +++ b/packages/core-backend/src/api/prices/client.test.ts @@ -11,7 +11,8 @@ import { setupTestEnvironment, } from '../test-utils.js'; import type { - PriceSupportedNetworksResponse, + PriceV1SupportedNetworksResponse, + PriceV2SupportedNetworksResponse, V1ExchangeRatesResponse, V3SpotPricesResponse, } from './types.js'; @@ -25,9 +26,9 @@ describe('PricesApiClient', () => { describe('Supported Networks', () => { it('fetches price v1 supported networks', async () => { - const mockResponse = { - fullSupport: ['0x1', '0x89'], - partialSupport: ['0x38'], + const mockResponse: PriceV1SupportedNetworksResponse = { + fullSupport: [1, 137], + partialSupport: { spotPricesV2: [56] }, }; mockFetch.mockResolvedValueOnce(createMockResponse(mockResponse)); @@ -40,16 +41,25 @@ describe('PricesApiClient', () => { ); }); - it('fetches price v2 supported networks', async () => { - const mockResponse = { + it('fetches price v2 supported networks with object-shaped partialSupport', async () => { + const mockResponse: PriceV2SupportedNetworksResponse = { fullSupport: ['eip155:1', 'eip155:137'], - partialSupport: ['eip155:56'], + partialSupport: { + spotPricesV2: ['eip155:56'], + spotPricesV3: [ + 'eip155:56', + 'solana:5eykt4UsFv8P8NJdTREpY1vzqKqZKvdp', + ], + }, }; mockFetch.mockResolvedValueOnce(createMockResponse(mockResponse)); const result = await client.prices.fetchPriceV2SupportedNetworks(); expect(result).toStrictEqual(mockResponse); + expect(result.partialSupport.spotPricesV3).toContain( + 'solana:5eykt4UsFv8P8NJdTREpY1vzqKqZKvdp', + ); expect(mockFetch).toHaveBeenCalledWith( `${API_URLS.PRICES}/v2/supportedNetworks`, expect.any(Object), @@ -650,8 +660,8 @@ describe('PricesApiClient', () => { describe('get*QueryOptions pass-through options (select, initialPageParam)', () => { it('getPriceV1SupportedNetworksQueryOptions merges select and initialPageParam from options', () => { const select = ( - data: PriceSupportedNetworksResponse, - ): PriceSupportedNetworksResponse => data; + data: PriceV1SupportedNetworksResponse, + ): PriceV1SupportedNetworksResponse => data; const options = client.prices.getPriceV1SupportedNetworksQueryOptions({ select, initialPageParam: 0, diff --git a/packages/core-backend/src/api/prices/client.ts b/packages/core-backend/src/api/prices/client.ts index 693bff25438..e8add01d789 100644 --- a/packages/core-backend/src/api/prices/client.ts +++ b/packages/core-backend/src/api/prices/client.ts @@ -24,7 +24,8 @@ import type { import type { CoinGeckoSpotPrice, V1ExchangeRatesResponse, - PriceSupportedNetworksResponse, + PriceV1SupportedNetworksResponse, + PriceV2SupportedNetworksResponse, V1HistoricalPricesResponse, V3SpotPricesResponse, V3HistoricalPricesResponse, @@ -60,11 +61,11 @@ export class PricesApiClient extends BaseApiClient { */ getPriceV1SupportedNetworksQueryOptions( options?: FetchOptions, - ): FetchQueryOptions { + ): FetchQueryOptions { return { queryKey: ['prices', 'v1SupportedNetworks'], queryFn: ({ signal }: QueryFunctionContext) => - this.fetch( + this.fetch( this.apiUrls.PRICES, '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/v1/supportedNetworks', { signal }, @@ -83,7 +84,7 @@ export class PricesApiClient extends BaseApiClient { */ async fetchPriceV1SupportedNetworks( options?: FetchOptions, - ): Promise { + ): Promise { return this.queryClient.fetchQuery( this.getPriceV1SupportedNetworksQueryOptions(options), ); @@ -97,11 +98,11 @@ export class PricesApiClient extends BaseApiClient { */ getPriceV2SupportedNetworksQueryOptions( options?: FetchOptions, - ): FetchQueryOptions { + ): FetchQueryOptions { return { queryKey: ['prices', 'v2SupportedNetworks'], queryFn: ({ signal }: QueryFunctionContext) => - this.fetch( + this.fetch( this.apiUrls.PRICES, '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/v2/supportedNetworks', { signal }, @@ -115,12 +116,15 @@ export class PricesApiClient extends BaseApiClient { /** * Get price supported networks in CAIP format (v2 endpoint). * + * `partialSupport` is an object keyed by spot price endpoint + * (`spotPricesV2`, `spotPricesV3`), not a flat array. + * * @param options - Fetch options including cache settings. * @returns The supported networks response. */ async fetchPriceV2SupportedNetworks( options?: FetchOptions, - ): Promise { + ): Promise { return this.queryClient.fetchQuery( this.getPriceV2SupportedNetworksQueryOptions(options), ); diff --git a/packages/core-backend/src/api/prices/index.ts b/packages/core-backend/src/api/prices/index.ts index f8f24383a96..ef4f2ee8042 100644 --- a/packages/core-backend/src/api/prices/index.ts +++ b/packages/core-backend/src/api/prices/index.ts @@ -9,6 +9,8 @@ export type { ExchangeRateInfo, V1ExchangeRatesResponse, PriceSupportedNetworksResponse, + PriceV1SupportedNetworksResponse, + PriceV2SupportedNetworksResponse, V1HistoricalPricesResponse, V3HistoricalPricesResponse, } from './types.js'; diff --git a/packages/core-backend/src/api/prices/types.ts b/packages/core-backend/src/api/prices/types.ts index ee3c5420eab..43ef8b88c3e 100644 --- a/packages/core-backend/src/api/prices/types.ts +++ b/packages/core-backend/src/api/prices/types.ts @@ -62,12 +62,43 @@ export type V1ExchangeRatesResponse = { // SUPPORTED NETWORKS TYPES // ============================================================================ -/** Price supported networks response */ -export type PriceSupportedNetworksResponse = { +/** + * Price v1 supported networks response (`/v1/supportedNetworks`). + * Chain IDs are decimal numbers. + */ +export type PriceV1SupportedNetworksResponse = { + /** Chains supported by every spot price endpoint. */ + fullSupport: number[]; + /** Chains supported only by specific spot price endpoints. */ + partialSupport: { + spotPricesV2: number[]; + }; +}; + +/** + * Price v2 supported networks response (`/v2/supportedNetworks`). + * Chain IDs are CAIP-2 strings (e.g. `eip155:1`). + * + * Note: `partialSupport` is an object keyed by endpoint, not an array. + */ +export type PriceV2SupportedNetworksResponse = { + /** Chains supported by every spot price endpoint. */ fullSupport: string[]; - partialSupport: string[]; + /** Chains supported only by specific spot price endpoints. */ + partialSupport: { + spotPricesV2: string[]; + spotPricesV3: string[]; + }; }; +/** + * Price supported networks response. + * + * @deprecated Use `PriceV2SupportedNetworksResponse` (or + * `PriceV1SupportedNetworksResponse` for the v1 endpoint) instead. + */ +export type PriceSupportedNetworksResponse = PriceV2SupportedNetworksResponse; + // ============================================================================ // HISTORICAL PRICES TYPES // ============================================================================ diff --git a/packages/core-backend/src/index.ts b/packages/core-backend/src/index.ts index be6b1a8c32c..8304f1a0a1a 100644 --- a/packages/core-backend/src/index.ts +++ b/packages/core-backend/src/index.ts @@ -174,6 +174,8 @@ export type { ExchangeRateInfo, V1ExchangeRatesResponse, PriceSupportedNetworksResponse, + PriceV1SupportedNetworksResponse, + PriceV2SupportedNetworksResponse, V1HistoricalPricesResponse, V3HistoricalPricesResponse, // Token API types