1use serde::Deserialize;
12use sha3::{Digest, Keccak256};
13use std::collections::HashMap;
14
15use crate::{
16 decode, encode, signature::long_signature, Error, EventParam, Hash, Log, LogParam, ParamType, RawLog,
17 RawTopicFilter, Result, Token, Topic, TopicFilter,
18};
19
20#[derive(Clone, Debug, PartialEq, Deserialize)]
22pub struct Event {
23 pub name: String,
25 pub inputs: Vec<EventParam>,
27 pub anonymous: bool,
29}
30
31impl Event {
32 fn params_names(&self) -> Vec<String> {
34 self.inputs.iter().map(|p| p.name.clone()).collect()
35 }
36
37 fn param_types(&self) -> Vec<ParamType> {
39 self.inputs.iter().map(|p| p.kind.clone()).collect()
40 }
41
42 fn indexed_params(&self, indexed: bool) -> Vec<EventParam> {
44 self.inputs.iter().filter(|p| p.indexed == indexed).cloned().collect()
45 }
46
47 pub fn signature(&self) -> Hash {
49 long_signature(&self.name, &self.param_types())
50 }
51
52 pub fn filter(&self, raw: RawTopicFilter) -> Result<TopicFilter> {
54 fn convert_token(token: Token, kind: &ParamType) -> Result<Hash> {
55 if !token.type_check(kind) {
56 return Err(Error::InvalidData);
57 }
58 let encoded = encode(&[token]);
59 if encoded.len() == 32 {
60 let mut data = [0u8; 32];
61 data.copy_from_slice(&encoded);
62 Ok(data.into())
63 } else {
64 Ok(Hash::from_slice(Keccak256::digest(&encoded).as_slice()))
65 }
66 }
67
68 fn convert_topic(topic: Topic<Token>, kind: Option<&ParamType>) -> Result<Topic<Hash>> {
69 match topic {
70 Topic::Any => Ok(Topic::Any),
71 Topic::OneOf(tokens) => match kind {
72 None => Err(Error::InvalidData),
73 Some(kind) => {
74 let topics =
75 tokens.into_iter().map(|token| convert_token(token, kind)).collect::<Result<Vec<_>>>()?;
76 Ok(Topic::OneOf(topics))
77 }
78 },
79 Topic::This(token) => match kind {
80 None => Err(Error::InvalidData),
81 Some(kind) => Ok(Topic::This(convert_token(token, kind)?)),
82 },
83 }
84 }
85
86 let kinds: Vec<_> = self.indexed_params(true).into_iter().map(|param| param.kind).collect();
87 let result = if self.anonymous {
88 TopicFilter {
89 topic0: convert_topic(raw.topic0, kinds.get(0))?,
90 topic1: convert_topic(raw.topic1, kinds.get(1))?,
91 topic2: convert_topic(raw.topic2, kinds.get(2))?,
92 topic3: Topic::Any,
93 }
94 } else {
95 TopicFilter {
96 topic0: Topic::This(self.signature()),
97 topic1: convert_topic(raw.topic0, kinds.get(0))?,
98 topic2: convert_topic(raw.topic1, kinds.get(1))?,
99 topic3: convert_topic(raw.topic2, kinds.get(2))?,
100 }
101 };
102
103 Ok(result)
104 }
105
106 fn convert_topic_param_type(&self, kind: &ParamType) -> ParamType {
111 match kind {
112 ParamType::String
113 | ParamType::Bytes
114 | ParamType::Array(_)
115 | ParamType::FixedArray(_, _)
116 | ParamType::Tuple(_) => ParamType::FixedBytes(32),
117 _ => kind.clone(),
118 }
119 }
120
121 pub fn parse_log(&self, log: RawLog) -> Result<Log> {
123 let topics = log.topics;
124 let data = log.data;
125 let topics_len = topics.len();
126 let topic_params = self.indexed_params(true);
128 let data_params = self.indexed_params(false);
129 let to_skip = if self.anonymous {
131 0
132 } else {
133 let event_signature = topics.get(0).ok_or(Error::InvalidData)?;
135 if event_signature != &self.signature() {
136 return Err(Error::InvalidData);
137 }
138 1
139 };
140
141 let topic_types =
142 topic_params.iter().map(|p| self.convert_topic_param_type(&p.kind)).collect::<Vec<ParamType>>();
143
144 let flat_topics = topics.into_iter().skip(to_skip).flat_map(|t| t.as_ref().to_vec()).collect::<Vec<u8>>();
145
146 let topic_tokens = decode(&topic_types, &flat_topics)?;
147
148 if topic_tokens.len() != topics_len - to_skip {
150 return Err(Error::InvalidData);
151 }
152
153 let topics_named_tokens = topic_params.into_iter().map(|p| p.name).zip(topic_tokens.into_iter());
154
155 let data_types = data_params.iter().map(|p| p.kind.clone()).collect::<Vec<ParamType>>();
156
157 let data_tokens = decode(&data_types, &data)?;
158
159 let data_named_tokens = data_params.into_iter().map(|p| p.name).zip(data_tokens.into_iter());
160
161 let named_tokens = topics_named_tokens.chain(data_named_tokens).collect::<HashMap<String, Token>>();
162
163 let decoded_params = self
164 .params_names()
165 .into_iter()
166 .map(|name| LogParam { name: name.clone(), value: named_tokens[&name].clone() })
167 .collect();
168
169 let result = Log { params: decoded_params };
170
171 Ok(result)
172 }
173}
174
175#[cfg(test)]
176mod tests {
177 use crate::{
178 log::{Log, RawLog},
179 signature::long_signature,
180 token::Token,
181 Event, EventParam, LogParam, ParamType,
182 };
183 use hex_literal::hex;
184
185 #[test]
186 fn test_decoding_event() {
187 let event = Event {
188 name: "foo".to_owned(),
189 inputs: vec![
190 EventParam { name: "a".to_owned(), kind: ParamType::Int(256), indexed: false },
191 EventParam { name: "b".to_owned(), kind: ParamType::Int(256), indexed: true },
192 EventParam { name: "c".to_owned(), kind: ParamType::Address, indexed: false },
193 EventParam { name: "d".to_owned(), kind: ParamType::Address, indexed: true },
194 EventParam { name: "e".to_owned(), kind: ParamType::String, indexed: true },
195 EventParam {
196 name: "f".to_owned(),
197 kind: ParamType::Array(Box::new(ParamType::Int(256))),
198 indexed: true,
199 },
200 EventParam {
201 name: "g".to_owned(),
202 kind: ParamType::FixedArray(Box::new(ParamType::Address), 5),
203 indexed: true,
204 },
205 ],
206 anonymous: false,
207 };
208
209 let log = RawLog {
210 topics: vec![
211 long_signature(
212 "foo",
213 &[
214 ParamType::Int(256),
215 ParamType::Int(256),
216 ParamType::Address,
217 ParamType::Address,
218 ParamType::String,
219 ParamType::Array(Box::new(ParamType::Int(256))),
220 ParamType::FixedArray(Box::new(ParamType::Address), 5),
221 ],
222 ),
223 hex!("0000000000000000000000000000000000000000000000000000000000000002").into(),
224 hex!("0000000000000000000000001111111111111111111111111111111111111111").into(),
225 hex!("00000000000000000aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa").into(),
226 hex!("00000000000000000bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb").into(),
227 hex!("00000000000000000ccccccccccccccccccccccccccccccccccccccccccccccc").into(),
228 ],
229 data: hex!(
230 "
231 0000000000000000000000000000000000000000000000000000000000000003
232 0000000000000000000000002222222222222222222222222222222222222222
233 "
234 )
235 .into(),
236 };
237 let result = event.parse_log(log).unwrap();
238
239 assert_eq!(
240 result,
241 Log {
242 params: [
243 ("a", Token::Int(hex!("0000000000000000000000000000000000000000000000000000000000000003").into()),),
244 ("b", Token::Int(hex!("0000000000000000000000000000000000000000000000000000000000000002").into()),),
245 ("c", Token::Address(hex!("2222222222222222222222222222222222222222").into())),
246 ("d", Token::Address(hex!("1111111111111111111111111111111111111111").into())),
247 (
248 "e",
249 Token::FixedBytes(
250 hex!("00000000000000000aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa").into()
251 )
252 ),
253 (
254 "f",
255 Token::FixedBytes(
256 hex!("00000000000000000bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb").into()
257 )
258 ),
259 (
260 "g",
261 Token::FixedBytes(
262 hex!("00000000000000000ccccccccccccccccccccccccccccccccccccccccccccccc").into()
263 )
264 ),
265 ]
266 .iter()
267 .cloned()
268 .map(|(name, value)| LogParam { name: name.to_string(), value })
269 .collect::<Vec<_>>()
270 }
271 );
272 }
273}