Skip to main content

Crate driftbrake

Crate driftbrake 

Source
Expand description

driftbrake: chain-agnostic pre-flight simulation traits, plus a drift-based halt guard that stops your strategy the moment realized profit diverges from the simulated prediction — before drift compounds into real capital loss.

This is a facade crate: it re-exports driftbrake_core, driftbrake_reconcile, driftbrake_receipt_poller, and driftbrake_journal under one dependency, so cargo add driftbrake gets you the chain-agnostic traits, the default dual-guard halt policy, receipt polling, and persistent WAL state in one shot.

The REVM simulation backend is intentionally not bundled here. driftbrake-revm-backend is a separate crate (cargo add driftbrake-revm-backend) — see docs/ARCHITECTURE.md’s “swappable simulation backend” design goal: a team with an existing simulation stack implements SimEngine against their own tooling and should never be forced to compile REVM just to depend on this crate for the reconciliation and halt logic. If you do want the REVM backend (most users will), add it alongside this crate — see the Quickstart below.

For the full mechanism specification and correctness argument, see docs/whitepaper.md. For exact trait contracts, see docs/API.md.

§Quickstart

This example type-checks against the real API (see this crate’s doc tests) but is marked no_run, since it needs a live RPC endpoint to actually execute — see examples/toy-arbitrage for a runnable end-to-end version.

use driftbrake::{
    DecodeError, HaltDecision, HaltPolicy, Log, PredictedProfit, ProfitDecoder,
    RawSimOutput, RealizedProfit, RealizedProfitDecoder, ReconcileHistory,
    ReconcilePolicy, SimEngine, TxReceipt, TxStatus,
};
// The REVM backend is a separate crate — `cargo add driftbrake-revm-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); this
//    minimal stub is just enough to make the example self-contained.
#[derive(Clone)]
struct MyDbFactory;

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

Structs§

DrawdownConfig
Absolute net capital drawdown configuration: halts if cumulative prediction deficit (sum(predicted - realized)) across the last window pairs exceeds max_drawdown_limit.
FastGuardConfig
Fast-guard configuration: halts on window consecutive ratios all below threshold.
JournaledHistory
A disk-backed Write-Ahead Log wrapping ReconcileHistory.
Log
A single decoded event log from a confirmed transaction receipt.
PollerConfig
Polling cadence and timeout, both expressed as a function of block time rather than a fixed millisecond constant — the same reasoning as driftbrake-revm-backend’s SimEngine timeout: a value tuned for one chain’s block time doesn’t transfer to a chain with a meaningfully different one.
PredictedProfit
Profit predicted by simulation, in the smallest denominated unit of the relevant token (e.g. wei for ETH-denominated strategies).
RawSimOutput
Opaque wrapper around whatever a crate::SimEngine implementation returns for a single candidate-transaction simulation.
RealizedProfit
Profit actually realized on-chain, net of gas cost, in the same unit and same token as the corresponding PredictedProfit.
ReceiptPoller
Polls a ReceiptSource for a transaction’s receipt and books its realized profit via a RealizedProfitDecoder, once both the confirmed-status and decodable-profit-event conditions are met.
ReconcileHistory
Append-only, confirmation-time-ordered history of (predicted, realized) pairs and on-chain revert events.
ReconcilePolicy
The default HaltPolicy: the phantom-guard dual guard, optionally extended with revert and capital-weighted risk guards.
RevertBurstConfig
Revert-burst configuration: halts if limit consecutive transactions revert on-chain.
RevertEvent
A confirmed-but-reverted transaction, tracked separately from ReconcileHistory::pairs rather than folded in as a zero-profit realization (whitepaper Section 4.5).
RevertGasConfig
Revert gas-budget configuration: halts if cumulative gas fees burned on reverts within the last window timeline entries exceeds budget_limit.
SlowGuardConfig
Slow-guard configuration: halts when the mean of the last window ratios drops below threshold.
TxReceipt
Standard confirmed-transaction receipt.
VolumeWeightedConfig
Volume-weighted average ratio configuration: halts if VWAR of the last window confirmed pairs drops below threshold.

Enums§

DecodeError
Shared error vocabulary for crate::ProfitDecoder and crate::RealizedProfitDecoder.
HaltDecision
Decision returned by crate::HaltPolicy::evaluate.
HaltReason
Structured reason a HaltDecision::Halt was returned.
HistoryEntry
A chronological event in the execution timeline.
JournalError
PollError
Error from the receipt source itself (RPC failure, connection drop, etc.) — distinct from PollOutcome::DecodeFailed, which is a successful poll that then failed to decode.
PollOutcome
Outcome of polling a single transaction to settlement (or giving up).
SimError
Error type returned by a crate::SimEngine implementation.
TxStatus
Confirmation status of a transaction, as reported by the chain.

Traits§

HaltPolicy
Given the full (sim, realized) history so far, decides whether to halt.
ProfitDecoder
Decodes a raw simulation result into a predicted profit.
RealizedProfitDecoder
Decodes a confirmed receipt + its logs into a realized profit, net of gas cost.
ReceiptSource
Where receipt-poller gets receipts and logs from.
SimEngine
Forks chain state and runs a candidate transaction through a simulation backend, returning a RawSimOutput within a hard latency budget.