@metamask/transaction-controller 8.0.0 → 9.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 (38) hide show
  1. package/CHANGELOG.md +44 -1
  2. package/dist/EtherscanRemoteTransactionSource.d.ts +15 -0
  3. package/dist/EtherscanRemoteTransactionSource.d.ts.map +1 -0
  4. package/dist/EtherscanRemoteTransactionSource.js +99 -0
  5. package/dist/EtherscanRemoteTransactionSource.js.map +1 -0
  6. package/dist/IncomingTransactionHelper.d.ts +24 -0
  7. package/dist/IncomingTransactionHelper.d.ts.map +1 -0
  8. package/dist/IncomingTransactionHelper.js +188 -0
  9. package/dist/IncomingTransactionHelper.js.map +1 -0
  10. package/dist/TransactionController.d.ts +58 -232
  11. package/dist/TransactionController.d.ts.map +1 -1
  12. package/dist/TransactionController.js +198 -311
  13. package/dist/TransactionController.js.map +1 -1
  14. package/dist/constants.d.ts +119 -0
  15. package/dist/constants.d.ts.map +1 -0
  16. package/dist/constants.js +124 -0
  17. package/dist/constants.js.map +1 -0
  18. package/dist/etherscan.d.ts +65 -0
  19. package/dist/etherscan.d.ts.map +1 -0
  20. package/dist/etherscan.js +116 -0
  21. package/dist/etherscan.js.map +1 -0
  22. package/dist/index.d.ts +2 -0
  23. package/dist/index.d.ts.map +1 -1
  24. package/dist/index.js +1 -0
  25. package/dist/index.js.map +1 -1
  26. package/dist/types.d.ts +265 -0
  27. package/dist/types.d.ts.map +1 -0
  28. package/dist/types.js +29 -0
  29. package/dist/types.js.map +1 -0
  30. package/dist/utils.d.ts +13 -23
  31. package/dist/utils.d.ts.map +1 -1
  32. package/dist/utils.js +20 -77
  33. package/dist/utils.js.map +1 -1
  34. package/package.json +5 -6
  35. package/dist/mocks/txsMock.d.ts +0 -64
  36. package/dist/mocks/txsMock.d.ts.map +0 -1
  37. package/dist/mocks/txsMock.js +0 -516
  38. package/dist/mocks/txsMock.js.map +0 -1
@@ -12,46 +12,24 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
12
12
  return (mod && mod.__esModule) ? mod : { "default": mod };
13
13
  };
14
14
  Object.defineProperty(exports, "__esModule", { value: true });
15
- exports.TransactionController = exports.SPEED_UP_RATE = exports.CANCEL_RATE = exports.WalletDevice = exports.TransactionStatus = exports.HARDFORK = void 0;
15
+ exports.TransactionController = exports.SPEED_UP_RATE = exports.CANCEL_RATE = exports.HARDFORK = void 0;
16
16
  const common_1 = require("@ethereumjs/common");
17
17
  const tx_1 = require("@ethereumjs/tx");
18
18
  const base_controller_1 = require("@metamask/base-controller");
19
19
  const controller_utils_1 = require("@metamask/controller-utils");
20
+ const eth_query_1 = __importDefault(require("@metamask/eth-query"));
20
21
  const async_mutex_1 = require("async-mutex");
21
22
  const eth_method_registry_1 = __importDefault(require("eth-method-registry"));
22
- const eth_query_1 = __importDefault(require("eth-query"));
23
23
  const eth_rpc_errors_1 = require("eth-rpc-errors");
24
24
  const ethereumjs_util_1 = require("ethereumjs-util");
25
25
  const events_1 = require("events");
26
26
  const nonce_tracker_1 = __importDefault(require("nonce-tracker"));
27
27
  const uuid_1 = require("uuid");
28
+ const EtherscanRemoteTransactionSource_1 = require("./EtherscanRemoteTransactionSource");
29
+ const IncomingTransactionHelper_1 = require("./IncomingTransactionHelper");
30
+ const types_1 = require("./types");
28
31
  const utils_1 = require("./utils");
29
32
  exports.HARDFORK = common_1.Hardfork.London;
30
- /**
31
- * The status of the transaction. Each status represents the state of the transaction internally
32
- * in the wallet. Some of these correspond with the state of the transaction on the network, but
33
- * some are wallet-specific.
34
- */
35
- var TransactionStatus;
36
- (function (TransactionStatus) {
37
- TransactionStatus["approved"] = "approved";
38
- TransactionStatus["cancelled"] = "cancelled";
39
- TransactionStatus["confirmed"] = "confirmed";
40
- TransactionStatus["failed"] = "failed";
41
- TransactionStatus["rejected"] = "rejected";
42
- TransactionStatus["signed"] = "signed";
43
- TransactionStatus["submitted"] = "submitted";
44
- TransactionStatus["unapproved"] = "unapproved";
45
- })(TransactionStatus = exports.TransactionStatus || (exports.TransactionStatus = {}));
46
- /**
47
- * Options for wallet device.
48
- */
49
- var WalletDevice;
50
- (function (WalletDevice) {
51
- WalletDevice["MM_MOBILE"] = "metamask_mobile";
52
- WalletDevice["MM_EXTENSION"] = "metamask_extension";
53
- WalletDevice["OTHER"] = "other_device";
54
- })(WalletDevice = exports.WalletDevice || (exports.WalletDevice = {}));
55
33
  /**
56
34
  * Multiplier used to determine a transaction's increased gas fee during cancellation
57
35
  */
@@ -72,45 +50,23 @@ class TransactionController extends base_controller_1.BaseController {
72
50
  * Creates a TransactionController instance.
73
51
  *
74
52
  * @param options - The controller options.
53
+ * @param options.blockTracker - The block tracker used to poll for new blocks data.
75
54
  * @param options.getNetworkState - Gets the state of the network controller.
55
+ * @param options.getSelectedAddress - Gets the address of the currently selected account.
56
+ * @param options.incomingTransactions - Configuration options for incoming transaction support.
57
+ * @param options.incomingTransactions.apiKey - An optional API key to use when fetching remote transaction data.
58
+ * @param options.incomingTransactions.includeTokenTransfers - Whether or not to include ERC20 token transfers.
59
+ * @param options.incomingTransactions.isEnabled - Whether or not incoming transaction retrieval is enabled.
60
+ * @param options.incomingTransactions.updateTransactions - Whether or not to update local transactions using remote transaction data.
61
+ * @param options.messenger - The controller messenger.
76
62
  * @param options.onNetworkStateChange - Allows subscribing to network controller state changes.
77
63
  * @param options.provider - The provider used to create the underlying EthQuery instance.
78
- * @param options.blockTracker - The block tracker used to poll for new blocks data.
79
- * @param options.messenger - The controller messenger.
80
64
  * @param config - Initial options used to configure this controller.
81
65
  * @param state - Initial state to set on this controller.
82
66
  */
83
- constructor({ getNetworkState, onNetworkStateChange, provider, blockTracker, messenger, }, config, state) {
67
+ constructor({ blockTracker, getNetworkState, getSelectedAddress, incomingTransactions = {}, messenger, onNetworkStateChange, provider, }, config, state) {
84
68
  super(config, state);
85
69
  this.mutex = new async_mutex_1.Mutex();
86
- this.normalizeTokenTx = (txMeta, currentNetworkID, currentChainId) => {
87
- const time = parseInt(txMeta.timeStamp, 10) * 1000;
88
- const { to, from, gas, gasPrice, gasUsed, hash, contractAddress, tokenDecimal, tokenSymbol, value, } = txMeta;
89
- return {
90
- id: (0, uuid_1.v1)({ msecs: time }),
91
- isTransfer: true,
92
- networkID: currentNetworkID,
93
- chainId: currentChainId,
94
- status: TransactionStatus.confirmed,
95
- time,
96
- transaction: {
97
- chainId: currentChainId,
98
- from,
99
- gas,
100
- gasPrice,
101
- gasUsed,
102
- to,
103
- value,
104
- },
105
- transactionHash: hash,
106
- transferInformation: {
107
- contractAddress,
108
- decimals: Number(tokenDecimal),
109
- symbol: tokenSymbol,
110
- },
111
- verifiedOnBlockchain: false,
112
- };
113
- };
114
70
  /**
115
71
  * EventEmitter instance used to listen to specific transactional events
116
72
  */
@@ -126,6 +82,7 @@ class TransactionController extends base_controller_1.BaseController {
126
82
  this.defaultState = {
127
83
  methodData: {},
128
84
  transactions: [],
85
+ lastFetchedBlockNumbers: {},
129
86
  };
130
87
  this.initialize();
131
88
  this.provider = provider;
@@ -136,9 +93,23 @@ class TransactionController extends base_controller_1.BaseController {
136
93
  this.nonceTracker = new nonce_tracker_1.default({
137
94
  provider,
138
95
  blockTracker,
139
- getPendingTransactions: (address) => (0, utils_1.getAndFormatTransactionsForNonceTracker)(address, TransactionStatus.submitted, this.state.transactions),
140
- getConfirmedTransactions: (address) => (0, utils_1.getAndFormatTransactionsForNonceTracker)(address, TransactionStatus.confirmed, this.state.transactions),
96
+ getPendingTransactions: (address) => (0, utils_1.getAndFormatTransactionsForNonceTracker)(address, types_1.TransactionStatus.submitted, this.state.transactions),
97
+ getConfirmedTransactions: (address) => (0, utils_1.getAndFormatTransactionsForNonceTracker)(address, types_1.TransactionStatus.confirmed, this.state.transactions),
141
98
  });
99
+ this.incomingTransactionHelper = new IncomingTransactionHelper_1.IncomingTransactionHelper({
100
+ blockTracker,
101
+ getCurrentAccount: getSelectedAddress,
102
+ getNetworkState,
103
+ isEnabled: incomingTransactions.isEnabled,
104
+ remoteTransactionSource: new EtherscanRemoteTransactionSource_1.EtherscanRemoteTransactionSource({
105
+ apiKey: incomingTransactions.apiKey,
106
+ includeTokenTransfers: incomingTransactions.includeTokenTransfers,
107
+ }),
108
+ transactionLimit: this.config.txHistoryLimit,
109
+ updateTransactions: incomingTransactions.updateTransactions,
110
+ });
111
+ this.incomingTransactionHelper.hub.on('transactions', this.onIncomingTransactions.bind(this));
112
+ this.incomingTransactionHelper.hub.on('updatedLastFetchedBlockNumbers', this.onUpdatedLastFetchedBlockNumbers.bind(this));
142
113
  onNetworkStateChange(() => {
143
114
  this.ethQuery = new eth_query_1.default(this.provider);
144
115
  this.registry = new eth_method_registry_1.default({ provider: this.provider });
@@ -146,7 +117,7 @@ class TransactionController extends base_controller_1.BaseController {
146
117
  this.poll();
147
118
  }
148
119
  failTransaction(transactionMeta, error) {
149
- const newTransactionMeta = Object.assign(Object.assign({}, transactionMeta), { error, status: TransactionStatus.failed });
120
+ const newTransactionMeta = Object.assign(Object.assign({}, transactionMeta), { error, status: types_1.TransactionStatus.failed });
150
121
  this.updateTransaction(newTransactionMeta);
151
122
  this.hub.emit(`${transactionMeta.id}:finished`, newTransactionMeta);
152
123
  }
@@ -157,43 +128,6 @@ class TransactionController extends base_controller_1.BaseController {
157
128
  return { registryMethod, parsedRegistryMethod };
158
129
  });
159
130
  }
160
- /**
161
- * Normalizes the transaction information from etherscan
162
- * to be compatible with the TransactionMeta interface.
163
- *
164
- * @param txMeta - The transaction.
165
- * @param currentNetworkID - The current network ID.
166
- * @param currentChainId - The current chain ID.
167
- * @returns The normalized transaction.
168
- */
169
- normalizeTx(txMeta, currentNetworkID, currentChainId) {
170
- const time = parseInt(txMeta.timeStamp, 10) * 1000;
171
- const normalizedTransactionBase = {
172
- blockNumber: txMeta.blockNumber,
173
- id: (0, uuid_1.v1)({ msecs: time }),
174
- networkID: currentNetworkID,
175
- chainId: currentChainId,
176
- time,
177
- transaction: {
178
- data: txMeta.input,
179
- from: txMeta.from,
180
- gas: (0, controller_utils_1.BNToHex)(new ethereumjs_util_1.BN(txMeta.gas)),
181
- gasPrice: (0, controller_utils_1.BNToHex)(new ethereumjs_util_1.BN(txMeta.gasPrice)),
182
- gasUsed: (0, controller_utils_1.BNToHex)(new ethereumjs_util_1.BN(txMeta.gasUsed)),
183
- nonce: (0, controller_utils_1.BNToHex)(new ethereumjs_util_1.BN(txMeta.nonce)),
184
- to: txMeta.to,
185
- value: (0, controller_utils_1.BNToHex)(new ethereumjs_util_1.BN(txMeta.value)),
186
- },
187
- transactionHash: txMeta.hash,
188
- verifiedOnBlockchain: false,
189
- };
190
- /* istanbul ignore else */
191
- if (txMeta.isError === '0') {
192
- return Object.assign(Object.assign({}, normalizedTransactionBase), { status: TransactionStatus.confirmed });
193
- }
194
- /* istanbul ignore next */
195
- return Object.assign(Object.assign({}, normalizedTransactionBase), { error: new Error('Transaction failed'), status: TransactionStatus.failed });
196
- }
197
131
  /**
198
132
  * Starts a new polling interval.
199
133
  *
@@ -241,26 +175,30 @@ class TransactionController extends base_controller_1.BaseController {
241
175
  * if not provided. If A `<tx.id>:unapproved` hub event will be emitted once added.
242
176
  *
243
177
  * @param transaction - The transaction object to add.
244
- * @param origin - The domain origin to append to the generated TransactionMeta.
245
- * @param deviceConfirmedOn - An enum to indicate what device the transaction was confirmed to append to the generated TransactionMeta.
178
+ * @param opts - Additional options to control how the transaction is added.
179
+ * @param opts.deviceConfirmedOn - An enum to indicate what device confirmed the transaction.
180
+ * @param opts.origin - The origin of the transaction request, such as a dApp hostname.
181
+ * @param opts.requireApproval - Whether the transaction requires approval by the user, defaults to true unless explicitly disabled.
246
182
  * @returns Object containing a promise resolving to the transaction hash if approved.
247
183
  */
248
- addTransaction(transaction, origin, deviceConfirmedOn) {
184
+ addTransaction(transaction, { deviceConfirmedOn, origin, requireApproval, } = {}) {
249
185
  return __awaiter(this, void 0, void 0, function* () {
250
- const { providerConfig, networkId } = this.getNetworkState();
186
+ const { chainId, networkId } = this.getChainAndNetworkId();
251
187
  const { transactions } = this.state;
252
188
  transaction = (0, utils_1.normalizeTransaction)(transaction);
253
189
  (0, utils_1.validateTransaction)(transaction);
190
+ const dappSuggestedGasFees = this.generateDappSuggestedGasFees(transaction, origin);
254
191
  const transactionMeta = {
255
192
  id: (0, uuid_1.v1)(),
256
193
  networkID: networkId !== null && networkId !== void 0 ? networkId : undefined,
257
- chainId: providerConfig.chainId,
194
+ chainId,
258
195
  origin,
259
- status: TransactionStatus.unapproved,
196
+ status: types_1.TransactionStatus.unapproved,
260
197
  time: Date.now(),
261
198
  transaction,
262
199
  deviceConfirmedOn,
263
200
  verifiedOnBlockchain: false,
201
+ dappSuggestedGasFees,
264
202
  };
265
203
  try {
266
204
  const { gas, estimateGasError } = yield this.estimateGas(transaction);
@@ -275,40 +213,39 @@ class TransactionController extends base_controller_1.BaseController {
275
213
  this.update({ transactions: this.trimTransactionsForState(transactions) });
276
214
  this.hub.emit(`unapprovedTransaction`, transactionMeta);
277
215
  return {
278
- result: this.processApproval(transactionMeta),
216
+ result: this.processApproval(transactionMeta, {
217
+ requireApproval,
218
+ }),
279
219
  transactionMeta,
280
220
  };
281
221
  });
282
222
  }
283
- prepareUnsignedEthTx(txParams) {
284
- return tx_1.TransactionFactory.fromTxData(txParams, {
285
- common: this.getCommonConfiguration(),
286
- freeze: false,
223
+ startIncomingTransactionPolling() {
224
+ this.incomingTransactionHelper.start();
225
+ }
226
+ stopIncomingTransactionPolling() {
227
+ this.incomingTransactionHelper.stop();
228
+ }
229
+ updateIncomingTransactions() {
230
+ return __awaiter(this, void 0, void 0, function* () {
231
+ yield this.incomingTransactionHelper.update();
287
232
  });
288
233
  }
289
234
  /**
290
- * `@ethereumjs/tx` uses `@ethereumjs/common` as a configuration tool for
291
- * specifying which chain, network, hardfork and EIPs to support for
292
- * a transaction. By referencing this configuration, and analyzing the fields
293
- * specified in txParams, @ethereumjs/tx is able to determine which EIP-2718
294
- * transaction type to use.
295
- *
296
- * @returns {Common} common configuration object
235
+ * Creates approvals for all unapproved transactions persisted.
297
236
  */
298
- getCommonConfiguration() {
299
- const { networkId, providerConfig: { type: chain, chainId, nickname: name }, } = this.getNetworkState();
300
- if (chain !== controller_utils_1.RPC &&
301
- chain !== controller_utils_1.NetworkType['linea-goerli'] &&
302
- chain !== controller_utils_1.NetworkType['linea-mainnet']) {
303
- return new common_1.Common({ chain, hardfork: exports.HARDFORK });
237
+ initApprovals() {
238
+ const { networkId, chainId } = this.getChainAndNetworkId();
239
+ const unapprovedTxs = this.state.transactions.filter((transaction) => transaction.status === types_1.TransactionStatus.unapproved &&
240
+ (0, utils_1.transactionMatchesNetwork)(transaction, chainId, networkId));
241
+ for (const txMeta of unapprovedTxs) {
242
+ this.processApproval(txMeta, {
243
+ shouldShowRequest: false,
244
+ }).catch((error) => {
245
+ /* istanbul ignore next */
246
+ console.error('Error during persisted transaction approval', error);
247
+ });
304
248
  }
305
- const customChainParams = {
306
- name,
307
- chainId: parseInt(chainId, 16),
308
- networkId: networkId === null ? NaN : parseInt(networkId, undefined),
309
- defaultHardfork: exports.HARDFORK,
310
- };
311
- return common_1.Common.custom(customChainParams);
312
249
  }
313
250
  /**
314
251
  * Attempts to cancel a transaction based on its ID by setting its status to "rejected"
@@ -373,7 +310,7 @@ class TransactionController extends base_controller_1.BaseController {
373
310
  const signedTx = yield this.sign(unsignedEthTx, transactionMeta.transaction.from);
374
311
  const rawTransaction = (0, ethereumjs_util_1.bufferToHex)(signedTx.serialize());
375
312
  yield (0, controller_utils_1.query)(this.ethQuery, 'sendRawTransaction', [rawTransaction]);
376
- transactionMeta.status = TransactionStatus.cancelled;
313
+ transactionMeta.status = types_1.TransactionStatus.cancelled;
377
314
  this.hub.emit(`${transactionMeta.id}:finished`, transactionMeta);
378
315
  });
379
316
  }
@@ -513,8 +450,7 @@ class TransactionController extends base_controller_1.BaseController {
513
450
  queryTransactionStatuses() {
514
451
  return __awaiter(this, void 0, void 0, function* () {
515
452
  const { transactions } = this.state;
516
- const { providerConfig, networkId: currentNetworkID } = this.getNetworkState();
517
- const { chainId: currentChainId } = providerConfig;
453
+ const { chainId: currentChainId, networkId: currentNetworkID } = this.getChainAndNetworkId();
518
454
  let gotUpdates = false;
519
455
  yield (0, controller_utils_1.safelyExecute)(() => Promise.all(transactions.map((meta, index) => __awaiter(this, void 0, void 0, function* () {
520
456
  // Using fallback to networkID only when there is no chainId present.
@@ -555,96 +491,49 @@ class TransactionController extends base_controller_1.BaseController {
555
491
  *
556
492
  * @param ignoreNetwork - Determines whether to wipe all transactions, or just those on the
557
493
  * current network. If `true`, all transactions are wiped.
494
+ * @param address - If specified, only transactions originating from this address will be
495
+ * wiped on current network.
558
496
  */
559
- wipeTransactions(ignoreNetwork) {
497
+ wipeTransactions(ignoreNetwork, address) {
560
498
  /* istanbul ignore next */
561
- if (ignoreNetwork) {
499
+ if (ignoreNetwork && !address) {
562
500
  this.update({ transactions: [] });
563
501
  return;
564
502
  }
565
- const { providerConfig, networkId: currentNetworkID } = this.getNetworkState();
566
- const { chainId: currentChainId } = providerConfig;
567
- const newTransactions = this.state.transactions.filter(({ networkID, chainId }) => {
503
+ const { chainId: currentChainId, networkId: currentNetworkID } = this.getChainAndNetworkId();
504
+ const newTransactions = this.state.transactions.filter(({ networkID, chainId, transaction }) => {
505
+ var _a;
568
506
  // Using fallback to networkID only when there is no chainId present. Should be removed when networkID is completely removed.
569
- const isCurrentNetwork = chainId === currentChainId ||
507
+ const isMatchingNetwork = ignoreNetwork ||
508
+ chainId === currentChainId ||
570
509
  (!chainId && networkID === currentNetworkID);
571
- return !isCurrentNetwork;
510
+ if (!isMatchingNetwork) {
511
+ return true;
512
+ }
513
+ const isMatchingAddress = !address || ((_a = transaction.from) === null || _a === void 0 ? void 0 : _a.toLowerCase()) === address.toLowerCase();
514
+ return !isMatchingAddress;
572
515
  });
573
516
  this.update({
574
517
  transactions: this.trimTransactionsForState(newTransactions),
575
518
  });
576
519
  }
577
- /**
578
- * Get transactions from Etherscan for the given address. By default all transactions are
579
- * returned, but the `fromBlock` option can be given to filter just for transactions from a
580
- * specific block onward.
581
- *
582
- * @param address - The address to fetch the transactions for.
583
- * @param opt - Object containing optional data, fromBlock and Etherscan API key.
584
- * @returns The block number of the latest incoming transaction.
585
- */
586
- fetchAll(address, opt) {
587
- return __awaiter(this, void 0, void 0, function* () {
588
- const { providerConfig, networkId: currentNetworkID } = this.getNetworkState();
589
- const { chainId: currentChainId, type: networkType } = providerConfig;
590
- const { transactions } = this.state;
591
- const supportedNetworkIds = ['1', '5', '11155111'];
592
- /* istanbul ignore next */
593
- if (currentNetworkID === null ||
594
- !supportedNetworkIds.includes(currentNetworkID)) {
595
- return undefined;
596
- }
597
- const [etherscanTxResponse, etherscanTokenResponse] = yield (0, utils_1.handleTransactionFetch)(networkType, address, this.config.txHistoryLimit, opt);
598
- const normalizedTxs = etherscanTxResponse.result.map((tx) => this.normalizeTx(tx, currentNetworkID, currentChainId));
599
- const normalizedTokenTxs = etherscanTokenResponse.result.map((tx) => this.normalizeTokenTx(tx, currentNetworkID, currentChainId));
600
- const [updateRequired, allTxs] = this.etherscanTransactionStateReconciler([...normalizedTxs, ...normalizedTokenTxs], transactions);
601
- allTxs.sort((a, b) => (a.time < b.time ? -1 : 1));
602
- let latestIncomingTxBlockNumber;
603
- allTxs.forEach((tx) => __awaiter(this, void 0, void 0, function* () {
604
- /* istanbul ignore next */
605
- if (
606
- // Using fallback to networkID only when there is no chainId present. Should be removed when networkID is completely removed.
607
- (tx.chainId === currentChainId ||
608
- (!tx.chainId && tx.networkID === currentNetworkID)) &&
609
- tx.transaction.to &&
610
- tx.transaction.to.toLowerCase() === address.toLowerCase()) {
611
- if (tx.blockNumber &&
612
- (!latestIncomingTxBlockNumber ||
613
- parseInt(latestIncomingTxBlockNumber, 10) <
614
- parseInt(tx.blockNumber, 10))) {
615
- latestIncomingTxBlockNumber = tx.blockNumber;
616
- }
617
- }
618
- /* istanbul ignore else */
619
- if (tx.toSmartContract === undefined) {
620
- // If not `to` is a contract deploy, if not `data` is send eth
621
- if (tx.transaction.to &&
622
- (!tx.transaction.data || tx.transaction.data !== '0x')) {
623
- const code = yield (0, controller_utils_1.query)(this.ethQuery, 'getCode', [
624
- tx.transaction.to,
625
- ]);
626
- tx.toSmartContract = (0, controller_utils_1.isSmartContractCode)(code);
627
- }
628
- else {
629
- tx.toSmartContract = false;
630
- }
631
- }
632
- }));
633
- // Update state only if new transactions were fetched or
634
- // the status or gas data of a transaction has changed
635
- if (updateRequired) {
636
- this.update({ transactions: this.trimTransactionsForState(allTxs) });
637
- }
638
- return latestIncomingTxBlockNumber;
639
- });
520
+ startIncomingTransactionProcessing() {
521
+ this.incomingTransactionHelper.start();
522
+ }
523
+ stopIncomingTransactionProcessing() {
524
+ this.incomingTransactionHelper.stop();
640
525
  }
641
- processApproval(transactionMeta) {
526
+ processApproval(transactionMeta, { requireApproval, shouldShowRequest = true, }) {
642
527
  return __awaiter(this, void 0, void 0, function* () {
643
528
  const transactionId = transactionMeta.id;
644
529
  let resultCallbacks;
645
530
  try {
646
- const acceptResult = yield this.requestApproval(transactionMeta);
647
- resultCallbacks = acceptResult.resultCallbacks;
531
+ if (requireApproval !== false) {
532
+ const acceptResult = yield this.requestApproval(transactionMeta, {
533
+ shouldShowRequest,
534
+ });
535
+ resultCallbacks = acceptResult.resultCallbacks;
536
+ }
648
537
  const { meta, isCompleted } = this.isTransactionCompleted(transactionId);
649
538
  if (meta && !isCompleted) {
650
539
  yield this.approveTransaction(transactionId);
@@ -664,14 +553,14 @@ class TransactionController extends base_controller_1.BaseController {
664
553
  }
665
554
  const finalMeta = this.getTransaction(transactionId);
666
555
  switch (finalMeta === null || finalMeta === void 0 ? void 0 : finalMeta.status) {
667
- case TransactionStatus.failed:
556
+ case types_1.TransactionStatus.failed:
668
557
  resultCallbacks === null || resultCallbacks === void 0 ? void 0 : resultCallbacks.error(finalMeta.error);
669
558
  throw eth_rpc_errors_1.ethErrors.rpc.internal(finalMeta.error.message);
670
- case TransactionStatus.cancelled:
559
+ case types_1.TransactionStatus.cancelled:
671
560
  const cancelError = eth_rpc_errors_1.ethErrors.rpc.internal('User cancelled the transaction');
672
561
  resultCallbacks === null || resultCallbacks === void 0 ? void 0 : resultCallbacks.error(cancelError);
673
562
  throw cancelError;
674
- case TransactionStatus.submitted:
563
+ case types_1.TransactionStatus.submitted:
675
564
  resultCallbacks === null || resultCallbacks === void 0 ? void 0 : resultCallbacks.success();
676
565
  return finalMeta.transactionHash;
677
566
  default:
@@ -693,8 +582,7 @@ class TransactionController extends base_controller_1.BaseController {
693
582
  return __awaiter(this, void 0, void 0, function* () {
694
583
  const { transactions } = this.state;
695
584
  const releaseLock = yield this.mutex.acquire();
696
- const { providerConfig } = this.getNetworkState();
697
- const { chainId } = providerConfig;
585
+ const { chainId } = this.getChainAndNetworkId();
698
586
  const index = transactions.findIndex(({ id }) => transactionID === id);
699
587
  const transactionMeta = transactions[index];
700
588
  const { transaction: { nonce, from }, } = transactionMeta;
@@ -710,7 +598,7 @@ class TransactionController extends base_controller_1.BaseController {
710
598
  this.failTransaction(transactionMeta, new Error('No chainId defined.'));
711
599
  return;
712
600
  }
713
- const { approved: status } = TransactionStatus;
601
+ const { approved: status } = types_1.TransactionStatus;
714
602
  let nonceToUse = nonce;
715
603
  // if a nonce already exists on the transactionMeta it means this is a speedup or cancel transaction
716
604
  // so we want to reuse that nonce and hope that it beats the previous attempt to chain. Otherwise use a new locked nonce
@@ -733,7 +621,7 @@ class TransactionController extends base_controller_1.BaseController {
733
621
  }
734
622
  const unsignedEthTx = this.prepareUnsignedEthTx(txParams);
735
623
  const signedTx = yield this.sign(unsignedEthTx, from);
736
- transactionMeta.status = TransactionStatus.signed;
624
+ transactionMeta.status = types_1.TransactionStatus.signed;
737
625
  this.updateTransaction(transactionMeta);
738
626
  const rawTransaction = (0, ethereumjs_util_1.bufferToHex)(signedTx.serialize());
739
627
  transactionMeta.rawTransaction = rawTransaction;
@@ -742,7 +630,7 @@ class TransactionController extends base_controller_1.BaseController {
742
630
  rawTransaction,
743
631
  ]);
744
632
  transactionMeta.transactionHash = transactionHash;
745
- transactionMeta.status = TransactionStatus.submitted;
633
+ transactionMeta.status = types_1.TransactionStatus.submitted;
746
634
  this.updateTransaction(transactionMeta);
747
635
  this.hub.emit(`${transactionMeta.id}:finished`, transactionMeta);
748
636
  }
@@ -769,7 +657,7 @@ class TransactionController extends base_controller_1.BaseController {
769
657
  if (!transactionMeta) {
770
658
  return;
771
659
  }
772
- transactionMeta.status = TransactionStatus.rejected;
660
+ transactionMeta.status = types_1.TransactionStatus.rejected;
773
661
  this.hub.emit(`${transactionMeta.id}:finished`, transactionMeta);
774
662
  const transactions = this.state.transactions.filter(({ id }) => id !== transactionID);
775
663
  this.update({ transactions: this.trimTransactionsForState(transactions) });
@@ -815,10 +703,10 @@ class TransactionController extends base_controller_1.BaseController {
815
703
  * @returns Whether the transaction is in a final state.
816
704
  */
817
705
  isFinalState(status) {
818
- return (status === TransactionStatus.rejected ||
819
- status === TransactionStatus.confirmed ||
820
- status === TransactionStatus.failed ||
821
- status === TransactionStatus.cancelled);
706
+ return (status === types_1.TransactionStatus.rejected ||
707
+ status === types_1.TransactionStatus.confirmed ||
708
+ status === types_1.TransactionStatus.failed ||
709
+ status === types_1.TransactionStatus.cancelled);
822
710
  }
823
711
  /**
824
712
  * Whether the transaction has at least completed all local processing.
@@ -828,11 +716,11 @@ class TransactionController extends base_controller_1.BaseController {
828
716
  */
829
717
  isLocalFinalState(status) {
830
718
  return [
831
- TransactionStatus.cancelled,
832
- TransactionStatus.confirmed,
833
- TransactionStatus.failed,
834
- TransactionStatus.rejected,
835
- TransactionStatus.submitted,
719
+ types_1.TransactionStatus.cancelled,
720
+ types_1.TransactionStatus.confirmed,
721
+ types_1.TransactionStatus.failed,
722
+ types_1.TransactionStatus.rejected,
723
+ types_1.TransactionStatus.submitted,
836
724
  ].includes(status);
837
725
  }
838
726
  /**
@@ -845,15 +733,20 @@ class TransactionController extends base_controller_1.BaseController {
845
733
  return __awaiter(this, void 0, void 0, function* () {
846
734
  const { status, transactionHash } = meta;
847
735
  switch (status) {
848
- case TransactionStatus.confirmed:
736
+ case types_1.TransactionStatus.confirmed:
849
737
  const txReceipt = yield (0, controller_utils_1.query)(this.ethQuery, 'getTransactionReceipt', [
850
738
  transactionHash,
851
739
  ]);
852
740
  if (!txReceipt) {
853
741
  return [meta, false];
854
742
  }
743
+ const txBlock = yield (0, controller_utils_1.query)(this.ethQuery, 'getBlockByHash', [
744
+ txReceipt.blockHash,
745
+ ]);
855
746
  meta.verifiedOnBlockchain = true;
856
747
  meta.transaction.gasUsed = txReceipt.gasUsed;
748
+ meta.txReceipt = txReceipt;
749
+ meta.baseFeePerGas = txBlock === null || txBlock === void 0 ? void 0 : txBlock.baseFeePerGas;
857
750
  // According to the Web3 docs:
858
751
  // TRUE if the transaction was successful, FALSE if the EVM reverted the transaction.
859
752
  if (Number(txReceipt.status) === 0) {
@@ -862,7 +755,7 @@ class TransactionController extends base_controller_1.BaseController {
862
755
  return [meta, false];
863
756
  }
864
757
  return [meta, true];
865
- case TransactionStatus.submitted:
758
+ case types_1.TransactionStatus.submitted:
866
759
  const txObj = yield (0, controller_utils_1.query)(this.ethQuery, 'getTransactionByHash', [
867
760
  transactionHash,
868
761
  ]);
@@ -877,7 +770,7 @@ class TransactionController extends base_controller_1.BaseController {
877
770
  }
878
771
  /* istanbul ignore next */
879
772
  if (txObj === null || txObj === void 0 ? void 0 : txObj.blockNumber) {
880
- meta.status = TransactionStatus.confirmed;
773
+ meta.status = types_1.TransactionStatus.confirmed;
881
774
  this.hub.emit(`${meta.id}:confirmed`, meta);
882
775
  return [meta, true];
883
776
  }
@@ -908,90 +801,7 @@ class TransactionController extends base_controller_1.BaseController {
908
801
  return Number(txReceipt.status) === 0;
909
802
  });
910
803
  }
911
- /**
912
- * Method to verify the state of transactions using Etherscan as a source of truth.
913
- *
914
- * @param remoteTxs - Transactions to reconcile that are from a remote source.
915
- * @param localTxs - Transactions to reconcile that are local.
916
- * @returns A tuple containing a boolean indicating whether or not an update was required, and the updated transaction.
917
- */
918
- etherscanTransactionStateReconciler(remoteTxs, localTxs) {
919
- const updatedTxs = this.getUpdatedTransactions(remoteTxs, localTxs);
920
- const newTxs = this.getNewTransactions(remoteTxs, localTxs);
921
- const updatedLocalTxs = localTxs.map((tx) => {
922
- const txIdx = updatedTxs.findIndex(({ transactionHash }) => transactionHash === tx.transactionHash);
923
- return txIdx === -1 ? tx : updatedTxs[txIdx];
924
- });
925
- const updateRequired = newTxs.length > 0 || updatedLocalTxs.length > 0;
926
- return [updateRequired, [...newTxs, ...updatedLocalTxs]];
927
- }
928
- /**
929
- * Get all transactions that are in the remote transactions array
930
- * but not in the local transactions array.
931
- *
932
- * @param remoteTxs - Array of transactions from remote source.
933
- * @param localTxs - Array of transactions stored locally.
934
- * @returns The new transactions.
935
- */
936
- getNewTransactions(remoteTxs, localTxs) {
937
- return remoteTxs.filter((tx) => {
938
- const alreadyInTransactions = localTxs.find(({ transactionHash }) => transactionHash === tx.transactionHash);
939
- return !alreadyInTransactions;
940
- });
941
- }
942
- /**
943
- * Get all the transactions that are locally outdated with respect
944
- * to a remote source (etherscan or blockchain). The returned array
945
- * contains the transactions with the updated data.
946
- *
947
- * @param remoteTxs - Array of transactions from remote source.
948
- * @param localTxs - Array of transactions stored locally.
949
- * @returns The updated transactions.
950
- */
951
- getUpdatedTransactions(remoteTxs, localTxs) {
952
- return remoteTxs.filter((remoteTx) => {
953
- const isTxOutdated = localTxs.find((localTx) => {
954
- return (remoteTx.transactionHash === localTx.transactionHash &&
955
- this.isTransactionOutdated(remoteTx, localTx));
956
- });
957
- return isTxOutdated;
958
- });
959
- }
960
- /**
961
- * Verifies if a local transaction is outdated with respect to the remote transaction.
962
- *
963
- * @param remoteTx - The remote transaction from Etherscan.
964
- * @param localTx - The local transaction.
965
- * @returns Whether the transaction is outdated.
966
- */
967
- isTransactionOutdated(remoteTx, localTx) {
968
- const statusOutdated = this.isStatusOutdated(remoteTx.transactionHash, localTx.transactionHash, remoteTx.status, localTx.status);
969
- const gasDataOutdated = this.isGasDataOutdated(remoteTx.transaction.gasUsed, localTx.transaction.gasUsed);
970
- return statusOutdated || gasDataOutdated;
971
- }
972
- /**
973
- * Verifies if the status of a local transaction is outdated with respect to the remote transaction.
974
- *
975
- * @param remoteTxHash - Remote transaction hash.
976
- * @param localTxHash - Local transaction hash.
977
- * @param remoteTxStatus - Remote transaction status.
978
- * @param localTxStatus - Local transaction status.
979
- * @returns Whether the status is outdated.
980
- */
981
- isStatusOutdated(remoteTxHash, localTxHash, remoteTxStatus, localTxStatus) {
982
- return remoteTxHash === localTxHash && remoteTxStatus !== localTxStatus;
983
- }
984
- /**
985
- * Verifies if the gas data of a local transaction is outdated with respect to the remote transaction.
986
- *
987
- * @param remoteGasUsed - Remote gas used in the transaction.
988
- * @param localGasUsed - Local gas used in the transaction.
989
- * @returns Whether the gas data is outdated.
990
- */
991
- isGasDataOutdated(remoteGasUsed, localGasUsed) {
992
- return remoteGasUsed !== localGasUsed;
993
- }
994
- requestApproval(txMeta) {
804
+ requestApproval(txMeta, { shouldShowRequest }) {
995
805
  return __awaiter(this, void 0, void 0, function* () {
996
806
  const id = this.getApprovalId(txMeta);
997
807
  const { origin } = txMeta;
@@ -1003,7 +813,7 @@ class TransactionController extends base_controller_1.BaseController {
1003
813
  type,
1004
814
  requestData,
1005
815
  expectsResult: true,
1006
- }, true));
816
+ }, shouldShowRequest));
1007
817
  });
1008
818
  }
1009
819
  getTransaction(transactionID) {
@@ -1021,6 +831,83 @@ class TransactionController extends base_controller_1.BaseController {
1021
831
  const isCompleted = this.isLocalFinalState(transaction.status);
1022
832
  return { meta: transaction, isCompleted };
1023
833
  }
834
+ getChainAndNetworkId() {
835
+ const { networkId, providerConfig } = this.getNetworkState();
836
+ const chainId = providerConfig === null || providerConfig === void 0 ? void 0 : providerConfig.chainId;
837
+ return { networkId, chainId };
838
+ }
839
+ prepareUnsignedEthTx(txParams) {
840
+ return tx_1.TransactionFactory.fromTxData(txParams, {
841
+ common: this.getCommonConfiguration(),
842
+ freeze: false,
843
+ });
844
+ }
845
+ /**
846
+ * `@ethereumjs/tx` uses `@ethereumjs/common` as a configuration tool for
847
+ * specifying which chain, network, hardfork and EIPs to support for
848
+ * a transaction. By referencing this configuration, and analyzing the fields
849
+ * specified in txParams, @ethereumjs/tx is able to determine which EIP-2718
850
+ * transaction type to use.
851
+ *
852
+ * @returns common configuration object
853
+ */
854
+ getCommonConfiguration() {
855
+ const { networkId, providerConfig: { type: chain, chainId, nickname: name }, } = this.getNetworkState();
856
+ if (chain !== controller_utils_1.RPC &&
857
+ chain !== controller_utils_1.NetworkType['linea-goerli'] &&
858
+ chain !== controller_utils_1.NetworkType['linea-mainnet']) {
859
+ return new common_1.Common({ chain, hardfork: exports.HARDFORK });
860
+ }
861
+ const customChainParams = {
862
+ name,
863
+ chainId: parseInt(chainId, 16),
864
+ networkId: networkId === null ? NaN : parseInt(networkId, undefined),
865
+ defaultHardfork: exports.HARDFORK,
866
+ };
867
+ return common_1.Common.custom(customChainParams);
868
+ }
869
+ onIncomingTransactions({ added, updated, }) {
870
+ const { transactions: currentTransactions } = this.state;
871
+ const updatedTransactions = [
872
+ ...added,
873
+ ...currentTransactions.map((originalTransaction) => {
874
+ const updatedTransaction = updated.find(({ transactionHash }) => transactionHash === originalTransaction.transactionHash);
875
+ return updatedTransaction !== null && updatedTransaction !== void 0 ? updatedTransaction : originalTransaction;
876
+ }),
877
+ ];
878
+ this.update({
879
+ transactions: this.trimTransactionsForState(updatedTransactions),
880
+ });
881
+ }
882
+ onUpdatedLastFetchedBlockNumbers({ lastFetchedBlockNumbers, blockNumber, }) {
883
+ this.update({ lastFetchedBlockNumbers });
884
+ this.hub.emit('incomingTransactionBlock', blockNumber);
885
+ }
886
+ generateDappSuggestedGasFees(transaction, origin) {
887
+ if (!origin || origin === controller_utils_1.ORIGIN_METAMASK) {
888
+ return undefined;
889
+ }
890
+ const { gasPrice, maxFeePerGas, maxPriorityFeePerGas, gas } = transaction;
891
+ if (gasPrice === undefined &&
892
+ maxFeePerGas === undefined &&
893
+ maxPriorityFeePerGas === undefined &&
894
+ gas === undefined) {
895
+ return undefined;
896
+ }
897
+ const dappSuggestedGasFees = {};
898
+ if (gasPrice !== undefined) {
899
+ dappSuggestedGasFees.gasPrice = gasPrice;
900
+ }
901
+ else if (maxFeePerGas !== undefined ||
902
+ maxPriorityFeePerGas !== undefined) {
903
+ dappSuggestedGasFees.maxFeePerGas = maxFeePerGas;
904
+ dappSuggestedGasFees.maxPriorityFeePerGas = maxPriorityFeePerGas;
905
+ }
906
+ if (gas !== undefined) {
907
+ dappSuggestedGasFees.gas = gas;
908
+ }
909
+ return dappSuggestedGasFees;
910
+ }
1024
911
  }
1025
912
  exports.TransactionController = TransactionController;
1026
913
  exports.default = TransactionController;