settleBatch's gas-exhaustion exit emits nothing, so an unattempted pair is indistinguishable from a failed one
The gas-exhaustion exit from settleBatch emits nothing, so an unattempted pair cannot be distinguished from a failed one in the logs, which are the relayer's only observable channel.
Description
settleBatch has three per-index outcomes and only two of them write to the log stream.
The oversized-signature skip and the failed self-call both emit SettlementSkipped. The
gas-exhaustion exit does not:
for (uint256 i = 0; i < n; ++i) {if (gasleft() < SETTLE_ONE_GAS_CAP + BATCH_GAS_FLOOR) break;
settled is a return value, and settleBatch is called by a relayer EOA in a transaction, so
the return data is not available to the submitter — logs are the only observable channel. An
index that was never attempted therefore produces no Settled and no SettlementSkipped,
which is indistinguishable from an index that was attempted and failed silently, or from a
dropped log.
Settled also carries no batch index:
event Settled(bytes32 indexed askHash,bytes32 indexed bidHash,address seller,...);
so resolving which positions succeeded requires recomputing every EIP-712 digest in the batch and matching them against unindexed events.
The fail-safe direction is correct — re-submitting a pair that did settle reverts
OrderAlreadySettled — so this is an observability gap rather than a double-settlement risk.
It matters because the off-chain layer decides whose order to cancel from these signals, and
treating absence as failure cancels the orders of every maker in the untouched tail.
Impact
An index that was never attempted is indistinguishable from one that was attempted and failed. Because the off-chain layer decides whose order to cancel from these signals, treating absence as failure cancels the orders of every maker in the untouched tail of the batch. No double settlement is possible — re-submitting a settled pair reverts — so the exposure is observability rather than custody.
Recommendation
Emit the stop point, and index the successes:
+event BatchStopped(uint256 firstUnprocessedIndex, uint256 gasRemaining);
-if (gasleft() < SETTLE_ONE_GAS_CAP + BATCH_GAS_FLOOR) break;+if (gasleft() < SETTLE_ONE_GAS_CAP + BATCH_GAS_FLOOR) {+ emit BatchStopped(i, gasleft());+ break;+}
event Settled(+ uint256 indexed index,bytes32 indexed askHash,bytes32 indexed bidHash,
Adding a fourth indexed parameter is not possible, so if index should be indexed, demote one
of the existing three. bidHash is the least useful as a topic given askHash already
identifies the pair.
Resolution
The gas-exhaustion exit now emits its stop point, and the settlement event leads with the batch
index, so a pair is attributed from its own log rather than by counting. The off-chain
dual-decode that carried the migration has been retired. Verified at audit-v2.
Affected files
src/PlakxioSettlement.sol#L374-L379andsrc/PlakxioSettlement.sol#L186-L196at commit5c38893