Skip to content

[evm]: cross-chain partial fills for IntentGatewayV2#980

Draft
seunlanlege wants to merge 1 commit into
mainfrom
feat/crosschain-partial-fills
Draft

[evm]: cross-chain partial fills for IntentGatewayV2#980
seunlanlege wants to merge 1 commit into
mainfrom
feat/crosschain-partial-fills

Conversation

@seunlanlege

Copy link
Copy Markdown
Member

Summary

Adds partial fills for cross-chain orders to IntentGatewayV2. Previously cross-chain fills were all-or-nothing; this lets multiple solvers each fill a slice of a cross-chain order, mirroring the existing same-chain partial-fill behaviour.

Design

The core invariant: every escrow movement uses a single monotonic _cumulativeReleased(escrowTotal, filled, totalRequired) function, so across any split — and regardless of cross-chain message arrival order — the redeemed slices plus any cancel refund sum to exactly the escrowed amount, with rounding dust deterministically landing in the completing fill.

  • _fillCrossChain — partial-aware: accumulates _partialFills, pays the beneficiary pro-rata, clears _filled on partial / keeps it on completion, and dispatches RedeemEscrowPartial (non-finalizing) or RedeemEscrow (finalizing). Output-calldata orders still require single-fill completion (PartialFillNotAllowed).
  • onAccept — new RedeemEscrowPartial kind releases a proportional slice without finalizing (escrow stays open, fee pot left for the completing redeem). The completing RedeemEscrow finalizes and forwards the fee pot (completing-solver-takes-all).
  • _cancelFromSource / onGetResponse — proves each output's _partialFills on the destination via a GET request and refunds only the proven-unredeemed fraction, never the raw remaining escrow, so in-flight redeems stay covered. The deadline gate makes the post-deadline proof a final snapshot.
  • _cancelFromDest — reads _partialFills locally and refunds the unredeemed fraction (no proof needed; freeze + snapshot are atomic).

Escrow proofs match values to inputs by storage key (GET responses come back sorted by key, not request order). EscrowReleased now fires for every source-side release (including partial redeems) and carries the solver. placeOrder now enforces inputs.length == outputs.length and rejects zero-amount outputs (both would otherwise strand escrow).

Testing

forge test --match-contract IntentGatewayV2 and the reentrancy suite — 125 tests pass. New tests cover: proportional release + two-solver completion, non-finalizing source redeem, cancel refunding the unredeemed fraction, in-flight-redeem-after-cancel consistency, the fully-filled-cancel race (refund 0, fee pot withheld for the completing solver), multi-token value-by-key matching, double-cancel idempotency, the calldata guard, the placeOrder validations, and a _partialFills storage-slot guard.

Follow-ups (out of scope)

  • TS SDK: handle RedeemEscrowPartial + cross-chain PartialFill, construct partial cross-chain fills.
  • Substrate intents-coprocessor partial support.
  • Independent/cloud review recommended before mainnet (cross-chain fund movement).

@seunlanlege

Copy link
Copy Markdown
Member Author

Would be cool to add simplex, sdk & indexer support to this PR as well

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.

1 participant