@metamask-previews/assets-controller 4.0.0-preview-b6a517f5e → 4.0.0-preview-47dc390

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 (74) hide show
  1. package/CHANGELOG.md +13 -0
  2. package/dist/AssetsController.cjs +68 -27
  3. package/dist/AssetsController.cjs.map +1 -1
  4. package/dist/AssetsController.d.cts +3 -3
  5. package/dist/AssetsController.d.cts.map +1 -1
  6. package/dist/AssetsController.d.mts +3 -3
  7. package/dist/AssetsController.d.mts.map +1 -1
  8. package/dist/AssetsController.mjs +68 -27
  9. package/dist/AssetsController.mjs.map +1 -1
  10. package/dist/data-sources/PriceDataSource.cjs +63 -38
  11. package/dist/data-sources/PriceDataSource.cjs.map +1 -1
  12. package/dist/data-sources/PriceDataSource.d.cts.map +1 -1
  13. package/dist/data-sources/PriceDataSource.d.mts.map +1 -1
  14. package/dist/data-sources/PriceDataSource.mjs +63 -38
  15. package/dist/data-sources/PriceDataSource.mjs.map +1 -1
  16. package/dist/data-sources/RpcDataSource.cjs +26 -59
  17. package/dist/data-sources/RpcDataSource.cjs.map +1 -1
  18. package/dist/data-sources/RpcDataSource.d.cts +1 -2
  19. package/dist/data-sources/RpcDataSource.d.cts.map +1 -1
  20. package/dist/data-sources/RpcDataSource.d.mts +1 -2
  21. package/dist/data-sources/RpcDataSource.d.mts.map +1 -1
  22. package/dist/data-sources/RpcDataSource.mjs +28 -61
  23. package/dist/data-sources/RpcDataSource.mjs.map +1 -1
  24. package/dist/data-sources/StakedBalanceDataSource.cjs +9 -2
  25. package/dist/data-sources/StakedBalanceDataSource.cjs.map +1 -1
  26. package/dist/data-sources/StakedBalanceDataSource.d.cts.map +1 -1
  27. package/dist/data-sources/StakedBalanceDataSource.d.mts.map +1 -1
  28. package/dist/data-sources/StakedBalanceDataSource.mjs +9 -2
  29. package/dist/data-sources/StakedBalanceDataSource.mjs.map +1 -1
  30. package/dist/data-sources/TokenDataSource.cjs +61 -30
  31. package/dist/data-sources/TokenDataSource.cjs.map +1 -1
  32. package/dist/data-sources/TokenDataSource.d.cts.map +1 -1
  33. package/dist/data-sources/TokenDataSource.d.mts.map +1 -1
  34. package/dist/data-sources/TokenDataSource.mjs +63 -32
  35. package/dist/data-sources/TokenDataSource.mjs.map +1 -1
  36. package/dist/data-sources/evm-rpc-services/clients/TokensApiClient.cjs +67 -0
  37. package/dist/data-sources/evm-rpc-services/clients/TokensApiClient.cjs.map +1 -0
  38. package/dist/data-sources/evm-rpc-services/clients/TokensApiClient.d.cts +23 -0
  39. package/dist/data-sources/evm-rpc-services/clients/TokensApiClient.d.cts.map +1 -0
  40. package/dist/data-sources/evm-rpc-services/clients/TokensApiClient.d.mts +23 -0
  41. package/dist/data-sources/evm-rpc-services/clients/TokensApiClient.d.mts.map +1 -0
  42. package/dist/data-sources/evm-rpc-services/clients/TokensApiClient.mjs +63 -0
  43. package/dist/data-sources/evm-rpc-services/clients/TokensApiClient.mjs.map +1 -0
  44. package/dist/data-sources/evm-rpc-services/clients/index.cjs +3 -1
  45. package/dist/data-sources/evm-rpc-services/clients/index.cjs.map +1 -1
  46. package/dist/data-sources/evm-rpc-services/clients/index.d.cts +1 -0
  47. package/dist/data-sources/evm-rpc-services/clients/index.d.cts.map +1 -1
  48. package/dist/data-sources/evm-rpc-services/clients/index.d.mts +1 -0
  49. package/dist/data-sources/evm-rpc-services/clients/index.d.mts.map +1 -1
  50. package/dist/data-sources/evm-rpc-services/clients/index.mjs +1 -0
  51. package/dist/data-sources/evm-rpc-services/clients/index.mjs.map +1 -1
  52. package/dist/data-sources/evm-rpc-services/index.cjs +2 -1
  53. package/dist/data-sources/evm-rpc-services/index.cjs.map +1 -1
  54. package/dist/data-sources/evm-rpc-services/index.d.cts +1 -1
  55. package/dist/data-sources/evm-rpc-services/index.d.cts.map +1 -1
  56. package/dist/data-sources/evm-rpc-services/index.d.mts +1 -1
  57. package/dist/data-sources/evm-rpc-services/index.d.mts.map +1 -1
  58. package/dist/data-sources/evm-rpc-services/index.mjs +1 -1
  59. package/dist/data-sources/evm-rpc-services/index.mjs.map +1 -1
  60. package/dist/data-sources/evm-rpc-services/services/TokenDetector.cjs +47 -50
  61. package/dist/data-sources/evm-rpc-services/services/TokenDetector.cjs.map +1 -1
  62. package/dist/data-sources/evm-rpc-services/services/TokenDetector.d.cts +12 -9
  63. package/dist/data-sources/evm-rpc-services/services/TokenDetector.d.cts.map +1 -1
  64. package/dist/data-sources/evm-rpc-services/services/TokenDetector.d.mts +12 -9
  65. package/dist/data-sources/evm-rpc-services/services/TokenDetector.d.mts.map +1 -1
  66. package/dist/data-sources/evm-rpc-services/services/TokenDetector.mjs +47 -50
  67. package/dist/data-sources/evm-rpc-services/services/TokenDetector.mjs.map +1 -1
  68. package/dist/data-sources/evm-rpc-services/services/index.cjs.map +1 -1
  69. package/dist/data-sources/evm-rpc-services/services/index.d.cts +1 -1
  70. package/dist/data-sources/evm-rpc-services/services/index.d.cts.map +1 -1
  71. package/dist/data-sources/evm-rpc-services/services/index.d.mts +1 -1
  72. package/dist/data-sources/evm-rpc-services/services/index.d.mts.map +1 -1
  73. package/dist/data-sources/evm-rpc-services/services/index.mjs.map +1 -1
  74. package/package.json +1 -1
package/CHANGELOG.md CHANGED
@@ -9,6 +9,19 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
9
9
 
10
10
  ### Changed
11
11
 
12
+ - `TokenDetector` now fetches the token list directly from the Tokens API (`/v3/chains/{chain}/assets`) via a new `TokensApiClient` instead of reading from `TokenListController:getState` ([#8385](https://github.com/MetaMask/core/pull/8385))
13
+ - `TokenDetectorMessenger` type has been removed; `TokenDetector` constructor now takes a `TokensApiClient` instance as its second argument
14
+ - `RpcDataSource` no longer requires `TokenListController:getState` — `GetTokenListState` has been removed from `RpcDataSourceAllowedActions` and `AssetsControllerAllowedActions`
15
+ - Unknown ERC-20 metadata is no longer looked up from the token list as a fallback in `RpcDataSource`; `TokenDataSource` handles enrichment downstream
16
+ - Split `getAssets` fetch pipeline into a fast awaited path and a parallel fire-and-forget background path to reduce perceived latency on unlock and onboarding ([#8383](https://github.com/MetaMask/core/pull/8383))
17
+ - Fast pipeline: AccountsApi + StakedBalance → Detection → Token + Price (awaited, committed to state immediately)
18
+ - Background pipeline: Snap + RPC run in parallel → Detection → Token + Price when basic functionality is enabled; when disabled (RPC-only mode), Token + Price are omitted (fire-and-forget merge)
19
+ - `handleAssetsUpdate` skips token/price enrichment and strips `metadata` / `price` from the effective request when basic functionality is disabled (RPC-only mode)
20
+ - `setSelectedCurrency` no longer triggers a price refresh via `getAssets` when basic functionality is disabled
21
+ - `PriceDataSource` now batches spot-price API requests in chunks of 50 using `reduceInBatchesSerially` to avoid DynamoDB batch-limit errors ([#8383](https://github.com/MetaMask/core/pull/8383))
22
+ - `TokenDataSource` now batches token metadata API requests in chunks of 50 using `reduceInBatchesSerially` to avoid DynamoDB batch-limit errors ([#8383](https://github.com/MetaMask/core/pull/8383))
23
+ - `PriceDataSource` filters out all synthetic `slip44:NUMBER-*` staking-position asset IDs before calling the Price API ([#8383](https://github.com/MetaMask/core/pull/8383))
24
+ - `TokenDataSource` filters EVM ERC-20 tokens by `occurrences >= 3` and treats missing occurrences as 0 ([#8383](https://github.com/MetaMask/core/pull/8383))
12
25
  - Bump `@metamask/keyring-controller` from `^25.1.1` to `^25.2.0` ([#8363](https://github.com/MetaMask/core/pull/8363))
13
26
  - Bump `@metamask/messenger` from `^1.0.0` to `^1.1.1` ([#8364](https://github.com/MetaMask/core/pull/8364), [#8373](https://github.com/MetaMask/core/pull/8373))
14
27
 
@@ -263,7 +263,15 @@ class AssetsController extends base_controller_1.BaseController {
263
263
  __classPrivateFieldSet(this, _AssetsController_trace, trace, "f");
264
264
  const rpcConfig = rpcDataSourceConfig ?? {};
265
265
  __classPrivateFieldSet(this, _AssetsController_onActiveChainsUpdated, (dataSourceName, chains, previousChains) => {
266
- __classPrivateFieldGet(this, _AssetsController_instances, "m", _AssetsController_handleActiveChainsUpdate).call(this, dataSourceName, chains, previousChains);
266
+ try {
267
+ __classPrivateFieldGet(this, _AssetsController_instances, "m", _AssetsController_handleActiveChainsUpdate).call(this, dataSourceName, chains, previousChains);
268
+ }
269
+ catch (error) {
270
+ log('Failed to handle active chains update', {
271
+ dataSourceName,
272
+ error,
273
+ });
274
+ }
267
275
  }, "f");
268
276
  __classPrivateFieldSet(this, _AssetsController_backendWebsocketDataSource, new BackendWebsocketDataSource_1.BackendWebsocketDataSource({
269
277
  messenger: this.messenger,
@@ -356,12 +364,18 @@ class AssetsController extends base_controller_1.BaseController {
356
364
  forceUpdate: true,
357
365
  assetsForPriceUpdate: options?.assetsForPriceUpdate,
358
366
  });
359
- const sources = __classPrivateFieldGet(this, _AssetsController_isBasicFunctionality, "f").call(this)
367
+ // Fast pipeline: accountsApi + stakedBalance → detection → token + price.
368
+ // Snap and RPC are excluded here due to their latency (snap triggers account
369
+ // creation, RPC is slow on many chains). Results are committed to state
370
+ // immediately so the UI can display balances without waiting for them.
371
+ //
372
+ // Both the fast and background pipelines use 'merge' mode because neither
373
+ // alone represents the full set of data sources. Using 'full' in either
374
+ // would wipe balances from the sources handled by the other pipeline.
375
+ const fastSources = __classPrivateFieldGet(this, _AssetsController_isBasicFunctionality, "f").call(this)
360
376
  ? [
361
377
  (0, ParallelMiddleware_1.createParallelBalanceMiddleware)([
362
378
  __classPrivateFieldGet(this, _AssetsController_accountsApiDataSource, "f"),
363
- __classPrivateFieldGet(this, _AssetsController_snapDataSource, "f"),
364
- __classPrivateFieldGet(this, _AssetsController_rpcDataSource, "f"),
365
379
  __classPrivateFieldGet(this, _AssetsController_stakedBalanceDataSource, "f"),
366
380
  ]),
367
381
  __classPrivateFieldGet(this, _AssetsController_detectionMiddleware, "f"),
@@ -370,18 +384,33 @@ class AssetsController extends base_controller_1.BaseController {
370
384
  __classPrivateFieldGet(this, _AssetsController_priceDataSource, "f"),
371
385
  ]),
372
386
  ]
373
- : [
374
- __classPrivateFieldGet(this, _AssetsController_rpcDataSource, "f"),
375
- __classPrivateFieldGet(this, _AssetsController_stakedBalanceDataSource, "f"),
376
- __classPrivateFieldGet(this, _AssetsController_detectionMiddleware, "f"),
377
- ];
378
- const { response, durationByDataSource } = await __classPrivateFieldGet(this, _AssetsController_instances, "m", _AssetsController_executeMiddlewares).call(this, sources, request);
379
- // Default to 'merge' when fetching a subset of chains so we don't wipe
380
- // balances from chains that weren't included in this fetch.
381
- const isPartialChainFetch = options?.chainIds !== undefined &&
382
- options.chainIds.length < __classPrivateFieldGet(this, _AssetsController_enabledChains, "f").size;
383
- const updateMode = options?.updateMode ?? (isPartialChainFetch ? 'merge' : 'full');
384
- await __classPrivateFieldGet(this, _AssetsController_instances, "m", _AssetsController_updateState).call(this, { ...response, updateMode });
387
+ : [__classPrivateFieldGet(this, _AssetsController_stakedBalanceDataSource, "f"), __classPrivateFieldGet(this, _AssetsController_detectionMiddleware, "f")];
388
+ const { response, durationByDataSource } = await __classPrivateFieldGet(this, _AssetsController_instances, "m", _AssetsController_executeMiddlewares).call(this, fastSources, request);
389
+ // The fast pipeline only contains a subset of data sources (AccountsApi +
390
+ // StakedBalance), so it must always merge to avoid wiping Snap/RPC
391
+ // balances that the background pipeline hasn't yet replaced.
392
+ await __classPrivateFieldGet(this, _AssetsController_instances, "m", _AssetsController_updateState).call(this, { ...response, updateMode: 'merge' });
393
+ // Background pipeline: snap and RPC run in parallel after the fast path
394
+ // commits to state. Their balances are merged together before detection.
395
+ // Token + price enrichment matches the pre-split behavior: only when basic
396
+ // functionality is on (RPC-only mode must not call token/price APIs).
397
+ const slowSources = __classPrivateFieldGet(this, _AssetsController_isBasicFunctionality, "f").call(this)
398
+ ? [__classPrivateFieldGet(this, _AssetsController_snapDataSource, "f"), __classPrivateFieldGet(this, _AssetsController_rpcDataSource, "f")]
399
+ : [__classPrivateFieldGet(this, _AssetsController_rpcDataSource, "f")];
400
+ __classPrivateFieldGet(this, _AssetsController_instances, "m", _AssetsController_executeMiddlewares).call(this, [
401
+ (0, ParallelMiddleware_1.createParallelBalanceMiddleware)(slowSources),
402
+ __classPrivateFieldGet(this, _AssetsController_detectionMiddleware, "f"),
403
+ ...(__classPrivateFieldGet(this, _AssetsController_isBasicFunctionality, "f").call(this)
404
+ ? [
405
+ (0, ParallelMiddleware_1.createParallelMiddleware)([
406
+ __classPrivateFieldGet(this, _AssetsController_tokenDataSource, "f"),
407
+ __classPrivateFieldGet(this, _AssetsController_priceDataSource, "f"),
408
+ ]),
409
+ ]
410
+ : []),
411
+ ], request)
412
+ .then(({ response: slowResponse }) => __classPrivateFieldGet(this, _AssetsController_instances, "m", _AssetsController_updateState).call(this, { ...slowResponse, updateMode: 'merge' }))
413
+ .catch((error) => log('Background pipeline failed', { error }));
385
414
  const durationMs = performance.now() - startTime;
386
415
  // Emit trace for every full fetch (Assets Health dashboard)
387
416
  __classPrivateFieldGet(this, _AssetsController_instances, "m", _AssetsController_emitTrace).call(this, TRACE_FULL_FETCH, {
@@ -648,6 +677,9 @@ class AssetsController extends base_controller_1.BaseController {
648
677
  previousCurrency,
649
678
  selectedCurrency,
650
679
  });
680
+ if (!__classPrivateFieldGet(this, _AssetsController_isBasicFunctionality, "f").call(this)) {
681
+ return;
682
+ }
651
683
  this.getAssets(__classPrivateFieldGet(this, _AssetsController_instances, "a", _AssetsController_selectedAccounts_get), {
652
684
  forceUpdate: true,
653
685
  dataTypes: ['price'],
@@ -722,7 +754,8 @@ class AssetsController extends base_controller_1.BaseController {
722
754
  /**
723
755
  * Handle assets updated from a data source.
724
756
  * Called via the onAssetsUpdate callback passed in SubscriptionRequest when the controller subscribes to a data source.
725
- * Enriches the response with token metadata (via middlewares) before updating state.
757
+ * Runs detection, then (when basic functionality is enabled) token metadata and price enrichment before updating state.
758
+ * When basic functionality is disabled (RPC-only mode), only detection runs; token and price APIs are not used.
726
759
  *
727
760
  * @param response - The data response with updated assets
728
761
  * @param sourceId - The data source ID reporting the update
@@ -735,19 +768,27 @@ class AssetsController extends base_controller_1.BaseController {
735
768
  hasBalance: Boolean(response.assetsBalance),
736
769
  hasPrice: Boolean(response.assetsPrice),
737
770
  });
738
- // Run through enrichment middlewares (Detection, then Token + Price in parallel)
739
- // Include 'metadata' in dataTypes so TokenDataSource runs to enrich detected assets
740
- const { response: enrichedResponse } = await __classPrivateFieldGet(this, _AssetsController_instances, "m", _AssetsController_executeMiddlewares).call(this, [
741
- __classPrivateFieldGet(this, _AssetsController_detectionMiddleware, "f"),
742
- (0, ParallelMiddleware_1.createParallelMiddleware)([
743
- __classPrivateFieldGet(this, _AssetsController_tokenDataSource, "f"),
744
- __classPrivateFieldGet(this, _AssetsController_priceDataSource, "f"),
745
- ]),
746
- ], request ?? {
771
+ const resolvedRequest = request ?? {
747
772
  accountsWithSupportedChains: [],
748
773
  chainIds: [],
749
774
  dataTypes: ['balance', 'metadata', 'price'],
750
- }, response);
775
+ };
776
+ // RPC-only mode (basic functionality off): never run token/price APIs. Strip
777
+ // those data types so downstream middleware cannot treat them as requested.
778
+ const pipelineRequest = __classPrivateFieldGet(this, _AssetsController_isBasicFunctionality, "f").call(this)
779
+ ? resolvedRequest
780
+ : {
781
+ ...resolvedRequest,
782
+ dataTypes: resolvedRequest.dataTypes.filter((dt) => dt !== 'metadata' && dt !== 'price'),
783
+ };
784
+ const enrichmentSources = [__classPrivateFieldGet(this, _AssetsController_detectionMiddleware, "f")];
785
+ if (__classPrivateFieldGet(this, _AssetsController_isBasicFunctionality, "f").call(this)) {
786
+ enrichmentSources.push((0, ParallelMiddleware_1.createParallelMiddleware)([
787
+ __classPrivateFieldGet(this, _AssetsController_tokenDataSource, "f"),
788
+ __classPrivateFieldGet(this, _AssetsController_priceDataSource, "f"),
789
+ ]));
790
+ }
791
+ const { response: enrichedResponse } = await __classPrivateFieldGet(this, _AssetsController_instances, "m", _AssetsController_executeMiddlewares).call(this, enrichmentSources, pipelineRequest, response);
751
792
  await __classPrivateFieldGet(this, _AssetsController_instances, "m", _AssetsController_updateState).call(this, enrichedResponse);
752
793
  __classPrivateFieldGet(this, _AssetsController_instances, "m", _AssetsController_emitTrace).call(this, TRACE_UPDATE_PIPELINE, {
753
794
  source: sourceId,