@@ -31,6 +31,108 @@ impl EventScanner {
3131 SyncScannerBuilder :: new ( )
3232 }
3333
34+ /// Streams the latest `count` matching events per registered listener.
35+ ///
36+ /// # Example
37+ ///
38+ /// ```no_run
39+ /// # use alloy::{network::Ethereum, primitives::Address};
40+ /// # use event_scanner::{EventFilter, EventScanner, Message};
41+ /// # use tokio_stream::StreamExt;
42+ /// #
43+ /// # async fn example() -> Result<(), Box<dyn std::error::Error>> {
44+ /// # let ws_url = "ws://localhost:8545".parse()?;
45+ /// # let contract_address = alloy::primitives::address!("0xd8dA6BF26964af9d7eed9e03e53415d37aa96045");
46+ /// // Collect the latest 10 events across Earliest..=Latest
47+ /// let mut scanner = EventScanner::latest()
48+ /// .count(10)
49+ /// .connect_ws::<Ethereum>(ws_url)
50+ /// .await?;
51+ ///
52+ /// let filter = EventFilter::new().contract_address(contract_address);
53+ /// let mut stream = scanner.subscribe(filter);
54+ ///
55+ /// scanner.start().await?;
56+ ///
57+ /// // Expect a single message with up to 10 logs, then the stream ends
58+ /// while let Some(Message::Data(logs)) = stream.next().await {
59+ /// println!("Latest logs: {}", logs.len());
60+ /// }
61+ /// # Ok(())
62+ /// # }
63+ /// ```
64+ ///
65+ /// Restricting to a specific block range:
66+ ///
67+ /// ```no_run
68+ /// # use alloy::network::Ethereum;
69+ /// # use event_scanner::EventScanner;
70+ /// #
71+ /// # async fn example() -> Result<(), Box<dyn std::error::Error>> {
72+ /// # let ws_url = "ws://localhost:8545".parse()?;
73+ /// // Collect the latest 5 events between blocks [1_000_000, 1_100_000]
74+ /// let mut scanner = EventScanner::latest()
75+ /// .count(5)
76+ /// .from_block(1_000_000)
77+ /// .to_block(1_100_000)
78+ /// .connect_ws::<Ethereum>(ws_url)
79+ /// .await?;
80+ /// # Ok(())
81+ /// # }
82+ /// ```
83+ ///
84+ /// # How it works
85+ ///
86+ /// The scanner performs a reverse-ordered scan (newest to oldest) within the specified block
87+ /// range, collecting up to `count` events per registered listener. Once the target count is
88+ /// reached or the range is exhausted, it delivers the events in chronological order (oldest to
89+ /// newest) and completes.
90+ ///
91+ /// When using a custom block range, the scanner automatically normalizes the range boundaries.
92+ /// This means you can specify `from_block` and `to_block` in any order - the scanner will
93+ /// always scan from the higher block number down to the lower one, regardless of which
94+ /// parameter holds which value.
95+ ///
96+ /// # Key behaviors
97+ ///
98+ /// - **Single delivery**: Each registered stream receives at most `count` logs in a single
99+ /// message, chronologically ordered
100+ /// - **One-shot operation**: The scanner completes after delivering messages; it does not
101+ /// continue streaming
102+ /// - **Flexible count**: If fewer than `count` events exist in the range, returns all available
103+ /// events
104+ /// - **Default range**: By default, scans from `Earliest` to `Latest` block
105+ /// - **Reorg handling**: Periodically checks the tip to detect reorgs during the scan
106+ ///
107+ /// # Important notes
108+ ///
109+ /// - Register event streams via [`scanner.subscribe(filter)`][subscribe] **before** calling
110+ /// [`scanner.start()`][start]
111+ /// - The [`scanner.start()`][start] method returns immediately; events are delivered
112+ /// asynchronously
113+ /// - For continuous streaming after collecting latest events, use
114+ /// [`EventScanner::sync().from_latest(count)`][sync_from_latest] instead
115+ ///
116+ /// # Reorg behavior
117+ ///
118+ /// During the scan, the scanner periodically checks the tip to detect reorgs. On reorg
119+ /// detection:
120+ /// 1. Emits [`ScannerStatus::ReorgDetected`][reorg] to all listeners
121+ /// 2. Resets to the updated tip
122+ /// 3. Restarts the scan from the new tip
123+ /// 4. Continues until `count` events are collected
124+ ///
125+ /// Final delivery to log listeners preserves chronological order regardless of reorgs.
126+ ///
127+ /// [count]: latest::LatestScannerBuilder::count
128+ /// [from_block]: latest::LatestScannerBuilder::from_block
129+ /// [to_block]: latest::LatestScannerBuilder::to_block
130+ /// [block_confirmations]: latest::LatestScannerBuilder::block_confirmations
131+ /// [max_block_range]: latest::LatestScannerBuilder::max_block_range
132+ /// [subscribe]: latest::LatestEventScanner::subscribe
133+ /// [start]: latest::LatestEventScanner::start
134+ /// [sync_from_latest]: SyncScannerBuilder::from_latest
135+ /// [reorg]: crate::types::ScannerStatus::ReorgDetected
34136 #[ must_use]
35137 pub fn latest ( ) -> LatestScannerBuilder {
36138 LatestScannerBuilder :: new ( )
0 commit comments