@metamask-previews/assets-controller 0.2.0-preview-c3cd77f → 0.2.0-preview-d01b2f93d
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 +4 -0
- package/dist/AssetsController-method-action-types.cjs +7 -0
- package/dist/AssetsController-method-action-types.cjs.map +1 -0
- package/dist/AssetsController-method-action-types.d.cts +78 -0
- package/dist/AssetsController-method-action-types.d.cts.map +1 -0
- package/dist/AssetsController-method-action-types.d.mts +78 -0
- package/dist/AssetsController-method-action-types.d.mts.map +1 -0
- package/dist/AssetsController-method-action-types.mjs +6 -0
- package/dist/AssetsController-method-action-types.mjs.map +1 -0
- package/dist/AssetsController.cjs +161 -116
- package/dist/AssetsController.cjs.map +1 -1
- package/dist/AssetsController.d.cts +23 -80
- package/dist/AssetsController.d.cts.map +1 -1
- package/dist/AssetsController.d.mts +23 -80
- package/dist/AssetsController.d.mts.map +1 -1
- package/dist/AssetsController.mjs +161 -116
- package/dist/AssetsController.mjs.map +1 -1
- package/dist/data-sources/AbstractDataSource.cjs.map +1 -1
- package/dist/data-sources/AbstractDataSource.d.cts +10 -1
- package/dist/data-sources/AbstractDataSource.d.cts.map +1 -1
- package/dist/data-sources/AbstractDataSource.d.mts +10 -1
- package/dist/data-sources/AbstractDataSource.d.mts.map +1 -1
- package/dist/data-sources/AbstractDataSource.mjs.map +1 -1
- package/dist/data-sources/AccountsApiDataSource.cjs +23 -99
- package/dist/data-sources/AccountsApiDataSource.cjs.map +1 -1
- package/dist/data-sources/AccountsApiDataSource.d.cts +5 -67
- package/dist/data-sources/AccountsApiDataSource.d.cts.map +1 -1
- package/dist/data-sources/AccountsApiDataSource.d.mts +5 -67
- package/dist/data-sources/AccountsApiDataSource.d.mts.map +1 -1
- package/dist/data-sources/AccountsApiDataSource.mjs +22 -97
- package/dist/data-sources/AccountsApiDataSource.mjs.map +1 -1
- package/dist/data-sources/BackendWebsocketDataSource.cjs +135 -45
- package/dist/data-sources/BackendWebsocketDataSource.cjs.map +1 -1
- package/dist/data-sources/BackendWebsocketDataSource.d.cts +19 -66
- package/dist/data-sources/BackendWebsocketDataSource.d.cts.map +1 -1
- package/dist/data-sources/BackendWebsocketDataSource.d.mts +19 -66
- package/dist/data-sources/BackendWebsocketDataSource.d.mts.map +1 -1
- package/dist/data-sources/BackendWebsocketDataSource.mjs +135 -45
- package/dist/data-sources/BackendWebsocketDataSource.mjs.map +1 -1
- package/dist/data-sources/PriceDataSource.cjs +22 -44
- package/dist/data-sources/PriceDataSource.cjs.map +1 -1
- package/dist/data-sources/PriceDataSource.d.cts +6 -89
- package/dist/data-sources/PriceDataSource.d.cts.map +1 -1
- package/dist/data-sources/PriceDataSource.d.mts +6 -89
- package/dist/data-sources/PriceDataSource.d.mts.map +1 -1
- package/dist/data-sources/PriceDataSource.mjs +22 -44
- package/dist/data-sources/PriceDataSource.mjs.map +1 -1
- package/dist/data-sources/RpcDataSource.cjs +57 -98
- package/dist/data-sources/RpcDataSource.cjs.map +1 -1
- package/dist/data-sources/RpcDataSource.d.cts +16 -55
- package/dist/data-sources/RpcDataSource.d.cts.map +1 -1
- package/dist/data-sources/RpcDataSource.d.mts +16 -55
- package/dist/data-sources/RpcDataSource.d.mts.map +1 -1
- package/dist/data-sources/RpcDataSource.mjs +57 -98
- package/dist/data-sources/RpcDataSource.mjs.map +1 -1
- package/dist/data-sources/SnapDataSource.cjs +30 -30
- package/dist/data-sources/SnapDataSource.cjs.map +1 -1
- package/dist/data-sources/SnapDataSource.d.cts +7 -44
- package/dist/data-sources/SnapDataSource.d.cts.map +1 -1
- package/dist/data-sources/SnapDataSource.d.mts +7 -44
- package/dist/data-sources/SnapDataSource.d.mts.map +1 -1
- package/dist/data-sources/SnapDataSource.mjs +30 -30
- package/dist/data-sources/SnapDataSource.mjs.map +1 -1
- package/dist/data-sources/TokenDataSource.cjs +3 -16
- package/dist/data-sources/TokenDataSource.cjs.map +1 -1
- package/dist/data-sources/TokenDataSource.d.cts +2 -25
- package/dist/data-sources/TokenDataSource.d.cts.map +1 -1
- package/dist/data-sources/TokenDataSource.d.mts +2 -25
- package/dist/data-sources/TokenDataSource.d.mts.map +1 -1
- package/dist/data-sources/TokenDataSource.mjs +3 -16
- package/dist/data-sources/TokenDataSource.mjs.map +1 -1
- package/dist/data-sources/index.cjs +1 -6
- package/dist/data-sources/index.cjs.map +1 -1
- package/dist/data-sources/index.d.cts +6 -7
- package/dist/data-sources/index.d.cts.map +1 -1
- package/dist/data-sources/index.d.mts +6 -7
- package/dist/data-sources/index.d.mts.map +1 -1
- package/dist/data-sources/index.mjs +1 -3
- package/dist/data-sources/index.mjs.map +1 -1
- package/dist/index.cjs +1 -6
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +9 -11
- package/dist/index.d.cts.map +1 -1
- package/dist/index.d.mts +9 -11
- package/dist/index.d.mts.map +1 -1
- package/dist/index.mjs +1 -3
- package/dist/index.mjs.map +1 -1
- package/dist/middlewares/DetectionMiddleware.cjs +4 -27
- package/dist/middlewares/DetectionMiddleware.cjs.map +1 -1
- package/dist/middlewares/DetectionMiddleware.d.cts +3 -26
- package/dist/middlewares/DetectionMiddleware.d.cts.map +1 -1
- package/dist/middlewares/DetectionMiddleware.d.mts +3 -26
- package/dist/middlewares/DetectionMiddleware.d.mts.map +1 -1
- package/dist/middlewares/DetectionMiddleware.mjs +4 -27
- package/dist/middlewares/DetectionMiddleware.mjs.map +1 -1
- package/dist/middlewares/index.cjs.map +1 -1
- package/dist/middlewares/index.d.cts +1 -1
- package/dist/middlewares/index.d.cts.map +1 -1
- package/dist/middlewares/index.d.mts +1 -1
- package/dist/middlewares/index.d.mts.map +1 -1
- package/dist/middlewares/index.mjs.map +1 -1
- package/dist/types.cjs.map +1 -1
- package/dist/types.d.cts +52 -3
- package/dist/types.d.cts.map +1 -1
- package/dist/types.d.mts +52 -3
- package/dist/types.d.mts.map +1 -1
- package/dist/types.mjs.map +1 -1
- package/package.json +1 -1
- package/dist/data-sources/initDataSources.cjs +0 -215
- package/dist/data-sources/initDataSources.cjs.map +0 -1
- package/dist/data-sources/initDataSources.d.cts +0 -140
- package/dist/data-sources/initDataSources.d.cts.map +0 -1
- package/dist/data-sources/initDataSources.d.mts +0 -140
- package/dist/data-sources/initDataSources.d.mts.map +0 -1
- package/dist/data-sources/initDataSources.mjs +0 -210
- package/dist/data-sources/initDataSources.mjs.map +0 -1
|
@@ -9,7 +9,7 @@ var __classPrivateFieldGet = (this && this.__classPrivateFieldGet) || function (
|
|
|
9
9
|
if (typeof state === "function" ? receiver !== state || !f : !state.has(receiver)) throw new TypeError("Cannot read private member from an object whose class did not declare it");
|
|
10
10
|
return kind === "m" ? f : kind === "a" ? f.call(receiver) : f ? f.value : state.get(receiver);
|
|
11
11
|
};
|
|
12
|
-
var _PriceDataSource_instances,
|
|
12
|
+
var _PriceDataSource_instances, _PriceDataSource_currency, _PriceDataSource_pollInterval, _PriceDataSource_apiClient, _PriceDataSource_activeSubscriptions, _PriceDataSource_fetchSpotPrices, _PriceDataSource_getAssetIdsFromBalanceState;
|
|
13
13
|
import { ApiPlatformClient } from "@metamask/core-backend";
|
|
14
14
|
import { projectLogger, createModuleLogger } from "../logger.mjs";
|
|
15
15
|
import { forDataTypes } from "../types.mjs";
|
|
@@ -65,44 +65,23 @@ function isValidMarketData(data) {
|
|
|
65
65
|
* This data source:
|
|
66
66
|
* - Fetches prices from Price API v3 spot-prices endpoint
|
|
67
67
|
* - Supports one-time fetch and subscription-based polling
|
|
68
|
-
* - In subscribe mode,
|
|
69
|
-
* - Publishes price updates via messenger events
|
|
68
|
+
* - In subscribe mode, uses getAssetsState from SubscriptionRequest to read assetsBalance and fetch prices
|
|
70
69
|
*
|
|
71
|
-
* Usage:
|
|
72
|
-
* ```typescript
|
|
73
|
-
* // Create and initialize (registers messenger actions)
|
|
74
|
-
* const priceDataSource = new PriceDataSource({ messenger });
|
|
75
|
-
*
|
|
76
|
-
* // One-time fetch for specific assets
|
|
77
|
-
* const response = await messenger.call('PriceDataSource:fetch', {
|
|
78
|
-
* customAssets: ['eip155:1/erc20:0x...'],
|
|
79
|
-
* });
|
|
80
|
-
*
|
|
81
|
-
* // Subscribe to price updates (polls all assets in balance state)
|
|
82
|
-
* await messenger.call('PriceDataSource:subscribe', { request, subscriptionId });
|
|
83
|
-
*
|
|
84
|
-
* // Listen for updates
|
|
85
|
-
* messenger.subscribe('PriceDataSource:assetsUpdated', (response) => {
|
|
86
|
-
* // Handle price updates
|
|
87
|
-
* });
|
|
88
|
-
* ```
|
|
70
|
+
* Usage: Create with queryApiClient; subscribe() requires getAssetsState in the request for balance-based pricing.
|
|
89
71
|
*/
|
|
90
72
|
export class PriceDataSource {
|
|
91
73
|
constructor(options) {
|
|
92
74
|
_PriceDataSource_instances.add(this);
|
|
93
75
|
this.name = CONTROLLER_NAME;
|
|
94
|
-
_PriceDataSource_messenger.set(this, void 0);
|
|
95
76
|
_PriceDataSource_currency.set(this, void 0);
|
|
96
77
|
_PriceDataSource_pollInterval.set(this, void 0);
|
|
97
78
|
/** ApiPlatformClient for cached API calls */
|
|
98
79
|
_PriceDataSource_apiClient.set(this, void 0);
|
|
99
80
|
/** Active subscriptions by ID */
|
|
100
81
|
_PriceDataSource_activeSubscriptions.set(this, new Map());
|
|
101
|
-
__classPrivateFieldSet(this, _PriceDataSource_messenger, options.messenger, "f");
|
|
102
82
|
__classPrivateFieldSet(this, _PriceDataSource_currency, options.currency ?? 'usd', "f");
|
|
103
83
|
__classPrivateFieldSet(this, _PriceDataSource_pollInterval, options.pollInterval ?? DEFAULT_POLL_INTERVAL, "f");
|
|
104
84
|
__classPrivateFieldSet(this, _PriceDataSource_apiClient, options.queryApiClient, "f");
|
|
105
|
-
__classPrivateFieldGet(this, _PriceDataSource_instances, "m", _PriceDataSource_registerActionHandlers).call(this);
|
|
106
85
|
}
|
|
107
86
|
// ============================================================================
|
|
108
87
|
// MIDDLEWARE
|
|
@@ -171,15 +150,16 @@ export class PriceDataSource {
|
|
|
171
150
|
// ============================================================================
|
|
172
151
|
/**
|
|
173
152
|
* Fetch prices for assets held by the accounts and chains in the request.
|
|
174
|
-
*
|
|
153
|
+
* When getAssetsState is provided, gets asset IDs from balance state; otherwise returns empty.
|
|
175
154
|
*
|
|
176
155
|
* @param request - The data request specifying accounts and chains.
|
|
156
|
+
* @param getAssetsState - Optional state access (e.g. from SubscriptionRequest).
|
|
177
157
|
* @returns DataResponse containing asset prices.
|
|
178
158
|
*/
|
|
179
|
-
async fetch(request) {
|
|
159
|
+
async fetch(request, getAssetsState) {
|
|
180
160
|
const response = {};
|
|
181
|
-
// Get asset IDs from balance state
|
|
182
|
-
const rawAssetIds = __classPrivateFieldGet(this, _PriceDataSource_instances, "m", _PriceDataSource_getAssetIdsFromBalanceState).call(this, request);
|
|
161
|
+
// Get asset IDs from balance state when state access is provided
|
|
162
|
+
const rawAssetIds = __classPrivateFieldGet(this, _PriceDataSource_instances, "m", _PriceDataSource_getAssetIdsFromBalanceState).call(this, request, getAssetsState);
|
|
183
163
|
// Filter out non-priceable assets (e.g., Tron bandwidth/energy resources)
|
|
184
164
|
const assetIds = rawAssetIds.filter(isPriceableAsset);
|
|
185
165
|
if (assetIds.length === 0) {
|
|
@@ -227,19 +207,19 @@ export class PriceDataSource {
|
|
|
227
207
|
// Clean up existing subscription
|
|
228
208
|
await this.unsubscribe(subscriptionId);
|
|
229
209
|
const pollInterval = request.updateInterval ?? __classPrivateFieldGet(this, _PriceDataSource_pollInterval, "f");
|
|
230
|
-
// Create poll function - fetches prices
|
|
210
|
+
// Create poll function - fetches prices using getAssetsState from subscription
|
|
231
211
|
const pollFn = async () => {
|
|
232
212
|
try {
|
|
233
213
|
const subscription = __classPrivateFieldGet(this, _PriceDataSource_activeSubscriptions, "f").get(subscriptionId);
|
|
234
214
|
if (!subscription) {
|
|
235
215
|
return;
|
|
236
216
|
}
|
|
237
|
-
// Fetch prices for all assets
|
|
238
|
-
const fetchResponse = await this.fetch(subscription.request);
|
|
217
|
+
// Fetch prices for all assets in balance state (uses subscription's getAssetsState)
|
|
218
|
+
const fetchResponse = await this.fetch(subscription.request, subscription.getAssetsState);
|
|
239
219
|
// Only report if we got prices
|
|
240
220
|
if (fetchResponse.assetsPrice &&
|
|
241
221
|
Object.keys(fetchResponse.assetsPrice).length > 0) {
|
|
242
|
-
await
|
|
222
|
+
await subscription.onAssetsUpdate(fetchResponse);
|
|
243
223
|
}
|
|
244
224
|
}
|
|
245
225
|
catch (error) {
|
|
@@ -250,12 +230,14 @@ export class PriceDataSource {
|
|
|
250
230
|
const timer = setInterval(() => {
|
|
251
231
|
pollFn().catch(console.error);
|
|
252
232
|
}, pollInterval);
|
|
253
|
-
// Store subscription
|
|
233
|
+
// Store subscription (getAssetsState from request for balance-based pricing)
|
|
254
234
|
__classPrivateFieldGet(this, _PriceDataSource_activeSubscriptions, "f").set(subscriptionId, {
|
|
255
235
|
cleanup: () => {
|
|
256
236
|
clearInterval(timer);
|
|
257
237
|
},
|
|
258
238
|
request,
|
|
239
|
+
onAssetsUpdate: subscriptionRequest.onAssetsUpdate,
|
|
240
|
+
getAssetsState: subscriptionRequest.getAssetsState,
|
|
259
241
|
});
|
|
260
242
|
// Initial fetch
|
|
261
243
|
await pollFn();
|
|
@@ -282,14 +264,7 @@ export class PriceDataSource {
|
|
|
282
264
|
__classPrivateFieldGet(this, _PriceDataSource_activeSubscriptions, "f").clear();
|
|
283
265
|
}
|
|
284
266
|
}
|
|
285
|
-
|
|
286
|
-
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
287
|
-
const messenger = __classPrivateFieldGet(this, _PriceDataSource_messenger, "f");
|
|
288
|
-
messenger.registerActionHandler('PriceDataSource:getAssetsMiddleware', () => this.assetsMiddleware);
|
|
289
|
-
messenger.registerActionHandler('PriceDataSource:fetch', (request) => this.fetch(request));
|
|
290
|
-
messenger.registerActionHandler('PriceDataSource:subscribe', (subscriptionRequest) => this.subscribe(subscriptionRequest));
|
|
291
|
-
messenger.registerActionHandler('PriceDataSource:unsubscribe', (subscriptionId) => this.unsubscribe(subscriptionId));
|
|
292
|
-
}, _PriceDataSource_fetchSpotPrices =
|
|
267
|
+
_PriceDataSource_currency = new WeakMap(), _PriceDataSource_pollInterval = new WeakMap(), _PriceDataSource_apiClient = new WeakMap(), _PriceDataSource_activeSubscriptions = new WeakMap(), _PriceDataSource_instances = new WeakSet(), _PriceDataSource_fetchSpotPrices =
|
|
293
268
|
// ============================================================================
|
|
294
269
|
// HELPERS
|
|
295
270
|
// ============================================================================
|
|
@@ -304,11 +279,14 @@ async function _PriceDataSource_fetchSpotPrices(assetIds) {
|
|
|
304
279
|
currency: __classPrivateFieldGet(this, _PriceDataSource_currency, "f"),
|
|
305
280
|
includeMarketData: true,
|
|
306
281
|
});
|
|
307
|
-
}, _PriceDataSource_getAssetIdsFromBalanceState = function _PriceDataSource_getAssetIdsFromBalanceState(request) {
|
|
282
|
+
}, _PriceDataSource_getAssetIdsFromBalanceState = function _PriceDataSource_getAssetIdsFromBalanceState(request, getAssetsState) {
|
|
283
|
+
if (!getAssetsState) {
|
|
284
|
+
return [];
|
|
285
|
+
}
|
|
308
286
|
try {
|
|
309
|
-
const state =
|
|
287
|
+
const state = getAssetsState();
|
|
310
288
|
const assetIds = new Set();
|
|
311
|
-
const accountIds = request.
|
|
289
|
+
const accountIds = request.accountsWithSupportedChains.map((a) => a.account.id);
|
|
312
290
|
const accountFilter = accountIds.length > 0 ? new Set(accountIds) : undefined;
|
|
313
291
|
const chainFilter = request.chainIds.length > 0 ? new Set(request.chainIds) : undefined;
|
|
314
292
|
if (state?.assetsBalance) {
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"PriceDataSource.mjs","sourceRoot":"","sources":["../../src/data-sources/PriceDataSource.ts"],"names":[],"mappings":"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","sourcesContent":["import type {\n SupportedCurrency,\n V3SpotPricesResponse,\n} from '@metamask/core-backend';\nimport { ApiPlatformClient } from '@metamask/core-backend';\nimport type { Messenger } from '@metamask/messenger';\n\nimport type { SubscriptionRequest } from './AbstractDataSource';\nimport { projectLogger, createModuleLogger } from '../logger';\nimport { forDataTypes } from '../types';\nimport type {\n Caip19AssetId,\n AssetBalance,\n AccountId,\n ChainId,\n DataRequest,\n DataResponse,\n FungibleAssetPrice,\n Middleware,\n} from '../types';\n\n// ============================================================================\n// CONSTANTS\n// ============================================================================\n\nconst CONTROLLER_NAME = 'PriceDataSource';\nconst DEFAULT_POLL_INTERVAL = 60_000; // 1 minute for price updates\n\nconst log = createModuleLogger(projectLogger, CONTROLLER_NAME);\n\n// ============================================================================\n// MESSENGER TYPES\n// ============================================================================\n\n/**\n * Action to get balance state (used to determine which assets need prices).\n */\ntype GetAssetsBalanceStateAction = {\n type: 'AssetsController:getState';\n handler: () => {\n assetsBalance: Record<AccountId, Record<Caip19AssetId, AssetBalance>>;\n };\n};\n\n/**\n * Action to get the PriceDataSource middleware.\n */\nexport type PriceDataSourceGetAssetsMiddlewareAction = {\n type: `${typeof CONTROLLER_NAME}:getAssetsMiddleware`;\n handler: () => Middleware;\n};\n\n/**\n * Action to fetch prices for assets.\n */\nexport type PriceDataSourceFetchAction = {\n type: `${typeof CONTROLLER_NAME}:fetch`;\n handler: (request: DataRequest) => Promise<DataResponse>;\n};\n\n/**\n * Action to subscribe to price updates.\n */\nexport type PriceDataSourceSubscribeAction = {\n type: `${typeof CONTROLLER_NAME}:subscribe`;\n handler: (request: SubscriptionRequest) => Promise<void>;\n};\n\n/**\n * Action to unsubscribe from price updates.\n */\nexport type PriceDataSourceUnsubscribeAction = {\n type: `${typeof CONTROLLER_NAME}:unsubscribe`;\n handler: (subscriptionId: string) => Promise<void>;\n};\n\n/**\n * All actions exposed by PriceDataSource.\n */\nexport type PriceDataSourceActions =\n | PriceDataSourceGetAssetsMiddlewareAction\n | PriceDataSourceFetchAction\n | PriceDataSourceSubscribeAction\n | PriceDataSourceUnsubscribeAction;\n\n/**\n * Event emitted when prices are updated.\n */\nexport type PriceDataSourceAssetsUpdatedEvent = {\n type: `${typeof CONTROLLER_NAME}:assetsUpdated`;\n payload: [DataResponse, string];\n};\n\n/**\n * All events exposed by PriceDataSource.\n */\nexport type PriceDataSourceEvents = PriceDataSourceAssetsUpdatedEvent;\n\n// Action to report assets updated to AssetsController\ntype AssetsControllerAssetsUpdateAction = {\n type: 'AssetsController:assetsUpdate';\n handler: (response: DataResponse, sourceId: string) => Promise<void>;\n};\n\n/**\n * External actions that PriceDataSource needs to call.\n * Note: Uses ApiPlatformClient directly, so no BackendApiClient actions needed.\n */\nexport type PriceDataSourceAllowedActions =\n | GetAssetsBalanceStateAction\n | AssetsControllerAssetsUpdateAction;\n\nexport type PriceDataSourceMessenger = Messenger<\n typeof CONTROLLER_NAME,\n PriceDataSourceAllowedActions | PriceDataSourceActions,\n PriceDataSourceEvents\n>;\n\n// ============================================================================\n// OPTIONS\n// ============================================================================\n\nexport type PriceDataSourceOptions = {\n messenger: PriceDataSourceMessenger;\n /** ApiPlatformClient for API calls with caching */\n queryApiClient: ApiPlatformClient;\n /** Currency to fetch prices in (default: 'usd') */\n currency?: SupportedCurrency;\n /** Polling interval in ms (default: 60000) */\n pollInterval?: number;\n};\n\n// ============================================================================\n// HELPER FUNCTIONS\n// ============================================================================\n\n/**\n * Asset reference patterns that should NOT be sent to the Price API.\n * These are internal resource tracking values without market prices.\n */\nconst NON_PRICEABLE_ASSET_PATTERNS = [\n // Tron resource assets (bandwidth, energy, staking states)\n /\\/slip44:\\d+-staked-for-/u,\n /\\/slip44:bandwidth$/u,\n /\\/slip44:energy$/u,\n /\\/slip44:maximum-bandwidth$/u,\n /\\/slip44:maximum-energy$/u,\n];\n\n/**\n * Check if an asset ID represents a priceable asset.\n * Filters out internal resource tracking values that don't have market prices.\n *\n * @param assetId - The CAIP-19 asset ID to check.\n * @returns True if the asset has market price data.\n */\nfunction isPriceableAsset(assetId: Caip19AssetId): boolean {\n return !NON_PRICEABLE_ASSET_PATTERNS.some((pattern) => pattern.test(assetId));\n}\n\n/** Market data item from spot prices response (same as FungibleAssetPrice without lastUpdated) */\ntype SpotPriceMarketData = Omit<FungibleAssetPrice, 'lastUpdated'>;\n\n/**\n * Type guard to check if market data has a valid price\n *\n * @param data - The data to check.\n * @returns True if data is valid SpotPriceMarketData.\n */\nfunction isValidMarketData(data: unknown): data is SpotPriceMarketData {\n return (\n typeof data === 'object' &&\n data !== null &&\n 'price' in data &&\n typeof (data as SpotPriceMarketData).price === 'number'\n );\n}\n\n// ============================================================================\n// PRICE DATA SOURCE\n// ============================================================================\n\n/**\n * PriceDataSource fetches asset prices from the Price API.\n *\n * This data source:\n * - Fetches prices from Price API v3 spot-prices endpoint\n * - Supports one-time fetch and subscription-based polling\n * - In subscribe mode, automatically fetches prices for all assets in assetsBalance state\n * - Publishes price updates via messenger events\n *\n * Usage:\n * ```typescript\n * // Create and initialize (registers messenger actions)\n * const priceDataSource = new PriceDataSource({ messenger });\n *\n * // One-time fetch for specific assets\n * const response = await messenger.call('PriceDataSource:fetch', {\n * customAssets: ['eip155:1/erc20:0x...'],\n * });\n *\n * // Subscribe to price updates (polls all assets in balance state)\n * await messenger.call('PriceDataSource:subscribe', { request, subscriptionId });\n *\n * // Listen for updates\n * messenger.subscribe('PriceDataSource:assetsUpdated', (response) => {\n * // Handle price updates\n * });\n * ```\n */\nexport class PriceDataSource {\n readonly name = CONTROLLER_NAME;\n\n readonly #messenger: PriceDataSourceMessenger;\n\n readonly #currency: SupportedCurrency;\n\n readonly #pollInterval: number;\n\n /** ApiPlatformClient for cached API calls */\n readonly #apiClient: ApiPlatformClient;\n\n /** Active subscriptions by ID */\n readonly #activeSubscriptions: Map<\n string,\n { cleanup: () => void; request: DataRequest }\n > = new Map();\n\n constructor(options: PriceDataSourceOptions) {\n this.#messenger = options.messenger;\n this.#currency = options.currency ?? 'usd';\n this.#pollInterval = options.pollInterval ?? DEFAULT_POLL_INTERVAL;\n this.#apiClient = options.queryApiClient;\n\n this.#registerActionHandlers();\n }\n\n #registerActionHandlers(): void {\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n const messenger = this.#messenger as any;\n\n messenger.registerActionHandler(\n 'PriceDataSource:getAssetsMiddleware',\n () => this.assetsMiddleware,\n );\n\n messenger.registerActionHandler(\n 'PriceDataSource:fetch',\n (request: DataRequest) => this.fetch(request),\n );\n\n messenger.registerActionHandler(\n 'PriceDataSource:subscribe',\n (subscriptionRequest: SubscriptionRequest) =>\n this.subscribe(subscriptionRequest),\n );\n\n messenger.registerActionHandler(\n 'PriceDataSource:unsubscribe',\n (subscriptionId: string) => this.unsubscribe(subscriptionId),\n );\n }\n\n // ============================================================================\n // MIDDLEWARE\n // ============================================================================\n\n /**\n * Get the middleware for enriching responses with price data.\n *\n * This middleware:\n * 1. Extracts the response from context\n * 2. Fetches prices for detected assets (assets without metadata)\n * 3. Enriches the response with fetched prices\n * 4. Calls next() at the end to continue the middleware chain\n *\n * Note: This middleware ONLY fetches prices for detected assets.\n * For fetching prices for all assets, use the subscription mechanism\n * which polls prices for all assets in the balance state.\n *\n * @returns The middleware function for the assets pipeline.\n */\n get assetsMiddleware(): Middleware {\n return forDataTypes(['price'], async (ctx, next) => {\n // Extract response from context\n const { response } = ctx;\n\n // Only fetch prices for detected assets (assets without metadata)\n // The subscription handles fetching prices for all existing assets\n if (!response.detectedAssets) {\n return next(ctx);\n }\n\n const detectedAssetIds = new Set<Caip19AssetId>();\n for (const detectedIds of Object.values(response.detectedAssets)) {\n for (const assetId of detectedIds) {\n detectedAssetIds.add(assetId);\n }\n }\n\n if (detectedAssetIds.size === 0) {\n return next(ctx);\n }\n\n // Filter to only priceable assets\n const priceableAssetIds = [...detectedAssetIds].filter(isPriceableAsset);\n\n if (priceableAssetIds.length === 0) {\n return next(ctx);\n }\n\n try {\n const priceResponse = await this.#fetchSpotPrices(priceableAssetIds);\n\n response.assetsPrice ??= {};\n\n for (const [assetId, marketData] of Object.entries(priceResponse)) {\n if (!isValidMarketData(marketData)) {\n continue;\n }\n\n const caipAssetId = assetId as Caip19AssetId;\n response.assetsPrice[caipAssetId] = {\n ...marketData,\n lastUpdated: Date.now(),\n };\n }\n } catch (error) {\n log('Failed to fetch prices via middleware', { error });\n }\n\n // Call next() at the end to continue the middleware chain\n return next(ctx);\n });\n }\n\n // ============================================================================\n // HELPERS\n // ============================================================================\n\n /**\n * Fetch spot prices with caching and deduplication via query service.\n *\n * @param assetIds - Array of CAIP-19 asset IDs\n * @returns Spot prices response\n */\n async #fetchSpotPrices(assetIds: string[]): Promise<V3SpotPricesResponse> {\n return this.#apiClient.prices.fetchV3SpotPrices(assetIds, {\n currency: this.#currency,\n includeMarketData: true,\n });\n }\n\n /**\n * Get unique asset IDs from the assetsBalance state.\n * Filters by accounts and chains from the request.\n *\n * @param request - Data request with accounts and chainIds filters.\n * @returns Array of CAIP-19 asset IDs from balance state.\n */\n #getAssetIdsFromBalanceState(request: DataRequest): Caip19AssetId[] {\n try {\n const state = this.#messenger.call('AssetsController:getState');\n const assetIds = new Set<Caip19AssetId>();\n\n const accountIds = request.accounts.map((a) => a.id);\n const accountFilter =\n accountIds.length > 0 ? new Set(accountIds) : undefined;\n const chainFilter =\n request.chainIds.length > 0 ? new Set(request.chainIds) : undefined;\n\n if (state?.assetsBalance) {\n for (const [accountId, accountBalances] of Object.entries(\n state.assetsBalance,\n )) {\n // Filter by account if specified\n if (accountFilter && !accountFilter.has(accountId)) {\n continue;\n }\n\n for (const assetId of Object.keys(\n accountBalances as Record<string, unknown>,\n )) {\n // Filter by chain if specified\n if (chainFilter) {\n const chainId = assetId.split('/')[0] as ChainId;\n if (!chainFilter.has(chainId)) {\n continue;\n }\n }\n assetIds.add(assetId as Caip19AssetId);\n }\n }\n }\n\n return [...assetIds];\n } catch (error) {\n log('Failed to get asset IDs from balance state', { error });\n return [];\n }\n }\n\n // ============================================================================\n // FETCH\n // ============================================================================\n\n /**\n * Fetch prices for assets held by the accounts and chains in the request.\n * Gets asset IDs from balance state, filtered by request.accounts and request.chainIds.\n *\n * @param request - The data request specifying accounts and chains.\n * @returns DataResponse containing asset prices.\n */\n async fetch(request: DataRequest): Promise<DataResponse> {\n const response: DataResponse = {};\n\n // Get asset IDs from balance state, filtered by accounts and chains\n const rawAssetIds = this.#getAssetIdsFromBalanceState(request);\n\n // Filter out non-priceable assets (e.g., Tron bandwidth/energy resources)\n const assetIds = rawAssetIds.filter(isPriceableAsset);\n\n if (assetIds.length === 0) {\n return response;\n }\n\n try {\n const priceResponse = await this.#fetchSpotPrices([...assetIds]);\n\n response.assetsPrice = {};\n\n for (const [assetId, marketData] of Object.entries(priceResponse)) {\n // Skip assets with invalid market data (API doesn't have price for this asset)\n if (!isValidMarketData(marketData)) {\n continue;\n }\n\n const caipAssetId = assetId as Caip19AssetId;\n response.assetsPrice[caipAssetId] = {\n ...marketData,\n lastUpdated: Date.now(),\n };\n }\n } catch (error) {\n log('Failed to fetch prices', { error });\n }\n\n return response;\n }\n\n // ============================================================================\n // SUBSCRIBE\n // ============================================================================\n\n /**\n * Subscribe to price updates.\n * Sets up polling that fetches prices for all assets in assetsBalance state.\n *\n * @param subscriptionRequest - The subscription request configuration.\n */\n async subscribe(subscriptionRequest: SubscriptionRequest): Promise<void> {\n const { request, subscriptionId, isUpdate } = subscriptionRequest;\n\n // Handle subscription update - just update the request\n if (isUpdate) {\n const existing = this.#activeSubscriptions.get(subscriptionId);\n if (existing) {\n existing.request = request;\n return;\n }\n }\n\n // Clean up existing subscription\n await this.unsubscribe(subscriptionId);\n\n const pollInterval = request.updateInterval ?? this.#pollInterval;\n\n // Create poll function - fetches prices for all assets in balance state\n const pollFn = async (): Promise<void> => {\n try {\n const subscription = this.#activeSubscriptions.get(subscriptionId);\n if (!subscription) {\n return;\n }\n\n // Fetch prices for all assets currently in balance state\n const fetchResponse = await this.fetch(subscription.request);\n\n // Only report if we got prices\n if (\n fetchResponse.assetsPrice &&\n Object.keys(fetchResponse.assetsPrice).length > 0\n ) {\n await this.#messenger.call(\n 'AssetsController:assetsUpdate',\n fetchResponse,\n CONTROLLER_NAME,\n );\n }\n } catch (error) {\n log('Subscription poll failed', { subscriptionId, error });\n }\n };\n\n // Set up polling\n const timer = setInterval(() => {\n pollFn().catch(console.error);\n }, pollInterval);\n\n // Store subscription\n this.#activeSubscriptions.set(subscriptionId, {\n cleanup: () => {\n clearInterval(timer);\n },\n request,\n });\n\n // Initial fetch\n await pollFn();\n }\n\n /**\n * Unsubscribe from price updates.\n *\n * @param subscriptionId - The ID of the subscription to cancel.\n */\n async unsubscribe(subscriptionId: string): Promise<void> {\n const subscription = this.#activeSubscriptions.get(subscriptionId);\n if (subscription) {\n subscription.cleanup();\n this.#activeSubscriptions.delete(subscriptionId);\n }\n }\n\n /**\n * Destroy the data source and clean up all subscriptions.\n */\n destroy(): void {\n for (const subscription of this.#activeSubscriptions.values()) {\n subscription.cleanup();\n }\n this.#activeSubscriptions.clear();\n }\n}\n"]}
|
|
1
|
+
{"version":3,"file":"PriceDataSource.mjs","sourceRoot":"","sources":["../../src/data-sources/PriceDataSource.ts"],"names":[],"mappings":"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","sourcesContent":["import type {\n SupportedCurrency,\n V3SpotPricesResponse,\n} from '@metamask/core-backend';\nimport { ApiPlatformClient } from '@metamask/core-backend';\n\nimport type { SubscriptionRequest } from './AbstractDataSource';\nimport { projectLogger, createModuleLogger } from '../logger';\nimport { forDataTypes } from '../types';\nimport type {\n Caip19AssetId,\n ChainId,\n DataRequest,\n DataResponse,\n FungibleAssetPrice,\n Middleware,\n AssetsControllerStateInternal,\n} from '../types';\n\n// ============================================================================\n// CONSTANTS\n// ============================================================================\n\nconst CONTROLLER_NAME = 'PriceDataSource';\nconst DEFAULT_POLL_INTERVAL = 60_000; // 1 minute for price updates\n\nconst log = createModuleLogger(projectLogger, CONTROLLER_NAME);\n\n// ============================================================================\n// OPTIONS\n// ============================================================================\n\nexport type PriceDataSourceOptions = {\n /** ApiPlatformClient for API calls with caching */\n queryApiClient: ApiPlatformClient;\n /** Currency to fetch prices in (default: 'usd') */\n currency?: SupportedCurrency;\n /** Polling interval in ms (default: 60000) */\n pollInterval?: number;\n};\n\n// ============================================================================\n// HELPER FUNCTIONS\n// ============================================================================\n\n/**\n * Asset reference patterns that should NOT be sent to the Price API.\n * These are internal resource tracking values without market prices.\n */\nconst NON_PRICEABLE_ASSET_PATTERNS = [\n // Tron resource assets (bandwidth, energy, staking states)\n /\\/slip44:\\d+-staked-for-/u,\n /\\/slip44:bandwidth$/u,\n /\\/slip44:energy$/u,\n /\\/slip44:maximum-bandwidth$/u,\n /\\/slip44:maximum-energy$/u,\n];\n\n/**\n * Check if an asset ID represents a priceable asset.\n * Filters out internal resource tracking values that don't have market prices.\n *\n * @param assetId - The CAIP-19 asset ID to check.\n * @returns True if the asset has market price data.\n */\nfunction isPriceableAsset(assetId: Caip19AssetId): boolean {\n return !NON_PRICEABLE_ASSET_PATTERNS.some((pattern) => pattern.test(assetId));\n}\n\n/** Market data item from spot prices response (same as FungibleAssetPrice without lastUpdated) */\ntype SpotPriceMarketData = Omit<FungibleAssetPrice, 'lastUpdated'>;\n\n/**\n * Type guard to check if market data has a valid price\n *\n * @param data - The data to check.\n * @returns True if data is valid SpotPriceMarketData.\n */\nfunction isValidMarketData(data: unknown): data is SpotPriceMarketData {\n return (\n typeof data === 'object' &&\n data !== null &&\n 'price' in data &&\n typeof (data as SpotPriceMarketData).price === 'number'\n );\n}\n\n// ============================================================================\n// PRICE DATA SOURCE\n// ============================================================================\n\n/**\n * PriceDataSource fetches asset prices from the Price API.\n *\n * This data source:\n * - Fetches prices from Price API v3 spot-prices endpoint\n * - Supports one-time fetch and subscription-based polling\n * - In subscribe mode, uses getAssetsState from SubscriptionRequest to read assetsBalance and fetch prices\n *\n * Usage: Create with queryApiClient; subscribe() requires getAssetsState in the request for balance-based pricing.\n */\nexport class PriceDataSource {\n readonly name = CONTROLLER_NAME;\n\n readonly #currency: SupportedCurrency;\n\n readonly #pollInterval: number;\n\n /** ApiPlatformClient for cached API calls */\n readonly #apiClient: ApiPlatformClient;\n\n /** Active subscriptions by ID */\n readonly #activeSubscriptions: Map<\n string,\n {\n cleanup: () => void;\n request: DataRequest;\n onAssetsUpdate: (response: DataResponse) => void | Promise<void>;\n getAssetsState?: () => AssetsControllerStateInternal;\n }\n > = new Map();\n\n constructor(options: PriceDataSourceOptions) {\n this.#currency = options.currency ?? 'usd';\n this.#pollInterval = options.pollInterval ?? DEFAULT_POLL_INTERVAL;\n this.#apiClient = options.queryApiClient;\n }\n\n // ============================================================================\n // MIDDLEWARE\n // ============================================================================\n\n /**\n * Get the middleware for enriching responses with price data.\n *\n * This middleware:\n * 1. Extracts the response from context\n * 2. Fetches prices for detected assets (assets without metadata)\n * 3. Enriches the response with fetched prices\n * 4. Calls next() at the end to continue the middleware chain\n *\n * Note: This middleware ONLY fetches prices for detected assets.\n * For fetching prices for all assets, use the subscription mechanism\n * which polls prices for all assets in the balance state.\n *\n * @returns The middleware function for the assets pipeline.\n */\n get assetsMiddleware(): Middleware {\n return forDataTypes(['price'], async (ctx, next) => {\n // Extract response from context\n const { response } = ctx;\n\n // Only fetch prices for detected assets (assets without metadata)\n // The subscription handles fetching prices for all existing assets\n if (!response.detectedAssets) {\n return next(ctx);\n }\n\n const detectedAssetIds = new Set<Caip19AssetId>();\n for (const detectedIds of Object.values(response.detectedAssets)) {\n for (const assetId of detectedIds) {\n detectedAssetIds.add(assetId);\n }\n }\n\n if (detectedAssetIds.size === 0) {\n return next(ctx);\n }\n\n // Filter to only priceable assets\n const priceableAssetIds = [...detectedAssetIds].filter(isPriceableAsset);\n\n if (priceableAssetIds.length === 0) {\n return next(ctx);\n }\n\n try {\n const priceResponse = await this.#fetchSpotPrices(priceableAssetIds);\n\n response.assetsPrice ??= {};\n\n for (const [assetId, marketData] of Object.entries(priceResponse)) {\n if (!isValidMarketData(marketData)) {\n continue;\n }\n\n const caipAssetId = assetId as Caip19AssetId;\n response.assetsPrice[caipAssetId] = {\n ...marketData,\n lastUpdated: Date.now(),\n };\n }\n } catch (error) {\n log('Failed to fetch prices via middleware', { error });\n }\n\n // Call next() at the end to continue the middleware chain\n return next(ctx);\n });\n }\n\n // ============================================================================\n // HELPERS\n // ============================================================================\n\n /**\n * Fetch spot prices with caching and deduplication via query service.\n *\n * @param assetIds - Array of CAIP-19 asset IDs\n * @returns Spot prices response\n */\n async #fetchSpotPrices(assetIds: string[]): Promise<V3SpotPricesResponse> {\n return this.#apiClient.prices.fetchV3SpotPrices(assetIds, {\n currency: this.#currency,\n includeMarketData: true,\n });\n }\n\n /**\n * Get unique asset IDs from the assetsBalance state.\n * Filters by accounts and chains from the request.\n *\n * @param request - Data request with accounts and chainIds filters.\n * @param getAssetsState - State access; when omitted, returns [].\n * @returns Array of CAIP-19 asset IDs from balance state.\n */\n #getAssetIdsFromBalanceState(\n request: DataRequest,\n getAssetsState?: () => AssetsControllerStateInternal,\n ): Caip19AssetId[] {\n if (!getAssetsState) {\n return [];\n }\n try {\n const state = getAssetsState();\n const assetIds = new Set<Caip19AssetId>();\n\n const accountIds = request.accountsWithSupportedChains.map(\n (a) => a.account.id,\n );\n const accountFilter =\n accountIds.length > 0 ? new Set(accountIds) : undefined;\n const chainFilter =\n request.chainIds.length > 0 ? new Set(request.chainIds) : undefined;\n\n if (state?.assetsBalance) {\n for (const [accountId, accountBalances] of Object.entries(\n state.assetsBalance,\n )) {\n // Filter by account if specified\n if (accountFilter && !accountFilter.has(accountId)) {\n continue;\n }\n\n for (const assetId of Object.keys(\n accountBalances as Record<string, unknown>,\n )) {\n // Filter by chain if specified\n if (chainFilter) {\n const chainId = assetId.split('/')[0] as ChainId;\n if (!chainFilter.has(chainId)) {\n continue;\n }\n }\n assetIds.add(assetId as Caip19AssetId);\n }\n }\n }\n\n return [...assetIds];\n } catch (error) {\n log('Failed to get asset IDs from balance state', { error });\n return [];\n }\n }\n\n // ============================================================================\n // FETCH\n // ============================================================================\n\n /**\n * Fetch prices for assets held by the accounts and chains in the request.\n * When getAssetsState is provided, gets asset IDs from balance state; otherwise returns empty.\n *\n * @param request - The data request specifying accounts and chains.\n * @param getAssetsState - Optional state access (e.g. from SubscriptionRequest).\n * @returns DataResponse containing asset prices.\n */\n async fetch(\n request: DataRequest,\n getAssetsState?: () => AssetsControllerStateInternal,\n ): Promise<DataResponse> {\n const response: DataResponse = {};\n\n // Get asset IDs from balance state when state access is provided\n const rawAssetIds = this.#getAssetIdsFromBalanceState(\n request,\n getAssetsState,\n );\n\n // Filter out non-priceable assets (e.g., Tron bandwidth/energy resources)\n const assetIds = rawAssetIds.filter(isPriceableAsset);\n\n if (assetIds.length === 0) {\n return response;\n }\n\n try {\n const priceResponse = await this.#fetchSpotPrices([...assetIds]);\n\n response.assetsPrice = {};\n\n for (const [assetId, marketData] of Object.entries(priceResponse)) {\n // Skip assets with invalid market data (API doesn't have price for this asset)\n if (!isValidMarketData(marketData)) {\n continue;\n }\n\n const caipAssetId = assetId as Caip19AssetId;\n response.assetsPrice[caipAssetId] = {\n ...marketData,\n lastUpdated: Date.now(),\n };\n }\n } catch (error) {\n log('Failed to fetch prices', { error });\n }\n\n return response;\n }\n\n // ============================================================================\n // SUBSCRIBE\n // ============================================================================\n\n /**\n * Subscribe to price updates.\n * Sets up polling that fetches prices for all assets in assetsBalance state.\n *\n * @param subscriptionRequest - The subscription request configuration.\n */\n async subscribe(subscriptionRequest: SubscriptionRequest): Promise<void> {\n const { request, subscriptionId, isUpdate } = subscriptionRequest;\n\n // Handle subscription update - just update the request\n if (isUpdate) {\n const existing = this.#activeSubscriptions.get(subscriptionId);\n if (existing) {\n existing.request = request;\n return;\n }\n }\n\n // Clean up existing subscription\n await this.unsubscribe(subscriptionId);\n\n const pollInterval = request.updateInterval ?? this.#pollInterval;\n\n // Create poll function - fetches prices using getAssetsState from subscription\n const pollFn = async (): Promise<void> => {\n try {\n const subscription = this.#activeSubscriptions.get(subscriptionId);\n if (!subscription) {\n return;\n }\n\n // Fetch prices for all assets in balance state (uses subscription's getAssetsState)\n const fetchResponse = await this.fetch(\n subscription.request,\n subscription.getAssetsState,\n );\n\n // Only report if we got prices\n if (\n fetchResponse.assetsPrice &&\n Object.keys(fetchResponse.assetsPrice).length > 0\n ) {\n await subscription.onAssetsUpdate(fetchResponse);\n }\n } catch (error) {\n log('Subscription poll failed', { subscriptionId, error });\n }\n };\n\n // Set up polling\n const timer = setInterval(() => {\n pollFn().catch(console.error);\n }, pollInterval);\n\n // Store subscription (getAssetsState from request for balance-based pricing)\n this.#activeSubscriptions.set(subscriptionId, {\n cleanup: () => {\n clearInterval(timer);\n },\n request,\n onAssetsUpdate: subscriptionRequest.onAssetsUpdate,\n getAssetsState: subscriptionRequest.getAssetsState,\n });\n\n // Initial fetch\n await pollFn();\n }\n\n /**\n * Unsubscribe from price updates.\n *\n * @param subscriptionId - The ID of the subscription to cancel.\n */\n async unsubscribe(subscriptionId: string): Promise<void> {\n const subscription = this.#activeSubscriptions.get(subscriptionId);\n if (subscription) {\n subscription.cleanup();\n this.#activeSubscriptions.delete(subscriptionId);\n }\n }\n\n /**\n * Destroy the data source and clean up all subscriptions.\n */\n destroy(): void {\n for (const subscription of this.#activeSubscriptions.values()) {\n subscription.cleanup();\n }\n this.#activeSubscriptions.clear();\n }\n}\n"]}
|
|
@@ -13,14 +13,14 @@ var __classPrivateFieldGet = (this && this.__classPrivateFieldGet) || function (
|
|
|
13
13
|
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
14
14
|
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
15
15
|
};
|
|
16
|
-
var _RpcDataSource_instances, _RpcDataSource_timeout, _RpcDataSource_tokenDetectionEnabled, _RpcDataSource_activeChains, _RpcDataSource_chainStatuses, _RpcDataSource_providerCache, _RpcDataSource_activeSubscriptions, _RpcDataSource_multicallClient, _RpcDataSource_balanceFetcher, _RpcDataSource_tokenDetector, _RpcDataSource_convertToHumanReadable, _RpcDataSource_collectMetadataForBalances, _RpcDataSource_handleBalanceUpdate, _RpcDataSource_handleDetectionUpdate,
|
|
16
|
+
var _RpcDataSource_instances, _RpcDataSource_messenger, _RpcDataSource_onActiveChainsUpdated, _RpcDataSource_timeout, _RpcDataSource_tokenDetectionEnabled, _RpcDataSource_activeChains, _RpcDataSource_chainStatuses, _RpcDataSource_providerCache, _RpcDataSource_activeSubscriptions, _RpcDataSource_multicallClient, _RpcDataSource_balanceFetcher, _RpcDataSource_tokenDetector, _RpcDataSource_convertToHumanReadable, _RpcDataSource_collectMetadataForBalances, _RpcDataSource_handleBalanceUpdate, _RpcDataSource_handleDetectionUpdate, _RpcDataSource_subscribeToNetworkController, _RpcDataSource_initializeFromNetworkController, _RpcDataSource_updateFromNetworkState, _RpcDataSource_getProvider, _RpcDataSource_getMulticallProvider, _RpcDataSource_clearProviderCache, _RpcDataSource_buildNativeAssetId, _RpcDataSource_getExistingAssetsMetadata, _RpcDataSource_getTokenMetadataFromTokenList;
|
|
17
17
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
18
18
|
exports.createRpcDataSource = exports.RpcDataSource = exports.caipChainIdToHex = void 0;
|
|
19
19
|
const providers_1 = require("@ethersproject/providers");
|
|
20
|
-
const base_controller_1 = require("@metamask/base-controller");
|
|
21
20
|
const controller_utils_1 = require("@metamask/controller-utils");
|
|
22
21
|
const utils_1 = require("@metamask/utils");
|
|
23
22
|
const bignumber_js_1 = __importDefault(require("bignumber.js"));
|
|
23
|
+
const AbstractDataSource_1 = require("./AbstractDataSource.cjs");
|
|
24
24
|
const evm_rpc_services_1 = require("./evm-rpc-services/index.cjs");
|
|
25
25
|
const logger_1 = require("../logger.cjs");
|
|
26
26
|
const CONTROLLER_NAME = 'RpcDataSource';
|
|
@@ -61,15 +61,12 @@ exports.caipChainIdToHex = caipChainIdToHex;
|
|
|
61
61
|
* - RpcDataSource:activeChainsUpdated
|
|
62
62
|
* - RpcDataSource:assetsUpdated
|
|
63
63
|
*/
|
|
64
|
-
class RpcDataSource extends
|
|
64
|
+
class RpcDataSource extends AbstractDataSource_1.AbstractDataSource {
|
|
65
65
|
constructor(options) {
|
|
66
|
-
super({
|
|
67
|
-
name: CONTROLLER_NAME,
|
|
68
|
-
metadata: {},
|
|
69
|
-
state: {},
|
|
70
|
-
messenger: options.messenger,
|
|
71
|
-
});
|
|
66
|
+
super(CONTROLLER_NAME, { activeChains: [] });
|
|
72
67
|
_RpcDataSource_instances.add(this);
|
|
68
|
+
_RpcDataSource_messenger.set(this, void 0);
|
|
69
|
+
_RpcDataSource_onActiveChainsUpdated.set(this, void 0);
|
|
73
70
|
_RpcDataSource_timeout.set(this, void 0);
|
|
74
71
|
_RpcDataSource_tokenDetectionEnabled.set(this, void 0);
|
|
75
72
|
/** Currently active chains */
|
|
@@ -84,6 +81,8 @@ class RpcDataSource extends base_controller_1.BaseController {
|
|
|
84
81
|
_RpcDataSource_multicallClient.set(this, void 0);
|
|
85
82
|
_RpcDataSource_balanceFetcher.set(this, void 0);
|
|
86
83
|
_RpcDataSource_tokenDetector.set(this, void 0);
|
|
84
|
+
__classPrivateFieldSet(this, _RpcDataSource_messenger, options.messenger, "f");
|
|
85
|
+
__classPrivateFieldSet(this, _RpcDataSource_onActiveChainsUpdated, options.onActiveChainsUpdated, "f");
|
|
87
86
|
__classPrivateFieldSet(this, _RpcDataSource_timeout, options.timeout ?? 10000, "f");
|
|
88
87
|
__classPrivateFieldSet(this, _RpcDataSource_tokenDetectionEnabled, options.tokenDetectionEnabled ?? false, "f");
|
|
89
88
|
const balanceInterval = options.balanceInterval ?? DEFAULT_BALANCE_INTERVAL;
|
|
@@ -101,7 +100,7 @@ class RpcDataSource extends base_controller_1.BaseController {
|
|
|
101
100
|
// Create messenger adapters for BalanceFetcher and TokenDetector
|
|
102
101
|
const balanceFetcherMessenger = {
|
|
103
102
|
call: (_action) => {
|
|
104
|
-
const state = this.
|
|
103
|
+
const state = __classPrivateFieldGet(this, _RpcDataSource_messenger, "f").call('AssetsController:getState');
|
|
105
104
|
return {
|
|
106
105
|
assetsBalance: (state.assetsBalance ?? {}),
|
|
107
106
|
};
|
|
@@ -109,7 +108,7 @@ class RpcDataSource extends base_controller_1.BaseController {
|
|
|
109
108
|
};
|
|
110
109
|
const tokenDetectorMessenger = {
|
|
111
110
|
call: (_action) => {
|
|
112
|
-
return this.
|
|
111
|
+
return __classPrivateFieldGet(this, _RpcDataSource_messenger, "f").call('TokenListController:getState');
|
|
113
112
|
},
|
|
114
113
|
};
|
|
115
114
|
// Initialize BalanceFetcher with polling interval
|
|
@@ -118,7 +117,6 @@ class RpcDataSource extends base_controller_1.BaseController {
|
|
|
118
117
|
// Initialize TokenDetector with polling interval
|
|
119
118
|
__classPrivateFieldSet(this, _RpcDataSource_tokenDetector, new evm_rpc_services_1.TokenDetector(__classPrivateFieldGet(this, _RpcDataSource_multicallClient, "f"), tokenDetectorMessenger, { pollingInterval: detectionInterval }), "f");
|
|
120
119
|
__classPrivateFieldGet(this, _RpcDataSource_tokenDetector, "f").setOnDetectionUpdate(__classPrivateFieldGet(this, _RpcDataSource_instances, "m", _RpcDataSource_handleDetectionUpdate).bind(this));
|
|
121
|
-
__classPrivateFieldGet(this, _RpcDataSource_instances, "m", _RpcDataSource_registerActionHandlers).call(this);
|
|
122
120
|
__classPrivateFieldGet(this, _RpcDataSource_instances, "m", _RpcDataSource_subscribeToNetworkController).call(this);
|
|
123
121
|
__classPrivateFieldGet(this, _RpcDataSource_instances, "m", _RpcDataSource_initializeFromNetworkController).call(this);
|
|
124
122
|
}
|
|
@@ -127,17 +125,6 @@ class RpcDataSource extends base_controller_1.BaseController {
|
|
|
127
125
|
*
|
|
128
126
|
* @returns The name of this data source.
|
|
129
127
|
*/
|
|
130
|
-
getName() {
|
|
131
|
-
return this.name;
|
|
132
|
-
}
|
|
133
|
-
/**
|
|
134
|
-
* Get currently active chains.
|
|
135
|
-
*
|
|
136
|
-
* @returns Promise resolving to array of active chain IDs.
|
|
137
|
-
*/
|
|
138
|
-
async getActiveChains() {
|
|
139
|
-
return __classPrivateFieldGet(this, _RpcDataSource_activeChains, "f");
|
|
140
|
-
}
|
|
141
128
|
/**
|
|
142
129
|
* Get the status of all configured chains.
|
|
143
130
|
*
|
|
@@ -193,7 +180,7 @@ class RpcDataSource extends base_controller_1.BaseController {
|
|
|
193
180
|
const response = {};
|
|
194
181
|
const chainsToFetch = request.chainIds.filter((chainId) => __classPrivateFieldGet(this, _RpcDataSource_activeChains, "f").includes(chainId));
|
|
195
182
|
log('Fetch requested', {
|
|
196
|
-
accounts: request.
|
|
183
|
+
accounts: request.accountsWithSupportedChains.map((a) => a.account.id),
|
|
197
184
|
requestedChains: request.chainIds,
|
|
198
185
|
chainsToFetch,
|
|
199
186
|
});
|
|
@@ -204,14 +191,15 @@ class RpcDataSource extends base_controller_1.BaseController {
|
|
|
204
191
|
const assetsBalance = {};
|
|
205
192
|
const assetsMetadata = {};
|
|
206
193
|
const failedChains = [];
|
|
207
|
-
// Fetch balances for each
|
|
208
|
-
for (const
|
|
209
|
-
const
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
194
|
+
// Fetch balances for each account and its supported chains (pre-computed in request)
|
|
195
|
+
for (const { account, supportedChains, } of request.accountsWithSupportedChains) {
|
|
196
|
+
const chainsForAccount = chainsToFetch.filter((chain) => supportedChains.includes(chain));
|
|
197
|
+
if (chainsForAccount.length === 0) {
|
|
198
|
+
continue;
|
|
199
|
+
}
|
|
200
|
+
const { address, id: accountId } = account;
|
|
201
|
+
for (const chainId of chainsForAccount) {
|
|
202
|
+
const hexChainId = (0, exports.caipChainIdToHex)(chainId);
|
|
215
203
|
try {
|
|
216
204
|
// Use BalanceFetcher for batched balance fetching
|
|
217
205
|
const result = await __classPrivateFieldGet(this, _RpcDataSource_balanceFetcher, "f").fetchBalancesForTokens(hexChainId, accountId, address, [], // Empty array means just native token
|
|
@@ -359,7 +347,7 @@ class RpcDataSource extends base_controller_1.BaseController {
|
|
|
359
347
|
let successfullyHandledChains = [];
|
|
360
348
|
log('Middleware fetching', {
|
|
361
349
|
chains: supportedChains,
|
|
362
|
-
accounts: request.
|
|
350
|
+
accounts: request.accountsWithSupportedChains.map((a) => a.account.id),
|
|
363
351
|
});
|
|
364
352
|
const response = await this.fetch({
|
|
365
353
|
...request,
|
|
@@ -409,7 +397,7 @@ class RpcDataSource extends base_controller_1.BaseController {
|
|
|
409
397
|
log('Subscribe requested', {
|
|
410
398
|
subscriptionId,
|
|
411
399
|
isUpdate,
|
|
412
|
-
accounts: request.
|
|
400
|
+
accounts: request.accountsWithSupportedChains.map((a) => a.account.id),
|
|
413
401
|
chainsToSubscribe,
|
|
414
402
|
});
|
|
415
403
|
if (chainsToSubscribe.length === 0) {
|
|
@@ -430,16 +418,17 @@ class RpcDataSource extends base_controller_1.BaseController {
|
|
|
430
418
|
}
|
|
431
419
|
// Clean up existing subscription (stops old polling)
|
|
432
420
|
await this.unsubscribe(subscriptionId);
|
|
433
|
-
// Start polling through BalanceFetcher and TokenDetector
|
|
421
|
+
// Start polling through BalanceFetcher and TokenDetector (use pre-computed supportedChains per account)
|
|
434
422
|
const balancePollingTokens = [];
|
|
435
423
|
const detectionPollingTokens = [];
|
|
436
|
-
for (const
|
|
437
|
-
const
|
|
438
|
-
|
|
439
|
-
|
|
440
|
-
|
|
441
|
-
|
|
442
|
-
|
|
424
|
+
for (const { account, supportedChains, } of request.accountsWithSupportedChains) {
|
|
425
|
+
const chainsForAccount = chainsToSubscribe.filter((chain) => supportedChains.includes(chain));
|
|
426
|
+
if (chainsForAccount.length === 0) {
|
|
427
|
+
continue;
|
|
428
|
+
}
|
|
429
|
+
const { address, id: accountId } = account;
|
|
430
|
+
for (const chainId of chainsForAccount) {
|
|
431
|
+
const hexChainId = (0, exports.caipChainIdToHex)(chainId);
|
|
443
432
|
// Start balance polling
|
|
444
433
|
const balanceInput = {
|
|
445
434
|
chainId: hexChainId,
|
|
@@ -461,11 +450,13 @@ class RpcDataSource extends base_controller_1.BaseController {
|
|
|
461
450
|
}
|
|
462
451
|
}
|
|
463
452
|
// Store subscription data
|
|
453
|
+
const accounts = request.accountsWithSupportedChains.map((entry) => entry.account);
|
|
464
454
|
__classPrivateFieldGet(this, _RpcDataSource_activeSubscriptions, "f").set(subscriptionId, {
|
|
465
455
|
balancePollingTokens,
|
|
466
456
|
detectionPollingTokens,
|
|
467
457
|
chains: chainsToSubscribe,
|
|
468
|
-
accounts
|
|
458
|
+
accounts,
|
|
459
|
+
onAssetsUpdate: subscriptionRequest.onAssetsUpdate,
|
|
469
460
|
});
|
|
470
461
|
log('Subscription SUCCESS', {
|
|
471
462
|
subscriptionId,
|
|
@@ -509,7 +500,7 @@ class RpcDataSource extends base_controller_1.BaseController {
|
|
|
509
500
|
}
|
|
510
501
|
}
|
|
511
502
|
exports.RpcDataSource = RpcDataSource;
|
|
512
|
-
_RpcDataSource_timeout = new WeakMap(), _RpcDataSource_tokenDetectionEnabled = new WeakMap(), _RpcDataSource_activeChains = new WeakMap(), _RpcDataSource_chainStatuses = new WeakMap(), _RpcDataSource_providerCache = new WeakMap(), _RpcDataSource_activeSubscriptions = new WeakMap(), _RpcDataSource_multicallClient = new WeakMap(), _RpcDataSource_balanceFetcher = new WeakMap(), _RpcDataSource_tokenDetector = new WeakMap(), _RpcDataSource_instances = new WeakSet(), _RpcDataSource_convertToHumanReadable = function _RpcDataSource_convertToHumanReadable(rawBalance, decimals) {
|
|
503
|
+
_RpcDataSource_messenger = new WeakMap(), _RpcDataSource_onActiveChainsUpdated = new WeakMap(), _RpcDataSource_timeout = new WeakMap(), _RpcDataSource_tokenDetectionEnabled = new WeakMap(), _RpcDataSource_activeChains = new WeakMap(), _RpcDataSource_chainStatuses = new WeakMap(), _RpcDataSource_providerCache = new WeakMap(), _RpcDataSource_activeSubscriptions = new WeakMap(), _RpcDataSource_multicallClient = new WeakMap(), _RpcDataSource_balanceFetcher = new WeakMap(), _RpcDataSource_tokenDetector = new WeakMap(), _RpcDataSource_instances = new WeakSet(), _RpcDataSource_convertToHumanReadable = function _RpcDataSource_convertToHumanReadable(rawBalance, decimals) {
|
|
513
504
|
const rawAmount = new bignumber_js_1.default(rawBalance);
|
|
514
505
|
const divisor = new bignumber_js_1.default(10).pow(decimals);
|
|
515
506
|
return rawAmount.dividedBy(divisor).toString();
|
|
@@ -585,11 +576,11 @@ _RpcDataSource_timeout = new WeakMap(), _RpcDataSource_tokenDetectionEnabled = n
|
|
|
585
576
|
accountId: result.accountId,
|
|
586
577
|
newBalanceCount: Object.keys(newBalances).length,
|
|
587
578
|
});
|
|
588
|
-
this.
|
|
589
|
-
.
|
|
590
|
-
|
|
591
|
-
|
|
592
|
-
}
|
|
579
|
+
for (const subscription of __classPrivateFieldGet(this, _RpcDataSource_activeSubscriptions, "f").values()) {
|
|
580
|
+
subscription.onAssetsUpdate(response)?.catch((error) => {
|
|
581
|
+
log('Failed to update assets', { error });
|
|
582
|
+
});
|
|
583
|
+
}
|
|
593
584
|
}, _RpcDataSource_handleDetectionUpdate = function _RpcDataSource_handleDetectionUpdate(result) {
|
|
594
585
|
log('Detected new tokens', {
|
|
595
586
|
count: result.detectedAssets.length,
|
|
@@ -635,24 +626,13 @@ _RpcDataSource_timeout = new WeakMap(), _RpcDataSource_tokenDetectionEnabled = n
|
|
|
635
626
|
[result.accountId]: newBalances,
|
|
636
627
|
},
|
|
637
628
|
};
|
|
638
|
-
this.
|
|
639
|
-
.
|
|
640
|
-
|
|
641
|
-
|
|
642
|
-
}
|
|
643
|
-
}, _RpcDataSource_registerActionHandlers = function _RpcDataSource_registerActionHandlers() {
|
|
644
|
-
const getAssetsMiddlewareHandler = () => this.assetsMiddleware;
|
|
645
|
-
const getActiveChainsHandler = async () => this.getActiveChains();
|
|
646
|
-
const fetchHandler = async (request) => this.fetch(request);
|
|
647
|
-
const subscribeHandler = async (request) => this.subscribe(request);
|
|
648
|
-
const unsubscribeHandler = async (subscriptionId) => this.unsubscribe(subscriptionId);
|
|
649
|
-
this.messenger.registerActionHandler('RpcDataSource:getAssetsMiddleware', getAssetsMiddlewareHandler);
|
|
650
|
-
this.messenger.registerActionHandler('RpcDataSource:getActiveChains', getActiveChainsHandler);
|
|
651
|
-
this.messenger.registerActionHandler('RpcDataSource:fetch', fetchHandler);
|
|
652
|
-
this.messenger.registerActionHandler('RpcDataSource:subscribe', subscribeHandler);
|
|
653
|
-
this.messenger.registerActionHandler('RpcDataSource:unsubscribe', unsubscribeHandler);
|
|
629
|
+
for (const subscription of __classPrivateFieldGet(this, _RpcDataSource_activeSubscriptions, "f").values()) {
|
|
630
|
+
subscription.onAssetsUpdate(response)?.catch((error) => {
|
|
631
|
+
log('Failed to update detected assets', { error });
|
|
632
|
+
});
|
|
633
|
+
}
|
|
654
634
|
}, _RpcDataSource_subscribeToNetworkController = function _RpcDataSource_subscribeToNetworkController() {
|
|
655
|
-
this.
|
|
635
|
+
__classPrivateFieldGet(this, _RpcDataSource_messenger, "f").subscribe('NetworkController:stateChange', (networkState) => {
|
|
656
636
|
log('NetworkController state changed');
|
|
657
637
|
__classPrivateFieldGet(this, _RpcDataSource_instances, "m", _RpcDataSource_clearProviderCache).call(this);
|
|
658
638
|
__classPrivateFieldGet(this, _RpcDataSource_instances, "m", _RpcDataSource_updateFromNetworkState).call(this, networkState);
|
|
@@ -660,7 +640,7 @@ _RpcDataSource_timeout = new WeakMap(), _RpcDataSource_tokenDetectionEnabled = n
|
|
|
660
640
|
}, _RpcDataSource_initializeFromNetworkController = function _RpcDataSource_initializeFromNetworkController() {
|
|
661
641
|
log('Initializing from NetworkController');
|
|
662
642
|
try {
|
|
663
|
-
const networkState = this.
|
|
643
|
+
const networkState = __classPrivateFieldGet(this, _RpcDataSource_messenger, "f").call('NetworkController:getState');
|
|
664
644
|
__classPrivateFieldGet(this, _RpcDataSource_instances, "m", _RpcDataSource_updateFromNetworkState).call(this, networkState);
|
|
665
645
|
}
|
|
666
646
|
catch (error) {
|
|
@@ -703,8 +683,9 @@ _RpcDataSource_timeout = new WeakMap(), _RpcDataSource_tokenDetectionEnabled = n
|
|
|
703
683
|
__classPrivateFieldSet(this, _RpcDataSource_chainStatuses, chainStatuses, "f");
|
|
704
684
|
__classPrivateFieldSet(this, _RpcDataSource_activeChains, activeChains, "f");
|
|
705
685
|
if (hasChanges) {
|
|
706
|
-
this.
|
|
686
|
+
__classPrivateFieldGet(this, _RpcDataSource_onActiveChainsUpdated, "f").call(this, activeChains);
|
|
707
687
|
}
|
|
688
|
+
this.state.activeChains = activeChains;
|
|
708
689
|
}, _RpcDataSource_getProvider = function _RpcDataSource_getProvider(chainId) {
|
|
709
690
|
const cached = __classPrivateFieldGet(this, _RpcDataSource_providerCache, "f").get(chainId);
|
|
710
691
|
if (cached) {
|
|
@@ -715,7 +696,10 @@ _RpcDataSource_timeout = new WeakMap(), _RpcDataSource_tokenDetectionEnabled = n
|
|
|
715
696
|
return undefined;
|
|
716
697
|
}
|
|
717
698
|
try {
|
|
718
|
-
const networkClient = this.
|
|
699
|
+
const networkClient = __classPrivateFieldGet(this, _RpcDataSource_messenger, "f").call('NetworkController:getNetworkClientById', chainStatus.networkClientId);
|
|
700
|
+
if (!networkClient?.provider) {
|
|
701
|
+
return undefined;
|
|
702
|
+
}
|
|
719
703
|
const web3Provider = new providers_1.Web3Provider(networkClient.provider);
|
|
720
704
|
__classPrivateFieldGet(this, _RpcDataSource_providerCache, "f").set(chainId, web3Provider);
|
|
721
705
|
return web3Provider;
|
|
@@ -745,38 +729,13 @@ _RpcDataSource_timeout = new WeakMap(), _RpcDataSource_tokenDetectionEnabled = n
|
|
|
745
729
|
};
|
|
746
730
|
}, _RpcDataSource_clearProviderCache = function _RpcDataSource_clearProviderCache() {
|
|
747
731
|
__classPrivateFieldGet(this, _RpcDataSource_providerCache, "f").clear();
|
|
748
|
-
}, _RpcDataSource_accountSupportsChain = function _RpcDataSource_accountSupportsChain(account, chainId) {
|
|
749
|
-
const scopes = account.scopes ?? [];
|
|
750
|
-
if (scopes.length === 0) {
|
|
751
|
-
return true;
|
|
752
|
-
}
|
|
753
|
-
const [chainNamespace, chainReference] = chainId.split(':');
|
|
754
|
-
for (const scope of scopes) {
|
|
755
|
-
const [scopeNamespace, scopeReference] = scope.split(':');
|
|
756
|
-
if (scopeNamespace !== chainNamespace) {
|
|
757
|
-
continue;
|
|
758
|
-
}
|
|
759
|
-
// Wildcard scope (e.g., eip155:0) matches all chains in the namespace
|
|
760
|
-
if (scopeReference === '0') {
|
|
761
|
-
return true;
|
|
762
|
-
}
|
|
763
|
-
// RpcDataSource only handles eip155 (EVM) chains
|
|
764
|
-
// Normalize hex chain references (e.g., 0x1 -> 1) for comparison
|
|
765
|
-
const normalizedScopeRef = scopeReference?.startsWith('0x')
|
|
766
|
-
? parseInt(scopeReference, 16).toString()
|
|
767
|
-
: scopeReference;
|
|
768
|
-
if (normalizedScopeRef === chainReference) {
|
|
769
|
-
return true;
|
|
770
|
-
}
|
|
771
|
-
}
|
|
772
|
-
return false;
|
|
773
732
|
}, _RpcDataSource_buildNativeAssetId = function _RpcDataSource_buildNativeAssetId(chainId) {
|
|
774
|
-
const { nativeAssetIdentifiers } = this.
|
|
733
|
+
const { nativeAssetIdentifiers } = __classPrivateFieldGet(this, _RpcDataSource_messenger, "f").call('NetworkEnablementController:getState');
|
|
775
734
|
return (nativeAssetIdentifiers[chainId] ??
|
|
776
735
|
`${chainId}/slip44:60`);
|
|
777
736
|
}, _RpcDataSource_getExistingAssetsMetadata = function _RpcDataSource_getExistingAssetsMetadata() {
|
|
778
737
|
try {
|
|
779
|
-
const state = this.
|
|
738
|
+
const state = __classPrivateFieldGet(this, _RpcDataSource_messenger, "f").call('AssetsController:getState');
|
|
780
739
|
return (state.assetsMetadata ?? {});
|
|
781
740
|
}
|
|
782
741
|
catch {
|
|
@@ -794,8 +753,8 @@ _RpcDataSource_timeout = new WeakMap(), _RpcDataSource_tokenDetectionEnabled = n
|
|
|
794
753
|
const tokenAddress = assetPart.slice(6); // Remove 'erc20:' prefix
|
|
795
754
|
const chainIdDecimal = chainPart.split(':')[1];
|
|
796
755
|
const hexChainId = `0x${parseInt(chainIdDecimal, 10).toString(16)}`;
|
|
797
|
-
const tokenListState = this.
|
|
798
|
-
const chainCacheEntry = tokenListState
|
|
756
|
+
const tokenListState = __classPrivateFieldGet(this, _RpcDataSource_messenger, "f").call('TokenListController:getState');
|
|
757
|
+
const chainCacheEntry = tokenListState?.tokensChainsCache?.[hexChainId];
|
|
799
758
|
const chainTokenList = chainCacheEntry?.data;
|
|
800
759
|
if (!chainTokenList) {
|
|
801
760
|
return undefined;
|