EVM bytecode decompiler and analyzer. Combines Heimdall
(pinned to 0.9.3) as the primary readable output with a native analysis
pipeline — disassembly, conservative CFG, selector and pattern recovery, a
TAC-like IR, and inferred Solidity-like codegen — merged into a single
confidence-scored result.
| Crate | Responsibility |
|---|---|
analysis-model |
Shared serde data model + JSON schema |
bytecode |
Hex validation, normalization, metadata stripping, hashing |
evm-disasm |
Opcode table + disassembler |
evm-cfg |
Basic blocks, conservative CFG, selector candidates |
evm-patterns |
Storage / event / error / call / calldata detectors |
evm-ir |
Bounded stack simulation → TAC-like IR + coverage |
solidity-codegen |
Inferred Solidity-like output |
result-merger |
Merges Heimdall + native facts; conflicts + confidence |
validator |
Fixture runner + metrics for the validation dashboard |
apps/api |
axum HTTP API, SQLite persistence, RPC lookup, exports |
apps/web |
React UI (analysis tabs + validation dashboard) |
- Rust (stable) and Cargo
- Node 24 + pnpm (via
corepack enable) - Heimdall
0.9.3onPATH(optional — the native pipeline runs without it):curl -L http://get.heimdall.rs | bash bifrost -v 0.9.3
cargo run -p api
# listens on :8080 (override with PORT)Configuration via environment variables:
| Var | Default | Meaning |
|---|---|---|
PORT |
8080 |
HTTP port |
HEIMDALL_TIMEOUT_SECS |
30 |
Heimdall subprocess timeout |
MAX_INPUT_BYTES |
1048576 |
Max bytecode size (raw bytes) |
DB_PATH |
:memory: |
SQLite path (use a file to persist) |
ALLOW_CUSTOM_RPC_URL |
false |
Permit client-supplied (SSRF-validated) RPC URLs |
LLM_REFINE |
false |
Enable the llm_refine_native stage (LLM-refined native output) |
LLM_TIMEOUT_SECS |
120 |
Timeout for the LLM refinement subprocess |
LLM_COMMAND |
claude |
LLM CLI command (non-interactive -p --output-format json) |
LLM_MODEL |
(CLI default) | Optional model id passed to the LLM CLI via --model |
With LLM_REFINE=true, an extra pipeline stage shells out to an LLM CLI
(Claude Code by default) to turn the conservative native skeleton into more
readable inferred Solidity — folding library patterns (SafeMath, EnumerableSet,
AccessControl) back into clean expressions. It uses whatever auth the CLI
already has (a logged-in session or its own key); this service never handles API
keys. The stage is non-fatal: any failure keeps the deterministic native
output. Refined output is marked generated_from: [..., "llm"] with an
llm_refined warning — it is plausible but not verified against the
bytecode. Best for local/personal use; a hosted multi-user service should use
the provider API directly rather than driving a subscription-authed CLI.
pnpm --dir apps/web install
pnpm --dir apps/web dev # Vite dev server, proxied to the APIcargo run -p validator -- fixtures # prints JSON metrics, exits non-zero on regressionPOST /api/v1/analyses # { input: { kind: "bytecode"|"address", ... } }
POST /api/v1/analyses/upload # multipart file upload (hex)
GET /api/v1/analyses/:id # job status + result
GET /api/v1/analyses/:id/export/solidity # readable output (.sol)
GET /api/v1/analyses/:id/export/json # full analysis (.json)
GET /api/v1/analyses/:id/export/cfg-dot # CFG (Graphviz .dot)
GET /api/v1/fixtures # validation dashboard metrics
Address lookup resolves chain from an explicit allowlist (ethereum, sepolia,
base, optimism, arbitrum). Custom RPC URLs are rejected unless
ALLOW_CUSTOM_RPC_URL=true, and even then loopback/private/link-local hosts are
refused (SSRF protection). Results are cached by bytecode keccak256 hash.
docker build -t reverser .
docker run -p 8080:8080 -v reverser-data:/app/data reverserThe image pins Heimdall 0.9.3 to match crates/heimdall-adapter.
Fixtures use a single growing format, seeded in Phase 3 (runtime hex +
expected.json) and promoted in Phase 8 to the full layout:
fixtures/
contracts/<name>/{Contract.sol, fixture.json} # source + compiler metadata
bytecode/<name>.runtime.hex # analyzed bytecode
expected/<name>.expected.json # partial ground truth
Expected facts are partial ground truth (selectors, event topics, storage summaries), not full reconstructed source.