@metamask/assets-controllers 105.1.0 → 106.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +49 -1
- package/dist/AccountTrackerController-method-action-types.cjs.map +1 -1
- package/dist/AccountTrackerController-method-action-types.d.cts +24 -1
- package/dist/AccountTrackerController-method-action-types.d.cts.map +1 -1
- package/dist/AccountTrackerController-method-action-types.d.mts +24 -1
- package/dist/AccountTrackerController-method-action-types.d.mts.map +1 -1
- package/dist/AccountTrackerController-method-action-types.mjs.map +1 -1
- package/dist/AccountTrackerController.cjs +2 -0
- package/dist/AccountTrackerController.cjs.map +1 -1
- package/dist/AccountTrackerController.d.cts.map +1 -1
- package/dist/AccountTrackerController.d.mts.map +1 -1
- package/dist/AccountTrackerController.mjs +2 -0
- package/dist/AccountTrackerController.mjs.map +1 -1
- package/dist/TokenBalancesController-method-action-types.cjs.map +1 -1
- package/dist/TokenBalancesController-method-action-types.d.cts +9 -1
- package/dist/TokenBalancesController-method-action-types.d.cts.map +1 -1
- package/dist/TokenBalancesController-method-action-types.d.mts +9 -1
- package/dist/TokenBalancesController-method-action-types.d.mts.map +1 -1
- package/dist/TokenBalancesController-method-action-types.mjs.map +1 -1
- package/dist/TokenBalancesController.cjs +19 -2
- package/dist/TokenBalancesController.cjs.map +1 -1
- package/dist/TokenBalancesController.d.cts.map +1 -1
- package/dist/TokenBalancesController.d.mts.map +1 -1
- package/dist/TokenBalancesController.mjs +20 -3
- package/dist/TokenBalancesController.mjs.map +1 -1
- package/dist/TokenDetectionController-method-action-types.cjs.map +1 -1
- package/dist/TokenDetectionController-method-action-types.d.cts +1 -1
- package/dist/TokenDetectionController-method-action-types.d.mts +1 -1
- package/dist/TokenDetectionController-method-action-types.mjs.map +1 -1
- package/dist/TokenDetectionController.cjs +137 -145
- package/dist/TokenDetectionController.cjs.map +1 -1
- package/dist/TokenDetectionController.d.cts +9 -13
- package/dist/TokenDetectionController.d.cts.map +1 -1
- package/dist/TokenDetectionController.d.mts +9 -13
- package/dist/TokenDetectionController.d.mts.map +1 -1
- package/dist/TokenDetectionController.mjs +138 -146
- package/dist/TokenDetectionController.mjs.map +1 -1
- package/dist/TokenListService.cjs +107 -0
- package/dist/TokenListService.cjs.map +1 -0
- package/dist/TokenListService.d.cts +40 -0
- package/dist/TokenListService.d.cts.map +1 -0
- package/dist/TokenListService.d.mts +40 -0
- package/dist/TokenListService.d.mts.map +1 -0
- package/dist/TokenListService.mjs +102 -0
- package/dist/TokenListService.mjs.map +1 -0
- package/dist/TokensController.cjs +49 -131
- package/dist/TokensController.cjs.map +1 -1
- package/dist/TokensController.d.cts +8 -6
- package/dist/TokensController.d.cts.map +1 -1
- package/dist/TokensController.d.mts +8 -6
- package/dist/TokensController.d.mts.map +1 -1
- package/dist/TokensController.mjs +49 -131
- package/dist/TokensController.mjs.map +1 -1
- package/dist/constants.cjs +32 -1
- package/dist/constants.cjs.map +1 -1
- package/dist/constants.d.cts +15 -0
- package/dist/constants.d.cts.map +1 -1
- package/dist/constants.d.mts +15 -0
- package/dist/constants.d.mts.map +1 -1
- package/dist/constants.mjs +31 -0
- package/dist/constants.mjs.map +1 -1
- package/dist/index.cjs +4 -1
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +3 -2
- package/dist/index.d.cts.map +1 -1
- package/dist/index.d.mts +3 -2
- package/dist/index.d.mts.map +1 -1
- package/dist/index.mjs +1 -0
- package/dist/index.mjs.map +1 -1
- package/dist/token-prices-service/codefi-v2.d.cts +1 -1
- package/dist/token-prices-service/codefi-v2.d.mts +1 -1
- package/package.json +7 -6
|
@@ -9,7 +9,7 @@ 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 _TokenDetectionController_instances, _TokenDetectionController_intervalId, _TokenDetectionController_selectedAccountId,
|
|
12
|
+
var _TokenDetectionController_instances, _TokenDetectionController_intervalId, _TokenDetectionController_selectedAccountId, _TokenDetectionController_tokenListService, _TokenDetectionController_disabled, _TokenDetectionController_isUnlocked, _TokenDetectionController_isDetectionEnabledFromPreferences, _TokenDetectionController_useTokenDetection, _TokenDetectionController_useExternalServices, _TokenDetectionController_getBalancesInSingleCall, _TokenDetectionController_trackMetaMetricsEvent, _TokenDetectionController_registerEventListeners, _TokenDetectionController_stopPolling, _TokenDetectionController_startPolling, _TokenDetectionController_getCorrectNetworkClientIdByChainId, _TokenDetectionController_restartTokenDetection, _TokenDetectionController_getChainCacheForDetection, _TokenDetectionController_detectTokensUsingRpc, _TokenDetectionController_getSlicesOfTokensToDetect, _TokenDetectionController_getConvertedStaticMainnetTokenList, _TokenDetectionController_buildMusdTokenListToken, _TokenDetectionController_mergeMusdIntoTokenListMap, _TokenDetectionController_applyMusdDefaultToTokensChainsCache, _TokenDetectionController_includeMusdInTokenDetectionSlice, _TokenDetectionController_addDetectedTokens, _TokenDetectionController_getSelectedAccount, _TokenDetectionController_getSelectedAddress;
|
|
13
13
|
function $importDefault(module) {
|
|
14
14
|
if (module?.__esModule) {
|
|
15
15
|
return module.default;
|
|
@@ -20,38 +20,9 @@ import $contractMap from "@metamask/contract-metadata";
|
|
|
20
20
|
const contractMap = $importDefault($contractMap);
|
|
21
21
|
import { ASSET_TYPES, ChainId, ERC20, safelyExecute, isEqualCaseInsensitive, toChecksumHexAddress } from "@metamask/controller-utils";
|
|
22
22
|
import { StaticIntervalPollingController } from "@metamask/polling-controller";
|
|
23
|
-
import
|
|
24
|
-
|
|
25
|
-
import { isTokenDetectionSupportedForNetwork } from "./assetsUtil.mjs";
|
|
26
|
-
import { SUPPORTED_NETWORKS_ACCOUNTS_API_V4 } from "./constants.mjs";
|
|
23
|
+
import { formatIconUrlWithProxy, isTokenDetectionSupportedForNetwork } from "./assetsUtil.mjs";
|
|
24
|
+
import { MUSD_ERC20_ADDRESS_LOWER, MUSD_TOKEN_DETECTION_CHAIN_IDS, MUSD_TOKEN_METADATA_BY_CHAIN, SUPPORTED_NETWORKS_ACCOUNTS_API_V4 } from "./constants.mjs";
|
|
27
25
|
const DEFAULT_INTERVAL = 180000;
|
|
28
|
-
/**
|
|
29
|
-
* Canonical contract address for MetaMask USD (mUSD) — same across every
|
|
30
|
-
* chain we deploy it to.
|
|
31
|
-
*/
|
|
32
|
-
const MUSD_ADDRESS = '0xaca92e438df0b2401ff60da7e4337b687a2435da';
|
|
33
|
-
/**
|
|
34
|
-
* Pre-built Token entry for mUSD — used when seeding default state.
|
|
35
|
-
*/
|
|
36
|
-
const MUSD_TOKEN = {
|
|
37
|
-
address: MUSD_ADDRESS,
|
|
38
|
-
decimals: 6,
|
|
39
|
-
symbol: 'mUSD',
|
|
40
|
-
name: 'MetaMask USD',
|
|
41
|
-
};
|
|
42
|
-
/**
|
|
43
|
-
* Hex chain IDs on which mUSD is deployed and should be added by default.
|
|
44
|
-
* - 0x1 — Ethereum mainnet (1)
|
|
45
|
-
* - 0xe708 — Linea (59144)
|
|
46
|
-
* - 0x8f — Monad mainnet (143)
|
|
47
|
-
* - 0x279f — Monad testnet (10143)
|
|
48
|
-
*/
|
|
49
|
-
const MUSD_SUPPORTED_CHAIN_IDS = new Set([
|
|
50
|
-
'0x1',
|
|
51
|
-
'0xe708',
|
|
52
|
-
'0x8f',
|
|
53
|
-
'0x279f',
|
|
54
|
-
]);
|
|
55
26
|
export const STATIC_MAINNET_TOKEN_LIST = Object.entries(contractMap).reduce((acc, [base, contract]) => {
|
|
56
27
|
const { logo, erc20, erc721, ...tokenMetadata } = contract;
|
|
57
28
|
return {
|
|
@@ -64,20 +35,7 @@ export const STATIC_MAINNET_TOKEN_LIST = Object.entries(contractMap).reduce((acc
|
|
|
64
35
|
},
|
|
65
36
|
};
|
|
66
37
|
}, {});
|
|
67
|
-
|
|
68
|
-
* Function that takes a TokensChainsCache object and maps chainId with TokenListMap.
|
|
69
|
-
*
|
|
70
|
-
* @param tokensChainsCache - TokensChainsCache input object
|
|
71
|
-
* @returns returns the map of chainId with TokenListMap
|
|
72
|
-
*/
|
|
73
|
-
export function mapChainIdWithTokenListMap(tokensChainsCache) {
|
|
74
|
-
return mapValues(tokensChainsCache, (value) => {
|
|
75
|
-
if (isObject(value) && 'data' in value) {
|
|
76
|
-
return get(value, ['data']);
|
|
77
|
-
}
|
|
78
|
-
return value;
|
|
79
|
-
});
|
|
80
|
-
}
|
|
38
|
+
const MUSD_TOKEN_DETECTION_CHAIN_ID_SET = new Set(MUSD_TOKEN_DETECTION_CHAIN_IDS);
|
|
81
39
|
export const controllerName = 'TokenDetectionController';
|
|
82
40
|
const MESSENGER_EXPOSED_METHODS = [
|
|
83
41
|
'addDetectedTokensViaWs',
|
|
@@ -110,6 +68,7 @@ export class TokenDetectionController extends StaticIntervalPollingController()
|
|
|
110
68
|
*
|
|
111
69
|
* @param options - The controller options.
|
|
112
70
|
* @param options.messenger - The controller messenger.
|
|
71
|
+
* @param options.tokenListService - Shared service for fetching the token list per chain.
|
|
113
72
|
* @param options.disabled - If set to true, all network requests are blocked.
|
|
114
73
|
* @param options.interval - Polling interval used to fetch new token rates
|
|
115
74
|
* @param options.getBalancesInSingleCall - Gets the balances of a list of tokens for the given address.
|
|
@@ -117,7 +76,7 @@ export class TokenDetectionController extends StaticIntervalPollingController()
|
|
|
117
76
|
* @param options.useTokenDetection - Feature Switch for using token detection (default: true)
|
|
118
77
|
* @param options.useExternalServices - Feature Switch for using external services (default: false)
|
|
119
78
|
*/
|
|
120
|
-
constructor({ interval = DEFAULT_INTERVAL, disabled = true, getBalancesInSingleCall, trackMetaMetricsEvent, messenger, useTokenDetection = () => true, useExternalServices = () => true, }) {
|
|
79
|
+
constructor({ interval = DEFAULT_INTERVAL, disabled = true, getBalancesInSingleCall, trackMetaMetricsEvent, messenger, tokenListService, useTokenDetection = () => true, useExternalServices = () => true, }) {
|
|
121
80
|
super({
|
|
122
81
|
name: controllerName,
|
|
123
82
|
messenger,
|
|
@@ -127,22 +86,19 @@ export class TokenDetectionController extends StaticIntervalPollingController()
|
|
|
127
86
|
_TokenDetectionController_instances.add(this);
|
|
128
87
|
_TokenDetectionController_intervalId.set(this, void 0);
|
|
129
88
|
_TokenDetectionController_selectedAccountId.set(this, void 0);
|
|
130
|
-
|
|
89
|
+
_TokenDetectionController_tokenListService.set(this, void 0);
|
|
131
90
|
_TokenDetectionController_disabled.set(this, void 0);
|
|
132
91
|
_TokenDetectionController_isUnlocked.set(this, void 0);
|
|
133
92
|
_TokenDetectionController_isDetectionEnabledFromPreferences.set(this, void 0);
|
|
134
93
|
_TokenDetectionController_useTokenDetection.set(this, void 0);
|
|
135
94
|
_TokenDetectionController_useExternalServices.set(this, void 0);
|
|
136
|
-
/** Tracks whether default tokens (mUSD) have been seeded for the current session. */
|
|
137
|
-
_TokenDetectionController_defaultTokensSeeded.set(this, false);
|
|
138
95
|
_TokenDetectionController_getBalancesInSingleCall.set(this, void 0);
|
|
139
96
|
_TokenDetectionController_trackMetaMetricsEvent.set(this, void 0);
|
|
140
97
|
messenger.registerMethodActionHandlers(this, MESSENGER_EXPOSED_METHODS);
|
|
141
98
|
__classPrivateFieldSet(this, _TokenDetectionController_disabled, disabled, "f");
|
|
142
99
|
this.setIntervalLength(interval);
|
|
143
100
|
__classPrivateFieldSet(this, _TokenDetectionController_selectedAccountId, __classPrivateFieldGet(this, _TokenDetectionController_instances, "m", _TokenDetectionController_getSelectedAccount).call(this).id, "f");
|
|
144
|
-
|
|
145
|
-
__classPrivateFieldSet(this, _TokenDetectionController_tokensChainsCache, tokensChainsCache, "f");
|
|
101
|
+
__classPrivateFieldSet(this, _TokenDetectionController_tokenListService, tokenListService, "f");
|
|
146
102
|
const { useTokenDetection: defaultUseTokenDetection } = this.messenger.call('PreferencesController:getState');
|
|
147
103
|
__classPrivateFieldSet(this, _TokenDetectionController_isDetectionEnabledFromPreferences, defaultUseTokenDetection, "f");
|
|
148
104
|
__classPrivateFieldSet(this, _TokenDetectionController_getBalancesInSingleCall, getBalancesInSingleCall, "f");
|
|
@@ -178,9 +134,6 @@ export class TokenDetectionController extends StaticIntervalPollingController()
|
|
|
178
134
|
*/
|
|
179
135
|
async start() {
|
|
180
136
|
this.enable();
|
|
181
|
-
// Seed mUSD as a default token via TokensController:addTokens. Runs
|
|
182
|
-
// once per session; idempotent because addTokens dedupes on address.
|
|
183
|
-
await __classPrivateFieldGet(this, _TokenDetectionController_instances, "m", _TokenDetectionController_seedDefaultTokens).call(this);
|
|
184
137
|
await __classPrivateFieldGet(this, _TokenDetectionController_instances, "m", _TokenDetectionController_startPolling).call(this);
|
|
185
138
|
}
|
|
186
139
|
/**
|
|
@@ -200,7 +153,7 @@ export class TokenDetectionController extends StaticIntervalPollingController()
|
|
|
200
153
|
});
|
|
201
154
|
}
|
|
202
155
|
/**
|
|
203
|
-
* For each token in the token list provided by the
|
|
156
|
+
* For each token in the token list provided by the TokenListService, checks the token's balance for the selected account address on the active network.
|
|
204
157
|
* On mainnet, if token detection is disabled in preferences, ERC20 token auto detection will be triggered for each contract address in the legacy token list from the @metamask/contract-metadata repo.
|
|
205
158
|
*
|
|
206
159
|
* @param options - Options for token detection.
|
|
@@ -257,18 +210,28 @@ export class TokenDetectionController extends StaticIntervalPollingController()
|
|
|
257
210
|
if (!__classPrivateFieldGet(this, _TokenDetectionController_useExternalServices, "f").call(this)) {
|
|
258
211
|
return;
|
|
259
212
|
}
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
213
|
+
let tokenListMap;
|
|
214
|
+
try {
|
|
215
|
+
tokenListMap = await __classPrivateFieldGet(this, _TokenDetectionController_tokenListService, "f").fetchTokensByChainId(chainId);
|
|
216
|
+
}
|
|
217
|
+
catch {
|
|
218
|
+
// These methods return void; there is no token array to return.
|
|
219
|
+
// Gracefully exit so the caller is unaffected — the next polling cycle
|
|
220
|
+
// will retry the fetch.
|
|
221
|
+
return;
|
|
222
|
+
}
|
|
223
|
+
const chainCache = __classPrivateFieldGet(this, _TokenDetectionController_instances, "m", _TokenDetectionController_applyMusdDefaultToTokensChainsCache).call(this, chainId, {
|
|
224
|
+
[chainId]: { data: tokenListMap, timestamp: Date.now() },
|
|
225
|
+
});
|
|
226
|
+
const effectiveSlice = __classPrivateFieldGet(this, _TokenDetectionController_instances, "m", _TokenDetectionController_includeMusdInTokenDetectionSlice).call(this, tokensSlice, chainId);
|
|
264
227
|
const tokensWithBalance = [];
|
|
265
228
|
const eventTokensDetails = [];
|
|
266
|
-
for (const tokenAddress of
|
|
229
|
+
for (const tokenAddress of effectiveSlice) {
|
|
267
230
|
// Normalize addresses explicitly (don't assume input format)
|
|
268
231
|
const lowercaseTokenAddress = tokenAddress.toLowerCase();
|
|
269
232
|
const checksummedTokenAddress = toChecksumHexAddress(tokenAddress);
|
|
270
233
|
// Check map of validated tokens (cache keys are lowercase)
|
|
271
|
-
const tokenData =
|
|
234
|
+
const tokenData = chainCache[chainId]?.data?.[lowercaseTokenAddress];
|
|
272
235
|
if (!tokenData) {
|
|
273
236
|
continue;
|
|
274
237
|
}
|
|
@@ -324,18 +287,28 @@ export class TokenDetectionController extends StaticIntervalPollingController()
|
|
|
324
287
|
if (!__classPrivateFieldGet(this, _TokenDetectionController_useExternalServices, "f").call(this)) {
|
|
325
288
|
return;
|
|
326
289
|
}
|
|
327
|
-
|
|
328
|
-
|
|
329
|
-
|
|
330
|
-
|
|
290
|
+
let tokenListMap;
|
|
291
|
+
try {
|
|
292
|
+
tokenListMap = await __classPrivateFieldGet(this, _TokenDetectionController_tokenListService, "f").fetchTokensByChainId(chainId);
|
|
293
|
+
}
|
|
294
|
+
catch {
|
|
295
|
+
// These methods return void; there is no token array to return.
|
|
296
|
+
// Gracefully exit so the caller is unaffected — the next polling cycle
|
|
297
|
+
// will retry the fetch.
|
|
298
|
+
return;
|
|
299
|
+
}
|
|
300
|
+
const chainCache = __classPrivateFieldGet(this, _TokenDetectionController_instances, "m", _TokenDetectionController_applyMusdDefaultToTokensChainsCache).call(this, chainId, {
|
|
301
|
+
[chainId]: { data: tokenListMap, timestamp: Date.now() },
|
|
302
|
+
});
|
|
331
303
|
const selectedAddress = __classPrivateFieldGet(this, _TokenDetectionController_instances, "m", _TokenDetectionController_getSelectedAddress).call(this);
|
|
332
304
|
// Get current token states to filter out already tracked/ignored tokens
|
|
333
305
|
const { allTokens, allIgnoredTokens } = this.messenger.call('TokensController:getState');
|
|
334
306
|
const existingTokenAddresses = (allTokens[chainId]?.[selectedAddress] ?? []).map((token) => token.address.toLowerCase());
|
|
335
307
|
const ignoredTokenAddresses = (allIgnoredTokens[chainId]?.[selectedAddress] ?? []).map((address) => address.toLowerCase());
|
|
308
|
+
const effectiveSlice = __classPrivateFieldGet(this, _TokenDetectionController_instances, "m", _TokenDetectionController_includeMusdInTokenDetectionSlice).call(this, tokensSlice, chainId);
|
|
336
309
|
const tokensWithBalance = [];
|
|
337
310
|
const eventTokensDetails = [];
|
|
338
|
-
for (const tokenAddress of
|
|
311
|
+
for (const tokenAddress of effectiveSlice) {
|
|
339
312
|
const lowercaseTokenAddress = tokenAddress.toLowerCase();
|
|
340
313
|
const checksummedTokenAddress = toChecksumHexAddress(tokenAddress);
|
|
341
314
|
// Skip tokens already in allTokens
|
|
@@ -347,7 +320,7 @@ export class TokenDetectionController extends StaticIntervalPollingController()
|
|
|
347
320
|
continue;
|
|
348
321
|
}
|
|
349
322
|
// Check map of validated tokens (cache keys are lowercase)
|
|
350
|
-
const tokenData =
|
|
323
|
+
const tokenData = chainCache[chainId]?.data?.[lowercaseTokenAddress];
|
|
351
324
|
if (!tokenData) {
|
|
352
325
|
continue;
|
|
353
326
|
}
|
|
@@ -380,7 +353,7 @@ export class TokenDetectionController extends StaticIntervalPollingController()
|
|
|
380
353
|
}
|
|
381
354
|
}
|
|
382
355
|
}
|
|
383
|
-
_TokenDetectionController_intervalId = new WeakMap(), _TokenDetectionController_selectedAccountId = new WeakMap(),
|
|
356
|
+
_TokenDetectionController_intervalId = new WeakMap(), _TokenDetectionController_selectedAccountId = new WeakMap(), _TokenDetectionController_tokenListService = new WeakMap(), _TokenDetectionController_disabled = new WeakMap(), _TokenDetectionController_isUnlocked = new WeakMap(), _TokenDetectionController_isDetectionEnabledFromPreferences = new WeakMap(), _TokenDetectionController_useTokenDetection = new WeakMap(), _TokenDetectionController_useExternalServices = new WeakMap(), _TokenDetectionController_getBalancesInSingleCall = new WeakMap(), _TokenDetectionController_trackMetaMetricsEvent = new WeakMap(), _TokenDetectionController_instances = new WeakSet(), _TokenDetectionController_registerEventListeners = function _TokenDetectionController_registerEventListeners() {
|
|
384
357
|
this.messenger.subscribe('KeyringController:unlock', () => {
|
|
385
358
|
__classPrivateFieldSet(this, _TokenDetectionController_isUnlocked, true, "f");
|
|
386
359
|
__classPrivateFieldGet(this, _TokenDetectionController_instances, "m", _TokenDetectionController_restartTokenDetection).call(this).catch(() => {
|
|
@@ -391,14 +364,6 @@ _TokenDetectionController_intervalId = new WeakMap(), _TokenDetectionController_
|
|
|
391
364
|
__classPrivateFieldSet(this, _TokenDetectionController_isUnlocked, false, "f");
|
|
392
365
|
__classPrivateFieldGet(this, _TokenDetectionController_instances, "m", _TokenDetectionController_stopPolling).call(this);
|
|
393
366
|
});
|
|
394
|
-
this.messenger.subscribe('TokenListController:stateChange', ({ tokensChainsCache }) => {
|
|
395
|
-
const isEqualValues = __classPrivateFieldGet(this, _TokenDetectionController_instances, "m", _TokenDetectionController_compareTokensChainsCache).call(this, tokensChainsCache, __classPrivateFieldGet(this, _TokenDetectionController_tokensChainsCache, "f"));
|
|
396
|
-
if (!isEqualValues) {
|
|
397
|
-
__classPrivateFieldGet(this, _TokenDetectionController_instances, "m", _TokenDetectionController_restartTokenDetection).call(this).catch(() => {
|
|
398
|
-
// Silently handle token detection errors
|
|
399
|
-
});
|
|
400
|
-
}
|
|
401
|
-
});
|
|
402
367
|
this.messenger.subscribe('PreferencesController:stateChange', ({ useTokenDetection }) => {
|
|
403
368
|
const selectedAccount = __classPrivateFieldGet(this, _TokenDetectionController_instances, "m", _TokenDetectionController_getSelectedAccount).call(this);
|
|
404
369
|
const isDetectionChangedFromPreferences = __classPrivateFieldGet(this, _TokenDetectionController_isDetectionEnabledFromPreferences, "f") !== useTokenDetection;
|
|
@@ -417,13 +382,6 @@ _TokenDetectionController_intervalId = new WeakMap(), _TokenDetectionController_
|
|
|
417
382
|
const isSelectedAccountIdChanged = __classPrivateFieldGet(this, _TokenDetectionController_selectedAccountId, "f") !== selectedAccount.id;
|
|
418
383
|
if (isSelectedAccountIdChanged) {
|
|
419
384
|
__classPrivateFieldSet(this, _TokenDetectionController_selectedAccountId, selectedAccount.id, "f");
|
|
420
|
-
// Re-seed mUSD for the newly selected account. addTokens only adds
|
|
421
|
-
// tokens for the currently selected account, so we need to re-run
|
|
422
|
-
// it whenever the active account changes.
|
|
423
|
-
__classPrivateFieldSet(this, _TokenDetectionController_defaultTokensSeeded, false, "f");
|
|
424
|
-
__classPrivateFieldGet(this, _TokenDetectionController_instances, "m", _TokenDetectionController_seedDefaultTokens).call(this).catch(() => {
|
|
425
|
-
// Silently handle default-token seeding errors
|
|
426
|
-
});
|
|
427
385
|
__classPrivateFieldGet(this, _TokenDetectionController_instances, "m", _TokenDetectionController_restartTokenDetection).call(this, {
|
|
428
386
|
selectedAddress: selectedAccount.address,
|
|
429
387
|
chainIds,
|
|
@@ -439,19 +397,6 @@ _TokenDetectionController_intervalId = new WeakMap(), _TokenDetectionController_
|
|
|
439
397
|
// Silently handle token detection errors
|
|
440
398
|
});
|
|
441
399
|
});
|
|
442
|
-
// Re-seed mUSD whenever a network is added. Covers the case where the
|
|
443
|
-
// user adds a supported chain (e.g. Monad testnet) after the controller
|
|
444
|
-
// has already started — the chain wasn't configured at start() time so
|
|
445
|
-
// findNetworkClientIdByChainId would have skipped it.
|
|
446
|
-
this.messenger.subscribe('NetworkController:networkAdded', ({ chainId }) => {
|
|
447
|
-
if (!MUSD_SUPPORTED_CHAIN_IDS.has(chainId)) {
|
|
448
|
-
return;
|
|
449
|
-
}
|
|
450
|
-
__classPrivateFieldSet(this, _TokenDetectionController_defaultTokensSeeded, false, "f");
|
|
451
|
-
__classPrivateFieldGet(this, _TokenDetectionController_instances, "m", _TokenDetectionController_seedDefaultTokens).call(this).catch(() => {
|
|
452
|
-
// Silently handle default-token seeding errors
|
|
453
|
-
});
|
|
454
|
-
});
|
|
455
400
|
}, _TokenDetectionController_stopPolling = function _TokenDetectionController_stopPolling() {
|
|
456
401
|
if (__classPrivateFieldGet(this, _TokenDetectionController_intervalId, "f")) {
|
|
457
402
|
clearInterval(__classPrivateFieldGet(this, _TokenDetectionController_intervalId, "f"));
|
|
@@ -471,11 +416,6 @@ async function _TokenDetectionController_startPolling() {
|
|
|
471
416
|
__classPrivateFieldSet(this, _TokenDetectionController_intervalId, setInterval(async () => {
|
|
472
417
|
await this.detectTokens();
|
|
473
418
|
}, this.getIntervalLength()), "f");
|
|
474
|
-
}, _TokenDetectionController_compareTokensChainsCache = function _TokenDetectionController_compareTokensChainsCache(tokensChainsCache, previousTokensChainsCache) {
|
|
475
|
-
const cleanPreviousTokensChainsCache = mapChainIdWithTokenListMap(previousTokensChainsCache);
|
|
476
|
-
const cleanTokensChainsCache = mapChainIdWithTokenListMap(tokensChainsCache);
|
|
477
|
-
const isEqualValues = isEqual(cleanTokensChainsCache, cleanPreviousTokensChainsCache);
|
|
478
|
-
return isEqualValues;
|
|
479
419
|
}, _TokenDetectionController_getCorrectNetworkClientIdByChainId = function _TokenDetectionController_getCorrectNetworkClientIdByChainId(chainIds) {
|
|
480
420
|
const { networkConfigurationsByChainId, selectedNetworkClientId } = this.messenger.call('NetworkController:getState');
|
|
481
421
|
if (!chainIds) {
|
|
@@ -510,30 +450,41 @@ async function _TokenDetectionController_restartTokenDetection({ selectedAddress
|
|
|
510
450
|
selectedAddress,
|
|
511
451
|
});
|
|
512
452
|
this.setIntervalLength(DEFAULT_INTERVAL);
|
|
513
|
-
},
|
|
453
|
+
}, _TokenDetectionController_getChainCacheForDetection =
|
|
454
|
+
/**
|
|
455
|
+
* Returns the token cache for `chainId` if detection should proceed, or `null` if it
|
|
456
|
+
* should be skipped. Each call fetches a fresh snapshot from `TokenListService` (which
|
|
457
|
+
* may serve from its in-memory cache) so concurrent calls for different chains never
|
|
458
|
+
* overwrite each other's data.
|
|
459
|
+
*
|
|
460
|
+
* @param chainId - The chain ID to build a detection cache for.
|
|
461
|
+
* @returns A `TokensChainsCache` scoped to `chainId`, or `null` when detection should be skipped.
|
|
462
|
+
*/
|
|
463
|
+
async function _TokenDetectionController_getChainCacheForDetection(chainId) {
|
|
514
464
|
if (!isTokenDetectionSupportedForNetwork(chainId)) {
|
|
515
|
-
return
|
|
465
|
+
return null;
|
|
516
466
|
}
|
|
517
467
|
if (!__classPrivateFieldGet(this, _TokenDetectionController_isDetectionEnabledFromPreferences, "f") &&
|
|
518
468
|
chainId !== ChainId.mainnet) {
|
|
519
|
-
return
|
|
469
|
+
return null;
|
|
520
470
|
}
|
|
521
471
|
const isMainnetDetectionInactive = !__classPrivateFieldGet(this, _TokenDetectionController_isDetectionEnabledFromPreferences, "f") && chainId === ChainId.mainnet;
|
|
522
472
|
if (isMainnetDetectionInactive) {
|
|
523
|
-
|
|
524
|
-
}
|
|
525
|
-
else {
|
|
526
|
-
const { tokensChainsCache } = this.messenger.call('TokenListController:getState');
|
|
527
|
-
__classPrivateFieldSet(this, _TokenDetectionController_tokensChainsCache, tokensChainsCache ?? {}, "f");
|
|
473
|
+
return __classPrivateFieldGet(this, _TokenDetectionController_instances, "m", _TokenDetectionController_getConvertedStaticMainnetTokenList).call(this);
|
|
528
474
|
}
|
|
529
|
-
|
|
475
|
+
const tokenListMap = await __classPrivateFieldGet(this, _TokenDetectionController_tokenListService, "f").fetchTokensByChainId(chainId);
|
|
476
|
+
return __classPrivateFieldGet(this, _TokenDetectionController_instances, "m", _TokenDetectionController_applyMusdDefaultToTokensChainsCache).call(this, chainId, {
|
|
477
|
+
[chainId]: { data: tokenListMap, timestamp: Date.now() },
|
|
478
|
+
});
|
|
530
479
|
}, _TokenDetectionController_detectTokensUsingRpc = async function _TokenDetectionController_detectTokensUsingRpc(chainsToDetectUsingRpc, addressToDetect) {
|
|
531
480
|
for (const { chainId, networkClientId } of chainsToDetectUsingRpc) {
|
|
532
|
-
|
|
481
|
+
const chainCache = await __classPrivateFieldGet(this, _TokenDetectionController_instances, "m", _TokenDetectionController_getChainCacheForDetection).call(this, chainId);
|
|
482
|
+
if (!chainCache) {
|
|
533
483
|
continue;
|
|
534
484
|
}
|
|
535
485
|
const tokenCandidateSlices = __classPrivateFieldGet(this, _TokenDetectionController_instances, "m", _TokenDetectionController_getSlicesOfTokensToDetect).call(this, {
|
|
536
486
|
chainId,
|
|
487
|
+
chainCache,
|
|
537
488
|
selectedAddress: addressToDetect,
|
|
538
489
|
});
|
|
539
490
|
const tokenDetectionPromises = tokenCandidateSlices.map((tokensSlice) => __classPrivateFieldGet(this, _TokenDetectionController_instances, "m", _TokenDetectionController_addDetectedTokens).call(this, {
|
|
@@ -541,10 +492,11 @@ async function _TokenDetectionController_restartTokenDetection({ selectedAddress
|
|
|
541
492
|
selectedAddress: addressToDetect,
|
|
542
493
|
networkClientId,
|
|
543
494
|
chainId,
|
|
495
|
+
chainCache,
|
|
544
496
|
}));
|
|
545
497
|
await Promise.all(tokenDetectionPromises);
|
|
546
498
|
}
|
|
547
|
-
}, _TokenDetectionController_getSlicesOfTokensToDetect = function _TokenDetectionController_getSlicesOfTokensToDetect({ chainId, selectedAddress, }) {
|
|
499
|
+
}, _TokenDetectionController_getSlicesOfTokensToDetect = function _TokenDetectionController_getSlicesOfTokensToDetect({ chainId, chainCache, selectedAddress, }) {
|
|
548
500
|
const { allTokens, allDetectedTokens, allIgnoredTokens } = this.messenger.call('TokensController:getState');
|
|
549
501
|
const [tokensAddresses, detectedTokensAddresses, ignoredTokensAddresses] = [
|
|
550
502
|
allTokens,
|
|
@@ -552,7 +504,7 @@ async function _TokenDetectionController_restartTokenDetection({ selectedAddress
|
|
|
552
504
|
allIgnoredTokens,
|
|
553
505
|
].map((tokens) => (tokens[chainId]?.[selectedAddress] ?? []).map((value) => typeof value === 'string' ? value : value.address));
|
|
554
506
|
const tokensToDetect = [];
|
|
555
|
-
for (const tokenAddress of Object.keys(
|
|
507
|
+
for (const tokenAddress of Object.keys(chainCache[chainId]?.data ?? {})) {
|
|
556
508
|
if ([
|
|
557
509
|
tokensAddresses,
|
|
558
510
|
detectedTokensAddresses,
|
|
@@ -578,50 +530,66 @@ async function _TokenDetectionController_restartTokenDetection({ selectedAddress
|
|
|
578
530
|
iconUrl: value?.iconUrl,
|
|
579
531
|
},
|
|
580
532
|
}), {});
|
|
533
|
+
const dataWithMusd = __classPrivateFieldGet(this, _TokenDetectionController_instances, "m", _TokenDetectionController_mergeMusdIntoTokenListMap).call(this, ChainId.mainnet, data);
|
|
581
534
|
return {
|
|
582
535
|
'0x1': {
|
|
583
|
-
data,
|
|
536
|
+
data: dataWithMusd,
|
|
584
537
|
timestamp: 0,
|
|
585
538
|
},
|
|
586
539
|
};
|
|
587
|
-
},
|
|
588
|
-
|
|
589
|
-
|
|
590
|
-
|
|
591
|
-
|
|
592
|
-
|
|
593
|
-
|
|
594
|
-
|
|
595
|
-
|
|
596
|
-
|
|
597
|
-
|
|
598
|
-
|
|
599
|
-
|
|
600
|
-
|
|
601
|
-
|
|
540
|
+
}, _TokenDetectionController_buildMusdTokenListToken = function _TokenDetectionController_buildMusdTokenListToken(chainId) {
|
|
541
|
+
const meta = MUSD_TOKEN_METADATA_BY_CHAIN[chainId];
|
|
542
|
+
return {
|
|
543
|
+
address: MUSD_ERC20_ADDRESS_LOWER,
|
|
544
|
+
name: meta.name,
|
|
545
|
+
symbol: meta.symbol,
|
|
546
|
+
decimals: meta.decimals,
|
|
547
|
+
aggregators: [...meta.aggregators],
|
|
548
|
+
iconUrl: formatIconUrlWithProxy({
|
|
549
|
+
chainId,
|
|
550
|
+
tokenAddress: MUSD_ERC20_ADDRESS_LOWER,
|
|
551
|
+
}),
|
|
552
|
+
occurrences: 999,
|
|
553
|
+
};
|
|
554
|
+
}, _TokenDetectionController_mergeMusdIntoTokenListMap = function _TokenDetectionController_mergeMusdIntoTokenListMap(chainId, data) {
|
|
555
|
+
return {
|
|
556
|
+
...data,
|
|
557
|
+
[MUSD_ERC20_ADDRESS_LOWER]: __classPrivateFieldGet(this, _TokenDetectionController_instances, "m", _TokenDetectionController_buildMusdTokenListToken).call(this, chainId),
|
|
558
|
+
};
|
|
559
|
+
}, _TokenDetectionController_applyMusdDefaultToTokensChainsCache = function _TokenDetectionController_applyMusdDefaultToTokensChainsCache(chainId, cache) {
|
|
560
|
+
if (!MUSD_TOKEN_DETECTION_CHAIN_ID_SET.has(chainId)) {
|
|
561
|
+
return cache;
|
|
602
562
|
}
|
|
603
|
-
|
|
604
|
-
|
|
605
|
-
|
|
606
|
-
|
|
607
|
-
|
|
608
|
-
|
|
609
|
-
|
|
610
|
-
|
|
611
|
-
|
|
612
|
-
|
|
613
|
-
|
|
614
|
-
// Silently handle per-chain seeding errors so one failure does not
|
|
615
|
-
// block seeding on the remaining supported chains.
|
|
616
|
-
}
|
|
563
|
+
const existing = cache[chainId];
|
|
564
|
+
return {
|
|
565
|
+
...cache,
|
|
566
|
+
[chainId]: {
|
|
567
|
+
data: __classPrivateFieldGet(this, _TokenDetectionController_instances, "m", _TokenDetectionController_mergeMusdIntoTokenListMap).call(this, chainId, existing?.data ?? {}),
|
|
568
|
+
timestamp: existing?.timestamp ?? 0,
|
|
569
|
+
},
|
|
570
|
+
};
|
|
571
|
+
}, _TokenDetectionController_includeMusdInTokenDetectionSlice = function _TokenDetectionController_includeMusdInTokenDetectionSlice(tokensSlice, chainId) {
|
|
572
|
+
if (!MUSD_TOKEN_DETECTION_CHAIN_ID_SET.has(chainId)) {
|
|
573
|
+
return tokensSlice;
|
|
617
574
|
}
|
|
618
|
-
|
|
575
|
+
if (tokensSlice.some((a) => isEqualCaseInsensitive(a, MUSD_ERC20_ADDRESS_LOWER))) {
|
|
576
|
+
return tokensSlice;
|
|
577
|
+
}
|
|
578
|
+
return [...tokensSlice, MUSD_ERC20_ADDRESS_LOWER];
|
|
579
|
+
}, _TokenDetectionController_addDetectedTokens = async function _TokenDetectionController_addDetectedTokens({ tokensSlice, selectedAddress, networkClientId, chainId, chainCache, }) {
|
|
619
580
|
await safelyExecute(async () => {
|
|
620
581
|
const balances = await __classPrivateFieldGet(this, _TokenDetectionController_getBalancesInSingleCall, "f").call(this, selectedAddress, tokensSlice, networkClientId);
|
|
582
|
+
const chainData = chainCache[chainId]?.data ?? {};
|
|
621
583
|
const tokensWithBalance = [];
|
|
622
584
|
const eventTokensDetails = [];
|
|
623
585
|
for (const nonZeroTokenAddress of Object.keys(balances)) {
|
|
624
|
-
|
|
586
|
+
// chainData keys are lowercase (normalised by buildTokenListMap);
|
|
587
|
+
// balance keys are checksummed, so normalise before lookup.
|
|
588
|
+
const tokenListEntry = chainData[nonZeroTokenAddress.toLowerCase()];
|
|
589
|
+
if (!tokenListEntry) {
|
|
590
|
+
continue;
|
|
591
|
+
}
|
|
592
|
+
const { decimals, symbol, aggregators, iconUrl, name, rwaData } = tokenListEntry;
|
|
625
593
|
eventTokensDetails.push(`${symbol} - ${nonZeroTokenAddress}`);
|
|
626
594
|
tokensWithBalance.push({
|
|
627
595
|
address: nonZeroTokenAddress,
|
|
@@ -634,6 +602,30 @@ async function _TokenDetectionController_seedDefaultTokens() {
|
|
|
634
602
|
...(rwaData && { rwaData }),
|
|
635
603
|
});
|
|
636
604
|
}
|
|
605
|
+
// mUSD is always in the chain token cache on supported networks, but
|
|
606
|
+
// getBalancesInSingleCall omits zero balances; still add mUSD so the wallet
|
|
607
|
+
// shows the asset (balance updates via the usual balance pipeline).
|
|
608
|
+
if (MUSD_TOKEN_DETECTION_CHAIN_ID_SET.has(chainId)) {
|
|
609
|
+
const musdInSlice = tokensSlice.some((addr) => isEqualCaseInsensitive(addr, MUSD_ERC20_ADDRESS_LOWER));
|
|
610
|
+
const musdHasNonZeroFromRpc = Object.keys(balances).some((addr) => isEqualCaseInsensitive(addr, MUSD_ERC20_ADDRESS_LOWER));
|
|
611
|
+
if (musdInSlice && !musdHasNonZeroFromRpc) {
|
|
612
|
+
const musdListToken = Object.entries(chainData).find(([key]) => isEqualCaseInsensitive(key, MUSD_ERC20_ADDRESS_LOWER))?.[1];
|
|
613
|
+
if (musdListToken) {
|
|
614
|
+
const { decimals, symbol, aggregators, iconUrl, name, rwaData } = musdListToken;
|
|
615
|
+
eventTokensDetails.push(`${symbol} - ${MUSD_ERC20_ADDRESS_LOWER}`);
|
|
616
|
+
tokensWithBalance.push({
|
|
617
|
+
address: MUSD_ERC20_ADDRESS_LOWER,
|
|
618
|
+
decimals,
|
|
619
|
+
symbol,
|
|
620
|
+
aggregators,
|
|
621
|
+
image: iconUrl,
|
|
622
|
+
isERC721: false,
|
|
623
|
+
name,
|
|
624
|
+
...(rwaData && { rwaData }),
|
|
625
|
+
});
|
|
626
|
+
}
|
|
627
|
+
}
|
|
628
|
+
}
|
|
637
629
|
if (tokensWithBalance.length) {
|
|
638
630
|
__classPrivateFieldGet(this, _TokenDetectionController_trackMetaMetricsEvent, "f").call(this, {
|
|
639
631
|
event: 'Token Detected',
|