Repository navigation
feat(transaction-pay-controller): support second-leg submission of target calls - #10501
matthewwalsh0 wants to merge 16 commits into
Conversation
93984a0 to
71ec7a0
Compare
fa035bc to
a0834c6
Compare
fc6005d to
b868774
Compare
0fe4cf7 to
4791df2
Compare
d062d63 to
f58e3a8
Compare
4791df2 to
2552899
Compare
f46884a to
acf8b02
Compare
… when the provider cannot execute them Server quote providers either execute the target calls as part of the quote or decline the request outright, so flows whose calls no provider can run were simply unsupported. Request such calls as optional, so providers that cannot execute them still return a funds-only quote flagged with `callsSupported: false`. When that happens the quote is marked `requiresSecondLeg` and the calls are submitted separately on the target chain once the quote settles. Extract the second-leg submission that the Money Account vault deposit already performed into a shared `second-leg` util, reading the settled amount from the settlement transfer logs so the second leg spends what actually landed rather than the quoted amount. `ma-vault-deposit` is now a thin Monad-pinned wrapper that layers on CHOMP race handling, and the relay strategy's recipient resolution moves into the same util so both strategies settle non-atomic funds on the executing account.
… to supportsDeferredCalls and callsDeferred
… submitSecondLeg with CHOMP recovery
…the second leg can be sponsored
…second leg tests Raise the `server-quotes.test.ts` counts and add a `second-leg.test.ts` entry to cover the literals added by the new tests, matching the call sites already suppressed in those files. Drop the `ma-vault-deposit.test.ts` entries, whose violations no longer occur now that the second leg resolves its own execution account.
… into submitSecondLeg and update tests
…ate and tidy second leg tests
…olymarket deposit wallet withdraws
…d realign lint suppressions
acf8b02 to
e912f9f
Compare
|
|
||
| // Phase 4: submit the calls the quote could not execute itself, now that the | ||
| // funds have settled on the target chain. | ||
| if (quote.requiresSecondLeg) { |
There was a problem hiding this comment.
I know this is an old code but should we mark isIntentComplete only after the second leg confirms, so a failed second leg does not leave funds settled with the parent looking done?
There was a problem hiding this comment.
Definitely, good spot, will fix in this PR since we're already refactoring.
…t HyperCore from the built quote request - Send the embedded server transfer to the account the delegated calls run from, rather than the paying account, when an account override is active. - Check the built request body for HyperCore when deciding whether a second leg is required, since perps deposits are only rewritten there. - Read the execution account from txParams.from directly and drop resolveExecutionAccount, as from is always set.
…second leg Mark the parent transaction complete once the second leg has been submitted, for both the server and relay strategies, so a failed second leg no longer leaves a failed transaction flagged as complete.
…non-atomic-second-leg # Conflicts: # packages/transaction-pay-controller/CHANGELOG.md
There was a problem hiding this comment.
Cursor Bugbot has reviewed your changes and found 2 potential issues.
❌ 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 e2cc524. Configure here.
| // satisfied by the quote delivering the target token to the recipient. | ||
| if (!transaction.nestedTransactions?.length) { | ||
| return false; | ||
| } |
There was a problem hiding this comment.
Deferred calls skip without nested txs
High Severity
supportsDeferredCalls is set whenever the account can sign a sponsored EIP-7702 batch, but isSecondLegRequired then returns false whenever nestedTransactions is empty. Calls that live only on txParams.data still get embedded and may come back as callsDeferred: true, after which the second leg is skipped and the parent is marked complete. Funds settle on the execution account and the original target calls never run.
Additional Locations (1)
Reviewed by Cursor Bugbot for commit e2cc524. Configure here.
| ? (nestedTransaction.type ?? TransactionType.tokenMethodApprove) | ||
| : TransactionType.contractInteraction, | ||
| })), | ||
| }); |
There was a problem hiding this comment.
Second leg skips EIP-7702 upgrade
High Severity
Quote-time deferred-call support includes the EIP-7702 authorizationList so a provider can upgrade the account while executing the calls. submitSecondLeg never receives that list and always submits with disableUpgrade: true. If the provider returns a funds-only quote, an EOA that is not already upgraded on the target chain cannot run the second-leg batch, so the calls fail after the funds have already settled.
Additional Locations (1)
Reviewed by Cursor Bugbot for commit e2cc524. Configure here.


Explanation
Server quote providers either execute the target calls or decline the request, and the server strategy declined non-atomic requests outright, so flows whose calls no provider can run were unsupported.
supportsDeferredCalls, so a provider that can't execute the calls returns a funds-only quote (callsDeferred: true) instead of declining.requiresSecondLeg, and the calls are submitted separately on the target chain once the funds settle.submitSecondLegutil, which:ma-vault-depositis removed in favour of the shared util.References
Stacked on #10404.
Checklist
Note
High Risk
Changes payment settlement, amount resolution, and multi-leg completion semantics across Relay, Server, and Fiat—failures now surface when on-chain amounts are missing instead of silent fallbacks.
Overview
Adds non-atomic / deferred-call support so quotes can deliver funds first and run target calls in a separate second leg on the account that executes them.
Shared second leg: Replaces
ma-vault-depositwithsubmitSecondLeg, used by Relay, Server, and Fiat (including direct mUSD). Quotes exposerequiresSecondLeg; Server can requestsupportsDeferredCallsand honor providercallsDeferred. Settlement amounts come only from on-chain transfer logs (no quote-minimum /order.cryptoAmountfallback). CHOMP race recovery is centralized viawithChompRecoveryfor Money Account deposits.Behavior fixes: Parent
isIntentCompleteis set only after a successful second leg (Relay/Server). Relay zeroes origin network fees for Polymarket deposit-wallet Predict withdraws. Server strategy no longer rejectsatomic: false; provider names are plain strings.Reviewed by Cursor Bugbot for commit e2cc524. Bugbot is set up for automated code reviews on this repo. Configure here.