ic_web3_rs/types/
log.rs

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