Skip to main content

aztec_core/abi/
encoder.rs

1use super::checkers::{
2    abi_type_size, is_address_struct, is_bounded_vec_struct, is_function_selector_struct,
3    is_option_struct, is_wrapped_field_struct,
4};
5use super::types::{AbiType, AbiValue, FunctionArtifact};
6use crate::types::Fr;
7use crate::Error;
8
9/// ABI-encode a function's arguments into field elements.
10pub fn encode_arguments(function: &FunctionArtifact, args: &[AbiValue]) -> Result<Vec<Fr>, Error> {
11    if function.parameters.len() != args.len() {
12        return Err(Error::Abi(format!(
13            "function '{}' expects {} argument(s), got {}",
14            function.name,
15            function.parameters.len(),
16            args.len()
17        )));
18    }
19
20    let mut out = Vec::new();
21    for (param, value) in function.parameters.iter().zip(args) {
22        encode_value(&param.typ, value, &mut out)?;
23    }
24    Ok(out)
25}
26
27pub fn encode_value(typ: &AbiType, value: &AbiValue, out: &mut Vec<Fr>) -> Result<(), Error> {
28    match (typ, value) {
29        (AbiType::Field, AbiValue::Field(field)) => {
30            out.push(*field);
31            Ok(())
32        }
33        (AbiType::Boolean, AbiValue::Boolean(boolean)) => {
34            out.push(if *boolean { Fr::one() } else { Fr::zero() });
35            Ok(())
36        }
37        (AbiType::Integer { sign, width }, AbiValue::Integer(integer)) => {
38            if sign == "signed" && *integer < 0 {
39                let encoded = if *width >= 128 {
40                    *integer as u128
41                } else {
42                    (*integer + (1i128 << *width)) as u128
43                };
44                let bytes = encoded.to_be_bytes();
45                let mut padded = [0u8; 32];
46                padded[16..].copy_from_slice(&bytes);
47                out.push(Fr::from(padded));
48            } else {
49                let bytes = (*integer as u128).to_be_bytes();
50                let mut padded = [0u8; 32];
51                padded[16..].copy_from_slice(&bytes);
52                out.push(Fr::from(padded));
53            }
54            Ok(())
55        }
56        // Allow passing a raw field element for integer parameters. This is
57        // needed when the value exceeds `i128::MAX` (e.g. U128 values ≥ 2^127
58        // or intentional overflow values like 2^128 for testing).
59        (AbiType::Integer { .. }, AbiValue::Field(field)) => {
60            out.push(*field);
61            Ok(())
62        }
63        (AbiType::Array { element, length }, AbiValue::Array(items)) => {
64            if items.len() != *length {
65                return Err(Error::Abi(format!(
66                    "expected array of length {}, got {}",
67                    length,
68                    items.len()
69                )));
70            }
71            for item in items {
72                encode_value(element, item, out)?;
73            }
74            Ok(())
75        }
76        (AbiType::String { length }, AbiValue::String(string)) => {
77            let bytes = string.as_bytes();
78            if bytes.len() > *length {
79                return Err(Error::Abi(format!(
80                    "string exceeds fixed ABI length {}",
81                    length
82                )));
83            }
84            for byte in bytes {
85                out.push(Fr::from(u64::from(*byte)));
86            }
87            for _ in bytes.len()..*length {
88                out.push(Fr::zero());
89            }
90            Ok(())
91        }
92        (AbiType::Struct { fields, .. }, _) => {
93            // Special struct handling
94            if is_address_struct(typ) {
95                match value {
96                    AbiValue::Field(f) => {
97                        out.push(*f);
98                        Ok(())
99                    }
100                    AbiValue::Struct(map) => {
101                        let f =
102                            map.get("inner")
103                                .or_else(|| map.get("address"))
104                                .ok_or_else(|| {
105                                    Error::Abi(
106                                        "address struct must have 'inner' or 'address' field"
107                                            .into(),
108                                    )
109                                })?;
110                        encode_value(&AbiType::Field, f, out)
111                    }
112                    _ => Err(Error::Abi(
113                        "expected Field or Struct for address type".into(),
114                    )),
115                }
116            } else if is_function_selector_struct(typ) {
117                match value {
118                    AbiValue::Integer(v) => {
119                        out.push(Fr::from(*v as u64));
120                        Ok(())
121                    }
122                    AbiValue::Field(f) => {
123                        out.push(*f);
124                        Ok(())
125                    }
126                    AbiValue::Struct(map) => {
127                        let f = map
128                            .get("value")
129                            .or_else(|| map.get("inner"))
130                            .ok_or_else(|| {
131                                Error::Abi(
132                                    "FunctionSelector struct must have 'value' or 'inner' field"
133                                        .into(),
134                                )
135                            })?;
136                        encode_value(&AbiType::Field, f, out)
137                    }
138                    _ => Err(Error::Abi(
139                        "expected Integer, Field, or Struct for FunctionSelector".into(),
140                    )),
141                }
142            } else if is_wrapped_field_struct(typ) {
143                match value {
144                    AbiValue::Field(f) => {
145                        out.push(*f);
146                        Ok(())
147                    }
148                    AbiValue::Struct(map) => {
149                        let f = map.get("inner").ok_or_else(|| {
150                            Error::Abi("wrapped field struct must have 'inner' field".into())
151                        })?;
152                        encode_value(&AbiType::Field, f, out)
153                    }
154                    _ => Err(Error::Abi(
155                        "expected Field or Struct for wrapped field type".into(),
156                    )),
157                }
158            } else if is_bounded_vec_struct(typ) {
159                let items = match value {
160                    AbiValue::Array(items) => items,
161                    AbiValue::Struct(map) => {
162                        if let Some(AbiValue::Array(items)) = map.get("storage") {
163                            items
164                        } else {
165                            return Err(Error::Abi(
166                                "BoundedVec struct must have 'storage' array field".into(),
167                            ));
168                        }
169                    }
170                    _ => return Err(Error::Abi("expected Array or Struct for BoundedVec".into())),
171                };
172                let (elem_type, max_len) = match &fields[0].typ {
173                    AbiType::Array { element, length } => (element.as_ref(), *length),
174                    _ => return Err(Error::Abi("BoundedVec storage must be an Array".into())),
175                };
176                if items.len() > max_len {
177                    return Err(Error::Abi(format!(
178                        "BoundedVec has {} items but max capacity is {}",
179                        items.len(),
180                        max_len
181                    )));
182                }
183                for item in items {
184                    encode_value(elem_type, item, out)?;
185                }
186                let elem_size = abi_type_size(elem_type);
187                for _ in 0..(max_len - items.len()) * elem_size {
188                    out.push(Fr::zero());
189                }
190                out.push(Fr::from(items.len() as u64));
191                Ok(())
192            } else if is_option_struct(typ) {
193                match value {
194                    AbiValue::Struct(map) => {
195                        let is_some = map.get("_is_some");
196                        let val = map.get("_value");
197                        match (is_some, val) {
198                            (Some(is_some_val), Some(value_val)) => {
199                                encode_value(&fields[0].typ, is_some_val, out)?;
200                                encode_value(&fields[1].typ, value_val, out)?;
201                                Ok(())
202                            }
203                            _ => Err(Error::Abi(
204                                "Option struct must have '_is_some' and '_value' fields".into(),
205                            )),
206                        }
207                    }
208                    _ => {
209                        // Treat as Some(value)
210                        out.push(Fr::one()); // _is_some = true
211                        encode_value(&fields[1].typ, value, out)?;
212                        Ok(())
213                    }
214                }
215            } else if let AbiValue::Struct(values) = value {
216                // Generic struct encoding
217                for field in fields {
218                    let field_value = values.get(&field.name).ok_or_else(|| {
219                        Error::Abi(format!("missing struct field '{}'", field.name))
220                    })?;
221                    encode_value(&field.typ, field_value, out)?;
222                }
223                Ok(())
224            } else {
225                Err(Error::Abi("argument type/value mismatch".to_owned()))
226            }
227        }
228        (AbiType::Tuple { elements }, AbiValue::Tuple(values)) => {
229            if elements.len() != values.len() {
230                return Err(Error::Abi(format!(
231                    "expected tuple of length {}, got {}",
232                    elements.len(),
233                    values.len()
234                )));
235            }
236            for (element, value) in elements.iter().zip(values) {
237                encode_value(element, value, out)?;
238            }
239            Ok(())
240        }
241        _ => Err(Error::Abi("argument type/value mismatch".to_owned())),
242    }
243}