Deadman Vault’s entire on-chain mechanics are built on top of FlowVault — a programmable token-routing smart contract on Stacks. Rather than writing a custom Clarity contract for every routing concern, Deadman Vault composes FlowVault’s three primitives — LOCK, SPLIT, and HOLD — to enforce heartbeat deadlines, extend them automatically on check-in, and route USDCx to beneficiaries when a vault flatlines. The keeper (a server-held Stacks keypair) is the entity that signs all FlowVault calls; the vault owner’s wallet only ever signs the initial USDCx transfer to the keeper address.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.
What is FlowVault?
FlowVault (flowvault-v2) is a smart contract on Stacks that provides programmable token routing via three composable primitives. Vault state is keyed off tx-sender — only the address that deposited can withdraw or modify routing rules. This makes the FlowVault SDK a natural fit for a custody layer, and it’s the reason Deadman Vault needs the custodial keeper pattern: a server-held key that can act as tx-sender on the owner’s behalf once they can no longer sign.
FlowVault SDK documentation is available at https://docs.flow-vault.dev/.
Primitives Used
LOCK — Heartbeat Period Enforcement
ThelockAmount + lockUntilBlock combination is the core of Deadman Vault’s proof-of-life mechanism. Funds deposited under a LOCK rule cannot be withdrawn by the keeper until lockUntilBlock is reached. This creates the heartbeat window: if the keeper tries to withdraw early (e.g., during an accidental trigger), FlowVault rejects it.
lockAmount: the micro-USDCx amount placed under the time locklockUntilBlock: the Stacks block height at which the lock expires (calculated ascurrentBlock + heartbeatIntervalBlocks)
SPLIT — Beneficiary Distribution
ThesplitAddress + splitAmount routing rule directs a specific amount to a beneficiary address at payout time. When a vault triggers and the keeper calls setRoutingRules with a SPLIT configuration before withdraw, FlowVault routes the designated amount to the split address automatically.
splitAddress: the beneficiary’s Stacks addresssplitAmount: the micro-USDCx amount allocated to that beneficiary
HOLD — Active Custody (Not Used in Deadman Flow)
HOLD represents an unlocked balance available for immediate owner withdrawal. Deadman Vault does not use HOLD during the active lifecycle — all deposited funds are placed under LOCK. HOLD becomes relevant only after the lock expires and the keeper has withdrawn the balance prior to distributing payouts.Lock Stacking — Heartbeat Extension
Lock stacking is FlowVault’s mechanism for extending an existing lock without losing the accumulated balance. Each time the owner checks in (heartbeat), the keeper callssetRoutingRules with a new lockUntilBlock, then calls deposit with the minimum heartbeat amount (0.01 USDCx). FlowVault extends the lock on the entire existing balance to the new deadline. No funds are lost; the lock period simply rolls forward.
Contract Addresses (Testnet)
| Contract | Address |
|---|---|
FlowVault (flowvault-v2) | STD7QG84VQQ0C35SZM2EYTHZV4M8FQ0R7YNSQWPD.flowvault-v2 |
| USDCx token | ST1PQHQKV0RJXZFY1DGX8MNSNYVE3VGZJSRTPGZGM.usdcx |
TypeScript Types
The FlowVault SDK exposes two key types used throughout the keeper layer:VaultState is fetched server-side via flowVault.getVaultState(keeperAddress) in the keeper cron and settlement logic. unlockedBalance determines whether the keeper needs to call withdraw before distributing payouts — if it’s already zero (e.g., lock hasn’t expired), settlement waits.
Vault Creation Flow
Owner deposits USDCx to keeper
The owner signs a SIP-010 USDCx
transfer from their wallet to the vault’s keeper address using signDepositTransfer() in src/lib/flowvault-client.ts. This is the only transaction the owner ever signs.Keeper calls setRoutingRules with LOCK
The keeper calls
flowVault.setRoutingRules({ lockAmount, lockUntilBlock }) using the FlowVault SDK. At this stage, no SPLIT rules are set — the vault is in active custody mode with a single time lock.Keeper calls deposit
The keeper calls
flowVault.deposit(amount) to lock the received USDCx balance in FlowVault. The funds are now locked until lockUntilBlock and cannot be withdrawn by anyone, including the keeper, until the block height is reached.Heartbeat Flow
Owner sends 0.01 USDCx to keeper
The owner signs a USDCx transfer of exactly 0.01 USDCx to the keeper address. The keeper cron detects the confirmed transaction and begins the heartbeat update.
Keeper calls setRoutingRules with new lockUntilBlock
The keeper calls
setRoutingRules with an updated lockUntilBlock calculated from the current block height plus the vault’s configured heartbeat interval in blocks. This updates the routing rules in FlowVault without touching the locked balance yet.Keeper calls deposit — lock stacking activates
The keeper calls
flowVault.deposit(HEARTBEAT_AMOUNT) with the 0.01 USDCx received from the owner. FlowVault’s lock-stacking mechanism extends the deadline on the entire existing locked balance to the new lockUntilBlock. The vault stays continuously locked without the owner re-depositing their full balance.Settlement Flow (Triggered Vault)
When the keeper cron determines that a vault has missed its deadline and the grace period has expired,settleVaultPayout() in src/lib/keeper/settlement.ts handles the on-chain payout:
Keeper fetches vault state
flowVault.getVaultState(keeperAddress) is called. unlockedBalance is checked — if greater than zero, the lock has expired and withdrawal is possible.Keeper calls withdraw
Keeper distributes shares to beneficiaries
For each unpaid beneficiary whose
vesting_release_at has passed, the keeper calls sendTokenTransfer() with the beneficiary’s proportional share (calculated from total_deposited and allocation_percent). Allocation is computed proportionally across all beneficiaries (not just unpaid ones) so rounding is consistent across partial retries.Settlement is idempotent — it is safe to call repeatedly. The keeper cron runs it on every sweep for triggered vaults, and the beneficiary’s “Check & Release Funds” button triggers it on demand. Beneficiaries whose transfers failed will be retried on the next call.
The tx-sender Constraint
FlowVault’s key constraint shapes every architectural decision in Deadman Vault:Vault state is keyed offThis is correct behavior for a standard DeFi vault — but it means a dead owner cannot authorize a withdrawal by definition. There are only two ways to handle this:tx-senderwith no owner/vault-id argument. Only the address that calleddepositcan ever callwithdraw.
- Custom Clarity contract (the non-custodial production fix): a wrapper contract with a permissionless
claimfunction that enforces the heartbeat deadline on-chain. No server holds keys. - Custodial keeper (the testnet approach): a server-held keypair is
tx-sender. It can act on behalf of the vault after the owner is gone — but it is a trusted third party.