ccnext_query_builder/abi/
utils.rs

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