Skip to main content

aztec_core/abi/
decoder.rs

1use std::collections::BTreeMap;
2
3use super::checkers::{
4    abi_type_size, is_aztec_address_struct, is_bounded_vec_struct, is_eth_address_struct,
5    is_option_struct,
6};
7use super::types::AbiType;
8use crate::types::Fr;
9use crate::Error;
10
11/// A decoded ABI value, produced by `decode_from_abi`.
12#[derive(Clone, Debug, PartialEq)]
13pub enum AbiDecoded {
14    /// A field element.
15    Field(Fr),
16    /// A boolean value.
17    Boolean(bool),
18    /// An integer (signed or unsigned, stored as i128).
19    Integer(i128),
20    /// A fixed-length array of decoded values.
21    Array(Vec<AbiDecoded>),
22    /// A decoded string.
23    String(String),
24    /// A struct with named fields.
25    Struct(BTreeMap<String, AbiDecoded>),
26    /// A tuple of decoded values.
27    Tuple(Vec<AbiDecoded>),
28    /// An AztecAddress (special-cased from struct decoding).
29    Address(crate::types::AztecAddress),
30    /// None / absent (decoded from Option with `_is_some == false`).
31    None,
32}
33
34/// Decode field elements back into typed ABI values.
35///
36/// `types` is the list of return types from a function's ABI.
37/// `fields` is the flat array of field elements returned by the function.
38pub fn decode_from_abi(types: &[AbiType], fields: &[Fr]) -> Result<AbiDecoded, Error> {
39    let mut cursor = 0;
40    if types.len() == 1 {
41        return decode_next(&types[0], fields, &mut cursor);
42    }
43    let results: Vec<AbiDecoded> = types
44        .iter()
45        .map(|t| decode_next(t, fields, &mut cursor))
46        .collect::<Result<_, _>>()?;
47    Ok(AbiDecoded::Tuple(results))
48}
49
50fn take(fields: &[Fr], cursor: &mut usize) -> Result<Fr, Error> {
51    if *cursor >= fields.len() {
52        return Err(Error::Abi(format!(
53            "insufficient fields for decoding: cursor {} >= len {}",
54            cursor,
55            fields.len()
56        )));
57    }
58    let fr = fields[*cursor];
59    *cursor += 1;
60    Ok(fr)
61}
62
63fn decode_next(typ: &AbiType, fields: &[Fr], cursor: &mut usize) -> Result<AbiDecoded, Error> {
64    match typ {
65        AbiType::Field => {
66            let fr = take(fields, cursor)?;
67            Ok(AbiDecoded::Field(fr))
68        }
69        AbiType::Boolean => {
70            let fr = take(fields, cursor)?;
71            Ok(AbiDecoded::Boolean(!fr.is_zero()))
72        }
73        AbiType::Integer { sign, width } => {
74            let fr = take(fields, cursor)?;
75            let bytes = fr.to_be_bytes();
76            let raw = u128::from_be_bytes(bytes[16..].try_into().unwrap());
77            if sign == "signed" {
78                let value = if *width >= 128 {
79                    // For 128-bit signed, u128-to-i128 reinterpretation handles
80                    // two's complement naturally (values >= 2^127 become negative).
81                    raw as i128
82                } else if raw >= (1u128 << (*width - 1)) {
83                    raw as i128 - (1i128 << *width)
84                } else {
85                    raw as i128
86                };
87                Ok(AbiDecoded::Integer(value))
88            } else {
89                Ok(AbiDecoded::Integer(raw as i128))
90            }
91        }
92        AbiType::Array { element, length } => {
93            let mut items = Vec::with_capacity(*length);
94            for _ in 0..*length {
95                items.push(decode_next(element, fields, cursor)?);
96            }
97            Ok(AbiDecoded::Array(items))
98        }
99        AbiType::String { length } => {
100            let mut chars = Vec::with_capacity(*length);
101            for _ in 0..*length {
102                let fr = take(fields, cursor)?;
103                let byte = fr.to_usize() as u8;
104                if byte == 0 {
105                    // Skip null terminators but still consume remaining fields
106                    for _ in (chars.len() + 1)..*length {
107                        take(fields, cursor)?;
108                    }
109                    break;
110                }
111                chars.push(char::from(byte));
112            }
113            Ok(AbiDecoded::String(chars.into_iter().collect()))
114        }
115        AbiType::Struct {
116            fields: struct_fields,
117            ..
118        } => {
119            if is_aztec_address_struct(typ) {
120                let fr = take(fields, cursor)?;
121                Ok(AbiDecoded::Address(crate::types::AztecAddress(fr)))
122            } else if is_eth_address_struct(typ) {
123                let fr = take(fields, cursor)?;
124                Ok(AbiDecoded::Field(fr))
125            } else if is_option_struct(typ) {
126                let is_some_fr = take(fields, cursor)?;
127                if is_some_fr.is_zero() {
128                    // Skip _value fields
129                    let value_size = abi_type_size(&struct_fields[1].typ);
130                    for _ in 0..value_size {
131                        take(fields, cursor)?;
132                    }
133                    Ok(AbiDecoded::None)
134                } else {
135                    let value = decode_next(&struct_fields[1].typ, fields, cursor)?;
136                    Ok(value)
137                }
138            } else if is_bounded_vec_struct(typ) {
139                let (elem_type, max_len) = match &struct_fields[0].typ {
140                    AbiType::Array { element, length } => (element.as_ref(), *length),
141                    _ => return Err(Error::Abi("BoundedVec storage must be an Array".into())),
142                };
143                let mut items = Vec::with_capacity(max_len);
144                for _ in 0..max_len {
145                    items.push(decode_next(elem_type, fields, cursor)?);
146                }
147                let len_fr = take(fields, cursor)?;
148                let actual_len = len_fr.to_usize();
149                items.truncate(actual_len);
150                Ok(AbiDecoded::Array(items))
151            } else {
152                // Generic struct decoding
153                let mut map = BTreeMap::new();
154                for field in struct_fields {
155                    let value = decode_next(&field.typ, fields, cursor)?;
156                    map.insert(field.name.clone(), value);
157                }
158                Ok(AbiDecoded::Struct(map))
159            }
160        }
161        AbiType::Tuple { elements } => {
162            let mut items = Vec::with_capacity(elements.len());
163            for elem in elements {
164                items.push(decode_next(elem, fields, cursor)?);
165            }
166            Ok(AbiDecoded::Tuple(items))
167        }
168    }
169}