Skip to content

fix: Fix hardware wallet MMPay on EIP-7702 chains by gating 7702 paths on account keyring capability#8388

Open
OGPoyraz wants to merge 44 commits intomainfrom
ogp/CONF-1151
Open

fix: Fix hardware wallet MMPay on EIP-7702 chains by gating 7702 paths on account keyring capability#8388
OGPoyraz wants to merge 44 commits intomainfrom
ogp/CONF-1151

Conversation

@OGPoyraz
Copy link
Copy Markdown
Member

@OGPoyraz OGPoyraz commented Apr 7, 2026

Explanation

Fix hardware wallet mUSD conversion on EIP-7702 chains by gating 7702 paths on account keyring capability, and fix batch transaction signing to prevent remaining hardware wallet prompts after a rejection.

Summary

@metamask/transaction-controller

  • BREAKING: Add KeyringControllerGetStateAction to AllowedActions — clients must add KeyringController:getState to the TransactionController messenger's allowed actions
  • Add doesAccountSupportEIP7702 in eip7702.ts — checks keyring type via KeyringController:getState, returns true only for HD Key Tree and Simple Key Pair keyrings, falls back to true when keyring is unresolved
  • Gate addTransactionBatch 7702 path on doesAccountSupportEIP7702 — hardware wallet accounts fall back to sequential/hook batch flow instead of attempting EIP-7702 batch signing
  • Sign batch transactions sequentially — each transaction waits for the previous signing to complete before calling addTransaction for the next, preventing remaining Ledger prompts from appearing after a rejection
  • Add CollectPublishHook.waitForSignedCount(count) to support sequential signing gating in the batch loop
  • Abort remaining in-flight transaction signings on batch failure via abortTransactionSigning

@metamask/transaction-pay-controller

  • BREAKING: Add KeyringControllerGetStateAction to AllowedActions — clients must add KeyringController:getState to the TransactionPayController messenger's allowed actions
  • Add accountSupports7702 utility in utils/7702.ts and KEYRING_TYPES_SUPPORTING_7702 constant in types.ts
  • Thread accountSupports7702 flag through PayStrategyGetQuotesRequest and PayStrategyExecuteRequest types
  • Gate relay quote useExecute flag on accountSupports7702 — ensures the relay API receives non-7702 requests for hardware wallets and returns quotes with proper individual gas limits
  • Pass accountSupports7702 from TransactionPayPublishHook into strategy execute() calls
  • Pass accountSupports7702 from fiat-quotes into relay-quotes
  • Re-estimate gas individually in estimateQuoteGasLimitsBatch when batch simulation returns a combined 7702 gas limit but account doesn't support 7702

References

  • Fixes CONF-1151

Checklist

  • I've updated the test suite for new or updated code as appropriate
  • I've updated documentation (JSDoc, Markdown, etc.) for new or updated code as appropriate
  • I've communicated my changes to consumers by updating changelogs for packages I've changed
  • I've introduced breaking changes in this PR and have prepared draft pull requests for clients and consumer packages to resolve them

Note

Medium Risk
Introduces breaking messenger permission changes (KeyringController:getState) and alters batch signing/7702 execution paths, which can affect transaction submission behavior especially for hardware wallets.

Overview
EIP-7702 support is now gated by keyring capability. TransactionController adds KeyringController:getState to AllowedActions, introduces doesAccountSupportEIP7702, and uses it to skip the 7702 batch path (and estimateGasBatch 7702 combined estimation) when the account’s keyring type is unsupported.

Batch signing is made more robust for hardware wallets. Hook-based batch processing now waits for each transaction to finish signing before proceeding, adds targeted waiting via CollectPublishHook.waitForSignedCount, and aborts remaining in-flight signings via abortTransactionSigning when a batch fails.

Transaction Pay flows now carry an explicit 7702 capability flag. TransactionPayController requires KeyringController:getState, adds accountSupports7702/KEYRING_TYPES_SUPPORTING_7702, threads accountSupports7702 through quote/execute request types, and gates relay/Aggregator 7702-specific behavior based on that flag; tests and changelogs are updated accordingly.

Reviewed by Cursor Bugbot for commit 46856b2. Bugbot is set up for automated code reviews on this repo. Configure here.

@OGPoyraz OGPoyraz changed the title Fix hardware wallet MMPay on EIP-7702 chains by gating 7702 paths on account keyring capability fix: Fix hardware wallet MMPay on EIP-7702 chains by gating 7702 paths on account keyring capability Apr 7, 2026
@OGPoyraz OGPoyraz marked this pull request as ready for review April 7, 2026 12:39
@OGPoyraz OGPoyraz requested review from a team as code owners April 7, 2026 12:39
@OGPoyraz
Copy link
Copy Markdown
Member Author

OGPoyraz commented Apr 7, 2026

@metamaskbot publish-preview

@github-actions
Copy link
Copy Markdown
Contributor

github-actions Bot commented Apr 7, 2026

Preview builds have been published. Learn how to use preview builds in other projects.

Expand for full list of packages and versions.
@metamask-previews/account-tree-controller@7.0.0-preview-3f6f27f
@metamask-previews/accounts-controller@37.2.0-preview-3f6f27f
@metamask-previews/address-book-controller@7.1.1-preview-3f6f27f
@metamask-previews/ai-controllers@0.6.3-preview-3f6f27f
@metamask-previews/analytics-controller@1.0.1-preview-3f6f27f
@metamask-previews/analytics-data-regulation-controller@0.0.0-preview-3f6f27f
@metamask-previews/announcement-controller@8.1.0-preview-3f6f27f
@metamask-previews/app-metadata-controller@2.0.1-preview-3f6f27f
@metamask-previews/approval-controller@9.0.1-preview-3f6f27f
@metamask-previews/assets-controller@4.0.0-preview-3f6f27f
@metamask-previews/assets-controllers@103.1.1-preview-3f6f27f
@metamask-previews/base-controller@9.0.1-preview-3f6f27f
@metamask-previews/base-data-service@0.1.1-preview-3f6f27f
@metamask-previews/bridge-controller@70.0.1-preview-3f6f27f
@metamask-previews/bridge-status-controller@70.0.5-preview-3f6f27f
@metamask-previews/build-utils@3.0.4-preview-3f6f27f
@metamask-previews/chain-agnostic-permission@1.5.0-preview-3f6f27f
@metamask-previews/claims-controller@0.5.0-preview-3f6f27f
@metamask-previews/client-controller@1.0.1-preview-3f6f27f
@metamask-previews/compliance-controller@2.0.0-preview-3f6f27f
@metamask-previews/composable-controller@12.0.1-preview-3f6f27f
@metamask-previews/config-registry-controller@0.2.0-preview-3f6f27f
@metamask-previews/connectivity-controller@0.2.0-preview-3f6f27f
@metamask-previews/controller-utils@11.20.0-preview-3f6f27f
@metamask-previews/core-backend@6.2.1-preview-3f6f27f
@metamask-previews/delegation-controller@2.1.0-preview-3f6f27f
@metamask-previews/earn-controller@11.2.1-preview-3f6f27f
@metamask-previews/eip-5792-middleware@3.0.3-preview-3f6f27f
@metamask-previews/eip-7702-internal-rpc-middleware@0.1.0-preview-3f6f27f
@metamask-previews/eip1193-permission-middleware@1.0.3-preview-3f6f27f
@metamask-previews/ens-controller@19.1.1-preview-3f6f27f
@metamask-previews/eth-block-tracker@15.0.1-preview-3f6f27f
@metamask-previews/eth-json-rpc-middleware@23.1.1-preview-3f6f27f
@metamask-previews/eth-json-rpc-provider@6.0.1-preview-3f6f27f
@metamask-previews/foundryup@1.0.1-preview-3f6f27f
@metamask-previews/gas-fee-controller@26.1.1-preview-3f6f27f
@metamask-previews/gator-permissions-controller@3.0.1-preview-3f6f27f
@metamask-previews/geolocation-controller@0.1.2-preview-3f6f27f
@metamask-previews/json-rpc-engine@10.2.4-preview-3f6f27f
@metamask-previews/json-rpc-middleware-stream@8.0.8-preview-3f6f27f
@metamask-previews/keyring-controller@25.2.0-preview-3f6f27f
@metamask-previews/logging-controller@8.0.1-preview-3f6f27f
@metamask-previews/message-manager@14.1.1-preview-3f6f27f
@metamask-previews/messenger@1.1.1-preview-3f6f27f
@metamask-previews/messenger-cli@0.1.0-preview-3f6f27f
@metamask-previews/money-account-controller@0.1.0-preview-3f6f27f
@metamask-previews/multichain-account-service@8.0.1-preview-3f6f27f
@metamask-previews/multichain-api-middleware@2.0.0-preview-3f6f27f
@metamask-previews/multichain-network-controller@3.0.6-preview-3f6f27f
@metamask-previews/multichain-transactions-controller@7.0.4-preview-3f6f27f
@metamask-previews/name-controller@9.1.1-preview-3f6f27f
@metamask-previews/network-controller@30.0.1-preview-3f6f27f
@metamask-previews/network-enablement-controller@5.0.2-preview-3f6f27f
@metamask-previews/notification-services-controller@23.0.1-preview-3f6f27f
@metamask-previews/permission-controller@12.3.0-preview-3f6f27f
@metamask-previews/permission-log-controller@5.1.0-preview-3f6f27f
@metamask-previews/perps-controller@2.0.0-preview-3f6f27f
@metamask-previews/phishing-controller@17.1.1-preview-3f6f27f
@metamask-previews/polling-controller@16.0.4-preview-3f6f27f
@metamask-previews/preferences-controller@23.1.0-preview-3f6f27f
@metamask-previews/profile-metrics-controller@3.1.3-preview-3f6f27f
@metamask-previews/profile-sync-controller@28.0.2-preview-3f6f27f
@metamask-previews/ramps-controller@13.0.0-preview-3f6f27f
@metamask-previews/rate-limit-controller@7.0.1-preview-3f6f27f
@metamask-previews/react-data-query@0.2.0-preview-3f6f27f
@metamask-previews/remote-feature-flag-controller@4.2.0-preview-3f6f27f
@metamask-previews/sample-controllers@4.0.4-preview-3f6f27f
@metamask-previews/seedless-onboarding-controller@9.1.0-preview-3f6f27f
@metamask-previews/selected-network-controller@26.1.0-preview-3f6f27f
@metamask-previews/shield-controller@5.1.1-preview-3f6f27f
@metamask-previews/signature-controller@39.1.2-preview-3f6f27f
@metamask-previews/social-controllers@0.1.0-preview-3f6f27f
@metamask-previews/storage-service@1.0.1-preview-3f6f27f
@metamask-previews/subscription-controller@6.1.2-preview-3f6f27f
@metamask-previews/transaction-controller@64.0.0-preview-3f6f27f
@metamask-previews/transaction-pay-controller@19.0.3-preview-3f6f27f
@metamask-previews/user-operation-controller@41.2.0-preview-3f6f27f

Comment thread packages/transaction-pay-controller/src/strategy/across/across-submit.ts Outdated
Comment thread packages/transaction-pay-controller/src/strategy/relay/relay-quotes.ts Outdated
@OGPoyraz
Copy link
Copy Markdown
Member Author

OGPoyraz commented Apr 8, 2026

@metamaskbot publish-preview

@github-actions
Copy link
Copy Markdown
Contributor

github-actions Bot commented Apr 8, 2026

Preview builds have been published. Learn how to use preview builds in other projects.

Expand for full list of packages and versions.
@metamask-previews/account-tree-controller@7.0.0-preview-bce8c098b
@metamask-previews/accounts-controller@37.2.0-preview-bce8c098b
@metamask-previews/address-book-controller@7.1.1-preview-bce8c098b
@metamask-previews/ai-controllers@0.6.3-preview-bce8c098b
@metamask-previews/analytics-controller@1.0.1-preview-bce8c098b
@metamask-previews/analytics-data-regulation-controller@0.0.0-preview-bce8c098b
@metamask-previews/announcement-controller@8.1.0-preview-bce8c098b
@metamask-previews/app-metadata-controller@2.0.1-preview-bce8c098b
@metamask-previews/approval-controller@9.0.1-preview-bce8c098b
@metamask-previews/assets-controller@4.0.0-preview-bce8c098b
@metamask-previews/assets-controllers@103.1.1-preview-bce8c098b
@metamask-previews/base-controller@9.0.1-preview-bce8c098b
@metamask-previews/base-data-service@0.1.1-preview-bce8c098b
@metamask-previews/bridge-controller@70.0.1-preview-bce8c098b
@metamask-previews/bridge-status-controller@70.0.5-preview-bce8c098b
@metamask-previews/build-utils@3.0.4-preview-bce8c098b
@metamask-previews/chain-agnostic-permission@1.5.0-preview-bce8c098b
@metamask-previews/claims-controller@0.5.0-preview-bce8c098b
@metamask-previews/client-controller@1.0.1-preview-bce8c098b
@metamask-previews/compliance-controller@2.0.0-preview-bce8c098b
@metamask-previews/composable-controller@12.0.1-preview-bce8c098b
@metamask-previews/config-registry-controller@0.2.0-preview-bce8c098b
@metamask-previews/connectivity-controller@0.2.0-preview-bce8c098b
@metamask-previews/controller-utils@11.20.0-preview-bce8c098b
@metamask-previews/core-backend@6.2.1-preview-bce8c098b
@metamask-previews/delegation-controller@2.1.0-preview-bce8c098b
@metamask-previews/earn-controller@11.2.1-preview-bce8c098b
@metamask-previews/eip-5792-middleware@3.0.3-preview-bce8c098b
@metamask-previews/eip-7702-internal-rpc-middleware@0.1.0-preview-bce8c098b
@metamask-previews/eip1193-permission-middleware@1.0.3-preview-bce8c098b
@metamask-previews/ens-controller@19.1.1-preview-bce8c098b
@metamask-previews/eth-block-tracker@15.0.1-preview-bce8c098b
@metamask-previews/eth-json-rpc-middleware@23.1.1-preview-bce8c098b
@metamask-previews/eth-json-rpc-provider@6.0.1-preview-bce8c098b
@metamask-previews/foundryup@1.0.1-preview-bce8c098b
@metamask-previews/gas-fee-controller@26.1.1-preview-bce8c098b
@metamask-previews/gator-permissions-controller@3.0.1-preview-bce8c098b
@metamask-previews/geolocation-controller@0.1.2-preview-bce8c098b
@metamask-previews/json-rpc-engine@10.2.4-preview-bce8c098b
@metamask-previews/json-rpc-middleware-stream@8.0.8-preview-bce8c098b
@metamask-previews/keyring-controller@25.2.0-preview-bce8c098b
@metamask-previews/logging-controller@8.0.1-preview-bce8c098b
@metamask-previews/message-manager@14.1.1-preview-bce8c098b
@metamask-previews/messenger@1.1.1-preview-bce8c098b
@metamask-previews/messenger-cli@0.1.0-preview-bce8c098b
@metamask-previews/money-account-controller@0.1.0-preview-bce8c098b
@metamask-previews/multichain-account-service@8.0.1-preview-bce8c098b
@metamask-previews/multichain-api-middleware@2.0.0-preview-bce8c098b
@metamask-previews/multichain-network-controller@3.0.6-preview-bce8c098b
@metamask-previews/multichain-transactions-controller@7.0.4-preview-bce8c098b
@metamask-previews/name-controller@9.1.1-preview-bce8c098b
@metamask-previews/network-controller@30.0.1-preview-bce8c098b
@metamask-previews/network-enablement-controller@5.0.2-preview-bce8c098b
@metamask-previews/notification-services-controller@23.0.1-preview-bce8c098b
@metamask-previews/permission-controller@12.3.0-preview-bce8c098b
@metamask-previews/permission-log-controller@5.1.0-preview-bce8c098b
@metamask-previews/perps-controller@2.0.0-preview-bce8c098b
@metamask-previews/phishing-controller@17.1.1-preview-bce8c098b
@metamask-previews/polling-controller@16.0.4-preview-bce8c098b
@metamask-previews/preferences-controller@23.1.0-preview-bce8c098b
@metamask-previews/profile-metrics-controller@3.1.3-preview-bce8c098b
@metamask-previews/profile-sync-controller@28.0.2-preview-bce8c098b
@metamask-previews/ramps-controller@13.0.0-preview-bce8c098b
@metamask-previews/rate-limit-controller@7.0.1-preview-bce8c098b
@metamask-previews/react-data-query@0.2.0-preview-bce8c098b
@metamask-previews/remote-feature-flag-controller@4.2.0-preview-bce8c098b
@metamask-previews/sample-controllers@4.0.4-preview-bce8c098b
@metamask-previews/seedless-onboarding-controller@9.1.0-preview-bce8c098b
@metamask-previews/selected-network-controller@26.1.0-preview-bce8c098b
@metamask-previews/shield-controller@5.1.1-preview-bce8c098b
@metamask-previews/signature-controller@39.1.2-preview-bce8c098b
@metamask-previews/social-controllers@0.1.0-preview-bce8c098b
@metamask-previews/storage-service@1.0.1-preview-bce8c098b
@metamask-previews/subscription-controller@6.1.2-preview-bce8c098b
@metamask-previews/transaction-controller@64.0.0-preview-bce8c098b
@metamask-previews/transaction-pay-controller@19.0.3-preview-bce8c098b
@metamask-previews/user-operation-controller@41.2.0-preview-bce8c098b

@OGPoyraz
Copy link
Copy Markdown
Member Author

OGPoyraz commented Apr 8, 2026

@metamaskbot publish-preview

@github-actions
Copy link
Copy Markdown
Contributor

github-actions Bot commented Apr 8, 2026

Preview builds have been published. Learn how to use preview builds in other projects.

Expand for full list of packages and versions.
@metamask-previews/account-tree-controller@7.0.0-preview-8fff4f2dc
@metamask-previews/accounts-controller@37.2.0-preview-8fff4f2dc
@metamask-previews/address-book-controller@7.1.1-preview-8fff4f2dc
@metamask-previews/ai-controllers@0.6.3-preview-8fff4f2dc
@metamask-previews/analytics-controller@1.0.1-preview-8fff4f2dc
@metamask-previews/analytics-data-regulation-controller@0.0.0-preview-8fff4f2dc
@metamask-previews/announcement-controller@8.1.0-preview-8fff4f2dc
@metamask-previews/app-metadata-controller@2.0.1-preview-8fff4f2dc
@metamask-previews/approval-controller@9.0.1-preview-8fff4f2dc
@metamask-previews/assets-controller@4.0.0-preview-8fff4f2dc
@metamask-previews/assets-controllers@103.1.1-preview-8fff4f2dc
@metamask-previews/base-controller@9.0.1-preview-8fff4f2dc
@metamask-previews/base-data-service@0.1.1-preview-8fff4f2dc
@metamask-previews/bridge-controller@70.0.1-preview-8fff4f2dc
@metamask-previews/bridge-status-controller@70.0.5-preview-8fff4f2dc
@metamask-previews/build-utils@3.0.4-preview-8fff4f2dc
@metamask-previews/chain-agnostic-permission@1.5.0-preview-8fff4f2dc
@metamask-previews/claims-controller@0.5.0-preview-8fff4f2dc
@metamask-previews/client-controller@1.0.1-preview-8fff4f2dc
@metamask-previews/compliance-controller@2.0.0-preview-8fff4f2dc
@metamask-previews/composable-controller@12.0.1-preview-8fff4f2dc
@metamask-previews/config-registry-controller@0.2.0-preview-8fff4f2dc
@metamask-previews/connectivity-controller@0.2.0-preview-8fff4f2dc
@metamask-previews/controller-utils@11.20.0-preview-8fff4f2dc
@metamask-previews/core-backend@6.2.1-preview-8fff4f2dc
@metamask-previews/delegation-controller@2.1.0-preview-8fff4f2dc
@metamask-previews/earn-controller@11.2.1-preview-8fff4f2dc
@metamask-previews/eip-5792-middleware@3.0.3-preview-8fff4f2dc
@metamask-previews/eip-7702-internal-rpc-middleware@0.1.0-preview-8fff4f2dc
@metamask-previews/eip1193-permission-middleware@1.0.3-preview-8fff4f2dc
@metamask-previews/ens-controller@19.1.1-preview-8fff4f2dc
@metamask-previews/eth-block-tracker@15.0.1-preview-8fff4f2dc
@metamask-previews/eth-json-rpc-middleware@23.1.1-preview-8fff4f2dc
@metamask-previews/eth-json-rpc-provider@6.0.1-preview-8fff4f2dc
@metamask-previews/foundryup@1.0.1-preview-8fff4f2dc
@metamask-previews/gas-fee-controller@26.1.1-preview-8fff4f2dc
@metamask-previews/gator-permissions-controller@3.0.1-preview-8fff4f2dc
@metamask-previews/geolocation-controller@0.1.2-preview-8fff4f2dc
@metamask-previews/json-rpc-engine@10.2.4-preview-8fff4f2dc
@metamask-previews/json-rpc-middleware-stream@8.0.8-preview-8fff4f2dc
@metamask-previews/keyring-controller@25.2.0-preview-8fff4f2dc
@metamask-previews/logging-controller@8.0.1-preview-8fff4f2dc
@metamask-previews/message-manager@14.1.1-preview-8fff4f2dc
@metamask-previews/messenger@1.1.1-preview-8fff4f2dc
@metamask-previews/messenger-cli@0.1.0-preview-8fff4f2dc
@metamask-previews/money-account-controller@0.1.0-preview-8fff4f2dc
@metamask-previews/multichain-account-service@8.0.1-preview-8fff4f2dc
@metamask-previews/multichain-api-middleware@2.0.0-preview-8fff4f2dc
@metamask-previews/multichain-network-controller@3.0.6-preview-8fff4f2dc
@metamask-previews/multichain-transactions-controller@7.0.4-preview-8fff4f2dc
@metamask-previews/name-controller@9.1.1-preview-8fff4f2dc
@metamask-previews/network-controller@30.0.1-preview-8fff4f2dc
@metamask-previews/network-enablement-controller@5.0.2-preview-8fff4f2dc
@metamask-previews/notification-services-controller@23.0.1-preview-8fff4f2dc
@metamask-previews/permission-controller@12.3.0-preview-8fff4f2dc
@metamask-previews/permission-log-controller@5.1.0-preview-8fff4f2dc
@metamask-previews/perps-controller@2.0.0-preview-8fff4f2dc
@metamask-previews/phishing-controller@17.1.1-preview-8fff4f2dc
@metamask-previews/polling-controller@16.0.4-preview-8fff4f2dc
@metamask-previews/preferences-controller@23.1.0-preview-8fff4f2dc
@metamask-previews/profile-metrics-controller@3.1.3-preview-8fff4f2dc
@metamask-previews/profile-sync-controller@28.0.2-preview-8fff4f2dc
@metamask-previews/ramps-controller@13.0.0-preview-8fff4f2dc
@metamask-previews/rate-limit-controller@7.0.1-preview-8fff4f2dc
@metamask-previews/react-data-query@0.2.0-preview-8fff4f2dc
@metamask-previews/remote-feature-flag-controller@4.2.0-preview-8fff4f2dc
@metamask-previews/sample-controllers@4.0.4-preview-8fff4f2dc
@metamask-previews/seedless-onboarding-controller@9.1.0-preview-8fff4f2dc
@metamask-previews/selected-network-controller@26.1.0-preview-8fff4f2dc
@metamask-previews/shield-controller@5.1.1-preview-8fff4f2dc
@metamask-previews/signature-controller@39.1.2-preview-8fff4f2dc
@metamask-previews/social-controllers@0.1.0-preview-8fff4f2dc
@metamask-previews/storage-service@1.0.1-preview-8fff4f2dc
@metamask-previews/subscription-controller@6.1.2-preview-8fff4f2dc
@metamask-previews/transaction-controller@64.0.0-preview-8fff4f2dc
@metamask-previews/transaction-pay-controller@19.0.3-preview-8fff4f2dc
@metamask-previews/user-operation-controller@41.2.0-preview-8fff4f2dc

Comment thread packages/transaction-pay-controller/src/strategy/relay/relay-submit.ts Outdated
Comment thread packages/transaction-pay-controller/src/strategy/across/across-submit.ts Outdated
@OGPoyraz
Copy link
Copy Markdown
Member Author

OGPoyraz commented Apr 8, 2026

@metamaskbot publish-preview

@github-actions
Copy link
Copy Markdown
Contributor

github-actions Bot commented Apr 8, 2026

Preview builds have been published. Learn how to use preview builds in other projects.

Expand for full list of packages and versions.
@metamask-previews/account-tree-controller@7.0.0-preview-19b4ad7e6
@metamask-previews/accounts-controller@37.2.0-preview-19b4ad7e6
@metamask-previews/address-book-controller@7.1.1-preview-19b4ad7e6
@metamask-previews/ai-controllers@0.6.3-preview-19b4ad7e6
@metamask-previews/analytics-controller@1.0.1-preview-19b4ad7e6
@metamask-previews/analytics-data-regulation-controller@0.0.0-preview-19b4ad7e6
@metamask-previews/announcement-controller@8.1.0-preview-19b4ad7e6
@metamask-previews/app-metadata-controller@2.0.1-preview-19b4ad7e6
@metamask-previews/approval-controller@9.0.1-preview-19b4ad7e6
@metamask-previews/assets-controller@5.0.0-preview-19b4ad7e6
@metamask-previews/assets-controllers@103.1.1-preview-19b4ad7e6
@metamask-previews/base-controller@9.0.1-preview-19b4ad7e6
@metamask-previews/base-data-service@0.1.1-preview-19b4ad7e6
@metamask-previews/bridge-controller@70.0.1-preview-19b4ad7e6
@metamask-previews/bridge-status-controller@70.0.5-preview-19b4ad7e6
@metamask-previews/build-utils@3.0.4-preview-19b4ad7e6
@metamask-previews/chain-agnostic-permission@1.5.0-preview-19b4ad7e6
@metamask-previews/claims-controller@0.5.0-preview-19b4ad7e6
@metamask-previews/client-controller@1.0.1-preview-19b4ad7e6
@metamask-previews/compliance-controller@2.0.0-preview-19b4ad7e6
@metamask-previews/composable-controller@12.0.1-preview-19b4ad7e6
@metamask-previews/config-registry-controller@0.2.0-preview-19b4ad7e6
@metamask-previews/connectivity-controller@0.2.0-preview-19b4ad7e6
@metamask-previews/controller-utils@11.20.0-preview-19b4ad7e6
@metamask-previews/core-backend@6.2.1-preview-19b4ad7e6
@metamask-previews/delegation-controller@2.1.0-preview-19b4ad7e6
@metamask-previews/earn-controller@11.2.1-preview-19b4ad7e6
@metamask-previews/eip-5792-middleware@3.0.3-preview-19b4ad7e6
@metamask-previews/eip-7702-internal-rpc-middleware@0.1.0-preview-19b4ad7e6
@metamask-previews/eip1193-permission-middleware@1.0.3-preview-19b4ad7e6
@metamask-previews/ens-controller@19.1.1-preview-19b4ad7e6
@metamask-previews/eth-block-tracker@15.0.1-preview-19b4ad7e6
@metamask-previews/eth-json-rpc-middleware@23.1.1-preview-19b4ad7e6
@metamask-previews/eth-json-rpc-provider@6.0.1-preview-19b4ad7e6
@metamask-previews/foundryup@1.0.1-preview-19b4ad7e6
@metamask-previews/gas-fee-controller@26.1.1-preview-19b4ad7e6
@metamask-previews/gator-permissions-controller@3.0.1-preview-19b4ad7e6
@metamask-previews/geolocation-controller@0.1.2-preview-19b4ad7e6
@metamask-previews/json-rpc-engine@10.2.4-preview-19b4ad7e6
@metamask-previews/json-rpc-middleware-stream@8.0.8-preview-19b4ad7e6
@metamask-previews/keyring-controller@25.2.0-preview-19b4ad7e6
@metamask-previews/logging-controller@8.0.1-preview-19b4ad7e6
@metamask-previews/message-manager@14.1.1-preview-19b4ad7e6
@metamask-previews/messenger@1.1.1-preview-19b4ad7e6
@metamask-previews/messenger-cli@0.1.0-preview-19b4ad7e6
@metamask-previews/money-account-controller@0.1.0-preview-19b4ad7e6
@metamask-previews/multichain-account-service@8.0.1-preview-19b4ad7e6
@metamask-previews/multichain-api-middleware@2.0.0-preview-19b4ad7e6
@metamask-previews/multichain-network-controller@3.0.6-preview-19b4ad7e6
@metamask-previews/multichain-transactions-controller@7.0.4-preview-19b4ad7e6
@metamask-previews/name-controller@9.1.1-preview-19b4ad7e6
@metamask-previews/network-controller@30.0.1-preview-19b4ad7e6
@metamask-previews/network-enablement-controller@5.0.2-preview-19b4ad7e6
@metamask-previews/notification-services-controller@23.0.1-preview-19b4ad7e6
@metamask-previews/permission-controller@12.3.0-preview-19b4ad7e6
@metamask-previews/permission-log-controller@5.1.0-preview-19b4ad7e6
@metamask-previews/perps-controller@2.0.0-preview-19b4ad7e6
@metamask-previews/phishing-controller@17.1.1-preview-19b4ad7e6
@metamask-previews/polling-controller@16.0.4-preview-19b4ad7e6
@metamask-previews/preferences-controller@23.1.0-preview-19b4ad7e6
@metamask-previews/profile-metrics-controller@3.1.3-preview-19b4ad7e6
@metamask-previews/profile-sync-controller@28.0.2-preview-19b4ad7e6
@metamask-previews/ramps-controller@13.0.0-preview-19b4ad7e6
@metamask-previews/rate-limit-controller@7.0.1-preview-19b4ad7e6
@metamask-previews/react-data-query@0.2.0-preview-19b4ad7e6
@metamask-previews/remote-feature-flag-controller@4.2.0-preview-19b4ad7e6
@metamask-previews/sample-controllers@4.0.4-preview-19b4ad7e6
@metamask-previews/seedless-onboarding-controller@9.1.0-preview-19b4ad7e6
@metamask-previews/selected-network-controller@26.1.0-preview-19b4ad7e6
@metamask-previews/shield-controller@5.1.1-preview-19b4ad7e6
@metamask-previews/signature-controller@39.1.2-preview-19b4ad7e6
@metamask-previews/social-controllers@0.1.0-preview-19b4ad7e6
@metamask-previews/storage-service@1.0.1-preview-19b4ad7e6
@metamask-previews/subscription-controller@6.1.2-preview-19b4ad7e6
@metamask-previews/transaction-controller@64.0.0-preview-19b4ad7e6
@metamask-previews/transaction-pay-controller@19.1.0-preview-19b4ad7e6
@metamask-previews/user-operation-controller@41.2.0-preview-19b4ad7e6

Comment thread packages/keyring-controller/src/KeyringController.ts Outdated
Comment thread packages/transaction-pay-controller/CHANGELOG.md Outdated
Comment thread packages/transaction-controller/src/TransactionController.ts Outdated
Comment thread packages/transaction-pay-controller/src/strategy/across/across-quotes.ts Outdated
Comment thread packages/transaction-pay-controller/src/strategy/across/across-submit.ts Outdated
Comment thread packages/transaction-pay-controller/src/strategy/across/across-submit.ts Outdated
Comment thread packages/transaction-pay-controller/src/strategy/relay/relay-quotes.ts Outdated
Comment thread packages/transaction-pay-controller/src/strategy/relay/relay-submit.ts Outdated
Comment thread packages/transaction-pay-controller/src/strategy/across/across-submit.ts Outdated
Comment thread packages/transaction-pay-controller/src/utils/quotes.ts Outdated
Comment thread packages/transaction-pay-controller/src/strategy/across/across-quotes.ts Outdated
Comment thread packages/transaction-pay-controller/src/helpers/TransactionPayPublishHook.ts Outdated
Comment thread packages/transaction-controller/CHANGELOG.md Outdated
Comment thread packages/transaction-pay-controller/src/strategy/relay/relay-submit.ts Outdated
Comment thread packages/transaction-pay-controller/src/strategy/relay/relay-submit.ts Outdated
Comment thread packages/transaction-pay-controller/src/strategy/relay/relay-submit.ts Outdated
Comment thread packages/transaction-pay-controller/src/strategy/relay/relay-quotes.ts Outdated
Comment thread packages/transaction-pay-controller/src/TransactionPayController.ts Outdated
Comment thread packages/transaction-controller/src/TransactionController.ts
Comment thread packages/transaction-pay-controller/CHANGELOG.md Outdated
Comment thread packages/transaction-pay-controller/src/utils/quote-gas.ts Outdated
Comment thread packages/transaction-pay-controller/src/utils/quotes.ts Outdated
OGPoyraz added 19 commits April 27, 2026 11:25
<!--
Thanks for your contribution! Take a moment to answer these questions so
that reviewers have the information they need to properly understand
your changes:

* What is the current state of things and why does it need to change?
* What is the solution your changes offer and how does it work?
* Are there any changes whose purpose might not obvious to those
unfamiliar with the domain?
* If your primary goal was to update one package but you found you had
to update another one along the way, why did you do so?
* If you had to upgrade a dependency, why did you do so?
-->

<!--
Are there any issues that this pull request is tied to?
Are there other links that reviewers should consult to understand these
changes better?
Are there client or consumer pull requests to adopt any breaking
changes?

For example:

* Fixes #12345
* Related to #67890
-->

- [ ] I've updated the test suite for new or updated code as appropriate
- [ ] I've updated documentation (JSDoc, Markdown, etc.) for new or
updated code as appropriate
- [ ] I've communicated my changes to consumers by [updating changelogs
for packages I've
changed](https://github.com/MetaMask/core/tree/main/docs/processes/updating-changelogs.md)
- [ ] I've introduced [breaking
changes](https://github.com/MetaMask/core/tree/main/docs/processes/breaking-changes.md)
in this PR and have prepared draft pull requests for clients and
consumer packages to resolve them

<!-- CURSOR_SUMMARY -->
---

> [!NOTE]
> <sup>[Cursor Bugbot](https://cursor.com/bugbot) is generating a
summary for commit c372c6d. Configure
[here](https://www.cursor.com/dashboard/bugbot).</sup>
<!-- /CURSOR_SUMMARY -->
…lly, preventing remaining hardware wallet prompts from appearing after a rejection
account: string,
): boolean {
const { keyrings } = messenger.call('KeyringController:getState');
const keyring = keyrings.find((k: { type: string; accounts: string[] }) =>
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Minor, could we return this with a some instead?

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Switched to some here. Also changed the fallback to return false when the keyring isn't found, since we'd rather be restrictive by default and not allow 7702 for accounts we can't resolve. Applied the same change to the pay controller duplicate.

}

const { transactionMeta } = await addTransaction(
const { transactionMeta, result: signingComplete } = await addTransaction(
Copy link
Copy Markdown
Member

@matthewwalsh0 matthewwalsh0 Apr 27, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is the goal here just to stop further signing if one of them is rejected at the signing stage? Meaning we ensure the whole thing (including any parent transaction via pay controller) fails?

If so, could we massively simplify by just awaiting the signing here so we ensure each one doesn't start until the previous is signed? Then no Promise.race nor changes to CollectPublishHook nor any need for abortTransactionSigning?

The only downside is probably a few quick gas requests for limits and fees that are no longer running in parallel, but the typical approval and swap case should always have gas limits anyway, and these are typically background processes anyway (usually 7702) so little impact.

We can't just await result since it won't resolve until the whole batch does.

But we could just make a small local function like waitForSigned(transactionId) that listens to the transactionStatusUpdated event and resolves if status is signed or throws if failed?

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is the goal here just to stop further signing if one of them is rejected at the signing stage?

Yes, the goal is to ensure transactions are signed sequentially so that hardware wallet prompts appear one at a time, and if a signing is rejected, the remaining transactions in the batch don't proceed (and remaining Ledger prompts don't appear).

Meaning we ensure the whole thing (including any parent transaction via pay controller) fails?

That's correct. If any signing fails, the error propagates out of the for loop into the catch block, which calls abortTransactionSigning for any remaining transactions and then calls collectHook.error() + resultCallbacks?.error(), failing the entire batch including the parent pay controller flow.

If so, could we massively simplify by just awaiting the signing here so we ensure each one doesn't start until the previous is signed? Then no Promise.race nor changes to CollectPublishHook nor any need for abortTransactionSigning?

I explored this approach and ran into a fundamental timing issue.

When addTransaction is called with requireApproval: false, it returns immediately - the signing chain (#processApproval > #approveTransaction > #signTransaction) runs as a detached async operation via the result promise. A waitForSigned helper that subscribes to transactionStatusUpdated after addTransaction returns has a race condition: the signed event can fire before the subscription is set up.

In practice this breaks the hardware wallet flow. The "Confirm on Ledger" modal never appears because the loop moves on before the signing flow has properly started - the event listener misses the status transitions entirely.

We can't subscribe before addTransaction either, because the transactionId isn't known until it returns.

The current CollectPublishHook.waitForSignedCount approach avoids this because the hook is passed into addTransaction and called from within the signing flow - it can't miss the event. The Promise.race with signingFailure handles the rejection path.

I think the current approach is the simplest one that's actually correct given the async topology here.

);

return keyring
? (KEYRING_TYPES_SUPPORTING_7702 as string[]).includes(keyring.type)
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Minor, this is obviously duplicated in transaction controller, but it lets pay have its own criteria also. Could expose via action in transaction controller but not urgent, so better a future PR when needed.

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I agree, this is a duplication for now since the pay controller uses its own messenger type. Happy to expose it via a TransactionController action in a follow-up PR.

// If the batch returned a combined 7702 gas limit but the account cannot
// sign EIP-7702 authorizations (e.g. hardware wallet), re-estimate each
// transaction individually so callers never see is7702=true.
const supports7702 = accountSupports7702(messenger, firstTransaction.from);
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is this all redundant if we check this inside estimateGasBatch in the transaction controller?

Since this too will be a limitation everywhere as with batch add?

We already check isAtomicBatchSupported for example, so would just check this too.

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Good call - moved the account support check into estimateGasBatch in the transaction controller so it applies everywhere. When the account doesn't support 7702, estimateGasBatch now skips the 7702 path entirely and falls through to per-transaction estimation. Removed the redundant check and the individual re-estimation fallback from the pay controller.

@OGPoyraz OGPoyraz requested a review from matthewwalsh0 April 27, 2026 13:33
Copy link
Copy Markdown

@cursor cursor Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Cursor Bugbot has reviewed your changes and found 1 potential issue.

Fix All in Cursor

❌ Bugbot Autofix is OFF. To automatically fix reported issues with cloud agents, have a team admin enable autofix in the Cursor dashboard.

Reviewed by Cursor Bugbot for commit 46856b2. Configure here.


const chainResult = accountSupports7702
? is7702Result.find((result) => result.chainId === chainId)
: undefined;
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Unnecessary RPC calls when account doesn't support 7702

Low Severity

estimateGasBatch unconditionally calls isAtomicBatchSupported (which performs on-chain RPC queries to check delegation status) before checking doesAccountSupportEIP7702. When the account doesn't support 7702 (e.g., hardware wallets), the entire is7702Result is discarded by setting chainResult to undefined. Moving the doesAccountSupportEIP7702 check before the isAtomicBatchSupported call would avoid unnecessary network round-trips for hardware wallet users.

Fix in Cursor Fix in Web

Reviewed by Cursor Bugbot for commit 46856b2. Configure here.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants