How a scan works
From an address to a verdict, step by step. Every step is a read against the chain; nothing is sent, signed or paid.
A scan takes one input, a hook address on Robinhood Chain (chain id 4663), and reads everything else from the chain at the latest block. It sends no transaction, needs no wallet and spends no gas. The block it read at is recorded on the report.
1. The address
The last 14 bits of a v4 hook's address are its permissions. They are mined into the address with CREATE2 when the hook is deployed, and the PoolManager (0x8366a39CC670B4001A1121B8F6A443A643e40951) calls only the callbacks they switch on. The scan decodes them into the permission register: fourteen lines, each on or off. See The permission bits.
2. The code
The scan fetches the runtime bytecode at the address. If there is none, the address is not a contract and there is nothing more to read.
It then looks for a proxy. It reads the three EIP-1967 storage slots (implementation, admin, beacon) and checks whether the code is an EIP-1167 minimal clone. If the hook forwards to an implementation, the implementation's code is the one that is read, since it is the code that runs.
The code is walked opcode by opcode, skipping push data and the compiler's metadata at the end. The walk collects:
- the function selectors the dispatcher compares against;
- whether DELEGATECALL, SELFDESTRUCT or CREATE appear;
- how many outside calls (CALL and CALLCODE opcodes) the code contains;
- the constant addresses pushed by the code, such as the PoolManager, a router or a fee wallet.
3. Who holds it
The scan calls owner() on the hook. An answer other than the zero address is recorded as the owner.
The selectors found in step 2 are looked up in the public signature database at openchain.xyz, so a function nobody published still has a name. Names that look like an owner's tools (set…, update…, withdraw…, pause, blacklist…, mint…, upgrade… and similar) are flagged.
4. A stranger knocks
Each callback the address switches on is called directly, with eth_call, from an outside address and a harmless pool key. Every one should refuse: a hook is meant to answer only the PoolManager. Any callback that does not refuse is listed as open.
5. The pools
censoredHooks keeps an index of every pool ever created on the PoolManager (every Initialize event since genesis), grouped by hook, with each hook's pool count, first and last pool time, and its most recent pools. For a hook that is not in the index yet, the scan searches recent blocks for its pools.
The scan picks up to two of the hook's most recent pools that have a price, and reads their state straight from the PoolManager's storage with extsload: the square-root price, the tick, the LP fee, the protocol fee, and the liquidity at the current price.
6. The probe
For each of those pools a probe contract is placed at an empty address for the length of one eth_call, using a state override. Inside that call it unlocks the PoolManager, swaps a small amount through the real pool (and so through the hook), and reverts with what it received. It does this once in each direction. A second probe adds a little liquidity around the price and takes it straight back out.
Because the swap is sized to stay inside the current tick-spacing interval, what a hookless pool would have returned is known exactly. The difference, beyond the pool fee, is what the hook took. See The probe.
7. The grade
The facts from steps 1 to 6 are graded into eight checks, each pass, caution, fail or n/a. The checks give a score from 0 to 100 and a verdict: danger if anything failed, caution if any check other than the track record raised a caution, safe otherwise. Safe means safe by construction: nothing that may take, no hand on a switch, a fixed fee, fixed code.
How long it takes
A scan makes a few dozen reads, three probe calls per pool (two swaps and a round trip), and a lookup in the signature database. It is not instant: expect seconds, not milliseconds. The board is a pass over the busiest hooks made in advance, so it loads at once; a hook's own page scans it again, live, every time it is opened.