{"version":3,"file":"token-service.mjs","sourceRoot":"","sources":["../src/token-service.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,OAAO,EACP,mBAAmB,EACnB,WAAW,EACX,YAAY,EACb,mCAAmC;AAGpC,OAAO,EAAE,8BAA8B,EAAE,yBAAqB;AAE9D,MAAM,CAAC,MAAM,mBAAmB,GAAG,kCAAkC,CAAC;AACtE,MAAM,CAAC,MAAM,+BAA+B,GAC1C,iEAAiE,CAAC;AAEpE;;;;;GAKG;AACH,SAAS,YAAY,CAAC,OAAY;IAChC,MAAM,eAAe,GACnB,OAAO,KAAK,OAAO,CAAC,eAAe,CAAC;QACpC,OAAO,KAAK,OAAO,CAAC,iBAAiB,CAAC;QACtC,OAAO,KAAK,QAAQ,CAAC,gBAAgB;QACnC,CAAC,CAAC,CAAC;QACH,CAAC,CAAC,CAAC,CAAC;IAER,OAAO,GAAG,mBAAmB,WAAW,mBAAmB,CACzD,OAAO,CACR,oBAAoB,eAAe,4IAA4I,CAAC;AACnL,CAAC;AAED;;;;;;GAMG;AACH,SAAS,mBAAmB,CAAC,OAAY,EAAE,YAAoB;IAC7D,OAAO,GAAG,mBAAmB,UAAU,mBAAmB,CACxD,OAAO,CACR,YAAY,YAAY,sBAAsB,CAAC;AAClD,CAAC;AAWD;;;;;;;;;;;GAWG;AACH,SAAS,iBAAiB,CAAC,OAO1B;IACC,MAAM,EAAE,QAAQ,EAAE,KAAK,EAAE,KAAK,EAAE,GAAG,cAAc,EAAE,GAAG,OAAO,CAAC;IAC9D,MAAM,YAAY,GAAG,kBAAkB,CAAC,KAAK,CAAC,CAAC;IAC/C,MAAM,eAAe,GAAG,QAAQ;SAC7B,GAAG,CAAC,CAAC,EAAE,EAAE,EAAE,CAAC,kBAAkB,CAAC,EAAE,CAAC,CAAC;SACnC,IAAI,CAAC,GAAG,CAAC,CAAC;IACb,MAAM,WAAW,GAAG,IAAI,eAAe,EAAE,CAAC;IAC1C,MAAM,CAAC,OAAO,CAAC,cAAc,CAAC,CAAC,OAAO,CAAC,CAAC,CAAC,GAAG,EAAE,KAAK,CAAC,EAAE,EAAE;QACtD,IAAI,KAAK,KAAK,SAAS,EAAE,CAAC;YACxB,WAAW,CAAC,MAAM,CAAC,GAAG,EAAE,MAAM,CAAC,KAAK,CAAC,CAAC,CAAC;QACzC,CAAC;IACH,CAAC,CAAC,CAAC;IAEH,IAAI,aAAa,CAAC;IAClB,IAAI,KAAK,EAAE,CAAC;QACV,IAAI,KAAK,IAAI,EAAE,EAAE,CAAC;YAChB,aAAa,GAAG,KAAK,CAAC;QACxB,CAAC;aAAM,IAAI,KAAK,CAAC,QAAQ,CAAC,MAAM,CAAC,IAAI,KAAK,IAAI,GAAG,EAAE,CAAC;YAClD,8GAA8G;YAC9G,aAAa,GAAG,KAAK,CAAC;QACxB,CAAC;aAAM,CAAC;YACN,aAAa,GAAG,EAAE,CAAC;QACrB,CAAC;IACH,CAAC;IAED,OAAO,GAAG,mBAAmB,2BAA2B,eAAe,UAAU,YAAY,GAAG,aAAa,CAAC,CAAC,CAAC,UAAU,aAAa,EAAE,CAAC,CAAC,CAAC,EAAE,IAAI,WAAW,CAAC,QAAQ,EAAE,EAAE,CAAC;AAC7K,CAAC;AAED;;;;;;;;;;;;;GAaG;AACH,SAAS,iBAAiB,CAAC,OAS1B;IACC,MAAM,EAAE,QAAQ,EAAE,GAAG,YAAY,EAAE,GAAG,OAAO,CAAC;IAC9C,MAAM,eAAe,GAAG,QAAQ;SAC7B,GAAG,CAAC,CAAC,EAAE,EAAE,EAAE,CAAC,kBAAkB,CAAC,EAAE,CAAC,CAAC;SACnC,IAAI,CAAC,GAAG,CAAC,CAAC;IACb,MAAM,WAAW,GAAG,IAAI,eAAe,EAAE,CAAC;IAC1C,MAAM,CAAC,OAAO,CAAC,YAAY,CAAC,CAAC,OAAO,CAAC,CAAC,CAAC,GAAG,EAAE,KAAK,CAAC,EAAE,EAAE;QACpD,IAAI,KAAK,KAAK,SAAS,EAAE,CAAC;YACxB,WAAW,CAAC,MAAM,CAAC,GAAG,EAAE,MAAM,CAAC,KAAK,CAAC,CAAC,CAAC;QACzC,CAAC;IACH,CAAC,CAAC,CAAC;IACH,OAAO,GAAG,mBAAmB,oBAAoB,eAAe,GAAG,WAAW,CAAC,QAAQ,EAAE,CAAC,CAAC,CAAC,IAAI,WAAW,CAAC,QAAQ,EAAE,EAAE,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC;AAClI,CAAC;AAED;;;;;;;;;;;;;;;;GAgBG;AACH,SAAS,oBAAoB,CAAC,OAY7B;IACC,MAAM,eAAe,GAAG,OAAO,CAAC,QAAQ;SACrC,GAAG,CAAC,CAAC,EAAE,EAAE,EAAE,CAAC,kBAAkB,CAAC,EAAE,CAAC,CAAC;SACnC,IAAI,CAAC,GAAG,CAAC,CAAC;IACb,mDAAmD;IACnD,MAAM,WAAW,GAAG,IAAI,eAAe,EAAE,CAAC;IAC1C,MAAM,EAAE,QAAQ,EAAE,aAAa,EAAE,GAAG,IAAI,EAAE,GAAG,OAAO,CAAC;IACrD,MAAM,CAAC,OAAO,CAAC,IAAI,CAAC,CAAC,OAAO,CAAC,CAAC,CAAC,GAAG,EAAE,KAAK,CAAC,EAAE,EAAE;QAC5C,IAAI,KAAK,KAAK,SAAS,EAAE,CAAC;YACxB,WAAW,CAAC,MAAM,CAAC,GAAG,EAAE,MAAM,CAAC,KAAK,CAAC,CAAC,CAAC;QACzC,CAAC;IACH,CAAC,CAAC,CAAC;IAEH,+DAA+D;IAC/D,iEAAiE;IACjE,MAAM,kBAAkB,GACtB,aAAa,KAAK,SAAS,IAAI,aAAa,CAAC,MAAM,GAAG,CAAC;QACrD,CAAC,CAAC,kBAAkB,aAAa,CAAC,IAAI,CAAC,GAAG,CAAC,EAAE;QAC7C,CAAC,CAAC,EAAE,CAAC;IAET,OAAO,GAAG,mBAAmB,gCAAgC,eAAe,GAAG,WAAW,CAAC,QAAQ,EAAE,CAAC,CAAC,CAAC,IAAI,WAAW,CAAC,QAAQ,EAAE,EAAE,CAAC,CAAC,CAAC,EAAE,GAAG,kBAAkB,EAAE,CAAC;AACnK,CAAC;AAED,MAAM,wBAAwB,GAAG,KAAM,CAAC;AAExC,qCAAqC;AACrC,yGAAyG;AACzG,MAAM,cAAc,GAAG,wBAAwB,CAAC;AAEhD;;;;;;;;;GASG;AACH,MAAM,CAAC,KAAK,UAAU,uBAAuB,CAC3C,OAAY,EACZ,WAAwB,EACxB,EAAE,OAAO,GAAG,cAAc,EAAE,GAAG,EAAE;IAEjC,MAAM,QAAQ,GAAG,YAAY,CAAC,OAAO,CAAC,CAAC;IACvC,MAAM,QAAQ,GAAG,MAAM,QAAQ,CAAC,QAAQ,EAAE,WAAW,EAAE,OAAO,CAAC,CAAC;IAChE,IAAI,QAAQ,EAAE,CAAC;QACb,MAAM,MAAM,GAAG,MAAM,iBAAiB,CAAC,QAAQ,CAAC,CAAC;QACjD,IAAI,KAAK,CAAC,OAAO,CAAC,MAAM,CAAC,IAAI,OAAO,KAAK,OAAO,CAAC,eAAe,CAAC,EAAE,CAAC;YAClE,OAAO,MAAM,CAAC,MAAM,CAClB,CAAC,GAAG,EAAE,EAAE,CACN,OAAO,CAAC,GAAG,CAAC,WAAW,CAAC,QAAQ,CAAC,WAAW,CAAC,CAAC;gBAC9C,GAAG,CAAC,WAAW,CAAC,MAAM,IAAI,CAAC,CAC9B,CAAC;QACJ,CAAC;QACD,OAAO,MAAM,CAAC;IAChB,CAAC;IACD,OAAO,SAAS,CAAC;AACnB,CAAC;AAuFD;;;;;;;;;;;GAWG;AACH,MAAM,CAAC,KAAK,UAAU,YAAY,CAChC,QAAuB,EACvB,KAAa,EACb,EACE,KAAK,GAAG,EAAE,EACV,iBAAiB,GAAG,KAAK,EACzB,cAAc,GAAG,IAAI,EACrB,wBAAwB,MACF,EAAE;IAE1B,MAAM,cAAc,GAAG,iBAAiB,CAAC;QACvC,QAAQ;QACR,KAAK;QACL,KAAK;QACL,iBAAiB;QACjB,cAAc;QACd,wBAAwB;KACzB,CAAC,CAAC;IAEH,IAAI,CAAC;QACH,MAAM,MAAM,GACV,MAAM,WAAW,CAAC,cAAc,CAAC,CAAC;QAEpC,6FAA6F;QAC7F,IAAI,MAAM,IAAI,OAAO,MAAM,KAAK,QAAQ,IAAI,KAAK,CAAC,OAAO,CAAC,MAAM,CAAC,IAAI,CAAC,EAAE,CAAC;YACvE,OAAO;gBACL,KAAK,EAAE,MAAM,CAAC,KAAK,IAAI,MAAM,CAAC,IAAI,CAAC,MAAM;gBACzC,IAAI,EAAE,MAAM,CAAC,IAAI;aAClB,CAAC;QACJ,CAAC;QAED,gCAAgC;QAChC,OAAO,EAAE,KAAK,EAAE,CAAC,EAAE,IAAI,EAAE,EAAE,EAAE,KAAK,EAAE,gCAAgC,EAAE,CAAC;IACzE,CAAC;IAAC,OAAO,KAAK,EAAE,CAAC;QACf,MAAM,YAAY,GAAG,KAAK,YAAY,KAAK,CAAC,CAAC,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC;QAC5E,OAAO,EAAE,KAAK,EAAE,CAAC,EAAE,IAAI,EAAE,EAAE,EAAE,KAAK,EAAE,YAAY,EAAE,CAAC;IACrD,CAAC;AACH,CAAC;AA4BD;;;;;;;;;;;;;;;;;GAiBG;AACH,MAAM,CAAC,KAAK,UAAU,iBAAiB,CAAC,EACtC,QAAQ,EACR,MAAM,EACN,YAAY,EACZ,eAAe,EACf,eAAe,EACf,YAAY,EACZ,YAAY,EACZ,aAAa,EACb,cAAc,GAAG,IAAI,EACrB,eAAe,GAAG,IAAI,EACtB,wBAAwB,GAazB;IACC,IAAI,QAAQ,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;QAC1B,OAAO,CAAC,KAAK,CAAC,oBAAoB,CAAC,CAAC;QACpC,OAAO,EAAE,CAAC;IACZ,CAAC;IAED,MAAM,iBAAiB,GAAG,oBAAoB,CAAC;QAC7C,QAAQ;QACR,IAAI,EAAE,MAAM;QACZ,YAAY;QACZ,eAAe;QACf,eAAe;QACf,YAAY;QACZ,YAAY;QACZ,aAAa;QACb,cAAc;QACd,eAAe;QACf,wBAAwB;KACzB,CAAC,CAAC;IAEH,IAAI,CAAC;QACH,MAAM,MAAM,GAAG,MAAM,WAAW,CAAC,iBAAiB,CAAC,CAAC;QAEpD,0CAA0C;QAC1C,IAAI,KAAK,CAAC,OAAO,CAAC,MAAM,CAAC,EAAE,CAAC;YAC1B,OAAO,MAAM,CAAC;QAChB,CAAC;QAED,gCAAgC;QAChC,OAAO,CAAC,KAAK,CAAC,kDAAkD,EAAE,MAAM,CAAC,CAAC;QAC1E,OAAO,EAAE,CAAC;IACZ,CAAC;IAAC,OAAO,KAAK,EAAE,CAAC;QACf,OAAO,CAAC,KAAK,CAAC,iCAAiC,EAAE,KAAK,CAAC,CAAC;QACxD,OAAO,EAAE,CAAC;IACZ,CAAC;AACH,CAAC;AAkCD;;;;;;;;;;;;;GAaG;AACH,MAAM,CAAC,KAAK,UAAU,gBAAgB,CACpC,QAAyB,EACzB,EACE,kBAAkB,EAClB,kBAAkB,EAClB,aAAa,EACb,iBAAiB,EACjB,kBAAkB,EAClB,wBAAwB,EACxB,cAAc,MACa,EAAE;IAE/B,IAAI,QAAQ,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;QAC1B,OAAO,EAAE,CAAC;IACZ,CAAC;IAED,MAAM,cAAc,GAAG,iBAAiB,CAAC;QACvC,QAAQ;QACR,kBAAkB;QAClB,kBAAkB;QAClB,aAAa;QACb,iBAAiB;QACjB,kBAAkB;QAClB,wBAAwB;QACxB,cAAc;KACf,CAAC,CAAC;IAEH,IAAI,CAAC;QACH,MAAM,MAAM,GAAG,MAAM,WAAW,CAAC,cAAc,CAAC,CAAC;QAEjD,IAAI,KAAK,CAAC,OAAO,CAAC,MAAM,CAAC,EAAE,CAAC;YAC1B,OAAO,MAAM,CAAC;QAChB,CAAC;QAED,OAAO,EAAE,CAAC;IACZ,CAAC;IAAC,MAAM,CAAC;QACP,OAAO,EAAE,CAAC;IACZ,CAAC;AACH,CAAC;AAED;;;;;;;;;;GAUG;AACH,MAAM,CAAC,KAAK,UAAU,kBAAkB,CACtC,OAAY,EACZ,YAAoB,EACpB,WAAwB,EACxB,EAAE,OAAO,GAAG,cAAc,EAAE,GAAG,EAAE;IAEjC,IAAI,CAAC,8BAA8B,CAAC,OAAO,CAAC,EAAE,CAAC;QAC7C,MAAM,IAAI,KAAK,CAAC,+BAA+B,CAAC,CAAC;IACnD,CAAC;IACD,MAAM,gBAAgB,GAAG,mBAAmB,CAAC,OAAO,EAAE,YAAY,CAAC,CAAC;IACpE,MAAM,QAAQ,GAAG,MAAM,QAAQ,CAAC,gBAAgB,EAAE,WAAW,EAAE,OAAO,CAAC,CAAC;IACxE,IAAI,QAAQ,EAAE,CAAC;QACb,OAAO,iBAAiB,CAAC,QAAQ,CAAqB,CAAC;IACzD,CAAC;IACD,OAAO,SAAS,CAAC;AACnB,CAAC;AAED;;;;;;;GAOG;AACH,KAAK,UAAU,QAAQ,CACrB,MAAc,EACd,WAAwB,EACxB,OAAe;IAEf,MAAM,YAAY,GAAgB;QAChC,QAAQ,EAAE,MAAM;QAChB,cAAc,EAAE,4BAA4B;QAC5C,MAAM,EAAE,KAAK;QACb,IAAI,EAAE,MAAM;QACZ,MAAM,EAAE,WAAW;QACnB,KAAK,EAAE,SAAS;QAChB,OAAO,EAAE;YACP,cAAc,EAAE,kBAAkB;SACnC;KACF,CAAC;IACF,IAAI,CAAC;QACH,OAAO,MAAM,YAAY,CAAC,MAAM,EAAE,YAAY,EAAE,OAAO,CAAC,CAAC;IAC3D,CAAC;IAAC,OAAO,KAAK,EAAE,CAAC;QACf,IAAI,KAAK,YAAY,KAAK,IAAI,KAAK,CAAC,IAAI,KAAK,YAAY,EAAE,CAAC;YAC1D,OAAO,CAAC,GAAG,CAAC,oBAAoB,CAAC,CAAC;QACpC,CAAC;IACH,CAAC;IACD,OAAO,SAAS,CAAC;AACnB,CAAC;AAED;;;;;;GAMG;AACH,KAAK,UAAU,iBAAiB,CAAC,WAAqB;IACpD,MAAM,WAAW,GAAG,MAAM,WAAW,CAAC,IAAI,EAAE,CAAC;IAC7C,0EAA0E;IAC1E,IAAI,WAAW,EAAE,KAAK,EAAE,CAAC;QACvB,MAAM,IAAI,KAAK,CAAC,uBAAuB,WAAW,CAAC,KAAK,EAAE,CAAC,CAAC;IAC9D,CAAC;IACD,OAAO,WAAW,CAAC;AACrB,CAAC","sourcesContent":["import {\n  ChainId,\n  convertHexToDecimal,\n  handleFetch,\n  timeoutFetch,\n} from '@metamask/controller-utils';\nimport type { CaipAssetType, CaipChainId, Hex } from '@metamask/utils';\n\nimport { isTokenListSupportedForNetwork } from './assetsUtil';\n\nexport const TOKEN_END_POINT_API = 'https://token.api.cx.metamask.io';\nexport const TOKEN_METADATA_NO_SUPPORT_ERROR =\n  'TokenService Error: Network does not support fetchTokenMetadata';\n\n/**\n * Get the tokens URL for a specific network.\n *\n * @param chainId - The chain ID of the network the tokens requested are on.\n * @returns The tokens URL.\n */\nfunction getTokensURL(chainId: Hex): string {\n  const occurrenceFloor =\n    chainId === ChainId['linea-mainnet'] ||\n    chainId === ChainId['megaeth-mainnet'] ||\n    chainId === '0x1079' // Tempo Mainnet\n      ? 1\n      : 3;\n\n  return `${TOKEN_END_POINT_API}/tokens/${convertHexToDecimal(\n    chainId,\n  )}?occurrenceFloor=${occurrenceFloor}&includeNativeAssets=false&includeTokenFees=false&includeAssetType=false&includeERC20Permit=false&includeStorage=false&includeRwaData=true`;\n}\n\n/**\n * Get the token metadata URL for the given network and token.\n *\n * @param chainId - The chain ID of the network the token is on.\n * @param tokenAddress - The token address.\n * @returns The token metadata URL.\n */\nfunction getTokenMetadataURL(chainId: Hex, tokenAddress: string): string {\n  return `${TOKEN_END_POINT_API}/token/${convertHexToDecimal(\n    chainId,\n  )}?address=${tokenAddress}&includeRwaData=true`;\n}\n\n/**\n * The sort by field for trending tokens.\n */\nexport type SortTrendingBy =\n  | 'm5_trending'\n  | 'h1_trending'\n  | 'h6_trending'\n  | 'h24_trending';\n\n/**\n * Get the token search URL for the given networks and search query.\n *\n * @param options - Options for getting token search URL.\n * @param options.chainIds - Array of CAIP format chain IDs (e.g., 'eip155:1', 'solana:5eykt4UsFv8P8NJdTREpY1vzqKqZKvdp').\n * @param options.query - The search query (token name, symbol, or address).\n * @param options.limit - Optional limit for the number of results (defaults to 10).\n * @param options.includeMarketData - Optional flag to include market data in the results (defaults to false).\n * @param options.includeRwaData - Optional flag to include RWA data in the results (defaults to false).\n * @param options.includeTokenSecurityData - Optional flag to include token security data in the results (defaults to false).\n * @returns The token search URL.\n */\nfunction getTokenSearchURL(options: {\n  chainIds: CaipChainId[];\n  query: string;\n  limit?: number;\n  includeMarketData?: boolean;\n  includeRwaData?: boolean;\n  includeTokenSecurityData?: boolean;\n}): string {\n  const { chainIds, query, limit, ...optionalParams } = options;\n  const encodedQuery = encodeURIComponent(query);\n  const encodedChainIds = chainIds\n    .map((id) => encodeURIComponent(id))\n    .join(',');\n  const queryParams = new URLSearchParams();\n  Object.entries(optionalParams).forEach(([key, value]) => {\n    if (value !== undefined) {\n      queryParams.append(key, String(value));\n    }\n  });\n\n  let numberOfItems;\n  if (limit) {\n    if (limit <= 50) {\n      numberOfItems = limit;\n    } else if (query.includes('Ondo') && limit <= 500) {\n      // There is an exception on the API side https://github.com/consensys-vertical-apps/va-mmcx-token-api/pull/287\n      numberOfItems = limit;\n    } else {\n      numberOfItems = 50;\n    }\n  }\n\n  return `${TOKEN_END_POINT_API}/tokens/search?networks=${encodedChainIds}&query=${encodedQuery}${numberOfItems ? `&first=${numberOfItems}` : ''}&${queryParams.toString()}`;\n}\n\n/**\n * Get the token assets URL for the given asset IDs.\n *\n * @param options - Options for getting token assets.\n * @param options.assetIds - Array of CAIP-19 asset IDs (e.g., ['eip155:1/erc20:0x...', 'solana:5eykt.../slip44:501']).\n * @param options.includeAggregators - Optional flag to include aggregator list in the results (defaults to false).\n * @param options.includeCoingeckoId - Optional flag to include CoinGecko ID in the results (defaults to false).\n * @param options.includeLabels - Optional flag to include labels in the results (defaults to false).\n * @param options.includeMarketData - Optional flag to include market data in the results (defaults to false).\n * @param options.includeOccurrences - Optional flag to include occurrence count in the results (defaults to false).\n * @param options.includeTokenSecurityData - Optional flag to include token security data in the results (defaults to false).\n * @param options.includeRwaData - Optional flag to include RWA data in the results (defaults to false).\n * @returns The token assets URL.\n */\nfunction getTokenAssetsURL(options: {\n  assetIds: CaipAssetType[];\n  includeAggregators?: boolean;\n  includeCoingeckoId?: boolean;\n  includeLabels?: boolean;\n  includeMarketData?: boolean;\n  includeOccurrences?: boolean;\n  includeTokenSecurityData?: boolean;\n  includeRwaData?: boolean;\n}): string {\n  const { assetIds, ...queryOptions } = options;\n  const encodedAssetIds = assetIds\n    .map((id) => encodeURIComponent(id))\n    .join(',');\n  const queryParams = new URLSearchParams();\n  Object.entries(queryOptions).forEach(([key, value]) => {\n    if (value !== undefined) {\n      queryParams.append(key, String(value));\n    }\n  });\n  return `${TOKEN_END_POINT_API}/assets?assetIds=${encodedAssetIds}${queryParams.toString() ? `&${queryParams.toString()}` : ''}`;\n}\n\n/**\n * Get the trending tokens URL for the given networks and search query.\n *\n * @param options - Options for getting trending tokens.\n * @param options.chainIds - Array of CAIP format chain IDs (e.g., ['eip155:1', 'eip155:137', 'solana:5eykt4UsFv8P8NJdTREpY1vzqKqZKvdp']).\n * @param options.sort - The sort field.\n * @param options.minLiquidity - The minimum liquidity.\n * @param options.minVolume24hUsd - The minimum volume 24h in USD.\n * @param options.maxVolume24hUsd - The maximum volume 24h in USD.\n * @param options.minMarketCap - The minimum market cap.\n * @param options.maxMarketCap - The maximum market cap.\n * @param options.excludeLabels - Array of labels to exclude (e.g., ['stable_coin', 'blue_chip']).\n * @param options.includeRwaData - Optional flag to include RWA data in the results (defaults to false).\n * @param options.usePriceApiData - Optional flag to use price API data in the results (defaults to false).\n * @param options.includeTokenSecurityData - Optional flag to include token security data in the results (defaults to false).\n * @returns The trending tokens URL.\n */\nfunction getTrendingTokensURL(options: {\n  chainIds: CaipChainId[];\n  sort?: SortTrendingBy;\n  minLiquidity?: number;\n  minVolume24hUsd?: number;\n  maxVolume24hUsd?: number;\n  minMarketCap?: number;\n  maxMarketCap?: number;\n  excludeLabels?: string[];\n  includeRwaData?: boolean;\n  usePriceApiData?: boolean;\n  includeTokenSecurityData?: boolean;\n}): string {\n  const encodedChainIds = options.chainIds\n    .map((id) => encodeURIComponent(id))\n    .join(',');\n  // Add the rest of query params if they are defined\n  const queryParams = new URLSearchParams();\n  const { chainIds, excludeLabels, ...rest } = options;\n  Object.entries(rest).forEach(([key, value]) => {\n    if (value !== undefined) {\n      queryParams.append(key, String(value));\n    }\n  });\n\n  // Handle excludeLabels separately to avoid encoding the commas\n  // The API expects: excludeLabels=stable_coin,blue_chip (not %2C)\n  const excludeLabelsParam =\n    excludeLabels !== undefined && excludeLabels.length > 0\n      ? `&excludeLabels=${excludeLabels.join(',')}`\n      : '';\n\n  return `${TOKEN_END_POINT_API}/v3/tokens/trending?chainIds=${encodedChainIds}${queryParams.toString() ? `&${queryParams.toString()}` : ''}${excludeLabelsParam}`;\n}\n\nconst tenSecondsInMilliseconds = 10_000;\n\n// Token list averages 1.6 MB in size\n// timeoutFetch by default has a 500ms timeout, which will almost always timeout given the response size.\nconst defaultTimeout = tenSecondsInMilliseconds;\n\n/**\n * Fetch the list of token metadata for a given network. This request is cancellable using the\n * abort signal passed in.\n *\n * @param chainId - The chain ID of the network the requested tokens are on.\n * @param abortSignal - The abort signal used to cancel the request if necessary.\n * @param options - Additional fetch options.\n * @param options.timeout - The fetch timeout.\n * @returns The token list, or `undefined` if the request was cancelled.\n */\nexport async function fetchTokenListByChainId(\n  chainId: Hex,\n  abortSignal: AbortSignal,\n  { timeout = defaultTimeout } = {},\n): Promise<unknown> {\n  const tokenURL = getTokensURL(chainId);\n  const response = await queryApi(tokenURL, abortSignal, timeout);\n  if (response) {\n    const result = await parseJsonResponse(response);\n    if (Array.isArray(result) && chainId === ChainId['linea-mainnet']) {\n      return result.filter(\n        (elm) =>\n          Boolean(elm.aggregators.includes('lineaTeam')) ||\n          elm.aggregators.length >= 3,\n      );\n    }\n    return result;\n  }\n  return undefined;\n}\n\nexport type TokenRwaData = {\n  market?: {\n    nextOpen?: string;\n    nextClose?: string;\n  };\n  nextPause?: {\n    start?: string;\n    end?: string;\n  };\n  ticker?: string;\n  instrumentType?: string;\n};\n\nexport type TokenSecurityFeature = {\n  featureId: string;\n  type: string;\n  description: string;\n};\n\nexport type TokenSecurityHolder = {\n  label: string;\n  name: string | null;\n  address: string;\n  holdingPercentage: number;\n};\n\nexport type TokenSecurityMarket = {\n  marketType: string;\n  marketName: string;\n  pairName: string;\n  reserveUSD: number;\n};\n\nexport type TokenSecurityFees = {\n  transfer: number;\n  transferFeeMaxAmount: number | null;\n  buy: number;\n  sell: number | null;\n};\n\nexport type TokenSecurityFinancialStats = {\n  supply: number;\n  topHolders: TokenSecurityHolder[];\n  holdersCount: number;\n  tradeVolume24h: number | null;\n  lockedLiquidityPct: number | null;\n  markets: TokenSecurityMarket[];\n};\n\nexport type TokenSecurityMetadata = {\n  externalLinks: {\n    homepage: string | null;\n    twitterPage: string | null;\n    telegramChannelId: string | null;\n  };\n};\n\nexport type TokenSecurityData = {\n  resultType: string;\n  maliciousScore: string;\n  fees: TokenSecurityFees;\n  features: TokenSecurityFeature[];\n  financialStats: TokenSecurityFinancialStats;\n  metadata: TokenSecurityMetadata;\n  created: string;\n};\n\nexport type TokenSearchItem = {\n  assetId: CaipAssetType;\n  name: string;\n  symbol: string;\n  decimals: number;\n  /** Optional RWA data for tokens when includeRwaData is true */\n  rwaData?: TokenRwaData;\n  /** Optional security data for tokens when includeTokenSecurityData is true */\n  securityData?: TokenSecurityData;\n};\n\ntype SearchTokenOptions = {\n  limit?: number;\n  includeMarketData?: boolean;\n  includeRwaData?: boolean;\n  includeTokenSecurityData?: boolean;\n};\n\n/**\n * Search for tokens across one or more networks by query string using CAIP format chain IDs.\n *\n * @param chainIds - Array of CAIP format chain IDs (e.g., ['eip155:1', 'eip155:137', 'solana:5eykt4UsFv8P8NJdTREpY1vzqKqZKvdp']).\n * @param query - The search query (token name, symbol, or address).\n * @param options - Additional fetch options.\n * @param options.limit - The maximum number of results to return.\n * @param options.includeMarketData - Optional flag to include market data in the results (defaults to false).\n * @param options.includeRwaData - Optional flag to include RWA data in the results (defaults to false).\n * @param options.includeTokenSecurityData - Optional flag to include token security data in the results (defaults to false).\n * @returns Object containing count, data array, and an optional error message if the request failed.\n */\nexport async function searchTokens(\n  chainIds: CaipChainId[],\n  query: string,\n  {\n    limit = 10,\n    includeMarketData = false,\n    includeRwaData = true,\n    includeTokenSecurityData,\n  }: SearchTokenOptions = {},\n): Promise<{ count: number; data: TokenSearchItem[]; error?: string }> {\n  const tokenSearchURL = getTokenSearchURL({\n    chainIds,\n    query,\n    limit,\n    includeMarketData,\n    includeRwaData,\n    includeTokenSecurityData,\n  });\n\n  try {\n    const result: { count: number; data: TokenSearchItem[] } =\n      await handleFetch(tokenSearchURL);\n\n    // The API returns an object with structure: { count: number, data: array, pageInfo: object }\n    if (result && typeof result === 'object' && Array.isArray(result.data)) {\n      return {\n        count: result.count ?? result.data.length,\n        data: result.data,\n      };\n    }\n\n    // Handle non-expected responses\n    return { count: 0, data: [], error: 'Unexpected API response format' };\n  } catch (error) {\n    const errorMessage = error instanceof Error ? error.message : String(error);\n    return { count: 0, data: [], error: errorMessage };\n  }\n}\n\n/**\n * The trending asset type.\n */\nexport type TrendingAsset = {\n  assetId: string;\n  name: string;\n  symbol: string;\n  decimals: number;\n  price: string;\n  aggregatedUsdVolume: number;\n  marketCap: number;\n  priceChangePct?: {\n    m5?: string;\n    m15?: string;\n    m30?: string;\n    h1?: string;\n    h6?: string;\n    h24?: string;\n  };\n  labels?: string[];\n  /** Optional RWA data for tokens when includeRwaData is true */\n  rwaData?: TokenRwaData;\n  /** Optional security data for tokens when includeTokenSecurityData is true */\n  securityData?: TokenSecurityData;\n};\n\n/**\n * Get the trending tokens for the given chains.\n *\n * @param options - Options for getting trending tokens.\n * @param options.chainIds - The chains to get the trending tokens for.\n * @param options.sortBy - The sort by field.\n * @param options.minLiquidity - The minimum liquidity.\n * @param options.minVolume24hUsd - The minimum volume 24h in USD.\n * @param options.maxVolume24hUsd - The maximum volume 24h in USD.\n * @param options.minMarketCap - The minimum market cap.\n * @param options.maxMarketCap - The maximum market cap.\n * @param options.excludeLabels - Array of labels to exclude (e.g., ['stable_coin', 'blue_chip']).\n * @param options.includeRwaData - Optional flag to include RWA data in the results (defaults to true).\n * @param options.usePriceApiData - Optional flag to use price API data in the results (defaults to true).\n * @param options.includeTokenSecurityData - Optional flag to include token security data in the results (defaults to false).\n * @returns The trending tokens.\n * @throws Will throw if the request fails.\n */\nexport async function getTrendingTokens({\n  chainIds,\n  sortBy,\n  minLiquidity,\n  minVolume24hUsd,\n  maxVolume24hUsd,\n  minMarketCap,\n  maxMarketCap,\n  excludeLabels,\n  includeRwaData = true,\n  usePriceApiData = true,\n  includeTokenSecurityData,\n}: {\n  chainIds: CaipChainId[];\n  sortBy?: SortTrendingBy;\n  minLiquidity?: number;\n  minVolume24hUsd?: number;\n  maxVolume24hUsd?: number;\n  minMarketCap?: number;\n  maxMarketCap?: number;\n  excludeLabels?: string[];\n  includeRwaData?: boolean;\n  usePriceApiData?: boolean;\n  includeTokenSecurityData?: boolean;\n}): Promise<TrendingAsset[]> {\n  if (chainIds.length === 0) {\n    console.error('No chains provided');\n    return [];\n  }\n\n  const trendingTokensURL = getTrendingTokensURL({\n    chainIds,\n    sort: sortBy,\n    minLiquidity,\n    minVolume24hUsd,\n    maxVolume24hUsd,\n    minMarketCap,\n    maxMarketCap,\n    excludeLabels,\n    includeRwaData,\n    usePriceApiData,\n    includeTokenSecurityData,\n  });\n\n  try {\n    const result = await handleFetch(trendingTokensURL);\n\n    // Validate that the API returned an array\n    if (Array.isArray(result)) {\n      return result;\n    }\n\n    // Handle non-expected responses\n    console.error('Trending tokens API returned non-array response:', result);\n    return [];\n  } catch (error) {\n    console.error('Trending tokens request failed:', error);\n    return [];\n  }\n}\n\n/**\n * The token asset type returned by the /assets endpoint.\n */\nexport type TokenAsset = {\n  assetId: CaipAssetType;\n  name: string;\n  symbol: string;\n  decimals: number;\n  /** Aggregator list when includeAggregators is true */\n  aggregators?: string[];\n  /** CoinGecko ID when includeCoingeckoId is true */\n  coingeckoId?: string;\n  /** Labels when includeLabels is true */\n  labels?: string[];\n  /** Occurrence count when includeOccurrences is true */\n  occurrences?: number;\n  /** RWA data when includeRwaData is true */\n  rwaData?: TokenRwaData;\n  /** Security data when includeTokenSecurityData is true */\n  securityData?: TokenSecurityData;\n};\n\ntype FetchTokenAssetsOptions = {\n  includeAggregators?: boolean;\n  includeCoingeckoId?: boolean;\n  includeLabels?: boolean;\n  includeMarketData?: boolean;\n  includeOccurrences?: boolean;\n  includeTokenSecurityData?: boolean;\n  includeRwaData?: boolean;\n};\n\n/**\n * Fetch asset metadata for the given CAIP-19 asset IDs.\n *\n * @param assetIds - Array of CAIP-19 asset IDs (e.g., ['eip155:1/erc20:0x...', 'solana:5eykt.../slip44:501']).\n * @param options - Additional fetch options.\n * @param options.includeAggregators - Optional flag to include aggregator list in the results (defaults to false).\n * @param options.includeCoingeckoId - Optional flag to include CoinGecko ID in the results (defaults to false).\n * @param options.includeLabels - Optional flag to include labels in the results (defaults to false).\n * @param options.includeMarketData - Optional flag to include market data in the results (defaults to false).\n * @param options.includeOccurrences - Optional flag to include occurrence count in the results (defaults to false).\n * @param options.includeTokenSecurityData - Optional flag to include token security data in the results (defaults to false).\n * @param options.includeRwaData - Optional flag to include RWA data in the results (defaults to false).\n * @returns Array of token assets, or empty array if the request failed or no IDs were provided.\n */\nexport async function fetchTokenAssets(\n  assetIds: CaipAssetType[],\n  {\n    includeAggregators,\n    includeCoingeckoId,\n    includeLabels,\n    includeMarketData,\n    includeOccurrences,\n    includeTokenSecurityData,\n    includeRwaData,\n  }: FetchTokenAssetsOptions = {},\n): Promise<TokenAsset[]> {\n  if (assetIds.length === 0) {\n    return [];\n  }\n\n  const tokenAssetsURL = getTokenAssetsURL({\n    assetIds,\n    includeAggregators,\n    includeCoingeckoId,\n    includeLabels,\n    includeMarketData,\n    includeOccurrences,\n    includeTokenSecurityData,\n    includeRwaData,\n  });\n\n  try {\n    const result = await handleFetch(tokenAssetsURL);\n\n    if (Array.isArray(result)) {\n      return result;\n    }\n\n    return [];\n  } catch {\n    return [];\n  }\n}\n\n/**\n * Fetch metadata for the token address provided for a given network. This request is cancellable\n * using the abort signal passed in.\n *\n * @param chainId - The chain ID of the network the token is on.\n * @param tokenAddress - The address of the token to fetch metadata for.\n * @param abortSignal - The abort signal used to cancel the request if necessary.\n * @param options - Additional fetch options.\n * @param options.timeout - The fetch timeout.\n * @returns The token metadata, or `undefined` if the request was either aborted or failed.\n */\nexport async function fetchTokenMetadata<TReturn>(\n  chainId: Hex,\n  tokenAddress: string,\n  abortSignal: AbortSignal,\n  { timeout = defaultTimeout } = {},\n): Promise<TReturn | undefined> {\n  if (!isTokenListSupportedForNetwork(chainId)) {\n    throw new Error(TOKEN_METADATA_NO_SUPPORT_ERROR);\n  }\n  const tokenMetadataURL = getTokenMetadataURL(chainId, tokenAddress);\n  const response = await queryApi(tokenMetadataURL, abortSignal, timeout);\n  if (response) {\n    return parseJsonResponse(response) as Promise<TReturn>;\n  }\n  return undefined;\n}\n\n/**\n * Perform fetch request against the api.\n *\n * @param apiURL - The URL of the API to fetch.\n * @param abortSignal - The abort signal used to cancel the request if necessary.\n * @param timeout - The fetch timeout.\n * @returns Promise resolving request response.\n */\nasync function queryApi(\n  apiURL: string,\n  abortSignal: AbortSignal,\n  timeout: number,\n): Promise<Response | undefined> {\n  const fetchOptions: RequestInit = {\n    referrer: apiURL,\n    referrerPolicy: 'no-referrer-when-downgrade',\n    method: 'GET',\n    mode: 'cors',\n    signal: abortSignal,\n    cache: 'default',\n    headers: {\n      'Content-Type': 'application/json',\n    },\n  };\n  try {\n    return await timeoutFetch(apiURL, fetchOptions, timeout);\n  } catch (error) {\n    if (error instanceof Error && error.name === 'AbortError') {\n      console.log('Request is aborted');\n    }\n  }\n  return undefined;\n}\n\n/**\n * Parse an API response and return the response JSON data.\n *\n * @param apiResponse - The API response to parse.\n * @returns The response JSON data.\n * @throws Will throw if the response includes an error.\n */\nasync function parseJsonResponse(apiResponse: Response): Promise<unknown> {\n  const responseObj = await apiResponse.json();\n  // api may return errors as json without setting an error http status code\n  if (responseObj?.error) {\n    throw new Error(`TokenService Error: ${responseObj.error}`);\n  }\n  return responseObj;\n}\n"]}