1use fuel_gql_client::fuel_tx::Receipt;
2use fuels_core::{try_from_bytes, DecodableLog, Parameterize, Tokenizable};
3use fuels_types::{bech32::Bech32ContractId, errors::Error, param_types::ParamType};
4use std::{
5 collections::{HashMap, HashSet},
6 fmt::Debug,
7};
8
9#[derive(Debug, Clone, Default)]
11pub struct LogDecoder {
12 pub logs_map: HashMap<(Bech32ContractId, u64), ParamType>,
14}
15
16impl LogDecoder {
17 pub fn get_logs(&self, receipts: &[Receipt]) -> Result<Vec<String>, Error> {
19 let ids_with_data = receipts.iter().filter_map(|r| match r {
20 Receipt::LogData { rb, data, id, .. } => {
21 Some(((Bech32ContractId::from(*id), *rb), data.clone()))
22 }
23 Receipt::Log { ra, rb, id, .. } => Some((
24 (Bech32ContractId::from(*id), *rb),
25 ra.to_be_bytes().to_vec(),
26 )),
27 _ => None,
28 });
29
30 ids_with_data
31 .map(|((c_id, log_id), data)| {
32 let param_type = self
33 .logs_map
34 .get(&(c_id, log_id))
35 .ok_or_else(|| Error::InvalidData("Failed to find log id".into()))?;
36
37 param_type.decode_log(&data)
38 })
39 .collect::<Result<Vec<String>, Error>>()
40 }
41
42 pub fn get_logs_with_type<T: Tokenizable + Parameterize>(
45 &self,
46 receipts: &[Receipt],
47 ) -> Result<Vec<T>, Error> {
48 let target_param_type = T::param_type();
49
50 let target_ids: HashSet<(Bech32ContractId, u64)> = self
51 .logs_map
52 .iter()
53 .filter_map(|((c_id, log_id), param_type)| {
54 if *param_type == target_param_type {
55 Some((c_id.clone(), *log_id))
56 } else {
57 None
58 }
59 })
60 .collect();
61
62 let decoded_logs: Vec<T> = receipts
63 .iter()
64 .filter_map(|r| match r {
65 Receipt::LogData { id, rb, data, .. }
66 if target_ids.contains(&(Bech32ContractId::from(*id), *rb)) =>
67 {
68 Some(data.clone())
69 }
70 Receipt::Log { id, ra, rb, .. }
71 if target_ids.contains(&(Bech32ContractId::from(*id), *rb)) =>
72 {
73 Some(ra.to_be_bytes().to_vec())
74 }
75 _ => None,
76 })
77 .map(|data| try_from_bytes(&data))
78 .collect::<Result<Vec<_>, _>>()?;
79
80 Ok(decoded_logs)
81 }
82
83 pub fn merge(&mut self, log_decoder: &LogDecoder) {
84 self.logs_map
85 .extend(log_decoder.logs_map.clone().into_iter());
86 }
87}
88
89pub fn decode_revert_error(err: Error, log_decoder: &LogDecoder) -> Error {
91 if let Error::RevertTransactionError(_, receipts) = &err {
92 if let Ok(logs) = log_decoder.get_logs(receipts) {
93 if let Some(log) = logs.into_iter().next() {
94 return Error::RevertTransactionError(log, receipts.to_owned());
95 }
96 }
97 }
98 err
99}