@metamask-previews/assets-controllers 88.0.0-preview-81857e91 → 88.0.0-preview-c039a3f
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 -9
- package/dist/CurrencyRateController.cjs +65 -1
- package/dist/CurrencyRateController.cjs.map +1 -1
- package/dist/CurrencyRateController.d.cts +2 -2
- package/dist/CurrencyRateController.d.cts.map +1 -1
- package/dist/CurrencyRateController.d.mts +2 -2
- package/dist/CurrencyRateController.d.mts.map +1 -1
- package/dist/CurrencyRateController.mjs +65 -1
- package/dist/CurrencyRateController.mjs.map +1 -1
- package/dist/TokenBalancesController.cjs +0 -4
- package/dist/TokenBalancesController.cjs.map +1 -1
- package/dist/TokenBalancesController.d.cts +1 -2
- package/dist/TokenBalancesController.d.cts.map +1 -1
- package/dist/TokenBalancesController.d.mts +1 -2
- package/dist/TokenBalancesController.d.mts.map +1 -1
- package/dist/TokenBalancesController.mjs +1 -5
- package/dist/TokenBalancesController.mjs.map +1 -1
- package/dist/TokenDetectionController.cjs +6 -11
- package/dist/TokenDetectionController.cjs.map +1 -1
- package/dist/TokenDetectionController.d.cts +1 -2
- package/dist/TokenDetectionController.d.cts.map +1 -1
- package/dist/TokenDetectionController.d.mts +1 -2
- package/dist/TokenDetectionController.d.mts.map +1 -1
- package/dist/TokenDetectionController.mjs +6 -11
- package/dist/TokenDetectionController.mjs.map +1 -1
- package/dist/TokenRatesController.cjs +79 -52
- package/dist/TokenRatesController.cjs.map +1 -1
- package/dist/TokenRatesController.d.cts.map +1 -1
- package/dist/TokenRatesController.d.mts.map +1 -1
- package/dist/TokenRatesController.mjs +80 -53
- package/dist/TokenRatesController.mjs.map +1 -1
- package/dist/multi-chain-accounts-service/api-balance-fetcher.cjs +5 -5
- package/dist/multi-chain-accounts-service/api-balance-fetcher.cjs.map +1 -1
- package/dist/multi-chain-accounts-service/api-balance-fetcher.d.cts +1 -2
- package/dist/multi-chain-accounts-service/api-balance-fetcher.d.cts.map +1 -1
- package/dist/multi-chain-accounts-service/api-balance-fetcher.d.mts +1 -2
- package/dist/multi-chain-accounts-service/api-balance-fetcher.d.mts.map +1 -1
- package/dist/multi-chain-accounts-service/api-balance-fetcher.mjs +5 -5
- package/dist/multi-chain-accounts-service/api-balance-fetcher.mjs.map +1 -1
- package/dist/multi-chain-accounts-service/multi-chain-accounts.cjs +8 -18
- package/dist/multi-chain-accounts-service/multi-chain-accounts.cjs.map +1 -1
- package/dist/multi-chain-accounts-service/multi-chain-accounts.d.cts +2 -4
- package/dist/multi-chain-accounts-service/multi-chain-accounts.d.cts.map +1 -1
- package/dist/multi-chain-accounts-service/multi-chain-accounts.d.mts +2 -4
- package/dist/multi-chain-accounts-service/multi-chain-accounts.d.mts.map +1 -1
- package/dist/multi-chain-accounts-service/multi-chain-accounts.mjs +8 -18
- package/dist/multi-chain-accounts-service/multi-chain-accounts.mjs.map +1 -1
- package/package.json +2 -4
package/CHANGELOG.md
CHANGED
|
@@ -7,15 +7,6 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
|
|
7
7
|
|
|
8
8
|
## [Unreleased]
|
|
9
9
|
|
|
10
|
-
### Added
|
|
11
|
-
|
|
12
|
-
- Add optional JWT token authentication to multi-chain accounts API calls ([#7165](https://github.com/MetaMask/core/pull/7165))
|
|
13
|
-
- `fetchMultiChainBalances` and `fetchMultiChainBalancesV4` now accept an optional `jwtToken` parameter
|
|
14
|
-
- `TokenDetectionController` fetches and passes JWT token from `AuthenticationController` when using Accounts API
|
|
15
|
-
- `TokenBalancesController` fetches and passes JWT token through balance fetcher chain
|
|
16
|
-
- JWT token is included in `Authorization: Bearer <token>` header when provided
|
|
17
|
-
- Backward compatible: token parameter is optional and APIs work without authentication
|
|
18
|
-
|
|
19
10
|
### Fixed
|
|
20
11
|
|
|
21
12
|
- Enable RPC fallback when Accounts API fails or times out in `TokenBalancesController` ([#7155](https://github.com/MetaMask/core/pull/7155))
|
|
@@ -17,6 +17,7 @@ const controller_utils_1 = require("@metamask/controller-utils");
|
|
|
17
17
|
const polling_controller_1 = require("@metamask/polling-controller");
|
|
18
18
|
const async_mutex_1 = require("async-mutex");
|
|
19
19
|
const crypto_compare_service_1 = require("./crypto-compare-service/index.cjs");
|
|
20
|
+
const codefi_v2_1 = require("./token-prices-service/codefi-v2.cjs");
|
|
20
21
|
const name = 'CurrencyRateController';
|
|
21
22
|
const metadata = {
|
|
22
23
|
currentCurrency: {
|
|
@@ -185,7 +186,70 @@ _CurrencyRateController_tokenPricesService = new WeakMap(), _CurrencyRateControl
|
|
|
185
186
|
catch (error) {
|
|
186
187
|
console.error('Failed to fetch exchange rates.', error);
|
|
187
188
|
}
|
|
188
|
-
// fallback
|
|
189
|
+
// fallback using spot price from token prices service
|
|
190
|
+
try {
|
|
191
|
+
// Step 1: Get all network configurations to find matching chainIds for native currencies
|
|
192
|
+
const networkControllerState = this.messenger.call('NetworkController:getState');
|
|
193
|
+
const networkConfigurations = networkControllerState.networkConfigurationsByChainId;
|
|
194
|
+
// Step 2: Build a map of nativeCurrency -> chainId(s)
|
|
195
|
+
const currencyToChainIds = Object.entries(nativeCurrenciesToFetch).reduce((acc, [nativeCurrency, fetchedCurrency]) => {
|
|
196
|
+
// Find chainIds that have this native currency
|
|
197
|
+
const chainIds = Object.entries(networkConfigurations)
|
|
198
|
+
.filter(([, config]) => config.nativeCurrency.toUpperCase() ===
|
|
199
|
+
fetchedCurrency.toUpperCase())
|
|
200
|
+
.map(([chainId]) => chainId);
|
|
201
|
+
if (chainIds.length > 0) {
|
|
202
|
+
acc[nativeCurrency] = {
|
|
203
|
+
fetchedCurrency,
|
|
204
|
+
chainId: chainIds[0], // Use the first matching chainId
|
|
205
|
+
};
|
|
206
|
+
}
|
|
207
|
+
return acc;
|
|
208
|
+
}, {});
|
|
209
|
+
// Step 3: Fetch token prices for each chainId
|
|
210
|
+
const ratesFromTokenPrices = await Promise.all(Object.entries(currencyToChainIds).map(async ([nativeCurrency, { chainId }]) => {
|
|
211
|
+
try {
|
|
212
|
+
const nativeTokenAddress = (0, codefi_v2_1.getNativeTokenAddress)(chainId);
|
|
213
|
+
const tokenPrices = await __classPrivateFieldGet(this, _CurrencyRateController_tokenPricesService, "f").fetchTokenPrices({
|
|
214
|
+
chainId,
|
|
215
|
+
tokenAddresses: [nativeTokenAddress],
|
|
216
|
+
currency: currentCurrency,
|
|
217
|
+
});
|
|
218
|
+
const tokenPrice = tokenPrices[nativeTokenAddress];
|
|
219
|
+
return {
|
|
220
|
+
nativeCurrency,
|
|
221
|
+
conversionDate: tokenPrice ? Date.now() / 1000 : null,
|
|
222
|
+
conversionRate: tokenPrice?.price ?? null,
|
|
223
|
+
usdConversionRate: null, // Token prices service doesn't provide USD rate in this context
|
|
224
|
+
};
|
|
225
|
+
}
|
|
226
|
+
catch (error) {
|
|
227
|
+
console.error(`Failed to fetch token price for ${nativeCurrency} on chain ${chainId}`, error);
|
|
228
|
+
return {
|
|
229
|
+
nativeCurrency,
|
|
230
|
+
conversionDate: null,
|
|
231
|
+
conversionRate: null,
|
|
232
|
+
usdConversionRate: null,
|
|
233
|
+
};
|
|
234
|
+
}
|
|
235
|
+
}));
|
|
236
|
+
// Step 4: Convert to the expected format
|
|
237
|
+
const ratesFromTokenPricesService = ratesFromTokenPrices.reduce((acc, rate) => {
|
|
238
|
+
acc[rate.nativeCurrency] = {
|
|
239
|
+
conversionDate: rate.conversionDate,
|
|
240
|
+
conversionRate: rate.conversionRate,
|
|
241
|
+
usdConversionRate: rate.usdConversionRate,
|
|
242
|
+
};
|
|
243
|
+
return acc;
|
|
244
|
+
}, {});
|
|
245
|
+
// If we got any rates, return them
|
|
246
|
+
if (Object.keys(ratesFromTokenPricesService).length > 0) {
|
|
247
|
+
return ratesFromTokenPricesService;
|
|
248
|
+
}
|
|
249
|
+
}
|
|
250
|
+
catch (error) {
|
|
251
|
+
console.error('Failed to fetch exchange rates from token prices service.', error);
|
|
252
|
+
}
|
|
189
253
|
try {
|
|
190
254
|
const fetchExchangeRateResponse = await this.fetchMultiExchangeRate(currentCurrency, [...new Set(Object.values(nativeCurrenciesToFetch))], this.includeUsdRate);
|
|
191
255
|
const rates = Object.entries(nativeCurrenciesToFetch).reduce((acc, [nativeCurrency, fetchedCurrency]) => {
|
|
@@ -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 { NetworkControllerGetNetworkClientByIdAction } from '@metamask/network-controller';\nimport { StaticIntervalPollingController } from '@metamask/polling-controller';\nimport { Mutex } from 'async-mutex';\n\nimport { fetchMultiExchangeRate as defaultFetchMultiExchangeRate } from './crypto-compare-service';\nimport type { AbstractTokenPricesService } from './token-prices-service/abstract-token-prices-service';\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 = NetworkControllerGetNetworkClientByIdAction;\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 fetchMultiExchangeRate;\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.fetchMultiExchangeRate - Fetches the exchange rate from an external API. This option is primarily meant for use in unit tests.\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 fetchMultiExchangeRate = defaultFetchMultiExchangeRate,\n tokenPricesService,\n }: {\n includeUsdRate?: boolean;\n interval?: number;\n messenger: CurrencyRateMessenger;\n state?: Partial<CurrencyRateState>;\n useExternalServices?: () => boolean;\n fetchMultiExchangeRate?: typeof defaultFetchMultiExchangeRate;\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.fetchMultiExchangeRate = fetchMultiExchangeRate;\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 to crypto compare\n\n try {\n const fetchExchangeRateResponse = await this.fetchMultiExchangeRate(\n currentCurrency,\n [...new Set(Object.values(nativeCurrenciesToFetch))],\n this.includeUsdRate,\n );\n\n const rates = Object.entries(nativeCurrenciesToFetch).reduce(\n (acc, [nativeCurrency, fetchedCurrency]) => {\n const rate = fetchExchangeRateResponse[fetchedCurrency.toLowerCase()];\n acc[nativeCurrency] = {\n conversionDate: rate !== undefined ? Date.now() / 1000 : null,\n conversionRate: rate?.[currentCurrency.toLowerCase()] ?? null,\n usdConversionRate: rate?.usd ?? null,\n };\n return acc;\n },\n {} as CurrencyRateState['currencyRates'],\n );\n\n return rates;\n } catch (error) {\n console.error('Failed to fetch exchange rates.', error);\n throw error;\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 { fetchMultiExchangeRate as defaultFetchMultiExchangeRate } from './crypto-compare-service';\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 fetchMultiExchangeRate;\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.fetchMultiExchangeRate - Fetches the exchange rate from an external API. This option is primarily meant for use in unit tests.\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 fetchMultiExchangeRate = defaultFetchMultiExchangeRate,\n tokenPricesService,\n }: {\n includeUsdRate?: boolean;\n interval?: number;\n messenger: CurrencyRateMessenger;\n state?: Partial<CurrencyRateState>;\n useExternalServices?: () => boolean;\n fetchMultiExchangeRate?: typeof defaultFetchMultiExchangeRate;\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.fetchMultiExchangeRate = fetchMultiExchangeRate;\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 chainIds that have this native currency\n const chainIds = (\n Object.entries(networkConfigurations) as [\n Hex,\n NetworkConfiguration,\n ][]\n )\n .filter(\n ([, config]) =>\n config.nativeCurrency.toUpperCase() ===\n fetchedCurrency.toUpperCase(),\n )\n .map(([chainId]) => chainId);\n\n if (chainIds.length > 0) {\n acc[nativeCurrency] = {\n fetchedCurrency,\n chainId: chainIds[0], // Use the first matching chainId\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 ratesFromTokenPrices = await Promise.all(\n Object.entries(currencyToChainIds).map(\n async ([nativeCurrency, { chainId }]) => {\n try {\n const nativeTokenAddress = getNativeTokenAddress(chainId);\n const tokenPrices =\n await this.#tokenPricesService.fetchTokenPrices({\n chainId,\n tokenAddresses: [nativeTokenAddress],\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 } catch (error) {\n console.error(\n `Failed to fetch token price for ${nativeCurrency} on chain ${chainId}`,\n error,\n );\n return {\n nativeCurrency,\n conversionDate: null,\n conversionRate: null,\n usdConversionRate: null,\n };\n }\n },\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 // If we got any rates, return them\n if (Object.keys(ratesFromTokenPricesService).length > 0) {\n return ratesFromTokenPricesService;\n }\n } catch (error) {\n console.error(\n 'Failed to fetch exchange rates from token prices service.',\n error,\n );\n }\n\n try {\n const fetchExchangeRateResponse = await this.fetchMultiExchangeRate(\n currentCurrency,\n [...new Set(Object.values(nativeCurrenciesToFetch))],\n this.includeUsdRate,\n );\n\n const rates = Object.entries(nativeCurrenciesToFetch).reduce(\n (acc, [nativeCurrency, fetchedCurrency]) => {\n const rate = fetchExchangeRateResponse[fetchedCurrency.toLowerCase()];\n acc[nativeCurrency] = {\n conversionDate: rate !== undefined ? Date.now() / 1000 : null,\n conversionRate: rate?.[currentCurrency.toLowerCase()] ?? null,\n usdConversionRate: rate?.usd ?? null,\n };\n return acc;\n },\n {} as CurrencyRateState['currencyRates'],\n );\n\n return rates;\n } catch (error) {\n console.error('Failed to fetch exchange rates.', error);\n throw error;\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,6 +1,6 @@
|
|
|
1
1
|
import type { ControllerGetStateAction, ControllerStateChangeEvent } from "@metamask/base-controller";
|
|
2
2
|
import type { Messenger } from "@metamask/messenger";
|
|
3
|
-
import type { NetworkControllerGetNetworkClientByIdAction } from "@metamask/network-controller";
|
|
3
|
+
import type { NetworkControllerGetNetworkClientByIdAction, NetworkControllerGetStateAction } from "@metamask/network-controller";
|
|
4
4
|
import { fetchMultiExchangeRate as defaultFetchMultiExchangeRate } from "./crypto-compare-service/index.cjs";
|
|
5
5
|
import type { AbstractTokenPricesService } from "./token-prices-service/abstract-token-prices-service.cjs";
|
|
6
6
|
/**
|
|
@@ -27,7 +27,7 @@ export type CurrencyRateStateChange = ControllerStateChangeEvent<typeof name, Cu
|
|
|
27
27
|
export type CurrencyRateControllerEvents = CurrencyRateStateChange;
|
|
28
28
|
export type GetCurrencyRateState = ControllerGetStateAction<typeof name, CurrencyRateState>;
|
|
29
29
|
export type CurrencyRateControllerActions = GetCurrencyRateState;
|
|
30
|
-
type AllowedActions = NetworkControllerGetNetworkClientByIdAction;
|
|
30
|
+
type AllowedActions = NetworkControllerGetNetworkClientByIdAction | NetworkControllerGetStateAction;
|
|
31
31
|
export type CurrencyRateMessenger = Messenger<typeof name, CurrencyRateControllerActions | AllowedActions, CurrencyRateControllerEvents>;
|
|
32
32
|
/** The input to start polling for the {@link CurrencyRateController} */
|
|
33
33
|
type CurrencyRatePollingInput = {
|
|
@@ -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,
|
|
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,EAAE,sBAAsB,IAAI,6BAA6B,EAAE,2CAAiC;AACnG,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,sBAAsB,CAAC;IAExC,OAAO,CAAC,QAAQ,CAAC,cAAc,CAAC;IAEhC,OAAO,CAAC,QAAQ,CAAC,mBAAmB,CAAgB;IAIpD;;;;;;;;;;;OAWG;gBACS,EACV,cAAsB,EACtB,QAAiB,EACjB,mBAAgC,EAChC,SAAS,EACT,KAAK,EACL,sBAAsD,EACtD,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,sBAAsB,CAAC,EAAE,OAAO,6BAA6B,CAAC;QAC9D,kBAAkB,EAAE,0BAA0B,CAAC;KAChD;IAcD;;;;OAIG;IACG,kBAAkB,CAAC,eAAe,EAAE,MAAM;IAsLhD;;;;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,6 +1,6 @@
|
|
|
1
1
|
import type { ControllerGetStateAction, ControllerStateChangeEvent } from "@metamask/base-controller";
|
|
2
2
|
import type { Messenger } from "@metamask/messenger";
|
|
3
|
-
import type { NetworkControllerGetNetworkClientByIdAction } from "@metamask/network-controller";
|
|
3
|
+
import type { NetworkControllerGetNetworkClientByIdAction, NetworkControllerGetStateAction } from "@metamask/network-controller";
|
|
4
4
|
import { fetchMultiExchangeRate as defaultFetchMultiExchangeRate } from "./crypto-compare-service/index.mjs";
|
|
5
5
|
import type { AbstractTokenPricesService } from "./token-prices-service/abstract-token-prices-service.mjs";
|
|
6
6
|
/**
|
|
@@ -27,7 +27,7 @@ export type CurrencyRateStateChange = ControllerStateChangeEvent<typeof name, Cu
|
|
|
27
27
|
export type CurrencyRateControllerEvents = CurrencyRateStateChange;
|
|
28
28
|
export type GetCurrencyRateState = ControllerGetStateAction<typeof name, CurrencyRateState>;
|
|
29
29
|
export type CurrencyRateControllerActions = GetCurrencyRateState;
|
|
30
|
-
type AllowedActions = NetworkControllerGetNetworkClientByIdAction;
|
|
30
|
+
type AllowedActions = NetworkControllerGetNetworkClientByIdAction | NetworkControllerGetStateAction;
|
|
31
31
|
export type CurrencyRateMessenger = Messenger<typeof name, CurrencyRateControllerActions | AllowedActions, CurrencyRateControllerEvents>;
|
|
32
32
|
/** The input to start polling for the {@link CurrencyRateController} */
|
|
33
33
|
type CurrencyRatePollingInput = {
|
|
@@ -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,
|
|
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,EAAE,sBAAsB,IAAI,6BAA6B,EAAE,2CAAiC;AACnG,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,sBAAsB,CAAC;IAExC,OAAO,CAAC,QAAQ,CAAC,cAAc,CAAC;IAEhC,OAAO,CAAC,QAAQ,CAAC,mBAAmB,CAAgB;IAIpD;;;;;;;;;;;OAWG;gBACS,EACV,cAAsB,EACtB,QAAiB,EACjB,mBAAgC,EAChC,SAAS,EACT,KAAK,EACL,sBAAsD,EACtD,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,sBAAsB,CAAC,EAAE,OAAO,6BAA6B,CAAC;QAC9D,kBAAkB,EAAE,0BAA0B,CAAC;KAChD;IAcD;;;;OAIG;IACG,kBAAkB,CAAC,eAAe,EAAE,MAAM;IAsLhD;;;;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"}
|
|
@@ -14,6 +14,7 @@ import { TESTNET_TICKER_SYMBOLS, FALL_BACK_VS_CURRENCY } from "@metamask/control
|
|
|
14
14
|
import { StaticIntervalPollingController } from "@metamask/polling-controller";
|
|
15
15
|
import { Mutex } from "async-mutex";
|
|
16
16
|
import { fetchMultiExchangeRate as defaultFetchMultiExchangeRate } from "./crypto-compare-service/index.mjs";
|
|
17
|
+
import { getNativeTokenAddress } from "./token-prices-service/codefi-v2.mjs";
|
|
17
18
|
const name = 'CurrencyRateController';
|
|
18
19
|
const metadata = {
|
|
19
20
|
currentCurrency: {
|
|
@@ -181,7 +182,70 @@ _CurrencyRateController_tokenPricesService = new WeakMap(), _CurrencyRateControl
|
|
|
181
182
|
catch (error) {
|
|
182
183
|
console.error('Failed to fetch exchange rates.', error);
|
|
183
184
|
}
|
|
184
|
-
// fallback
|
|
185
|
+
// fallback using spot price from token prices service
|
|
186
|
+
try {
|
|
187
|
+
// Step 1: Get all network configurations to find matching chainIds for native currencies
|
|
188
|
+
const networkControllerState = this.messenger.call('NetworkController:getState');
|
|
189
|
+
const networkConfigurations = networkControllerState.networkConfigurationsByChainId;
|
|
190
|
+
// Step 2: Build a map of nativeCurrency -> chainId(s)
|
|
191
|
+
const currencyToChainIds = Object.entries(nativeCurrenciesToFetch).reduce((acc, [nativeCurrency, fetchedCurrency]) => {
|
|
192
|
+
// Find chainIds that have this native currency
|
|
193
|
+
const chainIds = Object.entries(networkConfigurations)
|
|
194
|
+
.filter(([, config]) => config.nativeCurrency.toUpperCase() ===
|
|
195
|
+
fetchedCurrency.toUpperCase())
|
|
196
|
+
.map(([chainId]) => chainId);
|
|
197
|
+
if (chainIds.length > 0) {
|
|
198
|
+
acc[nativeCurrency] = {
|
|
199
|
+
fetchedCurrency,
|
|
200
|
+
chainId: chainIds[0], // Use the first matching chainId
|
|
201
|
+
};
|
|
202
|
+
}
|
|
203
|
+
return acc;
|
|
204
|
+
}, {});
|
|
205
|
+
// Step 3: Fetch token prices for each chainId
|
|
206
|
+
const ratesFromTokenPrices = await Promise.all(Object.entries(currencyToChainIds).map(async ([nativeCurrency, { chainId }]) => {
|
|
207
|
+
try {
|
|
208
|
+
const nativeTokenAddress = getNativeTokenAddress(chainId);
|
|
209
|
+
const tokenPrices = await __classPrivateFieldGet(this, _CurrencyRateController_tokenPricesService, "f").fetchTokenPrices({
|
|
210
|
+
chainId,
|
|
211
|
+
tokenAddresses: [nativeTokenAddress],
|
|
212
|
+
currency: currentCurrency,
|
|
213
|
+
});
|
|
214
|
+
const tokenPrice = tokenPrices[nativeTokenAddress];
|
|
215
|
+
return {
|
|
216
|
+
nativeCurrency,
|
|
217
|
+
conversionDate: tokenPrice ? Date.now() / 1000 : null,
|
|
218
|
+
conversionRate: tokenPrice?.price ?? null,
|
|
219
|
+
usdConversionRate: null, // Token prices service doesn't provide USD rate in this context
|
|
220
|
+
};
|
|
221
|
+
}
|
|
222
|
+
catch (error) {
|
|
223
|
+
console.error(`Failed to fetch token price for ${nativeCurrency} on chain ${chainId}`, error);
|
|
224
|
+
return {
|
|
225
|
+
nativeCurrency,
|
|
226
|
+
conversionDate: null,
|
|
227
|
+
conversionRate: null,
|
|
228
|
+
usdConversionRate: null,
|
|
229
|
+
};
|
|
230
|
+
}
|
|
231
|
+
}));
|
|
232
|
+
// Step 4: Convert to the expected format
|
|
233
|
+
const ratesFromTokenPricesService = ratesFromTokenPrices.reduce((acc, rate) => {
|
|
234
|
+
acc[rate.nativeCurrency] = {
|
|
235
|
+
conversionDate: rate.conversionDate,
|
|
236
|
+
conversionRate: rate.conversionRate,
|
|
237
|
+
usdConversionRate: rate.usdConversionRate,
|
|
238
|
+
};
|
|
239
|
+
return acc;
|
|
240
|
+
}, {});
|
|
241
|
+
// If we got any rates, return them
|
|
242
|
+
if (Object.keys(ratesFromTokenPricesService).length > 0) {
|
|
243
|
+
return ratesFromTokenPricesService;
|
|
244
|
+
}
|
|
245
|
+
}
|
|
246
|
+
catch (error) {
|
|
247
|
+
console.error('Failed to fetch exchange rates from token prices service.', error);
|
|
248
|
+
}
|
|
185
249
|
try {
|
|
186
250
|
const fetchExchangeRateResponse = await this.fetchMultiExchangeRate(currentCurrency, [...new Set(Object.values(nativeCurrenciesToFetch))], this.includeUsdRate);
|
|
187
251
|
const rates = Object.entries(nativeCurrenciesToFetch).reduce((acc, [nativeCurrency, fetchedCurrency]) => {
|
|
@@ -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 { NetworkControllerGetNetworkClientByIdAction } from '@metamask/network-controller';\nimport { StaticIntervalPollingController } from '@metamask/polling-controller';\nimport { Mutex } from 'async-mutex';\n\nimport { fetchMultiExchangeRate as defaultFetchMultiExchangeRate } from './crypto-compare-service';\nimport type { AbstractTokenPricesService } from './token-prices-service/abstract-token-prices-service';\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 = NetworkControllerGetNetworkClientByIdAction;\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 fetchMultiExchangeRate;\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.fetchMultiExchangeRate - Fetches the exchange rate from an external API. This option is primarily meant for use in unit tests.\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 fetchMultiExchangeRate = defaultFetchMultiExchangeRate,\n tokenPricesService,\n }: {\n includeUsdRate?: boolean;\n interval?: number;\n messenger: CurrencyRateMessenger;\n state?: Partial<CurrencyRateState>;\n useExternalServices?: () => boolean;\n fetchMultiExchangeRate?: typeof defaultFetchMultiExchangeRate;\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.fetchMultiExchangeRate = fetchMultiExchangeRate;\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 to crypto compare\n\n try {\n const fetchExchangeRateResponse = await this.fetchMultiExchangeRate(\n currentCurrency,\n [...new Set(Object.values(nativeCurrenciesToFetch))],\n this.includeUsdRate,\n );\n\n const rates = Object.entries(nativeCurrenciesToFetch).reduce(\n (acc, [nativeCurrency, fetchedCurrency]) => {\n const rate = fetchExchangeRateResponse[fetchedCurrency.toLowerCase()];\n acc[nativeCurrency] = {\n conversionDate: rate !== undefined ? Date.now() / 1000 : null,\n conversionRate: rate?.[currentCurrency.toLowerCase()] ?? null,\n usdConversionRate: rate?.usd ?? null,\n };\n return acc;\n },\n {} as CurrencyRateState['currencyRates'],\n );\n\n return rates;\n } catch (error) {\n console.error('Failed to fetch exchange rates.', error);\n throw error;\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 { fetchMultiExchangeRate as defaultFetchMultiExchangeRate } from './crypto-compare-service';\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 fetchMultiExchangeRate;\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.fetchMultiExchangeRate - Fetches the exchange rate from an external API. This option is primarily meant for use in unit tests.\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 fetchMultiExchangeRate = defaultFetchMultiExchangeRate,\n tokenPricesService,\n }: {\n includeUsdRate?: boolean;\n interval?: number;\n messenger: CurrencyRateMessenger;\n state?: Partial<CurrencyRateState>;\n useExternalServices?: () => boolean;\n fetchMultiExchangeRate?: typeof defaultFetchMultiExchangeRate;\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.fetchMultiExchangeRate = fetchMultiExchangeRate;\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 chainIds that have this native currency\n const chainIds = (\n Object.entries(networkConfigurations) as [\n Hex,\n NetworkConfiguration,\n ][]\n )\n .filter(\n ([, config]) =>\n config.nativeCurrency.toUpperCase() ===\n fetchedCurrency.toUpperCase(),\n )\n .map(([chainId]) => chainId);\n\n if (chainIds.length > 0) {\n acc[nativeCurrency] = {\n fetchedCurrency,\n chainId: chainIds[0], // Use the first matching chainId\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 ratesFromTokenPrices = await Promise.all(\n Object.entries(currencyToChainIds).map(\n async ([nativeCurrency, { chainId }]) => {\n try {\n const nativeTokenAddress = getNativeTokenAddress(chainId);\n const tokenPrices =\n await this.#tokenPricesService.fetchTokenPrices({\n chainId,\n tokenAddresses: [nativeTokenAddress],\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 } catch (error) {\n console.error(\n `Failed to fetch token price for ${nativeCurrency} on chain ${chainId}`,\n error,\n );\n return {\n nativeCurrency,\n conversionDate: null,\n conversionRate: null,\n usdConversionRate: null,\n };\n }\n },\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 // If we got any rates, return them\n if (Object.keys(ratesFromTokenPricesService).length > 0) {\n return ratesFromTokenPricesService;\n }\n } catch (error) {\n console.error(\n 'Failed to fetch exchange rates from token prices service.',\n error,\n );\n }\n\n try {\n const fetchExchangeRateResponse = await this.fetchMultiExchangeRate(\n currentCurrency,\n [...new Set(Object.values(nativeCurrenciesToFetch))],\n this.includeUsdRate,\n );\n\n const rates = Object.entries(nativeCurrenciesToFetch).reduce(\n (acc, [nativeCurrency, fetchedCurrency]) => {\n const rate = fetchExchangeRateResponse[fetchedCurrency.toLowerCase()];\n acc[nativeCurrency] = {\n conversionDate: rate !== undefined ? Date.now() / 1000 : null,\n conversionRate: rate?.[currentCurrency.toLowerCase()] ?? null,\n usdConversionRate: rate?.usd ?? null,\n };\n return acc;\n },\n {} as CurrencyRateState['currencyRates'],\n );\n\n return rates;\n } catch (error) {\n console.error('Failed to fetch exchange rates.', error);\n throw error;\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"]}
|
|
@@ -457,9 +457,6 @@ class TokenBalancesController extends (0, polling_controller_1.StaticIntervalPol
|
|
|
457
457
|
}
|
|
458
458
|
const { address: selected } = this.messenger.call('AccountsController:getSelectedAccount');
|
|
459
459
|
const allAccounts = this.messenger.call('AccountsController:listAccounts');
|
|
460
|
-
const jwtToken = await (0, controller_utils_1.safelyExecuteWithTimeout)(() => {
|
|
461
|
-
return this.messenger.call('AuthenticationController:getBearerToken');
|
|
462
|
-
}, false, 5000);
|
|
463
460
|
const aggregated = [];
|
|
464
461
|
let remainingChains = [...targetChains];
|
|
465
462
|
// Try each fetcher in order, removing successfully processed chains
|
|
@@ -474,7 +471,6 @@ class TokenBalancesController extends (0, polling_controller_1.StaticIntervalPol
|
|
|
474
471
|
queryAllAccounts: queryAllAccounts ?? __classPrivateFieldGet(this, _TokenBalancesController_queryAllAccounts, "f"),
|
|
475
472
|
selectedAccount: selected,
|
|
476
473
|
allAccounts,
|
|
477
|
-
jwtToken,
|
|
478
474
|
});
|
|
479
475
|
if (result.balances && result.balances.length > 0) {
|
|
480
476
|
aggregated.push(...result.balances);
|