@metamask-previews/assets-controller 16.0.0-preview-ff6e6f34a → 16.0.0-preview-95104ffdb
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 +4 -10
- package/dist/AssetsController.d.ts +3 -7
- package/dist/AssetsController.d.ts.map +1 -1
- package/dist/AssetsController.js +240 -671
- package/dist/AssetsController.js.map +1 -1
- package/dist/data-sources/AbstractDataSource.d.ts +1 -13
- package/dist/data-sources/AbstractDataSource.d.ts.map +1 -1
- package/dist/data-sources/AbstractDataSource.js +0 -23
- package/dist/data-sources/AbstractDataSource.js.map +1 -1
- package/dist/data-sources/AccountActivityDataSource.d.ts.map +1 -1
- package/dist/data-sources/AccountActivityDataSource.js +1 -2
- package/dist/data-sources/AccountActivityDataSource.js.map +1 -1
- package/dist/data-sources/AccountsApiDataSource.d.ts +2 -18
- package/dist/data-sources/AccountsApiDataSource.d.ts.map +1 -1
- package/dist/data-sources/AccountsApiDataSource.js +41 -165
- package/dist/data-sources/AccountsApiDataSource.js.map +1 -1
- package/dist/data-sources/RpcDataSource.d.ts +0 -17
- package/dist/data-sources/RpcDataSource.d.ts.map +1 -1
- package/dist/data-sources/RpcDataSource.js +23 -200
- package/dist/data-sources/RpcDataSource.js.map +1 -1
- package/dist/data-sources/evm-rpc-services/services/BalanceFetcher.d.ts +2 -7
- package/dist/data-sources/evm-rpc-services/services/BalanceFetcher.d.ts.map +1 -1
- package/dist/data-sources/evm-rpc-services/services/BalanceFetcher.js +12 -23
- package/dist/data-sources/evm-rpc-services/services/BalanceFetcher.js.map +1 -1
- package/dist/data-sources/evm-rpc-services/types/state.d.ts +2 -3
- package/dist/data-sources/evm-rpc-services/types/state.d.ts.map +1 -1
- package/dist/data-sources/evm-rpc-services/types/state.js.map +1 -1
- package/dist/middlewares/ParallelMiddleware.d.ts.map +1 -1
- package/dist/middlewares/ParallelMiddleware.js +0 -9
- package/dist/middlewares/ParallelMiddleware.js.map +1 -1
- package/dist/middlewares/RpcFallbackMiddleware.d.ts +14 -10
- package/dist/middlewares/RpcFallbackMiddleware.d.ts.map +1 -1
- package/dist/middlewares/RpcFallbackMiddleware.js +123 -157
- package/dist/middlewares/RpcFallbackMiddleware.js.map +1 -1
- package/dist/types.d.ts +24 -28
- package/dist/types.d.ts.map +1 -1
- package/dist/types.js.map +1 -1
- package/package.json +4 -4
package/dist/AssetsController.js
CHANGED
|
@@ -13,7 +13,7 @@ import { RpcDataSource } from './data-sources/RpcDataSource.js';
|
|
|
13
13
|
import { SnapDataSource } from './data-sources/SnapDataSource.js';
|
|
14
14
|
import { StakedBalanceDataSource } from './data-sources/StakedBalanceDataSource.js';
|
|
15
15
|
import { CaipAssetNamespace, TokenDataSource, } from './data-sources/TokenDataSource.js';
|
|
16
|
-
import { CHAINS_WITH_DEFAULT_TRACKED_ASSETS, DEFAULT_TRACKED_ASSETS_BY_CHAIN, buildDefaultAssetsInfo, getDefaultAssetMetadata,
|
|
16
|
+
import { CHAINS_WITH_DEFAULT_TRACKED_ASSETS, DEFAULT_TRACKED_ASSETS_BY_CHAIN, buildDefaultAssetsInfo, getDefaultAssetMetadata, } from './defaults.js';
|
|
17
17
|
import { AssetsDataSourceError } from './errors.js';
|
|
18
18
|
import { projectLogger, createModuleLogger } from './logger.js';
|
|
19
19
|
import { CustomAssetGraduationMiddleware } from './middlewares/CustomAssetGraduationMiddleware.js';
|
|
@@ -189,10 +189,6 @@ function normalizeResponse(response) {
|
|
|
189
189
|
if (response.replaceCoveredChainBalances) {
|
|
190
190
|
normalized.replaceCoveredChainBalances = true;
|
|
191
191
|
}
|
|
192
|
-
if (response.unprocessedCustomAssets &&
|
|
193
|
-
response.unprocessedCustomAssets.length > 0) {
|
|
194
|
-
normalized.unprocessedCustomAssets = response.unprocessedCustomAssets.map((assetId) => normalizeAssetId(assetId));
|
|
195
|
-
}
|
|
196
192
|
return normalized;
|
|
197
193
|
}
|
|
198
194
|
/**
|
|
@@ -200,13 +196,12 @@ function normalizeResponse(response) {
|
|
|
200
196
|
*
|
|
201
197
|
* @param previousBalances - Balances already in state for this account.
|
|
202
198
|
* @param accountBalances - Balances from the incoming response.
|
|
203
|
-
* @param customAssetIds - Custom assets to preserve when replacing covered chains
|
|
204
|
-
*
|
|
205
|
-
*
|
|
206
|
-
* on chains present in the response before applying it, then restore custom assets.
|
|
199
|
+
* @param customAssetIds - Custom assets to preserve when replacing covered chains.
|
|
200
|
+
* @param replaceCoveredChains - When true, drop prior balances on chains present
|
|
201
|
+
* in the response before applying it (authoritative chain slice).
|
|
207
202
|
* @returns The merged balance map for the account.
|
|
208
203
|
*/
|
|
209
|
-
function
|
|
204
|
+
function mergeAccountBalances(previousBalances, accountBalances, customAssetIds, replaceCoveredChains) {
|
|
210
205
|
if (!replaceCoveredChains) {
|
|
211
206
|
return { ...previousBalances, ...accountBalances };
|
|
212
207
|
}
|
|
@@ -220,8 +215,8 @@ function mergeAccountBalancesV5(previousBalances, accountBalances, customAssetId
|
|
|
220
215
|
Object.assign(next, accountBalances);
|
|
221
216
|
for (const customId of customAssetIds) {
|
|
222
217
|
if (!Object.prototype.hasOwnProperty.call(next, customId)) {
|
|
223
|
-
const
|
|
224
|
-
next[customId] =
|
|
218
|
+
const prev = previousBalances[customId];
|
|
219
|
+
next[customId] = prev ?? { amount: '0' };
|
|
225
220
|
}
|
|
226
221
|
}
|
|
227
222
|
// Staked vault balances are owned by StakedBalanceDataSource. When an
|
|
@@ -235,38 +230,6 @@ function mergeAccountBalancesV5(previousBalances, accountBalances, customAssetId
|
|
|
235
230
|
}
|
|
236
231
|
return next;
|
|
237
232
|
}
|
|
238
|
-
/**
|
|
239
|
-
* Apply an Accounts API v6 authoritative chain snapshot.
|
|
240
|
-
*
|
|
241
|
-
* @param previousBalances - Balances already in state for this account.
|
|
242
|
-
* @param accountBalances - Balances from the incoming response.
|
|
243
|
-
* @param unprocessedCustomAssets - Pins that v6 and RPC could not resolve.
|
|
244
|
-
* @returns The merged balance map for the account.
|
|
245
|
-
*/
|
|
246
|
-
function mergeAccountBalancesV6(previousBalances, accountBalances, unprocessedCustomAssets = []) {
|
|
247
|
-
const coveredChains = new Set(Object.keys(accountBalances).map((assetId) => assetId.split('/')[0]));
|
|
248
|
-
const next = {};
|
|
249
|
-
for (const [assetId, balance] of Object.entries(previousBalances)) {
|
|
250
|
-
if (!coveredChains.has(assetId.split('/')[0])) {
|
|
251
|
-
next[assetId] = balance;
|
|
252
|
-
}
|
|
253
|
-
}
|
|
254
|
-
Object.assign(next, accountBalances);
|
|
255
|
-
const unprocessedSet = new Set(unprocessedCustomAssets.map((assetId) => normalizeAssetId(assetId)));
|
|
256
|
-
for (const [assetId, balance] of Object.entries(previousBalances)) {
|
|
257
|
-
if (unprocessedSet.has(normalizeAssetId(assetId)) &&
|
|
258
|
-
!Object.prototype.hasOwnProperty.call(next, assetId)) {
|
|
259
|
-
next[assetId] = balance;
|
|
260
|
-
}
|
|
261
|
-
}
|
|
262
|
-
for (const [assetId, balance] of Object.entries(previousBalances)) {
|
|
263
|
-
if (isStakingContractAssetId(assetId) &&
|
|
264
|
-
!Object.prototype.hasOwnProperty.call(next, assetId)) {
|
|
265
|
-
next[assetId] = balance;
|
|
266
|
-
}
|
|
267
|
-
}
|
|
268
|
-
return next;
|
|
269
|
-
}
|
|
270
233
|
// ============================================================================
|
|
271
234
|
// CONTROLLER IMPLEMENTATION
|
|
272
235
|
// ============================================================================
|
|
@@ -502,7 +465,6 @@ export class AssetsController extends BaseController {
|
|
|
502
465
|
queryApiClient,
|
|
503
466
|
onActiveChainsUpdated: this.#onActiveChainsUpdated,
|
|
504
467
|
...accountsApiDataSourceConfig,
|
|
505
|
-
isBalanceV6Enabled: () => this.#isBalanceV6Enabled(),
|
|
506
468
|
});
|
|
507
469
|
this.#snapDataSource = new SnapDataSource({
|
|
508
470
|
messenger: this.messenger,
|
|
@@ -522,7 +484,6 @@ export class AssetsController extends BaseController {
|
|
|
522
484
|
...rpcConfig,
|
|
523
485
|
isOnboarded: rpcConfig.isOnboarded ?? isOnboarded,
|
|
524
486
|
getAssetType: (assetId) => this.#getAssetType(assetId),
|
|
525
|
-
isBalanceV6Enabled: () => this.#isBalanceV6Enabled(),
|
|
526
487
|
});
|
|
527
488
|
this.#stakedBalanceDataSource = new StakedBalanceDataSource({
|
|
528
489
|
messenger: this.messenger,
|
|
@@ -555,7 +516,6 @@ export class AssetsController extends BaseController {
|
|
|
555
516
|
});
|
|
556
517
|
this.#rpcFallbackMiddleware = new RpcFallbackMiddleware({
|
|
557
518
|
rpcDataSource: this.#rpcDataSource,
|
|
558
|
-
isBalanceV6Enabled: () => this.#isBalanceV6Enabled(),
|
|
559
519
|
});
|
|
560
520
|
log('Initializing AssetsController', {
|
|
561
521
|
defaultUpdateInterval: this.#defaultUpdateInterval,
|
|
@@ -997,310 +957,152 @@ export class AssetsController extends BaseController {
|
|
|
997
957
|
if (accounts.length === 0 || chainIds.length === 0) {
|
|
998
958
|
return this.#getAssetsFromState(accounts, chainIds, assetTypes);
|
|
999
959
|
}
|
|
960
|
+
// Collect custom assets for all requested accounts
|
|
961
|
+
const customAssets = [];
|
|
962
|
+
for (const account of accounts) {
|
|
963
|
+
const accountCustomAssets = this.getCustomAssets(account.id);
|
|
964
|
+
customAssets.push(...accountCustomAssets);
|
|
965
|
+
}
|
|
1000
966
|
if (options?.forceUpdate) {
|
|
967
|
+
// Pipeline spans only on unlock/first-init fetch; later forceUpdates pass
|
|
968
|
+
// `undefined` so `emitTrace` / `withTrace` no-op without call-site if/else.
|
|
1001
969
|
const pipelineTrace = this.#firstInitFetchReported
|
|
1002
970
|
? undefined
|
|
1003
971
|
: this.#trace;
|
|
1004
|
-
const
|
|
972
|
+
const request = this.#buildDataRequest(accounts, chainIds, {
|
|
1005
973
|
assetTypes,
|
|
1006
974
|
dataTypes,
|
|
975
|
+
customAssets: customAssets.length > 0 ? customAssets : undefined,
|
|
1007
976
|
forceUpdate: true,
|
|
1008
977
|
bypassServerCache: options?.bypassServerCache,
|
|
1009
978
|
assetsForPriceUpdate: options?.assetsForPriceUpdate,
|
|
1010
|
-
};
|
|
1011
|
-
|
|
1012
|
-
|
|
1013
|
-
|
|
1014
|
-
|
|
1015
|
-
|
|
1016
|
-
|
|
1017
|
-
|
|
1018
|
-
|
|
1019
|
-
|
|
1020
|
-
|
|
1021
|
-
|
|
1022
|
-
|
|
1023
|
-
|
|
1024
|
-
|
|
1025
|
-
|
|
1026
|
-
|
|
1027
|
-
|
|
1028
|
-
|
|
979
|
+
});
|
|
980
|
+
// Fast pipeline: accountsApi + stakedBalance → detection → token + price.
|
|
981
|
+
// Snap and RPC are excluded here due to their latency (snap triggers account
|
|
982
|
+
// creation, RPC is slow on many chains). Results are committed to state
|
|
983
|
+
// immediately so the UI can display balances without waiting for them.
|
|
984
|
+
//
|
|
985
|
+
// Fast/slow pipelines use merge so partial API snapshots cannot wipe
|
|
986
|
+
// tokens missing from the response (e.g. USDC when only native balance
|
|
987
|
+
// is returned). Balances present in the response are still refreshed.
|
|
988
|
+
const fastSources = this.#isBasicFunctionality()
|
|
989
|
+
? [
|
|
990
|
+
createParallelBalanceMiddleware([
|
|
991
|
+
this.#accountsApiDataSource,
|
|
992
|
+
this.#stakedBalanceDataSource,
|
|
993
|
+
]),
|
|
994
|
+
// Graduation must run BEFORE the RPC fallback so it only sees
|
|
995
|
+
// AccountsApi/Websocket balances. RPC intentionally carries
|
|
996
|
+
// custom assets and must never trigger graduation.
|
|
997
|
+
this.#customAssetGraduationMiddleware,
|
|
998
|
+
this.#rpcFallbackMiddleware,
|
|
999
|
+
this.#detectionMiddleware,
|
|
1000
|
+
createParallelMiddleware([
|
|
1001
|
+
this.#tokenDataSource,
|
|
1002
|
+
this.#priceDataSource,
|
|
1003
|
+
]),
|
|
1004
|
+
]
|
|
1005
|
+
: [this.#stakedBalanceDataSource, this.#detectionMiddleware];
|
|
1006
|
+
const { response } = await withTrace({
|
|
1007
|
+
name: TRACE_FETCH_PIPELINE,
|
|
1008
|
+
trace: pipelineTrace,
|
|
1009
|
+
data: {
|
|
1010
|
+
chain_count: chainIds.length,
|
|
1011
|
+
account_count: accounts.length,
|
|
1012
|
+
basic_functionality: this.#isBasicFunctionality(),
|
|
1013
|
+
},
|
|
1014
|
+
fn: async (parentContext) => {
|
|
1015
|
+
const startTime = performance.now();
|
|
1016
|
+
const result = await this.#executeMiddlewares({
|
|
1017
|
+
sources: fastSources,
|
|
1018
|
+
request,
|
|
1019
|
+
parentContext,
|
|
1020
|
+
trace: pipelineTrace,
|
|
1021
|
+
});
|
|
1022
|
+
await this.#updateState({
|
|
1023
|
+
...result.response,
|
|
1024
|
+
updateMode: 'merge',
|
|
1025
|
+
replaceCoveredChainBalances: true,
|
|
1026
|
+
});
|
|
1027
|
+
const durationMs = performance.now() - startTime;
|
|
1028
|
+
// Summary fields for Assets Health (nested under the parent span).
|
|
1029
|
+
emitTrace({
|
|
1030
|
+
name: TRACE_FULL_FETCH,
|
|
1031
|
+
trace: pipelineTrace,
|
|
1032
|
+
data: {
|
|
1033
|
+
duration_ms: durationMs,
|
|
1034
|
+
chain_count: chainIds.length,
|
|
1035
|
+
account_count: accounts.length,
|
|
1036
|
+
basic_functionality: this.#isBasicFunctionality(),
|
|
1037
|
+
asset_count: result.response.assetsBalance
|
|
1038
|
+
? Object.values(result.response.assetsBalance).reduce((sum, acct) => sum + Object.keys(acct).length, 0)
|
|
1039
|
+
: 0,
|
|
1040
|
+
price_count: result.response.assetsPrice
|
|
1041
|
+
? Object.keys(result.response.assetsPrice).length
|
|
1042
|
+
: 0,
|
|
1043
|
+
...result.durationByDataSource,
|
|
1044
|
+
},
|
|
1045
|
+
parentContext,
|
|
1046
|
+
});
|
|
1047
|
+
emitTrace({
|
|
1048
|
+
name: TRACE_FIRST_INIT_FETCH,
|
|
1049
|
+
trace: pipelineTrace,
|
|
1050
|
+
data: {
|
|
1051
|
+
duration_ms: durationMs,
|
|
1052
|
+
chain_ids: JSON.stringify(chainIds),
|
|
1053
|
+
...result.durationByDataSource,
|
|
1054
|
+
},
|
|
1055
|
+
parentContext,
|
|
1056
|
+
});
|
|
1057
|
+
return result;
|
|
1058
|
+
},
|
|
1059
|
+
});
|
|
1060
|
+
// Mark after the unlock/first forceUpdate so later polls skip pipeline spans.
|
|
1061
|
+
this.#firstInitFetchReported = true;
|
|
1062
|
+
// Background (slow) lane — flattened sibling of the fast lane (not nested
|
|
1063
|
+
// inside its withTrace callback). Still fire-and-forget so UI is not blocked.
|
|
1064
|
+
const slowPipelineChainIds = this.#getSlowPipelineChainIds(chainIds, response);
|
|
1065
|
+
if (slowPipelineChainIds.length > 0) {
|
|
1066
|
+
const slowSources = this.#isBasicFunctionality()
|
|
1067
|
+
? [this.#snapDataSource, this.#rpcDataSource]
|
|
1068
|
+
: [this.#rpcDataSource];
|
|
1069
|
+
const slowRequest = { ...request, chainIds: slowPipelineChainIds };
|
|
1070
|
+
withTrace({
|
|
1071
|
+
name: TRACE_BACKGROUND_FETCH,
|
|
1072
|
+
trace: pipelineTrace,
|
|
1073
|
+
data: {
|
|
1074
|
+
chain_count: slowPipelineChainIds.length,
|
|
1075
|
+
account_count: accounts.length,
|
|
1076
|
+
},
|
|
1077
|
+
fn: async (slowParentContext) => {
|
|
1078
|
+
const { response: slowResponse } = await this.#executeMiddlewares({
|
|
1079
|
+
sources: [
|
|
1080
|
+
createParallelBalanceMiddleware(slowSources),
|
|
1081
|
+
this.#detectionMiddleware,
|
|
1082
|
+
...(this.#isBasicFunctionality()
|
|
1083
|
+
? [
|
|
1084
|
+
createParallelMiddleware([
|
|
1085
|
+
this.#tokenDataSource,
|
|
1086
|
+
this.#priceDataSource,
|
|
1087
|
+
]),
|
|
1088
|
+
]
|
|
1089
|
+
: []),
|
|
1090
|
+
],
|
|
1091
|
+
request: slowRequest,
|
|
1092
|
+
parentContext: slowParentContext,
|
|
1093
|
+
trace: pipelineTrace,
|
|
1094
|
+
});
|
|
1095
|
+
await this.#updateState({
|
|
1096
|
+
...slowResponse,
|
|
1097
|
+
updateMode: 'merge',
|
|
1098
|
+
});
|
|
1099
|
+
},
|
|
1100
|
+
}).catch((error) => log('Background pipeline failed', { error }));
|
|
1029
1101
|
}
|
|
1030
1102
|
}
|
|
1031
1103
|
const result = this.#getAssetsFromState(accounts, chainIds, assetTypes);
|
|
1032
1104
|
return result;
|
|
1033
1105
|
}
|
|
1034
|
-
/**
|
|
1035
|
-
* v5 force-update request: every pin of the requested accounts, unscoped.
|
|
1036
|
-
* Delete with the rest of the v5 path when `assetsAccountsApiV6` is the default.
|
|
1037
|
-
*
|
|
1038
|
-
* @param accounts - Accounts in this fetch.
|
|
1039
|
-
* @param chainIds - Chains in this fetch.
|
|
1040
|
-
* @param requestOptions - Shared force-update request fields.
|
|
1041
|
-
* @param requestOptions.assetTypes - Asset types to fetch.
|
|
1042
|
-
* @param requestOptions.dataTypes - Data types to fetch.
|
|
1043
|
-
* @param requestOptions.forceUpdate - Always `true` to bypass caches.
|
|
1044
|
-
* @param requestOptions.bypassServerCache - Also bypass server-side HTTP caches.
|
|
1045
|
-
* @param requestOptions.assetsForPriceUpdate - Assets to refresh prices for.
|
|
1046
|
-
* @returns The v5 data request.
|
|
1047
|
-
*/
|
|
1048
|
-
#buildForceUpdateRequestV5(accounts, chainIds, requestOptions) {
|
|
1049
|
-
const customAssets = [];
|
|
1050
|
-
for (const account of accounts) {
|
|
1051
|
-
customAssets.push(...this.#getVisibleCustomAssets(account.id));
|
|
1052
|
-
}
|
|
1053
|
-
return this.#buildDataRequest(accounts, chainIds, {
|
|
1054
|
-
...requestOptions,
|
|
1055
|
-
customAssets: customAssets.length > 0 ? customAssets : undefined,
|
|
1056
|
-
});
|
|
1057
|
-
}
|
|
1058
|
-
/**
|
|
1059
|
-
* v6 force-update request: chain-scoped pins (or `customAssets` override)
|
|
1060
|
-
* plus hidden assets as `excludeAssetIds`.
|
|
1061
|
-
*
|
|
1062
|
-
* @param accounts - Accounts in this fetch.
|
|
1063
|
-
* @param chainIds - Chains in this fetch.
|
|
1064
|
-
* @param requestOptions - Shared force-update request fields and optional pin override.
|
|
1065
|
-
* @param requestOptions.assetTypes - Asset types to fetch.
|
|
1066
|
-
* @param requestOptions.dataTypes - Data types to fetch.
|
|
1067
|
-
* @param requestOptions.forceUpdate - Always `true` to bypass caches.
|
|
1068
|
-
* @param requestOptions.bypassServerCache - Also bypass server-side HTTP caches.
|
|
1069
|
-
* @param requestOptions.assetsForPriceUpdate - Assets to refresh prices for.
|
|
1070
|
-
* @param requestOptions.customAssetsOverride - Pinned assets to use instead of the stored custom assets.
|
|
1071
|
-
* @returns The v6 data request.
|
|
1072
|
-
*/
|
|
1073
|
-
#buildForceUpdateRequestV6(accounts, chainIds, requestOptions) {
|
|
1074
|
-
const requestedChains = new Set(chainIds);
|
|
1075
|
-
const candidateCustomAssets = requestOptions.customAssetsOverride ??
|
|
1076
|
-
accounts.flatMap((account) => this.getCustomAssets(account.id));
|
|
1077
|
-
const customAssetsSet = new Set();
|
|
1078
|
-
for (const assetId of candidateCustomAssets) {
|
|
1079
|
-
try {
|
|
1080
|
-
const normalizedAssetId = normalizeAssetId(assetId);
|
|
1081
|
-
if (requestedChains.has(parseCaipAssetType(normalizedAssetId).chainId) &&
|
|
1082
|
-
!this.state.assetPreferences[normalizedAssetId]?.hidden) {
|
|
1083
|
-
customAssetsSet.add(normalizedAssetId);
|
|
1084
|
-
}
|
|
1085
|
-
}
|
|
1086
|
-
catch {
|
|
1087
|
-
// Skip unparseable asset IDs
|
|
1088
|
-
}
|
|
1089
|
-
}
|
|
1090
|
-
const customAssets = [...customAssetsSet];
|
|
1091
|
-
const hiddenAssets = this.#getHiddenAssetIds();
|
|
1092
|
-
return this.#buildDataRequest(accounts, chainIds, {
|
|
1093
|
-
assetTypes: requestOptions.assetTypes,
|
|
1094
|
-
dataTypes: requestOptions.dataTypes,
|
|
1095
|
-
forceUpdate: requestOptions.forceUpdate,
|
|
1096
|
-
bypassServerCache: requestOptions.bypassServerCache,
|
|
1097
|
-
assetsForPriceUpdate: requestOptions.assetsForPriceUpdate,
|
|
1098
|
-
customAssets: customAssets.length > 0 ? customAssets : undefined,
|
|
1099
|
-
excludeAssetIds: hiddenAssets.length > 0 ? hiddenAssets : undefined,
|
|
1100
|
-
});
|
|
1101
|
-
}
|
|
1102
|
-
async #forceUpdateAssetsV5({ accounts, chainIds, request, pipelineTrace, }) {
|
|
1103
|
-
const isBasicFunctionality = this.#isBasicFunctionality();
|
|
1104
|
-
const fastSources = isBasicFunctionality
|
|
1105
|
-
? [
|
|
1106
|
-
createParallelBalanceMiddleware([
|
|
1107
|
-
this.#accountsApiDataSource,
|
|
1108
|
-
this.#stakedBalanceDataSource,
|
|
1109
|
-
]),
|
|
1110
|
-
this.#customAssetGraduationMiddleware,
|
|
1111
|
-
this.#rpcFallbackMiddleware,
|
|
1112
|
-
this.#detectionMiddleware,
|
|
1113
|
-
createParallelMiddleware([
|
|
1114
|
-
this.#tokenDataSource,
|
|
1115
|
-
this.#priceDataSource,
|
|
1116
|
-
]),
|
|
1117
|
-
]
|
|
1118
|
-
: [this.#stakedBalanceDataSource, this.#detectionMiddleware];
|
|
1119
|
-
const fastResponse = await this.#runFastFetchV5({
|
|
1120
|
-
accounts,
|
|
1121
|
-
chainIds,
|
|
1122
|
-
request,
|
|
1123
|
-
pipelineTrace,
|
|
1124
|
-
fastSources,
|
|
1125
|
-
isBasicFunctionality,
|
|
1126
|
-
});
|
|
1127
|
-
this.#firstInitFetchReported = true;
|
|
1128
|
-
this.#runBackgroundFetch({
|
|
1129
|
-
accounts,
|
|
1130
|
-
request,
|
|
1131
|
-
pipelineTrace,
|
|
1132
|
-
chainIds: this.#getSlowPipelineChainIds(chainIds, fastResponse),
|
|
1133
|
-
isBasicFunctionality,
|
|
1134
|
-
});
|
|
1135
|
-
}
|
|
1136
|
-
async #forceUpdateAssetsV6({ accounts, chainIds, request, pipelineTrace, }) {
|
|
1137
|
-
const isBasicFunctionality = this.#isBasicFunctionality();
|
|
1138
|
-
const fastSources = isBasicFunctionality
|
|
1139
|
-
? [
|
|
1140
|
-
createParallelBalanceMiddleware([
|
|
1141
|
-
this.#accountsApiDataSource,
|
|
1142
|
-
this.#stakedBalanceDataSource,
|
|
1143
|
-
]),
|
|
1144
|
-
this.#rpcFallbackMiddleware,
|
|
1145
|
-
this.#detectionMiddleware,
|
|
1146
|
-
createParallelMiddleware([
|
|
1147
|
-
this.#tokenDataSource,
|
|
1148
|
-
this.#priceDataSource,
|
|
1149
|
-
]),
|
|
1150
|
-
]
|
|
1151
|
-
: [this.#stakedBalanceDataSource, this.#detectionMiddleware];
|
|
1152
|
-
const fastResponse = await this.#runFastFetchV6({
|
|
1153
|
-
accounts,
|
|
1154
|
-
chainIds,
|
|
1155
|
-
request,
|
|
1156
|
-
pipelineTrace,
|
|
1157
|
-
fastSources,
|
|
1158
|
-
isBasicFunctionality,
|
|
1159
|
-
});
|
|
1160
|
-
this.#firstInitFetchReported = true;
|
|
1161
|
-
this.#runBackgroundFetch({
|
|
1162
|
-
accounts,
|
|
1163
|
-
request,
|
|
1164
|
-
pipelineTrace,
|
|
1165
|
-
chainIds: this.#getSlowPipelineChainIds(chainIds, fastResponse),
|
|
1166
|
-
isBasicFunctionality,
|
|
1167
|
-
});
|
|
1168
|
-
}
|
|
1169
|
-
async #runFastFetchV5({ accounts, chainIds, request, pipelineTrace, fastSources, isBasicFunctionality, }) {
|
|
1170
|
-
const { response } = await withTrace({
|
|
1171
|
-
name: TRACE_FETCH_PIPELINE,
|
|
1172
|
-
trace: pipelineTrace,
|
|
1173
|
-
data: {
|
|
1174
|
-
chain_count: chainIds.length,
|
|
1175
|
-
account_count: accounts.length,
|
|
1176
|
-
basic_functionality: isBasicFunctionality,
|
|
1177
|
-
},
|
|
1178
|
-
fn: async (parentContext) => {
|
|
1179
|
-
const startTime = performance.now();
|
|
1180
|
-
const result = await this.#executeMiddlewares({
|
|
1181
|
-
sources: fastSources,
|
|
1182
|
-
request,
|
|
1183
|
-
parentContext,
|
|
1184
|
-
trace: pipelineTrace,
|
|
1185
|
-
});
|
|
1186
|
-
await this.#updateState({
|
|
1187
|
-
...result.response,
|
|
1188
|
-
updateMode: 'merge',
|
|
1189
|
-
replaceCoveredChainBalances: true,
|
|
1190
|
-
});
|
|
1191
|
-
this.#emitFastFetchTraces({
|
|
1192
|
-
accounts,
|
|
1193
|
-
chainIds,
|
|
1194
|
-
result,
|
|
1195
|
-
durationMs: performance.now() - startTime,
|
|
1196
|
-
isBasicFunctionality,
|
|
1197
|
-
parentContext,
|
|
1198
|
-
pipelineTrace,
|
|
1199
|
-
});
|
|
1200
|
-
return result;
|
|
1201
|
-
},
|
|
1202
|
-
});
|
|
1203
|
-
return response;
|
|
1204
|
-
}
|
|
1205
|
-
async #runFastFetchV6({ accounts, chainIds, request, pipelineTrace, fastSources, isBasicFunctionality, }) {
|
|
1206
|
-
const { response } = await withTrace({
|
|
1207
|
-
name: TRACE_FETCH_PIPELINE,
|
|
1208
|
-
trace: pipelineTrace,
|
|
1209
|
-
data: {
|
|
1210
|
-
chain_count: chainIds.length,
|
|
1211
|
-
account_count: accounts.length,
|
|
1212
|
-
basic_functionality: isBasicFunctionality,
|
|
1213
|
-
},
|
|
1214
|
-
fn: async (parentContext) => {
|
|
1215
|
-
const startTime = performance.now();
|
|
1216
|
-
const result = await this.#executeMiddlewares({
|
|
1217
|
-
sources: fastSources,
|
|
1218
|
-
request,
|
|
1219
|
-
parentContext,
|
|
1220
|
-
trace: pipelineTrace,
|
|
1221
|
-
});
|
|
1222
|
-
await this.#updateState(result.response);
|
|
1223
|
-
this.#emitFastFetchTraces({
|
|
1224
|
-
accounts,
|
|
1225
|
-
chainIds,
|
|
1226
|
-
result,
|
|
1227
|
-
durationMs: performance.now() - startTime,
|
|
1228
|
-
isBasicFunctionality,
|
|
1229
|
-
parentContext,
|
|
1230
|
-
pipelineTrace,
|
|
1231
|
-
});
|
|
1232
|
-
return result;
|
|
1233
|
-
},
|
|
1234
|
-
});
|
|
1235
|
-
return response;
|
|
1236
|
-
}
|
|
1237
|
-
#runBackgroundFetch({ accounts, chainIds, request, pipelineTrace, isBasicFunctionality, }) {
|
|
1238
|
-
if (chainIds.length === 0) {
|
|
1239
|
-
return;
|
|
1240
|
-
}
|
|
1241
|
-
const slowSources = isBasicFunctionality
|
|
1242
|
-
? [this.#snapDataSource, this.#rpcDataSource]
|
|
1243
|
-
: [this.#rpcDataSource];
|
|
1244
|
-
const slowRequest = { ...request, chainIds };
|
|
1245
|
-
withTrace({
|
|
1246
|
-
name: TRACE_BACKGROUND_FETCH,
|
|
1247
|
-
trace: pipelineTrace,
|
|
1248
|
-
data: {
|
|
1249
|
-
chain_count: chainIds.length,
|
|
1250
|
-
account_count: accounts.length,
|
|
1251
|
-
},
|
|
1252
|
-
fn: async (parentContext) => {
|
|
1253
|
-
const { response } = await this.#executeMiddlewares({
|
|
1254
|
-
sources: [
|
|
1255
|
-
createParallelBalanceMiddleware(slowSources),
|
|
1256
|
-
this.#detectionMiddleware,
|
|
1257
|
-
...(isBasicFunctionality
|
|
1258
|
-
? [
|
|
1259
|
-
createParallelMiddleware([
|
|
1260
|
-
this.#tokenDataSource,
|
|
1261
|
-
this.#priceDataSource,
|
|
1262
|
-
]),
|
|
1263
|
-
]
|
|
1264
|
-
: []),
|
|
1265
|
-
],
|
|
1266
|
-
request: slowRequest,
|
|
1267
|
-
parentContext,
|
|
1268
|
-
trace: pipelineTrace,
|
|
1269
|
-
});
|
|
1270
|
-
await this.#updateState({ ...response, updateMode: 'merge' });
|
|
1271
|
-
},
|
|
1272
|
-
}).catch((error) => log('Background pipeline failed', { error }));
|
|
1273
|
-
}
|
|
1274
|
-
#emitFastFetchTraces({ accounts, chainIds, result, durationMs, isBasicFunctionality, parentContext, pipelineTrace, }) {
|
|
1275
|
-
emitTrace({
|
|
1276
|
-
name: TRACE_FULL_FETCH,
|
|
1277
|
-
trace: pipelineTrace,
|
|
1278
|
-
data: {
|
|
1279
|
-
duration_ms: durationMs,
|
|
1280
|
-
chain_count: chainIds.length,
|
|
1281
|
-
account_count: accounts.length,
|
|
1282
|
-
basic_functionality: isBasicFunctionality,
|
|
1283
|
-
asset_count: result.response.assetsBalance
|
|
1284
|
-
? Object.values(result.response.assetsBalance).reduce((sum, account) => sum + Object.keys(account).length, 0)
|
|
1285
|
-
: 0,
|
|
1286
|
-
price_count: result.response.assetsPrice
|
|
1287
|
-
? Object.keys(result.response.assetsPrice).length
|
|
1288
|
-
: 0,
|
|
1289
|
-
...result.durationByDataSource,
|
|
1290
|
-
},
|
|
1291
|
-
parentContext,
|
|
1292
|
-
});
|
|
1293
|
-
emitTrace({
|
|
1294
|
-
name: TRACE_FIRST_INIT_FETCH,
|
|
1295
|
-
trace: pipelineTrace,
|
|
1296
|
-
data: {
|
|
1297
|
-
duration_ms: durationMs,
|
|
1298
|
-
chain_ids: JSON.stringify(chainIds),
|
|
1299
|
-
...result.durationByDataSource,
|
|
1300
|
-
},
|
|
1301
|
-
parentContext,
|
|
1302
|
-
});
|
|
1303
|
-
}
|
|
1304
1106
|
async getAssetsBalance(accounts, options) {
|
|
1305
1107
|
// Reuse getAssets with dataTypes: ['balance'] only
|
|
1306
1108
|
const assets = await this.getAssets(accounts, {
|
|
@@ -1568,28 +1370,20 @@ export class AssetsController extends BaseController {
|
|
|
1568
1370
|
balances[accountId] ??= {};
|
|
1569
1371
|
balances[accountId][normalizedAssetId] ??= { amount: '0' };
|
|
1570
1372
|
});
|
|
1373
|
+
// Fetch data for the newly added custom asset (merge to preserve other chains)
|
|
1571
1374
|
const account = this.#getSelectedAccounts().find((a) => a.id === accountId);
|
|
1572
1375
|
if (account) {
|
|
1573
1376
|
const chainId = extractChainId(normalizedAssetId);
|
|
1574
|
-
|
|
1575
|
-
|
|
1576
|
-
|
|
1577
|
-
|
|
1578
|
-
|
|
1579
|
-
|
|
1580
|
-
|
|
1581
|
-
});
|
|
1582
|
-
}
|
|
1583
|
-
else {
|
|
1584
|
-
await this.getAssets([account], {
|
|
1585
|
-
chainIds: [chainId],
|
|
1586
|
-
dataTypes: ['balance', 'metadata', 'price'],
|
|
1587
|
-
assetTypes: ['fungible'],
|
|
1588
|
-
forceUpdate: true,
|
|
1589
|
-
});
|
|
1590
|
-
}
|
|
1377
|
+
await this.getAssets([account], {
|
|
1378
|
+
chainIds: [chainId],
|
|
1379
|
+
dataTypes: ['balance', 'metadata', 'price'],
|
|
1380
|
+
assetTypes: ['fungible'],
|
|
1381
|
+
forceUpdate: true,
|
|
1382
|
+
updateMode: 'merge',
|
|
1383
|
+
});
|
|
1591
1384
|
}
|
|
1592
|
-
// Re-evaluate subscriptions so
|
|
1385
|
+
// Re-evaluate subscriptions so the supplemental RPC poll picks up the
|
|
1386
|
+
// new customAsset on chains another data source already owns.
|
|
1593
1387
|
this.#subscribeAssets();
|
|
1594
1388
|
}
|
|
1595
1389
|
/**
|
|
@@ -1610,7 +1404,8 @@ export class AssetsController extends BaseController {
|
|
|
1610
1404
|
}
|
|
1611
1405
|
}
|
|
1612
1406
|
});
|
|
1613
|
-
// Re-evaluate subscriptions so
|
|
1407
|
+
// Re-evaluate subscriptions so the supplemental RPC poll for that chain
|
|
1408
|
+
// is torn down when no more customAssets remain there.
|
|
1614
1409
|
this.#subscribeAssets();
|
|
1615
1410
|
}
|
|
1616
1411
|
/**
|
|
@@ -1641,8 +1436,6 @@ export class AssetsController extends BaseController {
|
|
|
1641
1436
|
}
|
|
1642
1437
|
state.assetPreferences[normalizedAssetId].hidden = true;
|
|
1643
1438
|
});
|
|
1644
|
-
// Re-evaluate subscriptions so polls exclude the newly hidden asset.
|
|
1645
|
-
this.#subscribeAssets();
|
|
1646
1439
|
}
|
|
1647
1440
|
/**
|
|
1648
1441
|
* Unhide an asset globally.
|
|
@@ -1661,53 +1454,6 @@ export class AssetsController extends BaseController {
|
|
|
1661
1454
|
}
|
|
1662
1455
|
}
|
|
1663
1456
|
});
|
|
1664
|
-
// Re-evaluate subscriptions so polls stop excluding the asset.
|
|
1665
|
-
this.#subscribeAssets();
|
|
1666
|
-
}
|
|
1667
|
-
/**
|
|
1668
|
-
* Collect globally hidden asset IDs (from `assetPreferences`), forwarded on
|
|
1669
|
-
* data requests as `excludeAssetIds`.
|
|
1670
|
-
*
|
|
1671
|
-
* @returns The CAIP-19 asset IDs the user has hidden.
|
|
1672
|
-
*/
|
|
1673
|
-
#getHiddenAssetIds() {
|
|
1674
|
-
const hidden = [];
|
|
1675
|
-
for (const [assetId, prefs] of Object.entries(this.state.assetPreferences)) {
|
|
1676
|
-
if (prefs.hidden) {
|
|
1677
|
-
hidden.push(assetId);
|
|
1678
|
-
}
|
|
1679
|
-
}
|
|
1680
|
-
return hidden;
|
|
1681
|
-
}
|
|
1682
|
-
/**
|
|
1683
|
-
* An account's pins minus the ones the user has hidden. A hide wins over a
|
|
1684
|
-
* pin on requests, so a hidden asset is never fetched; the pin stays in
|
|
1685
|
-
* `customAssets` to record that the token was imported, and unhiding it
|
|
1686
|
-
* restores the pin on the next subscription.
|
|
1687
|
-
*
|
|
1688
|
-
* @param accountId - The account whose pins should be collected.
|
|
1689
|
-
* @returns The account's pinned asset IDs that are not hidden.
|
|
1690
|
-
*/
|
|
1691
|
-
#getVisibleCustomAssets(accountId) {
|
|
1692
|
-
return this.getCustomAssets(accountId).filter((assetId) => !this.state.assetPreferences[assetId]?.hidden);
|
|
1693
|
-
}
|
|
1694
|
-
/**
|
|
1695
|
-
* Whether Accounts API v6 (and the v6 custom-asset path) is enabled.
|
|
1696
|
-
* Injected into AccountsApiDataSource and RpcFallbackMiddleware.
|
|
1697
|
-
*
|
|
1698
|
-
* `RemoteFeatureFlagController` resolves a threshold-scoped flag to the
|
|
1699
|
-
* selected group's `value`, so processed state is a boolean.
|
|
1700
|
-
*
|
|
1701
|
-
* @returns `true` when the v6 remote flag is on.
|
|
1702
|
-
*/
|
|
1703
|
-
#isBalanceV6Enabled() {
|
|
1704
|
-
try {
|
|
1705
|
-
const { remoteFeatureFlags } = this.messenger.call('RemoteFeatureFlagController:getState');
|
|
1706
|
-
return remoteFeatureFlags?.assetsAccountsApiV6 === true;
|
|
1707
|
-
}
|
|
1708
|
-
catch {
|
|
1709
|
-
return false;
|
|
1710
|
-
}
|
|
1711
1457
|
}
|
|
1712
1458
|
// ============================================================================
|
|
1713
1459
|
// CURRENT CURRENCY MANAGEMENT
|
|
@@ -1930,22 +1676,6 @@ export class AssetsController extends BaseController {
|
|
|
1930
1676
|
#getNativeAssetIdsForAccount(account) {
|
|
1931
1677
|
return this.#resolveNativeAssetIds(this.#getEnabledChainsForAccount(account));
|
|
1932
1678
|
}
|
|
1933
|
-
/**
|
|
1934
|
-
* Returns the controller-managed default tracked asset IDs (e.g. mUSD) for
|
|
1935
|
-
* the chains this account supports (account scopes ∩ enabled chains).
|
|
1936
|
-
* Non-EVM accounts resolve to an empty list because every chain in the
|
|
1937
|
-
* defaults registry is EVM today.
|
|
1938
|
-
*
|
|
1939
|
-
* @param account - The account (scopes determine which chains apply).
|
|
1940
|
-
* @returns Array of default tracked asset IDs across the supported chains.
|
|
1941
|
-
*/
|
|
1942
|
-
#getDefaultTrackedAssetIdsForAccount(account) {
|
|
1943
|
-
const ids = [];
|
|
1944
|
-
for (const chainId of this.#getEnabledChainsForAccount(account)) {
|
|
1945
|
-
ids.push(...getDefaultTrackedAssetsForChain(chainId));
|
|
1946
|
-
}
|
|
1947
|
-
return ids;
|
|
1948
|
-
}
|
|
1949
1679
|
/**
|
|
1950
1680
|
* Chains for the post-commit slow pipeline (Snap + RPC). Excludes chains the
|
|
1951
1681
|
* fast Accounts API path already handled without error so stale RPC data cannot
|
|
@@ -1957,16 +1687,10 @@ export class AssetsController extends BaseController {
|
|
|
1957
1687
|
*/
|
|
1958
1688
|
#getSlowPipelineChainIds(chainIds, fastResponse) {
|
|
1959
1689
|
const accountsApiChains = new Set(this.#accountsApiDataSource.getActiveChainsSync());
|
|
1960
|
-
// Chains whose pins went unresolved (`unprocessedCustomAssets`): route
|
|
1961
|
-
// them to the slow pipeline so RPC fetches the pins.
|
|
1962
|
-
const unprocessedCustomAssetChains = new Set((fastResponse.unprocessedCustomAssets ?? []).map((assetId) => assetId.split('/')[0]));
|
|
1963
1690
|
return chainIds.filter((chainId) => {
|
|
1964
1691
|
if (fastResponse.errors?.[chainId]) {
|
|
1965
1692
|
return true;
|
|
1966
1693
|
}
|
|
1967
|
-
if (unprocessedCustomAssetChains.has(chainId)) {
|
|
1968
|
-
return true;
|
|
1969
|
-
}
|
|
1970
1694
|
if (!accountsApiChains.has(chainId)) {
|
|
1971
1695
|
return true;
|
|
1972
1696
|
}
|
|
@@ -2130,9 +1854,9 @@ export class AssetsController extends BaseController {
|
|
|
2130
1854
|
for (const [accountId, accountBalances] of Object.entries(normalizedResponse.assetsBalance)) {
|
|
2131
1855
|
const previousBalances = previousState.assetsBalance[accountId] ?? {};
|
|
2132
1856
|
const customAssetIds = state.customAssets[accountId] ?? [];
|
|
2133
|
-
const
|
|
2134
|
-
|
|
2135
|
-
|
|
1857
|
+
const replaceCoveredChains = mode === 'full' ||
|
|
1858
|
+
normalizedResponse.replaceCoveredChainBalances === true;
|
|
1859
|
+
const effective = mergeAccountBalances(previousBalances, accountBalances, customAssetIds, replaceCoveredChains);
|
|
2136
1860
|
// Ensure native tokens have an entry (0 if missing) for chains this account supports
|
|
2137
1861
|
const account = this.#getSelectedAccounts().find((a) => a.id === accountId);
|
|
2138
1862
|
const nativeAssetIdsForAccount = account
|
|
@@ -2143,19 +1867,6 @@ export class AssetsController extends BaseController {
|
|
|
2143
1867
|
effective[nativeAssetId] = { amount: '0' };
|
|
2144
1868
|
}
|
|
2145
1869
|
}
|
|
2146
|
-
// Default tracked assets (mUSD) are controller-managed and, like
|
|
2147
|
-
// natives, must render at zero balance. An authoritative
|
|
2148
|
-
// chain-slice replace omits them whenever the account holds none,
|
|
2149
|
-
// so re-assert them here — otherwise a force refresh (e.g. the
|
|
2150
|
-
// "Refresh list" action) would drop mUSD from the token list.
|
|
2151
|
-
const defaultTrackedAssetIdsForAccount = account
|
|
2152
|
-
? this.#getDefaultTrackedAssetIdsForAccount(account)
|
|
2153
|
-
: [];
|
|
2154
|
-
for (const defaultAssetId of defaultTrackedAssetIdsForAccount) {
|
|
2155
|
-
if (!Object.prototype.hasOwnProperty.call(effective, defaultAssetId)) {
|
|
2156
|
-
effective[defaultAssetId] = { amount: '0' };
|
|
2157
|
-
}
|
|
2158
|
-
}
|
|
2159
1870
|
for (const [assetId, balance] of Object.entries(effective)) {
|
|
2160
1871
|
const previousBalance = previousBalances[assetId];
|
|
2161
1872
|
// Coerce amounts (e.g. "1e-18" from a data source stringifying
|
|
@@ -2167,13 +1878,19 @@ export class AssetsController extends BaseController {
|
|
|
2167
1878
|
// their metadata aren't lossily truncated.
|
|
2168
1879
|
const assetDecimals = metadata[assetId]?.decimals;
|
|
2169
1880
|
const newAmount = normalizeAmountString(balance.amount, assetDecimals);
|
|
2170
|
-
|
|
1881
|
+
// Keep existing metadata when the incoming update is
|
|
1882
|
+
// amount-only (e.g. Account Activity websocket). Incoming
|
|
1883
|
+
// metadata still wins when present.
|
|
1884
|
+
effective[assetId] = {
|
|
1885
|
+
...previousBalance,
|
|
1886
|
+
...balance,
|
|
1887
|
+
amount: newAmount,
|
|
1888
|
+
};
|
|
2171
1889
|
const oldAmount = previousBalance?.amount;
|
|
2172
|
-
const
|
|
1890
|
+
const isNewDefaultNativeZero = oldAmount === undefined &&
|
|
2173
1891
|
newAmount === '0' &&
|
|
2174
|
-
|
|
2175
|
-
|
|
2176
|
-
if (oldAmount !== newAmount && !isNewSeededZero) {
|
|
1892
|
+
nativeAssetIdsForAccount.includes(assetId);
|
|
1893
|
+
if (oldAmount !== newAmount && !isNewDefaultNativeZero) {
|
|
2177
1894
|
changedBalances.push({
|
|
2178
1895
|
accountId,
|
|
2179
1896
|
assetId,
|
|
@@ -2203,8 +1920,10 @@ export class AssetsController extends BaseController {
|
|
|
2203
1920
|
changedPriceAssets.length > 0) {
|
|
2204
1921
|
log('State updated', {
|
|
2205
1922
|
changedBalances: changedBalances.length > 0 ? changedBalances : undefined,
|
|
2206
|
-
|
|
2207
|
-
|
|
1923
|
+
changedMetadataCount: changedMetadata.length > 0 ? changedMetadata.length : undefined,
|
|
1924
|
+
changedPricesCount: changedPriceAssets.length > 0
|
|
1925
|
+
? changedPriceAssets.length
|
|
1926
|
+
: undefined,
|
|
2208
1927
|
newAssets: Object.keys(detectedAssets).length > 0
|
|
2209
1928
|
? Object.entries(detectedAssets).map(([accountId, assets]) => ({
|
|
2210
1929
|
accountId,
|
|
@@ -2426,10 +2145,16 @@ export class AssetsController extends BaseController {
|
|
|
2426
2145
|
if (!subscriptionKey.startsWith('ds:')) {
|
|
2427
2146
|
continue;
|
|
2428
2147
|
}
|
|
2429
|
-
// Subscription keys take the form `ds:<SourceName
|
|
2148
|
+
// Subscription keys take the form `ds:<SourceName>` for the regular
|
|
2149
|
+
// subscription or `ds:<SourceName>:<suffix>` for supplemental
|
|
2150
|
+
// subscriptions (e.g. `ds:RpcDataSource:custom`). Split on `:` and
|
|
2151
|
+
// pick the source-name segment so both shapes resolve correctly.
|
|
2430
2152
|
const [, sourceId] = subscriptionKey.split(':');
|
|
2431
2153
|
const source = allSources.find((ds) => ds.getName() === sourceId);
|
|
2432
2154
|
if (source) {
|
|
2155
|
+
// Unsubscribe by the actual key — `#unsubscribeDataSource` only
|
|
2156
|
+
// knows the regular `ds:<SourceName>` shape and would miss
|
|
2157
|
+
// supplemental subscriptions, leaking their polling timers.
|
|
2433
2158
|
this.#unsubscribeBySubscriptionKey(source, subscriptionKey);
|
|
2434
2159
|
}
|
|
2435
2160
|
}
|
|
@@ -2473,12 +2198,10 @@ export class AssetsController extends BaseController {
|
|
|
2473
2198
|
* Strategy to minimize data source calls:
|
|
2474
2199
|
* 1. Collect all chains to subscribe based on enabled networks
|
|
2475
2200
|
* 2. Map chains to accounts based on their scopes
|
|
2476
|
-
* 3. Split by data source (priority
|
|
2477
|
-
* subscription, claiming chains AND pinned assets (`claimCustomAssets`);
|
|
2478
|
-
* unclaimed assets fall through to lower-priority sources.
|
|
2201
|
+
* 3. Split by data source (ordered by priority) - each data source gets ONE subscription
|
|
2479
2202
|
*
|
|
2480
2203
|
* This ensures we make minimal subscriptions to each data source while covering
|
|
2481
|
-
* all accounts
|
|
2204
|
+
* all accounts and chains.
|
|
2482
2205
|
*
|
|
2483
2206
|
* @param accounts - Accounts to subscribe balance updates for.
|
|
2484
2207
|
* @param chainIds - Chain IDs to subscribe for.
|
|
@@ -2486,16 +2209,12 @@ export class AssetsController extends BaseController {
|
|
|
2486
2209
|
* @param options.skipInitialFetch - Forwarded to AccountsApi subscribe.
|
|
2487
2210
|
*/
|
|
2488
2211
|
#subscribeAssetsBalance(accounts, chainIds, options) {
|
|
2489
|
-
if (this.#isBalanceV6Enabled()) {
|
|
2490
|
-
this.#subscribeAssetsBalanceV6(accounts, chainIds, options);
|
|
2491
|
-
return;
|
|
2492
|
-
}
|
|
2493
|
-
this.#subscribeAssetsBalanceV5(accounts, chainIds, options);
|
|
2494
|
-
}
|
|
2495
|
-
#subscribeAssetsBalanceV5(accounts, chainIds, options) {
|
|
2496
2212
|
const chainToAccounts = this.#buildChainToAccountsMap(accounts, new Set(chainIds));
|
|
2497
2213
|
const remainingChains = new Set(chainToAccounts.keys());
|
|
2498
|
-
|
|
2214
|
+
// When basic functionality is on, use all balance data sources; when off,
|
|
2215
|
+
// RPC only.
|
|
2216
|
+
const isBasicFunctionality = this.#isBasicFunctionality();
|
|
2217
|
+
const balanceDataSources = isBasicFunctionality
|
|
2499
2218
|
? this.#allBalanceDataSources
|
|
2500
2219
|
: [this.#rpcDataSource];
|
|
2501
2220
|
let rpcAssignedChains = new Set();
|
|
@@ -2534,88 +2253,22 @@ export class AssetsController extends BaseController {
|
|
|
2534
2253
|
});
|
|
2535
2254
|
}
|
|
2536
2255
|
}
|
|
2256
|
+
// Supplemental RPC subscription for customAssets on chains another data
|
|
2257
|
+
// source claimed during regular handoff. RPC is the sole balance fetcher
|
|
2258
|
+
// for customAssets, so we must always poll them — even when (e.g.)
|
|
2259
|
+
// AccountsApi is already covering the chain for normal balances. The
|
|
2260
|
+
// supplemental subscription runs in `customAssetsOnly` mode so it does
|
|
2261
|
+
// NOT double-poll the regular tracked balances.
|
|
2537
2262
|
this.#subscribeRpcCustomAssetsSupplement(accounts, chainToAccounts, rpcAssignedChains);
|
|
2538
2263
|
}
|
|
2539
|
-
#subscribeAssetsBalanceV6(accounts, chainIds, options) {
|
|
2540
|
-
const chainToAccounts = this.#buildChainToAccountsMap(accounts, new Set(chainIds));
|
|
2541
|
-
const remainingChains = new Set(chainToAccounts.keys());
|
|
2542
|
-
const remainingCustomAssets = new Set();
|
|
2543
|
-
for (const account of accounts) {
|
|
2544
|
-
for (const assetId of this.#getVisibleCustomAssets(account.id)) {
|
|
2545
|
-
try {
|
|
2546
|
-
if (remainingChains.has(parseCaipAssetType(assetId).chainId)) {
|
|
2547
|
-
remainingCustomAssets.add(assetId);
|
|
2548
|
-
}
|
|
2549
|
-
}
|
|
2550
|
-
catch {
|
|
2551
|
-
// Skip unparseable asset IDs
|
|
2552
|
-
}
|
|
2553
|
-
}
|
|
2554
|
-
}
|
|
2555
|
-
const balanceDataSources = this.#isBasicFunctionality()
|
|
2556
|
-
? this.#allBalanceDataSources
|
|
2557
|
-
: [this.#rpcDataSource];
|
|
2558
|
-
for (const source of balanceDataSources) {
|
|
2559
|
-
const availableChains = new Set(source.getActiveChainsSync());
|
|
2560
|
-
const assignedChains = [];
|
|
2561
|
-
for (const chainId of remainingChains) {
|
|
2562
|
-
if (availableChains.has(chainId)) {
|
|
2563
|
-
assignedChains.push(chainId);
|
|
2564
|
-
remainingChains.delete(chainId);
|
|
2565
|
-
}
|
|
2566
|
-
}
|
|
2567
|
-
const claimedAssets = source.claimCustomAssets([...remainingCustomAssets], assignedChains);
|
|
2568
|
-
for (const assetId of claimedAssets) {
|
|
2569
|
-
remainingCustomAssets.delete(assetId);
|
|
2570
|
-
}
|
|
2571
|
-
if (assignedChains.length === 0 && claimedAssets.length === 0) {
|
|
2572
|
-
this.#unsubscribeDataSource(source);
|
|
2573
|
-
continue;
|
|
2574
|
-
}
|
|
2575
|
-
const claimedAssetsSet = new Set(claimedAssets);
|
|
2576
|
-
const seenIds = new Set();
|
|
2577
|
-
const accountsForSource = assignedChains
|
|
2578
|
-
.flatMap((chainId) => chainToAccounts.get(chainId) ?? [])
|
|
2579
|
-
.filter((account) => {
|
|
2580
|
-
if (seenIds.has(account.id)) {
|
|
2581
|
-
return false;
|
|
2582
|
-
}
|
|
2583
|
-
seenIds.add(account.id);
|
|
2584
|
-
return true;
|
|
2585
|
-
});
|
|
2586
|
-
for (const account of accounts) {
|
|
2587
|
-
if (!seenIds.has(account.id) &&
|
|
2588
|
-
this.getCustomAssets(account.id).some((assetId) => claimedAssetsSet.has(assetId))) {
|
|
2589
|
-
seenIds.add(account.id);
|
|
2590
|
-
accountsForSource.push(account);
|
|
2591
|
-
}
|
|
2592
|
-
}
|
|
2593
|
-
if (accountsForSource.length > 0) {
|
|
2594
|
-
const hiddenAssets = this.#getHiddenAssetIds();
|
|
2595
|
-
this.#subscribeDataSource(source, accountsForSource, assignedChains, {
|
|
2596
|
-
customAssets: claimedAssets,
|
|
2597
|
-
excludeAssetIds: hiddenAssets.length > 0 ? hiddenAssets : undefined,
|
|
2598
|
-
...(options?.skipInitialFetch &&
|
|
2599
|
-
source === this.#accountsApiDataSource
|
|
2600
|
-
? { skipInitialFetch: true }
|
|
2601
|
-
: {}),
|
|
2602
|
-
});
|
|
2603
|
-
}
|
|
2604
|
-
else {
|
|
2605
|
-
this.#unsubscribeDataSource(source);
|
|
2606
|
-
}
|
|
2607
|
-
}
|
|
2608
|
-
if (remainingCustomAssets.size > 0) {
|
|
2609
|
-
log('Custom assets unclaimed by any data source', {
|
|
2610
|
-
assetIds: [...remainingCustomAssets],
|
|
2611
|
-
});
|
|
2612
|
-
}
|
|
2613
|
-
}
|
|
2614
2264
|
/**
|
|
2615
2265
|
* Guarantee that customAssets are **always** polled by RPC, even when
|
|
2616
2266
|
* AccountsApi or another data source has claimed the chain in the
|
|
2617
2267
|
* regular handoff. RPC is the sole balance fetcher for user-imported
|
|
2618
|
-
* tokens
|
|
2268
|
+
* tokens (see `pickRpcCustomAssetsSupplement` for the full rationale),
|
|
2269
|
+
* so we run a dedicated subscription in `customAssetsOnly` mode under a
|
|
2270
|
+
* distinct subscription key (`ds:RpcDataSource:custom`) that does not
|
|
2271
|
+
* interfere with the regular RPC subscription.
|
|
2619
2272
|
*
|
|
2620
2273
|
* @param accounts - Accounts to consider for customAssets.
|
|
2621
2274
|
* @param chainToAccounts - Map of chain → accounts (built by caller).
|
|
@@ -2624,16 +2277,9 @@ export class AssetsController extends BaseController {
|
|
|
2624
2277
|
#subscribeRpcCustomAssetsSupplement(accounts, chainToAccounts, rpcAssignedChains) {
|
|
2625
2278
|
const rpc = this.#rpcDataSource;
|
|
2626
2279
|
const supplementalKey = `ds:${rpc.getName()}:custom`;
|
|
2627
|
-
const visibleCustomAssetsByAccount = {};
|
|
2628
|
-
for (const account of accounts) {
|
|
2629
|
-
const visibleCustomAssets = this.#getVisibleCustomAssets(account.id);
|
|
2630
|
-
if (visibleCustomAssets.length > 0) {
|
|
2631
|
-
visibleCustomAssetsByAccount[account.id] = visibleCustomAssets;
|
|
2632
|
-
}
|
|
2633
|
-
}
|
|
2634
2280
|
const decision = pickRpcCustomAssetsSupplement({
|
|
2635
2281
|
accountIds: accounts.map((account) => account.id),
|
|
2636
|
-
customAssetsByAccount:
|
|
2282
|
+
customAssetsByAccount: this.state.customAssets,
|
|
2637
2283
|
rpcAssignedChains,
|
|
2638
2284
|
rpcAvailableChains: new Set(rpc.getActiveChainsSync()),
|
|
2639
2285
|
enabledChains: new Set(chainToAccounts.keys()),
|
|
@@ -2720,12 +2366,7 @@ export class AssetsController extends BaseController {
|
|
|
2720
2366
|
* @param chains - Array of chain IDs to subscribe for.
|
|
2721
2367
|
* @param options - Optional subscription overrides.
|
|
2722
2368
|
* @param options.subscriptionKey - Custom subscription key (default: `ds:<sourceId>`).
|
|
2723
|
-
* @param options.
|
|
2724
|
-
* (`claimCustomAssets`), forwarded on the poll request (Accounts API v6).
|
|
2725
|
-
* @param options.customAssetsOnly - When true, only poll customAssets for these
|
|
2726
|
-
* chains (Accounts API v5 supplemental RPC subscription).
|
|
2727
|
-
* @param options.excludeAssetIds - Hidden assets forwarded on the poll request
|
|
2728
|
-
* (Accounts API v6).
|
|
2369
|
+
* @param options.customAssetsOnly - When true, only poll customAssets for these chains.
|
|
2729
2370
|
* @param options.skipInitialFetch - When true, skip the data source's subscribe-time fetch.
|
|
2730
2371
|
*/
|
|
2731
2372
|
#subscribeDataSource(source, accounts, chains, options = {}) {
|
|
@@ -2733,14 +2374,12 @@ export class AssetsController extends BaseController {
|
|
|
2733
2374
|
const subscriptionKey = options.subscriptionKey ?? `ds:${sourceId}`;
|
|
2734
2375
|
const existingSubscription = this.#activeSubscriptions.get(subscriptionKey);
|
|
2735
2376
|
const isUpdate = existingSubscription !== undefined;
|
|
2736
|
-
const customAssets = options.customAssets ?? [];
|
|
2737
2377
|
log('Subscribe to data source', {
|
|
2738
2378
|
sourceId,
|
|
2739
2379
|
subscriptionKey,
|
|
2740
2380
|
isUpdate,
|
|
2741
2381
|
accountCount: accounts.length,
|
|
2742
2382
|
chainCount: chains.length,
|
|
2743
|
-
customAssetCount: customAssets.length,
|
|
2744
2383
|
customAssetsOnly: options.customAssetsOnly === true,
|
|
2745
2384
|
skipInitialFetch: options.skipInitialFetch === true,
|
|
2746
2385
|
});
|
|
@@ -2749,10 +2388,6 @@ export class AssetsController extends BaseController {
|
|
|
2749
2388
|
assetTypes: ['fungible'],
|
|
2750
2389
|
dataTypes: ['balance'],
|
|
2751
2390
|
updateInterval: this.#defaultUpdateInterval,
|
|
2752
|
-
customAssets: customAssets.length > 0 ? customAssets : undefined,
|
|
2753
|
-
excludeAssetIds: options.excludeAssetIds && options.excludeAssetIds.length > 0
|
|
2754
|
-
? options.excludeAssetIds
|
|
2755
|
-
: undefined,
|
|
2756
2391
|
...(options.customAssetsOnly === true
|
|
2757
2392
|
? { customAssetsOnly: true }
|
|
2758
2393
|
: {}),
|
|
@@ -2944,13 +2579,13 @@ export class AssetsController extends BaseController {
|
|
|
2944
2579
|
// The data will simply not be updated until the network is re-enabled.
|
|
2945
2580
|
// Refresh subscriptions for new chain set
|
|
2946
2581
|
this.#subscribeAssets();
|
|
2947
|
-
//
|
|
2948
|
-
// those chains; existing balances on other chains are left untouched.
|
|
2582
|
+
// Do one-time fetch for newly enabled chains; merge so we keep existing chain balances
|
|
2949
2583
|
const accounts = this.#getSelectedAccounts();
|
|
2950
2584
|
if (addedChains.length > 0 && accounts.length > 0) {
|
|
2951
2585
|
await this.getAssets(accounts, {
|
|
2952
2586
|
chainIds: addedChains,
|
|
2953
2587
|
forceUpdate: true,
|
|
2588
|
+
updateMode: 'merge',
|
|
2954
2589
|
});
|
|
2955
2590
|
}
|
|
2956
2591
|
this.#ensureNativeBalancesDefaultZero();
|
|
@@ -3110,28 +2745,48 @@ export class AssetsController extends BaseController {
|
|
|
3110
2745
|
async handleAssetsUpdate(response, sourceId, request) {
|
|
3111
2746
|
log('Assets updated from data source', {
|
|
3112
2747
|
sourceId,
|
|
3113
|
-
|
|
3114
|
-
|
|
2748
|
+
hasBalance: Boolean(response.assetsBalance),
|
|
2749
|
+
hasPrice: Boolean(response.assetsPrice),
|
|
3115
2750
|
});
|
|
3116
|
-
|
|
3117
|
-
await this.#handleAssetsUpdateV6(response, sourceId, request);
|
|
3118
|
-
return;
|
|
3119
|
-
}
|
|
3120
|
-
await this.#handleAssetsUpdateV5(response, sourceId, request);
|
|
3121
|
-
}
|
|
3122
|
-
async #handleAssetsUpdateV5(response, sourceId, request) {
|
|
2751
|
+
// Enrichment spans only before unlock/first-init fetch completes.
|
|
3123
2752
|
const pipelineTrace = this.#firstInitFetchReported
|
|
3124
2753
|
? undefined
|
|
3125
2754
|
: this.#trace;
|
|
3126
2755
|
await withTrace({
|
|
3127
2756
|
name: TRACE_UPDATE_PARENT,
|
|
3128
2757
|
trace: pipelineTrace,
|
|
3129
|
-
data:
|
|
2758
|
+
data: {
|
|
2759
|
+
source: sourceId,
|
|
2760
|
+
has_balance: Boolean(response.assetsBalance),
|
|
2761
|
+
has_price: Boolean(response.assetsPrice),
|
|
2762
|
+
balance_account_count: response.assetsBalance
|
|
2763
|
+
? Object.keys(response.assetsBalance).length
|
|
2764
|
+
: 0,
|
|
2765
|
+
},
|
|
3130
2766
|
fn: async (parentContext) => {
|
|
3131
2767
|
const updateStart = performance.now();
|
|
3132
|
-
const
|
|
2768
|
+
const resolvedRequest = request ?? {
|
|
2769
|
+
accountsWithSupportedChains: [],
|
|
2770
|
+
chainIds: [],
|
|
2771
|
+
dataTypes: ['balance', 'metadata', 'price'],
|
|
2772
|
+
};
|
|
2773
|
+
// RPC-only mode (basic functionality off): never run token/price APIs. Strip
|
|
2774
|
+
// those data types so downstream middleware cannot treat them as requested.
|
|
2775
|
+
const pipelineRequest = this.#isBasicFunctionality()
|
|
2776
|
+
? resolvedRequest
|
|
2777
|
+
: {
|
|
2778
|
+
...resolvedRequest,
|
|
2779
|
+
dataTypes: resolvedRequest.dataTypes.filter((dt) => dt !== 'metadata' && dt !== 'price'),
|
|
2780
|
+
};
|
|
2781
|
+
// Graduate custom assets only when AccountsAPI / AccountActivity reports
|
|
2782
|
+
// them. RPC already fetches custom assets on purpose, and Snap handles
|
|
2783
|
+
// non-EVM chains the rule does not apply to, so skip the middleware for
|
|
2784
|
+
// those.
|
|
3133
2785
|
const shouldGraduateCustomAssets = sourceId === 'AccountsApiDataSource' ||
|
|
3134
2786
|
sourceId === 'AccountActivityDataSource';
|
|
2787
|
+
// Websocket updates can carry brand-new spam airdrops: enrich them
|
|
2788
|
+
// with Token API occurrences and drop below-floor tokens BEFORE
|
|
2789
|
+
// detection, so spam is never detected, enriched, priced or persisted.
|
|
3135
2790
|
const shouldFilterOccurrences = sourceId === 'AccountActivityDataSource' &&
|
|
3136
2791
|
this.#isBasicFunctionality();
|
|
3137
2792
|
const shouldRunRpcFallback = sourceId === 'AccountsApiDataSource';
|
|
@@ -3150,7 +2805,6 @@ export class AssetsController extends BaseController {
|
|
|
3150
2805
|
...(shouldRunRpcFallback ? [this.#rpcFallbackMiddleware] : []),
|
|
3151
2806
|
this.#detectionMiddleware,
|
|
3152
2807
|
];
|
|
3153
|
-
enrichmentSources.push(this.#detectionMiddleware);
|
|
3154
2808
|
if (this.#isBasicFunctionality()) {
|
|
3155
2809
|
enrichmentSources.push(createParallelMiddleware([
|
|
3156
2810
|
this.#tokenDataSource,
|
|
@@ -3168,110 +2822,25 @@ export class AssetsController extends BaseController {
|
|
|
3168
2822
|
...enrichedResponse,
|
|
3169
2823
|
replaceCoveredChainBalances: response.replaceCoveredChainBalances,
|
|
3170
2824
|
});
|
|
3171
|
-
|
|
3172
|
-
|
|
3173
|
-
|
|
3174
|
-
enrichedResponse,
|
|
3175
|
-
updateStart,
|
|
3176
|
-
pipelineTrace,
|
|
3177
|
-
parentContext,
|
|
3178
|
-
});
|
|
3179
|
-
},
|
|
3180
|
-
});
|
|
3181
|
-
}
|
|
3182
|
-
async #handleAssetsUpdateV6(response, sourceId, request) {
|
|
3183
|
-
const pipelineTrace = this.#firstInitFetchReported
|
|
3184
|
-
? undefined
|
|
3185
|
-
: this.#trace;
|
|
3186
|
-
await withTrace({
|
|
3187
|
-
name: TRACE_UPDATE_PARENT,
|
|
3188
|
-
trace: pipelineTrace,
|
|
3189
|
-
data: this.#getUpdateTraceData(response, sourceId),
|
|
3190
|
-
fn: async (parentContext) => {
|
|
3191
|
-
const updateStart = performance.now();
|
|
3192
|
-
const pipelineRequest = this.#getUpdatePipelineRequest(request);
|
|
3193
|
-
const shouldFilterOccurrences = sourceId === 'AccountActivityDataSource' &&
|
|
3194
|
-
this.#isBasicFunctionality();
|
|
3195
|
-
const enrichmentSources = [
|
|
3196
|
-
...(shouldFilterOccurrences
|
|
3197
|
-
? [
|
|
3198
|
-
{
|
|
3199
|
-
getName: () => 'OccurrenceFloorFilter',
|
|
3200
|
-
assetsMiddleware: this.#tokenDataSource.occurrenceFilterMiddleware,
|
|
3201
|
-
},
|
|
3202
|
-
]
|
|
3203
|
-
: []),
|
|
3204
|
-
];
|
|
3205
|
-
if (this.#isBasicFunctionality()) {
|
|
3206
|
-
enrichmentSources.push(this.#rpcFallbackMiddleware);
|
|
3207
|
-
}
|
|
3208
|
-
enrichmentSources.push(this.#detectionMiddleware);
|
|
3209
|
-
if (this.#isBasicFunctionality()) {
|
|
3210
|
-
enrichmentSources.push(createParallelMiddleware([
|
|
3211
|
-
this.#tokenDataSource,
|
|
3212
|
-
this.#priceDataSource,
|
|
3213
|
-
]));
|
|
3214
|
-
}
|
|
3215
|
-
const { response: enrichedResponse } = await this.#executeMiddlewares({
|
|
3216
|
-
sources: enrichmentSources,
|
|
3217
|
-
request: pipelineRequest,
|
|
3218
|
-
initialResponse: response,
|
|
3219
|
-
parentContext,
|
|
2825
|
+
// Summary fields for Assets Health (nested under the parent span).
|
|
2826
|
+
emitTrace({
|
|
2827
|
+
name: TRACE_UPDATE_PIPELINE,
|
|
3220
2828
|
trace: pipelineTrace,
|
|
3221
|
-
|
|
3222
|
-
|
|
3223
|
-
|
|
3224
|
-
|
|
3225
|
-
|
|
3226
|
-
|
|
3227
|
-
|
|
3228
|
-
|
|
2829
|
+
data: {
|
|
2830
|
+
source: sourceId,
|
|
2831
|
+
duration_ms: performance.now() - updateStart,
|
|
2832
|
+
has_balance: Boolean(response.assetsBalance),
|
|
2833
|
+
has_price: Boolean(response.assetsPrice),
|
|
2834
|
+
has_metadata: Boolean(enrichedResponse.assetsInfo),
|
|
2835
|
+
balance_account_count: response.assetsBalance
|
|
2836
|
+
? Object.keys(response.assetsBalance).length
|
|
2837
|
+
: 0,
|
|
2838
|
+
},
|
|
3229
2839
|
parentContext,
|
|
3230
2840
|
});
|
|
3231
2841
|
},
|
|
3232
2842
|
});
|
|
3233
2843
|
}
|
|
3234
|
-
#getUpdatePipelineRequest(request) {
|
|
3235
|
-
const resolvedRequest = request ?? {
|
|
3236
|
-
accountsWithSupportedChains: [],
|
|
3237
|
-
chainIds: [],
|
|
3238
|
-
dataTypes: ['balance', 'metadata', 'price'],
|
|
3239
|
-
};
|
|
3240
|
-
if (this.#isBasicFunctionality()) {
|
|
3241
|
-
return resolvedRequest;
|
|
3242
|
-
}
|
|
3243
|
-
return {
|
|
3244
|
-
...resolvedRequest,
|
|
3245
|
-
dataTypes: resolvedRequest.dataTypes.filter((dataType) => dataType !== 'metadata' && dataType !== 'price'),
|
|
3246
|
-
};
|
|
3247
|
-
}
|
|
3248
|
-
#getUpdateTraceData(response, sourceId) {
|
|
3249
|
-
return {
|
|
3250
|
-
source: sourceId,
|
|
3251
|
-
has_balance: Boolean(response.assetsBalance),
|
|
3252
|
-
has_price: Boolean(response.assetsPrice),
|
|
3253
|
-
balance_account_count: response.assetsBalance
|
|
3254
|
-
? Object.keys(response.assetsBalance).length
|
|
3255
|
-
: 0,
|
|
3256
|
-
};
|
|
3257
|
-
}
|
|
3258
|
-
#emitUpdateTrace({ response, sourceId, enrichedResponse, updateStart, pipelineTrace, parentContext, }) {
|
|
3259
|
-
emitTrace({
|
|
3260
|
-
name: TRACE_UPDATE_PIPELINE,
|
|
3261
|
-
trace: pipelineTrace,
|
|
3262
|
-
data: {
|
|
3263
|
-
source: sourceId,
|
|
3264
|
-
duration_ms: performance.now() - updateStart,
|
|
3265
|
-
has_balance: Boolean(response.assetsBalance),
|
|
3266
|
-
has_price: Boolean(response.assetsPrice),
|
|
3267
|
-
has_metadata: Boolean(enrichedResponse.assetsInfo),
|
|
3268
|
-
balance_account_count: response.assetsBalance
|
|
3269
|
-
? Object.keys(response.assetsBalance).length
|
|
3270
|
-
: 0,
|
|
3271
|
-
},
|
|
3272
|
-
parentContext,
|
|
3273
|
-
});
|
|
3274
|
-
}
|
|
3275
2844
|
// ============================================================================
|
|
3276
2845
|
// CLEANUP
|
|
3277
2846
|
// ============================================================================
|