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