quantus_cli/cli/
events.rs1use crate::{
2 chain::client::QuantusClient, cli::address_format::QuantusSS58, log_print, log_verbose,
3};
4use colored::Colorize;
5use jsonrpsee::core::client::ClientT;
6use std::str::FromStr;
7
8pub async fn handle_events_command(
9 block: Option<u32>,
10 block_hash: Option<String>,
11 finalized: bool,
12 pallet_filter: Option<String>,
13 raw: bool,
14 decode: bool,
15 node_url: &str,
16) -> crate::error::Result<()> {
17 let quantus_client = QuantusClient::new(node_url).await?;
19
20 let (block_hash, block_number) = if let Some(block_num) = block {
22 log_print!("📋 Querying events from block #{}", block_num);
23
24 let hash: subxt::utils::H256 = quantus_client
26 .rpc_client()
27 .request::<subxt::utils::H256, [u32; 1]>("chain_getBlockHash", [block_num])
28 .await
29 .map_err(|e| {
30 crate::error::QuantusError::NetworkError(format!(
31 "Failed to get block hash for #{block_num}: {e:?}"
32 ))
33 })?;
34 (hash, block_num)
35 } else if let Some(hash_str) = block_hash {
36 log_print!("📋 Querying events from block hash: {}", hash_str);
37
38 let hash = subxt::utils::H256::from_str(&hash_str).map_err(|e| {
40 crate::error::QuantusError::NetworkError(format!("Invalid block hash: {e}"))
41 })?;
42
43 let block_header: serde_json::Value = quantus_client
45 .rpc_client()
46 .request::<serde_json::Value, [String; 1]>("chain_getHeader", [hash_str.clone()])
47 .await
48 .map_err(|e| {
49 crate::error::QuantusError::NetworkError(format!(
50 "Failed to get block header for hash {hash_str}: {e:?}"
51 ))
52 })?;
53
54 let block_num = if let Some(block_number_str) = block_header["number"].as_str() {
55 u64::from_str_radix(&block_number_str[2..], 16).map(|n| n as u32).unwrap_or(0)
56 } else {
57 0
58 };
59
60 (hash, block_num)
61 } else if finalized {
62 log_print!("📋 Querying events from finalized block");
63
64 let hash: subxt::utils::H256 = quantus_client
66 .rpc_client()
67 .request::<subxt::utils::H256, [(); 0]>("chain_getFinalizedHead", [])
68 .await
69 .map_err(|e| {
70 crate::error::QuantusError::NetworkError(format!(
71 "Failed to get finalized head: {e:?}"
72 ))
73 })?;
74
75 let block_header: serde_json::Value = quantus_client
77 .rpc_client()
78 .request::<serde_json::Value, [String; 1]>("chain_getHeader", [format!("0x{hash:x}")])
79 .await
80 .map_err(|e| {
81 crate::error::QuantusError::NetworkError(format!(
82 "Failed to get finalized block header: {e:?}"
83 ))
84 })?;
85
86 let block_num = if let Some(block_number_str) = block_header["number"].as_str() {
87 u64::from_str_radix(&block_number_str[2..], 16).map(|n| n as u32).unwrap_or(0)
88 } else {
89 0
90 };
91
92 (hash, block_num)
93 } else {
94 log_print!("📋 Querying events from latest block");
96
97 let hash = quantus_client.get_latest_block().await?;
98
99 let block_header: serde_json::Value = quantus_client
101 .rpc_client()
102 .request::<serde_json::Value, [(); 0]>("chain_getHeader", [])
103 .await
104 .map_err(|e| {
105 crate::error::QuantusError::NetworkError(format!(
106 "Failed to get latest block header: {e:?}"
107 ))
108 })?;
109
110 let block_num = if let Some(block_number_str) = block_header["number"].as_str() {
111 u64::from_str_radix(&block_number_str[2..], 16).map(|n| n as u32).unwrap_or(0)
112 } else {
113 0
114 };
115
116 (hash, block_num)
117 };
118
119 log_print!("🔮 Quantus CLI");
120 log_print!("🎯 Found Block #{}", block_number);
121
122 let events = quantus_client.client().blocks().at(block_hash).await?.events().await?;
124
125 log_print!("📋 Block Events:");
126
127 let mut event_count = 0;
128 let mut filtered_count = 0;
129
130 for event in events.iter() {
132 event_count += 1;
133
134 let event = event.map_err(|e| {
135 crate::error::QuantusError::NetworkError(format!("Failed to decode event: {e:?}"))
136 })?;
137
138 if let Some(ref filter) = pallet_filter {
140 if event.pallet_name() != filter {
141 continue;
142 }
143 }
144
145 filtered_count += 1;
146
147 log_print!(
149 " 📌 {}.{}",
150 event.pallet_name().bright_cyan(),
151 event.variant_name().bright_yellow()
152 );
153
154 if !raw && decode {
156 decode_event_details(&event)?;
157 }
158
159 if raw || crate::log::is_verbose() {
161 log_verbose!(" 📝 Raw event data: {:?}", event.field_bytes());
162 }
163 }
164
165 log_print!("");
167 if let Some(ref filter) = pallet_filter {
168 log_print!(
169 "📊 Summary: {} events total, {} events from {} pallet",
170 event_count,
171 filtered_count,
172 filter.bright_cyan()
173 );
174 } else {
175 log_print!("📊 Summary: {} events total", event_count);
176 }
177
178 if filtered_count == 0 && pallet_filter.is_some() {
179 log_print!("💡 Tip: No events found for the specified pallet. Try without --pallet filter to see all events.");
180 }
181
182 log_print!("💡 Tip: Use --verbose for raw event data");
183 log_print!("💡 Tip: Use --pallet <PALLET_NAME> to filter events by pallet");
184
185 Ok(())
186}
187
188fn decode_event_details<T: subxt::Config>(
190 event: &subxt::events::EventDetails<T>,
191) -> crate::error::Result<()> {
192 if let Some(typed_message) = decode_event_typed(event) {
194 log_print!("{}", typed_message);
195 }
196
197 Ok(())
198}
199
200fn decode_event_typed<T: subxt::Config>(event: &subxt::events::EventDetails<T>) -> Option<String> {
201 let typed_event = event.as_root_event::<crate::chain::quantus_subxt::api::Event>().ok()?;
203
204 let formatted_event = format_event_with_ss58_addresses(&typed_event);
206
207 Some(format!(" 📝 {}", formatted_event.bright_cyan()))
209}
210
211fn format_event_with_ss58_addresses(event: &crate::chain::quantus_subxt::api::Event) -> String {
213 let debug_str = format!("{event:?}");
214
215 let mut result = debug_str.clone();
217
218 let mut replacements = 0;
220 while let Some(account_id) = extract_account_id_from_debug(&result) {
221 let ss58_address = format_account_id(&account_id);
222 let account_debug = format!("{account_id:?}");
223 result = result.replace(&account_debug, &ss58_address);
224 replacements += 1;
225 if replacements > 10 {
226 break;
227 } }
229
230 result
231}
232
233fn extract_account_id_from_debug(debug_str: &str) -> Option<subxt::utils::AccountId32> {
235 if let Some(start) = debug_str.find("AccountId32([") {
237 if let Some(end) = debug_str[start..].find("])") {
238 let bytes_str = &debug_str[start + 13..start + end]; let bytes: Vec<u8> = bytes_str
242 .split(',')
243 .map(|s| s.trim().parse::<u8>().ok())
244 .collect::<Option<Vec<u8>>>()?;
245
246 if bytes.len() == 32 {
247 let mut account_bytes = [0u8; 32];
248 account_bytes.copy_from_slice(&bytes);
249 return Some(subxt::utils::AccountId32::from(account_bytes));
250 }
251 }
252 }
253 None
254}
255
256fn format_account_id(account_id: &subxt::utils::AccountId32) -> String {
258 account_id.to_quantus_ss58()
259}