hypersync_net_types/
transaction.rs

1use crate::{
2    hypersync_net_types_capnp,
3    types::{AnyOf, Sighash},
4    CapnpBuilder, CapnpReader, Selection,
5};
6use anyhow::Context;
7use hypersync_format::{Address, FilterWrapper, Hash};
8use serde::{Deserialize, Serialize};
9
10#[derive(Default, Serialize, Deserialize, Clone, Debug, PartialEq)]
11pub struct AuthorizationSelection {
12    /// List of chain ids to match in the transaction authorizationList
13    #[serde(default, skip_serializing_if = "Vec::is_empty")]
14    pub chain_id: Vec<u64>,
15    /// List of addresses to match in the transaction authorizationList
16    #[serde(default, skip_serializing_if = "Vec::is_empty")]
17    pub address: Vec<Address>,
18}
19
20impl AuthorizationSelection {
21    /// Create an authorization selection that matches all authorizations.
22    ///
23    /// This creates an empty selection with no constraints, which will match all authorizations.
24    /// You can then use the builder methods to add specific filtering criteria.
25    pub fn all() -> Self {
26        Default::default()
27    }
28
29    /// Filter authorizations by any of the provided chain IDs.
30    ///
31    /// # Arguments
32    /// * `chain_ids` - An iterable of chain IDs to filter by
33    ///
34    /// # Examples
35    ///
36    /// ```
37    /// use hypersync_net_types::AuthorizationSelection;
38    ///
39    /// // Filter by a single chain ID (Ethereum mainnet)
40    /// let selection = AuthorizationSelection::all()
41    ///     .and_chain_id([1]);
42    ///
43    /// // Filter by multiple chain IDs
44    /// let selection = AuthorizationSelection::all()
45    ///     .and_chain_id([
46    ///         1,      // Ethereum mainnet
47    ///         137,    // Polygon
48    ///         42161,  // Arbitrum One
49    ///     ]);
50    ///
51    /// // Chain with address filter
52    /// let selection = AuthorizationSelection::all()
53    ///     .and_chain_id([1, 137])
54    ///     .and_address(["0xdac17f958d2ee523a2206206994597c13d831ec7"])?;
55    /// # Ok::<(), anyhow::Error>(())
56    /// ```
57    pub fn and_chain_id<I>(mut self, chain_ids: I) -> Self
58    where
59        I: IntoIterator<Item = u64>,
60    {
61        self.chain_id = chain_ids.into_iter().collect();
62        self
63    }
64
65    /// Filter authorizations by any of the provided addresses.
66    ///
67    /// This method accepts any iterable of values that can be converted to `Address`.
68    /// Common input types include string slices, byte arrays, and `Address` objects.
69    ///
70    /// # Arguments
71    /// * `addresses` - An iterable of addresses to filter by
72    ///
73    /// # Returns
74    /// * `Ok(Self)` - The updated selection on success
75    /// * `Err(anyhow::Error)` - If any address fails to convert
76    ///
77    /// # Examples
78    ///
79    /// ```
80    /// use hypersync_net_types::AuthorizationSelection;
81    ///
82    /// // Filter by a single address
83    /// let selection = AuthorizationSelection::all()
84    ///     .and_address(["0xdac17f958d2ee523a2206206994597c13d831ec7"])?;
85    ///
86    /// // Filter by multiple addresses
87    /// let selection = AuthorizationSelection::all()
88    ///     .and_address([
89    ///         "0xdac17f958d2ee523a2206206994597c13d831ec7", // Address 1
90    ///         "0xa0b86a33e6c11c8c0c5c0b5e6adee30d1a234567", // Address 2
91    ///     ])?;
92    ///
93    /// // Using byte arrays
94    /// let auth_address = [
95    ///     0xda, 0xc1, 0x7f, 0x95, 0x8d, 0x2e, 0xe5, 0x23, 0xa2, 0x20,
96    ///     0x62, 0x06, 0x99, 0x45, 0x97, 0xc1, 0x3d, 0x83, 0x1e, 0xc7
97    /// ];
98    /// let selection = AuthorizationSelection::all()
99    ///     .and_address([auth_address])?;
100    /// # Ok::<(), anyhow::Error>(())
101    /// ```
102    pub fn and_address<I, A>(mut self, addresses: I) -> anyhow::Result<Self>
103    where
104        I: IntoIterator<Item = A>,
105        A: TryInto<Address>,
106        A::Error: std::error::Error + Send + Sync + 'static,
107    {
108        let mut converted_addresses: Vec<Address> = Vec::new();
109        for (idx, address) in addresses.into_iter().enumerate() {
110            converted_addresses.push(
111                address
112                    .try_into()
113                    .with_context(|| format!("invalid authorization address at position {idx}"))?,
114            );
115        }
116        self.address = converted_addresses;
117        Ok(self)
118    }
119}
120
121pub type TransactionSelection = Selection<TransactionFilter>;
122
123impl From<TransactionFilter> for AnyOf<TransactionFilter> {
124    fn from(filter: TransactionFilter) -> Self {
125        Self::new(filter)
126    }
127}
128
129#[derive(Default, Serialize, Deserialize, Clone, Debug, PartialEq)]
130pub struct TransactionFilter {
131    /// Address the transaction should originate from. If transaction.from matches any of these, the transaction
132    /// will be returned. Keep in mind that this has an and relationship with to filter, so each transaction should
133    /// match both of them. Empty means match all.
134    #[serde(default, skip_serializing_if = "Vec::is_empty")]
135    pub from: Vec<Address>,
136    #[serde(default, skip_serializing_if = "Option::is_none")]
137    pub from_filter: Option<FilterWrapper>,
138    /// Address the transaction should go to. If transaction.to matches any of these, the transaction will
139    /// be returned. Keep in mind that this has an and relationship with from filter, so each transaction should
140    /// match both of them. Empty means match all.
141    #[serde(default, skip_serializing_if = "Vec::is_empty")]
142    pub to: Vec<Address>,
143    #[serde(default, skip_serializing_if = "Option::is_none")]
144    pub to_filter: Option<FilterWrapper>,
145    /// If first 4 bytes of transaction input matches any of these, transaction will be returned. Empty means match all.
146    #[serde(default, skip_serializing_if = "Vec::is_empty")]
147    pub sighash: Vec<Sighash>,
148    /// If transaction.status matches this value, the transaction will be returned.
149    #[serde(skip_serializing_if = "Option::is_none")]
150    pub status: Option<u8>,
151    /// If transaction.type matches any of these values, the transaction will be returned
152    #[serde(rename = "type")]
153    #[serde(default, skip_serializing_if = "Vec::is_empty")]
154    pub type_: Vec<u8>,
155    /// If transaction.contract_address matches any of these values, the transaction will be returned.
156    #[serde(default, skip_serializing_if = "Vec::is_empty")]
157    pub contract_address: Vec<Address>,
158    /// Bloom filter to filter by transaction.contract_address field. If the bloom filter contains the hash
159    /// of transaction.contract_address then the transaction will be returned. This field doesn't utilize the server side filtering
160    /// so it should be used alongside some non-probabilistic filters if possible.
161    #[serde(default, skip_serializing_if = "Option::is_none")]
162    pub contract_address_filter: Option<FilterWrapper>,
163    /// If transaction.hash matches any of these values the transaction will be returned.
164    /// empty means match all.
165    #[serde(default, skip_serializing_if = "Vec::is_empty")]
166    pub hash: Vec<Hash>,
167
168    /// List of authorizations from eip-7702 transactions, the query will return transactions that match any of these selections
169    #[serde(default, skip_serializing_if = "Vec::is_empty")]
170    pub authorization_list: Vec<AuthorizationSelection>,
171}
172
173impl TransactionFilter {
174    /// Create a transaction filter that matches all transactions.
175    ///
176    /// This creates an empty filter with no constraints, which will match all transactions.
177    /// You can then use the builder methods to add specific filtering criteria.
178    pub fn all() -> Self {
179        Default::default()
180    }
181
182    /// Combine this filter with another using logical OR.
183    ///
184    /// Creates an `AnyOf` that matches transactions satisfying either this filter or the other filter.
185    /// This allows for fluent chaining of multiple transaction filters with OR semantics.
186    ///
187    /// # Arguments
188    /// * `other` - Another `TransactionFilter` to combine with this one
189    ///
190    /// # Returns
191    /// An `AnyOf<TransactionFilter>` that matches transactions satisfying either filter
192    ///
193    /// # Examples
194    ///
195    /// ```
196    /// use hypersync_net_types::TransactionFilter;
197    ///
198    /// // Match transactions from specific senders OR with specific function signatures
199    /// let filter = TransactionFilter::all()
200    ///     .and_from(["0xa0b86a33e6c11c8c0c5c0b5e6adee30d1a234567"])?
201    ///     .or(
202    ///         TransactionFilter::all()
203    ///             .and_sighash(["0xa9059cbb"])? // transfer(address,uint256)
204    ///     );
205    /// # Ok::<(), anyhow::Error>(())
206    /// ```
207    pub fn or(self, other: Self) -> AnyOf<Self> {
208        AnyOf::new(self).or(other)
209    }
210
211    /// Filter transactions by any of the provided sender addresses.
212    ///
213    /// This method accepts any iterable of values that can be converted to `Address`.
214    /// Common input types include string slices, byte arrays, and `Address` objects.
215    ///
216    /// # Arguments
217    /// * `addresses` - An iterable of sender addresses to filter by
218    ///
219    /// # Returns
220    /// * `Ok(Self)` - The updated filter on success
221    /// * `Err(anyhow::Error)` - If any address fails to convert
222    ///
223    /// # Examples
224    ///
225    /// ```
226    /// use hypersync_net_types::TransactionFilter;
227    ///
228    /// // Filter by a single sender address
229    /// let filter = TransactionFilter::all()
230    ///     .and_from(["0xdac17f958d2ee523a2206206994597c13d831ec7"])?;
231    ///
232    /// // Filter by multiple sender addresses
233    /// let filter = TransactionFilter::all()
234    ///     .and_from([
235    ///         "0xdac17f958d2ee523a2206206994597c13d831ec7", // Address 1
236    ///         "0xa0b86a33e6c11c8c0c5c0b5e6adee30d1a234567", // Address 2
237    ///     ])?;
238    ///
239    /// // Using byte arrays
240    /// let sender_address = [
241    ///     0xda, 0xc1, 0x7f, 0x95, 0x8d, 0x2e, 0xe5, 0x23, 0xa2, 0x20,
242    ///     0x62, 0x06, 0x99, 0x45, 0x97, 0xc1, 0x3d, 0x83, 0x1e, 0xc7
243    /// ];
244    /// let filter = TransactionFilter::all()
245    ///     .and_from([sender_address])?;
246    /// # Ok::<(), anyhow::Error>(())
247    /// ```
248    pub fn and_from<I, A>(mut self, addresses: I) -> anyhow::Result<Self>
249    where
250        I: IntoIterator<Item = A>,
251        A: TryInto<Address>,
252        A::Error: std::error::Error + Send + Sync + 'static,
253    {
254        let mut converted_addresses: Vec<Address> = Vec::new();
255        for (idx, address) in addresses.into_iter().enumerate() {
256            converted_addresses.push(
257                address
258                    .try_into()
259                    .with_context(|| format!("invalid from address at position {idx}"))?,
260            );
261        }
262        self.from = converted_addresses;
263        Ok(self)
264    }
265
266    /// Filter transactions by any of the provided recipient addresses.
267    ///
268    /// This method accepts any iterable of values that can be converted to `Address`.
269    /// Common input types include string slices, byte arrays, and `Address` objects.
270    ///
271    /// # Arguments
272    /// * `addresses` - An iterable of recipient addresses to filter by
273    ///
274    /// # Returns
275    /// * `Ok(Self)` - The updated filter on success
276    /// * `Err(anyhow::Error)` - If any address fails to convert
277    ///
278    /// # Examples
279    ///
280    /// ```
281    /// use hypersync_net_types::TransactionFilter;
282    ///
283    /// // Filter by a single recipient address
284    /// let filter = TransactionFilter::all()
285    ///     .and_to(["0xdac17f958d2ee523a2206206994597c13d831ec7"])?;
286    ///
287    /// // Filter by multiple recipient addresses (e.g., popular DeFi contracts)
288    /// let filter = TransactionFilter::all()
289    ///     .and_to([
290    ///         "0xdac17f958d2ee523a2206206994597c13d831ec7", // Contract 1
291    ///         "0xa0b86a33e6c11c8c0c5c0b5e6adee30d1a234567", // Contract 2
292    ///     ])?;
293    ///
294    /// // Chain with sender filter
295    /// let filter = TransactionFilter::all()
296    ///     .and_from(["0xa0b86a33e6c11c8c0c5c0b5e6adee30d1a234567"])?
297    ///     .and_to(["0xdac17f958d2ee523a2206206994597c13d831ec7"])?;
298    /// # Ok::<(), anyhow::Error>(())
299    /// ```
300    pub fn and_to<I, A>(mut self, addresses: I) -> anyhow::Result<Self>
301    where
302        I: IntoIterator<Item = A>,
303        A: TryInto<Address>,
304        A::Error: std::error::Error + Send + Sync + 'static,
305    {
306        let mut converted_addresses: Vec<Address> = Vec::new();
307        for (idx, address) in addresses.into_iter().enumerate() {
308            converted_addresses.push(
309                address
310                    .try_into()
311                    .with_context(|| format!("invalid to address at position {idx}"))?,
312            );
313        }
314        self.to = converted_addresses;
315        Ok(self)
316    }
317
318    /// Filter transactions by any of the provided function signatures (first 4 bytes of input).
319    ///
320    /// This method accepts any iterable of values that can be converted to `Sighash`.
321    /// Common input types include string slices, byte arrays, and `Sighash` objects.
322    ///
323    /// # Arguments
324    /// * `sighashes` - An iterable of function signatures to filter by
325    ///
326    /// # Returns
327    /// * `Ok(Self)` - The updated filter on success
328    /// * `Err(anyhow::Error)` - If any sighash fails to convert
329    ///
330    /// # Examples
331    ///
332    /// ```
333    /// use hypersync_net_types::TransactionFilter;
334    ///
335    /// // Filter by a single function signature (transfer)
336    /// let filter = TransactionFilter::all()
337    ///     .and_sighash(["0xa9059cbb"])?; // transfer(address,uint256)
338    ///
339    /// // Filter by multiple function signatures
340    /// let filter = TransactionFilter::all()
341    ///     .and_sighash([
342    ///         "0xa9059cbb", // transfer(address,uint256)
343    ///         "0x23b872dd", // transferFrom(address,address,uint256)
344    ///         "0x095ea7b3", // approve(address,uint256)
345    ///     ])?;
346    ///
347    /// // Using byte arrays
348    /// let transfer_sig = [0xa9, 0x05, 0x9c, 0xbb];
349    /// let filter = TransactionFilter::all()
350    ///     .and_sighash([transfer_sig])?;
351    /// # Ok::<(), anyhow::Error>(())
352    /// ```
353    pub fn and_sighash<I, S>(mut self, sighashes: I) -> anyhow::Result<Self>
354    where
355        I: IntoIterator<Item = S>,
356        S: TryInto<Sighash>,
357        S::Error: std::error::Error + Send + Sync + 'static,
358    {
359        let mut converted_sighashes: Vec<Sighash> = Vec::new();
360        for (idx, sighash) in sighashes.into_iter().enumerate() {
361            converted_sighashes.push(
362                sighash
363                    .try_into()
364                    .with_context(|| format!("invalid sighash at position {idx}"))?,
365            );
366        }
367        self.sighash = converted_sighashes;
368        Ok(self)
369    }
370
371    /// Filter transactions by status (success or failure).
372    ///
373    /// # Arguments
374    /// * `status` - The transaction status to filter by (typically 0 for failure, 1 for success)
375    ///
376    /// # Examples
377    ///
378    /// ```
379    /// use hypersync_net_types::TransactionFilter;
380    ///
381    /// // Filter for successful transactions only
382    /// let filter = TransactionFilter::all()
383    ///     .and_status(1);
384    ///
385    /// // Filter for failed transactions only
386    /// let filter = TransactionFilter::all()
387    ///     .and_status(0);
388    ///
389    /// // Chain with other filters
390    /// let filter = TransactionFilter::all()
391    ///     .and_from(["0xdac17f958d2ee523a2206206994597c13d831ec7"])?
392    ///     .and_status(1); // Only successful transactions from this address
393    /// # Ok::<(), anyhow::Error>(())
394    /// ```
395    pub fn and_status(mut self, status: u8) -> Self {
396        self.status = Some(status);
397        self
398    }
399
400    /// Filter transactions by any of the provided transaction types.
401    ///
402    /// # Arguments
403    /// * `types` - An iterable of transaction types to filter by
404    ///
405    /// # Examples
406    ///
407    /// ```
408    /// use hypersync_net_types::TransactionFilter;
409    ///
410    /// // Filter for legacy transactions only
411    /// let filter = TransactionFilter::all()
412    ///     .and_type([0]);
413    ///
414    /// // Filter for EIP-1559 transactions only
415    /// let filter = TransactionFilter::all()
416    ///     .and_type([2]);
417    ///
418    /// // Filter for multiple transaction types
419    /// let filter = TransactionFilter::all()
420    ///     .and_type([0, 1, 2]); // Legacy, Access List, and EIP-1559
421    /// ```
422    pub fn and_type<I>(mut self, types: I) -> Self
423    where
424        I: IntoIterator<Item = u8>,
425    {
426        self.type_ = types.into_iter().collect();
427        self
428    }
429
430    /// Filter transactions by any of the provided contract addresses.
431    ///
432    /// This method accepts any iterable of values that can be converted to `Address`.
433    /// Common input types include string slices, byte arrays, and `Address` objects.
434    ///
435    /// # Arguments
436    /// * `addresses` - An iterable of contract addresses to filter by
437    ///
438    /// # Returns
439    /// * `Ok(Self)` - The updated filter on success
440    /// * `Err(anyhow::Error)` - If any address fails to convert
441    ///
442    /// # Examples
443    ///
444    /// ```
445    /// use hypersync_net_types::TransactionFilter;
446    ///
447    /// // Filter by a single contract address
448    /// let filter = TransactionFilter::all()
449    ///     .and_contract_address(["0xdac17f958d2ee523a2206206994597c13d831ec7"])?;
450    ///
451    /// // Filter by multiple contract addresses
452    /// let filter = TransactionFilter::all()
453    ///     .and_contract_address([
454    ///         "0xdac17f958d2ee523a2206206994597c13d831ec7", // Contract 1
455    ///         "0xa0b86a33e6c11c8c0c5c0b5e6adee30d1a234567", // Contract 2
456    ///     ])?;
457    /// # Ok::<(), anyhow::Error>(())
458    /// ```
459    pub fn and_contract_address<I, A>(mut self, addresses: I) -> anyhow::Result<Self>
460    where
461        I: IntoIterator<Item = A>,
462        A: TryInto<Address>,
463        A::Error: std::error::Error + Send + Sync + 'static,
464    {
465        let mut converted_addresses: Vec<Address> = Vec::new();
466        for (idx, address) in addresses.into_iter().enumerate() {
467            converted_addresses.push(
468                address
469                    .try_into()
470                    .with_context(|| format!("invalid contract address at position {idx}"))?,
471            );
472        }
473        self.contract_address = converted_addresses;
474        Ok(self)
475    }
476
477    /// Filter transactions by any of the provided transaction hashes.
478    ///
479    /// This method accepts any iterable of values that can be converted to `Hash`.
480    /// Common input types include string slices, byte arrays, and `Hash` objects.
481    ///
482    /// # Arguments
483    /// * `hashes` - An iterable of transaction hashes to filter by
484    ///
485    /// # Returns
486    /// * `Ok(Self)` - The updated filter on success
487    /// * `Err(anyhow::Error)` - If any hash fails to convert
488    ///
489    /// # Examples
490    ///
491    /// ```
492    /// use hypersync_net_types::TransactionFilter;
493    ///
494    /// // Filter by a single transaction hash
495    /// let filter = TransactionFilter::all()
496    ///     .and_hash(["0x40d008f2a1653f09b7b028d30c7fd1ba7c84900fcfb032040b3eb3d16f84d294"])?;
497    ///
498    /// // Filter by multiple transaction hashes
499    /// let filter = TransactionFilter::all()
500    ///     .and_hash([
501    ///         "0x40d008f2a1653f09b7b028d30c7fd1ba7c84900fcfb032040b3eb3d16f84d294",
502    ///         "0x88e96d4537bea4d9c05d12549907b32561d3bf31f45aae734cdc119f13406cb6",
503    ///     ])?;
504    ///
505    /// // Using byte arrays
506    /// let tx_hash = [
507    ///     0x40, 0xd0, 0x08, 0xf2, 0xa1, 0x65, 0x3f, 0x09, 0xb7, 0xb0, 0x28, 0xd3, 0x0c, 0x7f, 0xd1, 0xba,
508    ///     0x7c, 0x84, 0x90, 0x0f, 0xcf, 0xb0, 0x32, 0x04, 0x0b, 0x3e, 0xb3, 0xd1, 0x6f, 0x84, 0xd2, 0x94
509    /// ];
510    /// let filter = TransactionFilter::all()
511    ///     .and_hash([tx_hash])?;
512    /// # Ok::<(), anyhow::Error>(())
513    /// ```
514    pub fn and_hash<I, H>(mut self, hashes: I) -> anyhow::Result<Self>
515    where
516        I: IntoIterator<Item = H>,
517        H: TryInto<Hash>,
518        H::Error: std::error::Error + Send + Sync + 'static,
519    {
520        let mut converted_hashes: Vec<Hash> = Vec::new();
521        for (idx, hash) in hashes.into_iter().enumerate() {
522            converted_hashes.push(
523                hash.try_into()
524                    .with_context(|| format!("invalid transaction hash at position {idx}"))?,
525            );
526        }
527        self.hash = converted_hashes;
528        Ok(self)
529    }
530
531    /// Filter transactions by any of the provided authorization selections.
532    ///
533    /// This method is used for EIP-7702 transactions that include authorization lists.
534    /// It accepts any iterable of `AuthorizationSelection` objects.
535    ///
536    /// # Arguments
537    /// * `selections` - An iterable of authorization selections to filter by
538    ///
539    /// # Examples
540    ///
541    /// ```
542    /// use hypersync_net_types::{TransactionFilter, AuthorizationSelection};
543    ///
544    /// // Filter by a single authorization selection
545    /// let auth_selection = AuthorizationSelection::all()
546    ///     .and_chain_id([1, 137])
547    ///     .and_address(["0xdac17f958d2ee523a2206206994597c13d831ec7"])?;
548    ///
549    /// let filter = TransactionFilter::all()
550    ///     .and_authorization_list([auth_selection])?;
551    ///
552    /// // Filter by multiple authorization selections
553    /// let mainnet_auth = AuthorizationSelection::all()
554    ///     .and_chain_id([1])
555    ///     .and_address(["0xdac17f958d2ee523a2206206994597c13d831ec7"])?;
556    ///
557    /// let polygon_auth = AuthorizationSelection::all()
558    ///     .and_chain_id([137])
559    ///     .and_address(["0xa0b86a33e6c11c8c0c5c0b5e6adee30d1a234567"])?;
560    ///
561    /// let filter = TransactionFilter::all()
562    ///     .and_authorization_list([mainnet_auth, polygon_auth])?;
563    ///
564    /// // Chain with other transaction filters
565    /// let filter = TransactionFilter::all()
566    ///     .and_from(["0xa0b86a33e6c11c8c0c5c0b5e6adee30d1a234567"])?
567    ///     .and_authorization_list([
568    ///         AuthorizationSelection::all()
569    ///             .and_chain_id([1])
570    ///             .and_address(["0xdac17f958d2ee523a2206206994597c13d831ec7"])?
571    ///     ])?;
572    /// # Ok::<(), anyhow::Error>(())
573    /// ```
574    pub fn and_authorization_list<I>(mut self, selections: I) -> anyhow::Result<Self>
575    where
576        I: IntoIterator<Item = AuthorizationSelection>,
577    {
578        self.authorization_list = selections.into_iter().collect();
579        Ok(self)
580    }
581}
582
583impl CapnpBuilder<hypersync_net_types_capnp::authorization_selection::Owned>
584    for AuthorizationSelection
585{
586    fn populate_builder(
587        &self,
588        builder: &mut hypersync_net_types_capnp::authorization_selection::Builder,
589    ) -> Result<(), capnp::Error> {
590        // Set chain ids
591        if !self.chain_id.is_empty() {
592            let mut chain_list = builder.reborrow().init_chain_id(self.chain_id.len() as u32);
593            for (i, chain_id) in self.chain_id.iter().enumerate() {
594                chain_list.set(i as u32, *chain_id);
595            }
596        }
597
598        // Set addresses
599        if !self.address.is_empty() {
600            let mut addr_list = builder.reborrow().init_address(self.address.len() as u32);
601            for (i, addr) in self.address.iter().enumerate() {
602                addr_list.set(i as u32, addr.as_slice());
603            }
604        }
605
606        Ok(())
607    }
608}
609
610impl CapnpReader<hypersync_net_types_capnp::authorization_selection::Owned>
611    for AuthorizationSelection
612{
613    /// Deserialize AuthorizationSelection from Cap'n Proto reader
614    fn from_reader(
615        reader: hypersync_net_types_capnp::authorization_selection::Reader,
616    ) -> Result<Self, capnp::Error> {
617        let mut auth_selection = AuthorizationSelection::default();
618
619        // Parse chain ids
620        if reader.has_chain_id() {
621            let chain_list = reader.get_chain_id()?;
622            for i in 0..chain_list.len() {
623                auth_selection.chain_id.push(chain_list.get(i));
624            }
625        }
626
627        // Parse addresses
628        if reader.has_address() {
629            let addr_list = reader.get_address()?;
630            for i in 0..addr_list.len() {
631                let addr_data = addr_list.get(i)?;
632                if addr_data.len() == 20 {
633                    let mut addr_bytes = [0u8; 20];
634                    addr_bytes.copy_from_slice(addr_data);
635                    auth_selection.address.push(Address::from(addr_bytes));
636                }
637            }
638        }
639
640        Ok(auth_selection)
641    }
642}
643
644impl CapnpBuilder<hypersync_net_types_capnp::transaction_filter::Owned> for TransactionFilter {
645    fn populate_builder(
646        &self,
647        builder: &mut hypersync_net_types_capnp::transaction_filter::Builder,
648    ) -> Result<(), capnp::Error> {
649        // Set from addresses
650        if !self.from.is_empty() {
651            let mut from_list = builder.reborrow().init_from(self.from.len() as u32);
652            for (i, addr) in self.from.iter().enumerate() {
653                from_list.set(i as u32, addr.as_slice());
654            }
655        }
656
657        // Set from filter
658        if let Some(filter) = &self.from_filter {
659            builder.reborrow().set_from_filter(filter.0.as_bytes());
660        }
661
662        // Set to addresses
663        if !self.to.is_empty() {
664            let mut to_list = builder.reborrow().init_to(self.to.len() as u32);
665            for (i, addr) in self.to.iter().enumerate() {
666                to_list.set(i as u32, addr.as_slice());
667            }
668        }
669
670        // Set to filter
671        if let Some(filter) = &self.to_filter {
672            builder.reborrow().set_to_filter(filter.0.as_bytes());
673        }
674
675        // Set sighash
676        if !self.sighash.is_empty() {
677            let mut sighash_list = builder.reborrow().init_sighash(self.sighash.len() as u32);
678            for (i, sighash) in self.sighash.iter().enumerate() {
679                sighash_list.set(i as u32, sighash.as_slice());
680            }
681        }
682
683        // Set status
684        if let Some(status) = self.status {
685            let mut status_builder = builder.reborrow().init_status();
686            status_builder.set_value(status);
687        }
688
689        // Set type
690        if !self.type_.is_empty() {
691            let mut type_list = builder.reborrow().init_type(self.type_.len() as u32);
692            for (i, type_) in self.type_.iter().enumerate() {
693                type_list.set(i as u32, *type_);
694            }
695        }
696
697        // Set contract addresses
698        if !self.contract_address.is_empty() {
699            let mut contract_list = builder
700                .reborrow()
701                .init_contract_address(self.contract_address.len() as u32);
702            for (i, addr) in self.contract_address.iter().enumerate() {
703                contract_list.set(i as u32, addr.as_slice());
704            }
705        }
706
707        // Set contract address filter
708        if let Some(filter) = &self.contract_address_filter {
709            builder
710                .reborrow()
711                .set_contract_address_filter(filter.0.as_bytes());
712        }
713
714        // Set hashes
715        if !self.hash.is_empty() {
716            let mut hash_list = builder.reborrow().init_hash(self.hash.len() as u32);
717            for (i, hash) in self.hash.iter().enumerate() {
718                hash_list.set(i as u32, hash.as_slice());
719            }
720        }
721
722        // Set authorization list
723        if !self.authorization_list.is_empty() {
724            let mut auth_list = builder
725                .reborrow()
726                .init_authorization_list(self.authorization_list.len() as u32);
727            for (i, auth_sel) in self.authorization_list.iter().enumerate() {
728                let mut auth_builder = auth_list.reborrow().get(i as u32);
729                AuthorizationSelection::populate_builder(auth_sel, &mut auth_builder)?;
730            }
731        }
732
733        Ok(())
734    }
735}
736
737impl CapnpReader<hypersync_net_types_capnp::transaction_filter::Owned> for TransactionFilter {
738    /// Deserialize TransactionSelection from Cap'n Proto reader
739    fn from_reader(
740        reader: hypersync_net_types_capnp::transaction_filter::Reader,
741    ) -> Result<Self, capnp::Error> {
742        let mut from = Vec::new();
743
744        // Parse from addresses
745        if reader.has_from() {
746            let from_list = reader.get_from()?;
747            for i in 0..from_list.len() {
748                let addr_data = from_list.get(i)?;
749                if addr_data.len() == 20 {
750                    let mut addr_bytes = [0u8; 20];
751                    addr_bytes.copy_from_slice(addr_data);
752                    from.push(Address::from(addr_bytes));
753                }
754            }
755        }
756
757        let mut from_filter = None;
758
759        // Parse from filter
760        if reader.has_from_filter() {
761            let filter_data = reader.get_from_filter()?;
762            // For now, skip filter deserialization - this would need proper Filter construction
763            let Ok(wrapper) = FilterWrapper::from_bytes(filter_data) else {
764                return Err(capnp::Error::failed("Invalid from filter".to_string()));
765            };
766            from_filter = Some(wrapper);
767        }
768
769        let mut to = Vec::new();
770
771        // Parse to addresses
772        if reader.has_to() {
773            let to_list = reader.get_to()?;
774            for i in 0..to_list.len() {
775                let addr_data = to_list.get(i)?;
776                if addr_data.len() == 20 {
777                    let mut addr_bytes = [0u8; 20];
778                    addr_bytes.copy_from_slice(addr_data);
779                    to.push(Address::from(addr_bytes));
780                }
781            }
782        }
783
784        let mut to_filter = None;
785
786        // Parse to filter
787        if reader.has_to_filter() {
788            let filter_data = reader.get_to_filter()?;
789            let Ok(wrapper) = FilterWrapper::from_bytes(filter_data) else {
790                return Err(capnp::Error::failed("Invalid to filter".to_string()));
791            };
792            to_filter = Some(wrapper);
793        }
794
795        let mut sighash = Vec::new();
796
797        // Parse sighash
798        if reader.has_sighash() {
799            let sighash_list = reader.get_sighash()?;
800            for i in 0..sighash_list.len() {
801                let sighash_data = sighash_list.get(i)?;
802                if sighash_data.len() == 4 {
803                    let mut sighash_bytes = [0u8; 4];
804                    sighash_bytes.copy_from_slice(sighash_data);
805                    sighash.push(Sighash::from(sighash_bytes));
806                }
807            }
808        }
809
810        // Parse status
811        let mut status = None;
812        if reader.has_status() {
813            let status_reader = reader.get_status()?;
814            status = Some(status_reader.get_value());
815        }
816
817        let mut type_ = Vec::new();
818
819        // Parse type
820        if reader.has_type() {
821            let type_list = reader.get_type()?;
822            for i in 0..type_list.len() {
823                type_.push(type_list.get(i));
824            }
825        }
826
827        let mut contract_address = Vec::new();
828        // Parse contract addresses
829        if reader.has_contract_address() {
830            let contract_list = reader.get_contract_address()?;
831            for i in 0..contract_list.len() {
832                let addr_data = contract_list.get(i)?;
833                if addr_data.len() == 20 {
834                    let mut addr_bytes = [0u8; 20];
835                    addr_bytes.copy_from_slice(addr_data);
836                    contract_address.push(Address::from(addr_bytes));
837                }
838            }
839        }
840
841        let mut contract_address_filter = None;
842
843        // Parse contract address filter
844        if reader.has_contract_address_filter() {
845            let filter_data = reader.get_contract_address_filter()?;
846            let Ok(wrapper) = FilterWrapper::from_bytes(filter_data) else {
847                return Err(capnp::Error::failed(
848                    "Invalid contract address filter".to_string(),
849                ));
850            };
851            contract_address_filter = Some(wrapper);
852        }
853
854        let mut hash = Vec::new();
855
856        // Parse hashes
857        if reader.has_hash() {
858            let hash_list = reader.get_hash()?;
859            for i in 0..hash_list.len() {
860                let hash_data = hash_list.get(i)?;
861                if hash_data.len() == 32 {
862                    let mut hash_bytes = [0u8; 32];
863                    hash_bytes.copy_from_slice(hash_data);
864                    hash.push(Hash::from(hash_bytes));
865                }
866            }
867        }
868
869        let mut authorization_list = Vec::new();
870
871        // Parse authorization list
872        if reader.has_authorization_list() {
873            let auth_list = reader.get_authorization_list()?;
874            for i in 0..auth_list.len() {
875                let auth_reader = auth_list.get(i);
876                let auth_selection = AuthorizationSelection::from_reader(auth_reader)?;
877                authorization_list.push(auth_selection);
878            }
879        }
880
881        Ok(Self {
882            from,
883            from_filter,
884            to,
885            to_filter,
886            sighash,
887            status,
888            type_,
889            contract_address,
890            contract_address_filter,
891            hash,
892            authorization_list,
893        })
894    }
895}
896
897#[derive(
898    Debug,
899    Clone,
900    Copy,
901    Serialize,
902    Deserialize,
903    PartialEq,
904    Eq,
905    schemars::JsonSchema,
906    strum_macros::EnumIter,
907    strum_macros::AsRefStr,
908    strum_macros::Display,
909    strum_macros::EnumString,
910)]
911#[serde(rename_all = "snake_case")]
912#[strum(serialize_all = "snake_case")]
913pub enum TransactionField {
914    // Non-nullable fields (required)
915    BlockHash,
916    BlockNumber,
917    Gas,
918    Hash,
919    Input,
920    Nonce,
921    TransactionIndex,
922    Value,
923    CumulativeGasUsed,
924    EffectiveGasPrice,
925    GasUsed,
926    LogsBloom,
927
928    // Nullable fields (optional)
929    From,
930    GasPrice,
931    To,
932    V,
933    R,
934    S,
935    MaxPriorityFeePerGas,
936    MaxFeePerGas,
937    ChainId,
938    ContractAddress,
939    Type,
940    Root,
941    Status,
942    YParity,
943    AccessList,
944    AuthorizationList,
945    L1Fee,
946    L1GasPrice,
947    L1GasUsed,
948    L1FeeScalar,
949    GasUsedForL1,
950    MaxFeePerBlobGas,
951    BlobVersionedHashes,
952    BlobGasPrice,
953    BlobGasUsed,
954    DepositNonce,
955    DepositReceiptVersion,
956    L1BaseFeeScalar,
957    L1BlobBaseFee,
958    L1BlobBaseFeeScalar,
959    L1BlockNumber,
960    Mint,
961    Sighash,
962    SourceHash,
963}
964
965impl Ord for TransactionField {
966    fn cmp(&self, other: &Self) -> std::cmp::Ordering {
967        self.as_ref().cmp(other.as_ref())
968    }
969}
970
971impl PartialOrd for TransactionField {
972    fn partial_cmp(&self, other: &Self) -> Option<std::cmp::Ordering> {
973        Some(self.cmp(other))
974    }
975}
976
977impl TransactionField {
978    pub fn all() -> std::collections::BTreeSet<Self> {
979        use strum::IntoEnumIterator;
980        Self::iter().collect()
981    }
982
983    /// Convert TransactionField to Cap'n Proto enum
984    pub fn to_capnp(&self) -> crate::hypersync_net_types_capnp::TransactionField {
985        match self {
986            TransactionField::BlockHash => {
987                crate::hypersync_net_types_capnp::TransactionField::BlockHash
988            }
989            TransactionField::BlockNumber => {
990                crate::hypersync_net_types_capnp::TransactionField::BlockNumber
991            }
992            TransactionField::Gas => crate::hypersync_net_types_capnp::TransactionField::Gas,
993            TransactionField::Hash => crate::hypersync_net_types_capnp::TransactionField::Hash,
994            TransactionField::Input => crate::hypersync_net_types_capnp::TransactionField::Input,
995            TransactionField::Nonce => crate::hypersync_net_types_capnp::TransactionField::Nonce,
996            TransactionField::TransactionIndex => {
997                crate::hypersync_net_types_capnp::TransactionField::TransactionIndex
998            }
999            TransactionField::Value => crate::hypersync_net_types_capnp::TransactionField::Value,
1000            TransactionField::CumulativeGasUsed => {
1001                crate::hypersync_net_types_capnp::TransactionField::CumulativeGasUsed
1002            }
1003            TransactionField::EffectiveGasPrice => {
1004                crate::hypersync_net_types_capnp::TransactionField::EffectiveGasPrice
1005            }
1006            TransactionField::GasUsed => {
1007                crate::hypersync_net_types_capnp::TransactionField::GasUsed
1008            }
1009            TransactionField::LogsBloom => {
1010                crate::hypersync_net_types_capnp::TransactionField::LogsBloom
1011            }
1012            TransactionField::From => crate::hypersync_net_types_capnp::TransactionField::From,
1013            TransactionField::GasPrice => {
1014                crate::hypersync_net_types_capnp::TransactionField::GasPrice
1015            }
1016            TransactionField::To => crate::hypersync_net_types_capnp::TransactionField::To,
1017            TransactionField::V => crate::hypersync_net_types_capnp::TransactionField::V,
1018            TransactionField::R => crate::hypersync_net_types_capnp::TransactionField::R,
1019            TransactionField::S => crate::hypersync_net_types_capnp::TransactionField::S,
1020            TransactionField::MaxPriorityFeePerGas => {
1021                crate::hypersync_net_types_capnp::TransactionField::MaxPriorityFeePerGas
1022            }
1023            TransactionField::MaxFeePerGas => {
1024                crate::hypersync_net_types_capnp::TransactionField::MaxFeePerGas
1025            }
1026            TransactionField::ChainId => {
1027                crate::hypersync_net_types_capnp::TransactionField::ChainId
1028            }
1029            TransactionField::ContractAddress => {
1030                crate::hypersync_net_types_capnp::TransactionField::ContractAddress
1031            }
1032            TransactionField::Type => crate::hypersync_net_types_capnp::TransactionField::Type,
1033            TransactionField::Root => crate::hypersync_net_types_capnp::TransactionField::Root,
1034            TransactionField::Status => crate::hypersync_net_types_capnp::TransactionField::Status,
1035            TransactionField::YParity => {
1036                crate::hypersync_net_types_capnp::TransactionField::YParity
1037            }
1038            TransactionField::AccessList => {
1039                crate::hypersync_net_types_capnp::TransactionField::AccessList
1040            }
1041            TransactionField::AuthorizationList => {
1042                crate::hypersync_net_types_capnp::TransactionField::AuthorizationList
1043            }
1044            TransactionField::L1Fee => crate::hypersync_net_types_capnp::TransactionField::L1Fee,
1045            TransactionField::L1GasPrice => {
1046                crate::hypersync_net_types_capnp::TransactionField::L1GasPrice
1047            }
1048            TransactionField::L1GasUsed => {
1049                crate::hypersync_net_types_capnp::TransactionField::L1GasUsed
1050            }
1051            TransactionField::L1FeeScalar => {
1052                crate::hypersync_net_types_capnp::TransactionField::L1FeeScalar
1053            }
1054            TransactionField::GasUsedForL1 => {
1055                crate::hypersync_net_types_capnp::TransactionField::GasUsedForL1
1056            }
1057            TransactionField::MaxFeePerBlobGas => {
1058                crate::hypersync_net_types_capnp::TransactionField::MaxFeePerBlobGas
1059            }
1060            TransactionField::BlobVersionedHashes => {
1061                crate::hypersync_net_types_capnp::TransactionField::BlobVersionedHashes
1062            }
1063            TransactionField::BlobGasPrice => {
1064                crate::hypersync_net_types_capnp::TransactionField::BlobGasPrice
1065            }
1066            TransactionField::BlobGasUsed => {
1067                crate::hypersync_net_types_capnp::TransactionField::BlobGasUsed
1068            }
1069            TransactionField::DepositNonce => {
1070                crate::hypersync_net_types_capnp::TransactionField::DepositNonce
1071            }
1072            TransactionField::DepositReceiptVersion => {
1073                crate::hypersync_net_types_capnp::TransactionField::DepositReceiptVersion
1074            }
1075            TransactionField::L1BaseFeeScalar => {
1076                crate::hypersync_net_types_capnp::TransactionField::L1BaseFeeScalar
1077            }
1078            TransactionField::L1BlobBaseFee => {
1079                crate::hypersync_net_types_capnp::TransactionField::L1BlobBaseFee
1080            }
1081            TransactionField::L1BlobBaseFeeScalar => {
1082                crate::hypersync_net_types_capnp::TransactionField::L1BlobBaseFeeScalar
1083            }
1084            TransactionField::L1BlockNumber => {
1085                crate::hypersync_net_types_capnp::TransactionField::L1BlockNumber
1086            }
1087            TransactionField::Mint => crate::hypersync_net_types_capnp::TransactionField::Mint,
1088            TransactionField::Sighash => {
1089                crate::hypersync_net_types_capnp::TransactionField::Sighash
1090            }
1091            TransactionField::SourceHash => {
1092                crate::hypersync_net_types_capnp::TransactionField::SourceHash
1093            }
1094        }
1095    }
1096
1097    /// Convert Cap'n Proto enum to TransactionField
1098    pub fn from_capnp(field: crate::hypersync_net_types_capnp::TransactionField) -> Self {
1099        match field {
1100            crate::hypersync_net_types_capnp::TransactionField::BlockHash => {
1101                TransactionField::BlockHash
1102            }
1103            crate::hypersync_net_types_capnp::TransactionField::BlockNumber => {
1104                TransactionField::BlockNumber
1105            }
1106            crate::hypersync_net_types_capnp::TransactionField::Gas => TransactionField::Gas,
1107            crate::hypersync_net_types_capnp::TransactionField::Hash => TransactionField::Hash,
1108            crate::hypersync_net_types_capnp::TransactionField::Input => TransactionField::Input,
1109            crate::hypersync_net_types_capnp::TransactionField::Nonce => TransactionField::Nonce,
1110            crate::hypersync_net_types_capnp::TransactionField::TransactionIndex => {
1111                TransactionField::TransactionIndex
1112            }
1113            crate::hypersync_net_types_capnp::TransactionField::Value => TransactionField::Value,
1114            crate::hypersync_net_types_capnp::TransactionField::CumulativeGasUsed => {
1115                TransactionField::CumulativeGasUsed
1116            }
1117            crate::hypersync_net_types_capnp::TransactionField::EffectiveGasPrice => {
1118                TransactionField::EffectiveGasPrice
1119            }
1120            crate::hypersync_net_types_capnp::TransactionField::GasUsed => {
1121                TransactionField::GasUsed
1122            }
1123            crate::hypersync_net_types_capnp::TransactionField::LogsBloom => {
1124                TransactionField::LogsBloom
1125            }
1126            crate::hypersync_net_types_capnp::TransactionField::From => TransactionField::From,
1127            crate::hypersync_net_types_capnp::TransactionField::GasPrice => {
1128                TransactionField::GasPrice
1129            }
1130            crate::hypersync_net_types_capnp::TransactionField::To => TransactionField::To,
1131            crate::hypersync_net_types_capnp::TransactionField::V => TransactionField::V,
1132            crate::hypersync_net_types_capnp::TransactionField::R => TransactionField::R,
1133            crate::hypersync_net_types_capnp::TransactionField::S => TransactionField::S,
1134            crate::hypersync_net_types_capnp::TransactionField::MaxPriorityFeePerGas => {
1135                TransactionField::MaxPriorityFeePerGas
1136            }
1137            crate::hypersync_net_types_capnp::TransactionField::MaxFeePerGas => {
1138                TransactionField::MaxFeePerGas
1139            }
1140            crate::hypersync_net_types_capnp::TransactionField::ChainId => {
1141                TransactionField::ChainId
1142            }
1143            crate::hypersync_net_types_capnp::TransactionField::ContractAddress => {
1144                TransactionField::ContractAddress
1145            }
1146            crate::hypersync_net_types_capnp::TransactionField::Type => TransactionField::Type,
1147            crate::hypersync_net_types_capnp::TransactionField::Root => TransactionField::Root,
1148            crate::hypersync_net_types_capnp::TransactionField::Status => TransactionField::Status,
1149            crate::hypersync_net_types_capnp::TransactionField::YParity => {
1150                TransactionField::YParity
1151            }
1152            crate::hypersync_net_types_capnp::TransactionField::AccessList => {
1153                TransactionField::AccessList
1154            }
1155            crate::hypersync_net_types_capnp::TransactionField::AuthorizationList => {
1156                TransactionField::AuthorizationList
1157            }
1158            crate::hypersync_net_types_capnp::TransactionField::L1Fee => TransactionField::L1Fee,
1159            crate::hypersync_net_types_capnp::TransactionField::L1GasPrice => {
1160                TransactionField::L1GasPrice
1161            }
1162            crate::hypersync_net_types_capnp::TransactionField::L1GasUsed => {
1163                TransactionField::L1GasUsed
1164            }
1165            crate::hypersync_net_types_capnp::TransactionField::L1FeeScalar => {
1166                TransactionField::L1FeeScalar
1167            }
1168            crate::hypersync_net_types_capnp::TransactionField::GasUsedForL1 => {
1169                TransactionField::GasUsedForL1
1170            }
1171            crate::hypersync_net_types_capnp::TransactionField::MaxFeePerBlobGas => {
1172                TransactionField::MaxFeePerBlobGas
1173            }
1174            crate::hypersync_net_types_capnp::TransactionField::BlobVersionedHashes => {
1175                TransactionField::BlobVersionedHashes
1176            }
1177            crate::hypersync_net_types_capnp::TransactionField::BlobGasPrice => {
1178                TransactionField::BlobGasPrice
1179            }
1180            crate::hypersync_net_types_capnp::TransactionField::BlobGasUsed => {
1181                TransactionField::BlobGasUsed
1182            }
1183            crate::hypersync_net_types_capnp::TransactionField::DepositNonce => {
1184                TransactionField::DepositNonce
1185            }
1186            crate::hypersync_net_types_capnp::TransactionField::DepositReceiptVersion => {
1187                TransactionField::DepositReceiptVersion
1188            }
1189            crate::hypersync_net_types_capnp::TransactionField::L1BaseFeeScalar => {
1190                TransactionField::L1BaseFeeScalar
1191            }
1192            crate::hypersync_net_types_capnp::TransactionField::L1BlobBaseFee => {
1193                TransactionField::L1BlobBaseFee
1194            }
1195            crate::hypersync_net_types_capnp::TransactionField::L1BlobBaseFeeScalar => {
1196                TransactionField::L1BlobBaseFeeScalar
1197            }
1198            crate::hypersync_net_types_capnp::TransactionField::L1BlockNumber => {
1199                TransactionField::L1BlockNumber
1200            }
1201            crate::hypersync_net_types_capnp::TransactionField::Mint => TransactionField::Mint,
1202            crate::hypersync_net_types_capnp::TransactionField::Sighash => {
1203                TransactionField::Sighash
1204            }
1205            crate::hypersync_net_types_capnp::TransactionField::SourceHash => {
1206                TransactionField::SourceHash
1207            }
1208        }
1209    }
1210}
1211
1212#[cfg(test)]
1213mod tests {
1214    use hypersync_format::Hex;
1215
1216    use super::*;
1217    use crate::{query::tests::test_query_serde, Query};
1218
1219    #[test]
1220    fn test_all_fields_in_schema() {
1221        let schema = hypersync_schema::transaction();
1222        let schema_fields = schema
1223            .fields
1224            .iter()
1225            .map(|f| f.name.clone())
1226            .collect::<std::collections::BTreeSet<_>>();
1227        let all_fields = TransactionField::all()
1228            .into_iter()
1229            .map(|f| f.as_ref().to_string())
1230            .collect::<std::collections::BTreeSet<_>>();
1231        assert_eq!(schema_fields, all_fields);
1232    }
1233
1234    #[test]
1235    fn test_serde_matches_strum() {
1236        for field in TransactionField::all() {
1237            let serialized = serde_json::to_string(&field).unwrap();
1238            let strum = serde_json::to_string(&field.as_ref()).unwrap();
1239            assert_eq!(serialized, strum, "strum value should be the same as serde");
1240        }
1241    }
1242
1243    #[test]
1244    fn test_transaction_filter_serde_with_defaults() {
1245        let transaction_filter = TransactionSelection::default();
1246        let query = Query::new()
1247            .where_transactions(transaction_filter)
1248            .select_transaction_fields(TransactionField::all());
1249
1250        test_query_serde(query, "transaction selection with defaults");
1251    }
1252    #[test]
1253    fn test_transaction_filter_serde_with_explicit_defaults() {
1254        let transaction_filter = TransactionFilter {
1255            from: Vec::default(),
1256            from_filter: Some(FilterWrapper::new(16, 0)),
1257            to: Vec::default(),
1258            to_filter: Some(FilterWrapper::new(16, 0)),
1259            sighash: Vec::default(),
1260            status: Some(u8::default()),
1261            type_: Vec::default(),
1262            contract_address: Vec::default(),
1263            contract_address_filter: Some(FilterWrapper::new(16, 0)),
1264            hash: Vec::default(),
1265            authorization_list: Vec::default(),
1266        };
1267        let query = Query::new()
1268            .where_transactions(transaction_filter)
1269            .select_transaction_fields(TransactionField::all());
1270
1271        test_query_serde(query, "transaction selection with explicit defaults");
1272    }
1273
1274    #[test]
1275    fn test_transaction_filter_serde_with_full_values() {
1276        let transaction_filter = TransactionFilter {
1277            from: vec![Address::decode_hex("0xdadB0d80178819F2319190D340ce9A924f783711").unwrap()],
1278            from_filter: Some(FilterWrapper::new(16, 1)),
1279            to: vec![Address::decode_hex("0x742d35Cc6634C0532925a3b8D4C9db96C4b4d8b6").unwrap()],
1280            to_filter: Some(FilterWrapper::new(16, 1)),
1281            sighash: vec![Sighash::from([0x12, 0x34, 0x56, 0x78])],
1282            status: Some(1),
1283            type_: vec![2],
1284            contract_address: vec![Address::decode_hex(
1285                "0x1234567890123456789012345678901234567890",
1286            )
1287            .unwrap()],
1288            contract_address_filter: Some(FilterWrapper::new(16, 1)),
1289            hash: vec![Hash::decode_hex(
1290                "0x40d008f2a1653f09b7b028d30c7fd1ba7c84900fcfb032040b3eb3d16f84d294",
1291            )
1292            .unwrap()],
1293            authorization_list: Vec::default(),
1294        };
1295        let query = Query::new()
1296            .where_transactions(transaction_filter)
1297            .select_transaction_fields(TransactionField::all());
1298
1299        test_query_serde(query, "transaction selection with full values");
1300    }
1301
1302    #[test]
1303    fn test_authorization_selection_serde_with_values() {
1304        let auth_selection = AuthorizationSelection {
1305            chain_id: vec![1, 137, 42161],
1306            address: vec![
1307                Address::decode_hex("0xdadB0d80178819F2319190D340ce9A924f783711").unwrap(),
1308            ],
1309        };
1310        let transaction_filter = TransactionFilter {
1311            from: Vec::default(),
1312            from_filter: Some(FilterWrapper::new(16, 0)),
1313            to: Vec::default(),
1314            to_filter: Some(FilterWrapper::new(16, 0)),
1315            sighash: Vec::default(),
1316            status: Some(u8::default()),
1317            type_: Vec::default(),
1318            contract_address: Vec::default(),
1319            contract_address_filter: Some(FilterWrapper::new(16, 0)),
1320            hash: Vec::default(),
1321            authorization_list: vec![auth_selection],
1322        };
1323        let query = Query::new()
1324            .where_transactions(transaction_filter)
1325            .select_transaction_fields(TransactionField::all());
1326
1327        test_query_serde(query, "authorization selection with rest defaults");
1328    }
1329}