Skip to main content

sol_safekey/
operations.rs

1//! Solana Operations Interactive Menu
2//!
3//! Provides interactive Solana operations using encrypted keystore
4//! 提供使用加密 keystore 的交互式 Solana 操作
5
6use std::io::{self, Write};
7use colored::*;
8use solana_sdk::signature::Keypair;
9use solana_sdk::signer::Signer;
10use solana_sdk::pubkey::Pubkey;
11use solana_sdk::bs58;
12use std::str::FromStr;
13
14#[cfg(feature = "solana-ops")]
15use crate::solana_utils::{SolanaClient, SolanaClientSdk, lamports_to_sol};
16
17#[cfg(feature = "solana-ops")]
18use solana_client::rpc_client::RpcClient;
19
20const DEFAULT_RPC_URL: &str = "https://api.mainnet-beta.solana.com";
21const DEVNET_RPC_URL: &str = "https://api.devnet.solana.com";
22
23/// Language for UI
24#[derive(Clone, Copy, PartialEq)]
25pub enum Language {
26    English,
27    Chinese,
28}
29
30/// Read user input with default value
31fn read_input(prompt: &str, default: &str) -> String {
32    print!("{}", prompt);
33    io::stdout().flush().unwrap();
34
35    let mut input = String::new();
36    io::stdin().read_line(&mut input).unwrap();
37    let input = input.trim();
38
39    if input.is_empty() && !default.is_empty() {
40        default.to_string()
41    } else {
42        input.to_string()
43    }
44}
45
46/// Show Solana operations menu
47#[cfg(feature = "solana-ops")]
48pub fn show_operations_menu(keypair: &Keypair, language: Language) -> Result<(), String> {
49    loop {
50        println!("\n{}", "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━".bright_cyan());
51        if language == Language::English {
52            println!("  {} - Solana Operations", "🔧 Sol-SafeKey".bright_yellow().bold());
53        } else {
54            println!("  {} - Solana 操作", "🔧 Sol-SafeKey".bright_yellow().bold());
55        }
56        println!("{}", "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━".bright_cyan());
57
58        println!("\n{}", if language == Language::English {
59            "Current Wallet:"
60        } else {
61            "当前钱包:"
62        }.bright_green());
63        println!("  📍 {}", keypair.pubkey().to_string().bright_white());
64
65        println!("\n{}", if language == Language::English {
66            "Available Operations:"
67        } else {
68            "可用操作:"
69        }.bright_green());
70
71        if language == Language::English {
72            println!("  {}  Check SOL Balance", "1.".bright_cyan());
73            println!("  {}  Transfer SOL", "2.".bright_cyan());
74            println!("  {}  Create WSOL ATA", "3.".bright_cyan());
75            println!("  {}  Wrap SOL → WSOL", "4.".bright_cyan());
76            println!("  {}  Unwrap WSOL → SOL", "5.".bright_cyan());
77            println!("  {}  Transfer SPL Token", "6.".bright_cyan());
78            println!("  {}  Create Nonce Account", "7.".bright_cyan());
79            println!("  {}  Back to Main Menu", "0.".bright_cyan());
80        } else {
81            println!("  {}  查询 SOL 余额", "1.".bright_cyan());
82            println!("  {}  转账 SOL", "2.".bright_cyan());
83            println!("  {}  创建 WSOL ATA 账户", "3.".bright_cyan());
84            println!("  {}  包装 SOL → WSOL", "4.".bright_cyan());
85            println!("  {}  解包 WSOL → SOL", "5.".bright_cyan());
86            println!("  {}  转账 SPL 代币", "6.".bright_cyan());
87            println!("  {}  创建 Nonce 账户", "7.".bright_cyan());
88            println!("  {}  返回主菜单", "0.".bright_cyan());
89        }
90
91        let prompt = if language == Language::English {
92            "\nSelect option [0-7]: "
93        } else {
94            "\n请输入选项 [0-7]: "
95        };
96
97        let choice = read_input(prompt, "");
98
99        match choice.as_str() {
100            "1" => check_balance(keypair, language)?,
101            "2" => transfer_sol(keypair, language)?,
102            "3" => create_wsol_ata(keypair, language)?,
103            "4" => wrap_sol(keypair, language)?,
104            "5" => unwrap_sol(keypair, language)?,
105            "6" => transfer_token(keypair, language)?,
106            "7" => create_nonce_account(keypair, language)?,
107            "0" => {
108                if language == Language::English {
109                    println!("\n{}", "Returning to main menu...".bright_green());
110                } else {
111                    println!("\n{}", "返回主菜单...".bright_green());
112                }
113                return Ok(());
114            }
115            _ => {
116                let msg = if language == Language::English {
117                    "❌ Invalid option, please try again"
118                } else {
119                    "❌ 无效选项,请重试"
120                };
121                println!("\n{}", msg.red());
122            }
123        }
124    }
125}
126
127/// Placeholder for non-solana-ops builds
128#[cfg(not(feature = "solana-ops"))]
129pub fn show_operations_menu(_keypair: &Keypair, language: Language) -> Result<(), String> {
130    let msg = if language == Language::English {
131        "❌ Solana operations require the 'solana-ops' feature. Please rebuild with: cargo build --features solana-ops"
132    } else {
133        "❌ Solana 操作需要 'solana-ops' 功能。请使用以下命令重新编译: cargo build --features solana-ops"
134    };
135    Err(msg.to_string())
136}
137
138#[cfg(feature = "solana-ops")]
139pub fn check_balance(keypair: &Keypair, language: Language) -> Result<(), String> {
140    println!("\n{}", "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━".bright_cyan());
141    if language == Language::English {
142        println!("  {}", "📊 Check SOL Balance".bright_yellow().bold());
143    } else {
144        println!("  {}", "📊 查询 SOL 余额".bright_yellow().bold());
145    }
146    println!("{}", "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━".bright_cyan());
147
148    let network_prompt = if language == Language::English {
149        "\nSelect network:\n  1. Mainnet\n  2. Devnet\nChoice [1]: "
150    } else {
151        "\n选择网络:\n  1. 主网 (Mainnet)\n  2. 测试网 (Devnet)\n选择 [1]: "
152    };
153
154    let network = read_input(network_prompt, "1");
155    let rpc_url = if network == "2" { DEVNET_RPC_URL } else { DEFAULT_RPC_URL };
156
157    if language == Language::English {
158        println!("\n🔍 Checking balance on {}...", if network == "2" { "Devnet" } else { "Mainnet" });
159    } else {
160        println!("\n🔍 正在查询{}余额...", if network == "2" { "测试网" } else { "主网" });
161    }
162
163    let client = SolanaClient::new(rpc_url.to_string());
164    match client.get_sol_balance(&keypair.pubkey()) {
165        Ok(balance) => {
166            let sol = lamports_to_sol(balance);
167            println!("\n{}", "✅ Balance:".bright_green());
168            println!("  💰 {} SOL", sol.to_string().bright_white().bold());
169            println!("  📊 {} lamports", balance.to_string().bright_white());
170            Ok(())
171        }
172        Err(e) => {
173            let msg = if language == Language::English {
174                format!("❌ Failed to fetch balance: {}", e)
175            } else {
176                format!("❌ 查询余额失败: {}", e)
177            };
178            Err(msg)
179        }
180    }
181}
182
183#[cfg(feature = "solana-ops")]
184pub fn transfer_sol(keypair: &Keypair, language: Language) -> Result<(), String> {
185    println!("\n{}", "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━".bright_cyan());
186    if language == Language::English {
187        println!("  {}", "💸 Transfer SOL".bright_yellow().bold());
188    } else {
189        println!("  {}", "💸 转账 SOL".bright_yellow().bold());
190    }
191    println!("{}", "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━".bright_cyan());
192
193    let network_prompt = if language == Language::English {
194        "\nSelect network:\n  1. Mainnet\n  2. Devnet\nChoice [1]: "
195    } else {
196        "\n选择网络:\n  1. 主网 (Mainnet)\n  2. 测试网 (Devnet)\n选择 [1]: "
197    };
198
199    let network = read_input(network_prompt, "1");
200    let rpc_url = if network == "2" { DEVNET_RPC_URL } else { DEFAULT_RPC_URL };
201
202    let recipient_prompt = if language == Language::English {
203        "\nRecipient address: "
204    } else {
205        "\n接收地址: "
206    };
207    let recipient_str = read_input(recipient_prompt, "");
208
209    let recipient = Pubkey::from_str(&recipient_str)
210        .map_err(|_| if language == Language::English {
211            "❌ Invalid recipient address".to_string()
212        } else {
213            "❌ 无效的接收地址".to_string()
214        })?;
215
216    let amount_prompt = if language == Language::English {
217        "Amount (SOL): "
218    } else {
219        "金额 (SOL): "
220    };
221    let amount_str = read_input(amount_prompt, "");
222    let amount_sol: f64 = amount_str.parse()
223        .map_err(|_| if language == Language::English {
224            "❌ Invalid amount".to_string()
225        } else {
226            "❌ 无效的金额".to_string()
227        })?;
228
229    let amount_lamports = (amount_sol * 1_000_000_000.0) as u64;
230
231    println!("\n{}", "📋 Transaction Summary:".bright_yellow());
232    println!("  From: {}", keypair.pubkey().to_string().bright_white());
233    println!("  To: {}", recipient.to_string().bright_white());
234    println!("  Amount: {} SOL", amount_sol.to_string().bright_white().bold());
235
236    let confirm_prompt = if language == Language::English {
237        "\nConfirm transaction? (yes/no) [no]: "
238    } else {
239        "\n确认交易? (yes/no) [no]: "
240    };
241    let confirm = read_input(confirm_prompt, "no");
242
243    if confirm.to_lowercase() != "yes" {
244        let msg = if language == Language::English {
245            "❌ Transaction cancelled"
246        } else {
247            "❌ 交易已取消"
248        };
249        println!("\n{}", msg.red());
250        return Ok(());
251    }
252
253    if language == Language::English {
254        println!("\n🚀 Sending transaction...");
255    } else {
256        println!("\n🚀 正在发送交易...");
257    }
258
259    let client = SolanaClient::new(rpc_url.to_string());
260    match client.transfer_sol(keypair, &recipient, amount_lamports) {
261        Ok(signature) => {
262            println!("\n{}", "✅ Transfer successful!".bright_green().bold());
263            println!("  📝 Signature: {}", signature.to_string().bright_white());
264            let explorer_url = if network == "2" {
265                format!("https://explorer.solana.com/tx/{}?cluster=devnet", signature)
266            } else {
267                format!("https://explorer.solana.com/tx/{}", signature)
268            };
269            println!("  🔗 Explorer: {}", explorer_url.bright_blue());
270            Ok(())
271        }
272        Err(e) => {
273            let msg = if language == Language::English {
274                format!("❌ Transfer failed: {}", e)
275            } else {
276                format!("❌ 转账失败: {}", e)
277            };
278            Err(msg)
279        }
280    }
281}
282
283/// Helper: Run async code, handling both sync and async contexts
284#[cfg(feature = "solana-ops")]
285fn run_async<F, T>(future: F) -> T
286where
287    F: std::future::Future<Output = T>,
288{
289    // Try to get current runtime handle
290    match tokio::runtime::Handle::try_current() {
291        Ok(handle) => {
292            // We're already in a runtime, use block_in_place
293            tokio::task::block_in_place(|| handle.block_on(future))
294        }
295        Err(_) => {
296            // No runtime exists, create a new one
297            let rt = tokio::runtime::Runtime::new().expect("Failed to create runtime");
298            rt.block_on(future)
299        }
300    }
301}
302
303/// Helper: Calculate and print WSOL ATA address
304#[cfg(feature = "solana-ops")]
305fn print_wsol_ata_address(owner: &Pubkey, language: Language, _use_seed: bool) {
306    use sol_trade_sdk::common::fast_fn::{
307        get_associated_token_address_with_program_id_fast,
308    };
309
310    let wsol_mint = Pubkey::from_str("So11111111111111111111111111111111111111112").unwrap();
311    let token_program = Pubkey::from_str("TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA").unwrap();
312
313    // SDK 强制对 WSOL 使用标准 ATA(不支持 seed 优化)
314    let ata = get_associated_token_address_with_program_id_fast(owner, &wsol_mint, &token_program);
315
316    println!("\n{}", if language == Language::English {
317        "📍 WSOL ATA Address:"
318    } else {
319        "📍 WSOL ATA 地址:"
320    }.bright_yellow());
321    println!("  {}", ata.to_string().bright_white().bold());
322}
323
324#[cfg(feature = "solana-ops")]
325pub fn create_wsol_ata(keypair: &Keypair, language: Language) -> Result<(), String> {
326    println!("\n{}", "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━".bright_cyan());
327    if language == Language::English {
328        println!("  {}", "🏦 Create WSOL ATA Account".bright_yellow().bold());
329    } else {
330        println!("  {}", "🏦 创建 WSOL ATA 账户".bright_yellow().bold());
331    }
332    println!("{}", "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━".bright_cyan());
333
334    let network_prompt = if language == Language::English {
335        "\nSelect network:\n  1. Mainnet\n  2. Devnet\nChoice [1]: "
336    } else {
337        "\n选择网络:\n  1. 主网 (Mainnet)\n  2. 测试网 (Devnet)\n选择 [1]: "
338    };
339
340    let network = read_input(network_prompt, "1");
341    let rpc_url = if network == "2" { DEVNET_RPC_URL } else { DEFAULT_RPC_URL };
342
343    // WSOL 强制使用标准 ATA(SDK 设计)
344    let use_seed_optimize = false;
345
346    // 打印WSOL ATA地址
347    print_wsol_ata_address(&keypair.pubkey(), language, use_seed_optimize);
348
349    // 检查账号是否已存在
350    if language == Language::English {
351        println!("\n🔍 Checking if account already exists...");
352    } else {
353        println!("\n🔍 检查账号是否已存在...");
354    }
355
356    let client_sdk = SolanaClientSdk::new(rpc_url.to_string(), use_seed_optimize);
357    let wsol_balance = client_sdk.get_wsol_balance(&keypair.pubkey())
358        .unwrap_or(0);
359
360    // 如果能查到余额(即使是0),说明账号已存在
361    let rpc_client = RpcClient::new(rpc_url.to_string());
362    let wsol_mint = Pubkey::from_str("So11111111111111111111111111111111111111112").unwrap();
363    let token_program = Pubkey::from_str("TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA").unwrap();
364
365    let ata = {
366        use sol_trade_sdk::common::fast_fn::get_associated_token_address_with_program_id_fast;
367        get_associated_token_address_with_program_id_fast(&keypair.pubkey(), &wsol_mint, &token_program)
368    };
369
370    let account_exists = rpc_client.get_account(&ata).is_ok();
371
372    if account_exists {
373        println!("\n{}", "ℹ️  WSOL ATA account already exists!".bright_yellow().bold());
374        println!("  💰 Balance: {} lamports ({} SOL)",
375            wsol_balance.to_string().bright_white(),
376            lamports_to_sol(wsol_balance).to_string().bright_white());
377
378        if language == Language::English {
379            println!("\n✅ Account is ready to use. No need to create.");
380        } else {
381            println!("\n✅ 账号已准备就绪,无需创建。");
382        }
383        return Ok(());
384    }
385
386    println!("\n{}", if language == Language::English {
387        "ℹ️  Account does not exist. Creating new WSOL ATA..."
388    } else {
389        "ℹ️  账号不存在,将创建新的 WSOL ATA..."
390    }.bright_yellow());
391
392    let confirm_prompt = if language == Language::English {
393        "\nConfirm operation? (yes/no) [no]: "
394    } else {
395        "\n确认操作? (yes/no) [no]: "
396    };
397    let confirm = read_input(confirm_prompt, "no");
398
399    if confirm.to_lowercase() != "yes" {
400        let msg = if language == Language::English {
401            "❌ Operation cancelled"
402        } else {
403            "❌ 操作已取消"
404        };
405        println!("\n{}", msg.red());
406        return Ok(());
407    }
408
409    if language == Language::English {
410        println!("\n🚀 Creating WSOL ATA...");
411    } else {
412        println!("\n🚀 正在创建 WSOL ATA...");
413    }
414
415    // 使用run_async执行异步操作
416    match run_async(client_sdk.create_wsol_ata(keypair)) {
417        Ok(signature) => {
418            println!("\n{}", "✅ WSOL ATA created successfully!".bright_green().bold());
419            println!("  📝 Signature: {}", signature.to_string().bright_white());
420            let explorer_url = if network == "2" {
421                format!("https://explorer.solana.com/tx/{}?cluster=devnet", signature)
422            } else {
423                format!("https://explorer.solana.com/tx/{}", signature)
424            };
425            println!("  🔗 Explorer: {}", explorer_url.bright_blue());
426            Ok(())
427        }
428        Err(e) => {
429            let msg = if language == Language::English {
430                format!("❌ Creation failed: {}", e)
431            } else {
432                format!("❌ 创建失败: {}", e)
433            };
434            Err(msg)
435        }
436    }
437}
438
439#[cfg(feature = "solana-ops")]
440pub fn wrap_sol(keypair: &Keypair, language: Language) -> Result<(), String> {
441    println!("\n{}", "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━".bright_cyan());
442    if language == Language::English {
443        println!("  {}", "🔄 Wrap SOL → WSOL".bright_yellow().bold());
444    } else {
445        println!("  {}", "🔄 包装 SOL → WSOL".bright_yellow().bold());
446    }
447    println!("{}", "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━".bright_cyan());
448
449    let network_prompt = if language == Language::English {
450        "\nSelect network:\n  1. Mainnet\n  2. Devnet\nChoice [1]: "
451    } else {
452        "\n选择网络:\n  1. 主网 (Mainnet)\n  2. 测试网 (Devnet)\n选择 [1]: "
453    };
454
455    let network = read_input(network_prompt, "1");
456    let rpc_url = if network == "2" { DEVNET_RPC_URL } else { DEFAULT_RPC_URL };
457
458    // WSOL 强制使用标准 ATA(SDK 设计)
459    let use_seed_optimize = false;
460
461    // 打印WSOL ATA地址
462    print_wsol_ata_address(&keypair.pubkey(), language, use_seed_optimize);
463
464    let amount_prompt = if language == Language::English {
465        "\nAmount to wrap (SOL): "
466    } else {
467        "\n包装金额 (SOL): "
468    };
469    let amount_str = read_input(amount_prompt, "");
470    let amount_sol: f64 = amount_str.parse()
471        .map_err(|_| if language == Language::English {
472            "❌ Invalid amount".to_string()
473        } else {
474            "❌ 无效的金额".to_string()
475        })?;
476
477    let amount_lamports = (amount_sol * 1_000_000_000.0) as u64;
478
479    println!("\n{}", "📋 Operation Summary:".bright_yellow());
480    println!("  Wrap: {} SOL → WSOL", amount_sol.to_string().bright_white().bold());
481
482    let confirm_prompt = if language == Language::English {
483        "\nConfirm operation? (yes/no) [no]: "
484    } else {
485        "\n确认操作? (yes/no) [no]: "
486    };
487    let confirm = read_input(confirm_prompt, "no");
488
489    if confirm.to_lowercase() != "yes" {
490        let msg = if language == Language::English {
491            "❌ Operation cancelled"
492        } else {
493            "❌ 操作已取消"
494        };
495        println!("\n{}", msg.red());
496        return Ok(());
497    }
498
499    if language == Language::English {
500        println!("\n🚀 Wrapping SOL...");
501    } else {
502        println!("\n🚀 正在包装 SOL...");
503    }
504
505    // 使用SolanaClientSdk调用sol-trade-sdk
506    let client = SolanaClientSdk::new(rpc_url.to_string(), use_seed_optimize);
507
508    // 使用run_async执行异步操作
509    match run_async(client.wrap_sol(keypair, amount_lamports)) {
510        Ok(signature) => {
511            println!("\n{}", "✅ Wrap successful!".bright_green().bold());
512            println!("  📝 Signature: {}", signature.to_string().bright_white());
513            let explorer_url = if network == "2" {
514                format!("https://explorer.solana.com/tx/{}?cluster=devnet", signature)
515            } else {
516                format!("https://explorer.solana.com/tx/{}", signature)
517            };
518            println!("  🔗 Explorer: {}", explorer_url.bright_blue());
519            Ok(())
520        }
521        Err(e) => {
522            let msg = if language == Language::English {
523                format!("❌ Wrap failed: {}", e)
524            } else {
525                format!("❌ 包装失败: {}", e)
526            };
527            Err(msg)
528        }
529    }
530}
531
532#[cfg(feature = "solana-ops")]
533pub fn unwrap_sol(keypair: &Keypair, language: Language) -> Result<(), String> {
534    println!("\n{}", "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━".bright_cyan());
535    if language == Language::English {
536        println!("  {}", "🔄 Unwrap WSOL → SOL".bright_yellow().bold());
537    } else {
538        println!("  {}", "🔄 解包 WSOL → SOL".bright_yellow().bold());
539    }
540    println!("{}", "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━".bright_cyan());
541
542    let network_prompt = if language == Language::English {
543        "\nSelect network:\n  1. Mainnet\n  2. Devnet\nChoice [1]: "
544    } else {
545        "\n选择网络:\n  1. 主网 (Mainnet)\n  2. 测试网 (Devnet)\n选择 [1]: "
546    };
547
548    let network = read_input(network_prompt, "1");
549    let rpc_url = if network == "2" { DEVNET_RPC_URL } else { DEFAULT_RPC_URL };
550
551    // WSOL 强制使用标准 ATA(SDK 设计)
552    let use_seed_optimize = false;
553
554    // 打印WSOL ATA地址
555    print_wsol_ata_address(&keypair.pubkey(), language, use_seed_optimize);
556
557    // 询问是否指定金额
558    let amount_prompt = if language == Language::English {
559        "\nUnwrap amount (in SOL, leave empty for ALL): "
560    } else {
561        "\n解包金额(单位: SOL,留空则解包全部): "
562    };
563    let amount_input = read_input(amount_prompt, "");
564    let amount_input = amount_input.trim();
565
566    let (is_partial, amount_lamports) = if amount_input.is_empty() {
567        // 解包全部
568        println!("\n{}", if language == Language::English {
569            "ℹ️  Will unwrap ALL WSOL back to SOL"
570        } else {
571            "ℹ️  将解包所有 WSOL 回 SOL"
572        }.bright_yellow());
573        (false, 0)
574    } else {
575        // 解包指定金额
576        let amount_sol: f64 = amount_input.parse()
577            .map_err(|_| if language == Language::English {
578                "❌ Invalid amount".to_string()
579            } else {
580                "❌ 无效的金额".to_string()
581            })?;
582        
583        if amount_sol <= 0.0 {
584            return Err(if language == Language::English {
585                "❌ Amount must be greater than 0".to_string()
586            } else {
587                "❌ 金额必须大于 0".to_string()
588            });
589        }
590
591        let lamports = (amount_sol * 1_000_000_000.0) as u64;
592        println!("\n{}", if language == Language::English {
593            format!("ℹ️  Will unwrap {} SOL from WSOL", amount_sol)
594        } else {
595            format!("ℹ️  将从 WSOL 解包 {} SOL", amount_sol)
596        }.bright_yellow());
597        (true, lamports)
598    };
599
600    let confirm_prompt = if language == Language::English {
601        "\nConfirm operation? (yes/no) [no]: "
602    } else {
603        "\n确认操作? (yes/no) [no]: "
604    };
605    let confirm = read_input(confirm_prompt, "no");
606
607    if confirm.to_lowercase() != "yes" {
608        let msg = if language == Language::English {
609            "❌ Operation cancelled"
610        } else {
611            "❌ 操作已取消"
612        };
613        println!("\n{}", msg.red());
614        return Ok(());
615    }
616
617    if language == Language::English {
618        println!("\n🚀 Unwrapping WSOL...");
619    } else {
620        println!("\n🚀 正在解包 WSOL...");
621    }
622
623    // 使用SolanaClientSdk调用sol-trade-sdk
624    let client = SolanaClientSdk::new(rpc_url.to_string(), use_seed_optimize);
625
626    // 使用run_async执行异步操作
627    let result = if is_partial {
628        run_async(client.unwrap_sol_partial(keypair, amount_lamports))
629    } else {
630        run_async(client.unwrap_sol(keypair))
631    };
632    
633    match result {
634        Ok(signature) => {
635            println!("\n{}", "✅ Unwrap successful!".bright_green().bold());
636            println!("  📝 Signature: {}", signature.to_string().bright_white());
637            let explorer_url = if network == "2" {
638                format!("https://explorer.solana.com/tx/{}?cluster=devnet", signature)
639            } else {
640                format!("https://explorer.solana.com/tx/{}", signature)
641            };
642            println!("  🔗 Explorer: {}", explorer_url.bright_blue());
643            Ok(())
644        }
645        Err(e) => {
646            let msg = if language == Language::English {
647                format!("❌ Unwrap failed: {}", e)
648            } else {
649                format!("❌ 解包失败: {}", e)
650            };
651            Err(msg)
652        }
653    }
654}
655
656#[cfg(feature = "solana-ops")]
657/// Close WSOL ATA account and reclaim rent
658pub fn close_wsol_ata(keypair: &Keypair, language: Language) -> Result<(), String> {
659    println!("\n{}", "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━".bright_cyan());
660    if language == Language::English {
661        println!("  {}", "🗑️  Close WSOL ATA Account".bright_yellow().bold());
662    } else {
663        println!("  {}", "🗑️  关闭 WSOL ATA 账号".bright_yellow().bold());
664    }
665    println!("{}", "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━".bright_cyan());
666
667    let network_prompt = if language == Language::English {
668        "\nSelect network:\n  1. Mainnet\n  2. Devnet\nChoice [1]: "
669    } else {
670        "\n选择网络:\n  1. 主网 (Mainnet)\n  2. 测试网 (Devnet)\n选择 [1]: "
671    };
672
673    let network = read_input(network_prompt, "1");
674    let rpc_url = if network == "2" { DEVNET_RPC_URL } else { DEFAULT_RPC_URL };
675
676    // WSOL 强制使用标准 ATA(SDK 设计)
677    let use_seed_optimize = false;
678
679    // 打印WSOL ATA地址
680    print_wsol_ata_address(&keypair.pubkey(), language, use_seed_optimize);
681
682    println!("\n{}", if language == Language::English {
683        "ℹ️  This operation will:"
684    } else {
685        "ℹ️  此操作将:"
686    }.bright_yellow().bold());
687    
688    if language == Language::English {
689        println!("     • Unwrap ALL WSOL back to SOL automatically");
690        println!("     • Close the WSOL ATA account");
691        println!("     • Reclaim rent (~0.00203928 SOL) to your wallet");
692    } else {
693        println!("     • 自动将所有 WSOL 解包回 SOL");
694        println!("     • 关闭 WSOL ATA 账号");
695        println!("     • 回收租金 (~0.00203928 SOL) 到您的钱包");
696    }
697
698    println!("\n{}", if language == Language::English {
699        "💰 All SOL (unwrapped WSOL + rent) will be returned to your wallet!"
700    } else {
701        "�� 所有 SOL(解包的 WSOL + 租金)将返回到您的钱包!"
702    }.green().bold());
703
704    let confirm_prompt = if language == Language::English {
705        "\nConfirm operation? (yes/no) [no]: "
706    } else {
707        "\n确认操作? (yes/no) [no]: "
708    };
709    let confirm = read_input(confirm_prompt, "no");
710
711    if confirm.to_lowercase() != "yes" {
712        let msg = if language == Language::English {
713            "❌ Operation cancelled"
714        } else {
715            "❌ 操作已取消"
716        };
717        println!("\n{}", msg.red());
718        return Ok(());
719    }
720
721    if language == Language::English {
722        println!("\n🚀 Closing WSOL ATA account...");
723    } else {
724        println!("\n🚀 正在关闭 WSOL ATA 账号...");
725    }
726
727    // 使用SolanaClientSdk调用sol-trade-sdk
728    let client = SolanaClientSdk::new(rpc_url.to_string(), use_seed_optimize);
729
730    // 使用run_async执行异步操作
731    match run_async(client.unwrap_sol(keypair)) {
732        Ok(signature) => {
733            println!("\n{}", "✅ WSOL ATA closed successfully!".bright_green().bold());
734            println!("  📝 Signature: {}", signature.to_string().bright_white());
735            let explorer_url = if network == "2" {
736                format!("https://explorer.solana.com/tx/{}?cluster=devnet", signature)
737            } else {
738                format!("https://explorer.solana.com/tx/{}", signature)
739            };
740            println!("  🔗 Explorer: {}", explorer_url.bright_blue());
741            println!("\n{}", if language == Language::English {
742                "💰 Rent reclaimed to your wallet!"
743            } else {
744                "💰 租金已返还到您的钱包!"
745            }.green());
746            Ok(())
747        }
748        Err(e) => {
749            let msg = if language == Language::English {
750                format!("❌ Close failed: {}", e)
751            } else {
752                format!("❌ 关闭失败: {}", e)
753            };
754            Err(msg)
755        }
756    }
757}
758
759#[cfg(feature = "solana-ops")]
760pub fn transfer_token(keypair: &Keypair, language: Language) -> Result<(), String> {
761    println!("\n{}", "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━".bright_cyan());
762    if language == Language::English {
763        println!("  {}", "💎 Transfer SPL Token".bright_yellow().bold());
764    } else {
765        println!("  {}", "💎 转账 SPL 代币".bright_yellow().bold());
766    }
767    println!("{}", "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━".bright_cyan());
768
769    let network_prompt = if language == Language::English {
770        "\nSelect network:\n  1. Mainnet\n  2. Devnet\nChoice [1]: "
771    } else {
772        "\n选择网络:\n  1. 主网 (Mainnet)\n  2. 测试网 (Devnet)\n选择 [1]: "
773    };
774
775    let network = read_input(network_prompt, "1");
776    let rpc_url = if network == "2" { DEVNET_RPC_URL } else { DEFAULT_RPC_URL };
777
778    let mint_prompt = if language == Language::English {
779        "\nToken Mint address: "
780    } else {
781        "\n代币 Mint 地址: "
782    };
783    let mint_str = read_input(mint_prompt, "");
784
785    let mint = Pubkey::from_str(&mint_str)
786        .map_err(|_| if language == Language::English {
787            "❌ Invalid mint address".to_string()
788        } else {
789            "❌ 无效的 Mint 地址".to_string()
790        })?;
791
792    let recipient_prompt = if language == Language::English {
793        "Recipient address: "
794    } else {
795        "接收地址: "
796    };
797    let recipient_str = read_input(recipient_prompt, "");
798
799    let recipient = Pubkey::from_str(&recipient_str)
800        .map_err(|_| if language == Language::English {
801            "❌ Invalid recipient address".to_string()
802        } else {
803            "❌ 无效的接收地址".to_string()
804        })?;
805
806    let amount_prompt = if language == Language::English {
807        "Amount (smallest units): "
808    } else {
809        "金额 (最小单位): "
810    };
811    let amount_str = read_input(amount_prompt, "");
812    let amount: u64 = amount_str.parse()
813        .map_err(|_| if language == Language::English {
814            "❌ Invalid amount".to_string()
815        } else {
816            "❌ 无效的金额".to_string()
817        })?;
818
819    println!("\n{}", "📋 Transaction Summary:".bright_yellow());
820    println!("  From: {}", keypair.pubkey().to_string().bright_white());
821    println!("  To: {}", recipient.to_string().bright_white());
822    println!("  Token: {}", mint.to_string().bright_white());
823    println!("  Amount: {} (smallest units)", amount.to_string().bright_white().bold());
824
825    let confirm_prompt = if language == Language::English {
826        "\nConfirm transaction? (yes/no) [no]: "
827    } else {
828        "\n确认交易? (yes/no) [no]: "
829    };
830    let confirm = read_input(confirm_prompt, "no");
831
832    if confirm.to_lowercase() != "yes" {
833        let msg = if language == Language::English {
834            "❌ Transaction cancelled"
835        } else {
836            "❌ 交易已取消"
837        };
838        println!("\n{}", msg.red());
839        return Ok(());
840    }
841
842    if language == Language::English {
843        println!("\n🚀 Sending transaction...");
844    } else {
845        println!("\n🚀 正在发送交易...");
846    }
847
848    let client = SolanaClient::new(rpc_url.to_string());
849    match client.transfer_token(keypair, &recipient, &mint, amount) {
850        Ok(signature) => {
851            println!("\n{}", "✅ Transfer successful!".bright_green().bold());
852            println!("  📝 Signature: {}", signature.to_string().bright_white());
853            let explorer_url = if network == "2" {
854                format!("https://explorer.solana.com/tx/{}?cluster=devnet", signature)
855            } else {
856                format!("https://explorer.solana.com/tx/{}", signature)
857            };
858            println!("  🔗 Explorer: {}", explorer_url.bright_blue());
859            Ok(())
860        }
861        Err(e) => {
862            let msg = if language == Language::English {
863                format!("❌ Transfer failed: {}", e)
864            } else {
865                format!("❌ 转账失败: {}", e)
866            };
867            Err(msg)
868        }
869    }
870}
871
872#[cfg(feature = "solana-ops")]
873pub fn create_nonce_account(keypair: &Keypair, language: Language) -> Result<(), String> {
874    println!("\n{}", "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━".bright_cyan());
875    if language == Language::English {
876        println!("  {}", "🔑 Create Nonce Account".bright_yellow().bold());
877    } else {
878        println!("  {}", "🔑 创建 Nonce 账户".bright_yellow().bold());
879    }
880    println!("{}", "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━".bright_cyan());
881
882    let network_prompt = if language == Language::English {
883        "\nSelect network:\n  1. Mainnet\n  2. Devnet\nChoice [1]: "
884    } else {
885        "\n选择网络:\n  1. 主网 (Mainnet)\n  2. 测试网 (Devnet)\n选择 [1]: "
886    };
887
888    let network = read_input(network_prompt, "1");
889    let rpc_url = if network == "2" { DEVNET_RPC_URL } else { DEFAULT_RPC_URL };
890
891    println!("\n{}", if language == Language::English {
892        "ℹ️  A nonce account will be created for durable transactions"
893    } else {
894        "ℹ️  将创建一个用于持久交易的 Nonce 账户"
895    }.bright_yellow());
896
897    println!("{}", if language == Language::English {
898        "ℹ️  This requires ~0.00144 SOL for rent exemption"
899    } else {
900        "ℹ️  这需要约 0.00144 SOL 用于租金豁免"
901    }.bright_yellow());
902
903    let confirm_prompt = if language == Language::English {
904        "\nConfirm creation? (yes/no) [no]: "
905    } else {
906        "\n确认创建? (yes/no) [no]: "
907    };
908    let confirm = read_input(confirm_prompt, "no");
909
910    if confirm.to_lowercase() != "yes" {
911        let msg = if language == Language::English {
912            "❌ Operation cancelled"
913        } else {
914            "❌ 操作已取消"
915        };
916        println!("\n{}", msg.red());
917        return Ok(());
918    }
919
920    if language == Language::English {
921        println!("\n🚀 Creating nonce account...");
922    } else {
923        println!("\n🚀 正在创建 Nonce 账户...");
924    }
925
926    let client = SolanaClient::new(rpc_url.to_string());
927    match client.create_nonce_account(keypair) {
928        Ok((nonce_pubkey, signature)) => {
929            println!("\n{}", "✅ Nonce account created successfully!".bright_green().bold());
930            println!("  🔑 Nonce Account: {}", nonce_pubkey.to_string().bright_white().bold());
931            println!("  📝 Signature: {}", signature.to_string().bright_white());
932            let explorer_url = if network == "2" {
933                format!("https://explorer.solana.com/address/{}?cluster=devnet", nonce_pubkey)
934            } else {
935                format!("https://explorer.solana.com/address/{}", nonce_pubkey)
936            };
937            println!("  🔗 Explorer: {}", explorer_url.bright_blue());
938            println!("\n{}", if language == Language::English {
939                "💡 Save this nonce account address for future use!"
940            } else {
941                "💡 请保存此 Nonce 账户地址以供将来使用!"
942            }.bright_yellow());
943            Ok(())
944        }
945        Err(e) => {
946            let msg = if language == Language::English {
947                format!("❌ Creation failed: {}", e)
948            } else {
949                format!("❌ 创建失败: {}", e)
950            };
951            Err(msg)
952        }
953    }
954}
955
956/// Entry point for Solana operations from interactive menu
957/// Prompts for keystore file and password, then shows operations menu
958pub fn show_solana_operations_menu(language: crate::interactive::Language) -> Result<(), String> {
959    #[cfg(feature = "solana-ops")]
960    {
961        use crate::KeyManager;
962
963        // Convert language from interactive module to operations module
964        let ops_language = match language {
965            crate::interactive::Language::English => Language::English,
966            crate::interactive::Language::Chinese => Language::Chinese,
967        };
968
969        // Prompt for keystore file
970        let file_prompt = if ops_language == Language::English {
971            "Keystore file path (default: wallet.json): "
972        } else {
973            "Keystore 文件路径 (默认: wallet.json): "
974        };
975
976        let file_path = read_input(file_prompt, "wallet.json");
977
978        // Check if file exists
979        if !std::path::Path::new(&file_path).exists() {
980            let err_msg = if ops_language == Language::English {
981                format!("❌ File not found: {}", file_path)
982            } else {
983                format!("❌ 文件不存在: {}", file_path)
984            };
985            return Err(err_msg);
986        }
987
988        // Prompt for password
989        let password_prompt = if ops_language == Language::English {
990            "Enter password: "
991        } else {
992            "请输入密码: "
993        };
994
995        print!("{}", password_prompt);
996        io::stdout().flush().map_err(|e| e.to_string())?;
997        let password = rpassword::read_password()
998            .map_err(|e| format!("Failed to read password: {}", e))?;
999
1000        // Load keystore
1001        let keystore_json = std::fs::read_to_string(&file_path)
1002            .map_err(|e| {
1003                if ops_language == Language::English {
1004                    format!("❌ Failed to read file: {}", e)
1005                } else {
1006                    format!("❌ 读取文件失败: {}", e)
1007                }
1008            })?;
1009
1010        let keypair = KeyManager::keypair_from_encrypted_json(&keystore_json, &password)
1011            .map_err(|e| {
1012                if ops_language == Language::English {
1013                    format!("❌ Failed to decrypt keystore: {}", e)
1014                } else {
1015                    format!("❌ 解密 keystore 失败: {}", e)
1016                }
1017            })?;
1018
1019        println!("\n{}", if ops_language == Language::English {
1020            "✅ Wallet unlocked successfully!"
1021        } else {
1022            "✅ 钱包解锁成功!"
1023        }.bright_green());
1024
1025        // Run synchronous operations menu
1026        show_operations_menu(&keypair, ops_language)
1027    }
1028
1029    #[cfg(not(feature = "solana-ops"))]
1030    {
1031        let _ = language; // Suppress unused variable warning
1032        Err("Solana operations require the 'solana-ops' feature".to_string())
1033    }
1034}
1035
1036/// PumpSwap 交互式卖出
1037#[cfg(feature = "sol-trade-sdk")]
1038pub fn pumpswap_sell_interactive(keypair: &Keypair, language: Language) -> Result<(), String> {
1039    println!("\n{}", "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━".bright_magenta());
1040    if language == Language::English {
1041        println!("  {}", "🔥 PumpSwap Sell Tokens".bright_magenta().bold());
1042    } else {
1043        println!("  {}", "🔥 PumpSwap 卖出代币".bright_magenta().bold());
1044    }
1045    println!("{}", "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━".bright_magenta());
1046
1047    println!("\n{}", if language == Language::English {
1048        "Current Wallet:"
1049    } else {
1050        "当前钱包:"
1051    }.bright_green());
1052    println!("  📍 {}", keypair.pubkey().to_string().bright_white());
1053
1054    // Step 1: 输入 RPC URL(可选)
1055    println!();
1056    let rpc_prompt = if language == Language::English {
1057        format!("RPC URL (default: {}): ", DEFAULT_RPC_URL)
1058    } else {
1059        format!("RPC URL (默认: {}): ", DEFAULT_RPC_URL)
1060    };
1061    let rpc_url = read_input(&rpc_prompt, DEFAULT_RPC_URL);
1062
1063    // Step 2: 询问是否使用 seed 优化
1064    println!();
1065    if language == Language::English {
1066        println!("{}", "🔧 Seed Optimization Configuration".bright_cyan());
1067        println!("   Seed optimization is used to create optimized ATA addresses");
1068        println!("   If your token ATA was created using the standard method, choose 'no'");
1069        println!("   If unsure, it's recommended to choose 'no'");
1070    } else {
1071        println!("{}", "🔧 Seed 优化配置".bright_cyan());
1072        println!("   Seed 优化用于创建优化的 ATA 地址,可以节省交易费用");
1073        println!("   如果你的代币 ATA 是通过标准方式创建的,请选择 'no'");
1074        println!("   如果不确定,建议选择 'yes'(默认)");
1075    }
1076
1077    print!("\n{} ", if language == Language::English {
1078        "❓ Enable Seed Optimization? (yes/no, default: yes):"
1079    } else {
1080        "❓ 启用 Seed 优化? (yes/no, 默认 yes):"
1081    }.yellow());
1082    io::stdout().flush().map_err(|e| e.to_string())?;
1083
1084    let mut seed_input = String::new();
1085    io::stdin().read_line(&mut seed_input).map_err(|e| e.to_string())?;
1086    let seed_input_trimmed = seed_input.trim().to_lowercase();
1087    // 默认为 yes:空输入或 yes/y 都启用,只有明确输入 no/n 才禁用
1088    let use_seed = seed_input_trimmed.is_empty() || seed_input_trimmed == "yes" || seed_input_trimmed == "y";
1089
1090    if use_seed {
1091        println!("{}", if language == Language::English {
1092            "✅ Seed optimization enabled"
1093        } else {
1094            "✅ 已启用 Seed 优化"
1095        }.green());
1096    } else {
1097        println!("{}", if language == Language::English {
1098            "✅ Using standard ATA"
1099        } else {
1100            "✅ 使用标准 ATA"
1101        }.green());
1102    }
1103
1104    // Step 3: 输入 token mint 地址(支持多个,用逗号或空格分割)
1105    println!();
1106    if language == Language::English {
1107        println!("{}", "💡 You can enter multiple mint addresses separated by commas or spaces".bright_cyan());
1108        println!("   Tokens will be sold in the order entered");
1109    } else {
1110        println!("{}", "💡 可以输入多个 Mint 地址,用逗号或空格分割".bright_cyan());
1111        println!("   将按输入顺序依次卖出");
1112    }
1113
1114    let mint_prompt = if language == Language::English {
1115        "\nToken Mint Address(es): "
1116    } else {
1117        "\n代币 Mint 地址: "
1118    };
1119    print!("{}", mint_prompt.yellow());
1120    io::stdout().flush().map_err(|e| e.to_string())?;
1121
1122    let mut mint_input = String::new();
1123    io::stdin().read_line(&mut mint_input).map_err(|e| e.to_string())?;
1124    let mint_input = mint_input.trim();
1125
1126    if mint_input.is_empty() {
1127        return Err(if language == Language::English {
1128            "Token mint address cannot be empty".to_string()
1129        } else {
1130            "代币 Mint 地址不能为空".to_string()
1131        });
1132    }
1133
1134    // 解析多个 mint 地址(支持逗号和空格分割)
1135    let mint_addresses: Vec<String> = mint_input
1136        .split(|c: char| c == ',' || c.is_whitespace())
1137        .map(|s| s.trim())
1138        .filter(|s| !s.is_empty())
1139        .map(|s| s.to_string())
1140        .collect();
1141
1142    if mint_addresses.is_empty() {
1143        return Err(if language == Language::English {
1144            "No valid mint addresses found".to_string()
1145        } else {
1146            "未找到有效的 Mint 地址".to_string()
1147        });
1148    }
1149
1150    // 验证所有 mint 地址格式
1151    for (idx, mint) in mint_addresses.iter().enumerate() {
1152        if let Err(e) = Pubkey::from_str(mint) {
1153            return Err(if language == Language::English {
1154                format!("Invalid mint address #{}: {} (error: {})", idx + 1, mint, e)
1155            } else {
1156                format!("无效的 Mint 地址 #{}: {} (错误: {})", idx + 1, mint, e)
1157            });
1158        }
1159    }
1160
1161    // 显示将要处理的 mint 地址
1162    println!();
1163    if language == Language::English {
1164        println!("{}", format!("📋 Found {} token(s) to sell:", mint_addresses.len()).bright_green());
1165    } else {
1166        println!("{}", format!("📋 找到 {} 个代币待卖出:", mint_addresses.len()).bright_green());
1167    }
1168    for (idx, mint) in mint_addresses.iter().enumerate() {
1169        println!("   {}. {}", idx + 1, mint.bright_white());
1170    }
1171
1172    // Step 4: 使用默认滑点 99%
1173    let slippage = 9900u64;
1174    println!();
1175    if language == Language::English {
1176        println!("📊 Slippage tolerance: {}%", slippage as f64 / 100.0);
1177    } else {
1178        println!("📊 滑点容忍度: {}%", slippage as f64 / 100.0);
1179    }
1180
1181    // Step 4.5: 批量卖出前统一确认
1182    let total_mints = mint_addresses.len();
1183    if total_mints > 1 {
1184        println!();
1185        if language == Language::English {
1186            println!("{}", format!("⚠️  You are about to sell {} tokens", total_mints).yellow().bold());
1187            println!("   All tokens will be sold automatically without individual confirmation");
1188        } else {
1189            println!("{}", format!("⚠️  您即将卖出 {} 个代币", total_mints).yellow().bold());
1190            println!("   所有代币将自动卖出,不会逐个确认");
1191        }
1192
1193        print!("\n{}", if language == Language::English {
1194            "❓ Confirm batch sell? (yes/no, default: yes): "
1195        } else {
1196            "❓ 确认批量卖出? (yes/no, 默认 yes): "
1197        }.yellow());
1198        io::stdout().flush().map_err(|e| e.to_string())?;
1199
1200        let mut confirm = String::new();
1201        io::stdin().read_line(&mut confirm).map_err(|e| e.to_string())?;
1202        let confirm_trimmed = confirm.trim().to_lowercase();
1203
1204        if confirm_trimmed == "no" || confirm_trimmed == "n" {
1205            return Err(if language == Language::English {
1206                "❌ Batch sell cancelled".to_string()
1207            } else {
1208                "❌ 批量卖出已取消".to_string()
1209            });
1210        }
1211    }
1212
1213    // Step 5: 循环处理每个 mint 地址
1214    // 批量卖出时跳过单个确认(skip_confirmation=true)
1215    let skip_confirmation = total_mints > 1;
1216    for (idx, mint) in mint_addresses.iter().enumerate() {
1217        println!();
1218        println!("{}", "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━".bright_magenta());
1219        if language == Language::English {
1220            println!("{}", format!("🚀 Processing token {}/{}", idx + 1, total_mints).bright_blue());
1221            println!("   Mint: {}", mint.bright_white());
1222        } else {
1223            println!("{}", format!("🚀 处理第 {}/{} 个代币", idx + 1, total_mints).bright_blue());
1224            println!("   Mint: {}", mint.bright_white());
1225        }
1226        println!("{}", "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━".bright_magenta());
1227
1228        // 使用当前 tokio runtime 执行异步操作
1229        let result = match tokio::runtime::Handle::try_current() {
1230            Ok(handle) => {
1231                // 在当前运行时中执行
1232                let keypair_b58 = bs58::encode(keypair.to_bytes()).into_string();
1233                let mint_clone = mint.to_string();
1234                let rpc_url_clone = rpc_url.clone();
1235
1236                std::thread::spawn(move || {
1237                    let keypair_clone = Keypair::from_base58_string(&keypair_b58);
1238                    handle.block_on(async move {
1239                        crate::solana_utils::pumpswap_sell::handle_pumpswap_sell_no_prompt(
1240                            &keypair_clone,
1241                            &mint_clone,
1242                            &rpc_url_clone,
1243                            slippage,
1244                            use_seed,
1245                            language,
1246                            skip_confirmation,  // 传入 skip_confirmation 参数
1247                        ).await
1248                    })
1249                })
1250                .join()
1251                .map_err(|_| "Thread panicked".to_string())?
1252            }
1253            Err(_) => {
1254                // 没有运行时,创建新的
1255                let rt = tokio::runtime::Runtime::new().map_err(|e| e.to_string())?;
1256                rt.block_on(async {
1257                    crate::solana_utils::pumpswap_sell::handle_pumpswap_sell_no_prompt(
1258                        keypair,
1259                        mint,
1260                        &rpc_url,
1261                        slippage,
1262                        use_seed,
1263                        language,
1264                        skip_confirmation,  // 传入 skip_confirmation 参数
1265                    ).await
1266                })
1267            }
1268        };
1269
1270        // 处理结果
1271        match result {
1272            Ok(_) => {
1273                if language == Language::English {
1274                    println!("\n{}", format!("✅ Token {}/{} sold successfully", idx + 1, total_mints).bright_green());
1275                } else {
1276                    println!("\n{}", format!("✅ 第 {}/{} 个代币卖出成功", idx + 1, total_mints).bright_green());
1277                }
1278            }
1279            Err(e) => {
1280                if language == Language::English {
1281                    println!("\n{}", format!("❌ Token {}/{} failed: {}", idx + 1, total_mints, e).bright_red());
1282                    println!("   Continuing with next token...");
1283                } else {
1284                    println!("\n{}", format!("❌ 第 {}/{} 个代币卖出失败: {}", idx + 1, total_mints, e).bright_red());
1285                    println!("   继续处理下一个代币...");
1286                }
1287            }
1288        }
1289
1290        // 如果不是最后一个,添加延迟
1291        if idx < total_mints - 1 {
1292            println!();
1293            if language == Language::English {
1294                println!("⏳ Waiting 2 seconds before next transaction...");
1295            } else {
1296                println!("⏳ 等待 2 秒后处理下一个交易...");
1297            }
1298            std::thread::sleep(std::time::Duration::from_secs(2));
1299        }
1300    }
1301
1302    // 所有交易完成
1303    println!();
1304    println!("{}", "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━".bright_magenta());
1305    if language == Language::English {
1306        println!("{}", "🎉 All transactions completed!".bright_green().bold());
1307    } else {
1308        println!("{}", "🎉 所有交易已完成!".bright_green().bold());
1309    }
1310    println!("{}", "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━".bright_magenta());
1311
1312    Ok(())
1313}
1314
1315#[cfg(not(feature = "sol-trade-sdk"))]
1316pub fn pumpswap_sell_interactive(_keypair: &Keypair, language: Language) -> Result<(), String> {
1317    Err(if language == Language::English {
1318        "PumpSwap sell requires 'sol-trade-sdk' feature. Please rebuild with:\ncargo build --release --features sol-trade-sdk".to_string()
1319    } else {
1320        "PumpSwap 卖出需要 'sol-trade-sdk' 功能。请使用以下命令重新编译:\ncargo build --release --features sol-trade-sdk".to_string()
1321    })
1322}
1323
1324/// Pump.fun 内盘(bonding curve)交互式卖出
1325#[cfg(feature = "sol-trade-sdk")]
1326pub fn pumpfun_sell_interactive(keypair: &Keypair, language: Language) -> Result<(), String> {
1327    println!("\n{}", "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━".bright_magenta());
1328    if language == Language::English {
1329        println!("  {}", "🔥 Pump.fun Bonding Curve Sell".bright_magenta().bold());
1330    } else {
1331        println!("  {}", "🔥 Pump.fun 内盘卖出代币".bright_magenta().bold());
1332    }
1333    println!("{}", "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━".bright_magenta());
1334
1335    println!("\n{}", if language == Language::English {
1336        "Current Wallet:"
1337    } else {
1338        "当前钱包:"
1339    }.bright_green());
1340    println!("  📍 {}", keypair.pubkey().to_string().bright_white());
1341
1342    let rpc_prompt = if language == Language::English {
1343        format!("RPC URL (default: {}): ", DEFAULT_RPC_URL)
1344    } else {
1345        format!("RPC URL (默认: {}): ", DEFAULT_RPC_URL)
1346    };
1347    let rpc_url = read_input(&rpc_prompt, DEFAULT_RPC_URL);
1348
1349    println!();
1350    if language == Language::English {
1351        println!("{}", "🔧 Seed Optimization Configuration".bright_cyan());
1352        println!("   If your token ATA was created using the standard method, choose 'no'");
1353        println!("   If unsure, it's recommended to choose 'no'");
1354    } else {
1355        println!("{}", "🔧 Seed 优化配置".bright_cyan());
1356        println!("   如果你的代币 ATA 是通过标准方式创建的,请选择 'no'");
1357        println!("   如果不确定,建议选择 'yes'(默认)");
1358    }
1359
1360    print!("\n{} ", if language == Language::English {
1361        "❓ Enable Seed Optimization? (yes/no, default: yes):"
1362    } else {
1363        "❓ 启用 Seed 优化? (yes/no, 默认 yes):"
1364    }.yellow());
1365    io::stdout().flush().map_err(|e| e.to_string())?;
1366
1367    let mut seed_input = String::new();
1368    io::stdin().read_line(&mut seed_input).map_err(|e| e.to_string())?;
1369    let seed_input_trimmed = seed_input.trim().to_lowercase();
1370    let use_seed = seed_input_trimmed.is_empty() || seed_input_trimmed == "yes" || seed_input_trimmed == "y";
1371
1372    if language == Language::English {
1373        println!("{}", "💡 You can enter multiple mint addresses separated by commas or spaces".bright_cyan());
1374    } else {
1375        println!("{}", "💡 可以输入多个 Mint 地址,用逗号或空格分割".bright_cyan());
1376    }
1377
1378    let mint_prompt = if language == Language::English {
1379        "\nToken Mint Address(es): "
1380    } else {
1381        "\n代币 Mint 地址: "
1382    };
1383    print!("{}", mint_prompt.yellow());
1384    io::stdout().flush().map_err(|e| e.to_string())?;
1385
1386    let mut mint_input = String::new();
1387    io::stdin().read_line(&mut mint_input).map_err(|e| e.to_string())?;
1388    let mint_input = mint_input.trim();
1389
1390    if mint_input.is_empty() {
1391        return Err(if language == Language::English {
1392            "Token mint address cannot be empty".to_string()
1393        } else {
1394            "代币 Mint 地址不能为空".to_string()
1395        });
1396    }
1397
1398    let mint_addresses: Vec<String> = mint_input
1399        .split(|c: char| c == ',' || c.is_whitespace())
1400        .map(|s| s.trim())
1401        .filter(|s| !s.is_empty())
1402        .map(|s| s.to_string())
1403        .collect();
1404
1405    if mint_addresses.is_empty() {
1406        return Err(if language == Language::English {
1407            "No valid mint addresses found".to_string()
1408        } else {
1409            "未找到有效的 Mint 地址".to_string()
1410        });
1411    }
1412
1413    for (idx, mint) in mint_addresses.iter().enumerate() {
1414        if let Err(e) = Pubkey::from_str(mint) {
1415            return Err(if language == Language::English {
1416                format!("Invalid mint address #{}: {} (error: {})", idx + 1, mint, e)
1417            } else {
1418                format!("无效的 Mint 地址 #{}: {} (错误: {})", idx + 1, mint, e)
1419            });
1420        }
1421    }
1422
1423    println!();
1424    if language == Language::English {
1425        println!("{}", format!("📋 Found {} token(s) to sell:", mint_addresses.len()).bright_green());
1426    } else {
1427        println!("{}", format!("📋 找到 {} 个代币待卖出:", mint_addresses.len()).bright_green());
1428    }
1429    for (idx, mint) in mint_addresses.iter().enumerate() {
1430        println!("   {}. {}", idx + 1, mint.bright_white());
1431    }
1432
1433    let slippage = 9900u64;
1434    println!();
1435    if language == Language::English {
1436        println!("📊 Slippage tolerance: {}%", slippage as f64 / 100.0);
1437    } else {
1438        println!("📊 滑点容忍度: {}%", slippage as f64 / 100.0);
1439    }
1440
1441    let total_mints = mint_addresses.len();
1442    if total_mints > 1 {
1443        println!();
1444        if language == Language::English {
1445            println!("{}", format!("⚠️  You are about to sell {} tokens", total_mints).yellow().bold());
1446        } else {
1447            println!("{}", format!("⚠️  您即将卖出 {} 个代币", total_mints).yellow().bold());
1448        }
1449
1450        print!("\n{}", if language == Language::English {
1451            "❓ Confirm batch sell? (yes/no, default: yes): "
1452        } else {
1453            "❓ 确认批量卖出? (yes/no, 默认 yes): "
1454        }.yellow());
1455        io::stdout().flush().map_err(|e| e.to_string())?;
1456
1457        let mut confirm = String::new();
1458        io::stdin().read_line(&mut confirm).map_err(|e| e.to_string())?;
1459        let confirm_trimmed = confirm.trim().to_lowercase();
1460
1461        if confirm_trimmed == "no" || confirm_trimmed == "n" {
1462            return Err(if language == Language::English {
1463                "❌ Batch sell cancelled".to_string()
1464            } else {
1465                "❌ 批量卖出已取消".to_string()
1466            });
1467        }
1468    }
1469
1470    let skip_confirmation = total_mints > 1;
1471    for (idx, mint) in mint_addresses.iter().enumerate() {
1472        println!();
1473        println!("{}", "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━".bright_magenta());
1474        if language == Language::English {
1475            println!("{}", format!("🚀 Processing token {}/{}", idx + 1, total_mints).bright_blue());
1476            println!("   Mint: {}", mint.bright_white());
1477        } else {
1478            println!("{}", format!("🚀 处理第 {}/{} 个代币", idx + 1, total_mints).bright_blue());
1479            println!("   Mint: {}", mint.bright_white());
1480        }
1481        println!("{}", "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━".bright_magenta());
1482
1483        let result = match tokio::runtime::Handle::try_current() {
1484            Ok(handle) => {
1485                let keypair_b58 = bs58::encode(keypair.to_bytes()).into_string();
1486                let mint_clone = mint.to_string();
1487                let rpc_url_clone = rpc_url.clone();
1488
1489                std::thread::spawn(move || {
1490                    let keypair_clone = Keypair::from_base58_string(&keypair_b58);
1491                    handle.block_on(async move {
1492                        crate::solana_utils::pumpfun_sell::handle_pumpfun_sell_no_prompt(
1493                            &keypair_clone,
1494                            &mint_clone,
1495                            &rpc_url_clone,
1496                            slippage,
1497                            use_seed,
1498                            language,
1499                            skip_confirmation,
1500                        ).await
1501                    })
1502                })
1503                .join()
1504                .map_err(|_| "Thread panicked".to_string())?
1505            }
1506            Err(_) => {
1507                let rt = tokio::runtime::Runtime::new().map_err(|e| e.to_string())?;
1508                rt.block_on(async {
1509                    crate::solana_utils::pumpfun_sell::handle_pumpfun_sell_no_prompt(
1510                        keypair,
1511                        mint,
1512                        &rpc_url,
1513                        slippage,
1514                        use_seed,
1515                        language,
1516                        skip_confirmation,
1517                    ).await
1518                })
1519            }
1520        };
1521
1522        match result {
1523            Ok(_) => {
1524                if language == Language::English {
1525                    println!("\n{}", format!("✅ Token {}/{} sold successfully", idx + 1, total_mints).bright_green());
1526                } else {
1527                    println!("\n{}", format!("✅ 第 {}/{} 个代币卖出成功", idx + 1, total_mints).bright_green());
1528                }
1529            }
1530            Err(e) => {
1531                if language == Language::English {
1532                    println!("\n{}", format!("❌ Token {}/{} failed: {}", idx + 1, total_mints, e).bright_red());
1533                } else {
1534                    println!("\n{}", format!("❌ 第 {}/{} 个代币卖出失败: {}", idx + 1, total_mints, e).bright_red());
1535                }
1536            }
1537        }
1538
1539        if idx < total_mints - 1 {
1540            println!();
1541            std::thread::sleep(std::time::Duration::from_secs(2));
1542        }
1543    }
1544
1545    println!();
1546    println!("{}", "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━".bright_magenta());
1547    if language == Language::English {
1548        println!("{}", "🎉 All transactions completed!".bright_green().bold());
1549    } else {
1550        println!("{}", "🎉 所有交易已完成!".bright_green().bold());
1551    }
1552    println!("{}", "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━".bright_magenta());
1553
1554    Ok(())
1555}
1556
1557/// Pump (Pump.fun) 返现:查询余额 → 显示 → 确认 → 领取(原生 SOL)
1558#[cfg(feature = "sol-trade-sdk")]
1559pub fn pumpfun_cashback_interactive(keypair: &Keypair, language: Language) -> Result<(), String> {
1560    use std::sync::Arc;
1561    use sol_trade_sdk::{common::TradeConfig, SolanaTrade};
1562    use solana_commitment_config::CommitmentConfig;
1563
1564    println!("\n{}", "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━".bright_magenta());
1565    if language == Language::English {
1566        println!("  {}", "💰 Pump (Pump.fun) Cashback – View & Claim".bright_magenta().bold());
1567        println!("  {}", "   Cashback is native SOL from trading on Pump.fun.".bright_white());
1568    } else {
1569        println!("  {}", "💰 Pump (Pump.fun) 返现 – 查看与领取".bright_magenta().bold());
1570        println!("  {}", "   返现为在 Pump.fun 交易累积的原生 SOL。".bright_white());
1571    }
1572    println!("{}", "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━".bright_magenta());
1573
1574    println!("\n{}", if language == Language::English {
1575        "Current Wallet:"
1576    } else {
1577        "当前钱包:"
1578    }.bright_green());
1579    println!("  📍 {}", keypair.pubkey().to_string().bright_white());
1580
1581    let rpc_prompt = if language == Language::English {
1582        format!("RPC URL (default: {}): ", DEFAULT_RPC_URL)
1583    } else {
1584        format!("RPC URL (默认: {}): ", DEFAULT_RPC_URL)
1585    };
1586    let rpc_url = read_input(&rpc_prompt, DEFAULT_RPC_URL);
1587
1588    // Step 1: 查询 UserVolumeAccumulator PDA 余额
1589    if language == Language::English {
1590        println!("\n{}", "🔍 Querying cashback balance...".bright_cyan());
1591    } else {
1592        println!("\n{}", "🔍 正在查询返现余额...".bright_cyan());
1593    }
1594
1595    let pda = sol_trade_sdk::instruction::utils::pumpfun::get_user_volume_accumulator_pda(&keypair.pubkey())
1596        .ok_or_else(|| if language == Language::English {
1597            "Failed to derive UserVolumeAccumulator PDA".to_string()
1598        } else {
1599            "无法派生 UserVolumeAccumulator PDA 地址".to_string()
1600        })?;
1601
1602    let rpc_client = RpcClient::new(rpc_url.clone());
1603    let pda_balance_lamports = rpc_client.get_balance(&pda).unwrap_or(0);
1604    let rent_exempt_min: u64 = 890_880;
1605    let claimable_lamports = pda_balance_lamports.saturating_sub(rent_exempt_min);
1606    let claimable_sol = claimable_lamports as f64 / 1_000_000_000.0;
1607
1608    // Step 2: 显示余额
1609    println!("\n{}", if language == Language::English {
1610        "📊 Cashback Info:"
1611    } else {
1612        "📊 返现信息:"
1613    }.bright_yellow());
1614    println!("  PDA: {}", pda.to_string().bright_white());
1615    if language == Language::English {
1616        println!("  PDA Balance: {} lamports ({:.9} SOL)", pda_balance_lamports, pda_balance_lamports as f64 / 1e9);
1617        println!("  Claimable:   {} lamports ({:.9} SOL)", claimable_lamports, claimable_sol);
1618    } else {
1619        println!("  PDA 余额:    {} lamports ({:.9} SOL)", pda_balance_lamports, pda_balance_lamports as f64 / 1e9);
1620        println!("  可领取:      {} lamports ({:.9} SOL)", claimable_lamports, claimable_sol);
1621    }
1622
1623    if claimable_lamports == 0 {
1624        if language == Language::English {
1625            println!("\n{}", "ℹ️  No cashback available to claim.".bright_yellow());
1626        } else {
1627            println!("\n{}", "ℹ️  暂无可领取的返现。".bright_yellow());
1628        }
1629        return Ok(());
1630    }
1631
1632    // Step 3: 确认是否领取
1633    let confirm_prompt = if language == Language::English {
1634        format!("\nClaim {:.9} SOL? (yes/no) [no]: ", claimable_sol)
1635    } else {
1636        format!("\n领取 {:.9} SOL? (yes/no) [no]: ", claimable_sol)
1637    };
1638    let confirm = read_input(&confirm_prompt, "no");
1639
1640    if confirm.to_lowercase() != "yes" {
1641        let msg = if language == Language::English {
1642            "❌ Claim cancelled"
1643        } else {
1644            "❌ 已取消领取"
1645        };
1646        println!("\n{}", msg.red());
1647        return Ok(());
1648    }
1649
1650    // Step 4: 执行领取
1651    if language == Language::English {
1652        println!("\n{}", "🚀 Claiming...".bright_cyan());
1653    } else {
1654        println!("\n{}", "🚀 正在领取...".bright_cyan());
1655    }
1656
1657    let sig = match tokio::runtime::Handle::try_current() {
1658        Ok(handle) => {
1659            let keypair_b58 = bs58::encode(keypair.to_bytes()).into_string();
1660            let rpc_url_clone = rpc_url.clone();
1661            std::thread::spawn(move || {
1662                let kp = Keypair::from_base58_string(&keypair_b58);
1663                let payer = Arc::new(kp);
1664                let config = TradeConfig {
1665                    rpc_url: rpc_url_clone.clone(),
1666                    swqos_configs: vec![sol_trade_sdk::swqos::SwqosConfig::Default(rpc_url_clone)],
1667                    commitment: CommitmentConfig::confirmed(),
1668                    create_wsol_ata_on_startup: false,
1669                    use_seed_optimize: false,
1670                    check_min_tip: false,
1671                    log_enabled: false,
1672                    use_core_affinity: false,
1673                };
1674                handle.block_on(async move {
1675                    let client = SolanaTrade::new(payer, config).await;
1676                    client.claim_cashback_pumpfun().await.map_err(|e| e.to_string())
1677                })
1678            })
1679            .join()
1680            .map_err(|_| "Thread panicked".to_string())??
1681        }
1682        Err(_) => {
1683            let payer = Arc::new(keypair.insecure_clone());
1684            let config = TradeConfig {
1685                rpc_url: rpc_url.clone(),
1686                swqos_configs: vec![sol_trade_sdk::swqos::SwqosConfig::Default(rpc_url)],
1687                commitment: CommitmentConfig::confirmed(),
1688                create_wsol_ata_on_startup: false,
1689                use_seed_optimize: false,
1690                check_min_tip: false,
1691                log_enabled: false,
1692                use_core_affinity: false,
1693            };
1694            let rt = tokio::runtime::Runtime::new().map_err(|e| e.to_string())?;
1695            let client = rt.block_on(SolanaTrade::new(payer, config));
1696            rt.block_on(client.claim_cashback_pumpfun()).map_err(|e| e.to_string())?
1697        }
1698    };
1699
1700    if language == Language::English {
1701        println!("{}", "✅ Claim successful!".bright_green().bold());
1702        println!("Signature: {}", sig.yellow());
1703        println!("Explorer: https://solscan.io/tx/{}", sig);
1704    } else {
1705        println!("{}", "✅ 领取成功!".bright_green().bold());
1706        println!("签名: {}", sig.yellow());
1707        println!("浏览器: https://solscan.io/tx/{}", sig);
1708    }
1709    Ok(())
1710}
1711
1712/// PumpSwap 返现:查询余额 → 显示 → 确认 → 领取(WSOL)
1713#[cfg(feature = "sol-trade-sdk")]
1714pub fn pumpswap_cashback_interactive(keypair: &Keypair, language: Language) -> Result<(), String> {
1715    use std::sync::Arc;
1716    use sol_trade_sdk::{common::TradeConfig, SolanaTrade};
1717    use solana_commitment_config::CommitmentConfig;
1718
1719    println!("\n{}", "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━".bright_magenta());
1720    if language == Language::English {
1721        println!("  {}", "💰 PumpSwap Cashback – View & Claim".bright_magenta().bold());
1722        println!("  {}", "   Cashback is WSOL from trading on PumpSwap.".bright_white());
1723    } else {
1724        println!("  {}", "💰 PumpSwap 返现 – 查看与领取".bright_magenta().bold());
1725        println!("  {}", "   返现为在 PumpSwap 交易累积的 WSOL。".bright_white());
1726    }
1727    println!("{}", "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━".bright_magenta());
1728
1729    println!("\n{}", if language == Language::English {
1730        "Current Wallet:"
1731    } else {
1732        "当前钱包:"
1733    }.bright_green());
1734    println!("  📍 {}", keypair.pubkey().to_string().bright_white());
1735
1736    let rpc_prompt = if language == Language::English {
1737        format!("RPC URL (default: {}): ", DEFAULT_RPC_URL)
1738    } else {
1739        format!("RPC URL (默认: {}): ", DEFAULT_RPC_URL)
1740    };
1741    let rpc_url = read_input(&rpc_prompt, DEFAULT_RPC_URL);
1742
1743    // Step 1: 查询 PumpSwap UserVolumeAccumulator 的 WSOL ATA 余额
1744    if language == Language::English {
1745        println!("\n{}", "🔍 Querying cashback balance...".bright_cyan());
1746    } else {
1747        println!("\n{}", "🔍 正在查询返现余额...".bright_cyan());
1748    }
1749
1750    let wsol_ata = sol_trade_sdk::instruction::utils::pumpswap::get_user_volume_accumulator_wsol_ata(&keypair.pubkey())
1751        .ok_or_else(|| if language == Language::English {
1752            "Failed to derive PumpSwap UserVolumeAccumulator WSOL ATA".to_string()
1753        } else {
1754            "无法派生 PumpSwap UserVolumeAccumulator WSOL ATA 地址".to_string()
1755        })?;
1756
1757    let rpc_client = RpcClient::new(rpc_url.clone());
1758    let claimable_lamports = match rpc_client.get_token_account_balance(&wsol_ata) {
1759        Ok(balance) => balance.amount.parse::<u64>().unwrap_or(0),
1760        Err(_) => 0,
1761    };
1762    let claimable_sol = claimable_lamports as f64 / 1_000_000_000.0;
1763
1764    // Step 2: 显示余额
1765    println!("\n{}", if language == Language::English {
1766        "📊 Cashback Info:"
1767    } else {
1768        "📊 返现信息:"
1769    }.bright_yellow());
1770    println!("  WSOL ATA: {}", wsol_ata.to_string().bright_white());
1771    if language == Language::English {
1772        println!("  Claimable: {} lamports ({:.9} SOL)", claimable_lamports, claimable_sol);
1773    } else {
1774        println!("  可领取:   {} lamports ({:.9} SOL)", claimable_lamports, claimable_sol);
1775    }
1776
1777    if claimable_lamports == 0 {
1778        if language == Language::English {
1779            println!("\n{}", "ℹ️  No cashback available to claim.".bright_yellow());
1780        } else {
1781            println!("\n{}", "ℹ️  暂无可领取的返现。".bright_yellow());
1782        }
1783        return Ok(());
1784    }
1785
1786    // Step 3: 确认是否领取
1787    let confirm_prompt = if language == Language::English {
1788        format!("\nClaim {:.9} SOL (WSOL)? (yes/no) [no]: ", claimable_sol)
1789    } else {
1790        format!("\n领取 {:.9} SOL (WSOL)? (yes/no) [no]: ", claimable_sol)
1791    };
1792    let confirm = read_input(&confirm_prompt, "no");
1793
1794    if confirm.to_lowercase() != "yes" {
1795        let msg = if language == Language::English {
1796            "❌ Claim cancelled"
1797        } else {
1798            "❌ 已取消领取"
1799        };
1800        println!("\n{}", msg.red());
1801        return Ok(());
1802    }
1803
1804    // Step 4: 执行领取
1805    if language == Language::English {
1806        println!("\n{}", "🚀 Claiming...".bright_cyan());
1807    } else {
1808        println!("\n{}", "🚀 正在领取...".bright_cyan());
1809    }
1810
1811    let sig = match tokio::runtime::Handle::try_current() {
1812        Ok(handle) => {
1813            let keypair_b58 = bs58::encode(keypair.to_bytes()).into_string();
1814            let rpc_url_clone = rpc_url.clone();
1815            std::thread::spawn(move || {
1816                let kp = Keypair::from_base58_string(&keypair_b58);
1817                let payer = Arc::new(kp);
1818                let config = TradeConfig {
1819                    rpc_url: rpc_url_clone.clone(),
1820                    swqos_configs: vec![sol_trade_sdk::swqos::SwqosConfig::Default(rpc_url_clone)],
1821                    commitment: CommitmentConfig::confirmed(),
1822                    create_wsol_ata_on_startup: false,
1823                    use_seed_optimize: false,
1824                    check_min_tip: false,
1825                    log_enabled: false,
1826                    use_core_affinity: false,
1827                };
1828                handle.block_on(async move {
1829                    let client = SolanaTrade::new(payer, config).await;
1830                    client.claim_cashback_pumpswap().await.map_err(|e| e.to_string())
1831                })
1832            })
1833            .join()
1834            .map_err(|_| "Thread panicked".to_string())??
1835        }
1836        Err(_) => {
1837            let payer = Arc::new(keypair.insecure_clone());
1838            let config = TradeConfig {
1839                rpc_url: rpc_url.clone(),
1840                swqos_configs: vec![sol_trade_sdk::swqos::SwqosConfig::Default(rpc_url)],
1841                commitment: CommitmentConfig::confirmed(),
1842                create_wsol_ata_on_startup: false,
1843                use_seed_optimize: false,
1844                check_min_tip: false,
1845                log_enabled: false,
1846                use_core_affinity: false,
1847            };
1848            let rt = tokio::runtime::Runtime::new().map_err(|e| e.to_string())?;
1849            let client = rt.block_on(SolanaTrade::new(payer, config));
1850            rt.block_on(client.claim_cashback_pumpswap()).map_err(|e| e.to_string())?
1851        }
1852    };
1853
1854    if language == Language::English {
1855        println!("{}", "✅ Claim successful!".bright_green().bold());
1856        println!("Signature: {}", sig.yellow());
1857        println!("Explorer: https://solscan.io/tx/{}", sig);
1858    } else {
1859        println!("{}", "✅ 领取成功!".bright_green().bold());
1860        println!("签名: {}", sig.yellow());
1861        println!("浏览器: https://solscan.io/tx/{}", sig);
1862    }
1863    Ok(())
1864}
1865
1866#[cfg(not(feature = "sol-trade-sdk"))]
1867pub fn pumpfun_sell_interactive(_keypair: &Keypair, language: Language) -> Result<(), String> {
1868    Err(if language == Language::English {
1869        "Pump.fun sell requires 'sol-trade-sdk' feature. Please rebuild with:\ncargo build --release --features sol-trade-sdk".to_string()
1870    } else {
1871        "Pump.fun 内盘卖出需要 'sol-trade-sdk' 功能。请使用以下命令重新编译:\ncargo build --release --features sol-trade-sdk".to_string()
1872    })
1873}
1874
1875#[cfg(not(feature = "sol-trade-sdk"))]
1876pub fn pumpfun_cashback_interactive(_keypair: &Keypair, language: Language) -> Result<(), String> {
1877    Err(if language == Language::English {
1878        "Pump cashback requires 'sol-trade-sdk' feature.".to_string()
1879    } else {
1880        "Pump 返现需要 'sol-trade-sdk' 功能。".to_string()
1881    })
1882}
1883
1884#[cfg(not(feature = "sol-trade-sdk"))]
1885pub fn pumpswap_cashback_interactive(_keypair: &Keypair, language: Language) -> Result<(), String> {
1886    Err(if language == Language::English {
1887        "PumpSwap cashback requires 'sol-trade-sdk' feature.".to_string()
1888    } else {
1889        "PumpSwap 返现需要 'sol-trade-sdk' 功能。".to_string()
1890    })
1891}