1use 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
16pub 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
25pub 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
33pub 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
41pub 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]); 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}