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