@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,296 @@
1
+ 'use strict';
2
+
3
+ var _objectSpread = require('@babel/runtime/helpers/objectSpread2');
4
+ var baseControllers = require('@toruslabs/base-controllers');
5
+ var lodashUtils = require('../utils/lodashUtils.js');
6
+ var TransactionStateHistoryHelper = require('./TransactionStateHistoryHelper.js');
7
+ var TransactionUtils = require('./TransactionUtils.js');
8
+
9
+ class TransactionStateManager extends baseControllers.BaseTransactionStateManager {
10
+ constructor({
11
+ config,
12
+ state,
13
+ getCurrentChainId
14
+ }) {
15
+ super({
16
+ config,
17
+ state,
18
+ getCurrentChainId
19
+ });
20
+ }
21
+ generateTxMeta(opts = {}) {
22
+ var _opts$transaction;
23
+ const chainId = this.getCurrentChainId();
24
+ if (chainId === "loading") throw new Error("Torus is having trouble connecting to the network");
25
+ let dappSuggestedGasFees = null;
26
+
27
+ // If we are dealing with a transaction suggested by a dapp and not
28
+ // an internally created transaction, we need to keep record of
29
+ // the originally submitted gasParams.
30
+ if (opts.transaction && typeof opts.origin === "string" && opts.origin !== "torus") {
31
+ if (typeof opts.transaction.gasPrice !== "undefined") {
32
+ dappSuggestedGasFees = {
33
+ gasPrice: opts.transaction.gasPrice
34
+ };
35
+ } else if (typeof opts.transaction.maxFeePerGas !== "undefined" || typeof opts.transaction.maxPriorityFeePerGas !== "undefined") {
36
+ dappSuggestedGasFees = {
37
+ maxPriorityFeePerGas: opts.transaction.maxPriorityFeePerGas,
38
+ maxFeePerGas: opts.transaction.maxFeePerGas
39
+ };
40
+ }
41
+ if (typeof opts.transaction.gas !== "undefined") {
42
+ dappSuggestedGasFees = _objectSpread(_objectSpread({}, dappSuggestedGasFees), {}, {
43
+ gas: opts.transaction.gas
44
+ });
45
+ }
46
+ }
47
+ return _objectSpread({
48
+ id: ((_opts$transaction = opts.transaction) === null || _opts$transaction === void 0 ? void 0 : _opts$transaction.id) || baseControllers.randomId(),
49
+ time: Date.now(),
50
+ status: baseControllers.TransactionStatus.unapproved,
51
+ loadingDefaults: true,
52
+ chainId,
53
+ dappSuggestedGasFees
54
+ }, opts);
55
+ }
56
+ addTransactionToState(txMeta) {
57
+ if (txMeta.transaction) {
58
+ txMeta.transaction = TransactionUtils.normalizeAndValidateTxParams(txMeta.transaction, false);
59
+ }
60
+ this.once(`${txMeta.id}:signed`, () => {
61
+ this.removeAllListeners(`${txMeta.id}:rejected`);
62
+ });
63
+ this.once(`${txMeta.id}:rejected`, () => {
64
+ this.removeAllListeners(`${txMeta.id}:signed`);
65
+ });
66
+ // initialize history
67
+ txMeta.history = [];
68
+ // capture initial snapshot of txMeta for history
69
+ const snapshot = TransactionStateHistoryHelper.snapshotFromTxMeta(txMeta);
70
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
71
+ txMeta.history.push(snapshot);
72
+ const transactions = this.getTransactions({
73
+ filterToCurrentNetwork: false
74
+ });
75
+ const {
76
+ txHistoryLimit
77
+ } = this.config;
78
+
79
+ // checks if the length of the tx history is longer then desired persistence
80
+ // limit and then if it is removes the oldest confirmed or rejected tx.
81
+ // Pending or unapproved transactions will not be removed by this
82
+ // operation. For safety of presenting a fully functional transaction UI
83
+ // representation, this function will not break apart transactions with the
84
+ // same nonce, per network. Not accounting for transactions of the same
85
+ // nonce and network combo can result in confusing or broken experiences
86
+ // in the UI.
87
+ //
88
+ // we will send UI only collected groups of transactions *per page* so at
89
+ // some point in the future, this persistence limit can be adjusted. When
90
+ // we do that I think we should figure out a better storage solution for
91
+ // transaction history entries.
92
+ const nonceNetworkSet = new Set();
93
+ const txsToDelete = transactions.reverse().filter(tx => {
94
+ const {
95
+ nonce
96
+ } = tx.transaction;
97
+ const {
98
+ chainId,
99
+ status
100
+ } = tx;
101
+ const key = `${nonce}-${chainId}`;
102
+ if (nonceNetworkSet.has(key)) {
103
+ return false;
104
+ }
105
+ if (nonceNetworkSet.size < txHistoryLimit - 1 || TransactionUtils.getFinalStates().includes(status) === false) {
106
+ nonceNetworkSet.add(key);
107
+ return false;
108
+ }
109
+ return true;
110
+ }).map(tx => tx.id);
111
+ this._deleteTransactions(txsToDelete);
112
+ this._addTransactionsToState([txMeta]);
113
+ return txMeta;
114
+ }
115
+
116
+ /**
117
+ Removes transaction from the given address for the current network
118
+ from the txList
119
+ */
120
+ wipeTransactions(address) {
121
+ const {
122
+ transactions
123
+ } = this.state;
124
+ const chainId = this.getCurrentChainId();
125
+ this.update({
126
+ transactions: baseControllers.omitBy(transactions, txMeta => {
127
+ const transactionMatch = baseControllers.transactionMatchesNetwork(txMeta, chainId);
128
+ return txMeta.transaction.from === address && transactionMatch;
129
+ })
130
+ });
131
+ }
132
+ getTransactions({
133
+ searchCriteria = {},
134
+ initialList = undefined,
135
+ filterToCurrentNetwork = true,
136
+ limit = undefined
137
+ } = {}) {
138
+ const chainId = this.getCurrentChainId();
139
+ // searchCriteria is an object that might have values that aren't predicate
140
+ // methods. When providing any other value type (string, number, etc), we
141
+ // consider this shorthand for "check the value at key for strict equality
142
+ // with the provided value". To conform this object to be only methods, we
143
+ // mapValues (lodash) such that every value on the object is a method that
144
+ // returns a boolean.
145
+ const predicateMethods = lodashUtils.mapValues(searchCriteria, predicate => typeof predicate === "function" ? predicate : v => v === predicate);
146
+
147
+ // If an initial list is provided we need to change it back into an object
148
+ // first, so that it matches the shape of our state. This is done by the
149
+ // lodash keyBy method. This is the edge case for this method, typically
150
+ // initialList will be undefined.
151
+ const transactionsToFilter = initialList ? lodashUtils.keyBy(initialList, "id") : this.state.transactions;
152
+
153
+ // Combine sortBy and pickBy to transform our state object into an array of
154
+ // matching transactions that are sorted by time.
155
+ const filteredTransactions = lodashUtils.sortBy(Object.values(baseControllers.pickBy(transactionsToFilter, txMeta => {
156
+ // default matchesCriteria to the value of transactionMatchesNetwork
157
+ // when filterToCurrentNetwork is true.
158
+ const transactionMatches = baseControllers.transactionMatchesNetwork(txMeta, chainId);
159
+ if (filterToCurrentNetwork && !transactionMatches) {
160
+ return false;
161
+ }
162
+ // iterate over the predicateMethods keys to check if the transaction
163
+ // matches the searchCriteria
164
+ for (const [key, predicate] of Object.entries(predicateMethods)) {
165
+ // We return false early as soon as we know that one of the specified
166
+ // search criteria do not match the transaction. This prevents
167
+ // needlessly checking all criteria when we already know the criteria
168
+ // are not fully satisfied. We check both txParams and the base
169
+ // object as predicate keys can be either.
170
+ if (key in txMeta.transaction) {
171
+ if (predicate(txMeta.transaction[key]) === false) {
172
+ return false;
173
+ }
174
+ } else if (predicate(txMeta[key]) === false) {
175
+ return false;
176
+ }
177
+ }
178
+ return true;
179
+ })), "time");
180
+ if (limit !== undefined) {
181
+ // We need to have all transactions of a given nonce in order to display
182
+ // necessary details in the UI. We use the size of this set to determine
183
+ // whether we have reached the limit provided, thus ensuring that all
184
+ // transactions of nonces we include will be sent to the UI.
185
+ const nonces = new Set();
186
+ const txs = [];
187
+ // By default, the transaction list we filter from is sorted by time ASC.
188
+ // To ensure that filtered results prefers the newest transactions we
189
+ // iterate from right to left, inserting transactions into front of a new
190
+ // array. The original order is preserved, but we ensure that newest txs
191
+ // are preferred.
192
+ for (let i = filteredTransactions.length - 1; i > -1; i -= 1) {
193
+ const txMeta = filteredTransactions[i];
194
+ const {
195
+ nonce
196
+ } = txMeta.transaction;
197
+ if (!nonces.has(nonce)) {
198
+ if (nonces.size < limit) {
199
+ nonces.add(nonce);
200
+ } else {
201
+ continue;
202
+ }
203
+ }
204
+ // Push transaction into the beginning of our array to ensure the
205
+ // original order is preserved.
206
+ txs.unshift(txMeta);
207
+ }
208
+ return txs;
209
+ }
210
+ return filteredTransactions;
211
+ }
212
+ getApprovedTransactions(address) {
213
+ const searchCriteria = {
214
+ status: baseControllers.TransactionStatus.approved
215
+ };
216
+ if (address) {
217
+ searchCriteria.from = address;
218
+ }
219
+ return this.getTransactions({
220
+ searchCriteria
221
+ });
222
+ }
223
+ getSubmittedTransactions(address) {
224
+ const searchCriteria = {
225
+ status: baseControllers.TransactionStatus.submitted
226
+ };
227
+ if (address) {
228
+ searchCriteria.from = address;
229
+ }
230
+ return this.getTransactions({
231
+ searchCriteria
232
+ });
233
+ }
234
+ getPendingTransactions(address) {
235
+ const submitted = this.getSubmittedTransactions(address);
236
+ const approved = this.getApprovedTransactions(address);
237
+ return [...submitted, ...approved];
238
+ }
239
+ getConfirmedTransactions(address) {
240
+ const searchCriteria = {
241
+ status: baseControllers.TransactionStatus.confirmed
242
+ };
243
+ if (address) {
244
+ searchCriteria.from = address;
245
+ }
246
+ return this.getTransactions({
247
+ searchCriteria
248
+ });
249
+ }
250
+ getUnapprovedTxList() {
251
+ const chainId = this.getCurrentChainId();
252
+ return baseControllers.pickBy(this.state.transactions, transaction => {
253
+ const transactionMatches = baseControllers.transactionMatchesNetwork(transaction, chainId);
254
+ return transaction.status === baseControllers.TransactionStatus.unapproved && transactionMatches;
255
+ });
256
+ }
257
+ updateTransactionInState(txMeta, note) {
258
+ // validate txParams
259
+ if (txMeta.transaction) {
260
+ txMeta.transaction = TransactionUtils.normalizeAndValidateTxParams(txMeta.transaction, false);
261
+ }
262
+
263
+ // create txMeta snapshot for history
264
+ const currentState = TransactionStateHistoryHelper.snapshotFromTxMeta(txMeta);
265
+ // recover previous tx state obj
266
+ const previousState = TransactionStateHistoryHelper.replayHistory(txMeta.history);
267
+ // generate history entry and add to history
268
+ const entry = TransactionStateHistoryHelper.generateHistoryEntry(previousState, currentState, note);
269
+ if (entry.length > 0) {
270
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
271
+ txMeta.history.push(entry);
272
+ }
273
+
274
+ // commit txMeta to state
275
+ this.updateTransaction(txMeta);
276
+ }
277
+ _setTransactionStatus(txId, status, isFinalStep) {
278
+ const txMeta = this.getTransaction(txId);
279
+ if (!txMeta) {
280
+ return;
281
+ }
282
+ txMeta.status = status;
283
+ this.updateTransactionInState(txMeta);
284
+ this.emit(baseControllers.TX_EVENTS.TX_STATUS_UPDATE, {
285
+ txId,
286
+ status
287
+ });
288
+ if (this.isFinalState(status) || isFinalStep) {
289
+ this.emit(`${txMeta.id}:finished`, txMeta);
290
+ } else {
291
+ this.emit(`${txMeta.id}:${status}`, txId);
292
+ }
293
+ }
294
+ }
295
+
296
+ exports.TransactionStateManager = TransactionStateManager;
@@ -0,0 +1,341 @@
1
+ 'use strict';
2
+
3
+ var util = require('@ethereumjs/util');
4
+ var baseControllers = require('@toruslabs/base-controllers');
5
+ var auth = require('@web3auth/auth');
6
+ var ethers = require('ethers');
7
+ var log = require('loglevel');
8
+ var abis = require('../utils/abis.js');
9
+ var constants = require('../utils/constants.js');
10
+
11
+ const erc20Interface = new ethers.Interface(abis.erc20Abi);
12
+ const erc721Interface = new ethers.Interface(abis.erc721Abi);
13
+ const erc1155Interface = new ethers.Interface(abis.erc1155Abi);
14
+
15
+ // functions that handle normalizing of that key in txParams
16
+
17
+ const normalizers = {
18
+ from: (from, LowerCase = true) => LowerCase ? util.addHexPrefix(from).toLowerCase() : util.addHexPrefix(from),
19
+ to: (to, LowerCase = true) => LowerCase ? util.addHexPrefix(to).toLowerCase() : util.addHexPrefix(to),
20
+ nonce: nonce => util.addHexPrefix(nonce),
21
+ customNonceValue: nonce => util.addHexPrefix(nonce),
22
+ value: value => util.addHexPrefix(value),
23
+ data: data => util.addHexPrefix(data),
24
+ gas: gas => util.addHexPrefix(gas),
25
+ gasPrice: gasPrice => util.addHexPrefix(gasPrice),
26
+ type: util.addHexPrefix,
27
+ maxFeePerGas: util.addHexPrefix,
28
+ maxPriorityFeePerGas: util.addHexPrefix
29
+ };
30
+
31
+ /**
32
+ * normalizes txParams
33
+ */
34
+ function normalizeTxParameters(txParameters, lowerCase = true) {
35
+ // apply only keys in the normalizers
36
+ const normalizedTxParameters = {
37
+ id: txParameters.id || baseControllers.randomId(),
38
+ from: txParameters.from
39
+ };
40
+ for (const key in normalizers) {
41
+ const currentKey = key;
42
+ if (txParameters[currentKey])
43
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
44
+ normalizedTxParameters[currentKey] = normalizers[currentKey](txParameters[currentKey], lowerCase);
45
+ }
46
+ return normalizedTxParameters;
47
+ }
48
+ function transactionMatchesNetwork(transaction, chainId) {
49
+ if (typeof transaction.chainId !== "undefined") {
50
+ return transaction.chainId === chainId;
51
+ }
52
+ return false;
53
+ }
54
+
55
+ /**
56
+ * Determines if the maxFeePerGas and maxPriorityFeePerGas fields are supplied
57
+ * and valid inputs. This will return false for non hex string inputs.
58
+ * the transaction to check
59
+ * @returns true if transaction uses valid EIP1559 fields
60
+ */
61
+ function isEIP1559Transaction(transaction) {
62
+ var _transaction$transact, _transaction$transact2;
63
+ return util.isHexString(util.addHexPrefix(transaction === null || transaction === void 0 || (_transaction$transact = transaction.transaction) === null || _transaction$transact === void 0 ? void 0 : _transaction$transact.maxFeePerGas)) && util.isHexString(util.addHexPrefix(transaction === null || transaction === void 0 || (_transaction$transact2 = transaction.transaction) === null || _transaction$transact2 === void 0 ? void 0 : _transaction$transact2.maxPriorityFeePerGas));
64
+ }
65
+
66
+ /**
67
+ * Determine if the maxFeePerGas and maxPriorityFeePerGas fields are not
68
+ * supplied and that the gasPrice field is valid if it is provided. This will
69
+ * return false if gasPrice is a non hex string.
70
+ * transaction -
71
+ * the transaction to check
72
+ * @returns true if transaction uses valid Legacy fields OR lacks
73
+ * EIP1559 fields
74
+ */
75
+ function isLegacyTransaction(transaction) {
76
+ return typeof transaction.transaction.maxFeePerGas === "undefined" && typeof transaction.transaction.maxPriorityFeePerGas === "undefined" && (typeof transaction.transaction.gasPrice === "undefined" || util.isHexString(util.addHexPrefix(transaction.transaction.gasPrice)));
77
+ }
78
+
79
+ /**
80
+ * Given two fields, ensure that the second field is not included in txParams,
81
+ * and if it is throw an invalidParams error.
82
+ */
83
+ function ensureMutuallyExclusiveFieldsNotProvided(txParams, fieldBeingValidated, mutuallyExclusiveField) {
84
+ if (typeof txParams[mutuallyExclusiveField] !== "undefined") {
85
+ throw auth.rpcErrors.invalidParams(`Invalid transaction params: specified ${fieldBeingValidated} but also included ${mutuallyExclusiveField}, these cannot be mixed`);
86
+ }
87
+ }
88
+
89
+ /**
90
+ * Ensures that the provided value for field is a string, throws an
91
+ * invalidParams error if field is not a string.
92
+ */
93
+ function ensureFieldIsString(txParams, field) {
94
+ if (typeof txParams[field] !== "string") {
95
+ throw auth.rpcErrors.invalidParams(`Invalid transaction params: ${field} is not a string. got: (${txParams[field]})`);
96
+ }
97
+ }
98
+
99
+ /**
100
+ * Ensures that the provided txParams has the proper 'type' specified for the
101
+ * given field, if it is provided. If types do not match throws an
102
+ * invalidParams error.
103
+ */
104
+ function ensureProperTransactionEnvelopeTypeProvided(txParams, field) {
105
+ switch (field) {
106
+ case "maxFeePerGas":
107
+ case "maxPriorityFeePerGas":
108
+ if (txParams.type && txParams.type !== constants.TRANSACTION_ENVELOPE_TYPES.FEE_MARKET) {
109
+ throw auth.rpcErrors.invalidParams(`Invalid transaction envelope type: specified type "${txParams.type}" but ` + `including maxFeePerGas and maxPriorityFeePerGas requires type: "${constants.TRANSACTION_ENVELOPE_TYPES.FEE_MARKET}"`);
110
+ }
111
+ break;
112
+ case "gasPrice":
113
+ default:
114
+ if (txParams.type && txParams.type === constants.TRANSACTION_ENVELOPE_TYPES.FEE_MARKET) {
115
+ throw auth.rpcErrors.invalidParams(`Invalid transaction envelope type: specified type "${txParams.type}" but ` + "included a gasPrice instead of maxFeePerGas and maxPriorityFeePerGas");
116
+ }
117
+ }
118
+ }
119
+
120
+ /**
121
+ * validates the from field in txParams
122
+ */
123
+ function validateFrom(txParams) {
124
+ if (!(typeof txParams.from === "string")) {
125
+ throw auth.rpcErrors.invalidParams(`Invalid "from" address "${txParams.from}": not a string.`);
126
+ }
127
+ if (!util.isValidAddress(txParams.from)) {
128
+ throw auth.rpcErrors.invalidParams('Invalid "from" address.');
129
+ }
130
+ }
131
+
132
+ /**
133
+ * validates the to field in txParams
134
+ */
135
+ function validateRecipient(txParameters) {
136
+ if (txParameters.to === "0x" || txParameters.to === null) {
137
+ if (txParameters.data) {
138
+ delete txParameters.to;
139
+ } else {
140
+ throw auth.rpcErrors.invalidParams('Invalid "to" address.');
141
+ }
142
+ } else if (txParameters.to !== undefined && !util.isValidAddress(txParameters.to)) {
143
+ throw auth.rpcErrors.invalidParams('Invalid "to" address.');
144
+ }
145
+ return txParameters;
146
+ }
147
+
148
+ /**
149
+ * Validates the given tx parameters
150
+ * @throws if the tx params contains invalid fields
151
+ */
152
+ function validateTxParameters(txParams, eip1559Compatibility = true) {
153
+ if (!txParams || typeof txParams !== "object" || Array.isArray(txParams)) {
154
+ throw auth.rpcErrors.invalidParams("Invalid transaction params: must be an object.");
155
+ }
156
+ if (!txParams.to && !txParams.data) {
157
+ throw auth.rpcErrors.invalidParams('Invalid transaction params: must specify "data" for contract deployments, or "to" (and optionally "data") for all other types of transactions.');
158
+ }
159
+ if (isEIP1559Transaction({
160
+ transaction: txParams
161
+ }) && !eip1559Compatibility) {
162
+ throw auth.rpcErrors.invalidParams("Invalid transaction params: params specify an EIP-1559 transaction but the current network does not support EIP-1559");
163
+ }
164
+ Object.entries(txParams).forEach(([key, value]) => {
165
+ // validate types
166
+ switch (key) {
167
+ case "from":
168
+ validateFrom(txParams);
169
+ break;
170
+ case "to":
171
+ validateRecipient(txParams);
172
+ break;
173
+ case "gasPrice":
174
+ ensureProperTransactionEnvelopeTypeProvided(txParams, "gasPrice");
175
+ ensureMutuallyExclusiveFieldsNotProvided(txParams, "gasPrice", "maxFeePerGas");
176
+ ensureMutuallyExclusiveFieldsNotProvided(txParams, "gasPrice", "maxPriorityFeePerGas");
177
+ ensureFieldIsString(txParams, "gasPrice");
178
+ break;
179
+ case "maxFeePerGas":
180
+ ensureProperTransactionEnvelopeTypeProvided(txParams, "maxFeePerGas");
181
+ ensureMutuallyExclusiveFieldsNotProvided(txParams, "maxFeePerGas", "gasPrice");
182
+ ensureFieldIsString(txParams, "maxFeePerGas");
183
+ break;
184
+ case "maxPriorityFeePerGas":
185
+ ensureProperTransactionEnvelopeTypeProvided(txParams, "maxPriorityFeePerGas");
186
+ ensureMutuallyExclusiveFieldsNotProvided(txParams, "maxPriorityFeePerGas", "gasPrice");
187
+ ensureFieldIsString(txParams, "maxPriorityFeePerGas");
188
+ break;
189
+ case "value":
190
+ ensureFieldIsString(txParams, "value");
191
+ if (value.toString().includes("-")) {
192
+ throw auth.rpcErrors.invalidParams(`Invalid transaction value "${value}": not a positive number.`);
193
+ }
194
+ if (value.toString().includes(".")) {
195
+ throw auth.rpcErrors.invalidParams(`Invalid transaction value of "${value}": number must be in wei.`);
196
+ }
197
+ break;
198
+ case "chainId":
199
+ if (typeof value !== "number" && typeof value !== "string") {
200
+ throw auth.rpcErrors.invalidParams(`Invalid transaction params: ${key} is not a Number or hex string. got: (${value})`);
201
+ }
202
+ break;
203
+ default:
204
+ ensureFieldIsString(txParams, key);
205
+ }
206
+ });
207
+ }
208
+ function normalizeAndValidateTxParams(txParams, lowerCase = true) {
209
+ const normalizedTxParams = normalizeTxParameters(txParams, lowerCase);
210
+ validateTxParameters(normalizedTxParams);
211
+ return normalizedTxParams;
212
+ }
213
+
214
+ /**
215
+ * @returns an array of states that can be considered final
216
+ */
217
+ function getFinalStates() {
218
+ return [baseControllers.TransactionStatus.rejected,
219
+ // the user has responded no!
220
+ baseControllers.TransactionStatus.confirmed,
221
+ // the tx has been included in a block.
222
+ baseControllers.TransactionStatus.failed,
223
+ // the tx failed for some reason, included on tx data.
224
+ baseControllers.TransactionStatus.dropped // the tx nonce was already used
225
+ ];
226
+ }
227
+ function parseStandardTokenTransactionData(data) {
228
+ try {
229
+ const txDesc = erc20Interface.parseTransaction({
230
+ data
231
+ });
232
+ if (txDesc) return {
233
+ name: txDesc.name,
234
+ methodParams: txDesc.args.toArray(),
235
+ type: constants.CONTRACT_TYPE_ERC20
236
+ };
237
+ } catch {
238
+ // ignore and next try to parse with erc721 ABI
239
+ }
240
+ try {
241
+ const txDesc = erc721Interface.parseTransaction({
242
+ data
243
+ });
244
+ if (txDesc) return {
245
+ name: txDesc.name,
246
+ methodParams: txDesc.args.toArray(),
247
+ type: constants.CONTRACT_TYPE_ERC721
248
+ };
249
+ } catch {
250
+ // ignore and next try to parse with erc1155 ABI
251
+ }
252
+ try {
253
+ const txDesc = erc1155Interface.parseTransaction({
254
+ data
255
+ });
256
+ if (txDesc) return {
257
+ name: txDesc.name,
258
+ methodParams: txDesc.args.toArray(),
259
+ type: constants.CONTRACT_TYPE_ERC1155
260
+ };
261
+ } catch {
262
+ // ignore and return undefined
263
+ }
264
+ return undefined;
265
+ }
266
+ const readAddressAsContract = async (provider, address) => {
267
+ let contractCode;
268
+ try {
269
+ contractCode = await provider.request({
270
+ method: constants.METHOD_TYPES.ETH_GET_CODE,
271
+ params: [address, "latest"]
272
+ });
273
+ } catch (e) {
274
+ contractCode = null;
275
+ }
276
+ const isContractAddress = contractCode ? contractCode !== "0x" && contractCode !== "0x0" : false;
277
+ return {
278
+ contractCode,
279
+ isContractAddress
280
+ };
281
+ };
282
+ async function determineTransactionType(txParams, provider) {
283
+ const {
284
+ data,
285
+ to
286
+ } = txParams;
287
+ let name = "";
288
+ let methodParams = [];
289
+ let type = "";
290
+ try {
291
+ ({
292
+ name,
293
+ methodParams,
294
+ type
295
+ } = data && parseStandardTokenTransactionData(data) || {});
296
+ } catch (error) {
297
+ log.debug("Failed to parse transaction data", error);
298
+ }
299
+ let result;
300
+ let contractCode = "";
301
+ if (data && !to) {
302
+ result = baseControllers.TRANSACTION_TYPES.DEPLOY_CONTRACT;
303
+ } else {
304
+ const {
305
+ contractCode: resultCode,
306
+ isContractAddress
307
+ } = await readAddressAsContract(provider, to);
308
+ contractCode = resultCode;
309
+ if (isContractAddress) {
310
+ const valueExists = txParams.value && Number(txParams.value) !== 0;
311
+ const tokenMethodName = [baseControllers.TRANSACTION_TYPES.TOKEN_METHOD_APPROVE, baseControllers.TRANSACTION_TYPES.TOKEN_METHOD_TRANSFER, baseControllers.TRANSACTION_TYPES.TOKEN_METHOD_TRANSFER_FROM, baseControllers.TRANSACTION_TYPES.COLLECTIBLE_METHOD_SAFE_TRANSFER_FROM, baseControllers.TRANSACTION_TYPES.SET_APPROVAL_FOR_ALL].find(x => {
312
+ var _name;
313
+ return x.toLowerCase() === ((_name = name) === null || _name === void 0 ? void 0 : _name.toLowerCase());
314
+ });
315
+ result = data && tokenMethodName && !valueExists ? tokenMethodName : baseControllers.TRANSACTION_TYPES.CONTRACT_INTERACTION;
316
+ } else {
317
+ result = baseControllers.TRANSACTION_TYPES.SENT_ETHER;
318
+ }
319
+ }
320
+ return {
321
+ type: type || constants.CONTRACT_TYPE_ETH,
322
+ category: result,
323
+ methodParams,
324
+ getCodeResponse: contractCode
325
+ };
326
+ }
327
+
328
+ exports.determineTransactionType = determineTransactionType;
329
+ exports.ensureFieldIsString = ensureFieldIsString;
330
+ exports.ensureMutuallyExclusiveFieldsNotProvided = ensureMutuallyExclusiveFieldsNotProvided;
331
+ exports.getFinalStates = getFinalStates;
332
+ exports.isEIP1559Transaction = isEIP1559Transaction;
333
+ exports.isLegacyTransaction = isLegacyTransaction;
334
+ exports.normalizeAndValidateTxParams = normalizeAndValidateTxParams;
335
+ exports.normalizeTxParameters = normalizeTxParameters;
336
+ exports.parseStandardTokenTransactionData = parseStandardTokenTransactionData;
337
+ exports.readAddressAsContract = readAddressAsContract;
338
+ exports.transactionMatchesNetwork = transactionMatchesNetwork;
339
+ exports.validateFrom = validateFrom;
340
+ exports.validateRecipient = validateRecipient;
341
+ exports.validateTxParameters = validateTxParameters;