{"version":3,"file":"MultichainBalancesController.cjs","sourceRoot":"","sources":["../../src/MultichainBalancesController/MultichainBalancesController.ts"],"names":[],"mappings":"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","sourcesContent":["import type {\n  AccountsControllerAccountAddedEvent,\n  AccountsControllerAccountRemovedEvent,\n  AccountsControllerListMultichainAccountsAction,\n  AccountsControllerAccountBalancesUpdatesEvent,\n} from '@metamask/accounts-controller';\nimport { BaseController } from '@metamask/base-controller';\nimport type {\n  StateMetadata,\n  ControllerGetStateAction,\n  ControllerStateChangeEvent,\n} from '@metamask/base-controller';\nimport { isEvmAccountType } from '@metamask/keyring-api';\nimport type {\n  Balance,\n  CaipAssetType,\n  AccountBalancesUpdatedEventPayload,\n} from '@metamask/keyring-api';\nimport type { KeyringControllerGetStateAction } from '@metamask/keyring-controller';\nimport type { InternalAccount } from '@metamask/keyring-internal-api';\nimport { KeyringClient } from '@metamask/keyring-snap-client';\nimport type { Messenger } from '@metamask/messenger';\nimport type { SnapControllerHandleRequestAction } from '@metamask/snaps-controllers';\nimport type { SnapId } from '@metamask/snaps-sdk';\nimport { HandlerType } from '@metamask/snaps-utils';\nimport type { Json, JsonRpcRequest } from '@metamask/utils';\nimport type { Draft } from 'immer';\n\nimport type {\n  MultichainAssetsControllerGetStateAction,\n  MultichainAssetsControllerAccountAssetListUpdatedEvent,\n} from '../MultichainAssetsController';\n\nconst controllerName = 'MultichainBalancesController';\n\n/**\n * State used by the {@link MultichainBalancesController} to cache account balances.\n */\nexport type MultichainBalancesControllerState = {\n  balances: {\n    [account: string]: {\n      [asset: string]: {\n        amount: string;\n        unit: string;\n      };\n    };\n  };\n};\n\n/**\n * Constructs the default {@link MultichainBalancesController} 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 MultichainBalancesController} state.\n */\nexport function getDefaultMultichainBalancesControllerState(): MultichainBalancesControllerState {\n  return { balances: {} };\n}\n\n/**\n * Returns the state of the {@link MultichainBalancesController}.\n */\nexport type MultichainBalancesControllerGetStateAction =\n  ControllerGetStateAction<\n    typeof controllerName,\n    MultichainBalancesControllerState\n  >;\n\n/**\n * Event emitted when the state of the {@link MultichainBalancesController} changes.\n */\nexport type MultichainBalancesControllerStateChange =\n  ControllerStateChangeEvent<\n    typeof controllerName,\n    MultichainBalancesControllerState\n  >;\n\n/**\n * Actions exposed by the {@link MultichainBalancesController}.\n */\nexport type MultichainBalancesControllerActions =\n  MultichainBalancesControllerGetStateAction;\n\n/**\n * Events emitted by {@link MultichainBalancesController}.\n */\nexport type MultichainBalancesControllerEvents =\n  MultichainBalancesControllerStateChange;\n\n/**\n * Actions that this controller is allowed to call.\n */\ntype AllowedActions =\n  | SnapControllerHandleRequestAction\n  | AccountsControllerListMultichainAccountsAction\n  | MultichainAssetsControllerGetStateAction\n  | KeyringControllerGetStateAction;\n\n/**\n * Events that this controller is allowed to subscribe.\n */\ntype AllowedEvents =\n  | AccountsControllerAccountAddedEvent\n  | AccountsControllerAccountRemovedEvent\n  | AccountsControllerAccountBalancesUpdatesEvent\n  | MultichainAssetsControllerAccountAssetListUpdatedEvent;\n/**\n * Messenger type for the MultichainBalancesController.\n */\nexport type MultichainBalancesControllerMessenger = Messenger<\n  typeof controllerName,\n  MultichainBalancesControllerActions | AllowedActions,\n  MultichainBalancesControllerEvents | AllowedEvents\n>;\n\n/**\n * {@link MultichainBalancesController}'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 balancesControllerMetadata: StateMetadata<MultichainBalancesControllerState> =\n  {\n    balances: {\n      includeInStateLogs: false,\n      persist: true,\n      includeInDebugSnapshot: false,\n      usedInUi: true,\n    },\n  };\n\n/**\n * The MultichainBalancesController is responsible for fetching and caching account\n * balances.\n */\nexport class MultichainBalancesController extends BaseController<\n  typeof controllerName,\n  MultichainBalancesControllerState,\n  MultichainBalancesControllerMessenger\n> {\n  constructor({\n    messenger,\n    state = {},\n  }: {\n    messenger: MultichainBalancesControllerMessenger;\n    state?: Partial<MultichainBalancesControllerState>;\n  }) {\n    super({\n      messenger,\n      name: controllerName,\n      metadata: balancesControllerMetadata,\n      state: {\n        ...getDefaultMultichainBalancesControllerState(),\n        ...state,\n      },\n    });\n\n    // Fetch initial balances for all non-EVM accounts\n    for (const account of this.#listAccounts()) {\n      // Fetching the balance is asynchronous and we cannot use `await` here.\n      // eslint-disable-next-line no-void\n      void this.updateBalance(account.id);\n    }\n\n    this.messenger.subscribe(\n      'AccountsController:accountRemoved',\n      (account: string) => this.#handleOnAccountRemoved(account),\n    );\n    this.messenger.subscribe(\n      'AccountsController:accountBalancesUpdated',\n      (balanceUpdate: AccountBalancesUpdatedEventPayload) =>\n        this.#handleOnAccountBalancesUpdated(balanceUpdate),\n    );\n\n    this.messenger.subscribe(\n      'MultichainAssetsController:accountAssetListUpdated',\n      async ({ assets }) => {\n        const newAccountAssets = Object.entries(assets).map(\n          ([accountId, { added }]) => ({\n            accountId,\n            assets: [...added],\n          }),\n        );\n        await this.#handleOnAccountAssetListUpdated(newAccountAssets);\n      },\n    );\n  }\n\n  /**\n   * Updates the balances for the given accounts.\n   *\n   * @param accounts - The accounts to update the balances for.\n   */\n  async #handleOnAccountAssetListUpdated(\n    accounts: {\n      accountId: string;\n      assets: CaipAssetType[];\n    }[],\n  ): Promise<void> {\n    const { isUnlocked } = this.messenger.call('KeyringController:getState');\n\n    if (!isUnlocked) {\n      return;\n    }\n    const balancesToUpdate: MultichainBalancesControllerState['balances'] = {};\n\n    for (const { accountId, assets } of accounts) {\n      const account = this.#getAccount(accountId);\n      if (account.metadata.snap) {\n        const accountBalance = await this.#getBalances(\n          account.id,\n          account.metadata.snap.id,\n          assets,\n        );\n        balancesToUpdate[accountId] = accountBalance;\n      }\n    }\n\n    if (Object.keys(balancesToUpdate).length === 0) {\n      return;\n    }\n\n    const accountsMap = new Map(accounts.map((acc) => [acc.accountId, acc]));\n\n    this.update((state: Draft<MultichainBalancesControllerState>) => {\n      for (const [accountId, accountBalances] of Object.entries(\n        balancesToUpdate,\n      )) {\n        if (\n          !state.balances[accountId] ||\n          Object.keys(state.balances[accountId]).length === 0\n        ) {\n          state.balances[accountId] = accountBalances;\n        } else {\n          const acc = accountsMap.get(accountId);\n\n          const assetsWithoutBalance = new Set(acc?.assets || []);\n\n          for (const assetId of Object.keys(accountBalances)) {\n            if (!state.balances[accountId][assetId]) {\n              state.balances[accountId][assetId] = accountBalances[assetId];\n            }\n            assetsWithoutBalance.delete(assetId as CaipAssetType);\n          }\n\n          // Triggered when an asset is added to the accountAssets list manually\n          for (const assetId of assetsWithoutBalance) {\n            state.balances[accountId][assetId] = { amount: '0', unit: '' };\n          }\n        }\n      }\n    });\n  }\n\n  /**\n   * Updates the balances of one account. This method doesn't return\n   * anything, but it updates the state of the controller.\n   *\n   * @param accountId - The account ID.\n   * @param assets - The list of asset types for this account to upadte.\n   */\n  async #updateBalance(\n    accountId: string,\n    assets: CaipAssetType[],\n  ): Promise<void> {\n    const { isUnlocked } = this.messenger.call('KeyringController:getState');\n\n    if (!isUnlocked) {\n      return;\n    }\n\n    try {\n      const account = this.#getAccount(accountId);\n\n      if (account.metadata.snap) {\n        const accountBalance = await this.#getBalances(\n          account.id,\n          account.metadata.snap.id,\n          assets,\n        );\n\n        this.update((state: Draft<MultichainBalancesControllerState>) => {\n          state.balances[accountId] = accountBalance;\n        });\n      }\n    } catch (error) {\n      // FIXME: Maybe we shouldn't catch all errors here since this method is also being\n      // used in the public methods. This means if something else uses `updateBalance` it\n      // won't be able to catch and gets the error itself...\n      console.error(\n        `Failed to fetch balances for account ${accountId}:`,\n        error,\n      );\n    }\n  }\n\n  /**\n   * Updates the balances of one account. This method doesn't return\n   * anything, but it updates the state of the controller.\n   *\n   * @param accountId - The account ID.\n   */\n  async updateBalance(accountId: string): Promise<void> {\n    await this.#updateBalance(accountId, this.#listAccountAssets(accountId));\n  }\n\n  /**\n   * Lists the multichain accounts coming from the `AccountsController`.\n   *\n   * @returns A list of multichain accounts.\n   */\n  #listMultichainAccounts(): InternalAccount[] {\n    return this.messenger.call('AccountsController:listMultichainAccounts');\n  }\n\n  /**\n   * Lists the accounts that we should get balances for.\n   *\n   * @returns A list of accounts that we should get balances for.\n   */\n  #listAccounts(): InternalAccount[] {\n    const accounts = this.#listMultichainAccounts();\n    return accounts.filter((account) => this.#isNonEvmAccount(account));\n  }\n\n  /**\n   * Lists the accounts assets.\n   *\n   * @param accountId - The account ID.\n   * @returns The list of assets for this account, returns an empty list if none.\n   */\n  #listAccountAssets(accountId: string): CaipAssetType[] {\n    // TODO: Add an action `MultichainAssetsController:getAccountAssets` maybe?\n    const assetsState = this.messenger.call(\n      'MultichainAssetsController:getState',\n    );\n\n    return assetsState.accountsAssets[accountId] ?? [];\n  }\n\n  /**\n   * Get a non-EVM account from its ID.\n   *\n   * @param accountId - The account ID.\n   * @returns The non-EVM account.\n   */\n  #getAccount(accountId: string): InternalAccount {\n    const account: InternalAccount | undefined = this.#listAccounts().find(\n      (multichainAccount) => multichainAccount.id === accountId,\n    );\n\n    if (!account) {\n      throw new Error(`Unknown account: ${accountId}`);\n    }\n\n    return account;\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 balance updates received from the AccountsController.\n   *\n   * @param balanceUpdate - The balance update event containing new balances.\n   */\n  #handleOnAccountBalancesUpdated(\n    balanceUpdate: AccountBalancesUpdatedEventPayload,\n  ): void {\n    this.update((state: Draft<MultichainBalancesControllerState>) => {\n      Object.entries(balanceUpdate.balances).forEach(\n        ([accountId, assetBalances]) => {\n          if (accountId in state.balances) {\n            Object.assign(state.balances[accountId], assetBalances);\n          }\n        },\n      );\n    });\n  }\n\n  /**\n   * Handles changes when a new account has been removed.\n   *\n   * @param accountId - The account ID being removed.\n   */\n  async #handleOnAccountRemoved(accountId: string): Promise<void> {\n    if (accountId in this.state.balances) {\n      this.update((state: Draft<MultichainBalancesControllerState>) => {\n        delete state.balances[accountId];\n      });\n    }\n  }\n\n  /**\n   * Get the balances for an account.\n   *\n   * @param accountId - ID of the account to get balances for.\n   * @param snapId - ID of the Snap which manages the account.\n   * @param assetTypes - Array of asset types to get balances for.\n   * @returns A map of asset types to balances.\n   */\n  async #getBalances(\n    accountId: string,\n    snapId: string,\n    assetTypes: CaipAssetType[],\n  ): Promise<Record<CaipAssetType, Balance>> {\n    return await this.#getClient(snapId).getAccountBalances(\n      accountId,\n      assetTypes,\n    );\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"]}