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 microsecondsMemory 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
Copy
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,
¶ms.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
Copy
// 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
- Range Selection: Choose optimal price ranges based on volatility
- Fee Tier Analysis: Select pools with appropriate fee tiers
- Rebalancing Frequency: Balance gas costs vs fee earnings
- Capital Efficiency: Maximize returns per unit of capital