11use std:: fmt:: { Debug , Display } ;
22
3- use alloy:: primitives:: Address ;
3+ use alloy:: {
4+ primitives:: { Address , keccak256} ,
5+ rpc:: types:: { Topic , ValueOrArray } ,
6+ } ;
47
58/// Type representing filters to apply when fetching events from the chain.
69///
@@ -23,19 +26,34 @@ use alloy::primitives::Address;
2326pub struct EventFilter {
2427 /// Contract address to filter events from. If None, events from all contracts will be tracked.
2528 pub ( crate ) contract_address : Option < Address > ,
26- /// Human-readable event signature, e.g. "Transfer(address,address,uint256)".
27- /// If None, all events from the specified contract(s) will be tracked.
29+ /// Human-readable event signatures, e.g. "Transfer(address,address,uint256)".
2830 pub ( crate ) events : Vec < String > ,
31+ /// Event signature hashes, e.g. `0x123...`.
32+ pub ( crate ) event_signatures : Topic ,
2933}
3034
3135impl Display for EventFilter {
3236 fn fmt ( & self , f : & mut std:: fmt:: Formatter < ' _ > ) -> std:: fmt:: Result {
33- let address =
34- self . contract_address . map_or ( "all contracts" . to_string ( ) , |addr| format ! ( "{addr:?}" ) ) ;
35- let events =
36- if self . events . is_empty ( ) { "all events" . to_string ( ) } else { self . events . join ( ", " ) } ;
37+ let mut content = vec ! [ ] ;
38+ if let Some ( address) = & self . contract_address {
39+ content. push ( format ! ( "contract: {address}" ) ) ;
40+ }
41+ if !self . events . is_empty ( ) {
42+ content. push ( format ! ( "events: [{}]" , self . events. join( ", " ) ) ) ;
43+ }
44+ if !self . event_signatures . is_empty ( ) {
45+ // No guarantee the order of values returned by `Topic`
46+ let value_or_array = self . event_signatures . to_value_or_array ( ) . unwrap ( ) ;
47+ let event_signatures = match value_or_array {
48+ ValueOrArray :: Value ( value) => format ! ( "{value}" ) ,
49+ ValueOrArray :: Array ( arr) => {
50+ arr. iter ( ) . map ( |t| format ! ( "{t}" ) ) . collect :: < Vec < _ > > ( ) . join ( ", " )
51+ }
52+ } ;
53+ content. push ( format ! ( "event_signatures: [{event_signatures}]" ) ) ;
54+ }
3755
38- write ! ( f, "EventFilter(contract: {address}, events: {events})" )
56+ write ! ( f, "EventFilter({})" , content . join ( ", " ) )
3957 }
4058}
4159
@@ -61,106 +79,220 @@ impl EventFilter {
6179 }
6280
6381 /// Sets the event signature to filter specific events.
64- /// If not set, all events from the specified contract(s) will be tracked.
82+ /// If neither events nor event signature hashes are set, all events from the specified contract(s) will be tracked.
6583 #[ must_use]
6684 pub fn with_event ( mut self , event : impl Into < String > ) -> Self {
67- self . events . push ( event. into ( ) ) ;
85+ let event = event. into ( ) ;
86+ if !event. is_empty ( ) {
87+ self . events . push ( event) ;
88+ }
6889 self
6990 }
7091
71- /// Sets the event signature to filter specific events.
72- /// If not set, all events from the specified contract(s) will be tracked.
92+ /// Sets the event signatures to filter specific events.
93+ /// If neither events nor event signature hashes are set, all events from the specified contract(s) will be tracked.
94+ #[ must_use]
95+ pub fn with_events ( mut self , events : impl IntoIterator < Item = impl Into < String > > ) -> Self {
96+ self . events . extend ( events. into_iter ( ) . map ( Into :: into) ) ;
97+ self
98+ }
99+
100+ /// Sets the event signature hash to filter specific events.
101+ /// If neither event signature hashes nor events are set, all events from the specified contract(s) will be tracked.
73102 #[ must_use]
74- pub fn with_events ( mut self , events : impl IntoIterator < Item = String > ) -> Self {
75- self . events . extend ( events ) ;
103+ pub fn with_event_signature ( mut self , event_signature : impl Into < Topic > ) -> Self {
104+ self . event_signatures = self . event_signatures . extend ( event_signature . into ( ) ) ;
76105 self
77106 }
107+
108+ /// Sets the event signature hashes to filter specific events.
109+ /// If neither event signature hashes nor events are set, all events from the specified contract(s) will be tracked.
110+ #[ must_use]
111+ pub fn with_event_signatures (
112+ mut self ,
113+ event_signatures : impl IntoIterator < Item = impl Into < Topic > > ,
114+ ) -> Self {
115+ for event_signature in event_signatures {
116+ self . event_signatures = self . event_signatures . extend ( event_signature) ;
117+ }
118+ self
119+ }
120+
121+ /// Returns a [`Topic`] containing all event signature hashes.
122+ /// If neither events nor event signature hashes are set, an empty [`Topic`] is returned.
123+ #[ must_use]
124+ pub fn all_events ( & self ) -> Topic {
125+ let events = self . events . iter ( ) . map ( |e| keccak256 ( e. as_bytes ( ) ) ) . collect :: < Vec < _ > > ( ) ;
126+ let sigs = self . event_signatures . clone ( ) ;
127+ let sigs = sigs. extend ( events) ;
128+ sigs
129+ }
78130}
79131
80132#[ cfg( test) ]
81133mod tests {
82134 use super :: EventFilter ;
83- use alloy:: primitives:: { Address , address } ;
135+ use alloy:: { primitives:: address , sol , sol_types :: SolEvent } ;
84136
85- fn addr_str ( addr : Address ) -> String {
86- format ! ( "{:?}" , addr)
137+ sol ! {
138+ contract SomeContract {
139+ event EventOne ( ) ;
140+ event EventTwo ( ) ;
141+ }
87142 }
88143
89144 #[ test]
90145 fn display_default_no_address_no_events ( ) {
91146 let filter = EventFilter :: new ( ) ;
92- let got = format ! ( "{}" , filter ) ;
93- let expected = "EventFilter(contract: all contracts, events: all events )" ;
147+ let got = format ! ( "{filter}" ) ;
148+ let expected = "EventFilter()" ;
94149 assert_eq ! ( got, expected) ;
95150
96151 // Debug should equal Display
97- assert_eq ! ( format!( "{:?}" , filter ) , got) ;
152+ assert_eq ! ( format!( "{filter :?}" ) , got) ;
98153 }
99154
100155 #[ test]
101- fn display_with_address_no_events ( ) {
102- let address = address ! ( "0xd8dA6BF26964af9d7eed9e03e53415d37aa96045 " ) ;
156+ fn display_with_address ( ) {
157+ let address = address ! ( "0x000000000000000000000000000000000000dEaD " ) ;
103158 let filter = EventFilter :: new ( ) . with_contract_address ( address) ;
104- let got = format ! ( "{}" , filter ) ;
105- let expected = format ! ( "EventFilter(contract: {}, events: all events)" , addr_str ( address ) ) ;
159+ let got = format ! ( "{filter}" ) ;
160+ let expected = format ! ( "EventFilter(contract: 0x000000000000000000000000000000000000dEaD)" ) ;
106161 assert_eq ! ( got, expected) ;
107162
108163 // Debug should equal Display
109- assert_eq ! ( format!( "{:?}" , filter ) , got) ;
164+ assert_eq ! ( format!( "{filter :?}" ) , got) ;
110165 }
111166
112167 #[ test]
113- fn display_single_event_no_address ( ) {
114- let event = "Transfer(address,address,uint256)" ;
168+ fn display_single_event ( ) {
169+ let event = SomeContract :: EventOne :: SIGNATURE ;
115170 let filter = EventFilter :: new ( ) . with_event ( event) ;
116- let got = format ! ( "{}" , filter) ;
117- let expected = format ! ( "EventFilter(contract: all contracts, events: {})" , event) ;
171+ let got = format ! ( "{filter}" ) ;
172+ let expected = format ! ( "EventFilter(events: [EventOne()])" ) ;
173+ assert_eq ! ( got, expected) ;
174+
175+ // Debug should equal Display
176+ assert_eq ! ( format!( "{filter:?}" ) , got) ;
177+ }
178+
179+ #[ test]
180+ fn display_single_event_signature ( ) {
181+ let event_signature = SomeContract :: EventOne :: SIGNATURE_HASH ;
182+ let filter = EventFilter :: new ( ) . with_event_signature ( event_signature) ;
183+ let got = format ! ( "{filter}" ) ;
184+ let expected = format ! (
185+ "EventFilter(event_signatures: [0xa08dd6fd0d644da5df33d075cb9256203802f6948ab81b87079960711810dc91])"
186+ ) ;
118187 assert_eq ! ( got, expected) ;
119188
120189 // Debug should equal Display
121- assert_eq ! ( format!( "{:?}" , filter ) , got) ;
190+ assert_eq ! ( format!( "{filter :?}" ) , got) ;
122191 }
123192
124193 #[ test]
125194 fn display_multiple_events_with_address ( ) {
126195 let address = address ! ( "0x000000000000000000000000000000000000dEaD" ) ;
127- let events = vec ! [
128- "Transfer(address,address,uint256)" . to_string( ) ,
129- "Approval(address,address,uint256)" . to_string( ) ,
130- "Sync(uint112,uint112)" . to_string( ) ,
131- ] ;
196+ let events = vec ! [ SomeContract :: EventOne :: SIGNATURE , SomeContract :: EventTwo :: SIGNATURE ] ;
132197 let filter = EventFilter :: new ( ) . with_contract_address ( address) . with_events ( events. clone ( ) ) ;
133198
134- let got = format ! ( "{}" , filter) ;
135- let expected =
136- format ! ( "EventFilter(contract: {}, events: {})" , addr_str( address) , events. join( ", " ) ) ;
199+ let got = format ! ( "{filter}" ) ;
200+ let expected = format ! (
201+ "EventFilter(contract: 0x000000000000000000000000000000000000dEaD, events: [EventOne(), EventTwo()])"
202+ ) ;
137203 assert_eq ! ( got, expected) ;
138204
139205 // Debug should equal Display
140- assert_eq ! ( format!( "{:?}" , filter ) , got) ;
206+ assert_eq ! ( format!( "{filter :?}" ) , got) ;
141207 }
142208
143209 #[ test]
144- fn display_with_empty_events_vector_noop ( ) {
145- // Providing an empty events vector should behave as if no events were set.
146- let filter = EventFilter :: new ( ) . with_events ( Vec :: < String > :: new ( ) ) ;
147- let got = format ! ( "{}" , filter) ;
148- let expected = "EventFilter(contract: all contracts, events: all events)" ;
149- assert_eq ! ( got, expected) ;
210+ fn display_multiple_event_signatures_with_address ( ) {
211+ let address = address ! ( "0x000000000000000000000000000000000000dEaD" ) ;
212+ let event_signatures =
213+ vec ! [ SomeContract :: EventOne :: SIGNATURE_HASH , SomeContract :: EventTwo :: SIGNATURE_HASH ] ;
214+ let filter = EventFilter :: new ( )
215+ . with_contract_address ( address)
216+ . with_event_signatures ( event_signatures. clone ( ) ) ;
217+
218+ let got = format ! ( "{filter}" ) ;
219+ // `Topic` doesn't guarantee the order of values it returns
220+ let expected_1 = format ! (
221+ "EventFilter(contract: 0x000000000000000000000000000000000000dEaD, event_signatures: [0x16eb4fc7651e068f1c31303645026f82d5fced11a8d5209bbf272072be23ddff, 0xa08dd6fd0d644da5df33d075cb9256203802f6948ab81b87079960711810dc91])"
222+ ) ;
223+ let expected_2 = format ! (
224+ "EventFilter(contract: 0x000000000000000000000000000000000000dEaD, event_signatures: [0xa08dd6fd0d644da5df33d075cb9256203802f6948ab81b87079960711810dc91, 0x16eb4fc7651e068f1c31303645026f82d5fced11a8d5209bbf272072be23ddff])"
225+ ) ;
226+ assert ! (
227+ got == expected_1 || got == expected_2,
228+ "got: {got},\n expected_1: {expected_1},\n expected_2: {expected_2}"
229+ ) ;
230+
231+ // Debug should equal Display
232+ assert_eq ! ( format!( "{filter:?}" ) , got) ;
233+ }
234+
235+ #[ test]
236+ fn display_multiple_events_and_event_signatures ( ) {
237+ let events = vec ! [ SomeContract :: EventOne :: SIGNATURE , SomeContract :: EventTwo :: SIGNATURE ] ;
238+ let event_signatures =
239+ vec ! [ SomeContract :: EventOne :: SIGNATURE_HASH , SomeContract :: EventTwo :: SIGNATURE_HASH ] ;
240+ let filter = EventFilter :: new ( )
241+ . with_events ( events. clone ( ) )
242+ . with_event_signatures ( event_signatures. clone ( ) ) ;
243+
244+ let got = format ! ( "{filter}" ) ;
245+ let expected_1 = format ! (
246+ "EventFilter(events: [EventOne(), EventTwo()], event_signatures: [0x16eb4fc7651e068f1c31303645026f82d5fced11a8d5209bbf272072be23ddff, 0xa08dd6fd0d644da5df33d075cb9256203802f6948ab81b87079960711810dc91])" ,
247+ ) ;
248+ let expected_2 = format ! (
249+ "EventFilter(events: [EventOne(), EventTwo()], event_signatures: [0xa08dd6fd0d644da5df33d075cb9256203802f6948ab81b87079960711810dc91, 0x16eb4fc7651e068f1c31303645026f82d5fced11a8d5209bbf272072be23ddff])" ,
250+ ) ;
251+ assert ! (
252+ got == expected_1 || got == expected_2,
253+ "got: {got},\n expected_1: {expected_1},\n expected_2: {expected_2}"
254+ ) ;
150255
151256 // Debug should equal Display
152- assert_eq ! ( format!( "{:?}" , filter ) , got) ;
257+ assert_eq ! ( format!( "{filter :?}" ) , got) ;
153258 }
154259
155260 #[ test]
156- fn display_with_empty_event_string_prints_empty ( ) {
157- // An explicitly empty event string results in an empty events field when joined.
158- let filter = EventFilter :: new ( ) . with_event ( "" ) ;
159- let got = format ! ( "{}" , filter) ;
160- let expected = "EventFilter(contract: all contracts, events: )" ;
261+ fn display_multiple_events_and_event_signatures_with_address ( ) {
262+ let address = address ! ( "0x000000000000000000000000000000000000dEaD" ) ;
263+ let events = vec ! [ SomeContract :: EventOne :: SIGNATURE , SomeContract :: EventTwo :: SIGNATURE ] ;
264+ let event_signatures =
265+ vec ! [ SomeContract :: EventOne :: SIGNATURE_HASH , SomeContract :: EventTwo :: SIGNATURE_HASH ] ;
266+ let filter = EventFilter :: new ( )
267+ . with_contract_address ( address)
268+ . with_events ( events. clone ( ) )
269+ . with_event_signatures ( event_signatures. clone ( ) ) ;
270+
271+ let got = format ! ( "{filter}" ) ;
272+ let expected_1 = format ! (
273+ "EventFilter(contract: 0x000000000000000000000000000000000000dEaD, events: [EventOne(), EventTwo()], event_signatures: [0x16eb4fc7651e068f1c31303645026f82d5fced11a8d5209bbf272072be23ddff, 0xa08dd6fd0d644da5df33d075cb9256203802f6948ab81b87079960711810dc91])" ,
274+ ) ;
275+ let expected_2 = format ! (
276+ "EventFilter(contract: 0x000000000000000000000000000000000000dEaD, events: [EventOne(), EventTwo()], event_signatures: [0xa08dd6fd0d644da5df33d075cb9256203802f6948ab81b87079960711810dc91, 0x16eb4fc7651e068f1c31303645026f82d5fced11a8d5209bbf272072be23ddff])" ,
277+ ) ;
278+ assert ! (
279+ got == expected_1 || got == expected_2,
280+ "got: {got},\n expected_1: {expected_1},\n expected_2: {expected_2}"
281+ ) ;
282+
283+ // Debug should equal Display
284+ assert_eq ! ( format!( "{filter:?}" ) , got) ;
285+ }
286+
287+ #[ test]
288+ fn display_with_empty_events_vector_noop ( ) {
289+ // Providing an empty events vector should behave as if no events were set.
290+ let filter = EventFilter :: new ( ) . with_events ( Vec :: < String > :: new ( ) ) ;
291+ let got = format ! ( "{filter}" ) ;
292+ let expected = "EventFilter()" ;
161293 assert_eq ! ( got, expected) ;
162294
163295 // Debug should equal Display
164- assert_eq ! ( format!( "{:?}" , filter ) , got) ;
296+ assert_eq ! ( format!( "{filter :?}" ) , got) ;
165297 }
166298}
0 commit comments