pink_web3/types/
log.rs

1use crate::prelude::*;
2use crate::types::{BlockNumber, Bytes, Index, H160, H256, U256, U64};
3use serde::{Deserialize, Serialize, Serializer};
4
5/// A log produced by a transaction.
6#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
7pub struct Log {
8    /// H160
9    pub address: H160,
10    /// Topics
11    pub topics: Vec<H256>,
12    /// Data
13    pub data: Bytes,
14    /// Block Hash
15    #[serde(rename = "blockHash")]
16    pub block_hash: Option<H256>,
17    /// Block Number
18    #[serde(rename = "blockNumber")]
19    pub block_number: Option<U64>,
20    /// Transaction Hash
21    #[serde(rename = "transactionHash")]
22    pub transaction_hash: Option<H256>,
23    /// Transaction Index
24    #[serde(rename = "transactionIndex")]
25    pub transaction_index: Option<Index>,
26    /// Log Index in Block
27    #[serde(rename = "logIndex")]
28    pub log_index: Option<U256>,
29    /// Log Index in Transaction
30    #[serde(rename = "transactionLogIndex")]
31    pub transaction_log_index: Option<U256>,
32    /// Log Type
33    #[serde(rename = "logType")]
34    pub log_type: Option<String>,
35    /// Removed
36    pub removed: Option<bool>,
37}
38
39impl Log {
40    /// Returns true if the log has been removed.
41    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/// Filter
78#[derive(Default, Debug, PartialEq, Clone, Serialize)]
79pub struct Filter {
80    /// From Block
81    #[serde(rename = "fromBlock", skip_serializing_if = "Option::is_none")]
82    from_block: Option<BlockNumber>,
83    /// To Block
84    #[serde(rename = "toBlock", skip_serializing_if = "Option::is_none")]
85    to_block: Option<BlockNumber>,
86    /// Block Hash
87    #[serde(rename = "blockHash", skip_serializing_if = "Option::is_none")]
88    block_hash: Option<H256>,
89    /// Address
90    #[serde(skip_serializing_if = "Option::is_none")]
91    address: Option<ValueOrArray<H160>>,
92    /// Topics
93    #[serde(skip_serializing_if = "Option::is_none")]
94    topics: Option<Vec<Option<ValueOrArray<H256>>>>,
95    /// Limit
96    #[serde(skip_serializing_if = "Option::is_none")]
97    limit: Option<usize>,
98}
99
100/// Filter Builder
101#[derive(Default, Clone)]
102pub struct FilterBuilder {
103    filter: Filter,
104}
105
106impl FilterBuilder {
107    /// Sets `from_block`. The fields `from_block` and `block_hash` are
108    /// mutually exclusive. Setting `from_block` will clear a previously set
109    /// `block_hash`.
110    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    /// Sets `to_block`. The fields `to_block` and `block_hash` are mutually
117    /// exclusive. Setting `to_block` will clear a previously set `block_hash`.
118    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    /// Sets `block_hash`. The field `block_hash` and the pair `from_block` and
125    /// `to_block` are mutually exclusive. Setting `block_hash` will clear a
126    /// previously set `from_block` and `to_block`.
127    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    /// Single address
135    pub fn address(mut self, address: Vec<H160>) -> Self {
136        self.filter.address = Some(ValueOrArray(address));
137        self
138    }
139
140    /// Topics
141    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    /// Sets the topics according to the given `ethabi` topic filter
161    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    /// Limit the result
171    pub fn limit(mut self, limit: usize) -> Self {
172        self.filter.limit = Some(limit);
173        self
174    }
175
176    /// Returns filter
177    pub fn build(&self) -> Filter {
178        self.filter.clone()
179    }
180}
181
182/// Converts a `Topic` to an equivalent `Option<Vec<T>>`, suitable for `FilterBuilder::topics`
183fn 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}