@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.
Files changed (116) hide show
  1. package/CHANGELOG.md +4 -0
  2. package/dist/AssetsController-method-action-types.cjs +7 -0
  3. package/dist/AssetsController-method-action-types.cjs.map +1 -0
  4. package/dist/AssetsController-method-action-types.d.cts +78 -0
  5. package/dist/AssetsController-method-action-types.d.cts.map +1 -0
  6. package/dist/AssetsController-method-action-types.d.mts +78 -0
  7. package/dist/AssetsController-method-action-types.d.mts.map +1 -0
  8. package/dist/AssetsController-method-action-types.mjs +6 -0
  9. package/dist/AssetsController-method-action-types.mjs.map +1 -0
  10. package/dist/AssetsController.cjs +161 -116
  11. package/dist/AssetsController.cjs.map +1 -1
  12. package/dist/AssetsController.d.cts +23 -80
  13. package/dist/AssetsController.d.cts.map +1 -1
  14. package/dist/AssetsController.d.mts +23 -80
  15. package/dist/AssetsController.d.mts.map +1 -1
  16. package/dist/AssetsController.mjs +161 -116
  17. package/dist/AssetsController.mjs.map +1 -1
  18. package/dist/data-sources/AbstractDataSource.cjs.map +1 -1
  19. package/dist/data-sources/AbstractDataSource.d.cts +10 -1
  20. package/dist/data-sources/AbstractDataSource.d.cts.map +1 -1
  21. package/dist/data-sources/AbstractDataSource.d.mts +10 -1
  22. package/dist/data-sources/AbstractDataSource.d.mts.map +1 -1
  23. package/dist/data-sources/AbstractDataSource.mjs.map +1 -1
  24. package/dist/data-sources/AccountsApiDataSource.cjs +23 -99
  25. package/dist/data-sources/AccountsApiDataSource.cjs.map +1 -1
  26. package/dist/data-sources/AccountsApiDataSource.d.cts +5 -67
  27. package/dist/data-sources/AccountsApiDataSource.d.cts.map +1 -1
  28. package/dist/data-sources/AccountsApiDataSource.d.mts +5 -67
  29. package/dist/data-sources/AccountsApiDataSource.d.mts.map +1 -1
  30. package/dist/data-sources/AccountsApiDataSource.mjs +22 -97
  31. package/dist/data-sources/AccountsApiDataSource.mjs.map +1 -1
  32. package/dist/data-sources/BackendWebsocketDataSource.cjs +135 -45
  33. package/dist/data-sources/BackendWebsocketDataSource.cjs.map +1 -1
  34. package/dist/data-sources/BackendWebsocketDataSource.d.cts +19 -66
  35. package/dist/data-sources/BackendWebsocketDataSource.d.cts.map +1 -1
  36. package/dist/data-sources/BackendWebsocketDataSource.d.mts +19 -66
  37. package/dist/data-sources/BackendWebsocketDataSource.d.mts.map +1 -1
  38. package/dist/data-sources/BackendWebsocketDataSource.mjs +135 -45
  39. package/dist/data-sources/BackendWebsocketDataSource.mjs.map +1 -1
  40. package/dist/data-sources/PriceDataSource.cjs +22 -44
  41. package/dist/data-sources/PriceDataSource.cjs.map +1 -1
  42. package/dist/data-sources/PriceDataSource.d.cts +6 -89
  43. package/dist/data-sources/PriceDataSource.d.cts.map +1 -1
  44. package/dist/data-sources/PriceDataSource.d.mts +6 -89
  45. package/dist/data-sources/PriceDataSource.d.mts.map +1 -1
  46. package/dist/data-sources/PriceDataSource.mjs +22 -44
  47. package/dist/data-sources/PriceDataSource.mjs.map +1 -1
  48. package/dist/data-sources/RpcDataSource.cjs +57 -98
  49. package/dist/data-sources/RpcDataSource.cjs.map +1 -1
  50. package/dist/data-sources/RpcDataSource.d.cts +16 -55
  51. package/dist/data-sources/RpcDataSource.d.cts.map +1 -1
  52. package/dist/data-sources/RpcDataSource.d.mts +16 -55
  53. package/dist/data-sources/RpcDataSource.d.mts.map +1 -1
  54. package/dist/data-sources/RpcDataSource.mjs +57 -98
  55. package/dist/data-sources/RpcDataSource.mjs.map +1 -1
  56. package/dist/data-sources/SnapDataSource.cjs +30 -30
  57. package/dist/data-sources/SnapDataSource.cjs.map +1 -1
  58. package/dist/data-sources/SnapDataSource.d.cts +7 -44
  59. package/dist/data-sources/SnapDataSource.d.cts.map +1 -1
  60. package/dist/data-sources/SnapDataSource.d.mts +7 -44
  61. package/dist/data-sources/SnapDataSource.d.mts.map +1 -1
  62. package/dist/data-sources/SnapDataSource.mjs +30 -30
  63. package/dist/data-sources/SnapDataSource.mjs.map +1 -1
  64. package/dist/data-sources/TokenDataSource.cjs +3 -16
  65. package/dist/data-sources/TokenDataSource.cjs.map +1 -1
  66. package/dist/data-sources/TokenDataSource.d.cts +2 -25
  67. package/dist/data-sources/TokenDataSource.d.cts.map +1 -1
  68. package/dist/data-sources/TokenDataSource.d.mts +2 -25
  69. package/dist/data-sources/TokenDataSource.d.mts.map +1 -1
  70. package/dist/data-sources/TokenDataSource.mjs +3 -16
  71. package/dist/data-sources/TokenDataSource.mjs.map +1 -1
  72. package/dist/data-sources/index.cjs +1 -6
  73. package/dist/data-sources/index.cjs.map +1 -1
  74. package/dist/data-sources/index.d.cts +6 -7
  75. package/dist/data-sources/index.d.cts.map +1 -1
  76. package/dist/data-sources/index.d.mts +6 -7
  77. package/dist/data-sources/index.d.mts.map +1 -1
  78. package/dist/data-sources/index.mjs +1 -3
  79. package/dist/data-sources/index.mjs.map +1 -1
  80. package/dist/index.cjs +1 -6
  81. package/dist/index.cjs.map +1 -1
  82. package/dist/index.d.cts +9 -11
  83. package/dist/index.d.cts.map +1 -1
  84. package/dist/index.d.mts +9 -11
  85. package/dist/index.d.mts.map +1 -1
  86. package/dist/index.mjs +1 -3
  87. package/dist/index.mjs.map +1 -1
  88. package/dist/middlewares/DetectionMiddleware.cjs +4 -27
  89. package/dist/middlewares/DetectionMiddleware.cjs.map +1 -1
  90. package/dist/middlewares/DetectionMiddleware.d.cts +3 -26
  91. package/dist/middlewares/DetectionMiddleware.d.cts.map +1 -1
  92. package/dist/middlewares/DetectionMiddleware.d.mts +3 -26
  93. package/dist/middlewares/DetectionMiddleware.d.mts.map +1 -1
  94. package/dist/middlewares/DetectionMiddleware.mjs +4 -27
  95. package/dist/middlewares/DetectionMiddleware.mjs.map +1 -1
  96. package/dist/middlewares/index.cjs.map +1 -1
  97. package/dist/middlewares/index.d.cts +1 -1
  98. package/dist/middlewares/index.d.cts.map +1 -1
  99. package/dist/middlewares/index.d.mts +1 -1
  100. package/dist/middlewares/index.d.mts.map +1 -1
  101. package/dist/middlewares/index.mjs.map +1 -1
  102. package/dist/types.cjs.map +1 -1
  103. package/dist/types.d.cts +52 -3
  104. package/dist/types.d.cts.map +1 -1
  105. package/dist/types.d.mts +52 -3
  106. package/dist/types.d.mts.map +1 -1
  107. package/dist/types.mjs.map +1 -1
  108. package/package.json +1 -1
  109. package/dist/data-sources/initDataSources.cjs +0 -215
  110. package/dist/data-sources/initDataSources.cjs.map +0 -1
  111. package/dist/data-sources/initDataSources.d.cts +0 -140
  112. package/dist/data-sources/initDataSources.d.cts.map +0 -1
  113. package/dist/data-sources/initDataSources.d.mts +0 -140
  114. package/dist/data-sources/initDataSources.d.mts.map +0 -1
  115. package/dist/data-sources/initDataSources.mjs +0 -210
  116. 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, _PriceDataSource_messenger, _PriceDataSource_currency, _PriceDataSource_pollInterval, _PriceDataSource_apiClient, _PriceDataSource_activeSubscriptions, _PriceDataSource_registerActionHandlers, _PriceDataSource_fetchSpotPrices, _PriceDataSource_getAssetIdsFromBalanceState;
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, automatically fetches prices for all assets in assetsBalance state
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
- * Gets asset IDs from balance state, filtered by request.accounts and request.chainIds.
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, filtered by accounts and chains
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 for all assets in balance state
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 currently in balance state
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 __classPrivateFieldGet(this, _PriceDataSource_messenger, "f").call('AssetsController:assetsUpdate', fetchResponse, CONTROLLER_NAME);
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
- _PriceDataSource_messenger = new WeakMap(), _PriceDataSource_currency = new WeakMap(), _PriceDataSource_pollInterval = new WeakMap(), _PriceDataSource_apiClient = new WeakMap(), _PriceDataSource_activeSubscriptions = new WeakMap(), _PriceDataSource_instances = new WeakSet(), _PriceDataSource_registerActionHandlers = function _PriceDataSource_registerActionHandlers() {
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 = __classPrivateFieldGet(this, _PriceDataSource_messenger, "f").call('AssetsController:getState');
287
+ const state = getAssetsState();
310
288
  const assetIds = new Set();
311
- const accountIds = request.accounts.map((a) => a.id);
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, _RpcDataSource_registerActionHandlers, _RpcDataSource_subscribeToNetworkController, _RpcDataSource_initializeFromNetworkController, _RpcDataSource_updateFromNetworkState, _RpcDataSource_getProvider, _RpcDataSource_getMulticallProvider, _RpcDataSource_clearProviderCache, _RpcDataSource_accountSupportsChain, _RpcDataSource_buildNativeAssetId, _RpcDataSource_getExistingAssetsMetadata, _RpcDataSource_getTokenMetadataFromTokenList;
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 base_controller_1.BaseController {
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.messenger.call('AssetsController:getState');
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.messenger.call('TokenListController:getState');
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.accounts.map((a) => a.id),
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 chain using BalanceFetcher
208
- for (const chainId of chainsToFetch) {
209
- const hexChainId = (0, exports.caipChainIdToHex)(chainId);
210
- for (const account of request.accounts) {
211
- if (!__classPrivateFieldGet(this, _RpcDataSource_instances, "m", _RpcDataSource_accountSupportsChain).call(this, account, chainId)) {
212
- continue;
213
- }
214
- const { address, id: accountId } = account;
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.accounts.map((a) => a.id),
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.accounts.map((a) => a.id),
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 chainId of chainsToSubscribe) {
437
- const hexChainId = (0, exports.caipChainIdToHex)(chainId);
438
- for (const account of request.accounts) {
439
- if (!__classPrivateFieldGet(this, _RpcDataSource_instances, "m", _RpcDataSource_accountSupportsChain).call(this, account, chainId)) {
440
- continue;
441
- }
442
- const { address, id: accountId } = account;
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: request.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.messenger
589
- .call('AssetsController:assetsUpdate', response, CONTROLLER_NAME)
590
- .catch((error) => {
591
- log('Failed to update assets', { error });
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.messenger
639
- .call('AssetsController:assetsUpdate', response, CONTROLLER_NAME)
640
- .catch((error) => {
641
- log('Failed to update detected assets', { error });
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.messenger.subscribe('NetworkController:stateChange', (networkState) => {
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.messenger.call('NetworkController:getState');
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.messenger.call('AssetsController:activeChainsUpdate', CONTROLLER_NAME, activeChains);
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.messenger.call('NetworkController:getNetworkClientById', chainStatus.networkClientId);
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.messenger.call('NetworkEnablementController:getState');
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.messenger.call('AssetsController:getState');
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.messenger.call('TokenListController:getState');
798
- const chainCacheEntry = tokenListState.tokensChainsCache[hexChainId];
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;