Skip to content

[scanner] Pre-signed liquidations bypass eth_call simulation gate — CLAUDE.md safety invariant violated #226

@obchain

Description

@obchain

Refs #46

PR: feat/21-mempool-monitor
File: crates/charon-scanner/src/mempool.rs
Types: PreSignedLiquidation, PendingCache::drain()

CLAUDE.md safety invariant (hard rule):
"Every liquidation transaction passes an eth_call simulation gate before broadcast."

Problem

PreSignedLiquidation.raw_tx is a fully-signed EIP-2718 envelope produced by TxBuilder::sign and stored ready for eth_sendRawTransaction. The PR's stated flow is:

  1. See pending oracle tx in mempool.
  2. Predict post-update state off-chain.
  3. Pre-sign liquidation against predicted state.
  4. Store in PendingCache.
  5. On NewBlock: drain cache then broadcast.

Step 5 contains no eth_call simulation. The signed tx was built against a predicted price that may never materialise: the oracle tx could revert, be replaced via EIP-1559, or simply not be included in the next block. The existing simulation gate in charon-executor runs eth_call against current confirmed state before eth_sendRawTransaction. That gate is structurally bypassed here: the raw bytes are signed before the triggering oracle tx confirms, and drained and broadcast without re-simulation.

Risk

Broadcasting a liquidation tx against a state that never materialised causes gas loss on revert, possible liquidation of a still-healthy position, and direct violation of the CLAUDE.md hard invariant.

Required fix

Option A: drain_candidates() returns entries; the wiring code must run eth_call simulation against the now-confirmed block state before calling eth_sendRawTransaction. Enforce via type wrapper that requires a SimResult before unwrapping raw_tx.

Option B: PendingCache stores an unsigned LiquidationOpportunity plus signer reference. Signing and simulation happen together in the drainer after block confirmation, in a single eth_call then sign then send sequence.

This PR must not be merged while the simulation invariant is violated.

Metadata

Metadata

Assignees

No one assigned

    Labels

    bugSomething isn't workinglayer:rustRust crates (core / scanner / protocols / executor / cli)priority:p0-blockerBlocks the critical path

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions