@metamask-previews/assets-controllers 93.1.0-preview-aabe1c65 → 93.1.0-preview-c92c27f1
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 +9 -0
- package/dist/TokenBalancesController.cjs +337 -377
- package/dist/TokenBalancesController.cjs.map +1 -1
- package/dist/TokenBalancesController.d.cts +17 -38
- package/dist/TokenBalancesController.d.cts.map +1 -1
- package/dist/TokenBalancesController.d.mts +17 -38
- package/dist/TokenBalancesController.d.mts.map +1 -1
- package/dist/TokenBalancesController.mjs +337 -377
- package/dist/TokenBalancesController.mjs.map +1 -1
- package/dist/TokenDetectionController.cjs +44 -205
- package/dist/TokenDetectionController.cjs.map +1 -1
- package/dist/TokenDetectionController.d.cts +12 -10
- package/dist/TokenDetectionController.d.cts.map +1 -1
- package/dist/TokenDetectionController.d.mts +12 -10
- package/dist/TokenDetectionController.d.mts.map +1 -1
- package/dist/TokenDetectionController.mjs +45 -206
- package/dist/TokenDetectionController.mjs.map +1 -1
- package/package.json +1 -1
|
@@ -10,7 +10,7 @@ var __classPrivateFieldSet = (this && this.__classPrivateFieldSet) || function (
|
|
|
10
10
|
if (typeof state === "function" ? receiver !== state || !f : !state.has(receiver)) throw new TypeError("Cannot write private member to an object whose class did not declare it");
|
|
11
11
|
return (kind === "a" ? f.call(receiver, value) : f ? f.value = value : state.set(receiver, value)), value;
|
|
12
12
|
};
|
|
13
|
-
var _TokenBalancesController_instances, _TokenBalancesController_platform, _TokenBalancesController_queryAllAccounts, _TokenBalancesController_accountsApiChainIds, _TokenBalancesController_balanceFetchers, _TokenBalancesController_allTokens, _TokenBalancesController_detectedTokens, _TokenBalancesController_allIgnoredTokens, _TokenBalancesController_defaultInterval, _TokenBalancesController_websocketActivePollingInterval, _TokenBalancesController_chainPollingConfig, _TokenBalancesController_intervalPollingTimers, _TokenBalancesController_isControllerPollingActive, _TokenBalancesController_requestedChainIds, _TokenBalancesController_statusChangeDebouncer, _TokenBalancesController_normalizeAccountAddresses, _TokenBalancesController_chainIdsWithTokens, _TokenBalancesController_getProvider, _TokenBalancesController_getNetworkClient, _TokenBalancesController_createAccountsApiFetcher, _TokenBalancesController_startIntervalGroupPolling, _TokenBalancesController_startPollingForInterval, _TokenBalancesController_setPollingTimer, _TokenBalancesController_isTokenTracked, _TokenBalancesController_onTokensChanged, _TokenBalancesController_onNetworkChanged, _TokenBalancesController_onAccountRemoved, _TokenBalancesController_onAccountChanged, _TokenBalancesController_prepareBalanceUpdates, _TokenBalancesController_onAccountActivityBalanceUpdate, _TokenBalancesController_onAccountActivityStatusChanged, _TokenBalancesController_processAccumulatedStatusChanges;
|
|
13
|
+
var _TokenBalancesController_instances, _TokenBalancesController_platform, _TokenBalancesController_queryAllAccounts, _TokenBalancesController_accountsApiChainIds, _TokenBalancesController_balanceFetchers, _TokenBalancesController_allTokens, _TokenBalancesController_detectedTokens, _TokenBalancesController_allIgnoredTokens, _TokenBalancesController_defaultInterval, _TokenBalancesController_websocketActivePollingInterval, _TokenBalancesController_chainPollingConfig, _TokenBalancesController_intervalPollingTimers, _TokenBalancesController_isControllerPollingActive, _TokenBalancesController_isUnlocked, _TokenBalancesController_requestedChainIds, _TokenBalancesController_statusChangeDebouncer, _TokenBalancesController_subscribeToControllers, _TokenBalancesController_registerActions, _TokenBalancesController_normalizeAccountAddresses, _TokenBalancesController_chainIdsWithTokens, _TokenBalancesController_getProvider, _TokenBalancesController_getNetworkClient, _TokenBalancesController_createAccountsApiFetcher, _TokenBalancesController_startIntervalGroupPolling, _TokenBalancesController_startPollingForInterval, _TokenBalancesController_setPollingTimer, _TokenBalancesController_stopAllPolling, _TokenBalancesController_getTargetChains, _TokenBalancesController_getAccountsAndJwt, _TokenBalancesController_fetchAllBalances, _TokenBalancesController_filterByTokenAddresses, _TokenBalancesController_getAccountsToProcess, _TokenBalancesController_applyTokenBalancesToState, _TokenBalancesController_buildNativeBalanceUpdates, _TokenBalancesController_buildStakedBalanceUpdates, _TokenBalancesController_importUntrackedTokens, _TokenBalancesController_isTokenTracked, _TokenBalancesController_onTokensChanged, _TokenBalancesController_onNetworkChanged, _TokenBalancesController_onAccountRemoved, _TokenBalancesController_onAccountChanged, _TokenBalancesController_prepareBalanceUpdates, _TokenBalancesController_onAccountActivityBalanceUpdate, _TokenBalancesController_onAccountActivityStatusChanged, _TokenBalancesController_processAccumulatedStatusChanges;
|
|
14
14
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
15
15
|
exports.TokenBalancesController = exports.parseAssetType = exports.caipChainIdToHex = void 0;
|
|
16
16
|
const providers_1 = require("@ethersproject/providers");
|
|
@@ -33,19 +33,14 @@ const metadata = {
|
|
|
33
33
|
usedInUi: true,
|
|
34
34
|
},
|
|
35
35
|
};
|
|
36
|
-
// endregion
|
|
37
|
-
// ────────────────────────────────────────────────────────────────────────────
|
|
38
|
-
// region: Helper utilities
|
|
39
36
|
const draft = (base, fn) => (0, immer_1.produce)(base, fn);
|
|
40
37
|
const ZERO_ADDRESS = '0x0000000000000000000000000000000000000000';
|
|
41
38
|
const checksum = (addr) => (0, controller_utils_1.toChecksumHexAddress)(addr);
|
|
42
39
|
/**
|
|
43
|
-
* Convert CAIP chain ID or hex chain ID to hex chain ID
|
|
44
|
-
* Handles both CAIP-2 format (e.g., "eip155:1") and hex format (e.g., "0x1")
|
|
40
|
+
* Convert CAIP chain ID or hex chain ID to hex chain ID.
|
|
45
41
|
*
|
|
46
|
-
* @param chainId - CAIP chain ID
|
|
47
|
-
* @returns Hex chain ID
|
|
48
|
-
* @throws {Error} If chainId is neither a valid CAIP-2 chain ID nor a hex string
|
|
42
|
+
* @param chainId - CAIP chain ID or hex chain ID.
|
|
43
|
+
* @returns Hex chain ID.
|
|
49
44
|
*/
|
|
50
45
|
const caipChainIdToHex = (chainId) => {
|
|
51
46
|
if ((0, utils_1.isStrictHexString)(chainId)) {
|
|
@@ -58,31 +53,27 @@ const caipChainIdToHex = (chainId) => {
|
|
|
58
53
|
};
|
|
59
54
|
exports.caipChainIdToHex = caipChainIdToHex;
|
|
60
55
|
/**
|
|
61
|
-
* Extract token address from asset type
|
|
62
|
-
* Returns tuple of [tokenAddress, isNativeToken] or null if invalid
|
|
56
|
+
* Extract token address from asset type.
|
|
63
57
|
*
|
|
64
|
-
* @param assetType - Asset type string
|
|
65
|
-
* @returns Tuple of [tokenAddress, isNativeToken] or null if invalid
|
|
58
|
+
* @param assetType - Asset type string.
|
|
59
|
+
* @returns Tuple of [tokenAddress, isNativeToken] or null if invalid.
|
|
66
60
|
*/
|
|
67
61
|
const parseAssetType = (assetType) => {
|
|
68
62
|
if (!(0, utils_1.isCaipAssetType)(assetType)) {
|
|
69
63
|
return null;
|
|
70
64
|
}
|
|
71
65
|
const parsed = (0, utils_1.parseCaipAssetType)(assetType);
|
|
72
|
-
// ERC20 token (e.g., "eip155:1/erc20:0x...")
|
|
73
66
|
if (parsed.assetNamespace === 'erc20') {
|
|
74
67
|
return [parsed.assetReference, false];
|
|
75
68
|
}
|
|
76
|
-
// Native token (e.g., "eip155:1/slip44:60")
|
|
77
69
|
if (parsed.assetNamespace === 'slip44') {
|
|
78
70
|
return [ZERO_ADDRESS, true];
|
|
79
71
|
}
|
|
80
72
|
return null;
|
|
81
73
|
};
|
|
82
74
|
exports.parseAssetType = parseAssetType;
|
|
83
|
-
// endregion
|
|
84
75
|
// ────────────────────────────────────────────────────────────────────────────
|
|
85
|
-
//
|
|
76
|
+
// Main controller
|
|
86
77
|
class TokenBalancesController extends (0, polling_controller_1.StaticIntervalPollingController)() {
|
|
87
78
|
constructor({ messenger, interval = DEFAULT_INTERVAL_MS, websocketActivePollingInterval = DEFAULT_WEBSOCKET_ACTIVE_POLLING_INTERVAL_MS, chainPollingIntervals = {}, state = {}, queryMultipleAccounts = true, accountsApiChainIds = () => [], allowExternalServices = () => true, platform, }) {
|
|
88
79
|
super({
|
|
@@ -109,6 +100,8 @@ class TokenBalancesController extends (0, polling_controller_1.StaticIntervalPol
|
|
|
109
100
|
_TokenBalancesController_intervalPollingTimers.set(this, new Map());
|
|
110
101
|
/** Track if controller-level polling is active */
|
|
111
102
|
_TokenBalancesController_isControllerPollingActive.set(this, false);
|
|
103
|
+
/** Track if the keyring is unlocked */
|
|
104
|
+
_TokenBalancesController_isUnlocked.set(this, false);
|
|
112
105
|
/** Store original chainIds from startPolling to preserve intent */
|
|
113
106
|
_TokenBalancesController_requestedChainIds.set(this, []);
|
|
114
107
|
/** Debouncing for rapid status changes to prevent excessive HTTP calls */
|
|
@@ -118,52 +111,34 @@ class TokenBalancesController extends (0, polling_controller_1.StaticIntervalPol
|
|
|
118
111
|
});
|
|
119
112
|
_TokenBalancesController_getProvider.set(this, (chainId) => {
|
|
120
113
|
const { networkConfigurationsByChainId } = this.messenger.call('NetworkController:getState');
|
|
121
|
-
const
|
|
122
|
-
const { networkClientId } =
|
|
114
|
+
const networkConfig = networkConfigurationsByChainId[chainId];
|
|
115
|
+
const { networkClientId } = networkConfig.rpcEndpoints[networkConfig.defaultRpcEndpointIndex];
|
|
123
116
|
const client = this.messenger.call('NetworkController:getNetworkClientById', networkClientId);
|
|
124
117
|
return new providers_1.Web3Provider(client.provider);
|
|
125
118
|
});
|
|
126
119
|
_TokenBalancesController_getNetworkClient.set(this, (chainId) => {
|
|
127
120
|
const { networkConfigurationsByChainId } = this.messenger.call('NetworkController:getState');
|
|
128
|
-
const
|
|
129
|
-
const { networkClientId } =
|
|
121
|
+
const networkConfig = networkConfigurationsByChainId[chainId];
|
|
122
|
+
const { networkClientId } = networkConfig.rpcEndpoints[networkConfig.defaultRpcEndpointIndex];
|
|
130
123
|
return this.messenger.call('NetworkController:getNetworkClientById', networkClientId);
|
|
131
124
|
});
|
|
132
|
-
/**
|
|
133
|
-
* Creates an AccountsApiBalanceFetcher that only supports chains in the accountsApiChainIds array
|
|
134
|
-
*
|
|
135
|
-
* @returns A BalanceFetcher that wraps AccountsApiBalanceFetcher with chainId filtering
|
|
136
|
-
*/
|
|
137
125
|
_TokenBalancesController_createAccountsApiFetcher.set(this, () => {
|
|
138
126
|
const originalFetcher = new api_balance_fetcher_1.AccountsApiBalanceFetcher(__classPrivateFieldGet(this, _TokenBalancesController_platform, "f"), __classPrivateFieldGet(this, _TokenBalancesController_getProvider, "f"));
|
|
139
127
|
return {
|
|
140
|
-
supports: (chainId) =>
|
|
141
|
-
|
|
142
|
-
// 1. In our specified accountsApiChainIds array
|
|
143
|
-
// 2. Actually supported by the AccountsApi
|
|
144
|
-
return (__classPrivateFieldGet(this, _TokenBalancesController_accountsApiChainIds, "f").call(this).includes(chainId) &&
|
|
145
|
-
originalFetcher.supports(chainId));
|
|
146
|
-
},
|
|
128
|
+
supports: (chainId) => __classPrivateFieldGet(this, _TokenBalancesController_accountsApiChainIds, "f").call(this).includes(chainId) &&
|
|
129
|
+
originalFetcher.supports(chainId),
|
|
147
130
|
fetch: originalFetcher.fetch.bind(originalFetcher),
|
|
148
131
|
};
|
|
149
132
|
});
|
|
133
|
+
// ────────────────────────────────────────────────────────────────────────
|
|
134
|
+
// TokensController / Network / Accounts events
|
|
150
135
|
_TokenBalancesController_onTokensChanged.set(this, async (state) => {
|
|
151
136
|
const changed = [];
|
|
152
137
|
let hasChanges = false;
|
|
153
|
-
// Get chains that have existing balances
|
|
154
|
-
const chainsWithBalances = new Set();
|
|
155
|
-
for (const address of Object.keys(this.state.tokenBalances)) {
|
|
156
|
-
const addressKey = address;
|
|
157
|
-
for (const chainId of Object.keys(this.state.tokenBalances[addressKey] || {})) {
|
|
158
|
-
chainsWithBalances.add(chainId);
|
|
159
|
-
}
|
|
160
|
-
}
|
|
161
|
-
// Only process chains that are explicitly mentioned in the incoming state change
|
|
162
138
|
const incomingChainIds = new Set([
|
|
163
139
|
...Object.keys(state.allTokens),
|
|
164
140
|
...Object.keys(state.allDetectedTokens),
|
|
165
141
|
]);
|
|
166
|
-
// Only proceed if there are actual changes to chains that have balances or are being added
|
|
167
142
|
const relevantChainIds = Array.from(incomingChainIds).filter((chainId) => {
|
|
168
143
|
const id = chainId;
|
|
169
144
|
const hasTokensNow = (state.allTokens[id] && Object.keys(state.allTokens[id]).length > 0) ||
|
|
@@ -172,20 +147,16 @@ class TokenBalancesController extends (0, polling_controller_1.StaticIntervalPol
|
|
|
172
147
|
const hadTokensBefore = (__classPrivateFieldGet(this, _TokenBalancesController_allTokens, "f")[id] && Object.keys(__classPrivateFieldGet(this, _TokenBalancesController_allTokens, "f")[id]).length > 0) ||
|
|
173
148
|
(__classPrivateFieldGet(this, _TokenBalancesController_detectedTokens, "f")[id] &&
|
|
174
149
|
Object.keys(__classPrivateFieldGet(this, _TokenBalancesController_detectedTokens, "f")[id]).length > 0);
|
|
175
|
-
// Check if there's an actual change in token state
|
|
176
150
|
const hasTokenChange = !(0, lodash_1.isEqual)(state.allTokens[id], __classPrivateFieldGet(this, _TokenBalancesController_allTokens, "f")[id]) ||
|
|
177
151
|
!(0, lodash_1.isEqual)(state.allDetectedTokens[id], __classPrivateFieldGet(this, _TokenBalancesController_detectedTokens, "f")[id]);
|
|
178
|
-
// Process chains that have actual changes OR are new chains getting tokens
|
|
179
152
|
return hasTokenChange || (!hadTokensBefore && hasTokensNow);
|
|
180
153
|
});
|
|
181
|
-
if (relevantChainIds.length
|
|
182
|
-
// No relevant changes, just update internal state
|
|
154
|
+
if (!relevantChainIds.length) {
|
|
183
155
|
__classPrivateFieldSet(this, _TokenBalancesController_allTokens, state.allTokens, "f");
|
|
184
156
|
__classPrivateFieldSet(this, _TokenBalancesController_detectedTokens, state.allDetectedTokens, "f");
|
|
185
157
|
return;
|
|
186
158
|
}
|
|
187
|
-
|
|
188
|
-
this.update((s) => {
|
|
159
|
+
this.update((currentState) => {
|
|
189
160
|
for (const chainId of relevantChainIds) {
|
|
190
161
|
const id = chainId;
|
|
191
162
|
const hasTokensNow = (state.allTokens[id] &&
|
|
@@ -196,20 +167,20 @@ class TokenBalancesController extends (0, polling_controller_1.StaticIntervalPol
|
|
|
196
167
|
Object.keys(__classPrivateFieldGet(this, _TokenBalancesController_allTokens, "f")[id]).length > 0) ||
|
|
197
168
|
(__classPrivateFieldGet(this, _TokenBalancesController_detectedTokens, "f")[id] &&
|
|
198
169
|
Object.keys(__classPrivateFieldGet(this, _TokenBalancesController_detectedTokens, "f")[id]).length > 0);
|
|
199
|
-
|
|
200
|
-
!(0, lodash_1.isEqual)(state.allDetectedTokens[id], __classPrivateFieldGet(this, _TokenBalancesController_detectedTokens, "f")[id])
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
170
|
+
const tokensChanged = !(0, lodash_1.isEqual)(state.allTokens[id], __classPrivateFieldGet(this, _TokenBalancesController_allTokens, "f")[id]) ||
|
|
171
|
+
!(0, lodash_1.isEqual)(state.allDetectedTokens[id], __classPrivateFieldGet(this, _TokenBalancesController_detectedTokens, "f")[id]);
|
|
172
|
+
if (!tokensChanged) {
|
|
173
|
+
continue;
|
|
174
|
+
}
|
|
175
|
+
if (hasTokensNow) {
|
|
176
|
+
changed.push(id);
|
|
177
|
+
}
|
|
178
|
+
else if (hadTokensBefore) {
|
|
179
|
+
for (const address of Object.keys(currentState.tokenBalances)) {
|
|
180
|
+
const addressKey = address;
|
|
181
|
+
if (currentState.tokenBalances[addressKey]?.[id]) {
|
|
182
|
+
currentState.tokenBalances[addressKey][id] = {};
|
|
183
|
+
hasChanges = true;
|
|
213
184
|
}
|
|
214
185
|
}
|
|
215
186
|
}
|
|
@@ -218,7 +189,6 @@ class TokenBalancesController extends (0, polling_controller_1.StaticIntervalPol
|
|
|
218
189
|
__classPrivateFieldSet(this, _TokenBalancesController_allTokens, state.allTokens, "f");
|
|
219
190
|
__classPrivateFieldSet(this, _TokenBalancesController_detectedTokens, state.allDetectedTokens, "f");
|
|
220
191
|
__classPrivateFieldSet(this, _TokenBalancesController_allIgnoredTokens, state.allIgnoredTokens, "f");
|
|
221
|
-
// Only update balances for chains that still have tokens (and only if we haven't already updated state)
|
|
222
192
|
if (changed.length && !hasChanges) {
|
|
223
193
|
this.updateBalances({ chainIds: changed }).catch((error) => {
|
|
224
194
|
console.warn('Error updating balances after token change:', error);
|
|
@@ -226,9 +196,7 @@ class TokenBalancesController extends (0, polling_controller_1.StaticIntervalPol
|
|
|
226
196
|
}
|
|
227
197
|
});
|
|
228
198
|
_TokenBalancesController_onNetworkChanged.set(this, (state) => {
|
|
229
|
-
// Check if any networks were removed by comparing with previous state
|
|
230
199
|
const currentNetworks = new Set(Object.keys(state.networkConfigurationsByChainId));
|
|
231
|
-
// Get all networks that currently have balances
|
|
232
200
|
const networksWithBalances = new Set();
|
|
233
201
|
for (const address of Object.keys(this.state.tokenBalances)) {
|
|
234
202
|
const addressKey = address;
|
|
@@ -236,83 +204,59 @@ class TokenBalancesController extends (0, polling_controller_1.StaticIntervalPol
|
|
|
236
204
|
networksWithBalances.add(network);
|
|
237
205
|
}
|
|
238
206
|
}
|
|
239
|
-
// Find networks that were removed
|
|
240
207
|
const removedNetworks = Array.from(networksWithBalances).filter((network) => !currentNetworks.has(network));
|
|
241
|
-
if (removedNetworks.length
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
208
|
+
if (!removedNetworks.length) {
|
|
209
|
+
return;
|
|
210
|
+
}
|
|
211
|
+
this.update((currentState) => {
|
|
212
|
+
for (const address of Object.keys(currentState.tokenBalances)) {
|
|
213
|
+
const addressKey = address;
|
|
214
|
+
for (const removedNetwork of removedNetworks) {
|
|
215
|
+
const networkKey = removedNetwork;
|
|
216
|
+
if (currentState.tokenBalances[addressKey]?.[networkKey]) {
|
|
217
|
+
delete currentState.tokenBalances[addressKey][networkKey];
|
|
251
218
|
}
|
|
252
219
|
}
|
|
253
|
-
}
|
|
254
|
-
}
|
|
220
|
+
}
|
|
221
|
+
});
|
|
255
222
|
});
|
|
256
223
|
_TokenBalancesController_onAccountRemoved.set(this, (addr) => {
|
|
257
224
|
if (!(0, utils_1.isStrictHexString)(addr) || !(0, controller_utils_1.isValidHexAddress)(addr)) {
|
|
258
225
|
return;
|
|
259
226
|
}
|
|
260
|
-
this.update((
|
|
261
|
-
delete
|
|
227
|
+
this.update((currentState) => {
|
|
228
|
+
delete currentState.tokenBalances[addr];
|
|
262
229
|
});
|
|
263
230
|
});
|
|
264
|
-
/**
|
|
265
|
-
* Handle account selection changes
|
|
266
|
-
* Triggers immediate balance fetch to ensure we have the latest balances
|
|
267
|
-
* since WebSocket only provides updates for changes going forward
|
|
268
|
-
*/
|
|
269
231
|
_TokenBalancesController_onAccountChanged.set(this, () => {
|
|
270
|
-
// Fetch balances for all chains with tokens when account changes
|
|
271
232
|
const chainIds = __classPrivateFieldGet(this, _TokenBalancesController_instances, "m", _TokenBalancesController_chainIdsWithTokens).call(this);
|
|
272
|
-
if (chainIds.length
|
|
273
|
-
|
|
274
|
-
// Silently handle polling errors
|
|
275
|
-
});
|
|
233
|
+
if (!chainIds.length) {
|
|
234
|
+
return;
|
|
276
235
|
}
|
|
236
|
+
this.updateBalances({ chainIds }).catch(() => {
|
|
237
|
+
// Silently handle polling errors
|
|
238
|
+
});
|
|
277
239
|
});
|
|
278
|
-
// ────────────────────────────────────────────────────────────────────────────
|
|
279
|
-
// AccountActivityService event handlers
|
|
280
|
-
/**
|
|
281
|
-
* Handle real-time balance updates from AccountActivityService
|
|
282
|
-
* Processes balance updates and updates the token balance state
|
|
283
|
-
* If any balance update has an error, triggers fallback polling for the chain
|
|
284
|
-
*
|
|
285
|
-
* @param options0 - Balance update parameters
|
|
286
|
-
* @param options0.address - Account address
|
|
287
|
-
* @param options0.chain - CAIP chain identifier
|
|
288
|
-
* @param options0.updates - Array of balance updates for the account
|
|
289
|
-
*/
|
|
290
240
|
_TokenBalancesController_onAccountActivityBalanceUpdate.set(this, async ({ address, chain, updates, }) => {
|
|
291
241
|
const chainId = (0, exports.caipChainIdToHex)(chain);
|
|
292
242
|
const checksummedAccount = checksum(address);
|
|
293
243
|
try {
|
|
294
|
-
// Process all balance updates at once
|
|
295
244
|
const { tokenBalances, newTokens, nativeBalanceUpdates } = __classPrivateFieldGet(this, _TokenBalancesController_instances, "m", _TokenBalancesController_prepareBalanceUpdates).call(this, updates, checksummedAccount, chainId);
|
|
296
|
-
// Update state once with all token balances
|
|
297
245
|
if (tokenBalances.length > 0) {
|
|
298
246
|
this.update((state) => {
|
|
299
247
|
var _a, _b;
|
|
300
|
-
// Temporary until ADR to normalize all keys - tokenBalances state requires: account in lowercase, token in checksum
|
|
301
248
|
const lowercaseAccount = checksummedAccount.toLowerCase();
|
|
302
249
|
(_a = state.tokenBalances)[lowercaseAccount] ?? (_a[lowercaseAccount] = {});
|
|
303
250
|
(_b = state.tokenBalances[lowercaseAccount])[chainId] ?? (_b[chainId] = {});
|
|
304
|
-
// Apply all token balance updates
|
|
305
251
|
for (const { tokenAddress, balance } of tokenBalances) {
|
|
306
252
|
state.tokenBalances[lowercaseAccount][chainId][tokenAddress] =
|
|
307
253
|
balance;
|
|
308
254
|
}
|
|
309
255
|
});
|
|
310
256
|
}
|
|
311
|
-
// Update native balances in AccountTrackerController
|
|
312
257
|
if (nativeBalanceUpdates.length > 0) {
|
|
313
258
|
this.messenger.call('AccountTrackerController:updateNativeBalances', nativeBalanceUpdates);
|
|
314
259
|
}
|
|
315
|
-
// Import any new tokens that were discovered (balance already updated from websocket)
|
|
316
260
|
if (newTokens.length > 0) {
|
|
317
261
|
await this.messenger.call('TokenDetectionController:addDetectedTokensViaWs', {
|
|
318
262
|
tokensSlice: newTokens,
|
|
@@ -323,35 +267,22 @@ class TokenBalancesController extends (0, polling_controller_1.StaticIntervalPol
|
|
|
323
267
|
catch (error) {
|
|
324
268
|
console.warn(`Error updating balances from AccountActivityService for chain ${chain}, account ${address}:`, error);
|
|
325
269
|
console.warn('Balance update data:', JSON.stringify(updates, null, 2));
|
|
326
|
-
// On error, trigger fallback polling
|
|
327
270
|
await this.updateBalances({ chainIds: [chainId] }).catch(() => {
|
|
328
271
|
// Silently handle polling errors
|
|
329
272
|
});
|
|
330
273
|
}
|
|
331
274
|
});
|
|
332
|
-
/**
|
|
333
|
-
* Handle status changes from AccountActivityService
|
|
334
|
-
* Uses aggressive debouncing to prevent excessive HTTP calls from rapid up/down changes
|
|
335
|
-
*
|
|
336
|
-
* @param options0 - Status change event data
|
|
337
|
-
* @param options0.chainIds - Array of chain identifiers
|
|
338
|
-
* @param options0.status - Connection status ('up' for connected, 'down' for disconnected)
|
|
339
|
-
*/
|
|
340
275
|
_TokenBalancesController_onAccountActivityStatusChanged.set(this, ({ chainIds, status, }) => {
|
|
341
|
-
// Update pending changes (latest status wins for each chain)
|
|
342
276
|
for (const chainId of chainIds) {
|
|
343
277
|
__classPrivateFieldGet(this, _TokenBalancesController_statusChangeDebouncer, "f").pendingChanges.set(chainId, status);
|
|
344
278
|
}
|
|
345
|
-
// Clear existing timer to extend debounce window
|
|
346
279
|
if (__classPrivateFieldGet(this, _TokenBalancesController_statusChangeDebouncer, "f").timer) {
|
|
347
280
|
clearTimeout(__classPrivateFieldGet(this, _TokenBalancesController_statusChangeDebouncer, "f").timer);
|
|
348
281
|
}
|
|
349
|
-
// Set new timer - only process changes after activity settles
|
|
350
282
|
__classPrivateFieldGet(this, _TokenBalancesController_statusChangeDebouncer, "f").timer = setTimeout(() => {
|
|
351
283
|
__classPrivateFieldGet(this, _TokenBalancesController_instances, "m", _TokenBalancesController_processAccumulatedStatusChanges).call(this);
|
|
352
|
-
}, 5000);
|
|
284
|
+
}, 5000);
|
|
353
285
|
});
|
|
354
|
-
// Normalize all account addresses to lowercase in existing state
|
|
355
286
|
__classPrivateFieldGet(this, _TokenBalancesController_instances, "m", _TokenBalancesController_normalizeAccountAddresses).call(this);
|
|
356
287
|
__classPrivateFieldSet(this, _TokenBalancesController_platform, platform ?? 'extension', "f");
|
|
357
288
|
__classPrivateFieldSet(this, _TokenBalancesController_queryAllAccounts, queryMultipleAccounts, "f");
|
|
@@ -359,7 +290,6 @@ class TokenBalancesController extends (0, polling_controller_1.StaticIntervalPol
|
|
|
359
290
|
__classPrivateFieldSet(this, _TokenBalancesController_defaultInterval, interval, "f");
|
|
360
291
|
__classPrivateFieldSet(this, _TokenBalancesController_websocketActivePollingInterval, websocketActivePollingInterval, "f");
|
|
361
292
|
__classPrivateFieldSet(this, _TokenBalancesController_chainPollingConfig, { ...chainPollingIntervals }, "f");
|
|
362
|
-
// Strategy order: API first, then RPC fallback
|
|
363
293
|
__classPrivateFieldSet(this, _TokenBalancesController_balanceFetchers, [
|
|
364
294
|
...(accountsApiChainIds().length > 0 && allowExternalServices()
|
|
365
295
|
? [__classPrivateFieldGet(this, _TokenBalancesController_createAccountsApiFetcher, "f").call(this)]
|
|
@@ -370,303 +300,181 @@ class TokenBalancesController extends (0, polling_controller_1.StaticIntervalPol
|
|
|
370
300
|
})),
|
|
371
301
|
], "f");
|
|
372
302
|
this.setIntervalLength(interval);
|
|
373
|
-
// initial token state & subscriptions
|
|
374
303
|
const { allTokens, allDetectedTokens, allIgnoredTokens } = this.messenger.call('TokensController:getState');
|
|
375
304
|
__classPrivateFieldSet(this, _TokenBalancesController_allTokens, allTokens, "f");
|
|
376
305
|
__classPrivateFieldSet(this, _TokenBalancesController_detectedTokens, allDetectedTokens, "f");
|
|
377
306
|
__classPrivateFieldSet(this, _TokenBalancesController_allIgnoredTokens, allIgnoredTokens, "f");
|
|
378
|
-
this.messenger.
|
|
379
|
-
|
|
380
|
-
|
|
381
|
-
|
|
382
|
-
});
|
|
383
|
-
this.messenger.subscribe('NetworkController:stateChange', __classPrivateFieldGet(this, _TokenBalancesController_onNetworkChanged, "f"));
|
|
384
|
-
this.messenger.subscribe('KeyringController:accountRemoved', __classPrivateFieldGet(this, _TokenBalancesController_onAccountRemoved, "f"));
|
|
385
|
-
this.messenger.subscribe('AccountsController:selectedEvmAccountChange', __classPrivateFieldGet(this, _TokenBalancesController_onAccountChanged, "f"));
|
|
386
|
-
// Register action handlers for polling interval control
|
|
387
|
-
this.messenger.registerActionHandler(`TokenBalancesController:updateChainPollingConfigs`, this.updateChainPollingConfigs.bind(this));
|
|
388
|
-
this.messenger.registerActionHandler(`TokenBalancesController:getChainPollingConfig`, this.getChainPollingConfig.bind(this));
|
|
389
|
-
// Subscribe to AccountActivityService balance updates for real-time updates
|
|
390
|
-
this.messenger.subscribe('AccountActivityService:balanceUpdated', __classPrivateFieldGet(this, _TokenBalancesController_onAccountActivityBalanceUpdate, "f").bind(this));
|
|
391
|
-
// Subscribe to AccountActivityService status changes for dynamic polling management
|
|
392
|
-
this.messenger.subscribe('AccountActivityService:statusChanged', __classPrivateFieldGet(this, _TokenBalancesController_onAccountActivityStatusChanged, "f").bind(this));
|
|
307
|
+
const { isUnlocked } = this.messenger.call('KeyringController:getState');
|
|
308
|
+
__classPrivateFieldSet(this, _TokenBalancesController_isUnlocked, isUnlocked, "f");
|
|
309
|
+
__classPrivateFieldGet(this, _TokenBalancesController_instances, "m", _TokenBalancesController_subscribeToControllers).call(this);
|
|
310
|
+
__classPrivateFieldGet(this, _TokenBalancesController_instances, "m", _TokenBalancesController_registerActions).call(this);
|
|
393
311
|
}
|
|
312
|
+
// ────────────────────────────────────────────────────────────────────────
|
|
313
|
+
// Address + network helpers
|
|
394
314
|
/**
|
|
395
|
-
*
|
|
315
|
+
* Whether the controller is active (keyring is unlocked).
|
|
316
|
+
* When locked, balance updates should be skipped.
|
|
396
317
|
*
|
|
397
|
-
* @
|
|
398
|
-
* @param options0.chainIds - Chain IDs to start polling for
|
|
318
|
+
* @returns Whether the keyring is unlocked.
|
|
399
319
|
*/
|
|
320
|
+
get isActive() {
|
|
321
|
+
return __classPrivateFieldGet(this, _TokenBalancesController_isUnlocked, "f");
|
|
322
|
+
}
|
|
323
|
+
// ────────────────────────────────────────────────────────────────────────
|
|
324
|
+
// Polling overrides
|
|
400
325
|
_startPolling({ chainIds }) {
|
|
401
|
-
// Store the original chainIds to preserve intent across config updates
|
|
402
326
|
__classPrivateFieldSet(this, _TokenBalancesController_requestedChainIds, [...chainIds], "f");
|
|
403
327
|
__classPrivateFieldSet(this, _TokenBalancesController_isControllerPollingActive, true, "f");
|
|
404
328
|
__classPrivateFieldGet(this, _TokenBalancesController_instances, "m", _TokenBalancesController_startIntervalGroupPolling).call(this, chainIds, true);
|
|
405
329
|
}
|
|
406
|
-
/**
|
|
407
|
-
* Override to handle our custom polling approach
|
|
408
|
-
*
|
|
409
|
-
* @param tokenSetId - The token set ID to stop polling for
|
|
410
|
-
*/
|
|
411
330
|
_stopPollingByPollingTokenSetId(tokenSetId) {
|
|
412
|
-
let parsedTokenSetId;
|
|
413
331
|
let chainsToStop = [];
|
|
414
332
|
try {
|
|
415
|
-
parsedTokenSetId = JSON.parse(tokenSetId);
|
|
416
|
-
chainsToStop = parsedTokenSetId.chainIds
|
|
333
|
+
const parsedTokenSetId = JSON.parse(tokenSetId);
|
|
334
|
+
chainsToStop = parsedTokenSetId.chainIds ?? [];
|
|
417
335
|
}
|
|
418
336
|
catch (error) {
|
|
419
337
|
console.warn('Failed to parse tokenSetId, stopping all polling:', error);
|
|
420
|
-
|
|
421
|
-
__classPrivateFieldSet(this, _TokenBalancesController_isControllerPollingActive, false, "f");
|
|
422
|
-
__classPrivateFieldSet(this, _TokenBalancesController_requestedChainIds, [], "f");
|
|
423
|
-
__classPrivateFieldGet(this, _TokenBalancesController_intervalPollingTimers, "f").forEach((timer) => clearInterval(timer));
|
|
424
|
-
__classPrivateFieldGet(this, _TokenBalancesController_intervalPollingTimers, "f").clear();
|
|
338
|
+
__classPrivateFieldGet(this, _TokenBalancesController_instances, "m", _TokenBalancesController_stopAllPolling).call(this);
|
|
425
339
|
return;
|
|
426
340
|
}
|
|
427
|
-
// Compare with current chains - only stop if it matches our current session
|
|
428
341
|
const currentChainsSet = new Set(__classPrivateFieldGet(this, _TokenBalancesController_requestedChainIds, "f"));
|
|
429
342
|
const stopChainsSet = new Set(chainsToStop);
|
|
430
|
-
// Check if this stop request is for our current session
|
|
431
343
|
const isCurrentSession = currentChainsSet.size === stopChainsSet.size &&
|
|
432
344
|
[...currentChainsSet].every((chain) => stopChainsSet.has(chain));
|
|
433
345
|
if (isCurrentSession) {
|
|
434
|
-
|
|
435
|
-
__classPrivateFieldSet(this, _TokenBalancesController_requestedChainIds, [], "f");
|
|
436
|
-
__classPrivateFieldGet(this, _TokenBalancesController_intervalPollingTimers, "f").forEach((timer) => clearInterval(timer));
|
|
437
|
-
__classPrivateFieldGet(this, _TokenBalancesController_intervalPollingTimers, "f").clear();
|
|
346
|
+
__classPrivateFieldGet(this, _TokenBalancesController_instances, "m", _TokenBalancesController_stopAllPolling).call(this);
|
|
438
347
|
}
|
|
439
348
|
}
|
|
440
|
-
/**
|
|
441
|
-
* Get polling configuration for a chain (includes default fallback)
|
|
442
|
-
*
|
|
443
|
-
* @param chainId - The chain ID to get config for
|
|
444
|
-
* @returns The polling configuration for the chain
|
|
445
|
-
*/
|
|
446
349
|
getChainPollingConfig(chainId) {
|
|
447
350
|
return (__classPrivateFieldGet(this, _TokenBalancesController_chainPollingConfig, "f")[chainId] ?? {
|
|
448
351
|
interval: __classPrivateFieldGet(this, _TokenBalancesController_defaultInterval, "f"),
|
|
449
352
|
});
|
|
450
353
|
}
|
|
451
354
|
async _executePoll({ chainIds, queryAllAccounts = false, }) {
|
|
452
|
-
// This won't be called with our custom implementation, but keep for compatibility
|
|
453
355
|
await this.updateBalances({ chainIds, queryAllAccounts });
|
|
454
356
|
}
|
|
455
|
-
/**
|
|
456
|
-
* Update multiple chain polling configurations at once
|
|
457
|
-
*
|
|
458
|
-
* @param configs - Object mapping chain IDs to polling configurations
|
|
459
|
-
* @param options - Optional configuration for the update behavior
|
|
460
|
-
* @param options.immediateUpdate - Whether to immediately fetch balances after updating configs (default: true)
|
|
461
|
-
*/
|
|
462
357
|
updateChainPollingConfigs(configs, options = { immediateUpdate: true }) {
|
|
463
358
|
Object.assign(__classPrivateFieldGet(this, _TokenBalancesController_chainPollingConfig, "f"), configs);
|
|
464
|
-
// If polling is currently active, restart with new interval groupings
|
|
465
359
|
if (__classPrivateFieldGet(this, _TokenBalancesController_isControllerPollingActive, "f")) {
|
|
466
|
-
// Restart polling with immediate fetch by default, unless explicitly disabled
|
|
467
360
|
__classPrivateFieldGet(this, _TokenBalancesController_instances, "m", _TokenBalancesController_startIntervalGroupPolling).call(this, __classPrivateFieldGet(this, _TokenBalancesController_requestedChainIds, "f"), options.immediateUpdate);
|
|
468
361
|
}
|
|
469
362
|
}
|
|
470
|
-
|
|
471
|
-
|
|
472
|
-
|
|
363
|
+
// ────────────────────────────────────────────────────────────────────────
|
|
364
|
+
// Balances update (main flow, refactored)
|
|
365
|
+
async updateBalances({ chainIds, tokenAddresses, queryAllAccounts = false, } = {}) {
|
|
366
|
+
if (!this.isActive) {
|
|
473
367
|
return;
|
|
474
368
|
}
|
|
475
|
-
const
|
|
476
|
-
|
|
477
|
-
|
|
478
|
-
return this.messenger.call('AuthenticationController:getBearerToken');
|
|
479
|
-
}, false, 5000);
|
|
480
|
-
const aggregated = [];
|
|
481
|
-
let remainingChains = [...targetChains];
|
|
482
|
-
// Try each fetcher in order, removing successfully processed chains
|
|
483
|
-
for (const fetcher of __classPrivateFieldGet(this, _TokenBalancesController_balanceFetchers, "f")) {
|
|
484
|
-
const supportedChains = remainingChains.filter((c) => fetcher.supports(c));
|
|
485
|
-
if (!supportedChains.length) {
|
|
486
|
-
continue;
|
|
487
|
-
}
|
|
488
|
-
try {
|
|
489
|
-
const result = await fetcher.fetch({
|
|
490
|
-
chainIds: supportedChains,
|
|
491
|
-
queryAllAccounts: queryAllAccounts ?? __classPrivateFieldGet(this, _TokenBalancesController_queryAllAccounts, "f"),
|
|
492
|
-
selectedAccount: selected,
|
|
493
|
-
allAccounts,
|
|
494
|
-
jwtToken,
|
|
495
|
-
});
|
|
496
|
-
if (result.balances && result.balances.length > 0) {
|
|
497
|
-
aggregated.push(...result.balances);
|
|
498
|
-
// Remove chains that were successfully processed
|
|
499
|
-
const processedChains = new Set(result.balances.map((b) => b.chainId));
|
|
500
|
-
remainingChains = remainingChains.filter((chain) => !processedChains.has(chain));
|
|
501
|
-
}
|
|
502
|
-
// Add unprocessed chains back to remainingChains for next fetcher
|
|
503
|
-
if (result.unprocessedChainIds &&
|
|
504
|
-
result.unprocessedChainIds.length > 0) {
|
|
505
|
-
const currentRemainingChains = remainingChains;
|
|
506
|
-
const chainsToAdd = result.unprocessedChainIds.filter((chainId) => supportedChains.includes(chainId) &&
|
|
507
|
-
!currentRemainingChains.includes(chainId));
|
|
508
|
-
remainingChains.push(...chainsToAdd);
|
|
509
|
-
}
|
|
510
|
-
}
|
|
511
|
-
catch (error) {
|
|
512
|
-
console.warn(`Balance fetcher failed for chains ${supportedChains.join(', ')}: ${String(error)}`);
|
|
513
|
-
// Continue to next fetcher (fallback)
|
|
514
|
-
}
|
|
515
|
-
// If all chains have been processed, break early
|
|
516
|
-
if (remainingChains.length === 0) {
|
|
517
|
-
break;
|
|
518
|
-
}
|
|
369
|
+
const targetChains = __classPrivateFieldGet(this, _TokenBalancesController_instances, "m", _TokenBalancesController_getTargetChains).call(this, chainIds);
|
|
370
|
+
if (!targetChains.length) {
|
|
371
|
+
return;
|
|
519
372
|
}
|
|
520
|
-
|
|
521
|
-
const
|
|
522
|
-
|
|
523
|
-
|
|
373
|
+
const { selectedAccount, allAccounts, jwtToken } = await __classPrivateFieldGet(this, _TokenBalancesController_instances, "m", _TokenBalancesController_getAccountsAndJwt).call(this);
|
|
374
|
+
const aggregatedBalances = await __classPrivateFieldGet(this, _TokenBalancesController_instances, "m", _TokenBalancesController_fetchAllBalances).call(this, {
|
|
375
|
+
targetChains,
|
|
376
|
+
selectedAccount,
|
|
377
|
+
allAccounts,
|
|
378
|
+
jwtToken,
|
|
379
|
+
queryAllAccounts: queryAllAccounts ?? __classPrivateFieldGet(this, _TokenBalancesController_queryAllAccounts, "f"),
|
|
380
|
+
});
|
|
381
|
+
const filteredAggregated = __classPrivateFieldGet(this, _TokenBalancesController_instances, "m", _TokenBalancesController_filterByTokenAddresses).call(this, aggregatedBalances, tokenAddresses);
|
|
382
|
+
const accountsToProcess = __classPrivateFieldGet(this, _TokenBalancesController_instances, "m", _TokenBalancesController_getAccountsToProcess).call(this, queryAllAccounts, allAccounts, selectedAccount);
|
|
524
383
|
const prev = this.state;
|
|
525
|
-
const next =
|
|
526
|
-
|
|
527
|
-
|
|
528
|
-
|
|
529
|
-
|
|
530
|
-
// Ensure the nested structure exists without overwriting existing balances
|
|
531
|
-
(_a = d.tokenBalances)[account] ?? (_a[account] = {});
|
|
532
|
-
(_b = d.tokenBalances[account])[chainId] ?? (_b[chainId] = {});
|
|
533
|
-
// Initialize tokens from allTokens only if they don't exist yet
|
|
534
|
-
const chainTokens = __classPrivateFieldGet(this, _TokenBalancesController_allTokens, "f")[chainId];
|
|
535
|
-
if (chainTokens?.[account]) {
|
|
536
|
-
Object.values(chainTokens[account]).forEach((token) => {
|
|
537
|
-
const tokenAddress = checksum(token.address);
|
|
538
|
-
// Only initialize if the token balance doesn't exist yet
|
|
539
|
-
if (!(tokenAddress in d.tokenBalances[account][chainId])) {
|
|
540
|
-
d.tokenBalances[account][chainId][tokenAddress] = '0x0';
|
|
541
|
-
}
|
|
542
|
-
});
|
|
543
|
-
}
|
|
544
|
-
// Initialize tokens from allDetectedTokens only if they don't exist yet
|
|
545
|
-
const detectedChainTokens = __classPrivateFieldGet(this, _TokenBalancesController_detectedTokens, "f")[chainId];
|
|
546
|
-
if (detectedChainTokens?.[account]) {
|
|
547
|
-
Object.values(detectedChainTokens[account]).forEach((token) => {
|
|
548
|
-
const tokenAddress = checksum(token.address);
|
|
549
|
-
// Only initialize if the token balance doesn't exist yet
|
|
550
|
-
if (!(tokenAddress in d.tokenBalances[account][chainId])) {
|
|
551
|
-
d.tokenBalances[account][chainId][tokenAddress] = '0x0';
|
|
552
|
-
}
|
|
553
|
-
});
|
|
554
|
-
}
|
|
555
|
-
}
|
|
556
|
-
}
|
|
557
|
-
// Update with actual fetched balances only if the value has changed
|
|
558
|
-
aggregated.forEach(({ success, value, account, token, chainId }) => {
|
|
559
|
-
var _a, _b;
|
|
560
|
-
if (success && value !== undefined) {
|
|
561
|
-
// Ensure all accounts we add/update are in lower-case
|
|
562
|
-
const lowerCaseAccount = account.toLowerCase();
|
|
563
|
-
const newBalance = (0, controller_utils_1.toHex)(value);
|
|
564
|
-
const tokenAddress = checksum(token);
|
|
565
|
-
const currentBalance = d.tokenBalances[lowerCaseAccount]?.[chainId]?.[tokenAddress];
|
|
566
|
-
// Only update if the balance has actually changed
|
|
567
|
-
if (currentBalance !== newBalance) {
|
|
568
|
-
((_b = ((_a = d.tokenBalances)[lowerCaseAccount] ?? (_a[lowerCaseAccount] = {})))[chainId] ?? (_b[chainId] = {}))[tokenAddress] = newBalance;
|
|
569
|
-
}
|
|
570
|
-
}
|
|
571
|
-
});
|
|
384
|
+
const next = __classPrivateFieldGet(this, _TokenBalancesController_instances, "m", _TokenBalancesController_applyTokenBalancesToState).call(this, {
|
|
385
|
+
prev,
|
|
386
|
+
targetChains,
|
|
387
|
+
accountsToProcess,
|
|
388
|
+
balances: filteredAggregated,
|
|
572
389
|
});
|
|
573
390
|
if (!(0, lodash_1.isEqual)(prev, next)) {
|
|
574
391
|
this.update(() => next);
|
|
575
|
-
const nativeBalances = aggregated.filter((r) => r.success && r.token === ZERO_ADDRESS);
|
|
576
|
-
// Get current AccountTracker state to compare existing balances
|
|
577
392
|
const accountTrackerState = this.messenger.call('AccountTrackerController:getState');
|
|
578
|
-
|
|
579
|
-
if (
|
|
580
|
-
|
|
581
|
-
.map((balance) => ({
|
|
582
|
-
address: balance.account,
|
|
583
|
-
chainId: balance.chainId,
|
|
584
|
-
balance: balance.value ? (0, controller_utils_1.BNToHex)(balance.value) : '0x0',
|
|
585
|
-
}))
|
|
586
|
-
.filter((update) => {
|
|
587
|
-
const currentBalance = accountTrackerState.accountsByChainId[update.chainId]?.[checksum(update.address)]?.balance;
|
|
588
|
-
// Only include if the balance has actually changed
|
|
589
|
-
return currentBalance !== update.balance;
|
|
590
|
-
});
|
|
591
|
-
if (balanceUpdates.length > 0) {
|
|
592
|
-
this.messenger.call('AccountTrackerController:updateNativeBalances', balanceUpdates);
|
|
593
|
-
}
|
|
393
|
+
const nativeUpdates = __classPrivateFieldGet(this, _TokenBalancesController_instances, "m", _TokenBalancesController_buildNativeBalanceUpdates).call(this, filteredAggregated, accountTrackerState);
|
|
394
|
+
if (nativeUpdates.length > 0) {
|
|
395
|
+
this.messenger.call('AccountTrackerController:updateNativeBalances', nativeUpdates);
|
|
594
396
|
}
|
|
595
|
-
|
|
596
|
-
|
|
597
|
-
|
|
598
|
-
return false;
|
|
599
|
-
}
|
|
600
|
-
// Check if the chainId and token address match any staking contract
|
|
601
|
-
const stakingContractAddress = AssetsContractController_1.STAKING_CONTRACT_ADDRESS_BY_CHAINID[r.chainId];
|
|
602
|
-
return (stakingContractAddress &&
|
|
603
|
-
stakingContractAddress.toLowerCase() === r.token.toLowerCase());
|
|
604
|
-
});
|
|
605
|
-
if (stakedBalances.length > 0) {
|
|
606
|
-
const stakedBalanceUpdates = stakedBalances
|
|
607
|
-
.map((balance) => ({
|
|
608
|
-
address: balance.account,
|
|
609
|
-
chainId: balance.chainId,
|
|
610
|
-
stakedBalance: balance.value ? (0, controller_utils_1.toHex)(balance.value) : '0x0',
|
|
611
|
-
}))
|
|
612
|
-
.filter((update) => {
|
|
613
|
-
const currentStakedBalance = accountTrackerState.accountsByChainId[update.chainId]?.[checksum(update.address)]?.stakedBalance;
|
|
614
|
-
// Only include if the staked balance has actually changed
|
|
615
|
-
return currentStakedBalance !== update.stakedBalance;
|
|
616
|
-
});
|
|
617
|
-
if (stakedBalanceUpdates.length > 0) {
|
|
618
|
-
this.messenger.call('AccountTrackerController:updateStakedBalances', stakedBalanceUpdates);
|
|
619
|
-
}
|
|
397
|
+
const stakedUpdates = __classPrivateFieldGet(this, _TokenBalancesController_instances, "m", _TokenBalancesController_buildStakedBalanceUpdates).call(this, filteredAggregated, accountTrackerState);
|
|
398
|
+
if (stakedUpdates.length > 0) {
|
|
399
|
+
this.messenger.call('AccountTrackerController:updateStakedBalances', stakedUpdates);
|
|
620
400
|
}
|
|
621
401
|
}
|
|
402
|
+
await __classPrivateFieldGet(this, _TokenBalancesController_instances, "m", _TokenBalancesController_importUntrackedTokens).call(this, filteredAggregated);
|
|
622
403
|
}
|
|
623
404
|
resetState() {
|
|
624
405
|
this.update(() => ({ tokenBalances: {} }));
|
|
625
406
|
}
|
|
626
|
-
|
|
627
|
-
|
|
628
|
-
*/
|
|
407
|
+
// ────────────────────────────────────────────────────────────────────────
|
|
408
|
+
// Destroy
|
|
629
409
|
destroy() {
|
|
630
410
|
__classPrivateFieldSet(this, _TokenBalancesController_isControllerPollingActive, false, "f");
|
|
631
411
|
__classPrivateFieldGet(this, _TokenBalancesController_intervalPollingTimers, "f").forEach((timer) => clearInterval(timer));
|
|
632
412
|
__classPrivateFieldGet(this, _TokenBalancesController_intervalPollingTimers, "f").clear();
|
|
633
|
-
// Clean up debouncing timer
|
|
634
413
|
if (__classPrivateFieldGet(this, _TokenBalancesController_statusChangeDebouncer, "f").timer) {
|
|
635
414
|
clearTimeout(__classPrivateFieldGet(this, _TokenBalancesController_statusChangeDebouncer, "f").timer);
|
|
636
415
|
__classPrivateFieldGet(this, _TokenBalancesController_statusChangeDebouncer, "f").timer = null;
|
|
637
416
|
}
|
|
638
|
-
// Unregister action handlers
|
|
639
417
|
this.messenger.unregisterActionHandler(`TokenBalancesController:updateChainPollingConfigs`);
|
|
640
418
|
this.messenger.unregisterActionHandler(`TokenBalancesController:getChainPollingConfig`);
|
|
641
419
|
super.destroy();
|
|
642
420
|
}
|
|
643
421
|
}
|
|
644
422
|
exports.TokenBalancesController = TokenBalancesController;
|
|
645
|
-
_TokenBalancesController_platform = new WeakMap(), _TokenBalancesController_queryAllAccounts = new WeakMap(), _TokenBalancesController_accountsApiChainIds = new WeakMap(), _TokenBalancesController_balanceFetchers = new WeakMap(), _TokenBalancesController_allTokens = new WeakMap(), _TokenBalancesController_detectedTokens = new WeakMap(), _TokenBalancesController_allIgnoredTokens = new WeakMap(), _TokenBalancesController_defaultInterval = new WeakMap(), _TokenBalancesController_websocketActivePollingInterval = new WeakMap(), _TokenBalancesController_chainPollingConfig = new WeakMap(), _TokenBalancesController_intervalPollingTimers = new WeakMap(), _TokenBalancesController_isControllerPollingActive = new WeakMap(), _TokenBalancesController_requestedChainIds = new WeakMap(), _TokenBalancesController_statusChangeDebouncer = new WeakMap(), _TokenBalancesController_getProvider = new WeakMap(), _TokenBalancesController_getNetworkClient = new WeakMap(), _TokenBalancesController_createAccountsApiFetcher = new WeakMap(), _TokenBalancesController_onTokensChanged = new WeakMap(), _TokenBalancesController_onNetworkChanged = new WeakMap(), _TokenBalancesController_onAccountRemoved = new WeakMap(), _TokenBalancesController_onAccountChanged = new WeakMap(), _TokenBalancesController_onAccountActivityBalanceUpdate = new WeakMap(), _TokenBalancesController_onAccountActivityStatusChanged = new WeakMap(), _TokenBalancesController_instances = new WeakSet(),
|
|
423
|
+
_TokenBalancesController_platform = new WeakMap(), _TokenBalancesController_queryAllAccounts = new WeakMap(), _TokenBalancesController_accountsApiChainIds = new WeakMap(), _TokenBalancesController_balanceFetchers = new WeakMap(), _TokenBalancesController_allTokens = new WeakMap(), _TokenBalancesController_detectedTokens = new WeakMap(), _TokenBalancesController_allIgnoredTokens = new WeakMap(), _TokenBalancesController_defaultInterval = new WeakMap(), _TokenBalancesController_websocketActivePollingInterval = new WeakMap(), _TokenBalancesController_chainPollingConfig = new WeakMap(), _TokenBalancesController_intervalPollingTimers = new WeakMap(), _TokenBalancesController_isControllerPollingActive = new WeakMap(), _TokenBalancesController_isUnlocked = new WeakMap(), _TokenBalancesController_requestedChainIds = new WeakMap(), _TokenBalancesController_statusChangeDebouncer = new WeakMap(), _TokenBalancesController_getProvider = new WeakMap(), _TokenBalancesController_getNetworkClient = new WeakMap(), _TokenBalancesController_createAccountsApiFetcher = new WeakMap(), _TokenBalancesController_onTokensChanged = new WeakMap(), _TokenBalancesController_onNetworkChanged = new WeakMap(), _TokenBalancesController_onAccountRemoved = new WeakMap(), _TokenBalancesController_onAccountChanged = new WeakMap(), _TokenBalancesController_onAccountActivityBalanceUpdate = new WeakMap(), _TokenBalancesController_onAccountActivityStatusChanged = new WeakMap(), _TokenBalancesController_instances = new WeakSet(), _TokenBalancesController_subscribeToControllers = function _TokenBalancesController_subscribeToControllers() {
|
|
424
|
+
this.messenger.subscribe('TokensController:stateChange', (tokensState) => {
|
|
425
|
+
__classPrivateFieldGet(this, _TokenBalancesController_onTokensChanged, "f").call(this, tokensState).catch((error) => {
|
|
426
|
+
console.warn('Error handling token state change:', error);
|
|
427
|
+
});
|
|
428
|
+
});
|
|
429
|
+
this.messenger.subscribe('NetworkController:stateChange', __classPrivateFieldGet(this, _TokenBalancesController_onNetworkChanged, "f"));
|
|
430
|
+
this.messenger.subscribe('KeyringController:unlock', () => {
|
|
431
|
+
__classPrivateFieldSet(this, _TokenBalancesController_isUnlocked, true, "f");
|
|
432
|
+
});
|
|
433
|
+
this.messenger.subscribe('KeyringController:lock', () => {
|
|
434
|
+
__classPrivateFieldSet(this, _TokenBalancesController_isUnlocked, false, "f");
|
|
435
|
+
});
|
|
436
|
+
this.messenger.subscribe('KeyringController:accountRemoved', __classPrivateFieldGet(this, _TokenBalancesController_onAccountRemoved, "f"));
|
|
437
|
+
this.messenger.subscribe('AccountsController:selectedEvmAccountChange', __classPrivateFieldGet(this, _TokenBalancesController_onAccountChanged, "f"));
|
|
438
|
+
this.messenger.subscribe('AccountActivityService:balanceUpdated', (event) => {
|
|
439
|
+
__classPrivateFieldGet(this, _TokenBalancesController_onAccountActivityBalanceUpdate, "f").call(this, event).catch((error) => {
|
|
440
|
+
console.warn('Error handling balance update:', error);
|
|
441
|
+
});
|
|
442
|
+
});
|
|
443
|
+
this.messenger.subscribe('AccountActivityService:statusChanged', __classPrivateFieldGet(this, _TokenBalancesController_onAccountActivityStatusChanged, "f").bind(this));
|
|
444
|
+
this.messenger.subscribe('TransactionController:transactionConfirmed', (transactionMeta) => {
|
|
445
|
+
this.updateBalances({
|
|
446
|
+
chainIds: [transactionMeta.chainId],
|
|
447
|
+
}).catch(() => {
|
|
448
|
+
// Silently handle balance update errors
|
|
449
|
+
});
|
|
450
|
+
});
|
|
451
|
+
this.messenger.subscribe('TransactionController:incomingTransactionsReceived', (incomingTransactions) => {
|
|
452
|
+
this.updateBalances({
|
|
453
|
+
chainIds: incomingTransactions.map((tx) => tx.chainId),
|
|
454
|
+
}).catch(() => {
|
|
455
|
+
// Silently handle balance update errors
|
|
456
|
+
});
|
|
457
|
+
});
|
|
458
|
+
}, _TokenBalancesController_registerActions = function _TokenBalancesController_registerActions() {
|
|
459
|
+
this.messenger.registerActionHandler(`TokenBalancesController:updateChainPollingConfigs`, this.updateChainPollingConfigs.bind(this));
|
|
460
|
+
this.messenger.registerActionHandler(`TokenBalancesController:getChainPollingConfig`, this.getChainPollingConfig.bind(this));
|
|
461
|
+
}, _TokenBalancesController_normalizeAccountAddresses = function _TokenBalancesController_normalizeAccountAddresses() {
|
|
462
|
+
var _a;
|
|
646
463
|
const currentState = this.state.tokenBalances;
|
|
647
464
|
const normalizedBalances = {};
|
|
648
|
-
// Iterate through all accounts and normalize to lowercase
|
|
649
465
|
for (const address of Object.keys(currentState)) {
|
|
650
466
|
const lowercaseAddress = address.toLowerCase();
|
|
651
467
|
const accountBalances = currentState[address];
|
|
652
468
|
if (!accountBalances) {
|
|
653
469
|
continue;
|
|
654
470
|
}
|
|
655
|
-
|
|
656
|
-
if (!normalizedBalances[lowercaseAddress]) {
|
|
657
|
-
normalizedBalances[lowercaseAddress] = {};
|
|
658
|
-
}
|
|
659
|
-
// Merge chain data
|
|
471
|
+
normalizedBalances[lowercaseAddress] ?? (normalizedBalances[lowercaseAddress] = {});
|
|
660
472
|
for (const chainId of Object.keys(accountBalances)) {
|
|
661
473
|
const chainIdKey = chainId;
|
|
662
|
-
|
|
663
|
-
normalizedBalances[lowercaseAddress][chainIdKey] = {};
|
|
664
|
-
}
|
|
665
|
-
// Merge token balances (later values override earlier ones if duplicates exist)
|
|
474
|
+
(_a = normalizedBalances[lowercaseAddress])[chainIdKey] ?? (_a[chainIdKey] = {});
|
|
666
475
|
Object.assign(normalizedBalances[lowercaseAddress][chainIdKey], accountBalances[chainIdKey]);
|
|
667
476
|
}
|
|
668
477
|
}
|
|
669
|
-
// Only update if there were changes
|
|
670
478
|
if (Object.keys(currentState).length !==
|
|
671
479
|
Object.keys(normalizedBalances).length ||
|
|
672
480
|
Object.keys(currentState).some((addr) => addr !== addr.toLowerCase())) {
|
|
@@ -680,18 +488,15 @@ _TokenBalancesController_platform = new WeakMap(), _TokenBalancesController_quer
|
|
|
680
488
|
]),
|
|
681
489
|
];
|
|
682
490
|
}, _TokenBalancesController_startIntervalGroupPolling = function _TokenBalancesController_startIntervalGroupPolling(chainIds, immediate = true) {
|
|
683
|
-
// Stop any existing interval timers
|
|
684
491
|
__classPrivateFieldGet(this, _TokenBalancesController_intervalPollingTimers, "f").forEach((timer) => clearInterval(timer));
|
|
685
492
|
__classPrivateFieldGet(this, _TokenBalancesController_intervalPollingTimers, "f").clear();
|
|
686
|
-
// Group chains by their polling intervals
|
|
687
493
|
const intervalGroups = new Map();
|
|
688
494
|
for (const chainId of chainIds) {
|
|
689
495
|
const config = this.getChainPollingConfig(chainId);
|
|
690
|
-
const
|
|
691
|
-
|
|
692
|
-
intervalGroups.set(config.interval,
|
|
496
|
+
const group = intervalGroups.get(config.interval) ?? [];
|
|
497
|
+
group.push(chainId);
|
|
498
|
+
intervalGroups.set(config.interval, group);
|
|
693
499
|
}
|
|
694
|
-
// Start separate polling loop for each interval group
|
|
695
500
|
for (const [interval, chainIdsGroup] of intervalGroups) {
|
|
696
501
|
__classPrivateFieldGet(this, _TokenBalancesController_instances, "m", _TokenBalancesController_startPollingForInterval).call(this, interval, chainIdsGroup, immediate);
|
|
697
502
|
}
|
|
@@ -707,33 +512,205 @@ _TokenBalancesController_platform = new WeakMap(), _TokenBalancesController_quer
|
|
|
707
512
|
console.warn(`Polling failed for chains ${chainIds.join(', ')} with interval ${interval}:`, error);
|
|
708
513
|
}
|
|
709
514
|
};
|
|
710
|
-
// Poll immediately first if requested
|
|
711
515
|
if (immediate) {
|
|
712
516
|
pollFunction().catch((error) => {
|
|
713
517
|
console.warn(`Immediate polling failed for chains ${chainIds.join(', ')}:`, error);
|
|
714
518
|
});
|
|
715
519
|
}
|
|
716
|
-
// Then start regular interval polling
|
|
717
520
|
__classPrivateFieldGet(this, _TokenBalancesController_instances, "m", _TokenBalancesController_setPollingTimer).call(this, interval, chainIds, pollFunction);
|
|
718
521
|
}, _TokenBalancesController_setPollingTimer = function _TokenBalancesController_setPollingTimer(interval, chainIds, pollFunction) {
|
|
719
|
-
// Clear any existing timer for this interval first
|
|
720
|
-
const existingTimer = __classPrivateFieldGet(this, _TokenBalancesController_intervalPollingTimers, "f").get(interval);
|
|
721
|
-
if (existingTimer) {
|
|
722
|
-
clearInterval(existingTimer);
|
|
723
|
-
}
|
|
724
522
|
const timer = setInterval(() => {
|
|
725
523
|
pollFunction().catch((error) => {
|
|
726
524
|
console.warn(`Interval polling failed for chains ${chainIds.join(', ')}:`, error);
|
|
727
525
|
});
|
|
728
526
|
}, interval);
|
|
729
527
|
__classPrivateFieldGet(this, _TokenBalancesController_intervalPollingTimers, "f").set(interval, timer);
|
|
528
|
+
}, _TokenBalancesController_stopAllPolling = function _TokenBalancesController_stopAllPolling() {
|
|
529
|
+
__classPrivateFieldSet(this, _TokenBalancesController_isControllerPollingActive, false, "f");
|
|
530
|
+
__classPrivateFieldSet(this, _TokenBalancesController_requestedChainIds, [], "f");
|
|
531
|
+
__classPrivateFieldGet(this, _TokenBalancesController_intervalPollingTimers, "f").forEach((timer) => clearInterval(timer));
|
|
532
|
+
__classPrivateFieldGet(this, _TokenBalancesController_intervalPollingTimers, "f").clear();
|
|
533
|
+
}, _TokenBalancesController_getTargetChains = function _TokenBalancesController_getTargetChains(chainIds) {
|
|
534
|
+
return chainIds?.length ? chainIds : __classPrivateFieldGet(this, _TokenBalancesController_instances, "m", _TokenBalancesController_chainIdsWithTokens).call(this);
|
|
535
|
+
}, _TokenBalancesController_getAccountsAndJwt = async function _TokenBalancesController_getAccountsAndJwt() {
|
|
536
|
+
const { address: selected } = this.messenger.call('AccountsController:getSelectedAccount');
|
|
537
|
+
const allAccounts = this.messenger.call('AccountsController:listAccounts');
|
|
538
|
+
const jwtToken = await (0, controller_utils_1.safelyExecuteWithTimeout)(() => {
|
|
539
|
+
return this.messenger.call('AuthenticationController:getBearerToken');
|
|
540
|
+
}, false, 5000);
|
|
541
|
+
return {
|
|
542
|
+
selectedAccount: selected,
|
|
543
|
+
allAccounts,
|
|
544
|
+
jwtToken,
|
|
545
|
+
};
|
|
546
|
+
}, _TokenBalancesController_fetchAllBalances = async function _TokenBalancesController_fetchAllBalances({ targetChains, selectedAccount, allAccounts, jwtToken, queryAllAccounts, }) {
|
|
547
|
+
const aggregated = [];
|
|
548
|
+
let remainingChains = [...targetChains];
|
|
549
|
+
for (const fetcher of __classPrivateFieldGet(this, _TokenBalancesController_balanceFetchers, "f")) {
|
|
550
|
+
const supportedChains = remainingChains.filter((chain) => fetcher.supports(chain));
|
|
551
|
+
if (!supportedChains.length) {
|
|
552
|
+
continue;
|
|
553
|
+
}
|
|
554
|
+
try {
|
|
555
|
+
const result = await fetcher.fetch({
|
|
556
|
+
chainIds: supportedChains,
|
|
557
|
+
queryAllAccounts,
|
|
558
|
+
selectedAccount,
|
|
559
|
+
allAccounts,
|
|
560
|
+
jwtToken,
|
|
561
|
+
});
|
|
562
|
+
if (result.balances?.length) {
|
|
563
|
+
aggregated.push(...result.balances);
|
|
564
|
+
const processed = new Set(result.balances.map((b) => b.chainId));
|
|
565
|
+
remainingChains = remainingChains.filter((chain) => !processed.has(chain));
|
|
566
|
+
}
|
|
567
|
+
if (result.unprocessedChainIds?.length) {
|
|
568
|
+
const currentRemaining = [...remainingChains];
|
|
569
|
+
const chainsToAdd = result.unprocessedChainIds.filter((chainId) => supportedChains.includes(chainId) &&
|
|
570
|
+
!currentRemaining.includes(chainId));
|
|
571
|
+
remainingChains.push(...chainsToAdd);
|
|
572
|
+
this.messenger
|
|
573
|
+
.call('TokenDetectionController:detectTokens', {
|
|
574
|
+
chainIds: result.unprocessedChainIds,
|
|
575
|
+
forceRpc: true,
|
|
576
|
+
})
|
|
577
|
+
.catch(() => {
|
|
578
|
+
// Silently handle token detection errors
|
|
579
|
+
});
|
|
580
|
+
}
|
|
581
|
+
}
|
|
582
|
+
catch (error) {
|
|
583
|
+
console.warn(`Balance fetcher failed for chains ${supportedChains.join(', ')}: ${String(error)}`);
|
|
584
|
+
this.messenger
|
|
585
|
+
.call('TokenDetectionController:detectTokens', {
|
|
586
|
+
chainIds: supportedChains,
|
|
587
|
+
forceRpc: true,
|
|
588
|
+
})
|
|
589
|
+
.catch(() => {
|
|
590
|
+
// Silently handle token detection errors
|
|
591
|
+
});
|
|
592
|
+
}
|
|
593
|
+
if (!remainingChains.length) {
|
|
594
|
+
break;
|
|
595
|
+
}
|
|
596
|
+
}
|
|
597
|
+
return aggregated;
|
|
598
|
+
}, _TokenBalancesController_filterByTokenAddresses = function _TokenBalancesController_filterByTokenAddresses(balances, tokenAddresses) {
|
|
599
|
+
if (!tokenAddresses?.length) {
|
|
600
|
+
return balances;
|
|
601
|
+
}
|
|
602
|
+
const lowered = tokenAddresses.map((a) => a.toLowerCase());
|
|
603
|
+
return balances.filter((balance) => lowered.includes(balance.token.toLowerCase()));
|
|
604
|
+
}, _TokenBalancesController_getAccountsToProcess = function _TokenBalancesController_getAccountsToProcess(queryAllAccountsParam, allAccounts, selectedAccount) {
|
|
605
|
+
const effectiveQueryAll = queryAllAccountsParam ?? __classPrivateFieldGet(this, _TokenBalancesController_queryAllAccounts, "f") ?? false;
|
|
606
|
+
if (!effectiveQueryAll) {
|
|
607
|
+
return [selectedAccount];
|
|
608
|
+
}
|
|
609
|
+
return allAccounts.map((account) => account.address);
|
|
610
|
+
}, _TokenBalancesController_applyTokenBalancesToState = function _TokenBalancesController_applyTokenBalancesToState({ prev, targetChains, accountsToProcess, balances, }) {
|
|
611
|
+
return draft(prev, (draftState) => {
|
|
612
|
+
var _a, _b;
|
|
613
|
+
for (const chainId of targetChains) {
|
|
614
|
+
for (const account of accountsToProcess) {
|
|
615
|
+
(_a = draftState.tokenBalances)[account] ?? (_a[account] = {});
|
|
616
|
+
(_b = draftState.tokenBalances[account])[chainId] ?? (_b[chainId] = {});
|
|
617
|
+
const chainTokens = __classPrivateFieldGet(this, _TokenBalancesController_allTokens, "f")[chainId];
|
|
618
|
+
if (chainTokens?.[account]) {
|
|
619
|
+
Object.values(chainTokens[account]).forEach((token) => {
|
|
620
|
+
var _a;
|
|
621
|
+
const tokenAddress = checksum(token.address);
|
|
622
|
+
(_a = draftState.tokenBalances[account][chainId])[tokenAddress] ?? (_a[tokenAddress] = '0x0');
|
|
623
|
+
});
|
|
624
|
+
}
|
|
625
|
+
const detectedChainTokens = __classPrivateFieldGet(this, _TokenBalancesController_detectedTokens, "f")[chainId];
|
|
626
|
+
if (detectedChainTokens?.[account]) {
|
|
627
|
+
Object.values(detectedChainTokens[account]).forEach((token) => {
|
|
628
|
+
var _a;
|
|
629
|
+
const tokenAddress = checksum(token.address);
|
|
630
|
+
(_a = draftState.tokenBalances[account][chainId])[tokenAddress] ?? (_a[tokenAddress] = '0x0');
|
|
631
|
+
});
|
|
632
|
+
}
|
|
633
|
+
}
|
|
634
|
+
}
|
|
635
|
+
balances.forEach(({ success, value, account, token, chainId }) => {
|
|
636
|
+
var _a, _b;
|
|
637
|
+
if (!success || value === undefined) {
|
|
638
|
+
return;
|
|
639
|
+
}
|
|
640
|
+
const lowerCaseAccount = account.toLowerCase();
|
|
641
|
+
const newBalance = (0, controller_utils_1.toHex)(value);
|
|
642
|
+
const tokenAddress = checksum(token);
|
|
643
|
+
const currentBalance = draftState.tokenBalances[lowerCaseAccount]?.[chainId]?.[tokenAddress];
|
|
644
|
+
if (currentBalance !== newBalance) {
|
|
645
|
+
((_b = ((_a = draftState.tokenBalances)[lowerCaseAccount] ?? (_a[lowerCaseAccount] = {})))[chainId] ?? (_b[chainId] = {}))[tokenAddress] = newBalance;
|
|
646
|
+
}
|
|
647
|
+
});
|
|
648
|
+
});
|
|
649
|
+
}, _TokenBalancesController_buildNativeBalanceUpdates = function _TokenBalancesController_buildNativeBalanceUpdates(balances, accountTrackerState) {
|
|
650
|
+
const nativeBalances = balances.filter((balance) => balance.success && balance.token === ZERO_ADDRESS);
|
|
651
|
+
if (!nativeBalances.length) {
|
|
652
|
+
return [];
|
|
653
|
+
}
|
|
654
|
+
return nativeBalances
|
|
655
|
+
.map((balance) => ({
|
|
656
|
+
address: balance.account,
|
|
657
|
+
chainId: balance.chainId,
|
|
658
|
+
balance: balance.value ? (0, controller_utils_1.BNToHex)(balance.value) : '0x0',
|
|
659
|
+
}))
|
|
660
|
+
.filter((update) => {
|
|
661
|
+
const currentBalance = accountTrackerState.accountsByChainId[update.chainId]?.[checksum(update.address)]?.balance;
|
|
662
|
+
return currentBalance !== update.balance;
|
|
663
|
+
});
|
|
664
|
+
}, _TokenBalancesController_buildStakedBalanceUpdates = function _TokenBalancesController_buildStakedBalanceUpdates(balances, accountTrackerState) {
|
|
665
|
+
const stakedBalances = balances.filter((balance) => {
|
|
666
|
+
if (!balance.success || balance.token === ZERO_ADDRESS) {
|
|
667
|
+
return false;
|
|
668
|
+
}
|
|
669
|
+
const stakingContractAddress = AssetsContractController_1.STAKING_CONTRACT_ADDRESS_BY_CHAINID[balance.chainId];
|
|
670
|
+
return (stakingContractAddress &&
|
|
671
|
+
stakingContractAddress.toLowerCase() === balance.token.toLowerCase());
|
|
672
|
+
});
|
|
673
|
+
if (!stakedBalances.length) {
|
|
674
|
+
return [];
|
|
675
|
+
}
|
|
676
|
+
return stakedBalances
|
|
677
|
+
.map((balance) => ({
|
|
678
|
+
address: balance.account,
|
|
679
|
+
chainId: balance.chainId,
|
|
680
|
+
stakedBalance: balance.value ? (0, controller_utils_1.toHex)(balance.value) : '0x0',
|
|
681
|
+
}))
|
|
682
|
+
.filter((update) => {
|
|
683
|
+
const currentStakedBalance = accountTrackerState.accountsByChainId[update.chainId]?.[checksum(update.address)]?.stakedBalance;
|
|
684
|
+
return currentStakedBalance !== update.stakedBalance;
|
|
685
|
+
});
|
|
686
|
+
}, _TokenBalancesController_importUntrackedTokens = async function _TokenBalancesController_importUntrackedTokens(balances) {
|
|
687
|
+
const untrackedTokensByChain = new Map();
|
|
688
|
+
for (const balance of balances) {
|
|
689
|
+
if (!balance.success || balance.token === ZERO_ADDRESS) {
|
|
690
|
+
continue;
|
|
691
|
+
}
|
|
692
|
+
const tokenAddress = checksum(balance.token);
|
|
693
|
+
const account = balance.account.toLowerCase();
|
|
694
|
+
if (!__classPrivateFieldGet(this, _TokenBalancesController_instances, "m", _TokenBalancesController_isTokenTracked).call(this, tokenAddress, account, balance.chainId)) {
|
|
695
|
+
const existing = untrackedTokensByChain.get(balance.chainId) ?? [];
|
|
696
|
+
if (!existing.includes(tokenAddress)) {
|
|
697
|
+
existing.push(tokenAddress);
|
|
698
|
+
untrackedTokensByChain.set(balance.chainId, existing);
|
|
699
|
+
}
|
|
700
|
+
}
|
|
701
|
+
}
|
|
702
|
+
for (const [chainId, tokens] of untrackedTokensByChain) {
|
|
703
|
+
await this.messenger.call('TokenDetectionController:addDetectedTokensViaWs', {
|
|
704
|
+
tokensSlice: tokens,
|
|
705
|
+
chainId,
|
|
706
|
+
});
|
|
707
|
+
}
|
|
730
708
|
}, _TokenBalancesController_isTokenTracked = function _TokenBalancesController_isTokenTracked(tokenAddress, account, chainId) {
|
|
731
|
-
|
|
732
|
-
if (__classPrivateFieldGet(this, _TokenBalancesController_allTokens, "f")?.[chainId]?.[
|
|
709
|
+
const normalizedAccount = account.toLowerCase();
|
|
710
|
+
if (__classPrivateFieldGet(this, _TokenBalancesController_allTokens, "f")?.[chainId]?.[normalizedAccount]?.some((token) => token.address === tokenAddress)) {
|
|
733
711
|
return true;
|
|
734
712
|
}
|
|
735
|
-
|
|
736
|
-
if (__classPrivateFieldGet(this, _TokenBalancesController_allIgnoredTokens, "f")?.[chainId]?.[account.toLowerCase()]?.some((token) => token === tokenAddress)) {
|
|
713
|
+
if (__classPrivateFieldGet(this, _TokenBalancesController_allIgnoredTokens, "f")?.[chainId]?.[normalizedAccount]?.some((token) => token === tokenAddress)) {
|
|
737
714
|
return true;
|
|
738
715
|
}
|
|
739
716
|
return false;
|
|
@@ -743,31 +720,25 @@ _TokenBalancesController_platform = new WeakMap(), _TokenBalancesController_quer
|
|
|
743
720
|
const nativeBalanceUpdates = [];
|
|
744
721
|
for (const update of updates) {
|
|
745
722
|
const { asset, postBalance } = update;
|
|
746
|
-
// Throw if balance update has an error
|
|
747
723
|
if (postBalance.error) {
|
|
748
724
|
throw new Error('Balance update has error');
|
|
749
725
|
}
|
|
750
|
-
// Parse token address from asset type
|
|
751
726
|
const parsed = (0, exports.parseAssetType)(asset.type);
|
|
752
727
|
if (!parsed) {
|
|
753
728
|
throw new Error('Failed to parse asset type');
|
|
754
729
|
}
|
|
755
730
|
const [tokenAddress, isNativeToken] = parsed;
|
|
756
|
-
// Validate token address
|
|
757
731
|
if (!(0, utils_1.isStrictHexString)(tokenAddress) ||
|
|
758
732
|
!(0, controller_utils_1.isValidHexAddress)(tokenAddress)) {
|
|
759
733
|
throw new Error('Invalid token address');
|
|
760
734
|
}
|
|
761
735
|
const checksumTokenAddress = checksum(tokenAddress);
|
|
762
736
|
const isTracked = __classPrivateFieldGet(this, _TokenBalancesController_instances, "m", _TokenBalancesController_isTokenTracked).call(this, checksumTokenAddress, account, chainId);
|
|
763
|
-
// postBalance.amount is in hex format (raw units)
|
|
764
737
|
const balanceHex = postBalance.amount;
|
|
765
|
-
// Add token balance (tracked tokens, ignored tokens, and native tokens all get balance updates)
|
|
766
738
|
tokenBalances.push({
|
|
767
739
|
tokenAddress: checksumTokenAddress,
|
|
768
740
|
balance: balanceHex,
|
|
769
741
|
});
|
|
770
|
-
// Add native balance update if this is a native token
|
|
771
742
|
if (isNativeToken) {
|
|
772
743
|
nativeBalanceUpdates.push({
|
|
773
744
|
address: account,
|
|
@@ -775,7 +746,6 @@ _TokenBalancesController_platform = new WeakMap(), _TokenBalancesController_quer
|
|
|
775
746
|
balance: balanceHex,
|
|
776
747
|
});
|
|
777
748
|
}
|
|
778
|
-
// Handle untracked ERC20 tokens - queue for import
|
|
779
749
|
if (!isNativeToken && !isTracked) {
|
|
780
750
|
newTokens.push(checksumTokenAddress);
|
|
781
751
|
}
|
|
@@ -785,28 +755,18 @@ _TokenBalancesController_platform = new WeakMap(), _TokenBalancesController_quer
|
|
|
785
755
|
const changes = Array.from(__classPrivateFieldGet(this, _TokenBalancesController_statusChangeDebouncer, "f").pendingChanges.entries());
|
|
786
756
|
__classPrivateFieldGet(this, _TokenBalancesController_statusChangeDebouncer, "f").pendingChanges.clear();
|
|
787
757
|
__classPrivateFieldGet(this, _TokenBalancesController_statusChangeDebouncer, "f").timer = null;
|
|
788
|
-
if (changes.length
|
|
758
|
+
if (!changes.length) {
|
|
789
759
|
return;
|
|
790
760
|
}
|
|
791
|
-
// Calculate final polling configurations
|
|
792
761
|
const chainConfigs = {};
|
|
793
762
|
for (const [chainId, status] of changes) {
|
|
794
|
-
// Convert CAIP format (eip155:1) to hex format (0x1)
|
|
795
|
-
// chainId is always in CAIP format from AccountActivityService
|
|
796
763
|
const hexChainId = (0, exports.caipChainIdToHex)(chainId);
|
|
797
|
-
|
|
798
|
-
|
|
799
|
-
|
|
800
|
-
|
|
801
|
-
else {
|
|
802
|
-
// Chain is up - use longer intervals since WebSocket provides real-time updates
|
|
803
|
-
chainConfigs[hexChainId] = {
|
|
804
|
-
interval: __classPrivateFieldGet(this, _TokenBalancesController_websocketActivePollingInterval, "f"),
|
|
805
|
-
};
|
|
806
|
-
}
|
|
764
|
+
chainConfigs[hexChainId] =
|
|
765
|
+
status === 'down'
|
|
766
|
+
? { interval: __classPrivateFieldGet(this, _TokenBalancesController_defaultInterval, "f") }
|
|
767
|
+
: { interval: __classPrivateFieldGet(this, _TokenBalancesController_websocketActivePollingInterval, "f") };
|
|
807
768
|
}
|
|
808
|
-
|
|
809
|
-
const jitterDelay = Math.random() * __classPrivateFieldGet(this, _TokenBalancesController_defaultInterval, "f"); // 0 to default interval
|
|
769
|
+
const jitterDelay = Math.random() * __classPrivateFieldGet(this, _TokenBalancesController_defaultInterval, "f");
|
|
810
770
|
setTimeout(() => {
|
|
811
771
|
this.updateChainPollingConfigs(chainConfigs, { immediateUpdate: true });
|
|
812
772
|
}, jitterDelay);
|