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        (AbiType::Array { element, length }, AbiValue::Array(items)) => {
57            if items.len() != *length {
58                return Err(Error::Abi(format!(
59                    "expected array of length {}, got {}",
60                    length,
61                    items.len()
62                )));
63            }
64            for item in items {
65                encode_value(element, item, out)?;
66            }
67            Ok(())
68        }
69        (AbiType::String { length }, AbiValue::String(string)) => {
70            let bytes = string.as_bytes();
71            if bytes.len() > *length {
72                return Err(Error::Abi(format!(
73                    "string exceeds fixed ABI length {}",
74                    length
75                )));
76            }
77            for byte in bytes {
78                out.push(Fr::from(u64::from(*byte)));
79            }
80            for _ in bytes.len()..*length {
81                out.push(Fr::zero());
82            }
83            Ok(())
84        }
85        (AbiType::Struct { fields, .. }, _) => {
86            // Special struct handling
87            if is_address_struct(typ) {
88                match value {
89                    AbiValue::Field(f) => {
90                        out.push(*f);
91                        Ok(())
92                    }
93                    AbiValue::Struct(map) => {
94                        let f =
95                            map.get("inner")
96                                .or_else(|| map.get("address"))
97                                .ok_or_else(|| {
98                                    Error::Abi(
99                                        "address struct must have 'inner' or 'address' field"
100                                            .into(),
101                                    )
102                                })?;
103                        encode_value(&AbiType::Field, f, out)
104                    }
105                    _ => Err(Error::Abi(
106                        "expected Field or Struct for address type".into(),
107                    )),
108                }
109            } else if is_function_selector_struct(typ) {
110                match value {
111                    AbiValue::Integer(v) => {
112                        out.push(Fr::from(*v as u64));
113                        Ok(())
114                    }
115                    AbiValue::Field(f) => {
116                        out.push(*f);
117                        Ok(())
118                    }
119                    AbiValue::Struct(map) => {
120                        let f = map
121                            .get("value")
122                            .or_else(|| map.get("inner"))
123                            .ok_or_else(|| {
124                                Error::Abi(
125                                    "FunctionSelector struct must have 'value' or 'inner' field"
126                                        .into(),
127                                )
128                            })?;
129                        encode_value(&AbiType::Field, f, out)
130                    }
131                    _ => Err(Error::Abi(
132                        "expected Integer, Field, or Struct for FunctionSelector".into(),
133                    )),
134                }
135            } else if is_wrapped_field_struct(typ) {
136                match value {
137                    AbiValue::Field(f) => {
138                        out.push(*f);
139                        Ok(())
140                    }
141                    AbiValue::Struct(map) => {
142                        let f = map.get("inner").ok_or_else(|| {
143                            Error::Abi("wrapped field struct must have 'inner' field".into())
144                        })?;
145                        encode_value(&AbiType::Field, f, out)
146                    }
147                    _ => Err(Error::Abi(
148                        "expected Field or Struct for wrapped field type".into(),
149                    )),
150                }
151            } else if is_bounded_vec_struct(typ) {
152                let items = match value {
153                    AbiValue::Array(items) => items,
154                    AbiValue::Struct(map) => {
155                        if let Some(AbiValue::Array(items)) = map.get("storage") {
156                            items
157                        } else {
158                            return Err(Error::Abi(
159                                "BoundedVec struct must have 'storage' array field".into(),
160                            ));
161                        }
162                    }
163                    _ => return Err(Error::Abi("expected Array or Struct for BoundedVec".into())),
164                };
165                let (elem_type, max_len) = match &fields[0].typ {
166                    AbiType::Array { element, length } => (element.as_ref(), *length),
167                    _ => return Err(Error::Abi("BoundedVec storage must be an Array".into())),
168                };
169                if items.len() > max_len {
170                    return Err(Error::Abi(format!(
171                        "BoundedVec has {} items but max capacity is {}",
172                        items.len(),
173                        max_len
174                    )));
175                }
176                for item in items {
177                    encode_value(elem_type, item, out)?;
178                }
179                let elem_size = abi_type_size(elem_type);
180                for _ in 0..(max_len - items.len()) * elem_size {
181                    out.push(Fr::zero());
182                }
183                out.push(Fr::from(items.len() as u64));
184                Ok(())
185            } else if is_option_struct(typ) {
186                match value {
187                    AbiValue::Struct(map) => {
188                        let is_some = map.get("_is_some");
189                        let val = map.get("_value");
190                        match (is_some, val) {
191                            (Some(is_some_val), Some(value_val)) => {
192                                encode_value(&fields[0].typ, is_some_val, out)?;
193                                encode_value(&fields[1].typ, value_val, out)?;
194                                Ok(())
195                            }
196                            _ => Err(Error::Abi(
197                                "Option struct must have '_is_some' and '_value' fields".into(),
198                            )),
199                        }
200                    }
201                    _ => {
202                        // Treat as Some(value)
203                        out.push(Fr::one()); // _is_some = true
204                        encode_value(&fields[1].typ, value, out)?;
205                        Ok(())
206                    }
207                }
208            } else if let AbiValue::Struct(values) = value {
209                // Generic struct encoding
210                for field in fields {
211                    let field_value = values.get(&field.name).ok_or_else(|| {
212                        Error::Abi(format!("missing struct field '{}'", field.name))
213                    })?;
214                    encode_value(&field.typ, field_value, out)?;
215                }
216                Ok(())
217            } else {
218                Err(Error::Abi("argument type/value mismatch".to_owned()))
219            }
220        }
221        (AbiType::Tuple { elements }, AbiValue::Tuple(values)) => {
222            if elements.len() != values.len() {
223                return Err(Error::Abi(format!(
224                    "expected tuple of length {}, got {}",
225                    elements.len(),
226                    values.len()
227                )));
228            }
229            for (element, value) in elements.iter().zip(values) {
230                encode_value(element, value, out)?;
231            }
232            Ok(())
233        }
234        _ => Err(Error::Abi("argument type/value mismatch".to_owned())),
235    }
236}