Fee Structure
A comprehensive guide to implementing and managing fees in your liquid staking protocol on Solana. Learn about fee types, calculation methods, distribution mechanisms, and best practices for sustainable revenue generation.
Overview
The fee structure is a critical component of your liquid staking protocol, balancing protocol sustainability with user incentives. This guide covers the implementation of various fee types, calculation methods, and distribution mechanisms.
Fee Types
The protocol implements several types of fees to ensure sustainable operation and fair value distribution.
Protocol Fee
A percentage of staking rewards collected for protocol maintenance and development. Typically ranges from 1% to 10% of rewards.
Performance Fee
Additional fee based on validator performance and excess returns. Incentivizes optimal validator selection and management.
Withdrawal Fee
Small fee applied to withdrawals to prevent abuse and cover operational costs. Usually 0.1% to 0.5% of withdrawal amount.
Fee Structure Implementation
1#[derive(Clone, Debug, PartialEq)]
2pub struct FeeSchedule {
3 /// Protocol fee percentage (0-100)
4 pub protocol_fee: u8,
5
6 /// Performance fee percentage (0-100)
7 pub performance_fee: u8,
8
9 /// Withdrawal fee percentage (0-100)
10 pub withdrawal_fee: u8,
11
12 /// Minimum fee amount in lamports
13 pub min_fee: u64,
14
15 /// Maximum fee percentage (0-100)
16 pub max_fee: u8,
17}
18
19impl FeeSchedule {
20 pub fn validate(&self) -> ProgramResult {
21 // Validate fee ranges
22 if self.protocol_fee > 100
23 || self.performance_fee > 100
24 || self.withdrawal_fee > 100
25 || self.max_fee > 100 {
26 return Err(StakePoolError::InvalidFeeRange.into());
27 }
28
29 // Validate total fee cap
30 let total_fee = self.protocol_fee
31 .checked_add(self.performance_fee)
32 .ok_or(StakePoolError::CalculationFailure)?;
33
34 if total_fee > self.max_fee {
35 return Err(StakePoolError::FeeLimitExceeded.into());
36 }
37
38 Ok(())
39 }
40}
Fee Calculation
Implement precise fee calculation methods that handle different scenarios and edge cases.
Fee Calculation Implementation
1pub struct FeeCalculator {
2 /// Fee schedule
3 pub fee_schedule: FeeSchedule,
4
5 /// Historical performance data
6 pub performance_data: PerformanceHistory,
7}
8
9impl FeeCalculator {
10 pub fn calculate_protocol_fee(
11 &self,
12 reward_amount: u64,
13 ) -> Result<u64, ProgramError> {
14 let fee_percentage = self.fee_schedule.protocol_fee as u64;
15
16 let fee_amount = (reward_amount as u128)
17 .checked_mul(fee_percentage as u128)
18 .and_then(|product| product.checked_div(100))
19 .and_then(|quotient| u64::try_from(quotient).ok())
20 .ok_or(StakePoolError::CalculationFailure)?;
21
22 // Apply minimum fee if necessary
23 Ok(std::cmp::max(fee_amount, self.fee_schedule.min_fee))
24 }
25
26 pub fn calculate_performance_fee(
27 &self,
28 epoch_return: f64,
29 baseline_return: f64,
30 stake_amount: u64,
31 ) -> Result<u64, ProgramError> {
32 // Only charge performance fee on excess returns
33 if epoch_return <= baseline_return {
34 return Ok(0);
35 }
36
37 let excess_return = epoch_return - baseline_return;
38 let fee_percentage = self.fee_schedule.performance_fee as f64 / 100.0;
39
40 let fee_amount = (stake_amount as f64 * excess_return * fee_percentage) as u64;
41
42 Ok(fee_amount)
43 }
44
45 pub fn calculate_withdrawal_fee(
46 &self,
47 withdrawal_amount: u64,
48 ) -> Result<u64, ProgramError> {
49 let fee_percentage = self.fee_schedule.withdrawal_fee as u64;
50
51 let fee_amount = (withdrawal_amount as u128)
52 .checked_mul(fee_percentage as u128)
53 .and_then(|product| product.checked_div(100))
54 .and_then(|quotient| u64::try_from(quotient).ok())
55 .ok_or(StakePoolError::CalculationFailure)?;
56
57 Ok(fee_amount)
58 }
59}
Fee Distribution
Implement secure fee collection and distribution mechanisms to handle protocol revenue.
Fee Distribution Implementation
1pub struct FeeDistributor {
2 /// Treasury account
3 pub treasury: Pubkey,
4
5 /// Development fund account
6 pub dev_fund: Pubkey,
7
8 /// Distribution ratios
9 pub distribution_ratios: DistributionRatios,
10}
11
12impl FeeDistributor {
13 pub fn distribute_fees(
14 &self,
15 program_id: &Pubkey,
16 fee_account: &AccountInfo,
17 amount: u64,
18 ) -> ProgramResult {
19 // Validate accounts
20 let treasury_account = next_account_info(account_info_iter)?;
21 let dev_fund_account = next_account_info(account_info_iter)?;
22
23 // Calculate distribution amounts
24 let treasury_amount = (amount as u128)
25 .checked_mul(self.distribution_ratios.treasury as u128)
26 .and_then(|product| product.checked_div(100))
27 .and_then(|quotient| u64::try_from(quotient).ok())
28 .ok_or(StakePoolError::CalculationFailure)?;
29
30 let dev_fund_amount = amount
31 .checked_sub(treasury_amount)
32 .ok_or(StakePoolError::CalculationFailure)?;
33
34 // Transfer to treasury
35 self.transfer_to_treasury(
36 program_id,
37 fee_account,
38 treasury_account,
39 treasury_amount,
40 )?;
41
42 // Transfer to dev fund
43 self.transfer_to_dev_fund(
44 program_id,
45 fee_account,
46 dev_fund_account,
47 dev_fund_amount,
48 )?;
49
50 // Emit distribution event
51 emit!(FeeDistributed {
52 treasury_amount,
53 dev_fund_amount,
54 timestamp: Clock::get()?.unix_timestamp,
55 });
56
57 Ok(())
58 }
59}
Fee Management
Implement administrative functions to manage and update fee parameters.
Fee Management Implementation
1pub struct FeeManager {
2 /// Manager authority
3 pub manager: Pubkey,
4
5 /// Current fee schedule
6 pub fee_schedule: FeeSchedule,
7
8 /// Pending fee updates
9 pub pending_updates: Option<PendingFeeUpdate>,
10}
11
12impl FeeManager {
13 pub fn update_fee_schedule(
14 &mut self,
15 authority: &AccountInfo,
16 new_schedule: FeeSchedule,
17 ) -> ProgramResult {
18 // Verify authority
19 if !authority.is_signer {
20 return Err(StakePoolError::SignatureMissing.into());
21 }
22 if authority.key != &self.manager {
23 return Err(StakePoolError::InvalidManager.into());
24 }
25
26 // Validate new schedule
27 new_schedule.validate()?;
28
29 // Create pending update with timelock
30 self.pending_updates = Some(PendingFeeUpdate {
31 schedule: new_schedule,
32 effective_epoch: Clock::get()?.epoch + TIMELOCK_EPOCHS,
33 authority: *authority.key,
34 });
35
36 // Emit update event
37 emit!(FeeUpdateInitiated {
38 old_schedule: self.fee_schedule.clone(),
39 new_schedule,
40 effective_epoch: self.pending_updates.as_ref().unwrap().effective_epoch,
41 });
42
43 Ok(())
44 }
45
46 pub fn apply_pending_update(&mut self) -> ProgramResult {
47 let current_epoch = Clock::get()?.epoch;
48
49 if let Some(pending) = &self.pending_updates {
50 if current_epoch >= pending.effective_epoch {
51 // Apply update
52 self.fee_schedule = pending.schedule.clone();
53 self.pending_updates = None;
54
55 // Emit event
56 emit!(FeeUpdateApplied {
57 new_schedule: self.fee_schedule.clone(),
58 epoch: current_epoch,
59 });
60 }
61 }
62
63 Ok(())
64 }
65}
Implementation Considerations
- Implement proper access controls for fee management functions
- Use checked math operations to prevent overflows
- Consider implementing fee caps and minimum thresholds
- Add timelocks for fee parameter updates
- Maintain transparent fee reporting mechanisms
- Consider market conditions when setting fees