ccnext_query_builder/abi/
query_builder_for_event.rs

1use alloy::{
2    dyn_abi::{DecodedEvent, Specifier},
3    rpc::types::Log,
4};
5use alloy_json_abi::Event;
6
7use super::{
8    models::{FieldMetadata, QueryBuilderError},
9    utils::compute_abi_offsets,
10};
11
12pub struct QueryBuilderForEvent {
13    field: FieldMetadata,
14    log: Log,
15    _decoded_event: DecodedEvent,
16    event: Event,
17    selected_offsets: Vec<(usize, usize)>,
18}
19
20impl QueryBuilderForEvent {
21    pub(crate) fn new(
22        log_field: FieldMetadata,
23        log: Log,
24        _decoded_event: DecodedEvent,
25        event: Event,
26    ) -> Self {
27        Self {
28            field: log_field,
29            log,
30            _decoded_event,
31            event,
32            selected_offsets: vec![],
33        }
34    }
35
36    pub fn add_argument(&mut self, name: &str) -> Result<&mut Self, QueryBuilderError> {
37        let mut topic_index: usize = 0;
38        let mut data_index: usize = 0;
39
40        for event_input in self.event.inputs.clone() {
41            // We add 1 before loop contents to skip index 0, which always holds the
42            // event signature.
43            if event_input.indexed {
44                topic_index += 1;
45            }
46
47            if event_input.name == name {
48                if event_input.indexed {
49                    // if its indexed..
50                    // calculate the offset..
51                    match self.field.children.get(1) {
52                        // Children are 0:address, 1:indexed, and 2:data
53                        Some(topics) => {
54                            match topics.children.get(topic_index) {
55                                Some(subject_topic) => {
56                                    // all topics are 32 length :)
57                                    // if you want to be extra safe you can always also do
58                                    // match subject_topic.size
59                                    self.selected_offsets.push((subject_topic.offset, 32));
60                                    return Ok(self);
61                                }
62                                None => {
63                                    return Err(QueryBuilderError::MissingDataInAbiOffsets);
64                                }
65                            }
66                        }
67                        None => {
68                            return Err(QueryBuilderError::MissingDataInAbiOffsets);
69                        }
70                    }
71                } else {
72                    // its a data field.
73                    let data_field = match self.field.children.get(2) {
74                        Some(df) => df,
75                        None => {
76                            return Err(QueryBuilderError::MissingDataInAbiOffsets);
77                        }
78                    };
79
80                    // construct a list of body solidity types.
81                    let mut body_sol_types = Vec::new();
82                    for input in self.event.inputs.clone() {
83                        if !input.indexed {
84                            match input.resolve() {
85                                Ok(st) => {
86                                    body_sol_types.push(st);
87                                }
88                                Err(_) => {
89                                    return Err(
90                                        QueryBuilderError::FailedToResolveSolTypesOfMatchedEvent(
91                                            self.event.clone(),
92                                        ),
93                                    );
94                                }
95                            }
96                        }
97                    }
98
99                    // we have the body solidity types :) of the data field.
100                    // we need to compute its offsets, similar to our transaction.
101                    let event_data_offsets =
102                        match compute_abi_offsets(body_sol_types, &self.log.data().data) {
103                            Ok(offsets) => offsets,
104                            Err(_) => {
105                                return Err(QueryBuilderError::FailedToGetEventDataOffsets(
106                                    Box::new(self.log.clone()),
107                                ))
108                            }
109                        };
110
111                    match event_data_offsets.get(data_index) {
112                        Some(argument_field) => match argument_field.size {
113                            Some(argument_field_size) => {
114                                self.selected_offsets.push((
115                                    data_field.offset + argument_field.offset,
116                                    argument_field_size,
117                                ));
118                                return Ok(self);
119                            }
120                            None => {
121                                return Err(QueryBuilderError::TryingToGetSizeOfDynamicType);
122                            }
123                        },
124                        None => {
125                            return Err(QueryBuilderError::MissingDataInAbiOffsets);
126                        }
127                    }
128                }
129            }
130
131            if !event_input.indexed {
132                data_index += 1;
133            }
134        }
135
136        Err(QueryBuilderError::MissingDataInAbiOffsets)
137    }
138
139    pub fn add_address(&mut self) -> Result<&mut Self, QueryBuilderError> {
140        match self.field.children.first() {
141            Some(address_field) => match address_field.size {
142                Some(address_field_size) => {
143                    self.selected_offsets
144                        .push((address_field.offset, address_field_size));
145                    Ok(self)
146                }
147                None => Err(QueryBuilderError::TryingToGetSizeOfDynamicType),
148            },
149            None => Err(QueryBuilderError::MissingDataInAbiOffsets),
150        }
151    }
152
153    pub fn add_signature(&mut self) -> Result<&mut Self, QueryBuilderError> {
154        // this is the topics..
155        match self.field.children.get(1) {
156            Some(topics) => match topics.children.first() {
157                Some(signature_topic) => match signature_topic.size {
158                    Some(size_of_topic) => {
159                        let offset_and_size = (signature_topic.offset, size_of_topic);
160                        self.selected_offsets.push(offset_and_size);
161                        Ok(self)
162                    }
163                    None => Err(QueryBuilderError::TryingToGetSizeOfDynamicType),
164                },
165                None => Err(QueryBuilderError::MissingDataInAbiOffsets),
166            },
167            None => Err(QueryBuilderError::MissingDataInAbiOffsets),
168        }
169    }
170
171    pub fn get_selected_offsets(self) -> Vec<(usize, usize)> {
172        self.selected_offsets.clone()
173    }
174}