The shape of an account

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.

The path

From a knock to the keys, call by call

  1. 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).
  2. 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.
  3. claim(account) — from an heir, once the silence has passed to the second (TooSoon before 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.
  4. 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.
  5. 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 contract

The one contract that is ours

contracts/Vigil.sol5,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.

CANDIDE's module

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.

Which stocks it prices

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.

StockRobinhood feedPrice when read
AAPL AppleRobinhood AAPL / USD$335.38
AMD AMDRHAMD / USD$559.42
AMZN AmazonRobinhood AMZN / USD$253.86
ASML ASML Holding NVRobinhood ASML / USD$1,676.63
BABA AlibabaRobinhood BABA / USD$113.77
CLSK CleanSparkRobinhood CLSK / USD$14.37
COIN CoinbaseRobinhood COIN / USD$193.91
CRCL Circle Internet GroupRobinhood CRCL / USD$91.76
CRWV CoreWeaveRobinhood CRWV / USD$81.33
DELL DellRobinhood DELL-USD$570.72
EWY iShares MSCI South Korea fundRobinhood EWY / USD$180.93
GME GameStopRobinhood GME / USD$22.55
GOOGL Alphabet Class ARobinhood GOOGL / USD$350.47
INTC IntelRHINTC / USD$109.05
IONQ IonQRobinhood IONQ / USD$39.09
META Meta PlatformsRobinhood META / USD$666.76
MSFT MicrosoftRHMSFT / USD$495.82
MSTR Strategy Inc.Robinhood MSTR / USD$152.64
MU Micron TechnologyRHMU / USD$1,014.43
NBIS Nebius GroupRobinhood NBIS / USD$222.71
NVDA NVIDIARHNVDA / USD$222.45
ORCL OracleRobinhood ORCL / USD$147.96
PLTR Palantir TechnologiesRobinhood PLTR / USD$177.60
QQQ Invesco QQQRobinhood QQQ / USD$720.37
RGTI Rigetti ComputingRobinhood RGTI / USD$15.74
RKLB Rocket Lab CorporationRobinhood RKLB / USD$64.61
SGOV iShares 0-3 Month Treasury BondRobinhood SGOV-USD$101.11
SLV iShares Silver TrustRobinhood SLV / USD$59.85
SNDK Sandisk CorporationRHSNDK / USD$1,792.32
SPCX Space Exploration Technologies Corp. Class A Common StockRobinhood SPCX / USD$152.82
SPY SPDR S&P 500 ETF TrustRHSPY / USD$761.55
TSLA TeslaRHTSLA / USD$363.80
TSM Taiwan Semiconductor ManufacturingRobinhood TSM / USD$434.26
USAR USA Rare EarthRobinhood USAR-USD$15.61
USO United States Oil FundRHUSO / USD$154.54

Read, inherited, but not priced

StockWhy no price
GLDreads a Uniswap pool ("Uniswap V3 Pool Price in USD")
RDDTreads a Uniswap pool ("Uniswap V3 Pool Price in USD")
COSTreads a Uniswap pool ("Uniswap V3 Pool Price in USD")
DJTreads a Uniswap pool ("Uniswap V3 Pool Price in USD")
NFLXreads a Uniswap pool ("Uniswap V3 Pool Price in USD"); "ETH / USD" does not name NFLX in USD
RBLXreads a Uniswap pool ("Uniswap V3 Pool Price in USD"); "ETH / USD" does not name RBLX in USD
MRNAreads a Uniswap pool ("Uniswap V3 Pool Price in USD")
RIVNreads a Uniswap pool ("Uniswap V3 Pool Price in USD")
HIMSreads a Uniswap pool ("Uniswap V3 Pool Price in USD")
TTWOreads a Uniswap pool ("Uniswap V3 Pool Price in USD")
AMCreads a Uniswap pool ("Uniswap V3 Pool Price in USD")
LLYreads a Uniswap pool ("Uniswap V3 Pool Price in USD")
LULUreads a Uniswap pool ("Uniswap V3 Pool Price in USD")
IBMreads a Uniswap pool ("Uniswap V3 Pool Price in USD")
Freads a Uniswap pool ("Uniswap V3 Pool Price in USD")
Every check

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.

Properties, on a fork of the real chain
24/24
against the real Safe and CANDIDE module; every refusal classified by name, with a control that must succeed
Deliberate breaks caught
28/28
each by the property named for it, after a green control
Fuzzed operation sequences
2×57
nine invariants after every call; 8/8 planted breaks caught by the invariant named for each
Addresses read back on chain
52/52
4 controls that had to fail
The real page, clicked through
29/29
in Chrome, against a fork of this chain: a will written, a knock, a silence, a hand-over
The live site, in a browser
8/8
reading the real chain through this site's own proxy
Colour pairs proved
66/66
each surface against the photograph at both ends, and a control that must fail
Painted pixels, beside the reference
0.037 chroma
flattest frame 20.8% against its 15.5%, busiest 5.5 against 7.2 — measured in one run, same browser

The properties

#PropertyResult
P0the 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
P1the account is the Safe the page predicted, owned by the owner alone; Vigil is this build
0xDb041064…, Vigil 0xE9A164b2…
pass
P2Candide's module is on, Vigil is its only guardian (threshold 1), and the will reads back exactly
180 days of silence, heir 0x4cd724…
pass
P3the owner deposits stocks and withdraws them in one transaction to where they say
AAPL and NVDA out in one transaction
pass
P4a stranger cannot knock (control: an heir can)
NotHeir
pass
P5the 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
P6any transaction by the owner after a knock voids it (control: without it the vote is cast)
OwnerActive
pass
P7the whole path: knock, silence, vote, 7 days, finalize — the heirs own the account and can withdraw
187 days from knock to keys
pass
P8the 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
P10rewriting the will clears the knock and replaces the heirs
heirs replaced, knock cleared
pass
P11the will is refused outside its limits, and accepted at every limit
10 refusals by name, 4 limits accepted
pass
P12a Safe that set a will but never made Vigil its guardian gets no vote (control: one that did)
NotGuardian
pass
P13removing Vigil leaves a plain Safe: no module, no guardian, no will
a plain Safe again
pass
P14a second knock is refused while one is live, and accepted once the owner has acted
AlreadyKnocking
pass
P15an heir cannot move anything before the account is theirs (control: the owner can)
GS026
pass
P16the page reads each stage correctly: watching, knocked, claimable, recovering, finalizable
watching → knocked → claimable → recovering → finalizable
pass
P17two of three heirs: the account passes to all three, and needs two of them to act
3 owners, threshold 2
pass
P18the vote cannot be replayed: after a claim there is no knock left to claim with
NotKnocking
pass
P19the knock is public: its event names the heir and the moment it may be claimed
claimable at knock + 180 days
pass
P20finalizing is refused inside the 7 days by Candide, by name, and anyone may finalize after
SM: recovery period still pending
pass
P21Vigil exposes only the five writes it declares and carries no DELEGATECALL, CREATE or SELFDESTRUCT
5 writes, opcodes clean
pass
P22Vigil holds nothing: after every path its token and ether balances are zero
nothing held
pass
P23a 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 breakMust be caught byResult
S1the owner acting after a knock no longer voids itP6caught
S2the silence is not enforced at allP5caught
S3the silence is one second shortP5caught
S4anyone may knockP4caught
S5anyone may cast the voteP23caught
S6the shortest silence is not enforcedP11caught
S7"I'm here" does not clear the knockP9caught
S8rewriting the will keeps the old knockP10caught
S9a claim leaves the knock in placeP18caught
S10a live knock can be replaced by another heirP14caught
S11the heirs' threshold is dropped to oneP17caught
S12the same heir may be named twiceP11caught
S13Vigil votes without checking it is a guardianP12caught
S14the knock records the wrong nonceP5caught
S15revoking leaves the heirs listedP13caught
S16the account may name itself as heirP11caught
S17the knock event promises the wrong momentP19caught
S18setup never makes Vigil a guardianP2caught
S19the owner transaction is signed as though by nobodyP0caught
S20withdrawals go to the token instead of where the owner saidP3caught
S21removing Vigil leaves Candide's module switched onP13caught
S22"cancel" only invalidates signatures, and the hand-over goes onP8caught
S23"I'm here" tears up the will instead of answeringP9caught
S24the will is written with one signer whatever the owner choseP17caught
S25the page predicts the wrong account addressP1caught
S26the page reads an answered knock as still liveP6caught
S27the page lets a duplicate heir through to the walletP11caught
S28"I'm here" during Candide's 7 days only answers the (spent) knockP8caught

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 breakInvariantResult
B1"I'm here" leaves the knock standingknockcaught
B2a rewritten will keeps one signerwillcaught
B3a claim leaves the knock in placeknockcaught
B4owner activity no longer voids a knockoutcomecaught
B5the knock records the wrong nonceknockcaught
B6the page reads an answered knock as livestagecaught
B7"cancel" only invalidates signaturesrecoverycaught
B8a withdrawal sends to the token, not the ownerbalancecaught

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.

What can go wrong

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.
Addresses

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.

WhatAddressHow it was checked
SafeL2 1.4.10x29fc…C762code hash = Safe registry 0xb1f926978a… (v1.4.1)
SafeProxyFactory 1.4.10x4e1D…ec67code hash = Safe registry 0x50c3cdc407… (v1.4.1)
CompatibilityFallbackHandler0xfd07…Ec99code hash = Safe registry 0x7c6007a5d7… (v1.4.1)
MultiSendCallOnly 1.4.10x9641…02e2code hash = Safe registry 0xecd5bd14a0… (v1.4.1)
Candide recovery = other chains0x088f…33f2code hash 0xd1d497d069… = base, arbitrum, optimism
Candide recovery period0x088f…33f2"Social Recovery Module" v0.0.1; recovery period 7 days, read from its runtime
Candide (other period) absent0x3827…541cno code (so not offered)
Candide (other period) absent0x9Bac…f25bno code (so not offered)
USDG0x5fc5…d168USDG, 6 decimals
ETH / USD feed0x78F3…d3A9"ETH / USD", 8 dp, 2637.20423511 at 2026-09-19T18:12:37.000Z
Multicall30xcA11…CA113808 bytes
CREATE2 deployer0x4e59…956Cruntime hash 0x2fa86add0a… (Arachnid)
Vigil (CREATE2 of this build)0xE9A1…93E2not deployed yet — the first account's setup deploys it