Autonomous on-chain security agent · built on KeeperHub
The agent that lands the revoke before the drainer moves.
Token approvals are the most common wallet-drain vector, and every tool built
for them is read-only: it tells you an approval is risky, and stops there.
Revoker acts. The moment a threat rule fires it executes
approve(spender, 0) through KeeperHub, with no human in the loop.
Every number on this page links to a transaction you can open.
(p95 24.88s)
succeeded
sponsored 25/25
Proof of execution
A real drain, really stopped.
The full cycle, executed on Ethereum Sepolia. Three transactions, in order. Open each one.
-
The trap
Victim grants unlimited approval
The everyday act that arms every drain:
approve(spender, MAX_UINT256)to a spender contract. Allowance jumps to 1.157e77 — effectively infinite. -
The response
Revoker fires — no human involved
Two rules trip —
unlimited-to-unverifiedanddenylisted. Revoker executesapprove(spender, 0)through KeeperHub's atomiccheck-and-execute, with no human in the loop. -
The verdict
The drainer fires anyway
The drain transaction succeeded. It did not revert. It was not blocked. It ran exactly as its author intended — and it found nothing to take, because the approval was already gone.
Step 3 is the whole story. Revoker doesn't intercept the attack — it makes the attack find an empty room.
took 0
victim's balance unchanged: 10,000 mUSDC across the whole sequence
Reading the explorer links: KeeperHub
executes through a sponsored relay, so the from address is KeeperHub's relayer —
not the signer. The flow is relayer 0x809d…0444 → forwarder 0x5af5…f07d →
Turnkey account 0x5E2e…bab7. The signer appears in the calldata; this is expected,
not an anomaly.
Don't take our word for it
Check the allowance yourself, right now.
One cast call against a public RPC. No credentials, no account,
no trust in this page required.
cast call 0x4facb5FD1682c4449cAD42b7590861f7eD5c88Cb \
"allowance(address,address)(uint256)" \
0x5E2e5Fd3aD7fDC9B94482930db8b5F45E439bab7 \
0x8eBf8540EdE8e40CD94825C418758d4029D8892e \
--rpc-url https://ethereum-sepolia-rpc.publicnode.com
Expected output: 0 — the allowance Revoker zeroed is still zero.
The technical differentiator
A revoke that can't be front-run.
Most agents read state, decide, then write. That gap between deciding and acting is exactly what a mempool-watching drainer needs.
read-then-write a typical agent
transferFrom in here — before the revoke lands.
check-and-execute Revoker, via KeeperHub
The condition is evaluated against the live chain, not a value this process passed in. On the reference run KeeperHub reported, at execution time:
observedValue: 115792089237316195423570985008687907853269984665640564039457584007913129639935= 2256 − 1 — the MAX_UINT256 allowance, re-read server-side.
No key to steal. Signing happens inside a Turnkey enclave via KeeperHub — the agent process never holds a private key. An autonomous agent with a hot key is a liability; one that can only ask a policy-bound signer to act is not.
Detection
Three rules. No model.
Concrete, auditable threat conditions. Every firing carries its evidence into the audit trail, so a revoke can be justified after the fact.
unlimited-to-unverified
A MAX_UINT256 allowance granted to a contract whose source is unreadable —
infinite trust in code nobody can inspect.
signal: KeeperHub ABI resolution, live
young-spender
The spender contract was deployed less than 7 days ago. Fresh contracts holding spend rights are a classic drain setup.
signal: eth_getCode + binary search
denylisted
The spender is on the known-bad list. No further debate — a confirmed bad actor with an open allowance gets revoked.
signal: data/denylist.json
Any one rule firing is sufficient. These are independent signals of different kinds, not weighted terms in a score — requiring consensus would mean ignoring a confirmed deny-list hit because the contract happened to be verified. And deliberately not an ML risk model: an agent that moves funds on an opaque score is not auditable, and “the model said so” is not a defence when it is wrong.
Benchmark
Measured, not claimed.
25 full detect→revoke cycles against live Sepolia, each one confirmed by reading the chain — not by trusting the execution report.
| Metric | p50 | p95 | min | max |
|---|---|---|---|---|
| response — detection → revoke confirmed | 12.95s | 24.88s | 10.33s | 24.95s |
| exposure — threat live → revoke confirmed | 13.38s | 25.01s | 10.47s | 25.28s |
What these numbers do not include. The benchmark triggers detection immediately
rather than waiting for the poll timer, so a deployment polling every
pollIntervalMs adds an average of pollIntervalMs/2 on top.
The p95 is nearly double the p50 because four consecutive cycles hit a slow block-inclusion window — that variance is the network's, not the agent's, which is exactly why this is reported as a distribution instead of a headline number. Full per-cycle table: BENCHMARK.md in the repo.
Integration
KeeperHub is the engine, not decoration.
Remove KeeperHub and Revoker needs seven separate systems: a transaction
relayer, a congestion-aware gas oracle, an MEV-protected submission route, a confirmation
poller, an action-discovery layer, an ABI resolution service, and an audit-log pipeline —
plus a custody solution. The sharpest dependency isn't convenience; it's that the
atomic check-and-execute and the enclave-held key are things a standalone
script cannot honestly replicate.
| Surface | Used for |
|---|---|
| POST /api/execute/check-and-execute | the atomic revoke |
| POST /api/execute/contract-call | contract writes, arming the demo approval |
| POST /api/execute/transfer | native transfers |
| GET /api/execute/{id}/status | confirmation, gas, sponsorship, audit record |
| GET /api/chains | network + explorer resolution |
| GET /api/chains/{id}/abi | source-verification signal for rule 1 |
| GET /api/user/wallet | signer identity assertion |
| GET /api/user/wallet/balances | token discovery (curated registry) |
| simulate: true | pre-flight dry runs before spending gas |
| Idempotency-Key | safe retries without double-execution |
Scope
Known limits, stated plainly.
A security tool that hides its blind spots is part of the problem. These are Revoker's, on the record.
-
Sepolia testnet only
The mainnet path is documented, not executed. No real funds were risked producing any number on this page.
-
Token discovery is watchlist-scoped
No public RPC serves an address-less
eth_getLogsover a useful block range. Revoker protects the tokens it is told to watch, rather than implying coverage it does not have. Production would resolve the set from an indexer. -
young-spenderneeds an archive RPCWithout one it returns
INDETERMINATEand names the remedy — loudly — instead of reporting “safe”. A threat rule that silently degrades into a rubber stamp is worse than one that admits it cannot see. -
The threat model is narrow on purpose
A spender that is verified, aged, and absent from the deny-list trips nothing. That case is out of scope, not silently mishandled.
-
The /verify dashboard runs locally
A continuous watcher cannot be a serverless function, so the live dashboard is served from the same long-lived process that does the watching.
Demo
Watch the full cycle.
Demo video — not yet published
The recorded run of the exact cycle proven above: seed the threat, watch Revoker detect and revoke it, then verify on-chain. This section will hold the embed.