1use crate::error::QuantusError;
4use codec::Decode;
5use colored::Colorize;
6
7pub async fn decode_preimage(
9 quantus_client: &crate::chain::client::QuantusClient,
10 hash: &subxt::utils::H256,
11 len: u32,
12) -> crate::error::Result<String> {
13 let latest_block_hash = quantus_client.get_latest_block().await?;
15 let storage_at = quantus_client.client().storage().at(latest_block_hash);
16
17 let preimage_addr = crate::chain::quantus_subxt::api::storage()
18 .preimage()
19 .preimage_for((*hash, len));
20
21 let preimage_result = storage_at.fetch(&preimage_addr).await;
22
23 let content = match preimage_result {
24 Ok(Some(bounded_vec)) => bounded_vec.0,
25 Ok(None) =>
26 return Err(QuantusError::Generic(format!("Preimage not found for hash {:?}", hash))),
27 Err(e) => return Err(QuantusError::Generic(format!("Error fetching preimage: {:?}", e))),
28 };
29
30 decode_runtime_call_direct(&content)
32}
33
34fn decode_runtime_call_direct(data: &[u8]) -> crate::error::Result<String> {
36 if data.len() < 3 {
38 return Err(QuantusError::Generic("Call data too short".to_string()));
39 }
40
41 let pallet_index = data[0];
42 let inner_index = data[1];
43 let call_index = data[2];
44
45 match (pallet_index, inner_index, call_index) {
46 (0, 0, idx) if idx > 100 => {
50 decode_system_remark_no_index(&data[2..])
52 },
53 (0, 0, _) => decode_system_call(&data[2..]),
54
55 (18, 5, _) => decode_treasury_spend_call(&data[3..]),
58
59 _ => Ok(format!(
61 " {} {} {} {}\n {} {} bytes\n {}:\n {}",
62 "Call Indices:".dimmed(),
63 pallet_index,
64 inner_index,
65 call_index,
66 "Args:".dimmed(),
67 data.len() - 3,
68 "Raw Hex".dimmed(),
69 hex::encode(&data[3..]).bright_green()
70 )),
71 }
72}
73
74fn decode_system_remark_no_index(args: &[u8]) -> crate::error::Result<String> {
76 let mut cursor = args;
78 let remark_bytes: Vec<u8> = Vec::decode(&mut cursor)
79 .map_err(|e| QuantusError::Generic(format!("Failed to decode remark: {:?}", e)))?;
80 let remark_str = String::from_utf8_lossy(&remark_bytes);
81
82 Ok(format!(
83 " {} {}\n {} {}\n {}:\n {} \"{}\"",
84 "Pallet:".dimmed(),
85 "System".bright_cyan(),
86 "Call:".dimmed(),
87 "remark".bright_yellow(),
88 "Parameters".dimmed(),
89 "message:".dimmed(),
90 remark_str.bright_green()
91 ))
92}
93
94fn decode_system_call(data_from_call: &[u8]) -> crate::error::Result<String> {
96 if data_from_call.is_empty() {
97 return Err(QuantusError::Generic("Empty system call data".to_string()));
98 }
99
100 let call_index = data_from_call[0];
101 let args = &data_from_call[1..];
102
103 match call_index {
104 0 => {
105 let mut cursor = args;
107 let remark_bytes: Vec<u8> = Vec::decode(&mut cursor)
108 .map_err(|e| QuantusError::Generic(format!("Failed to decode remark: {:?}", e)))?;
109 let remark_str = String::from_utf8_lossy(&remark_bytes);
110
111 Ok(format!(
112 " {} {}\n {} {}\n {}:\n {} \"{}\"",
113 "Pallet:".dimmed(),
114 "System".bright_cyan(),
115 "Call:".dimmed(),
116 "remark".bright_yellow(),
117 "Parameters".dimmed(),
118 "message:".dimmed(),
119 remark_str.bright_green()
120 ))
121 },
122 1 => {
123 let remark_str = if args.len() > 1 {
125 String::from_utf8_lossy(&args[1..])
126 } else {
127 String::from_utf8_lossy(args)
128 };
129
130 Ok(format!(
131 " {} {}\n {} {}\n {}:\n {} \"{}\"",
132 "Pallet:".dimmed(),
133 "System".bright_cyan(),
134 "Call:".dimmed(),
135 "remark_with_event".bright_yellow(),
136 "Parameters".dimmed(),
137 "message:".dimmed(),
138 remark_str.bright_green()
139 ))
140 },
141 7 => {
142 Ok(format!(
144 " {} {}\n {} {} {}\n {} {}",
145 "Pallet:".dimmed(),
146 "System".bright_cyan(),
147 "Call:".dimmed(),
148 "set_code".bright_yellow(),
149 "(Runtime Upgrade)".dimmed(),
150 "Parameters:".dimmed(),
151 "<WASM binary>".bright_green()
152 ))
153 },
154 _ => Ok(format!(
155 " {} {}\n {} {} (index {})",
156 "Pallet:".dimmed(),
157 "System".bright_cyan(),
158 "Call:".dimmed(),
159 "unknown".yellow(),
160 call_index
161 )),
162 }
163}
164
165fn decode_treasury_spend_call(args: &[u8]) -> crate::error::Result<String> {
168 use sp_core::crypto::Ss58Codec;
169
170 crate::log_verbose!("Decoding treasury spend, args length: {} bytes", args.len());
171 crate::log_verbose!("Args hex: {}", hex::encode(args));
172
173 if args.len() < 34 {
174 return Err(QuantusError::Generic(format!(
175 "Args too short for treasury spend: {} bytes (expected 40-42)",
176 args.len()
177 )));
178 }
179
180 let amount_bytes_len = args.len() - 32 - 1;
193 if !(1..=16).contains(&amount_bytes_len) {
194 return Err(QuantusError::Generic(format!(
195 "Invalid amount bytes length: {}",
196 amount_bytes_len
197 )));
198 }
199
200 let mut amount_bytes_extended = [0u8; 16];
202 amount_bytes_extended[..amount_bytes_len].copy_from_slice(&args[..amount_bytes_len]);
203 let amount = u128::from_le_bytes(amount_bytes_extended);
204
205 let beneficiary_start = amount_bytes_len;
207 let account_bytes: [u8; 32] = args[beneficiary_start..beneficiary_start + 32]
208 .try_into()
209 .map_err(|_| QuantusError::Generic("Failed to extract beneficiary bytes".to_string()))?;
210 let sp_account = sp_core::crypto::AccountId32::from(account_bytes);
211 let ss58 = sp_account.to_ss58check_with_version(sp_core::crypto::Ss58AddressFormat::custom(42));
212 let beneficiary_str = format!("{} ({}...{})", ss58, &ss58[..8], &ss58[ss58.len() - 6..]);
213
214 let valid_from_byte = args[args.len() - 1];
216 let valid_from_str = if valid_from_byte == 0 {
217 "None (immediate)".to_string()
218 } else {
219 format!("Some (byte: 0x{:02x})", valid_from_byte)
220 };
221
222 let quan = amount as f64 / 1_000_000_000_000.0;
224
225 Ok(format!(
226 " {} {}\n {} {}\n {}:\n {} {} {} ({} raw)\n {} {}\n {} {}\n\n {} {}",
227 "Pallet:".dimmed(),
228 "TreasuryPallet".bright_cyan(),
229 "Call:".dimmed(),
230 "spend".bright_yellow(),
231 "Parameters".dimmed(),
232 "amount:".dimmed(),
233 quan.to_string().bright_green().bold(),
234 "QUAN".bright_green(),
235 amount,
236 "beneficiary:".dimmed(),
237 beneficiary_str.bright_green(),
238 "valid_from:".dimmed(),
239 valid_from_str.bright_green(),
240 "💡 Info:".cyan(),
241 "Vote YES if you approve this Treasury spend, NO to reject.".cyan()
242 ))
243}