driftbrake 0.1.0

Chain-agnostic pre-flight simulation + drift-based halt guard for simulation-driven trading strategies. Re-exports core, reconcile, and receipt-poller as a single dependency. The REVM simulation backend (driftbrake-revm-backend) is a separate, opt-in crate, not bundled here — see docs/ARCHITECTURE.md's 'swappable simulation backend' goal: a team using their own simulation stack should not be forced to compile REVM just to depend on driftbrake.
Documentation

driftbrake

A chain-agnostic Rust crate that pre-flight simulates a transaction through REVM, then halts your strategy the moment realized profit drifts from the simulated prediction — before drift compounds into real capital loss.

CI License: MIT crates.io docs.rs book

Any bot's internal model of the world — its REVM fork, its price feed, its assumption about which venue fills first — can go stale relative to actual chain state. driftbrake packages three tested fixes for the failure modes that causes (ratio-direction inversion, blocking-thread starvation, unbounded RPC saturation) behind a small set of chain-agnostic traits, plus a dual-guard halt policy that catches both a sudden break and a slow bleed. For the full mechanism specification and correctness argument, see the whitepaper — or read it rendered at xtley001.github.io/driftbrake.

Documentation

  • xtley001.github.io/driftbrake — the design docs (architecture, whitepaper, API reference, benchmark methodology) as a browsable site, built from docs/ via mdBook. This is the "why."
  • docs.rs/driftbrake — generated API reference (every public trait/type, from doc comments). This is the "what."
  • This README is the "how" — the Quickstart below.

Installation

cargo add driftbrake
# Add the REVM simulation backend too, unless you're bringing your own SimEngine:
cargo add driftbrake-revm-backend

Quickstart

use driftbrake::{
    DecodeError, HaltDecision, HaltPolicy, Log, PredictedProfit, ProfitDecoder,
    RawSimOutput, RealizedProfit, RealizedProfitDecoder, ReconcileHistory,
    ReconcilePolicy, SimEngine, TxReceipt, TxStatus,
};
// The REVM backend is a separate, opt-in crate — `cargo add driftbrake-revm-backend`.
// (Bringing your own simulation stack instead? Implement `SimEngine` against it and
// skip this import entirely — see docs/ARCHITECTURE.md's "swappable simulation backend".)
use driftbrake_revm_backend::{BlockContext, CandidateTx, DbFactory, RevmBackend};

// 1. `SimEngine` needs a `DbFactory` — something that forks chain state.
//    Bring your own (a live RPC-backed one, or a test fixture).
#[derive(Clone)]
struct MyDbFactory;

impl DbFactory for MyDbFactory {
    type Db = revm::db::CacheDB<revm::db::EmptyDB>;
    type Error = std::convert::Infallible;
    fn fresh_db(&self) -> Result<Self::Db, Self::Error> {
        Ok(revm::db::CacheDB::new(revm::db::EmptyDB::new()))
    }
}

// 2. Implement `ProfitDecoder` / `RealizedProfitDecoder` for your executor
//    contract's specific ABI shape — see docs/API.md.
struct MyProfitDecoder;
impl ProfitDecoder for MyProfitDecoder {
    fn decode_predicted(&self, raw: &RawSimOutput) -> Result<PredictedProfit, DecodeError> {
        if raw.revert_reason.is_some() {
            return Err(DecodeError::Reverted);
        }
        Ok(PredictedProfit(0)) // decode your executor's real return value here
    }
}

struct MyRealizedDecoder;
impl RealizedProfitDecoder for MyRealizedDecoder {
    fn decode_realized(&self, receipt: &TxReceipt, _logs: &[Log]) -> Result<RealizedProfit, DecodeError> {
        if receipt.status != TxStatus::Confirmed {
            return Err(DecodeError::Other("not confirmed".into()));
        }
        Ok(RealizedProfit(0)) // decode your Profit event and net gas cost here
    }
}

#[tokio::main]
async fn main() -> anyhow::Result<()> {
    let block = BlockContext { number: 0, timestamp: 0, gas_limit: 30_000_000, basefee: 0, coinbase: [0; 20] };
    let sim = RevmBackend::new(MyDbFactory, block, /* block_time_ms */ 12_000, /* timeout_fraction */ 0.5, /* concurrency */ 4);
    let predicted_decoder = MyProfitDecoder;
    let realized_decoder = MyRealizedDecoder;
    let mut guard = ReconcilePolicy::default_dual_guard();
    let mut history = ReconcileHistory::new();

    let candidate_tx = CandidateTx { caller: [0; 20], to: [0; 20], value: 0, data: vec![], gas_limit: 100_000 };
    let raw = sim.simulate(&candidate_tx).await?;
    let predicted = match predicted_decoder.decode_predicted(&raw) {
        Ok(p) if p.0 > 0 => p,
        _ => return Ok(()), // not profitable in simulation (or reverted), skip submission
    };

    let receipt = submit_and_wait(&candidate_tx).await?;
    let realized = realized_decoder.decode_realized(&receipt, &[])?;
    history.append(predicted, realized);

    match guard.evaluate(&history) {
        HaltDecision::Continue => {}
        HaltDecision::Halt(reason) => {
            eprintln!("phantom-guard halted strategy: {reason:?}");
            std::process::exit(1);
        }
    }
    Ok(())
}

This example is kept honest against the real API: it's a doctest in the driftbrake crate (cargo test --doc -p driftbrake), so it can't silently drift from what actually compiles the way a hand-maintained snippet can.

Architecture

driftbrake/
├── driftbrake/            # facade crate: re-exports core + reconcile + receipt-poller
│   ├── core/                  # chain-agnostic traits, zero REVM/alloy dependency
│   │   ├── ProfitDecoder
│   │   ├── RealizedProfitDecoder
│   │   └── HaltPolicy
│   ├── reconcile/             # the halt-guard logic (phantom-guard) — see docs/ARCHITECTURE.md
│   └── receipt-poller/        # confirms receipts, decodes realized profit
├── revm-backend/          # separate, opt-in: concrete SimEngine (fork-and-simulate,
│                          # spawn_blocking, bounded concurrency) — not bundled into
│                          # `driftbrake` itself, see docs/ARCHITECTURE.md
├── telemetry/             # planned, not yet built — structured events for the benchmark harness
├── benchmark/              # docs/BENCHMARK.md's methodology as a runnable tool (cargo run -p driftbrake-benchmark)
└── examples/
    └── toy-arbitrage/     # minimal 2-pool arb wired end-to-end against a public testnet fork

For the full module breakdown, trait contracts, and why the two halt guards are independently necessary, see docs/ARCHITECTURE.md.

Benchmark

The fast/slow guard thresholds (0.50, 0.70) and window sizes (3, 20) are not asserted defaults — they're derived from a synthetic (sim, realized) sweep plotting false-halt rate against missed-catch rate. See docs/BENCHMARK.md to reproduce the sweep or re-tune it against your own historical data.

Testing

cargo test --workspace

The reconcile module's ratio-direction regression test is the single most important test in the suite — see docs/whitepaper.md for why.

Security

driftbrake gates a live trading loop, so a bug in reconcile or revm-backend has direct capital consequences. Report vulnerabilities per our security policy. This code has not been independently audited — review the reconcile and HaltPolicy logic yourself before running it against real capital.

Contributing

See CONTRIBUTING.md for dev environment setup, module boundaries, and PR guidelines.

License

Released under the MIT License.