discover hidden admin power structures in EVM contracts.
owner()tells you who the contract says is in charge.shadow-admintells you who actually is.
most governance analysis stops at "who is the owner?" but real admin power is distributed across roles, modules, timelocks, and proxy patterns that aren't visible from the surface. shadow-admin digs deeper:
- AccessControl roles — scans for DEFAULT_ADMIN_ROLE, PAUSER_ROLE, MINTER_ROLE, UPGRADER_ROLE holders, including through proxy contracts
- TimelockController — maps proposer/executor/canceller/admin roles and flags zero-delay timelocks
- Safe modules — finds enabled modules that bypass multisig threshold (the most overlooked admin path)
- pause authority — identifies who can freeze a protocol's operations
- mint authority — finds unlimited minting capability and supply risk
- CREATE2 risks — detects metamorphic contract patterns (selfdestruct + CREATE2)
- deprecated permissions — flags stale EOAs and zero-nonce addresses still holding roles
- admin chain resolution — recursively traces admin → timelock → multisig → EOA ownership trees
npm install
npm run buildnpx shadow-admin scan 0x1234...abcdnpx shadow-admin scan 0x1234...abcd --chain arbitrumsupported chains: ethereum, arbitrum, optimism, base
npx shadow-admin scan 0x1234...abcd --deepnpx shadow-admin scan 0x1234...abcd --jsonnpx shadow-admin scan 0x1234...abcd --rpc https://my-rpc.example.comnpx shadow-admin scan 0x1234...abcd --scanners access-control,timelockavailable scanners: access-control, timelock, safe-modules, pausable, mintable, create2, deprecated
shadow-admin v0.1.0
scanning 0x1234...abcd on Ethereum (ethereum)
[1/7] scanning AccessControl roles...
[2/7] scanning TimelockController roles...
[3/7] scanning Safe modules...
[4/7] scanning pause authority...
[5/7] scanning mint authority...
[6/7] scanning CREATE2 risks...
[7/7] scanning deprecated permissions...
--- results ---
found 3 shadow admin path(s):
[CRITICAL]
0xaaaa...aaaa
type: access-control-role
source: AccessControl.DEFAULT_ADMIN_ROLE
details: holds DEFAULT_ADMIN_ROLE — can grant/revoke any role...
[HIGH]
0xbbbb...bbbb
type: pauser
source: Pausable via owner()
details: can call pause(), unpause() on the contract...
[MEDIUM]
0xcccc...cccc
type: create2-deployer
source: bytecode analysis (CREATE2 opcode)
details: contract bytecode contains CREATE2 opcode...
--- admin chain ---
0x1234...abcd (contract)
└── 0x5678...5678 (Timelock (24h delay))
└── 0xabcd...abcd (Safe 3-of-5)
├── 0x1111...1111 (EOA) ← SINGLE KEY
├── 0x2222...2222 (EOA) ← SINGLE KEY
├── 0x3333...3333 (EOA) ← SINGLE KEY
├── 0x4444...4444 (EOA) ← SINGLE KEY
└── 0x5555...5555 (EOA) ← SINGLE KEY
| Variable | Description |
|---|---|
RPC_ETHEREUM_URL |
Ethereum RPC endpoint |
RPC_ARBITRUM_URL |
Arbitrum RPC endpoint |
RPC_OPTIMISM_URL |
Optimism RPC endpoint |
RPC_BASE_URL |
Base RPC endpoint |
npm run build # compile TypeScript
npm test # run tests
npm start # run from sourceMIT