
The Investigatoor
Paste an address, watch the graph fill in.
About
The problem it solves ## The auditor's problem Smart-contract investigations are the same five tabs every time: Sourcify, Etherscan, a JSON-RPC console, a notes file, and the friend who already audited this protocol. Auditors don't think in tables — they think in **graphs**. *Who deployed this. What does it call. What slot points where.* Existing tools are great at producing one of those facts at a time and terrible at stitching them together. When an exploit drops at 3 AM, you need a mental map *now* — not after an hour of copy-pasting hex between tabs. ## What investigatoor does Paste a contract address (or a tx hash). A long-running Claude agent fans out: - hits **Sourcify v2** for verified source, ABI, storage layout, proxy resolution, and deployment in a single call - finds the deployer EOA, the proxy implementation, the storage references that look like other deployed contracts - recurses outward, drawing nodes and edges *as it goes* Every contract becomes a node. Every relationship — deployment, storage-ref, call, delegatecall, admin — becomes an edge. Transcript on the left, live graph on the right, both updating in real time. When something looks wrong, you grab the canvas yourself: drag an edge, drop a sticky note, pin a block. ## Use cases - **Live investigation.** A new exploit hits — paste the attacker tx, get the full call tree as a graph in one shot, then start asking the agent questions. - **Pre-audit recon.** Map a protocol's contract surface (proxies, admins, modules, dependencies) before reading a single line of code. - **Onboarding.** Hand a junior auditor a graph instead of a Notion page full of addresses. ## What makes it different A workspace the agent and the human can both edit at the same time. You're not pasting output into Notion afterwards — you're watching the agent build the map while you read the transcript. Single binary, local-only, no backend, no DB. Your investigation is a folder on disk you can `git diff` and `grep`.
How it's made
Challenges we ran into ## Sandboxing the agent The agent is a spawned local `claude` CLI driven by the **Claude Agent SDK** with `settingSources: []` and only an in-process MCP server exposed (`mcp__investigator__*` tool prefix, plus `Read` / `Grep` so it can recover oversized tool results). No `Bash`, no `Write`, no `WebFetch` — the agent can only touch the EVM through tools we wrote and audited. ## Filesystem as the database `.app-state/` on disk is the source of truth: `contracts/<chain>/<addr>/` holds source, ABI, storage layout, deployer; `graphs/<id>/` holds the canvas state. A `chokidar` watcher streams every change to the browser as **Server-Sent Events**. The canvas updates because the file changed, not because the agent told it to — so two browser tabs on the same investigation see each other's edits for free. ## Block pinning, end-to-end A pinned block (`📌 21000000`) has to thread through *every* read path: storage probes, proxy-slot reads, `call_contract`, the live Functions panel. One missed read silently returns live state and breaks reproducibility. Solved by routing every read through a single helper that takes the graph's `pinnedBlock` and refuses to compile without it. ## Layout that doesn't overlap ELK gives nice layered placement but cards still collide after recursive expansion. Added a connected-components grid pass followed by a push-apart relaxation. The agent itself can call `get_layout` (returns bounding boxes + overlap pairs) and `auto_layout`, so it cleans up after a discovery burst instead of leaving a tangle. ## Tx-trace bootstrap `POST /api/graph/[id]/from-tx` runs `debug_traceTransaction` (callTracer), walks the frame tree, drops a node per unique address and an edge per call — selectors decoded via the **Sourcify signature database** (`api.4byte.sourcify.dev`), with newly-seen signatures contributed back via `/import` so the registry grows from real investigations. You get a populated canvas *before* the agent says a word.