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