@metamask-previews/assets-controller 0.0.0-preview-6dfa4aeba → 0.1.0-preview-ce3ee897a

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 (44) hide show
  1. package/CHANGELOG.md +16 -3
  2. package/dist/AssetsController.cjs +84 -15
  3. package/dist/AssetsController.cjs.map +1 -1
  4. package/dist/AssetsController.d.cts +39 -12
  5. package/dist/AssetsController.d.cts.map +1 -1
  6. package/dist/AssetsController.d.mts +39 -12
  7. package/dist/AssetsController.d.mts.map +1 -1
  8. package/dist/AssetsController.mjs +84 -15
  9. package/dist/AssetsController.mjs.map +1 -1
  10. package/dist/data-sources/RpcDataSource.cjs.map +1 -1
  11. package/dist/data-sources/RpcDataSource.d.cts +0 -7
  12. package/dist/data-sources/RpcDataSource.d.cts.map +1 -1
  13. package/dist/data-sources/RpcDataSource.d.mts +0 -7
  14. package/dist/data-sources/RpcDataSource.d.mts.map +1 -1
  15. package/dist/data-sources/RpcDataSource.mjs.map +1 -1
  16. package/dist/data-sources/evm-rpc-services/clients/MulticallClient.cjs +192 -95
  17. package/dist/data-sources/evm-rpc-services/clients/MulticallClient.cjs.map +1 -1
  18. package/dist/data-sources/evm-rpc-services/clients/MulticallClient.d.cts +22 -1
  19. package/dist/data-sources/evm-rpc-services/clients/MulticallClient.d.cts.map +1 -1
  20. package/dist/data-sources/evm-rpc-services/clients/MulticallClient.d.mts +22 -1
  21. package/dist/data-sources/evm-rpc-services/clients/MulticallClient.d.mts.map +1 -1
  22. package/dist/data-sources/evm-rpc-services/clients/MulticallClient.mjs +192 -95
  23. package/dist/data-sources/evm-rpc-services/clients/MulticallClient.mjs.map +1 -1
  24. package/dist/data-sources/initDataSources.cjs +10 -4
  25. package/dist/data-sources/initDataSources.cjs.map +1 -1
  26. package/dist/data-sources/initDataSources.d.cts +8 -4
  27. package/dist/data-sources/initDataSources.d.cts.map +1 -1
  28. package/dist/data-sources/initDataSources.d.mts +8 -4
  29. package/dist/data-sources/initDataSources.d.mts.map +1 -1
  30. package/dist/data-sources/initDataSources.mjs +10 -4
  31. package/dist/data-sources/initDataSources.mjs.map +1 -1
  32. package/dist/index.cjs.map +1 -1
  33. package/dist/index.d.cts +1 -1
  34. package/dist/index.d.cts.map +1 -1
  35. package/dist/index.d.mts +1 -1
  36. package/dist/index.d.mts.map +1 -1
  37. package/dist/index.mjs.map +1 -1
  38. package/dist/types.cjs.map +1 -1
  39. package/dist/types.d.cts +10 -0
  40. package/dist/types.d.cts.map +1 -1
  41. package/dist/types.d.mts +10 -0
  42. package/dist/types.d.mts.map +1 -1
  43. package/dist/types.mjs.map +1 -1
  44. package/package.json +2 -3
package/CHANGELOG.md CHANGED
@@ -9,13 +9,26 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
9
9
 
10
10
  ### Added
11
11
 
12
+ - Add `assetPreferences` state and `AssetPreferences` type for per-asset UI preferences (e.g. `hidden`), separate from `assetsMetadata` ([#7777](https://github.com/MetaMask/core/pull/7777))
13
+ - Add `hideAsset(assetId)`, `unhideAsset(assetId)` for managing hidden assets globally; hidden assets are excluded from `getAssets` but balance updates continue to be tracked ([#7777](https://github.com/MetaMask/core/pull/7777))
14
+
15
+ ### Changed
16
+
17
+ - 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))
18
+
19
+ - Replace `viem` dependency with manual ABI encoding/decoding in `MulticallClient` ([#7839](https://github.com/MetaMask/core/pull/7839))
20
+
21
+ ## [0.1.0]
22
+
23
+ ### Added
24
+
25
+ - Add `isEnabled` option to `AssetsController`, `initMessengers`, and `initDataSources` to conditionally skip initialization when disabled and avoid duplicated requests ([#7831](https://github.com/MetaMask/core/pull/7831))
12
26
  - Complete rewrite of AssetsController with middleware architecture for unified asset management across all blockchain networks (EVM and non-EVM) ([#7685](https://github.com/MetaMask/core/pull/7685))
13
27
  - Initial release ([#7587](https://github.com/MetaMask/core/pull/7587))
14
28
  - Add `MulticallClient` for batching RPC calls using Multicall3 contract ([#7677](https://github.com/MetaMask/core/pull/7677))
15
29
  - Add batch utilities (`divideIntoBatches`, `reduceInBatchesSerially`) for processing arrays in batches ([#7677](https://github.com/MetaMask/core/pull/7677))
16
30
  - Add `TokenDetector` service for detecting ERC-20 tokens with non-zero balances on a chain ([#7683](https://github.com/MetaMask/core/pull/7683))
17
31
  - Add `BalanceFetcher` service for fetching token balances for user's imported/detected tokens ([#7684](https://github.com/MetaMask/core/pull/7684))
18
- - Add `viem` dependency for ABI encoding/decoding in MulticallClient
19
32
  - Add configurable polling intervals for `RpcDataSource` via `RpcDataSourceConfig` in `initDataSources` ([#7709](https://github.com/MetaMask/core/pull/7709))
20
33
  - Add comprehensive unit tests for data sources (`AccountsApiDataSource`, `BackendWebsocketDataSource`, `PriceDataSource`, `TokenDataSource`, `SnapDataSource`), `DetectionMiddleware`, and `AssetsController` ([#7714](https://github.com/MetaMask/core/pull/7714))
21
34
 
@@ -26,8 +39,8 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
26
39
  - Store native token metadata (type, symbol, name, decimals) in `assetsMetadata` derived from `NetworkController` chain status ([#7752](https://github.com/MetaMask/core/pull/7752))
27
40
  - `AccountsApiDataSource` now includes `assetsMetadata` in response with token info from V5 API ([#7752](https://github.com/MetaMask/core/pull/7752))
28
41
  - Bump `@metamask/keyring-controller` from `^25.0.0` to `^25.1.0` ([#7713](https://github.com/MetaMask/core/pull/7713))
29
- - Refactor `MulticallClient` to use viem for ABI encoding/decoding instead of manual implementation
30
42
  - Refactor `RpcDataSource` to delegate polling to `BalanceFetcher` and `TokenDetector` services ([#7709](https://github.com/MetaMask/core/pull/7709))
31
43
  - Refactor `BalanceFetcher` and `TokenDetector` to extend `StaticIntervalPollingControllerOnly` for independent polling management ([#7709](https://github.com/MetaMask/core/pull/7709))
32
44
 
33
- [Unreleased]: https://github.com/MetaMask/core/
45
+ [Unreleased]: https://github.com/MetaMask/core/compare/@metamask/assets-controller@0.1.0...HEAD
46
+ [0.1.0]: https://github.com/MetaMask/core/releases/tag/@metamask/assets-controller@0.1.0
@@ -13,7 +13,7 @@ var __classPrivateFieldGet = (this && this.__classPrivateFieldGet) || 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_defaultUpdateInterval, _AssetsController_controllerMutex, _AssetsController_activeSubscriptions, _AssetsController_enabledChains, _AssetsController_selectedAccounts_get, _AssetsController_dataSources, _AssetsController_initializeState, _AssetsController_extractEnabledChains, _AssetsController_normalizeChainReference, _AssetsController_subscribeToEvents, _AssetsController_registerActionHandlers, _AssetsController_executeMiddlewares, _AssetsController_assignChainsToDataSources, _AssetsController_updateState, _AssetsController_getAssetsFromState, _AssetsController_tokenStandardToAssetType, _AssetsController_start, _AssetsController_stop, _AssetsController_subscribeToDataSources, _AssetsController_subscribeAssetsBalance, _AssetsController_buildChainToAccountsMap, _AssetsController_getAccountsForChains, _AssetsController_subscribeToDataSource, _AssetsController_unsubscribeDataSource, _AssetsController_getEnabledChainsForAccount, _AssetsController_handleAccountGroupChanged, _AssetsController_handleEnabledNetworksChanged, _AssetsController_handleSubscriptionUpdate;
16
+ var _AssetsController_instances, _AssetsController_isEnabled, _AssetsController_defaultUpdateInterval, _AssetsController_controllerMutex, _AssetsController_activeSubscriptions, _AssetsController_enabledChains, _AssetsController_selectedAccounts_get, _AssetsController_dataSources, _AssetsController_initializeState, _AssetsController_extractEnabledChains, _AssetsController_normalizeChainReference, _AssetsController_subscribeToEvents, _AssetsController_registerActionHandlers, _AssetsController_executeMiddlewares, _AssetsController_assignChainsToDataSources, _AssetsController_updateState, _AssetsController_getAssetsFromState, _AssetsController_tokenStandardToAssetType, _AssetsController_start, _AssetsController_stop, _AssetsController_subscribeToDataSources, _AssetsController_subscribeAssetsBalance, _AssetsController_buildChainToAccountsMap, _AssetsController_getAccountsForChains, _AssetsController_subscribeToDataSource, _AssetsController_unsubscribeDataSource, _AssetsController_getEnabledChainsForAccount, _AssetsController_handleAccountGroupChanged, _AssetsController_handleEnabledNetworksChanged, _AssetsController_handleSubscriptionUpdate;
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");
@@ -33,7 +33,7 @@ const log = (0, logger_1.createModuleLogger)(logger_1.projectLogger, CONTROLLER_
33
33
  /**
34
34
  * Returns the default state for AssetsController.
35
35
  *
36
- * @returns The default AssetsController state with empty metadata, balance, price, and customAssets maps.
36
+ * @returns The default AssetsController state with empty maps.
37
37
  */
38
38
  function getDefaultAssetsControllerState() {
39
39
  return {
@@ -41,6 +41,7 @@ function getDefaultAssetsControllerState() {
41
41
  assetsBalance: {},
42
42
  assetsPrice: {},
43
43
  customAssets: {},
44
+ assetPreferences: {},
44
45
  };
45
46
  }
46
47
  exports.getDefaultAssetsControllerState = getDefaultAssetsControllerState;
@@ -72,6 +73,12 @@ const stateMetadata = {
72
73
  includeInDebugSnapshot: false,
73
74
  usedInUi: true,
74
75
  },
76
+ assetPreferences: {
77
+ persist: true,
78
+ includeInStateLogs: false,
79
+ includeInDebugSnapshot: false,
80
+ usedInUi: true,
81
+ },
75
82
  };
76
83
  // ============================================================================
77
84
  // HELPER FUNCTIONS
@@ -161,7 +168,7 @@ function normalizeResponse(response) {
161
168
  * - The controller does NOT manage polling - it simply receives pushed updates
162
169
  */
163
170
  class AssetsController extends base_controller_1.BaseController {
164
- constructor({ messenger, state = {}, defaultUpdateInterval = DEFAULT_POLLING_INTERVAL_MS, }) {
171
+ constructor({ messenger, state = {}, defaultUpdateInterval = DEFAULT_POLLING_INTERVAL_MS, isEnabled = () => true, }) {
165
172
  super({
166
173
  name: CONTROLLER_NAME,
167
174
  messenger,
@@ -172,6 +179,8 @@ class AssetsController extends base_controller_1.BaseController {
172
179
  },
173
180
  });
174
181
  _AssetsController_instances.add(this);
182
+ /** Whether the controller is enabled */
183
+ _AssetsController_isEnabled.set(this, void 0);
175
184
  /** Default update interval hint passed to data sources */
176
185
  _AssetsController_defaultUpdateInterval.set(this, void 0);
177
186
  _AssetsController_controllerMutex.set(this, new async_mutex_1.Mutex());
@@ -190,7 +199,12 @@ class AssetsController extends base_controller_1.BaseController {
190
199
  * Key: sourceId, Value: Set of currently available chainIds
191
200
  */
192
201
  _AssetsController_dataSources.set(this, new Map());
202
+ __classPrivateFieldSet(this, _AssetsController_isEnabled, isEnabled(), "f");
193
203
  __classPrivateFieldSet(this, _AssetsController_defaultUpdateInterval, defaultUpdateInterval, "f");
204
+ if (!__classPrivateFieldGet(this, _AssetsController_isEnabled, "f")) {
205
+ log('AssetsController is disabled, skipping initialization');
206
+ return;
207
+ }
194
208
  log('Initializing AssetsController', {
195
209
  defaultUpdateInterval,
196
210
  });
@@ -347,6 +361,7 @@ class AssetsController extends base_controller_1.BaseController {
347
361
  /**
348
362
  * Add a custom asset for an account.
349
363
  * Custom assets are included in subscription and fetch operations.
364
+ * Adding a custom asset also unhides it if it was previously hidden.
350
365
  *
351
366
  * @param accountId - The account ID to add the custom asset for.
352
367
  * @param assetId - The CAIP-19 asset ID to add.
@@ -363,6 +378,14 @@ class AssetsController extends base_controller_1.BaseController {
363
378
  if (!customAssets[accountId].includes(normalizedAssetId)) {
364
379
  customAssets[accountId].push(normalizedAssetId);
365
380
  }
381
+ // Unhide the asset if it was hidden (via assetPreferences)
382
+ const prefs = state.assetPreferences[normalizedAssetId];
383
+ if (prefs?.hidden) {
384
+ delete prefs.hidden;
385
+ if (Object.keys(prefs).length === 0) {
386
+ delete state.assetPreferences[normalizedAssetId];
387
+ }
388
+ }
366
389
  });
367
390
  // Fetch data for the newly added custom asset
368
391
  const account = __classPrivateFieldGet(this, _AssetsController_instances, "a", _AssetsController_selectedAccounts_get).find((a) => a.id === accountId);
@@ -384,12 +407,11 @@ class AssetsController extends base_controller_1.BaseController {
384
407
  const normalizedAssetId = (0, utils_2.normalizeAssetId)(assetId);
385
408
  log('Removing custom asset', { accountId, assetId: normalizedAssetId });
386
409
  this.update((state) => {
387
- const customAssets = state.customAssets;
388
- if (customAssets[accountId]) {
389
- customAssets[accountId] = customAssets[accountId].filter((id) => id !== normalizedAssetId);
410
+ if (state.customAssets[accountId]) {
411
+ state.customAssets[accountId] = state.customAssets[accountId].filter((id) => id !== normalizedAssetId);
390
412
  // Clean up empty arrays
391
- if (customAssets[accountId].length === 0) {
392
- delete customAssets[accountId];
413
+ if (state.customAssets[accountId].length === 0) {
414
+ delete state.customAssets[accountId];
393
415
  }
394
416
  }
395
417
  });
@@ -401,7 +423,45 @@ class AssetsController extends base_controller_1.BaseController {
401
423
  * @returns Array of CAIP-19 asset IDs for the account's custom assets.
402
424
  */
403
425
  getCustomAssets(accountId) {
404
- return (this.state.customAssets[accountId] ?? []);
426
+ return this.state.customAssets[accountId] ?? [];
427
+ }
428
+ // ============================================================================
429
+ // HIDDEN ASSETS MANAGEMENT
430
+ // ============================================================================
431
+ /**
432
+ * Hide an asset globally.
433
+ * Hidden assets are excluded from the asset list returned by getAssets.
434
+ * The hidden state is stored in assetPreferences.
435
+ *
436
+ * @param assetId - The CAIP-19 asset ID to hide.
437
+ */
438
+ hideAsset(assetId) {
439
+ const normalizedAssetId = (0, utils_2.normalizeAssetId)(assetId);
440
+ log('Hiding asset', { assetId: normalizedAssetId });
441
+ this.update((state) => {
442
+ if (!state.assetPreferences[normalizedAssetId]) {
443
+ state.assetPreferences[normalizedAssetId] = {};
444
+ }
445
+ state.assetPreferences[normalizedAssetId].hidden = true;
446
+ });
447
+ }
448
+ /**
449
+ * Unhide an asset globally.
450
+ *
451
+ * @param assetId - The CAIP-19 asset ID to unhide.
452
+ */
453
+ unhideAsset(assetId) {
454
+ const normalizedAssetId = (0, utils_2.normalizeAssetId)(assetId);
455
+ log('Unhiding asset', { assetId: normalizedAssetId });
456
+ this.update((state) => {
457
+ const prefs = state.assetPreferences[normalizedAssetId];
458
+ if (prefs) {
459
+ delete prefs.hidden;
460
+ if (Object.keys(prefs).length === 0) {
461
+ delete state.assetPreferences[normalizedAssetId];
462
+ }
463
+ }
464
+ });
405
465
  }
406
466
  /**
407
467
  * Subscribe to price updates for all assets held by the given accounts.
@@ -494,10 +554,12 @@ class AssetsController extends base_controller_1.BaseController {
494
554
  this.messenger.unregisterActionHandler('AssetsController:addCustomAsset');
495
555
  this.messenger.unregisterActionHandler('AssetsController:removeCustomAsset');
496
556
  this.messenger.unregisterActionHandler('AssetsController:getCustomAssets');
557
+ this.messenger.unregisterActionHandler('AssetsController:hideAsset');
558
+ this.messenger.unregisterActionHandler('AssetsController:unhideAsset');
497
559
  }
498
560
  }
499
561
  exports.AssetsController = AssetsController;
500
- _AssetsController_defaultUpdateInterval = new WeakMap(), _AssetsController_controllerMutex = new WeakMap(), _AssetsController_activeSubscriptions = new WeakMap(), _AssetsController_enabledChains = new WeakMap(), _AssetsController_dataSources = new WeakMap(), _AssetsController_instances = new WeakSet(), _AssetsController_selectedAccounts_get = function _AssetsController_selectedAccounts_get() {
562
+ _AssetsController_isEnabled = new WeakMap(), _AssetsController_defaultUpdateInterval = new WeakMap(), _AssetsController_controllerMutex = new WeakMap(), _AssetsController_activeSubscriptions = new WeakMap(), _AssetsController_enabledChains = new WeakMap(), _AssetsController_dataSources = new WeakMap(), _AssetsController_instances = new WeakSet(), _AssetsController_selectedAccounts_get = function _AssetsController_selectedAccounts_get() {
501
563
  return this.messenger.call('AccountTreeController:getAccountsFromSelectedAccountGroup');
502
564
  }, _AssetsController_initializeState = function _AssetsController_initializeState() {
503
565
  const { enabledNetworkMap } = this.messenger.call('NetworkEnablementController:getState');
@@ -556,6 +618,8 @@ _AssetsController_defaultUpdateInterval = new WeakMap(), _AssetsController_contr
556
618
  this.messenger.registerActionHandler('AssetsController:addCustomAsset', this.addCustomAsset.bind(this));
557
619
  this.messenger.registerActionHandler('AssetsController:removeCustomAsset', this.removeCustomAsset.bind(this));
558
620
  this.messenger.registerActionHandler('AssetsController:getCustomAssets', this.getCustomAssets.bind(this));
621
+ this.messenger.registerActionHandler('AssetsController:hideAsset', this.hideAsset.bind(this));
622
+ this.messenger.registerActionHandler('AssetsController:unhideAsset', this.unhideAsset.bind(this));
559
623
  }, _AssetsController_executeMiddlewares =
560
624
  // ============================================================================
561
625
  // MIDDLEWARE EXECUTION
@@ -627,7 +691,7 @@ async function _AssetsController_updateState(response) {
627
691
  const changedBalances = [];
628
692
  const changedMetadata = [];
629
693
  this.update((state) => {
630
- // Use type assertions to avoid deep type instantiation issues with Draft<Json>
694
+ // Use type assertions to avoid deep type instantiation issues with Immer Draft types
631
695
  const metadata = state.assetsMetadata;
632
696
  const balances = state.assetsBalance;
633
697
  const prices = state.assetsPrice;
@@ -738,16 +802,21 @@ async function _AssetsController_updateState(response) {
738
802
  const accountBalances = this.state.assetsBalance[account.id] ?? {};
739
803
  for (const [assetId, balance] of Object.entries(accountBalances)) {
740
804
  const typedAssetId = assetId;
741
- const assetChainId = extractChainId(typedAssetId);
742
- if (!chainIdSet.has(assetChainId)) {
743
- continue;
744
- }
745
805
  const metadataRaw = this.state.assetsMetadata[typedAssetId];
746
806
  // Skip assets without metadata
747
807
  if (!metadataRaw) {
748
808
  continue;
749
809
  }
750
810
  const metadata = metadataRaw;
811
+ // Skip hidden assets (assetPreferences)
812
+ const prefs = this.state.assetPreferences[typedAssetId];
813
+ if (prefs?.hidden) {
814
+ continue;
815
+ }
816
+ const assetChainId = extractChainId(typedAssetId);
817
+ if (!chainIdSet.has(assetChainId)) {
818
+ continue;
819
+ }
751
820
  // Filter by asset type
752
821
  const tokenAssetType = __classPrivateFieldGet(this, _AssetsController_instances, "m", _AssetsController_tokenStandardToAssetType).call(this, metadata.type);
753
822
  if (!assetTypeSet.has(tokenAssetType)) {