Basic Concepts

Learn the fundamental concepts and components of a liquid staking protocol on Solana.

Last updated: 2024-02-20
Edit on GitHub

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.

Liquid Staking Flow Diagram

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
Liquid staking protocols enable users to maintain liquidity while earning staking rewards, effectively solving the capital efficiency problem in traditional staking.

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
Always implement proper error handling and input validation for each instruction to ensure protocol security.

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
Rewards are automatically distributed by updating the exchange rate between stSOL and SOL. As rewards accumulate, each stSOL becomes worth more SOL, creating a rebasing 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
Always implement proper access controls, input validation, and emergency procedures in your liquid staking protocol. Regular security audits are essential.

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