Crossfoot

crossfoot

Crossfoot audits tokenized-asset feeds from the outside: it replays the feed's public rules at exact blocks, shows whether each update went through the on-chain check or the issuer's documented high-deviation path, and gives consumers an evidence-backed decision.

What it does

Recomputes value from the contractual terms, compares it with the chain

A vendored ACTUS PAM engine in Rust models what a tokenized instrument should be worth from its contract terms, in exact decimal arithmetic with no floats and no clock reads, and compares the result with what the contract stored on chain at pinned blocks.

Where the underlying portfolio is not observable, the value is never recomputed. The tool replays the oracle's own posting rules per implementation era instead: deviation bound, spacing, min and max, stored rounds against emitted events.

Three targets today. svzchf, the Frankencoin savings vault: full recomputation, two independent model paths. midas, the 66 Midas customFeed contracts: the posting path of every round replayed, the result always carries INPUT_GAP for the NAV. mtbill, the original Midas mTBILL checks.

Read-only by construction

The binary issues chain reads and keyless HTTP GETs. It holds no key and has no code path that can sign or send a transaction. Every read is pinned to an explicit block; nothing is read at latest.

Every run writes evidence

Every raw response is stored byte for byte with its sha256 in a manifest, and crossfoot verify <bundle> re-hashes every file and replays the verdict from the bundle alone, without the network; exit code 0 means the root hash on the page reproduces. Bundles download from every feed page of the explorer.

One verdict vocabulary for every target

MODEL_MATCH / CONSISTENT
all values equal, or all rules satisfied on enough data
OBSERVED_DEVIATION
a nonzero residual or a rule violation
MODEL_INCONSISTENT
the two model paths disagree; the chain is not judged
INSUFFICIENT_WINDOW
a check lacked rounds or days to run. Not a pass.
SOURCE_STALE
an input could not be read at the pinned block
INPUT_GAP
a required series is unobtainable; outranks the others

The Midas survey

44 bounded feeds, no unchecked post over the bound
16 feeds with at least one unchecked post over the bound in force
10 of them with such a post in the last six months
6 derived wrapper feeds without a bound, excluded from the replay
66 Midas customFeed contracts on Ethereum mainnet, replayed at block 25,885,578 (2026-09-01) from external transaction lists and, on the six oldest feeds, from the AnswerUpdated logs of rounds posted through Safes. The order of the squares is arbitrary; the explorer draws the same field per feed, with the evidence behind each square.
66feeds replayed, every setter call decoded
57unchecked posts over the bound, on 16 feeds; 29 on 14 from transaction lists alone
12of them in the last six months, on 10 feeds

An unchecked post is an update sent through the setter without the on-chain deviation check. Midas documents that path as a higher-quorum process applied off chain; nothing on chain shows which process was followed. It says nothing about whether the value was wrong.

The three largest cases
Largest casesPostsBound thenLargest movePeriod
mBTC150.05 %0.105 %2024-11-21 to 2025-03-11, posted through a Safe; the most by count
mSL100.05 %5.727 %2025-05-07 to 2026-03-31, then the bound raised to 0.35 %
mevBTC50.4 %19.312 %2026-02-13 to 2026-03-18, five weeks; the largest move

The bound is a governance parameter changed by timelocked upgrades that never show in the feed's own transaction list, so Crossfoot reads it from archive state at the block, never from head. Rounds posted through a Safe are invisible there too: on the six oldest feeds, 215 such rounds came from AnswerUpdated logs, 28 of them over the bound.

One worked case: on 2026-05-06 the mRE7 feed took the path without the on-chain check for a 2.22 percent move against a 0.36 percent bound; the consumer agent routes it to REVIEW. See the round on its timeline, with the bound step, the findings and the bundle that reproduces the page.

Run it

Rust, one binary, no key

Binaries are attached to the latest release; the README covers install, targets and the verdict vocabulary.

--offline serves every read from the cache and fails on a miss, which proves a replay made no network call.

--verify-root is where cache/ and bundles/ live. Live cross-checks are ignored by default and need the network once.

# build and test (offline)
cargo build --release
cargo test
cargo test -p crossfoot -- --ignored   # live cross-checks

# fetch, run, render
crossfoot fetch svzchf --block <B1> [--baseline-block <B0>]
crossfoot run svzchf --baseline-block <B0> --block <B1>
crossfoot run midas --block <B1> --config config/midas-mainnet.json
crossfoot render --bundles bundles --out site

# decide, then re-check any bundle offline
crossfoot consume --subgraph $CROSSFOOT_SUBGRAPH_URL --timeline mRE7
crossfoot verify <bundle>.tar.gz   # exit 0: root hash reproduces