Files
2026-07-12 14:35:58 +08:00

1519 lines
51 KiB
Rust

use clap::Parser;
use sol_trade_sdk::{
common::{
fast_fn::get_associated_token_address_with_program_id_fast_use_seed,
spl_associated_token_account::{
create_associated_token_account_idempotent, get_associated_token_address,
},
spl_token::{self, close_account},
AnyResult, TradeConfig,
},
constants::WSOL_TOKEN_ACCOUNT,
swqos::SwqosConfig,
trading::{
core::params::{
BonkParams, DexParamEnum, PumpFunParams, PumpSwapParams, RaydiumAmmV4Params,
RaydiumCpmmParams,
},
factory::DexType,
},
SolanaTrade, TradeBuyParams, TradeSellParams, TradeTokenType,
};
use solana_commitment_config::CommitmentConfig;
use solana_sdk::{
message::{AccountMeta, Instruction},
native_token::sol_str_to_lamports,
pubkey::Pubkey,
};
use solana_system_interface::instruction::transfer;
use std::sync::Arc;
use std::{
io::{self, Write},
str::FromStr,
};
static DEXS: &[&str] = &["pumpfun", "pumpswap", "bonk", "raydium_v4", "raydium_cpmm"];
fn rpc_url() -> String {
std::env::var("RPC_URL")
.or_else(|_| std::env::var("SOLANA_RPC_URL"))
.unwrap_or_else(|_| "https://api.mainnet-beta.solana.com".to_string())
}
#[derive(Parser)]
#[command(name = "sol-trade-cli")]
#[command(about = "SOL Trade CLI - A command line interface for trading tokens on Solana")]
struct Cli {
#[command(subcommand)]
command: Option<Command>,
}
#[derive(Parser)]
enum Command {
/// Buy tokens with SOL
Buy {
/// Token mint address
mint: String,
/// DEX to use (pumpfun, pumpswap, bonk, raydium_v4, raydium_cpmm)
dex: String,
/// Amount of SOL to spend
#[arg(short, long)]
amount: f64,
/// Slippage tolerance (optional)
#[arg(short, long)]
slippage: Option<u64>,
/// AMM address for Raydium V4 (required for raydium_v4)
#[arg(long)]
amm: Option<String>,
/// Pool address for Raydium CPMM (required for raydium_cpmm)
#[arg(long)]
pool: Option<String>,
},
/// Sell tokens for SOL
Sell {
/// Token mint address
mint: String,
/// DEX to use (pumpfun, pumpswap, bonk, raydium_v4, raydium_cpmm)
dex: String,
/// Amount of tokens to sell (sell all if not specified)
#[arg(short, long)]
amount: Option<f64>,
/// Slippage tolerance (optional)
#[arg(short, long)]
slippage: Option<u64>,
/// AMM address for Raydium V4 (required for raydium_v4)
#[arg(long)]
amm: Option<String>,
/// Pool address for Raydium CPMM (required for raydium_cpmm)
#[arg(long)]
pool: Option<String>,
},
/// Wrap SOL to WSOL
WrapSol {
/// Amount of SOL to wrap
#[arg(short, long)]
amount: f64,
},
/// Close WSOL account and recover SOL
CloseWsol,
/// Check wallet status and balances
Wallet,
/// Start interactive mode
Interactive,
}
#[tokio::main]
async fn main() -> Result<(), Box<dyn std::error::Error>> {
let cli = Cli::parse();
match cli.command {
Some(command) => {
// Handle direct command line usage
handle_command(command).await?
}
None => {
// No command provided, run interactive mode
println!("🚀 SOL Trade CLI - Interactive Mode");
println!("═══════════════════════════════════\n");
run_interactive_mode().await?
}
}
Ok(())
}
async fn handle_command(command: Command) -> Result<(), Box<dyn std::error::Error>> {
match command {
Command::Buy { mint, dex, amount, slippage, amm, pool } => {
validate_dex_params(&dex, &amm, &pool)?;
match dex.as_str() {
"raydium_v4" => {
if let Some(amm_addr) = amm {
handle_buy_rv4(&mint, &amm_addr, amount, slippage).await?
}
}
"raydium_cpmm" => {
if let Some(pool_addr) = pool {
handle_buy_rcpmm(&mint, &pool_addr, amount, slippage).await?
}
}
_ => handle_buy(&mint, &dex, amount, slippage).await?,
}
}
Command::Sell { mint, dex, amount, slippage, amm, pool } => {
validate_dex_params(&dex, &amm, &pool)?;
match dex.as_str() {
"raydium_v4" => {
if let Some(amm_addr) = amm {
handle_sell_rv4(&mint, &amm_addr, amount, slippage).await?
}
}
"raydium_cpmm" => {
if let Some(pool_addr) = pool {
handle_sell_rcpmm(&mint, &pool_addr, amount, slippage).await?
}
}
_ => handle_sell(&mint, &dex, amount, slippage).await?,
}
}
Command::WrapSol { amount } => handle_wrap_sol(amount).await?,
Command::CloseWsol => handle_close_wsol().await?,
Command::Wallet => handle_wallet().await?,
Command::Interactive => {
println!("🚀 SOL Trade CLI - Interactive Mode");
println!("═══════════════════════════════════\n");
run_interactive_mode().await?
}
}
Ok(())
}
fn validate_dex_params(
dex: &str,
amm: &Option<String>,
pool: &Option<String>,
) -> Result<(), Box<dyn std::error::Error>> {
if !DEXS.contains(&dex) {
return Err(format!("Invalid DEX: '{}'. Supported DEXs: {}", dex, DEXS.join(", ")).into());
}
match dex {
"raydium_v4" => {
if amm.is_none() {
return Err("AMM address is required for Raydium V4. Use --amm flag.".into());
}
}
"raydium_cpmm" => {
if pool.is_none() {
return Err("Pool address is required for Raydium CPMM. Use --pool flag.".into());
}
}
_ => {}
}
Ok(())
}
async fn show_startup_info() {
println!("\n📋 STARTUP INFORMATION");
println!("══════════════════════════════════════");
println!("🌐 RPC URL: {}", rpc_url());
// Try to initialize client to show wallet info
match initialize_real_client().await {
Ok(client) => {
println!("👛 Wallet Address: {}", client.get_payer_pubkey());
// Try to get balance
match client.get_payer_sol_balance().await {
Ok(balance) => {
println!("💰 SOL Balance: {:.6} SOL", balance as f64 / 1_000_000_000.0);
}
Err(_) => {
println!("💰 SOL Balance: Unable to fetch (network issue)");
}
}
}
Err(err) => {
println!("👛 Wallet: unavailable ({})", err);
println!("💰 SOL Balance: Unable to fetch");
}
}
println!("⚡ Network: Mainnet-beta");
println!("══════════════════════════════════════");
// Show help
show_help();
println!("══════════════════════════════════════\n");
}
async fn run_interactive_mode() -> Result<(), Box<dyn std::error::Error>> {
println!("🔄 Interactive Mode Started");
// Show startup information
show_startup_info().await;
println!("Type 'help' for available commands, 'quit' to exit\n");
loop {
print!("sol-trade> ");
io::stdout().flush().unwrap();
let mut input = String::new();
io::stdin().read_line(&mut input)?;
let input = input.trim();
if input.is_empty() {
continue;
}
match input {
"quit" | "exit" | "q" => {
println!("👋 Goodbye!");
break;
}
"help" | "h" => {
show_help();
}
"wallet" | "status" => {
handle_wallet().await?;
}
"close_wsol" | "close-wsol" => {
handle_close_wsol().await?;
}
_ => {
if let Some(args) = input.strip_prefix("raydium_cpmm_buy ") {
handle_interactive_raydium_cpmm_buy(args).await?;
} else if let Some(args) = input.strip_prefix("raydium_cpmm_sell ") {
handle_interactive_raydium_cpmm_sell(args).await?;
} else if let Some(args) = input.strip_prefix("raydium_v4_buy ") {
handle_interactive_raydium_v4_buy(args).await?;
} else if let Some(args) = input.strip_prefix("raydium_v4_sell ") {
handle_interactive_raydium_v4_sell(args).await?;
} else if let Some(args) = input.strip_prefix("buy ") {
handle_interactive_buy(args).await?;
} else if let Some(args) = input.strip_prefix("sell ") {
handle_interactive_sell(args).await?;
} else if let Some(amount_str) =
input.strip_prefix("wrap_sol ").or_else(|| input.strip_prefix("wrap-sol "))
{
if let Ok(amount) = amount_str.parse::<f64>() {
handle_wrap_sol(amount).await?;
} else {
println!("❌ Invalid amount. Usage: wrap_sol <amount>");
}
} else {
println!(
"❌ Unknown command: '{}'. Type 'help' for available commands.",
input
);
}
}
}
println!(); // Add blank line after each command
}
Ok(())
}
fn show_help() {
println!("📚 Available Commands:");
println!(" buy <mint> <dex> <sol_amount> [slippage] - Buy tokens with SOL");
println!(" sell <mint> <dex> [token_amount] [slippage] - Sell tokens (all if no amount)");
println!(" raydium_v4_buy <mint> <amm> <sol_amount> [slippage] - Buy tokens with SOL from Raydium V4");
println!(
" raydium_v4_sell <mint> <amm> [token_amount] [slippage] - Sell tokens from Raydium V4"
);
println!(" raydium_cpmm_buy <mint> <pool_address> <sol_amount> [slippage] - Buy tokens with SOL from Raydium CPMM");
println!(" raydium_cpmm_sell <mint> <pool_address> [token_amount] [slippage] - Sell tokens from Raydium CPMM");
println!(" wrap_sol <amount> - Wrap SOL to WSOL");
println!(" close_wsol - Close WSOL account");
println!(" wallet - Check wallet status");
println!(" help - Show this help");
println!(" quit - Exit interactive mode");
println!();
println!("🏛️ Supported DEXs: {}", DEXS.join(", "));
println!();
println!("📝 Examples:");
println!(" buy xxxxxxx pumpfun 1.0");
println!(" wrap_sol 2.5");
}
async fn handle_interactive_buy(args: &str) -> Result<(), Box<dyn std::error::Error>> {
let parts: Vec<&str> = args.split_whitespace().collect();
if parts.len() < 3 {
println!("❌ Usage: buy <mint> <dex> <sol_amount> [slippage]");
return Ok(());
}
let mint = parts[0];
let dex = parts[1];
let sol_amount = match parts[2].parse::<f64>() {
Ok(amount) => amount,
Err(_) => {
println!("❌ Invalid SOL amount: {}", parts[2]);
return Ok(());
}
};
let slippage = if parts.len() > 3 { parts[3].parse::<u64>().ok() } else { None };
handle_buy(mint, dex, sol_amount, slippage).await
}
async fn handle_interactive_sell(args: &str) -> Result<(), Box<dyn std::error::Error>> {
let parts: Vec<&str> = args.split_whitespace().collect();
if parts.len() < 2 {
println!("❌ Usage: sell <mint> <dex> [token_amount] [slippage]");
return Ok(());
}
let mint = parts[0];
let dex = parts[1];
let token_amount = if parts.len() > 2 {
match parts[2].parse::<f64>() {
Ok(amount) => Some(amount),
Err(_) => {
println!("❌ Invalid token amount: {}", parts[2]);
return Ok(());
}
}
} else {
None
};
let slippage = if parts.len() > 3 { parts[3].parse::<u64>().ok() } else { None };
handle_sell(mint, dex, token_amount, slippage).await
}
async fn handle_interactive_raydium_v4_buy(args: &str) -> Result<(), Box<dyn std::error::Error>> {
let parts: Vec<&str> = args.split_whitespace().collect();
if parts.len() < 3 {
println!("❌ Usage: raydium_v4_buy <mint> <amm> <sol_amount> [slippage]");
return Ok(());
}
let mint = parts[0];
let amm = parts[1];
let sol_amount = match parts[2].parse::<f64>() {
Ok(amount) => amount,
Err(_) => {
println!("❌ Invalid SOL amount: {}", parts[2]);
return Ok(());
}
};
let slippage = if parts.len() > 3 { parts[3].parse::<u64>().ok() } else { None };
handle_buy_rv4(mint, amm, sol_amount, slippage).await
}
async fn handle_interactive_raydium_v4_sell(args: &str) -> Result<(), Box<dyn std::error::Error>> {
let parts: Vec<&str> = args.split_whitespace().collect();
if parts.len() < 2 {
println!("❌ Usage: raydium_v4_sell <mint> <amm> [token_amount] [slippage]");
return Ok(());
}
let mint = parts[0];
let amm = parts[1];
let token_amount = if parts.len() > 2 {
match parts[2].parse::<f64>() {
Ok(amount) => Some(amount),
Err(_) => {
println!("❌ Invalid token amount: {}", parts[2]);
return Ok(());
}
}
} else {
None
};
let slippage = if parts.len() > 3 { parts[3].parse::<u64>().ok() } else { None };
handle_sell_rv4(mint, amm, token_amount, slippage).await
}
async fn handle_interactive_raydium_cpmm_buy(args: &str) -> Result<(), Box<dyn std::error::Error>> {
let parts: Vec<&str> = args.split_whitespace().collect();
if parts.len() < 3 {
println!("❌ Usage: raydium_cpmm_buy <mint> <pool_address> <sol_amount> [slippage]");
return Ok(());
}
let mint = parts[0];
let pool_address = parts[1];
let sol_amount = match parts[2].parse::<f64>() {
Ok(amount) => amount,
Err(_) => {
println!("❌ Invalid SOL amount: {}", parts[2]);
return Ok(());
}
};
let slippage = if parts.len() > 3 { parts[3].parse::<u64>().ok() } else { None };
handle_buy_rcpmm(mint, pool_address, sol_amount, slippage).await
}
async fn handle_interactive_raydium_cpmm_sell(
args: &str,
) -> Result<(), Box<dyn std::error::Error>> {
let parts: Vec<&str> = args.split_whitespace().collect();
if parts.len() < 2 {
println!("❌ Usage: raydium_cpmm_sell <mint> <pool_address> [token_amount] [slippage]");
return Ok(());
}
let mint = parts[0];
let pool_address = parts[1];
let token_amount = if parts.len() > 2 {
match parts[2].parse::<f64>() {
Ok(amount) => Some(amount),
Err(_) => {
println!("❌ Invalid token amount: {}", parts[2]);
return Ok(());
}
}
} else {
None
};
let slippage = if parts.len() > 3 { parts[3].parse::<u64>().ok() } else { None };
handle_sell_rcpmm(mint, pool_address, token_amount, slippage).await
}
async fn check_mint_ata(
client: &SolanaTrade,
mint: &str,
) -> Result<(bool, bool, Pubkey, f64, u8), Box<dyn std::error::Error>> {
let mut create_mint_ata = true;
let mut use_seed = false;
let mut decimals = 0;
let mut amount_f64: f64 = 0.0;
let mint_pubkey = Pubkey::from_str(mint).unwrap();
if let Ok(mint_info) = client.infrastructure.rpc.get_account(&mint_pubkey).await {
let owner_pubkey = mint_info.owner;
let mint_ata = get_associated_token_address_with_program_id_fast_use_seed(
&client.get_payer_pubkey(),
&mint_pubkey,
&owner_pubkey,
false,
);
if let Ok(balance) = client.infrastructure.rpc.get_token_account_balance(&mint_ata).await {
let amount = balance.ui_amount.unwrap_or(0.0);
decimals = balance.decimals;
amount_f64 = amount * 10_f64.powi(decimals as i32);
create_mint_ata = false;
use_seed = false;
}
if !create_mint_ata {
return Ok((create_mint_ata, use_seed, owner_pubkey, amount_f64, decimals));
}
let mint_ata = get_associated_token_address_with_program_id_fast_use_seed(
&client.get_payer_pubkey(),
&mint_pubkey,
&owner_pubkey,
true,
);
if client.infrastructure.rpc.get_token_account_balance(&mint_ata).await.is_ok() {
create_mint_ata = false;
use_seed = true;
}
return Ok((create_mint_ata, use_seed, owner_pubkey, amount_f64, decimals));
}
Err("Mint account not found".to_string().into())
}
// Buy and sell functions - currently in demo mode since trading logic is complex
async fn handle_buy(
mint: &str,
dex: &str,
sol_amount: f64,
slippage: Option<u64>,
) -> Result<(), Box<dyn std::error::Error>> {
// Validate DEX parameter
if !DEXS.contains(&dex) {
println!("❌ Invalid DEX: '{}'. Supported DEXs: {}", dex, DEXS.join(", "));
return Ok(());
}
let client = initialize_real_client().await?;
let (create_mint_ata, use_seed, owner_pubkey, _amount_f64, _decimals) =
check_mint_ata(&client, mint).await?;
match dex {
"pumpfun" => {
handle_buy_pumpfun(mint, sol_amount, slippage, create_mint_ata, use_seed, owner_pubkey)
.await?;
}
"pumpswap" => {
handle_buy_pumpswap(
mint,
sol_amount,
slippage,
create_mint_ata,
use_seed,
owner_pubkey,
)
.await?;
}
"bonk" => {
handle_buy_bonk(mint, sol_amount, slippage, create_mint_ata, use_seed, owner_pubkey)
.await?;
}
_ => {}
}
Ok(())
}
async fn handle_buy_rv4(
mint: &str,
amm: &str,
sol_amount: f64,
slippage: Option<u64>,
) -> Result<(), Box<dyn std::error::Error>> {
let client = initialize_real_client().await?;
let (create_mint_ata, use_seed, owner_pubkey, _amount_f64, _decimals) =
check_mint_ata(&client, mint).await?;
handle_buy_raydium_v4(mint, amm, sol_amount, slippage, create_mint_ata, use_seed, owner_pubkey)
.await?;
Ok(())
}
async fn handle_buy_rcpmm(
mint: &str,
pool_address: &str,
sol_amount: f64,
slippage: Option<u64>,
) -> Result<(), Box<dyn std::error::Error>> {
let client = initialize_real_client().await?;
let (create_mint_ata, use_seed, owner_pubkey, _amount_f64, _decimals) =
check_mint_ata(&client, mint).await?;
handle_buy_raydium_cpmm(
mint,
pool_address,
sol_amount,
slippage,
create_mint_ata,
use_seed,
owner_pubkey,
)
.await?;
Ok(())
}
async fn handle_buy_pumpfun(
mint: &str,
sol_amount: f64,
slippage: Option<u64>,
create_mint_ata: bool,
_use_seed: bool,
_owner_pubkey: Pubkey,
) -> Result<(), Box<dyn std::error::Error>> {
println!("🔥 BUY PUMPFUN COMMAND");
println!(" Token Mint: {}", mint);
println!(" SOL Amount: {} SOL", sol_amount);
if let Some(slippage) = slippage {
println!(" Slippage: {}", slippage);
}
let client = initialize_real_client().await?;
let mint_pubkey = Pubkey::from_str(mint)?;
let param = PumpFunParams::from_mint_by_rpc(&client.infrastructure.rpc, &mint_pubkey).await?;
let recent_blockhash = client.infrastructure.rpc.get_latest_blockhash().await?;
let sol_lamports = sol_str_to_lamports(sol_amount.to_string().as_str()).unwrap();
let gas_fee_strategy = sol_trade_sdk::common::GasFeeStrategy::new();
gas_fee_strategy.set_global_fee_strategy(150000, 150000, 500000, 500000, 0.001, 0.001);
let buy_params = TradeBuyParams {
dex_type: DexType::PumpFun,
input_token_type: TradeTokenType::SOL,
mint: mint_pubkey,
input_token_amount: sol_lamports,
slippage_basis_points: slippage,
recent_blockhash: Some(recent_blockhash),
extension_params: DexParamEnum::PumpFun(param),
address_lookup_table_accounts: Vec::new(),
wait_tx_confirmed: true,
wait_for_all_submits: false,
create_input_token_ata: false,
close_input_token_ata: false,
create_mint_ata,
durable_nonce: None,
fixed_output_token_amount: None,
gas_fee_strategy,
simulate: false,
use_exact_sol_amount: None,
grpc_recv_us: None,
};
match client.buy(buy_params).await {
Ok((_, signature, _, _)) => {
println!(" ✅ Successfully bought tokens from PumpFun!");
println!(" ✅ Transaction Signature: {:?}", signature);
}
Err(e) => {
println!(" ❌ Failed to buy tokens from PumpFun: {}", e);
}
}
Ok(())
}
async fn handle_buy_pumpswap(
mint: &str,
sol_amount: f64,
slippage: Option<u64>,
create_mint_ata: bool,
_use_seed: bool,
_owner_pubkey: Pubkey,
) -> Result<(), Box<dyn std::error::Error>> {
let client = initialize_real_client().await?;
println!("🔥 BUY PUMPSWAP COMMAND");
println!(" Token Mint: {}", mint);
println!(" SOL Amount: {} SOL", sol_amount);
if let Some(slippage) = slippage {
println!(" Slippage: {}%", slippage);
}
let mint_pubkey = Pubkey::from_str(mint)?;
let param = PumpSwapParams::from_mint_by_rpc(&client.infrastructure.rpc, &mint_pubkey).await?;
let recent_blockhash = client.infrastructure.rpc.get_latest_blockhash().await?;
let sol_lamports = sol_str_to_lamports(sol_amount.to_string().as_str()).unwrap();
let gas_fee_strategy = sol_trade_sdk::common::GasFeeStrategy::new();
gas_fee_strategy.set_global_fee_strategy(150000, 150000, 500000, 500000, 0.001, 0.001);
let buy_params = TradeBuyParams {
dex_type: DexType::PumpSwap,
input_token_type: TradeTokenType::WSOL,
mint: mint_pubkey,
input_token_amount: sol_lamports,
slippage_basis_points: slippage,
recent_blockhash: Some(recent_blockhash),
extension_params: DexParamEnum::PumpSwap(param),
address_lookup_table_accounts: Vec::new(),
wait_tx_confirmed: true,
wait_for_all_submits: false,
create_input_token_ata: true,
close_input_token_ata: false,
create_mint_ata,
durable_nonce: None,
fixed_output_token_amount: None,
gas_fee_strategy,
simulate: false,
use_exact_sol_amount: None,
grpc_recv_us: None,
};
match client.buy(buy_params).await {
Ok((_, signature, _, _)) => {
println!(" ✅ Successfully bought tokens from PumpSwap!");
println!(" ✅ Transaction Signature: {:?}", signature);
}
Err(e) => {
println!(" ❌ Failed to buy tokens from PumpSwap: {}", e);
}
}
Ok(())
}
async fn handle_buy_bonk(
mint: &str,
sol_amount: f64,
slippage: Option<u64>,
create_mint_ata: bool,
_use_seed: bool,
_owner_pubkey: Pubkey,
) -> Result<(), Box<dyn std::error::Error>> {
let client = initialize_real_client().await?;
println!("🔥 BUY BONK COMMAND");
println!(" Token Mint: {}", mint);
println!(" SOL Amount: {} SOL", sol_amount);
if let Some(slippage) = slippage {
println!(" Slippage: {}%", slippage);
}
let mint_pubkey = Pubkey::from_str(mint)?;
let param =
BonkParams::from_mint_by_rpc(&client.infrastructure.rpc, &mint_pubkey, false).await?;
let recent_blockhash = client.infrastructure.rpc.get_latest_blockhash().await?;
let sol_lamports = sol_str_to_lamports(sol_amount.to_string().as_str()).unwrap();
let gas_fee_strategy = sol_trade_sdk::common::GasFeeStrategy::new();
gas_fee_strategy.set_global_fee_strategy(150000, 150000, 500000, 500000, 0.001, 0.001);
let buy_params = TradeBuyParams {
dex_type: DexType::Bonk,
input_token_type: TradeTokenType::WSOL,
mint: mint_pubkey,
input_token_amount: sol_lamports,
slippage_basis_points: slippage,
recent_blockhash: Some(recent_blockhash),
extension_params: DexParamEnum::Bonk(param),
address_lookup_table_accounts: Vec::new(),
wait_tx_confirmed: true,
wait_for_all_submits: false,
create_input_token_ata: true,
close_input_token_ata: false,
create_mint_ata,
durable_nonce: None,
fixed_output_token_amount: None,
gas_fee_strategy,
simulate: false,
use_exact_sol_amount: None,
grpc_recv_us: None,
};
match client.buy(buy_params).await {
Ok((_, signature, _, _)) => {
println!(" ✅ Successfully bought tokens from Bonk!");
println!(" ✅ Transaction Signature: {:?}", signature);
}
Err(e) => {
println!(" ❌ Failed to buy tokens from Bonk: {}", e);
}
}
Ok(())
}
async fn handle_buy_raydium_v4(
mint: &str,
amm: &str,
sol_amount: f64,
slippage: Option<u64>,
create_mint_ata: bool,
_use_seed: bool,
_owner_pubkey: Pubkey,
) -> Result<(), Box<dyn std::error::Error>> {
let client = initialize_real_client().await?;
println!("🔥 BUY RAYDIUM V4 COMMAND");
println!(" Token Mint: {}", mint);
println!(" AMM: {}", amm);
println!(" SOL Amount: {} SOL", sol_amount);
if let Some(slippage) = slippage {
println!(" Slippage: {}%", slippage);
}
let mint_pubkey = Pubkey::from_str(mint)?;
let amm_pubkey = Pubkey::from_str(amm)?;
let param =
RaydiumAmmV4Params::from_amm_address_by_rpc(&client.infrastructure.rpc, amm_pubkey).await?;
let recent_blockhash = client.infrastructure.rpc.get_latest_blockhash().await?;
let sol_lamports = sol_str_to_lamports(sol_amount.to_string().as_str()).unwrap();
let gas_fee_strategy = sol_trade_sdk::common::GasFeeStrategy::new();
gas_fee_strategy.set_global_fee_strategy(150000, 150000, 500000, 500000, 0.001, 0.001);
let buy_params = TradeBuyParams {
dex_type: DexType::RaydiumAmmV4,
input_token_type: TradeTokenType::WSOL,
mint: mint_pubkey,
input_token_amount: sol_lamports,
slippage_basis_points: slippage,
recent_blockhash: Some(recent_blockhash),
extension_params: DexParamEnum::RaydiumAmmV4(param),
address_lookup_table_accounts: Vec::new(),
wait_tx_confirmed: true,
wait_for_all_submits: false,
create_input_token_ata: true,
close_input_token_ata: false,
create_mint_ata,
durable_nonce: None,
fixed_output_token_amount: None,
gas_fee_strategy,
simulate: false,
use_exact_sol_amount: None,
grpc_recv_us: None,
};
match client.buy(buy_params).await {
Ok((_, signature, _, _)) => {
println!(" ✅ Successfully bought tokens from Raydium V4!");
println!(" ✅ Transaction Signature: {:?}", signature);
}
Err(e) => {
println!(" ❌ Failed to buy tokens from Raydium V4: {}", e);
}
}
Ok(())
}
async fn handle_buy_raydium_cpmm(
mint: &str,
pool_address: &str,
sol_amount: f64,
slippage: Option<u64>,
create_mint_ata: bool,
_use_seed: bool,
_owner_pubkey: Pubkey,
) -> Result<(), Box<dyn std::error::Error>> {
let client = initialize_real_client().await?;
println!("🔥 BUY RAYDIUM CPMM COMMAND");
println!(" Pool Address: {}", pool_address);
println!(" Token Mint: {}", mint);
println!(" SOL Amount: {} SOL", sol_amount);
if let Some(slippage) = slippage {
println!(" Slippage: {}%", slippage);
}
let mint_pubkey = Pubkey::from_str(mint)?;
let pool_pubkey = Pubkey::from_str(pool_address)?;
let param =
RaydiumCpmmParams::from_pool_address_by_rpc(&client.infrastructure.rpc, &pool_pubkey)
.await?;
let recent_blockhash = client.infrastructure.rpc.get_latest_blockhash().await?;
let sol_lamports = sol_str_to_lamports(sol_amount.to_string().as_str()).unwrap();
let gas_fee_strategy = sol_trade_sdk::common::GasFeeStrategy::new();
gas_fee_strategy.set_global_fee_strategy(150000, 150000, 500000, 500000, 0.001, 0.001);
let buy_params = TradeBuyParams {
dex_type: DexType::RaydiumCpmm,
input_token_type: TradeTokenType::WSOL,
mint: mint_pubkey,
input_token_amount: sol_lamports,
slippage_basis_points: slippage,
recent_blockhash: Some(recent_blockhash),
extension_params: DexParamEnum::RaydiumCpmm(param),
address_lookup_table_accounts: Vec::new(),
wait_tx_confirmed: true,
wait_for_all_submits: false,
create_input_token_ata: true,
close_input_token_ata: false,
create_mint_ata,
durable_nonce: None,
fixed_output_token_amount: None,
gas_fee_strategy,
simulate: false,
use_exact_sol_amount: None,
grpc_recv_us: None,
};
match client.buy(buy_params).await {
Ok((_, signature, _, _)) => {
println!(" ✅ Successfully bought tokens from Raydium CPMM!");
println!(" ✅ Transaction Signature: {:?}", signature);
}
Err(e) => {
println!(" ❌ Failed to buy tokens from Raydium CPMM: {}", e);
}
}
Ok(())
}
async fn handle_sell(
mint: &str,
dex: &str,
token_amount: Option<f64>,
slippage: Option<u64>,
) -> Result<(), Box<dyn std::error::Error>> {
// Validate DEX parameter
if !DEXS.contains(&dex) {
println!("❌ Invalid DEX: '{}'. Supported DEXs: {}", dex, DEXS.join(", "));
return Ok(());
}
let client = initialize_real_client().await?;
let (create_mint_ata, use_seed, owner_pubkey, amount_f64, decimals) =
check_mint_ata(&client, mint).await?;
match dex {
"pumpfun" => {
handle_sell_pumpfun(
mint,
token_amount,
slippage,
create_mint_ata,
use_seed,
owner_pubkey,
amount_f64,
decimals,
)
.await?;
}
"pumpswap" => {
handle_sell_pumpswap(
mint,
token_amount,
slippage,
create_mint_ata,
use_seed,
owner_pubkey,
amount_f64,
decimals,
)
.await?;
}
"bonk" => {
handle_sell_bonk(
mint,
token_amount,
slippage,
create_mint_ata,
use_seed,
owner_pubkey,
amount_f64,
decimals,
)
.await?;
}
_ => {}
}
Ok(())
}
async fn handle_sell_rv4(
mint: &str,
amm: &str,
token_amount: Option<f64>,
slippage: Option<u64>,
) -> Result<(), Box<dyn std::error::Error>> {
let client = initialize_real_client().await?;
let (create_mint_ata, use_seed, owner_pubkey, amount_f64, decimals) =
check_mint_ata(&client, mint).await?;
handle_sell_raydium_v4(
amm,
mint,
token_amount,
slippage,
create_mint_ata,
use_seed,
owner_pubkey,
amount_f64,
decimals,
)
.await?;
Ok(())
}
async fn handle_sell_rcpmm(
mint: &str,
pool_address: &str,
token_amount: Option<f64>,
slippage: Option<u64>,
) -> Result<(), Box<dyn std::error::Error>> {
let client = initialize_real_client().await?;
let (create_mint_ata, use_seed, owner_pubkey, amount_f64, decimals) =
check_mint_ata(&client, mint).await?;
handle_sell_raydium_cpmm(
mint,
pool_address,
token_amount,
slippage,
create_mint_ata,
use_seed,
owner_pubkey,
amount_f64,
decimals,
)
.await?;
Ok(())
}
async fn handle_sell_pumpfun(
mint: &str,
token_amount: Option<f64>,
slippage: Option<u64>,
_create_mint_ata: bool,
_use_seed: bool,
_owner_pubkey: Pubkey,
amount_f64: f64,
_decimals: u8,
) -> Result<(), Box<dyn std::error::Error>> {
let amount = token_amount.unwrap_or(amount_f64);
println!("🔥 SELL PUMPFUN COMMAND");
println!(" Token Mint: {}", mint);
println!(" Token Amount: {} ", amount);
if let Some(slippage) = slippage {
println!(" Slippage: {}%", slippage);
}
let client = initialize_real_client().await?;
let mint_pubkey = Pubkey::from_str(mint)?;
let param = PumpFunParams::from_mint_by_rpc(&client.infrastructure.rpc, &mint_pubkey).await?;
let recent_blockhash = client.infrastructure.rpc.get_latest_blockhash().await?;
let gas_fee_strategy = sol_trade_sdk::common::GasFeeStrategy::new();
gas_fee_strategy.set_global_fee_strategy(150000, 150000, 500000, 500000, 0.001, 0.001);
let sell_params = TradeSellParams {
dex_type: DexType::PumpFun,
output_token_type: TradeTokenType::SOL,
mint: mint_pubkey,
input_token_amount: amount as u64,
slippage_basis_points: slippage,
recent_blockhash: Some(recent_blockhash),
with_tip: false,
extension_params: DexParamEnum::PumpFun(param),
address_lookup_table_accounts: Vec::new(),
wait_tx_confirmed: true,
wait_for_all_submits: false,
create_output_token_ata: true,
close_output_token_ata: false,
close_mint_token_ata: false,
durable_nonce: None,
fixed_output_token_amount: None,
gas_fee_strategy,
simulate: false,
grpc_recv_us: None,
};
match client.sell(sell_params).await {
Ok((_, signature, _, _)) => {
println!(" ✅ Successfully sold tokens from PumpFun!");
println!(" ✅ Transaction Signature: {:?}", signature);
}
Err(e) => {
println!(" ❌ Failed to sell tokens from PumpFun: {}", e);
}
}
Ok(())
}
async fn handle_sell_pumpswap(
mint: &str,
token_amount: Option<f64>,
slippage: Option<u64>,
_create_mint_ata: bool,
_use_seed: bool,
_owner_pubkey: Pubkey,
amount_f64: f64,
_decimals: u8,
) -> Result<(), Box<dyn std::error::Error>> {
let amount = token_amount.unwrap_or(amount_f64);
println!("🔥 SELL PUMPSWAP COMMAND");
println!(" Token Mint: {}", mint);
println!(" Token Amount: {}", amount);
if let Some(slippage) = slippage {
println!(" Slippage: {}", slippage);
}
let client = initialize_real_client().await?;
let mint_pubkey = Pubkey::from_str(mint)?;
let param = PumpSwapParams::from_mint_by_rpc(&client.infrastructure.rpc, &mint_pubkey).await?;
let recent_blockhash = client.infrastructure.rpc.get_latest_blockhash().await?;
let gas_fee_strategy = sol_trade_sdk::common::GasFeeStrategy::new();
gas_fee_strategy.set_global_fee_strategy(150000, 150000, 500000, 500000, 0.001, 0.001);
let sell_params = TradeSellParams {
dex_type: DexType::PumpSwap,
output_token_type: TradeTokenType::WSOL,
mint: mint_pubkey,
input_token_amount: amount as u64,
slippage_basis_points: slippage,
recent_blockhash: Some(recent_blockhash),
with_tip: false,
extension_params: DexParamEnum::PumpSwap(param),
address_lookup_table_accounts: Vec::new(),
wait_tx_confirmed: true,
wait_for_all_submits: false,
create_output_token_ata: true,
close_output_token_ata: false,
close_mint_token_ata: false,
durable_nonce: None,
fixed_output_token_amount: None,
gas_fee_strategy,
simulate: false,
grpc_recv_us: None,
};
match client.sell(sell_params).await {
Ok((_, signature, _, _)) => {
println!(" ✅ Successfully sold tokens from PumpSwap!");
println!(" ✅ Transaction Signature: {:?}", signature);
}
Err(e) => {
println!(" ❌ Failed to sell tokens from PumpSwap: {}", e);
}
}
Ok(())
}
async fn handle_sell_bonk(
mint: &str,
token_amount: Option<f64>,
slippage: Option<u64>,
_create_mint_ata: bool,
_use_seed: bool,
_owner_pubkey: Pubkey,
amount_f64: f64,
_decimals: u8,
) -> Result<(), Box<dyn std::error::Error>> {
let amount = token_amount.unwrap_or(amount_f64);
println!("🔥 SELL PUMPSWAP COMMAND");
println!(" Token Mint: {}", mint);
println!(" Token Amount: {}", amount);
if let Some(slippage) = slippage {
println!(" Slippage: {}", slippage);
}
let client = initialize_real_client().await?;
let mint_pubkey = Pubkey::from_str(mint)?;
let param =
BonkParams::from_mint_by_rpc(&client.infrastructure.rpc, &mint_pubkey, false).await?;
let recent_blockhash = client.infrastructure.rpc.get_latest_blockhash().await?;
let gas_fee_strategy = sol_trade_sdk::common::GasFeeStrategy::new();
gas_fee_strategy.set_global_fee_strategy(150000, 150000, 500000, 500000, 0.001, 0.001);
let sell_params = TradeSellParams {
dex_type: DexType::Bonk,
output_token_type: TradeTokenType::WSOL,
mint: mint_pubkey,
input_token_amount: amount as u64,
slippage_basis_points: slippage,
recent_blockhash: Some(recent_blockhash),
with_tip: false,
extension_params: DexParamEnum::Bonk(param),
address_lookup_table_accounts: Vec::new(),
wait_tx_confirmed: true,
wait_for_all_submits: false,
create_output_token_ata: true,
close_output_token_ata: false,
close_mint_token_ata: false,
durable_nonce: None,
fixed_output_token_amount: None,
gas_fee_strategy,
simulate: false,
grpc_recv_us: None,
};
match client.sell(sell_params).await {
Ok((_, signature, _, _)) => {
println!(" ✅ Successfully sold tokens from Bonk!");
println!(" ✅ Transaction Signature: {:?}", signature);
}
Err(e) => {
println!(" ❌ Failed to sell tokens from Bonk: {}", e);
}
}
Ok(())
}
async fn handle_sell_raydium_v4(
amm: &str,
mint: &str,
token_amount: Option<f64>,
slippage: Option<u64>,
_create_mint_ata: bool,
_use_seed: bool,
_owner_pubkey: Pubkey,
amount_f64: f64,
_decimals: u8,
) -> Result<(), Box<dyn std::error::Error>> {
let amount = token_amount.unwrap_or(amount_f64);
println!("🔥 SELL RAYDIUM V4 COMMAND");
println!(" AMM: {}", amm);
println!(" Token Mint: {}", mint);
println!(" Token Amount: {}", amount);
if let Some(slippage) = slippage {
println!(" Slippage: {}", slippage);
}
let client = initialize_real_client().await?;
let amm_pubkey = Pubkey::from_str(amm)?;
let mint_pubkey = Pubkey::from_str(mint)?;
let param =
RaydiumAmmV4Params::from_amm_address_by_rpc(&client.infrastructure.rpc, amm_pubkey).await?;
let recent_blockhash = client.infrastructure.rpc.get_latest_blockhash().await?;
let gas_fee_strategy = sol_trade_sdk::common::GasFeeStrategy::new();
gas_fee_strategy.set_global_fee_strategy(150000, 150000, 500000, 500000, 0.001, 0.001);
let sell_params = TradeSellParams {
dex_type: DexType::RaydiumAmmV4,
output_token_type: TradeTokenType::WSOL,
mint: mint_pubkey,
input_token_amount: amount as u64,
slippage_basis_points: slippage,
recent_blockhash: Some(recent_blockhash),
with_tip: false,
extension_params: DexParamEnum::RaydiumAmmV4(param),
address_lookup_table_accounts: Vec::new(),
wait_tx_confirmed: true,
wait_for_all_submits: false,
create_output_token_ata: true,
close_output_token_ata: false,
close_mint_token_ata: false,
durable_nonce: None,
fixed_output_token_amount: None,
gas_fee_strategy,
simulate: false,
grpc_recv_us: None,
};
match client.sell(sell_params).await {
Ok((_, signature, _, _)) => {
println!(" ✅ Successfully sold tokens from Raydium V4!");
println!(" ✅ Transaction Signature: {:?}", signature);
}
Err(e) => {
println!(" ❌ Failed to sell tokens from Raydium V4: {}", e);
}
}
Ok(())
}
async fn handle_sell_raydium_cpmm(
mint: &str,
pool_address: &str,
token_amount: Option<f64>,
slippage: Option<u64>,
_create_mint_ata: bool,
_use_seed: bool,
_owner_pubkey: Pubkey,
amount_f64: f64,
_decimals: u8,
) -> Result<(), Box<dyn std::error::Error>> {
let amount = token_amount.unwrap_or(amount_f64);
println!("🔥 SELL RAYDIUM CPMM COMMAND");
println!(" Pool Address: {}", pool_address);
println!(" Token Mint: {}", mint);
println!(" Token Amount: {}", amount);
if let Some(slippage) = slippage {
println!(" Slippage: {}", slippage);
}
let client = initialize_real_client().await?;
let pool_pubkey = Pubkey::from_str(pool_address)?;
let mint_pubkey = Pubkey::from_str(mint)?;
let param =
RaydiumCpmmParams::from_pool_address_by_rpc(&client.infrastructure.rpc, &pool_pubkey)
.await?;
let recent_blockhash = client.infrastructure.rpc.get_latest_blockhash().await?;
let gas_fee_strategy = sol_trade_sdk::common::GasFeeStrategy::new();
gas_fee_strategy.set_global_fee_strategy(150000, 150000, 500000, 500000, 0.001, 0.001);
let sell_params = TradeSellParams {
dex_type: DexType::RaydiumCpmm,
output_token_type: TradeTokenType::WSOL,
mint: mint_pubkey,
input_token_amount: amount as u64,
slippage_basis_points: slippage,
recent_blockhash: Some(recent_blockhash),
with_tip: false,
extension_params: DexParamEnum::RaydiumCpmm(param),
address_lookup_table_accounts: Vec::new(),
wait_tx_confirmed: true,
wait_for_all_submits: false,
create_output_token_ata: true,
close_output_token_ata: false,
close_mint_token_ata: false,
durable_nonce: None,
fixed_output_token_amount: None,
gas_fee_strategy,
simulate: false,
grpc_recv_us: None,
};
match client.sell(sell_params).await {
Ok((_, signature, _, _)) => {
println!(" ✅ Successfully sold tokens from Raydium CPMM!");
println!(" ✅ Transaction Signature: {:?}", signature);
}
Err(e) => {
println!(" ❌ Failed to sell tokens from Raydium CPMM: {}", e);
}
}
Ok(())
}
async fn handle_wrap_sol(amount: f64) -> Result<(), Box<dyn std::error::Error>> {
println!("📦 WRAP SOL COMMAND");
println!(" Amount: {} SOL → {} WSOL", amount, amount);
match initialize_real_client().await {
Ok(client) => {
let amount_lamports = (amount * 1_000_000_000.0) as u64;
match wrap_sol_real(&client, amount_lamports).await {
Ok(_) => {
println!(" ✅ Successfully wrapped {} SOL to WSOL!", amount);
}
Err(e) => {
println!(" ❌ Failed to wrap SOL: {}", e);
}
}
}
Err(_) => {
println!(" ⚠️ Cannot connect to Solana network");
}
}
Ok(())
}
async fn handle_close_wsol() -> Result<(), Box<dyn std::error::Error>> {
println!("🔄 CLOSE WSOL COMMAND");
println!(" Action: Closing WSOL account to recover SOL");
match initialize_real_client().await {
Ok(client) => {
// Check WSOL balance first
let wsol_mint = WSOL_TOKEN_ACCOUNT;
match client.get_payer_token_balance(&wsol_mint).await {
Ok(wsol_balance) => {
if wsol_balance == 0 {
println!(" ⚠️ No WSOL balance found to recover");
return Ok(());
}
println!(
" 💰 Found {:.6} WSOL to recover",
wsol_balance as f64 / 1_000_000_000.0
);
match close_wsol_real(&client).await {
Ok(_) => {
println!(
" ✅ Successfully closed WSOL account and recovered {:.6} SOL",
wsol_balance as f64 / 1_000_000_000.0
);
}
Err(e) => {
println!(" ❌ Failed to close WSOL account: {}", e);
}
}
}
Err(_) => {
println!(" ⚠️ No WSOL account found");
}
}
}
Err(_) => {
println!(" ⚠️ Cannot connect to Solana network");
}
}
Ok(())
}
async fn handle_wallet() -> Result<(), Box<dyn std::error::Error>> {
println!("👛 WALLET STATUS");
match initialize_real_client().await {
Ok(client) => match get_wallet_real(&client).await {
Ok(_) => {}
Err(e) => {
println!(" ❌ Failed to get wallet status: {}", e);
}
},
Err(_) => {
println!(" ⚠️ Cannot connect to Solana network");
}
}
Ok(())
}
// Real implementation functions
async fn initialize_real_client() -> AnyResult<SolanaTrade> {
println!("🚀 Initializing SolanaTrade client...");
let payer = Arc::new(sol_trade_sdk::common::keypair::load_keypair_from_env("PRIVATE_KEY")?);
let rpc_url = rpc_url();
let commitment = CommitmentConfig::confirmed();
let swqos_configs: Vec<SwqosConfig> = vec![SwqosConfig::Default(rpc_url.clone())];
let trade_config = TradeConfig::builder(rpc_url, swqos_configs, commitment)
// .create_wsol_ata_on_startup(true) // default: true
// .use_seed_optimize(true) // default: true
// .log_enabled(true) // default: true
// .check_min_tip(false) // default: false
// .swqos_cores_from_end(false) // default: false
// .mev_protection(false) // default: false
.build();
let solana_trade = SolanaTrade::new(payer, trade_config).await;
println!("✅ SolanaTrade client initialized successfully!");
Ok(solana_trade)
}
async fn wrap_sol_real(
client: &SolanaTrade,
amount_lamports: u64,
) -> Result<(), Box<dyn std::error::Error>> {
use solana_sdk::transaction::Transaction;
// Check balance first
let balance = client.get_payer_sol_balance().await?;
if balance < amount_lamports + 1_000_000 {
// Need extra for transaction fees
return Err(format!(
"Insufficient balance. Have {:.3} SOL, need {:.3} SOL (including fees)",
balance as f64 / 1_000_000_000.0,
(amount_lamports + 1_000_000) as f64 / 1_000_000_000.0
)
.into());
}
// Create WSOL account and wrap SOL
let wsol_mint = "So11111111111111111111111111111111111111112".parse().unwrap();
let instructions = vec![
// Create associated token account for WSOL
create_associated_token_account_idempotent(
&client.get_payer_pubkey(),
&client.get_payer_pubkey(),
&wsol_mint,
&spl_token::ID,
),
// Transfer SOL to WSOL account
transfer(
&client.get_payer_pubkey(),
&get_associated_token_address(&client.get_payer_pubkey(), &wsol_mint),
amount_lamports,
),
// Sync native (convert SOL to WSOL tokens)
Instruction {
program_id: sol_trade_sdk::constants::TOKEN_PROGRAM,
accounts: vec![AccountMeta::new(
get_associated_token_address(&client.get_payer_pubkey(), &wsol_mint),
false,
)],
data: vec![17],
},
];
// Build and send transaction
let recent_blockhash = client.infrastructure.rpc.get_latest_blockhash().await?;
let transaction = Transaction::new_signed_with_payer(
&instructions,
Some(&client.get_payer_pubkey()),
&[client.get_payer()],
recent_blockhash,
);
let signature = client.infrastructure.rpc.send_and_confirm_transaction(&transaction).await?;
println!(" 📝 Transaction Signature: {}", signature);
Ok(())
}
async fn close_wsol_real(client: &SolanaTrade) -> Result<(), Box<dyn std::error::Error>> {
use solana_sdk::transaction::Transaction;
let wsol_mint = "So11111111111111111111111111111111111111112".parse().unwrap();
let wsol_account = get_associated_token_address(&client.get_payer_pubkey(), &wsol_mint);
// Check if WSOL account exists
let account_info = client.infrastructure.rpc.get_account(&wsol_account).await;
if account_info.is_err() {
return Err("WSOL account not found".into());
}
// Close WSOL account instruction
let close_instruction = close_account(
&spl_token::ID,
&wsol_account,
&client.get_payer_pubkey(),
&client.get_payer_pubkey(),
&[],
)?;
// Build and send transaction
let recent_blockhash = client.infrastructure.rpc.get_latest_blockhash().await?;
let transaction = Transaction::new_signed_with_payer(
&[close_instruction],
Some(&client.get_payer_pubkey()),
&[client.get_payer()],
recent_blockhash,
);
let signature = client.infrastructure.rpc.send_and_confirm_transaction(&transaction).await?;
println!(" 📝 Transaction Signature: {}", signature);
Ok(())
}
async fn get_wallet_real(client: &SolanaTrade) -> Result<(), Box<dyn std::error::Error>> {
// Get SOL balance
let sol_balance = client.get_payer_sol_balance().await?;
// Get WSOL balance
let wsol_mint = "So11111111111111111111111111111111111111112".parse().unwrap();
let wsol_balance = client.get_payer_token_balance(&wsol_mint).await.unwrap_or(0);
// Get wallet address
let wallet_address = client.get_payer_pubkey();
println!("\n 📊 REAL WALLET OVERVIEW:");
println!(" ═══════════════════════════════════");
println!(" 💰 SOL Balance: {:.6} SOL", sol_balance as f64 / 1_000_000_000.0);
println!(" 🪙 WSOL Balance: {:.6} WSOL", wsol_balance as f64 / 1_000_000_000.0);
println!(" 🏛️ Wallet Address: {}", wallet_address);
println!(" ⚡ Network: Mainnet-beta");
Ok(())
}