feat(transaction-pay-controller): add server pay strategy#8894
Conversation
e5c9ffc to
0c91233
Compare
e079367 to
fbd3a1b
Compare
…for non-gasless generic quotes The generic strategy now branches on the backend's gasless flag: gasless quotes still go through POST /submit; non-gasless quotes are submitted through TransactionController.addTransaction or addTransactionBatch, including EIP-7702 batching and gas-station support, mirroring the existing Relay strategy. Status is polled via /status on both paths until CONFIRMED. Source network fees are computed for non-gasless quotes; gasless quotes remain zeroed since the relayer covers gas.
…s in generic strategy Detects Hyperliquid perps deposits by sniffing the parent transaction calldata for the Hyperliquid bridge contract address (no transaction-type coupling) and rewrites the destination to a provider-agnostic HyperCore sentinel (`0x2100…`) with 8-decimal amount. Backend providers translate the sentinel to their own native HyperCore identifiers.
…ic quote requests Adds an optional `supportsGasless` field to `GenericQuoteRequest` and sets it in `buildGenericQuoteRequest` when: - the calling account supports EIP-7702, - Relay execute is enabled via the remote feature flag, and - the source chain is EIP-7702-capable. This mirrors the existing Relay strategy condition and lets backend providers opt into gasless execution paths (notably `originGasOverhead` on Relay) without coupling the client to any one provider's wire format.
… generic supportsGasless Removes the `isRelayExecuteEnabled` gate from `supportsGasless`. The flag is not yet rolled out in the relevant environments, but generic gasless flow should remain available whenever the account itself supports EIP-7702 on a 7702-capable chain. Backend providers remain the source of truth for whether they actually return a gasless quote.
…ntent confirms Mirrors the Relay and Across strategies: once the generic /status endpoint reports CONFIRMED, mark the parent transaction's `isIntentComplete` so downstream UI (activity list, confirmation cleanup) treats the pay flow as done. Previously the generic strategy returned the target hash but left `isIntentComplete` unset, causing the parent transaction to linger in an incomplete state.
… generic strategy - Set default intents API URL to prod (https://intents.api.cx.metamask.io) - Remove unnecessary Boolean() wrapper in buildGenericQuoteRequest - Use Array.at(-1) instead of slice(-1)[0] - Simplify URLSearchParams optional hash param - Rename datas to fragments in transactionDataReferencesBridge with clarifying comment - Simplify GENERIC_DEFAULT_PROVIDER_PRIORITY cast - Remove obfuscated string-concat mock accessors in generic-submit test - Trim changelog entry to consumer-facing surface area
…t quote time - Pass sourceHash from metamaskPay into getStatus call so M0 provider can match openTx - Estimate gas (maxFeePerGas, maxPriorityFeePerGas) at quote time via GasFeeController - Add gasLimits, is7702, maxFeePerGas, maxPriorityFeePerGas to ServerQuoteClient type - stepToParams uses step.maxFeePerGas with client estimate as fallback - calculateSourceNetworkCost uses gas estimate from quote for cost display
036f479 to
a2e42ba
Compare
There was a problem hiding this comment.
Cursor Bugbot has reviewed your changes and found 1 potential issue.
❌ 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 e34ddc9. Configure here.
| supports(request: PayStrategyGetQuotesRequest): boolean { | ||
| const config = getPayStrategiesConfig(request.messenger); | ||
| return config.server.enabled; | ||
| } |
There was a problem hiding this comment.
Just want to confirm we won't enable this strategy until we fully support the listed use-cases in the PR description right?
As we only gate with FF here, when this flag is eventually turned on, and Server becomes first in DEFAULT_STRATEGY_ORDER and will be attempted for every transaction - including the not implemented flows listed in the PR description.
The fallback loop only skips to the next strategy if getQuotes throws or returns empty. If the intents API happens to return a valid-looking quote for one of these unsupported flows, it'll be accepted and executed without the special handling that Relay/Across provide for those cases.
There was a problem hiding this comment.
Absolutely, this is just the initial logic.
The endpoints don't even exist in the production API yet so it would throw and try the next strategy in the priority order.

Explanation
Adds a Server strategy to
TransactionPayControllerthat consumes the intents-API's provider-agnostic/quote,/submit, and/statusendpoints.The strategy supports both gasless flows (where the quote provider handles submission using a signed delegation, removing the need for the user to submit an on-chain transaction) and non-gasless flows (where the quote steps are submitted through
TransactionControlleras normal transactions). Both paths converge on a/statuspolling loop to track completion and surface the target hash.Server strategy flags live in
confirmations_pay_extendedrather thanconfirmations_pay, keeping the rollout gated independently from Relay and Across. The strategy is disabled by default.Not yet supported in this initial implementation:
References
Checklist
Note
Medium Risk
Touches payment execution, delegation/EIP-7702 quoting, and on-chain submission paths; rollout is gated by remote flags but misconfiguration or API errors could affect user fund flows.
Overview
Adds a Server pay strategy to
transaction-pay-controllerthat talks to the MetaMask intents API (/quote,/submit,/status) instead of provider-specific Relay/Across clients directly.Routing & flags:
TransactionPayStrategy.Serveris registered in strategy resolution; default strategy order now prefers Server first. Enablement, base URL, and polling settings come fromconfirmations_pay_extended.payStrategies.server(off by default), separate from Relay/Across flags.Quote path: Builds intents quote requests (exact input vs expected output, ERC-20 recipient decoding, EIP-7702 delegation
calls/authorizationList, gasless eligibility) and normalizes API results intoTransactionPayQuote, including gas-station / source-network fee handling for non-gasless quotes. Hyperliquid perps deposits are detected via bridge calldata and rewritten to a HyperCore sentinel target.Submit path: Gasless quotes submit a signed delegation via
/submit; non-gasless quotes push steps throughTransactionController(addTransaction/addTransactionBatch), then both paths poll/statusuntil confirmed (with timeouts, source/target hash updates, andisIntentComplete).getGasFeeis exported for reuse in server quote gas math.Tests & changelog: Broad unit coverage for API client, quotes, submit, perps normalization, strategy wiring, and feature-flag behavior.
Reviewed by Cursor Bugbot for commit e34ddc9. Bugbot is set up for automated code reviews on this repo. Configure here.