stratus/eth/
codegen.rs

1//! Auto-generated code.
2
3use std::borrow::Cow;
4
5use crate::eth::primitives::Address;
6use crate::eth::primitives::LogFilter;
7use crate::infra::metrics;
8
9include!(concat!(env!("OUT_DIR"), "/contracts.rs"));
10include!(concat!(env!("OUT_DIR"), "/signatures.rs"));
11
12pub type SoliditySignature = &'static str;
13
14pub type ContractName = &'static str;
15
16/// Returns the contract name to be used in observability tasks.
17pub fn contract_name(address: &Option<Address>) -> ContractName {
18    let Some(address) = address else { return metrics::LABEL_MISSING };
19    match CONTRACTS.get(address.as_slice()) {
20        Some(contract_name) => contract_name,
21        None => metrics::LABEL_UNKNOWN,
22    }
23}
24
25/// Returns the function name to be used in observability tasks.
26pub fn function_sig(bytes: impl AsRef<[u8]>) -> SoliditySignature {
27    match function_sig_opt(bytes) {
28        Some(signature) => signature,
29        None => metrics::LABEL_UNKNOWN,
30    }
31}
32
33/// Returns the function name to be used in observability tasks.
34pub fn function_sig_opt(bytes: impl AsRef<[u8]>) -> Option<SoliditySignature> {
35    let Some(id) = bytes.as_ref().get(..4) else {
36        return Some(metrics::LABEL_MISSING);
37    };
38    SIGNATURES_4_BYTES.get(id).copied()
39}
40
41/// Returns the error name or string.
42pub fn error_sig_opt(bytes: impl AsRef<[u8]>) -> Option<Cow<'static, str>> {
43    bytes
44        .as_ref()
45        .get(..4)
46        .and_then(|id| SIGNATURES_4_BYTES.get(id))
47        .copied()
48        .map(Cow::Borrowed)
49        .or_else(|| {
50            bytes.as_ref().get(4..).and_then(|bytes| {
51                ethabi::decode(&[ethabi::ParamType::String], bytes)
52                    .ok()
53                    .and_then(|res| res.first().cloned())
54                    .and_then(|token| token.into_string())
55                    .map(Cow::Owned)
56            })
57        })
58}
59
60pub fn event_sig(bytes: impl AsRef<[u8]>) -> SoliditySignature {
61    match event_sig_opt(bytes) {
62        Some(signature) => signature,
63        None => metrics::LABEL_UNKNOWN,
64    }
65}
66
67pub fn event_sig_opt(bytes: impl AsRef<[u8]>) -> Option<SoliditySignature> {
68    let bytes_slice = bytes.as_ref();
69    if bytes_slice.len() != 32 {
70        return Some(metrics::LABEL_MISSING);
71    }
72    let id: [u8; 32] = bytes_slice.try_into().ok()?;
73    SIGNATURES_32_BYTES.get(&id).copied()
74}
75
76pub fn event_names_from_filter(filter: &LogFilter) -> SoliditySignature {
77    let topics = &filter.original_input.topics;
78    if topics.is_empty() {
79        return metrics::LABEL_MISSING;
80    }
81    let topic0 = &topics[0];
82    if topic0.is_empty() || topic0.contains(&None) {
83        return metrics::LABEL_MISSING;
84    }
85    if let Some(Some(first_topic)) = topic0.0.first() {
86        return event_sig(first_topic.as_ref());
87    }
88
89    metrics::LABEL_MISSING
90}
91
92#[cfg(test)]
93mod tests {
94    use std::sync::Arc;
95
96    use super::*;
97    use crate::eth::primitives::LogFilterInput;
98    use crate::eth::primitives::LogFilterInputTopic;
99    use crate::eth::primitives::LogTopic;
100    use crate::eth::storage::StratusStorage;
101
102    #[test]
103    fn test_event_sig_with_empty_bytes() {
104        let result = event_sig([]);
105        assert_eq!(result, metrics::LABEL_MISSING);
106    }
107
108    #[test]
109    fn test_event_sig_with_wrong_size() {
110        let result = event_sig([0u8; 4]); // 4 bytes instead of 32
111        assert_eq!(result, metrics::LABEL_MISSING);
112    }
113
114    #[test]
115    fn test_event_sig_with_unknown_signature() {
116        let unknown_sig = [0xFFu8; 32];
117        let result = event_sig(unknown_sig);
118        assert_eq!(result, metrics::LABEL_UNKNOWN);
119    }
120
121    #[test]
122    fn test_event_names_from_filter_no_topics() {
123        let storage = StratusStorage::new_test().unwrap();
124        let filter_input = LogFilterInput::default();
125        let filter = filter_input.parse(&Arc::new(storage)).unwrap();
126
127        let result = event_names_from_filter(&filter);
128        assert_eq!(result, metrics::LABEL_MISSING);
129    }
130
131    #[test]
132    fn test_event_names_from_filter_with_none_topic() {
133        let storage = StratusStorage::new_test().unwrap();
134        let filter_input = LogFilterInput {
135            topics: vec![LogFilterInputTopic(vec![None])],
136            ..Default::default()
137        };
138        let filter = filter_input.parse(&Arc::new(storage)).unwrap();
139
140        let result = event_names_from_filter(&filter);
141        assert_eq!(result, metrics::LABEL_MISSING);
142    }
143
144    #[test]
145    fn test_event_names_from_filter_with_unknown_topic() {
146        let storage = StratusStorage::new_test().unwrap();
147        let unknown_topic = LogTopic::from([0xFFu8; 32]);
148        let filter_input = LogFilterInput {
149            topics: vec![LogFilterInputTopic(vec![Some(unknown_topic)])],
150            ..Default::default()
151        };
152        let filter = filter_input.parse(&Arc::new(storage)).unwrap();
153
154        let result = event_names_from_filter(&filter);
155        assert_eq!(result, metrics::LABEL_UNKNOWN);
156    }
157
158    #[test]
159    fn test_event_names_from_filter_with_known_topic() {
160        let storage = StratusStorage::new_test().unwrap();
161        let transfer_event = [
162            221, 242, 82, 173, 27, 226, 200, 155, 105, 194, 176, 104, 252, 55, 141, 170, 149, 43, 167, 241, 99, 196, 161, 22, 40, 245, 90, 77, 245, 35, 179,
163            239,
164        ];
165        let filter_input = LogFilterInput {
166            topics: vec![LogFilterInputTopic(vec![Some(LogTopic::from(transfer_event))])],
167            ..Default::default()
168        };
169        let filter = filter_input.parse(&Arc::new(storage)).unwrap();
170        let result = event_names_from_filter(&filter);
171        assert_eq!(result, "Transfer(address,address,uint256)");
172    }
173}