ccnext_query_builder/abi/
utils.rs

1use alloy::{
2    dyn_abi::{Decoder, DynSolType},
3    sol_types::Error,
4};
5
6use super::models::FieldMetadata;
7
8const WORD_SIZE: usize = 32;
9
10#[derive(Debug)]
11pub enum ComputeAbiOffsetsError {
12    FailedToDecode(Error),
13}
14
15pub fn compute_abi_offsets(
16    types: Vec<DynSolType>,
17    abi: Vec<u8>,
18) -> Result<Vec<FieldMetadata>, ComputeAbiOffsetsError> {
19    let mut reader = Decoder::new(&abi, false);
20
21    match decode_offset_recursive(&mut reader, types, 0) {
22        Ok(result) => Ok(result),
23        Err(err) => Err(ComputeAbiOffsetsError::FailedToDecode(err)),
24    }
25}
26
27fn decode_offset_recursive(
28    reader: &mut Decoder,
29    types: Vec<DynSolType>,
30    base_offset: usize,
31) -> Result<Vec<FieldMetadata>, Error> {
32    let mut result = Vec::new();
33    for sol_type in types {
34        match sol_type.clone() {
35            DynSolType::Bool
36            | DynSolType::Int(_)
37            | DynSolType::Uint(_)
38            | DynSolType::FixedBytes(_)
39            | DynSolType::Address => {
40                let relative_offset = reader.offset();
41                let absolute_offset = base_offset + relative_offset;
42                let word = reader.take_word()?;
43                result.push(FieldMetadata {
44                    offset: absolute_offset,
45                    children: vec![],
46                    is_dynamic: false,
47                    size: Some(WORD_SIZE),
48                    sol_type,
49                    value: Some(word.0.to_vec()),
50                });
51            }
52            DynSolType::Bytes | DynSolType::String => {
53                let offset = reader.take_offset()?;
54                let mut sub_reader = reader.child(offset)?;
55                let size = sub_reader.take_offset()?; // take_size() dosen't exist, its the same though.
56                let data = sub_reader.take_slice(size)?;
57                let dynamic_field_absolute_offset = base_offset + offset + WORD_SIZE;
58                result.push(FieldMetadata {
59                    offset: dynamic_field_absolute_offset,
60                    children: vec![],
61                    is_dynamic: true,
62                    size: Some(size),
63                    sol_type,
64                    value: Some(data.to_vec()),
65                });
66            }
67            DynSolType::Array(array_element_sol_type_boxed) => {
68                // all array's not fixed length are always dynamic
69                let field_dynamic_offset = reader.take_offset()?; // first we get the offset of the array
70                let absolute_offset = base_offset + field_dynamic_offset;
71                let mut sub_reader = reader.child(field_dynamic_offset)?; // create a sub reader..
72                let number_of_elements = sub_reader.take_offset()?; // reader the number of elements..
73
74                // if the child of the array is dynamic, its treated quite differently
75                // as if it was a dynamic child.
76                let array_element_sol_type_unboxed = *array_element_sol_type_boxed;
77
78                // this can probably be a function on its own D:
79                let array_components: Vec<DynSolType> = match array_element_sol_type_unboxed.clone()
80                {
81                    // Likely a bug. We would want to add `dyn_sol_types` repeatedly for 0..number_of_elements.
82                    // It's fine for the number of array_components to end up larger than the number_of_elements.
83                    DynSolType::Tuple(dyn_sol_types) => dyn_sol_types,
84                    other => {
85                        let mut types = Vec::new();
86                        for _ in 0..number_of_elements {
87                            types.push(other.clone());
88                        }
89                        types
90                    }
91                };
92
93                let mut children = Vec::new();
94                if is_dynamic(array_element_sol_type_unboxed.clone()) {
95                    // if its a dynamic child we need to compute the offsets
96                    // for each element.
97                    let mut dynamic_array_relative_offsets = Vec::new();
98                    for _ in 0..number_of_elements {
99                        let child_element_offset = sub_reader.take_offset()?;
100                        dynamic_array_relative_offsets.push(child_element_offset);
101                    }
102
103                    // next we need to loop through..
104                    //let size_of_offsets = number_of_elements * WORD_SIZE;
105                    for array_element_offset in dynamic_array_relative_offsets {
106                        // calculate an absolute position, from the current.
107                        let semi_absolute_positon =
108                            field_dynamic_offset + array_element_offset + WORD_SIZE;
109                        let mut child_element_sub_reader = reader.child(semi_absolute_positon)?;
110
111                        // absolute position since parents..
112                        let position = base_offset + semi_absolute_positon;
113
114                        // go decode the children :)
115                        let children_of_child = decode_offset_recursive(
116                            &mut child_element_sub_reader,
117                            array_components.clone(),
118                            position,
119                        )?;
120
121                        children.push(FieldMetadata {
122                            children: children_of_child,
123                            is_dynamic: true,
124                            offset: position,
125                            size: None,
126                            sol_type: array_element_sol_type_unboxed.clone(),
127                            value: None,
128                        });
129                    }
130                } else {
131                    // in this case it means that the inner type of the dynamic array is not a dynamic type
132                    // and what you want to do is to decode them by passing the sub reader down to the
133                    // next iteration of the decoder, this way it moves forward properly.
134                    children = decode_offset_recursive(
135                        &mut sub_reader,
136                        array_components,
137                        absolute_offset,
138                    )?;
139                }
140
141                result.push(FieldMetadata {
142                    children,
143                    is_dynamic: true,
144                    offset: absolute_offset,
145                    size: None,
146                    sol_type,
147                    value: None,
148                });
149            }
150            DynSolType::FixedArray(array_element_sol_type_boxed, number_of_elements) => {
151                // if the child of the array is dynamic, its treated quite differently
152                // as if it was a dynamic child.
153                let array_element_sol_type_unboxed = *array_element_sol_type_boxed;
154
155                // this can probably be a function on its own D:
156                let array_components: Vec<DynSolType> = match array_element_sol_type_unboxed.clone()
157                {
158                    // Maybe same issue here as noted in Array case
159                    DynSolType::Tuple(dyn_sol_types) => dyn_sol_types,
160                    other => {
161                        let mut types = Vec::new();
162                        for _ in 0..number_of_elements {
163                            types.push(other.clone());
164                        }
165                        types
166                    }
167                };
168
169                // if its dynamic don't know what to do :D
170                if is_dynamic(array_element_sol_type_unboxed.clone()) {
171                    // okay so this uses an offset..
172                    let offset = reader.take_offset()?;
173                    let absolute_offset = base_offset + offset;
174
175                    // we need a sub reader there..
176                    let mut sub_reader = reader.child(offset)?;
177                    let children = decode_offset_recursive(
178                        &mut sub_reader,
179                        array_components,
180                        absolute_offset,
181                    )?;
182                    result.push(FieldMetadata {
183                        children,
184                        is_dynamic: true,
185                        offset: absolute_offset,
186                        size: None,
187                        value: None,
188                        sol_type,
189                    });
190                } else {
191                    // easy enough we just recursive.
192                    let absolute_offset = base_offset + reader.offset();
193                    let children =
194                        decode_offset_recursive(reader, array_components, absolute_offset)?;
195                    result.push(FieldMetadata {
196                        children,
197                        is_dynamic: false,
198                        offset: absolute_offset,
199                        size: None,
200                        value: None,
201                        sol_type,
202                    });
203                }
204            }
205            DynSolType::Tuple(tuple_components) => {
206                if is_dynamic(sol_type.clone()) {
207                    let offset_of_tuple = base_offset + reader.offset();
208                    let offset_of_dynamic_data = reader.take_offset()?;
209                    let mut sub_reader = reader.child(offset_of_dynamic_data)?;
210                    let children = decode_offset_recursive(
211                        &mut sub_reader,
212                        tuple_components,
213                        offset_of_dynamic_data,
214                    )?;
215                    result.push(FieldMetadata {
216                        offset: offset_of_tuple,
217                        children,
218                        is_dynamic: true,
219                        size: None,
220                        sol_type,
221                        value: None,
222                    });
223                } else {
224                    let offset_of_tuple = base_offset + reader.offset();
225                    let children = decode_offset_recursive(reader, tuple_components, base_offset)?;
226                    result.push(FieldMetadata {
227                        offset: offset_of_tuple,
228                        children,
229                        is_dynamic: false,
230                        size: None,
231                        sol_type,
232                        value: None,
233                    });
234                }
235            }
236            DynSolType::Function => {
237                return Err(Error::custom(
238                    "Cannot decode functions, with this decoder..",
239                ));
240            }
241        }
242    }
243
244    Ok(result)
245}
246
247pub fn is_dynamic(sol_type: DynSolType) -> bool {
248    /*
249        export function isDynamic(param: ParamType): boolean {
250        // A dynamic array is indicated by arrayLength === -1.
251        if (param.arrayChildren) {
252            if (param.arrayLength === -1) return true;
253            // Even fixed-size arrays are dynamic if their element type is dynamic.
254            return isDynamic(param.arrayChildren);
255        }
256        // String and bytes types are dynamic.
257        if (param.type === "string" || param.type === "bytes") {
258            return true;
259        }
260        // For tuples: if any component is dynamic, then the tuple is dynamic.
261        if (param.baseType === "tuple" && param.components) {
262            return param.components.some(component => isDynamic(component));
263        }
264        // Otherwise, we assume it is static.
265        return false;
266    }
267         */
268
269    match sol_type {
270        DynSolType::Bool => false,
271        DynSolType::Int(_) => false,
272        DynSolType::Uint(_) => false,
273        DynSolType::FixedBytes(_) => false,
274        DynSolType::Address => false,
275        DynSolType::Function => todo!(),
276        DynSolType::Bytes => true,
277        DynSolType::String => true,
278        DynSolType::Array(_array_children) => true,
279        DynSolType::FixedArray(array_children, _array_size) => is_dynamic(*array_children),
280        DynSolType::Tuple(tuple_components) => {
281            for tuple_component in tuple_components {
282                if is_dynamic(tuple_component) {
283                    return true;
284                }
285            }
286
287            false
288        }
289    }
290}