@metamask-previews/assets-controllers 68.1.0-preview-8e80bb02 → 68.1.0-preview-4193b35
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 +1 -0
- package/dist/MultichainAssetsRatesController/MultichainAssetsRatesController.cjs +8 -76
- package/dist/MultichainAssetsRatesController/MultichainAssetsRatesController.cjs.map +1 -1
- package/dist/MultichainAssetsRatesController/MultichainAssetsRatesController.d.cts +1 -9
- package/dist/MultichainAssetsRatesController/MultichainAssetsRatesController.d.cts.map +1 -1
- package/dist/MultichainAssetsRatesController/MultichainAssetsRatesController.d.mts +1 -9
- package/dist/MultichainAssetsRatesController/MultichainAssetsRatesController.d.mts.map +1 -1
- package/dist/MultichainAssetsRatesController/MultichainAssetsRatesController.mjs +8 -76
- package/dist/MultichainAssetsRatesController/MultichainAssetsRatesController.mjs.map +1 -1
- package/dist/TokenBalancesController.cjs +6 -1
- 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 +6 -1
- package/dist/TokenBalancesController.mjs.map +1 -1
- package/package.json +1 -2
package/CHANGELOG.md
CHANGED
|
@@ -19,6 +19,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
|
|
19
19
|
- Prevented `AccountTrackerController` from updating state with empty or unchanged account balance data during refresh ([#5942](https://github.com/MetaMask/core/pull/5942))
|
|
20
20
|
- Added guards to skip state updates when fetched balances are empty or identical to existing state
|
|
21
21
|
- Reduces unnecessary `stateChange` emissions and preserves previously-cached balances under network failure scenarios
|
|
22
|
+
- Prevented `TokenBalancesController` from updating account balance to 0 while multicall contract failed ([#5975](https://github.com/MetaMask/core/pull/5975))
|
|
22
23
|
|
|
23
24
|
## [68.1.0]
|
|
24
25
|
|
|
@@ -16,15 +16,8 @@ exports.MultichainAssetsRatesController = exports.getDefaultMultichainAssetsRate
|
|
|
16
16
|
const keyring_api_1 = require("@metamask/keyring-api");
|
|
17
17
|
const polling_controller_1 = require("@metamask/polling-controller");
|
|
18
18
|
const snaps_utils_1 = require("@metamask/snaps-utils");
|
|
19
|
-
const superstruct_1 = require("@metamask/superstruct");
|
|
20
|
-
const utils_1 = require("@metamask/utils");
|
|
21
19
|
const async_mutex_1 = require("async-mutex");
|
|
22
20
|
const constant_1 = require("./constant.cjs");
|
|
23
|
-
const GetHistoricalPricesResponseStruct = (0, superstruct_1.object)({
|
|
24
|
-
prices: (0, superstruct_1.array)((0, superstruct_1.tuple)([(0, superstruct_1.number)(), (0, superstruct_1.number)()])),
|
|
25
|
-
marketCaps: (0, superstruct_1.array)((0, superstruct_1.tuple)([(0, superstruct_1.number)(), (0, superstruct_1.number)()])),
|
|
26
|
-
totalVolumes: (0, superstruct_1.array)((0, superstruct_1.tuple)([(0, superstruct_1.number)(), (0, superstruct_1.number)()])),
|
|
27
|
-
});
|
|
28
21
|
/**
|
|
29
22
|
* The name of the MultichainAssetsRatesController.
|
|
30
23
|
*/
|
|
@@ -161,15 +154,14 @@ class MultichainAssetsRatesController extends (0, polling_controller_1.StaticInt
|
|
|
161
154
|
const selectedAccount = account ??
|
|
162
155
|
this.messagingSystem.call('AccountsController:getSelectedMultichainAccount');
|
|
163
156
|
try {
|
|
164
|
-
const historicalPricesResponse = await
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
// });
|
|
157
|
+
const historicalPricesResponse = await __classPrivateFieldGet(this, _MultichainAssetsRatesController_instances, "m", _MultichainAssetsRatesController_handleSnapRequest).call(this, {
|
|
158
|
+
snapId: selectedAccount?.metadata.snap?.id,
|
|
159
|
+
handler: snaps_utils_1.HandlerType.OnAssetHistoricalPrice,
|
|
160
|
+
params: {
|
|
161
|
+
from: asset,
|
|
162
|
+
to: currentCaipCurrency,
|
|
163
|
+
},
|
|
164
|
+
});
|
|
173
165
|
// skip state update if no historical prices are returned
|
|
174
166
|
if (!historicalPricesResponse) {
|
|
175
167
|
return;
|
|
@@ -191,66 +183,6 @@ class MultichainAssetsRatesController extends (0, polling_controller_1.StaticInt
|
|
|
191
183
|
releaseLock();
|
|
192
184
|
});
|
|
193
185
|
}
|
|
194
|
-
/**
|
|
195
|
-
* Fetches historical prices for a specific asset using the Price API.
|
|
196
|
-
*
|
|
197
|
-
* @param asset - The CAIP asset type to fetch historical prices for.
|
|
198
|
-
* @param currency - The currency to get prices in (defaults to current currency).
|
|
199
|
-
* @returns A promise that resolves with the historical price data in OnAssetHistoricalPriceResponse format.
|
|
200
|
-
*/
|
|
201
|
-
async getHistoricalPriceFromApi(asset, currency) {
|
|
202
|
-
const timePeriodsToFetch = ['1d', '7d', '1m', '3m', '1y', '1000y'];
|
|
203
|
-
try {
|
|
204
|
-
// For each time period, call the Price API to fetch the historical prices
|
|
205
|
-
const promises = timePeriodsToFetch.map(async (timePeriod) => {
|
|
206
|
-
try {
|
|
207
|
-
// https://price.api.cx.metamask.io/v3/historical-prices/solana:5eykt4UsFv8P8NJdTREpY1vzqKqZKvdp/slip44:501?timePeriod=1d&vsCurrency=usd
|
|
208
|
-
const url = `https://price.api.cx.metamask.io/v3/historical-prices/${asset}?timePeriod=${timePeriod}&vsCurrency=${currency}`;
|
|
209
|
-
console.log('MultichainAssetsRatesController.getHistoricalPriceFromApi (new internal logic gutted from snap) calling', url);
|
|
210
|
-
const fetchResponse = await fetch(url);
|
|
211
|
-
if (!fetchResponse.ok) {
|
|
212
|
-
throw new Error(`HTTP error! status: ${fetchResponse.status}`);
|
|
213
|
-
}
|
|
214
|
-
const response = await fetchResponse.json();
|
|
215
|
-
// Add assertion similar to snap
|
|
216
|
-
(0, utils_1.assertStruct)(response, GetHistoricalPricesResponseStruct);
|
|
217
|
-
return {
|
|
218
|
-
timePeriod,
|
|
219
|
-
response,
|
|
220
|
-
};
|
|
221
|
-
}
|
|
222
|
-
catch (error) {
|
|
223
|
-
console.warn(`Error fetching historical prices for ${asset} to ${currency} with time period ${timePeriod}`, error);
|
|
224
|
-
return {
|
|
225
|
-
timePeriod,
|
|
226
|
-
response: { prices: [] }, // Return empty prices on error
|
|
227
|
-
};
|
|
228
|
-
}
|
|
229
|
-
});
|
|
230
|
-
const wrappedHistoricalPrices = await Promise.all(promises);
|
|
231
|
-
const intervals = wrappedHistoricalPrices.reduce((acc, { timePeriod, response }) => {
|
|
232
|
-
const iso8601Interval = `P${timePeriod.toUpperCase()}`;
|
|
233
|
-
acc[iso8601Interval] = response.prices.map((price) => [
|
|
234
|
-
price[0],
|
|
235
|
-
price[1].toString(),
|
|
236
|
-
]);
|
|
237
|
-
return acc;
|
|
238
|
-
}, {});
|
|
239
|
-
const now = Date.now();
|
|
240
|
-
const historicalPrice = {
|
|
241
|
-
intervals,
|
|
242
|
-
updateTime: now,
|
|
243
|
-
expirationTime: now + 1000 * 60, // 1 minute expiration
|
|
244
|
-
};
|
|
245
|
-
return {
|
|
246
|
-
historicalPrice,
|
|
247
|
-
};
|
|
248
|
-
}
|
|
249
|
-
catch (error) {
|
|
250
|
-
console.error(`Failed to fetch historical prices for asset: ${asset}`, error);
|
|
251
|
-
return null;
|
|
252
|
-
}
|
|
253
|
-
}
|
|
254
186
|
}
|
|
255
187
|
exports.MultichainAssetsRatesController = MultichainAssetsRatesController;
|
|
256
188
|
_MultichainAssetsRatesController_mutex = new WeakMap(), _MultichainAssetsRatesController_currentCurrency = new WeakMap(), _MultichainAssetsRatesController_accountsAssets = new WeakMap(), _MultichainAssetsRatesController_isUnlocked = new WeakMap(), _MultichainAssetsRatesController_instances = new WeakSet(), _MultichainAssetsRatesController_isNonEvmAccount = function _MultichainAssetsRatesController_isNonEvmAccount(account) {
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"MultichainAssetsRatesController.cjs","sourceRoot":"","sources":["../../src/MultichainAssetsRatesController/MultichainAssetsRatesController.ts"],"names":[],"mappings":"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","sourcesContent":["import type {\n AccountsControllerListMultichainAccountsAction,\n AccountsControllerAccountAddedEvent,\n AccountsControllerGetSelectedMultichainAccountAction,\n} from '@metamask/accounts-controller';\nimport type {\n RestrictedMessenger,\n ControllerStateChangeEvent,\n ControllerGetStateAction,\n} from '@metamask/base-controller';\nimport { type CaipAssetType, isEvmAccountType } from '@metamask/keyring-api';\nimport type {\n KeyringControllerLockEvent,\n KeyringControllerUnlockEvent,\n} from '@metamask/keyring-controller';\nimport type { InternalAccount } from '@metamask/keyring-internal-api';\nimport { StaticIntervalPollingController } from '@metamask/polling-controller';\nimport type { HandleSnapRequest } from '@metamask/snaps-controllers';\nimport {\n type SnapId,\n type AssetConversion,\n type OnAssetsConversionArguments,\n type OnAssetsConversionResponse,\n type OnAssetHistoricalPriceArguments,\n type OnAssetHistoricalPriceResponse,\n type HistoricalPriceIntervals,\n} from '@metamask/snaps-sdk';\nimport { HandlerType } from '@metamask/snaps-utils';\nimport { object, array, tuple, number } from '@metamask/superstruct';\nimport { assertStruct } from '@metamask/utils';\nimport { Mutex } from 'async-mutex';\nimport type { Draft } from 'immer';\n\nimport { MAP_CAIP_CURRENCIES } from './constant';\nimport type {\n CurrencyRateState,\n CurrencyRateStateChange,\n GetCurrencyRateState,\n} from '../CurrencyRateController';\nimport type {\n MultichainAssetsControllerGetStateAction,\n MultichainAssetsControllerAccountAssetListUpdatedEvent,\n MultichainAssetsControllerState,\n} from '../MultichainAssetsController';\n\nconst GetHistoricalPricesResponseStruct = object({\n prices: array(tuple([number(), number()])),\n marketCaps: array(tuple([number(), number()])),\n totalVolumes: array(tuple([number(), number()])),\n});\n\n/**\n * The name of the MultichainAssetsRatesController.\n */\nconst controllerName = 'MultichainAssetsRatesController';\n\ntype HistoricalPricesApiResponse = {\n /** Array of price data points, each containing [timestamp, price] */\n prices: [number, number][];\n /** Array of market cap data points, each containing [timestamp, marketCap] */\n marketCaps: [number, number][];\n /** Array of total volume data points, each containing [timestamp, volume] */\n totalVolumes: [number, number][];\n};\n\n// This is temporary until its exported from snap\ntype HistoricalPrice = {\n intervals: HistoricalPriceIntervals;\n // The UNIX timestamp of when the historical price was last updated.\n updateTime: number;\n // The UNIX timestamp of when the historical price will expire.\n expirationTime?: number;\n};\n\n/**\n * State used by the MultichainAssetsRatesController to cache token conversion rates.\n */\nexport type MultichainAssetsRatesControllerState = {\n conversionRates: Record<CaipAssetType, AssetConversion>;\n historicalPrices: Record<CaipAssetType, Record<string, HistoricalPrice>>; // string being the current currency we fetched historical prices for\n};\n\n/**\n * Returns the state of the MultichainAssetsRatesController.\n */\nexport type MultichainAssetsRatesControllerGetStateAction =\n ControllerGetStateAction<\n typeof controllerName,\n MultichainAssetsRatesControllerState\n >;\n\n/**\n * Action to update the rates of all supported tokens.\n */\nexport type MultichainAssetsRatesControllerUpdateRatesAction = {\n type: `${typeof controllerName}:updateAssetsRates`;\n handler: MultichainAssetsRatesController['updateAssetsRates'];\n};\n\n/**\n * Constructs the default {@link MultichainAssetsRatesController} state. This allows\n * consumers to provide a partial state object when initializing the controller\n * and also helps in constructing complete state objects for this controller in\n * tests.\n *\n * @returns The default {@link MultichainAssetsRatesController} state.\n */\nexport function getDefaultMultichainAssetsRatesControllerState(): MultichainAssetsRatesControllerState {\n return { conversionRates: {}, historicalPrices: {} };\n}\n\n/**\n * Event emitted when the state of the MultichainAssetsRatesController changes.\n */\nexport type MultichainAssetsRatesControllerStateChange =\n ControllerStateChangeEvent<\n typeof controllerName,\n MultichainAssetsRatesControllerState\n >;\n\n/**\n * Actions exposed by the MultichainAssetsRatesController.\n */\nexport type MultichainAssetsRatesControllerActions =\n | MultichainAssetsRatesControllerGetStateAction\n | MultichainAssetsRatesControllerUpdateRatesAction;\n\n/**\n * Events emitted by MultichainAssetsRatesController.\n */\nexport type MultichainAssetsRatesControllerEvents =\n MultichainAssetsRatesControllerStateChange;\n\n/**\n * Actions that this controller is allowed to call.\n */\nexport type AllowedActions =\n | HandleSnapRequest\n | AccountsControllerListMultichainAccountsAction\n | GetCurrencyRateState\n | MultichainAssetsControllerGetStateAction\n | AccountsControllerGetSelectedMultichainAccountAction;\n\n/**\n * Events that this controller is allowed to subscribe to.\n */\nexport type AllowedEvents =\n | KeyringControllerLockEvent\n | KeyringControllerUnlockEvent\n | AccountsControllerAccountAddedEvent\n | CurrencyRateStateChange\n | MultichainAssetsControllerAccountAssetListUpdatedEvent;\n/**\n * Messenger type for the MultichainAssetsRatesController.\n */\nexport type MultichainAssetsRatesControllerMessenger = RestrictedMessenger<\n typeof controllerName,\n MultichainAssetsRatesControllerActions | AllowedActions,\n MultichainAssetsRatesControllerEvents | AllowedEvents,\n AllowedActions['type'],\n AllowedEvents['type']\n>;\n\n/**\n * The input for starting polling in MultichainAssetsRatesController.\n */\nexport type MultichainAssetsRatesPollingInput = {\n accountId: string;\n};\n\nconst metadata = {\n conversionRates: { persist: true, anonymous: true },\n historicalPrices: { persist: false, anonymous: true },\n};\n\n/**\n * Controller that manages multichain token conversion rates.\n *\n * This controller polls for token conversion rates and updates its state.\n */\nexport class MultichainAssetsRatesController extends StaticIntervalPollingController<MultichainAssetsRatesPollingInput>()<\n typeof controllerName,\n MultichainAssetsRatesControllerState,\n MultichainAssetsRatesControllerMessenger\n> {\n readonly #mutex = new Mutex();\n\n #currentCurrency: CurrencyRateState['currentCurrency'];\n\n readonly #accountsAssets: MultichainAssetsControllerState['accountsAssets'];\n\n #isUnlocked = true;\n\n /**\n * Creates an instance of MultichainAssetsRatesController.\n *\n * @param options - Constructor options.\n * @param options.interval - The polling interval in milliseconds.\n * @param options.state - The initial state.\n * @param options.messenger - A reference to the messaging system.\n */\n constructor({\n interval = 18000,\n state = {},\n messenger,\n }: {\n interval?: number;\n state?: Partial<MultichainAssetsRatesControllerState>;\n messenger: MultichainAssetsRatesControllerMessenger;\n }) {\n super({\n name: controllerName,\n messenger,\n state: {\n ...getDefaultMultichainAssetsRatesControllerState(),\n ...state,\n },\n metadata,\n });\n\n this.setIntervalLength(interval);\n\n // Subscribe to keyring lock/unlock events.\n this.messagingSystem.subscribe('KeyringController:lock', () => {\n this.#isUnlocked = false;\n });\n this.messagingSystem.subscribe('KeyringController:unlock', () => {\n this.#isUnlocked = true;\n });\n\n ({ accountsAssets: this.#accountsAssets } = this.messagingSystem.call(\n 'MultichainAssetsController:getState',\n ));\n\n ({ currentCurrency: this.#currentCurrency } = this.messagingSystem.call(\n 'CurrencyRateController:getState',\n ));\n\n this.messagingSystem.subscribe(\n 'CurrencyRateController:stateChange',\n async (currencyRatesState: CurrencyRateState) => {\n this.#currentCurrency = currencyRatesState.currentCurrency;\n await this.updateAssetsRates();\n },\n );\n\n this.messagingSystem.subscribe(\n 'MultichainAssetsController:accountAssetListUpdated',\n async ({ assets }) => {\n const newAccountAssets = Object.entries(assets).map(\n ([accountId, { added }]) => ({\n accountId,\n assets: [...added],\n }),\n );\n // TODO; removed can be used in future for further cleanup\n await this.#updateAssetsRatesForNewAssets(newAccountAssets);\n },\n );\n }\n\n /**\n * Executes a poll by updating token conversion rates for the current account.\n *\n * @returns A promise that resolves when the polling completes.\n */\n async _executePoll(): Promise<void> {\n await this.updateAssetsRates();\n }\n\n /**\n * Determines whether the controller is active.\n *\n * @returns True if the keyring is unlocked; otherwise, false.\n */\n get isActive(): boolean {\n return this.#isUnlocked;\n }\n\n /**\n * Checks if an account is a non-EVM account with a Snap.\n *\n * @param account - The account to check.\n * @returns True if the account is non-EVM and has Snap metadata; otherwise, false.\n */\n #isNonEvmAccount(account: InternalAccount): boolean {\n return (\n !isEvmAccountType(account.type) && account.metadata.snap !== undefined\n );\n }\n\n /**\n * Retrieves all multichain accounts from the AccountsController.\n *\n * @returns An array of internal accounts.\n */\n #listMultichainAccounts(): InternalAccount[] {\n return this.messagingSystem.call(\n 'AccountsController:listMultichainAccounts',\n );\n }\n\n /**\n * Filters and returns non-EVM accounts that should have balances.\n *\n * @returns An array of non-EVM internal accounts.\n */\n #listAccounts(): InternalAccount[] {\n const accounts = this.#listMultichainAccounts();\n return accounts.filter((account) => this.#isNonEvmAccount(account));\n }\n\n /**\n * Updates token conversion rates for each non-EVM account.\n *\n * @returns A promise that resolves when the rates are updated.\n */\n async updateAssetsRates(): Promise<void> {\n const releaseLock = await this.#mutex.acquire();\n\n return (async () => {\n if (!this.isActive) {\n return;\n }\n const accounts = this.#listAccounts();\n\n for (const account of accounts) {\n const assets = this.#getAssetsForAccount(account.id);\n\n if (assets?.length === 0) {\n continue;\n }\n\n const rates = await this.#getUpdatedRatesFor(account, assets);\n // Apply these updated rates to controller state\n this.#applyUpdatedRates(rates);\n }\n })().finally(() => {\n releaseLock();\n });\n }\n\n async #getUpdatedRatesFor(\n account: InternalAccount,\n assets: CaipAssetType[],\n ): Promise<Record<string, AssetConversion & { currency: CaipAssetType }>> {\n // Build the conversions array\n const conversions = this.#buildConversions(assets);\n\n // Retrieve rates from Snap\n const accountRates: OnAssetsConversionResponse =\n (await this.#handleSnapRequest({\n snapId: account?.metadata.snap?.id as SnapId,\n handler: HandlerType.OnAssetsConversion,\n params: {\n ...conversions,\n includeMarketData: true,\n },\n })) as OnAssetsConversionResponse;\n\n // Flatten nested rates if needed\n const flattenedRates = this.#flattenRates(accountRates);\n\n // Build the updatedRates object for these assets\n const updatedRates = this.#buildUpdatedRates(assets, flattenedRates);\n return updatedRates;\n }\n\n /**\n * Fetches historical prices for the current account\n *\n * @param asset - The asset to fetch historical prices for.\n * @param account - optional account to fetch historical prices for\n * @returns The historical prices.\n */\n async fetchHistoricalPricesForAsset(\n asset: CaipAssetType,\n account?: InternalAccount,\n ): Promise<void> {\n const releaseLock = await this.#mutex.acquire();\n return (async () => {\n const currentCaipCurrency =\n MAP_CAIP_CURRENCIES[this.#currentCurrency] ?? MAP_CAIP_CURRENCIES.usd;\n // Check if we already have historical prices for this asset and currency\n const historicalPriceExpirationTime =\n this.state.historicalPrices[asset]?.[this.#currentCurrency]\n ?.expirationTime;\n\n const historicalPriceHasExpired =\n historicalPriceExpirationTime &&\n historicalPriceExpirationTime < Date.now();\n\n if (historicalPriceHasExpired === false) {\n return;\n }\n\n const selectedAccount =\n account ??\n this.messagingSystem.call(\n 'AccountsController:getSelectedMultichainAccount',\n );\n try {\n const historicalPricesResponse = await this.getHistoricalPriceFromApi(\n asset,\n this.#currentCurrency,\n );\n // const historicalPricesResponse = await this.#handleSnapRequest({\n // snapId: selectedAccount?.metadata.snap?.id as SnapId,\n // handler: HandlerType.OnAssetHistoricalPrice,\n // params: {\n // from: asset,\n // to: currentCaipCurrency,\n // },\n // });\n\n // skip state update if no historical prices are returned\n if (!historicalPricesResponse) {\n return;\n }\n\n this.update((state) => {\n state.historicalPrices = {\n ...state.historicalPrices,\n [asset]: {\n ...state.historicalPrices[asset],\n [this.#currentCurrency]: (\n historicalPricesResponse as OnAssetHistoricalPriceResponse\n )?.historicalPrice,\n },\n };\n });\n } catch {\n throw new Error(\n `Failed to fetch historical prices for asset: ${asset}`,\n );\n }\n })().finally(() => {\n releaseLock();\n });\n }\n\n /**\n * Updates the conversion rates for new assets.\n *\n * @param accounts - The accounts to update the conversion rates for.\n * @returns A promise that resolves when the rates are updated.\n */\n async #updateAssetsRatesForNewAssets(\n accounts: {\n accountId: string;\n assets: CaipAssetType[];\n }[],\n ): Promise<void> {\n const releaseLock = await this.#mutex.acquire();\n\n return (async () => {\n if (!this.isActive) {\n return;\n }\n const allNewRates: Record<\n string,\n { rate: string | null; conversionTime: number | null }\n > = {};\n\n for (const { accountId, assets } of accounts) {\n const account = this.#getAccount(accountId);\n\n const rates = await this.#getUpdatedRatesFor(account, assets);\n // Track new rates\n for (const [asset, rate] of Object.entries(rates)) {\n allNewRates[asset] = rate;\n }\n }\n\n this.#applyUpdatedRates(allNewRates);\n })().finally(() => {\n releaseLock();\n });\n }\n\n /**\n * Get a non-EVM account from its ID.\n *\n * @param accountId - The account ID.\n * @returns The non-EVM account.\n */\n #getAccount(accountId: string): InternalAccount {\n const account: InternalAccount | undefined = this.#listAccounts().find(\n (multichainAccount) => multichainAccount.id === accountId,\n );\n\n if (!account) {\n throw new Error(`Unknown account: ${accountId}`);\n }\n\n return account;\n }\n\n /**\n * Returns the array of CAIP-19 assets for the given account ID.\n * If none are found, returns an empty array.\n *\n * @param accountId - The account ID to get the assets for.\n * @returns An array of CAIP-19 assets.\n */\n #getAssetsForAccount(accountId: string): CaipAssetType[] {\n return this.#accountsAssets?.[accountId] ?? [];\n }\n\n /**\n * Builds a conversions array (from each asset → the current currency).\n *\n * @param assets - The assets to build the conversions for.\n * @returns A conversions array.\n */\n #buildConversions(assets: CaipAssetType[]): OnAssetsConversionArguments {\n const currency =\n MAP_CAIP_CURRENCIES[this.#currentCurrency] ?? MAP_CAIP_CURRENCIES.usd;\n return {\n conversions: assets.map((asset) => ({\n from: asset,\n to: currency,\n })),\n };\n }\n\n /**\n * Flattens any nested structure in the conversion rates returned by Snap.\n *\n * @param assetsConversionResponse - The conversion rates to flatten.\n * @returns A flattened rates object.\n */\n #flattenRates(\n assetsConversionResponse: OnAssetsConversionResponse,\n ): Record<CaipAssetType, AssetConversion | null> {\n const { conversionRates } = assetsConversionResponse;\n\n return Object.fromEntries(\n Object.entries(conversionRates).map(([asset, nestedObj]) => {\n // e.g., nestedObj might look like: { \"swift:0/iso4217:EUR\": { rate, conversionTime } }\n const singleValue = Object.values(nestedObj)[0];\n return [asset, singleValue];\n }),\n );\n }\n\n /**\n * Builds a rates object that covers all given assets, ensuring that\n * any asset not returned by Snap is set to null for both `rate` and `conversionTime`.\n *\n * @param assets - The assets to build the rates for.\n * @param flattenedRates - The rates to merge.\n * @returns A rates object that covers all given assets.\n */\n #buildUpdatedRates(\n assets: CaipAssetType[],\n flattenedRates: Record<CaipAssetType, AssetConversion | null>,\n ): Record<string, AssetConversion & { currency: CaipAssetType }> {\n const updatedRates: Record<\n CaipAssetType,\n AssetConversion & { currency: CaipAssetType }\n > = {};\n\n for (const asset of assets) {\n if (flattenedRates[asset]) {\n updatedRates[asset] = {\n ...(flattenedRates[asset] as AssetConversion),\n currency:\n MAP_CAIP_CURRENCIES[this.#currentCurrency] ??\n MAP_CAIP_CURRENCIES.usd,\n };\n }\n }\n return updatedRates;\n }\n\n /**\n * Merges the new rates into the controller’s state.\n *\n * @param updatedRates - The new rates to merge.\n */\n #applyUpdatedRates(\n updatedRates: Record<\n string,\n { rate: string | null; conversionTime: number | null }\n >,\n ): void {\n if (Object.keys(updatedRates).length === 0) {\n return;\n }\n this.update((state: Draft<MultichainAssetsRatesControllerState>) => {\n state.conversionRates = {\n ...state.conversionRates,\n ...updatedRates,\n };\n });\n }\n\n /**\n * Forwards a Snap request to the SnapController.\n *\n * @param args - The request parameters.\n * @param args.snapId - The ID of the Snap.\n * @param args.handler - The handler type.\n * @param args.params - The asset conversions.\n * @returns A promise that resolves with the account rates.\n */\n async #handleSnapRequest({\n snapId,\n handler,\n params,\n }: {\n snapId: SnapId;\n handler: HandlerType;\n params: OnAssetsConversionArguments | OnAssetHistoricalPriceArguments;\n }): Promise<OnAssetsConversionResponse | OnAssetHistoricalPriceResponse> {\n return this.messagingSystem.call('SnapController:handleRequest', {\n snapId,\n origin: 'metamask',\n handler,\n request: {\n jsonrpc: '2.0',\n method: handler,\n params,\n },\n }) as Promise<OnAssetsConversionResponse | OnAssetHistoricalPriceResponse>;\n }\n\n /**\n * Fetches historical prices for a specific asset using the Price API.\n *\n * @param asset - The CAIP asset type to fetch historical prices for.\n * @param currency - The currency to get prices in (defaults to current currency).\n * @returns A promise that resolves with the historical price data in OnAssetHistoricalPriceResponse format.\n */\n async getHistoricalPriceFromApi(\n asset: CaipAssetType,\n currency?: string,\n ): Promise<OnAssetHistoricalPriceResponse> {\n const timePeriodsToFetch = ['1d', '7d', '1m', '3m', '1y', '1000y'];\n\n try {\n // For each time period, call the Price API to fetch the historical prices\n const promises = timePeriodsToFetch.map(async (timePeriod) => {\n try {\n // https://price.api.cx.metamask.io/v3/historical-prices/solana:5eykt4UsFv8P8NJdTREpY1vzqKqZKvdp/slip44:501?timePeriod=1d&vsCurrency=usd\n const url = `https://price.api.cx.metamask.io/v3/historical-prices/${asset}?timePeriod=${timePeriod}&vsCurrency=${currency}`;\n console.log(\n 'MultichainAssetsRatesController.getHistoricalPriceFromApi (new internal logic gutted from snap) calling',\n url,\n );\n\n const fetchResponse = await fetch(url);\n if (!fetchResponse.ok) {\n throw new Error(`HTTP error! status: ${fetchResponse.status}`);\n }\n\n const response: HistoricalPricesApiResponse =\n await fetchResponse.json();\n\n // Add assertion similar to snap\n assertStruct(response, GetHistoricalPricesResponseStruct);\n\n return {\n timePeriod,\n response,\n };\n } catch (error) {\n console.warn(\n `Error fetching historical prices for ${asset} to ${currency} with time period ${timePeriod}`,\n error,\n );\n return {\n timePeriod,\n response: { prices: [] }, // Return empty prices on error\n };\n }\n });\n\n const wrappedHistoricalPrices = await Promise.all(promises);\n\n const intervals =\n wrappedHistoricalPrices.reduce<HistoricalPriceIntervals>(\n (acc, { timePeriod, response }) => {\n const iso8601Interval = `P${timePeriod.toUpperCase()}`;\n acc[iso8601Interval] = response.prices.map((price) => [\n price[0],\n price[1].toString(),\n ]);\n return acc;\n },\n {},\n );\n\n const now = Date.now();\n const historicalPrice = {\n intervals,\n updateTime: now,\n expirationTime: now + 1000 * 60, // 1 minute expiration\n };\n\n return {\n historicalPrice,\n };\n } catch (error) {\n console.error(\n `Failed to fetch historical prices for asset: ${asset}`,\n error,\n );\n return null;\n }\n }\n}\n"]}
|
|
1
|
+
{"version":3,"file":"MultichainAssetsRatesController.cjs","sourceRoot":"","sources":["../../src/MultichainAssetsRatesController/MultichainAssetsRatesController.ts"],"names":[],"mappings":"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","sourcesContent":["import type {\n AccountsControllerListMultichainAccountsAction,\n AccountsControllerAccountAddedEvent,\n AccountsControllerGetSelectedMultichainAccountAction,\n} from '@metamask/accounts-controller';\nimport type {\n RestrictedMessenger,\n ControllerStateChangeEvent,\n ControllerGetStateAction,\n} from '@metamask/base-controller';\nimport { type CaipAssetType, isEvmAccountType } from '@metamask/keyring-api';\nimport type {\n KeyringControllerLockEvent,\n KeyringControllerUnlockEvent,\n} from '@metamask/keyring-controller';\nimport type { InternalAccount } from '@metamask/keyring-internal-api';\nimport { StaticIntervalPollingController } from '@metamask/polling-controller';\nimport type { HandleSnapRequest } from '@metamask/snaps-controllers';\nimport type {\n SnapId,\n AssetConversion,\n OnAssetsConversionArguments,\n OnAssetsConversionResponse,\n OnAssetHistoricalPriceArguments,\n OnAssetHistoricalPriceResponse,\n HistoricalPriceIntervals,\n} from '@metamask/snaps-sdk';\nimport { HandlerType } from '@metamask/snaps-utils';\nimport { Mutex } from 'async-mutex';\nimport type { Draft } from 'immer';\n\nimport { MAP_CAIP_CURRENCIES } from './constant';\nimport type {\n CurrencyRateState,\n CurrencyRateStateChange,\n GetCurrencyRateState,\n} from '../CurrencyRateController';\nimport type {\n MultichainAssetsControllerGetStateAction,\n MultichainAssetsControllerAccountAssetListUpdatedEvent,\n MultichainAssetsControllerState,\n} from '../MultichainAssetsController';\n\n/**\n * The name of the MultichainAssetsRatesController.\n */\nconst controllerName = 'MultichainAssetsRatesController';\n\n// This is temporary until its exported from snap\ntype HistoricalPrice = {\n intervals: HistoricalPriceIntervals;\n // The UNIX timestamp of when the historical price was last updated.\n updateTime: number;\n // The UNIX timestamp of when the historical price will expire.\n expirationTime?: number;\n};\n\n/**\n * State used by the MultichainAssetsRatesController to cache token conversion rates.\n */\nexport type MultichainAssetsRatesControllerState = {\n conversionRates: Record<CaipAssetType, AssetConversion>;\n historicalPrices: Record<CaipAssetType, Record<string, HistoricalPrice>>; // string being the current currency we fetched historical prices for\n};\n\n/**\n * Returns the state of the MultichainAssetsRatesController.\n */\nexport type MultichainAssetsRatesControllerGetStateAction =\n ControllerGetStateAction<\n typeof controllerName,\n MultichainAssetsRatesControllerState\n >;\n\n/**\n * Action to update the rates of all supported tokens.\n */\nexport type MultichainAssetsRatesControllerUpdateRatesAction = {\n type: `${typeof controllerName}:updateAssetsRates`;\n handler: MultichainAssetsRatesController['updateAssetsRates'];\n};\n\n/**\n * Constructs the default {@link MultichainAssetsRatesController} state. This allows\n * consumers to provide a partial state object when initializing the controller\n * and also helps in constructing complete state objects for this controller in\n * tests.\n *\n * @returns The default {@link MultichainAssetsRatesController} state.\n */\nexport function getDefaultMultichainAssetsRatesControllerState(): MultichainAssetsRatesControllerState {\n return { conversionRates: {}, historicalPrices: {} };\n}\n\n/**\n * Event emitted when the state of the MultichainAssetsRatesController changes.\n */\nexport type MultichainAssetsRatesControllerStateChange =\n ControllerStateChangeEvent<\n typeof controllerName,\n MultichainAssetsRatesControllerState\n >;\n\n/**\n * Actions exposed by the MultichainAssetsRatesController.\n */\nexport type MultichainAssetsRatesControllerActions =\n | MultichainAssetsRatesControllerGetStateAction\n | MultichainAssetsRatesControllerUpdateRatesAction;\n\n/**\n * Events emitted by MultichainAssetsRatesController.\n */\nexport type MultichainAssetsRatesControllerEvents =\n MultichainAssetsRatesControllerStateChange;\n\n/**\n * Actions that this controller is allowed to call.\n */\nexport type AllowedActions =\n | HandleSnapRequest\n | AccountsControllerListMultichainAccountsAction\n | GetCurrencyRateState\n | MultichainAssetsControllerGetStateAction\n | AccountsControllerGetSelectedMultichainAccountAction;\n\n/**\n * Events that this controller is allowed to subscribe to.\n */\nexport type AllowedEvents =\n | KeyringControllerLockEvent\n | KeyringControllerUnlockEvent\n | AccountsControllerAccountAddedEvent\n | CurrencyRateStateChange\n | MultichainAssetsControllerAccountAssetListUpdatedEvent;\n/**\n * Messenger type for the MultichainAssetsRatesController.\n */\nexport type MultichainAssetsRatesControllerMessenger = RestrictedMessenger<\n typeof controllerName,\n MultichainAssetsRatesControllerActions | AllowedActions,\n MultichainAssetsRatesControllerEvents | AllowedEvents,\n AllowedActions['type'],\n AllowedEvents['type']\n>;\n\n/**\n * The input for starting polling in MultichainAssetsRatesController.\n */\nexport type MultichainAssetsRatesPollingInput = {\n accountId: string;\n};\n\nconst metadata = {\n conversionRates: { persist: true, anonymous: true },\n historicalPrices: { persist: false, anonymous: true },\n};\n\n/**\n * Controller that manages multichain token conversion rates.\n *\n * This controller polls for token conversion rates and updates its state.\n */\nexport class MultichainAssetsRatesController extends StaticIntervalPollingController<MultichainAssetsRatesPollingInput>()<\n typeof controllerName,\n MultichainAssetsRatesControllerState,\n MultichainAssetsRatesControllerMessenger\n> {\n readonly #mutex = new Mutex();\n\n #currentCurrency: CurrencyRateState['currentCurrency'];\n\n readonly #accountsAssets: MultichainAssetsControllerState['accountsAssets'];\n\n #isUnlocked = true;\n\n /**\n * Creates an instance of MultichainAssetsRatesController.\n *\n * @param options - Constructor options.\n * @param options.interval - The polling interval in milliseconds.\n * @param options.state - The initial state.\n * @param options.messenger - A reference to the messaging system.\n */\n constructor({\n interval = 18000,\n state = {},\n messenger,\n }: {\n interval?: number;\n state?: Partial<MultichainAssetsRatesControllerState>;\n messenger: MultichainAssetsRatesControllerMessenger;\n }) {\n super({\n name: controllerName,\n messenger,\n state: {\n ...getDefaultMultichainAssetsRatesControllerState(),\n ...state,\n },\n metadata,\n });\n\n this.setIntervalLength(interval);\n\n // Subscribe to keyring lock/unlock events.\n this.messagingSystem.subscribe('KeyringController:lock', () => {\n this.#isUnlocked = false;\n });\n this.messagingSystem.subscribe('KeyringController:unlock', () => {\n this.#isUnlocked = true;\n });\n\n ({ accountsAssets: this.#accountsAssets } = this.messagingSystem.call(\n 'MultichainAssetsController:getState',\n ));\n\n ({ currentCurrency: this.#currentCurrency } = this.messagingSystem.call(\n 'CurrencyRateController:getState',\n ));\n\n this.messagingSystem.subscribe(\n 'CurrencyRateController:stateChange',\n async (currencyRatesState: CurrencyRateState) => {\n this.#currentCurrency = currencyRatesState.currentCurrency;\n await this.updateAssetsRates();\n },\n );\n\n this.messagingSystem.subscribe(\n 'MultichainAssetsController:accountAssetListUpdated',\n async ({ assets }) => {\n const newAccountAssets = Object.entries(assets).map(\n ([accountId, { added }]) => ({\n accountId,\n assets: [...added],\n }),\n );\n // TODO; removed can be used in future for further cleanup\n await this.#updateAssetsRatesForNewAssets(newAccountAssets);\n },\n );\n }\n\n /**\n * Executes a poll by updating token conversion rates for the current account.\n *\n * @returns A promise that resolves when the polling completes.\n */\n async _executePoll(): Promise<void> {\n await this.updateAssetsRates();\n }\n\n /**\n * Determines whether the controller is active.\n *\n * @returns True if the keyring is unlocked; otherwise, false.\n */\n get isActive(): boolean {\n return this.#isUnlocked;\n }\n\n /**\n * Checks if an account is a non-EVM account with a Snap.\n *\n * @param account - The account to check.\n * @returns True if the account is non-EVM and has Snap metadata; otherwise, false.\n */\n #isNonEvmAccount(account: InternalAccount): boolean {\n return (\n !isEvmAccountType(account.type) && account.metadata.snap !== undefined\n );\n }\n\n /**\n * Retrieves all multichain accounts from the AccountsController.\n *\n * @returns An array of internal accounts.\n */\n #listMultichainAccounts(): InternalAccount[] {\n return this.messagingSystem.call(\n 'AccountsController:listMultichainAccounts',\n );\n }\n\n /**\n * Filters and returns non-EVM accounts that should have balances.\n *\n * @returns An array of non-EVM internal accounts.\n */\n #listAccounts(): InternalAccount[] {\n const accounts = this.#listMultichainAccounts();\n return accounts.filter((account) => this.#isNonEvmAccount(account));\n }\n\n /**\n * Updates token conversion rates for each non-EVM account.\n *\n * @returns A promise that resolves when the rates are updated.\n */\n async updateAssetsRates(): Promise<void> {\n const releaseLock = await this.#mutex.acquire();\n\n return (async () => {\n if (!this.isActive) {\n return;\n }\n const accounts = this.#listAccounts();\n\n for (const account of accounts) {\n const assets = this.#getAssetsForAccount(account.id);\n\n if (assets?.length === 0) {\n continue;\n }\n\n const rates = await this.#getUpdatedRatesFor(account, assets);\n // Apply these updated rates to controller state\n this.#applyUpdatedRates(rates);\n }\n })().finally(() => {\n releaseLock();\n });\n }\n\n async #getUpdatedRatesFor(\n account: InternalAccount,\n assets: CaipAssetType[],\n ): Promise<Record<string, AssetConversion & { currency: CaipAssetType }>> {\n // Build the conversions array\n const conversions = this.#buildConversions(assets);\n\n // Retrieve rates from Snap\n const accountRates: OnAssetsConversionResponse =\n (await this.#handleSnapRequest({\n snapId: account?.metadata.snap?.id as SnapId,\n handler: HandlerType.OnAssetsConversion,\n params: {\n ...conversions,\n includeMarketData: true,\n },\n })) as OnAssetsConversionResponse;\n\n // Flatten nested rates if needed\n const flattenedRates = this.#flattenRates(accountRates);\n\n // Build the updatedRates object for these assets\n const updatedRates = this.#buildUpdatedRates(assets, flattenedRates);\n return updatedRates;\n }\n\n /**\n * Fetches historical prices for the current account\n *\n * @param asset - The asset to fetch historical prices for.\n * @param account - optional account to fetch historical prices for\n * @returns The historical prices.\n */\n async fetchHistoricalPricesForAsset(\n asset: CaipAssetType,\n account?: InternalAccount,\n ): Promise<void> {\n const releaseLock = await this.#mutex.acquire();\n return (async () => {\n const currentCaipCurrency =\n MAP_CAIP_CURRENCIES[this.#currentCurrency] ?? MAP_CAIP_CURRENCIES.usd;\n // Check if we already have historical prices for this asset and currency\n const historicalPriceExpirationTime =\n this.state.historicalPrices[asset]?.[this.#currentCurrency]\n ?.expirationTime;\n\n const historicalPriceHasExpired =\n historicalPriceExpirationTime &&\n historicalPriceExpirationTime < Date.now();\n\n if (historicalPriceHasExpired === false) {\n return;\n }\n\n const selectedAccount =\n account ??\n this.messagingSystem.call(\n 'AccountsController:getSelectedMultichainAccount',\n );\n try {\n const historicalPricesResponse = await this.#handleSnapRequest({\n snapId: selectedAccount?.metadata.snap?.id as SnapId,\n handler: HandlerType.OnAssetHistoricalPrice,\n params: {\n from: asset,\n to: currentCaipCurrency,\n },\n });\n\n // skip state update if no historical prices are returned\n if (!historicalPricesResponse) {\n return;\n }\n\n this.update((state) => {\n state.historicalPrices = {\n ...state.historicalPrices,\n [asset]: {\n ...state.historicalPrices[asset],\n [this.#currentCurrency]: (\n historicalPricesResponse as OnAssetHistoricalPriceResponse\n )?.historicalPrice,\n },\n };\n });\n } catch {\n throw new Error(\n `Failed to fetch historical prices for asset: ${asset}`,\n );\n }\n })().finally(() => {\n releaseLock();\n });\n }\n\n /**\n * Updates the conversion rates for new assets.\n *\n * @param accounts - The accounts to update the conversion rates for.\n * @returns A promise that resolves when the rates are updated.\n */\n async #updateAssetsRatesForNewAssets(\n accounts: {\n accountId: string;\n assets: CaipAssetType[];\n }[],\n ): Promise<void> {\n const releaseLock = await this.#mutex.acquire();\n\n return (async () => {\n if (!this.isActive) {\n return;\n }\n const allNewRates: Record<\n string,\n { rate: string | null; conversionTime: number | null }\n > = {};\n\n for (const { accountId, assets } of accounts) {\n const account = this.#getAccount(accountId);\n\n const rates = await this.#getUpdatedRatesFor(account, assets);\n // Track new rates\n for (const [asset, rate] of Object.entries(rates)) {\n allNewRates[asset] = rate;\n }\n }\n\n this.#applyUpdatedRates(allNewRates);\n })().finally(() => {\n releaseLock();\n });\n }\n\n /**\n * Get a non-EVM account from its ID.\n *\n * @param accountId - The account ID.\n * @returns The non-EVM account.\n */\n #getAccount(accountId: string): InternalAccount {\n const account: InternalAccount | undefined = this.#listAccounts().find(\n (multichainAccount) => multichainAccount.id === accountId,\n );\n\n if (!account) {\n throw new Error(`Unknown account: ${accountId}`);\n }\n\n return account;\n }\n\n /**\n * Returns the array of CAIP-19 assets for the given account ID.\n * If none are found, returns an empty array.\n *\n * @param accountId - The account ID to get the assets for.\n * @returns An array of CAIP-19 assets.\n */\n #getAssetsForAccount(accountId: string): CaipAssetType[] {\n return this.#accountsAssets?.[accountId] ?? [];\n }\n\n /**\n * Builds a conversions array (from each asset → the current currency).\n *\n * @param assets - The assets to build the conversions for.\n * @returns A conversions array.\n */\n #buildConversions(assets: CaipAssetType[]): OnAssetsConversionArguments {\n const currency =\n MAP_CAIP_CURRENCIES[this.#currentCurrency] ?? MAP_CAIP_CURRENCIES.usd;\n return {\n conversions: assets.map((asset) => ({\n from: asset,\n to: currency,\n })),\n };\n }\n\n /**\n * Flattens any nested structure in the conversion rates returned by Snap.\n *\n * @param assetsConversionResponse - The conversion rates to flatten.\n * @returns A flattened rates object.\n */\n #flattenRates(\n assetsConversionResponse: OnAssetsConversionResponse,\n ): Record<CaipAssetType, AssetConversion | null> {\n const { conversionRates } = assetsConversionResponse;\n\n return Object.fromEntries(\n Object.entries(conversionRates).map(([asset, nestedObj]) => {\n // e.g., nestedObj might look like: { \"swift:0/iso4217:EUR\": { rate, conversionTime } }\n const singleValue = Object.values(nestedObj)[0];\n return [asset, singleValue];\n }),\n );\n }\n\n /**\n * Builds a rates object that covers all given assets, ensuring that\n * any asset not returned by Snap is set to null for both `rate` and `conversionTime`.\n *\n * @param assets - The assets to build the rates for.\n * @param flattenedRates - The rates to merge.\n * @returns A rates object that covers all given assets.\n */\n #buildUpdatedRates(\n assets: CaipAssetType[],\n flattenedRates: Record<CaipAssetType, AssetConversion | null>,\n ): Record<string, AssetConversion & { currency: CaipAssetType }> {\n const updatedRates: Record<\n CaipAssetType,\n AssetConversion & { currency: CaipAssetType }\n > = {};\n\n for (const asset of assets) {\n if (flattenedRates[asset]) {\n updatedRates[asset] = {\n ...(flattenedRates[asset] as AssetConversion),\n currency:\n MAP_CAIP_CURRENCIES[this.#currentCurrency] ??\n MAP_CAIP_CURRENCIES.usd,\n };\n }\n }\n return updatedRates;\n }\n\n /**\n * Merges the new rates into the controller’s state.\n *\n * @param updatedRates - The new rates to merge.\n */\n #applyUpdatedRates(\n updatedRates: Record<\n string,\n { rate: string | null; conversionTime: number | null }\n >,\n ): void {\n if (Object.keys(updatedRates).length === 0) {\n return;\n }\n this.update((state: Draft<MultichainAssetsRatesControllerState>) => {\n state.conversionRates = {\n ...state.conversionRates,\n ...updatedRates,\n };\n });\n }\n\n /**\n * Forwards a Snap request to the SnapController.\n *\n * @param args - The request parameters.\n * @param args.snapId - The ID of the Snap.\n * @param args.handler - The handler type.\n * @param args.params - The asset conversions.\n * @returns A promise that resolves with the account rates.\n */\n async #handleSnapRequest({\n snapId,\n handler,\n params,\n }: {\n snapId: SnapId;\n handler: HandlerType;\n params: OnAssetsConversionArguments | OnAssetHistoricalPriceArguments;\n }): Promise<OnAssetsConversionResponse | OnAssetHistoricalPriceResponse> {\n return this.messagingSystem.call('SnapController:handleRequest', {\n snapId,\n origin: 'metamask',\n handler,\n request: {\n jsonrpc: '2.0',\n method: handler,\n params,\n },\n }) as Promise<OnAssetsConversionResponse | OnAssetHistoricalPriceResponse>;\n }\n}\n"]}
|
|
@@ -4,7 +4,7 @@ import { type CaipAssetType } from "@metamask/keyring-api";
|
|
|
4
4
|
import type { KeyringControllerLockEvent, KeyringControllerUnlockEvent } from "@metamask/keyring-controller";
|
|
5
5
|
import type { InternalAccount } from "@metamask/keyring-internal-api";
|
|
6
6
|
import type { HandleSnapRequest } from "@metamask/snaps-controllers";
|
|
7
|
-
import {
|
|
7
|
+
import type { AssetConversion, HistoricalPriceIntervals } from "@metamask/snaps-sdk";
|
|
8
8
|
import type { CurrencyRateStateChange, GetCurrencyRateState } from "../CurrencyRateController.cjs";
|
|
9
9
|
import type { MultichainAssetsControllerGetStateAction, MultichainAssetsControllerAccountAssetListUpdatedEvent } from "../MultichainAssetsController/index.cjs";
|
|
10
10
|
/**
|
|
@@ -134,14 +134,6 @@ export declare class MultichainAssetsRatesController extends MultichainAssetsRat
|
|
|
134
134
|
* @returns The historical prices.
|
|
135
135
|
*/
|
|
136
136
|
fetchHistoricalPricesForAsset(asset: CaipAssetType, account?: InternalAccount): Promise<void>;
|
|
137
|
-
/**
|
|
138
|
-
* Fetches historical prices for a specific asset using the Price API.
|
|
139
|
-
*
|
|
140
|
-
* @param asset - The CAIP asset type to fetch historical prices for.
|
|
141
|
-
* @param currency - The currency to get prices in (defaults to current currency).
|
|
142
|
-
* @returns A promise that resolves with the historical price data in OnAssetHistoricalPriceResponse format.
|
|
143
|
-
*/
|
|
144
|
-
getHistoricalPriceFromApi(asset: CaipAssetType, currency?: string): Promise<OnAssetHistoricalPriceResponse>;
|
|
145
137
|
}
|
|
146
138
|
export {};
|
|
147
139
|
//# sourceMappingURL=MultichainAssetsRatesController.d.cts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"MultichainAssetsRatesController.d.cts","sourceRoot":"","sources":["../../src/MultichainAssetsRatesController/MultichainAssetsRatesController.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EACV,8CAA8C,EAC9C,mCAAmC,EACnC,oDAAoD,EACrD,sCAAsC;AACvC,OAAO,KAAK,EACV,mBAAmB,EACnB,0BAA0B,EAC1B,wBAAwB,EACzB,kCAAkC;AACnC,OAAO,EAAE,KAAK,aAAa,EAAoB,8BAA8B;AAC7E,OAAO,KAAK,EACV,0BAA0B,EAC1B,4BAA4B,EAC7B,qCAAqC;AACtC,OAAO,KAAK,EAAE,eAAe,EAAE,uCAAuC;AAEtE,OAAO,KAAK,EAAE,iBAAiB,EAAE,oCAAoC;AACrE,OAAO,
|
|
1
|
+
{"version":3,"file":"MultichainAssetsRatesController.d.cts","sourceRoot":"","sources":["../../src/MultichainAssetsRatesController/MultichainAssetsRatesController.ts"],"names":[],"mappings":"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"}
|
|
@@ -4,7 +4,7 @@ import { type CaipAssetType } from "@metamask/keyring-api";
|
|
|
4
4
|
import type { KeyringControllerLockEvent, KeyringControllerUnlockEvent } from "@metamask/keyring-controller";
|
|
5
5
|
import type { InternalAccount } from "@metamask/keyring-internal-api";
|
|
6
6
|
import type { HandleSnapRequest } from "@metamask/snaps-controllers";
|
|
7
|
-
import {
|
|
7
|
+
import type { AssetConversion, HistoricalPriceIntervals } from "@metamask/snaps-sdk";
|
|
8
8
|
import type { CurrencyRateStateChange, GetCurrencyRateState } from "../CurrencyRateController.mjs";
|
|
9
9
|
import type { MultichainAssetsControllerGetStateAction, MultichainAssetsControllerAccountAssetListUpdatedEvent } from "../MultichainAssetsController/index.mjs";
|
|
10
10
|
/**
|
|
@@ -134,14 +134,6 @@ export declare class MultichainAssetsRatesController extends MultichainAssetsRat
|
|
|
134
134
|
* @returns The historical prices.
|
|
135
135
|
*/
|
|
136
136
|
fetchHistoricalPricesForAsset(asset: CaipAssetType, account?: InternalAccount): Promise<void>;
|
|
137
|
-
/**
|
|
138
|
-
* Fetches historical prices for a specific asset using the Price API.
|
|
139
|
-
*
|
|
140
|
-
* @param asset - The CAIP asset type to fetch historical prices for.
|
|
141
|
-
* @param currency - The currency to get prices in (defaults to current currency).
|
|
142
|
-
* @returns A promise that resolves with the historical price data in OnAssetHistoricalPriceResponse format.
|
|
143
|
-
*/
|
|
144
|
-
getHistoricalPriceFromApi(asset: CaipAssetType, currency?: string): Promise<OnAssetHistoricalPriceResponse>;
|
|
145
137
|
}
|
|
146
138
|
export {};
|
|
147
139
|
//# sourceMappingURL=MultichainAssetsRatesController.d.mts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"MultichainAssetsRatesController.d.mts","sourceRoot":"","sources":["../../src/MultichainAssetsRatesController/MultichainAssetsRatesController.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EACV,8CAA8C,EAC9C,mCAAmC,EACnC,oDAAoD,EACrD,sCAAsC;AACvC,OAAO,KAAK,EACV,mBAAmB,EACnB,0BAA0B,EAC1B,wBAAwB,EACzB,kCAAkC;AACnC,OAAO,EAAE,KAAK,aAAa,EAAoB,8BAA8B;AAC7E,OAAO,KAAK,EACV,0BAA0B,EAC1B,4BAA4B,EAC7B,qCAAqC;AACtC,OAAO,KAAK,EAAE,eAAe,EAAE,uCAAuC;AAEtE,OAAO,KAAK,EAAE,iBAAiB,EAAE,oCAAoC;AACrE,OAAO,
|
|
1
|
+
{"version":3,"file":"MultichainAssetsRatesController.d.mts","sourceRoot":"","sources":["../../src/MultichainAssetsRatesController/MultichainAssetsRatesController.ts"],"names":[],"mappings":"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"}
|
|
@@ -13,15 +13,8 @@ var _MultichainAssetsRatesController_instances, _MultichainAssetsRatesController
|
|
|
13
13
|
import { isEvmAccountType } from "@metamask/keyring-api";
|
|
14
14
|
import { StaticIntervalPollingController } from "@metamask/polling-controller";
|
|
15
15
|
import { HandlerType } from "@metamask/snaps-utils";
|
|
16
|
-
import { object, array, tuple, number } from "@metamask/superstruct";
|
|
17
|
-
import { assertStruct } from "@metamask/utils";
|
|
18
16
|
import { Mutex } from "async-mutex";
|
|
19
17
|
import { MAP_CAIP_CURRENCIES } from "./constant.mjs";
|
|
20
|
-
const GetHistoricalPricesResponseStruct = object({
|
|
21
|
-
prices: array(tuple([number(), number()])),
|
|
22
|
-
marketCaps: array(tuple([number(), number()])),
|
|
23
|
-
totalVolumes: array(tuple([number(), number()])),
|
|
24
|
-
});
|
|
25
18
|
/**
|
|
26
19
|
* The name of the MultichainAssetsRatesController.
|
|
27
20
|
*/
|
|
@@ -157,15 +150,14 @@ export class MultichainAssetsRatesController extends StaticIntervalPollingContro
|
|
|
157
150
|
const selectedAccount = account ??
|
|
158
151
|
this.messagingSystem.call('AccountsController:getSelectedMultichainAccount');
|
|
159
152
|
try {
|
|
160
|
-
const historicalPricesResponse = await
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
// });
|
|
153
|
+
const historicalPricesResponse = await __classPrivateFieldGet(this, _MultichainAssetsRatesController_instances, "m", _MultichainAssetsRatesController_handleSnapRequest).call(this, {
|
|
154
|
+
snapId: selectedAccount?.metadata.snap?.id,
|
|
155
|
+
handler: HandlerType.OnAssetHistoricalPrice,
|
|
156
|
+
params: {
|
|
157
|
+
from: asset,
|
|
158
|
+
to: currentCaipCurrency,
|
|
159
|
+
},
|
|
160
|
+
});
|
|
169
161
|
// skip state update if no historical prices are returned
|
|
170
162
|
if (!historicalPricesResponse) {
|
|
171
163
|
return;
|
|
@@ -187,66 +179,6 @@ export class MultichainAssetsRatesController extends StaticIntervalPollingContro
|
|
|
187
179
|
releaseLock();
|
|
188
180
|
});
|
|
189
181
|
}
|
|
190
|
-
/**
|
|
191
|
-
* Fetches historical prices for a specific asset using the Price API.
|
|
192
|
-
*
|
|
193
|
-
* @param asset - The CAIP asset type to fetch historical prices for.
|
|
194
|
-
* @param currency - The currency to get prices in (defaults to current currency).
|
|
195
|
-
* @returns A promise that resolves with the historical price data in OnAssetHistoricalPriceResponse format.
|
|
196
|
-
*/
|
|
197
|
-
async getHistoricalPriceFromApi(asset, currency) {
|
|
198
|
-
const timePeriodsToFetch = ['1d', '7d', '1m', '3m', '1y', '1000y'];
|
|
199
|
-
try {
|
|
200
|
-
// For each time period, call the Price API to fetch the historical prices
|
|
201
|
-
const promises = timePeriodsToFetch.map(async (timePeriod) => {
|
|
202
|
-
try {
|
|
203
|
-
// https://price.api.cx.metamask.io/v3/historical-prices/solana:5eykt4UsFv8P8NJdTREpY1vzqKqZKvdp/slip44:501?timePeriod=1d&vsCurrency=usd
|
|
204
|
-
const url = `https://price.api.cx.metamask.io/v3/historical-prices/${asset}?timePeriod=${timePeriod}&vsCurrency=${currency}`;
|
|
205
|
-
console.log('MultichainAssetsRatesController.getHistoricalPriceFromApi (new internal logic gutted from snap) calling', url);
|
|
206
|
-
const fetchResponse = await fetch(url);
|
|
207
|
-
if (!fetchResponse.ok) {
|
|
208
|
-
throw new Error(`HTTP error! status: ${fetchResponse.status}`);
|
|
209
|
-
}
|
|
210
|
-
const response = await fetchResponse.json();
|
|
211
|
-
// Add assertion similar to snap
|
|
212
|
-
assertStruct(response, GetHistoricalPricesResponseStruct);
|
|
213
|
-
return {
|
|
214
|
-
timePeriod,
|
|
215
|
-
response,
|
|
216
|
-
};
|
|
217
|
-
}
|
|
218
|
-
catch (error) {
|
|
219
|
-
console.warn(`Error fetching historical prices for ${asset} to ${currency} with time period ${timePeriod}`, error);
|
|
220
|
-
return {
|
|
221
|
-
timePeriod,
|
|
222
|
-
response: { prices: [] }, // Return empty prices on error
|
|
223
|
-
};
|
|
224
|
-
}
|
|
225
|
-
});
|
|
226
|
-
const wrappedHistoricalPrices = await Promise.all(promises);
|
|
227
|
-
const intervals = wrappedHistoricalPrices.reduce((acc, { timePeriod, response }) => {
|
|
228
|
-
const iso8601Interval = `P${timePeriod.toUpperCase()}`;
|
|
229
|
-
acc[iso8601Interval] = response.prices.map((price) => [
|
|
230
|
-
price[0],
|
|
231
|
-
price[1].toString(),
|
|
232
|
-
]);
|
|
233
|
-
return acc;
|
|
234
|
-
}, {});
|
|
235
|
-
const now = Date.now();
|
|
236
|
-
const historicalPrice = {
|
|
237
|
-
intervals,
|
|
238
|
-
updateTime: now,
|
|
239
|
-
expirationTime: now + 1000 * 60, // 1 minute expiration
|
|
240
|
-
};
|
|
241
|
-
return {
|
|
242
|
-
historicalPrice,
|
|
243
|
-
};
|
|
244
|
-
}
|
|
245
|
-
catch (error) {
|
|
246
|
-
console.error(`Failed to fetch historical prices for asset: ${asset}`, error);
|
|
247
|
-
return null;
|
|
248
|
-
}
|
|
249
|
-
}
|
|
250
182
|
}
|
|
251
183
|
_MultichainAssetsRatesController_mutex = new WeakMap(), _MultichainAssetsRatesController_currentCurrency = new WeakMap(), _MultichainAssetsRatesController_accountsAssets = new WeakMap(), _MultichainAssetsRatesController_isUnlocked = new WeakMap(), _MultichainAssetsRatesController_instances = new WeakSet(), _MultichainAssetsRatesController_isNonEvmAccount = function _MultichainAssetsRatesController_isNonEvmAccount(account) {
|
|
252
184
|
return (!isEvmAccountType(account.type) && account.metadata.snap !== undefined);
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"MultichainAssetsRatesController.mjs","sourceRoot":"","sources":["../../src/MultichainAssetsRatesController/MultichainAssetsRatesController.ts"],"names":[],"mappings":";;;;;;;;;;;;AAUA,OAAO,EAAsB,gBAAgB,EAAE,8BAA8B;AAM7E,OAAO,EAAE,+BAA+B,EAAE,qCAAqC;AAW/E,OAAO,EAAE,WAAW,EAAE,8BAA8B;AACpD,OAAO,EAAE,MAAM,EAAE,KAAK,EAAE,KAAK,EAAE,MAAM,EAAE,8BAA8B;AACrE,OAAO,EAAE,YAAY,EAAE,wBAAwB;AAC/C,OAAO,EAAE,KAAK,EAAE,oBAAoB;AAGpC,OAAO,EAAE,mBAAmB,EAAE,uBAAmB;AAYjD,MAAM,iCAAiC,GAAG,MAAM,CAAC;IAC/C,MAAM,EAAE,KAAK,CAAC,KAAK,CAAC,CAAC,MAAM,EAAE,EAAE,MAAM,EAAE,CAAC,CAAC,CAAC;IAC1C,UAAU,EAAE,KAAK,CAAC,KAAK,CAAC,CAAC,MAAM,EAAE,EAAE,MAAM,EAAE,CAAC,CAAC,CAAC;IAC9C,YAAY,EAAE,KAAK,CAAC,KAAK,CAAC,CAAC,MAAM,EAAE,EAAE,MAAM,EAAE,CAAC,CAAC,CAAC;CACjD,CAAC,CAAC;AAEH;;GAEG;AACH,MAAM,cAAc,GAAG,iCAAiC,CAAC;AA6CzD;;;;;;;GAOG;AACH,MAAM,UAAU,8CAA8C;IAC5D,OAAO,EAAE,eAAe,EAAE,EAAE,EAAE,gBAAgB,EAAE,EAAE,EAAE,CAAC;AACvD,CAAC;AA6DD,MAAM,QAAQ,GAAG;IACf,eAAe,EAAE,EAAE,OAAO,EAAE,IAAI,EAAE,SAAS,EAAE,IAAI,EAAE;IACnD,gBAAgB,EAAE,EAAE,OAAO,EAAE,KAAK,EAAE,SAAS,EAAE,IAAI,EAAE;CACtD,CAAC;AAEF;;;;GAIG;AACH,MAAM,OAAO,+BAAgC,SAAQ,+BAA+B,EAInF;IASC;;;;;;;OAOG;IACH,YAAY,EACV,QAAQ,GAAG,KAAK,EAChB,KAAK,GAAG,EAAE,EACV,SAAS,GAKV;;QACC,KAAK,CAAC;YACJ,IAAI,EAAE,cAAc;YACpB,SAAS;YACT,KAAK,EAAE;gBACL,GAAG,8CAA8C,EAAE;gBACnD,GAAG,KAAK;aACT;YACD,QAAQ;SACT,CAAC,CAAC;;QAjCI,iDAAS,IAAI,KAAK,EAAE,EAAC;QAE9B,mEAAuD;QAE9C,kEAAmE;QAE5E,sDAAc,IAAI,EAAC;QA6BjB,IAAI,CAAC,iBAAiB,CAAC,QAAQ,CAAC,CAAC;QAEjC,2CAA2C;QAC3C,IAAI,CAAC,eAAe,CAAC,SAAS,CAAC,wBAAwB,EAAE,GAAG,EAAE;YAC5D,uBAAA,IAAI,+CAAe,KAAK,MAAA,CAAC;QAC3B,CAAC,CAAC,CAAC;QACH,IAAI,CAAC,eAAe,CAAC,SAAS,CAAC,0BAA0B,EAAE,GAAG,EAAE;YAC9D,uBAAA,IAAI,+CAAe,IAAI,MAAA,CAAC;QAC1B,CAAC,CAAC,CAAC;QAEH,MAAmB,IAAI,EAAtB,EAAE,cAAc,qHAAsB,EAAE,GAAG,IAAI,CAAC,eAAe,CAAC,IAAI,CACnE,qCAAqC,CACtC,CAAC,CAAC;QAEH,MAAoB,IAAI,EAAvB,EAAE,eAAe,sHAAuB,EAAE,GAAG,IAAI,CAAC,eAAe,CAAC,IAAI,CACrE,iCAAiC,CAClC,CAAC,CAAC;QAEH,IAAI,CAAC,eAAe,CAAC,SAAS,CAC5B,oCAAoC,EACpC,KAAK,EAAE,kBAAqC,EAAE,EAAE;YAC9C,uBAAA,IAAI,oDAAoB,kBAAkB,CAAC,eAAe,MAAA,CAAC;YAC3D,MAAM,IAAI,CAAC,iBAAiB,EAAE,CAAC;QACjC,CAAC,CACF,CAAC;QAEF,IAAI,CAAC,eAAe,CAAC,SAAS,CAC5B,oDAAoD,EACpD,KAAK,EAAE,EAAE,MAAM,EAAE,EAAE,EAAE;YACnB,MAAM,gBAAgB,GAAG,MAAM,CAAC,OAAO,CAAC,MAAM,CAAC,CAAC,GAAG,CACjD,CAAC,CAAC,SAAS,EAAE,EAAE,KAAK,EAAE,CAAC,EAAE,EAAE,CAAC,CAAC;gBAC3B,SAAS;gBACT,MAAM,EAAE,CAAC,GAAG,KAAK,CAAC;aACnB,CAAC,CACH,CAAC;YACF,0DAA0D;YAC1D,MAAM,uBAAA,IAAI,kHAA+B,MAAnC,IAAI,EAAgC,gBAAgB,CAAC,CAAC;QAC9D,CAAC,CACF,CAAC;IACJ,CAAC;IAED;;;;OAIG;IACH,KAAK,CAAC,YAAY;QAChB,MAAM,IAAI,CAAC,iBAAiB,EAAE,CAAC;IACjC,CAAC;IAED;;;;OAIG;IACH,IAAI,QAAQ;QACV,OAAO,uBAAA,IAAI,mDAAY,CAAC;IAC1B,CAAC;IAmCD;;;;OAIG;IACH,KAAK,CAAC,iBAAiB;QACrB,MAAM,WAAW,GAAG,MAAM,uBAAA,IAAI,8CAAO,CAAC,OAAO,EAAE,CAAC;QAEhD,OAAO,CAAC,KAAK,IAAI,EAAE;YACjB,IAAI,CAAC,IAAI,CAAC,QAAQ,EAAE;gBAClB,OAAO;aACR;YACD,MAAM,QAAQ,GAAG,uBAAA,IAAI,iGAAc,MAAlB,IAAI,CAAgB,CAAC;YAEtC,KAAK,MAAM,OAAO,IAAI,QAAQ,EAAE;gBAC9B,MAAM,MAAM,GAAG,uBAAA,IAAI,wGAAqB,MAAzB,IAAI,EAAsB,OAAO,CAAC,EAAE,CAAC,CAAC;gBAErD,IAAI,MAAM,EAAE,MAAM,KAAK,CAAC,EAAE;oBACxB,SAAS;iBACV;gBAED,MAAM,KAAK,GAAG,MAAM,uBAAA,IAAI,uGAAoB,MAAxB,IAAI,EAAqB,OAAO,EAAE,MAAM,CAAC,CAAC;gBAC9D,gDAAgD;gBAChD,uBAAA,IAAI,sGAAmB,MAAvB,IAAI,EAAoB,KAAK,CAAC,CAAC;aAChC;QACH,CAAC,CAAC,EAAE,CAAC,OAAO,CAAC,GAAG,EAAE;YAChB,WAAW,EAAE,CAAC;QAChB,CAAC,CAAC,CAAC;IACL,CAAC;IA4BD;;;;;;OAMG;IACH,KAAK,CAAC,6BAA6B,CACjC,KAAoB,EACpB,OAAyB;QAEzB,MAAM,WAAW,GAAG,MAAM,uBAAA,IAAI,8CAAO,CAAC,OAAO,EAAE,CAAC;QAChD,OAAO,CAAC,KAAK,IAAI,EAAE;YACjB,MAAM,mBAAmB,GACvB,mBAAmB,CAAC,uBAAA,IAAI,wDAAiB,CAAC,IAAI,mBAAmB,CAAC,GAAG,CAAC;YACxE,yEAAyE;YACzE,MAAM,6BAA6B,GACjC,IAAI,CAAC,KAAK,CAAC,gBAAgB,CAAC,KAAK,CAAC,EAAE,CAAC,uBAAA,IAAI,wDAAiB,CAAC;gBACzD,EAAE,cAAc,CAAC;YAErB,MAAM,yBAAyB,GAC7B,6BAA6B;gBAC7B,6BAA6B,GAAG,IAAI,CAAC,GAAG,EAAE,CAAC;YAE7C,IAAI,yBAAyB,KAAK,KAAK,EAAE;gBACvC,OAAO;aACR;YAED,MAAM,eAAe,GACnB,OAAO;gBACP,IAAI,CAAC,eAAe,CAAC,IAAI,CACvB,iDAAiD,CAClD,CAAC;YACJ,IAAI;gBACF,MAAM,wBAAwB,GAAG,MAAM,IAAI,CAAC,yBAAyB,CACnE,KAAK,EACL,uBAAA,IAAI,wDAAiB,CACtB,CAAC;gBACF,mEAAmE;gBACnE,0DAA0D;gBAC1D,+CAA+C;gBAC/C,cAAc;gBACd,mBAAmB;gBACnB,+BAA+B;gBAC/B,OAAO;gBACP,MAAM;gBAEN,yDAAyD;gBACzD,IAAI,CAAC,wBAAwB,EAAE;oBAC7B,OAAO;iBACR;gBAED,IAAI,CAAC,MAAM,CAAC,CAAC,KAAK,EAAE,EAAE;oBACpB,KAAK,CAAC,gBAAgB,GAAG;wBACvB,GAAG,KAAK,CAAC,gBAAgB;wBACzB,CAAC,KAAK,CAAC,EAAE;4BACP,GAAG,KAAK,CAAC,gBAAgB,CAAC,KAAK,CAAC;4BAChC,CAAC,uBAAA,IAAI,wDAAiB,CAAC,EACrB,wBACD,EAAE,eAAe;yBACnB;qBACF,CAAC;gBACJ,CAAC,CAAC,CAAC;aACJ;YAAC,MAAM;gBACN,MAAM,IAAI,KAAK,CACb,gDAAgD,KAAK,EAAE,CACxD,CAAC;aACH;QACH,CAAC,CAAC,EAAE,CAAC,OAAO,CAAC,GAAG,EAAE;YAChB,WAAW,EAAE,CAAC;QAChB,CAAC,CAAC,CAAC;IACL,CAAC;IA6LD;;;;;;OAMG;IACH,KAAK,CAAC,yBAAyB,CAC7B,KAAoB,EACpB,QAAiB;QAEjB,MAAM,kBAAkB,GAAG,CAAC,IAAI,EAAE,IAAI,EAAE,IAAI,EAAE,IAAI,EAAE,IAAI,EAAE,OAAO,CAAC,CAAC;QAEnE,IAAI;YACF,0EAA0E;YAC1E,MAAM,QAAQ,GAAG,kBAAkB,CAAC,GAAG,CAAC,KAAK,EAAE,UAAU,EAAE,EAAE;gBAC3D,IAAI;oBACF,wIAAwI;oBACxI,MAAM,GAAG,GAAG,yDAAyD,KAAK,eAAe,UAAU,eAAe,QAAQ,EAAE,CAAC;oBAC7H,OAAO,CAAC,GAAG,CACT,yGAAyG,EACzG,GAAG,CACJ,CAAC;oBAEF,MAAM,aAAa,GAAG,MAAM,KAAK,CAAC,GAAG,CAAC,CAAC;oBACvC,IAAI,CAAC,aAAa,CAAC,EAAE,EAAE;wBACrB,MAAM,IAAI,KAAK,CAAC,uBAAuB,aAAa,CAAC,MAAM,EAAE,CAAC,CAAC;qBAChE;oBAED,MAAM,QAAQ,GACZ,MAAM,aAAa,CAAC,IAAI,EAAE,CAAC;oBAE7B,gCAAgC;oBAChC,YAAY,CAAC,QAAQ,EAAE,iCAAiC,CAAC,CAAC;oBAE1D,OAAO;wBACL,UAAU;wBACV,QAAQ;qBACT,CAAC;iBACH;gBAAC,OAAO,KAAK,EAAE;oBACd,OAAO,CAAC,IAAI,CACV,wCAAwC,KAAK,OAAO,QAAQ,qBAAqB,UAAU,EAAE,EAC7F,KAAK,CACN,CAAC;oBACF,OAAO;wBACL,UAAU;wBACV,QAAQ,EAAE,EAAE,MAAM,EAAE,EAAE,EAAE,EAAE,+BAA+B;qBAC1D,CAAC;iBACH;YACH,CAAC,CAAC,CAAC;YAEH,MAAM,uBAAuB,GAAG,MAAM,OAAO,CAAC,GAAG,CAAC,QAAQ,CAAC,CAAC;YAE5D,MAAM,SAAS,GACb,uBAAuB,CAAC,MAAM,CAC5B,CAAC,GAAG,EAAE,EAAE,UAAU,EAAE,QAAQ,EAAE,EAAE,EAAE;gBAChC,MAAM,eAAe,GAAG,IAAI,UAAU,CAAC,WAAW,EAAE,EAAE,CAAC;gBACvD,GAAG,CAAC,eAAe,CAAC,GAAG,QAAQ,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC,KAAK,EAAE,EAAE,CAAC;oBACpD,KAAK,CAAC,CAAC,CAAC;oBACR,KAAK,CAAC,CAAC,CAAC,CAAC,QAAQ,EAAE;iBACpB,CAAC,CAAC;gBACH,OAAO,GAAG,CAAC;YACb,CAAC,EACD,EAAE,CACH,CAAC;YAEJ,MAAM,GAAG,GAAG,IAAI,CAAC,GAAG,EAAE,CAAC;YACvB,MAAM,eAAe,GAAG;gBACtB,SAAS;gBACT,UAAU,EAAE,GAAG;gBACf,cAAc,EAAE,GAAG,GAAG,IAAI,GAAG,EAAE,EAAE,sBAAsB;aACxD,CAAC;YAEF,OAAO;gBACL,eAAe;aAChB,CAAC;SACH;QAAC,OAAO,KAAK,EAAE;YACd,OAAO,CAAC,KAAK,CACX,gDAAgD,KAAK,EAAE,EACvD,KAAK,CACN,CAAC;YACF,OAAO,IAAI,CAAC;SACb;IACH,CAAC;CACF;iaA3akB,OAAwB;IACvC,OAAO,CACL,CAAC,gBAAgB,CAAC,OAAO,CAAC,IAAI,CAAC,IAAI,OAAO,CAAC,QAAQ,CAAC,IAAI,KAAK,SAAS,CACvE,CAAC;AACJ,CAAC;IAQC,OAAO,IAAI,CAAC,eAAe,CAAC,IAAI,CAC9B,2CAA2C,CAC5C,CAAC;AACJ,CAAC;IAQC,MAAM,QAAQ,GAAG,uBAAA,IAAI,2GAAwB,MAA5B,IAAI,CAA0B,CAAC;IAChD,OAAO,QAAQ,CAAC,MAAM,CAAC,CAAC,OAAO,EAAE,EAAE,CAAC,uBAAA,IAAI,oGAAiB,MAArB,IAAI,EAAkB,OAAO,CAAC,CAAC,CAAC;AACtE,CAAC,wDAgCD,KAAK,8DACH,OAAwB,EACxB,MAAuB;IAEvB,8BAA8B;IAC9B,MAAM,WAAW,GAAG,uBAAA,IAAI,qGAAkB,MAAtB,IAAI,EAAmB,MAAM,CAAC,CAAC;IAEnD,2BAA2B;IAC3B,MAAM,YAAY,GAChB,CAAC,MAAM,uBAAA,IAAI,sGAAmB,MAAvB,IAAI,EAAoB;QAC7B,MAAM,EAAE,OAAO,EAAE,QAAQ,CAAC,IAAI,EAAE,EAAY;QAC5C,OAAO,EAAE,WAAW,CAAC,kBAAkB;QACvC,MAAM,EAAE;YACN,GAAG,WAAW;YACd,iBAAiB,EAAE,IAAI;SACxB;KACF,CAAC,CAA+B,CAAC;IAEpC,iCAAiC;IACjC,MAAM,cAAc,GAAG,uBAAA,IAAI,iGAAc,MAAlB,IAAI,EAAe,YAAY,CAAC,CAAC;IAExD,iDAAiD;IACjD,MAAM,YAAY,GAAG,uBAAA,IAAI,sGAAmB,MAAvB,IAAI,EAAoB,MAAM,EAAE,cAAc,CAAC,CAAC;IACrE,OAAO,YAAY,CAAC;AACtB,CAAC;AA2ED;;;;;GAKG;AACH,KAAK,yEACH,QAGG;IAEH,MAAM,WAAW,GAAG,MAAM,uBAAA,IAAI,8CAAO,CAAC,OAAO,EAAE,CAAC;IAEhD,OAAO,CAAC,KAAK,IAAI,EAAE;QACjB,IAAI,CAAC,IAAI,CAAC,QAAQ,EAAE;YAClB,OAAO;SACR;QACD,MAAM,WAAW,GAGb,EAAE,CAAC;QAEP,KAAK,MAAM,EAAE,SAAS,EAAE,MAAM,EAAE,IAAI,QAAQ,EAAE;YAC5C,MAAM,OAAO,GAAG,uBAAA,IAAI,+FAAY,MAAhB,IAAI,EAAa,SAAS,CAAC,CAAC;YAE5C,MAAM,KAAK,GAAG,MAAM,uBAAA,IAAI,uGAAoB,MAAxB,IAAI,EAAqB,OAAO,EAAE,MAAM,CAAC,CAAC;YAC9D,kBAAkB;YAClB,KAAK,MAAM,CAAC,KAAK,EAAE,IAAI,CAAC,IAAI,MAAM,CAAC,OAAO,CAAC,KAAK,CAAC,EAAE;gBACjD,WAAW,CAAC,KAAK,CAAC,GAAG,IAAI,CAAC;aAC3B;SACF;QAED,uBAAA,IAAI,sGAAmB,MAAvB,IAAI,EAAoB,WAAW,CAAC,CAAC;IACvC,CAAC,CAAC,EAAE,CAAC,OAAO,CAAC,GAAG,EAAE;QAChB,WAAW,EAAE,CAAC;IAChB,CAAC,CAAC,CAAC;AACL,CAAC,qGAQW,SAAiB;IAC3B,MAAM,OAAO,GAAgC,uBAAA,IAAI,iGAAc,MAAlB,IAAI,CAAgB,CAAC,IAAI,CACpE,CAAC,iBAAiB,EAAE,EAAE,CAAC,iBAAiB,CAAC,EAAE,KAAK,SAAS,CAC1D,CAAC;IAEF,IAAI,CAAC,OAAO,EAAE;QACZ,MAAM,IAAI,KAAK,CAAC,oBAAoB,SAAS,EAAE,CAAC,CAAC;KAClD;IAED,OAAO,OAAO,CAAC;AACjB,CAAC,uHASoB,SAAiB;IACpC,OAAO,uBAAA,IAAI,uDAAgB,EAAE,CAAC,SAAS,CAAC,IAAI,EAAE,CAAC;AACjD,CAAC,iHAQiB,MAAuB;IACvC,MAAM,QAAQ,GACZ,mBAAmB,CAAC,uBAAA,IAAI,wDAAiB,CAAC,IAAI,mBAAmB,CAAC,GAAG,CAAC;IACxE,OAAO;QACL,WAAW,EAAE,MAAM,CAAC,GAAG,CAAC,CAAC,KAAK,EAAE,EAAE,CAAC,CAAC;YAClC,IAAI,EAAE,KAAK;YACX,EAAE,EAAE,QAAQ;SACb,CAAC,CAAC;KACJ,CAAC;AACJ,CAAC,yGASC,wBAAoD;IAEpD,MAAM,EAAE,eAAe,EAAE,GAAG,wBAAwB,CAAC;IAErD,OAAO,MAAM,CAAC,WAAW,CACvB,MAAM,CAAC,OAAO,CAAC,eAAe,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,KAAK,EAAE,SAAS,CAAC,EAAE,EAAE;QACzD,uFAAuF;QACvF,MAAM,WAAW,GAAG,MAAM,CAAC,MAAM,CAAC,SAAS,CAAC,CAAC,CAAC,CAAC,CAAC;QAChD,OAAO,CAAC,KAAK,EAAE,WAAW,CAAC,CAAC;IAC9B,CAAC,CAAC,CACH,CAAC;AACJ,CAAC,mHAWC,MAAuB,EACvB,cAA6D;IAE7D,MAAM,YAAY,GAGd,EAAE,CAAC;IAEP,KAAK,MAAM,KAAK,IAAI,MAAM,EAAE;QAC1B,IAAI,cAAc,CAAC,KAAK,CAAC,EAAE;YACzB,YAAY,CAAC,KAAK,CAAC,GAAG;gBACpB,GAAI,cAAc,CAAC,KAAK,CAAqB;gBAC7C,QAAQ,EACN,mBAAmB,CAAC,uBAAA,IAAI,wDAAiB,CAAC;oBAC1C,mBAAmB,CAAC,GAAG;aAC1B,CAAC;SACH;KACF;IACD,OAAO,YAAY,CAAC;AACtB,CAAC,mHAQC,YAGC;IAED,IAAI,MAAM,CAAC,IAAI,CAAC,YAAY,CAAC,CAAC,MAAM,KAAK,CAAC,EAAE;QAC1C,OAAO;KACR;IACD,IAAI,CAAC,MAAM,CAAC,CAAC,KAAkD,EAAE,EAAE;QACjE,KAAK,CAAC,eAAe,GAAG;YACtB,GAAG,KAAK,CAAC,eAAe;YACxB,GAAG,YAAY;SAChB,CAAC;IACJ,CAAC,CAAC,CAAC;AACL,CAAC;AAED;;;;;;;;GAQG;AACH,KAAK,6DAAoB,EACvB,MAAM,EACN,OAAO,EACP,MAAM,GAKP;IACC,OAAO,IAAI,CAAC,eAAe,CAAC,IAAI,CAAC,8BAA8B,EAAE;QAC/D,MAAM;QACN,MAAM,EAAE,UAAU;QAClB,OAAO;QACP,OAAO,EAAE;YACP,OAAO,EAAE,KAAK;YACd,MAAM,EAAE,OAAO;YACf,MAAM;SACP;KACF,CAAyE,CAAC;AAC7E,CAAC","sourcesContent":["import type {\n AccountsControllerListMultichainAccountsAction,\n AccountsControllerAccountAddedEvent,\n AccountsControllerGetSelectedMultichainAccountAction,\n} from '@metamask/accounts-controller';\nimport type {\n RestrictedMessenger,\n ControllerStateChangeEvent,\n ControllerGetStateAction,\n} from '@metamask/base-controller';\nimport { type CaipAssetType, isEvmAccountType } from '@metamask/keyring-api';\nimport type {\n KeyringControllerLockEvent,\n KeyringControllerUnlockEvent,\n} from '@metamask/keyring-controller';\nimport type { InternalAccount } from '@metamask/keyring-internal-api';\nimport { StaticIntervalPollingController } from '@metamask/polling-controller';\nimport type { HandleSnapRequest } from '@metamask/snaps-controllers';\nimport {\n type SnapId,\n type AssetConversion,\n type OnAssetsConversionArguments,\n type OnAssetsConversionResponse,\n type OnAssetHistoricalPriceArguments,\n type OnAssetHistoricalPriceResponse,\n type HistoricalPriceIntervals,\n} from '@metamask/snaps-sdk';\nimport { HandlerType } from '@metamask/snaps-utils';\nimport { object, array, tuple, number } from '@metamask/superstruct';\nimport { assertStruct } from '@metamask/utils';\nimport { Mutex } from 'async-mutex';\nimport type { Draft } from 'immer';\n\nimport { MAP_CAIP_CURRENCIES } from './constant';\nimport type {\n CurrencyRateState,\n CurrencyRateStateChange,\n GetCurrencyRateState,\n} from '../CurrencyRateController';\nimport type {\n MultichainAssetsControllerGetStateAction,\n MultichainAssetsControllerAccountAssetListUpdatedEvent,\n MultichainAssetsControllerState,\n} from '../MultichainAssetsController';\n\nconst GetHistoricalPricesResponseStruct = object({\n prices: array(tuple([number(), number()])),\n marketCaps: array(tuple([number(), number()])),\n totalVolumes: array(tuple([number(), number()])),\n});\n\n/**\n * The name of the MultichainAssetsRatesController.\n */\nconst controllerName = 'MultichainAssetsRatesController';\n\ntype HistoricalPricesApiResponse = {\n /** Array of price data points, each containing [timestamp, price] */\n prices: [number, number][];\n /** Array of market cap data points, each containing [timestamp, marketCap] */\n marketCaps: [number, number][];\n /** Array of total volume data points, each containing [timestamp, volume] */\n totalVolumes: [number, number][];\n};\n\n// This is temporary until its exported from snap\ntype HistoricalPrice = {\n intervals: HistoricalPriceIntervals;\n // The UNIX timestamp of when the historical price was last updated.\n updateTime: number;\n // The UNIX timestamp of when the historical price will expire.\n expirationTime?: number;\n};\n\n/**\n * State used by the MultichainAssetsRatesController to cache token conversion rates.\n */\nexport type MultichainAssetsRatesControllerState = {\n conversionRates: Record<CaipAssetType, AssetConversion>;\n historicalPrices: Record<CaipAssetType, Record<string, HistoricalPrice>>; // string being the current currency we fetched historical prices for\n};\n\n/**\n * Returns the state of the MultichainAssetsRatesController.\n */\nexport type MultichainAssetsRatesControllerGetStateAction =\n ControllerGetStateAction<\n typeof controllerName,\n MultichainAssetsRatesControllerState\n >;\n\n/**\n * Action to update the rates of all supported tokens.\n */\nexport type MultichainAssetsRatesControllerUpdateRatesAction = {\n type: `${typeof controllerName}:updateAssetsRates`;\n handler: MultichainAssetsRatesController['updateAssetsRates'];\n};\n\n/**\n * Constructs the default {@link MultichainAssetsRatesController} state. This allows\n * consumers to provide a partial state object when initializing the controller\n * and also helps in constructing complete state objects for this controller in\n * tests.\n *\n * @returns The default {@link MultichainAssetsRatesController} state.\n */\nexport function getDefaultMultichainAssetsRatesControllerState(): MultichainAssetsRatesControllerState {\n return { conversionRates: {}, historicalPrices: {} };\n}\n\n/**\n * Event emitted when the state of the MultichainAssetsRatesController changes.\n */\nexport type MultichainAssetsRatesControllerStateChange =\n ControllerStateChangeEvent<\n typeof controllerName,\n MultichainAssetsRatesControllerState\n >;\n\n/**\n * Actions exposed by the MultichainAssetsRatesController.\n */\nexport type MultichainAssetsRatesControllerActions =\n | MultichainAssetsRatesControllerGetStateAction\n | MultichainAssetsRatesControllerUpdateRatesAction;\n\n/**\n * Events emitted by MultichainAssetsRatesController.\n */\nexport type MultichainAssetsRatesControllerEvents =\n MultichainAssetsRatesControllerStateChange;\n\n/**\n * Actions that this controller is allowed to call.\n */\nexport type AllowedActions =\n | HandleSnapRequest\n | AccountsControllerListMultichainAccountsAction\n | GetCurrencyRateState\n | MultichainAssetsControllerGetStateAction\n | AccountsControllerGetSelectedMultichainAccountAction;\n\n/**\n * Events that this controller is allowed to subscribe to.\n */\nexport type AllowedEvents =\n | KeyringControllerLockEvent\n | KeyringControllerUnlockEvent\n | AccountsControllerAccountAddedEvent\n | CurrencyRateStateChange\n | MultichainAssetsControllerAccountAssetListUpdatedEvent;\n/**\n * Messenger type for the MultichainAssetsRatesController.\n */\nexport type MultichainAssetsRatesControllerMessenger = RestrictedMessenger<\n typeof controllerName,\n MultichainAssetsRatesControllerActions | AllowedActions,\n MultichainAssetsRatesControllerEvents | AllowedEvents,\n AllowedActions['type'],\n AllowedEvents['type']\n>;\n\n/**\n * The input for starting polling in MultichainAssetsRatesController.\n */\nexport type MultichainAssetsRatesPollingInput = {\n accountId: string;\n};\n\nconst metadata = {\n conversionRates: { persist: true, anonymous: true },\n historicalPrices: { persist: false, anonymous: true },\n};\n\n/**\n * Controller that manages multichain token conversion rates.\n *\n * This controller polls for token conversion rates and updates its state.\n */\nexport class MultichainAssetsRatesController extends StaticIntervalPollingController<MultichainAssetsRatesPollingInput>()<\n typeof controllerName,\n MultichainAssetsRatesControllerState,\n MultichainAssetsRatesControllerMessenger\n> {\n readonly #mutex = new Mutex();\n\n #currentCurrency: CurrencyRateState['currentCurrency'];\n\n readonly #accountsAssets: MultichainAssetsControllerState['accountsAssets'];\n\n #isUnlocked = true;\n\n /**\n * Creates an instance of MultichainAssetsRatesController.\n *\n * @param options - Constructor options.\n * @param options.interval - The polling interval in milliseconds.\n * @param options.state - The initial state.\n * @param options.messenger - A reference to the messaging system.\n */\n constructor({\n interval = 18000,\n state = {},\n messenger,\n }: {\n interval?: number;\n state?: Partial<MultichainAssetsRatesControllerState>;\n messenger: MultichainAssetsRatesControllerMessenger;\n }) {\n super({\n name: controllerName,\n messenger,\n state: {\n ...getDefaultMultichainAssetsRatesControllerState(),\n ...state,\n },\n metadata,\n });\n\n this.setIntervalLength(interval);\n\n // Subscribe to keyring lock/unlock events.\n this.messagingSystem.subscribe('KeyringController:lock', () => {\n this.#isUnlocked = false;\n });\n this.messagingSystem.subscribe('KeyringController:unlock', () => {\n this.#isUnlocked = true;\n });\n\n ({ accountsAssets: this.#accountsAssets } = this.messagingSystem.call(\n 'MultichainAssetsController:getState',\n ));\n\n ({ currentCurrency: this.#currentCurrency } = this.messagingSystem.call(\n 'CurrencyRateController:getState',\n ));\n\n this.messagingSystem.subscribe(\n 'CurrencyRateController:stateChange',\n async (currencyRatesState: CurrencyRateState) => {\n this.#currentCurrency = currencyRatesState.currentCurrency;\n await this.updateAssetsRates();\n },\n );\n\n this.messagingSystem.subscribe(\n 'MultichainAssetsController:accountAssetListUpdated',\n async ({ assets }) => {\n const newAccountAssets = Object.entries(assets).map(\n ([accountId, { added }]) => ({\n accountId,\n assets: [...added],\n }),\n );\n // TODO; removed can be used in future for further cleanup\n await this.#updateAssetsRatesForNewAssets(newAccountAssets);\n },\n );\n }\n\n /**\n * Executes a poll by updating token conversion rates for the current account.\n *\n * @returns A promise that resolves when the polling completes.\n */\n async _executePoll(): Promise<void> {\n await this.updateAssetsRates();\n }\n\n /**\n * Determines whether the controller is active.\n *\n * @returns True if the keyring is unlocked; otherwise, false.\n */\n get isActive(): boolean {\n return this.#isUnlocked;\n }\n\n /**\n * Checks if an account is a non-EVM account with a Snap.\n *\n * @param account - The account to check.\n * @returns True if the account is non-EVM and has Snap metadata; otherwise, false.\n */\n #isNonEvmAccount(account: InternalAccount): boolean {\n return (\n !isEvmAccountType(account.type) && account.metadata.snap !== undefined\n );\n }\n\n /**\n * Retrieves all multichain accounts from the AccountsController.\n *\n * @returns An array of internal accounts.\n */\n #listMultichainAccounts(): InternalAccount[] {\n return this.messagingSystem.call(\n 'AccountsController:listMultichainAccounts',\n );\n }\n\n /**\n * Filters and returns non-EVM accounts that should have balances.\n *\n * @returns An array of non-EVM internal accounts.\n */\n #listAccounts(): InternalAccount[] {\n const accounts = this.#listMultichainAccounts();\n return accounts.filter((account) => this.#isNonEvmAccount(account));\n }\n\n /**\n * Updates token conversion rates for each non-EVM account.\n *\n * @returns A promise that resolves when the rates are updated.\n */\n async updateAssetsRates(): Promise<void> {\n const releaseLock = await this.#mutex.acquire();\n\n return (async () => {\n if (!this.isActive) {\n return;\n }\n const accounts = this.#listAccounts();\n\n for (const account of accounts) {\n const assets = this.#getAssetsForAccount(account.id);\n\n if (assets?.length === 0) {\n continue;\n }\n\n const rates = await this.#getUpdatedRatesFor(account, assets);\n // Apply these updated rates to controller state\n this.#applyUpdatedRates(rates);\n }\n })().finally(() => {\n releaseLock();\n });\n }\n\n async #getUpdatedRatesFor(\n account: InternalAccount,\n assets: CaipAssetType[],\n ): Promise<Record<string, AssetConversion & { currency: CaipAssetType }>> {\n // Build the conversions array\n const conversions = this.#buildConversions(assets);\n\n // Retrieve rates from Snap\n const accountRates: OnAssetsConversionResponse =\n (await this.#handleSnapRequest({\n snapId: account?.metadata.snap?.id as SnapId,\n handler: HandlerType.OnAssetsConversion,\n params: {\n ...conversions,\n includeMarketData: true,\n },\n })) as OnAssetsConversionResponse;\n\n // Flatten nested rates if needed\n const flattenedRates = this.#flattenRates(accountRates);\n\n // Build the updatedRates object for these assets\n const updatedRates = this.#buildUpdatedRates(assets, flattenedRates);\n return updatedRates;\n }\n\n /**\n * Fetches historical prices for the current account\n *\n * @param asset - The asset to fetch historical prices for.\n * @param account - optional account to fetch historical prices for\n * @returns The historical prices.\n */\n async fetchHistoricalPricesForAsset(\n asset: CaipAssetType,\n account?: InternalAccount,\n ): Promise<void> {\n const releaseLock = await this.#mutex.acquire();\n return (async () => {\n const currentCaipCurrency =\n MAP_CAIP_CURRENCIES[this.#currentCurrency] ?? MAP_CAIP_CURRENCIES.usd;\n // Check if we already have historical prices for this asset and currency\n const historicalPriceExpirationTime =\n this.state.historicalPrices[asset]?.[this.#currentCurrency]\n ?.expirationTime;\n\n const historicalPriceHasExpired =\n historicalPriceExpirationTime &&\n historicalPriceExpirationTime < Date.now();\n\n if (historicalPriceHasExpired === false) {\n return;\n }\n\n const selectedAccount =\n account ??\n this.messagingSystem.call(\n 'AccountsController:getSelectedMultichainAccount',\n );\n try {\n const historicalPricesResponse = await this.getHistoricalPriceFromApi(\n asset,\n this.#currentCurrency,\n );\n // const historicalPricesResponse = await this.#handleSnapRequest({\n // snapId: selectedAccount?.metadata.snap?.id as SnapId,\n // handler: HandlerType.OnAssetHistoricalPrice,\n // params: {\n // from: asset,\n // to: currentCaipCurrency,\n // },\n // });\n\n // skip state update if no historical prices are returned\n if (!historicalPricesResponse) {\n return;\n }\n\n this.update((state) => {\n state.historicalPrices = {\n ...state.historicalPrices,\n [asset]: {\n ...state.historicalPrices[asset],\n [this.#currentCurrency]: (\n historicalPricesResponse as OnAssetHistoricalPriceResponse\n )?.historicalPrice,\n },\n };\n });\n } catch {\n throw new Error(\n `Failed to fetch historical prices for asset: ${asset}`,\n );\n }\n })().finally(() => {\n releaseLock();\n });\n }\n\n /**\n * Updates the conversion rates for new assets.\n *\n * @param accounts - The accounts to update the conversion rates for.\n * @returns A promise that resolves when the rates are updated.\n */\n async #updateAssetsRatesForNewAssets(\n accounts: {\n accountId: string;\n assets: CaipAssetType[];\n }[],\n ): Promise<void> {\n const releaseLock = await this.#mutex.acquire();\n\n return (async () => {\n if (!this.isActive) {\n return;\n }\n const allNewRates: Record<\n string,\n { rate: string | null; conversionTime: number | null }\n > = {};\n\n for (const { accountId, assets } of accounts) {\n const account = this.#getAccount(accountId);\n\n const rates = await this.#getUpdatedRatesFor(account, assets);\n // Track new rates\n for (const [asset, rate] of Object.entries(rates)) {\n allNewRates[asset] = rate;\n }\n }\n\n this.#applyUpdatedRates(allNewRates);\n })().finally(() => {\n releaseLock();\n });\n }\n\n /**\n * Get a non-EVM account from its ID.\n *\n * @param accountId - The account ID.\n * @returns The non-EVM account.\n */\n #getAccount(accountId: string): InternalAccount {\n const account: InternalAccount | undefined = this.#listAccounts().find(\n (multichainAccount) => multichainAccount.id === accountId,\n );\n\n if (!account) {\n throw new Error(`Unknown account: ${accountId}`);\n }\n\n return account;\n }\n\n /**\n * Returns the array of CAIP-19 assets for the given account ID.\n * If none are found, returns an empty array.\n *\n * @param accountId - The account ID to get the assets for.\n * @returns An array of CAIP-19 assets.\n */\n #getAssetsForAccount(accountId: string): CaipAssetType[] {\n return this.#accountsAssets?.[accountId] ?? [];\n }\n\n /**\n * Builds a conversions array (from each asset → the current currency).\n *\n * @param assets - The assets to build the conversions for.\n * @returns A conversions array.\n */\n #buildConversions(assets: CaipAssetType[]): OnAssetsConversionArguments {\n const currency =\n MAP_CAIP_CURRENCIES[this.#currentCurrency] ?? MAP_CAIP_CURRENCIES.usd;\n return {\n conversions: assets.map((asset) => ({\n from: asset,\n to: currency,\n })),\n };\n }\n\n /**\n * Flattens any nested structure in the conversion rates returned by Snap.\n *\n * @param assetsConversionResponse - The conversion rates to flatten.\n * @returns A flattened rates object.\n */\n #flattenRates(\n assetsConversionResponse: OnAssetsConversionResponse,\n ): Record<CaipAssetType, AssetConversion | null> {\n const { conversionRates } = assetsConversionResponse;\n\n return Object.fromEntries(\n Object.entries(conversionRates).map(([asset, nestedObj]) => {\n // e.g., nestedObj might look like: { \"swift:0/iso4217:EUR\": { rate, conversionTime } }\n const singleValue = Object.values(nestedObj)[0];\n return [asset, singleValue];\n }),\n );\n }\n\n /**\n * Builds a rates object that covers all given assets, ensuring that\n * any asset not returned by Snap is set to null for both `rate` and `conversionTime`.\n *\n * @param assets - The assets to build the rates for.\n * @param flattenedRates - The rates to merge.\n * @returns A rates object that covers all given assets.\n */\n #buildUpdatedRates(\n assets: CaipAssetType[],\n flattenedRates: Record<CaipAssetType, AssetConversion | null>,\n ): Record<string, AssetConversion & { currency: CaipAssetType }> {\n const updatedRates: Record<\n CaipAssetType,\n AssetConversion & { currency: CaipAssetType }\n > = {};\n\n for (const asset of assets) {\n if (flattenedRates[asset]) {\n updatedRates[asset] = {\n ...(flattenedRates[asset] as AssetConversion),\n currency:\n MAP_CAIP_CURRENCIES[this.#currentCurrency] ??\n MAP_CAIP_CURRENCIES.usd,\n };\n }\n }\n return updatedRates;\n }\n\n /**\n * Merges the new rates into the controller’s state.\n *\n * @param updatedRates - The new rates to merge.\n */\n #applyUpdatedRates(\n updatedRates: Record<\n string,\n { rate: string | null; conversionTime: number | null }\n >,\n ): void {\n if (Object.keys(updatedRates).length === 0) {\n return;\n }\n this.update((state: Draft<MultichainAssetsRatesControllerState>) => {\n state.conversionRates = {\n ...state.conversionRates,\n ...updatedRates,\n };\n });\n }\n\n /**\n * Forwards a Snap request to the SnapController.\n *\n * @param args - The request parameters.\n * @param args.snapId - The ID of the Snap.\n * @param args.handler - The handler type.\n * @param args.params - The asset conversions.\n * @returns A promise that resolves with the account rates.\n */\n async #handleSnapRequest({\n snapId,\n handler,\n params,\n }: {\n snapId: SnapId;\n handler: HandlerType;\n params: OnAssetsConversionArguments | OnAssetHistoricalPriceArguments;\n }): Promise<OnAssetsConversionResponse | OnAssetHistoricalPriceResponse> {\n return this.messagingSystem.call('SnapController:handleRequest', {\n snapId,\n origin: 'metamask',\n handler,\n request: {\n jsonrpc: '2.0',\n method: handler,\n params,\n },\n }) as Promise<OnAssetsConversionResponse | OnAssetHistoricalPriceResponse>;\n }\n\n /**\n * Fetches historical prices for a specific asset using the Price API.\n *\n * @param asset - The CAIP asset type to fetch historical prices for.\n * @param currency - The currency to get prices in (defaults to current currency).\n * @returns A promise that resolves with the historical price data in OnAssetHistoricalPriceResponse format.\n */\n async getHistoricalPriceFromApi(\n asset: CaipAssetType,\n currency?: string,\n ): Promise<OnAssetHistoricalPriceResponse> {\n const timePeriodsToFetch = ['1d', '7d', '1m', '3m', '1y', '1000y'];\n\n try {\n // For each time period, call the Price API to fetch the historical prices\n const promises = timePeriodsToFetch.map(async (timePeriod) => {\n try {\n // https://price.api.cx.metamask.io/v3/historical-prices/solana:5eykt4UsFv8P8NJdTREpY1vzqKqZKvdp/slip44:501?timePeriod=1d&vsCurrency=usd\n const url = `https://price.api.cx.metamask.io/v3/historical-prices/${asset}?timePeriod=${timePeriod}&vsCurrency=${currency}`;\n console.log(\n 'MultichainAssetsRatesController.getHistoricalPriceFromApi (new internal logic gutted from snap) calling',\n url,\n );\n\n const fetchResponse = await fetch(url);\n if (!fetchResponse.ok) {\n throw new Error(`HTTP error! status: ${fetchResponse.status}`);\n }\n\n const response: HistoricalPricesApiResponse =\n await fetchResponse.json();\n\n // Add assertion similar to snap\n assertStruct(response, GetHistoricalPricesResponseStruct);\n\n return {\n timePeriod,\n response,\n };\n } catch (error) {\n console.warn(\n `Error fetching historical prices for ${asset} to ${currency} with time period ${timePeriod}`,\n error,\n );\n return {\n timePeriod,\n response: { prices: [] }, // Return empty prices on error\n };\n }\n });\n\n const wrappedHistoricalPrices = await Promise.all(promises);\n\n const intervals =\n wrappedHistoricalPrices.reduce<HistoricalPriceIntervals>(\n (acc, { timePeriod, response }) => {\n const iso8601Interval = `P${timePeriod.toUpperCase()}`;\n acc[iso8601Interval] = response.prices.map((price) => [\n price[0],\n price[1].toString(),\n ]);\n return acc;\n },\n {},\n );\n\n const now = Date.now();\n const historicalPrice = {\n intervals,\n updateTime: now,\n expirationTime: now + 1000 * 60, // 1 minute expiration\n };\n\n return {\n historicalPrice,\n };\n } catch (error) {\n console.error(\n `Failed to fetch historical prices for asset: ${asset}`,\n error,\n );\n return null;\n }\n }\n}\n"]}
|
|
1
|
+
{"version":3,"file":"MultichainAssetsRatesController.mjs","sourceRoot":"","sources":["../../src/MultichainAssetsRatesController/MultichainAssetsRatesController.ts"],"names":[],"mappings":"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","sourcesContent":["import type {\n AccountsControllerListMultichainAccountsAction,\n AccountsControllerAccountAddedEvent,\n AccountsControllerGetSelectedMultichainAccountAction,\n} from '@metamask/accounts-controller';\nimport type {\n RestrictedMessenger,\n ControllerStateChangeEvent,\n ControllerGetStateAction,\n} from '@metamask/base-controller';\nimport { type CaipAssetType, isEvmAccountType } from '@metamask/keyring-api';\nimport type {\n KeyringControllerLockEvent,\n KeyringControllerUnlockEvent,\n} from '@metamask/keyring-controller';\nimport type { InternalAccount } from '@metamask/keyring-internal-api';\nimport { StaticIntervalPollingController } from '@metamask/polling-controller';\nimport type { HandleSnapRequest } from '@metamask/snaps-controllers';\nimport type {\n SnapId,\n AssetConversion,\n OnAssetsConversionArguments,\n OnAssetsConversionResponse,\n OnAssetHistoricalPriceArguments,\n OnAssetHistoricalPriceResponse,\n HistoricalPriceIntervals,\n} from '@metamask/snaps-sdk';\nimport { HandlerType } from '@metamask/snaps-utils';\nimport { Mutex } from 'async-mutex';\nimport type { Draft } from 'immer';\n\nimport { MAP_CAIP_CURRENCIES } from './constant';\nimport type {\n CurrencyRateState,\n CurrencyRateStateChange,\n GetCurrencyRateState,\n} from '../CurrencyRateController';\nimport type {\n MultichainAssetsControllerGetStateAction,\n MultichainAssetsControllerAccountAssetListUpdatedEvent,\n MultichainAssetsControllerState,\n} from '../MultichainAssetsController';\n\n/**\n * The name of the MultichainAssetsRatesController.\n */\nconst controllerName = 'MultichainAssetsRatesController';\n\n// This is temporary until its exported from snap\ntype HistoricalPrice = {\n intervals: HistoricalPriceIntervals;\n // The UNIX timestamp of when the historical price was last updated.\n updateTime: number;\n // The UNIX timestamp of when the historical price will expire.\n expirationTime?: number;\n};\n\n/**\n * State used by the MultichainAssetsRatesController to cache token conversion rates.\n */\nexport type MultichainAssetsRatesControllerState = {\n conversionRates: Record<CaipAssetType, AssetConversion>;\n historicalPrices: Record<CaipAssetType, Record<string, HistoricalPrice>>; // string being the current currency we fetched historical prices for\n};\n\n/**\n * Returns the state of the MultichainAssetsRatesController.\n */\nexport type MultichainAssetsRatesControllerGetStateAction =\n ControllerGetStateAction<\n typeof controllerName,\n MultichainAssetsRatesControllerState\n >;\n\n/**\n * Action to update the rates of all supported tokens.\n */\nexport type MultichainAssetsRatesControllerUpdateRatesAction = {\n type: `${typeof controllerName}:updateAssetsRates`;\n handler: MultichainAssetsRatesController['updateAssetsRates'];\n};\n\n/**\n * Constructs the default {@link MultichainAssetsRatesController} state. This allows\n * consumers to provide a partial state object when initializing the controller\n * and also helps in constructing complete state objects for this controller in\n * tests.\n *\n * @returns The default {@link MultichainAssetsRatesController} state.\n */\nexport function getDefaultMultichainAssetsRatesControllerState(): MultichainAssetsRatesControllerState {\n return { conversionRates: {}, historicalPrices: {} };\n}\n\n/**\n * Event emitted when the state of the MultichainAssetsRatesController changes.\n */\nexport type MultichainAssetsRatesControllerStateChange =\n ControllerStateChangeEvent<\n typeof controllerName,\n MultichainAssetsRatesControllerState\n >;\n\n/**\n * Actions exposed by the MultichainAssetsRatesController.\n */\nexport type MultichainAssetsRatesControllerActions =\n | MultichainAssetsRatesControllerGetStateAction\n | MultichainAssetsRatesControllerUpdateRatesAction;\n\n/**\n * Events emitted by MultichainAssetsRatesController.\n */\nexport type MultichainAssetsRatesControllerEvents =\n MultichainAssetsRatesControllerStateChange;\n\n/**\n * Actions that this controller is allowed to call.\n */\nexport type AllowedActions =\n | HandleSnapRequest\n | AccountsControllerListMultichainAccountsAction\n | GetCurrencyRateState\n | MultichainAssetsControllerGetStateAction\n | AccountsControllerGetSelectedMultichainAccountAction;\n\n/**\n * Events that this controller is allowed to subscribe to.\n */\nexport type AllowedEvents =\n | KeyringControllerLockEvent\n | KeyringControllerUnlockEvent\n | AccountsControllerAccountAddedEvent\n | CurrencyRateStateChange\n | MultichainAssetsControllerAccountAssetListUpdatedEvent;\n/**\n * Messenger type for the MultichainAssetsRatesController.\n */\nexport type MultichainAssetsRatesControllerMessenger = RestrictedMessenger<\n typeof controllerName,\n MultichainAssetsRatesControllerActions | AllowedActions,\n MultichainAssetsRatesControllerEvents | AllowedEvents,\n AllowedActions['type'],\n AllowedEvents['type']\n>;\n\n/**\n * The input for starting polling in MultichainAssetsRatesController.\n */\nexport type MultichainAssetsRatesPollingInput = {\n accountId: string;\n};\n\nconst metadata = {\n conversionRates: { persist: true, anonymous: true },\n historicalPrices: { persist: false, anonymous: true },\n};\n\n/**\n * Controller that manages multichain token conversion rates.\n *\n * This controller polls for token conversion rates and updates its state.\n */\nexport class MultichainAssetsRatesController extends StaticIntervalPollingController<MultichainAssetsRatesPollingInput>()<\n typeof controllerName,\n MultichainAssetsRatesControllerState,\n MultichainAssetsRatesControllerMessenger\n> {\n readonly #mutex = new Mutex();\n\n #currentCurrency: CurrencyRateState['currentCurrency'];\n\n readonly #accountsAssets: MultichainAssetsControllerState['accountsAssets'];\n\n #isUnlocked = true;\n\n /**\n * Creates an instance of MultichainAssetsRatesController.\n *\n * @param options - Constructor options.\n * @param options.interval - The polling interval in milliseconds.\n * @param options.state - The initial state.\n * @param options.messenger - A reference to the messaging system.\n */\n constructor({\n interval = 18000,\n state = {},\n messenger,\n }: {\n interval?: number;\n state?: Partial<MultichainAssetsRatesControllerState>;\n messenger: MultichainAssetsRatesControllerMessenger;\n }) {\n super({\n name: controllerName,\n messenger,\n state: {\n ...getDefaultMultichainAssetsRatesControllerState(),\n ...state,\n },\n metadata,\n });\n\n this.setIntervalLength(interval);\n\n // Subscribe to keyring lock/unlock events.\n this.messagingSystem.subscribe('KeyringController:lock', () => {\n this.#isUnlocked = false;\n });\n this.messagingSystem.subscribe('KeyringController:unlock', () => {\n this.#isUnlocked = true;\n });\n\n ({ accountsAssets: this.#accountsAssets } = this.messagingSystem.call(\n 'MultichainAssetsController:getState',\n ));\n\n ({ currentCurrency: this.#currentCurrency } = this.messagingSystem.call(\n 'CurrencyRateController:getState',\n ));\n\n this.messagingSystem.subscribe(\n 'CurrencyRateController:stateChange',\n async (currencyRatesState: CurrencyRateState) => {\n this.#currentCurrency = currencyRatesState.currentCurrency;\n await this.updateAssetsRates();\n },\n );\n\n this.messagingSystem.subscribe(\n 'MultichainAssetsController:accountAssetListUpdated',\n async ({ assets }) => {\n const newAccountAssets = Object.entries(assets).map(\n ([accountId, { added }]) => ({\n accountId,\n assets: [...added],\n }),\n );\n // TODO; removed can be used in future for further cleanup\n await this.#updateAssetsRatesForNewAssets(newAccountAssets);\n },\n );\n }\n\n /**\n * Executes a poll by updating token conversion rates for the current account.\n *\n * @returns A promise that resolves when the polling completes.\n */\n async _executePoll(): Promise<void> {\n await this.updateAssetsRates();\n }\n\n /**\n * Determines whether the controller is active.\n *\n * @returns True if the keyring is unlocked; otherwise, false.\n */\n get isActive(): boolean {\n return this.#isUnlocked;\n }\n\n /**\n * Checks if an account is a non-EVM account with a Snap.\n *\n * @param account - The account to check.\n * @returns True if the account is non-EVM and has Snap metadata; otherwise, false.\n */\n #isNonEvmAccount(account: InternalAccount): boolean {\n return (\n !isEvmAccountType(account.type) && account.metadata.snap !== undefined\n );\n }\n\n /**\n * Retrieves all multichain accounts from the AccountsController.\n *\n * @returns An array of internal accounts.\n */\n #listMultichainAccounts(): InternalAccount[] {\n return this.messagingSystem.call(\n 'AccountsController:listMultichainAccounts',\n );\n }\n\n /**\n * Filters and returns non-EVM accounts that should have balances.\n *\n * @returns An array of non-EVM internal accounts.\n */\n #listAccounts(): InternalAccount[] {\n const accounts = this.#listMultichainAccounts();\n return accounts.filter((account) => this.#isNonEvmAccount(account));\n }\n\n /**\n * Updates token conversion rates for each non-EVM account.\n *\n * @returns A promise that resolves when the rates are updated.\n */\n async updateAssetsRates(): Promise<void> {\n const releaseLock = await this.#mutex.acquire();\n\n return (async () => {\n if (!this.isActive) {\n return;\n }\n const accounts = this.#listAccounts();\n\n for (const account of accounts) {\n const assets = this.#getAssetsForAccount(account.id);\n\n if (assets?.length === 0) {\n continue;\n }\n\n const rates = await this.#getUpdatedRatesFor(account, assets);\n // Apply these updated rates to controller state\n this.#applyUpdatedRates(rates);\n }\n })().finally(() => {\n releaseLock();\n });\n }\n\n async #getUpdatedRatesFor(\n account: InternalAccount,\n assets: CaipAssetType[],\n ): Promise<Record<string, AssetConversion & { currency: CaipAssetType }>> {\n // Build the conversions array\n const conversions = this.#buildConversions(assets);\n\n // Retrieve rates from Snap\n const accountRates: OnAssetsConversionResponse =\n (await this.#handleSnapRequest({\n snapId: account?.metadata.snap?.id as SnapId,\n handler: HandlerType.OnAssetsConversion,\n params: {\n ...conversions,\n includeMarketData: true,\n },\n })) as OnAssetsConversionResponse;\n\n // Flatten nested rates if needed\n const flattenedRates = this.#flattenRates(accountRates);\n\n // Build the updatedRates object for these assets\n const updatedRates = this.#buildUpdatedRates(assets, flattenedRates);\n return updatedRates;\n }\n\n /**\n * Fetches historical prices for the current account\n *\n * @param asset - The asset to fetch historical prices for.\n * @param account - optional account to fetch historical prices for\n * @returns The historical prices.\n */\n async fetchHistoricalPricesForAsset(\n asset: CaipAssetType,\n account?: InternalAccount,\n ): Promise<void> {\n const releaseLock = await this.#mutex.acquire();\n return (async () => {\n const currentCaipCurrency =\n MAP_CAIP_CURRENCIES[this.#currentCurrency] ?? MAP_CAIP_CURRENCIES.usd;\n // Check if we already have historical prices for this asset and currency\n const historicalPriceExpirationTime =\n this.state.historicalPrices[asset]?.[this.#currentCurrency]\n ?.expirationTime;\n\n const historicalPriceHasExpired =\n historicalPriceExpirationTime &&\n historicalPriceExpirationTime < Date.now();\n\n if (historicalPriceHasExpired === false) {\n return;\n }\n\n const selectedAccount =\n account ??\n this.messagingSystem.call(\n 'AccountsController:getSelectedMultichainAccount',\n );\n try {\n const historicalPricesResponse = await this.#handleSnapRequest({\n snapId: selectedAccount?.metadata.snap?.id as SnapId,\n handler: HandlerType.OnAssetHistoricalPrice,\n params: {\n from: asset,\n to: currentCaipCurrency,\n },\n });\n\n // skip state update if no historical prices are returned\n if (!historicalPricesResponse) {\n return;\n }\n\n this.update((state) => {\n state.historicalPrices = {\n ...state.historicalPrices,\n [asset]: {\n ...state.historicalPrices[asset],\n [this.#currentCurrency]: (\n historicalPricesResponse as OnAssetHistoricalPriceResponse\n )?.historicalPrice,\n },\n };\n });\n } catch {\n throw new Error(\n `Failed to fetch historical prices for asset: ${asset}`,\n );\n }\n })().finally(() => {\n releaseLock();\n });\n }\n\n /**\n * Updates the conversion rates for new assets.\n *\n * @param accounts - The accounts to update the conversion rates for.\n * @returns A promise that resolves when the rates are updated.\n */\n async #updateAssetsRatesForNewAssets(\n accounts: {\n accountId: string;\n assets: CaipAssetType[];\n }[],\n ): Promise<void> {\n const releaseLock = await this.#mutex.acquire();\n\n return (async () => {\n if (!this.isActive) {\n return;\n }\n const allNewRates: Record<\n string,\n { rate: string | null; conversionTime: number | null }\n > = {};\n\n for (const { accountId, assets } of accounts) {\n const account = this.#getAccount(accountId);\n\n const rates = await this.#getUpdatedRatesFor(account, assets);\n // Track new rates\n for (const [asset, rate] of Object.entries(rates)) {\n allNewRates[asset] = rate;\n }\n }\n\n this.#applyUpdatedRates(allNewRates);\n })().finally(() => {\n releaseLock();\n });\n }\n\n /**\n * Get a non-EVM account from its ID.\n *\n * @param accountId - The account ID.\n * @returns The non-EVM account.\n */\n #getAccount(accountId: string): InternalAccount {\n const account: InternalAccount | undefined = this.#listAccounts().find(\n (multichainAccount) => multichainAccount.id === accountId,\n );\n\n if (!account) {\n throw new Error(`Unknown account: ${accountId}`);\n }\n\n return account;\n }\n\n /**\n * Returns the array of CAIP-19 assets for the given account ID.\n * If none are found, returns an empty array.\n *\n * @param accountId - The account ID to get the assets for.\n * @returns An array of CAIP-19 assets.\n */\n #getAssetsForAccount(accountId: string): CaipAssetType[] {\n return this.#accountsAssets?.[accountId] ?? [];\n }\n\n /**\n * Builds a conversions array (from each asset → the current currency).\n *\n * @param assets - The assets to build the conversions for.\n * @returns A conversions array.\n */\n #buildConversions(assets: CaipAssetType[]): OnAssetsConversionArguments {\n const currency =\n MAP_CAIP_CURRENCIES[this.#currentCurrency] ?? MAP_CAIP_CURRENCIES.usd;\n return {\n conversions: assets.map((asset) => ({\n from: asset,\n to: currency,\n })),\n };\n }\n\n /**\n * Flattens any nested structure in the conversion rates returned by Snap.\n *\n * @param assetsConversionResponse - The conversion rates to flatten.\n * @returns A flattened rates object.\n */\n #flattenRates(\n assetsConversionResponse: OnAssetsConversionResponse,\n ): Record<CaipAssetType, AssetConversion | null> {\n const { conversionRates } = assetsConversionResponse;\n\n return Object.fromEntries(\n Object.entries(conversionRates).map(([asset, nestedObj]) => {\n // e.g., nestedObj might look like: { \"swift:0/iso4217:EUR\": { rate, conversionTime } }\n const singleValue = Object.values(nestedObj)[0];\n return [asset, singleValue];\n }),\n );\n }\n\n /**\n * Builds a rates object that covers all given assets, ensuring that\n * any asset not returned by Snap is set to null for both `rate` and `conversionTime`.\n *\n * @param assets - The assets to build the rates for.\n * @param flattenedRates - The rates to merge.\n * @returns A rates object that covers all given assets.\n */\n #buildUpdatedRates(\n assets: CaipAssetType[],\n flattenedRates: Record<CaipAssetType, AssetConversion | null>,\n ): Record<string, AssetConversion & { currency: CaipAssetType }> {\n const updatedRates: Record<\n CaipAssetType,\n AssetConversion & { currency: CaipAssetType }\n > = {};\n\n for (const asset of assets) {\n if (flattenedRates[asset]) {\n updatedRates[asset] = {\n ...(flattenedRates[asset] as AssetConversion),\n currency:\n MAP_CAIP_CURRENCIES[this.#currentCurrency] ??\n MAP_CAIP_CURRENCIES.usd,\n };\n }\n }\n return updatedRates;\n }\n\n /**\n * Merges the new rates into the controller’s state.\n *\n * @param updatedRates - The new rates to merge.\n */\n #applyUpdatedRates(\n updatedRates: Record<\n string,\n { rate: string | null; conversionTime: number | null }\n >,\n ): void {\n if (Object.keys(updatedRates).length === 0) {\n return;\n }\n this.update((state: Draft<MultichainAssetsRatesControllerState>) => {\n state.conversionRates = {\n ...state.conversionRates,\n ...updatedRates,\n };\n });\n }\n\n /**\n * Forwards a Snap request to the SnapController.\n *\n * @param args - The request parameters.\n * @param args.snapId - The ID of the Snap.\n * @param args.handler - The handler type.\n * @param args.params - The asset conversions.\n * @returns A promise that resolves with the account rates.\n */\n async #handleSnapRequest({\n snapId,\n handler,\n params,\n }: {\n snapId: SnapId;\n handler: HandlerType;\n params: OnAssetsConversionArguments | OnAssetHistoricalPriceArguments;\n }): Promise<OnAssetsConversionResponse | OnAssetHistoricalPriceResponse> {\n return this.messagingSystem.call('SnapController:handleRequest', {\n snapId,\n origin: 'metamask',\n handler,\n request: {\n jsonrpc: '2.0',\n method: handler,\n params,\n },\n }) as Promise<OnAssetsConversionResponse | OnAssetHistoricalPriceResponse>;\n }\n}\n"]}
|
|
@@ -211,7 +211,12 @@ class TokenBalancesController extends (0, polling_controller_1.StaticIntervalPol
|
|
|
211
211
|
const { value } = res;
|
|
212
212
|
const { accountAddress, tokenAddress } = accountTokenPairs[i];
|
|
213
213
|
const currentTokenBalanceValueForAccount = currentTokenBalances.tokenBalances?.[accountAddress]?.[chainId]?.[tokenAddress];
|
|
214
|
-
|
|
214
|
+
// `value` can be null or undefined if the multicall failed due to RPC issue.
|
|
215
|
+
// Please see packages/assets-controllers/src/multicall.ts#L365.
|
|
216
|
+
// Hence we should not update the balance in that case.
|
|
217
|
+
const isTokenBalanceValueChanged = res.success && value !== undefined && value !== null
|
|
218
|
+
? currentTokenBalanceValueForAccount !== (0, controller_utils_1.toHex)(value)
|
|
219
|
+
: false;
|
|
215
220
|
return {
|
|
216
221
|
...res,
|
|
217
222
|
isTokenBalanceValueChanged,
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"TokenBalancesController.cjs","sourceRoot":"","sources":["../src/TokenBalancesController.ts"],"names":[],"mappings":"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","sourcesContent":["import { Contract } from '@ethersproject/contracts';\nimport { Web3Provider } from '@ethersproject/providers';\nimport type {\n AccountsControllerGetSelectedAccountAction,\n AccountsControllerListAccountsAction,\n} from '@metamask/accounts-controller';\nimport type {\n RestrictedMessenger,\n ControllerGetStateAction,\n ControllerStateChangeEvent,\n} from '@metamask/base-controller';\nimport {\n isValidHexAddress,\n toChecksumHexAddress,\n toHex,\n} from '@metamask/controller-utils';\nimport type { KeyringControllerAccountRemovedEvent } from '@metamask/keyring-controller';\nimport { abiERC20 } from '@metamask/metamask-eth-abis';\nimport type {\n NetworkControllerGetNetworkClientByIdAction,\n NetworkControllerGetStateAction,\n NetworkControllerStateChangeEvent,\n NetworkState,\n} from '@metamask/network-controller';\nimport { StaticIntervalPollingController } from '@metamask/polling-controller';\nimport type {\n PreferencesControllerGetStateAction,\n PreferencesControllerStateChangeEvent,\n PreferencesState,\n} from '@metamask/preferences-controller';\nimport { isStrictHexString, type Hex } from '@metamask/utils';\nimport type BN from 'bn.js';\nimport type { Patch } from 'immer';\nimport { isEqual } from 'lodash';\n\nimport type { MulticallResult } from './multicall';\nimport { multicallOrFallback } from './multicall';\nimport type { Token } from './TokenRatesController';\nimport type {\n TokensControllerGetStateAction,\n TokensControllerState,\n TokensControllerStateChangeEvent,\n} from './TokensController';\n\nconst DEFAULT_INTERVAL = 180000;\n\nconst controllerName = 'TokenBalancesController';\n\nconst metadata = {\n tokenBalances: { persist: true, anonymous: false },\n};\n\n/**\n * Token balances controller options\n * @property interval - Polling interval used to fetch new token balances.\n * @property messenger - A messenger.\n * @property state - Initial state for the controller.\n */\ntype TokenBalancesControllerOptions = {\n interval?: number;\n messenger: TokenBalancesControllerMessenger;\n state?: Partial<TokenBalancesControllerState>;\n};\n\n/**\n * A mapping from account address to chain id to token address to balance.\n */\ntype TokenBalances = Record<Hex, Record<Hex, Record<Hex, Hex>>>;\n\n/**\n * Token balances controller state\n * @property tokenBalances - A mapping from account address to chain id to token address to balance.\n */\nexport type TokenBalancesControllerState = {\n tokenBalances: TokenBalances;\n};\n\nexport type TokenBalancesControllerGetStateAction = ControllerGetStateAction<\n typeof controllerName,\n TokenBalancesControllerState\n>;\n\nexport type TokenBalancesControllerActions =\n TokenBalancesControllerGetStateAction;\n\nexport type AllowedActions =\n | NetworkControllerGetNetworkClientByIdAction\n | NetworkControllerGetStateAction\n | TokensControllerGetStateAction\n | PreferencesControllerGetStateAction\n | AccountsControllerGetSelectedAccountAction\n | AccountsControllerListAccountsAction;\n\nexport type TokenBalancesControllerStateChangeEvent =\n ControllerStateChangeEvent<\n typeof controllerName,\n TokenBalancesControllerState\n >;\n\nexport type TokenBalancesControllerEvents =\n TokenBalancesControllerStateChangeEvent;\n\nexport type AllowedEvents =\n | TokensControllerStateChangeEvent\n | PreferencesControllerStateChangeEvent\n | NetworkControllerStateChangeEvent\n | KeyringControllerAccountRemovedEvent;\n\nexport type TokenBalancesControllerMessenger = RestrictedMessenger<\n typeof controllerName,\n TokenBalancesControllerActions | AllowedActions,\n TokenBalancesControllerEvents | AllowedEvents,\n AllowedActions['type'],\n AllowedEvents['type']\n>;\n\n/**\n * Get the default TokenBalancesController state.\n *\n * @returns The default TokenBalancesController state.\n */\nexport function getDefaultTokenBalancesState(): TokenBalancesControllerState {\n return {\n tokenBalances: {},\n };\n}\n\n/** The input to start polling for the {@link TokenBalancesController} */\nexport type TokenBalancesPollingInput = {\n chainId: Hex;\n};\n\n/**\n * Controller that passively polls on a set interval token balances\n * for tokens stored in the TokensController\n */\nexport class TokenBalancesController extends StaticIntervalPollingController<TokenBalancesPollingInput>()<\n typeof controllerName,\n TokenBalancesControllerState,\n TokenBalancesControllerMessenger\n> {\n #queryMultipleAccounts: boolean;\n\n #allTokens: TokensControllerState['allTokens'];\n\n #allDetectedTokens: TokensControllerState['allDetectedTokens'];\n\n /**\n * Construct a Token Balances Controller.\n *\n * @param options - The controller options.\n * @param options.interval - Polling interval used to fetch new token balances.\n * @param options.state - Initial state to set on this controller.\n * @param options.messenger - The controller restricted messenger.\n */\n constructor({\n interval = DEFAULT_INTERVAL,\n messenger,\n state = {},\n }: TokenBalancesControllerOptions) {\n super({\n name: controllerName,\n metadata,\n messenger,\n state: {\n ...getDefaultTokenBalancesState(),\n ...state,\n },\n });\n\n this.setIntervalLength(interval);\n\n // Set initial preference for querying multiple accounts, and subscribe to changes\n this.#queryMultipleAccounts = this.#calculateQueryMultipleAccounts(\n this.messagingSystem.call('PreferencesController:getState'),\n );\n this.messagingSystem.subscribe(\n 'PreferencesController:stateChange',\n this.#onPreferencesStateChange.bind(this),\n );\n\n // Set initial tokens, and subscribe to changes\n ({\n allTokens: this.#allTokens,\n allDetectedTokens: this.#allDetectedTokens,\n } = this.messagingSystem.call('TokensController:getState'));\n\n this.messagingSystem.subscribe(\n 'TokensController:stateChange',\n this.#onTokensStateChange.bind(this),\n );\n\n // Subscribe to network state changes\n this.messagingSystem.subscribe(\n 'NetworkController:stateChange',\n this.#onNetworkStateChange.bind(this),\n );\n\n // subscribe to account removed event to cleanup stale balances\n\n this.messagingSystem.subscribe(\n 'KeyringController:accountRemoved',\n (accountAddress: string) => this.#handleOnAccountRemoved(accountAddress),\n );\n }\n\n /**\n * Determines whether to query all accounts, or just the selected account.\n * @param preferences - The preferences state.\n * @param preferences.isMultiAccountBalancesEnabled - whether to query all accounts (mobile).\n * @param preferences.useMultiAccountBalanceChecker - whether to query all accounts (extension).\n * @returns true if all accounts should be queried.\n */\n #calculateQueryMultipleAccounts = ({\n isMultiAccountBalancesEnabled,\n useMultiAccountBalanceChecker,\n }: PreferencesState & { useMultiAccountBalanceChecker?: boolean }) => {\n return Boolean(\n // Note: These settings have different names on extension vs mobile\n isMultiAccountBalancesEnabled || useMultiAccountBalanceChecker,\n );\n };\n\n /**\n * Handles the event for preferences state changes.\n * @param preferences - The preferences state.\n */\n #onPreferencesStateChange = (preferences: PreferencesState) => {\n // Update the user preference for whether to query multiple accounts.\n const queryMultipleAccounts =\n this.#calculateQueryMultipleAccounts(preferences);\n\n // Refresh when flipped off -> on\n const refresh = queryMultipleAccounts && !this.#queryMultipleAccounts;\n this.#queryMultipleAccounts = queryMultipleAccounts;\n\n if (refresh) {\n this.updateBalances().catch(console.error);\n }\n };\n\n /**\n * Handles the event for tokens state changes.\n * @param state - The token state.\n * @param state.allTokens - The state for imported tokens across all chains.\n * @param state.allDetectedTokens - The state for detected tokens across all chains.\n */\n #onTokensStateChange = ({\n allTokens,\n allDetectedTokens,\n }: TokensControllerState) => {\n // Refresh token balances on chains whose tokens have changed.\n const chainIds = this.#getChainIds(allTokens, allDetectedTokens);\n const chainIdsToUpdate = chainIds.filter(\n (chainId) =>\n !isEqual(this.#allTokens[chainId], allTokens[chainId]) ||\n !isEqual(this.#allDetectedTokens[chainId], allDetectedTokens[chainId]),\n );\n\n this.#allTokens = allTokens;\n this.#allDetectedTokens = allDetectedTokens;\n this.#handleTokensControllerStateChange({\n chainIds: chainIdsToUpdate,\n }).catch(console.error);\n };\n\n /**\n * Handles the event for network state changes.\n * @param _ - The network state.\n * @param patches - An array of patch operations performed on the network state.\n */\n #onNetworkStateChange(_: NetworkState, patches: Patch[]) {\n // Remove state for deleted networks\n for (const patch of patches) {\n if (\n patch.op === 'remove' &&\n patch.path[0] === 'networkConfigurationsByChainId'\n ) {\n const removedChainId = patch.path[1] as Hex;\n\n this.update((state) => {\n for (const accountAddress of Object.keys(state.tokenBalances)) {\n delete state.tokenBalances[accountAddress as Hex][removedChainId];\n }\n });\n }\n }\n }\n\n /**\n * Handles changes when an account has been removed.\n *\n * @param accountAddress - The account address being removed.\n */\n #handleOnAccountRemoved(accountAddress: string) {\n const isEthAddress =\n isStrictHexString(accountAddress.toLowerCase()) &&\n isValidHexAddress(accountAddress);\n if (!isEthAddress) {\n return;\n }\n\n this.update((state) => {\n delete state.tokenBalances[accountAddress as `0x${string}`];\n });\n }\n\n /**\n * Returns an array of chain ids that have tokens.\n * @param allTokens - The state for imported tokens across all chains.\n * @param allDetectedTokens - The state for detected tokens across all chains.\n * @returns An array of chain ids that have tokens.\n */\n #getChainIds = (\n allTokens: TokensControllerState['allTokens'],\n allDetectedTokens: TokensControllerState['allDetectedTokens'],\n ) =>\n [\n ...new Set([\n ...Object.keys(allTokens),\n ...Object.keys(allDetectedTokens),\n ]),\n ] as Hex[];\n\n /**\n * Polls for erc20 token balances.\n * @param input - The input for the poll.\n * @param input.chainId - The chain id to poll token balances on.\n */\n async _executePoll({ chainId }: TokenBalancesPollingInput) {\n await this.updateBalancesByChainId({ chainId });\n }\n\n /**\n * Updates the token balances for the given chain ids.\n * @param input - The input for the update.\n * @param input.chainIds - The chain ids to update token balances for.\n * Or omitted to update all chains that contain tokens.\n */\n async updateBalances({ chainIds }: { chainIds?: Hex[] } = {}) {\n chainIds ??= this.#getChainIds(this.#allTokens, this.#allDetectedTokens);\n\n await Promise.allSettled(\n chainIds.map((chainId) => this.updateBalancesByChainId({ chainId })),\n );\n }\n\n async #handleTokensControllerStateChange({\n chainIds,\n }: { chainIds?: Hex[] } = {}) {\n const currentTokenBalancesState = this.messagingSystem.call(\n 'TokenBalancesController:getState',\n );\n const currentTokenBalances = currentTokenBalancesState.tokenBalances;\n const currentAllTokens = this.#allTokens;\n const chainIdsSet = new Set(chainIds);\n\n // first we check if the state change was due to a token being removed\n for (const currentAccount of Object.keys(currentTokenBalances)) {\n const allChains = currentTokenBalances[currentAccount as `0x${string}`];\n for (const currentChain of Object.keys(allChains)) {\n if (chainIds?.length && !chainIdsSet.has(currentChain as Hex)) {\n continue;\n }\n const tokensObject = allChains[currentChain as Hex];\n const allCurrentTokens = Object.keys(tokensObject);\n const existingTokensInState =\n currentAllTokens[currentChain as Hex]?.[\n currentAccount as `0x${string}`\n ] || [];\n const existingSet = new Set(\n existingTokensInState.map((elm) => elm.address),\n );\n\n for (const singleToken of allCurrentTokens) {\n if (!existingSet.has(singleToken)) {\n this.update((state) => {\n delete state.tokenBalances[currentAccount as Hex][\n currentChain as Hex\n ][singleToken as `0x${string}`];\n });\n }\n }\n }\n }\n\n // then we check if the state change was due to a token being added\n let shouldUpdate = false;\n for (const currentChain of Object.keys(currentAllTokens)) {\n if (chainIds?.length && !chainIdsSet.has(currentChain as Hex)) {\n continue;\n }\n const accountsPerChain = currentAllTokens[currentChain as Hex];\n\n for (const currentAccount of Object.keys(accountsPerChain)) {\n const tokensList = accountsPerChain[currentAccount as `0x${string}`];\n const tokenBalancesObject =\n currentTokenBalances[currentAccount as `0x${string}`]?.[\n currentChain as Hex\n ] || {};\n for (const singleToken of tokensList) {\n if (!tokenBalancesObject?.[singleToken.address as `0x${string}`]) {\n shouldUpdate = true;\n break;\n }\n }\n }\n }\n if (shouldUpdate) {\n await this.updateBalances({ chainIds }).catch(console.error);\n }\n }\n\n /**\n * Get an Ethers.js Web3Provider for the requested chain.\n *\n * @param chainId - The chain id to get the provider for.\n * @returns The provider for the given chain id.\n */\n #getProvider(chainId: Hex): Web3Provider {\n return new Web3Provider(this.#getNetworkClient(chainId).provider);\n }\n\n /**\n * Internal util: run `balanceOf` for an arbitrary set of account/token pairs.\n *\n * @param params - The parameters for the balance fetch.\n * @param params.chainId - The chain id to fetch balances on.\n * @param params.pairs - The account/token pairs to fetch balances for.\n * @returns The balances for the given token addresses.\n */\n async #batchBalanceOf({\n chainId,\n pairs,\n }: {\n chainId: Hex;\n pairs: { accountAddress: Hex; tokenAddress: Hex }[];\n }): Promise<MulticallResult[]> {\n if (!pairs.length) {\n return [];\n }\n\n const provider = this.#getProvider(chainId);\n\n const calls = pairs.map(({ accountAddress, tokenAddress }) => ({\n contract: new Contract(tokenAddress, abiERC20, provider),\n functionSignature: 'balanceOf(address)',\n arguments: [accountAddress],\n }));\n\n return multicallOrFallback(calls, chainId, provider);\n }\n\n /**\n * Returns ERC-20 balances for a single account on a single chain.\n *\n * @param params - The parameters for the balance fetch.\n * @param params.chainId - The chain id to fetch balances on.\n * @param params.accountAddress - The account address to fetch balances for.\n * @param params.tokenAddresses - The token addresses to fetch balances for.\n * @returns A mapping from token address to balance (hex) | null.\n */\n async getErc20Balances({\n chainId,\n accountAddress,\n tokenAddresses,\n }: {\n chainId: Hex;\n accountAddress: Hex;\n tokenAddresses: Hex[];\n }): Promise<Record<Hex, Hex | null>> {\n // Return early if no token addresses provided\n if (tokenAddresses.length === 0) {\n return {};\n }\n\n const pairs = tokenAddresses.map((tokenAddress) => ({\n accountAddress,\n tokenAddress,\n }));\n\n const results = await this.#batchBalanceOf({ chainId, pairs });\n\n const balances: Record<Hex, Hex | null> = {};\n tokenAddresses.forEach((tokenAddress, i) => {\n balances[tokenAddress] = results[i]?.success\n ? toHex(results[i].value as BN)\n : null;\n });\n\n return balances;\n }\n\n /**\n * Updates token balances for the given chain id.\n * @param input - The input for the update.\n * @param input.chainId - The chain id to update token balances on.\n */\n async updateBalancesByChainId({ chainId }: { chainId: Hex }) {\n const { address: selectedAccountAddress } = this.messagingSystem.call(\n 'AccountsController:getSelectedAccount',\n );\n\n const isSelectedAccount = (accountAddress: string) =>\n toChecksumHexAddress(accountAddress) ===\n toChecksumHexAddress(selectedAccountAddress);\n\n const accountTokenPairs: { accountAddress: Hex; tokenAddress: Hex }[] = [];\n\n const addTokens = ([accountAddress, tokens]: [string, Token[]]) =>\n this.#queryMultipleAccounts || isSelectedAccount(accountAddress)\n ? tokens.forEach((t) =>\n accountTokenPairs.push({\n accountAddress: accountAddress as Hex,\n tokenAddress: t.address as Hex,\n }),\n )\n : undefined;\n\n // Balances will be updated for both imported and detected tokens\n Object.entries(this.#allTokens[chainId] ?? {}).forEach(addTokens);\n Object.entries(this.#allDetectedTokens[chainId] ?? {}).forEach(addTokens);\n\n let results: MulticallResult[] = [];\n\n const currentTokenBalances = this.messagingSystem.call(\n 'TokenBalancesController:getState',\n );\n\n if (accountTokenPairs.length > 0) {\n results = await this.#batchBalanceOf({\n chainId,\n pairs: accountTokenPairs,\n });\n }\n\n const updatedResults: (MulticallResult & {\n isTokenBalanceValueChanged?: boolean;\n })[] = results.map((res, i) => {\n const { value } = res;\n const { accountAddress, tokenAddress } = accountTokenPairs[i];\n const currentTokenBalanceValueForAccount =\n currentTokenBalances.tokenBalances?.[accountAddress]?.[chainId]?.[\n tokenAddress\n ];\n const isTokenBalanceValueChanged =\n currentTokenBalanceValueForAccount !== toHex(value as BN);\n return {\n ...res,\n isTokenBalanceValueChanged,\n };\n });\n\n // if all values of isTokenBalanceValueChanged are false, return\n if (updatedResults.every((result) => !result.isTokenBalanceValueChanged)) {\n return;\n }\n\n this.update((state) => {\n for (let i = 0; i < updatedResults.length; i++) {\n const { success, value, isTokenBalanceValueChanged } =\n updatedResults[i];\n const { accountAddress, tokenAddress } = accountTokenPairs[i];\n if (success && isTokenBalanceValueChanged) {\n ((state.tokenBalances[accountAddress] ??= {})[chainId] ??= {})[\n tokenAddress\n ] = toHex(value as BN);\n }\n }\n });\n }\n\n /**\n * Reset the controller state to the default state.\n */\n resetState() {\n this.update(() => {\n return getDefaultTokenBalancesState();\n });\n }\n\n /**\n * Returns the network client for a given chain id\n * @param chainId - The chain id to get the network client for.\n * @returns The network client for the given chain id.\n */\n #getNetworkClient(chainId: Hex) {\n const { networkConfigurationsByChainId } = this.messagingSystem.call(\n 'NetworkController:getState',\n );\n\n const networkConfiguration = networkConfigurationsByChainId[chainId];\n if (!networkConfiguration) {\n throw new Error(\n `TokenBalancesController: No network configuration found for chainId ${chainId}`,\n );\n }\n\n const { networkClientId } =\n networkConfiguration.rpcEndpoints[\n networkConfiguration.defaultRpcEndpointIndex\n ];\n\n return this.messagingSystem.call(\n `NetworkController:getNetworkClientById`,\n networkClientId,\n );\n }\n}\n\nexport default TokenBalancesController;\n"]}
|
|
1
|
+
{"version":3,"file":"TokenBalancesController.cjs","sourceRoot":"","sources":["../src/TokenBalancesController.ts"],"names":[],"mappings":"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","sourcesContent":["import { Contract } from '@ethersproject/contracts';\nimport { Web3Provider } from '@ethersproject/providers';\nimport type {\n AccountsControllerGetSelectedAccountAction,\n AccountsControllerListAccountsAction,\n} from '@metamask/accounts-controller';\nimport type {\n RestrictedMessenger,\n ControllerGetStateAction,\n ControllerStateChangeEvent,\n} from '@metamask/base-controller';\nimport {\n isValidHexAddress,\n toChecksumHexAddress,\n toHex,\n} from '@metamask/controller-utils';\nimport type { KeyringControllerAccountRemovedEvent } from '@metamask/keyring-controller';\nimport { abiERC20 } from '@metamask/metamask-eth-abis';\nimport type {\n NetworkControllerGetNetworkClientByIdAction,\n NetworkControllerGetStateAction,\n NetworkControllerStateChangeEvent,\n NetworkState,\n} from '@metamask/network-controller';\nimport { StaticIntervalPollingController } from '@metamask/polling-controller';\nimport type {\n PreferencesControllerGetStateAction,\n PreferencesControllerStateChangeEvent,\n PreferencesState,\n} from '@metamask/preferences-controller';\nimport { isStrictHexString, type Hex } from '@metamask/utils';\nimport type BN from 'bn.js';\nimport type { Patch } from 'immer';\nimport { isEqual } from 'lodash';\n\nimport type { MulticallResult } from './multicall';\nimport { multicallOrFallback } from './multicall';\nimport type { Token } from './TokenRatesController';\nimport type {\n TokensControllerGetStateAction,\n TokensControllerState,\n TokensControllerStateChangeEvent,\n} from './TokensController';\n\nconst DEFAULT_INTERVAL = 180000;\n\nconst controllerName = 'TokenBalancesController';\n\nconst metadata = {\n tokenBalances: { persist: true, anonymous: false },\n};\n\n/**\n * Token balances controller options\n * @property interval - Polling interval used to fetch new token balances.\n * @property messenger - A messenger.\n * @property state - Initial state for the controller.\n */\ntype TokenBalancesControllerOptions = {\n interval?: number;\n messenger: TokenBalancesControllerMessenger;\n state?: Partial<TokenBalancesControllerState>;\n};\n\n/**\n * A mapping from account address to chain id to token address to balance.\n */\ntype TokenBalances = Record<Hex, Record<Hex, Record<Hex, Hex>>>;\n\n/**\n * Token balances controller state\n * @property tokenBalances - A mapping from account address to chain id to token address to balance.\n */\nexport type TokenBalancesControllerState = {\n tokenBalances: TokenBalances;\n};\n\nexport type TokenBalancesControllerGetStateAction = ControllerGetStateAction<\n typeof controllerName,\n TokenBalancesControllerState\n>;\n\nexport type TokenBalancesControllerActions =\n TokenBalancesControllerGetStateAction;\n\nexport type AllowedActions =\n | NetworkControllerGetNetworkClientByIdAction\n | NetworkControllerGetStateAction\n | TokensControllerGetStateAction\n | PreferencesControllerGetStateAction\n | AccountsControllerGetSelectedAccountAction\n | AccountsControllerListAccountsAction;\n\nexport type TokenBalancesControllerStateChangeEvent =\n ControllerStateChangeEvent<\n typeof controllerName,\n TokenBalancesControllerState\n >;\n\nexport type TokenBalancesControllerEvents =\n TokenBalancesControllerStateChangeEvent;\n\nexport type AllowedEvents =\n | TokensControllerStateChangeEvent\n | PreferencesControllerStateChangeEvent\n | NetworkControllerStateChangeEvent\n | KeyringControllerAccountRemovedEvent;\n\nexport type TokenBalancesControllerMessenger = RestrictedMessenger<\n typeof controllerName,\n TokenBalancesControllerActions | AllowedActions,\n TokenBalancesControllerEvents | AllowedEvents,\n AllowedActions['type'],\n AllowedEvents['type']\n>;\n\n/**\n * Get the default TokenBalancesController state.\n *\n * @returns The default TokenBalancesController state.\n */\nexport function getDefaultTokenBalancesState(): TokenBalancesControllerState {\n return {\n tokenBalances: {},\n };\n}\n\n/** The input to start polling for the {@link TokenBalancesController} */\nexport type TokenBalancesPollingInput = {\n chainId: Hex;\n};\n\n/**\n * Controller that passively polls on a set interval token balances\n * for tokens stored in the TokensController\n */\nexport class TokenBalancesController extends StaticIntervalPollingController<TokenBalancesPollingInput>()<\n typeof controllerName,\n TokenBalancesControllerState,\n TokenBalancesControllerMessenger\n> {\n #queryMultipleAccounts: boolean;\n\n #allTokens: TokensControllerState['allTokens'];\n\n #allDetectedTokens: TokensControllerState['allDetectedTokens'];\n\n /**\n * Construct a Token Balances Controller.\n *\n * @param options - The controller options.\n * @param options.interval - Polling interval used to fetch new token balances.\n * @param options.state - Initial state to set on this controller.\n * @param options.messenger - The controller restricted messenger.\n */\n constructor({\n interval = DEFAULT_INTERVAL,\n messenger,\n state = {},\n }: TokenBalancesControllerOptions) {\n super({\n name: controllerName,\n metadata,\n messenger,\n state: {\n ...getDefaultTokenBalancesState(),\n ...state,\n },\n });\n\n this.setIntervalLength(interval);\n\n // Set initial preference for querying multiple accounts, and subscribe to changes\n this.#queryMultipleAccounts = this.#calculateQueryMultipleAccounts(\n this.messagingSystem.call('PreferencesController:getState'),\n );\n this.messagingSystem.subscribe(\n 'PreferencesController:stateChange',\n this.#onPreferencesStateChange.bind(this),\n );\n\n // Set initial tokens, and subscribe to changes\n ({\n allTokens: this.#allTokens,\n allDetectedTokens: this.#allDetectedTokens,\n } = this.messagingSystem.call('TokensController:getState'));\n\n this.messagingSystem.subscribe(\n 'TokensController:stateChange',\n this.#onTokensStateChange.bind(this),\n );\n\n // Subscribe to network state changes\n this.messagingSystem.subscribe(\n 'NetworkController:stateChange',\n this.#onNetworkStateChange.bind(this),\n );\n\n // subscribe to account removed event to cleanup stale balances\n\n this.messagingSystem.subscribe(\n 'KeyringController:accountRemoved',\n (accountAddress: string) => this.#handleOnAccountRemoved(accountAddress),\n );\n }\n\n /**\n * Determines whether to query all accounts, or just the selected account.\n * @param preferences - The preferences state.\n * @param preferences.isMultiAccountBalancesEnabled - whether to query all accounts (mobile).\n * @param preferences.useMultiAccountBalanceChecker - whether to query all accounts (extension).\n * @returns true if all accounts should be queried.\n */\n #calculateQueryMultipleAccounts = ({\n isMultiAccountBalancesEnabled,\n useMultiAccountBalanceChecker,\n }: PreferencesState & { useMultiAccountBalanceChecker?: boolean }) => {\n return Boolean(\n // Note: These settings have different names on extension vs mobile\n isMultiAccountBalancesEnabled || useMultiAccountBalanceChecker,\n );\n };\n\n /**\n * Handles the event for preferences state changes.\n * @param preferences - The preferences state.\n */\n #onPreferencesStateChange = (preferences: PreferencesState) => {\n // Update the user preference for whether to query multiple accounts.\n const queryMultipleAccounts =\n this.#calculateQueryMultipleAccounts(preferences);\n\n // Refresh when flipped off -> on\n const refresh = queryMultipleAccounts && !this.#queryMultipleAccounts;\n this.#queryMultipleAccounts = queryMultipleAccounts;\n\n if (refresh) {\n this.updateBalances().catch(console.error);\n }\n };\n\n /**\n * Handles the event for tokens state changes.\n * @param state - The token state.\n * @param state.allTokens - The state for imported tokens across all chains.\n * @param state.allDetectedTokens - The state for detected tokens across all chains.\n */\n #onTokensStateChange = ({\n allTokens,\n allDetectedTokens,\n }: TokensControllerState) => {\n // Refresh token balances on chains whose tokens have changed.\n const chainIds = this.#getChainIds(allTokens, allDetectedTokens);\n const chainIdsToUpdate = chainIds.filter(\n (chainId) =>\n !isEqual(this.#allTokens[chainId], allTokens[chainId]) ||\n !isEqual(this.#allDetectedTokens[chainId], allDetectedTokens[chainId]),\n );\n\n this.#allTokens = allTokens;\n this.#allDetectedTokens = allDetectedTokens;\n this.#handleTokensControllerStateChange({\n chainIds: chainIdsToUpdate,\n }).catch(console.error);\n };\n\n /**\n * Handles the event for network state changes.\n * @param _ - The network state.\n * @param patches - An array of patch operations performed on the network state.\n */\n #onNetworkStateChange(_: NetworkState, patches: Patch[]) {\n // Remove state for deleted networks\n for (const patch of patches) {\n if (\n patch.op === 'remove' &&\n patch.path[0] === 'networkConfigurationsByChainId'\n ) {\n const removedChainId = patch.path[1] as Hex;\n\n this.update((state) => {\n for (const accountAddress of Object.keys(state.tokenBalances)) {\n delete state.tokenBalances[accountAddress as Hex][removedChainId];\n }\n });\n }\n }\n }\n\n /**\n * Handles changes when an account has been removed.\n *\n * @param accountAddress - The account address being removed.\n */\n #handleOnAccountRemoved(accountAddress: string) {\n const isEthAddress =\n isStrictHexString(accountAddress.toLowerCase()) &&\n isValidHexAddress(accountAddress);\n if (!isEthAddress) {\n return;\n }\n\n this.update((state) => {\n delete state.tokenBalances[accountAddress as `0x${string}`];\n });\n }\n\n /**\n * Returns an array of chain ids that have tokens.\n * @param allTokens - The state for imported tokens across all chains.\n * @param allDetectedTokens - The state for detected tokens across all chains.\n * @returns An array of chain ids that have tokens.\n */\n #getChainIds = (\n allTokens: TokensControllerState['allTokens'],\n allDetectedTokens: TokensControllerState['allDetectedTokens'],\n ) =>\n [\n ...new Set([\n ...Object.keys(allTokens),\n ...Object.keys(allDetectedTokens),\n ]),\n ] as Hex[];\n\n /**\n * Polls for erc20 token balances.\n * @param input - The input for the poll.\n * @param input.chainId - The chain id to poll token balances on.\n */\n async _executePoll({ chainId }: TokenBalancesPollingInput) {\n await this.updateBalancesByChainId({ chainId });\n }\n\n /**\n * Updates the token balances for the given chain ids.\n * @param input - The input for the update.\n * @param input.chainIds - The chain ids to update token balances for.\n * Or omitted to update all chains that contain tokens.\n */\n async updateBalances({ chainIds }: { chainIds?: Hex[] } = {}) {\n chainIds ??= this.#getChainIds(this.#allTokens, this.#allDetectedTokens);\n\n await Promise.allSettled(\n chainIds.map((chainId) => this.updateBalancesByChainId({ chainId })),\n );\n }\n\n async #handleTokensControllerStateChange({\n chainIds,\n }: { chainIds?: Hex[] } = {}) {\n const currentTokenBalancesState = this.messagingSystem.call(\n 'TokenBalancesController:getState',\n );\n const currentTokenBalances = currentTokenBalancesState.tokenBalances;\n const currentAllTokens = this.#allTokens;\n const chainIdsSet = new Set(chainIds);\n\n // first we check if the state change was due to a token being removed\n for (const currentAccount of Object.keys(currentTokenBalances)) {\n const allChains = currentTokenBalances[currentAccount as `0x${string}`];\n for (const currentChain of Object.keys(allChains)) {\n if (chainIds?.length && !chainIdsSet.has(currentChain as Hex)) {\n continue;\n }\n const tokensObject = allChains[currentChain as Hex];\n const allCurrentTokens = Object.keys(tokensObject);\n const existingTokensInState =\n currentAllTokens[currentChain as Hex]?.[\n currentAccount as `0x${string}`\n ] || [];\n const existingSet = new Set(\n existingTokensInState.map((elm) => elm.address),\n );\n\n for (const singleToken of allCurrentTokens) {\n if (!existingSet.has(singleToken)) {\n this.update((state) => {\n delete state.tokenBalances[currentAccount as Hex][\n currentChain as Hex\n ][singleToken as `0x${string}`];\n });\n }\n }\n }\n }\n\n // then we check if the state change was due to a token being added\n let shouldUpdate = false;\n for (const currentChain of Object.keys(currentAllTokens)) {\n if (chainIds?.length && !chainIdsSet.has(currentChain as Hex)) {\n continue;\n }\n const accountsPerChain = currentAllTokens[currentChain as Hex];\n\n for (const currentAccount of Object.keys(accountsPerChain)) {\n const tokensList = accountsPerChain[currentAccount as `0x${string}`];\n const tokenBalancesObject =\n currentTokenBalances[currentAccount as `0x${string}`]?.[\n currentChain as Hex\n ] || {};\n for (const singleToken of tokensList) {\n if (!tokenBalancesObject?.[singleToken.address as `0x${string}`]) {\n shouldUpdate = true;\n break;\n }\n }\n }\n }\n if (shouldUpdate) {\n await this.updateBalances({ chainIds }).catch(console.error);\n }\n }\n\n /**\n * Get an Ethers.js Web3Provider for the requested chain.\n *\n * @param chainId - The chain id to get the provider for.\n * @returns The provider for the given chain id.\n */\n #getProvider(chainId: Hex): Web3Provider {\n return new Web3Provider(this.#getNetworkClient(chainId).provider);\n }\n\n /**\n * Internal util: run `balanceOf` for an arbitrary set of account/token pairs.\n *\n * @param params - The parameters for the balance fetch.\n * @param params.chainId - The chain id to fetch balances on.\n * @param params.pairs - The account/token pairs to fetch balances for.\n * @returns The balances for the given token addresses.\n */\n async #batchBalanceOf({\n chainId,\n pairs,\n }: {\n chainId: Hex;\n pairs: { accountAddress: Hex; tokenAddress: Hex }[];\n }): Promise<MulticallResult[]> {\n if (!pairs.length) {\n return [];\n }\n\n const provider = this.#getProvider(chainId);\n\n const calls = pairs.map(({ accountAddress, tokenAddress }) => ({\n contract: new Contract(tokenAddress, abiERC20, provider),\n functionSignature: 'balanceOf(address)',\n arguments: [accountAddress],\n }));\n\n return multicallOrFallback(calls, chainId, provider);\n }\n\n /**\n * Returns ERC-20 balances for a single account on a single chain.\n *\n * @param params - The parameters for the balance fetch.\n * @param params.chainId - The chain id to fetch balances on.\n * @param params.accountAddress - The account address to fetch balances for.\n * @param params.tokenAddresses - The token addresses to fetch balances for.\n * @returns A mapping from token address to balance (hex) | null.\n */\n async getErc20Balances({\n chainId,\n accountAddress,\n tokenAddresses,\n }: {\n chainId: Hex;\n accountAddress: Hex;\n tokenAddresses: Hex[];\n }): Promise<Record<Hex, Hex | null>> {\n // Return early if no token addresses provided\n if (tokenAddresses.length === 0) {\n return {};\n }\n\n const pairs = tokenAddresses.map((tokenAddress) => ({\n accountAddress,\n tokenAddress,\n }));\n\n const results = await this.#batchBalanceOf({ chainId, pairs });\n\n const balances: Record<Hex, Hex | null> = {};\n tokenAddresses.forEach((tokenAddress, i) => {\n balances[tokenAddress] = results[i]?.success\n ? toHex(results[i].value as BN)\n : null;\n });\n\n return balances;\n }\n\n /**\n * Updates token balances for the given chain id.\n * @param input - The input for the update.\n * @param input.chainId - The chain id to update token balances on.\n */\n async updateBalancesByChainId({ chainId }: { chainId: Hex }) {\n const { address: selectedAccountAddress } = this.messagingSystem.call(\n 'AccountsController:getSelectedAccount',\n );\n\n const isSelectedAccount = (accountAddress: string) =>\n toChecksumHexAddress(accountAddress) ===\n toChecksumHexAddress(selectedAccountAddress);\n\n const accountTokenPairs: { accountAddress: Hex; tokenAddress: Hex }[] = [];\n\n const addTokens = ([accountAddress, tokens]: [string, Token[]]) =>\n this.#queryMultipleAccounts || isSelectedAccount(accountAddress)\n ? tokens.forEach((t) =>\n accountTokenPairs.push({\n accountAddress: accountAddress as Hex,\n tokenAddress: t.address as Hex,\n }),\n )\n : undefined;\n\n // Balances will be updated for both imported and detected tokens\n Object.entries(this.#allTokens[chainId] ?? {}).forEach(addTokens);\n Object.entries(this.#allDetectedTokens[chainId] ?? {}).forEach(addTokens);\n\n let results: MulticallResult[] = [];\n\n const currentTokenBalances = this.messagingSystem.call(\n 'TokenBalancesController:getState',\n );\n\n if (accountTokenPairs.length > 0) {\n results = await this.#batchBalanceOf({\n chainId,\n pairs: accountTokenPairs,\n });\n }\n\n const updatedResults: (MulticallResult & {\n isTokenBalanceValueChanged?: boolean;\n })[] = results.map((res, i) => {\n const { value } = res;\n const { accountAddress, tokenAddress } = accountTokenPairs[i];\n const currentTokenBalanceValueForAccount =\n currentTokenBalances.tokenBalances?.[accountAddress]?.[chainId]?.[\n tokenAddress\n ];\n // `value` can be null or undefined if the multicall failed due to RPC issue.\n // Please see packages/assets-controllers/src/multicall.ts#L365.\n // Hence we should not update the balance in that case.\n const isTokenBalanceValueChanged =\n res.success && value !== undefined && value !== null\n ? currentTokenBalanceValueForAccount !== toHex(value as BN)\n : false;\n return {\n ...res,\n isTokenBalanceValueChanged,\n };\n });\n\n // if all values of isTokenBalanceValueChanged are false, return\n if (updatedResults.every((result) => !result.isTokenBalanceValueChanged)) {\n return;\n }\n\n this.update((state) => {\n for (let i = 0; i < updatedResults.length; i++) {\n const { success, value, isTokenBalanceValueChanged } =\n updatedResults[i];\n const { accountAddress, tokenAddress } = accountTokenPairs[i];\n if (success && isTokenBalanceValueChanged) {\n ((state.tokenBalances[accountAddress] ??= {})[chainId] ??= {})[\n tokenAddress\n ] = toHex(value as BN);\n }\n }\n });\n }\n\n /**\n * Reset the controller state to the default state.\n */\n resetState() {\n this.update(() => {\n return getDefaultTokenBalancesState();\n });\n }\n\n /**\n * Returns the network client for a given chain id\n * @param chainId - The chain id to get the network client for.\n * @returns The network client for the given chain id.\n */\n #getNetworkClient(chainId: Hex) {\n const { networkConfigurationsByChainId } = this.messagingSystem.call(\n 'NetworkController:getState',\n );\n\n const networkConfiguration = networkConfigurationsByChainId[chainId];\n if (!networkConfiguration) {\n throw new Error(\n `TokenBalancesController: No network configuration found for chainId ${chainId}`,\n );\n }\n\n const { networkClientId } =\n networkConfiguration.rpcEndpoints[\n networkConfiguration.defaultRpcEndpointIndex\n ];\n\n return this.messagingSystem.call(\n `NetworkController:getNetworkClientById`,\n networkClientId,\n );\n }\n}\n\nexport default TokenBalancesController;\n"]}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"TokenBalancesController.d.cts","sourceRoot":"","sources":["../src/TokenBalancesController.ts"],"names":[],"mappings":"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
|
|
1
|
+
{"version":3,"file":"TokenBalancesController.d.cts","sourceRoot":"","sources":["../src/TokenBalancesController.ts"],"names":[],"mappings":"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"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"TokenBalancesController.d.mts","sourceRoot":"","sources":["../src/TokenBalancesController.ts"],"names":[],"mappings":"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
|
|
1
|
+
{"version":3,"file":"TokenBalancesController.d.mts","sourceRoot":"","sources":["../src/TokenBalancesController.ts"],"names":[],"mappings":"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"}
|
|
@@ -208,7 +208,12 @@ export class TokenBalancesController extends StaticIntervalPollingController() {
|
|
|
208
208
|
const { value } = res;
|
|
209
209
|
const { accountAddress, tokenAddress } = accountTokenPairs[i];
|
|
210
210
|
const currentTokenBalanceValueForAccount = currentTokenBalances.tokenBalances?.[accountAddress]?.[chainId]?.[tokenAddress];
|
|
211
|
-
|
|
211
|
+
// `value` can be null or undefined if the multicall failed due to RPC issue.
|
|
212
|
+
// Please see packages/assets-controllers/src/multicall.ts#L365.
|
|
213
|
+
// Hence we should not update the balance in that case.
|
|
214
|
+
const isTokenBalanceValueChanged = res.success && value !== undefined && value !== null
|
|
215
|
+
? currentTokenBalanceValueForAccount !== toHex(value)
|
|
216
|
+
: false;
|
|
212
217
|
return {
|
|
213
218
|
...res,
|
|
214
219
|
isTokenBalanceValueChanged,
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"TokenBalancesController.mjs","sourceRoot":"","sources":["../src/TokenBalancesController.ts"],"names":[],"mappings":"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","sourcesContent":["import { Contract } from '@ethersproject/contracts';\nimport { Web3Provider } from '@ethersproject/providers';\nimport type {\n AccountsControllerGetSelectedAccountAction,\n AccountsControllerListAccountsAction,\n} from '@metamask/accounts-controller';\nimport type {\n RestrictedMessenger,\n ControllerGetStateAction,\n ControllerStateChangeEvent,\n} from '@metamask/base-controller';\nimport {\n isValidHexAddress,\n toChecksumHexAddress,\n toHex,\n} from '@metamask/controller-utils';\nimport type { KeyringControllerAccountRemovedEvent } from '@metamask/keyring-controller';\nimport { abiERC20 } from '@metamask/metamask-eth-abis';\nimport type {\n NetworkControllerGetNetworkClientByIdAction,\n NetworkControllerGetStateAction,\n NetworkControllerStateChangeEvent,\n NetworkState,\n} from '@metamask/network-controller';\nimport { StaticIntervalPollingController } from '@metamask/polling-controller';\nimport type {\n PreferencesControllerGetStateAction,\n PreferencesControllerStateChangeEvent,\n PreferencesState,\n} from '@metamask/preferences-controller';\nimport { isStrictHexString, type Hex } from '@metamask/utils';\nimport type BN from 'bn.js';\nimport type { Patch } from 'immer';\nimport { isEqual } from 'lodash';\n\nimport type { MulticallResult } from './multicall';\nimport { multicallOrFallback } from './multicall';\nimport type { Token } from './TokenRatesController';\nimport type {\n TokensControllerGetStateAction,\n TokensControllerState,\n TokensControllerStateChangeEvent,\n} from './TokensController';\n\nconst DEFAULT_INTERVAL = 180000;\n\nconst controllerName = 'TokenBalancesController';\n\nconst metadata = {\n tokenBalances: { persist: true, anonymous: false },\n};\n\n/**\n * Token balances controller options\n * @property interval - Polling interval used to fetch new token balances.\n * @property messenger - A messenger.\n * @property state - Initial state for the controller.\n */\ntype TokenBalancesControllerOptions = {\n interval?: number;\n messenger: TokenBalancesControllerMessenger;\n state?: Partial<TokenBalancesControllerState>;\n};\n\n/**\n * A mapping from account address to chain id to token address to balance.\n */\ntype TokenBalances = Record<Hex, Record<Hex, Record<Hex, Hex>>>;\n\n/**\n * Token balances controller state\n * @property tokenBalances - A mapping from account address to chain id to token address to balance.\n */\nexport type TokenBalancesControllerState = {\n tokenBalances: TokenBalances;\n};\n\nexport type TokenBalancesControllerGetStateAction = ControllerGetStateAction<\n typeof controllerName,\n TokenBalancesControllerState\n>;\n\nexport type TokenBalancesControllerActions =\n TokenBalancesControllerGetStateAction;\n\nexport type AllowedActions =\n | NetworkControllerGetNetworkClientByIdAction\n | NetworkControllerGetStateAction\n | TokensControllerGetStateAction\n | PreferencesControllerGetStateAction\n | AccountsControllerGetSelectedAccountAction\n | AccountsControllerListAccountsAction;\n\nexport type TokenBalancesControllerStateChangeEvent =\n ControllerStateChangeEvent<\n typeof controllerName,\n TokenBalancesControllerState\n >;\n\nexport type TokenBalancesControllerEvents =\n TokenBalancesControllerStateChangeEvent;\n\nexport type AllowedEvents =\n | TokensControllerStateChangeEvent\n | PreferencesControllerStateChangeEvent\n | NetworkControllerStateChangeEvent\n | KeyringControllerAccountRemovedEvent;\n\nexport type TokenBalancesControllerMessenger = RestrictedMessenger<\n typeof controllerName,\n TokenBalancesControllerActions | AllowedActions,\n TokenBalancesControllerEvents | AllowedEvents,\n AllowedActions['type'],\n AllowedEvents['type']\n>;\n\n/**\n * Get the default TokenBalancesController state.\n *\n * @returns The default TokenBalancesController state.\n */\nexport function getDefaultTokenBalancesState(): TokenBalancesControllerState {\n return {\n tokenBalances: {},\n };\n}\n\n/** The input to start polling for the {@link TokenBalancesController} */\nexport type TokenBalancesPollingInput = {\n chainId: Hex;\n};\n\n/**\n * Controller that passively polls on a set interval token balances\n * for tokens stored in the TokensController\n */\nexport class TokenBalancesController extends StaticIntervalPollingController<TokenBalancesPollingInput>()<\n typeof controllerName,\n TokenBalancesControllerState,\n TokenBalancesControllerMessenger\n> {\n #queryMultipleAccounts: boolean;\n\n #allTokens: TokensControllerState['allTokens'];\n\n #allDetectedTokens: TokensControllerState['allDetectedTokens'];\n\n /**\n * Construct a Token Balances Controller.\n *\n * @param options - The controller options.\n * @param options.interval - Polling interval used to fetch new token balances.\n * @param options.state - Initial state to set on this controller.\n * @param options.messenger - The controller restricted messenger.\n */\n constructor({\n interval = DEFAULT_INTERVAL,\n messenger,\n state = {},\n }: TokenBalancesControllerOptions) {\n super({\n name: controllerName,\n metadata,\n messenger,\n state: {\n ...getDefaultTokenBalancesState(),\n ...state,\n },\n });\n\n this.setIntervalLength(interval);\n\n // Set initial preference for querying multiple accounts, and subscribe to changes\n this.#queryMultipleAccounts = this.#calculateQueryMultipleAccounts(\n this.messagingSystem.call('PreferencesController:getState'),\n );\n this.messagingSystem.subscribe(\n 'PreferencesController:stateChange',\n this.#onPreferencesStateChange.bind(this),\n );\n\n // Set initial tokens, and subscribe to changes\n ({\n allTokens: this.#allTokens,\n allDetectedTokens: this.#allDetectedTokens,\n } = this.messagingSystem.call('TokensController:getState'));\n\n this.messagingSystem.subscribe(\n 'TokensController:stateChange',\n this.#onTokensStateChange.bind(this),\n );\n\n // Subscribe to network state changes\n this.messagingSystem.subscribe(\n 'NetworkController:stateChange',\n this.#onNetworkStateChange.bind(this),\n );\n\n // subscribe to account removed event to cleanup stale balances\n\n this.messagingSystem.subscribe(\n 'KeyringController:accountRemoved',\n (accountAddress: string) => this.#handleOnAccountRemoved(accountAddress),\n );\n }\n\n /**\n * Determines whether to query all accounts, or just the selected account.\n * @param preferences - The preferences state.\n * @param preferences.isMultiAccountBalancesEnabled - whether to query all accounts (mobile).\n * @param preferences.useMultiAccountBalanceChecker - whether to query all accounts (extension).\n * @returns true if all accounts should be queried.\n */\n #calculateQueryMultipleAccounts = ({\n isMultiAccountBalancesEnabled,\n useMultiAccountBalanceChecker,\n }: PreferencesState & { useMultiAccountBalanceChecker?: boolean }) => {\n return Boolean(\n // Note: These settings have different names on extension vs mobile\n isMultiAccountBalancesEnabled || useMultiAccountBalanceChecker,\n );\n };\n\n /**\n * Handles the event for preferences state changes.\n * @param preferences - The preferences state.\n */\n #onPreferencesStateChange = (preferences: PreferencesState) => {\n // Update the user preference for whether to query multiple accounts.\n const queryMultipleAccounts =\n this.#calculateQueryMultipleAccounts(preferences);\n\n // Refresh when flipped off -> on\n const refresh = queryMultipleAccounts && !this.#queryMultipleAccounts;\n this.#queryMultipleAccounts = queryMultipleAccounts;\n\n if (refresh) {\n this.updateBalances().catch(console.error);\n }\n };\n\n /**\n * Handles the event for tokens state changes.\n * @param state - The token state.\n * @param state.allTokens - The state for imported tokens across all chains.\n * @param state.allDetectedTokens - The state for detected tokens across all chains.\n */\n #onTokensStateChange = ({\n allTokens,\n allDetectedTokens,\n }: TokensControllerState) => {\n // Refresh token balances on chains whose tokens have changed.\n const chainIds = this.#getChainIds(allTokens, allDetectedTokens);\n const chainIdsToUpdate = chainIds.filter(\n (chainId) =>\n !isEqual(this.#allTokens[chainId], allTokens[chainId]) ||\n !isEqual(this.#allDetectedTokens[chainId], allDetectedTokens[chainId]),\n );\n\n this.#allTokens = allTokens;\n this.#allDetectedTokens = allDetectedTokens;\n this.#handleTokensControllerStateChange({\n chainIds: chainIdsToUpdate,\n }).catch(console.error);\n };\n\n /**\n * Handles the event for network state changes.\n * @param _ - The network state.\n * @param patches - An array of patch operations performed on the network state.\n */\n #onNetworkStateChange(_: NetworkState, patches: Patch[]) {\n // Remove state for deleted networks\n for (const patch of patches) {\n if (\n patch.op === 'remove' &&\n patch.path[0] === 'networkConfigurationsByChainId'\n ) {\n const removedChainId = patch.path[1] as Hex;\n\n this.update((state) => {\n for (const accountAddress of Object.keys(state.tokenBalances)) {\n delete state.tokenBalances[accountAddress as Hex][removedChainId];\n }\n });\n }\n }\n }\n\n /**\n * Handles changes when an account has been removed.\n *\n * @param accountAddress - The account address being removed.\n */\n #handleOnAccountRemoved(accountAddress: string) {\n const isEthAddress =\n isStrictHexString(accountAddress.toLowerCase()) &&\n isValidHexAddress(accountAddress);\n if (!isEthAddress) {\n return;\n }\n\n this.update((state) => {\n delete state.tokenBalances[accountAddress as `0x${string}`];\n });\n }\n\n /**\n * Returns an array of chain ids that have tokens.\n * @param allTokens - The state for imported tokens across all chains.\n * @param allDetectedTokens - The state for detected tokens across all chains.\n * @returns An array of chain ids that have tokens.\n */\n #getChainIds = (\n allTokens: TokensControllerState['allTokens'],\n allDetectedTokens: TokensControllerState['allDetectedTokens'],\n ) =>\n [\n ...new Set([\n ...Object.keys(allTokens),\n ...Object.keys(allDetectedTokens),\n ]),\n ] as Hex[];\n\n /**\n * Polls for erc20 token balances.\n * @param input - The input for the poll.\n * @param input.chainId - The chain id to poll token balances on.\n */\n async _executePoll({ chainId }: TokenBalancesPollingInput) {\n await this.updateBalancesByChainId({ chainId });\n }\n\n /**\n * Updates the token balances for the given chain ids.\n * @param input - The input for the update.\n * @param input.chainIds - The chain ids to update token balances for.\n * Or omitted to update all chains that contain tokens.\n */\n async updateBalances({ chainIds }: { chainIds?: Hex[] } = {}) {\n chainIds ??= this.#getChainIds(this.#allTokens, this.#allDetectedTokens);\n\n await Promise.allSettled(\n chainIds.map((chainId) => this.updateBalancesByChainId({ chainId })),\n );\n }\n\n async #handleTokensControllerStateChange({\n chainIds,\n }: { chainIds?: Hex[] } = {}) {\n const currentTokenBalancesState = this.messagingSystem.call(\n 'TokenBalancesController:getState',\n );\n const currentTokenBalances = currentTokenBalancesState.tokenBalances;\n const currentAllTokens = this.#allTokens;\n const chainIdsSet = new Set(chainIds);\n\n // first we check if the state change was due to a token being removed\n for (const currentAccount of Object.keys(currentTokenBalances)) {\n const allChains = currentTokenBalances[currentAccount as `0x${string}`];\n for (const currentChain of Object.keys(allChains)) {\n if (chainIds?.length && !chainIdsSet.has(currentChain as Hex)) {\n continue;\n }\n const tokensObject = allChains[currentChain as Hex];\n const allCurrentTokens = Object.keys(tokensObject);\n const existingTokensInState =\n currentAllTokens[currentChain as Hex]?.[\n currentAccount as `0x${string}`\n ] || [];\n const existingSet = new Set(\n existingTokensInState.map((elm) => elm.address),\n );\n\n for (const singleToken of allCurrentTokens) {\n if (!existingSet.has(singleToken)) {\n this.update((state) => {\n delete state.tokenBalances[currentAccount as Hex][\n currentChain as Hex\n ][singleToken as `0x${string}`];\n });\n }\n }\n }\n }\n\n // then we check if the state change was due to a token being added\n let shouldUpdate = false;\n for (const currentChain of Object.keys(currentAllTokens)) {\n if (chainIds?.length && !chainIdsSet.has(currentChain as Hex)) {\n continue;\n }\n const accountsPerChain = currentAllTokens[currentChain as Hex];\n\n for (const currentAccount of Object.keys(accountsPerChain)) {\n const tokensList = accountsPerChain[currentAccount as `0x${string}`];\n const tokenBalancesObject =\n currentTokenBalances[currentAccount as `0x${string}`]?.[\n currentChain as Hex\n ] || {};\n for (const singleToken of tokensList) {\n if (!tokenBalancesObject?.[singleToken.address as `0x${string}`]) {\n shouldUpdate = true;\n break;\n }\n }\n }\n }\n if (shouldUpdate) {\n await this.updateBalances({ chainIds }).catch(console.error);\n }\n }\n\n /**\n * Get an Ethers.js Web3Provider for the requested chain.\n *\n * @param chainId - The chain id to get the provider for.\n * @returns The provider for the given chain id.\n */\n #getProvider(chainId: Hex): Web3Provider {\n return new Web3Provider(this.#getNetworkClient(chainId).provider);\n }\n\n /**\n * Internal util: run `balanceOf` for an arbitrary set of account/token pairs.\n *\n * @param params - The parameters for the balance fetch.\n * @param params.chainId - The chain id to fetch balances on.\n * @param params.pairs - The account/token pairs to fetch balances for.\n * @returns The balances for the given token addresses.\n */\n async #batchBalanceOf({\n chainId,\n pairs,\n }: {\n chainId: Hex;\n pairs: { accountAddress: Hex; tokenAddress: Hex }[];\n }): Promise<MulticallResult[]> {\n if (!pairs.length) {\n return [];\n }\n\n const provider = this.#getProvider(chainId);\n\n const calls = pairs.map(({ accountAddress, tokenAddress }) => ({\n contract: new Contract(tokenAddress, abiERC20, provider),\n functionSignature: 'balanceOf(address)',\n arguments: [accountAddress],\n }));\n\n return multicallOrFallback(calls, chainId, provider);\n }\n\n /**\n * Returns ERC-20 balances for a single account on a single chain.\n *\n * @param params - The parameters for the balance fetch.\n * @param params.chainId - The chain id to fetch balances on.\n * @param params.accountAddress - The account address to fetch balances for.\n * @param params.tokenAddresses - The token addresses to fetch balances for.\n * @returns A mapping from token address to balance (hex) | null.\n */\n async getErc20Balances({\n chainId,\n accountAddress,\n tokenAddresses,\n }: {\n chainId: Hex;\n accountAddress: Hex;\n tokenAddresses: Hex[];\n }): Promise<Record<Hex, Hex | null>> {\n // Return early if no token addresses provided\n if (tokenAddresses.length === 0) {\n return {};\n }\n\n const pairs = tokenAddresses.map((tokenAddress) => ({\n accountAddress,\n tokenAddress,\n }));\n\n const results = await this.#batchBalanceOf({ chainId, pairs });\n\n const balances: Record<Hex, Hex | null> = {};\n tokenAddresses.forEach((tokenAddress, i) => {\n balances[tokenAddress] = results[i]?.success\n ? toHex(results[i].value as BN)\n : null;\n });\n\n return balances;\n }\n\n /**\n * Updates token balances for the given chain id.\n * @param input - The input for the update.\n * @param input.chainId - The chain id to update token balances on.\n */\n async updateBalancesByChainId({ chainId }: { chainId: Hex }) {\n const { address: selectedAccountAddress } = this.messagingSystem.call(\n 'AccountsController:getSelectedAccount',\n );\n\n const isSelectedAccount = (accountAddress: string) =>\n toChecksumHexAddress(accountAddress) ===\n toChecksumHexAddress(selectedAccountAddress);\n\n const accountTokenPairs: { accountAddress: Hex; tokenAddress: Hex }[] = [];\n\n const addTokens = ([accountAddress, tokens]: [string, Token[]]) =>\n this.#queryMultipleAccounts || isSelectedAccount(accountAddress)\n ? tokens.forEach((t) =>\n accountTokenPairs.push({\n accountAddress: accountAddress as Hex,\n tokenAddress: t.address as Hex,\n }),\n )\n : undefined;\n\n // Balances will be updated for both imported and detected tokens\n Object.entries(this.#allTokens[chainId] ?? {}).forEach(addTokens);\n Object.entries(this.#allDetectedTokens[chainId] ?? {}).forEach(addTokens);\n\n let results: MulticallResult[] = [];\n\n const currentTokenBalances = this.messagingSystem.call(\n 'TokenBalancesController:getState',\n );\n\n if (accountTokenPairs.length > 0) {\n results = await this.#batchBalanceOf({\n chainId,\n pairs: accountTokenPairs,\n });\n }\n\n const updatedResults: (MulticallResult & {\n isTokenBalanceValueChanged?: boolean;\n })[] = results.map((res, i) => {\n const { value } = res;\n const { accountAddress, tokenAddress } = accountTokenPairs[i];\n const currentTokenBalanceValueForAccount =\n currentTokenBalances.tokenBalances?.[accountAddress]?.[chainId]?.[\n tokenAddress\n ];\n const isTokenBalanceValueChanged =\n currentTokenBalanceValueForAccount !== toHex(value as BN);\n return {\n ...res,\n isTokenBalanceValueChanged,\n };\n });\n\n // if all values of isTokenBalanceValueChanged are false, return\n if (updatedResults.every((result) => !result.isTokenBalanceValueChanged)) {\n return;\n }\n\n this.update((state) => {\n for (let i = 0; i < updatedResults.length; i++) {\n const { success, value, isTokenBalanceValueChanged } =\n updatedResults[i];\n const { accountAddress, tokenAddress } = accountTokenPairs[i];\n if (success && isTokenBalanceValueChanged) {\n ((state.tokenBalances[accountAddress] ??= {})[chainId] ??= {})[\n tokenAddress\n ] = toHex(value as BN);\n }\n }\n });\n }\n\n /**\n * Reset the controller state to the default state.\n */\n resetState() {\n this.update(() => {\n return getDefaultTokenBalancesState();\n });\n }\n\n /**\n * Returns the network client for a given chain id\n * @param chainId - The chain id to get the network client for.\n * @returns The network client for the given chain id.\n */\n #getNetworkClient(chainId: Hex) {\n const { networkConfigurationsByChainId } = this.messagingSystem.call(\n 'NetworkController:getState',\n );\n\n const networkConfiguration = networkConfigurationsByChainId[chainId];\n if (!networkConfiguration) {\n throw new Error(\n `TokenBalancesController: No network configuration found for chainId ${chainId}`,\n );\n }\n\n const { networkClientId } =\n networkConfiguration.rpcEndpoints[\n networkConfiguration.defaultRpcEndpointIndex\n ];\n\n return this.messagingSystem.call(\n `NetworkController:getNetworkClientById`,\n networkClientId,\n );\n }\n}\n\nexport default TokenBalancesController;\n"]}
|
|
1
|
+
{"version":3,"file":"TokenBalancesController.mjs","sourceRoot":"","sources":["../src/TokenBalancesController.ts"],"names":[],"mappings":";;;;;;;;;;;;AAAA,OAAO,EAAE,QAAQ,EAAE,iCAAiC;AACpD,OAAO,EAAE,YAAY,EAAE,iCAAiC;AAUxD,OAAO,EACL,iBAAiB,EACjB,oBAAoB,EACpB,KAAK,EACN,mCAAmC;AAEpC,OAAO,EAAE,QAAQ,EAAE,oCAAoC;AAOvD,OAAO,EAAE,+BAA+B,EAAE,qCAAqC;AAM/E,OAAO,EAAE,iBAAiB,EAAY,wBAAwB;;;AAM9D,OAAO,EAAE,mBAAmB,EAAE,wBAAoB;AAQlD,MAAM,gBAAgB,GAAG,MAAM,CAAC;AAEhC,MAAM,cAAc,GAAG,yBAAyB,CAAC;AAEjD,MAAM,QAAQ,GAAG;IACf,aAAa,EAAE,EAAE,OAAO,EAAE,IAAI,EAAE,SAAS,EAAE,KAAK,EAAE;CACnD,CAAC;AAkEF;;;;GAIG;AACH,MAAM,UAAU,4BAA4B;IAC1C,OAAO;QACL,aAAa,EAAE,EAAE;KAClB,CAAC;AACJ,CAAC;AAOD;;;GAGG;AACH,MAAM,OAAO,uBAAwB,SAAQ,+BAA+B,EAI3E;IAOC;;;;;;;OAOG;IACH,YAAY,EACV,QAAQ,GAAG,gBAAgB,EAC3B,SAAS,EACT,KAAK,GAAG,EAAE,GACqB;;QAC/B,KAAK,CAAC;YACJ,IAAI,EAAE,cAAc;YACpB,QAAQ;YACR,SAAS;YACT,KAAK,EAAE;gBACL,GAAG,4BAA4B,EAAE;gBACjC,GAAG,KAAK;aACT;SACF,CAAC,CAAC;;QA3BL,iEAAgC;QAEhC,qDAA+C;QAE/C,6DAA+D;QA6D/D;;;;;;WAMG;QACH,kEAAkC,CAAC,EACjC,6BAA6B,EAC7B,6BAA6B,GACkC,EAAE,EAAE;YACnE,OAAO,OAAO;YACZ,mEAAmE;YACnE,6BAA6B,IAAI,6BAA6B,CAC/D,CAAC;QACJ,CAAC,EAAC;QAEF;;;WAGG;QACH,4DAA4B,CAAC,WAA6B,EAAE,EAAE;YAC5D,qEAAqE;YACrE,MAAM,qBAAqB,GACzB,uBAAA,IAAI,+DAAgC,MAApC,IAAI,EAAiC,WAAW,CAAC,CAAC;YAEpD,iCAAiC;YACjC,MAAM,OAAO,GAAG,qBAAqB,IAAI,CAAC,uBAAA,IAAI,sDAAuB,CAAC;YACtE,uBAAA,IAAI,kDAA0B,qBAAqB,MAAA,CAAC;YAEpD,IAAI,OAAO,EAAE;gBACX,IAAI,CAAC,cAAc,EAAE,CAAC,KAAK,CAAC,OAAO,CAAC,KAAK,CAAC,CAAC;aAC5C;QACH,CAAC,EAAC;QAEF;;;;;WAKG;QACH,uDAAuB,CAAC,EACtB,SAAS,EACT,iBAAiB,GACK,EAAE,EAAE;YAC1B,8DAA8D;YAC9D,MAAM,QAAQ,GAAG,uBAAA,IAAI,4CAAa,MAAjB,IAAI,EAAc,SAAS,EAAE,iBAAiB,CAAC,CAAC;YACjE,MAAM,gBAAgB,GAAG,QAAQ,CAAC,MAAM,CACtC,CAAC,OAAO,EAAE,EAAE,CACV,CAAC,OAAO,CAAC,uBAAA,IAAI,0CAAW,CAAC,OAAO,CAAC,EAAE,SAAS,CAAC,OAAO,CAAC,CAAC;gBACtD,CAAC,OAAO,CAAC,uBAAA,IAAI,kDAAmB,CAAC,OAAO,CAAC,EAAE,iBAAiB,CAAC,OAAO,CAAC,CAAC,CACzE,CAAC;YAEF,uBAAA,IAAI,sCAAc,SAAS,MAAA,CAAC;YAC5B,uBAAA,IAAI,8CAAsB,iBAAiB,MAAA,CAAC;YAC5C,uBAAA,IAAI,sGAAmC,MAAvC,IAAI,EAAoC;gBACtC,QAAQ,EAAE,gBAAgB;aAC3B,CAAC,CAAC,KAAK,CAAC,OAAO,CAAC,KAAK,CAAC,CAAC;QAC1B,CAAC,EAAC;QA2CF;;;;;WAKG;QACH,+CAAe,CACb,SAA6C,EAC7C,iBAA6D,EAC7D,EAAE,CACF;YACE,GAAG,IAAI,GAAG,CAAC;gBACT,GAAG,MAAM,CAAC,IAAI,CAAC,SAAS,CAAC;gBACzB,GAAG,MAAM,CAAC,IAAI,CAAC,iBAAiB,CAAC;aAClC,CAAC;SACM,EAAC;QAxJX,IAAI,CAAC,iBAAiB,CAAC,QAAQ,CAAC,CAAC;QAEjC,kFAAkF;QAClF,uBAAA,IAAI,kDAA0B,uBAAA,IAAI,+DAAgC,MAApC,IAAI,EAChC,IAAI,CAAC,eAAe,CAAC,IAAI,CAAC,gCAAgC,CAAC,CAC5D,MAAA,CAAC;QACF,IAAI,CAAC,eAAe,CAAC,SAAS,CAC5B,mCAAmC,EACnC,uBAAA,IAAI,yDAA0B,CAAC,IAAI,CAAC,IAAI,CAAC,CAC1C,CAAC;QAEF,+CAA+C;QAC/C,MACa,IAAI,OACI,IAAI,EAFxB;YACC,SAAS,wGAAiB;YAC1B,iBAAiB,gHAAyB;SAC3C,GAAG,IAAI,CAAC,eAAe,CAAC,IAAI,CAAC,2BAA2B,CAAC,CAAC,CAAC;QAE5D,IAAI,CAAC,eAAe,CAAC,SAAS,CAC5B,8BAA8B,EAC9B,uBAAA,IAAI,oDAAqB,CAAC,IAAI,CAAC,IAAI,CAAC,CACrC,CAAC;QAEF,qCAAqC;QACrC,IAAI,CAAC,eAAe,CAAC,SAAS,CAC5B,+BAA+B,EAC/B,uBAAA,IAAI,yFAAsB,CAAC,IAAI,CAAC,IAAI,CAAC,CACtC,CAAC;QAEF,+DAA+D;QAE/D,IAAI,CAAC,eAAe,CAAC,SAAS,CAC5B,kCAAkC,EAClC,CAAC,cAAsB,EAAE,EAAE,CAAC,uBAAA,IAAI,2FAAwB,MAA5B,IAAI,EAAyB,cAAc,CAAC,CACzE,CAAC;IACJ,CAAC;IAwHD;;;;OAIG;IACH,KAAK,CAAC,YAAY,CAAC,EAAE,OAAO,EAA6B;QACvD,MAAM,IAAI,CAAC,uBAAuB,CAAC,EAAE,OAAO,EAAE,CAAC,CAAC;IAClD,CAAC;IAED;;;;;OAKG;IACH,KAAK,CAAC,cAAc,CAAC,EAAE,QAAQ,KAA2B,EAAE;QAC1D,QAAQ,KAAR,QAAQ,GAAK,uBAAA,IAAI,4CAAa,MAAjB,IAAI,EAAc,uBAAA,IAAI,0CAAW,EAAE,uBAAA,IAAI,kDAAmB,CAAC,EAAC;QAEzE,MAAM,OAAO,CAAC,UAAU,CACtB,QAAQ,CAAC,GAAG,CAAC,CAAC,OAAO,EAAE,EAAE,CAAC,IAAI,CAAC,uBAAuB,CAAC,EAAE,OAAO,EAAE,CAAC,CAAC,CACrE,CAAC;IACJ,CAAC;IA4GD;;;;;;;;OAQG;IACH,KAAK,CAAC,gBAAgB,CAAC,EACrB,OAAO,EACP,cAAc,EACd,cAAc,GAKf;QACC,8CAA8C;QAC9C,IAAI,cAAc,CAAC,MAAM,KAAK,CAAC,EAAE;YAC/B,OAAO,EAAE,CAAC;SACX;QAED,MAAM,KAAK,GAAG,cAAc,CAAC,GAAG,CAAC,CAAC,YAAY,EAAE,EAAE,CAAC,CAAC;YAClD,cAAc;YACd,YAAY;SACb,CAAC,CAAC,CAAC;QAEJ,MAAM,OAAO,GAAG,MAAM,uBAAA,IAAI,mFAAgB,MAApB,IAAI,EAAiB,EAAE,OAAO,EAAE,KAAK,EAAE,CAAC,CAAC;QAE/D,MAAM,QAAQ,GAA4B,EAAE,CAAC;QAC7C,cAAc,CAAC,OAAO,CAAC,CAAC,YAAY,EAAE,CAAC,EAAE,EAAE;YACzC,QAAQ,CAAC,YAAY,CAAC,GAAG,OAAO,CAAC,CAAC,CAAC,EAAE,OAAO;gBAC1C,CAAC,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC,CAAC,CAAC,KAAW,CAAC;gBAC/B,CAAC,CAAC,IAAI,CAAC;QACX,CAAC,CAAC,CAAC;QAEH,OAAO,QAAQ,CAAC;IAClB,CAAC;IAED;;;;OAIG;IACH,KAAK,CAAC,uBAAuB,CAAC,EAAE,OAAO,EAAoB;QACzD,MAAM,EAAE,OAAO,EAAE,sBAAsB,EAAE,GAAG,IAAI,CAAC,eAAe,CAAC,IAAI,CACnE,uCAAuC,CACxC,CAAC;QAEF,MAAM,iBAAiB,GAAG,CAAC,cAAsB,EAAE,EAAE,CACnD,oBAAoB,CAAC,cAAc,CAAC;YACpC,oBAAoB,CAAC,sBAAsB,CAAC,CAAC;QAE/C,MAAM,iBAAiB,GAAiD,EAAE,CAAC;QAE3E,MAAM,SAAS,GAAG,CAAC,CAAC,cAAc,EAAE,MAAM,CAAoB,EAAE,EAAE,CAChE,uBAAA,IAAI,sDAAuB,IAAI,iBAAiB,CAAC,cAAc,CAAC;YAC9D,CAAC,CAAC,MAAM,CAAC,OAAO,CAAC,CAAC,CAAC,EAAE,EAAE,CACnB,iBAAiB,CAAC,IAAI,CAAC;gBACrB,cAAc,EAAE,cAAqB;gBACrC,YAAY,EAAE,CAAC,CAAC,OAAc;aAC/B,CAAC,CACH;YACH,CAAC,CAAC,SAAS,CAAC;QAEhB,iEAAiE;QACjE,MAAM,CAAC,OAAO,CAAC,uBAAA,IAAI,0CAAW,CAAC,OAAO,CAAC,IAAI,EAAE,CAAC,CAAC,OAAO,CAAC,SAAS,CAAC,CAAC;QAClE,MAAM,CAAC,OAAO,CAAC,uBAAA,IAAI,kDAAmB,CAAC,OAAO,CAAC,IAAI,EAAE,CAAC,CAAC,OAAO,CAAC,SAAS,CAAC,CAAC;QAE1E,IAAI,OAAO,GAAsB,EAAE,CAAC;QAEpC,MAAM,oBAAoB,GAAG,IAAI,CAAC,eAAe,CAAC,IAAI,CACpD,kCAAkC,CACnC,CAAC;QAEF,IAAI,iBAAiB,CAAC,MAAM,GAAG,CAAC,EAAE;YAChC,OAAO,GAAG,MAAM,uBAAA,IAAI,mFAAgB,MAApB,IAAI,EAAiB;gBACnC,OAAO;gBACP,KAAK,EAAE,iBAAiB;aACzB,CAAC,CAAC;SACJ;QAED,MAAM,cAAc,GAEb,OAAO,CAAC,GAAG,CAAC,CAAC,GAAG,EAAE,CAAC,EAAE,EAAE;YAC5B,MAAM,EAAE,KAAK,EAAE,GAAG,GAAG,CAAC;YACtB,MAAM,EAAE,cAAc,EAAE,YAAY,EAAE,GAAG,iBAAiB,CAAC,CAAC,CAAC,CAAC;YAC9D,MAAM,kCAAkC,GACtC,oBAAoB,CAAC,aAAa,EAAE,CAAC,cAAc,CAAC,EAAE,CAAC,OAAO,CAAC,EAAE,CAC/D,YAAY,CACb,CAAC;YACJ,6EAA6E;YAC7E,gEAAgE;YAChE,uDAAuD;YACvD,MAAM,0BAA0B,GAC9B,GAAG,CAAC,OAAO,IAAI,KAAK,KAAK,SAAS,IAAI,KAAK,KAAK,IAAI;gBAClD,CAAC,CAAC,kCAAkC,KAAK,KAAK,CAAC,KAAW,CAAC;gBAC3D,CAAC,CAAC,KAAK,CAAC;YACZ,OAAO;gBACL,GAAG,GAAG;gBACN,0BAA0B;aAC3B,CAAC;QACJ,CAAC,CAAC,CAAC;QAEH,gEAAgE;QAChE,IAAI,cAAc,CAAC,KAAK,CAAC,CAAC,MAAM,EAAE,EAAE,CAAC,CAAC,MAAM,CAAC,0BAA0B,CAAC,EAAE;YACxE,OAAO;SACR;QAED,IAAI,CAAC,MAAM,CAAC,CAAC,KAAK,EAAE,EAAE;;YACpB,KAAK,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC,GAAG,cAAc,CAAC,MAAM,EAAE,CAAC,EAAE,EAAE;gBAC9C,MAAM,EAAE,OAAO,EAAE,KAAK,EAAE,0BAA0B,EAAE,GAClD,cAAc,CAAC,CAAC,CAAC,CAAC;gBACpB,MAAM,EAAE,cAAc,EAAE,YAAY,EAAE,GAAG,iBAAiB,CAAC,CAAC,CAAC,CAAC;gBAC9D,IAAI,OAAO,IAAI,0BAA0B,EAAE;oBACzC,OAAC,OAAC,KAAK,CAAC,aAAa,EAAC,cAAc,SAAd,cAAc,IAAM,EAAE,EAAC,EAAC,OAAO,SAAP,OAAO,IAAM,EAAE,EAAC,CAC5D,YAAY,CACb,GAAG,KAAK,CAAC,KAAW,CAAC,CAAC;iBACxB;aACF;QACH,CAAC,CAAC,CAAC;IACL,CAAC;IAED;;OAEG;IACH,UAAU;QACR,IAAI,CAAC,MAAM,CAAC,GAAG,EAAE;YACf,OAAO,4BAA4B,EAAE,CAAC;QACxC,CAAC,CAAC,CAAC;IACL,CAAC;CA6BF;2kBAtVuB,CAAe,EAAE,OAAgB;IACrD,oCAAoC;IACpC,KAAK,MAAM,KAAK,IAAI,OAAO,EAAE;QAC3B,IACE,KAAK,CAAC,EAAE,KAAK,QAAQ;YACrB,KAAK,CAAC,IAAI,CAAC,CAAC,CAAC,KAAK,gCAAgC,EAClD;YACA,MAAM,cAAc,GAAG,KAAK,CAAC,IAAI,CAAC,CAAC,CAAQ,CAAC;YAE5C,IAAI,CAAC,MAAM,CAAC,CAAC,KAAK,EAAE,EAAE;gBACpB,KAAK,MAAM,cAAc,IAAI,MAAM,CAAC,IAAI,CAAC,KAAK,CAAC,aAAa,CAAC,EAAE;oBAC7D,OAAO,KAAK,CAAC,aAAa,CAAC,cAAqB,CAAC,CAAC,cAAc,CAAC,CAAC;iBACnE;YACH,CAAC,CAAC,CAAC;SACJ;KACF;AACH,CAAC,6GAOuB,cAAsB;IAC5C,MAAM,YAAY,GAChB,iBAAiB,CAAC,cAAc,CAAC,WAAW,EAAE,CAAC;QAC/C,iBAAiB,CAAC,cAAc,CAAC,CAAC;IACpC,IAAI,CAAC,YAAY,EAAE;QACjB,OAAO;KACR;IAED,IAAI,CAAC,MAAM,CAAC,CAAC,KAAK,EAAE,EAAE;QACpB,OAAO,KAAK,CAAC,aAAa,CAAC,cAA+B,CAAC,CAAC;IAC9D,CAAC,CAAC,CAAC;AACL,CAAC,+DA0CD,KAAK,qEAAoC,EACvC,QAAQ,MACgB,EAAE;IAC1B,MAAM,yBAAyB,GAAG,IAAI,CAAC,eAAe,CAAC,IAAI,CACzD,kCAAkC,CACnC,CAAC;IACF,MAAM,oBAAoB,GAAG,yBAAyB,CAAC,aAAa,CAAC;IACrE,MAAM,gBAAgB,GAAG,uBAAA,IAAI,0CAAW,CAAC;IACzC,MAAM,WAAW,GAAG,IAAI,GAAG,CAAC,QAAQ,CAAC,CAAC;IAEtC,sEAAsE;IACtE,KAAK,MAAM,cAAc,IAAI,MAAM,CAAC,IAAI,CAAC,oBAAoB,CAAC,EAAE;QAC9D,MAAM,SAAS,GAAG,oBAAoB,CAAC,cAA+B,CAAC,CAAC;QACxE,KAAK,MAAM,YAAY,IAAI,MAAM,CAAC,IAAI,CAAC,SAAS,CAAC,EAAE;YACjD,IAAI,QAAQ,EAAE,MAAM,IAAI,CAAC,WAAW,CAAC,GAAG,CAAC,YAAmB,CAAC,EAAE;gBAC7D,SAAS;aACV;YACD,MAAM,YAAY,GAAG,SAAS,CAAC,YAAmB,CAAC,CAAC;YACpD,MAAM,gBAAgB,GAAG,MAAM,CAAC,IAAI,CAAC,YAAY,CAAC,CAAC;YACnD,MAAM,qBAAqB,GACzB,gBAAgB,CAAC,YAAmB,CAAC,EAAE,CACrC,cAA+B,CAChC,IAAI,EAAE,CAAC;YACV,MAAM,WAAW,GAAG,IAAI,GAAG,CACzB,qBAAqB,CAAC,GAAG,CAAC,CAAC,GAAG,EAAE,EAAE,CAAC,GAAG,CAAC,OAAO,CAAC,CAChD,CAAC;YAEF,KAAK,MAAM,WAAW,IAAI,gBAAgB,EAAE;gBAC1C,IAAI,CAAC,WAAW,CAAC,GAAG,CAAC,WAAW,CAAC,EAAE;oBACjC,IAAI,CAAC,MAAM,CAAC,CAAC,KAAK,EAAE,EAAE;wBACpB,OAAO,KAAK,CAAC,aAAa,CAAC,cAAqB,CAAC,CAC/C,YAAmB,CACpB,CAAC,WAA4B,CAAC,CAAC;oBAClC,CAAC,CAAC,CAAC;iBACJ;aACF;SACF;KACF;IAED,mEAAmE;IACnE,IAAI,YAAY,GAAG,KAAK,CAAC;IACzB,KAAK,MAAM,YAAY,IAAI,MAAM,CAAC,IAAI,CAAC,gBAAgB,CAAC,EAAE;QACxD,IAAI,QAAQ,EAAE,MAAM,IAAI,CAAC,WAAW,CAAC,GAAG,CAAC,YAAmB,CAAC,EAAE;YAC7D,SAAS;SACV;QACD,MAAM,gBAAgB,GAAG,gBAAgB,CAAC,YAAmB,CAAC,CAAC;QAE/D,KAAK,MAAM,cAAc,IAAI,MAAM,CAAC,IAAI,CAAC,gBAAgB,CAAC,EAAE;YAC1D,MAAM,UAAU,GAAG,gBAAgB,CAAC,cAA+B,CAAC,CAAC;YACrE,MAAM,mBAAmB,GACvB,oBAAoB,CAAC,cAA+B,CAAC,EAAE,CACrD,YAAmB,CACpB,IAAI,EAAE,CAAC;YACV,KAAK,MAAM,WAAW,IAAI,UAAU,EAAE;gBACpC,IAAI,CAAC,mBAAmB,EAAE,CAAC,WAAW,CAAC,OAAwB,CAAC,EAAE;oBAChE,YAAY,GAAG,IAAI,CAAC;oBACpB,MAAM;iBACP;aACF;SACF;KACF;IACD,IAAI,YAAY,EAAE;QAChB,MAAM,IAAI,CAAC,cAAc,CAAC,EAAE,QAAQ,EAAE,CAAC,CAAC,KAAK,CAAC,OAAO,CAAC,KAAK,CAAC,CAAC;KAC9D;AACH,CAAC,uFAQY,OAAY;IACvB,OAAO,IAAI,YAAY,CAAC,uBAAA,IAAI,qFAAkB,MAAtB,IAAI,EAAmB,OAAO,CAAC,CAAC,QAAQ,CAAC,CAAC;AACpE,CAAC;AAED;;;;;;;GAOG;AACH,KAAK,kDAAiB,EACpB,OAAO,EACP,KAAK,GAIN;IACC,IAAI,CAAC,KAAK,CAAC,MAAM,EAAE;QACjB,OAAO,EAAE,CAAC;KACX;IAED,MAAM,QAAQ,GAAG,uBAAA,IAAI,gFAAa,MAAjB,IAAI,EAAc,OAAO,CAAC,CAAC;IAE5C,MAAM,KAAK,GAAG,KAAK,CAAC,GAAG,CAAC,CAAC,EAAE,cAAc,EAAE,YAAY,EAAE,EAAE,EAAE,CAAC,CAAC;QAC7D,QAAQ,EAAE,IAAI,QAAQ,CAAC,YAAY,EAAE,QAAQ,EAAE,QAAQ,CAAC;QACxD,iBAAiB,EAAE,oBAAoB;QACvC,SAAS,EAAE,CAAC,cAAc,CAAC;KAC5B,CAAC,CAAC,CAAC;IAEJ,OAAO,mBAAmB,CAAC,KAAK,EAAE,OAAO,EAAE,QAAQ,CAAC,CAAC;AACvD,CAAC,iGA4IiB,OAAY;IAC5B,MAAM,EAAE,8BAA8B,EAAE,GAAG,IAAI,CAAC,eAAe,CAAC,IAAI,CAClE,4BAA4B,CAC7B,CAAC;IAEF,MAAM,oBAAoB,GAAG,8BAA8B,CAAC,OAAO,CAAC,CAAC;IACrE,IAAI,CAAC,oBAAoB,EAAE;QACzB,MAAM,IAAI,KAAK,CACb,uEAAuE,OAAO,EAAE,CACjF,CAAC;KACH;IAED,MAAM,EAAE,eAAe,EAAE,GACvB,oBAAoB,CAAC,YAAY,CAC/B,oBAAoB,CAAC,uBAAuB,CAC7C,CAAC;IAEJ,OAAO,IAAI,CAAC,eAAe,CAAC,IAAI,CAC9B,wCAAwC,EACxC,eAAe,CAChB,CAAC;AACJ,CAAC;AAGH,eAAe,uBAAuB,CAAC","sourcesContent":["import { Contract } from '@ethersproject/contracts';\nimport { Web3Provider } from '@ethersproject/providers';\nimport type {\n AccountsControllerGetSelectedAccountAction,\n AccountsControllerListAccountsAction,\n} from '@metamask/accounts-controller';\nimport type {\n RestrictedMessenger,\n ControllerGetStateAction,\n ControllerStateChangeEvent,\n} from '@metamask/base-controller';\nimport {\n isValidHexAddress,\n toChecksumHexAddress,\n toHex,\n} from '@metamask/controller-utils';\nimport type { KeyringControllerAccountRemovedEvent } from '@metamask/keyring-controller';\nimport { abiERC20 } from '@metamask/metamask-eth-abis';\nimport type {\n NetworkControllerGetNetworkClientByIdAction,\n NetworkControllerGetStateAction,\n NetworkControllerStateChangeEvent,\n NetworkState,\n} from '@metamask/network-controller';\nimport { StaticIntervalPollingController } from '@metamask/polling-controller';\nimport type {\n PreferencesControllerGetStateAction,\n PreferencesControllerStateChangeEvent,\n PreferencesState,\n} from '@metamask/preferences-controller';\nimport { isStrictHexString, type Hex } from '@metamask/utils';\nimport type BN from 'bn.js';\nimport type { Patch } from 'immer';\nimport { isEqual } from 'lodash';\n\nimport type { MulticallResult } from './multicall';\nimport { multicallOrFallback } from './multicall';\nimport type { Token } from './TokenRatesController';\nimport type {\n TokensControllerGetStateAction,\n TokensControllerState,\n TokensControllerStateChangeEvent,\n} from './TokensController';\n\nconst DEFAULT_INTERVAL = 180000;\n\nconst controllerName = 'TokenBalancesController';\n\nconst metadata = {\n tokenBalances: { persist: true, anonymous: false },\n};\n\n/**\n * Token balances controller options\n * @property interval - Polling interval used to fetch new token balances.\n * @property messenger - A messenger.\n * @property state - Initial state for the controller.\n */\ntype TokenBalancesControllerOptions = {\n interval?: number;\n messenger: TokenBalancesControllerMessenger;\n state?: Partial<TokenBalancesControllerState>;\n};\n\n/**\n * A mapping from account address to chain id to token address to balance.\n */\ntype TokenBalances = Record<Hex, Record<Hex, Record<Hex, Hex>>>;\n\n/**\n * Token balances controller state\n * @property tokenBalances - A mapping from account address to chain id to token address to balance.\n */\nexport type TokenBalancesControllerState = {\n tokenBalances: TokenBalances;\n};\n\nexport type TokenBalancesControllerGetStateAction = ControllerGetStateAction<\n typeof controllerName,\n TokenBalancesControllerState\n>;\n\nexport type TokenBalancesControllerActions =\n TokenBalancesControllerGetStateAction;\n\nexport type AllowedActions =\n | NetworkControllerGetNetworkClientByIdAction\n | NetworkControllerGetStateAction\n | TokensControllerGetStateAction\n | PreferencesControllerGetStateAction\n | AccountsControllerGetSelectedAccountAction\n | AccountsControllerListAccountsAction;\n\nexport type TokenBalancesControllerStateChangeEvent =\n ControllerStateChangeEvent<\n typeof controllerName,\n TokenBalancesControllerState\n >;\n\nexport type TokenBalancesControllerEvents =\n TokenBalancesControllerStateChangeEvent;\n\nexport type AllowedEvents =\n | TokensControllerStateChangeEvent\n | PreferencesControllerStateChangeEvent\n | NetworkControllerStateChangeEvent\n | KeyringControllerAccountRemovedEvent;\n\nexport type TokenBalancesControllerMessenger = RestrictedMessenger<\n typeof controllerName,\n TokenBalancesControllerActions | AllowedActions,\n TokenBalancesControllerEvents | AllowedEvents,\n AllowedActions['type'],\n AllowedEvents['type']\n>;\n\n/**\n * Get the default TokenBalancesController state.\n *\n * @returns The default TokenBalancesController state.\n */\nexport function getDefaultTokenBalancesState(): TokenBalancesControllerState {\n return {\n tokenBalances: {},\n };\n}\n\n/** The input to start polling for the {@link TokenBalancesController} */\nexport type TokenBalancesPollingInput = {\n chainId: Hex;\n};\n\n/**\n * Controller that passively polls on a set interval token balances\n * for tokens stored in the TokensController\n */\nexport class TokenBalancesController extends StaticIntervalPollingController<TokenBalancesPollingInput>()<\n typeof controllerName,\n TokenBalancesControllerState,\n TokenBalancesControllerMessenger\n> {\n #queryMultipleAccounts: boolean;\n\n #allTokens: TokensControllerState['allTokens'];\n\n #allDetectedTokens: TokensControllerState['allDetectedTokens'];\n\n /**\n * Construct a Token Balances Controller.\n *\n * @param options - The controller options.\n * @param options.interval - Polling interval used to fetch new token balances.\n * @param options.state - Initial state to set on this controller.\n * @param options.messenger - The controller restricted messenger.\n */\n constructor({\n interval = DEFAULT_INTERVAL,\n messenger,\n state = {},\n }: TokenBalancesControllerOptions) {\n super({\n name: controllerName,\n metadata,\n messenger,\n state: {\n ...getDefaultTokenBalancesState(),\n ...state,\n },\n });\n\n this.setIntervalLength(interval);\n\n // Set initial preference for querying multiple accounts, and subscribe to changes\n this.#queryMultipleAccounts = this.#calculateQueryMultipleAccounts(\n this.messagingSystem.call('PreferencesController:getState'),\n );\n this.messagingSystem.subscribe(\n 'PreferencesController:stateChange',\n this.#onPreferencesStateChange.bind(this),\n );\n\n // Set initial tokens, and subscribe to changes\n ({\n allTokens: this.#allTokens,\n allDetectedTokens: this.#allDetectedTokens,\n } = this.messagingSystem.call('TokensController:getState'));\n\n this.messagingSystem.subscribe(\n 'TokensController:stateChange',\n this.#onTokensStateChange.bind(this),\n );\n\n // Subscribe to network state changes\n this.messagingSystem.subscribe(\n 'NetworkController:stateChange',\n this.#onNetworkStateChange.bind(this),\n );\n\n // subscribe to account removed event to cleanup stale balances\n\n this.messagingSystem.subscribe(\n 'KeyringController:accountRemoved',\n (accountAddress: string) => this.#handleOnAccountRemoved(accountAddress),\n );\n }\n\n /**\n * Determines whether to query all accounts, or just the selected account.\n * @param preferences - The preferences state.\n * @param preferences.isMultiAccountBalancesEnabled - whether to query all accounts (mobile).\n * @param preferences.useMultiAccountBalanceChecker - whether to query all accounts (extension).\n * @returns true if all accounts should be queried.\n */\n #calculateQueryMultipleAccounts = ({\n isMultiAccountBalancesEnabled,\n useMultiAccountBalanceChecker,\n }: PreferencesState & { useMultiAccountBalanceChecker?: boolean }) => {\n return Boolean(\n // Note: These settings have different names on extension vs mobile\n isMultiAccountBalancesEnabled || useMultiAccountBalanceChecker,\n );\n };\n\n /**\n * Handles the event for preferences state changes.\n * @param preferences - The preferences state.\n */\n #onPreferencesStateChange = (preferences: PreferencesState) => {\n // Update the user preference for whether to query multiple accounts.\n const queryMultipleAccounts =\n this.#calculateQueryMultipleAccounts(preferences);\n\n // Refresh when flipped off -> on\n const refresh = queryMultipleAccounts && !this.#queryMultipleAccounts;\n this.#queryMultipleAccounts = queryMultipleAccounts;\n\n if (refresh) {\n this.updateBalances().catch(console.error);\n }\n };\n\n /**\n * Handles the event for tokens state changes.\n * @param state - The token state.\n * @param state.allTokens - The state for imported tokens across all chains.\n * @param state.allDetectedTokens - The state for detected tokens across all chains.\n */\n #onTokensStateChange = ({\n allTokens,\n allDetectedTokens,\n }: TokensControllerState) => {\n // Refresh token balances on chains whose tokens have changed.\n const chainIds = this.#getChainIds(allTokens, allDetectedTokens);\n const chainIdsToUpdate = chainIds.filter(\n (chainId) =>\n !isEqual(this.#allTokens[chainId], allTokens[chainId]) ||\n !isEqual(this.#allDetectedTokens[chainId], allDetectedTokens[chainId]),\n );\n\n this.#allTokens = allTokens;\n this.#allDetectedTokens = allDetectedTokens;\n this.#handleTokensControllerStateChange({\n chainIds: chainIdsToUpdate,\n }).catch(console.error);\n };\n\n /**\n * Handles the event for network state changes.\n * @param _ - The network state.\n * @param patches - An array of patch operations performed on the network state.\n */\n #onNetworkStateChange(_: NetworkState, patches: Patch[]) {\n // Remove state for deleted networks\n for (const patch of patches) {\n if (\n patch.op === 'remove' &&\n patch.path[0] === 'networkConfigurationsByChainId'\n ) {\n const removedChainId = patch.path[1] as Hex;\n\n this.update((state) => {\n for (const accountAddress of Object.keys(state.tokenBalances)) {\n delete state.tokenBalances[accountAddress as Hex][removedChainId];\n }\n });\n }\n }\n }\n\n /**\n * Handles changes when an account has been removed.\n *\n * @param accountAddress - The account address being removed.\n */\n #handleOnAccountRemoved(accountAddress: string) {\n const isEthAddress =\n isStrictHexString(accountAddress.toLowerCase()) &&\n isValidHexAddress(accountAddress);\n if (!isEthAddress) {\n return;\n }\n\n this.update((state) => {\n delete state.tokenBalances[accountAddress as `0x${string}`];\n });\n }\n\n /**\n * Returns an array of chain ids that have tokens.\n * @param allTokens - The state for imported tokens across all chains.\n * @param allDetectedTokens - The state for detected tokens across all chains.\n * @returns An array of chain ids that have tokens.\n */\n #getChainIds = (\n allTokens: TokensControllerState['allTokens'],\n allDetectedTokens: TokensControllerState['allDetectedTokens'],\n ) =>\n [\n ...new Set([\n ...Object.keys(allTokens),\n ...Object.keys(allDetectedTokens),\n ]),\n ] as Hex[];\n\n /**\n * Polls for erc20 token balances.\n * @param input - The input for the poll.\n * @param input.chainId - The chain id to poll token balances on.\n */\n async _executePoll({ chainId }: TokenBalancesPollingInput) {\n await this.updateBalancesByChainId({ chainId });\n }\n\n /**\n * Updates the token balances for the given chain ids.\n * @param input - The input for the update.\n * @param input.chainIds - The chain ids to update token balances for.\n * Or omitted to update all chains that contain tokens.\n */\n async updateBalances({ chainIds }: { chainIds?: Hex[] } = {}) {\n chainIds ??= this.#getChainIds(this.#allTokens, this.#allDetectedTokens);\n\n await Promise.allSettled(\n chainIds.map((chainId) => this.updateBalancesByChainId({ chainId })),\n );\n }\n\n async #handleTokensControllerStateChange({\n chainIds,\n }: { chainIds?: Hex[] } = {}) {\n const currentTokenBalancesState = this.messagingSystem.call(\n 'TokenBalancesController:getState',\n );\n const currentTokenBalances = currentTokenBalancesState.tokenBalances;\n const currentAllTokens = this.#allTokens;\n const chainIdsSet = new Set(chainIds);\n\n // first we check if the state change was due to a token being removed\n for (const currentAccount of Object.keys(currentTokenBalances)) {\n const allChains = currentTokenBalances[currentAccount as `0x${string}`];\n for (const currentChain of Object.keys(allChains)) {\n if (chainIds?.length && !chainIdsSet.has(currentChain as Hex)) {\n continue;\n }\n const tokensObject = allChains[currentChain as Hex];\n const allCurrentTokens = Object.keys(tokensObject);\n const existingTokensInState =\n currentAllTokens[currentChain as Hex]?.[\n currentAccount as `0x${string}`\n ] || [];\n const existingSet = new Set(\n existingTokensInState.map((elm) => elm.address),\n );\n\n for (const singleToken of allCurrentTokens) {\n if (!existingSet.has(singleToken)) {\n this.update((state) => {\n delete state.tokenBalances[currentAccount as Hex][\n currentChain as Hex\n ][singleToken as `0x${string}`];\n });\n }\n }\n }\n }\n\n // then we check if the state change was due to a token being added\n let shouldUpdate = false;\n for (const currentChain of Object.keys(currentAllTokens)) {\n if (chainIds?.length && !chainIdsSet.has(currentChain as Hex)) {\n continue;\n }\n const accountsPerChain = currentAllTokens[currentChain as Hex];\n\n for (const currentAccount of Object.keys(accountsPerChain)) {\n const tokensList = accountsPerChain[currentAccount as `0x${string}`];\n const tokenBalancesObject =\n currentTokenBalances[currentAccount as `0x${string}`]?.[\n currentChain as Hex\n ] || {};\n for (const singleToken of tokensList) {\n if (!tokenBalancesObject?.[singleToken.address as `0x${string}`]) {\n shouldUpdate = true;\n break;\n }\n }\n }\n }\n if (shouldUpdate) {\n await this.updateBalances({ chainIds }).catch(console.error);\n }\n }\n\n /**\n * Get an Ethers.js Web3Provider for the requested chain.\n *\n * @param chainId - The chain id to get the provider for.\n * @returns The provider for the given chain id.\n */\n #getProvider(chainId: Hex): Web3Provider {\n return new Web3Provider(this.#getNetworkClient(chainId).provider);\n }\n\n /**\n * Internal util: run `balanceOf` for an arbitrary set of account/token pairs.\n *\n * @param params - The parameters for the balance fetch.\n * @param params.chainId - The chain id to fetch balances on.\n * @param params.pairs - The account/token pairs to fetch balances for.\n * @returns The balances for the given token addresses.\n */\n async #batchBalanceOf({\n chainId,\n pairs,\n }: {\n chainId: Hex;\n pairs: { accountAddress: Hex; tokenAddress: Hex }[];\n }): Promise<MulticallResult[]> {\n if (!pairs.length) {\n return [];\n }\n\n const provider = this.#getProvider(chainId);\n\n const calls = pairs.map(({ accountAddress, tokenAddress }) => ({\n contract: new Contract(tokenAddress, abiERC20, provider),\n functionSignature: 'balanceOf(address)',\n arguments: [accountAddress],\n }));\n\n return multicallOrFallback(calls, chainId, provider);\n }\n\n /**\n * Returns ERC-20 balances for a single account on a single chain.\n *\n * @param params - The parameters for the balance fetch.\n * @param params.chainId - The chain id to fetch balances on.\n * @param params.accountAddress - The account address to fetch balances for.\n * @param params.tokenAddresses - The token addresses to fetch balances for.\n * @returns A mapping from token address to balance (hex) | null.\n */\n async getErc20Balances({\n chainId,\n accountAddress,\n tokenAddresses,\n }: {\n chainId: Hex;\n accountAddress: Hex;\n tokenAddresses: Hex[];\n }): Promise<Record<Hex, Hex | null>> {\n // Return early if no token addresses provided\n if (tokenAddresses.length === 0) {\n return {};\n }\n\n const pairs = tokenAddresses.map((tokenAddress) => ({\n accountAddress,\n tokenAddress,\n }));\n\n const results = await this.#batchBalanceOf({ chainId, pairs });\n\n const balances: Record<Hex, Hex | null> = {};\n tokenAddresses.forEach((tokenAddress, i) => {\n balances[tokenAddress] = results[i]?.success\n ? toHex(results[i].value as BN)\n : null;\n });\n\n return balances;\n }\n\n /**\n * Updates token balances for the given chain id.\n * @param input - The input for the update.\n * @param input.chainId - The chain id to update token balances on.\n */\n async updateBalancesByChainId({ chainId }: { chainId: Hex }) {\n const { address: selectedAccountAddress } = this.messagingSystem.call(\n 'AccountsController:getSelectedAccount',\n );\n\n const isSelectedAccount = (accountAddress: string) =>\n toChecksumHexAddress(accountAddress) ===\n toChecksumHexAddress(selectedAccountAddress);\n\n const accountTokenPairs: { accountAddress: Hex; tokenAddress: Hex }[] = [];\n\n const addTokens = ([accountAddress, tokens]: [string, Token[]]) =>\n this.#queryMultipleAccounts || isSelectedAccount(accountAddress)\n ? tokens.forEach((t) =>\n accountTokenPairs.push({\n accountAddress: accountAddress as Hex,\n tokenAddress: t.address as Hex,\n }),\n )\n : undefined;\n\n // Balances will be updated for both imported and detected tokens\n Object.entries(this.#allTokens[chainId] ?? {}).forEach(addTokens);\n Object.entries(this.#allDetectedTokens[chainId] ?? {}).forEach(addTokens);\n\n let results: MulticallResult[] = [];\n\n const currentTokenBalances = this.messagingSystem.call(\n 'TokenBalancesController:getState',\n );\n\n if (accountTokenPairs.length > 0) {\n results = await this.#batchBalanceOf({\n chainId,\n pairs: accountTokenPairs,\n });\n }\n\n const updatedResults: (MulticallResult & {\n isTokenBalanceValueChanged?: boolean;\n })[] = results.map((res, i) => {\n const { value } = res;\n const { accountAddress, tokenAddress } = accountTokenPairs[i];\n const currentTokenBalanceValueForAccount =\n currentTokenBalances.tokenBalances?.[accountAddress]?.[chainId]?.[\n tokenAddress\n ];\n // `value` can be null or undefined if the multicall failed due to RPC issue.\n // Please see packages/assets-controllers/src/multicall.ts#L365.\n // Hence we should not update the balance in that case.\n const isTokenBalanceValueChanged =\n res.success && value !== undefined && value !== null\n ? currentTokenBalanceValueForAccount !== toHex(value as BN)\n : false;\n return {\n ...res,\n isTokenBalanceValueChanged,\n };\n });\n\n // if all values of isTokenBalanceValueChanged are false, return\n if (updatedResults.every((result) => !result.isTokenBalanceValueChanged)) {\n return;\n }\n\n this.update((state) => {\n for (let i = 0; i < updatedResults.length; i++) {\n const { success, value, isTokenBalanceValueChanged } =\n updatedResults[i];\n const { accountAddress, tokenAddress } = accountTokenPairs[i];\n if (success && isTokenBalanceValueChanged) {\n ((state.tokenBalances[accountAddress] ??= {})[chainId] ??= {})[\n tokenAddress\n ] = toHex(value as BN);\n }\n }\n });\n }\n\n /**\n * Reset the controller state to the default state.\n */\n resetState() {\n this.update(() => {\n return getDefaultTokenBalancesState();\n });\n }\n\n /**\n * Returns the network client for a given chain id\n * @param chainId - The chain id to get the network client for.\n * @returns The network client for the given chain id.\n */\n #getNetworkClient(chainId: Hex) {\n const { networkConfigurationsByChainId } = this.messagingSystem.call(\n 'NetworkController:getState',\n );\n\n const networkConfiguration = networkConfigurationsByChainId[chainId];\n if (!networkConfiguration) {\n throw new Error(\n `TokenBalancesController: No network configuration found for chainId ${chainId}`,\n );\n }\n\n const { networkClientId } =\n networkConfiguration.rpcEndpoints[\n networkConfiguration.defaultRpcEndpointIndex\n ];\n\n return this.messagingSystem.call(\n `NetworkController:getNetworkClientById`,\n networkClientId,\n );\n }\n}\n\nexport default TokenBalancesController;\n"]}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@metamask-previews/assets-controllers",
|
|
3
|
-
"version": "68.1.0-preview-
|
|
3
|
+
"version": "68.1.0-preview-4193b35",
|
|
4
4
|
"description": "Controllers which manage interactions involving ERC-20, ERC-721, and ERC-1155 tokens (including NFTs)",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"MetaMask",
|
|
@@ -63,7 +63,6 @@
|
|
|
63
63
|
"@metamask/polling-controller": "^13.0.0",
|
|
64
64
|
"@metamask/rpc-errors": "^7.0.2",
|
|
65
65
|
"@metamask/snaps-utils": "^9.4.0",
|
|
66
|
-
"@metamask/superstruct": "^3.1.0",
|
|
67
66
|
"@metamask/utils": "^11.2.0",
|
|
68
67
|
"@types/bn.js": "^5.1.5",
|
|
69
68
|
"@types/uuid": "^8.3.0",
|