@metamask/transaction-controller 16.0.0 → 17.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.
- package/CHANGELOG.md +42 -1
- package/dist/TransactionController.d.ts +139 -15
- package/dist/TransactionController.d.ts.map +1 -1
- package/dist/TransactionController.js +461 -83
- package/dist/TransactionController.js.map +1 -1
- package/dist/constants.d.ts +2 -3
- package/dist/constants.d.ts.map +1 -1
- package/dist/constants.js +3 -15
- package/dist/constants.js.map +1 -1
- package/dist/helpers/PendingTransactionTracker.d.ts +22 -4
- package/dist/helpers/PendingTransactionTracker.d.ts.map +1 -1
- package/dist/helpers/PendingTransactionTracker.js +263 -108
- package/dist/helpers/PendingTransactionTracker.js.map +1 -1
- package/dist/logger.d.ts +0 -1
- package/dist/logger.d.ts.map +1 -1
- package/dist/logger.js +1 -2
- package/dist/logger.js.map +1 -1
- package/dist/types.d.ts +219 -6
- package/dist/types.d.ts.map +1 -1
- package/dist/types.js +1 -0
- package/dist/types.js.map +1 -1
- package/dist/utils/etherscan.d.ts.map +1 -1
- package/dist/utils/etherscan.js.map +1 -1
- package/dist/utils/gas-fees.d.ts +3 -2
- package/dist/utils/gas-fees.d.ts.map +1 -1
- package/dist/utils/gas-fees.js +22 -4
- package/dist/utils/gas-fees.js.map +1 -1
- package/dist/utils/gas.d.ts +2 -0
- package/dist/utils/gas.d.ts.map +1 -1
- package/dist/utils/gas.js +26 -17
- package/dist/utils/gas.js.map +1 -1
- package/dist/utils/swaps.d.ts +81 -0
- package/dist/utils/swaps.d.ts.map +1 -0
- package/dist/utils/swaps.js +240 -0
- package/dist/utils/swaps.js.map +1 -0
- package/dist/utils/utils.d.ts +11 -1
- package/dist/utils/utils.d.ts.map +1 -1
- package/dist/utils/utils.js +18 -2
- package/dist/utils/utils.js.map +1 -1
- package/dist/utils/validation.d.ts +1 -1
- package/dist/utils/validation.d.ts.map +1 -1
- package/dist/utils/validation.js +90 -7
- package/dist/utils/validation.js.map +1 -1
- package/package.json +10 -9
|
@@ -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 swaps_1 = require("./utils/swaps");
|
|
38
39
|
const transaction_type_1 = require("./utils/transaction-type");
|
|
39
40
|
const utils_1 = require("./utils/utils");
|
|
40
41
|
const validation_1 = require("./utils/validation");
|
|
@@ -62,6 +63,8 @@ class TransactionController extends base_controller_1.BaseController {
|
|
|
62
63
|
* @param options.blockTracker - The block tracker used to poll for new blocks data.
|
|
63
64
|
* @param options.disableHistory - Whether to disable storing history in transaction metadata.
|
|
64
65
|
* @param options.disableSendFlowHistory - Explicitly disable transaction metadata history.
|
|
66
|
+
* @param options.disableSwaps - Whether to disable additional processing on swaps transactions.
|
|
67
|
+
* @param options.getSavedGasFees - Gets the saved gas fee config.
|
|
65
68
|
* @param options.getCurrentAccountEIP1559Compatibility - Whether or not the account supports EIP-1559.
|
|
66
69
|
* @param options.getCurrentNetworkEIP1559Compatibility - Whether or not the network supports EIP-1559.
|
|
67
70
|
* @param options.getGasFeeEstimates - Callback to retrieve gas fee estimates.
|
|
@@ -75,13 +78,23 @@ class TransactionController extends base_controller_1.BaseController {
|
|
|
75
78
|
* @param options.incomingTransactions.updateTransactions - Whether to update local transactions using remote transaction data.
|
|
76
79
|
* @param options.messenger - The controller messenger.
|
|
77
80
|
* @param options.onNetworkStateChange - Allows subscribing to network controller state changes.
|
|
81
|
+
* @param options.pendingTransactions - Configuration options for pending transaction support.
|
|
82
|
+
* @param options.pendingTransactions.isResubmitEnabled - Whether transaction publishing is automatically retried.
|
|
78
83
|
* @param options.provider - The provider used to create the underlying EthQuery instance.
|
|
79
84
|
* @param options.securityProviderRequest - A function for verifying a transaction, whether it is malicious or not.
|
|
85
|
+
* @param options.hooks - The controller hooks.
|
|
86
|
+
* @param options.hooks.afterSign - Additional logic to execute after signing a transaction. Return false to not change the status to signed.
|
|
87
|
+
* @param options.hooks.beforeApproveOnInit - Additional logic to execute before starting an approval flow for a transaction during initialization. Return false to skip the transaction.
|
|
88
|
+
* @param options.hooks.beforeCheckPendingTransaction - Additional logic to execute before checking pending transactions. Return false to prevent the broadcast of the transaction.
|
|
89
|
+
* @param options.hooks.beforePublish - Additional logic to execute before publishing a transaction. Return false to prevent the broadcast of the transaction.
|
|
90
|
+
* @param options.hooks.getAdditionalSignArguments - Returns additional arguments required to sign a transaction.
|
|
80
91
|
* @param config - Initial options used to configure this controller.
|
|
81
92
|
* @param state - Initial state to set on this controller.
|
|
82
93
|
*/
|
|
83
|
-
constructor({ blockTracker, disableHistory, disableSendFlowHistory, getCurrentAccountEIP1559Compatibility, getCurrentNetworkEIP1559Compatibility, getGasFeeEstimates, getNetworkState, getPermittedAccounts, getSelectedAddress, incomingTransactions = {}, messenger, onNetworkStateChange, provider, securityProviderRequest, }, config, state) {
|
|
94
|
+
constructor({ blockTracker, disableHistory, disableSendFlowHistory, disableSwaps, getSavedGasFees, getCurrentAccountEIP1559Compatibility, getCurrentNetworkEIP1559Compatibility, getGasFeeEstimates, getNetworkState, getPermittedAccounts, getSelectedAddress, incomingTransactions = {}, messenger, onNetworkStateChange, pendingTransactions = {}, provider, securityProviderRequest, hooks = {}, }, config, state) {
|
|
95
|
+
var _a, _b, _c, _d, _e;
|
|
84
96
|
super(config, state);
|
|
97
|
+
this.inProcessOfSigning = new Set();
|
|
85
98
|
this.mutex = new async_mutex_1.Mutex();
|
|
86
99
|
/**
|
|
87
100
|
* EventEmitter instance used to listen to specific transactional events
|
|
@@ -103,11 +116,12 @@ class TransactionController extends base_controller_1.BaseController {
|
|
|
103
116
|
this.provider = provider;
|
|
104
117
|
this.messagingSystem = messenger;
|
|
105
118
|
this.getNetworkState = getNetworkState;
|
|
106
|
-
// @ts-expect-error TODO: Provider type alignment
|
|
107
119
|
this.ethQuery = new eth_query_1.default(provider);
|
|
108
120
|
this.isSendFlowHistoryDisabled = disableSendFlowHistory !== null && disableSendFlowHistory !== void 0 ? disableSendFlowHistory : false;
|
|
109
121
|
this.isHistoryDisabled = disableHistory !== null && disableHistory !== void 0 ? disableHistory : false;
|
|
122
|
+
this.isSwapsDisabled = disableSwaps !== null && disableSwaps !== void 0 ? disableSwaps : false;
|
|
110
123
|
this.registry = new eth_method_registry_1.default({ provider });
|
|
124
|
+
this.getSavedGasFees = getSavedGasFees !== null && getSavedGasFees !== void 0 ? getSavedGasFees : ((_chainId) => undefined);
|
|
111
125
|
this.getCurrentAccountEIP1559Compatibility =
|
|
112
126
|
getCurrentAccountEIP1559Compatibility;
|
|
113
127
|
this.getCurrentNetworkEIP1559Compatibility =
|
|
@@ -117,6 +131,15 @@ class TransactionController extends base_controller_1.BaseController {
|
|
|
117
131
|
this.getPermittedAccounts = getPermittedAccounts;
|
|
118
132
|
this.getSelectedAddress = getSelectedAddress;
|
|
119
133
|
this.securityProviderRequest = securityProviderRequest;
|
|
134
|
+
this.afterSign = (_a = hooks === null || hooks === void 0 ? void 0 : hooks.afterSign) !== null && _a !== void 0 ? _a : (() => true);
|
|
135
|
+
this.beforeApproveOnInit = (_b = hooks === null || hooks === void 0 ? void 0 : hooks.beforeApproveOnInit) !== null && _b !== void 0 ? _b : (() => true);
|
|
136
|
+
this.beforeCheckPendingTransaction =
|
|
137
|
+
(_c = hooks === null || hooks === void 0 ? void 0 : hooks.beforeCheckPendingTransaction) !== null && _c !== void 0 ? _c :
|
|
138
|
+
/* istanbul ignore next */
|
|
139
|
+
(() => true);
|
|
140
|
+
this.beforePublish = (_d = hooks === null || hooks === void 0 ? void 0 : hooks.beforePublish) !== null && _d !== void 0 ? _d : (() => true);
|
|
141
|
+
this.getAdditionalSignArguments =
|
|
142
|
+
(_e = hooks === null || hooks === void 0 ? void 0 : hooks.getAdditionalSignArguments) !== null && _e !== void 0 ? _e : (() => []);
|
|
120
143
|
this.nonceTracker = new nonce_tracker_1.default({
|
|
121
144
|
provider,
|
|
122
145
|
blockTracker,
|
|
@@ -139,24 +162,35 @@ class TransactionController extends base_controller_1.BaseController {
|
|
|
139
162
|
this.incomingTransactionHelper.hub.on('transactions', this.onIncomingTransactions.bind(this));
|
|
140
163
|
this.incomingTransactionHelper.hub.on('updatedLastFetchedBlockNumbers', this.onUpdatedLastFetchedBlockNumbers.bind(this));
|
|
141
164
|
this.pendingTransactionTracker = new PendingTransactionTracker_1.PendingTransactionTracker({
|
|
165
|
+
approveTransaction: this.approveTransaction.bind(this),
|
|
142
166
|
blockTracker,
|
|
143
|
-
failTransaction: this.failTransaction.bind(this),
|
|
144
167
|
getChainId: this.getChainId.bind(this),
|
|
145
168
|
getEthQuery: () => this.ethQuery,
|
|
146
169
|
getTransactions: () => this.state.transactions,
|
|
170
|
+
isResubmitEnabled: pendingTransactions.isResubmitEnabled,
|
|
147
171
|
nonceTracker: this.nonceTracker,
|
|
172
|
+
onStateChange: this.subscribe.bind(this),
|
|
173
|
+
publishTransaction: this.publishTransaction.bind(this),
|
|
174
|
+
hooks: {
|
|
175
|
+
beforeCheckPendingTransaction: this.beforeCheckPendingTransaction.bind(this),
|
|
176
|
+
beforePublish: this.beforePublish.bind(this),
|
|
177
|
+
},
|
|
148
178
|
});
|
|
149
|
-
this.
|
|
150
|
-
this.pendingTransactionTracker.hub.on('transaction-confirmed', (transactionMeta) => this.hub.emit(`${transactionMeta.id}:confirmed`, transactionMeta));
|
|
179
|
+
this.addPendingTransactionTrackerListeners();
|
|
151
180
|
onNetworkStateChange(() => {
|
|
152
|
-
// @ts-expect-error TODO: Provider type alignment
|
|
153
181
|
this.ethQuery = new eth_query_1.default(this.provider);
|
|
154
182
|
this.registry = new eth_method_registry_1.default({ provider: this.provider });
|
|
183
|
+
this.onBootCleanup();
|
|
155
184
|
});
|
|
156
|
-
this.
|
|
185
|
+
this.onBootCleanup();
|
|
157
186
|
}
|
|
158
|
-
failTransaction(transactionMeta, error) {
|
|
159
|
-
const newTransactionMeta = Object.assign(Object.assign({}, transactionMeta), { error, status: types_1.TransactionStatus.failed });
|
|
187
|
+
failTransaction(transactionMeta, error, actionId) {
|
|
188
|
+
const newTransactionMeta = Object.assign(Object.assign({}, transactionMeta), { error: (0, utils_1.normalizeTxError)(error), status: types_1.TransactionStatus.failed });
|
|
189
|
+
this.hub.emit('transaction-failed', {
|
|
190
|
+
actionId,
|
|
191
|
+
error: error.message,
|
|
192
|
+
transactionMeta: newTransactionMeta,
|
|
193
|
+
});
|
|
160
194
|
this.updateTransaction(newTransactionMeta, 'TransactionController#failTransaction - Add error message and set status to failed');
|
|
161
195
|
this.hub.emit(`${transactionMeta.id}:finished`, newTransactionMeta);
|
|
162
196
|
}
|
|
@@ -208,12 +242,14 @@ class TransactionController extends base_controller_1.BaseController {
|
|
|
208
242
|
* @param opts.securityAlertResponse - Response from security validator.
|
|
209
243
|
* @param opts.sendFlowHistory - The sendFlowHistory entries to add.
|
|
210
244
|
* @param opts.type - Type of transaction to add, such as 'cancel' or 'swap'.
|
|
245
|
+
* @param opts.swaps - Options for swaps transactions.
|
|
246
|
+
* @param opts.swaps.hasApproveTx - Whether the transaction has an approval transaction.
|
|
247
|
+
* @param opts.swaps.meta - Metadata for swap transaction.
|
|
211
248
|
* @returns Object containing a promise resolving to the transaction hash if approved.
|
|
212
249
|
*/
|
|
213
|
-
addTransaction(txParams, { actionId, deviceConfirmedOn, method, origin, requireApproval, securityAlertResponse, sendFlowHistory, type, } = {}) {
|
|
250
|
+
addTransaction(txParams, { actionId, deviceConfirmedOn, method, origin, requireApproval, securityAlertResponse, sendFlowHistory, swaps = {}, type, } = {}) {
|
|
214
251
|
return __awaiter(this, void 0, void 0, function* () {
|
|
215
252
|
const chainId = this.getChainId();
|
|
216
|
-
const { transactions } = this.state;
|
|
217
253
|
txParams = (0, utils_1.normalizeTxParams)(txParams);
|
|
218
254
|
const isEIP1559Compatible = yield this.getEIP1559Compatibility();
|
|
219
255
|
(0, validation_1.validateTxParams)(txParams, isEIP1559Compatible);
|
|
@@ -240,17 +276,7 @@ class TransactionController extends base_controller_1.BaseController {
|
|
|
240
276
|
verifiedOnBlockchain: false,
|
|
241
277
|
type: transactionType,
|
|
242
278
|
};
|
|
243
|
-
yield (
|
|
244
|
-
ethQuery: this.ethQuery,
|
|
245
|
-
providerConfig: this.getNetworkState().providerConfig,
|
|
246
|
-
txMeta: transactionMeta,
|
|
247
|
-
});
|
|
248
|
-
yield (0, gas_fees_1.updateGasFees)({
|
|
249
|
-
eip1559: isEIP1559Compatible,
|
|
250
|
-
ethQuery: this.ethQuery,
|
|
251
|
-
getGasFeeEstimates: this.getGasFeeEstimates.bind(this),
|
|
252
|
-
txMeta: transactionMeta,
|
|
253
|
-
});
|
|
279
|
+
yield this.updateGasProperties(transactionMeta);
|
|
254
280
|
// Checks if a transaction already exists with a given actionId
|
|
255
281
|
if (!existingTransactionMeta) {
|
|
256
282
|
// Set security provider response
|
|
@@ -265,16 +291,19 @@ class TransactionController extends base_controller_1.BaseController {
|
|
|
265
291
|
if (!this.isHistoryDisabled) {
|
|
266
292
|
(0, history_1.addInitialHistorySnapshot)(transactionMeta);
|
|
267
293
|
}
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
294
|
+
yield (0, swaps_1.updateSwapsTransaction)(transactionMeta, transactionType, swaps, {
|
|
295
|
+
isSwapsDisabled: this.isSwapsDisabled,
|
|
296
|
+
cancelTransaction: this.cancelTransaction.bind(this),
|
|
297
|
+
controllerHubEmitter: this.hub.emit.bind(this.hub),
|
|
271
298
|
});
|
|
299
|
+
this.addMetadata(transactionMeta);
|
|
272
300
|
this.hub.emit(`unapprovedTransaction`, transactionMeta);
|
|
273
301
|
}
|
|
274
302
|
return {
|
|
275
303
|
result: this.processApproval(transactionMeta, {
|
|
276
304
|
isExisting: Boolean(existingTransactionMeta),
|
|
277
305
|
requireApproval,
|
|
306
|
+
actionId,
|
|
278
307
|
}),
|
|
279
308
|
transactionMeta,
|
|
280
309
|
};
|
|
@@ -291,22 +320,6 @@ class TransactionController extends base_controller_1.BaseController {
|
|
|
291
320
|
yield this.incomingTransactionHelper.update();
|
|
292
321
|
});
|
|
293
322
|
}
|
|
294
|
-
/**
|
|
295
|
-
* Creates approvals for all unapproved transactions persisted.
|
|
296
|
-
*/
|
|
297
|
-
initApprovals() {
|
|
298
|
-
const chainId = this.getChainId();
|
|
299
|
-
const unapprovedTxs = this.state.transactions.filter((transaction) => transaction.status === types_1.TransactionStatus.unapproved &&
|
|
300
|
-
transaction.chainId === chainId);
|
|
301
|
-
for (const txMeta of unapprovedTxs) {
|
|
302
|
-
this.processApproval(txMeta, {
|
|
303
|
-
shouldShowRequest: false,
|
|
304
|
-
}).catch((error) => {
|
|
305
|
-
/* istanbul ignore next */
|
|
306
|
-
console.error('Error during persisted transaction approval', error);
|
|
307
|
-
});
|
|
308
|
-
}
|
|
309
|
-
}
|
|
310
323
|
/**
|
|
311
324
|
* Attempts to cancel a transaction based on its ID by setting its status to "rejected"
|
|
312
325
|
* and emitting a `<tx.id>:finished` hub event.
|
|
@@ -314,15 +327,20 @@ class TransactionController extends base_controller_1.BaseController {
|
|
|
314
327
|
* @param transactionId - The ID of the transaction to cancel.
|
|
315
328
|
* @param gasValues - The gas values to use for the cancellation transaction.
|
|
316
329
|
* @param options - The options for the cancellation transaction.
|
|
330
|
+
* @param options.actionId - Unique ID to prevent duplicate requests.
|
|
317
331
|
* @param options.estimatedBaseFee - The estimated base fee of the transaction.
|
|
318
332
|
*/
|
|
319
|
-
stopTransaction(transactionId, gasValues, { estimatedBaseFee } = {}) {
|
|
333
|
+
stopTransaction(transactionId, gasValues, { estimatedBaseFee, actionId, } = {}) {
|
|
320
334
|
var _a, _b;
|
|
321
335
|
return __awaiter(this, void 0, void 0, function* () {
|
|
336
|
+
// If transaction is found for same action id, do not create a cancel transaction.
|
|
337
|
+
if (this.getTransactionWithActionId(actionId)) {
|
|
338
|
+
return;
|
|
339
|
+
}
|
|
322
340
|
if (gasValues) {
|
|
323
341
|
(0, utils_1.validateGasValues)(gasValues);
|
|
324
342
|
}
|
|
325
|
-
const transactionMeta = this.
|
|
343
|
+
const transactionMeta = this.getTransaction(transactionId);
|
|
326
344
|
if (!transactionMeta) {
|
|
327
345
|
return;
|
|
328
346
|
}
|
|
@@ -349,13 +367,13 @@ class TransactionController extends base_controller_1.BaseController {
|
|
|
349
367
|
const newMaxPriorityFeePerGas = (maxPriorityFeePerGasValues &&
|
|
350
368
|
(0, utils_1.validateMinimumIncrease)(maxPriorityFeePerGasValues, minMaxPriorityFeePerGas)) ||
|
|
351
369
|
(existingMaxPriorityFeePerGas && minMaxPriorityFeePerGas);
|
|
352
|
-
const
|
|
370
|
+
const newTxParams = newMaxFeePerGas && newMaxPriorityFeePerGas
|
|
353
371
|
? {
|
|
354
372
|
from: transactionMeta.txParams.from,
|
|
355
373
|
gasLimit: transactionMeta.txParams.gas,
|
|
356
374
|
maxFeePerGas: newMaxFeePerGas,
|
|
357
375
|
maxPriorityFeePerGas: newMaxPriorityFeePerGas,
|
|
358
|
-
type: 2,
|
|
376
|
+
type: '2',
|
|
359
377
|
nonce: transactionMeta.txParams.nonce,
|
|
360
378
|
to: transactionMeta.txParams.from,
|
|
361
379
|
value: '0x0',
|
|
@@ -368,14 +386,33 @@ class TransactionController extends base_controller_1.BaseController {
|
|
|
368
386
|
to: transactionMeta.txParams.from,
|
|
369
387
|
value: '0x0',
|
|
370
388
|
};
|
|
371
|
-
const unsignedEthTx = this.prepareUnsignedEthTx(
|
|
389
|
+
const unsignedEthTx = this.prepareUnsignedEthTx(newTxParams);
|
|
372
390
|
const signedTx = yield this.sign(unsignedEthTx, transactionMeta.txParams.from);
|
|
373
|
-
yield this.updateTransactionMetaRSV(transactionMeta, signedTx);
|
|
374
391
|
const rawTx = (0, ethereumjs_util_1.bufferToHex)(signedTx.serialize());
|
|
375
|
-
yield
|
|
376
|
-
|
|
377
|
-
|
|
378
|
-
|
|
392
|
+
const hash = yield this.publishTransaction(rawTx);
|
|
393
|
+
const cancelTransactionMeta = {
|
|
394
|
+
actionId,
|
|
395
|
+
chainId: transactionMeta.chainId,
|
|
396
|
+
estimatedBaseFee,
|
|
397
|
+
hash,
|
|
398
|
+
id: (0, uuid_1.v1)(),
|
|
399
|
+
originalGasEstimate: transactionMeta.txParams.gas,
|
|
400
|
+
status: types_1.TransactionStatus.submitted,
|
|
401
|
+
time: Date.now(),
|
|
402
|
+
type: types_1.TransactionType.cancel,
|
|
403
|
+
txParams: newTxParams,
|
|
404
|
+
};
|
|
405
|
+
this.addMetadata(cancelTransactionMeta);
|
|
406
|
+
// stopTransaction has no approval request, so we assume the user has already approved the transaction
|
|
407
|
+
this.hub.emit('transaction-approved', {
|
|
408
|
+
transactionMeta: cancelTransactionMeta,
|
|
409
|
+
actionId,
|
|
410
|
+
});
|
|
411
|
+
this.hub.emit('transaction-submitted', {
|
|
412
|
+
transactionMeta: cancelTransactionMeta,
|
|
413
|
+
actionId,
|
|
414
|
+
});
|
|
415
|
+
this.hub.emit(`${cancelTransactionMeta.id}:finished`, cancelTransactionMeta);
|
|
379
416
|
});
|
|
380
417
|
}
|
|
381
418
|
/**
|
|
@@ -406,7 +443,6 @@ class TransactionController extends base_controller_1.BaseController {
|
|
|
406
443
|
if (!this.sign) {
|
|
407
444
|
throw new Error('No sign method defined.');
|
|
408
445
|
}
|
|
409
|
-
const { transactions } = this.state;
|
|
410
446
|
// gasPrice (legacy non EIP1559)
|
|
411
447
|
const minGasPrice = (0, utils_1.getIncreasedPriceFromExisting)(transactionMeta.txParams.gasPrice, exports.SPEED_UP_RATE);
|
|
412
448
|
const gasPriceFromValues = (0, utils_1.isGasPriceValue)(gasValues) && gasValues.gasPrice;
|
|
@@ -428,18 +464,26 @@ class TransactionController extends base_controller_1.BaseController {
|
|
|
428
464
|
(0, utils_1.validateMinimumIncrease)(maxPriorityFeePerGasValues, minMaxPriorityFeePerGas)) ||
|
|
429
465
|
(existingMaxPriorityFeePerGas && minMaxPriorityFeePerGas);
|
|
430
466
|
const txParams = newMaxFeePerGas && newMaxPriorityFeePerGas
|
|
431
|
-
? 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 });
|
|
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 });
|
|
432
468
|
const unsignedEthTx = this.prepareUnsignedEthTx(txParams);
|
|
433
469
|
const signedTx = yield this.sign(unsignedEthTx, transactionMeta.txParams.from);
|
|
434
470
|
yield this.updateTransactionMetaRSV(transactionMeta, signedTx);
|
|
435
471
|
const rawTx = (0, ethereumjs_util_1.bufferToHex)(signedTx.serialize());
|
|
436
472
|
const hash = yield (0, controller_utils_1.query)(this.ethQuery, 'sendRawTransaction', [rawTx]);
|
|
437
473
|
const baseTransactionMeta = Object.assign(Object.assign({}, transactionMeta), { estimatedBaseFee, id: (0, uuid_1.v1)(), time: Date.now(), hash,
|
|
438
|
-
actionId, originalGasEstimate: transactionMeta.txParams.gas, type: types_1.TransactionType.retry });
|
|
474
|
+
actionId, originalGasEstimate: transactionMeta.txParams.gas, type: types_1.TransactionType.retry, originalType: transactionMeta.type });
|
|
439
475
|
const newTransactionMeta = newMaxFeePerGas && newMaxPriorityFeePerGas
|
|
440
476
|
? Object.assign(Object.assign({}, baseTransactionMeta), { txParams: Object.assign(Object.assign({}, transactionMeta.txParams), { maxFeePerGas: newMaxFeePerGas, maxPriorityFeePerGas: newMaxPriorityFeePerGas }) }) : Object.assign(Object.assign({}, baseTransactionMeta), { txParams: Object.assign(Object.assign({}, transactionMeta.txParams), { gasPrice: newGasPrice }) });
|
|
441
|
-
|
|
442
|
-
|
|
477
|
+
this.addMetadata(newTransactionMeta);
|
|
478
|
+
// speedUpTransaction has no approval request, so we assume the user has already approved the transaction
|
|
479
|
+
this.hub.emit('transaction-approved', {
|
|
480
|
+
transactionMeta: newTransactionMeta,
|
|
481
|
+
actionId,
|
|
482
|
+
});
|
|
483
|
+
this.hub.emit('transaction-submitted', {
|
|
484
|
+
transactionMeta: newTransactionMeta,
|
|
485
|
+
actionId,
|
|
486
|
+
});
|
|
443
487
|
this.hub.emit(`${transactionMeta.id}:speedup`, newTransactionMeta);
|
|
444
488
|
});
|
|
445
489
|
}
|
|
@@ -455,6 +499,22 @@ class TransactionController extends base_controller_1.BaseController {
|
|
|
455
499
|
return { gas: estimatedGas, simulationFails };
|
|
456
500
|
});
|
|
457
501
|
}
|
|
502
|
+
/**
|
|
503
|
+
* Estimates required gas for a given transaction and add additional gas buffer with the given multiplier.
|
|
504
|
+
*
|
|
505
|
+
* @param transaction - The transaction params to estimate gas for.
|
|
506
|
+
* @param multiplier - The multiplier to use for the gas buffer.
|
|
507
|
+
*/
|
|
508
|
+
estimateGasBuffered(transaction, multiplier) {
|
|
509
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
510
|
+
const { blockGasLimit, estimatedGas, simulationFails } = yield (0, gas_1.estimateGas)(transaction, this.ethQuery);
|
|
511
|
+
const gas = (0, gas_1.addGasBuffer)(estimatedGas, blockGasLimit, multiplier);
|
|
512
|
+
return {
|
|
513
|
+
gas,
|
|
514
|
+
simulationFails,
|
|
515
|
+
};
|
|
516
|
+
});
|
|
517
|
+
}
|
|
458
518
|
/**
|
|
459
519
|
* Updates an existing transaction in state.
|
|
460
520
|
*
|
|
@@ -472,6 +532,23 @@ class TransactionController extends base_controller_1.BaseController {
|
|
|
472
532
|
transactions[index] = transactionMeta;
|
|
473
533
|
this.update({ transactions: this.trimTransactionsForState(transactions) });
|
|
474
534
|
}
|
|
535
|
+
/**
|
|
536
|
+
* Update the security alert response for a transaction.
|
|
537
|
+
*
|
|
538
|
+
* @param transactionId - ID of the transaction.
|
|
539
|
+
* @param securityAlertResponse - The new security alert response for the transaction.
|
|
540
|
+
*/
|
|
541
|
+
updateSecurityAlertResponse(transactionId, securityAlertResponse) {
|
|
542
|
+
if (!securityAlertResponse) {
|
|
543
|
+
throw new Error('updateSecurityAlertResponse: securityAlertResponse should not be null');
|
|
544
|
+
}
|
|
545
|
+
const transactionMeta = this.getTransaction(transactionId);
|
|
546
|
+
if (!transactionMeta) {
|
|
547
|
+
throw new Error(`Cannot update security alert response as no transaction metadata found`);
|
|
548
|
+
}
|
|
549
|
+
const updatedMeta = (0, lodash_1.merge)(transactionMeta, { securityAlertResponse });
|
|
550
|
+
this.updateTransaction(updatedMeta, 'TransactionController:updatesecurityAlertResponse - securityAlertResponse updated');
|
|
551
|
+
}
|
|
475
552
|
/**
|
|
476
553
|
* Removes all transactions from state, optionally based on the current network.
|
|
477
554
|
*
|
|
@@ -529,6 +606,26 @@ class TransactionController extends base_controller_1.BaseController {
|
|
|
529
606
|
this.markNonceDuplicatesDropped(transactionId);
|
|
530
607
|
// Update external provided transaction with updated gas values and confirmed status.
|
|
531
608
|
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
|
+
}
|
|
626
|
+
this.hub.emit('transaction-confirmed', {
|
|
627
|
+
transactionMeta,
|
|
628
|
+
});
|
|
532
629
|
}
|
|
533
630
|
catch (error) {
|
|
534
631
|
console.error(error);
|
|
@@ -610,6 +707,36 @@ class TransactionController extends base_controller_1.BaseController {
|
|
|
610
707
|
this.updateTransaction(updatedMeta, 'TransactionController:updateTransactionGasFees - gas values updated');
|
|
611
708
|
return this.getTransaction(transactionId);
|
|
612
709
|
}
|
|
710
|
+
/**
|
|
711
|
+
* Update the previous gas values of a transaction.
|
|
712
|
+
*
|
|
713
|
+
* @param transactionId - The ID of the transaction to update.
|
|
714
|
+
* @param previousGas - Previous gas values to update.
|
|
715
|
+
* @param previousGas.gasLimit - Maxmimum number of units of gas to use for this transaction.
|
|
716
|
+
* @param previousGas.maxFeePerGas - Maximum amount per gas to pay for the transaction, including the priority fee.
|
|
717
|
+
* @param previousGas.maxPriorityFeePerGas - Maximum amount per gas to give to validator as incentive.
|
|
718
|
+
* @returns The updated transactionMeta.
|
|
719
|
+
*/
|
|
720
|
+
updatePreviousGasParams(transactionId, { gasLimit, maxFeePerGas, maxPriorityFeePerGas, }) {
|
|
721
|
+
const transactionMeta = this.getTransaction(transactionId);
|
|
722
|
+
if (!transactionMeta) {
|
|
723
|
+
throw new Error(`Cannot update transaction as no transaction metadata found`);
|
|
724
|
+
}
|
|
725
|
+
(0, utils_1.validateIfTransactionUnapproved)(transactionMeta, 'updatePreviousGasParams');
|
|
726
|
+
const transactionPreviousGas = {
|
|
727
|
+
previousGas: {
|
|
728
|
+
gasLimit,
|
|
729
|
+
maxFeePerGas,
|
|
730
|
+
maxPriorityFeePerGas,
|
|
731
|
+
},
|
|
732
|
+
};
|
|
733
|
+
// only update what is defined
|
|
734
|
+
transactionPreviousGas.previousGas = (0, lodash_1.pickBy)(transactionPreviousGas.previousGas);
|
|
735
|
+
// merge updated previous gas values with existing transaction meta
|
|
736
|
+
const updatedMeta = (0, lodash_1.merge)(transactionMeta, transactionPreviousGas);
|
|
737
|
+
this.updateTransaction(updatedMeta, 'TransactionController:updatePreviousGasParams - Previous gas values updated');
|
|
738
|
+
return this.getTransaction(transactionId);
|
|
739
|
+
}
|
|
613
740
|
/**
|
|
614
741
|
* Gets the next nonce according to the nonce-tracker.
|
|
615
742
|
* Ensure `releaseLock` is called once processing of the `nonce` value is complete.
|
|
@@ -622,7 +749,208 @@ class TransactionController extends base_controller_1.BaseController {
|
|
|
622
749
|
return this.nonceTracker.getNonceLock(address);
|
|
623
750
|
});
|
|
624
751
|
}
|
|
625
|
-
|
|
752
|
+
/**
|
|
753
|
+
* Signs and returns the raw transaction data for provided transaction params list.
|
|
754
|
+
*
|
|
755
|
+
* @param listOfTxParams - The list of transaction params to approve.
|
|
756
|
+
* @returns The raw transactions.
|
|
757
|
+
*/
|
|
758
|
+
approveTransactionsWithSameNonce(listOfTxParams = []) {
|
|
759
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
760
|
+
if (listOfTxParams.length === 0) {
|
|
761
|
+
return '';
|
|
762
|
+
}
|
|
763
|
+
const initialTx = listOfTxParams[0];
|
|
764
|
+
const common = this.getCommonConfiguration();
|
|
765
|
+
const initialTxAsEthTx = tx_1.TransactionFactory.fromTxData(initialTx, {
|
|
766
|
+
common,
|
|
767
|
+
});
|
|
768
|
+
const initialTxAsSerializedHex = (0, ethereumjs_util_1.bufferToHex)(initialTxAsEthTx.serialize());
|
|
769
|
+
if (this.inProcessOfSigning.has(initialTxAsSerializedHex)) {
|
|
770
|
+
return '';
|
|
771
|
+
}
|
|
772
|
+
this.inProcessOfSigning.add(initialTxAsSerializedHex);
|
|
773
|
+
let rawTransactions, nonceLock;
|
|
774
|
+
try {
|
|
775
|
+
// TODO: we should add a check to verify that all transactions have the same from address
|
|
776
|
+
const fromAddress = initialTx.from;
|
|
777
|
+
nonceLock = yield this.nonceTracker.getNonceLock(fromAddress);
|
|
778
|
+
const nonce = nonceLock.nextNonce;
|
|
779
|
+
rawTransactions = yield Promise.all(listOfTxParams.map((txParams) => {
|
|
780
|
+
txParams.nonce = (0, ethereumjs_util_1.addHexPrefix)(nonce.toString(16));
|
|
781
|
+
return this.signExternalTransaction(txParams);
|
|
782
|
+
}));
|
|
783
|
+
}
|
|
784
|
+
catch (err) {
|
|
785
|
+
(0, logger_1.projectLogger)('Error while signing transactions with same nonce', err);
|
|
786
|
+
// Must set transaction to submitted/failed before releasing lock
|
|
787
|
+
// continue with error chain
|
|
788
|
+
throw err;
|
|
789
|
+
}
|
|
790
|
+
finally {
|
|
791
|
+
if (nonceLock) {
|
|
792
|
+
nonceLock.releaseLock();
|
|
793
|
+
}
|
|
794
|
+
this.inProcessOfSigning.delete(initialTxAsSerializedHex);
|
|
795
|
+
}
|
|
796
|
+
return rawTransactions;
|
|
797
|
+
});
|
|
798
|
+
}
|
|
799
|
+
/**
|
|
800
|
+
* Update a custodial transaction.
|
|
801
|
+
*
|
|
802
|
+
* @param transactionId - The ID of the transaction to update.
|
|
803
|
+
* @param options - The custodial transaction options to update.
|
|
804
|
+
* @param options.custodyStatus - The new custody status value to be assigned.
|
|
805
|
+
* @param options.errorMessage - The error message to be assigned in case transaction status update to failed.
|
|
806
|
+
* @param options.hash - The new hash value to be assigned.
|
|
807
|
+
* @param options.status - The new status value to be assigned.
|
|
808
|
+
*/
|
|
809
|
+
updateCustodialTransaction(transactionId, { custodyStatus, errorMessage, hash, status, }) {
|
|
810
|
+
let transactionMeta;
|
|
811
|
+
transactionMeta = this.getTransaction(transactionId);
|
|
812
|
+
if (!transactionMeta) {
|
|
813
|
+
throw new Error(`Cannot update custodial transaction as no transaction metadata found`);
|
|
814
|
+
}
|
|
815
|
+
if (!transactionMeta.custodyId) {
|
|
816
|
+
throw new Error('Transaction must be a custodian transaction');
|
|
817
|
+
}
|
|
818
|
+
if (status &&
|
|
819
|
+
![
|
|
820
|
+
types_1.TransactionStatus.submitted,
|
|
821
|
+
types_1.TransactionStatus.signed,
|
|
822
|
+
types_1.TransactionStatus.failed,
|
|
823
|
+
].includes(status)) {
|
|
824
|
+
throw new Error(`Cannot update custodial transaction with status: ${status}`);
|
|
825
|
+
}
|
|
826
|
+
if (status === types_1.TransactionStatus.signed) {
|
|
827
|
+
transactionMeta.status = status;
|
|
828
|
+
}
|
|
829
|
+
if (status === types_1.TransactionStatus.submitted) {
|
|
830
|
+
transactionMeta.submittedTime = new Date().getTime();
|
|
831
|
+
transactionMeta.status = status;
|
|
832
|
+
}
|
|
833
|
+
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 });
|
|
835
|
+
}
|
|
836
|
+
if (custodyStatus) {
|
|
837
|
+
transactionMeta.custodyStatus = custodyStatus;
|
|
838
|
+
}
|
|
839
|
+
if (hash) {
|
|
840
|
+
transactionMeta.hash = hash;
|
|
841
|
+
}
|
|
842
|
+
this.updateTransaction(transactionMeta, `TransactionController:updateCustodialTransaction - Custodial transaction updated`);
|
|
843
|
+
}
|
|
844
|
+
signExternalTransaction(transactionParams) {
|
|
845
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
846
|
+
if (!this.sign) {
|
|
847
|
+
throw new Error('No sign method defined.');
|
|
848
|
+
}
|
|
849
|
+
const normalizedTransactionParams = (0, utils_1.normalizeTxParams)(transactionParams);
|
|
850
|
+
const chainId = this.getChainId();
|
|
851
|
+
const type = (0, utils_1.isEIP1559Transaction)(normalizedTransactionParams)
|
|
852
|
+
? types_1.TransactionEnvelopeType.feeMarket
|
|
853
|
+
: types_1.TransactionEnvelopeType.legacy;
|
|
854
|
+
const updatedTransactionParams = Object.assign(Object.assign({}, normalizedTransactionParams), { type, gasLimit: normalizedTransactionParams.gas, chainId });
|
|
855
|
+
const { from } = updatedTransactionParams;
|
|
856
|
+
const common = this.getCommonConfiguration();
|
|
857
|
+
const unsignedTransaction = tx_1.TransactionFactory.fromTxData(updatedTransactionParams, { common });
|
|
858
|
+
const signedTransaction = yield this.sign(unsignedTransaction, from);
|
|
859
|
+
const rawTransaction = (0, ethereumjs_util_1.bufferToHex)(signedTransaction.serialize());
|
|
860
|
+
return rawTransaction;
|
|
861
|
+
});
|
|
862
|
+
}
|
|
863
|
+
/**
|
|
864
|
+
* Removes unapproved transactions from state.
|
|
865
|
+
*/
|
|
866
|
+
clearUnapprovedTransactions() {
|
|
867
|
+
const transactions = this.state.transactions.filter(({ status }) => status !== types_1.TransactionStatus.unapproved);
|
|
868
|
+
this.update({ transactions: this.trimTransactionsForState(transactions) });
|
|
869
|
+
}
|
|
870
|
+
addMetadata(transactionMeta) {
|
|
871
|
+
const { transactions } = this.state;
|
|
872
|
+
transactions.push(transactionMeta);
|
|
873
|
+
this.update({ transactions: this.trimTransactionsForState(transactions) });
|
|
874
|
+
}
|
|
875
|
+
updateGasProperties(transactionMeta) {
|
|
876
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
877
|
+
const isEIP1559Compatible = yield this.getEIP1559Compatibility();
|
|
878
|
+
const chainId = this.getChainId();
|
|
879
|
+
yield (0, gas_1.updateGas)({
|
|
880
|
+
ethQuery: this.ethQuery,
|
|
881
|
+
providerConfig: this.getNetworkState().providerConfig,
|
|
882
|
+
txMeta: transactionMeta,
|
|
883
|
+
});
|
|
884
|
+
yield (0, gas_fees_1.updateGasFees)({
|
|
885
|
+
eip1559: isEIP1559Compatible,
|
|
886
|
+
ethQuery: this.ethQuery,
|
|
887
|
+
getSavedGasFees: this.getSavedGasFees.bind(this, chainId),
|
|
888
|
+
getGasFeeEstimates: this.getGasFeeEstimates.bind(this),
|
|
889
|
+
txMeta: transactionMeta,
|
|
890
|
+
});
|
|
891
|
+
});
|
|
892
|
+
}
|
|
893
|
+
getCurrentChainTransactionsByStatus(status) {
|
|
894
|
+
const chainId = this.getChainId();
|
|
895
|
+
return this.state.transactions.filter((transaction) => transaction.status === status && transaction.chainId === chainId);
|
|
896
|
+
}
|
|
897
|
+
onBootCleanup() {
|
|
898
|
+
this.createApprovalsForUnapprovedTransactions();
|
|
899
|
+
this.loadGasValuesForUnapprovedTransactions();
|
|
900
|
+
this.submitApprovedTransactions();
|
|
901
|
+
}
|
|
902
|
+
/**
|
|
903
|
+
* Create approvals for all unapproved transactions on current chain.
|
|
904
|
+
*/
|
|
905
|
+
createApprovalsForUnapprovedTransactions() {
|
|
906
|
+
const unapprovedTransactions = this.getCurrentChainTransactionsByStatus(types_1.TransactionStatus.unapproved);
|
|
907
|
+
for (const transactionMeta of unapprovedTransactions) {
|
|
908
|
+
this.processApproval(transactionMeta, {
|
|
909
|
+
shouldShowRequest: false,
|
|
910
|
+
}).catch((error) => {
|
|
911
|
+
if ((error === null || error === void 0 ? void 0 : error.code) === rpc_errors_1.errorCodes.provider.userRejectedRequest) {
|
|
912
|
+
return;
|
|
913
|
+
}
|
|
914
|
+
/* istanbul ignore next */
|
|
915
|
+
console.error('Error during persisted transaction approval', error);
|
|
916
|
+
});
|
|
917
|
+
}
|
|
918
|
+
}
|
|
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
|
+
/**
|
|
940
|
+
* Force to submit approved transactions on current chain.
|
|
941
|
+
*/
|
|
942
|
+
submitApprovedTransactions() {
|
|
943
|
+
const approvedTransactions = this.getCurrentChainTransactionsByStatus(types_1.TransactionStatus.approved);
|
|
944
|
+
for (const transactionMeta of approvedTransactions) {
|
|
945
|
+
if (this.beforeApproveOnInit(transactionMeta)) {
|
|
946
|
+
this.approveTransaction(transactionMeta.id).catch((error) => {
|
|
947
|
+
/* istanbul ignore next */
|
|
948
|
+
console.error('Error while submitting persisted transaction', error);
|
|
949
|
+
});
|
|
950
|
+
}
|
|
951
|
+
}
|
|
952
|
+
}
|
|
953
|
+
processApproval(transactionMeta, { isExisting = false, requireApproval, shouldShowRequest = true, actionId, }) {
|
|
626
954
|
return __awaiter(this, void 0, void 0, function* () {
|
|
627
955
|
const transactionId = transactionMeta.id;
|
|
628
956
|
let resultCallbacks;
|
|
@@ -641,17 +969,22 @@ class TransactionController extends base_controller_1.BaseController {
|
|
|
641
969
|
const { isCompleted: isTxCompleted } = this.isTransactionCompleted(transactionId);
|
|
642
970
|
if (!isTxCompleted) {
|
|
643
971
|
yield this.approveTransaction(transactionId);
|
|
972
|
+
const updatedTransactionMeta = this.getTransaction(transactionId);
|
|
973
|
+
this.hub.emit('transaction-approved', {
|
|
974
|
+
transactionMeta: updatedTransactionMeta,
|
|
975
|
+
actionId,
|
|
976
|
+
});
|
|
644
977
|
}
|
|
645
978
|
}
|
|
646
979
|
catch (error) {
|
|
647
980
|
const { isCompleted: isTxCompleted } = this.isTransactionCompleted(transactionId);
|
|
648
981
|
if (!isTxCompleted) {
|
|
649
|
-
if (error.code === rpc_errors_1.errorCodes.provider.userRejectedRequest) {
|
|
650
|
-
this.cancelTransaction(transactionId);
|
|
651
|
-
throw rpc_errors_1.providerErrors.userRejectedRequest('User
|
|
982
|
+
if ((error === null || error === void 0 ? void 0 : error.code) === rpc_errors_1.errorCodes.provider.userRejectedRequest) {
|
|
983
|
+
this.cancelTransaction(transactionId, actionId);
|
|
984
|
+
throw rpc_errors_1.providerErrors.userRejectedRequest('MetaMask Tx Signature: User denied transaction signature.');
|
|
652
985
|
}
|
|
653
986
|
else {
|
|
654
|
-
this.failTransaction(meta, error);
|
|
987
|
+
this.failTransaction(meta, error, actionId);
|
|
655
988
|
}
|
|
656
989
|
}
|
|
657
990
|
}
|
|
@@ -661,10 +994,6 @@ class TransactionController extends base_controller_1.BaseController {
|
|
|
661
994
|
case types_1.TransactionStatus.failed:
|
|
662
995
|
resultCallbacks === null || resultCallbacks === void 0 ? void 0 : resultCallbacks.error(finalMeta.error);
|
|
663
996
|
throw rpc_errors_1.rpcErrors.internal(finalMeta.error.message);
|
|
664
|
-
case types_1.TransactionStatus.cancelled:
|
|
665
|
-
const cancelError = rpc_errors_1.rpcErrors.internal('User cancelled the transaction');
|
|
666
|
-
resultCallbacks === null || resultCallbacks === void 0 ? void 0 : resultCallbacks.error(cancelError);
|
|
667
|
-
throw cancelError;
|
|
668
997
|
case types_1.TransactionStatus.submitted:
|
|
669
998
|
resultCallbacks === null || resultCallbacks === void 0 ? void 0 : resultCallbacks.success();
|
|
670
999
|
return finalMeta.hash;
|
|
@@ -703,6 +1032,10 @@ class TransactionController extends base_controller_1.BaseController {
|
|
|
703
1032
|
this.failTransaction(transactionMeta, new Error('No chainId defined.'));
|
|
704
1033
|
return;
|
|
705
1034
|
}
|
|
1035
|
+
if (this.inProcessOfSigning.has(transactionId)) {
|
|
1036
|
+
(0, logger_1.projectLogger)('Skipping approval as signing in progress', transactionId);
|
|
1037
|
+
return;
|
|
1038
|
+
}
|
|
706
1039
|
const { approved: status } = types_1.TransactionStatus;
|
|
707
1040
|
let nonceToUse = nonce;
|
|
708
1041
|
// if a nonce already exists on the transactionMeta it means this is a speedup or cancel transaction
|
|
@@ -715,27 +1048,31 @@ class TransactionController extends base_controller_1.BaseController {
|
|
|
715
1048
|
transactionMeta.txParams.nonce = nonceToUse;
|
|
716
1049
|
transactionMeta.txParams.chainId = chainId;
|
|
717
1050
|
const baseTxParams = Object.assign(Object.assign({}, transactionMeta.txParams), { gasLimit: transactionMeta.txParams.gas });
|
|
1051
|
+
this.updateTransaction(transactionMeta, 'TransactionController#approveTransaction - Transaction approved');
|
|
718
1052
|
const isEIP1559 = (0, utils_1.isEIP1559Transaction)(transactionMeta.txParams);
|
|
719
1053
|
const txParams = isEIP1559
|
|
720
1054
|
? Object.assign(Object.assign({}, baseTxParams), { maxFeePerGas: transactionMeta.txParams.maxFeePerGas, maxPriorityFeePerGas: transactionMeta.txParams.maxPriorityFeePerGas, estimatedBaseFee: transactionMeta.txParams.estimatedBaseFee,
|
|
721
1055
|
// specify type 2 if maxFeePerGas and maxPriorityFeePerGas are set
|
|
722
|
-
type: 2 }) : baseTxParams;
|
|
1056
|
+
type: '2' }) : baseTxParams;
|
|
723
1057
|
// delete gasPrice if maxFeePerGas and maxPriorityFeePerGas are set
|
|
724
1058
|
if (isEIP1559) {
|
|
725
1059
|
delete txParams.gasPrice;
|
|
726
1060
|
}
|
|
727
|
-
const
|
|
728
|
-
|
|
729
|
-
|
|
730
|
-
|
|
731
|
-
|
|
732
|
-
|
|
733
|
-
|
|
734
|
-
|
|
735
|
-
const hash = yield
|
|
1061
|
+
const rawTx = yield this.signTransaction(transactionMeta);
|
|
1062
|
+
if (!this.beforePublish(transactionMeta)) {
|
|
1063
|
+
(0, logger_1.projectLogger)('Skipping publishing transaction based on hook');
|
|
1064
|
+
return;
|
|
1065
|
+
}
|
|
1066
|
+
if (!rawTx) {
|
|
1067
|
+
return;
|
|
1068
|
+
}
|
|
1069
|
+
const hash = yield this.publishTransaction(rawTx);
|
|
736
1070
|
transactionMeta.hash = hash;
|
|
737
1071
|
transactionMeta.status = types_1.TransactionStatus.submitted;
|
|
738
1072
|
transactionMeta.submittedTime = new Date().getTime();
|
|
1073
|
+
this.hub.emit('transaction-submitted', {
|
|
1074
|
+
transactionMeta,
|
|
1075
|
+
});
|
|
739
1076
|
this.updateTransaction(transactionMeta, 'TransactionController#approveTransaction - Transaction submitted');
|
|
740
1077
|
this.hub.emit(`${transactionMeta.id}:finished`, transactionMeta);
|
|
741
1078
|
}
|
|
@@ -743,6 +1080,7 @@ class TransactionController extends base_controller_1.BaseController {
|
|
|
743
1080
|
this.failTransaction(transactionMeta, error);
|
|
744
1081
|
}
|
|
745
1082
|
finally {
|
|
1083
|
+
this.inProcessOfSigning.delete(transactionId);
|
|
746
1084
|
// must set transaction to submitted/failed before releasing lock
|
|
747
1085
|
if (nonceLock) {
|
|
748
1086
|
nonceLock.releaseLock();
|
|
@@ -751,19 +1089,29 @@ class TransactionController extends base_controller_1.BaseController {
|
|
|
751
1089
|
}
|
|
752
1090
|
});
|
|
753
1091
|
}
|
|
1092
|
+
publishTransaction(rawTransaction) {
|
|
1093
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
1094
|
+
return yield (0, controller_utils_1.query)(this.ethQuery, 'sendRawTransaction', [rawTransaction]);
|
|
1095
|
+
});
|
|
1096
|
+
}
|
|
754
1097
|
/**
|
|
755
1098
|
* Cancels a transaction based on its ID by setting its status to "rejected"
|
|
756
1099
|
* and emitting a `<tx.id>:finished` hub event.
|
|
757
1100
|
*
|
|
758
1101
|
* @param transactionId - The ID of the transaction to cancel.
|
|
1102
|
+
* @param actionId - The actionId passed from UI
|
|
759
1103
|
*/
|
|
760
|
-
cancelTransaction(transactionId) {
|
|
1104
|
+
cancelTransaction(transactionId, actionId) {
|
|
761
1105
|
const transactionMeta = this.state.transactions.find(({ id }) => id === transactionId);
|
|
762
1106
|
if (!transactionMeta) {
|
|
763
1107
|
return;
|
|
764
1108
|
}
|
|
765
1109
|
transactionMeta.status = types_1.TransactionStatus.rejected;
|
|
766
1110
|
this.hub.emit(`${transactionMeta.id}:finished`, transactionMeta);
|
|
1111
|
+
this.hub.emit('transaction-rejected', {
|
|
1112
|
+
transactionMeta,
|
|
1113
|
+
actionId,
|
|
1114
|
+
});
|
|
767
1115
|
const transactions = this.state.transactions.filter(({ id }) => id !== transactionId);
|
|
768
1116
|
this.update({ transactions: this.trimTransactionsForState(transactions) });
|
|
769
1117
|
}
|
|
@@ -812,8 +1160,7 @@ class TransactionController extends base_controller_1.BaseController {
|
|
|
812
1160
|
isFinalState(status) {
|
|
813
1161
|
return (status === types_1.TransactionStatus.rejected ||
|
|
814
1162
|
status === types_1.TransactionStatus.confirmed ||
|
|
815
|
-
status === types_1.TransactionStatus.failed
|
|
816
|
-
status === types_1.TransactionStatus.cancelled);
|
|
1163
|
+
status === types_1.TransactionStatus.failed);
|
|
817
1164
|
}
|
|
818
1165
|
/**
|
|
819
1166
|
* Whether the transaction has at least completed all local processing.
|
|
@@ -823,7 +1170,6 @@ class TransactionController extends base_controller_1.BaseController {
|
|
|
823
1170
|
*/
|
|
824
1171
|
isLocalFinalState(status) {
|
|
825
1172
|
return [
|
|
826
|
-
types_1.TransactionStatus.cancelled,
|
|
827
1173
|
types_1.TransactionStatus.confirmed,
|
|
828
1174
|
types_1.TransactionStatus.failed,
|
|
829
1175
|
types_1.TransactionStatus.rejected,
|
|
@@ -910,10 +1256,6 @@ class TransactionController extends base_controller_1.BaseController {
|
|
|
910
1256
|
this.update({ lastFetchedBlockNumbers });
|
|
911
1257
|
this.hub.emit('incomingTransactionBlock', blockNumber);
|
|
912
1258
|
}
|
|
913
|
-
onPendingTransactionsUpdate(transactions) {
|
|
914
|
-
(0, logger_1.pendingTransactionsLogger)('Updated pending transactions');
|
|
915
|
-
this.update({ transactions: this.trimTransactionsForState(transactions) });
|
|
916
|
-
}
|
|
917
1259
|
generateDappSuggestedGasFees(txParams, origin) {
|
|
918
1260
|
if (!origin || origin === controller_utils_1.ORIGIN_METAMASK) {
|
|
919
1261
|
return undefined;
|
|
@@ -1005,6 +1347,9 @@ class TransactionController extends base_controller_1.BaseController {
|
|
|
1005
1347
|
*/
|
|
1006
1348
|
setTransactionStatusDropped(transactionMeta) {
|
|
1007
1349
|
transactionMeta.status = types_1.TransactionStatus.dropped;
|
|
1350
|
+
this.hub.emit('transaction-dropped', {
|
|
1351
|
+
transactionMeta,
|
|
1352
|
+
});
|
|
1008
1353
|
this.updateTransaction(transactionMeta, 'TransactionController#setTransactionStatusDropped - Transaction dropped');
|
|
1009
1354
|
}
|
|
1010
1355
|
/**
|
|
@@ -1053,6 +1398,39 @@ class TransactionController extends base_controller_1.BaseController {
|
|
|
1053
1398
|
return (currentNetworkIsEIP1559Compatible && currentAccountIsEIP1559Compatible);
|
|
1054
1399
|
});
|
|
1055
1400
|
}
|
|
1401
|
+
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
|
+
});
|
|
1406
|
+
this.pendingTransactionTracker.hub.on('transaction-dropped', this.setTransactionStatusDropped.bind(this));
|
|
1407
|
+
this.pendingTransactionTracker.hub.on('transaction-failed', this.failTransaction.bind(this));
|
|
1408
|
+
this.pendingTransactionTracker.hub.on('transaction-updated', this.updateTransaction.bind(this));
|
|
1409
|
+
}
|
|
1410
|
+
signTransaction(transactionMeta) {
|
|
1411
|
+
var _a;
|
|
1412
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
1413
|
+
const { txParams } = transactionMeta;
|
|
1414
|
+
const unsignedEthTx = this.prepareUnsignedEthTx(txParams);
|
|
1415
|
+
this.inProcessOfSigning.add(transactionMeta.id);
|
|
1416
|
+
const signedTx = yield ((_a = this.sign) === null || _a === void 0 ? void 0 : _a.call(this, unsignedEthTx, txParams.from, ...this.getAdditionalSignArguments(transactionMeta)));
|
|
1417
|
+
if (!signedTx) {
|
|
1418
|
+
(0, logger_1.projectLogger)('Skipping signed status as no signed transaction');
|
|
1419
|
+
return undefined;
|
|
1420
|
+
}
|
|
1421
|
+
if (!this.afterSign(transactionMeta, signedTx)) {
|
|
1422
|
+
(0, logger_1.projectLogger)('Skipping signed status based on hook');
|
|
1423
|
+
return undefined;
|
|
1424
|
+
}
|
|
1425
|
+
yield this.updateTransactionMetaRSV(transactionMeta, signedTx);
|
|
1426
|
+
transactionMeta.status = types_1.TransactionStatus.signed;
|
|
1427
|
+
this.updateTransaction(transactionMeta, 'TransactionController#approveTransaction - Transaction signed');
|
|
1428
|
+
const rawTx = (0, ethereumjs_util_1.bufferToHex)(signedTx.serialize());
|
|
1429
|
+
transactionMeta.rawTx = rawTx;
|
|
1430
|
+
this.updateTransaction(transactionMeta, 'TransactionController#approveTransaction - RawTransaction added');
|
|
1431
|
+
return rawTx;
|
|
1432
|
+
});
|
|
1433
|
+
}
|
|
1056
1434
|
}
|
|
1057
1435
|
exports.TransactionController = TransactionController;
|
|
1058
1436
|
exports.default = TransactionController;
|