📖 ARCHITECTURE.md — how the whole system works (pipeline, agent & contract lifecycle, providers/models, dashboard API). 🚀 DEPLOYMENT.md — set it up on a fresh host. 🔒 SECURITY.md — authorized use only; responsible disclosure; the dashboard is an unauthenticated control plane — keep it private.
(Internal docs deliberately live here, not in
CLAUDE.md/AGENTS.md, because those are auto-read by the audit agent and would confuse it.)License: add a
LICENSEfile before publishing — pick one that fits your intent (e.g. MIT/Apache-2.0). This is research tooling; see SECURITY.md for the use policy.
Continuously find funded smart contracts on ETH/BSC and queue them for an AI agent (pi.dev) to deep-audit one at a time in isolation. A secondary one-shot mode runs static analysis (Slither / bytecode) + a Foundry fork-fuzz over an address list.
Why "funded" is the only pre-filter that matters: contracts with obvious high-severity bugs that hold money are almost always already drained by sweeper bots. The edge is a deeper auditor finding non-obvious bugs — so we just surface funded contracts and let the agent do the real work.
discover → liveness/value filter → source resolve → static analysis → score → report → fork-fuzz
This is a research / triage tool. Its output is a candidate list, not confirmed exploits, and the legitimate destination for any finding is responsible disclosure or a bug-bounty / audit program — never on-chain draining.
cd /path/to/guml
PY=~/.venvs/guml/bin/python
# 1) Crawl funded contracts into the SQLite queue (guml.db).
# --source rpc = recent/new contracts (no account); --source bigquery = old/bulk (needs GCP auth).
$PY -m guml.crawl --chain eth --source rpc --min-balance-eth 0.05 --loop
# 2) Audit workers: each claims one contract (row-locked) and runs pi.dev on it,
# isolated in audits/<chain>/<address>/. Run up to ~5 in parallel.
$PY -m guml.audit --worker w1 --loop &
$PY -m guml.audit --worker w2 --loop &Each audit folder gets AGENTS.md (the brief pi reads automatically), bytecode.txt,
contract.json, verified source.sol when available, best-effort decompiled.sol for
unverified contracts when Heimdall succeeds, and a Foundry PoC
scaffold at foundry/test/Exploit.t.sol. The agent writes findings.json and completes
the PoC. The worker then runs the PoC on a local fork: if it passes (provably extracts
value), the contract is marked vulnerable and its exploit + proof are staged to
confirmed/<chain>/<address>/. PoCs run on a local anvil fork only — never broadcast.
The pi.dev agent is installed by setup/install.sh (standalone Node 22 +
@earendil-works/pi-coding-agent under ~/.guml-pi — kept off the repo mount to
avoid WSL DrvFs corruption; resolved automatically by guml/tools.py). It uses the
provider in ~/.pi/agent/models.json with OPENROUTER_API_KEY from .env. Swap the
model via the PI_MODEL env var or that file. Without pi, the worker still prepares
each workspace and writes a placeholder.
Model note: the configured free model
nvidia/nemotron-3-ultra-550b-a55b:freeworks but is very slow (~74s for 20 tokens) — fine for validating wiring, impractical for real agentic audits. PointPI_MODELat a faster model for real runs. SetPI_MODEL_FALLBACKS(comma-separated) or thepi_model_fallbackssetting to continue the same audit with another model after quota/auth/provider failures.
$PY -m guml.dashboard # http://127.0.0.1:8000A control panel, not just a viewer:
- Queue stats + which worker is on which contract, refreshed live.
- Click any contract to watch the pi agent's live output (
pi_live.log) and itsfindings.json. - Controls: pause/resume all workers, pause/stop a single worker, and kill a stuck pi (requeues the contract).
- Switch the audit model at runtime from the model box — takes effect on the next contract each worker claims.
- Confirmed vulnerabilities with links to proof + exploit code.
The audit agent is provider-agnostic: pi supports OpenAI-compatible and Anthropic-compatible
endpoints plus OpenRouter — configure providers in pi/models.json and keys in .env
(OPENROUTER_API_KEY / OPENAI_API_KEY / ANTHROPIC_API_KEY), then pick the model in the UI.
- BigQuery source (
--source bigquery, ETH only):gcloud auth application-default loginonce. - Queue (
guml.db), workspaces (audits/), and confirmed exploits (confirmed/) are gitignored.
| Stage | Module | What it does |
|---|---|---|
| Discover | discover.py |
candidate addresses from a CSV export (BigQuery/Dune) or an RPC block-scan |
| Liveness | liveness.py |
drop EOAs / self-destructed; record native balance (value-at-risk) |
| Source | explorer.py |
Etherscan V2 (one key, chainid 1 or 56) → verified source or "unverified" |
| Static | analyze/slither_runner.py |
Slither detectors on verified source (high confidence) |
| Static | analyze/bytecode_runner.py |
opcode scan + heimdall decompile on unverified bytecode (low confidence) |
| Score | score.py |
combine finding severity × confidence, weighted by value-at-risk → 0–100 |
| Report | report.py |
ranked.csv + per-contract markdown |
| Dynamic | dynamic/foundry_harness.py |
anvil fork + invariant fuzz: can an unprivileged caller drain it? |
Runs on Linux or WSL (the security tools are Unix-first). See DEPLOYMENT.md for the full setup.
Installed by the setup step:
- Python venv at
~/.venvs/gumlwithweb3,slither-analyzer,solc-select, … solc(via solc-select),heimdall(via bifrost),forge/anvil/cast(Foundry), Rust
-
Toolchain — already installed into WSL (
~/.venvs/guml, Foundry, heimdall, solc). To reproduce on another machine, re-runsetup/install_wsl.sh(see that file). -
Secrets — copy and fill in:
cp .env.example .env
ETHERSCAN_API_KEY— required for verified-source analysis. One free key from https://etherscan.io/apis works for both ETH and BSC (Etherscan V2). Without it, the pipeline still runs in bytecode-only mode.ETH_RPC_URL,BSC_RPC_URL— public defaults are pre-filled; a paid node is faster.
-
Discovery data (optional, for bulk runs):
- ETH: run
old_contracts.sqlin BigQuery, export theaddresscolumn to a CSV. - BSC: a Dune query on BNB-chain contracts, exported the same way.
- Or skip both and use
--scan START ENDto discover over RPC.
- ETH: run
From WSL, via the wrapper (sets the venv + path for you):
# Analyze an exported address list on BSC, keep contracts holding >= 0.1 BNB
bash run.sh --chain bsc --in addresses.csv --min-balance-eth 0.1
# Discover by scanning a recent block range on ETH, then fork-fuzz the top hits
bash run.sh --chain eth --scan 19000000 19000200 --dynamic --dynamic-top 5
# Quick smoke of the analyzers (no RPC/key needed)
PYTHONPATH=. ~/.venvs/guml/bin/python setup/smoke.pyOutput lands in out/<chain>/: ranked.csv, ranked.json, per-contract *.md,
and (with --dynamic) a Foundry project + probe_result.txt per top candidate.
Performance note: the static pipeline is fast, but --dynamic fork-fuzzing is
network-bound — on a public RPC it can take ~5 min per contract (lazy fork-state
fetches). Use a paid/low-latency RPC and a small --dynamic-top for real runs, or
tune [invariant] runs/depth in the generated foundry.toml.
~/.venvs/guml/bin/python -m pytest- Output is a ranked candidate list; confirm by hand + simulation before believing it.
- BSC contracts are mostly unverified → the noisier bytecode path dominates there.
- The fork-fuzz invariants are heuristics: expect false positives (legit permissionless flows) and false negatives (multi-step / token-based exploits).
- Funded + verified + known-vuln contracts on ETH are heavily swept already.