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.

Last updated: 2024-03-21
Edit on GitHub

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 Structure Flow

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