1use alloc::collections::BTreeMap;
12
13#[cfg(feature = "serde")]
14use serde::{Deserialize, Serialize};
15use sha3::{Digest, Keccak256};
16
17#[cfg(not(feature = "std"))]
18use crate::no_std_prelude::*;
19use crate::{
20 decode, decode_validate, encode, signature::long_signature, Error, EventParam, Hash, Log,
21 LogParam, ParamType, RawLog, RawTopicFilter, Result, Token, Topic, TopicFilter,
22};
23
24#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
26#[derive(Clone, Debug, PartialEq)]
27pub struct Event {
28 #[cfg_attr(
30 feature = "serde",
31 serde(deserialize_with = "crate::util::sanitize_name::deserialize")
32 )]
33 pub name: String,
34 pub inputs: Vec<EventParam>,
36 pub anonymous: bool,
38}
39
40impl Event {
41 fn params_names(&self) -> Vec<String> {
43 self.inputs.iter().map(|p| p.name.clone()).collect()
44 }
45
46 fn param_types(&self) -> Vec<ParamType> {
48 self.inputs.iter().map(|p| p.kind.clone()).collect()
49 }
50
51 fn indexed_params(&self, indexed: bool) -> Vec<EventParam> {
53 self.inputs
54 .iter()
55 .filter(|p| p.indexed == indexed)
56 .cloned()
57 .collect()
58 }
59
60 pub fn signature(&self) -> Hash {
62 long_signature(&self.name, &self.param_types())
63 }
64
65 pub fn filter(&self, raw: RawTopicFilter) -> Result<TopicFilter> {
67 fn convert_token(token: Token, kind: &ParamType) -> Result<Hash> {
68 if !token.type_check(kind) {
69 return Err(Error::InvalidData);
70 }
71 let encoded = encode(&[token]);
72 if encoded.len() == 32 {
73 let mut data = [0u8; 32];
74 data.copy_from_slice(&encoded);
75 Ok(data.into())
76 } else {
77 Ok(Hash::from_slice(Keccak256::digest(&encoded).as_slice()))
78 }
79 }
80
81 fn convert_topic(topic: Topic<Token>, kind: Option<&ParamType>) -> Result<Topic<Hash>> {
82 match topic {
83 Topic::None => Ok(Topic::None),
84 Topic::Any => Ok(Topic::Any),
85 Topic::OneOf(tokens) => match kind {
86 None => Err(Error::InvalidData),
87 Some(kind) => {
88 let topics = tokens
89 .into_iter()
90 .map(|token| convert_token(token, kind))
91 .collect::<Result<Vec<_>>>()?;
92 Ok(Topic::OneOf(topics))
93 }
94 },
95 Topic::This(token) => match kind {
96 None => Err(Error::InvalidData),
97 Some(kind) => Ok(Topic::This(convert_token(token, kind)?)),
98 },
99 }
100 }
101
102 let kinds: Vec<_> = self
103 .indexed_params(true)
104 .into_iter()
105 .map(|param| param.kind)
106 .collect();
107 let result = if self.anonymous {
108 TopicFilter {
109 topic0: convert_topic(raw.topic0, kinds.get(0))?,
110 topic1: convert_topic(raw.topic1, kinds.get(1))?,
111 topic2: convert_topic(raw.topic2, kinds.get(2))?,
112 topic3: Topic::Any,
113 }
114 } else {
115 TopicFilter {
116 topic0: Topic::This(self.signature()),
117 topic1: convert_topic(raw.topic0, kinds.get(0))?,
118 topic2: convert_topic(raw.topic1, kinds.get(1))?,
119 topic3: convert_topic(raw.topic2, kinds.get(2))?,
120 }
121 };
122
123 Ok(result)
124 }
125
126 fn convert_topic_param_type(&self, kind: &ParamType) -> ParamType {
131 match kind {
132 ParamType::String
133 | ParamType::Bytes
134 | ParamType::Array(_)
135 | ParamType::FixedArray(_, _)
136 | ParamType::Tuple(_) => ParamType::FixedBytes(32),
137 _ => kind.clone(),
138 }
139 }
140
141 fn parse_log_inner<F: Fn(&[ParamType], &[u8]) -> Result<Vec<Token>>>(
142 &self,
143 log: RawLog,
144 decode: F,
145 ) -> Result<Log> {
146 let topics = log.topics;
147 let data = log.data;
148 let topics_len = topics.len();
149 let topic_params = self.indexed_params(true);
151 let data_params = self.indexed_params(false);
152 let to_skip = if self.anonymous {
154 0
155 } else {
156 let event_signature = topics.get(0).ok_or(Error::InvalidData)?;
158 if event_signature != &self.signature() {
159 return Err(Error::InvalidData);
160 }
161 1
162 };
163
164 let topic_types = topic_params
165 .iter()
166 .map(|p| self.convert_topic_param_type(&p.kind))
167 .collect::<Vec<ParamType>>();
168
169 let flat_topics = topics
170 .into_iter()
171 .skip(to_skip)
172 .flat_map(|t| t.as_ref().to_vec())
173 .collect::<Vec<u8>>();
174
175 let topic_tokens = decode(&topic_types, &flat_topics)?;
176
177 if topic_tokens.len() != topics_len - to_skip {
179 return Err(Error::InvalidData);
180 }
181
182 let topics_named_tokens = topic_params
183 .into_iter()
184 .map(|p| p.name)
185 .zip(topic_tokens.into_iter());
186
187 let data_types = data_params
188 .iter()
189 .map(|p| p.kind.clone())
190 .collect::<Vec<ParamType>>();
191
192 let data_tokens = decode(&data_types, &data)?;
193
194 let data_named_tokens = data_params
195 .into_iter()
196 .map(|p| p.name)
197 .zip(data_tokens.into_iter());
198
199 let named_tokens = topics_named_tokens
200 .chain(data_named_tokens)
201 .collect::<BTreeMap<String, Token>>();
202
203 let decoded_params = self
204 .params_names()
205 .into_iter()
206 .map(|name| LogParam {
207 name: name.clone(),
208 value: named_tokens[&name].clone(),
209 })
210 .collect();
211
212 let result = Log {
213 params: decoded_params,
214 };
215
216 Ok(result)
217 }
218
219 pub fn parse_log_validate(&self, log: RawLog) -> Result<Log> {
222 self.parse_log_inner(log, decode_validate)
223 }
224
225 pub fn parse_log(&self, log: RawLog) -> Result<Log> {
227 self.parse_log_inner(log, decode)
228 }
229}
230
231#[cfg(test)]
232mod tests {
233 use hex_literal::hex;
234
235 #[cfg(not(feature = "std"))]
236 use crate::no_std_prelude::*;
237 use crate::{
238 log::{Log, RawLog},
239 signature::long_signature,
240 token::Token,
241 Event, EventParam, LogParam, ParamType,
242 };
243
244 #[test]
245 fn test_decoding_event() {
246 let event = Event {
247 name: "foo".to_owned(),
248 inputs: vec![
249 EventParam {
250 name: "a".to_owned(),
251 kind: ParamType::Int(256),
252 indexed: false,
253 },
254 EventParam {
255 name: "b".to_owned(),
256 kind: ParamType::Int(256),
257 indexed: true,
258 },
259 EventParam {
260 name: "c".to_owned(),
261 kind: ParamType::Address,
262 indexed: false,
263 },
264 EventParam {
265 name: "d".to_owned(),
266 kind: ParamType::Address,
267 indexed: true,
268 },
269 EventParam {
270 name: "e".to_owned(),
271 kind: ParamType::String,
272 indexed: true,
273 },
274 EventParam {
275 name: "f".to_owned(),
276 kind: ParamType::Array(Box::new(ParamType::Int(256))),
277 indexed: true,
278 },
279 EventParam {
280 name: "g".to_owned(),
281 kind: ParamType::FixedArray(Box::new(ParamType::Address), 5),
282 indexed: true,
283 },
284 ],
285 anonymous: false,
286 };
287
288 let log = RawLog {
289 topics: vec![
290 long_signature(
291 "foo",
292 &[
293 ParamType::Int(256),
294 ParamType::Int(256),
295 ParamType::Address,
296 ParamType::Address,
297 ParamType::String,
298 ParamType::Array(Box::new(ParamType::Int(256))),
299 ParamType::FixedArray(Box::new(ParamType::Address), 5),
300 ],
301 ),
302 hex!("0000000000000000000000000000000000000000000000000000000000000002").into(),
303 hex!("0000000000000000000000001111111111111111111111111111111111111111").into(),
304 hex!("00000000000000000aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa").into(),
305 hex!("00000000000000000bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb").into(),
306 hex!("00000000000000000ccccccccccccccccccccccccccccccccccccccccccccccc").into(),
307 ],
308 data: hex!(
309 "
310 0000000000000000000000000000000000000000000000000000000000000003
311 0000000000000000000000002222222222222222222222222222222222222222
312 "
313 )
314 .into(),
315 };
316 let result = event.parse_log(log).unwrap();
317
318 assert_eq!(
319 result,
320 Log {
321 params: [
322 (
323 "a",
324 Token::Int(
325 hex!(
326 "0000000000000000000000000000000000000000000000000000000000000003"
327 )
328 .into()
329 ),
330 ),
331 (
332 "b",
333 Token::Int(
334 hex!(
335 "0000000000000000000000000000000000000000000000000000000000000002"
336 )
337 .into()
338 ),
339 ),
340 (
341 "c",
342 Token::Address(hex!("2222222222222222222222222222222222222222").into())
343 ),
344 (
345 "d",
346 Token::Address(hex!("1111111111111111111111111111111111111111").into())
347 ),
348 (
349 "e",
350 Token::FixedBytes(
351 hex!(
352 "00000000000000000aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"
353 )
354 .into()
355 )
356 ),
357 (
358 "f",
359 Token::FixedBytes(
360 hex!(
361 "00000000000000000bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb"
362 )
363 .into()
364 )
365 ),
366 (
367 "g",
368 Token::FixedBytes(
369 hex!(
370 "00000000000000000ccccccccccccccccccccccccccccccccccccccccccccccc"
371 )
372 .into()
373 )
374 ),
375 ]
376 .iter()
377 .cloned()
378 .map(|(name, value)| LogParam {
379 name: name.to_string(),
380 value
381 })
382 .collect::<Vec<_>>()
383 }
384 );
385 }
386
387 #[test]
388 fn parse_log_whole() {
389 let correct_event = Event {
390 name: "Test".into(),
391 inputs: vec![
392 EventParam {
393 name: "tuple".into(),
394 kind: ParamType::Tuple(vec![ParamType::Address, ParamType::Address]),
395 indexed: false,
396 },
397 EventParam {
398 name: "addr".into(),
399 kind: ParamType::Address,
400 indexed: true,
401 },
402 ],
403 anonymous: false,
404 };
405 let mut wrong_event = correct_event.clone();
407 wrong_event.inputs[0].indexed = true;
408 wrong_event.inputs[1].indexed = false;
409
410 let log = RawLog {
411 topics: vec![
412 hex!("cf74b4e62f836eeedcd6f92120ffb5afea90e6fa490d36f8b81075e2a7de0cf7").into(),
413 hex!("0000000000000000000000000000000000000000000000000000000000012321").into(),
414 ],
415 data: hex!(
416 "
417 0000000000000000000000000000000000000000000000000000000000012345
418 0000000000000000000000000000000000000000000000000000000000054321
419 "
420 )
421 .into(),
422 };
423
424 assert!(wrong_event.parse_log(log.clone()).is_ok());
425 assert!(wrong_event.parse_log_validate(log.clone()).is_err());
426 assert!(correct_event.parse_log_validate(log).is_ok());
427 }
428}