@metamask/assets-controller 0.2.0 → 1.0.0

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 (126) hide show
  1. package/CHANGELOG.md +17 -1
  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 +201 -237
  11. package/dist/AssetsController.cjs.map +1 -1
  12. package/dist/AssetsController.d.cts +29 -92
  13. package/dist/AssetsController.d.cts.map +1 -1
  14. package/dist/AssetsController.d.mts +29 -92
  15. package/dist/AssetsController.d.mts.map +1 -1
  16. package/dist/AssetsController.mjs +201 -237
  17. package/dist/AssetsController.mjs.map +1 -1
  18. package/dist/data-sources/AbstractDataSource.cjs +8 -0
  19. package/dist/data-sources/AbstractDataSource.cjs.map +1 -1
  20. package/dist/data-sources/AbstractDataSource.d.cts +16 -1
  21. package/dist/data-sources/AbstractDataSource.d.cts.map +1 -1
  22. package/dist/data-sources/AbstractDataSource.d.mts +16 -1
  23. package/dist/data-sources/AbstractDataSource.d.mts.map +1 -1
  24. package/dist/data-sources/AbstractDataSource.mjs +8 -0
  25. package/dist/data-sources/AbstractDataSource.mjs.map +1 -1
  26. package/dist/data-sources/AccountsApiDataSource.cjs +25 -99
  27. package/dist/data-sources/AccountsApiDataSource.cjs.map +1 -1
  28. package/dist/data-sources/AccountsApiDataSource.d.cts +5 -67
  29. package/dist/data-sources/AccountsApiDataSource.d.cts.map +1 -1
  30. package/dist/data-sources/AccountsApiDataSource.d.mts +5 -67
  31. package/dist/data-sources/AccountsApiDataSource.d.mts.map +1 -1
  32. package/dist/data-sources/AccountsApiDataSource.mjs +24 -97
  33. package/dist/data-sources/AccountsApiDataSource.mjs.map +1 -1
  34. package/dist/data-sources/BackendWebsocketDataSource.cjs +138 -45
  35. package/dist/data-sources/BackendWebsocketDataSource.cjs.map +1 -1
  36. package/dist/data-sources/BackendWebsocketDataSource.d.cts +19 -66
  37. package/dist/data-sources/BackendWebsocketDataSource.d.cts.map +1 -1
  38. package/dist/data-sources/BackendWebsocketDataSource.d.mts +19 -66
  39. package/dist/data-sources/BackendWebsocketDataSource.d.mts.map +1 -1
  40. package/dist/data-sources/BackendWebsocketDataSource.mjs +138 -45
  41. package/dist/data-sources/BackendWebsocketDataSource.mjs.map +1 -1
  42. package/dist/data-sources/PriceDataSource.cjs +22 -44
  43. package/dist/data-sources/PriceDataSource.cjs.map +1 -1
  44. package/dist/data-sources/PriceDataSource.d.cts +6 -89
  45. package/dist/data-sources/PriceDataSource.d.cts.map +1 -1
  46. package/dist/data-sources/PriceDataSource.d.mts +6 -89
  47. package/dist/data-sources/PriceDataSource.d.mts.map +1 -1
  48. package/dist/data-sources/PriceDataSource.mjs +22 -44
  49. package/dist/data-sources/PriceDataSource.mjs.map +1 -1
  50. package/dist/data-sources/RpcDataSource.cjs +64 -102
  51. package/dist/data-sources/RpcDataSource.cjs.map +1 -1
  52. package/dist/data-sources/RpcDataSource.d.cts +16 -55
  53. package/dist/data-sources/RpcDataSource.d.cts.map +1 -1
  54. package/dist/data-sources/RpcDataSource.d.mts +16 -55
  55. package/dist/data-sources/RpcDataSource.d.mts.map +1 -1
  56. package/dist/data-sources/RpcDataSource.mjs +64 -102
  57. package/dist/data-sources/RpcDataSource.mjs.map +1 -1
  58. package/dist/data-sources/SnapDataSource.cjs +32 -30
  59. package/dist/data-sources/SnapDataSource.cjs.map +1 -1
  60. package/dist/data-sources/SnapDataSource.d.cts +7 -44
  61. package/dist/data-sources/SnapDataSource.d.cts.map +1 -1
  62. package/dist/data-sources/SnapDataSource.d.mts +7 -44
  63. package/dist/data-sources/SnapDataSource.d.mts.map +1 -1
  64. package/dist/data-sources/SnapDataSource.mjs +32 -30
  65. package/dist/data-sources/SnapDataSource.mjs.map +1 -1
  66. package/dist/data-sources/TokenDataSource.cjs +3 -16
  67. package/dist/data-sources/TokenDataSource.cjs.map +1 -1
  68. package/dist/data-sources/TokenDataSource.d.cts +2 -25
  69. package/dist/data-sources/TokenDataSource.d.cts.map +1 -1
  70. package/dist/data-sources/TokenDataSource.d.mts +2 -25
  71. package/dist/data-sources/TokenDataSource.d.mts.map +1 -1
  72. package/dist/data-sources/TokenDataSource.mjs +3 -16
  73. package/dist/data-sources/TokenDataSource.mjs.map +1 -1
  74. package/dist/data-sources/index.cjs +1 -6
  75. package/dist/data-sources/index.cjs.map +1 -1
  76. package/dist/data-sources/index.d.cts +6 -7
  77. package/dist/data-sources/index.d.cts.map +1 -1
  78. package/dist/data-sources/index.d.mts +6 -7
  79. package/dist/data-sources/index.d.mts.map +1 -1
  80. package/dist/data-sources/index.mjs +1 -3
  81. package/dist/data-sources/index.mjs.map +1 -1
  82. package/dist/index.cjs +6 -6
  83. package/dist/index.cjs.map +1 -1
  84. package/dist/index.d.cts +11 -11
  85. package/dist/index.d.cts.map +1 -1
  86. package/dist/index.d.mts +11 -11
  87. package/dist/index.d.mts.map +1 -1
  88. package/dist/index.mjs +3 -3
  89. package/dist/index.mjs.map +1 -1
  90. package/dist/middlewares/DetectionMiddleware.cjs +4 -27
  91. package/dist/middlewares/DetectionMiddleware.cjs.map +1 -1
  92. package/dist/middlewares/DetectionMiddleware.d.cts +3 -26
  93. package/dist/middlewares/DetectionMiddleware.d.cts.map +1 -1
  94. package/dist/middlewares/DetectionMiddleware.d.mts +3 -26
  95. package/dist/middlewares/DetectionMiddleware.d.mts.map +1 -1
  96. package/dist/middlewares/DetectionMiddleware.mjs +4 -27
  97. package/dist/middlewares/DetectionMiddleware.mjs.map +1 -1
  98. package/dist/middlewares/index.cjs.map +1 -1
  99. package/dist/middlewares/index.d.cts +1 -1
  100. package/dist/middlewares/index.d.cts.map +1 -1
  101. package/dist/middlewares/index.d.mts +1 -1
  102. package/dist/middlewares/index.d.mts.map +1 -1
  103. package/dist/middlewares/index.mjs.map +1 -1
  104. package/dist/selectors/balance.cjs +306 -0
  105. package/dist/selectors/balance.cjs.map +1 -0
  106. package/dist/selectors/balance.d.cts +22 -0
  107. package/dist/selectors/balance.d.cts.map +1 -0
  108. package/dist/selectors/balance.d.mts +22 -0
  109. package/dist/selectors/balance.d.mts.map +1 -0
  110. package/dist/selectors/balance.mjs +297 -0
  111. package/dist/selectors/balance.mjs.map +1 -0
  112. package/dist/types.cjs.map +1 -1
  113. package/dist/types.d.cts +53 -18
  114. package/dist/types.d.cts.map +1 -1
  115. package/dist/types.d.mts +53 -18
  116. package/dist/types.d.mts.map +1 -1
  117. package/dist/types.mjs.map +1 -1
  118. package/package.json +2 -2
  119. package/dist/data-sources/initDataSources.cjs +0 -215
  120. package/dist/data-sources/initDataSources.cjs.map +0 -1
  121. package/dist/data-sources/initDataSources.d.cts +0 -140
  122. package/dist/data-sources/initDataSources.d.cts.map +0 -1
  123. package/dist/data-sources/initDataSources.d.mts +0 -140
  124. package/dist/data-sources/initDataSources.d.mts.map +0 -1
  125. package/dist/data-sources/initDataSources.mjs +0 -210
  126. package/dist/data-sources/initDataSources.mjs.map +0 -1
@@ -1,19 +1,19 @@
1
1
  "use strict";
2
+ var __classPrivateFieldGet = (this && this.__classPrivateFieldGet) || function (receiver, state, kind, f) {
3
+ if (kind === "a" && !f) throw new TypeError("Private accessor was defined without a getter");
4
+ 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");
5
+ return kind === "m" ? f : kind === "a" ? f.call(receiver) : f ? f.value : state.get(receiver);
6
+ };
2
7
  var __classPrivateFieldSet = (this && this.__classPrivateFieldSet) || function (receiver, state, value, kind, f) {
3
8
  if (kind === "m") throw new TypeError("Private method is not writable");
4
9
  if (kind === "a" && !f) throw new TypeError("Private accessor was defined without a setter");
5
10
  if (typeof state === "function" ? receiver !== state || !f : !state.has(receiver)) throw new TypeError("Cannot write private member to an object whose class did not declare it");
6
11
  return (kind === "a" ? f.call(receiver, value) : f ? f.value = value : state.set(receiver, value)), value;
7
12
  };
8
- var __classPrivateFieldGet = (this && this.__classPrivateFieldGet) || function (receiver, state, kind, f) {
9
- if (kind === "a" && !f) throw new TypeError("Private accessor was defined without a getter");
10
- 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");
11
- return kind === "m" ? f : kind === "a" ? f.call(receiver) : f ? f.value : state.get(receiver);
12
- };
13
13
  var __importDefault = (this && this.__importDefault) || function (mod) {
14
14
  return (mod && mod.__esModule) ? mod : { "default": mod };
15
15
  };
16
- 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;
16
+ var _AssetsController_instances, _AssetsController_isEnabled, _AssetsController_defaultUpdateInterval, _AssetsController_controllerMutex, _AssetsController_activeSubscriptions, _AssetsController_enabledChains, _AssetsController_selectedAccounts_get, _AssetsController_backendWebsocketDataSource, _AssetsController_accountsApiDataSource, _AssetsController_snapDataSource, _AssetsController_rpcDataSource, _AssetsController_subscriptionBalanceDataSources_get, _AssetsController_priceDataSource, _AssetsController_detectionMiddleware, _AssetsController_tokenDataSource, _AssetsController_initializeState, _AssetsController_extractEnabledChains, _AssetsController_normalizeChainReference, _AssetsController_subscribeToEvents, _AssetsController_registerActionHandlers, _AssetsController_executeMiddlewares, _AssetsController_updateState, _AssetsController_getAssetsFromState, _AssetsController_tokenStandardToAssetType, _AssetsController_start, _AssetsController_stop, _AssetsController_subscribeAssets, _AssetsController_subscribeAssetsBalance, _AssetsController_buildChainToAccountsMap, _AssetsController_subscribeDataSource, _AssetsController_unsubscribeDataSource, _AssetsController_buildDataRequest, _AssetsController_getEnabledChainsForAccount, _AssetsController_handleAccountGroupChanged, _AssetsController_handleEnabledNetworksChanged;
17
17
  Object.defineProperty(exports, "__esModule", { value: true });
18
18
  exports.AssetsController = exports.getDefaultAssetsControllerState = void 0;
19
19
  const base_controller_1 = require("@metamask/base-controller");
@@ -21,12 +21,31 @@ const utils_1 = require("@metamask/utils");
21
21
  const async_mutex_1 = require("async-mutex");
22
22
  const bignumber_js_1 = __importDefault(require("bignumber.js"));
23
23
  const lodash_1 = require("lodash");
24
+ const AccountsApiDataSource_1 = require("./data-sources/AccountsApiDataSource.cjs");
25
+ const BackendWebsocketDataSource_1 = require("./data-sources/BackendWebsocketDataSource.cjs");
26
+ const PriceDataSource_1 = require("./data-sources/PriceDataSource.cjs");
27
+ const RpcDataSource_1 = require("./data-sources/RpcDataSource.cjs");
28
+ const SnapDataSource_1 = require("./data-sources/SnapDataSource.cjs");
29
+ const TokenDataSource_1 = require("./data-sources/TokenDataSource.cjs");
24
30
  const logger_1 = require("./logger.cjs");
31
+ const DetectionMiddleware_1 = require("./middlewares/DetectionMiddleware.cjs");
25
32
  const utils_2 = require("./utils.cjs");
26
33
  // ============================================================================
27
34
  // CONTROLLER CONSTANTS
28
35
  // ============================================================================
29
36
  const CONTROLLER_NAME = 'AssetsController';
37
+ /** Method names exposed as messenger actions (AssetsController:getAssets, etc.) */
38
+ const MESSENGER_EXPOSED_METHODS = [
39
+ 'getAssets',
40
+ 'getAssetsBalance',
41
+ 'getAssetMetadata',
42
+ 'getAssetsPrice',
43
+ 'addCustomAsset',
44
+ 'removeCustomAsset',
45
+ 'getCustomAssets',
46
+ 'hideAsset',
47
+ 'unhideAsset',
48
+ ];
30
49
  /** Default polling interval hint for data sources (30 seconds) */
31
50
  const DEFAULT_POLLING_INTERVAL_MS = 30000;
32
51
  const log = (0, logger_1.createModuleLogger)(logger_1.projectLogger, CONTROLLER_NAME);
@@ -153,13 +172,8 @@ function normalizeResponse(response) {
153
172
  * based on which chains they support. When active chains change, the controller
154
173
  * dynamically adjusts subscriptions.
155
174
  *
156
- * 4. **App Lifecycle Management**: Listens to app open/close events via messenger
157
- * to start/stop subscriptions automatically, conserving resources when app is closed.
158
- *
159
- * ## App Lifecycle
160
- *
161
- * - **App Opened** (`AppStateController:appOpened`): Starts subscriptions, fetches initial data
162
- * - **App Closed** (`AppStateController:appClosed`): Stops all subscriptions to conserve resources
175
+ * 4. **Keyring Lifecycle**: Listens to KeyringController unlock/lock events to
176
+ * start/stop subscriptions when the wallet is unlocked or locked.
163
177
  *
164
178
  * ## Architecture
165
179
  *
@@ -168,7 +182,7 @@ function normalizeResponse(response) {
168
182
  * - The controller does NOT manage polling - it simply receives pushed updates
169
183
  */
170
184
  class AssetsController extends base_controller_1.BaseController {
171
- constructor({ messenger, state = {}, defaultUpdateInterval = DEFAULT_POLLING_INTERVAL_MS, isEnabled = () => true, }) {
185
+ constructor({ messenger, state = {}, defaultUpdateInterval = DEFAULT_POLLING_INTERVAL_MS, isEnabled = () => true, queryApiClient, rpcDataSourceConfig, }) {
172
186
  super({
173
187
  name: CONTROLLER_NAME,
174
188
  messenger,
@@ -193,79 +207,82 @@ class AssetsController extends base_controller_1.BaseController {
193
207
  _AssetsController_activeSubscriptions.set(this, new Map());
194
208
  /** Currently enabled chains from NetworkEnablementController */
195
209
  _AssetsController_enabledChains.set(this, new Set());
196
- /**
197
- * Registered data sources with their available chains.
198
- * Updated continuously and independently from subscription flows.
199
- * Key: sourceId, Value: Set of currently available chainIds
200
- */
201
- _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);
202
217
  __classPrivateFieldSet(this, _AssetsController_isEnabled, isEnabled(), "f");
203
218
  __classPrivateFieldSet(this, _AssetsController_defaultUpdateInterval, defaultUpdateInterval, "f");
219
+ const rpcConfig = rpcDataSourceConfig ?? {};
220
+ const onActiveChainsUpdated = (dataSourceName, chains, previousChains) => this.handleActiveChainsUpdate(dataSourceName, chains, previousChains);
221
+ __classPrivateFieldSet(this, _AssetsController_backendWebsocketDataSource, new BackendWebsocketDataSource_1.BackendWebsocketDataSource({
222
+ messenger: this.messenger,
223
+ queryApiClient,
224
+ onActiveChainsUpdated,
225
+ }), "f");
226
+ __classPrivateFieldSet(this, _AssetsController_accountsApiDataSource, new AccountsApiDataSource_1.AccountsApiDataSource({
227
+ queryApiClient,
228
+ onActiveChainsUpdated,
229
+ }), "f");
230
+ __classPrivateFieldSet(this, _AssetsController_snapDataSource, new SnapDataSource_1.SnapDataSource({
231
+ messenger: this.messenger,
232
+ onActiveChainsUpdated,
233
+ }), "f");
234
+ __classPrivateFieldSet(this, _AssetsController_rpcDataSource, new RpcDataSource_1.RpcDataSource({
235
+ messenger: this.messenger,
236
+ onActiveChainsUpdated,
237
+ ...rpcConfig,
238
+ }), "f");
239
+ __classPrivateFieldSet(this, _AssetsController_tokenDataSource, new TokenDataSource_1.TokenDataSource({
240
+ queryApiClient,
241
+ }), "f");
242
+ __classPrivateFieldSet(this, _AssetsController_priceDataSource, new PriceDataSource_1.PriceDataSource({
243
+ queryApiClient,
244
+ }), "f");
245
+ __classPrivateFieldSet(this, _AssetsController_detectionMiddleware, new DetectionMiddleware_1.DetectionMiddleware(), "f");
204
246
  if (!__classPrivateFieldGet(this, _AssetsController_isEnabled, "f")) {
205
247
  log('AssetsController is disabled, skipping initialization');
206
248
  return;
207
249
  }
208
250
  log('Initializing AssetsController', {
209
- defaultUpdateInterval,
251
+ defaultUpdateInterval: __classPrivateFieldGet(this, _AssetsController_defaultUpdateInterval, "f"),
210
252
  });
211
253
  __classPrivateFieldGet(this, _AssetsController_instances, "m", _AssetsController_initializeState).call(this);
212
254
  __classPrivateFieldGet(this, _AssetsController_instances, "m", _AssetsController_subscribeToEvents).call(this);
213
255
  __classPrivateFieldGet(this, _AssetsController_instances, "m", _AssetsController_registerActionHandlers).call(this);
214
- // Register data sources (order = subscription priority)
215
- this.registerDataSources([
216
- 'BackendWebsocketDataSource', // Real-time push updates
217
- 'AccountsApiDataSource', // HTTP polling fallback
218
- 'SnapDataSource', // Solana/Bitcoin/Tron snaps
219
- 'RpcDataSource', // Direct blockchain queries
220
- ]);
221
- }
222
- // ============================================================================
223
- // DATA SOURCE MANAGEMENT
224
- // ============================================================================
225
- /**
226
- * Register data sources with the controller.
227
- * Order of the array determines subscription order.
228
- *
229
- * Data sources report chain changes by calling `AssetsController:activeChainsUpdate` action.
230
- *
231
- * @param dataSourceIds - Array of data source identifiers to register.
232
- */
233
- registerDataSources(dataSourceIds) {
234
- for (const id of dataSourceIds) {
235
- log('Registering data source', { id });
236
- // Initialize available chains tracking for this source
237
- __classPrivateFieldGet(this, _AssetsController_dataSources, "f").set(id, new Set());
238
- }
239
256
  }
240
257
  // ============================================================================
241
258
  // DATA SOURCE CHAIN MANAGEMENT
242
259
  // ============================================================================
243
260
  /**
244
- * Handle when a data source's active chains change.
245
- * Active chains are chains that are both supported AND available.
246
- * Updates centralized chain tracking and triggers re-selection if needed.
261
+ * Handle when a data source's supported chains change.
262
+ * Used to refresh balance subscriptions and run a one-time fetch when a new chain is supported.
263
+ *
264
+ * - On any add/remove: re-subscribes to data sources so chain assignment stays correct.
265
+ * - When chains are added: fetches balances for the new chains (for selected accounts on enabled networks).
247
266
  *
248
- * Data sources should call this via `AssetsController:activeChainsUpdate` action.
267
+ * Controller does not store chains; sources report via this callback. previousChains is required for diff.
249
268
  *
250
269
  * @param dataSourceId - The identifier of the data source reporting the change.
251
- * @param activeChains - Array of currently active chain IDs for this source.
270
+ * @param activeChains - Currently active (supported and available) chain IDs for this source.
271
+ * @param previousChains - Previous chains; used to compute added/removed.
252
272
  */
253
- handleActiveChainsUpdate(dataSourceId, activeChains) {
273
+ handleActiveChainsUpdate(dataSourceId, activeChains, previousChains) {
254
274
  log('Data source active chains changed', {
255
275
  dataSourceId,
256
276
  chainCount: activeChains.length,
257
277
  chains: activeChains,
258
278
  });
259
- const previousChains = __classPrivateFieldGet(this, _AssetsController_dataSources, "f").get(dataSourceId) ?? new Set();
260
- const newChains = new Set(activeChains);
261
- // Update centralized available chains tracking
262
- __classPrivateFieldGet(this, _AssetsController_dataSources, "f").set(dataSourceId, newChains);
263
- // Check for changes
264
- const addedChains = activeChains.filter((chain) => !previousChains.has(chain));
265
- const removedChains = Array.from(previousChains).filter((chain) => !newChains.has(chain));
279
+ const previous = previousChains;
280
+ const previousSet = new Set(previous);
281
+ const addedChains = activeChains.filter((ch) => !previousSet.has(ch));
282
+ const removedChains = previous.filter((ch) => !activeChains.includes(ch));
266
283
  if (addedChains.length > 0 || removedChains.length > 0) {
267
284
  // Refresh subscriptions to use updated data source availability
268
- __classPrivateFieldGet(this, _AssetsController_instances, "m", _AssetsController_subscribeToDataSources).call(this);
285
+ __classPrivateFieldGet(this, _AssetsController_instances, "m", _AssetsController_subscribeAssets).call(this);
269
286
  }
270
287
  // If chains were added and we have selected accounts, do one-time fetch
271
288
  if (addedChains.length > 0 && __classPrivateFieldGet(this, _AssetsController_instances, "a", _AssetsController_selectedAccounts_get).length > 0) {
@@ -295,21 +312,20 @@ class AssetsController extends base_controller_1.BaseController {
295
312
  customAssets.push(...accountCustomAssets);
296
313
  }
297
314
  if (options?.forceUpdate) {
298
- const response = await __classPrivateFieldGet(this, _AssetsController_instances, "m", _AssetsController_executeMiddlewares).call(this, [
299
- this.messenger.call('AccountsApiDataSource:getAssetsMiddleware'),
300
- this.messenger.call('SnapDataSource:getAssetsMiddleware'),
301
- this.messenger.call('RpcDataSource:getAssetsMiddleware'),
302
- this.messenger.call('DetectionMiddleware:getAssetsMiddleware'),
303
- this.messenger.call('TokenDataSource:getAssetsMiddleware'),
304
- this.messenger.call('PriceDataSource:getAssetsMiddleware'),
305
- ], {
306
- accounts,
307
- chainIds,
315
+ const request = __classPrivateFieldGet(this, _AssetsController_instances, "m", _AssetsController_buildDataRequest).call(this, accounts, chainIds, {
308
316
  assetTypes,
309
317
  dataTypes,
310
318
  customAssets: customAssets.length > 0 ? customAssets : undefined,
311
319
  forceUpdate: true,
312
320
  });
321
+ const response = await __classPrivateFieldGet(this, _AssetsController_instances, "m", _AssetsController_executeMiddlewares).call(this, [
322
+ __classPrivateFieldGet(this, _AssetsController_accountsApiDataSource, "f").assetsMiddleware,
323
+ __classPrivateFieldGet(this, _AssetsController_snapDataSource, "f").assetsMiddleware,
324
+ __classPrivateFieldGet(this, _AssetsController_rpcDataSource, "f").assetsMiddleware,
325
+ __classPrivateFieldGet(this, _AssetsController_detectionMiddleware, "f").assetsMiddleware,
326
+ __classPrivateFieldGet(this, _AssetsController_tokenDataSource, "f").assetsMiddleware,
327
+ __classPrivateFieldGet(this, _AssetsController_priceDataSource, "f").assetsMiddleware,
328
+ ], request);
313
329
  await __classPrivateFieldGet(this, _AssetsController_instances, "m", _AssetsController_updateState).call(this, response);
314
330
  }
315
331
  return __classPrivateFieldGet(this, _AssetsController_instances, "m", _AssetsController_getAssetsFromState).call(this, accounts, chainIds, assetTypes);
@@ -463,6 +479,9 @@ class AssetsController extends base_controller_1.BaseController {
463
479
  }
464
480
  });
465
481
  }
482
+ // ============================================================================
483
+ // SUBSCRIPTIONS
484
+ // ============================================================================
466
485
  /**
467
486
  * Subscribe to price updates for all assets held by the given accounts.
468
487
  * Polls PriceDataSource which fetches prices from balance state.
@@ -477,19 +496,17 @@ class AssetsController extends base_controller_1.BaseController {
477
496
  const subscriptionKey = 'ds:PriceDataSource';
478
497
  const existingSubscription = __classPrivateFieldGet(this, _AssetsController_activeSubscriptions, "f").get(subscriptionKey);
479
498
  const isUpdate = existingSubscription !== undefined;
480
- // Fire-and-forget - errors are handled internally by PriceDataSource
481
- this.messenger
482
- .call('PriceDataSource:subscribe', {
483
- request: {
484
- accounts,
485
- chainIds,
499
+ const subscribeReq = {
500
+ request: __classPrivateFieldGet(this, _AssetsController_instances, "m", _AssetsController_buildDataRequest).call(this, accounts, chainIds, {
486
501
  dataTypes: ['price'],
487
502
  updateInterval,
488
- },
503
+ }),
489
504
  subscriptionId: subscriptionKey,
490
505
  isUpdate,
491
- })
492
- .catch(console.error);
506
+ onAssetsUpdate: (response) => this.handleAssetsUpdate(response, 'PriceDataSource'),
507
+ getAssetsState: () => this.state,
508
+ };
509
+ __classPrivateFieldGet(this, _AssetsController_priceDataSource, "f").subscribe(subscribeReq).catch(console.error);
493
510
  // Track subscription
494
511
  const subscription = {
495
512
  chains: chainIds,
@@ -511,37 +528,55 @@ class AssetsController extends base_controller_1.BaseController {
511
528
  if (!existingSubscription) {
512
529
  return;
513
530
  }
514
- // Fire-and-forget - errors are handled internally by PriceDataSource
515
- this.messenger
516
- .call('PriceDataSource:unsubscribe', subscriptionKey)
517
- .catch(console.error);
531
+ __classPrivateFieldGet(this, _AssetsController_priceDataSource, "f").unsubscribe(subscriptionKey).catch(console.error);
518
532
  existingSubscription.unsubscribe();
519
533
  }
520
534
  /**
521
535
  * Handle assets updated from a data source.
522
- * Called via `AssetsController:assetsUpdate` action by data sources.
536
+ * Called via the onAssetsUpdate callback passed in SubscriptionRequest when the controller subscribes to a data source.
537
+ * Enriches the response with token metadata (via middlewares) before updating state.
523
538
  *
524
539
  * @param response - The data response with updated assets
525
540
  * @param sourceId - The data source ID reporting the update
541
+ * @param request - Optional original request for context when enriching
526
542
  */
527
- async handleAssetsUpdate(response, sourceId) {
543
+ async handleAssetsUpdate(response, sourceId, request) {
528
544
  log('Assets updated from data source', {
529
545
  sourceId,
530
546
  hasBalance: Boolean(response.assetsBalance),
531
547
  hasPrice: Boolean(response.assetsPrice),
532
548
  });
533
- await __classPrivateFieldGet(this, _AssetsController_instances, "m", _AssetsController_handleSubscriptionUpdate).call(this, response, sourceId);
549
+ // Run through enrichment middlewares (Event Stack: Detection → Token → Price)
550
+ // Include 'metadata' in dataTypes so TokenDataSource runs to enrich detected assets
551
+ const enrichedResponse = await __classPrivateFieldGet(this, _AssetsController_instances, "m", _AssetsController_executeMiddlewares).call(this, [
552
+ __classPrivateFieldGet(this, _AssetsController_detectionMiddleware, "f").assetsMiddleware,
553
+ __classPrivateFieldGet(this, _AssetsController_tokenDataSource, "f").assetsMiddleware,
554
+ __classPrivateFieldGet(this, _AssetsController_priceDataSource, "f").assetsMiddleware,
555
+ ], request ?? {
556
+ accountsWithSupportedChains: [],
557
+ chainIds: [],
558
+ dataTypes: ['balance', 'metadata', 'price'],
559
+ }, response);
560
+ await __classPrivateFieldGet(this, _AssetsController_instances, "m", _AssetsController_updateState).call(this, enrichedResponse);
534
561
  }
535
562
  // ============================================================================
536
563
  // CLEANUP
537
564
  // ============================================================================
538
565
  destroy() {
539
566
  log('Destroying AssetsController', {
540
- dataSourceCount: __classPrivateFieldGet(this, _AssetsController_dataSources, "f").size,
567
+ dataSourceCount: __classPrivateFieldGet(this, _AssetsController_instances, "a", _AssetsController_subscriptionBalanceDataSources_get).length,
541
568
  subscriptionCount: __classPrivateFieldGet(this, _AssetsController_activeSubscriptions, "f").size,
542
569
  });
543
- // Clear data sources
544
- __classPrivateFieldGet(this, _AssetsController_dataSources, "f").clear();
570
+ // Destroy instantiated data sources
571
+ __classPrivateFieldGet(this, _AssetsController_backendWebsocketDataSource, "f")?.destroy?.();
572
+ __classPrivateFieldGet(this, _AssetsController_accountsApiDataSource, "f")?.destroy?.();
573
+ __classPrivateFieldGet(this, _AssetsController_snapDataSource, "f")?.destroy?.();
574
+ if (__classPrivateFieldGet(this, _AssetsController_rpcDataSource, "f") &&
575
+ 'destroy' in __classPrivateFieldGet(this, _AssetsController_rpcDataSource, "f") &&
576
+ typeof __classPrivateFieldGet(this, _AssetsController_rpcDataSource, "f").destroy ===
577
+ 'function') {
578
+ __classPrivateFieldGet(this, _AssetsController_rpcDataSource, "f").destroy();
579
+ }
545
580
  // Stop all active subscriptions
546
581
  __classPrivateFieldGet(this, _AssetsController_instances, "m", _AssetsController_stop).call(this);
547
582
  // Unregister action handlers
@@ -549,8 +584,6 @@ class AssetsController extends base_controller_1.BaseController {
549
584
  this.messenger.unregisterActionHandler('AssetsController:getAssetsBalance');
550
585
  this.messenger.unregisterActionHandler('AssetsController:getAssetMetadata');
551
586
  this.messenger.unregisterActionHandler('AssetsController:getAssetsPrice');
552
- this.messenger.unregisterActionHandler('AssetsController:activeChainsUpdate');
553
- this.messenger.unregisterActionHandler('AssetsController:assetsUpdate');
554
587
  this.messenger.unregisterActionHandler('AssetsController:addCustomAsset');
555
588
  this.messenger.unregisterActionHandler('AssetsController:removeCustomAsset');
556
589
  this.messenger.unregisterActionHandler('AssetsController:getCustomAssets');
@@ -559,8 +592,15 @@ class AssetsController extends base_controller_1.BaseController {
559
592
  }
560
593
  }
561
594
  exports.AssetsController = AssetsController;
562
- _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() {
595
+ _AssetsController_isEnabled = new WeakMap(), _AssetsController_defaultUpdateInterval = new WeakMap(), _AssetsController_controllerMutex = new WeakMap(), _AssetsController_activeSubscriptions = new WeakMap(), _AssetsController_enabledChains = 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() {
563
596
  return this.messenger.call('AccountTreeController:getAccountsFromSelectedAccountGroup');
597
+ }, _AssetsController_subscriptionBalanceDataSources_get = function _AssetsController_subscriptionBalanceDataSources_get() {
598
+ return [
599
+ __classPrivateFieldGet(this, _AssetsController_backendWebsocketDataSource, "f"),
600
+ __classPrivateFieldGet(this, _AssetsController_accountsApiDataSource, "f"),
601
+ __classPrivateFieldGet(this, _AssetsController_snapDataSource, "f"),
602
+ __classPrivateFieldGet(this, _AssetsController_rpcDataSource, "f"),
603
+ ];
564
604
  }, _AssetsController_initializeState = function _AssetsController_initializeState() {
565
605
  const { enabledNetworkMap } = this.messenger.call('NetworkEnablementController:getState');
566
606
  __classPrivateFieldSet(this, _AssetsController_enabledChains, __classPrivateFieldGet(this, _AssetsController_instances, "m", _AssetsController_extractEnabledChains).call(this, enabledNetworkMap), "f");
@@ -602,24 +642,11 @@ _AssetsController_isEnabled = new WeakMap(), _AssetsController_defaultUpdateInte
602
642
  this.messenger.subscribe('NetworkEnablementController:stateChange', ({ enabledNetworkMap }) => {
603
643
  __classPrivateFieldGet(this, _AssetsController_instances, "m", _AssetsController_handleEnabledNetworksChanged).call(this, enabledNetworkMap).catch(console.error);
604
644
  });
605
- // App lifecycle: start when opened, stop when closed
606
- this.messenger.subscribe('AppStateController:appOpened', () => __classPrivateFieldGet(this, _AssetsController_instances, "m", _AssetsController_start).call(this));
607
- this.messenger.subscribe('AppStateController:appClosed', () => __classPrivateFieldGet(this, _AssetsController_instances, "m", _AssetsController_stop).call(this));
608
645
  // Keyring lifecycle: start when unlocked, stop when locked
609
646
  this.messenger.subscribe('KeyringController:unlock', () => __classPrivateFieldGet(this, _AssetsController_instances, "m", _AssetsController_start).call(this));
610
647
  this.messenger.subscribe('KeyringController:lock', () => __classPrivateFieldGet(this, _AssetsController_instances, "m", _AssetsController_stop).call(this));
611
648
  }, _AssetsController_registerActionHandlers = function _AssetsController_registerActionHandlers() {
612
- this.messenger.registerActionHandler('AssetsController:getAssets', this.getAssets.bind(this));
613
- this.messenger.registerActionHandler('AssetsController:getAssetsBalance', this.getAssetsBalance.bind(this));
614
- this.messenger.registerActionHandler('AssetsController:getAssetMetadata', this.getAssetMetadata.bind(this));
615
- this.messenger.registerActionHandler('AssetsController:getAssetsPrice', this.getAssetsPrice.bind(this));
616
- this.messenger.registerActionHandler('AssetsController:activeChainsUpdate', this.handleActiveChainsUpdate.bind(this));
617
- this.messenger.registerActionHandler('AssetsController:assetsUpdate', this.handleAssetsUpdate.bind(this));
618
- this.messenger.registerActionHandler('AssetsController:addCustomAsset', this.addCustomAsset.bind(this));
619
- this.messenger.registerActionHandler('AssetsController:removeCustomAsset', this.removeCustomAsset.bind(this));
620
- this.messenger.registerActionHandler('AssetsController:getCustomAssets', this.getCustomAssets.bind(this));
621
- this.messenger.registerActionHandler('AssetsController:hideAsset', this.hideAsset.bind(this));
622
- this.messenger.registerActionHandler('AssetsController:unhideAsset', this.unhideAsset.bind(this));
649
+ this.messenger.registerMethodActionHandlers(this, MESSENGER_EXPOSED_METHODS);
623
650
  }, _AssetsController_executeMiddlewares =
624
651
  // ============================================================================
625
652
  // MIDDLEWARE EXECUTION
@@ -648,32 +675,6 @@ async function _AssetsController_executeMiddlewares(middlewares, request, initia
648
675
  getAssetsState: () => this.state,
649
676
  });
650
677
  return result.response;
651
- }, _AssetsController_assignChainsToDataSources = function _AssetsController_assignChainsToDataSources(requestedChains) {
652
- const assignment = new Map();
653
- const remainingChains = new Set(requestedChains);
654
- for (const sourceId of __classPrivateFieldGet(this, _AssetsController_dataSources, "f").keys()) {
655
- // Get available chains for this data source
656
- const availableChains = __classPrivateFieldGet(this, _AssetsController_dataSources, "f").get(sourceId);
657
- if (!availableChains || availableChains.size === 0) {
658
- continue;
659
- }
660
- const chainsForThisSource = [];
661
- for (const chainId of remainingChains) {
662
- // Check if this chain is available on this source
663
- if (availableChains.has(chainId)) {
664
- chainsForThisSource.push(chainId);
665
- remainingChains.delete(chainId);
666
- }
667
- }
668
- if (chainsForThisSource.length > 0) {
669
- assignment.set(sourceId, chainsForThisSource);
670
- log('Assigned chains to data source', {
671
- sourceId,
672
- chains: chainsForThisSource,
673
- });
674
- }
675
- }
676
- return assignment;
677
678
  }, _AssetsController_updateState =
678
679
  // ============================================================================
679
680
  // STATE MANAGEMENT
@@ -867,7 +868,7 @@ async function _AssetsController_updateState(response) {
867
868
  selectedAccountCount: __classPrivateFieldGet(this, _AssetsController_instances, "a", _AssetsController_selectedAccounts_get).length,
868
869
  enabledChainCount: __classPrivateFieldGet(this, _AssetsController_enabledChains, "f").size,
869
870
  });
870
- __classPrivateFieldGet(this, _AssetsController_instances, "m", _AssetsController_subscribeToDataSources).call(this);
871
+ __classPrivateFieldGet(this, _AssetsController_instances, "m", _AssetsController_subscribeAssets).call(this);
871
872
  if (__classPrivateFieldGet(this, _AssetsController_instances, "a", _AssetsController_selectedAccounts_get).length > 0) {
872
873
  this.getAssets(__classPrivateFieldGet(this, _AssetsController_instances, "a", _AssetsController_selectedAccounts_get), {
873
874
  chainIds: [...__classPrivateFieldGet(this, _AssetsController_enabledChains, "f")],
@@ -888,77 +889,67 @@ async function _AssetsController_updateState(response) {
888
889
  // Convert to array first to avoid modifying map during iteration
889
890
  const subscriptionKeys = [...__classPrivateFieldGet(this, _AssetsController_activeSubscriptions, "f").keys()];
890
891
  for (const subscriptionKey of subscriptionKeys) {
891
- // Extract sourceId from subscription key (format: "ds:${sourceId}")
892
892
  if (subscriptionKey.startsWith('ds:')) {
893
893
  const sourceId = subscriptionKey.slice(3);
894
- __classPrivateFieldGet(this, _AssetsController_instances, "m", _AssetsController_unsubscribeDataSource).call(this, sourceId);
894
+ const source = __classPrivateFieldGet(this, _AssetsController_instances, "a", _AssetsController_subscriptionBalanceDataSources_get).find((ds) => ds.getName() === sourceId);
895
+ if (source) {
896
+ __classPrivateFieldGet(this, _AssetsController_instances, "m", _AssetsController_unsubscribeDataSource).call(this, source);
897
+ }
895
898
  }
896
899
  }
897
900
  __classPrivateFieldGet(this, _AssetsController_activeSubscriptions, "f").clear();
898
- }, _AssetsController_subscribeToDataSources = function _AssetsController_subscribeToDataSources() {
901
+ }, _AssetsController_subscribeAssets = function _AssetsController_subscribeAssets() {
899
902
  if (__classPrivateFieldGet(this, _AssetsController_instances, "a", _AssetsController_selectedAccounts_get).length === 0) {
900
903
  return;
901
904
  }
902
905
  // Subscribe to balance updates (batched by data source)
903
- __classPrivateFieldGet(this, _AssetsController_instances, "m", _AssetsController_subscribeAssetsBalance).call(this);
906
+ __classPrivateFieldGet(this, _AssetsController_instances, "m", _AssetsController_subscribeAssetsBalance).call(this, __classPrivateFieldGet(this, _AssetsController_instances, "a", _AssetsController_selectedAccounts_get), [
907
+ ...__classPrivateFieldGet(this, _AssetsController_enabledChains, "f"),
908
+ ]);
904
909
  // Subscribe to price updates for all assets held by selected accounts
905
910
  this.subscribeAssetsPrice(__classPrivateFieldGet(this, _AssetsController_instances, "a", _AssetsController_selectedAccounts_get), [...__classPrivateFieldGet(this, _AssetsController_enabledChains, "f")]);
906
- }, _AssetsController_subscribeAssetsBalance = function _AssetsController_subscribeAssetsBalance() {
907
- // Step 1: Build chain -> accounts mapping based on account scopes and enabled networks
908
- const chainToAccounts = __classPrivateFieldGet(this, _AssetsController_instances, "m", _AssetsController_buildChainToAccountsMap).call(this, __classPrivateFieldGet(this, _AssetsController_instances, "a", _AssetsController_selectedAccounts_get), __classPrivateFieldGet(this, _AssetsController_enabledChains, "f"));
909
- // Step 2: Split by data source active chains (ordered by priority)
910
- // Get all chains that need to be subscribed
911
+ }, _AssetsController_subscribeAssetsBalance = function _AssetsController_subscribeAssetsBalance(accounts, chainIds) {
912
+ const chainToAccounts = __classPrivateFieldGet(this, _AssetsController_instances, "m", _AssetsController_buildChainToAccountsMap).call(this, accounts, new Set(chainIds));
911
913
  const remainingChains = new Set(chainToAccounts.keys());
912
- // Assign chains to data sources based on availability (ordered by priority)
913
- const chainAssignment = __classPrivateFieldGet(this, _AssetsController_instances, "m", _AssetsController_assignChainsToDataSources).call(this, Array.from(remainingChains));
914
- log('Subscribe - chain assignment', {
915
- totalChains: remainingChains.size,
916
- dataSourceAssignments: Array.from(chainAssignment.entries()).map(([sourceId, chains]) => ({ sourceId, chainCount: chains.length })),
917
- });
918
- // Subscribe to each data source with its assigned chains and relevant accounts
919
- for (const sourceId of __classPrivateFieldGet(this, _AssetsController_dataSources, "f").keys()) {
920
- const assignedChains = chainAssignment.get(sourceId);
921
- if (!assignedChains || assignedChains.length === 0) {
922
- // Unsubscribe from data sources with no assigned chains
923
- __classPrivateFieldGet(this, _AssetsController_instances, "m", _AssetsController_unsubscribeDataSource).call(this, sourceId);
924
- continue;
914
+ for (const source of __classPrivateFieldGet(this, _AssetsController_instances, "a", _AssetsController_subscriptionBalanceDataSources_get)) {
915
+ const availableChains = new Set(source.getActiveChainsSync());
916
+ const assignedChains = [];
917
+ for (const chainId of remainingChains) {
918
+ if (availableChains.has(chainId)) {
919
+ assignedChains.push(chainId);
920
+ remainingChains.delete(chainId);
921
+ }
925
922
  }
926
- // Collect unique accounts that need any of the assigned chains
927
- const accountsForSource = __classPrivateFieldGet(this, _AssetsController_instances, "m", _AssetsController_getAccountsForChains).call(this, assignedChains, chainToAccounts);
928
- if (accountsForSource.length === 0) {
923
+ if (assignedChains.length === 0) {
924
+ __classPrivateFieldGet(this, _AssetsController_instances, "m", _AssetsController_unsubscribeDataSource).call(this, source);
929
925
  continue;
930
926
  }
931
- // Subscribe with ONE call per data source
932
- __classPrivateFieldGet(this, _AssetsController_instances, "m", _AssetsController_subscribeToDataSource).call(this, sourceId, accountsForSource, assignedChains);
927
+ const seenIds = new Set();
928
+ const accountsForSource = assignedChains
929
+ .flatMap((chainId) => chainToAccounts.get(chainId) ?? [])
930
+ .filter((account) => !seenIds.has(account.id) && (seenIds.add(account.id), true));
931
+ if (accountsForSource.length > 0) {
932
+ __classPrivateFieldGet(this, _AssetsController_instances, "m", _AssetsController_subscribeDataSource).call(this, source, accountsForSource, assignedChains);
933
+ }
933
934
  }
934
935
  }, _AssetsController_buildChainToAccountsMap = function _AssetsController_buildChainToAccountsMap(accounts, chainsToSubscribe) {
935
936
  const chainToAccounts = new Map();
936
937
  for (const account of accounts) {
937
- const accountChains = __classPrivateFieldGet(this, _AssetsController_instances, "m", _AssetsController_getEnabledChainsForAccount).call(this, account);
938
- for (const chainId of accountChains) {
938
+ for (const chainId of __classPrivateFieldGet(this, _AssetsController_instances, "m", _AssetsController_getEnabledChainsForAccount).call(this, account)) {
939
939
  if (!chainsToSubscribe.has(chainId)) {
940
940
  continue;
941
941
  }
942
- const existingAccounts = chainToAccounts.get(chainId) ?? [];
943
- existingAccounts.push(account);
944
- chainToAccounts.set(chainId, existingAccounts);
945
- }
946
- }
947
- return chainToAccounts;
948
- }, _AssetsController_getAccountsForChains = function _AssetsController_getAccountsForChains(chains, chainToAccounts) {
949
- const accountIds = new Set();
950
- const accounts = [];
951
- for (const chainId of chains) {
952
- const chainAccounts = chainToAccounts.get(chainId) ?? [];
953
- for (const account of chainAccounts) {
954
- if (!accountIds.has(account.id)) {
955
- accountIds.add(account.id);
956
- accounts.push(account);
942
+ let list = chainToAccounts.get(chainId);
943
+ if (!list) {
944
+ list = [];
945
+ chainToAccounts.set(chainId, list);
957
946
  }
947
+ list.push(account);
958
948
  }
959
949
  }
960
- return accounts;
961
- }, _AssetsController_subscribeToDataSource = function _AssetsController_subscribeToDataSource(sourceId, accounts, chains) {
950
+ return chainToAccounts;
951
+ }, _AssetsController_subscribeDataSource = function _AssetsController_subscribeDataSource(source, accounts, chains) {
952
+ const sourceId = source.getName();
962
953
  const subscriptionKey = `ds:${sourceId}`;
963
954
  const existingSubscription = __classPrivateFieldGet(this, _AssetsController_activeSubscriptions, "f").get(subscriptionKey);
964
955
  const isUpdate = existingSubscription !== undefined;
@@ -969,53 +960,49 @@ async function _AssetsController_updateState(response) {
969
960
  accountCount: accounts.length,
970
961
  chainCount: chains.length,
971
962
  });
972
- // Call data source subscribe action via Messenger (fire-and-forget)
973
- (async () => {
974
- try {
975
- await this.messenger.call(`${sourceId}:subscribe`, {
976
- request: {
977
- accounts,
978
- chainIds: chains,
979
- assetTypes: ['fungible'],
980
- dataTypes: ['balance'],
981
- updateInterval: __classPrivateFieldGet(this, _AssetsController_defaultUpdateInterval, "f"),
982
- },
983
- subscriptionId: subscriptionKey,
984
- isUpdate,
985
- });
986
- }
987
- catch (error) {
988
- console.error(`[AssetsController] Failed to subscribe to '${sourceId}':`, error);
989
- }
990
- })().catch(console.error);
963
+ const subscribeReq = {
964
+ request: __classPrivateFieldGet(this, _AssetsController_instances, "m", _AssetsController_buildDataRequest).call(this, accounts, chains, {
965
+ assetTypes: ['fungible'],
966
+ dataTypes: ['balance'],
967
+ updateInterval: __classPrivateFieldGet(this, _AssetsController_defaultUpdateInterval, "f"),
968
+ }),
969
+ subscriptionId: subscriptionKey,
970
+ isUpdate,
971
+ onAssetsUpdate: (response) => this.handleAssetsUpdate(response, sourceId),
972
+ getAssetsState: () => this.state,
973
+ };
974
+ source.subscribe(subscribeReq).catch((error) => {
975
+ console.error(`[AssetsController] Failed to subscribe to '${sourceId}':`, error);
976
+ });
991
977
  // Track subscription
992
978
  const subscription = {
993
979
  chains,
994
980
  accountId: subscriptionKey,
995
981
  assetTypes: ['fungible'],
996
- dataTypes: ['balance', 'price'],
982
+ dataTypes: ['balance'],
997
983
  unsubscribe: () => {
998
984
  __classPrivateFieldGet(this, _AssetsController_activeSubscriptions, "f").delete(subscriptionKey);
999
985
  },
1000
986
  };
1001
987
  __classPrivateFieldGet(this, _AssetsController_activeSubscriptions, "f").set(subscriptionKey, subscription);
1002
- }, _AssetsController_unsubscribeDataSource = function _AssetsController_unsubscribeDataSource(sourceId) {
1003
- const subscriptionKey = `ds:${sourceId}`;
988
+ }, _AssetsController_unsubscribeDataSource = function _AssetsController_unsubscribeDataSource(source) {
989
+ const subscriptionKey = `ds:${source.getName()}`;
1004
990
  const existingSubscription = __classPrivateFieldGet(this, _AssetsController_activeSubscriptions, "f").get(subscriptionKey);
1005
991
  if (existingSubscription) {
1006
- // Fire-and-forget unsubscribe call
1007
- (async () => {
1008
- try {
1009
- await this.messenger.call(`${sourceId}:unsubscribe`, subscriptionKey);
1010
- }
1011
- catch {
1012
- // Ignore errors - source may not have been subscribed
1013
- }
1014
- })().catch(() => {
1015
- // Ignore errors - source may not have been subscribed
1016
- });
992
+ source.unsubscribe(subscriptionKey).catch(() => undefined);
1017
993
  existingSubscription.unsubscribe();
1018
994
  }
995
+ }, _AssetsController_buildDataRequest = function _AssetsController_buildDataRequest(accounts, chainIds, partial = {}) {
996
+ const chainIdSet = new Set(chainIds);
997
+ const accountsWithSupportedChains = accounts.map((account) => ({
998
+ account,
999
+ supportedChains: __classPrivateFieldGet(this, _AssetsController_instances, "m", _AssetsController_getEnabledChainsForAccount).call(this, account).filter((chain) => chainIdSet.has(chain)),
1000
+ }));
1001
+ return {
1002
+ accountsWithSupportedChains,
1003
+ chainIds,
1004
+ ...partial,
1005
+ };
1019
1006
  }, _AssetsController_getEnabledChainsForAccount = function _AssetsController_getEnabledChainsForAccount(account) {
1020
1007
  // Account scopes are CAIP-2 chain IDs like "eip155:1", "solana:mainnet", "bip122:..."
1021
1008
  const scopes = account.scopes ?? [];
@@ -1050,7 +1037,7 @@ async function _AssetsController_handleAccountGroupChanged() {
1050
1037
  accountIds: accounts.map((a) => a.id),
1051
1038
  });
1052
1039
  // Subscribe and fetch for the new account group
1053
- __classPrivateFieldGet(this, _AssetsController_instances, "m", _AssetsController_subscribeToDataSources).call(this);
1040
+ __classPrivateFieldGet(this, _AssetsController_instances, "m", _AssetsController_subscribeAssets).call(this);
1054
1041
  if (accounts.length > 0) {
1055
1042
  await this.getAssets(accounts, {
1056
1043
  chainIds: [...__classPrivateFieldGet(this, _AssetsController_enabledChains, "f")],
@@ -1084,7 +1071,7 @@ async function _AssetsController_handleAccountGroupChanged() {
1084
1071
  // Users may want to see historical balances even if the network is currently disabled.
1085
1072
  // The data will simply not be updated until the network is re-enabled.
1086
1073
  // Refresh subscriptions for new chain set
1087
- __classPrivateFieldGet(this, _AssetsController_instances, "m", _AssetsController_subscribeToDataSources).call(this);
1074
+ __classPrivateFieldGet(this, _AssetsController_instances, "m", _AssetsController_subscribeAssets).call(this);
1088
1075
  // Do one-time fetch for newly enabled chains
1089
1076
  if (addedChains.length > 0 && __classPrivateFieldGet(this, _AssetsController_instances, "a", _AssetsController_selectedAccounts_get).length > 0) {
1090
1077
  await this.getAssets(__classPrivateFieldGet(this, _AssetsController_instances, "a", _AssetsController_selectedAccounts_get), {
@@ -1092,28 +1079,5 @@ async function _AssetsController_handleAccountGroupChanged() {
1092
1079
  forceUpdate: true,
1093
1080
  });
1094
1081
  }
1095
- }, _AssetsController_handleSubscriptionUpdate =
1096
- /**
1097
- * Handle an async update from a data source subscription.
1098
- * Enriches response with token metadata before updating state.
1099
- *
1100
- * @param response - The data response from the data source.
1101
- * @param _sourceId - The source ID (unused but kept for logging context).
1102
- * @param request - Optional original request for context.
1103
- */
1104
- async function _AssetsController_handleSubscriptionUpdate(response, _sourceId, request) {
1105
- // Run through enrichment middlewares (Event Stack: Detection → Token → Price)
1106
- // Include 'metadata' in dataTypes so TokenDataSource runs to enrich detected assets
1107
- const enrichedResponse = await __classPrivateFieldGet(this, _AssetsController_instances, "m", _AssetsController_executeMiddlewares).call(this, [
1108
- this.messenger.call('DetectionMiddleware:getAssetsMiddleware'),
1109
- this.messenger.call('TokenDataSource:getAssetsMiddleware'),
1110
- this.messenger.call('PriceDataSource:getAssetsMiddleware'),
1111
- ], request ?? {
1112
- accounts: [],
1113
- chainIds: [],
1114
- dataTypes: ['balance', 'metadata', 'price'],
1115
- }, response);
1116
- // Update state
1117
- await __classPrivateFieldGet(this, _AssetsController_instances, "m", _AssetsController_updateState).call(this, enrichedResponse);
1118
1082
  };
1119
1083
  //# sourceMappingURL=AssetsController.cjs.map