Read it yourself: who controls a contract, with a public RPC and curl

Two readers look at every tracked contract. The scorer feeds the score. The independent reader shares no code with it and answers one question: who can change this contract, and how many of them. Everything it does is a plain JSON-RPC call, listed here, so you can repeat it without trusting our code.

The calls, in the order the reader makes them

Set RPC to a public endpoint of the contract's chain, replace TARGET with the contract address and HOLDER with the address found in step 2 or 3 (left-padded words: the address is the last 20 bytes). Each contract page has these calls prefilled under "Reproduce this read yourself".

# 1. Is there code at the target? 0x means no: a native identifier, not a contract
curl -s -X POST $RPC -H 'content-type: application/json' -d '{"jsonrpc":"2.0","id":1,"method":"eth_getCode","params":["TARGET","latest"]}'

# 2. The EIP-1967 admin slot of a proxy: a non-zero word is the admin (a key, a ProxyAdmin or a Safe)
curl -s -X POST $RPC -H 'content-type: application/json' -d '{"jsonrpc":"2.0","id":1,"method":"eth_getStorageAt","params":["TARGET","0xb53127684a568b3173ae13b9f8a6016e243e63b6e8ee1178d6a717850b5d6103","latest"]}'

# 3. Otherwise owner(): the last 20 bytes of the answer are the holder
curl -s -X POST $RPC -H 'content-type: application/json' -d '{"jsonrpc":"2.0","id":1,"method":"eth_call","params":[{"to":"TARGET","data":"0x8da5cb5b"},"latest"]}'

# 3b. Or admin()
curl -s -X POST $RPC -H 'content-type: application/json' -d '{"jsonrpc":"2.0","id":1,"method":"eth_call","params":[{"to":"TARGET","data":"0xf851a440"},"latest"]}'

# 4. Classify the holder. eth_getCode on it: 0x is a plain key
curl -s -X POST $RPC -H 'content-type: application/json' -d '{"jsonrpc":"2.0","id":1,"method":"eth_getCode","params":["HOLDER","latest"]}'

# 5a. A Safe: how many signatures are needed
curl -s -X POST $RPC -H 'content-type: application/json' -d '{"jsonrpc":"2.0","id":1,"method":"eth_call","params":[{"to":"HOLDER","data":"0xe75235b8"},"latest"]}'

# 5b. A Safe: who can sign (the answer lists the owner addresses)
curl -s -X POST $RPC -H 'content-type: application/json' -d '{"jsonrpc":"2.0","id":1,"method":"eth_call","params":[{"to":"HOLDER","data":"0xa0e67e2b"},"latest"]}'

# 5c. A timelock: the minimum delay in seconds (OpenZeppelin)
curl -s -X POST $RPC -H 'content-type: application/json' -d '{"jsonrpc":"2.0","id":1,"method":"eth_call","params":[{"to":"HOLDER","data":"0xf27a0c92"},"latest"]}'

# 5d. A timelock: the delay in seconds (Compound style)
curl -s -X POST $RPC -H 'content-type: application/json' -d '{"jsonrpc":"2.0","id":1,"method":"eth_call","params":[{"to":"HOLDER","data":"0x6a42b8f8"},"latest"]}'

# 6. The code behind a proxy: a change in this word is an upgrade
curl -s -X POST $RPC -H 'content-type: application/json' -d '{"jsonrpc":"2.0","id":1,"method":"eth_getStorageAt","params":["TARGET","0x360894a13ba1a3210667c828492db98dca3e2076cc3735a920a3ca505d382bbc","latest"]}'

How to read the answers

What the reader found, in numbers

198 reads in the published file, by what the holder turned out to be (second_opinion.json):

safe46
unreadable38
contract35
timelock23
squads17
eoa16
native16
pda7

What this reader does not probe

38 of the 198 reads are "unreadable": none of the steps above gave a holder. Known blind spots, found by checking twelve pages against the chain on 12 October 2026: contracts whose control sits behind governance(), gov(), feeToSetter() or upgradeAdmin() are not read by these steps, and an owner chain longer than two hops stops at the second. The scorer and the reader can both be wrong; a contract page says when they disagree.

If your own read differs from what a page shows, open an issue with your call and the answer: a disagreement is published, not smoothed.

Slots and selectors on this page are read from the reader's source at build. Not an audit, not investment advice. Back to the dashboard.