@metamask/assets-controllers 74.0.0 → 74.1.1
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 +50 -1
- package/dist/AccountTrackerController.cjs +222 -84
- package/dist/AccountTrackerController.cjs.map +1 -1
- package/dist/AccountTrackerController.d.cts +7 -1
- package/dist/AccountTrackerController.d.cts.map +1 -1
- package/dist/AccountTrackerController.d.mts +7 -1
- package/dist/AccountTrackerController.d.mts.map +1 -1
- package/dist/AccountTrackerController.mjs +224 -85
- package/dist/AccountTrackerController.mjs.map +1 -1
- package/dist/TokenBalancesController.cjs +17 -14
- package/dist/TokenBalancesController.cjs.map +1 -1
- package/dist/TokenBalancesController.d.cts.map +1 -1
- package/dist/TokenBalancesController.d.mts.map +1 -1
- package/dist/TokenBalancesController.mjs +18 -15
- package/dist/TokenBalancesController.mjs.map +1 -1
- package/dist/rpc-service/rpc-balance-fetcher.cjs +31 -9
- package/dist/rpc-service/rpc-balance-fetcher.cjs.map +1 -1
- package/dist/rpc-service/rpc-balance-fetcher.d.cts.map +1 -1
- package/dist/rpc-service/rpc-balance-fetcher.d.mts.map +1 -1
- package/dist/rpc-service/rpc-balance-fetcher.mjs +32 -10
- package/dist/rpc-service/rpc-balance-fetcher.mjs.map +1 -1
- package/dist/selectors/token-selectors.cjs +24 -9
- package/dist/selectors/token-selectors.cjs.map +1 -1
- package/dist/selectors/token-selectors.d.cts +96 -20
- package/dist/selectors/token-selectors.d.cts.map +1 -1
- package/dist/selectors/token-selectors.d.mts +96 -20
- package/dist/selectors/token-selectors.d.mts.map +1 -1
- package/dist/selectors/token-selectors.mjs +24 -9
- package/dist/selectors/token-selectors.mjs.map +1 -1
- package/package.json +3 -3
|
@@ -32,6 +32,7 @@ const metadata = {
|
|
|
32
32
|
// region: Helper utilities
|
|
33
33
|
const draft = (base, fn) => (0, immer_1.produce)(base, fn);
|
|
34
34
|
const ZERO_ADDRESS = '0x0000000000000000000000000000000000000000';
|
|
35
|
+
const checksum = (addr) => (0, controller_utils_1.toChecksumHexAddress)(addr);
|
|
35
36
|
// endregion
|
|
36
37
|
// ────────────────────────────────────────────────────────────────────────────
|
|
37
38
|
// region: Main controller
|
|
@@ -190,7 +191,11 @@ class TokenBalancesController extends (0, polling_controller_1.StaticIntervalPol
|
|
|
190
191
|
const { allTokens, allDetectedTokens } = this.messagingSystem.call('TokensController:getState');
|
|
191
192
|
__classPrivateFieldSet(this, _TokenBalancesController_allTokens, allTokens, "f");
|
|
192
193
|
__classPrivateFieldSet(this, _TokenBalancesController_detectedTokens, allDetectedTokens, "f");
|
|
193
|
-
this.messagingSystem.subscribe('TokensController:stateChange',
|
|
194
|
+
this.messagingSystem.subscribe('TokensController:stateChange', (tokensState) => {
|
|
195
|
+
__classPrivateFieldGet(this, _TokenBalancesController_onTokensChanged, "f").call(this, tokensState).catch((error) => {
|
|
196
|
+
console.warn('Error handling token state change:', error);
|
|
197
|
+
});
|
|
198
|
+
});
|
|
194
199
|
this.messagingSystem.subscribe('NetworkController:stateChange', __classPrivateFieldGet(this, _TokenBalancesController_onNetworkChanged, "f"));
|
|
195
200
|
this.messagingSystem.subscribe('KeyringController:accountRemoved', __classPrivateFieldGet(this, _TokenBalancesController_onAccountRemoved, "f"));
|
|
196
201
|
}
|
|
@@ -213,14 +218,12 @@ class TokenBalancesController extends (0, polling_controller_1.StaticIntervalPol
|
|
|
213
218
|
continue;
|
|
214
219
|
}
|
|
215
220
|
try {
|
|
216
|
-
const balances = await
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
});
|
|
223
|
-
}, false, this.getIntervalLength());
|
|
221
|
+
const balances = await fetcher.fetch({
|
|
222
|
+
chainIds: supportedChains,
|
|
223
|
+
queryAllAccounts: __classPrivateFieldGet(this, _TokenBalancesController_queryAllAccounts, "f"),
|
|
224
|
+
selectedAccount: selected,
|
|
225
|
+
allAccounts,
|
|
226
|
+
});
|
|
224
227
|
if (balances && balances.length > 0) {
|
|
225
228
|
aggregated.push(...balances);
|
|
226
229
|
// Remove chains that were successfully processed
|
|
@@ -252,7 +255,7 @@ class TokenBalancesController extends (0, polling_controller_1.StaticIntervalPol
|
|
|
252
255
|
if (chainTokens?.[account]) {
|
|
253
256
|
Object.values(chainTokens[account]).forEach((token) => {
|
|
254
257
|
var _a, _b;
|
|
255
|
-
const tokenAddress = token.address;
|
|
258
|
+
const tokenAddress = checksum(token.address);
|
|
256
259
|
((_b = ((_a = d.tokenBalances)[account] ?? (_a[account] = {})))[chainId] ?? (_b[chainId] = {}))[tokenAddress] = '0x0';
|
|
257
260
|
});
|
|
258
261
|
}
|
|
@@ -261,7 +264,7 @@ class TokenBalancesController extends (0, polling_controller_1.StaticIntervalPol
|
|
|
261
264
|
if (detectedChainTokens?.[account]) {
|
|
262
265
|
Object.values(detectedChainTokens[account]).forEach((token) => {
|
|
263
266
|
var _a, _b;
|
|
264
|
-
const tokenAddress = token.address;
|
|
267
|
+
const tokenAddress = checksum(token.address);
|
|
265
268
|
((_b = ((_a = d.tokenBalances)[account] ?? (_a[account] = {})))[chainId] ?? (_b[chainId] = {}))[tokenAddress] = '0x0';
|
|
266
269
|
});
|
|
267
270
|
}
|
|
@@ -271,7 +274,7 @@ class TokenBalancesController extends (0, polling_controller_1.StaticIntervalPol
|
|
|
271
274
|
aggregated.forEach(({ success, value, account, token, chainId }) => {
|
|
272
275
|
var _a, _b;
|
|
273
276
|
if (success && value !== undefined) {
|
|
274
|
-
((_b = ((_a = d.tokenBalances)[account] ?? (_a[account] = {})))[chainId] ?? (_b[chainId] = {}))[token] =
|
|
277
|
+
((_b = ((_a = d.tokenBalances)[account] ?? (_a[account] = {})))[chainId] ?? (_b[chainId] = {}))[checksum(token)] =
|
|
275
278
|
(0, controller_utils_1.toHex)(value);
|
|
276
279
|
}
|
|
277
280
|
});
|
|
@@ -284,7 +287,7 @@ class TokenBalancesController extends (0, polling_controller_1.StaticIntervalPol
|
|
|
284
287
|
const balanceUpdates = nativeBalances.map((balance) => ({
|
|
285
288
|
address: balance.account,
|
|
286
289
|
chainId: balance.chainId,
|
|
287
|
-
balance: balance.value?.toString() ?? '
|
|
290
|
+
balance: balance.value?.toString() ?? '0x0',
|
|
288
291
|
}));
|
|
289
292
|
this.messagingSystem.call('AccountTrackerController:updateNativeBalances', balanceUpdates);
|
|
290
293
|
}
|
|
@@ -300,7 +303,7 @@ class TokenBalancesController extends (0, polling_controller_1.StaticIntervalPol
|
|
|
300
303
|
const stakedBalanceUpdates = stakedBalances.map((balance) => ({
|
|
301
304
|
address: balance.account,
|
|
302
305
|
chainId: balance.chainId,
|
|
303
|
-
stakedBalance: balance.value
|
|
306
|
+
stakedBalance: balance.value ? (0, controller_utils_1.toHex)(balance.value) : '0x0',
|
|
304
307
|
}));
|
|
305
308
|
this.messagingSystem.call('AccountTrackerController:updateStakedBalances', stakedBalanceUpdates);
|
|
306
309
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"TokenBalancesController.cjs","sourceRoot":"","sources":["../src/TokenBalancesController.ts"],"names":[],"mappings":";;;;;;;;;;;;;;;AAAA,wDAAwD;AAUxD,iEAIoC;AAQpC,qEAA+E;AAM/E,2CAAoD;AACpD,iCAAgC;AAChC,mCAAiC;AAMjC,6EAAiF;AACjF,gGAI4D;AAC5D,+EAAsE;AAUtE,MAAM,UAAU,GAAG,yBAAkC,CAAC;AACtD,MAAM,mBAAmB,GAAG,MAAO,CAAC,CAAC,YAAY;AAEjD,MAAM,QAAQ,GAAG;IACf,aAAa,EAAE,EAAE,OAAO,EAAE,IAAI,EAAE,SAAS,EAAE,KAAK,EAAE;CACnD,CAAC;AAqEF,YAAY;AAEZ,+EAA+E;AAC/E,2BAA2B;AAC3B,MAAM,KAAK,GAAG,CAAI,IAAO,EAAE,EAAkB,EAAK,EAAE,CAAC,IAAA,eAAO,EAAC,IAAI,EAAE,EAAE,CAAC,CAAC;AAEvE,MAAM,YAAY,GAChB,4CAA+D,CAAC;AAClE,YAAY;AAEZ,+EAA+E;AAC/E,0BAA0B;AAC1B,MAAa,uBAAwB,SAAQ,IAAA,oDAA+B,GAM3E;IASC,YAAY,EACV,SAAS,EACT,QAAQ,GAAG,mBAAmB,EAC9B,KAAK,GAAG,EAAE,EACV,qBAAqB,GAAG,IAAI,EAC5B,cAAc,GAAG,KAAK,EACtB,qBAAqB,GAAG,GAAG,EAAE,CAAC,IAAI,GACH;QAC/B,KAAK,CAAC;YACJ,IAAI,EAAE,UAAU;YAChB,SAAS;YACT,QAAQ;YACR,KAAK,EAAE,EAAE,aAAa,EAAE,EAAE,EAAE,GAAG,KAAK,EAAE;SACvC,CAAC,CAAC;;QArBI,4DAA2B;QAE3B,2DAAmC;QAE5C,6CAAiD,EAAE,EAAC;QAEpD,kDAA8D,EAAE,EAAC;QA8DxD,+CAAe,CAAC,OAAmB,EAAgB,EAAE;YAC5D,MAAM,EAAE,8BAA8B,EAAE,GAAG,IAAI,CAAC,eAAe,CAAC,IAAI,CAClE,4BAA4B,CAC7B,CAAC;YACF,MAAM,GAAG,GAAG,8BAA8B,CAAC,OAAO,CAAC,CAAC;YACpD,MAAM,EAAE,eAAe,EAAE,GAAG,GAAG,CAAC,YAAY,CAAC,GAAG,CAAC,uBAAuB,CAAC,CAAC;YAC1E,MAAM,MAAM,GAAG,IAAI,CAAC,eAAe,CAAC,IAAI,CACtC,wCAAwC,EACxC,eAAe,CAChB,CAAC;YACF,OAAO,IAAI,wBAAY,CAAC,MAAM,CAAC,QAAQ,CAAC,CAAC;QAC3C,CAAC,EAAC;QAEO,oDAAoB,CAAC,OAAmB,EAAE,EAAE;YACnD,MAAM,EAAE,8BAA8B,EAAE,GAAG,IAAI,CAAC,eAAe,CAAC,IAAI,CAClE,4BAA4B,CAC7B,CAAC;YACF,MAAM,GAAG,GAAG,8BAA8B,CAAC,OAAO,CAAC,CAAC;YACpD,MAAM,EAAE,eAAe,EAAE,GAAG,GAAG,CAAC,YAAY,CAAC,GAAG,CAAC,uBAAuB,CAAC,CAAC;YAC1E,OAAO,IAAI,CAAC,eAAe,CAAC,IAAI,CAC9B,wCAAwC,EACxC,eAAe,CAChB,CAAC;QACJ,CAAC,EAAC;QAwKO,mDAAmB,KAAK,EAAE,KAA4B,EAAE,EAAE;YACjE,MAAM,OAAO,GAAiB,EAAE,CAAC;YACjC,IAAI,UAAU,GAAG,KAAK,CAAC;YAEvB,yCAAyC;YACzC,MAAM,kBAAkB,GAAG,IAAI,GAAG,EAAc,CAAC;YACjD,KAAK,MAAM,OAAO,IAAI,MAAM,CAAC,IAAI,CAAC,IAAI,CAAC,KAAK,CAAC,aAAa,CAAC,EAAE;gBAC3D,MAAM,UAAU,GAAG,OAA0B,CAAC;gBAC9C,KAAK,MAAM,OAAO,IAAI,MAAM,CAAC,IAAI,CAC/B,IAAI,CAAC,KAAK,CAAC,aAAa,CAAC,UAAU,CAAC,IAAI,EAAE,CAC3C,EAAE;oBACD,kBAAkB,CAAC,GAAG,CAAC,OAAqB,CAAC,CAAC;iBAC/C;aACF;YAED,iFAAiF;YACjF,MAAM,gBAAgB,GAAG,IAAI,GAAG,CAAC;gBAC/B,GAAG,MAAM,CAAC,IAAI,CAAC,KAAK,CAAC,SAAS,CAAC;gBAC/B,GAAG,MAAM,CAAC,IAAI,CAAC,KAAK,CAAC,iBAAiB,CAAC;aACxC,CAAC,CAAC;YAEH,2FAA2F;YAC3F,MAAM,gBAAgB,GAAG,KAAK,CAAC,IAAI,CAAC,gBAAgB,CAAC,CAAC,MAAM,CAAC,CAAC,OAAO,EAAE,EAAE;gBACvE,MAAM,EAAE,GAAG,OAAqB,CAAC;gBAEjC,MAAM,YAAY,GAChB,CAAC,KAAK,CAAC,SAAS,CAAC,EAAE,CAAC,IAAI,MAAM,CAAC,IAAI,CAAC,KAAK,CAAC,SAAS,CAAC,EAAE,CAAC,CAAC,CAAC,MAAM,GAAG,CAAC,CAAC;oBACpE,CAAC,KAAK,CAAC,iBAAiB,CAAC,EAAE,CAAC;wBAC1B,MAAM,CAAC,IAAI,CAAC,KAAK,CAAC,iBAAiB,CAAC,EAAE,CAAC,CAAC,CAAC,MAAM,GAAG,CAAC,CAAC,CAAC;gBACzD,MAAM,eAAe,GACnB,CAAC,uBAAA,IAAI,0CAAW,CAAC,EAAE,CAAC,IAAI,MAAM,CAAC,IAAI,CAAC,uBAAA,IAAI,0CAAW,CAAC,EAAE,CAAC,CAAC,CAAC,MAAM,GAAG,CAAC,CAAC;oBACpE,CAAC,uBAAA,IAAI,+CAAgB,CAAC,EAAE,CAAC;wBACvB,MAAM,CAAC,IAAI,CAAC,uBAAA,IAAI,+CAAgB,CAAC,EAAE,CAAC,CAAC,CAAC,MAAM,GAAG,CAAC,CAAC,CAAC;gBAEtD,mDAAmD;gBACnD,MAAM,cAAc,GAClB,CAAC,IAAA,gBAAO,EAAC,KAAK,CAAC,SAAS,CAAC,EAAE,CAAC,EAAE,uBAAA,IAAI,0CAAW,CAAC,EAAE,CAAC,CAAC;oBAClD,CAAC,IAAA,gBAAO,EAAC,KAAK,CAAC,iBAAiB,CAAC,EAAE,CAAC,EAAE,uBAAA,IAAI,+CAAgB,CAAC,EAAE,CAAC,CAAC,CAAC;gBAElE,2EAA2E;gBAC3E,OAAO,cAAc,IAAI,CAAC,CAAC,eAAe,IAAI,YAAY,CAAC,CAAC;YAC9D,CAAC,CAAC,CAAC;YAEH,IAAI,gBAAgB,CAAC,MAAM,KAAK,CAAC,EAAE;gBACjC,kDAAkD;gBAClD,uBAAA,IAAI,sCAAc,KAAK,CAAC,SAAS,MAAA,CAAC;gBAClC,uBAAA,IAAI,2CAAmB,KAAK,CAAC,iBAAiB,MAAA,CAAC;gBAC/C,OAAO;aACR;YAED,2DAA2D;YAC3D,IAAI,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,EAAE;gBAChB,KAAK,MAAM,OAAO,IAAI,gBAAgB,EAAE;oBACtC,MAAM,EAAE,GAAG,OAAqB,CAAC;oBACjC,MAAM,YAAY,GAChB,CAAC,KAAK,CAAC,SAAS,CAAC,EAAE,CAAC;wBAClB,MAAM,CAAC,IAAI,CAAC,KAAK,CAAC,SAAS,CAAC,EAAE,CAAC,CAAC,CAAC,MAAM,GAAG,CAAC,CAAC;wBAC9C,CAAC,KAAK,CAAC,iBAAiB,CAAC,EAAE,CAAC;4BAC1B,MAAM,CAAC,IAAI,CAAC,KAAK,CAAC,iBAAiB,CAAC,EAAE,CAAC,CAAC,CAAC,MAAM,GAAG,CAAC,CAAC,CAAC;oBACzD,MAAM,eAAe,GACnB,CAAC,uBAAA,IAAI,0CAAW,CAAC,EAAE,CAAC;wBAClB,MAAM,CAAC,IAAI,CAAC,uBAAA,IAAI,0CAAW,CAAC,EAAE,CAAC,CAAC,CAAC,MAAM,GAAG,CAAC,CAAC;wBAC9C,CAAC,uBAAA,IAAI,+CAAgB,CAAC,EAAE,CAAC;4BACvB,MAAM,CAAC,IAAI,CAAC,uBAAA,IAAI,+CAAgB,CAAC,EAAE,CAAC,CAAC,CAAC,MAAM,GAAG,CAAC,CAAC,CAAC;oBAEtD,IACE,CAAC,IAAA,gBAAO,EAAC,KAAK,CAAC,SAAS,CAAC,EAAE,CAAC,EAAE,uBAAA,IAAI,0CAAW,CAAC,EAAE,CAAC,CAAC;wBAClD,CAAC,IAAA,gBAAO,EAAC,KAAK,CAAC,iBAAiB,CAAC,EAAE,CAAC,EAAE,uBAAA,IAAI,+CAAgB,CAAC,EAAE,CAAC,CAAC,EAC/D;wBACA,IAAI,YAAY,EAAE;4BAChB,yDAAyD;4BACzD,OAAO,CAAC,IAAI,CAAC,EAAE,CAAC,CAAC;yBAClB;6BAAM,IAAI,eAAe,EAAE;4BAC1B,0EAA0E;4BAC1E,KAAK,MAAM,OAAO,IAAI,MAAM,CAAC,IAAI,CAAC,CAAC,CAAC,aAAa,CAAC,EAAE;gCAClD,MAAM,UAAU,GAAG,OAA0B,CAAC;gCAC9C,IAAI,CAAC,CAAC,aAAa,CAAC,UAAU,CAAC,EAAE,CAAC,EAAE,CAAC,EAAE;oCACrC,CAAC,CAAC,aAAa,CAAC,UAAU,CAAC,CAAC,EAAE,CAAC,GAAG,EAAE,CAAC;oCACrC,UAAU,GAAG,IAAI,CAAC;iCACnB;6BACF;yBACF;qBACF;iBACF;YACH,CAAC,CAAC,CAAC;YAEH,uBAAA,IAAI,sCAAc,KAAK,CAAC,SAAS,MAAA,CAAC;YAClC,uBAAA,IAAI,2CAAmB,KAAK,CAAC,iBAAiB,MAAA,CAAC;YAE/C,wGAAwG;YACxG,IAAI,OAAO,CAAC,MAAM,IAAI,CAAC,UAAU,EAAE;gBACjC,IAAI,CAAC,cAAc,CAAC,EAAE,QAAQ,EAAE,OAAO,EAAE,CAAC,CAAC,KAAK,CAAC,CAAC,KAAK,EAAE,EAAE;oBACzD,OAAO,CAAC,IAAI,CAAC,6CAA6C,EAAE,KAAK,CAAC,CAAC;gBACrE,CAAC,CAAC,CAAC;aACJ;QACH,CAAC,EAAC;QAEO,oDAAoB,CAAC,KAAmB,EAAE,EAAE;YACnD,sEAAsE;YACtE,MAAM,eAAe,GAAG,IAAI,GAAG,CAC7B,MAAM,CAAC,IAAI,CAAC,KAAK,CAAC,8BAA8B,CAAC,CAClD,CAAC;YAEF,gDAAgD;YAChD,MAAM,oBAAoB,GAAG,IAAI,GAAG,EAAU,CAAC;YAC/C,KAAK,MAAM,OAAO,IAAI,MAAM,CAAC,IAAI,CAAC,IAAI,CAAC,KAAK,CAAC,aAAa,CAAC,EAAE;gBAC3D,MAAM,UAAU,GAAG,OAA0B,CAAC;gBAC9C,KAAK,MAAM,OAAO,IAAI,MAAM,CAAC,IAAI,CAC/B,IAAI,CAAC,KAAK,CAAC,aAAa,CAAC,UAAU,CAAC,IAAI,EAAE,CAC3C,EAAE;oBACD,oBAAoB,CAAC,GAAG,CAAC,OAAO,CAAC,CAAC;iBACnC;aACF;YAED,kCAAkC;YAClC,MAAM,eAAe,GAAG,KAAK,CAAC,IAAI,CAAC,oBAAoB,CAAC,CAAC,MAAM,CAC7D,CAAC,OAAO,EAAE,EAAE,CAAC,CAAC,eAAe,CAAC,GAAG,CAAC,OAAO,CAAC,CAC3C,CAAC;YAEF,IAAI,eAAe,CAAC,MAAM,GAAG,CAAC,EAAE;gBAC9B,IAAI,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,EAAE;oBAChB,2DAA2D;oBAC3D,KAAK,MAAM,OAAO,IAAI,MAAM,CAAC,IAAI,CAAC,CAAC,CAAC,aAAa,CAAC,EAAE;wBAClD,MAAM,UAAU,GAAG,OAA0B,CAAC;wBAC9C,KAAK,MAAM,cAAc,IAAI,eAAe,EAAE;4BAC5C,MAAM,UAAU,GAAG,cAA4B,CAAC;4BAChD,IAAI,CAAC,CAAC,aAAa,CAAC,UAAU,CAAC,EAAE,CAAC,UAAU,CAAC,EAAE;gCAC7C,OAAO,CAAC,CAAC,aAAa,CAAC,UAAU,CAAC,CAAC,UAAU,CAAC,CAAC;6BAChD;yBACF;qBACF;gBACH,CAAC,CAAC,CAAC;aACJ;QACH,CAAC,EAAC;QAEO,oDAAoB,CAAC,IAAY,EAAE,EAAE;YAC5C,IAAI,CAAC,IAAA,yBAAiB,EAAC,IAAI,CAAC,IAAI,CAAC,IAAA,oCAAiB,EAAC,IAAI,CAAC,EAAE;gBACxD,OAAO;aACR;YACD,IAAI,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,EAAE;gBAChB,OAAO,CAAC,CAAC,aAAa,CAAC,IAAuB,CAAC,CAAC;YAClD,CAAC,CAAC,CAAC;QACL,CAAC,EAAC;QA1XA,uBAAA,IAAI,6CAAqB,qBAAqB,MAAA,CAAC;QAE/C,+CAA+C;QAC/C,uBAAA,IAAI,4CAAoB;YACtB,GAAG,CAAC,cAAc,IAAI,qBAAqB,EAAE;gBAC3C,CAAC,CAAC,CAAC,IAAI,+CAAyB,CAAC,WAAW,EAAE,uBAAA,IAAI,4CAAa,CAAC,CAAC;gBACjE,CAAC,CAAC,EAAE,CAAC;YACP,IAAI,uCAAiB,CAAC,uBAAA,IAAI,4CAAa,EAAE,uBAAA,IAAI,iDAAkB,EAAE,GAAG,EAAE,CAAC,CAAC;gBACtE,SAAS,EAAE,uBAAA,IAAI,0CAAW;gBAC1B,iBAAiB,EAAE,uBAAA,IAAI,+CAAgB;aACxC,CAAC,CAAC;SACJ,MAAA,CAAC;QAEF,IAAI,CAAC,iBAAiB,CAAC,QAAQ,CAAC,CAAC;QAEjC,sCAAsC;QACtC,MAAM,EAAE,SAAS,EAAE,iBAAiB,EAAE,GAAG,IAAI,CAAC,eAAe,CAAC,IAAI,CAChE,2BAA2B,CAC5B,CAAC;QACF,uBAAA,IAAI,sCAAc,SAAS,MAAA,CAAC;QAC5B,uBAAA,IAAI,2CAAmB,iBAAiB,MAAA,CAAC;QAEzC,IAAI,CAAC,eAAe,CAAC,SAAS,CAC5B,8BAA8B,EAC9B,uBAAA,IAAI,gDAAiB,CACtB,CAAC;QACF,IAAI,CAAC,eAAe,CAAC,SAAS,CAC5B,+BAA+B,EAC/B,uBAAA,IAAI,iDAAkB,CACvB,CAAC;QACF,IAAI,CAAC,eAAe,CAAC,SAAS,CAC5B,kCAAkC,EAClC,uBAAA,IAAI,iDAAkB,CACvB,CAAC;IACJ,CAAC;IAoCD,KAAK,CAAC,YAAY,CAAC,EAAE,QAAQ,EAA8B;QACzD,MAAM,IAAI,CAAC,cAAc,CAAC,EAAE,QAAQ,EAAE,CAAC,CAAC;IAC1C,CAAC;IAED,KAAK,CAAC,cAAc,CAAC,EAAE,QAAQ,KAAkC,EAAE;QACjE,MAAM,YAAY,GAAG,QAAQ,IAAI,uBAAA,IAAI,uFAAoB,MAAxB,IAAI,CAAsB,CAAC;QAC5D,IAAI,CAAC,YAAY,CAAC,MAAM,EAAE;YACxB,OAAO;SACR;QAED,MAAM,EAAE,OAAO,EAAE,QAAQ,EAAE,GAAG,IAAI,CAAC,eAAe,CAAC,IAAI,CACrD,uCAAuC,CACxC,CAAC;QACF,MAAM,WAAW,GAAG,IAAI,CAAC,eAAe,CAAC,IAAI,CAC3C,iCAAiC,CAClC,CAAC;QAEF,MAAM,UAAU,GAAuB,EAAE,CAAC;QAC1C,IAAI,eAAe,GAAG,CAAC,GAAG,YAAY,CAAC,CAAC;QAExC,oEAAoE;QACpE,KAAK,MAAM,OAAO,IAAI,uBAAA,IAAI,gDAAiB,EAAE;YAC3C,MAAM,eAAe,GAAG,eAAe,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,EAAE,CACnD,OAAO,CAAC,QAAQ,CAAC,CAAC,CAAC,CACpB,CAAC;YACF,IAAI,CAAC,eAAe,CAAC,MAAM,EAAE;gBAC3B,SAAS;aACV;YAED,IAAI;gBACF,MAAM,QAAQ,GAAG,MAAM,IAAA,2CAAwB,EAC7C,KAAK,IAAI,EAAE;oBACT,OAAO,MAAM,OAAO,CAAC,KAAK,CAAC;wBACzB,QAAQ,EAAE,eAAe;wBACzB,gBAAgB,EAAE,uBAAA,IAAI,iDAAkB;wBACxC,eAAe,EAAE,QAA2B;wBAC5C,WAAW;qBACZ,CAAC,CAAC;gBACL,CAAC,EACD,KAAK,EACL,IAAI,CAAC,iBAAiB,EAAE,CACzB,CAAC;gBAEF,IAAI,QAAQ,IAAI,QAAQ,CAAC,MAAM,GAAG,CAAC,EAAE;oBACnC,UAAU,CAAC,IAAI,CAAC,GAAG,QAAQ,CAAC,CAAC;oBAC7B,iDAAiD;oBACjD,MAAM,eAAe,GAAG,IAAI,GAAG,CAAC,QAAQ,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,OAAO,CAAC,CAAC,CAAC;oBAChE,eAAe,GAAG,eAAe,CAAC,MAAM,CACtC,CAAC,KAAK,EAAE,EAAE,CAAC,CAAC,eAAe,CAAC,GAAG,CAAC,KAAK,CAAC,CACvC,CAAC;iBACH;aACF;YAAC,OAAO,KAAK,EAAE;gBACd,OAAO,CAAC,IAAI,CACV,qCAAqC,eAAe,CAAC,IAAI,CAAC,IAAI,CAAC,KAAK,MAAM,CAAC,KAAK,CAAC,EAAE,CACpF,CAAC;gBACF,sCAAsC;aACvC;YAED,iDAAiD;YACjD,IAAI,eAAe,CAAC,MAAM,KAAK,CAAC,EAAE;gBAChC,MAAM;aACP;SACF;QAED,sCAAsC;QACtC,MAAM,iBAAiB,GAAG,uBAAA,IAAI,iDAAkB;YAC9C,CAAC,CAAC,WAAW,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,OAA0B,CAAC;YACtD,CAAC,CAAC,CAAC,QAA2B,CAAC,CAAC;QAElC,MAAM,IAAI,GAAG,IAAI,CAAC,KAAK,CAAC;QACxB,MAAM,IAAI,GAAG,KAAK,CAAC,IAAI,EAAE,CAAC,CAAC,EAAE,EAAE;YAC7B,mEAAmE;YACnE,+CAA+C;YAC/C,KAAK,MAAM,OAAO,IAAI,YAAY,EAAE;gBAClC,KAAK,MAAM,OAAO,IAAI,iBAAiB,EAAE;oBACvC,mCAAmC;oBACnC,MAAM,WAAW,GAAG,uBAAA,IAAI,0CAAW,CAAC,OAAO,CAAC,CAAC;oBAC7C,IAAI,WAAW,EAAE,CAAC,OAAO,CAAC,EAAE;wBAC1B,MAAM,CAAC,MAAM,CAAC,WAAW,CAAC,OAAO,CAAC,CAAC,CAAC,OAAO,CACzC,CAAC,KAA0B,EAAE,EAAE;;4BAC7B,MAAM,YAAY,GAAG,KAAK,CAAC,OAA0B,CAAC;4BACtD,OAAC,OAAC,CAAC,CAAC,aAAa,EAAC,OAAO,SAAP,OAAO,IAAM,EAAE,EAAC,EAAC,OAAO,SAAP,OAAO,IAAM,EAAE,EAAC,CACjD,YAAY,CACb,GAAG,KAAK,CAAC;wBACZ,CAAC,CACF,CAAC;qBACH;oBAED,2CAA2C;oBAC3C,MAAM,mBAAmB,GAAG,uBAAA,IAAI,+CAAgB,CAAC,OAAO,CAAC,CAAC;oBAC1D,IAAI,mBAAmB,EAAE,CAAC,OAAO,CAAC,EAAE;wBAClC,MAAM,CAAC,MAAM,CAAC,mBAAmB,CAAC,OAAO,CAAC,CAAC,CAAC,OAAO,CACjD,CAAC,KAA0B,EAAE,EAAE;;4BAC7B,MAAM,YAAY,GAAG,KAAK,CAAC,OAA0B,CAAC;4BACtD,OAAC,OAAC,CAAC,CAAC,aAAa,EAAC,OAAO,SAAP,OAAO,IAAM,EAAE,EAAC,EAAC,OAAO,SAAP,OAAO,IAAM,EAAE,EAAC,CACjD,YAAY,CACb,GAAG,KAAK,CAAC;wBACZ,CAAC,CACF,CAAC;qBACH;iBACF;aACF;YAED,2DAA2D;YAC3D,UAAU,CAAC,OAAO,CAAC,CAAC,EAAE,OAAO,EAAE,KAAK,EAAE,OAAO,EAAE,KAAK,EAAE,OAAO,EAAE,EAAE,EAAE;;gBACjE,IAAI,OAAO,IAAI,KAAK,KAAK,SAAS,EAAE;oBAClC,OAAC,OAAC,CAAC,CAAC,aAAa,EAAC,OAAO,SAAP,OAAO,IAAM,EAAE,EAAC,EAAC,OAAO,SAAP,OAAO,IAAM,EAAE,EAAC,CAAC,KAAK,CAAC;wBACxD,IAAA,wBAAK,EAAC,KAAK,CAAC,CAAC;iBAChB;YACH,CAAC,CAAC,CAAC;QACL,CAAC,CAAC,CAAC;QAEH,IAAI,CAAC,IAAA,gBAAO,EAAC,IAAI,EAAE,IAAI,CAAC,EAAE;YACxB,IAAI,CAAC,MAAM,CAAC,GAAG,EAAE,CAAC,IAAI,CAAC,CAAC;YAExB,MAAM,cAAc,GAAG,UAAU,CAAC,MAAM,CACtC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,OAAO,IAAI,CAAC,CAAC,KAAK,KAAK,YAAY,CAC7C,CAAC;YAEF,kFAAkF;YAClF,IAAI,cAAc,CAAC,MAAM,GAAG,CAAC,EAAE;gBAC7B,MAAM,cAAc,GAAG,cAAc,CAAC,GAAG,CAAC,CAAC,OAAO,EAAE,EAAE,CAAC,CAAC;oBACtD,OAAO,EAAE,OAAO,CAAC,OAAO;oBACxB,OAAO,EAAE,OAAO,CAAC,OAAO;oBACxB,OAAO,EAAE,OAAO,CAAC,KAAK,EAAE,QAAQ,EAAE,IAAI,GAAG;iBAC1C,CAAC,CAAC,CAAC;gBAEJ,IAAI,CAAC,eAAe,CAAC,IAAI,CACvB,+CAA+C,EAC/C,cAAc,CACf,CAAC;aACH;YAED,+CAA+C;YAC/C,MAAM,wBAAwB,GAAG,MAAM,CAAC,MAAM,CAC5C,8DAAmC,CACpC,CAAC,GAAG,CAAC,CAAC,IAAI,EAAE,EAAE,CAAC,IAAI,CAAC,WAAW,EAAE,CAAC,CAAC;YAEpC,uFAAuF;YACvF,MAAM,cAAc,GAAG,UAAU,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,EAAE;gBAC7C,OAAO,CACL,CAAC,CAAC,OAAO;oBACT,CAAC,CAAC,KAAK,KAAK,YAAY;oBACxB,wBAAwB,CAAC,QAAQ,CAAC,CAAC,CAAC,KAAK,CAAC,WAAW,EAAE,CAAC,CACzD,CAAC;YACJ,CAAC,CAAC,CAAC;YAEH,IAAI,cAAc,CAAC,MAAM,GAAG,CAAC,EAAE;gBAC7B,MAAM,oBAAoB,GAAG,cAAc,CAAC,GAAG,CAAC,CAAC,OAAO,EAAE,EAAE,CAAC,CAAC;oBAC5D,OAAO,EAAE,OAAO,CAAC,OAAO;oBACxB,OAAO,EAAE,OAAO,CAAC,OAAO;oBACxB,aAAa,EAAE,OAAO,CAAC,KAAK,EAAE,QAAQ,EAAE,IAAI,GAAG;iBAChD,CAAC,CAAC,CAAC;gBAEJ,IAAI,CAAC,eAAe,CAAC,IAAI,CACvB,+CAA+C,EAC/C,oBAAoB,CACrB,CAAC;aACH;SACF;IACH,CAAC;IAED,UAAU;QACR,IAAI,CAAC,MAAM,CAAC,GAAG,EAAE,CAAC,CAAC,EAAE,aAAa,EAAE,EAAE,EAAE,CAAC,CAAC,CAAC;IAC7C,CAAC;CAiJF;AAzZD,0DAyZC;;IAtVG,OAAO;QACL,GAAG,IAAI,GAAG,CAAC;YACT,GAAG,MAAM,CAAC,IAAI,CAAC,uBAAA,IAAI,0CAAW,CAAC;YAC/B,GAAG,MAAM,CAAC,IAAI,CAAC,uBAAA,IAAI,+CAAgB,CAAC;SACrC,CAAC;KACa,CAAC;AACpB,CAAC;AAkVH,kBAAe,uBAAuB,CAAC","sourcesContent":["import { Web3Provider } from '@ethersproject/providers';\nimport type {\n AccountsControllerGetSelectedAccountAction,\n AccountsControllerListAccountsAction,\n} from '@metamask/accounts-controller';\nimport type {\n ControllerGetStateAction,\n ControllerStateChangeEvent,\n RestrictedMessenger,\n} from '@metamask/base-controller';\nimport {\n isValidHexAddress,\n safelyExecuteWithTimeout,\n toHex,\n} from '@metamask/controller-utils';\nimport type { KeyringControllerAccountRemovedEvent } from '@metamask/keyring-controller';\nimport type {\n NetworkControllerGetNetworkClientByIdAction,\n NetworkControllerGetStateAction,\n NetworkControllerStateChangeEvent,\n NetworkState,\n} from '@metamask/network-controller';\nimport { StaticIntervalPollingController } from '@metamask/polling-controller';\nimport type {\n PreferencesControllerGetStateAction,\n PreferencesControllerStateChangeEvent,\n} from '@metamask/preferences-controller';\nimport type { Hex } from '@metamask/utils';\nimport { isStrictHexString } from '@metamask/utils';\nimport { produce } from 'immer';\nimport { isEqual } from 'lodash';\n\nimport type {\n AccountTrackerUpdateNativeBalancesAction,\n AccountTrackerUpdateStakedBalancesAction,\n} from './AccountTrackerController';\nimport { STAKING_CONTRACT_ADDRESS_BY_CHAINID } from './AssetsContractController';\nimport {\n AccountsApiBalanceFetcher,\n type BalanceFetcher,\n type ProcessedBalance,\n} from './multi-chain-accounts-service/api-balance-fetcher';\nimport { RpcBalanceFetcher } from './rpc-service/rpc-balance-fetcher';\nimport type {\n TokensControllerGetStateAction,\n TokensControllerState,\n TokensControllerStateChangeEvent,\n} from './TokensController';\n\nexport type ChainIdHex = Hex;\nexport type ChecksumAddress = Hex;\n\nconst CONTROLLER = 'TokenBalancesController' as const;\nconst DEFAULT_INTERVAL_MS = 180_000; // 3 minutes\n\nconst metadata = {\n tokenBalances: { persist: true, anonymous: false },\n};\n\n// account → chain → token → balance\nexport type TokenBalances = Record<\n ChecksumAddress,\n Record<ChainIdHex, Record<ChecksumAddress, Hex>>\n>;\n\nexport type TokenBalancesControllerState = {\n tokenBalances: TokenBalances;\n};\n\nexport type TokenBalancesControllerGetStateAction = ControllerGetStateAction<\n typeof CONTROLLER,\n TokenBalancesControllerState\n>;\n\nexport type TokenBalancesControllerActions =\n TokenBalancesControllerGetStateAction;\n\nexport type TokenBalancesControllerStateChangeEvent =\n ControllerStateChangeEvent<typeof CONTROLLER, TokenBalancesControllerState>;\n\nexport type NativeBalanceEvent = {\n type: `${typeof CONTROLLER}:updatedNativeBalance`;\n payload: unknown[];\n};\n\nexport type TokenBalancesControllerEvents =\n | TokenBalancesControllerStateChangeEvent\n | NativeBalanceEvent;\n\nexport type AllowedActions =\n | NetworkControllerGetNetworkClientByIdAction\n | NetworkControllerGetStateAction\n | TokensControllerGetStateAction\n | PreferencesControllerGetStateAction\n | AccountsControllerGetSelectedAccountAction\n | AccountsControllerListAccountsAction\n | AccountTrackerUpdateNativeBalancesAction\n | AccountTrackerUpdateStakedBalancesAction;\n\nexport type AllowedEvents =\n | TokensControllerStateChangeEvent\n | PreferencesControllerStateChangeEvent\n | NetworkControllerStateChangeEvent\n | KeyringControllerAccountRemovedEvent;\n\nexport type TokenBalancesControllerMessenger = RestrictedMessenger<\n typeof CONTROLLER,\n TokenBalancesControllerActions | AllowedActions,\n TokenBalancesControllerEvents | AllowedEvents,\n AllowedActions['type'],\n AllowedEvents['type']\n>;\n\nexport type TokenBalancesControllerOptions = {\n messenger: TokenBalancesControllerMessenger;\n interval?: number;\n state?: Partial<TokenBalancesControllerState>;\n /** When `true`, balances for *all* known accounts are queried. */\n queryMultipleAccounts?: boolean;\n /** Enable Accounts‑API strategy (if supported chain). */\n useAccountsAPI?: boolean;\n /** Disable external HTTP calls (privacy / offline mode). */\n allowExternalServices?: () => boolean;\n /** Custom logger. */\n log?: (...args: unknown[]) => void;\n};\n// endregion\n\n// ────────────────────────────────────────────────────────────────────────────\n// region: Helper utilities\nconst draft = <T>(base: T, fn: (d: T) => void): T => produce(base, fn);\n\nconst ZERO_ADDRESS =\n '0x0000000000000000000000000000000000000000' as ChecksumAddress;\n// endregion\n\n// ────────────────────────────────────────────────────────────────────────────\n// region: Main controller\nexport class TokenBalancesController extends StaticIntervalPollingController<{\n chainIds: ChainIdHex[];\n}>()<\n typeof CONTROLLER,\n TokenBalancesControllerState,\n TokenBalancesControllerMessenger\n> {\n readonly #queryAllAccounts: boolean;\n\n readonly #balanceFetchers: BalanceFetcher[];\n\n #allTokens: TokensControllerState['allTokens'] = {};\n\n #detectedTokens: TokensControllerState['allDetectedTokens'] = {};\n\n constructor({\n messenger,\n interval = DEFAULT_INTERVAL_MS,\n state = {},\n queryMultipleAccounts = true,\n useAccountsAPI = false,\n allowExternalServices = () => true,\n }: TokenBalancesControllerOptions) {\n super({\n name: CONTROLLER,\n messenger,\n metadata,\n state: { tokenBalances: {}, ...state },\n });\n\n this.#queryAllAccounts = queryMultipleAccounts;\n\n // Strategy order: API first, then RPC fallback\n this.#balanceFetchers = [\n ...(useAccountsAPI && allowExternalServices()\n ? [new AccountsApiBalanceFetcher('extension', this.#getProvider)]\n : []),\n new RpcBalanceFetcher(this.#getProvider, this.#getNetworkClient, () => ({\n allTokens: this.#allTokens,\n allDetectedTokens: this.#detectedTokens,\n })),\n ];\n\n this.setIntervalLength(interval);\n\n // initial token state & subscriptions\n const { allTokens, allDetectedTokens } = this.messagingSystem.call(\n 'TokensController:getState',\n );\n this.#allTokens = allTokens;\n this.#detectedTokens = allDetectedTokens;\n\n this.messagingSystem.subscribe(\n 'TokensController:stateChange',\n this.#onTokensChanged,\n );\n this.messagingSystem.subscribe(\n 'NetworkController:stateChange',\n this.#onNetworkChanged,\n );\n this.messagingSystem.subscribe(\n 'KeyringController:accountRemoved',\n this.#onAccountRemoved,\n );\n }\n\n #chainIdsWithTokens(): ChainIdHex[] {\n return [\n ...new Set([\n ...Object.keys(this.#allTokens),\n ...Object.keys(this.#detectedTokens),\n ]),\n ] as ChainIdHex[];\n }\n\n readonly #getProvider = (chainId: ChainIdHex): Web3Provider => {\n const { networkConfigurationsByChainId } = this.messagingSystem.call(\n 'NetworkController:getState',\n );\n const cfg = networkConfigurationsByChainId[chainId];\n const { networkClientId } = cfg.rpcEndpoints[cfg.defaultRpcEndpointIndex];\n const client = this.messagingSystem.call(\n 'NetworkController:getNetworkClientById',\n networkClientId,\n );\n return new Web3Provider(client.provider);\n };\n\n readonly #getNetworkClient = (chainId: ChainIdHex) => {\n const { networkConfigurationsByChainId } = this.messagingSystem.call(\n 'NetworkController:getState',\n );\n const cfg = networkConfigurationsByChainId[chainId];\n const { networkClientId } = cfg.rpcEndpoints[cfg.defaultRpcEndpointIndex];\n return this.messagingSystem.call(\n 'NetworkController:getNetworkClientById',\n networkClientId,\n );\n };\n\n async _executePoll({ chainIds }: { chainIds: ChainIdHex[] }) {\n await this.updateBalances({ chainIds });\n }\n\n async updateBalances({ chainIds }: { chainIds?: ChainIdHex[] } = {}) {\n const targetChains = chainIds ?? this.#chainIdsWithTokens();\n if (!targetChains.length) {\n return;\n }\n\n const { address: selected } = this.messagingSystem.call(\n 'AccountsController:getSelectedAccount',\n );\n const allAccounts = this.messagingSystem.call(\n 'AccountsController:listAccounts',\n );\n\n const aggregated: ProcessedBalance[] = [];\n let remainingChains = [...targetChains];\n\n // Try each fetcher in order, removing successfully processed chains\n for (const fetcher of this.#balanceFetchers) {\n const supportedChains = remainingChains.filter((c) =>\n fetcher.supports(c),\n );\n if (!supportedChains.length) {\n continue;\n }\n\n try {\n const balances = await safelyExecuteWithTimeout(\n async () => {\n return await fetcher.fetch({\n chainIds: supportedChains,\n queryAllAccounts: this.#queryAllAccounts,\n selectedAccount: selected as ChecksumAddress,\n allAccounts,\n });\n },\n false,\n this.getIntervalLength(),\n );\n\n if (balances && balances.length > 0) {\n aggregated.push(...balances);\n // Remove chains that were successfully processed\n const processedChains = new Set(balances.map((b) => b.chainId));\n remainingChains = remainingChains.filter(\n (chain) => !processedChains.has(chain),\n );\n }\n } catch (error) {\n console.warn(\n `Balance fetcher failed for chains ${supportedChains.join(', ')}: ${String(error)}`,\n );\n // Continue to next fetcher (fallback)\n }\n\n // If all chains have been processed, break early\n if (remainingChains.length === 0) {\n break;\n }\n }\n\n // Determine which accounts to process\n const accountsToProcess = this.#queryAllAccounts\n ? allAccounts.map((a) => a.address as ChecksumAddress)\n : [selected as ChecksumAddress];\n\n const prev = this.state;\n const next = draft(prev, (d) => {\n // First, initialize all tokens from allTokens state with balance 0\n // for the accounts and chains we're processing\n for (const chainId of targetChains) {\n for (const account of accountsToProcess) {\n // Initialize tokens from allTokens\n const chainTokens = this.#allTokens[chainId];\n if (chainTokens?.[account]) {\n Object.values(chainTokens[account]).forEach(\n (token: { address: string }) => {\n const tokenAddress = token.address as ChecksumAddress;\n ((d.tokenBalances[account] ??= {})[chainId] ??= {})[\n tokenAddress\n ] = '0x0';\n },\n );\n }\n\n // Initialize tokens from allDetectedTokens\n const detectedChainTokens = this.#detectedTokens[chainId];\n if (detectedChainTokens?.[account]) {\n Object.values(detectedChainTokens[account]).forEach(\n (token: { address: string }) => {\n const tokenAddress = token.address as ChecksumAddress;\n ((d.tokenBalances[account] ??= {})[chainId] ??= {})[\n tokenAddress\n ] = '0x0';\n },\n );\n }\n }\n }\n\n // Then update with actual fetched balances where available\n aggregated.forEach(({ success, value, account, token, chainId }) => {\n if (success && value !== undefined) {\n ((d.tokenBalances[account] ??= {})[chainId] ??= {})[token] =\n toHex(value);\n }\n });\n });\n\n if (!isEqual(prev, next)) {\n this.update(() => next);\n\n const nativeBalances = aggregated.filter(\n (r) => r.success && r.token === ZERO_ADDRESS,\n );\n\n // Update native token balances in a single batch operation for better performance\n if (nativeBalances.length > 0) {\n const balanceUpdates = nativeBalances.map((balance) => ({\n address: balance.account,\n chainId: balance.chainId,\n balance: balance.value?.toString() ?? '0',\n }));\n\n this.messagingSystem.call(\n 'AccountTrackerController:updateNativeBalances',\n balanceUpdates,\n );\n }\n\n // Get staking contract addresses for filtering\n const stakingContractAddresses = Object.values(\n STAKING_CONTRACT_ADDRESS_BY_CHAINID,\n ).map((addr) => addr.toLowerCase());\n\n // Filter and update staked balances in a single batch operation for better performance\n const stakedBalances = aggregated.filter((r) => {\n return (\n r.success &&\n r.token !== ZERO_ADDRESS &&\n stakingContractAddresses.includes(r.token.toLowerCase())\n );\n });\n\n if (stakedBalances.length > 0) {\n const stakedBalanceUpdates = stakedBalances.map((balance) => ({\n address: balance.account,\n chainId: balance.chainId,\n stakedBalance: balance.value?.toString() ?? '0',\n }));\n\n this.messagingSystem.call(\n 'AccountTrackerController:updateStakedBalances',\n stakedBalanceUpdates,\n );\n }\n }\n }\n\n resetState() {\n this.update(() => ({ tokenBalances: {} }));\n }\n\n readonly #onTokensChanged = async (state: TokensControllerState) => {\n const changed: ChainIdHex[] = [];\n let hasChanges = false;\n\n // Get chains that have existing balances\n const chainsWithBalances = new Set<ChainIdHex>();\n for (const address of Object.keys(this.state.tokenBalances)) {\n const addressKey = address as ChecksumAddress;\n for (const chainId of Object.keys(\n this.state.tokenBalances[addressKey] || {},\n )) {\n chainsWithBalances.add(chainId as ChainIdHex);\n }\n }\n\n // Only process chains that are explicitly mentioned in the incoming state change\n const incomingChainIds = new Set([\n ...Object.keys(state.allTokens),\n ...Object.keys(state.allDetectedTokens),\n ]);\n\n // Only proceed if there are actual changes to chains that have balances or are being added\n const relevantChainIds = Array.from(incomingChainIds).filter((chainId) => {\n const id = chainId as ChainIdHex;\n\n const hasTokensNow =\n (state.allTokens[id] && Object.keys(state.allTokens[id]).length > 0) ||\n (state.allDetectedTokens[id] &&\n Object.keys(state.allDetectedTokens[id]).length > 0);\n const hadTokensBefore =\n (this.#allTokens[id] && Object.keys(this.#allTokens[id]).length > 0) ||\n (this.#detectedTokens[id] &&\n Object.keys(this.#detectedTokens[id]).length > 0);\n\n // Check if there's an actual change in token state\n const hasTokenChange =\n !isEqual(state.allTokens[id], this.#allTokens[id]) ||\n !isEqual(state.allDetectedTokens[id], this.#detectedTokens[id]);\n\n // Process chains that have actual changes OR are new chains getting tokens\n return hasTokenChange || (!hadTokensBefore && hasTokensNow);\n });\n\n if (relevantChainIds.length === 0) {\n // No relevant changes, just update internal state\n this.#allTokens = state.allTokens;\n this.#detectedTokens = state.allDetectedTokens;\n return;\n }\n\n // Handle both cleanup and updates in a single state update\n this.update((s) => {\n for (const chainId of relevantChainIds) {\n const id = chainId as ChainIdHex;\n const hasTokensNow =\n (state.allTokens[id] &&\n Object.keys(state.allTokens[id]).length > 0) ||\n (state.allDetectedTokens[id] &&\n Object.keys(state.allDetectedTokens[id]).length > 0);\n const hadTokensBefore =\n (this.#allTokens[id] &&\n Object.keys(this.#allTokens[id]).length > 0) ||\n (this.#detectedTokens[id] &&\n Object.keys(this.#detectedTokens[id]).length > 0);\n\n if (\n !isEqual(state.allTokens[id], this.#allTokens[id]) ||\n !isEqual(state.allDetectedTokens[id], this.#detectedTokens[id])\n ) {\n if (hasTokensNow) {\n // Chain still has tokens - mark for async balance update\n changed.push(id);\n } else if (hadTokensBefore) {\n // Chain had tokens before but doesn't now - clean up balances immediately\n for (const address of Object.keys(s.tokenBalances)) {\n const addressKey = address as ChecksumAddress;\n if (s.tokenBalances[addressKey]?.[id]) {\n s.tokenBalances[addressKey][id] = {};\n hasChanges = true;\n }\n }\n }\n }\n }\n });\n\n this.#allTokens = state.allTokens;\n this.#detectedTokens = state.allDetectedTokens;\n\n // Only update balances for chains that still have tokens (and only if we haven't already updated state)\n if (changed.length && !hasChanges) {\n this.updateBalances({ chainIds: changed }).catch((error) => {\n console.warn('Error updating balances after token change:', error);\n });\n }\n };\n\n readonly #onNetworkChanged = (state: NetworkState) => {\n // Check if any networks were removed by comparing with previous state\n const currentNetworks = new Set(\n Object.keys(state.networkConfigurationsByChainId),\n );\n\n // Get all networks that currently have balances\n const networksWithBalances = new Set<string>();\n for (const address of Object.keys(this.state.tokenBalances)) {\n const addressKey = address as ChecksumAddress;\n for (const network of Object.keys(\n this.state.tokenBalances[addressKey] || {},\n )) {\n networksWithBalances.add(network);\n }\n }\n\n // Find networks that were removed\n const removedNetworks = Array.from(networksWithBalances).filter(\n (network) => !currentNetworks.has(network),\n );\n\n if (removedNetworks.length > 0) {\n this.update((s) => {\n // Remove balances for all accounts on the deleted networks\n for (const address of Object.keys(s.tokenBalances)) {\n const addressKey = address as ChecksumAddress;\n for (const removedNetwork of removedNetworks) {\n const networkKey = removedNetwork as ChainIdHex;\n if (s.tokenBalances[addressKey]?.[networkKey]) {\n delete s.tokenBalances[addressKey][networkKey];\n }\n }\n }\n });\n }\n };\n\n readonly #onAccountRemoved = (addr: string) => {\n if (!isStrictHexString(addr) || !isValidHexAddress(addr)) {\n return;\n }\n this.update((s) => {\n delete s.tokenBalances[addr as ChecksumAddress];\n });\n };\n}\n\nexport default TokenBalancesController;\n"]}
|
|
1
|
+
{"version":3,"file":"TokenBalancesController.cjs","sourceRoot":"","sources":["../src/TokenBalancesController.ts"],"names":[],"mappings":"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","sourcesContent":["import { Web3Provider } from '@ethersproject/providers';\nimport type {\n AccountsControllerGetSelectedAccountAction,\n AccountsControllerListAccountsAction,\n} from '@metamask/accounts-controller';\nimport type {\n ControllerGetStateAction,\n ControllerStateChangeEvent,\n RestrictedMessenger,\n} from '@metamask/base-controller';\nimport {\n isValidHexAddress,\n toChecksumHexAddress,\n toHex,\n} from '@metamask/controller-utils';\nimport type { KeyringControllerAccountRemovedEvent } from '@metamask/keyring-controller';\nimport type {\n NetworkControllerGetNetworkClientByIdAction,\n NetworkControllerGetStateAction,\n NetworkControllerStateChangeEvent,\n NetworkState,\n} from '@metamask/network-controller';\nimport { StaticIntervalPollingController } from '@metamask/polling-controller';\nimport type {\n PreferencesControllerGetStateAction,\n PreferencesControllerStateChangeEvent,\n} from '@metamask/preferences-controller';\nimport type { Hex } from '@metamask/utils';\nimport { isStrictHexString } from '@metamask/utils';\nimport { produce } from 'immer';\nimport { isEqual } from 'lodash';\n\nimport type {\n AccountTrackerUpdateNativeBalancesAction,\n AccountTrackerUpdateStakedBalancesAction,\n} from './AccountTrackerController';\nimport { STAKING_CONTRACT_ADDRESS_BY_CHAINID } from './AssetsContractController';\nimport {\n AccountsApiBalanceFetcher,\n type BalanceFetcher,\n type ProcessedBalance,\n} from './multi-chain-accounts-service/api-balance-fetcher';\nimport { RpcBalanceFetcher } from './rpc-service/rpc-balance-fetcher';\nimport type {\n TokensControllerGetStateAction,\n TokensControllerState,\n TokensControllerStateChangeEvent,\n} from './TokensController';\n\nexport type ChainIdHex = Hex;\nexport type ChecksumAddress = Hex;\n\nconst CONTROLLER = 'TokenBalancesController' as const;\nconst DEFAULT_INTERVAL_MS = 180_000; // 3 minutes\n\nconst metadata = {\n tokenBalances: { persist: true, anonymous: false },\n};\n\n// account → chain → token → balance\nexport type TokenBalances = Record<\n ChecksumAddress,\n Record<ChainIdHex, Record<ChecksumAddress, Hex>>\n>;\n\nexport type TokenBalancesControllerState = {\n tokenBalances: TokenBalances;\n};\n\nexport type TokenBalancesControllerGetStateAction = ControllerGetStateAction<\n typeof CONTROLLER,\n TokenBalancesControllerState\n>;\n\nexport type TokenBalancesControllerActions =\n TokenBalancesControllerGetStateAction;\n\nexport type TokenBalancesControllerStateChangeEvent =\n ControllerStateChangeEvent<typeof CONTROLLER, TokenBalancesControllerState>;\n\nexport type NativeBalanceEvent = {\n type: `${typeof CONTROLLER}:updatedNativeBalance`;\n payload: unknown[];\n};\n\nexport type TokenBalancesControllerEvents =\n | TokenBalancesControllerStateChangeEvent\n | NativeBalanceEvent;\n\nexport type AllowedActions =\n | NetworkControllerGetNetworkClientByIdAction\n | NetworkControllerGetStateAction\n | TokensControllerGetStateAction\n | PreferencesControllerGetStateAction\n | AccountsControllerGetSelectedAccountAction\n | AccountsControllerListAccountsAction\n | AccountTrackerUpdateNativeBalancesAction\n | AccountTrackerUpdateStakedBalancesAction;\n\nexport type AllowedEvents =\n | TokensControllerStateChangeEvent\n | PreferencesControllerStateChangeEvent\n | NetworkControllerStateChangeEvent\n | KeyringControllerAccountRemovedEvent;\n\nexport type TokenBalancesControllerMessenger = RestrictedMessenger<\n typeof CONTROLLER,\n TokenBalancesControllerActions | AllowedActions,\n TokenBalancesControllerEvents | AllowedEvents,\n AllowedActions['type'],\n AllowedEvents['type']\n>;\n\nexport type TokenBalancesControllerOptions = {\n messenger: TokenBalancesControllerMessenger;\n interval?: number;\n state?: Partial<TokenBalancesControllerState>;\n /** When `true`, balances for *all* known accounts are queried. */\n queryMultipleAccounts?: boolean;\n /** Enable Accounts‑API strategy (if supported chain). */\n useAccountsAPI?: boolean;\n /** Disable external HTTP calls (privacy / offline mode). */\n allowExternalServices?: () => boolean;\n /** Custom logger. */\n log?: (...args: unknown[]) => void;\n};\n// endregion\n\n// ────────────────────────────────────────────────────────────────────────────\n// region: Helper utilities\nconst draft = <T>(base: T, fn: (d: T) => void): T => produce(base, fn);\n\nconst ZERO_ADDRESS =\n '0x0000000000000000000000000000000000000000' as ChecksumAddress;\n\nconst checksum = (addr: string): ChecksumAddress =>\n toChecksumHexAddress(addr) as ChecksumAddress;\n// endregion\n\n// ────────────────────────────────────────────────────────────────────────────\n// region: Main controller\nexport class TokenBalancesController extends StaticIntervalPollingController<{\n chainIds: ChainIdHex[];\n}>()<\n typeof CONTROLLER,\n TokenBalancesControllerState,\n TokenBalancesControllerMessenger\n> {\n readonly #queryAllAccounts: boolean;\n\n readonly #balanceFetchers: BalanceFetcher[];\n\n #allTokens: TokensControllerState['allTokens'] = {};\n\n #detectedTokens: TokensControllerState['allDetectedTokens'] = {};\n\n constructor({\n messenger,\n interval = DEFAULT_INTERVAL_MS,\n state = {},\n queryMultipleAccounts = true,\n useAccountsAPI = false,\n allowExternalServices = () => true,\n }: TokenBalancesControllerOptions) {\n super({\n name: CONTROLLER,\n messenger,\n metadata,\n state: { tokenBalances: {}, ...state },\n });\n\n this.#queryAllAccounts = queryMultipleAccounts;\n\n // Strategy order: API first, then RPC fallback\n this.#balanceFetchers = [\n ...(useAccountsAPI && allowExternalServices()\n ? [new AccountsApiBalanceFetcher('extension', this.#getProvider)]\n : []),\n new RpcBalanceFetcher(this.#getProvider, this.#getNetworkClient, () => ({\n allTokens: this.#allTokens,\n allDetectedTokens: this.#detectedTokens,\n })),\n ];\n\n this.setIntervalLength(interval);\n\n // initial token state & subscriptions\n const { allTokens, allDetectedTokens } = this.messagingSystem.call(\n 'TokensController:getState',\n );\n this.#allTokens = allTokens;\n this.#detectedTokens = allDetectedTokens;\n\n this.messagingSystem.subscribe(\n 'TokensController:stateChange',\n (tokensState: TokensControllerState) => {\n this.#onTokensChanged(tokensState).catch((error) => {\n console.warn('Error handling token state change:', error);\n });\n },\n );\n this.messagingSystem.subscribe(\n 'NetworkController:stateChange',\n this.#onNetworkChanged,\n );\n this.messagingSystem.subscribe(\n 'KeyringController:accountRemoved',\n this.#onAccountRemoved,\n );\n }\n\n #chainIdsWithTokens(): ChainIdHex[] {\n return [\n ...new Set([\n ...Object.keys(this.#allTokens),\n ...Object.keys(this.#detectedTokens),\n ]),\n ] as ChainIdHex[];\n }\n\n readonly #getProvider = (chainId: ChainIdHex): Web3Provider => {\n const { networkConfigurationsByChainId } = this.messagingSystem.call(\n 'NetworkController:getState',\n );\n const cfg = networkConfigurationsByChainId[chainId];\n const { networkClientId } = cfg.rpcEndpoints[cfg.defaultRpcEndpointIndex];\n const client = this.messagingSystem.call(\n 'NetworkController:getNetworkClientById',\n networkClientId,\n );\n return new Web3Provider(client.provider);\n };\n\n readonly #getNetworkClient = (chainId: ChainIdHex) => {\n const { networkConfigurationsByChainId } = this.messagingSystem.call(\n 'NetworkController:getState',\n );\n const cfg = networkConfigurationsByChainId[chainId];\n const { networkClientId } = cfg.rpcEndpoints[cfg.defaultRpcEndpointIndex];\n return this.messagingSystem.call(\n 'NetworkController:getNetworkClientById',\n networkClientId,\n );\n };\n\n async _executePoll({ chainIds }: { chainIds: ChainIdHex[] }) {\n await this.updateBalances({ chainIds });\n }\n\n async updateBalances({ chainIds }: { chainIds?: ChainIdHex[] } = {}) {\n const targetChains = chainIds ?? this.#chainIdsWithTokens();\n if (!targetChains.length) {\n return;\n }\n\n const { address: selected } = this.messagingSystem.call(\n 'AccountsController:getSelectedAccount',\n );\n const allAccounts = this.messagingSystem.call(\n 'AccountsController:listAccounts',\n );\n\n const aggregated: ProcessedBalance[] = [];\n let remainingChains = [...targetChains];\n\n // Try each fetcher in order, removing successfully processed chains\n for (const fetcher of this.#balanceFetchers) {\n const supportedChains = remainingChains.filter((c) =>\n fetcher.supports(c),\n );\n if (!supportedChains.length) {\n continue;\n }\n\n try {\n const balances = await fetcher.fetch({\n chainIds: supportedChains,\n queryAllAccounts: this.#queryAllAccounts,\n selectedAccount: selected as ChecksumAddress,\n allAccounts,\n });\n\n if (balances && balances.length > 0) {\n aggregated.push(...balances);\n // Remove chains that were successfully processed\n const processedChains = new Set(balances.map((b) => b.chainId));\n remainingChains = remainingChains.filter(\n (chain) => !processedChains.has(chain),\n );\n }\n } catch (error) {\n console.warn(\n `Balance fetcher failed for chains ${supportedChains.join(', ')}: ${String(error)}`,\n );\n // Continue to next fetcher (fallback)\n }\n\n // If all chains have been processed, break early\n if (remainingChains.length === 0) {\n break;\n }\n }\n\n // Determine which accounts to process\n const accountsToProcess = this.#queryAllAccounts\n ? allAccounts.map((a) => a.address as ChecksumAddress)\n : [selected as ChecksumAddress];\n\n const prev = this.state;\n const next = draft(prev, (d) => {\n // First, initialize all tokens from allTokens state with balance 0\n // for the accounts and chains we're processing\n for (const chainId of targetChains) {\n for (const account of accountsToProcess) {\n // Initialize tokens from allTokens\n const chainTokens = this.#allTokens[chainId];\n if (chainTokens?.[account]) {\n Object.values(chainTokens[account]).forEach(\n (token: { address: string }) => {\n const tokenAddress = checksum(token.address);\n ((d.tokenBalances[account] ??= {})[chainId] ??= {})[\n tokenAddress\n ] = '0x0';\n },\n );\n }\n\n // Initialize tokens from allDetectedTokens\n const detectedChainTokens = this.#detectedTokens[chainId];\n if (detectedChainTokens?.[account]) {\n Object.values(detectedChainTokens[account]).forEach(\n (token: { address: string }) => {\n const tokenAddress = checksum(token.address);\n ((d.tokenBalances[account] ??= {})[chainId] ??= {})[\n tokenAddress\n ] = '0x0';\n },\n );\n }\n }\n }\n\n // Then update with actual fetched balances where available\n aggregated.forEach(({ success, value, account, token, chainId }) => {\n if (success && value !== undefined) {\n ((d.tokenBalances[account] ??= {})[chainId] ??= {})[checksum(token)] =\n toHex(value);\n }\n });\n });\n\n if (!isEqual(prev, next)) {\n this.update(() => next);\n\n const nativeBalances = aggregated.filter(\n (r) => r.success && r.token === ZERO_ADDRESS,\n );\n\n // Update native token balances in a single batch operation for better performance\n if (nativeBalances.length > 0) {\n const balanceUpdates = nativeBalances.map((balance) => ({\n address: balance.account,\n chainId: balance.chainId,\n balance: balance.value?.toString() ?? '0x0',\n }));\n\n this.messagingSystem.call(\n 'AccountTrackerController:updateNativeBalances',\n balanceUpdates,\n );\n }\n\n // Get staking contract addresses for filtering\n const stakingContractAddresses = Object.values(\n STAKING_CONTRACT_ADDRESS_BY_CHAINID,\n ).map((addr) => addr.toLowerCase());\n\n // Filter and update staked balances in a single batch operation for better performance\n const stakedBalances = aggregated.filter((r) => {\n return (\n r.success &&\n r.token !== ZERO_ADDRESS &&\n stakingContractAddresses.includes(r.token.toLowerCase())\n );\n });\n\n if (stakedBalances.length > 0) {\n const stakedBalanceUpdates = stakedBalances.map((balance) => ({\n address: balance.account,\n chainId: balance.chainId,\n stakedBalance: balance.value ? toHex(balance.value) : '0x0',\n }));\n\n this.messagingSystem.call(\n 'AccountTrackerController:updateStakedBalances',\n stakedBalanceUpdates,\n );\n }\n }\n }\n\n resetState() {\n this.update(() => ({ tokenBalances: {} }));\n }\n\n readonly #onTokensChanged = async (state: TokensControllerState) => {\n const changed: ChainIdHex[] = [];\n let hasChanges = false;\n\n // Get chains that have existing balances\n const chainsWithBalances = new Set<ChainIdHex>();\n for (const address of Object.keys(this.state.tokenBalances)) {\n const addressKey = address as ChecksumAddress;\n for (const chainId of Object.keys(\n this.state.tokenBalances[addressKey] || {},\n )) {\n chainsWithBalances.add(chainId as ChainIdHex);\n }\n }\n\n // Only process chains that are explicitly mentioned in the incoming state change\n const incomingChainIds = new Set([\n ...Object.keys(state.allTokens),\n ...Object.keys(state.allDetectedTokens),\n ]);\n\n // Only proceed if there are actual changes to chains that have balances or are being added\n const relevantChainIds = Array.from(incomingChainIds).filter((chainId) => {\n const id = chainId as ChainIdHex;\n\n const hasTokensNow =\n (state.allTokens[id] && Object.keys(state.allTokens[id]).length > 0) ||\n (state.allDetectedTokens[id] &&\n Object.keys(state.allDetectedTokens[id]).length > 0);\n const hadTokensBefore =\n (this.#allTokens[id] && Object.keys(this.#allTokens[id]).length > 0) ||\n (this.#detectedTokens[id] &&\n Object.keys(this.#detectedTokens[id]).length > 0);\n\n // Check if there's an actual change in token state\n const hasTokenChange =\n !isEqual(state.allTokens[id], this.#allTokens[id]) ||\n !isEqual(state.allDetectedTokens[id], this.#detectedTokens[id]);\n\n // Process chains that have actual changes OR are new chains getting tokens\n return hasTokenChange || (!hadTokensBefore && hasTokensNow);\n });\n\n if (relevantChainIds.length === 0) {\n // No relevant changes, just update internal state\n this.#allTokens = state.allTokens;\n this.#detectedTokens = state.allDetectedTokens;\n return;\n }\n\n // Handle both cleanup and updates in a single state update\n this.update((s) => {\n for (const chainId of relevantChainIds) {\n const id = chainId as ChainIdHex;\n const hasTokensNow =\n (state.allTokens[id] &&\n Object.keys(state.allTokens[id]).length > 0) ||\n (state.allDetectedTokens[id] &&\n Object.keys(state.allDetectedTokens[id]).length > 0);\n const hadTokensBefore =\n (this.#allTokens[id] &&\n Object.keys(this.#allTokens[id]).length > 0) ||\n (this.#detectedTokens[id] &&\n Object.keys(this.#detectedTokens[id]).length > 0);\n\n if (\n !isEqual(state.allTokens[id], this.#allTokens[id]) ||\n !isEqual(state.allDetectedTokens[id], this.#detectedTokens[id])\n ) {\n if (hasTokensNow) {\n // Chain still has tokens - mark for async balance update\n changed.push(id);\n } else if (hadTokensBefore) {\n // Chain had tokens before but doesn't now - clean up balances immediately\n for (const address of Object.keys(s.tokenBalances)) {\n const addressKey = address as ChecksumAddress;\n if (s.tokenBalances[addressKey]?.[id]) {\n s.tokenBalances[addressKey][id] = {};\n hasChanges = true;\n }\n }\n }\n }\n }\n });\n\n this.#allTokens = state.allTokens;\n this.#detectedTokens = state.allDetectedTokens;\n\n // Only update balances for chains that still have tokens (and only if we haven't already updated state)\n if (changed.length && !hasChanges) {\n this.updateBalances({ chainIds: changed }).catch((error) => {\n console.warn('Error updating balances after token change:', error);\n });\n }\n };\n\n readonly #onNetworkChanged = (state: NetworkState) => {\n // Check if any networks were removed by comparing with previous state\n const currentNetworks = new Set(\n Object.keys(state.networkConfigurationsByChainId),\n );\n\n // Get all networks that currently have balances\n const networksWithBalances = new Set<string>();\n for (const address of Object.keys(this.state.tokenBalances)) {\n const addressKey = address as ChecksumAddress;\n for (const network of Object.keys(\n this.state.tokenBalances[addressKey] || {},\n )) {\n networksWithBalances.add(network);\n }\n }\n\n // Find networks that were removed\n const removedNetworks = Array.from(networksWithBalances).filter(\n (network) => !currentNetworks.has(network),\n );\n\n if (removedNetworks.length > 0) {\n this.update((s) => {\n // Remove balances for all accounts on the deleted networks\n for (const address of Object.keys(s.tokenBalances)) {\n const addressKey = address as ChecksumAddress;\n for (const removedNetwork of removedNetworks) {\n const networkKey = removedNetwork as ChainIdHex;\n if (s.tokenBalances[addressKey]?.[networkKey]) {\n delete s.tokenBalances[addressKey][networkKey];\n }\n }\n }\n });\n }\n };\n\n readonly #onAccountRemoved = (addr: string) => {\n if (!isStrictHexString(addr) || !isValidHexAddress(addr)) {\n return;\n }\n this.update((s) => {\n delete s.tokenBalances[addr as ChecksumAddress];\n });\n };\n}\n\nexport default TokenBalancesController;\n"]}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"TokenBalancesController.d.cts","sourceRoot":"","sources":["../src/TokenBalancesController.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EACV,0CAA0C,EAC1C,oCAAoC,EACrC,sCAAsC;AACvC,OAAO,KAAK,EACV,wBAAwB,EACxB,0BAA0B,EAC1B,mBAAmB,EACpB,kCAAkC;AAMnC,OAAO,KAAK,EAAE,oCAAoC,EAAE,qCAAqC;AACzF,OAAO,KAAK,EACV,2CAA2C,EAC3C,+BAA+B,EAC/B,iCAAiC,EAElC,qCAAqC;AAEtC,OAAO,KAAK,EACV,mCAAmC,EACnC,qCAAqC,EACtC,yCAAyC;AAC1C,OAAO,KAAK,EAAE,GAAG,EAAE,wBAAwB;AAK3C,OAAO,KAAK,EACV,wCAAwC,EACxC,wCAAwC,EACzC,uCAAmC;AAQpC,OAAO,KAAK,EACV,8BAA8B,EAE9B,gCAAgC,EACjC,+BAA2B;AAE5B,MAAM,MAAM,UAAU,GAAG,GAAG,CAAC;AAC7B,MAAM,MAAM,eAAe,GAAG,GAAG,CAAC;AAElC,QAAA,MAAM,UAAU,2BAAqC,CAAC;AAQtD,MAAM,MAAM,aAAa,GAAG,MAAM,CAChC,eAAe,EACf,MAAM,CAAC,UAAU,EAAE,MAAM,CAAC,eAAe,EAAE,GAAG,CAAC,CAAC,CACjD,CAAC;AAEF,MAAM,MAAM,4BAA4B,GAAG;IACzC,aAAa,EAAE,aAAa,CAAC;CAC9B,CAAC;AAEF,MAAM,MAAM,qCAAqC,GAAG,wBAAwB,CAC1E,OAAO,UAAU,EACjB,4BAA4B,CAC7B,CAAC;AAEF,MAAM,MAAM,8BAA8B,GACxC,qCAAqC,CAAC;AAExC,MAAM,MAAM,uCAAuC,GACjD,0BAA0B,CAAC,OAAO,UAAU,EAAE,4BAA4B,CAAC,CAAC;AAE9E,MAAM,MAAM,kBAAkB,GAAG;IAC/B,IAAI,EAAE,GAAG,OAAO,UAAU,uBAAuB,CAAC;IAClD,OAAO,EAAE,OAAO,EAAE,CAAC;CACpB,CAAC;AAEF,MAAM,MAAM,6BAA6B,GACrC,uCAAuC,GACvC,kBAAkB,CAAC;AAEvB,MAAM,MAAM,cAAc,GACtB,2CAA2C,GAC3C,+BAA+B,GAC/B,8BAA8B,GAC9B,mCAAmC,GACnC,0CAA0C,GAC1C,oCAAoC,GACpC,wCAAwC,GACxC,wCAAwC,CAAC;AAE7C,MAAM,MAAM,aAAa,GACrB,gCAAgC,GAChC,qCAAqC,GACrC,iCAAiC,GACjC,oCAAoC,CAAC;AAEzC,MAAM,MAAM,gCAAgC,GAAG,mBAAmB,CAChE,OAAO,UAAU,EACjB,8BAA8B,GAAG,cAAc,EAC/C,6BAA6B,GAAG,aAAa,EAC7C,cAAc,CAAC,MAAM,CAAC,EACtB,aAAa,CAAC,MAAM,CAAC,CACtB,CAAC;AAEF,MAAM,MAAM,8BAA8B,GAAG;IAC3C,SAAS,EAAE,gCAAgC,CAAC;IAC5C,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,KAAK,CAAC,EAAE,OAAO,CAAC,4BAA4B,CAAC,CAAC;IAC9C,kEAAkE;IAClE,qBAAqB,CAAC,EAAE,OAAO,CAAC;IAChC,yDAAyD;IACzD,cAAc,CAAC,EAAE,OAAO,CAAC;IACzB,4DAA4D;IAC5D,qBAAqB,CAAC,EAAE,MAAM,OAAO,CAAC;IACtC,qBAAqB;IACrB,GAAG,CAAC,EAAE,CAAC,GAAG,IAAI,EAAE,OAAO,EAAE,KAAK,IAAI,CAAC;CACpC,CAAC;;;;;;;
|
|
1
|
+
{"version":3,"file":"TokenBalancesController.d.cts","sourceRoot":"","sources":["../src/TokenBalancesController.ts"],"names":[],"mappings":"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"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"TokenBalancesController.d.mts","sourceRoot":"","sources":["../src/TokenBalancesController.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EACV,0CAA0C,EAC1C,oCAAoC,EACrC,sCAAsC;AACvC,OAAO,KAAK,EACV,wBAAwB,EACxB,0BAA0B,EAC1B,mBAAmB,EACpB,kCAAkC;AAMnC,OAAO,KAAK,EAAE,oCAAoC,EAAE,qCAAqC;AACzF,OAAO,KAAK,EACV,2CAA2C,EAC3C,+BAA+B,EAC/B,iCAAiC,EAElC,qCAAqC;AAEtC,OAAO,KAAK,EACV,mCAAmC,EACnC,qCAAqC,EACtC,yCAAyC;AAC1C,OAAO,KAAK,EAAE,GAAG,EAAE,wBAAwB;AAK3C,OAAO,KAAK,EACV,wCAAwC,EACxC,wCAAwC,EACzC,uCAAmC;AAQpC,OAAO,KAAK,EACV,8BAA8B,EAE9B,gCAAgC,EACjC,+BAA2B;AAE5B,MAAM,MAAM,UAAU,GAAG,GAAG,CAAC;AAC7B,MAAM,MAAM,eAAe,GAAG,GAAG,CAAC;AAElC,QAAA,MAAM,UAAU,2BAAqC,CAAC;AAQtD,MAAM,MAAM,aAAa,GAAG,MAAM,CAChC,eAAe,EACf,MAAM,CAAC,UAAU,EAAE,MAAM,CAAC,eAAe,EAAE,GAAG,CAAC,CAAC,CACjD,CAAC;AAEF,MAAM,MAAM,4BAA4B,GAAG;IACzC,aAAa,EAAE,aAAa,CAAC;CAC9B,CAAC;AAEF,MAAM,MAAM,qCAAqC,GAAG,wBAAwB,CAC1E,OAAO,UAAU,EACjB,4BAA4B,CAC7B,CAAC;AAEF,MAAM,MAAM,8BAA8B,GACxC,qCAAqC,CAAC;AAExC,MAAM,MAAM,uCAAuC,GACjD,0BAA0B,CAAC,OAAO,UAAU,EAAE,4BAA4B,CAAC,CAAC;AAE9E,MAAM,MAAM,kBAAkB,GAAG;IAC/B,IAAI,EAAE,GAAG,OAAO,UAAU,uBAAuB,CAAC;IAClD,OAAO,EAAE,OAAO,EAAE,CAAC;CACpB,CAAC;AAEF,MAAM,MAAM,6BAA6B,GACrC,uCAAuC,GACvC,kBAAkB,CAAC;AAEvB,MAAM,MAAM,cAAc,GACtB,2CAA2C,GAC3C,+BAA+B,GAC/B,8BAA8B,GAC9B,mCAAmC,GACnC,0CAA0C,GAC1C,oCAAoC,GACpC,wCAAwC,GACxC,wCAAwC,CAAC;AAE7C,MAAM,MAAM,aAAa,GACrB,gCAAgC,GAChC,qCAAqC,GACrC,iCAAiC,GACjC,oCAAoC,CAAC;AAEzC,MAAM,MAAM,gCAAgC,GAAG,mBAAmB,CAChE,OAAO,UAAU,EACjB,8BAA8B,GAAG,cAAc,EAC/C,6BAA6B,GAAG,aAAa,EAC7C,cAAc,CAAC,MAAM,CAAC,EACtB,aAAa,CAAC,MAAM,CAAC,CACtB,CAAC;AAEF,MAAM,MAAM,8BAA8B,GAAG;IAC3C,SAAS,EAAE,gCAAgC,CAAC;IAC5C,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,KAAK,CAAC,EAAE,OAAO,CAAC,4BAA4B,CAAC,CAAC;IAC9C,kEAAkE;IAClE,qBAAqB,CAAC,EAAE,OAAO,CAAC;IAChC,yDAAyD;IACzD,cAAc,CAAC,EAAE,OAAO,CAAC;IACzB,4DAA4D;IAC5D,qBAAqB,CAAC,EAAE,MAAM,OAAO,CAAC;IACtC,qBAAqB;IACrB,GAAG,CAAC,EAAE,CAAC,GAAG,IAAI,EAAE,OAAO,EAAE,KAAK,IAAI,CAAC;CACpC,CAAC;;;;;;;
|
|
1
|
+
{"version":3,"file":"TokenBalancesController.d.mts","sourceRoot":"","sources":["../src/TokenBalancesController.ts"],"names":[],"mappings":"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"}
|
|
@@ -11,7 +11,7 @@ var __classPrivateFieldGet = (this && this.__classPrivateFieldGet) || function (
|
|
|
11
11
|
};
|
|
12
12
|
var _TokenBalancesController_instances, _TokenBalancesController_queryAllAccounts, _TokenBalancesController_balanceFetchers, _TokenBalancesController_allTokens, _TokenBalancesController_detectedTokens, _TokenBalancesController_chainIdsWithTokens, _TokenBalancesController_getProvider, _TokenBalancesController_getNetworkClient, _TokenBalancesController_onTokensChanged, _TokenBalancesController_onNetworkChanged, _TokenBalancesController_onAccountRemoved;
|
|
13
13
|
import { Web3Provider } from "@ethersproject/providers";
|
|
14
|
-
import { isValidHexAddress,
|
|
14
|
+
import { isValidHexAddress, toChecksumHexAddress, toHex } from "@metamask/controller-utils";
|
|
15
15
|
import { StaticIntervalPollingController } from "@metamask/polling-controller";
|
|
16
16
|
import { isStrictHexString } from "@metamask/utils";
|
|
17
17
|
import { produce } from "immer";
|
|
@@ -30,6 +30,7 @@ const metadata = {
|
|
|
30
30
|
// region: Helper utilities
|
|
31
31
|
const draft = (base, fn) => produce(base, fn);
|
|
32
32
|
const ZERO_ADDRESS = '0x0000000000000000000000000000000000000000';
|
|
33
|
+
const checksum = (addr) => toChecksumHexAddress(addr);
|
|
33
34
|
// endregion
|
|
34
35
|
// ────────────────────────────────────────────────────────────────────────────
|
|
35
36
|
// region: Main controller
|
|
@@ -188,7 +189,11 @@ export class TokenBalancesController extends StaticIntervalPollingController() {
|
|
|
188
189
|
const { allTokens, allDetectedTokens } = this.messagingSystem.call('TokensController:getState');
|
|
189
190
|
__classPrivateFieldSet(this, _TokenBalancesController_allTokens, allTokens, "f");
|
|
190
191
|
__classPrivateFieldSet(this, _TokenBalancesController_detectedTokens, allDetectedTokens, "f");
|
|
191
|
-
this.messagingSystem.subscribe('TokensController:stateChange',
|
|
192
|
+
this.messagingSystem.subscribe('TokensController:stateChange', (tokensState) => {
|
|
193
|
+
__classPrivateFieldGet(this, _TokenBalancesController_onTokensChanged, "f").call(this, tokensState).catch((error) => {
|
|
194
|
+
console.warn('Error handling token state change:', error);
|
|
195
|
+
});
|
|
196
|
+
});
|
|
192
197
|
this.messagingSystem.subscribe('NetworkController:stateChange', __classPrivateFieldGet(this, _TokenBalancesController_onNetworkChanged, "f"));
|
|
193
198
|
this.messagingSystem.subscribe('KeyringController:accountRemoved', __classPrivateFieldGet(this, _TokenBalancesController_onAccountRemoved, "f"));
|
|
194
199
|
}
|
|
@@ -211,14 +216,12 @@ export class TokenBalancesController extends StaticIntervalPollingController() {
|
|
|
211
216
|
continue;
|
|
212
217
|
}
|
|
213
218
|
try {
|
|
214
|
-
const balances = await
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
});
|
|
221
|
-
}, false, this.getIntervalLength());
|
|
219
|
+
const balances = await fetcher.fetch({
|
|
220
|
+
chainIds: supportedChains,
|
|
221
|
+
queryAllAccounts: __classPrivateFieldGet(this, _TokenBalancesController_queryAllAccounts, "f"),
|
|
222
|
+
selectedAccount: selected,
|
|
223
|
+
allAccounts,
|
|
224
|
+
});
|
|
222
225
|
if (balances && balances.length > 0) {
|
|
223
226
|
aggregated.push(...balances);
|
|
224
227
|
// Remove chains that were successfully processed
|
|
@@ -250,7 +253,7 @@ export class TokenBalancesController extends StaticIntervalPollingController() {
|
|
|
250
253
|
if (chainTokens?.[account]) {
|
|
251
254
|
Object.values(chainTokens[account]).forEach((token) => {
|
|
252
255
|
var _a, _b;
|
|
253
|
-
const tokenAddress = token.address;
|
|
256
|
+
const tokenAddress = checksum(token.address);
|
|
254
257
|
((_b = ((_a = d.tokenBalances)[account] ?? (_a[account] = {})))[chainId] ?? (_b[chainId] = {}))[tokenAddress] = '0x0';
|
|
255
258
|
});
|
|
256
259
|
}
|
|
@@ -259,7 +262,7 @@ export class TokenBalancesController extends StaticIntervalPollingController() {
|
|
|
259
262
|
if (detectedChainTokens?.[account]) {
|
|
260
263
|
Object.values(detectedChainTokens[account]).forEach((token) => {
|
|
261
264
|
var _a, _b;
|
|
262
|
-
const tokenAddress = token.address;
|
|
265
|
+
const tokenAddress = checksum(token.address);
|
|
263
266
|
((_b = ((_a = d.tokenBalances)[account] ?? (_a[account] = {})))[chainId] ?? (_b[chainId] = {}))[tokenAddress] = '0x0';
|
|
264
267
|
});
|
|
265
268
|
}
|
|
@@ -269,7 +272,7 @@ export class TokenBalancesController extends StaticIntervalPollingController() {
|
|
|
269
272
|
aggregated.forEach(({ success, value, account, token, chainId }) => {
|
|
270
273
|
var _a, _b;
|
|
271
274
|
if (success && value !== undefined) {
|
|
272
|
-
((_b = ((_a = d.tokenBalances)[account] ?? (_a[account] = {})))[chainId] ?? (_b[chainId] = {}))[token] =
|
|
275
|
+
((_b = ((_a = d.tokenBalances)[account] ?? (_a[account] = {})))[chainId] ?? (_b[chainId] = {}))[checksum(token)] =
|
|
273
276
|
toHex(value);
|
|
274
277
|
}
|
|
275
278
|
});
|
|
@@ -282,7 +285,7 @@ export class TokenBalancesController extends StaticIntervalPollingController() {
|
|
|
282
285
|
const balanceUpdates = nativeBalances.map((balance) => ({
|
|
283
286
|
address: balance.account,
|
|
284
287
|
chainId: balance.chainId,
|
|
285
|
-
balance: balance.value?.toString() ?? '
|
|
288
|
+
balance: balance.value?.toString() ?? '0x0',
|
|
286
289
|
}));
|
|
287
290
|
this.messagingSystem.call('AccountTrackerController:updateNativeBalances', balanceUpdates);
|
|
288
291
|
}
|
|
@@ -298,7 +301,7 @@ export class TokenBalancesController extends StaticIntervalPollingController() {
|
|
|
298
301
|
const stakedBalanceUpdates = stakedBalances.map((balance) => ({
|
|
299
302
|
address: balance.account,
|
|
300
303
|
chainId: balance.chainId,
|
|
301
|
-
stakedBalance: balance.value
|
|
304
|
+
stakedBalance: balance.value ? toHex(balance.value) : '0x0',
|
|
302
305
|
}));
|
|
303
306
|
this.messagingSystem.call('AccountTrackerController:updateStakedBalances', stakedBalanceUpdates);
|
|
304
307
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"TokenBalancesController.mjs","sourceRoot":"","sources":["../src/TokenBalancesController.ts"],"names":[],"mappings":"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","sourcesContent":["import { Web3Provider } from '@ethersproject/providers';\nimport type {\n AccountsControllerGetSelectedAccountAction,\n AccountsControllerListAccountsAction,\n} from '@metamask/accounts-controller';\nimport type {\n ControllerGetStateAction,\n ControllerStateChangeEvent,\n RestrictedMessenger,\n} from '@metamask/base-controller';\nimport {\n isValidHexAddress,\n safelyExecuteWithTimeout,\n toHex,\n} from '@metamask/controller-utils';\nimport type { KeyringControllerAccountRemovedEvent } from '@metamask/keyring-controller';\nimport type {\n NetworkControllerGetNetworkClientByIdAction,\n NetworkControllerGetStateAction,\n NetworkControllerStateChangeEvent,\n NetworkState,\n} from '@metamask/network-controller';\nimport { StaticIntervalPollingController } from '@metamask/polling-controller';\nimport type {\n PreferencesControllerGetStateAction,\n PreferencesControllerStateChangeEvent,\n} from '@metamask/preferences-controller';\nimport type { Hex } from '@metamask/utils';\nimport { isStrictHexString } from '@metamask/utils';\nimport { produce } from 'immer';\nimport { isEqual } from 'lodash';\n\nimport type {\n AccountTrackerUpdateNativeBalancesAction,\n AccountTrackerUpdateStakedBalancesAction,\n} from './AccountTrackerController';\nimport { STAKING_CONTRACT_ADDRESS_BY_CHAINID } from './AssetsContractController';\nimport {\n AccountsApiBalanceFetcher,\n type BalanceFetcher,\n type ProcessedBalance,\n} from './multi-chain-accounts-service/api-balance-fetcher';\nimport { RpcBalanceFetcher } from './rpc-service/rpc-balance-fetcher';\nimport type {\n TokensControllerGetStateAction,\n TokensControllerState,\n TokensControllerStateChangeEvent,\n} from './TokensController';\n\nexport type ChainIdHex = Hex;\nexport type ChecksumAddress = Hex;\n\nconst CONTROLLER = 'TokenBalancesController' as const;\nconst DEFAULT_INTERVAL_MS = 180_000; // 3 minutes\n\nconst metadata = {\n tokenBalances: { persist: true, anonymous: false },\n};\n\n// account → chain → token → balance\nexport type TokenBalances = Record<\n ChecksumAddress,\n Record<ChainIdHex, Record<ChecksumAddress, Hex>>\n>;\n\nexport type TokenBalancesControllerState = {\n tokenBalances: TokenBalances;\n};\n\nexport type TokenBalancesControllerGetStateAction = ControllerGetStateAction<\n typeof CONTROLLER,\n TokenBalancesControllerState\n>;\n\nexport type TokenBalancesControllerActions =\n TokenBalancesControllerGetStateAction;\n\nexport type TokenBalancesControllerStateChangeEvent =\n ControllerStateChangeEvent<typeof CONTROLLER, TokenBalancesControllerState>;\n\nexport type NativeBalanceEvent = {\n type: `${typeof CONTROLLER}:updatedNativeBalance`;\n payload: unknown[];\n};\n\nexport type TokenBalancesControllerEvents =\n | TokenBalancesControllerStateChangeEvent\n | NativeBalanceEvent;\n\nexport type AllowedActions =\n | NetworkControllerGetNetworkClientByIdAction\n | NetworkControllerGetStateAction\n | TokensControllerGetStateAction\n | PreferencesControllerGetStateAction\n | AccountsControllerGetSelectedAccountAction\n | AccountsControllerListAccountsAction\n | AccountTrackerUpdateNativeBalancesAction\n | AccountTrackerUpdateStakedBalancesAction;\n\nexport type AllowedEvents =\n | TokensControllerStateChangeEvent\n | PreferencesControllerStateChangeEvent\n | NetworkControllerStateChangeEvent\n | KeyringControllerAccountRemovedEvent;\n\nexport type TokenBalancesControllerMessenger = RestrictedMessenger<\n typeof CONTROLLER,\n TokenBalancesControllerActions | AllowedActions,\n TokenBalancesControllerEvents | AllowedEvents,\n AllowedActions['type'],\n AllowedEvents['type']\n>;\n\nexport type TokenBalancesControllerOptions = {\n messenger: TokenBalancesControllerMessenger;\n interval?: number;\n state?: Partial<TokenBalancesControllerState>;\n /** When `true`, balances for *all* known accounts are queried. */\n queryMultipleAccounts?: boolean;\n /** Enable Accounts‑API strategy (if supported chain). */\n useAccountsAPI?: boolean;\n /** Disable external HTTP calls (privacy / offline mode). */\n allowExternalServices?: () => boolean;\n /** Custom logger. */\n log?: (...args: unknown[]) => void;\n};\n// endregion\n\n// ────────────────────────────────────────────────────────────────────────────\n// region: Helper utilities\nconst draft = <T>(base: T, fn: (d: T) => void): T => produce(base, fn);\n\nconst ZERO_ADDRESS =\n '0x0000000000000000000000000000000000000000' as ChecksumAddress;\n// endregion\n\n// ────────────────────────────────────────────────────────────────────────────\n// region: Main controller\nexport class TokenBalancesController extends StaticIntervalPollingController<{\n chainIds: ChainIdHex[];\n}>()<\n typeof CONTROLLER,\n TokenBalancesControllerState,\n TokenBalancesControllerMessenger\n> {\n readonly #queryAllAccounts: boolean;\n\n readonly #balanceFetchers: BalanceFetcher[];\n\n #allTokens: TokensControllerState['allTokens'] = {};\n\n #detectedTokens: TokensControllerState['allDetectedTokens'] = {};\n\n constructor({\n messenger,\n interval = DEFAULT_INTERVAL_MS,\n state = {},\n queryMultipleAccounts = true,\n useAccountsAPI = false,\n allowExternalServices = () => true,\n }: TokenBalancesControllerOptions) {\n super({\n name: CONTROLLER,\n messenger,\n metadata,\n state: { tokenBalances: {}, ...state },\n });\n\n this.#queryAllAccounts = queryMultipleAccounts;\n\n // Strategy order: API first, then RPC fallback\n this.#balanceFetchers = [\n ...(useAccountsAPI && allowExternalServices()\n ? [new AccountsApiBalanceFetcher('extension', this.#getProvider)]\n : []),\n new RpcBalanceFetcher(this.#getProvider, this.#getNetworkClient, () => ({\n allTokens: this.#allTokens,\n allDetectedTokens: this.#detectedTokens,\n })),\n ];\n\n this.setIntervalLength(interval);\n\n // initial token state & subscriptions\n const { allTokens, allDetectedTokens } = this.messagingSystem.call(\n 'TokensController:getState',\n );\n this.#allTokens = allTokens;\n this.#detectedTokens = allDetectedTokens;\n\n this.messagingSystem.subscribe(\n 'TokensController:stateChange',\n this.#onTokensChanged,\n );\n this.messagingSystem.subscribe(\n 'NetworkController:stateChange',\n this.#onNetworkChanged,\n );\n this.messagingSystem.subscribe(\n 'KeyringController:accountRemoved',\n this.#onAccountRemoved,\n );\n }\n\n #chainIdsWithTokens(): ChainIdHex[] {\n return [\n ...new Set([\n ...Object.keys(this.#allTokens),\n ...Object.keys(this.#detectedTokens),\n ]),\n ] as ChainIdHex[];\n }\n\n readonly #getProvider = (chainId: ChainIdHex): Web3Provider => {\n const { networkConfigurationsByChainId } = this.messagingSystem.call(\n 'NetworkController:getState',\n );\n const cfg = networkConfigurationsByChainId[chainId];\n const { networkClientId } = cfg.rpcEndpoints[cfg.defaultRpcEndpointIndex];\n const client = this.messagingSystem.call(\n 'NetworkController:getNetworkClientById',\n networkClientId,\n );\n return new Web3Provider(client.provider);\n };\n\n readonly #getNetworkClient = (chainId: ChainIdHex) => {\n const { networkConfigurationsByChainId } = this.messagingSystem.call(\n 'NetworkController:getState',\n );\n const cfg = networkConfigurationsByChainId[chainId];\n const { networkClientId } = cfg.rpcEndpoints[cfg.defaultRpcEndpointIndex];\n return this.messagingSystem.call(\n 'NetworkController:getNetworkClientById',\n networkClientId,\n );\n };\n\n async _executePoll({ chainIds }: { chainIds: ChainIdHex[] }) {\n await this.updateBalances({ chainIds });\n }\n\n async updateBalances({ chainIds }: { chainIds?: ChainIdHex[] } = {}) {\n const targetChains = chainIds ?? this.#chainIdsWithTokens();\n if (!targetChains.length) {\n return;\n }\n\n const { address: selected } = this.messagingSystem.call(\n 'AccountsController:getSelectedAccount',\n );\n const allAccounts = this.messagingSystem.call(\n 'AccountsController:listAccounts',\n );\n\n const aggregated: ProcessedBalance[] = [];\n let remainingChains = [...targetChains];\n\n // Try each fetcher in order, removing successfully processed chains\n for (const fetcher of this.#balanceFetchers) {\n const supportedChains = remainingChains.filter((c) =>\n fetcher.supports(c),\n );\n if (!supportedChains.length) {\n continue;\n }\n\n try {\n const balances = await safelyExecuteWithTimeout(\n async () => {\n return await fetcher.fetch({\n chainIds: supportedChains,\n queryAllAccounts: this.#queryAllAccounts,\n selectedAccount: selected as ChecksumAddress,\n allAccounts,\n });\n },\n false,\n this.getIntervalLength(),\n );\n\n if (balances && balances.length > 0) {\n aggregated.push(...balances);\n // Remove chains that were successfully processed\n const processedChains = new Set(balances.map((b) => b.chainId));\n remainingChains = remainingChains.filter(\n (chain) => !processedChains.has(chain),\n );\n }\n } catch (error) {\n console.warn(\n `Balance fetcher failed for chains ${supportedChains.join(', ')}: ${String(error)}`,\n );\n // Continue to next fetcher (fallback)\n }\n\n // If all chains have been processed, break early\n if (remainingChains.length === 0) {\n break;\n }\n }\n\n // Determine which accounts to process\n const accountsToProcess = this.#queryAllAccounts\n ? allAccounts.map((a) => a.address as ChecksumAddress)\n : [selected as ChecksumAddress];\n\n const prev = this.state;\n const next = draft(prev, (d) => {\n // First, initialize all tokens from allTokens state with balance 0\n // for the accounts and chains we're processing\n for (const chainId of targetChains) {\n for (const account of accountsToProcess) {\n // Initialize tokens from allTokens\n const chainTokens = this.#allTokens[chainId];\n if (chainTokens?.[account]) {\n Object.values(chainTokens[account]).forEach(\n (token: { address: string }) => {\n const tokenAddress = token.address as ChecksumAddress;\n ((d.tokenBalances[account] ??= {})[chainId] ??= {})[\n tokenAddress\n ] = '0x0';\n },\n );\n }\n\n // Initialize tokens from allDetectedTokens\n const detectedChainTokens = this.#detectedTokens[chainId];\n if (detectedChainTokens?.[account]) {\n Object.values(detectedChainTokens[account]).forEach(\n (token: { address: string }) => {\n const tokenAddress = token.address as ChecksumAddress;\n ((d.tokenBalances[account] ??= {})[chainId] ??= {})[\n tokenAddress\n ] = '0x0';\n },\n );\n }\n }\n }\n\n // Then update with actual fetched balances where available\n aggregated.forEach(({ success, value, account, token, chainId }) => {\n if (success && value !== undefined) {\n ((d.tokenBalances[account] ??= {})[chainId] ??= {})[token] =\n toHex(value);\n }\n });\n });\n\n if (!isEqual(prev, next)) {\n this.update(() => next);\n\n const nativeBalances = aggregated.filter(\n (r) => r.success && r.token === ZERO_ADDRESS,\n );\n\n // Update native token balances in a single batch operation for better performance\n if (nativeBalances.length > 0) {\n const balanceUpdates = nativeBalances.map((balance) => ({\n address: balance.account,\n chainId: balance.chainId,\n balance: balance.value?.toString() ?? '0',\n }));\n\n this.messagingSystem.call(\n 'AccountTrackerController:updateNativeBalances',\n balanceUpdates,\n );\n }\n\n // Get staking contract addresses for filtering\n const stakingContractAddresses = Object.values(\n STAKING_CONTRACT_ADDRESS_BY_CHAINID,\n ).map((addr) => addr.toLowerCase());\n\n // Filter and update staked balances in a single batch operation for better performance\n const stakedBalances = aggregated.filter((r) => {\n return (\n r.success &&\n r.token !== ZERO_ADDRESS &&\n stakingContractAddresses.includes(r.token.toLowerCase())\n );\n });\n\n if (stakedBalances.length > 0) {\n const stakedBalanceUpdates = stakedBalances.map((balance) => ({\n address: balance.account,\n chainId: balance.chainId,\n stakedBalance: balance.value?.toString() ?? '0',\n }));\n\n this.messagingSystem.call(\n 'AccountTrackerController:updateStakedBalances',\n stakedBalanceUpdates,\n );\n }\n }\n }\n\n resetState() {\n this.update(() => ({ tokenBalances: {} }));\n }\n\n readonly #onTokensChanged = async (state: TokensControllerState) => {\n const changed: ChainIdHex[] = [];\n let hasChanges = false;\n\n // Get chains that have existing balances\n const chainsWithBalances = new Set<ChainIdHex>();\n for (const address of Object.keys(this.state.tokenBalances)) {\n const addressKey = address as ChecksumAddress;\n for (const chainId of Object.keys(\n this.state.tokenBalances[addressKey] || {},\n )) {\n chainsWithBalances.add(chainId as ChainIdHex);\n }\n }\n\n // Only process chains that are explicitly mentioned in the incoming state change\n const incomingChainIds = new Set([\n ...Object.keys(state.allTokens),\n ...Object.keys(state.allDetectedTokens),\n ]);\n\n // Only proceed if there are actual changes to chains that have balances or are being added\n const relevantChainIds = Array.from(incomingChainIds).filter((chainId) => {\n const id = chainId as ChainIdHex;\n\n const hasTokensNow =\n (state.allTokens[id] && Object.keys(state.allTokens[id]).length > 0) ||\n (state.allDetectedTokens[id] &&\n Object.keys(state.allDetectedTokens[id]).length > 0);\n const hadTokensBefore =\n (this.#allTokens[id] && Object.keys(this.#allTokens[id]).length > 0) ||\n (this.#detectedTokens[id] &&\n Object.keys(this.#detectedTokens[id]).length > 0);\n\n // Check if there's an actual change in token state\n const hasTokenChange =\n !isEqual(state.allTokens[id], this.#allTokens[id]) ||\n !isEqual(state.allDetectedTokens[id], this.#detectedTokens[id]);\n\n // Process chains that have actual changes OR are new chains getting tokens\n return hasTokenChange || (!hadTokensBefore && hasTokensNow);\n });\n\n if (relevantChainIds.length === 0) {\n // No relevant changes, just update internal state\n this.#allTokens = state.allTokens;\n this.#detectedTokens = state.allDetectedTokens;\n return;\n }\n\n // Handle both cleanup and updates in a single state update\n this.update((s) => {\n for (const chainId of relevantChainIds) {\n const id = chainId as ChainIdHex;\n const hasTokensNow =\n (state.allTokens[id] &&\n Object.keys(state.allTokens[id]).length > 0) ||\n (state.allDetectedTokens[id] &&\n Object.keys(state.allDetectedTokens[id]).length > 0);\n const hadTokensBefore =\n (this.#allTokens[id] &&\n Object.keys(this.#allTokens[id]).length > 0) ||\n (this.#detectedTokens[id] &&\n Object.keys(this.#detectedTokens[id]).length > 0);\n\n if (\n !isEqual(state.allTokens[id], this.#allTokens[id]) ||\n !isEqual(state.allDetectedTokens[id], this.#detectedTokens[id])\n ) {\n if (hasTokensNow) {\n // Chain still has tokens - mark for async balance update\n changed.push(id);\n } else if (hadTokensBefore) {\n // Chain had tokens before but doesn't now - clean up balances immediately\n for (const address of Object.keys(s.tokenBalances)) {\n const addressKey = address as ChecksumAddress;\n if (s.tokenBalances[addressKey]?.[id]) {\n s.tokenBalances[addressKey][id] = {};\n hasChanges = true;\n }\n }\n }\n }\n }\n });\n\n this.#allTokens = state.allTokens;\n this.#detectedTokens = state.allDetectedTokens;\n\n // Only update balances for chains that still have tokens (and only if we haven't already updated state)\n if (changed.length && !hasChanges) {\n this.updateBalances({ chainIds: changed }).catch((error) => {\n console.warn('Error updating balances after token change:', error);\n });\n }\n };\n\n readonly #onNetworkChanged = (state: NetworkState) => {\n // Check if any networks were removed by comparing with previous state\n const currentNetworks = new Set(\n Object.keys(state.networkConfigurationsByChainId),\n );\n\n // Get all networks that currently have balances\n const networksWithBalances = new Set<string>();\n for (const address of Object.keys(this.state.tokenBalances)) {\n const addressKey = address as ChecksumAddress;\n for (const network of Object.keys(\n this.state.tokenBalances[addressKey] || {},\n )) {\n networksWithBalances.add(network);\n }\n }\n\n // Find networks that were removed\n const removedNetworks = Array.from(networksWithBalances).filter(\n (network) => !currentNetworks.has(network),\n );\n\n if (removedNetworks.length > 0) {\n this.update((s) => {\n // Remove balances for all accounts on the deleted networks\n for (const address of Object.keys(s.tokenBalances)) {\n const addressKey = address as ChecksumAddress;\n for (const removedNetwork of removedNetworks) {\n const networkKey = removedNetwork as ChainIdHex;\n if (s.tokenBalances[addressKey]?.[networkKey]) {\n delete s.tokenBalances[addressKey][networkKey];\n }\n }\n }\n });\n }\n };\n\n readonly #onAccountRemoved = (addr: string) => {\n if (!isStrictHexString(addr) || !isValidHexAddress(addr)) {\n return;\n }\n this.update((s) => {\n delete s.tokenBalances[addr as ChecksumAddress];\n });\n };\n}\n\nexport default TokenBalancesController;\n"]}
|
|
1
|
+
{"version":3,"file":"TokenBalancesController.mjs","sourceRoot":"","sources":["../src/TokenBalancesController.ts"],"names":[],"mappings":"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","sourcesContent":["import { Web3Provider } from '@ethersproject/providers';\nimport type {\n AccountsControllerGetSelectedAccountAction,\n AccountsControllerListAccountsAction,\n} from '@metamask/accounts-controller';\nimport type {\n ControllerGetStateAction,\n ControllerStateChangeEvent,\n RestrictedMessenger,\n} from '@metamask/base-controller';\nimport {\n isValidHexAddress,\n toChecksumHexAddress,\n toHex,\n} from '@metamask/controller-utils';\nimport type { KeyringControllerAccountRemovedEvent } from '@metamask/keyring-controller';\nimport type {\n NetworkControllerGetNetworkClientByIdAction,\n NetworkControllerGetStateAction,\n NetworkControllerStateChangeEvent,\n NetworkState,\n} from '@metamask/network-controller';\nimport { StaticIntervalPollingController } from '@metamask/polling-controller';\nimport type {\n PreferencesControllerGetStateAction,\n PreferencesControllerStateChangeEvent,\n} from '@metamask/preferences-controller';\nimport type { Hex } from '@metamask/utils';\nimport { isStrictHexString } from '@metamask/utils';\nimport { produce } from 'immer';\nimport { isEqual } from 'lodash';\n\nimport type {\n AccountTrackerUpdateNativeBalancesAction,\n AccountTrackerUpdateStakedBalancesAction,\n} from './AccountTrackerController';\nimport { STAKING_CONTRACT_ADDRESS_BY_CHAINID } from './AssetsContractController';\nimport {\n AccountsApiBalanceFetcher,\n type BalanceFetcher,\n type ProcessedBalance,\n} from './multi-chain-accounts-service/api-balance-fetcher';\nimport { RpcBalanceFetcher } from './rpc-service/rpc-balance-fetcher';\nimport type {\n TokensControllerGetStateAction,\n TokensControllerState,\n TokensControllerStateChangeEvent,\n} from './TokensController';\n\nexport type ChainIdHex = Hex;\nexport type ChecksumAddress = Hex;\n\nconst CONTROLLER = 'TokenBalancesController' as const;\nconst DEFAULT_INTERVAL_MS = 180_000; // 3 minutes\n\nconst metadata = {\n tokenBalances: { persist: true, anonymous: false },\n};\n\n// account → chain → token → balance\nexport type TokenBalances = Record<\n ChecksumAddress,\n Record<ChainIdHex, Record<ChecksumAddress, Hex>>\n>;\n\nexport type TokenBalancesControllerState = {\n tokenBalances: TokenBalances;\n};\n\nexport type TokenBalancesControllerGetStateAction = ControllerGetStateAction<\n typeof CONTROLLER,\n TokenBalancesControllerState\n>;\n\nexport type TokenBalancesControllerActions =\n TokenBalancesControllerGetStateAction;\n\nexport type TokenBalancesControllerStateChangeEvent =\n ControllerStateChangeEvent<typeof CONTROLLER, TokenBalancesControllerState>;\n\nexport type NativeBalanceEvent = {\n type: `${typeof CONTROLLER}:updatedNativeBalance`;\n payload: unknown[];\n};\n\nexport type TokenBalancesControllerEvents =\n | TokenBalancesControllerStateChangeEvent\n | NativeBalanceEvent;\n\nexport type AllowedActions =\n | NetworkControllerGetNetworkClientByIdAction\n | NetworkControllerGetStateAction\n | TokensControllerGetStateAction\n | PreferencesControllerGetStateAction\n | AccountsControllerGetSelectedAccountAction\n | AccountsControllerListAccountsAction\n | AccountTrackerUpdateNativeBalancesAction\n | AccountTrackerUpdateStakedBalancesAction;\n\nexport type AllowedEvents =\n | TokensControllerStateChangeEvent\n | PreferencesControllerStateChangeEvent\n | NetworkControllerStateChangeEvent\n | KeyringControllerAccountRemovedEvent;\n\nexport type TokenBalancesControllerMessenger = RestrictedMessenger<\n typeof CONTROLLER,\n TokenBalancesControllerActions | AllowedActions,\n TokenBalancesControllerEvents | AllowedEvents,\n AllowedActions['type'],\n AllowedEvents['type']\n>;\n\nexport type TokenBalancesControllerOptions = {\n messenger: TokenBalancesControllerMessenger;\n interval?: number;\n state?: Partial<TokenBalancesControllerState>;\n /** When `true`, balances for *all* known accounts are queried. */\n queryMultipleAccounts?: boolean;\n /** Enable Accounts‑API strategy (if supported chain). */\n useAccountsAPI?: boolean;\n /** Disable external HTTP calls (privacy / offline mode). */\n allowExternalServices?: () => boolean;\n /** Custom logger. */\n log?: (...args: unknown[]) => void;\n};\n// endregion\n\n// ────────────────────────────────────────────────────────────────────────────\n// region: Helper utilities\nconst draft = <T>(base: T, fn: (d: T) => void): T => produce(base, fn);\n\nconst ZERO_ADDRESS =\n '0x0000000000000000000000000000000000000000' as ChecksumAddress;\n\nconst checksum = (addr: string): ChecksumAddress =>\n toChecksumHexAddress(addr) as ChecksumAddress;\n// endregion\n\n// ────────────────────────────────────────────────────────────────────────────\n// region: Main controller\nexport class TokenBalancesController extends StaticIntervalPollingController<{\n chainIds: ChainIdHex[];\n}>()<\n typeof CONTROLLER,\n TokenBalancesControllerState,\n TokenBalancesControllerMessenger\n> {\n readonly #queryAllAccounts: boolean;\n\n readonly #balanceFetchers: BalanceFetcher[];\n\n #allTokens: TokensControllerState['allTokens'] = {};\n\n #detectedTokens: TokensControllerState['allDetectedTokens'] = {};\n\n constructor({\n messenger,\n interval = DEFAULT_INTERVAL_MS,\n state = {},\n queryMultipleAccounts = true,\n useAccountsAPI = false,\n allowExternalServices = () => true,\n }: TokenBalancesControllerOptions) {\n super({\n name: CONTROLLER,\n messenger,\n metadata,\n state: { tokenBalances: {}, ...state },\n });\n\n this.#queryAllAccounts = queryMultipleAccounts;\n\n // Strategy order: API first, then RPC fallback\n this.#balanceFetchers = [\n ...(useAccountsAPI && allowExternalServices()\n ? [new AccountsApiBalanceFetcher('extension', this.#getProvider)]\n : []),\n new RpcBalanceFetcher(this.#getProvider, this.#getNetworkClient, () => ({\n allTokens: this.#allTokens,\n allDetectedTokens: this.#detectedTokens,\n })),\n ];\n\n this.setIntervalLength(interval);\n\n // initial token state & subscriptions\n const { allTokens, allDetectedTokens } = this.messagingSystem.call(\n 'TokensController:getState',\n );\n this.#allTokens = allTokens;\n this.#detectedTokens = allDetectedTokens;\n\n this.messagingSystem.subscribe(\n 'TokensController:stateChange',\n (tokensState: TokensControllerState) => {\n this.#onTokensChanged(tokensState).catch((error) => {\n console.warn('Error handling token state change:', error);\n });\n },\n );\n this.messagingSystem.subscribe(\n 'NetworkController:stateChange',\n this.#onNetworkChanged,\n );\n this.messagingSystem.subscribe(\n 'KeyringController:accountRemoved',\n this.#onAccountRemoved,\n );\n }\n\n #chainIdsWithTokens(): ChainIdHex[] {\n return [\n ...new Set([\n ...Object.keys(this.#allTokens),\n ...Object.keys(this.#detectedTokens),\n ]),\n ] as ChainIdHex[];\n }\n\n readonly #getProvider = (chainId: ChainIdHex): Web3Provider => {\n const { networkConfigurationsByChainId } = this.messagingSystem.call(\n 'NetworkController:getState',\n );\n const cfg = networkConfigurationsByChainId[chainId];\n const { networkClientId } = cfg.rpcEndpoints[cfg.defaultRpcEndpointIndex];\n const client = this.messagingSystem.call(\n 'NetworkController:getNetworkClientById',\n networkClientId,\n );\n return new Web3Provider(client.provider);\n };\n\n readonly #getNetworkClient = (chainId: ChainIdHex) => {\n const { networkConfigurationsByChainId } = this.messagingSystem.call(\n 'NetworkController:getState',\n );\n const cfg = networkConfigurationsByChainId[chainId];\n const { networkClientId } = cfg.rpcEndpoints[cfg.defaultRpcEndpointIndex];\n return this.messagingSystem.call(\n 'NetworkController:getNetworkClientById',\n networkClientId,\n );\n };\n\n async _executePoll({ chainIds }: { chainIds: ChainIdHex[] }) {\n await this.updateBalances({ chainIds });\n }\n\n async updateBalances({ chainIds }: { chainIds?: ChainIdHex[] } = {}) {\n const targetChains = chainIds ?? this.#chainIdsWithTokens();\n if (!targetChains.length) {\n return;\n }\n\n const { address: selected } = this.messagingSystem.call(\n 'AccountsController:getSelectedAccount',\n );\n const allAccounts = this.messagingSystem.call(\n 'AccountsController:listAccounts',\n );\n\n const aggregated: ProcessedBalance[] = [];\n let remainingChains = [...targetChains];\n\n // Try each fetcher in order, removing successfully processed chains\n for (const fetcher of this.#balanceFetchers) {\n const supportedChains = remainingChains.filter((c) =>\n fetcher.supports(c),\n );\n if (!supportedChains.length) {\n continue;\n }\n\n try {\n const balances = await fetcher.fetch({\n chainIds: supportedChains,\n queryAllAccounts: this.#queryAllAccounts,\n selectedAccount: selected as ChecksumAddress,\n allAccounts,\n });\n\n if (balances && balances.length > 0) {\n aggregated.push(...balances);\n // Remove chains that were successfully processed\n const processedChains = new Set(balances.map((b) => b.chainId));\n remainingChains = remainingChains.filter(\n (chain) => !processedChains.has(chain),\n );\n }\n } catch (error) {\n console.warn(\n `Balance fetcher failed for chains ${supportedChains.join(', ')}: ${String(error)}`,\n );\n // Continue to next fetcher (fallback)\n }\n\n // If all chains have been processed, break early\n if (remainingChains.length === 0) {\n break;\n }\n }\n\n // Determine which accounts to process\n const accountsToProcess = this.#queryAllAccounts\n ? allAccounts.map((a) => a.address as ChecksumAddress)\n : [selected as ChecksumAddress];\n\n const prev = this.state;\n const next = draft(prev, (d) => {\n // First, initialize all tokens from allTokens state with balance 0\n // for the accounts and chains we're processing\n for (const chainId of targetChains) {\n for (const account of accountsToProcess) {\n // Initialize tokens from allTokens\n const chainTokens = this.#allTokens[chainId];\n if (chainTokens?.[account]) {\n Object.values(chainTokens[account]).forEach(\n (token: { address: string }) => {\n const tokenAddress = checksum(token.address);\n ((d.tokenBalances[account] ??= {})[chainId] ??= {})[\n tokenAddress\n ] = '0x0';\n },\n );\n }\n\n // Initialize tokens from allDetectedTokens\n const detectedChainTokens = this.#detectedTokens[chainId];\n if (detectedChainTokens?.[account]) {\n Object.values(detectedChainTokens[account]).forEach(\n (token: { address: string }) => {\n const tokenAddress = checksum(token.address);\n ((d.tokenBalances[account] ??= {})[chainId] ??= {})[\n tokenAddress\n ] = '0x0';\n },\n );\n }\n }\n }\n\n // Then update with actual fetched balances where available\n aggregated.forEach(({ success, value, account, token, chainId }) => {\n if (success && value !== undefined) {\n ((d.tokenBalances[account] ??= {})[chainId] ??= {})[checksum(token)] =\n toHex(value);\n }\n });\n });\n\n if (!isEqual(prev, next)) {\n this.update(() => next);\n\n const nativeBalances = aggregated.filter(\n (r) => r.success && r.token === ZERO_ADDRESS,\n );\n\n // Update native token balances in a single batch operation for better performance\n if (nativeBalances.length > 0) {\n const balanceUpdates = nativeBalances.map((balance) => ({\n address: balance.account,\n chainId: balance.chainId,\n balance: balance.value?.toString() ?? '0x0',\n }));\n\n this.messagingSystem.call(\n 'AccountTrackerController:updateNativeBalances',\n balanceUpdates,\n );\n }\n\n // Get staking contract addresses for filtering\n const stakingContractAddresses = Object.values(\n STAKING_CONTRACT_ADDRESS_BY_CHAINID,\n ).map((addr) => addr.toLowerCase());\n\n // Filter and update staked balances in a single batch operation for better performance\n const stakedBalances = aggregated.filter((r) => {\n return (\n r.success &&\n r.token !== ZERO_ADDRESS &&\n stakingContractAddresses.includes(r.token.toLowerCase())\n );\n });\n\n if (stakedBalances.length > 0) {\n const stakedBalanceUpdates = stakedBalances.map((balance) => ({\n address: balance.account,\n chainId: balance.chainId,\n stakedBalance: balance.value ? toHex(balance.value) : '0x0',\n }));\n\n this.messagingSystem.call(\n 'AccountTrackerController:updateStakedBalances',\n stakedBalanceUpdates,\n );\n }\n }\n }\n\n resetState() {\n this.update(() => ({ tokenBalances: {} }));\n }\n\n readonly #onTokensChanged = async (state: TokensControllerState) => {\n const changed: ChainIdHex[] = [];\n let hasChanges = false;\n\n // Get chains that have existing balances\n const chainsWithBalances = new Set<ChainIdHex>();\n for (const address of Object.keys(this.state.tokenBalances)) {\n const addressKey = address as ChecksumAddress;\n for (const chainId of Object.keys(\n this.state.tokenBalances[addressKey] || {},\n )) {\n chainsWithBalances.add(chainId as ChainIdHex);\n }\n }\n\n // Only process chains that are explicitly mentioned in the incoming state change\n const incomingChainIds = new Set([\n ...Object.keys(state.allTokens),\n ...Object.keys(state.allDetectedTokens),\n ]);\n\n // Only proceed if there are actual changes to chains that have balances or are being added\n const relevantChainIds = Array.from(incomingChainIds).filter((chainId) => {\n const id = chainId as ChainIdHex;\n\n const hasTokensNow =\n (state.allTokens[id] && Object.keys(state.allTokens[id]).length > 0) ||\n (state.allDetectedTokens[id] &&\n Object.keys(state.allDetectedTokens[id]).length > 0);\n const hadTokensBefore =\n (this.#allTokens[id] && Object.keys(this.#allTokens[id]).length > 0) ||\n (this.#detectedTokens[id] &&\n Object.keys(this.#detectedTokens[id]).length > 0);\n\n // Check if there's an actual change in token state\n const hasTokenChange =\n !isEqual(state.allTokens[id], this.#allTokens[id]) ||\n !isEqual(state.allDetectedTokens[id], this.#detectedTokens[id]);\n\n // Process chains that have actual changes OR are new chains getting tokens\n return hasTokenChange || (!hadTokensBefore && hasTokensNow);\n });\n\n if (relevantChainIds.length === 0) {\n // No relevant changes, just update internal state\n this.#allTokens = state.allTokens;\n this.#detectedTokens = state.allDetectedTokens;\n return;\n }\n\n // Handle both cleanup and updates in a single state update\n this.update((s) => {\n for (const chainId of relevantChainIds) {\n const id = chainId as ChainIdHex;\n const hasTokensNow =\n (state.allTokens[id] &&\n Object.keys(state.allTokens[id]).length > 0) ||\n (state.allDetectedTokens[id] &&\n Object.keys(state.allDetectedTokens[id]).length > 0);\n const hadTokensBefore =\n (this.#allTokens[id] &&\n Object.keys(this.#allTokens[id]).length > 0) ||\n (this.#detectedTokens[id] &&\n Object.keys(this.#detectedTokens[id]).length > 0);\n\n if (\n !isEqual(state.allTokens[id], this.#allTokens[id]) ||\n !isEqual(state.allDetectedTokens[id], this.#detectedTokens[id])\n ) {\n if (hasTokensNow) {\n // Chain still has tokens - mark for async balance update\n changed.push(id);\n } else if (hadTokensBefore) {\n // Chain had tokens before but doesn't now - clean up balances immediately\n for (const address of Object.keys(s.tokenBalances)) {\n const addressKey = address as ChecksumAddress;\n if (s.tokenBalances[addressKey]?.[id]) {\n s.tokenBalances[addressKey][id] = {};\n hasChanges = true;\n }\n }\n }\n }\n }\n });\n\n this.#allTokens = state.allTokens;\n this.#detectedTokens = state.allDetectedTokens;\n\n // Only update balances for chains that still have tokens (and only if we haven't already updated state)\n if (changed.length && !hasChanges) {\n this.updateBalances({ chainIds: changed }).catch((error) => {\n console.warn('Error updating balances after token change:', error);\n });\n }\n };\n\n readonly #onNetworkChanged = (state: NetworkState) => {\n // Check if any networks were removed by comparing with previous state\n const currentNetworks = new Set(\n Object.keys(state.networkConfigurationsByChainId),\n );\n\n // Get all networks that currently have balances\n const networksWithBalances = new Set<string>();\n for (const address of Object.keys(this.state.tokenBalances)) {\n const addressKey = address as ChecksumAddress;\n for (const network of Object.keys(\n this.state.tokenBalances[addressKey] || {},\n )) {\n networksWithBalances.add(network);\n }\n }\n\n // Find networks that were removed\n const removedNetworks = Array.from(networksWithBalances).filter(\n (network) => !currentNetworks.has(network),\n );\n\n if (removedNetworks.length > 0) {\n this.update((s) => {\n // Remove balances for all accounts on the deleted networks\n for (const address of Object.keys(s.tokenBalances)) {\n const addressKey = address as ChecksumAddress;\n for (const removedNetwork of removedNetworks) {\n const networkKey = removedNetwork as ChainIdHex;\n if (s.tokenBalances[addressKey]?.[networkKey]) {\n delete s.tokenBalances[addressKey][networkKey];\n }\n }\n }\n });\n }\n };\n\n readonly #onAccountRemoved = (addr: string) => {\n if (!isStrictHexString(addr) || !isValidHexAddress(addr)) {\n return;\n }\n this.update((s) => {\n delete s.tokenBalances[addr as ChecksumAddress];\n });\n };\n}\n\nexport default TokenBalancesController;\n"]}
|
|
@@ -20,6 +20,7 @@ const controller_utils_1 = require("@metamask/controller-utils");
|
|
|
20
20
|
const bn_js_1 = __importDefault(require("bn.js"));
|
|
21
21
|
const AssetsContractController_1 = require("../AssetsContractController.cjs");
|
|
22
22
|
const multicall_1 = require("../multicall.cjs");
|
|
23
|
+
const RPC_TIMEOUT_MS = 30000;
|
|
23
24
|
const ZERO_ADDRESS = '0x0000000000000000000000000000000000000000';
|
|
24
25
|
const checksum = (addr) => (0, controller_utils_1.toChecksumHexAddress)(addr);
|
|
25
26
|
class RpcBalanceFetcher {
|
|
@@ -36,17 +37,25 @@ class RpcBalanceFetcher {
|
|
|
36
37
|
return true; // fallback – supports every chain
|
|
37
38
|
}
|
|
38
39
|
async fetch({ chainIds, queryAllAccounts, selectedAccount, allAccounts, }) {
|
|
39
|
-
|
|
40
|
-
|
|
40
|
+
// Process all chains in parallel for better performance
|
|
41
|
+
const chainProcessingPromises = chainIds.map(async (chainId) => {
|
|
41
42
|
const tokensState = __classPrivateFieldGet(this, _RpcBalanceFetcher_getTokensState, "f").call(this);
|
|
42
43
|
const accountTokenGroups = buildAccountTokenGroupsStatic(chainId, queryAllAccounts, selectedAccount, allAccounts, tokensState.allTokens, tokensState.allDetectedTokens);
|
|
43
44
|
if (!accountTokenGroups.length) {
|
|
44
|
-
|
|
45
|
+
return [];
|
|
45
46
|
}
|
|
46
47
|
const provider = __classPrivateFieldGet(this, _RpcBalanceFetcher_getProvider, "f").call(this, chainId);
|
|
47
48
|
await __classPrivateFieldGet(this, _RpcBalanceFetcher_instances, "m", _RpcBalanceFetcher_ensureFreshBlockData).call(this, chainId);
|
|
48
|
-
const
|
|
49
|
-
|
|
49
|
+
const balanceResult = await (0, controller_utils_1.safelyExecuteWithTimeout)(async () => {
|
|
50
|
+
return await (0, multicall_1.getTokenBalancesForMultipleAddresses)(accountTokenGroups, chainId, provider, true, // include native
|
|
51
|
+
true);
|
|
52
|
+
}, true, RPC_TIMEOUT_MS);
|
|
53
|
+
// If timeout or error occurred, return empty array for this chain
|
|
54
|
+
if (!balanceResult) {
|
|
55
|
+
return [];
|
|
56
|
+
}
|
|
57
|
+
const { tokenBalances, stakedBalances } = balanceResult;
|
|
58
|
+
const chainResults = [];
|
|
50
59
|
// Add native token entries for all addresses being processed
|
|
51
60
|
const allAddressesForNative = new Set();
|
|
52
61
|
accountTokenGroups.forEach((group) => {
|
|
@@ -55,7 +64,7 @@ class RpcBalanceFetcher {
|
|
|
55
64
|
// Ensure native token entries exist for all addresses
|
|
56
65
|
allAddressesForNative.forEach((address) => {
|
|
57
66
|
const nativeBalance = tokenBalances[ZERO_ADDRESS]?.[address] || null;
|
|
58
|
-
|
|
67
|
+
chainResults.push({
|
|
59
68
|
success: true,
|
|
60
69
|
value: nativeBalance ? nativeBalance : new bn_js_1.default('0'),
|
|
61
70
|
account: address,
|
|
@@ -70,7 +79,7 @@ class RpcBalanceFetcher {
|
|
|
70
79
|
return;
|
|
71
80
|
}
|
|
72
81
|
Object.entries(balances).forEach(([acct, bn]) => {
|
|
73
|
-
|
|
82
|
+
chainResults.push({
|
|
74
83
|
success: bn !== null,
|
|
75
84
|
value: bn,
|
|
76
85
|
account: acct,
|
|
@@ -91,7 +100,7 @@ class RpcBalanceFetcher {
|
|
|
91
100
|
const checksummedStakingAddress = checksum(stakingContractAddress);
|
|
92
101
|
allAddresses.forEach((address) => {
|
|
93
102
|
const stakedBalance = stakedBalances?.[address] || null;
|
|
94
|
-
|
|
103
|
+
chainResults.push({
|
|
95
104
|
success: true,
|
|
96
105
|
value: stakedBalance ? stakedBalance : new bn_js_1.default('0'),
|
|
97
106
|
account: address,
|
|
@@ -100,7 +109,20 @@ class RpcBalanceFetcher {
|
|
|
100
109
|
});
|
|
101
110
|
});
|
|
102
111
|
}
|
|
103
|
-
|
|
112
|
+
return chainResults;
|
|
113
|
+
});
|
|
114
|
+
// Wait for all chains to complete (or fail) and collect results
|
|
115
|
+
const chainResultsArray = await Promise.allSettled(chainProcessingPromises);
|
|
116
|
+
const results = [];
|
|
117
|
+
chainResultsArray.forEach((chainResult) => {
|
|
118
|
+
if (chainResult.status === 'fulfilled') {
|
|
119
|
+
results.push(...chainResult.value);
|
|
120
|
+
}
|
|
121
|
+
else {
|
|
122
|
+
// Log error but continue with other chains
|
|
123
|
+
console.warn('Chain processing failed:', chainResult.reason);
|
|
124
|
+
}
|
|
125
|
+
});
|
|
104
126
|
return results;
|
|
105
127
|
}
|
|
106
128
|
}
|