1use crate::{
3 chain::quantus_subxt,
4 error::QuantusError,
5 log_error, log_print, log_success, log_verbose,
6 wallet::{password::get_mnemonic_from_user, WalletManager},
7};
8use clap::Subcommand;
9use colored::Colorize;
10use sp_core::crypto::{AccountId32, Ss58Codec};
11use std::io::{self, Write};
12
13#[derive(Subcommand, Debug)]
15pub enum WalletCommands {
16 Create {
18 #[arg(short, long)]
20 name: String,
21
22 #[arg(short, long)]
24 password: Option<String>,
25 },
26
27 View {
29 #[arg(short, long)]
31 name: Option<String>,
32
33 #[arg(short, long)]
35 all: bool,
36 },
37
38 Export {
40 #[arg(short, long)]
42 name: String,
43
44 #[arg(short, long)]
46 password: Option<String>,
47
48 #[arg(short, long, default_value = "mnemonic")]
50 format: String,
51 },
52
53 Import {
55 #[arg(short, long)]
57 name: String,
58
59 #[arg(short, long)]
61 mnemonic: Option<String>,
62
63 #[arg(short, long)]
65 password: Option<String>,
66 },
67
68 List,
70
71 Delete {
73 #[arg(short, long)]
75 name: String,
76
77 #[arg(short, long)]
79 force: bool,
80 },
81
82 Nonce {
84 #[arg(short, long)]
86 address: Option<String>,
87
88 #[arg(short, long, required_unless_present("address"))]
90 wallet: Option<String>,
91
92 #[arg(short, long)]
94 password: Option<String>,
95 },
96}
97
98pub async fn get_account_nonce(
100 quantus_client: &crate::chain::client::QuantusClient,
101 account_address: &str,
102) -> crate::error::Result<u32> {
103 log_verbose!("#️⃣ Querying nonce for account: {}", account_address.bright_green());
104
105 let account_id_sp = AccountId32::from_ss58check(account_address)
107 .map_err(|e| QuantusError::NetworkError(format!("Invalid SS58 address: {e:?}")))?;
108
109 log_verbose!("🔍 SP Account ID: {:?}", account_id_sp);
110
111 let account_bytes: [u8; 32] = *account_id_sp.as_ref();
113 let account_id = subxt::ext::subxt_core::utils::AccountId32::from(account_bytes);
114
115 log_verbose!("🔍 SubXT Account ID: {:?}", account_id);
116
117 use quantus_subxt::api;
119 let storage_addr = api::storage().system().account(account_id);
120
121 let latest_block_hash = quantus_client.get_latest_block().await?;
123
124 let storage_at = quantus_client.client().storage().at(latest_block_hash);
125
126 let account_info = storage_at
127 .fetch_or_default(&storage_addr)
128 .await
129 .map_err(|e| QuantusError::NetworkError(format!("Failed to fetch account info: {e:?}")))?;
130
131 log_verbose!("✅ Account info retrieved with storage query!");
132 log_verbose!("🔢 Nonce: {}", account_info.nonce);
133
134 Ok(account_info.nonce)
135}
136
137pub async fn handle_wallet_command(
139 command: WalletCommands,
140 node_url: &str,
141) -> crate::error::Result<()> {
142 match command {
143 WalletCommands::Create { name, password } => {
144 log_print!("🔐 Creating new quantum wallet...");
145
146 let wallet_manager = WalletManager::new()?;
147
148 match wallet_manager.create_wallet(&name, password.as_deref()).await {
149 Ok(wallet_info) => {
150 log_success!("Wallet name: {}", name.bright_green());
151 log_success!("Address: {}", wallet_info.address.bright_cyan());
152 log_success!("Key type: {}", wallet_info.key_type.bright_yellow());
153 log_success!(
154 "Created: {}",
155 wallet_info.created_at.format("%Y-%m-%d %H:%M:%S UTC").to_string().dimmed()
156 );
157 log_success!("✅ Wallet created successfully!");
158 },
159 Err(e) => {
160 log_error!("{}", format!("❌ Failed to create wallet: {e}").red());
161 return Err(e);
162 },
163 }
164
165 Ok(())
166 },
167
168 WalletCommands::View { name, all } => {
169 log_print!("👁️ Viewing wallet information...");
170
171 let wallet_manager = WalletManager::new()?;
172
173 if all {
174 match wallet_manager.list_wallets() {
176 Ok(wallets) =>
177 if wallets.is_empty() {
178 log_print!("{}", "No wallets found.".dimmed());
179 } else {
180 log_print!("All wallets ({}):\n", wallets.len());
181
182 for (i, wallet) in wallets.iter().enumerate() {
183 log_print!(
184 "{}. {}",
185 (i + 1).to_string().bright_yellow(),
186 wallet.name.bright_green()
187 );
188 log_print!(" Address: {}", wallet.address.bright_cyan());
189 log_print!(" Type: {}", wallet.key_type.bright_yellow());
190 log_print!(
191 " Created: {}",
192 wallet
193 .created_at
194 .format("%Y-%m-%d %H:%M:%S UTC")
195 .to_string()
196 .dimmed()
197 );
198 if i < wallets.len() - 1 {
199 log_print!();
200 }
201 }
202 },
203 Err(e) => {
204 log_error!("{}", format!("❌ Failed to view wallets: {e}").red());
205 return Err(e);
206 },
207 }
208 } else if let Some(wallet_name) = name {
209 match wallet_manager.get_wallet(&wallet_name, None) {
211 Ok(Some(wallet_info)) => {
212 log_print!("Wallet Details:\n");
213 log_print!("Name: {}", wallet_info.name.bright_green());
214 log_print!("Address: {}", wallet_info.address.bright_cyan());
215 log_print!("Key Type: {}", wallet_info.key_type.bright_yellow());
216 log_print!(
217 "Created: {}",
218 wallet_info
219 .created_at
220 .format("%Y-%m-%d %H:%M:%S UTC")
221 .to_string()
222 .dimmed()
223 );
224
225 if wallet_info.address.contains("[") {
226 log_print!(
227 "\n{}",
228 "💡 To see the full address, use the export command with password"
229 .dimmed()
230 );
231 }
232 },
233 Ok(None) => {
234 log_error!("{}", format!("❌ Wallet '{wallet_name}' not found").red());
235 log_print!(
236 "Use {} to see available wallets",
237 "quantus wallet list".bright_green()
238 );
239 },
240 Err(e) => {
241 log_error!("{}", format!("❌ Failed to view wallet: {e}").red());
242 return Err(e);
243 },
244 }
245 } else {
246 log_print!(
247 "{}",
248 "Please specify a wallet name with --name or use --all to show all wallets"
249 .yellow()
250 );
251 log_print!("Examples:");
252 log_print!(" {}", "quantus wallet view --name my-wallet".bright_green());
253 log_print!(" {}", "quantus wallet view --all".bright_green());
254 }
255
256 Ok(())
257 },
258
259 WalletCommands::Export { name, password, format } => {
260 log_print!("📤 Exporting wallet...");
261
262 if format.to_lowercase() != "mnemonic" {
263 log_error!("Only 'mnemonic' export format is currently supported.");
264 return Err(crate::error::QuantusError::Generic(
265 "Export format not supported".to_string(),
266 ));
267 }
268
269 let wallet_manager = WalletManager::new()?;
270
271 match wallet_manager.export_mnemonic(&name, password.as_deref()) {
272 Ok(mnemonic) => {
273 log_success!("✅ Wallet exported successfully!");
274 log_print!("\nYour secret mnemonic phrase:");
275 log_print!("{}", "--------------------------------------------------".dimmed());
276 log_print!("{}", mnemonic.bright_yellow());
277 log_print!("{}", "--------------------------------------------------".dimmed());
278 log_print!(
279 "\n{}",
280 "⚠️ Keep this phrase safe and secret. Anyone with this phrase can access your funds."
281 .bright_red()
282 );
283 },
284 Err(e) => {
285 log_error!("{}", format!("❌ Failed to export wallet: {e}").red());
286 return Err(e);
287 },
288 }
289
290 Ok(())
291 },
292
293 WalletCommands::Import { name, mnemonic, password } => {
294 log_print!("📥 Importing wallet...");
295
296 let wallet_manager = WalletManager::new()?;
297
298 let mnemonic_phrase =
300 if let Some(mnemonic) = mnemonic { mnemonic } else { get_mnemonic_from_user()? };
301
302 let final_password =
304 crate::wallet::password::get_wallet_password(&name, password, None)?;
305
306 match wallet_manager
307 .import_wallet(&name, &mnemonic_phrase, Some(&final_password))
308 .await
309 {
310 Ok(wallet_info) => {
311 log_success!("Wallet name: {}", name.bright_green());
312 log_success!("Address: {}", wallet_info.address.bright_cyan());
313 log_success!("Key type: {}", wallet_info.key_type.bright_yellow());
314 log_success!(
315 "Imported: {}",
316 wallet_info.created_at.format("%Y-%m-%d %H:%M:%S UTC").to_string().dimmed()
317 );
318 log_success!("✅ Wallet imported successfully!");
319 },
320 Err(e) => {
321 log_error!("{}", format!("❌ Failed to import wallet: {e}").red());
322 return Err(e);
323 },
324 }
325
326 Ok(())
327 },
328
329 WalletCommands::List => {
330 log_print!("📋 Listing all wallets...");
331
332 let wallet_manager = WalletManager::new()?;
333
334 match wallet_manager.list_wallets() {
335 Ok(wallets) =>
336 if wallets.is_empty() {
337 log_print!("{}", "No wallets found.".dimmed());
338 log_print!(
339 "Create a new wallet with: {}",
340 "quantus wallet create --name <name>".bright_green()
341 );
342 } else {
343 log_print!("Found {} wallet(s):\n", wallets.len());
344
345 for (i, wallet) in wallets.iter().enumerate() {
346 log_print!(
347 "{}. {}",
348 (i + 1).to_string().bright_yellow(),
349 wallet.name.bright_green()
350 );
351 log_print!(" Address: {}", wallet.address.bright_cyan());
352 log_print!(" Type: {}", wallet.key_type.bright_yellow());
353 log_print!(
354 " Created: {}",
355 wallet
356 .created_at
357 .format("%Y-%m-%d %H:%M:%S UTC")
358 .to_string()
359 .dimmed()
360 );
361 if i < wallets.len() - 1 {
362 log_print!();
363 }
364 }
365
366 log_print!(
367 "\n{}",
368 "💡 Use 'quantus wallet view --name <wallet>' to see full details"
369 .dimmed()
370 );
371 },
372 Err(e) => {
373 log_error!("{}", format!("❌ Failed to list wallets: {e}").red());
374 return Err(e);
375 },
376 }
377
378 Ok(())
379 },
380
381 WalletCommands::Delete { name, force } => {
382 log_print!("🗑️ Deleting wallet...");
383
384 let wallet_manager = WalletManager::new()?;
385
386 match wallet_manager.get_wallet(&name, None) {
388 Ok(Some(wallet_info)) => {
389 log_print!("Wallet to delete:");
391 log_print!(" Name: {}", wallet_info.name.bright_green());
392 log_print!(" Address: {}", wallet_info.address.bright_cyan());
393 log_print!(" Type: {}", wallet_info.key_type.bright_yellow());
394 log_print!(
395 " Created: {}",
396 wallet_info.created_at.format("%Y-%m-%d %H:%M:%S UTC").to_string().dimmed()
397 );
398
399 if !force {
401 log_print!("\n{}", "⚠️ This action cannot be undone!".bright_red());
402 log_print!("Type the wallet name to confirm deletion:");
403
404 print!("Confirm wallet name: ");
405 io::stdout().flush().unwrap();
406
407 let mut input = String::new();
408 io::stdin().read_line(&mut input).unwrap();
409 let input = input.trim();
410
411 if input != name {
412 log_print!(
413 "{}",
414 "❌ Wallet name doesn't match. Deletion cancelled.".red()
415 );
416 return Ok(());
417 }
418 }
419
420 match wallet_manager.delete_wallet(&name) {
422 Ok(true) => {
423 log_success!("✅ Wallet '{}' deleted successfully!", name);
424 },
425 Ok(false) => {
426 log_error!("{}", format!("❌ Wallet '{name}' was not found").red());
427 },
428 Err(e) => {
429 log_error!("{}", format!("❌ Failed to delete wallet: {e}").red());
430 return Err(e);
431 },
432 }
433 },
434 Ok(None) => {
435 log_error!("{}", format!("❌ Wallet '{name}' not found").red());
436 log_print!(
437 "Use {} to see available wallets",
438 "quantus wallet list".bright_green()
439 );
440 },
441 Err(e) => {
442 log_error!("{}", format!("❌ Failed to check wallet: {e}").red());
443 return Err(e);
444 },
445 }
446
447 Ok(())
448 },
449
450 WalletCommands::Nonce { address, wallet, password } => {
451 log_print!("🔢 Querying account nonce...");
452
453 let quantus_client = crate::chain::client::QuantusClient::new(node_url).await?;
454
455 let target_address = match (address, wallet) {
457 (Some(addr), _) => {
458 AccountId32::from_ss58check(&addr)
460 .map_err(|e| QuantusError::Generic(format!("Invalid address: {e:?}")))?;
461 addr
462 },
463 (None, Some(wallet_name)) => {
464 let keypair =
466 crate::wallet::load_keypair_from_wallet(&wallet_name, password, None)?;
467 keypair.to_account_id_ss58check()
468 },
469 (None, None) => {
470 unreachable!("Either --address or --wallet must be provided");
472 },
473 };
474
475 log_print!("Account: {}", target_address.bright_cyan());
476
477 match get_account_nonce(&quantus_client, &target_address).await {
478 Ok(nonce) => {
479 log_success!("Nonce: {}", nonce.to_string().bright_green());
480 },
481 Err(e) => {
482 log_print!("❌ Failed to get nonce: {}", e);
483 return Err(e);
484 },
485 }
486
487 Ok(())
488 },
489 }
490}