ccnext_query_builder/abi/
query_builder.rs

1use std::collections::HashMap;
2
3use alloy::{
4    consensus::Transaction as _,
5    dyn_abi::{DecodedEvent, DynSolType, EventExt},
6    hex::FromHex,
7    json_abi::JsonAbi,
8    primitives::{map::HashSet, FixedBytes},
9    rpc::types::{Log, Transaction, TransactionReceipt},
10};
11use alloy_json_abi::Event;
12use async_trait::async_trait;
13
14use super::{
15    field_mapping::get_all_fields_for_transaction, models::QueryBuilderError,
16    query_builder_for_event::QueryBuilderForEvent, utils::compute_abi_offsets,
17};
18use crate::abi::{
19    models::{FieldMetadata, QueryableFields},
20    query_builder_for_function::QueryBuilderForFunction,
21    utils::{make_offsets_absolute, WORD_SIZE},
22};
23use ccnext_abi_encoding::{abi::abi_encode, common::EncodingVersion};
24
25#[async_trait]
26pub trait AbiProvider {
27    async fn get_abi(&self, contract_address: String) -> Result<String, QueryBuilderError>;
28}
29
30pub struct QueryBuilder {
31    tx: Transaction,
32    rx: TransactionReceipt,
33    abi_provider: Option<Box<dyn AbiProvider>>,
34    _computed_offsets: Vec<FieldMetadata>,
35    mapped_offsets: HashMap<QueryableFields, FieldMetadata>,
36    selected_offsets: Vec<(usize, usize)>,
37    abi_cache: HashMap<String, JsonAbi>,
38}
39
40fn hex_to_4_bytes(hex: &str) -> Result<[u8; 4], &'static str> {
41    let hex = hex.strip_prefix("0x").unwrap_or(hex); // Remove "0x" if present
42
43    if hex.len() != 8 {
44        return Err("Hex string must be exactly 8 characters long (excluding 0x)");
45    }
46
47    let bytes = hex::decode(hex).map_err(|_| "Invalid hex string")?;
48
49    let mut arr = [0u8; 4];
50    arr.copy_from_slice(&bytes[..4]);
51
52    Ok(arr)
53}
54
55impl QueryBuilder {
56    pub fn create_from_transaction(
57        tx: Transaction,
58        rx: TransactionReceipt,
59        encoding: EncodingVersion,
60    ) -> Result<QueryBuilder, QueryBuilderError> {
61        // encode the transaction
62        let encoded = match abi_encode(tx.clone(), rx.clone(), encoding) {
63            Some(encoded_result) => encoded_result,
64            None => {
65                return Err(QueryBuilderError::FailedToAbiEncode);
66            }
67        };
68
69        let abi_bytes = encoded.abi().to_vec();
70
71        // get the strongly typed elements.
72        let field_and_types = get_all_fields_for_transaction(tx.inner.tx_type(), encoding);
73        let fields: Vec<QueryableFields> = field_and_types.get_all_fields();
74
75        let param_types = vec![
76            DynSolType::Uint(8),
77            DynSolType::Array(DynSolType::Bytes.into()),
78        ];
79
80        let initial_offsets = match compute_abi_offsets(param_types, &abi_bytes) {
81            Ok(offsets) => {
82                if offsets.len() != 2 || offsets[1].children.is_empty() {
83                    return Err(QueryBuilderError::FailedToComputeOffsets);
84                }
85
86                offsets
87            }
88            Err(_) => return Err(QueryBuilderError::FailedToComputeOffsets),
89        };
90
91        let byte_array_offset = &initial_offsets[1];
92
93        if byte_array_offset.children.len() != field_and_types.chunks.len() {
94            return Err(QueryBuilderError::FailedToComputeOffsets);
95        }
96
97        let mut computed_offsets = Vec::new();
98
99        for (chunk_index, chunk) in field_and_types.chunks.iter().enumerate() {
100            let chunk_data_offset = byte_array_offset.children[chunk_index].offset;
101
102            let length_prefix_start = chunk_data_offset - WORD_SIZE;
103            let length_bytes = &abi_bytes[length_prefix_start..chunk_data_offset];
104
105            // Decode length (last 8 bytes of the 32-byte word, big-endian)
106            let length = u64::from_be_bytes(
107                length_bytes[24..32]
108                    .try_into()
109                    .map_err(|_| QueryBuilderError::FailedToComputeOffsets)?,
110            );
111
112            // Extract chunk bytes (decoded, no length prefix)
113            let chunk_end = chunk_data_offset
114                .checked_add(length as usize)
115                .ok_or(QueryBuilderError::FailedToComputeOffsets)?;
116
117            if chunk_end > abi_bytes.len() {
118                return Err(QueryBuilderError::FailedToComputeOffsets);
119            }
120
121            let chunk_bytes = &abi_bytes[chunk_data_offset..chunk_end];
122            let chunk_types = chunk.get_types();
123
124            // For each chunk, compute its offsets.
125            let chunk_computed_offsets = match compute_abi_offsets(chunk_types, chunk_bytes) {
126                Ok(mut computed) => {
127                    for field_offset in &mut computed {
128                        make_offsets_absolute(field_offset, chunk_data_offset);
129                    }
130                    computed
131                }
132                Err(_) => return Err(QueryBuilderError::FailedToComputeOffsets),
133            };
134
135            if chunk.fields.len() != chunk_computed_offsets.len() {
136                return Err(QueryBuilderError::FailedToComputeOffsets);
137            }
138
139            computed_offsets.extend(chunk_computed_offsets);
140        }
141
142        if computed_offsets.len() != fields.len() {
143            return Err(QueryBuilderError::MissMatchedLengthDecoding);
144        }
145
146        // create a map of offsets.
147        let mut mapped_offsets = HashMap::<QueryableFields, FieldMetadata>::new();
148
149        // Addint type as the first field.
150        mapped_offsets.insert(QueryableFields::Type, initial_offsets[0].clone());
151
152        let fields_offset = fields.iter().zip(computed_offsets.clone());
153
154        for (field, offset) in fields_offset {
155            mapped_offsets.insert(field.clone(), offset);
156        }
157
158        Ok(QueryBuilder {
159            tx,
160            rx,
161            abi_provider: None,
162            mapped_offsets,
163            _computed_offsets: computed_offsets.clone(),
164            selected_offsets: vec![],
165            abi_cache: HashMap::new(),
166        })
167    }
168
169    pub fn set_abi_provider(&mut self, abi_provider: Box<dyn AbiProvider>) {
170        self.abi_provider = Some(abi_provider);
171    }
172
173    pub async fn function_builder(
174        &mut self,
175        name_or_signature: String,
176        configurator: fn(&mut QueryBuilderForFunction) -> Result<(), QueryBuilderError>,
177    ) -> Result<&mut Self, QueryBuilderError> {
178        if self.tx.inner.input().is_empty() {
179            return Err(QueryBuilderError::RequestingFunctionArgumentOfAnEmptyCalldataTransaction);
180        }
181
182        let contract_address = match self.tx.to() {
183            Some(ca) => ca,
184            None => {
185                return Err(QueryBuilderError::RequestingFunctionArgumentButNoToAddressPresent);
186            }
187        };
188
189        let data_field = match self.mapped_offsets.get(&QueryableFields::TxData) {
190            Some(t) => t,
191            None => return Err(QueryBuilderError::FailedToFindTxDataField),
192        }
193        .clone();
194
195        let abi = self
196            .get_abi_from_provider_cached(contract_address.to_string())
197            .await?;
198        // If signature, then we can get function without ambiguity
199        let matched_function = if name_or_signature.starts_with("0x") {
200            let name_of_signature_bytes = match self::hex_to_4_bytes(name_or_signature.as_str()) {
201                Ok(t) => t,
202                Err(_) => {
203                    return Err(QueryBuilderError::FunctionSignatureNameProvidedIsNotValidHex)
204                }
205            };
206
207            match abi
208                .functions()
209                .find(|f| f.selector().0 == name_of_signature_bytes)
210            {
211                Some(t) => t,
212                None => {
213                    return Err(QueryBuilderError::FailedToFindFunctionByNameOrSignature(
214                        name_or_signature,
215                    ))
216                }
217            }
218        } else {
219            // If name was passed, then we get the function with that name. For now, we error out when
220            // multiple functions are found with the same name.
221            match abi.function(&name_or_signature) {
222                Some(functions) => {
223                    if functions.len() > 1 {
224                        return Err(QueryBuilderError::AmbigiousFunctionMatch(functions.clone()));
225                    }
226
227                    match functions.first() {
228                        Some(t) => t,
229                        None => {
230                            return Err(QueryBuilderError::FailedToFindFunctionByNameOrSignature(
231                                name_or_signature,
232                            ))
233                        }
234                    }
235                }
236                None => {
237                    return Err(QueryBuilderError::FailedToFindFunctionByNameOrSignature(
238                        name_or_signature,
239                    ));
240                }
241            }
242        };
243
244        // now that we have a matched function :)
245        // we can create a function builder for it.
246        let mut builder = QueryBuilderForFunction::new(
247            matched_function.clone(),
248            self.tx.clone(),
249            data_field.clone(),
250        );
251        configurator(&mut builder)?;
252        let offsets_from_builder = builder.get_selected_offsets();
253        self.selected_offsets.extend(offsets_from_builder);
254        Ok(self)
255    }
256
257    pub fn add_static_field(
258        &mut self,
259        field: QueryableFields,
260    ) -> Result<&mut Self, QueryBuilderError> {
261        match self.mapped_offsets.get(&field) {
262            Some(field_offset) => match field_offset.size {
263                Some(size) => {
264                    self.selected_offsets.push((field_offset.offset, size));
265                    Ok(self)
266                }
267                None => Err(QueryBuilderError::FieldIsNotStatic),
268            },
269            None => Err(QueryBuilderError::FieldNotPresentInTx),
270        }
271    }
272
273    pub async fn multi_event_builder(
274        &mut self,
275        event_name_or_signature: String,
276        filter: fn(Log, DecodedEvent, usize) -> bool,
277        configurator: fn(&mut QueryBuilderForEvent) -> Result<(), QueryBuilderError>,
278    ) -> Result<&mut Self, QueryBuilderError> {
279        let matched_events = self
280            .find_all_events(event_name_or_signature.clone(), filter)
281            .await?;
282
283        let logs_field = match self.mapped_offsets.get(&QueryableFields::RxLogs) {
284            Some(lf) => lf,
285            None => {
286                return Err(QueryBuilderError::FailedToFindRxLogsField);
287            }
288        };
289
290        for (log, decoded_event, log_index, event) in matched_events {
291            let log_field = match logs_field.children.get(log_index) {
292                Some(t) => t,
293                None => {
294                    return Err(QueryBuilderError::FailedToGetEventDataOffsets(Box::new(
295                        log,
296                    )))
297                }
298            };
299
300            let mut event_builder =
301                QueryBuilderForEvent::new(log_field.clone(), log, decoded_event, event);
302            configurator(&mut event_builder)?;
303            let selected_offsets_from_event_builder = event_builder.get_selected_offsets();
304            self.selected_offsets
305                .extend(selected_offsets_from_event_builder);
306        }
307
308        Ok(self)
309    }
310
311    pub async fn event_builder(
312        &mut self,
313        event_name_or_signature: String,
314        filter: fn(Log, DecodedEvent, usize) -> bool,
315        take_first_if_multiple: bool,
316        configurator: fn(&mut QueryBuilderForEvent) -> Result<(), QueryBuilderError>,
317    ) -> Result<&mut Self, QueryBuilderError> {
318        let (log, decoded_event, log_index, event) = match self
319            .find_event(
320                event_name_or_signature.clone(),
321                filter,
322                take_first_if_multiple,
323            )
324            .await?
325        {
326            Some(m) => m,
327            None => {
328                return Err(QueryBuilderError::FailedToFindEventByNameOrSignature(
329                    event_name_or_signature,
330                ))
331            }
332        };
333
334        let logs_field = match self.mapped_offsets.get(&QueryableFields::RxLogs) {
335            Some(lf) => lf,
336            None => {
337                return Err(QueryBuilderError::FailedToFindRxLogsField);
338            }
339        };
340
341        let log_field = match logs_field.children.get(log_index) {
342            Some(f) => f,
343            None => {
344                return Err(QueryBuilderError::MissingLogInAbiOffsets(log_index));
345            }
346        };
347
348        let mut event_builder =
349            QueryBuilderForEvent::new(log_field.clone(), log, decoded_event, event);
350        configurator(&mut event_builder)?;
351        let selected_offsets_from_event_builder = event_builder.get_selected_offsets();
352        self.selected_offsets
353            .extend(selected_offsets_from_event_builder);
354        Ok(self)
355    }
356
357    pub async fn find_event(
358        &mut self,
359        event_name_or_signature: String,
360        filter: fn(Log, DecodedEvent, usize) -> bool,
361        take_first_if_multiple: bool,
362    ) -> Result<Option<(Log, DecodedEvent, usize, Event)>, QueryBuilderError> {
363        let events = self
364            .find_all_events(event_name_or_signature.clone(), filter)
365            .await?;
366        if events.is_empty() {
367            Ok(None)
368        } else if events.len() == 1 || take_first_if_multiple {
369            let first_element = events.first().unwrap();
370            Ok(Some(first_element.clone()))
371        } else {
372            Err(QueryBuilderError::AmbigiousEventMatch(
373                event_name_or_signature,
374            ))
375        }
376    }
377
378    pub async fn find_all_events(
379        &mut self,
380        event_name_or_signature: String,
381        filter: fn(Log, DecodedEvent, usize) -> bool,
382    ) -> Result<Vec<(Log, DecodedEvent, usize, Event)>, QueryBuilderError> {
383        let mut extended_logs = Vec::new();
384
385        if event_name_or_signature.starts_with("0x") {
386            let event_signature_as_fixed_bytes =
387                match FixedBytes::<32>::from_hex(event_name_or_signature.clone()) {
388                    Ok(decoded_fixed_bytes) => decoded_fixed_bytes,
389                    Err(_) => {
390                        return Err(QueryBuilderError::EventSignatureNameProvidedIsNotValidHex)
391                    }
392                };
393
394            // filter the logs, that match the criteria.
395            let mut filtered_logs = Vec::new();
396            let mut contract_addresses = Vec::new();
397            for (log_index, log) in self.rx.inner.logs().iter().enumerate() {
398                if let Some(event_hash) = log.topic0() {
399                    if event_hash.eq(&event_signature_as_fixed_bytes) {
400                        contract_addresses.push(log.address().to_string());
401                        filtered_logs.push((log_index, log.clone()));
402                    }
403                }
404            }
405
406            // get the contract addresses
407            let abis = self
408                .get_abis_of_contract_addresses(contract_addresses)
409                .await?;
410            for (log_index, log) in filtered_logs {
411                let contract_address = log.address().to_string();
412                let abi = match abis.get(&contract_address) {
413                    Some(a) => a,
414                    None => {
415                        return Err(QueryBuilderError::NoAbiFoundForContract(
416                            contract_address.clone(),
417                        ))
418                    }
419                };
420
421                let event_of_signature = abi
422                    .events()
423                    .find(|e| e.selector().0 == event_signature_as_fixed_bytes.0);
424
425                if let Some(event) = event_of_signature {
426                    // we have the event woot woot.
427                    match event.decode_log(&log.inner, true) {
428                        Ok(decoded_event) => {
429                            extended_logs.push((log, decoded_event, log_index, event.clone()));
430                        }
431                        Err(_) => {
432                            return Err(QueryBuilderError::FailedToDecodeLog(Box::new(
433                                log.clone(),
434                            )));
435                        }
436                    }
437                } else {
438                    return Err(QueryBuilderError::FailedToFindEventByNameOrSignature(
439                        event_name_or_signature.clone(),
440                    ));
441                }
442            }
443        } else {
444            // we need to get all the abi's possible in the events.
445            let abis = self.get_receipt_abis().await?;
446            let mut log_index = 0;
447            for log in self.rx.inner.logs() {
448                // get the ABI for this log.
449                let abi = match abis.get(&log.address().to_string()) {
450                    Some(json_abi) => json_abi,
451                    None => {
452                        return Err(QueryBuilderError::NoAbiFoundForContract(
453                            log.address().to_string(),
454                        ));
455                    }
456                };
457
458                // get the event signature
459                let event_signature = match log.topic0() {
460                    Some(es) => es,
461                    None => {
462                        log_index += 1;
463                        continue;
464                    }
465                };
466
467                // find the event...
468                let event_of_signature = abi.events().find(|e| e.selector().0 == event_signature.0);
469
470                // attempt to parse the log :)
471                if let Some(event) = event_of_signature {
472                    // before we try to decode the log, lets first check its the event name
473                    // we care about..
474                    if event.name == event_name_or_signature {
475                        // we have the event woot woot.
476                        match event.decode_log(&log.inner, true) {
477                            Ok(decoded_event) => {
478                                extended_logs.push((
479                                    log.clone(),
480                                    decoded_event,
481                                    log_index,
482                                    event.clone(),
483                                ));
484                            }
485                            Err(_) => {
486                                return Err(QueryBuilderError::FailedToDecodeLog(Box::new(
487                                    log.clone(),
488                                )));
489                            }
490                        }
491                    }
492                } else {
493                    // its not that we can't find it, its more that the log is not decodable
494                    // due not being able to find the event in the ABI.
495                    return Err(QueryBuilderError::FailedToDecodeLog(Box::new(log.clone())));
496                }
497
498                log_index += 1;
499            }
500        }
501
502        // now that we have only extended logs of an event that either matches by name or signature.
503        // we just need to offer the ability to filter to the user..
504        let mut matches = Vec::new();
505        for (log, decoded_event, log_index, event) in extended_logs {
506            if filter(log.clone(), decoded_event.clone(), log_index) {
507                matches.push((log, decoded_event, log_index, event));
508            }
509        }
510
511        Ok(matches)
512    }
513
514    pub async fn get_receipt_abis(
515        &mut self,
516    ) -> Result<HashMap<String, JsonAbi>, QueryBuilderError> {
517        let contract_addresses: Vec<String> = self
518            .rx
519            .inner
520            .logs()
521            .iter()
522            .map(|f| f.address().to_string())
523            .collect();
524
525        let result = self
526            .get_abis_of_contract_addresses(contract_addresses)
527            .await?;
528        Ok(result)
529    }
530
531    pub async fn get_abis_of_contract_addresses(
532        &mut self,
533        contract_addresses: Vec<String>,
534    ) -> Result<HashMap<String, JsonAbi>, QueryBuilderError> {
535        let unique_contract_addresses: HashSet<_> = contract_addresses.into_iter().collect();
536        let mut abi_map = HashMap::new();
537        for contract_address in unique_contract_addresses {
538            let abi: JsonAbi = self
539                .get_abi_from_provider_cached(contract_address.clone())
540                .await?;
541            abi_map.insert(contract_address.clone(), abi);
542        }
543        Ok(abi_map)
544    }
545
546    pub async fn get_abi_from_provider_cached(
547        &mut self,
548        contract_address: String,
549    ) -> Result<JsonAbi, QueryBuilderError> {
550        let result = match self.abi_cache.get(&contract_address.clone()) {
551            Some(existing) => existing,
552            None => {
553                let abi = self.get_abi_from_provider(contract_address.clone()).await?;
554                self.abi_cache.insert(contract_address.clone(), abi.clone());
555                &abi.clone()
556            }
557        };
558
559        Ok(result.clone())
560    }
561
562    pub async fn get_abi_from_provider(
563        &self,
564        contract_address: String,
565    ) -> Result<JsonAbi, QueryBuilderError> {
566        let abi_provider = match &self.abi_provider {
567            Some(ap) => ap,
568            None => return Err(QueryBuilderError::AbiProviderNotInitialized),
569        };
570
571        let abi_raw = abi_provider.get_abi(contract_address.clone()).await?;
572
573        match JsonAbi::from_json_str(&abi_raw) {
574            Ok(json_abi) => Ok(json_abi),
575            Err(_) => Err(QueryBuilderError::FailedToParseAbi(
576                contract_address.clone(),
577                abi_raw,
578            )),
579        }
580    }
581
582    pub fn get_selected_offsets(&self) -> Vec<(usize, usize)> {
583        self.selected_offsets.clone()
584    }
585}