@metamask-previews/assets-controllers 89.0.1-preview-f83c2058 → 89.0.1-preview-fa19a4d3
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +0 -8
- package/dist/CurrencyRateController.cjs +3 -3
- package/dist/CurrencyRateController.cjs.map +1 -1
- package/dist/CurrencyRateController.d.cts.map +1 -1
- package/dist/CurrencyRateController.d.mts.map +1 -1
- package/dist/CurrencyRateController.mjs +3 -3
- package/dist/CurrencyRateController.mjs.map +1 -1
- package/dist/TokenBalancesController.cjs +64 -22
- package/dist/TokenBalancesController.cjs.map +1 -1
- package/dist/TokenBalancesController.d.cts.map +1 -1
- package/dist/TokenBalancesController.d.mts.map +1 -1
- package/dist/TokenBalancesController.mjs +64 -22
- package/dist/TokenBalancesController.mjs.map +1 -1
- package/dist/TokenRatesController.cjs +278 -82
- package/dist/TokenRatesController.cjs.map +1 -1
- package/dist/TokenRatesController.d.cts +35 -9
- package/dist/TokenRatesController.d.cts.map +1 -1
- package/dist/TokenRatesController.d.mts +35 -9
- package/dist/TokenRatesController.d.mts.map +1 -1
- package/dist/TokenRatesController.mjs +279 -83
- package/dist/TokenRatesController.mjs.map +1 -1
- package/dist/TokenSearchDiscoveryDataController/TokenSearchDiscoveryDataController.cjs +3 -2
- package/dist/TokenSearchDiscoveryDataController/TokenSearchDiscoveryDataController.cjs.map +1 -1
- package/dist/TokenSearchDiscoveryDataController/TokenSearchDiscoveryDataController.d.cts.map +1 -1
- package/dist/TokenSearchDiscoveryDataController/TokenSearchDiscoveryDataController.d.mts.map +1 -1
- package/dist/TokenSearchDiscoveryDataController/TokenSearchDiscoveryDataController.mjs +3 -2
- package/dist/TokenSearchDiscoveryDataController/TokenSearchDiscoveryDataController.mjs.map +1 -1
- package/dist/TokenSearchDiscoveryDataController/types.cjs.map +1 -1
- package/dist/TokenSearchDiscoveryDataController/types.d.cts +2 -2
- package/dist/TokenSearchDiscoveryDataController/types.d.cts.map +1 -1
- package/dist/TokenSearchDiscoveryDataController/types.d.mts +2 -2
- package/dist/TokenSearchDiscoveryDataController/types.d.mts.map +1 -1
- package/dist/TokenSearchDiscoveryDataController/types.mjs.map +1 -1
- package/dist/assetsUtil.cjs +5 -11
- package/dist/assetsUtil.cjs.map +1 -1
- package/dist/assetsUtil.d.cts +2 -2
- package/dist/assetsUtil.d.cts.map +1 -1
- package/dist/assetsUtil.d.mts +2 -2
- package/dist/assetsUtil.d.mts.map +1 -1
- package/dist/assetsUtil.mjs +5 -11
- package/dist/assetsUtil.mjs.map +1 -1
- package/dist/token-prices-service/abstract-token-prices-service.cjs.map +1 -1
- package/dist/token-prices-service/abstract-token-prices-service.d.cts +43 -17
- package/dist/token-prices-service/abstract-token-prices-service.d.cts.map +1 -1
- package/dist/token-prices-service/abstract-token-prices-service.d.mts +43 -17
- package/dist/token-prices-service/abstract-token-prices-service.d.mts.map +1 -1
- package/dist/token-prices-service/abstract-token-prices-service.mjs.map +1 -1
- package/dist/token-prices-service/codefi-v2.cjs +109 -175
- package/dist/token-prices-service/codefi-v2.cjs.map +1 -1
- package/dist/token-prices-service/codefi-v2.d.cts +10 -47
- package/dist/token-prices-service/codefi-v2.d.cts.map +1 -1
- package/dist/token-prices-service/codefi-v2.d.mts +10 -47
- package/dist/token-prices-service/codefi-v2.d.mts.map +1 -1
- package/dist/token-prices-service/codefi-v2.mjs +109 -175
- package/dist/token-prices-service/codefi-v2.mjs.map +1 -1
- package/package.json +1 -1
package/CHANGELOG.md
CHANGED
|
@@ -7,14 +7,6 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
|
|
7
7
|
|
|
8
8
|
## [Unreleased]
|
|
9
9
|
|
|
10
|
-
### Changed
|
|
11
|
-
|
|
12
|
-
- **BREAKING:** Update `spot-prices` endpoint to use Price API v3 ([#7119](https://github.com/MetaMask/core/pull/7119))
|
|
13
|
-
- Update `AbstractTokenPricesService.fetchTokenPrices` arguments and return type
|
|
14
|
-
- Update `CodefiTokenPricesServiceV2` list of supported currencies
|
|
15
|
-
- Update `TokenRatesController` to fetch prices by native currency instead of by chain
|
|
16
|
-
- Remove legacy polling code and unused events from `TokenRatesController`
|
|
17
|
-
|
|
18
10
|
## [89.0.1]
|
|
19
11
|
|
|
20
12
|
### Fixed
|
|
@@ -207,11 +207,11 @@ _CurrencyRateController_tokenPricesService = new WeakMap(), _CurrencyRateControl
|
|
|
207
207
|
const nativeTokenAddress = (0, codefi_v2_1.getNativeTokenAddress)(chainId);
|
|
208
208
|
// Pass empty array as fetchTokenPrices automatically includes the native token address
|
|
209
209
|
const tokenPrices = await __classPrivateFieldGet(this, _CurrencyRateController_tokenPricesService, "f").fetchTokenPrices({
|
|
210
|
-
|
|
210
|
+
chainId,
|
|
211
|
+
tokenAddresses: [],
|
|
211
212
|
currency: currentCurrency,
|
|
212
213
|
});
|
|
213
|
-
const tokenPrice = tokenPrices
|
|
214
|
-
nativeTokenAddress.toLowerCase());
|
|
214
|
+
const tokenPrice = tokenPrices[nativeTokenAddress];
|
|
215
215
|
return {
|
|
216
216
|
nativeCurrency,
|
|
217
217
|
conversionDate: tokenPrice ? Date.now() / 1000 : null,
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"CurrencyRateController.cjs","sourceRoot":"","sources":["../src/CurrencyRateController.ts"],"names":[],"mappings":"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","sourcesContent":["import type {\n ControllerGetStateAction,\n ControllerStateChangeEvent,\n StateMetadata,\n} from '@metamask/base-controller';\nimport {\n TESTNET_TICKER_SYMBOLS,\n FALL_BACK_VS_CURRENCY,\n} from '@metamask/controller-utils';\nimport type { Messenger } from '@metamask/messenger';\nimport type {\n NetworkControllerGetNetworkClientByIdAction,\n NetworkControllerGetStateAction,\n NetworkConfiguration,\n} from '@metamask/network-controller';\nimport { StaticIntervalPollingController } from '@metamask/polling-controller';\nimport type { Hex } from '@metamask/utils';\nimport { Mutex } from 'async-mutex';\n\nimport type { AbstractTokenPricesService } from './token-prices-service/abstract-token-prices-service';\nimport { getNativeTokenAddress } from './token-prices-service/codefi-v2';\n\n/**\n * currencyRates - Object keyed by native currency\n *\n * currencyRates.conversionDate - Timestamp of conversion rate expressed in ms since UNIX epoch\n *\n * currencyRates.conversionRate - Conversion rate from current base asset to the current currency\n *\n * currentCurrency - Currently-active ISO 4217 currency code\n *\n * usdConversionRate - Conversion rate from usd to the current currency\n */\nexport type CurrencyRateState = {\n currentCurrency: string;\n currencyRates: Record<\n string,\n {\n conversionDate: number | null;\n conversionRate: number | null;\n usdConversionRate: number | null;\n }\n >;\n};\n\nconst name = 'CurrencyRateController';\n\nexport type CurrencyRateStateChange = ControllerStateChangeEvent<\n typeof name,\n CurrencyRateState\n>;\n\nexport type CurrencyRateControllerEvents = CurrencyRateStateChange;\n\nexport type GetCurrencyRateState = ControllerGetStateAction<\n typeof name,\n CurrencyRateState\n>;\n\nexport type CurrencyRateControllerActions = GetCurrencyRateState;\n\ntype AllowedActions =\n | NetworkControllerGetNetworkClientByIdAction\n | NetworkControllerGetStateAction;\n\nexport type CurrencyRateMessenger = Messenger<\n typeof name,\n CurrencyRateControllerActions | AllowedActions,\n CurrencyRateControllerEvents\n>;\n\nconst metadata: StateMetadata<CurrencyRateState> = {\n currentCurrency: {\n includeInStateLogs: true,\n persist: true,\n includeInDebugSnapshot: true,\n usedInUi: true,\n },\n currencyRates: {\n includeInStateLogs: true,\n persist: true,\n includeInDebugSnapshot: true,\n usedInUi: true,\n },\n};\n\nconst defaultState = {\n currentCurrency: 'usd',\n currencyRates: {\n ETH: {\n conversionDate: 0,\n conversionRate: 0,\n usdConversionRate: null,\n },\n },\n};\n\n/** The input to start polling for the {@link CurrencyRateController} */\ntype CurrencyRatePollingInput = {\n nativeCurrencies: string[];\n};\n\n/**\n * Controller that passively polls on a set interval for an exchange rate from the current network\n * asset to the user's preferred currency.\n */\nexport class CurrencyRateController extends StaticIntervalPollingController<CurrencyRatePollingInput>()<\n typeof name,\n CurrencyRateState,\n CurrencyRateMessenger\n> {\n private readonly mutex = new Mutex();\n\n private readonly includeUsdRate;\n\n private readonly useExternalServices: () => boolean;\n\n readonly #tokenPricesService: AbstractTokenPricesService;\n\n /**\n * Creates a CurrencyRateController instance.\n *\n * @param options - Constructor options.\n * @param options.includeUsdRate - Keep track of the USD rate in addition to the current currency rate.\n * @param options.interval - The polling interval, in milliseconds.\n * @param options.messenger - A reference to the messenger.\n * @param options.state - Initial state to set on this controller.\n * @param options.useExternalServices - Feature Switch for using external services (default: true)\n * @param options.tokenPricesService - An object in charge of retrieving token prices\n */\n constructor({\n includeUsdRate = false,\n interval = 180000,\n useExternalServices = () => true,\n messenger,\n state,\n tokenPricesService,\n }: {\n includeUsdRate?: boolean;\n interval?: number;\n messenger: CurrencyRateMessenger;\n state?: Partial<CurrencyRateState>;\n useExternalServices?: () => boolean;\n tokenPricesService: AbstractTokenPricesService;\n }) {\n super({\n name,\n metadata,\n messenger,\n state: { ...defaultState, ...state },\n });\n this.includeUsdRate = includeUsdRate;\n this.useExternalServices = useExternalServices;\n this.setIntervalLength(interval);\n this.#tokenPricesService = tokenPricesService;\n }\n\n /**\n * Sets a currency to track.\n *\n * @param currentCurrency - ISO 4217 currency code.\n */\n async setCurrentCurrency(currentCurrency: string) {\n const releaseLock = await this.mutex.acquire();\n const nativeCurrencies = Object.keys(this.state.currencyRates);\n try {\n this.update(() => {\n return {\n ...defaultState,\n currentCurrency,\n };\n });\n } finally {\n releaseLock();\n }\n // TODO: Either fix this lint violation or explain why it's necessary to ignore.\n // eslint-disable-next-line @typescript-eslint/no-floating-promises\n this.updateExchangeRate(nativeCurrencies);\n }\n\n async #fetchExchangeRatesWithFallback(\n nativeCurrenciesToFetch: Record<string, string>,\n ): Promise<CurrencyRateState['currencyRates']> {\n const { currentCurrency } = this.state;\n\n try {\n const priceApiExchangeRatesResponse =\n await this.#tokenPricesService.fetchExchangeRates({\n baseCurrency: currentCurrency,\n includeUsdRate: this.includeUsdRate,\n cryptocurrencies: [\n ...new Set(Object.values(nativeCurrenciesToFetch)),\n ],\n });\n\n const ratesPriceApi = Object.entries(nativeCurrenciesToFetch).reduce(\n (acc, [nativeCurrency, fetchedCurrency]) => {\n const rate =\n priceApiExchangeRatesResponse[fetchedCurrency.toLowerCase()];\n\n acc[nativeCurrency] = {\n conversionDate: rate !== undefined ? Date.now() / 1000 : null,\n conversionRate: rate?.value\n ? Number((1 / rate?.value).toFixed(2))\n : null,\n usdConversionRate: rate?.usd\n ? Number((1 / rate?.usd).toFixed(2))\n : null,\n };\n return acc;\n },\n {} as CurrencyRateState['currencyRates'],\n );\n return ratesPriceApi;\n } catch (error) {\n console.error('Failed to fetch exchange rates.', error);\n }\n\n // fallback using spot price from token prices service\n try {\n // Step 1: Get all network configurations to find matching chainIds for native currencies\n const networkControllerState = this.messenger.call(\n 'NetworkController:getState',\n );\n const networkConfigurations =\n networkControllerState.networkConfigurationsByChainId;\n\n // Step 2: Build a map of nativeCurrency -> chainId(s)\n const currencyToChainIds = Object.entries(nativeCurrenciesToFetch).reduce(\n (acc, [nativeCurrency, fetchedCurrency]) => {\n // Find the first chainId that has this native currency\n const matchingEntry = (\n Object.entries(networkConfigurations) as [\n Hex,\n NetworkConfiguration,\n ][]\n ).find(\n ([, config]) =>\n config.nativeCurrency.toUpperCase() ===\n fetchedCurrency.toUpperCase(),\n );\n\n if (matchingEntry) {\n acc[nativeCurrency] = {\n fetchedCurrency,\n chainId: matchingEntry[0],\n };\n }\n\n return acc;\n },\n {} as Record<string, { fetchedCurrency: string; chainId: Hex }>,\n );\n\n // Step 3: Fetch token prices for each chainId\n const currencyToChainIdsEntries = Object.entries(currencyToChainIds);\n const ratesResults = await Promise.allSettled(\n currencyToChainIdsEntries.map(async ([nativeCurrency, { chainId }]) => {\n const nativeTokenAddress = getNativeTokenAddress(chainId);\n // Pass empty array as fetchTokenPrices automatically includes the native token address\n const tokenPrices = await this.#tokenPricesService.fetchTokenPrices({\n assets: [{ chainId, tokenAddress: nativeTokenAddress }],\n currency: currentCurrency,\n });\n\n const tokenPrice = tokenPrices.find(\n (item) =>\n item.tokenAddress.toLowerCase() ===\n nativeTokenAddress.toLowerCase(),\n );\n\n return {\n nativeCurrency,\n conversionDate: tokenPrice ? Date.now() / 1000 : null,\n conversionRate: tokenPrice?.price ?? null,\n usdConversionRate: null, // Token prices service doesn't provide USD rate in this context\n };\n }),\n );\n const ratesFromTokenPrices = ratesResults.map((result, index) => {\n const [nativeCurrency, { chainId }] = currencyToChainIdsEntries[index];\n if (result.status === 'fulfilled') {\n return result.value;\n }\n console.error(\n `Failed to fetch token price for ${nativeCurrency} on chain ${chainId}`,\n result.reason,\n );\n return {\n nativeCurrency,\n conversionDate: null,\n conversionRate: null,\n usdConversionRate: null,\n };\n });\n\n // Step 4: Convert to the expected format\n const ratesFromTokenPricesService = ratesFromTokenPrices.reduce(\n (acc, rate) => {\n acc[rate.nativeCurrency] = {\n conversionDate: rate.conversionDate,\n conversionRate: rate.conversionRate,\n usdConversionRate: rate.usdConversionRate,\n };\n return acc;\n },\n {} as CurrencyRateState['currencyRates'],\n );\n\n return ratesFromTokenPricesService;\n } catch (error) {\n console.error(\n 'Failed to fetch exchange rates from token prices service.',\n error,\n );\n // Return null state for all requested currencies\n return Object.keys(nativeCurrenciesToFetch).reduce(\n (acc, nativeCurrency) => {\n acc[nativeCurrency] = {\n conversionDate: null,\n conversionRate: null,\n usdConversionRate: null,\n };\n return acc;\n },\n {} as CurrencyRateState['currencyRates'],\n );\n }\n }\n\n /**\n * Updates the exchange rate for the current currency and native currency pairs.\n *\n * @param nativeCurrencies - The native currency symbols to fetch exchange rates for.\n */\n async updateExchangeRate(\n nativeCurrencies: (string | undefined)[],\n ): Promise<void> {\n if (!this.useExternalServices()) {\n return;\n }\n\n const releaseLock = await this.mutex.acquire();\n try {\n // For preloaded testnets (Goerli, Sepolia) we want to fetch exchange rate for real ETH.\n // Map each native currency to the symbol we want to fetch for it.\n const testnetSymbols = Object.values(TESTNET_TICKER_SYMBOLS);\n const nativeCurrenciesToFetch = nativeCurrencies.reduce(\n (acc, nativeCurrency) => {\n if (!nativeCurrency) {\n return acc;\n }\n\n acc[nativeCurrency] = testnetSymbols.includes(nativeCurrency)\n ? FALL_BACK_VS_CURRENCY\n : nativeCurrency;\n return acc;\n },\n {} as Record<string, string>,\n );\n\n const rates = await this.#fetchExchangeRatesWithFallback(\n nativeCurrenciesToFetch,\n );\n\n this.update((state) => {\n state.currencyRates = {\n ...state.currencyRates,\n ...rates,\n };\n });\n } catch (error) {\n console.error('Failed to fetch exchange rates.', error);\n throw error;\n } finally {\n releaseLock();\n }\n }\n\n /**\n * Prepare to discard this controller.\n *\n * This stops any active polling.\n */\n override destroy() {\n super.destroy();\n this.stopAllPolling();\n }\n\n /**\n * Updates exchange rate for the current currency.\n *\n * @param input - The input for the poll.\n * @param input.nativeCurrencies - The native currency symbols to poll prices for.\n */\n async _executePoll({\n nativeCurrencies,\n }: CurrencyRatePollingInput): Promise<void> {\n await this.updateExchangeRate(nativeCurrencies);\n }\n}\n\nexport default CurrencyRateController;\n"]}
|
|
1
|
+
{"version":3,"file":"CurrencyRateController.cjs","sourceRoot":"","sources":["../src/CurrencyRateController.ts"],"names":[],"mappings":"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","sourcesContent":["import type {\n ControllerGetStateAction,\n ControllerStateChangeEvent,\n StateMetadata,\n} from '@metamask/base-controller';\nimport {\n TESTNET_TICKER_SYMBOLS,\n FALL_BACK_VS_CURRENCY,\n} from '@metamask/controller-utils';\nimport type { Messenger } from '@metamask/messenger';\nimport type {\n NetworkControllerGetNetworkClientByIdAction,\n NetworkControllerGetStateAction,\n NetworkConfiguration,\n} from '@metamask/network-controller';\nimport { StaticIntervalPollingController } from '@metamask/polling-controller';\nimport type { Hex } from '@metamask/utils';\nimport { Mutex } from 'async-mutex';\n\nimport type { AbstractTokenPricesService } from './token-prices-service/abstract-token-prices-service';\nimport { getNativeTokenAddress } from './token-prices-service/codefi-v2';\n\n/**\n * currencyRates - Object keyed by native currency\n *\n * currencyRates.conversionDate - Timestamp of conversion rate expressed in ms since UNIX epoch\n *\n * currencyRates.conversionRate - Conversion rate from current base asset to the current currency\n *\n * currentCurrency - Currently-active ISO 4217 currency code\n *\n * usdConversionRate - Conversion rate from usd to the current currency\n */\nexport type CurrencyRateState = {\n currentCurrency: string;\n currencyRates: Record<\n string,\n {\n conversionDate: number | null;\n conversionRate: number | null;\n usdConversionRate: number | null;\n }\n >;\n};\n\nconst name = 'CurrencyRateController';\n\nexport type CurrencyRateStateChange = ControllerStateChangeEvent<\n typeof name,\n CurrencyRateState\n>;\n\nexport type CurrencyRateControllerEvents = CurrencyRateStateChange;\n\nexport type GetCurrencyRateState = ControllerGetStateAction<\n typeof name,\n CurrencyRateState\n>;\n\nexport type CurrencyRateControllerActions = GetCurrencyRateState;\n\ntype AllowedActions =\n | NetworkControllerGetNetworkClientByIdAction\n | NetworkControllerGetStateAction;\n\nexport type CurrencyRateMessenger = Messenger<\n typeof name,\n CurrencyRateControllerActions | AllowedActions,\n CurrencyRateControllerEvents\n>;\n\nconst metadata: StateMetadata<CurrencyRateState> = {\n currentCurrency: {\n includeInStateLogs: true,\n persist: true,\n includeInDebugSnapshot: true,\n usedInUi: true,\n },\n currencyRates: {\n includeInStateLogs: true,\n persist: true,\n includeInDebugSnapshot: true,\n usedInUi: true,\n },\n};\n\nconst defaultState = {\n currentCurrency: 'usd',\n currencyRates: {\n ETH: {\n conversionDate: 0,\n conversionRate: 0,\n usdConversionRate: null,\n },\n },\n};\n\n/** The input to start polling for the {@link CurrencyRateController} */\ntype CurrencyRatePollingInput = {\n nativeCurrencies: string[];\n};\n\n/**\n * Controller that passively polls on a set interval for an exchange rate from the current network\n * asset to the user's preferred currency.\n */\nexport class CurrencyRateController extends StaticIntervalPollingController<CurrencyRatePollingInput>()<\n typeof name,\n CurrencyRateState,\n CurrencyRateMessenger\n> {\n private readonly mutex = new Mutex();\n\n private readonly includeUsdRate;\n\n private readonly useExternalServices: () => boolean;\n\n readonly #tokenPricesService: AbstractTokenPricesService;\n\n /**\n * Creates a CurrencyRateController instance.\n *\n * @param options - Constructor options.\n * @param options.includeUsdRate - Keep track of the USD rate in addition to the current currency rate.\n * @param options.interval - The polling interval, in milliseconds.\n * @param options.messenger - A reference to the messenger.\n * @param options.state - Initial state to set on this controller.\n * @param options.useExternalServices - Feature Switch for using external services (default: true)\n * @param options.tokenPricesService - An object in charge of retrieving token prices\n */\n constructor({\n includeUsdRate = false,\n interval = 180000,\n useExternalServices = () => true,\n messenger,\n state,\n tokenPricesService,\n }: {\n includeUsdRate?: boolean;\n interval?: number;\n messenger: CurrencyRateMessenger;\n state?: Partial<CurrencyRateState>;\n useExternalServices?: () => boolean;\n tokenPricesService: AbstractTokenPricesService;\n }) {\n super({\n name,\n metadata,\n messenger,\n state: { ...defaultState, ...state },\n });\n this.includeUsdRate = includeUsdRate;\n this.useExternalServices = useExternalServices;\n this.setIntervalLength(interval);\n this.#tokenPricesService = tokenPricesService;\n }\n\n /**\n * Sets a currency to track.\n *\n * @param currentCurrency - ISO 4217 currency code.\n */\n async setCurrentCurrency(currentCurrency: string) {\n const releaseLock = await this.mutex.acquire();\n const nativeCurrencies = Object.keys(this.state.currencyRates);\n try {\n this.update(() => {\n return {\n ...defaultState,\n currentCurrency,\n };\n });\n } finally {\n releaseLock();\n }\n // TODO: Either fix this lint violation or explain why it's necessary to ignore.\n // eslint-disable-next-line @typescript-eslint/no-floating-promises\n this.updateExchangeRate(nativeCurrencies);\n }\n\n async #fetchExchangeRatesWithFallback(\n nativeCurrenciesToFetch: Record<string, string>,\n ): Promise<CurrencyRateState['currencyRates']> {\n const { currentCurrency } = this.state;\n\n try {\n const priceApiExchangeRatesResponse =\n await this.#tokenPricesService.fetchExchangeRates({\n baseCurrency: currentCurrency,\n includeUsdRate: this.includeUsdRate,\n cryptocurrencies: [\n ...new Set(Object.values(nativeCurrenciesToFetch)),\n ],\n });\n\n const ratesPriceApi = Object.entries(nativeCurrenciesToFetch).reduce(\n (acc, [nativeCurrency, fetchedCurrency]) => {\n const rate =\n priceApiExchangeRatesResponse[fetchedCurrency.toLowerCase()];\n\n acc[nativeCurrency] = {\n conversionDate: rate !== undefined ? Date.now() / 1000 : null,\n conversionRate: rate?.value\n ? Number((1 / rate?.value).toFixed(2))\n : null,\n usdConversionRate: rate?.usd\n ? Number((1 / rate?.usd).toFixed(2))\n : null,\n };\n return acc;\n },\n {} as CurrencyRateState['currencyRates'],\n );\n return ratesPriceApi;\n } catch (error) {\n console.error('Failed to fetch exchange rates.', error);\n }\n\n // fallback using spot price from token prices service\n try {\n // Step 1: Get all network configurations to find matching chainIds for native currencies\n const networkControllerState = this.messenger.call(\n 'NetworkController:getState',\n );\n const networkConfigurations =\n networkControllerState.networkConfigurationsByChainId;\n\n // Step 2: Build a map of nativeCurrency -> chainId(s)\n const currencyToChainIds = Object.entries(nativeCurrenciesToFetch).reduce(\n (acc, [nativeCurrency, fetchedCurrency]) => {\n // Find the first chainId that has this native currency\n const matchingEntry = (\n Object.entries(networkConfigurations) as [\n Hex,\n NetworkConfiguration,\n ][]\n ).find(\n ([, config]) =>\n config.nativeCurrency.toUpperCase() ===\n fetchedCurrency.toUpperCase(),\n );\n\n if (matchingEntry) {\n acc[nativeCurrency] = {\n fetchedCurrency,\n chainId: matchingEntry[0],\n };\n }\n\n return acc;\n },\n {} as Record<string, { fetchedCurrency: string; chainId: Hex }>,\n );\n\n // Step 3: Fetch token prices for each chainId\n const currencyToChainIdsEntries = Object.entries(currencyToChainIds);\n const ratesResults = await Promise.allSettled(\n currencyToChainIdsEntries.map(async ([nativeCurrency, { chainId }]) => {\n const nativeTokenAddress = getNativeTokenAddress(chainId);\n // Pass empty array as fetchTokenPrices automatically includes the native token address\n const tokenPrices = await this.#tokenPricesService.fetchTokenPrices({\n chainId,\n tokenAddresses: [],\n currency: currentCurrency,\n });\n\n const tokenPrice = tokenPrices[nativeTokenAddress];\n\n return {\n nativeCurrency,\n conversionDate: tokenPrice ? Date.now() / 1000 : null,\n conversionRate: tokenPrice?.price ?? null,\n usdConversionRate: null, // Token prices service doesn't provide USD rate in this context\n };\n }),\n );\n const ratesFromTokenPrices = ratesResults.map((result, index) => {\n const [nativeCurrency, { chainId }] = currencyToChainIdsEntries[index];\n if (result.status === 'fulfilled') {\n return result.value;\n }\n console.error(\n `Failed to fetch token price for ${nativeCurrency} on chain ${chainId}`,\n result.reason,\n );\n return {\n nativeCurrency,\n conversionDate: null,\n conversionRate: null,\n usdConversionRate: null,\n };\n });\n\n // Step 4: Convert to the expected format\n const ratesFromTokenPricesService = ratesFromTokenPrices.reduce(\n (acc, rate) => {\n acc[rate.nativeCurrency] = {\n conversionDate: rate.conversionDate,\n conversionRate: rate.conversionRate,\n usdConversionRate: rate.usdConversionRate,\n };\n return acc;\n },\n {} as CurrencyRateState['currencyRates'],\n );\n\n return ratesFromTokenPricesService;\n } catch (error) {\n console.error(\n 'Failed to fetch exchange rates from token prices service.',\n error,\n );\n // Return null state for all requested currencies\n return Object.keys(nativeCurrenciesToFetch).reduce(\n (acc, nativeCurrency) => {\n acc[nativeCurrency] = {\n conversionDate: null,\n conversionRate: null,\n usdConversionRate: null,\n };\n return acc;\n },\n {} as CurrencyRateState['currencyRates'],\n );\n }\n }\n\n /**\n * Updates the exchange rate for the current currency and native currency pairs.\n *\n * @param nativeCurrencies - The native currency symbols to fetch exchange rates for.\n */\n async updateExchangeRate(\n nativeCurrencies: (string | undefined)[],\n ): Promise<void> {\n if (!this.useExternalServices()) {\n return;\n }\n\n const releaseLock = await this.mutex.acquire();\n try {\n // For preloaded testnets (Goerli, Sepolia) we want to fetch exchange rate for real ETH.\n // Map each native currency to the symbol we want to fetch for it.\n const testnetSymbols = Object.values(TESTNET_TICKER_SYMBOLS);\n const nativeCurrenciesToFetch = nativeCurrencies.reduce(\n (acc, nativeCurrency) => {\n if (!nativeCurrency) {\n return acc;\n }\n\n acc[nativeCurrency] = testnetSymbols.includes(nativeCurrency)\n ? FALL_BACK_VS_CURRENCY\n : nativeCurrency;\n return acc;\n },\n {} as Record<string, string>,\n );\n\n const rates = await this.#fetchExchangeRatesWithFallback(\n nativeCurrenciesToFetch,\n );\n\n this.update((state) => {\n state.currencyRates = {\n ...state.currencyRates,\n ...rates,\n };\n });\n } catch (error) {\n console.error('Failed to fetch exchange rates.', error);\n throw error;\n } finally {\n releaseLock();\n }\n }\n\n /**\n * Prepare to discard this controller.\n *\n * This stops any active polling.\n */\n override destroy() {\n super.destroy();\n this.stopAllPolling();\n }\n\n /**\n * Updates exchange rate for the current currency.\n *\n * @param input - The input for the poll.\n * @param input.nativeCurrencies - The native currency symbols to poll prices for.\n */\n async _executePoll({\n nativeCurrencies,\n }: CurrencyRatePollingInput): Promise<void> {\n await this.updateExchangeRate(nativeCurrencies);\n }\n}\n\nexport default CurrencyRateController;\n"]}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"CurrencyRateController.d.cts","sourceRoot":"","sources":["../src/CurrencyRateController.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EACV,wBAAwB,EACxB,0BAA0B,EAE3B,kCAAkC;AAKnC,OAAO,KAAK,EAAE,SAAS,EAAE,4BAA4B;AACrD,OAAO,KAAK,EACV,2CAA2C,EAC3C,+BAA+B,EAEhC,qCAAqC;AAKtC,OAAO,KAAK,EAAE,0BAA0B,EAAE,iEAA6D;AAGvG;;;;;;;;;;GAUG;AACH,MAAM,MAAM,iBAAiB,GAAG;IAC9B,eAAe,EAAE,MAAM,CAAC;IACxB,aAAa,EAAE,MAAM,CACnB,MAAM,EACN;QACE,cAAc,EAAE,MAAM,GAAG,IAAI,CAAC;QAC9B,cAAc,EAAE,MAAM,GAAG,IAAI,CAAC;QAC9B,iBAAiB,EAAE,MAAM,GAAG,IAAI,CAAC;KAClC,CACF,CAAC;CACH,CAAC;AAEF,QAAA,MAAM,IAAI,2BAA2B,CAAC;AAEtC,MAAM,MAAM,uBAAuB,GAAG,0BAA0B,CAC9D,OAAO,IAAI,EACX,iBAAiB,CAClB,CAAC;AAEF,MAAM,MAAM,4BAA4B,GAAG,uBAAuB,CAAC;AAEnE,MAAM,MAAM,oBAAoB,GAAG,wBAAwB,CACzD,OAAO,IAAI,EACX,iBAAiB,CAClB,CAAC;AAEF,MAAM,MAAM,6BAA6B,GAAG,oBAAoB,CAAC;AAEjE,KAAK,cAAc,GACf,2CAA2C,GAC3C,+BAA+B,CAAC;AAEpC,MAAM,MAAM,qBAAqB,GAAG,SAAS,CAC3C,OAAO,IAAI,EACX,6BAA6B,GAAG,cAAc,EAC9C,4BAA4B,CAC7B,CAAC;AA4BF,wEAAwE;AACxE,KAAK,wBAAwB,GAAG;IAC9B,gBAAgB,EAAE,MAAM,EAAE,CAAC;CAC5B,CAAC;;;;;;;;;;;;;;;;AAEF;;;GAGG;AACH,qBAAa,sBAAuB,SAAQ,4BAC1C,OAAO,IAAI,EACX,iBAAiB,EACjB,qBAAqB,CACtB;;IACC,OAAO,CAAC,QAAQ,CAAC,KAAK,CAAe;IAErC,OAAO,CAAC,QAAQ,CAAC,cAAc,CAAC;IAEhC,OAAO,CAAC,QAAQ,CAAC,mBAAmB,CAAgB;IAIpD;;;;;;;;;;OAUG;gBACS,EACV,cAAsB,EACtB,QAAiB,EACjB,mBAAgC,EAChC,SAAS,EACT,KAAK,EACL,kBAAkB,GACnB,EAAE;QACD,cAAc,CAAC,EAAE,OAAO,CAAC;QACzB,QAAQ,CAAC,EAAE,MAAM,CAAC;QAClB,SAAS,EAAE,qBAAqB,CAAC;QACjC,KAAK,CAAC,EAAE,OAAO,CAAC,iBAAiB,CAAC,CAAC;QACnC,mBAAmB,CAAC,EAAE,MAAM,OAAO,CAAC;QACpC,kBAAkB,EAAE,0BAA0B,CAAC;KAChD;IAaD;;;;OAIG;IACG,kBAAkB,CAAC,eAAe,EAAE,MAAM;
|
|
1
|
+
{"version":3,"file":"CurrencyRateController.d.cts","sourceRoot":"","sources":["../src/CurrencyRateController.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EACV,wBAAwB,EACxB,0BAA0B,EAE3B,kCAAkC;AAKnC,OAAO,KAAK,EAAE,SAAS,EAAE,4BAA4B;AACrD,OAAO,KAAK,EACV,2CAA2C,EAC3C,+BAA+B,EAEhC,qCAAqC;AAKtC,OAAO,KAAK,EAAE,0BAA0B,EAAE,iEAA6D;AAGvG;;;;;;;;;;GAUG;AACH,MAAM,MAAM,iBAAiB,GAAG;IAC9B,eAAe,EAAE,MAAM,CAAC;IACxB,aAAa,EAAE,MAAM,CACnB,MAAM,EACN;QACE,cAAc,EAAE,MAAM,GAAG,IAAI,CAAC;QAC9B,cAAc,EAAE,MAAM,GAAG,IAAI,CAAC;QAC9B,iBAAiB,EAAE,MAAM,GAAG,IAAI,CAAC;KAClC,CACF,CAAC;CACH,CAAC;AAEF,QAAA,MAAM,IAAI,2BAA2B,CAAC;AAEtC,MAAM,MAAM,uBAAuB,GAAG,0BAA0B,CAC9D,OAAO,IAAI,EACX,iBAAiB,CAClB,CAAC;AAEF,MAAM,MAAM,4BAA4B,GAAG,uBAAuB,CAAC;AAEnE,MAAM,MAAM,oBAAoB,GAAG,wBAAwB,CACzD,OAAO,IAAI,EACX,iBAAiB,CAClB,CAAC;AAEF,MAAM,MAAM,6BAA6B,GAAG,oBAAoB,CAAC;AAEjE,KAAK,cAAc,GACf,2CAA2C,GAC3C,+BAA+B,CAAC;AAEpC,MAAM,MAAM,qBAAqB,GAAG,SAAS,CAC3C,OAAO,IAAI,EACX,6BAA6B,GAAG,cAAc,EAC9C,4BAA4B,CAC7B,CAAC;AA4BF,wEAAwE;AACxE,KAAK,wBAAwB,GAAG;IAC9B,gBAAgB,EAAE,MAAM,EAAE,CAAC;CAC5B,CAAC;;;;;;;;;;;;;;;;AAEF;;;GAGG;AACH,qBAAa,sBAAuB,SAAQ,4BAC1C,OAAO,IAAI,EACX,iBAAiB,EACjB,qBAAqB,CACtB;;IACC,OAAO,CAAC,QAAQ,CAAC,KAAK,CAAe;IAErC,OAAO,CAAC,QAAQ,CAAC,cAAc,CAAC;IAEhC,OAAO,CAAC,QAAQ,CAAC,mBAAmB,CAAgB;IAIpD;;;;;;;;;;OAUG;gBACS,EACV,cAAsB,EACtB,QAAiB,EACjB,mBAAgC,EAChC,SAAS,EACT,KAAK,EACL,kBAAkB,GACnB,EAAE;QACD,cAAc,CAAC,EAAE,OAAO,CAAC;QACzB,QAAQ,CAAC,EAAE,MAAM,CAAC;QAClB,SAAS,EAAE,qBAAqB,CAAC;QACjC,KAAK,CAAC,EAAE,OAAO,CAAC,iBAAiB,CAAC,CAAC;QACnC,mBAAmB,CAAC,EAAE,MAAM,OAAO,CAAC;QACpC,kBAAkB,EAAE,0BAA0B,CAAC;KAChD;IAaD;;;;OAIG;IACG,kBAAkB,CAAC,eAAe,EAAE,MAAM;IAqKhD;;;;OAIG;IACG,kBAAkB,CACtB,gBAAgB,EAAE,CAAC,MAAM,GAAG,SAAS,CAAC,EAAE,GACvC,OAAO,CAAC,IAAI,CAAC;IA0ChB;;;;OAIG;IACM,OAAO;IAKhB;;;;;OAKG;IACG,YAAY,CAAC,EACjB,gBAAgB,GACjB,EAAE,wBAAwB,GAAG,OAAO,CAAC,IAAI,CAAC;CAG5C;AAED,eAAe,sBAAsB,CAAC"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"CurrencyRateController.d.mts","sourceRoot":"","sources":["../src/CurrencyRateController.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EACV,wBAAwB,EACxB,0BAA0B,EAE3B,kCAAkC;AAKnC,OAAO,KAAK,EAAE,SAAS,EAAE,4BAA4B;AACrD,OAAO,KAAK,EACV,2CAA2C,EAC3C,+BAA+B,EAEhC,qCAAqC;AAKtC,OAAO,KAAK,EAAE,0BAA0B,EAAE,iEAA6D;AAGvG;;;;;;;;;;GAUG;AACH,MAAM,MAAM,iBAAiB,GAAG;IAC9B,eAAe,EAAE,MAAM,CAAC;IACxB,aAAa,EAAE,MAAM,CACnB,MAAM,EACN;QACE,cAAc,EAAE,MAAM,GAAG,IAAI,CAAC;QAC9B,cAAc,EAAE,MAAM,GAAG,IAAI,CAAC;QAC9B,iBAAiB,EAAE,MAAM,GAAG,IAAI,CAAC;KAClC,CACF,CAAC;CACH,CAAC;AAEF,QAAA,MAAM,IAAI,2BAA2B,CAAC;AAEtC,MAAM,MAAM,uBAAuB,GAAG,0BAA0B,CAC9D,OAAO,IAAI,EACX,iBAAiB,CAClB,CAAC;AAEF,MAAM,MAAM,4BAA4B,GAAG,uBAAuB,CAAC;AAEnE,MAAM,MAAM,oBAAoB,GAAG,wBAAwB,CACzD,OAAO,IAAI,EACX,iBAAiB,CAClB,CAAC;AAEF,MAAM,MAAM,6BAA6B,GAAG,oBAAoB,CAAC;AAEjE,KAAK,cAAc,GACf,2CAA2C,GAC3C,+BAA+B,CAAC;AAEpC,MAAM,MAAM,qBAAqB,GAAG,SAAS,CAC3C,OAAO,IAAI,EACX,6BAA6B,GAAG,cAAc,EAC9C,4BAA4B,CAC7B,CAAC;AA4BF,wEAAwE;AACxE,KAAK,wBAAwB,GAAG;IAC9B,gBAAgB,EAAE,MAAM,EAAE,CAAC;CAC5B,CAAC;;;;;;;;;;;;;;;;AAEF;;;GAGG;AACH,qBAAa,sBAAuB,SAAQ,4BAC1C,OAAO,IAAI,EACX,iBAAiB,EACjB,qBAAqB,CACtB;;IACC,OAAO,CAAC,QAAQ,CAAC,KAAK,CAAe;IAErC,OAAO,CAAC,QAAQ,CAAC,cAAc,CAAC;IAEhC,OAAO,CAAC,QAAQ,CAAC,mBAAmB,CAAgB;IAIpD;;;;;;;;;;OAUG;gBACS,EACV,cAAsB,EACtB,QAAiB,EACjB,mBAAgC,EAChC,SAAS,EACT,KAAK,EACL,kBAAkB,GACnB,EAAE;QACD,cAAc,CAAC,EAAE,OAAO,CAAC;QACzB,QAAQ,CAAC,EAAE,MAAM,CAAC;QAClB,SAAS,EAAE,qBAAqB,CAAC;QACjC,KAAK,CAAC,EAAE,OAAO,CAAC,iBAAiB,CAAC,CAAC;QACnC,mBAAmB,CAAC,EAAE,MAAM,OAAO,CAAC;QACpC,kBAAkB,EAAE,0BAA0B,CAAC;KAChD;IAaD;;;;OAIG;IACG,kBAAkB,CAAC,eAAe,EAAE,MAAM;
|
|
1
|
+
{"version":3,"file":"CurrencyRateController.d.mts","sourceRoot":"","sources":["../src/CurrencyRateController.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EACV,wBAAwB,EACxB,0BAA0B,EAE3B,kCAAkC;AAKnC,OAAO,KAAK,EAAE,SAAS,EAAE,4BAA4B;AACrD,OAAO,KAAK,EACV,2CAA2C,EAC3C,+BAA+B,EAEhC,qCAAqC;AAKtC,OAAO,KAAK,EAAE,0BAA0B,EAAE,iEAA6D;AAGvG;;;;;;;;;;GAUG;AACH,MAAM,MAAM,iBAAiB,GAAG;IAC9B,eAAe,EAAE,MAAM,CAAC;IACxB,aAAa,EAAE,MAAM,CACnB,MAAM,EACN;QACE,cAAc,EAAE,MAAM,GAAG,IAAI,CAAC;QAC9B,cAAc,EAAE,MAAM,GAAG,IAAI,CAAC;QAC9B,iBAAiB,EAAE,MAAM,GAAG,IAAI,CAAC;KAClC,CACF,CAAC;CACH,CAAC;AAEF,QAAA,MAAM,IAAI,2BAA2B,CAAC;AAEtC,MAAM,MAAM,uBAAuB,GAAG,0BAA0B,CAC9D,OAAO,IAAI,EACX,iBAAiB,CAClB,CAAC;AAEF,MAAM,MAAM,4BAA4B,GAAG,uBAAuB,CAAC;AAEnE,MAAM,MAAM,oBAAoB,GAAG,wBAAwB,CACzD,OAAO,IAAI,EACX,iBAAiB,CAClB,CAAC;AAEF,MAAM,MAAM,6BAA6B,GAAG,oBAAoB,CAAC;AAEjE,KAAK,cAAc,GACf,2CAA2C,GAC3C,+BAA+B,CAAC;AAEpC,MAAM,MAAM,qBAAqB,GAAG,SAAS,CAC3C,OAAO,IAAI,EACX,6BAA6B,GAAG,cAAc,EAC9C,4BAA4B,CAC7B,CAAC;AA4BF,wEAAwE;AACxE,KAAK,wBAAwB,GAAG;IAC9B,gBAAgB,EAAE,MAAM,EAAE,CAAC;CAC5B,CAAC;;;;;;;;;;;;;;;;AAEF;;;GAGG;AACH,qBAAa,sBAAuB,SAAQ,4BAC1C,OAAO,IAAI,EACX,iBAAiB,EACjB,qBAAqB,CACtB;;IACC,OAAO,CAAC,QAAQ,CAAC,KAAK,CAAe;IAErC,OAAO,CAAC,QAAQ,CAAC,cAAc,CAAC;IAEhC,OAAO,CAAC,QAAQ,CAAC,mBAAmB,CAAgB;IAIpD;;;;;;;;;;OAUG;gBACS,EACV,cAAsB,EACtB,QAAiB,EACjB,mBAAgC,EAChC,SAAS,EACT,KAAK,EACL,kBAAkB,GACnB,EAAE;QACD,cAAc,CAAC,EAAE,OAAO,CAAC;QACzB,QAAQ,CAAC,EAAE,MAAM,CAAC;QAClB,SAAS,EAAE,qBAAqB,CAAC;QACjC,KAAK,CAAC,EAAE,OAAO,CAAC,iBAAiB,CAAC,CAAC;QACnC,mBAAmB,CAAC,EAAE,MAAM,OAAO,CAAC;QACpC,kBAAkB,EAAE,0BAA0B,CAAC;KAChD;IAaD;;;;OAIG;IACG,kBAAkB,CAAC,eAAe,EAAE,MAAM;IAqKhD;;;;OAIG;IACG,kBAAkB,CACtB,gBAAgB,EAAE,CAAC,MAAM,GAAG,SAAS,CAAC,EAAE,GACvC,OAAO,CAAC,IAAI,CAAC;IA0ChB;;;;OAIG;IACM,OAAO;IAKhB;;;;;OAKG;IACG,YAAY,CAAC,EACjB,gBAAgB,GACjB,EAAE,wBAAwB,GAAG,OAAO,CAAC,IAAI,CAAC;CAG5C;AAED,eAAe,sBAAsB,CAAC"}
|
|
@@ -203,11 +203,11 @@ _CurrencyRateController_tokenPricesService = new WeakMap(), _CurrencyRateControl
|
|
|
203
203
|
const nativeTokenAddress = getNativeTokenAddress(chainId);
|
|
204
204
|
// Pass empty array as fetchTokenPrices automatically includes the native token address
|
|
205
205
|
const tokenPrices = await __classPrivateFieldGet(this, _CurrencyRateController_tokenPricesService, "f").fetchTokenPrices({
|
|
206
|
-
|
|
206
|
+
chainId,
|
|
207
|
+
tokenAddresses: [],
|
|
207
208
|
currency: currentCurrency,
|
|
208
209
|
});
|
|
209
|
-
const tokenPrice = tokenPrices
|
|
210
|
-
nativeTokenAddress.toLowerCase());
|
|
210
|
+
const tokenPrice = tokenPrices[nativeTokenAddress];
|
|
211
211
|
return {
|
|
212
212
|
nativeCurrency,
|
|
213
213
|
conversionDate: tokenPrice ? Date.now() / 1000 : null,
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"CurrencyRateController.mjs","sourceRoot":"","sources":["../src/CurrencyRateController.ts"],"names":[],"mappings":"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","sourcesContent":["import type {\n ControllerGetStateAction,\n ControllerStateChangeEvent,\n StateMetadata,\n} from '@metamask/base-controller';\nimport {\n TESTNET_TICKER_SYMBOLS,\n FALL_BACK_VS_CURRENCY,\n} from '@metamask/controller-utils';\nimport type { Messenger } from '@metamask/messenger';\nimport type {\n NetworkControllerGetNetworkClientByIdAction,\n NetworkControllerGetStateAction,\n NetworkConfiguration,\n} from '@metamask/network-controller';\nimport { StaticIntervalPollingController } from '@metamask/polling-controller';\nimport type { Hex } from '@metamask/utils';\nimport { Mutex } from 'async-mutex';\n\nimport type { AbstractTokenPricesService } from './token-prices-service/abstract-token-prices-service';\nimport { getNativeTokenAddress } from './token-prices-service/codefi-v2';\n\n/**\n * currencyRates - Object keyed by native currency\n *\n * currencyRates.conversionDate - Timestamp of conversion rate expressed in ms since UNIX epoch\n *\n * currencyRates.conversionRate - Conversion rate from current base asset to the current currency\n *\n * currentCurrency - Currently-active ISO 4217 currency code\n *\n * usdConversionRate - Conversion rate from usd to the current currency\n */\nexport type CurrencyRateState = {\n currentCurrency: string;\n currencyRates: Record<\n string,\n {\n conversionDate: number | null;\n conversionRate: number | null;\n usdConversionRate: number | null;\n }\n >;\n};\n\nconst name = 'CurrencyRateController';\n\nexport type CurrencyRateStateChange = ControllerStateChangeEvent<\n typeof name,\n CurrencyRateState\n>;\n\nexport type CurrencyRateControllerEvents = CurrencyRateStateChange;\n\nexport type GetCurrencyRateState = ControllerGetStateAction<\n typeof name,\n CurrencyRateState\n>;\n\nexport type CurrencyRateControllerActions = GetCurrencyRateState;\n\ntype AllowedActions =\n | NetworkControllerGetNetworkClientByIdAction\n | NetworkControllerGetStateAction;\n\nexport type CurrencyRateMessenger = Messenger<\n typeof name,\n CurrencyRateControllerActions | AllowedActions,\n CurrencyRateControllerEvents\n>;\n\nconst metadata: StateMetadata<CurrencyRateState> = {\n currentCurrency: {\n includeInStateLogs: true,\n persist: true,\n includeInDebugSnapshot: true,\n usedInUi: true,\n },\n currencyRates: {\n includeInStateLogs: true,\n persist: true,\n includeInDebugSnapshot: true,\n usedInUi: true,\n },\n};\n\nconst defaultState = {\n currentCurrency: 'usd',\n currencyRates: {\n ETH: {\n conversionDate: 0,\n conversionRate: 0,\n usdConversionRate: null,\n },\n },\n};\n\n/** The input to start polling for the {@link CurrencyRateController} */\ntype CurrencyRatePollingInput = {\n nativeCurrencies: string[];\n};\n\n/**\n * Controller that passively polls on a set interval for an exchange rate from the current network\n * asset to the user's preferred currency.\n */\nexport class CurrencyRateController extends StaticIntervalPollingController<CurrencyRatePollingInput>()<\n typeof name,\n CurrencyRateState,\n CurrencyRateMessenger\n> {\n private readonly mutex = new Mutex();\n\n private readonly includeUsdRate;\n\n private readonly useExternalServices: () => boolean;\n\n readonly #tokenPricesService: AbstractTokenPricesService;\n\n /**\n * Creates a CurrencyRateController instance.\n *\n * @param options - Constructor options.\n * @param options.includeUsdRate - Keep track of the USD rate in addition to the current currency rate.\n * @param options.interval - The polling interval, in milliseconds.\n * @param options.messenger - A reference to the messenger.\n * @param options.state - Initial state to set on this controller.\n * @param options.useExternalServices - Feature Switch for using external services (default: true)\n * @param options.tokenPricesService - An object in charge of retrieving token prices\n */\n constructor({\n includeUsdRate = false,\n interval = 180000,\n useExternalServices = () => true,\n messenger,\n state,\n tokenPricesService,\n }: {\n includeUsdRate?: boolean;\n interval?: number;\n messenger: CurrencyRateMessenger;\n state?: Partial<CurrencyRateState>;\n useExternalServices?: () => boolean;\n tokenPricesService: AbstractTokenPricesService;\n }) {\n super({\n name,\n metadata,\n messenger,\n state: { ...defaultState, ...state },\n });\n this.includeUsdRate = includeUsdRate;\n this.useExternalServices = useExternalServices;\n this.setIntervalLength(interval);\n this.#tokenPricesService = tokenPricesService;\n }\n\n /**\n * Sets a currency to track.\n *\n * @param currentCurrency - ISO 4217 currency code.\n */\n async setCurrentCurrency(currentCurrency: string) {\n const releaseLock = await this.mutex.acquire();\n const nativeCurrencies = Object.keys(this.state.currencyRates);\n try {\n this.update(() => {\n return {\n ...defaultState,\n currentCurrency,\n };\n });\n } finally {\n releaseLock();\n }\n // TODO: Either fix this lint violation or explain why it's necessary to ignore.\n // eslint-disable-next-line @typescript-eslint/no-floating-promises\n this.updateExchangeRate(nativeCurrencies);\n }\n\n async #fetchExchangeRatesWithFallback(\n nativeCurrenciesToFetch: Record<string, string>,\n ): Promise<CurrencyRateState['currencyRates']> {\n const { currentCurrency } = this.state;\n\n try {\n const priceApiExchangeRatesResponse =\n await this.#tokenPricesService.fetchExchangeRates({\n baseCurrency: currentCurrency,\n includeUsdRate: this.includeUsdRate,\n cryptocurrencies: [\n ...new Set(Object.values(nativeCurrenciesToFetch)),\n ],\n });\n\n const ratesPriceApi = Object.entries(nativeCurrenciesToFetch).reduce(\n (acc, [nativeCurrency, fetchedCurrency]) => {\n const rate =\n priceApiExchangeRatesResponse[fetchedCurrency.toLowerCase()];\n\n acc[nativeCurrency] = {\n conversionDate: rate !== undefined ? Date.now() / 1000 : null,\n conversionRate: rate?.value\n ? Number((1 / rate?.value).toFixed(2))\n : null,\n usdConversionRate: rate?.usd\n ? Number((1 / rate?.usd).toFixed(2))\n : null,\n };\n return acc;\n },\n {} as CurrencyRateState['currencyRates'],\n );\n return ratesPriceApi;\n } catch (error) {\n console.error('Failed to fetch exchange rates.', error);\n }\n\n // fallback using spot price from token prices service\n try {\n // Step 1: Get all network configurations to find matching chainIds for native currencies\n const networkControllerState = this.messenger.call(\n 'NetworkController:getState',\n );\n const networkConfigurations =\n networkControllerState.networkConfigurationsByChainId;\n\n // Step 2: Build a map of nativeCurrency -> chainId(s)\n const currencyToChainIds = Object.entries(nativeCurrenciesToFetch).reduce(\n (acc, [nativeCurrency, fetchedCurrency]) => {\n // Find the first chainId that has this native currency\n const matchingEntry = (\n Object.entries(networkConfigurations) as [\n Hex,\n NetworkConfiguration,\n ][]\n ).find(\n ([, config]) =>\n config.nativeCurrency.toUpperCase() ===\n fetchedCurrency.toUpperCase(),\n );\n\n if (matchingEntry) {\n acc[nativeCurrency] = {\n fetchedCurrency,\n chainId: matchingEntry[0],\n };\n }\n\n return acc;\n },\n {} as Record<string, { fetchedCurrency: string; chainId: Hex }>,\n );\n\n // Step 3: Fetch token prices for each chainId\n const currencyToChainIdsEntries = Object.entries(currencyToChainIds);\n const ratesResults = await Promise.allSettled(\n currencyToChainIdsEntries.map(async ([nativeCurrency, { chainId }]) => {\n const nativeTokenAddress = getNativeTokenAddress(chainId);\n // Pass empty array as fetchTokenPrices automatically includes the native token address\n const tokenPrices = await this.#tokenPricesService.fetchTokenPrices({\n assets: [{ chainId, tokenAddress: nativeTokenAddress }],\n currency: currentCurrency,\n });\n\n const tokenPrice = tokenPrices.find(\n (item) =>\n item.tokenAddress.toLowerCase() ===\n nativeTokenAddress.toLowerCase(),\n );\n\n return {\n nativeCurrency,\n conversionDate: tokenPrice ? Date.now() / 1000 : null,\n conversionRate: tokenPrice?.price ?? null,\n usdConversionRate: null, // Token prices service doesn't provide USD rate in this context\n };\n }),\n );\n const ratesFromTokenPrices = ratesResults.map((result, index) => {\n const [nativeCurrency, { chainId }] = currencyToChainIdsEntries[index];\n if (result.status === 'fulfilled') {\n return result.value;\n }\n console.error(\n `Failed to fetch token price for ${nativeCurrency} on chain ${chainId}`,\n result.reason,\n );\n return {\n nativeCurrency,\n conversionDate: null,\n conversionRate: null,\n usdConversionRate: null,\n };\n });\n\n // Step 4: Convert to the expected format\n const ratesFromTokenPricesService = ratesFromTokenPrices.reduce(\n (acc, rate) => {\n acc[rate.nativeCurrency] = {\n conversionDate: rate.conversionDate,\n conversionRate: rate.conversionRate,\n usdConversionRate: rate.usdConversionRate,\n };\n return acc;\n },\n {} as CurrencyRateState['currencyRates'],\n );\n\n return ratesFromTokenPricesService;\n } catch (error) {\n console.error(\n 'Failed to fetch exchange rates from token prices service.',\n error,\n );\n // Return null state for all requested currencies\n return Object.keys(nativeCurrenciesToFetch).reduce(\n (acc, nativeCurrency) => {\n acc[nativeCurrency] = {\n conversionDate: null,\n conversionRate: null,\n usdConversionRate: null,\n };\n return acc;\n },\n {} as CurrencyRateState['currencyRates'],\n );\n }\n }\n\n /**\n * Updates the exchange rate for the current currency and native currency pairs.\n *\n * @param nativeCurrencies - The native currency symbols to fetch exchange rates for.\n */\n async updateExchangeRate(\n nativeCurrencies: (string | undefined)[],\n ): Promise<void> {\n if (!this.useExternalServices()) {\n return;\n }\n\n const releaseLock = await this.mutex.acquire();\n try {\n // For preloaded testnets (Goerli, Sepolia) we want to fetch exchange rate for real ETH.\n // Map each native currency to the symbol we want to fetch for it.\n const testnetSymbols = Object.values(TESTNET_TICKER_SYMBOLS);\n const nativeCurrenciesToFetch = nativeCurrencies.reduce(\n (acc, nativeCurrency) => {\n if (!nativeCurrency) {\n return acc;\n }\n\n acc[nativeCurrency] = testnetSymbols.includes(nativeCurrency)\n ? FALL_BACK_VS_CURRENCY\n : nativeCurrency;\n return acc;\n },\n {} as Record<string, string>,\n );\n\n const rates = await this.#fetchExchangeRatesWithFallback(\n nativeCurrenciesToFetch,\n );\n\n this.update((state) => {\n state.currencyRates = {\n ...state.currencyRates,\n ...rates,\n };\n });\n } catch (error) {\n console.error('Failed to fetch exchange rates.', error);\n throw error;\n } finally {\n releaseLock();\n }\n }\n\n /**\n * Prepare to discard this controller.\n *\n * This stops any active polling.\n */\n override destroy() {\n super.destroy();\n this.stopAllPolling();\n }\n\n /**\n * Updates exchange rate for the current currency.\n *\n * @param input - The input for the poll.\n * @param input.nativeCurrencies - The native currency symbols to poll prices for.\n */\n async _executePoll({\n nativeCurrencies,\n }: CurrencyRatePollingInput): Promise<void> {\n await this.updateExchangeRate(nativeCurrencies);\n }\n}\n\nexport default CurrencyRateController;\n"]}
|
|
1
|
+
{"version":3,"file":"CurrencyRateController.mjs","sourceRoot":"","sources":["../src/CurrencyRateController.ts"],"names":[],"mappings":"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","sourcesContent":["import type {\n ControllerGetStateAction,\n ControllerStateChangeEvent,\n StateMetadata,\n} from '@metamask/base-controller';\nimport {\n TESTNET_TICKER_SYMBOLS,\n FALL_BACK_VS_CURRENCY,\n} from '@metamask/controller-utils';\nimport type { Messenger } from '@metamask/messenger';\nimport type {\n NetworkControllerGetNetworkClientByIdAction,\n NetworkControllerGetStateAction,\n NetworkConfiguration,\n} from '@metamask/network-controller';\nimport { StaticIntervalPollingController } from '@metamask/polling-controller';\nimport type { Hex } from '@metamask/utils';\nimport { Mutex } from 'async-mutex';\n\nimport type { AbstractTokenPricesService } from './token-prices-service/abstract-token-prices-service';\nimport { getNativeTokenAddress } from './token-prices-service/codefi-v2';\n\n/**\n * currencyRates - Object keyed by native currency\n *\n * currencyRates.conversionDate - Timestamp of conversion rate expressed in ms since UNIX epoch\n *\n * currencyRates.conversionRate - Conversion rate from current base asset to the current currency\n *\n * currentCurrency - Currently-active ISO 4217 currency code\n *\n * usdConversionRate - Conversion rate from usd to the current currency\n */\nexport type CurrencyRateState = {\n currentCurrency: string;\n currencyRates: Record<\n string,\n {\n conversionDate: number | null;\n conversionRate: number | null;\n usdConversionRate: number | null;\n }\n >;\n};\n\nconst name = 'CurrencyRateController';\n\nexport type CurrencyRateStateChange = ControllerStateChangeEvent<\n typeof name,\n CurrencyRateState\n>;\n\nexport type CurrencyRateControllerEvents = CurrencyRateStateChange;\n\nexport type GetCurrencyRateState = ControllerGetStateAction<\n typeof name,\n CurrencyRateState\n>;\n\nexport type CurrencyRateControllerActions = GetCurrencyRateState;\n\ntype AllowedActions =\n | NetworkControllerGetNetworkClientByIdAction\n | NetworkControllerGetStateAction;\n\nexport type CurrencyRateMessenger = Messenger<\n typeof name,\n CurrencyRateControllerActions | AllowedActions,\n CurrencyRateControllerEvents\n>;\n\nconst metadata: StateMetadata<CurrencyRateState> = {\n currentCurrency: {\n includeInStateLogs: true,\n persist: true,\n includeInDebugSnapshot: true,\n usedInUi: true,\n },\n currencyRates: {\n includeInStateLogs: true,\n persist: true,\n includeInDebugSnapshot: true,\n usedInUi: true,\n },\n};\n\nconst defaultState = {\n currentCurrency: 'usd',\n currencyRates: {\n ETH: {\n conversionDate: 0,\n conversionRate: 0,\n usdConversionRate: null,\n },\n },\n};\n\n/** The input to start polling for the {@link CurrencyRateController} */\ntype CurrencyRatePollingInput = {\n nativeCurrencies: string[];\n};\n\n/**\n * Controller that passively polls on a set interval for an exchange rate from the current network\n * asset to the user's preferred currency.\n */\nexport class CurrencyRateController extends StaticIntervalPollingController<CurrencyRatePollingInput>()<\n typeof name,\n CurrencyRateState,\n CurrencyRateMessenger\n> {\n private readonly mutex = new Mutex();\n\n private readonly includeUsdRate;\n\n private readonly useExternalServices: () => boolean;\n\n readonly #tokenPricesService: AbstractTokenPricesService;\n\n /**\n * Creates a CurrencyRateController instance.\n *\n * @param options - Constructor options.\n * @param options.includeUsdRate - Keep track of the USD rate in addition to the current currency rate.\n * @param options.interval - The polling interval, in milliseconds.\n * @param options.messenger - A reference to the messenger.\n * @param options.state - Initial state to set on this controller.\n * @param options.useExternalServices - Feature Switch for using external services (default: true)\n * @param options.tokenPricesService - An object in charge of retrieving token prices\n */\n constructor({\n includeUsdRate = false,\n interval = 180000,\n useExternalServices = () => true,\n messenger,\n state,\n tokenPricesService,\n }: {\n includeUsdRate?: boolean;\n interval?: number;\n messenger: CurrencyRateMessenger;\n state?: Partial<CurrencyRateState>;\n useExternalServices?: () => boolean;\n tokenPricesService: AbstractTokenPricesService;\n }) {\n super({\n name,\n metadata,\n messenger,\n state: { ...defaultState, ...state },\n });\n this.includeUsdRate = includeUsdRate;\n this.useExternalServices = useExternalServices;\n this.setIntervalLength(interval);\n this.#tokenPricesService = tokenPricesService;\n }\n\n /**\n * Sets a currency to track.\n *\n * @param currentCurrency - ISO 4217 currency code.\n */\n async setCurrentCurrency(currentCurrency: string) {\n const releaseLock = await this.mutex.acquire();\n const nativeCurrencies = Object.keys(this.state.currencyRates);\n try {\n this.update(() => {\n return {\n ...defaultState,\n currentCurrency,\n };\n });\n } finally {\n releaseLock();\n }\n // TODO: Either fix this lint violation or explain why it's necessary to ignore.\n // eslint-disable-next-line @typescript-eslint/no-floating-promises\n this.updateExchangeRate(nativeCurrencies);\n }\n\n async #fetchExchangeRatesWithFallback(\n nativeCurrenciesToFetch: Record<string, string>,\n ): Promise<CurrencyRateState['currencyRates']> {\n const { currentCurrency } = this.state;\n\n try {\n const priceApiExchangeRatesResponse =\n await this.#tokenPricesService.fetchExchangeRates({\n baseCurrency: currentCurrency,\n includeUsdRate: this.includeUsdRate,\n cryptocurrencies: [\n ...new Set(Object.values(nativeCurrenciesToFetch)),\n ],\n });\n\n const ratesPriceApi = Object.entries(nativeCurrenciesToFetch).reduce(\n (acc, [nativeCurrency, fetchedCurrency]) => {\n const rate =\n priceApiExchangeRatesResponse[fetchedCurrency.toLowerCase()];\n\n acc[nativeCurrency] = {\n conversionDate: rate !== undefined ? Date.now() / 1000 : null,\n conversionRate: rate?.value\n ? Number((1 / rate?.value).toFixed(2))\n : null,\n usdConversionRate: rate?.usd\n ? Number((1 / rate?.usd).toFixed(2))\n : null,\n };\n return acc;\n },\n {} as CurrencyRateState['currencyRates'],\n );\n return ratesPriceApi;\n } catch (error) {\n console.error('Failed to fetch exchange rates.', error);\n }\n\n // fallback using spot price from token prices service\n try {\n // Step 1: Get all network configurations to find matching chainIds for native currencies\n const networkControllerState = this.messenger.call(\n 'NetworkController:getState',\n );\n const networkConfigurations =\n networkControllerState.networkConfigurationsByChainId;\n\n // Step 2: Build a map of nativeCurrency -> chainId(s)\n const currencyToChainIds = Object.entries(nativeCurrenciesToFetch).reduce(\n (acc, [nativeCurrency, fetchedCurrency]) => {\n // Find the first chainId that has this native currency\n const matchingEntry = (\n Object.entries(networkConfigurations) as [\n Hex,\n NetworkConfiguration,\n ][]\n ).find(\n ([, config]) =>\n config.nativeCurrency.toUpperCase() ===\n fetchedCurrency.toUpperCase(),\n );\n\n if (matchingEntry) {\n acc[nativeCurrency] = {\n fetchedCurrency,\n chainId: matchingEntry[0],\n };\n }\n\n return acc;\n },\n {} as Record<string, { fetchedCurrency: string; chainId: Hex }>,\n );\n\n // Step 3: Fetch token prices for each chainId\n const currencyToChainIdsEntries = Object.entries(currencyToChainIds);\n const ratesResults = await Promise.allSettled(\n currencyToChainIdsEntries.map(async ([nativeCurrency, { chainId }]) => {\n const nativeTokenAddress = getNativeTokenAddress(chainId);\n // Pass empty array as fetchTokenPrices automatically includes the native token address\n const tokenPrices = await this.#tokenPricesService.fetchTokenPrices({\n chainId,\n tokenAddresses: [],\n currency: currentCurrency,\n });\n\n const tokenPrice = tokenPrices[nativeTokenAddress];\n\n return {\n nativeCurrency,\n conversionDate: tokenPrice ? Date.now() / 1000 : null,\n conversionRate: tokenPrice?.price ?? null,\n usdConversionRate: null, // Token prices service doesn't provide USD rate in this context\n };\n }),\n );\n const ratesFromTokenPrices = ratesResults.map((result, index) => {\n const [nativeCurrency, { chainId }] = currencyToChainIdsEntries[index];\n if (result.status === 'fulfilled') {\n return result.value;\n }\n console.error(\n `Failed to fetch token price for ${nativeCurrency} on chain ${chainId}`,\n result.reason,\n );\n return {\n nativeCurrency,\n conversionDate: null,\n conversionRate: null,\n usdConversionRate: null,\n };\n });\n\n // Step 4: Convert to the expected format\n const ratesFromTokenPricesService = ratesFromTokenPrices.reduce(\n (acc, rate) => {\n acc[rate.nativeCurrency] = {\n conversionDate: rate.conversionDate,\n conversionRate: rate.conversionRate,\n usdConversionRate: rate.usdConversionRate,\n };\n return acc;\n },\n {} as CurrencyRateState['currencyRates'],\n );\n\n return ratesFromTokenPricesService;\n } catch (error) {\n console.error(\n 'Failed to fetch exchange rates from token prices service.',\n error,\n );\n // Return null state for all requested currencies\n return Object.keys(nativeCurrenciesToFetch).reduce(\n (acc, nativeCurrency) => {\n acc[nativeCurrency] = {\n conversionDate: null,\n conversionRate: null,\n usdConversionRate: null,\n };\n return acc;\n },\n {} as CurrencyRateState['currencyRates'],\n );\n }\n }\n\n /**\n * Updates the exchange rate for the current currency and native currency pairs.\n *\n * @param nativeCurrencies - The native currency symbols to fetch exchange rates for.\n */\n async updateExchangeRate(\n nativeCurrencies: (string | undefined)[],\n ): Promise<void> {\n if (!this.useExternalServices()) {\n return;\n }\n\n const releaseLock = await this.mutex.acquire();\n try {\n // For preloaded testnets (Goerli, Sepolia) we want to fetch exchange rate for real ETH.\n // Map each native currency to the symbol we want to fetch for it.\n const testnetSymbols = Object.values(TESTNET_TICKER_SYMBOLS);\n const nativeCurrenciesToFetch = nativeCurrencies.reduce(\n (acc, nativeCurrency) => {\n if (!nativeCurrency) {\n return acc;\n }\n\n acc[nativeCurrency] = testnetSymbols.includes(nativeCurrency)\n ? FALL_BACK_VS_CURRENCY\n : nativeCurrency;\n return acc;\n },\n {} as Record<string, string>,\n );\n\n const rates = await this.#fetchExchangeRatesWithFallback(\n nativeCurrenciesToFetch,\n );\n\n this.update((state) => {\n state.currencyRates = {\n ...state.currencyRates,\n ...rates,\n };\n });\n } catch (error) {\n console.error('Failed to fetch exchange rates.', error);\n throw error;\n } finally {\n releaseLock();\n }\n }\n\n /**\n * Prepare to discard this controller.\n *\n * This stops any active polling.\n */\n override destroy() {\n super.destroy();\n this.stopAllPolling();\n }\n\n /**\n * Updates exchange rate for the current currency.\n *\n * @param input - The input for the poll.\n * @param input.nativeCurrencies - The native currency symbols to poll prices for.\n */\n async _executePoll({\n nativeCurrencies,\n }: CurrencyRatePollingInput): Promise<void> {\n await this.updateExchangeRate(nativeCurrencies);\n }\n}\n\nexport default CurrencyRateController;\n"]}
|
|
@@ -1,18 +1,19 @@
|
|
|
1
1
|
"use strict";
|
|
2
|
+
var __classPrivateFieldGet = (this && this.__classPrivateFieldGet) || function (receiver, state, kind, f) {
|
|
3
|
+
if (kind === "a" && !f) throw new TypeError("Private accessor was defined without a getter");
|
|
4
|
+
if (typeof state === "function" ? receiver !== state || !f : !state.has(receiver)) throw new TypeError("Cannot read private member from an object whose class did not declare it");
|
|
5
|
+
return kind === "m" ? f : kind === "a" ? f.call(receiver) : f ? f.value : state.get(receiver);
|
|
6
|
+
};
|
|
2
7
|
var __classPrivateFieldSet = (this && this.__classPrivateFieldSet) || function (receiver, state, value, kind, f) {
|
|
3
8
|
if (kind === "m") throw new TypeError("Private method is not writable");
|
|
4
9
|
if (kind === "a" && !f) throw new TypeError("Private accessor was defined without a setter");
|
|
5
10
|
if (typeof state === "function" ? receiver !== state || !f : !state.has(receiver)) throw new TypeError("Cannot write private member to an object whose class did not declare it");
|
|
6
11
|
return (kind === "a" ? f.call(receiver, value) : f ? f.value = value : state.set(receiver, value)), value;
|
|
7
12
|
};
|
|
8
|
-
var
|
|
9
|
-
if (kind === "a" && !f) throw new TypeError("Private accessor was defined without a getter");
|
|
10
|
-
if (typeof state === "function" ? receiver !== state || !f : !state.has(receiver)) throw new TypeError("Cannot read private member from an object whose class did not declare it");
|
|
11
|
-
return kind === "m" ? f : kind === "a" ? f.call(receiver) : f ? f.value : state.get(receiver);
|
|
12
|
-
};
|
|
13
|
-
var _TokenBalancesController_instances, _TokenBalancesController_platform, _TokenBalancesController_queryAllAccounts, _TokenBalancesController_accountsApiChainIds, _TokenBalancesController_balanceFetchers, _TokenBalancesController_allTokens, _TokenBalancesController_detectedTokens, _TokenBalancesController_allIgnoredTokens, _TokenBalancesController_defaultInterval, _TokenBalancesController_websocketActivePollingInterval, _TokenBalancesController_chainPollingConfig, _TokenBalancesController_intervalPollingTimers, _TokenBalancesController_isControllerPollingActive, _TokenBalancesController_requestedChainIds, _TokenBalancesController_statusChangeDebouncer, _TokenBalancesController_chainIdsWithTokens, _TokenBalancesController_getProvider, _TokenBalancesController_getNetworkClient, _TokenBalancesController_createAccountsApiFetcher, _TokenBalancesController_startIntervalGroupPolling, _TokenBalancesController_startPollingForInterval, _TokenBalancesController_setPollingTimer, _TokenBalancesController_isTokenTracked, _TokenBalancesController_onTokensChanged, _TokenBalancesController_onNetworkChanged, _TokenBalancesController_onAccountRemoved, _TokenBalancesController_prepareBalanceUpdates, _TokenBalancesController_onAccountActivityBalanceUpdate, _TokenBalancesController_onAccountActivityStatusChanged, _TokenBalancesController_processAccumulatedStatusChanges;
|
|
13
|
+
var _TokenBalancesController_instances, _TokenBalancesController_platform, _TokenBalancesController_queryAllAccounts, _TokenBalancesController_accountsApiChainIds, _TokenBalancesController_balanceFetchers, _TokenBalancesController_allTokens, _TokenBalancesController_detectedTokens, _TokenBalancesController_allIgnoredTokens, _TokenBalancesController_defaultInterval, _TokenBalancesController_websocketActivePollingInterval, _TokenBalancesController_chainPollingConfig, _TokenBalancesController_intervalPollingTimers, _TokenBalancesController_isControllerPollingActive, _TokenBalancesController_requestedChainIds, _TokenBalancesController_statusChangeDebouncer, _TokenBalancesController_normalizeAccountAddresses, _TokenBalancesController_chainIdsWithTokens, _TokenBalancesController_getProvider, _TokenBalancesController_getNetworkClient, _TokenBalancesController_createAccountsApiFetcher, _TokenBalancesController_startIntervalGroupPolling, _TokenBalancesController_startPollingForInterval, _TokenBalancesController_setPollingTimer, _TokenBalancesController_isTokenTracked, _TokenBalancesController_onTokensChanged, _TokenBalancesController_onNetworkChanged, _TokenBalancesController_onAccountRemoved, _TokenBalancesController_prepareBalanceUpdates, _TokenBalancesController_onAccountActivityBalanceUpdate, _TokenBalancesController_onAccountActivityStatusChanged, _TokenBalancesController_processAccumulatedStatusChanges;
|
|
14
14
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
15
15
|
exports.TokenBalancesController = exports.parseAssetType = exports.caipChainIdToHex = void 0;
|
|
16
|
+
/* eslint-disable @typescript-eslint/no-explicit-any */
|
|
16
17
|
const providers_1 = require("@ethersproject/providers");
|
|
17
18
|
const controller_utils_1 = require("@metamask/controller-utils");
|
|
18
19
|
const polling_controller_1 = require("@metamask/polling-controller");
|
|
@@ -258,7 +259,8 @@ class TokenBalancesController extends (0, polling_controller_1.StaticIntervalPol
|
|
|
258
259
|
return;
|
|
259
260
|
}
|
|
260
261
|
this.update((s) => {
|
|
261
|
-
|
|
262
|
+
const lowercaseAddr = addr.toLowerCase();
|
|
263
|
+
delete s.tokenBalances[lowercaseAddr];
|
|
262
264
|
});
|
|
263
265
|
});
|
|
264
266
|
// ────────────────────────────────────────────────────────────────────────────
|
|
@@ -337,6 +339,8 @@ class TokenBalancesController extends (0, polling_controller_1.StaticIntervalPol
|
|
|
337
339
|
__classPrivateFieldGet(this, _TokenBalancesController_instances, "m", _TokenBalancesController_processAccumulatedStatusChanges).call(this);
|
|
338
340
|
}, 5000); // 5-second debounce window
|
|
339
341
|
});
|
|
342
|
+
// Normalize all account addresses to lowercase in existing state
|
|
343
|
+
__classPrivateFieldGet(this, _TokenBalancesController_instances, "m", _TokenBalancesController_normalizeAccountAddresses).call(this);
|
|
340
344
|
__classPrivateFieldSet(this, _TokenBalancesController_platform, platform ?? 'extension', "f");
|
|
341
345
|
__classPrivateFieldSet(this, _TokenBalancesController_queryAllAccounts, queryMultipleAccounts, "f");
|
|
342
346
|
__classPrivateFieldSet(this, _TokenBalancesController_accountsApiChainIds, accountsApiChainIds, "f");
|
|
@@ -370,7 +374,11 @@ class TokenBalancesController extends (0, polling_controller_1.StaticIntervalPol
|
|
|
370
374
|
this.messenger.registerActionHandler(`TokenBalancesController:updateChainPollingConfigs`, this.updateChainPollingConfigs.bind(this));
|
|
371
375
|
this.messenger.registerActionHandler(`TokenBalancesController:getChainPollingConfig`, this.getChainPollingConfig.bind(this));
|
|
372
376
|
// Subscribe to AccountActivityService balance updates for real-time updates
|
|
373
|
-
this.messenger.subscribe('AccountActivityService:balanceUpdated',
|
|
377
|
+
this.messenger.subscribe('AccountActivityService:balanceUpdated', (event) => {
|
|
378
|
+
__classPrivateFieldGet(this, _TokenBalancesController_onAccountActivityBalanceUpdate, "f").call(this, event).catch((error) => {
|
|
379
|
+
console.warn('Error handling balance update:', error);
|
|
380
|
+
});
|
|
381
|
+
});
|
|
374
382
|
// Subscribe to AccountActivityService status changes for dynamic polling management
|
|
375
383
|
this.messenger.subscribe('AccountActivityService:statusChanged', __classPrivateFieldGet(this, _TokenBalancesController_onAccountActivityStatusChanged, "f").bind(this));
|
|
376
384
|
}
|
|
@@ -502,32 +510,35 @@ class TokenBalancesController extends (0, polling_controller_1.StaticIntervalPol
|
|
|
502
510
|
: [selected];
|
|
503
511
|
const prev = this.state;
|
|
504
512
|
const next = draft(prev, (d) => {
|
|
505
|
-
var _a, _b;
|
|
513
|
+
var _a, _b, _c;
|
|
506
514
|
// Initialize account and chain structures if they don't exist, but preserve existing balances
|
|
507
515
|
for (const chainId of targetChains) {
|
|
508
516
|
for (const account of accountsToProcess) {
|
|
517
|
+
const lowercaseAccount = account.toLowerCase();
|
|
509
518
|
// Ensure the nested structure exists without overwriting existing balances
|
|
510
|
-
(_a = d.tokenBalances)[
|
|
511
|
-
(
|
|
519
|
+
(_a = d.tokenBalances)[_b = lowercaseAccount] ?? (_a[_b] = {});
|
|
520
|
+
(_c = d.tokenBalances[lowercaseAccount])[chainId] ?? (_c[chainId] = {});
|
|
512
521
|
// Initialize tokens from allTokens only if they don't exist yet
|
|
513
522
|
const chainTokens = __classPrivateFieldGet(this, _TokenBalancesController_allTokens, "f")[chainId];
|
|
514
|
-
if (chainTokens?.[
|
|
515
|
-
Object.values(chainTokens[
|
|
523
|
+
if (chainTokens?.[lowercaseAccount]) {
|
|
524
|
+
Object.values(chainTokens[lowercaseAccount]).forEach((token) => {
|
|
516
525
|
const tokenAddress = checksum(token.address);
|
|
517
526
|
// Only initialize if the token balance doesn't exist yet
|
|
518
|
-
if (!(tokenAddress in
|
|
519
|
-
d.tokenBalances[
|
|
527
|
+
if (!(tokenAddress in
|
|
528
|
+
d.tokenBalances[lowercaseAccount][chainId])) {
|
|
529
|
+
d.tokenBalances[lowercaseAccount][chainId][tokenAddress] = '0x0';
|
|
520
530
|
}
|
|
521
531
|
});
|
|
522
532
|
}
|
|
523
533
|
// Initialize tokens from allDetectedTokens only if they don't exist yet
|
|
524
534
|
const detectedChainTokens = __classPrivateFieldGet(this, _TokenBalancesController_detectedTokens, "f")[chainId];
|
|
525
|
-
if (detectedChainTokens?.[
|
|
526
|
-
Object.values(detectedChainTokens[
|
|
535
|
+
if (detectedChainTokens?.[lowercaseAccount]) {
|
|
536
|
+
Object.values(detectedChainTokens[lowercaseAccount]).forEach((token) => {
|
|
527
537
|
const tokenAddress = checksum(token.address);
|
|
528
538
|
// Only initialize if the token balance doesn't exist yet
|
|
529
|
-
if (!(tokenAddress in
|
|
530
|
-
d.tokenBalances[
|
|
539
|
+
if (!(tokenAddress in
|
|
540
|
+
d.tokenBalances[lowercaseAccount][chainId])) {
|
|
541
|
+
d.tokenBalances[lowercaseAccount][chainId][tokenAddress] = '0x0';
|
|
531
542
|
}
|
|
532
543
|
});
|
|
533
544
|
}
|
|
@@ -537,12 +548,13 @@ class TokenBalancesController extends (0, polling_controller_1.StaticIntervalPol
|
|
|
537
548
|
aggregated.forEach(({ success, value, account, token, chainId }) => {
|
|
538
549
|
var _a, _b, _c;
|
|
539
550
|
if (success && value !== undefined) {
|
|
551
|
+
const lowercaseAccount = account.toLowerCase();
|
|
540
552
|
const newBalance = (0, controller_utils_1.toHex)(value);
|
|
541
553
|
const tokenAddress = checksum(token);
|
|
542
|
-
const currentBalance = d.tokenBalances[
|
|
554
|
+
const currentBalance = d.tokenBalances[lowercaseAccount]?.[chainId]?.[tokenAddress];
|
|
543
555
|
// Only update if the balance has actually changed
|
|
544
556
|
if (currentBalance !== newBalance) {
|
|
545
|
-
((_c = ((_a = d.tokenBalances)[_b =
|
|
557
|
+
((_c = ((_a = d.tokenBalances)[_b = lowercaseAccount] ?? (_a[_b] = {})))[chainId] ?? (_c[chainId] = {}))[tokenAddress] = newBalance;
|
|
546
558
|
}
|
|
547
559
|
}
|
|
548
560
|
});
|
|
@@ -619,7 +631,37 @@ class TokenBalancesController extends (0, polling_controller_1.StaticIntervalPol
|
|
|
619
631
|
}
|
|
620
632
|
}
|
|
621
633
|
exports.TokenBalancesController = TokenBalancesController;
|
|
622
|
-
_TokenBalancesController_platform = new WeakMap(), _TokenBalancesController_queryAllAccounts = new WeakMap(), _TokenBalancesController_accountsApiChainIds = new WeakMap(), _TokenBalancesController_balanceFetchers = new WeakMap(), _TokenBalancesController_allTokens = new WeakMap(), _TokenBalancesController_detectedTokens = new WeakMap(), _TokenBalancesController_allIgnoredTokens = new WeakMap(), _TokenBalancesController_defaultInterval = new WeakMap(), _TokenBalancesController_websocketActivePollingInterval = new WeakMap(), _TokenBalancesController_chainPollingConfig = new WeakMap(), _TokenBalancesController_intervalPollingTimers = new WeakMap(), _TokenBalancesController_isControllerPollingActive = new WeakMap(), _TokenBalancesController_requestedChainIds = new WeakMap(), _TokenBalancesController_statusChangeDebouncer = new WeakMap(), _TokenBalancesController_getProvider = new WeakMap(), _TokenBalancesController_getNetworkClient = new WeakMap(), _TokenBalancesController_createAccountsApiFetcher = new WeakMap(), _TokenBalancesController_onTokensChanged = new WeakMap(), _TokenBalancesController_onNetworkChanged = new WeakMap(), _TokenBalancesController_onAccountRemoved = new WeakMap(), _TokenBalancesController_onAccountActivityBalanceUpdate = new WeakMap(), _TokenBalancesController_onAccountActivityStatusChanged = new WeakMap(), _TokenBalancesController_instances = new WeakSet(),
|
|
634
|
+
_TokenBalancesController_platform = new WeakMap(), _TokenBalancesController_queryAllAccounts = new WeakMap(), _TokenBalancesController_accountsApiChainIds = new WeakMap(), _TokenBalancesController_balanceFetchers = new WeakMap(), _TokenBalancesController_allTokens = new WeakMap(), _TokenBalancesController_detectedTokens = new WeakMap(), _TokenBalancesController_allIgnoredTokens = new WeakMap(), _TokenBalancesController_defaultInterval = new WeakMap(), _TokenBalancesController_websocketActivePollingInterval = new WeakMap(), _TokenBalancesController_chainPollingConfig = new WeakMap(), _TokenBalancesController_intervalPollingTimers = new WeakMap(), _TokenBalancesController_isControllerPollingActive = new WeakMap(), _TokenBalancesController_requestedChainIds = new WeakMap(), _TokenBalancesController_statusChangeDebouncer = new WeakMap(), _TokenBalancesController_getProvider = new WeakMap(), _TokenBalancesController_getNetworkClient = new WeakMap(), _TokenBalancesController_createAccountsApiFetcher = new WeakMap(), _TokenBalancesController_onTokensChanged = new WeakMap(), _TokenBalancesController_onNetworkChanged = new WeakMap(), _TokenBalancesController_onAccountRemoved = new WeakMap(), _TokenBalancesController_onAccountActivityBalanceUpdate = new WeakMap(), _TokenBalancesController_onAccountActivityStatusChanged = new WeakMap(), _TokenBalancesController_instances = new WeakSet(), _TokenBalancesController_normalizeAccountAddresses = function _TokenBalancesController_normalizeAccountAddresses() {
|
|
635
|
+
const currentState = this.state.tokenBalances;
|
|
636
|
+
const normalizedBalances = {};
|
|
637
|
+
// Iterate through all accounts and normalize to lowercase
|
|
638
|
+
for (const address of Object.keys(currentState)) {
|
|
639
|
+
const lowercaseAddress = address.toLowerCase();
|
|
640
|
+
const accountBalances = currentState[address];
|
|
641
|
+
if (!accountBalances) {
|
|
642
|
+
continue;
|
|
643
|
+
}
|
|
644
|
+
// If this lowercase address doesn't exist yet, create it
|
|
645
|
+
if (!normalizedBalances[lowercaseAddress]) {
|
|
646
|
+
normalizedBalances[lowercaseAddress] = {};
|
|
647
|
+
}
|
|
648
|
+
// Merge chain data
|
|
649
|
+
for (const chainId of Object.keys(accountBalances)) {
|
|
650
|
+
const chainIdKey = chainId;
|
|
651
|
+
if (!normalizedBalances[lowercaseAddress][chainIdKey]) {
|
|
652
|
+
normalizedBalances[lowercaseAddress][chainIdKey] = {};
|
|
653
|
+
}
|
|
654
|
+
// Merge token balances (later values override earlier ones if duplicates exist)
|
|
655
|
+
Object.assign(normalizedBalances[lowercaseAddress][chainIdKey], accountBalances[chainIdKey]);
|
|
656
|
+
}
|
|
657
|
+
}
|
|
658
|
+
// Only update if there were changes
|
|
659
|
+
if (Object.keys(currentState).length !==
|
|
660
|
+
Object.keys(normalizedBalances).length ||
|
|
661
|
+
Object.keys(currentState).some((addr) => addr !== addr.toLowerCase())) {
|
|
662
|
+
this.update(() => ({ tokenBalances: normalizedBalances }));
|
|
663
|
+
}
|
|
664
|
+
}, _TokenBalancesController_chainIdsWithTokens = function _TokenBalancesController_chainIdsWithTokens() {
|
|
623
665
|
return [
|
|
624
666
|
...new Set([
|
|
625
667
|
...Object.keys(__classPrivateFieldGet(this, _TokenBalancesController_allTokens, "f")),
|