stratus/eth/primitives/
log.rs

1use display_json::DebugAsJson;
2
3use crate::alias::AlloyLog;
4use crate::alias::AlloyLogData;
5use crate::alias::AlloyLogPrimitive;
6use crate::alias::RevmLog;
7use crate::eth::primitives::Address;
8use crate::eth::primitives::BlockNumber;
9use crate::eth::primitives::Bytes;
10use crate::eth::primitives::Hash;
11use crate::eth::primitives::Index;
12use crate::eth::primitives::LogTopic;
13use crate::eth::primitives::UnixTime;
14
15/// Log is an event emitted by the EVM during contract execution.
16#[derive(DebugAsJson, Clone, Default, PartialEq, Eq, serde::Serialize, serde::Deserialize)]
17#[cfg_attr(test, derive(fake::Dummy))]
18pub struct Log {
19    /// Address that emitted the log.
20    pub address: Address,
21
22    /// Topics (0 to 4 positions) describing the log.
23    pub topic0: Option<LogTopic>,
24    pub topic1: Option<LogTopic>,
25    pub topic2: Option<LogTopic>,
26    pub topic3: Option<LogTopic>,
27
28    /// Additional data.
29    pub data: Bytes,
30}
31
32impl Log {
33    /// Returns all topics in the log.
34    pub fn topics(&self) -> [Option<LogTopic>; 4] {
35        [self.topic0, self.topic1, self.topic2, self.topic3]
36    }
37
38    /// Returns all non-empty topics in the log.
39    pub fn topics_non_empty(&self) -> Vec<LogTopic> {
40        self.topics().into_iter().flatten().collect()
41    }
42
43    pub fn to_alloy_log(
44        self,
45        block_hash: Hash,
46        block_number: BlockNumber,
47        block_timestamp: UnixTime,
48        transaction_hash: Hash,
49        transaction_index: Index,
50        log_index: Index,
51    ) -> AlloyLog {
52        AlloyLog {
53            inner: AlloyLogPrimitive {
54                address: self.address.into(),
55                // Using new_unchecked is safe because topics_non_empty() guarantees ≤ 4 topics
56                data: AlloyLogData::new_unchecked(self.topics_non_empty().into_iter().map(Into::into).collect(), self.data.into()),
57            },
58            block_hash: Some(block_hash.into()),
59            block_number: Some(block_number.as_u64()),
60            block_timestamp: Some(*block_timestamp),
61            transaction_hash: Some(transaction_hash.into()),
62            transaction_index: Some(transaction_index.into()),
63            log_index: Some(log_index.into()),
64            removed: false,
65        }
66    }
67}
68
69// -----------------------------------------------------------------------------
70// Conversions: Other -> Self
71// ----------------------------------------------------------------------------
72impl From<RevmLog> for Log {
73    fn from(value: RevmLog) -> Self {
74        let (topics, data) = value.data.split();
75        let topics_len = topics.len();
76
77        let mut log = Self {
78            address: value.address.into(),
79            data: data.into(),
80            ..Default::default()
81        };
82
83        // you may not like it but this is what peak performance looks like
84        match topics_len {
85            4 => {
86                log.topic0 = Some(topics[0].into());
87                log.topic1 = Some(topics[1].into());
88                log.topic2 = Some(topics[2].into());
89                log.topic3 = Some(topics[3].into());
90            }
91            3 => {
92                log.topic0 = Some(topics[0].into());
93                log.topic1 = Some(topics[1].into());
94                log.topic2 = Some(topics[2].into());
95            }
96            2 => {
97                log.topic0 = Some(topics[0].into());
98                log.topic1 = Some(topics[1].into());
99            }
100            1 => {
101                log.topic0 = Some(topics[0].into());
102            }
103            _ => {}
104        }
105
106        log
107    }
108}
109
110impl From<AlloyLog> for Log {
111    fn from(value: AlloyLog) -> Self {
112        let topics = value.inner.topics().to_vec();
113        let topics_len = topics.len();
114
115        let mut log = Self {
116            address: value.inner.address.into(),
117            data: value.inner.data.data.into(),
118            ..Default::default()
119        };
120
121        // you may not like it but this is what peak performance looks like
122        match topics_len {
123            4 => {
124                log.topic0 = Some(topics[0].into());
125                log.topic1 = Some(topics[1].into());
126                log.topic2 = Some(topics[2].into());
127                log.topic3 = Some(topics[3].into());
128            }
129            3 => {
130                log.topic0 = Some(topics[0].into());
131                log.topic1 = Some(topics[1].into());
132                log.topic2 = Some(topics[2].into());
133            }
134            2 => {
135                log.topic0 = Some(topics[0].into());
136                log.topic1 = Some(topics[1].into());
137            }
138            1 => {
139                log.topic0 = Some(topics[0].into());
140            }
141            _ => {}
142        }
143
144        log
145    }
146}