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).await,
276 Commands::TechCollective(tech_collective_cmd) =>
277 tech_collective::handle_tech_collective_command(tech_collective_cmd, node_url).await,
278 Commands::Preimage(preimage_cmd) =>
279 preimage::handle_preimage_command(preimage_cmd, node_url, finalized).await,
280 Commands::TechReferenda(tech_referenda_cmd) =>
281 tech_referenda::handle_tech_referenda_command(tech_referenda_cmd, node_url).await,
282 Commands::Referenda(referenda_cmd) =>
283 referenda::handle_referenda_command(referenda_cmd, node_url).await,
284 Commands::Treasury(treasury_cmd) =>
285 treasury::handle_treasury_command(treasury_cmd, node_url).await,
286 Commands::Runtime(runtime_cmd) =>
287 runtime::handle_runtime_command(runtime_cmd, node_url).await,
288 Commands::Call {
289 pallet,
290 call,
291 args,
292 from,
293 password,
294 password_file,
295 tip,
296 offline,
297 call_data_only,
298 } =>
299 handle_generic_call_command(
300 pallet,
301 call,
302 args,
303 from,
304 password,
305 password_file,
306 tip,
307 offline,
308 call_data_only,
309 node_url,
310 finalized,
311 )
312 .await,
313 Commands::Balance { address } => {
314 let quantus_client = crate::chain::client::QuantusClient::new(node_url).await?;
315
316 let resolved_address = common::resolve_address(&address)?;
318
319 let balance = send::get_balance(&quantus_client, &resolved_address).await?;
320 let formatted_balance =
321 send::format_balance_with_symbol(&quantus_client, balance).await?;
322 log_print!("๐ฐ Balance: {}", formatted_balance);
323 Ok(())
324 },
325 Commands::Developer(dev_cmd) => match dev_cmd {
326 DeveloperCommands::CreateTestWallets => {
327 let _ = crate::cli::handle_developer_command(DeveloperCommands::CreateTestWallets)
328 .await;
329 Ok(())
330 },
331 },
332 Commands::Events { block, block_hash, latest: _, finalized, pallet, raw, no_decode } =>
333 events::handle_events_command(
334 block, block_hash, finalized, pallet, raw, !no_decode, node_url,
335 )
336 .await,
337 Commands::System { runtime, metadata, rpc_methods } => {
338 if runtime || metadata || rpc_methods {
339 system::handle_system_extended_command(
340 node_url,
341 runtime,
342 metadata,
343 rpc_methods,
344 verbose,
345 )
346 .await
347 } else {
348 system::handle_system_command(node_url).await
349 }
350 },
351 Commands::Metadata { no_docs, stats_only, pallet } =>
352 metadata::handle_metadata_command(node_url, no_docs, stats_only, pallet).await,
353 Commands::Version => {
354 log_print!("CLI Version: Quantus CLI v{}", env!("CARGO_PKG_VERSION"));
355 Ok(())
356 },
357 Commands::CompatibilityCheck => handle_compatibility_check(node_url).await,
358 Commands::Block(block_cmd) => block::handle_block_command(block_cmd, node_url).await,
359 }
360}
361
362#[allow(clippy::too_many_arguments)]
364async fn handle_generic_call_command(
365 pallet: String,
366 call: String,
367 args: Option<String>,
368 from: String,
369 password: Option<String>,
370 password_file: Option<String>,
371 tip: Option<String>,
372 offline: bool,
373 call_data_only: bool,
374 node_url: &str,
375 finalized: bool,
376) -> crate::error::Result<()> {
377 if offline {
379 log_error!("โ Offline mode is not yet implemented");
380 log_print!("๐ก Currently only live submission is supported");
381 return Ok(());
382 }
383
384 if call_data_only {
385 log_error!("โ Call-data-only mode is not yet implemented");
386 log_print!("๐ก Currently only live submission is supported");
387 return Ok(());
388 }
389
390 let keypair = crate::wallet::load_keypair_from_wallet(&from, password, password_file)?;
391
392 let args_vec = if let Some(args_str) = args {
393 serde_json::from_str(&args_str).map_err(|e| {
394 crate::error::QuantusError::Generic(format!("Invalid JSON for arguments: {e}"))
395 })?
396 } else {
397 vec![]
398 };
399
400 generic_call::handle_generic_call(&pallet, &call, args_vec, &keypair, tip, node_url, finalized)
401 .await
402}
403
404pub async fn handle_developer_command(command: DeveloperCommands) -> crate::error::Result<()> {
406 match command {
407 DeveloperCommands::CreateTestWallets => {
408 use crate::wallet::WalletManager;
409
410 log_print!(
411 "๐งช {} Creating standard test wallets...",
412 "DEVELOPER".bright_magenta().bold()
413 );
414 log_print!("");
415
416 let wallet_manager = WalletManager::new()?;
417
418 let test_wallets = vec![
420 ("crystal_alice", "Alice's test wallet for development"),
421 ("crystal_bob", "Bob's test wallet for development"),
422 ("crystal_charlie", "Charlie's test wallet for development"),
423 ];
424
425 let mut created_count = 0;
426
427 for (name, description) in test_wallets {
428 log_verbose!("Creating wallet: {}", name.bright_green());
429
430 match wallet_manager.create_developer_wallet(name).await {
432 Ok(wallet_info) => {
433 log_success!("โ
Created {}", name.bright_green());
434 log_success!(" Address: {}", wallet_info.address.bright_cyan());
435 log_success!(" Description: {}", description.dimmed());
436 created_count += 1;
437 },
438 Err(e) => {
439 log_error!("โ Failed to create {}: {}", name.bright_red(), e);
440 },
441 }
442 }
443
444 log_print!("");
445 log_success!("๐ Test wallet creation complete!");
446 log_success!(" Created: {} wallets", created_count.to_string().bright_green());
447 log_print!("");
448 log_print!("๐ก {} You can now use these wallets:", "TIP".bright_blue().bold());
449 log_print!(" quantus send --from crystal_alice --to <address> --amount 1000");
450 log_print!(" quantus send --from crystal_bob --to <address> --amount 1000");
451 log_print!(" quantus send --from crystal_charlie --to <address> --amount 1000");
452 log_print!("");
453
454 Ok(())
455 },
456 }
457}
458
459async fn handle_compatibility_check(node_url: &str) -> crate::error::Result<()> {
461 log_print!("๐ Compatibility Check");
462 log_print!("๐ Connecting to: {}", node_url.bright_cyan());
463 log_print!("");
464
465 let quantus_client = crate::chain::client::QuantusClient::new(node_url).await?;
467
468 let runtime_version = runtime::get_runtime_version(quantus_client.client()).await?;
470
471 let chain_info = system::get_complete_chain_info(node_url).await?;
473
474 log_print!("๐ Version Information:");
475 log_print!(" โข CLI Version: {}", env!("CARGO_PKG_VERSION").bright_green());
476 log_print!(
477 " โข Runtime Spec Version: {}",
478 runtime_version.spec_version.to_string().bright_yellow()
479 );
480 log_print!(
481 " โข Runtime Impl Version: {}",
482 runtime_version.impl_version.to_string().bright_blue()
483 );
484 log_print!(
485 " โข Transaction Version: {}",
486 runtime_version.transaction_version.to_string().bright_magenta()
487 );
488
489 if let Some(name) = &chain_info.chain_name {
490 log_print!(" โข Chain Name: {}", name.bright_cyan());
491 }
492
493 log_print!("");
494
495 let is_compatible = crate::config::is_runtime_compatible(runtime_version.spec_version);
497
498 log_print!("๐ Compatibility Analysis:");
499 log_print!(" โข Supported Runtime Versions: {:?}", crate::config::COMPATIBLE_RUNTIME_VERSIONS);
500 log_print!(" โข Current Runtime Version: {}", runtime_version.spec_version);
501
502 if is_compatible {
503 log_success!("โ
COMPATIBLE - This CLI version supports the connected node");
504 log_print!(" โข All features should work correctly");
505 log_print!(" โข You can safely use all CLI commands");
506 } else {
507 log_error!("โ INCOMPATIBLE - This CLI version may not work with the connected node");
508 log_print!(" โข Some features may not work correctly");
509 log_print!(" โข Consider updating the CLI or connecting to a compatible node");
510 log_print!(" โข Supported versions: {:?}", crate::config::COMPATIBLE_RUNTIME_VERSIONS);
511 }
512
513 log_print!("");
514 log_print!("๐ก Tip: Use 'quantus version' for quick version check");
515 log_print!("๐ก Tip: Use 'quantus system --runtime' for detailed system info");
516
517 Ok(())
518}