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