@metamask/assets-controllers 73.1.0 → 73.3.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 +43 -1
- package/dist/AccountTrackerController.cjs +51 -1
- package/dist/AccountTrackerController.cjs.map +1 -1
- package/dist/AccountTrackerController.d.cts +40 -1
- package/dist/AccountTrackerController.d.cts.map +1 -1
- package/dist/AccountTrackerController.d.mts +40 -1
- package/dist/AccountTrackerController.d.mts.map +1 -1
- package/dist/AccountTrackerController.mjs +51 -1
- package/dist/AccountTrackerController.mjs.map +1 -1
- package/dist/TokenBalancesController.cjs +278 -319
- package/dist/TokenBalancesController.cjs.map +1 -1
- package/dist/TokenBalancesController.d.cts +51 -93
- package/dist/TokenBalancesController.d.cts.map +1 -1
- package/dist/TokenBalancesController.d.mts +51 -93
- package/dist/TokenBalancesController.d.mts.map +1 -1
- package/dist/TokenBalancesController.mjs +277 -317
- package/dist/TokenBalancesController.mjs.map +1 -1
- package/dist/assetsUtil.cjs +13 -1
- package/dist/assetsUtil.cjs.map +1 -1
- package/dist/assetsUtil.d.cts +8 -0
- package/dist/assetsUtil.d.cts.map +1 -1
- package/dist/assetsUtil.d.mts +8 -0
- package/dist/assetsUtil.d.mts.map +1 -1
- package/dist/assetsUtil.mjs +12 -1
- package/dist/assetsUtil.mjs.map +1 -1
- package/dist/balances.cjs +447 -0
- package/dist/balances.cjs.map +1 -0
- package/dist/balances.d.cts +88 -0
- package/dist/balances.d.cts.map +1 -0
- package/dist/balances.d.mts +88 -0
- package/dist/balances.d.mts.map +1 -0
- package/dist/balances.mjs +441 -0
- package/dist/balances.mjs.map +1 -0
- package/dist/constants.cjs +13 -1
- package/dist/constants.cjs.map +1 -1
- package/dist/constants.d.cts +1 -0
- package/dist/constants.d.cts.map +1 -1
- package/dist/constants.d.mts +1 -0
- package/dist/constants.d.mts.map +1 -1
- package/dist/constants.mjs +12 -0
- package/dist/constants.mjs.map +1 -1
- package/dist/index.cjs +6 -3
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +6 -4
- package/dist/index.d.cts.map +1 -1
- package/dist/index.d.mts +6 -4
- package/dist/index.d.mts.map +1 -1
- package/dist/index.mjs +2 -1
- package/dist/index.mjs.map +1 -1
- package/dist/multi-chain-accounts-service/api-balance-fetcher.cjs +286 -0
- package/dist/multi-chain-accounts-service/api-balance-fetcher.cjs.map +1 -0
- package/dist/multi-chain-accounts-service/api-balance-fetcher.d.cts +30 -0
- package/dist/multi-chain-accounts-service/api-balance-fetcher.d.cts.map +1 -0
- package/dist/multi-chain-accounts-service/api-balance-fetcher.d.mts +30 -0
- package/dist/multi-chain-accounts-service/api-balance-fetcher.d.mts.map +1 -0
- package/dist/multi-chain-accounts-service/api-balance-fetcher.mjs +286 -0
- package/dist/multi-chain-accounts-service/api-balance-fetcher.mjs.map +1 -0
- package/dist/multi-chain-accounts-service/multi-chain-accounts.cjs +35 -1
- package/dist/multi-chain-accounts-service/multi-chain-accounts.cjs.map +1 -1
- package/dist/multi-chain-accounts-service/multi-chain-accounts.d.cts +16 -0
- package/dist/multi-chain-accounts-service/multi-chain-accounts.d.cts.map +1 -1
- package/dist/multi-chain-accounts-service/multi-chain-accounts.d.mts +16 -0
- package/dist/multi-chain-accounts-service/multi-chain-accounts.d.mts.map +1 -1
- package/dist/multi-chain-accounts-service/multi-chain-accounts.mjs +33 -0
- package/dist/multi-chain-accounts-service/multi-chain-accounts.mjs.map +1 -1
- package/dist/multi-chain-accounts-service/types.cjs.map +1 -1
- package/dist/multi-chain-accounts-service/types.d.cts +8 -0
- package/dist/multi-chain-accounts-service/types.d.cts.map +1 -1
- package/dist/multi-chain-accounts-service/types.d.mts +8 -0
- package/dist/multi-chain-accounts-service/types.d.mts.map +1 -1
- package/dist/multi-chain-accounts-service/types.mjs.map +1 -1
- package/dist/multicall.cjs +457 -1
- package/dist/multicall.cjs.map +1 -1
- package/dist/multicall.d.cts +51 -0
- package/dist/multicall.d.cts.map +1 -1
- package/dist/multicall.d.mts +51 -0
- package/dist/multicall.d.mts.map +1 -1
- package/dist/multicall.mjs +457 -0
- package/dist/multicall.mjs.map +1 -1
- package/dist/rpc-service/rpc-balance-fetcher.cjs +184 -0
- package/dist/rpc-service/rpc-balance-fetcher.cjs.map +1 -0
- package/dist/rpc-service/rpc-balance-fetcher.d.cts +34 -0
- package/dist/rpc-service/rpc-balance-fetcher.d.cts.map +1 -0
- package/dist/rpc-service/rpc-balance-fetcher.d.mts +34 -0
- package/dist/rpc-service/rpc-balance-fetcher.d.mts.map +1 -0
- package/dist/rpc-service/rpc-balance-fetcher.mjs +184 -0
- package/dist/rpc-service/rpc-balance-fetcher.mjs.map +1 -0
- package/package.json +11 -11
- package/dist/selectors/balanceSelectors.cjs +0 -328
- package/dist/selectors/balanceSelectors.cjs.map +0 -1
- package/dist/selectors/balanceSelectors.d.cts +0 -1676
- package/dist/selectors/balanceSelectors.d.cts.map +0 -1
- package/dist/selectors/balanceSelectors.d.mts +0 -1676
- package/dist/selectors/balanceSelectors.d.mts.map +0 -1
- package/dist/selectors/balanceSelectors.mjs +0 -321
- package/dist/selectors/balanceSelectors.mjs.map +0 -1
|
@@ -1,364 +1,323 @@
|
|
|
1
1
|
"use strict";
|
|
2
|
-
var __classPrivateFieldGet = (this && this.__classPrivateFieldGet) || function (receiver, state, kind, f) {
|
|
3
|
-
if (kind === "a" && !f) throw new TypeError("Private accessor was defined without a getter");
|
|
4
|
-
if (typeof state === "function" ? receiver !== state || !f : !state.has(receiver)) throw new TypeError("Cannot read private member from an object whose class did not declare it");
|
|
5
|
-
return kind === "m" ? f : kind === "a" ? f.call(receiver) : f ? f.value : state.get(receiver);
|
|
6
|
-
};
|
|
7
2
|
var __classPrivateFieldSet = (this && this.__classPrivateFieldSet) || function (receiver, state, value, kind, f) {
|
|
8
3
|
if (kind === "m") throw new TypeError("Private method is not writable");
|
|
9
4
|
if (kind === "a" && !f) throw new TypeError("Private accessor was defined without a setter");
|
|
10
5
|
if (typeof state === "function" ? receiver !== state || !f : !state.has(receiver)) throw new TypeError("Cannot write private member to an object whose class did not declare it");
|
|
11
6
|
return (kind === "a" ? f.call(receiver, value) : f ? f.value = value : state.set(receiver, value)), value;
|
|
12
7
|
};
|
|
13
|
-
var
|
|
8
|
+
var __classPrivateFieldGet = (this && this.__classPrivateFieldGet) || function (receiver, state, kind, f) {
|
|
9
|
+
if (kind === "a" && !f) throw new TypeError("Private accessor was defined without a getter");
|
|
10
|
+
if (typeof state === "function" ? receiver !== state || !f : !state.has(receiver)) throw new TypeError("Cannot read private member from an object whose class did not declare it");
|
|
11
|
+
return kind === "m" ? f : kind === "a" ? f.call(receiver) : f ? f.value : state.get(receiver);
|
|
12
|
+
};
|
|
13
|
+
var _TokenBalancesController_instances, _TokenBalancesController_queryAllAccounts, _TokenBalancesController_balanceFetchers, _TokenBalancesController_allTokens, _TokenBalancesController_detectedTokens, _TokenBalancesController_chainIdsWithTokens, _TokenBalancesController_getProvider, _TokenBalancesController_getNetworkClient, _TokenBalancesController_onTokensChanged, _TokenBalancesController_onNetworkChanged, _TokenBalancesController_onAccountRemoved;
|
|
14
14
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
15
|
-
exports.TokenBalancesController =
|
|
16
|
-
const contracts_1 = require("@ethersproject/contracts");
|
|
15
|
+
exports.TokenBalancesController = void 0;
|
|
17
16
|
const providers_1 = require("@ethersproject/providers");
|
|
18
17
|
const controller_utils_1 = require("@metamask/controller-utils");
|
|
19
|
-
const metamask_eth_abis_1 = require("@metamask/metamask-eth-abis");
|
|
20
18
|
const polling_controller_1 = require("@metamask/polling-controller");
|
|
21
19
|
const utils_1 = require("@metamask/utils");
|
|
20
|
+
const immer_1 = require("immer");
|
|
22
21
|
const lodash_1 = require("lodash");
|
|
23
|
-
const
|
|
24
|
-
const
|
|
25
|
-
const
|
|
22
|
+
const AssetsContractController_1 = require("./AssetsContractController.cjs");
|
|
23
|
+
const api_balance_fetcher_1 = require("./multi-chain-accounts-service/api-balance-fetcher.cjs");
|
|
24
|
+
const rpc_balance_fetcher_1 = require("./rpc-service/rpc-balance-fetcher.cjs");
|
|
25
|
+
const CONTROLLER = 'TokenBalancesController';
|
|
26
|
+
const DEFAULT_INTERVAL_MS = 180000; // 3 minutes
|
|
26
27
|
const metadata = {
|
|
27
28
|
tokenBalances: { persist: true, anonymous: false },
|
|
28
29
|
};
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
};
|
|
38
|
-
}
|
|
39
|
-
exports.getDefaultTokenBalancesState = getDefaultTokenBalancesState;
|
|
40
|
-
/**
|
|
41
|
-
* Controller that passively polls on a set interval token balances
|
|
42
|
-
* for tokens stored in the TokensController
|
|
43
|
-
*/
|
|
30
|
+
// endregion
|
|
31
|
+
// ────────────────────────────────────────────────────────────────────────────
|
|
32
|
+
// region: Helper utilities
|
|
33
|
+
const draft = (base, fn) => (0, immer_1.produce)(base, fn);
|
|
34
|
+
const ZERO_ADDRESS = '0x0000000000000000000000000000000000000000';
|
|
35
|
+
// endregion
|
|
36
|
+
// ────────────────────────────────────────────────────────────────────────────
|
|
37
|
+
// region: Main controller
|
|
44
38
|
class TokenBalancesController extends (0, polling_controller_1.StaticIntervalPollingController)() {
|
|
45
|
-
|
|
46
|
-
* Construct a Token Balances Controller.
|
|
47
|
-
*
|
|
48
|
-
* @param options - The controller options.
|
|
49
|
-
* @param options.interval - Polling interval used to fetch new token balances.
|
|
50
|
-
* @param options.state - Initial state to set on this controller.
|
|
51
|
-
* @param options.messenger - The controller restricted messenger.
|
|
52
|
-
*/
|
|
53
|
-
constructor({ interval = DEFAULT_INTERVAL, messenger, state = {}, }) {
|
|
54
|
-
var _a, _b;
|
|
39
|
+
constructor({ messenger, interval = DEFAULT_INTERVAL_MS, state = {}, queryMultipleAccounts = true, useAccountsAPI = false, allowExternalServices = () => true, }) {
|
|
55
40
|
super({
|
|
56
|
-
name:
|
|
57
|
-
metadata,
|
|
41
|
+
name: CONTROLLER,
|
|
58
42
|
messenger,
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
...state,
|
|
62
|
-
},
|
|
43
|
+
metadata,
|
|
44
|
+
state: { tokenBalances: {}, ...state },
|
|
63
45
|
});
|
|
64
46
|
_TokenBalancesController_instances.add(this);
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
_TokenBalancesController_calculateQueryMultipleAccounts.set(this, ({ isMultiAccountBalancesEnabled, useMultiAccountBalanceChecker, }) => {
|
|
76
|
-
return Boolean(
|
|
77
|
-
// Note: These settings have different names on extension vs mobile
|
|
78
|
-
isMultiAccountBalancesEnabled || useMultiAccountBalanceChecker);
|
|
79
|
-
});
|
|
80
|
-
/**
|
|
81
|
-
* Handles the event for preferences state changes.
|
|
82
|
-
* @param preferences - The preferences state.
|
|
83
|
-
*/
|
|
84
|
-
_TokenBalancesController_onPreferencesStateChange.set(this, (preferences) => {
|
|
85
|
-
// Update the user preference for whether to query multiple accounts.
|
|
86
|
-
const queryMultipleAccounts = __classPrivateFieldGet(this, _TokenBalancesController_calculateQueryMultipleAccounts, "f").call(this, preferences);
|
|
87
|
-
// Refresh when flipped off -> on
|
|
88
|
-
const refresh = queryMultipleAccounts && !__classPrivateFieldGet(this, _TokenBalancesController_queryMultipleAccounts, "f");
|
|
89
|
-
__classPrivateFieldSet(this, _TokenBalancesController_queryMultipleAccounts, queryMultipleAccounts, "f");
|
|
90
|
-
if (refresh) {
|
|
91
|
-
this.updateBalances().catch(console.error);
|
|
92
|
-
}
|
|
47
|
+
_TokenBalancesController_queryAllAccounts.set(this, void 0);
|
|
48
|
+
_TokenBalancesController_balanceFetchers.set(this, void 0);
|
|
49
|
+
_TokenBalancesController_allTokens.set(this, {});
|
|
50
|
+
_TokenBalancesController_detectedTokens.set(this, {});
|
|
51
|
+
_TokenBalancesController_getProvider.set(this, (chainId) => {
|
|
52
|
+
const { networkConfigurationsByChainId } = this.messagingSystem.call('NetworkController:getState');
|
|
53
|
+
const cfg = networkConfigurationsByChainId[chainId];
|
|
54
|
+
const { networkClientId } = cfg.rpcEndpoints[cfg.defaultRpcEndpointIndex];
|
|
55
|
+
const client = this.messagingSystem.call('NetworkController:getNetworkClientById', networkClientId);
|
|
56
|
+
return new providers_1.Web3Provider(client.provider);
|
|
93
57
|
});
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
*/
|
|
100
|
-
_TokenBalancesController_onTokensStateChange.set(this, ({ allTokens, allDetectedTokens, }) => {
|
|
101
|
-
// Refresh token balances on chains whose tokens have changed.
|
|
102
|
-
const chainIds = __classPrivateFieldGet(this, _TokenBalancesController_getChainIds, "f").call(this, allTokens, allDetectedTokens);
|
|
103
|
-
const chainIdsToUpdate = chainIds.filter((chainId) => !(0, lodash_1.isEqual)(__classPrivateFieldGet(this, _TokenBalancesController_allTokens, "f")[chainId], allTokens[chainId]) ||
|
|
104
|
-
!(0, lodash_1.isEqual)(__classPrivateFieldGet(this, _TokenBalancesController_allDetectedTokens, "f")[chainId], allDetectedTokens[chainId]));
|
|
105
|
-
__classPrivateFieldSet(this, _TokenBalancesController_allTokens, allTokens, "f");
|
|
106
|
-
__classPrivateFieldSet(this, _TokenBalancesController_allDetectedTokens, allDetectedTokens, "f");
|
|
107
|
-
__classPrivateFieldGet(this, _TokenBalancesController_instances, "m", _TokenBalancesController_handleTokensControllerStateChange).call(this, {
|
|
108
|
-
chainIds: chainIdsToUpdate,
|
|
109
|
-
}).catch(console.error);
|
|
58
|
+
_TokenBalancesController_getNetworkClient.set(this, (chainId) => {
|
|
59
|
+
const { networkConfigurationsByChainId } = this.messagingSystem.call('NetworkController:getState');
|
|
60
|
+
const cfg = networkConfigurationsByChainId[chainId];
|
|
61
|
+
const { networkClientId } = cfg.rpcEndpoints[cfg.defaultRpcEndpointIndex];
|
|
62
|
+
return this.messagingSystem.call('NetworkController:getNetworkClientById', networkClientId);
|
|
110
63
|
});
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
* @param input.chainId - The chain id to poll token balances on.
|
|
142
|
-
*/
|
|
143
|
-
async _executePoll({ chainId }) {
|
|
144
|
-
await this.updateBalancesByChainId({ chainId });
|
|
145
|
-
}
|
|
146
|
-
/**
|
|
147
|
-
* Updates the token balances for the given chain ids.
|
|
148
|
-
* @param input - The input for the update.
|
|
149
|
-
* @param input.chainIds - The chain ids to update token balances for.
|
|
150
|
-
* Or omitted to update all chains that contain tokens.
|
|
151
|
-
*/
|
|
152
|
-
async updateBalances({ chainIds } = {}) {
|
|
153
|
-
chainIds ?? (chainIds = __classPrivateFieldGet(this, _TokenBalancesController_getChainIds, "f").call(this, __classPrivateFieldGet(this, _TokenBalancesController_allTokens, "f"), __classPrivateFieldGet(this, _TokenBalancesController_allDetectedTokens, "f")));
|
|
154
|
-
await Promise.allSettled(chainIds.map((chainId) => this.updateBalancesByChainId({ chainId })));
|
|
155
|
-
}
|
|
156
|
-
/**
|
|
157
|
-
* Returns ERC-20 balances for a single account on a single chain.
|
|
158
|
-
*
|
|
159
|
-
* @param params - The parameters for the balance fetch.
|
|
160
|
-
* @param params.chainId - The chain id to fetch balances on.
|
|
161
|
-
* @param params.accountAddress - The account address to fetch balances for.
|
|
162
|
-
* @param params.tokenAddresses - The token addresses to fetch balances for.
|
|
163
|
-
* @returns A mapping from token address to balance (hex) | null.
|
|
164
|
-
*/
|
|
165
|
-
async getErc20Balances({ chainId, accountAddress, tokenAddresses, }) {
|
|
166
|
-
// Return early if no token addresses provided
|
|
167
|
-
if (tokenAddresses.length === 0) {
|
|
168
|
-
return {};
|
|
169
|
-
}
|
|
170
|
-
const pairs = tokenAddresses.map((tokenAddress) => ({
|
|
171
|
-
accountAddress,
|
|
172
|
-
tokenAddress,
|
|
173
|
-
}));
|
|
174
|
-
const results = await __classPrivateFieldGet(this, _TokenBalancesController_instances, "m", _TokenBalancesController_batchBalanceOf).call(this, { chainId, pairs });
|
|
175
|
-
const balances = {};
|
|
176
|
-
tokenAddresses.forEach((tokenAddress, i) => {
|
|
177
|
-
balances[tokenAddress] = results[i]?.success
|
|
178
|
-
? (0, controller_utils_1.toHex)(results[i].value)
|
|
179
|
-
: null;
|
|
180
|
-
});
|
|
181
|
-
return balances;
|
|
182
|
-
}
|
|
183
|
-
/**
|
|
184
|
-
* Updates token balances for the given chain id.
|
|
185
|
-
* @param input - The input for the update.
|
|
186
|
-
* @param input.chainId - The chain id to update token balances on.
|
|
187
|
-
*/
|
|
188
|
-
async updateBalancesByChainId({ chainId }) {
|
|
189
|
-
const { address: selectedAccountAddress } = this.messagingSystem.call('AccountsController:getSelectedAccount');
|
|
190
|
-
const isSelectedAccount = (accountAddress) => (0, controller_utils_1.toChecksumHexAddress)(accountAddress) ===
|
|
191
|
-
(0, controller_utils_1.toChecksumHexAddress)(selectedAccountAddress);
|
|
192
|
-
const accountTokenPairs = [];
|
|
193
|
-
const addTokens = ([accountAddress, tokens]) => __classPrivateFieldGet(this, _TokenBalancesController_queryMultipleAccounts, "f") || isSelectedAccount(accountAddress)
|
|
194
|
-
? tokens.forEach((t) => accountTokenPairs.push({
|
|
195
|
-
accountAddress: accountAddress,
|
|
196
|
-
tokenAddress: t.address,
|
|
197
|
-
}))
|
|
198
|
-
: undefined;
|
|
199
|
-
// Balances will be updated for both imported and detected tokens
|
|
200
|
-
Object.entries(__classPrivateFieldGet(this, _TokenBalancesController_allTokens, "f")[chainId] ?? {}).forEach(addTokens);
|
|
201
|
-
Object.entries(__classPrivateFieldGet(this, _TokenBalancesController_allDetectedTokens, "f")[chainId] ?? {}).forEach(addTokens);
|
|
202
|
-
let results = [];
|
|
203
|
-
const currentTokenBalances = this.messagingSystem.call('TokenBalancesController:getState');
|
|
204
|
-
if (accountTokenPairs.length > 0) {
|
|
205
|
-
results = await __classPrivateFieldGet(this, _TokenBalancesController_instances, "m", _TokenBalancesController_batchBalanceOf).call(this, {
|
|
206
|
-
chainId,
|
|
207
|
-
pairs: accountTokenPairs,
|
|
64
|
+
_TokenBalancesController_onTokensChanged.set(this, async (state) => {
|
|
65
|
+
const changed = [];
|
|
66
|
+
let hasChanges = false;
|
|
67
|
+
// Get chains that have existing balances
|
|
68
|
+
const chainsWithBalances = new Set();
|
|
69
|
+
for (const address of Object.keys(this.state.tokenBalances)) {
|
|
70
|
+
const addressKey = address;
|
|
71
|
+
for (const chainId of Object.keys(this.state.tokenBalances[addressKey] || {})) {
|
|
72
|
+
chainsWithBalances.add(chainId);
|
|
73
|
+
}
|
|
74
|
+
}
|
|
75
|
+
// Only process chains that are explicitly mentioned in the incoming state change
|
|
76
|
+
const incomingChainIds = new Set([
|
|
77
|
+
...Object.keys(state.allTokens),
|
|
78
|
+
...Object.keys(state.allDetectedTokens),
|
|
79
|
+
]);
|
|
80
|
+
// Only proceed if there are actual changes to chains that have balances or are being added
|
|
81
|
+
const relevantChainIds = Array.from(incomingChainIds).filter((chainId) => {
|
|
82
|
+
const id = chainId;
|
|
83
|
+
const hasTokensNow = (state.allTokens[id] && Object.keys(state.allTokens[id]).length > 0) ||
|
|
84
|
+
(state.allDetectedTokens[id] &&
|
|
85
|
+
Object.keys(state.allDetectedTokens[id]).length > 0);
|
|
86
|
+
const hadTokensBefore = (__classPrivateFieldGet(this, _TokenBalancesController_allTokens, "f")[id] && Object.keys(__classPrivateFieldGet(this, _TokenBalancesController_allTokens, "f")[id]).length > 0) ||
|
|
87
|
+
(__classPrivateFieldGet(this, _TokenBalancesController_detectedTokens, "f")[id] &&
|
|
88
|
+
Object.keys(__classPrivateFieldGet(this, _TokenBalancesController_detectedTokens, "f")[id]).length > 0);
|
|
89
|
+
// Check if there's an actual change in token state
|
|
90
|
+
const hasTokenChange = !(0, lodash_1.isEqual)(state.allTokens[id], __classPrivateFieldGet(this, _TokenBalancesController_allTokens, "f")[id]) ||
|
|
91
|
+
!(0, lodash_1.isEqual)(state.allDetectedTokens[id], __classPrivateFieldGet(this, _TokenBalancesController_detectedTokens, "f")[id]);
|
|
92
|
+
// Process chains that have actual changes OR are new chains getting tokens
|
|
93
|
+
return hasTokenChange || (!hadTokensBefore && hasTokensNow);
|
|
208
94
|
});
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
//
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
95
|
+
if (relevantChainIds.length === 0) {
|
|
96
|
+
// No relevant changes, just update internal state
|
|
97
|
+
__classPrivateFieldSet(this, _TokenBalancesController_allTokens, state.allTokens, "f");
|
|
98
|
+
__classPrivateFieldSet(this, _TokenBalancesController_detectedTokens, state.allDetectedTokens, "f");
|
|
99
|
+
return;
|
|
100
|
+
}
|
|
101
|
+
// Handle both cleanup and updates in a single state update
|
|
102
|
+
this.update((s) => {
|
|
103
|
+
for (const chainId of relevantChainIds) {
|
|
104
|
+
const id = chainId;
|
|
105
|
+
const hasTokensNow = (state.allTokens[id] &&
|
|
106
|
+
Object.keys(state.allTokens[id]).length > 0) ||
|
|
107
|
+
(state.allDetectedTokens[id] &&
|
|
108
|
+
Object.keys(state.allDetectedTokens[id]).length > 0);
|
|
109
|
+
const hadTokensBefore = (__classPrivateFieldGet(this, _TokenBalancesController_allTokens, "f")[id] &&
|
|
110
|
+
Object.keys(__classPrivateFieldGet(this, _TokenBalancesController_allTokens, "f")[id]).length > 0) ||
|
|
111
|
+
(__classPrivateFieldGet(this, _TokenBalancesController_detectedTokens, "f")[id] &&
|
|
112
|
+
Object.keys(__classPrivateFieldGet(this, _TokenBalancesController_detectedTokens, "f")[id]).length > 0);
|
|
113
|
+
if (!(0, lodash_1.isEqual)(state.allTokens[id], __classPrivateFieldGet(this, _TokenBalancesController_allTokens, "f")[id]) ||
|
|
114
|
+
!(0, lodash_1.isEqual)(state.allDetectedTokens[id], __classPrivateFieldGet(this, _TokenBalancesController_detectedTokens, "f")[id])) {
|
|
115
|
+
if (hasTokensNow) {
|
|
116
|
+
// Chain still has tokens - mark for async balance update
|
|
117
|
+
changed.push(id);
|
|
118
|
+
}
|
|
119
|
+
else if (hadTokensBefore) {
|
|
120
|
+
// Chain had tokens before but doesn't now - clean up balances immediately
|
|
121
|
+
for (const address of Object.keys(s.tokenBalances)) {
|
|
122
|
+
const addressKey = address;
|
|
123
|
+
if (s.tokenBalances[addressKey]?.[id]) {
|
|
124
|
+
s.tokenBalances[addressKey][id] = {};
|
|
125
|
+
hasChanges = true;
|
|
126
|
+
}
|
|
127
|
+
}
|
|
128
|
+
}
|
|
129
|
+
}
|
|
236
130
|
}
|
|
131
|
+
});
|
|
132
|
+
__classPrivateFieldSet(this, _TokenBalancesController_allTokens, state.allTokens, "f");
|
|
133
|
+
__classPrivateFieldSet(this, _TokenBalancesController_detectedTokens, state.allDetectedTokens, "f");
|
|
134
|
+
// Only update balances for chains that still have tokens (and only if we haven't already updated state)
|
|
135
|
+
if (changed.length && !hasChanges) {
|
|
136
|
+
this.updateBalances({ chainIds: changed }).catch((error) => {
|
|
137
|
+
console.warn('Error updating balances after token change:', error);
|
|
138
|
+
});
|
|
237
139
|
}
|
|
238
140
|
});
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
}
|
|
249
|
-
exports.TokenBalancesController = TokenBalancesController;
|
|
250
|
-
_TokenBalancesController_queryMultipleAccounts = new WeakMap(), _TokenBalancesController_allTokens = new WeakMap(), _TokenBalancesController_allDetectedTokens = new WeakMap(), _TokenBalancesController_calculateQueryMultipleAccounts = new WeakMap(), _TokenBalancesController_onPreferencesStateChange = new WeakMap(), _TokenBalancesController_onTokensStateChange = new WeakMap(), _TokenBalancesController_getChainIds = new WeakMap(), _TokenBalancesController_instances = new WeakSet(), _TokenBalancesController_onNetworkStateChange = function _TokenBalancesController_onNetworkStateChange(_, patches) {
|
|
251
|
-
// Remove state for deleted networks
|
|
252
|
-
for (const patch of patches) {
|
|
253
|
-
if (patch.op === 'remove' &&
|
|
254
|
-
patch.path[0] === 'networkConfigurationsByChainId') {
|
|
255
|
-
const removedChainId = patch.path[1];
|
|
256
|
-
this.update((state) => {
|
|
257
|
-
for (const accountAddress of Object.keys(state.tokenBalances)) {
|
|
258
|
-
delete state.tokenBalances[accountAddress][removedChainId];
|
|
141
|
+
_TokenBalancesController_onNetworkChanged.set(this, (state) => {
|
|
142
|
+
// Check if any networks were removed by comparing with previous state
|
|
143
|
+
const currentNetworks = new Set(Object.keys(state.networkConfigurationsByChainId));
|
|
144
|
+
// Get all networks that currently have balances
|
|
145
|
+
const networksWithBalances = new Set();
|
|
146
|
+
for (const address of Object.keys(this.state.tokenBalances)) {
|
|
147
|
+
const addressKey = address;
|
|
148
|
+
for (const network of Object.keys(this.state.tokenBalances[addressKey] || {})) {
|
|
149
|
+
networksWithBalances.add(network);
|
|
259
150
|
}
|
|
151
|
+
}
|
|
152
|
+
// Find networks that were removed
|
|
153
|
+
const removedNetworks = Array.from(networksWithBalances).filter((network) => !currentNetworks.has(network));
|
|
154
|
+
if (removedNetworks.length > 0) {
|
|
155
|
+
this.update((s) => {
|
|
156
|
+
// Remove balances for all accounts on the deleted networks
|
|
157
|
+
for (const address of Object.keys(s.tokenBalances)) {
|
|
158
|
+
const addressKey = address;
|
|
159
|
+
for (const removedNetwork of removedNetworks) {
|
|
160
|
+
const networkKey = removedNetwork;
|
|
161
|
+
if (s.tokenBalances[addressKey]?.[networkKey]) {
|
|
162
|
+
delete s.tokenBalances[addressKey][networkKey];
|
|
163
|
+
}
|
|
164
|
+
}
|
|
165
|
+
}
|
|
166
|
+
});
|
|
167
|
+
}
|
|
168
|
+
});
|
|
169
|
+
_TokenBalancesController_onAccountRemoved.set(this, (addr) => {
|
|
170
|
+
if (!(0, utils_1.isStrictHexString)(addr) || !(0, controller_utils_1.isValidHexAddress)(addr)) {
|
|
171
|
+
return;
|
|
172
|
+
}
|
|
173
|
+
this.update((s) => {
|
|
174
|
+
delete s.tokenBalances[addr];
|
|
260
175
|
});
|
|
261
|
-
}
|
|
176
|
+
});
|
|
177
|
+
__classPrivateFieldSet(this, _TokenBalancesController_queryAllAccounts, queryMultipleAccounts, "f");
|
|
178
|
+
// Strategy order: API first, then RPC fallback
|
|
179
|
+
__classPrivateFieldSet(this, _TokenBalancesController_balanceFetchers, [
|
|
180
|
+
...(useAccountsAPI && allowExternalServices()
|
|
181
|
+
? [new api_balance_fetcher_1.AccountsApiBalanceFetcher('extension', __classPrivateFieldGet(this, _TokenBalancesController_getProvider, "f"))]
|
|
182
|
+
: []),
|
|
183
|
+
new rpc_balance_fetcher_1.RpcBalanceFetcher(__classPrivateFieldGet(this, _TokenBalancesController_getProvider, "f"), __classPrivateFieldGet(this, _TokenBalancesController_getNetworkClient, "f"), () => ({
|
|
184
|
+
allTokens: __classPrivateFieldGet(this, _TokenBalancesController_allTokens, "f"),
|
|
185
|
+
allDetectedTokens: __classPrivateFieldGet(this, _TokenBalancesController_detectedTokens, "f"),
|
|
186
|
+
})),
|
|
187
|
+
], "f");
|
|
188
|
+
this.setIntervalLength(interval);
|
|
189
|
+
// initial token state & subscriptions
|
|
190
|
+
const { allTokens, allDetectedTokens } = this.messagingSystem.call('TokensController:getState');
|
|
191
|
+
__classPrivateFieldSet(this, _TokenBalancesController_allTokens, allTokens, "f");
|
|
192
|
+
__classPrivateFieldSet(this, _TokenBalancesController_detectedTokens, allDetectedTokens, "f");
|
|
193
|
+
this.messagingSystem.subscribe('TokensController:stateChange', __classPrivateFieldGet(this, _TokenBalancesController_onTokensChanged, "f"));
|
|
194
|
+
this.messagingSystem.subscribe('NetworkController:stateChange', __classPrivateFieldGet(this, _TokenBalancesController_onNetworkChanged, "f"));
|
|
195
|
+
this.messagingSystem.subscribe('KeyringController:accountRemoved', __classPrivateFieldGet(this, _TokenBalancesController_onAccountRemoved, "f"));
|
|
262
196
|
}
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
(0, controller_utils_1.isValidHexAddress)(accountAddress);
|
|
266
|
-
if (!isEthAddress) {
|
|
267
|
-
return;
|
|
197
|
+
async _executePoll({ chainIds }) {
|
|
198
|
+
await this.updateBalances({ chainIds });
|
|
268
199
|
}
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
const
|
|
280
|
-
|
|
281
|
-
if (
|
|
200
|
+
async updateBalances({ chainIds } = {}) {
|
|
201
|
+
const targetChains = chainIds ?? __classPrivateFieldGet(this, _TokenBalancesController_instances, "m", _TokenBalancesController_chainIdsWithTokens).call(this);
|
|
202
|
+
if (!targetChains.length) {
|
|
203
|
+
return;
|
|
204
|
+
}
|
|
205
|
+
const { address: selected } = this.messagingSystem.call('AccountsController:getSelectedAccount');
|
|
206
|
+
const allAccounts = this.messagingSystem.call('AccountsController:listAccounts');
|
|
207
|
+
const aggregated = [];
|
|
208
|
+
let remainingChains = [...targetChains];
|
|
209
|
+
// Try each fetcher in order, removing successfully processed chains
|
|
210
|
+
for (const fetcher of __classPrivateFieldGet(this, _TokenBalancesController_balanceFetchers, "f")) {
|
|
211
|
+
const supportedChains = remainingChains.filter((c) => fetcher.supports(c));
|
|
212
|
+
if (!supportedChains.length) {
|
|
282
213
|
continue;
|
|
283
214
|
}
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
delete state.tokenBalances[currentAccount][currentChain][singleToken];
|
|
215
|
+
try {
|
|
216
|
+
const balances = await (0, controller_utils_1.safelyExecuteWithTimeout)(async () => {
|
|
217
|
+
return await fetcher.fetch({
|
|
218
|
+
chainIds: supportedChains,
|
|
219
|
+
queryAllAccounts: __classPrivateFieldGet(this, _TokenBalancesController_queryAllAccounts, "f"),
|
|
220
|
+
selectedAccount: selected,
|
|
221
|
+
allAccounts,
|
|
292
222
|
});
|
|
223
|
+
}, false, this.getIntervalLength());
|
|
224
|
+
if (balances && balances.length > 0) {
|
|
225
|
+
aggregated.push(...balances);
|
|
226
|
+
// Remove chains that were successfully processed
|
|
227
|
+
const processedChains = new Set(balances.map((b) => b.chainId));
|
|
228
|
+
remainingChains = remainingChains.filter((chain) => !processedChains.has(chain));
|
|
293
229
|
}
|
|
294
230
|
}
|
|
231
|
+
catch (error) {
|
|
232
|
+
console.warn(`Balance fetcher failed for chains ${supportedChains.join(', ')}: ${String(error)}`);
|
|
233
|
+
// Continue to next fetcher (fallback)
|
|
234
|
+
}
|
|
235
|
+
// If all chains have been processed, break early
|
|
236
|
+
if (remainingChains.length === 0) {
|
|
237
|
+
break;
|
|
238
|
+
}
|
|
295
239
|
}
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
|
|
240
|
+
// Determine which accounts to process
|
|
241
|
+
const accountsToProcess = __classPrivateFieldGet(this, _TokenBalancesController_queryAllAccounts, "f")
|
|
242
|
+
? allAccounts.map((a) => a.address)
|
|
243
|
+
: [selected];
|
|
244
|
+
const prev = this.state;
|
|
245
|
+
const next = draft(prev, (d) => {
|
|
246
|
+
// First, initialize all tokens from allTokens state with balance 0
|
|
247
|
+
// for the accounts and chains we're processing
|
|
248
|
+
for (const chainId of targetChains) {
|
|
249
|
+
for (const account of accountsToProcess) {
|
|
250
|
+
// Initialize tokens from allTokens
|
|
251
|
+
const chainTokens = __classPrivateFieldGet(this, _TokenBalancesController_allTokens, "f")[chainId];
|
|
252
|
+
if (chainTokens?.[account]) {
|
|
253
|
+
Object.values(chainTokens[account]).forEach((token) => {
|
|
254
|
+
var _a, _b;
|
|
255
|
+
const tokenAddress = token.address;
|
|
256
|
+
((_b = ((_a = d.tokenBalances)[account] ?? (_a[account] = {})))[chainId] ?? (_b[chainId] = {}))[tokenAddress] = '0x0';
|
|
257
|
+
});
|
|
258
|
+
}
|
|
259
|
+
// Initialize tokens from allDetectedTokens
|
|
260
|
+
const detectedChainTokens = __classPrivateFieldGet(this, _TokenBalancesController_detectedTokens, "f")[chainId];
|
|
261
|
+
if (detectedChainTokens?.[account]) {
|
|
262
|
+
Object.values(detectedChainTokens[account]).forEach((token) => {
|
|
263
|
+
var _a, _b;
|
|
264
|
+
const tokenAddress = token.address;
|
|
265
|
+
((_b = ((_a = d.tokenBalances)[account] ?? (_a[account] = {})))[chainId] ?? (_b[chainId] = {}))[tokenAddress] = '0x0';
|
|
266
|
+
});
|
|
267
|
+
}
|
|
311
268
|
}
|
|
312
269
|
}
|
|
270
|
+
// Then update with actual fetched balances where available
|
|
271
|
+
aggregated.forEach(({ success, value, account, token, chainId }) => {
|
|
272
|
+
var _a, _b;
|
|
273
|
+
if (success && value !== undefined) {
|
|
274
|
+
((_b = ((_a = d.tokenBalances)[account] ?? (_a[account] = {})))[chainId] ?? (_b[chainId] = {}))[token] =
|
|
275
|
+
(0, controller_utils_1.toHex)(value);
|
|
276
|
+
}
|
|
277
|
+
});
|
|
278
|
+
});
|
|
279
|
+
if (!(0, lodash_1.isEqual)(prev, next)) {
|
|
280
|
+
this.update(() => next);
|
|
281
|
+
const nativeBalances = aggregated.filter((r) => r.success && r.token === ZERO_ADDRESS);
|
|
282
|
+
// Update native token balances in a single batch operation for better performance
|
|
283
|
+
if (nativeBalances.length > 0) {
|
|
284
|
+
const balanceUpdates = nativeBalances.map((balance) => ({
|
|
285
|
+
address: balance.account,
|
|
286
|
+
chainId: balance.chainId,
|
|
287
|
+
balance: balance.value?.toString() ?? '0',
|
|
288
|
+
}));
|
|
289
|
+
this.messagingSystem.call('AccountTrackerController:updateNativeBalances', balanceUpdates);
|
|
290
|
+
}
|
|
291
|
+
// Get staking contract addresses for filtering
|
|
292
|
+
const stakingContractAddresses = Object.values(AssetsContractController_1.STAKING_CONTRACT_ADDRESS_BY_CHAINID).map((addr) => addr.toLowerCase());
|
|
293
|
+
// Filter and update staked balances in a single batch operation for better performance
|
|
294
|
+
const stakedBalances = aggregated.filter((r) => {
|
|
295
|
+
return (r.success &&
|
|
296
|
+
r.token !== ZERO_ADDRESS &&
|
|
297
|
+
stakingContractAddresses.includes(r.token.toLowerCase()));
|
|
298
|
+
});
|
|
299
|
+
if (stakedBalances.length > 0) {
|
|
300
|
+
const stakedBalanceUpdates = stakedBalances.map((balance) => ({
|
|
301
|
+
address: balance.account,
|
|
302
|
+
chainId: balance.chainId,
|
|
303
|
+
stakedBalance: balance.value?.toString() ?? '0',
|
|
304
|
+
}));
|
|
305
|
+
this.messagingSystem.call('AccountTrackerController:updateStakedBalances', stakedBalanceUpdates);
|
|
306
|
+
}
|
|
313
307
|
}
|
|
314
308
|
}
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
}
|
|
318
|
-
}, _TokenBalancesController_getProvider = function _TokenBalancesController_getProvider(chainId) {
|
|
319
|
-
return new providers_1.Web3Provider(__classPrivateFieldGet(this, _TokenBalancesController_instances, "m", _TokenBalancesController_getNetworkClient).call(this, chainId).provider);
|
|
320
|
-
}, _TokenBalancesController_ensureFreshBlockData =
|
|
321
|
-
/**
|
|
322
|
-
* Ensures that the block tracker has the latest block data before performing multicall operations.
|
|
323
|
-
* This is a temporary fix to ensure that the block number is up to date.
|
|
324
|
-
*
|
|
325
|
-
* @param chainId - The chain id to update block data for.
|
|
326
|
-
*/
|
|
327
|
-
async function _TokenBalancesController_ensureFreshBlockData(chainId) {
|
|
328
|
-
// Force fresh block data before multicall
|
|
329
|
-
// TODO: This is a temporary fix to ensure that the block number is up to date.
|
|
330
|
-
// We should remove this once we have a better solution for this on the block tracker controller.
|
|
331
|
-
const networkClient = __classPrivateFieldGet(this, _TokenBalancesController_instances, "m", _TokenBalancesController_getNetworkClient).call(this, chainId);
|
|
332
|
-
await networkClient.blockTracker?.checkForLatestBlock?.();
|
|
333
|
-
}, _TokenBalancesController_batchBalanceOf =
|
|
334
|
-
/**
|
|
335
|
-
* Internal util: run `balanceOf` for an arbitrary set of account/token pairs.
|
|
336
|
-
*
|
|
337
|
-
* @param params - The parameters for the balance fetch.
|
|
338
|
-
* @param params.chainId - The chain id to fetch balances on.
|
|
339
|
-
* @param params.pairs - The account/token pairs to fetch balances for.
|
|
340
|
-
* @returns The balances for the given token addresses.
|
|
341
|
-
*/
|
|
342
|
-
async function _TokenBalancesController_batchBalanceOf({ chainId, pairs, }) {
|
|
343
|
-
if (!pairs.length) {
|
|
344
|
-
return [];
|
|
345
|
-
}
|
|
346
|
-
const provider = __classPrivateFieldGet(this, _TokenBalancesController_instances, "m", _TokenBalancesController_getProvider).call(this, chainId);
|
|
347
|
-
const calls = pairs.map(({ accountAddress, tokenAddress }) => ({
|
|
348
|
-
contract: new contracts_1.Contract(tokenAddress, metamask_eth_abis_1.abiERC20, provider),
|
|
349
|
-
functionSignature: 'balanceOf(address)',
|
|
350
|
-
arguments: [accountAddress],
|
|
351
|
-
}));
|
|
352
|
-
await __classPrivateFieldGet(this, _TokenBalancesController_instances, "m", _TokenBalancesController_ensureFreshBlockData).call(this, chainId);
|
|
353
|
-
return (0, multicall_1.multicallOrFallback)(calls, chainId, provider);
|
|
354
|
-
}, _TokenBalancesController_getNetworkClient = function _TokenBalancesController_getNetworkClient(chainId) {
|
|
355
|
-
const { networkConfigurationsByChainId } = this.messagingSystem.call('NetworkController:getState');
|
|
356
|
-
const networkConfiguration = networkConfigurationsByChainId[chainId];
|
|
357
|
-
if (!networkConfiguration) {
|
|
358
|
-
throw new Error(`TokenBalancesController: No network configuration found for chainId ${chainId}`);
|
|
309
|
+
resetState() {
|
|
310
|
+
this.update(() => ({ tokenBalances: {} }));
|
|
359
311
|
}
|
|
360
|
-
|
|
361
|
-
|
|
312
|
+
}
|
|
313
|
+
exports.TokenBalancesController = TokenBalancesController;
|
|
314
|
+
_TokenBalancesController_queryAllAccounts = new WeakMap(), _TokenBalancesController_balanceFetchers = new WeakMap(), _TokenBalancesController_allTokens = new WeakMap(), _TokenBalancesController_detectedTokens = new WeakMap(), _TokenBalancesController_getProvider = new WeakMap(), _TokenBalancesController_getNetworkClient = new WeakMap(), _TokenBalancesController_onTokensChanged = new WeakMap(), _TokenBalancesController_onNetworkChanged = new WeakMap(), _TokenBalancesController_onAccountRemoved = new WeakMap(), _TokenBalancesController_instances = new WeakSet(), _TokenBalancesController_chainIdsWithTokens = function _TokenBalancesController_chainIdsWithTokens() {
|
|
315
|
+
return [
|
|
316
|
+
...new Set([
|
|
317
|
+
...Object.keys(__classPrivateFieldGet(this, _TokenBalancesController_allTokens, "f")),
|
|
318
|
+
...Object.keys(__classPrivateFieldGet(this, _TokenBalancesController_detectedTokens, "f")),
|
|
319
|
+
]),
|
|
320
|
+
];
|
|
362
321
|
};
|
|
363
322
|
exports.default = TokenBalancesController;
|
|
364
323
|
//# sourceMappingURL=TokenBalancesController.cjs.map
|