@metamask/assets-controllers 92.0.0 → 93.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +19 -1
- package/dist/AccountTrackerController.cjs +8 -2
- package/dist/AccountTrackerController.cjs.map +1 -1
- package/dist/AccountTrackerController.d.cts.map +1 -1
- package/dist/AccountTrackerController.d.mts.map +1 -1
- package/dist/AccountTrackerController.mjs +8 -2
- package/dist/AccountTrackerController.mjs.map +1 -1
- package/dist/CurrencyRateController.cjs +2 -6
- package/dist/CurrencyRateController.cjs.map +1 -1
- package/dist/CurrencyRateController.d.cts.map +1 -1
- package/dist/CurrencyRateController.d.mts.map +1 -1
- package/dist/CurrencyRateController.mjs +2 -6
- package/dist/CurrencyRateController.mjs.map +1 -1
- package/dist/TokenBalancesController.cjs +17 -2
- package/dist/TokenBalancesController.cjs.map +1 -1
- package/dist/TokenBalancesController.d.cts +2 -2
- package/dist/TokenBalancesController.d.cts.map +1 -1
- package/dist/TokenBalancesController.d.mts +2 -2
- package/dist/TokenBalancesController.d.mts.map +1 -1
- package/dist/TokenBalancesController.mjs +17 -2
- package/dist/TokenBalancesController.mjs.map +1 -1
- package/dist/token-service.cjs +1 -1
- package/dist/token-service.cjs.map +1 -1
- package/dist/token-service.mjs +1 -1
- package/dist/token-service.mjs.map +1 -1
- package/package.json +3 -3
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"TokenBalancesController.mjs","sourceRoot":"","sources":["../src/TokenBalancesController.ts"],"names":[],"mappings":"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","sourcesContent":["import { Web3Provider } from '@ethersproject/providers';\nimport type {\n AccountsControllerGetSelectedAccountAction,\n AccountsControllerListAccountsAction,\n} from '@metamask/accounts-controller';\nimport type {\n ControllerGetStateAction,\n ControllerStateChangeEvent,\n StateMetadata,\n} from '@metamask/base-controller';\nimport {\n BNToHex,\n isValidHexAddress,\n safelyExecuteWithTimeout,\n toChecksumHexAddress,\n toHex,\n} from '@metamask/controller-utils';\nimport type {\n BalanceUpdate,\n AccountActivityServiceBalanceUpdatedEvent,\n AccountActivityServiceStatusChangedEvent,\n} from '@metamask/core-backend';\nimport type { KeyringControllerAccountRemovedEvent } from '@metamask/keyring-controller';\nimport type { Messenger } from '@metamask/messenger';\nimport type {\n NetworkControllerGetNetworkClientByIdAction,\n NetworkControllerGetStateAction,\n NetworkControllerStateChangeEvent,\n NetworkState,\n} from '@metamask/network-controller';\nimport { StaticIntervalPollingController } from '@metamask/polling-controller';\nimport type {\n PreferencesControllerGetStateAction,\n PreferencesControllerStateChangeEvent,\n} from '@metamask/preferences-controller';\nimport type { AuthenticationController } from '@metamask/profile-sync-controller';\nimport type { Hex } from '@metamask/utils';\nimport {\n isCaipAssetType,\n isCaipChainId,\n isStrictHexString,\n parseCaipAssetType,\n parseCaipChainId,\n} from '@metamask/utils';\nimport { produce } from 'immer';\nimport { isEqual } from 'lodash';\n\nimport type {\n AccountTrackerControllerGetStateAction,\n AccountTrackerUpdateNativeBalancesAction,\n AccountTrackerUpdateStakedBalancesAction,\n} from './AccountTrackerController';\nimport { STAKING_CONTRACT_ADDRESS_BY_CHAINID } from './AssetsContractController';\nimport {\n AccountsApiBalanceFetcher,\n type BalanceFetcher,\n type ProcessedBalance,\n} from './multi-chain-accounts-service/api-balance-fetcher';\nimport { RpcBalanceFetcher } from './rpc-service/rpc-balance-fetcher';\nimport type { TokenDetectionControllerAddDetectedTokensViaWsAction } from './TokenDetectionController';\nimport type {\n TokensControllerGetStateAction,\n TokensControllerState,\n TokensControllerStateChangeEvent,\n} from './TokensController';\n\nexport type ChainIdHex = Hex;\nexport type ChecksumAddress = Hex;\n\nconst CONTROLLER = 'TokenBalancesController' as const;\nconst DEFAULT_INTERVAL_MS = 30_000; // 30 seconds\nconst DEFAULT_WEBSOCKET_ACTIVE_POLLING_INTERVAL_MS = 300_000; // 5 minutes\n\nconst metadata: StateMetadata<TokenBalancesControllerState> = {\n tokenBalances: {\n includeInStateLogs: false,\n persist: true,\n includeInDebugSnapshot: false,\n usedInUi: true,\n },\n};\n\n// account → chain → token → balance\nexport type TokenBalances = Record<\n ChecksumAddress,\n Record<ChainIdHex, Record<ChecksumAddress, Hex>>\n>;\n\nexport type TokenBalancesControllerState = {\n tokenBalances: TokenBalances;\n};\n\nexport type TokenBalancesControllerGetStateAction = ControllerGetStateAction<\n typeof CONTROLLER,\n TokenBalancesControllerState\n>;\n\nexport type TokenBalancesControllerUpdateChainPollingConfigsAction = {\n type: `TokenBalancesController:updateChainPollingConfigs`;\n handler: TokenBalancesController['updateChainPollingConfigs'];\n};\n\nexport type TokenBalancesControllerGetChainPollingConfigAction = {\n type: `TokenBalancesController:getChainPollingConfig`;\n handler: TokenBalancesController['getChainPollingConfig'];\n};\n\nexport type TokenBalancesControllerActions =\n | TokenBalancesControllerGetStateAction\n | TokenBalancesControllerUpdateChainPollingConfigsAction\n | TokenBalancesControllerGetChainPollingConfigAction;\n\nexport type TokenBalancesControllerStateChangeEvent =\n ControllerStateChangeEvent<typeof CONTROLLER, TokenBalancesControllerState>;\n\nexport type NativeBalanceEvent = {\n type: `${typeof CONTROLLER}:updatedNativeBalance`;\n payload: unknown[];\n};\n\nexport type TokenBalancesControllerEvents =\n | TokenBalancesControllerStateChangeEvent\n | NativeBalanceEvent;\n\nexport type AllowedActions =\n | NetworkControllerGetNetworkClientByIdAction\n | NetworkControllerGetStateAction\n | TokensControllerGetStateAction\n | TokenDetectionControllerAddDetectedTokensViaWsAction\n | PreferencesControllerGetStateAction\n | AccountsControllerGetSelectedAccountAction\n | AccountsControllerListAccountsAction\n | AccountTrackerControllerGetStateAction\n | AccountTrackerUpdateNativeBalancesAction\n | AccountTrackerUpdateStakedBalancesAction\n | AuthenticationController.AuthenticationControllerGetBearerToken;\n\nexport type AllowedEvents =\n | TokensControllerStateChangeEvent\n | PreferencesControllerStateChangeEvent\n | NetworkControllerStateChangeEvent\n | KeyringControllerAccountRemovedEvent\n | AccountActivityServiceBalanceUpdatedEvent\n | AccountActivityServiceStatusChangedEvent;\n\nexport type TokenBalancesControllerMessenger = Messenger<\n typeof CONTROLLER,\n TokenBalancesControllerActions | AllowedActions,\n TokenBalancesControllerEvents | AllowedEvents\n>;\n\nexport type ChainPollingConfig = {\n /** Polling interval in milliseconds for this chain */\n interval: number;\n};\n\nexport type UpdateChainPollingConfigsOptions = {\n /** Whether to immediately fetch balances after updating configs (default: true) */\n immediateUpdate?: boolean;\n};\n\nexport type TokenBalancesControllerOptions = {\n messenger: TokenBalancesControllerMessenger;\n /** Default interval for chains not specified in chainPollingIntervals */\n interval?: number;\n /** Per-chain polling configuration */\n chainPollingIntervals?: Record<ChainIdHex, ChainPollingConfig>;\n state?: Partial<TokenBalancesControllerState>;\n /** When `true`, balances for *all* known accounts are queried. */\n queryMultipleAccounts?: boolean;\n /** Array of chainIds that should use Accounts-API strategy (if supported by API). */\n accountsApiChainIds?: () => ChainIdHex[];\n /** Disable external HTTP calls (privacy / offline mode). */\n allowExternalServices?: () => boolean;\n /** Custom logger. */\n log?: (...args: unknown[]) => void;\n platform?: 'extension' | 'mobile';\n /** Polling interval when WebSocket is active and providing real-time updates */\n websocketActivePollingInterval?: number;\n};\n// endregion\n\n// ────────────────────────────────────────────────────────────────────────────\n// region: Helper utilities\nconst draft = <T>(base: T, fn: (d: T) => void): T => produce(base, fn);\n\nconst ZERO_ADDRESS =\n '0x0000000000000000000000000000000000000000' as ChecksumAddress;\n\nconst checksum = (addr: string): ChecksumAddress =>\n toChecksumHexAddress(addr) as ChecksumAddress;\n\n/**\n * Convert CAIP chain ID or hex chain ID to hex chain ID\n * Handles both CAIP-2 format (e.g., \"eip155:1\") and hex format (e.g., \"0x1\")\n *\n * @param chainId - CAIP chain ID (e.g., \"eip155:1\") or hex chain ID (e.g., \"0x1\")\n * @returns Hex chain ID (e.g., \"0x1\")\n * @throws {Error} If chainId is neither a valid CAIP-2 chain ID nor a hex string\n */\nexport const caipChainIdToHex = (chainId: string): ChainIdHex => {\n if (isStrictHexString(chainId)) {\n return chainId;\n }\n\n if (isCaipChainId(chainId)) {\n return toHex(parseCaipChainId(chainId).reference);\n }\n\n throw new Error('caipChainIdToHex - Failed to provide CAIP-2 or Hex chainId');\n};\n\n/**\n * Extract token address from asset type\n * Returns tuple of [tokenAddress, isNativeToken] or null if invalid\n *\n * @param assetType - Asset type string (e.g., 'eip155:1/erc20:0x...' or 'eip155:1/slip44:60')\n * @returns Tuple of [tokenAddress, isNativeToken] or null if invalid\n */\nexport const parseAssetType = (assetType: string): [string, boolean] | null => {\n if (!isCaipAssetType(assetType)) {\n return null;\n }\n\n const parsed = parseCaipAssetType(assetType);\n\n // ERC20 token (e.g., \"eip155:1/erc20:0x...\")\n if (parsed.assetNamespace === 'erc20') {\n return [parsed.assetReference, false];\n }\n\n // Native token (e.g., \"eip155:1/slip44:60\")\n if (parsed.assetNamespace === 'slip44') {\n return [ZERO_ADDRESS, true];\n }\n\n return null;\n};\n// endregion\n\n// ────────────────────────────────────────────────────────────────────────────\n// region: Main controller\nexport class TokenBalancesController extends StaticIntervalPollingController<{\n chainIds: ChainIdHex[];\n}>()<\n typeof CONTROLLER,\n TokenBalancesControllerState,\n TokenBalancesControllerMessenger\n> {\n readonly #platform: 'extension' | 'mobile';\n\n readonly #queryAllAccounts: boolean;\n\n readonly #accountsApiChainIds: () => ChainIdHex[];\n\n readonly #balanceFetchers: BalanceFetcher[];\n\n #allTokens: TokensControllerState['allTokens'] = {};\n\n #detectedTokens: TokensControllerState['allDetectedTokens'] = {};\n\n #allIgnoredTokens: TokensControllerState['allIgnoredTokens'] = {};\n\n /** Default polling interval for chains without specific configuration */\n readonly #defaultInterval: number;\n\n /** Polling interval when WebSocket is active and providing real-time updates */\n readonly #websocketActivePollingInterval: number;\n\n /** Per-chain polling configuration */\n readonly #chainPollingConfig: Record<ChainIdHex, ChainPollingConfig>;\n\n /** Active polling timers grouped by interval */\n readonly #intervalPollingTimers: Map<number, NodeJS.Timeout> = new Map();\n\n /** Track if controller-level polling is active */\n #isControllerPollingActive = false;\n\n /** Store original chainIds from startPolling to preserve intent */\n #requestedChainIds: ChainIdHex[] = [];\n\n /** Debouncing for rapid status changes to prevent excessive HTTP calls */\n readonly #statusChangeDebouncer: {\n timer: NodeJS.Timeout | null;\n pendingChanges: Map<string, 'up' | 'down'>;\n } = {\n timer: null,\n pendingChanges: new Map(),\n };\n\n constructor({\n messenger,\n interval = DEFAULT_INTERVAL_MS,\n websocketActivePollingInterval = DEFAULT_WEBSOCKET_ACTIVE_POLLING_INTERVAL_MS,\n chainPollingIntervals = {},\n state = {},\n queryMultipleAccounts = true,\n accountsApiChainIds = () => [],\n allowExternalServices = () => true,\n platform,\n }: TokenBalancesControllerOptions) {\n super({\n name: CONTROLLER,\n messenger,\n metadata,\n state: { tokenBalances: {}, ...state },\n });\n\n // Normalize all account addresses to lowercase in existing state\n this.#normalizeAccountAddresses();\n\n this.#platform = platform ?? 'extension';\n this.#queryAllAccounts = queryMultipleAccounts;\n this.#accountsApiChainIds = accountsApiChainIds;\n this.#defaultInterval = interval;\n this.#websocketActivePollingInterval = websocketActivePollingInterval;\n this.#chainPollingConfig = { ...chainPollingIntervals };\n\n // Strategy order: API first, then RPC fallback\n this.#balanceFetchers = [\n ...(accountsApiChainIds().length > 0 && allowExternalServices()\n ? [this.#createAccountsApiFetcher()]\n : []),\n new RpcBalanceFetcher(this.#getProvider, this.#getNetworkClient, () => ({\n allTokens: this.#allTokens,\n allDetectedTokens: this.#detectedTokens,\n })),\n ];\n\n this.setIntervalLength(interval);\n\n // initial token state & subscriptions\n const { allTokens, allDetectedTokens, allIgnoredTokens } =\n this.messenger.call('TokensController:getState');\n this.#allTokens = allTokens;\n this.#detectedTokens = allDetectedTokens;\n this.#allIgnoredTokens = allIgnoredTokens;\n\n this.messenger.subscribe(\n 'TokensController:stateChange',\n (tokensState: TokensControllerState) => {\n this.#onTokensChanged(tokensState).catch((error) => {\n console.warn('Error handling token state change:', error);\n });\n },\n );\n this.messenger.subscribe(\n 'NetworkController:stateChange',\n this.#onNetworkChanged,\n );\n this.messenger.subscribe(\n 'KeyringController:accountRemoved',\n this.#onAccountRemoved,\n );\n\n // Register action handlers for polling interval control\n this.messenger.registerActionHandler(\n `TokenBalancesController:updateChainPollingConfigs`,\n this.updateChainPollingConfigs.bind(this),\n );\n\n this.messenger.registerActionHandler(\n `TokenBalancesController:getChainPollingConfig`,\n this.getChainPollingConfig.bind(this),\n );\n\n // Subscribe to AccountActivityService balance updates for real-time updates\n this.messenger.subscribe(\n 'AccountActivityService:balanceUpdated',\n this.#onAccountActivityBalanceUpdate.bind(this),\n );\n\n // Subscribe to AccountActivityService status changes for dynamic polling management\n this.messenger.subscribe(\n 'AccountActivityService:statusChanged',\n this.#onAccountActivityStatusChanged.bind(this),\n );\n }\n\n /**\n * Normalize all account addresses to lowercase and merge duplicates\n * This handles migration from old state where addresses might be checksummed\n */\n #normalizeAccountAddresses() {\n const currentState = this.state.tokenBalances;\n const normalizedBalances: TokenBalances = {};\n\n // Iterate through all accounts and normalize to lowercase\n for (const address of Object.keys(currentState)) {\n const lowercaseAddress = address.toLowerCase() as ChecksumAddress;\n const accountBalances = currentState[address as ChecksumAddress];\n\n if (!accountBalances) {\n continue;\n }\n\n // If this lowercase address doesn't exist yet, create it\n if (!normalizedBalances[lowercaseAddress]) {\n normalizedBalances[lowercaseAddress] = {};\n }\n\n // Merge chain data\n for (const chainId of Object.keys(accountBalances)) {\n const chainIdKey = chainId as ChainIdHex;\n\n if (!normalizedBalances[lowercaseAddress][chainIdKey]) {\n normalizedBalances[lowercaseAddress][chainIdKey] = {};\n }\n\n // Merge token balances (later values override earlier ones if duplicates exist)\n Object.assign(\n normalizedBalances[lowercaseAddress][chainIdKey],\n accountBalances[chainIdKey],\n );\n }\n }\n\n // Only update if there were changes\n if (\n Object.keys(currentState).length !==\n Object.keys(normalizedBalances).length ||\n Object.keys(currentState).some((addr) => addr !== addr.toLowerCase())\n ) {\n this.update(() => ({ tokenBalances: normalizedBalances }));\n }\n }\n\n #chainIdsWithTokens(): ChainIdHex[] {\n return [\n ...new Set([\n ...Object.keys(this.#allTokens),\n ...Object.keys(this.#detectedTokens),\n ]),\n ] as ChainIdHex[];\n }\n\n readonly #getProvider = (chainId: ChainIdHex): Web3Provider => {\n const { networkConfigurationsByChainId } = this.messenger.call(\n 'NetworkController:getState',\n );\n const cfg = networkConfigurationsByChainId[chainId];\n const { networkClientId } = cfg.rpcEndpoints[cfg.defaultRpcEndpointIndex];\n const client = this.messenger.call(\n 'NetworkController:getNetworkClientById',\n networkClientId,\n );\n return new Web3Provider(client.provider);\n };\n\n readonly #getNetworkClient = (chainId: ChainIdHex) => {\n const { networkConfigurationsByChainId } = this.messenger.call(\n 'NetworkController:getState',\n );\n const cfg = networkConfigurationsByChainId[chainId];\n const { networkClientId } = cfg.rpcEndpoints[cfg.defaultRpcEndpointIndex];\n return this.messenger.call(\n 'NetworkController:getNetworkClientById',\n networkClientId,\n );\n };\n\n /**\n * Creates an AccountsApiBalanceFetcher that only supports chains in the accountsApiChainIds array\n *\n * @returns A BalanceFetcher that wraps AccountsApiBalanceFetcher with chainId filtering\n */\n readonly #createAccountsApiFetcher = (): BalanceFetcher => {\n const originalFetcher = new AccountsApiBalanceFetcher(\n this.#platform,\n this.#getProvider,\n );\n\n return {\n supports: (chainId: ChainIdHex): boolean => {\n // Only support chains that are both:\n // 1. In our specified accountsApiChainIds array\n // 2. Actually supported by the AccountsApi\n return (\n this.#accountsApiChainIds().includes(chainId) &&\n originalFetcher.supports(chainId)\n );\n },\n fetch: originalFetcher.fetch.bind(originalFetcher),\n };\n };\n\n /**\n * Override to support per-chain polling intervals by grouping chains by interval\n *\n * @param options0 - The polling options\n * @param options0.chainIds - Chain IDs to start polling for\n */\n override _startPolling({ chainIds }: { chainIds: ChainIdHex[] }) {\n // Store the original chainIds to preserve intent across config updates\n this.#requestedChainIds = [...chainIds];\n this.#isControllerPollingActive = true;\n this.#startIntervalGroupPolling(chainIds, true);\n }\n\n /**\n * Start or restart interval-based polling for multiple chains\n *\n * @param chainIds - Chain IDs to start polling for\n * @param immediate - Whether to poll immediately before starting timers (default: true)\n */\n #startIntervalGroupPolling(chainIds: ChainIdHex[], immediate = true) {\n // Stop any existing interval timers\n this.#intervalPollingTimers.forEach((timer) => clearInterval(timer));\n this.#intervalPollingTimers.clear();\n\n // Group chains by their polling intervals\n const intervalGroups = new Map<number, ChainIdHex[]>();\n\n for (const chainId of chainIds) {\n const config = this.getChainPollingConfig(chainId);\n const existing = intervalGroups.get(config.interval) || [];\n existing.push(chainId);\n intervalGroups.set(config.interval, existing);\n }\n\n // Start separate polling loop for each interval group\n for (const [interval, chainIdsGroup] of intervalGroups) {\n this.#startPollingForInterval(interval, chainIdsGroup, immediate);\n }\n }\n\n /**\n * Start polling loop for chains that share the same interval\n *\n * @param interval - The polling interval in milliseconds\n * @param chainIds - Chain IDs that share this interval\n * @param immediate - Whether to poll immediately before starting the timer (default: true)\n */\n #startPollingForInterval(\n interval: number,\n chainIds: ChainIdHex[],\n immediate = true,\n ) {\n const pollFunction = async () => {\n if (!this.#isControllerPollingActive) {\n return;\n }\n try {\n await this._executePoll({ chainIds });\n } catch (error) {\n console.warn(\n `Polling failed for chains ${chainIds.join(', ')} with interval ${interval}:`,\n error,\n );\n }\n };\n\n // Poll immediately first if requested\n if (immediate) {\n pollFunction().catch((error) => {\n console.warn(\n `Immediate polling failed for chains ${chainIds.join(', ')}:`,\n error,\n );\n });\n }\n\n // Then start regular interval polling\n this.#setPollingTimer(interval, chainIds, pollFunction);\n }\n\n /**\n * Helper method to set up polling timer\n *\n * @param interval - The polling interval in milliseconds\n * @param chainIds - Chain IDs for this interval\n * @param pollFunction - The function to call on each poll\n */\n #setPollingTimer(\n interval: number,\n chainIds: ChainIdHex[],\n pollFunction: () => Promise<void>,\n ) {\n // Clear any existing timer for this interval first\n const existingTimer = this.#intervalPollingTimers.get(interval);\n if (existingTimer) {\n clearInterval(existingTimer);\n }\n\n const timer = setInterval(() => {\n pollFunction().catch((error) => {\n console.warn(\n `Interval polling failed for chains ${chainIds.join(', ')}:`,\n error,\n );\n });\n }, interval);\n this.#intervalPollingTimers.set(interval, timer);\n }\n\n /**\n * Override to handle our custom polling approach\n *\n * @param tokenSetId - The token set ID to stop polling for\n */\n override _stopPollingByPollingTokenSetId(tokenSetId: string) {\n let parsedTokenSetId;\n let chainsToStop: ChainIdHex[] = [];\n\n try {\n parsedTokenSetId = JSON.parse(tokenSetId);\n chainsToStop = parsedTokenSetId.chainIds || [];\n } catch (error) {\n console.warn('Failed to parse tokenSetId, stopping all polling:', error);\n // Fallback: stop all polling if we can't parse the tokenSetId\n this.#isControllerPollingActive = false;\n this.#requestedChainIds = [];\n this.#intervalPollingTimers.forEach((timer) => clearInterval(timer));\n this.#intervalPollingTimers.clear();\n return;\n }\n\n // Compare with current chains - only stop if it matches our current session\n const currentChainsSet = new Set(this.#requestedChainIds);\n const stopChainsSet = new Set(chainsToStop);\n\n // Check if this stop request is for our current session\n const isCurrentSession =\n currentChainsSet.size === stopChainsSet.size &&\n [...currentChainsSet].every((chain) => stopChainsSet.has(chain));\n\n if (isCurrentSession) {\n this.#isControllerPollingActive = false;\n this.#requestedChainIds = [];\n this.#intervalPollingTimers.forEach((timer) => clearInterval(timer));\n this.#intervalPollingTimers.clear();\n }\n }\n\n /**\n * Get polling configuration for a chain (includes default fallback)\n *\n * @param chainId - The chain ID to get config for\n * @returns The polling configuration for the chain\n */\n getChainPollingConfig(chainId: ChainIdHex): ChainPollingConfig {\n return (\n this.#chainPollingConfig[chainId] ?? {\n interval: this.#defaultInterval,\n }\n );\n }\n\n override async _executePoll({\n chainIds,\n queryAllAccounts = false,\n }: {\n chainIds: ChainIdHex[];\n queryAllAccounts?: boolean;\n }) {\n // This won't be called with our custom implementation, but keep for compatibility\n await this.updateBalances({ chainIds, queryAllAccounts });\n }\n\n /**\n * Update multiple chain polling configurations at once\n *\n * @param configs - Object mapping chain IDs to polling configurations\n * @param options - Optional configuration for the update behavior\n * @param options.immediateUpdate - Whether to immediately fetch balances after updating configs (default: true)\n */\n updateChainPollingConfigs(\n configs: Record<ChainIdHex, ChainPollingConfig>,\n options: UpdateChainPollingConfigsOptions = { immediateUpdate: true },\n ): void {\n Object.assign(this.#chainPollingConfig, configs);\n\n // If polling is currently active, restart with new interval groupings\n if (this.#isControllerPollingActive) {\n // Restart polling with immediate fetch by default, unless explicitly disabled\n this.#startIntervalGroupPolling(\n this.#requestedChainIds,\n options.immediateUpdate,\n );\n }\n }\n\n async updateBalances({\n chainIds,\n queryAllAccounts = false,\n }: { chainIds?: ChainIdHex[]; queryAllAccounts?: boolean } = {}) {\n const targetChains = chainIds ?? this.#chainIdsWithTokens();\n if (!targetChains.length) {\n return;\n }\n\n const { address: selected } = this.messenger.call(\n 'AccountsController:getSelectedAccount',\n );\n const allAccounts = this.messenger.call('AccountsController:listAccounts');\n\n const jwtToken = await safelyExecuteWithTimeout<string | undefined>(\n () => {\n return this.messenger.call('AuthenticationController:getBearerToken');\n },\n false,\n 5000,\n );\n\n const aggregated: ProcessedBalance[] = [];\n let remainingChains = [...targetChains];\n\n // Try each fetcher in order, removing successfully processed chains\n for (const fetcher of this.#balanceFetchers) {\n const supportedChains = remainingChains.filter((c) =>\n fetcher.supports(c),\n );\n if (!supportedChains.length) {\n continue;\n }\n\n try {\n const result = await fetcher.fetch({\n chainIds: supportedChains,\n queryAllAccounts: queryAllAccounts ?? this.#queryAllAccounts,\n selectedAccount: selected as ChecksumAddress,\n allAccounts,\n jwtToken,\n });\n\n if (result.balances && result.balances.length > 0) {\n aggregated.push(...result.balances);\n // Remove chains that were successfully processed\n const processedChains = new Set(\n result.balances.map((b) => b.chainId),\n );\n remainingChains = remainingChains.filter(\n (chain) => !processedChains.has(chain),\n );\n }\n\n // Add unprocessed chains back to remainingChains for next fetcher\n if (\n result.unprocessedChainIds &&\n result.unprocessedChainIds.length > 0\n ) {\n const currentRemainingChains = remainingChains;\n const chainsToAdd = result.unprocessedChainIds.filter(\n (chainId) =>\n supportedChains.includes(chainId) &&\n !currentRemainingChains.includes(chainId),\n );\n remainingChains.push(...chainsToAdd);\n }\n } catch (error) {\n console.warn(\n `Balance fetcher failed for chains ${supportedChains.join(', ')}: ${String(error)}`,\n );\n // Continue to next fetcher (fallback)\n }\n\n // If all chains have been processed, break early\n if (remainingChains.length === 0) {\n break;\n }\n }\n\n // Determine which accounts to process based on queryAllAccounts parameter\n const accountsToProcess =\n (queryAllAccounts ?? this.#queryAllAccounts)\n ? allAccounts.map((a) => a.address as ChecksumAddress)\n : [selected as ChecksumAddress];\n\n const prev = this.state;\n const next = draft(prev, (d) => {\n // Initialize account and chain structures if they don't exist, but preserve existing balances\n for (const chainId of targetChains) {\n for (const account of accountsToProcess) {\n // Ensure the nested structure exists without overwriting existing balances\n d.tokenBalances[account] ??= {};\n d.tokenBalances[account][chainId] ??= {};\n // Initialize tokens from allTokens only if they don't exist yet\n const chainTokens = this.#allTokens[chainId];\n if (chainTokens?.[account]) {\n Object.values(chainTokens[account]).forEach(\n (token: { address: string }) => {\n const tokenAddress = checksum(token.address);\n // Only initialize if the token balance doesn't exist yet\n if (!(tokenAddress in d.tokenBalances[account][chainId])) {\n d.tokenBalances[account][chainId][tokenAddress] = '0x0';\n }\n },\n );\n }\n\n // Initialize tokens from allDetectedTokens only if they don't exist yet\n const detectedChainTokens = this.#detectedTokens[chainId];\n if (detectedChainTokens?.[account]) {\n Object.values(detectedChainTokens[account]).forEach(\n (token: { address: string }) => {\n const tokenAddress = checksum(token.address);\n // Only initialize if the token balance doesn't exist yet\n if (!(tokenAddress in d.tokenBalances[account][chainId])) {\n d.tokenBalances[account][chainId][tokenAddress] = '0x0';\n }\n },\n );\n }\n }\n }\n\n // Update with actual fetched balances only if the value has changed\n aggregated.forEach(({ success, value, account, token, chainId }) => {\n if (success && value !== undefined) {\n // Ensure all accounts we add/update are in lower-case\n const lowerCaseAccount = account.toLowerCase() as ChecksumAddress;\n const newBalance = toHex(value);\n const tokenAddress = checksum(token);\n const currentBalance =\n d.tokenBalances[lowerCaseAccount]?.[chainId]?.[tokenAddress];\n\n // Only update if the balance has actually changed\n if (currentBalance !== newBalance) {\n ((d.tokenBalances[lowerCaseAccount] ??= {})[chainId] ??= {})[\n tokenAddress\n ] = newBalance;\n }\n }\n });\n });\n\n if (!isEqual(prev, next)) {\n this.update(() => next);\n\n const nativeBalances = aggregated.filter(\n (r) => r.success && r.token === ZERO_ADDRESS,\n );\n\n // Get current AccountTracker state to compare existing balances\n const accountTrackerState = this.messenger.call(\n 'AccountTrackerController:getState',\n );\n\n // Update native token balances only if they have changed\n if (nativeBalances.length > 0) {\n const balanceUpdates = nativeBalances\n .map((balance) => ({\n address: balance.account,\n chainId: balance.chainId,\n balance: balance.value ? BNToHex(balance.value) : '0x0',\n }))\n .filter((update) => {\n const currentBalance =\n accountTrackerState.accountsByChainId[update.chainId]?.[\n checksum(update.address)\n ]?.balance;\n // Only include if the balance has actually changed\n return currentBalance !== update.balance;\n });\n\n if (balanceUpdates.length > 0) {\n this.messenger.call(\n 'AccountTrackerController:updateNativeBalances',\n balanceUpdates,\n );\n }\n }\n\n // Filter and update staked balances in a single batch operation for better performance\n const stakedBalances = aggregated.filter((r) => {\n if (!r.success || r.token === ZERO_ADDRESS) {\n return false;\n }\n\n // Check if the chainId and token address match any staking contract\n const stakingContractAddress =\n STAKING_CONTRACT_ADDRESS_BY_CHAINID[\n r.chainId as keyof typeof STAKING_CONTRACT_ADDRESS_BY_CHAINID\n ];\n return (\n stakingContractAddress &&\n stakingContractAddress.toLowerCase() === r.token.toLowerCase()\n );\n });\n\n if (stakedBalances.length > 0) {\n const stakedBalanceUpdates = stakedBalances\n .map((balance) => ({\n address: balance.account,\n chainId: balance.chainId,\n stakedBalance: balance.value ? toHex(balance.value) : '0x0',\n }))\n .filter((update) => {\n const currentStakedBalance =\n accountTrackerState.accountsByChainId[update.chainId]?.[\n checksum(update.address)\n ]?.stakedBalance;\n // Only include if the staked balance has actually changed\n return currentStakedBalance !== update.stakedBalance;\n });\n\n if (stakedBalanceUpdates.length > 0) {\n this.messenger.call(\n 'AccountTrackerController:updateStakedBalances',\n stakedBalanceUpdates,\n );\n }\n }\n }\n }\n\n resetState() {\n this.update(() => ({ tokenBalances: {} }));\n }\n\n /**\n * Helper method to check if a token is tracked (exists in allTokens or allIgnoredTokens)\n *\n * @param tokenAddress - The token address to check\n * @param account - The account address\n * @param chainId - The chain ID\n * @returns True if the token is tracked (imported or ignored)\n */\n #isTokenTracked(\n tokenAddress: string,\n account: ChecksumAddress,\n chainId: ChainIdHex,\n ): boolean {\n // Check if token exists in allTokens\n if (\n this.#allTokens?.[chainId]?.[account.toLowerCase()]?.some(\n (token) => token.address === tokenAddress,\n )\n ) {\n return true;\n }\n\n // Check if token exists in allIgnoredTokens\n if (\n this.#allIgnoredTokens?.[chainId]?.[account.toLowerCase()]?.some(\n (token) => token === tokenAddress,\n )\n ) {\n return true;\n }\n\n return false;\n }\n\n readonly #onTokensChanged = async (state: TokensControllerState) => {\n const changed: ChainIdHex[] = [];\n let hasChanges = false;\n\n // Get chains that have existing balances\n const chainsWithBalances = new Set<ChainIdHex>();\n for (const address of Object.keys(this.state.tokenBalances)) {\n const addressKey = address as ChecksumAddress;\n for (const chainId of Object.keys(\n this.state.tokenBalances[addressKey] || {},\n )) {\n chainsWithBalances.add(chainId as ChainIdHex);\n }\n }\n\n // Only process chains that are explicitly mentioned in the incoming state change\n const incomingChainIds = new Set([\n ...Object.keys(state.allTokens),\n ...Object.keys(state.allDetectedTokens),\n ]);\n\n // Only proceed if there are actual changes to chains that have balances or are being added\n const relevantChainIds = Array.from(incomingChainIds).filter((chainId) => {\n const id = chainId as ChainIdHex;\n\n const hasTokensNow =\n (state.allTokens[id] && Object.keys(state.allTokens[id]).length > 0) ||\n (state.allDetectedTokens[id] &&\n Object.keys(state.allDetectedTokens[id]).length > 0);\n const hadTokensBefore =\n (this.#allTokens[id] && Object.keys(this.#allTokens[id]).length > 0) ||\n (this.#detectedTokens[id] &&\n Object.keys(this.#detectedTokens[id]).length > 0);\n\n // Check if there's an actual change in token state\n const hasTokenChange =\n !isEqual(state.allTokens[id], this.#allTokens[id]) ||\n !isEqual(state.allDetectedTokens[id], this.#detectedTokens[id]);\n\n // Process chains that have actual changes OR are new chains getting tokens\n return hasTokenChange || (!hadTokensBefore && hasTokensNow);\n });\n\n if (relevantChainIds.length === 0) {\n // No relevant changes, just update internal state\n this.#allTokens = state.allTokens;\n this.#detectedTokens = state.allDetectedTokens;\n return;\n }\n\n // Handle both cleanup and updates in a single state update\n this.update((s) => {\n for (const chainId of relevantChainIds) {\n const id = chainId as ChainIdHex;\n const hasTokensNow =\n (state.allTokens[id] &&\n Object.keys(state.allTokens[id]).length > 0) ||\n (state.allDetectedTokens[id] &&\n Object.keys(state.allDetectedTokens[id]).length > 0);\n const hadTokensBefore =\n (this.#allTokens[id] &&\n Object.keys(this.#allTokens[id]).length > 0) ||\n (this.#detectedTokens[id] &&\n Object.keys(this.#detectedTokens[id]).length > 0);\n\n if (\n !isEqual(state.allTokens[id], this.#allTokens[id]) ||\n !isEqual(state.allDetectedTokens[id], this.#detectedTokens[id])\n ) {\n if (hasTokensNow) {\n // Chain still has tokens - mark for async balance update\n changed.push(id);\n } else if (hadTokensBefore) {\n // Chain had tokens before but doesn't now - clean up balances immediately\n for (const address of Object.keys(s.tokenBalances)) {\n const addressKey = address as ChecksumAddress;\n if (s.tokenBalances[addressKey]?.[id]) {\n s.tokenBalances[addressKey][id] = {};\n hasChanges = true;\n }\n }\n }\n }\n }\n });\n\n this.#allTokens = state.allTokens;\n this.#detectedTokens = state.allDetectedTokens;\n this.#allIgnoredTokens = state.allIgnoredTokens;\n\n // Only update balances for chains that still have tokens (and only if we haven't already updated state)\n if (changed.length && !hasChanges) {\n this.updateBalances({ chainIds: changed }).catch((error) => {\n console.warn('Error updating balances after token change:', error);\n });\n }\n };\n\n readonly #onNetworkChanged = (state: NetworkState) => {\n // Check if any networks were removed by comparing with previous state\n const currentNetworks = new Set(\n Object.keys(state.networkConfigurationsByChainId),\n );\n\n // Get all networks that currently have balances\n const networksWithBalances = new Set<string>();\n for (const address of Object.keys(this.state.tokenBalances)) {\n const addressKey = address as ChecksumAddress;\n for (const network of Object.keys(\n this.state.tokenBalances[addressKey] || {},\n )) {\n networksWithBalances.add(network);\n }\n }\n\n // Find networks that were removed\n const removedNetworks = Array.from(networksWithBalances).filter(\n (network) => !currentNetworks.has(network),\n );\n\n if (removedNetworks.length > 0) {\n this.update((s) => {\n // Remove balances for all accounts on the deleted networks\n for (const address of Object.keys(s.tokenBalances)) {\n const addressKey = address as ChecksumAddress;\n for (const removedNetwork of removedNetworks) {\n const networkKey = removedNetwork as ChainIdHex;\n if (s.tokenBalances[addressKey]?.[networkKey]) {\n delete s.tokenBalances[addressKey][networkKey];\n }\n }\n }\n });\n }\n };\n\n readonly #onAccountRemoved = (addr: string) => {\n if (!isStrictHexString(addr) || !isValidHexAddress(addr)) {\n return;\n }\n this.update((s) => {\n delete s.tokenBalances[addr as ChecksumAddress];\n });\n };\n\n // ────────────────────────────────────────────────────────────────────────────\n // AccountActivityService integration helpers\n\n /**\n * Prepare balance updates from AccountActivityService\n * Processes all updates and returns categorized results\n * Throws an error if any updates have validation/parsing issues\n *\n * @param updates - Array of balance updates from AccountActivityService\n * @param account - Lowercase account address (for consistency with tokenBalances state format)\n * @param chainId - Hex chain ID\n * @returns Object containing arrays of token balances, new token addresses to add, and native balance updates\n * @throws Error if any balance update has validation or parsing errors\n */\n #prepareBalanceUpdates(\n updates: BalanceUpdate[],\n account: ChecksumAddress,\n chainId: ChainIdHex,\n ): {\n tokenBalances: { tokenAddress: ChecksumAddress; balance: Hex }[];\n newTokens: string[];\n nativeBalanceUpdates: { address: string; chainId: Hex; balance: Hex }[];\n } {\n const tokenBalances: { tokenAddress: ChecksumAddress; balance: Hex }[] = [];\n const newTokens: string[] = [];\n const nativeBalanceUpdates: {\n address: string;\n chainId: Hex;\n balance: Hex;\n }[] = [];\n\n for (const update of updates) {\n const { asset, postBalance } = update;\n\n // Throw if balance update has an error\n if (postBalance.error) {\n throw new Error('Balance update has error');\n }\n\n // Parse token address from asset type\n const parsed = parseAssetType(asset.type);\n if (!parsed) {\n throw new Error('Failed to parse asset type');\n }\n\n const [tokenAddress, isNativeToken] = parsed;\n\n // Validate token address\n if (\n !isStrictHexString(tokenAddress) ||\n !isValidHexAddress(tokenAddress)\n ) {\n throw new Error('Invalid token address');\n }\n\n const checksumTokenAddress = checksum(tokenAddress);\n const isTracked = this.#isTokenTracked(\n checksumTokenAddress,\n account,\n chainId,\n );\n\n // postBalance.amount is in hex format (raw units)\n const balanceHex = postBalance.amount as Hex;\n\n // Add token balance (tracked tokens, ignored tokens, and native tokens all get balance updates)\n tokenBalances.push({\n tokenAddress: checksumTokenAddress,\n balance: balanceHex,\n });\n\n // Add native balance update if this is a native token\n if (isNativeToken) {\n nativeBalanceUpdates.push({\n address: account,\n chainId,\n balance: balanceHex,\n });\n }\n\n // Handle untracked ERC20 tokens - queue for import\n if (!isNativeToken && !isTracked) {\n newTokens.push(checksumTokenAddress);\n }\n }\n\n return { tokenBalances, newTokens, nativeBalanceUpdates };\n }\n\n // ────────────────────────────────────────────────────────────────────────────\n // AccountActivityService event handlers\n\n /**\n * Handle real-time balance updates from AccountActivityService\n * Processes balance updates and updates the token balance state\n * If any balance update has an error, triggers fallback polling for the chain\n *\n * @param options0 - Balance update parameters\n * @param options0.address - Account address\n * @param options0.chain - CAIP chain identifier\n * @param options0.updates - Array of balance updates for the account\n */\n readonly #onAccountActivityBalanceUpdate = async ({\n address,\n chain,\n updates,\n }: {\n address: string;\n chain: string;\n updates: BalanceUpdate[];\n }) => {\n const chainId = caipChainIdToHex(chain);\n const checksummedAccount = checksum(address);\n\n try {\n // Process all balance updates at once\n const { tokenBalances, newTokens, nativeBalanceUpdates } =\n this.#prepareBalanceUpdates(updates, checksummedAccount, chainId);\n\n // Update state once with all token balances\n if (tokenBalances.length > 0) {\n this.update((state) => {\n // Temporary until ADR to normalize all keys - tokenBalances state requires: account in lowercase, token in checksum\n const lowercaseAccount =\n checksummedAccount.toLowerCase() as ChecksumAddress;\n state.tokenBalances[lowercaseAccount] ??= {};\n state.tokenBalances[lowercaseAccount][chainId] ??= {};\n\n // Apply all token balance updates\n for (const { tokenAddress, balance } of tokenBalances) {\n state.tokenBalances[lowercaseAccount][chainId][tokenAddress] =\n balance;\n }\n });\n }\n\n // Update native balances in AccountTrackerController\n if (nativeBalanceUpdates.length > 0) {\n this.messenger.call(\n 'AccountTrackerController:updateNativeBalances',\n nativeBalanceUpdates,\n );\n }\n\n // Import any new tokens that were discovered (balance already updated from websocket)\n if (newTokens.length > 0) {\n await this.messenger.call(\n 'TokenDetectionController:addDetectedTokensViaWs',\n {\n tokensSlice: newTokens,\n chainId: chainId as Hex,\n },\n );\n }\n } catch (error) {\n console.warn(\n `Error updating balances from AccountActivityService for chain ${chain}, account ${address}:`,\n error,\n );\n console.warn('Balance update data:', JSON.stringify(updates, null, 2));\n\n // On error, trigger fallback polling\n await this.updateBalances({ chainIds: [chainId] }).catch(() => {\n // Silently handle polling errors\n });\n }\n };\n\n /**\n * Handle status changes from AccountActivityService\n * Uses aggressive debouncing to prevent excessive HTTP calls from rapid up/down changes\n *\n * @param options0 - Status change event data\n * @param options0.chainIds - Array of chain identifiers\n * @param options0.status - Connection status ('up' for connected, 'down' for disconnected)\n */\n readonly #onAccountActivityStatusChanged = ({\n chainIds,\n status,\n }: {\n chainIds: string[];\n status: 'up' | 'down';\n }) => {\n // Update pending changes (latest status wins for each chain)\n for (const chainId of chainIds) {\n this.#statusChangeDebouncer.pendingChanges.set(chainId, status);\n }\n\n // Clear existing timer to extend debounce window\n if (this.#statusChangeDebouncer.timer) {\n clearTimeout(this.#statusChangeDebouncer.timer);\n }\n\n // Set new timer - only process changes after activity settles\n this.#statusChangeDebouncer.timer = setTimeout(() => {\n this.#processAccumulatedStatusChanges();\n }, 5000); // 5-second debounce window\n };\n\n /**\n * Process all accumulated status changes in one batch to minimize HTTP calls\n */\n #processAccumulatedStatusChanges(): void {\n const changes = Array.from(\n this.#statusChangeDebouncer.pendingChanges.entries(),\n );\n this.#statusChangeDebouncer.pendingChanges.clear();\n this.#statusChangeDebouncer.timer = null;\n\n if (changes.length === 0) {\n return;\n }\n\n // Calculate final polling configurations\n const chainConfigs: Record<ChainIdHex, { interval: number }> = {};\n\n for (const [chainId, status] of changes) {\n // Convert CAIP format (eip155:1) to hex format (0x1)\n // chainId is always in CAIP format from AccountActivityService\n const hexChainId = caipChainIdToHex(chainId);\n\n if (status === 'down') {\n // Chain is down - use default polling since no real-time updates available\n chainConfigs[hexChainId] = { interval: this.#defaultInterval };\n } else {\n // Chain is up - use longer intervals since WebSocket provides real-time updates\n chainConfigs[hexChainId] = {\n interval: this.#websocketActivePollingInterval,\n };\n }\n }\n\n // Add jitter to prevent synchronized requests across instances\n const jitterDelay = Math.random() * this.#defaultInterval; // 0 to default interval\n\n setTimeout(() => {\n this.updateChainPollingConfigs(chainConfigs, { immediateUpdate: true });\n }, jitterDelay);\n }\n\n /**\n * Clean up all timers and resources when controller is destroyed\n */\n override destroy(): void {\n this.#isControllerPollingActive = false;\n this.#intervalPollingTimers.forEach((timer) => clearInterval(timer));\n this.#intervalPollingTimers.clear();\n\n // Clean up debouncing timer\n if (this.#statusChangeDebouncer.timer) {\n clearTimeout(this.#statusChangeDebouncer.timer);\n this.#statusChangeDebouncer.timer = null;\n }\n\n // Unregister action handlers\n this.messenger.unregisterActionHandler(\n `TokenBalancesController:updateChainPollingConfigs`,\n );\n this.messenger.unregisterActionHandler(\n `TokenBalancesController:getChainPollingConfig`,\n );\n\n super.destroy();\n }\n}\n\nexport default TokenBalancesController;\n"]}
|
|
1
|
+
{"version":3,"file":"TokenBalancesController.mjs","sourceRoot":"","sources":["../src/TokenBalancesController.ts"],"names":[],"mappings":"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","sourcesContent":["import { Web3Provider } from '@ethersproject/providers';\nimport type {\n AccountsControllerGetSelectedAccountAction,\n AccountsControllerListAccountsAction,\n AccountsControllerSelectedEvmAccountChangeEvent,\n} from '@metamask/accounts-controller';\nimport type {\n ControllerGetStateAction,\n ControllerStateChangeEvent,\n StateMetadata,\n} from '@metamask/base-controller';\nimport {\n BNToHex,\n isValidHexAddress,\n safelyExecuteWithTimeout,\n toChecksumHexAddress,\n toHex,\n} from '@metamask/controller-utils';\nimport type {\n BalanceUpdate,\n AccountActivityServiceBalanceUpdatedEvent,\n AccountActivityServiceStatusChangedEvent,\n} from '@metamask/core-backend';\nimport type { KeyringControllerAccountRemovedEvent } from '@metamask/keyring-controller';\nimport type { Messenger } from '@metamask/messenger';\nimport type {\n NetworkControllerGetNetworkClientByIdAction,\n NetworkControllerGetStateAction,\n NetworkControllerStateChangeEvent,\n NetworkState,\n} from '@metamask/network-controller';\nimport { StaticIntervalPollingController } from '@metamask/polling-controller';\nimport type {\n PreferencesControllerGetStateAction,\n PreferencesControllerStateChangeEvent,\n} from '@metamask/preferences-controller';\nimport type { AuthenticationController } from '@metamask/profile-sync-controller';\nimport type { Hex } from '@metamask/utils';\nimport {\n isCaipAssetType,\n isCaipChainId,\n isStrictHexString,\n parseCaipAssetType,\n parseCaipChainId,\n} from '@metamask/utils';\nimport { produce } from 'immer';\nimport { isEqual } from 'lodash';\n\nimport type {\n AccountTrackerControllerGetStateAction,\n AccountTrackerUpdateNativeBalancesAction,\n AccountTrackerUpdateStakedBalancesAction,\n} from './AccountTrackerController';\nimport { STAKING_CONTRACT_ADDRESS_BY_CHAINID } from './AssetsContractController';\nimport {\n AccountsApiBalanceFetcher,\n type BalanceFetcher,\n type ProcessedBalance,\n} from './multi-chain-accounts-service/api-balance-fetcher';\nimport { RpcBalanceFetcher } from './rpc-service/rpc-balance-fetcher';\nimport type { TokenDetectionControllerAddDetectedTokensViaWsAction } from './TokenDetectionController';\nimport type {\n TokensControllerGetStateAction,\n TokensControllerState,\n TokensControllerStateChangeEvent,\n} from './TokensController';\n\nexport type ChainIdHex = Hex;\nexport type ChecksumAddress = Hex;\n\nconst CONTROLLER = 'TokenBalancesController' as const;\nconst DEFAULT_INTERVAL_MS = 30_000; // 30 seconds\nconst DEFAULT_WEBSOCKET_ACTIVE_POLLING_INTERVAL_MS = 300_000; // 5 minutes\n\nconst metadata: StateMetadata<TokenBalancesControllerState> = {\n tokenBalances: {\n includeInStateLogs: false,\n persist: true,\n includeInDebugSnapshot: false,\n usedInUi: true,\n },\n};\n\n// account → chain → token → balance\nexport type TokenBalances = Record<\n ChecksumAddress,\n Record<ChainIdHex, Record<ChecksumAddress, Hex>>\n>;\n\nexport type TokenBalancesControllerState = {\n tokenBalances: TokenBalances;\n};\n\nexport type TokenBalancesControllerGetStateAction = ControllerGetStateAction<\n typeof CONTROLLER,\n TokenBalancesControllerState\n>;\n\nexport type TokenBalancesControllerUpdateChainPollingConfigsAction = {\n type: `TokenBalancesController:updateChainPollingConfigs`;\n handler: TokenBalancesController['updateChainPollingConfigs'];\n};\n\nexport type TokenBalancesControllerGetChainPollingConfigAction = {\n type: `TokenBalancesController:getChainPollingConfig`;\n handler: TokenBalancesController['getChainPollingConfig'];\n};\n\nexport type TokenBalancesControllerActions =\n | TokenBalancesControllerGetStateAction\n | TokenBalancesControllerUpdateChainPollingConfigsAction\n | TokenBalancesControllerGetChainPollingConfigAction;\n\nexport type TokenBalancesControllerStateChangeEvent =\n ControllerStateChangeEvent<typeof CONTROLLER, TokenBalancesControllerState>;\n\nexport type NativeBalanceEvent = {\n type: `${typeof CONTROLLER}:updatedNativeBalance`;\n payload: unknown[];\n};\n\nexport type TokenBalancesControllerEvents =\n | TokenBalancesControllerStateChangeEvent\n | NativeBalanceEvent;\n\nexport type AllowedActions =\n | NetworkControllerGetNetworkClientByIdAction\n | NetworkControllerGetStateAction\n | TokensControllerGetStateAction\n | TokenDetectionControllerAddDetectedTokensViaWsAction\n | PreferencesControllerGetStateAction\n | AccountsControllerGetSelectedAccountAction\n | AccountsControllerListAccountsAction\n | AccountTrackerControllerGetStateAction\n | AccountTrackerUpdateNativeBalancesAction\n | AccountTrackerUpdateStakedBalancesAction\n | AuthenticationController.AuthenticationControllerGetBearerToken;\n\nexport type AllowedEvents =\n | TokensControllerStateChangeEvent\n | PreferencesControllerStateChangeEvent\n | NetworkControllerStateChangeEvent\n | KeyringControllerAccountRemovedEvent\n | AccountActivityServiceBalanceUpdatedEvent\n | AccountActivityServiceStatusChangedEvent\n | AccountsControllerSelectedEvmAccountChangeEvent;\n\nexport type TokenBalancesControllerMessenger = Messenger<\n typeof CONTROLLER,\n TokenBalancesControllerActions | AllowedActions,\n TokenBalancesControllerEvents | AllowedEvents\n>;\n\nexport type ChainPollingConfig = {\n /** Polling interval in milliseconds for this chain */\n interval: number;\n};\n\nexport type UpdateChainPollingConfigsOptions = {\n /** Whether to immediately fetch balances after updating configs (default: true) */\n immediateUpdate?: boolean;\n};\n\nexport type TokenBalancesControllerOptions = {\n messenger: TokenBalancesControllerMessenger;\n /** Default interval for chains not specified in chainPollingIntervals */\n interval?: number;\n /** Per-chain polling configuration */\n chainPollingIntervals?: Record<ChainIdHex, ChainPollingConfig>;\n state?: Partial<TokenBalancesControllerState>;\n /** When `true`, balances for *all* known accounts are queried. */\n queryMultipleAccounts?: boolean;\n /** Array of chainIds that should use Accounts-API strategy (if supported by API). */\n accountsApiChainIds?: () => ChainIdHex[];\n /** Disable external HTTP calls (privacy / offline mode). */\n allowExternalServices?: () => boolean;\n /** Custom logger. */\n log?: (...args: unknown[]) => void;\n platform?: 'extension' | 'mobile';\n /** Polling interval when WebSocket is active and providing real-time updates */\n websocketActivePollingInterval?: number;\n};\n// endregion\n\n// ────────────────────────────────────────────────────────────────────────────\n// region: Helper utilities\nconst draft = <T>(base: T, fn: (d: T) => void): T => produce(base, fn);\n\nconst ZERO_ADDRESS =\n '0x0000000000000000000000000000000000000000' as ChecksumAddress;\n\nconst checksum = (addr: string): ChecksumAddress =>\n toChecksumHexAddress(addr) as ChecksumAddress;\n\n/**\n * Convert CAIP chain ID or hex chain ID to hex chain ID\n * Handles both CAIP-2 format (e.g., \"eip155:1\") and hex format (e.g., \"0x1\")\n *\n * @param chainId - CAIP chain ID (e.g., \"eip155:1\") or hex chain ID (e.g., \"0x1\")\n * @returns Hex chain ID (e.g., \"0x1\")\n * @throws {Error} If chainId is neither a valid CAIP-2 chain ID nor a hex string\n */\nexport const caipChainIdToHex = (chainId: string): ChainIdHex => {\n if (isStrictHexString(chainId)) {\n return chainId;\n }\n\n if (isCaipChainId(chainId)) {\n return toHex(parseCaipChainId(chainId).reference);\n }\n\n throw new Error('caipChainIdToHex - Failed to provide CAIP-2 or Hex chainId');\n};\n\n/**\n * Extract token address from asset type\n * Returns tuple of [tokenAddress, isNativeToken] or null if invalid\n *\n * @param assetType - Asset type string (e.g., 'eip155:1/erc20:0x...' or 'eip155:1/slip44:60')\n * @returns Tuple of [tokenAddress, isNativeToken] or null if invalid\n */\nexport const parseAssetType = (assetType: string): [string, boolean] | null => {\n if (!isCaipAssetType(assetType)) {\n return null;\n }\n\n const parsed = parseCaipAssetType(assetType);\n\n // ERC20 token (e.g., \"eip155:1/erc20:0x...\")\n if (parsed.assetNamespace === 'erc20') {\n return [parsed.assetReference, false];\n }\n\n // Native token (e.g., \"eip155:1/slip44:60\")\n if (parsed.assetNamespace === 'slip44') {\n return [ZERO_ADDRESS, true];\n }\n\n return null;\n};\n// endregion\n\n// ────────────────────────────────────────────────────────────────────────────\n// region: Main controller\nexport class TokenBalancesController extends StaticIntervalPollingController<{\n chainIds: ChainIdHex[];\n}>()<\n typeof CONTROLLER,\n TokenBalancesControllerState,\n TokenBalancesControllerMessenger\n> {\n readonly #platform: 'extension' | 'mobile';\n\n readonly #queryAllAccounts: boolean;\n\n readonly #accountsApiChainIds: () => ChainIdHex[];\n\n readonly #balanceFetchers: BalanceFetcher[];\n\n #allTokens: TokensControllerState['allTokens'] = {};\n\n #detectedTokens: TokensControllerState['allDetectedTokens'] = {};\n\n #allIgnoredTokens: TokensControllerState['allIgnoredTokens'] = {};\n\n /** Default polling interval for chains without specific configuration */\n readonly #defaultInterval: number;\n\n /** Polling interval when WebSocket is active and providing real-time updates */\n readonly #websocketActivePollingInterval: number;\n\n /** Per-chain polling configuration */\n readonly #chainPollingConfig: Record<ChainIdHex, ChainPollingConfig>;\n\n /** Active polling timers grouped by interval */\n readonly #intervalPollingTimers: Map<number, NodeJS.Timeout> = new Map();\n\n /** Track if controller-level polling is active */\n #isControllerPollingActive = false;\n\n /** Store original chainIds from startPolling to preserve intent */\n #requestedChainIds: ChainIdHex[] = [];\n\n /** Debouncing for rapid status changes to prevent excessive HTTP calls */\n readonly #statusChangeDebouncer: {\n timer: NodeJS.Timeout | null;\n pendingChanges: Map<string, 'up' | 'down'>;\n } = {\n timer: null,\n pendingChanges: new Map(),\n };\n\n constructor({\n messenger,\n interval = DEFAULT_INTERVAL_MS,\n websocketActivePollingInterval = DEFAULT_WEBSOCKET_ACTIVE_POLLING_INTERVAL_MS,\n chainPollingIntervals = {},\n state = {},\n queryMultipleAccounts = true,\n accountsApiChainIds = () => [],\n allowExternalServices = () => true,\n platform,\n }: TokenBalancesControllerOptions) {\n super({\n name: CONTROLLER,\n messenger,\n metadata,\n state: { tokenBalances: {}, ...state },\n });\n\n // Normalize all account addresses to lowercase in existing state\n this.#normalizeAccountAddresses();\n\n this.#platform = platform ?? 'extension';\n this.#queryAllAccounts = queryMultipleAccounts;\n this.#accountsApiChainIds = accountsApiChainIds;\n this.#defaultInterval = interval;\n this.#websocketActivePollingInterval = websocketActivePollingInterval;\n this.#chainPollingConfig = { ...chainPollingIntervals };\n\n // Strategy order: API first, then RPC fallback\n this.#balanceFetchers = [\n ...(accountsApiChainIds().length > 0 && allowExternalServices()\n ? [this.#createAccountsApiFetcher()]\n : []),\n new RpcBalanceFetcher(this.#getProvider, this.#getNetworkClient, () => ({\n allTokens: this.#allTokens,\n allDetectedTokens: this.#detectedTokens,\n })),\n ];\n\n this.setIntervalLength(interval);\n\n // initial token state & subscriptions\n const { allTokens, allDetectedTokens, allIgnoredTokens } =\n this.messenger.call('TokensController:getState');\n this.#allTokens = allTokens;\n this.#detectedTokens = allDetectedTokens;\n this.#allIgnoredTokens = allIgnoredTokens;\n\n this.messenger.subscribe(\n 'TokensController:stateChange',\n (tokensState: TokensControllerState) => {\n this.#onTokensChanged(tokensState).catch((error) => {\n console.warn('Error handling token state change:', error);\n });\n },\n );\n this.messenger.subscribe(\n 'NetworkController:stateChange',\n this.#onNetworkChanged,\n );\n this.messenger.subscribe(\n 'KeyringController:accountRemoved',\n this.#onAccountRemoved,\n );\n this.messenger.subscribe(\n 'AccountsController:selectedEvmAccountChange',\n this.#onAccountChanged,\n );\n\n // Register action handlers for polling interval control\n this.messenger.registerActionHandler(\n `TokenBalancesController:updateChainPollingConfigs`,\n this.updateChainPollingConfigs.bind(this),\n );\n\n this.messenger.registerActionHandler(\n `TokenBalancesController:getChainPollingConfig`,\n this.getChainPollingConfig.bind(this),\n );\n\n // Subscribe to AccountActivityService balance updates for real-time updates\n this.messenger.subscribe(\n 'AccountActivityService:balanceUpdated',\n this.#onAccountActivityBalanceUpdate.bind(this),\n );\n\n // Subscribe to AccountActivityService status changes for dynamic polling management\n this.messenger.subscribe(\n 'AccountActivityService:statusChanged',\n this.#onAccountActivityStatusChanged.bind(this),\n );\n }\n\n /**\n * Normalize all account addresses to lowercase and merge duplicates\n * This handles migration from old state where addresses might be checksummed\n */\n #normalizeAccountAddresses() {\n const currentState = this.state.tokenBalances;\n const normalizedBalances: TokenBalances = {};\n\n // Iterate through all accounts and normalize to lowercase\n for (const address of Object.keys(currentState)) {\n const lowercaseAddress = address.toLowerCase() as ChecksumAddress;\n const accountBalances = currentState[address as ChecksumAddress];\n\n if (!accountBalances) {\n continue;\n }\n\n // If this lowercase address doesn't exist yet, create it\n if (!normalizedBalances[lowercaseAddress]) {\n normalizedBalances[lowercaseAddress] = {};\n }\n\n // Merge chain data\n for (const chainId of Object.keys(accountBalances)) {\n const chainIdKey = chainId as ChainIdHex;\n\n if (!normalizedBalances[lowercaseAddress][chainIdKey]) {\n normalizedBalances[lowercaseAddress][chainIdKey] = {};\n }\n\n // Merge token balances (later values override earlier ones if duplicates exist)\n Object.assign(\n normalizedBalances[lowercaseAddress][chainIdKey],\n accountBalances[chainIdKey],\n );\n }\n }\n\n // Only update if there were changes\n if (\n Object.keys(currentState).length !==\n Object.keys(normalizedBalances).length ||\n Object.keys(currentState).some((addr) => addr !== addr.toLowerCase())\n ) {\n this.update(() => ({ tokenBalances: normalizedBalances }));\n }\n }\n\n #chainIdsWithTokens(): ChainIdHex[] {\n return [\n ...new Set([\n ...Object.keys(this.#allTokens),\n ...Object.keys(this.#detectedTokens),\n ]),\n ] as ChainIdHex[];\n }\n\n readonly #getProvider = (chainId: ChainIdHex): Web3Provider => {\n const { networkConfigurationsByChainId } = this.messenger.call(\n 'NetworkController:getState',\n );\n const cfg = networkConfigurationsByChainId[chainId];\n const { networkClientId } = cfg.rpcEndpoints[cfg.defaultRpcEndpointIndex];\n const client = this.messenger.call(\n 'NetworkController:getNetworkClientById',\n networkClientId,\n );\n return new Web3Provider(client.provider);\n };\n\n readonly #getNetworkClient = (chainId: ChainIdHex) => {\n const { networkConfigurationsByChainId } = this.messenger.call(\n 'NetworkController:getState',\n );\n const cfg = networkConfigurationsByChainId[chainId];\n const { networkClientId } = cfg.rpcEndpoints[cfg.defaultRpcEndpointIndex];\n return this.messenger.call(\n 'NetworkController:getNetworkClientById',\n networkClientId,\n );\n };\n\n /**\n * Creates an AccountsApiBalanceFetcher that only supports chains in the accountsApiChainIds array\n *\n * @returns A BalanceFetcher that wraps AccountsApiBalanceFetcher with chainId filtering\n */\n readonly #createAccountsApiFetcher = (): BalanceFetcher => {\n const originalFetcher = new AccountsApiBalanceFetcher(\n this.#platform,\n this.#getProvider,\n );\n\n return {\n supports: (chainId: ChainIdHex): boolean => {\n // Only support chains that are both:\n // 1. In our specified accountsApiChainIds array\n // 2. Actually supported by the AccountsApi\n return (\n this.#accountsApiChainIds().includes(chainId) &&\n originalFetcher.supports(chainId)\n );\n },\n fetch: originalFetcher.fetch.bind(originalFetcher),\n };\n };\n\n /**\n * Override to support per-chain polling intervals by grouping chains by interval\n *\n * @param options0 - The polling options\n * @param options0.chainIds - Chain IDs to start polling for\n */\n override _startPolling({ chainIds }: { chainIds: ChainIdHex[] }) {\n // Store the original chainIds to preserve intent across config updates\n this.#requestedChainIds = [...chainIds];\n this.#isControllerPollingActive = true;\n this.#startIntervalGroupPolling(chainIds, true);\n }\n\n /**\n * Start or restart interval-based polling for multiple chains\n *\n * @param chainIds - Chain IDs to start polling for\n * @param immediate - Whether to poll immediately before starting timers (default: true)\n */\n #startIntervalGroupPolling(chainIds: ChainIdHex[], immediate = true) {\n // Stop any existing interval timers\n this.#intervalPollingTimers.forEach((timer) => clearInterval(timer));\n this.#intervalPollingTimers.clear();\n\n // Group chains by their polling intervals\n const intervalGroups = new Map<number, ChainIdHex[]>();\n\n for (const chainId of chainIds) {\n const config = this.getChainPollingConfig(chainId);\n const existing = intervalGroups.get(config.interval) || [];\n existing.push(chainId);\n intervalGroups.set(config.interval, existing);\n }\n\n // Start separate polling loop for each interval group\n for (const [interval, chainIdsGroup] of intervalGroups) {\n this.#startPollingForInterval(interval, chainIdsGroup, immediate);\n }\n }\n\n /**\n * Start polling loop for chains that share the same interval\n *\n * @param interval - The polling interval in milliseconds\n * @param chainIds - Chain IDs that share this interval\n * @param immediate - Whether to poll immediately before starting the timer (default: true)\n */\n #startPollingForInterval(\n interval: number,\n chainIds: ChainIdHex[],\n immediate = true,\n ) {\n const pollFunction = async () => {\n if (!this.#isControllerPollingActive) {\n return;\n }\n try {\n await this._executePoll({ chainIds });\n } catch (error) {\n console.warn(\n `Polling failed for chains ${chainIds.join(', ')} with interval ${interval}:`,\n error,\n );\n }\n };\n\n // Poll immediately first if requested\n if (immediate) {\n pollFunction().catch((error) => {\n console.warn(\n `Immediate polling failed for chains ${chainIds.join(', ')}:`,\n error,\n );\n });\n }\n\n // Then start regular interval polling\n this.#setPollingTimer(interval, chainIds, pollFunction);\n }\n\n /**\n * Helper method to set up polling timer\n *\n * @param interval - The polling interval in milliseconds\n * @param chainIds - Chain IDs for this interval\n * @param pollFunction - The function to call on each poll\n */\n #setPollingTimer(\n interval: number,\n chainIds: ChainIdHex[],\n pollFunction: () => Promise<void>,\n ) {\n // Clear any existing timer for this interval first\n const existingTimer = this.#intervalPollingTimers.get(interval);\n if (existingTimer) {\n clearInterval(existingTimer);\n }\n\n const timer = setInterval(() => {\n pollFunction().catch((error) => {\n console.warn(\n `Interval polling failed for chains ${chainIds.join(', ')}:`,\n error,\n );\n });\n }, interval);\n this.#intervalPollingTimers.set(interval, timer);\n }\n\n /**\n * Override to handle our custom polling approach\n *\n * @param tokenSetId - The token set ID to stop polling for\n */\n override _stopPollingByPollingTokenSetId(tokenSetId: string) {\n let parsedTokenSetId;\n let chainsToStop: ChainIdHex[] = [];\n\n try {\n parsedTokenSetId = JSON.parse(tokenSetId);\n chainsToStop = parsedTokenSetId.chainIds || [];\n } catch (error) {\n console.warn('Failed to parse tokenSetId, stopping all polling:', error);\n // Fallback: stop all polling if we can't parse the tokenSetId\n this.#isControllerPollingActive = false;\n this.#requestedChainIds = [];\n this.#intervalPollingTimers.forEach((timer) => clearInterval(timer));\n this.#intervalPollingTimers.clear();\n return;\n }\n\n // Compare with current chains - only stop if it matches our current session\n const currentChainsSet = new Set(this.#requestedChainIds);\n const stopChainsSet = new Set(chainsToStop);\n\n // Check if this stop request is for our current session\n const isCurrentSession =\n currentChainsSet.size === stopChainsSet.size &&\n [...currentChainsSet].every((chain) => stopChainsSet.has(chain));\n\n if (isCurrentSession) {\n this.#isControllerPollingActive = false;\n this.#requestedChainIds = [];\n this.#intervalPollingTimers.forEach((timer) => clearInterval(timer));\n this.#intervalPollingTimers.clear();\n }\n }\n\n /**\n * Get polling configuration for a chain (includes default fallback)\n *\n * @param chainId - The chain ID to get config for\n * @returns The polling configuration for the chain\n */\n getChainPollingConfig(chainId: ChainIdHex): ChainPollingConfig {\n return (\n this.#chainPollingConfig[chainId] ?? {\n interval: this.#defaultInterval,\n }\n );\n }\n\n override async _executePoll({\n chainIds,\n queryAllAccounts = false,\n }: {\n chainIds: ChainIdHex[];\n queryAllAccounts?: boolean;\n }) {\n // This won't be called with our custom implementation, but keep for compatibility\n await this.updateBalances({ chainIds, queryAllAccounts });\n }\n\n /**\n * Update multiple chain polling configurations at once\n *\n * @param configs - Object mapping chain IDs to polling configurations\n * @param options - Optional configuration for the update behavior\n * @param options.immediateUpdate - Whether to immediately fetch balances after updating configs (default: true)\n */\n updateChainPollingConfigs(\n configs: Record<ChainIdHex, ChainPollingConfig>,\n options: UpdateChainPollingConfigsOptions = { immediateUpdate: true },\n ): void {\n Object.assign(this.#chainPollingConfig, configs);\n\n // If polling is currently active, restart with new interval groupings\n if (this.#isControllerPollingActive) {\n // Restart polling with immediate fetch by default, unless explicitly disabled\n this.#startIntervalGroupPolling(\n this.#requestedChainIds,\n options.immediateUpdate,\n );\n }\n }\n\n async updateBalances({\n chainIds,\n queryAllAccounts = false,\n }: { chainIds?: ChainIdHex[]; queryAllAccounts?: boolean } = {}) {\n const targetChains = chainIds ?? this.#chainIdsWithTokens();\n if (!targetChains.length) {\n return;\n }\n\n const { address: selected } = this.messenger.call(\n 'AccountsController:getSelectedAccount',\n );\n const allAccounts = this.messenger.call('AccountsController:listAccounts');\n\n const jwtToken = await safelyExecuteWithTimeout<string | undefined>(\n () => {\n return this.messenger.call('AuthenticationController:getBearerToken');\n },\n false,\n 5000,\n );\n\n const aggregated: ProcessedBalance[] = [];\n let remainingChains = [...targetChains];\n\n // Try each fetcher in order, removing successfully processed chains\n for (const fetcher of this.#balanceFetchers) {\n const supportedChains = remainingChains.filter((c) =>\n fetcher.supports(c),\n );\n if (!supportedChains.length) {\n continue;\n }\n\n try {\n const result = await fetcher.fetch({\n chainIds: supportedChains,\n queryAllAccounts: queryAllAccounts ?? this.#queryAllAccounts,\n selectedAccount: selected as ChecksumAddress,\n allAccounts,\n jwtToken,\n });\n\n if (result.balances && result.balances.length > 0) {\n aggregated.push(...result.balances);\n // Remove chains that were successfully processed\n const processedChains = new Set(\n result.balances.map((b) => b.chainId),\n );\n remainingChains = remainingChains.filter(\n (chain) => !processedChains.has(chain),\n );\n }\n\n // Add unprocessed chains back to remainingChains for next fetcher\n if (\n result.unprocessedChainIds &&\n result.unprocessedChainIds.length > 0\n ) {\n const currentRemainingChains = remainingChains;\n const chainsToAdd = result.unprocessedChainIds.filter(\n (chainId) =>\n supportedChains.includes(chainId) &&\n !currentRemainingChains.includes(chainId),\n );\n remainingChains.push(...chainsToAdd);\n }\n } catch (error) {\n console.warn(\n `Balance fetcher failed for chains ${supportedChains.join(', ')}: ${String(error)}`,\n );\n // Continue to next fetcher (fallback)\n }\n\n // If all chains have been processed, break early\n if (remainingChains.length === 0) {\n break;\n }\n }\n\n // Determine which accounts to process based on queryAllAccounts parameter\n const accountsToProcess =\n (queryAllAccounts ?? this.#queryAllAccounts)\n ? allAccounts.map((a) => a.address as ChecksumAddress)\n : [selected as ChecksumAddress];\n\n const prev = this.state;\n const next = draft(prev, (d) => {\n // Initialize account and chain structures if they don't exist, but preserve existing balances\n for (const chainId of targetChains) {\n for (const account of accountsToProcess) {\n // Ensure the nested structure exists without overwriting existing balances\n d.tokenBalances[account] ??= {};\n d.tokenBalances[account][chainId] ??= {};\n // Initialize tokens from allTokens only if they don't exist yet\n const chainTokens = this.#allTokens[chainId];\n if (chainTokens?.[account]) {\n Object.values(chainTokens[account]).forEach(\n (token: { address: string }) => {\n const tokenAddress = checksum(token.address);\n // Only initialize if the token balance doesn't exist yet\n if (!(tokenAddress in d.tokenBalances[account][chainId])) {\n d.tokenBalances[account][chainId][tokenAddress] = '0x0';\n }\n },\n );\n }\n\n // Initialize tokens from allDetectedTokens only if they don't exist yet\n const detectedChainTokens = this.#detectedTokens[chainId];\n if (detectedChainTokens?.[account]) {\n Object.values(detectedChainTokens[account]).forEach(\n (token: { address: string }) => {\n const tokenAddress = checksum(token.address);\n // Only initialize if the token balance doesn't exist yet\n if (!(tokenAddress in d.tokenBalances[account][chainId])) {\n d.tokenBalances[account][chainId][tokenAddress] = '0x0';\n }\n },\n );\n }\n }\n }\n\n // Update with actual fetched balances only if the value has changed\n aggregated.forEach(({ success, value, account, token, chainId }) => {\n if (success && value !== undefined) {\n // Ensure all accounts we add/update are in lower-case\n const lowerCaseAccount = account.toLowerCase() as ChecksumAddress;\n const newBalance = toHex(value);\n const tokenAddress = checksum(token);\n const currentBalance =\n d.tokenBalances[lowerCaseAccount]?.[chainId]?.[tokenAddress];\n\n // Only update if the balance has actually changed\n if (currentBalance !== newBalance) {\n ((d.tokenBalances[lowerCaseAccount] ??= {})[chainId] ??= {})[\n tokenAddress\n ] = newBalance;\n }\n }\n });\n });\n\n if (!isEqual(prev, next)) {\n this.update(() => next);\n\n const nativeBalances = aggregated.filter(\n (r) => r.success && r.token === ZERO_ADDRESS,\n );\n\n // Get current AccountTracker state to compare existing balances\n const accountTrackerState = this.messenger.call(\n 'AccountTrackerController:getState',\n );\n\n // Update native token balances only if they have changed\n if (nativeBalances.length > 0) {\n const balanceUpdates = nativeBalances\n .map((balance) => ({\n address: balance.account,\n chainId: balance.chainId,\n balance: balance.value ? BNToHex(balance.value) : '0x0',\n }))\n .filter((update) => {\n const currentBalance =\n accountTrackerState.accountsByChainId[update.chainId]?.[\n checksum(update.address)\n ]?.balance;\n // Only include if the balance has actually changed\n return currentBalance !== update.balance;\n });\n\n if (balanceUpdates.length > 0) {\n this.messenger.call(\n 'AccountTrackerController:updateNativeBalances',\n balanceUpdates,\n );\n }\n }\n\n // Filter and update staked balances in a single batch operation for better performance\n const stakedBalances = aggregated.filter((r) => {\n if (!r.success || r.token === ZERO_ADDRESS) {\n return false;\n }\n\n // Check if the chainId and token address match any staking contract\n const stakingContractAddress =\n STAKING_CONTRACT_ADDRESS_BY_CHAINID[\n r.chainId as keyof typeof STAKING_CONTRACT_ADDRESS_BY_CHAINID\n ];\n return (\n stakingContractAddress &&\n stakingContractAddress.toLowerCase() === r.token.toLowerCase()\n );\n });\n\n if (stakedBalances.length > 0) {\n const stakedBalanceUpdates = stakedBalances\n .map((balance) => ({\n address: balance.account,\n chainId: balance.chainId,\n stakedBalance: balance.value ? toHex(balance.value) : '0x0',\n }))\n .filter((update) => {\n const currentStakedBalance =\n accountTrackerState.accountsByChainId[update.chainId]?.[\n checksum(update.address)\n ]?.stakedBalance;\n // Only include if the staked balance has actually changed\n return currentStakedBalance !== update.stakedBalance;\n });\n\n if (stakedBalanceUpdates.length > 0) {\n this.messenger.call(\n 'AccountTrackerController:updateStakedBalances',\n stakedBalanceUpdates,\n );\n }\n }\n }\n }\n\n resetState() {\n this.update(() => ({ tokenBalances: {} }));\n }\n\n /**\n * Helper method to check if a token is tracked (exists in allTokens or allIgnoredTokens)\n *\n * @param tokenAddress - The token address to check\n * @param account - The account address\n * @param chainId - The chain ID\n * @returns True if the token is tracked (imported or ignored)\n */\n #isTokenTracked(\n tokenAddress: string,\n account: ChecksumAddress,\n chainId: ChainIdHex,\n ): boolean {\n // Check if token exists in allTokens\n if (\n this.#allTokens?.[chainId]?.[account.toLowerCase()]?.some(\n (token) => token.address === tokenAddress,\n )\n ) {\n return true;\n }\n\n // Check if token exists in allIgnoredTokens\n if (\n this.#allIgnoredTokens?.[chainId]?.[account.toLowerCase()]?.some(\n (token) => token === tokenAddress,\n )\n ) {\n return true;\n }\n\n return false;\n }\n\n readonly #onTokensChanged = async (state: TokensControllerState) => {\n const changed: ChainIdHex[] = [];\n let hasChanges = false;\n\n // Get chains that have existing balances\n const chainsWithBalances = new Set<ChainIdHex>();\n for (const address of Object.keys(this.state.tokenBalances)) {\n const addressKey = address as ChecksumAddress;\n for (const chainId of Object.keys(\n this.state.tokenBalances[addressKey] || {},\n )) {\n chainsWithBalances.add(chainId as ChainIdHex);\n }\n }\n\n // Only process chains that are explicitly mentioned in the incoming state change\n const incomingChainIds = new Set([\n ...Object.keys(state.allTokens),\n ...Object.keys(state.allDetectedTokens),\n ]);\n\n // Only proceed if there are actual changes to chains that have balances or are being added\n const relevantChainIds = Array.from(incomingChainIds).filter((chainId) => {\n const id = chainId as ChainIdHex;\n\n const hasTokensNow =\n (state.allTokens[id] && Object.keys(state.allTokens[id]).length > 0) ||\n (state.allDetectedTokens[id] &&\n Object.keys(state.allDetectedTokens[id]).length > 0);\n const hadTokensBefore =\n (this.#allTokens[id] && Object.keys(this.#allTokens[id]).length > 0) ||\n (this.#detectedTokens[id] &&\n Object.keys(this.#detectedTokens[id]).length > 0);\n\n // Check if there's an actual change in token state\n const hasTokenChange =\n !isEqual(state.allTokens[id], this.#allTokens[id]) ||\n !isEqual(state.allDetectedTokens[id], this.#detectedTokens[id]);\n\n // Process chains that have actual changes OR are new chains getting tokens\n return hasTokenChange || (!hadTokensBefore && hasTokensNow);\n });\n\n if (relevantChainIds.length === 0) {\n // No relevant changes, just update internal state\n this.#allTokens = state.allTokens;\n this.#detectedTokens = state.allDetectedTokens;\n return;\n }\n\n // Handle both cleanup and updates in a single state update\n this.update((s) => {\n for (const chainId of relevantChainIds) {\n const id = chainId as ChainIdHex;\n const hasTokensNow =\n (state.allTokens[id] &&\n Object.keys(state.allTokens[id]).length > 0) ||\n (state.allDetectedTokens[id] &&\n Object.keys(state.allDetectedTokens[id]).length > 0);\n const hadTokensBefore =\n (this.#allTokens[id] &&\n Object.keys(this.#allTokens[id]).length > 0) ||\n (this.#detectedTokens[id] &&\n Object.keys(this.#detectedTokens[id]).length > 0);\n\n if (\n !isEqual(state.allTokens[id], this.#allTokens[id]) ||\n !isEqual(state.allDetectedTokens[id], this.#detectedTokens[id])\n ) {\n if (hasTokensNow) {\n // Chain still has tokens - mark for async balance update\n changed.push(id);\n } else if (hadTokensBefore) {\n // Chain had tokens before but doesn't now - clean up balances immediately\n for (const address of Object.keys(s.tokenBalances)) {\n const addressKey = address as ChecksumAddress;\n if (s.tokenBalances[addressKey]?.[id]) {\n s.tokenBalances[addressKey][id] = {};\n hasChanges = true;\n }\n }\n }\n }\n }\n });\n\n this.#allTokens = state.allTokens;\n this.#detectedTokens = state.allDetectedTokens;\n this.#allIgnoredTokens = state.allIgnoredTokens;\n\n // Only update balances for chains that still have tokens (and only if we haven't already updated state)\n if (changed.length && !hasChanges) {\n this.updateBalances({ chainIds: changed }).catch((error) => {\n console.warn('Error updating balances after token change:', error);\n });\n }\n };\n\n readonly #onNetworkChanged = (state: NetworkState) => {\n // Check if any networks were removed by comparing with previous state\n const currentNetworks = new Set(\n Object.keys(state.networkConfigurationsByChainId),\n );\n\n // Get all networks that currently have balances\n const networksWithBalances = new Set<string>();\n for (const address of Object.keys(this.state.tokenBalances)) {\n const addressKey = address as ChecksumAddress;\n for (const network of Object.keys(\n this.state.tokenBalances[addressKey] || {},\n )) {\n networksWithBalances.add(network);\n }\n }\n\n // Find networks that were removed\n const removedNetworks = Array.from(networksWithBalances).filter(\n (network) => !currentNetworks.has(network),\n );\n\n if (removedNetworks.length > 0) {\n this.update((s) => {\n // Remove balances for all accounts on the deleted networks\n for (const address of Object.keys(s.tokenBalances)) {\n const addressKey = address as ChecksumAddress;\n for (const removedNetwork of removedNetworks) {\n const networkKey = removedNetwork as ChainIdHex;\n if (s.tokenBalances[addressKey]?.[networkKey]) {\n delete s.tokenBalances[addressKey][networkKey];\n }\n }\n }\n });\n }\n };\n\n readonly #onAccountRemoved = (addr: string) => {\n if (!isStrictHexString(addr) || !isValidHexAddress(addr)) {\n return;\n }\n this.update((s) => {\n delete s.tokenBalances[addr as ChecksumAddress];\n });\n };\n\n /**\n * Handle account selection changes\n * Triggers immediate balance fetch to ensure we have the latest balances\n * since WebSocket only provides updates for changes going forward\n */\n readonly #onAccountChanged = () => {\n // Fetch balances for all chains with tokens when account changes\n const chainIds = this.#chainIdsWithTokens();\n if (chainIds.length > 0) {\n this.updateBalances({ chainIds }).catch(() => {\n // Silently handle polling errors\n });\n }\n };\n\n // ────────────────────────────────────────────────────────────────────────────\n // AccountActivityService integration helpers\n\n /**\n * Prepare balance updates from AccountActivityService\n * Processes all updates and returns categorized results\n * Throws an error if any updates have validation/parsing issues\n *\n * @param updates - Array of balance updates from AccountActivityService\n * @param account - Lowercase account address (for consistency with tokenBalances state format)\n * @param chainId - Hex chain ID\n * @returns Object containing arrays of token balances, new token addresses to add, and native balance updates\n * @throws Error if any balance update has validation or parsing errors\n */\n #prepareBalanceUpdates(\n updates: BalanceUpdate[],\n account: ChecksumAddress,\n chainId: ChainIdHex,\n ): {\n tokenBalances: { tokenAddress: ChecksumAddress; balance: Hex }[];\n newTokens: string[];\n nativeBalanceUpdates: { address: string; chainId: Hex; balance: Hex }[];\n } {\n const tokenBalances: { tokenAddress: ChecksumAddress; balance: Hex }[] = [];\n const newTokens: string[] = [];\n const nativeBalanceUpdates: {\n address: string;\n chainId: Hex;\n balance: Hex;\n }[] = [];\n\n for (const update of updates) {\n const { asset, postBalance } = update;\n\n // Throw if balance update has an error\n if (postBalance.error) {\n throw new Error('Balance update has error');\n }\n\n // Parse token address from asset type\n const parsed = parseAssetType(asset.type);\n if (!parsed) {\n throw new Error('Failed to parse asset type');\n }\n\n const [tokenAddress, isNativeToken] = parsed;\n\n // Validate token address\n if (\n !isStrictHexString(tokenAddress) ||\n !isValidHexAddress(tokenAddress)\n ) {\n throw new Error('Invalid token address');\n }\n\n const checksumTokenAddress = checksum(tokenAddress);\n const isTracked = this.#isTokenTracked(\n checksumTokenAddress,\n account,\n chainId,\n );\n\n // postBalance.amount is in hex format (raw units)\n const balanceHex = postBalance.amount as Hex;\n\n // Add token balance (tracked tokens, ignored tokens, and native tokens all get balance updates)\n tokenBalances.push({\n tokenAddress: checksumTokenAddress,\n balance: balanceHex,\n });\n\n // Add native balance update if this is a native token\n if (isNativeToken) {\n nativeBalanceUpdates.push({\n address: account,\n chainId,\n balance: balanceHex,\n });\n }\n\n // Handle untracked ERC20 tokens - queue for import\n if (!isNativeToken && !isTracked) {\n newTokens.push(checksumTokenAddress);\n }\n }\n\n return { tokenBalances, newTokens, nativeBalanceUpdates };\n }\n\n // ────────────────────────────────────────────────────────────────────────────\n // AccountActivityService event handlers\n\n /**\n * Handle real-time balance updates from AccountActivityService\n * Processes balance updates and updates the token balance state\n * If any balance update has an error, triggers fallback polling for the chain\n *\n * @param options0 - Balance update parameters\n * @param options0.address - Account address\n * @param options0.chain - CAIP chain identifier\n * @param options0.updates - Array of balance updates for the account\n */\n readonly #onAccountActivityBalanceUpdate = async ({\n address,\n chain,\n updates,\n }: {\n address: string;\n chain: string;\n updates: BalanceUpdate[];\n }) => {\n const chainId = caipChainIdToHex(chain);\n const checksummedAccount = checksum(address);\n\n try {\n // Process all balance updates at once\n const { tokenBalances, newTokens, nativeBalanceUpdates } =\n this.#prepareBalanceUpdates(updates, checksummedAccount, chainId);\n\n // Update state once with all token balances\n if (tokenBalances.length > 0) {\n this.update((state) => {\n // Temporary until ADR to normalize all keys - tokenBalances state requires: account in lowercase, token in checksum\n const lowercaseAccount =\n checksummedAccount.toLowerCase() as ChecksumAddress;\n state.tokenBalances[lowercaseAccount] ??= {};\n state.tokenBalances[lowercaseAccount][chainId] ??= {};\n\n // Apply all token balance updates\n for (const { tokenAddress, balance } of tokenBalances) {\n state.tokenBalances[lowercaseAccount][chainId][tokenAddress] =\n balance;\n }\n });\n }\n\n // Update native balances in AccountTrackerController\n if (nativeBalanceUpdates.length > 0) {\n this.messenger.call(\n 'AccountTrackerController:updateNativeBalances',\n nativeBalanceUpdates,\n );\n }\n\n // Import any new tokens that were discovered (balance already updated from websocket)\n if (newTokens.length > 0) {\n await this.messenger.call(\n 'TokenDetectionController:addDetectedTokensViaWs',\n {\n tokensSlice: newTokens,\n chainId: chainId as Hex,\n },\n );\n }\n } catch (error) {\n console.warn(\n `Error updating balances from AccountActivityService for chain ${chain}, account ${address}:`,\n error,\n );\n console.warn('Balance update data:', JSON.stringify(updates, null, 2));\n\n // On error, trigger fallback polling\n await this.updateBalances({ chainIds: [chainId] }).catch(() => {\n // Silently handle polling errors\n });\n }\n };\n\n /**\n * Handle status changes from AccountActivityService\n * Uses aggressive debouncing to prevent excessive HTTP calls from rapid up/down changes\n *\n * @param options0 - Status change event data\n * @param options0.chainIds - Array of chain identifiers\n * @param options0.status - Connection status ('up' for connected, 'down' for disconnected)\n */\n readonly #onAccountActivityStatusChanged = ({\n chainIds,\n status,\n }: {\n chainIds: string[];\n status: 'up' | 'down';\n }) => {\n // Update pending changes (latest status wins for each chain)\n for (const chainId of chainIds) {\n this.#statusChangeDebouncer.pendingChanges.set(chainId, status);\n }\n\n // Clear existing timer to extend debounce window\n if (this.#statusChangeDebouncer.timer) {\n clearTimeout(this.#statusChangeDebouncer.timer);\n }\n\n // Set new timer - only process changes after activity settles\n this.#statusChangeDebouncer.timer = setTimeout(() => {\n this.#processAccumulatedStatusChanges();\n }, 5000); // 5-second debounce window\n };\n\n /**\n * Process all accumulated status changes in one batch to minimize HTTP calls\n */\n #processAccumulatedStatusChanges(): void {\n const changes = Array.from(\n this.#statusChangeDebouncer.pendingChanges.entries(),\n );\n this.#statusChangeDebouncer.pendingChanges.clear();\n this.#statusChangeDebouncer.timer = null;\n\n if (changes.length === 0) {\n return;\n }\n\n // Calculate final polling configurations\n const chainConfigs: Record<ChainIdHex, { interval: number }> = {};\n\n for (const [chainId, status] of changes) {\n // Convert CAIP format (eip155:1) to hex format (0x1)\n // chainId is always in CAIP format from AccountActivityService\n const hexChainId = caipChainIdToHex(chainId);\n\n if (status === 'down') {\n // Chain is down - use default polling since no real-time updates available\n chainConfigs[hexChainId] = { interval: this.#defaultInterval };\n } else {\n // Chain is up - use longer intervals since WebSocket provides real-time updates\n chainConfigs[hexChainId] = {\n interval: this.#websocketActivePollingInterval,\n };\n }\n }\n\n // Add jitter to prevent synchronized requests across instances\n const jitterDelay = Math.random() * this.#defaultInterval; // 0 to default interval\n\n setTimeout(() => {\n this.updateChainPollingConfigs(chainConfigs, { immediateUpdate: true });\n }, jitterDelay);\n }\n\n /**\n * Clean up all timers and resources when controller is destroyed\n */\n override destroy(): void {\n this.#isControllerPollingActive = false;\n this.#intervalPollingTimers.forEach((timer) => clearInterval(timer));\n this.#intervalPollingTimers.clear();\n\n // Clean up debouncing timer\n if (this.#statusChangeDebouncer.timer) {\n clearTimeout(this.#statusChangeDebouncer.timer);\n this.#statusChangeDebouncer.timer = null;\n }\n\n // Unregister action handlers\n this.messenger.unregisterActionHandler(\n `TokenBalancesController:updateChainPollingConfigs`,\n );\n this.messenger.unregisterActionHandler(\n `TokenBalancesController:getChainPollingConfig`,\n );\n\n super.destroy();\n }\n}\n\nexport default TokenBalancesController;\n"]}
|
package/dist/token-service.cjs
CHANGED
|
@@ -39,7 +39,7 @@ function getTokenSearchURL(chainIds, query, limit = 10, includeMarketData = fals
|
|
|
39
39
|
const encodedChainIds = chainIds
|
|
40
40
|
.map((id) => encodeURIComponent(id))
|
|
41
41
|
.join(',');
|
|
42
|
-
return `${exports.TOKEN_END_POINT_API}/tokens/search?
|
|
42
|
+
return `${exports.TOKEN_END_POINT_API}/tokens/search?networks=${encodedChainIds}&query=${encodedQuery}&limit=${limit}&includeMarketData=${includeMarketData}`;
|
|
43
43
|
}
|
|
44
44
|
/**
|
|
45
45
|
* Get the trending tokens URL for the given networks and search query.
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"token-service.cjs","sourceRoot":"","sources":["../src/token-service.ts"],"names":[],"mappings":"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","sourcesContent":["import {\n ChainId,\n convertHexToDecimal,\n handleFetch,\n timeoutFetch,\n} from '@metamask/controller-utils';\nimport type { 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) {\n const occurrenceFloor = chainId === ChainId['linea-mainnet'] ? 1 : 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`;\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) {\n return `${TOKEN_END_POINT_API}/token/${convertHexToDecimal(\n chainId,\n )}?address=${tokenAddress}`;\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 chainIds - Array of CAIP format chain IDs (e.g., 'eip155:1', 'solana:5eykt4UsFv8P8NJdTREpY1vzqKqZKvdp').\n * @param query - The search query (token name, symbol, or address).\n * @param limit - Optional limit for the number of results (defaults to 10).\n * @param includeMarketData - Optional flag to include market data in the results (defaults to false).\n * @returns The token search URL.\n */\nfunction getTokenSearchURL(\n chainIds: CaipChainId[],\n query: string,\n limit = 10,\n includeMarketData = false,\n) {\n const encodedQuery = encodeURIComponent(query);\n const encodedChainIds = chainIds\n .map((id) => encodeURIComponent(id))\n .join(',');\n return `${TOKEN_END_POINT_API}/tokens/search?chainIds=${encodedChainIds}&query=${encodedQuery}&limit=${limit}&includeMarketData=${includeMarketData}`;\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.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 * @returns The trending tokens URL.\n */\nfunction getTrendingTokensURL(options: {\n chainIds: CaipChainId[];\n sortBy?: SortTrendingBy;\n minLiquidity?: number;\n minVolume24hUsd?: number;\n maxVolume24hUsd?: number;\n minMarketCap?: number;\n maxMarketCap?: number;\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, ...rest } = options;\n Object.entries(rest).forEach(([key, value]) => {\n if (value !== undefined) {\n queryParams.append(key, String(value));\n }\n });\n\n return `${TOKEN_END_POINT_API}/v3/tokens/trending?chainIds=${encodedChainIds}${queryParams.toString() ? `&${queryParams.toString()}` : ''}`;\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 elm.aggregators.includes('lineaTeam') || elm.aggregators.length >= 3,\n );\n }\n return result;\n }\n return undefined;\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 * @returns Object containing count and data array. Returns { count: 0, data: [] } if request fails.\n */\nexport async function searchTokens(\n chainIds: CaipChainId[],\n query: string,\n { limit = 10, includeMarketData = false } = {},\n): Promise<{ count: number; data: unknown[] }> {\n const tokenSearchURL = getTokenSearchURL(\n chainIds,\n query,\n limit,\n includeMarketData,\n );\n\n try {\n const result = 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: [] };\n } catch (error) {\n // Handle 400 errors and other failures by returning count 0 and empty array\n console.log('Search request failed:', error);\n return { count: 0, data: [] };\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};\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 * @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}: {\n chainIds: CaipChainId[];\n sortBy?: SortTrendingBy;\n minLiquidity?: number;\n minVolume24hUsd?: number;\n maxVolume24hUsd?: number;\n minMarketCap?: number;\n maxMarketCap?: number;\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 sortBy,\n minLiquidity,\n minVolume24hUsd,\n maxVolume24hUsd,\n minMarketCap,\n maxMarketCap,\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 * 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<T>(\n chainId: Hex,\n tokenAddress: string,\n abortSignal: AbortSignal,\n { timeout = defaultTimeout } = {},\n): Promise<T | 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<T>;\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 };\n fetchOptions.headers = new window.Headers();\n fetchOptions.headers.set('Content-Type', 'application/json');\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"]}
|
|
1
|
+
{"version":3,"file":"token-service.cjs","sourceRoot":"","sources":["../src/token-service.ts"],"names":[],"mappings":"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","sourcesContent":["import {\n ChainId,\n convertHexToDecimal,\n handleFetch,\n timeoutFetch,\n} from '@metamask/controller-utils';\nimport type { 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) {\n const occurrenceFloor = chainId === ChainId['linea-mainnet'] ? 1 : 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`;\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) {\n return `${TOKEN_END_POINT_API}/token/${convertHexToDecimal(\n chainId,\n )}?address=${tokenAddress}`;\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 chainIds - Array of CAIP format chain IDs (e.g., 'eip155:1', 'solana:5eykt4UsFv8P8NJdTREpY1vzqKqZKvdp').\n * @param query - The search query (token name, symbol, or address).\n * @param limit - Optional limit for the number of results (defaults to 10).\n * @param includeMarketData - Optional flag to include market data in the results (defaults to false).\n * @returns The token search URL.\n */\nfunction getTokenSearchURL(\n chainIds: CaipChainId[],\n query: string,\n limit = 10,\n includeMarketData = false,\n) {\n const encodedQuery = encodeURIComponent(query);\n const encodedChainIds = chainIds\n .map((id) => encodeURIComponent(id))\n .join(',');\n return `${TOKEN_END_POINT_API}/tokens/search?networks=${encodedChainIds}&query=${encodedQuery}&limit=${limit}&includeMarketData=${includeMarketData}`;\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.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 * @returns The trending tokens URL.\n */\nfunction getTrendingTokensURL(options: {\n chainIds: CaipChainId[];\n sortBy?: SortTrendingBy;\n minLiquidity?: number;\n minVolume24hUsd?: number;\n maxVolume24hUsd?: number;\n minMarketCap?: number;\n maxMarketCap?: number;\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, ...rest } = options;\n Object.entries(rest).forEach(([key, value]) => {\n if (value !== undefined) {\n queryParams.append(key, String(value));\n }\n });\n\n return `${TOKEN_END_POINT_API}/v3/tokens/trending?chainIds=${encodedChainIds}${queryParams.toString() ? `&${queryParams.toString()}` : ''}`;\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 elm.aggregators.includes('lineaTeam') || elm.aggregators.length >= 3,\n );\n }\n return result;\n }\n return undefined;\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 * @returns Object containing count and data array. Returns { count: 0, data: [] } if request fails.\n */\nexport async function searchTokens(\n chainIds: CaipChainId[],\n query: string,\n { limit = 10, includeMarketData = false } = {},\n): Promise<{ count: number; data: unknown[] }> {\n const tokenSearchURL = getTokenSearchURL(\n chainIds,\n query,\n limit,\n includeMarketData,\n );\n\n try {\n const result = 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: [] };\n } catch (error) {\n // Handle 400 errors and other failures by returning count 0 and empty array\n console.log('Search request failed:', error);\n return { count: 0, data: [] };\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};\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 * @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}: {\n chainIds: CaipChainId[];\n sortBy?: SortTrendingBy;\n minLiquidity?: number;\n minVolume24hUsd?: number;\n maxVolume24hUsd?: number;\n minMarketCap?: number;\n maxMarketCap?: number;\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 sortBy,\n minLiquidity,\n minVolume24hUsd,\n maxVolume24hUsd,\n minMarketCap,\n maxMarketCap,\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 * 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<T>(\n chainId: Hex,\n tokenAddress: string,\n abortSignal: AbortSignal,\n { timeout = defaultTimeout } = {},\n): Promise<T | 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<T>;\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 };\n fetchOptions.headers = new window.Headers();\n fetchOptions.headers.set('Content-Type', 'application/json');\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"]}
|
package/dist/token-service.mjs
CHANGED
|
@@ -36,7 +36,7 @@ function getTokenSearchURL(chainIds, query, limit = 10, includeMarketData = fals
|
|
|
36
36
|
const encodedChainIds = chainIds
|
|
37
37
|
.map((id) => encodeURIComponent(id))
|
|
38
38
|
.join(',');
|
|
39
|
-
return `${TOKEN_END_POINT_API}/tokens/search?
|
|
39
|
+
return `${TOKEN_END_POINT_API}/tokens/search?networks=${encodedChainIds}&query=${encodedQuery}&limit=${limit}&includeMarketData=${includeMarketData}`;
|
|
40
40
|
}
|
|
41
41
|
/**
|
|
42
42
|
* Get the trending tokens URL for the given networks and search query.
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"token-service.mjs","sourceRoot":"","sources":["../src/token-service.ts"],"names":[],"mappings":"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","sourcesContent":["import {\n ChainId,\n convertHexToDecimal,\n handleFetch,\n timeoutFetch,\n} from '@metamask/controller-utils';\nimport type { 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) {\n const occurrenceFloor = chainId === ChainId['linea-mainnet'] ? 1 : 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`;\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) {\n return `${TOKEN_END_POINT_API}/token/${convertHexToDecimal(\n chainId,\n )}?address=${tokenAddress}`;\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 chainIds - Array of CAIP format chain IDs (e.g., 'eip155:1', 'solana:5eykt4UsFv8P8NJdTREpY1vzqKqZKvdp').\n * @param query - The search query (token name, symbol, or address).\n * @param limit - Optional limit for the number of results (defaults to 10).\n * @param includeMarketData - Optional flag to include market data in the results (defaults to false).\n * @returns The token search URL.\n */\nfunction getTokenSearchURL(\n chainIds: CaipChainId[],\n query: string,\n limit = 10,\n includeMarketData = false,\n) {\n const encodedQuery = encodeURIComponent(query);\n const encodedChainIds = chainIds\n .map((id) => encodeURIComponent(id))\n .join(',');\n return `${TOKEN_END_POINT_API}/tokens/search?chainIds=${encodedChainIds}&query=${encodedQuery}&limit=${limit}&includeMarketData=${includeMarketData}`;\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.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 * @returns The trending tokens URL.\n */\nfunction getTrendingTokensURL(options: {\n chainIds: CaipChainId[];\n sortBy?: SortTrendingBy;\n minLiquidity?: number;\n minVolume24hUsd?: number;\n maxVolume24hUsd?: number;\n minMarketCap?: number;\n maxMarketCap?: number;\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, ...rest } = options;\n Object.entries(rest).forEach(([key, value]) => {\n if (value !== undefined) {\n queryParams.append(key, String(value));\n }\n });\n\n return `${TOKEN_END_POINT_API}/v3/tokens/trending?chainIds=${encodedChainIds}${queryParams.toString() ? `&${queryParams.toString()}` : ''}`;\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 elm.aggregators.includes('lineaTeam') || elm.aggregators.length >= 3,\n );\n }\n return result;\n }\n return undefined;\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 * @returns Object containing count and data array. Returns { count: 0, data: [] } if request fails.\n */\nexport async function searchTokens(\n chainIds: CaipChainId[],\n query: string,\n { limit = 10, includeMarketData = false } = {},\n): Promise<{ count: number; data: unknown[] }> {\n const tokenSearchURL = getTokenSearchURL(\n chainIds,\n query,\n limit,\n includeMarketData,\n );\n\n try {\n const result = 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: [] };\n } catch (error) {\n // Handle 400 errors and other failures by returning count 0 and empty array\n console.log('Search request failed:', error);\n return { count: 0, data: [] };\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};\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 * @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}: {\n chainIds: CaipChainId[];\n sortBy?: SortTrendingBy;\n minLiquidity?: number;\n minVolume24hUsd?: number;\n maxVolume24hUsd?: number;\n minMarketCap?: number;\n maxMarketCap?: number;\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 sortBy,\n minLiquidity,\n minVolume24hUsd,\n maxVolume24hUsd,\n minMarketCap,\n maxMarketCap,\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 * 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<T>(\n chainId: Hex,\n tokenAddress: string,\n abortSignal: AbortSignal,\n { timeout = defaultTimeout } = {},\n): Promise<T | 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<T>;\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 };\n fetchOptions.headers = new window.Headers();\n fetchOptions.headers.set('Content-Type', 'application/json');\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"]}
|
|
1
|
+
{"version":3,"file":"token-service.mjs","sourceRoot":"","sources":["../src/token-service.ts"],"names":[],"mappings":"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","sourcesContent":["import {\n ChainId,\n convertHexToDecimal,\n handleFetch,\n timeoutFetch,\n} from '@metamask/controller-utils';\nimport type { 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) {\n const occurrenceFloor = chainId === ChainId['linea-mainnet'] ? 1 : 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`;\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) {\n return `${TOKEN_END_POINT_API}/token/${convertHexToDecimal(\n chainId,\n )}?address=${tokenAddress}`;\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 chainIds - Array of CAIP format chain IDs (e.g., 'eip155:1', 'solana:5eykt4UsFv8P8NJdTREpY1vzqKqZKvdp').\n * @param query - The search query (token name, symbol, or address).\n * @param limit - Optional limit for the number of results (defaults to 10).\n * @param includeMarketData - Optional flag to include market data in the results (defaults to false).\n * @returns The token search URL.\n */\nfunction getTokenSearchURL(\n chainIds: CaipChainId[],\n query: string,\n limit = 10,\n includeMarketData = false,\n) {\n const encodedQuery = encodeURIComponent(query);\n const encodedChainIds = chainIds\n .map((id) => encodeURIComponent(id))\n .join(',');\n return `${TOKEN_END_POINT_API}/tokens/search?networks=${encodedChainIds}&query=${encodedQuery}&limit=${limit}&includeMarketData=${includeMarketData}`;\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.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 * @returns The trending tokens URL.\n */\nfunction getTrendingTokensURL(options: {\n chainIds: CaipChainId[];\n sortBy?: SortTrendingBy;\n minLiquidity?: number;\n minVolume24hUsd?: number;\n maxVolume24hUsd?: number;\n minMarketCap?: number;\n maxMarketCap?: number;\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, ...rest } = options;\n Object.entries(rest).forEach(([key, value]) => {\n if (value !== undefined) {\n queryParams.append(key, String(value));\n }\n });\n\n return `${TOKEN_END_POINT_API}/v3/tokens/trending?chainIds=${encodedChainIds}${queryParams.toString() ? `&${queryParams.toString()}` : ''}`;\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 elm.aggregators.includes('lineaTeam') || elm.aggregators.length >= 3,\n );\n }\n return result;\n }\n return undefined;\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 * @returns Object containing count and data array. Returns { count: 0, data: [] } if request fails.\n */\nexport async function searchTokens(\n chainIds: CaipChainId[],\n query: string,\n { limit = 10, includeMarketData = false } = {},\n): Promise<{ count: number; data: unknown[] }> {\n const tokenSearchURL = getTokenSearchURL(\n chainIds,\n query,\n limit,\n includeMarketData,\n );\n\n try {\n const result = 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: [] };\n } catch (error) {\n // Handle 400 errors and other failures by returning count 0 and empty array\n console.log('Search request failed:', error);\n return { count: 0, data: [] };\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};\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 * @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}: {\n chainIds: CaipChainId[];\n sortBy?: SortTrendingBy;\n minLiquidity?: number;\n minVolume24hUsd?: number;\n maxVolume24hUsd?: number;\n minMarketCap?: number;\n maxMarketCap?: number;\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 sortBy,\n minLiquidity,\n minVolume24hUsd,\n maxVolume24hUsd,\n minMarketCap,\n maxMarketCap,\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 * 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<T>(\n chainId: Hex,\n tokenAddress: string,\n abortSignal: AbortSignal,\n { timeout = defaultTimeout } = {},\n): Promise<T | 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<T>;\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 };\n fetchOptions.headers = new window.Headers();\n fetchOptions.headers.set('Content-Type', 'application/json');\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"]}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@metamask/assets-controllers",
|
|
3
|
-
"version": "
|
|
3
|
+
"version": "93.0.0",
|
|
4
4
|
"description": "Controllers which manage interactions involving ERC-20, ERC-721, and ERC-1155 tokens (including NFTs)",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"MetaMask",
|
|
@@ -68,7 +68,7 @@
|
|
|
68
68
|
"@metamask/messenger": "^0.3.0",
|
|
69
69
|
"@metamask/metamask-eth-abis": "^3.1.1",
|
|
70
70
|
"@metamask/multichain-account-service": "^4.0.0",
|
|
71
|
-
"@metamask/network-controller": "^
|
|
71
|
+
"@metamask/network-controller": "^27.0.0",
|
|
72
72
|
"@metamask/permission-controller": "^12.1.1",
|
|
73
73
|
"@metamask/phishing-controller": "^16.1.0",
|
|
74
74
|
"@metamask/polling-controller": "^16.0.0",
|
|
@@ -78,7 +78,7 @@
|
|
|
78
78
|
"@metamask/snaps-controllers": "^14.0.1",
|
|
79
79
|
"@metamask/snaps-sdk": "^9.0.0",
|
|
80
80
|
"@metamask/snaps-utils": "^11.0.0",
|
|
81
|
-
"@metamask/transaction-controller": "^62.
|
|
81
|
+
"@metamask/transaction-controller": "^62.4.0",
|
|
82
82
|
"@metamask/utils": "^11.8.1",
|
|
83
83
|
"@types/bn.js": "^5.1.5",
|
|
84
84
|
"@types/uuid": "^8.3.0",
|