Skip to content

EVMX — Proof Engine

Evidence-backed EVM transaction forensics with honest uncertainty.

EVMX takes a transaction hash and returns a verdict you can check — not a score, not a confidence percentage, but a named pattern, the five pieces of evidence that establish it, and their exact positions in the execution trace. And when the evidence does not support a verdict, it says so — and says why.


Rust MSRV CI GitHub release Edition License Tests Networks Workspace


Table of Contents


What it is

EVMX is a forensic evidence engine for EVM transactions. It reconstructs the relevant execution evidence from a trace, separates what was observed from what was inferred, and refuses to manufacture certainty when the trace cannot support a verdict.

transaction hash ──► execution trace ──► typed evidence ──► verdict ──► report

It is not a vulnerability scanner, not a "risk score" generator, and not another blockchain explorer. It is a tool that turns a transaction hash into a checked, reproducible, human- and machine-readable report grounded in the execution trace itself.


What it detects, exactly

Two detectors today, both for reentrancy, both defined in terms of trace structure rather than heuristics:

Detector Pairing rule
reentrancy.classic the same contract re-entered through the same function
reentrancy.cross_function the same contract and code, re-entered through a different function

A finding requires six conditions (R1..R6) and produces five pieces of evidence (E1..E5) — all five or nothing. A finding missing any one of them is not constructed at all; that is enforced by the type system, not by a runtime check.

Evidence Meaning
E1 the external call — who called whom, with what value
E2 the storage read that preceded it — contract, slot, value
E3 the write that followed — before and after
E4 the re-entry pair, with the full frame lineage between them
E5 the stale read inside the inner frame

Three outcomes, never two

Most tools answer vulnerable or not vulnerable. EVMX has a third answer, and treats it as a first-class result rather than a failure:

Verdict Meaning
PATTERN_CANDIDATE the structure is present, with its evidence
NO_FINDING the pattern did not hold — on named grounds
INCONCLUSIVE the evidence does not support a verdict — and here is precisely why

INCONCLUSIVE is not hedging. It carries a named cause (SELECTOR_UNAVAILABLE, STALE_READ_UNDETERMINED, …) and, where one exists, what would lift it — for example: re-capture the trace with memory enabled, and the selector becomes readable.

This follows one rule applied everywhere in the engine:

"We do not know" is never collapsed into "there is nothing."


What it does not claim

These limits are enforced in the output, not buried in a footnote.

  • NO_FINDING is not a clean bill of health. One pattern was examined. Other patterns, and vulnerabilities of entirely different classes, are outside what was checked.
  • A finding is a structure, not a loss. EVMX reports that a read → call → write chain with a stale read occurred. It does not establish that funds moved, or that the contract is exploitable.
  • No aggregate verdict exists. Detectors are reported independently. Where they disagree, that disagreement is information; the report type has no field in which a combined score could be stored.
  • Provenance is part of the result. Every report states the chain, the endpoint host (never the URL, never a key), and whether the trace came from a live query or recorded evidence.

Networks

Network Status
Ethereum supported
Arbitrum One supported — after a documented compatibility measurement
Base supported — after a documented compatibility measurement

Each network is admitted only after its trace shape, storage semantics, frame semantics, precompile behaviour and payload limits are measured, not assumed. The Arbitrum measurement is written up in docs/24, and the Base measurement in docs/25 — each including what was not measured, so the boundary stays honest.

Chain identity is verified, never declared: live endpoints are checked with eth_chainId before a single trace is fetched, and recorded evidence carries its chain in its manifest so a fixture cannot be opened under the wrong network.


Getting started

Prerequisites

Run the demo (no RPC key required)

The demo ships real, hash-verified mainnet evidence and runs entirely offline — no API key, no live node:

cargo run -p evmx-api --features demo --bin evmx-demo

# then open http://127.0.0.1:8080

You can pick the network the demo serves recorded evidence for:

cargo run -p evmx-api --features demo --bin evmx-demo -- arbitrum-one   # or: ethereum (default)

Running a live analysis requires an endpoint whose RPC exposes the per-step structLogs this engine consumes — a capability that varies by provider and is measured by the engine rather than assumed.


Usage

The analysis is served over HTTP, and answers the question "give me a tx hash → tell me what the trace shows."

Machine-readable analysis

POST /v1/analyze     {"chain":"ethereum","tx_hash":"0x…"}   ──► application/json

Human-readable report

POST /v1/report      {"chain":"ethereum","tx_hash":"0x…"}   ──► text/html
POST /v1/report/ar                                            ╰─► text/html (Arabic)

Both routes are one analysis in two representations — a test asserts that every detector name and every reason code present in the JSON also appears in the rendered page. The renderer may add words; it can never drop a verdict.

The HTML report is self-contained: no scripts, no fonts, no external assets. It opens identically a year later, offline.

Note. The current interface is the HTTP demo/report layer plus the engine crates as a library. A dedicated evmx CLI (analyze, compare, explain, batch) is an explicit roadmap item — see Roadmap.


Architecture

The engine is a Rust workspace of eight crates, each with a single responsibility and a typed interface:

Crate Responsibility
evmx-core decoding, error model, frame identity, canonical JSON
evmx-trace struct-logs → access timeline, frame indexing, labels, frame kinds
evmx-state storage/account state, diffs, provenance, pre-state cross-check
evmx-detectors pattern detectors (reentrancy.classic, reentrancy.cross_function)
evmx-analysis orchestration: trace + state → analysis → report summary
evmx-report rendering: narrative + self-contained HTML (EN/AR)
evmx-rpc chain identity, provider capability/refusal model, live + recorded I/O
evmx-api HTTP server (evmx-demo) exposing /v1/analyze, /v1/report, /v1/report/ar

A central idea: a detector returns a typed verdict with evidence, and the verdict type has no field in which an unreachable or unsupported conclusion could be stored (proven by construction via Rust's type system).


Project structure

.
├── Cargo.toml                 # workspace manifest (8 crates)
├── rust-toolchain.toml        # pinned, reproducible toolchain
├── crates/
│   ├── evmx-core/             # types, decoding, canonical JSON
│   ├── evmx-trace/            # struct logs → timeline
│   ├── evmx-state/            # storage/account state & provenance
│   ├── evmx-detectors/        # pattern detectors
│   ├── evmx-analysis/         # orchestration + results
│   ├── evmx-report/           # narrative + HTML rendering
│   ├── evmx-rpc/              # provider capability + evidence I/O
│   └── evmx-api/              # HTTP server (evmx-demo)
├── tests/
│   └── fixtures/              # frozen, hash-verified evidence corpus
├── docs/                      # design reviews, measurements, decisions
├── tools/                     # capture / verify / build-evidence scripts
└── provider-check/            # measured provider capability results

Reproducibility

Findings are not reproducible in principle — they are reproducible in fact.

  • 19 frozen fixtures, each a real mainnet transaction with its trace hashed and its provenance recorded.
  • 382 tests, no network required. Live-network tests exist and are #[ignore]d by default: a test whose result depends on network conditions tests the network, not the code.
  • Guards are proven by mutation. Each guard is deliberately broken, the failing tests are recorded, and the file is restored and byte-compared. A guard no test can break is not a guard.
# build
cargo build --workspace

# run the offline test suite (live-network tests are ignore-d by default)
cargo test --workspace

# static analysis
cargo clippy --workspace --all-targets

Status

The core is closed and tagged. The detector_known_positive gate stands at 2 of 3: two historical incidents are anchored to independent published sources at the level of the specific contract, variable and values. No third case in the current corpus meets that bar, and none was manufactured to reach the number — see docs/23.

That gap is a corpus limit, and it is published rather than papered over. It is also the shortest honest summary of how this project is built.


Documentation

The repository's internal design log lives in docs/, led by PROJECT_MAP.md. Highlights:

Document Scope
01-tech-stack dependency matrix and rationale
03-architecture crate boundaries, contracts, data models
05-proof-format canonicalization and .evmxproof scheme
07-threat-model trust boundaries and invariants INV-1..INV-11
08-fixtures-registry the corpus registry as a formal entity
20-b5-historical-corpus the historical-incident corpus
23-ff-gate-closure the detector_known_positive gate
24-multi-network-arbitrum the Arbitrum One measurement
25-multi-network-base the Base measurement

Roadmap

The project is deliberately sequenced around evidence and market signal, not feature-count:

  1. Close the detector gate — a genuinely independent third positive case against the published record (never manufactured).
  2. A dedicated CLIevmx analyze, evmx report, evmx batch as a first-class utility that fits inside a researcher's daily workflow.
  3. Provider-aware compatibility layer — publish the measured provider capability model so live tracing is predictable across endpoints.
  4. Network expansion on demand — Base joined on a measured capability; any further network is added only when there is a commercial request or a measured capability, never for logo count.

Contributing

Contributions are welcome and are expected to meet the same bar as the code they touch. Please read CONTRIBUTING.md first — in particular the rule that "we do not know" is never collapsed into "there is nothing", which applies to pull requests as much as to verdicts.

The default branch is protected: all changes land through a pull request that requires at least one approving review.


Security

Please report vulnerabilities privately and only after responsible disclosure — never in a public issue. See SECURITY.md for the reporting process and supported versions.


License

Dual-licensed under Apache-2.0 or MIT — see LICENSE, LICENSE-APACHE, and LICENSE-MIT.


Credits

Author & maintainer: EslaM-X

About

EVMX — an evidence-backed EVM transaction forensics engine. Give it a tx hash and get a verdict you can check: named patterns, five pieces of trace evidence, and an explicit INCONCLUSIVE instead of a false certainty. Ethereum + Arbitrum One, with a reproducible offline corpus.

Topics

Resources

Code of conduct

Contributing

Security policy

Stars

1 star

Watchers

1 watching

Forks

Releases

Packages

Contributors

Languages