Skip to content

Commit 7887e43

Browse files
authored
build: prepare for v0.1.0-alpha.2 release (#82)
1 parent dec76a5 commit 7887e43

File tree

4 files changed

+49
-117
lines changed

4 files changed

+49
-117
lines changed

Cargo.lock

Lines changed: 3 additions & 3 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

Cargo.toml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@ authors = ["OpenZeppelin"]
77
edition = "2024"
88
license = "AGPL-3.0-only"
99
repository = "https://github.com/OpenZeppelin/Event-Scanner"
10-
version = "0.1.0-alpha.1"
10+
version = "0.1.0-alpha.2"
1111

1212
[workspace.lints.clippy]
1313
pedantic = "warn"

README.md

Lines changed: 43 additions & 111 deletions
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@
88
99
## About
1010

11-
Event Scanner is a Rust library for monitoring EVM-based smart contract events. It is built on top of the [`alloy`](https://github.com/alloy-rs/alloy) ecosystem and focuses on in-memory scanning without a backing database. Applications provide event filters and callback implementations; the scanner takes care of subscribing to historical ranges, bridging into live mode, and delivering events with retry-aware execution strategies.
11+
Event Scanner is a Rust library for streaming EVM-based smart contract events. It is built on top of the [`alloy`](https://github.com/alloy-rs/alloy) ecosystem and focuses on in-memory scanning without a backing database. Applications provide event filters; the scanner takes care of fetching historical ranges, bridging into live streaming mode, all whilst delivering the events as streams of data.
1212

1313
---
1414

@@ -29,11 +29,10 @@ Event Scanner is a Rust library for monitoring EVM-based smart contract events.
2929

3030
## Features
3131

32-
- **Historical replay**scan block ranges.
33-
- **Live subscriptions** – stay up to date with latest blocks via WebSocket or IPC transports.
32+
- **Historical replay**stream events from past block ranges.
33+
- **Live subscriptions** – stay up to date with latest events via WebSocket or IPC transports.
3434
- **Hybrid flow** – automatically transition from historical catch-up into streaming mode.
35-
- **Composable filters** – register one or many contract + event signature pairs with their own callbacks.
36-
- **Retry strategies** – built-in retryable callback backoff strategies
35+
- **Composable filters** – register one or many contract + event signature pairs.
3736
- **No database** – processing happens in-memory; persistence is left to the host application.
3837

3938
---
@@ -42,11 +41,9 @@ Event Scanner is a Rust library for monitoring EVM-based smart contract events.
4241

4342
The library exposes two primary layers:
4443

45-
- `EventScannerBuilder` / `EventScanner` – the main module the application will interact with.
44+
- `EventScanner` – the main module the application will interact with.
4645
- `BlockRangeScanner` – lower-level component that streams block ranges, handles reorg, batching, and provider subscriptions.
4746

48-
Callbacks implement the `EventCallback` trait. They are executed through a `CallbackStrategy` that performs retries when necessary before reporting failures.
49-
5047
---
5148

5249
## Quick Start
@@ -55,38 +52,38 @@ Add `event-scanner` to your `Cargo.toml`:
5552

5653
```toml
5754
[dependencies]
58-
event-scanner = "0.1.0-alpha.1"
55+
event-scanner = "0.1.0-alpha.2"
5956
```
60-
Create a callback implementing `EventCallback` and register it with the builder:
57+
58+
Create an event stream for the given event filters registered with the `EventScanner`:
6159

6260
```rust
63-
use std::{sync::{Arc, atomic::{AtomicUsize, Ordering}}};
64-
use alloy::{eips::BlockNumberOrTag, network::Ethereum, rpc::types::Log, sol_types::SolEvent};
65-
use async_trait::async_trait;
66-
use event_scanner::{event_scanner::EventScannerBuilder, EventCallback, EventFilter};
67-
68-
struct CounterCallback { processed: Arc<AtomicUsize> }
69-
70-
#[async_trait]
71-
impl EventCallback for CounterCallback {
72-
async fn on_event(&self, _log: &Log) -> anyhow::Result<()> {
73-
self.processed.fetch_add(1, Ordering::SeqCst);
74-
Ok(())
75-
}
76-
}
61+
use alloy::{eips::BlockNumberOrTag, network::Ethereum, sol_types::SolEvent};
62+
use event_scanner::{EventFilter, event_scanner::EventScanner, block_range_scanner::Error};
63+
use tokio_stream::StreamExt;
64+
65+
use crate::MyContract;
66+
67+
async fn run_scanner(
68+
ws_url: alloy::transports::http::reqwest::Url,
69+
contract: alloy::primitives::Address,
70+
) -> Result<(), Error> {
71+
let mut client = EventScanner::new().connect_ws::<Ethereum>(ws_url).await?;
7772

78-
async fn run_scanner(ws_url: alloy::transports::http::reqwest::Url, contract: alloy::primitives::Address) -> anyhow::Result<()> {
7973
let filter = EventFilter::new()
8074
.with_contract_address(contract)
81-
.with_event(MyContract::SomeEvent::SIGNATURE)
82-
.with_callback(Arc::new(CounterCallback { processed: Arc::new(AtomicUsize::new(0)) }));
75+
.with_event(MyContract::SomeEvent::SIGNATURE);
76+
77+
let mut stream = client.create_event_stream(filter);
8378

84-
let mut client = EventScannerBuilder::new()
85-
.with_event_filter(filter)
86-
.connect_ws::<Ethereum>(ws_url)
87-
.await?;
79+
tokio::spawn(async move {
80+
client.start_scanner(BlockNumberOrTag::Earliest, Some(BlockNumberOrTag::Latest)).await
81+
});
82+
83+
while let Some(Ok(logs)) = stream.next().await {
84+
println!("Fetched logs: {logs:?}");
85+
}
8886

89-
client.start_scanner(BlockNumberOrTag::Latest, None).await?;
9087
Ok(())
9188
}
9289
```
@@ -97,74 +94,37 @@ async fn run_scanner(ws_url: alloy::transports::http::reqwest::Url, contract: al
9794

9895
### Building a Scanner
9996

100-
`EventScannerBuilder` supports:
97+
`EventScanner` supports:
10198

102-
- `with_event_filter(s)` – attach [filters](#defining-event-filters).
103-
- `with_callback_strategy(strategy)` – override retry behaviour (`StateSyncAwareStrategy` by default).
10499
- `with_blocks_read_per_epoch` - how many blocks are read at a time in a single batch (taken into consideration when fetching historical blocks)
105-
- `with_reorg_rewind_depth` - how many blocks to rewind when a reorg is detected
106-
- `with_block_confirmations` - how many confirmations to wait for before considering a block final
107-
108-
Once configured, connect using either `connect_ws::<Ethereum>(ws_url)` or `connect_ipc::<Ethereum>(path)`. This will `build` the `EventScanner` and allow you to call run to start in various [modes](#scanning-Modes).
100+
- `with_reorg_rewind_depth` - how many blocks to rewind when a reorg is detected (NOTE ⚠️: still WIP)
101+
- `with_block_confirmations` - how many confirmations to wait for before considering a block final (NOTE ⚠️: still WIP)
109102

103+
Once configured, connect using either `connect_ws::<Ethereum>(ws_url)` or `connect_ipc::<Ethereum>(path)`. This will `connect` the `EventScanner` and allow you to create event streams and start scanning in various [modes](#scanning-Modes).
110104

111105
### Defining Event Filters
112106

113-
Create an `EventFilter` for each contract/event pair you want to track. The filter bundles the contract address, the event signature (from `SolEvent::SIGNATURE`), and an `Arc<dyn EventCallback + Send + Sync>`.
107+
Create an `EventFilter` for each contract/event pair you want to track. The filter specifies the contract address where events originated, and the event signature (from `SolEvent::SIGNATURE`).
114108

115109
Both `contract_address` and `event` fields are optional, allowing for flexible event tracking.
116110

117-
You can construct EventFilters using either the builder pattern (recommended) or direct struct construction:
118-
119-
### Builder Pattern (Recommended)
120-
121111
```rust
122112
// Track a specific event from a specific contract
123113
let specific_filter = EventFilter::new()
124114
.with_contract_address(*counter_contract.address())
125-
.with_event(Counter::CountIncreased::SIGNATURE)
126-
.with_callback(Arc::new(CounterCallback));
115+
.with_event(Counter::CountIncreased::SIGNATURE);
127116

128117
// Track ALL events from a specific contract
129118
let all_contract_events_filter = EventFilter::new()
130-
.with_contract_address(*counter_contract.address())
131-
.with_callback(Arc::new(AllEventsCallback));
119+
.with_contract_address(*counter_contract.address());
132120

133121
// Track ALL events from ALL contracts in the block range
134-
let all_events_filter = EventFilter::new()
135-
.with_callback(Arc::new(GlobalEventsCallback));
122+
let all_events_filter = EventFilter::new();
136123
```
137124

138-
### Direct Struct Construction
125+
Register multiple filters by invoking `create_event_stream` repeatedly.
139126

140-
```rust
141-
// Track a specific event from a specific contract (traditional usage)
142-
let specific_filter = EventFilter {
143-
contract_address: Some(*counter_contract.address()),
144-
event: Some(Counter::CountIncreased::SIGNATURE.to_owned()),
145-
callback: Arc::new(CounterCallback),
146-
};
147-
148-
// Track ALL events from a specific contract
149-
let all_contract_events_filter = EventFilter {
150-
contract_address: Some(*counter_contract.address()),
151-
event: None, // Will track all events from this contract
152-
callback: Arc::new(AllEventsCallback),
153-
};
154-
155-
// Track ALL events from ALL contracts in the block range
156-
let all_events_filter = EventFilter {
157-
contract_address: None, // Will track events from all contracts
158-
event: None, // Will track all event types
159-
callback: Arc::new(GlobalEventsCallback),
160-
};
161-
```
162-
163-
Register multiple filters by calling either `with_event_filter` repeatedly or `with_event_filters` once.
164-
165-
#### Use Cases for Optional Fields
166-
167-
The optional `contract_address` and `event` fields enable several powerful use cases:
127+
Event filters enable several powerful use cases:
168128

169129
- **Track all events from a specific contract**: Set `contract_address` but leave `event` as `None`
170130
- **Track all events across all contracts**: Set both `contract_address` and `event` as `None`
@@ -173,44 +133,16 @@ The optional `contract_address` and `event` fields enable several powerful use c
173133

174134
This flexibility allows you to build sophisticated event monitoring systems that can track events at different granularities depending on your application's needs.
175135

176-
177136
### Scanning Modes
178137

179-
- **Live mode**`start(BlockNumberOrTag::Latest, None)` subscribes to new blocks only.
180-
- **Historical mode**`start(BlockNumberOrTag::Number(start, Some(BlockNumberOrTag::Number(end)))`, scanner fetches events from a historical block range.
181-
- **Historical → Live**`start(BlockNumberOrTag::Number(start, None)` replays from `start` to current head, then streams future blocks.
138+
- **Live mode**`start_scanner(BlockNumberOrTag::Latest, None)` subscribes to new blocks only.
139+
- **Historical mode**`start_scanner(BlockNumberOrTag::Number(start), Some(BlockNumberOrTag::Number(end)))`, scanner fetches events from a historical block range.
140+
- **Historical → Live**`start_scanner(BlockNumberOrTag::Number(start), None)` replays from `start` to current head, then streams future blocks.
182141

183142
For now modes are deduced from the `start` and `end` parameters. In the future, we might add explicit commands to select the mode.
184143

185144
See the integration tests under `tests/live_mode`, `tests/historic_mode`, and `tests/historic_to_live` for concrete examples.
186145

187-
### Working with Callbacks
188-
189-
Implement `EventCallback`:
190-
191-
```rust
192-
#[async_trait]
193-
impl EventCallback for RollupCallback {
194-
async fn on_event(&self, log: &Log) -> anyhow::Result<()> {
195-
// decode event, send to EL etc.
196-
Ok(())
197-
}
198-
}
199-
```
200-
201-
Advanced users can write custom retry behaviour by implementing the `CallbackStrategy` trait. The default `StateSyncAwareStrategy` automatically detects state-sync errors and performs exponential backoff ([smart retry mechanism](https://github.com/taikoxyz/taiko-mono/blob/f4b3a0e830e42e2fee54829326389709dd422098/packages/taiko-client/pkg/chain_iterator/block_batch_iterator.go#L149) from the geth driver) before falling back to a fixed retry policy configured via `FixedRetryConfig`.
202-
203-
```rust
204-
#[async_trait]
205-
pub trait CallbackStrategy: Send + Sync {
206-
async fn execute(
207-
&self,
208-
callback: &Arc<dyn EventCallback + Send + Sync>,
209-
log: &Log,
210-
) -> anyhow::Result<()>;
211-
}
212-
```
213-
214146
---
215147

216148
## Examples
@@ -226,7 +158,7 @@ RUST_LOG=info cargo run -p simple_counter
226158
RUST_LOG=info cargo run -p historical_scanning
227159
```
228160

229-
Both examples spin up a local `anvil` instance and deploy a demo counter contract before starting the scanner.
161+
Both examples spin up a local `anvil` instance, deploy a demo counter contract, and demonstrate using event streams to process events.
230162

231163
---
232164

src/block_range_scanner.rs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -28,9 +28,9 @@
2828
//! // Create client to send subscribe command to block scanner
2929
//! let client: BlockRangeScannerClient = block_range_scanner.run()?;
3030
//!
31-
//! let mut receiver = client.stream_live().await?;
31+
//! let mut stream = client.stream_live().await?;
3232
//!
33-
//! while let Some(result) = receiver.next().await {
33+
//! while let Some(result) = stream.next().await {
3434
//! match result {
3535
//! Ok(range) => {
3636
//! // process range

0 commit comments

Comments
 (0)