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 progress_spinner;
14pub mod recovery;
15pub mod reversible;
16pub mod runtime;
17pub mod scheduler;
18pub mod send;
19pub mod storage;
20pub mod system;
21pub mod tech_collective;
22pub mod wallet;
23
24#[derive(Subcommand, Debug)]
26pub enum Commands {
27 #[command(subcommand)]
29 Wallet(wallet::WalletCommands),
30
31 Send {
33 #[arg(short, long)]
35 to: String,
36
37 #[arg(short, long)]
39 amount: String,
40
41 #[arg(short, long)]
43 from: String,
44
45 #[arg(short, long)]
47 password: Option<String>,
48
49 #[arg(long)]
51 password_file: Option<String>,
52
53 #[arg(long)]
55 tip: Option<String>,
56
57 #[arg(long)]
59 nonce: Option<u32>,
60 },
61
62 #[command(subcommand)]
64 Batch(batch::BatchCommands),
65
66 #[command(subcommand)]
68 Reversible(reversible::ReversibleCommands),
69
70 #[command(subcommand)]
72 HighSecurity(high_security::HighSecurityCommands),
73
74 #[command(subcommand)]
76 Recovery(recovery::RecoveryCommands),
77
78 #[command(subcommand)]
80 Scheduler(scheduler::SchedulerCommands),
81
82 #[command(subcommand)]
84 Storage(storage::StorageCommands),
85
86 #[command(subcommand)]
88 TechCollective(tech_collective::TechCollectiveCommands),
89
90 #[command(subcommand)]
92 Runtime(runtime::RuntimeCommands),
93
94 Call {
96 #[arg(long)]
98 pallet: String,
99
100 #[arg(short, long)]
102 call: String,
103
104 #[arg(short, long)]
107 args: Option<String>,
108
109 #[arg(short, long)]
111 from: String,
112
113 #[arg(short, long)]
115 password: Option<String>,
116
117 #[arg(long)]
119 password_file: Option<String>,
120
121 #[arg(long)]
123 tip: Option<String>,
124
125 #[arg(long)]
127 offline: bool,
128
129 #[arg(long)]
131 call_data_only: bool,
132 },
133
134 Balance {
136 #[arg(short, long)]
138 address: String,
139 },
140
141 #[command(subcommand)]
143 Developer(DeveloperCommands),
144
145 Events {
147 #[arg(long)]
149 block: Option<u32>,
150
151 #[arg(long)]
153 block_hash: Option<String>,
154
155 #[arg(long)]
157 latest: bool,
158
159 #[arg(long)]
161 finalized: bool,
162
163 #[arg(long)]
165 pallet: Option<String>,
166
167 #[arg(long)]
169 raw: bool,
170
171 #[arg(long)]
173 no_decode: bool,
174 },
175
176 System {
178 #[arg(long)]
180 runtime: bool,
181
182 #[arg(long)]
184 metadata: bool,
185
186 #[arg(long)]
188 rpc_methods: bool,
189 },
190
191 Metadata {
193 #[arg(long)]
195 no_docs: bool,
196
197 #[arg(long)]
199 stats_only: bool,
200
201 #[arg(long)]
203 pallet: Option<String>,
204 },
205
206 Version,
208
209 CompatibilityCheck,
211
212 #[command(subcommand)]
214 Block(block::BlockCommands),
215}
216
217#[derive(Subcommand, Debug)]
219pub enum DeveloperCommands {
220 CreateTestWallets,
222}
223
224pub async fn execute_command(
226 command: Commands,
227 node_url: &str,
228 verbose: bool,
229) -> crate::error::Result<()> {
230 match command {
231 Commands::Wallet(wallet_cmd) => wallet::handle_wallet_command(wallet_cmd, node_url).await,
232 Commands::Send { from, to, amount, password, password_file, tip, nonce } =>
233 send::handle_send_command(
234 from,
235 to,
236 &amount,
237 node_url,
238 password,
239 password_file,
240 tip,
241 nonce,
242 )
243 .await,
244 Commands::Batch(batch_cmd) => batch::handle_batch_command(batch_cmd, node_url).await,
245 Commands::Reversible(reversible_cmd) =>
246 reversible::handle_reversible_command(reversible_cmd, node_url).await,
247 Commands::HighSecurity(hs_cmd) =>
248 high_security::handle_high_security_command(hs_cmd, node_url).await,
249 Commands::Recovery(recovery_cmd) =>
250 recovery::handle_recovery_command(recovery_cmd, node_url).await,
251 Commands::Scheduler(scheduler_cmd) =>
252 scheduler::handle_scheduler_command(scheduler_cmd, node_url).await,
253 Commands::Storage(storage_cmd) =>
254 storage::handle_storage_command(storage_cmd, node_url).await,
255 Commands::TechCollective(tech_collective_cmd) =>
256 tech_collective::handle_tech_collective_command(tech_collective_cmd, node_url).await,
257 Commands::Runtime(runtime_cmd) =>
258 runtime::handle_runtime_command(runtime_cmd, node_url).await,
259 Commands::Call {
260 pallet,
261 call,
262 args,
263 from,
264 password,
265 password_file,
266 tip,
267 offline,
268 call_data_only,
269 } =>
270 handle_generic_call_command(
271 pallet,
272 call,
273 args,
274 from,
275 password,
276 password_file,
277 tip,
278 offline,
279 call_data_only,
280 node_url,
281 )
282 .await,
283 Commands::Balance { address } => {
284 let quantus_client = crate::chain::client::QuantusClient::new(node_url).await?;
285
286 let resolved_address = common::resolve_address(&address)?;
288
289 let balance = send::get_balance(&quantus_client, &resolved_address).await?;
290 let formatted_balance =
291 send::format_balance_with_symbol(&quantus_client, balance).await?;
292 log_print!("๐ฐ Balance: {}", formatted_balance);
293 Ok(())
294 },
295 Commands::Developer(dev_cmd) => match dev_cmd {
296 DeveloperCommands::CreateTestWallets => {
297 let _ = crate::cli::handle_developer_command(DeveloperCommands::CreateTestWallets)
298 .await;
299 Ok(())
300 },
301 },
302 Commands::Events { block, block_hash, latest: _, finalized, pallet, raw, no_decode } =>
303 events::handle_events_command(
304 block, block_hash, finalized, pallet, raw, !no_decode, node_url,
305 )
306 .await,
307 Commands::System { runtime, metadata, rpc_methods } =>
308 if runtime || metadata || rpc_methods {
309 system::handle_system_extended_command(
310 node_url,
311 runtime,
312 metadata,
313 rpc_methods,
314 verbose,
315 )
316 .await
317 } else {
318 system::handle_system_command(node_url).await
319 },
320 Commands::Metadata { no_docs, stats_only, pallet } =>
321 metadata::handle_metadata_command(node_url, no_docs, stats_only, pallet).await,
322 Commands::Version => {
323 log_print!("CLI Version: Quantus CLI v{}", env!("CARGO_PKG_VERSION"));
324 Ok(())
325 },
326 Commands::CompatibilityCheck => handle_compatibility_check(node_url).await,
327 Commands::Block(block_cmd) => block::handle_block_command(block_cmd, node_url).await,
328 }
329}
330
331async fn handle_generic_call_command(
333 pallet: String,
334 call: String,
335 args: Option<String>,
336 from: String,
337 password: Option<String>,
338 password_file: Option<String>,
339 tip: Option<String>,
340 offline: bool,
341 call_data_only: bool,
342 node_url: &str,
343) -> crate::error::Result<()> {
344 if offline {
346 log_error!("โ Offline mode is not yet implemented");
347 log_print!("๐ก Currently only live submission is supported");
348 return Ok(());
349 }
350
351 if call_data_only {
352 log_error!("โ Call-data-only mode is not yet implemented");
353 log_print!("๐ก Currently only live submission is supported");
354 return Ok(());
355 }
356
357 let keypair = crate::wallet::load_keypair_from_wallet(&from, password, password_file)?;
358
359 let args_vec = if let Some(args_str) = args {
360 serde_json::from_str(&args_str).map_err(|e| {
361 crate::error::QuantusError::Generic(format!("Invalid JSON for arguments: {e}"))
362 })?
363 } else {
364 vec![]
365 };
366
367 generic_call::handle_generic_call(&pallet, &call, args_vec, &keypair, tip, node_url).await
368}
369
370pub async fn handle_developer_command(command: DeveloperCommands) -> crate::error::Result<()> {
372 match command {
373 DeveloperCommands::CreateTestWallets => {
374 use crate::wallet::WalletManager;
375
376 log_print!(
377 "๐งช {} Creating standard test wallets...",
378 "DEVELOPER".bright_magenta().bold()
379 );
380 log_print!("");
381
382 let wallet_manager = WalletManager::new()?;
383
384 let test_wallets = vec![
386 ("crystal_alice", "Alice's test wallet for development"),
387 ("crystal_bob", "Bob's test wallet for development"),
388 ("crystal_charlie", "Charlie's test wallet for development"),
389 ];
390
391 let mut created_count = 0;
392
393 for (name, description) in test_wallets {
394 log_verbose!("Creating wallet: {}", name.bright_green());
395
396 match wallet_manager.create_developer_wallet(name).await {
398 Ok(wallet_info) => {
399 log_success!("โ
Created {}", name.bright_green());
400 log_success!(" Address: {}", wallet_info.address.bright_cyan());
401 log_success!(" Description: {}", description.dimmed());
402 created_count += 1;
403 },
404 Err(e) => {
405 log_error!("โ Failed to create {}: {}", name.bright_red(), e);
406 },
407 }
408 }
409
410 log_print!("");
411 log_success!("๐ Test wallet creation complete!");
412 log_success!(" Created: {} wallets", created_count.to_string().bright_green());
413 log_print!("");
414 log_print!("๐ก {} You can now use these wallets:", "TIP".bright_blue().bold());
415 log_print!(" quantus send --from crystal_alice --to <address> --amount 1000");
416 log_print!(" quantus send --from crystal_bob --to <address> --amount 1000");
417 log_print!(" quantus send --from crystal_charlie --to <address> --amount 1000");
418 log_print!("");
419
420 Ok(())
421 },
422 }
423}
424
425async fn handle_compatibility_check(node_url: &str) -> crate::error::Result<()> {
427 log_print!("๐ Compatibility Check");
428 log_print!("๐ Connecting to: {}", node_url.bright_cyan());
429 log_print!("");
430
431 let quantus_client = crate::chain::client::QuantusClient::new(node_url).await?;
433
434 let runtime_version = runtime::get_runtime_version(quantus_client.client()).await?;
436
437 let chain_info = system::get_complete_chain_info(node_url).await?;
439
440 log_print!("๐ Version Information:");
441 log_print!(" โข CLI Version: {}", env!("CARGO_PKG_VERSION").bright_green());
442 log_print!(
443 " โข Runtime Spec Version: {}",
444 runtime_version.spec_version.to_string().bright_yellow()
445 );
446 log_print!(
447 " โข Runtime Impl Version: {}",
448 runtime_version.impl_version.to_string().bright_blue()
449 );
450 log_print!(
451 " โข Transaction Version: {}",
452 runtime_version.transaction_version.to_string().bright_magenta()
453 );
454
455 if let Some(name) = &chain_info.chain_name {
456 log_print!(" โข Chain Name: {}", name.bright_cyan());
457 }
458
459 log_print!("");
460
461 let is_compatible = crate::config::is_runtime_compatible(runtime_version.spec_version);
463
464 log_print!("๐ Compatibility Analysis:");
465 log_print!(" โข Supported Runtime Versions: {:?}", crate::config::COMPATIBLE_RUNTIME_VERSIONS);
466 log_print!(" โข Current Runtime Version: {}", runtime_version.spec_version);
467
468 if is_compatible {
469 log_success!("โ
COMPATIBLE - This CLI version supports the connected node");
470 log_print!(" โข All features should work correctly");
471 log_print!(" โข You can safely use all CLI commands");
472 } else {
473 log_error!("โ INCOMPATIBLE - This CLI version may not work with the connected node");
474 log_print!(" โข Some features may not work correctly");
475 log_print!(" โข Consider updating the CLI or connecting to a compatible node");
476 log_print!(" โข Supported versions: {:?}", crate::config::COMPATIBLE_RUNTIME_VERSIONS);
477 }
478
479 log_print!("");
480 log_print!("๐ก Tip: Use 'quantus version' for quick version check");
481 log_print!("๐ก Tip: Use 'quantus system --runtime' for detailed system info");
482
483 Ok(())
484}