alloy_dyn_abi/ext/
event.rs1use crate::{DecodedEvent, DynSolEvent, DynSolType, Error, Result, Specifier};
2use alloc::vec::Vec;
3use alloy_json_abi::Event;
4use alloy_primitives::{LogData, B256};
5
6#[allow(unknown_lints, unnameable_types)]
7mod sealed {
8 pub trait Sealed {}
9 impl Sealed for alloy_json_abi::Event {}
10}
11use sealed::Sealed;
12
13impl Specifier<DynSolEvent> for Event {
14 fn resolve(&self) -> Result<DynSolEvent> {
15 let mut indexed = Vec::with_capacity(self.inputs.len());
16 let mut body = Vec::with_capacity(self.inputs.len());
17 for param in &self.inputs {
18 let ty = param.resolve()?;
19 if param.indexed {
20 indexed.push(ty);
21 } else {
22 body.push(ty);
23 }
24 }
25 let topic_0 = if self.anonymous { None } else { Some(self.selector()) };
26
27 let num_topics = indexed.len() + topic_0.is_some() as usize;
28 if num_topics > 4 {
29 return Err(Error::TopicLengthMismatch { expected: 4, actual: num_topics });
30 }
31
32 Ok(DynSolEvent::new_unchecked(topic_0, indexed, DynSolType::Tuple(body)))
33 }
34}
35
36pub trait EventExt: Sealed {
41 fn decode_log_parts<I>(&self, topics: I, data: &[u8]) -> Result<DecodedEvent>
57 where
58 I: IntoIterator<Item = B256>;
59
60 #[inline]
64 fn decode_log(&self, log: &LogData) -> Result<DecodedEvent> {
65 self.decode_log_parts(log.topics().iter().copied(), &log.data)
66 }
67}
68
69impl EventExt for Event {
70 fn decode_log_parts<I>(&self, topics: I, data: &[u8]) -> Result<DecodedEvent>
71 where
72 I: IntoIterator<Item = B256>,
73 {
74 self.resolve()?.decode_log_parts(topics, data)
75 }
76}
77
78#[cfg(test)]
79mod tests {
80 use super::*;
81 use crate::DynSolValue;
82 use alloy_json_abi::EventParam;
83 use alloy_primitives::{address, b256, bytes, hex, keccak256, Signed};
84
85 #[test]
86 fn empty() {
87 let mut event = Event { name: "MyEvent".into(), inputs: vec![], anonymous: false };
88
89 let values = event.decode_log_parts(Some(keccak256("MyEvent()")), &[]).unwrap();
90 assert!(values.indexed.is_empty());
91 assert!(values.body.is_empty());
92 event.anonymous = true;
93 let values = event.decode_log_parts(None, &[]).unwrap();
94 assert!(values.indexed.is_empty());
95 assert!(values.body.is_empty());
96 let values = event.decode_log_parts(None, &[]).unwrap();
97 assert!(values.indexed.is_empty());
98 assert!(values.body.is_empty());
99 }
100
101 #[test]
103 fn test_decoding_event() {
104 let event = Event {
105 name: "foo".into(),
106 inputs: vec![
107 EventParam { ty: "int256".into(), indexed: false, ..Default::default() },
108 EventParam { ty: "int256".into(), indexed: true, ..Default::default() },
109 EventParam { ty: "address".into(), indexed: false, ..Default::default() },
110 EventParam { ty: "address".into(), indexed: true, ..Default::default() },
111 EventParam { ty: "string".into(), indexed: true, ..Default::default() },
112 ],
113 anonymous: false,
114 };
115
116 let result = event
117 .decode_log_parts(
118 [
119 event.selector(),
120 b256!("0x0000000000000000000000000000000000000000000000000000000000000002"),
121 b256!("0x0000000000000000000000001111111111111111111111111111111111111111"),
122 b256!("0x00000000000000000aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"),
123 ],
124 &hex!(
125 "
126 0000000000000000000000000000000000000000000000000000000000000003
127 0000000000000000000000002222222222222222222222222222222222222222
128 "
129 ),
130 )
131 .unwrap();
132
133 assert_eq!(
134 result.body,
135 [
136 DynSolValue::Int(
137 Signed::from_be_bytes(hex!(
138 "0000000000000000000000000000000000000000000000000000000000000003"
139 )),
140 256
141 ),
142 DynSolValue::Address(address!("0x2222222222222222222222222222222222222222")),
143 ]
144 );
145 assert_eq!(
146 result.indexed,
147 [
148 DynSolValue::Int(
149 Signed::from_be_bytes(hex!(
150 "0000000000000000000000000000000000000000000000000000000000000002"
151 )),
152 256
153 ),
154 DynSolValue::Address(address!("0x1111111111111111111111111111111111111111")),
155 DynSolValue::FixedBytes(
156 b256!("0x00000000000000000aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"),
157 32
158 ),
159 ]
160 )
161 }
162
163 #[test]
164 fn parse_log_whole() {
165 let correct_event = Event {
166 name: "Test".into(),
167 inputs: vec![
168 EventParam { ty: "(address,address)".into(), indexed: false, ..Default::default() },
169 EventParam { ty: "address".into(), indexed: true, ..Default::default() },
170 ],
171 anonymous: false,
172 };
173 let mut wrong_event = correct_event.clone();
175 wrong_event.inputs[0].indexed = true;
176 wrong_event.inputs[1].indexed = false;
177
178 let log = LogData::new_unchecked(
179 vec![
180 b256!("0xcf74b4e62f836eeedcd6f92120ffb5afea90e6fa490d36f8b81075e2a7de0cf7"),
181 b256!("0x0000000000000000000000000000000000000000000000000000000000012321"),
182 ],
183 bytes!(
184 "
185 0000000000000000000000000000000000000000000000000000000000012345
186 0000000000000000000000000000000000000000000000000000000000054321
187 "
188 ),
189 );
190
191 wrong_event.decode_log(&log).unwrap();
192 correct_event.decode_log(&log).unwrap();
193 correct_event.decode_log(&log).unwrap();
194 }
195}