1use crate::{
4 chain::{client::ChainConfig, quantus_subxt},
5 cli::address_format::QuantusSS58,
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 finalized: bool,
261) -> crate::error::Result<subxt::utils::H256> {
262 log_verbose!("✍️ Creating set_storage transaction...");
263
264 let set_storage_call =
266 quantus_subxt::api::Call::System(quantus_subxt::api::system::Call::set_storage {
267 items: vec![(storage_key, value_bytes)],
268 });
269
270 let sudo_call = quantus_subxt::api::tx().sudo().sudo(set_storage_call);
272
273 let tx_hash = crate::cli::common::submit_transaction(
274 quantus_client,
275 from_keypair,
276 sudo_call,
277 None,
278 finalized,
279 )
280 .await?;
281
282 log_verbose!("📋 Set storage transaction submitted: {:?}", tx_hash);
283
284 Ok(tx_hash)
285}
286
287pub async fn list_storage_items(
289 quantus_client: &crate::chain::client::QuantusClient,
290 pallet_name: &str,
291 names_only: bool,
292) -> crate::error::Result<()> {
293 log_print!("📋 Listing storage items for pallet: {}", pallet_name.bright_green());
294
295 validate_pallet_exists(quantus_client.client(), pallet_name)?;
297
298 let metadata = quantus_client.client().metadata();
299 let pallet = metadata.pallet_by_name(pallet_name).unwrap();
300
301 if let Some(storage_metadata) = pallet.storage() {
302 let entries = storage_metadata.entries();
303 log_print!("Found {} storage items: \n", entries.len());
304
305 for (index, entry) in entries.iter().enumerate() {
306 log_print!(
307 "{}. {}",
308 (index + 1).to_string().bright_yellow(),
309 entry.name().bright_cyan()
310 );
311
312 if !names_only {
313 log_print!(" Type: {:?}", entry.entry_type());
314 if !entry.docs().is_empty() {
315 log_print!(" Docs: {}", entry.docs().join(" ").dimmed());
316 }
317 log_print!("");
318 }
319 }
320 } else {
321 log_print!("❌ Pallet '{}' has no storage items.", pallet_name.bright_red());
322 }
323
324 Ok(())
325}
326
327pub async fn list_pallets_with_storage(
329 quantus_client: &crate::chain::client::QuantusClient,
330 with_counts: bool,
331) -> crate::error::Result<()> {
332 log_print!("🏛️ Listing all pallets with storage:");
333 log_print!("");
334
335 let metadata = quantus_client.client().metadata();
336 let pallets: Vec<_> = metadata.pallets().collect();
337
338 let mut storage_pallets = BTreeMap::new();
339
340 for pallet in pallets {
341 if let Some(storage_metadata) = pallet.storage() {
342 let entry_count = storage_metadata.entries().len();
343 storage_pallets.insert(pallet.name(), entry_count);
344 }
345 }
346
347 if storage_pallets.is_empty() {
348 log_print!("❌ No pallets with storage found.");
349 return Ok(());
350 }
351
352 for (index, (pallet_name, count)) in storage_pallets.iter().enumerate() {
353 if with_counts {
354 log_print!(
355 "{}. {} ({} items)",
356 (index + 1).to_string().bright_yellow(),
357 pallet_name.bright_green(),
358 count.to_string().bright_blue()
359 );
360 } else {
361 log_print!(
362 "{}. {}",
363 (index + 1).to_string().bright_yellow(),
364 pallet_name.bright_green()
365 );
366 }
367 }
368
369 log_print!("");
370 log_print!("Total: {} pallets with storage", storage_pallets.len().to_string().bright_green());
371
372 Ok(())
373}
374
375pub async fn show_storage_stats(
377 quantus_client: &crate::chain::client::QuantusClient,
378 pallet_name: Option<String>,
379 detailed: bool,
380) -> crate::error::Result<()> {
381 log_print!("📊 Storage size statistics: \n");
382
383 let metadata = quantus_client.client().metadata();
384
385 if let Some(pallet) = pallet_name {
386 validate_pallet_exists(quantus_client.client(), &pallet)?;
388 let pallet_meta = metadata.pallet_by_name(&pallet).unwrap();
389
390 if let Some(storage_metadata) = pallet_meta.storage() {
391 let entries = storage_metadata.entries();
392 log_print!("Pallet: {}", pallet.bright_green());
393 log_print!("Storage items: {}", entries.len().to_string().bright_blue());
394
395 if detailed {
396 log_print!("");
397 log_print!("Items:");
398 for (index, entry) in entries.iter().enumerate() {
399 log_print!(
400 " {}. {} - {:?}",
401 (index + 1).to_string().dimmed(),
402 entry.name().bright_cyan(),
403 entry.entry_type()
404 );
405 }
406 }
407 } else {
408 log_print!("❌ Pallet '{}' has no storage items.", pallet.bright_red());
409 }
410 } else {
411 let pallets: Vec<_> = metadata.pallets().collect();
413 let mut total_storage_items = 0;
414 let mut pallets_with_storage = 0;
415
416 let mut pallet_stats = Vec::new();
417
418 for pallet in pallets {
419 if let Some(storage_metadata) = pallet.storage() {
420 let entry_count = storage_metadata.entries().len();
421 total_storage_items += entry_count;
422 pallets_with_storage += 1;
423 pallet_stats.push((pallet.name(), entry_count));
424 }
425 }
426
427 log_print!("Total pallets: {}", metadata.pallets().len().to_string().bright_blue());
428 log_print!("Pallets with storage: {}", pallets_with_storage.to_string().bright_green());
429 log_print!("Total storage items: {}", total_storage_items.to_string().bright_yellow());
430
431 if detailed && !pallet_stats.is_empty() {
432 log_print!("");
433 log_print!("Per-pallet breakdown:");
434
435 pallet_stats.sort_by(|a, b| b.1.cmp(&a.1));
437
438 for (pallet_name, count) in pallet_stats {
439 log_print!(
440 " {} - {} items",
441 pallet_name.bright_cyan(),
442 count.to_string().bright_blue()
443 );
444 }
445 }
446 }
447
448 Ok(())
449}
450
451pub async fn count_storage_entries(
453 quantus_client: &crate::chain::client::QuantusClient,
454 pallet_name: &str,
455 storage_name: &str,
456 block_hash: subxt::utils::H256,
457) -> crate::error::Result<u32> {
458 let mut prefix = twox_128(pallet_name.as_bytes()).to_vec();
460 prefix.extend(&twox_128(storage_name.as_bytes()));
461
462 log_verbose!("🔑 Storage prefix for counting: 0x{}", hex::encode(&prefix));
463
464 use jsonrpsee::core::client::ClientT;
465
466 let block_hash_str = format!("{block_hash:#x}");
467 let prefix_hex = format!("0x{}", hex::encode(&prefix));
468 let page_size = 1000u32; let mut total_count = 0u32;
470 let mut start_key: Option<String> = None;
471
472 loop {
473 let keys: Vec<String> = quantus_client
475 .rpc_client()
476 .request::<Vec<String>, (String, u32, Option<String>, Option<String>)>(
477 "state_getKeysPaged",
478 (
479 prefix_hex.clone(), page_size, start_key.clone(), Some(block_hash_str.clone()), ),
484 )
485 .await
486 .map_err(|e| {
487 QuantusError::NetworkError(format!(
488 "Failed to fetch storage keys at block {block_hash:?}: {e:?}"
489 ))
490 })?;
491
492 let keys_count = keys.len() as u32;
493 total_count += keys_count;
494
495 log_verbose!("📊 Fetched {} keys (total: {})", keys_count, total_count);
496
497 if keys_count < page_size {
499 break;
500 }
501
502 start_key = keys.last().cloned();
504 if start_key.is_none() {
505 break;
506 }
507 }
508
509 Ok(total_count)
510}
511
512pub async fn iterate_storage_entries(
514 quantus_client: &crate::chain::client::QuantusClient,
515 pallet_name: &str,
516 storage_name: &str,
517 limit: u32,
518 decode_as: Option<String>,
519 block_identifier: Option<String>,
520) -> crate::error::Result<()> {
521 log_print!(
522 "🔄 Iterating storage {}::{} (limit: {})",
523 pallet_name.bright_green(),
524 storage_name.bright_cyan(),
525 limit.to_string().bright_yellow()
526 );
527
528 validate_pallet_exists(quantus_client.client(), pallet_name)?;
530
531 let block_hash = if let Some(block_id) = block_identifier {
533 resolve_block_hash(quantus_client, &block_id).await?
534 } else {
535 quantus_client.get_latest_block().await?
536 };
537
538 log_verbose!("📦 Using block: {:?}", block_hash);
539
540 let metadata = quantus_client.client().metadata();
542 let pallet = metadata.pallet_by_name(pallet_name).unwrap();
543
544 if let Some(storage_metadata) = pallet.storage() {
545 if let Some(entry) = storage_metadata.entry_by_name(storage_name) {
546 log_print!("📝 Storage type: {:?}", entry.entry_type());
547 if !entry.docs().is_empty() {
548 log_print!("📖 Docs: {}", entry.docs().join(" ").dimmed());
549 }
550 }
551 }
552
553 log_print!("🔢 Counting storage entries...");
555 let total_count =
556 count_storage_entries(quantus_client, pallet_name, storage_name, block_hash).await?;
557
558 log_success!(
559 "📊 Total entries in {}::{}: {}",
560 pallet_name.bright_green(),
561 storage_name.bright_cyan(),
562 total_count.to_string().bright_yellow()
563 );
564
565 if limit == 0 {
567 return Ok(());
568 }
569
570 let mut prefix = twox_128(pallet_name.as_bytes()).to_vec();
572 prefix.extend(&twox_128(storage_name.as_bytes()));
573
574 log_verbose!("🔑 Storage prefix: 0x{}", hex::encode(&prefix));
575
576 use jsonrpsee::core::client::ClientT;
578
579 let block_hash_str = format!("{block_hash:#x}");
580 let keys: Vec<String> = quantus_client
581 .rpc_client()
582 .request::<Vec<String>, (String, u32, Option<String>, Option<String>)>(
583 "state_getKeysPaged",
584 (
585 format!("0x{}", hex::encode(&prefix)),
586 limit, None, Some(block_hash_str), ),
590 )
591 .await
592 .map_err(|e| {
593 QuantusError::NetworkError(format!(
594 "Failed to fetch storage keys at block {block_hash:?}: {e:?}"
595 ))
596 })?;
597
598 if keys.is_empty() {
599 log_print!("❌ No entries found.");
600 return Ok(());
601 }
602
603 log_print!("📋 First {} entries:", keys.len().min(limit as usize));
604 log_print!("");
605
606 for (index, key) in keys.iter().take(limit as usize).enumerate() {
608 log_print!("{}. Key: {}", (index + 1).to_string().bright_yellow(), key.dimmed());
609
610 if index < 3 && decode_as.is_some() {
612 if let Ok(key_bytes) = hex::decode(key.strip_prefix("0x").unwrap_or(key)) {
613 if let Ok(Some(value_bytes)) =
614 get_storage_raw_at_block(quantus_client, key_bytes, block_hash).await
615 {
616 if let Some(ref decode_type) = decode_as {
617 match decode_storage_value(&value_bytes, decode_type) {
618 Ok(decoded_value) => {
619 log_print!(" Value: {}", decoded_value.bright_green())
620 },
621 Err(_) => log_print!(
622 " Value: 0x{} (raw)",
623 hex::encode(&value_bytes).dimmed()
624 ),
625 }
626 }
627 }
628 }
629 }
630 }
631
632 if total_count > limit {
633 log_print!("");
634 log_print!(
635 "... and {} more entries (use --limit 0 to just count)",
636 (total_count - limit).to_string().bright_blue()
637 );
638 }
639
640 Ok(())
641}
642
643fn decode_storage_value(value_bytes: &[u8], type_str: &str) -> crate::error::Result<String> {
645 match type_str.to_lowercase().as_str() {
646 "u32" => match u32::decode(&mut &value_bytes[..]) {
647 Ok(decoded_value) => Ok(decoded_value.to_string()),
648 Err(e) => Err(QuantusError::Generic(format!("Failed to decode as u32: {e}"))),
649 },
650 "u64" | "moment" => match u64::decode(&mut &value_bytes[..]) {
651 Ok(decoded_value) => Ok(decoded_value.to_string()),
652 Err(e) => Err(QuantusError::Generic(format!("Failed to decode as u64: {e}"))),
653 },
654 "u128" | "balance" => match u128::decode(&mut &value_bytes[..]) {
655 Ok(decoded_value) => Ok(decoded_value.to_string()),
656 Err(e) => Err(QuantusError::Generic(format!("Failed to decode as u128: {e}"))),
657 },
658 "accountid" | "accountid32" => match AccountId32::decode(&mut &value_bytes[..]) {
659 Ok(account_id) => Ok(account_id.to_quantus_ss58()),
660 Err(e) => Err(QuantusError::Generic(format!("Failed to decode as AccountId32: {e}"))),
661 },
662 "accountinfo" => match AccountInfo::decode(&mut &value_bytes[..]) {
663 Ok(account_info) => Ok(format!("{account_info:#?}")),
664 Err(e) => Err(QuantusError::Generic(format!("Failed to decode as AccountInfo: {e}"))),
665 },
666 _ => Err(QuantusError::Generic(format!(
667 "Unsupported type for decoding: {type_str}. Supported types: u32, u64, moment, u128, balance, accountid, accountinfo"
668 ))),
669 }
670}
671
672async fn get_storage_by_storage_key(
674 quantus_client: &crate::chain::client::QuantusClient,
675 storage_key: String,
676 block: Option<String>,
677 decode_as: Option<String>,
678) -> crate::error::Result<()> {
679 log_print!("🗄️ Storage");
680
681 let encoded_storage_key = encode_storage_key(&storage_key, "raw")?;
682
683 let result = if let Some(block_id) = block {
684 let block_hash = resolve_block_hash(quantus_client, &block_id).await?;
685 get_storage_raw_at_block(quantus_client, encoded_storage_key, block_hash).await?
686 } else {
687 get_storage_raw(quantus_client, encoded_storage_key).await?
688 };
689
690 if let Some(value_bytes) = result {
691 log_success!("Raw Value: 0x{}", hex::encode(&value_bytes).bright_yellow());
692 if let Some(type_str) = decode_as {
693 log_print!("Attempting to decode as {}...", type_str.bright_cyan());
694 match decode_storage_value(&value_bytes, &type_str) {
695 Ok(decoded_value) => {
696 log_success!("Decoded Value: {}", decoded_value.bright_green())
697 },
698 Err(e) => log_error!("{}", e),
699 }
700 }
701 } else {
702 log_print!("{}", "No value found at this storage location.".dimmed());
703 }
704
705 Ok(())
706}
707
708async fn get_storage_by_parts(
710 quantus_client: &crate::chain::client::QuantusClient,
711 pallet: String,
712 name: String,
713 key: Option<String>,
714 key_type: Option<String>,
715 block: Option<String>,
716 decode_as: Option<String>,
717 count: bool,
718) -> crate::error::Result<()> {
719 if let Some(block_value) = &block {
720 log_print!(
721 "🔎 Getting storage for {}::{} at block {}",
722 pallet.bright_green(),
723 name.bright_cyan(),
724 block_value.bright_yellow()
725 );
726 } else {
727 log_print!(
728 "🔎 Getting storage for {}::{} (latest block)",
729 pallet.bright_green(),
730 name.bright_cyan()
731 );
732 }
733
734 if let Some(key_value) = &key {
735 log_print!("🔑 With key: {}", key_value.bright_yellow());
736 }
737
738 validate_pallet_exists(quantus_client.client(), &pallet)?;
739
740 let block_hash = if let Some(block_id) = &block {
741 resolve_block_hash(quantus_client, block_id).await?
742 } else {
743 quantus_client.get_latest_block().await?
744 };
745
746 let entry_count = count_storage_entries(quantus_client, &pallet, &name, block_hash).await?;
747 let is_storage_value = entry_count == 1;
748
749 let should_count = count || (key.is_none() && !is_storage_value);
750
751 if should_count {
752 log_print!("🔢 Counting all entries in {}::{}", pallet.bright_green(), name.bright_cyan());
753
754 let block_display = if let Some(ref block_id) = block {
755 format!(" at block {}", block_id.bright_yellow())
756 } else {
757 " (latest)".to_string()
758 };
759
760 log_success!(
761 "👥 Total entries{}: {}",
762 block_display,
763 entry_count.to_string().bright_green().bold()
764 );
765 } else {
766 let mut storage_key = twox_128(pallet.as_bytes()).to_vec();
767 storage_key.extend(&twox_128(name.as_bytes()));
768
769 if let Some(key_value) = &key {
770 if let Some(key_type_str) = &key_type {
771 let key_bytes = encode_storage_key(key_value, key_type_str)?;
772 storage_key.extend(key_bytes);
773 } else {
774 log_error!("Key type (--key-type) is required when using --key parameter");
775 return Ok(());
776 }
777 } else if !is_storage_value {
778 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);
779 return Ok(());
780 }
781
782 let result = get_storage_raw_at_block(quantus_client, storage_key, block_hash).await?;
783
784 if let Some(value_bytes) = result {
785 log_success!("Raw Value: 0x{}", hex::encode(&value_bytes).bright_yellow());
786
787 if let Some(type_str) = decode_as {
788 log_print!("Attempting to decode as {}...", type_str.bright_cyan());
789 match decode_storage_value(&value_bytes, &type_str) {
790 Ok(decoded_value) => {
791 log_success!("Decoded Value: {}", decoded_value.bright_green())
792 },
793 Err(e) => log_error!("{}", e),
794 }
795 }
796 } else {
797 log_print!("{}", "No value found at this storage location.".dimmed());
798 }
799 }
800
801 Ok(())
802}
803
804pub async fn handle_storage_command(
806 command: StorageCommands,
807 node_url: &str,
808 finalized: bool,
809) -> crate::error::Result<()> {
810 log_print!("🗄️ Storage");
811
812 let quantus_client = crate::chain::client::QuantusClient::new(node_url).await?;
813
814 match command {
815 StorageCommands::Get {
816 pallet,
817 name,
818 block,
819 decode_as,
820 key,
821 key_type,
822 count,
823 storage_key,
824 } => {
825 if let Some(s_key) = storage_key {
826 get_storage_by_storage_key(&quantus_client, s_key, block, decode_as).await
827 } else {
828 get_storage_by_parts(
830 &quantus_client,
831 pallet.unwrap(),
832 name.unwrap(),
833 key,
834 key_type,
835 block,
836 decode_as,
837 count,
838 )
839 .await
840 }
841 },
842 StorageCommands::List { pallet, names_only } =>
843 list_storage_items(&quantus_client, &pallet, names_only).await,
844 StorageCommands::ListPallets { with_counts } =>
845 list_pallets_with_storage(&quantus_client, with_counts).await,
846 StorageCommands::Stats { pallet, detailed } =>
847 show_storage_stats(&quantus_client, pallet, detailed).await,
848 StorageCommands::Iterate { pallet, name, limit, decode_as, block } =>
849 iterate_storage_entries(&quantus_client, &pallet, &name, limit, decode_as, block).await,
850
851 StorageCommands::Set { pallet, name, value, wallet, password, password_file, r#type } => {
852 log_print!("✍️ Setting storage for {}::{}", pallet.bright_green(), name.bright_cyan());
853 log_print!("\n{}", "🛑 This is a SUDO operation!".bright_red().bold());
854
855 validate_pallet_exists(quantus_client.client(), &pallet)?;
857
858 let keypair =
860 crate::wallet::load_keypair_from_wallet(&wallet, password, password_file)?;
861 log_verbose!("🔐 Using wallet: {}", wallet.bright_green());
862
863 let value_bytes = match r#type.as_deref() {
865 Some("u64") | Some("moment") => value
866 .parse::<u64>()
867 .map_err(|e| QuantusError::Generic(format!("Invalid u64 value: {e}")))?
868 .encode(),
869 Some("u128") | Some("balance") => value
870 .parse::<u128>()
871 .map_err(|e| QuantusError::Generic(format!("Invalid u128 value: {e}")))?
872 .encode(),
873 Some("accountid") | Some("accountid32") => AccountId32::from_ss58check(&value)
874 .map_err(|e| QuantusError::Generic(format!("Invalid AccountId value: {e:?}")))?
875 .encode(),
876 None => {
877 if value.starts_with("0x") && value.len() == 66 {
880 let h256_value = subxt::utils::H256::from_str(&value).map_err(|e| {
883 QuantusError::Generic(format!("Invalid H256 value: {e}"))
884 })?;
885 h256_value.0.to_vec()
886 } else {
887 let value_hex = value.strip_prefix("0x").unwrap_or(&value);
889 hex::decode(value_hex)
890 .map_err(|e| QuantusError::Generic(format!("Invalid hex value: {e}")))?
891 }
892 },
893 Some(unsupported) =>
894 return Err(QuantusError::Generic(format!(
895 "Unsupported type for --type: {unsupported}"
896 ))),
897 };
898
899 log_verbose!("Encoded value bytes: 0x{}", hex::encode(&value_bytes).dimmed());
900
901 let storage_key = {
903 let mut key = twox_128(pallet.as_bytes()).to_vec();
904 key.extend(&twox_128(name.as_bytes()));
905 key
906 };
907
908 let tx_hash =
910 set_storage_value(&quantus_client, &keypair, storage_key, value_bytes, finalized)
911 .await?;
912
913 log_print!(
914 "✅ {} Set storage transaction submitted! Hash: {:?}",
915 "SUCCESS".bright_green().bold(),
916 tx_hash
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}