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