1extern crate alloc;
28
29use alloc::string::String;
30use alloc::vec::Vec;
31
32use crate::codec::{Codec32, Encoder};
33use crate::env;
34use crate::error::Result;
35use crate::hashing;
36
37pub mod __private {
39 pub use alloc::vec::Vec;
40}
41
42pub fn emit(topics: &[[u8; 32]], data: &[u8]) -> Result<()> {
44 let mut flat = Vec::with_capacity(topics.len() * 32);
45 for topic in topics {
46 flat.extend_from_slice(topic);
47 }
48 env::emit_log_bytes(&flat, data)
49}
50
51pub fn topic_from_bytes(bytes: &[u8]) -> [u8; 32] {
53 let mut topic = [0u8; 32];
54 let copy_len = bytes.len().min(32);
55 topic[..copy_len].copy_from_slice(&bytes[..copy_len]);
56 topic
57}
58
59pub fn event_signature(signature: &str) -> [u8; 32] {
61 hashing::hash32(signature.as_bytes())
62}
63
64pub trait EventTopic {
66 fn to_topic(&self) -> [u8; 32];
68}
69
70pub trait EventData {
72 fn encode_event_data(&self, encoder: &mut Encoder);
74}
75
76pub trait Event {
80 fn event_name() -> &'static str;
82
83 fn is_anonymous() -> bool {
85 false
86 }
87
88 fn event_signature() -> [u8; 32] {
90 event_signature(Self::event_name())
91 }
92
93 fn event_topics(&self) -> Vec<[u8; 32]>;
95
96 fn event_data(&self) -> Vec<u8>;
98
99 fn emit(&self) -> Result<()>
101 where
102 Self: Sized,
103 {
104 emit_event(self)
105 }
106}
107
108pub fn emit_event<E: Event>(event: &E) -> Result<()> {
110 let topics = event.event_topics();
111 let data = event.event_data();
112 emit(&topics, &data)
113}
114
115pub fn event_topics_match<E: Event>(topics: &[[u8; 32]]) -> bool {
117 if E::is_anonymous() {
118 true
119 } else {
120 topics
121 .first()
122 .map(|t| *t == E::event_signature())
123 .unwrap_or(false)
124 }
125}
126
127pub fn indexed_topics<'a, E: Event>(topics: &'a [[u8; 32]]) -> &'a [[u8; 32]] {
129 if E::is_anonymous() {
130 topics
131 } else {
132 topics.get(1..).unwrap_or(&[])
133 }
134}
135
136pub fn decode_event_data<T: crate::codec::BytesCodec>(data: &[u8]) -> Result<T> {
138 T::decode_bytes(data)
139}
140
141macro_rules! impl_topic_codec32 {
142 ($ty:ty) => {
143 impl EventTopic for $ty {
144 fn to_topic(&self) -> [u8; 32] {
145 self.encode_32()
146 }
147 }
148 };
149}
150
151impl_topic_codec32!(bool);
152impl_topic_codec32!(u8);
153impl_topic_codec32!(u16);
154impl_topic_codec32!(u32);
155impl_topic_codec32!(u64);
156impl_topic_codec32!(u128);
157impl_topic_codec32!(i8);
158impl_topic_codec32!(i16);
159impl_topic_codec32!(i32);
160impl_topic_codec32!(i64);
161impl_topic_codec32!(i128);
162
163impl<const N: usize> EventTopic for [u8; N] {
164 fn to_topic(&self) -> [u8; 32] {
165 if N == 32 {
166 let mut out = [0u8; 32];
167 out.copy_from_slice(self);
168 out
169 } else if N < 32 {
170 let mut out = [0u8; 32];
171 out[..N].copy_from_slice(self);
172 out
173 } else {
174 hashing::hash32(self)
175 }
176 }
177}
178
179impl EventTopic for [u8] {
180 fn to_topic(&self) -> [u8; 32] {
181 if self.len() <= 32 {
182 topic_from_bytes(self)
183 } else {
184 hashing::hash32(self)
185 }
186 }
187}
188
189impl EventTopic for Vec<u8> {
190 fn to_topic(&self) -> [u8; 32] {
191 self.as_slice().to_topic()
192 }
193}
194
195impl EventTopic for str {
196 fn to_topic(&self) -> [u8; 32] {
197 hashing::hash32(self.as_bytes())
198 }
199}
200
201impl EventTopic for String {
202 fn to_topic(&self) -> [u8; 32] {
203 self.as_str().to_topic()
204 }
205}
206
207impl<T: crate::codec::BytesCodec> EventData for T {
208 fn encode_event_data(&self, encoder: &mut Encoder) {
209 encoder.push_bytes(&self.encode_bytes());
210 }
211}
212
213impl EventData for [u8] {
214 fn encode_event_data(&self, encoder: &mut Encoder) {
215 encoder.push_bytes(self);
216 }
217}
218
219impl EventData for str {
220 fn encode_event_data(&self, encoder: &mut Encoder) {
221 encoder.push_bytes(self.as_bytes());
222 }
223}
224
225#[cfg(test)]
226mod tests {
227 use super::*;
228
229 #[derive(crate::Event)]
230 #[event(name = "CounterUpdated")]
231 struct CounterUpdated {
232 #[topic]
233 caller: [u8; 32],
234 value: u64,
235 memo: alloc::string::String,
236 }
237
238 #[test]
239 fn derive_event_encodes_topics_and_data() {
240 let event = CounterUpdated {
241 caller: [9u8; 32],
242 value: 42,
243 memo: alloc::string::String::from("ok"),
244 };
245
246 let topics = event.event_topics();
247 assert_eq!(topics.len(), 2);
248 assert_eq!(topics[1], [9u8; 32]);
249
250 let data = event.event_data();
251 assert!(!data.is_empty());
252 }
253
254 fn encode_u64_topic(v: &u64) -> [u8; 32] {
255 let mut out = [0u8; 32];
256 out[..8].copy_from_slice(&v.to_le_bytes());
257 out
258 }
259
260 fn encode_bool_data(v: &bool, encoder: &mut crate::codec::Encoder) {
261 encoder.push_bool(*v);
262 }
263
264 #[derive(crate::Event)]
265 #[event(name = "OrderFilled", anonymous)]
266 struct OrderFilled {
267 #[event(topic, with_topic = "encode_u64_topic", type = "uint64")]
268 order_id: u64,
269 #[event(with_data = "encode_bool_data")]
270 settled: bool,
271 #[event(skip)]
272 _padding: u8,
273 }
274
275 #[test]
276 fn derive_event_supports_advanced_field_options() {
277 let event = OrderFilled {
278 order_id: 77,
279 settled: true,
280 _padding: 0,
281 };
282
283 assert!(OrderFilled::is_anonymous());
284 let topics = event.event_topics();
285 assert_eq!(topics.len(), 1);
286 assert_eq!(topics[0][..8], 77u64.to_le_bytes());
287 assert!(!event.event_data().is_empty());
288 }
289}