ethane_abi/
lib.rs

1use std::collections::HashMap;
2use std::fs::File;
3use std::path::Path;
4
5use tiny_keccak::{Hasher, Keccak};
6
7mod function;
8mod parameter;
9
10use function::Function;
11pub use function::StateMutability;
12pub use parameter::Parameter;
13use parameter::ParameterType;
14
15/// Parses a `.json` file containing ABI encoded Solidity functions.
16///
17/// It stores the functions in a `HashMap` with the function name being the key
18/// and the parsed function the value.
19pub struct Abi {
20    pub functions: HashMap<String, Function>,
21}
22
23impl Default for Abi {
24    fn default() -> Self {
25        Self::new()
26    }
27}
28
29impl Abi {
30    /// Creates a new `Abi` instance with an empty `HashMap` within.
31    #[inline]
32    pub fn new() -> Self {
33        Self {
34            functions: HashMap::new(),
35        }
36    }
37
38    /// Parses an ABI value into the `Abi` instance.
39    pub fn parse_json(&mut self, abi: serde_json::Value) -> Result<(), AbiParserError> {
40        let mut i: usize = 0;
41        while abi[i] != serde_json::Value::Null {
42            if abi[i]["type"] == "function" {
43                if abi[i]["name"] != serde_json::Value::Null {
44                    let name = abi[i]["name"].as_str().unwrap().to_owned();
45                    self.functions.insert(name, Function::parse(&abi[i])?);
46                } else {
47                    return Err(AbiParserError::MissingData(
48                        "Function name is missing from ABI.".to_owned(),
49                    ));
50                }
51            }
52            i += 1;
53        }
54
55        Ok(())
56    }
57
58    /// Parses an ABI `.json` file into the `Abi` instance.
59    pub fn parse_file(&mut self, path_to_abi: &Path) -> Result<(), AbiParserError> {
60        let reader =
61            File::open(path_to_abi).map_err(|e| AbiParserError::FileIoError(e.to_string()))?;
62        let abi: serde_json::Value =
63            serde_json::from_reader(reader).map_err(|e| AbiParserError::Serde(e.to_string()))?;
64
65        self.parse_json(abi)
66    }
67
68    pub fn get_state_mutability(&self, function_name: &str) -> Option<StateMutability> {
69        if let Some(function) = self.functions.get(function_name) {
70            return function.state_mutability;
71        }
72
73        None
74    }
75
76    /// Encodes a function call according to Solidity's contract [ABI
77    /// specification](https://docs.soliditylang.org/en/v0.5.3/abi-spec.html#function-selector).
78    ///
79    /// If the given function is found in the parsed ABI file, the function
80    /// encodes the function signature and the input data provided in the form
81    /// of a [`Parameter`] vector.
82    pub fn encode(
83        &self,
84        function_name: &str,
85        parameters: Vec<Parameter>,
86    ) -> Result<Vec<u8>, AbiParserError> {
87        if let Some(function) = self.functions.get(function_name) {
88            let mut abi_arguments = Vec::<String>::with_capacity(parameters.len());
89            for (input, param) in function.inputs.iter().zip(parameters.iter()) {
90                if input.parameter_type.type_check(param) {
91                    abi_arguments.push(input.parameter_type.as_abi_string())
92                } else {
93                    return Err(AbiParserError::InvalidAbiEncoding(format!(
94                        "Invalid parameter type supplied. Expected {:?}",
95                        input.parameter_type
96                    )));
97                }
98            }
99            let signature = format!("{}({})", function_name, abi_arguments.join(","));
100            let mut hasher = Keccak::v256();
101            hasher.update(signature.as_bytes());
102            // Take first 4 bytes of the Keccak hash
103            let mut out = [0_u8; 32];
104            hasher.finalize(&mut out);
105            let mut hash = out[0..4].to_vec();
106            // Append the encoded parameters to the hash
107            parameter::encode_into(&mut hash, parameters);
108            Ok(hash)
109        } else {
110            Err(AbiParserError::MissingData(
111                "Function name not found in ABI".to_owned(),
112            ))
113        }
114    }
115
116    /// Decodes a hash into a [`Parameter`] vector.
117    ///
118    /// Based on the given ABI function name, the `Abi` parser iterates over that
119    /// function's output parameter types and decodes the output hash accordingly.
120    pub fn decode(
121        &self,
122        function_name: &str,
123        hash: &[u8],
124    ) -> Result<Vec<Parameter>, AbiParserError> {
125        if let Some(function) = self.functions.get(function_name) {
126            let mut start_index = 0;
127            let mut parameters = Vec::<Parameter>::with_capacity(function.outputs.len());
128            for output in &function.outputs {
129                let (parameter, i) =
130                    Parameter::decode(&output.parameter_type, &hash[start_index..]);
131                start_index += i;
132                parameters.push(parameter);
133            }
134
135            Ok(parameters)
136        } else {
137            Err(AbiParserError::MissingData(
138                "Function name not found in ABI".to_owned(),
139            ))
140        }
141    }
142}
143
144#[derive(Debug)]
145pub enum AbiParserError {
146    FileIoError(String),
147    Serde(String),
148    MissingData(String),
149    InvalidAbiEncoding(String),
150    TypeError,
151}