A Solana smart contract that enables the distribution of a vault of digital assets to designated beneficiaries after a configurable period of vault inactivity.
Will Wallet implements a solution for "crypto wallet inheritance":
- Checks vault inactivity using blockchain slot numbers
- Allows distribution of SOL and SPL tokens to beneficiaries after inactivity periods
- Prevents unauthorized access through cryptographic ownership verification
- Supports up to 8 beneficiaries with customizable percentage distributions
- Provides maker control with ability to withdraw, update beneficiaries, and trigger immediate distributions
A Program Derived Address (PDA) that stores:
- Maker's public key (vault owner)
- List of beneficiaries and their percentage allocations
- Inactivity period (measured in blockchain slots)
- Last activity slot for timing calculations
- SOL and SPL token balances
Uses Solana's consensus-validated slot numbers to estimate inactivity time. Any vault operation (deposits, withdrawals, keep_alive calls) updates the last activity slot.
- Last beneficiary automatically receives remainder percentage to ensure 100% distribution
- Distributions only occur after
inactivity_period_slotshave passed since last activity - Rent-exempt balance protection prevents vault drainage
Purpose: Initialize a new vault with beneficiaries and inactivity settings Parameters:
beneficiaries: Vec<Beneficiary>- List of up to 8 beneficiaries with addresses and percentagesinactivity_period_slots: u64- Number of slots of inactivity before distribution is allowed
Beneficiary Structure:
struct Beneficiary {
address: Pubkey, // Beneficiary's wallet address
percentage: u8, // Percentage allocation (0-100, last beneficiary gets remainder)
}Purpose: Add SOL to the vault and update activity timestamp Parameters:
amount: u64- Amount in lamports to deposit Access: Maker only
Purpose: Add SPL tokens to the vault and update activity timestamp Parameters:
amount: u64- Amount of tokens to deposit (in token's native units) Access: Maker only Requires:maker_token_account,mint,associated_token_program, andsystem_programaccounts Security: Vault token account is automatically derived and created if needed using PDA authority
Purpose: Withdraw SOL from vault and update activity timestamp Parameters:
amount: u64- Amount in lamports to withdraw Access: Maker only
Purpose: Withdraw SPL tokens from vault and update activity timestamp Parameters:
amount: u64- Amount of tokens to withdraw Access: Maker only Requires:maker_token_account,mint, andassociated_token_programaccounts Security: Vault token account is derived from vault PDA and mint, preventing user manipulation
Purpose: Update last activity slot without moving funds Parameters: None Access: Maker only
Purpose: Change a beneficiary's address (maker-controlled) Parameters:
index: usize- Index of beneficiary to update (0-based)new_address: Pubkey- New wallet address for the beneficiary Access: Maker only
Purpose: Allow beneficiary to update their own address Parameters:
new_address: Pubkey- New wallet address Access: Current beneficiary only
Purpose: Immediately distribute all vault funds to beneficiaries
Parameters: None
Access: Maker only
Requires: List of beneficiary accounts in remaining_accounts
Purpose: Distribute funds after inactivity period has elapsed Parameters: None Access: Anyone (typically called by beneficiaries) Requires:
- Inactivity period must have elapsed
- List of beneficiary accounts in
remaining_accounts
Purpose: Close vault and return remaining SOL to maker Parameters: None Access: Maker only
Here's a complete example of setting up and using a will wallet from the command line:
# Install Solana CLI tools
sh -c "$(curl -sSfL https://release.solana.com/v1.17.0/install)"
# Install Anchor
npm install -g @coral-xyz/anchor-cli
# Clone and setup project
git clone <repository-url>
cd will-wallet
yarn install
# Generate a test keypair (for development only)
solana-keygen new --no-bip39-passphrase --silent --outfile ~/.config/solana/id.json# Start local validator
solana-test-validator
# In another terminal, build and deploy
anchor build
anchor deploy
# Note the Program ID from output (should match Anchor.toml)# Create vault with 2 beneficiaries
# Beneficiary 1: 60%, Beneficiary 2: gets remainder (40%)
# Inactivity period: 1,000,000 slots (~11.5 days at 2.5 slots/second)
anchor run create-vault \
--provider.cluster localnet \
--beneficiaries '[
{"address": "BENEFICIARY1_PUBKEY_HERE", "percentage": 60},
{"address": "BENEFICIARY2_PUBKEY_HERE", "percentage": 0}
]' \
--inactivity-period 1000000# Deposit 5 SOL to the vault
anchor run deposit-sol --amount 5000000000 # 5 SOL in lamports
# Deposit SPL tokens (requires mint address)
anchor run deposit-token --amount 1000000000 --mint YOUR_TOKEN_MINT_ADDRESS# Option 1: Make deposits/withdrawals (updates activity automatically)
anchor run deposit-sol --amount 1000000000 # 1 SOL
# Option 2: Send keep-alive signal without moving funds
anchor run keep-alive
# Option 3: Withdraw some funds
anchor run withdraw-sol --amount 500000000 # 0.5 SOL# Maker updates beneficiary address
anchor run update-beneficiary \
--index 0 \
--new-address "NEW_BENEFICIARY_PUBKEY"
# Beneficiary updates their own address
anchor run change-beneficiary-address \
--new-address "NEW_ADDRESS" \
--signer "CURRENT_BENEFICIARY_KEYPAIR"# Maker decides to distribute immediately
anchor run trigger-distribution \
--beneficiaries '["BENEFICIARY1_PUBKEY", "BENEFICIARY2_PUBKEY"]'# After 1,000,000 slots of inactivity, anyone can trigger distribution
anchor run claim-distribution \
--beneficiaries '["BENEFICIARY1_PUBKEY", "BENEFICIARY2_PUBKEY"]'# Check vault status
anchor run get-vault-info
# Close vault and recover remaining SOL (maker only)
anchor run close-vaultgit clone <repository-url>
cd will-wallet
yarn install# Build the smart contract
anchor build
# Run tests
anchor test
# Run linting
yarn run lint
yarn run lint:fixThe project is configured for multiple environments in Anchor.toml:
- localnet: Development and testing
- devnet: Staging environment
- mainnet: Production deployment
Program ID: 4LjAaMirdWtJz4QVy6kphwAAtVj3fGVQZw9qdMPova3L
Uses consensus-validated blockchain slots instead of manipulatable timestamps for inactivity detection.
Vaults are Program Derived Addresses ensuring only the program can modify vault state and only authorized users can call functions.
Ensures total percentages don't exceed 100% and automatically handles remainder distribution.
Maintains minimum rent-exempt balance to prevent vault account closure.
- Automatic Derivation: Vault token accounts are derived deterministically from vault PDA + mint
- PDA Ownership: Token accounts are owned by the vault PDA, preventing user manipulation
- Auto-Creation: Token accounts are created automatically when needed using
init_if_needed - No User Input: Users cannot specify arbitrary token accounts, eliminating attack vectors
- Maker-only functions: deposits, withdrawals, beneficiary updates, immediate distributions
- Beneficiary functions: self-address updates
- Public functions: claim distribution (after inactivity period)
The contract includes comprehensive error codes:
TooManyBeneficiaries: Exceeds 8 beneficiary limitNoBeneficiaries: Empty beneficiary listInvalidPercentageDistribution: Total percentages >= 100%UnauthorizedMaker: Non-maker attempting maker-only operationBeneficiaryNotFound: Invalid beneficiary index/addressInsufficientFunds: Withdrawal exceeds available balanceVaultStillActive: Attempting distribution before inactivity periodInvalidBeneficiaryIndex: Beneficiary index out of bounds
[Add your license information here]
[Add contributing guidelines here]