nautilus_blockchain/exchanges/parsing/
core.rs1use alloy::primitives::Address;
22
23pub fn extract_address_from_bytes(bytes: &[u8]) -> anyhow::Result<Address> {
32 anyhow::ensure!(
33 bytes.len() >= 32,
34 "Topic must be at least 32 bytes, got {}",
35 bytes.len()
36 );
37 Ok(Address::from_slice(&bytes[12..32]))
38}
39
40pub fn extract_u32_from_bytes(bytes: &[u8]) -> anyhow::Result<u32> {
49 anyhow::ensure!(
50 bytes.len() >= 32,
51 "Topic must be at least 32 bytes, got {}",
52 bytes.len()
53 );
54 Ok(u32::from_be_bytes(bytes[28..32].try_into()?))
55}
56
57pub fn extract_i32_from_bytes(bytes: &[u8]) -> anyhow::Result<i32> {
66 anyhow::ensure!(
67 bytes.len() >= 32,
68 "Topic must be at least 32 bytes, got {}",
69 bytes.len()
70 );
71 Ok(i32::from_be_bytes(bytes[28..32].try_into()?))
72}
73
74pub fn validate_signature_bytes(
84 actual: &[u8],
85 expected_hex: &str,
86 event_name: &str,
87) -> anyhow::Result<()> {
88 let actual_hex = hex::encode(actual);
89 anyhow::ensure!(
90 actual_hex == expected_hex,
91 "Invalid event signature for '{event_name}': expected {expected_hex}, got {actual_hex}",
92 );
93 Ok(())
94}
95
96#[cfg(test)]
97mod tests {
98 use rstest::rstest;
99
100 use super::*;
101
102 #[rstest]
103 fn test_extract_address_token0() {
104 let bytes = hex::decode("0000000000000000000000002e5353426c89f4ecd52d1036da822d47e73376c4")
106 .unwrap();
107
108 let address = extract_address_from_bytes(&bytes).unwrap();
109 assert_eq!(
110 address.to_string().to_lowercase(),
111 "0x2e5353426c89f4ecd52d1036da822d47e73376c4"
112 );
113 }
114
115 #[rstest]
116 fn test_extract_address_token1_block() {
117 let bytes = hex::decode("000000000000000000000000838930cfe7502dd36b0b1ebbef8001fbf94f3bfb")
119 .unwrap();
120
121 let address = extract_address_from_bytes(&bytes).unwrap();
122 assert_eq!(
123 address.to_string().to_lowercase(),
124 "0x838930cfe7502dd36b0b1ebbef8001fbf94f3bfb"
125 );
126 }
127
128 #[rstest]
129 fn test_extract_address_from_bytes_too_short() {
130 let bytes = vec![0u8; 31];
131 let result = extract_address_from_bytes(&bytes);
132 assert!(result.is_err());
133 assert!(
134 result
135 .unwrap_err()
136 .to_string()
137 .contains("Topic must be at least 32 bytes")
138 );
139 }
140
141 #[rstest]
142 fn test_extract_u32_fee_3000() {
143 let bytes = hex::decode("0000000000000000000000000000000000000000000000000000000000000bb8")
144 .unwrap();
145
146 let value = extract_u32_from_bytes(&bytes).unwrap();
147 assert_eq!(value, 3000);
148 }
149
150 #[rstest]
151 fn test_extract_u32_fee_500() {
152 let bytes = hex::decode("00000000000000000000000000000000000000000000000000000000000001f4")
153 .unwrap();
154
155 let value = extract_u32_from_bytes(&bytes).unwrap();
156 assert_eq!(value, 500);
157 }
158
159 #[rstest]
160 fn test_extract_i32_tick_spacing_60() {
161 let bytes = hex::decode("000000000000000000000000000000000000000000000000000000000000003c")
162 .unwrap();
163
164 let value = extract_i32_from_bytes(&bytes).unwrap();
165 assert_eq!(value, 60);
166 }
167
168 #[rstest]
169 fn test_extract_i32_tick_spacing_10() {
170 let bytes = hex::decode("000000000000000000000000000000000000000000000000000000000000000a")
171 .unwrap();
172
173 let value = extract_i32_from_bytes(&bytes).unwrap();
174 assert_eq!(value, 10);
175 }
176
177 #[rstest]
178 fn test_extract_i32_from_bytes_negative() {
179 let bytes = hex::decode("ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc4")
180 .unwrap();
181
182 let value = extract_i32_from_bytes(&bytes).unwrap();
183 assert_eq!(value, -60);
184 }
185
186 #[rstest]
187 fn test_validate_signature_pool_created() {
188 let pool_created_signature =
189 hex::decode("783cca1c0412dd0d695e784568c96da2e9c22ff989357a2e8b1d9b2b4e6b7118")
190 .unwrap();
191 let expected = "783cca1c0412dd0d695e784568c96da2e9c22ff989357a2e8b1d9b2b4e6b7118";
192
193 let result = validate_signature_bytes(&pool_created_signature, expected, "PoolCreated");
194 assert!(result.is_ok());
195 }
196
197 #[rstest]
198 fn test_validate_signature_bytes_mismatch() {
199 let pool_created_signature =
200 hex::decode("783cca1c0412dd0d695e784568c96da2e9c22ff989357a2e8b1d9b2b4e6b7118")
201 .unwrap();
202 let swap_expected = "c42079f94a6350d7e6235f29174924f928cc2ac818eb64fed8004e115fbcca67";
203
204 let result = validate_signature_bytes(&pool_created_signature, swap_expected, "Swap");
205 assert!(result.is_err());
206 assert!(
207 result
208 .unwrap_err()
209 .to_string()
210 .contains("Invalid event signature for 'Swap'")
211 );
212 }
213}