@metamask-previews/assets-controller 6.1.0-preview-f3d7baae5 → 6.2.0-preview-495c91e

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (116) hide show
  1. package/CHANGELOG.md +25 -4
  2. package/dist/AssetsController.cjs +151 -15
  3. package/dist/AssetsController.cjs.map +1 -1
  4. package/dist/AssetsController.d.cts +8 -1
  5. package/dist/AssetsController.d.cts.map +1 -1
  6. package/dist/AssetsController.d.mts +8 -1
  7. package/dist/AssetsController.d.mts.map +1 -1
  8. package/dist/AssetsController.mjs +151 -15
  9. package/dist/AssetsController.mjs.map +1 -1
  10. package/dist/data-sources/AccountsApiDataSource.cjs +6 -3
  11. package/dist/data-sources/AccountsApiDataSource.cjs.map +1 -1
  12. package/dist/data-sources/AccountsApiDataSource.d.cts +6 -0
  13. package/dist/data-sources/AccountsApiDataSource.d.cts.map +1 -1
  14. package/dist/data-sources/AccountsApiDataSource.d.mts +6 -0
  15. package/dist/data-sources/AccountsApiDataSource.d.mts.map +1 -1
  16. package/dist/data-sources/AccountsApiDataSource.mjs +7 -4
  17. package/dist/data-sources/AccountsApiDataSource.mjs.map +1 -1
  18. package/dist/data-sources/PriceDataSource.cjs +12 -8
  19. package/dist/data-sources/PriceDataSource.cjs.map +1 -1
  20. package/dist/data-sources/PriceDataSource.d.cts +5 -0
  21. package/dist/data-sources/PriceDataSource.d.cts.map +1 -1
  22. package/dist/data-sources/PriceDataSource.d.mts +5 -0
  23. package/dist/data-sources/PriceDataSource.d.mts.map +1 -1
  24. package/dist/data-sources/PriceDataSource.mjs +12 -8
  25. package/dist/data-sources/PriceDataSource.mjs.map +1 -1
  26. package/dist/data-sources/RpcDataSource.cjs +9 -2
  27. package/dist/data-sources/RpcDataSource.cjs.map +1 -1
  28. package/dist/data-sources/RpcDataSource.d.cts.map +1 -1
  29. package/dist/data-sources/RpcDataSource.d.mts.map +1 -1
  30. package/dist/data-sources/RpcDataSource.mjs +9 -2
  31. package/dist/data-sources/RpcDataSource.mjs.map +1 -1
  32. package/dist/data-sources/TokenDataSource.cjs +8 -4
  33. package/dist/data-sources/TokenDataSource.cjs.map +1 -1
  34. package/dist/data-sources/TokenDataSource.d.cts +5 -0
  35. package/dist/data-sources/TokenDataSource.d.cts.map +1 -1
  36. package/dist/data-sources/TokenDataSource.d.mts +5 -0
  37. package/dist/data-sources/TokenDataSource.d.mts.map +1 -1
  38. package/dist/data-sources/TokenDataSource.mjs +8 -4
  39. package/dist/data-sources/TokenDataSource.mjs.map +1 -1
  40. package/dist/data-sources/evm-rpc-services/services/BalanceFetcher.cjs +40 -22
  41. package/dist/data-sources/evm-rpc-services/services/BalanceFetcher.cjs.map +1 -1
  42. package/dist/data-sources/evm-rpc-services/services/BalanceFetcher.d.cts +7 -0
  43. package/dist/data-sources/evm-rpc-services/services/BalanceFetcher.d.cts.map +1 -1
  44. package/dist/data-sources/evm-rpc-services/services/BalanceFetcher.d.mts +7 -0
  45. package/dist/data-sources/evm-rpc-services/services/BalanceFetcher.d.mts.map +1 -1
  46. package/dist/data-sources/evm-rpc-services/services/BalanceFetcher.mjs +40 -22
  47. package/dist/data-sources/evm-rpc-services/services/BalanceFetcher.mjs.map +1 -1
  48. package/dist/data-sources/evm-rpc-services/types/state.cjs.map +1 -1
  49. package/dist/data-sources/evm-rpc-services/types/state.d.cts +6 -0
  50. package/dist/data-sources/evm-rpc-services/types/state.d.cts.map +1 -1
  51. package/dist/data-sources/evm-rpc-services/types/state.d.mts +6 -0
  52. package/dist/data-sources/evm-rpc-services/types/state.d.mts.map +1 -1
  53. package/dist/data-sources/evm-rpc-services/types/state.mjs.map +1 -1
  54. package/dist/errors.cjs +25 -0
  55. package/dist/errors.cjs.map +1 -0
  56. package/dist/errors.d.cts +25 -0
  57. package/dist/errors.d.cts.map +1 -0
  58. package/dist/errors.d.mts +25 -0
  59. package/dist/errors.d.mts.map +1 -0
  60. package/dist/errors.mjs +21 -0
  61. package/dist/errors.mjs.map +1 -0
  62. package/dist/index.cjs +5 -1
  63. package/dist/index.cjs.map +1 -1
  64. package/dist/index.d.cts +3 -1
  65. package/dist/index.d.cts.map +1 -1
  66. package/dist/index.d.mts +3 -1
  67. package/dist/index.d.mts.map +1 -1
  68. package/dist/index.mjs +2 -1
  69. package/dist/index.mjs.map +1 -1
  70. package/dist/middlewares/CustomAssetGraduationMiddleware.cjs +121 -0
  71. package/dist/middlewares/CustomAssetGraduationMiddleware.cjs.map +1 -0
  72. package/dist/middlewares/CustomAssetGraduationMiddleware.d.cts +25 -0
  73. package/dist/middlewares/CustomAssetGraduationMiddleware.d.cts.map +1 -0
  74. package/dist/middlewares/CustomAssetGraduationMiddleware.d.mts +25 -0
  75. package/dist/middlewares/CustomAssetGraduationMiddleware.d.mts.map +1 -0
  76. package/dist/middlewares/CustomAssetGraduationMiddleware.mjs +117 -0
  77. package/dist/middlewares/CustomAssetGraduationMiddleware.mjs.map +1 -0
  78. package/dist/middlewares/RpcFallbackMiddleware.cjs +90 -0
  79. package/dist/middlewares/RpcFallbackMiddleware.cjs.map +1 -0
  80. package/dist/middlewares/RpcFallbackMiddleware.d.cts +23 -0
  81. package/dist/middlewares/RpcFallbackMiddleware.d.cts.map +1 -0
  82. package/dist/middlewares/RpcFallbackMiddleware.d.mts +23 -0
  83. package/dist/middlewares/RpcFallbackMiddleware.d.mts.map +1 -0
  84. package/dist/middlewares/RpcFallbackMiddleware.mjs +86 -0
  85. package/dist/middlewares/RpcFallbackMiddleware.mjs.map +1 -0
  86. package/dist/middlewares/index.cjs +5 -1
  87. package/dist/middlewares/index.cjs.map +1 -1
  88. package/dist/middlewares/index.d.cts +4 -0
  89. package/dist/middlewares/index.d.cts.map +1 -1
  90. package/dist/middlewares/index.d.mts +4 -0
  91. package/dist/middlewares/index.d.mts.map +1 -1
  92. package/dist/middlewares/index.mjs +2 -0
  93. package/dist/middlewares/index.mjs.map +1 -1
  94. package/dist/types.cjs.map +1 -1
  95. package/dist/types.d.cts +7 -0
  96. package/dist/types.d.cts.map +1 -1
  97. package/dist/types.d.mts +7 -0
  98. package/dist/types.d.mts.map +1 -1
  99. package/dist/types.mjs.map +1 -1
  100. package/dist/utils/fetchWithTimeout.cjs +30 -0
  101. package/dist/utils/fetchWithTimeout.cjs.map +1 -0
  102. package/dist/utils/fetchWithTimeout.d.cts +11 -0
  103. package/dist/utils/fetchWithTimeout.d.cts.map +1 -0
  104. package/dist/utils/fetchWithTimeout.d.mts +11 -0
  105. package/dist/utils/fetchWithTimeout.d.mts.map +1 -0
  106. package/dist/utils/fetchWithTimeout.mjs +26 -0
  107. package/dist/utils/fetchWithTimeout.mjs.map +1 -0
  108. package/dist/utils/index.cjs +3 -1
  109. package/dist/utils/index.cjs.map +1 -1
  110. package/dist/utils/index.d.cts +1 -0
  111. package/dist/utils/index.d.cts.map +1 -1
  112. package/dist/utils/index.d.mts +1 -0
  113. package/dist/utils/index.d.mts.map +1 -1
  114. package/dist/utils/index.mjs +1 -0
  115. package/dist/utils/index.mjs.map +1 -1
  116. package/package.json +2 -2
package/CHANGELOG.md CHANGED
@@ -7,6 +7,27 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
7
7
 
8
8
  ## [Unreleased]
9
9
 
10
+ ## [6.2.0]
11
+
12
+ ### Added
13
+
14
+ - Add `CustomAssetGraduationMiddleware` that removes an EVM asset from `customAssets[selectedAccount]` when `AccountsApiDataSource` or `BackendWebsocketDataSource` reports a balance for it ([#8582](https://github.com/MetaMask/core/pull/8582))
15
+ - Non-EVM custom assets (Solana, BTC, Tron) are not affected.
16
+ - `RpcDataSource` continues to be the sole balance fetcher for assets still in `customAssets`.
17
+ - Add `RpcFallbackMiddleware` to the fast pipeline so chains that error in `response.errors` (network error, `unprocessedNetworks`, timeout) fall back to `RpcDataSource` ([#8582](https://github.com/MetaMask/core/pull/8582))
18
+ - Successful RPC results are merged into the response and recovered chains are cleared from `response.errors`.
19
+ - Add a configurable `fetchTimeoutMs` option (default `15000`) to `AccountsApiDataSource`, `PriceDataSource`, and `TokenDataSource` ([#8582](https://github.com/MetaMask/core/pull/8582))
20
+ - On timeout, AccountsAPI marks every requested chain as errored so `RpcFallbackMiddleware` picks them up; price and token enrichment degrade gracefully.
21
+ - Add `fetchWithTimeout` utility that races an async task against a timeout ([#8582](https://github.com/MetaMask/core/pull/8582))
22
+
23
+ ### Changed
24
+
25
+ - Bump `@metamask/transaction-controller` from `^64.3.0` to `^64.4.0` ([#8585](https://github.com/MetaMask/core/pull/8585))
26
+
27
+ ### Fixed
28
+
29
+ - Handle trace callback returning `undefined` without throwing ([#8586](https://github.com/MetaMask/core/pull/8586))
30
+
10
31
  ## [6.1.0]
11
32
 
12
33
  ### Added
@@ -211,9 +232,9 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
211
232
 
212
233
  ### Changed
213
234
 
214
- - Bump `@metamask/assets-controllers` from `^100.0.3` to `^100.2.0` ([#8107](https://github.com/MetaMask/core/pull/8107)), ([#8140](https://github.com/MetaMask/core/pull/8140))
235
+ - Bump `@metamask/assets-controllers` from `^100.0.3` to `^100.2.0`, ([#8107](https://github.com/MetaMask/core/pull/8107), [#8140](https://github.com/MetaMask/core/pull/8140))
215
236
  - Bump `@metamask/network-enablement-controller` from `^4.1.2` to `^4.2.0` ([#8107](https://github.com/MetaMask/core/pull/8107))
216
- - Bump `@metamask/transaction-controller` from `^62.19.0` to `^62.21.0` ([#8104](https://github.com/MetaMask/core/pull/8104)), ([#8140](https://github.com/MetaMask/core/pull/8140))
237
+ - Bump `@metamask/transaction-controller` from `^62.19.0` to `^62.21.0`, ([#8104](https://github.com/MetaMask/core/pull/8104), [#8140](https://github.com/MetaMask/core/pull/8140))
217
238
  - Bump `@metamask/account-tree-controller` from `^4.1.1` to `^5.0.0` ([#8140](https://github.com/MetaMask/core/pull/8140))
218
239
  - Bump `@metamask/core-backend` from `^6.0.0` to `^6.1.0` ([#8140](https://github.com/MetaMask/core/pull/8140))
219
240
  - Bump `@metamask/preferences-controller` from `^22.1.0` to `^23.0.0` ([#8140](https://github.com/MetaMask/core/pull/8140))
@@ -326,7 +347,6 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
326
347
  ### Changed
327
348
 
328
349
  - Narrow `AssetsControllerState` types from `Json` to semantic types: `assetsMetadata` → `AssetMetadata`, `assetsBalance` → `AssetBalance`, `assetsPrice` → `AssetPrice`, `assetPreferences` → `AssetPreferences`, `customAssets` → `Caip19AssetId[]` ([#7777](https://github.com/MetaMask/core/pull/7777))
329
-
330
350
  - Replace `viem` dependency with `@ethersproject/abi` for ABI encoding/decoding in `MulticallClient` ([#7839](https://github.com/MetaMask/core/pull/7839))
331
351
 
332
352
  ## [0.1.0]
@@ -353,7 +373,8 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
353
373
  - Refactor `RpcDataSource` to delegate polling to `BalanceFetcher` and `TokenDetector` services ([#7709](https://github.com/MetaMask/core/pull/7709))
354
374
  - Refactor `BalanceFetcher` and `TokenDetector` to extend `StaticIntervalPollingControllerOnly` for independent polling management ([#7709](https://github.com/MetaMask/core/pull/7709))
355
375
 
356
- [Unreleased]: https://github.com/MetaMask/core/compare/@metamask/assets-controller@6.1.0...HEAD
376
+ [Unreleased]: https://github.com/MetaMask/core/compare/@metamask/assets-controller@6.2.0...HEAD
377
+ [6.2.0]: https://github.com/MetaMask/core/compare/@metamask/assets-controller@6.1.0...@metamask/assets-controller@6.2.0
357
378
  [6.1.0]: https://github.com/MetaMask/core/compare/@metamask/assets-controller@6.0.0...@metamask/assets-controller@6.1.0
358
379
  [6.0.0]: https://github.com/MetaMask/core/compare/@metamask/assets-controller@5.0.1...@metamask/assets-controller@6.0.0
359
380
  [5.0.1]: https://github.com/MetaMask/core/compare/@metamask/assets-controller@5.0.0...@metamask/assets-controller@5.0.1
@@ -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_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_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_resolveNativeAssetIds, _AssetsController_getNativeAssetIdsForEnabledChains, _AssetsController_getNativeAssetIdsForAccount, _AssetsController_ensureNativeBalancesDefaultZero, _AssetsController_updateState, _AssetsController_getAssetsFromState, _AssetsController_shouldHideNativeToken, _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_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_resolveNativeAssetIds, _AssetsController_getNativeAssetIdsForEnabledChains, _AssetsController_getNativeAssetIdsForAccount, _AssetsController_ensureNativeBalancesDefaultZero, _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;
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");
@@ -30,9 +30,12 @@ const RpcDataSource_1 = require("./data-sources/RpcDataSource.cjs");
30
30
  const SnapDataSource_1 = require("./data-sources/SnapDataSource.cjs");
31
31
  const StakedBalanceDataSource_1 = require("./data-sources/StakedBalanceDataSource.cjs");
32
32
  const TokenDataSource_1 = require("./data-sources/TokenDataSource.cjs");
33
+ const errors_1 = require("./errors.cjs");
33
34
  const logger_1 = require("./logger.cjs");
35
+ const CustomAssetGraduationMiddleware_1 = require("./middlewares/CustomAssetGraduationMiddleware.cjs");
34
36
  const DetectionMiddleware_1 = require("./middlewares/DetectionMiddleware.cjs");
35
37
  const ParallelMiddleware_1 = require("./middlewares/ParallelMiddleware.cjs");
38
+ const RpcFallbackMiddleware_1 = require("./middlewares/RpcFallbackMiddleware.cjs");
36
39
  const utils_2 = require("./utils/index.cjs");
37
40
  const constants_1 = require("./utils/constants.cjs");
38
41
  const NATIVE_ASSETS_QUERY_KEY = ['nativeAssets'];
@@ -212,7 +215,7 @@ function normalizeResponse(response) {
212
215
  * - The controller does NOT manage polling - it simply receives pushed updates
213
216
  */
214
217
  class AssetsController extends base_controller_1.BaseController {
215
- constructor({ messenger, state = {}, defaultUpdateInterval = DEFAULT_POLLING_INTERVAL_MS, isEnabled = () => true, isBasicFunctionality, subscribeToBasicFunctionalityChange, queryApiClient, rpcDataSourceConfig, trace, accountsApiDataSourceConfig, priceDataSourceConfig, stakedBalanceDataSourceConfig, isOnboarded, }) {
218
+ constructor({ messenger, state = {}, defaultUpdateInterval = DEFAULT_POLLING_INTERVAL_MS, isEnabled = () => true, isBasicFunctionality, subscribeToBasicFunctionalityChange, queryApiClient, rpcDataSourceConfig, trace, captureException, accountsApiDataSourceConfig, priceDataSourceConfig, stakedBalanceDataSourceConfig, isOnboarded, }) {
216
219
  super({
217
220
  name: CONTROLLER_NAME,
218
221
  messenger,
@@ -231,6 +234,8 @@ class AssetsController extends base_controller_1.BaseController {
231
234
  _AssetsController_defaultUpdateInterval.set(this, void 0);
232
235
  /** Optional trace callback for first init/fetch measurement (duration). */
233
236
  _AssetsController_trace.set(this, void 0);
237
+ /** Optional reporter for Issue-style errors (e.g. Sentry.captureException). */
238
+ _AssetsController_captureException.set(this, void 0);
234
239
  /** Whether we have already reported first init fetch for this session (reset on #stop). */
235
240
  _AssetsController_firstInitFetchReported.set(this, false);
236
241
  /** Whether we have already reported state size for this session (reset on #stop). */
@@ -262,6 +267,8 @@ class AssetsController extends base_controller_1.BaseController {
262
267
  _AssetsController_stakedBalanceDataSource.set(this, void 0);
263
268
  _AssetsController_priceDataSource.set(this, void 0);
264
269
  _AssetsController_detectionMiddleware.set(this, void 0);
270
+ _AssetsController_customAssetGraduationMiddleware.set(this, void 0);
271
+ _AssetsController_rpcFallbackMiddleware.set(this, void 0);
265
272
  _AssetsController_tokenDataSource.set(this, void 0);
266
273
  _AssetsController_unsubscribeBasicFunctionality.set(this, null);
267
274
  _AssetsController_queryApiClient.set(this, void 0);
@@ -270,6 +277,7 @@ class AssetsController extends base_controller_1.BaseController {
270
277
  __classPrivateFieldSet(this, _AssetsController_isBasicFunctionality, isBasicFunctionality ?? (() => true), "f");
271
278
  __classPrivateFieldSet(this, _AssetsController_defaultUpdateInterval, defaultUpdateInterval, "f");
272
279
  __classPrivateFieldSet(this, _AssetsController_trace, trace, "f");
280
+ __classPrivateFieldSet(this, _AssetsController_captureException, captureException, "f");
273
281
  __classPrivateFieldSet(this, _AssetsController_queryApiClient, queryApiClient, "f");
274
282
  const rpcConfig = rpcDataSourceConfig ?? {};
275
283
  __classPrivateFieldGet(this, _AssetsController_instances, "m", _AssetsController_initializeNativeAssetsMap).call(this, queryApiClient);
@@ -324,6 +332,20 @@ class AssetsController extends base_controller_1.BaseController {
324
332
  ...priceDataSourceConfig,
325
333
  }), "f");
326
334
  __classPrivateFieldSet(this, _AssetsController_detectionMiddleware, new DetectionMiddleware_1.DetectionMiddleware(), "f");
335
+ __classPrivateFieldSet(this, _AssetsController_customAssetGraduationMiddleware, new CustomAssetGraduationMiddleware_1.CustomAssetGraduationMiddleware({
336
+ getSelectedAccountId: () => {
337
+ try {
338
+ return __classPrivateFieldGet(this, _AssetsController_instances, "m", _AssetsController_getSelectedAccounts).call(this)[0]?.id;
339
+ }
340
+ catch {
341
+ return undefined;
342
+ }
343
+ },
344
+ removeCustomAsset: (accountId, assetId) => this.removeCustomAsset(accountId, assetId),
345
+ }), "f");
346
+ __classPrivateFieldSet(this, _AssetsController_rpcFallbackMiddleware, new RpcFallbackMiddleware_1.RpcFallbackMiddleware({
347
+ rpcDataSource: __classPrivateFieldGet(this, _AssetsController_rpcDataSource, "f"),
348
+ }), "f");
327
349
  if (!__classPrivateFieldGet(this, _AssetsController_isEnabled, "f")) {
328
350
  log('AssetsController is disabled, skipping initialization');
329
351
  return;
@@ -392,6 +414,11 @@ class AssetsController extends base_controller_1.BaseController {
392
414
  __classPrivateFieldGet(this, _AssetsController_accountsApiDataSource, "f"),
393
415
  __classPrivateFieldGet(this, _AssetsController_stakedBalanceDataSource, "f"),
394
416
  ]),
417
+ // Graduation must run BEFORE the RPC fallback so it only sees
418
+ // AccountsApi/Websocket balances. RPC intentionally carries
419
+ // custom assets and must never trigger graduation.
420
+ __classPrivateFieldGet(this, _AssetsController_customAssetGraduationMiddleware, "f"),
421
+ __classPrivateFieldGet(this, _AssetsController_rpcFallbackMiddleware, "f"),
395
422
  __classPrivateFieldGet(this, _AssetsController_detectionMiddleware, "f"),
396
423
  (0, ParallelMiddleware_1.createParallelMiddleware)([
397
424
  __classPrivateFieldGet(this, _AssetsController_tokenDataSource, "f"),
@@ -603,6 +630,9 @@ class AssetsController extends base_controller_1.BaseController {
603
630
  updateMode: 'merge',
604
631
  });
605
632
  }
633
+ // Re-evaluate subscriptions so the supplemental RPC poll picks up the
634
+ // new customAsset on chains another data source already owns.
635
+ __classPrivateFieldGet(this, _AssetsController_instances, "m", _AssetsController_subscribeAssets).call(this);
606
636
  }
607
637
  /**
608
638
  * Remove a custom asset from an account.
@@ -622,6 +652,9 @@ class AssetsController extends base_controller_1.BaseController {
622
652
  }
623
653
  }
624
654
  });
655
+ // Re-evaluate subscriptions so the supplemental RPC poll for that chain
656
+ // is torn down when no more customAssets remain there.
657
+ __classPrivateFieldGet(this, _AssetsController_instances, "m", _AssetsController_subscribeAssets).call(this);
625
658
  }
626
659
  /**
627
660
  * Get all custom assets for an account.
@@ -795,7 +828,17 @@ class AssetsController extends base_controller_1.BaseController {
795
828
  ...resolvedRequest,
796
829
  dataTypes: resolvedRequest.dataTypes.filter((dt) => dt !== 'metadata' && dt !== 'price'),
797
830
  };
798
- const enrichmentSources = [__classPrivateFieldGet(this, _AssetsController_detectionMiddleware, "f")];
831
+ // Graduate custom assets only when AccountsAPI / Websocket reports them.
832
+ // RPC already fetches custom assets on purpose, and Snap handles non-EVM
833
+ // chains the rule does not apply to, so skip the middleware for those.
834
+ const shouldGraduateCustomAssets = sourceId === 'AccountsApiDataSource' ||
835
+ sourceId === 'BackendWebsocketDataSource';
836
+ const enrichmentSources = [
837
+ ...(shouldGraduateCustomAssets
838
+ ? [__classPrivateFieldGet(this, _AssetsController_customAssetGraduationMiddleware, "f")]
839
+ : []),
840
+ __classPrivateFieldGet(this, _AssetsController_detectionMiddleware, "f"),
841
+ ];
799
842
  if (__classPrivateFieldGet(this, _AssetsController_isBasicFunctionality, "f").call(this)) {
800
843
  enrichmentSources.push((0, ParallelMiddleware_1.createParallelMiddleware)([
801
844
  __classPrivateFieldGet(this, _AssetsController_tokenDataSource, "f"),
@@ -850,13 +893,13 @@ class AssetsController extends base_controller_1.BaseController {
850
893
  }
851
894
  }
852
895
  exports.AssetsController = AssetsController;
853
- _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_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_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 = {
896
+ _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 = {
854
897
  controller: 'AssetsController',
855
898
  }) {
856
899
  if (!__classPrivateFieldGet(this, _AssetsController_trace, "f")) {
857
900
  return;
858
901
  }
859
- __classPrivateFieldGet(this, _AssetsController_trace, "f").call(this, { name, data, tags }, () => undefined).catch(() => {
902
+ __classPrivateFieldGet(this, _AssetsController_trace, "f").call(this, { name, data, tags }, () => undefined)?.catch(() => {
860
903
  // Telemetry failure must not break.
861
904
  });
862
905
  }, _AssetsController_emitStateSizeTrace = function _AssetsController_emitStateSizeTrace() {
@@ -1160,12 +1203,28 @@ async function _AssetsController_executeMiddlewares(sources, request, initialRes
1160
1203
  account_count: request.accountsWithSupportedChains.length,
1161
1204
  });
1162
1205
  }
1163
- // Emit error traces for failed middlewares
1206
+ // Failed middlewares: Issues (optional) + perf/Dashboard spans
1164
1207
  if (middlewareErrors.length > 0) {
1208
+ const failedSources = middlewareErrors.join(',');
1209
+ const assetsError = new errors_1.AssetsDataSourceError({
1210
+ failedSources,
1211
+ errorCount: middlewareErrors.length,
1212
+ chainCount: request.chainIds.length,
1213
+ });
1214
+ try {
1215
+ __classPrivateFieldGet(this, _AssetsController_captureException, "f")?.call(this, assetsError);
1216
+ }
1217
+ catch {
1218
+ // Never let telemetry throw.
1219
+ }
1165
1220
  __classPrivateFieldGet(this, _AssetsController_instances, "m", _AssetsController_emitTrace).call(this, TRACE_DATA_SOURCE_ERROR, {
1166
- failed_sources: middlewareErrors.join(','),
1221
+ failed_sources: failedSources,
1167
1222
  error_count: middlewareErrors.length,
1168
1223
  chain_count: request.chainIds.length,
1224
+ }, {
1225
+ controller: 'AssetsController',
1226
+ severity: 'error',
1227
+ error_type: assetsError.name,
1169
1228
  });
1170
1229
  }
1171
1230
  return { response: result.response, durationByDataSource };
@@ -1507,12 +1566,20 @@ async function _AssetsController_executeMiddlewares(sources, request, initialRes
1507
1566
  ];
1508
1567
  const subscriptionKeys = [...__classPrivateFieldGet(this, _AssetsController_activeSubscriptions, "f").keys()];
1509
1568
  for (const subscriptionKey of subscriptionKeys) {
1510
- if (subscriptionKey.startsWith('ds:')) {
1511
- const sourceId = subscriptionKey.slice(3);
1512
- const source = allSources.find((ds) => ds.getName() === sourceId);
1513
- if (source) {
1514
- __classPrivateFieldGet(this, _AssetsController_instances, "m", _AssetsController_unsubscribeDataSource).call(this, source);
1515
- }
1569
+ if (!subscriptionKey.startsWith('ds:')) {
1570
+ continue;
1571
+ }
1572
+ // Subscription keys take the form `ds:<SourceName>` for the regular
1573
+ // subscription or `ds:<SourceName>:<suffix>` for supplemental
1574
+ // subscriptions (e.g. `ds:RpcDataSource:custom`). Split on `:` and
1575
+ // pick the source-name segment so both shapes resolve correctly.
1576
+ const [, sourceId] = subscriptionKey.split(':');
1577
+ const source = allSources.find((ds) => ds.getName() === sourceId);
1578
+ if (source) {
1579
+ // Unsubscribe by the actual key — `#unsubscribeDataSource` only
1580
+ // knows the regular `ds:<SourceName>` shape and would miss
1581
+ // supplemental subscriptions, leaking their polling timers.
1582
+ __classPrivateFieldGet(this, _AssetsController_instances, "m", _AssetsController_unsubscribeBySubscriptionKey).call(this, source, subscriptionKey);
1516
1583
  }
1517
1584
  }
1518
1585
  __classPrivateFieldGet(this, _AssetsController_activeSubscriptions, "f").clear();
@@ -1535,6 +1602,7 @@ async function _AssetsController_executeMiddlewares(sources, request, initialRes
1535
1602
  const balanceDataSources = __classPrivateFieldGet(this, _AssetsController_isBasicFunctionality, "f").call(this)
1536
1603
  ? __classPrivateFieldGet(this, _AssetsController_instances, "a", _AssetsController_allBalanceDataSources_get)
1537
1604
  : [__classPrivateFieldGet(this, _AssetsController_rpcDataSource, "f")];
1605
+ let rpcAssignedChains = new Set();
1538
1606
  for (const source of balanceDataSources) {
1539
1607
  const availableChains = new Set(source.getActiveChainsSync());
1540
1608
  const assignedChains = [];
@@ -1548,6 +1616,9 @@ async function _AssetsController_executeMiddlewares(sources, request, initialRes
1548
1616
  __classPrivateFieldGet(this, _AssetsController_instances, "m", _AssetsController_unsubscribeDataSource).call(this, source);
1549
1617
  continue;
1550
1618
  }
1619
+ if (source === __classPrivateFieldGet(this, _AssetsController_rpcDataSource, "f")) {
1620
+ rpcAssignedChains = new Set(assignedChains);
1621
+ }
1551
1622
  const seenIds = new Set();
1552
1623
  const accountsForSource = assignedChains
1553
1624
  .flatMap((chainId) => chainToAccounts.get(chainId) ?? [])
@@ -1556,6 +1627,67 @@ async function _AssetsController_executeMiddlewares(sources, request, initialRes
1556
1627
  __classPrivateFieldGet(this, _AssetsController_instances, "m", _AssetsController_subscribeDataSource).call(this, source, accountsForSource, assignedChains);
1557
1628
  }
1558
1629
  }
1630
+ // Supplemental RPC subscription for customAssets on chains another data
1631
+ // source claimed during regular handoff. RPC is the sole balance fetcher
1632
+ // for customAssets, so we must always poll them — even when (e.g.)
1633
+ // AccountsApi is already covering the chain for normal balances. The
1634
+ // supplemental subscription runs in `customAssetsOnly` mode so it does
1635
+ // NOT double-poll the regular tracked balances.
1636
+ __classPrivateFieldGet(this, _AssetsController_instances, "m", _AssetsController_subscribeRpcCustomAssetsSupplement).call(this, accounts, chainToAccounts, rpcAssignedChains);
1637
+ }, _AssetsController_subscribeRpcCustomAssetsSupplement = function _AssetsController_subscribeRpcCustomAssetsSupplement(accounts, chainToAccounts, rpcAssignedChains) {
1638
+ const rpc = __classPrivateFieldGet(this, _AssetsController_rpcDataSource, "f");
1639
+ const rpcAvailableChains = new Set(rpc.getActiveChainsSync());
1640
+ // Collect chains that have customAssets for at least one of the given
1641
+ // accounts and are NOT already covered by the regular RPC subscription.
1642
+ const supplementalChainSet = new Set();
1643
+ const accountsWithCustomAssets = new Set();
1644
+ for (const account of accounts) {
1645
+ const customForAccount = this.state.customAssets[account.id] ?? [];
1646
+ if (customForAccount.length === 0) {
1647
+ continue;
1648
+ }
1649
+ accountsWithCustomAssets.add(account.id);
1650
+ for (const assetId of customForAccount) {
1651
+ let chainId;
1652
+ try {
1653
+ chainId = extractChainId(assetId);
1654
+ }
1655
+ catch {
1656
+ continue;
1657
+ }
1658
+ if (rpcAssignedChains.has(chainId)) {
1659
+ continue;
1660
+ }
1661
+ if (!rpcAvailableChains.has(chainId)) {
1662
+ continue;
1663
+ }
1664
+ if (!chainToAccounts.has(chainId)) {
1665
+ continue;
1666
+ }
1667
+ supplementalChainSet.add(chainId);
1668
+ }
1669
+ }
1670
+ const supplementalKey = `ds:${rpc.getName()}:custom`;
1671
+ if (supplementalChainSet.size === 0) {
1672
+ __classPrivateFieldGet(this, _AssetsController_instances, "m", _AssetsController_unsubscribeBySubscriptionKey).call(this, rpc, supplementalKey);
1673
+ return;
1674
+ }
1675
+ const supplementalChains = [...supplementalChainSet];
1676
+ const supplementalAccounts = accounts.filter((account) => accountsWithCustomAssets.has(account.id));
1677
+ if (supplementalAccounts.length === 0) {
1678
+ __classPrivateFieldGet(this, _AssetsController_instances, "m", _AssetsController_unsubscribeBySubscriptionKey).call(this, rpc, supplementalKey);
1679
+ return;
1680
+ }
1681
+ __classPrivateFieldGet(this, _AssetsController_instances, "m", _AssetsController_subscribeDataSource).call(this, rpc, supplementalAccounts, supplementalChains, {
1682
+ subscriptionKey: supplementalKey,
1683
+ customAssetsOnly: true,
1684
+ });
1685
+ }, _AssetsController_unsubscribeBySubscriptionKey = function _AssetsController_unsubscribeBySubscriptionKey(source, subscriptionKey) {
1686
+ const existingSubscription = __classPrivateFieldGet(this, _AssetsController_activeSubscriptions, "f").get(subscriptionKey);
1687
+ if (existingSubscription) {
1688
+ source.unsubscribe(subscriptionKey).catch(() => undefined);
1689
+ existingSubscription.unsubscribe();
1690
+ }
1559
1691
  }, _AssetsController_subscribeStakedBalance = function _AssetsController_subscribeStakedBalance(accounts, chainIds) {
1560
1692
  const source = __classPrivateFieldGet(this, _AssetsController_stakedBalanceDataSource, "f");
1561
1693
  if (!source) {
@@ -1584,9 +1716,9 @@ async function _AssetsController_executeMiddlewares(sources, request, initialRes
1584
1716
  }
1585
1717
  }
1586
1718
  return chainToAccounts;
1587
- }, _AssetsController_subscribeDataSource = function _AssetsController_subscribeDataSource(source, accounts, chains) {
1719
+ }, _AssetsController_subscribeDataSource = function _AssetsController_subscribeDataSource(source, accounts, chains, options = {}) {
1588
1720
  const sourceId = source.getName();
1589
- const subscriptionKey = `ds:${sourceId}`;
1721
+ const subscriptionKey = options.subscriptionKey ?? `ds:${sourceId}`;
1590
1722
  const existingSubscription = __classPrivateFieldGet(this, _AssetsController_activeSubscriptions, "f").get(subscriptionKey);
1591
1723
  const isUpdate = existingSubscription !== undefined;
1592
1724
  log('Subscribe to data source', {
@@ -1595,12 +1727,16 @@ async function _AssetsController_executeMiddlewares(sources, request, initialRes
1595
1727
  isUpdate,
1596
1728
  accountCount: accounts.length,
1597
1729
  chainCount: chains.length,
1730
+ customAssetsOnly: options.customAssetsOnly === true,
1598
1731
  });
1599
1732
  const subscribeReq = {
1600
1733
  request: __classPrivateFieldGet(this, _AssetsController_instances, "m", _AssetsController_buildDataRequest).call(this, accounts, chains, {
1601
1734
  assetTypes: ['fungible'],
1602
1735
  dataTypes: ['balance'],
1603
1736
  updateInterval: __classPrivateFieldGet(this, _AssetsController_defaultUpdateInterval, "f"),
1737
+ ...(options.customAssetsOnly === true
1738
+ ? { customAssetsOnly: true }
1739
+ : {}),
1604
1740
  }),
1605
1741
  subscriptionId: subscriptionKey,
1606
1742
  isUpdate,