Understudy
Migrates keeper duties off a dead automation service. It finds every contract that still trusts the old keeper, rebuilds the jobs, and only revokes once a judge has verified the replacement.
- Role
- Design and implementation
- When
- 2026
- Stack
- TypeScript
- Foundry
- viem
- MCP
- Blockscout
When an automation service shuts down, its address does not leave with it. It stays embedded in contract roles and allowlists across every protocol that trusted it, and the old key cannot be exported. Someone has to find each one and hand the job over without leaving the protocol unattended in between.
Scout discovers contracts through Blockscout, an analyst resolves their role hashes, jobs are rebuilt and shadow run, an adjudicator rules on the difference, and only then is the old keeper revoked.
Find what still trusts it
Discovery runs through Blockscout rather than a node RPC, because archive access is what makes historical role grants visible at all.
Resolve the roles
Contract roles are namespaced hashes with no global registry to look them up in, so an analyst reads the contract source and recovers the string that produced each hash.
Role classification reached 89.7% accuracy, 26 of 29, against a 72.4% baseline.
Shadow before you cut over
The rebuilt job runs alongside the live one, and an adjudicator compares the two to separate genuine regressions from benign cascades. Adjudication scored 100%, 8 of 8, including deliberately planted twin scenarios.
Grant, verify, revoke, attest
Cutover is staged, and three real settlements must be observed before the old keeper is revoked. Across the run there were zero uncovered samples: no moment where the protocol had no keeper at all. One of five contracts was migrated end to end, with 16 transactions mined.
The ordering trap has no safe middle. Revoke too early and the protocol sits unattended, a liquidation is missed and real money leaves. Revoke too late and a dead credential keeps production write access. The only resolution was to make revocation conditional on a verdict rather than on a timer, which is why the adjudicator exists at all.
Models will confidently invent a role name that hashes to nothing. No name is returned unless keccak256 of the model's answer reproduces the hash actually observed on chain; anything else is discarded and counted separately. The guard is cheap and it is the only reason the classification number means anything.
A 100% agreement score can be worth nothing. One run reported perfect agreement between shadow and live and returned READY, but it had observed no state transitions at all, so there was nothing to disagree about. Identical agreement rates produced opposite verdicts depending on whether anything had actually happened.
That vacuous-agreement case is a designed fix that was not built. A verdict should require a minimum number of observed state transitions before agreement counts for anything, and until that exists the quiet-window READY is a known false positive rather than a solved problem.
Everything ran on a testnet against a protocol under my own control, with a test keypair standing in for the dead keeper. Scores come from a small, cheap model; a larger one reaches 93 to 100% on the same classification task, so the reported accuracy is a floor rather than a ceiling.