1use super::{
18 BalanceVariant,
19 TokenMetadata,
20};
21use crate::DEFAULT_KEY_COL_WIDTH;
22use colored::Colorize as _;
23use contract_build::Verbosity;
24use contract_transcode::{
25 ContractMessageTranscoder,
26 Hex,
27 TranscoderBuilder,
28 Value,
29};
30
31use anyhow::Result;
32use ink_env::Environment;
33use scale_info::form::PortableForm;
34use std::{
35 fmt::{
36 Display,
37 Write,
38 },
39 str::FromStr,
40};
41use subxt::{
42 self,
43 Config,
44 blocks::ExtrinsicEvents,
45 config::HashFor,
46 events::StaticEvent,
47 ext::{
48 scale_decode::{
49 self,
50 IntoVisitor,
51 },
52 scale_encode,
53 },
54 utils::{
55 H160,
56 H256,
57 },
58};
59
60#[derive(
62 scale::Decode,
63 scale::Encode,
64 scale_decode::DecodeAsType,
65 scale_encode::EncodeAsType,
66 Debug,
67)]
68#[decode_as_type(crate_path = "subxt::ext::scale_decode")]
69#[encode_as_type(crate_path = "subxt::ext::scale_encode")]
70pub struct ContractEmitted {
71 contract: H160,
73 data: Vec<u8>,
76 topics: Vec<H256>,
79}
80
81impl StaticEvent for ContractEmitted {
82 const PALLET: &'static str = "Revive";
83 const EVENT: &'static str = "ContractEmitted";
84}
85
86#[derive(
88 scale::Decode,
89 scale::Encode,
90 scale_decode::DecodeAsType,
91 scale_encode::EncodeAsType,
92 Debug,
93)]
94#[decode_as_type(crate_path = "subxt::ext::scale_decode")]
95#[encode_as_type(crate_path = "subxt::ext::scale_encode")]
96pub struct ContractInstantiated {
97 pub deployer: H160,
99 pub contract: H160,
101}
102
103impl StaticEvent for ContractInstantiated {
104 const PALLET: &'static str = "Revive";
105 const EVENT: &'static str = "Instantiated";
106}
107
108#[derive(serde::Serialize)]
110pub struct Field {
111 pub name: String,
113 pub value: Value,
115 #[serde(skip_serializing)]
117 pub type_name: Option<String>,
118}
119
120impl Field {
121 pub fn new(name: String, value: Value, type_name: Option<String>) -> Self {
122 Field {
123 name,
124 value,
125 type_name,
126 }
127 }
128}
129
130#[derive(serde::Serialize)]
132pub struct Event {
133 pub pallet: String,
135 pub name: String,
137 pub fields: Vec<Field>,
139}
140
141#[derive(serde::Serialize)]
143#[allow(dead_code)]
144pub struct Events(Vec<Event>);
145
146#[derive(serde::Serialize)]
148pub struct DisplayEvents(Vec<Event>);
149
150#[allow(clippy::needless_borrows_for_generic_args)]
151impl DisplayEvents {
152 pub fn from_events<C: Config, E: Environment>(
154 result: &ExtrinsicEvents<C>,
155 transcoder: Option<&ContractMessageTranscoder>,
156 subxt_metadata: &subxt::Metadata,
157 ) -> Result<DisplayEvents>
158 where
159 C::AccountId: IntoVisitor,
160 {
161 let mut events: Vec<Event> = vec![];
162
163 let events_transcoder = TranscoderBuilder::new(subxt_metadata.types())
164 .with_default_custom_type_transcoders()
165 .done();
166
167 for event in result.iter() {
168 let event = event?;
169 tracing::debug!(
170 "displaying event {}:{}",
171 event.pallet_name(),
172 event.variant_name()
173 );
174
175 let event_metadata = event.event_metadata();
176 let event_fields = &event_metadata.variant.fields;
177
178 let mut event_entry = Event {
179 pallet: event.pallet_name().to_string(),
180 name: event.variant_name().to_string(),
181 fields: vec![],
182 };
183
184 let event_data = &mut event.field_bytes();
185 let event_sig_topic = event.topics().iter().next();
186 let mut unnamed_field_name = 0;
187 for field_metadata in event_fields {
188 if <ContractEmitted as StaticEvent>::is_event(
189 event.pallet_name(),
190 event.variant_name(),
191 ) && (field_metadata.name == Some("data".to_string())
192 || field_metadata.name == Some("topics".to_string()))
193 {
194 tracing::debug!("event data: {:?}", hex::encode(&event_data));
195 let field = contract_event_vec_field::<C>(
196 transcoder,
197 field_metadata,
198 event_sig_topic,
199 event_data,
200 field_metadata.name.as_ref().expect("must exist"),
201 )?;
202 event_entry.fields.push(field);
203 } else {
204 let field_name = field_metadata
205 .name
206 .as_ref()
207 .map(|s| s.to_string())
208 .unwrap_or_else(|| {
209 let name = unnamed_field_name.to_string();
210 unnamed_field_name += 1;
211 name
212 });
213
214 let decoded_field = events_transcoder.decode(
215 subxt_metadata.types(),
216 field_metadata.ty.id,
217 event_data,
218 )?;
219 let field = Field::new(
220 field_name,
221 decoded_field,
222 field_metadata.type_name.as_ref().map(|s| s.to_string()),
223 );
224 event_entry.fields.push(field);
225 }
226 }
227 events.push(event_entry);
228 }
229
230 Ok(DisplayEvents(events))
231 }
232
233 pub fn display_events<E: Environment>(
235 &self,
236 verbosity: Verbosity,
237 token_metadata: &TokenMetadata,
238 ) -> Result<String>
239 where
240 E::Balance: Display + From<u128>,
241 {
242 let event_field_indent: usize = DEFAULT_KEY_COL_WIDTH - 3;
243 let mut out = format!(
244 "{:>width$}\n",
245 "Events".bright_purple().bold(),
246 width = DEFAULT_KEY_COL_WIDTH
247 );
248 for event in &self.0 {
249 let _ = writeln!(
250 out,
251 "{:>width$} {} ➜ {}",
252 "Event".bright_green().bold(),
253 event.pallet.bright_white(),
254 event.name.bright_white().bold(),
255 width = DEFAULT_KEY_COL_WIDTH
256 );
257
258 for field in &event.fields {
259 if verbosity.is_verbose() {
260 let mut value: String = field.value.to_string();
261 if (field.type_name == Some("T::Balance".to_string())
262 || field.type_name == Some("BalanceOf<T>".to_string()))
263 && let Value::UInt(balance) = field.value
264 {
265 value = BalanceVariant::<E::Balance>::from(
266 balance,
267 Some(token_metadata),
268 )?
269 .to_string();
270 }
271 if field.type_name == Some("H160".to_string()) {
272 if let (Some(start), Some(end)) =
276 (value.find('['), value.find(']'))
277 {
278 let byte_str = &value[start + 1..end];
279 let bytes: Vec<u8> = byte_str
280 .split(", ")
281 .filter_map(|s| s.parse::<u8>().ok())
282 .collect();
283 let h160_value = H160::from_slice(&bytes);
284 value = format!("0x{}", hex::encode(h160_value.as_bytes()));
285 }
286 }
287 let _ = writeln!(
288 out,
289 "{:width$}{}: {}",
290 "",
291 field.name.bright_white(),
292 value,
293 width = event_field_indent,
294 );
295 }
296 }
297 }
298 Ok(out)
299 }
300
301 pub fn to_json(&self) -> Result<String> {
303 Ok(serde_json::to_string_pretty(self)?)
304 }
305}
306
307#[allow(clippy::needless_borrows_for_generic_args)]
310fn contract_event_vec_field<C: Config>(
311 transcoder: Option<&ContractMessageTranscoder>,
312 field_metadata: &scale_info::Field<PortableForm>,
313 event_sig_topic: Option<&HashFor<C>>,
314 event_data: &mut &[u8],
315 field_name: &String,
316) -> Result<Field> {
317 let event_value = if let Some(transcoder) = transcoder {
318 if let Some(event_sig_topic) = event_sig_topic {
319 match transcoder.decode_contract_event(event_sig_topic, event_data) {
320 Ok(contract_event) => contract_event,
321 Err(err) => {
322 tracing::warn!(
323 "Decoding contract event failed: {:?}. It might have come from another contract.",
324 err
325 );
326 Value::Hex(Hex::from_str(&hex::encode(&event_data))?)
327 }
328 }
329 } else {
330 tracing::info!("Anonymous event not decoded. Data displayed as raw hex.");
331 Value::Hex(Hex::from_str(&hex::encode(event_data))?)
332 }
333 } else {
334 Value::Hex(Hex::from_str(&hex::encode(event_data))?)
335 };
336 Ok(Field::new(
337 field_name.to_string(),
338 event_value,
339 field_metadata.type_name.as_ref().map(|s| s.to_string()),
340 ))
341}