@metamask/transaction-controller 15.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 +48 -1
- package/dist/TransactionController.d.ts +159 -20
- package/dist/TransactionController.d.ts.map +1 -1
- package/dist/TransactionController.js +489 -151
- 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/{EtherscanRemoteTransactionSource.d.ts → helpers/EtherscanRemoteTransactionSource.d.ts} +1 -1
- package/dist/helpers/EtherscanRemoteTransactionSource.d.ts.map +1 -0
- package/dist/{EtherscanRemoteTransactionSource.js → helpers/EtherscanRemoteTransactionSource.js} +4 -4
- package/dist/helpers/EtherscanRemoteTransactionSource.js.map +1 -0
- package/dist/{IncomingTransactionHelper.d.ts → helpers/IncomingTransactionHelper.d.ts} +1 -1
- package/dist/helpers/IncomingTransactionHelper.d.ts.map +1 -0
- package/dist/{IncomingTransactionHelper.js → helpers/IncomingTransactionHelper.js} +1 -1
- package/dist/helpers/IncomingTransactionHelper.js.map +1 -0
- package/dist/helpers/PendingTransactionTracker.d.ts +38 -0
- package/dist/helpers/PendingTransactionTracker.d.ts.map +1 -0
- package/dist/helpers/PendingTransactionTracker.js +329 -0
- package/dist/helpers/PendingTransactionTracker.js.map +1 -0
- package/dist/index.d.ts +2 -2
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +1 -1
- package/dist/index.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 +290 -8
- package/dist/types.d.ts.map +1 -1
- package/dist/types.js +37 -1
- package/dist/types.js.map +1 -1
- package/dist/utils/etherscan.d.ts.map +1 -0
- package/dist/{etherscan.js → utils/etherscan.js} +3 -4
- package/dist/utils/etherscan.js.map +1 -0
- package/dist/{external-transactions.d.ts → utils/external-transactions.d.ts} +1 -1
- package/dist/utils/external-transactions.d.ts.map +1 -0
- package/dist/{external-transactions.js → utils/external-transactions.js} +1 -1
- package/dist/utils/external-transactions.js.map +1 -0
- package/dist/utils/gas-fees.d.ts +31 -0
- package/dist/utils/gas-fees.d.ts.map +1 -0
- package/dist/utils/gas-fees.js +212 -0
- package/dist/utils/gas-fees.js.map +1 -0
- package/dist/utils/gas.d.ts +27 -0
- package/dist/utils/gas.d.ts.map +1 -0
- package/dist/utils/gas.js +134 -0
- package/dist/utils/gas.js.map +1 -0
- package/dist/{history.d.ts → utils/history.d.ts} +1 -1
- package/dist/utils/history.d.ts.map +1 -0
- package/dist/utils/history.js.map +1 -0
- 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/{transaction-type.d.ts → utils/transaction-type.d.ts} +1 -1
- package/dist/utils/transaction-type.d.ts.map +1 -0
- package/dist/{transaction-type.js → utils/transaction-type.js} +1 -1
- package/dist/utils/transaction-type.js.map +1 -0
- package/dist/{utils.d.ts → utils/utils.d.ts} +13 -3
- package/dist/utils/utils.d.ts.map +1 -0
- package/dist/{utils.js → utils/utils.js} +21 -5
- package/dist/utils/utils.js.map +1 -0
- package/dist/{validation.d.ts → utils/validation.d.ts} +1 -1
- package/dist/utils/validation.d.ts.map +1 -0
- package/dist/{validation.js → utils/validation.js} +90 -7
- package/dist/utils/validation.js.map +1 -0
- package/package.json +12 -9
- package/dist/EtherscanRemoteTransactionSource.d.ts.map +0 -1
- package/dist/EtherscanRemoteTransactionSource.js.map +0 -1
- package/dist/IncomingTransactionHelper.d.ts.map +0 -1
- package/dist/IncomingTransactionHelper.js.map +0 -1
- package/dist/PendingTransactionTracker.d.ts +0 -18
- package/dist/PendingTransactionTracker.d.ts.map +0 -1
- package/dist/PendingTransactionTracker.js +0 -144
- package/dist/PendingTransactionTracker.js.map +0 -1
- package/dist/etherscan.d.ts.map +0 -1
- package/dist/etherscan.js.map +0 -1
- package/dist/external-transactions.d.ts.map +0 -1
- package/dist/external-transactions.js.map +0 -1
- package/dist/history.d.ts.map +0 -1
- package/dist/history.js.map +0 -1
- package/dist/transaction-type.d.ts.map +0 -1
- package/dist/transaction-type.js.map +0 -1
- package/dist/utils.d.ts.map +0 -1
- package/dist/utils.js.map +0 -1
- package/dist/validation.d.ts.map +0 -1
- package/dist/validation.js.map +0 -1
- /package/dist/{etherscan.d.ts → utils/etherscan.d.ts} +0 -0
- /package/dist/{history.js → utils/history.js} +0 -0
|
@@ -26,16 +26,19 @@ const events_1 = require("events");
|
|
|
26
26
|
const lodash_1 = require("lodash");
|
|
27
27
|
const nonce_tracker_1 = __importDefault(require("nonce-tracker"));
|
|
28
28
|
const uuid_1 = require("uuid");
|
|
29
|
-
const EtherscanRemoteTransactionSource_1 = require("./EtherscanRemoteTransactionSource");
|
|
30
|
-
const
|
|
31
|
-
const
|
|
32
|
-
const IncomingTransactionHelper_1 = require("./IncomingTransactionHelper");
|
|
29
|
+
const EtherscanRemoteTransactionSource_1 = require("./helpers/EtherscanRemoteTransactionSource");
|
|
30
|
+
const IncomingTransactionHelper_1 = require("./helpers/IncomingTransactionHelper");
|
|
31
|
+
const PendingTransactionTracker_1 = require("./helpers/PendingTransactionTracker");
|
|
33
32
|
const logger_1 = require("./logger");
|
|
34
|
-
const PendingTransactionTracker_1 = require("./PendingTransactionTracker");
|
|
35
|
-
const transaction_type_1 = require("./transaction-type");
|
|
36
33
|
const types_1 = require("./types");
|
|
37
|
-
const
|
|
38
|
-
const
|
|
34
|
+
const external_transactions_1 = require("./utils/external-transactions");
|
|
35
|
+
const gas_1 = require("./utils/gas");
|
|
36
|
+
const gas_fees_1 = require("./utils/gas-fees");
|
|
37
|
+
const history_1 = require("./utils/history");
|
|
38
|
+
const swaps_1 = require("./utils/swaps");
|
|
39
|
+
const transaction_type_1 = require("./utils/transaction-type");
|
|
40
|
+
const utils_1 = require("./utils/utils");
|
|
41
|
+
const validation_1 = require("./utils/validation");
|
|
39
42
|
exports.HARDFORK = common_1.Hardfork.London;
|
|
40
43
|
/**
|
|
41
44
|
* Multiplier used to determine a transaction's increased gas fee during cancellation
|
|
@@ -60,8 +63,11 @@ class TransactionController extends base_controller_1.BaseController {
|
|
|
60
63
|
* @param options.blockTracker - The block tracker used to poll for new blocks data.
|
|
61
64
|
* @param options.disableHistory - Whether to disable storing history in transaction metadata.
|
|
62
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.
|
|
63
68
|
* @param options.getCurrentAccountEIP1559Compatibility - Whether or not the account supports EIP-1559.
|
|
64
69
|
* @param options.getCurrentNetworkEIP1559Compatibility - Whether or not the network supports EIP-1559.
|
|
70
|
+
* @param options.getGasFeeEstimates - Callback to retrieve gas fee estimates.
|
|
65
71
|
* @param options.getNetworkState - Gets the state of the network controller.
|
|
66
72
|
* @param options.getPermittedAccounts - Get accounts that a given origin has permissions for.
|
|
67
73
|
* @param options.getSelectedAddress - Gets the address of the currently selected account.
|
|
@@ -72,13 +78,23 @@ class TransactionController extends base_controller_1.BaseController {
|
|
|
72
78
|
* @param options.incomingTransactions.updateTransactions - Whether to update local transactions using remote transaction data.
|
|
73
79
|
* @param options.messenger - The controller messenger.
|
|
74
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.
|
|
75
83
|
* @param options.provider - The provider used to create the underlying EthQuery instance.
|
|
76
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.
|
|
77
91
|
* @param config - Initial options used to configure this controller.
|
|
78
92
|
* @param state - Initial state to set on this controller.
|
|
79
93
|
*/
|
|
80
|
-
constructor({ blockTracker, disableHistory, disableSendFlowHistory, getCurrentAccountEIP1559Compatibility, getCurrentNetworkEIP1559Compatibility, 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;
|
|
81
96
|
super(config, state);
|
|
97
|
+
this.inProcessOfSigning = new Set();
|
|
82
98
|
this.mutex = new async_mutex_1.Mutex();
|
|
83
99
|
/**
|
|
84
100
|
* EventEmitter instance used to listen to specific transactional events
|
|
@@ -100,18 +116,30 @@ class TransactionController extends base_controller_1.BaseController {
|
|
|
100
116
|
this.provider = provider;
|
|
101
117
|
this.messagingSystem = messenger;
|
|
102
118
|
this.getNetworkState = getNetworkState;
|
|
103
|
-
// @ts-expect-error TODO: Provider type alignment
|
|
104
119
|
this.ethQuery = new eth_query_1.default(provider);
|
|
105
120
|
this.isSendFlowHistoryDisabled = disableSendFlowHistory !== null && disableSendFlowHistory !== void 0 ? disableSendFlowHistory : false;
|
|
106
121
|
this.isHistoryDisabled = disableHistory !== null && disableHistory !== void 0 ? disableHistory : false;
|
|
122
|
+
this.isSwapsDisabled = disableSwaps !== null && disableSwaps !== void 0 ? disableSwaps : false;
|
|
107
123
|
this.registry = new eth_method_registry_1.default({ provider });
|
|
124
|
+
this.getSavedGasFees = getSavedGasFees !== null && getSavedGasFees !== void 0 ? getSavedGasFees : ((_chainId) => undefined);
|
|
108
125
|
this.getCurrentAccountEIP1559Compatibility =
|
|
109
126
|
getCurrentAccountEIP1559Compatibility;
|
|
110
127
|
this.getCurrentNetworkEIP1559Compatibility =
|
|
111
128
|
getCurrentNetworkEIP1559Compatibility;
|
|
129
|
+
this.getGasFeeEstimates =
|
|
130
|
+
getGasFeeEstimates || (() => Promise.resolve({}));
|
|
112
131
|
this.getPermittedAccounts = getPermittedAccounts;
|
|
113
132
|
this.getSelectedAddress = getSelectedAddress;
|
|
114
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 : (() => []);
|
|
115
143
|
this.nonceTracker = new nonce_tracker_1.default({
|
|
116
144
|
provider,
|
|
117
145
|
blockTracker,
|
|
@@ -134,23 +162,35 @@ class TransactionController extends base_controller_1.BaseController {
|
|
|
134
162
|
this.incomingTransactionHelper.hub.on('transactions', this.onIncomingTransactions.bind(this));
|
|
135
163
|
this.incomingTransactionHelper.hub.on('updatedLastFetchedBlockNumbers', this.onUpdatedLastFetchedBlockNumbers.bind(this));
|
|
136
164
|
this.pendingTransactionTracker = new PendingTransactionTracker_1.PendingTransactionTracker({
|
|
165
|
+
approveTransaction: this.approveTransaction.bind(this),
|
|
137
166
|
blockTracker,
|
|
138
|
-
failTransaction: this.failTransaction.bind(this),
|
|
139
167
|
getChainId: this.getChainId.bind(this),
|
|
140
168
|
getEthQuery: () => this.ethQuery,
|
|
141
169
|
getTransactions: () => this.state.transactions,
|
|
170
|
+
isResubmitEnabled: pendingTransactions.isResubmitEnabled,
|
|
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
|
+
},
|
|
142
178
|
});
|
|
143
|
-
this.
|
|
144
|
-
this.pendingTransactionTracker.hub.on('transaction-confirmed', (transactionMeta) => this.hub.emit(`${transactionMeta.id}:confirmed`, transactionMeta));
|
|
179
|
+
this.addPendingTransactionTrackerListeners();
|
|
145
180
|
onNetworkStateChange(() => {
|
|
146
|
-
// @ts-expect-error TODO: Provider type alignment
|
|
147
181
|
this.ethQuery = new eth_query_1.default(this.provider);
|
|
148
182
|
this.registry = new eth_method_registry_1.default({ provider: this.provider });
|
|
183
|
+
this.onBootCleanup();
|
|
149
184
|
});
|
|
150
|
-
this.
|
|
185
|
+
this.onBootCleanup();
|
|
151
186
|
}
|
|
152
|
-
failTransaction(transactionMeta, error) {
|
|
153
|
-
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
|
+
});
|
|
154
194
|
this.updateTransaction(newTransactionMeta, 'TransactionController#failTransaction - Add error message and set status to failed');
|
|
155
195
|
this.hub.emit(`${transactionMeta.id}:finished`, newTransactionMeta);
|
|
156
196
|
}
|
|
@@ -202,12 +242,14 @@ class TransactionController extends base_controller_1.BaseController {
|
|
|
202
242
|
* @param opts.securityAlertResponse - Response from security validator.
|
|
203
243
|
* @param opts.sendFlowHistory - The sendFlowHistory entries to add.
|
|
204
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.
|
|
205
248
|
* @returns Object containing a promise resolving to the transaction hash if approved.
|
|
206
249
|
*/
|
|
207
|
-
addTransaction(txParams, { actionId, deviceConfirmedOn, method, origin, requireApproval, securityAlertResponse, sendFlowHistory, type, } = {}) {
|
|
250
|
+
addTransaction(txParams, { actionId, deviceConfirmedOn, method, origin, requireApproval, securityAlertResponse, sendFlowHistory, swaps = {}, type, } = {}) {
|
|
208
251
|
return __awaiter(this, void 0, void 0, function* () {
|
|
209
252
|
const chainId = this.getChainId();
|
|
210
|
-
const { transactions } = this.state;
|
|
211
253
|
txParams = (0, utils_1.normalizeTxParams)(txParams);
|
|
212
254
|
const isEIP1559Compatible = yield this.getEIP1559Compatibility();
|
|
213
255
|
(0, validation_1.validateTxParams)(txParams, isEIP1559Compatible);
|
|
@@ -234,16 +276,7 @@ class TransactionController extends base_controller_1.BaseController {
|
|
|
234
276
|
verifiedOnBlockchain: false,
|
|
235
277
|
type: transactionType,
|
|
236
278
|
};
|
|
237
|
-
|
|
238
|
-
const { gas, estimateGasError } = yield this.estimateGas(txParams);
|
|
239
|
-
txParams.gas = gas;
|
|
240
|
-
txParams.estimateGasError = estimateGasError;
|
|
241
|
-
transactionMeta.originalGasEstimate = gas;
|
|
242
|
-
}
|
|
243
|
-
catch (error) {
|
|
244
|
-
this.failTransaction(transactionMeta, error);
|
|
245
|
-
return Promise.reject(error);
|
|
246
|
-
}
|
|
279
|
+
yield this.updateGasProperties(transactionMeta);
|
|
247
280
|
// Checks if a transaction already exists with a given actionId
|
|
248
281
|
if (!existingTransactionMeta) {
|
|
249
282
|
// Set security provider response
|
|
@@ -258,16 +291,19 @@ class TransactionController extends base_controller_1.BaseController {
|
|
|
258
291
|
if (!this.isHistoryDisabled) {
|
|
259
292
|
(0, history_1.addInitialHistorySnapshot)(transactionMeta);
|
|
260
293
|
}
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
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),
|
|
264
298
|
});
|
|
299
|
+
this.addMetadata(transactionMeta);
|
|
265
300
|
this.hub.emit(`unapprovedTransaction`, transactionMeta);
|
|
266
301
|
}
|
|
267
302
|
return {
|
|
268
303
|
result: this.processApproval(transactionMeta, {
|
|
269
304
|
isExisting: Boolean(existingTransactionMeta),
|
|
270
305
|
requireApproval,
|
|
306
|
+
actionId,
|
|
271
307
|
}),
|
|
272
308
|
transactionMeta,
|
|
273
309
|
};
|
|
@@ -284,22 +320,6 @@ class TransactionController extends base_controller_1.BaseController {
|
|
|
284
320
|
yield this.incomingTransactionHelper.update();
|
|
285
321
|
});
|
|
286
322
|
}
|
|
287
|
-
/**
|
|
288
|
-
* Creates approvals for all unapproved transactions persisted.
|
|
289
|
-
*/
|
|
290
|
-
initApprovals() {
|
|
291
|
-
const chainId = this.getChainId();
|
|
292
|
-
const unapprovedTxs = this.state.transactions.filter((transaction) => transaction.status === types_1.TransactionStatus.unapproved &&
|
|
293
|
-
transaction.chainId === chainId);
|
|
294
|
-
for (const txMeta of unapprovedTxs) {
|
|
295
|
-
this.processApproval(txMeta, {
|
|
296
|
-
shouldShowRequest: false,
|
|
297
|
-
}).catch((error) => {
|
|
298
|
-
/* istanbul ignore next */
|
|
299
|
-
console.error('Error during persisted transaction approval', error);
|
|
300
|
-
});
|
|
301
|
-
}
|
|
302
|
-
}
|
|
303
323
|
/**
|
|
304
324
|
* Attempts to cancel a transaction based on its ID by setting its status to "rejected"
|
|
305
325
|
* and emitting a `<tx.id>:finished` hub event.
|
|
@@ -307,15 +327,20 @@ class TransactionController extends base_controller_1.BaseController {
|
|
|
307
327
|
* @param transactionId - The ID of the transaction to cancel.
|
|
308
328
|
* @param gasValues - The gas values to use for the cancellation transaction.
|
|
309
329
|
* @param options - The options for the cancellation transaction.
|
|
330
|
+
* @param options.actionId - Unique ID to prevent duplicate requests.
|
|
310
331
|
* @param options.estimatedBaseFee - The estimated base fee of the transaction.
|
|
311
332
|
*/
|
|
312
|
-
stopTransaction(transactionId, gasValues, { estimatedBaseFee } = {}) {
|
|
333
|
+
stopTransaction(transactionId, gasValues, { estimatedBaseFee, actionId, } = {}) {
|
|
313
334
|
var _a, _b;
|
|
314
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
|
+
}
|
|
315
340
|
if (gasValues) {
|
|
316
341
|
(0, utils_1.validateGasValues)(gasValues);
|
|
317
342
|
}
|
|
318
|
-
const transactionMeta = this.
|
|
343
|
+
const transactionMeta = this.getTransaction(transactionId);
|
|
319
344
|
if (!transactionMeta) {
|
|
320
345
|
return;
|
|
321
346
|
}
|
|
@@ -342,13 +367,13 @@ class TransactionController extends base_controller_1.BaseController {
|
|
|
342
367
|
const newMaxPriorityFeePerGas = (maxPriorityFeePerGasValues &&
|
|
343
368
|
(0, utils_1.validateMinimumIncrease)(maxPriorityFeePerGasValues, minMaxPriorityFeePerGas)) ||
|
|
344
369
|
(existingMaxPriorityFeePerGas && minMaxPriorityFeePerGas);
|
|
345
|
-
const
|
|
370
|
+
const newTxParams = newMaxFeePerGas && newMaxPriorityFeePerGas
|
|
346
371
|
? {
|
|
347
372
|
from: transactionMeta.txParams.from,
|
|
348
373
|
gasLimit: transactionMeta.txParams.gas,
|
|
349
374
|
maxFeePerGas: newMaxFeePerGas,
|
|
350
375
|
maxPriorityFeePerGas: newMaxPriorityFeePerGas,
|
|
351
|
-
type: 2,
|
|
376
|
+
type: '2',
|
|
352
377
|
nonce: transactionMeta.txParams.nonce,
|
|
353
378
|
to: transactionMeta.txParams.from,
|
|
354
379
|
value: '0x0',
|
|
@@ -361,14 +386,33 @@ class TransactionController extends base_controller_1.BaseController {
|
|
|
361
386
|
to: transactionMeta.txParams.from,
|
|
362
387
|
value: '0x0',
|
|
363
388
|
};
|
|
364
|
-
const unsignedEthTx = this.prepareUnsignedEthTx(
|
|
389
|
+
const unsignedEthTx = this.prepareUnsignedEthTx(newTxParams);
|
|
365
390
|
const signedTx = yield this.sign(unsignedEthTx, transactionMeta.txParams.from);
|
|
366
|
-
yield this.updateTransactionMetaRSV(transactionMeta, signedTx);
|
|
367
391
|
const rawTx = (0, ethereumjs_util_1.bufferToHex)(signedTx.serialize());
|
|
368
|
-
yield
|
|
369
|
-
|
|
370
|
-
|
|
371
|
-
|
|
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);
|
|
372
416
|
});
|
|
373
417
|
}
|
|
374
418
|
/**
|
|
@@ -399,7 +443,6 @@ class TransactionController extends base_controller_1.BaseController {
|
|
|
399
443
|
if (!this.sign) {
|
|
400
444
|
throw new Error('No sign method defined.');
|
|
401
445
|
}
|
|
402
|
-
const { transactions } = this.state;
|
|
403
446
|
// gasPrice (legacy non EIP1559)
|
|
404
447
|
const minGasPrice = (0, utils_1.getIncreasedPriceFromExisting)(transactionMeta.txParams.gasPrice, exports.SPEED_UP_RATE);
|
|
405
448
|
const gasPriceFromValues = (0, utils_1.isGasPriceValue)(gasValues) && gasValues.gasPrice;
|
|
@@ -421,18 +464,26 @@ class TransactionController extends base_controller_1.BaseController {
|
|
|
421
464
|
(0, utils_1.validateMinimumIncrease)(maxPriorityFeePerGasValues, minMaxPriorityFeePerGas)) ||
|
|
422
465
|
(existingMaxPriorityFeePerGas && minMaxPriorityFeePerGas);
|
|
423
466
|
const txParams = newMaxFeePerGas && newMaxPriorityFeePerGas
|
|
424
|
-
? 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 });
|
|
425
468
|
const unsignedEthTx = this.prepareUnsignedEthTx(txParams);
|
|
426
469
|
const signedTx = yield this.sign(unsignedEthTx, transactionMeta.txParams.from);
|
|
427
470
|
yield this.updateTransactionMetaRSV(transactionMeta, signedTx);
|
|
428
471
|
const rawTx = (0, ethereumjs_util_1.bufferToHex)(signedTx.serialize());
|
|
429
472
|
const hash = yield (0, controller_utils_1.query)(this.ethQuery, 'sendRawTransaction', [rawTx]);
|
|
430
473
|
const baseTransactionMeta = Object.assign(Object.assign({}, transactionMeta), { estimatedBaseFee, id: (0, uuid_1.v1)(), time: Date.now(), hash,
|
|
431
|
-
actionId, originalGasEstimate: transactionMeta.txParams.gas, type: types_1.TransactionType.retry });
|
|
474
|
+
actionId, originalGasEstimate: transactionMeta.txParams.gas, type: types_1.TransactionType.retry, originalType: transactionMeta.type });
|
|
432
475
|
const newTransactionMeta = newMaxFeePerGas && newMaxPriorityFeePerGas
|
|
433
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 }) });
|
|
434
|
-
|
|
435
|
-
|
|
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
|
+
});
|
|
436
487
|
this.hub.emit(`${transactionMeta.id}:speedup`, newTransactionMeta);
|
|
437
488
|
});
|
|
438
489
|
}
|
|
@@ -444,67 +495,24 @@ class TransactionController extends base_controller_1.BaseController {
|
|
|
444
495
|
*/
|
|
445
496
|
estimateGas(transaction) {
|
|
446
497
|
return __awaiter(this, void 0, void 0, function* () {
|
|
447
|
-
const
|
|
448
|
-
|
|
449
|
-
|
|
450
|
-
|
|
451
|
-
|
|
452
|
-
|
|
453
|
-
|
|
454
|
-
|
|
455
|
-
|
|
456
|
-
|
|
457
|
-
|
|
458
|
-
|
|
459
|
-
|
|
460
|
-
|
|
461
|
-
|
|
462
|
-
|
|
463
|
-
|
|
464
|
-
|
|
465
|
-
const code = to ? yield (0, controller_utils_1.query)(this.ethQuery, 'getCode', [to]) : undefined;
|
|
466
|
-
/* istanbul ignore next */
|
|
467
|
-
if (!isCustomNetwork &&
|
|
468
|
-
(!to || (to && !data && (!code || code === '0x')))) {
|
|
469
|
-
return { gas: '0x5208', gasPrice };
|
|
470
|
-
}
|
|
471
|
-
// if data, should be hex string format
|
|
472
|
-
estimatedTransaction.data = !data
|
|
473
|
-
? data
|
|
474
|
-
: /* istanbul ignore next */ (0, ethereumjs_util_1.addHexPrefix)(data);
|
|
475
|
-
// 3. If this is a contract address, safely estimate gas using RPC
|
|
476
|
-
estimatedTransaction.value =
|
|
477
|
-
typeof value === 'undefined' ? '0x0' : /* istanbul ignore next */ value;
|
|
478
|
-
const gasLimitBN = (0, controller_utils_1.hexToBN)(gasLimit);
|
|
479
|
-
estimatedTransaction.gas = (0, controller_utils_1.BNToHex)((0, controller_utils_1.fractionBN)(gasLimitBN, 19, 20));
|
|
480
|
-
let gasHex;
|
|
481
|
-
let estimateGasError;
|
|
482
|
-
try {
|
|
483
|
-
gasHex = yield (0, controller_utils_1.query)(this.ethQuery, 'estimateGas', [
|
|
484
|
-
estimatedTransaction,
|
|
485
|
-
]);
|
|
486
|
-
}
|
|
487
|
-
catch (error) {
|
|
488
|
-
estimateGasError = utils_1.ESTIMATE_GAS_ERROR;
|
|
489
|
-
}
|
|
490
|
-
// 4. Pad estimated gas without exceeding the most recent block gasLimit. If the network is a
|
|
491
|
-
// a custom network then return the eth_estimateGas value.
|
|
492
|
-
const gasBN = (0, controller_utils_1.hexToBN)(gasHex);
|
|
493
|
-
const maxGasBN = gasLimitBN.muln(0.9);
|
|
494
|
-
const paddedGasBN = gasBN.muln(1.5);
|
|
495
|
-
/* istanbul ignore next */
|
|
496
|
-
if (gasBN.gt(maxGasBN) || isCustomNetwork) {
|
|
497
|
-
return { gas: (0, ethereumjs_util_1.addHexPrefix)(gasHex), gasPrice, estimateGasError };
|
|
498
|
-
}
|
|
499
|
-
/* istanbul ignore next */
|
|
500
|
-
if (paddedGasBN.lt(maxGasBN)) {
|
|
501
|
-
return {
|
|
502
|
-
gas: (0, ethereumjs_util_1.addHexPrefix)((0, controller_utils_1.BNToHex)(paddedGasBN)),
|
|
503
|
-
gasPrice,
|
|
504
|
-
estimateGasError,
|
|
505
|
-
};
|
|
506
|
-
}
|
|
507
|
-
return { gas: (0, ethereumjs_util_1.addHexPrefix)((0, controller_utils_1.BNToHex)(maxGasBN)), gasPrice, estimateGasError };
|
|
498
|
+
const { estimatedGas, simulationFails } = yield (0, gas_1.estimateGas)(transaction, this.ethQuery);
|
|
499
|
+
return { gas: estimatedGas, simulationFails };
|
|
500
|
+
});
|
|
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
|
+
};
|
|
508
516
|
});
|
|
509
517
|
}
|
|
510
518
|
/**
|
|
@@ -524,6 +532,23 @@ class TransactionController extends base_controller_1.BaseController {
|
|
|
524
532
|
transactions[index] = transactionMeta;
|
|
525
533
|
this.update({ transactions: this.trimTransactionsForState(transactions) });
|
|
526
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
|
+
}
|
|
527
552
|
/**
|
|
528
553
|
* Removes all transactions from state, optionally based on the current network.
|
|
529
554
|
*
|
|
@@ -581,6 +606,26 @@ class TransactionController extends base_controller_1.BaseController {
|
|
|
581
606
|
this.markNonceDuplicatesDropped(transactionId);
|
|
582
607
|
// Update external provided transaction with updated gas values and confirmed status.
|
|
583
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
|
+
});
|
|
584
629
|
}
|
|
585
630
|
catch (error) {
|
|
586
631
|
console.error(error);
|
|
@@ -662,7 +707,250 @@ class TransactionController extends base_controller_1.BaseController {
|
|
|
662
707
|
this.updateTransaction(updatedMeta, 'TransactionController:updateTransactionGasFees - gas values updated');
|
|
663
708
|
return this.getTransaction(transactionId);
|
|
664
709
|
}
|
|
665
|
-
|
|
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
|
+
}
|
|
740
|
+
/**
|
|
741
|
+
* Gets the next nonce according to the nonce-tracker.
|
|
742
|
+
* Ensure `releaseLock` is called once processing of the `nonce` value is complete.
|
|
743
|
+
*
|
|
744
|
+
* @param address - The hex string address for the transaction.
|
|
745
|
+
* @returns object with the `nextNonce` `nonceDetails`, and the releaseLock.
|
|
746
|
+
*/
|
|
747
|
+
getNonceLock(address) {
|
|
748
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
749
|
+
return this.nonceTracker.getNonceLock(address);
|
|
750
|
+
});
|
|
751
|
+
}
|
|
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, }) {
|
|
666
954
|
return __awaiter(this, void 0, void 0, function* () {
|
|
667
955
|
const transactionId = transactionMeta.id;
|
|
668
956
|
let resultCallbacks;
|
|
@@ -681,17 +969,22 @@ class TransactionController extends base_controller_1.BaseController {
|
|
|
681
969
|
const { isCompleted: isTxCompleted } = this.isTransactionCompleted(transactionId);
|
|
682
970
|
if (!isTxCompleted) {
|
|
683
971
|
yield this.approveTransaction(transactionId);
|
|
972
|
+
const updatedTransactionMeta = this.getTransaction(transactionId);
|
|
973
|
+
this.hub.emit('transaction-approved', {
|
|
974
|
+
transactionMeta: updatedTransactionMeta,
|
|
975
|
+
actionId,
|
|
976
|
+
});
|
|
684
977
|
}
|
|
685
978
|
}
|
|
686
979
|
catch (error) {
|
|
687
980
|
const { isCompleted: isTxCompleted } = this.isTransactionCompleted(transactionId);
|
|
688
981
|
if (!isTxCompleted) {
|
|
689
|
-
if (error.code === rpc_errors_1.errorCodes.provider.userRejectedRequest) {
|
|
690
|
-
this.cancelTransaction(transactionId);
|
|
691
|
-
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.');
|
|
692
985
|
}
|
|
693
986
|
else {
|
|
694
|
-
this.failTransaction(meta, error);
|
|
987
|
+
this.failTransaction(meta, error, actionId);
|
|
695
988
|
}
|
|
696
989
|
}
|
|
697
990
|
}
|
|
@@ -701,10 +994,6 @@ class TransactionController extends base_controller_1.BaseController {
|
|
|
701
994
|
case types_1.TransactionStatus.failed:
|
|
702
995
|
resultCallbacks === null || resultCallbacks === void 0 ? void 0 : resultCallbacks.error(finalMeta.error);
|
|
703
996
|
throw rpc_errors_1.rpcErrors.internal(finalMeta.error.message);
|
|
704
|
-
case types_1.TransactionStatus.cancelled:
|
|
705
|
-
const cancelError = rpc_errors_1.rpcErrors.internal('User cancelled the transaction');
|
|
706
|
-
resultCallbacks === null || resultCallbacks === void 0 ? void 0 : resultCallbacks.error(cancelError);
|
|
707
|
-
throw cancelError;
|
|
708
997
|
case types_1.TransactionStatus.submitted:
|
|
709
998
|
resultCallbacks === null || resultCallbacks === void 0 ? void 0 : resultCallbacks.success();
|
|
710
999
|
return finalMeta.hash;
|
|
@@ -743,6 +1032,10 @@ class TransactionController extends base_controller_1.BaseController {
|
|
|
743
1032
|
this.failTransaction(transactionMeta, new Error('No chainId defined.'));
|
|
744
1033
|
return;
|
|
745
1034
|
}
|
|
1035
|
+
if (this.inProcessOfSigning.has(transactionId)) {
|
|
1036
|
+
(0, logger_1.projectLogger)('Skipping approval as signing in progress', transactionId);
|
|
1037
|
+
return;
|
|
1038
|
+
}
|
|
746
1039
|
const { approved: status } = types_1.TransactionStatus;
|
|
747
1040
|
let nonceToUse = nonce;
|
|
748
1041
|
// if a nonce already exists on the transactionMeta it means this is a speedup or cancel transaction
|
|
@@ -755,27 +1048,31 @@ class TransactionController extends base_controller_1.BaseController {
|
|
|
755
1048
|
transactionMeta.txParams.nonce = nonceToUse;
|
|
756
1049
|
transactionMeta.txParams.chainId = chainId;
|
|
757
1050
|
const baseTxParams = Object.assign(Object.assign({}, transactionMeta.txParams), { gasLimit: transactionMeta.txParams.gas });
|
|
1051
|
+
this.updateTransaction(transactionMeta, 'TransactionController#approveTransaction - Transaction approved');
|
|
758
1052
|
const isEIP1559 = (0, utils_1.isEIP1559Transaction)(transactionMeta.txParams);
|
|
759
1053
|
const txParams = isEIP1559
|
|
760
1054
|
? Object.assign(Object.assign({}, baseTxParams), { maxFeePerGas: transactionMeta.txParams.maxFeePerGas, maxPriorityFeePerGas: transactionMeta.txParams.maxPriorityFeePerGas, estimatedBaseFee: transactionMeta.txParams.estimatedBaseFee,
|
|
761
1055
|
// specify type 2 if maxFeePerGas and maxPriorityFeePerGas are set
|
|
762
|
-
type: 2 }) : baseTxParams;
|
|
1056
|
+
type: '2' }) : baseTxParams;
|
|
763
1057
|
// delete gasPrice if maxFeePerGas and maxPriorityFeePerGas are set
|
|
764
1058
|
if (isEIP1559) {
|
|
765
1059
|
delete txParams.gasPrice;
|
|
766
1060
|
}
|
|
767
|
-
const
|
|
768
|
-
|
|
769
|
-
|
|
770
|
-
|
|
771
|
-
|
|
772
|
-
|
|
773
|
-
|
|
774
|
-
|
|
775
|
-
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);
|
|
776
1070
|
transactionMeta.hash = hash;
|
|
777
1071
|
transactionMeta.status = types_1.TransactionStatus.submitted;
|
|
778
1072
|
transactionMeta.submittedTime = new Date().getTime();
|
|
1073
|
+
this.hub.emit('transaction-submitted', {
|
|
1074
|
+
transactionMeta,
|
|
1075
|
+
});
|
|
779
1076
|
this.updateTransaction(transactionMeta, 'TransactionController#approveTransaction - Transaction submitted');
|
|
780
1077
|
this.hub.emit(`${transactionMeta.id}:finished`, transactionMeta);
|
|
781
1078
|
}
|
|
@@ -783,6 +1080,7 @@ class TransactionController extends base_controller_1.BaseController {
|
|
|
783
1080
|
this.failTransaction(transactionMeta, error);
|
|
784
1081
|
}
|
|
785
1082
|
finally {
|
|
1083
|
+
this.inProcessOfSigning.delete(transactionId);
|
|
786
1084
|
// must set transaction to submitted/failed before releasing lock
|
|
787
1085
|
if (nonceLock) {
|
|
788
1086
|
nonceLock.releaseLock();
|
|
@@ -791,19 +1089,29 @@ class TransactionController extends base_controller_1.BaseController {
|
|
|
791
1089
|
}
|
|
792
1090
|
});
|
|
793
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
|
+
}
|
|
794
1097
|
/**
|
|
795
1098
|
* Cancels a transaction based on its ID by setting its status to "rejected"
|
|
796
1099
|
* and emitting a `<tx.id>:finished` hub event.
|
|
797
1100
|
*
|
|
798
1101
|
* @param transactionId - The ID of the transaction to cancel.
|
|
1102
|
+
* @param actionId - The actionId passed from UI
|
|
799
1103
|
*/
|
|
800
|
-
cancelTransaction(transactionId) {
|
|
1104
|
+
cancelTransaction(transactionId, actionId) {
|
|
801
1105
|
const transactionMeta = this.state.transactions.find(({ id }) => id === transactionId);
|
|
802
1106
|
if (!transactionMeta) {
|
|
803
1107
|
return;
|
|
804
1108
|
}
|
|
805
1109
|
transactionMeta.status = types_1.TransactionStatus.rejected;
|
|
806
1110
|
this.hub.emit(`${transactionMeta.id}:finished`, transactionMeta);
|
|
1111
|
+
this.hub.emit('transaction-rejected', {
|
|
1112
|
+
transactionMeta,
|
|
1113
|
+
actionId,
|
|
1114
|
+
});
|
|
807
1115
|
const transactions = this.state.transactions.filter(({ id }) => id !== transactionId);
|
|
808
1116
|
this.update({ transactions: this.trimTransactionsForState(transactions) });
|
|
809
1117
|
}
|
|
@@ -852,8 +1160,7 @@ class TransactionController extends base_controller_1.BaseController {
|
|
|
852
1160
|
isFinalState(status) {
|
|
853
1161
|
return (status === types_1.TransactionStatus.rejected ||
|
|
854
1162
|
status === types_1.TransactionStatus.confirmed ||
|
|
855
|
-
status === types_1.TransactionStatus.failed
|
|
856
|
-
status === types_1.TransactionStatus.cancelled);
|
|
1163
|
+
status === types_1.TransactionStatus.failed);
|
|
857
1164
|
}
|
|
858
1165
|
/**
|
|
859
1166
|
* Whether the transaction has at least completed all local processing.
|
|
@@ -863,7 +1170,6 @@ class TransactionController extends base_controller_1.BaseController {
|
|
|
863
1170
|
*/
|
|
864
1171
|
isLocalFinalState(status) {
|
|
865
1172
|
return [
|
|
866
|
-
types_1.TransactionStatus.cancelled,
|
|
867
1173
|
types_1.TransactionStatus.confirmed,
|
|
868
1174
|
types_1.TransactionStatus.failed,
|
|
869
1175
|
types_1.TransactionStatus.rejected,
|
|
@@ -950,10 +1256,6 @@ class TransactionController extends base_controller_1.BaseController {
|
|
|
950
1256
|
this.update({ lastFetchedBlockNumbers });
|
|
951
1257
|
this.hub.emit('incomingTransactionBlock', blockNumber);
|
|
952
1258
|
}
|
|
953
|
-
onPendingTransactionsUpdate(transactions) {
|
|
954
|
-
(0, logger_1.pendingTransactionsLogger)('Updated pending transactions');
|
|
955
|
-
this.update({ transactions: this.trimTransactionsForState(transactions) });
|
|
956
|
-
}
|
|
957
1259
|
generateDappSuggestedGasFees(txParams, origin) {
|
|
958
1260
|
if (!origin || origin === controller_utils_1.ORIGIN_METAMASK) {
|
|
959
1261
|
return undefined;
|
|
@@ -1045,6 +1347,9 @@ class TransactionController extends base_controller_1.BaseController {
|
|
|
1045
1347
|
*/
|
|
1046
1348
|
setTransactionStatusDropped(transactionMeta) {
|
|
1047
1349
|
transactionMeta.status = types_1.TransactionStatus.dropped;
|
|
1350
|
+
this.hub.emit('transaction-dropped', {
|
|
1351
|
+
transactionMeta,
|
|
1352
|
+
});
|
|
1048
1353
|
this.updateTransaction(transactionMeta, 'TransactionController#setTransactionStatusDropped - Transaction dropped');
|
|
1049
1354
|
}
|
|
1050
1355
|
/**
|
|
@@ -1093,6 +1398,39 @@ class TransactionController extends base_controller_1.BaseController {
|
|
|
1093
1398
|
return (currentNetworkIsEIP1559Compatible && currentAccountIsEIP1559Compatible);
|
|
1094
1399
|
});
|
|
1095
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
|
+
}
|
|
1096
1434
|
}
|
|
1097
1435
|
exports.TransactionController = TransactionController;
|
|
1098
1436
|
exports.default = TransactionController;
|