@metamask-previews/transaction-controller 17.0.0-preview.eb58a59 → 18.3.0-preview.3fbb6b41

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 (39) hide show
  1. package/CHANGELOG.md +48 -1
  2. package/dist/TransactionController.d.ts +65 -16
  3. package/dist/TransactionController.d.ts.map +1 -1
  4. package/dist/TransactionController.js +309 -129
  5. package/dist/TransactionController.js.map +1 -1
  6. package/dist/constants.d.ts +4 -0
  7. package/dist/constants.d.ts.map +1 -1
  8. package/dist/constants.js +5 -1
  9. package/dist/constants.js.map +1 -1
  10. package/dist/helpers/EtherscanRemoteTransactionSource.d.ts.map +1 -1
  11. package/dist/helpers/EtherscanRemoteTransactionSource.js +1 -0
  12. package/dist/helpers/EtherscanRemoteTransactionSource.js.map +1 -1
  13. package/dist/helpers/PendingTransactionTracker.d.ts +1 -1
  14. package/dist/helpers/PendingTransactionTracker.d.ts.map +1 -1
  15. package/dist/helpers/PendingTransactionTracker.js.map +1 -1
  16. package/dist/types.d.ts +10 -4
  17. package/dist/types.d.ts.map +1 -1
  18. package/dist/types.js.map +1 -1
  19. package/dist/utils/gas.d.ts.map +1 -1
  20. package/dist/utils/gas.js +4 -1
  21. package/dist/utils/gas.js.map +1 -1
  22. package/dist/utils/history.d.ts.map +1 -1
  23. package/dist/utils/history.js +3 -0
  24. package/dist/utils/history.js.map +1 -1
  25. package/dist/utils/nonce.d.ts +21 -0
  26. package/dist/utils/nonce.d.ts.map +1 -0
  27. package/dist/utils/nonce.js +76 -0
  28. package/dist/utils/nonce.js.map +1 -0
  29. package/dist/utils/swaps.d.ts.map +1 -1
  30. package/dist/utils/swaps.js +13 -0
  31. package/dist/utils/swaps.js.map +1 -1
  32. package/dist/utils/transaction-type.d.ts.map +1 -1
  33. package/dist/utils/transaction-type.js +19 -12
  34. package/dist/utils/transaction-type.js.map +1 -1
  35. package/dist/utils/utils.d.ts +7 -11
  36. package/dist/utils/utils.d.ts.map +1 -1
  37. package/dist/utils/utils.js +23 -30
  38. package/dist/utils/utils.js.map +1 -1
  39. package/package.json +11 -11
@@ -24,7 +24,7 @@ const eth_method_registry_1 = __importDefault(require("eth-method-registry"));
24
24
  const ethereumjs_util_1 = require("ethereumjs-util");
25
25
  const events_1 = require("events");
26
26
  const lodash_1 = require("lodash");
27
- const nonce_tracker_1 = __importDefault(require("nonce-tracker"));
27
+ const nonce_tracker_1 = require("nonce-tracker");
28
28
  const uuid_1 = require("uuid");
29
29
  const EtherscanRemoteTransactionSource_1 = require("./helpers/EtherscanRemoteTransactionSource");
30
30
  const IncomingTransactionHelper_1 = require("./helpers/IncomingTransactionHelper");
@@ -35,6 +35,7 @@ const external_transactions_1 = require("./utils/external-transactions");
35
35
  const gas_1 = require("./utils/gas");
36
36
  const gas_fees_1 = require("./utils/gas-fees");
37
37
  const history_1 = require("./utils/history");
38
+ const nonce_1 = require("./utils/nonce");
38
39
  const swaps_1 = require("./utils/swaps");
39
40
  const transaction_type_1 = require("./utils/transaction-type");
40
41
  const utils_1 = require("./utils/utils");
@@ -55,21 +56,23 @@ const controllerName = 'TransactionController';
55
56
  /**
56
57
  * Controller responsible for submitting and managing transactions.
57
58
  */
58
- class TransactionController extends base_controller_1.BaseController {
59
+ class TransactionController extends base_controller_1.BaseControllerV1 {
59
60
  /**
60
61
  * Creates a TransactionController instance.
61
62
  *
62
63
  * @param options - The controller options.
63
64
  * @param options.blockTracker - The block tracker used to poll for new blocks data.
65
+ * @param options.cancelMultiplier - Multiplier used to determine a transaction's increased gas fee during cancellation.
64
66
  * @param options.disableHistory - Whether to disable storing history in transaction metadata.
65
67
  * @param options.disableSendFlowHistory - Explicitly disable transaction metadata history.
66
68
  * @param options.disableSwaps - Whether to disable additional processing on swaps transactions.
67
- * @param options.getSavedGasFees - Gets the saved gas fee config.
68
69
  * @param options.getCurrentAccountEIP1559Compatibility - Whether or not the account supports EIP-1559.
69
70
  * @param options.getCurrentNetworkEIP1559Compatibility - Whether or not the network supports EIP-1559.
71
+ * @param options.getExternalPendingTransactions - Callback to retrieve pending transactions from external sources.
70
72
  * @param options.getGasFeeEstimates - Callback to retrieve gas fee estimates.
71
73
  * @param options.getNetworkState - Gets the state of the network controller.
72
74
  * @param options.getPermittedAccounts - Get accounts that a given origin has permissions for.
75
+ * @param options.getSavedGasFees - Gets the saved gas fee config.
73
76
  * @param options.getSelectedAddress - Gets the address of the currently selected account.
74
77
  * @param options.incomingTransactions - Configuration options for incoming transaction support.
75
78
  * @param options.incomingTransactions.includeTokenTransfers - Whether or not to include ERC20 token transfers.
@@ -82,6 +85,7 @@ class TransactionController extends base_controller_1.BaseController {
82
85
  * @param options.pendingTransactions.isResubmitEnabled - Whether transaction publishing is automatically retried.
83
86
  * @param options.provider - The provider used to create the underlying EthQuery instance.
84
87
  * @param options.securityProviderRequest - A function for verifying a transaction, whether it is malicious or not.
88
+ * @param options.speedUpMultiplier - Multiplier used to determine a transaction's increased gas fee during speed up.
85
89
  * @param options.hooks - The controller hooks.
86
90
  * @param options.hooks.afterSign - Additional logic to execute after signing a transaction. Return false to not change the status to signed.
87
91
  * @param options.hooks.beforeApproveOnInit - Additional logic to execute before starting an approval flow for a transaction during initialization. Return false to skip the transaction.
@@ -91,7 +95,7 @@ class TransactionController extends base_controller_1.BaseController {
91
95
  * @param config - Initial options used to configure this controller.
92
96
  * @param state - Initial state to set on this controller.
93
97
  */
94
- constructor({ blockTracker, disableHistory, disableSendFlowHistory, disableSwaps, getSavedGasFees, getCurrentAccountEIP1559Compatibility, getCurrentNetworkEIP1559Compatibility, getGasFeeEstimates, getNetworkState, getPermittedAccounts, getSelectedAddress, incomingTransactions = {}, messenger, onNetworkStateChange, pendingTransactions = {}, provider, securityProviderRequest, hooks = {}, }, config, state) {
98
+ constructor({ blockTracker, cancelMultiplier, disableHistory, disableSendFlowHistory, disableSwaps, getCurrentAccountEIP1559Compatibility, getCurrentNetworkEIP1559Compatibility, getExternalPendingTransactions, getGasFeeEstimates, getNetworkState, getPermittedAccounts, getSavedGasFees, getSelectedAddress, incomingTransactions = {}, messenger, onNetworkStateChange, pendingTransactions = {}, provider, securityProviderRequest, speedUpMultiplier, hooks = {}, }, config, state) {
95
99
  var _a, _b, _c, _d, _e;
96
100
  super(config, state);
97
101
  this.inProcessOfSigning = new Set();
@@ -123,14 +127,18 @@ class TransactionController extends base_controller_1.BaseController {
123
127
  this.registry = new eth_method_registry_1.default({ provider });
124
128
  this.getSavedGasFees = getSavedGasFees !== null && getSavedGasFees !== void 0 ? getSavedGasFees : ((_chainId) => undefined);
125
129
  this.getCurrentAccountEIP1559Compatibility =
126
- getCurrentAccountEIP1559Compatibility;
130
+ getCurrentAccountEIP1559Compatibility !== null && getCurrentAccountEIP1559Compatibility !== void 0 ? getCurrentAccountEIP1559Compatibility : (() => Promise.resolve(true));
127
131
  this.getCurrentNetworkEIP1559Compatibility =
128
132
  getCurrentNetworkEIP1559Compatibility;
129
133
  this.getGasFeeEstimates =
130
134
  getGasFeeEstimates || (() => Promise.resolve({}));
131
135
  this.getPermittedAccounts = getPermittedAccounts;
132
136
  this.getSelectedAddress = getSelectedAddress;
137
+ this.getExternalPendingTransactions =
138
+ getExternalPendingTransactions !== null && getExternalPendingTransactions !== void 0 ? getExternalPendingTransactions : (() => []);
133
139
  this.securityProviderRequest = securityProviderRequest;
140
+ this.cancelMultiplier = cancelMultiplier !== null && cancelMultiplier !== void 0 ? cancelMultiplier : exports.CANCEL_RATE;
141
+ this.speedUpMultiplier = speedUpMultiplier !== null && speedUpMultiplier !== void 0 ? speedUpMultiplier : exports.SPEED_UP_RATE;
134
142
  this.afterSign = (_a = hooks === null || hooks === void 0 ? void 0 : hooks.afterSign) !== null && _a !== void 0 ? _a : (() => true);
135
143
  this.beforeApproveOnInit = (_b = hooks === null || hooks === void 0 ? void 0 : hooks.beforeApproveOnInit) !== null && _b !== void 0 ? _b : (() => true);
136
144
  this.beforeCheckPendingTransaction =
@@ -140,11 +148,12 @@ class TransactionController extends base_controller_1.BaseController {
140
148
  this.beforePublish = (_d = hooks === null || hooks === void 0 ? void 0 : hooks.beforePublish) !== null && _d !== void 0 ? _d : (() => true);
141
149
  this.getAdditionalSignArguments =
142
150
  (_e = hooks === null || hooks === void 0 ? void 0 : hooks.getAdditionalSignArguments) !== null && _e !== void 0 ? _e : (() => []);
143
- this.nonceTracker = new nonce_tracker_1.default({
151
+ this.nonceTracker = new nonce_tracker_1.NonceTracker({
152
+ // @ts-expect-error provider types misaligned: SafeEventEmitterProvider vs Record<string,string>
144
153
  provider,
145
154
  blockTracker,
146
- getPendingTransactions: (address) => (0, utils_1.getAndFormatTransactionsForNonceTracker)(address, types_1.TransactionStatus.submitted, this.state.transactions),
147
- getConfirmedTransactions: (address) => (0, utils_1.getAndFormatTransactionsForNonceTracker)(address, types_1.TransactionStatus.confirmed, this.state.transactions),
155
+ getPendingTransactions: this.getNonceTrackerPendingTransactions.bind(this),
156
+ getConfirmedTransactions: this.getNonceTrackerTransactions.bind(this, types_1.TransactionStatus.confirmed),
148
157
  });
149
158
  this.incomingTransactionHelper = new IncomingTransactionHelper_1.IncomingTransactionHelper({
150
159
  blockTracker,
@@ -192,6 +201,7 @@ class TransactionController extends base_controller_1.BaseController {
192
201
  transactionMeta: newTransactionMeta,
193
202
  });
194
203
  this.updateTransaction(newTransactionMeta, 'TransactionController#failTransaction - Add error message and set status to failed');
204
+ this.onTransactionStatusChange(newTransactionMeta);
195
205
  this.hub.emit(`${transactionMeta.id}:finished`, newTransactionMeta);
196
206
  }
197
207
  registryLookup(fourBytePrefix) {
@@ -249,7 +259,7 @@ class TransactionController extends base_controller_1.BaseController {
249
259
  */
250
260
  addTransaction(txParams, { actionId, deviceConfirmedOn, method, origin, requireApproval, securityAlertResponse, sendFlowHistory, swaps = {}, type, } = {}) {
251
261
  return __awaiter(this, void 0, void 0, function* () {
252
- const chainId = this.getChainId();
262
+ (0, logger_1.projectLogger)('Adding transaction', txParams);
253
263
  txParams = (0, utils_1.normalizeTxParams)(txParams);
254
264
  const isEIP1559Compatible = yield this.getEIP1559Compatibility();
255
265
  (0, validation_1.validateTxParams)(txParams, isEIP1559Compatible);
@@ -259,6 +269,7 @@ class TransactionController extends base_controller_1.BaseController {
259
269
  const dappSuggestedGasFees = this.generateDappSuggestedGasFees(txParams, origin);
260
270
  const transactionType = type !== null && type !== void 0 ? type : (yield (0, transaction_type_1.determineTransactionType)(txParams, this.ethQuery)).type;
261
271
  const existingTransactionMeta = this.getTransactionWithActionId(actionId);
272
+ const chainId = this.getChainId();
262
273
  // If a request to add a transaction with the same actionId is submitted again, a new transaction will not be created for it.
263
274
  const transactionMeta = existingTransactionMeta || {
264
275
  // Add actionId to txMeta to check if same actionId is seen again
@@ -331,15 +342,18 @@ class TransactionController extends base_controller_1.BaseController {
331
342
  * @param options.estimatedBaseFee - The estimated base fee of the transaction.
332
343
  */
333
344
  stopTransaction(transactionId, gasValues, { estimatedBaseFee, actionId, } = {}) {
334
- var _a, _b;
345
+ var _a, _b, _c;
335
346
  return __awaiter(this, void 0, void 0, function* () {
336
347
  // If transaction is found for same action id, do not create a cancel transaction.
337
348
  if (this.getTransactionWithActionId(actionId)) {
338
349
  return;
339
350
  }
340
351
  if (gasValues) {
352
+ // Not good practice to reassign a parameter but temporarily avoiding a larger refactor.
353
+ gasValues = (0, utils_1.normalizeGasFeeValues)(gasValues);
341
354
  (0, utils_1.validateGasValues)(gasValues);
342
355
  }
356
+ (0, logger_1.projectLogger)('Creating cancel transaction', transactionId, gasValues);
343
357
  const transactionMeta = this.getTransaction(transactionId);
344
358
  if (!transactionMeta) {
345
359
  return;
@@ -348,21 +362,21 @@ class TransactionController extends base_controller_1.BaseController {
348
362
  throw new Error('No sign method defined.');
349
363
  }
350
364
  // gasPrice (legacy non EIP1559)
351
- const minGasPrice = (0, utils_1.getIncreasedPriceFromExisting)(transactionMeta.txParams.gasPrice, exports.CANCEL_RATE);
365
+ const minGasPrice = (0, utils_1.getIncreasedPriceFromExisting)(transactionMeta.txParams.gasPrice, this.cancelMultiplier);
352
366
  const gasPriceFromValues = (0, utils_1.isGasPriceValue)(gasValues) && gasValues.gasPrice;
353
367
  const newGasPrice = (gasPriceFromValues &&
354
368
  (0, utils_1.validateMinimumIncrease)(gasPriceFromValues, minGasPrice)) ||
355
369
  minGasPrice;
356
370
  // maxFeePerGas (EIP1559)
357
371
  const existingMaxFeePerGas = (_a = transactionMeta.txParams) === null || _a === void 0 ? void 0 : _a.maxFeePerGas;
358
- const minMaxFeePerGas = (0, utils_1.getIncreasedPriceFromExisting)(existingMaxFeePerGas, exports.CANCEL_RATE);
372
+ const minMaxFeePerGas = (0, utils_1.getIncreasedPriceFromExisting)(existingMaxFeePerGas, this.cancelMultiplier);
359
373
  const maxFeePerGasValues = (0, utils_1.isFeeMarketEIP1559Values)(gasValues) && gasValues.maxFeePerGas;
360
374
  const newMaxFeePerGas = (maxFeePerGasValues &&
361
375
  (0, utils_1.validateMinimumIncrease)(maxFeePerGasValues, minMaxFeePerGas)) ||
362
376
  (existingMaxFeePerGas && minMaxFeePerGas);
363
377
  // maxPriorityFeePerGas (EIP1559)
364
378
  const existingMaxPriorityFeePerGas = (_b = transactionMeta.txParams) === null || _b === void 0 ? void 0 : _b.maxPriorityFeePerGas;
365
- const minMaxPriorityFeePerGas = (0, utils_1.getIncreasedPriceFromExisting)(existingMaxPriorityFeePerGas, exports.CANCEL_RATE);
379
+ const minMaxPriorityFeePerGas = (0, utils_1.getIncreasedPriceFromExisting)(existingMaxPriorityFeePerGas, this.cancelMultiplier);
366
380
  const maxPriorityFeePerGasValues = (0, utils_1.isFeeMarketEIP1559Values)(gasValues) && gasValues.maxPriorityFeePerGas;
367
381
  const newMaxPriorityFeePerGas = (maxPriorityFeePerGasValues &&
368
382
  (0, utils_1.validateMinimumIncrease)(maxPriorityFeePerGasValues, minMaxPriorityFeePerGas)) ||
@@ -373,7 +387,7 @@ class TransactionController extends base_controller_1.BaseController {
373
387
  gasLimit: transactionMeta.txParams.gas,
374
388
  maxFeePerGas: newMaxFeePerGas,
375
389
  maxPriorityFeePerGas: newMaxPriorityFeePerGas,
376
- type: '2',
390
+ type: types_1.TransactionEnvelopeType.feeMarket,
377
391
  nonce: transactionMeta.txParams.nonce,
378
392
  to: transactionMeta.txParams.from,
379
393
  value: '0x0',
@@ -389,6 +403,15 @@ class TransactionController extends base_controller_1.BaseController {
389
403
  const unsignedEthTx = this.prepareUnsignedEthTx(newTxParams);
390
404
  const signedTx = yield this.sign(unsignedEthTx, transactionMeta.txParams.from);
391
405
  const rawTx = (0, ethereumjs_util_1.bufferToHex)(signedTx.serialize());
406
+ const newFee = (_c = newTxParams.maxFeePerGas) !== null && _c !== void 0 ? _c : newTxParams.gasPrice;
407
+ const oldFee = newTxParams.maxFeePerGas
408
+ ? transactionMeta.txParams.maxFeePerGas
409
+ : transactionMeta.txParams.gasPrice;
410
+ (0, logger_1.projectLogger)('Submitting cancel transaction', {
411
+ oldFee,
412
+ newFee,
413
+ txParams: newTxParams,
414
+ });
392
415
  const hash = yield this.publishTransaction(rawTx);
393
416
  const cancelTransactionMeta = {
394
417
  actionId,
@@ -425,15 +448,18 @@ class TransactionController extends base_controller_1.BaseController {
425
448
  * @param options.estimatedBaseFee - The estimated base fee of the transaction.
426
449
  */
427
450
  speedUpTransaction(transactionId, gasValues, { actionId, estimatedBaseFee, } = {}) {
428
- var _a, _b;
451
+ var _a, _b, _c;
429
452
  return __awaiter(this, void 0, void 0, function* () {
430
453
  // If transaction is found for same action id, do not create a new speed up transaction.
431
454
  if (this.getTransactionWithActionId(actionId)) {
432
455
  return;
433
456
  }
434
457
  if (gasValues) {
458
+ // Not good practice to reassign a parameter but temporarily avoiding a larger refactor.
459
+ gasValues = (0, utils_1.normalizeGasFeeValues)(gasValues);
435
460
  (0, utils_1.validateGasValues)(gasValues);
436
461
  }
462
+ (0, logger_1.projectLogger)('Creating speed up transaction', transactionId, gasValues);
437
463
  const transactionMeta = this.state.transactions.find(({ id }) => id === transactionId);
438
464
  /* istanbul ignore next */
439
465
  if (!transactionMeta) {
@@ -444,31 +470,36 @@ class TransactionController extends base_controller_1.BaseController {
444
470
  throw new Error('No sign method defined.');
445
471
  }
446
472
  // gasPrice (legacy non EIP1559)
447
- const minGasPrice = (0, utils_1.getIncreasedPriceFromExisting)(transactionMeta.txParams.gasPrice, exports.SPEED_UP_RATE);
473
+ const minGasPrice = (0, utils_1.getIncreasedPriceFromExisting)(transactionMeta.txParams.gasPrice, this.speedUpMultiplier);
448
474
  const gasPriceFromValues = (0, utils_1.isGasPriceValue)(gasValues) && gasValues.gasPrice;
449
475
  const newGasPrice = (gasPriceFromValues &&
450
476
  (0, utils_1.validateMinimumIncrease)(gasPriceFromValues, minGasPrice)) ||
451
477
  minGasPrice;
452
478
  // maxFeePerGas (EIP1559)
453
479
  const existingMaxFeePerGas = (_a = transactionMeta.txParams) === null || _a === void 0 ? void 0 : _a.maxFeePerGas;
454
- const minMaxFeePerGas = (0, utils_1.getIncreasedPriceFromExisting)(existingMaxFeePerGas, exports.SPEED_UP_RATE);
480
+ const minMaxFeePerGas = (0, utils_1.getIncreasedPriceFromExisting)(existingMaxFeePerGas, this.speedUpMultiplier);
455
481
  const maxFeePerGasValues = (0, utils_1.isFeeMarketEIP1559Values)(gasValues) && gasValues.maxFeePerGas;
456
482
  const newMaxFeePerGas = (maxFeePerGasValues &&
457
483
  (0, utils_1.validateMinimumIncrease)(maxFeePerGasValues, minMaxFeePerGas)) ||
458
484
  (existingMaxFeePerGas && minMaxFeePerGas);
459
485
  // maxPriorityFeePerGas (EIP1559)
460
486
  const existingMaxPriorityFeePerGas = (_b = transactionMeta.txParams) === null || _b === void 0 ? void 0 : _b.maxPriorityFeePerGas;
461
- const minMaxPriorityFeePerGas = (0, utils_1.getIncreasedPriceFromExisting)(existingMaxPriorityFeePerGas, exports.SPEED_UP_RATE);
487
+ const minMaxPriorityFeePerGas = (0, utils_1.getIncreasedPriceFromExisting)(existingMaxPriorityFeePerGas, this.speedUpMultiplier);
462
488
  const maxPriorityFeePerGasValues = (0, utils_1.isFeeMarketEIP1559Values)(gasValues) && gasValues.maxPriorityFeePerGas;
463
489
  const newMaxPriorityFeePerGas = (maxPriorityFeePerGasValues &&
464
490
  (0, utils_1.validateMinimumIncrease)(maxPriorityFeePerGasValues, minMaxPriorityFeePerGas)) ||
465
491
  (existingMaxPriorityFeePerGas && minMaxPriorityFeePerGas);
466
492
  const txParams = newMaxFeePerGas && newMaxPriorityFeePerGas
467
- ? Object.assign(Object.assign({}, transactionMeta.txParams), { gasLimit: transactionMeta.txParams.gas, maxFeePerGas: newMaxFeePerGas, maxPriorityFeePerGas: newMaxPriorityFeePerGas, type: '2' }) : Object.assign(Object.assign({}, transactionMeta.txParams), { gasLimit: transactionMeta.txParams.gas, gasPrice: newGasPrice });
493
+ ? Object.assign(Object.assign({}, transactionMeta.txParams), { gasLimit: transactionMeta.txParams.gas, maxFeePerGas: newMaxFeePerGas, maxPriorityFeePerGas: newMaxPriorityFeePerGas, type: types_1.TransactionEnvelopeType.feeMarket }) : Object.assign(Object.assign({}, transactionMeta.txParams), { gasLimit: transactionMeta.txParams.gas, gasPrice: newGasPrice });
468
494
  const unsignedEthTx = this.prepareUnsignedEthTx(txParams);
469
495
  const signedTx = yield this.sign(unsignedEthTx, transactionMeta.txParams.from);
470
496
  yield this.updateTransactionMetaRSV(transactionMeta, signedTx);
471
497
  const rawTx = (0, ethereumjs_util_1.bufferToHex)(signedTx.serialize());
498
+ const newFee = (_c = txParams.maxFeePerGas) !== null && _c !== void 0 ? _c : txParams.gasPrice;
499
+ const oldFee = txParams.maxFeePerGas
500
+ ? transactionMeta.txParams.maxFeePerGas
501
+ : transactionMeta.txParams.gasPrice;
502
+ (0, logger_1.projectLogger)('Submitting speed up transaction', { oldFee, newFee, txParams });
472
503
  const hash = yield (0, controller_utils_1.query)(this.ethQuery, 'sendRawTransaction', [rawTx]);
473
504
  const baseTransactionMeta = Object.assign(Object.assign({}, transactionMeta), { estimatedBaseFee, id: (0, uuid_1.v1)(), time: Date.now(), hash,
474
505
  actionId, originalGasEstimate: transactionMeta.txParams.gas, type: types_1.TransactionType.retry, originalType: transactionMeta.type });
@@ -606,29 +637,16 @@ class TransactionController extends base_controller_1.BaseController {
606
637
  this.markNonceDuplicatesDropped(transactionId);
607
638
  // Update external provided transaction with updated gas values and confirmed status.
608
639
  this.updateTransaction(transactionMeta, 'TransactionController:confirmExternalTransaction - Add external transaction');
609
- if (transactionMeta.type === types_1.TransactionType.swap) {
610
- (0, swaps_1.updatePostTransactionBalance)(transactionMeta, {
611
- ethQuery: this.ethQuery,
612
- getTransaction: this.getTransaction.bind(this),
613
- updateTransaction: this.updateTransaction.bind(this),
614
- })
615
- .then(({ updatedTransactionMeta, approvalTransactionMeta }) => {
616
- this.hub.emit('post-transaction-balance-updated', {
617
- transactionMeta: updatedTransactionMeta,
618
- approvalTransactionMeta,
619
- });
620
- })
621
- .catch((error) => {
622
- /* istanbul ignore next */
623
- (0, logger_1.projectLogger)('Error while updating post transaction balance', error);
624
- });
625
- }
640
+ this.onTransactionStatusChange(transactionMeta);
641
+ // Intentional given potential duration of process.
642
+ // eslint-disable-next-line @typescript-eslint/no-floating-promises
643
+ this.updatePostBalance(transactionMeta);
626
644
  this.hub.emit('transaction-confirmed', {
627
645
  transactionMeta,
628
646
  });
629
647
  }
630
648
  catch (error) {
631
- console.error(error);
649
+ console.error('Failed to confirm external transaction', error);
632
650
  }
633
651
  });
634
652
  }
@@ -749,6 +767,44 @@ class TransactionController extends base_controller_1.BaseController {
749
767
  return this.nonceTracker.getNonceLock(address);
750
768
  });
751
769
  }
770
+ /**
771
+ * Updates the editable parameters of a transaction.
772
+ *
773
+ * @param txId - The ID of the transaction to update.
774
+ * @param params - The editable parameters to update.
775
+ * @param params.data - Data to pass with the transaction.
776
+ * @param params.gas - Maximum number of units of gas to use for the transaction.
777
+ * @param params.gasPrice - Price per gas for legacy transactions.
778
+ * @param params.from - Address to send the transaction from.
779
+ * @param params.to - Address to send the transaction to.
780
+ * @param params.value - Value associated with the transaction.
781
+ * @returns The updated transaction metadata.
782
+ */
783
+ updateEditableParams(txId, { data, gas, gasPrice, from, to, value, }) {
784
+ return __awaiter(this, void 0, void 0, function* () {
785
+ const transactionMeta = this.getTransaction(txId);
786
+ if (!transactionMeta) {
787
+ throw new Error(`Cannot update editable params as no transaction metadata found`);
788
+ }
789
+ (0, utils_1.validateIfTransactionUnapproved)(transactionMeta, 'updateEditableParams');
790
+ const editableParams = {
791
+ txParams: {
792
+ data,
793
+ from,
794
+ to,
795
+ value,
796
+ gas,
797
+ gasPrice,
798
+ },
799
+ };
800
+ editableParams.txParams = (0, lodash_1.pickBy)(editableParams.txParams);
801
+ const updatedTransaction = (0, lodash_1.merge)(transactionMeta, editableParams);
802
+ const { type } = yield (0, transaction_type_1.determineTransactionType)(updatedTransaction.txParams, this.ethQuery);
803
+ updatedTransaction.type = type;
804
+ this.updateTransaction(updatedTransaction, `Update Editable Params for ${txId}`);
805
+ return this.getTransaction(txId);
806
+ });
807
+ }
752
808
  /**
753
809
  * Signs and returns the raw transaction data for provided transaction params list.
754
810
  *
@@ -757,6 +813,9 @@ class TransactionController extends base_controller_1.BaseController {
757
813
  */
758
814
  approveTransactionsWithSameNonce(listOfTxParams = []) {
759
815
  return __awaiter(this, void 0, void 0, function* () {
816
+ (0, logger_1.projectLogger)('Approving transactions with same nonce', {
817
+ transactions: listOfTxParams,
818
+ });
760
819
  if (listOfTxParams.length === 0) {
761
820
  return '';
762
821
  }
@@ -776,6 +835,7 @@ class TransactionController extends base_controller_1.BaseController {
776
835
  const fromAddress = initialTx.from;
777
836
  nonceLock = yield this.nonceTracker.getNonceLock(fromAddress);
778
837
  const nonce = nonceLock.nextNonce;
838
+ (0, logger_1.projectLogger)('Using nonce from nonce tracker', nonce, nonceLock.nonceDetails);
779
839
  rawTransactions = yield Promise.all(listOfTxParams.map((txParams) => {
780
840
  txParams.nonce = (0, ethereumjs_util_1.addHexPrefix)(nonce.toString(16));
781
841
  return this.signExternalTransaction(txParams);
@@ -801,14 +861,12 @@ class TransactionController extends base_controller_1.BaseController {
801
861
  *
802
862
  * @param transactionId - The ID of the transaction to update.
803
863
  * @param options - The custodial transaction options to update.
804
- * @param options.custodyStatus - The new custody status value to be assigned.
805
864
  * @param options.errorMessage - The error message to be assigned in case transaction status update to failed.
806
865
  * @param options.hash - The new hash value to be assigned.
807
866
  * @param options.status - The new status value to be assigned.
808
867
  */
809
- updateCustodialTransaction(transactionId, { custodyStatus, errorMessage, hash, status, }) {
810
- let transactionMeta;
811
- transactionMeta = this.getTransaction(transactionId);
868
+ updateCustodialTransaction(transactionId, { errorMessage, hash, status, }) {
869
+ const transactionMeta = this.getTransaction(transactionId);
812
870
  if (!transactionMeta) {
813
871
  throw new Error(`Cannot update custodial transaction as no transaction metadata found`);
814
872
  }
@@ -823,23 +881,112 @@ class TransactionController extends base_controller_1.BaseController {
823
881
  ].includes(status)) {
824
882
  throw new Error(`Cannot update custodial transaction with status: ${status}`);
825
883
  }
826
- if (status === types_1.TransactionStatus.signed) {
827
- transactionMeta.status = status;
828
- }
884
+ const updatedTransactionMeta = (0, lodash_1.merge)(transactionMeta, (0, lodash_1.pickBy)({ hash, status }));
829
885
  if (status === types_1.TransactionStatus.submitted) {
830
- transactionMeta.submittedTime = new Date().getTime();
831
- transactionMeta.status = status;
886
+ updatedTransactionMeta.submittedTime = new Date().getTime();
832
887
  }
833
888
  if (status === types_1.TransactionStatus.failed) {
834
- transactionMeta = Object.assign(Object.assign({}, transactionMeta), { error: (0, utils_1.normalizeTxError)(new Error(errorMessage)), status: types_1.TransactionStatus.failed });
889
+ updatedTransactionMeta.error = (0, utils_1.normalizeTxError)(new Error(errorMessage));
835
890
  }
836
- if (custodyStatus) {
837
- transactionMeta.custodyStatus = custodyStatus;
891
+ this.updateTransaction(updatedTransactionMeta, `TransactionController:updateCustodialTransaction - Custodial transaction updated`);
892
+ }
893
+ /**
894
+ * Creates approvals for all unapproved transactions persisted.
895
+ */
896
+ initApprovals() {
897
+ const chainId = this.getChainId();
898
+ const unapprovedTxs = this.state.transactions.filter((transaction) => transaction.status === types_1.TransactionStatus.unapproved &&
899
+ transaction.chainId === chainId);
900
+ for (const txMeta of unapprovedTxs) {
901
+ this.processApproval(txMeta, {
902
+ shouldShowRequest: false,
903
+ }).catch((error) => {
904
+ if ((error === null || error === void 0 ? void 0 : error.code) === rpc_errors_1.errorCodes.provider.userRejectedRequest) {
905
+ return;
906
+ }
907
+ console.error('Error during persisted transaction approval', error);
908
+ });
838
909
  }
839
- if (hash) {
840
- transactionMeta.hash = hash;
910
+ }
911
+ /**
912
+ * Search transaction metadata for matching entries.
913
+ *
914
+ * @param opts - Options bag.
915
+ * @param opts.searchCriteria - An object containing values or functions for transaction properties to filter transactions with.
916
+ * @param opts.initialList - The transactions to search. Defaults to the current state.
917
+ * @param opts.filterToCurrentNetwork - Whether to filter the results to the current network. Defaults to true.
918
+ * @param opts.limit - The maximum number of transactions to return. No limit by default.
919
+ * @returns An array of transactions matching the provided options.
920
+ */
921
+ getTransactions({ searchCriteria = {}, initialList, filterToCurrentNetwork = true, limit, } = {}) {
922
+ const chainId = this.getChainId();
923
+ // searchCriteria is an object that might have values that aren't predicate
924
+ // methods. When providing any other value type (string, number, etc), we
925
+ // consider this shorthand for "check the value at key for strict equality
926
+ // with the provided value". To conform this object to be only methods, we
927
+ // mapValues (lodash) such that every value on the object is a method that
928
+ // returns a boolean.
929
+ const predicateMethods = (0, lodash_1.mapValues)(searchCriteria, (predicate) => {
930
+ return typeof predicate === 'function'
931
+ ? predicate
932
+ : (v) => v === predicate;
933
+ });
934
+ const transactionsToFilter = initialList !== null && initialList !== void 0 ? initialList : this.state.transactions;
935
+ // Combine sortBy and pickBy to transform our state object into an array of
936
+ // matching transactions that are sorted by time.
937
+ const filteredTransactions = (0, lodash_1.sortBy)((0, lodash_1.pickBy)(transactionsToFilter, (transaction) => {
938
+ if (filterToCurrentNetwork && transaction.chainId !== chainId) {
939
+ return false;
940
+ }
941
+ // iterate over the predicateMethods keys to check if the transaction
942
+ // matches the searchCriteria
943
+ for (const [key, predicate] of Object.entries(predicateMethods)) {
944
+ // We return false early as soon as we know that one of the specified
945
+ // search criteria do not match the transaction. This prevents
946
+ // needlessly checking all criteria when we already know the criteria
947
+ // are not fully satisfied. We check both txParams and the base
948
+ // object as predicate keys can be either.
949
+ if (key in transaction.txParams) {
950
+ if (predicate(transaction.txParams[key]) === false) {
951
+ return false;
952
+ }
953
+ }
954
+ else if (predicate(transaction[key]) === false) {
955
+ return false;
956
+ }
957
+ }
958
+ return true;
959
+ }), 'time');
960
+ if (limit !== undefined) {
961
+ // We need to have all transactions of a given nonce in order to display
962
+ // necessary details in the UI. We use the size of this set to determine
963
+ // whether we have reached the limit provided, thus ensuring that all
964
+ // transactions of nonces we include will be sent to the UI.
965
+ const nonces = new Set();
966
+ const txs = [];
967
+ // By default, the transaction list we filter from is sorted by time ASC.
968
+ // To ensure that filtered results prefers the newest transactions we
969
+ // iterate from right to left, inserting transactions into front of a new
970
+ // array. The original order is preserved, but we ensure that newest txs
971
+ // are preferred.
972
+ for (let i = filteredTransactions.length - 1; i > -1; i--) {
973
+ const txMeta = filteredTransactions[i];
974
+ const { nonce } = txMeta.txParams;
975
+ if (!nonces.has(nonce)) {
976
+ if (nonces.size < limit) {
977
+ nonces.add(nonce);
978
+ }
979
+ else {
980
+ continue;
981
+ }
982
+ }
983
+ // Push transaction into the beginning of our array to ensure the
984
+ // original order is preserved.
985
+ txs.unshift(txMeta);
986
+ }
987
+ return txs;
841
988
  }
842
- this.updateTransaction(transactionMeta, `TransactionController:updateCustodialTransaction - Custodial transaction updated`);
989
+ return filteredTransactions;
843
990
  }
844
991
  signExternalTransaction(transactionParams) {
845
992
  return __awaiter(this, void 0, void 0, function* () {
@@ -874,7 +1021,8 @@ class TransactionController extends base_controller_1.BaseController {
874
1021
  }
875
1022
  updateGasProperties(transactionMeta) {
876
1023
  return __awaiter(this, void 0, void 0, function* () {
877
- const isEIP1559Compatible = yield this.getEIP1559Compatibility();
1024
+ const isEIP1559Compatible = (yield this.getEIP1559Compatibility()) &&
1025
+ transactionMeta.txParams.type !== types_1.TransactionEnvelopeType.legacy;
878
1026
  const chainId = this.getChainId();
879
1027
  yield (0, gas_1.updateGas)({
880
1028
  ethQuery: this.ethQuery,
@@ -895,8 +1043,6 @@ class TransactionController extends base_controller_1.BaseController {
895
1043
  return this.state.transactions.filter((transaction) => transaction.status === status && transaction.chainId === chainId);
896
1044
  }
897
1045
  onBootCleanup() {
898
- this.createApprovalsForUnapprovedTransactions();
899
- this.loadGasValuesForUnapprovedTransactions();
900
1046
  this.submitApprovedTransactions();
901
1047
  }
902
1048
  /**
@@ -916,26 +1062,6 @@ class TransactionController extends base_controller_1.BaseController {
916
1062
  });
917
1063
  }
918
1064
  }
919
- /**
920
- * Update the gas values of all unapproved transactions on current chain.
921
- */
922
- loadGasValuesForUnapprovedTransactions() {
923
- return __awaiter(this, void 0, void 0, function* () {
924
- const unapprovedTransactions = this.getCurrentChainTransactionsByStatus(types_1.TransactionStatus.unapproved);
925
- const results = yield Promise.allSettled(unapprovedTransactions.map((transactionMeta) => __awaiter(this, void 0, void 0, function* () {
926
- yield this.updateGasProperties(transactionMeta);
927
- this.updateTransaction(transactionMeta, 'TransactionController:loadGasValuesForUnapprovedTransactions - Gas values updated');
928
- })));
929
- for (const [index, result] of results.entries()) {
930
- if (result.status === 'rejected') {
931
- const transactionMeta = unapprovedTransactions[index];
932
- this.failTransaction(transactionMeta, result.reason);
933
- /* istanbul ignore next */
934
- console.error('Error while loading gas values for persisted transaction id: ', transactionMeta.id, result.reason);
935
- }
936
- }
937
- });
938
- }
939
1065
  /**
940
1066
  * Force to submit approved transactions on current chain.
941
1067
  */
@@ -965,6 +1091,22 @@ class TransactionController extends base_controller_1.BaseController {
965
1091
  shouldShowRequest,
966
1092
  });
967
1093
  resultCallbacks = acceptResult.resultCallbacks;
1094
+ if (resultCallbacks) {
1095
+ this.hub.once(`${transactionId}:publish-skip`, () => {
1096
+ resultCallbacks === null || resultCallbacks === void 0 ? void 0 : resultCallbacks.success();
1097
+ // Remove the reference to prevent additional reports once submitted.
1098
+ resultCallbacks = undefined;
1099
+ });
1100
+ }
1101
+ const approvalValue = acceptResult.value;
1102
+ const updatedTransaction = approvalValue === null || approvalValue === void 0 ? void 0 : approvalValue.txMeta;
1103
+ if (updatedTransaction) {
1104
+ (0, logger_1.projectLogger)('Updating transaction with approval data', {
1105
+ customNonce: updatedTransaction.customNonceValue,
1106
+ params: updatedTransaction.txParams,
1107
+ });
1108
+ this.updateTransaction(updatedTransaction, 'TransactionController#processApproval - Updated with approval data');
1109
+ }
968
1110
  }
969
1111
  const { isCompleted: isTxCompleted } = this.isTransactionCompleted(transactionId);
970
1112
  if (!isTxCompleted) {
@@ -1019,8 +1161,8 @@ class TransactionController extends base_controller_1.BaseController {
1019
1161
  const chainId = this.getChainId();
1020
1162
  const index = transactions.findIndex(({ id }) => transactionId === id);
1021
1163
  const transactionMeta = transactions[index];
1022
- const { txParams: { nonce, from }, } = transactionMeta;
1023
- let nonceLock;
1164
+ const { txParams: { from }, } = transactionMeta;
1165
+ let releaseNonceLock;
1024
1166
  try {
1025
1167
  if (!this.sign) {
1026
1168
  releaseLock();
@@ -1036,45 +1178,44 @@ class TransactionController extends base_controller_1.BaseController {
1036
1178
  (0, logger_1.projectLogger)('Skipping approval as signing in progress', transactionId);
1037
1179
  return;
1038
1180
  }
1039
- const { approved: status } = types_1.TransactionStatus;
1040
- let nonceToUse = nonce;
1041
- // if a nonce already exists on the transactionMeta it means this is a speedup or cancel transaction
1042
- // so we want to reuse that nonce and hope that it beats the previous attempt to chain. Otherwise use a new locked nonce
1043
- if (!nonceToUse) {
1044
- nonceLock = yield this.nonceTracker.getNonceLock(from);
1045
- nonceToUse = (0, ethereumjs_util_1.addHexPrefix)(nonceLock.nextNonce.toString(16));
1046
- }
1047
- transactionMeta.status = status;
1048
- transactionMeta.txParams.nonce = nonceToUse;
1181
+ const [nonce, releaseNonce] = yield (0, nonce_1.getNextNonce)(transactionMeta, this.nonceTracker);
1182
+ releaseNonceLock = releaseNonce;
1183
+ transactionMeta.status = types_1.TransactionStatus.approved;
1184
+ transactionMeta.txParams.nonce = nonce;
1049
1185
  transactionMeta.txParams.chainId = chainId;
1050
1186
  const baseTxParams = Object.assign(Object.assign({}, transactionMeta.txParams), { gasLimit: transactionMeta.txParams.gas });
1051
1187
  this.updateTransaction(transactionMeta, 'TransactionController#approveTransaction - Transaction approved');
1188
+ this.onTransactionStatusChange(transactionMeta);
1052
1189
  const isEIP1559 = (0, utils_1.isEIP1559Transaction)(transactionMeta.txParams);
1053
1190
  const txParams = isEIP1559
1054
- ? Object.assign(Object.assign({}, baseTxParams), { maxFeePerGas: transactionMeta.txParams.maxFeePerGas, maxPriorityFeePerGas: transactionMeta.txParams.maxPriorityFeePerGas, estimatedBaseFee: transactionMeta.txParams.estimatedBaseFee,
1055
- // specify type 2 if maxFeePerGas and maxPriorityFeePerGas are set
1056
- type: '2' }) : baseTxParams;
1057
- // delete gasPrice if maxFeePerGas and maxPriorityFeePerGas are set
1058
- if (isEIP1559) {
1059
- delete txParams.gasPrice;
1060
- }
1061
- const rawTx = yield this.signTransaction(transactionMeta);
1191
+ ? Object.assign(Object.assign({}, baseTxParams), { estimatedBaseFee: transactionMeta.txParams.estimatedBaseFee, type: types_1.TransactionEnvelopeType.feeMarket }) : baseTxParams;
1192
+ const rawTx = yield this.signTransaction(transactionMeta, txParams);
1062
1193
  if (!this.beforePublish(transactionMeta)) {
1063
1194
  (0, logger_1.projectLogger)('Skipping publishing transaction based on hook');
1195
+ this.hub.emit(`${transactionMeta.id}:publish-skip`, transactionMeta);
1064
1196
  return;
1065
1197
  }
1066
1198
  if (!rawTx) {
1067
1199
  return;
1068
1200
  }
1201
+ if (transactionMeta.type === types_1.TransactionType.swap) {
1202
+ (0, logger_1.projectLogger)('Determining pre-transaction balance');
1203
+ const preTxBalance = yield (0, controller_utils_1.query)(this.ethQuery, 'getBalance', [from]);
1204
+ transactionMeta.preTxBalance = preTxBalance;
1205
+ (0, logger_1.projectLogger)('Updated pre-transaction balance', transactionMeta.preTxBalance);
1206
+ }
1207
+ (0, logger_1.projectLogger)('Publishing transaction', txParams);
1069
1208
  const hash = yield this.publishTransaction(rawTx);
1209
+ (0, logger_1.projectLogger)('Publish successful', hash);
1070
1210
  transactionMeta.hash = hash;
1071
1211
  transactionMeta.status = types_1.TransactionStatus.submitted;
1072
1212
  transactionMeta.submittedTime = new Date().getTime();
1213
+ this.updateTransaction(transactionMeta, 'TransactionController#approveTransaction - Transaction submitted');
1073
1214
  this.hub.emit('transaction-submitted', {
1074
1215
  transactionMeta,
1075
1216
  });
1076
- this.updateTransaction(transactionMeta, 'TransactionController#approveTransaction - Transaction submitted');
1077
1217
  this.hub.emit(`${transactionMeta.id}:finished`, transactionMeta);
1218
+ this.onTransactionStatusChange(transactionMeta);
1078
1219
  }
1079
1220
  catch (error) {
1080
1221
  this.failTransaction(transactionMeta, error);
@@ -1082,9 +1223,7 @@ class TransactionController extends base_controller_1.BaseController {
1082
1223
  finally {
1083
1224
  this.inProcessOfSigning.delete(transactionId);
1084
1225
  // must set transaction to submitted/failed before releasing lock
1085
- if (nonceLock) {
1086
- nonceLock.releaseLock();
1087
- }
1226
+ releaseNonceLock === null || releaseNonceLock === void 0 ? void 0 : releaseNonceLock();
1088
1227
  releaseLock();
1089
1228
  }
1090
1229
  });
@@ -1107,13 +1246,14 @@ class TransactionController extends base_controller_1.BaseController {
1107
1246
  return;
1108
1247
  }
1109
1248
  transactionMeta.status = types_1.TransactionStatus.rejected;
1249
+ const transactions = this.state.transactions.filter(({ id }) => id !== transactionId);
1250
+ this.update({ transactions: this.trimTransactionsForState(transactions) });
1110
1251
  this.hub.emit(`${transactionMeta.id}:finished`, transactionMeta);
1111
1252
  this.hub.emit('transaction-rejected', {
1112
1253
  transactionMeta,
1113
1254
  actionId,
1114
1255
  });
1115
- const transactions = this.state.transactions.filter(({ id }) => id !== transactionId);
1116
- this.update({ transactions: this.trimTransactionsForState(transactions) });
1256
+ this.onTransactionStatusChange(transactionMeta);
1117
1257
  }
1118
1258
  /**
1119
1259
  * Trim the amount of transactions that are set on the state. Checks
@@ -1124,7 +1264,7 @@ class TransactionController extends base_controller_1.BaseController {
1124
1264
  * representation, this function will not break apart transactions with the
1125
1265
  * same nonce, created on the same day, per network. Not accounting for transactions of the same
1126
1266
  * nonce, same day and network combo can result in confusing or broken experiences
1127
- * in the UI. The transactions are then updated using the BaseController update.
1267
+ * in the UI. The transactions are then updated using the BaseControllerV1 update.
1128
1268
  *
1129
1269
  * @param transactions - The transactions to be applied to the state.
1130
1270
  * @returns The trimmed list of transactions.
@@ -1288,25 +1428,23 @@ class TransactionController extends base_controller_1.BaseController {
1288
1428
  */
1289
1429
  addExternalTransaction(transactionMeta) {
1290
1430
  var _a, _b;
1291
- return __awaiter(this, void 0, void 0, function* () {
1292
- const chainId = this.getChainId();
1293
- const { transactions } = this.state;
1294
- const fromAddress = (_a = transactionMeta === null || transactionMeta === void 0 ? void 0 : transactionMeta.txParams) === null || _a === void 0 ? void 0 : _a.from;
1295
- const sameFromAndNetworkTransactions = transactions.filter((transaction) => transaction.txParams.from === fromAddress &&
1296
- transaction.chainId === chainId);
1297
- const confirmedTxs = sameFromAndNetworkTransactions.filter((transaction) => transaction.status === types_1.TransactionStatus.confirmed);
1298
- const pendingTxs = sameFromAndNetworkTransactions.filter((transaction) => transaction.status === types_1.TransactionStatus.submitted);
1299
- (0, external_transactions_1.validateConfirmedExternalTransaction)(transactionMeta, confirmedTxs, pendingTxs);
1300
- // Make sure provided external transaction has non empty history array
1301
- if (!((_b = transactionMeta.history) !== null && _b !== void 0 ? _b : []).length) {
1302
- if (!this.isHistoryDisabled) {
1303
- (0, history_1.addInitialHistorySnapshot)(transactionMeta);
1304
- }
1431
+ const chainId = this.getChainId();
1432
+ const { transactions } = this.state;
1433
+ const fromAddress = (_a = transactionMeta === null || transactionMeta === void 0 ? void 0 : transactionMeta.txParams) === null || _a === void 0 ? void 0 : _a.from;
1434
+ const sameFromAndNetworkTransactions = transactions.filter((transaction) => transaction.txParams.from === fromAddress &&
1435
+ transaction.chainId === chainId);
1436
+ const confirmedTxs = sameFromAndNetworkTransactions.filter((transaction) => transaction.status === types_1.TransactionStatus.confirmed);
1437
+ const pendingTxs = sameFromAndNetworkTransactions.filter((transaction) => transaction.status === types_1.TransactionStatus.submitted);
1438
+ (0, external_transactions_1.validateConfirmedExternalTransaction)(transactionMeta, confirmedTxs, pendingTxs);
1439
+ // Make sure provided external transaction has non empty history array
1440
+ if (!((_b = transactionMeta.history) !== null && _b !== void 0 ? _b : []).length) {
1441
+ if (!this.isHistoryDisabled) {
1442
+ (0, history_1.addInitialHistorySnapshot)(transactionMeta);
1305
1443
  }
1306
- const updatedTransactions = [...transactions, transactionMeta];
1307
- this.update({
1308
- transactions: this.trimTransactionsForState(updatedTransactions),
1309
- });
1444
+ }
1445
+ const updatedTransactions = [...transactions, transactionMeta];
1446
+ this.update({
1447
+ transactions: this.trimTransactionsForState(updatedTransactions),
1310
1448
  });
1311
1449
  }
1312
1450
  /**
@@ -1351,6 +1489,7 @@ class TransactionController extends base_controller_1.BaseController {
1351
1489
  transactionMeta,
1352
1490
  });
1353
1491
  this.updateTransaction(transactionMeta, 'TransactionController#setTransactionStatusDropped - Transaction dropped');
1492
+ this.onTransactionStatusChange(transactionMeta);
1354
1493
  }
1355
1494
  /**
1356
1495
  * Get transaction with provided actionId.
@@ -1391,26 +1530,22 @@ class TransactionController extends base_controller_1.BaseController {
1391
1530
  });
1392
1531
  }
1393
1532
  getEIP1559Compatibility() {
1394
- var _a, _b;
1395
1533
  return __awaiter(this, void 0, void 0, function* () {
1396
1534
  const currentNetworkIsEIP1559Compatible = yield this.getCurrentNetworkEIP1559Compatibility();
1397
- const currentAccountIsEIP1559Compatible = (_b = (_a = this.getCurrentAccountEIP1559Compatibility) === null || _a === void 0 ? void 0 : _a.call(this)) !== null && _b !== void 0 ? _b : true;
1535
+ const currentAccountIsEIP1559Compatible = yield this.getCurrentAccountEIP1559Compatibility();
1398
1536
  return (currentNetworkIsEIP1559Compatible && currentAccountIsEIP1559Compatible);
1399
1537
  });
1400
1538
  }
1401
1539
  addPendingTransactionTrackerListeners() {
1402
- this.pendingTransactionTracker.hub.on('transaction-confirmed', (transactionMeta) => {
1403
- this.hub.emit('transaction-confirmed', { transactionMeta });
1404
- this.hub.emit(`${transactionMeta.id}:confirmed`, transactionMeta);
1405
- });
1540
+ this.pendingTransactionTracker.hub.on('transaction-confirmed', this.onConfirmedTransaction.bind(this));
1406
1541
  this.pendingTransactionTracker.hub.on('transaction-dropped', this.setTransactionStatusDropped.bind(this));
1407
1542
  this.pendingTransactionTracker.hub.on('transaction-failed', this.failTransaction.bind(this));
1408
1543
  this.pendingTransactionTracker.hub.on('transaction-updated', this.updateTransaction.bind(this));
1409
1544
  }
1410
- signTransaction(transactionMeta) {
1545
+ signTransaction(transactionMeta, txParams) {
1411
1546
  var _a;
1412
1547
  return __awaiter(this, void 0, void 0, function* () {
1413
- const { txParams } = transactionMeta;
1548
+ (0, logger_1.projectLogger)('Signing transaction', txParams);
1414
1549
  const unsignedEthTx = this.prepareUnsignedEthTx(txParams);
1415
1550
  this.inProcessOfSigning.add(transactionMeta.id);
1416
1551
  const signedTx = yield ((_a = this.sign) === null || _a === void 0 ? void 0 : _a.call(this, unsignedEthTx, txParams.from, ...this.getAdditionalSignArguments(transactionMeta)));
@@ -1419,19 +1554,64 @@ class TransactionController extends base_controller_1.BaseController {
1419
1554
  return undefined;
1420
1555
  }
1421
1556
  if (!this.afterSign(transactionMeta, signedTx)) {
1557
+ this.updateTransaction(transactionMeta, 'TransactionController#signTransaction - Update after sign');
1422
1558
  (0, logger_1.projectLogger)('Skipping signed status based on hook');
1423
1559
  return undefined;
1424
1560
  }
1425
1561
  yield this.updateTransactionMetaRSV(transactionMeta, signedTx);
1426
1562
  transactionMeta.status = types_1.TransactionStatus.signed;
1427
1563
  this.updateTransaction(transactionMeta, 'TransactionController#approveTransaction - Transaction signed');
1564
+ this.onTransactionStatusChange(transactionMeta);
1428
1565
  const rawTx = (0, ethereumjs_util_1.bufferToHex)(signedTx.serialize());
1429
1566
  transactionMeta.rawTx = rawTx;
1430
1567
  this.updateTransaction(transactionMeta, 'TransactionController#approveTransaction - RawTransaction added');
1431
1568
  return rawTx;
1432
1569
  });
1433
1570
  }
1571
+ onTransactionStatusChange(transactionMeta) {
1572
+ this.hub.emit('transaction-status-update', { transactionMeta });
1573
+ }
1574
+ getNonceTrackerPendingTransactions(address) {
1575
+ const standardPendingTransactions = this.getNonceTrackerTransactions(types_1.TransactionStatus.submitted, address);
1576
+ const externalPendingTransactions = this.getExternalPendingTransactions(address);
1577
+ return [...standardPendingTransactions, ...externalPendingTransactions];
1578
+ }
1579
+ getNonceTrackerTransactions(status, address) {
1580
+ const currentChainId = this.getChainId();
1581
+ return (0, nonce_1.getAndFormatTransactionsForNonceTracker)(currentChainId, address, status, this.state.transactions);
1582
+ }
1583
+ onConfirmedTransaction(transactionMeta) {
1584
+ (0, logger_1.projectLogger)('Processing confirmed transaction', transactionMeta.id);
1585
+ this.markNonceDuplicatesDropped(transactionMeta.id);
1586
+ this.hub.emit('transaction-confirmed', { transactionMeta });
1587
+ this.hub.emit(`${transactionMeta.id}:confirmed`, transactionMeta);
1588
+ this.onTransactionStatusChange(transactionMeta);
1589
+ // Intentional given potential duration of process.
1590
+ // eslint-disable-next-line @typescript-eslint/no-floating-promises
1591
+ this.updatePostBalance(transactionMeta);
1592
+ }
1593
+ updatePostBalance(transactionMeta) {
1594
+ return __awaiter(this, void 0, void 0, function* () {
1595
+ try {
1596
+ if (transactionMeta.type !== types_1.TransactionType.swap) {
1597
+ return;
1598
+ }
1599
+ const { updatedTransactionMeta, approvalTransactionMeta } = yield (0, swaps_1.updatePostTransactionBalance)(transactionMeta, {
1600
+ ethQuery: this.ethQuery,
1601
+ getTransaction: this.getTransaction.bind(this),
1602
+ updateTransaction: this.updateTransaction.bind(this),
1603
+ });
1604
+ this.hub.emit('post-transaction-balance-updated', {
1605
+ transactionMeta: updatedTransactionMeta,
1606
+ approvalTransactionMeta,
1607
+ });
1608
+ }
1609
+ catch (error) {
1610
+ /* istanbul ignore next */
1611
+ (0, logger_1.projectLogger)('Error while updating post transaction balance', error);
1612
+ }
1613
+ });
1614
+ }
1434
1615
  }
1435
1616
  exports.TransactionController = TransactionController;
1436
- exports.default = TransactionController;
1437
1617
  //# sourceMappingURL=TransactionController.js.map