Skip to main content

sol_safekey/
interactive.rs

1//! Interactive Menu Module
2//!
3//! Provides a simple interactive interface - no need to memorize commands
4//! 提供简单的交互式界面 - 无需记住命令
5
6use std::io::{self, Write};
7use colored::*;
8use solana_sdk::signature::Keypair;
9use solana_sdk::signer::Signer;
10
11use crate::KeyManager;
12
13/// Search for a keystore file recursively in subdirectories (max 3 levels deep).
14/// Returns the first match as a relative path, or None if not found.
15/// Skips directories named "dev" and prefers "prod" paths.
16fn search_keystore_in_subdirs(filename: &str) -> Option<String> {
17    fn search(dir: &std::path::Path, filename: &str, depth: usize, max_depth: usize, results: &mut Vec<String>) {
18        if depth > max_depth {
19            return;
20        }
21        if let Ok(entries) = std::fs::read_dir(dir) {
22            for entry in entries.flatten() {
23                let path = entry.path();
24                if path.is_dir() {
25                    let name = path.file_name().unwrap_or_default().to_string_lossy();
26                    // Skip hidden dirs, common large dirs, and "dev" dirs
27                    if name.starts_with('.') || name == "target" || name == "node_modules" || name == "dev" {
28                        continue;
29                    }
30                    search(&path, filename, depth + 1, max_depth, results);
31                } else if path.file_name().unwrap_or_default() == filename {
32                    results.push(path.to_string_lossy().to_string());
33                }
34            }
35        }
36    }
37    let mut results = Vec::new();
38    search(std::path::Path::new("."), filename, 0, 3, &mut results);
39    // Prefer prod path
40    results.into_iter().find(|p| p.contains("prod")).or_else(|| {
41        // Fallback: return first non-dev result (shouldn't happen since we skip dev dirs)
42        let mut results = Vec::new();
43        search(std::path::Path::new("."), filename, 0, 3, &mut results);
44        results.into_iter().next()
45    })
46}
47
48/// Language selection
49#[derive(Clone, Copy, PartialEq)]
50pub enum Language {
51    English,
52    Chinese,
53}
54
55/// Text strings for bilingual UI
56struct Texts {
57    // Main menu
58    title: &'static str,
59    core_functions: &'static str,
60    create_plain: &'static str,
61    create_encrypted: &'static str,
62    decrypt: &'static str,
63    exit: &'static str,
64    select_option: &'static str,
65    goodbye: &'static str,
66    invalid_option: &'static str,
67    continue_use: &'static str,
68
69    // Plain key creation
70    create_plain_title: &'static str,
71    keypair_generated: &'static str,
72    public_key: &'static str,
73    private_key: &'static str,
74    output_method: &'static str,
75    display_only: &'static str,
76    save_to_file: &'static str,
77    select: &'static str,
78    file_path: &'static str,
79    file_saved: &'static str,
80    security_warning: &'static str,
81    plaintext_warning: &'static str,
82    save_securely: &'static str,
83    dont_share: &'static str,
84    recommend_encrypted: &'static str,
85
86    // Encrypted key creation
87    create_encrypted_title: &'static str,
88    choose_method: &'static str,
89    generate_new: &'static str,
90    import_existing: &'static str,
91    generating: &'static str,
92    enter_private_key: &'static str,
93    private_key_empty: &'static str,
94    keypair_ready: &'static str,
95    keystore_recommended: &'static str,
96    show_encrypted_string: &'static str,
97    keystore_created: &'static str,
98    private_key_encrypted: &'static str,
99    important_note: &'static str,
100    keep_safe: &'static str,
101    lost_password_warning: &'static str,
102    backup_recommended: &'static str,
103    encrypted_private_key: &'static str,
104    keep_safe_both: &'static str,
105
106    // Key decryption
107    decrypt_title: &'static str,
108    input_method: &'static str,
109    from_keystore: &'static str,
110    from_encrypted_string: &'static str,
111    encrypted_key: &'static str,
112    enter_password: &'static str,
113    decrypt_success: &'static str,
114    file_not_exist: &'static str,
115    dont_share_warning: &'static str,
116    delete_plaintext: &'static str,
117    use_encryption: &'static str,
118
119    // Password
120    set_password: &'static str,
121    new_password: &'static str,
122    confirm_password: &'static str,
123    password_empty: &'static str,
124    password_min_length: &'static str,
125    password_mismatch: &'static str,
126    password_set: &'static str,
127
128    // Errors
129    invalid_choice: &'static str,
130    write_failed: &'static str,
131}
132
133impl Texts {
134    fn chinese() -> Self {
135        Self {
136            title: "  Sol-SafeKey - Solana 密钥管理工具",
137            core_functions: "核心功能 (只需3个操作):",
138            create_plain: "  {}  创建明文私钥",
139            create_encrypted: "  {}  创建加密私钥(bot)",
140            decrypt: "  {}  解密私钥",
141            exit: "  {}  退出",
142            select_option: "请输入选项 [0-14]: ",
143            goodbye: "👋 再见!",
144            invalid_option: "❌ 无效选项,请重新选择",
145            continue_use: "是否继续使用? [Y/n]: ",
146
147            create_plain_title: "  创建明文私钥",
148            keypair_generated: "✅ 密钥对生成成功!",
149            public_key: "公钥地址:",
150            private_key: "私钥:",
151            output_method: "输出方式:",
152            display_only: "  1. 仅显示 (当前已显示)",
153            save_to_file: "  2. 保存到文件",
154            select: "请选择 [1/2]: ",
155            file_path: "文件路径 (默认: keypair.json): ",
156            file_saved: "✅ 已保存到文件",
157            security_warning: "⚠️  安全警告:",
158            plaintext_warning: "  • 明文私钥非常不安全",
159            save_securely: "  • 请立即保存到安全位置",
160            dont_share: "  • 不要分享给任何人",
161            recommend_encrypted: "  • 建议使用 '创建加密私钥' 功能",
162
163            create_encrypted_title: "  创建加密私钥",
164            choose_method: "选择方式:",
165            generate_new: "  1. 生成新的密钥对并加密",
166            import_existing: "  2. 导入现有私钥并加密",
167            generating: "🎲 生成新的密钥对...",
168            enter_private_key: "请输入私钥 (base58 格式): ",
169            private_key_empty: "私钥不能为空",
170            keypair_ready: "✅ 密钥对准备完成",
171            keystore_recommended: "  1. 保存为 Keystore 文件 (推荐)",
172            show_encrypted_string: "  2. 显示加密字符串",
173            keystore_created: "  ✅ Keystore 创建成功!",
174            private_key_encrypted: "🔒 私钥已加密保存",
175            important_note: "⚠️  重要提示:",
176            keep_safe: "  • 请妥善保管 Keystore 文件和密码",
177            lost_password_warning: "  • 丢失密码将无法恢复钱包",
178            backup_recommended: "  • 建议备份到安全位置",
179            encrypted_private_key: "加密后的私钥:",
180            keep_safe_both: "⚠️  提示: 请妥善保管加密私钥和密码",
181
182            decrypt_title: "  解密私钥",
183            input_method: "输入方式:",
184            from_keystore: "  1. 从 Keystore 文件读取",
185            from_encrypted_string: "  2. 输入加密字符串",
186            encrypted_key: "加密的私钥: ",
187            enter_password: "请输入密码: ",
188            decrypt_success: "  ✅ 解密成功!",
189            file_not_exist: "文件不存在: {}",
190            dont_share_warning: "  • 请勿分享私钥给任何人",
191            delete_plaintext: "  • 使用完毕后请立即删除明文私钥文件",
192            use_encryption: "  • 建议使用加密方式保存",
193
194            set_password: "设置加密密码 (至少 10 个字符):",
195            new_password: "新密码: ",
196            confirm_password: "确认密码: ",
197            password_empty: "密码不能为空",
198            password_min_length: "密码长度至少 10 个字符",
199            password_mismatch: "两次密码不一致",
200            password_set: "✅ 密码设置成功",
201
202            invalid_choice: "无效选项",
203            write_failed: "写入文件失败: {}",
204        }
205    }
206
207    fn english() -> Self {
208        Self {
209            title: "  Sol-SafeKey - Solana Key Management Tool",
210            core_functions: "Core Functions (3 operations):",
211            create_plain: "  {}  Create Plain Private Key",
212            create_encrypted: "  {}  Create Encrypted Private Key (Bot)",
213            decrypt: "  {}  Decrypt Private Key",
214            exit: "  {}  Exit",
215            select_option: "Select option [0-14]: ",
216            goodbye: "👋 Goodbye!",
217            invalid_option: "❌ Invalid option, please try again",
218            continue_use: "Continue? [Y/n]: ",
219
220            create_plain_title: "  Create Plain Private Key",
221            keypair_generated: "✅ Keypair generated successfully!",
222            public_key: "Public Key:",
223            private_key: "Private Key:",
224            output_method: "Output Method:",
225            display_only: "  1. Display Only (already shown)",
226            save_to_file: "  2. Save to File",
227            select: "Select [1/2]: ",
228            file_path: "File path (default: keypair.json): ",
229            file_saved: "✅ Saved to file",
230            security_warning: "⚠️  Security Warning:",
231            plaintext_warning: "  • Plaintext private key is very insecure",
232            save_securely: "  • Save to a secure location immediately",
233            dont_share: "  • Never share with anyone",
234            recommend_encrypted: "  • Consider using 'Create Encrypted Private Key'",
235
236            create_encrypted_title: "  Create Encrypted Private Key",
237            choose_method: "Choose Method:",
238            generate_new: "  1. Generate new keypair and encrypt",
239            import_existing: "  2. Import existing private key and encrypt",
240            generating: "🎲 Generating new keypair...",
241            enter_private_key: "Enter private key (base58 format): ",
242            private_key_empty: "Private key cannot be empty",
243            keypair_ready: "✅ Keypair ready",
244            keystore_recommended: "  1. Save as Keystore file (Recommended)",
245            show_encrypted_string: "  2. Show encrypted string",
246            keystore_created: "  ✅ Keystore created successfully!",
247            private_key_encrypted: "🔒 Private key encrypted and saved",
248            important_note: "⚠️  Important:",
249            keep_safe: "  • Keep Keystore file and password safe",
250            lost_password_warning: "  • Lost password = lost wallet",
251            backup_recommended: "  • Backup to a secure location",
252            encrypted_private_key: "Encrypted Private Key:",
253            keep_safe_both: "⚠️  Note: Keep encrypted key and password safe",
254
255            decrypt_title: "  Decrypt Private Key",
256            input_method: "Input Method:",
257            from_keystore: "  1. From Keystore file",
258            from_encrypted_string: "  2. Enter encrypted string",
259            encrypted_key: "Encrypted key: ",
260            enter_password: "Enter password: ",
261            decrypt_success: "  ✅ Decryption successful!",
262            file_not_exist: "File not found: {}",
263            dont_share_warning: "  • Never share private key with anyone",
264            delete_plaintext: "  • Delete plaintext key file after use",
265            use_encryption: "  • Consider using encryption for storage",
266
267            set_password: "Set encryption password (minimum 10 characters):",
268            new_password: "New password: ",
269            confirm_password: "Confirm password: ",
270            password_empty: "Password cannot be empty",
271            password_min_length: "Password must be at least 10 characters",
272            password_mismatch: "Passwords do not match",
273            password_set: "✅ Password set successfully",
274
275            invalid_choice: "Invalid choice",
276            write_failed: "Write failed: {}",
277        }
278    }
279}
280
281/// 选择语言
282fn select_language() -> Result<Language, String> {
283    println!("\n{}", "=".repeat(50).cyan());
284    println!("{}", "  Language / 语言选择".cyan().bold());
285    println!("{}", "=".repeat(50).cyan());
286    println!();
287    println!("  {}  English", "1.".green().bold());
288    println!("  {}  中文", "2.".green().bold());
289    println!();
290    print!("Select / 选择 [1/2]: ");
291    io::stdout().flush().map_err(|e| e.to_string())?;
292
293    let mut choice = String::new();
294    io::stdin().read_line(&mut choice).map_err(|e| e.to_string())?;
295    let choice = choice.trim();
296
297    match choice {
298        "1" => Ok(Language::English),
299        "2" => Ok(Language::Chinese),
300        _ => {
301            println!("\n{}", "❌ Invalid option / 无效选项".red());
302            select_language()
303        }
304    }
305}
306
307/// Session state to hold unlocked keypair
308struct SessionState {
309    keypair: Option<Keypair>,
310    keystore_path: Option<String>,
311}
312
313impl SessionState {
314    fn new() -> Self {
315        Self {
316            keypair: None,
317            keystore_path: None,
318        }
319    }
320
321    fn is_unlocked(&self) -> bool {
322        self.keypair.is_some()
323    }
324
325    fn unlock(&mut self, keypair: Keypair, path: String) {
326        self.keypair = Some(keypair);
327        self.keystore_path = Some(path);
328    }
329
330    fn get_keypair(&self) -> Option<&Keypair> {
331        self.keypair.as_ref()
332    }
333
334    fn lock(&mut self) {
335        self.keypair = None;
336        self.keystore_path = None;
337    }
338}
339
340/// 显示主菜单并处理用户选择
341pub fn show_main_menu() -> Result<(), String> {
342    // 首先选择语言
343    let lang = select_language()?;
344    let texts = match lang {
345        Language::Chinese => Texts::chinese(),
346        Language::English => Texts::english(),
347    };
348
349    // Create session state to hold unlocked keypair
350    let mut session = SessionState::new();
351
352    loop {
353        println!("\n{}", "=".repeat(50).cyan());
354        println!("{}", texts.title.cyan().bold());
355        println!("{}", "=".repeat(50).cyan());
356        println!();
357        println!("{}", texts.core_functions);
358        println!();
359        println!("  {}  {}", "1.".green().bold(), &texts.create_plain[6..]);
360        println!("  {}  {}", "2.".green().bold(), &texts.create_encrypted[6..]);
361        println!("  {}  {}", "3.".green().bold(), &texts.decrypt[6..]);
362
363        // Show unlock/lock status
364        println!();
365        if session.is_unlocked() {
366            if lang == Language::Chinese {
367                println!("  🔓 {} {}", "钱包已解锁:".green().bold(), session.get_keypair().unwrap().pubkey().to_string().bright_white());
368                println!("  {}  {}", "L.".yellow().bold(), "锁定钱包".yellow());
369            } else {
370                println!("  🔓 {} {}", "Wallet Unlocked:".green().bold(), session.get_keypair().unwrap().pubkey().to_string().bright_white());
371                println!("  {}  {}", "L.".yellow().bold(), "Lock Wallet".yellow());
372            }
373        } else {
374            if lang == Language::Chinese {
375                println!("  🔒 {} {}", "钱包状态:".red(), "未解锁".red());
376                println!("  {}  {}", "U.".green().bold(), "解锁钱包(用于Solana操作)".green());
377            } else {
378                println!("  🔒 {} {}", "Wallet Status:".red(), "Locked".red());
379                println!("  {}  {}", "U.".green().bold(), "Unlock Wallet (for Solana Operations)".green());
380            }
381        }
382
383        // Advanced security features
384        #[cfg(feature = "2fa")]
385        {
386            println!();
387            if lang == Language::Chinese {
388                println!("{}", "  高级安全功能:".bright_magenta().bold());
389            } else {
390                println!("{}", "  Advanced Security:".bright_magenta().bold());
391            }
392            println!("  {}  {}", "4.".bright_magenta().bold(), if lang == Language::Chinese { "设置 2FA 认证" } else { "Setup 2FA Authentication" });
393            println!("  {}  {}", "5.".bright_magenta().bold(), if lang == Language::Chinese { "生成三因子钱包" } else { "Generate Triple-Factor Wallet" });
394            println!("  {}  {}", "6.".bright_magenta().bold(), if lang == Language::Chinese { "解锁三因子钱包" } else { "Unlock Triple-Factor Wallet" });
395        }
396
397        // Solana operations (if feature is enabled)
398        #[cfg(feature = "solana-ops")]
399        {
400            println!();
401            if lang == Language::Chinese {
402                println!("{}", "  Solana 链上操作:".bright_blue().bold());
403            } else {
404                println!("{}", "  Solana Operations:".bright_blue().bold());
405            }
406            #[cfg(feature = "2fa")]
407            {
408                println!("  {}  {}", "7.".bright_cyan().bold(), if lang == Language::Chinese { "查询 SOL 余额" } else { "Check SOL Balance" });
409                println!("  {}  {}", "8.".bright_cyan().bold(), if lang == Language::Chinese { "转账 SOL" } else { "Transfer SOL" });
410                println!("  {}  {}", "9.".bright_cyan().bold(), if lang == Language::Chinese { "创建 WSOL ATA" } else { "Create WSOL ATA" });
411                println!("  {}  {}", "10.".bright_cyan().bold(), if lang == Language::Chinese { "包装 SOL → WSOL" } else { "Wrap SOL → WSOL" });
412                println!("  {}  {}", "11.".bright_cyan().bold(), if lang == Language::Chinese { "解包 WSOL → SOL" } else { "Unwrap WSOL → SOL" });
413                println!("  {}  {}", "12.".bright_cyan().bold(), if lang == Language::Chinese { "关闭 WSOL ATA" } else { "Close WSOL ATA" });
414                println!("  {}  {}", "13.".bright_cyan().bold(), if lang == Language::Chinese { "转账 SPL 代币" } else { "Transfer SPL Token" });
415                println!("  {}  {}", "14.".bright_cyan().bold(), if lang == Language::Chinese { "创建 Nonce 账户" } else { "Create Nonce Account" });
416
417                #[cfg(feature = "sol-trade-sdk")]
418                println!("  {}  {}", "15.".bright_magenta().bold(), if lang == Language::Chinese { "Pump.fun 卖出代币" } else { "Pump.fun Sell Tokens" });
419                #[cfg(feature = "sol-trade-sdk")]
420                println!("  {}  {}", "16.".bright_magenta().bold(), if lang == Language::Chinese { "PumpSwap 卖出代币" } else { "PumpSwap Sell Tokens" });
421                #[cfg(feature = "sol-trade-sdk")]
422                println!("  {}  {}", "17.".bright_magenta().bold(), if lang == Language::Chinese { "Pump.fun 返现(查看与领取)" } else { "Pump.fun Cashback (View & Claim)" });
423                #[cfg(feature = "sol-trade-sdk")]
424                println!("  {}  {}", "18.".bright_magenta().bold(), if lang == Language::Chinese { "PumpSwap 返现(查看与领取)" } else { "PumpSwap Cashback (View & Claim)" });
425            }
426            #[cfg(not(feature = "2fa"))]
427            {
428                println!("  {}  {}", "4.".bright_cyan().bold(), if lang == Language::Chinese { "查询 SOL 余额" } else { "Check SOL Balance" });
429                println!("  {}  {}", "5.".bright_cyan().bold(), if lang == Language::Chinese { "转账 SOL" } else { "Transfer SOL" });
430                println!("  {}  {}", "6.".bright_cyan().bold(), if lang == Language::Chinese { "创建 WSOL ATA" } else { "Create WSOL ATA" });
431                println!("  {}  {}", "7.".bright_cyan().bold(), if lang == Language::Chinese { "包装 SOL → WSOL" } else { "Wrap SOL → WSOL" });
432                println!("  {}  {}", "8.".bright_cyan().bold(), if lang == Language::Chinese { "解包 WSOL → SOL" } else { "Unwrap WSOL → SOL" });
433                println!("  {}  {}", "9.".bright_cyan().bold(), if lang == Language::Chinese { "关闭 WSOL ATA" } else { "Close WSOL ATA" });
434                println!("  {}  {}", "10.".bright_cyan().bold(), if lang == Language::Chinese { "转账 SPL 代币" } else { "Transfer SPL Token" });
435                println!("  {}  {}", "11.".bright_cyan().bold(), if lang == Language::Chinese { "创建 Nonce 账户" } else { "Create Nonce Account" });
436
437                #[cfg(feature = "sol-trade-sdk")]
438                println!("  {}  {}", "12.".bright_magenta().bold(), if lang == Language::Chinese { "Pump.fun 卖出代币" } else { "Pump.fun Sell Tokens" });
439                #[cfg(feature = "sol-trade-sdk")]
440                println!("  {}  {}", "13.".bright_magenta().bold(), if lang == Language::Chinese { "PumpSwap 卖出代币" } else { "PumpSwap Sell Tokens" });
441                #[cfg(feature = "sol-trade-sdk")]
442                println!("  {}  {}", "14.".bright_magenta().bold(), if lang == Language::Chinese { "Pump.fun 返现(查看与领取)" } else { "Pump.fun Cashback (View & Claim)" });
443                #[cfg(feature = "sol-trade-sdk")]
444                println!("  {}  {}", "15.".bright_magenta().bold(), if lang == Language::Chinese { "PumpSwap 返现(查看与领取)" } else { "PumpSwap Cashback (View & Claim)" });
445            }
446        }
447
448        println!();
449        println!("  {}  {}", "0.".red().bold(), &texts.exit[6..]);
450        println!();
451        print!("{}", texts.select_option);
452        io::stdout().flush().map_err(|e| e.to_string())?;
453
454        let mut choice = String::new();
455        io::stdin().read_line(&mut choice).map_err(|e| e.to_string())?;
456        let choice = choice.trim();
457
458        match choice.to_lowercase().as_str() {
459            "1" => create_plain_key_interactive(&texts)?,
460            "2" => create_encrypted_key_interactive(&texts)?,
461            "3" => decrypt_key_interactive(&texts)?,
462
463            // Unlock/Lock wallet
464            "u" => {
465                if session.is_unlocked() {
466                    if lang == Language::Chinese {
467                        println!("\n✅ 钱包已经解锁!");
468                    } else {
469                        println!("\n✅ Wallet already unlocked!");
470                    }
471                } else {
472                    if let Err(e) = unlock_wallet_interactive(&mut session, lang) {
473                        eprintln!("❌ {}", e);
474                    }
475                }
476                // 解锁/锁定后直接回到菜单,不再问「是否继续使用」
477                continue;
478            }
479            "l" => {
480                if session.is_unlocked() {
481                    session.lock();
482                    if lang == Language::Chinese {
483                        println!("\n🔒 钱包已锁定");
484                    } else {
485                        println!("\n🔒 Wallet locked");
486                    }
487                } else {
488                    if lang == Language::Chinese {
489                        println!("\n⚠️ 钱包未解锁");
490                    } else {
491                        println!("\n⚠️ Wallet not unlocked");
492                    }
493                }
494                continue;
495            }
496
497            // Advanced security features (2FA)
498            #[cfg(feature = "2fa")]
499            "4" => {
500                if let Err(e) = setup_2fa_interactive(lang) {
501                    eprintln!("❌ {}", e);
502                }
503            }
504            #[cfg(feature = "2fa")]
505            "5" => {
506                if let Err(e) = generate_triple_factor_wallet_interactive(lang) {
507                    eprintln!("❌ {}", e);
508                }
509            }
510            #[cfg(feature = "2fa")]
511            "6" => {
512                if let Err(e) = unlock_triple_factor_wallet_interactive(lang) {
513                    eprintln!("❌ {}", e);
514                }
515            }
516
517            // Solana operations
518            #[cfg(all(feature = "solana-ops", feature = "2fa"))]
519            "7" | "8" | "9" | "10" | "11" | "12" | "13" | "14" | "15" | "16" | "17" | "18" => {
520                if let Err(e) = handle_solana_operation(choice, lang, &mut session) {
521                    eprintln!("❌ {}", e);
522                }
523            }
524            #[cfg(all(feature = "solana-ops", not(feature = "2fa")))]
525            "4" | "5" | "6" | "7" | "8" | "9" | "10" | "11" | "12" | "13" | "14" | "15" => {
526                if let Err(e) = handle_solana_operation(choice, lang, &mut session) {
527                    eprintln!("❌ {}", e);
528                }
529            }
530
531            "0" => {
532                println!("\n{}", texts.goodbye.cyan());
533                break;
534            }
535            _ => {
536                println!("\n{}", texts.invalid_option.red());
537                continue;
538            }
539        }
540
541        // 询问是否继续
542        println!();
543        print!("{}", texts.continue_use);
544        io::stdout().flush().map_err(|e| e.to_string())?;
545
546        let mut continue_choice = String::new();
547        io::stdin().read_line(&mut continue_choice).map_err(|e| e.to_string())?;
548        let continue_choice = continue_choice.trim().to_lowercase();
549
550        if continue_choice == "n" || continue_choice == "no" {
551            println!("\n{}", texts.goodbye.cyan());
552            break;
553        }
554    }
555
556    Ok(())
557}
558
559/// 功能1: 创建明文私钥
560fn create_plain_key_interactive(texts: &Texts) -> Result<(), String> {
561    println!("\n{}", "=".repeat(50).yellow());
562    println!("{}", texts.create_plain_title.yellow().bold());
563    println!("{}", "=".repeat(50).yellow());
564    println!();
565
566    // 生成密钥对
567    let keypair = KeyManager::generate_keypair();
568    let pubkey = keypair.pubkey();
569    let private_key = keypair.to_base58_string();
570
571    println!("{}", texts.keypair_generated.green().bold());
572    println!();
573    println!("{} {}", texts.public_key.cyan(), pubkey.to_string().white().bold());
574    println!("{} {}", texts.private_key.red().bold(), private_key);
575    println!();
576
577    // 询问输出方式
578    println!("{}",texts.output_method);
579    println!("{}", texts.display_only);
580    println!("{}", texts.save_to_file);
581    println!();
582    print!("{}", texts.select);
583    io::stdout().flush().map_err(|e| e.to_string())?;
584
585    let mut output_choice = String::new();
586    io::stdin().read_line(&mut output_choice).map_err(|e| e.to_string())?;
587    let output_choice = output_choice.trim();
588
589    if output_choice == "2" {
590        print!("{}", texts.file_path);
591        io::stdout().flush().map_err(|e| e.to_string())?;
592
593        let mut file_path = String::new();
594        io::stdin().read_line(&mut file_path).map_err(|e| e.to_string())?;
595        let file_path = file_path.trim();
596        let file_path = if file_path.is_empty() {
597            "keypair.json"
598        } else {
599            file_path
600        };
601
602        // 保存为 Solana keypair JSON 格式 (数组格式)
603        let bytes = keypair.to_bytes();
604        let json = serde_json::to_string(&bytes.to_vec())
605            .map_err(|e| format!("{}", texts.write_failed.replace("{}", &e.to_string())))?;
606
607        std::fs::write(file_path, json)
608            .map_err(|e| format!("{}", texts.write_failed.replace("{}", &e.to_string())))?;
609
610        println!();
611        println!("{}", texts.file_saved.green());
612        println!("{} {}", texts.file_path.trim_end_matches(':'), file_path);
613    }
614
615    println!();
616    println!("{}", texts.security_warning.yellow().bold());
617    println!("{}", texts.plaintext_warning);
618    println!("{}", texts.save_securely);
619    println!("{}", texts.dont_share);
620    println!("{}", texts.recommend_encrypted);
621
622    Ok(())
623}
624
625/// 功能2: 创建加密私钥
626fn create_encrypted_key_interactive(texts: &Texts) -> Result<(), String> {
627    println!("\n{}", "=".repeat(50).yellow());
628    println!("{}", texts.create_encrypted_title.yellow().bold());
629    println!("{}", "=".repeat(50).yellow());
630    println!();
631
632    // 询问是生成新的还是导入现有私钥
633    println!("{}", texts.choose_method);
634    println!("{}", texts.generate_new);
635    println!("{}", texts.import_existing);
636    println!();
637    print!("{}", texts.select);
638    io::stdout().flush().map_err(|e| e.to_string())?;
639
640    let mut source_choice = String::new();
641    io::stdin().read_line(&mut source_choice).map_err(|e| e.to_string())?;
642    let source_choice = source_choice.trim();
643
644    let keypair = match source_choice {
645        "1" => {
646            // 生成新密钥对
647            println!();
648            println!("{}", texts.generating.cyan());
649            KeyManager::generate_keypair()
650        }
651        "2" => {
652            // 导入现有私钥
653            println!();
654            print!("{}", texts.enter_private_key);
655            io::stdout().flush().map_err(|e| e.to_string())?;
656
657            let mut private_key = String::new();
658            io::stdin().read_line(&mut private_key).map_err(|e| e.to_string())?;
659            let private_key = private_key.trim();
660
661            if private_key.is_empty() {
662                return Err(texts.private_key_empty.to_string());
663            }
664
665            Keypair::from_base58_string(private_key)
666        }
667        _ => {
668            return Err(texts.invalid_choice.to_string());
669        }
670    };
671
672    let pubkey = keypair.pubkey();
673
674    println!();
675    println!("{}", texts.keypair_ready.green());
676    println!("{} {}", texts.public_key.cyan(), pubkey);
677    println!();
678
679    // 获取密码
680    let password = read_password_confirmed(texts)?;
681
682    // 询问输出方式
683    println!();
684    println!("{}", texts.output_method);
685    println!("{}", texts.keystore_recommended);
686    println!("{}", texts.show_encrypted_string);
687    println!();
688    print!("{}", texts.select);
689    io::stdout().flush().map_err(|e| e.to_string())?;
690
691    let mut output_choice = String::new();
692    io::stdin().read_line(&mut output_choice).map_err(|e| e.to_string())?;
693    let output_choice = output_choice.trim();
694
695    match output_choice {
696        "1" => {
697            // 保存为文件
698            print!("{}", texts.file_path.replace("keypair", "wallet"));
699            io::stdout().flush().map_err(|e| e.to_string())?;
700
701            let mut file_path = String::new();
702            io::stdin().read_line(&mut file_path).map_err(|e| e.to_string())?;
703            let file_path = file_path.trim();
704            let file_path = if file_path.is_empty() {
705                "keystore.json"
706            } else {
707                file_path
708            };
709
710            let keystore_json = KeyManager::keypair_to_encrypted_json(&keypair, &password)?;
711            std::fs::write(file_path, keystore_json)
712                .map_err(|e| format!("{}", texts.write_failed.replace("{}", &e.to_string())))?;
713
714            println!();
715            println!("{}", "=".repeat(50).green());
716            println!("{}", texts.keystore_created.green().bold());
717            println!("{}", "=".repeat(50).green());
718            println!();
719            println!("{} {}", texts.file_path.trim_end_matches(':'), file_path);
720            println!("{} {}", texts.public_key.cyan(), pubkey);
721            println!("{}", texts.private_key_encrypted.green());
722            println!();
723            println!("{}", texts.important_note.yellow().bold());
724            println!("{}", texts.keep_safe);
725            println!("{}", texts.lost_password_warning);
726            println!("{}", texts.backup_recommended);
727        }
728        "2" => {
729            // 显示加密字符串
730            let private_key = keypair.to_base58_string();
731            let encrypted = KeyManager::encrypt_with_password(&private_key, &password)?;
732
733            println!();
734            println!("{}", texts.keypair_ready.green().bold());
735            println!();
736            println!("{} {}", texts.public_key.cyan(), pubkey);
737            println!("{}", texts.encrypted_private_key.cyan());
738            println!("{}", encrypted);
739            println!();
740            println!("{}", texts.keep_safe_both.yellow());
741        }
742        _ => {
743            return Err(texts.invalid_choice.to_string());
744        }
745    }
746
747    Ok(())
748}
749
750/// 功能3: 解密私钥
751fn decrypt_key_interactive(texts: &Texts) -> Result<(), String> {
752    println!("\n{}", "=".repeat(50).yellow());
753    println!("{}", texts.decrypt_title.yellow().bold());
754    println!("{}", "=".repeat(50).yellow());
755    println!();
756
757    // 选择输入方式
758    println!("{}", texts.input_method);
759    println!("{}", texts.from_keystore);
760    println!("{}", texts.from_encrypted_string);
761    println!();
762    print!("{}", texts.select);
763    io::stdout().flush().map_err(|e| e.to_string())?;
764
765    let mut input_choice = String::new();
766    io::stdin().read_line(&mut input_choice).map_err(|e| e.to_string())?;
767    let input_choice = input_choice.trim();
768
769    let (private_key, pubkey) = match input_choice {
770        "1" => {
771            // 从文件读取
772            print!("{}", texts.file_path.trim_end_matches("(默认: keypair.json): ").trim_end_matches("(default: keypair.json): "));
773            io::stdout().flush().map_err(|e| e.to_string())?;
774
775            let mut file_path = String::new();
776            io::stdin().read_line(&mut file_path).map_err(|e| e.to_string())?;
777            let file_path = file_path.trim();
778
779            if !std::path::Path::new(file_path).exists() {
780                return Err(format!("{}", texts.file_not_exist.replace("{}", file_path)));
781            }
782
783            println!();
784            let password = prompt_password(texts.enter_password, texts)?;
785
786            let keystore_json = std::fs::read_to_string(file_path)
787                .map_err(|e| format!("{}", texts.write_failed.replace("{}", &e.to_string())))?;
788
789            let keypair = KeyManager::keypair_from_encrypted_json(&keystore_json, &password)?;
790            let pubkey = keypair.pubkey();
791            let private_key = keypair.to_base58_string();
792
793            (private_key, pubkey)
794        }
795        "2" => {
796            // 输入加密字符串
797            print!("{}", texts.encrypted_key);
798            io::stdout().flush().map_err(|e| e.to_string())?;
799
800            let mut encrypted = String::new();
801            io::stdin().read_line(&mut encrypted).map_err(|e| e.to_string())?;
802            let encrypted = encrypted.trim();
803
804            println!();
805            let password = prompt_password(texts.enter_password, texts)?;
806
807            let private_key = KeyManager::decrypt_with_password(encrypted, &password)?;
808            let keypair = Keypair::from_base58_string(&private_key);
809            let pubkey = keypair.pubkey();
810
811            (private_key, pubkey)
812        }
813        _ => {
814            return Err(texts.invalid_choice.to_string());
815        }
816    };
817
818    println!();
819    println!("{}", "=".repeat(50).green());
820    println!("{}", texts.decrypt_success.green().bold());
821    println!("{}", "=".repeat(50).green());
822    println!();
823    println!("{} {}", texts.public_key.cyan(), pubkey);
824    println!("{} {}", texts.private_key.red().bold(), private_key);
825    println!();
826
827    // 询问输出方式
828    println!("{}", texts.output_method);
829    println!("{}", texts.display_only);
830    println!("{}", texts.save_to_file);
831    println!();
832    print!("{}", texts.select);
833    io::stdout().flush().map_err(|e| e.to_string())?;
834
835    let mut output_choice = String::new();
836    io::stdin().read_line(&mut output_choice).map_err(|e| e.to_string())?;
837    let output_choice = output_choice.trim();
838
839    if output_choice == "2" {
840        let default_filename = if texts.file_path.contains("默认") {
841            "decrypted_key.txt"
842        } else {
843            "decrypted_key.txt"
844        };
845
846        print!("{}", texts.file_path.replace("keypair.json", default_filename));
847        io::stdout().flush().map_err(|e| e.to_string())?;
848
849        let mut file_path = String::new();
850        io::stdin().read_line(&mut file_path).map_err(|e| e.to_string())?;
851        let file_path = file_path.trim();
852        let file_path = if file_path.is_empty() {
853            default_filename
854        } else {
855            file_path
856        };
857
858        let content = format!("{} {}\n{} {}\n", texts.public_key, pubkey, texts.private_key.trim_end_matches(':'), private_key);
859        std::fs::write(file_path, content)
860            .map_err(|e| format!("{}", texts.write_failed.replace("{}", &e.to_string())))?;
861
862        println!();
863        println!("{}", texts.file_saved.green());
864        println!("{} {}", texts.file_path.trim_end_matches(':'), file_path);
865    }
866
867    println!();
868    println!("{}", texts.security_warning.yellow().bold());
869    println!("{}", texts.dont_share_warning);
870    println!("{}", texts.delete_plaintext);
871    println!("{}", texts.use_encryption);
872
873    Ok(())
874}
875
876/// 在 **stdout** 打印提示,在 TTY 上无回显读取密码(避免 `prompt_password` 仅写 TTY 导致 IDE 终端里看不到提示)。
877fn prompt_password(prompt: &str, texts: &Texts) -> Result<String, String> {
878    print!("{}", prompt);
879    io::stdout().flush().map_err(|e| e.to_string())?;
880
881    let password = rpassword::read_password()
882        .map_err(|e| format!("{}", texts.write_failed.replace("{}", &e.to_string())))?;
883
884    Ok(password.trim().to_string())
885}
886
887/// Unlock wallet interactively and store in session
888fn unlock_wallet_interactive(session: &mut SessionState, language: Language) -> Result<(), String> {
889    println!();
890    if language == Language::Chinese {
891        println!("{}", "  解锁钱包".cyan().bold());
892        print!("Keystore 文件路径 [keystore.json]: ");
893    } else {
894        println!("{}", "  Unlock Wallet".cyan().bold());
895        print!("Keystore file path [keystore.json]: ");
896    }
897    io::stdout().flush().map_err(|e| e.to_string())?;
898
899    let mut keystore_path = String::new();
900    io::stdin().read_line(&mut keystore_path).map_err(|e| e.to_string())?;
901    let keystore_path = keystore_path.trim();
902
903    // Only search subdirectories when user pressed Enter without typing a path
904    let keystore_path = if keystore_path.is_empty() {
905        if let Some(found) = search_keystore_in_subdirs("keystore.json") {
906            if language == Language::Chinese {
907                println!("🔍 在子目录中找到: {}", found);
908            } else {
909                println!("🔍 Found in subdirectory: {}", found);
910            }
911            found
912        } else {
913            "keystore.json".to_string()
914        }
915    } else {
916        keystore_path.to_string()
917    };
918
919    // Read encrypted file
920    let file_content = std::fs::read_to_string(&keystore_path).map_err(|e| {
921        if language == Language::Chinese {
922            format!(
923                "无法读取 Keystore 文件「{}」: {}(请确认路径正确、当前工作目录是否为文件所在目录)",
924                keystore_path, e
925            )
926        } else {
927            format!("Failed to read keystore '{}': {}", keystore_path, e)
928        }
929    })?;
930
931    // Parse JSON to get encryption type
932    let json: serde_json::Value = serde_json::from_str(&file_content)
933        .map_err(|e| format!("Failed to parse keystore: {}", e))?;
934
935    let encryption_type = json["encryption_type"].as_str().unwrap_or("password_only");
936
937    // Decrypt keypair: use bot_helper::unlock_wallet for password_only to guarantee
938    // the exact same code path as bot startup (config/mod.rs → ensure_wallet_ready).
939    let keypair = match encryption_type {
940        "password_only" => {
941            crate::bot_helper::unlock_wallet(&keystore_path)
942                .map_err(|e| format!("Failed to decrypt keystore: {}", e))?
943        }
944        "triple_factor_v1" => {
945            return Err("Triple-factor wallets not yet supported in interactive mode. Please use the CLI.".to_string());
946        }
947        _ => {
948            return Err(format!("Unknown encryption type: {}", encryption_type));
949        }
950    };
951
952    // Store in session
953    session.unlock(keypair, keystore_path.to_string());
954
955    if language == Language::Chinese {
956        println!("✅ 钱包解锁成功!");
957        println!("📍 钱包地址: {}", session.get_keypair().unwrap().pubkey());
958        println!("💡 提示: 在本次会话中,Solana操作将使用此钱包,无需重复输入密码");
959    } else {
960        println!("✅ Wallet unlocked successfully!");
961        println!("📍 Wallet address: {}", session.get_keypair().unwrap().pubkey());
962        println!("💡 Tip: Solana operations in this session will use this wallet without re-entering password");
963    }
964
965    Ok(())
966}
967
968/// Handle Solana operation using session keypair
969#[cfg(feature = "solana-ops")]
970fn handle_solana_operation(choice: &str, language: Language, session: &mut SessionState) -> Result<(), String> {
971    // Convert Language to operations::Language
972    let ops_language = match language {
973        Language::English => crate::operations::Language::English,
974        Language::Chinese => crate::operations::Language::Chinese,
975    };
976
977    // Check if wallet is unlocked
978    let keypair = if let Some(kp) = session.get_keypair() {
979        kp
980    } else {
981        // Wallet not unlocked, prompt user to unlock first
982        if language == Language::Chinese {
983            println!("\n⚠️  请先使用 'U' 选项解锁钱包");
984        } else {
985            println!("\n⚠️  Please unlock wallet first using 'U' option");
986        }
987        return Ok(());
988    };
989
990    if language == Language::Chinese {
991        println!("\n📍 使用钱包: {}", keypair.pubkey());
992    } else {
993        println!("\n📍 Using wallet: {}", keypair.pubkey());
994    }
995
996    // Call the appropriate operation
997    #[cfg(feature = "2fa")]
998    let result = match choice {
999        "7" => crate::operations::check_balance(&keypair, ops_language),
1000        "8" => crate::operations::transfer_sol(&keypair, ops_language),
1001        "9" => crate::operations::create_wsol_ata(&keypair, ops_language),
1002        "10" => crate::operations::wrap_sol(&keypair, ops_language),
1003        "11" => crate::operations::unwrap_sol(&keypair, ops_language),
1004        "12" => crate::operations::close_wsol_ata(&keypair, ops_language),
1005        "13" => crate::operations::transfer_token(&keypair, ops_language),
1006        "14" => crate::operations::create_nonce_account(&keypair, ops_language),
1007        #[cfg(feature = "sol-trade-sdk")]
1008        "15" => crate::operations::pumpfun_sell_interactive(&keypair, ops_language),
1009        #[cfg(feature = "sol-trade-sdk")]
1010        "16" => crate::operations::pumpswap_sell_interactive(&keypair, ops_language),
1011        #[cfg(feature = "sol-trade-sdk")]
1012        "17" => crate::operations::pumpfun_cashback_interactive(&keypair, ops_language),
1013        #[cfg(feature = "sol-trade-sdk")]
1014        "18" => crate::operations::pumpswap_cashback_interactive(&keypair, ops_language),
1015        _ => Err("Invalid operation".to_string()),
1016    };
1017
1018    #[cfg(not(feature = "2fa"))]
1019    let result = match choice {
1020        "4" => crate::operations::check_balance(&keypair, ops_language),
1021        "5" => crate::operations::transfer_sol(&keypair, ops_language),
1022        "6" => crate::operations::create_wsol_ata(&keypair, ops_language),
1023        "7" => crate::operations::wrap_sol(&keypair, ops_language),
1024        "8" => crate::operations::unwrap_sol(&keypair, ops_language),
1025        "9" => crate::operations::close_wsol_ata(&keypair, ops_language),
1026        "10" => crate::operations::transfer_token(&keypair, ops_language),
1027        "11" => crate::operations::create_nonce_account(&keypair, ops_language),
1028        #[cfg(feature = "sol-trade-sdk")]
1029        "12" => crate::operations::pumpfun_sell_interactive(&keypair, ops_language),
1030        #[cfg(feature = "sol-trade-sdk")]
1031        "13" => crate::operations::pumpswap_sell_interactive(&keypair, ops_language),
1032        #[cfg(feature = "sol-trade-sdk")]
1033        "14" => crate::operations::pumpfun_cashback_interactive(&keypair, ops_language),
1034        #[cfg(feature = "sol-trade-sdk")]
1035        "15" => crate::operations::pumpswap_cashback_interactive(&keypair, ops_language),
1036        _ => Err("Invalid operation".to_string()),
1037    };
1038
1039    result
1040}
1041
1042/// Setup 2FA authentication interactively
1043#[cfg(feature = "2fa")]
1044fn setup_2fa_interactive(language: Language) -> Result<(), String> {
1045    use crate::{derive_totp_secret_from_hardware_and_password, hardware_fingerprint::HardwareFingerprint, security_question::SecurityQuestion, totp::*};
1046    use rpassword;
1047
1048    let account = "wallet";
1049    let issuer = "Sol-SafeKey";
1050
1051    println!("\n{}", "=".repeat(50).bright_magenta());
1052    if language == Language::Chinese {
1053        println!("{}", "  🔐 三因子 2FA 安全设置".bright_magenta().bold());
1054    } else {
1055        println!("{}", "  🔐 Triple-Factor 2FA Security Setup".bright_magenta().bold());
1056    }
1057    println!("{}", "=".repeat(50).bright_magenta());
1058    println!();
1059
1060    if language == Language::Chinese {
1061        println!("{}", "⚠️  安全架构说明:".yellow().bold());
1062        println!("  • 因子1: 硬件指纹(自动收集,绑定设备)");
1063        println!("  • 因子2: 主密码(您设置的强密码)");
1064        println!("  • 因子3: 安全问题答案(防止密码泄露)");
1065        println!("  • 2FA密钥: 从硬件指纹+主密码派生(确定性)");
1066        println!("  • 解锁需要: 主密码 + 安全问题答案 + 2FA动态验证码");
1067    } else {
1068        println!("{}", "⚠️  Security Architecture:".yellow().bold());
1069        println!("  • Factor 1: Hardware Fingerprint (auto-collected, device-bound)");
1070        println!("  • Factor 2: Master Password (your strong password)");
1071        println!("  • Factor 3: Security Question Answer (prevents password leak)");
1072        println!("  • 2FA Key: Derived from hardware fingerprint + master password");
1073        println!("  • Unlock requires: Master password + Security answer + 2FA code");
1074    }
1075    println!();
1076
1077    // Step 1: Collect hardware fingerprint
1078    if language == Language::Chinese {
1079        println!("{}", "步骤 1/4: 收集硬件指纹...".bright_blue());
1080    } else {
1081        println!("{}", "Step 1/4: Collecting hardware fingerprint...".bright_blue());
1082    }
1083
1084    let hardware_fp = HardwareFingerprint::collect()
1085        .map_err(|e| format!("Failed to collect hardware fingerprint: {}", e))?;
1086
1087    if language == Language::Chinese {
1088        println!("{} 硬件指纹已收集(SHA256哈希)", "✅".green());
1089        println!("   指纹预览: {}...", &hardware_fp.as_str()[..16]);
1090    } else {
1091        println!("{} Hardware fingerprint collected (SHA256 hash)", "✅".green());
1092        println!("   Preview: {}...", &hardware_fp.as_str()[..16]);
1093    }
1094    println!();
1095
1096    // Step 2: Set master password
1097    if language == Language::Chinese {
1098        println!("{}", "步骤 2/4: 设置主密码".bright_blue());
1099    } else {
1100        println!("{}", "Step 2/4: Set master password".bright_blue());
1101    }
1102
1103    let master_password = loop {
1104        let password = rpassword::prompt_password(
1105            if language == Language::Chinese { "请输入主密码: " } else { "Enter master password: " }
1106        ).map_err(|e| format!("Failed to read password: {}", e))?;
1107
1108        if password.is_empty() {
1109            println!("{} {}", "❌".red(), if language == Language::Chinese { "主密码不能为空" } else { "Master password cannot be empty" });
1110            continue;
1111        }
1112
1113        // Check password strength
1114        if password.len() < 10 {
1115            println!("{} {}", "❌".red(), if language == Language::Chinese { "密码长度至少10个字符" } else { "Password must be at least 10 characters" });
1116            continue;
1117        }
1118
1119        let password_confirm = rpassword::prompt_password(
1120            if language == Language::Chinese { "请再次输入主密码确认: " } else { "Confirm master password: " }
1121        ).map_err(|e| format!("Failed to read password: {}", e))?;
1122
1123        if password != password_confirm {
1124            println!("{} {}", "❌".red(), if language == Language::Chinese { "两次输入的密码不一致" } else { "Passwords do not match" });
1125            continue;
1126        }
1127
1128        break password;
1129    };
1130
1131    if language == Language::Chinese {
1132        println!("{} 主密码设置成功", "✅".green());
1133    } else {
1134        println!("{} Master password set successfully", "✅".green());
1135    }
1136    println!();
1137
1138    // Step 3: Set security question
1139    if language == Language::Chinese {
1140        println!("{}", "步骤 3/4: 设置安全问题".bright_blue());
1141    } else {
1142        println!("{}", "Step 3/4: Set security question".bright_blue());
1143    }
1144
1145    let (question_index, _security_answer) = SecurityQuestion::setup_interactive()
1146        .map_err(|e| format!("Failed to setup security question: {}", e))?;
1147    println!();
1148
1149    // Step 4: Setup 2FA
1150    if language == Language::Chinese {
1151        println!("{}", "步骤 4/4: 设置 2FA 动态验证码".bright_blue());
1152    } else {
1153        println!("{}", "Step 4/4: Setup 2FA TOTP".bright_blue());
1154    }
1155
1156    let twofa_secret = derive_totp_secret_from_hardware_and_password(
1157        hardware_fp.as_str(),
1158        &master_password,
1159        account,
1160        issuer,
1161    ).map_err(|e| format!("Failed to derive 2FA secret: {}", e))?;
1162
1163    let config = TOTPConfig {
1164        secret: twofa_secret.clone(),
1165        account: account.to_string(),
1166        issuer: issuer.to_string(),
1167        algorithm: "SHA1".to_string(),
1168        digits: 6,
1169        step: 30,
1170    };
1171
1172    let totp_manager = TOTPManager::new(config);
1173
1174    if language == Language::Chinese {
1175        println!("{}", "📱 请使用 Google Authenticator 或 Authy 扫描以下 QR 码:".yellow());
1176    } else {
1177        println!("{}", "📱 Scan this QR code with Google Authenticator or Authy:".yellow());
1178    }
1179    println!();
1180
1181    match totp_manager.generate_qr_code() {
1182        Ok(qr_code) => {
1183            println!("{}", qr_code);
1184        }
1185        Err(e) => {
1186            if language == Language::Chinese {
1187                eprintln!("{} QR 码生成失败: {}", "⚠️".yellow(), e);
1188                println!("{}", "📝 请手动输入以下信息:".yellow());
1189            } else {
1190                eprintln!("{} QR code generation failed: {}", "⚠️".yellow(), e);
1191                println!("{}", "📝 Please enter this info manually:".yellow());
1192            }
1193            println!("{}", totp_manager.get_manual_setup_info());
1194        }
1195    }
1196
1197    println!();
1198    if language == Language::Chinese {
1199        println!("{} 或者手动输入密钥: {}", "🔑".bright_cyan(), twofa_secret.bright_white());
1200    } else {
1201        println!("{} Or enter manually: {}", "🔑".bright_cyan(), twofa_secret.bright_white());
1202    }
1203    println!();
1204
1205    // Verify 2FA setup
1206    loop {
1207        print!("{}", if language == Language::Chinese {
1208            "请输入认证器显示的 6 位验证码以确认设置: "
1209        } else {
1210            "Enter the 6-digit code from your authenticator to verify: "
1211        });
1212        io::stdout().flush().map_err(|e| e.to_string())?;
1213
1214        let mut input = String::new();
1215        io::stdin().read_line(&mut input).map_err(|e| e.to_string())?;
1216        let code = input.trim();
1217
1218        match totp_manager.verify_code(code) {
1219            Ok(true) => {
1220                println!("{}", if language == Language::Chinese {
1221                    "✅ 2FA 验证成功!".green()
1222                } else {
1223                    "✅ 2FA verification successful!".green()
1224                });
1225                break;
1226            }
1227            Ok(false) => {
1228                println!("{}", if language == Language::Chinese {
1229                    "❌ 验证码不正确,请重试".red()
1230                } else {
1231                    "❌ Code incorrect, please try again".red()
1232                });
1233                continue;
1234            }
1235            Err(e) => {
1236                eprintln!("{} {}: {}", "❌".red(), if language == Language::Chinese { "验证失败" } else { "Verification failed" }, e);
1237                continue;
1238            }
1239        }
1240    }
1241
1242    println!();
1243    if language == Language::Chinese {
1244        println!("{}", "🎉 三因子 2FA 设置完成!".green().bold());
1245        println!();
1246        println!("{}", "📝 重要信息(请妥善保管):".yellow().bold());
1247        println!("  • 硬件指纹: 已绑定到当前设备");
1248        println!("  • 安全问题: 问题 {} - {}", question_index + 1, crate::security_question::SECURITY_QUESTIONS[question_index]);
1249        println!("  • 2FA密钥: 已添加到认证器");
1250        println!();
1251        println!("{}", "💡 下一步: 使用选项5生成三因子钱包".bright_blue());
1252    } else {
1253        println!("{}", "🎉 Triple-factor 2FA setup complete!".green().bold());
1254        println!();
1255        println!("{}", "📝 Important info (keep safe):".yellow().bold());
1256        println!("  • Hardware fingerprint: Bound to current device");
1257        println!("  • Security question: Question {} - {}", question_index + 1, crate::security_question::SECURITY_QUESTIONS[question_index]);
1258        println!("  • 2FA key: Added to authenticator");
1259        println!();
1260        println!("{}", "💡 Next step: Use option 5 to generate triple-factor wallet".bright_blue());
1261    }
1262
1263    Ok(())
1264}
1265
1266/// Generate triple-factor wallet interactively
1267#[cfg(feature = "2fa")]
1268fn generate_triple_factor_wallet_interactive(_language: Language) -> Result<(), String> {
1269    Err("This feature will be implemented soon. Please use CLI command: sol-safekey gen-2fa-wallet".to_string())
1270}
1271
1272/// Unlock triple-factor wallet interactively
1273#[cfg(feature = "2fa")]
1274fn unlock_triple_factor_wallet_interactive(_language: Language) -> Result<(), String> {
1275    Err("This feature will be implemented soon. Please use CLI command: sol-safekey unlock-2fa-wallet -f <file>".to_string())
1276}
1277
1278/// Read password with confirmation and validation
1279fn read_password_confirmed(texts: &Texts) -> Result<String, String> {
1280    println!("{}", texts.set_password);
1281
1282    let password = prompt_password(texts.new_password, texts)?;
1283
1284    if password.is_empty() {
1285        return Err(texts.password_empty.to_string());
1286    }
1287
1288    if password.len() < 10 {
1289        return Err(texts.password_min_length.to_string());
1290    }
1291
1292    let password_confirm = prompt_password(texts.confirm_password, texts)?;
1293
1294    if password != password_confirm {
1295        return Err(texts.password_mismatch.to_string());
1296    }
1297
1298    println!("{}", texts.password_set.green());
1299    Ok(password)
1300}