use { solana_farm_client::client::FarmClient, solana_sdk::{commitment_config::CommitmentConfig, signature::Keypair, signer::Signer}, }; use crate::{utils, utils::Swap}; const MAX_SOL_BALANCE_TO_USE: f64 = 0.1; pub fn do_swap(client: &FarmClient, keypair: &Keypair, swap: &Swap) { let amount = if swap.amount == 0.0 { utils::get_token_or_native_balance(client, &keypair.pubkey(), swap.from_token) } else if swap.amount < 0.0 { -1.0 * swap.amount * utils::get_token_or_native_balance(client, &keypair.pubkey(), swap.from_token) } else { swap.amount }; if amount < 0.0003 { return; } println!( ">> Swap {} {} to {}", amount, swap.from_token, swap.to_token ); println!( " Done: {}", client .swap( keypair, swap.protocol .parse() .expect("Failed to parse protocol argument"), swap.from_token, swap.to_token, amount, 0.0, ) .unwrap() ); let _ = utils::get_balances( client, &keypair.pubkey(), swap.from_token, swap.to_token, "After swap", ); } pub fn do_add_liquidity( client: &FarmClient, keypair: &Keypair, pool_name: &str, max_token_a_ui_amount: f64, max_token_b_ui_amount: f64, ) -> f64 { println!( ">> Add liquidity to {}: {}, {}", pool_name, max_token_a_ui_amount, max_token_b_ui_amount ); let (token_a_str, token_b_str, lp_token_name) = client.get_pool_token_names(pool_name).unwrap(); let lp_balance = utils::get_token_or_native_balance(client, &keypair.pubkey(), &lp_token_name); println!( " Done: {}", client .add_liquidity_pool( keypair, pool_name, max_token_a_ui_amount, max_token_b_ui_amount, ) .unwrap() ); let _ = utils::get_balances( client, &keypair.pubkey(), &token_a_str, &token_b_str, "After add liquidity", ); let _ = utils::get_balance(client, &keypair.pubkey(), &lp_token_name, "LP"); utils::get_token_or_native_balance(client, &keypair.pubkey(), &lp_token_name) - lp_balance } pub fn do_stake(client: &FarmClient, keypair: &Keypair, farm_name: &str, amount: f64) { println!(">> Stake liquidity to {}: {}", farm_name, amount); let (token_a_str, token_b_str, lp_token_name) = client.get_farm_token_names(farm_name).unwrap(); println!( " Done: {}", client.stake(keypair, farm_name, amount).unwrap() ); let _ = utils::get_balances( client, &keypair.pubkey(), &token_a_str, &token_b_str, "After stake", ); let _ = utils::get_balance(client, &keypair.pubkey(), &lp_token_name, "LP after stake"); } pub fn do_harvest(client: &FarmClient, keypair: &Keypair, farm_name: &str) { println!(">> Harvest from {}", farm_name); let (token_a_str, token_b_str, lp_token_name) = client.get_farm_token_names(farm_name).unwrap(); println!(" Done: {}", client.harvest(keypair, farm_name).unwrap()); let _ = utils::get_balances( client, &keypair.pubkey(), &token_a_str, &token_b_str, "After harvest", ); let _ = utils::get_balance( client, &keypair.pubkey(), &lp_token_name, "LP after harvest", ); } pub fn do_unstake(client: &FarmClient, keypair: &Keypair, farm_name: &str, amount: f64) { println!(">> Unstake liquidity from {}: {}", farm_name, amount); let (token_a_str, token_b_str, lp_token_name) = client.get_farm_token_names(farm_name).unwrap(); println!( " Done: {}", client.unstake(keypair, farm_name, amount).unwrap() ); let _ = utils::get_balances( client, &keypair.pubkey(), &token_a_str, &token_b_str, "After unstake", ); let _ = utils::get_balance( client, &keypair.pubkey(), &lp_token_name, "LP after unstake", ); } pub fn do_remove_liquidity(client: &FarmClient, keypair: &Keypair, pool_name: &str, amount: f64) { println!(">> Remove liquidity from {}: {}", pool_name, amount); let (token_a_str, token_b_str, lp_token_name) = client.get_pool_token_names(pool_name).unwrap(); println!( " Done: {}", client .remove_liquidity_pool(keypair, pool_name, amount) .unwrap() ); let _ = utils::get_balances( client, &keypair.pubkey(), &token_a_str, &token_b_str, "After remove liquidity", ); let _ = utils::get_balance(client, &keypair.pubkey(), &lp_token_name, "LP"); } pub fn cleanup( client: &FarmClient, keypair: &Keypair, pool_name: &str, cleanup_swaps: Vec, pool_only: bool, ) { println!("\n>>> Clean-up {}...", pool_name); let wallet = keypair.pubkey(); let (token_a_str, token_b_str, lp_token_name) = client.get_pool_token_names(pool_name).unwrap(); if !pool_only { let farms = client.find_farms_with_lp(&lp_token_name).unwrap(); for farm in farms.iter() { let farm_token_name = "LP.".to_string() + &farm.name.as_str()[..farm.name.as_str().len() - 3]; if let Ok(dd_farms) = client.find_farms_with_lp(&farm_token_name) { for farm in dd_farms.iter() { if let Ok(staked_balance) = client.get_user_stake_balance(&wallet, farm.name.as_str()) { if staked_balance > 0.0 { do_unstake(client, keypair, farm.name.as_str(), staked_balance); } } } } if let Ok(staked_balance) = client.get_user_stake_balance(&wallet, farm.name.as_str()) { if staked_balance > 0.0 { do_unstake(client, keypair, farm.name.as_str(), staked_balance); } } } } let lp_token_balance = utils::get_token_or_native_balance(client, &wallet, &lp_token_name); if lp_token_balance > 0.0 { do_remove_liquidity(client, keypair, pool_name, lp_token_balance); } for swap in cleanup_swaps { do_swap(client, keypair, &swap); } if token_a_str != "SOL" { let token_a_balance = utils::get_token_or_native_balance(client, &wallet, &token_a_str); if token_a_balance > 0.0 { do_swap( client, keypair, &Swap { protocol: "RDM", from_token: token_a_str.as_str(), to_token: "SOL", amount: token_a_balance, }, ); } } if token_b_str != "SOL" { let token_b_balance = utils::get_token_or_native_balance(client, &wallet, &token_b_str); if token_b_balance > 0.0 { do_swap( client, keypair, &Swap { protocol: "RDM", from_token: token_b_str.as_str(), to_token: "SOL", amount: token_b_balance, }, ); } } } pub fn run_test(pool_name: &str, swaps: Vec, cleanup_swaps: Vec, pool_only: bool) { let (endpoint, keypair) = utils::get_endpoint_and_keypair(); let client = FarmClient::new_with_commitment(&endpoint, CommitmentConfig::confirmed()); let wallet = keypair.pubkey(); cleanup( &client, &keypair, pool_name, cleanup_swaps.clone(), pool_only, ); println!("\n>>> Testing {}...", pool_name); let (token_a_str, token_b_str, lp_token_name) = client.get_pool_token_names(pool_name).unwrap(); let (_, _) = utils::get_balances(&client, &wallet, &token_a_str, &token_b_str, "Initial"); //initial swaps for swap in swaps { do_swap(&client, &keypair, &swap); } let token_a_balance = if token_a_str == "SOL" { MAX_SOL_BALANCE_TO_USE.min(utils::get_token_or_native_balance( &client, &wallet, &token_a_str, )) } else { utils::get_token_or_native_balance(&client, &wallet, &token_a_str) }; let token_b_balance = if token_b_str == "SOL" { MAX_SOL_BALANCE_TO_USE.min(utils::get_token_or_native_balance( &client, &wallet, &token_b_str, )) } else { utils::get_token_or_native_balance(&client, &wallet, &token_b_str) }; assert!(token_a_balance > 0.0 && token_b_balance > 0.0); // main tests let mut lp_received = do_add_liquidity(&client, &keypair, pool_name, token_a_balance / 3.0, 0.0); assert!(lp_received > 0.0); lp_received += do_add_liquidity(&client, &keypair, pool_name, 0.0, token_b_balance / 3.0); if !pool_only { let farms = client.find_farms_with_lp(&lp_token_name).unwrap(); for farm in farms.iter() { do_stake(&client, &keypair, farm.name.as_str(), lp_received / 2.0); do_stake(&client, &keypair, farm.name.as_str(), 0.0); do_harvest(&client, &keypair, farm.name.as_str()); // orca double-dip farms if farm.name.starts_with("ORC.") { let farm_token_name = "LP.".to_string() + &farm.name.as_str()[..farm.name.as_str().len() - 3]; if let Ok(dd_farms) = client.find_farms_with_lp(&farm_token_name) { for dd_farm in dd_farms.iter() { do_stake(&client, &keypair, dd_farm.name.as_str(), lp_received / 2.0); do_stake(&client, &keypair, dd_farm.name.as_str(), 0.0); do_harvest(&client, &keypair, dd_farm.name.as_str()); do_unstake(&client, &keypair, dd_farm.name.as_str(), lp_received / 2.0); do_unstake(&client, &keypair, dd_farm.name.as_str(), 0.0); } } } do_unstake(&client, &keypair, farm.name.as_str(), lp_received / 2.0); do_unstake(&client, &keypair, farm.name.as_str(), 0.0); } } do_remove_liquidity(&client, &keypair, pool_name, lp_received / 2.0); do_remove_liquidity(&client, &keypair, pool_name, 0.0); cleanup(&client, &keypair, pool_name, cleanup_swaps, pool_only); let (_, _) = utils::get_balances(&client, &wallet, &token_a_str, &token_b_str, "Final"); }