@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,19 +9,38 @@ 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 _AssetsController_instances, _AssetsController_isEnabled, _AssetsController_defaultUpdateInterval, _AssetsController_controllerMutex, _AssetsController_activeSubscriptions, _AssetsController_enabledChains, _AssetsController_selectedAccounts_get, _AssetsController_dataSources, _AssetsController_initializeState, _AssetsController_extractEnabledChains, _AssetsController_normalizeChainReference, _AssetsController_subscribeToEvents, _AssetsController_registerActionHandlers, _AssetsController_executeMiddlewares, _AssetsController_assignChainsToDataSources, _AssetsController_updateState, _AssetsController_getAssetsFromState, _AssetsController_tokenStandardToAssetType, _AssetsController_start, _AssetsController_stop, _AssetsController_subscribeToDataSources, _AssetsController_subscribeAssetsBalance, _AssetsController_buildChainToAccountsMap, _AssetsController_getAccountsForChains, _AssetsController_subscribeToDataSource, _AssetsController_unsubscribeDataSource, _AssetsController_getEnabledChainsForAccount, _AssetsController_handleAccountGroupChanged, _AssetsController_handleEnabledNetworksChanged, _AssetsController_handleSubscriptionUpdate;
12
+ var _AssetsController_instances, _AssetsController_isEnabled, _AssetsController_defaultUpdateInterval, _AssetsController_controllerMutex, _AssetsController_activeSubscriptions, _AssetsController_enabledChains, _AssetsController_selectedAccounts_get, _AssetsController_dataSources, _AssetsController_backendWebsocketDataSource, _AssetsController_accountsApiDataSource, _AssetsController_snapDataSource, _AssetsController_rpcDataSource, _AssetsController_priceDataSource, _AssetsController_detectionMiddleware, _AssetsController_tokenDataSource, _AssetsController_getBalanceDataSource, _AssetsController_initializeState, _AssetsController_extractEnabledChains, _AssetsController_normalizeChainReference, _AssetsController_subscribeToEvents, _AssetsController_registerActionHandlers, _AssetsController_executeMiddlewares, _AssetsController_assignChainsToDataSources, _AssetsController_updateState, _AssetsController_getAssetsFromState, _AssetsController_tokenStandardToAssetType, _AssetsController_start, _AssetsController_stop, _AssetsController_subscribeToDataSources, _AssetsController_subscribeAssetsBalance, _AssetsController_buildChainToAccountsMap, _AssetsController_getAccountsForChains, _AssetsController_subscribeToDataSource, _AssetsController_unsubscribeDataSource, _AssetsController_buildDataRequest, _AssetsController_getEnabledChainsForAccount, _AssetsController_handleAccountGroupChanged, _AssetsController_handleEnabledNetworksChanged;
13
13
  import { BaseController } from "@metamask/base-controller";
14
14
  import { parseCaipAssetType } from "@metamask/utils";
15
15
  import { Mutex } from "async-mutex";
16
16
  import BigNumberJS from "bignumber.js";
17
17
  import $lodash from "lodash";
18
18
  const { isEqual } = $lodash;
19
+ import { AccountsApiDataSource } from "./data-sources/AccountsApiDataSource.mjs";
20
+ import { BackendWebsocketDataSource } from "./data-sources/BackendWebsocketDataSource.mjs";
21
+ import { PriceDataSource } from "./data-sources/PriceDataSource.mjs";
22
+ import { RpcDataSource } from "./data-sources/RpcDataSource.mjs";
23
+ import { SnapDataSource } from "./data-sources/SnapDataSource.mjs";
24
+ import { TokenDataSource } from "./data-sources/TokenDataSource.mjs";
19
25
  import { projectLogger, createModuleLogger } from "./logger.mjs";
26
+ import { DetectionMiddleware } from "./middlewares/DetectionMiddleware.mjs";
20
27
  import { normalizeAssetId } from "./utils.mjs";
21
28
  // ============================================================================
22
29
  // CONTROLLER CONSTANTS
23
30
  // ============================================================================
24
31
  const CONTROLLER_NAME = 'AssetsController';
32
+ /** Method names exposed as messenger actions (AssetsController:getAssets, etc.) */
33
+ const MESSENGER_EXPOSED_METHODS = [
34
+ 'getAssets',
35
+ 'getAssetsBalance',
36
+ 'getAssetMetadata',
37
+ 'getAssetsPrice',
38
+ 'addCustomAsset',
39
+ 'removeCustomAsset',
40
+ 'getCustomAssets',
41
+ 'hideAsset',
42
+ 'unhideAsset',
43
+ ];
25
44
  /** Default polling interval hint for data sources (30 seconds) */
26
45
  const DEFAULT_POLLING_INTERVAL_MS = 30000;
27
46
  const log = createModuleLogger(projectLogger, CONTROLLER_NAME);
@@ -147,13 +166,8 @@ function normalizeResponse(response) {
147
166
  * based on which chains they support. When active chains change, the controller
148
167
  * dynamically adjusts subscriptions.
149
168
  *
150
- * 4. **App Lifecycle Management**: Listens to app open/close events via messenger
151
- * to start/stop subscriptions automatically, conserving resources when app is closed.
152
- *
153
- * ## App Lifecycle
154
- *
155
- * - **App Opened** (`AppStateController:appOpened`): Starts subscriptions, fetches initial data
156
- * - **App Closed** (`AppStateController:appClosed`): Stops all subscriptions to conserve resources
169
+ * 4. **Keyring Lifecycle**: Listens to KeyringController unlock/lock events to
170
+ * start/stop subscriptions when the wallet is unlocked or locked.
157
171
  *
158
172
  * ## Architecture
159
173
  *
@@ -162,7 +176,7 @@ function normalizeResponse(response) {
162
176
  * - The controller does NOT manage polling - it simply receives pushed updates
163
177
  */
164
178
  export class AssetsController extends BaseController {
165
- constructor({ messenger, state = {}, defaultUpdateInterval = DEFAULT_POLLING_INTERVAL_MS, isEnabled = () => true, }) {
179
+ constructor({ messenger, state = {}, defaultUpdateInterval = DEFAULT_POLLING_INTERVAL_MS, isEnabled = () => true, queryApiClient, rpcDataSourceConfig, }) {
166
180
  super({
167
181
  name: CONTROLLER_NAME,
168
182
  messenger,
@@ -193,25 +207,57 @@ export class AssetsController extends BaseController {
193
207
  * Key: sourceId, Value: Set of currently available chainIds
194
208
  */
195
209
  _AssetsController_dataSources.set(this, new Map());
210
+ _AssetsController_backendWebsocketDataSource.set(this, void 0);
211
+ _AssetsController_accountsApiDataSource.set(this, void 0);
212
+ _AssetsController_snapDataSource.set(this, void 0);
213
+ _AssetsController_rpcDataSource.set(this, void 0);
214
+ _AssetsController_priceDataSource.set(this, void 0);
215
+ _AssetsController_detectionMiddleware.set(this, void 0);
216
+ _AssetsController_tokenDataSource.set(this, void 0);
196
217
  __classPrivateFieldSet(this, _AssetsController_isEnabled, isEnabled(), "f");
197
218
  __classPrivateFieldSet(this, _AssetsController_defaultUpdateInterval, defaultUpdateInterval, "f");
219
+ const rpcConfig = rpcDataSourceConfig ?? {};
220
+ __classPrivateFieldSet(this, _AssetsController_backendWebsocketDataSource, new BackendWebsocketDataSource({
221
+ messenger: this.messenger,
222
+ queryApiClient,
223
+ onActiveChainsUpdated: (chains) => this.handleActiveChainsUpdate('BackendWebsocketDataSource', chains),
224
+ }), "f");
225
+ __classPrivateFieldSet(this, _AssetsController_accountsApiDataSource, new AccountsApiDataSource({
226
+ queryApiClient,
227
+ onActiveChainsUpdated: (chains) => {
228
+ this.handleActiveChainsUpdate('AccountsApiDataSource', chains);
229
+ },
230
+ }), "f");
231
+ __classPrivateFieldSet(this, _AssetsController_snapDataSource, new SnapDataSource({
232
+ messenger: this.messenger,
233
+ onActiveChainsUpdated: (chains) => this.handleActiveChainsUpdate('SnapDataSource', chains),
234
+ }), "f");
235
+ __classPrivateFieldSet(this, _AssetsController_rpcDataSource, new RpcDataSource({
236
+ messenger: this.messenger,
237
+ onActiveChainsUpdated: (chains) => this.handleActiveChainsUpdate('RpcDataSource', chains),
238
+ ...rpcConfig,
239
+ }), "f");
240
+ __classPrivateFieldSet(this, _AssetsController_tokenDataSource, new TokenDataSource({
241
+ queryApiClient,
242
+ }), "f");
243
+ __classPrivateFieldSet(this, _AssetsController_priceDataSource, new PriceDataSource({
244
+ queryApiClient,
245
+ }), "f");
246
+ __classPrivateFieldSet(this, _AssetsController_detectionMiddleware, new DetectionMiddleware(), "f");
247
+ __classPrivateFieldGet(this, _AssetsController_dataSources, "f").set('BackendWebsocketDataSource', new Set());
248
+ __classPrivateFieldGet(this, _AssetsController_dataSources, "f").set('AccountsApiDataSource', new Set());
249
+ __classPrivateFieldGet(this, _AssetsController_dataSources, "f").set('SnapDataSource', new Set());
250
+ __classPrivateFieldGet(this, _AssetsController_dataSources, "f").set('RpcDataSource', new Set());
198
251
  if (!__classPrivateFieldGet(this, _AssetsController_isEnabled, "f")) {
199
252
  log('AssetsController is disabled, skipping initialization');
200
253
  return;
201
254
  }
202
255
  log('Initializing AssetsController', {
203
- defaultUpdateInterval,
256
+ defaultUpdateInterval: __classPrivateFieldGet(this, _AssetsController_defaultUpdateInterval, "f"),
204
257
  });
205
258
  __classPrivateFieldGet(this, _AssetsController_instances, "m", _AssetsController_initializeState).call(this);
206
259
  __classPrivateFieldGet(this, _AssetsController_instances, "m", _AssetsController_subscribeToEvents).call(this);
207
260
  __classPrivateFieldGet(this, _AssetsController_instances, "m", _AssetsController_registerActionHandlers).call(this);
208
- // Register data sources (order = subscription priority)
209
- this.registerDataSources([
210
- 'BackendWebsocketDataSource', // Real-time push updates
211
- 'AccountsApiDataSource', // HTTP polling fallback
212
- 'SnapDataSource', // Solana/Bitcoin/Tron snaps
213
- 'RpcDataSource', // Direct blockchain queries
214
- ]);
215
261
  }
216
262
  // ============================================================================
217
263
  // DATA SOURCE MANAGEMENT
@@ -220,7 +266,7 @@ export class AssetsController extends BaseController {
220
266
  * Register data sources with the controller.
221
267
  * Order of the array determines subscription order.
222
268
  *
223
- * Data sources report chain changes by calling `AssetsController:activeChainsUpdate` action.
269
+ * Data sources report chain changes via the onActiveChainsUpdated callback passed at construction.
224
270
  *
225
271
  * @param dataSourceIds - Array of data source identifiers to register.
226
272
  */
@@ -239,7 +285,7 @@ export class AssetsController extends BaseController {
239
285
  * Active chains are chains that are both supported AND available.
240
286
  * Updates centralized chain tracking and triggers re-selection if needed.
241
287
  *
242
- * Data sources should call this via `AssetsController:activeChainsUpdate` action.
288
+ * Called from the onActiveChainsUpdated callbacks passed to data sources at construction.
243
289
  *
244
290
  * @param dataSourceId - The identifier of the data source reporting the change.
245
291
  * @param activeChains - Array of currently active chain IDs for this source.
@@ -250,6 +296,10 @@ export class AssetsController extends BaseController {
250
296
  chainCount: activeChains.length,
251
297
  chains: activeChains,
252
298
  });
299
+ // When BackendWebsocketDataSource is updated via AccountsApiDataSource callback, sync its state
300
+ if (dataSourceId === 'BackendWebsocketDataSource') {
301
+ __classPrivateFieldGet(this, _AssetsController_backendWebsocketDataSource, "f").setActiveChainsFromAccountsApi(activeChains);
302
+ }
253
303
  const previousChains = __classPrivateFieldGet(this, _AssetsController_dataSources, "f").get(dataSourceId) ?? new Set();
254
304
  const newChains = new Set(activeChains);
255
305
  // Update centralized available chains tracking
@@ -289,21 +339,20 @@ export class AssetsController extends BaseController {
289
339
  customAssets.push(...accountCustomAssets);
290
340
  }
291
341
  if (options?.forceUpdate) {
292
- const response = await __classPrivateFieldGet(this, _AssetsController_instances, "m", _AssetsController_executeMiddlewares).call(this, [
293
- this.messenger.call('AccountsApiDataSource:getAssetsMiddleware'),
294
- this.messenger.call('SnapDataSource:getAssetsMiddleware'),
295
- this.messenger.call('RpcDataSource:getAssetsMiddleware'),
296
- this.messenger.call('DetectionMiddleware:getAssetsMiddleware'),
297
- this.messenger.call('TokenDataSource:getAssetsMiddleware'),
298
- this.messenger.call('PriceDataSource:getAssetsMiddleware'),
299
- ], {
300
- accounts,
301
- chainIds,
342
+ const request = __classPrivateFieldGet(this, _AssetsController_instances, "m", _AssetsController_buildDataRequest).call(this, accounts, chainIds, {
302
343
  assetTypes,
303
344
  dataTypes,
304
345
  customAssets: customAssets.length > 0 ? customAssets : undefined,
305
346
  forceUpdate: true,
306
347
  });
348
+ const response = await __classPrivateFieldGet(this, _AssetsController_instances, "m", _AssetsController_executeMiddlewares).call(this, [
349
+ __classPrivateFieldGet(this, _AssetsController_accountsApiDataSource, "f").assetsMiddleware,
350
+ __classPrivateFieldGet(this, _AssetsController_snapDataSource, "f").assetsMiddleware,
351
+ __classPrivateFieldGet(this, _AssetsController_rpcDataSource, "f").assetsMiddleware,
352
+ __classPrivateFieldGet(this, _AssetsController_detectionMiddleware, "f").assetsMiddleware,
353
+ __classPrivateFieldGet(this, _AssetsController_tokenDataSource, "f").assetsMiddleware,
354
+ __classPrivateFieldGet(this, _AssetsController_priceDataSource, "f").assetsMiddleware,
355
+ ], request);
307
356
  await __classPrivateFieldGet(this, _AssetsController_instances, "m", _AssetsController_updateState).call(this, response);
308
357
  }
309
358
  return __classPrivateFieldGet(this, _AssetsController_instances, "m", _AssetsController_getAssetsFromState).call(this, accounts, chainIds, assetTypes);
@@ -471,19 +520,17 @@ export class AssetsController extends BaseController {
471
520
  const subscriptionKey = 'ds:PriceDataSource';
472
521
  const existingSubscription = __classPrivateFieldGet(this, _AssetsController_activeSubscriptions, "f").get(subscriptionKey);
473
522
  const isUpdate = existingSubscription !== undefined;
474
- // Fire-and-forget - errors are handled internally by PriceDataSource
475
- this.messenger
476
- .call('PriceDataSource:subscribe', {
477
- request: {
478
- accounts,
479
- chainIds,
523
+ const subscribeReq = {
524
+ request: __classPrivateFieldGet(this, _AssetsController_instances, "m", _AssetsController_buildDataRequest).call(this, accounts, chainIds, {
480
525
  dataTypes: ['price'],
481
526
  updateInterval,
482
- },
527
+ }),
483
528
  subscriptionId: subscriptionKey,
484
529
  isUpdate,
485
- })
486
- .catch(console.error);
530
+ onAssetsUpdate: (response) => this.handleAssetsUpdate(response, 'PriceDataSource'),
531
+ getAssetsState: () => this.state,
532
+ };
533
+ __classPrivateFieldGet(this, _AssetsController_priceDataSource, "f").subscribe(subscribeReq).catch(console.error);
487
534
  // Track subscription
488
535
  const subscription = {
489
536
  chains: chainIds,
@@ -505,26 +552,36 @@ export class AssetsController extends BaseController {
505
552
  if (!existingSubscription) {
506
553
  return;
507
554
  }
508
- // Fire-and-forget - errors are handled internally by PriceDataSource
509
- this.messenger
510
- .call('PriceDataSource:unsubscribe', subscriptionKey)
511
- .catch(console.error);
555
+ __classPrivateFieldGet(this, _AssetsController_priceDataSource, "f").unsubscribe(subscriptionKey).catch(console.error);
512
556
  existingSubscription.unsubscribe();
513
557
  }
514
558
  /**
515
559
  * Handle assets updated from a data source.
516
- * Called via `AssetsController:assetsUpdate` action by data sources.
560
+ * Called via the onAssetsUpdate callback passed in SubscriptionRequest when the controller subscribes to a data source.
561
+ * Enriches the response with token metadata (via middlewares) before updating state.
517
562
  *
518
563
  * @param response - The data response with updated assets
519
564
  * @param sourceId - The data source ID reporting the update
565
+ * @param request - Optional original request for context when enriching
520
566
  */
521
- async handleAssetsUpdate(response, sourceId) {
567
+ async handleAssetsUpdate(response, sourceId, request) {
522
568
  log('Assets updated from data source', {
523
569
  sourceId,
524
570
  hasBalance: Boolean(response.assetsBalance),
525
571
  hasPrice: Boolean(response.assetsPrice),
526
572
  });
527
- await __classPrivateFieldGet(this, _AssetsController_instances, "m", _AssetsController_handleSubscriptionUpdate).call(this, response, sourceId);
573
+ // Run through enrichment middlewares (Event Stack: Detection → Token → Price)
574
+ // Include 'metadata' in dataTypes so TokenDataSource runs to enrich detected assets
575
+ const enrichedResponse = await __classPrivateFieldGet(this, _AssetsController_instances, "m", _AssetsController_executeMiddlewares).call(this, [
576
+ __classPrivateFieldGet(this, _AssetsController_detectionMiddleware, "f").assetsMiddleware,
577
+ __classPrivateFieldGet(this, _AssetsController_tokenDataSource, "f").assetsMiddleware,
578
+ __classPrivateFieldGet(this, _AssetsController_priceDataSource, "f").assetsMiddleware,
579
+ ], request ?? {
580
+ accountsWithSupportedChains: [],
581
+ chainIds: [],
582
+ dataTypes: ['balance', 'metadata', 'price'],
583
+ }, response);
584
+ await __classPrivateFieldGet(this, _AssetsController_instances, "m", _AssetsController_updateState).call(this, enrichedResponse);
528
585
  }
529
586
  // ============================================================================
530
587
  // CLEANUP
@@ -534,6 +591,16 @@ export class AssetsController extends BaseController {
534
591
  dataSourceCount: __classPrivateFieldGet(this, _AssetsController_dataSources, "f").size,
535
592
  subscriptionCount: __classPrivateFieldGet(this, _AssetsController_activeSubscriptions, "f").size,
536
593
  });
594
+ // Destroy instantiated data sources
595
+ __classPrivateFieldGet(this, _AssetsController_backendWebsocketDataSource, "f")?.destroy?.();
596
+ __classPrivateFieldGet(this, _AssetsController_accountsApiDataSource, "f")?.destroy?.();
597
+ __classPrivateFieldGet(this, _AssetsController_snapDataSource, "f")?.destroy?.();
598
+ if (__classPrivateFieldGet(this, _AssetsController_rpcDataSource, "f") &&
599
+ 'destroy' in __classPrivateFieldGet(this, _AssetsController_rpcDataSource, "f") &&
600
+ typeof __classPrivateFieldGet(this, _AssetsController_rpcDataSource, "f").destroy ===
601
+ 'function') {
602
+ __classPrivateFieldGet(this, _AssetsController_rpcDataSource, "f").destroy();
603
+ }
537
604
  // Clear data sources
538
605
  __classPrivateFieldGet(this, _AssetsController_dataSources, "f").clear();
539
606
  // Stop all active subscriptions
@@ -543,8 +610,6 @@ export class AssetsController extends BaseController {
543
610
  this.messenger.unregisterActionHandler('AssetsController:getAssetsBalance');
544
611
  this.messenger.unregisterActionHandler('AssetsController:getAssetMetadata');
545
612
  this.messenger.unregisterActionHandler('AssetsController:getAssetsPrice');
546
- this.messenger.unregisterActionHandler('AssetsController:activeChainsUpdate');
547
- this.messenger.unregisterActionHandler('AssetsController:assetsUpdate');
548
613
  this.messenger.unregisterActionHandler('AssetsController:addCustomAsset');
549
614
  this.messenger.unregisterActionHandler('AssetsController:removeCustomAsset');
550
615
  this.messenger.unregisterActionHandler('AssetsController:getCustomAssets');
@@ -552,8 +617,21 @@ export class AssetsController extends BaseController {
552
617
  this.messenger.unregisterActionHandler('AssetsController:unhideAsset');
553
618
  }
554
619
  }
555
- _AssetsController_isEnabled = new WeakMap(), _AssetsController_defaultUpdateInterval = new WeakMap(), _AssetsController_controllerMutex = new WeakMap(), _AssetsController_activeSubscriptions = new WeakMap(), _AssetsController_enabledChains = new WeakMap(), _AssetsController_dataSources = new WeakMap(), _AssetsController_instances = new WeakSet(), _AssetsController_selectedAccounts_get = function _AssetsController_selectedAccounts_get() {
620
+ _AssetsController_isEnabled = new WeakMap(), _AssetsController_defaultUpdateInterval = new WeakMap(), _AssetsController_controllerMutex = new WeakMap(), _AssetsController_activeSubscriptions = new WeakMap(), _AssetsController_enabledChains = new WeakMap(), _AssetsController_dataSources = new WeakMap(), _AssetsController_backendWebsocketDataSource = new WeakMap(), _AssetsController_accountsApiDataSource = new WeakMap(), _AssetsController_snapDataSource = new WeakMap(), _AssetsController_rpcDataSource = new WeakMap(), _AssetsController_priceDataSource = new WeakMap(), _AssetsController_detectionMiddleware = new WeakMap(), _AssetsController_tokenDataSource = new WeakMap(), _AssetsController_instances = new WeakSet(), _AssetsController_selectedAccounts_get = function _AssetsController_selectedAccounts_get() {
556
621
  return this.messenger.call('AccountTreeController:getAccountsFromSelectedAccountGroup');
622
+ }, _AssetsController_getBalanceDataSource = function _AssetsController_getBalanceDataSource(sourceId) {
623
+ switch (sourceId) {
624
+ case 'BackendWebsocketDataSource':
625
+ return __classPrivateFieldGet(this, _AssetsController_backendWebsocketDataSource, "f");
626
+ case 'AccountsApiDataSource':
627
+ return __classPrivateFieldGet(this, _AssetsController_accountsApiDataSource, "f");
628
+ case 'SnapDataSource':
629
+ return __classPrivateFieldGet(this, _AssetsController_snapDataSource, "f");
630
+ case 'RpcDataSource':
631
+ return __classPrivateFieldGet(this, _AssetsController_rpcDataSource, "f");
632
+ default:
633
+ return undefined;
634
+ }
557
635
  }, _AssetsController_initializeState = function _AssetsController_initializeState() {
558
636
  const { enabledNetworkMap } = this.messenger.call('NetworkEnablementController:getState');
559
637
  __classPrivateFieldSet(this, _AssetsController_enabledChains, __classPrivateFieldGet(this, _AssetsController_instances, "m", _AssetsController_extractEnabledChains).call(this, enabledNetworkMap), "f");
@@ -595,24 +673,11 @@ _AssetsController_isEnabled = new WeakMap(), _AssetsController_defaultUpdateInte
595
673
  this.messenger.subscribe('NetworkEnablementController:stateChange', ({ enabledNetworkMap }) => {
596
674
  __classPrivateFieldGet(this, _AssetsController_instances, "m", _AssetsController_handleEnabledNetworksChanged).call(this, enabledNetworkMap).catch(console.error);
597
675
  });
598
- // App lifecycle: start when opened, stop when closed
599
- this.messenger.subscribe('AppStateController:appOpened', () => __classPrivateFieldGet(this, _AssetsController_instances, "m", _AssetsController_start).call(this));
600
- this.messenger.subscribe('AppStateController:appClosed', () => __classPrivateFieldGet(this, _AssetsController_instances, "m", _AssetsController_stop).call(this));
601
676
  // Keyring lifecycle: start when unlocked, stop when locked
602
677
  this.messenger.subscribe('KeyringController:unlock', () => __classPrivateFieldGet(this, _AssetsController_instances, "m", _AssetsController_start).call(this));
603
678
  this.messenger.subscribe('KeyringController:lock', () => __classPrivateFieldGet(this, _AssetsController_instances, "m", _AssetsController_stop).call(this));
604
679
  }, _AssetsController_registerActionHandlers = function _AssetsController_registerActionHandlers() {
605
- this.messenger.registerActionHandler('AssetsController:getAssets', this.getAssets.bind(this));
606
- this.messenger.registerActionHandler('AssetsController:getAssetsBalance', this.getAssetsBalance.bind(this));
607
- this.messenger.registerActionHandler('AssetsController:getAssetMetadata', this.getAssetMetadata.bind(this));
608
- this.messenger.registerActionHandler('AssetsController:getAssetsPrice', this.getAssetsPrice.bind(this));
609
- this.messenger.registerActionHandler('AssetsController:activeChainsUpdate', this.handleActiveChainsUpdate.bind(this));
610
- this.messenger.registerActionHandler('AssetsController:assetsUpdate', this.handleAssetsUpdate.bind(this));
611
- this.messenger.registerActionHandler('AssetsController:addCustomAsset', this.addCustomAsset.bind(this));
612
- this.messenger.registerActionHandler('AssetsController:removeCustomAsset', this.removeCustomAsset.bind(this));
613
- this.messenger.registerActionHandler('AssetsController:getCustomAssets', this.getCustomAssets.bind(this));
614
- this.messenger.registerActionHandler('AssetsController:hideAsset', this.hideAsset.bind(this));
615
- this.messenger.registerActionHandler('AssetsController:unhideAsset', this.unhideAsset.bind(this));
680
+ this.messenger.registerMethodActionHandlers(this, MESSENGER_EXPOSED_METHODS);
616
681
  }, _AssetsController_executeMiddlewares =
617
682
  // ============================================================================
618
683
  // MIDDLEWARE EXECUTION
@@ -962,31 +1027,30 @@ async function _AssetsController_updateState(response) {
962
1027
  accountCount: accounts.length,
963
1028
  chainCount: chains.length,
964
1029
  });
965
- // Call data source subscribe action via Messenger (fire-and-forget)
966
- (async () => {
967
- try {
968
- await this.messenger.call(`${sourceId}:subscribe`, {
969
- request: {
970
- accounts,
971
- chainIds: chains,
972
- assetTypes: ['fungible'],
973
- dataTypes: ['balance'],
974
- updateInterval: __classPrivateFieldGet(this, _AssetsController_defaultUpdateInterval, "f"),
975
- },
976
- subscriptionId: subscriptionKey,
977
- isUpdate,
978
- });
979
- }
980
- catch (error) {
981
- console.error(`[AssetsController] Failed to subscribe to '${sourceId}':`, error);
982
- }
983
- })().catch(console.error);
1030
+ const subscribeReq = {
1031
+ request: __classPrivateFieldGet(this, _AssetsController_instances, "m", _AssetsController_buildDataRequest).call(this, accounts, chains, {
1032
+ assetTypes: ['fungible'],
1033
+ dataTypes: ['balance'],
1034
+ updateInterval: __classPrivateFieldGet(this, _AssetsController_defaultUpdateInterval, "f"),
1035
+ }),
1036
+ subscriptionId: subscriptionKey,
1037
+ isUpdate,
1038
+ onAssetsUpdate: (response) => this.handleAssetsUpdate(response, sourceId),
1039
+ getAssetsState: () => this.state,
1040
+ };
1041
+ const balanceDs = __classPrivateFieldGet(this, _AssetsController_instances, "m", _AssetsController_getBalanceDataSource).call(this, sourceId);
1042
+ if (!balanceDs) {
1043
+ return;
1044
+ }
1045
+ balanceDs.subscribe(subscribeReq).catch((error) => {
1046
+ console.error(`[AssetsController] Failed to subscribe to '${sourceId}':`, error);
1047
+ });
984
1048
  // Track subscription
985
1049
  const subscription = {
986
1050
  chains,
987
1051
  accountId: subscriptionKey,
988
1052
  assetTypes: ['fungible'],
989
- dataTypes: ['balance', 'price'],
1053
+ dataTypes: ['balance'],
990
1054
  unsubscribe: () => {
991
1055
  __classPrivateFieldGet(this, _AssetsController_activeSubscriptions, "f").delete(subscriptionKey);
992
1056
  },
@@ -996,19 +1060,23 @@ async function _AssetsController_updateState(response) {
996
1060
  const subscriptionKey = `ds:${sourceId}`;
997
1061
  const existingSubscription = __classPrivateFieldGet(this, _AssetsController_activeSubscriptions, "f").get(subscriptionKey);
998
1062
  if (existingSubscription) {
999
- // Fire-and-forget unsubscribe call
1000
- (async () => {
1001
- try {
1002
- await this.messenger.call(`${sourceId}:unsubscribe`, subscriptionKey);
1003
- }
1004
- catch {
1005
- // Ignore errors - source may not have been subscribed
1006
- }
1007
- })().catch(() => {
1008
- // Ignore errors - source may not have been subscribed
1009
- });
1063
+ const balanceDs = __classPrivateFieldGet(this, _AssetsController_instances, "m", _AssetsController_getBalanceDataSource).call(this, sourceId);
1064
+ if (balanceDs) {
1065
+ balanceDs.unsubscribe(subscriptionKey).catch(() => undefined);
1066
+ }
1010
1067
  existingSubscription.unsubscribe();
1011
1068
  }
1069
+ }, _AssetsController_buildDataRequest = function _AssetsController_buildDataRequest(accounts, chainIds, partial = {}) {
1070
+ const chainIdSet = new Set(chainIds);
1071
+ const accountsWithSupportedChains = accounts.map((account) => ({
1072
+ account,
1073
+ supportedChains: __classPrivateFieldGet(this, _AssetsController_instances, "m", _AssetsController_getEnabledChainsForAccount).call(this, account).filter((chain) => chainIdSet.has(chain)),
1074
+ }));
1075
+ return {
1076
+ accountsWithSupportedChains,
1077
+ chainIds,
1078
+ ...partial,
1079
+ };
1012
1080
  }, _AssetsController_getEnabledChainsForAccount = function _AssetsController_getEnabledChainsForAccount(account) {
1013
1081
  // Account scopes are CAIP-2 chain IDs like "eip155:1", "solana:mainnet", "bip122:..."
1014
1082
  const scopes = account.scopes ?? [];
@@ -1085,28 +1153,5 @@ async function _AssetsController_handleAccountGroupChanged() {
1085
1153
  forceUpdate: true,
1086
1154
  });
1087
1155
  }
1088
- }, _AssetsController_handleSubscriptionUpdate =
1089
- /**
1090
- * Handle an async update from a data source subscription.
1091
- * Enriches response with token metadata before updating state.
1092
- *
1093
- * @param response - The data response from the data source.
1094
- * @param _sourceId - The source ID (unused but kept for logging context).
1095
- * @param request - Optional original request for context.
1096
- */
1097
- async function _AssetsController_handleSubscriptionUpdate(response, _sourceId, request) {
1098
- // Run through enrichment middlewares (Event Stack: Detection → Token → Price)
1099
- // Include 'metadata' in dataTypes so TokenDataSource runs to enrich detected assets
1100
- const enrichedResponse = await __classPrivateFieldGet(this, _AssetsController_instances, "m", _AssetsController_executeMiddlewares).call(this, [
1101
- this.messenger.call('DetectionMiddleware:getAssetsMiddleware'),
1102
- this.messenger.call('TokenDataSource:getAssetsMiddleware'),
1103
- this.messenger.call('PriceDataSource:getAssetsMiddleware'),
1104
- ], request ?? {
1105
- accounts: [],
1106
- chainIds: [],
1107
- dataTypes: ['balance', 'metadata', 'price'],
1108
- }, response);
1109
- // Update state
1110
- await __classPrivateFieldGet(this, _AssetsController_instances, "m", _AssetsController_updateState).call(this, enrichedResponse);
1111
1156
  };
1112
1157
  //# sourceMappingURL=AssetsController.mjs.map