fuels_core/codec/
abi_formatter.rs

1use std::{collections::HashMap, io::Read};
2
3use fuel_abi_types::abi::unified_program::UnifiedProgramABI;
4use itertools::Itertools;
5
6use crate::{error, types::param_types::ParamType, Result};
7
8use super::{ABIDecoder, DecoderConfig};
9
10pub struct ABIFormatter {
11    functions: HashMap<String, Vec<ParamType>>,
12    configurables: Vec<(String, ParamType)>,
13    decoder: ABIDecoder,
14}
15
16impl ABIFormatter {
17    pub fn has_fn(&self, fn_name: &str) -> bool {
18        self.functions.contains_key(fn_name)
19    }
20
21    pub fn with_decoder_config(mut self, config: DecoderConfig) -> Self {
22        self.decoder = ABIDecoder::new(config);
23        self
24    }
25
26    pub fn from_abi(abi: UnifiedProgramABI) -> Result<Self> {
27        let functions = abi
28            .functions
29            .iter()
30            .map(|fun| (fun.name.clone(), fun.clone()))
31            .collect::<HashMap<_, _>>();
32
33        let type_lookup = abi
34            .types
35            .iter()
36            .map(|decl| (decl.type_id, decl.clone()))
37            .collect::<HashMap<_, _>>();
38
39        let functions = functions
40            .into_iter()
41            .map(|(name, fun)| {
42                let args = fun
43                    .inputs
44                    .iter()
45                    .map(|type_application| {
46                        ParamType::try_from_type_application(type_application, &type_lookup)
47                    })
48                    .collect::<Result<Vec<_>>>()?;
49                Ok((name.clone(), args))
50            })
51            .collect::<Result<HashMap<_, _>>>()?;
52
53        let configurables = abi
54            .configurables
55            .into_iter()
56            .flatten()
57            .sorted_by_key(|c| c.offset)
58            .map(|c| {
59                let param_type =
60                    ParamType::try_from_type_application(&c.application, &type_lookup)?;
61
62                Ok((c.name, param_type))
63            })
64            .collect::<Result<Vec<_>>>()?;
65
66        Ok(Self {
67            functions,
68            decoder: ABIDecoder::default(),
69            configurables,
70        })
71    }
72
73    pub fn from_json_abi(abi: impl AsRef<str>) -> Result<Self> {
74        let parsed_abi = UnifiedProgramABI::from_json_abi(abi.as_ref())?;
75        Self::from_abi(parsed_abi)
76    }
77
78    pub fn decode_fn_args<R: Read>(&self, fn_name: &str, data: R) -> Result<Vec<String>> {
79        let args = self
80            .functions
81            .get(fn_name)
82            .ok_or_else(|| error!(Codec, "Function '{}' not found in the ABI", fn_name))?;
83
84        self.decoder.decode_multiple_as_debug_str(args, data)
85    }
86
87    pub fn decode_configurables<R: Read>(
88        &self,
89        configurable_data: R,
90    ) -> Result<Vec<(String, String)>> {
91        let param_types = self
92            .configurables
93            .iter()
94            .map(|(_, param_type)| param_type)
95            .cloned()
96            .collect::<Vec<_>>();
97
98        let decoded = self
99            .decoder
100            .decode_multiple_as_debug_str(&param_types, configurable_data)?
101            .into_iter()
102            .zip(&self.configurables)
103            .map(|(value, (name, _))| (name.clone(), value))
104            .collect();
105
106        Ok(decoded)
107    }
108}
109
110#[cfg(test)]
111mod tests {
112    use crate::types::errors::Error;
113
114    use super::*;
115
116    #[test]
117    fn gracefully_handles_missing_fn() {
118        // given
119        let decoder = ABIFormatter::from_abi(UnifiedProgramABI::default()).unwrap();
120
121        // when
122        let err = decoder
123            .decode_fn_args("non_existent_fn", [].as_slice())
124            .unwrap_err();
125
126        // then
127        let Error::Codec(err) = err else {
128            panic!("Expected Codec error, got {:?}", err);
129        };
130
131        assert_eq!(err, "Function 'non_existent_fn' not found in the ABI");
132    }
133}