1use crate::{Address, B256, Bytes};
4
5#[derive(Clone, Debug, Default, PartialEq, Eq, Hash)]
7#[non_exhaustive]
8pub struct Log {
9 pub address: Address,
11 topics: Vec<B256>,
12 pub data: Bytes,
14}
15
16impl Log {
17 pub fn new_unchecked(address: Address, topics: Vec<B256>, data: impl Into<Bytes>) -> Self {
21 Self { address, topics, data: data.into() }
22 }
23
24 pub fn new(address: Address, topics: Vec<B256>, data: impl Into<Bytes>) -> Option<Self> {
26 let log = Self::new_unchecked(address, topics, data);
27 log.is_valid().then_some(log)
28 }
29
30 pub fn is_valid(&self) -> bool {
33 self.topics.len() <= 4
34 }
35
36 pub fn topics(&self) -> &[B256] {
38 &self.topics
39 }
40
41 pub fn topics_mut(&mut self) -> &mut [B256] {
44 &mut self.topics
45 }
46}
47
48#[cfg(test)]
49mod tests {
50 use super::*;
51
52 #[test]
53 fn constructs_from_shared_primitive_fields() {
54 let address = Address::from_evm_bytes([0x11; 20]);
55 let topic = B256::from([0x22; 32]);
56 let log = Log::new(address, vec![topic], b"payload".to_vec()).unwrap();
57
58 assert_eq!(log.address, address);
59 assert_eq!(log.topics(), [topic]);
60 assert_eq!(log.data.as_ref(), b"payload");
61 assert!(log.is_valid());
62 }
63
64 #[test]
65 fn rejects_too_many_topics_but_unchecked_preserves_them() {
66 let topics = vec![B256::ZERO; 5];
67 assert!(Log::new(Address::ZERO, topics.clone(), Bytes::new()).is_none());
68
69 let log = Log::new_unchecked(Address::ZERO, topics.clone(), Bytes::new());
70 assert!(!log.is_valid());
71 assert_eq!(log.topics(), topics);
72 }
73}