@metamask/assets-controllers 20.0.0 → 22.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.
Files changed (73) hide show
  1. package/CHANGELOG.md +80 -1
  2. package/dist/AccountTrackerController.d.ts +28 -7
  3. package/dist/AccountTrackerController.d.ts.map +1 -1
  4. package/dist/AccountTrackerController.js +104 -30
  5. package/dist/AccountTrackerController.js.map +1 -1
  6. package/dist/AssetsContractController.d.ts +3 -3
  7. package/dist/AssetsContractController.d.ts.map +1 -1
  8. package/dist/AssetsContractController.js +9 -3
  9. package/dist/AssetsContractController.js.map +1 -1
  10. package/dist/CurrencyRateController.d.ts +2 -2
  11. package/dist/CurrencyRateController.d.ts.map +1 -1
  12. package/dist/CurrencyRateController.js +1 -1
  13. package/dist/CurrencyRateController.js.map +1 -1
  14. package/dist/NftController.d.ts +73 -1
  15. package/dist/NftController.d.ts.map +1 -1
  16. package/dist/NftController.js +26 -9
  17. package/dist/NftController.js.map +1 -1
  18. package/dist/NftDetectionController.d.ts +7 -4
  19. package/dist/NftDetectionController.d.ts.map +1 -1
  20. package/dist/NftDetectionController.js +26 -14
  21. package/dist/NftDetectionController.js.map +1 -1
  22. package/dist/Standards/ERC20Standard.d.ts.map +1 -1
  23. package/dist/Standards/ERC20Standard.js +4 -0
  24. package/dist/Standards/ERC20Standard.js.map +1 -1
  25. package/dist/Standards/NftStandards/ERC1155/ERC1155Standard.d.ts +11 -10
  26. package/dist/Standards/NftStandards/ERC1155/ERC1155Standard.d.ts.map +1 -1
  27. package/dist/Standards/NftStandards/ERC1155/ERC1155Standard.js +106 -85
  28. package/dist/Standards/NftStandards/ERC1155/ERC1155Standard.js.map +1 -1
  29. package/dist/Standards/NftStandards/ERC721/ERC721Standard.d.ts.map +1 -1
  30. package/dist/Standards/NftStandards/ERC721/ERC721Standard.js +2 -0
  31. package/dist/Standards/NftStandards/ERC721/ERC721Standard.js.map +1 -1
  32. package/dist/Standards/standards-types.d.ts.map +1 -1
  33. package/dist/Standards/standards-types.js.map +1 -1
  34. package/dist/TokenDetectionController.d.ts +2 -2
  35. package/dist/TokenDetectionController.d.ts.map +1 -1
  36. package/dist/TokenDetectionController.js +1 -1
  37. package/dist/TokenDetectionController.js.map +1 -1
  38. package/dist/TokenListController.d.ts +2 -2
  39. package/dist/TokenListController.d.ts.map +1 -1
  40. package/dist/TokenListController.js +3 -15
  41. package/dist/TokenListController.js.map +1 -1
  42. package/dist/TokenRatesController.d.ts +9 -75
  43. package/dist/TokenRatesController.d.ts.map +1 -1
  44. package/dist/TokenRatesController.js +180 -203
  45. package/dist/TokenRatesController.js.map +1 -1
  46. package/dist/TokensController.d.ts +6 -8
  47. package/dist/TokensController.d.ts.map +1 -1
  48. package/dist/TokensController.js +70 -21
  49. package/dist/TokensController.js.map +1 -1
  50. package/dist/assetsUtil.d.ts +59 -9
  51. package/dist/assetsUtil.d.ts.map +1 -1
  52. package/dist/assetsUtil.js +138 -27
  53. package/dist/assetsUtil.js.map +1 -1
  54. package/dist/index.d.ts +1 -0
  55. package/dist/index.d.ts.map +1 -1
  56. package/dist/index.js +3 -1
  57. package/dist/index.js.map +1 -1
  58. package/dist/token-prices-service/abstract-token-prices-service.d.ts +62 -0
  59. package/dist/token-prices-service/abstract-token-prices-service.d.ts.map +1 -0
  60. package/dist/token-prices-service/abstract-token-prices-service.js +3 -0
  61. package/dist/token-prices-service/abstract-token-prices-service.js.map +1 -0
  62. package/dist/token-prices-service/codefi-v2.d.ts +80 -0
  63. package/dist/token-prices-service/codefi-v2.d.ts.map +1 -0
  64. package/dist/token-prices-service/codefi-v2.js +327 -0
  65. package/dist/token-prices-service/codefi-v2.js.map +1 -0
  66. package/dist/token-prices-service/index.d.ts +3 -0
  67. package/dist/token-prices-service/index.d.ts.map +1 -0
  68. package/dist/token-prices-service/index.js +6 -0
  69. package/dist/token-prices-service/index.js.map +1 -0
  70. package/dist/token-service.d.ts.map +1 -1
  71. package/dist/token-service.js +1 -1
  72. package/dist/token-service.js.map +1 -1
  73. package/package.json +6 -3
@@ -8,60 +8,67 @@ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, ge
8
8
  step((generator = generator.apply(thisArg, _arguments || [])).next());
9
9
  });
10
10
  };
11
- var __classPrivateFieldGet = (this && this.__classPrivateFieldGet) || function (receiver, state, kind, f) {
12
- if (kind === "a" && !f) throw new TypeError("Private accessor was defined without a getter");
13
- 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");
14
- return kind === "m" ? f : kind === "a" ? f.call(receiver) : f ? f.value : state.get(receiver);
15
- };
16
11
  var __classPrivateFieldSet = (this && this.__classPrivateFieldSet) || function (receiver, state, value, kind, f) {
17
12
  if (kind === "m") throw new TypeError("Private method is not writable");
18
13
  if (kind === "a" && !f) throw new TypeError("Private accessor was defined without a setter");
19
14
  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");
20
15
  return (kind === "a" ? f.call(receiver, value) : f ? f.value = value : state.set(receiver, value)), value;
21
16
  };
22
- var _TokenRatesController_instances, _TokenRatesController_pollState, _TokenRatesController_updateTokenList, _TokenRatesController_stopPoll, _TokenRatesController_poll;
17
+ var __classPrivateFieldGet = (this && this.__classPrivateFieldGet) || function (receiver, state, kind, f) {
18
+ if (kind === "a" && !f) throw new TypeError("Private accessor was defined without a getter");
19
+ 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");
20
+ return kind === "m" ? f : kind === "a" ? f.call(receiver) : f ? f.value : state.get(receiver);
21
+ };
22
+ var _TokenRatesController_instances, _TokenRatesController_pollState, _TokenRatesController_tokenPricesService, _TokenRatesController_inProcessExchangeRateUpdates, _TokenRatesController_getTokenAddresses, _TokenRatesController_stopPoll, _TokenRatesController_poll, _TokenRatesController_fetchAndMapExchangeRates, _TokenRatesController_fetchAndMapExchangeRatesForSupportedNativeCurrency, _TokenRatesController_fetchAndMapExchangeRatesForUnsupportedNativeCurrency;
23
23
  Object.defineProperty(exports, "__esModule", { value: true });
24
24
  exports.TokenRatesController = void 0;
25
25
  const controller_utils_1 = require("@metamask/controller-utils");
26
26
  const polling_controller_1 = require("@metamask/polling-controller");
27
+ const lodash_1 = require("lodash");
28
+ const assetsUtil_1 = require("./assetsUtil");
27
29
  const crypto_compare_1 = require("./crypto-compare");
28
30
  var PollState;
29
31
  (function (PollState) {
30
32
  PollState["Active"] = "Active";
31
33
  PollState["Inactive"] = "Inactive";
32
34
  })(PollState || (PollState = {}));
33
- const CoinGeckoApi = {
34
- BASE_URL: 'https://api.coingecko.com/api/v3',
35
- getTokenPriceURL(chainSlug, query) {
36
- return `${this.BASE_URL}/simple/token_price/${chainSlug}?${query}`;
37
- },
38
- getPlatformsURL() {
39
- return `${this.BASE_URL}/asset_platforms`;
40
- },
41
- getSupportedVsCurrencies() {
42
- return `${this.BASE_URL}/simple/supported_vs_currencies`;
43
- },
44
- };
45
35
  /**
46
- * Finds the chain slug in the data array given a chainId.
36
+ * The maximum number of token addresses that should be sent to the Price API in
37
+ * a single request.
38
+ */
39
+ const TOKEN_PRICES_BATCH_SIZE = 100;
40
+ /**
41
+ * Uses the CryptoCompare API to fetch the exchange rate between one currency
42
+ * and another, i.e., the multiplier to apply the amount of one currency in
43
+ * order to convert it to another.
47
44
  *
48
- * @param chainId - The current chain ID.
49
- * @param data - A list platforms supported by the CoinGecko API.
50
- * @returns The CoinGecko slug for the given chain ID, or `null` if the slug was not found.
45
+ * @param args - The arguments to this function.
46
+ * @param args.from - The currency to convert from.
47
+ * @param args.to - The currency to convert to.
48
+ * @returns The exchange rate between `fromCurrency` to `toCurrency` if one
49
+ * exists, or null if one does not.
51
50
  */
52
- function findChainSlug(chainId, data) {
53
- var _a;
54
- if (!data) {
55
- return null;
56
- }
57
- const chain = (_a = data.find(({ chain_identifier }) => chain_identifier !== null && (0, controller_utils_1.toHex)(chain_identifier) === chainId)) !== null && _a !== void 0 ? _a : null;
58
- return (chain === null || chain === void 0 ? void 0 : chain.id) || null;
51
+ function getCurrencyConversionRate({ from, to, }) {
52
+ return __awaiter(this, void 0, void 0, function* () {
53
+ const includeUSDRate = false;
54
+ try {
55
+ const result = yield (0, crypto_compare_1.fetchExchangeRate)(to, from, includeUSDRate);
56
+ return result.conversionRate;
57
+ }
58
+ catch (error) {
59
+ if (error instanceof Error &&
60
+ error.message.includes('market does not exist for this coin pair')) {
61
+ return null;
62
+ }
63
+ throw error;
64
+ }
65
+ });
59
66
  }
60
67
  /**
61
68
  * Controller that passively polls on a set interval for token-to-fiat exchange rates
62
69
  * for tokens stored in the TokensController
63
70
  */
64
- class TokenRatesController extends polling_controller_1.PollingControllerV1 {
71
+ class TokenRatesController extends polling_controller_1.StaticIntervalPollingControllerV1 {
65
72
  /**
66
73
  * Creates a TokenRatesController instance.
67
74
  *
@@ -75,22 +82,16 @@ class TokenRatesController extends polling_controller_1.PollingControllerV1 {
75
82
  * @param options.onPreferencesStateChange - Allows subscribing to preference controller state changes.
76
83
  * @param options.onTokensStateChange - Allows subscribing to token controller state changes.
77
84
  * @param options.onNetworkStateChange - Allows subscribing to network state changes.
85
+ * @param options.tokenPricesService - An object in charge of retrieving token prices.
78
86
  * @param config - Initial options used to configure this controller.
79
87
  * @param state - Initial state to set on this controller.
80
88
  */
81
- constructor({ interval = 3 * 60 * 1000, threshold = 6 * 60 * 60 * 1000, getNetworkClientById, chainId: initialChainId, ticker: initialTicker, selectedAddress: initialSelectedAddress, onPreferencesStateChange, onTokensStateChange, onNetworkStateChange, }, config, state) {
89
+ constructor({ interval = 3 * 60 * 1000, threshold = 6 * 60 * 60 * 1000, getNetworkClientById, chainId: initialChainId, ticker: initialTicker, selectedAddress: initialSelectedAddress, onPreferencesStateChange, onTokensStateChange, onNetworkStateChange, tokenPricesService, }, config, state) {
82
90
  super(config, state);
83
91
  _TokenRatesController_instances.add(this);
84
- this.tokenList = [];
85
- this.supportedChains = {
86
- timestamp: 0,
87
- data: null,
88
- };
89
- this.supportedVsCurrencies = {
90
- timestamp: 0,
91
- data: [],
92
- };
93
92
  _TokenRatesController_pollState.set(this, PollState.Inactive);
93
+ _TokenRatesController_tokenPricesService.set(this, void 0);
94
+ _TokenRatesController_inProcessExchangeRateUpdates.set(this, {});
94
95
  /**
95
96
  * Name of this controller used during composition
96
97
  */
@@ -112,28 +113,25 @@ class TokenRatesController extends polling_controller_1.PollingControllerV1 {
112
113
  this.initialize();
113
114
  this.setIntervalLength(interval);
114
115
  this.getNetworkClientById = getNetworkClientById;
116
+ __classPrivateFieldSet(this, _TokenRatesController_tokenPricesService, tokenPricesService, "f");
115
117
  if (config === null || config === void 0 ? void 0 : config.disabled) {
116
118
  this.configure({ disabled: true }, false, false);
117
119
  }
118
- __classPrivateFieldGet(this, _TokenRatesController_instances, "m", _TokenRatesController_updateTokenList).call(this);
119
120
  onPreferencesStateChange(({ selectedAddress }) => __awaiter(this, void 0, void 0, function* () {
120
121
  if (this.config.selectedAddress !== selectedAddress) {
121
122
  this.configure({ selectedAddress });
122
- __classPrivateFieldGet(this, _TokenRatesController_instances, "m", _TokenRatesController_updateTokenList).call(this);
123
123
  if (__classPrivateFieldGet(this, _TokenRatesController_pollState, "f") === PollState.Active) {
124
124
  yield this.updateExchangeRates();
125
125
  }
126
126
  }
127
127
  }));
128
128
  onTokensStateChange(({ allTokens, allDetectedTokens }) => __awaiter(this, void 0, void 0, function* () {
129
- // These two state properties are assumed to be immutable
130
- if (this.config.allTokens !== allTokens ||
131
- this.config.allDetectedTokens !== allDetectedTokens) {
132
- this.configure({ allTokens, allDetectedTokens });
133
- __classPrivateFieldGet(this, _TokenRatesController_instances, "m", _TokenRatesController_updateTokenList).call(this);
134
- if (__classPrivateFieldGet(this, _TokenRatesController_pollState, "f") === PollState.Active) {
135
- yield this.updateExchangeRates();
136
- }
129
+ const previousTokenAddresses = __classPrivateFieldGet(this, _TokenRatesController_instances, "m", _TokenRatesController_getTokenAddresses).call(this, this.config.chainId);
130
+ this.configure({ allTokens, allDetectedTokens });
131
+ const newTokenAddresses = __classPrivateFieldGet(this, _TokenRatesController_instances, "m", _TokenRatesController_getTokenAddresses).call(this, this.config.chainId);
132
+ if (!(0, lodash_1.isEqual)(previousTokenAddresses, newTokenAddresses) &&
133
+ __classPrivateFieldGet(this, _TokenRatesController_pollState, "f") === PollState.Active) {
134
+ yield this.updateExchangeRates();
137
135
  }
138
136
  }));
139
137
  onNetworkStateChange(({ providerConfig }) => __awaiter(this, void 0, void 0, function* () {
@@ -142,7 +140,6 @@ class TokenRatesController extends polling_controller_1.PollingControllerV1 {
142
140
  this.config.nativeCurrency !== ticker) {
143
141
  this.update({ contractExchangeRates: {} });
144
142
  this.configure({ chainId, nativeCurrency: ticker });
145
- __classPrivateFieldGet(this, _TokenRatesController_instances, "m", _TokenRatesController_updateTokenList).call(this);
146
143
  if (__classPrivateFieldGet(this, _TokenRatesController_pollState, "f") === PollState.Active) {
147
144
  yield this.updateExchangeRates();
148
145
  }
@@ -166,84 +163,15 @@ class TokenRatesController extends polling_controller_1.PollingControllerV1 {
166
163
  __classPrivateFieldGet(this, _TokenRatesController_instances, "m", _TokenRatesController_stopPoll).call(this);
167
164
  __classPrivateFieldSet(this, _TokenRatesController_pollState, PollState.Inactive, "f");
168
165
  }
169
- /**
170
- * Fetches a pairs of token address and native currency.
171
- *
172
- * @param chainSlug - The chain string identifier.
173
- * @param vsCurrency - The currency used to generate pairs against the tokens.
174
- * @param tokenAddresses - The addresses for the token contracts.
175
- * @returns The exchange rates for the given pairs.
176
- */
177
- fetchExchangeRate(chainSlug, vsCurrency, tokenAddresses = this.tokenList.map((token) => token.address)) {
178
- return __awaiter(this, void 0, void 0, function* () {
179
- const tokenPairs = tokenAddresses.join(',');
180
- const query = `contract_addresses=${tokenPairs}&vs_currencies=${vsCurrency.toLowerCase()}`;
181
- return (0, controller_utils_1.handleFetch)(CoinGeckoApi.getTokenPriceURL(chainSlug, query));
182
- });
183
- }
184
- /**
185
- * Checks if the current native currency is a supported vs currency to use
186
- * to query for token exchange rates.
187
- *
188
- * @param nativeCurrency - The native currency to check.
189
- * @returns A boolean indicating whether it's a supported vsCurrency.
190
- */
191
- checkIsSupportedVsCurrency(nativeCurrency) {
192
- return __awaiter(this, void 0, void 0, function* () {
193
- const { threshold } = this.config;
194
- const { timestamp, data } = this.supportedVsCurrencies;
195
- const now = Date.now();
196
- if (now - timestamp > threshold) {
197
- const currencies = yield (0, controller_utils_1.handleFetch)(CoinGeckoApi.getSupportedVsCurrencies());
198
- this.supportedVsCurrencies = {
199
- data: currencies,
200
- timestamp: Date.now(),
201
- };
202
- return currencies.includes(nativeCurrency.toLowerCase());
203
- }
204
- return data.includes(nativeCurrency.toLowerCase());
205
- });
206
- }
207
- /**
208
- * Gets the slug from cached supported platforms CoinGecko API response for the chain ID.
209
- * If cached supported platforms response is stale, fetches and updates it.
210
- *
211
- * @param chainId - The chain ID.
212
- * @returns The CoinGecko slug for the chain ID.
213
- */
214
- getChainSlug(chainId = this.config.chainId) {
215
- return __awaiter(this, void 0, void 0, function* () {
216
- const { threshold } = this.config;
217
- const { data, timestamp } = this.supportedChains;
218
- const now = Date.now();
219
- if (now - timestamp > threshold) {
220
- const platforms = yield (0, controller_utils_1.handleFetch)(CoinGeckoApi.getPlatformsURL());
221
- this.supportedChains = {
222
- data: platforms,
223
- timestamp: Date.now(),
224
- };
225
- return findChainSlug(chainId, platforms);
226
- }
227
- return findChainSlug(chainId, data);
228
- });
229
- }
230
166
  /**
231
167
  * Updates exchange rates for all tokens.
232
168
  */
233
169
  updateExchangeRates() {
234
170
  return __awaiter(this, void 0, void 0, function* () {
235
- if (this.tokenList.length === 0 || this.disabled) {
236
- return;
237
- }
238
171
  const { chainId, nativeCurrency } = this.config;
239
- const tokenAddresses = this.tokenList.map((token) => token.address);
240
172
  yield this.updateExchangeRatesByChainId({
241
173
  chainId,
242
174
  nativeCurrency,
243
- tokenAddresses,
244
- });
245
- this.update({
246
- contractExchangeRates: this.state.contractExchangeRatesByChainId[chainId][nativeCurrency],
247
175
  });
248
176
  });
249
177
  }
@@ -253,117 +181,80 @@ class TokenRatesController extends polling_controller_1.PollingControllerV1 {
253
181
  * @param options - The options to fetch exchange rates.
254
182
  * @param options.chainId - The chain ID.
255
183
  * @param options.nativeCurrency - The ticker for the chain.
256
- * @param options.tokenAddresses - The addresses for the token contracts.
257
184
  */
258
- updateExchangeRatesByChainId({ chainId, nativeCurrency, tokenAddresses, }) {
259
- var _a, _b;
185
+ updateExchangeRatesByChainId({ chainId, nativeCurrency, }) {
186
+ var _a;
260
187
  return __awaiter(this, void 0, void 0, function* () {
261
- if (!tokenAddresses.length) {
188
+ if (this.disabled) {
262
189
  return;
263
190
  }
264
- const chainSlug = yield this.getChainSlug(chainId);
265
- let newContractExchangeRates = {};
266
- if (!chainSlug) {
267
- tokenAddresses.forEach((tokenAddress) => {
268
- const address = (0, controller_utils_1.toChecksumHexAddress)(tokenAddress);
269
- newContractExchangeRates[address] = undefined;
270
- });
191
+ const tokenAddresses = __classPrivateFieldGet(this, _TokenRatesController_instances, "m", _TokenRatesController_getTokenAddresses).call(this, chainId);
192
+ if (tokenAddresses.length === 0) {
193
+ return;
271
194
  }
272
- else {
273
- newContractExchangeRates = yield this.fetchAndMapExchangeRates(nativeCurrency, chainSlug, tokenAddresses);
195
+ const updateKey = `${chainId}:${nativeCurrency}`;
196
+ if (updateKey in __classPrivateFieldGet(this, _TokenRatesController_inProcessExchangeRateUpdates, "f")) {
197
+ // This prevents redundant updates
198
+ // This promise is resolved after the in-progress update has finished,
199
+ // and state has been updated.
200
+ yield __classPrivateFieldGet(this, _TokenRatesController_inProcessExchangeRateUpdates, "f")[updateKey];
201
+ return;
274
202
  }
275
- const existingContractExchangeRatesForChainId = (_a = this.state.contractExchangeRatesByChainId[chainId]) !== null && _a !== void 0 ? _a : {};
276
- this.update({
277
- contractExchangeRatesByChainId: Object.assign(Object.assign({}, this.state.contractExchangeRatesByChainId), { [chainId]: Object.assign(Object.assign({}, existingContractExchangeRatesForChainId), { [nativeCurrency]: Object.assign(Object.assign({}, ((_b = existingContractExchangeRatesForChainId[nativeCurrency]) !== null && _b !== void 0 ? _b : {})), newContractExchangeRates) }) }),
278
- });
279
- });
280
- }
281
- /**
282
- * Checks if the active network's native currency is supported by the coingecko API.
283
- * If supported, it fetches and maps contractExchange rates to a format to be consumed by the UI.
284
- * If not supported, it fetches contractExchange rates and maps them from token/fallback-currency
285
- * to token/nativeCurrency.
286
- *
287
- * @param nativeCurrency - The native currency ticker against which to fetch exchange rates
288
- * @param chainSlug - The unique slug used to id the chain by the coingecko api
289
- * should be used to query token exchange rates.
290
- * @param tokenAddresses - The addresses for the token contracts against which to fetch exchange rates.
291
- * @returns An object with conversion rates for each token
292
- * related to the network's native currency.
293
- */
294
- fetchAndMapExchangeRates(nativeCurrency, chainSlug, tokenAddresses = this.tokenList.map((token) => token.address)) {
295
- return __awaiter(this, void 0, void 0, function* () {
296
- const contractExchangeRates = {};
297
- // check if native currency is supported as a vs_currency by the API
298
- const nativeCurrencySupported = yield this.checkIsSupportedVsCurrency(nativeCurrency);
299
- if (nativeCurrencySupported) {
300
- // If it is we can do a simple fetch against the CoinGecko API
301
- const prices = yield this.fetchExchangeRate(chainSlug, nativeCurrency, tokenAddresses);
302
- tokenAddresses.forEach((tokenAddress) => {
303
- const price = prices[tokenAddress.toLowerCase()];
304
- contractExchangeRates[(0, controller_utils_1.toChecksumHexAddress)(tokenAddress)] = price
305
- ? price[nativeCurrency.toLowerCase()]
306
- : 0;
203
+ const { promise: inProgressUpdate, resolve: updateSucceeded, reject: updateFailed, } = createDeferredPromise({ suppressUnhandledRejection: true });
204
+ __classPrivateFieldGet(this, _TokenRatesController_inProcessExchangeRateUpdates, "f")[updateKey] = inProgressUpdate;
205
+ try {
206
+ const newContractExchangeRates = yield __classPrivateFieldGet(this, _TokenRatesController_instances, "m", _TokenRatesController_fetchAndMapExchangeRates).call(this, {
207
+ tokenAddresses,
208
+ chainId,
209
+ nativeCurrency,
307
210
  });
211
+ const existingContractExchangeRates = this.state.contractExchangeRates;
212
+ const updatedContractExchangeRates = chainId === this.config.chainId &&
213
+ nativeCurrency === this.config.nativeCurrency
214
+ ? newContractExchangeRates
215
+ : existingContractExchangeRates;
216
+ const existingContractExchangeRatesForChainId = (_a = this.state.contractExchangeRatesByChainId[chainId]) !== null && _a !== void 0 ? _a : {};
217
+ const updatedContractExchangeRatesForChainId = Object.assign(Object.assign({}, this.state.contractExchangeRatesByChainId), { [chainId]: Object.assign(Object.assign({}, existingContractExchangeRatesForChainId), { [nativeCurrency]: Object.assign(Object.assign({}, existingContractExchangeRatesForChainId[nativeCurrency]), newContractExchangeRates) }) });
218
+ this.update({
219
+ contractExchangeRates: updatedContractExchangeRates,
220
+ contractExchangeRatesByChainId: updatedContractExchangeRatesForChainId,
221
+ });
222
+ updateSucceeded();
308
223
  }
309
- else {
310
- // if native currency is not supported we need to use a fallback vsCurrency, get the exchange rates
311
- // in token/fallback-currency format and convert them to expected token/nativeCurrency format.
312
- let tokenExchangeRates;
313
- let vsCurrencyToNativeCurrencyConversionRate = 0;
314
- try {
315
- [
316
- tokenExchangeRates,
317
- { conversionRate: vsCurrencyToNativeCurrencyConversionRate },
318
- ] = yield Promise.all([
319
- this.fetchExchangeRate(chainSlug, controller_utils_1.FALL_BACK_VS_CURRENCY, tokenAddresses),
320
- (0, crypto_compare_1.fetchExchangeRate)(nativeCurrency, controller_utils_1.FALL_BACK_VS_CURRENCY, false),
321
- ]);
322
- }
323
- catch (error) {
324
- if (error instanceof Error &&
325
- error.message.includes('market does not exist for this coin pair')) {
326
- return {};
327
- }
328
- throw error;
329
- }
330
- for (const [tokenAddress, conversion] of Object.entries(tokenExchangeRates)) {
331
- const tokenToVsCurrencyConversionRate = conversion[controller_utils_1.FALL_BACK_VS_CURRENCY.toLowerCase()];
332
- contractExchangeRates[(0, controller_utils_1.toChecksumHexAddress)(tokenAddress)] =
333
- tokenToVsCurrencyConversionRate *
334
- vsCurrencyToNativeCurrencyConversionRate;
335
- }
224
+ catch (error) {
225
+ updateFailed(error);
226
+ throw error;
227
+ }
228
+ finally {
229
+ delete __classPrivateFieldGet(this, _TokenRatesController_inProcessExchangeRateUpdates, "f")[updateKey];
336
230
  }
337
- return contractExchangeRates;
338
231
  });
339
232
  }
340
233
  /**
341
- * Updates token rates for the given networkClientId and contract addresses
234
+ * Updates token rates for the given networkClientId
342
235
  *
343
236
  * @param networkClientId - The network client ID used to get a ticker value.
344
- * @param options - The polling options.
345
- * @param options.tokenAddresses - The addresses for the token contracts.
346
237
  * @returns The controller state.
347
238
  */
348
- _executePoll(networkClientId, options) {
239
+ _executePoll(networkClientId) {
349
240
  return __awaiter(this, void 0, void 0, function* () {
350
241
  const networkClient = this.getNetworkClientById(networkClientId);
351
242
  yield this.updateExchangeRatesByChainId({
352
243
  chainId: networkClient.configuration.chainId,
353
244
  nativeCurrency: networkClient.configuration.ticker,
354
- tokenAddresses: options.tokenAddresses,
355
245
  });
356
246
  });
357
247
  }
358
248
  }
359
249
  exports.TokenRatesController = TokenRatesController;
360
- _TokenRatesController_pollState = new WeakMap(), _TokenRatesController_instances = new WeakSet(), _TokenRatesController_updateTokenList = function _TokenRatesController_updateTokenList() {
250
+ _TokenRatesController_pollState = new WeakMap(), _TokenRatesController_tokenPricesService = new WeakMap(), _TokenRatesController_inProcessExchangeRateUpdates = new WeakMap(), _TokenRatesController_instances = new WeakSet(), _TokenRatesController_getTokenAddresses = function _TokenRatesController_getTokenAddresses(chainId) {
361
251
  var _a, _b;
362
252
  const { allTokens, allDetectedTokens } = this.config;
363
- const tokens = ((_a = allTokens[this.config.chainId]) === null || _a === void 0 ? void 0 : _a[this.config.selectedAddress]) || [];
364
- const detectedTokens = ((_b = allDetectedTokens[this.config.chainId]) === null || _b === void 0 ? void 0 : _b[this.config.selectedAddress]) ||
365
- [];
366
- this.tokenList = [...tokens, ...detectedTokens];
253
+ const tokens = ((_a = allTokens[chainId]) === null || _a === void 0 ? void 0 : _a[this.config.selectedAddress]) || [];
254
+ const detectedTokens = ((_b = allDetectedTokens[chainId]) === null || _b === void 0 ? void 0 : _b[this.config.selectedAddress]) || [];
255
+ return [
256
+ ...new Set([...tokens, ...detectedTokens].map((token) => (0, controller_utils_1.toHex)((0, controller_utils_1.toChecksumHexAddress)(token.address)))),
257
+ ].sort();
367
258
  }, _TokenRatesController_stopPoll = function _TokenRatesController_stopPoll() {
368
259
  if (this.handle) {
369
260
  clearTimeout(this.handle);
@@ -377,6 +268,92 @@ _TokenRatesController_pollState = new WeakMap(), _TokenRatesController_instances
377
268
  __classPrivateFieldGet(this, _TokenRatesController_instances, "m", _TokenRatesController_poll).call(this);
378
269
  }, this.config.interval);
379
270
  });
271
+ }, _TokenRatesController_fetchAndMapExchangeRates = function _TokenRatesController_fetchAndMapExchangeRates({ tokenAddresses, chainId, nativeCurrency, }) {
272
+ return __awaiter(this, void 0, void 0, function* () {
273
+ if (!__classPrivateFieldGet(this, _TokenRatesController_tokenPricesService, "f").validateChainIdSupported(chainId)) {
274
+ return tokenAddresses.reduce((obj, tokenAddress) => {
275
+ return Object.assign(Object.assign({}, obj), { [tokenAddress]: undefined });
276
+ }, {});
277
+ }
278
+ if (__classPrivateFieldGet(this, _TokenRatesController_tokenPricesService, "f").validateCurrencySupported(nativeCurrency)) {
279
+ return yield __classPrivateFieldGet(this, _TokenRatesController_instances, "m", _TokenRatesController_fetchAndMapExchangeRatesForSupportedNativeCurrency).call(this, {
280
+ tokenAddresses,
281
+ chainId,
282
+ nativeCurrency,
283
+ });
284
+ }
285
+ return yield __classPrivateFieldGet(this, _TokenRatesController_instances, "m", _TokenRatesController_fetchAndMapExchangeRatesForUnsupportedNativeCurrency).call(this, {
286
+ tokenAddresses,
287
+ nativeCurrency,
288
+ });
289
+ });
290
+ }, _TokenRatesController_fetchAndMapExchangeRatesForSupportedNativeCurrency = function _TokenRatesController_fetchAndMapExchangeRatesForSupportedNativeCurrency({ tokenAddresses, chainId, nativeCurrency, }) {
291
+ return __awaiter(this, void 0, void 0, function* () {
292
+ const tokenPricesByTokenAddress = yield (0, assetsUtil_1.reduceInBatchesSerially)({
293
+ values: tokenAddresses,
294
+ batchSize: TOKEN_PRICES_BATCH_SIZE,
295
+ eachBatch: (allTokenPricesByTokenAddress, batch) => __awaiter(this, void 0, void 0, function* () {
296
+ const tokenPricesByTokenAddressForBatch = yield __classPrivateFieldGet(this, _TokenRatesController_tokenPricesService, "f").fetchTokenPrices({
297
+ tokenAddresses: batch,
298
+ chainId,
299
+ currency: nativeCurrency,
300
+ });
301
+ return Object.assign(Object.assign({}, allTokenPricesByTokenAddress), tokenPricesByTokenAddressForBatch);
302
+ }),
303
+ initialResult: {},
304
+ });
305
+ return Object.entries(tokenPricesByTokenAddress).reduce((obj, [tokenAddress, tokenPrice]) => {
306
+ return Object.assign(Object.assign({}, obj), { [tokenAddress]: tokenPrice.value });
307
+ }, {});
308
+ });
309
+ }, _TokenRatesController_fetchAndMapExchangeRatesForUnsupportedNativeCurrency = function _TokenRatesController_fetchAndMapExchangeRatesForUnsupportedNativeCurrency({ tokenAddresses, nativeCurrency, }) {
310
+ return __awaiter(this, void 0, void 0, function* () {
311
+ const [contractExchangeRates, fallbackCurrencyToNativeCurrencyConversionRate,] = yield Promise.all([
312
+ __classPrivateFieldGet(this, _TokenRatesController_instances, "m", _TokenRatesController_fetchAndMapExchangeRatesForSupportedNativeCurrency).call(this, {
313
+ tokenAddresses,
314
+ chainId: this.config.chainId,
315
+ nativeCurrency: controller_utils_1.FALL_BACK_VS_CURRENCY,
316
+ }),
317
+ getCurrencyConversionRate({
318
+ from: controller_utils_1.FALL_BACK_VS_CURRENCY,
319
+ to: nativeCurrency,
320
+ }),
321
+ ]);
322
+ if (fallbackCurrencyToNativeCurrencyConversionRate === null) {
323
+ return {};
324
+ }
325
+ return Object.entries(contractExchangeRates).reduce((obj, [tokenAddress, tokenValue]) => {
326
+ return Object.assign(Object.assign({}, obj), { [tokenAddress]: tokenValue
327
+ ? tokenValue * fallbackCurrencyToNativeCurrencyConversionRate
328
+ : undefined });
329
+ }, {});
330
+ });
380
331
  };
332
+ /**
333
+ * Create a defered Promise.
334
+ *
335
+ * TODO: Migrate this to utils
336
+ *
337
+ * @param args - The arguments.
338
+ * @param args.suppressUnhandledRejection - This option adds an empty error handler
339
+ * to the Promise to suppress the UnhandledPromiseRejection error. This can be
340
+ * useful if the deferred Promise is sometimes intentionally not used.
341
+ * @returns A deferred Promise.
342
+ */
343
+ function createDeferredPromise({ suppressUnhandledRejection = false, }) {
344
+ let resolve;
345
+ let reject;
346
+ const promise = new Promise((innerResolve, innerReject) => {
347
+ resolve = innerResolve;
348
+ reject = innerReject;
349
+ });
350
+ if (suppressUnhandledRejection) {
351
+ promise.catch((_error) => {
352
+ // This handler is used to suppress the UnhandledPromiseRejection error
353
+ });
354
+ }
355
+ // @ts-expect-error We know that these are assigned, but TypeScript doesn't
356
+ return { promise, resolve, reject };
357
+ }
381
358
  exports.default = TokenRatesController;
382
359
  //# sourceMappingURL=TokenRatesController.js.map