{"version":3,"file":"TokenDetectionController.cjs","sourceRoot":"","sources":["../src/TokenDetectionController.ts"],"names":[],"mappings":"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","sourcesContent":["import type {\n  AccountsControllerGetSelectedAccountAction,\n  AccountsControllerGetAccountAction,\n  AccountsControllerSelectedEvmAccountChangeEvent,\n} from '@metamask/accounts-controller';\nimport type {\n  ControllerGetStateAction,\n  ControllerStateChangeEvent,\n} from '@metamask/base-controller';\nimport contractMap from '@metamask/contract-metadata';\nimport {\n  ASSET_TYPES,\n  ChainId,\n  ERC20,\n  safelyExecute,\n  isEqualCaseInsensitive,\n  toChecksumHexAddress,\n} from '@metamask/controller-utils';\nimport type {\n  KeyringControllerGetStateAction,\n  KeyringControllerLockEvent,\n  KeyringControllerUnlockEvent,\n} from '@metamask/keyring-controller';\nimport type { InternalAccount } from '@metamask/keyring-internal-api';\nimport type { Messenger } from '@metamask/messenger';\nimport type {\n  NetworkClientId,\n  NetworkControllerFindNetworkClientIdByChainIdAction,\n  NetworkControllerGetNetworkClientByIdAction,\n  NetworkControllerGetNetworkConfigurationByNetworkClientId,\n  NetworkControllerGetStateAction,\n  NetworkControllerNetworkDidChangeEvent,\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 { TransactionControllerTransactionConfirmedEvent } from '@metamask/transaction-controller';\nimport type { Hex } from '@metamask/utils';\nimport { isEqual, mapValues, isObject, get } from 'lodash';\n\nimport type { AssetsContractController } from './AssetsContractController';\nimport { isTokenDetectionSupportedForNetwork } from './assetsUtil';\nimport { SUPPORTED_NETWORKS_ACCOUNTS_API_V4 } from './constants';\nimport type { TokenDetectionControllerMethodActions } from './TokenDetectionController-method-action-types';\nimport type {\n  GetTokenListState,\n  TokenListMap,\n  TokenListStateChange,\n  TokensChainsCache,\n} from './TokenListController';\nimport type { Token } from './TokenRatesController';\nimport type { TokensControllerGetStateAction } from './TokensController';\nimport type {\n  TokensControllerAddDetectedTokensAction,\n  TokensControllerAddTokensAction,\n} from './TokensController-method-action-types';\n\nconst DEFAULT_INTERVAL = 180000;\n\ntype LegacyToken = {\n  name: string;\n  logo: `${string}.svg`;\n  symbol: string;\n  decimals: number;\n  erc20?: boolean;\n  erc721?: boolean;\n};\n\ntype TokenDetectionMap = {\n  [P in keyof TokenListMap]: Omit<TokenListMap[P], 'occurrences'>;\n};\n\ntype NetworkClient = {\n  chainId: Hex;\n  networkClientId: string;\n};\n\nexport const STATIC_MAINNET_TOKEN_LIST = Object.entries<LegacyToken>(\n  contractMap,\n).reduce<TokenDetectionMap>((acc, [base, contract]) => {\n  const { logo, erc20, erc721, ...tokenMetadata } = contract;\n  return {\n    ...acc,\n    [base.toLowerCase()]: {\n      ...tokenMetadata,\n      address: base.toLowerCase(),\n      iconUrl: `images/contract/${logo}`,\n      aggregators: [],\n    },\n  };\n}, {});\n\n/**\n * Function that takes a TokensChainsCache object and maps chainId with TokenListMap.\n *\n * @param tokensChainsCache - TokensChainsCache input object\n * @returns returns the map of chainId with TokenListMap\n */\nexport function mapChainIdWithTokenListMap(\n  tokensChainsCache: TokensChainsCache,\n): Record<string, unknown> {\n  return mapValues(tokensChainsCache, (value) => {\n    if (isObject(value) && 'data' in value) {\n      return get(value, ['data']);\n    }\n    return value;\n  });\n}\n\nexport const controllerName = 'TokenDetectionController';\n\nexport type TokenDetectionState = Record<never, never>;\n\nexport type TokenDetectionControllerGetStateAction = ControllerGetStateAction<\n  typeof controllerName,\n  TokenDetectionState\n>;\n\nexport type TokenDetectionControllerActions =\n  | TokenDetectionControllerGetStateAction\n  | TokenDetectionControllerMethodActions;\n\nexport type AllowedActions =\n  | AccountsControllerGetSelectedAccountAction\n  | AccountsControllerGetAccountAction\n  | NetworkControllerGetNetworkClientByIdAction\n  | NetworkControllerGetNetworkConfigurationByNetworkClientId\n  | NetworkControllerGetStateAction\n  | GetTokenListState\n  | KeyringControllerGetStateAction\n  | PreferencesControllerGetStateAction\n  | TokensControllerGetStateAction\n  | TokensControllerAddDetectedTokensAction\n  | TokensControllerAddTokensAction\n  | NetworkControllerFindNetworkClientIdByChainIdAction\n  | AuthenticationController.AuthenticationControllerGetBearerTokenAction;\n\nexport type TokenDetectionControllerStateChangeEvent =\n  ControllerStateChangeEvent<typeof controllerName, TokenDetectionState>;\n\nexport type TokenDetectionControllerEvents =\n  TokenDetectionControllerStateChangeEvent;\n\nexport type AllowedEvents =\n  | AccountsControllerSelectedEvmAccountChangeEvent\n  | NetworkControllerNetworkDidChangeEvent\n  | TokenListStateChange\n  | KeyringControllerLockEvent\n  | KeyringControllerUnlockEvent\n  | PreferencesControllerStateChangeEvent\n  | TransactionControllerTransactionConfirmedEvent;\n\nexport type TokenDetectionControllerMessenger = Messenger<\n  typeof controllerName,\n  TokenDetectionControllerActions | AllowedActions,\n  TokenDetectionControllerEvents | AllowedEvents\n>;\n\n/** The input to start polling for the {@link TokenDetectionController} */\ntype TokenDetectionPollingInput = {\n  chainIds: Hex[];\n  address: string;\n};\n\nconst MESSENGER_EXPOSED_METHODS = [\n  'addDetectedTokensViaWs',\n  'addDetectedTokensViaPolling',\n  'detectTokens',\n  'enable',\n  'disable',\n  'start',\n  'stop',\n] as const;\n\n/**\n * Controller that passively polls on a set interval for Tokens auto detection\n *\n * intervalId - Polling interval used to fetch new token rates\n *\n * selectedAddress - Vault selected address\n *\n * networkClientId - The network client ID of the current selected network\n *\n * disabled - Boolean to track if network requests are blocked\n *\n * isUnlocked - Boolean to track if the keyring state is unlocked\n *\n * isDetectionEnabledFromPreferences - Boolean to track if detection is enabled from PreferencesController\n *\n */\nexport class TokenDetectionController extends StaticIntervalPollingController<TokenDetectionPollingInput>()<\n  typeof controllerName,\n  TokenDetectionState,\n  TokenDetectionControllerMessenger\n> {\n  #intervalId?: ReturnType<typeof setTimeout>;\n\n  #selectedAccountId: string;\n\n  #tokensChainsCache: TokensChainsCache = {};\n\n  #disabled: boolean;\n\n  #isUnlocked: boolean;\n\n  #isDetectionEnabledFromPreferences: boolean;\n\n  readonly #useTokenDetection: () => boolean;\n\n  readonly #useExternalServices: () => boolean;\n\n  readonly #getBalancesInSingleCall: AssetsContractController['getBalancesInSingleCall'];\n\n  readonly #trackMetaMetricsEvent: (options: {\n    event: string;\n    category: string;\n    properties: {\n      tokens: string[];\n      // eslint-disable-next-line @typescript-eslint/naming-convention\n      token_standard: string;\n      // eslint-disable-next-line @typescript-eslint/naming-convention\n      asset_type: string;\n    };\n  }) => void;\n\n  /**\n   * Creates a TokenDetectionController instance.\n   *\n   * @param options - The controller options.\n   * @param options.messenger - The controller messenger.\n   * @param options.disabled - If set to true, all network requests are blocked.\n   * @param options.interval - Polling interval used to fetch new token rates\n   * @param options.getBalancesInSingleCall - Gets the balances of a list of tokens for the given address.\n   * @param options.trackMetaMetricsEvent - Sets options for MetaMetrics event tracking.\n   * @param options.useTokenDetection - Feature Switch for using token detection (default: true)\n   * @param options.useExternalServices - Feature Switch for using external services (default: false)\n   */\n  constructor({\n    interval = DEFAULT_INTERVAL,\n    disabled = true,\n    getBalancesInSingleCall,\n    trackMetaMetricsEvent,\n    messenger,\n    useTokenDetection = (): boolean => true,\n    useExternalServices = (): boolean => true,\n  }: {\n    interval?: number;\n    disabled?: boolean;\n    getBalancesInSingleCall: AssetsContractController['getBalancesInSingleCall'];\n    trackMetaMetricsEvent: (options: {\n      event: string;\n      category: string;\n      properties: {\n        tokens: string[];\n        // eslint-disable-next-line @typescript-eslint/naming-convention\n        token_standard: string;\n        // eslint-disable-next-line @typescript-eslint/naming-convention\n        asset_type: string;\n      };\n    }) => void;\n    messenger: TokenDetectionControllerMessenger;\n    useTokenDetection?: () => boolean;\n    useExternalServices?: () => boolean;\n  }) {\n    super({\n      name: controllerName,\n      messenger,\n      state: {},\n      metadata: {},\n    });\n\n    messenger.registerMethodActionHandlers(this, MESSENGER_EXPOSED_METHODS);\n\n    this.#disabled = disabled;\n    this.setIntervalLength(interval);\n\n    this.#selectedAccountId = this.#getSelectedAccount().id;\n\n    const { tokensChainsCache } = this.messenger.call(\n      'TokenListController:getState',\n    );\n\n    this.#tokensChainsCache = tokensChainsCache;\n\n    const { useTokenDetection: defaultUseTokenDetection } = this.messenger.call(\n      'PreferencesController:getState',\n    );\n    this.#isDetectionEnabledFromPreferences = defaultUseTokenDetection;\n\n    this.#getBalancesInSingleCall = getBalancesInSingleCall;\n\n    this.#trackMetaMetricsEvent = trackMetaMetricsEvent;\n\n    const { isUnlocked } = this.messenger.call('KeyringController:getState');\n    this.#isUnlocked = isUnlocked;\n\n    this.#useTokenDetection = useTokenDetection;\n    this.#useExternalServices = useExternalServices;\n\n    this.#registerEventListeners();\n  }\n\n  /**\n   * Constructor helper for registering this controller's messenger subscriptions to controller events.\n   */\n  #registerEventListeners(): void {\n    this.messenger.subscribe('KeyringController:unlock', () => {\n      this.#isUnlocked = true;\n      this.#restartTokenDetection().catch(() => {\n        // Silently handle token detection errors\n      });\n    });\n\n    this.messenger.subscribe('KeyringController:lock', () => {\n      this.#isUnlocked = false;\n      this.#stopPolling();\n    });\n\n    this.messenger.subscribe(\n      'TokenListController:stateChange',\n      ({ tokensChainsCache }) => {\n        const isEqualValues = this.#compareTokensChainsCache(\n          tokensChainsCache,\n          this.#tokensChainsCache,\n        );\n        if (!isEqualValues) {\n          this.#restartTokenDetection().catch(() => {\n            // Silently handle token detection errors\n          });\n        }\n      },\n    );\n\n    this.messenger.subscribe(\n      'PreferencesController:stateChange',\n      ({ useTokenDetection }) => {\n        const selectedAccount = this.#getSelectedAccount();\n        const isDetectionChangedFromPreferences =\n          this.#isDetectionEnabledFromPreferences !== useTokenDetection;\n\n        this.#isDetectionEnabledFromPreferences = useTokenDetection;\n\n        if (isDetectionChangedFromPreferences) {\n          this.#restartTokenDetection({\n            selectedAddress: selectedAccount.address,\n          }).catch(() => {\n            // Silently handle token detection errors\n          });\n        }\n      },\n    );\n\n    this.messenger.subscribe(\n      'AccountsController:selectedEvmAccountChange',\n      (selectedAccount) => {\n        const { networkConfigurationsByChainId } = this.messenger.call(\n          'NetworkController:getState',\n        );\n\n        const chainIds = Object.keys(networkConfigurationsByChainId) as Hex[];\n        const isSelectedAccountIdChanged =\n          this.#selectedAccountId !== selectedAccount.id;\n        if (isSelectedAccountIdChanged) {\n          this.#selectedAccountId = selectedAccount.id;\n          this.#restartTokenDetection({\n            selectedAddress: selectedAccount.address,\n            chainIds,\n          }).catch(() => {\n            // Silently handle token detection errors\n          });\n        }\n      },\n    );\n\n    this.messenger.subscribe(\n      'TransactionController:transactionConfirmed',\n      (transactionMeta) => {\n        this.detectTokens({\n          chainIds: [transactionMeta.chainId],\n        }).catch(() => {\n          // Silently handle token detection errors\n        });\n      },\n    );\n  }\n\n  /**\n   * Allows controller to make active and passive polling requests\n   */\n  enable(): void {\n    this.#disabled = false;\n  }\n\n  /**\n   * Blocks controller from making network calls\n   */\n  disable(): void {\n    this.#disabled = true;\n  }\n\n  /**\n   * Internal isActive state\n   *\n   * @returns Whether the controller is active (not disabled and keyring is unlocked)\n   */\n  get isActive(): boolean {\n    return !this.#disabled && this.#isUnlocked;\n  }\n\n  /**\n   * Start polling for detected tokens.\n   */\n  async start(): Promise<void> {\n    this.enable();\n    await this.#startPolling();\n  }\n\n  /**\n   * Stop polling for detected tokens.\n   */\n  stop(): void {\n    this.disable();\n    this.#stopPolling();\n  }\n\n  #stopPolling(): void {\n    if (this.#intervalId) {\n      clearInterval(this.#intervalId);\n    }\n  }\n\n  /**\n   * Starts a new polling interval.\n   */\n  async #startPolling(): Promise<void> {\n    if (!this.isActive) {\n      return;\n    }\n    this.#stopPolling();\n    await this.detectTokens();\n    // TODO: Either fix this lint violation or explain why it's necessary to ignore.\n    // eslint-disable-next-line @typescript-eslint/no-misused-promises\n    this.#intervalId = setInterval(async () => {\n      await this.detectTokens();\n    }, this.getIntervalLength());\n  }\n\n  /**\n   * Compares current and previous tokensChainsCache object focusing only on the data object.\n   *\n   * @param tokensChainsCache - current tokensChainsCache input object\n   * @param previousTokensChainsCache - previous tokensChainsCache input object\n   * @returns boolean indicating if the two objects are equal\n   */\n\n  #compareTokensChainsCache(\n    tokensChainsCache: TokensChainsCache,\n    previousTokensChainsCache: TokensChainsCache,\n  ): boolean {\n    const cleanPreviousTokensChainsCache = mapChainIdWithTokenListMap(\n      previousTokensChainsCache,\n    );\n    const cleanTokensChainsCache =\n      mapChainIdWithTokenListMap(tokensChainsCache);\n    const isEqualValues = isEqual(\n      cleanTokensChainsCache,\n      cleanPreviousTokensChainsCache,\n    );\n    return isEqualValues;\n  }\n\n  #getCorrectNetworkClientIdByChainId(\n    chainIds: Hex[] | undefined,\n  ): { chainId: Hex; networkClientId: NetworkClientId }[] {\n    const { networkConfigurationsByChainId, selectedNetworkClientId } =\n      this.messenger.call('NetworkController:getState');\n\n    if (!chainIds) {\n      const networkConfiguration = this.messenger.call(\n        'NetworkController:getNetworkConfigurationByNetworkClientId',\n        selectedNetworkClientId,\n      );\n\n      return [\n        {\n          chainId: networkConfiguration?.chainId ?? ChainId.mainnet,\n          networkClientId: selectedNetworkClientId,\n        },\n      ];\n    }\n\n    return chainIds.map((chainId) => {\n      const configuration = networkConfigurationsByChainId[chainId];\n      return {\n        chainId,\n        networkClientId:\n          configuration.rpcEndpoints[configuration.defaultRpcEndpointIndex]\n            .networkClientId,\n      };\n    });\n  }\n\n  async _executePoll({\n    chainIds,\n    address,\n  }: TokenDetectionPollingInput): Promise<void> {\n    if (!this.isActive) {\n      return;\n    }\n    await this.detectTokens({\n      chainIds,\n      selectedAddress: address,\n    });\n  }\n\n  /**\n   * Restart token detection polling period and call detectNewTokens\n   * in case of address change or user session initialization.\n   *\n   * @param options - Options for restart token detection.\n   * @param options.selectedAddress - the selectedAddress against which to detect for token balances\n   * @param options.chainIds - The chain IDs of the network client to use.\n   */\n  async #restartTokenDetection({\n    selectedAddress,\n    chainIds,\n  }: {\n    selectedAddress?: string;\n    chainIds?: Hex[];\n  } = {}): Promise<void> {\n    await this.detectTokens({\n      chainIds,\n      selectedAddress,\n    });\n    this.setIntervalLength(DEFAULT_INTERVAL);\n  }\n\n  #shouldDetectTokens(chainId: Hex): boolean {\n    if (!isTokenDetectionSupportedForNetwork(chainId)) {\n      return false;\n    }\n    if (\n      !this.#isDetectionEnabledFromPreferences &&\n      chainId !== ChainId.mainnet\n    ) {\n      return false;\n    }\n\n    const isMainnetDetectionInactive =\n      !this.#isDetectionEnabledFromPreferences && chainId === ChainId.mainnet;\n    if (isMainnetDetectionInactive) {\n      this.#tokensChainsCache = this.#getConvertedStaticMainnetTokenList();\n    } else {\n      const { tokensChainsCache } = this.messenger.call(\n        'TokenListController:getState',\n      );\n      this.#tokensChainsCache = tokensChainsCache ?? {};\n    }\n\n    return true;\n  }\n\n  async #detectTokensUsingRpc(\n    chainsToDetectUsingRpc: NetworkClient[],\n    addressToDetect: string,\n  ): Promise<void> {\n    for (const { chainId, networkClientId } of chainsToDetectUsingRpc) {\n      if (!this.#shouldDetectTokens(chainId)) {\n        continue;\n      }\n\n      const tokenCandidateSlices = this.#getSlicesOfTokensToDetect({\n        chainId,\n        selectedAddress: addressToDetect,\n      });\n      const tokenDetectionPromises = tokenCandidateSlices.map((tokensSlice) =>\n        this.#addDetectedTokens({\n          tokensSlice,\n          selectedAddress: addressToDetect,\n          networkClientId,\n          chainId,\n        }),\n      );\n\n      await Promise.all(tokenDetectionPromises);\n    }\n  }\n\n  /**\n   * For each token in the token list provided by the TokenListController, checks the token's balance for the selected account address on the active network.\n   * On mainnet, if token detection is disabled in preferences, ERC20 token auto detection will be triggered for each contract address in the legacy token list from the @metamask/contract-metadata repo.\n   *\n   * @param options - Options for token detection.\n   * @param options.chainIds - The chain IDs of the network client to use.\n   * @param options.selectedAddress - the selectedAddress against which to detect for token balances.\n   * @param options.forceRpc - Force RPC-based token detection for all specified chains,\n   * bypassing external services check and ensuring RPC is used even for chains\n   * that might otherwise be handled by the Accounts API.\n   */\n  async detectTokens({\n    chainIds,\n    selectedAddress,\n    forceRpc = false,\n  }: {\n    chainIds?: Hex[];\n    selectedAddress?: string;\n    forceRpc?: boolean;\n  } = {}): Promise<void> {\n    if (!this.isActive) {\n      return;\n    }\n\n    // When forceRpc is true, bypass the useTokenDetection check to ensure RPC detection runs\n    if (!forceRpc && !this.#useTokenDetection()) {\n      return;\n    }\n\n    // If external services are disabled and not forcing RPC, skip all detection\n    if (!forceRpc && !this.#useExternalServices()) {\n      return;\n    }\n\n    const addressToDetect = selectedAddress ?? this.#getSelectedAddress();\n    const clientNetworks = this.#getCorrectNetworkClientIdByChainId(chainIds);\n\n    // If forceRpc is true, use RPC for all chains\n    // Otherwise, skip chains supported by Accounts API (they are handled by TokenBalancesController)\n    const chainsToDetectUsingRpc = forceRpc\n      ? clientNetworks\n      : clientNetworks.filter(\n          ({ chainId }) =>\n            !SUPPORTED_NETWORKS_ACCOUNTS_API_V4.includes(chainId),\n        );\n\n    if (chainsToDetectUsingRpc.length === 0) {\n      return;\n    }\n\n    await this.#detectTokensUsingRpc(chainsToDetectUsingRpc, addressToDetect);\n  }\n\n  #getSlicesOfTokensToDetect({\n    chainId,\n    selectedAddress,\n  }: {\n    chainId: Hex;\n    selectedAddress: string;\n  }): string[][] {\n    const { allTokens, allDetectedTokens, allIgnoredTokens } =\n      this.messenger.call('TokensController:getState');\n    const [tokensAddresses, detectedTokensAddresses, ignoredTokensAddresses] = [\n      allTokens,\n      allDetectedTokens,\n      allIgnoredTokens,\n    ].map((tokens) =>\n      (tokens[chainId]?.[selectedAddress] ?? []).map((value) =>\n        typeof value === 'string' ? value : value.address,\n      ),\n    );\n\n    const tokensToDetect: string[] = [];\n    for (const tokenAddress of Object.keys(\n      this.#tokensChainsCache?.[chainId]?.data || {},\n    )) {\n      if (\n        [\n          tokensAddresses,\n          detectedTokensAddresses,\n          ignoredTokensAddresses,\n        ].every(\n          (addresses) =>\n            !addresses.find((address) =>\n              isEqualCaseInsensitive(address, tokenAddress),\n            ),\n        )\n      ) {\n        tokensToDetect.push(tokenAddress);\n      }\n    }\n\n    const slicesOfTokensToDetect = [];\n    for (let i = 0, size = 1000; i < tokensToDetect.length; i += size) {\n      slicesOfTokensToDetect.push(tokensToDetect.slice(i, i + size));\n    }\n\n    return slicesOfTokensToDetect;\n  }\n\n  #getConvertedStaticMainnetTokenList(): TokensChainsCache {\n    const data: TokenListMap = Object.entries(STATIC_MAINNET_TOKEN_LIST).reduce(\n      (acc, [key, value]) => ({\n        ...acc,\n        [key]: {\n          name: value.name,\n          symbol: value.symbol,\n          decimals: value.decimals,\n          address: value.address,\n          aggregators: [],\n          iconUrl: value?.iconUrl,\n        },\n      }),\n      {},\n    );\n    return {\n      '0x1': {\n        data,\n        timestamp: 0,\n      },\n    };\n  }\n\n  async #addDetectedTokens({\n    tokensSlice,\n    selectedAddress,\n    networkClientId,\n    chainId,\n  }: {\n    tokensSlice: string[];\n    selectedAddress: string;\n    networkClientId: NetworkClientId;\n    chainId: Hex;\n  }): Promise<void> {\n    await safelyExecute(async () => {\n      const balances = await this.#getBalancesInSingleCall(\n        selectedAddress,\n        tokensSlice,\n        networkClientId,\n      );\n\n      const tokensWithBalance: Token[] = [];\n      const eventTokensDetails: string[] = [];\n      for (const nonZeroTokenAddress of Object.keys(balances)) {\n        const { decimals, symbol, aggregators, iconUrl, name, rwaData } =\n          this.#tokensChainsCache[chainId].data[nonZeroTokenAddress];\n        eventTokensDetails.push(`${symbol} - ${nonZeroTokenAddress}`);\n        tokensWithBalance.push({\n          address: nonZeroTokenAddress,\n          decimals,\n          symbol,\n          aggregators,\n          image: iconUrl,\n          isERC721: false,\n          name,\n          ...(rwaData && { rwaData }),\n        });\n      }\n\n      if (tokensWithBalance.length) {\n        this.#trackMetaMetricsEvent({\n          event: 'Token Detected',\n          category: 'Wallet',\n          properties: {\n            tokens: eventTokensDetails,\n            token_standard: ERC20,\n            asset_type: ASSET_TYPES.TOKEN,\n          },\n        });\n\n        await this.messenger.call(\n          'TokensController:addTokens',\n          tokensWithBalance,\n          networkClientId,\n        );\n      }\n    });\n  }\n\n  /**\n   * Add tokens detected from websocket balance updates\n   * This method:\n   * - Checks if useTokenDetection preference is enabled (skips if disabled)\n   * - Checks if external services are enabled (skips if disabled)\n   * - Tokens are expected to be in the tokensChainsCache with full metadata\n   * - Balance fetching is skipped since balances are provided by the websocket\n   * - Ignored tokens have been filtered out by the caller\n   *\n   * @param options - The options object\n   * @param options.tokensSlice - Array of token addresses detected from websocket (already filtered to exclude ignored tokens)\n   * @param options.chainId - Hex chain ID\n   * @returns Promise that resolves when tokens are added\n   */\n  async addDetectedTokensViaWs({\n    tokensSlice,\n    chainId,\n  }: {\n    tokensSlice: string[];\n    chainId: Hex;\n  }): Promise<void> {\n    // Check if token detection is enabled via preferences\n    if (!this.#useTokenDetection()) {\n      return;\n    }\n\n    // Check if external services are enabled (websocket requires external services)\n    if (!this.#useExternalServices()) {\n      return;\n    }\n\n    // Refresh the token cache to ensure we have the latest token metadata\n    // This fixes a bug where the cache from construction time could be stale/empty\n    const { tokensChainsCache } = this.messenger.call(\n      'TokenListController:getState',\n    );\n    this.#tokensChainsCache = tokensChainsCache ?? {};\n\n    const tokensWithBalance: Token[] = [];\n    const eventTokensDetails: string[] = [];\n\n    for (const tokenAddress of tokensSlice) {\n      // Normalize addresses explicitly (don't assume input format)\n      const lowercaseTokenAddress = tokenAddress.toLowerCase();\n      const checksummedTokenAddress = toChecksumHexAddress(tokenAddress);\n\n      // Check map of validated tokens (cache keys are lowercase)\n      const tokenData =\n        this.#tokensChainsCache[chainId]?.data?.[lowercaseTokenAddress];\n\n      if (!tokenData) {\n        continue;\n      }\n\n      const { decimals, symbol, aggregators, iconUrl, name, rwaData } =\n        tokenData;\n\n      // Push to lists with checksummed address (for allTokens storage)\n      eventTokensDetails.push(`${symbol} - ${checksummedTokenAddress}`);\n      tokensWithBalance.push({\n        address: checksummedTokenAddress,\n        decimals,\n        symbol,\n        aggregators,\n        image: iconUrl,\n        isERC721: false,\n        name,\n        ...(rwaData && { rwaData }),\n      });\n    }\n\n    // Perform addition\n    if (tokensWithBalance.length) {\n      this.#trackMetaMetricsEvent({\n        event: 'Token Detected',\n        category: 'Wallet',\n        properties: {\n          tokens: eventTokensDetails,\n          token_standard: ERC20,\n          asset_type: ASSET_TYPES.TOKEN,\n        },\n      });\n\n      const networkClientId = this.messenger.call(\n        'NetworkController:findNetworkClientIdByChainId',\n        chainId,\n      );\n\n      await this.messenger.call(\n        'TokensController:addTokens',\n        tokensWithBalance,\n        networkClientId,\n      );\n    }\n  }\n\n  /**\n   * Add tokens detected from polling balance updates\n   * This method:\n   * - Checks if useTokenDetection preference is enabled (skips if disabled)\n   * - Checks if external services are enabled (skips if disabled)\n   * - Filters out tokens already in allTokens or allIgnoredTokens\n   * - Tokens are expected to be in the tokensChainsCache with full metadata\n   * - Balance fetching is skipped since balances are provided by the caller\n   *\n   * @param options - The options object\n   * @param options.tokensSlice - Array of token addresses detected from polling\n   * @param options.chainId - Hex chain ID\n   * @returns Promise that resolves when tokens are added\n   */\n  async addDetectedTokensViaPolling({\n    tokensSlice,\n    chainId,\n  }: {\n    tokensSlice: string[];\n    chainId: Hex;\n  }): Promise<void> {\n    // Check if token detection is enabled via preferences\n    if (!this.#useTokenDetection()) {\n      return;\n    }\n\n    // Check if external services are enabled (polling via API requires external services)\n    if (!this.#useExternalServices()) {\n      return;\n    }\n\n    // Refresh the token cache to ensure we have the latest token metadata\n    // This fixes a bug where the cache from construction time could be stale/empty\n    const { tokensChainsCache } = this.messenger.call(\n      'TokenListController:getState',\n    );\n    this.#tokensChainsCache = tokensChainsCache ?? {};\n\n    const selectedAddress = this.#getSelectedAddress();\n\n    // Get current token states to filter out already tracked/ignored tokens\n    const { allTokens, allIgnoredTokens } = this.messenger.call(\n      'TokensController:getState',\n    );\n\n    const existingTokenAddresses = (\n      allTokens[chainId]?.[selectedAddress] ?? []\n    ).map((token) => token.address.toLowerCase());\n\n    const ignoredTokenAddresses = (\n      allIgnoredTokens[chainId]?.[selectedAddress] ?? []\n    ).map((address) => address.toLowerCase());\n\n    const tokensWithBalance: Token[] = [];\n    const eventTokensDetails: string[] = [];\n\n    for (const tokenAddress of tokensSlice) {\n      const lowercaseTokenAddress = tokenAddress.toLowerCase();\n      const checksummedTokenAddress = toChecksumHexAddress(tokenAddress);\n\n      // Skip tokens already in allTokens\n      if (existingTokenAddresses.includes(lowercaseTokenAddress)) {\n        continue;\n      }\n\n      // Skip tokens in allIgnoredTokens\n      if (ignoredTokenAddresses.includes(lowercaseTokenAddress)) {\n        continue;\n      }\n\n      // Check map of validated tokens (cache keys are lowercase)\n      const tokenData =\n        this.#tokensChainsCache[chainId]?.data?.[lowercaseTokenAddress];\n\n      if (!tokenData) {\n        continue;\n      }\n\n      const { decimals, symbol, aggregators, iconUrl, name, rwaData } =\n        tokenData;\n\n      eventTokensDetails.push(`${symbol} - ${checksummedTokenAddress}`);\n      tokensWithBalance.push({\n        address: checksummedTokenAddress,\n        decimals,\n        symbol,\n        aggregators,\n        image: iconUrl,\n        isERC721: false,\n        name,\n        ...(rwaData && { rwaData }),\n      });\n    }\n\n    // Perform addition\n    if (tokensWithBalance.length) {\n      this.#trackMetaMetricsEvent({\n        event: 'Token Detected',\n        category: 'Wallet',\n        properties: {\n          tokens: eventTokensDetails,\n          token_standard: ERC20,\n          asset_type: ASSET_TYPES.TOKEN,\n        },\n      });\n\n      const networkClientId = this.messenger.call(\n        'NetworkController:findNetworkClientIdByChainId',\n        chainId,\n      );\n\n      await this.messenger.call(\n        'TokensController:addTokens',\n        tokensWithBalance,\n        networkClientId,\n      );\n    }\n  }\n\n  #getSelectedAccount(): InternalAccount {\n    return this.messenger.call('AccountsController:getSelectedAccount');\n  }\n\n  #getSelectedAddress(): string {\n    // If the address is not defined (or empty), we fallback to the currently selected account's address\n    const account = this.messenger.call(\n      'AccountsController:getAccount',\n      this.#selectedAccountId,\n    );\n    return account?.address ?? '';\n  }\n}\n\nexport default TokenDetectionController;\n"]}