Skip to main content

tronz_primitives/
log.rs

1//! TRON smart-contract event log type.
2
3use crate::{Address, B256, Bytes};
4
5/// An EVM-style event log emitted during contract execution.
6#[derive(Clone, Debug, Default, PartialEq, Eq, Hash)]
7#[non_exhaustive]
8pub struct Log {
9    /// Emitting contract address.
10    pub address: Address,
11    topics: Vec<B256>,
12    /// Non-indexed data.
13    pub data: Bytes,
14}
15
16impl Log {
17    /// Construct a log without checking the topic count.
18    ///
19    /// Decoders use this to round-trip node responses even when malformed.
20    pub fn new_unchecked(address: Address, topics: Vec<B256>, data: impl Into<Bytes>) -> Self {
21        Self { address, topics, data: data.into() }
22    }
23
24    /// Construct a log, returning `None` if it carries more than four topics.
25    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    /// Returns whether this log has at most four topics, as required by the
31    /// EVM-compatible event log format.
32    pub fn is_valid(&self) -> bool {
33        self.topics.len() <= 4
34    }
35
36    /// The indexed topics (topic0 = event signature hash).
37    pub fn topics(&self) -> &[B256] {
38        &self.topics
39    }
40
41    /// The indexed topics, mutably. Grants access to the existing entries
42    /// without allowing the list to grow past the topic limit.
43    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}