Skip to main content
Manual liquidity management leaves money on the table - positions go out of range, fees accumulate unclaimed, and opportunities are missed. Rust’s speed lets you build automated systems that maximize yield through constant optimization.

What You’ll Achieve

  • Earn 50-300% more fees than manual management
  • Automatically rebalance positions when price moves
  • Compound earnings back into positions for growth
  • Manage hundreds of positions simultaneously

Why Rust Excels at Yield Optimization

Real-time Calculations: Process complex yield math in microseconds
Memory Efficiency: Track thousands of positions with minimal RAM
Concurrent Operations: Monitor multiple pools and rebalance simultaneously
Zero Downtime: Rust’s reliability means no missed opportunities

Build an Automated Yield Optimizer

use dlmm_rust_sdk::{
    DlmmPool, LiquidityManager, Position, BinLiquidityDistribution,
    error::DlmmError,
};
use solana_sdk::{
    pubkey::Pubkey,
    signature::{Keypair, Signer},
};
use std::collections::HashMap;

/// Automated yield farming system
pub struct YieldOptimizer {
    pools: HashMap<Pubkey, DlmmPool>,
    positions: HashMap<String, Position>,
    rebalance_threshold: f64, // Price movement % to trigger rebalance
    min_fee_claim: f64,       // Minimum fees before claiming
}

#[derive(Debug, Clone)]
pub struct Position {
    pub id: String,
    pub pool_address: Pubkey,
    pub lower_bin_id: i32,
    pub upper_bin_id: i32,
    pub liquidity_x: u64,
    pub liquidity_y: u64,
    pub fees_earned_x: u64,
    pub fees_earned_y: u64,
    pub created_at: u64,
}

#[derive(Debug)]
pub struct LiquidityParams {
    pub amount_x: u64,
    pub amount_y: u64,
    pub active_bin_id: i32,
    pub width: u16, // Number of bins on each side
    pub distribution_type: DistributionType,
}

#[derive(Debug, Clone)]
pub enum DistributionType {
    Uniform,     // Equal distribution across all bins
    Concentrated, // More liquidity near active bin
    Custom(Vec<f64>), // Custom distribution weights
}

impl YieldOptimizer {
    pub fn new(rebalance_threshold: f64, min_fee_claim: f64) -> Self {
        Self {
            pools: HashMap::new(),
            positions: HashMap::new(),
            rebalance_threshold,
            min_fee_claim,
        }
    }

    pub fn add_pool(&mut self, pool: DlmmPool) {
        self.pools.insert(pool.get_address(), pool);
    }

    /// Create optimized position that maximizes fee earnings
    pub async fn create_yield_position(
        &mut self,
        pool_address: &Pubkey,
        params: LiquidityParams,
        payer: &Keypair,
    ) -> Result<String, DlmmError> {
        let pool = self.pools.get(pool_address)
            .ok_or(DlmmError::PoolNotFound)?;

        // Calculate bin distribution based on strategy
        let distribution = self.calculate_liquidity_distribution(
            params.active_bin_id,
            params.width,
            params.amount_x,
            params.amount_y,
            &params.distribution_type,
        )?;

        println!("Adding liquidity:");
        println!("  Pool: {}", pool_address);
        println!("  Amount X: {} tokens", params.amount_x);
        println!("  Amount Y: {} tokens", params.amount_y);
        println!("  Bin range: {} to {}", 
                 params.active_bin_id - (params.width as i32),
                 params.active_bin_id + (params.width as i32));
        println!("  Distribution type: {:?}", params.distribution_type);

        // Add liquidity to the pool
        let add_result = pool.add_liquidity(
            distribution,
            payer,
        ).await?;

        // Create position record
        let position_id = format!(
            "{}-{}-{}", 
            pool_address, 
            params.active_bin_id,
            std::time::SystemTime::now()
                .duration_since(std::time::UNIX_EPOCH)
                .unwrap()
                .as_secs()
        );

        let position = Position {
            id: position_id.clone(),
            pool_address: *pool_address,
            lower_bin_id: params.active_bin_id - (params.width as i32),
            upper_bin_id: params.active_bin_id + (params.width as i32),
            liquidity_x: add_result.liquidity_x_added,
            liquidity_y: add_result.liquidity_y_added,
            fees_earned_x: 0,
            fees_earned_y: 0,
            created_at: std::time::SystemTime::now()
                .duration_since(std::time::UNIX_EPOCH)
                .unwrap()
                .as_secs(),
        };

        self.positions.insert(position_id.clone(), position);

        println!("Liquidity added successfully!");
        println!("Position ID: {}", position_id);
        println!("Transaction: {}", add_result.signature);

        Ok(position_id)
    }

    /// Remove liquidity from a position
    pub async fn remove_liquidity(
        &mut self,
        position_id: &str,
        percentage: f64, // 0.0 to 1.0 (percentage to remove)
        payer: &Keypair,
    ) -> Result<RemoveLiquidityResult, DlmmError> {
        let position = self.positions.get(position_id)
            .ok_or(DlmmError::PositionNotFound)?
            .clone();

        let pool = self.pools.get(&position.pool_address)
            .ok_or(DlmmError::PoolNotFound)?;

        // Calculate amounts to remove
        let liquidity_x_to_remove = ((position.liquidity_x as f64) * percentage) as u64;
        let liquidity_y_to_remove = ((position.liquidity_y as f64) * percentage) as u64;

        println!("Removing liquidity:");
        println!("  Position: {}", position_id);
        println!("  Percentage: {:.1}%", percentage * 100.0);
        println!("  X to remove: {} tokens", liquidity_x_to_remove);
        println!("  Y to remove: {} tokens", liquidity_y_to_remove);

        // Remove liquidity from pool
        let remove_result = pool.remove_liquidity(
            position.lower_bin_id,
            position.upper_bin_id,
            liquidity_x_to_remove,
            liquidity_y_to_remove,
            payer,
        ).await?;

        // Update position
        if let Some(pos) = self.positions.get_mut(position_id) {
            pos.liquidity_x -= liquidity_x_to_remove;
            pos.liquidity_y -= liquidity_y_to_remove;
            pos.fees_earned_x += remove_result.fees_earned_x;
            pos.fees_earned_y += remove_result.fees_earned_y;
        }

        // Remove position if completely withdrawn
        if percentage >= 1.0 {
            self.positions.remove(position_id);
            println!("Position {} closed", position_id);
        }

        println!("Liquidity removed successfully!");
        println!("Tokens received X: {}", remove_result.tokens_received_x);
        println!("Tokens received Y: {}", remove_result.tokens_received_y);
        println!("Fees earned X: {}", remove_result.fees_earned_x);
        println!("Fees earned Y: {}", remove_result.fees_earned_y);

        Ok(remove_result)
    }

    /// Rebalance a position to maintain optimal range
    pub async fn rebalance_position(
        &mut self,
        position_id: &str,
        new_active_bin_id: i32,
        payer: &Keypair,
    ) -> Result<String, DlmmError> {
        // Remove current position
        let remove_result = self.remove_liquidity(position_id, 1.0, payer).await?;
        
        let position = self.positions.get(position_id)
            .ok_or(DlmmError::PositionNotFound)?
            .clone();
            
        let pool = self.pools.get(&position.pool_address)
            .ok_or(DlmmError::PoolNotFound)?;

        // Calculate new distribution around new active bin
        let width = ((position.upper_bin_id - position.lower_bin_id) / 2) as u16;
        
        let rebalance_params = LiquidityParams {
            amount_x: remove_result.tokens_received_x,
            amount_y: remove_result.tokens_received_y,
            active_bin_id: new_active_bin_id,
            width,
            distribution_type: DistributionType::Concentrated,
        };

        // Add liquidity at new range
        let new_position_id = self.add_liquidity(
            &position.pool_address,
            rebalance_params,
            payer,
        ).await?;

        println!("Position rebalanced:");
        println!("  Old position: {}", position_id);
        println!("  New position: {}", new_position_id);
        println!("  New active bin: {}", new_active_bin_id);

        Ok(new_position_id)
    }

    /// Get current position status and fees
    pub async fn get_position_status(
        &self,
        position_id: &str,
    ) -> Result<PositionStatus, DlmmError> {
        let position = self.positions.get(position_id)
            .ok_or(DlmmError::PositionNotFound)?;

        let pool = self.pools.get(&position.pool_address)
            .ok_or(DlmmError::PoolNotFound)?;

        // Get current pool state
        let pool_state = pool.get_pool_state().await?;
        
        // Calculate current value and fees
        let current_fees = pool.get_position_fees(
            position.lower_bin_id,
            position.upper_bin_id,
        ).await?;

        let current_value = pool.calculate_position_value(
            position.lower_bin_id,
            position.upper_bin_id,
            position.liquidity_x,
            position.liquidity_y,
        ).await?;

        // Check if position is in range
        let in_range = pool_state.active_id >= position.lower_bin_id && 
                       pool_state.active_id <= position.upper_bin_id;

        Ok(PositionStatus {
            position: position.clone(),
            current_value_x: current_value.value_x,
            current_value_y: current_value.value_y,
            pending_fees_x: current_fees.fees_x,
            pending_fees_y: current_fees.fees_y,
            in_range,
            current_price: pool_state.get_price(),
            impermanent_loss: current_value.impermanent_loss_percent,
        })
    }

    /// Auto-compound fees back into the position
    pub async fn compound_fees(
        &mut self,
        position_id: &str,
        payer: &Keypair,
    ) -> Result<CompoundResult, DlmmError> {
        let position = self.positions.get(position_id)
            .ok_or(DlmmError::PositionNotFound)?
            .clone();

        let pool = self.pools.get(&position.pool_address)
            .ok_or(DlmmError::PoolNotFound)?;

        // Collect pending fees
        let fees = pool.collect_fees(
            position.lower_bin_id,
            position.upper_bin_id,
            payer,
        ).await?;

        if fees.fees_x == 0 && fees.fees_y == 0 {
            return Ok(CompoundResult {
                fees_collected_x: 0,
                fees_collected_y: 0,
                liquidity_added_x: 0,
                liquidity_added_y: 0,
                signature: String::new(),
            });
        }

        // Add collected fees back as liquidity
        let distribution = self.calculate_liquidity_distribution(
            (position.lower_bin_id + position.upper_bin_id) / 2,
            ((position.upper_bin_id - position.lower_bin_id) / 2) as u16,
            fees.fees_x,
            fees.fees_y,
            &DistributionType::Uniform,
        )?;

        let add_result = pool.add_liquidity(distribution, payer).await?;

        // Update position
        if let Some(pos) = self.positions.get_mut(position_id) {
            pos.liquidity_x += add_result.liquidity_x_added;
            pos.liquidity_y += add_result.liquidity_y_added;
            pos.fees_earned_x += fees.fees_x;
            pos.fees_earned_y += fees.fees_y;
        }

        println!("Fees compounded:");
        println!("  Fees collected X: {}", fees.fees_x);
        println!("  Fees collected Y: {}", fees.fees_y);
        println!("  Liquidity added X: {}", add_result.liquidity_x_added);
        println!("  Liquidity added Y: {}", add_result.liquidity_y_added);

        Ok(CompoundResult {
            fees_collected_x: fees.fees_x,
            fees_collected_y: fees.fees_y,
            liquidity_added_x: add_result.liquidity_x_added,
            liquidity_added_y: add_result.liquidity_y_added,
            signature: add_result.signature,
        })
    }

    /// Calculate optimal liquidity distribution
    fn calculate_liquidity_distribution(
        &self,
        active_bin_id: i32,
        width: u16,
        amount_x: u64,
        amount_y: u64,
        distribution_type: &DistributionType,
    ) -> Result<BinLiquidityDistribution, DlmmError> {
        let lower_bin = active_bin_id - (width as i32);
        let upper_bin = active_bin_id + (width as i32);
        let num_bins = (upper_bin - lower_bin + 1) as usize;
        
        let mut distribution = BinLiquidityDistribution {
            bin_ids: Vec::new(),
            liquidity_x: Vec::new(),
            liquidity_y: Vec::new(),
        };

        match distribution_type {
            DistributionType::Uniform => {
                // Equal distribution across all bins
                let x_per_bin = amount_x / (num_bins as u64);
                let y_per_bin = amount_y / (num_bins as u64);
                
                for bin_id in lower_bin..=upper_bin {
                    distribution.bin_ids.push(bin_id);
                    distribution.liquidity_x.push(x_per_bin);
                    distribution.liquidity_y.push(y_per_bin);
                }
            }
            DistributionType::Concentrated => {
                // More liquidity near active bin (bell curve)
                let mut weights = Vec::new();
                let center = active_bin_id as f64;
                
                for bin_id in lower_bin..=upper_bin {
                    let distance = ((bin_id as f64) - center).abs();
                    let weight = (-distance * distance / (width as f64)).exp();
                    weights.push(weight);
                }
                
                let total_weight: f64 = weights.iter().sum();
                
                for (i, bin_id) in (lower_bin..=upper_bin).enumerate() {
                    let weight_fraction = weights[i] / total_weight;
                    distribution.bin_ids.push(bin_id);
                    distribution.liquidity_x.push(((amount_x as f64) * weight_fraction) as u64);
                    distribution.liquidity_y.push(((amount_y as f64) * weight_fraction) as u64);
                }
            }
            DistributionType::Custom(weights) => {
                if weights.len() != num_bins {
                    return Err(DlmmError::InvalidDistribution);
                }
                
                let total_weight: f64 = weights.iter().sum();
                
                for (i, bin_id) in (lower_bin..=upper_bin).enumerate() {
                    let weight_fraction = weights[i] / total_weight;
                    distribution.bin_ids.push(bin_id);
                    distribution.liquidity_x.push(((amount_x as f64) * weight_fraction) as u64);
                    distribution.liquidity_y.push(((amount_y as f64) * weight_fraction) as u64);
                }
            }
        }

        Ok(distribution)
    }

    /// Get all positions for monitoring
    pub fn get_all_positions(&self) -> &HashMap<String, Position> {
        &self.positions
    }
}

#[derive(Debug)]
pub struct PositionStatus {
    pub position: Position,
    pub current_value_x: u64,
    pub current_value_y: u64,
    pub pending_fees_x: u64,
    pub pending_fees_y: u64,
    pub in_range: bool,
    pub current_price: f64,
    pub impermanent_loss: f64,
}

#[derive(Debug)]
pub struct RemoveLiquidityResult {
    pub tokens_received_x: u64,
    pub tokens_received_y: u64,
    pub fees_earned_x: u64,
    pub fees_earned_y: u64,
    pub signature: String,
}

#[derive(Debug)]
pub struct CompoundResult {
    pub fees_collected_x: u64,
    pub fees_collected_y: u64,
    pub liquidity_added_x: u64,
    pub liquidity_added_y: u64,
    pub signature: String,
}

#[tokio::main]
async fn main() -> Result<(), Box<dyn std::error::Error>> {
    let mut manager = LiquidityManager::new();
    
    // Add pool (replace with actual pool)
    // manager.add_pool(usdc_sol_pool);
    
    let payer = Keypair::new();
    let pool_address = Pubkey::new_unique();
    
    // Add concentrated liquidity
    let liquidity_params = LiquidityParams {
        amount_x: 1_000_000, // 1 USDC
        amount_y: 2_000_000, // 2 SOL
        active_bin_id: 8_388_608, // Current active bin
        width: 20, // 20 bins on each side
        distribution_type: DistributionType::Concentrated,
    };
    
    match manager.add_liquidity(&pool_address, liquidity_params, &payer).await {
        Ok(position_id) => {
            println!("Position created: {}", position_id);
            
            // Check position status
            if let Ok(status) = manager.get_position_status(&position_id).await {
                println!("Position status: {:?}", status);
            }
            
            // Compound fees after some time
            if let Ok(compound_result) = manager.compound_fees(&position_id, &payer).await {
                println!("Compounded fees: {:?}", compound_result);
            }
        }
        Err(e) => println!("Failed to add liquidity: {:?}", e),
    }
    
    Ok(())
}

Liquidity Management Features

Position Types

  • Concentrated: More liquidity near current price
  • Uniform: Equal distribution across price range
  • Custom: User-defined distribution weights
  • Range Orders: Limit order-like behavior

Advanced Operations

  • Auto-Rebalancing: Maintain optimal price ranges
  • Fee Compounding: Reinvest earned fees automatically
  • Position Splitting: Divide large positions for better management
  • Cross-Pool Arbitrage: Exploit price differences

Risk Management

// Monitor position health
let status = manager.get_position_status(position_id).await?;
if status.impermanent_loss > 5.0 { // 5% IL threshold
    println!("Warning: High impermanent loss detected!");
    // Consider rebalancing or closing position
}

if !status.in_range {
    println!("Position out of range, no fees being earned");
    // Consider rebalancing to active range
}

Optimization Strategies

  1. Range Selection: Choose optimal price ranges based on volatility
  2. Fee Tier Analysis: Select pools with appropriate fee tiers
  3. Rebalancing Frequency: Balance gas costs vs fee earnings
  4. Capital Efficiency: Maximize returns per unit of capital