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 inner_value = 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                        // Inner can be either Field or Integer (u32) — accept both.
137                        match inner_value {
138                            AbiValue::Field(f) => {
139                                out.push(*f);
140                                Ok(())
141                            }
142                            AbiValue::Integer(v) => {
143                                out.push(Fr::from(*v as u64));
144                                Ok(())
145                            }
146                            _ => Err(Error::Abi(
147                                "FunctionSelector inner must be Field or Integer".into(),
148                            )),
149                        }
150                    }
151                    _ => Err(Error::Abi(
152                        "expected Integer, Field, or Struct for FunctionSelector".into(),
153                    )),
154                }
155            } else if is_wrapped_field_struct(typ) {
156                match value {
157                    AbiValue::Field(f) => {
158                        out.push(*f);
159                        Ok(())
160                    }
161                    AbiValue::Struct(map) => {
162                        let f = map.get("inner").ok_or_else(|| {
163                            Error::Abi("wrapped field struct must have 'inner' field".into())
164                        })?;
165                        encode_value(&AbiType::Field, f, out)
166                    }
167                    _ => Err(Error::Abi(
168                        "expected Field or Struct for wrapped field type".into(),
169                    )),
170                }
171            } else if is_bounded_vec_struct(typ) {
172                let items = match value {
173                    AbiValue::Array(items) => items,
174                    AbiValue::Struct(map) => {
175                        if let Some(AbiValue::Array(items)) = map.get("storage") {
176                            items
177                        } else {
178                            return Err(Error::Abi(
179                                "BoundedVec struct must have 'storage' array field".into(),
180                            ));
181                        }
182                    }
183                    _ => return Err(Error::Abi("expected Array or Struct for BoundedVec".into())),
184                };
185                let (elem_type, max_len) = match &fields[0].typ {
186                    AbiType::Array { element, length } => (element.as_ref(), *length),
187                    _ => return Err(Error::Abi("BoundedVec storage must be an Array".into())),
188                };
189                if items.len() > max_len {
190                    return Err(Error::Abi(format!(
191                        "BoundedVec has {} items but max capacity is {}",
192                        items.len(),
193                        max_len
194                    )));
195                }
196                for item in items {
197                    encode_value(elem_type, item, out)?;
198                }
199                let elem_size = abi_type_size(elem_type);
200                for _ in 0..(max_len - items.len()) * elem_size {
201                    out.push(Fr::zero());
202                }
203                out.push(Fr::from(items.len() as u64));
204                Ok(())
205            } else if is_option_struct(typ) {
206                match value {
207                    AbiValue::Struct(map) => {
208                        let is_some = map.get("_is_some");
209                        let val = map.get("_value");
210                        match (is_some, val) {
211                            (Some(is_some_val), Some(value_val)) => {
212                                encode_value(&fields[0].typ, is_some_val, out)?;
213                                encode_value(&fields[1].typ, value_val, out)?;
214                                Ok(())
215                            }
216                            _ => Err(Error::Abi(
217                                "Option struct must have '_is_some' and '_value' fields".into(),
218                            )),
219                        }
220                    }
221                    _ => {
222                        // Treat as Some(value)
223                        out.push(Fr::one()); // _is_some = true
224                        encode_value(&fields[1].typ, value, out)?;
225                        Ok(())
226                    }
227                }
228            } else if let AbiValue::Struct(values) = value {
229                // Generic struct encoding
230                for field in fields {
231                    let field_value = values.get(&field.name).ok_or_else(|| {
232                        Error::Abi(format!("missing struct field '{}'", field.name))
233                    })?;
234                    encode_value(&field.typ, field_value, out)?;
235                }
236                Ok(())
237            } else {
238                Err(Error::Abi("argument type/value mismatch".to_owned()))
239            }
240        }
241        (AbiType::Tuple { elements }, AbiValue::Tuple(values)) => {
242            if elements.len() != values.len() {
243                return Err(Error::Abi(format!(
244                    "expected tuple of length {}, got {}",
245                    elements.len(),
246                    values.len()
247                )));
248            }
249            for (element, value) in elements.iter().zip(values) {
250                encode_value(element, value, out)?;
251            }
252            Ok(())
253        }
254        _ => Err(Error::Abi("argument type/value mismatch".to_owned())),
255    }
256}