use std::fmt::{Debug, Display};
use alloy::{
primitives::{Address, keccak256},
rpc::types::{Filter, Topic, ValueOrArray},
};
#[derive(Clone, Default)]
pub struct EventFilter {
pub(crate) contract_addresses: Vec<Address>,
pub(crate) events: Vec<String>,
pub(crate) event_signatures: Topic,
}
impl Display for EventFilter {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
let mut content = vec![];
if !self.contract_addresses.is_empty() {
let contracts = self
.contract_addresses
.iter()
.map(|addr| format!("{addr}"))
.collect::<Vec<_>>()
.join(", ");
content.push(format!("contracts: [{contracts}]",));
}
if !self.events.is_empty() {
content.push(format!("events: [{}]", self.events.join(", ")));
}
if let Some(value_or_array) = self.event_signatures.to_value_or_array() {
let event_signatures = match value_or_array {
ValueOrArray::Value(value) => format!("{value}"),
ValueOrArray::Array(arr) => {
arr.iter().map(|t| format!("{t}")).collect::<Vec<_>>().join(", ")
}
};
content.push(format!("event_signatures: [{event_signatures}]"));
}
write!(f, "EventFilter({})", content.join(", "))
}
}
impl Debug for EventFilter {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(f, "{self}")
}
}
impl EventFilter {
#[must_use]
pub fn new() -> Self {
EventFilter::default()
}
#[must_use]
pub fn contract_address(mut self, contract_address: impl Into<Address>) -> Self {
self.contract_addresses.push(contract_address.into());
self
}
#[must_use]
pub fn contract_addresses(
mut self,
contract_addresses: impl IntoIterator<Item = impl Into<Address>>,
) -> Self {
self.contract_addresses.extend(contract_addresses.into_iter().map(Into::into));
self
}
#[must_use]
pub fn event(mut self, event: impl Into<String>) -> Self {
let event = event.into();
if !event.is_empty() {
self.events.push(event);
}
self
}
#[must_use]
pub fn events(mut self, events: impl IntoIterator<Item = impl Into<String>>) -> Self {
self.events.extend(events.into_iter().map(Into::into));
self
}
#[must_use]
pub fn event_signature(mut self, event_signature: impl Into<Topic>) -> Self {
self.event_signatures = self.event_signatures.extend(event_signature.into());
self
}
#[must_use]
pub fn event_signatures(
mut self,
event_signatures: impl IntoIterator<Item = impl Into<Topic>>,
) -> Self {
for event_signature in event_signatures {
self.event_signatures = self.event_signatures.extend(event_signature);
}
self
}
#[must_use]
fn all_events(&self) -> Topic {
let events = self.events.iter().map(|e| keccak256(e.as_bytes())).collect::<Vec<_>>();
let sigs = self.event_signatures.clone();
sigs.extend(events)
}
}
impl From<EventFilter> for Filter {
fn from(value: EventFilter) -> Self {
let mut filter = Filter::new();
let events = value.all_events();
if !events.is_empty() {
filter = filter.event_signature(events);
}
if !value.contract_addresses.is_empty() {
filter = filter.address(value.contract_addresses);
}
filter
}
}
impl From<&EventFilter> for Filter {
fn from(value: &EventFilter) -> Self {
let mut filter = Filter::new();
let events = value.all_events();
if !events.is_empty() {
filter = filter.event_signature(events);
}
if !value.contract_addresses.is_empty() {
filter = filter.address(value.contract_addresses.clone());
}
filter
}
}
#[cfg(test)]
mod tests {
use super::EventFilter;
use alloy::{
primitives::{Address, address},
rpc::types::Topic,
sol,
sol_types::SolEvent,
};
sol! {
contract SomeContract {
event EventOne();
event EventTwo();
}
}
#[test]
fn display_default_no_address_no_events() {
let filter = EventFilter::new();
let got = format!("{filter}");
let expected = "EventFilter()";
assert_eq!(got, expected);
assert_eq!(format!("{filter:?}"), got);
}
#[test]
fn display_with_address() {
let address = address!("0x000000000000000000000000000000000000dEaD");
let filter = EventFilter::new().contract_address(address);
let got = format!("{filter}");
let expected = "EventFilter(contracts: [0x000000000000000000000000000000000000dEaD])";
assert_eq!(got, expected);
let filter = EventFilter::new().contract_addresses(vec![address]);
let got = format!("{filter}");
assert_eq!(got, expected);
assert_eq!(format!("{filter:?}"), got);
}
#[test]
fn display_with_multiple_addresses() {
let address_1 = address!("0x000000000000000000000000000000000000dEaD");
let address_2 = address!("0x0000000000000000000000000000000000000001");
let filter = EventFilter::new().contract_address(address_1).contract_address(address_2);
let got = format!("{filter}");
let expected = "EventFilter(contracts: [0x000000000000000000000000000000000000dEaD, 0x0000000000000000000000000000000000000001])";
assert_eq!(got, expected);
let filter = EventFilter::new().contract_addresses(vec![address_1, address_2]);
let got = format!("{filter}");
assert_eq!(got, expected);
assert_eq!(format!("{filter:?}"), got);
}
#[test]
fn display_single_event() {
let event = SomeContract::EventOne::SIGNATURE;
let filter = EventFilter::new().event(event);
let got = format!("{filter}");
let expected = "EventFilter(events: [EventOne()])";
assert_eq!(got, expected);
assert_eq!(format!("{filter:?}"), got);
}
#[test]
fn display_single_event_signature() {
let event_signature = SomeContract::EventOne::SIGNATURE_HASH;
let filter = EventFilter::new().event_signature(event_signature);
let got = format!("{filter}");
let expected = "EventFilter(event_signatures: [0xa08dd6fd0d644da5df33d075cb9256203802f6948ab81b87079960711810dc91])";
assert_eq!(got, expected);
assert_eq!(format!("{filter:?}"), got);
}
#[test]
fn display_multiple_events_with_address() {
let address = address!("0x000000000000000000000000000000000000dEaD");
let events = vec![SomeContract::EventOne::SIGNATURE, SomeContract::EventTwo::SIGNATURE];
let filter = EventFilter::new().contract_address(address).events(events.clone());
let got = format!("{filter}");
let expected = "EventFilter(contracts: [0x000000000000000000000000000000000000dEaD], events: [EventOne(), EventTwo()])";
assert_eq!(got, expected);
assert_eq!(format!("{filter:?}"), got);
}
#[test]
fn display_multiple_event_signatures_with_address() {
let address = address!("0x000000000000000000000000000000000000dEaD");
let event_signatures =
vec![SomeContract::EventOne::SIGNATURE_HASH, SomeContract::EventTwo::SIGNATURE_HASH];
let filter =
EventFilter::new().contract_address(address).event_signatures(event_signatures.clone());
let got = format!("{filter}");
let expected_1 = "EventFilter(contracts: [0x000000000000000000000000000000000000dEaD], event_signatures: [0x16eb4fc7651e068f1c31303645026f82d5fced11a8d5209bbf272072be23ddff, 0xa08dd6fd0d644da5df33d075cb9256203802f6948ab81b87079960711810dc91])";
let expected_2 = "EventFilter(contracts: [0x000000000000000000000000000000000000dEaD], event_signatures: [0xa08dd6fd0d644da5df33d075cb9256203802f6948ab81b87079960711810dc91, 0x16eb4fc7651e068f1c31303645026f82d5fced11a8d5209bbf272072be23ddff])";
assert!(
got == expected_1 || got == expected_2,
"got: {got},\nexpected_1: {expected_1},\nexpected_2: {expected_2}"
);
assert_eq!(format!("{filter:?}"), got);
}
#[test]
fn display_multiple_events_and_event_signatures() {
let events = vec![SomeContract::EventOne::SIGNATURE, SomeContract::EventTwo::SIGNATURE];
let event_signatures =
vec![SomeContract::EventOne::SIGNATURE_HASH, SomeContract::EventTwo::SIGNATURE_HASH];
let filter =
EventFilter::new().events(events.clone()).event_signatures(event_signatures.clone());
let got = format!("{filter}");
let expected_1 = "EventFilter(events: [EventOne(), EventTwo()], event_signatures: [0x16eb4fc7651e068f1c31303645026f82d5fced11a8d5209bbf272072be23ddff, 0xa08dd6fd0d644da5df33d075cb9256203802f6948ab81b87079960711810dc91])";
let expected_2 = "EventFilter(events: [EventOne(), EventTwo()], event_signatures: [0xa08dd6fd0d644da5df33d075cb9256203802f6948ab81b87079960711810dc91, 0x16eb4fc7651e068f1c31303645026f82d5fced11a8d5209bbf272072be23ddff])";
assert!(
got == expected_1 || got == expected_2,
"got: {got},\nexpected_1: {expected_1},\nexpected_2: {expected_2}"
);
assert_eq!(format!("{filter:?}"), got);
}
#[test]
fn display_multiple_events_and_event_signatures_with_addresses() {
let addresses = vec![
address!("0x000000000000000000000000000000000000dEaD"),
address!("0x0000000000000000000000000000000000000001"),
];
let events = vec![SomeContract::EventOne::SIGNATURE, SomeContract::EventTwo::SIGNATURE];
let event_signatures =
vec![SomeContract::EventOne::SIGNATURE_HASH, SomeContract::EventTwo::SIGNATURE_HASH];
let filter = EventFilter::new()
.contract_addresses(addresses)
.events(events.clone())
.event_signatures(event_signatures.clone());
let got = format!("{filter}");
let expected_1 = "EventFilter(contracts: [0x000000000000000000000000000000000000dEaD, 0x0000000000000000000000000000000000000001], events: [EventOne(), EventTwo()], event_signatures: [0x16eb4fc7651e068f1c31303645026f82d5fced11a8d5209bbf272072be23ddff, 0xa08dd6fd0d644da5df33d075cb9256203802f6948ab81b87079960711810dc91])";
let expected_2 = "EventFilter(contracts: [0x000000000000000000000000000000000000dEaD, 0x0000000000000000000000000000000000000001], events: [EventOne(), EventTwo()], event_signatures: [0xa08dd6fd0d644da5df33d075cb9256203802f6948ab81b87079960711810dc91, 0x16eb4fc7651e068f1c31303645026f82d5fced11a8d5209bbf272072be23ddff])";
assert!(
got == expected_1 || got == expected_2,
"got: {got},\nexpected_1: {expected_1},\nexpected_2: {expected_2}"
);
assert_eq!(format!("{filter:?}"), got);
}
#[test]
fn display_with_empty_address_vector_noop() {
let filter = EventFilter::new().contract_addresses(Vec::<Address>::new());
let got = format!("{filter}");
let expected = "EventFilter()";
assert_eq!(got, expected);
assert_eq!(format!("{filter:?}"), got);
}
#[test]
fn display_with_empty_events_vector_noop() {
let filter = EventFilter::new().events(Vec::<String>::new());
let got = format!("{filter}");
let expected = "EventFilter()";
assert_eq!(got, expected);
assert_eq!(format!("{filter:?}"), got);
}
#[test]
fn display_with_empty_event_signatures_vector_noop() {
let filter = EventFilter::new().event_signatures(Vec::<Topic>::new());
let got = format!("{filter}");
let expected = "EventFilter()";
assert_eq!(got, expected);
assert_eq!(format!("{filter:?}"), got);
}
}