@metamask/bridge-status-controller 68.1.0 → 69.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.
Files changed (60) hide show
  1. package/CHANGELOG.md +22 -1
  2. package/dist/bridge-status-controller.cjs +50 -33
  3. package/dist/bridge-status-controller.cjs.map +1 -1
  4. package/dist/bridge-status-controller.d.cts.map +1 -1
  5. package/dist/bridge-status-controller.d.mts.map +1 -1
  6. package/dist/bridge-status-controller.intent.cjs +3 -6
  7. package/dist/bridge-status-controller.intent.cjs.map +1 -1
  8. package/dist/bridge-status-controller.intent.d.cts +4 -3
  9. package/dist/bridge-status-controller.intent.d.cts.map +1 -1
  10. package/dist/bridge-status-controller.intent.d.mts +4 -3
  11. package/dist/bridge-status-controller.intent.d.mts.map +1 -1
  12. package/dist/bridge-status-controller.intent.mjs +4 -7
  13. package/dist/bridge-status-controller.intent.mjs.map +1 -1
  14. package/dist/bridge-status-controller.mjs +50 -33
  15. package/dist/bridge-status-controller.mjs.map +1 -1
  16. package/dist/types.cjs.map +1 -1
  17. package/dist/types.d.cts +2 -2
  18. package/dist/types.d.cts.map +1 -1
  19. package/dist/types.d.mts +2 -2
  20. package/dist/types.d.mts.map +1 -1
  21. package/dist/types.mjs.map +1 -1
  22. package/dist/utils/bridge-status.cjs +2 -4
  23. package/dist/utils/bridge-status.cjs.map +1 -1
  24. package/dist/utils/bridge-status.d.cts.map +1 -1
  25. package/dist/utils/bridge-status.d.mts.map +1 -1
  26. package/dist/utils/bridge-status.mjs +2 -4
  27. package/dist/utils/bridge-status.mjs.map +1 -1
  28. package/dist/utils/gas.cjs +2 -2
  29. package/dist/utils/gas.cjs.map +1 -1
  30. package/dist/utils/gas.d.cts +1 -1
  31. package/dist/utils/gas.d.cts.map +1 -1
  32. package/dist/utils/gas.d.mts +1 -1
  33. package/dist/utils/gas.d.mts.map +1 -1
  34. package/dist/utils/gas.mjs +2 -2
  35. package/dist/utils/gas.mjs.map +1 -1
  36. package/dist/utils/intent-api.cjs +2 -2
  37. package/dist/utils/intent-api.cjs.map +1 -1
  38. package/dist/utils/intent-api.d.cts +8 -7
  39. package/dist/utils/intent-api.d.cts.map +1 -1
  40. package/dist/utils/intent-api.d.mts +8 -7
  41. package/dist/utils/intent-api.d.mts.map +1 -1
  42. package/dist/utils/intent-api.mjs +4 -4
  43. package/dist/utils/intent-api.mjs.map +1 -1
  44. package/dist/utils/transaction.cjs +23 -14
  45. package/dist/utils/transaction.cjs.map +1 -1
  46. package/dist/utils/transaction.d.cts +4 -2
  47. package/dist/utils/transaction.d.cts.map +1 -1
  48. package/dist/utils/transaction.d.mts +4 -2
  49. package/dist/utils/transaction.d.mts.map +1 -1
  50. package/dist/utils/transaction.mjs +23 -14
  51. package/dist/utils/transaction.mjs.map +1 -1
  52. package/dist/utils/validators.cjs +5 -5
  53. package/dist/utils/validators.cjs.map +1 -1
  54. package/dist/utils/validators.d.cts +5 -10
  55. package/dist/utils/validators.d.cts.map +1 -1
  56. package/dist/utils/validators.d.mts +5 -10
  57. package/dist/utils/validators.d.mts.map +1 -1
  58. package/dist/utils/validators.mjs +3 -3
  59. package/dist/utils/validators.mjs.map +1 -1
  60. package/package.json +4 -4
package/CHANGELOG.md CHANGED
@@ -7,6 +7,26 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
7
7
 
8
8
  ## [Unreleased]
9
9
 
10
+ ## [69.0.0]
11
+
12
+ ### Added
13
+
14
+ - Added more unit test coverage for intents and EVM transactions. Also refactored some mocks and code blocks to improve testability ([#8186](https://github.com/MetaMask/core/pull/8186))
15
+
16
+ ### Changed
17
+
18
+ - Bump `@metamask/bridge-controller` from `^69.1.0` to `^69.1.1` ([#8225](https://github.com/MetaMask/core/pull/8225))
19
+ - Bump `@metamask/gas-fee-controller` from `^26.0.3` to `^26.1.0` ([#8225](https://github.com/MetaMask/core/pull/8225))
20
+ - **BREAKING:** `BridgeStatusControllerMessenger` must now allow `TransactionController:isAtomicBatchSupported` action ([#8125](https://github.com/MetaMask/core/pull/8125))
21
+ - Bump `@metamask/transaction-controller` from `^62.21.0` to `^63.0.0` ([#8217](https://github.com/MetaMask/core/pull/8217), [#8225](https://github.com/MetaMask/core/pull/8225))
22
+
23
+ ### Fixed
24
+
25
+ - Delegated accounts (EIP-7702) now use batched transactions to avoid in-flight transaction limit ([#8125](https://github.com/MetaMask/core/pull/8125))
26
+ - Bridge transaction types now properly matched in 7702 batch path ([#8125](https://github.com/MetaMask/core/pull/8125))
27
+ - Delegated account batch transactions now recorded in bridge status history ([#8125](https://github.com/MetaMask/core/pull/8125))
28
+ - Gas fields now included for delegated account transactions that are not gas-sponsored ([#8125](https://github.com/MetaMask/core/pull/8125))
29
+
10
30
  ## [68.1.0]
11
31
 
12
32
  ### Added
@@ -1020,7 +1040,8 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
1020
1040
 
1021
1041
  - Initial release ([#5317](https://github.com/MetaMask/core/pull/5317))
1022
1042
 
1023
- [Unreleased]: https://github.com/MetaMask/core/compare/@metamask/bridge-status-controller@68.1.0...HEAD
1043
+ [Unreleased]: https://github.com/MetaMask/core/compare/@metamask/bridge-status-controller@69.0.0...HEAD
1044
+ [69.0.0]: https://github.com/MetaMask/core/compare/@metamask/bridge-status-controller@68.1.0...@metamask/bridge-status-controller@69.0.0
1024
1045
  [68.1.0]: https://github.com/MetaMask/core/compare/@metamask/bridge-status-controller@68.0.2...@metamask/bridge-status-controller@68.1.0
1025
1046
  [68.0.2]: https://github.com/MetaMask/core/compare/@metamask/bridge-status-controller@68.0.1...@metamask/bridge-status-controller@68.0.2
1026
1047
  [68.0.1]: https://github.com/MetaMask/core/compare/@metamask/bridge-status-controller@68.0.0...@metamask/bridge-status-controller@68.0.1
@@ -246,7 +246,7 @@ class BridgeStatusController extends (0, polling_controller_1.StaticIntervalPoll
246
246
  },
247
247
  hasApprovalTx: Boolean(quoteResponse.approval),
248
248
  approvalTxId,
249
- isStxEnabled: isStxEnabled ?? false,
249
+ isStxEnabled: Boolean(isStxEnabled),
250
250
  featureId: quoteResponse.featureId,
251
251
  location,
252
252
  ...(abTests && { abTests }),
@@ -382,9 +382,8 @@ class BridgeStatusController extends (0, polling_controller_1.StaticIntervalPoll
382
382
  try {
383
383
  let status;
384
384
  let validationFailures = [];
385
- const isIntent = Boolean(historyItem.quote.intent);
386
- if (isIntent) {
387
- const intentTxStatus = await __classPrivateFieldGet(this, _BridgeStatusController_intentManager, "f").getIntentTransactionStatus(bridgeTxMetaId, historyItem, __classPrivateFieldGet(this, _BridgeStatusController_clientId, "f"));
385
+ if (historyItem.quote.intent) {
386
+ const intentTxStatus = await __classPrivateFieldGet(this, _BridgeStatusController_intentManager, "f").getIntentTransactionStatus(bridgeTxMetaId, historyItem.quote.srcChainId, historyItem.quote.intent.protocol, __classPrivateFieldGet(this, _BridgeStatusController_clientId, "f"), historyItem.status.srcChain.txHash);
388
387
  if (intentTxStatus?.bridgeStatus === null ||
389
388
  intentTxStatus?.bridgeStatus === undefined) {
390
389
  return;
@@ -429,7 +428,7 @@ class BridgeStatusController extends (0, polling_controller_1.StaticIntervalPoll
429
428
  this.update((state) => {
430
429
  state.txHistory[bridgeTxMetaId] = newBridgeHistoryItem;
431
430
  });
432
- if (isIntent) {
431
+ if (historyItem.quote.intent) {
433
432
  __classPrivateFieldGet(this, _BridgeStatusController_intentManager, "f").syncTransactionFromIntentStatus(bridgeTxMetaId, historyItem);
434
433
  }
435
434
  // 5. After effects
@@ -563,7 +562,7 @@ class BridgeStatusController extends (0, polling_controller_1.StaticIntervalPoll
563
562
  });
564
563
  });
565
564
  _BridgeStatusController_handleApprovalTx.set(this, async (isBridgeTx, srcChainId, approval, resetApproval, requireApproval) => {
566
- if (approval) {
565
+ if (approval && (0, bridge_controller_1.isEvmTxData)(approval)) {
567
566
  const approveTx = async () => {
568
567
  await __classPrivateFieldGet(this, _BridgeStatusController_handleUSDTAllowanceReset, "f").call(this, resetApproval);
569
568
  const approvalTxMeta = await __classPrivateFieldGet(this, _BridgeStatusController_handleEvmTransaction, "f").call(this, {
@@ -650,8 +649,8 @@ class BridgeStatusController extends (0, polling_controller_1.StaticIntervalPoll
650
649
  // Convert to hex since txFee values from the quote are decimal strings
651
650
  if (txFee) {
652
651
  return {
653
- maxFeePerGas: (0, controller_utils_1.toHex)(txFee.maxFeePerGas ?? 0),
654
- maxPriorityFeePerGas: (0, controller_utils_1.toHex)(txFee.maxPriorityFeePerGas ?? 0),
652
+ maxFeePerGas: (0, controller_utils_1.toHex)(txFee.maxFeePerGas),
653
+ maxPriorityFeePerGas: (0, controller_utils_1.toHex)(txFee.maxPriorityFeePerGas),
655
654
  gas: gas ? (0, controller_utils_1.toHex)(gas) : undefined,
656
655
  };
657
656
  }
@@ -735,6 +734,7 @@ class BridgeStatusController extends (0, polling_controller_1.StaticIntervalPoll
735
734
  __classPrivateFieldGet(this, _BridgeStatusController_trackUnifiedSwapBridgeEvent, "f").call(this, bridge_controller_1.UnifiedSwapBridgeEventName.Submitted, undefined, preConfirmationProperties);
736
735
  let txMeta;
737
736
  let approvalTxId;
737
+ let isDelegatedAccount = false;
738
738
  const startTime = Date.now();
739
739
  const isBridgeTx = (0, bridge_controller_1.isCrossChain)(quoteResponse.quote.srcChainId, quoteResponse.quote.destChainId);
740
740
  const isTronTx = (0, bridge_controller_1.isTronChainId)(quoteResponse.quote.srcChainId);
@@ -755,7 +755,9 @@ class BridgeStatusController extends (0, polling_controller_1.StaticIntervalPoll
755
755
  return quoteResponse.approval &&
756
756
  (0, bridge_controller_1.isTronTrade)(quoteResponse.approval)
757
757
  ? await __classPrivateFieldGet(this, _BridgeStatusController_handleNonEvmTx, "f").call(this, quoteResponse.approval, quoteResponse, selectedAccount)
758
- : undefined;
758
+ : /* c8 ignore start */
759
+ undefined;
760
+ /* c8 ignore end */
759
761
  }
760
762
  catch (error) {
761
763
  !quoteResponse.featureId &&
@@ -789,7 +791,7 @@ class BridgeStatusController extends (0, polling_controller_1.StaticIntervalPoll
789
791
  }
790
792
  catch (error) {
791
793
  !quoteResponse.featureId &&
792
- __classPrivateFieldGet(this, _BridgeStatusController_trackUnifiedSwapBridgeEvent, "f").call(this, bridge_controller_1.UnifiedSwapBridgeEventName.Failed, txMeta?.id, {
794
+ __classPrivateFieldGet(this, _BridgeStatusController_trackUnifiedSwapBridgeEvent, "f").call(this, bridge_controller_1.UnifiedSwapBridgeEventName.Failed, undefined, {
793
795
  error_message: error?.message,
794
796
  ...preConfirmationProperties,
795
797
  });
@@ -815,7 +817,25 @@ class BridgeStatusController extends (0, polling_controller_1.StaticIntervalPoll
815
817
  if (!(0, bridge_controller_1.isEvmTxData)(quoteResponse.trade)) {
816
818
  throw new Error('Failed to submit cross-chain swap transaction: trade is not an EVM transaction');
817
819
  }
818
- if (isStxEnabledOnClient || quoteResponse.quote.gasIncluded7702) {
820
+ // Check if the account is an EIP-7702 delegated account
821
+ // Delegated accounts only allow 1 in-flight tx, so approve + swap
822
+ // must be batched into a single transaction
823
+ const hexChainId = (0, bridge_controller_1.formatChainIdToHex)(quoteResponse.quote.srcChainId);
824
+ isDelegatedAccount = await (async () => {
825
+ try {
826
+ const atomicBatchSupport = await this.messenger.call('TransactionController:isAtomicBatchSupported', {
827
+ address: quoteResponse.trade.from,
828
+ chainIds: [hexChainId],
829
+ });
830
+ return atomicBatchSupport.some((entry) => entry.isSupported && entry.delegationAddress);
831
+ }
832
+ catch {
833
+ return false;
834
+ }
835
+ })();
836
+ if (isStxEnabledOnClient ||
837
+ quoteResponse.quote.gasIncluded7702 ||
838
+ isDelegatedAccount) {
819
839
  const { tradeMeta, approvalMeta } = await __classPrivateFieldGet(this, _BridgeStatusController_handleEvmTransactionBatch, "f").call(this, {
820
840
  isBridgeTx,
821
841
  resetApproval: quoteResponse.resetApproval,
@@ -825,6 +845,7 @@ class BridgeStatusController extends (0, polling_controller_1.StaticIntervalPoll
825
845
  trade: quoteResponse.trade,
826
846
  quoteResponse,
827
847
  requireApproval,
848
+ isDelegatedAccount,
828
849
  });
829
850
  approvalTxId = approvalMeta?.id;
830
851
  return tradeMeta;
@@ -877,7 +898,8 @@ class BridgeStatusController extends (0, polling_controller_1.StaticIntervalPoll
877
898
  // Only add history here for non-EVM and batch EVM transactions
878
899
  const isNonBatchEvm = !(0, bridge_controller_1.isNonEvmChainId)(quoteResponse.quote.srcChainId) &&
879
900
  !isStxEnabledOnClient &&
880
- !quoteResponse.quote.gasIncluded7702;
901
+ !quoteResponse.quote.gasIncluded7702 &&
902
+ !isDelegatedAccount;
881
903
  if (!isNonBatchEvm) {
882
904
  // Add swap or bridge tx to history
883
905
  __classPrivateFieldGet(this, _BridgeStatusController_addTxToHistory, "f").call(this, {
@@ -934,26 +956,21 @@ class BridgeStatusController extends (0, polling_controller_1.StaticIntervalPoll
934
956
  const intent = (0, transaction_1.getIntentFromQuote)(quoteResponse);
935
957
  // If backend provided an approval tx for this intent quote, submit it first (on-chain),
936
958
  // then proceed with off-chain intent submission.
937
- let approvalTxId;
938
- if (quoteResponse.approval) {
939
- const isBridgeTx = (0, bridge_controller_1.isCrossChain)(quoteResponse.quote.srcChainId, quoteResponse.quote.destChainId);
940
- // Handle approval silently for better UX in intent flows
941
- const approvalTxMeta = await __classPrivateFieldGet(this, _BridgeStatusController_handleApprovalTx, "f").call(this, isBridgeTx, quoteResponse.quote.srcChainId, quoteResponse.approval && (0, bridge_controller_1.isEvmTxData)(quoteResponse.approval)
942
- ? quoteResponse.approval
943
- : undefined, quoteResponse.resetApproval,
944
- /* requireApproval */ false);
945
- approvalTxId = approvalTxMeta?.id;
946
- if (approvalTxId) {
947
- await __classPrivateFieldGet(this, _BridgeStatusController_waitForTxConfirmation, "f").call(this, approvalTxId);
948
- }
959
+ const isBridgeTx = (0, bridge_controller_1.isCrossChain)(quoteResponse.quote.srcChainId, quoteResponse.quote.destChainId);
960
+ const requireApproval = isHardwareAccount && __classPrivateFieldGet(this, _BridgeStatusController_clientId, "f") === types_1.BridgeClientId.MOBILE;
961
+ // Handle approval silently for better UX in intent flows
962
+ const approvalTxMeta = await __classPrivateFieldGet(this, _BridgeStatusController_handleApprovalTx, "f").call(this, isBridgeTx, quoteResponse.quote.srcChainId, quoteResponse.approval, quoteResponse.resetApproval, requireApproval);
963
+ const approvalTxId = approvalTxMeta?.id;
964
+ if (approvalTxId) {
965
+ await __classPrivateFieldGet(this, _BridgeStatusController_waitForTxConfirmation, "f").call(this, approvalTxId);
949
966
  }
950
- const { srcChainId: chainId, requestId } = quoteResponse.quote;
967
+ const { srcChainId, requestId } = quoteResponse.quote;
951
968
  const signature = await this.messenger.call('KeyringController:signTypedMessage', {
952
969
  from: accountAddress,
953
970
  data: intent.typedData,
954
971
  }, keyring_controller_1.SignTypedDataVersion.V4);
955
972
  const submissionParams = {
956
- srcChainId: chainId.toString(),
973
+ srcChainId,
957
974
  quoteId: requestId,
958
975
  signature,
959
976
  order: intent.order,
@@ -963,16 +980,17 @@ class BridgeStatusController extends (0, polling_controller_1.StaticIntervalPoll
963
980
  const intentOrder = await __classPrivateFieldGet(this, _BridgeStatusController_intentManager, "f").submitIntent(submissionParams, __classPrivateFieldGet(this, _BridgeStatusController_clientId, "f"));
964
981
  const orderUid = intentOrder.id;
965
982
  // Determine transaction type: swap for same-chain, bridge for cross-chain
966
- const isCrossChainTx = (0, bridge_controller_1.isCrossChain)(quoteResponse.quote.srcChainId, quoteResponse.quote.destChainId);
967
- const transactionType = isCrossChainTx
968
- ? transaction_controller_1.TransactionType.bridge
969
- : transaction_controller_1.TransactionType.swap;
983
+ const transactionType = isBridgeTx
984
+ ? /* c8 ignore start */
985
+ transaction_controller_1.TransactionType.bridge
986
+ : /* c8 ignore end */
987
+ transaction_controller_1.TransactionType.swap;
970
988
  // Create actual transaction in Transaction Controller first
971
- const networkClientId = this.messenger.call('NetworkController:findNetworkClientIdByChainId', (0, bridge_controller_1.formatChainIdToHex)(chainId));
989
+ const networkClientId = this.messenger.call('NetworkController:findNetworkClientIdByChainId', (0, bridge_controller_1.formatChainIdToHex)(srcChainId));
972
990
  // This is a synthetic transaction whose purpose is to be able
973
991
  // to track the order status via the history
974
992
  const intentTransactionParams = {
975
- chainId: (0, bridge_controller_1.formatChainIdToHex)(chainId),
993
+ chainId: (0, bridge_controller_1.formatChainIdToHex)(srcChainId),
976
994
  from: accountAddress,
977
995
  to: intent.settlementContract ??
978
996
  '0x9008D19f58AAbd9eD0D60971565AA8510560ab41', // Default settlement contract
@@ -1002,7 +1020,6 @@ class BridgeStatusController extends (0, polling_controller_1.StaticIntervalPoll
1002
1020
  ...statusUpdatedTxMeta,
1003
1021
  isIntentTx: true,
1004
1022
  orderUid,
1005
- intentType: isCrossChainTx ? 'bridge' : 'swap',
1006
1023
  };
1007
1024
  // Record in bridge history with actual transaction metadata
1008
1025
  try {