Basic Concepts
Learn the fundamental concepts and components of a liquid staking protocol on Solana.
Understanding Liquid Staking
Liquid staking is a DeFi primitive that allows users to stake their assets while receiving a liquid representation token that can be used in other DeFi protocols. This innovation solves the "lock-up" problem traditional staking presents.
Diagram: Flow of assets in a liquid staking protocol
Traditional Staking
- •Assets are locked for a period
- •No DeFi participation during staking
- •Manual validator management
- •Direct staking rewards
Liquid Staking
- •Receive liquid tokens (stSOL)
- •Participate in DeFi while staking
- •Automated validator selection
- •Compounded staking rewards
Key Innovation
Understanding Liquid Staking
Liquid staking is a DeFi primitive that allows users to stake their assets while receiving a liquid representation token that can be used in other DeFi protocols. This innovation solves the "lock-up" problem traditional staking presents.
Traditional Staking
- •Assets are locked for a period
- •No DeFi participation during staking
- •Manual validator management
- •Direct staking rewards
Liquid Staking
- •Receive liquid tokens (stSOL)
- •Participate in DeFi while staking
- •Automated validator selection
- •Compounded staking rewards
Best Practice
Core Components
A liquid staking protocol consists of several key components working together to provide a seamless staking experience.
1. Stake Pool
The central component that manages staked SOL and distributes rewards.
Stake Pool State
1#[account]
2pub struct StakePool {
3 pub version: u8,
4 pub manager_authority: Pubkey,
5 pub staker_authority: Pubkey,
6 pub stake_deposit_authority: Pubkey,
7 pub stake_withdraw_bump_seed: u8,
8 pub validator_list: Pubkey,
9 pub reserve_stake: Pubkey,
10 pub pool_mint: Pubkey,
11 pub manager_fee_account: Pubkey,
12 pub token_program_id: Pubkey,
13 pub total_lamports: u64,
14 pub pool_token_supply: u64,
15 pub last_update_epoch: u64,
16 pub lockup: Lockup,
17 pub epoch_fee: Fee,
18 pub next_epoch_fee: Option<Fee>,
19 pub preferred_deposit_validator_vote_address: Option<Pubkey>,
20 pub preferred_withdraw_validator_vote_address: Option<Pubkey>,
21 pub stake_deposit_fee: Fee,
22 pub stake_withdrawal_fee: Fee,
23 pub next_stake_withdrawal_fee: Option<Fee>,
24 pub stake_referral_fee: u8,
25 pub sol_deposit_authority: Option<Pubkey>,
26 pub sol_deposit_fee: Fee,
27 pub sol_referral_fee: u8,
28 pub sol_withdraw_authority: Option<Pubkey>,
29 pub sol_withdrawal_fee: Fee,
30 pub next_sol_withdrawal_fee: Option<Fee>,
31 pub last_epoch_pool_token_supply: u64,
32 pub last_epoch_total_lamports: u64,
33}
2. Liquid Staking Token
An SPL token representing staked SOL (e.g., stSOL).
Token Creation
1import { Token, TOKEN_PROGRAM_ID } from '@solana/spl-token'
2
3// Create mint for liquid staking token
4const liquidTokenMint = await Token.createMint(
5 connection,
6 payer,
7 mintAuthority.publicKey,
8 freezeAuthority.publicKey,
9 9, // 9 decimals to match SOL
10 TOKEN_PROGRAM_ID
11)
3. Validator List
Manages the list of validators and their stake accounts.
Validator List Structure
1#[account]
2pub struct ValidatorList {
3 pub header: ValidatorListHeader,
4 pub validators: Vec<ValidatorStakeInfo>,
5}
6
7#[derive(Clone, Debug, Default, PartialEq)]
8pub struct ValidatorStakeInfo {
9 pub active_stake_lamports: u64,
10 pub transient_stake_lamports: u64,
11 pub last_update_epoch: u64,
12 pub transient_seed_suffix: u64,
13 pub unused: u32,
14 pub validator_seed_suffix: u32,
15 pub status: StakeStatus,
16 pub vote_account_address: Pubkey,
17}
Protocol Mechanics
1. Staking Process
The flow of SOL from user deposit to validator stake.
Deposit Instruction
1pub fn process_deposit(
2 program_id: &Pubkey,
3 accounts: &[AccountInfo],
4 amount: u64,
5) -> ProgramResult {
6 let account_info_iter = &mut accounts.iter();
7
8 // Get accounts
9 let stake_pool = next_account_info(account_info_iter)?;
10 let validator_list = next_account_info(account_info_iter)?;
11 let deposit_authority = next_account_info(account_info_iter)?;
12 let withdrawer_authority = next_account_info(account_info_iter)?;
13 let stake_account = next_account_info(account_info_iter)?;
14 let validator_stake = next_account_info(account_info_iter)?;
15 let reserve_stake = next_account_info(account_info_iter)?;
16 let user_stake = next_account_info(account_info_iter)?;
17 let pool_tokens = next_account_info(account_info_iter)?;
18
19 // Verify accounts and process deposit
20 // ... implementation ...
21}
2. Reward Distribution
How staking rewards are collected and distributed to token holders.
Update Reward Distribution
1pub fn process_update_reward_distribution(
2 program_id: &Pubkey,
3 accounts: &[AccountInfo],
4) -> ProgramResult {
5 let account_info_iter = &mut accounts.iter();
6
7 // Get accounts
8 let stake_pool = next_account_info(account_info_iter)?;
9 let validator_list = next_account_info(account_info_iter)?;
10 let pool_mint = next_account_info(account_info_iter)?;
11 let manager_authority = next_account_info(account_info_iter)?;
12
13 // Calculate and distribute rewards
14 // ... implementation ...
15}
Reward Mechanism
Security Considerations
Access Control
Authority Verification
1// Verify manager authority
2if !manager_authority.is_signer {
3 return Err(StakePoolError::SignatureMissing.into());
4}
5if *manager_authority.key != stake_pool.manager_authority {
6 return Err(StakePoolError::InvalidProgramAddress.into());
7}
8
9// Verify staker authority
10if *staker_authority.key != stake_pool.staker_authority {
11 return Err(StakePoolError::InvalidStakePoolAuthority.into());
12}
Slashing Protection
Mechanisms to protect against validator misbehavior:
- •Validator performance monitoring
- •Automatic stake reallocation
- •Emergency unstake procedures
Security Best Practices
Advanced Features
1. Fee Management
Fee Structure
1#[derive(Clone, Copy, Debug, Default, PartialEq)]
2pub struct Fee {
3 pub numerator: u64,
4 pub denominator: u64,
5}
6
7impl Fee {
8 pub fn calculate(&self, amount: u64) -> Option<u64> {
9 if self.denominator == 0 {
10 return None;
11 }
12 Some(amount
13 .checked_mul(self.numerator)?
14 .checked_div(self.denominator)?)
15 }
16}
2. Validator Selection
Implement advanced validator selection criteria:
Validator Selection Logic
1interface ValidatorCriteria {
2 minStake: number;
3 maxCommission: number;
4 minUptime: number;
5 maxSkippedSlots: number;
6}
7
8function selectValidators(
9 validators: ValidatorInfo[],
10 criteria: ValidatorCriteria
11): ValidatorInfo[] {
12 return validators.filter(validator =>
13 validator.stake >= criteria.minStake &&
14 validator.commission <= criteria.maxCommission &&
15 validator.uptime >= criteria.minUptime &&
16 validator.skippedSlots <= criteria.maxSkippedSlots
17 );
18}
Next Steps
Now that you understand the basic concepts, you can: