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