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 },
615 Err(_) => log_print!(
616 " Value: 0x{} (raw)",
617 hex::encode(&value_bytes).dimmed()
618 ),
619 }
620 }
621 }
622 }
623 }
624 }
625
626 if total_count > limit {
627 log_print!("");
628 log_print!(
629 "... and {} more entries (use --limit 0 to just count)",
630 (total_count - limit).to_string().bright_blue()
631 );
632 }
633
634 Ok(())
635}
636
637fn decode_storage_value(value_bytes: &[u8], type_str: &str) -> crate::error::Result<String> {
639 match type_str.to_lowercase().as_str() {
640 "u32" => match u32::decode(&mut &value_bytes[..]) {
641 Ok(decoded_value) => Ok(decoded_value.to_string()),
642 Err(e) => Err(QuantusError::Generic(format!("Failed to decode as u32: {e}"))),
643 },
644 "u64" | "moment" => match u64::decode(&mut &value_bytes[..]) {
645 Ok(decoded_value) => Ok(decoded_value.to_string()),
646 Err(e) => Err(QuantusError::Generic(format!("Failed to decode as u64: {e}"))),
647 },
648 "u128" | "balance" => match u128::decode(&mut &value_bytes[..]) {
649 Ok(decoded_value) => Ok(decoded_value.to_string()),
650 Err(e) => Err(QuantusError::Generic(format!("Failed to decode as u128: {e}"))),
651 },
652 "accountid" | "accountid32" => match AccountId32::decode(&mut &value_bytes[..]) {
653 Ok(account_id) => Ok(account_id.to_quantus_ss58()),
654 Err(e) => Err(QuantusError::Generic(format!("Failed to decode as AccountId32: {e}"))),
655 },
656 "accountinfo" => match AccountInfo::decode(&mut &value_bytes[..]) {
657 Ok(account_info) => Ok(format!("{account_info:#?}")),
658 Err(e) => Err(QuantusError::Generic(format!("Failed to decode as AccountInfo: {e}"))),
659 },
660 _ => Err(QuantusError::Generic(format!(
661 "Unsupported type for decoding: {type_str}. Supported types: u32, u64, moment, u128, balance, accountid, accountinfo"
662 ))),
663 }
664}
665
666async fn get_storage_by_storage_key(
668 quantus_client: &crate::chain::client::QuantusClient,
669 storage_key: String,
670 block: Option<String>,
671 decode_as: Option<String>,
672) -> crate::error::Result<()> {
673 log_print!("🗄️ Storage");
674
675 let encoded_storage_key = encode_storage_key(&storage_key, "raw")?;
676
677 let result = if let Some(block_id) = block {
678 let block_hash = resolve_block_hash(quantus_client, &block_id).await?;
679 get_storage_raw_at_block(quantus_client, encoded_storage_key, block_hash).await?
680 } else {
681 get_storage_raw(quantus_client, encoded_storage_key).await?
682 };
683
684 if let Some(value_bytes) = result {
685 log_success!("Raw Value: 0x{}", hex::encode(&value_bytes).bright_yellow());
686 if let Some(type_str) = decode_as {
687 log_print!("Attempting to decode as {}...", type_str.bright_cyan());
688 match decode_storage_value(&value_bytes, &type_str) {
689 Ok(decoded_value) => {
690 log_success!("Decoded Value: {}", decoded_value.bright_green())
691 },
692 Err(e) => log_error!("{}", e),
693 }
694 }
695 } else {
696 log_print!("{}", "No value found at this storage location.".dimmed());
697 }
698
699 Ok(())
700}
701
702async fn get_storage_by_parts(
704 quantus_client: &crate::chain::client::QuantusClient,
705 pallet: String,
706 name: String,
707 key: Option<String>,
708 key_type: Option<String>,
709 block: Option<String>,
710 decode_as: Option<String>,
711 count: bool,
712) -> crate::error::Result<()> {
713 if let Some(block_value) = &block {
714 log_print!(
715 "🔎 Getting storage for {}::{} at block {}",
716 pallet.bright_green(),
717 name.bright_cyan(),
718 block_value.bright_yellow()
719 );
720 } else {
721 log_print!(
722 "🔎 Getting storage for {}::{} (latest block)",
723 pallet.bright_green(),
724 name.bright_cyan()
725 );
726 }
727
728 if let Some(key_value) = &key {
729 log_print!("🔑 With key: {}", key_value.bright_yellow());
730 }
731
732 validate_pallet_exists(quantus_client.client(), &pallet)?;
733
734 let block_hash = if let Some(block_id) = &block {
735 resolve_block_hash(quantus_client, block_id).await?
736 } else {
737 quantus_client.get_latest_block().await?
738 };
739
740 let entry_count = count_storage_entries(quantus_client, &pallet, &name, block_hash).await?;
741 let is_storage_value = entry_count == 1;
742
743 let should_count = count || (key.is_none() && !is_storage_value);
744
745 if should_count {
746 log_print!("🔢 Counting all entries in {}::{}", pallet.bright_green(), name.bright_cyan());
747
748 let block_display = if let Some(ref block_id) = block {
749 format!(" at block {}", block_id.bright_yellow())
750 } else {
751 " (latest)".to_string()
752 };
753
754 log_success!(
755 "👥 Total entries{}: {}",
756 block_display,
757 entry_count.to_string().bright_green().bold()
758 );
759 } else {
760 let mut storage_key = twox_128(pallet.as_bytes()).to_vec();
761 storage_key.extend(&twox_128(name.as_bytes()));
762
763 if let Some(key_value) = &key {
764 if let Some(key_type_str) = &key_type {
765 let key_bytes = encode_storage_key(key_value, key_type_str)?;
766 storage_key.extend(key_bytes);
767 } else {
768 log_error!("Key type (--key-type) is required when using --key parameter");
769 return Ok(());
770 }
771 } else if !is_storage_value {
772 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);
773 return Ok(());
774 }
775
776 let result = get_storage_raw_at_block(quantus_client, storage_key, block_hash).await?;
777
778 if let Some(value_bytes) = result {
779 log_success!("Raw Value: 0x{}", hex::encode(&value_bytes).bright_yellow());
780
781 if let Some(type_str) = decode_as {
782 log_print!("Attempting to decode as {}...", type_str.bright_cyan());
783 match decode_storage_value(&value_bytes, &type_str) {
784 Ok(decoded_value) => {
785 log_success!("Decoded Value: {}", decoded_value.bright_green())
786 },
787 Err(e) => log_error!("{}", e),
788 }
789 }
790 } else {
791 log_print!("{}", "No value found at this storage location.".dimmed());
792 }
793 }
794
795 Ok(())
796}
797
798pub async fn handle_storage_command(
800 command: StorageCommands,
801 node_url: &str,
802) -> crate::error::Result<()> {
803 log_print!("🗄️ Storage");
804
805 let quantus_client = crate::chain::client::QuantusClient::new(node_url).await?;
806
807 match command {
808 StorageCommands::Get {
809 pallet,
810 name,
811 block,
812 decode_as,
813 key,
814 key_type,
815 count,
816 storage_key,
817 } => {
818 if let Some(s_key) = storage_key {
819 get_storage_by_storage_key(&quantus_client, s_key, block, decode_as).await
820 } else {
821 get_storage_by_parts(
823 &quantus_client,
824 pallet.unwrap(),
825 name.unwrap(),
826 key,
827 key_type,
828 block,
829 decode_as,
830 count,
831 )
832 .await
833 }
834 },
835 StorageCommands::List { pallet, names_only } =>
836 list_storage_items(&quantus_client, &pallet, names_only).await,
837 StorageCommands::ListPallets { with_counts } =>
838 list_pallets_with_storage(&quantus_client, with_counts).await,
839 StorageCommands::Stats { pallet, detailed } =>
840 show_storage_stats(&quantus_client, pallet, detailed).await,
841 StorageCommands::Iterate { pallet, name, limit, decode_as, block } =>
842 iterate_storage_entries(&quantus_client, &pallet, &name, limit, decode_as, block).await,
843
844 StorageCommands::Set { pallet, name, value, wallet, password, password_file, r#type } => {
845 log_print!("✍️ Setting storage for {}::{}", pallet.bright_green(), name.bright_cyan());
846 log_print!("\n{}", "🛑 This is a SUDO operation!".bright_red().bold());
847
848 validate_pallet_exists(quantus_client.client(), &pallet)?;
850
851 let keypair =
853 crate::wallet::load_keypair_from_wallet(&wallet, password, password_file)?;
854 log_verbose!("🔐 Using wallet: {}", wallet.bright_green());
855
856 let value_bytes = match r#type.as_deref() {
858 Some("u64") | Some("moment") => value
859 .parse::<u64>()
860 .map_err(|e| QuantusError::Generic(format!("Invalid u64 value: {e}")))?
861 .encode(),
862 Some("u128") | Some("balance") => value
863 .parse::<u128>()
864 .map_err(|e| QuantusError::Generic(format!("Invalid u128 value: {e}")))?
865 .encode(),
866 Some("accountid") | Some("accountid32") => AccountId32::from_ss58check(&value)
867 .map_err(|e| QuantusError::Generic(format!("Invalid AccountId value: {e:?}")))?
868 .encode(),
869 None => {
870 if value.starts_with("0x") && value.len() == 66 {
873 let h256_value = subxt::utils::H256::from_str(&value).map_err(|e| {
876 QuantusError::Generic(format!("Invalid H256 value: {e}"))
877 })?;
878 h256_value.0.to_vec()
879 } else {
880 let value_hex = value.strip_prefix("0x").unwrap_or(&value);
882 hex::decode(value_hex)
883 .map_err(|e| QuantusError::Generic(format!("Invalid hex value: {e}")))?
884 }
885 },
886 Some(unsupported) =>
887 return Err(QuantusError::Generic(format!(
888 "Unsupported type for --type: {unsupported}"
889 ))),
890 };
891
892 log_verbose!("Encoded value bytes: 0x{}", hex::encode(&value_bytes).dimmed());
893
894 let storage_key = {
896 let mut key = twox_128(pallet.as_bytes()).to_vec();
897 key.extend(&twox_128(name.as_bytes()));
898 key
899 };
900
901 let tx_hash =
903 set_storage_value(&quantus_client, &keypair, storage_key, value_bytes).await?;
904
905 log_print!(
906 "✅ {} Set storage transaction submitted! Hash: {:?}",
907 "SUCCESS".bright_green().bold(),
908 tx_hash
909 );
910
911 let success = wait_for_tx_confirmation(quantus_client.client(), tx_hash).await?;
912
913 if success {
914 log_success!(
915 "🎉 {} Set storage transaction confirmed!",
916 "FINISHED".bright_green().bold()
917 );
918 } else {
919 log_error!("Transaction failed!");
920 }
921
922 Ok(())
923 },
924 }
925}
926
927fn encode_storage_key(key_value: &str, key_type: &str) -> crate::error::Result<Vec<u8>> {
929 use codec::Encode;
930 use sp_core::crypto::{AccountId32 as SpAccountId32, Ss58Codec};
931
932 match key_type.to_lowercase().as_str() {
933 "accountid" | "accountid32" => {
934 let account_id = SpAccountId32::from_ss58check(key_value).map_err(|e| {
935 crate::error::QuantusError::Generic(format!("Invalid AccountId: {e:?}"))
936 })?;
937 Ok(account_id.encode())
938 },
939 "u64" => {
940 let value = key_value
941 .parse::<u64>()
942 .map_err(|e| crate::error::QuantusError::Generic(format!("Invalid u64: {e}")))?;
943 Ok(value.encode())
944 },
945 "u128" => {
946 let value = key_value
947 .parse::<u128>()
948 .map_err(|e| crate::error::QuantusError::Generic(format!("Invalid u128: {e}")))?;
949 Ok(value.encode())
950 },
951 "u32" => {
952 let value = key_value
953 .parse::<u32>()
954 .map_err(|e| crate::error::QuantusError::Generic(format!("Invalid u32: {e}")))?;
955 Ok(value.encode())
956 },
957 "hex" | "raw" => {
958 let value_hex = key_value.strip_prefix("0x").unwrap_or(key_value);
960 hex::decode(value_hex)
961 .map_err(|e| crate::error::QuantusError::Generic(format!("Invalid hex value: {e}")))
962 },
963 _ => Err(crate::error::QuantusError::Generic(format!(
964 "Unsupported key type: {key_type}. Supported types: accountid, u64, u128, u32, hex, raw"
965 ))),
966 }
967}