@metamask-previews/transaction-controller 46.0.0-preview-5dad659d → 46.0.0-preview-5b37150f

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 (54) hide show
  1. package/CHANGELOG.md +1 -0
  2. package/dist/TransactionController.cjs +54 -17
  3. package/dist/TransactionController.cjs.map +1 -1
  4. package/dist/TransactionController.d.cts +20 -2
  5. package/dist/TransactionController.d.cts.map +1 -1
  6. package/dist/TransactionController.d.mts +20 -2
  7. package/dist/TransactionController.d.mts.map +1 -1
  8. package/dist/TransactionController.mjs +54 -17
  9. package/dist/TransactionController.mjs.map +1 -1
  10. package/dist/hooks/CollectPublishHook.cjs +84 -0
  11. package/dist/hooks/CollectPublishHook.cjs.map +1 -0
  12. package/dist/hooks/CollectPublishHook.d.cts +29 -0
  13. package/dist/hooks/CollectPublishHook.d.cts.map +1 -0
  14. package/dist/hooks/CollectPublishHook.d.mts +29 -0
  15. package/dist/hooks/CollectPublishHook.d.mts.map +1 -0
  16. package/dist/hooks/CollectPublishHook.mjs +80 -0
  17. package/dist/hooks/CollectPublishHook.mjs.map +1 -0
  18. package/dist/hooks/ExtraTransactionsPublishHook.cjs +76 -0
  19. package/dist/hooks/ExtraTransactionsPublishHook.cjs.map +1 -0
  20. package/dist/hooks/ExtraTransactionsPublishHook.d.cts +18 -0
  21. package/dist/hooks/ExtraTransactionsPublishHook.d.cts.map +1 -0
  22. package/dist/hooks/ExtraTransactionsPublishHook.d.mts +18 -0
  23. package/dist/hooks/ExtraTransactionsPublishHook.d.mts.map +1 -0
  24. package/dist/hooks/ExtraTransactionsPublishHook.mjs +72 -0
  25. package/dist/hooks/ExtraTransactionsPublishHook.mjs.map +1 -0
  26. package/dist/index.cjs.map +1 -1
  27. package/dist/index.d.cts +1 -1
  28. package/dist/index.d.cts.map +1 -1
  29. package/dist/index.d.mts +1 -1
  30. package/dist/index.d.mts.map +1 -1
  31. package/dist/index.mjs.map +1 -1
  32. package/dist/types.cjs.map +1 -1
  33. package/dist/types.d.cts +71 -0
  34. package/dist/types.d.cts.map +1 -1
  35. package/dist/types.d.mts +71 -0
  36. package/dist/types.d.mts.map +1 -1
  37. package/dist/types.mjs.map +1 -1
  38. package/dist/utils/batch.cjs +129 -2
  39. package/dist/utils/batch.cjs.map +1 -1
  40. package/dist/utils/batch.d.cts +8 -1
  41. package/dist/utils/batch.d.cts.map +1 -1
  42. package/dist/utils/batch.d.mts +8 -1
  43. package/dist/utils/batch.d.mts.map +1 -1
  44. package/dist/utils/batch.mjs +129 -2
  45. package/dist/utils/batch.mjs.map +1 -1
  46. package/dist/utils/validation.cjs +19 -1
  47. package/dist/utils/validation.cjs.map +1 -1
  48. package/dist/utils/validation.d.cts +12 -0
  49. package/dist/utils/validation.d.cts.map +1 -1
  50. package/dist/utils/validation.d.mts +12 -0
  51. package/dist/utils/validation.d.mts.map +1 -1
  52. package/dist/utils/validation.mjs +17 -0
  53. package/dist/utils/validation.mjs.map +1 -1
  54. package/package.json +4 -4
package/CHANGELOG.md CHANGED
@@ -24,6 +24,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
24
24
 
25
25
  ### Changed
26
26
 
27
+ - Throw if `addTransactionBatch` is called with any nested transaction with `to` matching internal account ([#5369](https://github.com/MetaMask/core/pull/5369))
27
28
  - **BREAKING:** Support atomic batch transactions ([#5306](https://github.com/MetaMask/core/pull/5306))
28
29
  - Require `AccountsController:getState` action permission in messenger.
29
30
  - Require `RemoteFeatureFlagController:getState` action permission in messenger.
@@ -13,7 +13,7 @@ var __classPrivateFieldSet = (this && this.__classPrivateFieldSet) || function (
13
13
  var __importDefault = (this && this.__importDefault) || function (mod) {
14
14
  return (mod && mod.__esModule) ? mod : { "default": mod };
15
15
  };
16
- var _TransactionController_instances, _TransactionController_internalEvents, _TransactionController_methodDataHelper, _TransactionController_incomingTransactionChainIds, _TransactionController_incomingTransactionHelper, _TransactionController_incomingTransactionOptions, _TransactionController_pendingTransactionOptions, _TransactionController_trace, _TransactionController_transactionHistoryLimit, _TransactionController_isFirstTimeInteractionEnabled, _TransactionController_isSimulationEnabled, _TransactionController_testGasFeeFlows, _TransactionController_multichainTrackingHelper, _TransactionController_retryTransaction, _TransactionController_getChainId, _TransactionController_getNetworkClientId, _TransactionController_getEthQuery, _TransactionController_getProvider, _TransactionController_createNonceTracker, _TransactionController_createPendingTransactionTracker, _TransactionController_checkForPendingTransactionAndStartPolling, _TransactionController_stopAllTracking, _TransactionController_addIncomingTransactionHelperListeners, _TransactionController_removePendingTransactionTrackerListeners, _TransactionController_addPendingTransactionTrackerListeners, _TransactionController_getNonceTrackerPendingTransactions, _TransactionController_getGasFeeFlows, _TransactionController_getLayer1GasFeeFlows, _TransactionController_updateTransactionInternal, _TransactionController_updateFirstTimeInteraction, _TransactionController_updateSimulationData, _TransactionController_onGasFeePollerTransactionUpdate, _TransactionController_getSelectedAccount, _TransactionController_getInternalAccounts, _TransactionController_updateSubmitHistory;
16
+ var _TransactionController_instances, _TransactionController_internalEvents, _TransactionController_methodDataHelper, _TransactionController_incomingTransactionChainIds, _TransactionController_incomingTransactionHelper, _TransactionController_incomingTransactionOptions, _TransactionController_pendingTransactionOptions, _TransactionController_publishBatchHook, _TransactionController_trace, _TransactionController_transactionHistoryLimit, _TransactionController_isFirstTimeInteractionEnabled, _TransactionController_isSimulationEnabled, _TransactionController_testGasFeeFlows, _TransactionController_multichainTrackingHelper, _TransactionController_retryTransaction, _TransactionController_getChainId, _TransactionController_getNetworkClientId, _TransactionController_getEthQuery, _TransactionController_getProvider, _TransactionController_createNonceTracker, _TransactionController_createPendingTransactionTracker, _TransactionController_checkForPendingTransactionAndStartPolling, _TransactionController_stopAllTracking, _TransactionController_addIncomingTransactionHelperListeners, _TransactionController_removePendingTransactionTrackerListeners, _TransactionController_addPendingTransactionTrackerListeners, _TransactionController_getNonceTrackerPendingTransactions, _TransactionController_getGasFeeFlows, _TransactionController_getLayer1GasFeeFlows, _TransactionController_updateTransactionInternal, _TransactionController_updateFirstTimeInteraction, _TransactionController_updateSimulationData, _TransactionController_onGasFeePollerTransactionUpdate, _TransactionController_getSelectedAccount, _TransactionController_getInternalAccounts, _TransactionController_updateSubmitHistory;
17
17
  Object.defineProperty(exports, "__esModule", { value: true });
18
18
  exports.TransactionController = exports.ApprovalState = exports.SPEED_UP_RATE = exports.CANCEL_RATE = void 0;
19
19
  const base_controller_1 = require("@metamask/base-controller");
@@ -23,7 +23,6 @@ const network_controller_1 = require("@metamask/network-controller");
23
23
  const nonce_tracker_1 = require("@metamask/nonce-tracker");
24
24
  const rpc_errors_1 = require("@metamask/rpc-errors");
25
25
  const utils_1 = require("@metamask/utils");
26
- const async_mutex_1 = require("async-mutex");
27
26
  // This package purposefully relies on Node's EventEmitter module.
28
27
  // eslint-disable-next-line import-x/no-nodejs-modules
29
28
  const events_1 = require("events");
@@ -42,6 +41,7 @@ const MethodDataHelper_1 = require("./helpers/MethodDataHelper.cjs");
42
41
  const MultichainTrackingHelper_1 = require("./helpers/MultichainTrackingHelper.cjs");
43
42
  const PendingTransactionTracker_1 = require("./helpers/PendingTransactionTracker.cjs");
44
43
  const ResimulateHelper_1 = require("./helpers/ResimulateHelper.cjs");
44
+ const ExtraTransactionsPublishHook_1 = require("./hooks/ExtraTransactionsPublishHook.cjs");
45
45
  const logger_1 = require("./logger.cjs");
46
46
  const types_1 = require("./types.cjs");
47
47
  const batch_1 = require("./utils/batch.cjs");
@@ -192,11 +192,11 @@ class TransactionController extends base_controller_1.BaseController {
192
192
  _TransactionController_internalEvents.set(this, new events_1.EventEmitter());
193
193
  this.approvingTransactionIds = new Set();
194
194
  _TransactionController_methodDataHelper.set(this, void 0);
195
- this.mutex = new async_mutex_1.Mutex();
196
195
  _TransactionController_incomingTransactionChainIds.set(this, new Set());
197
196
  _TransactionController_incomingTransactionHelper.set(this, void 0);
198
197
  _TransactionController_incomingTransactionOptions.set(this, void 0);
199
198
  _TransactionController_pendingTransactionOptions.set(this, void 0);
199
+ _TransactionController_publishBatchHook.set(this, void 0);
200
200
  this.signAbortCallbacks = new Map();
201
201
  _TransactionController_trace.set(this, void 0);
202
202
  _TransactionController_transactionHistoryLimit.set(this, void 0);
@@ -227,6 +227,7 @@ class TransactionController extends base_controller_1.BaseController {
227
227
  this.securityProviderRequest = securityProviderRequest;
228
228
  __classPrivateFieldSet(this, _TransactionController_incomingTransactionOptions, incomingTransactions, "f");
229
229
  __classPrivateFieldSet(this, _TransactionController_pendingTransactionOptions, pendingTransactions, "f");
230
+ __classPrivateFieldSet(this, _TransactionController_publishBatchHook, hooks?.publishBatch, "f");
230
231
  __classPrivateFieldSet(this, _TransactionController_transactionHistoryLimit, transactionHistoryLimit, "f");
231
232
  this.sign = sign;
232
233
  __classPrivateFieldSet(this, _TransactionController_testGasFeeFlows, testGasFeeFlows === true, "f");
@@ -340,8 +341,12 @@ class TransactionController extends base_controller_1.BaseController {
340
341
  addTransaction: this.addTransaction.bind(this),
341
342
  getChainId: __classPrivateFieldGet(this, _TransactionController_instances, "m", _TransactionController_getChainId).bind(this),
342
343
  getEthQuery: (networkClientId) => __classPrivateFieldGet(this, _TransactionController_instances, "m", _TransactionController_getEthQuery).call(this, { networkClientId }),
344
+ getInternalAccounts: __classPrivateFieldGet(this, _TransactionController_instances, "m", _TransactionController_getInternalAccounts).bind(this),
345
+ getTransaction: (transactionId) => this.getTransactionOrThrow(transactionId),
343
346
  messenger: this.messagingSystem,
347
+ publishBatchHook: __classPrivateFieldGet(this, _TransactionController_publishBatchHook, "f"),
344
348
  request,
349
+ updateTransaction: __classPrivateFieldGet(this, _TransactionController_instances, "m", _TransactionController_updateTransactionInternal).bind(this),
345
350
  });
346
351
  }
347
352
  /**
@@ -365,10 +370,12 @@ class TransactionController extends base_controller_1.BaseController {
365
370
  * @param txParams - Standard parameters for an Ethereum transaction.
366
371
  * @param options - Additional options to control how the transaction is added.
367
372
  * @param options.actionId - Unique ID to prevent duplicate requests.
373
+ * @param options.batchId - ID of the batch this transaction belongs to.
368
374
  * @param options.deviceConfirmedOn - An enum to indicate what device confirmed the transaction.
369
375
  * @param options.method - RPC method that requested the transaction.
370
376
  * @param options.nestedTransactions - Params for any nested transactions encoded in the data.
371
377
  * @param options.origin - The origin of the transaction request, such as a dApp hostname.
378
+ * @param options.publishHook - Custom logic to publish the transaction.
372
379
  * @param options.requireApproval - Whether the transaction requires approval by the user, defaults to true unless explicitly disabled.
373
380
  * @param options.securityAlertResponse - Response from security validator.
374
381
  * @param options.sendFlowHistory - The sendFlowHistory entries to add.
@@ -382,7 +389,7 @@ class TransactionController extends base_controller_1.BaseController {
382
389
  */
383
390
  async addTransaction(txParams, options) {
384
391
  (0, logger_1.projectLogger)('Adding transaction', txParams, options);
385
- const { actionId, deviceConfirmedOn, method, nestedTransactions, networkClientId, origin, requireApproval, securityAlertResponse, sendFlowHistory, swaps = {}, traceContext, type, } = options;
392
+ const { actionId, batchId, deviceConfirmedOn, method, nestedTransactions, networkClientId, origin, publishHook, requireApproval, securityAlertResponse, sendFlowHistory, swaps = {}, traceContext, type, } = options;
386
393
  txParams = (0, utils_2.normalizeTransactionParams)(txParams);
387
394
  if (!__classPrivateFieldGet(this, _TransactionController_multichainTrackingHelper, "f").has(networkClientId)) {
388
395
  throw new Error(`Network client not found - ${networkClientId}`);
@@ -416,6 +423,7 @@ class TransactionController extends base_controller_1.BaseController {
416
423
  : {
417
424
  // Add actionId to txMeta to check if same actionId is seen again
418
425
  actionId,
426
+ batchId,
419
427
  chainId,
420
428
  dappSuggestedGasFees,
421
429
  deviceConfirmedOn,
@@ -476,9 +484,10 @@ class TransactionController extends base_controller_1.BaseController {
476
484
  }
477
485
  return {
478
486
  result: this.processApproval(addedTransactionMeta, {
487
+ actionId,
479
488
  isExisting: Boolean(existingTransactionMeta),
489
+ publishHook,
480
490
  requireApproval,
481
- actionId,
482
491
  traceContext,
483
492
  }),
484
493
  transactionMeta: addedTransactionMeta,
@@ -1122,6 +1131,22 @@ class TransactionController extends base_controller_1.BaseController {
1122
1131
  abortCallback();
1123
1132
  this.signAbortCallbacks.delete(transactionId);
1124
1133
  }
1134
+ /**
1135
+ * Update the batch transactions associated with a transaction.
1136
+ * These transactions will be submitted with the main transaction as a batch.
1137
+ *
1138
+ * @param request - The request object.
1139
+ * @param request.transactionId - The ID of the transaction to update.
1140
+ * @param request.batchTransactions - The new batch transactions.
1141
+ */
1142
+ updateBatchTransactions({ transactionId, batchTransactions, }) {
1143
+ __classPrivateFieldGet(this, _TransactionController_instances, "m", _TransactionController_updateTransactionInternal).call(this, {
1144
+ transactionId,
1145
+ note: 'TransactionController#updateBatchTransactions - Batch transactions updated',
1146
+ }, (transactionMeta) => {
1147
+ transactionMeta.batchTransactions = batchTransactions;
1148
+ });
1149
+ }
1125
1150
  addMetadata(transactionMeta) {
1126
1151
  (0, validation_1.validateTxParams)(transactionMeta.txParams);
1127
1152
  this.update((state) => {
@@ -1171,7 +1196,7 @@ class TransactionController extends base_controller_1.BaseController {
1171
1196
  this.failTransaction(transactionMeta, new Error('Transaction incomplete at startup'));
1172
1197
  }
1173
1198
  }
1174
- async processApproval(transactionMeta, { isExisting = false, requireApproval, shouldShowRequest = true, actionId, traceContext, }) {
1199
+ async processApproval(transactionMeta, { actionId, isExisting = false, publishHook, requireApproval, shouldShowRequest = true, traceContext, }) {
1175
1200
  const transactionId = transactionMeta.id;
1176
1201
  let resultCallbacks;
1177
1202
  const { meta, isCompleted } = this.isTransactionCompleted(transactionId);
@@ -1198,7 +1223,7 @@ class TransactionController extends base_controller_1.BaseController {
1198
1223
  }
1199
1224
  const { isCompleted: isTxCompleted } = this.isTransactionCompleted(transactionId);
1200
1225
  if (!isTxCompleted) {
1201
- const approvalResult = await this.approveTransaction(transactionId, traceContext);
1226
+ const approvalResult = await this.approveTransaction(transactionId, traceContext, publishHook);
1202
1227
  if (approvalResult === ApprovalState.SkippedViaBeforePublishHook &&
1203
1228
  resultCallbacks) {
1204
1229
  resultCallbacks.success();
@@ -1232,7 +1257,7 @@ class TransactionController extends base_controller_1.BaseController {
1232
1257
  switch (finalMeta?.status) {
1233
1258
  case types_1.TransactionStatus.failed:
1234
1259
  resultCallbacks?.error(finalMeta.error);
1235
- throw rpc_errors_1.rpcErrors.internal(finalMeta.error.message);
1260
+ throw rpc_errors_1.rpcErrors.internal(finalMeta.error.stack);
1236
1261
  case types_1.TransactionStatus.submitted:
1237
1262
  resultCallbacks?.success();
1238
1263
  return finalMeta.hash;
@@ -1250,10 +1275,12 @@ class TransactionController extends base_controller_1.BaseController {
1250
1275
  *
1251
1276
  * @param transactionId - The ID of the transaction to approve.
1252
1277
  * @param traceContext - The parent context for any new traces.
1278
+ * @param publishHookOverride - Custom logic to publish the transaction.
1279
+ * @returns The approval state of the transaction.
1253
1280
  */
1254
- async approveTransaction(transactionId, traceContext) {
1255
- const cleanupTasks = new Array();
1256
- cleanupTasks.push(await this.mutex.acquire());
1281
+ async approveTransaction(transactionId, traceContext, publishHookOverride) {
1282
+ let clearApprovingTransactionId;
1283
+ let clearNonceLock;
1257
1284
  let transactionMeta = this.getTransactionOrThrow(transactionId);
1258
1285
  try {
1259
1286
  if (!this.sign) {
@@ -1269,10 +1296,9 @@ class TransactionController extends base_controller_1.BaseController {
1269
1296
  return ApprovalState.NotApproved;
1270
1297
  }
1271
1298
  this.approvingTransactionIds.add(transactionId);
1272
- cleanupTasks.push(() => this.approvingTransactionIds.delete(transactionId));
1299
+ clearApprovingTransactionId = () => this.approvingTransactionIds.delete(transactionId);
1273
1300
  const [nonce, releaseNonce] = await (0, nonce_1.getNextNonce)(transactionMeta, (address) => __classPrivateFieldGet(this, _TransactionController_multichainTrackingHelper, "f").getNonceLock(address, transactionMeta.networkClientId));
1274
- // must set transaction to submitted/failed before releasing lock
1275
- releaseNonce && cleanupTasks.push(releaseNonce);
1301
+ clearNonceLock = releaseNonce;
1276
1302
  transactionMeta = __classPrivateFieldGet(this, _TransactionController_instances, "m", _TransactionController_updateTransactionInternal).call(this, {
1277
1303
  transactionId,
1278
1304
  note: 'TransactionController#approveTransaction - Transaction approved',
@@ -1309,8 +1335,18 @@ class TransactionController extends base_controller_1.BaseController {
1309
1335
  }
1310
1336
  (0, logger_1.projectLogger)('Publishing transaction', transactionMeta.txParams);
1311
1337
  let hash;
1338
+ clearNonceLock?.();
1339
+ clearNonceLock = undefined;
1340
+ if (transactionMeta.batchTransactions?.length) {
1341
+ const extraTransactionsPublishHook = new ExtraTransactionsPublishHook_1.ExtraTransactionsPublishHook({
1342
+ addTransactionBatch: this.addTransactionBatch.bind(this),
1343
+ transactions: transactionMeta.batchTransactions,
1344
+ });
1345
+ publishHookOverride = extraTransactionsPublishHook.getHook();
1346
+ }
1312
1347
  await __classPrivateFieldGet(this, _TransactionController_trace, "f").call(this, { name: 'Publish', parentContext: traceContext }, async () => {
1313
- ({ transactionHash: hash } = await this.publish(transactionMeta, rawTx));
1348
+ const publishHook = publishHookOverride ?? this.publish;
1349
+ ({ transactionHash: hash } = await publishHook(transactionMeta, rawTx));
1314
1350
  if (hash === undefined) {
1315
1351
  hash = await this.publishTransaction(ethQuery, {
1316
1352
  ...transactionMeta,
@@ -1346,7 +1382,8 @@ class TransactionController extends base_controller_1.BaseController {
1346
1382
  return ApprovalState.NotApproved;
1347
1383
  }
1348
1384
  finally {
1349
- cleanupTasks.forEach((task) => task());
1385
+ clearApprovingTransactionId?.();
1386
+ clearNonceLock?.();
1350
1387
  }
1351
1388
  }
1352
1389
  async publishTransaction(ethQuery, transactionMeta, { skipSubmitHistory } = {}) {
@@ -1763,7 +1800,7 @@ class TransactionController extends base_controller_1.BaseController {
1763
1800
  }
1764
1801
  }
1765
1802
  exports.TransactionController = TransactionController;
1766
- _TransactionController_internalEvents = new WeakMap(), _TransactionController_methodDataHelper = new WeakMap(), _TransactionController_incomingTransactionChainIds = new WeakMap(), _TransactionController_incomingTransactionHelper = new WeakMap(), _TransactionController_incomingTransactionOptions = new WeakMap(), _TransactionController_pendingTransactionOptions = new WeakMap(), _TransactionController_trace = new WeakMap(), _TransactionController_transactionHistoryLimit = new WeakMap(), _TransactionController_isFirstTimeInteractionEnabled = new WeakMap(), _TransactionController_isSimulationEnabled = new WeakMap(), _TransactionController_testGasFeeFlows = new WeakMap(), _TransactionController_multichainTrackingHelper = new WeakMap(), _TransactionController_checkForPendingTransactionAndStartPolling = new WeakMap(), _TransactionController_instances = new WeakSet(), _TransactionController_retryTransaction = async function _TransactionController_retryTransaction({ actionId, afterSubmit, estimatedBaseFee, gasValues, label, prepareTransactionParams, rate, transactionId, transactionType, }) {
1803
+ _TransactionController_internalEvents = new WeakMap(), _TransactionController_methodDataHelper = new WeakMap(), _TransactionController_incomingTransactionChainIds = new WeakMap(), _TransactionController_incomingTransactionHelper = new WeakMap(), _TransactionController_incomingTransactionOptions = new WeakMap(), _TransactionController_pendingTransactionOptions = new WeakMap(), _TransactionController_publishBatchHook = new WeakMap(), _TransactionController_trace = new WeakMap(), _TransactionController_transactionHistoryLimit = new WeakMap(), _TransactionController_isFirstTimeInteractionEnabled = new WeakMap(), _TransactionController_isSimulationEnabled = new WeakMap(), _TransactionController_testGasFeeFlows = new WeakMap(), _TransactionController_multichainTrackingHelper = new WeakMap(), _TransactionController_checkForPendingTransactionAndStartPolling = new WeakMap(), _TransactionController_instances = new WeakSet(), _TransactionController_retryTransaction = async function _TransactionController_retryTransaction({ actionId, afterSubmit, estimatedBaseFee, gasValues, label, prepareTransactionParams, rate, transactionId, transactionType, }) {
1767
1804
  // If transaction is found for same action id, do not create a new transaction.
1768
1805
  if (this.getTransactionWithActionId(actionId)) {
1769
1806
  return;