ccnext_query_builder/abi/
query_builder.rs1use std::collections::HashMap;
2
3use alloy::{consensus::Transaction as _, dyn_abi::{DecodedEvent, DynSolType, EventExt}, hex::FromHex, json_abi::JsonAbi, primitives::{map::HashSet, FixedBytes}, rpc::types::{Log, Transaction, TransactionReceipt}};
4use alloy_json_abi::{Event, Function};
5use async_trait::async_trait;
6
7use crate::abi::{models::{FieldMetadata, QueryableFields}, query_builder_for_function::QueryBuilderForFunction};
8use ccnext_abi_encoding::abi::abi_encode;
9use super::{abi_encoding_mapping::get_all_fields_for_transaction, models::QueryBuilderError, query_builder_for_event::QueryBuilderForEvent, utils::compute_abi_offsets};
10
11#[async_trait]
12pub trait AbiProvider {
13 async fn get_abi(&self, contract_address: String) -> Result<String, QueryBuilderError>;
14}
15
16pub struct QueryBuilder {
17 tx: Transaction,
18 rx: TransactionReceipt,
19 abi_provider: Option<Box<dyn AbiProvider>>,
20 _computed_offsets: Vec<FieldMetadata>,
21 mapped_offsets: HashMap<QueryableFields, FieldMetadata>,
22 selected_offsets: Vec<(usize, usize)>,
23 abi_cache: HashMap<String, JsonAbi>
24}
25fn hex_to_4_bytes(hex: &str) -> Result<[u8; 4], &'static str> {
26 let hex = hex.strip_prefix("0x").unwrap_or(hex); if hex.len() != 8 {
29 return Err("Hex string must be exactly 8 characters long (excluding 0x)");
30 }
31
32 let bytes = hex::decode(hex).map_err(|_| "Invalid hex string")?;
33
34 let mut arr = [0u8; 4];
35 arr.copy_from_slice(&bytes[..4]);
36
37 Ok(arr)
38}
39
40impl QueryBuilder {
41 pub fn create_from_transaction(tx: Transaction, rx: TransactionReceipt) -> Result<QueryBuilder, QueryBuilderError> {
42
43 let encoded = match abi_encode(tx.clone(), rx.clone()) {
45 Ok(encoded_result) => encoded_result,
46 Err(_) => {
47 return Err(QueryBuilderError::FailedToAbiEncode);
48 },
49 };
50
51 let field_and_types = get_all_fields_for_transaction(tx.inner.tx_type().clone());
53
54 let fields: Vec<QueryableFields> = field_and_types.iter().map(|f| f.0.clone()).collect();
56
57 let sol_types: Vec<DynSolType> = field_and_types.iter().map(|f| f.1.clone()).collect();
59
60 let computed_offsets = match compute_abi_offsets(sol_types, encoded.abi.clone()) {
62 Ok(co) => {
63 co
64 },
65 Err(_) => {
66 return Err(QueryBuilderError::FailedToComputeOffsets);
67 }
68 };
69
70 if computed_offsets.len() != field_and_types.len() {
74 return Err(QueryBuilderError::MissMatchedLengthDecoding);
75 }
76
77 let mut mapped_offsets = HashMap::<QueryableFields, FieldMetadata>::new();
79 let fields_offset = fields.iter().zip(computed_offsets.clone());
80 for (field, offset) in fields_offset {
81 mapped_offsets.insert(field.clone(), offset);
82 }
83
84 Ok(QueryBuilder {
85 tx,
86 rx,
87 abi_provider: None,
88 mapped_offsets,
89 _computed_offsets: computed_offsets.clone(),
90 selected_offsets: vec![],
91 abi_cache: HashMap::new()
92 })
93 }
94
95 pub fn set_abi_provider(&mut self, abi_provider: Box<dyn AbiProvider>) {
96 self.abi_provider = Some(abi_provider);
97 }
98
99 pub async fn function_builder(&mut self, name_or_signature: String,
100 configurator: fn(&mut QueryBuilderForFunction) -> Result<(), QueryBuilderError>
101 ) -> Result<&mut Self, QueryBuilderError> {
102
103 if self.tx.inner.input().is_empty() {
104 return Err(QueryBuilderError::RequestingFunctionArgumentOfAnEmptyCalldataTransaction);
105 }
106
107 let contract_address = match self.tx.to() {
108 Some(ca) => ca,
109 None => {
110 return Err(QueryBuilderError::RequestingFunctionArgumentButNoToAddressPresent);
111 }
112 };
113
114 let data_field = match self.mapped_offsets.get(&QueryableFields::TxData) {
115 Some(t) => t,
116 None => return Err(QueryBuilderError::FailedToFindTxDataField)
117 }.clone();
118
119 let abi = self.get_abi_from_provider_cached(contract_address.to_string()).await?;
120 let matched_function: &Function;
121 if name_or_signature.starts_with("0x") {
123
124 let name_of_signature_bytes = match self::hex_to_4_bytes(name_or_signature.as_str()) {
125 Ok(t) => t,
126 Err(_) => return Err(QueryBuilderError::FunctionSignatureNameProvidedIsNotValidHex)
127 };
128
129 matched_function = match abi.functions().find(|f| f.selector().0 == name_of_signature_bytes) {
130 Some(t) => t,
131 None => return Err(QueryBuilderError::FailedToFindFunctionByNameOrSignature(name_or_signature))
132 };
133
134 } else {
135
136 matched_function = match abi.function(&name_or_signature) {
139 Some(functions) => {
140 if functions.len() > 1 {
141 return Err(QueryBuilderError::AmbigiousFunctionMatch(functions.clone()));
142 }
143
144 match functions.get(0) {
145 Some(t) => t,
146 None => return Err(QueryBuilderError::FailedToFindFunctionByNameOrSignature(name_or_signature))
147 }
148 },
149 None => {
150 return Err(QueryBuilderError::FailedToFindFunctionByNameOrSignature(name_or_signature));
151 }
152 };
153 }
154
155 let mut builder = QueryBuilderForFunction::new(matched_function.clone(), self.tx.clone(), data_field.clone());
158 configurator(&mut builder)?;
159 let offsets_from_builder = builder.get_selected_offsets();
160 self.selected_offsets.extend(offsets_from_builder);
161 Ok(self)
162 }
163
164 pub fn add_static_field(&mut self, field: QueryableFields) -> Result<&mut Self, QueryBuilderError> {
165 match self.mapped_offsets.get(&field) {
166 Some(field_offset) => {
167 match field_offset.size {
168 Some(size) => {
169 self.selected_offsets.push((field_offset.offset, size));
170 Ok(self)
171 }
172 None => {
173 Err(QueryBuilderError::FieldIsNotStatic)
174 }
175 }
176 },
177 None => {
178 Err(QueryBuilderError::FieldNotPresentInTx)
179 }
180 }
181 }
182
183 pub async fn multi_event_builder(&mut self,
184 event_name_or_signature: String,
185 filter: fn(Log, DecodedEvent, usize) -> bool,
186 configurator: fn(&mut QueryBuilderForEvent) -> Result<(), QueryBuilderError>
187 ) -> Result<&mut Self, QueryBuilderError> {
188
189 let matched_events = self.find_all_events(event_name_or_signature.clone(), filter).await?;
190
191 let logs_field = match self.mapped_offsets.get(&QueryableFields::RxLogs) {
192 Some(lf) => lf,
193 None => {
194 return Err(QueryBuilderError::FailedToFindRxLogsField);
195 }
196 };
197
198 for (log, decoded_event, log_index, event) in matched_events {
199
200 let log_field = match logs_field.children.get(log_index) {
201 Some(t) => t,
202 None => return Err(QueryBuilderError::FailedToGetEventDataOffsets(log))
203 };
204
205 let mut event_builder = QueryBuilderForEvent::new(
206 log_field.clone(),
207 log,
208 decoded_event,
209 event
210 );
211 configurator(&mut event_builder)?;
212 let selected_offsets_from_event_builder = event_builder.get_selected_offsets();
213 self.selected_offsets.extend(selected_offsets_from_event_builder);
214 }
215
216 Ok(self)
217 }
218
219 pub async fn event_builder(&mut self,
220 event_name_or_signature: String,
221 filter: fn(Log, DecodedEvent, usize) -> bool,
222 configurator: fn(&mut QueryBuilderForEvent) -> Result<(), QueryBuilderError>
223 ) -> Result<&mut Self, QueryBuilderError> {
224
225 let (log, decoded_event, log_index, event) = match self.find_event(event_name_or_signature.clone(), filter).await? {
226 Some(m) => {
227 m
228 },
229 None => {
230 return Err(QueryBuilderError::FailedToFindEventByNameOrSignature(event_name_or_signature))
231 }
232 };
233
234 let logs_field = match self.mapped_offsets.get(&QueryableFields::RxLogs) {
235 Some(lf) => lf,
236 None => {
237 return Err(QueryBuilderError::FailedToFindRxLogsField);
238 }
239 };
240
241 let log_field = match logs_field.children.get(log_index) {
242 Some(f) => f,
243 None => {
244 return Err(QueryBuilderError::MissingLogInAbiOffsets(log_index));
245 }
246 };
247
248 let mut event_builder = QueryBuilderForEvent::new(log_field.clone(), log, decoded_event, event);
249 configurator(&mut event_builder)?;
250 let selected_offsets_from_event_builder = event_builder.get_selected_offsets();
251 self.selected_offsets.extend(selected_offsets_from_event_builder);
252 Ok(self)
253 }
254
255 pub async fn find_event(&mut self, event_name_or_signature: String, filter: fn(Log, DecodedEvent, usize) -> bool) -> Result<Option<(Log, DecodedEvent, usize, Event)>, QueryBuilderError> {
256 let events = self.find_all_events(event_name_or_signature.clone(), filter).await?;
257 if events.len() == 0 {
258 Ok(None)
259 } else if events.len() == 1 {
260 let first_element = events.get(0).unwrap();
261 Ok(Some(first_element.clone()))
262 } else {
263 Err(QueryBuilderError::AmbigiousEventMatch(event_name_or_signature))
264 }
265 }
266
267 pub async fn find_all_events(&mut self, event_name_or_signature: String, filter: fn (Log, DecodedEvent, usize) -> bool) -> Result<Vec<(Log, DecodedEvent, usize, Event)>, QueryBuilderError> {
268
269
270 let mut extended_logs = Vec::new();
271
272 if event_name_or_signature.starts_with("0x") {
273 let event_signature_as_fixed_bytes = match FixedBytes::<32>::from_hex(event_name_or_signature.clone()) {
274 Ok(decoded_fixed_bytes) => decoded_fixed_bytes,
275 Err(_) => return Err(QueryBuilderError::EventSignatureNameProvidedIsNotValidHex)
276 };
277
278 let mut filtered_logs = Vec::new();
280 let mut log_index = 0;
281 let mut contract_addresses = Vec::new();
282 for log in self.rx.inner.logs() {
283
284 if let Some(event_hash) = log.topic0() {
285 if event_hash.eq(&event_signature_as_fixed_bytes) {
286 contract_addresses.push(log.address().to_string());
287 filtered_logs.push((log_index, log.clone()));
288 }
289 }
290
291 log_index += 1;
292 }
293
294 let abis = self.get_abis_of_contract_addresses(contract_addresses).await?;
296 for (log_index, log) in filtered_logs {
297 let contract_address = log.address().to_string();
298 let abi = match abis.get(&contract_address) {
299 Some(a) => a,
300 None => return Err(QueryBuilderError::NoAbiFoundForContract(contract_address.clone()))
301 };
302
303 let event_of_signature = abi.events().find(|e| {
304 e.selector().0 == event_signature_as_fixed_bytes.0
305 });
306
307 if let Some(event) = event_of_signature {
308
309 match event.decode_log(&log.inner, true) {
311 Ok(decoded_event) => {
312 extended_logs.push((log, decoded_event, log_index, event.clone()));
313 }
314 Err(_) => {
315 return Err(QueryBuilderError::FailedToDecodeLog(log.clone()));
316 }
317 }
318 } else {
319 return Err(QueryBuilderError::FailedToFindEventByNameOrSignature(event_name_or_signature.clone()));
320 }
321 }
322
323 } else {
324
325 let abis = self.get_receipt_abis().await?;
327 let mut log_index = 0;
328 for log in self.rx.inner.logs() {
329
330 let abi = match abis.get(&log.address().to_string()) {
332 Some(json_abi) => {
333 json_abi
334 }
335 None => {
336 return Err(QueryBuilderError::NoAbiFoundForContract(log.address().to_string()));
337 }
338 };
339
340 let event_signature = match log.topic0() {
342 Some(es) => es,
343 None => {
344 log_index += 1;
345 continue;
346 }
347 };
348
349 let event_of_signature = abi.events().find(|e| {
351 e.selector().0 == event_signature.0
352 });
353
354 if let Some(event) = event_of_signature {
356
357 if event.name == event_name_or_signature {
360 match event.decode_log(&log.inner, true) {
362 Ok(decoded_event) => {
363 extended_logs.push((log.clone(), decoded_event, log_index, event.clone()));
364 }
365 Err(_) => {
366 return Err(QueryBuilderError::FailedToDecodeLog(log.clone()));
367 }
368 }
369 }
370 } else {
371 return Err(QueryBuilderError::FailedToDecodeLog(log.clone()));
374 }
375
376 log_index += 1;
377 }
378 }
379
380 let mut matches = Vec::new();
383 for (log, decoded_event, log_index, event) in extended_logs {
384 if true == filter(log.clone(), decoded_event.clone(), log_index.clone()) {
385 matches.push((log, decoded_event, log_index, event));
386 }
387 }
388
389 Ok(matches)
390 }
391
392 pub async fn get_receipt_abis(&mut self) -> Result<HashMap<String, JsonAbi>, QueryBuilderError> {
393 let contract_addresses: Vec<String> = self.rx.inner
394 .logs()
395 .iter()
396 .map(|f| f.address().to_string())
397 .collect();
398
399 let result = self.get_abis_of_contract_addresses(contract_addresses).await?;
400 Ok(result)
401 }
402
403 pub async fn get_abis_of_contract_addresses(&mut self, contract_addresses: Vec<String>) -> Result<HashMap<String, JsonAbi>, QueryBuilderError> {
404 let unique_contract_addresses: HashSet<_> = contract_addresses.into_iter().collect();
405 let mut abi_map = HashMap::new();
406 for contract_address in unique_contract_addresses {
407 let abi: JsonAbi = self.get_abi_from_provider_cached(contract_address.clone()).await?;
408 abi_map.insert(contract_address.clone(), abi);
409 }
410 Ok(abi_map)
411 }
412
413 pub async fn get_abi_from_provider_cached(&mut self, contract_address: String) -> Result<JsonAbi, QueryBuilderError> {
414
415
416 let result = match self.abi_cache.get(&contract_address.clone()) {
417 Some(existing) => {
418 existing
419 }
420 None => {
421 let abi = self.get_abi_from_provider(contract_address.clone()).await?;
422 self.abi_cache.insert(contract_address.clone(), abi.clone());
423 &abi.clone()
424 }
425 };
426
427 Ok(result.clone())
428 }
429
430 pub async fn get_abi_from_provider(&self, contract_address: String) -> Result<JsonAbi, QueryBuilderError> {
431 let abi_provider = match &self.abi_provider {
432 Some(ap) => ap,
433 None => return Err(QueryBuilderError::AbiProviderNotInitialized)
434 };
435
436 let abi_raw = abi_provider.get_abi(contract_address.clone()).await?;
437
438 match JsonAbi::from_json_str(&abi_raw) {
439 Ok(json_abi) => Ok(json_abi),
440 Err(_) => Err(QueryBuilderError::FailedToParseAbi(contract_address.clone(), abi_raw))
441 }
442 }
443
444 pub fn get_selected_offsets(&self) -> Vec<(usize, usize)> {
445 self.selected_offsets.clone()
446 }
447}