@metamask/assets-controller 0.2.0 → 2.0.0

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 (202) hide show
  1. package/CHANGELOG.md +39 -1
  2. package/dist/AssetsController-method-action-types.cjs +7 -0
  3. package/dist/AssetsController-method-action-types.cjs.map +1 -0
  4. package/dist/AssetsController-method-action-types.d.cts +78 -0
  5. package/dist/AssetsController-method-action-types.d.cts.map +1 -0
  6. package/dist/AssetsController-method-action-types.d.mts +78 -0
  7. package/dist/AssetsController-method-action-types.d.mts.map +1 -0
  8. package/dist/AssetsController-method-action-types.mjs +6 -0
  9. package/dist/AssetsController-method-action-types.mjs.map +1 -0
  10. package/dist/AssetsController.cjs +346 -271
  11. package/dist/AssetsController.cjs.map +1 -1
  12. package/dist/AssetsController.d.cts +92 -96
  13. package/dist/AssetsController.d.cts.map +1 -1
  14. package/dist/AssetsController.d.mts +92 -96
  15. package/dist/AssetsController.d.mts.map +1 -1
  16. package/dist/AssetsController.mjs +347 -272
  17. package/dist/AssetsController.mjs.map +1 -1
  18. package/dist/data-sources/AbstractDataSource.cjs +8 -0
  19. package/dist/data-sources/AbstractDataSource.cjs.map +1 -1
  20. package/dist/data-sources/AbstractDataSource.d.cts +16 -1
  21. package/dist/data-sources/AbstractDataSource.d.cts.map +1 -1
  22. package/dist/data-sources/AbstractDataSource.d.mts +16 -1
  23. package/dist/data-sources/AbstractDataSource.d.mts.map +1 -1
  24. package/dist/data-sources/AbstractDataSource.mjs +8 -0
  25. package/dist/data-sources/AbstractDataSource.mjs.map +1 -1
  26. package/dist/data-sources/AccountsApiDataSource.cjs +92 -111
  27. package/dist/data-sources/AccountsApiDataSource.cjs.map +1 -1
  28. package/dist/data-sources/AccountsApiDataSource.d.cts +27 -70
  29. package/dist/data-sources/AccountsApiDataSource.d.cts.map +1 -1
  30. package/dist/data-sources/AccountsApiDataSource.d.mts +27 -70
  31. package/dist/data-sources/AccountsApiDataSource.d.mts.map +1 -1
  32. package/dist/data-sources/AccountsApiDataSource.mjs +88 -107
  33. package/dist/data-sources/AccountsApiDataSource.mjs.map +1 -1
  34. package/dist/data-sources/BackendWebsocketDataSource.cjs +141 -46
  35. package/dist/data-sources/BackendWebsocketDataSource.cjs.map +1 -1
  36. package/dist/data-sources/BackendWebsocketDataSource.d.cts +19 -66
  37. package/dist/data-sources/BackendWebsocketDataSource.d.cts.map +1 -1
  38. package/dist/data-sources/BackendWebsocketDataSource.d.mts +19 -66
  39. package/dist/data-sources/BackendWebsocketDataSource.d.mts.map +1 -1
  40. package/dist/data-sources/BackendWebsocketDataSource.mjs +141 -46
  41. package/dist/data-sources/BackendWebsocketDataSource.mjs.map +1 -1
  42. package/dist/data-sources/PriceDataSource.cjs +39 -48
  43. package/dist/data-sources/PriceDataSource.cjs.map +1 -1
  44. package/dist/data-sources/PriceDataSource.d.cts +13 -92
  45. package/dist/data-sources/PriceDataSource.d.cts.map +1 -1
  46. package/dist/data-sources/PriceDataSource.d.mts +13 -92
  47. package/dist/data-sources/PriceDataSource.d.mts.map +1 -1
  48. package/dist/data-sources/PriceDataSource.mjs +39 -48
  49. package/dist/data-sources/PriceDataSource.mjs.map +1 -1
  50. package/dist/data-sources/RpcDataSource.cjs +184 -142
  51. package/dist/data-sources/RpcDataSource.cjs.map +1 -1
  52. package/dist/data-sources/RpcDataSource.d.cts +29 -114
  53. package/dist/data-sources/RpcDataSource.d.cts.map +1 -1
  54. package/dist/data-sources/RpcDataSource.d.mts +29 -114
  55. package/dist/data-sources/RpcDataSource.d.mts.map +1 -1
  56. package/dist/data-sources/RpcDataSource.mjs +185 -143
  57. package/dist/data-sources/RpcDataSource.mjs.map +1 -1
  58. package/dist/data-sources/SnapDataSource.cjs +53 -39
  59. package/dist/data-sources/SnapDataSource.cjs.map +1 -1
  60. package/dist/data-sources/SnapDataSource.d.cts +9 -45
  61. package/dist/data-sources/SnapDataSource.d.cts.map +1 -1
  62. package/dist/data-sources/SnapDataSource.d.mts +9 -45
  63. package/dist/data-sources/SnapDataSource.d.mts.map +1 -1
  64. package/dist/data-sources/SnapDataSource.mjs +53 -39
  65. package/dist/data-sources/SnapDataSource.mjs.map +1 -1
  66. package/dist/data-sources/StakedBalanceDataSource.cjs +609 -0
  67. package/dist/data-sources/StakedBalanceDataSource.cjs.map +1 -0
  68. package/dist/data-sources/StakedBalanceDataSource.d.cts +72 -0
  69. package/dist/data-sources/StakedBalanceDataSource.d.cts.map +1 -0
  70. package/dist/data-sources/StakedBalanceDataSource.d.mts +72 -0
  71. package/dist/data-sources/StakedBalanceDataSource.d.mts.map +1 -0
  72. package/dist/data-sources/StakedBalanceDataSource.mjs +605 -0
  73. package/dist/data-sources/StakedBalanceDataSource.mjs.map +1 -0
  74. package/dist/data-sources/TokenDataSource.cjs +15 -21
  75. package/dist/data-sources/TokenDataSource.cjs.map +1 -1
  76. package/dist/data-sources/TokenDataSource.d.cts +3 -25
  77. package/dist/data-sources/TokenDataSource.d.cts.map +1 -1
  78. package/dist/data-sources/TokenDataSource.d.mts +3 -25
  79. package/dist/data-sources/TokenDataSource.d.mts.map +1 -1
  80. package/dist/data-sources/TokenDataSource.mjs +15 -21
  81. package/dist/data-sources/TokenDataSource.mjs.map +1 -1
  82. package/dist/data-sources/evm-rpc-services/index.cjs +5 -1
  83. package/dist/data-sources/evm-rpc-services/index.cjs.map +1 -1
  84. package/dist/data-sources/evm-rpc-services/index.d.cts +1 -1
  85. package/dist/data-sources/evm-rpc-services/index.d.cts.map +1 -1
  86. package/dist/data-sources/evm-rpc-services/index.d.mts +1 -1
  87. package/dist/data-sources/evm-rpc-services/index.d.mts.map +1 -1
  88. package/dist/data-sources/evm-rpc-services/index.mjs +1 -1
  89. package/dist/data-sources/evm-rpc-services/index.mjs.map +1 -1
  90. package/dist/data-sources/evm-rpc-services/services/StakedBalanceFetcher.cjs +132 -0
  91. package/dist/data-sources/evm-rpc-services/services/StakedBalanceFetcher.cjs.map +1 -0
  92. package/dist/data-sources/evm-rpc-services/services/StakedBalanceFetcher.d.cts +78 -0
  93. package/dist/data-sources/evm-rpc-services/services/StakedBalanceFetcher.d.cts.map +1 -0
  94. package/dist/data-sources/evm-rpc-services/services/StakedBalanceFetcher.d.mts +78 -0
  95. package/dist/data-sources/evm-rpc-services/services/StakedBalanceFetcher.d.mts.map +1 -0
  96. package/dist/data-sources/evm-rpc-services/services/StakedBalanceFetcher.mjs +126 -0
  97. package/dist/data-sources/evm-rpc-services/services/StakedBalanceFetcher.mjs.map +1 -0
  98. package/dist/data-sources/evm-rpc-services/services/TokenDetector.cjs +16 -0
  99. package/dist/data-sources/evm-rpc-services/services/TokenDetector.cjs.map +1 -1
  100. package/dist/data-sources/evm-rpc-services/services/TokenDetector.d.cts +5 -1
  101. package/dist/data-sources/evm-rpc-services/services/TokenDetector.d.cts.map +1 -1
  102. package/dist/data-sources/evm-rpc-services/services/TokenDetector.d.mts +5 -1
  103. package/dist/data-sources/evm-rpc-services/services/TokenDetector.d.mts.map +1 -1
  104. package/dist/data-sources/evm-rpc-services/services/TokenDetector.mjs +16 -0
  105. package/dist/data-sources/evm-rpc-services/services/TokenDetector.mjs.map +1 -1
  106. package/dist/data-sources/evm-rpc-services/services/index.cjs +6 -1
  107. package/dist/data-sources/evm-rpc-services/services/index.cjs.map +1 -1
  108. package/dist/data-sources/evm-rpc-services/services/index.d.cts +1 -0
  109. package/dist/data-sources/evm-rpc-services/services/index.d.cts.map +1 -1
  110. package/dist/data-sources/evm-rpc-services/services/index.d.mts +1 -0
  111. package/dist/data-sources/evm-rpc-services/services/index.d.mts.map +1 -1
  112. package/dist/data-sources/evm-rpc-services/services/index.mjs +1 -0
  113. package/dist/data-sources/evm-rpc-services/services/index.mjs.map +1 -1
  114. package/dist/data-sources/evm-rpc-services/types/services.cjs.map +1 -1
  115. package/dist/data-sources/evm-rpc-services/types/services.d.cts +4 -0
  116. package/dist/data-sources/evm-rpc-services/types/services.d.cts.map +1 -1
  117. package/dist/data-sources/evm-rpc-services/types/services.d.mts +4 -0
  118. package/dist/data-sources/evm-rpc-services/types/services.d.mts.map +1 -1
  119. package/dist/data-sources/evm-rpc-services/types/services.mjs.map +1 -1
  120. package/dist/data-sources/evm-rpc-services/types/state.cjs.map +1 -1
  121. package/dist/data-sources/evm-rpc-services/types/state.d.cts +3 -3
  122. package/dist/data-sources/evm-rpc-services/types/state.d.cts.map +1 -1
  123. package/dist/data-sources/evm-rpc-services/types/state.d.mts +3 -3
  124. package/dist/data-sources/evm-rpc-services/types/state.d.mts.map +1 -1
  125. package/dist/data-sources/evm-rpc-services/types/state.mjs.map +1 -1
  126. package/dist/data-sources/evm-rpc-services/utils/index.cjs +9 -1
  127. package/dist/data-sources/evm-rpc-services/utils/index.cjs.map +1 -1
  128. package/dist/data-sources/evm-rpc-services/utils/index.d.cts +2 -0
  129. package/dist/data-sources/evm-rpc-services/utils/index.d.cts.map +1 -1
  130. package/dist/data-sources/evm-rpc-services/utils/index.d.mts +2 -0
  131. package/dist/data-sources/evm-rpc-services/utils/index.d.mts.map +1 -1
  132. package/dist/data-sources/evm-rpc-services/utils/index.mjs +2 -0
  133. package/dist/data-sources/evm-rpc-services/utils/index.mjs.map +1 -1
  134. package/dist/data-sources/evm-rpc-services/utils/parsing.cjs +47 -0
  135. package/dist/data-sources/evm-rpc-services/utils/parsing.cjs.map +1 -0
  136. package/dist/data-sources/evm-rpc-services/utils/parsing.d.cts +25 -0
  137. package/dist/data-sources/evm-rpc-services/utils/parsing.d.cts.map +1 -0
  138. package/dist/data-sources/evm-rpc-services/utils/parsing.d.mts +25 -0
  139. package/dist/data-sources/evm-rpc-services/utils/parsing.d.mts.map +1 -0
  140. package/dist/data-sources/evm-rpc-services/utils/parsing.mjs +39 -0
  141. package/dist/data-sources/evm-rpc-services/utils/parsing.mjs.map +1 -0
  142. package/dist/data-sources/evm-rpc-services/utils/staking-contracts.cjs +68 -0
  143. package/dist/data-sources/evm-rpc-services/utils/staking-contracts.cjs.map +1 -0
  144. package/dist/data-sources/evm-rpc-services/utils/staking-contracts.d.cts +25 -0
  145. package/dist/data-sources/evm-rpc-services/utils/staking-contracts.d.cts.map +1 -0
  146. package/dist/data-sources/evm-rpc-services/utils/staking-contracts.d.mts +25 -0
  147. package/dist/data-sources/evm-rpc-services/utils/staking-contracts.d.mts.map +1 -0
  148. package/dist/data-sources/evm-rpc-services/utils/staking-contracts.mjs +62 -0
  149. package/dist/data-sources/evm-rpc-services/utils/staking-contracts.mjs.map +1 -0
  150. package/dist/data-sources/index.cjs +3 -6
  151. package/dist/data-sources/index.cjs.map +1 -1
  152. package/dist/data-sources/index.d.cts +7 -7
  153. package/dist/data-sources/index.d.cts.map +1 -1
  154. package/dist/data-sources/index.d.mts +7 -7
  155. package/dist/data-sources/index.d.mts.map +1 -1
  156. package/dist/data-sources/index.mjs +2 -3
  157. package/dist/data-sources/index.mjs.map +1 -1
  158. package/dist/index.cjs +6 -6
  159. package/dist/index.cjs.map +1 -1
  160. package/dist/index.d.cts +11 -11
  161. package/dist/index.d.cts.map +1 -1
  162. package/dist/index.d.mts +11 -11
  163. package/dist/index.d.mts.map +1 -1
  164. package/dist/index.mjs +3 -3
  165. package/dist/index.mjs.map +1 -1
  166. package/dist/middlewares/DetectionMiddleware.cjs +8 -28
  167. package/dist/middlewares/DetectionMiddleware.cjs.map +1 -1
  168. package/dist/middlewares/DetectionMiddleware.d.cts +4 -26
  169. package/dist/middlewares/DetectionMiddleware.d.cts.map +1 -1
  170. package/dist/middlewares/DetectionMiddleware.d.mts +4 -26
  171. package/dist/middlewares/DetectionMiddleware.d.mts.map +1 -1
  172. package/dist/middlewares/DetectionMiddleware.mjs +8 -28
  173. package/dist/middlewares/DetectionMiddleware.mjs.map +1 -1
  174. package/dist/middlewares/index.cjs.map +1 -1
  175. package/dist/middlewares/index.d.cts +1 -1
  176. package/dist/middlewares/index.d.cts.map +1 -1
  177. package/dist/middlewares/index.d.mts +1 -1
  178. package/dist/middlewares/index.d.mts.map +1 -1
  179. package/dist/middlewares/index.mjs.map +1 -1
  180. package/dist/selectors/balance.cjs +306 -0
  181. package/dist/selectors/balance.cjs.map +1 -0
  182. package/dist/selectors/balance.d.cts +22 -0
  183. package/dist/selectors/balance.d.cts.map +1 -0
  184. package/dist/selectors/balance.d.mts +22 -0
  185. package/dist/selectors/balance.d.mts.map +1 -0
  186. package/dist/selectors/balance.mjs +297 -0
  187. package/dist/selectors/balance.mjs.map +1 -0
  188. package/dist/types.cjs.map +1 -1
  189. package/dist/types.d.cts +56 -21
  190. package/dist/types.d.cts.map +1 -1
  191. package/dist/types.d.mts +56 -21
  192. package/dist/types.d.mts.map +1 -1
  193. package/dist/types.mjs.map +1 -1
  194. package/package.json +13 -10
  195. package/dist/data-sources/initDataSources.cjs +0 -215
  196. package/dist/data-sources/initDataSources.cjs.map +0 -1
  197. package/dist/data-sources/initDataSources.d.cts +0 -140
  198. package/dist/data-sources/initDataSources.d.cts.map +0 -1
  199. package/dist/data-sources/initDataSources.d.mts +0 -140
  200. package/dist/data-sources/initDataSources.d.mts.map +0 -1
  201. package/dist/data-sources/initDataSources.mjs +0 -210
  202. package/dist/data-sources/initDataSources.mjs.map +0 -1
@@ -1,27 +1,47 @@
1
+ var __classPrivateFieldGet = (this && this.__classPrivateFieldGet) || function (receiver, state, kind, f) {
2
+ if (kind === "a" && !f) throw new TypeError("Private accessor was defined without a getter");
3
+ if (typeof state === "function" ? receiver !== state || !f : !state.has(receiver)) throw new TypeError("Cannot read private member from an object whose class did not declare it");
4
+ return kind === "m" ? f : kind === "a" ? f.call(receiver) : f ? f.value : state.get(receiver);
5
+ };
1
6
  var __classPrivateFieldSet = (this && this.__classPrivateFieldSet) || function (receiver, state, value, kind, f) {
2
7
  if (kind === "m") throw new TypeError("Private method is not writable");
3
8
  if (kind === "a" && !f) throw new TypeError("Private accessor was defined without a setter");
4
9
  if (typeof state === "function" ? receiver !== state || !f : !state.has(receiver)) throw new TypeError("Cannot write private member to an object whose class did not declare it");
5
10
  return (kind === "a" ? f.call(receiver, value) : f ? f.value = value : state.set(receiver, value)), value;
6
11
  };
7
- var __classPrivateFieldGet = (this && this.__classPrivateFieldGet) || function (receiver, state, kind, f) {
8
- if (kind === "a" && !f) throw new TypeError("Private accessor was defined without a getter");
9
- if (typeof state === "function" ? receiver !== state || !f : !state.has(receiver)) throw new TypeError("Cannot read private member from an object whose class did not declare it");
10
- return kind === "m" ? f : kind === "a" ? f.call(receiver) : f ? f.value : state.get(receiver);
11
- };
12
- 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;
12
+ var _AssetsController_instances, _AssetsController_isEnabled, _AssetsController_isBasicFunctionality, _AssetsController_defaultUpdateInterval, _AssetsController_trackMetaMetricsEvent, _AssetsController_firstInitFetchReported, _AssetsController_controllerMutex, _AssetsController_activeSubscriptions, _AssetsController_enabledChains, _AssetsController_selectedAccounts_get, _AssetsController_backendWebsocketDataSource, _AssetsController_accountsApiDataSource, _AssetsController_snapDataSource, _AssetsController_rpcDataSource, _AssetsController_stakedBalanceDataSource, _AssetsController_allBalanceDataSources_get, _AssetsController_priceDataSource, _AssetsController_detectionMiddleware, _AssetsController_tokenDataSource, _AssetsController_unsubscribeBasicFunctionality, _AssetsController_initializeState, _AssetsController_extractEnabledChains, _AssetsController_normalizeChainReference, _AssetsController_subscribeToEvents, _AssetsController_registerActionHandlers, _AssetsController_executeMiddlewares, _AssetsController_updateState, _AssetsController_getAssetsFromState, _AssetsController_tokenStandardToAssetType, _AssetsController_start, _AssetsController_stop, _AssetsController_subscribeAssets, _AssetsController_subscribeAssetsBalance, _AssetsController_subscribeStakedBalance, _AssetsController_buildChainToAccountsMap, _AssetsController_subscribeDataSource, _AssetsController_unsubscribeDataSource, _AssetsController_buildDataRequest, _AssetsController_getEnabledChainsForAccount, _AssetsController_handleAccountGroupChanged, _AssetsController_handleEnabledNetworksChanged;
13
13
  import { BaseController } from "@metamask/base-controller";
14
- import { parseCaipAssetType } from "@metamask/utils";
14
+ import { isCaipChainId, isStrictHexString, parseCaipAssetType, parseCaipChainId } from "@metamask/utils";
15
15
  import { Mutex } from "async-mutex";
16
16
  import BigNumberJS from "bignumber.js";
17
17
  import $lodash from "lodash";
18
18
  const { isEqual } = $lodash;
19
+ import { AccountsApiDataSource } from "./data-sources/AccountsApiDataSource.mjs";
20
+ import { BackendWebsocketDataSource } from "./data-sources/BackendWebsocketDataSource.mjs";
21
+ import { PriceDataSource } from "./data-sources/PriceDataSource.mjs";
22
+ import { RpcDataSource } from "./data-sources/RpcDataSource.mjs";
23
+ import { SnapDataSource } from "./data-sources/SnapDataSource.mjs";
24
+ import { StakedBalanceDataSource } from "./data-sources/StakedBalanceDataSource.mjs";
25
+ import { TokenDataSource } from "./data-sources/TokenDataSource.mjs";
19
26
  import { projectLogger, createModuleLogger } from "./logger.mjs";
27
+ import { DetectionMiddleware } from "./middlewares/DetectionMiddleware.mjs";
20
28
  import { normalizeAssetId } from "./utils.mjs";
21
29
  // ============================================================================
22
30
  // CONTROLLER CONSTANTS
23
31
  // ============================================================================
24
32
  const CONTROLLER_NAME = 'AssetsController';
33
+ /** Method names exposed as messenger actions (AssetsController:getAssets, etc.) */
34
+ const MESSENGER_EXPOSED_METHODS = [
35
+ 'getAssets',
36
+ 'getAssetsBalance',
37
+ 'getAssetMetadata',
38
+ 'getAssetsPrice',
39
+ 'addCustomAsset',
40
+ 'removeCustomAsset',
41
+ 'getCustomAssets',
42
+ 'hideAsset',
43
+ 'unhideAsset',
44
+ ];
25
45
  /** Default polling interval hint for data sources (30 seconds) */
26
46
  const DEFAULT_POLLING_INTERVAL_MS = 30000;
27
47
  const log = createModuleLogger(projectLogger, CONTROLLER_NAME);
@@ -32,7 +52,7 @@ const log = createModuleLogger(projectLogger, CONTROLLER_NAME);
32
52
  */
33
53
  export function getDefaultAssetsControllerState() {
34
54
  return {
35
- assetsMetadata: {},
55
+ assetsInfo: {},
36
56
  assetsBalance: {},
37
57
  assetsPrice: {},
38
58
  customAssets: {},
@@ -43,7 +63,7 @@ export function getDefaultAssetsControllerState() {
43
63
  // STATE METADATA
44
64
  // ============================================================================
45
65
  const stateMetadata = {
46
- assetsMetadata: {
66
+ assetsInfo: {
47
67
  persist: true,
48
68
  includeInStateLogs: false,
49
69
  includeInDebugSnapshot: false,
@@ -91,11 +111,11 @@ function extractChainId(assetId) {
91
111
  */
92
112
  function normalizeResponse(response) {
93
113
  const normalized = {};
94
- if (response.assetsMetadata) {
95
- normalized.assetsMetadata = {};
96
- for (const [assetId, metadata] of Object.entries(response.assetsMetadata)) {
114
+ if (response.assetsInfo) {
115
+ normalized.assetsInfo = {};
116
+ for (const [assetId, metadata] of Object.entries(response.assetsInfo)) {
97
117
  const normalizedId = normalizeAssetId(assetId);
98
- normalized.assetsMetadata[normalizedId] = metadata;
118
+ normalized.assetsInfo[normalizedId] = metadata;
99
119
  }
100
120
  }
101
121
  if (response.assetsPrice) {
@@ -147,13 +167,8 @@ function normalizeResponse(response) {
147
167
  * based on which chains they support. When active chains change, the controller
148
168
  * dynamically adjusts subscriptions.
149
169
  *
150
- * 4. **App Lifecycle Management**: Listens to app open/close events via messenger
151
- * to start/stop subscriptions automatically, conserving resources when app is closed.
152
- *
153
- * ## App Lifecycle
154
- *
155
- * - **App Opened** (`AppStateController:appOpened`): Starts subscriptions, fetches initial data
156
- * - **App Closed** (`AppStateController:appClosed`): Stops all subscriptions to conserve resources
170
+ * 4. **Keyring Lifecycle**: Listens to KeyringController unlock/lock events to
171
+ * start/stop subscriptions when the wallet is unlocked or locked.
157
172
  *
158
173
  * ## Architecture
159
174
  *
@@ -162,7 +177,7 @@ function normalizeResponse(response) {
162
177
  * - The controller does NOT manage polling - it simply receives pushed updates
163
178
  */
164
179
  export class AssetsController extends BaseController {
165
- constructor({ messenger, state = {}, defaultUpdateInterval = DEFAULT_POLLING_INTERVAL_MS, isEnabled = () => true, }) {
180
+ constructor({ messenger, state = {}, defaultUpdateInterval = DEFAULT_POLLING_INTERVAL_MS, isEnabled = () => true, isBasicFunctionality, subscribeToBasicFunctionalityChange, queryApiClient, rpcDataSourceConfig, trackMetaMetricsEvent, accountsApiDataSourceConfig, priceDataSourceConfig, stakedBalanceDataSourceConfig, }) {
166
181
  super({
167
182
  name: CONTROLLER_NAME,
168
183
  messenger,
@@ -175,8 +190,14 @@ export class AssetsController extends BaseController {
175
190
  _AssetsController_instances.add(this);
176
191
  /** Whether the controller is enabled */
177
192
  _AssetsController_isEnabled.set(this, void 0);
193
+ /** Getter for basic functionality (only balance fetch/subscribe use RPC; token/price API not used). No attribute stored. */
194
+ _AssetsController_isBasicFunctionality.set(this, void 0);
178
195
  /** Default update interval hint passed to data sources */
179
196
  _AssetsController_defaultUpdateInterval.set(this, void 0);
197
+ /** Optional callback for first init/fetch MetaMetrics (duration). */
198
+ _AssetsController_trackMetaMetricsEvent.set(this, void 0);
199
+ /** Whether we have already reported first init fetch for this session (reset on #stop). */
200
+ _AssetsController_firstInitFetchReported.set(this, false);
180
201
  _AssetsController_controllerMutex.set(this, new Mutex());
181
202
  /**
182
203
  * Active balance subscriptions keyed by account ID.
@@ -187,79 +208,107 @@ export class AssetsController extends BaseController {
187
208
  _AssetsController_activeSubscriptions.set(this, new Map());
188
209
  /** Currently enabled chains from NetworkEnablementController */
189
210
  _AssetsController_enabledChains.set(this, new Set());
190
- /**
191
- * Registered data sources with their available chains.
192
- * Updated continuously and independently from subscription flows.
193
- * Key: sourceId, Value: Set of currently available chainIds
194
- */
195
- _AssetsController_dataSources.set(this, new Map());
211
+ _AssetsController_backendWebsocketDataSource.set(this, void 0);
212
+ _AssetsController_accountsApiDataSource.set(this, void 0);
213
+ _AssetsController_snapDataSource.set(this, void 0);
214
+ _AssetsController_rpcDataSource.set(this, void 0);
215
+ _AssetsController_stakedBalanceDataSource.set(this, void 0);
216
+ _AssetsController_priceDataSource.set(this, void 0);
217
+ _AssetsController_detectionMiddleware.set(this, void 0);
218
+ _AssetsController_tokenDataSource.set(this, void 0);
219
+ _AssetsController_unsubscribeBasicFunctionality.set(this, null);
196
220
  __classPrivateFieldSet(this, _AssetsController_isEnabled, isEnabled(), "f");
221
+ __classPrivateFieldSet(this, _AssetsController_isBasicFunctionality, isBasicFunctionality ?? (() => true), "f");
197
222
  __classPrivateFieldSet(this, _AssetsController_defaultUpdateInterval, defaultUpdateInterval, "f");
223
+ __classPrivateFieldSet(this, _AssetsController_trackMetaMetricsEvent, trackMetaMetricsEvent, "f");
224
+ const rpcConfig = rpcDataSourceConfig ?? {};
225
+ const onActiveChainsUpdated = (dataSourceName, chains, previousChains) => {
226
+ this.handleActiveChainsUpdate(dataSourceName, chains, previousChains);
227
+ };
228
+ __classPrivateFieldSet(this, _AssetsController_backendWebsocketDataSource, new BackendWebsocketDataSource({
229
+ messenger: this.messenger,
230
+ queryApiClient,
231
+ onActiveChainsUpdated,
232
+ }), "f");
233
+ __classPrivateFieldSet(this, _AssetsController_accountsApiDataSource, new AccountsApiDataSource({
234
+ queryApiClient,
235
+ onActiveChainsUpdated,
236
+ ...accountsApiDataSourceConfig,
237
+ }), "f");
238
+ __classPrivateFieldSet(this, _AssetsController_snapDataSource, new SnapDataSource({
239
+ messenger: this.messenger,
240
+ onActiveChainsUpdated,
241
+ }), "f");
242
+ __classPrivateFieldSet(this, _AssetsController_rpcDataSource, new RpcDataSource({
243
+ messenger: this.messenger,
244
+ onActiveChainsUpdated,
245
+ ...rpcConfig,
246
+ }), "f");
247
+ __classPrivateFieldSet(this, _AssetsController_stakedBalanceDataSource, new StakedBalanceDataSource({
248
+ messenger: this.messenger,
249
+ onActiveChainsUpdated,
250
+ ...stakedBalanceDataSourceConfig,
251
+ }), "f");
252
+ __classPrivateFieldSet(this, _AssetsController_tokenDataSource, new TokenDataSource({
253
+ queryApiClient,
254
+ }), "f");
255
+ __classPrivateFieldSet(this, _AssetsController_priceDataSource, new PriceDataSource({
256
+ queryApiClient,
257
+ ...priceDataSourceConfig,
258
+ }), "f");
259
+ __classPrivateFieldSet(this, _AssetsController_detectionMiddleware, new DetectionMiddleware(), "f");
198
260
  if (!__classPrivateFieldGet(this, _AssetsController_isEnabled, "f")) {
199
261
  log('AssetsController is disabled, skipping initialization');
200
262
  return;
201
263
  }
202
264
  log('Initializing AssetsController', {
203
- defaultUpdateInterval,
265
+ defaultUpdateInterval: __classPrivateFieldGet(this, _AssetsController_defaultUpdateInterval, "f"),
204
266
  });
205
267
  __classPrivateFieldGet(this, _AssetsController_instances, "m", _AssetsController_initializeState).call(this);
206
268
  __classPrivateFieldGet(this, _AssetsController_instances, "m", _AssetsController_subscribeToEvents).call(this);
207
269
  __classPrivateFieldGet(this, _AssetsController_instances, "m", _AssetsController_registerActionHandlers).call(this);
208
- // Register data sources (order = subscription priority)
209
- this.registerDataSources([
210
- 'BackendWebsocketDataSource', // Real-time push updates
211
- 'AccountsApiDataSource', // HTTP polling fallback
212
- 'SnapDataSource', // Solana/Bitcoin/Tron snaps
213
- 'RpcDataSource', // Direct blockchain queries
214
- ]);
215
- }
216
- // ============================================================================
217
- // DATA SOURCE MANAGEMENT
218
- // ============================================================================
219
- /**
220
- * Register data sources with the controller.
221
- * Order of the array determines subscription order.
222
- *
223
- * Data sources report chain changes by calling `AssetsController:activeChainsUpdate` action.
224
- *
225
- * @param dataSourceIds - Array of data source identifiers to register.
226
- */
227
- registerDataSources(dataSourceIds) {
228
- for (const id of dataSourceIds) {
229
- log('Registering data source', { id });
230
- // Initialize available chains tracking for this source
231
- __classPrivateFieldGet(this, _AssetsController_dataSources, "f").set(id, new Set());
270
+ // Subscriptions start only on KeyringController:unlock -> #start(), not here.
271
+ // Subscribe to basic-functionality changes after construction so a synchronous
272
+ // onChange during subscribe cannot run before data sources are initialized.
273
+ if (subscribeToBasicFunctionalityChange) {
274
+ const unsubscribe = subscribeToBasicFunctionalityChange((isBasic) => this.handleBasicFunctionalityChange(isBasic));
275
+ if (typeof unsubscribe === 'function') {
276
+ __classPrivateFieldSet(this, _AssetsController_unsubscribeBasicFunctionality, unsubscribe, "f");
277
+ }
232
278
  }
233
279
  }
234
280
  // ============================================================================
235
281
  // DATA SOURCE CHAIN MANAGEMENT
236
282
  // ============================================================================
237
283
  /**
238
- * Handle when a data source's active chains change.
239
- * Active chains are chains that are both supported AND available.
240
- * Updates centralized chain tracking and triggers re-selection if needed.
284
+ * Handle when a data source's supported chains change.
285
+ * Used to refresh balance subscriptions and run a one-time fetch when a new chain is supported.
286
+ *
287
+ * - On any add/remove: re-subscribes to data sources so chain assignment stays correct.
288
+ * - When chains are added: fetches balances for the new chains (for selected accounts on enabled networks).
241
289
  *
242
- * Data sources should call this via `AssetsController:activeChainsUpdate` action.
290
+ * Controller does not store chains; sources report via this callback. previousChains is required for diff.
243
291
  *
244
292
  * @param dataSourceId - The identifier of the data source reporting the change.
245
- * @param activeChains - Array of currently active chain IDs for this source.
293
+ * @param activeChains - Currently active (supported and available) chain IDs for this source.
294
+ * @param previousChains - Previous chains; used to compute added/removed.
246
295
  */
247
- handleActiveChainsUpdate(dataSourceId, activeChains) {
296
+ handleActiveChainsUpdate(dataSourceId, activeChains, previousChains) {
297
+ if (!__classPrivateFieldGet(this, _AssetsController_isEnabled, "f")) {
298
+ return;
299
+ }
248
300
  log('Data source active chains changed', {
249
301
  dataSourceId,
250
302
  chainCount: activeChains.length,
251
303
  chains: activeChains,
252
304
  });
253
- const previousChains = __classPrivateFieldGet(this, _AssetsController_dataSources, "f").get(dataSourceId) ?? new Set();
254
- const newChains = new Set(activeChains);
255
- // Update centralized available chains tracking
256
- __classPrivateFieldGet(this, _AssetsController_dataSources, "f").set(dataSourceId, newChains);
257
- // Check for changes
258
- const addedChains = activeChains.filter((chain) => !previousChains.has(chain));
259
- const removedChains = Array.from(previousChains).filter((chain) => !newChains.has(chain));
305
+ const previous = previousChains;
306
+ const previousSet = new Set(previous);
307
+ const addedChains = activeChains.filter((ch) => !previousSet.has(ch));
308
+ const removedChains = previous.filter((ch) => !activeChains.includes(ch));
260
309
  if (addedChains.length > 0 || removedChains.length > 0) {
261
310
  // Refresh subscriptions to use updated data source availability
262
- __classPrivateFieldGet(this, _AssetsController_instances, "m", _AssetsController_subscribeToDataSources).call(this);
311
+ __classPrivateFieldGet(this, _AssetsController_instances, "m", _AssetsController_subscribeAssets).call(this);
263
312
  }
264
313
  // If chains were added and we have selected accounts, do one-time fetch
265
314
  if (addedChains.length > 0 && __classPrivateFieldGet(this, _AssetsController_instances, "a", _AssetsController_selectedAccounts_get).length > 0) {
@@ -289,22 +338,39 @@ export class AssetsController extends BaseController {
289
338
  customAssets.push(...accountCustomAssets);
290
339
  }
291
340
  if (options?.forceUpdate) {
292
- const response = await __classPrivateFieldGet(this, _AssetsController_instances, "m", _AssetsController_executeMiddlewares).call(this, [
293
- this.messenger.call('AccountsApiDataSource:getAssetsMiddleware'),
294
- this.messenger.call('SnapDataSource:getAssetsMiddleware'),
295
- this.messenger.call('RpcDataSource:getAssetsMiddleware'),
296
- this.messenger.call('DetectionMiddleware:getAssetsMiddleware'),
297
- this.messenger.call('TokenDataSource:getAssetsMiddleware'),
298
- this.messenger.call('PriceDataSource:getAssetsMiddleware'),
299
- ], {
300
- accounts,
301
- chainIds,
341
+ const startTime = Date.now();
342
+ const request = __classPrivateFieldGet(this, _AssetsController_instances, "m", _AssetsController_buildDataRequest).call(this, accounts, chainIds, {
302
343
  assetTypes,
303
344
  dataTypes,
304
345
  customAssets: customAssets.length > 0 ? customAssets : undefined,
305
346
  forceUpdate: true,
306
347
  });
348
+ const sources = __classPrivateFieldGet(this, _AssetsController_isBasicFunctionality, "f").call(this)
349
+ ? [
350
+ __classPrivateFieldGet(this, _AssetsController_accountsApiDataSource, "f"),
351
+ __classPrivateFieldGet(this, _AssetsController_snapDataSource, "f"),
352
+ __classPrivateFieldGet(this, _AssetsController_rpcDataSource, "f"),
353
+ __classPrivateFieldGet(this, _AssetsController_stakedBalanceDataSource, "f"),
354
+ __classPrivateFieldGet(this, _AssetsController_detectionMiddleware, "f"),
355
+ __classPrivateFieldGet(this, _AssetsController_tokenDataSource, "f"),
356
+ __classPrivateFieldGet(this, _AssetsController_priceDataSource, "f"),
357
+ ]
358
+ : [
359
+ __classPrivateFieldGet(this, _AssetsController_rpcDataSource, "f"),
360
+ __classPrivateFieldGet(this, _AssetsController_stakedBalanceDataSource, "f"),
361
+ __classPrivateFieldGet(this, _AssetsController_detectionMiddleware, "f"),
362
+ ];
363
+ const { response, durationByDataSource } = await __classPrivateFieldGet(this, _AssetsController_instances, "m", _AssetsController_executeMiddlewares).call(this, sources, request);
307
364
  await __classPrivateFieldGet(this, _AssetsController_instances, "m", _AssetsController_updateState).call(this, response);
365
+ if (__classPrivateFieldGet(this, _AssetsController_trackMetaMetricsEvent, "f") && !__classPrivateFieldGet(this, _AssetsController_firstInitFetchReported, "f")) {
366
+ __classPrivateFieldSet(this, _AssetsController_firstInitFetchReported, true, "f");
367
+ const durationMs = Date.now() - startTime;
368
+ __classPrivateFieldGet(this, _AssetsController_trackMetaMetricsEvent, "f").call(this, {
369
+ durationMs,
370
+ chainIds,
371
+ durationByDataSource,
372
+ });
373
+ }
308
374
  }
309
375
  return __classPrivateFieldGet(this, _AssetsController_instances, "m", _AssetsController_getAssetsFromState).call(this, accounts, chainIds, assetTypes);
310
376
  }
@@ -329,7 +395,7 @@ export class AssetsController extends BaseController {
329
395
  return result;
330
396
  }
331
397
  getAssetMetadata(assetId) {
332
- return this.state.assetsMetadata[assetId];
398
+ return this.state.assetsInfo[assetId];
333
399
  }
334
400
  async getAssetsPrice(accounts, options) {
335
401
  const assets = await this.getAssets(accounts, {
@@ -457,33 +523,33 @@ export class AssetsController extends BaseController {
457
523
  }
458
524
  });
459
525
  }
526
+ // ============================================================================
527
+ // SUBSCRIPTIONS
528
+ // ============================================================================
460
529
  /**
461
530
  * Subscribe to price updates for all assets held by the given accounts.
462
531
  * Polls PriceDataSource which fetches prices from balance state.
463
532
  *
464
533
  * @param accounts - Accounts to subscribe price updates for.
465
534
  * @param chainIds - Chain IDs to filter prices for.
466
- * @param options - Subscription options.
467
- * @param options.updateInterval - Polling interval in ms.
468
535
  */
469
- subscribeAssetsPrice(accounts, chainIds, options = {}) {
470
- const { updateInterval = __classPrivateFieldGet(this, _AssetsController_defaultUpdateInterval, "f") } = options;
536
+ subscribeAssetsPrice(accounts, chainIds) {
537
+ if (!__classPrivateFieldGet(this, _AssetsController_isBasicFunctionality, "f").call(this)) {
538
+ return;
539
+ }
471
540
  const subscriptionKey = 'ds:PriceDataSource';
472
541
  const existingSubscription = __classPrivateFieldGet(this, _AssetsController_activeSubscriptions, "f").get(subscriptionKey);
473
542
  const isUpdate = existingSubscription !== undefined;
474
- // Fire-and-forget - errors are handled internally by PriceDataSource
475
- this.messenger
476
- .call('PriceDataSource:subscribe', {
477
- request: {
478
- accounts,
479
- chainIds,
543
+ const subscribeReq = {
544
+ request: __classPrivateFieldGet(this, _AssetsController_instances, "m", _AssetsController_buildDataRequest).call(this, accounts, chainIds, {
480
545
  dataTypes: ['price'],
481
- updateInterval,
482
- },
546
+ }),
483
547
  subscriptionId: subscriptionKey,
484
548
  isUpdate,
485
- })
486
- .catch(console.error);
549
+ onAssetsUpdate: (response) => this.handleAssetsUpdate(response, 'PriceDataSource'),
550
+ getAssetsState: () => this.state,
551
+ };
552
+ __classPrivateFieldGet(this, _AssetsController_priceDataSource, "f").subscribe(subscribeReq).catch(console.error);
487
553
  // Track subscription
488
554
  const subscription = {
489
555
  chains: chainIds,
@@ -505,46 +571,70 @@ export class AssetsController extends BaseController {
505
571
  if (!existingSubscription) {
506
572
  return;
507
573
  }
508
- // Fire-and-forget - errors are handled internally by PriceDataSource
509
- this.messenger
510
- .call('PriceDataSource:unsubscribe', subscriptionKey)
511
- .catch(console.error);
574
+ __classPrivateFieldGet(this, _AssetsController_priceDataSource, "f").unsubscribe(subscriptionKey).catch(console.error);
512
575
  existingSubscription.unsubscribe();
513
576
  }
577
+ /**
578
+ * Handle basic functionality toggle change. Call this from the consumer (extension or mobile)
579
+ * when the user changes the "Basic functionality" setting. Refreshes subscriptions so the
580
+ * current {@link AssetsControllerOptions.isBasicFunctionality} getter is used (true = APIs on,
581
+ * false = RPC only).
582
+ *
583
+ * @param _isBasic - The new value (for call-site clarity; the getter is the source of truth).
584
+ */
585
+ handleBasicFunctionalityChange(_isBasic) {
586
+ __classPrivateFieldGet(this, _AssetsController_instances, "m", _AssetsController_stop).call(this);
587
+ __classPrivateFieldGet(this, _AssetsController_instances, "m", _AssetsController_subscribeAssets).call(this);
588
+ }
514
589
  /**
515
590
  * Handle assets updated from a data source.
516
- * Called via `AssetsController:assetsUpdate` action by data sources.
591
+ * Called via the onAssetsUpdate callback passed in SubscriptionRequest when the controller subscribes to a data source.
592
+ * Enriches the response with token metadata (via middlewares) before updating state.
517
593
  *
518
594
  * @param response - The data response with updated assets
519
595
  * @param sourceId - The data source ID reporting the update
596
+ * @param request - Optional original request for context when enriching
520
597
  */
521
- async handleAssetsUpdate(response, sourceId) {
598
+ async handleAssetsUpdate(response, sourceId, request) {
522
599
  log('Assets updated from data source', {
523
600
  sourceId,
524
601
  hasBalance: Boolean(response.assetsBalance),
525
602
  hasPrice: Boolean(response.assetsPrice),
526
603
  });
527
- await __classPrivateFieldGet(this, _AssetsController_instances, "m", _AssetsController_handleSubscriptionUpdate).call(this, response, sourceId);
604
+ // Run through enrichment middlewares (Event Stack: Detection → Token → Price)
605
+ // Include 'metadata' in dataTypes so TokenDataSource runs to enrich detected assets
606
+ const { response: enrichedResponse } = await __classPrivateFieldGet(this, _AssetsController_instances, "m", _AssetsController_executeMiddlewares).call(this, [__classPrivateFieldGet(this, _AssetsController_detectionMiddleware, "f"), __classPrivateFieldGet(this, _AssetsController_tokenDataSource, "f"), __classPrivateFieldGet(this, _AssetsController_priceDataSource, "f")], request ?? {
607
+ accountsWithSupportedChains: [],
608
+ chainIds: [],
609
+ dataTypes: ['balance', 'metadata', 'price'],
610
+ }, response);
611
+ await __classPrivateFieldGet(this, _AssetsController_instances, "m", _AssetsController_updateState).call(this, enrichedResponse);
528
612
  }
529
613
  // ============================================================================
530
614
  // CLEANUP
531
615
  // ============================================================================
532
616
  destroy() {
533
617
  log('Destroying AssetsController', {
534
- dataSourceCount: __classPrivateFieldGet(this, _AssetsController_dataSources, "f").size,
618
+ dataSourceCount: __classPrivateFieldGet(this, _AssetsController_instances, "a", _AssetsController_allBalanceDataSources_get).length,
535
619
  subscriptionCount: __classPrivateFieldGet(this, _AssetsController_activeSubscriptions, "f").size,
536
620
  });
537
- // Clear data sources
538
- __classPrivateFieldGet(this, _AssetsController_dataSources, "f").clear();
621
+ // Destroy instantiated data sources
622
+ __classPrivateFieldGet(this, _AssetsController_backendWebsocketDataSource, "f")?.destroy?.();
623
+ __classPrivateFieldGet(this, _AssetsController_accountsApiDataSource, "f")?.destroy?.();
624
+ __classPrivateFieldGet(this, _AssetsController_snapDataSource, "f")?.destroy?.();
625
+ __classPrivateFieldGet(this, _AssetsController_rpcDataSource, "f")?.destroy?.();
626
+ __classPrivateFieldGet(this, _AssetsController_stakedBalanceDataSource, "f")?.destroy?.();
539
627
  // Stop all active subscriptions
540
628
  __classPrivateFieldGet(this, _AssetsController_instances, "m", _AssetsController_stop).call(this);
629
+ if (__classPrivateFieldGet(this, _AssetsController_unsubscribeBasicFunctionality, "f")) {
630
+ __classPrivateFieldGet(this, _AssetsController_unsubscribeBasicFunctionality, "f").call(this);
631
+ __classPrivateFieldSet(this, _AssetsController_unsubscribeBasicFunctionality, null, "f");
632
+ }
541
633
  // Unregister action handlers
542
634
  this.messenger.unregisterActionHandler('AssetsController:getAssets');
543
635
  this.messenger.unregisterActionHandler('AssetsController:getAssetsBalance');
544
636
  this.messenger.unregisterActionHandler('AssetsController:getAssetMetadata');
545
637
  this.messenger.unregisterActionHandler('AssetsController:getAssetsPrice');
546
- this.messenger.unregisterActionHandler('AssetsController:activeChainsUpdate');
547
- this.messenger.unregisterActionHandler('AssetsController:assetsUpdate');
548
638
  this.messenger.unregisterActionHandler('AssetsController:addCustomAsset');
549
639
  this.messenger.unregisterActionHandler('AssetsController:removeCustomAsset');
550
640
  this.messenger.unregisterActionHandler('AssetsController:getCustomAssets');
@@ -552,8 +642,15 @@ export class AssetsController extends BaseController {
552
642
  this.messenger.unregisterActionHandler('AssetsController:unhideAsset');
553
643
  }
554
644
  }
555
- _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() {
645
+ _AssetsController_isEnabled = new WeakMap(), _AssetsController_isBasicFunctionality = new WeakMap(), _AssetsController_defaultUpdateInterval = new WeakMap(), _AssetsController_trackMetaMetricsEvent = new WeakMap(), _AssetsController_firstInitFetchReported = new WeakMap(), _AssetsController_controllerMutex = new WeakMap(), _AssetsController_activeSubscriptions = new WeakMap(), _AssetsController_enabledChains = new WeakMap(), _AssetsController_backendWebsocketDataSource = new WeakMap(), _AssetsController_accountsApiDataSource = new WeakMap(), _AssetsController_snapDataSource = new WeakMap(), _AssetsController_rpcDataSource = new WeakMap(), _AssetsController_stakedBalanceDataSource = new WeakMap(), _AssetsController_priceDataSource = new WeakMap(), _AssetsController_detectionMiddleware = new WeakMap(), _AssetsController_tokenDataSource = new WeakMap(), _AssetsController_unsubscribeBasicFunctionality = new WeakMap(), _AssetsController_instances = new WeakSet(), _AssetsController_selectedAccounts_get = function _AssetsController_selectedAccounts_get() {
556
646
  return this.messenger.call('AccountTreeController:getAccountsFromSelectedAccountGroup');
647
+ }, _AssetsController_allBalanceDataSources_get = function _AssetsController_allBalanceDataSources_get() {
648
+ return [
649
+ __classPrivateFieldGet(this, _AssetsController_backendWebsocketDataSource, "f"),
650
+ __classPrivateFieldGet(this, _AssetsController_accountsApiDataSource, "f"),
651
+ __classPrivateFieldGet(this, _AssetsController_snapDataSource, "f"),
652
+ __classPrivateFieldGet(this, _AssetsController_rpcDataSource, "f"),
653
+ ];
557
654
  }, _AssetsController_initializeState = function _AssetsController_initializeState() {
558
655
  const { enabledNetworkMap } = this.messenger.call('NetworkEnablementController:getState');
559
656
  __classPrivateFieldSet(this, _AssetsController_enabledChains, __classPrivateFieldGet(this, _AssetsController_instances, "m", _AssetsController_extractEnabledChains).call(this, enabledNetworkMap), "f");
@@ -581,7 +678,7 @@ _AssetsController_isEnabled = new WeakMap(), _AssetsController_defaultUpdateInte
581
678
  }
582
679
  return chains;
583
680
  }, _AssetsController_normalizeChainReference = function _AssetsController_normalizeChainReference(namespace, reference) {
584
- if (namespace === 'eip155' && reference.startsWith('0x')) {
681
+ if (namespace === 'eip155' && isStrictHexString(reference)) {
585
682
  // Convert hex to decimal for EIP155 chains
586
683
  return parseInt(reference, 16).toString();
587
684
  }
@@ -595,38 +692,38 @@ _AssetsController_isEnabled = new WeakMap(), _AssetsController_defaultUpdateInte
595
692
  this.messenger.subscribe('NetworkEnablementController:stateChange', ({ enabledNetworkMap }) => {
596
693
  __classPrivateFieldGet(this, _AssetsController_instances, "m", _AssetsController_handleEnabledNetworksChanged).call(this, enabledNetworkMap).catch(console.error);
597
694
  });
598
- // App lifecycle: start when opened, stop when closed
599
- this.messenger.subscribe('AppStateController:appOpened', () => __classPrivateFieldGet(this, _AssetsController_instances, "m", _AssetsController_start).call(this));
600
- this.messenger.subscribe('AppStateController:appClosed', () => __classPrivateFieldGet(this, _AssetsController_instances, "m", _AssetsController_stop).call(this));
601
695
  // Keyring lifecycle: start when unlocked, stop when locked
602
696
  this.messenger.subscribe('KeyringController:unlock', () => __classPrivateFieldGet(this, _AssetsController_instances, "m", _AssetsController_start).call(this));
603
697
  this.messenger.subscribe('KeyringController:lock', () => __classPrivateFieldGet(this, _AssetsController_instances, "m", _AssetsController_stop).call(this));
604
698
  }, _AssetsController_registerActionHandlers = function _AssetsController_registerActionHandlers() {
605
- this.messenger.registerActionHandler('AssetsController:getAssets', this.getAssets.bind(this));
606
- this.messenger.registerActionHandler('AssetsController:getAssetsBalance', this.getAssetsBalance.bind(this));
607
- this.messenger.registerActionHandler('AssetsController:getAssetMetadata', this.getAssetMetadata.bind(this));
608
- this.messenger.registerActionHandler('AssetsController:getAssetsPrice', this.getAssetsPrice.bind(this));
609
- this.messenger.registerActionHandler('AssetsController:activeChainsUpdate', this.handleActiveChainsUpdate.bind(this));
610
- this.messenger.registerActionHandler('AssetsController:assetsUpdate', this.handleAssetsUpdate.bind(this));
611
- this.messenger.registerActionHandler('AssetsController:addCustomAsset', this.addCustomAsset.bind(this));
612
- this.messenger.registerActionHandler('AssetsController:removeCustomAsset', this.removeCustomAsset.bind(this));
613
- this.messenger.registerActionHandler('AssetsController:getCustomAssets', this.getCustomAssets.bind(this));
614
- this.messenger.registerActionHandler('AssetsController:hideAsset', this.hideAsset.bind(this));
615
- this.messenger.registerActionHandler('AssetsController:unhideAsset', this.unhideAsset.bind(this));
699
+ this.messenger.registerMethodActionHandlers(this, MESSENGER_EXPOSED_METHODS);
616
700
  }, _AssetsController_executeMiddlewares =
617
701
  // ============================================================================
618
702
  // MIDDLEWARE EXECUTION
619
703
  // ============================================================================
620
704
  /**
621
705
  * Execute middlewares with request/response context.
706
+ * Returns response and exclusive duration per source (sum ≈ wall time).
622
707
  *
623
- * @param middlewares - Middlewares to execute in order.
708
+ * @param sources - Data sources or middlewares with getName() and assetsMiddleware (executed in order).
624
709
  * @param request - The data request.
625
710
  * @param initialResponse - Optional initial response (for enriching existing data).
626
- * @returns The final DataResponse after all middlewares have processed.
711
+ * @returns Response and durationByDataSource (exclusive ms per source name).
627
712
  */
628
- async function _AssetsController_executeMiddlewares(middlewares, request, initialResponse = {}) {
629
- const chain = middlewares.reduceRight((next, middleware) => async (ctx) => {
713
+ async function _AssetsController_executeMiddlewares(sources, request, initialResponse = {}) {
714
+ const names = sources.map((source) => source.getName());
715
+ const middlewares = sources.map((source) => source.assetsMiddleware);
716
+ const inclusive = [];
717
+ const wrapped = middlewares.map((middleware, i) => (async (ctx, next) => {
718
+ const start = Date.now();
719
+ try {
720
+ return await middleware(ctx, next);
721
+ }
722
+ finally {
723
+ inclusive[i] = Date.now() - start;
724
+ }
725
+ }));
726
+ const chain = wrapped.reduceRight((next, middleware) => async (ctx) => {
630
727
  try {
631
728
  return await middleware(ctx, next);
632
729
  }
@@ -640,33 +737,16 @@ async function _AssetsController_executeMiddlewares(middlewares, request, initia
640
737
  response: initialResponse,
641
738
  getAssetsState: () => this.state,
642
739
  });
643
- return result.response;
644
- }, _AssetsController_assignChainsToDataSources = function _AssetsController_assignChainsToDataSources(requestedChains) {
645
- const assignment = new Map();
646
- const remainingChains = new Set(requestedChains);
647
- for (const sourceId of __classPrivateFieldGet(this, _AssetsController_dataSources, "f").keys()) {
648
- // Get available chains for this data source
649
- const availableChains = __classPrivateFieldGet(this, _AssetsController_dataSources, "f").get(sourceId);
650
- if (!availableChains || availableChains.size === 0) {
651
- continue;
652
- }
653
- const chainsForThisSource = [];
654
- for (const chainId of remainingChains) {
655
- // Check if this chain is available on this source
656
- if (availableChains.has(chainId)) {
657
- chainsForThisSource.push(chainId);
658
- remainingChains.delete(chainId);
659
- }
660
- }
661
- if (chainsForThisSource.length > 0) {
662
- assignment.set(sourceId, chainsForThisSource);
663
- log('Assigned chains to data source', {
664
- sourceId,
665
- chains: chainsForThisSource,
666
- });
740
+ const durationByDataSource = {};
741
+ for (let i = 0; i < inclusive.length; i++) {
742
+ const nextInc = i + 1 < inclusive.length ? (inclusive[i + 1] ?? 0) : 0;
743
+ const exclusive = Math.max(0, (inclusive[i] ?? 0) - nextInc);
744
+ const name = names[i];
745
+ if (name !== undefined) {
746
+ durationByDataSource[name] = exclusive;
667
747
  }
668
748
  }
669
- return assignment;
749
+ return { response: result.response, durationByDataSource };
670
750
  }, _AssetsController_updateState =
671
751
  // ============================================================================
672
752
  // STATE MANAGEMENT
@@ -685,12 +765,12 @@ async function _AssetsController_updateState(response) {
685
765
  const changedMetadata = [];
686
766
  this.update((state) => {
687
767
  // Use type assertions to avoid deep type instantiation issues with Immer Draft types
688
- const metadata = state.assetsMetadata;
768
+ const metadata = state.assetsInfo;
689
769
  const balances = state.assetsBalance;
690
770
  const prices = state.assetsPrice;
691
- if (normalizedResponse.assetsMetadata) {
692
- for (const [key, value] of Object.entries(normalizedResponse.assetsMetadata)) {
693
- if (!isEqual(previousState.assetsMetadata[key], value)) {
771
+ if (normalizedResponse.assetsInfo) {
772
+ for (const [key, value] of Object.entries(normalizedResponse.assetsInfo)) {
773
+ if (!isEqual(previousState.assetsInfo[key], value)) {
694
774
  changedMetadata.push(key);
695
775
  }
696
776
  metadata[key] = value;
@@ -795,7 +875,7 @@ async function _AssetsController_updateState(response) {
795
875
  const accountBalances = this.state.assetsBalance[account.id] ?? {};
796
876
  for (const [assetId, balance] of Object.entries(accountBalances)) {
797
877
  const typedAssetId = assetId;
798
- const metadataRaw = this.state.assetsMetadata[typedAssetId];
878
+ const metadataRaw = this.state.assetsInfo[typedAssetId];
799
879
  // Skip assets without metadata
800
880
  if (!metadataRaw) {
801
881
  continue;
@@ -860,98 +940,112 @@ async function _AssetsController_updateState(response) {
860
940
  selectedAccountCount: __classPrivateFieldGet(this, _AssetsController_instances, "a", _AssetsController_selectedAccounts_get).length,
861
941
  enabledChainCount: __classPrivateFieldGet(this, _AssetsController_enabledChains, "f").size,
862
942
  });
863
- __classPrivateFieldGet(this, _AssetsController_instances, "m", _AssetsController_subscribeToDataSources).call(this);
864
- if (__classPrivateFieldGet(this, _AssetsController_instances, "a", _AssetsController_selectedAccounts_get).length > 0) {
865
- this.getAssets(__classPrivateFieldGet(this, _AssetsController_instances, "a", _AssetsController_selectedAccounts_get), {
866
- chainIds: [...__classPrivateFieldGet(this, _AssetsController_enabledChains, "f")],
867
- forceUpdate: true,
868
- }).catch((error) => {
869
- log('Failed to fetch assets', error);
870
- });
871
- }
943
+ __classPrivateFieldGet(this, _AssetsController_instances, "m", _AssetsController_subscribeAssets).call(this);
944
+ this.getAssets(__classPrivateFieldGet(this, _AssetsController_instances, "a", _AssetsController_selectedAccounts_get), {
945
+ chainIds: [...__classPrivateFieldGet(this, _AssetsController_enabledChains, "f")],
946
+ forceUpdate: true,
947
+ }).catch((error) => {
948
+ log('Failed to fetch assets', error);
949
+ });
872
950
  }, _AssetsController_stop = function _AssetsController_stop() {
873
951
  log('Stopping asset tracking', {
874
952
  activeSubscriptionCount: __classPrivateFieldGet(this, _AssetsController_activeSubscriptions, "f").size,
875
953
  hasPriceSubscription: __classPrivateFieldGet(this, _AssetsController_activeSubscriptions, "f").has('ds:PriceDataSource'),
876
954
  });
955
+ __classPrivateFieldSet(this, _AssetsController_firstInitFetchReported, false, "f");
877
956
  // Stop price subscription first (uses direct messenger call)
878
957
  this.unsubscribeAssetsPrice();
879
958
  // Stop balance subscriptions by properly notifying data sources via messenger
880
- // This ensures data sources stop their polling timers
881
- // Convert to array first to avoid modifying map during iteration
959
+ // This ensures data sources stop their polling timers.
960
+ // Use #allBalanceDataSources + staked balance source so we unsubscribe from
961
+ // every source that may have been subscribed.
962
+ const allSources = [
963
+ ...__classPrivateFieldGet(this, _AssetsController_instances, "a", _AssetsController_allBalanceDataSources_get),
964
+ __classPrivateFieldGet(this, _AssetsController_stakedBalanceDataSource, "f"),
965
+ ];
882
966
  const subscriptionKeys = [...__classPrivateFieldGet(this, _AssetsController_activeSubscriptions, "f").keys()];
883
967
  for (const subscriptionKey of subscriptionKeys) {
884
- // Extract sourceId from subscription key (format: "ds:${sourceId}")
885
968
  if (subscriptionKey.startsWith('ds:')) {
886
969
  const sourceId = subscriptionKey.slice(3);
887
- __classPrivateFieldGet(this, _AssetsController_instances, "m", _AssetsController_unsubscribeDataSource).call(this, sourceId);
970
+ const source = allSources.find((ds) => ds.getName() === sourceId);
971
+ if (source) {
972
+ __classPrivateFieldGet(this, _AssetsController_instances, "m", _AssetsController_unsubscribeDataSource).call(this, source);
973
+ }
888
974
  }
889
975
  }
890
976
  __classPrivateFieldGet(this, _AssetsController_activeSubscriptions, "f").clear();
891
- }, _AssetsController_subscribeToDataSources = function _AssetsController_subscribeToDataSources() {
977
+ }, _AssetsController_subscribeAssets = function _AssetsController_subscribeAssets() {
892
978
  if (__classPrivateFieldGet(this, _AssetsController_instances, "a", _AssetsController_selectedAccounts_get).length === 0) {
893
979
  return;
894
980
  }
895
981
  // Subscribe to balance updates (batched by data source)
896
- __classPrivateFieldGet(this, _AssetsController_instances, "m", _AssetsController_subscribeAssetsBalance).call(this);
982
+ __classPrivateFieldGet(this, _AssetsController_instances, "m", _AssetsController_subscribeAssetsBalance).call(this, __classPrivateFieldGet(this, _AssetsController_instances, "a", _AssetsController_selectedAccounts_get), [
983
+ ...__classPrivateFieldGet(this, _AssetsController_enabledChains, "f"),
984
+ ]);
985
+ // Subscribe to staked balance updates (separate from regular balance chain-claiming)
986
+ __classPrivateFieldGet(this, _AssetsController_instances, "m", _AssetsController_subscribeStakedBalance).call(this, __classPrivateFieldGet(this, _AssetsController_instances, "a", _AssetsController_selectedAccounts_get), [
987
+ ...__classPrivateFieldGet(this, _AssetsController_enabledChains, "f"),
988
+ ]);
897
989
  // Subscribe to price updates for all assets held by selected accounts
898
990
  this.subscribeAssetsPrice(__classPrivateFieldGet(this, _AssetsController_instances, "a", _AssetsController_selectedAccounts_get), [...__classPrivateFieldGet(this, _AssetsController_enabledChains, "f")]);
899
- }, _AssetsController_subscribeAssetsBalance = function _AssetsController_subscribeAssetsBalance() {
900
- // Step 1: Build chain -> accounts mapping based on account scopes and enabled networks
901
- const chainToAccounts = __classPrivateFieldGet(this, _AssetsController_instances, "m", _AssetsController_buildChainToAccountsMap).call(this, __classPrivateFieldGet(this, _AssetsController_instances, "a", _AssetsController_selectedAccounts_get), __classPrivateFieldGet(this, _AssetsController_enabledChains, "f"));
902
- // Step 2: Split by data source active chains (ordered by priority)
903
- // Get all chains that need to be subscribed
991
+ }, _AssetsController_subscribeAssetsBalance = function _AssetsController_subscribeAssetsBalance(accounts, chainIds) {
992
+ const chainToAccounts = __classPrivateFieldGet(this, _AssetsController_instances, "m", _AssetsController_buildChainToAccountsMap).call(this, accounts, new Set(chainIds));
904
993
  const remainingChains = new Set(chainToAccounts.keys());
905
- // Assign chains to data sources based on availability (ordered by priority)
906
- const chainAssignment = __classPrivateFieldGet(this, _AssetsController_instances, "m", _AssetsController_assignChainsToDataSources).call(this, Array.from(remainingChains));
907
- log('Subscribe - chain assignment', {
908
- totalChains: remainingChains.size,
909
- dataSourceAssignments: Array.from(chainAssignment.entries()).map(([sourceId, chains]) => ({ sourceId, chainCount: chains.length })),
910
- });
911
- // Subscribe to each data source with its assigned chains and relevant accounts
912
- for (const sourceId of __classPrivateFieldGet(this, _AssetsController_dataSources, "f").keys()) {
913
- const assignedChains = chainAssignment.get(sourceId);
914
- if (!assignedChains || assignedChains.length === 0) {
915
- // Unsubscribe from data sources with no assigned chains
916
- __classPrivateFieldGet(this, _AssetsController_instances, "m", _AssetsController_unsubscribeDataSource).call(this, sourceId);
917
- continue;
994
+ // When basic functionality is on (getter true), use all balance data sources; when off (getter false), RPC only.
995
+ const balanceDataSources = __classPrivateFieldGet(this, _AssetsController_isBasicFunctionality, "f").call(this)
996
+ ? __classPrivateFieldGet(this, _AssetsController_instances, "a", _AssetsController_allBalanceDataSources_get)
997
+ : [__classPrivateFieldGet(this, _AssetsController_rpcDataSource, "f")];
998
+ for (const source of balanceDataSources) {
999
+ const availableChains = new Set(source.getActiveChainsSync());
1000
+ const assignedChains = [];
1001
+ for (const chainId of remainingChains) {
1002
+ if (availableChains.has(chainId)) {
1003
+ assignedChains.push(chainId);
1004
+ remainingChains.delete(chainId);
1005
+ }
918
1006
  }
919
- // Collect unique accounts that need any of the assigned chains
920
- const accountsForSource = __classPrivateFieldGet(this, _AssetsController_instances, "m", _AssetsController_getAccountsForChains).call(this, assignedChains, chainToAccounts);
921
- if (accountsForSource.length === 0) {
1007
+ if (assignedChains.length === 0) {
1008
+ __classPrivateFieldGet(this, _AssetsController_instances, "m", _AssetsController_unsubscribeDataSource).call(this, source);
922
1009
  continue;
923
1010
  }
924
- // Subscribe with ONE call per data source
925
- __classPrivateFieldGet(this, _AssetsController_instances, "m", _AssetsController_subscribeToDataSource).call(this, sourceId, accountsForSource, assignedChains);
1011
+ const seenIds = new Set();
1012
+ const accountsForSource = assignedChains
1013
+ .flatMap((chainId) => chainToAccounts.get(chainId) ?? [])
1014
+ .filter((account) => !seenIds.has(account.id) && (seenIds.add(account.id), true));
1015
+ if (accountsForSource.length > 0) {
1016
+ __classPrivateFieldGet(this, _AssetsController_instances, "m", _AssetsController_subscribeDataSource).call(this, source, accountsForSource, assignedChains);
1017
+ }
1018
+ }
1019
+ }, _AssetsController_subscribeStakedBalance = function _AssetsController_subscribeStakedBalance(accounts, chainIds) {
1020
+ const source = __classPrivateFieldGet(this, _AssetsController_stakedBalanceDataSource, "f");
1021
+ if (!source) {
1022
+ return;
926
1023
  }
1024
+ const availableChains = new Set(source.getActiveChainsSync());
1025
+ const assignedChains = chainIds.filter((chainId) => availableChains.has(chainId));
1026
+ if (assignedChains.length === 0) {
1027
+ __classPrivateFieldGet(this, _AssetsController_instances, "m", _AssetsController_unsubscribeDataSource).call(this, source);
1028
+ return;
1029
+ }
1030
+ __classPrivateFieldGet(this, _AssetsController_instances, "m", _AssetsController_subscribeDataSource).call(this, source, accounts, assignedChains);
927
1031
  }, _AssetsController_buildChainToAccountsMap = function _AssetsController_buildChainToAccountsMap(accounts, chainsToSubscribe) {
928
1032
  const chainToAccounts = new Map();
929
1033
  for (const account of accounts) {
930
- const accountChains = __classPrivateFieldGet(this, _AssetsController_instances, "m", _AssetsController_getEnabledChainsForAccount).call(this, account);
931
- for (const chainId of accountChains) {
1034
+ for (const chainId of __classPrivateFieldGet(this, _AssetsController_instances, "m", _AssetsController_getEnabledChainsForAccount).call(this, account)) {
932
1035
  if (!chainsToSubscribe.has(chainId)) {
933
1036
  continue;
934
1037
  }
935
- const existingAccounts = chainToAccounts.get(chainId) ?? [];
936
- existingAccounts.push(account);
937
- chainToAccounts.set(chainId, existingAccounts);
938
- }
939
- }
940
- return chainToAccounts;
941
- }, _AssetsController_getAccountsForChains = function _AssetsController_getAccountsForChains(chains, chainToAccounts) {
942
- const accountIds = new Set();
943
- const accounts = [];
944
- for (const chainId of chains) {
945
- const chainAccounts = chainToAccounts.get(chainId) ?? [];
946
- for (const account of chainAccounts) {
947
- if (!accountIds.has(account.id)) {
948
- accountIds.add(account.id);
949
- accounts.push(account);
1038
+ let list = chainToAccounts.get(chainId);
1039
+ if (!list) {
1040
+ list = [];
1041
+ chainToAccounts.set(chainId, list);
950
1042
  }
1043
+ list.push(account);
951
1044
  }
952
1045
  }
953
- return accounts;
954
- }, _AssetsController_subscribeToDataSource = function _AssetsController_subscribeToDataSource(sourceId, accounts, chains) {
1046
+ return chainToAccounts;
1047
+ }, _AssetsController_subscribeDataSource = function _AssetsController_subscribeDataSource(source, accounts, chains) {
1048
+ const sourceId = source.getName();
955
1049
  const subscriptionKey = `ds:${sourceId}`;
956
1050
  const existingSubscription = __classPrivateFieldGet(this, _AssetsController_activeSubscriptions, "f").get(subscriptionKey);
957
1051
  const isUpdate = existingSubscription !== undefined;
@@ -962,68 +1056,72 @@ async function _AssetsController_updateState(response) {
962
1056
  accountCount: accounts.length,
963
1057
  chainCount: chains.length,
964
1058
  });
965
- // Call data source subscribe action via Messenger (fire-and-forget)
966
- (async () => {
967
- try {
968
- await this.messenger.call(`${sourceId}:subscribe`, {
969
- request: {
970
- accounts,
971
- chainIds: chains,
972
- assetTypes: ['fungible'],
973
- dataTypes: ['balance'],
974
- updateInterval: __classPrivateFieldGet(this, _AssetsController_defaultUpdateInterval, "f"),
975
- },
976
- subscriptionId: subscriptionKey,
977
- isUpdate,
978
- });
979
- }
980
- catch (error) {
981
- console.error(`[AssetsController] Failed to subscribe to '${sourceId}':`, error);
982
- }
983
- })().catch(console.error);
1059
+ const subscribeReq = {
1060
+ request: __classPrivateFieldGet(this, _AssetsController_instances, "m", _AssetsController_buildDataRequest).call(this, accounts, chains, {
1061
+ assetTypes: ['fungible'],
1062
+ dataTypes: ['balance'],
1063
+ updateInterval: __classPrivateFieldGet(this, _AssetsController_defaultUpdateInterval, "f"),
1064
+ }),
1065
+ subscriptionId: subscriptionKey,
1066
+ isUpdate,
1067
+ onAssetsUpdate: (response) => this.handleAssetsUpdate(response, sourceId),
1068
+ getAssetsState: () => this.state,
1069
+ };
1070
+ source.subscribe(subscribeReq).catch((error) => {
1071
+ console.error(`[AssetsController] Failed to subscribe to '${sourceId}':`, error);
1072
+ });
984
1073
  // Track subscription
985
1074
  const subscription = {
986
1075
  chains,
987
1076
  accountId: subscriptionKey,
988
1077
  assetTypes: ['fungible'],
989
- dataTypes: ['balance', 'price'],
1078
+ dataTypes: ['balance'],
990
1079
  unsubscribe: () => {
991
1080
  __classPrivateFieldGet(this, _AssetsController_activeSubscriptions, "f").delete(subscriptionKey);
992
1081
  },
993
1082
  };
994
1083
  __classPrivateFieldGet(this, _AssetsController_activeSubscriptions, "f").set(subscriptionKey, subscription);
995
- }, _AssetsController_unsubscribeDataSource = function _AssetsController_unsubscribeDataSource(sourceId) {
996
- const subscriptionKey = `ds:${sourceId}`;
1084
+ }, _AssetsController_unsubscribeDataSource = function _AssetsController_unsubscribeDataSource(source) {
1085
+ const subscriptionKey = `ds:${source.getName()}`;
997
1086
  const existingSubscription = __classPrivateFieldGet(this, _AssetsController_activeSubscriptions, "f").get(subscriptionKey);
998
1087
  if (existingSubscription) {
999
- // Fire-and-forget unsubscribe call
1000
- (async () => {
1001
- try {
1002
- await this.messenger.call(`${sourceId}:unsubscribe`, subscriptionKey);
1003
- }
1004
- catch {
1005
- // Ignore errors - source may not have been subscribed
1006
- }
1007
- })().catch(() => {
1008
- // Ignore errors - source may not have been subscribed
1009
- });
1088
+ source.unsubscribe(subscriptionKey).catch(() => undefined);
1010
1089
  existingSubscription.unsubscribe();
1011
1090
  }
1091
+ }, _AssetsController_buildDataRequest = function _AssetsController_buildDataRequest(accounts, chainIds, partial = {}) {
1092
+ const chainIdSet = new Set(chainIds);
1093
+ const accountsWithSupportedChains = accounts.map((account) => ({
1094
+ account,
1095
+ supportedChains: __classPrivateFieldGet(this, _AssetsController_instances, "m", _AssetsController_getEnabledChainsForAccount).call(this, account).filter((chain) => chainIdSet.has(chain)),
1096
+ }));
1097
+ return {
1098
+ accountsWithSupportedChains,
1099
+ chainIds,
1100
+ ...partial,
1101
+ };
1012
1102
  }, _AssetsController_getEnabledChainsForAccount = function _AssetsController_getEnabledChainsForAccount(account) {
1013
1103
  // Account scopes are CAIP-2 chain IDs like "eip155:1", "solana:mainnet", "bip122:..."
1014
1104
  const scopes = account.scopes ?? [];
1015
1105
  const result = [];
1016
1106
  for (const scope of scopes) {
1017
- const [namespace, reference] = scope.split(':');
1107
+ const scopeStr = scope;
1108
+ if (!isCaipChainId(scopeStr)) {
1109
+ result.push(scope);
1110
+ continue;
1111
+ }
1112
+ const { namespace, reference } = parseCaipChainId(scopeStr);
1018
1113
  // Wildcard scope (e.g., "eip155:0" means all enabled chains in that namespace)
1019
1114
  if (reference === '0') {
1020
1115
  for (const chain of __classPrivateFieldGet(this, _AssetsController_enabledChains, "f")) {
1021
- if (chain.startsWith(`${namespace}:`)) {
1022
- result.push(chain);
1116
+ if (isCaipChainId(chain)) {
1117
+ const chainParsed = parseCaipChainId(chain);
1118
+ if (chainParsed.namespace === namespace) {
1119
+ result.push(chain);
1120
+ }
1023
1121
  }
1024
1122
  }
1025
1123
  }
1026
- else if (namespace === 'eip155' && reference?.startsWith('0x')) {
1124
+ else if (namespace === 'eip155' && isStrictHexString(reference)) {
1027
1125
  // Normalize hex to decimal for EIP155
1028
1126
  result.push(`eip155:${parseInt(reference, 16)}`);
1029
1127
  }
@@ -1043,7 +1141,7 @@ async function _AssetsController_handleAccountGroupChanged() {
1043
1141
  accountIds: accounts.map((a) => a.id),
1044
1142
  });
1045
1143
  // Subscribe and fetch for the new account group
1046
- __classPrivateFieldGet(this, _AssetsController_instances, "m", _AssetsController_subscribeToDataSources).call(this);
1144
+ __classPrivateFieldGet(this, _AssetsController_instances, "m", _AssetsController_subscribeAssets).call(this);
1047
1145
  if (accounts.length > 0) {
1048
1146
  await this.getAssets(accounts, {
1049
1147
  chainIds: [...__classPrivateFieldGet(this, _AssetsController_enabledChains, "f")],
@@ -1077,7 +1175,7 @@ async function _AssetsController_handleAccountGroupChanged() {
1077
1175
  // Users may want to see historical balances even if the network is currently disabled.
1078
1176
  // The data will simply not be updated until the network is re-enabled.
1079
1177
  // Refresh subscriptions for new chain set
1080
- __classPrivateFieldGet(this, _AssetsController_instances, "m", _AssetsController_subscribeToDataSources).call(this);
1178
+ __classPrivateFieldGet(this, _AssetsController_instances, "m", _AssetsController_subscribeAssets).call(this);
1081
1179
  // Do one-time fetch for newly enabled chains
1082
1180
  if (addedChains.length > 0 && __classPrivateFieldGet(this, _AssetsController_instances, "a", _AssetsController_selectedAccounts_get).length > 0) {
1083
1181
  await this.getAssets(__classPrivateFieldGet(this, _AssetsController_instances, "a", _AssetsController_selectedAccounts_get), {
@@ -1085,28 +1183,5 @@ async function _AssetsController_handleAccountGroupChanged() {
1085
1183
  forceUpdate: true,
1086
1184
  });
1087
1185
  }
1088
- }, _AssetsController_handleSubscriptionUpdate =
1089
- /**
1090
- * Handle an async update from a data source subscription.
1091
- * Enriches response with token metadata before updating state.
1092
- *
1093
- * @param response - The data response from the data source.
1094
- * @param _sourceId - The source ID (unused but kept for logging context).
1095
- * @param request - Optional original request for context.
1096
- */
1097
- async function _AssetsController_handleSubscriptionUpdate(response, _sourceId, request) {
1098
- // Run through enrichment middlewares (Event Stack: Detection → Token → Price)
1099
- // Include 'metadata' in dataTypes so TokenDataSource runs to enrich detected assets
1100
- const enrichedResponse = await __classPrivateFieldGet(this, _AssetsController_instances, "m", _AssetsController_executeMiddlewares).call(this, [
1101
- this.messenger.call('DetectionMiddleware:getAssetsMiddleware'),
1102
- this.messenger.call('TokenDataSource:getAssetsMiddleware'),
1103
- this.messenger.call('PriceDataSource:getAssetsMiddleware'),
1104
- ], request ?? {
1105
- accounts: [],
1106
- chainIds: [],
1107
- dataTypes: ['balance', 'metadata', 'price'],
1108
- }, response);
1109
- // Update state
1110
- await __classPrivateFieldGet(this, _AssetsController_instances, "m", _AssetsController_updateState).call(this, enrichedResponse);
1111
1186
  };
1112
1187
  //# sourceMappingURL=AssetsController.mjs.map