@toruslabs/ethereum-controllers 5.5.3 → 5.5.5

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 (50) hide show
  1. package/dist/ethereumControllers.cjs.js +3 -12
  2. package/dist/ethereumControllers.esm.js +2 -11
  3. package/dist/ethereumControllers.umd.min.js +1 -2
  4. package/dist/ethereumControllers.umd.min.js.LICENSE.txt +0 -2
  5. package/dist/types/Account/AccountTrackerController.d.ts +1 -1
  6. package/package.json +14 -15
  7. package/dist/ethereumControllers.cjs.js.map +0 -1
  8. package/dist/ethereumControllers.esm.js.map +0 -1
  9. package/dist/ethereumControllers.umd.min.js.map +0 -1
  10. package/src/Account/AccountTrackerController.ts +0 -172
  11. package/src/Block/PollingBlockTracker.ts +0 -89
  12. package/src/Currency/CurrencyController.ts +0 -117
  13. package/src/Gas/GasFeeController.ts +0 -261
  14. package/src/Gas/IGasFeeController.ts +0 -56
  15. package/src/Gas/gasUtil.ts +0 -163
  16. package/src/Keyring/KeyringController.ts +0 -117
  17. package/src/Message/AbstractMessageController.ts +0 -136
  18. package/src/Message/AddChainController.ts +0 -73
  19. package/src/Message/DecryptMessageController.ts +0 -76
  20. package/src/Message/EncryptionPublicKeyController.ts +0 -75
  21. package/src/Message/MessageController.ts +0 -74
  22. package/src/Message/PersonalMessageController.ts +0 -74
  23. package/src/Message/SwitchChainController.ts +0 -74
  24. package/src/Message/TypedMessageController.ts +0 -109
  25. package/src/Message/utils.ts +0 -155
  26. package/src/Network/NetworkController.ts +0 -184
  27. package/src/Network/createEthereumMiddleware.ts +0 -475
  28. package/src/Network/createJsonRpcClient.ts +0 -63
  29. package/src/Nfts/INftsController.ts +0 -13
  30. package/src/Nfts/NftHandler.ts +0 -191
  31. package/src/Nfts/NftsController.ts +0 -216
  32. package/src/Preferences/PreferencesController.ts +0 -473
  33. package/src/Tokens/ITokensController.ts +0 -13
  34. package/src/Tokens/TokenHandler.ts +0 -60
  35. package/src/Tokens/TokenRatesController.ts +0 -134
  36. package/src/Tokens/TokensController.ts +0 -273
  37. package/src/Transaction/NonceTracker.ts +0 -152
  38. package/src/Transaction/PendingTransactionTracker.ts +0 -235
  39. package/src/Transaction/TransactionController.ts +0 -558
  40. package/src/Transaction/TransactionGasUtil.ts +0 -74
  41. package/src/Transaction/TransactionStateHistoryHelper.ts +0 -41
  42. package/src/Transaction/TransactionStateManager.ts +0 -315
  43. package/src/Transaction/TransactionUtils.ts +0 -333
  44. package/src/index.ts +0 -49
  45. package/src/utils/abis.ts +0 -677
  46. package/src/utils/constants.ts +0 -438
  47. package/src/utils/contractAddresses.ts +0 -19
  48. package/src/utils/conversionUtils.ts +0 -269
  49. package/src/utils/helpers.ts +0 -245
  50. package/src/utils/interfaces.ts +0 -519
@@ -1,473 +0,0 @@
1
- import { stripHexPrefix } from "@ethereumjs/util";
2
- import {
3
- ACCOUNT_TYPE,
4
- BasePreferencesController,
5
- CustomNft,
6
- CustomToken,
7
- InitPreferencesParams,
8
- IPreferencesController,
9
- PreferencesConfig,
10
- PreferencesState,
11
- TransactionStatus,
12
- } from "@toruslabs/base-controllers";
13
- import { get, patch, post, remove } from "@toruslabs/http-helpers";
14
- import { SafeEventEmitterProvider } from "@toruslabs/openlogin-jrpc";
15
- import { Mutex } from "async-mutex";
16
- import log from "loglevel";
17
-
18
- import KeyringController from "../Keyring/KeyringController";
19
- import NetworkController from "../Network/NetworkController";
20
- import { ETHERSCAN_SUPPORTED_CHAINS, SUPPORTED_NETWORKS } from "../utils/constants";
21
- import { addEtherscanTransactions, formatDate, formatPastTx, formatTime, getEthTxStatus } from "../utils/helpers";
22
- import type {
23
- AddChainMessageParams,
24
- CustomNetworkPayload,
25
- CustomNetworks,
26
- CustomNftInfo,
27
- CustomTokenInfo,
28
- EthereumProviderConfig,
29
- EthereumUser,
30
- EtherscanTransaction,
31
- ExtendedAddressPreferences,
32
- FetchedTransaction,
33
- FormattedTransactionActivity,
34
- TransactionPayload,
35
- } from "../utils/interfaces";
36
-
37
- export interface IPreferencesControllerOptions {
38
- config?: Partial<PreferencesConfig> & Pick<PreferencesConfig, "api" | "commonApiHost">;
39
- state?: Partial<PreferencesState<ExtendedAddressPreferences>>;
40
- provider: SafeEventEmitterProvider;
41
- signAuthMessage?: KeyringController["signAuthMessage"];
42
- getProviderConfig?: NetworkController["getProviderConfig"];
43
- setProviderConfig?: NetworkController["setProviderConfig"];
44
- validateSignMessage: (message: string) => Promise<void>;
45
- }
46
-
47
- export default class PreferencesController
48
- extends BasePreferencesController<ExtendedAddressPreferences, PreferencesConfig, PreferencesState<ExtendedAddressPreferences>>
49
- implements IPreferencesController<ExtendedAddressPreferences, PreferencesConfig, PreferencesState<ExtendedAddressPreferences>>
50
- {
51
- private _handle?: number;
52
-
53
- private _mutex: Mutex = new Mutex();
54
-
55
- private getProviderConfig: NetworkController["getProviderConfig"];
56
-
57
- private setProviderConfig: NetworkController["setProviderConfig"];
58
-
59
- private provider: SafeEventEmitterProvider;
60
-
61
- constructor({
62
- config,
63
- state,
64
- provider,
65
- signAuthMessage,
66
- getProviderConfig,
67
- setProviderConfig,
68
- validateSignMessage,
69
- }: IPreferencesControllerOptions) {
70
- super({
71
- config,
72
- state,
73
- defaultPreferences: { formattedPastTransactions: [], fetchedPastTx: [], paymentTx: [], etherscanTransactions: [] },
74
- signAuthMessage,
75
- validateSignMessage,
76
- });
77
- this.provider = provider;
78
- this.getProviderConfig = getProviderConfig;
79
- this.setProviderConfig = setProviderConfig;
80
- }
81
-
82
- public async poll(interval?: number): Promise<void> {
83
- const releaseLock = await this._mutex.acquire();
84
- if (interval) this.configure({ pollInterval: interval });
85
- if (this._handle) window.clearTimeout(this._handle);
86
- // call here
87
- const storeSelectedAddress = this.state.selectedAddress;
88
- if (!storeSelectedAddress) return;
89
- if (!this.getAddressState(storeSelectedAddress)?.jwtToken) return;
90
- // This should never throw
91
- await this.sync(storeSelectedAddress);
92
- releaseLock();
93
- this._handle = window.setTimeout(() => {
94
- this.poll(this.config.pollInterval);
95
- }, this.config.pollInterval);
96
- }
97
-
98
- public async initPreferences(params: InitPreferencesParams): Promise<void> {
99
- const { address, jwtToken, calledFromEmbed, userInfo, rehydrate, locale = "en", type, signatures, web3AuthClientId, web3AuthNetwork } = params;
100
- await super.init({ address, userInfo, idToken: jwtToken, type, metadata: { email: userInfo.email, signatures, network: web3AuthNetwork } });
101
- const { aggregateVerifier, verifier, verifierId } = userInfo || {};
102
- const userExists = await this.sync(address);
103
- if (!userExists) {
104
- const accountState = this.getAddressState(address);
105
- await this.createUser({
106
- selectedCurrency: accountState.selectedCurrency,
107
- theme: accountState.theme,
108
- verifier: aggregateVerifier || verifier,
109
- verifierId,
110
- locale,
111
- address,
112
- type,
113
- web3AuthNetwork,
114
- });
115
- }
116
- await this.storeUserLogin({
117
- verifier: aggregateVerifier || verifier,
118
- verifierId,
119
- options: { calledFromEmbed, rehydrate },
120
- address,
121
- web3AuthClientId,
122
- web3AuthNetwork,
123
- });
124
- }
125
-
126
- public getSelectedAddress(): string {
127
- return this.state.selectedAddress;
128
- }
129
-
130
- async sync(address: string): Promise<boolean> {
131
- try {
132
- const user = await this.getUser<EthereumUser>(address);
133
- if (user) {
134
- const {
135
- default_currency: defaultCurrency,
136
- contacts,
137
- theme,
138
- locale,
139
- public_address: userPublicAddress,
140
- default_public_address: defaultPublicAddress,
141
- customNetworks,
142
- customTokens,
143
- customNfts,
144
- account_type: accountType,
145
- } = user || {};
146
-
147
- // update latest data in state.
148
- this.updateState(
149
- {
150
- contacts,
151
- theme,
152
- selectedCurrency: defaultCurrency,
153
- locale,
154
- defaultPublicAddress: defaultPublicAddress || userPublicAddress,
155
- customTokens,
156
- customNfts,
157
- customNetworks,
158
- accountType: accountType as ACCOUNT_TYPE,
159
- },
160
- address
161
- );
162
- return true;
163
- }
164
- return false;
165
- } catch (error) {
166
- log.error(error);
167
- return false;
168
- } finally {
169
- Promise.all([
170
- this.getWalletOrders<FetchedTransaction>(address).catch((error) => {
171
- log.error("unable to fetch wallet orders", error);
172
- }),
173
- ])
174
- .then((data) => {
175
- const [walletTx] = data;
176
- // eslint-disable-next-line promise/always-return
177
- if (walletTx && walletTx.length > 0) {
178
- this.updateState({ fetchedPastTx: [...walletTx] }, address);
179
- this.calculatePastTx(walletTx, address);
180
- }
181
- })
182
- .catch((error) => log.error(error));
183
- }
184
- }
185
-
186
- public async patchNewTx(tx: TransactionPayload, address: string): Promise<void> {
187
- const formattedTx = formatPastTx(tx);
188
- const storePastTx = this.getAddressState(address)?.formattedPastTransactions || [];
189
- const duplicateIndex = storePastTx.findIndex((x) => x.transaction_hash === tx.transaction_hash && x.chainId === tx.chain_id);
190
- if (tx.status === TransactionStatus.submitted || tx.status === TransactionStatus.confirmed) {
191
- if (duplicateIndex === -1) {
192
- // No duplicate found
193
-
194
- const finalTx = this.cancelTxCalculate([...storePastTx, formattedTx]);
195
- tx.is_cancel = formattedTx.is_cancel;
196
- tx.to = tx.to?.toLowerCase();
197
- tx.from = tx.from.toLowerCase();
198
-
199
- this.updateState({ formattedPastTransactions: finalTx }, address);
200
- this.postPastTx<TransactionPayload>(tx, address);
201
- } else {
202
- // avoid overriding is_cancel
203
- formattedTx.is_cancel = storePastTx[duplicateIndex].is_cancel;
204
- storePastTx[duplicateIndex] = formattedTx;
205
- this.updateState({ formattedPastTransactions: this.cancelTxCalculate([...storePastTx]) }, address);
206
- }
207
- }
208
- }
209
-
210
- public recalculatePastTx(address?: string) {
211
- // This triggers store update which calculates past Tx status for that network
212
- const selectedAddress = address || this.state.selectedAddress;
213
- const state = this.getAddressState(selectedAddress);
214
- if (!state?.fetchedPastTx) return;
215
- this.calculatePastTx(state.fetchedPastTx, selectedAddress);
216
- }
217
-
218
- public async refetchEtherscanTx(address?: string) {
219
- const selectedAddress = address || this.state.selectedAddress;
220
- if (this.getAddressState(selectedAddress)?.jwtToken) {
221
- const { chainId } = this.getProviderConfig();
222
- if (ETHERSCAN_SUPPORTED_CHAINS.includes(chainId)) {
223
- const etherscanTxn = await this.fetchEtherscanTx<EtherscanTransaction>({
224
- selectedAddress,
225
- chainId: this.getProviderConfig().chainId,
226
- });
227
- const finalEthScanTxn = await addEtherscanTransactions(etherscanTxn, selectedAddress, this.provider, chainId);
228
- log.info("Formatted Etherscan Response", finalEthScanTxn);
229
- this.updateState({ etherscanTransactions: finalEthScanTxn });
230
- return etherscanTxn;
231
- }
232
- }
233
- }
234
-
235
- async fetchEtherscanTx<T>(parameters: { selectedAddress: string; chainId: string }): Promise<T[]> {
236
- try {
237
- const url = new URL(`${this.config.api}/etherscan`);
238
- url.searchParams.append("chainId", parameters.chainId);
239
- const response = await get<{ success: boolean; data: T[] }>(url.href, this.headers(parameters.selectedAddress));
240
- log.info("Etherscan Response API", response);
241
- return response.success ? response.data : [];
242
- } catch (error) {
243
- log.error("unable to fetch etherscan tx", error);
244
- return [];
245
- }
246
- }
247
-
248
- public async getEtherScanTokens(address: string, chainId: string): Promise<CustomTokenInfo[]> {
249
- const selectedAddress = address;
250
- const apiUrl = new URL(this.config.api);
251
- apiUrl.pathname = `/tokens`;
252
- apiUrl.searchParams.append("chainId", chainId);
253
- apiUrl.searchParams.append("address", selectedAddress);
254
- const result = await get<{ data: CustomTokenInfo[] }>(apiUrl.href, this.headers(this.state.selectedAddress));
255
- return result.data;
256
- }
257
-
258
- public async getSimpleHashNfts(address: string, chainId: string): Promise<CustomNftInfo[]> {
259
- const selectedAddress = address;
260
- const apiUrl = new URL(this.config.api);
261
- apiUrl.pathname = `/nfts`;
262
- apiUrl.searchParams.append("chainId", chainId);
263
- apiUrl.searchParams.append("address", selectedAddress);
264
- const result = await get<{ data: CustomNftInfo[] }>(apiUrl.href, this.headers(this.state.selectedAddress));
265
- return result.data;
266
- }
267
-
268
- public getCustomTokens(address?: string): CustomToken[] {
269
- return this.getAddressState(address)?.customTokens ?? [];
270
- }
271
-
272
- public getCustomNfts(address?: string): CustomNft[] {
273
- return this.getAddressState(address)?.customNfts ?? [];
274
- }
275
-
276
- public isChainIdSupported(address: string, chainId: string): boolean {
277
- const approveChainOptions = this.getChainOptions(address);
278
- const providerConfig = approveChainOptions.find((x) => stripHexPrefix(x.chainId) === chainId);
279
- return !!providerConfig;
280
- }
281
-
282
- public async addChain(network: AddChainMessageParams): Promise<void> {
283
- const approveChainOptions = this.getChainOptions();
284
- const providerConfig = approveChainOptions.find((x) => x.chainId === network.chainId);
285
-
286
- if (providerConfig) {
287
- throw new Error(`chainId ${network.chainId} already exists`);
288
- }
289
-
290
- const newNetwork: CustomNetworkPayload = {
291
- displayName: network.chainName,
292
- rpcTarget: network.rpcUrls[0],
293
- ticker: network.nativeCurrency.symbol,
294
- chainId: network.chainId,
295
- blockExplorerUrl: network.blockExplorerUrls[0],
296
- tickerName: network.nativeCurrency.name,
297
- logo: network.nativeCurrency.symbol,
298
- };
299
-
300
- const isSuccess = await this.addCustomNetwork({ network: newNetwork });
301
- if (!isSuccess) throw new Error("unable to add custom network");
302
- }
303
-
304
- switchChain(data: { chainId: string }) {
305
- const chainOptions = this.getChainOptions();
306
- const providerConfig = chainOptions.find((x) => x.chainId === data.chainId);
307
- if (providerConfig) {
308
- this.setProviderConfig(providerConfig);
309
- } else {
310
- throw new Error(`chainId ${data.chainId} is not supported`);
311
- }
312
- }
313
-
314
- // Custom Network methods
315
- public async addCustomNetwork({ network }: { network: CustomNetworkPayload }): Promise<number> {
316
- try {
317
- const apiUrl = new URL(this.config.api);
318
- apiUrl.pathname = `/customnetwork`;
319
- const { selectedAddress } = this.state;
320
- const payload: Partial<CustomNetworks> = {
321
- network_name: network.displayName,
322
- rpc_url: network.rpcTarget,
323
- chain_id: network.chainId,
324
- symbol: network.ticker,
325
- block_explorer_url: network.blockExplorerUrl || undefined,
326
- is_testnet: network.isTestnet || false,
327
- logo: network.logo,
328
- symbol_name: network.tickerName,
329
- };
330
- const res = await post<{ data: CustomNetworks }>(apiUrl.href, payload, this.headers(selectedAddress), { useAPIKey: true });
331
- await this.sync(selectedAddress);
332
-
333
- return res.data.id;
334
- } catch {
335
- log.error("error adding custom network");
336
- return null;
337
- }
338
- }
339
-
340
- async deleteCustomNetwork(id: number) {
341
- try {
342
- const { selectedAddress } = this.state;
343
- const apiUrl = new URL(this.config.api);
344
- apiUrl.pathname = `/customnetwork/${id}`;
345
- await remove(apiUrl.href, {}, this.headers(selectedAddress), { useAPIKey: true });
346
- await this.sync(selectedAddress);
347
- return true;
348
- } catch {
349
- log.error("error deleting custom network");
350
- return false;
351
- }
352
- }
353
-
354
- async editCustomNetwork({ network, id }: { network: CustomNetworkPayload; id: number | null }) {
355
- try {
356
- const { selectedAddress } = this.state;
357
- const apiUrl = new URL(this.config.api);
358
- apiUrl.pathname = `/customnetwork/${id}`;
359
-
360
- const payload: Partial<CustomNetworks> = {
361
- network_name: network.displayName,
362
- rpc_url: network.rpcTarget,
363
- chain_id: network.chainId,
364
- symbol: network.ticker || undefined,
365
- block_explorer_url: network.blockExplorerUrl || undefined,
366
- is_testnet: network.isTestnet || false,
367
- };
368
- await patch(apiUrl.href, payload, this.headers(selectedAddress), { useAPIKey: true });
369
- await this.sync(selectedAddress);
370
- return true;
371
- } catch {
372
- log.error("error editing custom network");
373
- return false;
374
- }
375
- }
376
-
377
- private getChainOptions(address: string = this.state.selectedAddress): EthereumProviderConfig[] {
378
- const { identities } = this.state;
379
- const customNetworks = identities[address]?.customNetworks ?? [];
380
-
381
- const custom: EthereumProviderConfig[] = Object.values(customNetworks).reduce((chains, network) => {
382
- const networkItem = {
383
- blockExplorerUrl: network.block_explorer_url,
384
- chainId: network.chain_id,
385
- displayName: network.network_name,
386
- logo: "eth.svg",
387
- rpcTarget: network.rpc_url,
388
- ticker: network.symbol,
389
- tickerName: network.symbol.toUpperCase(),
390
- isCustom: true,
391
- id: network.id,
392
- };
393
- if (Object.keys(SUPPORTED_NETWORKS).includes(networkItem.chainId)) return chains;
394
- chains.push(networkItem);
395
- return chains;
396
- }, []);
397
-
398
- const supported = Object.values(SUPPORTED_NETWORKS).reduce((chains, network) => {
399
- chains.push(network);
400
- return chains;
401
- }, []);
402
-
403
- return [...supported, ...custom];
404
- }
405
-
406
- private async calculatePastTx(txs: FetchedTransaction[], address: string) {
407
- const pastTx = [];
408
- const pendingTx = [];
409
- const lowerCaseSelectedAddress = address.toLowerCase();
410
- for (const x of txs) {
411
- if (
412
- x.chain_id === SUPPORTED_NETWORKS[this.getProviderConfig().chainId].chainId &&
413
- x.to &&
414
- x.from &&
415
- (lowerCaseSelectedAddress === x.from.toLowerCase() || lowerCaseSelectedAddress === x.to?.toLowerCase())
416
- ) {
417
- if (x.status !== "confirmed") {
418
- pendingTx.push(x);
419
- } else {
420
- const finalObject = formatPastTx(x, lowerCaseSelectedAddress);
421
- pastTx.push(finalObject);
422
- }
423
- }
424
- }
425
- const pendingTxPromises = pendingTx.map((x) => getEthTxStatus(x.transaction_hash, this.provider).catch((error) => log.error(error)));
426
- const resolvedTxStatuses = await Promise.all(pendingTxPromises);
427
- for (const [index, element] of pendingTx.entries()) {
428
- const finalObject = formatPastTx(element, lowerCaseSelectedAddress);
429
- finalObject.status = resolvedTxStatuses[index] || TransactionStatus.submitted;
430
- pastTx.push(finalObject);
431
- if (lowerCaseSelectedAddress === element.from.toLowerCase() && finalObject.status && finalObject.status !== element.status)
432
- this.patchPastTx({ id: element.id, status: finalObject.status }, address);
433
- }
434
-
435
- const finalTx = this.cancelTxCalculate(pastTx);
436
-
437
- this.updateState({ formattedPastTransactions: [...finalTx] }, address);
438
- }
439
-
440
- private cancelTxCalculate(pastTx: FormattedTransactionActivity[]) {
441
- const nonceMap: Record<string, FormattedTransactionActivity[]> = {};
442
- for (const x of pastTx) {
443
- if (!nonceMap[x.nonce]) nonceMap[x.nonce] = [x];
444
- else {
445
- nonceMap[x.nonce].push(x);
446
- }
447
- }
448
-
449
- for (const [, value] of Object.entries(nonceMap)) {
450
- // has duplicate
451
- if (value.length > 1) {
452
- // get latest and mark it as is_cancel
453
- const latestTxs = value.sort((a, b) => {
454
- const aDate = new Date(a.date).getTime();
455
- const bDate = new Date(b.date).getTime();
456
- return bDate - aDate;
457
- });
458
- const latestCancelTx = latestTxs[0];
459
- latestCancelTx.is_cancel = true;
460
- latestTxs.slice(1).forEach((x) => {
461
- x.hasCancel = true;
462
- x.status = latestCancelTx.status === "confirmed" ? TransactionStatus.cancelled : TransactionStatus.cancelling;
463
- x.cancelDateInitiated = `${formatTime(new Date(latestCancelTx.date).getTime())} - ${formatDate(latestCancelTx.date)}`;
464
- x.etherscanLink = latestCancelTx.etherscanLink;
465
- x.cancelGas = latestCancelTx.gas;
466
- x.cancelGasPrice = latestCancelTx.gasPrice;
467
- });
468
- }
469
- }
470
-
471
- return pastTx;
472
- }
473
- }
@@ -1,13 +0,0 @@
1
- import { BaseConfig, BaseState } from "@toruslabs/base-controllers";
2
-
3
- import { CustomTokenInfo } from "../utils/interfaces";
4
-
5
- export interface TokensControllerConfig extends BaseConfig {
6
- interval?: number;
7
- selectedAddress: string;
8
- chainId: string;
9
- }
10
-
11
- export interface TokensControllerState extends BaseState {
12
- tokens: Record<string, CustomTokenInfo[]>;
13
- }
@@ -1,60 +0,0 @@
1
- import BigNumber from "bignumber.js";
2
- import { BrowserProvider, Contract } from "ethers";
3
- import log from "loglevel";
4
-
5
- import { erc20Abi } from "../utils/abis";
6
-
7
- export interface ITokenOptions {
8
- address: string;
9
- symbol: string;
10
- decimals: number;
11
- name: string;
12
- provider: BrowserProvider;
13
- }
14
-
15
- export class TokenHandler {
16
- public address: string;
17
-
18
- public symbol: string;
19
-
20
- public decimals: number;
21
-
22
- public name: string;
23
-
24
- public contract: Contract;
25
-
26
- constructor({ address, symbol, decimals, name, provider }: ITokenOptions) {
27
- this.address = address;
28
-
29
- this.contract = new Contract(address, erc20Abi, provider);
30
- this.symbol = symbol;
31
- this.decimals = decimals;
32
- this.name = name;
33
- }
34
-
35
- public async getSymbol(): Promise<string> {
36
- if (!this.symbol || this.symbol === "ERC20") this.symbol = await this.contract.symbol();
37
- return this.symbol;
38
- }
39
-
40
- public async getDecimals(): Promise<number> {
41
- try {
42
- if (!this.decimals) this.decimals = await this.contract.decimals();
43
- return this.decimals;
44
- } catch (error) {
45
- log.warn(`Could not get decimals for token ${this.address}`, error);
46
- return 0;
47
- }
48
- }
49
-
50
- public async getName(): Promise<string> {
51
- if (!this.name) this.name = await this.contract.name();
52
- return this.name;
53
- }
54
-
55
- public async getUserBalance(userAddress: string) {
56
- if (!this.decimals) await this.getDecimals();
57
- const balance = await this.contract.balanceOf(userAddress);
58
- return new BigNumber(balance).toString(16);
59
- }
60
- }
@@ -1,134 +0,0 @@
1
- import { BaseConfig, BaseController, BaseState, PreferencesState } from "@toruslabs/base-controllers";
2
- import { get } from "@toruslabs/http-helpers";
3
- import log from "loglevel";
4
-
5
- import { COINGECKO_PLATFORMS_CHAIN_CODE_MAP, COINGECKO_SUPPORTED_CURRENCIES } from "../utils/constants";
6
- import { idleTimeTracker } from "../utils/helpers";
7
- import { CustomTokenInfo, EthereumNetworkState, ExtendedAddressPreferences } from "../utils/interfaces";
8
- import { TokensControllerState } from "./ITokensController";
9
-
10
- export interface CoinGeckoResponse {
11
- [address: string]: {
12
- [currency: string]: number;
13
- };
14
- }
15
-
16
- export type ContractExchangeRates = Record<string, number | undefined>;
17
-
18
- export const DEFAULT_CURRENCY = "eth";
19
-
20
- export interface ITokenRatesControllerState extends BaseState {
21
- contractExchangeRates: ContractExchangeRates;
22
- }
23
-
24
- export interface ITokenRatesControllerConfig extends BaseConfig {
25
- pollInterval: number;
26
- api: string;
27
- currencyApi: string;
28
- chainId: string;
29
- selectedAddress: string;
30
- nativeCurrency: string;
31
- tokens: CustomTokenInfo[];
32
- }
33
-
34
- export interface TokenRatesControllerOptions {
35
- config: Partial<ITokenRatesControllerConfig>;
36
- state: Partial<ITokenRatesControllerState>;
37
- onPreferencesStateChange: (listener: (preferencesState: PreferencesState<ExtendedAddressPreferences>) => void) => void;
38
- onTokensStateChange: (listener: (tokensState: TokensControllerState) => void) => void;
39
- onNetworkStateChange: (listener: (networkState: EthereumNetworkState) => void) => void;
40
- }
41
-
42
- export class TokenRatesController extends BaseController<ITokenRatesControllerConfig, ITokenRatesControllerState> {
43
- private conversionInterval: number;
44
-
45
- constructor({ config, state, onPreferencesStateChange, onNetworkStateChange, onTokensStateChange }: TokenRatesControllerOptions) {
46
- super({ config, state });
47
- this.defaultState = {
48
- ...this.defaultState,
49
- contractExchangeRates: {},
50
- };
51
- this.initialize();
52
-
53
- onPreferencesStateChange((preferencesState) => {
54
- const { selectedAddress } = preferencesState;
55
- this.configure({ selectedAddress });
56
- });
57
-
58
- onNetworkStateChange((networkState) => {
59
- const { chainId, ticker } = networkState.providerConfig;
60
- this.configure({ chainId, nativeCurrency: ticker });
61
- });
62
-
63
- onTokensStateChange((tokensState) => {
64
- const { tokens } = tokensState;
65
- const currentUserTokens = tokens[this.config.selectedAddress];
66
- if (currentUserTokens?.length > 0 && this.config.tokens !== tokens[this.config.selectedAddress]) {
67
- this.configure({ tokens: tokens[this.config.selectedAddress] });
68
- this.updateExchangeRates();
69
- }
70
- });
71
- }
72
-
73
- /**
74
- * Creates a new poll, using setInterval, to periodically call updateConversionRate. The id of the interval is
75
- * stored at the controller's conversionInterval property. If it is called and such an id already exists, the
76
- * previous interval is clear and a new one is created.
77
- */
78
- public scheduleConversionInterval(): void {
79
- if (this.conversionInterval) {
80
- window.clearInterval(this.conversionInterval);
81
- }
82
- this.conversionInterval = window.setInterval(() => {
83
- if (!idleTimeTracker.checkIfIdle()) {
84
- this.updateExchangeRates();
85
- }
86
- }, this.config.pollInterval);
87
- }
88
-
89
- public async updateExchangeRates() {
90
- const chainCodes = COINGECKO_PLATFORMS_CHAIN_CODE_MAP[this.config.chainId];
91
- let newContractExchangeRates: ContractExchangeRates = {};
92
- if (!chainCodes) {
93
- log.info(`ChainId ${this.config.chainId} not supported by coingecko`);
94
- this.config.tokens.forEach((token) => {
95
- newContractExchangeRates[token.tokenAddress] = undefined;
96
- });
97
- } else {
98
- newContractExchangeRates = await this.fetchExchangeRates(this.config.nativeCurrency, chainCodes);
99
- }
100
- this.update({ contractExchangeRates: newContractExchangeRates });
101
- }
102
-
103
- private async fetchExchangeRates(nativeCurrency: string, chainCodes: { platform: string; currency: string }): Promise<ContractExchangeRates> {
104
- const contractAddresses = this.config.tokens.map((token) => token.tokenAddress);
105
-
106
- const isNativeCurrencySupported = COINGECKO_SUPPORTED_CURRENCIES.has(nativeCurrency.toLowerCase());
107
- if (isNativeCurrencySupported) {
108
- const response = await get<CoinGeckoResponse>(
109
- `${this.config.api}/simple/token_price/${chainCodes.platform}?contract_addresses=${contractAddresses.join(
110
- ","
111
- )}&vs_currencies=${nativeCurrency.toLowerCase()}&include_market_cap=false&include_24hr_vol=false&include_24hr_change=false&include_last_updated_at=false`
112
- );
113
- const newContractExchangeRates: ContractExchangeRates = {};
114
- Object.keys(response).forEach((contractAddress) => {
115
- newContractExchangeRates[contractAddress] = response[contractAddress][nativeCurrency.toLowerCase()] || 0;
116
- });
117
- return newContractExchangeRates;
118
- }
119
- const [response, currencyResponse] = await Promise.all([
120
- get<CoinGeckoResponse>(
121
- `${this.config.api}/simple/token_price/${chainCodes.platform}?contract_addresses=${contractAddresses.join(
122
- ","
123
- )}&vs_currencies=${DEFAULT_CURRENCY}&include_market_cap=false&include_24hr_vol=false&include_24hr_change=false&include_last_updated_at=false`
124
- ),
125
- get<Record<string, string>>(`${this.config.currencyApi}/currency?fsym=${nativeCurrency.toUpperCase()}&tsyms=${DEFAULT_CURRENCY.toUpperCase()}`),
126
- ]);
127
- const newContractExchangeRates: ContractExchangeRates = {};
128
- Object.keys(response).forEach((contractAddress) => {
129
- newContractExchangeRates[contractAddress] =
130
- response[contractAddress][DEFAULT_CURRENCY] * Number.parseFloat(currencyResponse[DEFAULT_CURRENCY]) || 0;
131
- });
132
- return newContractExchangeRates;
133
- }
134
- }