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); 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 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 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 let length = u64::from_be_bytes(
107 length_bytes[24..32]
108 .try_into()
109 .map_err(|_| QueryBuilderError::FailedToComputeOffsets)?,
110 );
111
112 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 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 let mut mapped_offsets = HashMap::<QueryableFields, FieldMetadata>::new();
148
149 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 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 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 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 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 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 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 let abis = self.get_receipt_abis().await?;
446 let mut log_index = 0;
447 for log in self.rx.inner.logs() {
448 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 let event_signature = match log.topic0() {
460 Some(es) => es,
461 None => {
462 log_index += 1;
463 continue;
464 }
465 };
466
467 let event_of_signature = abi.events().find(|e| e.selector().0 == event_signature.0);
469
470 if let Some(event) = event_of_signature {
472 if event.name == event_name_or_signature {
475 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 return Err(QueryBuilderError::FailedToDecodeLog(Box::new(log.clone())));
496 }
497
498 log_index += 1;
499 }
500 }
501
502 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}