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