use super::{
runtime_api::api::contracts::events::ContractEmitted,
DefaultConfig,
};
use crate::{
Verbosity,
DEFAULT_KEY_COL_WIDTH,
};
use colored::Colorize as _;
use transcode::{
ContractMessageTranscoder,
TranscoderBuilder,
Value,
};
use anyhow::Result;
use std::fmt::Write;
use subxt::{
self,
events::StaticEvent,
tx::TxEvents,
};
#[derive(serde::Serialize)]
pub struct Field {
pub name: String,
pub value: Value,
}
impl Field {
pub fn new(name: String, value: Value) -> Self {
Field { name, value }
}
}
#[derive(serde::Serialize)]
pub struct Event {
pub pallet: String,
pub name: String,
pub fields: Vec<Field>,
}
#[derive(serde::Serialize)]
pub struct DisplayEvents(Vec<Event>);
impl DisplayEvents {
pub fn from_events(
result: &TxEvents<DefaultConfig>,
transcoder: &ContractMessageTranscoder,
subxt_metadata: &subxt::Metadata,
) -> Result<DisplayEvents> {
let mut events: Vec<Event> = vec![];
let runtime_metadata = subxt_metadata.runtime_metadata();
let events_transcoder = TranscoderBuilder::new(&runtime_metadata.types)
.with_default_custom_type_transcoders()
.done();
for event in result.iter() {
let event = event?;
tracing::debug!("displaying event {:?}", event);
let event_metadata =
subxt_metadata.event(event.pallet_index(), event.variant_index())?;
let event_fields = event_metadata.fields();
let mut event_entry = Event {
pallet: event.pallet_name().to_string(),
name: event.variant_name().to_string(),
fields: vec![],
};
let event_data = &mut event.field_bytes();
let mut unnamed_field_name = 0;
for field in event_fields {
if <ContractEmitted as StaticEvent>::is_event(
event.pallet_name(),
event.variant_name(),
) && field.name() == Some("data")
{
tracing::debug!("event data: {:?}", hex::encode(&event_data));
match transcoder.decode_contract_event(event_data) {
Ok(contract_event) => {
let field = Field::new(String::from("data"), contract_event);
event_entry.fields.push(field);
}
Err(err) => {
tracing::warn!(
"Decoding contract event failed: {:?}. It might have come from another contract.",
err
)
}
}
} else {
let field_name =
field.name().map(ToOwned::to_owned).unwrap_or_else(|| {
let name = unnamed_field_name.to_string();
unnamed_field_name += 1;
name
});
let decoded_field = events_transcoder.decode(
&runtime_metadata.types,
field.type_id(),
event_data,
)?;
let field = Field::new(field_name, decoded_field);
event_entry.fields.push(field);
}
}
events.push(event_entry);
}
Ok(DisplayEvents(events))
}
pub fn display_events(&self, verbosity: Verbosity) -> String {
let event_field_indent: usize = DEFAULT_KEY_COL_WIDTH - 3;
let mut out = format!(
"{:>width$}\n",
"Events".bold(),
width = DEFAULT_KEY_COL_WIDTH
);
for event in &self.0 {
let _ = writeln!(
out,
"{:>width$} {} ➜ {}",
"Event".bright_green().bold(),
event.pallet.bright_white(),
event.name.bright_white().bold(),
width = DEFAULT_KEY_COL_WIDTH
);
for field in &event.fields {
if verbosity.is_verbose() {
let _ = writeln!(
out,
"{:width$}{}: {}",
"",
field.name.bright_white(),
field.value,
width = event_field_indent,
);
}
}
}
out
}
pub fn to_json(&self) -> Result<String> {
Ok(serde_json::to_string_pretty(self)?)
}
}