use crate::{
chain::client::QuantusClient, cli::address_format::QuantusSS58, log_print, log_verbose,
};
use colored::Colorize;
use jsonrpsee::core::client::ClientT;
use std::str::FromStr;
pub async fn handle_events_command(
block: Option<u32>,
block_hash: Option<String>,
finalized: bool,
pallet_filter: Option<String>,
raw: bool,
decode: bool,
node_url: &str,
) -> crate::error::Result<()> {
let quantus_client = QuantusClient::new(node_url).await?;
let (block_hash, block_number) = if let Some(block_num) = block {
log_print!("📋 Querying events from block #{}", block_num);
let hash: subxt::utils::H256 = quantus_client
.rpc_client()
.request::<subxt::utils::H256, [u32; 1]>("chain_getBlockHash", [block_num])
.await
.map_err(|e| {
crate::error::QuantusError::NetworkError(format!(
"Failed to get block hash for #{block_num}: {e:?}"
))
})?;
(hash, block_num)
} else if let Some(hash_str) = block_hash {
log_print!("📋 Querying events from block hash: {}", hash_str);
let hash = subxt::utils::H256::from_str(&hash_str).map_err(|e| {
crate::error::QuantusError::NetworkError(format!("Invalid block hash: {e}"))
})?;
let block_header: serde_json::Value = quantus_client
.rpc_client()
.request::<serde_json::Value, [String; 1]>("chain_getHeader", [hash_str.clone()])
.await
.map_err(|e| {
crate::error::QuantusError::NetworkError(format!(
"Failed to get block header for hash {hash_str}: {e:?}"
))
})?;
let block_num = if let Some(block_number_str) = block_header["number"].as_str() {
u64::from_str_radix(&block_number_str[2..], 16).map(|n| n as u32).unwrap_or(0)
} else {
0
};
(hash, block_num)
} else if finalized {
log_print!("📋 Querying events from finalized block");
let hash: subxt::utils::H256 = quantus_client
.rpc_client()
.request::<subxt::utils::H256, [(); 0]>("chain_getFinalizedHead", [])
.await
.map_err(|e| {
crate::error::QuantusError::NetworkError(format!(
"Failed to get finalized head: {e:?}"
))
})?;
let block_header: serde_json::Value = quantus_client
.rpc_client()
.request::<serde_json::Value, [String; 1]>("chain_getHeader", [format!("0x{hash:x}")])
.await
.map_err(|e| {
crate::error::QuantusError::NetworkError(format!(
"Failed to get finalized block header: {e:?}"
))
})?;
let block_num = if let Some(block_number_str) = block_header["number"].as_str() {
u64::from_str_radix(&block_number_str[2..], 16).map(|n| n as u32).unwrap_or(0)
} else {
0
};
(hash, block_num)
} else {
log_print!("📋 Querying events from latest block");
let hash = quantus_client.get_latest_block().await?;
let block_header: serde_json::Value = quantus_client
.rpc_client()
.request::<serde_json::Value, [(); 0]>("chain_getHeader", [])
.await
.map_err(|e| {
crate::error::QuantusError::NetworkError(format!(
"Failed to get latest block header: {e:?}"
))
})?;
let block_num = if let Some(block_number_str) = block_header["number"].as_str() {
u64::from_str_radix(&block_number_str[2..], 16).map(|n| n as u32).unwrap_or(0)
} else {
0
};
(hash, block_num)
};
log_print!("🔮 Quantus CLI");
log_print!("🎯 Found Block #{}", block_number);
let events = quantus_client.client().blocks().at(block_hash).await?.events().await?;
log_print!("📋 Block Events:");
let mut event_count = 0;
let mut filtered_count = 0;
for event in events.iter() {
event_count += 1;
let event = event.map_err(|e| {
crate::error::QuantusError::NetworkError(format!("Failed to decode event: {e:?}"))
})?;
if let Some(ref filter) = pallet_filter {
if event.pallet_name() != filter {
continue;
}
}
filtered_count += 1;
log_print!(
" 📌 {}.{}",
event.pallet_name().bright_cyan(),
event.variant_name().bright_yellow()
);
if !raw && decode {
decode_event_details(&event)?;
}
if raw || crate::log::is_verbose() {
log_verbose!(" 📝 Raw event data: {:?}", event.field_bytes());
}
}
log_print!("");
if let Some(ref filter) = pallet_filter {
log_print!(
"📊 Summary: {} events total, {} events from {} pallet",
event_count,
filtered_count,
filter.bright_cyan()
);
} else {
log_print!("📊 Summary: {} events total", event_count);
}
if filtered_count == 0 && pallet_filter.is_some() {
log_print!("💡 Tip: No events found for the specified pallet. Try without --pallet filter to see all events.");
}
log_print!("💡 Tip: Use --verbose for raw event data");
log_print!("💡 Tip: Use --pallet <PALLET_NAME> to filter events by pallet");
Ok(())
}
fn decode_event_details<T: subxt::Config>(
event: &subxt::events::EventDetails<T>,
) -> crate::error::Result<()> {
if let Some(typed_message) = decode_event_typed(event) {
log_print!("{}", typed_message);
}
Ok(())
}
fn decode_event_typed<T: subxt::Config>(event: &subxt::events::EventDetails<T>) -> Option<String> {
let typed_event = event.as_root_event::<crate::chain::quantus_subxt::api::Event>().ok()?;
let formatted_event = format_event_with_ss58_addresses(&typed_event);
Some(format!(" 📝 {}", formatted_event.bright_cyan()))
}
fn format_event_with_ss58_addresses(event: &crate::chain::quantus_subxt::api::Event) -> String {
let debug_str = format!("{event:?}");
let mut result = debug_str.clone();
let mut replacements = 0;
while let Some(account_id) = extract_account_id_from_debug(&result) {
let ss58_address = format_account_id(&account_id);
let account_debug = format!("{account_id:?}");
result = result.replace(&account_debug, &ss58_address);
replacements += 1;
if replacements > 10 {
break;
} }
result
}
fn extract_account_id_from_debug(debug_str: &str) -> Option<subxt::utils::AccountId32> {
if let Some(start) = debug_str.find("AccountId32([") {
if let Some(end) = debug_str[start..].find("])") {
let bytes_str = &debug_str[start + 13..start + end];
let bytes: Vec<u8> = bytes_str
.split(',')
.map(|s| s.trim().parse::<u8>().ok())
.collect::<Option<Vec<u8>>>()?;
if bytes.len() == 32 {
let mut account_bytes = [0u8; 32];
account_bytes.copy_from_slice(&bytes);
return Some(subxt::utils::AccountId32::from(account_bytes));
}
}
}
None
}
fn format_account_id(account_id: &subxt::utils::AccountId32) -> String {
account_id.to_quantus_ss58()
}