@metamask-previews/transaction-controller 21.0.0-preview.8cdddaf → 21.1.0-preview.cbe2fbb7

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (36) hide show
  1. package/CHANGELOG.md +107 -5
  2. package/dist/TransactionController.d.ts +85 -30
  3. package/dist/TransactionController.d.ts.map +1 -1
  4. package/dist/TransactionController.js +441 -108
  5. package/dist/TransactionController.js.map +1 -1
  6. package/dist/helpers/EtherscanRemoteTransactionSource.d.ts.map +1 -1
  7. package/dist/helpers/EtherscanRemoteTransactionSource.js +28 -8
  8. package/dist/helpers/EtherscanRemoteTransactionSource.js.map +1 -1
  9. package/dist/helpers/IncomingTransactionHelper.d.ts +3 -3
  10. package/dist/helpers/IncomingTransactionHelper.d.ts.map +1 -1
  11. package/dist/helpers/IncomingTransactionHelper.js +17 -14
  12. package/dist/helpers/IncomingTransactionHelper.js.map +1 -1
  13. package/dist/helpers/PendingTransactionTracker.d.ts +7 -7
  14. package/dist/helpers/PendingTransactionTracker.d.ts.map +1 -1
  15. package/dist/helpers/PendingTransactionTracker.js +26 -26
  16. package/dist/helpers/PendingTransactionTracker.js.map +1 -1
  17. package/dist/types.d.ts +8 -0
  18. package/dist/types.d.ts.map +1 -1
  19. package/dist/types.js.map +1 -1
  20. package/dist/utils/etherscan.d.ts +7 -0
  21. package/dist/utils/etherscan.d.ts.map +1 -1
  22. package/dist/utils/etherscan.js +16 -6
  23. package/dist/utils/etherscan.js.map +1 -1
  24. package/dist/utils/gas-fees.d.ts +4 -3
  25. package/dist/utils/gas-fees.d.ts.map +1 -1
  26. package/dist/utils/gas-fees.js +6 -2
  27. package/dist/utils/gas-fees.js.map +1 -1
  28. package/dist/utils/gas.d.ts +3 -2
  29. package/dist/utils/gas.d.ts.map +1 -1
  30. package/dist/utils/gas.js +4 -5
  31. package/dist/utils/gas.js.map +1 -1
  32. package/dist/utils/nonce.d.ts +3 -3
  33. package/dist/utils/nonce.d.ts.map +1 -1
  34. package/dist/utils/nonce.js +3 -3
  35. package/dist/utils/nonce.js.map +1 -1
  36. package/package.json +2 -1
@@ -8,9 +8,15 @@ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, ge
8
8
  step((generator = generator.apply(thisArg, _arguments || [])).next());
9
9
  });
10
10
  };
11
+ var __classPrivateFieldGet = (this && this.__classPrivateFieldGet) || function (receiver, state, kind, f) {
12
+ if (kind === "a" && !f) throw new TypeError("Private accessor was defined without a getter");
13
+ if (typeof state === "function" ? receiver !== state || !f : !state.has(receiver)) throw new TypeError("Cannot read private member from an object whose class did not declare it");
14
+ return kind === "m" ? f : kind === "a" ? f.call(receiver) : f ? f.value : state.get(receiver);
15
+ };
11
16
  var __importDefault = (this && this.__importDefault) || function (mod) {
12
17
  return (mod && mod.__esModule) ? mod : { "default": mod };
13
18
  };
19
+ var _TransactionController_instances, _TransactionController_acquireNonceLockForChainIdKey, _TransactionController_getEthQuery, _TransactionController_refreshEtherscanRemoteTransactionSources, _TransactionController_refreshTrackingMap, _TransactionController_initTrackingMap, _TransactionController_checkForPendingTransactionAndStartPolling, _TransactionController_stopTrackingByNetworkClientId, _TransactionController_stopAllTracking, _TransactionController_startTrackingByNetworkClientId;
14
20
  Object.defineProperty(exports, "__esModule", { value: true });
15
21
  exports.TransactionController = exports.SPEED_UP_RATE = exports.CANCEL_RATE = exports.HARDFORK = void 0;
16
22
  const common_1 = require("@ethereumjs/common");
@@ -18,6 +24,7 @@ const tx_1 = require("@ethereumjs/tx");
18
24
  const base_controller_1 = require("@metamask/base-controller");
19
25
  const controller_utils_1 = require("@metamask/controller-utils");
20
26
  const eth_query_1 = __importDefault(require("@metamask/eth-query"));
27
+ const network_controller_1 = require("@metamask/network-controller");
21
28
  const rpc_errors_1 = require("@metamask/rpc-errors");
22
29
  const async_mutex_1 = require("async-mutex");
23
30
  const eth_method_registry_1 = require("eth-method-registry");
@@ -75,31 +82,34 @@ class TransactionController extends base_controller_1.BaseControllerV1 {
75
82
  * @param options.getSavedGasFees - Gets the saved gas fee config.
76
83
  * @param options.getSelectedAddress - Gets the address of the currently selected account.
77
84
  * @param options.incomingTransactions - Configuration options for incoming transaction support.
78
- * @param options.incomingTransactions.includeTokenTransfers - Whether or not to include ERC20 token transfers.
79
- * @param options.incomingTransactions.isEnabled - Whether or not incoming transaction retrieval is enabled.
80
- * @param options.incomingTransactions.queryEntireHistory - Whether to initially query the entire transaction history or only recent blocks.
81
- * @param options.incomingTransactions.updateTransactions - Whether to update local transactions using remote transaction data.
82
85
  * @param options.messenger - The controller messenger.
83
86
  * @param options.onNetworkStateChange - Allows subscribing to network controller state changes.
84
87
  * @param options.pendingTransactions - Configuration options for pending transaction support.
85
- * @param options.pendingTransactions.isResubmitEnabled - Whether transaction publishing is automatically retried.
86
88
  * @param options.provider - The provider used to create the underlying EthQuery instance.
87
89
  * @param options.securityProviderRequest - A function for verifying a transaction, whether it is malicious or not.
88
90
  * @param options.speedUpMultiplier - Multiplier used to determine a transaction's increased gas fee during speed up.
91
+ * @param options.findNetworkClientIdByChainId - Finds a networkClientId with the given chainId from the NetworkController.
92
+ * @param options.getNetworkClientById - Gets the network client with the given id from the NetworkController.
89
93
  * @param options.hooks - The controller hooks.
90
94
  * @param options.hooks.afterSign - Additional logic to execute after signing a transaction. Return false to not change the status to signed.
91
95
  * @param options.hooks.beforeApproveOnInit - Additional logic to execute before starting an approval flow for a transaction during initialization. Return false to skip the transaction.
92
96
  * @param options.hooks.beforeCheckPendingTransaction - Additional logic to execute before checking pending transactions. Return false to prevent the broadcast of the transaction.
93
97
  * @param options.hooks.beforePublish - Additional logic to execute before publishing a transaction. Return false to prevent the broadcast of the transaction.
94
98
  * @param options.hooks.getAdditionalSignArguments - Returns additional arguments required to sign a transaction.
99
+ * @param options.hub - Use a different event emitter for the hub.
100
+ * @param options.getNetworkClientRegistry - Gets the network client registry.
101
+ * @param options.enableMultichain - Enable multichain support.
95
102
  * @param config - Initial options used to configure this controller.
96
103
  * @param state - Initial state to set on this controller.
97
104
  */
98
- constructor({ blockTracker, cancelMultiplier, disableHistory, disableSendFlowHistory, disableSwaps, getCurrentAccountEIP1559Compatibility, getCurrentNetworkEIP1559Compatibility, getExternalPendingTransactions, getGasFeeEstimates, getNetworkState, getPermittedAccounts, getSavedGasFees, getSelectedAddress, incomingTransactions = {}, messenger, onNetworkStateChange, pendingTransactions = {}, provider, securityProviderRequest, speedUpMultiplier, hooks = {}, }, config, state) {
105
+ constructor({ blockTracker, cancelMultiplier, disableHistory, disableSendFlowHistory, disableSwaps, getCurrentAccountEIP1559Compatibility, getCurrentNetworkEIP1559Compatibility, getExternalPendingTransactions, getGasFeeEstimates, getNetworkState, getPermittedAccounts, getSavedGasFees, getSelectedAddress, incomingTransactions = {}, messenger, onNetworkStateChange, pendingTransactions = {}, provider, securityProviderRequest, speedUpMultiplier, findNetworkClientIdByChainId, getNetworkClientById, getNetworkClientRegistry, hub, enableMultichain = false, hooks = {}, }, config, state) {
99
106
  var _a, _b, _c, _d, _e;
100
107
  super(config, state);
108
+ _TransactionController_instances.add(this);
101
109
  this.inProcessOfSigning = new Set();
102
110
  this.mutex = new async_mutex_1.Mutex();
111
+ this.nonceMutexesByChainId = new Map();
112
+ this.signAbortCallbacks = new Map();
103
113
  /**
104
114
  * EventEmitter instance used to listen to specific transactional events
105
115
  */
@@ -108,6 +118,50 @@ class TransactionController extends base_controller_1.BaseControllerV1 {
108
118
  * Name of this controller used during composition
109
119
  */
110
120
  this.name = 'TransactionController';
121
+ this.trackingMap = new Map();
122
+ this.etherscanRemoteTransactionSourcesMap = new Map();
123
+ _TransactionController_refreshEtherscanRemoteTransactionSources.set(this, (networkClients) => {
124
+ // this will be prettier when we have consolidated network clients with a single chainId:
125
+ // check if there are still other network clients using the same chainId
126
+ // if not remove the etherscanRemoteTransaction source from the map
127
+ const chainIdsInRegistry = new Set();
128
+ Object.values(networkClients).forEach((networkClient) => chainIdsInRegistry.add(networkClient.configuration.chainId));
129
+ const existingChainIds = Array.from(this.etherscanRemoteTransactionSourcesMap.keys());
130
+ const chainIdsToRemove = existingChainIds.filter((chainId) => !chainIdsInRegistry.has(chainId));
131
+ chainIdsToRemove.forEach((chainId) => {
132
+ this.etherscanRemoteTransactionSourcesMap.delete(chainId);
133
+ });
134
+ });
135
+ _TransactionController_refreshTrackingMap.set(this, (networkClients) => {
136
+ __classPrivateFieldGet(this, _TransactionController_refreshEtherscanRemoteTransactionSources, "f").call(this, networkClients);
137
+ const networkClientIds = Object.keys(networkClients);
138
+ const existingNetworkClientIds = Array.from(this.trackingMap.keys());
139
+ // Remove tracking for NetworkClientIds that no longer exist
140
+ const networkClientIdsToRemove = existingNetworkClientIds.filter((id) => !networkClientIds.includes(id));
141
+ networkClientIdsToRemove.forEach((id) => {
142
+ __classPrivateFieldGet(this, _TransactionController_instances, "m", _TransactionController_stopTrackingByNetworkClientId).call(this, id);
143
+ });
144
+ // Start tracking new NetworkClientIds from the registry
145
+ const networkClientIdsToAdd = networkClientIds.filter((id) => !existingNetworkClientIds.includes(id));
146
+ networkClientIdsToAdd.forEach((id) => {
147
+ __classPrivateFieldGet(this, _TransactionController_instances, "m", _TransactionController_startTrackingByNetworkClientId).call(this, id);
148
+ });
149
+ });
150
+ _TransactionController_initTrackingMap.set(this, () => {
151
+ const networkClients = this.getNetworkClientRegistry();
152
+ const networkClientIds = Object.keys(networkClients);
153
+ networkClientIds.map((id) => __classPrivateFieldGet(this, _TransactionController_instances, "m", _TransactionController_startTrackingByNetworkClientId).call(this, id));
154
+ this.hub.emit('tracking-map-init', networkClientIds);
155
+ });
156
+ _TransactionController_checkForPendingTransactionAndStartPolling.set(this, () => {
157
+ // PendingTransactionTracker reads state through its getTransactions hook
158
+ this.pendingTransactionTracker.startIfPendingTransactions();
159
+ if (this.enableMultichain) {
160
+ for (const [, trackingMap] of this.trackingMap) {
161
+ trackingMap.pendingTransactionTracker.startIfPendingTransactions();
162
+ }
163
+ }
164
+ });
111
165
  this.defaultConfig = {
112
166
  txHistoryLimit: 40,
113
167
  };
@@ -117,6 +171,11 @@ class TransactionController extends base_controller_1.BaseControllerV1 {
117
171
  lastFetchedBlockNumbers: {},
118
172
  };
119
173
  this.initialize();
174
+ this.hub = hub !== null && hub !== void 0 ? hub : this.hub;
175
+ this.enableMultichain = enableMultichain;
176
+ this.findNetworkClientIdByChainId = findNetworkClientIdByChainId;
177
+ this.getNetworkClientById = getNetworkClientById;
178
+ this.getNetworkClientRegistry = getNetworkClientRegistry;
120
179
  this.provider = provider;
121
180
  this.messagingSystem = messenger;
122
181
  this.getNetworkState = getNetworkState;
@@ -140,6 +199,8 @@ class TransactionController extends base_controller_1.BaseControllerV1 {
140
199
  this.securityProviderRequest = securityProviderRequest;
141
200
  this.cancelMultiplier = cancelMultiplier !== null && cancelMultiplier !== void 0 ? cancelMultiplier : exports.CANCEL_RATE;
142
201
  this.speedUpMultiplier = speedUpMultiplier !== null && speedUpMultiplier !== void 0 ? speedUpMultiplier : exports.SPEED_UP_RATE;
202
+ this.incomingTransactionOptions = incomingTransactions;
203
+ this.pendingTransactionOptions = pendingTransactions;
143
204
  this.afterSign = (_a = hooks === null || hooks === void 0 ? void 0 : hooks.afterSign) !== null && _a !== void 0 ? _a : (() => true);
144
205
  this.beforeApproveOnInit = (_b = hooks === null || hooks === void 0 ? void 0 : hooks.beforeApproveOnInit) !== null && _b !== void 0 ? _b : (() => true);
145
206
  this.beforeCheckPendingTransaction =
@@ -153,24 +214,24 @@ class TransactionController extends base_controller_1.BaseControllerV1 {
153
214
  // @ts-expect-error provider types misaligned: SafeEventEmitterProvider vs Record<string,string>
154
215
  provider,
155
216
  blockTracker,
156
- getPendingTransactions: this.getNonceTrackerPendingTransactions.bind(this),
217
+ getPendingTransactions: this.getNonceTrackerPendingTransactions.bind(this, undefined),
157
218
  getConfirmedTransactions: this.getNonceTrackerTransactions.bind(this, types_1.TransactionStatus.confirmed),
158
219
  });
220
+ const etherscanRemoteTransactionSource = new EtherscanRemoteTransactionSource_1.EtherscanRemoteTransactionSource({
221
+ includeTokenTransfers: incomingTransactions.includeTokenTransfers,
222
+ });
159
223
  this.incomingTransactionHelper = new IncomingTransactionHelper_1.IncomingTransactionHelper({
160
224
  blockTracker,
161
225
  getCurrentAccount: getSelectedAddress,
162
226
  getLastFetchedBlockNumbers: () => this.state.lastFetchedBlockNumbers,
163
- getNetworkState,
227
+ getChainId: this.getChainId.bind(this),
164
228
  isEnabled: incomingTransactions.isEnabled,
165
229
  queryEntireHistory: incomingTransactions.queryEntireHistory,
166
- remoteTransactionSource: new EtherscanRemoteTransactionSource_1.EtherscanRemoteTransactionSource({
167
- includeTokenTransfers: incomingTransactions.includeTokenTransfers,
168
- }),
230
+ remoteTransactionSource: etherscanRemoteTransactionSource,
169
231
  transactionLimit: this.config.txHistoryLimit,
170
232
  updateTransactions: incomingTransactions.updateTransactions,
171
233
  });
172
- this.incomingTransactionHelper.hub.on('transactions', this.onIncomingTransactions.bind(this));
173
- this.incomingTransactionHelper.hub.on('updatedLastFetchedBlockNumbers', this.onUpdatedLastFetchedBlockNumbers.bind(this));
234
+ this.addIncomingTransactionHelperListeners();
174
235
  this.pendingTransactionTracker = new PendingTransactionTracker_1.PendingTransactionTracker({
175
236
  approveTransaction: this.approveTransaction.bind(this),
176
237
  blockTracker,
@@ -178,12 +239,11 @@ class TransactionController extends base_controller_1.BaseControllerV1 {
178
239
  getEthQuery: () => this.ethQuery,
179
240
  getTransactions: () => this.state.transactions,
180
241
  isResubmitEnabled: pendingTransactions.isResubmitEnabled,
181
- nonceTracker: this.nonceTracker,
182
- onStateChange: (listener) => {
183
- this.subscribe(listener);
184
- onNetworkStateChange(listener);
185
- listener();
186
- },
242
+ getGlobalLock: () => __awaiter(this, void 0, void 0, function* () {
243
+ return __classPrivateFieldGet(this, _TransactionController_instances, "m", _TransactionController_acquireNonceLockForChainIdKey).call(this, {
244
+ chainId: this.getChainId(),
245
+ });
246
+ }),
187
247
  publishTransaction: this.publishTransaction.bind(this),
188
248
  hooks: {
189
249
  beforeCheckPendingTransaction: this.beforeCheckPendingTransaction.bind(this),
@@ -191,11 +251,32 @@ class TransactionController extends base_controller_1.BaseControllerV1 {
191
251
  },
192
252
  });
193
253
  this.addPendingTransactionTrackerListeners();
254
+ // when transactionsController state changes
255
+ // check for pending transactions and start polling if there are any
256
+ this.subscribe(__classPrivateFieldGet(this, _TransactionController_checkForPendingTransactionAndStartPolling, "f"));
257
+ // TODO once v2 is merged make sure this only runs when
258
+ // selectedNetworkClientId changes
194
259
  onNetworkStateChange(() => {
195
260
  (0, logger_1.projectLogger)('Detected network change', this.getChainId());
261
+ this.pendingTransactionTracker.startIfPendingTransactions();
196
262
  this.onBootCleanup();
197
263
  });
198
264
  this.onBootCleanup();
265
+ this.messagingSystem.subscribe('NetworkController:stateChange', (_, patches) => {
266
+ if (this.enableMultichain) {
267
+ const networkClients = this.getNetworkClientRegistry();
268
+ patches.forEach(({ op, path }) => {
269
+ if (op === 'remove' && path[0] === 'networkConfigurations') {
270
+ const networkClientId = path[1];
271
+ delete networkClients[networkClientId];
272
+ }
273
+ });
274
+ __classPrivateFieldGet(this, _TransactionController_refreshTrackingMap, "f").call(this, networkClients);
275
+ }
276
+ });
277
+ if (this.enableMultichain) {
278
+ __classPrivateFieldGet(this, _TransactionController_initTrackingMap, "f").call(this);
279
+ }
199
280
  }
200
281
  failTransaction(transactionMeta, error, actionId) {
201
282
  const newTransactionMeta = Object.assign(Object.assign({}, transactionMeta), { error: (0, utils_1.normalizeTxError)(error), status: types_1.TransactionStatus.failed });
@@ -215,6 +296,18 @@ class TransactionController extends base_controller_1.BaseControllerV1 {
215
296
  return { registryMethod, parsedRegistryMethod };
216
297
  });
217
298
  }
299
+ setEnableMultichain(enableMultichain) {
300
+ this.enableMultichain = enableMultichain;
301
+ if (enableMultichain) {
302
+ __classPrivateFieldGet(this, _TransactionController_initTrackingMap, "f").call(this);
303
+ }
304
+ }
305
+ /**
306
+ * Stops polling and removes listeners to prepare the controller for garbage collection.
307
+ */
308
+ destroy() {
309
+ __classPrivateFieldGet(this, _TransactionController_instances, "m", _TransactionController_stopAllTracking).call(this);
310
+ }
218
311
  /**
219
312
  * Handle new method data request.
220
313
  *
@@ -259,21 +352,26 @@ class TransactionController extends base_controller_1.BaseControllerV1 {
259
352
  * @param opts.swaps - Options for swaps transactions.
260
353
  * @param opts.swaps.hasApproveTx - Whether the transaction has an approval transaction.
261
354
  * @param opts.swaps.meta - Metadata for swap transaction.
355
+ * @param opts.networkClientId - The id of the network client for this transaction.
262
356
  * @returns Object containing a promise resolving to the transaction hash if approved.
263
357
  */
264
- addTransaction(txParams, { actionId, deviceConfirmedOn, method, origin, requireApproval, securityAlertResponse, sendFlowHistory, swaps = {}, type, } = {}) {
358
+ addTransaction(txParams, { actionId, deviceConfirmedOn, method, origin, requireApproval, securityAlertResponse, sendFlowHistory, swaps = {}, type, networkClientId, } = {}) {
265
359
  return __awaiter(this, void 0, void 0, function* () {
266
360
  (0, logger_1.projectLogger)('Adding transaction', txParams);
267
361
  txParams = (0, utils_1.normalizeTxParams)(txParams);
268
- const isEIP1559Compatible = yield this.getEIP1559Compatibility();
362
+ if (networkClientId && !this.trackingMap.has(networkClientId)) {
363
+ throw new Error('The networkClientId for this transaction could not be found');
364
+ }
365
+ const isEIP1559Compatible = yield this.getEIP1559Compatibility(networkClientId);
269
366
  (0, validation_1.validateTxParams)(txParams, isEIP1559Compatible);
270
367
  if (origin) {
271
368
  yield (0, validation_1.validateTransactionOrigin)(yield this.getPermittedAccounts(origin), this.getSelectedAddress(), txParams.from, origin);
272
369
  }
273
370
  const dappSuggestedGasFees = this.generateDappSuggestedGasFees(txParams, origin);
274
- const transactionType = type !== null && type !== void 0 ? type : (yield (0, transaction_type_1.determineTransactionType)(txParams, this.ethQuery)).type;
371
+ const chainId = this.getChainId(networkClientId);
372
+ const ethQuery = __classPrivateFieldGet(this, _TransactionController_instances, "m", _TransactionController_getEthQuery).call(this, { networkClientId, chainId });
373
+ const transactionType = type !== null && type !== void 0 ? type : (yield (0, transaction_type_1.determineTransactionType)(txParams, ethQuery)).type;
275
374
  const existingTransactionMeta = this.getTransactionWithActionId(actionId);
276
- const chainId = this.getChainId();
277
375
  // If a request to add a transaction with the same actionId is submitted again, a new transaction will not be created for it.
278
376
  const transactionMeta = existingTransactionMeta || {
279
377
  // Add actionId to txMeta to check if same actionId is seen again
@@ -290,6 +388,7 @@ class TransactionController extends base_controller_1.BaseControllerV1 {
290
388
  userEditedGasLimit: false,
291
389
  verifiedOnBlockchain: false,
292
390
  type: transactionType,
391
+ networkClientId,
293
392
  };
294
393
  yield this.updateGasProperties(transactionMeta);
295
394
  // Checks if a transaction already exists with a given actionId
@@ -326,15 +425,45 @@ class TransactionController extends base_controller_1.BaseControllerV1 {
326
425
  };
327
426
  });
328
427
  }
329
- startIncomingTransactionPolling() {
330
- this.incomingTransactionHelper.start();
428
+ startIncomingTransactionPolling(networkClientIds = []) {
429
+ if (networkClientIds.length === 0 || !this.enableMultichain) {
430
+ this.incomingTransactionHelper.start();
431
+ return;
432
+ }
433
+ networkClientIds.forEach((networkClientId) => {
434
+ var _a;
435
+ (_a = this.trackingMap.get(networkClientId)) === null || _a === void 0 ? void 0 : _a.incomingTransactionHelper.start();
436
+ });
331
437
  }
332
- stopIncomingTransactionPolling() {
438
+ stopIncomingTransactionPolling(networkClientIds = []) {
439
+ if (networkClientIds.length === 0 || !this.enableMultichain) {
440
+ this.incomingTransactionHelper.stop();
441
+ return;
442
+ }
443
+ networkClientIds.forEach((networkClientId) => {
444
+ var _a;
445
+ (_a = this.trackingMap.get(networkClientId)) === null || _a === void 0 ? void 0 : _a.incomingTransactionHelper.stop();
446
+ });
447
+ }
448
+ stopAllIncomingTransactionPolling() {
333
449
  this.incomingTransactionHelper.stop();
450
+ if (this.enableMultichain) {
451
+ for (const [, trackingMap] of this.trackingMap) {
452
+ trackingMap.incomingTransactionHelper.stop();
453
+ }
454
+ }
334
455
  }
335
- updateIncomingTransactions() {
456
+ updateIncomingTransactions(networkClientIds = []) {
336
457
  return __awaiter(this, void 0, void 0, function* () {
337
- yield this.incomingTransactionHelper.update();
458
+ if (networkClientIds.length === 0 || !this.enableMultichain) {
459
+ yield this.incomingTransactionHelper.update();
460
+ return;
461
+ }
462
+ yield Promise.allSettled(networkClientIds.map((networkClientId) => __awaiter(this, void 0, void 0, function* () {
463
+ var _a;
464
+ return yield ((_a = this.trackingMap
465
+ .get(networkClientId)) === null || _a === void 0 ? void 0 : _a.incomingTransactionHelper.update());
466
+ })));
338
467
  });
339
468
  }
340
469
  /**
@@ -406,7 +535,7 @@ class TransactionController extends base_controller_1.BaseControllerV1 {
406
535
  to: transactionMeta.txParams.from,
407
536
  value: '0x0',
408
537
  };
409
- const unsignedEthTx = this.prepareUnsignedEthTx(newTxParams);
538
+ const unsignedEthTx = this.prepareUnsignedEthTx(transactionMeta.chainId, newTxParams);
410
539
  const signedTx = yield this.sign(unsignedEthTx, transactionMeta.txParams.from);
411
540
  const rawTx = (0, ethereumjs_util_1.bufferToHex)(signedTx.serialize());
412
541
  const newFee = (_c = newTxParams.maxFeePerGas) !== null && _c !== void 0 ? _c : newTxParams.gasPrice;
@@ -418,10 +547,15 @@ class TransactionController extends base_controller_1.BaseControllerV1 {
418
547
  newFee,
419
548
  txParams: newTxParams,
420
549
  });
421
- const hash = yield this.publishTransaction(rawTx);
550
+ const ethQuery = __classPrivateFieldGet(this, _TransactionController_instances, "m", _TransactionController_getEthQuery).call(this, {
551
+ networkClientId: transactionMeta.networkClientId,
552
+ chainId: transactionMeta.chainId,
553
+ });
554
+ const hash = yield this.publishTransaction(ethQuery, rawTx);
422
555
  const cancelTransactionMeta = {
423
556
  actionId,
424
557
  chainId: transactionMeta.chainId,
558
+ networkClientId: transactionMeta.networkClientId,
425
559
  estimatedBaseFee,
426
560
  hash,
427
561
  id: (0, uuid_1.v1)(),
@@ -497,7 +631,7 @@ class TransactionController extends base_controller_1.BaseControllerV1 {
497
631
  (existingMaxPriorityFeePerGas && minMaxPriorityFeePerGas);
498
632
  const txParams = newMaxFeePerGas && newMaxPriorityFeePerGas
499
633
  ? Object.assign(Object.assign({}, transactionMeta.txParams), { gasLimit: transactionMeta.txParams.gas, maxFeePerGas: newMaxFeePerGas, maxPriorityFeePerGas: newMaxPriorityFeePerGas, type: types_1.TransactionEnvelopeType.feeMarket }) : Object.assign(Object.assign({}, transactionMeta.txParams), { gasLimit: transactionMeta.txParams.gas, gasPrice: newGasPrice });
500
- const unsignedEthTx = this.prepareUnsignedEthTx(txParams);
634
+ const unsignedEthTx = this.prepareUnsignedEthTx(transactionMeta.chainId, txParams);
501
635
  const signedTx = yield this.sign(unsignedEthTx, transactionMeta.txParams.from);
502
636
  yield this.updateTransactionMetaRSV(transactionMeta, signedTx);
503
637
  const rawTx = (0, ethereumjs_util_1.bufferToHex)(signedTx.serialize());
@@ -506,7 +640,11 @@ class TransactionController extends base_controller_1.BaseControllerV1 {
506
640
  ? transactionMeta.txParams.maxFeePerGas
507
641
  : transactionMeta.txParams.gasPrice;
508
642
  (0, logger_1.projectLogger)('Submitting speed up transaction', { oldFee, newFee, txParams });
509
- const hash = yield (0, controller_utils_1.query)(this.ethQuery, 'sendRawTransaction', [rawTx]);
643
+ const ethQuery = __classPrivateFieldGet(this, _TransactionController_instances, "m", _TransactionController_getEthQuery).call(this, {
644
+ networkClientId: transactionMeta.networkClientId,
645
+ chainId: transactionMeta.chainId,
646
+ });
647
+ const hash = yield this.publishTransaction(ethQuery, rawTx);
510
648
  const baseTransactionMeta = Object.assign(Object.assign({}, transactionMeta), { estimatedBaseFee, id: (0, uuid_1.v1)(), time: Date.now(), hash,
511
649
  actionId, originalGasEstimate: transactionMeta.txParams.gas, type: types_1.TransactionType.retry, originalType: transactionMeta.type });
512
650
  const newTransactionMeta = newMaxFeePerGas && newMaxPriorityFeePerGas
@@ -528,11 +666,13 @@ class TransactionController extends base_controller_1.BaseControllerV1 {
528
666
  * Estimates required gas for a given transaction.
529
667
  *
530
668
  * @param transaction - The transaction to estimate gas for.
669
+ * @param networkClientId - The network client id to use for the estimate.
531
670
  * @returns The gas and gas price.
532
671
  */
533
- estimateGas(transaction) {
672
+ estimateGas(transaction, networkClientId) {
534
673
  return __awaiter(this, void 0, void 0, function* () {
535
- const { estimatedGas, simulationFails } = yield (0, gas_1.estimateGas)(transaction, this.ethQuery);
674
+ const ethQuery = __classPrivateFieldGet(this, _TransactionController_instances, "m", _TransactionController_getEthQuery).call(this, { networkClientId });
675
+ const { estimatedGas, simulationFails } = yield (0, gas_1.estimateGas)(transaction, ethQuery);
536
676
  return { gas: estimatedGas, simulationFails };
537
677
  });
538
678
  }
@@ -541,10 +681,12 @@ class TransactionController extends base_controller_1.BaseControllerV1 {
541
681
  *
542
682
  * @param transaction - The transaction params to estimate gas for.
543
683
  * @param multiplier - The multiplier to use for the gas buffer.
684
+ * @param networkClientId - The network client id to use for the estimate.
544
685
  */
545
- estimateGasBuffered(transaction, multiplier) {
686
+ estimateGasBuffered(transaction, multiplier, networkClientId) {
546
687
  return __awaiter(this, void 0, void 0, function* () {
547
- const { blockGasLimit, estimatedGas, simulationFails } = yield (0, gas_1.estimateGas)(transaction, this.ethQuery);
688
+ const ethQuery = __classPrivateFieldGet(this, _TransactionController_instances, "m", _TransactionController_getEthQuery).call(this, { networkClientId });
689
+ const { blockGasLimit, estimatedGas, simulationFails } = yield (0, gas_1.estimateGas)(transaction, ethQuery);
548
690
  const gas = (0, gas_1.addGasBuffer)(estimatedGas, blockGasLimit, multiplier);
549
691
  return {
550
692
  gas,
@@ -614,12 +756,6 @@ class TransactionController extends base_controller_1.BaseControllerV1 {
614
756
  transactions: this.trimTransactionsForState(newTransactions),
615
757
  });
616
758
  }
617
- startIncomingTransactionProcessing() {
618
- this.incomingTransactionHelper.start();
619
- }
620
- stopIncomingTransactionProcessing() {
621
- this.incomingTransactionHelper.stop();
622
- }
623
759
  /**
624
760
  * Adds external provided transaction to state as confirmed transaction.
625
761
  *
@@ -770,11 +906,40 @@ class TransactionController extends base_controller_1.BaseControllerV1 {
770
906
  * Ensure `releaseLock` is called once processing of the `nonce` value is complete.
771
907
  *
772
908
  * @param address - The hex string address for the transaction.
909
+ * @param networkClientId - The network client ID for the transaction, used to fetch the correct nonce tracker.
773
910
  * @returns object with the `nextNonce` `nonceDetails`, and the releaseLock.
774
911
  */
775
- getNonceLock(address) {
912
+ getNonceLock(address, networkClientId) {
776
913
  return __awaiter(this, void 0, void 0, function* () {
777
- return this.nonceTracker.getNonceLock(address);
914
+ let releaseLockForChainIdKey;
915
+ let { nonceTracker } = this;
916
+ if (networkClientId && this.enableMultichain) {
917
+ const networkClient = this.getNetworkClientById(networkClientId);
918
+ releaseLockForChainIdKey = yield __classPrivateFieldGet(this, _TransactionController_instances, "m", _TransactionController_acquireNonceLockForChainIdKey).call(this, {
919
+ chainId: networkClient.configuration.chainId,
920
+ key: address,
921
+ });
922
+ const trackers = this.trackingMap.get(networkClientId);
923
+ if (!trackers) {
924
+ throw new Error('missing nonceTracker for networkClientId');
925
+ }
926
+ nonceTracker = trackers === null || trackers === void 0 ? void 0 : trackers.nonceTracker;
927
+ }
928
+ // Acquires the lock for the chainId + address and the nonceLock from the nonceTracker, then
929
+ // couples them together by replacing the nonceLock's releaseLock method with
930
+ // an anonymous function that calls releases both the original nonceLock and the
931
+ // lock for the chainId.
932
+ try {
933
+ const nonceLock = yield nonceTracker.getNonceLock(address);
934
+ return Object.assign(Object.assign({}, nonceLock), { releaseLock: () => {
935
+ nonceLock.releaseLock();
936
+ releaseLockForChainIdKey === null || releaseLockForChainIdKey === void 0 ? void 0 : releaseLockForChainIdKey();
937
+ } });
938
+ }
939
+ catch (err) {
940
+ releaseLockForChainIdKey === null || releaseLockForChainIdKey === void 0 ? void 0 : releaseLockForChainIdKey();
941
+ throw err;
942
+ }
778
943
  });
779
944
  }
780
945
  /**
@@ -809,7 +974,10 @@ class TransactionController extends base_controller_1.BaseControllerV1 {
809
974
  };
810
975
  editableParams.txParams = (0, lodash_1.pickBy)(editableParams.txParams);
811
976
  const updatedTransaction = (0, lodash_1.merge)(transactionMeta, editableParams);
812
- const { type } = yield (0, transaction_type_1.determineTransactionType)(updatedTransaction.txParams, this.ethQuery);
977
+ const { type } = yield (0, transaction_type_1.determineTransactionType)(updatedTransaction.txParams, __classPrivateFieldGet(this, _TransactionController_instances, "m", _TransactionController_getEthQuery).call(this, {
978
+ networkClientId: transactionMeta.networkClientId,
979
+ chainId: transactionMeta.chainId,
980
+ }));
813
981
  updatedTransaction.type = type;
814
982
  this.updateTransaction(updatedTransaction, `Update Editable Params for ${txId}`);
815
983
  return this.getTransaction(txId);
@@ -830,7 +998,7 @@ class TransactionController extends base_controller_1.BaseControllerV1 {
830
998
  return '';
831
999
  }
832
1000
  const initialTx = listOfTxParams[0];
833
- const common = this.getCommonConfiguration();
1001
+ const common = this.getCommonConfiguration(initialTx.chainId);
834
1002
  const initialTxAsEthTx = tx_1.TransactionFactory.fromTxData(initialTx, {
835
1003
  common,
836
1004
  });
@@ -843,12 +1011,12 @@ class TransactionController extends base_controller_1.BaseControllerV1 {
843
1011
  try {
844
1012
  // TODO: we should add a check to verify that all transactions have the same from address
845
1013
  const fromAddress = initialTx.from;
846
- nonceLock = yield this.nonceTracker.getNonceLock(fromAddress);
1014
+ nonceLock = yield this.getNonceLock(fromAddress);
847
1015
  const nonce = nonceLock.nextNonce;
848
1016
  (0, logger_1.projectLogger)('Using nonce from nonce tracker', nonce, nonceLock.nonceDetails);
849
1017
  rawTransactions = yield Promise.all(listOfTxParams.map((txParams) => {
850
1018
  txParams.nonce = (0, ethereumjs_util_1.addHexPrefix)(nonce.toString(16));
851
- return this.signExternalTransaction(txParams);
1019
+ return this.signExternalTransaction(txParams.chainId, txParams);
852
1020
  }));
853
1021
  }
854
1022
  catch (err) {
@@ -894,13 +1062,14 @@ class TransactionController extends base_controller_1.BaseControllerV1 {
894
1062
  const updatedTransactionMeta = (0, lodash_1.merge)(transactionMeta, (0, lodash_1.pickBy)({ hash, status }));
895
1063
  if (status === types_1.TransactionStatus.submitted) {
896
1064
  updatedTransactionMeta.submittedTime = new Date().getTime();
897
- this.hub.emit(`${transactionMeta.id}:finished`, updatedTransactionMeta);
898
1065
  }
899
1066
  if (status === types_1.TransactionStatus.failed) {
900
1067
  updatedTransactionMeta.error = (0, utils_1.normalizeTxError)(new Error(errorMessage));
901
- this.hub.emit(`${transactionMeta.id}:finished`, updatedTransactionMeta);
902
1068
  }
903
1069
  this.updateTransaction(updatedTransactionMeta, `TransactionController:updateCustodialTransaction - Custodial transaction updated`);
1070
+ if ([types_1.TransactionStatus.submitted, types_1.TransactionStatus.failed].includes(status)) {
1071
+ this.hub.emit(`${transactionMeta.id}:finished`, updatedTransactionMeta);
1072
+ }
904
1073
  }
905
1074
  /**
906
1075
  * Creates approvals for all unapproved transactions persisted.
@@ -1007,19 +1176,18 @@ class TransactionController extends base_controller_1.BaseControllerV1 {
1007
1176
  }
1008
1177
  return filteredTransactions;
1009
1178
  }
1010
- signExternalTransaction(transactionParams) {
1179
+ signExternalTransaction(chainId, transactionParams) {
1011
1180
  return __awaiter(this, void 0, void 0, function* () {
1012
1181
  if (!this.sign) {
1013
1182
  throw new Error('No sign method defined.');
1014
1183
  }
1015
1184
  const normalizedTransactionParams = (0, utils_1.normalizeTxParams)(transactionParams);
1016
- const chainId = this.getChainId();
1017
1185
  const type = (0, utils_1.isEIP1559Transaction)(normalizedTransactionParams)
1018
1186
  ? types_1.TransactionEnvelopeType.feeMarket
1019
1187
  : types_1.TransactionEnvelopeType.legacy;
1020
1188
  const updatedTransactionParams = Object.assign(Object.assign({}, normalizedTransactionParams), { type, gasLimit: normalizedTransactionParams.gas, chainId });
1021
1189
  const { from } = updatedTransactionParams;
1022
- const common = this.getCommonConfiguration();
1190
+ const common = this.getCommonConfiguration(chainId);
1023
1191
  const unsignedTransaction = tx_1.TransactionFactory.fromTxData(updatedTransactionParams, { common });
1024
1192
  const signedTransaction = yield this.sign(unsignedTransaction, from);
1025
1193
  const rawTransaction = (0, ethereumjs_util_1.bufferToHex)(signedTransaction.serialize());
@@ -1033,6 +1201,23 @@ class TransactionController extends base_controller_1.BaseControllerV1 {
1033
1201
  const transactions = this.state.transactions.filter(({ status }) => status !== types_1.TransactionStatus.unapproved);
1034
1202
  this.update({ transactions: this.trimTransactionsForState(transactions) });
1035
1203
  }
1204
+ /**
1205
+ * Stop the signing process for a specific transaction.
1206
+ * Throws an error causing the transaction status to be set to failed.
1207
+ * @param transactionId - The ID of the transaction to stop signing.
1208
+ */
1209
+ abortTransactionSigning(transactionId) {
1210
+ const transactionMeta = this.getTransaction(transactionId);
1211
+ if (!transactionMeta) {
1212
+ throw new Error(`Cannot abort signing as no transaction metadata found`);
1213
+ }
1214
+ const abortCallback = this.signAbortCallbacks.get(transactionId);
1215
+ if (!abortCallback) {
1216
+ throw new Error(`Cannot abort signing as transaction is not waiting for signing`);
1217
+ }
1218
+ abortCallback();
1219
+ this.signAbortCallbacks.delete(transactionId);
1220
+ }
1036
1221
  addMetadata(transactionMeta) {
1037
1222
  const { transactions } = this.state;
1038
1223
  transactions.push(transactionMeta);
@@ -1040,35 +1225,36 @@ class TransactionController extends base_controller_1.BaseControllerV1 {
1040
1225
  }
1041
1226
  updateGasProperties(transactionMeta) {
1042
1227
  return __awaiter(this, void 0, void 0, function* () {
1043
- const isEIP1559Compatible = (yield this.getEIP1559Compatibility()) &&
1228
+ const isEIP1559Compatible = (yield this.getEIP1559Compatibility(transactionMeta.networkClientId)) &&
1044
1229
  transactionMeta.txParams.type !== types_1.TransactionEnvelopeType.legacy;
1045
- const chainId = this.getChainId();
1230
+ const { networkClientId, chainId } = transactionMeta;
1231
+ const isCustomNetwork = networkClientId
1232
+ ? this.getNetworkClientById(networkClientId).configuration.type ===
1233
+ network_controller_1.NetworkClientType.Custom
1234
+ : this.getNetworkState().providerConfig.type === controller_utils_1.NetworkType.rpc;
1046
1235
  yield (0, gas_1.updateGas)({
1047
- ethQuery: this.ethQuery,
1048
- providerConfig: this.getNetworkState().providerConfig,
1236
+ ethQuery: __classPrivateFieldGet(this, _TransactionController_instances, "m", _TransactionController_getEthQuery).call(this, { networkClientId, chainId }),
1237
+ chainId,
1238
+ isCustomNetwork,
1049
1239
  txMeta: transactionMeta,
1050
1240
  });
1051
1241
  yield (0, gas_fees_1.updateGasFees)({
1052
1242
  eip1559: isEIP1559Compatible,
1053
- ethQuery: this.ethQuery,
1054
- getSavedGasFees: this.getSavedGasFees.bind(this, chainId),
1243
+ ethQuery: __classPrivateFieldGet(this, _TransactionController_instances, "m", _TransactionController_getEthQuery).call(this, { networkClientId, chainId }),
1244
+ getSavedGasFees: this.getSavedGasFees.bind(this),
1055
1245
  getGasFeeEstimates: this.getGasFeeEstimates.bind(this),
1056
1246
  txMeta: transactionMeta,
1057
1247
  });
1058
1248
  });
1059
1249
  }
1060
- getCurrentChainTransactionsByStatus(status) {
1061
- const chainId = this.getChainId();
1062
- return this.state.transactions.filter((transaction) => transaction.status === status && transaction.chainId === chainId);
1063
- }
1064
1250
  onBootCleanup() {
1065
1251
  this.submitApprovedTransactions();
1066
1252
  }
1067
1253
  /**
1068
- * Force to submit approved transactions on current chain.
1254
+ * Force submit approved transactions for all chains.
1069
1255
  */
1070
1256
  submitApprovedTransactions() {
1071
- const approvedTransactions = this.getCurrentChainTransactionsByStatus(types_1.TransactionStatus.approved);
1257
+ const approvedTransactions = this.state.transactions.filter((transaction) => transaction.status === types_1.TransactionStatus.approved);
1072
1258
  for (const transactionMeta of approvedTransactions) {
1073
1259
  if (this.beforeApproveOnInit(transactionMeta)) {
1074
1260
  this.approveTransaction(transactionMeta.id).catch((error) => {
@@ -1162,10 +1348,9 @@ class TransactionController extends base_controller_1.BaseControllerV1 {
1162
1348
  return __awaiter(this, void 0, void 0, function* () {
1163
1349
  const { transactions } = this.state;
1164
1350
  const releaseLock = yield this.mutex.acquire();
1165
- const chainId = this.getChainId();
1166
1351
  const index = transactions.findIndex(({ id }) => transactionId === id);
1167
1352
  const transactionMeta = transactions[index];
1168
- const { txParams: { from }, } = transactionMeta;
1353
+ const { txParams: { from }, networkClientId, } = transactionMeta;
1169
1354
  let releaseNonceLock;
1170
1355
  try {
1171
1356
  if (!this.sign) {
@@ -1173,7 +1358,7 @@ class TransactionController extends base_controller_1.BaseControllerV1 {
1173
1358
  this.failTransaction(transactionMeta, new Error('No sign method defined.'));
1174
1359
  return;
1175
1360
  }
1176
- else if (!chainId) {
1361
+ else if (!transactionMeta.chainId) {
1177
1362
  releaseLock();
1178
1363
  this.failTransaction(transactionMeta, new Error('No chainId defined.'));
1179
1364
  return;
@@ -1182,11 +1367,11 @@ class TransactionController extends base_controller_1.BaseControllerV1 {
1182
1367
  (0, logger_1.projectLogger)('Skipping approval as signing in progress', transactionId);
1183
1368
  return;
1184
1369
  }
1185
- const [nonce, releaseNonce] = yield (0, nonce_1.getNextNonce)(transactionMeta, this.nonceTracker);
1370
+ const [nonce, releaseNonce] = yield (0, nonce_1.getNextNonce)(transactionMeta, (address) => this.getNonceLock(address, networkClientId));
1186
1371
  releaseNonceLock = releaseNonce;
1187
1372
  transactionMeta.status = types_1.TransactionStatus.approved;
1188
1373
  transactionMeta.txParams.nonce = nonce;
1189
- transactionMeta.txParams.chainId = chainId;
1374
+ transactionMeta.txParams.chainId = transactionMeta.chainId;
1190
1375
  const baseTxParams = Object.assign(Object.assign({}, transactionMeta.txParams), { gasLimit: transactionMeta.txParams.gas });
1191
1376
  this.updateTransaction(transactionMeta, 'TransactionController#approveTransaction - Transaction approved');
1192
1377
  this.onTransactionStatusChange(transactionMeta);
@@ -1202,14 +1387,18 @@ class TransactionController extends base_controller_1.BaseControllerV1 {
1202
1387
  if (!rawTx) {
1203
1388
  return;
1204
1389
  }
1390
+ const ethQuery = __classPrivateFieldGet(this, _TransactionController_instances, "m", _TransactionController_getEthQuery).call(this, {
1391
+ networkClientId: transactionMeta.networkClientId,
1392
+ chainId: transactionMeta.chainId,
1393
+ });
1205
1394
  if (transactionMeta.type === types_1.TransactionType.swap) {
1206
1395
  (0, logger_1.projectLogger)('Determining pre-transaction balance');
1207
- const preTxBalance = yield (0, controller_utils_1.query)(this.ethQuery, 'getBalance', [from]);
1396
+ const preTxBalance = yield (0, controller_utils_1.query)(ethQuery, 'getBalance', [from]);
1208
1397
  transactionMeta.preTxBalance = preTxBalance;
1209
1398
  (0, logger_1.projectLogger)('Updated pre-transaction balance', transactionMeta.preTxBalance);
1210
1399
  }
1211
1400
  (0, logger_1.projectLogger)('Publishing transaction', txParams);
1212
- const hash = yield this.publishTransaction(rawTx);
1401
+ const hash = yield this.publishTransaction(ethQuery, rawTx);
1213
1402
  (0, logger_1.projectLogger)('Publish successful', hash);
1214
1403
  transactionMeta.hash = hash;
1215
1404
  transactionMeta.status = types_1.TransactionStatus.submitted;
@@ -1234,9 +1423,9 @@ class TransactionController extends base_controller_1.BaseControllerV1 {
1234
1423
  }
1235
1424
  });
1236
1425
  }
1237
- publishTransaction(rawTransaction) {
1426
+ publishTransaction(ethQuery, rawTransaction) {
1238
1427
  return __awaiter(this, void 0, void 0, function* () {
1239
- return yield (0, controller_utils_1.query)(this.ethQuery, 'sendRawTransaction', [rawTransaction]);
1428
+ return yield (0, controller_utils_1.query)(ethQuery, 'sendRawTransaction', [rawTransaction]);
1240
1429
  });
1241
1430
  }
1242
1431
  /**
@@ -1352,14 +1541,17 @@ class TransactionController extends base_controller_1.BaseControllerV1 {
1352
1541
  const isCompleted = this.isLocalFinalState(transaction.status);
1353
1542
  return { meta: transaction, isCompleted };
1354
1543
  }
1355
- getChainId() {
1544
+ getChainId(networkClientId) {
1545
+ if (networkClientId) {
1546
+ return this.getNetworkClientById(networkClientId).configuration.chainId;
1547
+ }
1356
1548
  const { providerConfig } = this.getNetworkState();
1357
1549
  return providerConfig.chainId;
1358
1550
  }
1359
- prepareUnsignedEthTx(txParams) {
1551
+ prepareUnsignedEthTx(chainId, txParams) {
1360
1552
  return tx_1.TransactionFactory.fromTxData(txParams, {
1361
- common: this.getCommonConfiguration(),
1362
1553
  freeze: false,
1554
+ common: this.getCommonConfiguration(chainId),
1363
1555
  });
1364
1556
  }
1365
1557
  /**
@@ -1369,17 +1561,11 @@ class TransactionController extends base_controller_1.BaseControllerV1 {
1369
1561
  * specified in txParams, @ethereumjs/tx is able to determine which EIP-2718
1370
1562
  * transaction type to use.
1371
1563
  *
1564
+ * @param chainId - The chainId to use for the configuration.
1372
1565
  * @returns common configuration object
1373
1566
  */
1374
- getCommonConfiguration() {
1375
- const { providerConfig: { type: chain, chainId, nickname: name }, } = this.getNetworkState();
1376
- if (chain !== controller_utils_1.RPC &&
1377
- chain !== controller_utils_1.NetworkType['linea-goerli'] &&
1378
- chain !== controller_utils_1.NetworkType['linea-mainnet']) {
1379
- return new common_1.Common({ chain, hardfork: exports.HARDFORK });
1380
- }
1567
+ getCommonConfiguration(chainId) {
1381
1568
  const customChainParams = {
1382
- name,
1383
1569
  chainId: parseInt(chainId, 16),
1384
1570
  defaultHardfork: exports.HARDFORK,
1385
1571
  };
@@ -1434,7 +1620,7 @@ class TransactionController extends base_controller_1.BaseControllerV1 {
1434
1620
  */
1435
1621
  addExternalTransaction(transactionMeta) {
1436
1622
  var _a, _b;
1437
- const chainId = this.getChainId();
1623
+ const { chainId } = transactionMeta;
1438
1624
  const { transactions } = this.state;
1439
1625
  const fromAddress = (_a = transactionMeta === null || transactionMeta === void 0 ? void 0 : transactionMeta.txParams) === null || _a === void 0 ? void 0 : _a.from;
1440
1626
  const sameFromAndNetworkTransactions = transactions.filter((transaction) => transaction.txParams.from === fromAddress &&
@@ -1461,10 +1647,13 @@ class TransactionController extends base_controller_1.BaseControllerV1 {
1461
1647
  */
1462
1648
  markNonceDuplicatesDropped(transactionId) {
1463
1649
  var _a, _b;
1464
- const chainId = this.getChainId();
1465
1650
  const transactionMeta = this.getTransaction(transactionId);
1466
- const nonce = (_a = transactionMeta === null || transactionMeta === void 0 ? void 0 : transactionMeta.txParams) === null || _a === void 0 ? void 0 : _a.nonce;
1467
- const from = (_b = transactionMeta === null || transactionMeta === void 0 ? void 0 : transactionMeta.txParams) === null || _b === void 0 ? void 0 : _b.from;
1651
+ if (!transactionMeta) {
1652
+ return;
1653
+ }
1654
+ const nonce = (_a = transactionMeta.txParams) === null || _a === void 0 ? void 0 : _a.nonce;
1655
+ const from = (_b = transactionMeta.txParams) === null || _b === void 0 ? void 0 : _b.from;
1656
+ const { chainId } = transactionMeta;
1468
1657
  const sameNonceTxs = this.state.transactions.filter((transaction) => transaction.id !== transactionId &&
1469
1658
  transaction.txParams.from === from &&
1470
1659
  transaction.txParams.nonce === nonce &&
@@ -1475,8 +1664,8 @@ class TransactionController extends base_controller_1.BaseControllerV1 {
1475
1664
  }
1476
1665
  // Mark all same nonce transactions as dropped and give it a replacedBy hash
1477
1666
  for (const transaction of sameNonceTxs) {
1478
- transaction.replacedBy = transactionMeta === null || transactionMeta === void 0 ? void 0 : transactionMeta.hash;
1479
- transaction.replacedById = transactionMeta === null || transactionMeta === void 0 ? void 0 : transactionMeta.id;
1667
+ transaction.replacedBy = transactionMeta.hash;
1668
+ transaction.replacedById = transactionMeta.id;
1480
1669
  // Drop any transaction that wasn't previously failed (off chain failure)
1481
1670
  if (transaction.status !== types_1.TransactionStatus.failed) {
1482
1671
  this.setTransactionStatusDropped(transaction);
@@ -1532,26 +1721,44 @@ class TransactionController extends base_controller_1.BaseControllerV1 {
1532
1721
  }
1533
1722
  });
1534
1723
  }
1535
- getEIP1559Compatibility() {
1724
+ getEIP1559Compatibility(networkClientId) {
1536
1725
  return __awaiter(this, void 0, void 0, function* () {
1537
- const currentNetworkIsEIP1559Compatible = yield this.getCurrentNetworkEIP1559Compatibility();
1726
+ const currentNetworkIsEIP1559Compatible = yield this.getCurrentNetworkEIP1559Compatibility(networkClientId);
1538
1727
  const currentAccountIsEIP1559Compatible = yield this.getCurrentAccountEIP1559Compatibility();
1539
1728
  return (currentNetworkIsEIP1559Compatible && currentAccountIsEIP1559Compatible);
1540
1729
  });
1541
1730
  }
1542
- addPendingTransactionTrackerListeners() {
1543
- this.pendingTransactionTracker.hub.on('transaction-confirmed', this.onConfirmedTransaction.bind(this));
1544
- this.pendingTransactionTracker.hub.on('transaction-dropped', this.setTransactionStatusDropped.bind(this));
1545
- this.pendingTransactionTracker.hub.on('transaction-failed', this.failTransaction.bind(this));
1546
- this.pendingTransactionTracker.hub.on('transaction-updated', this.updateTransaction.bind(this));
1731
+ removeIncomingTransactionHelperListeners(incomingTransactionHelper = this.incomingTransactionHelper) {
1732
+ incomingTransactionHelper.hub.removeAllListeners('transactions');
1733
+ incomingTransactionHelper.hub.removeAllListeners('updatedLastFetchedBlockNumbers');
1734
+ }
1735
+ addIncomingTransactionHelperListeners(incomingTransactionHelper = this.incomingTransactionHelper) {
1736
+ incomingTransactionHelper.hub.on('transactions', this.onIncomingTransactions.bind(this));
1737
+ incomingTransactionHelper.hub.on('updatedLastFetchedBlockNumbers', this.onUpdatedLastFetchedBlockNumbers.bind(this));
1738
+ }
1739
+ removePendingTransactionTrackerListeners(pendingTransactionTracker = this.pendingTransactionTracker) {
1740
+ pendingTransactionTracker.hub.removeAllListeners('transaction-confirmed');
1741
+ pendingTransactionTracker.hub.removeAllListeners('transaction-dropped');
1742
+ pendingTransactionTracker.hub.removeAllListeners('transaction-failed');
1743
+ pendingTransactionTracker.hub.removeAllListeners('transaction-updated');
1744
+ }
1745
+ addPendingTransactionTrackerListeners(pendingTransactionTracker = this.pendingTransactionTracker) {
1746
+ pendingTransactionTracker.hub.on('transaction-confirmed', this.onConfirmedTransaction.bind(this));
1747
+ pendingTransactionTracker.hub.on('transaction-dropped', this.setTransactionStatusDropped.bind(this));
1748
+ pendingTransactionTracker.hub.on('transaction-failed', this.failTransaction.bind(this));
1749
+ pendingTransactionTracker.hub.on('transaction-updated', this.updateTransaction.bind(this));
1547
1750
  }
1548
1751
  signTransaction(transactionMeta, txParams) {
1549
- var _a;
1550
1752
  return __awaiter(this, void 0, void 0, function* () {
1551
1753
  (0, logger_1.projectLogger)('Signing transaction', txParams);
1552
- const unsignedEthTx = this.prepareUnsignedEthTx(txParams);
1754
+ const unsignedEthTx = this.prepareUnsignedEthTx(transactionMeta.chainId, txParams);
1553
1755
  this.inProcessOfSigning.add(transactionMeta.id);
1554
- const signedTx = yield ((_a = this.sign) === null || _a === void 0 ? void 0 : _a.call(this, unsignedEthTx, txParams.from, ...this.getAdditionalSignArguments(transactionMeta)));
1756
+ const signedTx = yield new Promise((resolve, reject) => {
1757
+ var _a;
1758
+ (_a = this.sign) === null || _a === void 0 ? void 0 : _a.call(this, unsignedEthTx, txParams.from, ...this.getAdditionalSignArguments(transactionMeta)).then(resolve, reject);
1759
+ this.signAbortCallbacks.set(transactionMeta.id, () => reject(new Error('Signing aborted by user')));
1760
+ });
1761
+ this.signAbortCallbacks.delete(transactionMeta.id);
1555
1762
  if (!signedTx) {
1556
1763
  (0, logger_1.projectLogger)('Skipping signed status as no signed transaction');
1557
1764
  return undefined;
@@ -1574,14 +1781,13 @@ class TransactionController extends base_controller_1.BaseControllerV1 {
1574
1781
  onTransactionStatusChange(transactionMeta) {
1575
1782
  this.hub.emit('transaction-status-update', { transactionMeta });
1576
1783
  }
1577
- getNonceTrackerPendingTransactions(address) {
1578
- const standardPendingTransactions = this.getNonceTrackerTransactions(types_1.TransactionStatus.submitted, address);
1579
- const externalPendingTransactions = this.getExternalPendingTransactions(address);
1784
+ getNonceTrackerPendingTransactions(chainId, address) {
1785
+ const standardPendingTransactions = this.getNonceTrackerTransactions(types_1.TransactionStatus.submitted, address, chainId);
1786
+ const externalPendingTransactions = this.getExternalPendingTransactions(address, chainId);
1580
1787
  return [...standardPendingTransactions, ...externalPendingTransactions];
1581
1788
  }
1582
- getNonceTrackerTransactions(status, address) {
1583
- const currentChainId = this.getChainId();
1584
- return (0, nonce_1.getAndFormatTransactionsForNonceTracker)(currentChainId, address, status, this.state.transactions);
1789
+ getNonceTrackerTransactions(status, address, chainId = this.getChainId()) {
1790
+ return (0, nonce_1.getAndFormatTransactionsForNonceTracker)(chainId, address, status, this.state.transactions);
1585
1791
  }
1586
1792
  onConfirmedTransaction(transactionMeta) {
1587
1793
  (0, logger_1.projectLogger)('Processing confirmed transaction', transactionMeta.id);
@@ -1599,8 +1805,12 @@ class TransactionController extends base_controller_1.BaseControllerV1 {
1599
1805
  if (transactionMeta.type !== types_1.TransactionType.swap) {
1600
1806
  return;
1601
1807
  }
1808
+ const ethQuery = __classPrivateFieldGet(this, _TransactionController_instances, "m", _TransactionController_getEthQuery).call(this, {
1809
+ networkClientId: transactionMeta.networkClientId,
1810
+ chainId: transactionMeta.chainId,
1811
+ });
1602
1812
  const { updatedTransactionMeta, approvalTransactionMeta } = yield (0, swaps_1.updatePostTransactionBalance)(transactionMeta, {
1603
- ethQuery: this.ethQuery,
1813
+ ethQuery,
1604
1814
  getTransaction: this.getTransaction.bind(this),
1605
1815
  updateTransaction: this.updateTransaction.bind(this),
1606
1816
  });
@@ -1617,4 +1827,127 @@ class TransactionController extends base_controller_1.BaseControllerV1 {
1617
1827
  }
1618
1828
  }
1619
1829
  exports.TransactionController = TransactionController;
1830
+ _TransactionController_refreshEtherscanRemoteTransactionSources = new WeakMap(), _TransactionController_refreshTrackingMap = new WeakMap(), _TransactionController_initTrackingMap = new WeakMap(), _TransactionController_checkForPendingTransactionAndStartPolling = new WeakMap(), _TransactionController_instances = new WeakSet(), _TransactionController_acquireNonceLockForChainIdKey = function _TransactionController_acquireNonceLockForChainIdKey({ chainId, key = 'global', }) {
1831
+ return __awaiter(this, void 0, void 0, function* () {
1832
+ let nonceMutexesForChainId = this.nonceMutexesByChainId.get(chainId);
1833
+ if (!nonceMutexesForChainId) {
1834
+ nonceMutexesForChainId = new Map();
1835
+ this.nonceMutexesByChainId.set(chainId, nonceMutexesForChainId);
1836
+ }
1837
+ let nonceMutexForKey = nonceMutexesForChainId.get(key);
1838
+ if (!nonceMutexForKey) {
1839
+ nonceMutexForKey = new async_mutex_1.Mutex();
1840
+ nonceMutexesForChainId.set(key, nonceMutexForKey);
1841
+ }
1842
+ return yield nonceMutexForKey.acquire();
1843
+ });
1844
+ }, _TransactionController_getEthQuery = function _TransactionController_getEthQuery({ networkClientId, chainId, }) {
1845
+ // if multichain is disabled, use the global ethQuery
1846
+ if (!this.enableMultichain) {
1847
+ return this.ethQuery;
1848
+ }
1849
+ let networkClient;
1850
+ if (networkClientId) {
1851
+ try {
1852
+ networkClient = this.getNetworkClientById(networkClientId);
1853
+ }
1854
+ catch (err) {
1855
+ (0, logger_1.projectLogger)('failed to get network client by networkClientId');
1856
+ }
1857
+ }
1858
+ if (!networkClient && chainId) {
1859
+ try {
1860
+ networkClientId = this.findNetworkClientIdByChainId(chainId);
1861
+ networkClient = this.getNetworkClientById(networkClientId);
1862
+ }
1863
+ catch (err) {
1864
+ (0, logger_1.projectLogger)('failed to get network client by chainId');
1865
+ }
1866
+ }
1867
+ if (networkClient) {
1868
+ return new eth_query_1.default(networkClient.provider);
1869
+ }
1870
+ // NOTE(JL): we're not ready to drop globally selected ethQuery yet.
1871
+ // Some calls to getEthQuery only have access to optional networkClientId
1872
+ // throw new Error('failed to get eth query instance');
1873
+ return this.ethQuery;
1874
+ }, _TransactionController_stopTrackingByNetworkClientId = function _TransactionController_stopTrackingByNetworkClientId(networkClientId) {
1875
+ const trackers = this.trackingMap.get(networkClientId);
1876
+ if (trackers) {
1877
+ trackers.pendingTransactionTracker.stop();
1878
+ this.removePendingTransactionTrackerListeners(trackers.pendingTransactionTracker);
1879
+ trackers.incomingTransactionHelper.stop();
1880
+ this.removeIncomingTransactionHelperListeners(trackers.incomingTransactionHelper);
1881
+ this.trackingMap.delete(networkClientId);
1882
+ this.hub.emit('tracking-map-remove', networkClientId);
1883
+ }
1884
+ }, _TransactionController_stopAllTracking = function _TransactionController_stopAllTracking() {
1885
+ this.pendingTransactionTracker.stop();
1886
+ this.removePendingTransactionTrackerListeners();
1887
+ this.incomingTransactionHelper.stop();
1888
+ this.removeIncomingTransactionHelperListeners();
1889
+ if (this.enableMultichain) {
1890
+ for (const [networkClientId] of this.trackingMap) {
1891
+ __classPrivateFieldGet(this, _TransactionController_instances, "m", _TransactionController_stopTrackingByNetworkClientId).call(this, networkClientId);
1892
+ }
1893
+ }
1894
+ }, _TransactionController_startTrackingByNetworkClientId = function _TransactionController_startTrackingByNetworkClientId(networkClientId) {
1895
+ const trackers = this.trackingMap.get(networkClientId);
1896
+ if (trackers) {
1897
+ return;
1898
+ }
1899
+ const networkClient = this.getNetworkClientById(networkClientId);
1900
+ const { chainId } = networkClient.configuration;
1901
+ let etherscanRemoteTransactionSource = this.etherscanRemoteTransactionSourcesMap.get(chainId);
1902
+ if (!etherscanRemoteTransactionSource) {
1903
+ etherscanRemoteTransactionSource = new EtherscanRemoteTransactionSource_1.EtherscanRemoteTransactionSource({
1904
+ includeTokenTransfers: this.incomingTransactionOptions.includeTokenTransfers,
1905
+ });
1906
+ this.etherscanRemoteTransactionSourcesMap.set(chainId, etherscanRemoteTransactionSource);
1907
+ }
1908
+ const ethQuery = new eth_query_1.default(networkClient.provider);
1909
+ const nonceTracker = new nonce_tracker_1.NonceTracker({
1910
+ // TODO: Replace `any` with type
1911
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
1912
+ provider: networkClient.provider,
1913
+ blockTracker: networkClient.blockTracker,
1914
+ getPendingTransactions: this.getNonceTrackerPendingTransactions.bind(this, chainId),
1915
+ getConfirmedTransactions: this.getNonceTrackerTransactions.bind(this, types_1.TransactionStatus.confirmed),
1916
+ });
1917
+ const incomingTransactionHelper = new IncomingTransactionHelper_1.IncomingTransactionHelper({
1918
+ blockTracker: networkClient.blockTracker,
1919
+ getCurrentAccount: this.getSelectedAddress,
1920
+ getLastFetchedBlockNumbers: () => this.state.lastFetchedBlockNumbers,
1921
+ getChainId: () => chainId,
1922
+ isEnabled: this.incomingTransactionOptions.isEnabled,
1923
+ queryEntireHistory: this.incomingTransactionOptions.queryEntireHistory,
1924
+ remoteTransactionSource: etherscanRemoteTransactionSource,
1925
+ transactionLimit: this.config.txHistoryLimit,
1926
+ updateTransactions: this.incomingTransactionOptions.updateTransactions,
1927
+ });
1928
+ this.addIncomingTransactionHelperListeners(incomingTransactionHelper);
1929
+ const pendingTransactionTracker = new PendingTransactionTracker_1.PendingTransactionTracker({
1930
+ approveTransaction: this.approveTransaction.bind(this),
1931
+ blockTracker: networkClient.blockTracker,
1932
+ getChainId: () => chainId,
1933
+ getEthQuery: () => ethQuery,
1934
+ getTransactions: () => this.state.transactions,
1935
+ isResubmitEnabled: this.pendingTransactionOptions.isResubmitEnabled,
1936
+ getGlobalLock: __classPrivateFieldGet(this, _TransactionController_instances, "m", _TransactionController_acquireNonceLockForChainIdKey).bind(this, {
1937
+ chainId,
1938
+ }),
1939
+ publishTransaction: this.publishTransaction.bind(this),
1940
+ hooks: {
1941
+ beforeCheckPendingTransaction: this.beforeCheckPendingTransaction.bind(this),
1942
+ beforePublish: this.beforePublish.bind(this),
1943
+ },
1944
+ });
1945
+ this.addPendingTransactionTrackerListeners(pendingTransactionTracker);
1946
+ this.trackingMap.set(networkClientId, {
1947
+ nonceTracker,
1948
+ incomingTransactionHelper,
1949
+ pendingTransactionTracker,
1950
+ });
1951
+ this.hub.emit('tracking-map-add', networkClientId);
1952
+ };
1620
1953
  //# sourceMappingURL=TransactionController.js.map