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