OpenZeppelin / OpenZeppelin/Event-Scanner
feat: Support Multiple Event Filters for a Single Event Stream
Nobody has claimed this yet.
- Dominant language
- Rust
- Stars
- 19
- Forks
- 7
- Avg merge
- 2h 15m
- Merged PRs (30d)
- 4
Description
Currently it is possible to create only a single event filter per an event stream, and if one wants multiple event filters, they need to create multiple event streams:
let mut scanner = EventScannerBuilder::live()
.block_confirmations(confirmations)
.connect(provider.clone())
.await?;
// track `CountIncreased` events from contract `a`
let a_filter = EventFilter::new()
.contract_address(*a.address())
.event(TestCounter::CountIncreased::SIGNATURE.to_owned());
// track `CountDecreased` events from contract `b`
let b_filter = EventFilter::new()
.contract_address(*b.address())
.event(TestCounter::CountDecreased::SIGNATURE.to_owned());
let a_stream = scanner.subscribe(a_filter);
let b_stream = scanner.subscribe(b_filter);
In the above example, merging the filters (and streams) would would result in different filter behavior:
// track `CountIncreased` and `CountDecreased` events from from both contracts `a` and `b`
let filter = EventFilter::new()
.contract_address(*a.address())
.contract_address(*b.address())
.event(TestCounter::CountIncreased::SIGNATURE.to_owned())
.event(TestCounter::CountDecreased::SIGNATURE.to_owned());
The difference is subtle, but important: in the first example the scanner would filter for CountIncreased events emitted only from contract a and for CountDecreased events emitted only from contract b, whereas in the second example the scanner would filter for both CountIncreased and CountDecreased events emitted from both contracts a and b; this means that CountIncreased emitted from b and CountDecreased emitted from a would also be tracked, which differs from the first example's behavior.
There are use cases when a user might want the scanner to filter events in the former way, but return them in a single stream, and not multiple ones.
This would probably look something like:
let mut scanner = EventScannerBuilder::live()
.block_confirmations(confirmations)
.connect(provider.clone())
.await?;
// track `CountIncreased` events from contract `a`
let a_filter = EventFilter::new()
.contract_address(*a.address())
.event(TestCounter::CountIncreased::SIGNATURE.to_owned());
// track `CountDecreased` events from contract `b`
let b_filter = EventFilter::new()
.contract_address(*b.address())
.event(TestCounter::CountDecreased::SIGNATURE.to_owned());
// pass multiple filters as a slice/vector
//
let stream = scanner.subscribe(&[a_filter, b_filter]);
To give an example of the expected behavior:
- event listeners:
AandB - block ranges being processed:
1-10, 11-20, 21-30 - scanner processing block range
1-10- collects all events
AandBin the range - sorts the events in chronological order (if not already sorted)
- streams the sorted batch of
AandBevents
- collects all events
- scanner processing block range
11-20
...
Contributor guide
First steps
- Read the whole issue, then the project's contributing guide.
- Comment on the issue to say you are picking it up — it saves two people doing the same work.
- Fork the repository and make your change on a branch.
- Open a pull request that references the issue number.
Research direction
Start at EventScannerBuilder::subscribe and the EventFilter usage shown in the issue, then trace how the scanner processes each block range and emits streams. Verify how multiple filters should preserve each filter's contract/event pairing while producing one chronologically sorted stream. Done means a slice or vector of filters can be subscribed to and batches from all matching filters are emitted as described.
Written by the indexing model from the issue text.
Assessment
- Tech stack
- rust
- Domain
- blockchain
- Issue type
- Feature
- Difficulty
- 4/5
- Estimated time
- 3-5 days
- Activity status
- Stale
- Clarity
- Mostly clear
- Newbie friendliness
- 42/100