@toruslabs/ethereum-controllers 5.11.0 → 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 +80 -431
  2. package/dist/ethereumControllers.esm.js +30 -349
  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 +4 -4
  108. package/dist/types/utils/interfaces.d.ts +43 -23
  109. package/package.json +7 -7
  110. package/dist/types/Message/DecryptMessageController.d.ts +0 -20
  111. package/dist/types/Message/EncryptionPublicKeyController.d.ts +0 -20
@@ -0,0 +1,515 @@
1
+ 'use strict';
2
+
3
+ var _objectSpread = require('@babel/runtime/helpers/objectSpread2');
4
+ var _defineProperty = require('@babel/runtime/helpers/defineProperty');
5
+ var util = require('@ethereumjs/util');
6
+ var baseControllers = require('@toruslabs/base-controllers');
7
+ var auth = require('@web3auth/auth');
8
+ var BigNumber = require('bignumber.js');
9
+ var ethers = require('ethers');
10
+ var log = require('loglevel');
11
+ var constants = require('../utils/constants.js');
12
+ var conversionUtils = require('../utils/conversionUtils.js');
13
+ var helpers = require('../utils/helpers.js');
14
+ var NonceTracker = require('./NonceTracker.js');
15
+ var PendingTransactionTracker = require('./PendingTransactionTracker.js');
16
+ var TransactionGasUtil = require('./TransactionGasUtil.js');
17
+ var TransactionStateManager = require('./TransactionStateManager.js');
18
+ var TransactionUtils = require('./TransactionUtils.js');
19
+
20
+ class TransactionController extends TransactionStateManager.TransactionStateManager {
21
+ constructor({
22
+ config,
23
+ state,
24
+ provider,
25
+ blockTracker,
26
+ signEthTx,
27
+ getCurrentChainId,
28
+ getCurrentNetworkEIP1559Compatibility,
29
+ getProviderConfig,
30
+ getCurrentAccountEIP1559Compatibility,
31
+ getSelectedAddress,
32
+ getEIP1559GasFeeEstimates
33
+ }) {
34
+ super({
35
+ config,
36
+ state,
37
+ getCurrentChainId
38
+ });
39
+ _defineProperty(this, "getSelectedAddress", void 0);
40
+ _defineProperty(this, "getEIP1559GasFeeEstimates", void 0);
41
+ _defineProperty(this, "nonceTracker", void 0);
42
+ _defineProperty(this, "pendingTxTracker", void 0);
43
+ _defineProperty(this, "txGasUtil", void 0);
44
+ _defineProperty(this, "_getCurrentNetworkEIP1559Compatibility", void 0);
45
+ _defineProperty(this, "_getCurrentAccountEIP1559Compatibility", void 0);
46
+ _defineProperty(this, "getProviderConfig", void 0);
47
+ _defineProperty(this, "signEthTx", void 0);
48
+ _defineProperty(this, "provider", void 0);
49
+ _defineProperty(this, "blockTracker", void 0);
50
+ _defineProperty(this, "inProcessOfSigning", new Set());
51
+ _defineProperty(this, "getUnapprovedTxCount", () => Object.keys(this.getUnapprovedTxList()).length);
52
+ _defineProperty(this, "getPendingTxCount", account => this.getPendingTransactions(account).length);
53
+ this.blockTracker = blockTracker;
54
+ this.getProviderConfig = getProviderConfig;
55
+ this._getCurrentNetworkEIP1559Compatibility = getCurrentNetworkEIP1559Compatibility;
56
+ this._getCurrentAccountEIP1559Compatibility = getCurrentAccountEIP1559Compatibility;
57
+ this.getSelectedAddress = getSelectedAddress;
58
+ this.getEIP1559GasFeeEstimates = getEIP1559GasFeeEstimates;
59
+ this.signEthTx = signEthTx;
60
+ this.provider = provider;
61
+ this.txGasUtil = new TransactionGasUtil.TransactionGasUtil(this.provider, this.blockTracker);
62
+ this.nonceTracker = new NonceTracker.NonceTracker({
63
+ provider,
64
+ blockTracker,
65
+ getConfirmedTransactions: this.getConfirmedTransactions.bind(this),
66
+ getPendingTransactions: this.getSubmittedTransactions.bind(this) // nonce tracker should only care about submitted transactions
67
+ });
68
+ this.pendingTxTracker = new PendingTransactionTracker.PendingTransactionTracker({
69
+ provider,
70
+ nonceTracker: this.nonceTracker,
71
+ getPendingTransactions: this.getPendingTransactions.bind(this),
72
+ // pending tx tracker should only care about submitted and approved transactions
73
+ getConfirmedTransactions: this.getConfirmedTransactions.bind(this),
74
+ approveTransaction: this.approveTransaction.bind(this),
75
+ publishTransaction: rawTx => this.provider.request({
76
+ method: constants.METHOD_TYPES.ETH_SEND_RAW_TRANSACTION,
77
+ params: [rawTx]
78
+ })
79
+ });
80
+ this._setupListeners();
81
+ }
82
+ addTransactionUnapproved(txMeta) {
83
+ this.addTransactionToState(txMeta);
84
+ this.emit(`${txMeta.id}:unapproved`, txMeta);
85
+ }
86
+ async addNewUnapprovedTransaction(txParams, req) {
87
+ const txMeta = await this.createTransaction(txParams, req);
88
+ return this.processApproval(txMeta);
89
+ }
90
+ async processApproval(txMeta) {
91
+ return new Promise((resolve, reject) => {
92
+ const handleFinished = msg => {
93
+ if (msg.status === baseControllers.TransactionStatus.rejected) {
94
+ return reject(auth.providerErrors.userRejectedRequest(`Transaction Signature: User denied message signature`));
95
+ }
96
+ if (msg.status === baseControllers.TransactionStatus.failed) {
97
+ return reject(auth.rpcErrors.internal(`Transaction Signature: failed to sign message ${msg.error}`));
98
+ }
99
+ if (msg.status === baseControllers.TransactionStatus.submitted) {
100
+ return resolve(msg.transactionHash);
101
+ }
102
+ return reject(auth.rpcErrors.internal(`Transaction Signature: Unknown problem: ${JSON.stringify(txMeta.transaction)}`));
103
+ };
104
+ this.once(`${txMeta.id}:finished`, handleFinished);
105
+ });
106
+ }
107
+ async approveTransaction(transactionID) {
108
+ const txMeta = this.getTransaction(transactionID);
109
+ if (this.inProcessOfSigning.has(transactionID)) {
110
+ return;
111
+ }
112
+ this.inProcessOfSigning.add(transactionID);
113
+ let nonceLock;
114
+ try {
115
+ this.setTxStatusApproved(transactionID);
116
+ const fromAddress = txMeta.transaction.from;
117
+ const {
118
+ customNonceValue
119
+ } = txMeta.transaction;
120
+ const customNonceValueNumber = Number(customNonceValue);
121
+ nonceLock = await this.nonceTracker.getNonceLock(fromAddress);
122
+ // add nonce to txParams
123
+ // if txMeta has previousGasParams then it is a retry at same nonce with
124
+ // higher gas settings and therefor the nonce should not be recalculated
125
+ const nonce = nonceLock.nextNonce;
126
+ const customOrNonce = customNonceValueNumber === 0 ? customNonceValue : customNonceValue || nonce;
127
+ txMeta.transaction.nonce = util.addHexPrefix(customOrNonce.toString(16));
128
+ // add nonce debugging information to txMeta
129
+ txMeta.nonceDetails = nonceLock.nonceDetails;
130
+ this.updateTransactionInState(txMeta, "transactions#approveTransaction");
131
+ // sign transaction
132
+ const rawTx = await this.signTransaction(transactionID);
133
+ await this.publishTransaction(transactionID, rawTx);
134
+ nonceLock.releaseLock();
135
+ } catch (err) {
136
+ try {
137
+ this.setTxStatusFailed(transactionID, err);
138
+ } catch (err2) {
139
+ log.error(err2);
140
+ }
141
+ // must set transaction to submitted/failed before releasing lock
142
+ if (nonceLock) {
143
+ nonceLock.releaseLock();
144
+ }
145
+ // continue with error chain
146
+ throw err;
147
+ } finally {
148
+ this.inProcessOfSigning.delete(transactionID);
149
+ }
150
+ }
151
+ async signTransaction(txId) {
152
+ const txMeta = this.getTransaction(txId);
153
+ const chainId = this.getCurrentChainId();
154
+ const type = TransactionUtils.isEIP1559Transaction(txMeta) ? constants.TRANSACTION_ENVELOPE_TYPES.FEE_MARKET : constants.TRANSACTION_ENVELOPE_TYPES.LEGACY;
155
+ const txParams = _objectSpread(_objectSpread({}, txMeta.transaction), {}, {
156
+ type,
157
+ chainId,
158
+ gasLimit: txMeta.transaction.gas
159
+ });
160
+ const fromAddress = txParams.from;
161
+ const common = await this.getCommonConfiguration(fromAddress);
162
+ const {
163
+ TransactionFactory
164
+ } = await import('@ethereumjs/tx');
165
+ // TODO: fix this when @ethereumjs/tx is updated.
166
+ const unsignedEthTx = TransactionFactory.fromTxData(txParams, {
167
+ common
168
+ });
169
+ const signedEthTx = await this.signEthTx(unsignedEthTx, fromAddress);
170
+ txMeta.r = util.addHexPrefix(signedEthTx.r.toString(16));
171
+ txMeta.s = util.addHexPrefix(signedEthTx.s.toString(16));
172
+ txMeta.v = util.addHexPrefix(signedEthTx.v.toString(16));
173
+ this.updateTransactionInState(txMeta, "transactions#signTransaction: add r, s, v values");
174
+ this.setTxStatusSigned(txId);
175
+ const rawTx = util.addHexPrefix(Buffer.from(signedEthTx.serialize()).toString("hex"));
176
+ return rawTx;
177
+ }
178
+ async publishTransaction(txId, rawTx) {
179
+ const txMeta = this.getTransaction(txId);
180
+ txMeta.rawTransaction = rawTx;
181
+ this.updateTransactionInState(txMeta, "transactions#publishTransaction");
182
+ let txHash;
183
+ try {
184
+ txHash = await this.provider.request({
185
+ method: constants.METHOD_TYPES.ETH_SEND_RAW_TRANSACTION,
186
+ params: [rawTx]
187
+ });
188
+ } catch (error) {
189
+ if (error.message.toLowerCase().includes("known transaction")) {
190
+ txHash = ethers.keccak256(util.addHexPrefix(rawTx));
191
+ txHash = util.addHexPrefix(txHash);
192
+ } else {
193
+ throw error;
194
+ }
195
+ }
196
+ this.setTxHash(txId, txHash);
197
+ this.setTxStatusSubmitted(txId);
198
+ }
199
+ async confirmTransaction(params) {
200
+ const {
201
+ txId,
202
+ txReceipt
203
+ } = params;
204
+ log.info(params, "confirm params");
205
+ const txMeta = this.getTransaction(txId);
206
+ if (!txMeta) return;
207
+ try {
208
+ txMeta.txReceipt = _objectSpread({}, txReceipt);
209
+ this.setTxStatusConfirmed(txId);
210
+ this.markNonceDuplicatesDropped(txId);
211
+ this.updateTransactionInState(txMeta, "transactions#confirmTransaction - add txReceipt");
212
+ } catch (error) {
213
+ log.error(error);
214
+ }
215
+ }
216
+ cancelTransaction(transactionID) {
217
+ throw new Error(`Method not implemented. ${transactionID}`);
218
+ }
219
+ async getEIP1559Compatibility(fromAddress) {
220
+ const currentNetworkIsCompatible = await this._getCurrentNetworkEIP1559Compatibility();
221
+ const fromAccountIsCompatible = await this._getCurrentAccountEIP1559Compatibility(fromAddress);
222
+ return currentNetworkIsCompatible && fromAccountIsCompatible;
223
+ }
224
+ async addTransactionGasDefaults(txMeta) {
225
+ let updateTxMeta = txMeta;
226
+ try {
227
+ updateTxMeta = await this.addTxGasDefaults(txMeta);
228
+ } catch (error) {
229
+ log.warn(error);
230
+ updateTxMeta = this.getTransaction(txMeta.id);
231
+ updateTxMeta.loadingDefaults = false;
232
+ this.updateTransactionInState(txMeta, "Failed to calculate gas defaults.");
233
+ throw error;
234
+ }
235
+ updateTxMeta.loadingDefaults = false;
236
+ this.updateTransactionInState(updateTxMeta, "Added new unapproved transaction.");
237
+ return updateTxMeta;
238
+ }
239
+ async addTxGasDefaults(txMeta) {
240
+ const eip1559Compatibility = txMeta.transaction.type !== constants.TRANSACTION_ENVELOPE_TYPES.LEGACY && (await this.getEIP1559Compatibility());
241
+ const {
242
+ gasPrice: defaultGasPrice,
243
+ maxFeePerGas: defaultMaxFeePerGas,
244
+ maxPriorityFeePerGas: defaultMaxPriorityFeePerGas
245
+ } = await this.getDefaultGasFees(txMeta, eip1559Compatibility);
246
+ const {
247
+ gasLimit: defaultGasLimit,
248
+ simulationFails
249
+ } = await this.getDefaultGasLimit(txMeta);
250
+ txMeta = this.getTransaction(txMeta.id);
251
+ if (simulationFails) {
252
+ txMeta.simulationFails = simulationFails;
253
+ }
254
+ if (eip1559Compatibility) {
255
+ // If the dapp has suggested a gas price, but no maxFeePerGas or maxPriorityFeePerGas
256
+ // then we set maxFeePerGas and maxPriorityFeePerGas to the suggested gasPrice.
257
+ if (txMeta.transaction.gasPrice && !txMeta.transaction.maxFeePerGas && !txMeta.transaction.maxPriorityFeePerGas) {
258
+ txMeta.transaction.maxFeePerGas = txMeta.transaction.gasPrice;
259
+ // If the dapp has suggested a gas price, but no maxFeePerGas or maxPriorityFeePerGas
260
+ // then we set maxFeePerGas to the suggested gasPrice.
261
+
262
+ txMeta.transaction.maxPriorityFeePerGas = helpers.bnLessThan(typeof defaultMaxPriorityFeePerGas === "string" ? util.stripHexPrefix(defaultMaxPriorityFeePerGas) : defaultMaxPriorityFeePerGas, typeof txMeta.transaction.gasPrice === "string" ? util.stripHexPrefix(txMeta.transaction.gasPrice) : txMeta.transaction.gasPrice) ? defaultMaxPriorityFeePerGas : txMeta.transaction.gasPrice;
263
+ } else {
264
+ if (defaultMaxFeePerGas && !txMeta.transaction.maxFeePerGas) {
265
+ // If the dapp has not set the gasPrice or the maxFeePerGas, then we set maxFeePerGas
266
+ // with the one returned by the gasFeeController, if that is available.
267
+ txMeta.transaction.maxFeePerGas = defaultMaxFeePerGas;
268
+ }
269
+ if (defaultMaxPriorityFeePerGas && !txMeta.transaction.maxPriorityFeePerGas) {
270
+ // If the dapp has not set the gasPrice or the maxPriorityFeePerGas, then we set maxPriorityFeePerGas
271
+ // with the one returned by the gasFeeController, if that is available.
272
+ txMeta.transaction.maxPriorityFeePerGas = defaultMaxPriorityFeePerGas;
273
+ }
274
+ if (defaultGasPrice && !txMeta.transaction.maxFeePerGas) {
275
+ // If the dapp has not set the gasPrice or the maxFeePerGas, and no maxFeePerGas is available
276
+ // from the gasFeeController, then we set maxFeePerGas to the defaultGasPrice, assuming it is
277
+ // available.
278
+ txMeta.transaction.maxFeePerGas = defaultGasPrice;
279
+ }
280
+ if (txMeta.transaction.maxFeePerGas && !txMeta.transaction.maxPriorityFeePerGas) {
281
+ // If the dapp has not set the gasPrice or the maxPriorityFeePerGas, and no maxPriorityFeePerGas is
282
+ // available from the gasFeeController, then we set maxPriorityFeePerGas to
283
+ // txMeta.transaction.maxFeePerGas, which will either be the gasPrice from the controller, the maxFeePerGas
284
+ // set by the dapp, or the maxFeePerGas from the controller.
285
+ txMeta.transaction.maxPriorityFeePerGas = txMeta.transaction.maxFeePerGas;
286
+ }
287
+ }
288
+
289
+ // We remove the gasPrice param entirely when on an eip1559 compatible network
290
+
291
+ delete txMeta.transaction.gasPrice;
292
+ } else {
293
+ // We ensure that maxFeePerGas and maxPriorityFeePerGas are not in the transaction params
294
+ // when not on a EIP1559 compatible network
295
+
296
+ delete txMeta.transaction.maxPriorityFeePerGas;
297
+ delete txMeta.transaction.maxFeePerGas;
298
+ }
299
+
300
+ // If we have gotten to this point, and none of gasPrice, maxPriorityFeePerGas or maxFeePerGas are
301
+ // set on transaction, it means that either we are on a non-EIP1559 network and the dapp didn't suggest
302
+ // a gas price, or we are on an EIP1559 network, and none of gasPrice, maxPriorityFeePerGas or maxFeePerGas
303
+ // were available from either the dapp or the network.
304
+ if (defaultGasPrice && !txMeta.transaction.gasPrice && !txMeta.transaction.maxPriorityFeePerGas && !txMeta.transaction.maxFeePerGas) {
305
+ txMeta.transaction.gasPrice = defaultGasPrice;
306
+ }
307
+ if (defaultGasLimit && !txMeta.transaction.gas) {
308
+ txMeta.transaction.gas = defaultGasLimit;
309
+ }
310
+ return txMeta;
311
+ }
312
+ setTxHash(txId, txHash) {
313
+ // Add the tx hash to the persisted meta-tx object
314
+ const txMeta = this.getTransaction(txId);
315
+ txMeta.transactionHash = txHash;
316
+ this.updateTransactionInState(txMeta, "transactions#setTxHash");
317
+ }
318
+ async getDefaultGasFees(txMeta, eip1559Compatibility) {
319
+ if (!eip1559Compatibility && txMeta.transaction.gasPrice || eip1559Compatibility && txMeta.transaction.maxFeePerGas && txMeta.transaction.maxPriorityFeePerGas) {
320
+ return {};
321
+ }
322
+ try {
323
+ const {
324
+ gasFeeEstimates,
325
+ gasEstimateType
326
+ } = await this.getEIP1559GasFeeEstimates();
327
+ if (eip1559Compatibility && gasEstimateType === constants.GAS_ESTIMATE_TYPES.FEE_MARKET) {
328
+ // this is in dec gwei
329
+ const {
330
+ medium: {
331
+ suggestedMaxPriorityFeePerGas,
332
+ suggestedMaxFeePerGas
333
+ } = {}
334
+ } = gasFeeEstimates;
335
+ if (suggestedMaxPriorityFeePerGas && suggestedMaxFeePerGas) {
336
+ return {
337
+ // send to controller in hex wei
338
+ maxFeePerGas: util.addHexPrefix(conversionUtils.decGWEIToHexWEI(new BigNumber(suggestedMaxFeePerGas)).toString(16)),
339
+ maxPriorityFeePerGas: util.addHexPrefix(conversionUtils.decGWEIToHexWEI(new BigNumber(suggestedMaxPriorityFeePerGas)).toString(16))
340
+ };
341
+ }
342
+ } else if (gasEstimateType === constants.GAS_ESTIMATE_TYPES.LEGACY) {
343
+ const {
344
+ medium
345
+ } = gasFeeEstimates;
346
+ // The LEGACY type includes low, medium and high estimates of
347
+ // gas price values.
348
+ return {
349
+ gasPrice: util.addHexPrefix(conversionUtils.decGWEIToHexWEI(new BigNumber(medium)).toString(16))
350
+ };
351
+ } else if (gasEstimateType === constants.GAS_ESTIMATE_TYPES.ETH_GASPRICE) {
352
+ const {
353
+ gasPrice
354
+ } = gasFeeEstimates;
355
+ // The ETH_GASPRICE type just includes a single gas price property,
356
+ // which we can assume was retrieved from eth_gasPrice
357
+ return {
358
+ gasPrice: util.addHexPrefix(conversionUtils.decGWEIToHexWEI(new BigNumber(gasPrice)).toString(16))
359
+ };
360
+ }
361
+ } catch (error) {
362
+ log.error(error);
363
+ }
364
+ const gasPrice = await this.provider.request({
365
+ method: constants.METHOD_TYPES.ETH_GET_GAS_PRICE
366
+ });
367
+ return {
368
+ gasPrice: gasPrice && util.addHexPrefix(gasPrice)
369
+ };
370
+ }
371
+ async getDefaultGasLimit(txMeta) {
372
+ const chainId = this.getCurrentChainId();
373
+ const customNetworkGasBuffer = constants.CHAIN_ID_TO_GAS_LIMIT_BUFFER_MAP[chainId];
374
+ const chainType = helpers.getChainType(chainId);
375
+ if (txMeta.transaction.gas) {
376
+ return {};
377
+ }
378
+ if (txMeta.transaction.to && txMeta.transactionCategory === baseControllers.TRANSACTION_TYPES.SENT_ETHER && chainType !== "custom" && !txMeta.transaction.data) {
379
+ // This is a standard ether simple send, gas requirement is exactly 21k
380
+ return {
381
+ gasLimit: helpers.GAS_LIMITS.SIMPLE
382
+ };
383
+ }
384
+ const {
385
+ blockGasLimit,
386
+ estimatedGasHex,
387
+ simulationFails
388
+ } = await this.txGasUtil.analyzeGasUsage(txMeta);
389
+
390
+ // add additional gas buffer to our estimation for safety
391
+ const gasLimit = this.txGasUtil.addGasBuffer(util.addHexPrefix(estimatedGasHex), blockGasLimit, customNetworkGasBuffer);
392
+ return {
393
+ gasLimit,
394
+ simulationFails
395
+ };
396
+ }
397
+ async createTransaction(txParameters, req) {
398
+ const normalizedTxParameters = TransactionUtils.normalizeTxParameters(txParameters);
399
+ const eip1559Compatibility = await this.getEIP1559Compatibility(txParameters.from);
400
+ TransactionUtils.validateTxParameters(normalizedTxParameters, eip1559Compatibility);
401
+ let txMeta = this.generateTxMeta({
402
+ transaction: normalizedTxParameters,
403
+ origin: req.origin
404
+ });
405
+ const {
406
+ type,
407
+ category,
408
+ methodParams
409
+ } = await TransactionUtils.determineTransactionType(txParameters, this.provider);
410
+ txMeta.contractType = type;
411
+ txMeta.transactionCategory = category;
412
+ txMeta.methodParams = methodParams;
413
+ txMeta.transaction.value = txMeta.transaction.value ? util.addHexPrefix(txMeta.transaction.value) : "0x0";
414
+ this.emit(`${txMeta.id}:unapproved`, txMeta);
415
+ txMeta = this.addTransactionToState(txMeta);
416
+ txMeta = await this.addTransactionGasDefaults(txMeta);
417
+ this.emit(baseControllers.TX_EVENTS.TX_UNAPPROVED, {
418
+ txMeta,
419
+ req
420
+ });
421
+ return txMeta;
422
+ }
423
+ _setupListeners() {
424
+ this.setupBlockTrackerListener();
425
+ this.pendingTxTracker.on(baseControllers.TX_EVENTS.TX_WARNING, data => {
426
+ this.updateTransactionInState(data.txMeta);
427
+ });
428
+ this.pendingTxTracker.on(baseControllers.TX_EVENTS.TX_DROPPED, data => this.setTxStatusDropped(data.txId));
429
+ this.pendingTxTracker.on(baseControllers.TX_EVENTS.TX_BLOCK_UPDATE, ({
430
+ txMeta,
431
+ latestBlockNumber
432
+ }) => {
433
+ if (!txMeta.firstRetryBlockNumber) {
434
+ txMeta.firstRetryBlockNumber = latestBlockNumber;
435
+ this.updateTransactionInState(txMeta);
436
+ }
437
+ });
438
+ this.pendingTxTracker.on(baseControllers.TX_EVENTS.TX_RETRY, data => {
439
+ if (!("retryCount" in data.txMeta)) {
440
+ data.txMeta.retryCount = 0;
441
+ }
442
+ data.txMeta.retryCount += 1;
443
+ this.updateTransactionInState(data.txMeta);
444
+ });
445
+ this.pendingTxTracker.on(baseControllers.TX_EVENTS.TX_FAILED, data => {
446
+ this.setTxStatusFailed(data.txId, data.error);
447
+ });
448
+ this.pendingTxTracker.on(baseControllers.TX_EVENTS.TX_CONFIRMED, data => this.confirmTransaction(data));
449
+ }
450
+ setupBlockTrackerListener() {
451
+ let listenersAreActive = false;
452
+ const latestBlockHandler = this.onLatestBlock.bind(this);
453
+ this.on(baseControllers.TX_EVENTS.TX_STATUS_UPDATE, () => {
454
+ const pendingTxs = this.getPendingTransactions();
455
+ if (!listenersAreActive && pendingTxs.length > 0) {
456
+ this.blockTracker.on("latest", latestBlockHandler);
457
+ listenersAreActive = true;
458
+ } else if (listenersAreActive && !pendingTxs.length) {
459
+ this.blockTracker.removeListener("latest", latestBlockHandler);
460
+ listenersAreActive = false;
461
+ }
462
+ });
463
+ }
464
+ async onLatestBlock(blockNumber) {
465
+ try {
466
+ await this.pendingTxTracker.updatePendingTxs();
467
+ } catch (error) {
468
+ log.error(error);
469
+ }
470
+ try {
471
+ await this.pendingTxTracker.resubmitPendingTxs(blockNumber);
472
+ } catch (error) {
473
+ log.error(error);
474
+ }
475
+ }
476
+ async getCommonConfiguration(fromAddress) {
477
+ const {
478
+ chainId,
479
+ displayName
480
+ } = this.getProviderConfig();
481
+ const supportsEIP1559 = await this.getEIP1559Compatibility(fromAddress);
482
+ const {
483
+ Common,
484
+ Hardfork
485
+ } = await import('@ethereumjs/common');
486
+ const hardfork = supportsEIP1559 ? Hardfork.Paris : Hardfork.Berlin;
487
+ return Common.custom({
488
+ chainId: chainId === "loading" ? 0 : Number.parseInt(chainId, 16),
489
+ defaultHardfork: hardfork,
490
+ name: displayName,
491
+ networkId: chainId === "loading" ? 0 : Number.parseInt(chainId, 16)
492
+ });
493
+ }
494
+ markNonceDuplicatesDropped(txId) {
495
+ const txMeta = this.getTransaction(txId);
496
+ const {
497
+ nonce,
498
+ from
499
+ } = txMeta.transaction;
500
+ const sameNonceTxs = this.getTransactions({
501
+ searchCriteria: {
502
+ from,
503
+ nonce
504
+ }
505
+ });
506
+ if (!sameNonceTxs.length) return;
507
+ sameNonceTxs.forEach(tx => {
508
+ if (tx.id === txId) return;
509
+ this.updateTransactionInState(txMeta, "transactions/pending-tx-tracker#event: tx:confirmed reference to confirmed txHash with same nonce");
510
+ if (tx.status !== baseControllers.TransactionStatus.failed) this.setTxStatusDropped(tx.id);
511
+ });
512
+ }
513
+ }
514
+
515
+ exports.TransactionController = TransactionController;
@@ -0,0 +1,81 @@
1
+ 'use strict';
2
+
3
+ var _defineProperty = require('@babel/runtime/helpers/defineProperty');
4
+ var util = require('@ethereumjs/util');
5
+ var baseControllers = require('@toruslabs/base-controllers');
6
+ var BN = require('bn.js');
7
+ var log = require('loglevel');
8
+
9
+ class TransactionGasUtil {
10
+ constructor(provider, blockTracker) {
11
+ _defineProperty(this, "provider", void 0);
12
+ _defineProperty(this, "blockTracker", void 0);
13
+ this.provider = provider;
14
+ this.blockTracker = blockTracker;
15
+ }
16
+ async analyzeGasUsage(txMeta) {
17
+ const block = await this.blockTracker.getLatestBlock();
18
+ // fallback to block gasLimit
19
+ const blockGasLimitBN = new BN.BN(util.stripHexPrefix(block.gasLimit), 16);
20
+ const saferGasLimitBN = blockGasLimitBN.mul(new BN.BN(19)).div(new BN.BN(20));
21
+ let estimatedGasHex = util.addHexPrefix(saferGasLimitBN.toString("hex"));
22
+ let simulationFails;
23
+ try {
24
+ estimatedGasHex = await this.estimateTxGas(txMeta);
25
+ } catch (error) {
26
+ log.warn(error);
27
+ simulationFails = {
28
+ reason: error.message,
29
+ errorKey: error.errorKey,
30
+ debug: {
31
+ blockNumber: block.idempotencyKey,
32
+ blockGasLimit: block.gasLimit
33
+ }
34
+ };
35
+ }
36
+ return {
37
+ blockGasLimit: block.gasLimit,
38
+ estimatedGasHex,
39
+ simulationFails
40
+ };
41
+ }
42
+
43
+ /**
44
+ Adds a gas buffer with out exceeding the block gas limit
45
+ */
46
+ addGasBuffer(initialGasLimitHex, blockGasLimitHex, multiplier = 1.5) {
47
+ const initialGasLimitBn = new BN.BN(util.stripHexPrefix(initialGasLimitHex), 16);
48
+ const blockGasLimitBn = new BN.BN(util.stripHexPrefix(blockGasLimitHex), 16);
49
+ const upperGasLimitBn = blockGasLimitBn.muln(0.9);
50
+ const bufferedGasLimitBn = initialGasLimitBn.muln(multiplier);
51
+
52
+ // if initialGasLimit is above blockGasLimit, dont modify it
53
+ if (initialGasLimitBn.gt(upperGasLimitBn)) return util.addHexPrefix(initialGasLimitBn.toString("hex"));
54
+ // if bufferedGasLimit is below blockGasLimit, use bufferedGasLimit
55
+ if (bufferedGasLimitBn.lt(upperGasLimitBn)) return util.addHexPrefix(bufferedGasLimitBn.toString("hex"));
56
+ // otherwise use blockGasLimit
57
+ return util.addHexPrefix(upperGasLimitBn.toString("hex"));
58
+ }
59
+
60
+ /**
61
+ Estimates the tx's gas usage
62
+ */
63
+ async estimateTxGas(txMeta) {
64
+ const txParams = baseControllers.cloneDeep(txMeta.transaction);
65
+
66
+ // `eth_estimateGas` can fail if the user has insufficient balance for the
67
+ // value being sent, or for the gas cost. We don't want to check their
68
+ // balance here, we just want the gas estimate. The gas price is removed
69
+ // to skip those balance checks. We check balance elsewhere. We also delete
70
+ // maxFeePerGas and maxPriorityFeePerGas to support EIP-1559 txs.
71
+ delete txParams.gasPrice;
72
+ delete txParams.maxFeePerGas;
73
+ delete txParams.maxPriorityFeePerGas;
74
+ return this.provider.request({
75
+ method: "eth_estimateGas",
76
+ params: [txParams]
77
+ });
78
+ }
79
+ }
80
+
81
+ exports.TransactionGasUtil = TransactionGasUtil;
@@ -0,0 +1,42 @@
1
+ 'use strict';
2
+
3
+ var _objectSpread = require('@babel/runtime/helpers/objectSpread2');
4
+ var baseControllers = require('@toruslabs/base-controllers');
5
+ var jsonDiffer = require('fast-json-patch');
6
+
7
+ /**
8
+ Generates an array of history objects sense the previous state.
9
+ The object has the keys
10
+ op (the operation performed),
11
+ path (the key and if a nested object then each key will be seperated with a `/`)
12
+ value
13
+ with the first entry having the note and a timestamp when the change took place
14
+ */
15
+ function generateHistoryEntry(previousState, newState, note) {
16
+ const entry = jsonDiffer.compare(previousState, newState);
17
+ // Add a note to the first op, since it breaks if we append it to the entry
18
+ if (entry[0]) {
19
+ if (note) {
20
+ entry[0].note = note;
21
+ }
22
+ entry[0].timestamp = Date.now();
23
+ }
24
+ return entry;
25
+ }
26
+
27
+ /**
28
+ Recovers previous txMeta state obj
29
+ */
30
+ function replayHistory(_shortHistory) {
31
+ const shortHistory = baseControllers.cloneDeep(_shortHistory);
32
+ return shortHistory.reduce((val, entry) => jsonDiffer.applyPatch(val, entry).newDocument);
33
+ }
34
+ function snapshotFromTxMeta(txMeta) {
35
+ const shallow = _objectSpread({}, txMeta);
36
+ delete shallow.history;
37
+ return baseControllers.cloneDeep(shallow);
38
+ }
39
+
40
+ exports.generateHistoryEntry = generateHistoryEntry;
41
+ exports.replayHistory = replayHistory;
42
+ exports.snapshotFromTxMeta = snapshotFromTxMeta;