Narrow successor to #169, scoped to what is actually missing in RaindexV6: an order whose rainlang eval reverts. Dead orders, too-expensive orders and non-positive amounts are already skipped; handle-io stays a hard revert.
Current behaviour
takeOrders4 already drops orders gracefully in two cases: a dead order emits OrderNotFound and the loop continues, and an order whose calculation is too expensive or yields a non-positive amount/ratio is a no-op. The rationale is in the code:
Skip orders that are too expensive rather than revert as we have no way of knowing if a specific order becomes too expensive between submitting to mempool and execution, but other orders may be valid so we want to take advantage of those if possible.
A revert gets none of that. calculateOrderIO calls order.evaluable.interpreter.eval4(...) (src/concrete/raindex/RaindexV6.sol:859) with no try, so a revert inside an order's calculate rainlang — or the UnsupportedCalculateOutputs check at :875 — takes the whole transaction down, including every viable order later in the list. The current answer sits in the comment beside that call: "the caller controls which orders are eval'd as they can drop failing calls and resubmit a new transaction" — resubmission, which is exactly what the price-skip path exists to avoid. It hits arb takers hardest, since they submit multi-order batches whose runtime behaviour they do not control.
Why this is now cheap and safe
calculateOrderIO makes no store writes. The calculate KVs are written inside handleIO (:1068), which runs only for orders that are taken. The loop says so: "An untaken order runs no handleIO, so it makes no state change at all."
- So catching a calculate revert leaves nothing to roll back, and the zero-amount branch it should fall into already exists and already writes nothing.
- The eval is an external call, so
try/catch is available at that site.
This is the resolution recorded on #169 in 2024: treat a calculate-io revert as amount io-ratio: 0 … and ignore its writes.
Explicitly out of scope
Handle-io reverts stay hard reverts. By the time handleIO evaluates, the order is taken and its calculate KVs are already committed at :1068, so swallowing a revert there would persist state for an order that did not settle. #169's 2026-06-15 triage note flags that invariant for the auditors; this issue must not weaken it.
Notes for implementation
- Emit an event for an order skipped because it reverted. A silent skip makes a broken order indistinguishable from an unprofitable one, both on chain and to the bots reading the logs.
- Decide the gas policy explicitly.
try forwards 63/64 of remaining gas, so an order that burns it all can still starve the rest of the batch — either cap the gas passed to the calculate eval, or state that it is deliberately unbounded.
- Tests: an order whose calculate reverts is skipped while later orders still take; the store shows no writes for the skipped order; a handle-io revert still reverts the whole call.
Refs #169.
Narrow successor to #169, scoped to what is actually missing in
RaindexV6: an order whose rainlang eval reverts. Dead orders, too-expensive orders and non-positive amounts are already skipped; handle-io stays a hard revert.Current behaviour
takeOrders4already drops orders gracefully in two cases: a dead order emitsOrderNotFoundand the loop continues, and an order whose calculation is too expensive or yields a non-positive amount/ratio is a no-op. The rationale is in the code:A revert gets none of that.
calculateOrderIOcallsorder.evaluable.interpreter.eval4(...)(src/concrete/raindex/RaindexV6.sol:859) with notry, so a revert inside an order's calculate rainlang — or theUnsupportedCalculateOutputscheck at:875— takes the whole transaction down, including every viable order later in the list. The current answer sits in the comment beside that call: "the caller controls which orders are eval'd as they can drop failing calls and resubmit a new transaction" — resubmission, which is exactly what the price-skip path exists to avoid. It hits arb takers hardest, since they submit multi-order batches whose runtime behaviour they do not control.Why this is now cheap and safe
calculateOrderIOmakes no store writes. The calculate KVs are written insidehandleIO(:1068), which runs only for orders that are taken. The loop says so: "An untaken order runs nohandleIO, so it makes no state change at all."try/catchis available at that site.This is the resolution recorded on #169 in 2024: treat a calculate-io revert as
amount io-ratio: 0 …and ignore its writes.Explicitly out of scope
Handle-io reverts stay hard reverts. By the time
handleIOevaluates, the order is taken and its calculate KVs are already committed at:1068, so swallowing a revert there would persist state for an order that did not settle. #169's 2026-06-15 triage note flags that invariant for the auditors; this issue must not weaken it.Notes for implementation
tryforwards 63/64 of remaining gas, so an order that burns it all can still starve the rest of the batch — either cap the gas passed to the calculate eval, or state that it is deliberately unbounded.Refs #169.