A Solana token staking smart contract built with Anchor that lets users stake SPL tokens and earn rewards over time. The program supports admin configuration, reward deposits, proportional reward distribution, and optional pausing—suitable for token staking dApps on Solana devnet and mainnet.
- Overview
- Project Architecture
- Prerequisites
- How to Run This Project
- Features
- Staking & Rewards System
- CLI Reference
- License
This repository contains a Solana token staking program (smart contract) that:
- Lets an admin configure the staking pool (token mint, claim period, reward multiplier, etc.) and deposit reward tokens.
- Lets users stake tokens, unstake (withdraw principal + rewards), or claim rewards only.
- Distributes rewards proportionally by share of total staked amount over configurable time periods.
- Supports two-step authority transfer and pause/unpause for safety.
Built with Rust and Anchor 0.30.1, with a TypeScript CLI for testing and interaction.
Solana-Token-Staking-Program/
├── programs/
│ └── tapestry-explorer-statking-contract/ # Anchor program (Rust)
│ └── src/
│ ├── lib.rs # Program entry, instruction routing
│ ├── state/ # On-chain account types
│ │ ├── mod.rs
│ │ ├── config.rs # Global config (authority, token mint, rewards)
│ │ └── user.rs # Per-user staking state
│ ├── instructions/
│ │ ├── mod.rs
│ │ ├── admin/ # Admin-only instructions
│ │ │ ├── configure.rs # Set global config
│ │ │ ├── deposit_fund.rs # Deposit reward tokens
│ │ │ ├── nominate_authority.rs # Propose new admin
│ │ │ ├── accept_authority.rs # Accept admin role
│ │ │ └── pause.rs # Pause/unpause contract
│ │ └── user/ # User instructions
│ │ ├── stake.rs # Stake tokens
│ │ ├── unstake.rs # Unstake + receive rewards
│ │ └── claim_reward.rs # Claim rewards only
│ ├── errors.rs # Custom error codes
│ ├── constants.rs # Seeds (CONFIG, USERINFO)
│ └── utils.rs # Math/helpers
├── lib/ # Shared TS helpers for CLI
│ ├── scripts.ts # Transaction builders (config, stake, unstake, etc.)
│ ├── util.ts # Execution helpers
│ └── constant.ts # SEED_*, TOKEN_ADDRESS, etc.
├── cli/
│ ├── command.ts # CLI entry (commander)
│ └── scripts.ts # CLI actions (config, deposit, stake, unstake, claim, pause, getuserinfo)
├── target/ # Build output (IDL, .so)
├── keys/ # Wallet keypairs (e.g. admin.json)
├── Anchor.toml # Anchor workspace & provider config
├── package.json # Node deps & scripts
└── Cargo.toml # Rust workspace
| Account | Seeds | Description |
|---|---|---|
| Config | ["config"] |
Global state: authority, pending_authority, token_mint_config, claim_period, total_rate, total_stakers, last_reward_time, reward_multiplier, deposit_time, total_deposit, purchase_amt, is_stop, initialized. |
| User | ["userinfo", user_pubkey] |
Per-user state: user pubkey, deposit (staked amount), debt (claimed rewards), last_update, initialized. |
- Admin:
configure→ (optional)nominate_authority/accept_authority→deposit_fundto fund rewards;pauseto stop/resume. - User:
stake(amount)→ laterunstake()(principal + rewards) orclaim_reward()(rewards only).
Rewards are computed from total_rate, reward_multiplier, claim_period, and user’s share of total_deposit (see Staking & Rewards System).
Before building or running the Solana token staking smart contract:
- Rust (stable + nightly used by Anchor)
- Solana CLI (e.g. 1.18.x)
- Anchor CLI 0.30.1
Installation guide: Solana docs – Installation.
Install Anchor 0.30.1:
avm install 0.30.1
avm use 0.30.1
anchor --version # should show 0.30.1- Node.js and Yarn (for CLI and tests):
node --version
yarn --version- Wallet keypair for deploy/admin (e.g.
./keys/admin.json). Set path inAnchor.tomlunder[provider]and in CLI (see below).
git clone <repo-url>
cd Solana-Token-Staking-Program
yarn installUse the same Rust nightly as in the project:
RUSTUP_TOOLCHAIN="nightly-2024-11-19" anchor buildArtifacts are in target/deploy/ (e.g. tapestry_explorer_statking_contract.so and the IDL).
-
Cluster: In
Anchor.toml, set[provider]to your target cluster and RPC:[provider] cluster = "https://api.devnet.solana.com" # or your RPC URL wallet = "./keys/admin.json"
-
CLI: Default RPC and keypair are in
cli/command.ts. Override per run with-rand-k(see CLI Reference).
Ensure Solana CLI is pointed at the same cluster:
solana config set --url devnet
solana airdrop 2 # if needed
anchor deployProgram ID is in Anchor.toml under [programs.devnet] and in programs/.../src/lib.rs (declare_id!(...)). After changing program ID, run anchor build again.
Set global config (token mint, claim period, reward multiplier, purchase amount, etc.):
yarn script configUse your admin keypair (e.g. ./keys/admin.json). Ensure lib/constant.ts has the correct TOKEN_ADDRESS (staking/reward token mint) for your deployment.
Fund the program’s reward vault so users can earn rewards:
yarn script depositAdmin’s token account for the configured mint must hold enough tokens; the program transfers them into the protocol’s vault.
-
Stake (amount in token base units, e.g. lamports for 6 decimals):
yarn script stake -a <TOKEN_AMOUNT>
-
Unstake (withdraw staked tokens + accrued rewards):
yarn script unstake
-
Claim (rewards only):
yarn script claim
Use the keypair set via -k (or default in cli/command.ts).
-
Pause / unpause (admin):
yarn script pause -s 1 # pause yarn script pause -s 0 # unpause
-
Inspect user state:
yarn script getuserinfo
yarn test(Uses test script from package.json; add tests under tests/ if needed.)
- Initialize / configure program – Admin sets token mint, claim period, reward multiplier, and other parameters.
- Deposit funds – Admin deposits reward tokens into the program vault.
- Stake tokens – Users stake SPL tokens and start earning rewards proportionally.
- Unstake tokens – Users withdraw staked tokens plus accrued rewards in one tx.
- Claim rewards – Users can claim only rewards without unstaking.
- Authority management – Two-step transfer:
nominate_authority→accept_authority. - Pause – Admin can pause staking/unstaking/rewards when needed.
purchase_amtis the total reward token allocation for a single reward period (e.g. 2 weeks, depending onclaim_periodand config).- Rewards are distributed proportionally to each user’s share of total staked amount.
- Example:
- User A stakes 100 tokens → 100% of rewards go to A while they are the only staker.
- User B stakes 400 tokens → Total pool = 500. B has 80%, A has 20%; rewards split 80% / 20%.
- On unstake, the user receives their original staked tokens plus accrued reward tokens.
Reward math uses total_rate, reward_multiplier, and claim_period; see state/config.rs and lib/scripts.ts (e.g. calcReward) for details.
CLI is built with commander and run via:
yarn script <command> [options]Common options (all commands):
| Option | Short | Description | Default |
|---|---|---|---|
--env |
-e |
Cluster name (e.g. mainnet-beta, devnet) | devnet |
--rpc |
-r |
RPC URL | https://api.devnet.solana.com |
--keypair |
-k |
Path to keypair JSON | See cli/command.ts |
Commands:
| Command | Description |
|---|---|
config |
(Admin) Initialize/update global program config. |
deposit |
(Admin) Deposit reward tokens into the vault. |
stake |
(User) Stake tokens. Requires -a / --amount. |
unstake |
(User) Unstake and receive principal + rewards. |
claim |
(User) Claim rewards only. |
pause |
(Admin) Pause (e.g. -s 1) or unpause (-s 0). |
getuserinfo |
Print current user staking state and reward info. |
Example:
yarn script stake -a 1000000000 -e devnet -k ./keys/user.json