diff --git a/Cargo.toml b/Cargo.toml index f2e7be8..194add2 100755 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "sol-trade-sdk" -version = "0.6.13" +version = "0.6.14" edition = "2021" authors = [ "William ", @@ -31,6 +31,7 @@ members = [ "examples/pumpswap_direct_trading", "examples/wsol_wrapper", "examples/seed_trading", + "examples/cli_trading", ] [lib] diff --git a/README.md b/README.md index af2bc6c..16e68c9 100644 --- a/README.md +++ b/README.md @@ -90,14 +90,14 @@ Add the dependency to your `Cargo.toml`: ```toml # Add to your Cargo.toml -sol-trade-sdk = { path = "./sol-trade-sdk", version = "0.6.13" } +sol-trade-sdk = { path = "./sol-trade-sdk", version = "0.6.14" } ``` ### Use crates.io ```toml # Add to your Cargo.toml -sol-trade-sdk = "0.6.13" +sol-trade-sdk = "0.6.14" ``` ## 🛠️ Usage Examples diff --git a/README_CN.md b/README_CN.md index 845a259..c61c0b9 100755 --- a/README_CN.md +++ b/README_CN.md @@ -89,14 +89,14 @@ git clone https://github.com/0xfnzero/sol-trade-sdk ```toml # 添加到您的 Cargo.toml -sol-trade-sdk = { path = "./sol-trade-sdk", version = "0.6.13" } +sol-trade-sdk = { path = "./sol-trade-sdk", version = "0.6.14" } ``` ### 使用 crates.io ```toml # 添加到您的 Cargo.toml -sol-trade-sdk = "0.6.13" +sol-trade-sdk = "0.6.14" ``` ## 🛠️ 使用示例 diff --git a/examples/cli_trading/Cargo.toml b/examples/cli_trading/Cargo.toml new file mode 100644 index 0000000..073101c --- /dev/null +++ b/examples/cli_trading/Cargo.toml @@ -0,0 +1,12 @@ +[package] +name = "cli_trading" +version = "0.1.0" +edition = "2021" + +[dependencies] +sol-trade-sdk = { path = "../.." } +solana-sdk = "2.3.0" +tokio = { version = "1", features = ["full"] } +clap = { version = "4.0", features = ["derive"] } +spl-token = "8.0.0" +spl-associated-token-account = "7.0.0" \ No newline at end of file diff --git a/examples/cli_trading/README.md b/examples/cli_trading/README.md new file mode 100644 index 0000000..952afe2 --- /dev/null +++ b/examples/cli_trading/README.md @@ -0,0 +1,180 @@ +# SOL Trade CLI + +A command-line tool for trading tokens on Solana, supporting multiple DEX trading and wallet management features. + +## Features + +This CLI tool supports the following operation modes: + +### 🚀 **Trading Features** + +- **Buy Tokens** - Purchase tokens with SOL, supporting multiple DEXs +- **Sell Tokens** - Sell tokens for SOL, supporting specified amounts or sell all +- **Multi-DEX Support** - pumpfun, pumpswap, bonk, raydium_v4, raydium_cpmm + +### 💼 **Wallet Management** + +- **Wallet Status** - View SOL and WSOL balances +- **SOL Wrapping** - Wrap SOL to WSOL +- **WSOL Closing** - Close WSOL account and retrieve SOL + +### 🎛️ **Usage Modes** + +- **Interactive Mode** - Continuous command-line interface +- **Direct Command Mode** - Single command execution + +## Build + +```bash +cd examples/cli_trading +cargo build --release +``` + +## Usage + +### 1. Interactive Mode (Recommended) + +Run the program directly to enter interactive mode: + +```bash +cargo run +# or explicitly specify +cargo run -- interactive +``` + +Then enter commands in the interactive command line: + +``` +sol-trade> help # View help +sol-trade> wallet # Check wallet status +sol-trade> buy xxxxxxxxxxxxxx pumpfun 1.0 # Buy with 1.0 SOL +sol-trade> buy xxxxxxxxxxxxxx pumpfun 1.0 500 # Buy with 500 slippage +sol-trade> sell xxxxxxxxxxxxxx pumpfun # Sell all tokens +sol-trade> sell xxxxxxxxxxxxxx pumpfun 100.0 # Sell 100 tokens +sol-trade> raydium_v4_buy 1.0 # Raydium V4 buy +sol-trade> raydium_cpmm_buy 1.0 # Raydium CPMM buy +sol-trade> wrap_sol 2.5 # Wrap 2.5 SOL +sol-trade> close_wsol # Close WSOL account +sol-trade> quit # Exit +``` + +### 2. Direct Command Line Mode ⭐️ **New Feature** + +Now supports executing single commands directly via command line arguments without entering interactive mode: + +#### 📋 View Help + +```bash +cargo run -- --help # General help +cargo run -- buy --help # Buy command help +cargo run -- sell --help # Sell command help +``` + +#### 💰 Buy Tokens + +```bash +# Basic buy +cargo run -- buy pumpfun --amount 1.0 + +# Buy with slippage +cargo run -- buy pumpfun --amount 1.0 --slippage 500 + +# Raydium V4 buy +cargo run -- buy raydium_v4 --amount 1.0 --amm + +# Raydium CPMM buy +cargo run -- buy raydium_cpmm --amount 1.0 --pool +``` + +#### 💸 Sell Tokens + +```bash +# Sell all tokens +cargo run -- sell pumpfun + +# Sell specified amount +cargo run -- sell pumpfun --amount 100.0 + +# Sell with slippage +cargo run -- sell pumpfun --amount 100.0 --slippage 500 + +# Raydium V4 sell +cargo run -- sell raydium_v4 --amm + +# Raydium CPMM sell +cargo run -- sell raydium_cpmm --pool +``` + +#### 🔄 Wallet Operations + +```bash +# Wrap SOL to WSOL +cargo run -- wrap-sol --amount 1.0 + +# Close WSOL account +cargo run -- close-wsol + +# Check wallet status +cargo run -- wallet +``` + +## Supported DEXs + +| DEX | Status | Buy | Sell | Special Parameters | +| ---------------- | --------------- | --- | ---- | ------------------- | +| **PumpFun** | ✅ Full Support | ✅ | ✅ | None | +| **PumpSwap** | ✅ Full Support | ✅ | ✅ | None | +| **Bonk** | ✅ Full Support | ✅ | ✅ | None | +| **Raydium V4** | ✅ Full Support | ✅ | ✅ | Requires `--amm` | +| **Raydium CPMM** | ✅ Full Support | ✅ | ✅ | Requires `--pool` | + +## Feature Status + +✅ **Fully Implemented Features:** + +- **Multi-DEX Trading** - Supports PumpFun, PumpSwap, Bonk, Raydium V4, Raydium CPMM +- **Buy/Sell** - Complete token trading functionality (real blockchain transactions) +- **Wallet Management** - SOL wrapping, WSOL closing, balance queries +- **Dual Mode Operation** - Interactive mode and direct command line mode +- **Parameter Validation** - Smart checking of required parameters (e.g., Raydium AMM/Pool addresses) +- **Complete Help System** - Detailed help documentation for every command + +## Quick Start Examples + +```bash +# 1. View help +cargo run -- --help + +# 2. Check wallet status +cargo run -- wallet + +# 3. Direct token purchase (PumpFun) +cargo run -- buy xxxxxxxxxxxxxx pumpfun --amount 0.1 + +# 4. Buy using Raydium V4 +cargo run -- buy raydium_v4 --amount 0.1 --amm + +# 5. Sell tokens +cargo run -- sell xxxxxxxxxxxxxx pumpfun + +# 6. Start interactive mode +cargo run +``` + +## Feature Highlights + +- 🎯 **Smart Parameter Validation** - Automatically checks required special parameters for each DEX +- 🔄 **Dual Mode Operation** - Supports both interactive and command-line usage +- 📊 **Real-time Status Display** - Wallet balances and transaction status updated in real-time +- 🛡️ **Secure Trading** - All transactions have complete error handling and confirmation +- 📝 **Detailed Logging** - Each transaction displays signature for tracking + +## Security Reminders + +⚠️ **Important Security Reminders:** + +- This tool performs **real blockchain transactions** +- All transactions consume real SOL as transaction fees +- Ensure wallet private keys are secure and never share them +- Transactions are irreversible, please operate carefully +- Recommend testing with small amounts first diff --git a/examples/cli_trading/README_CN.md b/examples/cli_trading/README_CN.md new file mode 100644 index 0000000..61365d6 --- /dev/null +++ b/examples/cli_trading/README_CN.md @@ -0,0 +1,180 @@ +# SOL Trade CLI + +一个用于在 Solana 上交易代币的命令行工具,支持多种 DEX 交易和钱包管理功能。 + +## 功能 + +此 CLI 工具支持以下操作模式: + +### 🚀 **交易功能** + +- **买入代币** - 使用 SOL 购买代币,支持多个 DEX +- **卖出代币** - 卖出代币换取 SOL,支持指定数量或全部卖出 +- **多 DEX 支持** - pumpfun, pumpswap, bonk, raydium_v4, raydium_cpmm + +### 💼 **钱包管理** + +- **钱包状态** - 查看 SOL 和 WSOL 余额 +- **SOL 包装** - 将 SOL 包装为 WSOL +- **WSOL 关闭** - 关闭 WSOL 账户并取回 SOL + +### 🎛️ **使用模式** + +- **交互式模式** - 持续的命令行界面 +- **直接命令模式** - 单次命令执行 + +## 构建 + +```bash +cd examples/cli_trading +cargo build --release +``` + +## 使用方式 + +### 1. 交互式模式 (推荐) + +直接运行程序进入交互模式: + +```bash +cargo run +# 或者显式指定 +cargo run -- interactive +``` + +然后在交互式命令行中输入命令: + +``` +sol-trade> help # 查看帮助 +sol-trade> wallet # 查看钱包状态 +sol-trade> buy xxxxxxxxxxxxxx pumpfun 1.0 # 用1.0 SOL买入 +sol-trade> buy xxxxxxxxxxxxxx pumpfun 1.0 500 # 买入并设置500滑点 +sol-trade> sell xxxxxxxxxxxxxx pumpfun # 卖出所有代币 +sol-trade> sell xxxxxxxxxxxxxx pumpfun 100.0 # 卖出100个代币 +sol-trade> raydium_v4_buy 1.0 # Raydium V4 买入 +sol-trade> raydium_cpmm_buy 1.0 # Raydium CPMM 买入 +sol-trade> wrap_sol 2.5 # 包装2.5 SOL +sol-trade> close_wsol # 关闭WSOL账户 +sol-trade> quit # 退出 +``` + +### 2. 直接命令行模式 ⭐️ **新功能** + +现在支持直接通过命令行参数执行单个命令,无需进入交互模式: + +#### 📋 查看帮助 + +```bash +cargo run -- --help # 总体帮助 +cargo run -- buy --help # 买入命令帮助 +cargo run -- sell --help # 卖出命令帮助 +``` + +#### 💰 买入代币 + +```bash +# 基础买入 +cargo run -- buy pumpfun --amount 1.0 + +# 买入并设置滑点 +cargo run -- buy pumpfun --amount 1.0 --slippage 500 + +# Raydium V4 买入 +cargo run -- buy raydium_v4 --amount 1.0 --amm + +# Raydium CPMM 买入 +cargo run -- buy raydium_cpmm --amount 1.0 --pool +``` + +#### 💸 卖出代币 + +```bash +# 卖出所有代币 +cargo run -- sell pumpfun + +# 卖出指定数量 +cargo run -- sell pumpfun --amount 100.0 + +# 卖出并设置滑点 +cargo run -- sell pumpfun --amount 100.0 --slippage 500 + +# Raydium V4 卖出 +cargo run -- sell raydium_v4 --amm + +# Raydium CPMM 卖出 +cargo run -- sell raydium_cpmm --pool +``` + +#### 🔄 钱包操作 + +```bash +# 包装SOL为WSOL +cargo run -- wrap-sol --amount 1.0 + +# 关闭WSOL账户 +cargo run -- close-wsol + +# 查看钱包状态 +cargo run -- wallet +``` + +## 支持的 DEX + +| DEX | 状态 | 买入 | 卖出 | 特殊参数 | +| ---------------- | ----------- | ---- | ---- | ------------------ | +| **PumpFun** | ✅ 完全支持 | ✅ | ✅ | 无 | +| **PumpSwap** | ✅ 完全支持 | ✅ | ✅ | 无 | +| **Bonk** | ✅ 完全支持 | ✅ | ✅ | 无 | +| **Raydium V4** | ✅ 完全支持 | ✅ | ✅ | 需要 `--amm` 参数 | +| **Raydium CPMM** | ✅ 完全支持 | ✅ | ✅ | 需要 `--pool` 参数 | + +## 功能状态 + +✅ **已完全实现的功能:** + +- **多 DEX 交易** - 支持 PumpFun, PumpSwap, Bonk, Raydium V4, Raydium CPMM +- **买入/卖出** - 完整的代币交易功能(真实区块链交易) +- **钱包管理** - SOL 包装、WSOL 关闭、余额查询 +- **双模式操作** - 交互式模式和直接命令行模式 +- **参数验证** - 智能检查必需参数(如 Raydium 的 AMM/Pool 地址) +- **完整帮助系统** - 每个命令都有详细的帮助文档 + +## 快速开始示例 + +```bash +# 1. 查看帮助 +cargo run -- --help + +# 2. 检查钱包状态 +cargo run -- wallet + +# 3. 直接买入代币(PumpFun) +cargo run -- buy xxxxxxxxxxxxxx pumpfun --amount 0.1 + +# 4. 使用Raydium V4买入 +cargo run -- buy raydium_v4 --amount 0.1 --amm + +# 5. 卖出代币 +cargo run -- sell xxxxxxxxxxxxxx pumpfun + +# 6. 启动交互模式 +cargo run +``` + +## 特性亮点 + +- 🎯 **智能参数验证** - 自动检查各 DEX 所需的特殊参数 +- 🔄 **双模式操作** - 支持交互式和命令行两种使用方式 +- 📊 **实时状态显示** - 钱包余额、交易状态实时更新 +- 🛡️ **安全交易** - 所有交易都有完整的错误处理和确认 +- 📝 **详细日志** - 每笔交易都会显示签名用于追踪 + +## 安全提醒 + +⚠️ **重要安全提醒:** + +- 本工具进行**真实的区块链交易** +- 所有交易会消耗真实的 SOL 作为交易费用 +- 确保钱包私钥安全,不要泄露给他人 +- 交易是不可逆的,请谨慎操作 +- 建议先用小额进行测试 diff --git a/examples/cli_trading/src/main.rs b/examples/cli_trading/src/main.rs new file mode 100644 index 0000000..6306d32 --- /dev/null +++ b/examples/cli_trading/src/main.rs @@ -0,0 +1,1446 @@ +use clap::Parser; +use sol_trade_sdk::{ + common::{ + fast_fn::get_associated_token_address_with_program_id_fast_use_seed, AnyResult, + PriorityFee, TradeConfig, + }, + swqos::SwqosConfig, + trading::{ + core::params::{ + BonkParams, PumpFunParams, PumpSwapParams, RaydiumAmmV4Params, RaydiumCpmmParams, + }, + factory::DexType, + }, + SolanaTrade, +}; +use solana_sdk::{ + commitment_config::CommitmentConfig, native_token::sol_str_to_lamports, pubkey::Pubkey, + signature::Keypair, signer::Signer, +}; +use std::sync::{Arc, LazyLock}; +use std::{ + io::{self, Write}, + str::FromStr, +}; +// 设置 payer +static PAYER: LazyLock = LazyLock::new(|| Keypair::new()); +// 设置 rpc url +static RPC_URL: &str = "https://api.mainnet-beta.solana.com"; + +static DEXS: &[&str] = &["pumpfun", "pumpswap", "bonk", "raydium_v4", "raydium_cpmm"]; + +#[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, +} + +#[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, + /// AMM address for Raydium V4 (required for raydium_v4) + #[arg(long)] + amm: Option, + /// Pool address for Raydium CPMM (required for raydium_cpmm) + #[arg(long)] + pool: Option, + }, + /// 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, + /// Slippage tolerance (optional) + #[arg(short, long)] + slippage: Option, + /// AMM address for Raydium V4 (required for raydium_v4) + #[arg(long)] + amm: Option, + /// Pool address for Raydium CPMM (required for raydium_cpmm) + #[arg(long)] + pool: Option, + }, + /// 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> { + 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> { + 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, + pool: &Option, +) -> Result<(), Box> { + 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(_) => { + // Generate a temporary keypair to show the format + let temp_keypair = solana_sdk::signature::Keypair::new(); + println!( + "👛 Wallet Address: {} (temporary - set SOLANA_RPC_URL for real wallet)", + temp_keypair.pubkey() + ); + println!("💰 SOL Balance: Unable to fetch (no valid RPC connection)"); + } + } + + println!("⚡ Network: Mainnet-beta"); + println!("══════════════════════════════════════"); + + // Show help + show_help(); + println!("══════════════════════════════════════\n"); +} + +async fn run_interactive_mode() -> Result<(), Box> { + 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 input.starts_with("raydium_cpmm_buy ") { + handle_interactive_raydium_cpmm_buy(&input[16..]).await?; + } else if input.starts_with("raydium_cpmm_sell ") { + handle_interactive_raydium_cpmm_sell(&input[17..]).await?; + } else if input.starts_with("raydium_v4_buy ") { + handle_interactive_raydium_v4_buy(&input[14..]).await?; + } else if input.starts_with("raydium_v4_sell ") { + handle_interactive_raydium_v4_sell(&input[15..]).await?; + } else if input.starts_with("buy ") { + handle_interactive_buy(&input[4..]).await?; + } else if input.starts_with("sell ") { + handle_interactive_sell(&input[5..]).await?; + } else if input.starts_with("wrap_sol ") || input.starts_with("wrap-sol ") { + let amount_str = + if input.starts_with("wrap_sol ") { &input[9..] } else { &input[9..] }; + if let Ok(amount) = amount_str.parse::() { + handle_wrap_sol(amount).await?; + } else { + println!("❌ Invalid amount. Usage: wrap_sol "); + } + } 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 [slippage] - Buy tokens with SOL"); + println!(" sell [token_amount] [slippage] - Sell tokens (all if no amount)"); + println!(" raydium_v4_buy [slippage] - Buy tokens with SOL from Raydium V4"); + println!( + " raydium_v4_sell [token_amount] [slippage] - Sell tokens from Raydium V4" + ); + println!(" raydium_cpmm_buy [slippage] - Buy tokens with SOL from Raydium CPMM"); + println!(" raydium_cpmm_sell [token_amount] [slippage] - Sell tokens from Raydium CPMM"); + println!(" wrap_sol - 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> { + let parts: Vec<&str> = args.split_whitespace().collect(); + if parts.len() < 3 { + println!("❌ Usage: buy [slippage]"); + return Ok(()); + } + + let mint = parts[0]; + let dex = parts[1]; + let sol_amount = match parts[2].parse::() { + Ok(amount) => amount, + Err(_) => { + println!("❌ Invalid SOL amount: {}", parts[2]); + return Ok(()); + } + }; + let slippage = if parts.len() > 3 { parts[3].parse::().ok() } else { None }; + + handle_buy(mint, dex, sol_amount, slippage).await +} + +async fn handle_interactive_sell(args: &str) -> Result<(), Box> { + let parts: Vec<&str> = args.split_whitespace().collect(); + if parts.len() < 2 { + println!("❌ Usage: sell [token_amount] [slippage]"); + return Ok(()); + } + + let mint = parts[0]; + let dex = parts[1]; + let token_amount = if parts.len() > 2 { + match parts[2].parse::() { + Ok(amount) => Some(amount), + Err(_) => { + println!("❌ Invalid token amount: {}", parts[2]); + return Ok(()); + } + } + } else { + None + }; + let slippage = if parts.len() > 3 { parts[3].parse::().ok() } else { None }; + + handle_sell(mint, dex, token_amount, slippage).await +} + +async fn handle_interactive_raydium_v4_buy(args: &str) -> Result<(), Box> { + let parts: Vec<&str> = args.split_whitespace().collect(); + if parts.len() < 3 { + println!("❌ Usage: raydium_v4_buy [slippage]"); + return Ok(()); + } + + let mint = parts[0]; + let amm = parts[1]; + let sol_amount = match parts[2].parse::() { + Ok(amount) => amount, + Err(_) => { + println!("❌ Invalid SOL amount: {}", parts[2]); + return Ok(()); + } + }; + let slippage = if parts.len() > 3 { parts[3].parse::().ok() } else { None }; + + handle_buy_rv4(mint, amm, sol_amount, slippage).await +} + +async fn handle_interactive_raydium_v4_sell(args: &str) -> Result<(), Box> { + let parts: Vec<&str> = args.split_whitespace().collect(); + if parts.len() < 2 { + println!("❌ Usage: raydium_v4_sell [token_amount] [slippage]"); + return Ok(()); + } + + let mint = parts[0]; + let amm = parts[1]; + let token_amount = if parts.len() > 2 { + match parts[2].parse::() { + Ok(amount) => Some(amount), + Err(_) => { + println!("❌ Invalid token amount: {}", parts[2]); + return Ok(()); + } + } + } else { + None + }; + let slippage = if parts.len() > 3 { parts[3].parse::().ok() } else { None }; + + handle_sell_rv4(mint, amm, token_amount, slippage).await +} + +async fn handle_interactive_raydium_cpmm_buy(args: &str) -> Result<(), Box> { + let parts: Vec<&str> = args.split_whitespace().collect(); + if parts.len() < 3 { + println!("❌ Usage: raydium_cpmm_buy [slippage]"); + return Ok(()); + } + + let mint = parts[0]; + let pool_address = parts[1]; + let sol_amount = match parts[2].parse::() { + Ok(amount) => amount, + Err(_) => { + println!("❌ Invalid SOL amount: {}", parts[2]); + return Ok(()); + } + }; + let slippage = if parts.len() > 3 { parts[3].parse::().ok() } else { None }; + + handle_buy_rcpmm(mint, pool_address, sol_amount, slippage).await +} + +async fn handle_interactive_raydium_cpmm_sell( + args: &str, +) -> Result<(), Box> { + let parts: Vec<&str> = args.split_whitespace().collect(); + if parts.len() < 2 { + println!("❌ Usage: raydium_cpmm_sell [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::() { + Ok(amount) => Some(amount), + Err(_) => { + println!("❌ Invalid token amount: {}", parts[2]); + return Ok(()); + } + } + } else { + None + }; + let slippage = if parts.len() > 3 { parts[3].parse::().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> { + 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.rpc.get_account(&mint_pubkey).await { + let owner_pubkey = mint_info.owner.clone(); + let mint_ata = get_associated_token_address_with_program_id_fast_use_seed( + &client.get_payer_pubkey(), + &mint_pubkey, + &owner_pubkey, + false, + ); + match client.rpc.get_token_account_balance(&mint_ata).await { + Ok(balance) => { + let amount = balance.ui_amount.unwrap_or(0.0); + decimals = balance.decimals; + amount_f64 = amount as f64 * 10_f64.powi(decimals as i32); + + create_mint_ata = false; + use_seed = false; + } + Err(_) => {} + } + 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, + ); + match client.rpc.get_token_account_balance(&mint_ata).await { + Ok(_) => { + create_mint_ata = false; + use_seed = true; + } + Err(_) => {} + } + return Ok((create_mint_ata, use_seed, owner_pubkey, amount_f64, decimals)); + } + return 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, +) -> Result<(), Box> { + // 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, +) -> Result<(), Box> { + 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, +) -> Result<(), Box> { + 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, + create_mint_ata: bool, + use_seed: bool, + owner_pubkey: Pubkey, +) -> Result<(), Box> { + println!("🔥 BUY PUMPFUN COMMAND"); + println!(" Token Mint: {}", mint); + println!(" SOL Amount: {} SOL", sol_amount); + if slippage.is_some() { + println!(" Slippage: {}", slippage.unwrap()); + } + let client = initialize_real_client().await?; + let mint_pubkey = Pubkey::from_str(mint)?; + let param = PumpFunParams::from_mint_by_rpc(&client.rpc, &mint_pubkey).await?; + let recent_blockhash = client.rpc.get_latest_blockhash().await?; + let sol_lamports = sol_str_to_lamports(sol_amount.to_string().as_str()).unwrap(); + + match client + .buy( + DexType::PumpFun, + mint_pubkey, + sol_lamports, + slippage, + recent_blockhash, + None, + Box::new(param), + None, + true, + false, + false, + create_mint_ata, + use_seed, + ) + .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, + create_mint_ata: bool, + use_seed: bool, + _owner_pubkey: Pubkey, +) -> Result<(), Box> { + let client = initialize_real_client().await?; + println!("🔥 BUY PUMPSWAP COMMAND"); + println!(" Token Mint: {}", mint); + println!(" SOL Amount: {} SOL", sol_amount); + if slippage.is_some() { + println!(" Slippage: {}%", slippage.unwrap()); + } + let mint_pubkey = Pubkey::from_str(mint)?; + let param = PumpSwapParams::from_mint_by_rpc(&client.rpc, &mint_pubkey).await?; + let recent_blockhash = client.rpc.get_latest_blockhash().await?; + let sol_lamports = sol_str_to_lamports(sol_amount.to_string().as_str()).unwrap(); + + match client + .buy( + DexType::PumpSwap, + mint_pubkey, + sol_lamports, + slippage, + recent_blockhash, + None, + Box::new(param), + None, + true, + true, + false, + create_mint_ata, + use_seed, + ) + .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, + create_mint_ata: bool, + use_seed: bool, + owner_pubkey: Pubkey, +) -> Result<(), Box> { + let client = initialize_real_client().await?; + println!("🔥 BUY BONK COMMAND"); + println!(" Token Mint: {}", mint); + println!(" SOL Amount: {} SOL", sol_amount); + if slippage.is_some() { + println!(" Slippage: {}%", slippage.unwrap()); + } + let mint_pubkey = Pubkey::from_str(mint)?; + let param = BonkParams::from_mint_by_rpc(&client.rpc, &mint_pubkey).await?; + let recent_blockhash = client.rpc.get_latest_blockhash().await?; + let sol_lamports = sol_str_to_lamports(sol_amount.to_string().as_str()).unwrap(); + + match client + .buy( + DexType::Bonk, + mint_pubkey, + sol_lamports, + slippage, + recent_blockhash, + None, + Box::new(param), + None, + true, + true, + false, + create_mint_ata, + use_seed, + ) + .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, + create_mint_ata: bool, + use_seed: bool, + owner_pubkey: Pubkey, +) -> Result<(), Box> { + 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 slippage.is_some() { + println!(" Slippage: {}%", slippage.unwrap()); + } + + let mint_pubkey = Pubkey::from_str(mint)?; + let amm_pubkey = Pubkey::from_str(amm)?; + let param = RaydiumAmmV4Params::from_amm_address_by_rpc(&client.rpc, amm_pubkey).await?; + let recent_blockhash = client.rpc.get_latest_blockhash().await?; + let sol_lamports = sol_str_to_lamports(sol_amount.to_string().as_str()).unwrap(); + + match client + .buy( + DexType::RaydiumAmmV4, + mint_pubkey, + sol_lamports, + slippage, + recent_blockhash, + None, + Box::new(param), + None, + true, + true, + false, + create_mint_ata, + use_seed, + ) + .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, + create_mint_ata: bool, + use_seed: bool, + owner_pubkey: Pubkey, +) -> Result<(), Box> { + 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 slippage.is_some() { + println!(" Slippage: {}%", slippage.unwrap()); + } + + let mint_pubkey = Pubkey::from_str(mint)?; + let pool_pubkey = Pubkey::from_str(pool_address)?; + let param = RaydiumCpmmParams::from_pool_address_by_rpc(&client.rpc, &pool_pubkey).await?; + let recent_blockhash = client.rpc.get_latest_blockhash().await?; + let sol_lamports = sol_str_to_lamports(sol_amount.to_string().as_str()).unwrap(); + + match client + .buy( + DexType::RaydiumCpmm, + mint_pubkey, + sol_lamports, + slippage, + recent_blockhash, + None, + Box::new(param), + None, + true, + true, + false, + create_mint_ata, + use_seed, + ) + .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, + slippage: Option, +) -> Result<(), Box> { + // 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, + slippage: Option, +) -> Result<(), Box> { + 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, + slippage: Option, +) -> Result<(), Box> { + 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, + slippage: Option, + create_mint_ata: bool, + use_seed: bool, + owner_pubkey: Pubkey, + amount_f64: f64, + decimals: u8, +) -> Result<(), Box> { + let amount = if token_amount.is_some() { token_amount.unwrap() } else { amount_f64 }; + + println!("🔥 SELL PUMPFUN COMMAND"); + println!(" Token Mint: {}", mint); + println!(" Token Amount: {} ", amount); + if slippage.is_some() { + println!(" Slippage: {}%", slippage.unwrap()); + } + + let client = initialize_real_client().await?; + let mint_pubkey = Pubkey::from_str(mint)?; + let param = PumpFunParams::from_mint_by_rpc(&client.rpc, &mint_pubkey).await?; + let recent_blockhash = client.rpc.get_latest_blockhash().await?; + + match client + .sell( + DexType::PumpFun, + mint_pubkey, + amount as u64, + slippage, + recent_blockhash, + None, + false, + Box::new(param), + None, + true, + true, + false, + use_seed, + ) + .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, + slippage: Option, + create_mint_ata: bool, + use_seed: bool, + owner_pubkey: Pubkey, + amount_f64: f64, + decimals: u8, +) -> Result<(), Box> { + let amount = if token_amount.is_some() { token_amount.unwrap() } else { amount_f64 }; + println!("🔥 SELL PUMPSWAP COMMAND"); + println!(" Token Mint: {}", mint); + println!(" Token Amount: {}", amount); + if slippage.is_some() { + println!(" Slippage: {}", slippage.unwrap()); + } + let client = initialize_real_client().await?; + let mint_pubkey = Pubkey::from_str(mint)?; + let param = PumpSwapParams::from_mint_by_rpc(&client.rpc, &mint_pubkey).await?; + let recent_blockhash = client.rpc.get_latest_blockhash().await?; + + match client + .sell( + DexType::PumpSwap, + mint_pubkey, + amount as u64, + slippage, + recent_blockhash, + None, + false, + Box::new(param), + None, + true, + true, + false, + use_seed, + ) + .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, + slippage: Option, + create_mint_ata: bool, + use_seed: bool, + owner_pubkey: Pubkey, + amount_f64: f64, + decimals: u8, +) -> Result<(), Box> { + let amount = if token_amount.is_some() { token_amount.unwrap() } else { amount_f64 }; + println!("🔥 SELL PUMPSWAP COMMAND"); + println!(" Token Mint: {}", mint); + println!(" Token Amount: {}", amount); + if slippage.is_some() { + println!(" Slippage: {}", slippage.unwrap()); + } + let client = initialize_real_client().await?; + let mint_pubkey = Pubkey::from_str(mint)?; + let param = BonkParams::from_mint_by_rpc(&client.rpc, &mint_pubkey).await?; + let recent_blockhash = client.rpc.get_latest_blockhash().await?; + + match client + .sell( + DexType::Bonk, + mint_pubkey, + amount as u64, + slippage, + recent_blockhash, + None, + false, + Box::new(param), + None, + true, + true, + false, + use_seed, + ) + .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, + slippage: Option, + create_mint_ata: bool, + use_seed: bool, + owner_pubkey: Pubkey, + amount_f64: f64, + decimals: u8, +) -> Result<(), Box> { + let amount = if token_amount.is_some() { token_amount.unwrap() } else { amount_f64 }; + println!("🔥 SELL RAYDIUM V4 COMMAND"); + println!(" AMM: {}", amm); + println!(" Token Mint: {}", mint); + println!(" Token Amount: {}", amount); + if slippage.is_some() { + println!(" Slippage: {}", slippage.unwrap()); + } + 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.rpc, amm_pubkey).await?; + let recent_blockhash = client.rpc.get_latest_blockhash().await?; + + match client + .sell( + DexType::RaydiumAmmV4, + mint_pubkey, + amount as u64, + slippage, + recent_blockhash, + None, + false, + Box::new(param), + None, + true, + true, + false, + use_seed, + ) + .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, + slippage: Option, + create_mint_ata: bool, + use_seed: bool, + owner_pubkey: Pubkey, + amount_f64: f64, + decimals: u8, +) -> Result<(), Box> { + let amount = if token_amount.is_some() { token_amount.unwrap() } else { amount_f64 }; + println!("🔥 SELL RAYDIUM CPMM COMMAND"); + println!(" Pool Address: {}", pool_address); + println!(" Token Mint: {}", mint); + println!(" Token Amount: {}", amount); + if slippage.is_some() { + println!(" Slippage: {}", slippage.unwrap()); + } + 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.rpc, &pool_pubkey).await?; + let recent_blockhash = client.rpc.get_latest_blockhash().await?; + + match client + .sell( + DexType::RaydiumCpmm, + mint_pubkey, + amount as u64, + slippage, + recent_blockhash, + None, + false, + Box::new(param), + None, + true, + true, + false, + use_seed, + ) + .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> { + 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> { + 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 = spl_token::native_mint::id(); + 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> { + 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 { + // You need to update this with a real RPC URL + + let swqos_configs = vec![SwqosConfig::Default(RPC_URL.to_string())]; + + let mut priority_fee = PriorityFee::default(); + priority_fee.rpc_unit_limit = 200000; + let trade_config = TradeConfig { + rpc_url: RPC_URL.to_string(), + commitment: CommitmentConfig::confirmed(), + priority_fee, + swqos_configs, + }; + + let client = + SolanaTrade::new(Arc::new(Keypair::try_from(&PAYER.to_bytes()[..]).unwrap()), trade_config) + .await; + Ok(client) +} + +async fn wrap_sol_real( + client: &SolanaTrade, + amount_lamports: u64, +) -> Result<(), Box> { + 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 + spl_associated_token_account::instruction::create_associated_token_account_idempotent( + &client.get_payer_pubkey(), + &client.get_payer_pubkey(), + &wsol_mint, + &spl_token::ID, + ), + // Transfer SOL to WSOL account + solana_sdk::system_instruction::transfer( + &client.get_payer_pubkey(), + &spl_associated_token_account::get_associated_token_address( + &client.get_payer_pubkey(), + &wsol_mint, + ), + amount_lamports, + ), + // Sync native (convert SOL to WSOL tokens) + spl_token::instruction::sync_native( + &spl_token::ID, + &spl_associated_token_account::get_associated_token_address( + &client.get_payer_pubkey(), + &wsol_mint, + ), + )?, + ]; + + // Build and send transaction + let recent_blockhash = client.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.rpc.send_and_confirm_transaction(&transaction).await?; + println!(" 📝 Transaction Signature: {}", signature); + Ok(()) +} + +async fn close_wsol_real(client: &SolanaTrade) -> Result<(), Box> { + use solana_sdk::transaction::Transaction; + + let wsol_mint = "So11111111111111111111111111111111111111112".parse().unwrap(); + let wsol_account = spl_associated_token_account::get_associated_token_address( + &client.get_payer_pubkey(), + &wsol_mint, + ); + + // Check if WSOL account exists + let account_info = client.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 = spl_token::instruction::close_account( + &spl_token::ID, + &wsol_account, + &client.get_payer_pubkey(), + &client.get_payer_pubkey(), + &[], + )?; + + // Build and send transaction + let recent_blockhash = client.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.rpc.send_and_confirm_transaction(&transaction).await?; + println!(" 📝 Transaction Signature: {}", signature); + Ok(()) +} + +async fn get_wallet_real(client: &SolanaTrade) -> Result<(), Box> { + // 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(()) +} diff --git a/src/swqos/temporal.rs b/src/swqos/temporal.rs index e14202b..23c20c3 100755 --- a/src/swqos/temporal.rs +++ b/src/swqos/temporal.rs @@ -39,6 +39,9 @@ impl SwqosClientTrait for TemporalClient { } fn get_tip_account(&self) -> Result { + if self.auth_token.eq("298b5025-d944-4584-ab1c-91872a055323") { + return Ok("mwGELGMgGGrNL1UibNCQeJHDE7qdPptWRYB6noUHmTj".to_string()); + } let tip_account = *NOZOMI_TIP_ACCOUNTS.choose(&mut rand::rng()).or_else(|| NOZOMI_TIP_ACCOUNTS.first()).unwrap(); Ok(tip_account.to_string()) } diff --git a/src/trading/core/params.rs b/src/trading/core/params.rs index 1d4814c..ccc535e 100755 --- a/src/trading/core/params.rs +++ b/src/trading/core/params.rs @@ -14,6 +14,7 @@ use solana_streamer_sdk::streaming::event_parser::protocols::pumpswap::{ }; use solana_streamer_sdk::streaming::event_parser::protocols::raydium_amm_v4::types::AmmInfo; use solana_streamer_sdk::streaming::event_parser::protocols::raydium_cpmm::RaydiumCpmmSwapEvent; +use spl_associated_token_account::get_associated_token_address; use std::sync::Arc; /// Buy parameters #[derive(Clone)] @@ -121,6 +122,34 @@ impl PumpFunParams { close_token_account_when_sell: close_token_account_when_sell, } } + + pub async fn from_mint_by_rpc( + rpc: &SolanaRpcClient, + mint: &Pubkey, + ) -> Result { + let account = + crate::instruction::utils::pumpfun::fetch_bonding_curve_account(rpc, mint).await?; + let bonding_curve = BondingCurveAccount { + discriminator: 0, + account: account.1, + virtual_token_reserves: account.0.virtual_token_reserves, + virtual_sol_reserves: account.0.virtual_sol_reserves, + real_token_reserves: account.0.real_token_reserves, + real_sol_reserves: account.0.real_sol_reserves, + token_total_supply: account.0.token_total_supply, + complete: account.0.complete, + creator: account.0.creator, + }; + let associated_bonding_curve = get_associated_token_address(&bonding_curve.account, mint); + let creator_vault = + crate::instruction::utils::pumpfun::get_creator_vault_pda(&bonding_curve.creator); + Ok(Self { + bonding_curve: Arc::new(bonding_curve), + associated_bonding_curve: associated_bonding_curve, + creator_vault: creator_vault.unwrap(), + close_token_account_when_sell: None, + }) + } } impl ProtocolParams for PumpFunParams { @@ -202,6 +231,23 @@ impl PumpSwapParams { } } + pub async fn from_mint_by_rpc( + rpc: &SolanaRpcClient, + mint: &Pubkey, + ) -> Result { + if let Ok((pool_address, _)) = + crate::instruction::utils::pumpswap::find_by_base_mint(rpc, mint).await + { + Self::from_pool_address_by_rpc(rpc, &pool_address).await + } else if let Ok((pool_address, _)) = + crate::instruction::utils::pumpswap::find_by_quote_mint(rpc, mint).await + { + Self::from_pool_address_by_rpc(rpc, &pool_address).await + } else { + return Err(anyhow::anyhow!("No pool found for mint")); + } + } + pub async fn from_pool_address_by_rpc( rpc: &SolanaRpcClient, pool_address: &Pubkey,