fuels_core/codec/
abi_formatter.rs

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
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
use std::{collections::HashMap, io::Read};

use fuel_abi_types::abi::unified_program::UnifiedProgramABI;
use itertools::Itertools;

use crate::{error, types::param_types::ParamType, Result};

use super::{ABIDecoder, DecoderConfig};

pub struct ABIFormatter {
    functions: HashMap<String, Vec<ParamType>>,
    configurables: Vec<(String, ParamType)>,
    decoder: ABIDecoder,
}

impl ABIFormatter {
    pub fn has_fn(&self, fn_name: &str) -> bool {
        self.functions.contains_key(fn_name)
    }

    pub fn with_decoder_config(mut self, config: DecoderConfig) -> Self {
        self.decoder = ABIDecoder::new(config);
        self
    }

    pub fn from_abi(abi: UnifiedProgramABI) -> Result<Self> {
        let functions = abi
            .functions
            .iter()
            .map(|fun| (fun.name.clone(), fun.clone()))
            .collect::<HashMap<_, _>>();

        let type_lookup = abi
            .types
            .iter()
            .map(|decl| (decl.type_id, decl.clone()))
            .collect::<HashMap<_, _>>();

        let functions = functions
            .into_iter()
            .map(|(name, fun)| {
                let args = fun
                    .inputs
                    .iter()
                    .map(|type_application| {
                        ParamType::try_from_type_application(type_application, &type_lookup)
                    })
                    .collect::<Result<Vec<_>>>()?;
                Ok((name.clone(), args))
            })
            .collect::<Result<HashMap<_, _>>>()?;

        let configurables = abi
            .configurables
            .into_iter()
            .flatten()
            .sorted_by_key(|c| c.offset)
            .map(|c| {
                let param_type =
                    ParamType::try_from_type_application(&c.application, &type_lookup)?;

                Ok((c.name, param_type))
            })
            .collect::<Result<Vec<_>>>()?;

        Ok(Self {
            functions,
            decoder: ABIDecoder::default(),
            configurables,
        })
    }

    pub fn from_json_abi(abi: impl AsRef<str>) -> Result<Self> {
        let parsed_abi = UnifiedProgramABI::from_json_abi(abi.as_ref())?;
        Self::from_abi(parsed_abi)
    }

    pub fn decode_fn_args<R: Read>(&self, fn_name: &str, data: R) -> Result<Vec<String>> {
        let args = self
            .functions
            .get(fn_name)
            .ok_or_else(|| error!(Codec, "Function '{}' not found in the ABI", fn_name))?;

        self.decoder.decode_multiple_as_debug_str(args, data)
    }

    pub fn decode_configurables<R: Read>(
        &self,
        configurable_data: R,
    ) -> Result<Vec<(String, String)>> {
        let param_types = self
            .configurables
            .iter()
            .map(|(_, param_type)| param_type)
            .cloned()
            .collect::<Vec<_>>();

        let decoded = self
            .decoder
            .decode_multiple_as_debug_str(&param_types, configurable_data)?
            .into_iter()
            .zip(&self.configurables)
            .map(|(value, (name, _))| (name.clone(), value))
            .collect();

        Ok(decoded)
    }
}

#[cfg(test)]
mod tests {
    use crate::types::errors::Error;

    use super::*;

    #[test]
    fn gracefully_handles_missing_fn() {
        // given
        let decoder = ABIFormatter::from_abi(UnifiedProgramABI::default()).unwrap();

        // when
        let err = decoder
            .decode_fn_args("non_existent_fn", [].as_slice())
            .unwrap_err();

        // then
        let Error::Codec(err) = err else {
            panic!("Expected Codec error, got {:?}", err);
        };

        assert_eq!(err, "Function 'non_existent_fn' not found in the ABI");
    }
}