1use crate::{log_error, log_print, log_success, log_verbose};
2use clap::Subcommand;
3use colored::Colorize;
4
5pub mod address_format;
6pub mod batch;
7pub mod block;
8pub mod common;
9pub mod events;
10pub mod generic_call;
11pub mod high_security;
12pub mod metadata;
13pub mod preimage;
14pub mod recovery;
15pub mod referenda;
16pub mod referenda_decode;
17pub mod reversible;
18pub mod runtime;
19pub mod scheduler;
20pub mod send;
21pub mod storage;
22pub mod system;
23pub mod tech_collective;
24pub mod tech_referenda;
25pub mod treasury;
26pub mod wallet;
27
28#[derive(Subcommand, Debug)]
30pub enum Commands {
31 #[command(subcommand)]
33 Wallet(wallet::WalletCommands),
34
35 Send {
37 #[arg(short, long)]
39 to: String,
40
41 #[arg(short, long)]
43 amount: String,
44
45 #[arg(short, long)]
47 from: String,
48
49 #[arg(short, long)]
51 password: Option<String>,
52
53 #[arg(long)]
55 password_file: Option<String>,
56
57 #[arg(long)]
59 tip: Option<String>,
60
61 #[arg(long)]
63 nonce: Option<u32>,
64 },
65
66 #[command(subcommand)]
68 Batch(batch::BatchCommands),
69
70 #[command(subcommand)]
72 Reversible(reversible::ReversibleCommands),
73
74 #[command(subcommand)]
76 HighSecurity(high_security::HighSecurityCommands),
77
78 #[command(subcommand)]
80 Recovery(recovery::RecoveryCommands),
81
82 #[command(subcommand)]
84 Scheduler(scheduler::SchedulerCommands),
85
86 #[command(subcommand)]
88 Storage(storage::StorageCommands),
89
90 #[command(subcommand)]
92 TechCollective(tech_collective::TechCollectiveCommands),
93
94 #[command(subcommand)]
96 Preimage(preimage::PreimageCommands),
97 #[command(subcommand)]
98 TechReferenda(tech_referenda::TechReferendaCommands),
99
100 #[command(subcommand)]
102 Referenda(referenda::ReferendaCommands),
103
104 #[command(subcommand)]
106 Treasury(treasury::TreasuryCommands),
107
108 #[command(subcommand)]
110 Runtime(runtime::RuntimeCommands),
111
112 Call {
114 #[arg(long)]
116 pallet: String,
117
118 #[arg(short, long)]
120 call: String,
121
122 #[arg(short, long)]
125 args: Option<String>,
126
127 #[arg(short, long)]
129 from: String,
130
131 #[arg(short, long)]
133 password: Option<String>,
134
135 #[arg(long)]
137 password_file: Option<String>,
138
139 #[arg(long)]
141 tip: Option<String>,
142
143 #[arg(long)]
145 offline: bool,
146
147 #[arg(long)]
149 call_data_only: bool,
150 },
151
152 Balance {
154 #[arg(short, long)]
156 address: String,
157 },
158
159 #[command(subcommand)]
161 Developer(DeveloperCommands),
162
163 Events {
165 #[arg(long)]
167 block: Option<u32>,
168
169 #[arg(long)]
171 block_hash: Option<String>,
172
173 #[arg(long)]
175 latest: bool,
176
177 #[arg(long)]
179 finalized: bool,
180
181 #[arg(long)]
183 pallet: Option<String>,
184
185 #[arg(long)]
187 raw: bool,
188
189 #[arg(long)]
191 no_decode: bool,
192 },
193
194 System {
196 #[arg(long)]
198 runtime: bool,
199
200 #[arg(long)]
202 metadata: bool,
203
204 #[arg(long)]
206 rpc_methods: bool,
207 },
208
209 Metadata {
211 #[arg(long)]
213 no_docs: bool,
214
215 #[arg(long)]
217 stats_only: bool,
218
219 #[arg(long)]
221 pallet: Option<String>,
222 },
223
224 Version,
226
227 CompatibilityCheck,
229
230 #[command(subcommand)]
232 Block(block::BlockCommands),
233}
234
235#[derive(Subcommand, Debug)]
237pub enum DeveloperCommands {
238 CreateTestWallets,
240}
241
242pub async fn execute_command(
244 command: Commands,
245 node_url: &str,
246 verbose: bool,
247 finalized: bool,
248) -> crate::error::Result<()> {
249 match command {
250 Commands::Wallet(wallet_cmd) => wallet::handle_wallet_command(wallet_cmd, node_url).await,
251 Commands::Send { from, to, amount, password, password_file, tip, nonce } =>
252 send::handle_send_command(
253 from,
254 to,
255 &amount,
256 node_url,
257 password,
258 password_file,
259 tip,
260 nonce,
261 finalized,
262 )
263 .await,
264 Commands::Batch(batch_cmd) =>
265 batch::handle_batch_command(batch_cmd, node_url, finalized).await,
266 Commands::Reversible(reversible_cmd) =>
267 reversible::handle_reversible_command(reversible_cmd, node_url, finalized).await,
268 Commands::HighSecurity(hs_cmd) =>
269 high_security::handle_high_security_command(hs_cmd, node_url, finalized).await,
270 Commands::Recovery(recovery_cmd) =>
271 recovery::handle_recovery_command(recovery_cmd, node_url, finalized).await,
272 Commands::Scheduler(scheduler_cmd) =>
273 scheduler::handle_scheduler_command(scheduler_cmd, node_url, finalized).await,
274 Commands::Storage(storage_cmd) =>
275 storage::handle_storage_command(storage_cmd, node_url, finalized).await,
276 Commands::TechCollective(tech_collective_cmd) =>
277 tech_collective::handle_tech_collective_command(
278 tech_collective_cmd,
279 node_url,
280 finalized,
281 )
282 .await,
283 Commands::Preimage(preimage_cmd) =>
284 preimage::handle_preimage_command(preimage_cmd, node_url, finalized).await,
285 Commands::TechReferenda(tech_referenda_cmd) =>
286 tech_referenda::handle_tech_referenda_command(tech_referenda_cmd, node_url, finalized)
287 .await,
288 Commands::Referenda(referenda_cmd) =>
289 referenda::handle_referenda_command(referenda_cmd, node_url, finalized).await,
290 Commands::Treasury(treasury_cmd) =>
291 treasury::handle_treasury_command(treasury_cmd, node_url, finalized).await,
292 Commands::Runtime(runtime_cmd) =>
293 runtime::handle_runtime_command(runtime_cmd, node_url, finalized).await,
294 Commands::Call {
295 pallet,
296 call,
297 args,
298 from,
299 password,
300 password_file,
301 tip,
302 offline,
303 call_data_only,
304 } =>
305 handle_generic_call_command(
306 pallet,
307 call,
308 args,
309 from,
310 password,
311 password_file,
312 tip,
313 offline,
314 call_data_only,
315 node_url,
316 finalized,
317 )
318 .await,
319 Commands::Balance { address } => {
320 let quantus_client = crate::chain::client::QuantusClient::new(node_url).await?;
321
322 let resolved_address = common::resolve_address(&address)?;
324
325 let balance = send::get_balance(&quantus_client, &resolved_address).await?;
326 let formatted_balance =
327 send::format_balance_with_symbol(&quantus_client, balance).await?;
328 log_print!("๐ฐ Balance: {}", formatted_balance);
329 Ok(())
330 },
331 Commands::Developer(dev_cmd) => match dev_cmd {
332 DeveloperCommands::CreateTestWallets => {
333 let _ = crate::cli::handle_developer_command(DeveloperCommands::CreateTestWallets)
334 .await;
335 Ok(())
336 },
337 },
338 Commands::Events { block, block_hash, latest: _, finalized, pallet, raw, no_decode } =>
339 events::handle_events_command(
340 block, block_hash, finalized, pallet, raw, !no_decode, node_url,
341 )
342 .await,
343 Commands::System { runtime, metadata, rpc_methods } => {
344 if runtime || metadata || rpc_methods {
345 system::handle_system_extended_command(
346 node_url,
347 runtime,
348 metadata,
349 rpc_methods,
350 verbose,
351 )
352 .await
353 } else {
354 system::handle_system_command(node_url).await
355 }
356 },
357 Commands::Metadata { no_docs, stats_only, pallet } =>
358 metadata::handle_metadata_command(node_url, no_docs, stats_only, pallet).await,
359 Commands::Version => {
360 log_print!("CLI Version: Quantus CLI v{}", env!("CARGO_PKG_VERSION"));
361 Ok(())
362 },
363 Commands::CompatibilityCheck => handle_compatibility_check(node_url).await,
364 Commands::Block(block_cmd) => block::handle_block_command(block_cmd, node_url).await,
365 }
366}
367
368#[allow(clippy::too_many_arguments)]
370async fn handle_generic_call_command(
371 pallet: String,
372 call: String,
373 args: Option<String>,
374 from: String,
375 password: Option<String>,
376 password_file: Option<String>,
377 tip: Option<String>,
378 offline: bool,
379 call_data_only: bool,
380 node_url: &str,
381 finalized: bool,
382) -> crate::error::Result<()> {
383 if offline {
385 log_error!("โ Offline mode is not yet implemented");
386 log_print!("๐ก Currently only live submission is supported");
387 return Ok(());
388 }
389
390 if call_data_only {
391 log_error!("โ Call-data-only mode is not yet implemented");
392 log_print!("๐ก Currently only live submission is supported");
393 return Ok(());
394 }
395
396 let keypair = crate::wallet::load_keypair_from_wallet(&from, password, password_file)?;
397
398 let args_vec = if let Some(args_str) = args {
399 serde_json::from_str(&args_str).map_err(|e| {
400 crate::error::QuantusError::Generic(format!("Invalid JSON for arguments: {e}"))
401 })?
402 } else {
403 vec![]
404 };
405
406 generic_call::handle_generic_call(&pallet, &call, args_vec, &keypair, tip, node_url, finalized)
407 .await
408}
409
410pub async fn handle_developer_command(command: DeveloperCommands) -> crate::error::Result<()> {
412 match command {
413 DeveloperCommands::CreateTestWallets => {
414 use crate::wallet::WalletManager;
415
416 log_print!(
417 "๐งช {} Creating standard test wallets...",
418 "DEVELOPER".bright_magenta().bold()
419 );
420 log_print!("");
421
422 let wallet_manager = WalletManager::new()?;
423
424 let test_wallets = vec![
426 ("crystal_alice", "Alice's test wallet for development"),
427 ("crystal_bob", "Bob's test wallet for development"),
428 ("crystal_charlie", "Charlie's test wallet for development"),
429 ];
430
431 let mut created_count = 0;
432
433 for (name, description) in test_wallets {
434 log_verbose!("Creating wallet: {}", name.bright_green());
435
436 match wallet_manager.create_developer_wallet(name).await {
438 Ok(wallet_info) => {
439 log_success!("โ
Created {}", name.bright_green());
440 log_success!(" Address: {}", wallet_info.address.bright_cyan());
441 log_success!(" Description: {}", description.dimmed());
442 created_count += 1;
443 },
444 Err(e) => {
445 log_error!("โ Failed to create {}: {}", name.bright_red(), e);
446 },
447 }
448 }
449
450 log_print!("");
451 log_success!("๐ Test wallet creation complete!");
452 log_success!(" Created: {} wallets", created_count.to_string().bright_green());
453 log_print!("");
454 log_print!("๐ก {} You can now use these wallets:", "TIP".bright_blue().bold());
455 log_print!(" quantus send --from crystal_alice --to <address> --amount 1000");
456 log_print!(" quantus send --from crystal_bob --to <address> --amount 1000");
457 log_print!(" quantus send --from crystal_charlie --to <address> --amount 1000");
458 log_print!("");
459
460 Ok(())
461 },
462 }
463}
464
465async fn handle_compatibility_check(node_url: &str) -> crate::error::Result<()> {
467 log_print!("๐ Compatibility Check");
468 log_print!("๐ Connecting to: {}", node_url.bright_cyan());
469 log_print!("");
470
471 let quantus_client = crate::chain::client::QuantusClient::new(node_url).await?;
473
474 let runtime_version = runtime::get_runtime_version(quantus_client.client()).await?;
476
477 let chain_info = system::get_complete_chain_info(node_url).await?;
479
480 log_print!("๐ Version Information:");
481 log_print!(" โข CLI Version: {}", env!("CARGO_PKG_VERSION").bright_green());
482 log_print!(
483 " โข Runtime Spec Version: {}",
484 runtime_version.spec_version.to_string().bright_yellow()
485 );
486 log_print!(
487 " โข Runtime Impl Version: {}",
488 runtime_version.impl_version.to_string().bright_blue()
489 );
490 log_print!(
491 " โข Transaction Version: {}",
492 runtime_version.transaction_version.to_string().bright_magenta()
493 );
494
495 if let Some(name) = &chain_info.chain_name {
496 log_print!(" โข Chain Name: {}", name.bright_cyan());
497 }
498
499 log_print!("");
500
501 let is_compatible = crate::config::is_runtime_compatible(runtime_version.spec_version);
503
504 log_print!("๐ Compatibility Analysis:");
505 log_print!(" โข Supported Runtime Versions: {:?}", crate::config::COMPATIBLE_RUNTIME_VERSIONS);
506 log_print!(" โข Current Runtime Version: {}", runtime_version.spec_version);
507
508 if is_compatible {
509 log_success!("โ
COMPATIBLE - This CLI version supports the connected node");
510 log_print!(" โข All features should work correctly");
511 log_print!(" โข You can safely use all CLI commands");
512 } else {
513 log_error!("โ INCOMPATIBLE - This CLI version may not work with the connected node");
514 log_print!(" โข Some features may not work correctly");
515 log_print!(" โข Consider updating the CLI or connecting to a compatible node");
516 log_print!(" โข Supported versions: {:?}", crate::config::COMPATIBLE_RUNTIME_VERSIONS);
517 }
518
519 log_print!("");
520 log_print!("๐ก Tip: Use 'quantus version' for quick version check");
521 log_print!("๐ก Tip: Use 'quantus system --runtime' for detailed system info");
522
523 Ok(())
524}