1use crate::types::{BlockNumber, Bytes, Index, H160, H256, U256, U64};
2use serde::{Deserialize, Serialize, Serializer};
3
4#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
6pub struct Log {
7 pub address: H160,
9 pub topics: Vec<H256>,
11 pub data: Bytes,
13 #[serde(rename = "blockHash")]
15 pub block_hash: Option<H256>,
16 #[serde(rename = "blockNumber")]
18 pub block_number: Option<U64>,
19 #[serde(rename = "transactionHash")]
21 pub transaction_hash: Option<H256>,
22 #[serde(rename = "transactionIndex")]
24 pub transaction_index: Option<Index>,
25 #[serde(rename = "logIndex")]
27 pub log_index: Option<U256>,
28 #[serde(rename = "transactionLogIndex")]
30 pub transaction_log_index: Option<U256>,
31 #[serde(rename = "logType")]
33 pub log_type: Option<String>,
34 pub removed: Option<bool>,
36}
37
38impl Log {
39 pub fn is_removed(&self) -> bool {
41 match self.removed {
42 Some(val_removed) => return val_removed,
43 None => (),
44 }
45 match self.log_type {
46 Some(ref val_log_type) => {
47 if val_log_type == "removed" {
48 return true;
49 }
50 }
51 None => (),
52 }
53 false
54 }
55}
56
57#[derive(Default, Debug, PartialEq, Clone)]
58struct ValueOrArray<T>(Vec<T>);
59
60impl<T> Serialize for ValueOrArray<T>
61where
62 T: Serialize,
63{
64 fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
65 where
66 S: Serializer,
67 {
68 match self.0.len() {
69 0 => serializer.serialize_none(),
70 1 => Serialize::serialize(&self.0[0], serializer),
71 _ => Serialize::serialize(&self.0, serializer),
72 }
73 }
74}
75
76#[derive(Default, Debug, PartialEq, Clone, Serialize)]
78pub struct Filter {
79 #[serde(rename = "fromBlock", skip_serializing_if = "Option::is_none")]
81 from_block: Option<BlockNumber>,
82 #[serde(rename = "toBlock", skip_serializing_if = "Option::is_none")]
84 to_block: Option<BlockNumber>,
85 #[serde(rename = "blockHash", skip_serializing_if = "Option::is_none")]
87 block_hash: Option<H256>,
88 #[serde(skip_serializing_if = "Option::is_none")]
90 address: Option<ValueOrArray<H160>>,
91 #[serde(skip_serializing_if = "Option::is_none")]
93 topics: Option<Vec<Option<ValueOrArray<H256>>>>,
94 #[serde(skip_serializing_if = "Option::is_none")]
96 limit: Option<usize>,
97}
98
99#[derive(Default, Clone)]
101pub struct FilterBuilder {
102 filter: Filter,
103}
104
105impl FilterBuilder {
106 pub fn from_block(mut self, block: BlockNumber) -> Self {
110 self.filter.block_hash = None;
111 self.filter.from_block = Some(block);
112 self
113 }
114
115 pub fn to_block(mut self, block: BlockNumber) -> Self {
118 self.filter.block_hash = None;
119 self.filter.to_block = Some(block);
120 self
121 }
122
123 pub fn block_hash(mut self, hash: H256) -> Self {
127 self.filter.from_block = None;
128 self.filter.to_block = None;
129 self.filter.block_hash = Some(hash);
130 self
131 }
132
133 pub fn address(mut self, address: Vec<H160>) -> Self {
135 self.filter.address = Some(ValueOrArray(address));
136 self
137 }
138
139 pub fn topics(
141 mut self,
142 topic1: Option<Vec<H256>>,
143 topic2: Option<Vec<H256>>,
144 topic3: Option<Vec<H256>>,
145 topic4: Option<Vec<H256>>,
146 ) -> Self {
147 let mut topics = vec![topic1, topic2, topic3, topic4]
148 .into_iter()
149 .rev()
150 .skip_while(Option::is_none)
151 .map(|option| option.map(ValueOrArray))
152 .collect::<Vec<_>>();
153 topics.reverse();
154
155 self.filter.topics = Some(topics);
156 self
157 }
158
159 pub fn topic_filter(self, topic_filter: ethabi::TopicFilter) -> Self {
161 self.topics(
162 topic_to_option(topic_filter.topic0),
163 topic_to_option(topic_filter.topic1),
164 topic_to_option(topic_filter.topic2),
165 topic_to_option(topic_filter.topic3),
166 )
167 }
168
169 pub fn limit(mut self, limit: usize) -> Self {
171 self.filter.limit = Some(limit);
172 self
173 }
174
175 pub fn build(&self) -> Filter {
177 self.filter.clone()
178 }
179}
180
181fn topic_to_option<T>(topic: ethabi::Topic<T>) -> Option<Vec<T>> {
183 match topic {
184 ethabi::Topic::Any => None,
185 ethabi::Topic::OneOf(v) => Some(v),
186 ethabi::Topic::This(t) => Some(vec![t]),
187 }
188}
189
190#[cfg(test)]
191mod tests {
192 use crate::types::{
193 log::{FilterBuilder, Log},
194 Address, H160, H256,
195 };
196 use hex_literal::hex;
197
198 #[test]
199 fn is_removed_removed_true() {
200 let log = Log {
201 address: Address::from_low_u64_be(1),
202 topics: vec![],
203 data: hex!("").into(),
204 block_hash: Some(H256::from_low_u64_be(2)),
205 block_number: Some(1.into()),
206 transaction_hash: Some(H256::from_low_u64_be(3)),
207 transaction_index: Some(0.into()),
208 log_index: Some(0.into()),
209 transaction_log_index: Some(0.into()),
210 log_type: None,
211 removed: Some(true),
212 };
213 assert!(log.is_removed());
214 }
215
216 #[test]
217 fn is_removed_removed_false() {
218 let log = Log {
219 address: H160::from_low_u64_be(1),
220 topics: vec![],
221 data: hex!("").into(),
222 block_hash: Some(H256::from_low_u64_be(2)),
223 block_number: Some(1.into()),
224 transaction_hash: Some(H256::from_low_u64_be(3)),
225 transaction_index: Some(0.into()),
226 log_index: Some(0.into()),
227 transaction_log_index: Some(0.into()),
228 log_type: None,
229 removed: Some(false),
230 };
231 assert!(!log.is_removed());
232 }
233
234 #[test]
235 fn is_removed_log_type_removed() {
236 let log = Log {
237 address: Address::from_low_u64_be(1),
238 topics: vec![],
239 data: hex!("").into(),
240 block_hash: Some(H256::from_low_u64_be(2)),
241 block_number: Some(1.into()),
242 transaction_hash: Some(H256::from_low_u64_be(3)),
243 transaction_index: Some(0.into()),
244 log_index: Some(0.into()),
245 transaction_log_index: Some(0.into()),
246 log_type: Some("removed".into()),
247 removed: None,
248 };
249 assert!(log.is_removed());
250 }
251
252 #[test]
253 fn is_removed_log_type_mined() {
254 let log = Log {
255 address: Address::from_low_u64_be(1),
256 topics: vec![],
257 data: hex!("").into(),
258 block_hash: Some(H256::from_low_u64_be(2)),
259 block_number: Some(1.into()),
260 transaction_hash: Some(H256::from_low_u64_be(3)),
261 transaction_index: Some(0.into()),
262 log_index: Some(0.into()),
263 transaction_log_index: Some(0.into()),
264 log_type: Some("mined".into()),
265 removed: None,
266 };
267 assert!(!log.is_removed());
268 }
269
270 #[test]
271 fn is_removed_log_type_and_removed_none() {
272 let log = Log {
273 address: Address::from_low_u64_be(1),
274 topics: vec![],
275 data: hex!("").into(),
276 block_hash: Some(H256::from_low_u64_be(2)),
277 block_number: Some(1.into()),
278 transaction_hash: Some(H256::from_low_u64_be(3)),
279 transaction_index: Some(0.into()),
280 log_index: Some(0.into()),
281 transaction_log_index: Some(0.into()),
282 log_type: None,
283 removed: None,
284 };
285 assert!(!log.is_removed());
286 }
287
288 #[test]
289 fn does_topic_filter_set_topics_correctly() {
290 let topic_filter = ethabi::TopicFilter {
291 topic0: ethabi::Topic::This(H256::from_low_u64_be(3)),
292 topic1: ethabi::Topic::OneOf(vec![5, 8].into_iter().map(H256::from_low_u64_be).collect()),
293 topic2: ethabi::Topic::This(H256::from_low_u64_be(13)),
294 topic3: ethabi::Topic::Any,
295 };
296 let filter0 = FilterBuilder::default().topic_filter(topic_filter).build();
297 let filter1 = FilterBuilder::default()
298 .topics(
299 Some(vec![3].into_iter().map(H256::from_low_u64_be).collect()),
300 Some(vec![5, 8].into_iter().map(H256::from_low_u64_be).collect()),
301 Some(vec![13].into_iter().map(H256::from_low_u64_be).collect()),
302 None,
303 )
304 .build();
305 assert_eq!(filter0, filter1);
306 }
307}