{"version":3,"file":"MultichainAssetsController.mjs","sourceRoot":"","sources":["../../src/MultichainAssetsController/MultichainAssetsController.ts"],"names":[],"mappings":"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","sourcesContent":["import type {\n  AccountsControllerAccountAddedEvent,\n  AccountsControllerAccountAssetListUpdatedEvent,\n  AccountsControllerAccountRemovedEvent,\n  AccountsControllerListMultichainAccountsAction,\n} from '@metamask/accounts-controller';\nimport { BaseController } from '@metamask/base-controller';\nimport type {\n  ControllerGetStateAction,\n  ControllerStateChangeEvent,\n  StateMetadata,\n} from '@metamask/base-controller';\nimport { isEvmAccountType } from '@metamask/keyring-api';\nimport type {\n  AccountAssetListUpdatedEventPayload,\n  CaipAssetType,\n  CaipAssetTypeOrId,\n} from '@metamask/keyring-api';\nimport type { InternalAccount } from '@metamask/keyring-internal-api';\nimport { KeyringClient } from '@metamask/keyring-snap-client';\nimport type { Messenger } from '@metamask/messenger';\nimport type {\n  GetPermissions,\n  PermissionConstraint,\n  SubjectPermissions,\n} from '@metamask/permission-controller';\nimport type {\n  BulkTokenScanResponse,\n  PhishingControllerBulkScanTokensAction,\n} from '@metamask/phishing-controller';\nimport { TokenScanResultType } from '@metamask/phishing-controller';\nimport type {\n  SnapControllerGetRunnableSnapsAction,\n  SnapControllerHandleRequestAction,\n} from '@metamask/snaps-controllers';\nimport type { FungibleAssetMetadata, Snap, SnapId } from '@metamask/snaps-sdk';\nimport { HandlerType } from '@metamask/snaps-utils';\nimport { isCaipAssetType, parseCaipAssetType } from '@metamask/utils';\nimport type { CaipChainId } from '@metamask/utils';\nimport type { Json, JsonRpcRequest } from '@metamask/utils';\nimport type { MutexInterface } from 'async-mutex';\nimport { Mutex } from 'async-mutex';\n\nimport type { MultichainAssetsControllerMethodActions } from './MultichainAssetsController-method-action-types';\nimport { getChainIdsCaveat } from './utils';\n\nconst controllerName = 'MultichainAssetsController';\n\nexport type MultichainAssetsControllerState = {\n  assetsMetadata: {\n    [asset: CaipAssetType]: FungibleAssetMetadata;\n  };\n  accountsAssets: { [account: string]: CaipAssetType[] };\n  allIgnoredAssets: { [account: string]: CaipAssetType[] };\n};\n\n// Represents the response of the asset snap's onAssetLookup handler\nexport type AssetMetadataResponse = {\n  assets: {\n    [asset: CaipAssetType]: FungibleAssetMetadata;\n  };\n};\n\nexport type MultichainAssetsControllerAccountAssetListUpdatedEvent = {\n  type: `${typeof controllerName}:accountAssetListUpdated`;\n  payload: AccountsControllerAccountAssetListUpdatedEvent['payload'];\n};\n\n/**\n * Constructs the default {@link MultichainAssetsController} state. This allows\n * consumers to provide a partial state object when initializing the controller\n * and also helps in constructing complete state objects for this controller in\n * tests.\n *\n * @returns The default {@link MultichainAssetsController} state.\n */\nexport function getDefaultMultichainAssetsControllerState(): MultichainAssetsControllerState {\n  return { accountsAssets: {}, assetsMetadata: {}, allIgnoredAssets: {} };\n}\n\n/**\n * Returns the state of the {@link MultichainAssetsController}.\n */\nexport type MultichainAssetsControllerGetStateAction = ControllerGetStateAction<\n  typeof controllerName,\n  MultichainAssetsControllerState\n>;\n\n/**\n * Event emitted when the state of the {@link MultichainAssetsController} changes.\n */\nexport type MultichainAssetsControllerStateChangeEvent =\n  ControllerStateChangeEvent<\n    typeof controllerName,\n    MultichainAssetsControllerState\n  >;\n\n/**\n * Actions exposed by the {@link MultichainAssetsController}.\n */\nexport type MultichainAssetsControllerActions =\n  | MultichainAssetsControllerGetStateAction\n  | MultichainAssetsControllerMethodActions;\n\n/**\n * Events emitted by {@link MultichainAssetsController}.\n */\nexport type MultichainAssetsControllerEvents =\n  | MultichainAssetsControllerStateChangeEvent\n  | MultichainAssetsControllerAccountAssetListUpdatedEvent;\n\n/**\n * A function executed within a mutually exclusive lock, with\n * a mutex releaser in its option bag.\n *\n * @param releaseLock - A function to release the lock.\n */\ntype MutuallyExclusiveCallback<Result> = ({\n  releaseLock,\n}: {\n  releaseLock: MutexInterface.Releaser;\n}) => Promise<Result>;\n\n/**\n * Actions that this controller is allowed to call.\n */\ntype AllowedActions =\n  | SnapControllerGetRunnableSnapsAction\n  | SnapControllerHandleRequestAction\n  | GetPermissions\n  | AccountsControllerListMultichainAccountsAction\n  | PhishingControllerBulkScanTokensAction;\n\n/**\n * Events that this controller is allowed to subscribe.\n */\ntype AllowedEvents =\n  | AccountsControllerAccountAddedEvent\n  | AccountsControllerAccountRemovedEvent\n  | AccountsControllerAccountAssetListUpdatedEvent;\n\n/**\n * Messenger type for the MultichainAssetsController.\n */\nexport type MultichainAssetsControllerMessenger = Messenger<\n  typeof controllerName,\n  MultichainAssetsControllerActions | AllowedActions,\n  MultichainAssetsControllerEvents | AllowedEvents\n>;\n\n/**\n * {@link MultichainAssetsController}'s metadata.\n *\n * This allows us to choose if fields of the state should be persisted or not\n * using the `persist` flag; and if they can be sent to Sentry or not, using\n * the `anonymous` flag.\n */\nconst assetsControllerMetadata: StateMetadata<MultichainAssetsControllerState> =\n  {\n    assetsMetadata: {\n      includeInStateLogs: false,\n      persist: true,\n      includeInDebugSnapshot: false,\n      usedInUi: true,\n    },\n    accountsAssets: {\n      includeInStateLogs: false,\n      persist: true,\n      includeInDebugSnapshot: false,\n      usedInUi: true,\n    },\n    allIgnoredAssets: {\n      includeInStateLogs: false,\n      persist: true,\n      includeInDebugSnapshot: false,\n      usedInUi: true,\n    },\n  };\n\nconst MESSENGER_EXPOSED_METHODS = [\n  'getAssetMetadata',\n  'ignoreAssets',\n  'addAssets',\n] as const;\n\n// TODO: make this controller extends StaticIntervalPollingController and update all assetsMetadata once a day.\n\nexport class MultichainAssetsController extends BaseController<\n  typeof controllerName,\n  MultichainAssetsControllerState,\n  MultichainAssetsControllerMessenger\n> {\n  // Mapping of CAIP-2 Chain ID to Asset Snaps.\n  #snaps: Record<CaipChainId, Snap[]>;\n\n  readonly #controllerOperationMutex = new Mutex();\n\n  constructor({\n    messenger,\n    state = {},\n  }: {\n    messenger: MultichainAssetsControllerMessenger;\n    state?: Partial<MultichainAssetsControllerState>;\n  }) {\n    super({\n      messenger,\n      name: controllerName,\n      metadata: assetsControllerMetadata,\n      state: {\n        ...getDefaultMultichainAssetsControllerState(),\n        ...state,\n      },\n    });\n\n    this.#snaps = {};\n\n    this.messenger.subscribe(\n      'AccountsController:accountAdded',\n      async (account) => await this.#handleOnAccountAddedEvent(account),\n    );\n    this.messenger.subscribe(\n      'AccountsController:accountRemoved',\n      async (account) => await this.#handleOnAccountRemovedEvent(account),\n    );\n    this.messenger.subscribe(\n      'AccountsController:accountAssetListUpdated',\n      async (event) => await this.#handleAccountAssetListUpdatedEvent(event),\n    );\n\n    messenger.registerMethodActionHandlers(this, MESSENGER_EXPOSED_METHODS);\n  }\n\n  async #handleAccountAssetListUpdatedEvent(\n    event: AccountAssetListUpdatedEventPayload,\n  ) {\n    return this.#withControllerLock(async () =>\n      this.#handleAccountAssetListUpdated(event),\n    );\n  }\n\n  async #handleOnAccountAddedEvent(account: InternalAccount) {\n    return this.#withControllerLock(async () =>\n      this.#handleOnAccountAdded(account),\n    );\n  }\n\n  /**\n   * Returns the metadata for the given asset\n   *\n   * @param asset - The asset to get metadata for\n   * @returns The metadata for the asset or undefined if not found.\n   */\n  getAssetMetadata(asset: CaipAssetType): FungibleAssetMetadata | undefined {\n    return this.state.assetsMetadata[asset];\n  }\n\n  /**\n   * Ignores a batch of assets for a specific account.\n   *\n   * @param assetsToIgnore - Array of asset IDs to ignore.\n   * @param accountId - The account ID to ignore assets for.\n   */\n  ignoreAssets(assetsToIgnore: CaipAssetType[], accountId: string): void {\n    this.update((state) => {\n      if (state.accountsAssets[accountId]) {\n        state.accountsAssets[accountId] = state.accountsAssets[\n          accountId\n        ].filter((asset) => !assetsToIgnore.includes(asset));\n      }\n\n      if (!state.allIgnoredAssets[accountId]) {\n        state.allIgnoredAssets[accountId] = [];\n      }\n\n      const newIgnoredAssets = assetsToIgnore.filter(\n        (asset) => !state.allIgnoredAssets[accountId].includes(asset),\n      );\n      state.allIgnoredAssets[accountId].push(...newIgnoredAssets);\n    });\n  }\n\n  /**\n   * Adds multiple assets to the stored asset list for a specific account.\n   * All assets must belong to the same chain.\n   *\n   * @param assetIds - Array of CAIP asset IDs to add (must be from same chain).\n   * @param accountId - The account ID to add the assets to.\n   * @returns The updated asset list for the account.\n   * @throws Error if assets are from different chains.\n   */\n  async addAssets(\n    assetIds: CaipAssetType[],\n    accountId: string,\n  ): Promise<CaipAssetType[]> {\n    if (assetIds.length === 0) {\n      return this.state.accountsAssets[accountId] || [];\n    }\n\n    // Validate that all assets are from the same chain\n    const chainIds = new Set(\n      assetIds.map((assetId) => parseCaipAssetType(assetId).chainId),\n    );\n    if (chainIds.size > 1) {\n      throw new Error(\n        `All assets must belong to the same chain. Found assets from chains: ${Array.from(chainIds).join(', ')}`,\n      );\n    }\n\n    return this.#withControllerLock(async () => {\n      // Refresh metadata for all assets\n      await this.#refreshAssetsMetadata(assetIds);\n\n      const addedAssets: CaipAssetType[] = [];\n\n      this.update((state) => {\n        // Initialize account assets if it doesn't exist\n        if (!state.accountsAssets[accountId]) {\n          state.accountsAssets[accountId] = [];\n        }\n\n        // Add assets if they don't already exist\n        for (const assetId of assetIds) {\n          if (!state.accountsAssets[accountId].includes(assetId)) {\n            state.accountsAssets[accountId].push(assetId);\n            addedAssets.push(assetId);\n          }\n        }\n\n        // Remove from ignored list if they exist there (inline logic like EVM)\n        if (state.allIgnoredAssets[accountId]) {\n          state.allIgnoredAssets[accountId] = state.allIgnoredAssets[\n            accountId\n          ].filter((asset) => !assetIds.includes(asset));\n\n          // Clean up empty arrays\n          if (state.allIgnoredAssets[accountId].length === 0) {\n            delete state.allIgnoredAssets[accountId];\n          }\n        }\n      });\n\n      // Publish event to notify other controllers (balances, rates) about the new assets\n      if (addedAssets.length > 0) {\n        this.messenger.publish(`${controllerName}:accountAssetListUpdated`, {\n          assets: {\n            [accountId]: {\n              added: addedAssets,\n              removed: [],\n            },\n          },\n        });\n      }\n\n      return this.state.accountsAssets[accountId] || [];\n    });\n  }\n\n  /**\n   * Checks if an asset is ignored for a specific account.\n   *\n   * @param asset - The asset ID to check.\n   * @param accountId - The account ID to check for.\n   * @returns True if the asset is ignored, false otherwise.\n   */\n  #isAssetIgnored(asset: CaipAssetType, accountId: string): boolean {\n    return this.state.allIgnoredAssets[accountId]?.includes(asset) ?? false;\n  }\n\n  /**\n   * Function to update the assets list for an account\n   *\n   * @param event - The list of assets to update\n   */\n  async #handleAccountAssetListUpdated(\n    event: AccountAssetListUpdatedEventPayload,\n  ) {\n    this.#assertControllerMutexIsLocked();\n\n    const assetsForMetadataRefresh = new Set<CaipAssetType>([]);\n    const accountsAndAssetsToUpdate: AccountAssetListUpdatedEventPayload['assets'] =\n      {};\n    for (const [accountId, { added, removed }] of Object.entries(\n      event.assets,\n    )) {\n      if (added.length > 0 || removed.length > 0) {\n        const existing = this.state.accountsAssets[accountId] || [];\n\n        // In case accountsAndAssetsToUpdate event is fired with \"added\" assets that already exist, we don't want to add them again\n        // Also filter out ignored assets\n        const preFilteredToBeAddedAssets = added.filter(\n          (asset) =>\n            !existing.includes(asset) &&\n            isCaipAssetType(asset) &&\n            !this.#isAssetIgnored(asset, accountId),\n        );\n\n        // Filter out tokens flagged by Blockaid as non-benign\n        const filteredToBeAddedAssets = await this.#filterBlockaidSpamTokens(\n          preFilteredToBeAddedAssets,\n        );\n\n        // In case accountsAndAssetsToUpdate event is fired with \"removed\" assets that don't exist, we don't want to remove them\n        const filteredToBeRemovedAssets = removed.filter(\n          (asset) => existing.includes(asset) && isCaipAssetType(asset),\n        );\n\n        if (\n          filteredToBeAddedAssets.length > 0 ||\n          filteredToBeRemovedAssets.length > 0\n        ) {\n          accountsAndAssetsToUpdate[accountId] = {\n            added: filteredToBeAddedAssets,\n            removed: filteredToBeRemovedAssets,\n          };\n        }\n\n        for (const asset of existing) {\n          assetsForMetadataRefresh.add(asset);\n        }\n        for (const asset of filteredToBeAddedAssets) {\n          assetsForMetadataRefresh.add(asset);\n        }\n        for (const asset of filteredToBeRemovedAssets) {\n          assetsForMetadataRefresh.delete(asset);\n        }\n      }\n    }\n\n    this.update((state) => {\n      for (const [accountId, { added, removed }] of Object.entries(\n        accountsAndAssetsToUpdate,\n      )) {\n        const assets = new Set([\n          ...(state.accountsAssets[accountId] || []),\n          ...added,\n        ]);\n        for (const asset of removed) {\n          assets.delete(asset);\n        }\n\n        state.accountsAssets[accountId] = Array.from(assets);\n      }\n    });\n\n    // Trigger fetching metadata for new assets\n    await this.#refreshAssetsMetadata(Array.from(assetsForMetadataRefresh));\n\n    this.messenger.publish(`${controllerName}:accountAssetListUpdated`, {\n      assets: accountsAndAssetsToUpdate,\n    });\n  }\n\n  /**\n   * Checks for non-EVM accounts.\n   *\n   * @param account - The new account to be checked.\n   * @returns True if the account is a non-EVM account, false otherwise.\n   */\n  #isNonEvmAccount(account: InternalAccount): boolean {\n    return (\n      !isEvmAccountType(account.type) &&\n      // Non-EVM accounts are backed by a Snap for now\n      account.metadata.snap !== undefined\n    );\n  }\n\n  /**\n   * Handles changes when a new account has been added.\n   *\n   * @param account - The new account being added.\n   */\n  async #handleOnAccountAdded(account: InternalAccount): Promise<void> {\n    if (!this.#isNonEvmAccount(account)) {\n      // Nothing to do here for EVM accounts\n      return;\n    }\n    this.#assertControllerMutexIsLocked();\n\n    // Get assets list\n    if (account.metadata.snap) {\n      const allAssets = await this.#getAssetsList(\n        account.id,\n        account.metadata.snap.id,\n      );\n      const assets = await this.#filterBlockaidSpamTokens(allAssets);\n      await this.#refreshAssetsMetadata(assets);\n      this.update((state) => {\n        state.accountsAssets[account.id] = assets;\n      });\n      this.messenger.publish(`${controllerName}:accountAssetListUpdated`, {\n        assets: {\n          [account.id]: {\n            added: assets,\n            removed: [],\n          },\n        },\n      });\n    }\n  }\n\n  /**\n   * Handles changes when a new account has been removed.\n   *\n   * @param accountId - The new account id being removed.\n   */\n  async #handleOnAccountRemovedEvent(accountId: string): Promise<void> {\n    this.update((state) => {\n      if (state.accountsAssets[accountId]) {\n        delete state.accountsAssets[accountId];\n      }\n      if (state.allIgnoredAssets[accountId]) {\n        delete state.allIgnoredAssets[accountId];\n      }\n      // TODO: We are not deleting the assetsMetadata because we will soon make this controller extends StaticIntervalPollingController\n      // and update all assetsMetadata once a day.\n    });\n  }\n\n  /**\n   * Refreshes the assets snaps and metadata for the given list of assets\n   *\n   * @param assets - The assets to refresh\n   */\n  async #refreshAssetsMetadata(assets: CaipAssetType[]) {\n    this.#assertControllerMutexIsLocked();\n\n    const assetsWithoutMetadata: CaipAssetType[] = assets.filter(\n      (asset) => !this.state.assetsMetadata[asset],\n    );\n\n    // Call the snap to get the metadata\n    if (assetsWithoutMetadata.length > 0) {\n      // Check if for every asset in assetsWithoutMetadata there is a snap in snaps by chainId else call getAssetSnaps\n      if (\n        !assetsWithoutMetadata.every((asset: CaipAssetType) => {\n          const { chainId } = parseCaipAssetType(asset);\n          return Boolean(this.#getAssetSnapFor(chainId));\n        })\n      ) {\n        this.#snaps = this.#getAssetSnaps();\n      }\n      await this.#updateAssetsMetadata(assetsWithoutMetadata);\n    }\n  }\n\n  /**\n   * Updates the assets metadata for the given list of assets\n   *\n   * @param assets - The assets to update\n   */\n  async #updateAssetsMetadata(assets: CaipAssetType[]) {\n    // Creates a mapping of scope to their respective assets list.\n    const assetsByScope: Record<CaipChainId, CaipAssetType[]> = {};\n    for (const asset of assets) {\n      const { chainId } = parseCaipAssetType(asset);\n      if (!assetsByScope[chainId]) {\n        assetsByScope[chainId] = [];\n      }\n      assetsByScope[chainId].push(asset);\n    }\n\n    let newMetadata: Record<CaipAssetType, FungibleAssetMetadata> = {};\n    for (const chainId of Object.keys(assetsByScope) as CaipChainId[]) {\n      const assetsForChain = assetsByScope[chainId];\n      // Now fetch metadata from the associated asset Snaps:\n      const snap = this.#getAssetSnapFor(chainId);\n      if (snap) {\n        const metadata = await this.#getAssetsMetadataFrom(\n          assetsForChain,\n          snap.id,\n        );\n        newMetadata = {\n          ...newMetadata,\n          ...(metadata?.assets ?? {}),\n        };\n      }\n    }\n    this.update((state) => {\n      state.assetsMetadata = {\n        ...this.state.assetsMetadata,\n        ...newMetadata,\n      };\n    });\n  }\n\n  /**\n   * Creates a mapping of CAIP-2 Chain ID to Asset Snaps.\n   *\n   * @returns A mapping of CAIP-2 Chain ID to Asset Snaps.\n   */\n  #getAssetSnaps(): Record<CaipChainId, Snap[]> {\n    const snaps: Record<CaipChainId, Snap[]> = {};\n    const allSnaps = this.#getAllSnaps();\n    const allPermissions = allSnaps.map((snap) =>\n      this.#getSnapsPermissions(snap.id),\n    );\n\n    for (const [index, permission] of allPermissions.entries()) {\n      let scopes;\n      for (const singlePermissionConstraint of Object.values(permission)) {\n        scopes = getChainIdsCaveat(singlePermissionConstraint);\n        if (!scopes) {\n          continue;\n        }\n        for (const scope of scopes as CaipChainId[]) {\n          if (!snaps[scope]) {\n            snaps[scope] = [];\n          }\n          snaps[scope].push(allSnaps[index]);\n        }\n      }\n    }\n    return snaps;\n  }\n\n  /**\n   * Returns the first asset snap for the given scope\n   *\n   * @param scope - The scope to get the asset snap for\n   * @returns The asset snap for the given scope\n   */\n  #getAssetSnapFor(scope: CaipChainId): Snap | undefined {\n    const allSnaps = this.#snaps[scope];\n    // Pick only the first one, we ignore the other Snaps if there are multiple candidates for now.\n    return allSnaps?.[0]; // Will be undefined if there's no Snaps candidate for this scope.\n  }\n\n  /**\n   * Returns all the asset snaps\n   *\n   * @returns All the asset snaps\n   */\n  #getAllSnaps(): Snap[] {\n    return this.messenger.call('SnapController:getRunnableSnaps');\n  }\n\n  /**\n   * Returns the permissions for the given origin\n   *\n   * @param origin - The origin to get the permissions for\n   * @returns The permissions for the given origin\n   */\n  #getSnapsPermissions(\n    origin: string,\n  ): SubjectPermissions<PermissionConstraint> {\n    return this.messenger.call(\n      'PermissionController:getPermissions',\n      origin,\n    ) as SubjectPermissions<PermissionConstraint>;\n  }\n\n  /**\n   * Returns the metadata for the given assets\n   *\n   * @param assets - The assets to get metadata for\n   * @param snapId - The snap ID to get metadata from\n   * @returns The metadata for the assets\n   */\n  async #getAssetsMetadataFrom(\n    assets: CaipAssetType[],\n    snapId: string,\n  ): Promise<AssetMetadataResponse | undefined> {\n    try {\n      return (await this.messenger.call('SnapController:handleRequest', {\n        snapId: snapId as SnapId,\n        origin: 'metamask',\n        handler: HandlerType.OnAssetsLookup,\n        request: {\n          jsonrpc: '2.0',\n          method: 'onAssetLookup',\n          params: {\n            assets,\n          },\n        },\n      })) as Promise<AssetMetadataResponse>;\n    } catch (error) {\n      // Ignore\n      console.error(error);\n      return undefined;\n    }\n  }\n\n  /**\n   * Filters out tokens flagged as malicious by Blockaid via the\n   * `PhishingController:bulkScanTokens` messenger action. Only tokens with\n   * an `assetNamespace` of \"token\" are scanned (native assets like slip44 are\n   * passed through unfiltered). If the scan fails, all tokens are kept\n   * (fail open).\n   *\n   * @param assets - The CAIP asset type list to filter.\n   * @returns The filtered list with malicious tokens removed.\n   */\n  async #filterBlockaidSpamTokens(\n    assets: CaipAssetType[],\n  ): Promise<CaipAssetType[]> {\n    // Group scannable token assets by chain namespace\n    const tokensByChain: Record<\n      string,\n      { asset: CaipAssetType; address: string }[]\n    > = {};\n\n    for (const asset of assets) {\n      const { assetNamespace, assetReference, chain } =\n        parseCaipAssetType(asset);\n\n      // Only scan fungible token assets (e.g. SPL tokens), skip native (slip44)\n      if (assetNamespace === 'token') {\n        const chainName = chain.namespace;\n        if (!tokensByChain[chainName]) {\n          tokensByChain[chainName] = [];\n        }\n        tokensByChain[chainName].push({ asset, address: assetReference });\n      }\n    }\n\n    // If there are no token assets to scan, return as-is\n    if (Object.keys(tokensByChain).length === 0) {\n      return assets;\n    }\n\n    // Build a set of assets to reject (non-benign tokens)\n    const rejectedAssets = new Set<CaipAssetType>();\n\n    // PhishingController:bulkScanTokens rejects requests with more than\n    // 100 tokens (returning {}). Batch addresses into chunks to stay within\n    // the limit.\n    const BATCH_SIZE = 100;\n\n    for (const [chainName, tokenEntries] of Object.entries(tokensByChain)) {\n      const addresses = tokenEntries.map((entry) => entry.address);\n\n      // Create batches of BATCH_SIZE\n      const batches: string[][] = [];\n      for (let i = 0; i < addresses.length; i += BATCH_SIZE) {\n        batches.push(addresses.slice(i, i + BATCH_SIZE));\n      }\n\n      // Scan all batches in parallel. Using Promise.allSettled so that a\n      // single batch failure doesn't discard results from successful batches\n      // (fail open at the batch level, not the chain level).\n      const batchResults = await Promise.allSettled(\n        batches.map((batch) =>\n          this.messenger.call('PhishingController:bulkScanTokens', {\n            chainId: chainName,\n            tokens: batch,\n          }),\n        ),\n      );\n\n      // Merge results from fulfilled batches (rejected batches fail open)\n      const scanResponse: BulkTokenScanResponse = {};\n      for (const result of batchResults) {\n        if (result.status === 'fulfilled') {\n          Object.assign(scanResponse, result.value);\n        }\n      }\n\n      for (const entry of tokenEntries) {\n        const result = scanResponse[entry.address];\n        if (result?.result_type === TokenScanResultType.Malicious) {\n          rejectedAssets.add(entry.asset);\n        }\n      }\n    }\n\n    // Filter while preserving original order\n    return assets.filter((asset) => !rejectedAssets.has(asset));\n  }\n\n  /**\n   * Get assets list for an account\n   *\n   * @param accountId - AccountId to get assets for\n   * @param snapId - Snap ID for the account\n   * @returns list of assets\n   */\n  async #getAssetsList(\n    accountId: string,\n    snapId: string,\n  ): Promise<CaipAssetTypeOrId[]> {\n    return await this.#getClient(snapId).listAccountAssets(accountId);\n  }\n\n  /**\n   * Gets a `KeyringClient` for a Snap.\n   *\n   * @param snapId - ID of the Snap to get the client for.\n   * @returns A `KeyringClient` for the Snap.\n   */\n  #getClient(snapId: string): KeyringClient {\n    return new KeyringClient({\n      send: async (request: JsonRpcRequest) =>\n        (await this.messenger.call('SnapController:handleRequest', {\n          snapId: snapId as SnapId,\n          origin: 'metamask',\n          handler: HandlerType.OnKeyringRequest,\n          request,\n        })) as Promise<Json>,\n    });\n  }\n\n  /**\n   * Assert that the controller mutex is locked.\n   *\n   * @throws If the controller mutex is not locked.\n   */\n  #assertControllerMutexIsLocked() {\n    if (!this.#controllerOperationMutex.isLocked()) {\n      throw new Error(\n        'MultichainAssetsControllerError - Attempt to update state',\n      );\n    }\n  }\n\n  /**\n   * Lock the controller mutex before executing the given function,\n   * and release it after the function is resolved or after an\n   * error is thrown.\n   *\n   * This wrapper ensures that each mutable operation that interacts with the\n   * controller and that changes its state is executed in a mutually exclusive way,\n   * preventing unsafe concurrent access that could lead to unpredictable behavior.\n   *\n   * @param callback - The function to execute while the controller mutex is locked.\n   * @returns The result of the function.\n   */\n  async #withControllerLock<Result>(\n    callback: MutuallyExclusiveCallback<Result>,\n  ): Promise<Result> {\n    return withLock(this.#controllerOperationMutex, callback);\n  }\n}\n\n/**\n * Lock the given mutex before executing the given function,\n * and release it after the function is resolved or after an\n * error is thrown.\n *\n * @param mutex - The mutex to lock.\n * @param callback - The function to execute while the mutex is locked.\n * @returns The result of the function.\n */\nasync function withLock<Result>(\n  mutex: Mutex,\n  callback: MutuallyExclusiveCallback<Result>,\n): Promise<Result> {\n  const releaseLock = await mutex.acquire();\n\n  try {\n    return await callback({ releaseLock });\n  } finally {\n    releaseLock();\n  }\n}\n"]}