Built for Agents Onchain — KeeperHub × DoraHacks
Revoker
Takes the approval away before it can be used.
$revoker watch
TypeScript · Solidity 0.8.28 + Foundry · viem · KeeperHub · Ethereum Sepolia
The most common wallet-drain vector
approve(spender, MAX_UINT256)
You signed it once, months ago. It never expires — and the day that contract turns malicious, the allowance is still live.
The problem
The tools that act need a human to authorise them.
Automated revoking now ships for consumer wallets — Revoke.cash Ultimate, MetaMask Advanced Permissions. Every one routes through a human granting a permission in a wallet UI. A keeper wallet has no human and no wallet UI, so that funnel never reaches it.
A spender you approved has been flagged. Your allowance is unlimited.
allowance = MAX_UINT256 · granted 214 days ago
AcknowledgeFor a headless signer the state of the art ends here — a notification, and a button that does nothing on-chain.
The solution
Revoker acts.
An autonomous agent that watches a wallet’s live approval set and executes
approve(spender, 0) through KeeperHub the instant a threat rule fires —
no human in the loop.
Proof of execution · Ethereum Sepolia
A real drain, really stopped.
Keeper wallet grants unlimited approval
approve(spender, MAX_UINT256) to the drainer contract.
Revoker fires — no human involved
approve(spender, 0) via KeeperHub check-and-execute.
The drainer fires anyway
The drain tx succeeded — it just found nothing left to take.
We let the drain contract fire after the revoke. It succeeded — and
took 0
balance unchanged: 10,000 mUSDC
across the whole sequence
How it works
One loop. No human.
That loop runs over two allowance ledgers, not one. Permit2 — the shared approval
contract most of the ERC-20 ecosystem routes through — keeps its own. A grant written
there changes nothing in the token and emits no Approval event, so
every watcher built on those logs is blind to it by construction. Revoker reads both,
and revokes Permit2 with lockdown(): however many slots, one
transaction.
The sharpest technical idea
No stale revoke, ever.
read-then-write a typical agent
transferFrom in here —
before the revoke lands.
check-and-execute Revoker, via KeeperHub
Integration depth
KeeperHub is the engine.
Remove KeeperHub and Revoker needs seven separate systems — relayer,
gas oracle, MEV-protected route, confirmation poller, action discovery, ABI resolution,
audit pipeline — plus custody. The atomic check-and-execute
and the enclave-held key are the two pieces a standalone script cannot honestly replicate.
No key to steal. Signing happens inside a Turnkey enclave via KeeperHub — the agent process never holds a private key.
POST /execute/check-and-executethe atomic revokePOST /execute/contract-callcontract writesPOST /execute/transfernative transfersGET /execute/{id}/statusconfirmation · gas · audit recordGET /chainsnetwork + explorer resolutionGET /chains/{id}/abisource-verification signal, rule 1GET /user/walletsigner identity assertionGET /user/wallet/balancestoken discoveryGET·POST·PATCH /workflows3 endpoints — tooling, not the agentsimulate: truepre-flight dry runsIdempotency-Keysafe retries, no double-executionReliability · 25 full cycles against live Sepolia
Measured, not claimed.
Every bar is a real transaction. Cycles 1, 13 and 18 hit a slow block-inclusion window (amber) — the network’s variance, reported as a distribution, not hidden behind an average.
Scope · stated plainly
Known limits, on the record.
A security tool that hides its blind spots is part of the problem. These are Revoker’s.
Sepolia testnet only
The mainnet path is documented, not executed. No real funds were risked producing any number in this deck.
Token discovery is watchlist-scoped
No public RPC serves an address-less eth_getLogs over a useful range.
Revoker protects the tokens it is told to watch — not coverage it doesn’t have.
young-spender abstains loudly
Without an archive RPC it returns INDETERMINATE and names the remedy —
instead of silently reporting “safe”.
The threat model is narrow on purpose
A spender that is verified, aged, and off the deny-list trips nothing. That case is out of scope, not silently mishandled.
The ask
Open the receipts.
Judge Revoker on execution — that is the point. Every number in this
deck is a Sepolia transaction; the allowance we zeroed is still zero, and one
cast call proves it.
Expected output: 0
lockdown(), same story on the second ledger
arm upstream · 11445297
Permit2.approve · 11445298
lockdown() · 11445392
The drainer fired.
It found an empty room.