@metamask/assets-controller 0.1.0 → 1.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.
- package/CHANGELOG.md +31 -3
- package/dist/AssetsController-method-action-types.cjs +7 -0
- package/dist/AssetsController-method-action-types.cjs.map +1 -0
- package/dist/AssetsController-method-action-types.d.cts +78 -0
- package/dist/AssetsController-method-action-types.d.cts.map +1 -0
- package/dist/AssetsController-method-action-types.d.mts +78 -0
- package/dist/AssetsController-method-action-types.d.mts.map +1 -0
- package/dist/AssetsController-method-action-types.mjs +6 -0
- package/dist/AssetsController-method-action-types.mjs.map +1 -0
- package/dist/AssetsController.cjs +273 -247
- package/dist/AssetsController.cjs.map +1 -1
- package/dist/AssetsController.d.cts +55 -93
- package/dist/AssetsController.d.cts.map +1 -1
- package/dist/AssetsController.d.mts +55 -93
- package/dist/AssetsController.d.mts.map +1 -1
- package/dist/AssetsController.mjs +273 -247
- package/dist/AssetsController.mjs.map +1 -1
- package/dist/data-sources/AbstractDataSource.cjs +8 -0
- package/dist/data-sources/AbstractDataSource.cjs.map +1 -1
- package/dist/data-sources/AbstractDataSource.d.cts +16 -1
- package/dist/data-sources/AbstractDataSource.d.cts.map +1 -1
- package/dist/data-sources/AbstractDataSource.d.mts +16 -1
- package/dist/data-sources/AbstractDataSource.d.mts.map +1 -1
- package/dist/data-sources/AbstractDataSource.mjs +8 -0
- package/dist/data-sources/AbstractDataSource.mjs.map +1 -1
- package/dist/data-sources/AccountsApiDataSource.cjs +25 -99
- package/dist/data-sources/AccountsApiDataSource.cjs.map +1 -1
- package/dist/data-sources/AccountsApiDataSource.d.cts +5 -67
- package/dist/data-sources/AccountsApiDataSource.d.cts.map +1 -1
- package/dist/data-sources/AccountsApiDataSource.d.mts +5 -67
- package/dist/data-sources/AccountsApiDataSource.d.mts.map +1 -1
- package/dist/data-sources/AccountsApiDataSource.mjs +24 -97
- package/dist/data-sources/AccountsApiDataSource.mjs.map +1 -1
- package/dist/data-sources/BackendWebsocketDataSource.cjs +138 -45
- package/dist/data-sources/BackendWebsocketDataSource.cjs.map +1 -1
- package/dist/data-sources/BackendWebsocketDataSource.d.cts +19 -66
- package/dist/data-sources/BackendWebsocketDataSource.d.cts.map +1 -1
- package/dist/data-sources/BackendWebsocketDataSource.d.mts +19 -66
- package/dist/data-sources/BackendWebsocketDataSource.d.mts.map +1 -1
- package/dist/data-sources/BackendWebsocketDataSource.mjs +138 -45
- package/dist/data-sources/BackendWebsocketDataSource.mjs.map +1 -1
- package/dist/data-sources/PriceDataSource.cjs +22 -44
- package/dist/data-sources/PriceDataSource.cjs.map +1 -1
- package/dist/data-sources/PriceDataSource.d.cts +6 -89
- package/dist/data-sources/PriceDataSource.d.cts.map +1 -1
- package/dist/data-sources/PriceDataSource.d.mts +6 -89
- package/dist/data-sources/PriceDataSource.d.mts.map +1 -1
- package/dist/data-sources/PriceDataSource.mjs +22 -44
- package/dist/data-sources/PriceDataSource.mjs.map +1 -1
- package/dist/data-sources/RpcDataSource.cjs +64 -102
- package/dist/data-sources/RpcDataSource.cjs.map +1 -1
- package/dist/data-sources/RpcDataSource.d.cts +16 -62
- package/dist/data-sources/RpcDataSource.d.cts.map +1 -1
- package/dist/data-sources/RpcDataSource.d.mts +16 -62
- package/dist/data-sources/RpcDataSource.d.mts.map +1 -1
- package/dist/data-sources/RpcDataSource.mjs +64 -102
- package/dist/data-sources/RpcDataSource.mjs.map +1 -1
- package/dist/data-sources/SnapDataSource.cjs +32 -30
- package/dist/data-sources/SnapDataSource.cjs.map +1 -1
- package/dist/data-sources/SnapDataSource.d.cts +7 -44
- package/dist/data-sources/SnapDataSource.d.cts.map +1 -1
- package/dist/data-sources/SnapDataSource.d.mts +7 -44
- package/dist/data-sources/SnapDataSource.d.mts.map +1 -1
- package/dist/data-sources/SnapDataSource.mjs +32 -30
- package/dist/data-sources/SnapDataSource.mjs.map +1 -1
- package/dist/data-sources/TokenDataSource.cjs +3 -16
- package/dist/data-sources/TokenDataSource.cjs.map +1 -1
- package/dist/data-sources/TokenDataSource.d.cts +2 -25
- package/dist/data-sources/TokenDataSource.d.cts.map +1 -1
- package/dist/data-sources/TokenDataSource.d.mts +2 -25
- package/dist/data-sources/TokenDataSource.d.mts.map +1 -1
- package/dist/data-sources/TokenDataSource.mjs +3 -16
- package/dist/data-sources/TokenDataSource.mjs.map +1 -1
- package/dist/data-sources/evm-rpc-services/clients/MulticallClient.cjs +27 -35
- package/dist/data-sources/evm-rpc-services/clients/MulticallClient.cjs.map +1 -1
- package/dist/data-sources/evm-rpc-services/clients/MulticallClient.d.cts.map +1 -1
- package/dist/data-sources/evm-rpc-services/clients/MulticallClient.d.mts.map +1 -1
- package/dist/data-sources/evm-rpc-services/clients/MulticallClient.mjs +27 -35
- package/dist/data-sources/evm-rpc-services/clients/MulticallClient.mjs.map +1 -1
- package/dist/data-sources/index.cjs +1 -6
- package/dist/data-sources/index.cjs.map +1 -1
- package/dist/data-sources/index.d.cts +6 -7
- package/dist/data-sources/index.d.cts.map +1 -1
- package/dist/data-sources/index.d.mts +6 -7
- package/dist/data-sources/index.d.mts.map +1 -1
- package/dist/data-sources/index.mjs +1 -3
- package/dist/data-sources/index.mjs.map +1 -1
- package/dist/index.cjs +6 -6
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +11 -11
- package/dist/index.d.cts.map +1 -1
- package/dist/index.d.mts +11 -11
- package/dist/index.d.mts.map +1 -1
- package/dist/index.mjs +3 -3
- package/dist/index.mjs.map +1 -1
- package/dist/middlewares/DetectionMiddleware.cjs +4 -27
- package/dist/middlewares/DetectionMiddleware.cjs.map +1 -1
- package/dist/middlewares/DetectionMiddleware.d.cts +3 -26
- package/dist/middlewares/DetectionMiddleware.d.cts.map +1 -1
- package/dist/middlewares/DetectionMiddleware.d.mts +3 -26
- package/dist/middlewares/DetectionMiddleware.d.mts.map +1 -1
- package/dist/middlewares/DetectionMiddleware.mjs +4 -27
- package/dist/middlewares/DetectionMiddleware.mjs.map +1 -1
- package/dist/middlewares/index.cjs.map +1 -1
- package/dist/middlewares/index.d.cts +1 -1
- package/dist/middlewares/index.d.cts.map +1 -1
- package/dist/middlewares/index.d.mts +1 -1
- package/dist/middlewares/index.d.mts.map +1 -1
- package/dist/middlewares/index.mjs.map +1 -1
- package/dist/selectors/balance.cjs +306 -0
- package/dist/selectors/balance.cjs.map +1 -0
- package/dist/selectors/balance.d.cts +22 -0
- package/dist/selectors/balance.d.cts.map +1 -0
- package/dist/selectors/balance.d.mts +22 -0
- package/dist/selectors/balance.d.mts.map +1 -0
- package/dist/selectors/balance.mjs +297 -0
- package/dist/selectors/balance.mjs.map +1 -0
- package/dist/types.cjs.map +1 -1
- package/dist/types.d.cts +63 -18
- package/dist/types.d.cts.map +1 -1
- package/dist/types.d.mts +63 -18
- package/dist/types.d.mts.map +1 -1
- package/dist/types.mjs.map +1 -1
- package/package.json +4 -4
- package/dist/data-sources/initDataSources.cjs +0 -215
- package/dist/data-sources/initDataSources.cjs.map +0 -1
- package/dist/data-sources/initDataSources.d.cts +0 -140
- package/dist/data-sources/initDataSources.d.cts.map +0 -1
- package/dist/data-sources/initDataSources.d.mts +0 -140
- package/dist/data-sources/initDataSources.d.mts.map +0 -1
- package/dist/data-sources/initDataSources.mjs +0 -210
- package/dist/data-sources/initDataSources.mjs.map +0 -1
|
@@ -1,34 +1,53 @@
|
|
|
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
|
|
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_defaultUpdateInterval, _AssetsController_controllerMutex, _AssetsController_activeSubscriptions, _AssetsController_enabledChains, _AssetsController_selectedAccounts_get, _AssetsController_backendWebsocketDataSource, _AssetsController_accountsApiDataSource, _AssetsController_snapDataSource, _AssetsController_rpcDataSource, _AssetsController_subscriptionBalanceDataSources_get, _AssetsController_priceDataSource, _AssetsController_detectionMiddleware, _AssetsController_tokenDataSource, _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_buildChainToAccountsMap, _AssetsController_subscribeDataSource, _AssetsController_unsubscribeDataSource, _AssetsController_buildDataRequest, _AssetsController_getEnabledChainsForAccount, _AssetsController_handleAccountGroupChanged, _AssetsController_handleEnabledNetworksChanged;
|
|
13
13
|
import { BaseController } from "@metamask/base-controller";
|
|
14
14
|
import { parseCaipAssetType } 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 { TokenDataSource } from "./data-sources/TokenDataSource.mjs";
|
|
19
25
|
import { projectLogger, createModuleLogger } from "./logger.mjs";
|
|
26
|
+
import { DetectionMiddleware } from "./middlewares/DetectionMiddleware.mjs";
|
|
20
27
|
import { normalizeAssetId } from "./utils.mjs";
|
|
21
28
|
// ============================================================================
|
|
22
29
|
// CONTROLLER CONSTANTS
|
|
23
30
|
// ============================================================================
|
|
24
31
|
const CONTROLLER_NAME = 'AssetsController';
|
|
32
|
+
/** Method names exposed as messenger actions (AssetsController:getAssets, etc.) */
|
|
33
|
+
const MESSENGER_EXPOSED_METHODS = [
|
|
34
|
+
'getAssets',
|
|
35
|
+
'getAssetsBalance',
|
|
36
|
+
'getAssetMetadata',
|
|
37
|
+
'getAssetsPrice',
|
|
38
|
+
'addCustomAsset',
|
|
39
|
+
'removeCustomAsset',
|
|
40
|
+
'getCustomAssets',
|
|
41
|
+
'hideAsset',
|
|
42
|
+
'unhideAsset',
|
|
43
|
+
];
|
|
25
44
|
/** Default polling interval hint for data sources (30 seconds) */
|
|
26
45
|
const DEFAULT_POLLING_INTERVAL_MS = 30000;
|
|
27
46
|
const log = createModuleLogger(projectLogger, CONTROLLER_NAME);
|
|
28
47
|
/**
|
|
29
48
|
* Returns the default state for AssetsController.
|
|
30
49
|
*
|
|
31
|
-
* @returns The default AssetsController state with empty
|
|
50
|
+
* @returns The default AssetsController state with empty maps.
|
|
32
51
|
*/
|
|
33
52
|
export function getDefaultAssetsControllerState() {
|
|
34
53
|
return {
|
|
@@ -36,6 +55,7 @@ export function getDefaultAssetsControllerState() {
|
|
|
36
55
|
assetsBalance: {},
|
|
37
56
|
assetsPrice: {},
|
|
38
57
|
customAssets: {},
|
|
58
|
+
assetPreferences: {},
|
|
39
59
|
};
|
|
40
60
|
}
|
|
41
61
|
// ============================================================================
|
|
@@ -66,6 +86,12 @@ const stateMetadata = {
|
|
|
66
86
|
includeInDebugSnapshot: false,
|
|
67
87
|
usedInUi: true,
|
|
68
88
|
},
|
|
89
|
+
assetPreferences: {
|
|
90
|
+
persist: true,
|
|
91
|
+
includeInStateLogs: false,
|
|
92
|
+
includeInDebugSnapshot: false,
|
|
93
|
+
usedInUi: true,
|
|
94
|
+
},
|
|
69
95
|
};
|
|
70
96
|
// ============================================================================
|
|
71
97
|
// HELPER FUNCTIONS
|
|
@@ -140,13 +166,8 @@ function normalizeResponse(response) {
|
|
|
140
166
|
* based on which chains they support. When active chains change, the controller
|
|
141
167
|
* dynamically adjusts subscriptions.
|
|
142
168
|
*
|
|
143
|
-
* 4. **
|
|
144
|
-
*
|
|
145
|
-
*
|
|
146
|
-
* ## App Lifecycle
|
|
147
|
-
*
|
|
148
|
-
* - **App Opened** (`AppStateController:appOpened`): Starts subscriptions, fetches initial data
|
|
149
|
-
* - **App Closed** (`AppStateController:appClosed`): Stops all subscriptions to conserve resources
|
|
169
|
+
* 4. **Keyring Lifecycle**: Listens to KeyringController unlock/lock events to
|
|
170
|
+
* start/stop subscriptions when the wallet is unlocked or locked.
|
|
150
171
|
*
|
|
151
172
|
* ## Architecture
|
|
152
173
|
*
|
|
@@ -155,7 +176,7 @@ function normalizeResponse(response) {
|
|
|
155
176
|
* - The controller does NOT manage polling - it simply receives pushed updates
|
|
156
177
|
*/
|
|
157
178
|
export class AssetsController extends BaseController {
|
|
158
|
-
constructor({ messenger, state = {}, defaultUpdateInterval = DEFAULT_POLLING_INTERVAL_MS, isEnabled = () => true, }) {
|
|
179
|
+
constructor({ messenger, state = {}, defaultUpdateInterval = DEFAULT_POLLING_INTERVAL_MS, isEnabled = () => true, queryApiClient, rpcDataSourceConfig, }) {
|
|
159
180
|
super({
|
|
160
181
|
name: CONTROLLER_NAME,
|
|
161
182
|
messenger,
|
|
@@ -180,79 +201,82 @@ export class AssetsController extends BaseController {
|
|
|
180
201
|
_AssetsController_activeSubscriptions.set(this, new Map());
|
|
181
202
|
/** Currently enabled chains from NetworkEnablementController */
|
|
182
203
|
_AssetsController_enabledChains.set(this, new Set());
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
204
|
+
_AssetsController_backendWebsocketDataSource.set(this, void 0);
|
|
205
|
+
_AssetsController_accountsApiDataSource.set(this, void 0);
|
|
206
|
+
_AssetsController_snapDataSource.set(this, void 0);
|
|
207
|
+
_AssetsController_rpcDataSource.set(this, void 0);
|
|
208
|
+
_AssetsController_priceDataSource.set(this, void 0);
|
|
209
|
+
_AssetsController_detectionMiddleware.set(this, void 0);
|
|
210
|
+
_AssetsController_tokenDataSource.set(this, void 0);
|
|
189
211
|
__classPrivateFieldSet(this, _AssetsController_isEnabled, isEnabled(), "f");
|
|
190
212
|
__classPrivateFieldSet(this, _AssetsController_defaultUpdateInterval, defaultUpdateInterval, "f");
|
|
213
|
+
const rpcConfig = rpcDataSourceConfig ?? {};
|
|
214
|
+
const onActiveChainsUpdated = (dataSourceName, chains, previousChains) => this.handleActiveChainsUpdate(dataSourceName, chains, previousChains);
|
|
215
|
+
__classPrivateFieldSet(this, _AssetsController_backendWebsocketDataSource, new BackendWebsocketDataSource({
|
|
216
|
+
messenger: this.messenger,
|
|
217
|
+
queryApiClient,
|
|
218
|
+
onActiveChainsUpdated,
|
|
219
|
+
}), "f");
|
|
220
|
+
__classPrivateFieldSet(this, _AssetsController_accountsApiDataSource, new AccountsApiDataSource({
|
|
221
|
+
queryApiClient,
|
|
222
|
+
onActiveChainsUpdated,
|
|
223
|
+
}), "f");
|
|
224
|
+
__classPrivateFieldSet(this, _AssetsController_snapDataSource, new SnapDataSource({
|
|
225
|
+
messenger: this.messenger,
|
|
226
|
+
onActiveChainsUpdated,
|
|
227
|
+
}), "f");
|
|
228
|
+
__classPrivateFieldSet(this, _AssetsController_rpcDataSource, new RpcDataSource({
|
|
229
|
+
messenger: this.messenger,
|
|
230
|
+
onActiveChainsUpdated,
|
|
231
|
+
...rpcConfig,
|
|
232
|
+
}), "f");
|
|
233
|
+
__classPrivateFieldSet(this, _AssetsController_tokenDataSource, new TokenDataSource({
|
|
234
|
+
queryApiClient,
|
|
235
|
+
}), "f");
|
|
236
|
+
__classPrivateFieldSet(this, _AssetsController_priceDataSource, new PriceDataSource({
|
|
237
|
+
queryApiClient,
|
|
238
|
+
}), "f");
|
|
239
|
+
__classPrivateFieldSet(this, _AssetsController_detectionMiddleware, new DetectionMiddleware(), "f");
|
|
191
240
|
if (!__classPrivateFieldGet(this, _AssetsController_isEnabled, "f")) {
|
|
192
241
|
log('AssetsController is disabled, skipping initialization');
|
|
193
242
|
return;
|
|
194
243
|
}
|
|
195
244
|
log('Initializing AssetsController', {
|
|
196
|
-
defaultUpdateInterval,
|
|
245
|
+
defaultUpdateInterval: __classPrivateFieldGet(this, _AssetsController_defaultUpdateInterval, "f"),
|
|
197
246
|
});
|
|
198
247
|
__classPrivateFieldGet(this, _AssetsController_instances, "m", _AssetsController_initializeState).call(this);
|
|
199
248
|
__classPrivateFieldGet(this, _AssetsController_instances, "m", _AssetsController_subscribeToEvents).call(this);
|
|
200
249
|
__classPrivateFieldGet(this, _AssetsController_instances, "m", _AssetsController_registerActionHandlers).call(this);
|
|
201
|
-
// Register data sources (order = subscription priority)
|
|
202
|
-
this.registerDataSources([
|
|
203
|
-
'BackendWebsocketDataSource', // Real-time push updates
|
|
204
|
-
'AccountsApiDataSource', // HTTP polling fallback
|
|
205
|
-
'SnapDataSource', // Solana/Bitcoin/Tron snaps
|
|
206
|
-
'RpcDataSource', // Direct blockchain queries
|
|
207
|
-
]);
|
|
208
|
-
}
|
|
209
|
-
// ============================================================================
|
|
210
|
-
// DATA SOURCE MANAGEMENT
|
|
211
|
-
// ============================================================================
|
|
212
|
-
/**
|
|
213
|
-
* Register data sources with the controller.
|
|
214
|
-
* Order of the array determines subscription order.
|
|
215
|
-
*
|
|
216
|
-
* Data sources report chain changes by calling `AssetsController:activeChainsUpdate` action.
|
|
217
|
-
*
|
|
218
|
-
* @param dataSourceIds - Array of data source identifiers to register.
|
|
219
|
-
*/
|
|
220
|
-
registerDataSources(dataSourceIds) {
|
|
221
|
-
for (const id of dataSourceIds) {
|
|
222
|
-
log('Registering data source', { id });
|
|
223
|
-
// Initialize available chains tracking for this source
|
|
224
|
-
__classPrivateFieldGet(this, _AssetsController_dataSources, "f").set(id, new Set());
|
|
225
|
-
}
|
|
226
250
|
}
|
|
227
251
|
// ============================================================================
|
|
228
252
|
// DATA SOURCE CHAIN MANAGEMENT
|
|
229
253
|
// ============================================================================
|
|
230
254
|
/**
|
|
231
|
-
* Handle when a data source's
|
|
232
|
-
*
|
|
233
|
-
*
|
|
255
|
+
* Handle when a data source's supported chains change.
|
|
256
|
+
* Used to refresh balance subscriptions and run a one-time fetch when a new chain is supported.
|
|
257
|
+
*
|
|
258
|
+
* - On any add/remove: re-subscribes to data sources so chain assignment stays correct.
|
|
259
|
+
* - When chains are added: fetches balances for the new chains (for selected accounts on enabled networks).
|
|
234
260
|
*
|
|
235
|
-
*
|
|
261
|
+
* Controller does not store chains; sources report via this callback. previousChains is required for diff.
|
|
236
262
|
*
|
|
237
263
|
* @param dataSourceId - The identifier of the data source reporting the change.
|
|
238
|
-
* @param activeChains -
|
|
264
|
+
* @param activeChains - Currently active (supported and available) chain IDs for this source.
|
|
265
|
+
* @param previousChains - Previous chains; used to compute added/removed.
|
|
239
266
|
*/
|
|
240
|
-
handleActiveChainsUpdate(dataSourceId, activeChains) {
|
|
267
|
+
handleActiveChainsUpdate(dataSourceId, activeChains, previousChains) {
|
|
241
268
|
log('Data source active chains changed', {
|
|
242
269
|
dataSourceId,
|
|
243
270
|
chainCount: activeChains.length,
|
|
244
271
|
chains: activeChains,
|
|
245
272
|
});
|
|
246
|
-
const
|
|
247
|
-
const
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
// Check for changes
|
|
251
|
-
const addedChains = activeChains.filter((chain) => !previousChains.has(chain));
|
|
252
|
-
const removedChains = Array.from(previousChains).filter((chain) => !newChains.has(chain));
|
|
273
|
+
const previous = previousChains;
|
|
274
|
+
const previousSet = new Set(previous);
|
|
275
|
+
const addedChains = activeChains.filter((ch) => !previousSet.has(ch));
|
|
276
|
+
const removedChains = previous.filter((ch) => !activeChains.includes(ch));
|
|
253
277
|
if (addedChains.length > 0 || removedChains.length > 0) {
|
|
254
278
|
// Refresh subscriptions to use updated data source availability
|
|
255
|
-
__classPrivateFieldGet(this, _AssetsController_instances, "m",
|
|
279
|
+
__classPrivateFieldGet(this, _AssetsController_instances, "m", _AssetsController_subscribeAssets).call(this);
|
|
256
280
|
}
|
|
257
281
|
// If chains were added and we have selected accounts, do one-time fetch
|
|
258
282
|
if (addedChains.length > 0 && __classPrivateFieldGet(this, _AssetsController_instances, "a", _AssetsController_selectedAccounts_get).length > 0) {
|
|
@@ -282,21 +306,20 @@ export class AssetsController extends BaseController {
|
|
|
282
306
|
customAssets.push(...accountCustomAssets);
|
|
283
307
|
}
|
|
284
308
|
if (options?.forceUpdate) {
|
|
285
|
-
const
|
|
286
|
-
this.messenger.call('AccountsApiDataSource:getAssetsMiddleware'),
|
|
287
|
-
this.messenger.call('SnapDataSource:getAssetsMiddleware'),
|
|
288
|
-
this.messenger.call('RpcDataSource:getAssetsMiddleware'),
|
|
289
|
-
this.messenger.call('DetectionMiddleware:getAssetsMiddleware'),
|
|
290
|
-
this.messenger.call('TokenDataSource:getAssetsMiddleware'),
|
|
291
|
-
this.messenger.call('PriceDataSource:getAssetsMiddleware'),
|
|
292
|
-
], {
|
|
293
|
-
accounts,
|
|
294
|
-
chainIds,
|
|
309
|
+
const request = __classPrivateFieldGet(this, _AssetsController_instances, "m", _AssetsController_buildDataRequest).call(this, accounts, chainIds, {
|
|
295
310
|
assetTypes,
|
|
296
311
|
dataTypes,
|
|
297
312
|
customAssets: customAssets.length > 0 ? customAssets : undefined,
|
|
298
313
|
forceUpdate: true,
|
|
299
314
|
});
|
|
315
|
+
const response = await __classPrivateFieldGet(this, _AssetsController_instances, "m", _AssetsController_executeMiddlewares).call(this, [
|
|
316
|
+
__classPrivateFieldGet(this, _AssetsController_accountsApiDataSource, "f").assetsMiddleware,
|
|
317
|
+
__classPrivateFieldGet(this, _AssetsController_snapDataSource, "f").assetsMiddleware,
|
|
318
|
+
__classPrivateFieldGet(this, _AssetsController_rpcDataSource, "f").assetsMiddleware,
|
|
319
|
+
__classPrivateFieldGet(this, _AssetsController_detectionMiddleware, "f").assetsMiddleware,
|
|
320
|
+
__classPrivateFieldGet(this, _AssetsController_tokenDataSource, "f").assetsMiddleware,
|
|
321
|
+
__classPrivateFieldGet(this, _AssetsController_priceDataSource, "f").assetsMiddleware,
|
|
322
|
+
], request);
|
|
300
323
|
await __classPrivateFieldGet(this, _AssetsController_instances, "m", _AssetsController_updateState).call(this, response);
|
|
301
324
|
}
|
|
302
325
|
return __classPrivateFieldGet(this, _AssetsController_instances, "m", _AssetsController_getAssetsFromState).call(this, accounts, chainIds, assetTypes);
|
|
@@ -348,6 +371,7 @@ export class AssetsController extends BaseController {
|
|
|
348
371
|
/**
|
|
349
372
|
* Add a custom asset for an account.
|
|
350
373
|
* Custom assets are included in subscription and fetch operations.
|
|
374
|
+
* Adding a custom asset also unhides it if it was previously hidden.
|
|
351
375
|
*
|
|
352
376
|
* @param accountId - The account ID to add the custom asset for.
|
|
353
377
|
* @param assetId - The CAIP-19 asset ID to add.
|
|
@@ -364,6 +388,14 @@ export class AssetsController extends BaseController {
|
|
|
364
388
|
if (!customAssets[accountId].includes(normalizedAssetId)) {
|
|
365
389
|
customAssets[accountId].push(normalizedAssetId);
|
|
366
390
|
}
|
|
391
|
+
// Unhide the asset if it was hidden (via assetPreferences)
|
|
392
|
+
const prefs = state.assetPreferences[normalizedAssetId];
|
|
393
|
+
if (prefs?.hidden) {
|
|
394
|
+
delete prefs.hidden;
|
|
395
|
+
if (Object.keys(prefs).length === 0) {
|
|
396
|
+
delete state.assetPreferences[normalizedAssetId];
|
|
397
|
+
}
|
|
398
|
+
}
|
|
367
399
|
});
|
|
368
400
|
// Fetch data for the newly added custom asset
|
|
369
401
|
const account = __classPrivateFieldGet(this, _AssetsController_instances, "a", _AssetsController_selectedAccounts_get).find((a) => a.id === accountId);
|
|
@@ -385,12 +417,11 @@ export class AssetsController extends BaseController {
|
|
|
385
417
|
const normalizedAssetId = normalizeAssetId(assetId);
|
|
386
418
|
log('Removing custom asset', { accountId, assetId: normalizedAssetId });
|
|
387
419
|
this.update((state) => {
|
|
388
|
-
|
|
389
|
-
|
|
390
|
-
customAssets[accountId] = customAssets[accountId].filter((id) => id !== normalizedAssetId);
|
|
420
|
+
if (state.customAssets[accountId]) {
|
|
421
|
+
state.customAssets[accountId] = state.customAssets[accountId].filter((id) => id !== normalizedAssetId);
|
|
391
422
|
// Clean up empty arrays
|
|
392
|
-
if (customAssets[accountId].length === 0) {
|
|
393
|
-
delete customAssets[accountId];
|
|
423
|
+
if (state.customAssets[accountId].length === 0) {
|
|
424
|
+
delete state.customAssets[accountId];
|
|
394
425
|
}
|
|
395
426
|
}
|
|
396
427
|
});
|
|
@@ -402,8 +433,49 @@ export class AssetsController extends BaseController {
|
|
|
402
433
|
* @returns Array of CAIP-19 asset IDs for the account's custom assets.
|
|
403
434
|
*/
|
|
404
435
|
getCustomAssets(accountId) {
|
|
405
|
-
return
|
|
436
|
+
return this.state.customAssets[accountId] ?? [];
|
|
406
437
|
}
|
|
438
|
+
// ============================================================================
|
|
439
|
+
// HIDDEN ASSETS MANAGEMENT
|
|
440
|
+
// ============================================================================
|
|
441
|
+
/**
|
|
442
|
+
* Hide an asset globally.
|
|
443
|
+
* Hidden assets are excluded from the asset list returned by getAssets.
|
|
444
|
+
* The hidden state is stored in assetPreferences.
|
|
445
|
+
*
|
|
446
|
+
* @param assetId - The CAIP-19 asset ID to hide.
|
|
447
|
+
*/
|
|
448
|
+
hideAsset(assetId) {
|
|
449
|
+
const normalizedAssetId = normalizeAssetId(assetId);
|
|
450
|
+
log('Hiding asset', { assetId: normalizedAssetId });
|
|
451
|
+
this.update((state) => {
|
|
452
|
+
if (!state.assetPreferences[normalizedAssetId]) {
|
|
453
|
+
state.assetPreferences[normalizedAssetId] = {};
|
|
454
|
+
}
|
|
455
|
+
state.assetPreferences[normalizedAssetId].hidden = true;
|
|
456
|
+
});
|
|
457
|
+
}
|
|
458
|
+
/**
|
|
459
|
+
* Unhide an asset globally.
|
|
460
|
+
*
|
|
461
|
+
* @param assetId - The CAIP-19 asset ID to unhide.
|
|
462
|
+
*/
|
|
463
|
+
unhideAsset(assetId) {
|
|
464
|
+
const normalizedAssetId = normalizeAssetId(assetId);
|
|
465
|
+
log('Unhiding asset', { assetId: normalizedAssetId });
|
|
466
|
+
this.update((state) => {
|
|
467
|
+
const prefs = state.assetPreferences[normalizedAssetId];
|
|
468
|
+
if (prefs) {
|
|
469
|
+
delete prefs.hidden;
|
|
470
|
+
if (Object.keys(prefs).length === 0) {
|
|
471
|
+
delete state.assetPreferences[normalizedAssetId];
|
|
472
|
+
}
|
|
473
|
+
}
|
|
474
|
+
});
|
|
475
|
+
}
|
|
476
|
+
// ============================================================================
|
|
477
|
+
// SUBSCRIPTIONS
|
|
478
|
+
// ============================================================================
|
|
407
479
|
/**
|
|
408
480
|
* Subscribe to price updates for all assets held by the given accounts.
|
|
409
481
|
* Polls PriceDataSource which fetches prices from balance state.
|
|
@@ -418,19 +490,17 @@ export class AssetsController extends BaseController {
|
|
|
418
490
|
const subscriptionKey = 'ds:PriceDataSource';
|
|
419
491
|
const existingSubscription = __classPrivateFieldGet(this, _AssetsController_activeSubscriptions, "f").get(subscriptionKey);
|
|
420
492
|
const isUpdate = existingSubscription !== undefined;
|
|
421
|
-
|
|
422
|
-
|
|
423
|
-
.call('PriceDataSource:subscribe', {
|
|
424
|
-
request: {
|
|
425
|
-
accounts,
|
|
426
|
-
chainIds,
|
|
493
|
+
const subscribeReq = {
|
|
494
|
+
request: __classPrivateFieldGet(this, _AssetsController_instances, "m", _AssetsController_buildDataRequest).call(this, accounts, chainIds, {
|
|
427
495
|
dataTypes: ['price'],
|
|
428
496
|
updateInterval,
|
|
429
|
-
},
|
|
497
|
+
}),
|
|
430
498
|
subscriptionId: subscriptionKey,
|
|
431
499
|
isUpdate,
|
|
432
|
-
|
|
433
|
-
|
|
500
|
+
onAssetsUpdate: (response) => this.handleAssetsUpdate(response, 'PriceDataSource'),
|
|
501
|
+
getAssetsState: () => this.state,
|
|
502
|
+
};
|
|
503
|
+
__classPrivateFieldGet(this, _AssetsController_priceDataSource, "f").subscribe(subscribeReq).catch(console.error);
|
|
434
504
|
// Track subscription
|
|
435
505
|
const subscription = {
|
|
436
506
|
chains: chainIds,
|
|
@@ -452,37 +522,55 @@ export class AssetsController extends BaseController {
|
|
|
452
522
|
if (!existingSubscription) {
|
|
453
523
|
return;
|
|
454
524
|
}
|
|
455
|
-
|
|
456
|
-
this.messenger
|
|
457
|
-
.call('PriceDataSource:unsubscribe', subscriptionKey)
|
|
458
|
-
.catch(console.error);
|
|
525
|
+
__classPrivateFieldGet(this, _AssetsController_priceDataSource, "f").unsubscribe(subscriptionKey).catch(console.error);
|
|
459
526
|
existingSubscription.unsubscribe();
|
|
460
527
|
}
|
|
461
528
|
/**
|
|
462
529
|
* Handle assets updated from a data source.
|
|
463
|
-
* Called via
|
|
530
|
+
* Called via the onAssetsUpdate callback passed in SubscriptionRequest when the controller subscribes to a data source.
|
|
531
|
+
* Enriches the response with token metadata (via middlewares) before updating state.
|
|
464
532
|
*
|
|
465
533
|
* @param response - The data response with updated assets
|
|
466
534
|
* @param sourceId - The data source ID reporting the update
|
|
535
|
+
* @param request - Optional original request for context when enriching
|
|
467
536
|
*/
|
|
468
|
-
async handleAssetsUpdate(response, sourceId) {
|
|
537
|
+
async handleAssetsUpdate(response, sourceId, request) {
|
|
469
538
|
log('Assets updated from data source', {
|
|
470
539
|
sourceId,
|
|
471
540
|
hasBalance: Boolean(response.assetsBalance),
|
|
472
541
|
hasPrice: Boolean(response.assetsPrice),
|
|
473
542
|
});
|
|
474
|
-
|
|
543
|
+
// Run through enrichment middlewares (Event Stack: Detection → Token → Price)
|
|
544
|
+
// Include 'metadata' in dataTypes so TokenDataSource runs to enrich detected assets
|
|
545
|
+
const enrichedResponse = await __classPrivateFieldGet(this, _AssetsController_instances, "m", _AssetsController_executeMiddlewares).call(this, [
|
|
546
|
+
__classPrivateFieldGet(this, _AssetsController_detectionMiddleware, "f").assetsMiddleware,
|
|
547
|
+
__classPrivateFieldGet(this, _AssetsController_tokenDataSource, "f").assetsMiddleware,
|
|
548
|
+
__classPrivateFieldGet(this, _AssetsController_priceDataSource, "f").assetsMiddleware,
|
|
549
|
+
], request ?? {
|
|
550
|
+
accountsWithSupportedChains: [],
|
|
551
|
+
chainIds: [],
|
|
552
|
+
dataTypes: ['balance', 'metadata', 'price'],
|
|
553
|
+
}, response);
|
|
554
|
+
await __classPrivateFieldGet(this, _AssetsController_instances, "m", _AssetsController_updateState).call(this, enrichedResponse);
|
|
475
555
|
}
|
|
476
556
|
// ============================================================================
|
|
477
557
|
// CLEANUP
|
|
478
558
|
// ============================================================================
|
|
479
559
|
destroy() {
|
|
480
560
|
log('Destroying AssetsController', {
|
|
481
|
-
dataSourceCount: __classPrivateFieldGet(this,
|
|
561
|
+
dataSourceCount: __classPrivateFieldGet(this, _AssetsController_instances, "a", _AssetsController_subscriptionBalanceDataSources_get).length,
|
|
482
562
|
subscriptionCount: __classPrivateFieldGet(this, _AssetsController_activeSubscriptions, "f").size,
|
|
483
563
|
});
|
|
484
|
-
//
|
|
485
|
-
__classPrivateFieldGet(this,
|
|
564
|
+
// Destroy instantiated data sources
|
|
565
|
+
__classPrivateFieldGet(this, _AssetsController_backendWebsocketDataSource, "f")?.destroy?.();
|
|
566
|
+
__classPrivateFieldGet(this, _AssetsController_accountsApiDataSource, "f")?.destroy?.();
|
|
567
|
+
__classPrivateFieldGet(this, _AssetsController_snapDataSource, "f")?.destroy?.();
|
|
568
|
+
if (__classPrivateFieldGet(this, _AssetsController_rpcDataSource, "f") &&
|
|
569
|
+
'destroy' in __classPrivateFieldGet(this, _AssetsController_rpcDataSource, "f") &&
|
|
570
|
+
typeof __classPrivateFieldGet(this, _AssetsController_rpcDataSource, "f").destroy ===
|
|
571
|
+
'function') {
|
|
572
|
+
__classPrivateFieldGet(this, _AssetsController_rpcDataSource, "f").destroy();
|
|
573
|
+
}
|
|
486
574
|
// Stop all active subscriptions
|
|
487
575
|
__classPrivateFieldGet(this, _AssetsController_instances, "m", _AssetsController_stop).call(this);
|
|
488
576
|
// Unregister action handlers
|
|
@@ -490,15 +578,22 @@ export class AssetsController extends BaseController {
|
|
|
490
578
|
this.messenger.unregisterActionHandler('AssetsController:getAssetsBalance');
|
|
491
579
|
this.messenger.unregisterActionHandler('AssetsController:getAssetMetadata');
|
|
492
580
|
this.messenger.unregisterActionHandler('AssetsController:getAssetsPrice');
|
|
493
|
-
this.messenger.unregisterActionHandler('AssetsController:activeChainsUpdate');
|
|
494
|
-
this.messenger.unregisterActionHandler('AssetsController:assetsUpdate');
|
|
495
581
|
this.messenger.unregisterActionHandler('AssetsController:addCustomAsset');
|
|
496
582
|
this.messenger.unregisterActionHandler('AssetsController:removeCustomAsset');
|
|
497
583
|
this.messenger.unregisterActionHandler('AssetsController:getCustomAssets');
|
|
584
|
+
this.messenger.unregisterActionHandler('AssetsController:hideAsset');
|
|
585
|
+
this.messenger.unregisterActionHandler('AssetsController:unhideAsset');
|
|
498
586
|
}
|
|
499
587
|
}
|
|
500
|
-
_AssetsController_isEnabled = new WeakMap(), _AssetsController_defaultUpdateInterval = new WeakMap(), _AssetsController_controllerMutex = new WeakMap(), _AssetsController_activeSubscriptions = new WeakMap(), _AssetsController_enabledChains = new WeakMap(),
|
|
588
|
+
_AssetsController_isEnabled = new WeakMap(), _AssetsController_defaultUpdateInterval = 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_priceDataSource = new WeakMap(), _AssetsController_detectionMiddleware = new WeakMap(), _AssetsController_tokenDataSource = new WeakMap(), _AssetsController_instances = new WeakSet(), _AssetsController_selectedAccounts_get = function _AssetsController_selectedAccounts_get() {
|
|
501
589
|
return this.messenger.call('AccountTreeController:getAccountsFromSelectedAccountGroup');
|
|
590
|
+
}, _AssetsController_subscriptionBalanceDataSources_get = function _AssetsController_subscriptionBalanceDataSources_get() {
|
|
591
|
+
return [
|
|
592
|
+
__classPrivateFieldGet(this, _AssetsController_backendWebsocketDataSource, "f"),
|
|
593
|
+
__classPrivateFieldGet(this, _AssetsController_accountsApiDataSource, "f"),
|
|
594
|
+
__classPrivateFieldGet(this, _AssetsController_snapDataSource, "f"),
|
|
595
|
+
__classPrivateFieldGet(this, _AssetsController_rpcDataSource, "f"),
|
|
596
|
+
];
|
|
502
597
|
}, _AssetsController_initializeState = function _AssetsController_initializeState() {
|
|
503
598
|
const { enabledNetworkMap } = this.messenger.call('NetworkEnablementController:getState');
|
|
504
599
|
__classPrivateFieldSet(this, _AssetsController_enabledChains, __classPrivateFieldGet(this, _AssetsController_instances, "m", _AssetsController_extractEnabledChains).call(this, enabledNetworkMap), "f");
|
|
@@ -540,22 +635,11 @@ _AssetsController_isEnabled = new WeakMap(), _AssetsController_defaultUpdateInte
|
|
|
540
635
|
this.messenger.subscribe('NetworkEnablementController:stateChange', ({ enabledNetworkMap }) => {
|
|
541
636
|
__classPrivateFieldGet(this, _AssetsController_instances, "m", _AssetsController_handleEnabledNetworksChanged).call(this, enabledNetworkMap).catch(console.error);
|
|
542
637
|
});
|
|
543
|
-
// App lifecycle: start when opened, stop when closed
|
|
544
|
-
this.messenger.subscribe('AppStateController:appOpened', () => __classPrivateFieldGet(this, _AssetsController_instances, "m", _AssetsController_start).call(this));
|
|
545
|
-
this.messenger.subscribe('AppStateController:appClosed', () => __classPrivateFieldGet(this, _AssetsController_instances, "m", _AssetsController_stop).call(this));
|
|
546
638
|
// Keyring lifecycle: start when unlocked, stop when locked
|
|
547
639
|
this.messenger.subscribe('KeyringController:unlock', () => __classPrivateFieldGet(this, _AssetsController_instances, "m", _AssetsController_start).call(this));
|
|
548
640
|
this.messenger.subscribe('KeyringController:lock', () => __classPrivateFieldGet(this, _AssetsController_instances, "m", _AssetsController_stop).call(this));
|
|
549
641
|
}, _AssetsController_registerActionHandlers = function _AssetsController_registerActionHandlers() {
|
|
550
|
-
this.messenger.
|
|
551
|
-
this.messenger.registerActionHandler('AssetsController:getAssetsBalance', this.getAssetsBalance.bind(this));
|
|
552
|
-
this.messenger.registerActionHandler('AssetsController:getAssetMetadata', this.getAssetMetadata.bind(this));
|
|
553
|
-
this.messenger.registerActionHandler('AssetsController:getAssetsPrice', this.getAssetsPrice.bind(this));
|
|
554
|
-
this.messenger.registerActionHandler('AssetsController:activeChainsUpdate', this.handleActiveChainsUpdate.bind(this));
|
|
555
|
-
this.messenger.registerActionHandler('AssetsController:assetsUpdate', this.handleAssetsUpdate.bind(this));
|
|
556
|
-
this.messenger.registerActionHandler('AssetsController:addCustomAsset', this.addCustomAsset.bind(this));
|
|
557
|
-
this.messenger.registerActionHandler('AssetsController:removeCustomAsset', this.removeCustomAsset.bind(this));
|
|
558
|
-
this.messenger.registerActionHandler('AssetsController:getCustomAssets', this.getCustomAssets.bind(this));
|
|
642
|
+
this.messenger.registerMethodActionHandlers(this, MESSENGER_EXPOSED_METHODS);
|
|
559
643
|
}, _AssetsController_executeMiddlewares =
|
|
560
644
|
// ============================================================================
|
|
561
645
|
// MIDDLEWARE EXECUTION
|
|
@@ -584,32 +668,6 @@ async function _AssetsController_executeMiddlewares(middlewares, request, initia
|
|
|
584
668
|
getAssetsState: () => this.state,
|
|
585
669
|
});
|
|
586
670
|
return result.response;
|
|
587
|
-
}, _AssetsController_assignChainsToDataSources = function _AssetsController_assignChainsToDataSources(requestedChains) {
|
|
588
|
-
const assignment = new Map();
|
|
589
|
-
const remainingChains = new Set(requestedChains);
|
|
590
|
-
for (const sourceId of __classPrivateFieldGet(this, _AssetsController_dataSources, "f").keys()) {
|
|
591
|
-
// Get available chains for this data source
|
|
592
|
-
const availableChains = __classPrivateFieldGet(this, _AssetsController_dataSources, "f").get(sourceId);
|
|
593
|
-
if (!availableChains || availableChains.size === 0) {
|
|
594
|
-
continue;
|
|
595
|
-
}
|
|
596
|
-
const chainsForThisSource = [];
|
|
597
|
-
for (const chainId of remainingChains) {
|
|
598
|
-
// Check if this chain is available on this source
|
|
599
|
-
if (availableChains.has(chainId)) {
|
|
600
|
-
chainsForThisSource.push(chainId);
|
|
601
|
-
remainingChains.delete(chainId);
|
|
602
|
-
}
|
|
603
|
-
}
|
|
604
|
-
if (chainsForThisSource.length > 0) {
|
|
605
|
-
assignment.set(sourceId, chainsForThisSource);
|
|
606
|
-
log('Assigned chains to data source', {
|
|
607
|
-
sourceId,
|
|
608
|
-
chains: chainsForThisSource,
|
|
609
|
-
});
|
|
610
|
-
}
|
|
611
|
-
}
|
|
612
|
-
return assignment;
|
|
613
671
|
}, _AssetsController_updateState =
|
|
614
672
|
// ============================================================================
|
|
615
673
|
// STATE MANAGEMENT
|
|
@@ -627,7 +685,7 @@ async function _AssetsController_updateState(response) {
|
|
|
627
685
|
const changedBalances = [];
|
|
628
686
|
const changedMetadata = [];
|
|
629
687
|
this.update((state) => {
|
|
630
|
-
// Use type assertions to avoid deep type instantiation issues with Draft
|
|
688
|
+
// Use type assertions to avoid deep type instantiation issues with Immer Draft types
|
|
631
689
|
const metadata = state.assetsMetadata;
|
|
632
690
|
const balances = state.assetsBalance;
|
|
633
691
|
const prices = state.assetsPrice;
|
|
@@ -738,16 +796,21 @@ async function _AssetsController_updateState(response) {
|
|
|
738
796
|
const accountBalances = this.state.assetsBalance[account.id] ?? {};
|
|
739
797
|
for (const [assetId, balance] of Object.entries(accountBalances)) {
|
|
740
798
|
const typedAssetId = assetId;
|
|
741
|
-
const assetChainId = extractChainId(typedAssetId);
|
|
742
|
-
if (!chainIdSet.has(assetChainId)) {
|
|
743
|
-
continue;
|
|
744
|
-
}
|
|
745
799
|
const metadataRaw = this.state.assetsMetadata[typedAssetId];
|
|
746
800
|
// Skip assets without metadata
|
|
747
801
|
if (!metadataRaw) {
|
|
748
802
|
continue;
|
|
749
803
|
}
|
|
750
804
|
const metadata = metadataRaw;
|
|
805
|
+
// Skip hidden assets (assetPreferences)
|
|
806
|
+
const prefs = this.state.assetPreferences[typedAssetId];
|
|
807
|
+
if (prefs?.hidden) {
|
|
808
|
+
continue;
|
|
809
|
+
}
|
|
810
|
+
const assetChainId = extractChainId(typedAssetId);
|
|
811
|
+
if (!chainIdSet.has(assetChainId)) {
|
|
812
|
+
continue;
|
|
813
|
+
}
|
|
751
814
|
// Filter by asset type
|
|
752
815
|
const tokenAssetType = __classPrivateFieldGet(this, _AssetsController_instances, "m", _AssetsController_tokenStandardToAssetType).call(this, metadata.type);
|
|
753
816
|
if (!assetTypeSet.has(tokenAssetType)) {
|
|
@@ -798,7 +861,7 @@ async function _AssetsController_updateState(response) {
|
|
|
798
861
|
selectedAccountCount: __classPrivateFieldGet(this, _AssetsController_instances, "a", _AssetsController_selectedAccounts_get).length,
|
|
799
862
|
enabledChainCount: __classPrivateFieldGet(this, _AssetsController_enabledChains, "f").size,
|
|
800
863
|
});
|
|
801
|
-
__classPrivateFieldGet(this, _AssetsController_instances, "m",
|
|
864
|
+
__classPrivateFieldGet(this, _AssetsController_instances, "m", _AssetsController_subscribeAssets).call(this);
|
|
802
865
|
if (__classPrivateFieldGet(this, _AssetsController_instances, "a", _AssetsController_selectedAccounts_get).length > 0) {
|
|
803
866
|
this.getAssets(__classPrivateFieldGet(this, _AssetsController_instances, "a", _AssetsController_selectedAccounts_get), {
|
|
804
867
|
chainIds: [...__classPrivateFieldGet(this, _AssetsController_enabledChains, "f")],
|
|
@@ -819,77 +882,67 @@ async function _AssetsController_updateState(response) {
|
|
|
819
882
|
// Convert to array first to avoid modifying map during iteration
|
|
820
883
|
const subscriptionKeys = [...__classPrivateFieldGet(this, _AssetsController_activeSubscriptions, "f").keys()];
|
|
821
884
|
for (const subscriptionKey of subscriptionKeys) {
|
|
822
|
-
// Extract sourceId from subscription key (format: "ds:${sourceId}")
|
|
823
885
|
if (subscriptionKey.startsWith('ds:')) {
|
|
824
886
|
const sourceId = subscriptionKey.slice(3);
|
|
825
|
-
__classPrivateFieldGet(this, _AssetsController_instances, "
|
|
887
|
+
const source = __classPrivateFieldGet(this, _AssetsController_instances, "a", _AssetsController_subscriptionBalanceDataSources_get).find((ds) => ds.getName() === sourceId);
|
|
888
|
+
if (source) {
|
|
889
|
+
__classPrivateFieldGet(this, _AssetsController_instances, "m", _AssetsController_unsubscribeDataSource).call(this, source);
|
|
890
|
+
}
|
|
826
891
|
}
|
|
827
892
|
}
|
|
828
893
|
__classPrivateFieldGet(this, _AssetsController_activeSubscriptions, "f").clear();
|
|
829
|
-
},
|
|
894
|
+
}, _AssetsController_subscribeAssets = function _AssetsController_subscribeAssets() {
|
|
830
895
|
if (__classPrivateFieldGet(this, _AssetsController_instances, "a", _AssetsController_selectedAccounts_get).length === 0) {
|
|
831
896
|
return;
|
|
832
897
|
}
|
|
833
898
|
// Subscribe to balance updates (batched by data source)
|
|
834
|
-
__classPrivateFieldGet(this, _AssetsController_instances, "m", _AssetsController_subscribeAssetsBalance).call(this)
|
|
899
|
+
__classPrivateFieldGet(this, _AssetsController_instances, "m", _AssetsController_subscribeAssetsBalance).call(this, __classPrivateFieldGet(this, _AssetsController_instances, "a", _AssetsController_selectedAccounts_get), [
|
|
900
|
+
...__classPrivateFieldGet(this, _AssetsController_enabledChains, "f"),
|
|
901
|
+
]);
|
|
835
902
|
// Subscribe to price updates for all assets held by selected accounts
|
|
836
903
|
this.subscribeAssetsPrice(__classPrivateFieldGet(this, _AssetsController_instances, "a", _AssetsController_selectedAccounts_get), [...__classPrivateFieldGet(this, _AssetsController_enabledChains, "f")]);
|
|
837
|
-
}, _AssetsController_subscribeAssetsBalance = function _AssetsController_subscribeAssetsBalance() {
|
|
838
|
-
|
|
839
|
-
const chainToAccounts = __classPrivateFieldGet(this, _AssetsController_instances, "m", _AssetsController_buildChainToAccountsMap).call(this, __classPrivateFieldGet(this, _AssetsController_instances, "a", _AssetsController_selectedAccounts_get), __classPrivateFieldGet(this, _AssetsController_enabledChains, "f"));
|
|
840
|
-
// Step 2: Split by data source active chains (ordered by priority)
|
|
841
|
-
// Get all chains that need to be subscribed
|
|
904
|
+
}, _AssetsController_subscribeAssetsBalance = function _AssetsController_subscribeAssetsBalance(accounts, chainIds) {
|
|
905
|
+
const chainToAccounts = __classPrivateFieldGet(this, _AssetsController_instances, "m", _AssetsController_buildChainToAccountsMap).call(this, accounts, new Set(chainIds));
|
|
842
906
|
const remainingChains = new Set(chainToAccounts.keys());
|
|
843
|
-
|
|
844
|
-
|
|
845
|
-
|
|
846
|
-
|
|
847
|
-
|
|
848
|
-
|
|
849
|
-
|
|
850
|
-
|
|
851
|
-
const assignedChains = chainAssignment.get(sourceId);
|
|
852
|
-
if (!assignedChains || assignedChains.length === 0) {
|
|
853
|
-
// Unsubscribe from data sources with no assigned chains
|
|
854
|
-
__classPrivateFieldGet(this, _AssetsController_instances, "m", _AssetsController_unsubscribeDataSource).call(this, sourceId);
|
|
855
|
-
continue;
|
|
907
|
+
for (const source of __classPrivateFieldGet(this, _AssetsController_instances, "a", _AssetsController_subscriptionBalanceDataSources_get)) {
|
|
908
|
+
const availableChains = new Set(source.getActiveChainsSync());
|
|
909
|
+
const assignedChains = [];
|
|
910
|
+
for (const chainId of remainingChains) {
|
|
911
|
+
if (availableChains.has(chainId)) {
|
|
912
|
+
assignedChains.push(chainId);
|
|
913
|
+
remainingChains.delete(chainId);
|
|
914
|
+
}
|
|
856
915
|
}
|
|
857
|
-
|
|
858
|
-
|
|
859
|
-
if (accountsForSource.length === 0) {
|
|
916
|
+
if (assignedChains.length === 0) {
|
|
917
|
+
__classPrivateFieldGet(this, _AssetsController_instances, "m", _AssetsController_unsubscribeDataSource).call(this, source);
|
|
860
918
|
continue;
|
|
861
919
|
}
|
|
862
|
-
|
|
863
|
-
|
|
920
|
+
const seenIds = new Set();
|
|
921
|
+
const accountsForSource = assignedChains
|
|
922
|
+
.flatMap((chainId) => chainToAccounts.get(chainId) ?? [])
|
|
923
|
+
.filter((account) => !seenIds.has(account.id) && (seenIds.add(account.id), true));
|
|
924
|
+
if (accountsForSource.length > 0) {
|
|
925
|
+
__classPrivateFieldGet(this, _AssetsController_instances, "m", _AssetsController_subscribeDataSource).call(this, source, accountsForSource, assignedChains);
|
|
926
|
+
}
|
|
864
927
|
}
|
|
865
928
|
}, _AssetsController_buildChainToAccountsMap = function _AssetsController_buildChainToAccountsMap(accounts, chainsToSubscribe) {
|
|
866
929
|
const chainToAccounts = new Map();
|
|
867
930
|
for (const account of accounts) {
|
|
868
|
-
const
|
|
869
|
-
for (const chainId of accountChains) {
|
|
931
|
+
for (const chainId of __classPrivateFieldGet(this, _AssetsController_instances, "m", _AssetsController_getEnabledChainsForAccount).call(this, account)) {
|
|
870
932
|
if (!chainsToSubscribe.has(chainId)) {
|
|
871
933
|
continue;
|
|
872
934
|
}
|
|
873
|
-
|
|
874
|
-
|
|
875
|
-
|
|
876
|
-
|
|
877
|
-
}
|
|
878
|
-
return chainToAccounts;
|
|
879
|
-
}, _AssetsController_getAccountsForChains = function _AssetsController_getAccountsForChains(chains, chainToAccounts) {
|
|
880
|
-
const accountIds = new Set();
|
|
881
|
-
const accounts = [];
|
|
882
|
-
for (const chainId of chains) {
|
|
883
|
-
const chainAccounts = chainToAccounts.get(chainId) ?? [];
|
|
884
|
-
for (const account of chainAccounts) {
|
|
885
|
-
if (!accountIds.has(account.id)) {
|
|
886
|
-
accountIds.add(account.id);
|
|
887
|
-
accounts.push(account);
|
|
935
|
+
let list = chainToAccounts.get(chainId);
|
|
936
|
+
if (!list) {
|
|
937
|
+
list = [];
|
|
938
|
+
chainToAccounts.set(chainId, list);
|
|
888
939
|
}
|
|
940
|
+
list.push(account);
|
|
889
941
|
}
|
|
890
942
|
}
|
|
891
|
-
return
|
|
892
|
-
},
|
|
943
|
+
return chainToAccounts;
|
|
944
|
+
}, _AssetsController_subscribeDataSource = function _AssetsController_subscribeDataSource(source, accounts, chains) {
|
|
945
|
+
const sourceId = source.getName();
|
|
893
946
|
const subscriptionKey = `ds:${sourceId}`;
|
|
894
947
|
const existingSubscription = __classPrivateFieldGet(this, _AssetsController_activeSubscriptions, "f").get(subscriptionKey);
|
|
895
948
|
const isUpdate = existingSubscription !== undefined;
|
|
@@ -900,53 +953,49 @@ async function _AssetsController_updateState(response) {
|
|
|
900
953
|
accountCount: accounts.length,
|
|
901
954
|
chainCount: chains.length,
|
|
902
955
|
});
|
|
903
|
-
|
|
904
|
-
|
|
905
|
-
|
|
906
|
-
|
|
907
|
-
|
|
908
|
-
|
|
909
|
-
|
|
910
|
-
|
|
911
|
-
|
|
912
|
-
|
|
913
|
-
|
|
914
|
-
|
|
915
|
-
|
|
916
|
-
|
|
917
|
-
}
|
|
918
|
-
catch (error) {
|
|
919
|
-
console.error(`[AssetsController] Failed to subscribe to '${sourceId}':`, error);
|
|
920
|
-
}
|
|
921
|
-
})().catch(console.error);
|
|
956
|
+
const subscribeReq = {
|
|
957
|
+
request: __classPrivateFieldGet(this, _AssetsController_instances, "m", _AssetsController_buildDataRequest).call(this, accounts, chains, {
|
|
958
|
+
assetTypes: ['fungible'],
|
|
959
|
+
dataTypes: ['balance'],
|
|
960
|
+
updateInterval: __classPrivateFieldGet(this, _AssetsController_defaultUpdateInterval, "f"),
|
|
961
|
+
}),
|
|
962
|
+
subscriptionId: subscriptionKey,
|
|
963
|
+
isUpdate,
|
|
964
|
+
onAssetsUpdate: (response) => this.handleAssetsUpdate(response, sourceId),
|
|
965
|
+
getAssetsState: () => this.state,
|
|
966
|
+
};
|
|
967
|
+
source.subscribe(subscribeReq).catch((error) => {
|
|
968
|
+
console.error(`[AssetsController] Failed to subscribe to '${sourceId}':`, error);
|
|
969
|
+
});
|
|
922
970
|
// Track subscription
|
|
923
971
|
const subscription = {
|
|
924
972
|
chains,
|
|
925
973
|
accountId: subscriptionKey,
|
|
926
974
|
assetTypes: ['fungible'],
|
|
927
|
-
dataTypes: ['balance'
|
|
975
|
+
dataTypes: ['balance'],
|
|
928
976
|
unsubscribe: () => {
|
|
929
977
|
__classPrivateFieldGet(this, _AssetsController_activeSubscriptions, "f").delete(subscriptionKey);
|
|
930
978
|
},
|
|
931
979
|
};
|
|
932
980
|
__classPrivateFieldGet(this, _AssetsController_activeSubscriptions, "f").set(subscriptionKey, subscription);
|
|
933
|
-
}, _AssetsController_unsubscribeDataSource = function _AssetsController_unsubscribeDataSource(
|
|
934
|
-
const subscriptionKey = `ds:${
|
|
981
|
+
}, _AssetsController_unsubscribeDataSource = function _AssetsController_unsubscribeDataSource(source) {
|
|
982
|
+
const subscriptionKey = `ds:${source.getName()}`;
|
|
935
983
|
const existingSubscription = __classPrivateFieldGet(this, _AssetsController_activeSubscriptions, "f").get(subscriptionKey);
|
|
936
984
|
if (existingSubscription) {
|
|
937
|
-
|
|
938
|
-
(async () => {
|
|
939
|
-
try {
|
|
940
|
-
await this.messenger.call(`${sourceId}:unsubscribe`, subscriptionKey);
|
|
941
|
-
}
|
|
942
|
-
catch {
|
|
943
|
-
// Ignore errors - source may not have been subscribed
|
|
944
|
-
}
|
|
945
|
-
})().catch(() => {
|
|
946
|
-
// Ignore errors - source may not have been subscribed
|
|
947
|
-
});
|
|
985
|
+
source.unsubscribe(subscriptionKey).catch(() => undefined);
|
|
948
986
|
existingSubscription.unsubscribe();
|
|
949
987
|
}
|
|
988
|
+
}, _AssetsController_buildDataRequest = function _AssetsController_buildDataRequest(accounts, chainIds, partial = {}) {
|
|
989
|
+
const chainIdSet = new Set(chainIds);
|
|
990
|
+
const accountsWithSupportedChains = accounts.map((account) => ({
|
|
991
|
+
account,
|
|
992
|
+
supportedChains: __classPrivateFieldGet(this, _AssetsController_instances, "m", _AssetsController_getEnabledChainsForAccount).call(this, account).filter((chain) => chainIdSet.has(chain)),
|
|
993
|
+
}));
|
|
994
|
+
return {
|
|
995
|
+
accountsWithSupportedChains,
|
|
996
|
+
chainIds,
|
|
997
|
+
...partial,
|
|
998
|
+
};
|
|
950
999
|
}, _AssetsController_getEnabledChainsForAccount = function _AssetsController_getEnabledChainsForAccount(account) {
|
|
951
1000
|
// Account scopes are CAIP-2 chain IDs like "eip155:1", "solana:mainnet", "bip122:..."
|
|
952
1001
|
const scopes = account.scopes ?? [];
|
|
@@ -981,7 +1030,7 @@ async function _AssetsController_handleAccountGroupChanged() {
|
|
|
981
1030
|
accountIds: accounts.map((a) => a.id),
|
|
982
1031
|
});
|
|
983
1032
|
// Subscribe and fetch for the new account group
|
|
984
|
-
__classPrivateFieldGet(this, _AssetsController_instances, "m",
|
|
1033
|
+
__classPrivateFieldGet(this, _AssetsController_instances, "m", _AssetsController_subscribeAssets).call(this);
|
|
985
1034
|
if (accounts.length > 0) {
|
|
986
1035
|
await this.getAssets(accounts, {
|
|
987
1036
|
chainIds: [...__classPrivateFieldGet(this, _AssetsController_enabledChains, "f")],
|
|
@@ -1015,7 +1064,7 @@ async function _AssetsController_handleAccountGroupChanged() {
|
|
|
1015
1064
|
// Users may want to see historical balances even if the network is currently disabled.
|
|
1016
1065
|
// The data will simply not be updated until the network is re-enabled.
|
|
1017
1066
|
// Refresh subscriptions for new chain set
|
|
1018
|
-
__classPrivateFieldGet(this, _AssetsController_instances, "m",
|
|
1067
|
+
__classPrivateFieldGet(this, _AssetsController_instances, "m", _AssetsController_subscribeAssets).call(this);
|
|
1019
1068
|
// Do one-time fetch for newly enabled chains
|
|
1020
1069
|
if (addedChains.length > 0 && __classPrivateFieldGet(this, _AssetsController_instances, "a", _AssetsController_selectedAccounts_get).length > 0) {
|
|
1021
1070
|
await this.getAssets(__classPrivateFieldGet(this, _AssetsController_instances, "a", _AssetsController_selectedAccounts_get), {
|
|
@@ -1023,28 +1072,5 @@ async function _AssetsController_handleAccountGroupChanged() {
|
|
|
1023
1072
|
forceUpdate: true,
|
|
1024
1073
|
});
|
|
1025
1074
|
}
|
|
1026
|
-
}, _AssetsController_handleSubscriptionUpdate =
|
|
1027
|
-
/**
|
|
1028
|
-
* Handle an async update from a data source subscription.
|
|
1029
|
-
* Enriches response with token metadata before updating state.
|
|
1030
|
-
*
|
|
1031
|
-
* @param response - The data response from the data source.
|
|
1032
|
-
* @param _sourceId - The source ID (unused but kept for logging context).
|
|
1033
|
-
* @param request - Optional original request for context.
|
|
1034
|
-
*/
|
|
1035
|
-
async function _AssetsController_handleSubscriptionUpdate(response, _sourceId, request) {
|
|
1036
|
-
// Run through enrichment middlewares (Event Stack: Detection → Token → Price)
|
|
1037
|
-
// Include 'metadata' in dataTypes so TokenDataSource runs to enrich detected assets
|
|
1038
|
-
const enrichedResponse = await __classPrivateFieldGet(this, _AssetsController_instances, "m", _AssetsController_executeMiddlewares).call(this, [
|
|
1039
|
-
this.messenger.call('DetectionMiddleware:getAssetsMiddleware'),
|
|
1040
|
-
this.messenger.call('TokenDataSource:getAssetsMiddleware'),
|
|
1041
|
-
this.messenger.call('PriceDataSource:getAssetsMiddleware'),
|
|
1042
|
-
], request ?? {
|
|
1043
|
-
accounts: [],
|
|
1044
|
-
chainIds: [],
|
|
1045
|
-
dataTypes: ['balance', 'metadata', 'price'],
|
|
1046
|
-
}, response);
|
|
1047
|
-
// Update state
|
|
1048
|
-
await __classPrivateFieldGet(this, _AssetsController_instances, "m", _AssetsController_updateState).call(this, enrichedResponse);
|
|
1049
1075
|
};
|
|
1050
1076
|
//# sourceMappingURL=AssetsController.mjs.map
|