Skip to main content

fnzero_safe/
operations.rs

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