Skip to content

feat(transaction-pay-controller): validate relay quotes#9143

Open
matthewwalsh0 wants to merge 23 commits into
mainfrom
feat/pay-quote-validation
Open

feat(transaction-pay-controller): validate relay quotes#9143
matthewwalsh0 wants to merge 23 commits into
mainfrom
feat/pay-quote-validation

Conversation

@matthewwalsh0

@matthewwalsh0 matthewwalsh0 commented Jun 16, 2026

Copy link
Copy Markdown
Member

Explanation

Transaction Pay surfaces Relay quotes without checking that their execution path is still executable, so clients can end up with quotes that only fail later at submission or simulation.

This PR validates Relay quotes while they are being fetched, before they are surfaced as executable. Each quote is converted into normalized execution transactions, then shared validation utilities check:

  • Source balance sufficiency
  • Decoded source-token transfer balance
  • Execution simulation (via @metamask/sentinel-api-service)

When validation fails, no executable quote is surfaced and TransactionData carries a structured quoteError: QuoteErrorInfo describing why. message/detail are display-ready copy and reason is a machine-readable discriminant ('simulation-failed' | 'insufficient-source-balance' | 'insufficient-transfer-balance' | 'balance-unavailable' | 'no-quotes') for analytics and for conditionally surfacing alerts in clients.

Simulation and validation live in generic utilities so other strategies can opt in, and the Relay execute-request building is unified into a single getRelayExecuteRequest helper shared by the validation and submit paths.

BREAKING: TransactionPayControllerMessenger now requires the SentinelApiService:simulateTransactions action. Consumers must include SentinelApiServiceActions in the AllowedActions type passed to the TransactionPayController messenger.

References

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

High Risk
Breaking messenger contract and changes when Relay quotes are shown; misconfigured Sentinel or validation could block pay flows, though validation defaults off via feature flag.

Overview
Adds pre-surface validation for Relay quotes: after fetch, quotes can be checked for live source balance, decoded ERC-20 transfer amounts, and Sentinel transaction simulation before clients treat them as executable. Validation is gated by payStrategies.relay.validationEnabled in confirmations_pay_extended (off by default as a kill switch).

When quote loading fails or yields no executable quotes, TransactionData.quoteError (QuoteErrorInfo with display copy plus machine-readable reason) is set; it clears when a new quote fetch starts. QuoteError / shared validateQuoteExecution and simulateQuoteTransactions are exported for reuse by other strategies.

Relay paths are refactored so validation and submit share getRelaySubmitCalls, buildRelaySubmitParams, and getRelayExecuteRequest (optional regenerateBatchParams for execute). Relay fetch errors are re-thrown without the previous Failed to fetch Relay quotes wrapper.

BREAKING: TransactionPayControllerMessenger must allow SentinelApiService:simulateTransactions (@metamask/sentinel-api-service dependency). Live balance RPC uses pending with latest fallback when pending is unsupported.

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

@matthewwalsh0
matthewwalsh0 force-pushed the feat/pay-quote-validation branch from 4c879f1 to c8d67d2 Compare June 16, 2026 11:29
@matthewwalsh0
matthewwalsh0 force-pushed the feat/pay-quote-validation branch 4 times, most recently from 8d2856d to 38a7453 Compare June 17, 2026 20:38
@matthewwalsh0
matthewwalsh0 force-pushed the feat/pay-quote-validation branch 2 times, most recently from 2d16a71 to 70adf3b Compare July 15, 2026 11:13
@socket-security

socket-security Bot commented Jul 15, 2026

Copy link
Copy Markdown

No dependency changes detected. Learn more about Socket for GitHub.

👍 No dependency changes detected in pull request

@matthewwalsh0
matthewwalsh0 force-pushed the feat/pay-quote-validation branch from 316de97 to 3f1ff46 Compare July 18, 2026 18:40
@matthewwalsh0

Copy link
Copy Markdown
Member Author

@metamaskbot publish-preview

@github-actions

Copy link
Copy Markdown
Contributor

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.5.5-preview-5cac90a
@metamask-previews/accounts-controller@39.0.5-preview-5cac90a
@metamask-previews/address-book-controller@7.1.2-preview-5cac90a
@metamask-previews/ai-controllers@0.8.0-preview-5cac90a
@metamask-previews/analytics-controller@1.2.1-preview-5cac90a
@metamask-previews/analytics-data-regulation-controller@0.0.0-preview-5cac90a
@metamask-previews/announcement-controller@8.1.0-preview-5cac90a
@metamask-previews/app-metadata-controller@2.0.1-preview-5cac90a
@metamask-previews/approval-controller@9.0.2-preview-5cac90a
@metamask-previews/assets-controller@11.0.0-preview-5cac90a
@metamask-previews/assets-controllers@109.4.1-preview-5cac90a
@metamask-previews/authenticated-user-storage@3.0.1-preview-5cac90a
@metamask-previews/base-controller@9.1.0-preview-5cac90a
@metamask-previews/base-data-service@0.1.3-preview-5cac90a
@metamask-previews/bitcoin-regtest-up@1.0.0-preview-5cac90a
@metamask-previews/bridge-controller@77.5.0-preview-5cac90a
@metamask-previews/bridge-status-controller@74.3.0-preview-5cac90a
@metamask-previews/build-utils@3.0.4-preview-5cac90a
@metamask-previews/chain-agnostic-permission@1.7.0-preview-5cac90a
@metamask-previews/chomp-api-service@3.1.0-preview-5cac90a
@metamask-previews/claims-controller@0.5.3-preview-5cac90a
@metamask-previews/client-controller@1.0.1-preview-5cac90a
@metamask-previews/client-utils@1.1.0-preview-5cac90a
@metamask-previews/compliance-controller@2.1.0-preview-5cac90a
@metamask-previews/composable-controller@12.0.1-preview-5cac90a
@metamask-previews/config-registry-controller@0.4.1-preview-5cac90a
@metamask-previews/connectivity-controller@0.3.0-preview-5cac90a
@metamask-previews/controller-utils@12.3.0-preview-5cac90a
@metamask-previews/core-backend@6.5.0-preview-5cac90a
@metamask-previews/delegation-controller@3.0.2-preview-5cac90a
@metamask-previews/earn-controller@12.2.2-preview-5cac90a
@metamask-previews/eip-5792-middleware@3.0.5-preview-5cac90a
@metamask-previews/eip-7702-internal-rpc-middleware@0.1.1-preview-5cac90a
@metamask-previews/eip1193-permission-middleware@2.0.1-preview-5cac90a
@metamask-previews/ens-controller@19.1.5-preview-5cac90a
@metamask-previews/eth-block-tracker@15.0.1-preview-5cac90a
@metamask-previews/eth-json-rpc-middleware@23.1.3-preview-5cac90a
@metamask-previews/eth-json-rpc-provider@6.0.1-preview-5cac90a
@metamask-previews/foundryup@1.0.1-preview-5cac90a
@metamask-previews/gas-fee-controller@26.2.4-preview-5cac90a
@metamask-previews/gator-permissions-controller@4.2.3-preview-5cac90a
@metamask-previews/geolocation-controller@0.1.3-preview-5cac90a
@metamask-previews/java-tron-up@1.0.0-preview-5cac90a
@metamask-previews/json-rpc-engine@10.5.0-preview-5cac90a
@metamask-previews/json-rpc-middleware-stream@8.0.8-preview-5cac90a
@metamask-previews/keyring-controller@27.1.0-preview-5cac90a
@metamask-previews/local-node-utils@1.0.0-preview-5cac90a
@metamask-previews/logging-controller@8.0.2-preview-5cac90a
@metamask-previews/message-manager@14.1.2-preview-5cac90a
@metamask-previews/messenger@2.0.0-preview-5cac90a
@metamask-previews/messenger-cli@0.2.0-preview-5cac90a
@metamask-previews/money-account-api-data-service@0.1.0-preview-5cac90a
@metamask-previews/money-account-balance-service@2.2.0-preview-5cac90a
@metamask-previews/money-account-controller@0.3.3-preview-5cac90a
@metamask-previews/money-account-upgrade-controller@2.2.1-preview-5cac90a
@metamask-previews/multichain-account-service@13.0.0-preview-5cac90a
@metamask-previews/multichain-api-middleware@4.0.1-preview-5cac90a
@metamask-previews/multichain-network-controller@3.2.1-preview-5cac90a
@metamask-previews/multichain-transactions-controller@7.1.1-preview-5cac90a
@metamask-previews/name-controller@9.1.2-preview-5cac90a
@metamask-previews/network-connection-banner-controller@0.1.0-preview-5cac90a
@metamask-previews/network-controller@34.0.0-preview-5cac90a
@metamask-previews/network-enablement-controller@5.6.0-preview-5cac90a
@metamask-previews/notification-services-controller@25.0.0-preview-5cac90a
@metamask-previews/passkey-controller@2.1.0-preview-5cac90a
@metamask-previews/permission-controller@13.1.1-preview-5cac90a
@metamask-previews/permission-log-controller@5.1.0-preview-5cac90a
@metamask-previews/perps-controller@9.2.1-preview-5cac90a
@metamask-previews/phishing-controller@17.3.0-preview-5cac90a
@metamask-previews/platform-api-docs@0.0.0-preview-5cac90a
@metamask-previews/polling-controller@16.0.8-preview-5cac90a
@metamask-previews/preferences-controller@23.1.0-preview-5cac90a
@metamask-previews/profile-metrics-controller@4.0.2-preview-5cac90a
@metamask-previews/profile-sync-controller@28.3.0-preview-5cac90a
@metamask-previews/ramps-controller@17.0.0-preview-5cac90a
@metamask-previews/rate-limit-controller@7.0.1-preview-5cac90a
@metamask-previews/react-data-query@0.2.2-preview-5cac90a
@metamask-previews/remote-feature-flag-controller@4.2.2-preview-5cac90a
@metamask-previews/sample-controllers@5.0.3-preview-5cac90a
@metamask-previews/seedless-onboarding-controller@10.0.3-preview-5cac90a
@metamask-previews/selected-network-controller@26.1.5-preview-5cac90a
@metamask-previews/sentinel-api-service@1.0.0-preview-5cac90a
@metamask-previews/shield-controller@5.1.3-preview-5cac90a
@metamask-previews/signature-controller@39.2.7-preview-5cac90a
@metamask-previews/smart-transactions-controller@25.0.1-preview-5cac90a
@metamask-previews/snap-account-service@2.0.0-preview-5cac90a
@metamask-previews/social-controllers@2.5.0-preview-5cac90a
@metamask-previews/solana-test-validator-up@1.0.0-preview-5cac90a
@metamask-previews/stellar-quickstart-up@0.0.0-preview-5cac90a
@metamask-previews/storage-service@1.0.2-preview-5cac90a
@metamask-previews/subscription-controller@6.2.1-preview-5cac90a
@metamask-previews/transaction-controller@69.0.0-preview-5cac90a
@metamask-previews/transaction-pay-controller@25.0.0-preview-5cac90a
@metamask-previews/user-operation-controller@41.2.7-preview-5cac90a
@metamask-previews/wallet@7.0.1-preview-5cac90a
@metamask-previews/wallet-cli@0.0.0-preview-5cac90a

@matthewwalsh0 matthewwalsh0 changed the title feat(transaction-pay-controller): validate Relay quotes feat(transaction-pay-controller): validate relay quotes Jul 19, 2026
@matthewwalsh0
matthewwalsh0 marked this pull request as ready for review July 20, 2026 12:53
@matthewwalsh0
matthewwalsh0 requested review from a team as code owners July 20, 2026 12:53
Comment thread packages/transaction-pay-controller/src/utils/validation.ts
@matthewwalsh0
matthewwalsh0 requested a review from jpuri July 21, 2026 11:00
jpuri
jpuri previously approved these changes Jul 21, 2026
…with human-formatted details

- Change QuoteValidationError from flat string to structured QuoteValidationErrorInfo
  { message, reason, detail? } across types, validation, relay-validation, quotes
- Add formatBalanceShortfall/formatTokenAmount helpers: shift by source token
  decimals via getTokenInfo, append symbol (e.g. 'Required: 1.5 USDC'),
  raw atomic fallback when token info unavailable
- Add 'no-quotes' QuoteValidationReason; capture strategy getQuotes() errors
  as validationError so genuine no-route failures surface in the UI
- Rename simulation error message to 'Quote simulation failed'
- Export QuoteValidationErrorInfo and QuoteValidationReason from index
- 1269 tests pass, 100% coverage, ESLint clean
…coder in relay-submit

Remove the dependency on generateEIP7702BatchTransaction exported from
transaction-controller. Restore the local buildEip7702BatchTransaction
function and ERC-7821 constants/interface so transaction-controller
needs no changes for this PR.
…ith SentinelApiService messenger action

- Add @metamask-previews/sentinel-api-service dependency
- Rewrite simulation.ts to call SentinelApiService:simulateTransactions via messenger
- Add SentinelApiServiceActions to TransactionPayControllerMessenger AllowedActions
- Delete sentinel.ts and sentinel.test.ts (local client removed)
- Rewrite simulation.test.ts and relay-validation.test.ts to use messenger mock
- Add BREAKING changelog entry for new messenger action requirement
- Re-export generateEIP7702BatchTransaction from transaction-controller (rebase fix)
…validation

- Revert TC CHANGELOG diff (no changes to transaction-controller source)
- Revert PayStrategyQuoteSupportResult: checkQuoteSupport returns boolean
- Slim relay-submit.ts: remove manual EIP-7702 encoder, un-export helpers
- Simplify simulation.ts: trust Sentinel transaction.error directly
- Fix exported function ordering (exported before private helpers)
- Delete relay-validation.ts: inline validateRelayQuotes into relay-quotes.ts
- Rename QuoteValidation* -> Quote* (QuoteError, QuoteErrorInfo, QuoteErrorReason,
  isQuoteError, QuoteSimulation, QuoteExecutionRequest)
- Add validation.test.ts with 100% coverage of all validation branches
…e balances

- Log the selected simulation type (execute / 7702-batch / normal) in relay validation.
- getLiveTokenBalance now falls back to the 'latest' block when the 'pending' query throws, with a warning log, for both native and ERC-20 paths.

@cursor cursor Bot left a comment

Copy link
Copy Markdown

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 1e8a1f7. Configure here.

@matthewwalsh0
matthewwalsh0 requested a review from jpuri July 21, 2026 15:29
@matthewwalsh0
matthewwalsh0 enabled auto-merge July 21, 2026 16:39
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants