How a grade is made, and how to check it.
A CULET grade answers one question: if you put a small amount into this pool and immediately took it back out, how much would be left, and how much of the loss came from the hook? Everything below is how that answer is produced, at which block, and how you can produce it yourself without CULET.
1 · Pin one block
The grader reads the latest block number and pins every read and both simulations to the block just before it. The pool's price, liquidity and fee in the report all come from that single block, which is printed on the report.
2 · Pick a test size
0.01 ETH when the pool pairs with ETH or WETH, 25 USDG when it pairs with USDG, and one thousandth of the pool's in-range reserve otherwise. If the size would be more than 5% of the pool's reserve on that side, it is cut to 2% and the report says so, so a thin pool is not graded as a trap just because the test moved its price.
3 · Run A, through the real hook
A probe contract is placed at an unused address for the length of one eth_call, using a state override. It calls PoolManager.unlock, swaps the test amount in, swaps the entire output back, and then reverts with both amounts. Because it reverts, nothing is settled, nothing is transferred, and no wallet is involved.
4 · Run B, with the hook switched off
The same call again, at the same block, with one more override: the hook's address is given a stand-in contract that answers beforeSwap and afterSwap with the correct selector and zero deltas. The pool's own maths runs untouched. The difference between run A and run B is what the hook took.
5 · Run C, with real token transfers
Runs A and B never move a token, so a token that taxes transfers or refuses to be sold would slip through. Run C repeats the round trip and settles it for real inside the simulation: the pool pays the token out to the probe, and the probe pays it back. The probe is funded through the same state override, and the call is made from the probe's own address so it behaves like an ordinary wallet. The report shows the buy tax, the sell tax, and whether the token itself stopped the sale.
6 · Grade
The loss of run A sets the clarity grade. If run A fails on the sale but run B succeeds, the hook blocked the sale. If both fail, the pool itself cannot complete the trip. Both cases are graded I3 and say which one it was.
Clarity scale
The letters are borrowed from diamond grading, where they count inclusions. Here they count what a round trip loses at the test size.
| Grade | Name | Rule |
|---|
A grade describes a pool at one block. A hook that runs on every swap can behave differently at another block or for another size, so the report lists what the hook is allowed to do alongside what it did.
The probe
The whole simulator is the contract below: under 160 lines, no owner, no storage, never deployed. It only exists inside a call. The bytecode your browser sends is compiled from exactly this file with solc 0.8.26.
Loading contracts/src/Probe.sol…
Simulation vs execution
A simulator is only useful if it matches reality. The tests below fork Robinhood Chain and check three things. First, the probe's quote equals a real swap that settles and takes the tokens, to the last unit, on a plain pool and on a pool under the Pons launch hook. Second, the settled round trip (run C) equals a real buy followed by a real sell. Third, two booby-trapped tokens are deployed on the fork, one with a hidden 5% transfer tax and one that refuses sales, and the probe must catch both exactly. It does, and the tests also show that a simulation without real transfers would have missed them.
Loading forge output…
Reproduce a grade
- Open any report and expand Re-run this grade yourself. It shows the exact JSON-RPC request your browser sent for run A: the probe bytecode as an override, the encoded call, and the block.
- Paste it into a terminal. Any Robinhood Chain RPC that keeps state for that block will answer; public endpoints keep only recent blocks, archive endpoints keep all of them.
- Decode the answer with the ABI of
roundTrip(PoolKey,bool,uint256): it returns(stage, received, returned, reason). Stage 2 means both swaps completed.1 − returned ÷ amountInis the loss on the report. - For run B, add the stand-in hook bytecode at the hook's address in the overrides and send it again.
eth_call feature.Data and snapshot
The pool list comes from every Initialize event the PoolManager has emitted, scanned in 50,000-block windows from block 0 to the snapshot block. Pools created after the snapshot are found live by your browser, from the snapshot block to the current head.
| File | What it holds |
|---|---|
| census.json | Totals by fee bucket, hooked and plain pools, the 60 largest hooks, first and last block. |
| live.json | The rolling 24-hour count of swaps that paid more than 50% and 90%, with the 40 most recent, each with its transaction hash. |
| hooks.json | Every hook address seen in an Initialize event, with the number of pools using it. |
| hooknames.json | Verified contract names from Sourcify for the 150 largest hooks. |
| /data/tok/<3 hex>.json | Pools by token, sharded by the first three hex characters of the address. |
| /data/pid/<3 hex>.json | Pools by pool id, sharded the same way. Each row's id is re-derived in the browser before use. |
What runs where
| Part | Where it runs | Status |
|---|---|---|
| Pool grades | Your browser, against public Robinhood Chain RPC endpoints, pinned to one block. | Live |
| Hook inspection | Your browser: code, EIP-1967 slot and owner() read from chain; names from Sourcify. | Live |
| Swap feed | Your browser reads new Swap events every few seconds. The fee in each event is the fee actually charged. | Live |
| Pool snapshot | Static files built from Initialize events, refreshed on a schedule. | Snapshot |
| 24-hour count | Built from every Swap event in the window, refreshed on a schedule. | Rolling |
| Probe vs execution | Six forge fork tests, output published above. | 6 / 6 passing |
| Wallet permissions | Your browser reads approval events from Blockscout and current allowances from the chain. Incomplete reads are reported, never shown as clean. | Live |