A Solana program that enables staking of any SPL token. Built with Anchor framework.
- Stake any SPL token
- Unstake at any time (no lock period)
- Track staking history and statistics
- Multiple users can stake in the same pool
- Daily reward epochs with anti-gaming measures
Note: This program is named
spl_staking_programin the codebase.
# Install Solana CLI (You can check the official solana website)
curl --proto '=https' --tlsv1.2 -sSfL https://solana-install.solana.workers.dev | bash
# Install Rust
curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh -s -- -y
source $HOME/.cargo/env
# Install Anchor
cargo install --git https://github.com/coral-xyz/anchor avm --locked --force
avm install latest
avm use latest
# Install project dependencies
npm install# Generate deployment wallet in project directory
# Make sure you're in the project root directory where Anchor.toml is located
solana-keygen new -o payer.json # Creates payer.json in current directory
# Configure CLI
solana config set --url localhost # For local testing
solana config get # Verify your settings# Start local validator in a separate terminal
solana-test-validator
# Build and deploy
anchor build
anchor deployI provide an example script (scripts/analysis/calculate_rewards.ts) that shows how to handle reward calculations.
- Users can stake/unstake at any time
- Hard to track exact amounts between movements
- Solution: Use daily epochs
Day 1: User stakes 100 → Eligible for rewards Day 2: User unstakes 50 → No rewards for this day Day 3: No movements → Rewards on remaining 50 - Skip rewards for epochs with movements
- Only reward "stable" staking periods
To run the example:
The script uses anchor.AnchorProvider.env() which requires two environment variables:
ANCHOR_PROVIDER_URL: Network RPC URLANCHOR_WALLET: Path to your wallet keypair
You can configure these in two ways:
- For the entire terminal session:
# Configure for local validator
export ANCHOR_PROVIDER_URL="http://127.0.0.1:8899"
export ANCHOR_WALLET="./payer.json"
# Or for devnet
export ANCHOR_PROVIDER_URL="https://api.devnet.solana.com"
export ANCHOR_WALLET="./payer.json"
# Or for mainnet
export ANCHOR_PROVIDER_URL="https://api.mainnet-beta.solana.com"
export ANCHOR_WALLET="./payer.json"
# Then run analysis
ts-node scripts/analysis/calculate_rewards.ts- Just for one command:
# All in one line
ANCHOR_PROVIDER_URL="http://127.0.0.1:8899" ANCHOR_WALLET="./payer.json" ts-node scripts/analysis/calculate_rewards.tsNote: This is the same mechanism that anchor test uses internally to configure the test environment.
This is just one possible approach - you might want to:
- Use a database to track historical stakes
- Implement on-chain epoch tracking
- Create your own reward distribution logic
# Run all tests with logs
anchor test -- --show-output
# Run specific test file
anchor test tests/staking.ts -- --show-output# Make sure you're connected to the right network
solana config set --url devnet # or mainnet-beta
# Run reward analysis script
ts-node scripts/analysis/calculate_rewards.tsThe reward analysis script:
- Calculates rewards based on daily epochs
- Shows staking statistics per user
- Displays pool-wide metrics
- Identifies stable stakers (no movements in current epoch)
Note: Users who stake/unstake during an epoch won't receive rewards for that epoch to ensure accurate reward calculation.
# Configure for devnet
solana config set --url devnet
# Get SOL from faucet
solana airdrop 2 payer.json
# Update Anchor.toml
[provider]
cluster = "devnet"
wallet = "./payer.json"
# Deploy
anchor deploy --provider.cluster devnet
# Initialize program
ts-node scripts/initialize.ts# 1. Configure for mainnet
solana config set --url mainnet-beta
# 2. Update Anchor.toml
[provider]
cluster = "mainnet"
wallet = "./payer.json"
# 3. Generate program keypair
solana-keygen new -o target/deploy/spl_staking_program-keypair.json --force
# 4. Update program ID
anchor keys sync
# 5. Build
anchor build
# 6. Deploy
anchor deploy --provider.cluster mainnet
# Or use npm script
npm run deploy:mainnet
# 7. Initialize
ts-node scripts/initialize.ts
# Or use npm script
npm run initsolana config set --url localhostsolana config set --url devnetsolana config set --url mainnet-betaVerify your configuration:
solana config get# Check program ID for spl_staking_program
solana address -k target/deploy/spl_staking_program-keypair.json
# Check program status
solana program show <PROGRAM_ID>
# Check wallet balance
solana balance -k payer.json
# Close program and recover rent (specify network)
# For devnet:
solana program close <PROGRAM_ID> --url devnet --keypair payer.json --bypass-warning
# For mainnet:
solana program close <PROGRAM_ID> --url mainnet-beta --keypair payer.json --bypass-warning- Always backup
payer.jsonand program keypair - Test thoroughly on devnet before mainnet
- Verify network configuration before transactions
- Double-check program ID in all files
- Devnet: Free (use airdrop)
- Mainnet deployment: ~2.8 SOL (recoverable)
- Transaction fees: ~0.00001 SOL per tx
- Wrong Network: Use
solana config getto verify - Insufficient Balance: Use
solana balanceto check - Build Errors: Try
anchor clean && anchor build - Test Validator: Use
solana-test-validator --resetto clear state
# Clean build artifacts
anchor clean
# Reset local validator
solana-test-validator --reset
# Close program if needed (specify network)
solana program close <PROGRAM_ID> --url devnet --keypair payer.json --bypass-warning # For devnet
solana program close <PROGRAM_ID> --url mainnet-beta --keypair payer.json --bypass-warning # For mainnet
# Verify program deployment
solana program show $(solana address -k target/deploy/spl_staking_program-keypair.json)