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};
22use ccnext_abi_encoding::abi::{abi_encode, EncodingVersion};
23
24#[async_trait]
25pub trait AbiProvider {
26 async fn get_abi(&self, contract_address: String) -> Result<String, QueryBuilderError>;
27}
28
29pub struct QueryBuilder {
30 tx: Transaction,
31 rx: TransactionReceipt,
32 abi_provider: Option<Box<dyn AbiProvider>>,
33 _computed_offsets: Vec<FieldMetadata>,
34 mapped_offsets: HashMap<QueryableFields, FieldMetadata>,
35 selected_offsets: Vec<(usize, usize)>,
36 abi_cache: HashMap<String, JsonAbi>,
37}
38
39fn hex_to_4_bytes(hex: &str) -> Result<[u8; 4], &'static str> {
40 let hex = hex.strip_prefix("0x").unwrap_or(hex); if hex.len() != 8 {
43 return Err("Hex string must be exactly 8 characters long (excluding 0x)");
44 }
45
46 let bytes = hex::decode(hex).map_err(|_| "Invalid hex string")?;
47
48 let mut arr = [0u8; 4];
49 arr.copy_from_slice(&bytes[..4]);
50
51 Ok(arr)
52}
53
54impl QueryBuilder {
55 pub fn create_from_transaction(
56 tx: Transaction,
57 rx: TransactionReceipt,
58 encoding: EncodingVersion,
59 ) -> Result<QueryBuilder, QueryBuilderError> {
60 let encoded = match abi_encode(tx.clone(), rx.clone(), encoding) {
62 Ok(encoded_result) => encoded_result,
63 Err(_) => {
64 return Err(QueryBuilderError::FailedToAbiEncode);
65 }
66 };
67
68 let field_and_types = get_all_fields_for_transaction(tx.inner.tx_type(), encoding);
70
71 let fields: Vec<QueryableFields> = field_and_types.iter().map(|f| f.0.clone()).collect();
73
74 let sol_types: Vec<DynSolType> = field_and_types.iter().map(|f| f.1.clone()).collect();
76
77 let computed_offsets = match compute_abi_offsets(sol_types, encoded.abi.clone()) {
79 Ok(co) => co,
80 Err(_) => {
81 return Err(QueryBuilderError::FailedToComputeOffsets);
82 }
83 };
84
85 if computed_offsets.len() != field_and_types.len() {
89 return Err(QueryBuilderError::MissMatchedLengthDecoding);
90 }
91
92 let mut mapped_offsets = HashMap::<QueryableFields, FieldMetadata>::new();
94 let fields_offset = fields.iter().zip(computed_offsets.clone());
95 for (field, offset) in fields_offset {
96 mapped_offsets.insert(field.clone(), offset);
97 }
98
99 Ok(QueryBuilder {
100 tx,
101 rx,
102 abi_provider: None,
103 mapped_offsets,
104 _computed_offsets: computed_offsets.clone(),
105 selected_offsets: vec![],
106 abi_cache: HashMap::new(),
107 })
108 }
109
110 pub fn set_abi_provider(&mut self, abi_provider: Box<dyn AbiProvider>) {
111 self.abi_provider = Some(abi_provider);
112 }
113
114 pub async fn function_builder(
115 &mut self,
116 name_or_signature: String,
117 configurator: fn(&mut QueryBuilderForFunction) -> Result<(), QueryBuilderError>,
118 ) -> Result<&mut Self, QueryBuilderError> {
119 if self.tx.inner.input().is_empty() {
120 return Err(QueryBuilderError::RequestingFunctionArgumentOfAnEmptyCalldataTransaction);
121 }
122
123 let contract_address = match self.tx.to() {
124 Some(ca) => ca,
125 None => {
126 return Err(QueryBuilderError::RequestingFunctionArgumentButNoToAddressPresent);
127 }
128 };
129
130 let data_field = match self.mapped_offsets.get(&QueryableFields::TxData) {
131 Some(t) => t,
132 None => return Err(QueryBuilderError::FailedToFindTxDataField),
133 }
134 .clone();
135
136 let abi = self
137 .get_abi_from_provider_cached(contract_address.to_string())
138 .await?;
139 let matched_function = if name_or_signature.starts_with("0x") {
141 let name_of_signature_bytes = match self::hex_to_4_bytes(name_or_signature.as_str()) {
142 Ok(t) => t,
143 Err(_) => {
144 return Err(QueryBuilderError::FunctionSignatureNameProvidedIsNotValidHex)
145 }
146 };
147
148 match abi
149 .functions()
150 .find(|f| f.selector().0 == name_of_signature_bytes)
151 {
152 Some(t) => t,
153 None => {
154 return Err(QueryBuilderError::FailedToFindFunctionByNameOrSignature(
155 name_or_signature,
156 ))
157 }
158 }
159 } else {
160 match abi.function(&name_or_signature) {
163 Some(functions) => {
164 if functions.len() > 1 {
165 return Err(QueryBuilderError::AmbigiousFunctionMatch(functions.clone()));
166 }
167
168 match functions.first() {
169 Some(t) => t,
170 None => {
171 return Err(QueryBuilderError::FailedToFindFunctionByNameOrSignature(
172 name_or_signature,
173 ))
174 }
175 }
176 }
177 None => {
178 return Err(QueryBuilderError::FailedToFindFunctionByNameOrSignature(
179 name_or_signature,
180 ));
181 }
182 }
183 };
184
185 let mut builder = QueryBuilderForFunction::new(
188 matched_function.clone(),
189 self.tx.clone(),
190 data_field.clone(),
191 );
192 configurator(&mut builder)?;
193 let offsets_from_builder = builder.get_selected_offsets();
194 self.selected_offsets.extend(offsets_from_builder);
195 Ok(self)
196 }
197
198 pub fn add_static_field(
199 &mut self,
200 field: QueryableFields,
201 ) -> Result<&mut Self, QueryBuilderError> {
202 match self.mapped_offsets.get(&field) {
203 Some(field_offset) => match field_offset.size {
204 Some(size) => {
205 self.selected_offsets.push((field_offset.offset, size));
206 Ok(self)
207 }
208 None => Err(QueryBuilderError::FieldIsNotStatic),
209 },
210 None => Err(QueryBuilderError::FieldNotPresentInTx),
211 }
212 }
213
214 pub async fn multi_event_builder(
215 &mut self,
216 event_name_or_signature: String,
217 filter: fn(Log, DecodedEvent, usize) -> bool,
218 configurator: fn(&mut QueryBuilderForEvent) -> Result<(), QueryBuilderError>,
219 ) -> Result<&mut Self, QueryBuilderError> {
220 let matched_events = self
221 .find_all_events(event_name_or_signature.clone(), filter)
222 .await?;
223
224 let logs_field = match self.mapped_offsets.get(&QueryableFields::RxLogs) {
225 Some(lf) => lf,
226 None => {
227 return Err(QueryBuilderError::FailedToFindRxLogsField);
228 }
229 };
230
231 for (log, decoded_event, log_index, event) in matched_events {
232 let log_field = match logs_field.children.get(log_index) {
233 Some(t) => t,
234 None => {
235 return Err(QueryBuilderError::FailedToGetEventDataOffsets(Box::new(
236 log,
237 )))
238 }
239 };
240
241 let mut event_builder =
242 QueryBuilderForEvent::new(log_field.clone(), log, decoded_event, event);
243 configurator(&mut event_builder)?;
244 let selected_offsets_from_event_builder = event_builder.get_selected_offsets();
245 self.selected_offsets
246 .extend(selected_offsets_from_event_builder);
247 }
248
249 Ok(self)
250 }
251
252 pub async fn event_builder(
253 &mut self,
254 event_name_or_signature: String,
255 filter: fn(Log, DecodedEvent, usize) -> bool,
256 take_first_if_multiple: bool,
257 configurator: fn(&mut QueryBuilderForEvent) -> Result<(), QueryBuilderError>,
258 ) -> Result<&mut Self, QueryBuilderError> {
259 let (log, decoded_event, log_index, event) = match self
260 .find_event(
261 event_name_or_signature.clone(),
262 filter,
263 take_first_if_multiple,
264 )
265 .await?
266 {
267 Some(m) => m,
268 None => {
269 return Err(QueryBuilderError::FailedToFindEventByNameOrSignature(
270 event_name_or_signature,
271 ))
272 }
273 };
274
275 let logs_field = match self.mapped_offsets.get(&QueryableFields::RxLogs) {
276 Some(lf) => lf,
277 None => {
278 return Err(QueryBuilderError::FailedToFindRxLogsField);
279 }
280 };
281
282 let log_field = match logs_field.children.get(log_index) {
283 Some(f) => f,
284 None => {
285 return Err(QueryBuilderError::MissingLogInAbiOffsets(log_index));
286 }
287 };
288
289 let mut event_builder =
290 QueryBuilderForEvent::new(log_field.clone(), log, decoded_event, event);
291 configurator(&mut event_builder)?;
292 let selected_offsets_from_event_builder = event_builder.get_selected_offsets();
293 self.selected_offsets
294 .extend(selected_offsets_from_event_builder);
295 Ok(self)
296 }
297
298 pub async fn find_event(
299 &mut self,
300 event_name_or_signature: String,
301 filter: fn(Log, DecodedEvent, usize) -> bool,
302 take_first_if_multiple: bool,
303 ) -> Result<Option<(Log, DecodedEvent, usize, Event)>, QueryBuilderError> {
304 let events = self
305 .find_all_events(event_name_or_signature.clone(), filter)
306 .await?;
307 if events.is_empty() {
308 Ok(None)
309 } else if events.len() == 1 || take_first_if_multiple {
310 let first_element = events.first().unwrap();
311 Ok(Some(first_element.clone()))
312 } else {
313 Err(QueryBuilderError::AmbigiousEventMatch(
314 event_name_or_signature,
315 ))
316 }
317 }
318
319 pub async fn find_all_events(
320 &mut self,
321 event_name_or_signature: String,
322 filter: fn(Log, DecodedEvent, usize) -> bool,
323 ) -> Result<Vec<(Log, DecodedEvent, usize, Event)>, QueryBuilderError> {
324 let mut extended_logs = Vec::new();
325
326 if event_name_or_signature.starts_with("0x") {
327 let event_signature_as_fixed_bytes =
328 match FixedBytes::<32>::from_hex(event_name_or_signature.clone()) {
329 Ok(decoded_fixed_bytes) => decoded_fixed_bytes,
330 Err(_) => {
331 return Err(QueryBuilderError::EventSignatureNameProvidedIsNotValidHex)
332 }
333 };
334
335 let mut filtered_logs = Vec::new();
337 let mut contract_addresses = Vec::new();
338 for (log_index, log) in self.rx.inner.logs().iter().enumerate() {
339 if let Some(event_hash) = log.topic0() {
340 if event_hash.eq(&event_signature_as_fixed_bytes) {
341 contract_addresses.push(log.address().to_string());
342 filtered_logs.push((log_index, log.clone()));
343 }
344 }
345 }
346
347 let abis = self
349 .get_abis_of_contract_addresses(contract_addresses)
350 .await?;
351 for (log_index, log) in filtered_logs {
352 let contract_address = log.address().to_string();
353 let abi = match abis.get(&contract_address) {
354 Some(a) => a,
355 None => {
356 return Err(QueryBuilderError::NoAbiFoundForContract(
357 contract_address.clone(),
358 ))
359 }
360 };
361
362 let event_of_signature = abi
363 .events()
364 .find(|e| e.selector().0 == event_signature_as_fixed_bytes.0);
365
366 if let Some(event) = event_of_signature {
367 match event.decode_log(&log.inner, true) {
369 Ok(decoded_event) => {
370 extended_logs.push((log, decoded_event, log_index, event.clone()));
371 }
372 Err(_) => {
373 return Err(QueryBuilderError::FailedToDecodeLog(Box::new(
374 log.clone(),
375 )));
376 }
377 }
378 } else {
379 return Err(QueryBuilderError::FailedToFindEventByNameOrSignature(
380 event_name_or_signature.clone(),
381 ));
382 }
383 }
384 } else {
385 let abis = self.get_receipt_abis().await?;
387 let mut log_index = 0;
388 for log in self.rx.inner.logs() {
389 let abi = match abis.get(&log.address().to_string()) {
391 Some(json_abi) => json_abi,
392 None => {
393 return Err(QueryBuilderError::NoAbiFoundForContract(
394 log.address().to_string(),
395 ));
396 }
397 };
398
399 let event_signature = match log.topic0() {
401 Some(es) => es,
402 None => {
403 log_index += 1;
404 continue;
405 }
406 };
407
408 let event_of_signature = abi.events().find(|e| e.selector().0 == event_signature.0);
410
411 if let Some(event) = event_of_signature {
413 if event.name == event_name_or_signature {
416 match event.decode_log(&log.inner, true) {
418 Ok(decoded_event) => {
419 extended_logs.push((
420 log.clone(),
421 decoded_event,
422 log_index,
423 event.clone(),
424 ));
425 }
426 Err(_) => {
427 return Err(QueryBuilderError::FailedToDecodeLog(Box::new(
428 log.clone(),
429 )));
430 }
431 }
432 }
433 } else {
434 return Err(QueryBuilderError::FailedToDecodeLog(Box::new(log.clone())));
437 }
438
439 log_index += 1;
440 }
441 }
442
443 let mut matches = Vec::new();
446 for (log, decoded_event, log_index, event) in extended_logs {
447 if filter(log.clone(), decoded_event.clone(), log_index) {
448 matches.push((log, decoded_event, log_index, event));
449 }
450 }
451
452 Ok(matches)
453 }
454
455 pub async fn get_receipt_abis(
456 &mut self,
457 ) -> Result<HashMap<String, JsonAbi>, QueryBuilderError> {
458 let contract_addresses: Vec<String> = self
459 .rx
460 .inner
461 .logs()
462 .iter()
463 .map(|f| f.address().to_string())
464 .collect();
465
466 let result = self
467 .get_abis_of_contract_addresses(contract_addresses)
468 .await?;
469 Ok(result)
470 }
471
472 pub async fn get_abis_of_contract_addresses(
473 &mut self,
474 contract_addresses: Vec<String>,
475 ) -> Result<HashMap<String, JsonAbi>, QueryBuilderError> {
476 let unique_contract_addresses: HashSet<_> = contract_addresses.into_iter().collect();
477 let mut abi_map = HashMap::new();
478 for contract_address in unique_contract_addresses {
479 let abi: JsonAbi = self
480 .get_abi_from_provider_cached(contract_address.clone())
481 .await?;
482 abi_map.insert(contract_address.clone(), abi);
483 }
484 Ok(abi_map)
485 }
486
487 pub async fn get_abi_from_provider_cached(
488 &mut self,
489 contract_address: String,
490 ) -> Result<JsonAbi, QueryBuilderError> {
491 let result = match self.abi_cache.get(&contract_address.clone()) {
492 Some(existing) => existing,
493 None => {
494 let abi = self.get_abi_from_provider(contract_address.clone()).await?;
495 self.abi_cache.insert(contract_address.clone(), abi.clone());
496 &abi.clone()
497 }
498 };
499
500 Ok(result.clone())
501 }
502
503 pub async fn get_abi_from_provider(
504 &self,
505 contract_address: String,
506 ) -> Result<JsonAbi, QueryBuilderError> {
507 let abi_provider = match &self.abi_provider {
508 Some(ap) => ap,
509 None => return Err(QueryBuilderError::AbiProviderNotInitialized),
510 };
511
512 let abi_raw = abi_provider.get_abi(contract_address.clone()).await?;
513
514 match JsonAbi::from_json_str(&abi_raw) {
515 Ok(json_abi) => Ok(json_abi),
516 Err(_) => Err(QueryBuilderError::FailedToParseAbi(
517 contract_address.clone(),
518 abi_raw,
519 )),
520 }
521 }
522
523 pub fn get_selected_offsets(&self) -> Vec<(usize, usize)> {
524 self.selected_offsets.clone()
525 }
526}