@metamask/assets-controller 3.1.1 → 3.2.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.
package/CHANGELOG.md CHANGED
@@ -7,6 +7,36 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
7
7
 
8
8
  ## [Unreleased]
9
9
 
10
+ ## [3.2.0]
11
+
12
+ ### Added
13
+
14
+ - Add Sentry traces for Assets Health dashboard ([#8310](https://github.com/MetaMask/core/pull/8310))
15
+ - `AssetsDataSourceTiming` — per-source latency for each middleware in the fetch pipeline
16
+ - `AssetsDataSourceError` — tracks middleware failures with source names and error counts
17
+ - `AssetsFullFetch` — end-to-end fetch timing with asset/price/chain/account counts
18
+ - `AssetsUpdatePipeline` — enrichment pipeline timing for pushed data source updates
19
+ - `AssetsSubscriptionError` — subscription failure tracking per data source
20
+ - `AssetsStateSize` — entry counts for balances, metadata, prices, custom assets, unique assets, and network count (once on app start)
21
+ - `AggregatedBalanceSelector` — balance selector computation time with asset/network/account counts
22
+ - Add optional `trace` parameter to `getAggregatedBalanceForAccount` selector
23
+
24
+ ### Changed
25
+
26
+ - Bump `@metamask/account-tree-controller` from `^5.0.1` to `^6.0.0` ([#8317](https://github.com/MetaMask/core/pull/8317))
27
+ - Bump `@metamask/assets-controllers` from `^101.0.1` to `^102.0.0` ([#8317](https://github.com/MetaMask/core/pull/8317))
28
+ - Bump `@metamask/base-controller` from `^9.0.0` to `^9.0.1` ([#8317](https://github.com/MetaMask/core/pull/8317))
29
+ - Bump `@metamask/client-controller` from `^1.0.0` to `^1.0.1` ([#8317](https://github.com/MetaMask/core/pull/8317))
30
+ - Bump `@metamask/core-backend` from `^6.2.0` to `^6.2.1` ([#8317](https://github.com/MetaMask/core/pull/8317))
31
+ - Bump `@metamask/keyring-controller` from `^25.1.0` to `^25.1.1` ([#8317](https://github.com/MetaMask/core/pull/8317))
32
+ - Bump `@metamask/messenger` from `^0.3.0` to `^1.0.0` ([#8317](https://github.com/MetaMask/core/pull/8317))
33
+ - Bump `@metamask/network-controller` from `^30.0.0` to `^30.0.1` ([#8317](https://github.com/MetaMask/core/pull/8317))
34
+ - Bump `@metamask/network-enablement-controller` from `^5.0.0` to `^5.0.1` ([#8317](https://github.com/MetaMask/core/pull/8317))
35
+ - Bump `@metamask/permission-controller` from `^12.2.1` to `^12.3.0` ([#8317](https://github.com/MetaMask/core/pull/8317))
36
+ - Bump `@metamask/polling-controller` from `^16.0.3` to `^16.0.4` ([#8317](https://github.com/MetaMask/core/pull/8317))
37
+ - Bump `@metamask/preferences-controller` from `^23.0.0` to `^23.1.0` ([#8317](https://github.com/MetaMask/core/pull/8317))
38
+ - Bump `@metamask/transaction-controller` from `^63.1.0` to `^63.3.1` ([#8301](https://github.com/MetaMask/core/pull/8301), [#8313](https://github.com/MetaMask/core/pull/8313), [#8317](https://github.com/MetaMask/core/pull/8317))
39
+
10
40
  ## [3.1.1]
11
41
 
12
42
  ### Fixed
@@ -208,7 +238,8 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
208
238
  - Refactor `RpcDataSource` to delegate polling to `BalanceFetcher` and `TokenDetector` services ([#7709](https://github.com/MetaMask/core/pull/7709))
209
239
  - Refactor `BalanceFetcher` and `TokenDetector` to extend `StaticIntervalPollingControllerOnly` for independent polling management ([#7709](https://github.com/MetaMask/core/pull/7709))
210
240
 
211
- [Unreleased]: https://github.com/MetaMask/core/compare/@metamask/assets-controller@3.1.1...HEAD
241
+ [Unreleased]: https://github.com/MetaMask/core/compare/@metamask/assets-controller@3.2.0...HEAD
242
+ [3.2.0]: https://github.com/MetaMask/core/compare/@metamask/assets-controller@3.1.1...@metamask/assets-controller@3.2.0
212
243
  [3.1.1]: https://github.com/MetaMask/core/compare/@metamask/assets-controller@3.1.0...@metamask/assets-controller@3.1.1
213
244
  [3.1.0]: https://github.com/MetaMask/core/compare/@metamask/assets-controller@3.0.0...@metamask/assets-controller@3.1.0
214
245
  [3.0.0]: https://github.com/MetaMask/core/compare/@metamask/assets-controller@2.4.0...@metamask/assets-controller@3.0.0
@@ -13,7 +13,7 @@ var __classPrivateFieldSet = (this && this.__classPrivateFieldSet) || function (
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_isBasicFunctionality, _AssetsController_defaultUpdateInterval, _AssetsController_trace, _AssetsController_firstInitFetchReported, _AssetsController_uiOpen, _AssetsController_keyringUnlocked, _AssetsController_controllerMutex, _AssetsController_activeSubscriptions, _AssetsController_enabledChains, _AssetsController_selectedAccounts_get, _AssetsController_backendWebsocketDataSource, _AssetsController_accountsApiDataSource, _AssetsController_snapDataSource, _AssetsController_rpcDataSource, _AssetsController_stakedBalanceDataSource, _AssetsController_allBalanceDataSources_get, _AssetsController_priceDataSource, _AssetsController_detectionMiddleware, _AssetsController_tokenDataSource, _AssetsController_unsubscribeBasicFunctionality, _AssetsController_onActiveChainsUpdated, _AssetsController_initializeState, _AssetsController_extractEnabledChains, _AssetsController_normalizeChainReference, _AssetsController_subscribeToEvents, _AssetsController_updateActive, _AssetsController_registerActionHandlers, _AssetsController_handleActiveChainsUpdate, _AssetsController_executeMiddlewares, _AssetsController_resolveNativeAssetIds, _AssetsController_getNativeAssetIdsForEnabledChains, _AssetsController_getNativeAssetIdsForAccount, _AssetsController_ensureNativeBalancesDefaultZero, _AssetsController_updateState, _AssetsController_getAssetsFromState, _AssetsController_tokenStandardToAssetType, _AssetsController_start, _AssetsController_stop, _AssetsController_subscribeAssets, _AssetsController_subscribeAssetsBalance, _AssetsController_subscribeStakedBalance, _AssetsController_buildChainToAccountsMap, _AssetsController_subscribeDataSource, _AssetsController_unsubscribeDataSource, _AssetsController_buildDataRequest, _AssetsController_getEnabledChainsForAccount, _AssetsController_handleAccountGroupChanged, _AssetsController_handleEnabledNetworksChanged;
16
+ var _AssetsController_instances, _AssetsController_isEnabled, _AssetsController_isBasicFunctionality, _AssetsController_defaultUpdateInterval, _AssetsController_trace, _AssetsController_firstInitFetchReported, _AssetsController_stateSizeReported, _AssetsController_emitTrace, _AssetsController_emitStateSizeTrace, _AssetsController_uiOpen, _AssetsController_keyringUnlocked, _AssetsController_controllerMutex, _AssetsController_activeSubscriptions, _AssetsController_enabledChains, _AssetsController_selectedAccounts_get, _AssetsController_backendWebsocketDataSource, _AssetsController_accountsApiDataSource, _AssetsController_snapDataSource, _AssetsController_rpcDataSource, _AssetsController_stakedBalanceDataSource, _AssetsController_allBalanceDataSources_get, _AssetsController_priceDataSource, _AssetsController_detectionMiddleware, _AssetsController_tokenDataSource, _AssetsController_unsubscribeBasicFunctionality, _AssetsController_onActiveChainsUpdated, _AssetsController_initializeState, _AssetsController_extractEnabledChains, _AssetsController_normalizeChainReference, _AssetsController_subscribeToEvents, _AssetsController_updateActive, _AssetsController_registerActionHandlers, _AssetsController_handleActiveChainsUpdate, _AssetsController_executeMiddlewares, _AssetsController_resolveNativeAssetIds, _AssetsController_getNativeAssetIdsForEnabledChains, _AssetsController_getNativeAssetIdsForAccount, _AssetsController_ensureNativeBalancesDefaultZero, _AssetsController_updateState, _AssetsController_getAssetsFromState, _AssetsController_tokenStandardToAssetType, _AssetsController_start, _AssetsController_stop, _AssetsController_subscribeAssets, _AssetsController_subscribeAssetsBalance, _AssetsController_subscribeStakedBalance, _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");
@@ -53,6 +53,16 @@ const MESSENGER_EXPOSED_METHODS = [
53
53
  ];
54
54
  /** Default polling interval hint for data sources (30 seconds) */
55
55
  const DEFAULT_POLLING_INTERVAL_MS = 30000;
56
+ // ============================================================================
57
+ // TRACE NAMES — used in Sentry spans (search these strings in Discover)
58
+ // ============================================================================
59
+ const TRACE_FIRST_INIT_FETCH = 'AssetsControllerFirstInitFetch';
60
+ const TRACE_FULL_FETCH = 'AssetsFullFetch';
61
+ const TRACE_DATA_SOURCE_TIMING = 'AssetsDataSourceTiming';
62
+ const TRACE_DATA_SOURCE_ERROR = 'AssetsDataSourceError';
63
+ const TRACE_UPDATE_PIPELINE = 'AssetsUpdatePipeline';
64
+ const TRACE_SUBSCRIPTION_ERROR = 'AssetsSubscriptionError';
65
+ const TRACE_STATE_SIZE = 'AssetsStateSize';
56
66
  const log = (0, logger_1.createModuleLogger)(logger_1.projectLogger, CONTROLLER_NAME);
57
67
  /**
58
68
  * Returns the default state for AssetsController.
@@ -220,6 +230,8 @@ class AssetsController extends base_controller_1.BaseController {
220
230
  _AssetsController_trace.set(this, void 0);
221
231
  /** Whether we have already reported first init fetch for this session (reset on #stop). */
222
232
  _AssetsController_firstInitFetchReported.set(this, false);
233
+ /** Whether we have already reported state size for this session (reset on #stop). */
234
+ _AssetsController_stateSizeReported.set(this, false);
223
235
  /** Whether the client (UI) is open. Combined with #keyringUnlocked for #updateActive. */
224
236
  _AssetsController_uiOpen.set(this, false);
225
237
  /** Whether the keyring is unlocked. Combined with #uiOpen for #updateActive. */
@@ -335,7 +347,7 @@ class AssetsController extends base_controller_1.BaseController {
335
347
  customAssets.push(...accountCustomAssets);
336
348
  }
337
349
  if (options?.forceUpdate) {
338
- const startTime = Date.now();
350
+ const startTime = performance.now();
339
351
  const request = __classPrivateFieldGet(this, _AssetsController_instances, "m", _AssetsController_buildDataRequest).call(this, accounts, chainIds, {
340
352
  assetTypes,
341
353
  dataTypes,
@@ -369,23 +381,28 @@ class AssetsController extends base_controller_1.BaseController {
369
381
  options.chainIds.length < __classPrivateFieldGet(this, _AssetsController_enabledChains, "f").size;
370
382
  const updateMode = options?.updateMode ?? (isPartialChainFetch ? 'merge' : 'full');
371
383
  await __classPrivateFieldGet(this, _AssetsController_instances, "m", _AssetsController_updateState).call(this, { ...response, updateMode });
372
- if (__classPrivateFieldGet(this, _AssetsController_trace, "f") && !__classPrivateFieldGet(this, _AssetsController_firstInitFetchReported, "f")) {
384
+ const durationMs = performance.now() - startTime;
385
+ // Emit trace for every full fetch (Assets Health dashboard)
386
+ __classPrivateFieldGet(this, _AssetsController_instances, "m", _AssetsController_emitTrace).call(this, TRACE_FULL_FETCH, {
387
+ duration_ms: durationMs,
388
+ chain_count: chainIds.length,
389
+ account_count: accounts.length,
390
+ basic_functionality: __classPrivateFieldGet(this, _AssetsController_isBasicFunctionality, "f").call(this),
391
+ asset_count: response.assetsBalance
392
+ ? Object.values(response.assetsBalance).reduce((sum, acct) => sum + Object.keys(acct).length, 0)
393
+ : 0,
394
+ price_count: response.assetsPrice
395
+ ? Object.keys(response.assetsPrice).length
396
+ : 0,
397
+ ...durationByDataSource,
398
+ });
399
+ if (!__classPrivateFieldGet(this, _AssetsController_firstInitFetchReported, "f")) {
373
400
  __classPrivateFieldSet(this, _AssetsController_firstInitFetchReported, true, "f");
374
- const durationMs = Date.now() - startTime;
375
- try {
376
- await __classPrivateFieldGet(this, _AssetsController_trace, "f").call(this, {
377
- name: 'AssetsController First Init Fetch',
378
- data: {
379
- duration_ms: durationMs,
380
- chain_ids: JSON.stringify(chainIds),
381
- ...durationByDataSource,
382
- },
383
- tags: { controller: 'AssetsController' },
384
- }, () => undefined);
385
- }
386
- catch {
387
- // Telemetry failure must not break.
388
- }
401
+ __classPrivateFieldGet(this, _AssetsController_instances, "m", _AssetsController_emitTrace).call(this, TRACE_FIRST_INIT_FETCH, {
402
+ duration_ms: durationMs,
403
+ chain_ids: JSON.stringify(chainIds),
404
+ ...durationByDataSource,
405
+ });
389
406
  }
390
407
  }
391
408
  const result = __classPrivateFieldGet(this, _AssetsController_instances, "m", _AssetsController_getAssetsFromState).call(this, accounts, chainIds, assetTypes);
@@ -711,6 +728,7 @@ class AssetsController extends base_controller_1.BaseController {
711
728
  * @param request - Optional original request for context when enriching
712
729
  */
713
730
  async handleAssetsUpdate(response, sourceId, request) {
731
+ const updateStart = performance.now();
714
732
  log('Assets updated from data source', {
715
733
  sourceId,
716
734
  hasBalance: Boolean(response.assetsBalance),
@@ -730,6 +748,16 @@ class AssetsController extends base_controller_1.BaseController {
730
748
  dataTypes: ['balance', 'metadata', 'price'],
731
749
  }, response);
732
750
  await __classPrivateFieldGet(this, _AssetsController_instances, "m", _AssetsController_updateState).call(this, enrichedResponse);
751
+ __classPrivateFieldGet(this, _AssetsController_instances, "m", _AssetsController_emitTrace).call(this, TRACE_UPDATE_PIPELINE, {
752
+ source: sourceId,
753
+ duration_ms: performance.now() - updateStart,
754
+ has_balance: Boolean(response.assetsBalance),
755
+ has_price: Boolean(response.assetsPrice),
756
+ has_metadata: Boolean(enrichedResponse.assetsInfo),
757
+ balance_account_count: response.assetsBalance
758
+ ? Object.keys(response.assetsBalance).length
759
+ : 0,
760
+ });
733
761
  }
734
762
  // ============================================================================
735
763
  // CLEANUP
@@ -766,7 +794,53 @@ class AssetsController extends base_controller_1.BaseController {
766
794
  }
767
795
  }
768
796
  exports.AssetsController = AssetsController;
769
- _AssetsController_isEnabled = new WeakMap(), _AssetsController_isBasicFunctionality = new WeakMap(), _AssetsController_defaultUpdateInterval = new WeakMap(), _AssetsController_trace = new WeakMap(), _AssetsController_firstInitFetchReported = new WeakMap(), _AssetsController_uiOpen = new WeakMap(), _AssetsController_keyringUnlocked = 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_stakedBalanceDataSource = new WeakMap(), _AssetsController_priceDataSource = new WeakMap(), _AssetsController_detectionMiddleware = new WeakMap(), _AssetsController_tokenDataSource = new WeakMap(), _AssetsController_unsubscribeBasicFunctionality = new WeakMap(), _AssetsController_onActiveChainsUpdated = new WeakMap(), _AssetsController_instances = new WeakSet(), _AssetsController_selectedAccounts_get = function _AssetsController_selectedAccounts_get() {
797
+ _AssetsController_isEnabled = new WeakMap(), _AssetsController_isBasicFunctionality = new WeakMap(), _AssetsController_defaultUpdateInterval = new WeakMap(), _AssetsController_trace = new WeakMap(), _AssetsController_firstInitFetchReported = new WeakMap(), _AssetsController_stateSizeReported = new WeakMap(), _AssetsController_uiOpen = new WeakMap(), _AssetsController_keyringUnlocked = 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_stakedBalanceDataSource = new WeakMap(), _AssetsController_priceDataSource = new WeakMap(), _AssetsController_detectionMiddleware = new WeakMap(), _AssetsController_tokenDataSource = new WeakMap(), _AssetsController_unsubscribeBasicFunctionality = new WeakMap(), _AssetsController_onActiveChainsUpdated = new WeakMap(), _AssetsController_instances = new WeakSet(), _AssetsController_emitTrace = function _AssetsController_emitTrace(name, data, tags = {
798
+ controller: 'AssetsController',
799
+ }) {
800
+ if (!__classPrivateFieldGet(this, _AssetsController_trace, "f")) {
801
+ return;
802
+ }
803
+ __classPrivateFieldGet(this, _AssetsController_trace, "f").call(this, { name, data, tags }, () => undefined).catch(() => {
804
+ // Telemetry failure must not break.
805
+ });
806
+ }, _AssetsController_emitStateSizeTrace = function _AssetsController_emitStateSizeTrace() {
807
+ if (!__classPrivateFieldGet(this, _AssetsController_trace, "f") || __classPrivateFieldGet(this, _AssetsController_stateSizeReported, "f")) {
808
+ return;
809
+ }
810
+ __classPrivateFieldSet(this, _AssetsController_stateSizeReported, true, "f");
811
+ const { assetsBalance: balances, customAssets, assetsInfo, assetsPrice, } = this.state;
812
+ // Count balance entries and collect unique asset IDs / chain IDs in one pass.
813
+ let balanceEntries = 0;
814
+ const uniqueAssets = new Set();
815
+ const uniqueNetworks = new Set();
816
+ for (const acct of Object.values(balances)) {
817
+ const assetIds = Object.keys(acct);
818
+ balanceEntries += assetIds.length;
819
+ for (const assetId of assetIds) {
820
+ uniqueAssets.add(assetId);
821
+ // CAIP-19 format: "eip155:1/slip44:60" — chainId is everything before "/"
822
+ const slash = assetId.indexOf('/');
823
+ if (slash > 0) {
824
+ uniqueNetworks.add(assetId.slice(0, slash));
825
+ }
826
+ }
827
+ }
828
+ let customAssetEntries = 0;
829
+ for (const ids of Object.values(customAssets)) {
830
+ if (Array.isArray(ids)) {
831
+ customAssetEntries += ids.length;
832
+ }
833
+ }
834
+ __classPrivateFieldGet(this, _AssetsController_instances, "m", _AssetsController_emitTrace).call(this, TRACE_STATE_SIZE, {
835
+ balance_entries: balanceEntries,
836
+ balance_accounts: Object.keys(balances).length,
837
+ unique_asset_count: uniqueAssets.size,
838
+ network_count: uniqueNetworks.size,
839
+ metadata_entries: Object.keys(assetsInfo).length,
840
+ price_entries: Object.keys(assetsPrice).length,
841
+ custom_asset_entries: customAssetEntries,
842
+ });
843
+ }, _AssetsController_selectedAccounts_get = function _AssetsController_selectedAccounts_get() {
770
844
  return this.messenger.call('AccountTreeController:getAccountsFromSelectedAccountGroup');
771
845
  }, _AssetsController_allBalanceDataSources_get = function _AssetsController_allBalanceDataSources_get() {
772
846
  return [
@@ -888,19 +962,22 @@ async function _AssetsController_executeMiddlewares(sources, request, initialRes
888
962
  const middlewares = sources.map((source) => source.assetsMiddleware);
889
963
  const inclusive = [];
890
964
  const wrapped = middlewares.map((middleware, i) => (async (ctx, next) => {
891
- const start = Date.now();
965
+ const start = performance.now();
892
966
  try {
893
967
  return await middleware(ctx, next);
894
968
  }
895
969
  finally {
896
- inclusive[i] = Date.now() - start;
970
+ inclusive[i] = performance.now() - start;
897
971
  }
898
972
  }));
899
- const chain = wrapped.reduceRight((next, middleware) => async (ctx) => {
973
+ const middlewareErrors = [];
974
+ const chain = wrapped.reduceRight((next, middleware, index) => async (ctx) => {
900
975
  try {
901
976
  return await middleware(ctx, next);
902
977
  }
903
978
  catch (error) {
979
+ const sourceName = names[index] ?? `middleware_${index}`;
980
+ middlewareErrors.push(sourceName);
904
981
  console.error('[AssetsController] Middleware failed:', error);
905
982
  return next(ctx);
906
983
  }
@@ -924,6 +1001,23 @@ async function _AssetsController_executeMiddlewares(sources, request, initialRes
924
1001
  durationByDataSource[key] = ms;
925
1002
  }
926
1003
  }
1004
+ // Emit per-source timing traces for the Assets Health dashboard
1005
+ for (const [sourceName, durationMs] of Object.entries(durationByDataSource)) {
1006
+ __classPrivateFieldGet(this, _AssetsController_instances, "m", _AssetsController_emitTrace).call(this, TRACE_DATA_SOURCE_TIMING, {
1007
+ source: sourceName,
1008
+ duration_ms: durationMs,
1009
+ chain_count: request.chainIds.length,
1010
+ account_count: request.accountsWithSupportedChains.length,
1011
+ });
1012
+ }
1013
+ // Emit error traces for failed middlewares
1014
+ if (middlewareErrors.length > 0) {
1015
+ __classPrivateFieldGet(this, _AssetsController_instances, "m", _AssetsController_emitTrace).call(this, TRACE_DATA_SOURCE_ERROR, {
1016
+ failed_sources: middlewareErrors.join(','),
1017
+ error_count: middlewareErrors.length,
1018
+ chain_count: request.chainIds.length,
1019
+ });
1020
+ }
927
1021
  return { response: result.response, durationByDataSource };
928
1022
  }, _AssetsController_resolveNativeAssetIds = function _AssetsController_resolveNativeAssetIds(chains) {
929
1023
  const { nativeAssetIdentifiers } = this.messenger.call('NetworkEnablementController:getState');
@@ -1064,6 +1158,8 @@ async function _AssetsController_executeMiddlewares(sources, request, initialRes
1064
1158
  }
1065
1159
  }
1066
1160
  });
1161
+ // Emit state size trace (throttled to avoid JSON.stringify on every update)
1162
+ __classPrivateFieldGet(this, _AssetsController_instances, "m", _AssetsController_emitStateSizeTrace).call(this);
1067
1163
  // Calculate changed prices
1068
1164
  const changedPriceAssets = normalizedResponse.assetsPrice
1069
1165
  ? Object.keys(normalizedResponse.assetsPrice).filter((assetId) => !(0, lodash_1.isEqual)(previousPrices[assetId], normalizedResponse.assetsPrice?.[assetId]))
@@ -1211,6 +1307,7 @@ async function _AssetsController_executeMiddlewares(sources, request, initialRes
1211
1307
  hasPriceSubscription: __classPrivateFieldGet(this, _AssetsController_activeSubscriptions, "f").has('ds:PriceDataSource'),
1212
1308
  });
1213
1309
  __classPrivateFieldSet(this, _AssetsController_firstInitFetchReported, false, "f");
1310
+ __classPrivateFieldSet(this, _AssetsController_stateSizeReported, false, "f");
1214
1311
  // Stop price subscription first (uses direct messenger call)
1215
1312
  this.unsubscribeAssetsPrice();
1216
1313
  // Stop balance subscriptions by properly notifying data sources via messenger
@@ -1327,6 +1424,10 @@ async function _AssetsController_executeMiddlewares(sources, request, initialRes
1327
1424
  };
1328
1425
  source.subscribe(subscribeReq).catch((error) => {
1329
1426
  console.error(`[AssetsController] Failed to subscribe to '${sourceId}':`, error);
1427
+ __classPrivateFieldGet(this, _AssetsController_instances, "m", _AssetsController_emitTrace).call(this, TRACE_SUBSCRIPTION_ERROR, {
1428
+ source: sourceId,
1429
+ error_message: String(error),
1430
+ });
1330
1431
  });
1331
1432
  // Track subscription
1332
1433
  const subscription = {