@metamask-previews/assets-controller 14.0.3-preview-e12b5ece6 → 14.0.3-preview-fe4b3be11
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 +5 -10
- package/dist/AssetsController.cjs +592 -273
- package/dist/AssetsController.cjs.map +1 -1
- package/dist/AssetsController.d.cts +7 -10
- package/dist/AssetsController.d.cts.map +1 -1
- package/dist/AssetsController.d.mts +7 -10
- package/dist/AssetsController.d.mts.map +1 -1
- package/dist/AssetsController.mjs +592 -273
- package/dist/AssetsController.mjs.map +1 -1
- package/dist/data-sources/AbstractDataSource.cjs +23 -0
- package/dist/data-sources/AbstractDataSource.cjs.map +1 -1
- package/dist/data-sources/AbstractDataSource.d.cts +13 -1
- package/dist/data-sources/AbstractDataSource.d.cts.map +1 -1
- package/dist/data-sources/AbstractDataSource.d.mts +13 -1
- package/dist/data-sources/AbstractDataSource.d.mts.map +1 -1
- package/dist/data-sources/AbstractDataSource.mjs +23 -0
- package/dist/data-sources/AbstractDataSource.mjs.map +1 -1
- package/dist/data-sources/AccountActivityDataSource.cjs +2 -0
- package/dist/data-sources/AccountActivityDataSource.cjs.map +1 -1
- package/dist/data-sources/AccountActivityDataSource.d.cts.map +1 -1
- package/dist/data-sources/AccountActivityDataSource.d.mts.map +1 -1
- package/dist/data-sources/AccountActivityDataSource.mjs +2 -0
- package/dist/data-sources/AccountActivityDataSource.mjs.map +1 -1
- package/dist/data-sources/AccountsApiDataSource.cjs +144 -31
- package/dist/data-sources/AccountsApiDataSource.cjs.map +1 -1
- package/dist/data-sources/AccountsApiDataSource.d.cts +18 -2
- package/dist/data-sources/AccountsApiDataSource.d.cts.map +1 -1
- package/dist/data-sources/AccountsApiDataSource.d.mts +18 -2
- package/dist/data-sources/AccountsApiDataSource.d.mts.map +1 -1
- package/dist/data-sources/AccountsApiDataSource.mjs +145 -32
- package/dist/data-sources/AccountsApiDataSource.mjs.map +1 -1
- package/dist/data-sources/RpcDataSource.cjs +163 -24
- package/dist/data-sources/RpcDataSource.cjs.map +1 -1
- package/dist/data-sources/RpcDataSource.d.cts +17 -0
- package/dist/data-sources/RpcDataSource.d.cts.map +1 -1
- package/dist/data-sources/RpcDataSource.d.mts +17 -0
- package/dist/data-sources/RpcDataSource.d.mts.map +1 -1
- package/dist/data-sources/RpcDataSource.mjs +164 -25
- package/dist/data-sources/RpcDataSource.mjs.map +1 -1
- package/dist/data-sources/evm-rpc-services/services/BalanceFetcher.cjs +21 -12
- package/dist/data-sources/evm-rpc-services/services/BalanceFetcher.cjs.map +1 -1
- package/dist/data-sources/evm-rpc-services/services/BalanceFetcher.d.cts +7 -2
- package/dist/data-sources/evm-rpc-services/services/BalanceFetcher.d.cts.map +1 -1
- package/dist/data-sources/evm-rpc-services/services/BalanceFetcher.d.mts +7 -2
- package/dist/data-sources/evm-rpc-services/services/BalanceFetcher.d.mts.map +1 -1
- package/dist/data-sources/evm-rpc-services/services/BalanceFetcher.mjs +21 -12
- package/dist/data-sources/evm-rpc-services/services/BalanceFetcher.mjs.map +1 -1
- package/dist/data-sources/evm-rpc-services/types/state.cjs.map +1 -1
- package/dist/data-sources/evm-rpc-services/types/state.d.cts +3 -2
- package/dist/data-sources/evm-rpc-services/types/state.d.cts.map +1 -1
- package/dist/data-sources/evm-rpc-services/types/state.d.mts +3 -2
- package/dist/data-sources/evm-rpc-services/types/state.d.mts.map +1 -1
- package/dist/data-sources/evm-rpc-services/types/state.mjs.map +1 -1
- package/dist/middlewares/ParallelMiddleware.cjs +9 -0
- package/dist/middlewares/ParallelMiddleware.cjs.map +1 -1
- package/dist/middlewares/ParallelMiddleware.d.cts.map +1 -1
- package/dist/middlewares/ParallelMiddleware.d.mts.map +1 -1
- package/dist/middlewares/ParallelMiddleware.mjs +9 -0
- package/dist/middlewares/ParallelMiddleware.mjs.map +1 -1
- package/dist/middlewares/RpcFallbackMiddleware.cjs +158 -128
- package/dist/middlewares/RpcFallbackMiddleware.cjs.map +1 -1
- package/dist/middlewares/RpcFallbackMiddleware.d.cts +10 -14
- package/dist/middlewares/RpcFallbackMiddleware.d.cts.map +1 -1
- package/dist/middlewares/RpcFallbackMiddleware.d.mts +10 -14
- package/dist/middlewares/RpcFallbackMiddleware.d.mts.map +1 -1
- package/dist/middlewares/RpcFallbackMiddleware.mjs +158 -128
- package/dist/middlewares/RpcFallbackMiddleware.mjs.map +1 -1
- package/dist/selectors/balance.cjs +4 -9
- package/dist/selectors/balance.cjs.map +1 -1
- package/dist/selectors/balance.d.cts.map +1 -1
- package/dist/selectors/balance.d.mts.map +1 -1
- package/dist/selectors/balance.mjs +4 -9
- package/dist/selectors/balance.mjs.map +1 -1
- package/dist/types.cjs.map +1 -1
- package/dist/types.d.cts +28 -26
- package/dist/types.d.cts.map +1 -1
- package/dist/types.d.mts +28 -26
- package/dist/types.d.mts.map +1 -1
- package/dist/types.mjs.map +1 -1
- package/package.json +4 -4
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { ApiPlatformClient } from "@metamask/core-backend";
|
|
2
2
|
import type { RemoteFeatureFlagControllerGetStateAction, RemoteFeatureFlagControllerStateChangeEvent } from "@metamask/remote-feature-flag-controller";
|
|
3
3
|
import type { AssetsControllerMessenger } from "../AssetsController.mjs";
|
|
4
|
-
import type { ChainId, DataRequest, DataResponse, Middleware, AssetsControllerStateInternal } from "../types.mjs";
|
|
4
|
+
import type { ChainId, Caip19AssetId, DataRequest, DataResponse, Middleware, AssetsControllerStateInternal } from "../types.mjs";
|
|
5
5
|
import type { DataSourceState, SubscriptionRequest } from "./AbstractDataSource.mjs";
|
|
6
6
|
import { AbstractDataSource } from "./AbstractDataSource.mjs";
|
|
7
7
|
declare const CONTROLLER_NAME = "AccountsApiDataSource";
|
|
@@ -28,13 +28,19 @@ export type AccountsApiDataSourceConfig = {
|
|
|
28
28
|
export type AccountsApiDataSourceOptions = AccountsApiDataSourceConfig & {
|
|
29
29
|
/**
|
|
30
30
|
* The AssetsController messenger (shared by all data sources). Used to read
|
|
31
|
-
*
|
|
31
|
+
* Snaps → AssetsController migration flags and subscribe to flag changes.
|
|
32
32
|
*/
|
|
33
33
|
messenger: AssetsControllerMessenger;
|
|
34
34
|
/** ApiPlatformClient for API calls with caching */
|
|
35
35
|
queryApiClient: ApiPlatformClient;
|
|
36
36
|
/** Called when active chains are updated. Pass dataSourceName so the controller knows the source. */
|
|
37
37
|
onActiveChainsUpdated: (dataSourceName: string, chains: ChainId[], previousChains: ChainId[]) => void;
|
|
38
|
+
/**
|
|
39
|
+
* Whether to use the Accounts API v6 balances endpoint (and `includeAssetIds`).
|
|
40
|
+
* Injected by AssetsController so the flag is read in one place. Defaults to
|
|
41
|
+
* `false` (v5). Read on demand, not cached.
|
|
42
|
+
*/
|
|
43
|
+
isBalanceV6Enabled?: () => boolean;
|
|
38
44
|
state?: Partial<AccountsApiDataSourceState>;
|
|
39
45
|
};
|
|
40
46
|
/**
|
|
@@ -75,6 +81,16 @@ export declare class AccountsApiDataSource extends AbstractDataSource<typeof CON
|
|
|
75
81
|
* @returns The middleware function for the assets pipeline.
|
|
76
82
|
*/
|
|
77
83
|
get assetsMiddleware(): Middleware;
|
|
84
|
+
/**
|
|
85
|
+
* Claim EVM pins on assigned chains (sent to v6 as `includeAssetIds`).
|
|
86
|
+
* v5 has no `includeAssetIds`, so with the v6 flag off nothing is claimed
|
|
87
|
+
* and pins fall through to RPC.
|
|
88
|
+
*
|
|
89
|
+
* @param customAssets - Candidate CAIP-19 asset IDs still unclaimed.
|
|
90
|
+
* @param assignedChains - Chains assigned to this source in the handoff.
|
|
91
|
+
* @returns The claimed subset of `customAssets`.
|
|
92
|
+
*/
|
|
93
|
+
claimCustomAssets(customAssets: Caip19AssetId[], assignedChains: ChainId[]): Caip19AssetId[];
|
|
78
94
|
subscribe(subscriptionRequest: SubscriptionRequest): Promise<void>;
|
|
79
95
|
destroy(): void;
|
|
80
96
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"AccountsApiDataSource.d.mts","sourceRoot":"","sources":["../../src/data-sources/AccountsApiDataSource.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,iBAAiB,EAAE,+BAA+B;AAC3D,OAAO,KAAK,EACV,yCAAyC,EACzC,2CAA2C,EAC5C,iDAAiD;
|
|
1
|
+
{"version":3,"file":"AccountsApiDataSource.d.mts","sourceRoot":"","sources":["../../src/data-sources/AccountsApiDataSource.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,iBAAiB,EAAE,+BAA+B;AAC3D,OAAO,KAAK,EACV,yCAAyC,EACzC,2CAA2C,EAC5C,iDAAiD;AAQlD,OAAO,KAAK,EAAE,yBAAyB,EAAE,gCAA+B;AAExE,OAAO,KAAK,EACV,OAAO,EACP,aAAa,EAEb,WAAW,EACX,YAAY,EACZ,UAAU,EACV,6BAA6B,EAC9B,qBAAoB;AAMrB,OAAO,KAAK,EACV,eAAe,EACf,mBAAmB,EACpB,iCAAgC;AACjC,OAAO,EAAE,kBAAkB,EAAE,iCAAgC;AAO7D,QAAA,MAAM,eAAe,0BAA0B,CAAC;AAehD,MAAM,MAAM,mCAAmC,GAC7C,yCAAyC,CAAC;AAI5C,MAAM,MAAM,kCAAkC,GAC5C,2CAA2C,CAAC;AAM9C,MAAM,MAAM,0BAA0B,GAAG,eAAe,CAAC;AAUzD,wDAAwD;AACxD,MAAM,MAAM,2BAA2B,GAAG;IACxC,8CAA8C;IAC9C,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB;;;;OAIG;IACH,qBAAqB,CAAC,EAAE,MAAM,OAAO,CAAC;IACtC;;;;OAIG;IACH,cAAc,CAAC,EAAE,MAAM,CAAC;CACzB,CAAC;AAEF,MAAM,MAAM,4BAA4B,GAAG,2BAA2B,GAAG;IACvE;;;OAGG;IACH,SAAS,EAAE,yBAAyB,CAAC;IACrC,mDAAmD;IACnD,cAAc,EAAE,iBAAiB,CAAC;IAClC,qGAAqG;IACrG,qBAAqB,EAAE,CACrB,cAAc,EAAE,MAAM,EACtB,MAAM,EAAE,OAAO,EAAE,EACjB,cAAc,EAAE,OAAO,EAAE,KACtB,IAAI,CAAC;IACV;;;;OAIG;IACH,kBAAkB,CAAC,EAAE,MAAM,OAAO,CAAC;IACnC,KAAK,CAAC,EAAE,OAAO,CAAC,0BAA0B,CAAC,CAAC;CAC7C,CAAC;AAgCF;;;;;;;GAOG;AACH,wBAAgB,2BAA2B,CACzC,QAAQ,EAAE,YAAY,EACtB,WAAW,EAAE,6BAA6B,GACzC,YAAY,CAqCd;AAMD;;;;;GAKG;AACH,qBAAa,qBAAsB,SAAQ,kBAAkB,CAC3D,OAAO,eAAe,EACtB,0BAA0B,CAC3B;;gBA6Ba,OAAO,EAAE,4BAA4B;IAmGjD;;;;;;OAMG;IACH,mBAAmB,IAAI,OAAO,CAAC,IAAI,CAAC;IA4B9B,KAAK,CAAC,OAAO,EAAE,WAAW,GAAG,OAAO,CAAC,YAAY,CAAC;IA6cxD;;;;;;;;;OASG;IACH,IAAI,gBAAgB,IAAI,UAAU,CAiGjC;IAMD;;;;;;;;OAQG;IACH,iBAAiB,CACf,YAAY,EAAE,aAAa,EAAE,EAC7B,cAAc,EAAE,OAAO,EAAE,GACxB,aAAa,EAAE;IAkBZ,SAAS,CAAC,mBAAmB,EAAE,mBAAmB,GAAG,OAAO,CAAC,IAAI,CAAC;IA0GxE,OAAO,IAAI,IAAI;CAOhB"}
|
|
@@ -9,9 +9,9 @@ var __classPrivateFieldGet = (this && this.__classPrivateFieldGet) || function (
|
|
|
9
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
10
|
return kind === "m" ? f : kind === "a" ? f.call(receiver) : f ? f.value : state.get(receiver);
|
|
11
11
|
};
|
|
12
|
-
var _AccountsApiDataSource_instances, _AccountsApiDataSource_onActiveChainsUpdated, _AccountsApiDataSource_pollInterval, _AccountsApiDataSource_fetchTimeoutMs, _AccountsApiDataSource_tokenDetectionEnabled, _AccountsApiDataSource_messenger, _AccountsApiDataSource_apiClient, _AccountsApiDataSource_chainsRefreshTimer, _AccountsApiDataSource_getAssetsState, _AccountsApiDataSource_handleMigrationFeatureFlagsChanged,
|
|
12
|
+
var _AccountsApiDataSource_instances, _AccountsApiDataSource_onActiveChainsUpdated, _AccountsApiDataSource_pollInterval, _AccountsApiDataSource_fetchTimeoutMs, _AccountsApiDataSource_tokenDetectionEnabled, _AccountsApiDataSource_messenger, _AccountsApiDataSource_isBalanceV6Enabled, _AccountsApiDataSource_apiClient, _AccountsApiDataSource_chainsRefreshTimer, _AccountsApiDataSource_getAssetsState, _AccountsApiDataSource_handleMigrationFeatureFlagsChanged, _AccountsApiDataSource_initializeActiveChains, _AccountsApiDataSource_refreshActiveChains, _AccountsApiDataSource_fetchActiveChains, _AccountsApiDataSource_getIncludeAssetIds, _AccountsApiDataSource_getExcludeAssetIds, _AccountsApiDataSource_fetchV5Balances, _AccountsApiDataSource_fetchV6Balances, _AccountsApiDataSource_buildAddressToAccountIdMap, _AccountsApiDataSource_processV5Balances, _AccountsApiDataSource_processV6Balances;
|
|
13
13
|
import { ApiPlatformClient } from "@metamask/core-backend";
|
|
14
|
-
import { isCaipChainId, KnownCaipNamespace, toCaipChainId } from "@metamask/utils";
|
|
14
|
+
import { isCaipChainId, KnownCaipNamespace, parseCaipAssetType, toCaipChainId } from "@metamask/utils";
|
|
15
15
|
import { projectLogger, createModuleLogger } from "../logger.mjs";
|
|
16
16
|
import { fetchWithTimeout, normalizeAssetId } from "../utils/index.mjs";
|
|
17
17
|
import { getMigrationStages, shouldSupportChain } from "../utils/snaps-assets-migration.mjs";
|
|
@@ -112,6 +112,8 @@ export class AccountsApiDataSource extends AbstractDataSource {
|
|
|
112
112
|
_AccountsApiDataSource_tokenDetectionEnabled.set(this, void 0);
|
|
113
113
|
/** Shared AssetsController messenger, used to read remote feature flags. */
|
|
114
114
|
_AccountsApiDataSource_messenger.set(this, void 0);
|
|
115
|
+
/** Injected by AssetsController; `true` when the v6 balances endpoint should be used. */
|
|
116
|
+
_AccountsApiDataSource_isBalanceV6Enabled.set(this, void 0);
|
|
115
117
|
/** ApiPlatformClient for cached API calls */
|
|
116
118
|
_AccountsApiDataSource_apiClient.set(this, void 0);
|
|
117
119
|
/** Chains refresh timer */
|
|
@@ -123,6 +125,7 @@ export class AccountsApiDataSource extends AbstractDataSource {
|
|
|
123
125
|
__classPrivateFieldSet(this, _AccountsApiDataSource_fetchTimeoutMs, options.fetchTimeoutMs ?? DEFAULT_FETCH_TIMEOUT_MS, "f");
|
|
124
126
|
__classPrivateFieldSet(this, _AccountsApiDataSource_tokenDetectionEnabled, options.tokenDetectionEnabled ?? (() => true), "f");
|
|
125
127
|
__classPrivateFieldSet(this, _AccountsApiDataSource_messenger, options.messenger, "f");
|
|
128
|
+
__classPrivateFieldSet(this, _AccountsApiDataSource_isBalanceV6Enabled, options.isBalanceV6Enabled ?? (() => false), "f");
|
|
126
129
|
__classPrivateFieldSet(this, _AccountsApiDataSource_apiClient, options.queryApiClient, "f");
|
|
127
130
|
// The Snaps → AssetsController migration flags gate which migration networks
|
|
128
131
|
// (Solana, Stellar, Tron) are surfaced as active chains (see
|
|
@@ -182,21 +185,41 @@ export class AccountsApiDataSource extends AbstractDataSource {
|
|
|
182
185
|
if (accountIds.length === 0) {
|
|
183
186
|
return response;
|
|
184
187
|
}
|
|
185
|
-
const fetchOptions = request.forceUpdate
|
|
186
|
-
? {
|
|
187
|
-
staleTime: 0,
|
|
188
|
-
gcTime: 0,
|
|
189
|
-
// Also defeats the API's server-side cache (via a random
|
|
190
|
-
// bypassServerCache query param) so a post-transaction refresh cannot
|
|
191
|
-
// be answered with a pre-transaction snapshot.
|
|
192
|
-
...(request.bypassServerCache ? { bypassServerCache: true } : {}),
|
|
193
|
-
}
|
|
188
|
+
const fetchOptions = request.forceUpdate
|
|
189
|
+
? { staleTime: 0, gcTime: 0 }
|
|
194
190
|
: undefined;
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
191
|
+
const isV6 = __classPrivateFieldGet(this, _AccountsApiDataSource_isBalanceV6Enabled, "f").call(this);
|
|
192
|
+
let fetchResult;
|
|
193
|
+
if (isV6) {
|
|
194
|
+
// User-pinned assets on the fetched chains, sent to v6 as
|
|
195
|
+
// `includeAssetIds` so the backend returns them even at zero balance.
|
|
196
|
+
const includeAssetIds = __classPrivateFieldGet(this, _AccountsApiDataSource_instances, "m", _AccountsApiDataSource_getIncludeAssetIds).call(this, request, chainsToFetch);
|
|
197
|
+
// User-hidden assets on the fetched chains, sent to v6 as
|
|
198
|
+
// `excludeAssetIds`. A pin wins over a hide.
|
|
199
|
+
const excludeAssetIds = __classPrivateFieldGet(this, _AccountsApiDataSource_instances, "m", _AccountsApiDataSource_getExcludeAssetIds).call(this, request, chainsToFetch, includeAssetIds);
|
|
200
|
+
fetchResult = await __classPrivateFieldGet(this, _AccountsApiDataSource_instances, "m", _AccountsApiDataSource_fetchV6Balances).call(this, accountIds, fetchOptions, request, includeAssetIds, excludeAssetIds);
|
|
201
|
+
const validUnprocessedAssetIds = fetchResult.unprocessedIncludeAssetIds.filter((assetId) => {
|
|
202
|
+
try {
|
|
203
|
+
parseCaipAssetType(assetId);
|
|
204
|
+
return true;
|
|
205
|
+
}
|
|
206
|
+
catch {
|
|
207
|
+
return false;
|
|
208
|
+
}
|
|
209
|
+
});
|
|
210
|
+
if (validUnprocessedAssetIds.length > 0) {
|
|
211
|
+
response.unprocessedCustomAssets = [
|
|
212
|
+
...(response.unprocessedCustomAssets ?? []),
|
|
213
|
+
...validUnprocessedAssetIds,
|
|
214
|
+
];
|
|
215
|
+
}
|
|
216
|
+
response.updateMode = 'full';
|
|
217
|
+
}
|
|
218
|
+
else {
|
|
219
|
+
fetchResult = await __classPrivateFieldGet(this, _AccountsApiDataSource_instances, "m", _AccountsApiDataSource_fetchV5Balances).call(this, accountIds, fetchOptions, request);
|
|
220
|
+
response.updateMode = 'merge';
|
|
221
|
+
}
|
|
222
|
+
const { unprocessedNetworks, assetsBalance } = fetchResult;
|
|
200
223
|
// Handle unprocessed networks - these will be passed to next middleware
|
|
201
224
|
if (unprocessedNetworks.length > 0) {
|
|
202
225
|
const unprocessedChainIds = unprocessedNetworks.map(caipChainIdToChainId);
|
|
@@ -207,7 +230,6 @@ export class AccountsApiDataSource extends AbstractDataSource {
|
|
|
207
230
|
}
|
|
208
231
|
}
|
|
209
232
|
response.assetsBalance = assetsBalance;
|
|
210
|
-
response.updateMode = 'merge';
|
|
211
233
|
}
|
|
212
234
|
catch (error) {
|
|
213
235
|
log('Fetch FAILED', { error, chains: chainsToFetch });
|
|
@@ -269,6 +291,27 @@ export class AccountsApiDataSource extends AbstractDataSource {
|
|
|
269
291
|
};
|
|
270
292
|
}
|
|
271
293
|
}
|
|
294
|
+
if (response.updateMode === 'full') {
|
|
295
|
+
context.response = {
|
|
296
|
+
...context.response,
|
|
297
|
+
updateMode: 'full',
|
|
298
|
+
};
|
|
299
|
+
}
|
|
300
|
+
else if (response.updateMode === 'merge' &&
|
|
301
|
+
context.response.updateMode !== 'full') {
|
|
302
|
+
context.response = {
|
|
303
|
+
...context.response,
|
|
304
|
+
updateMode: 'merge',
|
|
305
|
+
};
|
|
306
|
+
}
|
|
307
|
+
// Forward the asset-axis signal so the RPC fallback recovers these pins.
|
|
308
|
+
if (response.unprocessedCustomAssets &&
|
|
309
|
+
response.unprocessedCustomAssets.length > 0) {
|
|
310
|
+
context.response.unprocessedCustomAssets = [
|
|
311
|
+
...(context.response.unprocessedCustomAssets ?? []),
|
|
312
|
+
...response.unprocessedCustomAssets,
|
|
313
|
+
];
|
|
314
|
+
}
|
|
272
315
|
// Determine successfully handled chains (exclude unprocessed/error chains)
|
|
273
316
|
const unprocessedChains = new Set(Object.keys(response.errors ?? {}));
|
|
274
317
|
successfullyHandledChains = request.chainIds.filter((chainId) => !unprocessedChains.has(chainId));
|
|
@@ -303,6 +346,31 @@ export class AccountsApiDataSource extends AbstractDataSource {
|
|
|
303
346
|
// ============================================================================
|
|
304
347
|
// SUBSCRIBE
|
|
305
348
|
// ============================================================================
|
|
349
|
+
/**
|
|
350
|
+
* Claim EVM pins on assigned chains (sent to v6 as `includeAssetIds`).
|
|
351
|
+
* v5 has no `includeAssetIds`, so with the v6 flag off nothing is claimed
|
|
352
|
+
* and pins fall through to RPC.
|
|
353
|
+
*
|
|
354
|
+
* @param customAssets - Candidate CAIP-19 asset IDs still unclaimed.
|
|
355
|
+
* @param assignedChains - Chains assigned to this source in the handoff.
|
|
356
|
+
* @returns The claimed subset of `customAssets`.
|
|
357
|
+
*/
|
|
358
|
+
claimCustomAssets(customAssets, assignedChains) {
|
|
359
|
+
if (!__classPrivateFieldGet(this, _AccountsApiDataSource_isBalanceV6Enabled, "f").call(this)) {
|
|
360
|
+
return [];
|
|
361
|
+
}
|
|
362
|
+
const assigned = new Set(assignedChains);
|
|
363
|
+
return customAssets.filter((assetId) => {
|
|
364
|
+
try {
|
|
365
|
+
const parsed = parseCaipAssetType(assetId);
|
|
366
|
+
return (parsed.chain.namespace === KnownCaipNamespace.Eip155 &&
|
|
367
|
+
assigned.has(parsed.chainId));
|
|
368
|
+
}
|
|
369
|
+
catch {
|
|
370
|
+
return false;
|
|
371
|
+
}
|
|
372
|
+
});
|
|
373
|
+
}
|
|
306
374
|
async subscribe(subscriptionRequest) {
|
|
307
375
|
const { request, subscriptionId, isUpdate, skipInitialFetch } = subscriptionRequest;
|
|
308
376
|
// Store state accessor for filtering when tokenDetectionEnabled is false
|
|
@@ -400,7 +468,7 @@ export class AccountsApiDataSource extends AbstractDataSource {
|
|
|
400
468
|
super.destroy();
|
|
401
469
|
}
|
|
402
470
|
}
|
|
403
|
-
_AccountsApiDataSource_onActiveChainsUpdated = new WeakMap(), _AccountsApiDataSource_pollInterval = new WeakMap(), _AccountsApiDataSource_fetchTimeoutMs = new WeakMap(), _AccountsApiDataSource_tokenDetectionEnabled = new WeakMap(), _AccountsApiDataSource_messenger = new WeakMap(), _AccountsApiDataSource_apiClient = new WeakMap(), _AccountsApiDataSource_chainsRefreshTimer = new WeakMap(), _AccountsApiDataSource_getAssetsState = new WeakMap(), _AccountsApiDataSource_instances = new WeakSet(), _AccountsApiDataSource_handleMigrationFeatureFlagsChanged =
|
|
471
|
+
_AccountsApiDataSource_onActiveChainsUpdated = new WeakMap(), _AccountsApiDataSource_pollInterval = new WeakMap(), _AccountsApiDataSource_fetchTimeoutMs = new WeakMap(), _AccountsApiDataSource_tokenDetectionEnabled = new WeakMap(), _AccountsApiDataSource_messenger = new WeakMap(), _AccountsApiDataSource_isBalanceV6Enabled = new WeakMap(), _AccountsApiDataSource_apiClient = new WeakMap(), _AccountsApiDataSource_chainsRefreshTimer = new WeakMap(), _AccountsApiDataSource_getAssetsState = new WeakMap(), _AccountsApiDataSource_instances = new WeakSet(), _AccountsApiDataSource_handleMigrationFeatureFlagsChanged =
|
|
404
472
|
/**
|
|
405
473
|
* Handle a change to the Snaps → AssetsController migration flags: re-fetch
|
|
406
474
|
* active chains so newly-enabled migration networks are surfaced (and disabled
|
|
@@ -416,17 +484,6 @@ async function _AccountsApiDataSource_handleMigrationFeatureFlagsChanged() {
|
|
|
416
484
|
error,
|
|
417
485
|
});
|
|
418
486
|
}
|
|
419
|
-
}, _AccountsApiDataSource_isBalanceV6Enabled = function _AccountsApiDataSource_isBalanceV6Enabled() {
|
|
420
|
-
try {
|
|
421
|
-
const { remoteFeatureFlags } = __classPrivateFieldGet(this, _AccountsApiDataSource_messenger, "f").call('RemoteFeatureFlagController:getState');
|
|
422
|
-
const flag = remoteFeatureFlags?.assetsAccountsApiV6;
|
|
423
|
-
return (typeof flag === 'object' &&
|
|
424
|
-
flag !== null &&
|
|
425
|
-
Boolean(flag.value));
|
|
426
|
-
}
|
|
427
|
-
catch {
|
|
428
|
-
return false;
|
|
429
|
-
}
|
|
430
487
|
}, _AccountsApiDataSource_initializeActiveChains =
|
|
431
488
|
// ============================================================================
|
|
432
489
|
// INITIALIZATION
|
|
@@ -471,6 +528,50 @@ async function _AccountsApiDataSource_initializeActiveChains() {
|
|
|
471
528
|
return response.fullSupport
|
|
472
529
|
.map(decimalToChainId)
|
|
473
530
|
.filter((chainId) => shouldSupportChain(chainId, remoteFeatureFlags));
|
|
531
|
+
}, _AccountsApiDataSource_getIncludeAssetIds = function _AccountsApiDataSource_getIncludeAssetIds(request, chainsToFetch) {
|
|
532
|
+
if (!request.customAssets || request.customAssets.length === 0) {
|
|
533
|
+
return undefined;
|
|
534
|
+
}
|
|
535
|
+
const chainsToFetchSet = new Set(chainsToFetch);
|
|
536
|
+
const includeAssetIds = new Set();
|
|
537
|
+
for (const assetId of request.customAssets) {
|
|
538
|
+
let chainId;
|
|
539
|
+
try {
|
|
540
|
+
chainId = parseCaipAssetType(assetId).chainId;
|
|
541
|
+
}
|
|
542
|
+
catch {
|
|
543
|
+
continue;
|
|
544
|
+
}
|
|
545
|
+
if (chainId.startsWith(`${KnownCaipNamespace.Eip155}:`) &&
|
|
546
|
+
chainsToFetchSet.has(chainId)) {
|
|
547
|
+
includeAssetIds.add(assetId);
|
|
548
|
+
}
|
|
549
|
+
}
|
|
550
|
+
return includeAssetIds.size > 0 ? [...includeAssetIds] : undefined;
|
|
551
|
+
}, _AccountsApiDataSource_getExcludeAssetIds = function _AccountsApiDataSource_getExcludeAssetIds(request, chainsToFetch, includeAssetIds) {
|
|
552
|
+
if (!request.excludeAssetIds || request.excludeAssetIds.length === 0) {
|
|
553
|
+
return undefined;
|
|
554
|
+
}
|
|
555
|
+
const chainsToFetchSet = new Set(chainsToFetch);
|
|
556
|
+
const includeSet = new Set(includeAssetIds ?? []);
|
|
557
|
+
const excludeAssetIds = new Set();
|
|
558
|
+
for (const assetId of request.excludeAssetIds) {
|
|
559
|
+
if (includeSet.has(assetId)) {
|
|
560
|
+
continue;
|
|
561
|
+
}
|
|
562
|
+
let chainId;
|
|
563
|
+
try {
|
|
564
|
+
chainId = parseCaipAssetType(assetId).chainId;
|
|
565
|
+
}
|
|
566
|
+
catch {
|
|
567
|
+
continue;
|
|
568
|
+
}
|
|
569
|
+
if (chainId.startsWith(`${KnownCaipNamespace.Eip155}:`) &&
|
|
570
|
+
chainsToFetchSet.has(chainId)) {
|
|
571
|
+
excludeAssetIds.add(assetId);
|
|
572
|
+
}
|
|
573
|
+
}
|
|
574
|
+
return excludeAssetIds.size > 0 ? [...excludeAssetIds] : undefined;
|
|
474
575
|
}, _AccountsApiDataSource_fetchV5Balances =
|
|
475
576
|
/**
|
|
476
577
|
* Fetch balances from the legacy v5 endpoint and process them.
|
|
@@ -485,6 +586,8 @@ async function _AccountsApiDataSource_fetchV5Balances(accountIds, fetchOptions,
|
|
|
485
586
|
const { assetsBalance } = __classPrivateFieldGet(this, _AccountsApiDataSource_instances, "m", _AccountsApiDataSource_processV5Balances).call(this, apiResponse.balances, request);
|
|
486
587
|
return {
|
|
487
588
|
unprocessedNetworks: apiResponse.unprocessedNetworks,
|
|
589
|
+
// v5 has no `includeAssetIds` support.
|
|
590
|
+
unprocessedIncludeAssetIds: [],
|
|
488
591
|
assetsBalance,
|
|
489
592
|
};
|
|
490
593
|
}, _AccountsApiDataSource_fetchV6Balances =
|
|
@@ -494,13 +597,23 @@ async function _AccountsApiDataSource_fetchV5Balances(accountIds, fetchOptions,
|
|
|
494
597
|
* @param accountIds - CAIP-10 account IDs to fetch balances for.
|
|
495
598
|
* @param fetchOptions - Cache/fetch options (e.g. force update settings).
|
|
496
599
|
* @param request - The original data request containing accounts to map.
|
|
497
|
-
* @
|
|
600
|
+
* @param includeAssetIds - Pinned asset IDs the backend must always return.
|
|
601
|
+
* @param excludeAssetIds - Hidden asset IDs the backend must drop.
|
|
602
|
+
* @returns Unprocessed networks, unprocessed pinned assets, and processed
|
|
603
|
+
* asset balances by account.
|
|
498
604
|
*/
|
|
499
|
-
async function _AccountsApiDataSource_fetchV6Balances(accountIds, fetchOptions, request) {
|
|
500
|
-
const
|
|
605
|
+
async function _AccountsApiDataSource_fetchV6Balances(accountIds, fetchOptions, request, includeAssetIds, excludeAssetIds) {
|
|
606
|
+
const params = includeAssetIds || excludeAssetIds
|
|
607
|
+
? {
|
|
608
|
+
...(includeAssetIds && { includeAssetIds }),
|
|
609
|
+
...(excludeAssetIds && { excludeAssetIds }),
|
|
610
|
+
}
|
|
611
|
+
: undefined;
|
|
612
|
+
const apiResponse = await fetchWithTimeout(() => __classPrivateFieldGet(this, _AccountsApiDataSource_apiClient, "f").accounts.fetchV6MultiAccountBalances(accountIds, params, fetchOptions), __classPrivateFieldGet(this, _AccountsApiDataSource_fetchTimeoutMs, "f"));
|
|
501
613
|
const { assetsBalance } = __classPrivateFieldGet(this, _AccountsApiDataSource_instances, "m", _AccountsApiDataSource_processV6Balances).call(this, apiResponse.balances, request);
|
|
502
614
|
return {
|
|
503
615
|
unprocessedNetworks: apiResponse.unprocessedNetworks,
|
|
616
|
+
unprocessedIncludeAssetIds: apiResponse.unprocessedIncludeAssetIds,
|
|
504
617
|
assetsBalance,
|
|
505
618
|
};
|
|
506
619
|
}, _AccountsApiDataSource_buildAddressToAccountIdMap = function _AccountsApiDataSource_buildAddressToAccountIdMap(request) {
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"AccountsApiDataSource.mjs","sourceRoot":"","sources":["../../src/data-sources/AccountsApiDataSource.ts"],"names":[],"mappings":"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","sourcesContent":["import type { V5BalanceItem, V6BalanceItem } from '@metamask/core-backend';\nimport { ApiPlatformClient } from '@metamask/core-backend';\nimport type {\n RemoteFeatureFlagControllerGetStateAction,\n RemoteFeatureFlagControllerStateChangeEvent,\n} from '@metamask/remote-feature-flag-controller';\nimport {\n isCaipChainId,\n KnownCaipNamespace,\n toCaipChainId,\n} from '@metamask/utils';\n\nimport type { AssetsControllerMessenger } from '../AssetsController.js';\nimport { projectLogger, createModuleLogger } from '../logger.js';\nimport type {\n ChainId,\n Caip19AssetId,\n AssetBalance,\n DataRequest,\n DataResponse,\n Middleware,\n AssetsControllerStateInternal,\n} from '../types.js';\nimport { fetchWithTimeout, normalizeAssetId } from '../utils/index.js';\nimport {\n getMigrationStages,\n shouldSupportChain,\n} from '../utils/snaps-assets-migration.js';\nimport type {\n DataSourceState,\n SubscriptionRequest,\n} from './AbstractDataSource.js';\nimport { AbstractDataSource } from './AbstractDataSource.js';\nimport { isStakingContractAssetId } from './evm-rpc-services/index.js';\n\n// ============================================================================\n// CONSTANTS\n// ============================================================================\n\nconst CONTROLLER_NAME = 'AccountsApiDataSource';\nconst DEFAULT_POLL_INTERVAL = 30_000;\nconst DEFAULT_FETCH_TIMEOUT_MS = 15_000;\n\nconst log = createModuleLogger(projectLogger, CONTROLLER_NAME);\n\n// ============================================================================\n// MESSENGER TYPES\n// ============================================================================\n\n// Allowed actions that AccountsApiDataSource can call. Balances are fetched via\n// ApiPlatformClient directly (no BackendApiClient actions needed); the messenger\n// is used to read the Accounts API v6 balances feature flag and to subscribe to\n// `RemoteFeatureFlagController:stateChange` (see constructor) so migration flag\n// changes refresh the active chains.\nexport type AccountsApiDataSourceAllowedActions =\n RemoteFeatureFlagControllerGetStateAction;\n\n// Allowed events that AccountsApiDataSource subscribes to. Migration flag\n// changes trigger a refresh of the chains surfaced as active.\nexport type AccountsApiDataSourceAllowedEvents =\n RemoteFeatureFlagControllerStateChangeEvent;\n\n// ============================================================================\n// STATE\n// ============================================================================\n\nexport type AccountsApiDataSourceState = DataSourceState;\n\nconst defaultState: AccountsApiDataSourceState = {\n activeChains: [],\n};\n\n// ============================================================================\n// OPTIONS\n// ============================================================================\n\n/** Optional configuration for AccountsApiDataSource. */\nexport type AccountsApiDataSourceConfig = {\n /** Polling interval in ms (default: 30000) */\n pollInterval?: number;\n /**\n * Function returning whether token detection is enabled (default: () => true).\n * When it returns false, balances are only returned for tokens already in state.\n * Using a getter avoids stale values when the user toggles the preference at runtime.\n */\n tokenDetectionEnabled?: () => boolean;\n /**\n * Timeout in ms for a single balances fetch call (default: 15000).\n * When it fires, every requested chain is marked as errored so the\n * middleware hands them off to the next data source (e.g. RPC fallback).\n */\n fetchTimeoutMs?: number;\n};\n\nexport type AccountsApiDataSourceOptions = AccountsApiDataSourceConfig & {\n /**\n * The AssetsController messenger (shared by all data sources). Used to read\n * the `assetsAccountsApiV6` remote feature flag.\n */\n messenger: AssetsControllerMessenger;\n /** ApiPlatformClient for API calls with caching */\n queryApiClient: ApiPlatformClient;\n /** Called when active chains are updated. Pass dataSourceName so the controller knows the source. */\n onActiveChainsUpdated: (\n dataSourceName: string,\n chains: ChainId[],\n previousChains: ChainId[],\n ) => void;\n state?: Partial<AccountsApiDataSourceState>;\n};\n\n// ============================================================================\n// HELPER FUNCTIONS\n// ============================================================================\n\nfunction decimalToChainId(decimalChainId: number | string): ChainId {\n // Handle both decimal numbers and already-formatted CAIP chain IDs\n if (typeof decimalChainId === 'string') {\n if (isCaipChainId(decimalChainId)) {\n return decimalChainId;\n }\n return toCaipChainId(KnownCaipNamespace.Eip155, decimalChainId);\n }\n return toCaipChainId(KnownCaipNamespace.Eip155, String(decimalChainId));\n}\n\n/**\n * Convert a CAIP-2 chain ID from the API response to our ChainId type.\n * Handles both formats: \"eip155:1\" or just \"1\" (decimal).\n * Uses @metamask/utils for CAIP parsing.\n *\n * @param chainIdStr - The chain ID string to convert.\n * @returns The normalized ChainId.\n */\nfunction caipChainIdToChainId(chainIdStr: string): ChainId {\n if (isCaipChainId(chainIdStr)) {\n return chainIdStr;\n }\n return toCaipChainId(KnownCaipNamespace.Eip155, chainIdStr);\n}\n\n/**\n * Filter a response to only include balances for assets already in state.\n * Used when tokenDetectionEnabled is false to prevent adding new tokens.\n *\n * @param response - The fetch response to filter.\n * @param assetsState - Current assets controller state to check existing balances against.\n * @returns A new response with only known asset balances.\n */\nexport function filterResponseToKnownAssets(\n response: DataResponse,\n assetsState: AssetsControllerStateInternal,\n): DataResponse {\n if (!response.assetsBalance) {\n return response;\n }\n\n const filteredBalance: Record<\n string,\n Record<Caip19AssetId, AssetBalance>\n > = {};\n\n for (const [accountId, accountBalances] of Object.entries(\n response.assetsBalance,\n )) {\n const existingBalances = assetsState.assetsBalance[accountId];\n if (!existingBalances) {\n // Account has no balances in state yet — skip all its tokens\n continue;\n }\n\n const filtered: Record<Caip19AssetId, AssetBalance> = {};\n for (const [assetId, balance] of Object.entries(accountBalances)) {\n // Only include assets already tracked in state\n if (assetId in existingBalances) {\n filtered[assetId as Caip19AssetId] = balance;\n }\n }\n\n if (Object.keys(filtered).length > 0) {\n filteredBalance[accountId] = filtered;\n }\n }\n\n return {\n ...response,\n assetsBalance:\n Object.keys(filteredBalance).length > 0 ? filteredBalance : undefined,\n };\n}\n\n// ============================================================================\n// ACCOUNTS API DATA SOURCE\n// ============================================================================\n\n/**\n * Data source for fetching balances from the MetaMask Accounts API.\n *\n * Uses ApiPlatformClient (queryApiClient) for all API calls. Does not use the\n * messenger. Reports active chains via onActiveChainsUpdated callback.\n */\nexport class AccountsApiDataSource extends AbstractDataSource<\n typeof CONTROLLER_NAME,\n AccountsApiDataSourceState\n> {\n readonly #onActiveChainsUpdated: (\n dataSourceName: string,\n chains: ChainId[],\n previousChains: ChainId[],\n ) => void;\n\n readonly #pollInterval: number;\n\n readonly #fetchTimeoutMs: number;\n\n /** Getter avoids stale value when user toggles token detection at runtime. */\n readonly #tokenDetectionEnabled: () => boolean;\n\n /** Shared AssetsController messenger, used to read remote feature flags. */\n readonly #messenger: AssetsControllerMessenger;\n\n /** ApiPlatformClient for cached API calls */\n readonly #apiClient: ApiPlatformClient;\n\n /** Chains refresh timer */\n #chainsRefreshTimer: ReturnType<typeof setInterval> | null = null;\n\n /** State accessor from subscriptions (for filtering when tokenDetectionEnabled is false) */\n #getAssetsState?: () => AssetsControllerStateInternal;\n\n constructor(options: AccountsApiDataSourceOptions) {\n super(CONTROLLER_NAME, {\n ...defaultState,\n ...options.state,\n });\n\n this.#onActiveChainsUpdated = options.onActiveChainsUpdated;\n this.#pollInterval = options.pollInterval ?? DEFAULT_POLL_INTERVAL;\n this.#fetchTimeoutMs = options.fetchTimeoutMs ?? DEFAULT_FETCH_TIMEOUT_MS;\n this.#tokenDetectionEnabled =\n options.tokenDetectionEnabled ?? ((): boolean => true);\n this.#messenger = options.messenger;\n this.#apiClient = options.queryApiClient;\n\n // The Snaps → AssetsController migration flags gate which migration networks\n // (Solana, Stellar, Tron) are surfaced as active chains (see\n // `#shouldSupportChain`). Mirror core-backend's AccountActivityService and\n // react to remote feature flag changes so newly-enabled chains are picked up\n // (and disabled ones dropped) without waiting for the periodic refresh.\n this.#messenger.subscribe(\n // eslint-disable-next-line no-restricted-syntax\n 'RemoteFeatureFlagController:stateChange',\n // Promise result intentionally not awaited\n // eslint-disable-next-line @typescript-eslint/no-misused-promises\n async () => await this.#handleMigrationFeatureFlagsChanged(),\n // Only react to changes in the set of migration stages. The messenger\n // compares selector results with strict equality, so the selector must\n // return a primitive rather than a fresh object.\n (state) => getMigrationStages(state.remoteFeatureFlags).join(','),\n );\n\n this.#initializeActiveChains().catch(console.error);\n }\n\n /**\n * Handle a change to the Snaps → AssetsController migration flags: re-fetch\n * active chains so newly-enabled migration networks are surfaced (and disabled\n * ones dropped) without waiting for the periodic refresh. The refresh invokes\n * `onActiveChainsUpdated` when the set changes, which drives re-subscription.\n */\n async #handleMigrationFeatureFlagsChanged(): Promise<void> {\n try {\n await this.#refreshActiveChains();\n } catch (error) {\n log('Failed to refresh active chains after feature flag change', {\n error,\n });\n }\n }\n\n /**\n * Whether the Accounts API v6 balances endpoint is enabled, read from the\n * RemoteFeatureFlagController (`assetsAccountsApiV6`). Read on demand (per\n * fetch) rather than cached so the value can be toggled at runtime, and behaves\n * the same across clients (extension, mobile). Defaults to `false` when the\n * flag is unset or the controller is unavailable.\n *\n * The flag is a LaunchDarkly JSON variation shaped `{ value: boolean }` (same\n * shape as `backendWebSocketConnection`), so the nested `value` is read rather\n * than treating the flag as a plain boolean.\n *\n * @returns `true` when the v6 balances endpoint should be used.\n */\n #isBalanceV6Enabled(): boolean {\n try {\n const { remoteFeatureFlags } = this.#messenger.call(\n 'RemoteFeatureFlagController:getState',\n );\n const flag = remoteFeatureFlags?.assetsAccountsApiV6;\n return (\n typeof flag === 'object' &&\n flag !== null &&\n Boolean((flag as { value?: unknown }).value)\n );\n } catch {\n return false;\n }\n }\n\n // ============================================================================\n // INITIALIZATION\n // ============================================================================\n\n async #initializeActiveChains(): Promise<void> {\n try {\n const chains = await this.#fetchActiveChains();\n const previous = [...this.state.activeChains];\n this.updateActiveChains(chains, (updatedChains) =>\n this.#onActiveChainsUpdated(this.getName(), updatedChains, previous),\n );\n\n // Periodically refresh active chains (every 20 minutes)\n this.#chainsRefreshTimer = setInterval(\n () => {\n this.#refreshActiveChains().catch(console.error);\n },\n 20 * 60 * 1000,\n );\n } catch (error) {\n log('Failed to fetch active chains', error);\n }\n }\n\n async #refreshActiveChains(): Promise<void> {\n try {\n const chains = await this.#fetchActiveChains();\n const previousChains = new Set(this.state.activeChains);\n const newChains = new Set(chains);\n\n // Check if chains changed\n const added = chains.filter((chain) => !previousChains.has(chain));\n const removed = Array.from(previousChains).filter(\n (chain) => !newChains.has(chain),\n );\n\n if (added.length > 0 || removed.length > 0) {\n const previous = [...this.state.activeChains];\n this.updateActiveChains(chains, (updatedChains) =>\n this.#onActiveChainsUpdated(this.getName(), updatedChains, previous),\n );\n }\n } catch (error) {\n log('Failed to refresh active chains', error);\n }\n }\n\n /**\n * Re-fetch supported networks from the Accounts API and update `activeChains`\n * when the list changed. Used when the selected EVM network switches so\n * chain claiming is not stuck on an empty init-time list.\n *\n * @returns Resolves when supported networks have been re-fetched.\n */\n refreshActiveChains(): Promise<void> {\n return this.#refreshActiveChains();\n }\n\n async #fetchActiveChains(): Promise<ChainId[]> {\n const response = await this.#apiClient.accounts.fetchV2SupportedNetworks();\n\n // Use fullSupport networks as active chains, gated by the Snaps →\n // AssetsController migration FF: non-migration namespaces (e.g. `eip155`)\n // are always surfaced, while migration networks (Solana, Stellar, Tron) are\n // only surfaced once their per-network stage reaches\n // ReadAssetsControllerWithFallback.\n const { remoteFeatureFlags } = this.#messenger.call(\n 'RemoteFeatureFlagController:getState',\n );\n return response.fullSupport\n .map(decimalToChainId)\n .filter((chainId) => shouldSupportChain(chainId, remoteFeatureFlags));\n }\n\n // ============================================================================\n // ACCOUNT SCOPE HELPERS\n // ============================================================================\n\n // ============================================================================\n // FETCH\n // ============================================================================\n\n async fetch(request: DataRequest): Promise<DataResponse> {\n let response: DataResponse = {};\n\n // Filter to only chains supported by Accounts API\n const supportedChains = new Set(this.state.activeChains);\n const chainsToFetch = request.chainIds.filter((chainId) =>\n supportedChains.has(chainId),\n );\n\n if (chainsToFetch.length === 0) {\n // Mark unsupported chains as errors so they pass to next middleware\n for (const chainId of request.chainIds) {\n if (!supportedChains.has(chainId)) {\n response.errors = response.errors ?? {};\n response.errors[chainId] = 'Chain not supported by Accounts API';\n }\n }\n return response;\n }\n\n try {\n // Build CAIP-10 account IDs (e.g., \"eip155:1:0x1234...\")\n // Use pre-computed supportedChains per account from the request\n const accountIds = request.accountsWithSupportedChains.flatMap(\n ({ account, supportedChains: accountChains }) =>\n chainsToFetch\n .filter((chainId) => accountChains.includes(chainId))\n .map((chainId) => `${chainId}:${account.address}`),\n );\n\n // Skip API call if no valid account-chain combinations\n if (accountIds.length === 0) {\n return response;\n }\n\n const fetchOptions =\n request.forceUpdate || request.bypassServerCache\n ? {\n staleTime: 0,\n gcTime: 0,\n // Also defeats the API's server-side cache (via a random\n // bypassServerCache query param) so a post-transaction refresh cannot\n // be answered with a pre-transaction snapshot.\n ...(request.bypassServerCache ? { bypassServerCache: true } : {}),\n }\n : undefined;\n\n // Feature-flagged: v6 endpoint with a fallback to legacy v5. The flag is\n // read here (not cached) so a runtime toggle can revert v6 -> v5.\n const { unprocessedNetworks, assetsBalance } = this.#isBalanceV6Enabled()\n ? await this.#fetchV6Balances(accountIds, fetchOptions, request)\n : await this.#fetchV5Balances(accountIds, fetchOptions, request);\n\n // Handle unprocessed networks - these will be passed to next middleware\n if (unprocessedNetworks.length > 0) {\n const unprocessedChainIds =\n unprocessedNetworks.map(caipChainIdToChainId);\n\n // Add unprocessed chains to errors so middleware passes them to next data source\n response.errors = response.errors ?? {};\n for (const chainId of unprocessedChainIds) {\n response.errors[chainId] = 'Unprocessed by Accounts API';\n }\n }\n\n response.assetsBalance = assetsBalance;\n response.updateMode = 'merge';\n } catch (error) {\n log('Fetch FAILED', { error, chains: chainsToFetch });\n\n // On error, mark all chains as errors so they can be handled by next middleware\n response.errors = response.errors ?? {};\n for (const chainId of chainsToFetch) {\n response.errors[chainId] =\n `Fetch failed: ${error instanceof Error ? error.message : String(error)}`;\n }\n }\n\n // Mark unsupported chains as errors so they pass to next middleware\n for (const chainId of request.chainIds) {\n if (!supportedChains.has(chainId)) {\n response.errors = response.errors ?? {};\n response.errors[chainId] = 'Chain not supported by Accounts API';\n }\n }\n\n // When token detection is disabled, filter out tokens not already in state\n if (!this.#tokenDetectionEnabled() && this.#getAssetsState) {\n response = filterResponseToKnownAssets(response, this.#getAssetsState());\n }\n\n return response;\n }\n\n /**\n * Fetch balances from the legacy v5 endpoint and process them.\n *\n * @param accountIds - CAIP-10 account IDs to fetch balances for.\n * @param fetchOptions - Cache/fetch options (e.g. force update settings).\n * @param request - The original data request containing accounts to map.\n * @returns Unprocessed networks and processed asset balances by account.\n */\n async #fetchV5Balances(\n accountIds: string[],\n fetchOptions:\n | { staleTime: number; gcTime: number; bypassServerCache?: boolean }\n | undefined,\n request: DataRequest,\n ): Promise<{\n unprocessedNetworks: string[];\n assetsBalance: Record<string, Record<Caip19AssetId, AssetBalance>>;\n }> {\n const apiResponse = await fetchWithTimeout(\n () =>\n this.#apiClient.accounts.fetchV5MultiAccountBalances(\n accountIds,\n undefined,\n fetchOptions,\n ),\n this.#fetchTimeoutMs,\n );\n\n const { assetsBalance } = this.#processV5Balances(\n apiResponse.balances,\n request,\n );\n\n return {\n unprocessedNetworks: apiResponse.unprocessedNetworks,\n assetsBalance,\n };\n }\n\n /**\n * Fetch balances from the v6 endpoint and process them.\n *\n * @param accountIds - CAIP-10 account IDs to fetch balances for.\n * @param fetchOptions - Cache/fetch options (e.g. force update settings).\n * @param request - The original data request containing accounts to map.\n * @returns Unprocessed networks and processed asset balances by account.\n */\n async #fetchV6Balances(\n accountIds: string[],\n fetchOptions:\n | { staleTime: number; gcTime: number; bypassServerCache?: boolean }\n | undefined,\n request: DataRequest,\n ): Promise<{\n unprocessedNetworks: string[];\n assetsBalance: Record<string, Record<Caip19AssetId, AssetBalance>>;\n }> {\n const apiResponse = await fetchWithTimeout(\n () =>\n this.#apiClient.accounts.fetchV6MultiAccountBalances(\n accountIds,\n undefined,\n fetchOptions,\n ),\n this.#fetchTimeoutMs,\n );\n\n const { assetsBalance } = this.#processV6Balances(\n apiResponse.balances,\n request,\n );\n\n return {\n unprocessedNetworks: apiResponse.unprocessedNetworks,\n assetsBalance,\n };\n }\n\n /**\n * Build a lookup of lowercased account address to the request's account ID.\n *\n * @param request - The original data request containing accounts to map.\n * @returns Map of lowercase address to account ID.\n */\n #buildAddressToAccountIdMap(request: DataRequest): Map<string, string> {\n const addressToAccountId = new Map<string, string>();\n for (const { account } of request.accountsWithSupportedChains) {\n if (account.address) {\n addressToAccountId.set(account.address.toLowerCase(), account.id);\n }\n }\n return addressToAccountId;\n }\n\n /**\n * Process V5 API balances response.\n * V5 returns a flat array of balance items, each with accountId and assetId.\n *\n * @param balances - Array of balance items from the V5 API response.\n * @param request - The original data request containing accounts to map.\n * @returns Object containing processed asset balances by account.\n */\n #processV5Balances(\n balances: V5BalanceItem[],\n request: DataRequest,\n ): {\n assetsBalance: Record<string, Record<Caip19AssetId, AssetBalance>>;\n } {\n const assetsBalance: Record<\n string,\n Record<Caip19AssetId, AssetBalance>\n > = {};\n\n // Build a map of lowercase addresses to account IDs for efficient lookup\n const addressToAccountId = this.#buildAddressToAccountIdMap(request);\n\n // V5 response: array of { accountId, assetId, balance, ... }\n for (const item of balances) {\n // Extract address from CAIP-10 account ID (e.g., \"eip155:1:0x1234...\" -> \"0x1234...\")\n const addressParts = item.accountId.split(':');\n if (addressParts.length < 3) {\n continue;\n }\n const address = addressParts[2].toLowerCase();\n\n // Find the matching account ID from request\n const accountId = addressToAccountId.get(address);\n if (!accountId) {\n // This is normal - API returns balances for all chains, but request may only have one account\n continue;\n }\n\n if (!assetsBalance[accountId]) {\n assetsBalance[accountId] = {};\n }\n\n // Normalize asset ID (checksum EVM addresses for ERC20 tokens)\n const normalizedAssetId = normalizeAssetId(item.assetId as Caip19AssetId);\n\n // Staked balances are owned by StakedBalanceDataSource. Accounts API may\n // return the vault share token as a normal ERC-20 (often 0 or stale),\n // which would overwrite or wipe the on-chain staked amount on merge.\n if (isStakingContractAssetId(normalizedAssetId)) {\n continue;\n }\n\n // Store balance as returned by API\n assetsBalance[accountId][normalizedAssetId] = {\n amount: item.balance,\n };\n }\n\n return { assetsBalance };\n }\n\n /**\n * Process V6 API balances response.\n * V6 returns a flat array of rows carrying their `accountId`.\n * Only `object: 'token'` rows are consumed here to preserve parity with\n * the v5 token-balance behavior; DeFi positions are ignored.\n *\n * @param balances - Flat balance rows from the V6 API response.\n * @param request - The original data request containing accounts to map.\n * @returns Object containing processed asset balances by account.\n */\n #processV6Balances(\n balances: V6BalanceItem[],\n request: DataRequest,\n ): {\n assetsBalance: Record<string, Record<Caip19AssetId, AssetBalance>>;\n } {\n const assetsBalance = Object.create(null) as Record<\n string,\n Record<Caip19AssetId, AssetBalance>\n >;\n\n // Build a map of lowercase addresses to account IDs for efficient lookup\n const addressToAccountId = this.#buildAddressToAccountIdMap(request);\n\n for (const item of balances) {\n // Extract address from CAIP-10 account ID (e.g., \"eip155:1:0x1234...\" -> \"0x1234...\")\n const addressParts = item.accountId.split(':');\n if (addressParts.length < 3) {\n continue;\n }\n const address = addressParts[2].toLowerCase();\n\n // Find the matching account ID from request\n const accountId = addressToAccountId.get(address);\n if (!accountId) {\n // This is normal - API returns balances for all chains, but request may only have one account\n continue;\n }\n\n // Only consume token balances; DeFi positions are handled elsewhere.\n if (item.object !== 'token') {\n continue;\n }\n\n if (!assetsBalance[accountId]) {\n assetsBalance[accountId] = Object.create(null) as Record<\n Caip19AssetId,\n AssetBalance\n >;\n }\n\n // Normalize asset ID (checksum EVM addresses for ERC20 tokens)\n const normalizedAssetId = normalizeAssetId(item.assetId as Caip19AssetId);\n\n // Staked balances are owned by StakedBalanceDataSource. Accounts API may\n // return the vault share token as a normal ERC-20 (often 0 or stale),\n // which would overwrite or wipe the on-chain staked amount on merge.\n if (isStakingContractAssetId(normalizedAssetId)) {\n continue;\n }\n\n // Store balance as returned by API\n assetsBalance[accountId][normalizedAssetId] = {\n amount: item.balance,\n };\n }\n\n return { assetsBalance };\n }\n\n // ============================================================================\n // MIDDLEWARE\n // ============================================================================\n\n /**\n * Get the middleware for fetching balances via Accounts API.\n * This middleware:\n * - Supports multiple accounts in a single request\n * - Uses unprocessedNetworks from API response to determine what to pass to next middleware\n * - Merges response into context\n * - Removes handled chains from request for next middleware\n *\n * @returns The middleware function for the assets pipeline.\n */\n get assetsMiddleware(): Middleware {\n return async (context, next) => {\n const { request } = context;\n\n // Price/metadata-only requests must not hit the Accounts API.\n if (!request.dataTypes.includes('balance')) {\n return next(context);\n }\n\n // If no chains requested, skip to next middleware\n if (request.chainIds.length === 0) {\n return next(context);\n }\n\n let successfullyHandledChains: ChainId[] = [];\n\n try {\n const response = await this.fetch(request);\n\n // Merge response into context\n if (response.assetsBalance) {\n context.response.assetsBalance ??= {};\n for (const [accountId, accountBalances] of Object.entries(\n response.assetsBalance,\n )) {\n context.response.assetsBalance[accountId] = {\n ...context.response.assetsBalance[accountId],\n ...accountBalances,\n };\n }\n }\n\n // Determine successfully handled chains (exclude unprocessed/error chains)\n const unprocessedChains = new Set(Object.keys(response.errors ?? {}));\n successfullyHandledChains = request.chainIds.filter(\n (chainId) => !unprocessedChains.has(chainId),\n );\n\n // When token detection is off and we filtered out all balance data (e.g. new\n // account with empty state), do not claim any chain as handled so that RPC\n // middleware can still process them and fetch native balances (ETH, MATIC, etc.).\n if (\n !this.#tokenDetectionEnabled() &&\n (!response.assetsBalance ||\n Object.keys(response.assetsBalance).length === 0)\n ) {\n successfullyHandledChains = [];\n }\n } catch (error) {\n log('Middleware fetch failed', { error });\n successfullyHandledChains = [];\n }\n\n // Remove successfully handled chains from request for next middleware\n if (successfullyHandledChains.length > 0) {\n const remainingChains = request.chainIds.filter(\n (chainId) => !successfullyHandledChains.includes(chainId),\n );\n\n return next({\n ...context,\n request: {\n ...request,\n chainIds: remainingChains,\n },\n });\n }\n\n // No chains handled - pass context unchanged\n return next(context);\n };\n }\n\n // ============================================================================\n // SUBSCRIBE\n // ============================================================================\n\n async subscribe(subscriptionRequest: SubscriptionRequest): Promise<void> {\n const { request, subscriptionId, isUpdate, skipInitialFetch } =\n subscriptionRequest;\n\n // Store state accessor for filtering when tokenDetectionEnabled is false\n if (subscriptionRequest.getAssetsState) {\n this.#getAssetsState = subscriptionRequest.getAssetsState;\n }\n\n // Try all requested chains - API will handle unsupported ones via unprocessedNetworks\n const chainsToSubscribe = request.chainIds;\n\n if (chainsToSubscribe.length === 0) {\n return;\n }\n\n // Handle subscription update - update both chains AND request (for accounts)\n if (isUpdate) {\n const existing = this.activeSubscriptions.get(subscriptionId);\n if (existing) {\n const previousChains = existing.chains;\n existing.chains = chainsToSubscribe;\n existing.request = request;\n\n // Chains handed off from another data source (e.g. the websocket\n // source releasing a chain that went down) would otherwise stay\n // stale until the next poll tick — fetch them immediately.\n const addedChains = chainsToSubscribe.filter(\n (chainId) => !previousChains.includes(chainId),\n );\n if (addedChains.length > 0) {\n try {\n const fetchResponse = await this.fetch({\n ...request,\n chainIds: addedChains,\n forceUpdate: true,\n });\n await existing.onAssetsUpdate(fetchResponse);\n } catch (error) {\n log('Initial fetch for added chains failed', {\n subscriptionId,\n addedChains,\n error,\n });\n }\n }\n return;\n }\n }\n\n // Clean up existing subscription if any\n await this.unsubscribe(subscriptionId);\n\n const pollInterval = request.updateInterval ?? this.#pollInterval;\n\n // Create poll function for this subscription\n const pollFn = async (): Promise<void> => {\n try {\n const subscription = this.activeSubscriptions.get(subscriptionId);\n if (!subscription?.request) {\n return;\n }\n\n // Use stored request (which gets updated on account changes).\n // forceUpdate so we don't get a stale response from the cache\n // (STALE_TIMES.BALANCES is 60s, longer than our 30s poll interval).\n const fetchResponse = await this.fetch({\n ...subscription.request,\n chainIds: subscription.chains,\n forceUpdate: true,\n });\n\n // Report update to AssetsController via callback\n await subscription.onAssetsUpdate(fetchResponse);\n } catch (error) {\n log('Subscription poll failed', { subscriptionId, error });\n }\n };\n\n // Set up polling\n const timer = setInterval(() => {\n pollFn().catch(console.error);\n }, pollInterval);\n\n // Store subscription with request for account updates\n this.activeSubscriptions.set(subscriptionId, {\n cleanup: () => {\n clearInterval(timer);\n },\n chains: chainsToSubscribe,\n request,\n onAssetsUpdate: subscriptionRequest.onAssetsUpdate,\n });\n\n // Interval above still polls on the normal cadence. This only skips the\n // one-shot fetch at subscribe time when the controller already ran a\n // force getAssets for the same scope (startup / group refresh).\n if (!skipInitialFetch) {\n await pollFn();\n }\n }\n\n // ============================================================================\n // CLEANUP\n // ============================================================================\n\n destroy(): void {\n if (this.#chainsRefreshTimer) {\n clearInterval(this.#chainsRefreshTimer);\n }\n\n super.destroy();\n }\n}\n"]}
|
|
1
|
+
{"version":3,"file":"AccountsApiDataSource.mjs","sourceRoot":"","sources":["../../src/data-sources/AccountsApiDataSource.ts"],"names":[],"mappings":"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","sourcesContent":["import type { V5BalanceItem, V6BalanceItem } from '@metamask/core-backend';\nimport { ApiPlatformClient } from '@metamask/core-backend';\nimport type {\n RemoteFeatureFlagControllerGetStateAction,\n RemoteFeatureFlagControllerStateChangeEvent,\n} from '@metamask/remote-feature-flag-controller';\nimport {\n isCaipChainId,\n KnownCaipNamespace,\n parseCaipAssetType,\n toCaipChainId,\n} from '@metamask/utils';\n\nimport type { AssetsControllerMessenger } from '../AssetsController.js';\nimport { projectLogger, createModuleLogger } from '../logger.js';\nimport type {\n ChainId,\n Caip19AssetId,\n AssetBalance,\n DataRequest,\n DataResponse,\n Middleware,\n AssetsControllerStateInternal,\n} from '../types.js';\nimport { fetchWithTimeout, normalizeAssetId } from '../utils/index.js';\nimport {\n getMigrationStages,\n shouldSupportChain,\n} from '../utils/snaps-assets-migration.js';\nimport type {\n DataSourceState,\n SubscriptionRequest,\n} from './AbstractDataSource.js';\nimport { AbstractDataSource } from './AbstractDataSource.js';\nimport { isStakingContractAssetId } from './evm-rpc-services/index.js';\n\n// ============================================================================\n// CONSTANTS\n// ============================================================================\n\nconst CONTROLLER_NAME = 'AccountsApiDataSource';\nconst DEFAULT_POLL_INTERVAL = 30_000;\nconst DEFAULT_FETCH_TIMEOUT_MS = 15_000;\n\nconst log = createModuleLogger(projectLogger, CONTROLLER_NAME);\n\n// ============================================================================\n// MESSENGER TYPES\n// ============================================================================\n\n// Allowed actions that AccountsApiDataSource can call. Balances are fetched via\n// ApiPlatformClient directly (no BackendApiClient actions needed); the messenger\n// is used to subscribe to `RemoteFeatureFlagController:stateChange` (see\n// constructor) so migration flag changes refresh the active chains, and to\n// read those flags when listing active chains.\nexport type AccountsApiDataSourceAllowedActions =\n RemoteFeatureFlagControllerGetStateAction;\n\n// Allowed events that AccountsApiDataSource subscribes to. Migration flag\n// changes trigger a refresh of the chains surfaced as active.\nexport type AccountsApiDataSourceAllowedEvents =\n RemoteFeatureFlagControllerStateChangeEvent;\n\n// ============================================================================\n// STATE\n// ============================================================================\n\nexport type AccountsApiDataSourceState = DataSourceState;\n\nconst defaultState: AccountsApiDataSourceState = {\n activeChains: [],\n};\n\n// ============================================================================\n// OPTIONS\n// ============================================================================\n\n/** Optional configuration for AccountsApiDataSource. */\nexport type AccountsApiDataSourceConfig = {\n /** Polling interval in ms (default: 30000) */\n pollInterval?: number;\n /**\n * Function returning whether token detection is enabled (default: () => true).\n * When it returns false, balances are only returned for tokens already in state.\n * Using a getter avoids stale values when the user toggles the preference at runtime.\n */\n tokenDetectionEnabled?: () => boolean;\n /**\n * Timeout in ms for a single balances fetch call (default: 15000).\n * When it fires, every requested chain is marked as errored so the\n * middleware hands them off to the next data source (e.g. RPC fallback).\n */\n fetchTimeoutMs?: number;\n};\n\nexport type AccountsApiDataSourceOptions = AccountsApiDataSourceConfig & {\n /**\n * The AssetsController messenger (shared by all data sources). Used to read\n * Snaps → AssetsController migration flags and subscribe to flag changes.\n */\n messenger: AssetsControllerMessenger;\n /** ApiPlatformClient for API calls with caching */\n queryApiClient: ApiPlatformClient;\n /** Called when active chains are updated. Pass dataSourceName so the controller knows the source. */\n onActiveChainsUpdated: (\n dataSourceName: string,\n chains: ChainId[],\n previousChains: ChainId[],\n ) => void;\n /**\n * Whether to use the Accounts API v6 balances endpoint (and `includeAssetIds`).\n * Injected by AssetsController so the flag is read in one place. Defaults to\n * `false` (v5). Read on demand, not cached.\n */\n isBalanceV6Enabled?: () => boolean;\n state?: Partial<AccountsApiDataSourceState>;\n};\n\n// ============================================================================\n// HELPER FUNCTIONS\n// ============================================================================\n\nfunction decimalToChainId(decimalChainId: number | string): ChainId {\n // Handle both decimal numbers and already-formatted CAIP chain IDs\n if (typeof decimalChainId === 'string') {\n if (isCaipChainId(decimalChainId)) {\n return decimalChainId;\n }\n return toCaipChainId(KnownCaipNamespace.Eip155, decimalChainId);\n }\n return toCaipChainId(KnownCaipNamespace.Eip155, String(decimalChainId));\n}\n\n/**\n * Convert a CAIP-2 chain ID from the API response to our ChainId type.\n * Handles both formats: \"eip155:1\" or just \"1\" (decimal).\n * Uses @metamask/utils for CAIP parsing.\n *\n * @param chainIdStr - The chain ID string to convert.\n * @returns The normalized ChainId.\n */\nfunction caipChainIdToChainId(chainIdStr: string): ChainId {\n if (isCaipChainId(chainIdStr)) {\n return chainIdStr;\n }\n return toCaipChainId(KnownCaipNamespace.Eip155, chainIdStr);\n}\n\n/**\n * Filter a response to only include balances for assets already in state.\n * Used when tokenDetectionEnabled is false to prevent adding new tokens.\n *\n * @param response - The fetch response to filter.\n * @param assetsState - Current assets controller state to check existing balances against.\n * @returns A new response with only known asset balances.\n */\nexport function filterResponseToKnownAssets(\n response: DataResponse,\n assetsState: AssetsControllerStateInternal,\n): DataResponse {\n if (!response.assetsBalance) {\n return response;\n }\n\n const filteredBalance: Record<\n string,\n Record<Caip19AssetId, AssetBalance>\n > = {};\n\n for (const [accountId, accountBalances] of Object.entries(\n response.assetsBalance,\n )) {\n const existingBalances = assetsState.assetsBalance[accountId];\n if (!existingBalances) {\n // Account has no balances in state yet — skip all its tokens\n continue;\n }\n\n const filtered: Record<Caip19AssetId, AssetBalance> = {};\n for (const [assetId, balance] of Object.entries(accountBalances)) {\n // Only include assets already tracked in state\n if (assetId in existingBalances) {\n filtered[assetId as Caip19AssetId] = balance;\n }\n }\n\n if (Object.keys(filtered).length > 0) {\n filteredBalance[accountId] = filtered;\n }\n }\n\n return {\n ...response,\n assetsBalance:\n Object.keys(filteredBalance).length > 0 ? filteredBalance : undefined,\n };\n}\n\n// ============================================================================\n// ACCOUNTS API DATA SOURCE\n// ============================================================================\n\n/**\n * Data source for fetching balances from the MetaMask Accounts API.\n *\n * Uses ApiPlatformClient (queryApiClient) for all API calls. Does not use the\n * messenger. Reports active chains via onActiveChainsUpdated callback.\n */\nexport class AccountsApiDataSource extends AbstractDataSource<\n typeof CONTROLLER_NAME,\n AccountsApiDataSourceState\n> {\n readonly #onActiveChainsUpdated: (\n dataSourceName: string,\n chains: ChainId[],\n previousChains: ChainId[],\n ) => void;\n\n readonly #pollInterval: number;\n\n readonly #fetchTimeoutMs: number;\n\n /** Getter avoids stale value when user toggles token detection at runtime. */\n readonly #tokenDetectionEnabled: () => boolean;\n\n /** Shared AssetsController messenger, used to read remote feature flags. */\n readonly #messenger: AssetsControllerMessenger;\n\n /** Injected by AssetsController; `true` when the v6 balances endpoint should be used. */\n readonly #isBalanceV6Enabled: () => boolean;\n\n /** ApiPlatformClient for cached API calls */\n readonly #apiClient: ApiPlatformClient;\n\n /** Chains refresh timer */\n #chainsRefreshTimer: ReturnType<typeof setInterval> | null = null;\n\n /** State accessor from subscriptions (for filtering when tokenDetectionEnabled is false) */\n #getAssetsState?: () => AssetsControllerStateInternal;\n\n constructor(options: AccountsApiDataSourceOptions) {\n super(CONTROLLER_NAME, {\n ...defaultState,\n ...options.state,\n });\n\n this.#onActiveChainsUpdated = options.onActiveChainsUpdated;\n this.#pollInterval = options.pollInterval ?? DEFAULT_POLL_INTERVAL;\n this.#fetchTimeoutMs = options.fetchTimeoutMs ?? DEFAULT_FETCH_TIMEOUT_MS;\n this.#tokenDetectionEnabled =\n options.tokenDetectionEnabled ?? ((): boolean => true);\n this.#messenger = options.messenger;\n this.#isBalanceV6Enabled =\n options.isBalanceV6Enabled ?? ((): boolean => false);\n this.#apiClient = options.queryApiClient;\n\n // The Snaps → AssetsController migration flags gate which migration networks\n // (Solana, Stellar, Tron) are surfaced as active chains (see\n // `#shouldSupportChain`). Mirror core-backend's AccountActivityService and\n // react to remote feature flag changes so newly-enabled chains are picked up\n // (and disabled ones dropped) without waiting for the periodic refresh.\n this.#messenger.subscribe(\n // eslint-disable-next-line no-restricted-syntax\n 'RemoteFeatureFlagController:stateChange',\n // Promise result intentionally not awaited\n // eslint-disable-next-line @typescript-eslint/no-misused-promises\n async () => await this.#handleMigrationFeatureFlagsChanged(),\n // Only react to changes in the set of migration stages. The messenger\n // compares selector results with strict equality, so the selector must\n // return a primitive rather than a fresh object.\n (state) => getMigrationStages(state.remoteFeatureFlags).join(','),\n );\n\n this.#initializeActiveChains().catch(console.error);\n }\n\n /**\n * Handle a change to the Snaps → AssetsController migration flags: re-fetch\n * active chains so newly-enabled migration networks are surfaced (and disabled\n * ones dropped) without waiting for the periodic refresh. The refresh invokes\n * `onActiveChainsUpdated` when the set changes, which drives re-subscription.\n */\n async #handleMigrationFeatureFlagsChanged(): Promise<void> {\n try {\n await this.#refreshActiveChains();\n } catch (error) {\n log('Failed to refresh active chains after feature flag change', {\n error,\n });\n }\n }\n\n // ============================================================================\n // INITIALIZATION\n // ============================================================================\n\n async #initializeActiveChains(): Promise<void> {\n try {\n const chains = await this.#fetchActiveChains();\n const previous = [...this.state.activeChains];\n this.updateActiveChains(chains, (updatedChains) =>\n this.#onActiveChainsUpdated(this.getName(), updatedChains, previous),\n );\n\n // Periodically refresh active chains (every 20 minutes)\n this.#chainsRefreshTimer = setInterval(\n () => {\n this.#refreshActiveChains().catch(console.error);\n },\n 20 * 60 * 1000,\n );\n } catch (error) {\n log('Failed to fetch active chains', error);\n }\n }\n\n async #refreshActiveChains(): Promise<void> {\n try {\n const chains = await this.#fetchActiveChains();\n const previousChains = new Set(this.state.activeChains);\n const newChains = new Set(chains);\n\n // Check if chains changed\n const added = chains.filter((chain) => !previousChains.has(chain));\n const removed = Array.from(previousChains).filter(\n (chain) => !newChains.has(chain),\n );\n\n if (added.length > 0 || removed.length > 0) {\n const previous = [...this.state.activeChains];\n this.updateActiveChains(chains, (updatedChains) =>\n this.#onActiveChainsUpdated(this.getName(), updatedChains, previous),\n );\n }\n } catch (error) {\n log('Failed to refresh active chains', error);\n }\n }\n\n /**\n * Re-fetch supported networks from the Accounts API and update `activeChains`\n * when the list changed. Used when the selected EVM network switches so\n * chain claiming is not stuck on an empty init-time list.\n *\n * @returns Resolves when supported networks have been re-fetched.\n */\n refreshActiveChains(): Promise<void> {\n return this.#refreshActiveChains();\n }\n\n async #fetchActiveChains(): Promise<ChainId[]> {\n const response = await this.#apiClient.accounts.fetchV2SupportedNetworks();\n\n // Use fullSupport networks as active chains, gated by the Snaps →\n // AssetsController migration FF: non-migration namespaces (e.g. `eip155`)\n // are always surfaced, while migration networks (Solana, Stellar, Tron) are\n // only surfaced once their per-network stage reaches\n // ReadAssetsControllerWithFallback.\n const { remoteFeatureFlags } = this.#messenger.call(\n 'RemoteFeatureFlagController:getState',\n );\n return response.fullSupport\n .map(decimalToChainId)\n .filter((chainId) => shouldSupportChain(chainId, remoteFeatureFlags));\n }\n\n // ============================================================================\n // ACCOUNT SCOPE HELPERS\n // ============================================================================\n\n // ============================================================================\n // FETCH\n // ============================================================================\n\n async fetch(request: DataRequest): Promise<DataResponse> {\n let response: DataResponse = {};\n\n // Filter to only chains supported by Accounts API\n const supportedChains = new Set(this.state.activeChains);\n const chainsToFetch = request.chainIds.filter((chainId) =>\n supportedChains.has(chainId),\n );\n\n if (chainsToFetch.length === 0) {\n // Mark unsupported chains as errors so they pass to next middleware\n for (const chainId of request.chainIds) {\n if (!supportedChains.has(chainId)) {\n response.errors = response.errors ?? {};\n response.errors[chainId] = 'Chain not supported by Accounts API';\n }\n }\n return response;\n }\n\n try {\n // Build CAIP-10 account IDs (e.g., \"eip155:1:0x1234...\")\n // Use pre-computed supportedChains per account from the request\n const accountIds = request.accountsWithSupportedChains.flatMap(\n ({ account, supportedChains: accountChains }) =>\n chainsToFetch\n .filter((chainId) => accountChains.includes(chainId))\n .map((chainId) => `${chainId}:${account.address}`),\n );\n\n // Skip API call if no valid account-chain combinations\n if (accountIds.length === 0) {\n return response;\n }\n\n const fetchOptions = request.forceUpdate\n ? { staleTime: 0, gcTime: 0 }\n : undefined;\n\n const isV6 = this.#isBalanceV6Enabled();\n let fetchResult: {\n unprocessedNetworks: string[];\n unprocessedIncludeAssetIds: string[];\n assetsBalance: Record<string, Record<Caip19AssetId, AssetBalance>>;\n };\n\n if (isV6) {\n // User-pinned assets on the fetched chains, sent to v6 as\n // `includeAssetIds` so the backend returns them even at zero balance.\n const includeAssetIds = this.#getIncludeAssetIds(\n request,\n chainsToFetch,\n );\n\n // User-hidden assets on the fetched chains, sent to v6 as\n // `excludeAssetIds`. A pin wins over a hide.\n const excludeAssetIds = this.#getExcludeAssetIds(\n request,\n chainsToFetch,\n includeAssetIds,\n );\n\n fetchResult = await this.#fetchV6Balances(\n accountIds,\n fetchOptions,\n request,\n includeAssetIds,\n excludeAssetIds,\n );\n\n const validUnprocessedAssetIds =\n fetchResult.unprocessedIncludeAssetIds.filter((assetId) => {\n try {\n parseCaipAssetType(assetId as Caip19AssetId);\n return true;\n } catch {\n return false;\n }\n });\n if (validUnprocessedAssetIds.length > 0) {\n response.unprocessedCustomAssets = [\n ...(response.unprocessedCustomAssets ?? []),\n ...(validUnprocessedAssetIds as Caip19AssetId[]),\n ];\n }\n response.updateMode = 'full';\n } else {\n fetchResult = await this.#fetchV5Balances(\n accountIds,\n fetchOptions,\n request,\n );\n response.updateMode = 'merge';\n }\n\n const { unprocessedNetworks, assetsBalance } = fetchResult;\n\n // Handle unprocessed networks - these will be passed to next middleware\n if (unprocessedNetworks.length > 0) {\n const unprocessedChainIds =\n unprocessedNetworks.map(caipChainIdToChainId);\n\n // Add unprocessed chains to errors so middleware passes them to next data source\n response.errors = response.errors ?? {};\n for (const chainId of unprocessedChainIds) {\n response.errors[chainId] = 'Unprocessed by Accounts API';\n }\n }\n\n response.assetsBalance = assetsBalance;\n } catch (error) {\n log('Fetch FAILED', { error, chains: chainsToFetch });\n\n // On error, mark all chains as errors so they can be handled by next middleware\n response.errors = response.errors ?? {};\n for (const chainId of chainsToFetch) {\n response.errors[chainId] =\n `Fetch failed: ${error instanceof Error ? error.message : String(error)}`;\n }\n }\n\n // Mark unsupported chains as errors so they pass to next middleware\n for (const chainId of request.chainIds) {\n if (!supportedChains.has(chainId)) {\n response.errors = response.errors ?? {};\n response.errors[chainId] = 'Chain not supported by Accounts API';\n }\n }\n\n // When token detection is disabled, filter out tokens not already in state\n if (!this.#tokenDetectionEnabled() && this.#getAssetsState) {\n response = filterResponseToKnownAssets(response, this.#getAssetsState());\n }\n\n return response;\n }\n\n /**\n * Collect the pinned EVM assets on the fetched chains to send to the v6\n * endpoint as `includeAssetIds`; malformed IDs are skipped.\n *\n * @param request - The data request (carries `customAssets`).\n * @param chainsToFetch - Chains being requested this fetch.\n * @returns Deduplicated asset IDs, or `undefined` when none.\n */\n #getIncludeAssetIds(\n request: DataRequest,\n chainsToFetch: ChainId[],\n ): Caip19AssetId[] | undefined {\n if (!request.customAssets || request.customAssets.length === 0) {\n return undefined;\n }\n\n const chainsToFetchSet = new Set<ChainId>(chainsToFetch);\n const includeAssetIds = new Set<Caip19AssetId>();\n\n for (const assetId of request.customAssets) {\n let chainId: ChainId;\n try {\n chainId = parseCaipAssetType(assetId).chainId;\n } catch {\n continue;\n }\n if (\n chainId.startsWith(`${KnownCaipNamespace.Eip155}:`) &&\n chainsToFetchSet.has(chainId)\n ) {\n includeAssetIds.add(assetId);\n }\n }\n\n return includeAssetIds.size > 0 ? [...includeAssetIds] : undefined;\n }\n\n /**\n * Collect the hidden EVM assets on the fetched chains to send to the v6\n * endpoint as `excludeAssetIds`; malformed IDs are skipped and pinned\n * assets are left out (a pin wins).\n *\n * @param request - The data request (carries `excludeAssetIds`).\n * @param chainsToFetch - Chains being requested this fetch.\n * @param includeAssetIds - Pinned asset IDs that must not be excluded.\n * @returns Deduplicated asset IDs, or `undefined` when none.\n */\n #getExcludeAssetIds(\n request: DataRequest,\n chainsToFetch: ChainId[],\n includeAssetIds: Caip19AssetId[] | undefined,\n ): Caip19AssetId[] | undefined {\n if (!request.excludeAssetIds || request.excludeAssetIds.length === 0) {\n return undefined;\n }\n\n const chainsToFetchSet = new Set<ChainId>(chainsToFetch);\n const includeSet = new Set<Caip19AssetId>(includeAssetIds ?? []);\n const excludeAssetIds = new Set<Caip19AssetId>();\n\n for (const assetId of request.excludeAssetIds) {\n if (includeSet.has(assetId)) {\n continue;\n }\n let chainId: ChainId;\n try {\n chainId = parseCaipAssetType(assetId).chainId;\n } catch {\n continue;\n }\n if (\n chainId.startsWith(`${KnownCaipNamespace.Eip155}:`) &&\n chainsToFetchSet.has(chainId)\n ) {\n excludeAssetIds.add(assetId);\n }\n }\n\n return excludeAssetIds.size > 0 ? [...excludeAssetIds] : undefined;\n }\n\n /**\n * Fetch balances from the legacy v5 endpoint and process them.\n *\n * @param accountIds - CAIP-10 account IDs to fetch balances for.\n * @param fetchOptions - Cache/fetch options (e.g. force update settings).\n * @param request - The original data request containing accounts to map.\n * @returns Unprocessed networks and processed asset balances by account.\n */\n async #fetchV5Balances(\n accountIds: string[],\n fetchOptions: { staleTime: number; gcTime: number } | undefined,\n request: DataRequest,\n ): Promise<{\n unprocessedNetworks: string[];\n unprocessedIncludeAssetIds: string[];\n assetsBalance: Record<string, Record<Caip19AssetId, AssetBalance>>;\n }> {\n const apiResponse = await fetchWithTimeout(\n () =>\n this.#apiClient.accounts.fetchV5MultiAccountBalances(\n accountIds,\n undefined,\n fetchOptions,\n ),\n this.#fetchTimeoutMs,\n );\n\n const { assetsBalance } = this.#processV5Balances(\n apiResponse.balances,\n request,\n );\n\n return {\n unprocessedNetworks: apiResponse.unprocessedNetworks,\n // v5 has no `includeAssetIds` support.\n unprocessedIncludeAssetIds: [],\n assetsBalance,\n };\n }\n\n /**\n * Fetch balances from the v6 endpoint and process them.\n *\n * @param accountIds - CAIP-10 account IDs to fetch balances for.\n * @param fetchOptions - Cache/fetch options (e.g. force update settings).\n * @param request - The original data request containing accounts to map.\n * @param includeAssetIds - Pinned asset IDs the backend must always return.\n * @param excludeAssetIds - Hidden asset IDs the backend must drop.\n * @returns Unprocessed networks, unprocessed pinned assets, and processed\n * asset balances by account.\n */\n async #fetchV6Balances(\n accountIds: string[],\n fetchOptions: { staleTime: number; gcTime: number } | undefined,\n request: DataRequest,\n includeAssetIds: Caip19AssetId[] | undefined,\n excludeAssetIds: Caip19AssetId[] | undefined,\n ): Promise<{\n unprocessedNetworks: string[];\n unprocessedIncludeAssetIds: string[];\n assetsBalance: Record<string, Record<Caip19AssetId, AssetBalance>>;\n }> {\n const params =\n includeAssetIds || excludeAssetIds\n ? {\n ...(includeAssetIds && { includeAssetIds }),\n ...(excludeAssetIds && { excludeAssetIds }),\n }\n : undefined;\n\n const apiResponse = await fetchWithTimeout(\n () =>\n this.#apiClient.accounts.fetchV6MultiAccountBalances(\n accountIds,\n params,\n fetchOptions,\n ),\n this.#fetchTimeoutMs,\n );\n\n const { assetsBalance } = this.#processV6Balances(\n apiResponse.balances,\n request,\n );\n\n return {\n unprocessedNetworks: apiResponse.unprocessedNetworks,\n unprocessedIncludeAssetIds: apiResponse.unprocessedIncludeAssetIds,\n assetsBalance,\n };\n }\n\n /**\n * Build a lookup of lowercased account address to the request's account ID.\n *\n * @param request - The original data request containing accounts to map.\n * @returns Map of lowercase address to account ID.\n */\n #buildAddressToAccountIdMap(request: DataRequest): Map<string, string> {\n const addressToAccountId = new Map<string, string>();\n for (const { account } of request.accountsWithSupportedChains) {\n if (account.address) {\n addressToAccountId.set(account.address.toLowerCase(), account.id);\n }\n }\n return addressToAccountId;\n }\n\n /**\n * Process V5 API balances response.\n * V5 returns a flat array of balance items, each with accountId and assetId.\n *\n * @param balances - Array of balance items from the V5 API response.\n * @param request - The original data request containing accounts to map.\n * @returns Object containing processed asset balances by account.\n */\n #processV5Balances(\n balances: V5BalanceItem[],\n request: DataRequest,\n ): {\n assetsBalance: Record<string, Record<Caip19AssetId, AssetBalance>>;\n } {\n const assetsBalance: Record<\n string,\n Record<Caip19AssetId, AssetBalance>\n > = {};\n\n // Build a map of lowercase addresses to account IDs for efficient lookup\n const addressToAccountId = this.#buildAddressToAccountIdMap(request);\n\n // V5 response: array of { accountId, assetId, balance, ... }\n for (const item of balances) {\n // Extract address from CAIP-10 account ID (e.g., \"eip155:1:0x1234...\" -> \"0x1234...\")\n const addressParts = item.accountId.split(':');\n if (addressParts.length < 3) {\n continue;\n }\n const address = addressParts[2].toLowerCase();\n\n // Find the matching account ID from request\n const accountId = addressToAccountId.get(address);\n if (!accountId) {\n // This is normal - API returns balances for all chains, but request may only have one account\n continue;\n }\n\n if (!assetsBalance[accountId]) {\n assetsBalance[accountId] = {};\n }\n\n // Normalize asset ID (checksum EVM addresses for ERC20 tokens)\n const normalizedAssetId = normalizeAssetId(item.assetId as Caip19AssetId);\n\n // Staked balances are owned by StakedBalanceDataSource. Accounts API may\n // return the vault share token as a normal ERC-20 (often 0 or stale),\n // which would overwrite or wipe the on-chain staked amount on merge.\n if (isStakingContractAssetId(normalizedAssetId)) {\n continue;\n }\n\n // Store balance as returned by API\n assetsBalance[accountId][normalizedAssetId] = {\n amount: item.balance,\n };\n }\n\n return { assetsBalance };\n }\n\n /**\n * Process V6 API balances response.\n * V6 returns a flat array of rows carrying their `accountId`.\n * Only `object: 'token'` rows are consumed here to preserve parity with\n * the v5 token-balance behavior; DeFi positions are ignored.\n *\n * @param balances - Flat balance rows from the V6 API response.\n * @param request - The original data request containing accounts to map.\n * @returns Object containing processed asset balances by account.\n */\n #processV6Balances(\n balances: V6BalanceItem[],\n request: DataRequest,\n ): {\n assetsBalance: Record<string, Record<Caip19AssetId, AssetBalance>>;\n } {\n const assetsBalance = Object.create(null) as Record<\n string,\n Record<Caip19AssetId, AssetBalance>\n >;\n\n // Build a map of lowercase addresses to account IDs for efficient lookup\n const addressToAccountId = this.#buildAddressToAccountIdMap(request);\n\n for (const item of balances) {\n // Extract address from CAIP-10 account ID (e.g., \"eip155:1:0x1234...\" -> \"0x1234...\")\n const addressParts = item.accountId.split(':');\n if (addressParts.length < 3) {\n continue;\n }\n const address = addressParts[2].toLowerCase();\n\n // Find the matching account ID from request\n const accountId = addressToAccountId.get(address);\n if (!accountId) {\n // This is normal - API returns balances for all chains, but request may only have one account\n continue;\n }\n\n // Only consume token balances; DeFi positions are handled elsewhere.\n if (item.object !== 'token') {\n continue;\n }\n\n if (!assetsBalance[accountId]) {\n assetsBalance[accountId] = Object.create(null) as Record<\n Caip19AssetId,\n AssetBalance\n >;\n }\n\n // Normalize asset ID (checksum EVM addresses for ERC20 tokens)\n const normalizedAssetId = normalizeAssetId(item.assetId as Caip19AssetId);\n\n // Staked balances are owned by StakedBalanceDataSource. Accounts API may\n // return the vault share token as a normal ERC-20 (often 0 or stale),\n // which would overwrite or wipe the on-chain staked amount on merge.\n if (isStakingContractAssetId(normalizedAssetId)) {\n continue;\n }\n\n // Store balance as returned by API\n assetsBalance[accountId][normalizedAssetId] = {\n amount: item.balance,\n };\n }\n\n return { assetsBalance };\n }\n\n // ============================================================================\n // MIDDLEWARE\n // ============================================================================\n\n /**\n * Get the middleware for fetching balances via Accounts API.\n * This middleware:\n * - Supports multiple accounts in a single request\n * - Uses unprocessedNetworks from API response to determine what to pass to next middleware\n * - Merges response into context\n * - Removes handled chains from request for next middleware\n *\n * @returns The middleware function for the assets pipeline.\n */\n get assetsMiddleware(): Middleware {\n return async (context, next) => {\n const { request } = context;\n\n // Price/metadata-only requests must not hit the Accounts API.\n if (!request.dataTypes.includes('balance')) {\n return next(context);\n }\n\n // If no chains requested, skip to next middleware\n if (request.chainIds.length === 0) {\n return next(context);\n }\n\n let successfullyHandledChains: ChainId[] = [];\n\n try {\n const response = await this.fetch(request);\n\n // Merge response into context\n if (response.assetsBalance) {\n context.response.assetsBalance ??= {};\n for (const [accountId, accountBalances] of Object.entries(\n response.assetsBalance,\n )) {\n context.response.assetsBalance[accountId] = {\n ...context.response.assetsBalance[accountId],\n ...accountBalances,\n };\n }\n }\n\n if (response.updateMode === 'full') {\n context.response = {\n ...context.response,\n updateMode: 'full',\n };\n } else if (\n response.updateMode === 'merge' &&\n context.response.updateMode !== 'full'\n ) {\n context.response = {\n ...context.response,\n updateMode: 'merge',\n };\n }\n\n // Forward the asset-axis signal so the RPC fallback recovers these pins.\n if (\n response.unprocessedCustomAssets &&\n response.unprocessedCustomAssets.length > 0\n ) {\n context.response.unprocessedCustomAssets = [\n ...(context.response.unprocessedCustomAssets ?? []),\n ...response.unprocessedCustomAssets,\n ];\n }\n\n // Determine successfully handled chains (exclude unprocessed/error chains)\n const unprocessedChains = new Set(Object.keys(response.errors ?? {}));\n successfullyHandledChains = request.chainIds.filter(\n (chainId) => !unprocessedChains.has(chainId),\n );\n\n // When token detection is off and we filtered out all balance data (e.g. new\n // account with empty state), do not claim any chain as handled so that RPC\n // middleware can still process them and fetch native balances (ETH, MATIC, etc.).\n if (\n !this.#tokenDetectionEnabled() &&\n (!response.assetsBalance ||\n Object.keys(response.assetsBalance).length === 0)\n ) {\n successfullyHandledChains = [];\n }\n } catch (error) {\n log('Middleware fetch failed', { error });\n successfullyHandledChains = [];\n }\n\n // Remove successfully handled chains from request for next middleware\n if (successfullyHandledChains.length > 0) {\n const remainingChains = request.chainIds.filter(\n (chainId) => !successfullyHandledChains.includes(chainId),\n );\n\n return next({\n ...context,\n request: {\n ...request,\n chainIds: remainingChains,\n },\n });\n }\n\n // No chains handled - pass context unchanged\n return next(context);\n };\n }\n\n // ============================================================================\n // SUBSCRIBE\n // ============================================================================\n\n /**\n * Claim EVM pins on assigned chains (sent to v6 as `includeAssetIds`).\n * v5 has no `includeAssetIds`, so with the v6 flag off nothing is claimed\n * and pins fall through to RPC.\n *\n * @param customAssets - Candidate CAIP-19 asset IDs still unclaimed.\n * @param assignedChains - Chains assigned to this source in the handoff.\n * @returns The claimed subset of `customAssets`.\n */\n claimCustomAssets(\n customAssets: Caip19AssetId[],\n assignedChains: ChainId[],\n ): Caip19AssetId[] {\n if (!this.#isBalanceV6Enabled()) {\n return [];\n }\n const assigned = new Set<ChainId>(assignedChains);\n return customAssets.filter((assetId) => {\n try {\n const parsed = parseCaipAssetType(assetId);\n return (\n parsed.chain.namespace === KnownCaipNamespace.Eip155 &&\n assigned.has(parsed.chainId)\n );\n } catch {\n return false;\n }\n });\n }\n\n async subscribe(subscriptionRequest: SubscriptionRequest): Promise<void> {\n const { request, subscriptionId, isUpdate, skipInitialFetch } =\n subscriptionRequest;\n\n // Store state accessor for filtering when tokenDetectionEnabled is false\n if (subscriptionRequest.getAssetsState) {\n this.#getAssetsState = subscriptionRequest.getAssetsState;\n }\n\n // Try all requested chains - API will handle unsupported ones via unprocessedNetworks\n const chainsToSubscribe = request.chainIds;\n\n if (chainsToSubscribe.length === 0) {\n return;\n }\n\n // Handle subscription update - update both chains AND request (for accounts)\n if (isUpdate) {\n const existing = this.activeSubscriptions.get(subscriptionId);\n if (existing) {\n const previousChains = existing.chains;\n existing.chains = chainsToSubscribe;\n existing.request = request;\n\n // Chains handed off from another data source (e.g. the websocket\n // source releasing a chain that went down) would otherwise stay\n // stale until the next poll tick — fetch them immediately.\n const addedChains = chainsToSubscribe.filter(\n (chainId) => !previousChains.includes(chainId),\n );\n if (addedChains.length > 0) {\n try {\n const fetchResponse = await this.fetch({\n ...request,\n chainIds: addedChains,\n forceUpdate: true,\n });\n await existing.onAssetsUpdate(fetchResponse);\n } catch (error) {\n log('Initial fetch for added chains failed', {\n subscriptionId,\n addedChains,\n error,\n });\n }\n }\n return;\n }\n }\n\n // Clean up existing subscription if any\n await this.unsubscribe(subscriptionId);\n\n const pollInterval = request.updateInterval ?? this.#pollInterval;\n\n // Create poll function for this subscription\n const pollFn = async (): Promise<void> => {\n try {\n const subscription = this.activeSubscriptions.get(subscriptionId);\n if (!subscription?.request) {\n return;\n }\n\n // Use stored request (which gets updated on account changes).\n // forceUpdate so we don't get a stale response from the cache\n // (STALE_TIMES.BALANCES is 60s, longer than our 30s poll interval).\n const fetchResponse = await this.fetch({\n ...subscription.request,\n chainIds: subscription.chains,\n forceUpdate: true,\n });\n\n // Report update to AssetsController via callback\n await subscription.onAssetsUpdate(fetchResponse);\n } catch (error) {\n log('Subscription poll failed', { subscriptionId, error });\n }\n };\n\n // Set up polling\n const timer = setInterval(() => {\n pollFn().catch(console.error);\n }, pollInterval);\n\n // Store subscription with request for account updates\n this.activeSubscriptions.set(subscriptionId, {\n cleanup: () => {\n clearInterval(timer);\n },\n chains: chainsToSubscribe,\n request,\n onAssetsUpdate: subscriptionRequest.onAssetsUpdate,\n });\n\n // Interval above still polls on the normal cadence. This only skips the\n // one-shot fetch at subscribe time when the controller already ran a\n // force getAssets for the same scope (startup / group refresh).\n if (!skipInitialFetch) {\n await pollFn();\n }\n }\n\n // ============================================================================\n // CLEANUP\n // ============================================================================\n\n destroy(): void {\n if (this.#chainsRefreshTimer) {\n clearInterval(this.#chainsRefreshTimer);\n }\n\n super.destroy();\n }\n}\n"]}
|