@metamask/assets-controllers 73.0.2 → 73.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +17 -1
- package/dist/AccountTrackerController.cjs +18 -11
- package/dist/AccountTrackerController.cjs.map +1 -1
- package/dist/AccountTrackerController.d.cts.map +1 -1
- package/dist/AccountTrackerController.d.mts.map +1 -1
- package/dist/AccountTrackerController.mjs +18 -11
- package/dist/AccountTrackerController.mjs.map +1 -1
- package/dist/DeFiPositionsController/DeFiPositionsController.cjs +1 -1
- package/dist/DeFiPositionsController/DeFiPositionsController.cjs.map +1 -1
- package/dist/DeFiPositionsController/DeFiPositionsController.mjs +1 -1
- package/dist/DeFiPositionsController/DeFiPositionsController.mjs.map +1 -1
- package/dist/index.cjs +3 -1
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +2 -0
- package/dist/index.d.cts.map +1 -1
- package/dist/index.d.mts +2 -0
- package/dist/index.d.mts.map +1 -1
- package/dist/index.mjs +1 -0
- package/dist/index.mjs.map +1 -1
- package/dist/selectors/balanceSelectors.cjs +328 -0
- package/dist/selectors/balanceSelectors.cjs.map +1 -0
- package/dist/selectors/balanceSelectors.d.cts +1676 -0
- package/dist/selectors/balanceSelectors.d.cts.map +1 -0
- package/dist/selectors/balanceSelectors.d.mts +1676 -0
- package/dist/selectors/balanceSelectors.d.mts.map +1 -0
- package/dist/selectors/balanceSelectors.mjs +321 -0
- package/dist/selectors/balanceSelectors.mjs.map +1 -0
- package/package.json +9 -4
package/CHANGELOG.md
CHANGED
|
@@ -7,6 +7,21 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
|
|
7
7
|
|
|
8
8
|
## [Unreleased]
|
|
9
9
|
|
|
10
|
+
## [73.1.0]
|
|
11
|
+
|
|
12
|
+
### Added
|
|
13
|
+
|
|
14
|
+
- Comprehensive balance selectors for multichain account groups and wallets ([#6235](https://github.com/MetaMask/core/pull/6235))
|
|
15
|
+
|
|
16
|
+
### Changed
|
|
17
|
+
|
|
18
|
+
- Bump `@metamask/keyring-api` from `^19.0.0` to `^20.0.0` ([#6248](https://github.com/MetaMask/core/pull/6248))
|
|
19
|
+
|
|
20
|
+
### Fixed
|
|
21
|
+
|
|
22
|
+
- Correct the polling rate for the DeFiPositionsController from 1 minute to 10 minutes. ([#6242](https://github.com/MetaMask/core/pull/6242))
|
|
23
|
+
- Fix `AccountTrackerController` to force block number update to avoid stale cached native balances ([#6250](https://github.com/MetaMask/core/pull/6250))
|
|
24
|
+
|
|
10
25
|
## [73.0.2]
|
|
11
26
|
|
|
12
27
|
### Fixed
|
|
@@ -1811,7 +1826,8 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
|
|
1811
1826
|
|
|
1812
1827
|
- Use Ethers for AssetsContractController ([#845](https://github.com/MetaMask/core/pull/845))
|
|
1813
1828
|
|
|
1814
|
-
[Unreleased]: https://github.com/MetaMask/core/compare/@metamask/assets-controllers@73.0
|
|
1829
|
+
[Unreleased]: https://github.com/MetaMask/core/compare/@metamask/assets-controllers@73.1.0...HEAD
|
|
1830
|
+
[73.1.0]: https://github.com/MetaMask/core/compare/@metamask/assets-controllers@73.0.2...@metamask/assets-controllers@73.1.0
|
|
1815
1831
|
[73.0.2]: https://github.com/MetaMask/core/compare/@metamask/assets-controllers@73.0.1...@metamask/assets-controllers@73.0.2
|
|
1816
1832
|
[73.0.1]: https://github.com/MetaMask/core/compare/@metamask/assets-controllers@73.0.0...@metamask/assets-controllers@73.0.1
|
|
1817
1833
|
[73.0.0]: https://github.com/MetaMask/core/compare/@metamask/assets-controllers@72.0.0...@metamask/assets-controllers@73.0.0
|
|
@@ -148,27 +148,33 @@ class AccountTrackerController extends (0, polling_controller_1.StaticIntervalPo
|
|
|
148
148
|
this.syncAccounts(chainIds);
|
|
149
149
|
// Create an array of promises for each networkClientId
|
|
150
150
|
const updatePromises = networkClientIds.map(async (networkClientId) => {
|
|
151
|
-
const { chainId, ethQuery, provider } = __classPrivateFieldGet(this, _AccountTrackerController_instances, "m", _AccountTrackerController_getCorrectNetworkClient).call(this, networkClientId);
|
|
151
|
+
const { chainId, ethQuery, provider, blockTracker } = __classPrivateFieldGet(this, _AccountTrackerController_instances, "m", _AccountTrackerController_getCorrectNetworkClient).call(this, networkClientId);
|
|
152
152
|
const { accountsByChainId } = this.state;
|
|
153
153
|
const { isMultiAccountBalancesEnabled } = this.messagingSystem.call('PreferencesController:getState');
|
|
154
154
|
const accountsToUpdate = isMultiAccountBalancesEnabled
|
|
155
155
|
? Object.keys(accountsByChainId[chainId])
|
|
156
156
|
: [(0, controller_utils_1.toChecksumHexAddress)(selectedAccount.address)];
|
|
157
157
|
const accountsForChain = { ...accountsByChainId[chainId] };
|
|
158
|
+
// Force fresh block data before multicall
|
|
159
|
+
// TODO: This is a temporary fix to ensure that the block number is up to date.
|
|
160
|
+
// We should remove this once we have a better solution for this on the block tracker controller.
|
|
161
|
+
await (0, controller_utils_1.safelyExecuteWithTimeout)(() => blockTracker?.checkForLatestBlock?.());
|
|
158
162
|
const stakedBalancesPromise = __classPrivateFieldGet(this, _AccountTrackerController_includeStakedAssets, "f")
|
|
159
163
|
? __classPrivateFieldGet(this, _AccountTrackerController_getStakedBalanceForChain, "f").call(this, accountsToUpdate, networkClientId)
|
|
160
164
|
: Promise.resolve({});
|
|
161
165
|
if ((0, utils_1.hasProperty)(AssetsContractController_1.SINGLE_CALL_BALANCES_ADDRESS_BY_CHAINID, chainId)) {
|
|
162
166
|
const contractAddress = AssetsContractController_1.SINGLE_CALL_BALANCES_ADDRESS_BY_CHAINID[chainId];
|
|
163
167
|
const contract = new contracts_1.Contract(contractAddress, single_call_balance_checker_abi_1.default, new providers_1.Web3Provider(provider));
|
|
164
|
-
const nativeBalances = await contract.balances(accountsToUpdate, [
|
|
168
|
+
const nativeBalances = await (0, controller_utils_1.safelyExecuteWithTimeout)(() => contract.balances(accountsToUpdate, [
|
|
165
169
|
'0x0000000000000000000000000000000000000000',
|
|
166
|
-
]);
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
170
|
+
]), false, 3000);
|
|
171
|
+
if (nativeBalances) {
|
|
172
|
+
accountsToUpdate.forEach((address, index) => {
|
|
173
|
+
accountsForChain[address] = {
|
|
174
|
+
balance: nativeBalances[index].toHexString(),
|
|
175
|
+
};
|
|
176
|
+
});
|
|
177
|
+
}
|
|
172
178
|
}
|
|
173
179
|
else {
|
|
174
180
|
// Process accounts in batches using reduceInBatchesSerially
|
|
@@ -191,8 +197,8 @@ class AccountTrackerController extends (0, polling_controller_1.StaticIntervalPo
|
|
|
191
197
|
},
|
|
192
198
|
});
|
|
193
199
|
}
|
|
194
|
-
const stakedBalanceResult = (await stakedBalancesPromise);
|
|
195
|
-
Object.entries(stakedBalanceResult).forEach(([address, balance]) => {
|
|
200
|
+
const stakedBalanceResult = await (0, controller_utils_1.safelyExecuteWithTimeout)(async () => (await stakedBalancesPromise));
|
|
201
|
+
Object.entries(stakedBalanceResult ?? {}).forEach(([address, balance]) => {
|
|
196
202
|
accountsForChain[address] = {
|
|
197
203
|
...accountsForChain[address],
|
|
198
204
|
stakedBalance: balance,
|
|
@@ -269,11 +275,12 @@ _AccountTrackerController_refreshMutex = new WeakMap(), _AccountTrackerControlle
|
|
|
269
275
|
const selectedNetworkClientId = networkClientId ??
|
|
270
276
|
this.messagingSystem.call('NetworkController:getState')
|
|
271
277
|
.selectedNetworkClientId;
|
|
272
|
-
const { configuration: { chainId }, provider, } = this.messagingSystem.call('NetworkController:getNetworkClientById', selectedNetworkClientId);
|
|
278
|
+
const { configuration: { chainId }, provider, blockTracker, } = this.messagingSystem.call('NetworkController:getNetworkClientById', selectedNetworkClientId);
|
|
273
279
|
return {
|
|
274
280
|
chainId,
|
|
275
281
|
provider,
|
|
276
282
|
ethQuery: new eth_query_1.default(provider),
|
|
283
|
+
blockTracker,
|
|
277
284
|
};
|
|
278
285
|
}, _AccountTrackerController_getNetworkClientIds = function _AccountTrackerController_getNetworkClientIds() {
|
|
279
286
|
const { networkConfigurationsByChainId } = this.messagingSystem.call('NetworkController:getState');
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"AccountTrackerController.cjs","sourceRoot":"","sources":["../src/AccountTrackerController.ts"],"names":[],"mappings":";;;;;;;;;;;;;;;;;;AACA,wDAAoD;AACpD,wDAAwD;AAYxD,iEAIoC;AAEpC,oEAA2C;AAM3C,qEAA+E;AAE/E,2CAAsD;AACtD,6CAAoC;AACpC,mCAA4C;AAC5C,sGAA4E;AAE5E,6EAIoC;AACpC,iDAAgF;AAEhF;;GAEG;AACH,MAAM,cAAc,GAAG,0BAA0B,CAAC;AA2BlD,MAAM,sBAAsB,GAAG;IAC7B,iBAAiB,EAAE;QACjB,OAAO,EAAE,IAAI;QACb,SAAS,EAAE,KAAK;KACjB;CACF,CAAC;AAgEF;;GAEG;AACH,MAAa,wBAAyB,SAAQ,IAAA,oDAA+B,GAI5E;IAOC;;;;;;;;;OASG;IACH,YAAY,EACV,QAAQ,GAAG,KAAK,EAChB,KAAK,EACL,SAAS,EACT,wBAAwB,EACxB,mBAAmB,GAAG,KAAK,GAO5B;QACC,MAAM,EAAE,uBAAuB,EAAE,GAAG,SAAS,CAAC,IAAI,CAChD,4BAA4B,CAC7B,CAAC;QACF,MAAM,EACJ,aAAa,EAAE,EAAE,OAAO,EAAE,GAC3B,GAAG,SAAS,CAAC,IAAI,CAChB,wCAAwC,EACxC,uBAAuB,CACxB,CAAC;QACF,KAAK,CAAC;YACJ,IAAI,EAAE,cAAc;YACpB,SAAS;YACT,KAAK,EAAE;gBACL,iBAAiB,EAAE;oBACjB,CAAC,OAAO,CAAC,EAAE,EAAE;iBACd;gBACD,GAAG,KAAK;aACT;YACD,QAAQ,EAAE,sBAAsB;SACjC,CAAC,CAAC;;QAhDI,iDAAgB,IAAI,mBAAK,EAAE,EAAC;QAE5B,gEAA8B;QAE9B,qEAAgF;QA6CvF,uBAAA,IAAI,sDAA6B,wBAAwB,MAAA,CAAC;QAE1D,uBAAA,IAAI,iDAAwB,mBAAmB,MAAA,CAAC;QAEhD,IAAI,CAAC,iBAAiB,CAAC,QAAQ,CAAC,CAAC;QAEjC,IAAI,CAAC,eAAe,CAAC,SAAS,CAC5B,6CAA6C,EAC7C,CAAC,UAAU,EAAE,WAAW,EAAE,EAAE;YAC1B,IAAI,UAAU,KAAK,WAAW,EAAE;gBAC9B,0CAA0C;gBAC1C,mEAAmE;gBACnE,IAAI,CAAC,OAAO,CAAC,uBAAA,IAAI,0FAAqB,MAAzB,IAAI,CAAuB,CAAC,CAAC;aAC3C;QACH,CAAC,EACD,CAAC,KAAK,EAAU,EAAE,CAAC,KAAK,CAAC,OAAO,CACjC,CAAC;IACJ,CAAC;IAEO,YAAY,CAAC,WAAqB;QACxC,MAAM,iBAAiB,GAAG,IAAA,kBAAS,EAAC,IAAI,CAAC,KAAK,CAAC,iBAAiB,CAAC,CAAC;QAClE,MAAM,EAAE,uBAAuB,EAAE,GAAG,IAAI,CAAC,eAAe,CAAC,IAAI,CAC3D,4BAA4B,CAC7B,CAAC;QACF,MAAM,EACJ,aAAa,EAAE,EAAE,OAAO,EAAE,cAAc,EAAE,GAC3C,GAAG,IAAI,CAAC,eAAe,CAAC,IAAI,CAC3B,wCAAwC,EACxC,uBAAuB,CACxB,CAAC;QAEF,MAAM,QAAQ,GAAG,MAAM,CAAC,IAAI,CAAC,iBAAiB,EAAE,CAAC,cAAc,CAAC,IAAI,EAAE,CAAC,CAAC;QAExE,+CAA+C;QAC/C,WAAW,CAAC,OAAO,CAAC,CAAC,UAAU,EAAE,EAAE;YACjC,IAAI,CAAC,iBAAiB,CAAC,UAAU,CAAC,EAAE;gBAClC,iBAAiB,CAAC,UAAU,CAAC,GAAG,EAAE,CAAC;gBACnC,QAAQ,CAAC,OAAO,CAAC,CAAC,OAAO,EAAE,EAAE;oBAC3B,iBAAiB,CAAC,UAAU,CAAC,CAAC,OAAO,CAAC,GAAG,EAAE,OAAO,EAAE,KAAK,EAAE,CAAC;gBAC9D,CAAC,CAAC,CAAC;aACJ;QACH,CAAC,CAAC,CAAC;QAEH,oEAAoE;QACpE,4DAA4D;QAC5D,MAAM,SAAS,GAAG,MAAM,CAAC,MAAM,CAC7B,IAAI,CAAC,eAAe;aACjB,IAAI,CAAC,iCAAiC,CAAC;aACvC,GAAG,CAAC,CAAC,eAAe,EAAE,EAAE,CACvB,IAAA,uCAAoB,EAAC,eAAe,CAAC,OAAO,CAAC,CAC9C,CACJ,CAAC;QACF,MAAM,YAAY,GAAG,SAAS,CAAC,MAAM,CACnC,CAAC,OAAO,EAAE,EAAE,CAAC,CAAC,QAAQ,CAAC,QAAQ,CAAC,OAAO,CAAC,CACzC,CAAC;QACF,MAAM,YAAY,GAAG,QAAQ,CAAC,MAAM,CAClC,CAAC,OAAO,EAAE,EAAE,CAAC,CAAC,SAAS,CAAC,QAAQ,CAAC,OAAO,CAAC,CAC1C,CAAC;QACF,MAAM,CAAC,IAAI,CAAC,iBAAiB,CAAC,CAAC,OAAO,CAAC,CAAC,OAAO,EAAE,EAAE;YACjD,YAAY,CAAC,OAAO,CAAC,CAAC,OAAO,EAAE,EAAE;gBAC/B,iBAAiB,CAAC,OAAO,CAAC,CAAC,OAAO,CAAC,GAAG;oBACpC,OAAO,EAAE,KAAK;iBACf,CAAC;YACJ,CAAC,CAAC,CAAC;QACL,CAAC,CAAC,CAAC;QAEH,MAAM,CAAC,IAAI,CAAC,iBAAiB,CAAC,CAAC,OAAO,CAAC,CAAC,OAAO,EAAE,EAAE;YACjD,YAAY,CAAC,OAAO,CAAC,CAAC,OAAO,EAAE,EAAE;gBAC/B,OAAO,iBAAiB,CAAC,OAAO,CAAC,CAAC,OAAO,CAAC,CAAC;YAC7C,CAAC,CAAC,CAAC;QACL,CAAC,CAAC,CAAC;QAEH,IAAI,CAAC,IAAA,gBAAO,EAAC,IAAI,CAAC,KAAK,CAAC,iBAAiB,EAAE,iBAAiB,CAAC,EAAE;YAC7D,IAAI,CAAC,MAAM,CAAC,CAAC,KAAK,EAAE,EAAE;gBACpB,KAAK,CAAC,iBAAiB,GAAG,iBAAiB,CAAC;YAC9C,CAAC,CAAC,CAAC;SACJ;IACH,CAAC;IAkDD;;;;;OAKG;IACH,KAAK,CAAC,YAAY,CAAC,EACjB,gBAAgB,GACW;QAC3B,gFAAgF;QAChF,mEAAmE;QACnE,IAAI,CAAC,OAAO,CAAC,gBAAgB,CAAC,CAAC;IACjC,CAAC;IAED;;;;;;OAMG;IACH,KAAK,CAAC,OAAO,CAAC,gBAAmC;QAC/C,MAAM,eAAe,GAAG,IAAI,CAAC,eAAe,CAAC,IAAI,CAC/C,uCAAuC,CACxC,CAAC;QACF,MAAM,WAAW,GAAG,MAAM,uBAAA,IAAI,8CAAc,CAAC,OAAO,EAAE,CAAC;QACvD,IAAI;YACF,MAAM,QAAQ,GAAG,gBAAgB,CAAC,GAAG,CAAC,CAAC,eAAe,EAAE,EAAE;gBACxD,MAAM,EAAE,OAAO,EAAE,GAAG,uBAAA,IAAI,8FAAyB,MAA7B,IAAI,EAA0B,eAAe,CAAC,CAAC;gBACnE,OAAO,OAAO,CAAC;YACjB,CAAC,CAAC,CAAC;YAEH,IAAI,CAAC,YAAY,CAAC,QAAQ,CAAC,CAAC;YAE5B,uDAAuD;YACvD,MAAM,cAAc,GAAG,gBAAgB,CAAC,GAAG,CAAC,KAAK,EAAE,eAAe,EAAE,EAAE;gBACpE,MAAM,EAAE,OAAO,EAAE,QAAQ,EAAE,QAAQ,EAAE,GACnC,uBAAA,IAAI,8FAAyB,MAA7B,IAAI,EAA0B,eAAe,CAAC,CAAC;gBACjD,MAAM,EAAE,iBAAiB,EAAE,GAAG,IAAI,CAAC,KAAK,CAAC;gBACzC,MAAM,EAAE,6BAA6B,EAAE,GAAG,IAAI,CAAC,eAAe,CAAC,IAAI,CACjE,gCAAgC,CACjC,CAAC;gBAEF,MAAM,gBAAgB,GAAG,6BAA6B;oBACpD,CAAC,CAAC,MAAM,CAAC,IAAI,CAAC,iBAAiB,CAAC,OAAO,CAAC,CAAC;oBACzC,CAAC,CAAC,CAAC,IAAA,uCAAoB,EAAC,eAAe,CAAC,OAAO,CAAC,CAAC,CAAC;gBAEpD,MAAM,gBAAgB,GAAG,EAAE,GAAG,iBAAiB,CAAC,OAAO,CAAC,EAAE,CAAC;gBAE3D,MAAM,qBAAqB,GAAG,uBAAA,IAAI,qDAAqB;oBACrD,CAAC,CAAC,uBAAA,IAAI,0DAA0B,MAA9B,IAAI,EAA2B,gBAAgB,EAAE,eAAe,CAAC;oBACnE,CAAC,CAAC,OAAO,CAAC,OAAO,CAAC,EAAE,CAAC,CAAC;gBAExB,IAAI,IAAA,mBAAW,EAAC,kEAAuC,EAAE,OAAO,CAAC,EAAE;oBACjE,MAAM,eAAe,GAAG,kEAAuC,CAC7D,OAAO,CACE,CAAC;oBAEZ,MAAM,QAAQ,GAAG,IAAI,oBAAQ,CAC3B,eAAe,EACf,yCAA6B,EAC7B,IAAI,wBAAY,CAAC,QAAQ,CAAC,CAC3B,CAAC;oBAEF,MAAM,cAAc,GAAG,MAAM,QAAQ,CAAC,QAAQ,CAAC,gBAAgB,EAAE;wBAC/D,4CAA4C;qBAC7C,CAAC,CAAC;oBAEH,gBAAgB,CAAC,OAAO,CAAC,CAAC,OAAO,EAAE,KAAK,EAAE,EAAE;wBAC1C,gBAAgB,CAAC,OAAO,CAAC,GAAG;4BAC1B,OAAO,EAAG,cAAc,CAAC,KAAK,CAAe,CAAC,WAAW,EAAE;yBAC5D,CAAC;oBACJ,CAAC,CAAC,CAAC;iBACJ;qBAAM;oBACL,4DAA4D;oBAC5D,MAAM,IAAA,oCAAuB,EAAe;wBAC1C,MAAM,EAAE,gBAAgB;wBACxB,SAAS,EAAE,oCAAuB;wBAClC,aAAa,EAAE,SAAS;wBACxB,SAAS,EAAE,KAAK,EAAE,aAAmB,EAAE,KAAe,EAAE,EAAE;4BACxD,MAAM,eAAe,GAAG,KAAK,CAAC,GAAG,CAAC,KAAK,EAAE,OAAe,EAAE,EAAE;gCAC1D,MAAM,aAAa,GAAG,MAAM,uBAAA,IAAI,0FAAqB,MAAzB,IAAI,EAC9B,OAAO,EACP,QAAQ,CACT,CAAC,KAAK,CAAC,GAAG,EAAE,CAAC,IAAI,CAAC,CAAC;gCAEpB,0BAA0B;gCAC1B,IAAI,aAAa,EAAE;oCACjB,gBAAgB,CAAC,OAAO,CAAC,GAAG;wCAC1B,OAAO,EAAE,aAAa;qCACvB,CAAC;iCACH;4BACH,CAAC,CAAC,CAAC;4BAEH,MAAM,OAAO,CAAC,UAAU,CAAC,eAAe,CAAC,CAAC;4BAC1C,OAAO,aAAa,CAAC;wBACvB,CAAC;qBACF,CAAC,CAAC;iBACJ;gBAED,MAAM,mBAAmB,GAAG,CAAC,MAAM,qBAAqB,CAGvD,CAAC;gBAEF,MAAM,CAAC,OAAO,CAAC,mBAAmB,CAAC,CAAC,OAAO,CAAC,CAAC,CAAC,OAAO,EAAE,OAAO,CAAC,EAAE,EAAE;oBACjE,gBAAgB,CAAC,OAAO,CAAC,GAAG;wBAC1B,GAAG,gBAAgB,CAAC,OAAO,CAAC;wBAC5B,aAAa,EAAE,OAAO;qBACvB,CAAC;gBACJ,CAAC,CAAC,CAAC;gBAEH,2DAA2D;gBAC3D,OAAO,EAAE,OAAO,EAAE,gBAAgB,EAAE,CAAC;YACvC,CAAC,CAAC,CAAC;YAEH,6DAA6D;YAC7D,MAAM,UAAU,GAAG,MAAM,OAAO,CAAC,UAAU,CAAC,cAAc,CAAC,CAAC;YAE5D,yEAAyE;YACzE,MAAM,qBAAqB,GACzB,IAAA,kBAAS,EAAC,IAAI,CAAC,KAAK,CAAC,iBAAiB,CAAC,CAAC;YAC1C,IAAI,UAAU,GAAG,KAAK,CAAC;YAEvB,UAAU,CAAC,OAAO,CAAC,CAAC,MAAM,EAAE,EAAE;gBAC5B,IAAI,MAAM,CAAC,MAAM,KAAK,WAAW,EAAE;oBACjC,MAAM,EAAE,OAAO,EAAE,gBAAgB,EAAE,GAAG,MAAM,CAAC,KAAK,CAAC;oBACnD,oDAAoD;oBACpD,IAAI,CAAC,IAAA,gBAAO,EAAC,qBAAqB,CAAC,OAAO,CAAC,EAAE,gBAAgB,CAAC,EAAE;wBAC9D,qBAAqB,CAAC,OAAO,CAAC,GAAG,gBAAgB,CAAC;wBAClD,UAAU,GAAG,IAAI,CAAC;qBACnB;iBACF;YACH,CAAC,CAAC,CAAC;YAEH,4DAA4D;YAC5D,IAAI,UAAU,EAAE;gBACd,IAAI,CAAC,MAAM,CAAC,CAAC,KAAK,EAAE,EAAE;oBACpB,KAAK,CAAC,iBAAiB,GAAG,qBAAqB,CAAC;gBAClD,CAAC,CAAC,CAAC;aACJ;SACF;gBAAS;YACR,WAAW,EAAE,CAAC;SACf;IACH,CAAC;IAmBD;;;;;;OAMG;IACH,KAAK,CAAC,wBAAwB,CAC5B,SAAmB,EACnB,eAAiC;QAIjC,MAAM,EAAE,QAAQ,EAAE,GAAG,uBAAA,IAAI,8FAAyB,MAA7B,IAAI,EAA0B,eAAe,CAAC,CAAC;QAEpE,4DAA4D;QAC5D,OAAO,MAAM,OAAO,CAAC,GAAG,CACtB,SAAS,CAAC,GAAG,CACX,CAAC,OAAO,EAAwD,EAAE;YAChE,OAAO,IAAA,2CAAwB,EAAC,KAAK,IAAI,EAAE;gBACzC,IAAA,cAAM,EAAC,QAAQ,EAAE,mBAAmB,CAAC,CAAC;gBACtC,MAAM,OAAO,GAAG,MAAM,IAAA,wBAAK,EAAC,QAAQ,EAAE,YAAY,EAAE,CAAC,OAAO,CAAC,CAAC,CAAC;gBAE/D,IAAI,aAA4B,CAAC;gBACjC,IAAI,uBAAA,IAAI,qDAAqB,EAAE;oBAC7B,aAAa,GAAG,CACd,MAAM,uBAAA,IAAI,0DAA0B,MAA9B,IAAI,EAA2B,CAAC,OAAO,CAAC,EAAE,eAAe,CAAC,CACjE,CAAC,OAAO,CAAC,CAAC;iBACZ;gBACD,OAAO,CAAC,OAAO,EAAE,OAAO,EAAE,aAAa,CAAC,CAAC;YAC3C,CAAC,CAAC,CAAC;QACL,CAAC,CACF,CACF,CAAC,IAAI,CAAC,CAAC,KAAK,EAAE,EAAE;YACf,OAAO,KAAK,CAAC,MAAM,CAAC,CAAC,GAAG,EAAE,IAAI,EAAE,EAAE;gBAChC,IAAI,CAAC,IAAI,EAAE;oBACT,OAAO,GAAG,CAAC;iBACZ;gBAED,MAAM,CAAC,OAAO,EAAE,OAAO,EAAE,aAAa,CAAC,GAAG,IAAI,CAAC;gBAC/C,OAAO;oBACL,GAAG,GAAG;oBACN,CAAC,OAAO,CAAC,EAAE;wBACT,OAAO;wBACP,aAAa;qBACd;iBACF,CAAC;YACJ,CAAC,EAAE,EAAE,CAAC,CAAC;QACT,CAAC,CAAC,CAAC;IACL,CAAC;CACF;AA1YD,4DA0YC;+VA9P0B,eAAiC;IAKxD,MAAM,uBAAuB,GAC3B,eAAe;QACf,IAAI,CAAC,eAAe,CAAC,IAAI,CAAC,4BAA4B,CAAC;aACpD,uBAAuB,CAAC;IAC7B,MAAM,EACJ,aAAa,EAAE,EAAE,OAAO,EAAE,EAC1B,QAAQ,GACT,GAAG,IAAI,CAAC,eAAe,CAAC,IAAI,CAC3B,wCAAwC,EACxC,uBAAuB,CACxB,CAAC;IAEF,OAAO;QACL,OAAO;QACP,QAAQ;QACR,QAAQ,EAAE,IAAI,mBAAQ,CAAC,QAAQ,CAAC;KACjC,CAAC;AACJ,CAAC;IAQC,MAAM,EAAE,8BAA8B,EAAE,GAAG,IAAI,CAAC,eAAe,CAAC,IAAI,CAClE,4BAA4B,CAC7B,CAAC;IACF,OAAO,MAAM,CAAC,MAAM,CAAC,8BAA8B,CAAC,CAAC,OAAO,CAC1D,CAAC,oBAAoB,EAAE,EAAE,CACvB,oBAAoB,CAAC,YAAY,CAAC,GAAG,CACnC,CAAC,WAAW,EAAE,EAAE,CAAC,WAAW,CAAC,eAAe,CAC7C,CACJ,CAAC;AACJ,CAAC;AAoJD;;;;;;GAMG;AACH,KAAK,wDACH,OAAe,EACf,QAAmB;IAEnB,OAAO,MAAM,IAAA,2CAAwB,EAAC,KAAK,IAAI,EAAE;QAC/C,IAAA,cAAM,EAAC,QAAQ,EAAE,mBAAmB,CAAC,CAAC;QACtC,OAAO,MAAM,IAAA,wBAAK,EAAC,QAAQ,EAAE,YAAY,EAAE,CAAC,OAAO,CAAC,CAAC,CAAC;IACxD,CAAC,CAAC,CAAC;AACL,CAAC;AAsDH,kBAAe,wBAAwB,CAAC","sourcesContent":["import type { BigNumber } from '@ethersproject/bignumber';\nimport { Contract } from '@ethersproject/contracts';\nimport { Web3Provider } from '@ethersproject/providers';\nimport type {\n AccountsControllerSelectedEvmAccountChangeEvent,\n AccountsControllerGetSelectedAccountAction,\n AccountsControllerListAccountsAction,\n AccountsControllerSelectedAccountChangeEvent,\n} from '@metamask/accounts-controller';\nimport type {\n ControllerStateChangeEvent,\n ControllerGetStateAction,\n RestrictedMessenger,\n} from '@metamask/base-controller';\nimport {\n query,\n safelyExecuteWithTimeout,\n toChecksumHexAddress,\n} from '@metamask/controller-utils';\nimport type { SafeEventEmitterProvider } from '@metamask/eth-json-rpc-provider';\nimport EthQuery from '@metamask/eth-query';\nimport type {\n NetworkClientId,\n NetworkControllerGetNetworkClientByIdAction,\n NetworkControllerGetStateAction,\n} from '@metamask/network-controller';\nimport { StaticIntervalPollingController } from '@metamask/polling-controller';\nimport type { PreferencesControllerGetStateAction } from '@metamask/preferences-controller';\nimport { assert, hasProperty } from '@metamask/utils';\nimport { Mutex } from 'async-mutex';\nimport { cloneDeep, isEqual } from 'lodash';\nimport abiSingleCallBalancesContract from 'single-call-balance-checker-abi';\n\nimport {\n SINGLE_CALL_BALANCES_ADDRESS_BY_CHAINID,\n type AssetsContractController,\n type StakedBalance,\n} from './AssetsContractController';\nimport { reduceInBatchesSerially, TOKEN_PRICES_BATCH_SIZE } from './assetsUtil';\n\n/**\n * The name of the {@link AccountTrackerController}.\n */\nconst controllerName = 'AccountTrackerController';\n\n/**\n * AccountInformation\n *\n * Account information object\n *\n * balance - Hex string of an account balance in wei\n *\n * stakedBalance - Hex string of an account staked balance in wei\n */\nexport type AccountInformation = {\n balance: string;\n stakedBalance?: string;\n};\n\n/**\n * AccountTrackerControllerState\n *\n * Account tracker controller state\n *\n * accountsByChainId - Map of addresses to account information by chain\n */\nexport type AccountTrackerControllerState = {\n accountsByChainId: Record<string, { [address: string]: AccountInformation }>;\n};\n\nconst accountTrackerMetadata = {\n accountsByChainId: {\n persist: true,\n anonymous: false,\n },\n};\n\n/**\n * The action that can be performed to get the state of the {@link AccountTrackerController}.\n */\nexport type AccountTrackerControllerGetStateAction = ControllerGetStateAction<\n typeof controllerName,\n AccountTrackerControllerState\n>;\n\n/**\n * The actions that can be performed using the {@link AccountTrackerController}.\n */\nexport type AccountTrackerControllerActions =\n AccountTrackerControllerGetStateAction;\n\n/**\n * The messenger of the {@link AccountTrackerController} for communication.\n */\nexport type AllowedActions =\n | AccountsControllerListAccountsAction\n | PreferencesControllerGetStateAction\n | AccountsControllerGetSelectedAccountAction\n | NetworkControllerGetStateAction\n | NetworkControllerGetNetworkClientByIdAction;\n\n/**\n * The event that {@link AccountTrackerController} can emit.\n */\nexport type AccountTrackerControllerStateChangeEvent =\n ControllerStateChangeEvent<\n typeof controllerName,\n AccountTrackerControllerState\n >;\n\n/**\n * The events that {@link AccountTrackerController} can emit.\n */\nexport type AccountTrackerControllerEvents =\n AccountTrackerControllerStateChangeEvent;\n\n/**\n * The external events available to the {@link AccountTrackerController}.\n */\nexport type AllowedEvents =\n | AccountsControllerSelectedEvmAccountChangeEvent\n | AccountsControllerSelectedAccountChangeEvent;\n\n/**\n * The messenger of the {@link AccountTrackerController}.\n */\nexport type AccountTrackerControllerMessenger = RestrictedMessenger<\n typeof controllerName,\n AccountTrackerControllerActions | AllowedActions,\n AccountTrackerControllerEvents | AllowedEvents,\n AllowedActions['type'],\n AllowedEvents['type']\n>;\n\n/** The input to start polling for the {@link AccountTrackerController} */\ntype AccountTrackerPollingInput = {\n networkClientIds: NetworkClientId[];\n};\n\n/**\n * Controller that tracks the network balances for all user accounts.\n */\nexport class AccountTrackerController extends StaticIntervalPollingController<AccountTrackerPollingInput>()<\n typeof controllerName,\n AccountTrackerControllerState,\n AccountTrackerControllerMessenger\n> {\n readonly #refreshMutex = new Mutex();\n\n readonly #includeStakedAssets: boolean;\n\n readonly #getStakedBalanceForChain: AssetsContractController['getStakedBalanceForChain'];\n\n /**\n * Creates an AccountTracker instance.\n *\n * @param options - The controller options.\n * @param options.interval - Polling interval used to fetch new account balances.\n * @param options.state - Initial state to set on this controller.\n * @param options.messenger - The controller messaging system.\n * @param options.getStakedBalanceForChain - The function to get the staked native asset balance for a chain.\n * @param options.includeStakedAssets - Whether to include staked assets in the account balances.\n */\n constructor({\n interval = 10000,\n state,\n messenger,\n getStakedBalanceForChain,\n includeStakedAssets = false,\n }: {\n interval?: number;\n state?: Partial<AccountTrackerControllerState>;\n messenger: AccountTrackerControllerMessenger;\n getStakedBalanceForChain: AssetsContractController['getStakedBalanceForChain'];\n includeStakedAssets?: boolean;\n }) {\n const { selectedNetworkClientId } = messenger.call(\n 'NetworkController:getState',\n );\n const {\n configuration: { chainId },\n } = messenger.call(\n 'NetworkController:getNetworkClientById',\n selectedNetworkClientId,\n );\n super({\n name: controllerName,\n messenger,\n state: {\n accountsByChainId: {\n [chainId]: {},\n },\n ...state,\n },\n metadata: accountTrackerMetadata,\n });\n this.#getStakedBalanceForChain = getStakedBalanceForChain;\n\n this.#includeStakedAssets = includeStakedAssets;\n\n this.setIntervalLength(interval);\n\n this.messagingSystem.subscribe(\n 'AccountsController:selectedEvmAccountChange',\n (newAddress, prevAddress) => {\n if (newAddress !== prevAddress) {\n // Making an async call for this new event\n // eslint-disable-next-line @typescript-eslint/no-floating-promises\n this.refresh(this.#getNetworkClientIds());\n }\n },\n (event): string => event.address,\n );\n }\n\n private syncAccounts(newChainIds: string[]) {\n const accountsByChainId = cloneDeep(this.state.accountsByChainId);\n const { selectedNetworkClientId } = this.messagingSystem.call(\n 'NetworkController:getState',\n );\n const {\n configuration: { chainId: currentChainId },\n } = this.messagingSystem.call(\n 'NetworkController:getNetworkClientById',\n selectedNetworkClientId,\n );\n\n const existing = Object.keys(accountsByChainId?.[currentChainId] ?? {});\n\n // Initialize new chain IDs if they don't exist\n newChainIds.forEach((newChainId) => {\n if (!accountsByChainId[newChainId]) {\n accountsByChainId[newChainId] = {};\n existing.forEach((address) => {\n accountsByChainId[newChainId][address] = { balance: '0x0' };\n });\n }\n });\n\n // Note: The address from the preferences controller are checksummed\n // The addresses from the accounts controller are lowercased\n const addresses = Object.values(\n this.messagingSystem\n .call('AccountsController:listAccounts')\n .map((internalAccount) =>\n toChecksumHexAddress(internalAccount.address),\n ),\n );\n const newAddresses = addresses.filter(\n (address) => !existing.includes(address),\n );\n const oldAddresses = existing.filter(\n (address) => !addresses.includes(address),\n );\n Object.keys(accountsByChainId).forEach((chainId) => {\n newAddresses.forEach((address) => {\n accountsByChainId[chainId][address] = {\n balance: '0x0',\n };\n });\n });\n\n Object.keys(accountsByChainId).forEach((chainId) => {\n oldAddresses.forEach((address) => {\n delete accountsByChainId[chainId][address];\n });\n });\n\n if (!isEqual(this.state.accountsByChainId, accountsByChainId)) {\n this.update((state) => {\n state.accountsByChainId = accountsByChainId;\n });\n }\n }\n\n /**\n * Resolves a networkClientId to a network client config\n * or globally selected network config if not provided\n *\n * @param networkClientId - Optional networkClientId to fetch a network client with\n * @returns network client config\n */\n #getCorrectNetworkClient(networkClientId?: NetworkClientId): {\n chainId: string;\n provider: SafeEventEmitterProvider;\n ethQuery?: EthQuery;\n } {\n const selectedNetworkClientId =\n networkClientId ??\n this.messagingSystem.call('NetworkController:getState')\n .selectedNetworkClientId;\n const {\n configuration: { chainId },\n provider,\n } = this.messagingSystem.call(\n 'NetworkController:getNetworkClientById',\n selectedNetworkClientId,\n );\n\n return {\n chainId,\n provider,\n ethQuery: new EthQuery(provider),\n };\n }\n\n /**\n * Retrieves the list of network client IDs.\n *\n * @returns An array of network client IDs.\n */\n #getNetworkClientIds(): NetworkClientId[] {\n const { networkConfigurationsByChainId } = this.messagingSystem.call(\n 'NetworkController:getState',\n );\n return Object.values(networkConfigurationsByChainId).flatMap(\n (networkConfiguration) =>\n networkConfiguration.rpcEndpoints.map(\n (rpcEndpoint) => rpcEndpoint.networkClientId,\n ),\n );\n }\n\n /**\n * Refreshes the balances of the accounts using the networkClientId\n *\n * @param input - The input for the poll.\n * @param input.networkClientIds - The network client IDs used to get balances.\n */\n async _executePoll({\n networkClientIds,\n }: AccountTrackerPollingInput): Promise<void> {\n // TODO: Either fix this lint violation or explain why it's necessary to ignore.\n // eslint-disable-next-line @typescript-eslint/no-floating-promises\n this.refresh(networkClientIds);\n }\n\n /**\n * Refreshes the balances of the accounts depending on the multi-account setting.\n * If multi-account is disabled, only updates the selected account balance.\n * If multi-account is enabled, updates balances for all accounts.\n *\n * @param networkClientIds - Optional network client IDs to fetch a network client with\n */\n async refresh(networkClientIds: NetworkClientId[]) {\n const selectedAccount = this.messagingSystem.call(\n 'AccountsController:getSelectedAccount',\n );\n const releaseLock = await this.#refreshMutex.acquire();\n try {\n const chainIds = networkClientIds.map((networkClientId) => {\n const { chainId } = this.#getCorrectNetworkClient(networkClientId);\n return chainId;\n });\n\n this.syncAccounts(chainIds);\n\n // Create an array of promises for each networkClientId\n const updatePromises = networkClientIds.map(async (networkClientId) => {\n const { chainId, ethQuery, provider } =\n this.#getCorrectNetworkClient(networkClientId);\n const { accountsByChainId } = this.state;\n const { isMultiAccountBalancesEnabled } = this.messagingSystem.call(\n 'PreferencesController:getState',\n );\n\n const accountsToUpdate = isMultiAccountBalancesEnabled\n ? Object.keys(accountsByChainId[chainId])\n : [toChecksumHexAddress(selectedAccount.address)];\n\n const accountsForChain = { ...accountsByChainId[chainId] };\n\n const stakedBalancesPromise = this.#includeStakedAssets\n ? this.#getStakedBalanceForChain(accountsToUpdate, networkClientId)\n : Promise.resolve({});\n\n if (hasProperty(SINGLE_CALL_BALANCES_ADDRESS_BY_CHAINID, chainId)) {\n const contractAddress = SINGLE_CALL_BALANCES_ADDRESS_BY_CHAINID[\n chainId\n ] as string;\n\n const contract = new Contract(\n contractAddress,\n abiSingleCallBalancesContract,\n new Web3Provider(provider),\n );\n\n const nativeBalances = await contract.balances(accountsToUpdate, [\n '0x0000000000000000000000000000000000000000',\n ]);\n\n accountsToUpdate.forEach((address, index) => {\n accountsForChain[address] = {\n balance: (nativeBalances[index] as BigNumber).toHexString(),\n };\n });\n } else {\n // Process accounts in batches using reduceInBatchesSerially\n await reduceInBatchesSerially<string, void>({\n values: accountsToUpdate,\n batchSize: TOKEN_PRICES_BATCH_SIZE,\n initialResult: undefined,\n eachBatch: async (workingResult: void, batch: string[]) => {\n const balancePromises = batch.map(async (address: string) => {\n const balanceResult = await this.#getBalanceFromChain(\n address,\n ethQuery,\n ).catch(() => null);\n\n // Update account balances\n if (balanceResult) {\n accountsForChain[address] = {\n balance: balanceResult,\n };\n }\n });\n\n await Promise.allSettled(balancePromises);\n return workingResult;\n },\n });\n }\n\n const stakedBalanceResult = (await stakedBalancesPromise) as Record<\n string,\n StakedBalance\n >;\n\n Object.entries(stakedBalanceResult).forEach(([address, balance]) => {\n accountsForChain[address] = {\n ...accountsForChain[address],\n stakedBalance: balance,\n };\n });\n\n // After all batches are processed, return the updated data\n return { chainId, accountsForChain };\n });\n\n // Wait for all networkClientId updates to settle in parallel\n const allResults = await Promise.allSettled(updatePromises);\n\n // Build a _copy_ of the current state and track whether anything changed\n const nextAccountsByChainId: AccountTrackerControllerState['accountsByChainId'] =\n cloneDeep(this.state.accountsByChainId);\n let hasChanges = false;\n\n allResults.forEach((result) => {\n if (result.status === 'fulfilled') {\n const { chainId, accountsForChain } = result.value;\n // Only mark as changed if the incoming data differs\n if (!isEqual(nextAccountsByChainId[chainId], accountsForChain)) {\n nextAccountsByChainId[chainId] = accountsForChain;\n hasChanges = true;\n }\n }\n });\n\n // 👇🏻 call `update` only when something is new / different\n if (hasChanges) {\n this.update((state) => {\n state.accountsByChainId = nextAccountsByChainId;\n });\n }\n } finally {\n releaseLock();\n }\n }\n\n /**\n * Fetches the balance of a given address from the blockchain.\n *\n * @param address - The account address to fetch the balance for.\n * @param ethQuery - The EthQuery instance to query getBalnce with.\n * @returns A promise that resolves to the balance in a hex string format.\n */\n async #getBalanceFromChain(\n address: string,\n ethQuery?: EthQuery,\n ): Promise<string | undefined> {\n return await safelyExecuteWithTimeout(async () => {\n assert(ethQuery, 'Provider not set.');\n return await query(ethQuery, 'getBalance', [address]);\n });\n }\n\n /**\n * Sync accounts balances with some additional addresses.\n *\n * @param addresses - the additional addresses, may be hardware wallet addresses.\n * @param networkClientId - Optional networkClientId to fetch a network client with.\n * @returns accounts - addresses with synced balance\n */\n async syncBalanceWithAddresses(\n addresses: string[],\n networkClientId?: NetworkClientId,\n ): Promise<\n Record<string, { balance: string; stakedBalance?: StakedBalance }>\n > {\n const { ethQuery } = this.#getCorrectNetworkClient(networkClientId);\n\n // TODO: This should use multicall when enabled by the user.\n return await Promise.all(\n addresses.map(\n (address): Promise<[string, string, StakedBalance] | undefined> => {\n return safelyExecuteWithTimeout(async () => {\n assert(ethQuery, 'Provider not set.');\n const balance = await query(ethQuery, 'getBalance', [address]);\n\n let stakedBalance: StakedBalance;\n if (this.#includeStakedAssets) {\n stakedBalance = (\n await this.#getStakedBalanceForChain([address], networkClientId)\n )[address];\n }\n return [address, balance, stakedBalance];\n });\n },\n ),\n ).then((value) => {\n return value.reduce((obj, item) => {\n if (!item) {\n return obj;\n }\n\n const [address, balance, stakedBalance] = item;\n return {\n ...obj,\n [address]: {\n balance,\n stakedBalance,\n },\n };\n }, {});\n });\n }\n}\n\nexport default AccountTrackerController;\n"]}
|
|
1
|
+
{"version":3,"file":"AccountTrackerController.cjs","sourceRoot":"","sources":["../src/AccountTrackerController.ts"],"names":[],"mappings":"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","sourcesContent":["import type { BigNumber } from '@ethersproject/bignumber';\nimport { Contract } from '@ethersproject/contracts';\nimport { Web3Provider } from '@ethersproject/providers';\nimport type {\n AccountsControllerSelectedEvmAccountChangeEvent,\n AccountsControllerGetSelectedAccountAction,\n AccountsControllerListAccountsAction,\n AccountsControllerSelectedAccountChangeEvent,\n} from '@metamask/accounts-controller';\nimport type {\n ControllerStateChangeEvent,\n ControllerGetStateAction,\n RestrictedMessenger,\n} from '@metamask/base-controller';\nimport {\n query,\n safelyExecuteWithTimeout,\n toChecksumHexAddress,\n} from '@metamask/controller-utils';\nimport EthQuery from '@metamask/eth-query';\nimport type {\n NetworkClientId,\n NetworkControllerGetNetworkClientByIdAction,\n NetworkControllerGetStateAction,\n} from '@metamask/network-controller';\nimport { StaticIntervalPollingController } from '@metamask/polling-controller';\nimport type { PreferencesControllerGetStateAction } from '@metamask/preferences-controller';\nimport { assert, hasProperty } from '@metamask/utils';\nimport { Mutex } from 'async-mutex';\nimport { cloneDeep, isEqual } from 'lodash';\nimport abiSingleCallBalancesContract from 'single-call-balance-checker-abi';\n\nimport {\n SINGLE_CALL_BALANCES_ADDRESS_BY_CHAINID,\n type AssetsContractController,\n type StakedBalance,\n} from './AssetsContractController';\nimport { reduceInBatchesSerially, TOKEN_PRICES_BATCH_SIZE } from './assetsUtil';\n\n/**\n * The name of the {@link AccountTrackerController}.\n */\nconst controllerName = 'AccountTrackerController';\n\n/**\n * AccountInformation\n *\n * Account information object\n *\n * balance - Hex string of an account balance in wei\n *\n * stakedBalance - Hex string of an account staked balance in wei\n */\nexport type AccountInformation = {\n balance: string;\n stakedBalance?: string;\n};\n\n/**\n * AccountTrackerControllerState\n *\n * Account tracker controller state\n *\n * accountsByChainId - Map of addresses to account information by chain\n */\nexport type AccountTrackerControllerState = {\n accountsByChainId: Record<string, { [address: string]: AccountInformation }>;\n};\n\nconst accountTrackerMetadata = {\n accountsByChainId: {\n persist: true,\n anonymous: false,\n },\n};\n\n/**\n * The action that can be performed to get the state of the {@link AccountTrackerController}.\n */\nexport type AccountTrackerControllerGetStateAction = ControllerGetStateAction<\n typeof controllerName,\n AccountTrackerControllerState\n>;\n\n/**\n * The actions that can be performed using the {@link AccountTrackerController}.\n */\nexport type AccountTrackerControllerActions =\n AccountTrackerControllerGetStateAction;\n\n/**\n * The messenger of the {@link AccountTrackerController} for communication.\n */\nexport type AllowedActions =\n | AccountsControllerListAccountsAction\n | PreferencesControllerGetStateAction\n | AccountsControllerGetSelectedAccountAction\n | NetworkControllerGetStateAction\n | NetworkControllerGetNetworkClientByIdAction;\n\n/**\n * The event that {@link AccountTrackerController} can emit.\n */\nexport type AccountTrackerControllerStateChangeEvent =\n ControllerStateChangeEvent<\n typeof controllerName,\n AccountTrackerControllerState\n >;\n\n/**\n * The events that {@link AccountTrackerController} can emit.\n */\nexport type AccountTrackerControllerEvents =\n AccountTrackerControllerStateChangeEvent;\n\n/**\n * The external events available to the {@link AccountTrackerController}.\n */\nexport type AllowedEvents =\n | AccountsControllerSelectedEvmAccountChangeEvent\n | AccountsControllerSelectedAccountChangeEvent;\n\n/**\n * The messenger of the {@link AccountTrackerController}.\n */\nexport type AccountTrackerControllerMessenger = RestrictedMessenger<\n typeof controllerName,\n AccountTrackerControllerActions | AllowedActions,\n AccountTrackerControllerEvents | AllowedEvents,\n AllowedActions['type'],\n AllowedEvents['type']\n>;\n\n/** The input to start polling for the {@link AccountTrackerController} */\ntype AccountTrackerPollingInput = {\n networkClientIds: NetworkClientId[];\n};\n\n/**\n * Controller that tracks the network balances for all user accounts.\n */\nexport class AccountTrackerController extends StaticIntervalPollingController<AccountTrackerPollingInput>()<\n typeof controllerName,\n AccountTrackerControllerState,\n AccountTrackerControllerMessenger\n> {\n readonly #refreshMutex = new Mutex();\n\n readonly #includeStakedAssets: boolean;\n\n readonly #getStakedBalanceForChain: AssetsContractController['getStakedBalanceForChain'];\n\n /**\n * Creates an AccountTracker instance.\n *\n * @param options - The controller options.\n * @param options.interval - Polling interval used to fetch new account balances.\n * @param options.state - Initial state to set on this controller.\n * @param options.messenger - The controller messaging system.\n * @param options.getStakedBalanceForChain - The function to get the staked native asset balance for a chain.\n * @param options.includeStakedAssets - Whether to include staked assets in the account balances.\n */\n constructor({\n interval = 10000,\n state,\n messenger,\n getStakedBalanceForChain,\n includeStakedAssets = false,\n }: {\n interval?: number;\n state?: Partial<AccountTrackerControllerState>;\n messenger: AccountTrackerControllerMessenger;\n getStakedBalanceForChain: AssetsContractController['getStakedBalanceForChain'];\n includeStakedAssets?: boolean;\n }) {\n const { selectedNetworkClientId } = messenger.call(\n 'NetworkController:getState',\n );\n const {\n configuration: { chainId },\n } = messenger.call(\n 'NetworkController:getNetworkClientById',\n selectedNetworkClientId,\n );\n super({\n name: controllerName,\n messenger,\n state: {\n accountsByChainId: {\n [chainId]: {},\n },\n ...state,\n },\n metadata: accountTrackerMetadata,\n });\n this.#getStakedBalanceForChain = getStakedBalanceForChain;\n\n this.#includeStakedAssets = includeStakedAssets;\n\n this.setIntervalLength(interval);\n\n this.messagingSystem.subscribe(\n 'AccountsController:selectedEvmAccountChange',\n (newAddress, prevAddress) => {\n if (newAddress !== prevAddress) {\n // Making an async call for this new event\n // eslint-disable-next-line @typescript-eslint/no-floating-promises\n this.refresh(this.#getNetworkClientIds());\n }\n },\n (event): string => event.address,\n );\n }\n\n private syncAccounts(newChainIds: string[]) {\n const accountsByChainId = cloneDeep(this.state.accountsByChainId);\n const { selectedNetworkClientId } = this.messagingSystem.call(\n 'NetworkController:getState',\n );\n const {\n configuration: { chainId: currentChainId },\n } = this.messagingSystem.call(\n 'NetworkController:getNetworkClientById',\n selectedNetworkClientId,\n );\n\n const existing = Object.keys(accountsByChainId?.[currentChainId] ?? {});\n\n // Initialize new chain IDs if they don't exist\n newChainIds.forEach((newChainId) => {\n if (!accountsByChainId[newChainId]) {\n accountsByChainId[newChainId] = {};\n existing.forEach((address) => {\n accountsByChainId[newChainId][address] = { balance: '0x0' };\n });\n }\n });\n\n // Note: The address from the preferences controller are checksummed\n // The addresses from the accounts controller are lowercased\n const addresses = Object.values(\n this.messagingSystem\n .call('AccountsController:listAccounts')\n .map((internalAccount) =>\n toChecksumHexAddress(internalAccount.address),\n ),\n );\n const newAddresses = addresses.filter(\n (address) => !existing.includes(address),\n );\n const oldAddresses = existing.filter(\n (address) => !addresses.includes(address),\n );\n Object.keys(accountsByChainId).forEach((chainId) => {\n newAddresses.forEach((address) => {\n accountsByChainId[chainId][address] = {\n balance: '0x0',\n };\n });\n });\n\n Object.keys(accountsByChainId).forEach((chainId) => {\n oldAddresses.forEach((address) => {\n delete accountsByChainId[chainId][address];\n });\n });\n\n if (!isEqual(this.state.accountsByChainId, accountsByChainId)) {\n this.update((state) => {\n state.accountsByChainId = accountsByChainId;\n });\n }\n }\n\n /**\n * Resolves a networkClientId to a network client config\n * or globally selected network config if not provided\n *\n * @param networkClientId - Optional networkClientId to fetch a network client with\n * @returns network client config\n */\n #getCorrectNetworkClient(networkClientId?: NetworkClientId) {\n const selectedNetworkClientId =\n networkClientId ??\n this.messagingSystem.call('NetworkController:getState')\n .selectedNetworkClientId;\n const {\n configuration: { chainId },\n provider,\n blockTracker,\n } = this.messagingSystem.call(\n 'NetworkController:getNetworkClientById',\n selectedNetworkClientId,\n );\n\n return {\n chainId,\n provider,\n ethQuery: new EthQuery(provider),\n blockTracker,\n };\n }\n\n /**\n * Retrieves the list of network client IDs.\n *\n * @returns An array of network client IDs.\n */\n #getNetworkClientIds(): NetworkClientId[] {\n const { networkConfigurationsByChainId } = this.messagingSystem.call(\n 'NetworkController:getState',\n );\n return Object.values(networkConfigurationsByChainId).flatMap(\n (networkConfiguration) =>\n networkConfiguration.rpcEndpoints.map(\n (rpcEndpoint) => rpcEndpoint.networkClientId,\n ),\n );\n }\n\n /**\n * Refreshes the balances of the accounts using the networkClientId\n *\n * @param input - The input for the poll.\n * @param input.networkClientIds - The network client IDs used to get balances.\n */\n async _executePoll({\n networkClientIds,\n }: AccountTrackerPollingInput): Promise<void> {\n // TODO: Either fix this lint violation or explain why it's necessary to ignore.\n // eslint-disable-next-line @typescript-eslint/no-floating-promises\n this.refresh(networkClientIds);\n }\n\n /**\n * Refreshes the balances of the accounts depending on the multi-account setting.\n * If multi-account is disabled, only updates the selected account balance.\n * If multi-account is enabled, updates balances for all accounts.\n *\n * @param networkClientIds - Optional network client IDs to fetch a network client with\n */\n async refresh(networkClientIds: NetworkClientId[]) {\n const selectedAccount = this.messagingSystem.call(\n 'AccountsController:getSelectedAccount',\n );\n const releaseLock = await this.#refreshMutex.acquire();\n try {\n const chainIds = networkClientIds.map((networkClientId) => {\n const { chainId } = this.#getCorrectNetworkClient(networkClientId);\n return chainId;\n });\n\n this.syncAccounts(chainIds);\n\n // Create an array of promises for each networkClientId\n const updatePromises = networkClientIds.map(async (networkClientId) => {\n const { chainId, ethQuery, provider, blockTracker } =\n this.#getCorrectNetworkClient(networkClientId);\n const { accountsByChainId } = this.state;\n const { isMultiAccountBalancesEnabled } = this.messagingSystem.call(\n 'PreferencesController:getState',\n );\n\n const accountsToUpdate = isMultiAccountBalancesEnabled\n ? Object.keys(accountsByChainId[chainId])\n : [toChecksumHexAddress(selectedAccount.address)];\n\n const accountsForChain = { ...accountsByChainId[chainId] };\n\n // Force fresh block data before multicall\n // TODO: This is a temporary fix to ensure that the block number is up to date.\n // We should remove this once we have a better solution for this on the block tracker controller.\n await safelyExecuteWithTimeout(() =>\n blockTracker?.checkForLatestBlock?.(),\n );\n\n const stakedBalancesPromise = this.#includeStakedAssets\n ? this.#getStakedBalanceForChain(accountsToUpdate, networkClientId)\n : Promise.resolve({});\n\n if (hasProperty(SINGLE_CALL_BALANCES_ADDRESS_BY_CHAINID, chainId)) {\n const contractAddress = SINGLE_CALL_BALANCES_ADDRESS_BY_CHAINID[\n chainId\n ] as string;\n\n const contract = new Contract(\n contractAddress,\n abiSingleCallBalancesContract,\n new Web3Provider(provider),\n );\n\n const nativeBalances = await safelyExecuteWithTimeout(\n () =>\n contract.balances(accountsToUpdate, [\n '0x0000000000000000000000000000000000000000',\n ]) as Promise<BigNumber[]>,\n false,\n 3_000, // 3s max call for multicall contract call\n );\n\n if (nativeBalances) {\n accountsToUpdate.forEach((address, index) => {\n accountsForChain[address] = {\n balance: nativeBalances[index].toHexString(),\n };\n });\n }\n } else {\n // Process accounts in batches using reduceInBatchesSerially\n await reduceInBatchesSerially<string, void>({\n values: accountsToUpdate,\n batchSize: TOKEN_PRICES_BATCH_SIZE,\n initialResult: undefined,\n eachBatch: async (workingResult: void, batch: string[]) => {\n const balancePromises = batch.map(async (address: string) => {\n const balanceResult = await this.#getBalanceFromChain(\n address,\n ethQuery,\n ).catch(() => null);\n\n // Update account balances\n if (balanceResult) {\n accountsForChain[address] = {\n balance: balanceResult,\n };\n }\n });\n\n await Promise.allSettled(balancePromises);\n return workingResult;\n },\n });\n }\n\n const stakedBalanceResult = await safelyExecuteWithTimeout(\n async () =>\n (await stakedBalancesPromise) as Record<string, StakedBalance>,\n );\n\n Object.entries(stakedBalanceResult ?? {}).forEach(\n ([address, balance]) => {\n accountsForChain[address] = {\n ...accountsForChain[address],\n stakedBalance: balance,\n };\n },\n );\n\n // After all batches are processed, return the updated data\n return { chainId, accountsForChain };\n });\n\n // Wait for all networkClientId updates to settle in parallel\n const allResults = await Promise.allSettled(updatePromises);\n\n // Build a _copy_ of the current state and track whether anything changed\n const nextAccountsByChainId: AccountTrackerControllerState['accountsByChainId'] =\n cloneDeep(this.state.accountsByChainId);\n let hasChanges = false;\n\n allResults.forEach((result) => {\n if (result.status === 'fulfilled') {\n const { chainId, accountsForChain } = result.value;\n // Only mark as changed if the incoming data differs\n if (!isEqual(nextAccountsByChainId[chainId], accountsForChain)) {\n nextAccountsByChainId[chainId] = accountsForChain;\n hasChanges = true;\n }\n }\n });\n\n // 👇🏻 call `update` only when something is new / different\n if (hasChanges) {\n this.update((state) => {\n state.accountsByChainId = nextAccountsByChainId;\n });\n }\n } finally {\n releaseLock();\n }\n }\n\n /**\n * Fetches the balance of a given address from the blockchain.\n *\n * @param address - The account address to fetch the balance for.\n * @param ethQuery - The EthQuery instance to query getBalnce with.\n * @returns A promise that resolves to the balance in a hex string format.\n */\n async #getBalanceFromChain(\n address: string,\n ethQuery?: EthQuery,\n ): Promise<string | undefined> {\n return await safelyExecuteWithTimeout(async () => {\n assert(ethQuery, 'Provider not set.');\n return await query(ethQuery, 'getBalance', [address]);\n });\n }\n\n /**\n * Sync accounts balances with some additional addresses.\n *\n * @param addresses - the additional addresses, may be hardware wallet addresses.\n * @param networkClientId - Optional networkClientId to fetch a network client with.\n * @returns accounts - addresses with synced balance\n */\n async syncBalanceWithAddresses(\n addresses: string[],\n networkClientId?: NetworkClientId,\n ): Promise<\n Record<string, { balance: string; stakedBalance?: StakedBalance }>\n > {\n const { ethQuery } = this.#getCorrectNetworkClient(networkClientId);\n\n // TODO: This should use multicall when enabled by the user.\n return await Promise.all(\n addresses.map(\n (address): Promise<[string, string, StakedBalance] | undefined> => {\n return safelyExecuteWithTimeout(async () => {\n assert(ethQuery, 'Provider not set.');\n const balance = await query(ethQuery, 'getBalance', [address]);\n\n let stakedBalance: StakedBalance;\n if (this.#includeStakedAssets) {\n stakedBalance = (\n await this.#getStakedBalanceForChain([address], networkClientId)\n )[address];\n }\n return [address, balance, stakedBalance];\n });\n },\n ),\n ).then((value) => {\n return value.reduce((obj, item) => {\n if (!item) {\n return obj;\n }\n\n const [address, balance, stakedBalance] = item;\n return {\n ...obj,\n [address]: {\n balance,\n stakedBalance,\n },\n };\n }, {});\n });\n }\n}\n\nexport default AccountTrackerController;\n"]}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"AccountTrackerController.d.cts","sourceRoot":"","sources":["../src/AccountTrackerController.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,EACV,+CAA+C,EAC/C,0CAA0C,EAC1C,oCAAoC,EACpC,4CAA4C,EAC7C,sCAAsC;AACvC,OAAO,KAAK,EACV,0BAA0B,EAC1B,wBAAwB,EACxB,mBAAmB,EACpB,kCAAkC;
|
|
1
|
+
{"version":3,"file":"AccountTrackerController.d.cts","sourceRoot":"","sources":["../src/AccountTrackerController.ts"],"names":[],"mappings":"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"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"AccountTrackerController.d.mts","sourceRoot":"","sources":["../src/AccountTrackerController.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,EACV,+CAA+C,EAC/C,0CAA0C,EAC1C,oCAAoC,EACpC,4CAA4C,EAC7C,sCAAsC;AACvC,OAAO,KAAK,EACV,0BAA0B,EAC1B,wBAAwB,EACxB,mBAAmB,EACpB,kCAAkC;
|
|
1
|
+
{"version":3,"file":"AccountTrackerController.d.mts","sourceRoot":"","sources":["../src/AccountTrackerController.ts"],"names":[],"mappings":"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"}
|
|
@@ -151,27 +151,33 @@ export class AccountTrackerController extends StaticIntervalPollingController()
|
|
|
151
151
|
this.syncAccounts(chainIds);
|
|
152
152
|
// Create an array of promises for each networkClientId
|
|
153
153
|
const updatePromises = networkClientIds.map(async (networkClientId) => {
|
|
154
|
-
const { chainId, ethQuery, provider } = __classPrivateFieldGet(this, _AccountTrackerController_instances, "m", _AccountTrackerController_getCorrectNetworkClient).call(this, networkClientId);
|
|
154
|
+
const { chainId, ethQuery, provider, blockTracker } = __classPrivateFieldGet(this, _AccountTrackerController_instances, "m", _AccountTrackerController_getCorrectNetworkClient).call(this, networkClientId);
|
|
155
155
|
const { accountsByChainId } = this.state;
|
|
156
156
|
const { isMultiAccountBalancesEnabled } = this.messagingSystem.call('PreferencesController:getState');
|
|
157
157
|
const accountsToUpdate = isMultiAccountBalancesEnabled
|
|
158
158
|
? Object.keys(accountsByChainId[chainId])
|
|
159
159
|
: [toChecksumHexAddress(selectedAccount.address)];
|
|
160
160
|
const accountsForChain = { ...accountsByChainId[chainId] };
|
|
161
|
+
// Force fresh block data before multicall
|
|
162
|
+
// TODO: This is a temporary fix to ensure that the block number is up to date.
|
|
163
|
+
// We should remove this once we have a better solution for this on the block tracker controller.
|
|
164
|
+
await safelyExecuteWithTimeout(() => blockTracker?.checkForLatestBlock?.());
|
|
161
165
|
const stakedBalancesPromise = __classPrivateFieldGet(this, _AccountTrackerController_includeStakedAssets, "f")
|
|
162
166
|
? __classPrivateFieldGet(this, _AccountTrackerController_getStakedBalanceForChain, "f").call(this, accountsToUpdate, networkClientId)
|
|
163
167
|
: Promise.resolve({});
|
|
164
168
|
if (hasProperty(SINGLE_CALL_BALANCES_ADDRESS_BY_CHAINID, chainId)) {
|
|
165
169
|
const contractAddress = SINGLE_CALL_BALANCES_ADDRESS_BY_CHAINID[chainId];
|
|
166
170
|
const contract = new Contract(contractAddress, abiSingleCallBalancesContract, new Web3Provider(provider));
|
|
167
|
-
const nativeBalances = await contract.balances(accountsToUpdate, [
|
|
171
|
+
const nativeBalances = await safelyExecuteWithTimeout(() => contract.balances(accountsToUpdate, [
|
|
168
172
|
'0x0000000000000000000000000000000000000000',
|
|
169
|
-
]);
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
173
|
+
]), false, 3000);
|
|
174
|
+
if (nativeBalances) {
|
|
175
|
+
accountsToUpdate.forEach((address, index) => {
|
|
176
|
+
accountsForChain[address] = {
|
|
177
|
+
balance: nativeBalances[index].toHexString(),
|
|
178
|
+
};
|
|
179
|
+
});
|
|
180
|
+
}
|
|
175
181
|
}
|
|
176
182
|
else {
|
|
177
183
|
// Process accounts in batches using reduceInBatchesSerially
|
|
@@ -194,8 +200,8 @@ export class AccountTrackerController extends StaticIntervalPollingController()
|
|
|
194
200
|
},
|
|
195
201
|
});
|
|
196
202
|
}
|
|
197
|
-
const stakedBalanceResult = (await stakedBalancesPromise);
|
|
198
|
-
Object.entries(stakedBalanceResult).forEach(([address, balance]) => {
|
|
203
|
+
const stakedBalanceResult = await safelyExecuteWithTimeout(async () => (await stakedBalancesPromise));
|
|
204
|
+
Object.entries(stakedBalanceResult ?? {}).forEach(([address, balance]) => {
|
|
199
205
|
accountsForChain[address] = {
|
|
200
206
|
...accountsForChain[address],
|
|
201
207
|
stakedBalance: balance,
|
|
@@ -271,11 +277,12 @@ _AccountTrackerController_refreshMutex = new WeakMap(), _AccountTrackerControlle
|
|
|
271
277
|
const selectedNetworkClientId = networkClientId ??
|
|
272
278
|
this.messagingSystem.call('NetworkController:getState')
|
|
273
279
|
.selectedNetworkClientId;
|
|
274
|
-
const { configuration: { chainId }, provider, } = this.messagingSystem.call('NetworkController:getNetworkClientById', selectedNetworkClientId);
|
|
280
|
+
const { configuration: { chainId }, provider, blockTracker, } = this.messagingSystem.call('NetworkController:getNetworkClientById', selectedNetworkClientId);
|
|
275
281
|
return {
|
|
276
282
|
chainId,
|
|
277
283
|
provider,
|
|
278
284
|
ethQuery: new EthQuery(provider),
|
|
285
|
+
blockTracker,
|
|
279
286
|
};
|
|
280
287
|
}, _AccountTrackerController_getNetworkClientIds = function _AccountTrackerController_getNetworkClientIds() {
|
|
281
288
|
const { networkConfigurationsByChainId } = this.messagingSystem.call('NetworkController:getState');
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"AccountTrackerController.mjs","sourceRoot":"","sources":["../src/AccountTrackerController.ts"],"names":[],"mappings":"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","sourcesContent":["import type { BigNumber } from '@ethersproject/bignumber';\nimport { Contract } from '@ethersproject/contracts';\nimport { Web3Provider } from '@ethersproject/providers';\nimport type {\n AccountsControllerSelectedEvmAccountChangeEvent,\n AccountsControllerGetSelectedAccountAction,\n AccountsControllerListAccountsAction,\n AccountsControllerSelectedAccountChangeEvent,\n} from '@metamask/accounts-controller';\nimport type {\n ControllerStateChangeEvent,\n ControllerGetStateAction,\n RestrictedMessenger,\n} from '@metamask/base-controller';\nimport {\n query,\n safelyExecuteWithTimeout,\n toChecksumHexAddress,\n} from '@metamask/controller-utils';\nimport type { SafeEventEmitterProvider } from '@metamask/eth-json-rpc-provider';\nimport EthQuery from '@metamask/eth-query';\nimport type {\n NetworkClientId,\n NetworkControllerGetNetworkClientByIdAction,\n NetworkControllerGetStateAction,\n} from '@metamask/network-controller';\nimport { StaticIntervalPollingController } from '@metamask/polling-controller';\nimport type { PreferencesControllerGetStateAction } from '@metamask/preferences-controller';\nimport { assert, hasProperty } from '@metamask/utils';\nimport { Mutex } from 'async-mutex';\nimport { cloneDeep, isEqual } from 'lodash';\nimport abiSingleCallBalancesContract from 'single-call-balance-checker-abi';\n\nimport {\n SINGLE_CALL_BALANCES_ADDRESS_BY_CHAINID,\n type AssetsContractController,\n type StakedBalance,\n} from './AssetsContractController';\nimport { reduceInBatchesSerially, TOKEN_PRICES_BATCH_SIZE } from './assetsUtil';\n\n/**\n * The name of the {@link AccountTrackerController}.\n */\nconst controllerName = 'AccountTrackerController';\n\n/**\n * AccountInformation\n *\n * Account information object\n *\n * balance - Hex string of an account balance in wei\n *\n * stakedBalance - Hex string of an account staked balance in wei\n */\nexport type AccountInformation = {\n balance: string;\n stakedBalance?: string;\n};\n\n/**\n * AccountTrackerControllerState\n *\n * Account tracker controller state\n *\n * accountsByChainId - Map of addresses to account information by chain\n */\nexport type AccountTrackerControllerState = {\n accountsByChainId: Record<string, { [address: string]: AccountInformation }>;\n};\n\nconst accountTrackerMetadata = {\n accountsByChainId: {\n persist: true,\n anonymous: false,\n },\n};\n\n/**\n * The action that can be performed to get the state of the {@link AccountTrackerController}.\n */\nexport type AccountTrackerControllerGetStateAction = ControllerGetStateAction<\n typeof controllerName,\n AccountTrackerControllerState\n>;\n\n/**\n * The actions that can be performed using the {@link AccountTrackerController}.\n */\nexport type AccountTrackerControllerActions =\n AccountTrackerControllerGetStateAction;\n\n/**\n * The messenger of the {@link AccountTrackerController} for communication.\n */\nexport type AllowedActions =\n | AccountsControllerListAccountsAction\n | PreferencesControllerGetStateAction\n | AccountsControllerGetSelectedAccountAction\n | NetworkControllerGetStateAction\n | NetworkControllerGetNetworkClientByIdAction;\n\n/**\n * The event that {@link AccountTrackerController} can emit.\n */\nexport type AccountTrackerControllerStateChangeEvent =\n ControllerStateChangeEvent<\n typeof controllerName,\n AccountTrackerControllerState\n >;\n\n/**\n * The events that {@link AccountTrackerController} can emit.\n */\nexport type AccountTrackerControllerEvents =\n AccountTrackerControllerStateChangeEvent;\n\n/**\n * The external events available to the {@link AccountTrackerController}.\n */\nexport type AllowedEvents =\n | AccountsControllerSelectedEvmAccountChangeEvent\n | AccountsControllerSelectedAccountChangeEvent;\n\n/**\n * The messenger of the {@link AccountTrackerController}.\n */\nexport type AccountTrackerControllerMessenger = RestrictedMessenger<\n typeof controllerName,\n AccountTrackerControllerActions | AllowedActions,\n AccountTrackerControllerEvents | AllowedEvents,\n AllowedActions['type'],\n AllowedEvents['type']\n>;\n\n/** The input to start polling for the {@link AccountTrackerController} */\ntype AccountTrackerPollingInput = {\n networkClientIds: NetworkClientId[];\n};\n\n/**\n * Controller that tracks the network balances for all user accounts.\n */\nexport class AccountTrackerController extends StaticIntervalPollingController<AccountTrackerPollingInput>()<\n typeof controllerName,\n AccountTrackerControllerState,\n AccountTrackerControllerMessenger\n> {\n readonly #refreshMutex = new Mutex();\n\n readonly #includeStakedAssets: boolean;\n\n readonly #getStakedBalanceForChain: AssetsContractController['getStakedBalanceForChain'];\n\n /**\n * Creates an AccountTracker instance.\n *\n * @param options - The controller options.\n * @param options.interval - Polling interval used to fetch new account balances.\n * @param options.state - Initial state to set on this controller.\n * @param options.messenger - The controller messaging system.\n * @param options.getStakedBalanceForChain - The function to get the staked native asset balance for a chain.\n * @param options.includeStakedAssets - Whether to include staked assets in the account balances.\n */\n constructor({\n interval = 10000,\n state,\n messenger,\n getStakedBalanceForChain,\n includeStakedAssets = false,\n }: {\n interval?: number;\n state?: Partial<AccountTrackerControllerState>;\n messenger: AccountTrackerControllerMessenger;\n getStakedBalanceForChain: AssetsContractController['getStakedBalanceForChain'];\n includeStakedAssets?: boolean;\n }) {\n const { selectedNetworkClientId } = messenger.call(\n 'NetworkController:getState',\n );\n const {\n configuration: { chainId },\n } = messenger.call(\n 'NetworkController:getNetworkClientById',\n selectedNetworkClientId,\n );\n super({\n name: controllerName,\n messenger,\n state: {\n accountsByChainId: {\n [chainId]: {},\n },\n ...state,\n },\n metadata: accountTrackerMetadata,\n });\n this.#getStakedBalanceForChain = getStakedBalanceForChain;\n\n this.#includeStakedAssets = includeStakedAssets;\n\n this.setIntervalLength(interval);\n\n this.messagingSystem.subscribe(\n 'AccountsController:selectedEvmAccountChange',\n (newAddress, prevAddress) => {\n if (newAddress !== prevAddress) {\n // Making an async call for this new event\n // eslint-disable-next-line @typescript-eslint/no-floating-promises\n this.refresh(this.#getNetworkClientIds());\n }\n },\n (event): string => event.address,\n );\n }\n\n private syncAccounts(newChainIds: string[]) {\n const accountsByChainId = cloneDeep(this.state.accountsByChainId);\n const { selectedNetworkClientId } = this.messagingSystem.call(\n 'NetworkController:getState',\n );\n const {\n configuration: { chainId: currentChainId },\n } = this.messagingSystem.call(\n 'NetworkController:getNetworkClientById',\n selectedNetworkClientId,\n );\n\n const existing = Object.keys(accountsByChainId?.[currentChainId] ?? {});\n\n // Initialize new chain IDs if they don't exist\n newChainIds.forEach((newChainId) => {\n if (!accountsByChainId[newChainId]) {\n accountsByChainId[newChainId] = {};\n existing.forEach((address) => {\n accountsByChainId[newChainId][address] = { balance: '0x0' };\n });\n }\n });\n\n // Note: The address from the preferences controller are checksummed\n // The addresses from the accounts controller are lowercased\n const addresses = Object.values(\n this.messagingSystem\n .call('AccountsController:listAccounts')\n .map((internalAccount) =>\n toChecksumHexAddress(internalAccount.address),\n ),\n );\n const newAddresses = addresses.filter(\n (address) => !existing.includes(address),\n );\n const oldAddresses = existing.filter(\n (address) => !addresses.includes(address),\n );\n Object.keys(accountsByChainId).forEach((chainId) => {\n newAddresses.forEach((address) => {\n accountsByChainId[chainId][address] = {\n balance: '0x0',\n };\n });\n });\n\n Object.keys(accountsByChainId).forEach((chainId) => {\n oldAddresses.forEach((address) => {\n delete accountsByChainId[chainId][address];\n });\n });\n\n if (!isEqual(this.state.accountsByChainId, accountsByChainId)) {\n this.update((state) => {\n state.accountsByChainId = accountsByChainId;\n });\n }\n }\n\n /**\n * Resolves a networkClientId to a network client config\n * or globally selected network config if not provided\n *\n * @param networkClientId - Optional networkClientId to fetch a network client with\n * @returns network client config\n */\n #getCorrectNetworkClient(networkClientId?: NetworkClientId): {\n chainId: string;\n provider: SafeEventEmitterProvider;\n ethQuery?: EthQuery;\n } {\n const selectedNetworkClientId =\n networkClientId ??\n this.messagingSystem.call('NetworkController:getState')\n .selectedNetworkClientId;\n const {\n configuration: { chainId },\n provider,\n } = this.messagingSystem.call(\n 'NetworkController:getNetworkClientById',\n selectedNetworkClientId,\n );\n\n return {\n chainId,\n provider,\n ethQuery: new EthQuery(provider),\n };\n }\n\n /**\n * Retrieves the list of network client IDs.\n *\n * @returns An array of network client IDs.\n */\n #getNetworkClientIds(): NetworkClientId[] {\n const { networkConfigurationsByChainId } = this.messagingSystem.call(\n 'NetworkController:getState',\n );\n return Object.values(networkConfigurationsByChainId).flatMap(\n (networkConfiguration) =>\n networkConfiguration.rpcEndpoints.map(\n (rpcEndpoint) => rpcEndpoint.networkClientId,\n ),\n );\n }\n\n /**\n * Refreshes the balances of the accounts using the networkClientId\n *\n * @param input - The input for the poll.\n * @param input.networkClientIds - The network client IDs used to get balances.\n */\n async _executePoll({\n networkClientIds,\n }: AccountTrackerPollingInput): Promise<void> {\n // TODO: Either fix this lint violation or explain why it's necessary to ignore.\n // eslint-disable-next-line @typescript-eslint/no-floating-promises\n this.refresh(networkClientIds);\n }\n\n /**\n * Refreshes the balances of the accounts depending on the multi-account setting.\n * If multi-account is disabled, only updates the selected account balance.\n * If multi-account is enabled, updates balances for all accounts.\n *\n * @param networkClientIds - Optional network client IDs to fetch a network client with\n */\n async refresh(networkClientIds: NetworkClientId[]) {\n const selectedAccount = this.messagingSystem.call(\n 'AccountsController:getSelectedAccount',\n );\n const releaseLock = await this.#refreshMutex.acquire();\n try {\n const chainIds = networkClientIds.map((networkClientId) => {\n const { chainId } = this.#getCorrectNetworkClient(networkClientId);\n return chainId;\n });\n\n this.syncAccounts(chainIds);\n\n // Create an array of promises for each networkClientId\n const updatePromises = networkClientIds.map(async (networkClientId) => {\n const { chainId, ethQuery, provider } =\n this.#getCorrectNetworkClient(networkClientId);\n const { accountsByChainId } = this.state;\n const { isMultiAccountBalancesEnabled } = this.messagingSystem.call(\n 'PreferencesController:getState',\n );\n\n const accountsToUpdate = isMultiAccountBalancesEnabled\n ? Object.keys(accountsByChainId[chainId])\n : [toChecksumHexAddress(selectedAccount.address)];\n\n const accountsForChain = { ...accountsByChainId[chainId] };\n\n const stakedBalancesPromise = this.#includeStakedAssets\n ? this.#getStakedBalanceForChain(accountsToUpdate, networkClientId)\n : Promise.resolve({});\n\n if (hasProperty(SINGLE_CALL_BALANCES_ADDRESS_BY_CHAINID, chainId)) {\n const contractAddress = SINGLE_CALL_BALANCES_ADDRESS_BY_CHAINID[\n chainId\n ] as string;\n\n const contract = new Contract(\n contractAddress,\n abiSingleCallBalancesContract,\n new Web3Provider(provider),\n );\n\n const nativeBalances = await contract.balances(accountsToUpdate, [\n '0x0000000000000000000000000000000000000000',\n ]);\n\n accountsToUpdate.forEach((address, index) => {\n accountsForChain[address] = {\n balance: (nativeBalances[index] as BigNumber).toHexString(),\n };\n });\n } else {\n // Process accounts in batches using reduceInBatchesSerially\n await reduceInBatchesSerially<string, void>({\n values: accountsToUpdate,\n batchSize: TOKEN_PRICES_BATCH_SIZE,\n initialResult: undefined,\n eachBatch: async (workingResult: void, batch: string[]) => {\n const balancePromises = batch.map(async (address: string) => {\n const balanceResult = await this.#getBalanceFromChain(\n address,\n ethQuery,\n ).catch(() => null);\n\n // Update account balances\n if (balanceResult) {\n accountsForChain[address] = {\n balance: balanceResult,\n };\n }\n });\n\n await Promise.allSettled(balancePromises);\n return workingResult;\n },\n });\n }\n\n const stakedBalanceResult = (await stakedBalancesPromise) as Record<\n string,\n StakedBalance\n >;\n\n Object.entries(stakedBalanceResult).forEach(([address, balance]) => {\n accountsForChain[address] = {\n ...accountsForChain[address],\n stakedBalance: balance,\n };\n });\n\n // After all batches are processed, return the updated data\n return { chainId, accountsForChain };\n });\n\n // Wait for all networkClientId updates to settle in parallel\n const allResults = await Promise.allSettled(updatePromises);\n\n // Build a _copy_ of the current state and track whether anything changed\n const nextAccountsByChainId: AccountTrackerControllerState['accountsByChainId'] =\n cloneDeep(this.state.accountsByChainId);\n let hasChanges = false;\n\n allResults.forEach((result) => {\n if (result.status === 'fulfilled') {\n const { chainId, accountsForChain } = result.value;\n // Only mark as changed if the incoming data differs\n if (!isEqual(nextAccountsByChainId[chainId], accountsForChain)) {\n nextAccountsByChainId[chainId] = accountsForChain;\n hasChanges = true;\n }\n }\n });\n\n // 👇🏻 call `update` only when something is new / different\n if (hasChanges) {\n this.update((state) => {\n state.accountsByChainId = nextAccountsByChainId;\n });\n }\n } finally {\n releaseLock();\n }\n }\n\n /**\n * Fetches the balance of a given address from the blockchain.\n *\n * @param address - The account address to fetch the balance for.\n * @param ethQuery - The EthQuery instance to query getBalnce with.\n * @returns A promise that resolves to the balance in a hex string format.\n */\n async #getBalanceFromChain(\n address: string,\n ethQuery?: EthQuery,\n ): Promise<string | undefined> {\n return await safelyExecuteWithTimeout(async () => {\n assert(ethQuery, 'Provider not set.');\n return await query(ethQuery, 'getBalance', [address]);\n });\n }\n\n /**\n * Sync accounts balances with some additional addresses.\n *\n * @param addresses - the additional addresses, may be hardware wallet addresses.\n * @param networkClientId - Optional networkClientId to fetch a network client with.\n * @returns accounts - addresses with synced balance\n */\n async syncBalanceWithAddresses(\n addresses: string[],\n networkClientId?: NetworkClientId,\n ): Promise<\n Record<string, { balance: string; stakedBalance?: StakedBalance }>\n > {\n const { ethQuery } = this.#getCorrectNetworkClient(networkClientId);\n\n // TODO: This should use multicall when enabled by the user.\n return await Promise.all(\n addresses.map(\n (address): Promise<[string, string, StakedBalance] | undefined> => {\n return safelyExecuteWithTimeout(async () => {\n assert(ethQuery, 'Provider not set.');\n const balance = await query(ethQuery, 'getBalance', [address]);\n\n let stakedBalance: StakedBalance;\n if (this.#includeStakedAssets) {\n stakedBalance = (\n await this.#getStakedBalanceForChain([address], networkClientId)\n )[address];\n }\n return [address, balance, stakedBalance];\n });\n },\n ),\n ).then((value) => {\n return value.reduce((obj, item) => {\n if (!item) {\n return obj;\n }\n\n const [address, balance, stakedBalance] = item;\n return {\n ...obj,\n [address]: {\n balance,\n stakedBalance,\n },\n };\n }, {});\n });\n }\n}\n\nexport default AccountTrackerController;\n"]}
|
|
1
|
+
{"version":3,"file":"AccountTrackerController.mjs","sourceRoot":"","sources":["../src/AccountTrackerController.ts"],"names":[],"mappings":";;;;;;;;;;;;;;;;;;AACA,OAAO,EAAE,QAAQ,EAAE,iCAAiC;AACpD,OAAO,EAAE,YAAY,EAAE,iCAAiC;AAYxD,OAAO,EACL,KAAK,EACL,wBAAwB,EACxB,oBAAoB,EACrB,mCAAmC;AACpC,OAAO,SAAQ,4BAA4B;;AAM3C,OAAO,EAAE,+BAA+B,EAAE,qCAAqC;AAE/E,OAAO,EAAE,MAAM,EAAE,WAAW,EAAE,wBAAwB;AACtD,OAAO,EAAE,KAAK,EAAE,oBAAoB;;;AAEpC,OAAO,8BAA6B,wCAAwC;;AAE5E,OAAO,EACL,uCAAuC,EAGxC,uCAAmC;AACpC,OAAO,EAAE,uBAAuB,EAAE,uBAAuB,EAAE,yBAAqB;AAEhF;;GAEG;AACH,MAAM,cAAc,GAAG,0BAA0B,CAAC;AA2BlD,MAAM,sBAAsB,GAAG;IAC7B,iBAAiB,EAAE;QACjB,OAAO,EAAE,IAAI;QACb,SAAS,EAAE,KAAK;KACjB;CACF,CAAC;AAgEF;;GAEG;AACH,MAAM,OAAO,wBAAyB,SAAQ,+BAA+B,EAI5E;IAOC;;;;;;;;;OASG;IACH,YAAY,EACV,QAAQ,GAAG,KAAK,EAChB,KAAK,EACL,SAAS,EACT,wBAAwB,EACxB,mBAAmB,GAAG,KAAK,GAO5B;QACC,MAAM,EAAE,uBAAuB,EAAE,GAAG,SAAS,CAAC,IAAI,CAChD,4BAA4B,CAC7B,CAAC;QACF,MAAM,EACJ,aAAa,EAAE,EAAE,OAAO,EAAE,GAC3B,GAAG,SAAS,CAAC,IAAI,CAChB,wCAAwC,EACxC,uBAAuB,CACxB,CAAC;QACF,KAAK,CAAC;YACJ,IAAI,EAAE,cAAc;YACpB,SAAS;YACT,KAAK,EAAE;gBACL,iBAAiB,EAAE;oBACjB,CAAC,OAAO,CAAC,EAAE,EAAE;iBACd;gBACD,GAAG,KAAK;aACT;YACD,QAAQ,EAAE,sBAAsB;SACjC,CAAC,CAAC;;QAhDI,iDAAgB,IAAI,KAAK,EAAE,EAAC;QAE5B,gEAA8B;QAE9B,qEAAgF;QA6CvF,uBAAA,IAAI,sDAA6B,wBAAwB,MAAA,CAAC;QAE1D,uBAAA,IAAI,iDAAwB,mBAAmB,MAAA,CAAC;QAEhD,IAAI,CAAC,iBAAiB,CAAC,QAAQ,CAAC,CAAC;QAEjC,IAAI,CAAC,eAAe,CAAC,SAAS,CAC5B,6CAA6C,EAC7C,CAAC,UAAU,EAAE,WAAW,EAAE,EAAE;YAC1B,IAAI,UAAU,KAAK,WAAW,EAAE;gBAC9B,0CAA0C;gBAC1C,mEAAmE;gBACnE,IAAI,CAAC,OAAO,CAAC,uBAAA,IAAI,0FAAqB,MAAzB,IAAI,CAAuB,CAAC,CAAC;aAC3C;QACH,CAAC,EACD,CAAC,KAAK,EAAU,EAAE,CAAC,KAAK,CAAC,OAAO,CACjC,CAAC;IACJ,CAAC;IAEO,YAAY,CAAC,WAAqB;QACxC,MAAM,iBAAiB,GAAG,SAAS,CAAC,IAAI,CAAC,KAAK,CAAC,iBAAiB,CAAC,CAAC;QAClE,MAAM,EAAE,uBAAuB,EAAE,GAAG,IAAI,CAAC,eAAe,CAAC,IAAI,CAC3D,4BAA4B,CAC7B,CAAC;QACF,MAAM,EACJ,aAAa,EAAE,EAAE,OAAO,EAAE,cAAc,EAAE,GAC3C,GAAG,IAAI,CAAC,eAAe,CAAC,IAAI,CAC3B,wCAAwC,EACxC,uBAAuB,CACxB,CAAC;QAEF,MAAM,QAAQ,GAAG,MAAM,CAAC,IAAI,CAAC,iBAAiB,EAAE,CAAC,cAAc,CAAC,IAAI,EAAE,CAAC,CAAC;QAExE,+CAA+C;QAC/C,WAAW,CAAC,OAAO,CAAC,CAAC,UAAU,EAAE,EAAE;YACjC,IAAI,CAAC,iBAAiB,CAAC,UAAU,CAAC,EAAE;gBAClC,iBAAiB,CAAC,UAAU,CAAC,GAAG,EAAE,CAAC;gBACnC,QAAQ,CAAC,OAAO,CAAC,CAAC,OAAO,EAAE,EAAE;oBAC3B,iBAAiB,CAAC,UAAU,CAAC,CAAC,OAAO,CAAC,GAAG,EAAE,OAAO,EAAE,KAAK,EAAE,CAAC;gBAC9D,CAAC,CAAC,CAAC;aACJ;QACH,CAAC,CAAC,CAAC;QAEH,oEAAoE;QACpE,4DAA4D;QAC5D,MAAM,SAAS,GAAG,MAAM,CAAC,MAAM,CAC7B,IAAI,CAAC,eAAe;aACjB,IAAI,CAAC,iCAAiC,CAAC;aACvC,GAAG,CAAC,CAAC,eAAe,EAAE,EAAE,CACvB,oBAAoB,CAAC,eAAe,CAAC,OAAO,CAAC,CAC9C,CACJ,CAAC;QACF,MAAM,YAAY,GAAG,SAAS,CAAC,MAAM,CACnC,CAAC,OAAO,EAAE,EAAE,CAAC,CAAC,QAAQ,CAAC,QAAQ,CAAC,OAAO,CAAC,CACzC,CAAC;QACF,MAAM,YAAY,GAAG,QAAQ,CAAC,MAAM,CAClC,CAAC,OAAO,EAAE,EAAE,CAAC,CAAC,SAAS,CAAC,QAAQ,CAAC,OAAO,CAAC,CAC1C,CAAC;QACF,MAAM,CAAC,IAAI,CAAC,iBAAiB,CAAC,CAAC,OAAO,CAAC,CAAC,OAAO,EAAE,EAAE;YACjD,YAAY,CAAC,OAAO,CAAC,CAAC,OAAO,EAAE,EAAE;gBAC/B,iBAAiB,CAAC,OAAO,CAAC,CAAC,OAAO,CAAC,GAAG;oBACpC,OAAO,EAAE,KAAK;iBACf,CAAC;YACJ,CAAC,CAAC,CAAC;QACL,CAAC,CAAC,CAAC;QAEH,MAAM,CAAC,IAAI,CAAC,iBAAiB,CAAC,CAAC,OAAO,CAAC,CAAC,OAAO,EAAE,EAAE;YACjD,YAAY,CAAC,OAAO,CAAC,CAAC,OAAO,EAAE,EAAE;gBAC/B,OAAO,iBAAiB,CAAC,OAAO,CAAC,CAAC,OAAO,CAAC,CAAC;YAC7C,CAAC,CAAC,CAAC;QACL,CAAC,CAAC,CAAC;QAEH,IAAI,CAAC,OAAO,CAAC,IAAI,CAAC,KAAK,CAAC,iBAAiB,EAAE,iBAAiB,CAAC,EAAE;YAC7D,IAAI,CAAC,MAAM,CAAC,CAAC,KAAK,EAAE,EAAE;gBACpB,KAAK,CAAC,iBAAiB,GAAG,iBAAiB,CAAC;YAC9C,CAAC,CAAC,CAAC;SACJ;IACH,CAAC;IAgDD;;;;;OAKG;IACH,KAAK,CAAC,YAAY,CAAC,EACjB,gBAAgB,GACW;QAC3B,gFAAgF;QAChF,mEAAmE;QACnE,IAAI,CAAC,OAAO,CAAC,gBAAgB,CAAC,CAAC;IACjC,CAAC;IAED;;;;;;OAMG;IACH,KAAK,CAAC,OAAO,CAAC,gBAAmC;QAC/C,MAAM,eAAe,GAAG,IAAI,CAAC,eAAe,CAAC,IAAI,CAC/C,uCAAuC,CACxC,CAAC;QACF,MAAM,WAAW,GAAG,MAAM,uBAAA,IAAI,8CAAc,CAAC,OAAO,EAAE,CAAC;QACvD,IAAI;YACF,MAAM,QAAQ,GAAG,gBAAgB,CAAC,GAAG,CAAC,CAAC,eAAe,EAAE,EAAE;gBACxD,MAAM,EAAE,OAAO,EAAE,GAAG,uBAAA,IAAI,8FAAyB,MAA7B,IAAI,EAA0B,eAAe,CAAC,CAAC;gBACnE,OAAO,OAAO,CAAC;YACjB,CAAC,CAAC,CAAC;YAEH,IAAI,CAAC,YAAY,CAAC,QAAQ,CAAC,CAAC;YAE5B,uDAAuD;YACvD,MAAM,cAAc,GAAG,gBAAgB,CAAC,GAAG,CAAC,KAAK,EAAE,eAAe,EAAE,EAAE;gBACpE,MAAM,EAAE,OAAO,EAAE,QAAQ,EAAE,QAAQ,EAAE,YAAY,EAAE,GACjD,uBAAA,IAAI,8FAAyB,MAA7B,IAAI,EAA0B,eAAe,CAAC,CAAC;gBACjD,MAAM,EAAE,iBAAiB,EAAE,GAAG,IAAI,CAAC,KAAK,CAAC;gBACzC,MAAM,EAAE,6BAA6B,EAAE,GAAG,IAAI,CAAC,eAAe,CAAC,IAAI,CACjE,gCAAgC,CACjC,CAAC;gBAEF,MAAM,gBAAgB,GAAG,6BAA6B;oBACpD,CAAC,CAAC,MAAM,CAAC,IAAI,CAAC,iBAAiB,CAAC,OAAO,CAAC,CAAC;oBACzC,CAAC,CAAC,CAAC,oBAAoB,CAAC,eAAe,CAAC,OAAO,CAAC,CAAC,CAAC;gBAEpD,MAAM,gBAAgB,GAAG,EAAE,GAAG,iBAAiB,CAAC,OAAO,CAAC,EAAE,CAAC;gBAE3D,0CAA0C;gBAC1C,+EAA+E;gBAC/E,iGAAiG;gBACjG,MAAM,wBAAwB,CAAC,GAAG,EAAE,CAClC,YAAY,EAAE,mBAAmB,EAAE,EAAE,CACtC,CAAC;gBAEF,MAAM,qBAAqB,GAAG,uBAAA,IAAI,qDAAqB;oBACrD,CAAC,CAAC,uBAAA,IAAI,0DAA0B,MAA9B,IAAI,EAA2B,gBAAgB,EAAE,eAAe,CAAC;oBACnE,CAAC,CAAC,OAAO,CAAC,OAAO,CAAC,EAAE,CAAC,CAAC;gBAExB,IAAI,WAAW,CAAC,uCAAuC,EAAE,OAAO,CAAC,EAAE;oBACjE,MAAM,eAAe,GAAG,uCAAuC,CAC7D,OAAO,CACE,CAAC;oBAEZ,MAAM,QAAQ,GAAG,IAAI,QAAQ,CAC3B,eAAe,EACf,6BAA6B,EAC7B,IAAI,YAAY,CAAC,QAAQ,CAAC,CAC3B,CAAC;oBAEF,MAAM,cAAc,GAAG,MAAM,wBAAwB,CACnD,GAAG,EAAE,CACH,QAAQ,CAAC,QAAQ,CAAC,gBAAgB,EAAE;wBAClC,4CAA4C;qBAC7C,CAAyB,EAC5B,KAAK,EACL,IAAK,CACN,CAAC;oBAEF,IAAI,cAAc,EAAE;wBAClB,gBAAgB,CAAC,OAAO,CAAC,CAAC,OAAO,EAAE,KAAK,EAAE,EAAE;4BAC1C,gBAAgB,CAAC,OAAO,CAAC,GAAG;gCAC1B,OAAO,EAAE,cAAc,CAAC,KAAK,CAAC,CAAC,WAAW,EAAE;6BAC7C,CAAC;wBACJ,CAAC,CAAC,CAAC;qBACJ;iBACF;qBAAM;oBACL,4DAA4D;oBAC5D,MAAM,uBAAuB,CAAe;wBAC1C,MAAM,EAAE,gBAAgB;wBACxB,SAAS,EAAE,uBAAuB;wBAClC,aAAa,EAAE,SAAS;wBACxB,SAAS,EAAE,KAAK,EAAE,aAAmB,EAAE,KAAe,EAAE,EAAE;4BACxD,MAAM,eAAe,GAAG,KAAK,CAAC,GAAG,CAAC,KAAK,EAAE,OAAe,EAAE,EAAE;gCAC1D,MAAM,aAAa,GAAG,MAAM,uBAAA,IAAI,0FAAqB,MAAzB,IAAI,EAC9B,OAAO,EACP,QAAQ,CACT,CAAC,KAAK,CAAC,GAAG,EAAE,CAAC,IAAI,CAAC,CAAC;gCAEpB,0BAA0B;gCAC1B,IAAI,aAAa,EAAE;oCACjB,gBAAgB,CAAC,OAAO,CAAC,GAAG;wCAC1B,OAAO,EAAE,aAAa;qCACvB,CAAC;iCACH;4BACH,CAAC,CAAC,CAAC;4BAEH,MAAM,OAAO,CAAC,UAAU,CAAC,eAAe,CAAC,CAAC;4BAC1C,OAAO,aAAa,CAAC;wBACvB,CAAC;qBACF,CAAC,CAAC;iBACJ;gBAED,MAAM,mBAAmB,GAAG,MAAM,wBAAwB,CACxD,KAAK,IAAI,EAAE,CACT,CAAC,MAAM,qBAAqB,CAAkC,CACjE,CAAC;gBAEF,MAAM,CAAC,OAAO,CAAC,mBAAmB,IAAI,EAAE,CAAC,CAAC,OAAO,CAC/C,CAAC,CAAC,OAAO,EAAE,OAAO,CAAC,EAAE,EAAE;oBACrB,gBAAgB,CAAC,OAAO,CAAC,GAAG;wBAC1B,GAAG,gBAAgB,CAAC,OAAO,CAAC;wBAC5B,aAAa,EAAE,OAAO;qBACvB,CAAC;gBACJ,CAAC,CACF,CAAC;gBAEF,2DAA2D;gBAC3D,OAAO,EAAE,OAAO,EAAE,gBAAgB,EAAE,CAAC;YACvC,CAAC,CAAC,CAAC;YAEH,6DAA6D;YAC7D,MAAM,UAAU,GAAG,MAAM,OAAO,CAAC,UAAU,CAAC,cAAc,CAAC,CAAC;YAE5D,yEAAyE;YACzE,MAAM,qBAAqB,GACzB,SAAS,CAAC,IAAI,CAAC,KAAK,CAAC,iBAAiB,CAAC,CAAC;YAC1C,IAAI,UAAU,GAAG,KAAK,CAAC;YAEvB,UAAU,CAAC,OAAO,CAAC,CAAC,MAAM,EAAE,EAAE;gBAC5B,IAAI,MAAM,CAAC,MAAM,KAAK,WAAW,EAAE;oBACjC,MAAM,EAAE,OAAO,EAAE,gBAAgB,EAAE,GAAG,MAAM,CAAC,KAAK,CAAC;oBACnD,oDAAoD;oBACpD,IAAI,CAAC,OAAO,CAAC,qBAAqB,CAAC,OAAO,CAAC,EAAE,gBAAgB,CAAC,EAAE;wBAC9D,qBAAqB,CAAC,OAAO,CAAC,GAAG,gBAAgB,CAAC;wBAClD,UAAU,GAAG,IAAI,CAAC;qBACnB;iBACF;YACH,CAAC,CAAC,CAAC;YAEH,4DAA4D;YAC5D,IAAI,UAAU,EAAE;gBACd,IAAI,CAAC,MAAM,CAAC,CAAC,KAAK,EAAE,EAAE;oBACpB,KAAK,CAAC,iBAAiB,GAAG,qBAAqB,CAAC;gBAClD,CAAC,CAAC,CAAC;aACJ;SACF;gBAAS;YACR,WAAW,EAAE,CAAC;SACf;IACH,CAAC;IAmBD;;;;;;OAMG;IACH,KAAK,CAAC,wBAAwB,CAC5B,SAAmB,EACnB,eAAiC;QAIjC,MAAM,EAAE,QAAQ,EAAE,GAAG,uBAAA,IAAI,8FAAyB,MAA7B,IAAI,EAA0B,eAAe,CAAC,CAAC;QAEpE,4DAA4D;QAC5D,OAAO,MAAM,OAAO,CAAC,GAAG,CACtB,SAAS,CAAC,GAAG,CACX,CAAC,OAAO,EAAwD,EAAE;YAChE,OAAO,wBAAwB,CAAC,KAAK,IAAI,EAAE;gBACzC,MAAM,CAAC,QAAQ,EAAE,mBAAmB,CAAC,CAAC;gBACtC,MAAM,OAAO,GAAG,MAAM,KAAK,CAAC,QAAQ,EAAE,YAAY,EAAE,CAAC,OAAO,CAAC,CAAC,CAAC;gBAE/D,IAAI,aAA4B,CAAC;gBACjC,IAAI,uBAAA,IAAI,qDAAqB,EAAE;oBAC7B,aAAa,GAAG,CACd,MAAM,uBAAA,IAAI,0DAA0B,MAA9B,IAAI,EAA2B,CAAC,OAAO,CAAC,EAAE,eAAe,CAAC,CACjE,CAAC,OAAO,CAAC,CAAC;iBACZ;gBACD,OAAO,CAAC,OAAO,EAAE,OAAO,EAAE,aAAa,CAAC,CAAC;YAC3C,CAAC,CAAC,CAAC;QACL,CAAC,CACF,CACF,CAAC,IAAI,CAAC,CAAC,KAAK,EAAE,EAAE;YACf,OAAO,KAAK,CAAC,MAAM,CAAC,CAAC,GAAG,EAAE,IAAI,EAAE,EAAE;gBAChC,IAAI,CAAC,IAAI,EAAE;oBACT,OAAO,GAAG,CAAC;iBACZ;gBAED,MAAM,CAAC,OAAO,EAAE,OAAO,EAAE,aAAa,CAAC,GAAG,IAAI,CAAC;gBAC/C,OAAO;oBACL,GAAG,GAAG;oBACN,CAAC,OAAO,CAAC,EAAE;wBACT,OAAO;wBACP,aAAa;qBACd;iBACF,CAAC;YACJ,CAAC,EAAE,EAAE,CAAC,CAAC;QACT,CAAC,CAAC,CAAC;IACL,CAAC;CACF;+VA5Q0B,eAAiC;IACxD,MAAM,uBAAuB,GAC3B,eAAe;QACf,IAAI,CAAC,eAAe,CAAC,IAAI,CAAC,4BAA4B,CAAC;aACpD,uBAAuB,CAAC;IAC7B,MAAM,EACJ,aAAa,EAAE,EAAE,OAAO,EAAE,EAC1B,QAAQ,EACR,YAAY,GACb,GAAG,IAAI,CAAC,eAAe,CAAC,IAAI,CAC3B,wCAAwC,EACxC,uBAAuB,CACxB,CAAC;IAEF,OAAO;QACL,OAAO;QACP,QAAQ;QACR,QAAQ,EAAE,IAAI,QAAQ,CAAC,QAAQ,CAAC;QAChC,YAAY;KACb,CAAC;AACJ,CAAC;IAQC,MAAM,EAAE,8BAA8B,EAAE,GAAG,IAAI,CAAC,eAAe,CAAC,IAAI,CAClE,4BAA4B,CAC7B,CAAC;IACF,OAAO,MAAM,CAAC,MAAM,CAAC,8BAA8B,CAAC,CAAC,OAAO,CAC1D,CAAC,oBAAoB,EAAE,EAAE,CACvB,oBAAoB,CAAC,YAAY,CAAC,GAAG,CACnC,CAAC,WAAW,EAAE,EAAE,CAAC,WAAW,CAAC,eAAe,CAC7C,CACJ,CAAC;AACJ,CAAC;AAoKD;;;;;;GAMG;AACH,KAAK,wDACH,OAAe,EACf,QAAmB;IAEnB,OAAO,MAAM,wBAAwB,CAAC,KAAK,IAAI,EAAE;QAC/C,MAAM,CAAC,QAAQ,EAAE,mBAAmB,CAAC,CAAC;QACtC,OAAO,MAAM,KAAK,CAAC,QAAQ,EAAE,YAAY,EAAE,CAAC,OAAO,CAAC,CAAC,CAAC;IACxD,CAAC,CAAC,CAAC;AACL,CAAC;AAsDH,eAAe,wBAAwB,CAAC","sourcesContent":["import type { BigNumber } from '@ethersproject/bignumber';\nimport { Contract } from '@ethersproject/contracts';\nimport { Web3Provider } from '@ethersproject/providers';\nimport type {\n AccountsControllerSelectedEvmAccountChangeEvent,\n AccountsControllerGetSelectedAccountAction,\n AccountsControllerListAccountsAction,\n AccountsControllerSelectedAccountChangeEvent,\n} from '@metamask/accounts-controller';\nimport type {\n ControllerStateChangeEvent,\n ControllerGetStateAction,\n RestrictedMessenger,\n} from '@metamask/base-controller';\nimport {\n query,\n safelyExecuteWithTimeout,\n toChecksumHexAddress,\n} from '@metamask/controller-utils';\nimport EthQuery from '@metamask/eth-query';\nimport type {\n NetworkClientId,\n NetworkControllerGetNetworkClientByIdAction,\n NetworkControllerGetStateAction,\n} from '@metamask/network-controller';\nimport { StaticIntervalPollingController } from '@metamask/polling-controller';\nimport type { PreferencesControllerGetStateAction } from '@metamask/preferences-controller';\nimport { assert, hasProperty } from '@metamask/utils';\nimport { Mutex } from 'async-mutex';\nimport { cloneDeep, isEqual } from 'lodash';\nimport abiSingleCallBalancesContract from 'single-call-balance-checker-abi';\n\nimport {\n SINGLE_CALL_BALANCES_ADDRESS_BY_CHAINID,\n type AssetsContractController,\n type StakedBalance,\n} from './AssetsContractController';\nimport { reduceInBatchesSerially, TOKEN_PRICES_BATCH_SIZE } from './assetsUtil';\n\n/**\n * The name of the {@link AccountTrackerController}.\n */\nconst controllerName = 'AccountTrackerController';\n\n/**\n * AccountInformation\n *\n * Account information object\n *\n * balance - Hex string of an account balance in wei\n *\n * stakedBalance - Hex string of an account staked balance in wei\n */\nexport type AccountInformation = {\n balance: string;\n stakedBalance?: string;\n};\n\n/**\n * AccountTrackerControllerState\n *\n * Account tracker controller state\n *\n * accountsByChainId - Map of addresses to account information by chain\n */\nexport type AccountTrackerControllerState = {\n accountsByChainId: Record<string, { [address: string]: AccountInformation }>;\n};\n\nconst accountTrackerMetadata = {\n accountsByChainId: {\n persist: true,\n anonymous: false,\n },\n};\n\n/**\n * The action that can be performed to get the state of the {@link AccountTrackerController}.\n */\nexport type AccountTrackerControllerGetStateAction = ControllerGetStateAction<\n typeof controllerName,\n AccountTrackerControllerState\n>;\n\n/**\n * The actions that can be performed using the {@link AccountTrackerController}.\n */\nexport type AccountTrackerControllerActions =\n AccountTrackerControllerGetStateAction;\n\n/**\n * The messenger of the {@link AccountTrackerController} for communication.\n */\nexport type AllowedActions =\n | AccountsControllerListAccountsAction\n | PreferencesControllerGetStateAction\n | AccountsControllerGetSelectedAccountAction\n | NetworkControllerGetStateAction\n | NetworkControllerGetNetworkClientByIdAction;\n\n/**\n * The event that {@link AccountTrackerController} can emit.\n */\nexport type AccountTrackerControllerStateChangeEvent =\n ControllerStateChangeEvent<\n typeof controllerName,\n AccountTrackerControllerState\n >;\n\n/**\n * The events that {@link AccountTrackerController} can emit.\n */\nexport type AccountTrackerControllerEvents =\n AccountTrackerControllerStateChangeEvent;\n\n/**\n * The external events available to the {@link AccountTrackerController}.\n */\nexport type AllowedEvents =\n | AccountsControllerSelectedEvmAccountChangeEvent\n | AccountsControllerSelectedAccountChangeEvent;\n\n/**\n * The messenger of the {@link AccountTrackerController}.\n */\nexport type AccountTrackerControllerMessenger = RestrictedMessenger<\n typeof controllerName,\n AccountTrackerControllerActions | AllowedActions,\n AccountTrackerControllerEvents | AllowedEvents,\n AllowedActions['type'],\n AllowedEvents['type']\n>;\n\n/** The input to start polling for the {@link AccountTrackerController} */\ntype AccountTrackerPollingInput = {\n networkClientIds: NetworkClientId[];\n};\n\n/**\n * Controller that tracks the network balances for all user accounts.\n */\nexport class AccountTrackerController extends StaticIntervalPollingController<AccountTrackerPollingInput>()<\n typeof controllerName,\n AccountTrackerControllerState,\n AccountTrackerControllerMessenger\n> {\n readonly #refreshMutex = new Mutex();\n\n readonly #includeStakedAssets: boolean;\n\n readonly #getStakedBalanceForChain: AssetsContractController['getStakedBalanceForChain'];\n\n /**\n * Creates an AccountTracker instance.\n *\n * @param options - The controller options.\n * @param options.interval - Polling interval used to fetch new account balances.\n * @param options.state - Initial state to set on this controller.\n * @param options.messenger - The controller messaging system.\n * @param options.getStakedBalanceForChain - The function to get the staked native asset balance for a chain.\n * @param options.includeStakedAssets - Whether to include staked assets in the account balances.\n */\n constructor({\n interval = 10000,\n state,\n messenger,\n getStakedBalanceForChain,\n includeStakedAssets = false,\n }: {\n interval?: number;\n state?: Partial<AccountTrackerControllerState>;\n messenger: AccountTrackerControllerMessenger;\n getStakedBalanceForChain: AssetsContractController['getStakedBalanceForChain'];\n includeStakedAssets?: boolean;\n }) {\n const { selectedNetworkClientId } = messenger.call(\n 'NetworkController:getState',\n );\n const {\n configuration: { chainId },\n } = messenger.call(\n 'NetworkController:getNetworkClientById',\n selectedNetworkClientId,\n );\n super({\n name: controllerName,\n messenger,\n state: {\n accountsByChainId: {\n [chainId]: {},\n },\n ...state,\n },\n metadata: accountTrackerMetadata,\n });\n this.#getStakedBalanceForChain = getStakedBalanceForChain;\n\n this.#includeStakedAssets = includeStakedAssets;\n\n this.setIntervalLength(interval);\n\n this.messagingSystem.subscribe(\n 'AccountsController:selectedEvmAccountChange',\n (newAddress, prevAddress) => {\n if (newAddress !== prevAddress) {\n // Making an async call for this new event\n // eslint-disable-next-line @typescript-eslint/no-floating-promises\n this.refresh(this.#getNetworkClientIds());\n }\n },\n (event): string => event.address,\n );\n }\n\n private syncAccounts(newChainIds: string[]) {\n const accountsByChainId = cloneDeep(this.state.accountsByChainId);\n const { selectedNetworkClientId } = this.messagingSystem.call(\n 'NetworkController:getState',\n );\n const {\n configuration: { chainId: currentChainId },\n } = this.messagingSystem.call(\n 'NetworkController:getNetworkClientById',\n selectedNetworkClientId,\n );\n\n const existing = Object.keys(accountsByChainId?.[currentChainId] ?? {});\n\n // Initialize new chain IDs if they don't exist\n newChainIds.forEach((newChainId) => {\n if (!accountsByChainId[newChainId]) {\n accountsByChainId[newChainId] = {};\n existing.forEach((address) => {\n accountsByChainId[newChainId][address] = { balance: '0x0' };\n });\n }\n });\n\n // Note: The address from the preferences controller are checksummed\n // The addresses from the accounts controller are lowercased\n const addresses = Object.values(\n this.messagingSystem\n .call('AccountsController:listAccounts')\n .map((internalAccount) =>\n toChecksumHexAddress(internalAccount.address),\n ),\n );\n const newAddresses = addresses.filter(\n (address) => !existing.includes(address),\n );\n const oldAddresses = existing.filter(\n (address) => !addresses.includes(address),\n );\n Object.keys(accountsByChainId).forEach((chainId) => {\n newAddresses.forEach((address) => {\n accountsByChainId[chainId][address] = {\n balance: '0x0',\n };\n });\n });\n\n Object.keys(accountsByChainId).forEach((chainId) => {\n oldAddresses.forEach((address) => {\n delete accountsByChainId[chainId][address];\n });\n });\n\n if (!isEqual(this.state.accountsByChainId, accountsByChainId)) {\n this.update((state) => {\n state.accountsByChainId = accountsByChainId;\n });\n }\n }\n\n /**\n * Resolves a networkClientId to a network client config\n * or globally selected network config if not provided\n *\n * @param networkClientId - Optional networkClientId to fetch a network client with\n * @returns network client config\n */\n #getCorrectNetworkClient(networkClientId?: NetworkClientId) {\n const selectedNetworkClientId =\n networkClientId ??\n this.messagingSystem.call('NetworkController:getState')\n .selectedNetworkClientId;\n const {\n configuration: { chainId },\n provider,\n blockTracker,\n } = this.messagingSystem.call(\n 'NetworkController:getNetworkClientById',\n selectedNetworkClientId,\n );\n\n return {\n chainId,\n provider,\n ethQuery: new EthQuery(provider),\n blockTracker,\n };\n }\n\n /**\n * Retrieves the list of network client IDs.\n *\n * @returns An array of network client IDs.\n */\n #getNetworkClientIds(): NetworkClientId[] {\n const { networkConfigurationsByChainId } = this.messagingSystem.call(\n 'NetworkController:getState',\n );\n return Object.values(networkConfigurationsByChainId).flatMap(\n (networkConfiguration) =>\n networkConfiguration.rpcEndpoints.map(\n (rpcEndpoint) => rpcEndpoint.networkClientId,\n ),\n );\n }\n\n /**\n * Refreshes the balances of the accounts using the networkClientId\n *\n * @param input - The input for the poll.\n * @param input.networkClientIds - The network client IDs used to get balances.\n */\n async _executePoll({\n networkClientIds,\n }: AccountTrackerPollingInput): Promise<void> {\n // TODO: Either fix this lint violation or explain why it's necessary to ignore.\n // eslint-disable-next-line @typescript-eslint/no-floating-promises\n this.refresh(networkClientIds);\n }\n\n /**\n * Refreshes the balances of the accounts depending on the multi-account setting.\n * If multi-account is disabled, only updates the selected account balance.\n * If multi-account is enabled, updates balances for all accounts.\n *\n * @param networkClientIds - Optional network client IDs to fetch a network client with\n */\n async refresh(networkClientIds: NetworkClientId[]) {\n const selectedAccount = this.messagingSystem.call(\n 'AccountsController:getSelectedAccount',\n );\n const releaseLock = await this.#refreshMutex.acquire();\n try {\n const chainIds = networkClientIds.map((networkClientId) => {\n const { chainId } = this.#getCorrectNetworkClient(networkClientId);\n return chainId;\n });\n\n this.syncAccounts(chainIds);\n\n // Create an array of promises for each networkClientId\n const updatePromises = networkClientIds.map(async (networkClientId) => {\n const { chainId, ethQuery, provider, blockTracker } =\n this.#getCorrectNetworkClient(networkClientId);\n const { accountsByChainId } = this.state;\n const { isMultiAccountBalancesEnabled } = this.messagingSystem.call(\n 'PreferencesController:getState',\n );\n\n const accountsToUpdate = isMultiAccountBalancesEnabled\n ? Object.keys(accountsByChainId[chainId])\n : [toChecksumHexAddress(selectedAccount.address)];\n\n const accountsForChain = { ...accountsByChainId[chainId] };\n\n // Force fresh block data before multicall\n // TODO: This is a temporary fix to ensure that the block number is up to date.\n // We should remove this once we have a better solution for this on the block tracker controller.\n await safelyExecuteWithTimeout(() =>\n blockTracker?.checkForLatestBlock?.(),\n );\n\n const stakedBalancesPromise = this.#includeStakedAssets\n ? this.#getStakedBalanceForChain(accountsToUpdate, networkClientId)\n : Promise.resolve({});\n\n if (hasProperty(SINGLE_CALL_BALANCES_ADDRESS_BY_CHAINID, chainId)) {\n const contractAddress = SINGLE_CALL_BALANCES_ADDRESS_BY_CHAINID[\n chainId\n ] as string;\n\n const contract = new Contract(\n contractAddress,\n abiSingleCallBalancesContract,\n new Web3Provider(provider),\n );\n\n const nativeBalances = await safelyExecuteWithTimeout(\n () =>\n contract.balances(accountsToUpdate, [\n '0x0000000000000000000000000000000000000000',\n ]) as Promise<BigNumber[]>,\n false,\n 3_000, // 3s max call for multicall contract call\n );\n\n if (nativeBalances) {\n accountsToUpdate.forEach((address, index) => {\n accountsForChain[address] = {\n balance: nativeBalances[index].toHexString(),\n };\n });\n }\n } else {\n // Process accounts in batches using reduceInBatchesSerially\n await reduceInBatchesSerially<string, void>({\n values: accountsToUpdate,\n batchSize: TOKEN_PRICES_BATCH_SIZE,\n initialResult: undefined,\n eachBatch: async (workingResult: void, batch: string[]) => {\n const balancePromises = batch.map(async (address: string) => {\n const balanceResult = await this.#getBalanceFromChain(\n address,\n ethQuery,\n ).catch(() => null);\n\n // Update account balances\n if (balanceResult) {\n accountsForChain[address] = {\n balance: balanceResult,\n };\n }\n });\n\n await Promise.allSettled(balancePromises);\n return workingResult;\n },\n });\n }\n\n const stakedBalanceResult = await safelyExecuteWithTimeout(\n async () =>\n (await stakedBalancesPromise) as Record<string, StakedBalance>,\n );\n\n Object.entries(stakedBalanceResult ?? {}).forEach(\n ([address, balance]) => {\n accountsForChain[address] = {\n ...accountsForChain[address],\n stakedBalance: balance,\n };\n },\n );\n\n // After all batches are processed, return the updated data\n return { chainId, accountsForChain };\n });\n\n // Wait for all networkClientId updates to settle in parallel\n const allResults = await Promise.allSettled(updatePromises);\n\n // Build a _copy_ of the current state and track whether anything changed\n const nextAccountsByChainId: AccountTrackerControllerState['accountsByChainId'] =\n cloneDeep(this.state.accountsByChainId);\n let hasChanges = false;\n\n allResults.forEach((result) => {\n if (result.status === 'fulfilled') {\n const { chainId, accountsForChain } = result.value;\n // Only mark as changed if the incoming data differs\n if (!isEqual(nextAccountsByChainId[chainId], accountsForChain)) {\n nextAccountsByChainId[chainId] = accountsForChain;\n hasChanges = true;\n }\n }\n });\n\n // 👇🏻 call `update` only when something is new / different\n if (hasChanges) {\n this.update((state) => {\n state.accountsByChainId = nextAccountsByChainId;\n });\n }\n } finally {\n releaseLock();\n }\n }\n\n /**\n * Fetches the balance of a given address from the blockchain.\n *\n * @param address - The account address to fetch the balance for.\n * @param ethQuery - The EthQuery instance to query getBalnce with.\n * @returns A promise that resolves to the balance in a hex string format.\n */\n async #getBalanceFromChain(\n address: string,\n ethQuery?: EthQuery,\n ): Promise<string | undefined> {\n return await safelyExecuteWithTimeout(async () => {\n assert(ethQuery, 'Provider not set.');\n return await query(ethQuery, 'getBalance', [address]);\n });\n }\n\n /**\n * Sync accounts balances with some additional addresses.\n *\n * @param addresses - the additional addresses, may be hardware wallet addresses.\n * @param networkClientId - Optional networkClientId to fetch a network client with.\n * @returns accounts - addresses with synced balance\n */\n async syncBalanceWithAddresses(\n addresses: string[],\n networkClientId?: NetworkClientId,\n ): Promise<\n Record<string, { balance: string; stakedBalance?: StakedBalance }>\n > {\n const { ethQuery } = this.#getCorrectNetworkClient(networkClientId);\n\n // TODO: This should use multicall when enabled by the user.\n return await Promise.all(\n addresses.map(\n (address): Promise<[string, string, StakedBalance] | undefined> => {\n return safelyExecuteWithTimeout(async () => {\n assert(ethQuery, 'Provider not set.');\n const balance = await query(ethQuery, 'getBalance', [address]);\n\n let stakedBalance: StakedBalance;\n if (this.#includeStakedAssets) {\n stakedBalance = (\n await this.#getStakedBalanceForChain([address], networkClientId)\n )[address];\n }\n return [address, balance, stakedBalance];\n });\n },\n ),\n ).then((value) => {\n return value.reduce((obj, item) => {\n if (!item) {\n return obj;\n }\n\n const [address, balance, stakedBalance] = item;\n return {\n ...obj,\n [address]: {\n balance,\n stakedBalance,\n },\n };\n }, {});\n });\n }\n}\n\nexport default AccountTrackerController;\n"]}
|
|
@@ -18,7 +18,7 @@ const calculate_defi_metrics_1 = require("./calculate-defi-metrics.cjs");
|
|
|
18
18
|
const fetch_positions_1 = require("./fetch-positions.cjs");
|
|
19
19
|
const group_defi_positions_1 = require("./group-defi-positions.cjs");
|
|
20
20
|
const assetsUtil_1 = require("../assetsUtil.cjs");
|
|
21
|
-
const TEN_MINUTES_IN_MS =
|
|
21
|
+
const TEN_MINUTES_IN_MS = 600000;
|
|
22
22
|
const FETCH_POSITIONS_BATCH_SIZE = 10;
|
|
23
23
|
const controllerName = 'DeFiPositionsController';
|
|
24
24
|
const controllerMetadata = {
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"DeFiPositionsController.cjs","sourceRoot":"","sources":["../../src/DeFiPositionsController/DeFiPositionsController.ts"],"names":[],"mappings":"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","sourcesContent":["import type {\n AccountsControllerAccountAddedEvent,\n AccountsControllerListAccountsAction,\n} from '@metamask/accounts-controller';\nimport type {\n ControllerGetStateAction,\n ControllerStateChangeEvent,\n RestrictedMessenger,\n StateMetadata,\n} from '@metamask/base-controller';\nimport type { KeyringControllerUnlockEvent } from '@metamask/keyring-controller';\nimport type { KeyringControllerLockEvent } from '@metamask/keyring-controller';\nimport { StaticIntervalPollingController } from '@metamask/polling-controller';\nimport type { TransactionControllerTransactionConfirmedEvent } from '@metamask/transaction-controller';\nimport type { Hex } from '@metamask/utils';\n\nimport { calculateDeFiPositionMetrics } from './calculate-defi-metrics';\nimport type { DefiPositionResponse } from './fetch-positions';\nimport { buildPositionFetcher } from './fetch-positions';\nimport {\n groupDeFiPositions,\n type GroupedDeFiPositions,\n} from './group-defi-positions';\nimport { reduceInBatchesSerially } from '../assetsUtil';\n\nconst TEN_MINUTES_IN_MS = 60_000;\n\nconst FETCH_POSITIONS_BATCH_SIZE = 10;\n\nconst controllerName = 'DeFiPositionsController';\n\nexport type GroupedDeFiPositionsPerChain = {\n [chain: Hex]: GroupedDeFiPositions;\n};\n\nexport type TrackingEventPayload = {\n event: string;\n category: string;\n properties: {\n totalPositions: number;\n totalMarketValueUSD: number;\n breakdown?: {\n protocolId: string;\n marketValueUSD: number;\n chainId: Hex;\n count: number;\n }[];\n };\n};\n\ntype TrackEventHook = (event: TrackingEventPayload) => void;\n\nexport type DeFiPositionsControllerState = {\n /**\n * Object containing DeFi positions per account and network\n */\n allDeFiPositions: {\n [accountAddress: string]: GroupedDeFiPositionsPerChain | null;\n };\n\n /**\n * Object containing DeFi positions count per account\n */\n allDeFiPositionsCount: {\n [accountAddress: string]: number;\n };\n};\n\nconst controllerMetadata: StateMetadata<DeFiPositionsControllerState> = {\n allDeFiPositions: {\n persist: false,\n anonymous: false,\n },\n allDeFiPositionsCount: {\n persist: false,\n anonymous: false,\n },\n};\n\nexport const getDefaultDefiPositionsControllerState =\n (): DeFiPositionsControllerState => {\n return {\n allDeFiPositions: {},\n allDeFiPositionsCount: {},\n };\n };\n\nexport type DeFiPositionsControllerActions =\n DeFiPositionsControllerGetStateAction;\n\nexport type DeFiPositionsControllerGetStateAction = ControllerGetStateAction<\n typeof controllerName,\n DeFiPositionsControllerState\n>;\n\nexport type DeFiPositionsControllerEvents =\n DeFiPositionsControllerStateChangeEvent;\n\nexport type DeFiPositionsControllerStateChangeEvent =\n ControllerStateChangeEvent<\n typeof controllerName,\n DeFiPositionsControllerState\n >;\n\n/**\n * The external actions available to the {@link DeFiPositionsController}.\n */\nexport type AllowedActions = AccountsControllerListAccountsAction;\n\n/**\n * The external events available to the {@link DeFiPositionsController}.\n */\nexport type AllowedEvents =\n | KeyringControllerUnlockEvent\n | KeyringControllerLockEvent\n | TransactionControllerTransactionConfirmedEvent\n | AccountsControllerAccountAddedEvent;\n\n/**\n * The messenger of the {@link DeFiPositionsController}.\n */\nexport type DeFiPositionsControllerMessenger = RestrictedMessenger<\n typeof controllerName,\n DeFiPositionsControllerActions | AllowedActions,\n DeFiPositionsControllerEvents | AllowedEvents,\n AllowedActions['type'],\n AllowedEvents['type']\n>;\n\n/**\n * Controller that stores assets and exposes convenience methods\n */\nexport class DeFiPositionsController extends StaticIntervalPollingController()<\n typeof controllerName,\n DeFiPositionsControllerState,\n DeFiPositionsControllerMessenger\n> {\n readonly #fetchPositions: (\n accountAddress: string,\n ) => Promise<DefiPositionResponse[]>;\n\n readonly #isEnabled: () => boolean;\n\n readonly #trackEvent?: TrackEventHook;\n\n /**\n * DeFiPositionsController constuctor\n *\n * @param options - Constructor options.\n * @param options.messenger - The controller messenger.\n * @param options.isEnabled - Function that returns whether the controller is enabled. (default: () => true)\n * @param options.trackEvent - Function to track events. (default: undefined)\n */\n constructor({\n messenger,\n isEnabled = () => true,\n trackEvent,\n }: {\n messenger: DeFiPositionsControllerMessenger;\n isEnabled?: () => boolean;\n trackEvent?: TrackEventHook;\n }) {\n super({\n name: controllerName,\n metadata: controllerMetadata,\n messenger,\n state: getDefaultDefiPositionsControllerState(),\n });\n\n this.setIntervalLength(TEN_MINUTES_IN_MS);\n\n this.#fetchPositions = buildPositionFetcher();\n this.#isEnabled = isEnabled;\n\n this.messagingSystem.subscribe('KeyringController:unlock', () => {\n this.startPolling(null);\n });\n\n this.messagingSystem.subscribe('KeyringController:lock', () => {\n this.stopAllPolling();\n });\n\n this.messagingSystem.subscribe(\n 'TransactionController:transactionConfirmed',\n async (transactionMeta) => {\n if (!this.#isEnabled()) {\n return;\n }\n\n await this.#updateAccountPositions(transactionMeta.txParams.from);\n },\n );\n\n this.messagingSystem.subscribe(\n 'AccountsController:accountAdded',\n async (account) => {\n if (!this.#isEnabled() || !account.type.startsWith('eip155:')) {\n return;\n }\n\n await this.#updateAccountPositions(account.address);\n },\n );\n\n this.#trackEvent = trackEvent;\n }\n\n async _executePoll(): Promise<void> {\n if (!this.#isEnabled()) {\n return;\n }\n\n const accounts = this.messagingSystem.call(\n 'AccountsController:listAccounts',\n );\n\n const initialResult: {\n accountAddress: string;\n positions: GroupedDeFiPositionsPerChain | null;\n }[] = [];\n\n const results = await reduceInBatchesSerially({\n initialResult,\n values: accounts,\n batchSize: FETCH_POSITIONS_BATCH_SIZE,\n eachBatch: async (workingResult, batch) => {\n const batchResults = (\n await Promise.all(\n batch.map(async ({ address: accountAddress, type }) => {\n if (type.startsWith('eip155:')) {\n const positions =\n await this.#fetchAccountPositions(accountAddress);\n\n return {\n accountAddress,\n positions,\n };\n }\n\n return undefined;\n }),\n )\n ).filter(Boolean) as {\n accountAddress: string;\n positions: GroupedDeFiPositionsPerChain | null;\n }[];\n\n return [...workingResult, ...batchResults];\n },\n });\n\n const allDefiPositions = results.reduce(\n (acc, { accountAddress, positions }) => {\n acc[accountAddress] = positions;\n return acc;\n },\n {} as DeFiPositionsControllerState['allDeFiPositions'],\n );\n\n this.update((state) => {\n state.allDeFiPositions = allDefiPositions;\n });\n }\n\n async #updateAccountPositions(accountAddress: string): Promise<void> {\n const accountPositionsPerChain =\n await this.#fetchAccountPositions(accountAddress);\n\n this.update((state) => {\n state.allDeFiPositions[accountAddress] = accountPositionsPerChain;\n });\n }\n\n async #fetchAccountPositions(\n accountAddress: string,\n ): Promise<GroupedDeFiPositionsPerChain | null> {\n try {\n const defiPositionsResponse = await this.#fetchPositions(accountAddress);\n\n const groupedDeFiPositions = groupDeFiPositions(defiPositionsResponse);\n\n try {\n this.#updatePositionsCountMetrics(groupedDeFiPositions, accountAddress);\n } catch (error) {\n console.error(\n `Failed to update positions count for account ${accountAddress}:`,\n error,\n );\n }\n\n return groupedDeFiPositions;\n } catch {\n return null;\n }\n }\n\n #updatePositionsCountMetrics(\n groupedDeFiPositions: GroupedDeFiPositionsPerChain,\n accountAddress: string,\n ) {\n // If no track event passed then skip the metrics update\n if (!this.#trackEvent) {\n return;\n }\n\n const defiMetrics = calculateDeFiPositionMetrics(groupedDeFiPositions);\n const { totalPositions } = defiMetrics.properties;\n\n if (totalPositions !== this.state.allDeFiPositionsCount[accountAddress]) {\n this.update((state) => {\n state.allDeFiPositionsCount[accountAddress] = totalPositions;\n });\n\n this.#trackEvent?.(defiMetrics);\n }\n }\n}\n"]}
|
|
1
|
+
{"version":3,"file":"DeFiPositionsController.cjs","sourceRoot":"","sources":["../../src/DeFiPositionsController/DeFiPositionsController.ts"],"names":[],"mappings":"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","sourcesContent":["import type {\n AccountsControllerAccountAddedEvent,\n AccountsControllerListAccountsAction,\n} from '@metamask/accounts-controller';\nimport type {\n ControllerGetStateAction,\n ControllerStateChangeEvent,\n RestrictedMessenger,\n StateMetadata,\n} from '@metamask/base-controller';\nimport type { KeyringControllerUnlockEvent } from '@metamask/keyring-controller';\nimport type { KeyringControllerLockEvent } from '@metamask/keyring-controller';\nimport { StaticIntervalPollingController } from '@metamask/polling-controller';\nimport type { TransactionControllerTransactionConfirmedEvent } from '@metamask/transaction-controller';\nimport type { Hex } from '@metamask/utils';\n\nimport { calculateDeFiPositionMetrics } from './calculate-defi-metrics';\nimport type { DefiPositionResponse } from './fetch-positions';\nimport { buildPositionFetcher } from './fetch-positions';\nimport {\n groupDeFiPositions,\n type GroupedDeFiPositions,\n} from './group-defi-positions';\nimport { reduceInBatchesSerially } from '../assetsUtil';\n\nconst TEN_MINUTES_IN_MS = 600_000;\n\nconst FETCH_POSITIONS_BATCH_SIZE = 10;\n\nconst controllerName = 'DeFiPositionsController';\n\nexport type GroupedDeFiPositionsPerChain = {\n [chain: Hex]: GroupedDeFiPositions;\n};\n\nexport type TrackingEventPayload = {\n event: string;\n category: string;\n properties: {\n totalPositions: number;\n totalMarketValueUSD: number;\n breakdown?: {\n protocolId: string;\n marketValueUSD: number;\n chainId: Hex;\n count: number;\n }[];\n };\n};\n\ntype TrackEventHook = (event: TrackingEventPayload) => void;\n\nexport type DeFiPositionsControllerState = {\n /**\n * Object containing DeFi positions per account and network\n */\n allDeFiPositions: {\n [accountAddress: string]: GroupedDeFiPositionsPerChain | null;\n };\n\n /**\n * Object containing DeFi positions count per account\n */\n allDeFiPositionsCount: {\n [accountAddress: string]: number;\n };\n};\n\nconst controllerMetadata: StateMetadata<DeFiPositionsControllerState> = {\n allDeFiPositions: {\n persist: false,\n anonymous: false,\n },\n allDeFiPositionsCount: {\n persist: false,\n anonymous: false,\n },\n};\n\nexport const getDefaultDefiPositionsControllerState =\n (): DeFiPositionsControllerState => {\n return {\n allDeFiPositions: {},\n allDeFiPositionsCount: {},\n };\n };\n\nexport type DeFiPositionsControllerActions =\n DeFiPositionsControllerGetStateAction;\n\nexport type DeFiPositionsControllerGetStateAction = ControllerGetStateAction<\n typeof controllerName,\n DeFiPositionsControllerState\n>;\n\nexport type DeFiPositionsControllerEvents =\n DeFiPositionsControllerStateChangeEvent;\n\nexport type DeFiPositionsControllerStateChangeEvent =\n ControllerStateChangeEvent<\n typeof controllerName,\n DeFiPositionsControllerState\n >;\n\n/**\n * The external actions available to the {@link DeFiPositionsController}.\n */\nexport type AllowedActions = AccountsControllerListAccountsAction;\n\n/**\n * The external events available to the {@link DeFiPositionsController}.\n */\nexport type AllowedEvents =\n | KeyringControllerUnlockEvent\n | KeyringControllerLockEvent\n | TransactionControllerTransactionConfirmedEvent\n | AccountsControllerAccountAddedEvent;\n\n/**\n * The messenger of the {@link DeFiPositionsController}.\n */\nexport type DeFiPositionsControllerMessenger = RestrictedMessenger<\n typeof controllerName,\n DeFiPositionsControllerActions | AllowedActions,\n DeFiPositionsControllerEvents | AllowedEvents,\n AllowedActions['type'],\n AllowedEvents['type']\n>;\n\n/**\n * Controller that stores assets and exposes convenience methods\n */\nexport class DeFiPositionsController extends StaticIntervalPollingController()<\n typeof controllerName,\n DeFiPositionsControllerState,\n DeFiPositionsControllerMessenger\n> {\n readonly #fetchPositions: (\n accountAddress: string,\n ) => Promise<DefiPositionResponse[]>;\n\n readonly #isEnabled: () => boolean;\n\n readonly #trackEvent?: TrackEventHook;\n\n /**\n * DeFiPositionsController constuctor\n *\n * @param options - Constructor options.\n * @param options.messenger - The controller messenger.\n * @param options.isEnabled - Function that returns whether the controller is enabled. (default: () => true)\n * @param options.trackEvent - Function to track events. (default: undefined)\n */\n constructor({\n messenger,\n isEnabled = () => true,\n trackEvent,\n }: {\n messenger: DeFiPositionsControllerMessenger;\n isEnabled?: () => boolean;\n trackEvent?: TrackEventHook;\n }) {\n super({\n name: controllerName,\n metadata: controllerMetadata,\n messenger,\n state: getDefaultDefiPositionsControllerState(),\n });\n\n this.setIntervalLength(TEN_MINUTES_IN_MS);\n\n this.#fetchPositions = buildPositionFetcher();\n this.#isEnabled = isEnabled;\n\n this.messagingSystem.subscribe('KeyringController:unlock', () => {\n this.startPolling(null);\n });\n\n this.messagingSystem.subscribe('KeyringController:lock', () => {\n this.stopAllPolling();\n });\n\n this.messagingSystem.subscribe(\n 'TransactionController:transactionConfirmed',\n async (transactionMeta) => {\n if (!this.#isEnabled()) {\n return;\n }\n\n await this.#updateAccountPositions(transactionMeta.txParams.from);\n },\n );\n\n this.messagingSystem.subscribe(\n 'AccountsController:accountAdded',\n async (account) => {\n if (!this.#isEnabled() || !account.type.startsWith('eip155:')) {\n return;\n }\n\n await this.#updateAccountPositions(account.address);\n },\n );\n\n this.#trackEvent = trackEvent;\n }\n\n async _executePoll(): Promise<void> {\n if (!this.#isEnabled()) {\n return;\n }\n\n const accounts = this.messagingSystem.call(\n 'AccountsController:listAccounts',\n );\n\n const initialResult: {\n accountAddress: string;\n positions: GroupedDeFiPositionsPerChain | null;\n }[] = [];\n\n const results = await reduceInBatchesSerially({\n initialResult,\n values: accounts,\n batchSize: FETCH_POSITIONS_BATCH_SIZE,\n eachBatch: async (workingResult, batch) => {\n const batchResults = (\n await Promise.all(\n batch.map(async ({ address: accountAddress, type }) => {\n if (type.startsWith('eip155:')) {\n const positions =\n await this.#fetchAccountPositions(accountAddress);\n\n return {\n accountAddress,\n positions,\n };\n }\n\n return undefined;\n }),\n )\n ).filter(Boolean) as {\n accountAddress: string;\n positions: GroupedDeFiPositionsPerChain | null;\n }[];\n\n return [...workingResult, ...batchResults];\n },\n });\n\n const allDefiPositions = results.reduce(\n (acc, { accountAddress, positions }) => {\n acc[accountAddress] = positions;\n return acc;\n },\n {} as DeFiPositionsControllerState['allDeFiPositions'],\n );\n\n this.update((state) => {\n state.allDeFiPositions = allDefiPositions;\n });\n }\n\n async #updateAccountPositions(accountAddress: string): Promise<void> {\n const accountPositionsPerChain =\n await this.#fetchAccountPositions(accountAddress);\n\n this.update((state) => {\n state.allDeFiPositions[accountAddress] = accountPositionsPerChain;\n });\n }\n\n async #fetchAccountPositions(\n accountAddress: string,\n ): Promise<GroupedDeFiPositionsPerChain | null> {\n try {\n const defiPositionsResponse = await this.#fetchPositions(accountAddress);\n\n const groupedDeFiPositions = groupDeFiPositions(defiPositionsResponse);\n\n try {\n this.#updatePositionsCountMetrics(groupedDeFiPositions, accountAddress);\n } catch (error) {\n console.error(\n `Failed to update positions count for account ${accountAddress}:`,\n error,\n );\n }\n\n return groupedDeFiPositions;\n } catch {\n return null;\n }\n }\n\n #updatePositionsCountMetrics(\n groupedDeFiPositions: GroupedDeFiPositionsPerChain,\n accountAddress: string,\n ) {\n // If no track event passed then skip the metrics update\n if (!this.#trackEvent) {\n return;\n }\n\n const defiMetrics = calculateDeFiPositionMetrics(groupedDeFiPositions);\n const { totalPositions } = defiMetrics.properties;\n\n if (totalPositions !== this.state.allDeFiPositionsCount[accountAddress]) {\n this.update((state) => {\n state.allDeFiPositionsCount[accountAddress] = totalPositions;\n });\n\n this.#trackEvent?.(defiMetrics);\n }\n }\n}\n"]}
|
|
@@ -15,7 +15,7 @@ import { calculateDeFiPositionMetrics } from "./calculate-defi-metrics.mjs";
|
|
|
15
15
|
import { buildPositionFetcher } from "./fetch-positions.mjs";
|
|
16
16
|
import { groupDeFiPositions } from "./group-defi-positions.mjs";
|
|
17
17
|
import { reduceInBatchesSerially } from "../assetsUtil.mjs";
|
|
18
|
-
const TEN_MINUTES_IN_MS =
|
|
18
|
+
const TEN_MINUTES_IN_MS = 600000;
|
|
19
19
|
const FETCH_POSITIONS_BATCH_SIZE = 10;
|
|
20
20
|
const controllerName = 'DeFiPositionsController';
|
|
21
21
|
const controllerMetadata = {
|