@metamask-previews/assets-controller 3.1.1-preview-17c76be24 → 3.1.1-preview-dbd93752f

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,18 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
7
7
 
8
8
  ## [Unreleased]
9
9
 
10
+ ### Added
11
+
12
+ - Add Sentry traces for Assets Health dashboard ([#8310](https://github.com/MetaMask/core/pull/8310))
13
+ - `Assets Data Source Timing` — per-source latency for each middleware in the fetch pipeline
14
+ - `Assets Data Source Error` — tracks middleware failures with source names and error counts
15
+ - `Assets Full Fetch` — end-to-end fetch timing with asset/price/chain/account counts
16
+ - `Assets Update Pipeline` — enrichment pipeline timing for pushed data source updates
17
+ - `Assets Subscription Error` — subscription failure tracking per data source
18
+ - `Assets State Size` — entry counts for balances, metadata, prices, and custom assets (once on app start)
19
+ - `Aggregated Balance Selector` — balance selector computation time with asset/network/account counts
20
+ - Add optional `trace` parameter to `getAggregatedBalanceForAccount` selector
21
+
10
22
  ### Changed
11
23
 
12
24
  - Bump `@metamask/transaction-controller` from `^63.1.0` to `^63.2.0` ([#8301](https://github.com/MetaMask/core/pull/8301))
@@ -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");
@@ -220,6 +220,8 @@ class AssetsController extends base_controller_1.BaseController {
220
220
  _AssetsController_trace.set(this, void 0);
221
221
  /** Whether we have already reported first init fetch for this session (reset on #stop). */
222
222
  _AssetsController_firstInitFetchReported.set(this, false);
223
+ /** Whether we have already reported state size for this session (reset on #stop). */
224
+ _AssetsController_stateSizeReported.set(this, false);
223
225
  /** Whether the client (UI) is open. Combined with #keyringUnlocked for #updateActive. */
224
226
  _AssetsController_uiOpen.set(this, false);
225
227
  /** Whether the keyring is unlocked. Combined with #uiOpen for #updateActive. */
@@ -369,23 +371,28 @@ class AssetsController extends base_controller_1.BaseController {
369
371
  options.chainIds.length < __classPrivateFieldGet(this, _AssetsController_enabledChains, "f").size;
370
372
  const updateMode = options?.updateMode ?? (isPartialChainFetch ? 'merge' : 'full');
371
373
  await __classPrivateFieldGet(this, _AssetsController_instances, "m", _AssetsController_updateState).call(this, { ...response, updateMode });
372
- if (__classPrivateFieldGet(this, _AssetsController_trace, "f") && !__classPrivateFieldGet(this, _AssetsController_firstInitFetchReported, "f")) {
374
+ const durationMs = Date.now() - startTime;
375
+ // Emit trace for every full fetch (Assets Health dashboard)
376
+ __classPrivateFieldGet(this, _AssetsController_instances, "m", _AssetsController_emitTrace).call(this, 'Assets Full Fetch', {
377
+ duration_ms: durationMs,
378
+ chain_count: chainIds.length,
379
+ account_count: accounts.length,
380
+ basic_functionality: __classPrivateFieldGet(this, _AssetsController_isBasicFunctionality, "f").call(this),
381
+ asset_count: response.assetsBalance
382
+ ? Object.values(response.assetsBalance).reduce((sum, acct) => sum + Object.keys(acct).length, 0)
383
+ : 0,
384
+ price_count: response.assetsPrice
385
+ ? Object.keys(response.assetsPrice).length
386
+ : 0,
387
+ ...durationByDataSource,
388
+ });
389
+ if (!__classPrivateFieldGet(this, _AssetsController_firstInitFetchReported, "f")) {
373
390
  __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
- }
391
+ __classPrivateFieldGet(this, _AssetsController_instances, "m", _AssetsController_emitTrace).call(this, 'AssetsController First Init Fetch', {
392
+ duration_ms: durationMs,
393
+ chain_ids: JSON.stringify(chainIds),
394
+ ...durationByDataSource,
395
+ });
389
396
  }
390
397
  }
391
398
  const result = __classPrivateFieldGet(this, _AssetsController_instances, "m", _AssetsController_getAssetsFromState).call(this, accounts, chainIds, assetTypes);
@@ -711,6 +718,7 @@ class AssetsController extends base_controller_1.BaseController {
711
718
  * @param request - Optional original request for context when enriching
712
719
  */
713
720
  async handleAssetsUpdate(response, sourceId, request) {
721
+ const updateStart = Date.now();
714
722
  log('Assets updated from data source', {
715
723
  sourceId,
716
724
  hasBalance: Boolean(response.assetsBalance),
@@ -730,6 +738,16 @@ class AssetsController extends base_controller_1.BaseController {
730
738
  dataTypes: ['balance', 'metadata', 'price'],
731
739
  }, response);
732
740
  await __classPrivateFieldGet(this, _AssetsController_instances, "m", _AssetsController_updateState).call(this, enrichedResponse);
741
+ __classPrivateFieldGet(this, _AssetsController_instances, "m", _AssetsController_emitTrace).call(this, 'Assets Update Pipeline', {
742
+ source: sourceId,
743
+ duration_ms: Date.now() - updateStart,
744
+ has_balance: Boolean(response.assetsBalance),
745
+ has_price: Boolean(response.assetsPrice),
746
+ has_metadata: Boolean(enrichedResponse.assetsInfo),
747
+ balance_account_count: response.assetsBalance
748
+ ? Object.keys(response.assetsBalance).length
749
+ : 0,
750
+ });
733
751
  }
734
752
  // ============================================================================
735
753
  // CLEANUP
@@ -766,7 +784,44 @@ class AssetsController extends base_controller_1.BaseController {
766
784
  }
767
785
  }
768
786
  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() {
787
+ _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 = {
788
+ controller: 'AssetsController',
789
+ }) {
790
+ if (!__classPrivateFieldGet(this, _AssetsController_trace, "f")) {
791
+ return;
792
+ }
793
+ try {
794
+ __classPrivateFieldGet(this, _AssetsController_trace, "f").call(this, { name, data, tags }, () => undefined).catch(() => {
795
+ // Telemetry failure must not break.
796
+ });
797
+ }
798
+ catch {
799
+ // Telemetry failure must not break.
800
+ }
801
+ }, _AssetsController_emitStateSizeTrace = function _AssetsController_emitStateSizeTrace() {
802
+ if (!__classPrivateFieldGet(this, _AssetsController_trace, "f") || __classPrivateFieldGet(this, _AssetsController_stateSizeReported, "f")) {
803
+ return;
804
+ }
805
+ __classPrivateFieldSet(this, _AssetsController_stateSizeReported, true, "f");
806
+ const { assetsBalance: balances, customAssets, assetsInfo, assetsPrice, } = this.state;
807
+ let balanceEntries = 0;
808
+ for (const acct of Object.values(balances)) {
809
+ balanceEntries += Object.keys(acct).length;
810
+ }
811
+ let customAssetEntries = 0;
812
+ for (const ids of Object.values(customAssets)) {
813
+ if (Array.isArray(ids)) {
814
+ customAssetEntries += ids.length;
815
+ }
816
+ }
817
+ __classPrivateFieldGet(this, _AssetsController_instances, "m", _AssetsController_emitTrace).call(this, 'Assets State Size', {
818
+ balance_entries: balanceEntries,
819
+ balance_accounts: Object.keys(balances).length,
820
+ metadata_entries: Object.keys(assetsInfo).length,
821
+ price_entries: Object.keys(assetsPrice).length,
822
+ custom_asset_entries: customAssetEntries,
823
+ });
824
+ }, _AssetsController_selectedAccounts_get = function _AssetsController_selectedAccounts_get() {
770
825
  return this.messenger.call('AccountTreeController:getAccountsFromSelectedAccountGroup');
771
826
  }, _AssetsController_allBalanceDataSources_get = function _AssetsController_allBalanceDataSources_get() {
772
827
  return [
@@ -896,11 +951,14 @@ async function _AssetsController_executeMiddlewares(sources, request, initialRes
896
951
  inclusive[i] = Date.now() - start;
897
952
  }
898
953
  }));
899
- const chain = wrapped.reduceRight((next, middleware) => async (ctx) => {
954
+ const middlewareErrors = [];
955
+ const chain = wrapped.reduceRight((next, middleware, index) => async (ctx) => {
900
956
  try {
901
957
  return await middleware(ctx, next);
902
958
  }
903
959
  catch (error) {
960
+ const sourceName = names[index] ?? `middleware_${index}`;
961
+ middlewareErrors.push(sourceName);
904
962
  console.error('[AssetsController] Middleware failed:', error);
905
963
  return next(ctx);
906
964
  }
@@ -924,6 +982,23 @@ async function _AssetsController_executeMiddlewares(sources, request, initialRes
924
982
  durationByDataSource[key] = ms;
925
983
  }
926
984
  }
985
+ // Emit per-source timing traces for the Assets Health dashboard
986
+ for (const [sourceName, durationMs] of Object.entries(durationByDataSource)) {
987
+ __classPrivateFieldGet(this, _AssetsController_instances, "m", _AssetsController_emitTrace).call(this, 'Assets Data Source Timing', {
988
+ source: sourceName,
989
+ duration_ms: durationMs,
990
+ chain_count: request.chainIds.length,
991
+ account_count: request.accountsWithSupportedChains.length,
992
+ });
993
+ }
994
+ // Emit error traces for failed middlewares
995
+ if (middlewareErrors.length > 0) {
996
+ __classPrivateFieldGet(this, _AssetsController_instances, "m", _AssetsController_emitTrace).call(this, 'Assets Data Source Error', {
997
+ failed_sources: middlewareErrors.join(','),
998
+ error_count: middlewareErrors.length,
999
+ chain_count: request.chainIds.length,
1000
+ });
1001
+ }
927
1002
  return { response: result.response, durationByDataSource };
928
1003
  }, _AssetsController_resolveNativeAssetIds = function _AssetsController_resolveNativeAssetIds(chains) {
929
1004
  const { nativeAssetIdentifiers } = this.messenger.call('NetworkEnablementController:getState');
@@ -1064,6 +1139,8 @@ async function _AssetsController_executeMiddlewares(sources, request, initialRes
1064
1139
  }
1065
1140
  }
1066
1141
  });
1142
+ // Emit state size trace (throttled to avoid JSON.stringify on every update)
1143
+ __classPrivateFieldGet(this, _AssetsController_instances, "m", _AssetsController_emitStateSizeTrace).call(this);
1067
1144
  // Calculate changed prices
1068
1145
  const changedPriceAssets = normalizedResponse.assetsPrice
1069
1146
  ? Object.keys(normalizedResponse.assetsPrice).filter((assetId) => !(0, lodash_1.isEqual)(previousPrices[assetId], normalizedResponse.assetsPrice?.[assetId]))
@@ -1211,6 +1288,7 @@ async function _AssetsController_executeMiddlewares(sources, request, initialRes
1211
1288
  hasPriceSubscription: __classPrivateFieldGet(this, _AssetsController_activeSubscriptions, "f").has('ds:PriceDataSource'),
1212
1289
  });
1213
1290
  __classPrivateFieldSet(this, _AssetsController_firstInitFetchReported, false, "f");
1291
+ __classPrivateFieldSet(this, _AssetsController_stateSizeReported, false, "f");
1214
1292
  // Stop price subscription first (uses direct messenger call)
1215
1293
  this.unsubscribeAssetsPrice();
1216
1294
  // Stop balance subscriptions by properly notifying data sources via messenger
@@ -1327,6 +1405,10 @@ async function _AssetsController_executeMiddlewares(sources, request, initialRes
1327
1405
  };
1328
1406
  source.subscribe(subscribeReq).catch((error) => {
1329
1407
  console.error(`[AssetsController] Failed to subscribe to '${sourceId}':`, error);
1408
+ __classPrivateFieldGet(this, _AssetsController_instances, "m", _AssetsController_emitTrace).call(this, 'Assets Subscription Error', {
1409
+ source: sourceId,
1410
+ error_message: String(error),
1411
+ });
1330
1412
  });
1331
1413
  // Track subscription
1332
1414
  const subscription = {