Skip to content

feat: Support Multiple Event Filters for a Single Event Stream #169

@0xNeshi

Description

@0xNeshi

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: A and B
  • block ranges being processed: 1-10, 11-20, 21-30
  • scanner processing block range 1-10
    • collects all events A and B in the range
    • sorts the events in chronological order (if not already sorted)
    • streams the sorted batch of A and B events
  • scanner processing block range 11-20
    ...

Metadata

Metadata

Assignees

No one assigned

    Labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions