alloy_rpc_types_eth/
filter.rs

1use crate::{BlockNumberOrTag, Log as RpcLog, Transaction};
2use alloc::{borrow::Cow, string::String, vec::Vec};
3use alloy_eips::BlockNumHash;
4use alloy_primitives::{
5    keccak256,
6    map::{hash_set, HashSet},
7    Address, BlockHash, Bloom, BloomInput, Log, LogData, B256, U256, U64,
8};
9use core::{
10    hash::Hash,
11    ops::{RangeFrom, RangeInclusive, RangeToInclusive},
12};
13use itertools::{
14    EitherOrBoth::{Both, Left, Right},
15    Itertools,
16};
17
18/// FilterSet is a set of values that will be used to filter logs.
19#[derive(Clone, Debug, PartialEq, Eq)]
20#[cfg_attr(feature = "serde", derive(serde::Deserialize))]
21#[cfg_attr(feature = "serde", serde(from = "HashSet<T>"))]
22pub struct FilterSet<T: Eq + Hash> {
23    set: HashSet<T>,
24
25    #[cfg(feature = "std")]
26    #[cfg_attr(feature = "serde", serde(skip, default))]
27    bloom_filter: std::sync::OnceLock<BloomFilter>,
28}
29
30impl<T: Eq + Hash> Default for FilterSet<T> {
31    fn default() -> Self {
32        Self {
33            set: Default::default(),
34            #[cfg(feature = "std")]
35            bloom_filter: Default::default(),
36        }
37    }
38}
39
40impl<T: Eq + Hash> From<T> for FilterSet<T> {
41    fn from(src: T) -> Self {
42        Self { set: core::iter::once(src).collect(), ..Default::default() }
43    }
44}
45
46impl<T: Eq + Hash> Hash for FilterSet<T> {
47    fn hash<H: core::hash::Hasher>(&self, state: &mut H) {
48        for value in &self.set {
49            value.hash(state);
50        }
51    }
52}
53
54impl<T: Eq + Hash> From<HashSet<T>> for FilterSet<T> {
55    fn from(src: HashSet<T>) -> Self {
56        Self { set: src, ..Default::default() }
57    }
58}
59
60impl<T: Eq + Hash> From<Vec<T>> for FilterSet<T> {
61    fn from(src: Vec<T>) -> Self {
62        src.into_iter().collect::<HashSet<_>>().into()
63    }
64}
65
66impl<T: Eq + Hash> From<ValueOrArray<T>> for FilterSet<T> {
67    fn from(src: ValueOrArray<T>) -> Self {
68        match src {
69            ValueOrArray::Value(val) => val.into(),
70            ValueOrArray::Array(arr) => arr.into(),
71        }
72    }
73}
74
75impl<T: Eq + Hash> From<ValueOrArray<Option<T>>> for FilterSet<T> {
76    fn from(src: ValueOrArray<Option<T>>) -> Self {
77        match src {
78            ValueOrArray::Value(None) => Default::default(),
79            ValueOrArray::Value(Some(val)) => val.into(),
80            ValueOrArray::Array(arr) => {
81                // If the array contains at least one `null` (ie. None), as it's considered
82                // a "wildcard" value, the whole filter should be treated as matching everything,
83                // thus is empty.
84                if arr.iter().contains(&None) {
85                    Default::default()
86                } else {
87                    // Otherwise, we flatten the array, knowing there are no `None` values
88                    arr.into_iter().flatten().collect::<Vec<T>>().into()
89                }
90            }
91        }
92    }
93}
94
95impl<T: Eq + Hash> IntoIterator for FilterSet<T> {
96    type Item = T;
97    type IntoIter = hash_set::IntoIter<T>;
98
99    fn into_iter(self) -> Self::IntoIter {
100        self.set.into_iter()
101    }
102}
103
104impl<T: Eq + Hash> FromIterator<T> for FilterSet<T> {
105    fn from_iter<I: IntoIterator<Item = T>>(iter: I) -> Self {
106        HashSet::from_iter(iter).into()
107    }
108}
109
110impl<T: Eq + Hash> FilterSet<T> {
111    /// Returns whether the filter is empty
112    pub fn is_empty(&self) -> bool {
113        self.set.is_empty()
114    }
115
116    /// Returns the number of values in the filter
117    pub fn len(&self) -> usize {
118        self.set.len()
119    }
120
121    /// Returns whether the given value matches the filter. If the filter is empty
122    /// any value matches. Otherwise, the filter must include the value
123    pub fn matches(&self, value: &T) -> bool {
124        self.is_empty() || self.set.contains(value)
125    }
126
127    /// Returns an iterator over the underlying HashSet. Values are visited
128    /// in an arbitrary order.
129    pub fn iter(&self) -> hash_set::Iter<'_, T> {
130        self.set.iter()
131    }
132
133    /// Check if the filter contains the given value
134    pub fn contains(&self, value: &T) -> bool {
135        self.set.contains(value)
136    }
137
138    /// Drop the bloom filter if it exists. This should be invoked by any
139    /// method taking `&mut self`.
140    fn unseal(&mut self) {
141        #[cfg(feature = "std")]
142        self.bloom_filter.take();
143    }
144
145    /// Insert a value into the filter
146    pub fn insert(&mut self, value: T) -> bool {
147        self.unseal();
148        self.set.insert(value)
149    }
150
151    /// Remove a value from the filter (if present)
152    pub fn remove(&mut self, value: &T) -> bool {
153        if self.contains(value) {
154            self.unseal();
155            self.set.remove(value)
156        } else {
157            false
158        }
159    }
160}
161
162impl<T: AsRef<[u8]> + Eq + Hash> FilterSet<T> {
163    /// Get a reference to the BloomFilter.
164    #[cfg(feature = "std")]
165    pub fn bloom_filter_ref(&self) -> &BloomFilter {
166        self.bloom_filter.get_or_init(|| self.make_bloom_filter())
167    }
168
169    /// Returns a list of Bloom (BloomFilter) corresponding to the filter's values
170    pub fn bloom_filter(&self) -> Cow<'_, BloomFilter> {
171        #[cfg(feature = "std")]
172        {
173            Cow::Borrowed(self.bloom_filter_ref())
174        }
175
176        #[cfg(not(feature = "std"))]
177        {
178            Cow::Owned(self.make_bloom_filter())
179        }
180    }
181
182    /// Returns a list of Bloom (BloomFilter) corresponding to the filter's values
183    fn make_bloom_filter(&self) -> BloomFilter {
184        self.set.iter().map(|a| BloomInput::Raw(a.as_ref()).into()).collect::<Vec<Bloom>>().into()
185    }
186}
187
188impl<T: Clone + Eq + Hash> FilterSet<T> {
189    /// Returns a ValueOrArray inside an Option, so that:
190    /// - If the filter is empty, it returns None
191    /// - If the filter has only 1 value, it returns the single value
192    /// - Otherwise it returns an array of values
193    pub fn to_value_or_array(&self) -> Option<ValueOrArray<T>> {
194        let mut values = self.set.iter().cloned().collect::<Vec<T>>();
195        match values.len() {
196            0 => None,
197            1 => Some(ValueOrArray::Value(values.pop().expect("values length is one"))),
198            _ => Some(ValueOrArray::Array(values)),
199        }
200    }
201}
202
203/// A single topic
204pub type Topic = FilterSet<B256>;
205
206impl Topic {
207    /// Extends the topic with a value that can be converted into a Topic
208    pub fn extend<T: Into<Self>>(mut self, value: T) -> Self {
209        self.unseal();
210        self.set.extend(value.into());
211        self
212    }
213}
214
215impl From<U256> for Topic {
216    fn from(src: U256) -> Self {
217        Into::<B256>::into(src).into()
218    }
219}
220
221impl From<Address> for Topic {
222    fn from(address: Address) -> Self {
223        let mut bytes = [0u8; 32];
224        bytes[12..].copy_from_slice(address.as_slice());
225        B256::from(bytes).into()
226    }
227}
228
229impl From<bool> for Topic {
230    fn from(value: bool) -> Self {
231        let mut bytes = [0u8; 32];
232        bytes[31] = if value { 1 } else { 0 };
233        B256::from(bytes).into()
234    }
235}
236
237impl From<[u8; 32]> for Topic {
238    fn from(bytes: [u8; 32]) -> Self {
239        B256::from(bytes).into()
240    }
241}
242
243/// Represents errors that can occur when setting block filters in `FilterBlockOption`.
244#[derive(Debug, PartialEq, Eq, thiserror::Error)]
245pub enum FilterBlockError {
246    /// Error indicating that the `from_block` is greater than the `to_block`.
247    #[error("`from_block` ({from}) is greater than `to_block` ({to})")]
248    FromBlockGreaterThanToBlock {
249        /// The starting block number, which is greater than `to`.
250        from: u64,
251        /// The ending block number, which is less than `from`.
252        to: u64,
253    },
254}
255
256/// Represents the target range of blocks for the filter
257#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
258pub enum FilterBlockOption {
259    /// Represents a range of blocks with optional from and to blocks
260    ///
261    /// Note: ranges are considered to be __inclusive__
262    Range {
263        /// The block number or tag this filter should start at.
264        from_block: Option<BlockNumberOrTag>,
265        /// The block number or that this filter should end at.
266        to_block: Option<BlockNumberOrTag>,
267    },
268    /// The hash of the block if the filter only targets a single block
269    AtBlockHash(BlockHash),
270}
271
272impl FilterBlockOption {
273    /// Returns the `from_block` value, if any
274    pub const fn get_to_block(&self) -> Option<&BlockNumberOrTag> {
275        match self {
276            Self::Range { to_block, .. } => to_block.as_ref(),
277            Self::AtBlockHash(_) => None,
278        }
279    }
280
281    /// Returns the `to_block` value, if any
282    pub const fn get_from_block(&self) -> Option<&BlockNumberOrTag> {
283        match self {
284            Self::Range { from_block, .. } => from_block.as_ref(),
285            Self::AtBlockHash(_) => None,
286        }
287    }
288
289    /// Returns the range (`from_block`, `to_block`) if this is a range filter.
290    pub const fn as_range(&self) -> (Option<&BlockNumberOrTag>, Option<&BlockNumberOrTag>) {
291        match self {
292            Self::Range { from_block, to_block } => (from_block.as_ref(), to_block.as_ref()),
293            Self::AtBlockHash(_) => (None, None),
294        }
295    }
296
297    /// Returns the block hash if this is a block hash filter.
298    pub const fn as_block_hash(&self) -> Option<&BlockHash> {
299        match self {
300            Self::AtBlockHash(hash) => Some(hash),
301            Self::Range { .. } => None,
302        }
303    }
304
305    /// Returns true if this is a range filter.
306    pub const fn is_range(&self) -> bool {
307        matches!(self, Self::Range { .. })
308    }
309
310    /// Returns true if this is a block hash filter.
311    pub const fn is_block_hash(&self) -> bool {
312        matches!(self, Self::AtBlockHash(_))
313    }
314
315    /// Ensure block range validity
316    pub fn ensure_valid_block_range(&self) -> Result<(), FilterBlockError> {
317        // Check if from_block is greater than to_block
318        if let (Some(from), Some(to)) = (
319            self.get_from_block().and_then(|from| from.as_number()),
320            self.get_to_block().and_then(|to| to.as_number()),
321        ) {
322            if from > to {
323                return Err(FilterBlockError::FromBlockGreaterThanToBlock { from, to });
324            }
325        }
326        Ok(())
327    }
328
329    /// Sets the block number this range filter should start at.
330    #[must_use]
331    pub const fn with_from_block(&self, block: BlockNumberOrTag) -> Self {
332        Self::Range { from_block: Some(block), to_block: self.get_to_block().copied() }
333    }
334
335    /// Sets the block number this range filter should end at.
336    #[must_use]
337    pub const fn with_to_block(&self, block: BlockNumberOrTag) -> Self {
338        Self::Range { from_block: self.get_from_block().copied(), to_block: Some(block) }
339    }
340
341    /// Pins the block hash this filter should target.
342    #[must_use]
343    pub const fn with_block_hash(&self, hash: B256) -> Self {
344        Self::AtBlockHash(hash)
345    }
346}
347
348impl From<BlockNumberOrTag> for FilterBlockOption {
349    fn from(block: BlockNumberOrTag) -> Self {
350        let block = Some(block);
351        Self::Range { from_block: block, to_block: block }
352    }
353}
354
355impl From<U64> for FilterBlockOption {
356    fn from(block: U64) -> Self {
357        BlockNumberOrTag::from(block).into()
358    }
359}
360
361impl From<u64> for FilterBlockOption {
362    fn from(block: u64) -> Self {
363        BlockNumberOrTag::from(block).into()
364    }
365}
366
367impl<T: Into<BlockNumberOrTag>> From<RangeInclusive<T>> for FilterBlockOption {
368    fn from(r: RangeInclusive<T>) -> Self {
369        let (start, end) = r.into_inner();
370        let from_block = Some(start.into());
371        let to_block = Some(end.into());
372        Self::Range { from_block, to_block }
373    }
374}
375
376impl<T: Into<BlockNumberOrTag>> From<RangeToInclusive<T>> for FilterBlockOption {
377    fn from(r: RangeToInclusive<T>) -> Self {
378        let to_block = Some(r.end.into());
379        Self::Range { from_block: Some(BlockNumberOrTag::Earliest), to_block }
380    }
381}
382
383impl<T: Into<BlockNumberOrTag>> From<RangeFrom<T>> for FilterBlockOption {
384    fn from(r: RangeFrom<T>) -> Self {
385        let from_block = Some(r.start.into());
386        Self::Range { from_block, to_block: Some(BlockNumberOrTag::Latest) }
387    }
388}
389
390impl From<B256> for FilterBlockOption {
391    fn from(hash: B256) -> Self {
392        Self::AtBlockHash(hash)
393    }
394}
395
396impl Default for FilterBlockOption {
397    fn default() -> Self {
398        Self::Range { from_block: None, to_block: None }
399    }
400}
401
402/// Filter for logs.
403#[derive(Clone, Debug, Default, PartialEq, Eq, Hash)]
404pub struct Filter {
405    /// Filter block options, specifying on which blocks the filter should match.
406    // https://eips.ethereum.org/EIPS/eip-234
407    pub block_option: FilterBlockOption,
408    /// A filter set for matching contract addresses in log queries.
409    ///
410    /// This field determines which contract addresses the filter applies to. It supports:
411    /// - A single address to match logs from that address only.
412    /// - Multiple addresses to match logs from any of them.
413    ///
414    /// ## Notes:
415    /// - An empty array (`[]`) may result in no logs being returned.
416    /// - Some RPC providers handle empty arrays differently than `None`.
417    /// - Large address lists may affect performance or hit provider limits.
418    pub address: FilterSet<Address>,
419    /// Topics (maximum of 4)
420    pub topics: [Topic; 4],
421}
422
423impl Filter {
424    /// Creates a new, empty filter
425    pub fn new() -> Self {
426        Self::default()
427    }
428
429    /// Sets the inner filter object
430    ///
431    /// *NOTE:* ranges are always inclusive
432    ///
433    /// # Examples
434    ///
435    /// Match only a specific block
436    ///
437    /// ```rust
438    /// # use alloy_rpc_types_eth::Filter;
439    /// # fn main() {
440    /// let filter = Filter::new().select(69u64);
441    /// # }
442    /// ```
443    /// This is the same as `Filter::new().from_block(1337u64).to_block(1337u64)`
444    ///
445    /// Match the latest block only
446    ///
447    /// ```rust
448    /// # use alloy_rpc_types_eth::BlockNumberOrTag;
449    /// # use alloy_rpc_types_eth::Filter;
450    /// # fn main() {
451    /// let filter = Filter::new().select(BlockNumberOrTag::Latest);
452    /// # }
453    /// ```
454    ///
455    /// Match a block by its hash
456    ///
457    /// ```rust
458    /// # use alloy_primitives::B256;
459    /// # use alloy_rpc_types_eth::Filter;
460    /// # fn main() {
461    /// let filter = Filter::new().select(B256::ZERO);
462    /// # }
463    /// ```
464    /// This is the same as `at_block_hash`
465    ///
466    /// Match a range of blocks
467    ///
468    /// ```rust
469    /// # use alloy_rpc_types_eth::Filter;
470    /// # fn main() {
471    /// let filter = Filter::new().select(0u64..=100u64);
472    /// # }
473    /// ```
474    ///
475    /// Match all blocks in range `(1337..BlockNumberOrTag::Latest)`
476    ///
477    /// ```rust
478    /// # use alloy_rpc_types_eth::Filter;
479    /// # fn main() {
480    /// let filter = Filter::new().select(1337u64..);
481    /// # }
482    /// ```
483    ///
484    /// Match all blocks in range `(BlockNumberOrTag::Earliest..1337)`
485    ///
486    /// ```rust
487    /// # use alloy_rpc_types_eth::Filter;
488    /// # fn main() {
489    /// let filter = Filter::new().select(..=1337u64);
490    /// # }
491    /// ```
492    #[must_use]
493    pub fn select(mut self, filter: impl Into<FilterBlockOption>) -> Self {
494        self.block_option = filter.into();
495        self
496    }
497
498    /// Sets the from block number
499    #[must_use]
500    pub fn from_block<T: Into<BlockNumberOrTag>>(mut self, block: T) -> Self {
501        self.block_option = self.block_option.with_from_block(block.into());
502        self
503    }
504
505    /// Sets the to block number
506    #[must_use]
507    pub fn to_block<T: Into<BlockNumberOrTag>>(mut self, block: T) -> Self {
508        self.block_option = self.block_option.with_to_block(block.into());
509        self
510    }
511
512    /// Return `true` if filter configured to match pending block.
513    /// This means that both from_block and to_block are set to the pending tag.
514    pub fn is_pending_block_filter(&self) -> bool {
515        self.block_option.get_from_block().is_some_and(BlockNumberOrTag::is_pending)
516            && self.block_option.get_to_block().is_some_and(BlockNumberOrTag::is_pending)
517    }
518
519    /// Pins the block hash for the filter
520    #[must_use]
521    pub fn at_block_hash<T: Into<B256>>(mut self, hash: T) -> Self {
522        self.block_option = self.block_option.with_block_hash(hash.into());
523        self
524    }
525
526    /// Sets the address to query with this filter.
527    ///
528    /// # Examples
529    ///
530    /// Match only a specific address `("0xAc4b3DacB91461209Ae9d41EC517c2B9Cb1B7DAF")`
531    ///
532    /// ```rust
533    /// # use alloy_primitives::Address;
534    /// # use alloy_rpc_types_eth::Filter;
535    /// # fn main() {
536    /// let filter = Filter::new()
537    ///     .address("0xAc4b3DacB91461209Ae9d41EC517c2B9Cb1B7DAF".parse::<Address>().unwrap());
538    /// # }
539    /// ```
540    ///
541    /// Match all addresses in array `(vec!["0xAc4b3DacB91461209Ae9d41EC517c2B9Cb1B7DAF",
542    /// "0x8ad599c3A0ff1De082011EFDDc58f1908eb6e6D8"])`
543    ///
544    /// ```rust
545    /// # use alloy_primitives::Address;
546    /// # use alloy_rpc_types_eth::Filter;
547    /// # fn main() {
548    /// let addresses = vec![
549    ///     "0xAc4b3DacB91461209Ae9d41EC517c2B9Cb1B7DAF".parse::<Address>().unwrap(),
550    ///     "0x8ad599c3A0ff1De082011EFDDc58f1908eb6e6D8".parse::<Address>().unwrap(),
551    /// ];
552    /// let filter = Filter::new().address(addresses);
553    /// # }
554    /// ```
555    #[must_use]
556    pub fn address<T: Into<ValueOrArray<Address>>>(mut self, address: T) -> Self {
557        self.address = address.into().into();
558        self
559    }
560
561    /// Given the event signature in string form, it hashes it and adds it to the topics to monitor
562    #[must_use]
563    pub fn event(self, event_name: &str) -> Self {
564        let hash = keccak256(event_name.as_bytes());
565        self.event_signature(hash)
566    }
567
568    /// Hashes all event signatures and sets them as array to event_signature(topic0)
569    #[must_use]
570    pub fn events(self, events: impl IntoIterator<Item = impl AsRef<[u8]>>) -> Self {
571        let events = events.into_iter().map(|e| keccak256(e.as_ref())).collect::<Vec<_>>();
572        self.event_signature(events)
573    }
574
575    /// Sets event_signature(topic0) (the event name for non-anonymous events)
576    #[must_use]
577    pub fn event_signature<T: Into<Topic>>(mut self, topic: T) -> Self {
578        self.topics[0] = topic.into();
579        self
580    }
581
582    /// Sets topic0 (the event name for non-anonymous events)
583    #[must_use]
584    #[deprecated(note = "use `event_signature` instead")]
585    pub fn topic0<T: Into<Topic>>(mut self, topic: T) -> Self {
586        self.topics[0] = topic.into();
587        self
588    }
589
590    /// Sets the 1st indexed topic
591    #[must_use]
592    pub fn topic1<T: Into<Topic>>(mut self, topic: T) -> Self {
593        self.topics[1] = topic.into();
594        self
595    }
596
597    /// Sets the 2nd indexed topic
598    #[must_use]
599    pub fn topic2<T: Into<Topic>>(mut self, topic: T) -> Self {
600        self.topics[2] = topic.into();
601        self
602    }
603
604    /// Sets the 3rd indexed topic
605    #[must_use]
606    pub fn topic3<T: Into<Topic>>(mut self, topic: T) -> Self {
607        self.topics[3] = topic.into();
608        self
609    }
610
611    /// Returns true if this is a range filter and has a from block
612    pub fn is_paginatable(&self) -> bool {
613        self.get_from_block().is_some()
614    }
615
616    /// Returns the numeric value of the `toBlock` field
617    pub fn get_to_block(&self) -> Option<u64> {
618        self.block_option.get_to_block().and_then(|b| b.as_number())
619    }
620
621    /// Returns the numeric value of the `fromBlock` field
622    pub fn get_from_block(&self) -> Option<u64> {
623        self.block_option.get_from_block().and_then(|b| b.as_number())
624    }
625
626    /// Returns the numeric value of the `fromBlock` field
627    pub const fn get_block_hash(&self) -> Option<B256> {
628        match self.block_option {
629            FilterBlockOption::AtBlockHash(hash) => Some(hash),
630            FilterBlockOption::Range { .. } => None,
631        }
632    }
633
634    /// Returns `true` if at least one topic is set
635    pub fn has_topics(&self) -> bool {
636        self.topics.iter().any(|t| !t.is_empty())
637    }
638
639    /// Create the [`BloomFilter`] for the addresses.
640    pub fn address_bloom_filter(&self) -> Cow<'_, BloomFilter> {
641        self.address.bloom_filter()
642    }
643
644    /// Create a [`BloomFilter`] for each topic filter.
645    pub fn topics_bloom_filter(&self) -> [Cow<'_, BloomFilter>; 4] {
646        self.topics.each_ref().map(|t| t.bloom_filter())
647    }
648
649    /// Check whether the provided bloom contains all topics and the address we
650    /// wish to filter on.
651    pub fn matches_bloom(&self, bloom: Bloom) -> bool {
652        self.address_bloom_filter().matches(bloom)
653            && self.topics_bloom_filter().iter().all(|topic_bloom| topic_bloom.matches(bloom))
654    }
655
656    /// Returns `true` if the filter matches the given topics.
657    pub fn matches_topics(&self, topics: &[B256]) -> bool {
658        self.topics.iter().zip_longest(topics.iter()).all(|topic| match topic {
659            Both(filter, log) => filter.matches(log),
660            Left(filter) => filter.is_empty(),
661            Right(_) => false,
662        })
663    }
664
665    /// Returns `true` if the filter matches the given address.
666    pub fn matches_address(&self, address: Address) -> bool {
667        self.address.matches(&address)
668    }
669
670    /// Returns `true` if the block matches the filter.
671    pub const fn matches_block_range(&self, block_number: u64) -> bool {
672        let mut res = true;
673
674        if let Some(BlockNumberOrTag::Number(num)) = self.block_option.get_from_block() {
675            if *num > block_number {
676                res = false;
677            }
678        }
679
680        if let Some(to) = self.block_option.get_to_block() {
681            match to {
682                BlockNumberOrTag::Number(num) => {
683                    if *num < block_number {
684                        res = false;
685                    }
686                }
687                BlockNumberOrTag::Earliest => {
688                    res = false;
689                }
690                _ => {}
691            }
692        }
693        res
694    }
695
696    /// Returns `true` if the filter matches the given block hash.
697    pub fn matches_block_hash(&self, block_hash: B256) -> bool {
698        match self.block_option {
699            FilterBlockOption::AtBlockHash(hash) => hash == block_hash,
700            FilterBlockOption::Range { .. } => false,
701        }
702    }
703
704    /// Returns `true` if the filter matches the given block. Checks both the
705    /// block number and hash.
706    pub fn matches_block(&self, block: &BlockNumHash) -> bool {
707        self.matches_block_range(block.number) || self.matches_block_hash(block.hash)
708    }
709
710    /// Returns `true` if either of the following is true:
711    /// - the filter and log are both pending
712    /// - the filter matches the block in the log. I.e. [`Self::matches_block`] returns true when
713    ///   called with the block number and hash from the log.
714    pub fn matches_log_block<T>(&self, log: &crate::Log<T>) -> bool {
715        if self.is_pending_block_filter() {
716            // We skip checking block_hash, as a mismatch here would indicate
717            // invalid log data
718            return log.block_number.is_none();
719        }
720
721        // If the data is invalid, we'll shortcut return false
722        let Some(number) = log.block_number else { return false };
723        let Some(hash) = log.block_hash else { return false };
724        let num_hash = BlockNumHash { number, hash };
725
726        self.matches_block(&num_hash)
727    }
728
729    /// Check if a [`Log`] matches the filter. This will check topics and
730    /// address.
731    ///
732    /// This checks [`Log<LogData>`], the raw, primitive type carrying un-parsed
733    /// [`LogData`].
734    ///
735    /// - For un-parsed RPC logs [`crate::Log<LogData>`], see [`Self::rpc_matches`] and
736    ///   [`Self::rpc_matches_parsed`].
737    /// - For parsed [`Log`]s (e.g. those returned by a contract), see [`Self::matches_parsed`].
738    pub fn matches(&self, log: &Log) -> bool {
739        if !self.matches_address(log.address) {
740            return false;
741        }
742
743        self.matches_topics(log.topics())
744    }
745
746    /// Check if a [`crate::Log`] matches the filter. This will check topics,
747    /// address, and block option.
748    ///
749    /// This function checks [`crate::Log<LogData>`], the RPC type carrying
750    /// un-parsed [`LogData`].
751    ///
752    /// - For parsed [`Log<T>`]s (e.g. those returned by a contract), see [`Self::matches_parsed`].
753    /// - For parsed [`crate::Log<T>`]s (e.g. those returned by a contract), see
754    ///   [`Self::rpc_matches`].
755    pub fn rpc_matches(&self, log: &crate::Log) -> bool {
756        self.matches_log_block(log) && self.matches(&log.inner)
757    }
758
759    /// Check if a parsed [`Log<T>`] matches the filter. This will check
760    /// topics and address.
761    ///
762    /// This function checks [`Log<T>`], the primitive `Log` type carrying
763    /// some parsed `T`, usually implementing [`SolEvent`].
764    ///
765    /// - For un-parsed [`Log<LogData>`] see [`Self::matches`].
766    /// - For un-parsed RPC logs [`crate::Log<LogData>`] see [`Self::rpc_matches`].
767    /// - For parsed RPC [`crate::Log<T>`]s (e.g. those returned by a contract), see
768    ///   [`Self::rpc_matches_parsed`].
769    ///
770    /// [`SolEvent`]: alloy_sol_types::SolEvent
771    pub fn matches_parsed<T, U>(&self, log: &T) -> bool
772    where
773        T: AsRef<Log<U>>,
774        for<'a> &'a U: Into<LogData>,
775    {
776        let log = log.as_ref().reserialize();
777        self.matches(&log)
778    }
779
780    /// Check if a parsed rpc log [`crate::Log<T>`] matches the filter. This
781    /// will check topics, address, and block option.
782    ///
783    /// If the RPC log block hash or number is `None` (indicating an uncled
784    /// block), this function will return `false`.
785    ///
786    /// This function checks [`crate::Log<T>`], the RPC type carrying some
787    /// parsed `T`, usually implementing [`SolEvent`].
788    ///
789    /// - For un-parsed [`Log<LogData>`] see [`Self::matches`].
790    /// - For parsed [`Log<T>`]s (e.g. those returned by a contract), see [`Self::matches_parsed`].
791    /// - For un-parsed RPC logs [`crate::Log<LogData>`] see [`Self::rpc_matches`].
792    ///
793    /// [`SolEvent`]: alloy_sol_types::SolEvent
794    pub fn rpc_matches_parsed<U>(&self, log: &crate::Log<U>) -> bool
795    where
796        for<'a> &'a U: Into<LogData>,
797    {
798        self.matches_log_block(log) && self.matches_parsed(log)
799    }
800
801    /// Appends logs matching the filter from a block's receipts.
802    ///
803    /// Iterates through receipts, filters logs, and appends them with
804    /// block metadata. Includes block number/hash matching.
805    ///
806    /// # Arguments
807    ///
808    /// * `all_logs` - Vector to append matching logs to
809    /// * `block_num_hash` - Block number and hash of the block
810    /// * `block_timestamp` - Block timestamp
811    /// * `tx_hashes_and_receipts` - Iterator of (transaction_hash, receipt) pairs
812    /// * `removed` - Whether logs are from a removed block (reorg)
813    pub fn append_matching_block_logs<'a, I, R>(
814        &self,
815        all_logs: &mut Vec<crate::Log>,
816        block_num_hash: BlockNumHash,
817        block_timestamp: u64,
818        tx_hashes_and_receipts: I,
819        removed: bool,
820    ) where
821        I: IntoIterator<Item = (B256, &'a R)>,
822        R: alloy_consensus::TxReceipt<Log = alloy_primitives::Log> + 'a,
823    {
824        // Early return if block doesn't match filter
825        if !self.matches_block(&block_num_hash) {
826            return;
827        }
828
829        // Tracks the index of a log in the entire block
830        let mut log_index: u64 = 0;
831
832        // Iterate over receipts and append matching logs
833        for (receipt_idx, (tx_hash, receipt)) in tx_hashes_and_receipts.into_iter().enumerate() {
834            for log in receipt.logs() {
835                if self.matches(log) {
836                    let log = crate::Log {
837                        inner: log.clone(),
838                        block_hash: Some(block_num_hash.hash),
839                        block_number: Some(block_num_hash.number),
840                        transaction_hash: Some(tx_hash),
841                        // The transaction and receipt index is always the same
842                        transaction_index: Some(receipt_idx as u64),
843                        log_index: Some(log_index),
844                        removed,
845                        block_timestamp: Some(block_timestamp),
846                    };
847                    all_logs.push(log);
848                }
849
850                log_index += 1;
851            }
852        }
853    }
854
855    /// Returns matching logs from a block's receipts grouped by transaction hashes.
856    ///
857    /// # Arguments
858    ///
859    /// * `block_num_hash` - Block number and hash of the block
860    /// * `block_timestamp` - Block timestamp
861    /// * `tx_hashes_and_receipts` - Iterator of (transaction_hash, receipt) pairs
862    /// * `removed` - Whether logs are from a removed block (reorg)
863    pub fn matching_block_logs<'a, I, R>(
864        &self,
865        block_num_hash: BlockNumHash,
866        block_timestamp: u64,
867        tx_hashes_and_receipts: I,
868        removed: bool,
869    ) -> Vec<crate::Log>
870    where
871        I: IntoIterator<Item = (B256, &'a R)>,
872        R: alloy_consensus::TxReceipt<Log = alloy_primitives::Log> + 'a,
873    {
874        let mut logs = Vec::new();
875        self.append_matching_block_logs(
876            &mut logs,
877            block_num_hash,
878            block_timestamp,
879            tx_hashes_and_receipts,
880            removed,
881        );
882        logs
883    }
884
885    /// Creates an iterator that filters receipts for matching logs.
886    ///
887    /// This method takes an iterator of blocks (where each block is an iterator of receipts)
888    /// and returns an iterator that yields all logs matching this filter.
889    ///
890    /// # Example
891    ///
892    /// ```no_run
893    /// # use alloy_rpc_types_eth::Filter;
894    /// # use alloy_consensus::Receipt;
895    /// # use alloy_primitives::{Address, Log, B256};
896    /// # fn example(receipts: Vec<Vec<Receipt>>) {
897    /// let filter = Filter::new()
898    ///     .address("0x1234...".parse::<Address>().unwrap())
899    ///     .event_signature(B256::from([0x01; 32]));
900    ///
901    /// let logs: Vec<Log> = filter.filter_receipts(receipts).collect();
902    /// # }
903    /// ```
904    pub fn filter_receipts<I, R>(&self, receipts: I) -> FilterReceiptsIter<'_, I::IntoIter, R>
905    where
906        I: IntoIterator,
907        I::Item: IntoIterator<Item = R>,
908        R: alloy_consensus::TxReceipt<Log = alloy_primitives::Log>,
909    {
910        FilterReceiptsIter {
911            filter: self,
912            blocks_iter: receipts.into_iter(),
913            current_block: None,
914            current_logs: None,
915        }
916    }
917}
918
919#[cfg(feature = "serde")]
920impl serde::Serialize for Filter {
921    fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
922    where
923        S: serde::Serializer,
924    {
925        use serde::ser::SerializeStruct;
926
927        let mut s = serializer.serialize_struct("Filter", 5)?;
928        match self.block_option {
929            FilterBlockOption::Range { from_block, to_block } => {
930                if let Some(ref from_block) = from_block {
931                    s.serialize_field("fromBlock", from_block)?;
932                }
933
934                if let Some(ref to_block) = to_block {
935                    s.serialize_field("toBlock", to_block)?;
936                }
937            }
938
939            FilterBlockOption::AtBlockHash(ref h) => s.serialize_field("blockHash", h)?,
940        }
941
942        if let Some(address) = self.address.to_value_or_array() {
943            s.serialize_field("address", &address)?;
944        }
945
946        let mut filtered_topics = Vec::new();
947        let mut filtered_topics_len = 0;
948        for (i, topic) in self.topics.iter().enumerate() {
949            if !topic.is_empty() {
950                filtered_topics_len = i + 1;
951            }
952            filtered_topics.push(topic.to_value_or_array());
953        }
954        filtered_topics.truncate(filtered_topics_len);
955        s.serialize_field("topics", &filtered_topics)?;
956
957        s.end()
958    }
959}
960
961#[cfg(feature = "serde")]
962impl<'de> serde::Deserialize<'de> for Filter {
963    fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
964    where
965        D: serde::Deserializer<'de>,
966    {
967        type RawAddressFilter = ValueOrArray<Option<Address>>;
968        type RawTopicsFilter = Vec<Option<ValueOrArray<Option<B256>>>>;
969
970        struct FilterVisitor;
971
972        impl<'de> serde::de::Visitor<'de> for FilterVisitor {
973            type Value = Filter;
974
975            fn expecting(&self, formatter: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
976                formatter.write_str("Filter object")
977            }
978
979            fn visit_map<A>(self, mut map: A) -> Result<Self::Value, A::Error>
980            where
981                A: serde::de::MapAccess<'de>,
982            {
983                let mut from_block: Option<Option<BlockNumberOrTag>> = None;
984                let mut to_block: Option<Option<BlockNumberOrTag>> = None;
985                let mut block_hash: Option<Option<B256>> = None;
986                let mut address: Option<Option<RawAddressFilter>> = None;
987                let mut topics: Option<Option<RawTopicsFilter>> = None;
988
989                while let Some(key) = map.next_key::<String>()? {
990                    match key.as_str() {
991                        "fromBlock" => {
992                            if from_block.is_some() {
993                                return Err(serde::de::Error::duplicate_field("fromBlock"));
994                            }
995                            from_block = Some(map.next_value()?)
996                        }
997                        "toBlock" => {
998                            if to_block.is_some() {
999                                return Err(serde::de::Error::duplicate_field("toBlock"));
1000                            }
1001                            to_block = Some(map.next_value()?)
1002                        }
1003                        "blockHash" => {
1004                            if block_hash.is_some() {
1005                                return Err(serde::de::Error::duplicate_field("blockHash"));
1006                            }
1007                            block_hash = Some(map.next_value()?)
1008                        }
1009                        "address" => {
1010                            if address.is_some() {
1011                                return Err(serde::de::Error::duplicate_field("address"));
1012                            }
1013                            address = Some(map.next_value()?)
1014                        }
1015                        "topics" => {
1016                            if topics.is_some() {
1017                                return Err(serde::de::Error::duplicate_field("topics"));
1018                            }
1019                            topics = Some(map.next_value()?)
1020                        }
1021
1022                        key => {
1023                            return Err(serde::de::Error::unknown_field(
1024                                key,
1025                                &["fromBlock", "toBlock", "address", "topics", "blockHash"],
1026                            ))
1027                        }
1028                    }
1029                }
1030
1031                // conflict check between block_hash and from_block/to_block
1032                let (block_hash, from_block, to_block) = if let Some(Some(hash)) = block_hash {
1033                    if from_block.is_some_and(|inner| inner.is_some())
1034                        || to_block.is_some_and(|inner| inner.is_some())
1035                    {
1036                        return Err(serde::de::Error::custom(
1037                            "cannot specify both blockHash and fromBlock/toBlock, choose one or the other",
1038                        ));
1039                    }
1040                    (Some(hash), None, None)
1041                } else {
1042                    (None, from_block.unwrap_or_default(), to_block.unwrap_or_default())
1043                };
1044
1045                let address = address.flatten().map(|a| a.into()).unwrap_or_default();
1046                let topics_vec = topics.flatten().unwrap_or_default();
1047
1048                // maximum allowed filter len
1049                if topics_vec.len() > 4 {
1050                    return Err(serde::de::Error::custom("exceeded maximum topics len"));
1051                }
1052                let mut topics: [Topic; 4] = [
1053                    Default::default(),
1054                    Default::default(),
1055                    Default::default(),
1056                    Default::default(),
1057                ];
1058                for (idx, topic) in topics_vec.into_iter().enumerate() {
1059                    topics[idx] = topic.map(|t| t.into()).unwrap_or_default();
1060                }
1061
1062                let block_option = block_hash
1063                    .map_or(FilterBlockOption::Range { from_block, to_block }, |block_hash| {
1064                        FilterBlockOption::AtBlockHash(block_hash)
1065                    });
1066
1067                Ok(Filter { block_option, address, topics })
1068            }
1069        }
1070
1071        deserializer.deserialize_any(FilterVisitor)
1072    }
1073}
1074
1075/// Union type for representing a single value or a vector of values inside a
1076/// filter.
1077#[derive(Clone, Debug, PartialEq, Eq, Hash)]
1078pub enum ValueOrArray<T> {
1079    /// A single value
1080    Value(T),
1081    /// A vector of values
1082    Array(Vec<T>),
1083}
1084
1085impl<T> ValueOrArray<T> {
1086    /// Get the value if present.
1087    pub const fn as_value(&self) -> Option<&T> {
1088        if let Self::Value(value) = self {
1089            Some(value)
1090        } else {
1091            None
1092        }
1093    }
1094
1095    /// Get the array if present.
1096    pub fn as_array(&self) -> Option<&[T]> {
1097        if let Self::Array(array) = self {
1098            Some(array)
1099        } else {
1100            None
1101        }
1102    }
1103
1104    /// Check if the enum is a single value.
1105    pub const fn is_value(&self) -> bool {
1106        matches!(self, Self::Value(_))
1107    }
1108
1109    /// Check if the enum is an array.
1110    pub const fn is_array(&self) -> bool {
1111        matches!(self, Self::Array(_))
1112    }
1113}
1114
1115impl From<Address> for ValueOrArray<Address> {
1116    fn from(src: Address) -> Self {
1117        Self::Value(src)
1118    }
1119}
1120
1121impl From<Vec<Address>> for ValueOrArray<Address> {
1122    fn from(src: Vec<Address>) -> Self {
1123        Self::Array(src)
1124    }
1125}
1126
1127impl From<Vec<B256>> for ValueOrArray<B256> {
1128    fn from(src: Vec<B256>) -> Self {
1129        Self::Array(src)
1130    }
1131}
1132
1133#[cfg(feature = "serde")]
1134impl<T> serde::Serialize for ValueOrArray<T>
1135where
1136    T: serde::Serialize,
1137{
1138    fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
1139    where
1140        S: serde::Serializer,
1141    {
1142        match self {
1143            Self::Value(inner) => inner.serialize(serializer),
1144            Self::Array(inner) => inner.serialize(serializer),
1145        }
1146    }
1147}
1148
1149#[cfg(feature = "serde")]
1150impl<'a, T> serde::Deserialize<'a> for ValueOrArray<T>
1151where
1152    T: serde::de::DeserializeOwned,
1153{
1154    fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
1155    where
1156        D: serde::Deserializer<'a>,
1157    {
1158        let value = serde_json::Value::deserialize(deserializer)?;
1159
1160        if value.is_null() {
1161            return Ok(Self::Array(Vec::new()));
1162        }
1163
1164        #[derive(serde::Deserialize)]
1165        #[serde(untagged)]
1166        enum Variadic<T> {
1167            Value(T),
1168            Array(Vec<T>),
1169        }
1170
1171        match serde_json::from_value::<Variadic<T>>(value).map_err(|err| {
1172            serde::de::Error::custom(format!("Invalid variadic value or array type: {err}"))
1173        })? {
1174            Variadic::Value(val) => Ok(Self::Value(val)),
1175            Variadic::Array(arr) => Ok(Self::Array(arr)),
1176        }
1177    }
1178}
1179
1180/// Response of the `eth_getFilterChanges` RPC.
1181#[derive(Default, Clone, Debug, PartialEq, Eq)]
1182#[cfg_attr(feature = "serde", derive(serde::Serialize))]
1183#[cfg_attr(feature = "serde", serde(untagged))]
1184pub enum FilterChanges<T = Transaction> {
1185    /// Empty result.
1186    #[cfg_attr(feature = "serde", serde(with = "empty_array"))]
1187    #[default]
1188    Empty,
1189    /// New logs.
1190    Logs(Vec<RpcLog>),
1191    /// New hashes (block or transactions).
1192    Hashes(Vec<B256>),
1193    /// New transactions.
1194    Transactions(Vec<T>),
1195}
1196
1197impl From<Vec<RpcLog>> for FilterChanges {
1198    fn from(logs: Vec<RpcLog>) -> Self {
1199        Self::Logs(logs)
1200    }
1201}
1202
1203impl From<Vec<B256>> for FilterChanges {
1204    fn from(hashes: Vec<B256>) -> Self {
1205        Self::Hashes(hashes)
1206    }
1207}
1208
1209impl From<Vec<Transaction>> for FilterChanges {
1210    fn from(transactions: Vec<Transaction>) -> Self {
1211        Self::Transactions(transactions)
1212    }
1213}
1214
1215impl<T> FilterChanges<T> {
1216    /// Get the hashes if present.
1217    pub fn as_hashes(&self) -> Option<&[B256]> {
1218        if let Self::Hashes(hashes) = self {
1219            Some(hashes)
1220        } else {
1221            None
1222        }
1223    }
1224
1225    /// Get the logs if present.
1226    pub fn as_logs(&self) -> Option<&[RpcLog]> {
1227        if let Self::Logs(logs) = self {
1228            Some(logs)
1229        } else {
1230            None
1231        }
1232    }
1233
1234    /// Get the transactions if present.
1235    pub fn as_transactions(&self) -> Option<&[T]> {
1236        if let Self::Transactions(transactions) = self {
1237            Some(transactions)
1238        } else {
1239            None
1240        }
1241    }
1242
1243    /// Check if the filter changes are empty.
1244    pub const fn is_empty(&self) -> bool {
1245        matches!(self, Self::Empty)
1246    }
1247
1248    /// Check if the filter changes contain logs.
1249    pub const fn is_logs(&self) -> bool {
1250        matches!(self, Self::Logs(_))
1251    }
1252
1253    /// Check if the filter changes contain hashes.
1254    pub const fn is_hashes(&self) -> bool {
1255        matches!(self, Self::Hashes(_))
1256    }
1257
1258    /// Check if the filter changes contain transactions.
1259    pub const fn is_transactions(&self) -> bool {
1260        matches!(self, Self::Transactions(_))
1261    }
1262}
1263
1264#[cfg(feature = "serde")]
1265mod empty_array {
1266    use serde::{Serialize, Serializer};
1267
1268    pub(super) fn serialize<S>(s: S) -> Result<S::Ok, S::Error>
1269    where
1270        S: Serializer,
1271    {
1272        (&[] as &[()]).serialize(s)
1273    }
1274}
1275
1276#[cfg(feature = "serde")]
1277impl<'de, T> serde::Deserialize<'de> for FilterChanges<T>
1278where
1279    T: serde::Deserialize<'de>,
1280{
1281    fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
1282    where
1283        D: serde::Deserializer<'de>,
1284    {
1285        #[derive(serde::Deserialize)]
1286        #[serde(untagged)]
1287        enum Changes<T = Transaction> {
1288            Hashes(Vec<B256>),
1289            Logs(Vec<RpcLog>),
1290            Transactions(Vec<T>),
1291        }
1292
1293        let changes = Changes::deserialize(deserializer)?;
1294        let changes = match changes {
1295            Changes::Logs(vals) => {
1296                if vals.is_empty() {
1297                    Self::Empty
1298                } else {
1299                    Self::Logs(vals)
1300                }
1301            }
1302            Changes::Hashes(vals) => {
1303                if vals.is_empty() {
1304                    Self::Empty
1305                } else {
1306                    Self::Hashes(vals)
1307                }
1308            }
1309            Changes::Transactions(vals) => {
1310                if vals.is_empty() {
1311                    Self::Empty
1312                } else {
1313                    Self::Transactions(vals)
1314                }
1315            }
1316        };
1317        Ok(changes)
1318    }
1319}
1320
1321/// Owned equivalent of a `SubscriptionId`
1322#[derive(Clone, Debug, PartialEq, Eq, Hash)]
1323#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
1324#[cfg_attr(feature = "serde", serde(deny_unknown_fields))]
1325#[cfg_attr(feature = "serde", serde(untagged))]
1326pub enum FilterId {
1327    /// Numeric id
1328    Num(u64),
1329    /// String id
1330    Str(String),
1331}
1332
1333impl From<u64> for FilterId {
1334    fn from(num: u64) -> Self {
1335        Self::Num(num)
1336    }
1337}
1338
1339impl From<String> for FilterId {
1340    fn from(str: String) -> Self {
1341        Self::Str(str)
1342    }
1343}
1344
1345#[cfg(feature = "jsonrpsee-types")]
1346impl From<FilterId> for jsonrpsee_types::SubscriptionId<'_> {
1347    fn from(value: FilterId) -> Self {
1348        match value {
1349            FilterId::Num(n) => jsonrpsee_types::SubscriptionId::Num(n),
1350            FilterId::Str(s) => jsonrpsee_types::SubscriptionId::Str(s.into()),
1351        }
1352    }
1353}
1354
1355#[cfg(feature = "jsonrpsee-types")]
1356impl From<jsonrpsee_types::SubscriptionId<'_>> for FilterId {
1357    fn from(value: jsonrpsee_types::SubscriptionId<'_>) -> Self {
1358        match value {
1359            jsonrpsee_types::SubscriptionId::Num(n) => n.into(),
1360            jsonrpsee_types::SubscriptionId::Str(s) => s.into_owned().into(),
1361        }
1362    }
1363}
1364
1365/// Specifies the kind of information you wish to receive from the `eth_newPendingTransactionFilter`
1366/// RPC endpoint.
1367///
1368/// When this type is used in a request, it determines whether the client wishes to receive:
1369/// - Only the transaction hashes (`Hashes` variant), or
1370/// - Full transaction details (`Full` variant).
1371#[derive(Clone, Copy, Debug, Default, PartialEq, Eq)]
1372pub enum PendingTransactionFilterKind {
1373    /// Receive only the hashes of the transactions.
1374    #[default]
1375    Hashes,
1376    /// Receive full details of the transactions.
1377    Full,
1378}
1379
1380#[cfg(feature = "serde")]
1381impl serde::Serialize for PendingTransactionFilterKind {
1382    /// Serializes the `PendingTransactionFilterKind` into a boolean value:
1383    /// - `false` for `Hashes`
1384    /// - `true` for `Full`
1385    fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
1386    where
1387        S: serde::Serializer,
1388    {
1389        match self {
1390            Self::Hashes => false.serialize(serializer),
1391            Self::Full => true.serialize(serializer),
1392        }
1393    }
1394}
1395
1396#[cfg(feature = "serde")]
1397impl<'a> serde::Deserialize<'a> for PendingTransactionFilterKind {
1398    /// Deserializes a boolean value into `PendingTransactionFilterKind`:
1399    /// - `false` becomes `Hashes`
1400    /// - `true` becomes `Full`
1401    fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
1402    where
1403        D: serde::Deserializer<'a>,
1404    {
1405        let val = Option::<bool>::deserialize(deserializer)?;
1406        match val {
1407            Some(true) => Ok(Self::Full),
1408            _ => Ok(Self::Hashes),
1409        }
1410    }
1411}
1412
1413/// Helper type to represent a bloom filter used for matching logs.
1414#[derive(Debug, Default, Clone, PartialEq, Eq)]
1415pub struct BloomFilter(Vec<Bloom>);
1416
1417impl From<Vec<Bloom>> for BloomFilter {
1418    fn from(src: Vec<Bloom>) -> Self {
1419        Self(src)
1420    }
1421}
1422
1423impl BloomFilter {
1424    /// Returns whether the given bloom matches the list of Blooms in the current filter.
1425    /// If the filter is empty (the list is empty), then any bloom matches
1426    /// Otherwise, there must be at least one match for the BloomFilter to match.
1427    pub fn matches(&self, bloom: Bloom) -> bool {
1428        self.0.is_empty() || self.0.iter().any(|a| bloom.contains(a))
1429    }
1430}
1431
1432/// Support for matching [Filter]s
1433#[derive(Debug, Default)]
1434pub struct FilteredParams {
1435    /// The original filter, if any
1436    pub filter: Option<Filter>,
1437}
1438
1439impl FilteredParams {
1440    /// Creates a new wrapper type for a [Filter], if any with flattened topics, that can be used
1441    /// for matching
1442    pub fn new(filter: Option<Filter>) -> Self {
1443        filter.map_or_else(Default::default, |filter| Self { filter: Some(filter) })
1444    }
1445
1446    /// Returns the [BloomFilter] for the given address
1447    pub fn address_filter(address: &FilterSet<Address>) -> BloomFilter {
1448        address.make_bloom_filter()
1449    }
1450
1451    /// Returns the [BloomFilter] for the given topics
1452    pub fn topics_filter(topics: &[FilterSet<B256>]) -> Vec<BloomFilter> {
1453        topics.iter().map(|t| t.make_bloom_filter()).collect()
1454    }
1455
1456    /// Returns `true` if the bloom matches the topics
1457    pub fn matches_topics(bloom: Bloom, topic_filters: &[BloomFilter]) -> bool {
1458        if topic_filters.is_empty() {
1459            return true;
1460        }
1461
1462        // for each filter, iterate through the list of filter blooms. for each set of filter
1463        // (each BloomFilter), the given `bloom` must match at least one of them, unless the list is
1464        // empty (no filters).
1465        for filter in topic_filters {
1466            if !filter.matches(bloom) {
1467                return false;
1468            }
1469        }
1470        true
1471    }
1472
1473    /// Returns `true` if the bloom contains one of the address blooms, or the address blooms
1474    /// list is empty (thus, no filters)
1475    pub fn matches_address(bloom: Bloom, address_filter: &BloomFilter) -> bool {
1476        address_filter.matches(bloom)
1477    }
1478
1479    /// Returns true if the filter matches the given block number
1480    pub const fn filter_block_range(&self, block_number: u64) -> bool {
1481        if self.filter.is_none() {
1482            return true;
1483        }
1484        let filter = self.filter.as_ref().unwrap();
1485        let mut res = true;
1486
1487        if let Some(BlockNumberOrTag::Number(num)) = filter.block_option.get_from_block() {
1488            if *num > block_number {
1489                res = false;
1490            }
1491        }
1492
1493        if let Some(to) = filter.block_option.get_to_block() {
1494            match to {
1495                BlockNumberOrTag::Number(num) => {
1496                    if *num < block_number {
1497                        res = false;
1498                    }
1499                }
1500                BlockNumberOrTag::Earliest => {
1501                    res = false;
1502                }
1503                _ => {}
1504            }
1505        }
1506        res
1507    }
1508
1509    /// Returns `true` if the filter matches the given block hash.
1510    pub fn filter_block_hash(&self, block_hash: B256) -> bool {
1511        if let Some(h) = self.filter.as_ref().and_then(|f| f.get_block_hash()) {
1512            if h != block_hash {
1513                return false;
1514            }
1515        }
1516        true
1517    }
1518
1519    /// Return `true` if the filter configured to match pending block.
1520    /// This means that both from_block and to_block are set to the pending tag.
1521    /// It calls [`Filter::is_pending_block_filter`] undercover.
1522    pub fn is_pending_block_filter(&self) -> bool {
1523        self.filter.as_ref().is_some_and(|f| f.is_pending_block_filter())
1524    }
1525
1526    /// Returns `true` if the filter matches the given address.
1527    pub fn filter_address(&self, address: &Address) -> bool {
1528        self.filter.as_ref().map(|f| f.address.matches(address)).unwrap_or(true)
1529    }
1530
1531    /// Returns `true` if the log matches the given topics
1532    pub fn filter_topics(&self, log_topics: &[B256]) -> bool {
1533        let topics = match self.filter.as_ref() {
1534            None => return true,
1535            Some(f) => &f.topics,
1536        };
1537        for topic_tuple in topics.iter().zip_longest(log_topics.iter()) {
1538            match topic_tuple {
1539                // We exhausted the `log.topics`, so if there's a filter set for
1540                // this topic index, there is no match. Otherwise (empty filter), continue.
1541                Left(filter_topic) => {
1542                    if !filter_topic.is_empty() {
1543                        return false;
1544                    }
1545                }
1546                // We exhausted the filter topics, therefore any subsequent log topic
1547                // will match.
1548                Right(_) => return true,
1549                // Check that `log_topic` is included in `filter_topic`
1550                Both(filter_topic, log_topic) => {
1551                    if !filter_topic.matches(log_topic) {
1552                        return false;
1553                    }
1554                }
1555            }
1556        }
1557        true
1558    }
1559}
1560
1561/// Iterator that yields logs from receipts that match a filter.
1562///
1563/// This iterator processes blocks of receipts, yielding all logs that match
1564/// the provided filter criteria.
1565pub struct FilterReceiptsIter<'a, I, R>
1566where
1567    I: Iterator,
1568    I::Item: IntoIterator<Item = R>,
1569    R: alloy_consensus::TxReceipt<Log = alloy_primitives::Log>,
1570{
1571    filter: &'a Filter,
1572    blocks_iter: I,
1573    current_block: Option<<I::Item as IntoIterator>::IntoIter>,
1574    current_logs: Option<alloc::vec::IntoIter<alloy_primitives::Log>>,
1575}
1576
1577impl<'a, I, R> core::fmt::Debug for FilterReceiptsIter<'a, I, R>
1578where
1579    I: Iterator,
1580    I::Item: IntoIterator<Item = R>,
1581    R: alloy_consensus::TxReceipt<Log = alloy_primitives::Log>,
1582{
1583    fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
1584        f.debug_struct("FilterReceiptsIter")
1585            .field("filter", &self.filter)
1586            .field("has_current_block", &self.current_block.is_some())
1587            .field("has_current_logs", &self.current_logs.is_some())
1588            .finish()
1589    }
1590}
1591
1592impl<'a, I, R> Iterator for FilterReceiptsIter<'a, I, R>
1593where
1594    I: Iterator,
1595    I::Item: IntoIterator<Item = R>,
1596    R: alloy_consensus::TxReceipt<Log = alloy_primitives::Log>,
1597{
1598    type Item = alloy_primitives::Log;
1599
1600    fn next(&mut self) -> Option<Self::Item> {
1601        loop {
1602            // First, try to return a log from current logs iterator
1603            if let Some(ref mut logs) = self.current_logs {
1604                if let Some(log) = logs.next() {
1605                    if self.filter.matches(&log) {
1606                        return Some(log);
1607                    }
1608                    continue;
1609                }
1610            }
1611
1612            // No more logs, try to get the next receipt
1613            if let Some(ref mut receipts) = self.current_block {
1614                if let Some(receipt) = receipts.next() {
1615                    // Create iterator from logs of this receipt
1616                    self.current_logs = Some(receipt.into_logs().into_iter());
1617                    continue;
1618                }
1619            }
1620
1621            // Current block exhausted or none set, try next block
1622            match self.blocks_iter.next() {
1623                Some(block) => {
1624                    self.current_block = Some(block.into_iter());
1625                    self.current_logs = None;
1626                }
1627                None => return None,
1628            }
1629        }
1630    }
1631}
1632
1633#[cfg(test)]
1634mod tests {
1635    use super::*;
1636    use alloy_primitives::{bloom, LogData};
1637    #[cfg(feature = "serde")]
1638    use serde_json::json;
1639    use similar_asserts::assert_eq;
1640
1641    #[cfg(feature = "serde")]
1642    fn serialize<T: serde::Serialize>(t: &T) -> serde_json::Value {
1643        serde_json::to_value(t).expect("Failed to serialize value")
1644    }
1645
1646    // <https://hoodi.etherscan.io/block/400001>
1647    #[test]
1648    #[cfg(feature = "serde")]
1649    fn test_any_addresses() {
1650        let s = r#"{
1651            "fromBlock": "0x61A80",
1652            "toBlock": "0x61B48",
1653            "address": [
1654                "0x8CBabC07717038DA6fAf1bC477a39F1627988a3a",
1655                "0x927F9c03d1Ac6e2630d31E614F226b5Ed028d443"
1656            ]
1657        }"#;
1658        let filter = serde_json::from_str::<Filter>(s).unwrap();
1659
1660        // <https://hoodi.etherscan.io/block/400001>
1661        let bloom = bloom!("0x10000000000010000000000000000200000002000000000000400000000000000000000400100000000900000000000000000000000000000000000000000000000000000000000000000008400000000000000080000000000080000000000000000000000000000000000000000000000000000002000000000010000000000000000000800000000000000000000000000000000000000020000000000000000000000000000000000000000000002000000000000000000000000000000000000002000000000000000000000000000000000000000000000000100000000000000000000000000000004000000000000000000000000000000000000000");
1662        assert!(filter.matches_bloom(bloom));
1663
1664        // <https://hoodi.etherscan.io/block/400002>
1665        let bloom = bloom!("0x10000000000010000000000000000200000002000000000000400000000000000000000400100000000900000000000000000000000000000000000000000000000000000000000000000008400000000000000080000000000080000000000000000000000000000000000000000000000000000002000000000010000000000000000000800000000000000000000000000000000000000020000000000000000000000000000000000000000000002000000000000000000000000000000000000002000000000000000000000000000000000000000000000000100000000000000000000000000000004000000000000000000000000000000000000000");
1666        assert!(filter.matches_bloom(bloom));
1667    }
1668
1669    #[test]
1670    #[cfg(feature = "serde")]
1671    fn test_empty_filter_topics_list() {
1672        let s = r#"{"fromBlock": "0xfc359e", "toBlock": "0xfc359e", "topics": [["0x8c5be1e5ebec7d5bd14f71427d1e84f3dd0314c0f7b2291e5b200ac8c7c3b925"], [], ["0x0000000000000000000000000c17e776cd218252adfca8d4e761d3fe757e9778"]]}"#;
1673        let filter = serde_json::from_str::<Filter>(s).unwrap();
1674        assert_eq!(
1675            filter.topics,
1676            [
1677                "0x8c5be1e5ebec7d5bd14f71427d1e84f3dd0314c0f7b2291e5b200ac8c7c3b925"
1678                    .parse::<B256>()
1679                    .unwrap()
1680                    .into(),
1681                Default::default(),
1682                "0x0000000000000000000000000c17e776cd218252adfca8d4e761d3fe757e9778"
1683                    .parse::<B256>()
1684                    .unwrap()
1685                    .into(),
1686                Default::default(),
1687            ]
1688        );
1689    }
1690
1691    #[test]
1692    fn test_with_from_block_correct_range() {
1693        // Test scenario where from_block is less than to_block
1694        let original = FilterBlockOption::Range {
1695            from_block: Some(BlockNumberOrTag::Number(1)),
1696            to_block: Some(BlockNumberOrTag::Number(10)),
1697        };
1698        let updated = original.with_from_block(BlockNumberOrTag::Number(5));
1699        assert!(updated.ensure_valid_block_range().is_ok());
1700    }
1701
1702    #[test]
1703    fn test_with_from_block_failure() {
1704        // Test scenario where from_block is greater than to_block
1705        let original = FilterBlockOption::Range {
1706            from_block: Some(BlockNumberOrTag::Number(10)),
1707            to_block: Some(BlockNumberOrTag::Number(5)),
1708        };
1709
1710        assert!(matches!(
1711            original.ensure_valid_block_range(),
1712            Err(FilterBlockError::FromBlockGreaterThanToBlock { .. })
1713        ));
1714    }
1715
1716    #[test]
1717    #[cfg(feature = "serde")]
1718    fn test_block_hash() {
1719        let s =
1720            r#"{"blockHash":"0x58dc57ab582b282c143424bd01e8d923cddfdcda9455bad02a29522f6274a948"}"#;
1721        let filter = serde_json::from_str::<Filter>(s).unwrap();
1722        assert_eq!(
1723            filter.block_option,
1724            FilterBlockOption::AtBlockHash(
1725                "0x58dc57ab582b282c143424bd01e8d923cddfdcda9455bad02a29522f6274a948"
1726                    .parse()
1727                    .unwrap()
1728            )
1729        );
1730    }
1731
1732    #[test]
1733    #[cfg(feature = "serde")]
1734    fn test_filter_topics_middle_wildcard() {
1735        let s = r#"{"fromBlock": "0xfc359e", "toBlock": "0xfc359e", "topics": [["0x8c5be1e5ebec7d5bd14f71427d1e84f3dd0314c0f7b2291e5b200ac8c7c3b925"], [], [null, "0x0000000000000000000000000c17e776cd218252adfca8d4e761d3fe757e9778"]]}"#;
1736        let filter = serde_json::from_str::<Filter>(s).unwrap();
1737        assert_eq!(
1738            filter.topics,
1739            [
1740                "0x8c5be1e5ebec7d5bd14f71427d1e84f3dd0314c0f7b2291e5b200ac8c7c3b925"
1741                    .parse::<B256>()
1742                    .unwrap()
1743                    .into(),
1744                Default::default(),
1745                Default::default(),
1746                Default::default(),
1747            ]
1748        );
1749    }
1750
1751    #[test]
1752    #[cfg(feature = "serde")]
1753    fn can_serde_value_or_array() {
1754        #[derive(Clone, Debug, PartialEq, Eq, serde::Serialize, serde::Deserialize)]
1755        struct Item {
1756            value: ValueOrArray<U256>,
1757        }
1758
1759        let item = Item { value: ValueOrArray::Value(U256::from(1u64)) };
1760        let json = serde_json::to_value(item.clone()).unwrap();
1761        let deserialized: Item = serde_json::from_value(json).unwrap();
1762        assert_eq!(item, deserialized);
1763
1764        let item = Item { value: ValueOrArray::Array(vec![U256::from(1u64), U256::ZERO]) };
1765        let json = serde_json::to_value(item.clone()).unwrap();
1766        let deserialized: Item = serde_json::from_value(json).unwrap();
1767        assert_eq!(item, deserialized);
1768    }
1769
1770    #[test]
1771    #[cfg(feature = "serde")]
1772    fn filter_serialization_test() {
1773        let t1 = "0000000000000000000000009729a6fbefefc8f6005933898b13dc45c3a2c8b7"
1774            .parse::<B256>()
1775            .unwrap();
1776        let t2 = B256::from([0; 32]);
1777        let t3 = U256::from(123);
1778
1779        let t1_padded = t1;
1780        let t3_padded = B256::from({
1781            let mut x = [0; 32];
1782            x[31] = 123;
1783            x
1784        });
1785
1786        let event = "ValueChanged(address,string,string)";
1787        let t0 = keccak256(event.as_bytes());
1788        let addr: Address = "f817796F60D268A36a57b8D2dF1B97B14C0D0E1d".parse().unwrap();
1789        let filter = Filter::new();
1790
1791        let ser = serialize(&filter);
1792        assert_eq!(ser, json!({ "topics": [] }));
1793
1794        let filter = filter.address(ValueOrArray::Value(addr));
1795
1796        let ser = serialize(&filter);
1797        assert_eq!(ser, json!({"address" : addr, "topics": []}));
1798
1799        let filter = filter.event(event);
1800
1801        // 0
1802        let ser = serialize(&filter);
1803        assert_eq!(ser, json!({ "address" : addr, "topics": [t0]}));
1804
1805        // 1
1806        let ser = serialize(&filter.clone().topic1(t1));
1807        assert_eq!(ser, json!({ "address" : addr, "topics": [t0, t1_padded]}));
1808
1809        // 2
1810        let ser = serialize(&filter.clone().topic2(t2));
1811        assert_eq!(ser, json!({ "address" : addr, "topics": [t0, null, t2]}));
1812
1813        // 3
1814        let ser = serialize(&filter.clone().topic3(t3));
1815        assert_eq!(ser, json!({ "address" : addr, "topics": [t0, null, null, t3_padded]}));
1816
1817        // 1 & 2
1818        let ser = serialize(&filter.clone().topic1(t1).topic2(t2));
1819        assert_eq!(ser, json!({ "address" : addr, "topics": [t0, t1_padded, t2]}));
1820
1821        // 1 & 3
1822        let ser = serialize(&filter.clone().topic1(t1).topic3(t3));
1823        assert_eq!(ser, json!({ "address" : addr, "topics": [t0, t1_padded, null, t3_padded]}));
1824
1825        // 2 & 3
1826        let ser = serialize(&filter.clone().topic2(t2).topic3(t3));
1827        assert_eq!(ser, json!({ "address" : addr, "topics": [t0, null, t2, t3_padded]}));
1828
1829        // 1 & 2 & 3
1830        let ser = serialize(&filter.topic1(t1).topic2(t2).topic3(t3));
1831        assert_eq!(ser, json!({ "address" : addr, "topics": [t0, t1_padded, t2, t3_padded]}));
1832    }
1833
1834    fn topic_filter(topic1: B256, topic2: B256, topic3: B256) -> Filter {
1835        Filter {
1836            block_option: Default::default(),
1837            address: Default::default(),
1838            topics: [
1839                topic1.into(),
1840                vec![topic2, topic3].into(),
1841                Default::default(),
1842                Default::default(),
1843            ],
1844        }
1845    }
1846
1847    #[test]
1848    fn can_detect_different_topics() {
1849        let topic1 = B256::with_last_byte(1);
1850        let topic2 = B256::with_last_byte(2);
1851        let topic3 = B256::with_last_byte(3);
1852
1853        let filter = topic_filter(topic1, topic2, topic3);
1854
1855        assert!(filter.matches_topics(&[topic1, topic2, topic3]));
1856    }
1857
1858    #[test]
1859    fn can_match_empty_topics() {
1860        let filter = Filter {
1861            block_option: Default::default(),
1862            address: Default::default(),
1863            topics: Default::default(),
1864        };
1865
1866        assert!(filter.matches_topics(&[
1867            B256::with_last_byte(1),
1868            B256::with_last_byte(2),
1869            B256::with_last_byte(3),
1870            B256::with_last_byte(4),
1871        ]));
1872    }
1873
1874    #[test]
1875    fn can_match_address_and_topics() {
1876        let address = Address::with_last_byte(1);
1877        let topic1 = B256::with_last_byte(2);
1878        let topic2 = B256::with_last_byte(3);
1879        let topic3 = B256::with_last_byte(4);
1880
1881        let filter = Filter {
1882            block_option: Default::default(),
1883            address: address.into(),
1884            topics: [
1885                topic1.into(),
1886                vec![topic2, topic3].into(),
1887                Default::default(),
1888                Default::default(),
1889            ],
1890        };
1891
1892        let log =
1893            Log { address, data: LogData::new_unchecked(vec![topic1, topic2], Default::default()) };
1894
1895        assert!(filter.matches(&log));
1896    }
1897
1898    #[test]
1899    fn can_match_topics_wildcard() {
1900        let address = Address::with_last_byte(1);
1901        let topic1 = B256::with_last_byte(2);
1902        let topic2 = B256::with_last_byte(3);
1903        let topic3 = B256::with_last_byte(4);
1904
1905        let filter = Filter {
1906            block_option: Default::default(),
1907            address: Default::default(),
1908            topics: [
1909                Default::default(),
1910                vec![topic2, topic3].into(),
1911                Default::default(),
1912                Default::default(),
1913            ],
1914        };
1915
1916        let log =
1917            Log { address, data: LogData::new_unchecked(vec![topic1, topic2], Default::default()) };
1918
1919        assert!(filter.matches(&log));
1920    }
1921
1922    #[test]
1923    fn can_match_topics_wildcard_mismatch() {
1924        let address = Address::with_last_byte(1);
1925        let topic1 = B256::with_last_byte(2);
1926        let topic2 = B256::with_last_byte(3);
1927        let bad_topic = B256::with_last_byte(4);
1928
1929        let filter = Filter {
1930            block_option: Default::default(),
1931            address: Default::default(),
1932            topics: [
1933                Default::default(),
1934                vec![topic1, topic2].into(),
1935                Default::default(),
1936                Default::default(),
1937            ],
1938        };
1939
1940        let log = Log {
1941            address,
1942            data: LogData::new_unchecked(vec![bad_topic, bad_topic], Default::default()),
1943        };
1944
1945        assert!(!filter.matches(&log));
1946    }
1947
1948    #[test]
1949    fn can_match_address_filter() {
1950        let address = Address::with_last_byte(1);
1951        let filter = Filter {
1952            block_option: Default::default(),
1953            address: address.into(),
1954            topics: Default::default(),
1955        };
1956
1957        assert!(filter.matches_address(address));
1958    }
1959
1960    #[test]
1961    fn can_detect_different_address() {
1962        let address = Address::with_last_byte(1);
1963        let bad_address = Address::with_last_byte(2);
1964        let filter = Filter {
1965            block_option: Default::default(),
1966            address: address.into(),
1967            topics: Default::default(),
1968        };
1969
1970        assert!(!filter.matches_address(bad_address));
1971    }
1972
1973    #[test]
1974    #[cfg(feature = "serde")]
1975    fn can_convert_to_ethers_filter() {
1976        let json = json!(
1977                    {
1978          "fromBlock": "0x429d3b",
1979          "toBlock": "0x429d3b",
1980          "address": "0xb59f67a8bff5d8cd03f6ac17265c550ed8f33907",
1981          "topics": [
1982          "0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef",
1983          "0x00000000000000000000000000b46c2526e227482e2ebb8f4c69e4674d262e75",
1984          "0x00000000000000000000000054a2d42a40f51259dedd1978f6c118a0f0eff078"
1985          ]
1986        }
1987            );
1988
1989        let filter: Filter = serde_json::from_value(json).unwrap();
1990        assert_eq!(
1991            filter,
1992            Filter {
1993                block_option: FilterBlockOption::Range {
1994                    from_block: Some(4365627u64.into()),
1995                    to_block: Some(4365627u64.into()),
1996                },
1997                address: "0xb59f67a8bff5d8cd03f6ac17265c550ed8f33907"
1998                    .parse::<Address>()
1999                    .unwrap()
2000                    .into(),
2001                topics: [
2002                    "0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef"
2003                        .parse::<B256>()
2004                        .unwrap()
2005                        .into(),
2006                    "0x00000000000000000000000000b46c2526e227482e2ebb8f4c69e4674d262e75"
2007                        .parse::<B256>()
2008                        .unwrap()
2009                        .into(),
2010                    "0x00000000000000000000000054a2d42a40f51259dedd1978f6c118a0f0eff078"
2011                        .parse::<B256>()
2012                        .unwrap()
2013                        .into(),
2014                    Default::default(),
2015                ],
2016            }
2017        );
2018    }
2019
2020    #[test]
2021    #[cfg(feature = "serde")]
2022    fn can_convert_to_ethers_filter_with_null_fields() {
2023        let json = json!(
2024                    {
2025          "fromBlock": "0x429d3b",
2026          "toBlock": "0x429d3b",
2027          "address": null,
2028          "topics": null
2029        }
2030            );
2031
2032        let filter: Filter = serde_json::from_value(json).unwrap();
2033        assert_eq!(
2034            filter,
2035            Filter {
2036                block_option: FilterBlockOption::Range {
2037                    from_block: Some(4365627u64.into()),
2038                    to_block: Some(4365627u64.into()),
2039                },
2040                address: Default::default(),
2041                topics: Default::default(),
2042            }
2043        );
2044    }
2045
2046    #[test]
2047    #[cfg(feature = "serde")]
2048    fn test_filter_with_null_range_block() {
2049        let json = json!(
2050                    {
2051          "fromBlock": null,
2052          "toBlock": null,
2053          "blockHash": "0xe903ebc49101d30b28d7256be411f81418bf6809ddbaefc40201b1b97f2e64ee",
2054          "address": null,
2055          "topics": null
2056        }
2057            );
2058
2059        let filter: Filter = serde_json::from_value(json).unwrap();
2060        assert_eq!(
2061            filter.block_option,
2062            FilterBlockOption::AtBlockHash(
2063                "0xe903ebc49101d30b28d7256be411f81418bf6809ddbaefc40201b1b97f2e64ee"
2064                    .parse()
2065                    .unwrap()
2066            )
2067        );
2068    }
2069
2070    #[test]
2071    #[cfg(feature = "serde")]
2072    fn test_filter_with_null_block_hash() {
2073        let json = json!(
2074                    {
2075          "fromBlock": "0x1",
2076          "toBlock": "0x2",
2077          "blockHash": null,
2078          "address": null,
2079          "topics": null
2080        }
2081            );
2082
2083        let filter: Filter = serde_json::from_value(json).unwrap();
2084        assert_eq!(
2085            filter.block_option,
2086            FilterBlockOption::Range { from_block: Some(1u64.into()), to_block: Some(2u64.into()) }
2087        );
2088    }
2089
2090    #[test]
2091    #[cfg(feature = "serde")]
2092    fn test_filter_with_null_block_hash_and_null_from_block() {
2093        let json = json!(
2094                    {
2095          "fromBlock": null,
2096          "toBlock": "0x2",
2097          "blockHash": null,
2098          "address": null,
2099          "topics": null
2100        }
2101            );
2102
2103        let filter: Filter = serde_json::from_value(json).unwrap();
2104        assert_eq!(
2105            filter.block_option,
2106            FilterBlockOption::Range { from_block: None, to_block: Some(2u64.into()) }
2107        );
2108    }
2109
2110    #[test]
2111    #[cfg(feature = "serde")]
2112    fn test_filter_with_null_block_hash_and_null_to_block() {
2113        let json = json!(
2114                    {
2115          "fromBlock": "0x1",
2116          "toBlock": null,
2117          "blockHash": null,
2118          "address": null,
2119          "topics": null
2120        }
2121            );
2122
2123        let filter: Filter = serde_json::from_value(json).unwrap();
2124        assert_eq!(
2125            filter.block_option,
2126            FilterBlockOption::Range { from_block: Some(1u64.into()), to_block: None }
2127        );
2128    }
2129
2130    #[test]
2131    fn test_is_pending_block_filter() {
2132        let filter = Filter {
2133            block_option: FilterBlockOption::Range {
2134                from_block: Some(BlockNumberOrTag::Pending),
2135                to_block: Some(BlockNumberOrTag::Pending),
2136            },
2137            address: "0xb59f67a8bff5d8cd03f6ac17265c550ed8f33907"
2138                .parse::<Address>()
2139                .unwrap()
2140                .into(),
2141            topics: [
2142                "0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef"
2143                    .parse::<B256>()
2144                    .unwrap()
2145                    .into(),
2146                "0x00000000000000000000000000b46c2526e227482e2ebb8f4c69e4674d262e75"
2147                    .parse::<B256>()
2148                    .unwrap()
2149                    .into(),
2150                "0x00000000000000000000000054a2d42a40f51259dedd1978f6c118a0f0eff078"
2151                    .parse::<B256>()
2152                    .unwrap()
2153                    .into(),
2154                Default::default(),
2155            ],
2156        };
2157        assert!(filter.is_pending_block_filter());
2158
2159        let filter = Filter {
2160            block_option: FilterBlockOption::Range {
2161                from_block: Some(4365627u64.into()),
2162                to_block: Some(4365627u64.into()),
2163            },
2164            address: "0xb59f67a8bff5d8cd03f6ac17265c550ed8f33907"
2165                .parse::<Address>()
2166                .unwrap()
2167                .into(),
2168            topics: [
2169                "0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef"
2170                    .parse::<B256>()
2171                    .unwrap()
2172                    .into(),
2173                "0x00000000000000000000000000b46c2526e227482e2ebb8f4c69e4674d262e75"
2174                    .parse::<B256>()
2175                    .unwrap()
2176                    .into(),
2177                "0x00000000000000000000000054a2d42a40f51259dedd1978f6c118a0f0eff078"
2178                    .parse::<B256>()
2179                    .unwrap()
2180                    .into(),
2181                Default::default(),
2182            ],
2183        };
2184        assert!(!filter.is_pending_block_filter());
2185    }
2186
2187    #[test]
2188    fn test_filter_set_topic_extend() {
2189        let mut topic = Topic::default();
2190
2191        // extending with different types that can be converted into Topic
2192        topic =
2193            topic.extend(U256::from(123)).extend(Address::random()).extend(true).extend([0u8; 32]);
2194
2195        assert_eq!(topic.set.len(), 4);
2196
2197        topic = topic.extend(U256::from(123));
2198        assert_eq!(topic.set.len(), 4);
2199
2200        topic = topic.extend(U256::from(456));
2201        assert_eq!(topic.set.len(), 5);
2202    }
2203
2204    #[test]
2205    fn test_append_matching_block_logs() {
2206        use alloy_consensus::Receipt;
2207        use alloy_primitives::Bytes;
2208
2209        // Create test addresses and topics
2210        let addr1 = Address::from([0x11; 20]);
2211        let addr2 = Address::from([0x22; 20]);
2212        let topic1 = B256::from([0x01; 32]);
2213        let topic2 = B256::from([0x02; 32]);
2214
2215        // Create test receipts with logs
2216        let receipt1 = Receipt {
2217            status: alloy_consensus::Eip658Value::Eip658(true),
2218            cumulative_gas_used: 100000,
2219            logs: vec![
2220                alloy_primitives::Log {
2221                    address: addr1,
2222                    data: LogData::new(vec![topic1], Bytes::from(vec![0x01, 0x02])).unwrap(),
2223                },
2224                alloy_primitives::Log {
2225                    address: addr2,
2226                    data: LogData::new(vec![topic2], Bytes::from(vec![0x03, 0x04])).unwrap(),
2227                },
2228            ],
2229        };
2230
2231        let receipt2 = Receipt {
2232            status: alloy_consensus::Eip658Value::Eip658(true),
2233            cumulative_gas_used: 200000,
2234            logs: vec![alloy_primitives::Log {
2235                address: addr1,
2236                data: LogData::new(vec![topic2], Bytes::from(vec![0x05])).unwrap(),
2237            }],
2238        };
2239
2240        let receipts = [receipt1, receipt2];
2241        let tx_hashes = [B256::from([0xaa; 32]), B256::from([0xbb; 32])];
2242
2243        let block_num_hash = BlockNumHash::new(1000, B256::from([0xff; 32]));
2244        let block_timestamp = 1234567890;
2245
2246        // Test 1: Filter by address
2247        let filter = Filter::new().address(addr1);
2248        let mut result = Vec::new();
2249        let tx_receipt_pairs: Vec<_> = tx_hashes.iter().copied().zip(receipts.iter()).collect();
2250        filter.append_matching_block_logs(
2251            &mut result,
2252            block_num_hash,
2253            block_timestamp,
2254            tx_receipt_pairs,
2255            false,
2256        );
2257
2258        assert_eq!(result.len(), 2);
2259        assert_eq!(result[0].inner.address, addr1);
2260        assert_eq!(result[0].transaction_hash, Some(tx_hashes[0]));
2261        assert_eq!(result[0].log_index, Some(0));
2262        assert_eq!(result[0].transaction_index, Some(0));
2263        assert_eq!(result[1].inner.address, addr1);
2264        assert_eq!(result[1].transaction_hash, Some(tx_hashes[1]));
2265        assert_eq!(result[1].log_index, Some(2));
2266        assert_eq!(result[1].transaction_index, Some(1));
2267
2268        // Test 2: Filter by topic
2269        let filter = Filter::new().event_signature(topic2);
2270        let mut result = Vec::new();
2271        let tx_receipt_pairs: Vec<_> = tx_hashes.iter().copied().zip(receipts.iter()).collect();
2272        filter.append_matching_block_logs(
2273            &mut result,
2274            block_num_hash,
2275            block_timestamp,
2276            tx_receipt_pairs,
2277            false,
2278        );
2279
2280        assert_eq!(result.len(), 2);
2281        assert_eq!(result[0].inner.data.topics()[0], topic2);
2282        assert_eq!(result[0].transaction_hash, Some(tx_hashes[0]));
2283        assert_eq!(result[0].log_index, Some(1));
2284        assert_eq!(result[1].inner.data.topics()[0], topic2);
2285        assert_eq!(result[1].transaction_hash, Some(tx_hashes[1]));
2286        assert_eq!(result[1].log_index, Some(2));
2287
2288        // Test 3: No matches
2289        let filter = Filter::new().address(Address::from([0x99; 20]));
2290        let mut result = Vec::new();
2291        let tx_receipt_pairs: Vec<_> = tx_hashes.iter().copied().zip(receipts.iter()).collect();
2292        filter.append_matching_block_logs(
2293            &mut result,
2294            block_num_hash,
2295            block_timestamp,
2296            tx_receipt_pairs,
2297            false,
2298        );
2299
2300        assert_eq!(result.len(), 0);
2301
2302        // Test 4: Check all metadata is properly set
2303        let filter = Filter::new();
2304        let mut result = Vec::new();
2305        let tx_receipt_pairs: Vec<_> = tx_hashes.iter().copied().zip(receipts.iter()).collect();
2306        filter.append_matching_block_logs(
2307            &mut result,
2308            block_num_hash,
2309            block_timestamp,
2310            tx_receipt_pairs,
2311            true, // removed = true
2312        );
2313
2314        assert_eq!(result.len(), 3);
2315        for log in &result {
2316            assert_eq!(log.block_hash, Some(block_num_hash.hash));
2317            assert_eq!(log.block_number, Some(block_num_hash.number));
2318            assert_eq!(log.block_timestamp, Some(block_timestamp));
2319            assert!(log.removed);
2320        }
2321
2322        // Test 5: Test matching_block_logs with block filter
2323        let filter = Filter::new().from_block(1000u64).to_block(1000u64).address(addr1);
2324        let tx_receipt_pairs: Vec<_> = tx_hashes.iter().copied().zip(receipts.iter()).collect();
2325        let result =
2326            filter.matching_block_logs(block_num_hash, block_timestamp, tx_receipt_pairs, false);
2327
2328        assert_eq!(result.len(), 2);
2329        assert_eq!(result[0].inner.address, addr1);
2330        assert_eq!(result[1].inner.address, addr1);
2331
2332        // Test 6: Test matching_block_logs with non-matching block
2333        let filter = Filter::new().from_block(2000u64).to_block(2000u64);
2334        let tx_receipt_pairs: Vec<_> = tx_hashes.iter().copied().zip(receipts.iter()).collect();
2335        let result =
2336            filter.matching_block_logs(block_num_hash, block_timestamp, tx_receipt_pairs, false);
2337
2338        assert_eq!(result.len(), 0); // Should not append any logs due to block mismatch
2339
2340        // Test 7: Test append_matching_block_logs with non-matching block
2341        let filter = Filter::new().from_block(2000u64).to_block(2000u64);
2342        let mut result = Vec::new();
2343        let tx_receipt_pairs: Vec<_> = tx_hashes.iter().copied().zip(receipts.iter()).collect();
2344        filter.append_matching_block_logs(
2345            &mut result,
2346            block_num_hash,
2347            block_timestamp,
2348            tx_receipt_pairs,
2349            false,
2350        );
2351
2352        assert_eq!(result.len(), 0); // Should not append any logs due to block mismatch
2353    }
2354
2355    #[test]
2356    fn test_filter_receipts_iterator() {
2357        use alloy_consensus::Receipt;
2358        use alloy_primitives::Bytes;
2359
2360        // Create test addresses and topics
2361        let addr1 = Address::from([0x11; 20]);
2362        let addr2 = Address::from([0x22; 20]);
2363        let topic1 = B256::from([0x01; 32]);
2364        let topic2 = B256::from([0x02; 32]);
2365
2366        // Create test receipts for block 1
2367        let block1_receipts = vec![
2368            Receipt {
2369                status: alloy_consensus::Eip658Value::Eip658(true),
2370                cumulative_gas_used: 100000,
2371                logs: vec![
2372                    alloy_primitives::Log {
2373                        address: addr1,
2374                        data: LogData::new(vec![topic1], Bytes::from(vec![0x01, 0x02])).unwrap(),
2375                    },
2376                    alloy_primitives::Log {
2377                        address: addr2,
2378                        data: LogData::new(vec![topic2], Bytes::from(vec![0x03, 0x04])).unwrap(),
2379                    },
2380                ],
2381            },
2382            Receipt {
2383                status: alloy_consensus::Eip658Value::Eip658(true),
2384                cumulative_gas_used: 200000,
2385                logs: vec![alloy_primitives::Log {
2386                    address: addr1,
2387                    data: LogData::new(vec![topic2], Bytes::from(vec![0x05])).unwrap(),
2388                }],
2389            },
2390        ];
2391
2392        // Create test receipts for block 2
2393        let block2_receipts = vec![Receipt {
2394            status: alloy_consensus::Eip658Value::Eip658(true),
2395            cumulative_gas_used: 300000,
2396            logs: vec![
2397                alloy_primitives::Log {
2398                    address: addr1,
2399                    data: LogData::new(vec![topic1], Bytes::from(vec![0x06])).unwrap(),
2400                },
2401                alloy_primitives::Log {
2402                    address: addr2,
2403                    data: LogData::new(vec![topic1], Bytes::from(vec![0x07])).unwrap(),
2404                },
2405            ],
2406        }];
2407
2408        let all_receipts = vec![block1_receipts, block2_receipts];
2409
2410        // Test 1: Filter by address
2411        let filter = Filter::new().address(addr1);
2412        let logs: Vec<_> = filter.filter_receipts(all_receipts.clone()).collect();
2413        assert_eq!(logs.len(), 3); // Should match 3 logs with addr1
2414        assert!(logs.iter().all(|log| log.address == addr1));
2415
2416        // Test 2: Filter by topic
2417        let filter = Filter::new().event_signature(topic1);
2418        let logs: Vec<_> = filter.filter_receipts(all_receipts.clone()).collect();
2419
2420        // Block 1, Receipt 1: topic1 (addr1), topic2 (addr2)
2421        // Block 1, Receipt 2: topic2 (addr1)
2422        // Block 2, Receipt 1: topic1 (addr1), topic1 (addr2)
2423        // Total: 3 logs with topic1
2424        assert_eq!(logs.len(), 3); // Should match 3 logs with topic1
2425        assert!(logs.iter().all(|log| log.topics()[0] == topic1));
2426
2427        // Test 3: Filter by address and topic
2428        let filter = Filter::new().address(addr1).event_signature(topic2);
2429        let logs: Vec<_> = filter.filter_receipts(all_receipts.clone()).collect();
2430        assert_eq!(logs.len(), 1); // Should match 1 log with addr1 and topic2
2431        assert_eq!(logs[0].address, addr1);
2432        assert_eq!(logs[0].topics()[0], topic2);
2433
2434        // Test 4: No matches
2435        let filter = Filter::new().address(Address::from([0x99; 20]));
2436        let logs: Vec<_> = filter.filter_receipts(all_receipts.clone()).collect();
2437        assert_eq!(logs.len(), 0);
2438
2439        // Test 5: Empty filter matches all
2440        let filter = Filter::new();
2441        let logs: Vec<_> = filter.filter_receipts(all_receipts).collect();
2442        assert_eq!(logs.len(), 5); // Should match all 5 logs
2443    }
2444}