@metamask-previews/assets-controller 9.1.0-preview-ba933fc1b → 9.1.0-preview-14cbc2873

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 (52) hide show
  1. package/CHANGELOG.md +0 -9
  2. package/dist/AssetsController.cjs +51 -98
  3. package/dist/AssetsController.cjs.map +1 -1
  4. package/dist/AssetsController.d.cts +1 -1
  5. package/dist/AssetsController.d.cts.map +1 -1
  6. package/dist/AssetsController.d.mts +1 -1
  7. package/dist/AssetsController.d.mts.map +1 -1
  8. package/dist/AssetsController.mjs +51 -98
  9. package/dist/AssetsController.mjs.map +1 -1
  10. package/dist/data-sources/AccountsApiDataSource.cjs +2 -5
  11. package/dist/data-sources/AccountsApiDataSource.cjs.map +1 -1
  12. package/dist/data-sources/AccountsApiDataSource.d.cts.map +1 -1
  13. package/dist/data-sources/AccountsApiDataSource.d.mts.map +1 -1
  14. package/dist/data-sources/AccountsApiDataSource.mjs +2 -5
  15. package/dist/data-sources/AccountsApiDataSource.mjs.map +1 -1
  16. package/dist/data-sources/BackendWebsocketDataSource.cjs +107 -223
  17. package/dist/data-sources/BackendWebsocketDataSource.cjs.map +1 -1
  18. package/dist/data-sources/BackendWebsocketDataSource.d.cts +0 -7
  19. package/dist/data-sources/BackendWebsocketDataSource.d.cts.map +1 -1
  20. package/dist/data-sources/BackendWebsocketDataSource.d.mts +0 -7
  21. package/dist/data-sources/BackendWebsocketDataSource.d.mts.map +1 -1
  22. package/dist/data-sources/BackendWebsocketDataSource.mjs +107 -223
  23. package/dist/data-sources/BackendWebsocketDataSource.mjs.map +1 -1
  24. package/dist/data-sources/RpcDataSource.cjs +2 -3
  25. package/dist/data-sources/RpcDataSource.cjs.map +1 -1
  26. package/dist/data-sources/RpcDataSource.d.cts.map +1 -1
  27. package/dist/data-sources/RpcDataSource.d.mts.map +1 -1
  28. package/dist/data-sources/RpcDataSource.mjs +2 -3
  29. package/dist/data-sources/RpcDataSource.mjs.map +1 -1
  30. package/dist/data-sources/SnapDataSource.cjs +3 -3
  31. package/dist/data-sources/SnapDataSource.cjs.map +1 -1
  32. package/dist/data-sources/SnapDataSource.mjs +3 -3
  33. package/dist/data-sources/SnapDataSource.mjs.map +1 -1
  34. package/dist/data-sources/evm-rpc-services/services/BalanceFetcher.d.cts +2 -2
  35. package/dist/data-sources/evm-rpc-services/services/BalanceFetcher.d.mts +2 -2
  36. package/dist/data-sources/evm-rpc-services/services/StakedBalanceFetcher.d.cts +2 -2
  37. package/dist/data-sources/evm-rpc-services/services/StakedBalanceFetcher.d.mts +2 -2
  38. package/dist/data-sources/evm-rpc-services/services/TokenDetector.d.cts +2 -2
  39. package/dist/data-sources/evm-rpc-services/services/TokenDetector.d.mts +2 -2
  40. package/dist/middlewares/ParallelMiddleware.cjs +0 -9
  41. package/dist/middlewares/ParallelMiddleware.cjs.map +1 -1
  42. package/dist/middlewares/ParallelMiddleware.d.cts.map +1 -1
  43. package/dist/middlewares/ParallelMiddleware.d.mts.map +1 -1
  44. package/dist/middlewares/ParallelMiddleware.mjs +0 -9
  45. package/dist/middlewares/ParallelMiddleware.mjs.map +1 -1
  46. package/dist/types.cjs.map +1 -1
  47. package/dist/types.d.cts +3 -8
  48. package/dist/types.d.cts.map +1 -1
  49. package/dist/types.d.mts +3 -8
  50. package/dist/types.d.mts.map +1 -1
  51. package/dist/types.mjs.map +1 -1
  52. package/package.json +1 -1
package/CHANGELOG.md CHANGED
@@ -11,15 +11,6 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
11
11
 
12
12
  - Bump `@metamask/transaction-controller` from `^68.1.1` to `^68.2.0` ([#9253](https://github.com/MetaMask/core/pull/9253))
13
13
 
14
- ### Fixed
15
-
16
- - Fix stale token balances after transactions when switching accounts or when websocket subscriptions reconnect; `AssetsController` now fetches before re-subscribing on account switch and serializes overlapping refresh work ([#9265](https://github.com/MetaMask/core/pull/9265))
17
- - `AccountsApiDataSource` bypasses the TanStack Query balance cache when `forceUpdate` is true so forced refreshes return up-to-date balances instead of 60-second cached values ([#9265](https://github.com/MetaMask/core/pull/9265))
18
- - `BackendWebsocketDataSource` re-subscribes when subscribed accounts change (case-insensitive EVM address matching), serializes subscribe/unsubscribe to prevent races on account switch, and registers channel callbacks as a fallback when server `subscriptionId` values do not match ([#9265](https://github.com/MetaMask/core/pull/9265))
19
- - Remove the 120-second websocket balance freshness guard that blocked force-refresh and polling updates from correcting stale websocket balances ([#9273](https://github.com/MetaMask/core/pull/9273))
20
- - Add `update` balance update mode so fetch and force-refresh pipelines patch balances without removing tokens omitted from partial API snapshots; `getAssets({ forceUpdate: true })`, `AccountsApiDataSource`, and `SnapDataSource` use this mode ([#9273](https://github.com/MetaMask/core/pull/9273))
21
- - `BackendWebsocketDataSource` registers subscription handlers before the subscribe handshake so in-flight account-activity notifications are not dropped, cleans up subscription state on subscribe failure, and resolves balance updates from stored subscription state when notifications arrive with stale subscription IDs ([#9273](https://github.com/MetaMask/core/pull/9273))
22
-
23
14
  ## [9.1.0]
24
15
 
25
16
  ### Added
@@ -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_captureException, _AssetsController_firstInitFetchReported, _AssetsController_stateSizeReported, _AssetsController_emitTrace, _AssetsController_emitStateSizeTrace, _AssetsController_uiOpen, _AssetsController_keyringUnlocked, _AssetsController_controllerMutex, _AssetsController_accountRefreshMutex, _AssetsController_activeSubscriptions, _AssetsController_enabledChains, _AssetsController_lastKnownAccountIds, _AssetsController_getSelectedAccounts, _AssetsController_backendWebsocketDataSource, _AssetsController_accountsApiDataSource, _AssetsController_snapDataSource, _AssetsController_rpcDataSource, _AssetsController_stakedBalanceDataSource, _AssetsController_allBalanceDataSources_get, _AssetsController_priceDataSource, _AssetsController_detectionMiddleware, _AssetsController_customAssetGraduationMiddleware, _AssetsController_rpcFallbackMiddleware, _AssetsController_tokenDataSource, _AssetsController_unsubscribeBasicFunctionality, _AssetsController_queryApiClient, _AssetsController_onActiveChainsUpdated, _AssetsController_initializeNativeAssetsMap, _AssetsController_initializeState, _AssetsController_extractEnabledChains, _AssetsController_normalizeChainReference, _AssetsController_subscribeToEvents, _AssetsController_onUnapprovedTransactionAdded, _AssetsController_updateActive, _AssetsController_handleAccountTreeStateChange, _AssetsController_runAccountTreeRefresh, _AssetsController_registerActionHandlers, _AssetsController_handleActiveChainsUpdate, _AssetsController_executeMiddlewares, _AssetsController_getNativeAssetMap, _AssetsController_isNativeAsset, _AssetsController_getAssetType, _AssetsController_resolveNativeAssetIds, _AssetsController_getNativeAssetIdsForEnabledChains, _AssetsController_getNativeAssetIdsForAccount, _AssetsController_ensureNativeBalancesDefaultZero, _AssetsController_ensureDefaultTrackedAssetsSeeded, _AssetsController_updateState, _AssetsController_getAssetsFromState, _AssetsController_shouldHideNativeToken, _AssetsController_tokenStandardToAssetType, _AssetsController_start, _AssetsController_stop, _AssetsController_subscribeAssets, _AssetsController_subscribeAssetsBalance, _AssetsController_subscribeRpcCustomAssetsSupplement, _AssetsController_unsubscribeBySubscriptionKey, _AssetsController_subscribeStakedBalance, _AssetsController_buildChainToAccountsMap, _AssetsController_subscribeDataSource, _AssetsController_unsubscribeDataSource, _AssetsController_buildDataRequest, _AssetsController_getEnabledChainsForAccount, _AssetsController_handleAccountGroupChanged, _AssetsController_handleEnabledNetworksChanged, _AssetsController_handleNetworkAdded, _AssetsController_refreshAssetsAfterNetworkChange;
16
+ var _AssetsController_instances, _AssetsController_isEnabled, _AssetsController_isBasicFunctionality, _AssetsController_defaultUpdateInterval, _AssetsController_trace, _AssetsController_captureException, _AssetsController_firstInitFetchReported, _AssetsController_stateSizeReported, _AssetsController_emitTrace, _AssetsController_emitStateSizeTrace, _AssetsController_uiOpen, _AssetsController_keyringUnlocked, _AssetsController_controllerMutex, _AssetsController_activeSubscriptions, _AssetsController_enabledChains, _AssetsController_lastKnownAccountIds, _AssetsController_getSelectedAccounts, _AssetsController_backendWebsocketDataSource, _AssetsController_accountsApiDataSource, _AssetsController_snapDataSource, _AssetsController_rpcDataSource, _AssetsController_stakedBalanceDataSource, _AssetsController_allBalanceDataSources_get, _AssetsController_priceDataSource, _AssetsController_detectionMiddleware, _AssetsController_customAssetGraduationMiddleware, _AssetsController_rpcFallbackMiddleware, _AssetsController_tokenDataSource, _AssetsController_unsubscribeBasicFunctionality, _AssetsController_queryApiClient, _AssetsController_onActiveChainsUpdated, _AssetsController_initializeNativeAssetsMap, _AssetsController_initializeState, _AssetsController_extractEnabledChains, _AssetsController_normalizeChainReference, _AssetsController_subscribeToEvents, _AssetsController_onUnapprovedTransactionAdded, _AssetsController_updateActive, _AssetsController_handleAccountTreeStateChange, _AssetsController_registerActionHandlers, _AssetsController_handleActiveChainsUpdate, _AssetsController_executeMiddlewares, _AssetsController_getNativeAssetMap, _AssetsController_isNativeAsset, _AssetsController_getAssetType, _AssetsController_resolveNativeAssetIds, _AssetsController_getNativeAssetIdsForEnabledChains, _AssetsController_getNativeAssetIdsForAccount, _AssetsController_ensureNativeBalancesDefaultZero, _AssetsController_ensureDefaultTrackedAssetsSeeded, _AssetsController_updateState, _AssetsController_getAssetsFromState, _AssetsController_shouldHideNativeToken, _AssetsController_tokenStandardToAssetType, _AssetsController_start, _AssetsController_stop, _AssetsController_subscribeAssets, _AssetsController_subscribeAssetsBalance, _AssetsController_subscribeRpcCustomAssetsSupplement, _AssetsController_unsubscribeBySubscriptionKey, _AssetsController_subscribeStakedBalance, _AssetsController_buildChainToAccountsMap, _AssetsController_subscribeDataSource, _AssetsController_unsubscribeDataSource, _AssetsController_buildDataRequest, _AssetsController_getEnabledChainsForAccount, _AssetsController_handleAccountGroupChanged, _AssetsController_handleEnabledNetworksChanged, _AssetsController_handleNetworkAdded, _AssetsController_refreshAssetsAfterNetworkChange;
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");
@@ -255,8 +255,6 @@ class AssetsController extends base_controller_1.BaseController {
255
255
  /** Whether the keyring is unlocked. Combined with #uiOpen for #updateActive. */
256
256
  _AssetsController_keyringUnlocked.set(this, false);
257
257
  _AssetsController_controllerMutex.set(this, new async_mutex_1.Mutex());
258
- /** Serializes account-switch fetch + subscribe to prevent overlapping races. */
259
- _AssetsController_accountRefreshMutex.set(this, new async_mutex_1.Mutex());
260
258
  /**
261
259
  * Active balance subscriptions keyed by account ID.
262
260
  * Each account has one logical subscription that may span multiple data sources.
@@ -420,9 +418,9 @@ class AssetsController extends base_controller_1.BaseController {
420
418
  // creation, RPC is slow on many chains). Results are committed to state
421
419
  // immediately so the UI can display balances without waiting for them.
422
420
  //
423
- // Fast/slow pipelines use 'update' so partial API snapshots cannot wipe
424
- // tokens missing from the response (e.g. USDC when only native balance
425
- // is returned). Balances present in the response are still refreshed.
421
+ // Both the fast and background pipelines use 'merge' mode because neither
422
+ // alone represents the full set of data sources. Using 'full' in either
423
+ // would wipe balances from the sources handled by the other pipeline.
426
424
  const fastSources = __classPrivateFieldGet(this, _AssetsController_isBasicFunctionality, "f").call(this)
427
425
  ? [
428
426
  (0, ParallelMiddleware_1.createParallelBalanceMiddleware)([
@@ -442,7 +440,10 @@ class AssetsController extends base_controller_1.BaseController {
442
440
  ]
443
441
  : [__classPrivateFieldGet(this, _AssetsController_stakedBalanceDataSource, "f"), __classPrivateFieldGet(this, _AssetsController_detectionMiddleware, "f")];
444
442
  const { response, durationByDataSource } = await __classPrivateFieldGet(this, _AssetsController_instances, "m", _AssetsController_executeMiddlewares).call(this, fastSources, request);
445
- await __classPrivateFieldGet(this, _AssetsController_instances, "m", _AssetsController_updateState).call(this, { ...response, updateMode: 'update' });
443
+ // The fast pipeline only contains a subset of data sources (AccountsApi +
444
+ // StakedBalance), so it must always merge to avoid wiping Snap/RPC
445
+ // balances that the background pipeline hasn't yet replaced.
446
+ await __classPrivateFieldGet(this, _AssetsController_instances, "m", _AssetsController_updateState).call(this, { ...response, updateMode: 'merge' });
446
447
  // Background pipeline: snap and RPC run in parallel after the fast path
447
448
  // commits to state. Their balances are merged together before detection.
448
449
  // Token + price enrichment matches the pre-split behavior: only when basic
@@ -462,7 +463,7 @@ class AssetsController extends base_controller_1.BaseController {
462
463
  ]
463
464
  : []),
464
465
  ], request)
465
- .then(({ response: slowResponse }) => __classPrivateFieldGet(this, _AssetsController_instances, "m", _AssetsController_updateState).call(this, { ...slowResponse, updateMode: 'update' }))
466
+ .then(({ response: slowResponse }) => __classPrivateFieldGet(this, _AssetsController_instances, "m", _AssetsController_updateState).call(this, { ...slowResponse, updateMode: 'merge' }))
466
467
  .catch((error) => log('Background pipeline failed', { error }));
467
468
  const durationMs = performance.now() - startTime;
468
469
  // Emit trace for every full fetch (Assets Health dashboard)
@@ -917,7 +918,7 @@ class AssetsController extends base_controller_1.BaseController {
917
918
  }
918
919
  }
919
920
  exports.AssetsController = AssetsController;
920
- _AssetsController_isEnabled = new WeakMap(), _AssetsController_isBasicFunctionality = new WeakMap(), _AssetsController_defaultUpdateInterval = new WeakMap(), _AssetsController_trace = new WeakMap(), _AssetsController_captureException = new WeakMap(), _AssetsController_firstInitFetchReported = new WeakMap(), _AssetsController_stateSizeReported = new WeakMap(), _AssetsController_uiOpen = new WeakMap(), _AssetsController_keyringUnlocked = new WeakMap(), _AssetsController_controllerMutex = new WeakMap(), _AssetsController_accountRefreshMutex = new WeakMap(), _AssetsController_activeSubscriptions = new WeakMap(), _AssetsController_enabledChains = new WeakMap(), _AssetsController_lastKnownAccountIds = 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_customAssetGraduationMiddleware = new WeakMap(), _AssetsController_rpcFallbackMiddleware = new WeakMap(), _AssetsController_tokenDataSource = new WeakMap(), _AssetsController_unsubscribeBasicFunctionality = new WeakMap(), _AssetsController_queryApiClient = new WeakMap(), _AssetsController_onActiveChainsUpdated = new WeakMap(), _AssetsController_instances = new WeakSet(), _AssetsController_emitTrace = function _AssetsController_emitTrace(name, data, tags = {
921
+ _AssetsController_isEnabled = new WeakMap(), _AssetsController_isBasicFunctionality = new WeakMap(), _AssetsController_defaultUpdateInterval = new WeakMap(), _AssetsController_trace = new WeakMap(), _AssetsController_captureException = 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_lastKnownAccountIds = 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_customAssetGraduationMiddleware = new WeakMap(), _AssetsController_rpcFallbackMiddleware = new WeakMap(), _AssetsController_tokenDataSource = new WeakMap(), _AssetsController_unsubscribeBasicFunctionality = new WeakMap(), _AssetsController_queryApiClient = new WeakMap(), _AssetsController_onActiveChainsUpdated = new WeakMap(), _AssetsController_instances = new WeakSet(), _AssetsController_emitTrace = function _AssetsController_emitTrace(name, data, tags = {
921
922
  controller: 'AssetsController',
922
923
  }) {
923
924
  if (!__classPrivateFieldGet(this, _AssetsController_trace, "f")) {
@@ -1124,39 +1125,23 @@ _AssetsController_isEnabled = new WeakMap(), _AssetsController_isBasicFunctional
1124
1125
  if (!accountsChanged) {
1125
1126
  return;
1126
1127
  }
1127
- const hasOverlap = [...currentIds].some((id) => __classPrivateFieldGet(this, _AssetsController_lastKnownAccountIds, "f").has(id));
1128
- if (!hasOverlap && __classPrivateFieldGet(this, _AssetsController_lastKnownAccountIds, "f").size > 0) {
1129
- return;
1130
- }
1131
1128
  log('Account tree changed with new accounts, re-subscribing', {
1132
1129
  previousCount: __classPrivateFieldGet(this, _AssetsController_lastKnownAccountIds, "f").size,
1133
1130
  currentCount: currentIds.size,
1134
1131
  });
1135
1132
  __classPrivateFieldSet(this, _AssetsController_lastKnownAccountIds, currentIds, "f");
1133
+ __classPrivateFieldGet(this, _AssetsController_instances, "m", _AssetsController_subscribeAssets).call(this);
1136
1134
  __classPrivateFieldGet(this, _AssetsController_instances, "m", _AssetsController_ensureNativeBalancesDefaultZero).call(this);
1137
1135
  __classPrivateFieldGet(this, _AssetsController_instances, "m", _AssetsController_ensureDefaultTrackedAssetsSeeded).call(this);
1138
- __classPrivateFieldGet(this, _AssetsController_instances, "m", _AssetsController_runAccountTreeRefresh).call(this, accounts).catch((error) => {
1139
- log('Failed to refresh assets after tree change', error);
1140
- });
1141
- }
1142
- else {
1143
- __classPrivateFieldGet(this, _AssetsController_instances, "m", _AssetsController_start).call(this);
1144
- }
1145
- }, _AssetsController_runAccountTreeRefresh = async function _AssetsController_runAccountTreeRefresh(accounts) {
1146
- const releaseLock = await __classPrivateFieldGet(this, _AssetsController_accountRefreshMutex, "f").acquire();
1147
- try {
1148
- await this.getAssets(accounts, {
1136
+ this.getAssets(accounts, {
1149
1137
  chainIds: [...__classPrivateFieldGet(this, _AssetsController_enabledChains, "f")],
1150
1138
  forceUpdate: true,
1139
+ }).catch((error) => {
1140
+ log('Failed to fetch assets after tree change', error);
1151
1141
  });
1152
- __classPrivateFieldGet(this, _AssetsController_instances, "m", _AssetsController_subscribeAssets).call(this);
1153
1142
  }
1154
- catch (error) {
1155
- log('Failed to fetch assets after tree change', error);
1156
- __classPrivateFieldGet(this, _AssetsController_instances, "m", _AssetsController_subscribeAssets).call(this);
1157
- }
1158
- finally {
1159
- releaseLock();
1143
+ else {
1144
+ __classPrivateFieldGet(this, _AssetsController_instances, "m", _AssetsController_start).call(this);
1160
1145
  }
1161
1146
  }, _AssetsController_registerActionHandlers = function _AssetsController_registerActionHandlers() {
1162
1147
  this.messenger.registerMethodActionHandlers(this, MESSENGER_EXPOSED_METHODS);
@@ -1397,43 +1382,29 @@ async function _AssetsController_executeMiddlewares(sources, request, initialRes
1397
1382
  const balances = state.assetsBalance;
1398
1383
  const prices = state.assetsPrice;
1399
1384
  if (normalizedResponse.assetsInfo) {
1400
- if (mode === 'update') {
1401
- // Balance-only refresh: patch amounts without overwriting metadata,
1402
- // but seed entries that are missing so RPC-only chains (e.g.
1403
- // Avalanche) can render native balances on first fetch.
1404
- for (const [key, value] of Object.entries(normalizedResponse.assetsInfo)) {
1405
- if (metadata[key]) {
1406
- continue;
1407
- }
1408
- metadata[key] = value;
1385
+ for (const [key, value] of Object.entries(normalizedResponse.assetsInfo)) {
1386
+ if (!(0, lodash_1.isEqual)(previousState.assetsInfo[key], value)) {
1409
1387
  changedMetadata.push(key);
1410
1388
  }
1411
- }
1412
- else {
1413
- for (const [key, value] of Object.entries(normalizedResponse.assetsInfo)) {
1414
- if (!(0, lodash_1.isEqual)(previousState.assetsInfo[key], value)) {
1415
- changedMetadata.push(key);
1416
- }
1417
- const existing = metadata[key];
1418
- const incoming = value;
1419
- // When the API returns no symbol or name for this token, it has no
1420
- // real knowledge of it (e.g. a user-deployed token not indexed by
1421
- // the API). Preserve richer metadata already in state (e.g. from
1422
- // pendingMetadata set by addCustomAsset) so that the correct
1423
- // decimals/symbol/name/image are not overwritten with empty values.
1424
- if (existing && !incoming.symbol && !incoming.name) {
1425
- metadata[key] = {
1426
- ...existing,
1427
- ...incoming,
1428
- symbol: existing.symbol,
1429
- name: existing.name,
1430
- decimals: existing.decimals ?? incoming.decimals,
1431
- image: existing.image ?? incoming.image,
1432
- };
1433
- }
1434
- else {
1435
- metadata[key] = value;
1436
- }
1389
+ const existing = metadata[key];
1390
+ const incoming = value;
1391
+ // When the API returns no symbol or name for this token, it has no
1392
+ // real knowledge of it (e.g. a user-deployed token not indexed by
1393
+ // the API). Preserve richer metadata already in state (e.g. from
1394
+ // pendingMetadata set by addCustomAsset) so that the correct
1395
+ // decimals/symbol/name/image are not overwritten with empty values.
1396
+ if (existing && !incoming.symbol && !incoming.name) {
1397
+ metadata[key] = {
1398
+ ...existing,
1399
+ ...incoming,
1400
+ symbol: existing.symbol,
1401
+ name: existing.name,
1402
+ decimals: existing.decimals ?? incoming.decimals,
1403
+ image: existing.image ?? incoming.image,
1404
+ };
1405
+ }
1406
+ else {
1407
+ metadata[key] = value;
1437
1408
  }
1438
1409
  }
1439
1410
  }
@@ -1464,9 +1435,8 @@ async function _AssetsController_executeMiddlewares(sources, request, initialRes
1464
1435
  // balances for chains not in the response are preserved from
1465
1436
  // previous state so unsupported chains (e.g. Ink on AccountsAPI)
1466
1437
  // are never inadvertently reset to zero.
1467
- // Merge / update: response overlays previous balances (update never
1468
- // removes tokens missing from a partial snapshot).
1469
- const effective = mode === 'merge' || mode === 'update'
1438
+ // Merge: response overlays previous balances.
1439
+ const effective = mode === 'merge'
1470
1440
  ? { ...previousBalances, ...accountBalances }
1471
1441
  : (() => {
1472
1442
  // Determine which chain namespaces this response covers.
@@ -1529,21 +1499,10 @@ async function _AssetsController_executeMiddlewares(sources, request, initialRes
1529
1499
  balances[accountId] = effective;
1530
1500
  }
1531
1501
  }
1532
- // Update prices in state. Update mode only seeds missing entries so
1533
- // balance-only refreshes do not clobber existing price data.
1502
+ // Update prices in state
1534
1503
  if (normalizedResponse.assetsPrice) {
1535
- if (mode === 'update') {
1536
- for (const [key, value] of Object.entries(normalizedResponse.assetsPrice)) {
1537
- if (prices[key]) {
1538
- continue;
1539
- }
1540
- prices[key] = value;
1541
- }
1542
- }
1543
- else {
1544
- for (const [key, value] of Object.entries(normalizedResponse.assetsPrice)) {
1545
- prices[key] = value;
1546
- }
1504
+ for (const [key, value] of Object.entries(normalizedResponse.assetsPrice)) {
1505
+ prices[key] = value;
1547
1506
  }
1548
1507
  }
1549
1508
  });
@@ -1973,22 +1932,16 @@ async function _AssetsController_handleAccountGroupChanged() {
1973
1932
  accountIds: accounts.map((a) => a.id),
1974
1933
  });
1975
1934
  __classPrivateFieldSet(this, _AssetsController_lastKnownAccountIds, new Set(accounts.map((a) => a.id)), "f");
1976
- const releaseLock = await __classPrivateFieldGet(this, _AssetsController_accountRefreshMutex, "f").acquire();
1977
- try {
1978
- if (accounts.length > 0) {
1979
- await this.getAssets(accounts, {
1980
- chainIds: [...__classPrivateFieldGet(this, _AssetsController_enabledChains, "f")],
1981
- forceUpdate: true,
1982
- });
1983
- }
1984
- // Subscribe after fetch so WS notifications can recover state
1985
- __classPrivateFieldGet(this, _AssetsController_instances, "m", _AssetsController_subscribeAssets).call(this);
1986
- __classPrivateFieldGet(this, _AssetsController_instances, "m", _AssetsController_ensureNativeBalancesDefaultZero).call(this);
1987
- __classPrivateFieldGet(this, _AssetsController_instances, "m", _AssetsController_ensureDefaultTrackedAssetsSeeded).call(this);
1988
- }
1989
- finally {
1990
- releaseLock();
1935
+ // Subscribe and fetch for the new account group
1936
+ __classPrivateFieldGet(this, _AssetsController_instances, "m", _AssetsController_subscribeAssets).call(this);
1937
+ if (accounts.length > 0) {
1938
+ await this.getAssets(accounts, {
1939
+ chainIds: [...__classPrivateFieldGet(this, _AssetsController_enabledChains, "f")],
1940
+ forceUpdate: true,
1941
+ });
1991
1942
  }
1943
+ __classPrivateFieldGet(this, _AssetsController_instances, "m", _AssetsController_ensureNativeBalancesDefaultZero).call(this);
1944
+ __classPrivateFieldGet(this, _AssetsController_instances, "m", _AssetsController_ensureDefaultTrackedAssetsSeeded).call(this);
1992
1945
  }, _AssetsController_handleEnabledNetworksChanged = async function _AssetsController_handleEnabledNetworksChanged(enabledNetworkMap) {
1993
1946
  const previousChains = __classPrivateFieldGet(this, _AssetsController_enabledChains, "f");
1994
1947
  __classPrivateFieldSet(this, _AssetsController_enabledChains, __classPrivateFieldGet(this, _AssetsController_instances, "m", _AssetsController_extractEnabledChains).call(this, enabledNetworkMap), "f");