11use ethers_core:: types:: Bytes ;
22use serde:: { Deserialize , Serialize } ;
3+ use serde_json:: Value ;
34use serde_with:: formats:: PreferOne ;
45use serde_with:: { serde_as, OneOrMany } ;
56
@@ -36,6 +37,40 @@ pub struct LogFilter {
3637 pub topics : Option < Vec < Option < LogTopicFilter > > > ,
3738}
3839
40+ impl TryFrom < Value > for LogFilter {
41+ type Error = jsonrpc_core:: Error ;
42+
43+ fn try_from ( value : Value ) -> Result < Self , Self :: Error > {
44+ if let Value :: Object ( ref map) = value {
45+ // According to documentation if `blockHash` property is specified then `fromBlock` and `toBlock` shouldn't be specified
46+ if map. contains_key ( "blockHash" )
47+ && ( map. contains_key ( "fromBlock" ) || map. contains_key ( "toBlock" ) )
48+ {
49+ Err ( Self :: Error :: invalid_params (
50+ "'blockHash' property cannot be used with 'fromBlock' or 'toBlock'" ,
51+ ) )
52+ } else {
53+ let mut filter: LogFilter =
54+ serde_json:: from_value ( value) . map_err ( |_| Self :: Error :: parse_error ( ) ) ?;
55+
56+ // Empty block filter can be serialized as `block_filter: BlockFilter::Bounded(from_block: None, to_block:None)`
57+ // That could be OK for us because it is equivalent to `block_filter: None`, but it's better to disambiguate things
58+ if let Some ( BlockFilter :: Bounded {
59+ from_block : None ,
60+ to_block : None ,
61+ } ) = filter. block_filter
62+ {
63+ filter. block_filter = None ;
64+ }
65+
66+ Ok ( filter)
67+ }
68+ } else {
69+ Err ( Self :: Error :: invalid_params ( "invalid json value" ) )
70+ }
71+ }
72+ }
73+
3974#[ derive( Clone , Debug , PartialEq , Eq , Serialize , Deserialize ) ]
4075#[ serde( rename_all = "camelCase" ) ]
4176/// Transaction's log entry.
@@ -57,3 +92,205 @@ pub struct TransactionLog {
5792 /// Log's Topics.
5893 pub topics : Vec < H256 > ,
5994}
95+
96+ #[ cfg( test) ]
97+ mod tests {
98+ use serde_json:: json;
99+
100+ use super :: * ;
101+
102+ const BLOCK_HASH_1 : & str = "f43869e67c02c57d1f9a07bb897b54bec1cfa1feb704d91a2ee087566de5df2c" ;
103+ const TOPIC_1 : & str = "cc6a069bf885d8cf2fb456ca33db48ab7d5e3df1e6504a18e7899a16d604f5c6" ;
104+ const TOPIC_2 : & str = "e4058f2da8dda0b1ffb454bb7d121c1498dcfc4446a3d86b7c03e27b34e29345" ;
105+ const ADDRESS : & str = "7fafd954cbcfd683304cd9be0a85848cbbb1c13d" ;
106+
107+ fn get_block_hash_1_str ( ) -> String {
108+ format ! ( "0x{BLOCK_HASH_1}" )
109+ }
110+
111+ fn get_block_hash_1 ( ) -> H256 {
112+ H256 :: from_hex_str ( BLOCK_HASH_1 ) . unwrap ( )
113+ }
114+
115+ fn get_topic_1 ( ) -> H256 {
116+ H256 :: from_hex_str ( TOPIC_1 ) . unwrap ( )
117+ }
118+
119+ fn get_topic_1_str ( ) -> String {
120+ format ! ( "0x{TOPIC_1}" )
121+ }
122+
123+ fn get_topic_2 ( ) -> H256 {
124+ H256 :: from_hex_str ( TOPIC_2 ) . unwrap ( )
125+ }
126+
127+ fn get_topic_2_str ( ) -> String {
128+ format ! ( "0x{TOPIC_2}" )
129+ }
130+
131+ fn get_address_str ( ) -> String {
132+ format ! ( "0x{ADDRESS}" )
133+ }
134+
135+ fn get_address_1 ( ) -> H160 {
136+ H160 :: from_hex_str ( ADDRESS ) . unwrap ( )
137+ }
138+
139+ #[ test]
140+ fn test_log_filter_deserialization_fail ( ) {
141+ assert ! ( LogFilter :: try_from( json!( [ ] ) ) . is_err( ) ) ;
142+ assert ! ( LogFilter :: try_from( json!( "str" ) ) . is_err( ) ) ;
143+ assert ! ( LogFilter :: try_from( json!( 42 ) ) . is_err( ) ) ;
144+ assert ! ( LogFilter :: try_from(
145+ json!( { "blockHash" : get_block_hash_1_str( ) , "fromBlock" : "earliest" } )
146+ )
147+ . is_err( ) ) ;
148+ assert ! ( LogFilter :: try_from(
149+ json!( { "blockHash" : get_block_hash_1_str( ) , "toBlock" : "0x01" } )
150+ )
151+ . is_err( ) ) ;
152+ }
153+
154+ #[ test]
155+ fn test_log_filter_deserialization_empty ( ) {
156+ let filter = LogFilter :: try_from ( json ! ( { } ) ) . unwrap ( ) ;
157+ let expected_filter = Default :: default ( ) ;
158+ assert_eq ! ( filter, expected_filter) ;
159+ }
160+
161+ #[ test]
162+ fn test_log_filter_deserialization_block_filter ( ) {
163+ let filter =
164+ LogFilter :: try_from ( json ! ( { "fromBlock" : "earliest" , "toBlock" : "0x01" } ) ) . unwrap ( ) ;
165+
166+ let expected_filter = LogFilter {
167+ block_filter : Some ( BlockFilter :: Bounded {
168+ from_block : Some ( BlockNumber :: Earliest ) ,
169+ to_block : Some ( BlockNumber :: Number ( U64 :: one ( ) ) ) ,
170+ } ) ,
171+ ..Default :: default ( )
172+ } ;
173+ assert_eq ! ( filter, expected_filter) ;
174+
175+ let filter = LogFilter :: try_from ( json ! ( { "blockHash" : get_block_hash_1_str( ) } ) ) . unwrap ( ) ;
176+ let expected_filter = LogFilter {
177+ block_filter : Some ( BlockFilter :: Exact {
178+ block_hash : get_block_hash_1 ( ) ,
179+ } ) ,
180+ ..Default :: default ( )
181+ } ;
182+ assert_eq ! ( filter, expected_filter) ;
183+ }
184+
185+ #[ test]
186+ fn test_log_filter_deserialization_address ( ) {
187+ let filter = LogFilter :: try_from ( json ! ( {
188+ "address" : [ ] ,
189+ } ) )
190+ . unwrap ( ) ;
191+ let expected_filter = LogFilter {
192+ address : Some ( LogAddressFilter ( vec ! [ ] ) ) ,
193+ ..Default :: default ( )
194+ } ;
195+ assert_eq ! ( filter, expected_filter) ;
196+
197+ let filter = LogFilter :: try_from ( json ! ( {
198+ "address" : [ get_address_str( ) ] ,
199+ } ) )
200+ . unwrap ( ) ;
201+ let expected_filter = LogFilter {
202+ address : Some ( LogAddressFilter ( vec ! [ get_address_1( ) ] ) ) ,
203+ ..Default :: default ( )
204+ } ;
205+ assert_eq ! ( filter, expected_filter) ;
206+
207+ let filter = LogFilter :: try_from ( json ! ( {
208+ "address" : [ get_address_str( ) , get_address_str( ) ] ,
209+ } ) )
210+ . unwrap ( ) ;
211+ let expected_filter = LogFilter {
212+ address : Some ( LogAddressFilter ( vec ! [ get_address_1( ) , get_address_1( ) ] ) ) ,
213+ ..Default :: default ( )
214+ } ;
215+ assert_eq ! ( filter, expected_filter) ;
216+ }
217+
218+ #[ test]
219+ fn test_log_filter_deserialization_topics ( ) {
220+ let filter = LogFilter :: try_from ( json ! ( {
221+ "topics" : [ ] ,
222+ } ) )
223+ . unwrap ( ) ;
224+ let expected_filter = LogFilter {
225+ topics : Some ( vec ! [ ] ) ,
226+ ..Default :: default ( )
227+ } ;
228+ assert_eq ! ( filter, expected_filter) ;
229+
230+ let filter = LogFilter :: try_from ( json ! ( {
231+ "topics" : [ null] ,
232+ } ) )
233+ . unwrap ( ) ;
234+ let expected_filter = LogFilter {
235+ topics : Some ( vec ! [ None ] ) ,
236+ ..Default :: default ( )
237+ } ;
238+ assert_eq ! ( filter, expected_filter) ;
239+
240+ let filter = LogFilter :: try_from ( json ! ( {
241+ "topics" : [ [ get_topic_1_str( ) ] ] ,
242+ } ) )
243+ . unwrap ( ) ;
244+ let expected_filter = LogFilter {
245+ topics : Some ( vec ! [ Some ( LogTopicFilter ( vec![ get_topic_1( ) ] ) ) ] ) ,
246+ ..Default :: default ( )
247+ } ;
248+ assert_eq ! ( filter, expected_filter) ;
249+
250+ let filter = LogFilter :: try_from ( json ! ( {
251+ "topics" : [ [ get_topic_1_str( ) ] , null] ,
252+ } ) )
253+ . unwrap ( ) ;
254+ let expected_filter = LogFilter {
255+ topics : Some ( vec ! [ Some ( LogTopicFilter ( vec![ get_topic_1( ) ] ) ) , None ] ) ,
256+ ..Default :: default ( )
257+ } ;
258+ assert_eq ! ( filter, expected_filter) ;
259+
260+ let filter = LogFilter :: try_from ( json ! ( {
261+ "topics" : [ [ get_topic_1_str( ) ] , null, [ get_topic_1_str( ) , get_topic_2_str( ) ] ] ,
262+ } ) )
263+ . unwrap ( ) ;
264+ let expected_filter = LogFilter {
265+ topics : Some ( vec ! [
266+ Some ( LogTopicFilter ( vec![ get_topic_1( ) ] ) ) ,
267+ None ,
268+ Some ( LogTopicFilter ( vec![ get_topic_1( ) , get_topic_2( ) ] ) ) ,
269+ ] ) ,
270+ ..Default :: default ( )
271+ } ;
272+ assert_eq ! ( filter, expected_filter) ;
273+ }
274+
275+ #[ test]
276+ fn test_log_filter_deserialization_combine ( ) {
277+ let filter = LogFilter :: try_from ( json ! ( {
278+ "blockHash" : get_block_hash_1_str( ) ,
279+ "address" : [ get_address_str( ) ] ,
280+ "topics" : [ null, [ get_topic_1_str( ) ] , [ get_topic_1_str( ) , get_topic_2_str( ) ] ] ,
281+ } ) )
282+ . unwrap ( ) ;
283+ let expected_filter = LogFilter {
284+ block_filter : Some ( BlockFilter :: Exact {
285+ block_hash : get_block_hash_1 ( ) ,
286+ } ) ,
287+ address : Some ( LogAddressFilter ( vec ! [ get_address_1( ) ] ) ) ,
288+ topics : Some ( vec ! [
289+ None ,
290+ Some ( LogTopicFilter ( vec![ get_topic_1( ) ] ) ) ,
291+ Some ( LogTopicFilter ( vec![ get_topic_1( ) , get_topic_2( ) ] ) ) ,
292+ ] ) ,
293+ } ;
294+ assert_eq ! ( filter, expected_filter) ;
295+ }
296+ }
0 commit comments