From 7266283ed6439d626deb6e8a0dfcf7d63dc288cb Mon Sep 17 00:00:00 2001 From: Brian Bergeron Date: Wed, 20 Nov 2024 14:36:50 -0800 Subject: [PATCH 1/4] zero address --- .../src/TokenRatesController.ts | 6 +++--- .../src/token-prices-service/codefi-v2.ts | 21 +++++++++++++++++-- 2 files changed, 22 insertions(+), 5 deletions(-) diff --git a/packages/assets-controllers/src/TokenRatesController.ts b/packages/assets-controllers/src/TokenRatesController.ts index 908c9b0319..50ffd583c9 100644 --- a/packages/assets-controllers/src/TokenRatesController.ts +++ b/packages/assets-controllers/src/TokenRatesController.ts @@ -27,7 +27,7 @@ import { isEqual } from 'lodash'; import { reduceInBatchesSerially, TOKEN_PRICES_BATCH_SIZE } from './assetsUtil'; import { fetchExchangeRate as fetchNativeCurrencyExchangeRate } from './crypto-compare-service'; import type { AbstractTokenPricesService } from './token-prices-service/abstract-token-prices-service'; -import { ZERO_ADDRESS } from './token-prices-service/codefi-v2'; +import { getNativeTokenAddress } from './token-prices-service/codefi-v2'; import type { TokensControllerGetStateAction, TokensControllerStateChangeEvent, @@ -718,9 +718,9 @@ export class TokenRatesController extends StaticIntervalPollingController = { + '0x89': '0x0000000000000000000000000000000000001010', +}; + +/** + * Returns the address that should be used to query the price api for the + * chain's native token. On most chains, this is signified by the zero address. + * But on some chains, the native token has a specific address. + * @param chainId + */ +export const getNativeTokenAddress = (chainId: Hex): Hex => + chainIdToNativeTokenAddress[chainId] ?? ZERO_ADDRESS; + /** * A currency that can be supplied as the `vsCurrency` parameter to * the `/spot-prices` endpoint. Covers both uppercase and lowercase versions. @@ -435,7 +452,7 @@ export class CodefiTokenPricesServiceV2 const url = new URL(`${BASE_URL}/chains/${chainIdAsNumber}/spot-prices`); url.searchParams.append( 'tokenAddresses', - [ZERO_ADDRESS, ...tokenAddresses].join(','), + [getNativeTokenAddress(chainId), ...tokenAddresses].join(','), ); url.searchParams.append('vsCurrency', currency); url.searchParams.append('includeMarketData', 'true'); @@ -445,7 +462,7 @@ export class CodefiTokenPricesServiceV2 handleFetch(url, { headers: { 'Cache-Control': 'no-cache' } }), ); - return [ZERO_ADDRESS, ...tokenAddresses].reduce( + return [getNativeTokenAddress(chainId), ...tokenAddresses].reduce( ( obj: Partial>, tokenAddress, From c1cd10520720f27e6e7c556eb3882bcd026f2d19 Mon Sep 17 00:00:00 2001 From: Brian Bergeron Date: Wed, 20 Nov 2024 14:41:34 -0800 Subject: [PATCH 2/4] export the function, lint --- packages/assets-controllers/src/index.ts | 1 + .../src/token-prices-service/codefi-v2.ts | 3 ++- .../assets-controllers/src/token-prices-service/index.ts | 6 +++++- 3 files changed, 8 insertions(+), 2 deletions(-) diff --git a/packages/assets-controllers/src/index.ts b/packages/assets-controllers/src/index.ts index 664642bbad..aaba11c846 100644 --- a/packages/assets-controllers/src/index.ts +++ b/packages/assets-controllers/src/index.ts @@ -134,6 +134,7 @@ export { export { CodefiTokenPricesServiceV2, SUPPORTED_CHAIN_IDS, + getNativeTokenAddress, } from './token-prices-service'; export { RatesController, Cryptocurrency } from './RatesController'; export type { 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 e27f584fe1..4f163203e4 100644 --- a/packages/assets-controllers/src/token-prices-service/codefi-v2.ts +++ b/packages/assets-controllers/src/token-prices-service/codefi-v2.ts @@ -168,7 +168,8 @@ const chainIdToNativeTokenAddress: Record = { * Returns the address that should be used to query the price api for the * chain's native token. On most chains, this is signified by the zero address. * But on some chains, the native token has a specific address. - * @param chainId + * @param chainId - The hexadecimal chain id. + * @returns The address of the chain's native token. */ export const getNativeTokenAddress = (chainId: Hex): Hex => chainIdToNativeTokenAddress[chainId] ?? ZERO_ADDRESS; diff --git a/packages/assets-controllers/src/token-prices-service/index.ts b/packages/assets-controllers/src/token-prices-service/index.ts index f6313c36e7..509fc68005 100644 --- a/packages/assets-controllers/src/token-prices-service/index.ts +++ b/packages/assets-controllers/src/token-prices-service/index.ts @@ -1,2 +1,6 @@ export type { AbstractTokenPricesService } from './abstract-token-prices-service'; -export { CodefiTokenPricesServiceV2, SUPPORTED_CHAIN_IDS } from './codefi-v2'; +export { + CodefiTokenPricesServiceV2, + SUPPORTED_CHAIN_IDS, + getNativeTokenAddress, +} from './codefi-v2'; From f6205d9f75714de909e3e435a4263a87cda2d84b Mon Sep 17 00:00:00 2001 From: Brian Bergeron Date: Wed, 20 Nov 2024 14:47:39 -0800 Subject: [PATCH 3/4] fix test --- .../assets-controllers/src/token-prices-service/index.test.ts | 1 + 1 file changed, 1 insertion(+) 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 a5a1e93f7a..a59be2ba4d 100644 --- a/packages/assets-controllers/src/token-prices-service/index.test.ts +++ b/packages/assets-controllers/src/token-prices-service/index.test.ts @@ -6,6 +6,7 @@ describe('token-prices-service', () => { Array [ "CodefiTokenPricesServiceV2", "SUPPORTED_CHAIN_IDS", + "getNativeTokenAddress", ] `); }); From 6f3061d23e1eef8ea65e12a3b1a6629411cd9a52 Mon Sep 17 00:00:00 2001 From: Prithpal Sooriya Date: Fri, 22 Nov 2024 15:05:04 +0000 Subject: [PATCH 4/4] test: add tests to cover new changes --- .../src/TokenRatesController.test.ts | 49 +++++++++++++++ .../token-prices-service/codefi-v2.test.ts | 60 +++++++++++++++++++ 2 files changed, 109 insertions(+) diff --git a/packages/assets-controllers/src/TokenRatesController.test.ts b/packages/assets-controllers/src/TokenRatesController.test.ts index d6daaabc22..4648151769 100644 --- a/packages/assets-controllers/src/TokenRatesController.test.ts +++ b/packages/assets-controllers/src/TokenRatesController.test.ts @@ -2348,6 +2348,55 @@ describe('TokenRatesController', () => { ); }); + it('correctly calls the Price API with unqiue native token addresses (e.g. MATIC)', async () => { + const tokenPricesService = buildMockTokenPricesService({ + fetchTokenPrices: jest.fn().mockResolvedValue({ + '0x0000000000000000000000000000000000001010': { + currency: 'MATIC', + tokenAddress: '0x0000000000000000000000000000000000001010', + value: 0.001, + }, + }), + }); + + await withController( + { + options: { tokenPricesService }, + mockNetworkClientConfigurationsByNetworkClientId: { + 'AAAA-BBBB-CCCC-DDDD': buildCustomNetworkClientConfiguration({ + chainId: '0x89', + }), + }, + }, + async ({ + controller, + triggerTokensStateChange, + triggerNetworkStateChange, + }) => { + await callUpdateExchangeRatesMethod({ + allTokens: { + '0x89': { + [defaultSelectedAddress]: [], + }, + }, + chainId: '0x89', + controller, + triggerTokensStateChange, + triggerNetworkStateChange, + method, + nativeCurrency: 'MATIC', + selectedNetworkClientId: 'AAAA-BBBB-CCCC-DDDD', + }); + + expect( + controller.state.marketData['0x89'][ + '0x0000000000000000000000000000000000001010' + ], + ).toBeDefined(); + }, + ); + }); + it('only updates rates once when called twice', async () => { const tokenAddresses = [ '0x0000000000000000000000000000000000000001', 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 2ebaa0d219..e1efe858ec 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 @@ -5,6 +5,8 @@ import { CodefiTokenPricesServiceV2, SUPPORTED_CHAIN_IDS, SUPPORTED_CURRENCIES, + ZERO_ADDRESS, + getNativeTokenAddress, } from './codefi-v2'; // We're not customizing the default max delay @@ -208,6 +210,51 @@ describe('CodefiTokenPricesServiceV2', () => { }); }); + it('calls the /spot-prices endpoint using the correct native token address', async () => { + const mockPriceAPI = nock('https://price.api.cx.metamask.io') + .get('/v2/chains/137/spot-prices') + .query({ + tokenAddresses: '0x0000000000000000000000000000000000001010', + vsCurrency: 'ETH', + includeMarketData: 'true', + }) + .reply(200, { + '0x0000000000000000000000000000000000001010': { + price: 14, + currency: 'ETH', + pricePercentChange1d: 1, + priceChange1d: 1, + marketCap: 117219.99428314982, + allTimeHigh: 0.00060467892389492, + allTimeLow: 0.00002303954000865728, + totalVolume: 5155.094053542448, + high1d: 0.00008020715848194385, + low1d: 0.00007792083564549064, + circulatingSupply: 1494269733.9526057, + dilutedMarketCap: 117669.5125951733, + marketCapPercentChange1d: 0.76671, + pricePercentChange1h: -1.0736342953259423, + pricePercentChange7d: -7.351582573655089, + pricePercentChange14d: -1.0799098946709822, + pricePercentChange30d: -25.776321124365992, + pricePercentChange200d: 46.091571238599165, + pricePercentChange1y: -2.2992517267242754, + }, + }); + + const marketData = + await new CodefiTokenPricesServiceV2().fetchTokenPrices({ + chainId: '0x89', + tokenAddresses: [], + currency: 'ETH', + }); + + expect(mockPriceAPI.isDone()).toBe(true); + expect( + marketData['0x0000000000000000000000000000000000001010'], + ).toBeDefined(); + }); + it('should not include token price object for token address when token price in not included the response data', async () => { nock('https://price.api.cx.metamask.io') .get('/v2/chains/1/spot-prices') @@ -1960,6 +2007,19 @@ describe('CodefiTokenPricesServiceV2', () => { ).toBe(false); }); }); + + describe('getNativeTokenAddress', () => { + it('should return unique native token address for MATIC', () => { + expect(getNativeTokenAddress('0x89')).toBe( + '0x0000000000000000000000000000000000001010', + ); + }); + it('should return zero address for other chains', () => { + (['0x1', '0x2', '0x1337'] as const).forEach((chainId) => { + expect(getNativeTokenAddress(chainId)).toBe(ZERO_ADDRESS); + }); + }); + }); }); /**