@metamask/assets-controllers 33.0.0 → 34.0.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.js +2 -2
- package/dist/AccountTrackerController.mjs +1 -1
- package/dist/TokenBalancesController.js +2 -2
- package/dist/TokenBalancesController.mjs +1 -1
- package/dist/TokenDetectionController.js +2 -2
- package/dist/TokenDetectionController.mjs +1 -1
- package/dist/TokenRatesController.js +2 -2
- package/dist/TokenRatesController.mjs +1 -1
- package/dist/TokensController.js +2 -2
- package/dist/TokensController.mjs +1 -1
- package/dist/{chunk-RJBYLTY5.js → chunk-2NQRWANM.js} +57 -30
- package/dist/chunk-2NQRWANM.js.map +1 -0
- package/dist/{chunk-3K3LE2CZ.mjs → chunk-2TSAUGE7.mjs} +30 -19
- package/dist/chunk-2TSAUGE7.mjs.map +1 -0
- package/dist/{chunk-UVE4XY5Y.mjs → chunk-4JLB5OIJ.mjs} +57 -30
- package/dist/chunk-4JLB5OIJ.mjs.map +1 -0
- package/dist/{chunk-OEEFNXR7.mjs → chunk-6VQJFTNC.mjs} +4 -4
- package/dist/chunk-6VQJFTNC.mjs.map +1 -0
- package/dist/{chunk-VZF43NDM.js → chunk-GHKGU6GK.js} +30 -19
- package/dist/chunk-GHKGU6GK.js.map +1 -0
- package/dist/{chunk-P3O5CVAH.mjs → chunk-GJCTAKK5.mjs} +36 -34
- package/dist/chunk-GJCTAKK5.mjs.map +1 -0
- package/dist/chunk-HVOIBGYN.js +282 -0
- package/dist/chunk-HVOIBGYN.js.map +1 -0
- package/dist/{chunk-NWVMFCSC.js → chunk-NHFZIY2K.js} +4 -4
- package/dist/chunk-NHFZIY2K.js.map +1 -0
- package/dist/{chunk-FGAZXVKS.js → chunk-RPQ737HL.js} +36 -34
- package/dist/chunk-RPQ737HL.js.map +1 -0
- package/dist/chunk-Z7RMCHD4.mjs +282 -0
- package/dist/chunk-Z7RMCHD4.mjs.map +1 -0
- package/dist/index.js +6 -6
- package/dist/index.mjs +5 -5
- package/dist/tsconfig.build.tsbuildinfo +1 -1
- package/dist/types/AccountTrackerController.d.ts +50 -66
- package/dist/types/AccountTrackerController.d.ts.map +1 -1
- package/dist/types/TokenBalancesController.d.ts +2 -2
- package/dist/types/TokenBalancesController.d.ts.map +1 -1
- package/dist/types/TokenDetectionController.d.ts +4 -6
- package/dist/types/TokenDetectionController.d.ts.map +1 -1
- package/dist/types/TokenRatesController.d.ts +3 -3
- package/dist/types/TokenRatesController.d.ts.map +1 -1
- package/dist/types/TokensController.d.ts +4 -6
- package/dist/types/TokensController.d.ts.map +1 -1
- package/dist/types/index.d.ts +2 -1
- package/dist/types/index.d.ts.map +1 -1
- package/package.json +2 -2
- package/dist/chunk-3K3LE2CZ.mjs.map +0 -1
- package/dist/chunk-526TATMH.mjs +0 -254
- package/dist/chunk-526TATMH.mjs.map +0 -1
- package/dist/chunk-AZ6SRJVI.js +0 -254
- package/dist/chunk-AZ6SRJVI.js.map +0 -1
- package/dist/chunk-FGAZXVKS.js.map +0 -1
- package/dist/chunk-NWVMFCSC.js.map +0 -1
- package/dist/chunk-OEEFNXR7.mjs.map +0 -1
- package/dist/chunk-P3O5CVAH.mjs.map +0 -1
- package/dist/chunk-RJBYLTY5.js.map +0 -1
- package/dist/chunk-UVE4XY5Y.mjs.map +0 -1
- package/dist/chunk-VZF43NDM.js.map +0 -1
|
@@ -0,0 +1,282 @@
|
|
|
1
|
+
"use strict";Object.defineProperty(exports, "__esModule", {value: true}); function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
|
|
2
|
+
|
|
3
|
+
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
var _chunkZ4BLTVTBjs = require('./chunk-Z4BLTVTB.js');
|
|
7
|
+
|
|
8
|
+
// src/AccountTrackerController.ts
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
|
|
13
|
+
var _controllerutils = require('@metamask/controller-utils');
|
|
14
|
+
var _ethquery = require('@metamask/eth-query'); var _ethquery2 = _interopRequireDefault(_ethquery);
|
|
15
|
+
var _pollingcontroller = require('@metamask/polling-controller');
|
|
16
|
+
var _utils = require('@metamask/utils');
|
|
17
|
+
var _asyncmutex = require('async-mutex');
|
|
18
|
+
var _lodash = require('lodash');
|
|
19
|
+
var controllerName = "AccountTrackerController";
|
|
20
|
+
var accountTrackerMetadata = {
|
|
21
|
+
accounts: {
|
|
22
|
+
persist: true,
|
|
23
|
+
anonymous: false
|
|
24
|
+
},
|
|
25
|
+
accountsByChainId: {
|
|
26
|
+
persist: true,
|
|
27
|
+
anonymous: false
|
|
28
|
+
}
|
|
29
|
+
};
|
|
30
|
+
var _refreshMutex, _handle, _getCurrentChainId, getCurrentChainId_fn, _getCorrectNetworkClient, getCorrectNetworkClient_fn, _getBalanceFromChain, getBalanceFromChain_fn;
|
|
31
|
+
var AccountTrackerController = class extends _pollingcontroller.StaticIntervalPollingController {
|
|
32
|
+
/**
|
|
33
|
+
* Creates an AccountTracker instance.
|
|
34
|
+
*
|
|
35
|
+
* @param options - The controller options.
|
|
36
|
+
* @param options.interval - Polling interval used to fetch new account balances.
|
|
37
|
+
* @param options.state - Initial state to set on this controller.
|
|
38
|
+
* @param options.messenger - The controller messaging system.
|
|
39
|
+
*/
|
|
40
|
+
constructor({
|
|
41
|
+
interval = 1e4,
|
|
42
|
+
state,
|
|
43
|
+
messenger
|
|
44
|
+
}) {
|
|
45
|
+
const { selectedNetworkClientId } = messenger.call(
|
|
46
|
+
"NetworkController:getState"
|
|
47
|
+
);
|
|
48
|
+
const {
|
|
49
|
+
configuration: { chainId }
|
|
50
|
+
} = messenger.call(
|
|
51
|
+
"NetworkController:getNetworkClientById",
|
|
52
|
+
selectedNetworkClientId
|
|
53
|
+
);
|
|
54
|
+
super({
|
|
55
|
+
name: controllerName,
|
|
56
|
+
messenger,
|
|
57
|
+
state: {
|
|
58
|
+
accounts: {},
|
|
59
|
+
accountsByChainId: {
|
|
60
|
+
[chainId]: {}
|
|
61
|
+
},
|
|
62
|
+
...state
|
|
63
|
+
},
|
|
64
|
+
metadata: accountTrackerMetadata
|
|
65
|
+
});
|
|
66
|
+
/**
|
|
67
|
+
* Gets the current chain ID.
|
|
68
|
+
* @returns The current chain ID.
|
|
69
|
+
*/
|
|
70
|
+
_chunkZ4BLTVTBjs.__privateAdd.call(void 0, this, _getCurrentChainId);
|
|
71
|
+
/**
|
|
72
|
+
* Resolves a networkClientId to a network client config
|
|
73
|
+
* or globally selected network config if not provided
|
|
74
|
+
*
|
|
75
|
+
* @param networkClientId - Optional networkClientId to fetch a network client with
|
|
76
|
+
* @returns network client config
|
|
77
|
+
*/
|
|
78
|
+
_chunkZ4BLTVTBjs.__privateAdd.call(void 0, this, _getCorrectNetworkClient);
|
|
79
|
+
/**
|
|
80
|
+
* Fetches the balance of a given address from the blockchain.
|
|
81
|
+
*
|
|
82
|
+
* @param address - The account address to fetch the balance for.
|
|
83
|
+
* @param ethQuery - The EthQuery instance to query getBalnce with.
|
|
84
|
+
* @returns A promise that resolves to the balance in a hex string format.
|
|
85
|
+
*/
|
|
86
|
+
_chunkZ4BLTVTBjs.__privateAdd.call(void 0, this, _getBalanceFromChain);
|
|
87
|
+
_chunkZ4BLTVTBjs.__privateAdd.call(void 0, this, _refreshMutex, new (0, _asyncmutex.Mutex)());
|
|
88
|
+
_chunkZ4BLTVTBjs.__privateAdd.call(void 0, this, _handle, void 0);
|
|
89
|
+
this.setIntervalLength(interval);
|
|
90
|
+
this.poll();
|
|
91
|
+
this.messagingSystem.subscribe(
|
|
92
|
+
"AccountsController:selectedEvmAccountChange",
|
|
93
|
+
// TODO: Either fix this lint violation or explain why it's necessary to ignore.
|
|
94
|
+
// eslint-disable-next-line @typescript-eslint/no-misused-promises
|
|
95
|
+
() => this.refresh()
|
|
96
|
+
);
|
|
97
|
+
}
|
|
98
|
+
syncAccounts(newChainId) {
|
|
99
|
+
const accounts = { ...this.state.accounts };
|
|
100
|
+
const accountsByChainId = _lodash.cloneDeep.call(void 0, this.state.accountsByChainId);
|
|
101
|
+
const existing = Object.keys(accounts);
|
|
102
|
+
if (!accountsByChainId[newChainId]) {
|
|
103
|
+
accountsByChainId[newChainId] = {};
|
|
104
|
+
existing.forEach((address) => {
|
|
105
|
+
accountsByChainId[newChainId][address] = { balance: "0x0" };
|
|
106
|
+
});
|
|
107
|
+
}
|
|
108
|
+
const addresses = Object.values(
|
|
109
|
+
this.messagingSystem.call("AccountsController:listAccounts").map(
|
|
110
|
+
(internalAccount) => _controllerutils.toChecksumHexAddress.call(void 0, internalAccount.address)
|
|
111
|
+
)
|
|
112
|
+
);
|
|
113
|
+
const newAddresses = addresses.filter(
|
|
114
|
+
(address) => !existing.includes(address)
|
|
115
|
+
);
|
|
116
|
+
const oldAddresses = existing.filter(
|
|
117
|
+
(address) => !addresses.includes(address)
|
|
118
|
+
);
|
|
119
|
+
newAddresses.forEach((address) => {
|
|
120
|
+
accounts[address] = { balance: "0x0" };
|
|
121
|
+
});
|
|
122
|
+
Object.keys(accountsByChainId).forEach((chainId) => {
|
|
123
|
+
newAddresses.forEach((address) => {
|
|
124
|
+
accountsByChainId[chainId][address] = {
|
|
125
|
+
balance: "0x0"
|
|
126
|
+
};
|
|
127
|
+
});
|
|
128
|
+
});
|
|
129
|
+
oldAddresses.forEach((address) => {
|
|
130
|
+
delete accounts[address];
|
|
131
|
+
});
|
|
132
|
+
Object.keys(accountsByChainId).forEach((chainId) => {
|
|
133
|
+
oldAddresses.forEach((address) => {
|
|
134
|
+
delete accountsByChainId[chainId][address];
|
|
135
|
+
});
|
|
136
|
+
});
|
|
137
|
+
this.update((state) => {
|
|
138
|
+
state.accounts = accounts;
|
|
139
|
+
state.accountsByChainId = accountsByChainId;
|
|
140
|
+
});
|
|
141
|
+
}
|
|
142
|
+
/**
|
|
143
|
+
* Starts a new polling interval.
|
|
144
|
+
*
|
|
145
|
+
* @param interval - Polling interval trigger a 'refresh'.
|
|
146
|
+
*/
|
|
147
|
+
async poll(interval) {
|
|
148
|
+
if (interval) {
|
|
149
|
+
this.setIntervalLength(interval);
|
|
150
|
+
}
|
|
151
|
+
if (_chunkZ4BLTVTBjs.__privateGet.call(void 0, this, _handle)) {
|
|
152
|
+
clearTimeout(_chunkZ4BLTVTBjs.__privateGet.call(void 0, this, _handle));
|
|
153
|
+
}
|
|
154
|
+
await this.refresh();
|
|
155
|
+
_chunkZ4BLTVTBjs.__privateSet.call(void 0, this, _handle, setTimeout(() => {
|
|
156
|
+
this.poll(this.getIntervalLength());
|
|
157
|
+
}, this.getIntervalLength()));
|
|
158
|
+
}
|
|
159
|
+
/**
|
|
160
|
+
* Refreshes the balances of the accounts using the networkClientId
|
|
161
|
+
*
|
|
162
|
+
* @param networkClientId - The network client ID used to get balances.
|
|
163
|
+
*/
|
|
164
|
+
async _executePoll(networkClientId) {
|
|
165
|
+
this.refresh(networkClientId);
|
|
166
|
+
}
|
|
167
|
+
/**
|
|
168
|
+
* Refreshes the balances of the accounts depending on the multi-account setting.
|
|
169
|
+
* If multi-account is disabled, only updates the selected account balance.
|
|
170
|
+
* If multi-account is enabled, updates balances for all accounts.
|
|
171
|
+
*
|
|
172
|
+
* @param networkClientId - Optional networkClientId to fetch a network client with
|
|
173
|
+
*/
|
|
174
|
+
async refresh(networkClientId) {
|
|
175
|
+
const selectedAccount = this.messagingSystem.call(
|
|
176
|
+
"AccountsController:getSelectedAccount"
|
|
177
|
+
);
|
|
178
|
+
const releaseLock = await _chunkZ4BLTVTBjs.__privateGet.call(void 0, this, _refreshMutex).acquire();
|
|
179
|
+
try {
|
|
180
|
+
const { chainId, ethQuery } = _chunkZ4BLTVTBjs.__privateMethod.call(void 0, this, _getCorrectNetworkClient, getCorrectNetworkClient_fn).call(this, networkClientId);
|
|
181
|
+
this.syncAccounts(chainId);
|
|
182
|
+
const { accounts, accountsByChainId } = this.state;
|
|
183
|
+
const { isMultiAccountBalancesEnabled } = this.messagingSystem.call(
|
|
184
|
+
"PreferencesController:getState"
|
|
185
|
+
);
|
|
186
|
+
const accountsToUpdate = isMultiAccountBalancesEnabled ? Object.keys(accounts) : [_controllerutils.toChecksumHexAddress.call(void 0, selectedAccount.address)];
|
|
187
|
+
const accountsForChain = { ...accountsByChainId[chainId] };
|
|
188
|
+
for (const address of accountsToUpdate) {
|
|
189
|
+
const balance = await _chunkZ4BLTVTBjs.__privateMethod.call(void 0, this, _getBalanceFromChain, getBalanceFromChain_fn).call(this, address, ethQuery);
|
|
190
|
+
if (balance) {
|
|
191
|
+
accountsForChain[address] = {
|
|
192
|
+
balance
|
|
193
|
+
};
|
|
194
|
+
}
|
|
195
|
+
}
|
|
196
|
+
this.update((state) => {
|
|
197
|
+
if (chainId === _chunkZ4BLTVTBjs.__privateMethod.call(void 0, this, _getCurrentChainId, getCurrentChainId_fn).call(this)) {
|
|
198
|
+
state.accounts = accountsForChain;
|
|
199
|
+
}
|
|
200
|
+
state.accountsByChainId[chainId] = accountsForChain;
|
|
201
|
+
});
|
|
202
|
+
} finally {
|
|
203
|
+
releaseLock();
|
|
204
|
+
}
|
|
205
|
+
}
|
|
206
|
+
/**
|
|
207
|
+
* Sync accounts balances with some additional addresses.
|
|
208
|
+
*
|
|
209
|
+
* @param addresses - the additional addresses, may be hardware wallet addresses.
|
|
210
|
+
* @param networkClientId - Optional networkClientId to fetch a network client with.
|
|
211
|
+
* @returns accounts - addresses with synced balance
|
|
212
|
+
*/
|
|
213
|
+
async syncBalanceWithAddresses(addresses, networkClientId) {
|
|
214
|
+
const { ethQuery } = _chunkZ4BLTVTBjs.__privateMethod.call(void 0, this, _getCorrectNetworkClient, getCorrectNetworkClient_fn).call(this, networkClientId);
|
|
215
|
+
return await Promise.all(
|
|
216
|
+
addresses.map((address) => {
|
|
217
|
+
return _controllerutils.safelyExecuteWithTimeout.call(void 0, async () => {
|
|
218
|
+
_utils.assert.call(void 0, ethQuery, "Provider not set.");
|
|
219
|
+
const balance = await _controllerutils.query.call(void 0, ethQuery, "getBalance", [address]);
|
|
220
|
+
return [address, balance];
|
|
221
|
+
});
|
|
222
|
+
})
|
|
223
|
+
).then((value) => {
|
|
224
|
+
return value.reduce((obj, item) => {
|
|
225
|
+
if (!item) {
|
|
226
|
+
return obj;
|
|
227
|
+
}
|
|
228
|
+
const [address, balance] = item;
|
|
229
|
+
return {
|
|
230
|
+
...obj,
|
|
231
|
+
[address]: {
|
|
232
|
+
balance
|
|
233
|
+
}
|
|
234
|
+
};
|
|
235
|
+
}, {});
|
|
236
|
+
});
|
|
237
|
+
}
|
|
238
|
+
};
|
|
239
|
+
_refreshMutex = new WeakMap();
|
|
240
|
+
_handle = new WeakMap();
|
|
241
|
+
_getCurrentChainId = new WeakSet();
|
|
242
|
+
getCurrentChainId_fn = function() {
|
|
243
|
+
const { selectedNetworkClientId } = this.messagingSystem.call(
|
|
244
|
+
"NetworkController:getState"
|
|
245
|
+
);
|
|
246
|
+
const {
|
|
247
|
+
configuration: { chainId }
|
|
248
|
+
} = this.messagingSystem.call(
|
|
249
|
+
"NetworkController:getNetworkClientById",
|
|
250
|
+
selectedNetworkClientId
|
|
251
|
+
);
|
|
252
|
+
return chainId;
|
|
253
|
+
};
|
|
254
|
+
_getCorrectNetworkClient = new WeakSet();
|
|
255
|
+
getCorrectNetworkClient_fn = function(networkClientId) {
|
|
256
|
+
const selectedNetworkClientId = networkClientId ?? this.messagingSystem.call("NetworkController:getState").selectedNetworkClientId;
|
|
257
|
+
const {
|
|
258
|
+
configuration: { chainId },
|
|
259
|
+
provider
|
|
260
|
+
} = this.messagingSystem.call(
|
|
261
|
+
"NetworkController:getNetworkClientById",
|
|
262
|
+
selectedNetworkClientId
|
|
263
|
+
);
|
|
264
|
+
return {
|
|
265
|
+
chainId,
|
|
266
|
+
ethQuery: new (0, _ethquery2.default)(provider)
|
|
267
|
+
};
|
|
268
|
+
};
|
|
269
|
+
_getBalanceFromChain = new WeakSet();
|
|
270
|
+
getBalanceFromChain_fn = async function(address, ethQuery) {
|
|
271
|
+
return await _controllerutils.safelyExecuteWithTimeout.call(void 0, async () => {
|
|
272
|
+
_utils.assert.call(void 0, ethQuery, "Provider not set.");
|
|
273
|
+
return await _controllerutils.query.call(void 0, ethQuery, "getBalance", [address]);
|
|
274
|
+
});
|
|
275
|
+
};
|
|
276
|
+
var AccountTrackerController_default = AccountTrackerController;
|
|
277
|
+
|
|
278
|
+
|
|
279
|
+
|
|
280
|
+
|
|
281
|
+
exports.AccountTrackerController = AccountTrackerController; exports.AccountTrackerController_default = AccountTrackerController_default;
|
|
282
|
+
//# sourceMappingURL=chunk-HVOIBGYN.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/AccountTrackerController.ts"],"names":[],"mappings":"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","sourcesContent":["import type {\n AccountsControllerSelectedEvmAccountChangeEvent,\n AccountsControllerGetSelectedAccountAction,\n AccountsControllerListAccountsAction,\n AccountsControllerSelectedAccountChangeEvent,\n} from '@metamask/accounts-controller';\nimport type {\n ControllerStateChangeEvent,\n ControllerGetStateAction,\n RestrictedControllerMessenger,\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 { type Hex, assert } from '@metamask/utils';\nimport { Mutex } from 'async-mutex';\nimport { cloneDeep } from 'lodash';\n\n/**\n * The name of the {@link AccountTrackerController}.\n */\nconst controllerName = 'AccountTrackerController';\n\n/**\n * @type AccountInformation\n *\n * Account information object\n * @property balance - Hex string of an account balancec in wei\n */\nexport type AccountInformation = {\n balance: string;\n};\n\n/**\n * @type AccountTrackerControllerState\n *\n * Account tracker controller state\n * @property accounts - Map of addresses to account information\n */\nexport type AccountTrackerControllerState = {\n accounts: { [address: string]: AccountInformation };\n accountsByChainId: Record<string, { [address: string]: AccountInformation }>;\n};\n\nconst accountTrackerMetadata = {\n accounts: {\n persist: true,\n anonymous: false,\n },\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 = RestrictedControllerMessenger<\n typeof controllerName,\n AccountTrackerControllerActions | AllowedActions,\n AccountTrackerControllerEvents | AllowedEvents,\n AllowedActions['type'],\n AllowedEvents['type']\n>;\n\n/**\n * Controller that tracks the network balances for all user accounts.\n */\nexport class AccountTrackerController extends StaticIntervalPollingController<\n typeof controllerName,\n AccountTrackerControllerState,\n AccountTrackerControllerMessenger\n> {\n readonly #refreshMutex = new Mutex();\n\n #handle?: ReturnType<typeof setTimeout>;\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 */\n constructor({\n interval = 10000,\n state,\n messenger,\n }: {\n interval?: number;\n state?: Partial<AccountTrackerControllerState>;\n messenger: AccountTrackerControllerMessenger;\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 accounts: {},\n accountsByChainId: {\n [chainId]: {},\n },\n ...state,\n },\n metadata: accountTrackerMetadata,\n });\n this.setIntervalLength(interval);\n\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.poll();\n\n this.messagingSystem.subscribe(\n 'AccountsController:selectedEvmAccountChange',\n // TODO: Either fix this lint violation or explain why it's necessary to ignore.\n // eslint-disable-next-line @typescript-eslint/no-misused-promises\n () => this.refresh(),\n );\n }\n\n /**\n * Gets the current chain ID.\n * @returns The current chain ID.\n */\n #getCurrentChainId(): Hex {\n const { selectedNetworkClientId } = this.messagingSystem.call(\n 'NetworkController:getState',\n );\n const {\n configuration: { chainId },\n } = this.messagingSystem.call(\n 'NetworkController:getNetworkClientById',\n selectedNetworkClientId,\n );\n return chainId;\n }\n\n private syncAccounts(newChainId: string) {\n const accounts = { ...this.state.accounts };\n const accountsByChainId = cloneDeep(this.state.accountsByChainId);\n\n const existing = Object.keys(accounts);\n if (!accountsByChainId[newChainId]) {\n accountsByChainId[newChainId] = {};\n existing.forEach((address) => {\n accountsByChainId[newChainId][address] = { balance: '0x0' };\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 newAddresses.forEach((address) => {\n accounts[address] = { balance: '0x0' };\n });\n Object.keys(accountsByChainId).forEach((chainId) => {\n newAddresses.forEach((address) => {\n accountsByChainId[chainId][address] = {\n balance: '0x0',\n };\n });\n });\n\n oldAddresses.forEach((address) => {\n delete accounts[address];\n });\n Object.keys(accountsByChainId).forEach((chainId) => {\n oldAddresses.forEach((address) => {\n delete accountsByChainId[chainId][address];\n });\n });\n\n this.update((state) => {\n state.accounts = accounts;\n state.accountsByChainId = accountsByChainId;\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 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 ethQuery: new EthQuery(provider),\n };\n }\n\n /**\n * Starts a new polling interval.\n *\n * @param interval - Polling interval trigger a 'refresh'.\n */\n async poll(interval?: number): Promise<void> {\n if (interval) {\n this.setIntervalLength(interval);\n }\n\n if (this.#handle) {\n clearTimeout(this.#handle);\n }\n\n await this.refresh();\n\n this.#handle = setTimeout(() => {\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.poll(this.getIntervalLength());\n }, this.getIntervalLength());\n }\n\n /**\n * Refreshes the balances of the accounts using the networkClientId\n *\n * @param networkClientId - The network client ID used to get balances.\n */\n async _executePoll(networkClientId: string): 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(networkClientId);\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 networkClientId - Optional networkClientId to fetch a network client with\n */\n async refresh(networkClientId?: NetworkClientId) {\n const selectedAccount = this.messagingSystem.call(\n 'AccountsController:getSelectedAccount',\n );\n const releaseLock = await this.#refreshMutex.acquire();\n try {\n const { chainId, ethQuery } =\n this.#getCorrectNetworkClient(networkClientId);\n this.syncAccounts(chainId);\n const { accounts, accountsByChainId } = this.state;\n const { isMultiAccountBalancesEnabled } = this.messagingSystem.call(\n 'PreferencesController:getState',\n );\n\n const accountsToUpdate = isMultiAccountBalancesEnabled\n ? Object.keys(accounts)\n : [toChecksumHexAddress(selectedAccount.address)];\n\n const accountsForChain = { ...accountsByChainId[chainId] };\n for (const address of accountsToUpdate) {\n const balance = await this.#getBalanceFromChain(address, ethQuery);\n if (balance) {\n accountsForChain[address] = {\n balance,\n };\n }\n }\n\n this.update((state) => {\n if (chainId === this.#getCurrentChainId()) {\n state.accounts = accountsForChain;\n }\n state.accountsByChainId[chainId] = accountsForChain;\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<Record<string, { balance: string }>> {\n const { ethQuery } = this.#getCorrectNetworkClient(networkClientId);\n\n return await Promise.all(\n addresses.map((address): Promise<[string, string] | undefined> => {\n return safelyExecuteWithTimeout(async () => {\n assert(ethQuery, 'Provider not set.');\n const balance = await query(ethQuery, 'getBalance', [address]);\n return [address, balance];\n });\n }),\n ).then((value) => {\n return value.reduce((obj, item) => {\n if (!item) {\n return obj;\n }\n\n const [address, balance] = item;\n return {\n ...obj,\n [address]: {\n balance,\n },\n };\n }, {});\n });\n }\n}\n\nexport default AccountTrackerController;\n"]}
|
|
@@ -103,14 +103,14 @@ var TokenBalancesController = class extends _basecontroller.BaseController {
|
|
|
103
103
|
if (_chunkZ4BLTVTBjs.__privateGet.call(void 0, this, _disabled)) {
|
|
104
104
|
return;
|
|
105
105
|
}
|
|
106
|
-
const
|
|
107
|
-
"
|
|
106
|
+
const selectedInternalAccount = this.messagingSystem.call(
|
|
107
|
+
"AccountsController:getSelectedAccount"
|
|
108
108
|
);
|
|
109
109
|
const newContractBalances = {};
|
|
110
110
|
for (const token of _chunkZ4BLTVTBjs.__privateGet.call(void 0, this, _tokens)) {
|
|
111
111
|
const { address } = token;
|
|
112
112
|
try {
|
|
113
|
-
const balance = await _chunkZ4BLTVTBjs.__privateGet.call(void 0, this, _getERC20BalanceOf).call(this, address,
|
|
113
|
+
const balance = await _chunkZ4BLTVTBjs.__privateGet.call(void 0, this, _getERC20BalanceOf).call(this, address, selectedInternalAccount.address);
|
|
114
114
|
newContractBalances[address] = _controllerutils.toHex.call(void 0, balance);
|
|
115
115
|
token.hasBalanceError = false;
|
|
116
116
|
} catch (error) {
|
|
@@ -135,4 +135,4 @@ var TokenBalancesController_default = TokenBalancesController;
|
|
|
135
135
|
|
|
136
136
|
|
|
137
137
|
exports.getDefaultTokenBalancesState = getDefaultTokenBalancesState; exports.TokenBalancesController = TokenBalancesController; exports.TokenBalancesController_default = TokenBalancesController_default;
|
|
138
|
-
//# sourceMappingURL=chunk-
|
|
138
|
+
//# sourceMappingURL=chunk-NHFZIY2K.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/TokenBalancesController.ts"],"names":[],"mappings":";;;;;;;AACA;AAAA,EAIE;AAAA,OACK;AACP,SAAS,eAAe,aAAa;AAMrC,IAAM,mBAAmB;AAEzB,IAAM,iBAAiB;AAEvB,IAAM,WAAW;AAAA,EACf,kBAAkB,EAAE,SAAS,MAAM,WAAW,MAAM;AACtD;AAiEO,SAAS,+BAA6D;AAC3E,SAAO;AAAA,IACL,kBAAkB,CAAC;AAAA,EACrB;AACF;AAxFA;AA8FO,IAAM,0BAAN,cAAsC,eAI3C;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAsBA,YAAY;AAAA,IACV,WAAW;AAAA,IACX,SAAS,CAAC;AAAA,IACV,WAAW;AAAA,IACX;AAAA,IACA;AAAA,IACA,QAAQ,CAAC;AAAA,EACX,GAAmC;AACjC,UAAM;AAAA,MACJ,MAAM;AAAA,MACN;AAAA,MACA;AAAA,MACA,OAAO;AAAA,QACL,GAAG,6BAA6B;AAAA,QAChC,GAAG;AAAA,MACL;AAAA,IACF,CAAC;AArCH;AAEA;AAEA;AAEA;AAEA;AA+BE,uBAAK,WAAY;AACjB,uBAAK,WAAY;AACjB,uBAAK,SAAU;AAEf,SAAK,gBAAgB;AAAA,MACnB;AAAA,MACA,CAAC,EAAE,QAAQ,WAAW,eAAe,MAAM;AACzC,2BAAK,SAAU,CAAC,GAAG,WAAW,GAAG,cAAc;AAG/C,aAAK,eAAe;AAAA,MACtB;AAAA,IACF;AAEA,uBAAK,oBAAqB;AAI1B,SAAK,KAAK;AAAA,EACZ;AAAA;AAAA;AAAA;AAAA,EAKA,SAAS;AACP,uBAAK,WAAY;AAAA,EACnB;AAAA;AAAA;AAAA;AAAA,EAKA,UAAU;AACR,uBAAK,WAAY;AAAA,EACnB;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAOA,MAAM,KAAK,UAAkC;AAC3C,QAAI,UAAU;AACZ,yBAAK,WAAY;AAAA,IACnB;AAEA,QAAI,mBAAK,UAAS;AAChB,mBAAa,mBAAK,QAAO;AAAA,IAC3B;AAEA,UAAM,cAAc,MAAM,KAAK,eAAe,CAAC;AAE/C,uBAAK,SAAU,WAAW,MAAM;AAG9B,WAAK,KAAK,mBAAK,UAAS;AAAA,IAC1B,GAAG,mBAAK,UAAS;AAAA,EACnB;AAAA;AAAA;AAAA;AAAA,EAKA,MAAM,iBAAiB;AACrB,QAAI,mBAAK,YAAW;AAClB;AAAA,IACF;AACA,UAAM,0BAA0B,KAAK,gBAAgB;AAAA,MACnD;AAAA,IACF;AAEA,UAAM,sBAAwC,CAAC;AAC/C,eAAW,SAAS,mBAAK,UAAS;AAChC,YAAM,EAAE,QAAQ,IAAI;AACpB,UAAI;AACF,cAAM,UAAU,MAAM,mBAAK,oBAAL,WACpB,SACA,wBAAwB;AAE1B,4BAAoB,OAAO,IAAI,MAAM,OAAO;AAC5C,cAAM,kBAAkB;AAAA,MAC1B,SAAS,OAAO;AACd,4BAAoB,OAAO,IAAI,MAAM,CAAC;AACtC,cAAM,kBAAkB;AAAA,MAC1B;AAAA,IACF;AAEA,SAAK,OAAO,CAAC,UAAU;AACrB,YAAM,mBAAmB;AAAA,IAC3B,CAAC;AAAA,EACH;AACF;AAhIE;AAEA;AAEA;AAEA;AAEA;AA0HF,IAAO,kCAAQ","sourcesContent":["import { type AccountsControllerGetSelectedAccountAction } from '@metamask/accounts-controller';\nimport {\n type RestrictedControllerMessenger,\n type ControllerGetStateAction,\n type ControllerStateChangeEvent,\n BaseController,\n} from '@metamask/base-controller';\nimport { safelyExecute, toHex } from '@metamask/controller-utils';\n\nimport type { AssetsContractController } from './AssetsContractController';\nimport type { Token } from './TokenRatesController';\nimport type { TokensControllerStateChangeEvent } from './TokensController';\n\nconst DEFAULT_INTERVAL = 180000;\n\nconst controllerName = 'TokenBalancesController';\n\nconst metadata = {\n contractBalances: { persist: true, anonymous: false },\n};\n\n/**\n * Token balances controller options\n * @property interval - Polling interval used to fetch new token balances.\n * @property tokens - List of tokens to track balances for.\n * @property disabled - If set to true, all tracked tokens contract balances updates are blocked.\n * @property getERC20BalanceOf - Gets the balance of the given account at the given contract address.\n */\ntype TokenBalancesControllerOptions = {\n interval?: number;\n tokens?: Token[];\n disabled?: boolean;\n getERC20BalanceOf: AssetsContractController['getERC20BalanceOf'];\n messenger: TokenBalancesControllerMessenger;\n state?: Partial<TokenBalancesControllerState>;\n};\n\n/**\n * Represents a mapping of hash token contract addresses to their balances.\n */\ntype ContractBalances = Record<string, string>;\n\n/**\n * Token balances controller state\n * @property contractBalances - Hash of token contract addresses to balances\n */\nexport type TokenBalancesControllerState = {\n contractBalances: ContractBalances;\n};\n\nexport type TokenBalancesControllerGetStateAction = ControllerGetStateAction<\n typeof controllerName,\n TokenBalancesControllerState\n>;\n\nexport type TokenBalancesControllerActions =\n TokenBalancesControllerGetStateAction;\n\nexport type AllowedActions = AccountsControllerGetSelectedAccountAction;\n\nexport type TokenBalancesControllerStateChangeEvent =\n ControllerStateChangeEvent<\n typeof controllerName,\n TokenBalancesControllerState\n >;\n\nexport type TokenBalancesControllerEvents =\n TokenBalancesControllerStateChangeEvent;\n\nexport type AllowedEvents = TokensControllerStateChangeEvent;\n\nexport type TokenBalancesControllerMessenger = RestrictedControllerMessenger<\n typeof controllerName,\n TokenBalancesControllerActions | AllowedActions,\n TokenBalancesControllerEvents | AllowedEvents,\n AllowedActions['type'],\n AllowedEvents['type']\n>;\n\n/**\n * Get the default TokenBalancesController state.\n *\n * @returns The default TokenBalancesController state.\n */\nexport function getDefaultTokenBalancesState(): TokenBalancesControllerState {\n return {\n contractBalances: {},\n };\n}\n\n/**\n * Controller that passively polls on a set interval token balances\n * for tokens stored in the TokensController\n */\nexport class TokenBalancesController extends BaseController<\n typeof controllerName,\n TokenBalancesControllerState,\n TokenBalancesControllerMessenger\n> {\n #handle?: ReturnType<typeof setTimeout>;\n\n #getERC20BalanceOf: AssetsContractController['getERC20BalanceOf'];\n\n #interval: number;\n\n #tokens: Token[];\n\n #disabled: boolean;\n\n /**\n * Construct a Token Balances Controller.\n *\n * @param options - The controller options.\n * @param options.interval - Polling interval used to fetch new token balances.\n * @param options.tokens - List of tokens to track balances for.\n * @param options.disabled - If set to true, all tracked tokens contract balances updates are blocked.\n * @param options.getERC20BalanceOf - Gets the balance of the given account at the given contract address.\n * @param options.state - Initial state to set on this controller.\n * @param options.messenger - The controller restricted messenger.\n */\n constructor({\n interval = DEFAULT_INTERVAL,\n tokens = [],\n disabled = false,\n getERC20BalanceOf,\n messenger,\n state = {},\n }: TokenBalancesControllerOptions) {\n super({\n name: controllerName,\n metadata,\n messenger,\n state: {\n ...getDefaultTokenBalancesState(),\n ...state,\n },\n });\n\n this.#disabled = disabled;\n this.#interval = interval;\n this.#tokens = tokens;\n\n this.messagingSystem.subscribe(\n 'TokensController:stateChange',\n ({ tokens: newTokens, detectedTokens }) => {\n this.#tokens = [...newTokens, ...detectedTokens];\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.updateBalances();\n },\n );\n\n this.#getERC20BalanceOf = getERC20BalanceOf;\n\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.poll();\n }\n\n /**\n * Allows controller to update tracked tokens contract balances.\n */\n enable() {\n this.#disabled = false;\n }\n\n /**\n * Blocks controller from updating tracked tokens contract balances.\n */\n disable() {\n this.#disabled = true;\n }\n\n /**\n * Starts a new polling interval.\n *\n * @param interval - Polling interval used to fetch new token balances.\n */\n async poll(interval?: number): Promise<void> {\n if (interval) {\n this.#interval = interval;\n }\n\n if (this.#handle) {\n clearTimeout(this.#handle);\n }\n\n await safelyExecute(() => this.updateBalances());\n\n this.#handle = setTimeout(() => {\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.poll(this.#interval);\n }, this.#interval);\n }\n\n /**\n * Updates balances for all tokens.\n */\n async updateBalances() {\n if (this.#disabled) {\n return;\n }\n const selectedInternalAccount = this.messagingSystem.call(\n 'AccountsController:getSelectedAccount',\n );\n\n const newContractBalances: ContractBalances = {};\n for (const token of this.#tokens) {\n const { address } = token;\n try {\n const balance = await this.#getERC20BalanceOf(\n address,\n selectedInternalAccount.address,\n );\n newContractBalances[address] = toHex(balance);\n token.hasBalanceError = false;\n } catch (error) {\n newContractBalances[address] = toHex(0);\n token.hasBalanceError = true;\n }\n }\n\n this.update((state) => {\n state.contractBalances = newContractBalances;\n });\n }\n}\n\nexport default TokenBalancesController;\n"]}
|
|
@@ -54,7 +54,7 @@ var getDefaultTokenRatesControllerState = () => {
|
|
|
54
54
|
marketData: {}
|
|
55
55
|
};
|
|
56
56
|
};
|
|
57
|
-
var _handle, _pollState, _tokenPricesService, _inProcessExchangeRateUpdates,
|
|
57
|
+
var _handle, _pollState, _tokenPricesService, _inProcessExchangeRateUpdates, _selectedAccountId, _disabled, _chainId, _ticker, _interval, _allTokens, _allDetectedTokens, _subscribeToTokensStateChange, subscribeToTokensStateChange_fn, _subscribeToNetworkStateChange, subscribeToNetworkStateChange_fn, _subscribeToAccountChange, subscribeToAccountChange_fn, _getTokenAddresses, getTokenAddresses_fn, _getSelectedAccount, getSelectedAccount_fn, _getChainIdAndTicker, getChainIdAndTicker_fn, _getTokensControllerState, getTokensControllerState_fn, _stopPoll, stopPoll_fn, _poll, poll_fn, _fetchAndMapExchangeRates, fetchAndMapExchangeRates_fn, _fetchAndMapExchangeRatesForSupportedNativeCurrency, fetchAndMapExchangeRatesForSupportedNativeCurrency_fn, _fetchAndMapExchangeRatesForUnsupportedNativeCurrency, fetchAndMapExchangeRatesForUnsupportedNativeCurrency_fn;
|
|
58
58
|
var TokenRatesController = class extends _pollingcontroller.StaticIntervalPollingController {
|
|
59
59
|
/**
|
|
60
60
|
* Creates a TokenRatesController instance.
|
|
@@ -79,9 +79,9 @@ var TokenRatesController = class extends _pollingcontroller.StaticIntervalPollin
|
|
|
79
79
|
state: { ...getDefaultTokenRatesControllerState(), ...state },
|
|
80
80
|
metadata: tokenRatesControllerMetadata
|
|
81
81
|
});
|
|
82
|
-
_chunkZ4BLTVTBjs.__privateAdd.call(void 0, this, _subscribeToPreferencesStateChange);
|
|
83
82
|
_chunkZ4BLTVTBjs.__privateAdd.call(void 0, this, _subscribeToTokensStateChange);
|
|
84
83
|
_chunkZ4BLTVTBjs.__privateAdd.call(void 0, this, _subscribeToNetworkStateChange);
|
|
84
|
+
_chunkZ4BLTVTBjs.__privateAdd.call(void 0, this, _subscribeToAccountChange);
|
|
85
85
|
/**
|
|
86
86
|
* Get the user's tokens for the given chain.
|
|
87
87
|
*
|
|
@@ -89,7 +89,7 @@ var TokenRatesController = class extends _pollingcontroller.StaticIntervalPollin
|
|
|
89
89
|
* @returns The list of tokens addresses for the current chain
|
|
90
90
|
*/
|
|
91
91
|
_chunkZ4BLTVTBjs.__privateAdd.call(void 0, this, _getTokenAddresses);
|
|
92
|
-
_chunkZ4BLTVTBjs.__privateAdd.call(void 0, this,
|
|
92
|
+
_chunkZ4BLTVTBjs.__privateAdd.call(void 0, this, _getSelectedAccount);
|
|
93
93
|
_chunkZ4BLTVTBjs.__privateAdd.call(void 0, this, _getChainIdAndTicker);
|
|
94
94
|
_chunkZ4BLTVTBjs.__privateAdd.call(void 0, this, _getTokensControllerState);
|
|
95
95
|
/**
|
|
@@ -151,7 +151,7 @@ var TokenRatesController = class extends _pollingcontroller.StaticIntervalPollin
|
|
|
151
151
|
_chunkZ4BLTVTBjs.__privateAdd.call(void 0, this, _pollState, "Inactive" /* Inactive */);
|
|
152
152
|
_chunkZ4BLTVTBjs.__privateAdd.call(void 0, this, _tokenPricesService, void 0);
|
|
153
153
|
_chunkZ4BLTVTBjs.__privateAdd.call(void 0, this, _inProcessExchangeRateUpdates, {});
|
|
154
|
-
_chunkZ4BLTVTBjs.__privateAdd.call(void 0, this,
|
|
154
|
+
_chunkZ4BLTVTBjs.__privateAdd.call(void 0, this, _selectedAccountId, void 0);
|
|
155
155
|
_chunkZ4BLTVTBjs.__privateAdd.call(void 0, this, _disabled, void 0);
|
|
156
156
|
_chunkZ4BLTVTBjs.__privateAdd.call(void 0, this, _chainId, void 0);
|
|
157
157
|
_chunkZ4BLTVTBjs.__privateAdd.call(void 0, this, _ticker, void 0);
|
|
@@ -165,13 +165,13 @@ var TokenRatesController = class extends _pollingcontroller.StaticIntervalPollin
|
|
|
165
165
|
const { chainId: currentChainId, ticker: currentTicker } = _chunkZ4BLTVTBjs.__privateMethod.call(void 0, this, _getChainIdAndTicker, getChainIdAndTicker_fn).call(this);
|
|
166
166
|
_chunkZ4BLTVTBjs.__privateSet.call(void 0, this, _chainId, currentChainId);
|
|
167
167
|
_chunkZ4BLTVTBjs.__privateSet.call(void 0, this, _ticker, currentTicker);
|
|
168
|
-
_chunkZ4BLTVTBjs.__privateSet.call(void 0, this,
|
|
168
|
+
_chunkZ4BLTVTBjs.__privateSet.call(void 0, this, _selectedAccountId, _chunkZ4BLTVTBjs.__privateMethod.call(void 0, this, _getSelectedAccount, getSelectedAccount_fn).call(this).id);
|
|
169
169
|
const { allTokens, allDetectedTokens } = _chunkZ4BLTVTBjs.__privateMethod.call(void 0, this, _getTokensControllerState, getTokensControllerState_fn).call(this);
|
|
170
170
|
_chunkZ4BLTVTBjs.__privateSet.call(void 0, this, _allTokens, allTokens);
|
|
171
171
|
_chunkZ4BLTVTBjs.__privateSet.call(void 0, this, _allDetectedTokens, allDetectedTokens);
|
|
172
|
-
_chunkZ4BLTVTBjs.__privateMethod.call(void 0, this, _subscribeToPreferencesStateChange, subscribeToPreferencesStateChange_fn).call(this);
|
|
173
172
|
_chunkZ4BLTVTBjs.__privateMethod.call(void 0, this, _subscribeToTokensStateChange, subscribeToTokensStateChange_fn).call(this);
|
|
174
173
|
_chunkZ4BLTVTBjs.__privateMethod.call(void 0, this, _subscribeToNetworkStateChange, subscribeToNetworkStateChange_fn).call(this);
|
|
174
|
+
_chunkZ4BLTVTBjs.__privateMethod.call(void 0, this, _subscribeToAccountChange, subscribeToAccountChange_fn).call(this);
|
|
175
175
|
}
|
|
176
176
|
/**
|
|
177
177
|
* Allows controller to make active and passive polling requests
|
|
@@ -278,32 +278,13 @@ _handle = new WeakMap();
|
|
|
278
278
|
_pollState = new WeakMap();
|
|
279
279
|
_tokenPricesService = new WeakMap();
|
|
280
280
|
_inProcessExchangeRateUpdates = new WeakMap();
|
|
281
|
-
|
|
281
|
+
_selectedAccountId = new WeakMap();
|
|
282
282
|
_disabled = new WeakMap();
|
|
283
283
|
_chainId = new WeakMap();
|
|
284
284
|
_ticker = new WeakMap();
|
|
285
285
|
_interval = new WeakMap();
|
|
286
286
|
_allTokens = new WeakMap();
|
|
287
287
|
_allDetectedTokens = new WeakMap();
|
|
288
|
-
_subscribeToPreferencesStateChange = new WeakSet();
|
|
289
|
-
subscribeToPreferencesStateChange_fn = function() {
|
|
290
|
-
this.messagingSystem.subscribe(
|
|
291
|
-
"PreferencesController:stateChange",
|
|
292
|
-
// TODO: Either fix this lint violation or explain why it's necessary to ignore.
|
|
293
|
-
// eslint-disable-next-line @typescript-eslint/no-misused-promises
|
|
294
|
-
async (selectedAddress) => {
|
|
295
|
-
if (_chunkZ4BLTVTBjs.__privateGet.call(void 0, this, _selectedAddress) !== selectedAddress) {
|
|
296
|
-
_chunkZ4BLTVTBjs.__privateSet.call(void 0, this, _selectedAddress, selectedAddress);
|
|
297
|
-
if (_chunkZ4BLTVTBjs.__privateGet.call(void 0, this, _pollState) === "Active" /* Active */) {
|
|
298
|
-
await this.updateExchangeRates();
|
|
299
|
-
}
|
|
300
|
-
}
|
|
301
|
-
},
|
|
302
|
-
({ selectedAddress }) => {
|
|
303
|
-
return selectedAddress;
|
|
304
|
-
}
|
|
305
|
-
);
|
|
306
|
-
};
|
|
307
288
|
_subscribeToTokensStateChange = new WeakSet();
|
|
308
289
|
subscribeToTokensStateChange_fn = function() {
|
|
309
290
|
this.messagingSystem.subscribe(
|
|
@@ -350,10 +331,31 @@ subscribeToNetworkStateChange_fn = function() {
|
|
|
350
331
|
}
|
|
351
332
|
);
|
|
352
333
|
};
|
|
334
|
+
_subscribeToAccountChange = new WeakSet();
|
|
335
|
+
subscribeToAccountChange_fn = function() {
|
|
336
|
+
this.messagingSystem.subscribe(
|
|
337
|
+
"AccountsController:selectedEvmAccountChange",
|
|
338
|
+
// TODO: Either fix this lint violation or explain why it's necessary to ignore.
|
|
339
|
+
// eslint-disable-next-line @typescript-eslint/no-misused-promises
|
|
340
|
+
async (selectedAccount) => {
|
|
341
|
+
if (_chunkZ4BLTVTBjs.__privateGet.call(void 0, this, _selectedAccountId) !== selectedAccount.id) {
|
|
342
|
+
_chunkZ4BLTVTBjs.__privateSet.call(void 0, this, _selectedAccountId, selectedAccount.id);
|
|
343
|
+
if (_chunkZ4BLTVTBjs.__privateGet.call(void 0, this, _pollState) === "Active" /* Active */) {
|
|
344
|
+
await this.updateExchangeRates();
|
|
345
|
+
}
|
|
346
|
+
}
|
|
347
|
+
}
|
|
348
|
+
);
|
|
349
|
+
};
|
|
353
350
|
_getTokenAddresses = new WeakSet();
|
|
354
351
|
getTokenAddresses_fn = function(chainId) {
|
|
355
|
-
const
|
|
356
|
-
|
|
352
|
+
const selectedAccount = this.messagingSystem.call(
|
|
353
|
+
"AccountsController:getAccount",
|
|
354
|
+
_chunkZ4BLTVTBjs.__privateGet.call(void 0, this, _selectedAccountId)
|
|
355
|
+
);
|
|
356
|
+
const selectedAddress = selectedAccount?.address ?? "";
|
|
357
|
+
const tokens = _chunkZ4BLTVTBjs.__privateGet.call(void 0, this, _allTokens)[chainId]?.[selectedAddress] || [];
|
|
358
|
+
const detectedTokens = _chunkZ4BLTVTBjs.__privateGet.call(void 0, this, _allDetectedTokens)[chainId]?.[selectedAddress] || [];
|
|
357
359
|
return [
|
|
358
360
|
...new Set(
|
|
359
361
|
[...tokens, ...detectedTokens].map(
|
|
@@ -362,12 +364,12 @@ getTokenAddresses_fn = function(chainId) {
|
|
|
362
364
|
)
|
|
363
365
|
].sort();
|
|
364
366
|
};
|
|
365
|
-
|
|
366
|
-
|
|
367
|
-
const
|
|
368
|
-
"
|
|
367
|
+
_getSelectedAccount = new WeakSet();
|
|
368
|
+
getSelectedAccount_fn = function() {
|
|
369
|
+
const selectedAccount = this.messagingSystem.call(
|
|
370
|
+
"AccountsController:getSelectedAccount"
|
|
369
371
|
);
|
|
370
|
-
return
|
|
372
|
+
return selectedAccount;
|
|
371
373
|
};
|
|
372
374
|
_getChainIdAndTicker = new WeakSet();
|
|
373
375
|
getChainIdAndTicker_fn = function() {
|
|
@@ -525,4 +527,4 @@ var TokenRatesController_default = TokenRatesController;
|
|
|
525
527
|
|
|
526
528
|
|
|
527
529
|
exports.controllerName = controllerName; exports.getDefaultTokenRatesControllerState = getDefaultTokenRatesControllerState; exports.TokenRatesController = TokenRatesController; exports.TokenRatesController_default = TokenRatesController_default;
|
|
528
|
-
//# sourceMappingURL=chunk-
|
|
530
|
+
//# sourceMappingURL=chunk-RPQ737HL.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/TokenRatesController.ts"],"names":[],"mappings":"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","sourcesContent":["import type {\n AccountsControllerGetAccountAction,\n AccountsControllerGetSelectedAccountAction,\n AccountsControllerSelectedEvmAccountChangeEvent,\n} from '@metamask/accounts-controller';\nimport type {\n ControllerGetStateAction,\n ControllerStateChangeEvent,\n RestrictedControllerMessenger,\n} from '@metamask/base-controller';\nimport {\n safelyExecute,\n toChecksumHexAddress,\n FALL_BACK_VS_CURRENCY,\n toHex,\n} from '@metamask/controller-utils';\nimport type { InternalAccount } from '@metamask/keyring-api';\nimport type {\n NetworkClientId,\n NetworkControllerGetNetworkClientByIdAction,\n NetworkControllerGetStateAction,\n NetworkControllerStateChangeEvent,\n} from '@metamask/network-controller';\nimport { StaticIntervalPollingController } from '@metamask/polling-controller';\nimport { createDeferredPromise, type Hex } from '@metamask/utils';\nimport { isEqual } from 'lodash';\n\nimport { reduceInBatchesSerially, TOKEN_PRICES_BATCH_SIZE } from './assetsUtil';\nimport { fetchExchangeRate as fetchNativeCurrencyExchangeRate } from './crypto-compare-service';\nimport type { AbstractTokenPricesService } from './token-prices-service/abstract-token-prices-service';\nimport { ZERO_ADDRESS } from './token-prices-service/codefi-v2';\nimport type {\n TokensControllerGetStateAction,\n TokensControllerStateChangeEvent,\n TokensControllerState,\n} from './TokensController';\n\n/**\n * @type Token\n *\n * Token representation\n * @property address - Hex address of the token contract\n * @property decimals - Number of decimals the token uses\n * @property symbol - Symbol of the token\n * @property aggregators - An array containing the token's aggregators\n * @property image - Image of the token, url or bit32 image\n * @property hasBalanceError - 'true' if there is an error while updating the token balance\n * @property isERC721 - 'true' if the token is a ERC721 token\n * @property name - Name of the token\n */\nexport type Token = {\n address: string;\n decimals: number;\n symbol: string;\n aggregators?: string[];\n image?: string;\n hasBalanceError?: boolean;\n isERC721?: boolean;\n name?: string;\n};\n\nconst DEFAULT_INTERVAL = 180000;\n\nexport type ContractExchangeRates = {\n [address: string]: number | undefined;\n};\n\ntype MarketDataDetails = {\n tokenAddress: `0x${string}`;\n currency: string;\n allTimeHigh: number;\n allTimeLow: number;\n circulatingSupply: number;\n dilutedMarketCap: number;\n high1d: number;\n low1d: number;\n marketCap: number;\n marketCapPercentChange1d: number;\n price: number;\n priceChange1d: number;\n pricePercentChange1d: number;\n pricePercentChange1h: number;\n pricePercentChange1y: number;\n pricePercentChange7d: number;\n pricePercentChange14d: number;\n pricePercentChange30d: number;\n pricePercentChange200d: number;\n totalVolume: number;\n};\n\n/**\n * Represents a mapping of token contract addresses to their market data.\n */\nexport type ContractMarketData = Record<Hex, MarketDataDetails>;\n\nenum PollState {\n Active = 'Active',\n Inactive = 'Inactive',\n}\n\n/**\n * The external actions available to the {@link TokenRatesController}.\n */\nexport type AllowedActions =\n | TokensControllerGetStateAction\n | NetworkControllerGetNetworkClientByIdAction\n | NetworkControllerGetStateAction\n | AccountsControllerGetAccountAction\n | AccountsControllerGetSelectedAccountAction;\n\n/**\n * The external events available to the {@link TokenRatesController}.\n */\nexport type AllowedEvents =\n | TokensControllerStateChangeEvent\n | NetworkControllerStateChangeEvent\n | AccountsControllerSelectedEvmAccountChangeEvent;\n\n/**\n * The name of the {@link TokenRatesController}.\n */\nexport const controllerName = 'TokenRatesController';\n\n/**\n * @type TokenRatesState\n *\n * Token rates controller state\n * @property marketData - Market data for tokens, keyed by chain ID and then token contract address.\n */\nexport type TokenRatesControllerState = {\n marketData: Record<Hex, Record<Hex, MarketDataDetails>>;\n};\n\n/**\n * The action that can be performed to get the state of the {@link TokenRatesController}.\n */\nexport type TokenRatesControllerGetStateAction = ControllerGetStateAction<\n typeof controllerName,\n TokenRatesControllerState\n>;\n\n/**\n * The actions that can be performed using the {@link TokenRatesController}.\n */\nexport type TokenRatesControllerActions = TokenRatesControllerGetStateAction;\n\n/**\n * The event that {@link TokenRatesController} can emit.\n */\nexport type TokenRatesControllerStateChangeEvent = ControllerStateChangeEvent<\n typeof controllerName,\n TokenRatesControllerState\n>;\n\n/**\n * The events that {@link TokenRatesController} can emit.\n */\nexport type TokenRatesControllerEvents = TokenRatesControllerStateChangeEvent;\n\n/**\n * The messenger of the {@link TokenRatesController} for communication.\n */\nexport type TokenRatesControllerMessenger = RestrictedControllerMessenger<\n typeof controllerName,\n TokenRatesControllerActions | AllowedActions,\n TokenRatesControllerEvents | AllowedEvents,\n AllowedActions['type'],\n AllowedEvents['type']\n>;\n\n/**\n * Uses the CryptoCompare API to fetch the exchange rate between one currency\n * and another, i.e., the multiplier to apply the amount of one currency in\n * order to convert it to another.\n *\n * @param args - The arguments to this function.\n * @param args.from - The currency to convert from.\n * @param args.to - The currency to convert to.\n * @returns The exchange rate between `fromCurrency` to `toCurrency` if one\n * exists, or null if one does not.\n */\nasync function getCurrencyConversionRate({\n from,\n to,\n}: {\n from: string;\n to: string;\n}) {\n const includeUSDRate = false;\n try {\n const result = await fetchNativeCurrencyExchangeRate(\n to,\n from,\n includeUSDRate,\n );\n return result.conversionRate;\n } catch (error) {\n if (\n error instanceof Error &&\n error.message.includes('market does not exist for this coin pair')\n ) {\n return null;\n }\n throw error;\n }\n}\n\nconst tokenRatesControllerMetadata = {\n marketData: { persist: true, anonymous: false },\n};\n\n/**\n * Get the default {@link TokenRatesController} state.\n *\n * @returns The default {@link TokenRatesController} state.\n */\nexport const getDefaultTokenRatesControllerState =\n (): TokenRatesControllerState => {\n return {\n marketData: {},\n };\n };\n\n/**\n * Controller that passively polls on a set interval for token-to-fiat exchange rates\n * for tokens stored in the TokensController\n */\nexport class TokenRatesController extends StaticIntervalPollingController<\n typeof controllerName,\n TokenRatesControllerState,\n TokenRatesControllerMessenger\n> {\n #handle?: ReturnType<typeof setTimeout>;\n\n #pollState = PollState.Inactive;\n\n #tokenPricesService: AbstractTokenPricesService;\n\n #inProcessExchangeRateUpdates: Record<`${Hex}:${string}`, Promise<void>> = {};\n\n #selectedAccountId: string;\n\n #disabled: boolean;\n\n #chainId: Hex;\n\n #ticker: string;\n\n #interval: number;\n\n #allTokens: TokensControllerState['allTokens'];\n\n #allDetectedTokens: TokensControllerState['allDetectedTokens'];\n\n /**\n * Creates a TokenRatesController instance.\n *\n * @param options - The controller options.\n * @param options.interval - The polling interval in ms\n * @param options.disabled - Boolean to track if network requests are blocked\n * @param options.tokenPricesService - An object in charge of retrieving token price\n * @param options.messenger - The controller messenger instance for communication\n * @param options.state - Initial state to set on this controller\n */\n constructor({\n interval = DEFAULT_INTERVAL,\n disabled = false,\n tokenPricesService,\n messenger,\n state,\n }: {\n interval?: number;\n disabled?: boolean;\n tokenPricesService: AbstractTokenPricesService;\n messenger: TokenRatesControllerMessenger;\n state?: Partial<TokenRatesControllerState>;\n }) {\n super({\n name: controllerName,\n messenger,\n state: { ...getDefaultTokenRatesControllerState(), ...state },\n metadata: tokenRatesControllerMetadata,\n });\n\n this.setIntervalLength(interval);\n this.#tokenPricesService = tokenPricesService;\n this.#disabled = disabled;\n this.#interval = interval;\n\n const { chainId: currentChainId, ticker: currentTicker } =\n this.#getChainIdAndTicker();\n this.#chainId = currentChainId;\n this.#ticker = currentTicker;\n\n this.#selectedAccountId = this.#getSelectedAccount().id;\n\n const { allTokens, allDetectedTokens } = this.#getTokensControllerState();\n this.#allTokens = allTokens;\n this.#allDetectedTokens = allDetectedTokens;\n\n this.#subscribeToTokensStateChange();\n\n this.#subscribeToNetworkStateChange();\n\n this.#subscribeToAccountChange();\n }\n\n #subscribeToTokensStateChange() {\n this.messagingSystem.subscribe(\n 'TokensController:stateChange',\n // TODO: Either fix this lint violation or explain why it's necessary to ignore.\n // eslint-disable-next-line @typescript-eslint/no-misused-promises\n async ({ allTokens, allDetectedTokens }) => {\n const previousTokenAddresses = this.#getTokenAddresses(this.#chainId);\n this.#allTokens = allTokens;\n this.#allDetectedTokens = allDetectedTokens;\n\n const newTokenAddresses = this.#getTokenAddresses(this.#chainId);\n if (\n !isEqual(previousTokenAddresses, newTokenAddresses) &&\n this.#pollState === PollState.Active\n ) {\n await this.updateExchangeRates();\n }\n },\n ({ allTokens, allDetectedTokens }) => {\n return { allTokens, allDetectedTokens };\n },\n );\n }\n\n #subscribeToNetworkStateChange() {\n this.messagingSystem.subscribe(\n 'NetworkController:stateChange',\n // TODO: Either fix this lint violation or explain why it's necessary to ignore.\n // eslint-disable-next-line @typescript-eslint/no-misused-promises\n async ({ selectedNetworkClientId }) => {\n const {\n configuration: { chainId, ticker },\n } = this.messagingSystem.call(\n 'NetworkController:getNetworkClientById',\n selectedNetworkClientId,\n );\n\n if (this.#chainId !== chainId || this.#ticker !== ticker) {\n this.update((state) => {\n state.marketData = {};\n });\n this.#chainId = chainId;\n this.#ticker = ticker;\n if (this.#pollState === PollState.Active) {\n await this.updateExchangeRates();\n }\n }\n },\n );\n }\n\n #subscribeToAccountChange() {\n this.messagingSystem.subscribe(\n 'AccountsController:selectedEvmAccountChange',\n // TODO: Either fix this lint violation or explain why it's necessary to ignore.\n // eslint-disable-next-line @typescript-eslint/no-misused-promises\n async (selectedAccount) => {\n if (this.#selectedAccountId !== selectedAccount.id) {\n this.#selectedAccountId = selectedAccount.id;\n if (this.#pollState === PollState.Active) {\n await this.updateExchangeRates();\n }\n }\n },\n );\n }\n\n /**\n * Get the user's tokens for the given chain.\n *\n * @param chainId - The chain ID.\n * @returns The list of tokens addresses for the current chain\n */\n #getTokenAddresses(chainId: Hex): Hex[] {\n const selectedAccount = this.messagingSystem.call(\n 'AccountsController:getAccount',\n this.#selectedAccountId,\n );\n const selectedAddress = selectedAccount?.address ?? '';\n const tokens = this.#allTokens[chainId]?.[selectedAddress] || [];\n const detectedTokens =\n this.#allDetectedTokens[chainId]?.[selectedAddress] || [];\n\n return [\n ...new Set(\n [...tokens, ...detectedTokens].map((token) =>\n toHex(toChecksumHexAddress(token.address)),\n ),\n ),\n ].sort();\n }\n\n /**\n * Allows controller to make active and passive polling requests\n */\n enable(): void {\n this.#disabled = false;\n }\n\n /**\n * Blocks controller from making network calls\n */\n disable(): void {\n this.#disabled = true;\n }\n\n /**\n * Start (or restart) polling.\n */\n async start() {\n this.#stopPoll();\n this.#pollState = PollState.Active;\n await this.#poll();\n }\n\n /**\n * Stop polling.\n */\n stop() {\n this.#stopPoll();\n this.#pollState = PollState.Inactive;\n }\n\n #getSelectedAccount(): InternalAccount {\n const selectedAccount = this.messagingSystem.call(\n 'AccountsController:getSelectedAccount',\n );\n\n return selectedAccount;\n }\n\n #getChainIdAndTicker(): {\n chainId: Hex;\n ticker: string;\n } {\n const { selectedNetworkClientId } = this.messagingSystem.call(\n 'NetworkController:getState',\n );\n const networkClient = this.messagingSystem.call(\n 'NetworkController:getNetworkClientById',\n selectedNetworkClientId,\n );\n return {\n chainId: networkClient.configuration.chainId,\n ticker: networkClient.configuration.ticker,\n };\n }\n\n #getTokensControllerState(): {\n allTokens: TokensControllerState['allTokens'];\n allDetectedTokens: TokensControllerState['allDetectedTokens'];\n } {\n const { allTokens, allDetectedTokens } = this.messagingSystem.call(\n 'TokensController:getState',\n );\n\n return {\n allTokens,\n allDetectedTokens,\n };\n }\n\n /**\n * Clear the active polling timer, if present.\n */\n #stopPoll() {\n if (this.#handle) {\n clearTimeout(this.#handle);\n }\n }\n\n /**\n * Poll for exchange rate updates.\n */\n async #poll() {\n await safelyExecute(() => this.updateExchangeRates());\n\n // Poll using recursive `setTimeout` instead of `setInterval` so that\n // requests don't stack if they take longer than the polling interval\n this.#handle = setTimeout(() => {\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.#poll();\n }, this.#interval);\n }\n\n /**\n * Updates exchange rates for all tokens.\n */\n async updateExchangeRates() {\n await this.updateExchangeRatesByChainId({\n chainId: this.#chainId,\n nativeCurrency: this.#ticker,\n });\n }\n\n /**\n * Updates exchange rates for all tokens.\n *\n * @param options - The options to fetch exchange rates.\n * @param options.chainId - The chain ID.\n * @param options.nativeCurrency - The ticker for the chain.\n */\n async updateExchangeRatesByChainId({\n chainId,\n nativeCurrency,\n }: {\n chainId: Hex;\n nativeCurrency: string;\n }) {\n if (this.#disabled) {\n return;\n }\n\n const tokenAddresses = this.#getTokenAddresses(chainId);\n\n const updateKey: `${Hex}:${string}` = `${chainId}:${nativeCurrency}`;\n if (updateKey in this.#inProcessExchangeRateUpdates) {\n // This prevents redundant updates\n // This promise is resolved after the in-progress update has finished,\n // and state has been updated.\n await this.#inProcessExchangeRateUpdates[updateKey];\n return;\n }\n\n const {\n promise: inProgressUpdate,\n resolve: updateSucceeded,\n reject: updateFailed,\n } = createDeferredPromise({ suppressUnhandledRejection: true });\n this.#inProcessExchangeRateUpdates[updateKey] = inProgressUpdate;\n\n try {\n const contractInformations = await this.#fetchAndMapExchangeRates({\n tokenAddresses,\n chainId,\n nativeCurrency,\n });\n\n const marketData = {\n [chainId]: {\n ...(contractInformations ?? {}),\n },\n };\n\n this.update((state) => {\n state.marketData = marketData;\n });\n updateSucceeded();\n } catch (error: unknown) {\n updateFailed(error);\n throw error;\n } finally {\n delete this.#inProcessExchangeRateUpdates[updateKey];\n }\n }\n\n /**\n * Uses the token prices service to retrieve exchange rates for tokens in a\n * particular currency.\n *\n * If the price API does not support the given chain ID, returns an empty\n * object.\n *\n * If the price API does not support the given currency, retrieves exchange\n * rates in a known currency instead, then converts those rates using the\n * exchange rate between the known currency and desired currency.\n *\n * @param args - The arguments to this function.\n * @param args.tokenAddresses - Addresses for tokens.\n * @param args.chainId - The EIP-155 ID of the chain where the tokens live.\n * @param args.nativeCurrency - The native currency in which to request\n * exchange rates.\n * @returns A map from token address to its exchange rate in the native\n * currency, or an empty map if no exchange rates can be obtained for the\n * chain ID.\n */\n async #fetchAndMapExchangeRates({\n tokenAddresses,\n chainId,\n nativeCurrency,\n }: {\n tokenAddresses: Hex[];\n chainId: Hex;\n nativeCurrency: string;\n }): Promise<ContractMarketData> {\n if (!this.#tokenPricesService.validateChainIdSupported(chainId)) {\n return tokenAddresses.reduce((obj, tokenAddress) => {\n obj = {\n ...obj,\n [tokenAddress]: undefined,\n };\n\n return obj;\n }, {});\n }\n\n if (this.#tokenPricesService.validateCurrencySupported(nativeCurrency)) {\n return await this.#fetchAndMapExchangeRatesForSupportedNativeCurrency({\n tokenAddresses,\n chainId,\n nativeCurrency,\n });\n }\n\n return await this.#fetchAndMapExchangeRatesForUnsupportedNativeCurrency({\n tokenAddresses,\n nativeCurrency,\n });\n }\n\n /**\n * Updates token rates for the given networkClientId\n *\n * @param networkClientId - The network client ID used to get a ticker value.\n * @returns The controller state.\n */\n async _executePoll(networkClientId: NetworkClientId): Promise<void> {\n const networkClient = this.messagingSystem.call(\n 'NetworkController:getNetworkClientById',\n networkClientId,\n );\n await this.updateExchangeRatesByChainId({\n chainId: networkClient.configuration.chainId,\n nativeCurrency: networkClient.configuration.ticker,\n });\n }\n\n /**\n * Retrieves prices in the given currency for the given tokens on the given\n * chain. Ensures that token addresses are checksum addresses.\n *\n * @param args - The arguments to this function.\n * @param args.tokenAddresses - Addresses for tokens.\n * @param args.chainId - The EIP-155 ID of the chain where the tokens live.\n * @param args.nativeCurrency - The native currency in which to request\n * prices.\n * @returns A map of the token addresses (as checksums) to their prices in the\n * native currency.\n */\n async #fetchAndMapExchangeRatesForSupportedNativeCurrency({\n tokenAddresses,\n chainId,\n nativeCurrency,\n }: {\n tokenAddresses: Hex[];\n chainId: Hex;\n nativeCurrency: string;\n }): Promise<ContractMarketData> {\n let contractNativeInformations;\n const tokenPricesByTokenAddress = await reduceInBatchesSerially<\n Hex,\n Awaited<ReturnType<AbstractTokenPricesService['fetchTokenPrices']>>\n >({\n values: [...tokenAddresses].sort(),\n batchSize: TOKEN_PRICES_BATCH_SIZE,\n eachBatch: async (allTokenPricesByTokenAddress, batch) => {\n const tokenPricesByTokenAddressForBatch =\n await this.#tokenPricesService.fetchTokenPrices({\n tokenAddresses: batch,\n chainId,\n currency: nativeCurrency,\n });\n\n return {\n ...allTokenPricesByTokenAddress,\n ...tokenPricesByTokenAddressForBatch,\n };\n },\n initialResult: {},\n });\n contractNativeInformations = tokenPricesByTokenAddress;\n\n // fetch for native token\n if (tokenAddresses.length === 0) {\n const contractNativeInformationsNative =\n await this.#tokenPricesService.fetchTokenPrices({\n tokenAddresses: [],\n chainId,\n currency: nativeCurrency,\n });\n\n contractNativeInformations = {\n [ZERO_ADDRESS]: {\n currency: nativeCurrency,\n ...contractNativeInformationsNative[ZERO_ADDRESS],\n },\n };\n }\n return Object.entries(contractNativeInformations).reduce(\n (obj, [tokenAddress, token]) => {\n obj = {\n ...obj,\n [tokenAddress]: { ...token },\n };\n\n return obj;\n },\n {},\n );\n }\n\n /**\n * If the price API does not support a given native currency, then we need to\n * convert it to a fallback currency and feed that currency into the price\n * API, then convert the prices to our desired native currency.\n *\n * @param args - The arguments to this function.\n * @param args.tokenAddresses - Addresses for tokens.\n * @param args.nativeCurrency - The native currency in which to request\n * prices.\n * @returns A map of the token addresses (as checksums) to their prices in the\n * native currency.\n */\n async #fetchAndMapExchangeRatesForUnsupportedNativeCurrency({\n tokenAddresses,\n nativeCurrency,\n }: {\n tokenAddresses: Hex[];\n nativeCurrency: string;\n }): Promise<ContractMarketData> {\n const [\n contractExchangeInformations,\n fallbackCurrencyToNativeCurrencyConversionRate,\n ] = await Promise.all([\n this.#fetchAndMapExchangeRatesForSupportedNativeCurrency({\n tokenAddresses,\n chainId: this.#chainId,\n nativeCurrency: FALL_BACK_VS_CURRENCY,\n }),\n getCurrencyConversionRate({\n from: FALL_BACK_VS_CURRENCY,\n to: nativeCurrency,\n }),\n ]);\n\n if (fallbackCurrencyToNativeCurrencyConversionRate === null) {\n return {};\n }\n\n const updatedContractExchangeRates = Object.entries(\n contractExchangeInformations,\n ).reduce((acc, [tokenAddress, token]) => {\n acc = {\n ...acc,\n [tokenAddress]: {\n ...token,\n price: token.price\n ? token.price * fallbackCurrencyToNativeCurrencyConversionRate\n : undefined,\n },\n };\n return acc;\n }, {});\n\n return updatedContractExchangeRates;\n }\n}\n\nexport default TokenRatesController;\n"]}
|