Three parts, and two of them were already there
A Vigil account is not a Vigil contract. It is a Safe, with CANDIDE's Social Recovery Module switched on, and one guardian: Vigil.
Safe v1.4.1 (L2) you are the only owner, threshold 1
└── module: CANDIDE Social Recovery 7-day grace period, audited (Ackee, v0.0.1)
└── guardian: Vigil, threshold 1 the only guardian
└── the will heirs (1–5), how many must sign, the silence
Opening one is two transactions from your own wallet: createProxyWithNonce on Safe's factory (the account's address comes from your address and a slot number, so the page finds it again with no database), then one Safe transaction — a MultiSendCallOnly batch — that switches on CANDIDE's module, names Vigil as its only guardian, and writes the will into Vigil. Every owner action after that is one Safe transaction sent from your wallet with Safe's pre-validated signature, which Safe accepts only when the sender is that owner.
From a knock to the keys, call by call
- knock(account) — from an heir's wallet. Vigil records the time and the Safe's nonce (how many transactions it has ever executed). Refused unless the caller is named in the will (
NotHeir), and refused while another knock is still live (AlreadyKnocking). - the silence — nothing is called. Any Safe transaction the owner makes raises the nonce, and a knock whose nonce no longer matches is void (
OwnerActive). So does answer() ("I'm here") and rewriting the will. - claim(account) — from an heir, once the silence has passed to the second (
TooSoonbefore it). Vigil checks the nonce has not moved and that it is still the account's guardian, clears the knock, and calls CANDIDE's confirmRecovery(account, heirs, threshold, execute = true). That is its one power. - CANDIDE's 7 days — the module records a recovery that may be finalised after 7 days. The owner can call cancelRecovery() from the account until then; the page's "I'm here" button does exactly that at this stage.
- finalizeRecovery(account) — anyone may call it after the 7 days. CANDIDE removes the old owner and makes the heirs the owners, with the threshold the will set.
Silence alone does nothing: until an heir knocks, there is nothing to count. A knock is an event on chain (Knocked, naming the heir and the moment it may be claimed), so it is visible to anyone who looks — including the owner, on this site.
The one contract that is ours
contracts/Vigil.sol — 5,354 bytes of runtime, compiled with solc 0.8.26. It is not audited. It has no owner, no admin and no upgrade path; it holds no token and refuses ether. It has exactly five functions that write: setWill, revoke and answer (called by the account itself), and knock and claim (called by an heir). Everything else is a view.
It is deployed through the standard CREATE2 deployer with a fixed salt, so its address — 0xE9A164b27c6b117d124137D1402E227A18aA93E2 — is a function of its bytecode and its one constructor argument, CANDIDE's module. Anyone can rebuild it from this site's source and get the same address; if the address holds different code, it is not Vigil. Its current state: not deployed yet — the first account deploys it.
If it were wrong, the worst it could do is cast its vote too early — which starts CANDIDE's 7 days, which the owner can still cancel. It cannot move anything itself.
Found on this chain, and checked
Nobody announced CANDIDE's Social Recovery Module on Robinhood Chain, but its 7-day deployment is there, at the same address as on every chain CANDIDE lists: 0x088f6cfD8BB1dDb1BB069CCb3fc1A98927D233f2. Before using it, this site checked:
- its code hash is identical to the same address on Base, Arbitrum and Optimism, where CANDIDE deployed it and its source is verified;
- its recovery period — an immutable, baked into the code — reads 604,800 seconds (7 days) out of the runtime itself, and no 3- or 14-day value appears;
- it answers as "Social Recovery Module", version 0.0.1 — the version Ackee Blockchain audited;
- the 3-day and 14-day deployments are NOT on this chain, so the site never offers a grace period that does not exist.
Its README on GitHub describes a newer version, in which changing guardians cancels a pending recovery. The deployed 0.0.1 does not do that, so when you remove Vigil during a hand-over, the page cancels the hand-over first, in the same transaction. The site is built against the deployed source, saved in research/src/srm/, not against the documentation.
Everything passes on. Most of it has a price.
At block 67,307,975, 271 Morpho markets named 262 oracles, which named 82 feeds. 35 tokenized stocks came out of that with a Robinhood USD feed of their own, which is what the page prices them with. 15 did not — 15 of those because their only "oracle" reads a Uniswap pool — and they are still read, still shown and still inherited, just without a price.
| Stock | Robinhood feed | Price when read |
|---|---|---|
| AAPL Apple | Robinhood AAPL / USD | $335.38 |
| AMD AMD | RHAMD / USD | $559.42 |
| AMZN Amazon | Robinhood AMZN / USD | $253.86 |
| ASML ASML Holding NV | Robinhood ASML / USD | $1,676.63 |
| BABA Alibaba | Robinhood BABA / USD | $113.77 |
| CLSK CleanSpark | Robinhood CLSK / USD | $14.37 |
| COIN Coinbase | Robinhood COIN / USD | $193.91 |
| CRCL Circle Internet Group | Robinhood CRCL / USD | $91.76 |
| CRWV CoreWeave | Robinhood CRWV / USD | $81.33 |
| DELL Dell | Robinhood DELL-USD | $570.72 |
| EWY iShares MSCI South Korea fund | Robinhood EWY / USD | $180.93 |
| GME GameStop | Robinhood GME / USD | $22.55 |
| GOOGL Alphabet Class A | Robinhood GOOGL / USD | $350.47 |
| INTC Intel | RHINTC / USD | $109.05 |
| IONQ IonQ | Robinhood IONQ / USD | $39.09 |
| META Meta Platforms | Robinhood META / USD | $666.76 |
| MSFT Microsoft | RHMSFT / USD | $495.82 |
| MSTR Strategy Inc. | Robinhood MSTR / USD | $152.64 |
| MU Micron Technology | RHMU / USD | $1,014.43 |
| NBIS Nebius Group | Robinhood NBIS / USD | $222.71 |
| NVDA NVIDIA | RHNVDA / USD | $222.45 |
| ORCL Oracle | Robinhood ORCL / USD | $147.96 |
| PLTR Palantir Technologies | Robinhood PLTR / USD | $177.60 |
| QQQ Invesco QQQ | Robinhood QQQ / USD | $720.37 |
| RGTI Rigetti Computing | Robinhood RGTI / USD | $15.74 |
| RKLB Rocket Lab Corporation | Robinhood RKLB / USD | $64.61 |
| SGOV iShares 0-3 Month Treasury Bond | Robinhood SGOV-USD | $101.11 |
| SLV iShares Silver Trust | Robinhood SLV / USD | $59.85 |
| SNDK Sandisk Corporation | RHSNDK / USD | $1,792.32 |
| SPCX Space Exploration Technologies Corp. Class A Common Stock | Robinhood SPCX / USD | $152.82 |
| SPY SPDR S&P 500 ETF Trust | RHSPY / USD | $761.55 |
| TSLA Tesla | RHTSLA / USD | $363.80 |
| TSM Taiwan Semiconductor Manufacturing | Robinhood TSM / USD | $434.26 |
| USAR USA Rare Earth | Robinhood USAR-USD | $15.61 |
| USO United States Oil Fund | RHUSO / USD | $154.54 |
Read, inherited, but not priced
| Stock | Why no price |
|---|---|
| GLD | reads a Uniswap pool ("Uniswap V3 Pool Price in USD") |
| RDDT | reads a Uniswap pool ("Uniswap V3 Pool Price in USD") |
| COST | reads a Uniswap pool ("Uniswap V3 Pool Price in USD") |
| DJT | reads a Uniswap pool ("Uniswap V3 Pool Price in USD") |
| NFLX | reads a Uniswap pool ("Uniswap V3 Pool Price in USD"); "ETH / USD" does not name NFLX in USD |
| RBLX | reads a Uniswap pool ("Uniswap V3 Pool Price in USD"); "ETH / USD" does not name RBLX in USD |
| MRNA | reads a Uniswap pool ("Uniswap V3 Pool Price in USD") |
| RIVN | reads a Uniswap pool ("Uniswap V3 Pool Price in USD") |
| HIMS | reads a Uniswap pool ("Uniswap V3 Pool Price in USD") |
| TTWO | reads a Uniswap pool ("Uniswap V3 Pool Price in USD") |
| AMC | reads a Uniswap pool ("Uniswap V3 Pool Price in USD") |
| LLY | reads a Uniswap pool ("Uniswap V3 Pool Price in USD") |
| LULU | reads a Uniswap pool ("Uniswap V3 Pool Price in USD") |
| IBM | reads a Uniswap pool ("Uniswap V3 Pool Price in USD") |
| F | reads a Uniswap pool ("Uniswap V3 Pool Price in USD") |
What was actually run
Every transaction below was built by js/plan.js — the same file the page loads — and sent on a fresh fork of Robinhood Chain against the real Safe and the real CANDIDE module. Every "refused" is classified by the error it was refused with, and carries a control proving the same call succeeds when the guard is not in the way.
The properties
| # | Property | Result |
|---|---|---|
| P0 | the account opens at all: the Safe is made, and one owner transaction sets up the will 4 calls in one owner transaction, 1,539,171 gas | pass |
| P1 | the account is the Safe the page predicted, owned by the owner alone; Vigil is this build 0xDb041064…, Vigil 0xE9A164b2… | pass |
| P2 | Candide's module is on, Vigil is its only guardian (threshold 1), and the will reads back exactly 180 days of silence, heir 0x4cd724… | pass |
| P3 | the owner deposits stocks and withdraws them in one transaction to where they say AAPL and NVDA out in one transaction | pass |
| P4 | a stranger cannot knock (control: an heir can) NotHeir | pass |
| P5 | the vote is refused one second before the silence ends (control: at the second it ends, it is cast) silence 180 days to the second | pass |
| P6 | any transaction by the owner after a knock voids it (control: without it the vote is cast) OwnerActive | pass |
| P7 | the whole path: knock, silence, vote, 7 days, finalize — the heirs own the account and can withdraw 187 days from knock to keys | pass |
| P8 | the owner can cancel inside Candide's 7 days (control: without the cancel it finalizes) cancelled on day 3 of 7 | pass |
| P9 | "I'm here" clears a knock by name (control: the same claim without it succeeds) NotKnocking | pass |
| P10 | rewriting the will clears the knock and replaces the heirs heirs replaced, knock cleared | pass |
| P11 | the will is refused outside its limits, and accepted at every limit 10 refusals by name, 4 limits accepted | pass |
| P12 | a Safe that set a will but never made Vigil its guardian gets no vote (control: one that did) NotGuardian | pass |
| P13 | removing Vigil leaves a plain Safe: no module, no guardian, no will a plain Safe again | pass |
| P14 | a second knock is refused while one is live, and accepted once the owner has acted AlreadyKnocking | pass |
| P15 | an heir cannot move anything before the account is theirs (control: the owner can) GS026 | pass |
| P16 | the page reads each stage correctly: watching, knocked, claimable, recovering, finalizable watching → knocked → claimable → recovering → finalizable | pass |
| P17 | two of three heirs: the account passes to all three, and needs two of them to act 3 owners, threshold 2 | pass |
| P18 | the vote cannot be replayed: after a claim there is no knock left to claim with NotKnocking | pass |
| P19 | the knock is public: its event names the heir and the moment it may be claimed claimable at knock + 180 days | pass |
| P20 | finalizing is refused inside the 7 days by Candide, by name, and anyone may finalize after SM: recovery period still pending | pass |
| P21 | Vigil exposes only the five writes it declares and carries no DELEGATECALL, CREATE or SELFDESTRUCT 5 writes, opcodes clean | pass |
| P22 | Vigil holds nothing: after every path its token and ether balances are zero nothing held | pass |
| P23 | a stranger cannot cast the vote even after the silence (control: the heir can) NotHeir | pass |
Broken on purpose
28 of 28 were caught by the property named for them, after the untouched build passed 24/24.
| # | The break | Must be caught by | Result |
|---|---|---|---|
| S1 | the owner acting after a knock no longer voids it | P6 | caught |
| S2 | the silence is not enforced at all | P5 | caught |
| S3 | the silence is one second short | P5 | caught |
| S4 | anyone may knock | P4 | caught |
| S5 | anyone may cast the vote | P23 | caught |
| S6 | the shortest silence is not enforced | P11 | caught |
| S7 | "I'm here" does not clear the knock | P9 | caught |
| S8 | rewriting the will keeps the old knock | P10 | caught |
| S9 | a claim leaves the knock in place | P18 | caught |
| S10 | a live knock can be replaced by another heir | P14 | caught |
| S11 | the heirs' threshold is dropped to one | P17 | caught |
| S12 | the same heir may be named twice | P11 | caught |
| S13 | Vigil votes without checking it is a guardian | P12 | caught |
| S14 | the knock records the wrong nonce | P5 | caught |
| S15 | revoking leaves the heirs listed | P13 | caught |
| S16 | the account may name itself as heir | P11 | caught |
| S17 | the knock event promises the wrong moment | P19 | caught |
| S18 | setup never makes Vigil a guardian | P2 | caught |
| S19 | the owner transaction is signed as though by nobody | P0 | caught |
| S20 | withdrawals go to the token instead of where the owner said | P3 | caught |
| S21 | removing Vigil leaves Candide's module switched on | P13 | caught |
| S22 | "cancel" only invalidates signatures, and the hand-over goes on | P8 | caught |
| S23 | "I'm here" tears up the will instead of answering | P9 | caught |
| S24 | the will is written with one signer whatever the owner chose | P17 | caught |
| S25 | the page predicts the wrong account address | P1 | caught |
| S26 | the page reads an answered knock as still live | P6 | caught |
| S27 | the page lets a duplicate heir through to the wallet | P11 | caught |
| S28 | "I'm here" during Candide's 7 days only answers the (spent) knock | P8 | caught |
Fuzzed
2 seeds of 57 operations each — including 10 knocks, 4 votes, 2 finished hand-overs and 3 answers that landed — ran clean against the real contracts; then 8 deliberately broken builds were replayed, and 8 were caught by the invariant named for each.
| # | The break | Invariant | Result |
|---|---|---|---|
| B1 | "I'm here" leaves the knock standing | knock | caught |
| B2 | a rewritten will keeps one signer | will | caught |
| B3 | a claim leaves the knock in place | knock | caught |
| B4 | owner activity no longer voids a knock | outcome | caught |
| B5 | the knock records the wrong nonce | knock | caught |
| B6 | the page reads an answered knock as live | stage | caught |
| B7 | "cancel" only invalidates signatures | recovery | caught |
| B8 | a withdrawal sends to the token, not the owner | balance | caught |
The first fuzz run caught only 4 of 8 planted breaks — not because the other four were safe, but because random walks almost never got through a whole silence to a claim, and the balance invariant only watched the account, not where a withdrawal went. The fuzzer now ends every seed with a scripted walk down the whole path, and checks every wallet's balance.
Read this before you put anything in it
- Silence is not death. If an heir knocks and you say nothing for the whole silence and 7 days more — in hospital, travelling, not looking — the account passes to them. Choose the silence for the longest absence you can imagine.
- Nobody will tell you about a knock. There is no server and no email. The knock is public and this site shows it, but only when you look. Using the account now and then answers any knock.
- An heir who knocks while you are alive learns nothing and loses nothing — but you should talk to them. Each knock starts a new silence.
- Your heirs' wallets are the will. If an heir loses their key, their share of the account is lost with it. Two of three heirs, say, is more robust than one.
- Vigil is not audited. Safe and CANDIDE's module are; the one contract this site adds is not. Its only power is one guardian vote, and a wrong vote still leaves you 7 days to cancel.
- It is not a legal will, and it does not settle taxes, debts or disputes. Tell whoever handles your estate that it exists.
- Robinhood's tokenized stocks have their own rules about who may hold them. An heir must be someone who can.
Everything this site touches
Each one read back on the live chain by tools/verify-infra.mjs, with controls that had to fail. 35 stock tokens and their feeds were checked the same way.
| What | Address | How it was checked |
|---|---|---|
| SafeL2 1.4.1 | 0x29fc…C762 | code hash = Safe registry 0xb1f926978a… (v1.4.1) |
| SafeProxyFactory 1.4.1 | 0x4e1D…ec67 | code hash = Safe registry 0x50c3cdc407… (v1.4.1) |
| CompatibilityFallbackHandler | 0xfd07…Ec99 | code hash = Safe registry 0x7c6007a5d7… (v1.4.1) |
| MultiSendCallOnly 1.4.1 | 0x9641…02e2 | code hash = Safe registry 0xecd5bd14a0… (v1.4.1) |
| Candide recovery = other chains | 0x088f…33f2 | code hash 0xd1d497d069… = base, arbitrum, optimism |
| Candide recovery period | 0x088f…33f2 | "Social Recovery Module" v0.0.1; recovery period 7 days, read from its runtime |
| Candide (other period) absent | 0x3827…541c | no code (so not offered) |
| Candide (other period) absent | 0x9Bac…f25b | no code (so not offered) |
| USDG | 0x5fc5…d168 | USDG, 6 decimals |
| ETH / USD feed | 0x78F3…d3A9 | "ETH / USD", 8 dp, 2637.20423511 at 2026-09-19T18:12:37.000Z |
| Multicall3 | 0xcA11…CA11 | 3808 bytes |
| CREATE2 deployer | 0x4e59…956C | runtime hash 0x2fa86add0a… (Arachnid) |
| Vigil (CREATE2 of this build) | 0xE9A1…93E2 | not deployed yet — the first account's setup deploys it |