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