1use airnode_abi::{DecodingError, ABI};
2use ethereum_types::{H160, H256, U256};
3use std::str::FromStr;
4use thiserror::Error;
5
6#[derive(Error, Debug, Clone)]
7pub enum EventParseError {
8 #[error("tx {0}: no topics")]
9 NoTopics(H256),
10 #[error("tx {0}: {1} topics found, {2} expected")]
11 InvalidTopics(H256, usize, usize),
12 #[error("tx {0}: {1} data length, {2} bytes expected")]
13 InvalidDataSize(H256, usize, usize),
14}
15
16pub struct LogReader {
17 pub topics: Vec<H256>,
18 pub data: Vec<u8>,
19 pub current_topic: usize,
21 pub data_offset: usize,
23}
24
25impl LogReader {
26 pub fn new(
27 log: &web3::types::Log,
28 expected_topics: usize,
29 expected_data_size: Option<usize>,
30 ) -> Result<Self, EventParseError> {
31 let hash = log.transaction_hash.unwrap();
32 if log.topics.len() == 0 {
33 return Err(EventParseError::NoTopics(hash));
34 }
35 if log.topics.len() - 1 != expected_topics {
36 return Err(EventParseError::InvalidTopics(
37 hash,
38 log.topics.len() - 1,
39 expected_topics,
40 ));
41 }
42 let data: &Vec<u8> = &log.data.0;
43 if let Some(sz) = expected_data_size {
44 if data.len() != sz * 32 {
45 return Err(EventParseError::InvalidDataSize(hash, data.len(), sz));
46 }
47 }
48 Ok(Self {
49 current_topic: 1,
50 data_offset: 0,
51 topics: log.topics.clone(),
52 data: data.clone(),
53 })
54 }
55
56 fn has_topics(&self) -> bool {
57 self.topics.len() > self.current_topic
58 }
59
60 fn has_data(&self) -> bool {
61 self.data.len() > self.data_offset
62 }
63
64 fn next32(&mut self) -> String {
65 if self.has_topics() {
66 let hex_str = format!("{:?}", self.topics.get(self.current_topic).unwrap()); self.current_topic += 1;
69 hex_str[2..].to_owned()
70 } else {
71 let hex_str = hex::encode(&self.data);
72 let offs: usize = 2 * self.data_offset;
73 let res: String = hex_str.chars().skip(offs).take(64).collect();
74 self.data_offset += 32;
75 res
76 }
77 }
78
79 pub fn text(&mut self) -> String {
81 let _hex_size = self.next32(); let _str_size = self.next32(); let mut s = String::from("");
84 while self.has_data() {
85 let nextword = self.next32();
86 let bts: Vec<u8> = hex::decode(nextword).unwrap();
87 bts.iter().filter(|ch| **ch != 0).for_each(|ch| {
88 if *ch == 0x1F {
89 s.push('|');
90 } else if *ch == '\\' as u8
91 || *ch == '"' as u8
92 || *ch == '\'' as u8
93 || *ch == '<' as u8
94 || *ch == '>' as u8
95 {
96 s.push(' ');
98 } else if *ch > 0x1F && *ch < 0x80 {
99 s.push(*ch as char);
100 }
101 });
102 }
103 s
104 }
105
106 pub fn address(&mut self) -> H160 {
108 let hex_str = self.next32();
109 H160::from_str(&hex_str[24..]).unwrap()
111 }
112
113 pub fn addresses(&mut self) -> Vec<H160> {
115 let _ = self.next32();
116 let _ = self.next32();
117 let mut res = vec![];
118 while self.has_data() {
119 res.push(self.address());
120 }
121 res
122 }
123
124 pub fn value(&mut self) -> U256 {
126 let hex_str = self.next32();
128 U256::from_str(hex_str.as_str()).unwrap()
130 }
131
132 pub fn value224_32(&mut self) -> (U256, u64) {
133 let packed = self.value();
135 let mut value = packed;
136 value.0[0] = value.0[0] & 0xFFFFFFFF;
137 let timestamp = packed.0[0];
138 (value, timestamp)
139 }
140
141 pub fn skip(&mut self) {
142 let _ = self.value();
143 }
144
145 pub fn values(&mut self) -> Vec<U256> {
147 let mut res = vec![];
148 while self.has_data() {
149 res.push(self.value());
150 }
151 res
152 }
153
154 pub fn bool(&mut self) -> bool {
156 let val = self.value().as_u64();
157 val > 0
158 }
159
160 pub fn decoded(&mut self) -> (Option<ABI>, Option<DecodingError>, Option<Vec<U256>>) {
162 self.skip();
163 self.skip();
164 let chunks = self.values();
165 if chunks.len() == 0 {
166 return (None, None, None);
167 }
168 let (parameters, error, data) = match ABI::decode(&chunks, false) {
169 Ok(x) => (Some(x), None, None),
170 Err(e) => (
171 None,
172 Some(e),
173 if chunks.len() > 0 {
174 Some(chunks.clone())
175 } else {
176 None
177 },
178 ),
179 };
180 (parameters, error, data)
181 }
182}
183
184#[cfg(test)]
185mod tests {
186 use super::*;
187 use ethereum_types::Address;
188 use hex_literal::hex;
189 use web3::types::Log;
190
191 #[test]
192 pub fn test_it_reads() {
193 let log = Log {
194 address: Address::from_low_u64_be(1),
195 topics: vec![
196 hex!("06fbd2297e6f6f7701a9cf99685a6af911cab275ec5c75ac7aaaf13b5cf3d61f").into(),
197 hex!("000000000000000000000000061b8335e1d2042975c4ed849943334bd07fb504").into(),
198 ],
199 data: hex!("0000000000000000000000000000000000000000000000056bc75e2d631000000000000000000000000000000000000000000000000000056bb73f60696ee4160000000000000000000000000000000000000000000000000000000060da02bd").into(),
200 block_hash: Some(H256::from_low_u64_be(2)),
201 block_number: Some(1.into()),
202 transaction_hash: Some(H256::from_low_u64_be(3)),
203 transaction_index: Some(0.into()),
204 log_index: Some(0.into()),
205 transaction_log_index: Some(0.into()),
206 log_type: None,
207 removed: Some(true),
208 };
209 let mut r = LogReader::new(&log, 1, Some(3)).unwrap();
210 assert_eq!(
211 r.address(),
212 hex!("061b8335e1d2042975c4ed849943334bd07fb504").into()
213 );
214 assert_eq!(
215 r.value(),
216 hex!("0000000000000000000000000000000000000000000000056bc75e2d63100000").into()
217 );
218 assert_eq!(
219 r.value(),
220 hex!("0000000000000000000000000000000000000000000000056bb73f60696ee416").into()
221 );
222 assert_eq!(
223 r.value(),
224 hex!("0000000000000000000000000000000000000000000000000000000060da02bd").into()
225 );
226 }
227
228 #[test]
229 pub fn test_reads_meta_data() {
230 let log = Log {
231 address: Address::from_low_u64_be(1),
232 topics: vec![
233 hex!("4d72fe0577a3a3f7da968d7b892779dde102519c25527b29cf7054f245c791b9").into(),
234 hex!("0000000000000000000000000000000000000000000000000000000000000000").into(),
235 hex!("000000000000000000000000061b8335e1d2042975c4ed849943334bd07fb504").into()
236 ],
237 data: hex!(
238 "00000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000047311f7472616e7366657228616464726573732c75696e74323536291f4d7920666972737420415049332070726f706f73616c1f466f722074657374696e6720707572706f73657300000000000000000000000000000000000000000000000000"
239 ).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: None,
247 removed: Some(true),
248 };
249 let mut r = LogReader::new(&log, 2, None).unwrap();
250 assert_eq!(
251 r.value(),
252 hex!("0000000000000000000000000000000000000000000000000000000000000000").into()
253 );
254 assert_eq!(
255 r.address(),
256 hex!("061b8335e1d2042975c4ed849943334bd07fb504").into()
257 );
258 assert_eq!(
259 r.text(),
260 "1|transfer(address,uint256)|My first API3 proposal|For testing purposes"
261 );
262 }
263}