@toruslabs/ethereum-controllers 5.10.1 → 6.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 (111) hide show
  1. package/dist/ethereumControllers.cjs.js +114 -432
  2. package/dist/ethereumControllers.esm.js +63 -351
  3. package/dist/ethereumControllers.umd.min.js +1 -1
  4. package/dist/ethereumControllers.umd.min.js.LICENSE.txt +7 -2
  5. package/dist/lib.cjs/Account/AccountTrackerController.js +160 -0
  6. package/dist/lib.cjs/Block/PollingBlockTracker.js +85 -0
  7. package/dist/lib.cjs/Currency/CurrencyController.js +111 -0
  8. package/dist/lib.cjs/Gas/GasFeeController.js +214 -0
  9. package/dist/lib.cjs/Gas/gasUtil.js +148 -0
  10. package/dist/lib.cjs/Keyring/KeyringController.js +93 -0
  11. package/dist/lib.cjs/Message/AbstractMessageController.js +107 -0
  12. package/dist/lib.cjs/Message/AddChainController.js +78 -0
  13. package/dist/lib.cjs/Message/MessageController.js +77 -0
  14. package/dist/lib.cjs/Message/PersonalMessageController.js +77 -0
  15. package/dist/lib.cjs/Message/SwitchChainController.js +78 -0
  16. package/dist/lib.cjs/Message/TypedMessageController.js +81 -0
  17. package/dist/lib.cjs/Message/utils.js +112 -0
  18. package/dist/lib.cjs/Network/NetworkController.js +201 -0
  19. package/dist/lib.cjs/Network/cacheIdentifier.js +112 -0
  20. package/dist/lib.cjs/Network/createEthereumMiddleware.js +302 -0
  21. package/dist/lib.cjs/Network/createJsonRpcClient.js +64 -0
  22. package/dist/lib.cjs/Nfts/NftHandler.js +180 -0
  23. package/dist/lib.cjs/Nfts/NftsController.js +213 -0
  24. package/dist/lib.cjs/Preferences/PreferencesController.js +476 -0
  25. package/dist/lib.cjs/Tokens/TokenHandler.js +51 -0
  26. package/dist/lib.cjs/Tokens/TokenRatesController.js +112 -0
  27. package/dist/lib.cjs/Tokens/TokensController.js +259 -0
  28. package/dist/lib.cjs/Transaction/NonceTracker.js +150 -0
  29. package/dist/lib.cjs/Transaction/PendingTransactionTracker.js +222 -0
  30. package/dist/lib.cjs/Transaction/TransactionController.js +515 -0
  31. package/dist/lib.cjs/Transaction/TransactionGasUtil.js +81 -0
  32. package/dist/lib.cjs/Transaction/TransactionStateHistoryHelper.js +42 -0
  33. package/dist/lib.cjs/Transaction/TransactionStateManager.js +296 -0
  34. package/dist/lib.cjs/Transaction/TransactionUtils.js +341 -0
  35. package/dist/lib.cjs/index.js +171 -0
  36. package/dist/lib.cjs/utils/abis.js +510 -0
  37. package/dist/lib.cjs/utils/constants.js +362 -0
  38. package/dist/lib.cjs/utils/contractAddresses.js +16 -0
  39. package/dist/lib.cjs/utils/conversionUtils.js +232 -0
  40. package/dist/lib.cjs/utils/helpers.js +244 -0
  41. package/dist/lib.cjs/utils/lodashUtils.js +25 -0
  42. package/dist/lib.esm/Account/AccountTrackerController.js +158 -0
  43. package/dist/lib.esm/Block/PollingBlockTracker.js +83 -0
  44. package/dist/lib.esm/Currency/CurrencyController.js +109 -0
  45. package/dist/lib.esm/Gas/GasFeeController.js +212 -0
  46. package/dist/lib.esm/Gas/gasUtil.js +141 -0
  47. package/dist/lib.esm/Keyring/KeyringController.js +91 -0
  48. package/dist/lib.esm/Message/AbstractMessageController.js +105 -0
  49. package/dist/lib.esm/Message/AddChainController.js +76 -0
  50. package/dist/lib.esm/Message/MessageController.js +75 -0
  51. package/dist/lib.esm/Message/PersonalMessageController.js +75 -0
  52. package/dist/lib.esm/Message/SwitchChainController.js +76 -0
  53. package/dist/lib.esm/Message/TypedMessageController.js +79 -0
  54. package/dist/lib.esm/Message/utils.js +105 -0
  55. package/dist/lib.esm/Network/NetworkController.js +199 -0
  56. package/dist/lib.esm/Network/cacheIdentifier.js +107 -0
  57. package/dist/lib.esm/Network/createEthereumMiddleware.js +289 -0
  58. package/dist/lib.esm/Network/createJsonRpcClient.js +60 -0
  59. package/dist/lib.esm/Nfts/NftHandler.js +178 -0
  60. package/dist/lib.esm/Nfts/NftsController.js +211 -0
  61. package/dist/lib.esm/Preferences/PreferencesController.js +474 -0
  62. package/dist/lib.esm/Tokens/TokenHandler.js +49 -0
  63. package/dist/lib.esm/Tokens/TokenRatesController.js +109 -0
  64. package/dist/lib.esm/Tokens/TokensController.js +257 -0
  65. package/dist/lib.esm/Transaction/NonceTracker.js +148 -0
  66. package/dist/lib.esm/Transaction/PendingTransactionTracker.js +220 -0
  67. package/dist/lib.esm/Transaction/TransactionController.js +513 -0
  68. package/dist/lib.esm/Transaction/TransactionGasUtil.js +79 -0
  69. package/dist/lib.esm/Transaction/TransactionStateHistoryHelper.js +38 -0
  70. package/dist/lib.esm/Transaction/TransactionStateManager.js +294 -0
  71. package/dist/lib.esm/Transaction/TransactionUtils.js +326 -0
  72. package/dist/lib.esm/index.js +33 -0
  73. package/dist/lib.esm/utils/abis.js +505 -0
  74. package/dist/lib.esm/utils/constants.js +323 -0
  75. package/dist/lib.esm/utils/contractAddresses.js +14 -0
  76. package/dist/lib.esm/utils/conversionUtils.js +218 -0
  77. package/dist/lib.esm/utils/helpers.js +227 -0
  78. package/dist/lib.esm/utils/lodashUtils.js +21 -0
  79. package/dist/types/Account/AccountTrackerController.d.ts +5 -5
  80. package/dist/types/Block/PollingBlockTracker.d.ts +1 -2
  81. package/dist/types/Currency/CurrencyController.d.ts +1 -1
  82. package/dist/types/Gas/GasFeeController.d.ts +3 -3
  83. package/dist/types/Gas/gasUtil.d.ts +1 -1
  84. package/dist/types/Keyring/KeyringController.d.ts +3 -5
  85. package/dist/types/Message/AbstractMessageController.d.ts +5 -6
  86. package/dist/types/Message/AddChainController.d.ts +4 -4
  87. package/dist/types/Message/MessageController.d.ts +4 -4
  88. package/dist/types/Message/PersonalMessageController.d.ts +4 -4
  89. package/dist/types/Message/SwitchChainController.d.ts +4 -4
  90. package/dist/types/Message/TypedMessageController.d.ts +6 -7
  91. package/dist/types/Message/utils.d.ts +2 -7
  92. package/dist/types/Network/NetworkController.d.ts +4 -4
  93. package/dist/types/Network/cacheIdentifier.d.ts +1 -1
  94. package/dist/types/Network/createEthereumMiddleware.d.ts +2 -18
  95. package/dist/types/Network/createJsonRpcClient.d.ts +2 -2
  96. package/dist/types/Nfts/NftsController.d.ts +2 -2
  97. package/dist/types/Preferences/PreferencesController.d.ts +4 -4
  98. package/dist/types/Tokens/TokensController.d.ts +3 -3
  99. package/dist/types/Transaction/NonceTracker.d.ts +5 -5
  100. package/dist/types/Transaction/PendingTransactionTracker.d.ts +5 -5
  101. package/dist/types/Transaction/TransactionController.d.ts +12 -12
  102. package/dist/types/Transaction/TransactionGasUtil.d.ts +4 -4
  103. package/dist/types/Transaction/TransactionStateManager.d.ts +3 -3
  104. package/dist/types/Transaction/TransactionUtils.d.ts +1 -1
  105. package/dist/types/index.d.ts +12 -14
  106. package/dist/types/utils/constants.d.ts +1 -5
  107. package/dist/types/utils/helpers.d.ts +7 -4
  108. package/dist/types/utils/interfaces.d.ts +43 -23
  109. package/package.json +22 -10
  110. package/dist/types/Message/DecryptMessageController.d.ts +0 -20
  111. package/dist/types/Message/EncryptionPublicKeyController.d.ts +0 -20
@@ -0,0 +1,244 @@
1
+ 'use strict';
2
+
3
+ var _objectSpread = require('@babel/runtime/helpers/objectSpread2');
4
+ var util = require('@ethereumjs/util');
5
+ var baseControllers = require('@toruslabs/base-controllers');
6
+ var BigNumber = require('bignumber.js');
7
+ var log = require('loglevel');
8
+ var TransactionUtils = require('../Transaction/TransactionUtils.js');
9
+ var constants = require('./constants.js');
10
+
11
+ function hexToBn(hex) {
12
+ return new BigNumber(util.stripHexPrefix(hex), 16);
13
+ }
14
+ function BNToHex(bn) {
15
+ return util.addHexPrefix(bn.toString(16));
16
+ }
17
+ function getEtherScanHashLink(txHash, chainId) {
18
+ if (!constants.SUPPORTED_NETWORKS[chainId]) return "";
19
+ return `${constants.SUPPORTED_NETWORKS[chainId].blockExplorerUrl}/tx/${txHash}`;
20
+ }
21
+ const formatPastTx = params => {
22
+ var _transaction$to;
23
+ const {
24
+ transaction,
25
+ lowerCaseSelectedAddress,
26
+ blockExplorerUrl
27
+ } = params;
28
+ let totalAmountString = "";
29
+ if (transaction.type === constants.CONTRACT_TYPE_ERC721 || transaction.type === constants.CONTRACT_TYPE_ERC1155) totalAmountString = transaction.symbol;else if (transaction.type === constants.CONTRACT_TYPE_ERC20) totalAmountString = baseControllers.formatSmallNumbers(Number.parseFloat(transaction.total_amount), transaction.symbol, true);else totalAmountString = baseControllers.formatSmallNumbers(Number.parseFloat(transaction.total_amount), transaction.type_name, true);
30
+ const currencyAmountString = transaction.type === constants.CONTRACT_TYPE_ERC721 || transaction.type === constants.CONTRACT_TYPE_ERC1155 || transaction.isEtherscan ? "" : baseControllers.formatSmallNumbers(Number.parseFloat(transaction.currency_amount), transaction.selected_currency, true);
31
+ const finalObject = {
32
+ id: transaction.created_at.toString(),
33
+ date: new Date(transaction.created_at).toString(),
34
+ from: transaction.from,
35
+ from_aa_address: transaction.from_aa_address,
36
+ slicedFrom: typeof transaction.from === "string" ? baseControllers.addressSlicer(transaction.from) : "",
37
+ to: transaction.to,
38
+ slicedTo: typeof transaction.to === "string" ? baseControllers.addressSlicer(transaction.to) : "",
39
+ action: lowerCaseSelectedAddress === ((_transaction$to = transaction.to) === null || _transaction$to === void 0 ? void 0 : _transaction$to.toLowerCase()) || "" ? baseControllers.ACTIVITY_ACTION_RECEIVE : baseControllers.ACTIVITY_ACTION_SEND,
40
+ totalAmount: transaction.total_amount,
41
+ totalAmountString,
42
+ currencyAmount: transaction.currency_amount,
43
+ currencyAmountString,
44
+ amount: `${totalAmountString} / ${currencyAmountString}`,
45
+ status: transaction.status,
46
+ etherscanLink: blockExplorerUrl ? `${blockExplorerUrl}/tx/${transaction.transaction_hash}` : "",
47
+ chainId: transaction.chain_id,
48
+ ethRate: Number.parseFloat(transaction === null || transaction === void 0 ? void 0 : transaction.total_amount) && Number.parseFloat(transaction === null || transaction === void 0 ? void 0 : transaction.currency_amount) ? `1 ${transaction.symbol} = ${baseControllers.significantDigits(Number.parseFloat(transaction.currency_amount) / Number.parseFloat(transaction.total_amount))}` : "",
49
+ currencyUsed: transaction.selected_currency,
50
+ type: transaction.type,
51
+ type_name: transaction.type_name,
52
+ type_image_link: transaction.type_image_link,
53
+ transaction_hash: transaction.transaction_hash,
54
+ transaction_category: transaction.transaction_category,
55
+ isEtherscan: transaction.isEtherscan,
56
+ input: transaction.input || "",
57
+ token_id: transaction.token_id || "",
58
+ contract_address: transaction.contract_address || "",
59
+ nonce: transaction.nonce || "",
60
+ is_cancel: !!transaction.is_cancel || false,
61
+ gas: transaction.gas || "",
62
+ gasPrice: transaction.gasPrice || ""
63
+ };
64
+ return finalObject;
65
+ };
66
+
67
+ /**
68
+ * Ref - https://ethereum.org/en/developers/docs/apis/json-rpc/#eth_gettransactionreceipt
69
+ */
70
+ const getEthTxStatus = async (hash, provider) => {
71
+ try {
72
+ const result = await provider.request({
73
+ method: constants.METHOD_TYPES.ETH_GET_TRANSACTION_RECEIPT,
74
+ params: [hash]
75
+ });
76
+ if (result === null) return baseControllers.TransactionStatus.submitted;
77
+ if (result && result.status === "0x1") return baseControllers.TransactionStatus.confirmed;
78
+ if (result && result.status === "0x0") return baseControllers.TransactionStatus.rejected;
79
+ return undefined;
80
+ } catch (err) {
81
+ log.warn("unable to fetch transaction status", err);
82
+ return undefined;
83
+ }
84
+ };
85
+ function formatDate(inputDate) {
86
+ const monthList = ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"];
87
+ const date = new Date(inputDate);
88
+ const day = date.getDate();
89
+ const month = monthList[date.getMonth()];
90
+ const year = date.getFullYear();
91
+ return `${day} ${month} ${year}`;
92
+ }
93
+ function formatTime(time) {
94
+ return new Date(time).toTimeString().slice(0, 8);
95
+ }
96
+ const idleTimeTracker = (activityThresholdTime => {
97
+ let isIdle = false;
98
+ let idleTimeout = null;
99
+ const resetTimer = () => {
100
+ if (idleTimeout) {
101
+ window.clearTimeout(idleTimeout);
102
+ }
103
+ isIdle = false;
104
+ idleTimeout = window.setTimeout(() => {
105
+ isIdle = true;
106
+ }, activityThresholdTime * 1000);
107
+ };
108
+ if (typeof window !== "undefined" && typeof document !== "undefined") {
109
+ window.addEventListener("load", resetTimer);
110
+ document.addEventListener("mousemove", resetTimer);
111
+ document.addEventListener("keydown", resetTimer);
112
+ }
113
+ function checkIfIdle() {
114
+ return isIdle;
115
+ }
116
+ return {
117
+ checkIfIdle
118
+ };
119
+ })(60 * 3);
120
+ function isAddressByChainId(address, _chainId) {
121
+ // TOOD: add rsk network checks.
122
+ return util.isValidAddress(address);
123
+ }
124
+ function toChecksumAddressByChainId(address, chainId) {
125
+ // TOOD: add rsk network checks.
126
+ if (!isAddressByChainId(address)) return address;
127
+ return util.toChecksumAddress(address);
128
+ }
129
+ const GAS_LIMITS = {
130
+ // maximum gasLimit of a simple send
131
+ SIMPLE: util.addHexPrefix(21000 .toString(16)),
132
+ // a base estimate for token transfers.
133
+ BASE_TOKEN_ESTIMATE: util.addHexPrefix(100000 .toString(16))
134
+ };
135
+ function bnLessThan(a, b) {
136
+ if (a === null || a === undefined || b === null || b === undefined) {
137
+ return null;
138
+ }
139
+ return new BigNumber(a, 10).lt(b, 10);
140
+ }
141
+ const getIpfsEndpoint = path => `https://infura-ipfs.io/${path}`;
142
+ function sanitizeNftMetdataUrl(url) {
143
+ let finalUri = url;
144
+ if (url !== null && url !== void 0 && url.startsWith("ipfs")) {
145
+ const ipfsPath = url.split("ipfs://")[1];
146
+ finalUri = getIpfsEndpoint(ipfsPath);
147
+ }
148
+ return finalUri;
149
+ }
150
+ function getChainType(chainId) {
151
+ if (chainId === constants.MAINNET_CHAIN_ID) {
152
+ return "mainnet";
153
+ } else if (constants.TEST_CHAINS.includes(chainId)) {
154
+ return "testnet";
155
+ }
156
+ return "custom";
157
+ }
158
+ const addEtherscanTransactions = async params => {
159
+ const {
160
+ txn,
161
+ lowerCaseSelectedAddress,
162
+ provider,
163
+ chainId,
164
+ blockExplorerUrl
165
+ } = params;
166
+ const transactionPromises = await Promise.all(txn.map(async tx => {
167
+ var _SUPPORTED_NETWORKS$c, _SUPPORTED_NETWORKS$c2;
168
+ const {
169
+ category,
170
+ type
171
+ } = await TransactionUtils.determineTransactionType(_objectSpread(_objectSpread({}, tx), {}, {
172
+ data: tx.input
173
+ }), provider);
174
+ tx.transaction_category = tx.transaction_category || category;
175
+ tx.type_image_link = ((_SUPPORTED_NETWORKS$c = constants.SUPPORTED_NETWORKS[chainId]) === null || _SUPPORTED_NETWORKS$c === void 0 ? void 0 : _SUPPORTED_NETWORKS$c.logo) || "";
176
+ tx.type_name = (_SUPPORTED_NETWORKS$c2 = constants.SUPPORTED_NETWORKS[chainId]) === null || _SUPPORTED_NETWORKS$c2 === void 0 ? void 0 : _SUPPORTED_NETWORKS$c2.ticker;
177
+ tx.type = type;
178
+ return tx;
179
+ }));
180
+ const finalTxs = transactionPromises.reduce((accumulator, x) => {
181
+ var _SUPPORTED_NETWORKS$c3, _SUPPORTED_NETWORKS$c4;
182
+ let totalAmountString = x.value ? new BigNumber(x.value).div(new BigNumber(10).pow(new BigNumber(x.tokenDecimal || 18))).toString() : "";
183
+ let type = constants.CONTRACT_TYPE_ETH;
184
+ if (x.contractAddress !== "") {
185
+ if (x.tokenID) {
186
+ type = x.tokenValue ? constants.CONTRACT_TYPE_ERC1155 : constants.CONTRACT_TYPE_ERC721;
187
+ } else {
188
+ type = constants.CONTRACT_TYPE_ERC20;
189
+ }
190
+ }
191
+ if (type === constants.CONTRACT_TYPE_ERC1155) {
192
+ totalAmountString = x.tokenValue;
193
+ }
194
+ const etherscanTransaction = {
195
+ type,
196
+ type_image_link: x.type_image_link || "n/a",
197
+ type_name: x.tokenName || ((_SUPPORTED_NETWORKS$c3 = constants.SUPPORTED_NETWORKS[chainId]) === null || _SUPPORTED_NETWORKS$c3 === void 0 ? void 0 : _SUPPORTED_NETWORKS$c3.ticker) || "n/a",
198
+ symbol: x.tokenSymbol || ((_SUPPORTED_NETWORKS$c4 = constants.SUPPORTED_NETWORKS[chainId]) === null || _SUPPORTED_NETWORKS$c4 === void 0 ? void 0 : _SUPPORTED_NETWORKS$c4.ticker),
199
+ token_id: x.tokenID || "",
200
+ total_amount: totalAmountString,
201
+ created_at: new Date(Number(x.timeStamp) * 1000),
202
+ from: x.from,
203
+ to: x.to,
204
+ transaction_hash: x.hash,
205
+ status: x.txreceipt_status && x.txreceipt_status === "0" ? baseControllers.TransactionStatus.failed : baseControllers.TransactionStatus.confirmed,
206
+ isEtherscan: true,
207
+ input: x.input,
208
+ contract_address: x.contractAddress,
209
+ transaction_category: x.transaction_category,
210
+ gas: `0x${new BigNumber(x.gasUsed || 0, 10).toString(16)}`,
211
+ gasPrice: `0x${new BigNumber(x.gasPrice || 0, 10).toString(16)}`,
212
+ chain_id: chainId,
213
+ currency_amount: "",
214
+ nonce: x.nonce,
215
+ from_aa_address: "",
216
+ is_cancel: false,
217
+ selected_currency: ""
218
+ };
219
+ accumulator.push(formatPastTx({
220
+ transaction: etherscanTransaction,
221
+ lowerCaseSelectedAddress,
222
+ blockExplorerUrl
223
+ }));
224
+ return accumulator;
225
+ }, []);
226
+ return finalTxs;
227
+ };
228
+
229
+ exports.BNToHex = BNToHex;
230
+ exports.GAS_LIMITS = GAS_LIMITS;
231
+ exports.addEtherscanTransactions = addEtherscanTransactions;
232
+ exports.bnLessThan = bnLessThan;
233
+ exports.formatDate = formatDate;
234
+ exports.formatPastTx = formatPastTx;
235
+ exports.formatTime = formatTime;
236
+ exports.getChainType = getChainType;
237
+ exports.getEthTxStatus = getEthTxStatus;
238
+ exports.getEtherScanHashLink = getEtherScanHashLink;
239
+ exports.getIpfsEndpoint = getIpfsEndpoint;
240
+ exports.hexToBn = hexToBn;
241
+ exports.idleTimeTracker = idleTimeTracker;
242
+ exports.isAddressByChainId = isAddressByChainId;
243
+ exports.sanitizeNftMetdataUrl = sanitizeNftMetdataUrl;
244
+ exports.toChecksumAddressByChainId = toChecksumAddressByChainId;
@@ -0,0 +1,25 @@
1
+ 'use strict';
2
+
3
+ function sortBy(arr, key) {
4
+ return arr.slice().sort((a, b) => {
5
+ if (a[key] < b[key]) return -1;
6
+ if (a[key] > b[key]) return 1;
7
+ return 0;
8
+ });
9
+ }
10
+ function keyBy(arr, key) {
11
+ return arr.reduce((acc, item) => {
12
+ const keyValue = item[key];
13
+ if (typeof keyValue === "string" || typeof keyValue === "number") {
14
+ acc[keyValue.toString()] = item;
15
+ }
16
+ return acc;
17
+ }, {});
18
+ }
19
+ function mapValues(obj, iteratee) {
20
+ return Object.fromEntries(Object.entries(obj).map(([key, value]) => [key, iteratee(value, key)]));
21
+ }
22
+
23
+ exports.keyBy = keyBy;
24
+ exports.mapValues = mapValues;
25
+ exports.sortBy = sortBy;
@@ -0,0 +1,158 @@
1
+ import _objectSpread from '@babel/runtime/helpers/objectSpread2';
2
+ import _defineProperty from '@babel/runtime/helpers/defineProperty';
3
+ import { BaseController } from '@toruslabs/base-controllers';
4
+ import { Mutex } from 'async-mutex';
5
+ import { BrowserProvider, toQuantity, Contract } from 'ethers';
6
+ import log from 'loglevel';
7
+ import { singleBalanceCheckerAbi } from '../utils/abis.js';
8
+ import { SINGLE_CALL_BALANCES_ADDRESSES } from '../utils/contractAddresses.js';
9
+
10
+ const ZERO_ADDRESS = "0x0000000000000000000000000000000000000000";
11
+
12
+ /**
13
+ * Tracks accounts based on blocks.
14
+ * If block tracker provides latest block, we query accounts from it.
15
+ * Preferences state changes also retrigger accounts update.
16
+ * Network state changes also retrigger accounts update.
17
+ */
18
+ class AccountTrackerController extends BaseController {
19
+ constructor({
20
+ config,
21
+ state,
22
+ provider,
23
+ blockTracker,
24
+ getIdentities,
25
+ onPreferencesStateChange,
26
+ getCurrentChainId
27
+ }) {
28
+ super({
29
+ config,
30
+ state
31
+ });
32
+ _defineProperty(this, "provider", void 0);
33
+ _defineProperty(this, "blockTracker", void 0);
34
+ _defineProperty(this, "mutex", new Mutex());
35
+ _defineProperty(this, "ethersProvider", void 0);
36
+ _defineProperty(this, "getIdentities", void 0);
37
+ _defineProperty(this, "getCurrentChainId", void 0);
38
+ this.defaultState = {
39
+ accounts: {}
40
+ };
41
+ this.initialize();
42
+ this.provider = provider;
43
+ this.blockTracker = blockTracker;
44
+ this.ethersProvider = new BrowserProvider(this.provider, "any");
45
+ this.getIdentities = getIdentities;
46
+ this.getCurrentChainId = getCurrentChainId;
47
+ onPreferencesStateChange(() => {
48
+ const refreshNeeded = this.syncAccounts();
49
+ if (refreshNeeded) {
50
+ log.info("onPreferencesStateChange called");
51
+ this.refresh();
52
+ }
53
+ });
54
+ this.blockTrackerListener = this.blockTrackerListener.bind(this);
55
+ }
56
+ blockTrackerListener() {
57
+ this.refresh();
58
+ }
59
+ startPolling() {
60
+ this.stopPolling();
61
+ // Initiate block tracker internal tracking.
62
+ if (Object.keys(this.state.accounts).length > 0) {
63
+ // Adding this listener on block tracker triggers it to start polling.
64
+ this.blockTracker.on("latest", this.blockTrackerListener);
65
+ }
66
+ }
67
+ stopPolling() {
68
+ this.blockTracker.removeListener("latest", this.blockTrackerListener);
69
+ }
70
+ syncAccounts() {
71
+ const {
72
+ accounts
73
+ } = this.state;
74
+ const addresses = Object.keys(this.getIdentities());
75
+ const existing = Object.keys(accounts);
76
+ const newAddresses = addresses.filter(address => existing.indexOf(address) === -1);
77
+ const oldAddresses = existing.filter(address => addresses.indexOf(address) === -1);
78
+ let isUpdated = false;
79
+ newAddresses.forEach(address => {
80
+ isUpdated = true;
81
+ accounts[address] = {
82
+ balance: "0x0"
83
+ };
84
+ });
85
+ oldAddresses.forEach(address => {
86
+ isUpdated = true;
87
+ delete accounts[address];
88
+ });
89
+ this.update({
90
+ accounts: _objectSpread({}, accounts)
91
+ });
92
+ return isUpdated;
93
+ }
94
+ async refresh() {
95
+ const releaseLock = await this.mutex.acquire();
96
+ try {
97
+ this._updateAccounts();
98
+ } catch (error) {} finally {
99
+ releaseLock();
100
+ }
101
+ }
102
+ async _updateAccounts() {
103
+ const {
104
+ accounts
105
+ } = this.state;
106
+ const addresses = Object.keys(accounts);
107
+ const chainId = this.getCurrentChainId();
108
+ if (chainId === "loading") return;
109
+ if (addresses.length > 0) {
110
+ if (SINGLE_CALL_BALANCES_ADDRESSES[chainId]) {
111
+ await this._updateAccountsViaBalanceChecker(addresses, SINGLE_CALL_BALANCES_ADDRESSES[chainId]);
112
+ return;
113
+ }
114
+ log.info("falling back to ethQuery.getBalance");
115
+ await Promise.all(addresses.map(x => this._updateAccount(x)));
116
+ }
117
+ }
118
+ async _updateAccount(address) {
119
+ const balance = await this.provider.request({
120
+ method: "eth_getBalance",
121
+ params: [address, "latest"]
122
+ });
123
+ const {
124
+ accounts
125
+ } = this.state;
126
+ if (!accounts[address]) return;
127
+ accounts[address] = {
128
+ balance: toQuantity(balance)
129
+ };
130
+ this.update({
131
+ accounts
132
+ });
133
+ }
134
+ async _updateAccountsViaBalanceChecker(addresses, deployedContractAddress) {
135
+ const ethContract = new Contract(deployedContractAddress, singleBalanceCheckerAbi, this.ethersProvider);
136
+ try {
137
+ const result = await ethContract.balances(addresses, [ZERO_ADDRESS]);
138
+ const {
139
+ accounts
140
+ } = this.state;
141
+ addresses.forEach((address, index) => {
142
+ const balance = toQuantity(result[index]);
143
+ if (!accounts[address]) return;
144
+ accounts[address] = {
145
+ balance
146
+ };
147
+ });
148
+ return this.update({
149
+ accounts
150
+ });
151
+ } catch (error) {
152
+ log.warn("Torus - Account Tracker single call balance fetch failed", error);
153
+ return Promise.all(addresses.map(x => this._updateAccount(x)));
154
+ }
155
+ }
156
+ }
157
+
158
+ export { AccountTrackerController };
@@ -0,0 +1,83 @@
1
+ import { BaseBlockTracker, timeout } from '@toruslabs/base-controllers';
2
+ import log from 'loglevel';
3
+ import { idleTimeTracker } from '../utils/helpers.js';
4
+
5
+ const DEFAULT_POLLING_INTERVAL = 20;
6
+ const DEFAULT_RETRY_TIMEOUT = 2;
7
+ const SEC = 1000;
8
+ class PollingBlockTracker extends BaseBlockTracker {
9
+ constructor({
10
+ config,
11
+ state = {}
12
+ }) {
13
+ if (!config.provider) {
14
+ throw new Error("PollingBlockTracker - no provider specified.");
15
+ }
16
+ super({
17
+ config,
18
+ state
19
+ });
20
+ const pollingInterval = config.pollingInterval || DEFAULT_POLLING_INTERVAL;
21
+ const retryTimeout = config.retryTimeout || DEFAULT_RETRY_TIMEOUT;
22
+
23
+ // merge default + provided config.
24
+ this.defaultConfig = {
25
+ provider: config.provider,
26
+ pollingInterval: pollingInterval * SEC,
27
+ retryTimeout: retryTimeout * SEC,
28
+ setSkipCacheFlag: config.setSkipCacheFlag || false
29
+ };
30
+ this.initialize();
31
+ }
32
+ async checkForLatestBlock() {
33
+ await this._updateLatestBlock();
34
+ return this.getLatestBlock();
35
+ }
36
+
37
+ // overrides the BaseBlockTracker._start method.
38
+ _start() {
39
+ this._synchronize().catch(err => this.emit("error", err));
40
+ }
41
+ async _synchronize() {
42
+ while (this.state._isRunning) {
43
+ if (idleTimeTracker.checkIfIdle()) return;
44
+ try {
45
+ await this._updateLatestBlock();
46
+ await timeout(this.config.pollingInterval);
47
+ } catch (err) {
48
+ const newErr = new Error(`PollingBlockTracker - encountered an error while attempting to update latest block:\n${err.stack}`);
49
+ try {
50
+ this.emit("error", newErr);
51
+ } catch (emitErr) {
52
+ log.error(newErr);
53
+ }
54
+ await timeout(this.config.retryTimeout);
55
+ }
56
+ }
57
+ }
58
+ async _updateLatestBlock() {
59
+ // fetch + set latest block
60
+ const latestBlock = await this._fetchLatestBlock();
61
+ this._newPotentialLatest(latestBlock);
62
+ }
63
+ async _fetchLatestBlock() {
64
+ try {
65
+ const block = await this.config.provider.request({
66
+ method: "eth_getBlockByNumber",
67
+ params: ["latest", false]
68
+ });
69
+ return {
70
+ blockHash: block.hash,
71
+ idempotencyKey: block.number,
72
+ timestamp: block.timestamp,
73
+ baseFeePerGas: block.baseFeePerGas,
74
+ gasLimit: block.gasLimit
75
+ };
76
+ } catch (error) {
77
+ log.error("Polling Block Tracker: ", error);
78
+ throw new Error(`PollingBlockTracker - encountered error fetching block:\n${error.message}`);
79
+ }
80
+ }
81
+ }
82
+
83
+ export { PollingBlockTracker };
@@ -0,0 +1,109 @@
1
+ import _objectSpread from '@babel/runtime/helpers/objectSpread2';
2
+ import _defineProperty from '@babel/runtime/helpers/defineProperty';
3
+ import { BaseCurrencyController } from '@toruslabs/base-controllers';
4
+ import { get } from '@toruslabs/http-helpers';
5
+ import log from 'loglevel';
6
+ import { idleTimeTracker } from '../utils/helpers.js';
7
+
8
+ class CurrencyController extends BaseCurrencyController {
9
+ constructor({
10
+ config,
11
+ state,
12
+ onNetworkChanged
13
+ }) {
14
+ super({
15
+ config,
16
+ state
17
+ });
18
+ _defineProperty(this, "conversionInterval", void 0);
19
+ this.defaultState = _objectSpread(_objectSpread({}, this.defaultState), {}, {
20
+ commonDenomination: "USD",
21
+ commonDenominatorPrice: 0
22
+ });
23
+ this.initialize();
24
+ onNetworkChanged(networkState => {
25
+ // to be called as (listener) => this.networkController.on('networkDidChange', listener);
26
+ if (networkState.providerConfig.ticker.toUpperCase() !== this.state.nativeCurrency.toUpperCase()) {
27
+ this.setNativeCurrency(networkState.providerConfig.ticker);
28
+ this.updateConversionRate();
29
+ }
30
+ });
31
+ }
32
+ setCommonDenomination(commonDenomination) {
33
+ this.update({
34
+ commonDenomination
35
+ });
36
+ }
37
+ getCommonDenomination() {
38
+ return this.state.commonDenomination;
39
+ }
40
+ setCommonDenominatorPrice(commonDenominatorPrice) {
41
+ this.update({
42
+ commonDenominatorPrice
43
+ });
44
+ }
45
+ getCommonDenominatorPrice() {
46
+ return this.state.commonDenominatorPrice;
47
+ }
48
+
49
+ /**
50
+ * Creates a new poll, using setInterval, to periodically call updateConversionRate. The id of the interval is
51
+ * stored at the controller's conversionInterval property. If it is called and such an id already exists, the
52
+ * previous interval is clear and a new one is created.
53
+ */
54
+ scheduleConversionInterval() {
55
+ if (this.conversionInterval) {
56
+ window.clearInterval(this.conversionInterval);
57
+ }
58
+ this.conversionInterval = window.setInterval(() => {
59
+ if (!idleTimeTracker.checkIfIdle()) {
60
+ this.updateConversionRate();
61
+ }
62
+ }, this.config.pollInterval);
63
+ }
64
+
65
+ /**
66
+ * Updates the conversionRate and conversionDate properties associated with the currentCurrency. Updated info is
67
+ * fetched from an external API
68
+ */
69
+ async updateConversionRate() {
70
+ const currentCurrency = this.getCurrentCurrency();
71
+ const nativeCurrency = this.getNativeCurrency();
72
+ const commonDenomination = this.getCommonDenomination();
73
+ const conversionRate = await this.retrieveConversionRate(nativeCurrency, currentCurrency, commonDenomination);
74
+ const currentCurrencyRate = Number.parseFloat(conversionRate[currentCurrency.toUpperCase()]);
75
+ const commonDenominationRate = Number.parseFloat(conversionRate[commonDenomination.toUpperCase()]);
76
+ // set conversion rate
77
+ if (currentCurrencyRate || commonDenominationRate) {
78
+ // ETC
79
+ this.setConversionRate(currentCurrencyRate);
80
+ this.setConversionDate(Math.floor(Date.now() / 1000).toString());
81
+ if (currentCurrency.toUpperCase() === commonDenomination.toUpperCase()) {
82
+ this.setCommonDenominatorPrice(currentCurrencyRate);
83
+ } else {
84
+ this.setCommonDenominatorPrice(commonDenominationRate);
85
+ }
86
+ } else {
87
+ this.setConversionRate(0);
88
+ this.setConversionDate("N/A");
89
+ }
90
+ }
91
+ async retrieveConversionRate(fromCurrency, toCurrency, commonDenomination) {
92
+ try {
93
+ let apiUrl = `${this.config.api}/currency?fsym=${fromCurrency.toUpperCase()}&tsyms=${toCurrency.toUpperCase()}`;
94
+ if (commonDenomination && commonDenomination.toUpperCase() !== toCurrency.toUpperCase()) {
95
+ apiUrl += `,${commonDenomination.toUpperCase()}`;
96
+ }
97
+ const parsedResponse = await get(apiUrl);
98
+ return parsedResponse;
99
+ } catch (error) {
100
+ log.error(error, `CurrencyController - updateCommonDenominatorPrice: Failed to query rate for currency: ${fromCurrency}/ ${toCurrency}`);
101
+ }
102
+ return {
103
+ [toCurrency.toUpperCase()]: "0",
104
+ [commonDenomination.toUpperCase()]: "0"
105
+ };
106
+ }
107
+ }
108
+
109
+ export { CurrencyController };