Deadman Vault enforces a simple contract: as long as you keep checking in, your funds stay locked and only you control them. The moment you stop — regardless of why — an automated keeper detects the silence, waits out a grace period, and then distributes your USDCx to your configured beneficiaries according to the exact splits and vesting schedules you defined at setup. No court orders, no probate, no custodian with discretionary power over your assets. The full lifecycle — from first deposit to final payout — is described below.Documentation Index
Fetch the complete documentation index at: https://mintlify.com/natureloved/DeadMan-Vault/llms.txt
Use this file to discover all available pages before exploring further.
Architecture
The protocol layers a Next.js application and Supabase database on top of FlowVault’s on-chain token routing, with a Vercel Cron Job acting as the automated keeper:The keeper is a server-held Stacks keypair, one per vault, generated at vault creation and encrypted at rest. Because
flowvault-v2 keys all vault state off tx-sender, only the address that called deposit can call withdraw — which means the keeper must act on the owner’s behalf once the vault flatlines. This is a custodial design limited to testnet. See the Security Model for the production-grade alternative.Step-by-Step Walkthrough
Create a Vault
The owner connects their Leather or Xverse wallet and fills in the vault creation form:
- Vault name and optional message to beneficiaries
- Heartbeat interval — how many days between required check-ins (options: 7 days, 14 days, 1 month, 2 months, 3 months, 6 months, 1 year, 2 years)
- Beneficiaries — one or more Stacks addresses, each with a name, optional email, an
allocation_percent(must sum to exactly 100%), and an optionalvesting_dayscliff - Deposit amount — the USDCx balance to lock in the vault
initVault(), which validates that allocations total 100%, generates a dedicated keeper keypair for this vault, saves an encrypted copy of the keeper private key to Supabase, and returns the vault’s unique keeper address. Vault status is set to "funding".The owner then signs a plain USDCx token transfer from their wallet to the keeper address. This is the only transaction the owner ever signs — all subsequent FlowVault interactions are performed by the keeper on their behalf.Vault Activation
Once the owner’s USDCx transfer is broadcast, they submit the transaction ID to the API, which calls
finalizeVaultFunding(). The keeper:- Queries the current Stacks block height
- Computes
deadlineBlock = currentBlock + heartbeatIntervalDays × 144 - Calls
FlowVault.setRoutingRules()withlockAmount = totalDepositedandlockUntilBlock = deadlineBlock - Calls
FlowVault.deposit()to lock the full balance on-chain
"active" and the deadline block is stored in Supabase.Lock-stacking is FlowVault’s mechanism for extending an existing lock. When a new deposit is made with an updated
lockUntilBlock, FlowVault extends the deadline on the entire locked balance — not just the new deposit amount. This is how heartbeat check-ins extend the countdown without unlocking previous funds.Heartbeat Check-Ins
To prove they are still in control, the owner must check in before each
heartbeatDeadlineBlock. A check-in works exactly like the initial deposit, but for the minimum amount:- The owner signs a USDCx transfer of 0.01 USDCx to the vault’s keeper address
- They submit the transaction ID to the API, which calls
performHeartbeat() - The keeper computes a new
deadlineBlock = currentBlock + heartbeatIntervalDays × 144and callssetRoutingRules()+deposit()withlockAmount = 0.01 USDCx - FlowVault’s lock-stacking extends the
lockUntilBlockfor the entire locked balance to the new deadline
heartbeats table with block_height, tx_id, and deposited_amount.Warning Phase
The keeper cron runs periodically and checks every active vault’s
heartbeat_deadline_block against the current block height. When the remaining blocks drop to 432 blocks (≈ 3 days) before the deadline, the keeper:- Sets vault status to
"warning" - Sends a warning email to the owner’s configured address via Resend
"active" status and the countdown resets.Grace Period
If the
heartbeat_deadline_block passes without a check-in, the keeper cron detects the overdue vault and begins the grace period:- Vault status changes to
"grace_period" - An urgent email is sent to the owner
- Optional notification emails are sent to beneficiaries (if emails were configured)
Trigger
When the grace period expires (current block ≥
heartbeatDeadlineBlock + 288), the keeper cron marks the vault as triggered:- Vault status changes to
"triggered" - Beneficiaries with email addresses receive a claim notification
vesting_release_attimestamps are computed for each beneficiary:triggerTimestamp + vestingDays × 86400 seconds
vesting_days = 0 are immediately eligible. Beneficiaries with a vesting cliff must wait until their individual vesting_release_at timestamp passes before receiving funds.Automatic Settlement
On each keeper cron run after the vault is triggered, the settlement routine:
- Calls
FlowVault.withdraw()through the keeper to retrieve the unlocked balance - Iterates through every beneficiary record for the vault
- For each beneficiary where
claimed = falseandvesting_release_at ≤ now: sends a USDCx transfer equal tototalBalance × (allocation_percent / 100) - Marks
claimed = trueand recordsclaimed_tx_idin thebeneficiariestable
"claimed" once all beneficiaries have been paid.Settlement is fully automatic — beneficiaries do not need to take any action. The keeper cron re-runs on every cycle, so beneficiaries with future vesting cliffs will be paid automatically once their cliff date passes, without any manual intervention.
Vesting Cliffs
Beneficiaries configured with
vesting_days > 0 receive their allocation only after their computed vesting_release_at timestamp passes. For example, a beneficiary with vesting_days = 365 will not receive funds until one year after the vault was triggered.On each keeper cron cycle, the settlement routine checks every unclaimed beneficiary. If vesting_release_at has passed since the last sweep, the transfer is sent automatically. This means:- No action required from beneficiaries — the cron handles retry
- Staggered releases — different beneficiaries can have different vesting periods on the same vault
- Vesting is enforced at the application layer by the keeper, not on-chain, consistent with the current testnet custodial architecture
Claim Page
If the keeper cron has not yet run since the vault was triggered, beneficiaries can manually initiate settlement by visiting the
/claim/[vaultId] page and clicking “Check & Release Funds”. This calls the same settlement routine as the cron job and immediately distributes any eligible balances.This ensures beneficiaries are never dependent on cron timing — they always have an on-demand fallback to trigger payout themselves.Timing Constants Reference
These constants (fromsrc/lib/constants.ts) govern all block-based timing calculations in the protocol:
| Constant | Value | Meaning |
|---|---|---|
BLOCKS_PER_DAY | 144 | Approximate Stacks blocks mined per day (one block every ~10 minutes) |
GRACE_PERIOD_BLOCKS | 288 | Grace period length — 2 days after a missed deadline before payout triggers |
WARNING_BLOCKS | 432 | Warning threshold — 3 days before deadline, warning email is sent |
HEARTBEAT_AMOUNT | "0.01" | Minimum USDCx required per heartbeat check-in transfer |
All deadlines are expressed as block heights, not wall-clock timestamps. Because Stacks block times are approximately (not exactly) 10 minutes, real-world elapsed time may vary slightly. The 144 blocks/day constant is a best-effort approximation — plan your heartbeat interval with some margin.