ccnext_query_builder/abi/
query_builder_for_function.rs

1use alloy::{consensus::Transaction as _, dyn_abi::Specifier, rpc::types::Transaction};
2use alloy_json_abi::Function;
3
4use crate::abi::utils::compute_abi_offsets;
5
6use super::models::{FieldMetadata, QueryBuilderError};
7
8const FUNCTION_SIGNATURE_SIZE: usize = 4;
9
10pub struct QueryBuilderForFunction {
11    selected_offsets: Vec<(usize, usize)>,
12    matched_function: Function,
13    tx: Transaction,
14    data_field: FieldMetadata,
15}
16
17impl QueryBuilderForFunction {
18    pub(crate) fn new(
19        matched_function: Function,
20        tx: Transaction,
21        data_field: FieldMetadata,
22    ) -> Self {
23        Self {
24            selected_offsets: vec![],
25            matched_function,
26            tx,
27            data_field,
28        }
29    }
30
31    pub fn get_selected_offsets(self) -> Vec<(usize, usize)> {
32        self.selected_offsets.clone()
33    }
34
35    pub fn add_signature(&mut self) -> Result<&mut Self, QueryBuilderError> {
36        if let Some(size) = self.data_field.size {
37            if size >= FUNCTION_SIGNATURE_SIZE {
38                self.selected_offsets
39                    .push((self.data_field.offset, FUNCTION_SIGNATURE_SIZE));
40                Ok(self)
41            } else {
42                Err(QueryBuilderError::DataFieldNotLongEnoughForSignatureExtraction)
43            }
44        } else {
45            Err(QueryBuilderError::DataFieldMissingSize)
46        }
47    }
48
49    pub fn add_argument(&mut self, name: String) -> Result<&mut Self, QueryBuilderError> {
50        let mut found_argument_index: Option<usize> = None;
51        for (argument_index, argument) in self.matched_function.inputs.iter().enumerate() {
52            if argument.name().eq(&name) {
53                found_argument_index = Some(argument_index);
54                break;
55            }
56        }
57
58        let data_size = match self.data_field.size {
59            Some(s) => s,
60            None => return Err(QueryBuilderError::DataFieldMissingSize),
61        };
62
63        let matched_argument_index = match found_argument_index {
64            Some(ma) => ma,
65            None => {
66                return Err(QueryBuilderError::CannotFindArgumentInFunction(
67                    self.matched_function.clone(),
68                    name,
69                ))
70            }
71        };
72
73        // we have the types :)
74        let mut calldata_sol_types = Vec::new();
75        for input in self.matched_function.inputs.clone() {
76            match input.resolve() {
77                Ok(st) => {
78                    calldata_sol_types.push(st);
79                }
80                Err(_) => {
81                    return Err(QueryBuilderError::FailedToResolveSolTypesOfMatchedFunction(
82                        self.matched_function.clone(),
83                    ));
84                }
85            }
86        }
87
88        // now we need to decode the contract call, but only from the slice of FUNCTION_SIGNITURE_SIZE...onwards..
89        let data = self.tx.inner.input().as_ref();
90        let sliced_data = &data[FUNCTION_SIGNATURE_SIZE..data_size];
91
92        // compute the offsets :)
93        let data_computed_offsets =
94            match compute_abi_offsets(calldata_sol_types, sliced_data.into()) {
95                Ok(offsets) => offsets,
96                Err(_) => return Err(QueryBuilderError::FailedToComputeOffsetsForCalldata),
97            };
98
99        match data_computed_offsets.get(matched_argument_index) {
100            Some(field) => match field.size {
101                Some(size) => {
102                    self.selected_offsets.push((
103                        self.data_field.offset + FUNCTION_SIGNATURE_SIZE + field.offset,
104                        size,
105                    ));
106                    Ok(self)
107                }
108                None => Err(QueryBuilderError::TryingToGetSizeOfDynamicType),
109            },
110            None => Err(QueryBuilderError::MissingDataInCalldataOffsets),
111        }
112    }
113}