Event Scanner
⚠️ WARNING: ACTIVE DEVELOPMENT ⚠️
This project is under active development and likely contains bugs. APIs and behaviour may change without notice. Use at your own risk.
About
Event Scanner is a Rust library for monitoring EVM-based smart contract events. It is built on top of the 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.
Table of Contents
Features
- Historical replay – scan block ranges.
- Live subscriptions – stay up to date with latest blocks via WebSocket or IPC transports.
- Hybrid flow – automatically transition from historical catch-up into streaming mode.
- Composable filters – register one or many contract + event signature pairs with their own callbacks.
- Retry strategies – built-in retryable callback backoff strategies
- No database – processing happens in-memory; persistence is left to the host application.
Architecture Overview
The library exposes two primary layers:
EventScannerBuilder/EventScanner– the main module the application will interact with.BlockRangeScanner– lower-level component that streams block ranges, handles reorg, batching, and provider subscriptions.
Callbacks implement the EventCallback trait. They are executed through a CallbackStrategy that performs retries when necessary before reporting failures.
Quick Start
Add event-scanner to your Cargo.toml:
[]
= "0.1.0-alpha.1"
Create a callback implementing EventCallback and register it with the builder:
use ;
use ;
use async_trait;
use ;
async
Usage
Building a Scanner
EventScannerBuilder supports:
with_event_filter(s)– attach filters.with_callback_strategy(strategy)– override retry behaviour (StateSyncAwareStrategyby default).with_blocks_read_per_epoch- how many blocks are read at a time in a single batch (taken into consideration when fetching historical blocks)with_reorg_rewind_depth- how many blocks to rewind when a reorg is detectedwith_retry_interval- how often to retry failed callbackswith_block_confirmations- how many confirmations to wait for before considering a block final
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.
Defining Event Filters
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>.
let filter = EventFilter ;
Register multiple filters by calling either with_event_filter repeatedly or with_event_filters once.
Scanning Modes
- Live mode –
start(BlockNumberOrTag::Latest, None)subscribes to new blocks only. - Historical mode –
start(BlockNumberOrTag::Number(start, Some(BlockNumberOrTag::Number(end))), scanner fetches events from a historical block range. - Historical → Live –
start(BlockNumberOrTag::Number(start, None)replays fromstartto current head, then streams future blocks.
For now modes are deduced from the start and end parameters. In the future, we might add explicit commands to select the mode.
See the integration tests under tests/live_mode, tests/historic_mode, and tests/historic_to_live for concrete examples.
Working with Callbacks
Implement EventCallback:
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 from the geth driver) before falling back to a fixed retry policy configured via FixedRetryConfig.
Examples
examples/simple_counter– minimal live-mode scannerexamples/historical_scanning– demonstrates replaying from genesis (block 0) before continuing streaming latest blocks
Run an example with:
RUST_LOG=info
# or
RUST_LOG=info
Both examples spin up a local anvil instance and deploy a demo counter contract before starting the scanner.
Testing
Integration tests cover live, historical, and hybrid flows: (We recommend using nextest to run the tests)