fuels_contract/
logs.rs

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/// Struct used to pass the log mappings from the Abigen
10#[derive(Debug, Clone, Default)]
11pub struct LogDecoder {
12    /// A mapping of (contract-id, log-id) and param-type
13    pub logs_map: HashMap<(Bech32ContractId, u64), ParamType>,
14}
15
16impl LogDecoder {
17    /// Get all decoded logs from the given receipts as `String`
18    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    /// Get decoded logs with specific type from the given receipts.
43    /// Note that this method returns the actual type and not a `String` representation.
44    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
89/// Decodes the logged type from the receipt of a `RevertTransactionError` if available
90pub 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}