Skip to content

[sdk/simplex/indexer]: off-chain support for cross-chain partial fills #982

Description

@seunlanlege

Summary

#980 adds cross-chain partial fills to IntentGatewayV2 at the contract level (multiple solvers each fill a slice of a cross-chain order; RedeemEscrowPartial releases a proportional escrow slice without finalizing; cancels refund only the proven-unredeemed fraction). That PR is contract-only — its own "Follow-ups" section defers the off-chain work. This issue tracks everything else required to ship the feature end-to-end.

Prerequisite: complete #981 first

This work should land after #981 (make IntentGatewayV2 a UUPS proxy upgradeable via cross-chain governance). Reasons:

  • Cross-chain partial fills are a substantial change to a fund-custody contract deployed at one canonical address across 9 chains. Without the proxy, rolling it out means redeploying and re-registering the gateway on every chain — which changes the canonical address and breaks cross-chain auth (_authenticate keys off the gateway address) and any in-flight orders. With the proxy, it ships as a single coordinated governance upgrade via the intents-coprocessor, preserving the address.
  • The PR itself flags "cross-chain fund movement — independent/cloud review recommended before mainnet." The proxy gives a governance-driven hotfix/rollback path if an issue is found post-deployment, instead of another multi-chain redeploy.
  • [evm]: cross-chain partial fills for IntentGatewayV2 #980 adds storage (_partialFills use for cross-chain, plus a slot guard). Landing the proxy first fixes the canonical storage layout so these additions are appended cleanly under the append-only rule.

Off-chain work required

SDK (sdk/packages/sdk/src/protocols/intents/)

  • Bid.ts / BidManager.ts — construct partial cross-chain fills (fill a slice of the outputs), decode/allow partial-amount fill-options for cross-chain bids. Today Bid.ts parses OrderFilled/PartialFill only to classify status; it must also be able to build a partial cross-chain fill.
  • OrderExecutor.ts — extend the cross-chain lifecycle (AWAITING_BIDS → BIDS_RECEIVED → BID_SELECTED → FILLED) to support repeated PARTIAL_FILL transitions like the same-chain path already does, until the order is fully filled, cancelled, or expired.
  • OrderStatusChecker.ts — cross-chain status can no longer be derived from the single _filled slot (it's cleared on partial, set only on completion). Read _partialFills per output to compute fill progress.
  • OrderCanceller.ts — cross-chain cancel quoting/refund must reflect that only the proven-unredeemed fraction is refunded (the GET proof now proves _partialFills), not the full escrow.
  • types.ts — add the RedeemEscrowPartial request kind and any new event shapes (EscrowReleased now fires per source-side release and carries the solver).

Simplex (sdk/packages/simplex/)

  • core/filler.ts + strategies/{base,basic,fx}.tscanFill/fill decisioning currently treats cross-chain as all-or-nothing. Add slice sizing for cross-chain orders and the ability to submit a partial fill.
  • Fee-pot economics — the completing solver takes the whole fee pot ("completing-solver-takes-all"). Strategy profitability/bid logic must account for this: a partial filler that never completes earns no fees, which changes expected value.
  • In-flight redeem risk — solvers must tolerate the cancel-vs-redeem race (cancel refunds only the unredeemed fraction; an in-flight RedeemEscrowPartial stays covered). Settlement tracking must not assume a fill is final until the finalizing RedeemEscrow.
  • core/event-monitor.ts — already watches OrderFilled/PartialFill; confirm cross-chain partials are surfaced and re-evaluated for follow-on fills.

Indexer (sdk/packages/indexer/src/handlers/events/intentGatewayV3/ + services/intentGatewayV3.service.ts)

  • ABI regenIntentGatewayV2Abi needs regenerating for changed event signatures (e.g. EscrowReleased now carries the solver and fires on every source-side release including partial redeems).
  • escrowReleasedV3 handler — must aggregate cumulative released amount per commitment (and per solver) rather than treating a release as terminal; partial redeems are now non-finalizing.
  • partialFilledV3 / orderFilledV3 handlers — represent "cross-chain order partially redeemed, escrow still open" in the order/settlement model; finalization only on the completing RedeemEscrow.
  • escrowRefundedV3 handler — handle partial refund amounts from cross-chain cancels (unredeemed fraction), not just full refunds.

Coprocessor (modules/pallets/intents-coprocessor/) — secondary

Execution: walk the bids by rate

Once #1259 lands, a bid is a slice with a rate and a size, on same-chain and cross-chain orders alike. OrderExecutor should then sort an order's bids by rate, best first, and execute them in that order until the order is full, one select and one fill per bid, instead of selecting a single winner. The last bid executed is usually larger than what is outstanding; the contract caps the fill and scales the take with it, so nothing is re-signed. A bid that fails is skipped and the walk continues; executed bids cannot be replayed. Bids on multi-leg orders are not totally ordered, so sort per leg or keep such orders single-leg. Cross-chain, each executed slice is its own redeem message and the completing slice takes the fee pot, which is a solver-side sizing concern, not the executor's.

Acceptance criteria

  • [evm/runtime]: make IntentGatewayV2 upgradeable via cross-chain governance #981 merged and IntentGatewayV2 deployed as a proxy; this feature ships as a governance upgrade.
  • SDK can construct, track, and cancel partial cross-chain orders end-to-end.
  • Simplex can size, submit, and settle partial cross-chain fills with fee-pot-aware profitability.
  • Indexer correctly tracks cumulative cross-chain releases, partial-redeem state, and partial refunds; ABI regenerated.
  • Coprocessor RequestKind byte parity maintained with the contract.
  • OrderExecutor executes bids best rate first until the order is full, on both same-chain and cross-chain orders ([evm]: Partial fills at the solver's rate #1259).

Activity

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

Metadata

Metadata

Assignees

Labels

No labels
No labels

Type

No type

Projects

No projects

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions