{"version":3,"file":"MultichainAssetsRatesController.mjs","sourceRoot":"","sources":["../../src/MultichainAssetsRatesController/MultichainAssetsRatesController.ts"],"names":[],"mappings":"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","sourcesContent":["import type {\n  AccountsControllerListMultichainAccountsAction,\n  AccountsControllerAccountAddedEvent,\n  AccountsControllerGetSelectedMultichainAccountAction,\n} from '@metamask/accounts-controller';\nimport type {\n  ControllerStateChangeEvent,\n  ControllerGetStateAction,\n  StateMetadata,\n} from '@metamask/base-controller';\nimport { isEvmAccountType } from '@metamask/keyring-api';\nimport type { CaipAssetType } from '@metamask/keyring-api';\nimport type {\n  KeyringControllerLockEvent,\n  KeyringControllerUnlockEvent,\n} from '@metamask/keyring-controller';\nimport type { InternalAccount } from '@metamask/keyring-internal-api';\nimport type { Messenger } from '@metamask/messenger';\nimport { StaticIntervalPollingController } from '@metamask/polling-controller';\nimport type { SnapControllerHandleRequestAction } from '@metamask/snaps-controllers';\nimport type {\n  SnapId,\n  AssetConversion,\n  OnAssetsConversionArguments,\n  OnAssetHistoricalPriceArguments,\n  OnAssetHistoricalPriceResponse,\n  HistoricalPriceIntervals,\n  OnAssetsMarketDataArguments,\n  OnAssetsMarketDataResponse,\n  FungibleAssetMarketData,\n  OnAssetsConversionResponse,\n} from '@metamask/snaps-sdk';\nimport { HandlerType } from '@metamask/snaps-utils';\nimport { Mutex } from 'async-mutex';\nimport type { Draft } from 'immer';\n\nimport type {\n  CurrencyRateState,\n  CurrencyRateStateChange,\n  GetCurrencyRateState,\n} from '../CurrencyRateController';\nimport type {\n  MultichainAssetsControllerGetStateAction,\n  MultichainAssetsControllerAccountAssetListUpdatedEvent,\n} from '../MultichainAssetsController';\nimport { MAP_CAIP_CURRENCIES } from './constant';\nimport type { MultichainAssetsRatesControllerMethodActions } from './MultichainAssetsRatesController-method-action-types';\n\n/**\n * The name of the MultichainAssetsRatesController.\n */\nconst controllerName = 'MultichainAssetsRatesController';\n\n// This is temporary until its exported from snap\ntype HistoricalPrice = {\n  intervals: HistoricalPriceIntervals;\n  // The UNIX timestamp of when the historical price was last updated.\n  updateTime: number;\n  // The UNIX timestamp of when the historical price will expire.\n  expirationTime?: number;\n};\n\n/**\n * State used by the MultichainAssetsRatesController to cache token conversion rates.\n */\nexport type MultichainAssetsRatesControllerState = {\n  conversionRates: Record<CaipAssetType, UnifiedAssetConversion>;\n  historicalPrices: Record<CaipAssetType, Record<string, HistoricalPrice>>; // string being the current currency we fetched historical prices for\n};\n\n/**\n * Returns the state of the MultichainAssetsRatesController.\n */\nexport type MultichainAssetsRatesControllerGetStateAction =\n  ControllerGetStateAction<\n    typeof controllerName,\n    MultichainAssetsRatesControllerState\n  >;\n\ntype UnifiedAssetConversion = AssetConversion & {\n  marketData?: FungibleAssetMarketData;\n};\n\n/**\n * Constructs the default {@link MultichainAssetsRatesController} 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 MultichainAssetsRatesController} state.\n */\nexport function getDefaultMultichainAssetsRatesControllerState(): MultichainAssetsRatesControllerState {\n  return { conversionRates: {}, historicalPrices: {} };\n}\n\n/**\n * Event emitted when the state of the MultichainAssetsRatesController changes.\n */\nexport type MultichainAssetsRatesControllerStateChange =\n  ControllerStateChangeEvent<\n    typeof controllerName,\n    MultichainAssetsRatesControllerState\n  >;\n\n/**\n * Actions exposed by the MultichainAssetsRatesController.\n */\nexport type MultichainAssetsRatesControllerActions =\n  | MultichainAssetsRatesControllerGetStateAction\n  | MultichainAssetsRatesControllerMethodActions;\n\n/**\n * Events emitted by MultichainAssetsRatesController.\n */\nexport type MultichainAssetsRatesControllerEvents =\n  MultichainAssetsRatesControllerStateChange;\n\n/**\n * Actions that this controller is allowed to call.\n */\nexport type AllowedActions =\n  | SnapControllerHandleRequestAction\n  | AccountsControllerListMultichainAccountsAction\n  | GetCurrencyRateState\n  | MultichainAssetsControllerGetStateAction\n  | AccountsControllerGetSelectedMultichainAccountAction;\n\n/**\n * Events that this controller is allowed to subscribe to.\n */\nexport type AllowedEvents =\n  | KeyringControllerLockEvent\n  | KeyringControllerUnlockEvent\n  | AccountsControllerAccountAddedEvent\n  | CurrencyRateStateChange\n  | MultichainAssetsControllerAccountAssetListUpdatedEvent;\n/**\n * Messenger type for the MultichainAssetsRatesController.\n */\nexport type MultichainAssetsRatesControllerMessenger = Messenger<\n  typeof controllerName,\n  MultichainAssetsRatesControllerActions | AllowedActions,\n  MultichainAssetsRatesControllerEvents | AllowedEvents\n>;\n\n/**\n * The input for starting polling in MultichainAssetsRatesController.\n */\nexport type MultichainAssetsRatesPollingInput = {\n  accountId: string;\n};\n\nconst metadata: StateMetadata<MultichainAssetsRatesControllerState> = {\n  conversionRates: {\n    includeInStateLogs: false,\n    persist: true,\n    includeInDebugSnapshot: true,\n    usedInUi: true,\n  },\n  historicalPrices: {\n    includeInStateLogs: false,\n    persist: false,\n    includeInDebugSnapshot: true,\n    usedInUi: true,\n  },\n};\n\nexport type ConversionRatesWithMarketData = {\n  conversionRates: Record<\n    CaipAssetType,\n    Record<CaipAssetType, UnifiedAssetConversion | null>\n  >;\n};\n\n/**\n * Arguments for a Snap request.\n */\ntype SnapRequestArgs<T> = {\n  snapId: SnapId;\n  handler: HandlerType;\n  params: T;\n};\n\nconst MESSENGER_EXPOSED_METHODS = [\n  'updateAssetsRates',\n  'fetchHistoricalPricesForAsset',\n] as const;\n\n/**\n * Controller that manages multichain token conversion rates.\n *\n * This controller polls for token conversion rates and updates its state.\n */\nexport class MultichainAssetsRatesController extends StaticIntervalPollingController<MultichainAssetsRatesPollingInput>()<\n  typeof controllerName,\n  MultichainAssetsRatesControllerState,\n  MultichainAssetsRatesControllerMessenger\n> {\n  readonly #mutex = new Mutex();\n\n  #currentCurrency: CurrencyRateState['currentCurrency'];\n\n  #isUnlocked = true;\n\n  /**\n   * Creates an instance of MultichainAssetsRatesController.\n   *\n   * @param options - Constructor options.\n   * @param options.interval - The polling interval in milliseconds.\n   * @param options.state - The initial state.\n   * @param options.messenger - A reference to the messenger.\n   */\n  constructor({\n    interval = 18000,\n    state = {},\n    messenger,\n  }: {\n    interval?: number;\n    state?: Partial<MultichainAssetsRatesControllerState>;\n    messenger: MultichainAssetsRatesControllerMessenger;\n  }) {\n    super({\n      name: controllerName,\n      messenger,\n      state: {\n        ...getDefaultMultichainAssetsRatesControllerState(),\n        ...state,\n      },\n      metadata,\n    });\n\n    this.setIntervalLength(interval);\n\n    messenger.registerMethodActionHandlers(this, MESSENGER_EXPOSED_METHODS);\n\n    // Subscribe to keyring lock/unlock events.\n    this.messenger.subscribe('KeyringController:lock', () => {\n      this.#isUnlocked = false;\n    });\n    this.messenger.subscribe('KeyringController:unlock', () => {\n      this.#isUnlocked = true;\n    });\n\n    ({ currentCurrency: this.#currentCurrency } = this.messenger.call(\n      'CurrencyRateController:getState',\n    ));\n\n    this.messenger.subscribe(\n      'CurrencyRateController:stateChange',\n      // eslint-disable-next-line @typescript-eslint/no-misused-promises\n      async (currentCurrency: string) => {\n        this.#currentCurrency = currentCurrency;\n        await this.updateAssetsRates();\n      },\n      (currencyRateControllerState) =>\n        currencyRateControllerState.currentCurrency,\n    );\n\n    this.messenger.subscribe(\n      'MultichainAssetsController:accountAssetListUpdated',\n      // eslint-disable-next-line @typescript-eslint/no-misused-promises\n      async ({ assets }) => {\n        const newAccountAssets = Object.entries(assets).map(\n          ([accountId, { added }]) => ({\n            accountId,\n            assets: [...added],\n          }),\n        );\n        // TODO; removed can be used in future for further cleanup\n        await this.#updateAssetsRatesForNewAssets(newAccountAssets);\n      },\n    );\n  }\n\n  /**\n   * Executes a poll by updating token conversion rates for the current account.\n   *\n   * @returns A promise that resolves when the polling completes.\n   */\n  async _executePoll(): Promise<void> {\n    await this.updateAssetsRates();\n  }\n\n  /**\n   * Determines whether the controller is active.\n   *\n   * @returns True if the keyring is unlocked; otherwise, false.\n   */\n  get isActive(): boolean {\n    return this.#isUnlocked;\n  }\n\n  /**\n   * Checks if an account is a non-EVM account with a Snap.\n   *\n   * @param account - The account to check.\n   * @returns True if the account is non-EVM and has Snap metadata; otherwise, false.\n   */\n  #isNonEvmAccount(account: InternalAccount): boolean {\n    return (\n      !isEvmAccountType(account.type) && account.metadata.snap !== undefined\n    );\n  }\n\n  /**\n   * Retrieves all multichain accounts from the AccountsController.\n   *\n   * @returns An array of internal accounts.\n   */\n  #listMultichainAccounts(): InternalAccount[] {\n    return this.messenger.call('AccountsController:listMultichainAccounts');\n  }\n\n  /**\n   * Filters and returns non-EVM accounts that should have balances.\n   *\n   * @returns An array of non-EVM internal accounts.\n   */\n  #listAccounts(): InternalAccount[] {\n    const accounts = this.#listMultichainAccounts();\n    return accounts.filter((account) => this.#isNonEvmAccount(account));\n  }\n\n  /**\n   * Adds the assets to a map of Snap ID to assets.\n   *\n   * @param snapIdToAssets - The map of Snap ID to assets.\n   * @param account - The account to add the assets for.\n   * @param assets - The assets to add.\n   */\n  #addAssetsToSnapIdMap(\n    snapIdToAssets: Map<SnapId, Set<CaipAssetType>>,\n    account: InternalAccount,\n    assets: CaipAssetType[],\n  ): void {\n    // Prevent creating a new set if there are no assets to add.\n    if (assets.length === 0) {\n      return;\n    }\n\n    // FIXME: Instead of using the Snap ID from the account, we should\n    // select the Snap based on the supported scopes defined in the Snaps'\n    // manifest.\n    const snapId = account.metadata.snap?.id as SnapId | undefined;\n    if (!snapId) {\n      return;\n    }\n\n    let snapAssets = snapIdToAssets.get(snapId);\n    if (!snapAssets) {\n      snapAssets = new Set();\n      snapIdToAssets.set(snapId, snapAssets);\n    }\n\n    for (const asset of assets) {\n      snapAssets.add(asset);\n    }\n  }\n\n  /**\n   * Updates token conversion rates for each non-EVM account.\n   *\n   * @returns A promise that resolves when the rates are updated.\n   */\n  async updateAssetsRates(): Promise<void> {\n    const releaseLock = await this.#mutex.acquire();\n\n    return (async (): Promise<void> => {\n      if (!this.isActive) {\n        return;\n      }\n\n      // Compute the set of unique assets from all accounts. It's important to\n      // deduplicate assets here to avoid duplicate requests to the Snap.\n      const accounts = this.#listAccounts();\n      const snapIdToAssets = new Map<SnapId, Set<CaipAssetType>>();\n      for (const account of accounts) {\n        this.#addAssetsToSnapIdMap(\n          snapIdToAssets,\n          account,\n          this.#getAssetsForAccount(account.id),\n        );\n      }\n\n      this.#applyUpdatedRates(await this.#getUpdatedRatesFor(snapIdToAssets));\n    })().finally(() => {\n      releaseLock();\n    });\n  }\n\n  /**\n   * Returns the CAIP-19 asset type for the current selected currency. Defaults\n   * to USD if the current selected currency is not supported.\n   *\n   * @returns The CAIP-19 asset type for the current selected currency.\n   */\n  #getCaipCurrentCurrency(): CaipAssetType {\n    return (\n      MAP_CAIP_CURRENCIES[this.#currentCurrency] ?? MAP_CAIP_CURRENCIES.usd\n    );\n  }\n\n  /**\n   * Fetches the conversion rates for the given assets from the given Snap.\n   *\n   * @param snapId - The ID of the Snap.\n   * @param assets - The assets to fetch the conversion rates for.\n   * @param currency - The currency to fetch the conversion rates for.\n   * @returns A record of CAIP-19 asset types to conversion rates.\n   */\n  async #getConversionRates(\n    snapId: SnapId,\n    assets: Set<CaipAssetType>,\n    currency: CaipAssetType,\n  ): Promise<Record<CaipAssetType, AssetConversion | undefined>> {\n    // Prevent making a Snap call if there are no assets to fetch.\n    if (assets.size === 0) {\n      return {};\n    }\n\n    const response = await this.#handleSnapRequest({\n      snapId,\n      handler: HandlerType.OnAssetsConversion,\n      params: {\n        conversions: Array.from(assets).map((asset) => ({\n          from: asset,\n          to: currency,\n        })),\n      },\n    });\n\n    if (!response) {\n      return {};\n    }\n\n    const assetToConversionRate: Record<\n      CaipAssetType,\n      AssetConversion | undefined\n    > = {};\n\n    for (const asset of assets) {\n      assetToConversionRate[asset] =\n        response.conversionRates?.[asset]?.[currency] ?? undefined;\n    }\n\n    return assetToConversionRate;\n  }\n\n  /**\n   * Fetches the market data for the given assets from the given Snap.\n   *\n   * @param snapId - The ID of the Snap.\n   * @param assets - The assets to fetch the market data for.\n   * @param currency - The currency to fetch the market data for.\n   * @returns A record of CAIP-19 asset types to market data.\n   */\n  async #getMarketData(\n    snapId: SnapId,\n    assets: Set<CaipAssetType>,\n    currency: CaipAssetType,\n  ): Promise<Record<CaipAssetType, FungibleAssetMarketData | undefined>> {\n    // Prevent making a Snap call if there are no assets to fetch.\n    if (assets.size === 0) {\n      return {};\n    }\n\n    const response = await this.#handleSnapRequest({\n      snapId,\n      handler: HandlerType.OnAssetsMarketData,\n      params: {\n        assets: Array.from(assets).map((asset) => ({\n          asset,\n          unit: currency,\n        })),\n      },\n    });\n\n    if (!response) {\n      return {};\n    }\n\n    const assetToMarketData: Record<\n      CaipAssetType,\n      FungibleAssetMarketData | undefined\n    > = {};\n\n    for (const asset of assets) {\n      const assetMarketData = response.marketData?.[asset]?.[currency];\n\n      // We do not consider NFTs here, so `fungible` must be `true`.\n      if (assetMarketData?.fungible) {\n        assetToMarketData[asset] = assetMarketData;\n      } else {\n        assetToMarketData[asset] = undefined;\n      }\n    }\n\n    return assetToMarketData;\n  }\n\n  /**\n   * Fetches the updated rates for the given assets from the given Snaps.\n   *\n   * @param snapIdToAssets - A map of Snap ID to CAIP-19 asset types.\n   * @returns A record of CAIP-19 asset types to unified asset conversions.\n   */\n  async #getUpdatedRatesFor(\n    snapIdToAssets: Map<SnapId, Set<CaipAssetType>>,\n  ): Promise<\n    Record<CaipAssetType, UnifiedAssetConversion & { currency: CaipAssetType }>\n  > {\n    const updatedRates: Record<\n      CaipAssetType,\n      UnifiedAssetConversion & { currency: CaipAssetType }\n    > = {};\n\n    // Keep a local copy to ensure that the currency is always the same for the\n    // entire loop.\n    const currency = this.#getCaipCurrentCurrency();\n\n    // Note: Since the assets come from a 1-to-1 mapping with Snap IDs, we know\n    // that a given asset will not appear under multiple Snap IDs.\n    for (const [snapId, assets] of snapIdToAssets.entries()) {\n      const [rates, marketData] = await Promise.all([\n        this.#getConversionRates(snapId, assets, currency),\n        this.#getMarketData(snapId, assets, currency),\n      ]);\n\n      for (const asset of assets) {\n        const assetRate = rates[asset];\n        const assetMarketData = marketData[asset];\n\n        // Rates are mandatory, so skip the asset if not available.\n        if (!assetRate) {\n          continue;\n        }\n\n        updatedRates[asset] = {\n          currency,\n          ...assetRate,\n          ...(assetMarketData && { marketData: assetMarketData }),\n        };\n      }\n    }\n\n    return updatedRates;\n  }\n\n  /**\n   * Fetches historical prices for the current account\n   *\n   * @param asset - The asset to fetch historical prices for.\n   * @param account - optional account to fetch historical prices for\n   * @returns The historical prices.\n   */\n  async fetchHistoricalPricesForAsset(\n    asset: CaipAssetType,\n    account?: InternalAccount,\n  ): Promise<void> {\n    const releaseLock = await this.#mutex.acquire();\n    return (async () => {\n      const currentCaipCurrency =\n        MAP_CAIP_CURRENCIES[this.#currentCurrency] ?? MAP_CAIP_CURRENCIES.usd;\n      // Check if we already have historical prices for this asset and currency\n      const historicalPriceExpirationTime =\n        this.state.historicalPrices[asset]?.[this.#currentCurrency]\n          ?.expirationTime;\n\n      const historicalPriceHasExpired =\n        historicalPriceExpirationTime &&\n        historicalPriceExpirationTime < Date.now();\n\n      if (historicalPriceHasExpired === false) {\n        return;\n      }\n\n      const selectedAccount =\n        account ??\n        this.messenger.call('AccountsController:getSelectedMultichainAccount');\n      try {\n        const historicalPricesResponse = await this.messenger.call(\n          'SnapController:handleRequest',\n          {\n            snapId: selectedAccount?.metadata.snap?.id as SnapId,\n            origin: 'metamask',\n            handler: HandlerType.OnAssetHistoricalPrice,\n            request: {\n              jsonrpc: '2.0',\n              method: HandlerType.OnAssetHistoricalPrice,\n              params: {\n                from: asset,\n                to: currentCaipCurrency,\n              },\n            },\n          },\n        );\n\n        // skip state update if no historical prices are returned\n        if (!historicalPricesResponse) {\n          return;\n        }\n\n        this.update((state) => {\n          state.historicalPrices = {\n            ...state.historicalPrices,\n            [asset]: {\n              ...state.historicalPrices[asset],\n              [this.#currentCurrency]: (\n                historicalPricesResponse as OnAssetHistoricalPriceResponse\n              )?.historicalPrice,\n            },\n          };\n        });\n      } catch {\n        throw new Error(\n          `Failed to fetch historical prices for asset: ${asset}`,\n        );\n      }\n    })().finally(() => {\n      releaseLock();\n    });\n  }\n\n  /**\n   * Updates the conversion rates for new assets.\n   *\n   * @param accounts - The accounts to update the conversion rates for.\n   * @returns A promise that resolves when the rates are updated.\n   */\n  async #updateAssetsRatesForNewAssets(\n    accounts: {\n      accountId: string;\n      assets: CaipAssetType[];\n    }[],\n  ): Promise<void> {\n    const releaseLock = await this.#mutex.acquire();\n\n    return (async () => {\n      if (!this.isActive) {\n        return;\n      }\n\n      // First build a map containing all assets that need to be updated per\n      // Snap ID, this will be used to batch the requests.\n      const snapIdToAssets = new Map<SnapId, Set<CaipAssetType>>();\n\n      for (const { accountId, assets } of accounts) {\n        this.#addAssetsToSnapIdMap(\n          snapIdToAssets,\n          this.#getAccount(accountId),\n          assets,\n        );\n      }\n\n      this.#applyUpdatedRates(await this.#getUpdatedRatesFor(snapIdToAssets));\n    })().finally(() => {\n      releaseLock();\n    });\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   * Returns the array of CAIP-19 assets for the given account ID.\n   * If none are found, returns an empty array.\n   *\n   * @param accountId - The account ID to get the assets for.\n   * @returns An array of CAIP-19 assets.\n   */\n  #getAssetsForAccount(accountId: string): CaipAssetType[] {\n    // Always fetch fresh state - MultichainAssetsController uses Immer which creates\n    // new object references on every update, so caching would become stale.\n    const { accountsAssets } = this.messenger.call(\n      'MultichainAssetsController:getState',\n    );\n    return accountsAssets?.[accountId] ?? [];\n  }\n\n  /**\n   * Merges the new rates into the controller's state.\n   *\n   * @param updatedRates - The new rates to merge.\n   */\n  #applyUpdatedRates(\n    updatedRates: Record<\n      CaipAssetType,\n      UnifiedAssetConversion & { currency: CaipAssetType }\n    >,\n  ): void {\n    if (Object.keys(updatedRates).length === 0) {\n      return;\n    }\n    this.update((state: Draft<MultichainAssetsRatesControllerState>) => {\n      state.conversionRates = {\n        ...state.conversionRates,\n        ...updatedRates,\n      };\n    });\n  }\n\n  /**\n   * Forwards a Snap request to the SnapController.\n   *\n   * @param args - The request parameters.\n   * @param args.snapId - The ID of the Snap.\n   * @param args.handler - The handler type.\n   * @param args.params - The asset conversions.\n   * @returns A promise that resolves with the account rates.\n   */\n  async #handleSnapRequest(\n    args: SnapRequestArgs<OnAssetsConversionArguments>,\n  ): Promise<OnAssetsConversionResponse | undefined>;\n\n  async #handleSnapRequest(\n    args: SnapRequestArgs<OnAssetHistoricalPriceArguments>,\n  ): Promise<OnAssetHistoricalPriceResponse | undefined>;\n\n  async #handleSnapRequest(\n    args: SnapRequestArgs<OnAssetsMarketDataArguments>,\n  ): Promise<OnAssetsMarketDataResponse | undefined>;\n\n  async #handleSnapRequest(args: SnapRequestArgs<unknown>): Promise<unknown> {\n    const { snapId, handler, params } = args;\n    try {\n      return await this.messenger.call('SnapController:handleRequest', {\n        snapId,\n        origin: 'metamask',\n        handler,\n        request: {\n          jsonrpc: '2.0',\n          method: handler,\n          params,\n        },\n      });\n    } catch (error) {\n      console.error(`Snap request failed for ${handler}:`, {\n        snapId,\n        handler,\n        message: (error as Error).message,\n        params,\n      });\n      return undefined;\n    }\n  }\n}\n"]}