Staking Mechanics
Learn about the staking mechanics in liquid staking protocols.
Last updated: 2024-03-21
Edit on GitHubOverview
The staking mechanics are the core functionality of your liquid staking protocol. This guide covers the implementation of secure and efficient staking operations, including stake delegation, validator management, and reward distribution.
Stake Delegation
Implement secure stake delegation to validators with proper validation and error handling.
Stake Delegation Implementation
1use solana_program::{
2 program_pack::Pack,
3 stake::state::{Authorized, Lockup, StakeState},
4 system_instruction,
5};
6
7pub struct StakeDelegation {
8 /// Stake pool
9 pub stake_pool: Pubkey,
10
11 /// Validator vote account
12 pub validator_vote: Pubkey,
13
14 /// Stake authority
15 pub stake_authority: Pubkey,
16
17 /// Withdraw authority
18 pub withdraw_authority: Pubkey,
19}
20
21impl StakeDelegation {
22 pub fn create_stake_account(
23 &self,
24 program_id: &Pubkey,
25 payer: &Pubkey,
26 stake_account: &Keypair,
27 amount: u64,
28 ) -> Result<Vec<Instruction>, ProgramError> {
29 let mut instructions = vec![];
30
31 // Calculate rent-exempt reserve
32 let rent = Rent::get()?;
33 let lamports = rent.minimum_balance(std::mem::size_of::<StakeState>())
34 .checked_add(amount)
35 .ok_or(StakePoolError::CalculationFailure)?;
36
37 // Create stake account
38 instructions.push(
39 system_instruction::create_account(
40 payer,
41 &stake_account.pubkey(),
42 lamports,
43 std::mem::size_of::<StakeState>() as u64,
44 &stake::program::id(),
45 ),
46 );
47
48 // Initialize stake account
49 instructions.push(
50 stake::instruction::initialize(
51 &stake_account.pubkey(),
52 &Authorized {
53 staker: self.stake_authority,
54 withdrawer: self.withdraw_authority,
55 },
56 &Lockup::default(), // No lockup
57 ),
58 );
59
60 // Delegate stake
61 instructions.push(
62 stake::instruction::delegate_stake(
63 &stake_account.pubkey(),
64 &self.stake_authority,
65 &self.validator_vote,
66 ),
67 );
68
69 Ok(instructions)
70 }
71
72 pub fn redelegate_stake(
73 &self,
74 program_id: &Pubkey,
75 stake_account: &Pubkey,
76 new_validator_vote: &Pubkey,
77 ) -> Result<Vec<Instruction>, ProgramError> {
78 let mut instructions = vec![];
79
80 // Deactivate current stake
81 instructions.push(
82 stake::instruction::deactivate_stake(
83 stake_account,
84 &self.stake_authority,
85 ),
86 );
87
88 // Wait for next epoch
89 // Then delegate to new validator
90 instructions.push(
91 stake::instruction::delegate_stake(
92 stake_account,
93 &self.stake_authority,
94 new_validator_vote,
95 ),
96 );
97
98 Ok(instructions)
99 }
100}
Validator Management
Implement efficient validator selection and management based on performance metrics.
Validator Management Implementation
1pub struct ValidatorManager {
2 /// Maximum number of validators
3 pub max_validators: u32,
4
5 /// Minimum stake amount per validator
6 pub min_stake_amount: u64,
7
8 /// Maximum stake amount per validator
9 pub max_stake_amount: u64,
10
11 /// Performance metrics
12 pub metrics: ValidatorMetrics,
13}
14
15impl ValidatorManager {
16 pub fn select_validators(
17 &self,
18 validators: &[ValidatorInfo],
19 total_stake: u64,
20 ) -> Result<Vec<ValidatorStake>, ProgramError> {
21 // Sort validators by score
22 let mut scored_validators: Vec<_> = validators
23 .iter()
24 .map(|v| {
25 let score = self.calculate_validator_score(v);
26 (v, score)
27 })
28 .collect();
29
30 scored_validators.sort_by(|(_, a), (_, b)| b.partial_cmp(a).unwrap());
31
32 // Calculate stake distribution
33 let mut distribution = Vec::new();
34 let mut remaining_stake = total_stake;
35
36 for (validator, score) in scored_validators {
37 if distribution.len() >= self.max_validators as usize {
38 break;
39 }
40
41 // Calculate target stake based on score
42 let target_stake = (total_stake as f64 * score) as u64;
43 let target_stake = target_stake
44 .max(self.min_stake_amount)
45 .min(self.max_stake_amount)
46 .min(remaining_stake);
47
48 if target_stake >= self.min_stake_amount {
49 distribution.push(ValidatorStake {
50 vote_account: validator.vote_account,
51 stake_amount: target_stake,
52 });
53
54 remaining_stake = remaining_stake
55 .checked_sub(target_stake)
56 .ok_or(StakePoolError::CalculationFailure)?;
57 }
58 }
59
60 Ok(distribution)
61 }
62
63 fn calculate_validator_score(&self, validator: &ValidatorInfo) -> f64 {
64 let mut score = 0.0;
65
66 // Base score (30%)
67 score += 0.3 * self.calculate_base_score(validator);
68
69 // Performance score (40%)
70 score += 0.4 * self.calculate_performance_score(validator);
71
72 // Risk score (30%)
73 score += 0.3 * self.calculate_risk_score(validator);
74
75 score
76 }
77
78 fn calculate_base_score(&self, validator: &ValidatorInfo) -> f64 {
79 let mut score = 0.0;
80
81 // Commission (lower is better)
82 score += (100.0 - validator.commission as f64) / 100.0;
83
84 // Self stake ratio
85 let self_stake_ratio = validator.self_stake as f64
86 / validator.total_stake as f64;
87 score += self_stake_ratio.min(0.1) * 10.0; // Cap at 10%
88
89 score / 2.0 // Normalize to 0-1
90 }
91
92 fn calculate_performance_score(&self, validator: &ValidatorInfo) -> f64 {
93 let mut score = 0.0;
94
95 // Uptime
96 score += validator.uptime;
97
98 // Skip rate (lower is better)
99 score += 1.0 - validator.skip_rate;
100
101 // Vote credits
102 let normalized_credits = (validator.epoch_credits as f64)
103 / self.metrics.max_epoch_credits as f64;
104 score += normalized_credits;
105
106 score / 3.0 // Normalize to 0-1
107 }
108
109 fn calculate_risk_score(&self, validator: &ValidatorInfo) -> f64 {
110 let mut score = 0.0;
111
112 // Stake concentration (lower is better)
113 let concentration = validator.total_stake as f64
114 / self.metrics.total_network_stake as f64;
115 score += 1.0 - concentration;
116
117 // Version score
118 score += if validator.version >= self.metrics.min_version {
119 1.0
120 } else {
121 0.0
122 };
123
124 // Delinquent status
125 score += if !validator.delinquent { 1.0 } else { 0.0 };
126
127 score / 3.0 // Normalize to 0-1
128 }
129}
Reward Distribution
Implement efficient reward collection and distribution with proper fee handling.
Reward Distribution Implementation
1pub struct RewardDistributor {
2 /// Stake pool
3 pub stake_pool: Pubkey,
4
5 /// Fee account
6 pub fee_account: Pubkey,
7
8 /// Protocol fee percentage
9 pub protocol_fee: u8,
10
11 /// Minimum distribution amount
12 pub min_distribution: u64,
13}
14
15impl RewardDistributor {
16 pub fn collect_rewards(
17 &self,
18 program_id: &Pubkey,
19 validator_list: &[ValidatorStakeInfo],
20 ) -> Result<Vec<Instruction>, ProgramError> {
21 let mut instructions = vec![];
22 let mut total_rewards = 0;
23
24 for validator in validator_list {
25 // Get stake account
26 let (stake_account, _) = Pubkey::find_program_address(
27 &[
28 &validator.vote_account.to_bytes(),
29 &self.stake_pool.to_bytes(),
30 ],
31 program_id,
32 );
33
34 // Calculate rewards
35 let rewards = self.calculate_stake_rewards(&stake_account)?;
36 if rewards > 0 {
37 // Withdraw rewards to reserve
38 instructions.push(
39 stake::instruction::withdraw(
40 &stake_account,
41 &self.stake_pool,
42 &self.stake_pool,
43 rewards,
44 None,
45 ),
46 );
47
48 total_rewards = total_rewards
49 .checked_add(rewards)
50 .ok_or(StakePoolError::CalculationFailure)?;
51 }
52 }
53
54 // Process fees
55 if total_rewards >= self.min_distribution {
56 let fee_amount = (total_rewards as u128)
57 .checked_mul(self.protocol_fee as u128)
58 .and_then(|x| x.checked_div(100))
59 .and_then(|x| u64::try_from(x).ok())
60 .ok_or(StakePoolError::CalculationFailure)?;
61
62 // Transfer fees
63 instructions.push(
64 system_instruction::transfer(
65 &self.stake_pool,
66 &self.fee_account,
67 fee_amount,
68 ),
69 );
70
71 // Update pool token supply to reflect rewards
72 let remaining_rewards = total_rewards
73 .checked_sub(fee_amount)
74 .ok_or(StakePoolError::CalculationFailure)?;
75
76 instructions.push(
77 self.update_pool_token_supply(remaining_rewards)?,
78 );
79 }
80
81 Ok(instructions)
82 }
83
84 fn calculate_stake_rewards(
85 &self,
86 stake_account: &Pubkey,
87 ) -> Result<u64, ProgramError> {
88 // Get stake account data
89 let account = get_stake_account(stake_account)?;
90
91 // Calculate rewards since last collection
92 let rewards = account
93 .rewards_since_last_collection()
94 .ok_or(StakePoolError::CalculationFailure)?;
95
96 Ok(rewards)
97 }
98
99 fn update_pool_token_supply(
100 &self,
101 rewards: u64,
102 ) -> Result<Instruction, ProgramError> {
103 // Calculate new tokens based on rewards
104 let pool_tokens = (rewards as f64 * self.get_exchange_rate()?) as u64;
105
106 // Create mint instruction
107 Ok(spl_token::instruction::mint_to(
108 &spl_token::id(),
109 &self.pool_mint,
110 &self.pool_token_account,
111 &self.authority,
112 &[],
113 pool_tokens,
114 )?)
115 }
116}
Exchange Rate Management
Implement accurate exchange rate calculations and updates.
Exchange Rate Implementation
1pub struct ExchangeRate {
2 /// Total SOL (including rewards)
3 pub total_sol: u64,
4
5 /// Total stSOL supply
6 pub total_st_sol: u64,
7
8 /// Last update epoch
9 pub last_update_epoch: u64,
10}
11
12impl ExchangeRate {
13 pub fn calculate_rate(&self) -> Result<f64, ProgramError> {
14 if self.total_st_sol == 0 {
15 return Ok(1.0);
16 }
17
18 Ok(self.total_sol as f64 / self.total_st_sol as f64)
19 }
20
21 pub fn sol_to_st_sol(
22 &self,
23 sol_amount: u64,
24 ) -> Result<u64, ProgramError> {
25 let rate = self.calculate_rate()?;
26 Ok((sol_amount as f64 / rate) as u64)
27 }
28
29 pub fn st_sol_to_sol(
30 &self,
31 st_sol_amount: u64,
32 ) -> Result<u64, ProgramError> {
33 let rate = self.calculate_rate()?;
34 Ok((st_sol_amount as f64 * rate) as u64)
35 }
36
37 pub fn update_with_rewards(
38 &mut self,
39 rewards: u64,
40 epoch: u64,
41 ) -> ProgramResult {
42 // Add rewards to total SOL
43 self.total_sol = self.total_sol
44 .checked_add(rewards)
45 .ok_or(StakePoolError::CalculationFailure)?;
46
47 // Update epoch
48 self.last_update_epoch = epoch;
49
50 Ok(())
51 }
52}
Implementation Considerations
- Always use checked math operations to prevent overflows
- Implement proper validation for all operations
- Consider gas efficiency in validator selection
- Maintain accurate exchange rates
- Implement proper slashing protection
- Add comprehensive event logging