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