1use crate::{
4 chain::{client::ChainConfig, quantus_subxt},
5 cli::{address_format::QuantusSS58, progress_spinner::wait_for_tx_confirmation},
6 error::QuantusError,
7 log_error, log_print, log_success, log_verbose,
8};
9use clap::Subcommand;
10use codec::{Decode, Encode};
11use colored::Colorize;
12use serde::Deserialize;
13use sp_core::{
14 crypto::{AccountId32, Ss58Codec},
15 twox_128,
16};
17use std::{collections::BTreeMap, str::FromStr};
18use subxt::OnlineClient;
19
20#[derive(Decode, Debug, Deserialize, Clone, PartialEq, Eq)]
22pub struct AccountData {
23 pub free: u128,
24 pub reserved: u128,
25 pub frozen: u128,
26 pub flags: u128,
27}
28
29#[derive(Decode, Debug, Deserialize, Clone, PartialEq, Eq)]
31pub struct AccountInfo {
32 pub nonce: u32,
33 pub consumers: u32,
34 pub providers: u32,
35 pub sufficients: u32,
36 pub data: AccountData,
37}
38
39fn validate_pallet_exists(
41 client: &OnlineClient<ChainConfig>,
42 pallet_name: &str,
43) -> crate::error::Result<()> {
44 let metadata = client.metadata();
45 metadata.pallet_by_name(pallet_name).ok_or_else(|| {
46 QuantusError::Generic(format!(
47 "Pallet '{}' not found in chain metadata. Available pallets: {}",
48 pallet_name,
49 quantus_subxt::api::PALLETS.join(", ")
50 ))
51 })?;
52 Ok(())
53}
54
55#[derive(Subcommand, Debug)]
57pub enum StorageCommands {
58 Get {
65 #[arg(long, required_unless_present = "storage_key")]
67 pallet: Option<String>,
68
69 #[arg(long, required_unless_present = "storage_key")]
71 name: Option<String>,
72
73 #[arg(long)]
75 block: Option<String>,
76
77 #[arg(long)]
80 decode_as: Option<String>,
81
82 #[arg(long, conflicts_with = "storage_key")]
84 key: Option<String>,
85
86 #[arg(long, requires("key"))]
88 key_type: Option<String>,
89
90 #[arg(long, conflicts_with = "storage_key")]
92 count: bool,
93
94 #[arg(long, conflicts_with_all = &["pallet", "name", "key", "count"])]
96 storage_key: Option<String>,
97 },
98 List {
102 #[arg(long)]
104 pallet: String,
105
106 #[arg(long)]
108 names_only: bool,
109 },
110 ListPallets {
114 #[arg(long)]
116 with_counts: bool,
117 },
118 Stats {
122 #[arg(long)]
124 pallet: Option<String>,
125
126 #[arg(long)]
128 detailed: bool,
129 },
130 Iterate {
134 #[arg(long)]
136 pallet: String,
137
138 #[arg(long)]
140 name: String,
141
142 #[arg(long, default_value = "10")]
144 limit: u32,
145
146 #[arg(long)]
148 decode_as: Option<String>,
149
150 #[arg(long)]
152 block: Option<String>,
153 },
154
155 Set {
161 #[arg(long)]
163 pallet: String,
164
165 #[arg(long)]
167 name: String,
168
169 #[arg(long)]
171 value: String,
172
173 #[arg(long)]
175 r#type: Option<String>,
176
177 #[arg(long)]
179 wallet: String,
180
181 #[arg(long)]
183 password: Option<String>,
184
185 #[arg(long)]
187 password_file: Option<String>,
188 },
189}
190
191pub async fn resolve_block_hash(
193 quantus_client: &crate::chain::client::QuantusClient,
194 block_identifier: &str,
195) -> crate::error::Result<subxt::utils::H256> {
196 if block_identifier.starts_with("0x") {
197 subxt::utils::H256::from_str(block_identifier)
199 .map_err(|e| QuantusError::Generic(format!("Invalid block hash format: {e}")))
200 } else {
201 let block_number = block_identifier.parse::<u32>().map_err(|e| {
203 QuantusError::Generic(format!("Invalid block number '{block_identifier}': {e}"))
204 })?;
205
206 log_verbose!("🔍 Converting block number {} to hash...", block_number);
207
208 use jsonrpsee::core::client::ClientT;
209 let block_hash: subxt::utils::H256 = quantus_client
210 .rpc_client()
211 .request::<subxt::utils::H256, [u32; 1]>("chain_getBlockHash", [block_number])
212 .await
213 .map_err(|e| {
214 QuantusError::NetworkError(format!(
215 "Failed to fetch block hash for block {block_number}: {e:?}"
216 ))
217 })?;
218
219 log_verbose!("📦 Block {} hash: {:?}", block_number, block_hash);
220 Ok(block_hash)
221 }
222}
223
224pub async fn get_storage_raw(
226 quantus_client: &crate::chain::client::QuantusClient,
227 key: Vec<u8>,
228) -> crate::error::Result<Option<Vec<u8>>> {
229 let latest_block_hash = quantus_client.get_latest_block().await?;
231
232 let storage_at = quantus_client.client().storage().at(latest_block_hash);
233
234 let result = storage_at.fetch_raw(key).await?;
235
236 Ok(result)
237}
238
239pub async fn get_storage_raw_at_block(
241 quantus_client: &crate::chain::client::QuantusClient,
242 key: Vec<u8>,
243 block_hash: subxt::utils::H256,
244) -> crate::error::Result<Option<Vec<u8>>> {
245 log_verbose!("🔍 Querying storage at block: {:?}", block_hash);
246
247 let storage_at = quantus_client.client().storage().at(block_hash);
248
249 let result = storage_at.fetch_raw(key).await?;
250
251 Ok(result)
252}
253
254pub async fn set_storage_value(
256 quantus_client: &crate::chain::client::QuantusClient,
257 from_keypair: &crate::wallet::QuantumKeyPair,
258 storage_key: Vec<u8>,
259 value_bytes: Vec<u8>,
260) -> crate::error::Result<subxt::utils::H256> {
261 log_verbose!("✍️ Creating set_storage transaction...");
262
263 let set_storage_call =
265 quantus_subxt::api::Call::System(quantus_subxt::api::system::Call::set_storage {
266 items: vec![(storage_key, value_bytes)],
267 });
268
269 let sudo_call = quantus_subxt::api::tx().sudo().sudo(set_storage_call);
271
272 let tx_hash =
273 crate::cli::common::submit_transaction(quantus_client, from_keypair, sudo_call, None)
274 .await?;
275
276 log_verbose!("📋 Set storage transaction submitted: {:?}", tx_hash);
277
278 Ok(tx_hash)
279}
280
281pub async fn list_storage_items(
283 quantus_client: &crate::chain::client::QuantusClient,
284 pallet_name: &str,
285 names_only: bool,
286) -> crate::error::Result<()> {
287 log_print!("📋 Listing storage items for pallet: {}", pallet_name.bright_green());
288
289 validate_pallet_exists(quantus_client.client(), pallet_name)?;
291
292 let metadata = quantus_client.client().metadata();
293 let pallet = metadata.pallet_by_name(pallet_name).unwrap();
294
295 if let Some(storage_metadata) = pallet.storage() {
296 let entries = storage_metadata.entries();
297 log_print!("Found {} storage items: \n", entries.len());
298
299 for (index, entry) in entries.iter().enumerate() {
300 log_print!(
301 "{}. {}",
302 (index + 1).to_string().bright_yellow(),
303 entry.name().bright_cyan()
304 );
305
306 if !names_only {
307 log_print!(" Type: {:?}", entry.entry_type());
308 if !entry.docs().is_empty() {
309 log_print!(" Docs: {}", entry.docs().join(" ").dimmed());
310 }
311 log_print!("");
312 }
313 }
314 } else {
315 log_print!("❌ Pallet '{}' has no storage items.", pallet_name.bright_red());
316 }
317
318 Ok(())
319}
320
321pub async fn list_pallets_with_storage(
323 quantus_client: &crate::chain::client::QuantusClient,
324 with_counts: bool,
325) -> crate::error::Result<()> {
326 log_print!("🏛️ Listing all pallets with storage:");
327 log_print!("");
328
329 let metadata = quantus_client.client().metadata();
330 let pallets: Vec<_> = metadata.pallets().collect();
331
332 let mut storage_pallets = BTreeMap::new();
333
334 for pallet in pallets {
335 if let Some(storage_metadata) = pallet.storage() {
336 let entry_count = storage_metadata.entries().len();
337 storage_pallets.insert(pallet.name(), entry_count);
338 }
339 }
340
341 if storage_pallets.is_empty() {
342 log_print!("❌ No pallets with storage found.");
343 return Ok(());
344 }
345
346 for (index, (pallet_name, count)) in storage_pallets.iter().enumerate() {
347 if with_counts {
348 log_print!(
349 "{}. {} ({} items)",
350 (index + 1).to_string().bright_yellow(),
351 pallet_name.bright_green(),
352 count.to_string().bright_blue()
353 );
354 } else {
355 log_print!(
356 "{}. {}",
357 (index + 1).to_string().bright_yellow(),
358 pallet_name.bright_green()
359 );
360 }
361 }
362
363 log_print!("");
364 log_print!("Total: {} pallets with storage", storage_pallets.len().to_string().bright_green());
365
366 Ok(())
367}
368
369pub async fn show_storage_stats(
371 quantus_client: &crate::chain::client::QuantusClient,
372 pallet_name: Option<String>,
373 detailed: bool,
374) -> crate::error::Result<()> {
375 log_print!("📊 Storage size statistics: \n");
376
377 let metadata = quantus_client.client().metadata();
378
379 if let Some(pallet) = pallet_name {
380 validate_pallet_exists(quantus_client.client(), &pallet)?;
382 let pallet_meta = metadata.pallet_by_name(&pallet).unwrap();
383
384 if let Some(storage_metadata) = pallet_meta.storage() {
385 let entries = storage_metadata.entries();
386 log_print!("Pallet: {}", pallet.bright_green());
387 log_print!("Storage items: {}", entries.len().to_string().bright_blue());
388
389 if detailed {
390 log_print!("");
391 log_print!("Items:");
392 for (index, entry) in entries.iter().enumerate() {
393 log_print!(
394 " {}. {} - {:?}",
395 (index + 1).to_string().dimmed(),
396 entry.name().bright_cyan(),
397 entry.entry_type()
398 );
399 }
400 }
401 } else {
402 log_print!("❌ Pallet '{}' has no storage items.", pallet.bright_red());
403 }
404 } else {
405 let pallets: Vec<_> = metadata.pallets().collect();
407 let mut total_storage_items = 0;
408 let mut pallets_with_storage = 0;
409
410 let mut pallet_stats = Vec::new();
411
412 for pallet in pallets {
413 if let Some(storage_metadata) = pallet.storage() {
414 let entry_count = storage_metadata.entries().len();
415 total_storage_items += entry_count;
416 pallets_with_storage += 1;
417 pallet_stats.push((pallet.name(), entry_count));
418 }
419 }
420
421 log_print!("Total pallets: {}", metadata.pallets().len().to_string().bright_blue());
422 log_print!("Pallets with storage: {}", pallets_with_storage.to_string().bright_green());
423 log_print!("Total storage items: {}", total_storage_items.to_string().bright_yellow());
424
425 if detailed && !pallet_stats.is_empty() {
426 log_print!("");
427 log_print!("Per-pallet breakdown:");
428
429 pallet_stats.sort_by(|a, b| b.1.cmp(&a.1));
431
432 for (pallet_name, count) in pallet_stats {
433 log_print!(
434 " {} - {} items",
435 pallet_name.bright_cyan(),
436 count.to_string().bright_blue()
437 );
438 }
439 }
440 }
441
442 Ok(())
443}
444
445pub async fn count_storage_entries(
447 quantus_client: &crate::chain::client::QuantusClient,
448 pallet_name: &str,
449 storage_name: &str,
450 block_hash: subxt::utils::H256,
451) -> crate::error::Result<u32> {
452 let mut prefix = twox_128(pallet_name.as_bytes()).to_vec();
454 prefix.extend(&twox_128(storage_name.as_bytes()));
455
456 log_verbose!("🔑 Storage prefix for counting: 0x{}", hex::encode(&prefix));
457
458 use jsonrpsee::core::client::ClientT;
459
460 let block_hash_str = format!("{block_hash:#x}");
461 let prefix_hex = format!("0x{}", hex::encode(&prefix));
462 let page_size = 1000u32; let mut total_count = 0u32;
464 let mut start_key: Option<String> = None;
465
466 loop {
467 let keys: Vec<String> = quantus_client
469 .rpc_client()
470 .request::<Vec<String>, (String, u32, Option<String>, Option<String>)>(
471 "state_getKeysPaged",
472 (
473 prefix_hex.clone(), page_size, start_key.clone(), Some(block_hash_str.clone()), ),
478 )
479 .await
480 .map_err(|e| {
481 QuantusError::NetworkError(format!(
482 "Failed to fetch storage keys at block {block_hash:?}: {e:?}"
483 ))
484 })?;
485
486 let keys_count = keys.len() as u32;
487 total_count += keys_count;
488
489 log_verbose!("📊 Fetched {} keys (total: {})", keys_count, total_count);
490
491 if keys_count < page_size {
493 break;
494 }
495
496 start_key = keys.last().cloned();
498 if start_key.is_none() {
499 break;
500 }
501 }
502
503 Ok(total_count)
504}
505
506pub async fn iterate_storage_entries(
508 quantus_client: &crate::chain::client::QuantusClient,
509 pallet_name: &str,
510 storage_name: &str,
511 limit: u32,
512 decode_as: Option<String>,
513 block_identifier: Option<String>,
514) -> crate::error::Result<()> {
515 log_print!(
516 "🔄 Iterating storage {}::{} (limit: {})",
517 pallet_name.bright_green(),
518 storage_name.bright_cyan(),
519 limit.to_string().bright_yellow()
520 );
521
522 validate_pallet_exists(quantus_client.client(), pallet_name)?;
524
525 let block_hash = if let Some(block_id) = block_identifier {
527 resolve_block_hash(quantus_client, &block_id).await?
528 } else {
529 quantus_client.get_latest_block().await?
530 };
531
532 log_verbose!("📦 Using block: {:?}", block_hash);
533
534 let metadata = quantus_client.client().metadata();
536 let pallet = metadata.pallet_by_name(pallet_name).unwrap();
537
538 if let Some(storage_metadata) = pallet.storage() {
539 if let Some(entry) = storage_metadata.entry_by_name(storage_name) {
540 log_print!("📝 Storage type: {:?}", entry.entry_type());
541 if !entry.docs().is_empty() {
542 log_print!("📖 Docs: {}", entry.docs().join(" ").dimmed());
543 }
544 }
545 }
546
547 log_print!("🔢 Counting storage entries...");
549 let total_count =
550 count_storage_entries(quantus_client, pallet_name, storage_name, block_hash).await?;
551
552 log_success!(
553 "📊 Total entries in {}::{}: {}",
554 pallet_name.bright_green(),
555 storage_name.bright_cyan(),
556 total_count.to_string().bright_yellow()
557 );
558
559 if limit == 0 {
561 return Ok(());
562 }
563
564 let mut prefix = twox_128(pallet_name.as_bytes()).to_vec();
566 prefix.extend(&twox_128(storage_name.as_bytes()));
567
568 log_verbose!("🔑 Storage prefix: 0x{}", hex::encode(&prefix));
569
570 use jsonrpsee::core::client::ClientT;
572
573 let block_hash_str = format!("{block_hash:#x}");
574 let keys: Vec<String> = quantus_client
575 .rpc_client()
576 .request::<Vec<String>, (String, u32, Option<String>, Option<String>)>(
577 "state_getKeysPaged",
578 (
579 format!("0x{}", hex::encode(&prefix)),
580 limit, None, Some(block_hash_str), ),
584 )
585 .await
586 .map_err(|e| {
587 QuantusError::NetworkError(format!(
588 "Failed to fetch storage keys at block {block_hash:?}: {e:?}"
589 ))
590 })?;
591
592 if keys.is_empty() {
593 log_print!("❌ No entries found.");
594 return Ok(());
595 }
596
597 log_print!("📋 First {} entries:", keys.len().min(limit as usize));
598 log_print!("");
599
600 for (index, key) in keys.iter().take(limit as usize).enumerate() {
602 log_print!("{}. Key: {}", (index + 1).to_string().bright_yellow(), key.dimmed());
603
604 if index < 3 && decode_as.is_some() {
606 if let Ok(key_bytes) = hex::decode(key.strip_prefix("0x").unwrap_or(key)) {
607 if let Ok(Some(value_bytes)) =
608 get_storage_raw_at_block(quantus_client, key_bytes, block_hash).await
609 {
610 if let Some(ref decode_type) = decode_as {
611 match decode_storage_value(&value_bytes, decode_type) {
612 Ok(decoded_value) =>
613 log_print!(" Value: {}", decoded_value.bright_green()),
614 Err(_) => log_print!(
615 " Value: 0x{} (raw)",
616 hex::encode(&value_bytes).dimmed()
617 ),
618 }
619 }
620 }
621 }
622 }
623 }
624
625 if total_count > limit {
626 log_print!("");
627 log_print!(
628 "... and {} more entries (use --limit 0 to just count)",
629 (total_count - limit).to_string().bright_blue()
630 );
631 }
632
633 Ok(())
634}
635
636fn decode_storage_value(value_bytes: &[u8], type_str: &str) -> crate::error::Result<String> {
638 match type_str.to_lowercase().as_str() {
639 "u32" => match u32::decode(&mut &value_bytes[..]) {
640 Ok(decoded_value) => Ok(decoded_value.to_string()),
641 Err(e) => Err(QuantusError::Generic(format!("Failed to decode as u32: {e}"))),
642 },
643 "u64" | "moment" => match u64::decode(&mut &value_bytes[..]) {
644 Ok(decoded_value) => Ok(decoded_value.to_string()),
645 Err(e) => Err(QuantusError::Generic(format!("Failed to decode as u64: {e}"))),
646 },
647 "u128" | "balance" => match u128::decode(&mut &value_bytes[..]) {
648 Ok(decoded_value) => Ok(decoded_value.to_string()),
649 Err(e) => Err(QuantusError::Generic(format!("Failed to decode as u128: {e}"))),
650 },
651 "accountid" | "accountid32" => match AccountId32::decode(&mut &value_bytes[..]) {
652 Ok(account_id) => Ok(account_id.to_quantus_ss58()),
653 Err(e) => Err(QuantusError::Generic(format!("Failed to decode as AccountId32: {e}"))),
654 },
655 "accountinfo" => match AccountInfo::decode(&mut &value_bytes[..]) {
656 Ok(account_info) => Ok(format!("{account_info:#?}")),
657 Err(e) => Err(QuantusError::Generic(format!("Failed to decode as AccountInfo: {e}"))),
658 },
659 _ => Err(QuantusError::Generic(format!(
660 "Unsupported type for decoding: {type_str}. Supported types: u32, u64, moment, u128, balance, accountid, accountinfo"
661 ))),
662 }
663}
664
665async fn get_storage_by_storage_key(
667 quantus_client: &crate::chain::client::QuantusClient,
668 storage_key: String,
669 block: Option<String>,
670 decode_as: Option<String>,
671) -> crate::error::Result<()> {
672 log_print!("🗄️ Storage");
673
674 let encoded_storage_key = encode_storage_key(&storage_key, "raw")?;
675
676 let result = if let Some(block_id) = block {
677 let block_hash = resolve_block_hash(quantus_client, &block_id).await?;
678 get_storage_raw_at_block(quantus_client, encoded_storage_key, block_hash).await?
679 } else {
680 get_storage_raw(quantus_client, encoded_storage_key).await?
681 };
682
683 if let Some(value_bytes) = result {
684 log_success!("Raw Value: 0x{}", hex::encode(&value_bytes).bright_yellow());
685 if let Some(type_str) = decode_as {
686 log_print!("Attempting to decode as {}...", type_str.bright_cyan());
687 match decode_storage_value(&value_bytes, &type_str) {
688 Ok(decoded_value) =>
689 log_success!("Decoded Value: {}", decoded_value.bright_green()),
690 Err(e) => log_error!("{}", e),
691 }
692 }
693 } else {
694 log_print!("{}", "No value found at this storage location.".dimmed());
695 }
696
697 Ok(())
698}
699
700async fn get_storage_by_parts(
702 quantus_client: &crate::chain::client::QuantusClient,
703 pallet: String,
704 name: String,
705 key: Option<String>,
706 key_type: Option<String>,
707 block: Option<String>,
708 decode_as: Option<String>,
709 count: bool,
710) -> crate::error::Result<()> {
711 if let Some(block_value) = &block {
712 log_print!(
713 "🔎 Getting storage for {}::{} at block {}",
714 pallet.bright_green(),
715 name.bright_cyan(),
716 block_value.bright_yellow()
717 );
718 } else {
719 log_print!(
720 "🔎 Getting storage for {}::{} (latest block)",
721 pallet.bright_green(),
722 name.bright_cyan()
723 );
724 }
725
726 if let Some(key_value) = &key {
727 log_print!("🔑 With key: {}", key_value.bright_yellow());
728 }
729
730 validate_pallet_exists(quantus_client.client(), &pallet)?;
731
732 let block_hash = if let Some(block_id) = &block {
733 resolve_block_hash(quantus_client, block_id).await?
734 } else {
735 quantus_client.get_latest_block().await?
736 };
737
738 let entry_count = count_storage_entries(quantus_client, &pallet, &name, block_hash).await?;
739 let is_storage_value = entry_count == 1;
740
741 let should_count = count || (key.is_none() && !is_storage_value);
742
743 if should_count {
744 log_print!("🔢 Counting all entries in {}::{}", pallet.bright_green(), name.bright_cyan());
745
746 let block_display = if let Some(ref block_id) = block {
747 format!(" at block {}", block_id.bright_yellow())
748 } else {
749 " (latest)".to_string()
750 };
751
752 log_success!(
753 "👥 Total entries{}: {}",
754 block_display,
755 entry_count.to_string().bright_green().bold()
756 );
757 } else {
758 let mut storage_key = twox_128(pallet.as_bytes()).to_vec();
759 storage_key.extend(&twox_128(name.as_bytes()));
760
761 if let Some(key_value) = &key {
762 if let Some(key_type_str) = &key_type {
763 let key_bytes = encode_storage_key(key_value, key_type_str)?;
764 storage_key.extend(key_bytes);
765 } else {
766 log_error!("Key type (--key-type) is required when using --key parameter");
767 return Ok(());
768 }
769 } else if !is_storage_value {
770 log_print!("🔢 This is a storage map with {} entries. Use --key to get a specific value or omit --key to count all entries.", entry_count);
771 return Ok(());
772 }
773
774 let result = get_storage_raw_at_block(quantus_client, storage_key, block_hash).await?;
775
776 if let Some(value_bytes) = result {
777 log_success!("Raw Value: 0x{}", hex::encode(&value_bytes).bright_yellow());
778
779 if let Some(type_str) = decode_as {
780 log_print!("Attempting to decode as {}...", type_str.bright_cyan());
781 match decode_storage_value(&value_bytes, &type_str) {
782 Ok(decoded_value) =>
783 log_success!("Decoded Value: {}", decoded_value.bright_green()),
784 Err(e) => log_error!("{}", e),
785 }
786 }
787 } else {
788 log_print!("{}", "No value found at this storage location.".dimmed());
789 }
790 }
791
792 Ok(())
793}
794
795pub async fn handle_storage_command(
797 command: StorageCommands,
798 node_url: &str,
799) -> crate::error::Result<()> {
800 log_print!("🗄️ Storage");
801
802 let quantus_client = crate::chain::client::QuantusClient::new(node_url).await?;
803
804 match command {
805 StorageCommands::Get {
806 pallet,
807 name,
808 block,
809 decode_as,
810 key,
811 key_type,
812 count,
813 storage_key,
814 } => {
815 if let Some(s_key) = storage_key {
816 get_storage_by_storage_key(&quantus_client, s_key, block, decode_as).await
817 } else {
818 get_storage_by_parts(
820 &quantus_client,
821 pallet.unwrap(),
822 name.unwrap(),
823 key,
824 key_type,
825 block,
826 decode_as,
827 count,
828 )
829 .await
830 }
831 },
832 StorageCommands::List { pallet, names_only } =>
833 list_storage_items(&quantus_client, &pallet, names_only).await,
834 StorageCommands::ListPallets { with_counts } =>
835 list_pallets_with_storage(&quantus_client, with_counts).await,
836 StorageCommands::Stats { pallet, detailed } =>
837 show_storage_stats(&quantus_client, pallet, detailed).await,
838 StorageCommands::Iterate { pallet, name, limit, decode_as, block } =>
839 iterate_storage_entries(&quantus_client, &pallet, &name, limit, decode_as, block).await,
840
841 StorageCommands::Set { pallet, name, value, wallet, password, password_file, r#type } => {
842 log_print!("✍️ Setting storage for {}::{}", pallet.bright_green(), name.bright_cyan());
843 log_print!("\n{}", "🛑 This is a SUDO operation!".bright_red().bold());
844
845 validate_pallet_exists(quantus_client.client(), &pallet)?;
847
848 let keypair =
850 crate::wallet::load_keypair_from_wallet(&wallet, password, password_file)?;
851 log_verbose!("🔐 Using wallet: {}", wallet.bright_green());
852
853 let value_bytes = match r#type.as_deref() {
855 Some("u64") | Some("moment") => value
856 .parse::<u64>()
857 .map_err(|e| QuantusError::Generic(format!("Invalid u64 value: {e}")))?
858 .encode(),
859 Some("u128") | Some("balance") => value
860 .parse::<u128>()
861 .map_err(|e| QuantusError::Generic(format!("Invalid u128 value: {e}")))?
862 .encode(),
863 Some("accountid") | Some("accountid32") => AccountId32::from_ss58check(&value)
864 .map_err(|e| QuantusError::Generic(format!("Invalid AccountId value: {e:?}")))?
865 .encode(),
866 None => {
867 if value.starts_with("0x") && value.len() == 66 {
870 let h256_value = subxt::utils::H256::from_str(&value).map_err(|e| {
873 QuantusError::Generic(format!("Invalid H256 value: {e}"))
874 })?;
875 h256_value.0.to_vec()
876 } else {
877 let value_hex = value.strip_prefix("0x").unwrap_or(&value);
879 hex::decode(value_hex)
880 .map_err(|e| QuantusError::Generic(format!("Invalid hex value: {e}")))?
881 }
882 },
883 Some(unsupported) =>
884 return Err(QuantusError::Generic(format!(
885 "Unsupported type for --type: {unsupported}"
886 ))),
887 };
888
889 log_verbose!("Encoded value bytes: 0x{}", hex::encode(&value_bytes).dimmed());
890
891 let storage_key = {
893 let mut key = twox_128(pallet.as_bytes()).to_vec();
894 key.extend(&twox_128(name.as_bytes()));
895 key
896 };
897
898 let tx_hash =
900 set_storage_value(&quantus_client, &keypair, storage_key, value_bytes).await?;
901
902 log_print!(
903 "✅ {} Set storage transaction submitted! Hash: {:?}",
904 "SUCCESS".bright_green().bold(),
905 tx_hash
906 );
907
908 let success = wait_for_tx_confirmation(quantus_client.client(), tx_hash).await?;
909
910 if success {
911 log_success!(
912 "🎉 {} Set storage transaction confirmed!",
913 "FINISHED".bright_green().bold()
914 );
915 } else {
916 log_error!("Transaction failed!");
917 }
918
919 Ok(())
920 },
921 }
922}
923
924fn encode_storage_key(key_value: &str, key_type: &str) -> crate::error::Result<Vec<u8>> {
926 use codec::Encode;
927 use sp_core::crypto::{AccountId32 as SpAccountId32, Ss58Codec};
928
929 match key_type.to_lowercase().as_str() {
930 "accountid" | "accountid32" => {
931 let account_id = SpAccountId32::from_ss58check(key_value).map_err(|e| {
932 crate::error::QuantusError::Generic(format!("Invalid AccountId: {e:?}"))
933 })?;
934 Ok(account_id.encode())
935 },
936 "u64" => {
937 let value = key_value
938 .parse::<u64>()
939 .map_err(|e| crate::error::QuantusError::Generic(format!("Invalid u64: {e}")))?;
940 Ok(value.encode())
941 },
942 "u128" => {
943 let value = key_value
944 .parse::<u128>()
945 .map_err(|e| crate::error::QuantusError::Generic(format!("Invalid u128: {e}")))?;
946 Ok(value.encode())
947 },
948 "u32" => {
949 let value = key_value
950 .parse::<u32>()
951 .map_err(|e| crate::error::QuantusError::Generic(format!("Invalid u32: {e}")))?;
952 Ok(value.encode())
953 },
954 "hex" | "raw" => {
955 let value_hex = key_value.strip_prefix("0x").unwrap_or(key_value);
957 hex::decode(value_hex)
958 .map_err(|e| crate::error::QuantusError::Generic(format!("Invalid hex value: {e}")))
959 },
960 _ => Err(crate::error::QuantusError::Generic(format!(
961 "Unsupported key type: {key_type}. Supported types: accountid, u64, u128, u32, hex, raw"
962 ))),
963 }
964}