Wallet forensics
Analyze a blockchain address and report what it actually holds, what it lost, and what still puts it at risk.
When to use this
Trigger on requests like:
- "Analyze this wallet: 0x…" / "What's in this address?"
- "How much have I spent on gas?"
- "Have I been sandwiched?" / "How much have I lost to MEV?" (EVM chains)
- "Are any of my approvals dangerous?" / "What should I revoke?"
- "Can I actually sell this token?" / "Is this position liquid?"
- "What's my PnL on this wallet?"
- A bare address pasted with any question about it
The core insight
Every portfolio tracker computes balance × spot price and calls it your net worth. For anything outside the top few hundred tokens that number is fiction. Spot price comes from the last trade, which may have been $40 against a pool holding $3,000. Selling a "$50,000" position into that pool does not yield $50,000.
This skill route-quotes the real sale and reports the gap. That gap is usually the most valuable thing in the report — lead with it.
Running it
node scripts/forensics.mjs <address> [options]
Requires Node 20+. No dependencies, no install step.
| Option | Effect |
|---|---|
--chain <list> | Comma-separated: ethereum,base,arbitrum,optimism,polygon,solana. Defaults to ethereum for 0x…, solana for base58 |
--all-evm | Analyze across every supported EVM chain |
--text | Human-readable summary instead of JSON |
--max <n> | Cap transactions fetched (default 2000) |
--no-mev | Skip sandwich detection — much faster |
--no-liquidity | Skip exit-liquidity routing quotes |
Output is JSON on stdout by default. Read it and explain it in prose; do not dump raw JSON at the user.
Environment
Everything has a working public default. Two optional variables meaningfully improve results:
ETHERSCAN_API_KEY— optional. Without it, EVM history comes from Blockscout, which needs no key and returns full history on most chains. With it, history comes from Etherscan, which is more complete and more reliable — one key covers every EVM chain (their V2 API is unified), and the free tier is sufficient. Base's Blockscout instance is currently unreliable, so a key matters most there.SOLANA_RPC_URL— the public endpoint is heavily rate limited and will be slow or fail on active wallets. A Helius/Triton/QuickNode URL fixes this.COINGECKO_API_KEY— optional, raises pricing rate limits.
Warnings are load-bearing. A run may succeed partially — history truncated by --max, token balances lost to a rate limit, an approval scan degraded by an RPC that refuses unbounded log queries. Each of those is reported in warnings, and each makes some headline number a floor rather than a total. Read them before presenting any figure as complete.
Reading the output
The JSON has this shape:
{
"chains": [{ "chain", "activity", "fees", "positions", "approvals", "mev", "liquidity", "regrets", "warnings" }],
"totals": { "realizedPnlUsd", "unrealizedPnlUsd", "feesUsd", "mevExtractedUsd",
"portfolioNominalUsd", "portfolioRealizableUsd" },
"topRegrets": [{ "kind", "title", "detail", "costUsd" }]
}
Lead with topRegrets. It is already ranked by dollar cost across every category, and it is what the user actually wants to know.
The headline number is portfolioNominalUsd vs portfolioRealizableUsd. If they diverge by more than a few percent, that gap is the story: "Your tracker says $84,000. You could actually get about $31,000 out."
Check warnings first. They tell you which numbers are trustworthy. "History truncated" means wallet age and lifetime fees are floors. "Token balances unavailable" means the portfolio total is a floor. "Approval scan degraded" means an empty approvals list proves nothing.
Approvals are ranked critical / high / medium / low by what could be taken right now — the smaller of the allowance and the current balance. An unlimited approval on an empty wallet is not urgent; the same approval on their main bag is.
MEV events carry a confidence field of high / medium / low. Report it. A low-confidence sandwich is a maybe, not a fact.
Sandwich detection is EVM-only and reads full blocks, which public RPCs often refuse. If warnings says blocks could not be read, an empty MEV list means not checked, not not sandwiched — never report the latter.
Interpreting responsibly
- Cost basis is inferred, not authoritative. Value is derived from each trade's stablecoin or native leg. Trades with neither are counted in
warningsand excluded from PnL. Never present these numbers as tax-ready. - Exit liquidity is a point-in-time quote. It moves with the market and ignores CEX depth entirely. A token may be perfectly sellable on Binance while looking illiquid on-chain.
- Absence of evidence is not evidence of absence. If approval scanning was degraded (public RPCs reject unbounded log queries), the report says so in
warnings. Do not tell someone their approvals are clean when the scan was partial. - Do not give financial advice. Report what the data shows. "This position has 85% price impact to exit" is a fact. "You should sell" is not yours to say.
- Revoking is the user's action. You can explain which approvals are risky and link to revoke.cash or the relevant explorer. Never construct or send a transaction.
Privacy
An address is pseudonymous but not anonymous. Analyze the address the user gives you. Do not go looking for other addresses belonging to the same person, and do not cross-reference an address against identity sources.
Deeper detail
Load these only when the specific question calls for it:
references/methodology.md— how cost basis inference, sandwich detection, and exit-liquidity simulation actually work, and where each breaks downreferences/interpreting-results.md— worked examples of turning a report into a useful explanation, with common misreadings to avoid