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