From 967a5b656144b02c13340717bae61507cfec6edd Mon Sep 17 00:00:00 2001 From: "dawnseeker8@gmail.com" Date: Tue, 23 Dec 2025 16:27:22 +0800 Subject: [PATCH 01/16] feat: fake the rwaData returned from backend to make sure it work for stock label. --- .../assets-controllers/src/token-service.ts | 86 ++++++++++++++++--- packages/bridge-controller/src/utils/fetch.ts | 17 +++- .../bridge-controller/src/utils/validators.ts | 16 ++++ 3 files changed, 108 insertions(+), 11 deletions(-) diff --git a/packages/assets-controllers/src/token-service.ts b/packages/assets-controllers/src/token-service.ts index 4672a817572..3a81c22c483 100644 --- a/packages/assets-controllers/src/token-service.ts +++ b/packages/assets-controllers/src/token-service.ts @@ -8,7 +8,9 @@ import type { CaipChainId, Hex } from '@metamask/utils'; import { isTokenListSupportedForNetwork } from './assetsUtil'; -export const TOKEN_END_POINT_API = 'https://token.api.cx.metamask.io'; +// export const TOKEN_END_POINT_API = 'https://token.api.cx.metamask.io'; +// TODO change it back after development is done +export const TOKEN_END_POINT_API = 'https://token.dev-api.cx.metamask.io'; export const TOKEN_METADATA_NO_SUPPORT_ERROR = 'TokenService Error: Network does not support fetchTokenMetadata'; @@ -18,7 +20,7 @@ export const TOKEN_METADATA_NO_SUPPORT_ERROR = * @param chainId - The chain ID of the network the tokens requested are on. * @returns The tokens URL. */ -function getTokensURL(chainId: Hex) { +function getTokensURL(chainId: Hex): string { const occurrenceFloor = chainId === ChainId['linea-mainnet'] ? 1 : 3; return `${TOKEN_END_POINT_API}/tokens/${convertHexToDecimal( @@ -33,7 +35,7 @@ function getTokensURL(chainId: Hex) { * @param tokenAddress - The token address. * @returns The token metadata URL. */ -function getTokenMetadataURL(chainId: Hex, tokenAddress: string) { +function getTokenMetadataURL(chainId: Hex, tokenAddress: string): string { return `${TOKEN_END_POINT_API}/token/${convertHexToDecimal( chainId, )}?address=${tokenAddress}`; @@ -62,12 +64,12 @@ function getTokenSearchURL( query: string, limit = 10, includeMarketData = false, -) { +): string { const encodedQuery = encodeURIComponent(query); const encodedChainIds = chainIds .map((id) => encodeURIComponent(id)) .join(','); - return `${TOKEN_END_POINT_API}/tokens/search?networks=${encodedChainIds}&query=${encodedQuery}&limit=${limit}&includeMarketData=${includeMarketData}`; + return `${TOKEN_END_POINT_API}/tokens/search?networks=${encodedChainIds}&query=${encodedQuery}&limit=${limit}&includeMarketData=${includeMarketData}&includeRwaData=true`; } /** @@ -142,16 +144,42 @@ export async function fetchTokenListByChainId( if (response) { const result = await parseJsonResponse(response); if (Array.isArray(result) && chainId === ChainId['linea-mainnet']) { - return result.filter( + const filteredResult = result.filter( (elm) => - elm.aggregators.includes('lineaTeam') || elm.aggregators.length >= 3, + elm.aggregators.includes('lineaTeam') ?? elm.aggregators.length >= 3, ); + // TODO: remove this after development is done + // if the filteredResult has an aggregator that includes 'Ondo' then append rwaData as metadata. + const filteredResultWithRwaData = filteredResult.map((elm) => { + const metadata = { + rwaData: { + instrumentType: 'stock', + ticker: elm.name?.split(' ')[0] ?? '', + market: { + nextOpen: new Date(new Date().setHours(9, 0, 0, 0)).toISOString(), + nextClose: new Date(new Date().setHours(16, 0, 0, 0)).toISOString(), + }, + nextPause: { + start: new Date(new Date().setHours(16, 0, 0, 0)).toISOString(), + end: new Date(new Date().setHours(17, 0, 0, 0)).toISOString(), + }, + }, + }; + if (elm.aggregators.includes('Ondo')) { + return { ...elm, ...metadata }; + } + return elm; + }); + + console.log('filteredResult', filteredResult); + return filteredResultWithRwaData; } return result; } return undefined; } +// TODO This end point already contain RwaData, so we don't need to append it here. /** * Search for tokens across one or more networks by query string using CAIP format chain IDs. * @@ -180,7 +208,7 @@ export async function searchTokens( // The API returns an object with structure: { count: number, data: array, pageInfo: object } if (result && typeof result === 'object' && Array.isArray(result.data)) { return { - count: result.count || result.data.length, + count: result.count ?? result.data.length, data: result.data, }; } @@ -271,7 +299,21 @@ export async function getTrendingTokens({ // Validate that the API returned an array if (Array.isArray(result)) { - return result; + // TODO hack the results to include RwaData + const filteredResultWithRwaData = result.map((elm) => { + const metadata = { + rwaData: { + instrumentType: 'stock', + ticker: elm.name?.split(' ')[0] ?? '', + market: { + nextOpen: new Date(new Date().setHours(9, 0, 0, 0)).toISOString(), + nextClose: new Date(new Date().setHours(16, 0, 0, 0)).toISOString(), + }, + }, + }; + return { ...elm, ...metadata }; + }); + return filteredResultWithRwaData; } // Handle non-expected responses @@ -306,7 +348,31 @@ export async function fetchTokenMetadata( const tokenMetadataURL = getTokenMetadataURL(chainId, tokenAddress); const response = await queryApi(tokenMetadataURL, abortSignal, timeout); if (response) { - return parseJsonResponse(response) as Promise; + const result = await parseJsonResponse(response); + if (Array.isArray(result)) { + const filteredResultWithRwaData = result.map((elm) => { + const metadata = { + rwaData: { + instrumentType: 'stock', + ticker: elm.name?.split(' ')[0] ?? '', + market: { + nextOpen: new Date(new Date().setHours(9, 0, 0, 0)).toISOString(), + nextClose: new Date(new Date().setHours(16, 0, 0, 0)).toISOString(), + }, + nextPause: { + start: new Date(new Date().setHours(16, 0, 0, 0)).toISOString(), + end: new Date(new Date().setHours(17, 0, 0, 0)).toISOString(), + }, + }, + }; + if (elm.aggregators.includes('Ondo')) { + return { ...elm, ...metadata }; + } + return elm; + }); + return filteredResultWithRwaData as unknown as T; + } + return result as T; } return undefined; } diff --git a/packages/bridge-controller/src/utils/fetch.ts b/packages/bridge-controller/src/utils/fetch.ts index 5a391c777f3..bc8bb0753e1 100644 --- a/packages/bridge-controller/src/utils/fetch.ts +++ b/packages/bridge-controller/src/utils/fetch.ts @@ -53,7 +53,22 @@ export async function fetchBridgeTokens( const transformedTokens: Record = {}; tokens.forEach((token: unknown) => { if (validateSwapsTokenObject(token)) { - transformedTokens[token.address] = token; + // TODO hack the results to include RwaData + const metadata = { + rwaData: { + instrumentType: 'stock', + ticker: token.name?.split(' ')[0] ?? '', + market: { + nextOpen: new Date(new Date().setHours(9, 0, 0, 0)).toISOString(), + nextClose: new Date(new Date().setHours(16, 0, 0, 0)).toISOString(), + }, + nextPause: { + start: new Date(new Date().setHours(16, 0, 0, 0)).toISOString(), + end: new Date(new Date().setHours(17, 0, 0, 0)).toISOString(), + }, + }, + }; + transformedTokens[token.address] = { ...token, ...metadata }; } }); return transformedTokens; diff --git a/packages/bridge-controller/src/utils/validators.ts b/packages/bridge-controller/src/utils/validators.ts index 327a3f5eee1..f256b9cf68a 100644 --- a/packages/bridge-controller/src/utils/validators.ts +++ b/packages/bridge-controller/src/utils/validators.ts @@ -16,6 +16,7 @@ import { assert, pattern, intersection, + date, } from '@metamask/superstruct'; import { CaipAssetTypeStruct, isStrictHexString } from '@metamask/utils'; @@ -86,6 +87,21 @@ export const BridgeAssetSchema = type({ * URL for token icon */ iconUrl: optional(nullable(string())), + + rwaData: optional( + type({ + instrumentType: string(), + ticker: string(), + market: type({ + nextOpen: string(), + nextClose: string(), + }), + nextPause: type({ + start: string(), + end: string(), + }), + }), + ), }); const DefaultPairSchema = type({ From 7e9d704a93e5d2b4d51e070d8accac6eb1a5e0c8 Mon Sep 17 00:00:00 2001 From: "dawnseeker8@gmail.com" Date: Wed, 24 Dec 2025 16:50:23 +0800 Subject: [PATCH 02/16] Add some type definition to fix home asset rwaData lost. --- .../src/TokenListController.ts | 12 ++++ .../assets-controllers/src/token-service.ts | 58 ++++++++++++++++++- 2 files changed, 67 insertions(+), 3 deletions(-) diff --git a/packages/assets-controllers/src/TokenListController.ts b/packages/assets-controllers/src/TokenListController.ts index 6ee57c476bb..10e01b3a295 100644 --- a/packages/assets-controllers/src/TokenListController.ts +++ b/packages/assets-controllers/src/TokenListController.ts @@ -34,6 +34,18 @@ export type TokenListToken = { occurrences: number; aggregators: string[]; iconUrl: string; + rwaData: { + instrumentType: string; + ticker: string; + market: { + nextOpen: string; + nextClose: string; + }; + nextPause: { + start: string; + end: string; + }; + }; }; export type TokenListMap = Record; diff --git a/packages/assets-controllers/src/token-service.ts b/packages/assets-controllers/src/token-service.ts index 3a81c22c483..fc714520d5f 100644 --- a/packages/assets-controllers/src/token-service.ts +++ b/packages/assets-controllers/src/token-service.ts @@ -157,7 +157,9 @@ export async function fetchTokenListByChainId( ticker: elm.name?.split(' ')[0] ?? '', market: { nextOpen: new Date(new Date().setHours(9, 0, 0, 0)).toISOString(), - nextClose: new Date(new Date().setHours(16, 0, 0, 0)).toISOString(), + nextClose: new Date( + new Date().setHours(16, 0, 0, 0), + ).toISOString(), }, nextPause: { start: new Date(new Date().setHours(16, 0, 0, 0)).toISOString(), @@ -174,6 +176,36 @@ export async function fetchTokenListByChainId( console.log('filteredResult', filteredResult); return filteredResultWithRwaData; } + + if (Array.isArray(result)) { + // TODO: remove this after development is done + // if the filteredResult has an aggregator that includes 'Ondo' then append rwaData as metadata. + const filteredResultWithRwaData = result.map((elm) => { + const metadata = { + rwaData: { + instrumentType: 'stock', + ticker: elm.name?.split(' ')[0] ?? '', + market: { + nextOpen: new Date(new Date().setHours(9, 0, 0, 0)).toISOString(), + nextClose: new Date( + new Date().setHours(16, 0, 0, 0), + ).toISOString(), + }, + nextPause: { + start: new Date(new Date().setHours(16, 0, 0, 0)).toISOString(), + end: new Date(new Date().setHours(17, 0, 0, 0)).toISOString(), + }, + }, + }; + if (elm.aggregators.includes('Ondo')) { + return { ...elm, ...metadata }; + } + return elm; + }); + + console.log('filteredResultWithRwaData', filteredResultWithRwaData); + return filteredResultWithRwaData; + } return result; } return undefined; @@ -242,6 +274,18 @@ export type TrendingAsset = { h24?: string; }; labels?: string[]; + rwaData?: { + instrumentType: string; + ticker: string; + market: { + nextOpen: string; + nextClose: string; + }; + nextPause: { + start: string; + end: string; + }; + }; }; /** @@ -307,7 +351,13 @@ export async function getTrendingTokens({ ticker: elm.name?.split(' ')[0] ?? '', market: { nextOpen: new Date(new Date().setHours(9, 0, 0, 0)).toISOString(), - nextClose: new Date(new Date().setHours(16, 0, 0, 0)).toISOString(), + nextClose: new Date( + new Date().setHours(16, 0, 0, 0), + ).toISOString(), + }, + nextPause: { + start: new Date(new Date().setHours(16, 0, 0, 0)).toISOString(), + end: new Date(new Date().setHours(17, 0, 0, 0)).toISOString(), }, }, }; @@ -357,7 +407,9 @@ export async function fetchTokenMetadata( ticker: elm.name?.split(' ')[0] ?? '', market: { nextOpen: new Date(new Date().setHours(9, 0, 0, 0)).toISOString(), - nextClose: new Date(new Date().setHours(16, 0, 0, 0)).toISOString(), + nextClose: new Date( + new Date().setHours(16, 0, 0, 0), + ).toISOString(), }, nextPause: { start: new Date(new Date().setHours(16, 0, 0, 0)).toISOString(), From 16a22306451c8ad37f4a1c87260a1e852f01b956 Mon Sep 17 00:00:00 2001 From: "dawnseeker8@gmail.com" Date: Wed, 24 Dec 2025 17:17:54 +0800 Subject: [PATCH 03/16] Fix the lint issue. --- packages/bridge-controller/src/utils/validators.ts | 1 - 1 file changed, 1 deletion(-) diff --git a/packages/bridge-controller/src/utils/validators.ts b/packages/bridge-controller/src/utils/validators.ts index f256b9cf68a..bbad4569f77 100644 --- a/packages/bridge-controller/src/utils/validators.ts +++ b/packages/bridge-controller/src/utils/validators.ts @@ -16,7 +16,6 @@ import { assert, pattern, intersection, - date, } from '@metamask/superstruct'; import { CaipAssetTypeStruct, isStrictHexString } from '@metamask/utils'; From 42084fe4fbd1c2c58135dec9d10afafec352aedd Mon Sep 17 00:00:00 2001 From: "dawnseeker8@gmail.com" Date: Wed, 24 Dec 2025 17:24:14 +0800 Subject: [PATCH 04/16] make rwaData as optional --- packages/assets-controllers/src/TokenListController.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/assets-controllers/src/TokenListController.ts b/packages/assets-controllers/src/TokenListController.ts index 10e01b3a295..6b2ecca89c9 100644 --- a/packages/assets-controllers/src/TokenListController.ts +++ b/packages/assets-controllers/src/TokenListController.ts @@ -34,7 +34,7 @@ export type TokenListToken = { occurrences: number; aggregators: string[]; iconUrl: string; - rwaData: { + rwaData?: { instrumentType: string; ticker: string; market: { From 759c20fcdb5a44d94f2416609062ce8f7a02ef4e Mon Sep 17 00:00:00 2001 From: "dawnseeker8@gmail.com" Date: Mon, 5 Jan 2026 10:38:33 +0800 Subject: [PATCH 05/16] feat: add RWAData into tokens controller so that data can be shown in frontend. --- .../src/TokenDetectionController.ts | 11 ++++++++--- .../assets-controllers/src/TokenRatesController.ts | 12 ++++++++++++ packages/assets-controllers/src/TokensController.ts | 6 +++++- 3 files changed, 25 insertions(+), 4 deletions(-) diff --git a/packages/assets-controllers/src/TokenDetectionController.ts b/packages/assets-controllers/src/TokenDetectionController.ts index f16b34f7007..2e43b98b1d5 100644 --- a/packages/assets-controllers/src/TokenDetectionController.ts +++ b/packages/assets-controllers/src/TokenDetectionController.ts @@ -752,7 +752,7 @@ export class TokenDetectionController extends StaticIntervalPollingController token.address.toLowerCase() === address.toLowerCase(), @@ -525,7 +526,7 @@ export class TokensController extends BaseController< }, {}); try { tokensToImport.forEach((tokenToAdd) => { - const { address, symbol, decimals, image, aggregators, name } = + const { address, symbol, decimals, image, aggregators, name, rwaData } = tokenToAdd; const checksumAddress = toChecksumHexAddress(address); const formattedToken: Token = { @@ -535,6 +536,7 @@ export class TokensController extends BaseController< image, aggregators, name, + rwaData, }; newTokensMap[checksumAddress] = formattedToken; importedTokensMap[address.toLowerCase()] = true; @@ -664,6 +666,7 @@ export class TokensController extends BaseController< aggregators, isERC721, name, + rwaData, } = tokenToAdd; const checksumAddress = toChecksumHexAddress(address); const newEntry: Token = { @@ -674,6 +677,7 @@ export class TokensController extends BaseController< isERC721, aggregators, name, + rwaData, }; const previousImportedIndex = newTokens.findIndex( From b218b3e77a0ec72824e3c35d85fcf969de5e39b4 Mon Sep 17 00:00:00 2001 From: "dawnseeker8@gmail.com" Date: Wed, 7 Jan 2026 17:10:47 +0800 Subject: [PATCH 06/16] make a backup and then revert to main version to see anything i did. --- .../src/TokenListController.ts | 12 ++++---- .../assets-controllers/src/token-service.ts | 30 +++++++++---------- 2 files changed, 22 insertions(+), 20 deletions(-) diff --git a/packages/assets-controllers/src/TokenListController.ts b/packages/assets-controllers/src/TokenListController.ts index 6b2ecca89c9..864bbb57c99 100644 --- a/packages/assets-controllers/src/TokenListController.ts +++ b/packages/assets-controllers/src/TokenListController.ts @@ -201,7 +201,9 @@ export class TokenListController extends StaticIntervalPollingController { const selectedNetworkClient = this.messenger.call( 'NetworkController:getNetworkClientById', networkControllerState.selectedNetworkClientId, @@ -226,7 +228,7 @@ export class TokenListController extends StaticIntervalPollingController { if (!isTokenListSupportedForNetwork(this.chainId)) { return; } @@ -239,7 +241,7 @@ export class TokenListController extends StaticIntervalPollingController { this.stopPolling(); await this.#startDeprecatedPolling(); } @@ -250,7 +252,7 @@ export class TokenListController extends StaticIntervalPollingController elm.aggregators.includes('lineaTeam') ?? elm.aggregators.length >= 3, - Boolean(elm.aggregators.includes('lineaTeam')) || - elm.aggregators.length >= 3, ); // TODO: remove this after development is done // if the filteredResult has an aggregator that includes 'Ondo' then append rwaData as metadata. @@ -181,10 +181,10 @@ export async function fetchTokenListByChainId( }, }, }; - if (elm.aggregators.includes('Ondo')) { - return { ...elm, ...metadata }; - } - return elm; + // if (elm.aggregators.includes('Ondo')) { + return { ...elm, ...metadata }; + // } + // return elm; }); console.log('filteredResult', filteredResult); @@ -211,10 +211,10 @@ export async function fetchTokenListByChainId( }, }, }; - if (elm.aggregators.includes('Ondo')) { - return { ...elm, ...metadata }; - } - return elm; + // if (elm.aggregators.includes('Ondo')) { + return { ...elm, ...metadata }; + // } + // return elm; }); console.log('filteredResultWithRwaData', filteredResultWithRwaData); @@ -279,7 +279,7 @@ export async function searchTokens( query, limit, includeMarketData, - includeRwaData, + includeRwaData: true, }); try { @@ -465,7 +465,7 @@ export async function fetchTokenMetadata( } return elm; }); - return filteredResultWithRwaData as unknown as T; + return filteredResultWithRwaData as unknown as TReturn; } return parseJsonResponse(response) as Promise; } From 92768acebfb7d41e801557c2aec7d374979ebf77 Mon Sep 17 00:00:00 2001 From: "dawnseeker8@gmail.com" Date: Wed, 7 Jan 2026 18:11:39 +0800 Subject: [PATCH 07/16] Pass rwaData to frontend --- packages/assets-controllers/src/selectors/token-selectors.ts | 2 ++ 1 file changed, 2 insertions(+) diff --git a/packages/assets-controllers/src/selectors/token-selectors.ts b/packages/assets-controllers/src/selectors/token-selectors.ts index a1772dfa6cd..9dada974087 100644 --- a/packages/assets-controllers/src/selectors/token-selectors.ts +++ b/packages/assets-controllers/src/selectors/token-selectors.ts @@ -8,6 +8,7 @@ import type { NetworkState } from '@metamask/network-controller'; import { hexToBigInt, parseCaipAssetType } from '@metamask/utils'; import type { Hex } from '@metamask/utils'; import { createSelector, weakMapMemoize } from 'reselect'; +import { TokenRwaData } from 'src/token-service'; import { parseBalanceWithDecimals, @@ -84,6 +85,7 @@ export type Asset = ( conversionRate: number; } | undefined; + rwaData?: TokenRwaData; }; export type AssetListState = { From 045a6667acd3513153e101e469216239166482f5 Mon Sep 17 00:00:00 2001 From: "dawnseeker8@gmail.com" Date: Thu, 8 Jan 2026 17:34:19 +0800 Subject: [PATCH 08/16] fix some lint tissue --- .../src/TokenDetectionController.ts | 6 +++--- .../src/TokensController.test.ts | 2 +- .../assets-controllers/src/TokensController.ts | 6 +++--- .../assets-controllers/src/token-service.ts | 7 +------ packages/bridge-controller/src/utils/fetch.ts | 17 +---------------- 5 files changed, 9 insertions(+), 29 deletions(-) diff --git a/packages/assets-controllers/src/TokenDetectionController.ts b/packages/assets-controllers/src/TokenDetectionController.ts index 2e43b98b1d5..464ed175d23 100644 --- a/packages/assets-controllers/src/TokenDetectionController.ts +++ b/packages/assets-controllers/src/TokenDetectionController.ts @@ -763,7 +763,7 @@ export class TokenDetectionController extends StaticIntervalPollingController { .get( `/token/${convertHexToDecimal( chainId, - )}?address=${dummyTokenAddress}`, + )}?address=${dummyTokenAddress}&includeRwaData=true`, ) .reply(200, { error }) .persist(); diff --git a/packages/assets-controllers/src/TokensController.ts b/packages/assets-controllers/src/TokensController.ts index 409a4152eb5..672bbb34291 100644 --- a/packages/assets-controllers/src/TokensController.ts +++ b/packages/assets-controllers/src/TokensController.ts @@ -457,7 +457,7 @@ export class TokensController extends BaseController< isERC721, aggregators: formatAggregatorNames(tokenMetadata?.aggregators || []), name, - rwaData: tokenMetadata?.rwaData, + ...(tokenMetadata?.rwaData && { rwaData: tokenMetadata.rwaData }), }; const previousIndex = newTokens.findIndex( (token) => token.address.toLowerCase() === address.toLowerCase(), @@ -536,7 +536,7 @@ export class TokensController extends BaseController< image, aggregators, name, - rwaData, + ...(rwaData && { rwaData }), }; newTokensMap[checksumAddress] = formattedToken; importedTokensMap[address.toLowerCase()] = true; @@ -677,7 +677,7 @@ export class TokensController extends BaseController< isERC721, aggregators, name, - rwaData, + ...(rwaData && { rwaData }), }; const previousImportedIndex = newTokens.findIndex( diff --git a/packages/assets-controllers/src/token-service.ts b/packages/assets-controllers/src/token-service.ts index 4e7a69c361f..54fd922b69a 100644 --- a/packages/assets-controllers/src/token-service.ts +++ b/packages/assets-controllers/src/token-service.ts @@ -268,18 +268,13 @@ type SearchTokenOptions = { export async function searchTokens( chainIds: CaipChainId[], query: string, - { - limit = 10, - includeMarketData = false, - includeRwaData, - }: SearchTokenOptions = {}, + { limit = 10, includeMarketData = false }: SearchTokenOptions = {}, ): Promise<{ count: number; data: TokenSearchItem[] }> { const tokenSearchURL = getTokenSearchURL({ chainIds, query, limit, includeMarketData, - includeRwaData: true, }); try { diff --git a/packages/bridge-controller/src/utils/fetch.ts b/packages/bridge-controller/src/utils/fetch.ts index bc8bb0753e1..5a391c777f3 100644 --- a/packages/bridge-controller/src/utils/fetch.ts +++ b/packages/bridge-controller/src/utils/fetch.ts @@ -53,22 +53,7 @@ export async function fetchBridgeTokens( const transformedTokens: Record = {}; tokens.forEach((token: unknown) => { if (validateSwapsTokenObject(token)) { - // TODO hack the results to include RwaData - const metadata = { - rwaData: { - instrumentType: 'stock', - ticker: token.name?.split(' ')[0] ?? '', - market: { - nextOpen: new Date(new Date().setHours(9, 0, 0, 0)).toISOString(), - nextClose: new Date(new Date().setHours(16, 0, 0, 0)).toISOString(), - }, - nextPause: { - start: new Date(new Date().setHours(16, 0, 0, 0)).toISOString(), - end: new Date(new Date().setHours(17, 0, 0, 0)).toISOString(), - }, - }, - }; - transformedTokens[token.address] = { ...token, ...metadata }; + transformedTokens[token.address] = token; } }); return transformedTokens; From 2f42013ab99cee78c4983f3f812edbd3c31624a1 Mon Sep 17 00:00:00 2001 From: "dawnseeker8@gmail.com" Date: Thu, 8 Jan 2026 17:42:07 +0800 Subject: [PATCH 09/16] fix the lint issue of documentation comments --- packages/assets-controllers/src/token-service.ts | 1 - 1 file changed, 1 deletion(-) diff --git a/packages/assets-controllers/src/token-service.ts b/packages/assets-controllers/src/token-service.ts index 54fd922b69a..1fbe34d1ab9 100644 --- a/packages/assets-controllers/src/token-service.ts +++ b/packages/assets-controllers/src/token-service.ts @@ -262,7 +262,6 @@ type SearchTokenOptions = { * @param options - Additional fetch options. * @param options.limit - The maximum number of results to return. * @param options.includeMarketData - Optional flag to include market data in the results (defaults to false). - * @param options.includeRwaData - Optional flag to include RWA data in the results (defaults to false). * @returns Object containing count and data array. Returns { count: 0, data: [] } if request fails. */ export async function searchTokens( From a5d4b43494be89cbcbc7357f12d41ca37db726d5 Mon Sep 17 00:00:00 2001 From: "dawnseeker8@gmail.com" Date: Fri, 9 Jan 2026 14:29:58 +0800 Subject: [PATCH 10/16] Revert the version to main version and add rwaData , also fix some yarn lint issue. --- .../assets-controllers/src/token-service.ts | 171 +++--------------- 1 file changed, 24 insertions(+), 147 deletions(-) diff --git a/packages/assets-controllers/src/token-service.ts b/packages/assets-controllers/src/token-service.ts index 1fbe34d1ab9..5b8ef2a5bde 100644 --- a/packages/assets-controllers/src/token-service.ts +++ b/packages/assets-controllers/src/token-service.ts @@ -8,9 +8,7 @@ import type { CaipAssetType, CaipChainId, Hex } from '@metamask/utils'; import { isTokenListSupportedForNetwork } from './assetsUtil'; -// export const TOKEN_END_POINT_API = 'https://token.api.cx.metamask.io'; -// TODO change it back after development is done -export const TOKEN_END_POINT_API = 'https://token.dev-api.cx.metamask.io'; +export const TOKEN_END_POINT_API = 'https://token.api.cx.metamask.io'; export const TOKEN_METADATA_NO_SUPPORT_ERROR = 'TokenService Error: Network does not support fetchTokenMetadata'; @@ -53,33 +51,23 @@ export type SortTrendingBy = /** * Get the token search URL for the given networks and search query. * - * @param options - Options for getting token search URL. - * @param options.chainIds - Array of CAIP format chain IDs (e.g., 'eip155:1', 'solana:5eykt4UsFv8P8NJdTREpY1vzqKqZKvdp'). - * @param options.query - The search query (token name, symbol, or address). - * @param options.limit - Optional limit for the number of results (defaults to 10). - * @param options.includeMarketData - Optional flag to include market data in the results (defaults to false). - * @param options.includeRwaData - Optional flag to include RWA data in the results (defaults to false). + * @param chainIds - Array of CAIP format chain IDs (e.g., 'eip155:1', 'solana:5eykt4UsFv8P8NJdTREpY1vzqKqZKvdp'). + * @param query - The search query (token name, symbol, or address). + * @param limit - Optional limit for the number of results (defaults to 10). + * @param includeMarketData - Optional flag to include market data in the results (defaults to false). * @returns The token search URL. */ -function getTokenSearchURL(options: { - chainIds: CaipChainId[]; - query: string; - limit?: number; - includeMarketData?: boolean; - includeRwaData?: true; -}): string { - const { chainIds, query, ...optionalParams } = options; +function getTokenSearchURL( + chainIds: CaipChainId[], + query: string, + limit = 10, + includeMarketData = false, +): string { const encodedQuery = encodeURIComponent(query); const encodedChainIds = chainIds .map((id) => encodeURIComponent(id)) .join(','); - const queryParams = new URLSearchParams(); - Object.entries(optionalParams).forEach(([key, value]) => { - if (value !== undefined) { - queryParams.append(key, String(value)); - } - }); - return `${TOKEN_END_POINT_API}/tokens/search?networks=${encodedChainIds}&query=${encodedQuery}&${queryParams.toString()}`; + return `${TOKEN_END_POINT_API}/tokens/search?networks=${encodedChainIds}&query=${encodedQuery}&limit=${limit}&includeMarketData=${includeMarketData}&includeRwaData=true`; } /** @@ -93,8 +81,7 @@ function getTokenSearchURL(options: { * @param options.maxVolume24hUsd - The maximum volume 24h in USD. * @param options.minMarketCap - The minimum market cap. * @param options.maxMarketCap - The maximum market cap. - * @param options.excludeLabels - Array of labels to exclude (e.g., ['stable_coin', 'blue_chip']). - * @param options.includeRwaData - Optional flag to include RWA data in the results (defaults to false). + * @param options.excludeLabels - Array of labels to exclude (e.g., ['stable_coin', 'blue_chip']).- Optional flag to include RWA data in the results (defaults to false). * @returns The trending tokens URL. */ function getTrendingTokensURL(options: { @@ -106,7 +93,6 @@ function getTrendingTokensURL(options: { minMarketCap?: number; maxMarketCap?: number; excludeLabels?: string[]; - includeRwaData?: boolean; }): string { const encodedChainIds = options.chainIds .map((id) => encodeURIComponent(id)) @@ -158,67 +144,10 @@ export async function fetchTokenListByChainId( if (response) { const result = await parseJsonResponse(response); if (Array.isArray(result) && chainId === ChainId['linea-mainnet']) { - const filteredResult = result.filter( + return result.filter( (elm) => elm.aggregators.includes('lineaTeam') ?? elm.aggregators.length >= 3, ); - // TODO: remove this after development is done - // if the filteredResult has an aggregator that includes 'Ondo' then append rwaData as metadata. - const filteredResultWithRwaData = filteredResult.map((elm) => { - const metadata = { - rwaData: { - instrumentType: 'stock', - ticker: elm.name?.split(' ')[0] ?? '', - market: { - nextOpen: new Date(new Date().setHours(9, 0, 0, 0)).toISOString(), - nextClose: new Date( - new Date().setHours(16, 0, 0, 0), - ).toISOString(), - }, - nextPause: { - start: new Date(new Date().setHours(16, 0, 0, 0)).toISOString(), - end: new Date(new Date().setHours(17, 0, 0, 0)).toISOString(), - }, - }, - }; - // if (elm.aggregators.includes('Ondo')) { - return { ...elm, ...metadata }; - // } - // return elm; - }); - - console.log('filteredResult', filteredResult); - return filteredResultWithRwaData; - } - - if (Array.isArray(result)) { - // TODO: remove this after development is done - // if the filteredResult has an aggregator that includes 'Ondo' then append rwaData as metadata. - const filteredResultWithRwaData = result.map((elm) => { - const metadata = { - rwaData: { - instrumentType: 'stock', - ticker: elm.name?.split(' ')[0] ?? '', - market: { - nextOpen: new Date(new Date().setHours(9, 0, 0, 0)).toISOString(), - nextClose: new Date( - new Date().setHours(16, 0, 0, 0), - ).toISOString(), - }, - nextPause: { - start: new Date(new Date().setHours(16, 0, 0, 0)).toISOString(), - end: new Date(new Date().setHours(17, 0, 0, 0)).toISOString(), - }, - }, - }; - // if (elm.aggregators.includes('Ondo')) { - return { ...elm, ...metadata }; - // } - // return elm; - }); - - console.log('filteredResultWithRwaData', filteredResultWithRwaData); - return filteredResultWithRwaData; } return result; } @@ -248,12 +177,6 @@ export type TokenSearchItem = { rwaData?: TokenRwaData; }; -type SearchTokenOptions = { - limit?: number; - includeMarketData?: boolean; - includeRwaData?: boolean; -}; - /** * Search for tokens across one or more networks by query string using CAIP format chain IDs. * @@ -267,14 +190,14 @@ type SearchTokenOptions = { export async function searchTokens( chainIds: CaipChainId[], query: string, - { limit = 10, includeMarketData = false }: SearchTokenOptions = {}, -): Promise<{ count: number; data: TokenSearchItem[] }> { - const tokenSearchURL = getTokenSearchURL({ + { limit = 10, includeMarketData = false } = {}, +): Promise<{ count: number; data: unknown[] }> { + const tokenSearchURL = getTokenSearchURL( chainIds, query, limit, includeMarketData, - }); + ); try { const result: { count: number; data: TokenSearchItem[] } = @@ -346,7 +269,6 @@ export async function getTrendingTokens({ minMarketCap, maxMarketCap, excludeLabels, - includeRwaData, }: { chainIds: CaipChainId[]; sortBy?: SortTrendingBy; @@ -372,7 +294,6 @@ export async function getTrendingTokens({ minMarketCap, maxMarketCap, excludeLabels, - includeRwaData, }); try { @@ -380,27 +301,7 @@ export async function getTrendingTokens({ // Validate that the API returned an array if (Array.isArray(result)) { - // TODO hack the results to include RwaData - const filteredResultWithRwaData = result.map((elm) => { - const metadata = { - rwaData: { - instrumentType: 'stock', - ticker: elm.name?.split(' ')[0] ?? '', - market: { - nextOpen: new Date(new Date().setHours(9, 0, 0, 0)).toISOString(), - nextClose: new Date( - new Date().setHours(16, 0, 0, 0), - ).toISOString(), - }, - nextPause: { - start: new Date(new Date().setHours(16, 0, 0, 0)).toISOString(), - end: new Date(new Date().setHours(17, 0, 0, 0)).toISOString(), - }, - }, - }; - return { ...elm, ...metadata }; - }); - return filteredResultWithRwaData; + return result; } // Handle non-expected responses @@ -435,32 +336,6 @@ export async function fetchTokenMetadata( const tokenMetadataURL = getTokenMetadataURL(chainId, tokenAddress); const response = await queryApi(tokenMetadataURL, abortSignal, timeout); if (response) { - const result = await parseJsonResponse(response); - if (Array.isArray(result)) { - const filteredResultWithRwaData = result.map((elm) => { - const metadata = { - rwaData: { - instrumentType: 'stock', - ticker: elm.name?.split(' ')[0] ?? '', - market: { - nextOpen: new Date(new Date().setHours(9, 0, 0, 0)).toISOString(), - nextClose: new Date( - new Date().setHours(16, 0, 0, 0), - ).toISOString(), - }, - nextPause: { - start: new Date(new Date().setHours(16, 0, 0, 0)).toISOString(), - end: new Date(new Date().setHours(17, 0, 0, 0)).toISOString(), - }, - }, - }; - if (elm.aggregators.includes('Ondo')) { - return { ...elm, ...metadata }; - } - return elm; - }); - return filteredResultWithRwaData as unknown as TReturn; - } return parseJsonResponse(response) as Promise; } return undefined; @@ -486,10 +361,12 @@ async function queryApi( mode: 'cors', signal: abortSignal, cache: 'default', - headers: { - 'Content-Type': 'application/json', - }, }; + // Use globalThis.Headers if available, otherwise skip setting headers (Node.js has no fetch headers by default) + if (typeof globalThis.Headers === 'function') { + fetchOptions.headers = new globalThis.Headers(); + fetchOptions.headers.set('Content-Type', 'application/json'); + } try { return await timeoutFetch(apiURL, fetchOptions, timeout); } catch (error) { From 2198f25a4cc3e1517661df94d39716535f1a028e Mon Sep 17 00:00:00 2001 From: "dawnseeker8@gmail.com" Date: Fri, 9 Jan 2026 14:42:51 +0800 Subject: [PATCH 11/16] fix yarn lint issue. --- eslint-suppressions.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/eslint-suppressions.json b/eslint-suppressions.json index 6cc37188e43..e1bc9498a86 100644 --- a/eslint-suppressions.json +++ b/eslint-suppressions.json @@ -359,7 +359,7 @@ }, "packages/assets-controllers/src/TokenListController.ts": { "@typescript-eslint/explicit-function-return-type": { - "count": 6 + "count": 1 }, "no-restricted-syntax": { "count": 7 From ffabbd2de5a0de2ff84356a69bb6dc9082572aa4 Mon Sep 17 00:00:00 2001 From: "dawnseeker8@gmail.com" Date: Fri, 9 Jan 2026 16:03:02 +0800 Subject: [PATCH 12/16] Fix the unit tests broken. --- .../src/TokenListController.test.ts | 2 +- .../src/token-service.test.ts | 50 +++++++++---------- 2 files changed, 26 insertions(+), 26 deletions(-) diff --git a/packages/assets-controllers/src/TokenListController.test.ts b/packages/assets-controllers/src/TokenListController.test.ts index b6a03aa7b52..1db82bd2ed4 100644 --- a/packages/assets-controllers/src/TokenListController.test.ts +++ b/packages/assets-controllers/src/TokenListController.test.ts @@ -1366,5 +1366,5 @@ describe('TokenListController', () => { function getTokensPath(chainId: Hex) { return `/tokens/${convertHexToDecimal( chainId, - )}?occurrenceFloor=3&includeNativeAssets=false&includeTokenFees=false&includeAssetType=false&includeERC20Permit=false&includeStorage=false`; + )}?occurrenceFloor=3&includeNativeAssets=false&includeTokenFees=false&includeAssetType=false&includeERC20Permit=false&includeStorage=false&includeRwaData=true`; } diff --git a/packages/assets-controllers/src/token-service.test.ts b/packages/assets-controllers/src/token-service.test.ts index 1d06f437e4a..63167640c3d 100644 --- a/packages/assets-controllers/src/token-service.test.ts +++ b/packages/assets-controllers/src/token-service.test.ts @@ -201,7 +201,7 @@ const sampleTokenListLinea = [ decimals: 18, occurrences: 11, aggregators: [ - 'paraswap', + 'lineaTeam', 'pmm', 'airswapLight', 'zeroEx', @@ -293,7 +293,7 @@ describe('Token service', () => { const { signal } = new AbortController(); nock(TOKEN_END_POINT_API) .get( - `/tokens/${sampleDecimalChainId}?occurrenceFloor=3&includeNativeAssets=false&includeTokenFees=false&includeAssetType=false&includeERC20Permit=false&includeStorage=false`, + `/tokens/${sampleDecimalChainId}?occurrenceFloor=3&includeNativeAssets=false&includeTokenFees=false&includeAssetType=false&includeERC20Permit=false&includeStorage=false&includeRwaData=true`, ) .reply(200, sampleTokenList) .persist(); @@ -310,7 +310,7 @@ describe('Token service', () => { nock(TOKEN_END_POINT_API) .get( - `/tokens/${lineaChainId}?occurrenceFloor=1&includeNativeAssets=false&includeTokenFees=false&includeAssetType=false&includeERC20Permit=false&includeStorage=false`, + `/tokens/${lineaChainId}?occurrenceFloor=1&includeNativeAssets=false&includeTokenFees=false&includeAssetType=false&includeERC20Permit=false&includeStorage=false&includeRwaData=true`, ) .reply(200, sampleTokenListLinea) .persist(); @@ -324,7 +324,7 @@ describe('Token service', () => { const abortController = new AbortController(); nock(TOKEN_END_POINT_API) .get( - `/tokens/${sampleDecimalChainId}?occurrenceFloor=3&includeNativeAssets=false&includeTokenFees=false&includeAssetType=false&includeERC20Permit=false&includeStorage=false`, + `/tokens/${sampleDecimalChainId}?occurrenceFloor=3&includeNativeAssets=false&includeTokenFees=false&includeAssetType=false&includeERC20Permit=false&includeStorage=false&includeRwaData=true`, ) // well beyond time it will take to abort .delay(ONE_SECOND_IN_MILLISECONDS) @@ -344,7 +344,7 @@ describe('Token service', () => { const { signal } = new AbortController(); nock(TOKEN_END_POINT_API) .get( - `/tokens/${sampleDecimalChainId}?occurrenceFloor=3&includeNativeAssets=false&includeTokenFees=false&includeAssetType=false&includeERC20Permit=false&includeStorage=false`, + `/tokens/${sampleDecimalChainId}?occurrenceFloor=3&includeNativeAssets=false&includeTokenFees=false&includeAssetType=false&includeERC20Permit=false&includeStorage=false&includeRwaData=true`, ) .replyWithError('Example network error') .persist(); @@ -358,7 +358,7 @@ describe('Token service', () => { const { signal } = new AbortController(); nock(TOKEN_END_POINT_API) .get( - `/tokens/${sampleDecimalChainId}?occurrenceFloor=3&includeNativeAssets=false&includeTokenFees=false&includeAssetType=false&includeERC20Permit=false&includeStorage=false`, + `/tokens/${sampleDecimalChainId}?occurrenceFloor=3&includeNativeAssets=false&includeTokenFees=false&includeAssetType=false&includeERC20Permit=false&includeStorage=false&includeRwaData=true`, ) .reply(500) .persist(); @@ -372,7 +372,7 @@ describe('Token service', () => { const { signal } = new AbortController(); nock(TOKEN_END_POINT_API) .get( - `/tokens/${sampleDecimalChainId}?occurrenceFloor=3&includeNativeAssets=false&includeTokenFees=false&includeAssetType=false&includeERC20Permit=false&includeStorage=false`, + `/tokens/${sampleDecimalChainId}?occurrenceFloor=3&includeNativeAssets=false&includeTokenFees=false&includeAssetType=false&includeERC20Permit=false&includeStorage=false&includeRwaData=true`, ) // well beyond timeout .delay(ONE_SECOND_IN_MILLISECONDS) @@ -392,7 +392,7 @@ describe('Token service', () => { const { signal } = new AbortController(); nock(TOKEN_END_POINT_API) .get( - `/token/${sampleDecimalChainId}?address=0x514910771af9ca656af840dff83e8264ecf986ca`, + `/token/${sampleDecimalChainId}?address=0x514910771af9ca656af840dff83e8264ecf986ca&includeRwaData=true`, ) .reply(200, sampleToken) .persist(); @@ -499,7 +499,7 @@ describe('Token service', () => { nock(TOKEN_END_POINT_API) .get( - `/tokens/search?networks=${encodeURIComponent(sampleCaipChainId)}&query=${searchQuery}&limit=10&includeMarketData=false`, + `/tokens/search?networks=${encodeURIComponent(sampleCaipChainId)}&query=${searchQuery}&limit=10&includeMarketData=false&includeRwaData=true`, ) .reply(200, mockResponse) .persist(); @@ -523,7 +523,7 @@ describe('Token service', () => { nock(TOKEN_END_POINT_API) .get( - `/tokens/search?networks=${encodeURIComponent(sampleCaipChainId)}&query=${searchQuery}&limit=${customLimit}&includeMarketData=false`, + `/tokens/search?networks=${encodeURIComponent(sampleCaipChainId)}&query=${searchQuery}&limit=${customLimit}&includeMarketData=false&includeRwaData=true`, ) .reply(200, mockResponse) .persist(); @@ -549,7 +549,7 @@ describe('Token service', () => { nock(TOKEN_END_POINT_API) .get( - `/tokens/search?networks=${encodeURIComponent(sampleCaipChainId)}&query=${encodedQuery}&limit=10&includeMarketData=false`, + `/tokens/search?networks=${encodeURIComponent(sampleCaipChainId)}&query=${encodedQuery}&limit=10&includeMarketData=false&includeRwaData=true`, ) .reply(200, mockResponse) .persist(); @@ -575,7 +575,7 @@ describe('Token service', () => { nock(TOKEN_END_POINT_API) .get( - `/tokens/search?networks=${encodedChainIds}&query=${searchQuery}&limit=10&includeMarketData=false`, + `/tokens/search?networks=${encodedChainIds}&query=${searchQuery}&limit=10&includeMarketData=false&includeRwaData=true`, ) .reply(200, mockResponse) .persist(); @@ -595,7 +595,7 @@ describe('Token service', () => { const searchQuery = 'USD'; nock(TOKEN_END_POINT_API) .get( - `/tokens/search?networks=${encodeURIComponent(sampleCaipChainId)}&query=${searchQuery}&limit=10&includeMarketData=false`, + `/tokens/search?networks=${encodeURIComponent(sampleCaipChainId)}&query=${searchQuery}&limit=10&includeMarketData=false&includeRwaData=true`, ) .replyWithError('Example network error') .persist(); @@ -609,7 +609,7 @@ describe('Token service', () => { const searchQuery = 'USD'; nock(TOKEN_END_POINT_API) .get( - `/tokens/search?networks=${encodeURIComponent(sampleCaipChainId)}&query=${searchQuery}&limit=10&includeMarketData=false`, + `/tokens/search?networks=${encodeURIComponent(sampleCaipChainId)}&query=${searchQuery}&limit=10&includeMarketData=false&includeRwaData=true`, ) .reply(400, { error: 'Bad Request' }) .persist(); @@ -623,7 +623,7 @@ describe('Token service', () => { const searchQuery = 'USD'; nock(TOKEN_END_POINT_API) .get( - `/tokens/search?networks=${encodeURIComponent(sampleCaipChainId)}&query=${searchQuery}&limit=10&includeMarketData=false`, + `/tokens/search?networks=${encodeURIComponent(sampleCaipChainId)}&query=${searchQuery}&limit=10&includeMarketData=false&includeRwaData=true`, ) .reply(500) .persist(); @@ -643,7 +643,7 @@ describe('Token service', () => { nock(TOKEN_END_POINT_API) .get( - `/tokens/search?networks=${encodeURIComponent(sampleCaipChainId)}&query=${searchQuery}&limit=10&includeMarketData=false`, + `/tokens/search?networks=${encodeURIComponent(sampleCaipChainId)}&query=${searchQuery}&limit=10&includeMarketData=false&includeRwaData=true`, ) .reply(200, mockResponse) .persist(); @@ -663,7 +663,7 @@ describe('Token service', () => { nock(TOKEN_END_POINT_API) .get( - `/tokens/search?networks=&query=${searchQuery}&limit=10&includeMarketData=false`, + `/tokens/search?networks=&query=${searchQuery}&limit=10&includeMarketData=false&includeRwaData=true`, ) .reply(200, mockResponse) .persist(); @@ -678,7 +678,7 @@ describe('Token service', () => { const errorResponse = { error: 'Invalid search query' }; nock(TOKEN_END_POINT_API) .get( - `/tokens/search?networks=${encodeURIComponent(sampleCaipChainId)}&query=${searchQuery}&limit=10&includeMarketData=false`, + `/tokens/search?networks=${encodeURIComponent(sampleCaipChainId)}&query=${searchQuery}&limit=10&includeMarketData=false&includeRwaData=true`, ) .reply(200, errorResponse) .persist(); @@ -711,7 +711,7 @@ describe('Token service', () => { nock(TOKEN_END_POINT_API) .get( - `/tokens/search?networks=${encodedChainIds}&query=${searchQuery}&limit=10&includeMarketData=false`, + `/tokens/search?networks=${encodedChainIds}&query=${searchQuery}&limit=10&includeMarketData=false&includeRwaData=true`, ) .reply(200, mockResponse) .persist(); @@ -734,7 +734,7 @@ describe('Token service', () => { nock(TOKEN_END_POINT_API) .get( - `/tokens/search?networks=${encodeURIComponent(sampleCaipChainId)}&query=${searchQuery}&limit=10&includeMarketData=true`, + `/tokens/search?networks=${encodeURIComponent(sampleCaipChainId)}&query=${searchQuery}&limit=10&includeMarketData=true&includeRwaData=true`, ) .reply(200, mockResponse) .persist(); @@ -779,7 +779,7 @@ describe('Token service', () => { it('returns empty array if api returns non-array response', async () => { nock(TOKEN_END_POINT_API) .get( - `/v3/tokens/trending?chainIds=${encodeURIComponent(sampleCaipChainId)}`, + `/v3/tokens/trending?chainIds=${encodeURIComponent(sampleCaipChainId)}&includeRwaData=true`, ) .reply(200, { error: 'Invalid response' }) .persist(); @@ -791,7 +791,7 @@ describe('Token service', () => { it('returns empty array if the fetch fails', async () => { nock(TOKEN_END_POINT_API) .get( - `/v3/tokens/trending?chainIds=${encodeURIComponent(sampleCaipChainId)}`, + `/v3/tokens/trending?chainIds=${encodeURIComponent(sampleCaipChainId)}&includeRwaData=true`, ) .reply(500) .persist(); @@ -810,7 +810,7 @@ describe('Token service', () => { const testMaxMarketCap = 1000000; nock(TOKEN_END_POINT_API) .get( - `/v3/tokens/trending?chainIds=${encodeURIComponent(testChainId)}&sort=${sortBy}&minLiquidity=${testMinLiquidity}&minVolume24hUsd=${testMinVolume24hUsd}&maxVolume24hUsd=${testMaxVolume24hUsd}&minMarketCap=${testMinMarketCap}&maxMarketCap=${testMaxMarketCap}`, + `/v3/tokens/trending?chainIds=${encodeURIComponent(testChainId)}&sort=${sortBy}&minLiquidity=${testMinLiquidity}&minVolume24hUsd=${testMinVolume24hUsd}&maxVolume24hUsd=${testMaxVolume24hUsd}&minMarketCap=${testMinMarketCap}&maxMarketCap=${testMaxMarketCap}&includeRwaData=true`, ) .reply(200, sampleTrendingTokens) .persist(); @@ -831,7 +831,7 @@ describe('Token service', () => { const testChainId = 'eip155:1'; nock(TOKEN_END_POINT_API) - .get(`/v3/tokens/trending?chainIds=${encodeURIComponent(testChainId)}`) + .get(`/v3/tokens/trending?chainIds=${encodeURIComponent(testChainId)}&includeRwaData=true`) .reply(200, sampleTrendingTokens) .persist(); @@ -847,7 +847,7 @@ describe('Token service', () => { nock(TOKEN_END_POINT_API) .get( - `/v3/tokens/trending?chainIds=${encodeURIComponent(testChainId)}&excludeLabels=${testExcludeLabels.join(',')}`, + `/v3/tokens/trending?chainIds=${encodeURIComponent(testChainId)}&includeRwaData=true&excludeLabels=${testExcludeLabels.join(',')}`, ) .reply(200, sampleTrendingTokens) .persist(); From 5200f35e62fca81658c608005be925ce2f28cbf5 Mon Sep 17 00:00:00 2001 From: "dawnseeker8@gmail.com" Date: Fri, 9 Jan 2026 17:31:10 +0800 Subject: [PATCH 13/16] Fix cursor reporting review comments. --- .../src/token-service.test.ts | 70 ++++++++++++++++++- .../assets-controllers/src/token-service.ts | 12 ++-- 2 files changed, 75 insertions(+), 7 deletions(-) diff --git a/packages/assets-controllers/src/token-service.test.ts b/packages/assets-controllers/src/token-service.test.ts index 63167640c3d..9fc199e3aa8 100644 --- a/packages/assets-controllers/src/token-service.test.ts +++ b/packages/assets-controllers/src/token-service.test.ts @@ -320,6 +320,53 @@ describe('Token service', () => { expect(tokens).toStrictEqual(sampleTokenListLinea); }); + it('should correctly filter linea tokens: include if has lineaTeam OR >= 3 aggregators', async () => { + const { signal } = new AbortController(); + const lineaChainId = 59144; + const lineaHexChain = toHex(lineaChainId); + + const mixedTokens = [ + { + // Should be included (has lineaTeam) + address: '0x1', + symbol: 'T1', + decimals: 18, + aggregators: ['lineaTeam', 'other'], + }, + { + // Should be included (no lineaTeam, but 3 aggregators) + address: '0x2', + symbol: 'T2', + decimals: 18, + aggregators: ['a1', 'a2', 'a3'], + }, + { + // Should be excluded (no lineaTeam, only 2 aggregators) + address: '0x3', + symbol: 'T3', + decimals: 18, + aggregators: ['a1', 'a2'], + }, + ]; + + nock(TOKEN_END_POINT_API) + .get( + `/tokens/${lineaChainId}?occurrenceFloor=1&includeNativeAssets=false&includeTokenFees=false&includeAssetType=false&includeERC20Permit=false&includeStorage=false&includeRwaData=true`, + ) + .reply(200, mixedTokens) + .persist(); + + const tokens = (await fetchTokenListByChainId( + lineaHexChain, + signal, + )) as any[]; + + expect(tokens).toHaveLength(2); + expect(tokens.find((t: any) => t.address === '0x1')).toBeDefined(); + expect(tokens.find((t: any) => t.address === '0x2')).toBeDefined(); + expect(tokens.find((t: any) => t.address === '0x3')).toBeUndefined(); + }); + it('should return undefined if the fetch is aborted', async () => { const abortController = new AbortController(); nock(TOKEN_END_POINT_API) @@ -779,7 +826,7 @@ describe('Token service', () => { it('returns empty array if api returns non-array response', async () => { nock(TOKEN_END_POINT_API) .get( - `/v3/tokens/trending?chainIds=${encodeURIComponent(sampleCaipChainId)}&includeRwaData=true`, + `/v3/tokens/trending?chainIds=${encodeURIComponent(sampleCaipChainId)}`, ) .reply(200, { error: 'Invalid response' }) .persist(); @@ -791,7 +838,7 @@ describe('Token service', () => { it('returns empty array if the fetch fails', async () => { nock(TOKEN_END_POINT_API) .get( - `/v3/tokens/trending?chainIds=${encodeURIComponent(sampleCaipChainId)}&includeRwaData=true`, + `/v3/tokens/trending?chainIds=${encodeURIComponent(sampleCaipChainId)}`, ) .reply(500) .persist(); @@ -847,7 +894,7 @@ describe('Token service', () => { nock(TOKEN_END_POINT_API) .get( - `/v3/tokens/trending?chainIds=${encodeURIComponent(testChainId)}&includeRwaData=true&excludeLabels=${testExcludeLabels.join(',')}`, + `/v3/tokens/trending?chainIds=${encodeURIComponent(testChainId)}&excludeLabels=${testExcludeLabels.join(',')}&includeRwaData=true`, ) .reply(200, sampleTrendingTokens) .persist(); @@ -858,5 +905,22 @@ describe('Token service', () => { }); expect(result).toStrictEqual(sampleTrendingTokens); }); + + it('returns the list of trending tokens with includeRwaData', async () => { + const testChainId = 'eip155:1'; + + nock(TOKEN_END_POINT_API) + .get( + `/v3/tokens/trending?chainIds=${encodeURIComponent(testChainId)}&includeRwaData=true`, + ) + .reply(200, sampleTrendingTokens) + .persist(); + + const result = await getTrendingTokens({ + chainIds: [testChainId], + includeRwaData: true, + }); + expect(result).toStrictEqual(sampleTrendingTokens); + }); }); }); diff --git a/packages/assets-controllers/src/token-service.ts b/packages/assets-controllers/src/token-service.ts index 5b8ef2a5bde..48608c15dfd 100644 --- a/packages/assets-controllers/src/token-service.ts +++ b/packages/assets-controllers/src/token-service.ts @@ -81,7 +81,8 @@ function getTokenSearchURL( * @param options.maxVolume24hUsd - The maximum volume 24h in USD. * @param options.minMarketCap - The minimum market cap. * @param options.maxMarketCap - The maximum market cap. - * @param options.excludeLabels - Array of labels to exclude (e.g., ['stable_coin', 'blue_chip']).- Optional flag to include RWA data in the results (defaults to false). + * @param options.excludeLabels - Array of labels to exclude (e.g., ['stable_coin', 'blue_chip']). + * @param options.includeRwaData - Optional flag to include RWA data in the results (defaults to false). * @returns The trending tokens URL. */ function getTrendingTokensURL(options: { @@ -93,6 +94,7 @@ function getTrendingTokensURL(options: { minMarketCap?: number; maxMarketCap?: number; excludeLabels?: string[]; + includeRwaData?: boolean; }): string { const encodedChainIds = options.chainIds .map((id) => encodeURIComponent(id)) @@ -106,8 +108,6 @@ function getTrendingTokensURL(options: { } }); - queryParams.append('includeRwaData', 'true'); - // Handle excludeLabels separately to avoid encoding the commas // The API expects: excludeLabels=stable_coin,blue_chip (not %2C) const excludeLabelsParam = @@ -146,7 +146,8 @@ export async function fetchTokenListByChainId( if (Array.isArray(result) && chainId === ChainId['linea-mainnet']) { return result.filter( (elm) => - elm.aggregators.includes('lineaTeam') ?? elm.aggregators.length >= 3, + // eslint-disable-next-line @typescript-eslint/prefer-nullish-coalescing + elm.aggregators.includes('lineaTeam') || elm.aggregators.length >= 3, ); } return result; @@ -269,6 +270,8 @@ export async function getTrendingTokens({ minMarketCap, maxMarketCap, excludeLabels, + // default set to true so we don't need to pass it in the function call + includeRwaData = true, }: { chainIds: CaipChainId[]; sortBy?: SortTrendingBy; @@ -294,6 +297,7 @@ export async function getTrendingTokens({ minMarketCap, maxMarketCap, excludeLabels, + includeRwaData, }); try { From fb56e970ac99944a33809600cea1298d89ccbf4b Mon Sep 17 00:00:00 2001 From: "dawnseeker8@gmail.com" Date: Fri, 9 Jan 2026 17:44:36 +0800 Subject: [PATCH 14/16] feat: fx the format format issue of `yarn lint` --- packages/assets-controllers/src/token-service.test.ts | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/packages/assets-controllers/src/token-service.test.ts b/packages/assets-controllers/src/token-service.test.ts index 9fc199e3aa8..196fdb96974 100644 --- a/packages/assets-controllers/src/token-service.test.ts +++ b/packages/assets-controllers/src/token-service.test.ts @@ -878,7 +878,9 @@ describe('Token service', () => { const testChainId = 'eip155:1'; nock(TOKEN_END_POINT_API) - .get(`/v3/tokens/trending?chainIds=${encodeURIComponent(testChainId)}&includeRwaData=true`) + .get( + `/v3/tokens/trending?chainIds=${encodeURIComponent(testChainId)}&includeRwaData=true`, + ) .reply(200, sampleTrendingTokens) .persist(); From a9886279399367081d354eec45e3841eef753d1d Mon Sep 17 00:00:00 2001 From: "dawnseeker8@gmail.com" Date: Fri, 9 Jan 2026 18:24:14 +0800 Subject: [PATCH 15/16] fix the lint issue created by last cursor change. --- .../assets-controllers/src/token-service.test.ts | 13 ++++++------- 1 file changed, 6 insertions(+), 7 deletions(-) diff --git a/packages/assets-controllers/src/token-service.test.ts b/packages/assets-controllers/src/token-service.test.ts index 196fdb96974..964031d9279 100644 --- a/packages/assets-controllers/src/token-service.test.ts +++ b/packages/assets-controllers/src/token-service.test.ts @@ -356,15 +356,14 @@ describe('Token service', () => { .reply(200, mixedTokens) .persist(); - const tokens = (await fetchTokenListByChainId( - lineaHexChain, - signal, - )) as any[]; + const tokens = (await fetchTokenListByChainId(lineaHexChain, signal)) as { + address: string; + }[]; expect(tokens).toHaveLength(2); - expect(tokens.find((t: any) => t.address === '0x1')).toBeDefined(); - expect(tokens.find((t: any) => t.address === '0x2')).toBeDefined(); - expect(tokens.find((t: any) => t.address === '0x3')).toBeUndefined(); + expect(tokens.find((token) => token.address === '0x1')).toBeDefined(); + expect(tokens.find((token) => token.address === '0x2')).toBeDefined(); + expect(tokens.find((token) => token.address === '0x3')).toBeUndefined(); }); it('should return undefined if the fetch is aborted', async () => { From 8a4f4ea3a387fa37fcbc828f8a44c470985cf391 Mon Sep 17 00:00:00 2001 From: "dawnseeker8@gmail.com" Date: Mon, 12 Jan 2026 09:50:11 +0800 Subject: [PATCH 16/16] refactor: make RWA data fields optional in Token type definition Updated the Token type definition in TokenRatesController to make the fields related to rwaData optional. This change enhances flexibility in handling token data structures. --- .../src/TokenRatesController.ts | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/packages/assets-controllers/src/TokenRatesController.ts b/packages/assets-controllers/src/TokenRatesController.ts index 43cce466789..4b7d99f7149 100644 --- a/packages/assets-controllers/src/TokenRatesController.ts +++ b/packages/assets-controllers/src/TokenRatesController.ts @@ -46,15 +46,15 @@ export type Token = { isERC721?: boolean; name?: string; rwaData?: { - instrumentType: string; - ticker: string; - market: { - nextOpen: string; - nextClose: string; + instrumentType?: string; + ticker?: string; + market?: { + nextOpen?: string; + nextClose?: string; }; - nextPause: { - start: string; - end: string; + nextPause?: { + start?: string; + end?: string; }; }; };