{"version":3,"file":"TokenRatesController.mjs","sourceRoot":"","sources":["../src/TokenRatesController.ts"],"names":[],"mappings":"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","sourcesContent":["import type {\n  ControllerGetStateAction,\n  ControllerStateChangeEvent,\n  StateMetadata,\n} from '@metamask/base-controller';\nimport { toChecksumHexAddress } from '@metamask/controller-utils';\nimport type { Messenger } from '@metamask/messenger';\nimport type {\n  NetworkControllerGetStateAction,\n  NetworkControllerStateChangeEvent,\n} from '@metamask/network-controller';\nimport type { NetworkEnablementControllerGetStateAction } from '@metamask/network-enablement-controller';\nimport { StaticIntervalPollingController } from '@metamask/polling-controller';\nimport type { Hex } from '@metamask/utils';\nimport { isEqual } from 'lodash';\n\nimport { reduceInBatchesSerially, TOKEN_PRICES_BATCH_SIZE } from './assetsUtil';\nimport type { AbstractTokenPricesService } from './token-prices-service/abstract-token-prices-service';\nimport { getNativeTokenAddress } from './token-prices-service/codefi-v2';\nimport { TokenRwaData } from './token-service';\nimport type {\n  TokensControllerGetStateAction,\n  TokensControllerStateChangeEvent,\n  TokensControllerState,\n} from './TokensController';\n\n/**\n * @type Token\n *\n * Token representation\n *\n * @property address - Hex address of the token contract\n * @property decimals - Number of decimals the token uses\n * @property symbol - Symbol of the token\n * @property aggregators - An array containing the token's aggregators\n * @property image - Image of the token, url or bit32 image\n * @property hasBalanceError - 'true' if there is an error while updating the token balance\n * @property isERC721 - 'true' if the token is a ERC721 token\n * @property name - Name of the token\n */\nexport type Token = {\n  address: string;\n  decimals: number;\n  symbol: string;\n  aggregators?: string[];\n  image?: string;\n  hasBalanceError?: boolean;\n  isERC721?: boolean;\n  name?: string;\n  rwaData?: TokenRwaData;\n};\n\nconst DEFAULT_INTERVAL = 180000;\n\nexport type ContractExchangeRates = {\n  [address: string]: number | undefined;\n};\n\nexport type MarketDataDetails = {\n  tokenAddress: `0x${string}`;\n  currency: string;\n  allTimeHigh: number;\n  allTimeLow: number;\n  circulatingSupply: number;\n  dilutedMarketCap: number;\n  high1d: number;\n  low1d: number;\n  marketCap: number;\n  marketCapPercentChange1d: number;\n  price: number;\n  priceChange1d: number;\n  pricePercentChange1d: number;\n  pricePercentChange1h: number;\n  pricePercentChange1y: number;\n  pricePercentChange7d: number;\n  pricePercentChange14d: number;\n  pricePercentChange30d: number;\n  pricePercentChange200d: number;\n  totalVolume: number;\n};\n\n/**\n * Represents a mapping of token contract addresses to their market data.\n */\nexport type ContractMarketData = Record<Hex, MarketDataDetails>;\n\ntype ChainIdAndNativeCurrency = {\n  chainId: Hex;\n  nativeCurrency: string;\n};\n\n/**\n * The external actions available to the {@link TokenRatesController}.\n */\nexport type AllowedActions =\n  | TokensControllerGetStateAction\n  | NetworkControllerGetStateAction\n  | NetworkEnablementControllerGetStateAction;\n\n/**\n * The external events available to the {@link TokenRatesController}.\n */\nexport type AllowedEvents =\n  | TokensControllerStateChangeEvent\n  | NetworkControllerStateChangeEvent;\n\n/**\n * The name of the {@link TokenRatesController}.\n */\nexport const controllerName = 'TokenRatesController';\n\n/**\n * @type TokenRatesState\n *\n * Token rates controller state\n *\n * @property marketData - Market data for tokens, keyed by chain ID and then token contract address.\n */\nexport type TokenRatesControllerState = {\n  marketData: Record<Hex, Record<Hex, MarketDataDetails>>;\n};\n\n/**\n * The action that can be performed to get the state of the {@link TokenRatesController}.\n */\nexport type TokenRatesControllerGetStateAction = ControllerGetStateAction<\n  typeof controllerName,\n  TokenRatesControllerState\n>;\n\n/**\n * The actions that can be performed using the {@link TokenRatesController}.\n */\nexport type TokenRatesControllerActions = TokenRatesControllerGetStateAction;\n\n/**\n * The event that {@link TokenRatesController} can emit.\n */\nexport type TokenRatesControllerStateChangeEvent = ControllerStateChangeEvent<\n  typeof controllerName,\n  TokenRatesControllerState\n>;\n\n/**\n * The events that {@link TokenRatesController} can emit.\n */\nexport type TokenRatesControllerEvents = TokenRatesControllerStateChangeEvent;\n\n/**\n * The messenger of the {@link TokenRatesController} for communication.\n */\nexport type TokenRatesControllerMessenger = Messenger<\n  typeof controllerName,\n  TokenRatesControllerActions | AllowedActions,\n  TokenRatesControllerEvents | AllowedEvents\n>;\n\nconst tokenRatesControllerMetadata: StateMetadata<TokenRatesControllerState> = {\n  marketData: {\n    includeInStateLogs: false,\n    persist: true,\n    includeInDebugSnapshot: false,\n    usedInUi: true,\n  },\n};\n\n/**\n * Get the default {@link TokenRatesController} state.\n *\n * @returns The default {@link TokenRatesController} state.\n */\nexport const getDefaultTokenRatesControllerState =\n  (): TokenRatesControllerState => {\n    return {\n      marketData: {},\n    };\n  };\n\n/** The input to start polling for the {@link TokenRatesController} */\nexport type TokenRatesPollingInput = {\n  chainIds: Hex[];\n};\n\n/**\n * Controller that passively polls on a set interval for token-to-fiat exchange rates\n * for tokens stored in the TokensController\n */\nexport class TokenRatesController extends StaticIntervalPollingController<TokenRatesPollingInput>()<\n  typeof controllerName,\n  TokenRatesControllerState,\n  TokenRatesControllerMessenger\n> {\n  readonly #tokenPricesService: AbstractTokenPricesService;\n\n  #disabled: boolean;\n\n  #allTokens: TokensControllerState['allTokens'];\n\n  #allDetectedTokens: TokensControllerState['allDetectedTokens'];\n\n  /**\n   * Creates a TokenRatesController instance.\n   *\n   * @param options - The controller options.\n   * @param options.interval - The polling interval in ms\n   * @param options.disabled - Boolean to track if network requests are blocked\n   * @param options.tokenPricesService - An object in charge of retrieving token price\n   * @param options.messenger - The messenger instance for communication\n   * @param options.state - Initial state to set on this controller\n   */\n  constructor({\n    interval = DEFAULT_INTERVAL,\n    disabled = false,\n    tokenPricesService,\n    messenger,\n    state,\n  }: {\n    interval?: number;\n    disabled?: boolean;\n    tokenPricesService: AbstractTokenPricesService;\n    messenger: TokenRatesControllerMessenger;\n    state?: Partial<TokenRatesControllerState>;\n  }) {\n    super({\n      name: controllerName,\n      messenger,\n      state: { ...getDefaultTokenRatesControllerState(), ...state },\n      metadata: tokenRatesControllerMetadata,\n    });\n\n    this.setIntervalLength(interval);\n    this.#tokenPricesService = tokenPricesService;\n    this.#disabled = disabled;\n\n    const { allTokens, allDetectedTokens } = this.#getTokensControllerState();\n    this.#allTokens = allTokens;\n    this.#allDetectedTokens = allDetectedTokens;\n\n    // Set native asset identifiers from NetworkEnablementController for CAIP-19 native token lookups\n    this.#initNativeAssetIdentifiers();\n\n    this.#subscribeToTokensStateChange();\n\n    this.#subscribeToNetworkStateChange();\n  }\n\n  #subscribeToTokensStateChange() {\n    this.messenger.subscribe(\n      'TokensController:stateChange',\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      async ({ allTokens, allDetectedTokens }) => {\n        if (this.#disabled) {\n          return;\n        }\n\n        const { networkConfigurationsByChainId } = this.messenger.call(\n          'NetworkController:getState',\n        );\n\n        const chainIds = [\n          ...new Set([\n            ...Object.keys(allTokens),\n            ...Object.keys(allDetectedTokens),\n          ]),\n        ] as Hex[];\n\n        const chainIdsToUpdate = chainIds.filter(\n          (chainId) =>\n            !isEqual(this.#allTokens[chainId], allTokens[chainId]) ||\n            !isEqual(\n              this.#allDetectedTokens[chainId],\n              allDetectedTokens[chainId],\n            ),\n        );\n\n        this.#allTokens = allTokens;\n        this.#allDetectedTokens = allDetectedTokens;\n\n        const chainIdAndNativeCurrency = chainIdsToUpdate.reduce<\n          { chainId: Hex; nativeCurrency: string }[]\n        >((acc, chainId) => {\n          const networkConfiguration = networkConfigurationsByChainId[chainId];\n          if (!networkConfiguration) {\n            console.error(\n              `TokenRatesController: No network configuration found for chainId ${chainId}`,\n            );\n            return acc;\n          }\n          acc.push({\n            chainId,\n            nativeCurrency: networkConfiguration.nativeCurrency,\n          });\n          return acc;\n        }, []);\n\n        await this.updateExchangeRates(chainIdAndNativeCurrency);\n      },\n      ({ allTokens, allDetectedTokens }) => {\n        return { allTokens, allDetectedTokens };\n      },\n    );\n  }\n\n  #subscribeToNetworkStateChange() {\n    this.messenger.subscribe(\n      'NetworkController:stateChange',\n      (_state, patches) => {\n        // Remove state for deleted networks\n        for (const patch of patches) {\n          if (\n            patch.op === 'remove' &&\n            patch.path[0] === 'networkConfigurationsByChainId'\n          ) {\n            const removedChainId = patch.path[1] as Hex;\n            this.update((state) => {\n              delete state.marketData[removedChainId];\n            });\n          }\n        }\n      },\n    );\n  }\n\n  /**\n   * Initialize the native asset identifiers from NetworkEnablementController.\n   * This provides CAIP-19 native asset IDs for the token prices service.\n   */\n  #initNativeAssetIdentifiers(): void {\n    if (this.#tokenPricesService.setNativeAssetIdentifiers) {\n      const { nativeAssetIdentifiers } = this.messenger.call(\n        'NetworkEnablementController:getState',\n      );\n      this.#tokenPricesService.setNativeAssetIdentifiers(\n        nativeAssetIdentifiers,\n      );\n    }\n  }\n\n  /**\n   * Get the tokens for the given chain.\n   *\n   * @param chainId - The chain ID.\n   * @returns The list of tokens addresses for the current chain\n   */\n  #getTokenAddresses(chainId: Hex): Hex[] {\n    const getTokens = (allTokens: Record<Hex, { address: string }[]>) =>\n      Object.values(allTokens ?? {}).flatMap((tokens) =>\n        tokens.map(({ address }) => toChecksumHexAddress(address) as Hex),\n      );\n\n    const tokenAddresses = getTokens(this.#allTokens[chainId]);\n    const detectedTokenAddresses = getTokens(this.#allDetectedTokens[chainId]);\n\n    return [\n      ...new Set([\n        ...tokenAddresses,\n        ...detectedTokenAddresses,\n        getNativeTokenAddress(chainId),\n      ]),\n    ].sort();\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  #getTokensControllerState(): {\n    allTokens: TokensControllerState['allTokens'];\n    allDetectedTokens: TokensControllerState['allDetectedTokens'];\n  } {\n    const { allTokens, allDetectedTokens } = this.messenger.call(\n      'TokensController:getState',\n    );\n\n    return {\n      allTokens,\n      allDetectedTokens,\n    };\n  }\n\n  /**\n   * Updates exchange rates for all tokens.\n   *\n   * @param chainIdAndNativeCurrency - The chain ID and native currency.\n   */\n  async updateExchangeRates(\n    chainIdAndNativeCurrency: ChainIdAndNativeCurrency[],\n  ): Promise<void> {\n    if (this.#disabled) {\n      return;\n    }\n\n    const marketData: Record<Hex, Record<Hex, MarketDataDetails>> = {};\n    const assetsByNativeCurrency: Record<\n      string,\n      {\n        chainId: Hex;\n        tokenAddress: Hex;\n      }[]\n    > = {};\n    const unsupportedAssetsByNativeCurrency: Record<\n      string,\n      {\n        chainId: Hex;\n        tokenAddress: Hex;\n      }[]\n    > = {};\n    for (const { chainId, nativeCurrency } of chainIdAndNativeCurrency) {\n      if (this.#tokenPricesService.validateChainIdSupported(chainId)) {\n        for (const tokenAddress of this.#getTokenAddresses(chainId)) {\n          if (\n            this.#tokenPricesService.validateCurrencySupported(nativeCurrency)\n          ) {\n            (assetsByNativeCurrency[nativeCurrency] ??= []).push({\n              chainId,\n              tokenAddress,\n            });\n          } else {\n            (unsupportedAssetsByNativeCurrency[nativeCurrency] ??= []).push({\n              chainId,\n              tokenAddress,\n            });\n          }\n        }\n      }\n    }\n\n    const promises = [\n      ...Object.entries(assetsByNativeCurrency).map(\n        ([nativeCurrency, assets]) =>\n          this.#fetchAndMapExchangeRatesForSupportedNativeCurrency(\n            assets,\n            nativeCurrency,\n            marketData,\n          ),\n      ),\n      ...Object.entries(unsupportedAssetsByNativeCurrency).map(\n        ([nativeCurrency, assets]) =>\n          this.#fetchAndMapExchangeRatesForUnsupportedNativeCurrency(\n            assets,\n            nativeCurrency,\n            marketData,\n          ),\n      ),\n    ];\n\n    await Promise.allSettled(promises);\n\n    const chainIds = new Set(\n      Object.values(chainIdAndNativeCurrency).map((chain) => chain.chainId),\n    );\n\n    for (const chainId of chainIds) {\n      if (!marketData[chainId]) {\n        marketData[chainId] = {};\n      }\n    }\n\n    if (Object.keys(marketData).length > 0) {\n      this.update((state) => {\n        state.marketData = {\n          ...state.marketData,\n          ...marketData,\n        };\n      });\n    }\n  }\n\n  async #fetchAndMapExchangeRatesForSupportedNativeCurrency(\n    assets: {\n      chainId: Hex;\n      tokenAddress: Hex;\n    }[],\n    currency: string,\n    marketData: Record<Hex, Record<Hex, MarketDataDetails>> = {},\n  ) {\n    return await reduceInBatchesSerially<\n      { chainId: Hex; tokenAddress: Hex },\n      Record<Hex, Record<Hex, MarketDataDetails>>\n    >({\n      values: assets,\n      batchSize: TOKEN_PRICES_BATCH_SIZE,\n      eachBatch: async (partialMarketData, assetsBatch) => {\n        const batchMarketData = await this.#tokenPricesService.fetchTokenPrices(\n          {\n            assets: assetsBatch,\n            currency,\n          },\n        );\n\n        for (const tokenPrice of batchMarketData) {\n          (partialMarketData[tokenPrice.chainId] ??= {})[\n            tokenPrice.tokenAddress\n          ] = tokenPrice;\n        }\n\n        return partialMarketData;\n      },\n      initialResult: marketData,\n    });\n  }\n\n  async #fetchAndMapExchangeRatesForUnsupportedNativeCurrency(\n    assets: {\n      chainId: Hex;\n      tokenAddress: Hex;\n    }[],\n    currency: string,\n    marketData: Record<Hex, Record<Hex, MarketDataDetails>>,\n  ) {\n    // Step -1: Then fetch all tracked tokens priced in USD\n    const marketDataInUSD =\n      await this.#fetchAndMapExchangeRatesForSupportedNativeCurrency(\n        assets,\n        'usd', // Fallback currency when the native currency is not supported\n      );\n\n    // Formula: price_in_native = token_usd / native_usd\n    const convertUSDToNative = (\n      valueInUSD: number,\n      nativeTokenPriceInUSD: number,\n    ) => valueInUSD / nativeTokenPriceInUSD;\n\n    // Step -2: Convert USD prices to native currency\n    for (const [chainId, marketDataByTokenAddress] of Object.entries(\n      marketDataInUSD,\n    ) as [Hex, Record<Hex, MarketDataDetails>][]) {\n      const nativeTokenPriceInUSD =\n        marketDataByTokenAddress[getNativeTokenAddress(chainId)]?.price;\n\n      // Return here if it's null, undefined or 0\n      if (!nativeTokenPriceInUSD) {\n        continue;\n      }\n\n      for (const [tokenAddress, tokenData] of Object.entries(\n        marketDataByTokenAddress,\n      ) as [Hex, MarketDataDetails][]) {\n        (marketData[chainId] ??= {})[tokenAddress] = {\n          ...tokenData,\n          currency,\n          price: convertUSDToNative(tokenData.price, nativeTokenPriceInUSD),\n          marketCap: convertUSDToNative(\n            tokenData.marketCap,\n            nativeTokenPriceInUSD,\n          ),\n          allTimeHigh: convertUSDToNative(\n            tokenData.allTimeHigh,\n            nativeTokenPriceInUSD,\n          ),\n          allTimeLow: convertUSDToNative(\n            tokenData.allTimeLow,\n            nativeTokenPriceInUSD,\n          ),\n          totalVolume: convertUSDToNative(\n            tokenData.totalVolume,\n            nativeTokenPriceInUSD,\n          ),\n          high1d: convertUSDToNative(tokenData.high1d, nativeTokenPriceInUSD),\n          low1d: convertUSDToNative(tokenData.low1d, nativeTokenPriceInUSD),\n          dilutedMarketCap: convertUSDToNative(\n            tokenData.dilutedMarketCap,\n            nativeTokenPriceInUSD,\n          ),\n        };\n      }\n    }\n  }\n\n  /**\n   * Updates token rates for the given networkClientId\n   *\n   * @param input - The input for the poll.\n   * @param input.chainIds - The chain ids to poll token rates on.\n   */\n  async _executePoll({ chainIds }: TokenRatesPollingInput): Promise<void> {\n    const { networkConfigurationsByChainId } = this.messenger.call(\n      'NetworkController:getState',\n    );\n\n    const chainIdAndNativeCurrency = chainIds.reduce<\n      { chainId: Hex; nativeCurrency: string }[]\n    >((acc, chainId) => {\n      const networkConfiguration = networkConfigurationsByChainId[chainId];\n      if (!networkConfiguration) {\n        console.error(\n          `TokenRatesController: No network configuration found for chainId ${chainId}`,\n        );\n        return acc;\n      }\n      acc.push({\n        chainId,\n        nativeCurrency: networkConfiguration.nativeCurrency,\n      });\n      return acc;\n    }, []);\n\n    await this.updateExchangeRates(chainIdAndNativeCurrency);\n  }\n\n  /**\n   * Reset the controller state to the default state.\n   */\n  resetState() {\n    this.update(() => {\n      return getDefaultTokenRatesControllerState();\n    });\n  }\n}\n\nexport default TokenRatesController;\n"]}