nautilus_blockchain/hypersync/
helpers.rs1pub fn extract_transaction_hash(
22 log: &hypersync_client::simple_types::Log,
23) -> anyhow::Result<String> {
24 log.transaction_hash
25 .as_ref()
26 .map(ToString::to_string)
27 .ok_or_else(|| anyhow::anyhow!("Missing transaction hash in log"))
28}
29
30pub fn extract_transaction_index(log: &hypersync_client::simple_types::Log) -> anyhow::Result<u32> {
36 log.transaction_index
37 .as_ref()
38 .map(|index| **index as u32)
39 .ok_or_else(|| anyhow::anyhow!("Missing transaction index in the log"))
40}
41
42pub fn extract_log_index(log: &hypersync_client::simple_types::Log) -> anyhow::Result<u32> {
48 log.log_index
49 .as_ref()
50 .map(|index| **index as u32)
51 .ok_or_else(|| anyhow::anyhow!("Missing log index in the log"))
52}
53
54pub fn extract_block_number(log: &hypersync_client::simple_types::Log) -> anyhow::Result<u64> {
60 log.block_number
61 .as_ref()
62 .map(|number| **number)
63 .ok_or_else(|| anyhow::anyhow!("Missing block number in the log"))
64}
65
66pub fn validate_event_signature_hash(
68 event_name: &str,
69 event_signature_hash: &str,
70 log: &hypersync_client::simple_types::Log,
71) -> anyhow::Result<()> {
72 if let Some(topic) = log.topics.first().and_then(|t| t.as_ref()) {
73 if hex::encode(topic) != event_signature_hash {
74 anyhow::bail!("Invalid event signature for event '{event_name}'");
75 }
76 } else {
77 anyhow::bail!("Missing event signature in topic0 for event '{event_name}'");
78 }
79 Ok(())
80}
81
82#[cfg(test)]
83mod tests {
84 use hypersync_client::simple_types::Log;
85 use rstest::*;
86 use serde_json::json;
87
88 use super::*;
89
90 #[fixture]
91 fn swap_log_1() -> Log {
92 let log_json = json!({
93 "removed": null,
94 "log_index": null,
95 "transaction_index": null,
96 "transaction_hash": null,
97 "block_hash": null,
98 "block_number": "0x1581b7e",
99 "address": "0x88e6a0c2ddd26feeb64f039a2c41296fcb3f5640",
100 "data": "0x",
101 "topics": [
102 "0xc42079f94a6350d7e6235f29174924f928cc2ac818eb64fed8004e115fbcca67",
103 "0x0000000000000000000000003fc91a3afd70395cd496c647d5a6cc9d4b2b7fad",
104 "0x0000000000000000000000003fc91a3afd70395cd496c647d5a6cc9d4b2b7fad",
105 null
106 ]
107 });
108 serde_json::from_value(log_json).expect("Failed to deserialize log")
109 }
110
111 #[fixture]
112 fn swap_log_2() -> Log {
113 let log_json = json!({
114 "removed": null,
115 "log_index": null,
116 "transaction_index": null,
117 "transaction_hash": null,
118 "block_hash": null,
119 "block_number": "0x1581b82",
120 "address": "0x88e6a0c2ddd26feeb64f039a2c41296fcb3f5640",
121 "data": "0x",
122 "topics": [
123 "0xc42079f94a6350d7e6235f29174924f928cc2ac818eb64fed8004e115fbcca67",
124 "0x00000000000000000000000066a9893cc07d91d95644aedd05d03f95e1dba8af",
125 "0x000000000000000000000000f90321d0ecad58ab2b0c8c79db8aaeeefa023578",
126 null
127 ]
128 });
129 serde_json::from_value(log_json).expect("Failed to deserialize log")
130 }
131
132 #[fixture]
133 fn log_without_topics() -> Log {
134 let log_json = json!({
135 "removed": null,
136 "log_index": null,
137 "transaction_index": null,
138 "transaction_hash": null,
139 "block_hash": null,
140 "block_number": "0x1581b82",
141 "address": "0x88e6a0c2ddd26feeb64f039a2c41296fcb3f5640",
142 "data": "0x",
143 "topics": []
144 });
145 serde_json::from_value(log_json).expect("Failed to deserialize log")
146 }
147
148 #[fixture]
149 fn log_with_none_topic0() -> Log {
150 let log_json = json!({
151 "removed": null,
152 "log_index": null,
153 "transaction_index": null,
154 "transaction_hash": null,
155 "block_hash": null,
156 "block_number": "0x1581b82",
157 "address": "0x88e6a0c2ddd26feeb64f039a2c41296fcb3f5640",
158 "data": "0x",
159 "topics": [null]
160 });
161 serde_json::from_value(log_json).expect("Failed to deserialize log")
162 }
163
164 #[rstest]
165 fn test_validate_event_signature_hash_success(swap_log_1: Log) {
166 let expected_hash = "c42079f94a6350d7e6235f29174924f928cc2ac818eb64fed8004e115fbcca67";
168
169 let result = validate_event_signature_hash("Swap", expected_hash, &swap_log_1);
170 assert!(result.is_ok());
171 }
172
173 #[rstest]
174 fn test_validate_event_signature_hash_success_log2(swap_log_2: Log) {
175 let expected_hash = "c42079f94a6350d7e6235f29174924f928cc2ac818eb64fed8004e115fbcca67";
177
178 let result = validate_event_signature_hash("Swap", expected_hash, &swap_log_2);
179 assert!(result.is_ok());
180 }
181
182 #[rstest]
183 fn test_validate_event_signature_hash_mismatch(swap_log_1: Log) {
184 let wrong_hash = "ddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef";
186
187 let result = validate_event_signature_hash("Transfer", wrong_hash, &swap_log_1);
188 assert!(result.is_err());
189 assert_eq!(
190 result.unwrap_err().to_string(),
191 "Invalid event signature for event 'Transfer'"
192 );
193 }
194
195 #[rstest]
196 fn test_validate_event_signature_hash_missing_topic0(log_without_topics: Log) {
197 let expected_hash = "c42079f94a6350d7e6235f29174924f928cc2ac818eb64fed8004e115fbcca67";
198
199 let result = validate_event_signature_hash("Swap", expected_hash, &log_without_topics);
200 assert!(result.is_err());
201 assert_eq!(
202 result.unwrap_err().to_string(),
203 "Missing event signature in topic0 for event 'Swap'"
204 );
205 }
206
207 #[rstest]
208 fn test_validate_event_signature_hash_none_topic0(log_with_none_topic0: Log) {
209 let expected_hash = "c42079f94a6350d7e6235f29174924f928cc2ac818eb64fed8004e115fbcca67";
210
211 let result = validate_event_signature_hash("Swap", expected_hash, &log_with_none_topic0);
212 assert!(result.is_err());
213 assert_eq!(
214 result.unwrap_err().to_string(),
215 "Missing event signature in topic0 for event 'Swap'"
216 );
217 }
218
219 #[rstest]
220 fn test_extract_transaction_hash_success() {
221 let log_json = json!({
222 "removed": null,
223 "log_index": null,
224 "transaction_index": null,
225 "transaction_hash": "0x1234567890abcdef1234567890abcdef1234567890abcdef1234567890abcdef",
226 "block_hash": null,
227 "block_number": "0x1581b82",
228 "address": "0x88e6a0c2ddd26feeb64f039a2c41296fcb3f5640",
229 "data": "0x",
230 "topics": []
231 });
232 let log: Log = serde_json::from_value(log_json).expect("Failed to deserialize log");
233
234 let result = extract_transaction_hash(&log);
235 assert!(result.is_ok());
236 assert_eq!(
237 result.unwrap(),
238 "0x1234567890abcdef1234567890abcdef1234567890abcdef1234567890abcdef"
239 );
240 }
241
242 #[rstest]
243 fn test_extract_transaction_hash_missing() {
244 let log_json = json!({
245 "removed": null,
246 "log_index": null,
247 "transaction_index": null,
248 "transaction_hash": null,
249 "block_hash": null,
250 "block_number": "0x1581b82",
251 "address": "0x88e6a0c2ddd26feeb64f039a2c41296fcb3f5640",
252 "data": "0x",
253 "topics": []
254 });
255 let log: Log = serde_json::from_value(log_json).expect("Failed to deserialize log");
256
257 let result = extract_transaction_hash(&log);
258 assert!(result.is_err());
259 assert_eq!(
260 result.unwrap_err().to_string(),
261 "Missing transaction hash in log"
262 );
263 }
264
265 #[rstest]
266 fn test_extract_transaction_index_success() {
267 let log_json = json!({
268 "removed": null,
269 "log_index": null,
270 "transaction_index": "0x5",
271 "transaction_hash": null,
272 "block_hash": null,
273 "block_number": "0x1581b82",
274 "address": "0x88e6a0c2ddd26feeb64f039a2c41296fcb3f5640",
275 "data": "0x",
276 "topics": []
277 });
278 let log: Log = serde_json::from_value(log_json).expect("Failed to deserialize log");
279
280 let result = extract_transaction_index(&log);
281 assert!(result.is_ok());
282 assert_eq!(result.unwrap(), 5u32);
283 }
284
285 #[rstest]
286 fn test_extract_transaction_index_missing() {
287 let log_json = json!({
288 "removed": null,
289 "log_index": null,
290 "transaction_index": null,
291 "transaction_hash": null,
292 "block_hash": null,
293 "block_number": "0x1581b82",
294 "address": "0x88e6a0c2ddd26feeb64f039a2c41296fcb3f5640",
295 "data": "0x",
296 "topics": []
297 });
298 let log: Log = serde_json::from_value(log_json).expect("Failed to deserialize log");
299
300 let result = extract_transaction_index(&log);
301 assert!(result.is_err());
302 assert_eq!(
303 result.unwrap_err().to_string(),
304 "Missing transaction index in the log"
305 );
306 }
307
308 #[rstest]
309 fn test_extract_log_index_success() {
310 let log_json = json!({
311 "removed": null,
312 "log_index": "0xa",
313 "transaction_index": null,
314 "transaction_hash": null,
315 "block_hash": null,
316 "block_number": "0x1581b82",
317 "address": "0x88e6a0c2ddd26feeb64f039a2c41296fcb3f5640",
318 "data": "0x",
319 "topics": []
320 });
321 let log: Log = serde_json::from_value(log_json).expect("Failed to deserialize log");
322
323 let result = extract_log_index(&log);
324 assert!(result.is_ok());
325 assert_eq!(result.unwrap(), 10u32);
326 }
327
328 #[rstest]
329 fn test_extract_log_index_missing() {
330 let log_json = json!({
331 "removed": null,
332 "log_index": null,
333 "transaction_index": null,
334 "transaction_hash": null,
335 "block_hash": null,
336 "block_number": "0x1581b82",
337 "address": "0x88e6a0c2ddd26feeb64f039a2c41296fcb3f5640",
338 "data": "0x",
339 "topics": []
340 });
341 let log: Log = serde_json::from_value(log_json).expect("Failed to deserialize log");
342
343 let result = extract_log_index(&log);
344 assert!(result.is_err());
345 assert_eq!(
346 result.unwrap_err().to_string(),
347 "Missing log index in the log"
348 );
349 }
350
351 #[rstest]
352 fn test_extract_block_number_success() {
353 let log_json = json!({
354 "removed": null,
355 "log_index": null,
356 "transaction_index": null,
357 "transaction_hash": null,
358 "block_hash": null,
359 "block_number": "0x1581b82",
360 "address": "0x88e6a0c2ddd26feeb64f039a2c41296fcb3f5640",
361 "data": "0x",
362 "topics": []
363 });
364 let log: Log = serde_json::from_value(log_json).expect("Failed to deserialize log");
365
366 let result = extract_block_number(&log);
367 assert!(result.is_ok());
368 assert_eq!(result.unwrap(), 22551426u64); }
370
371 #[rstest]
372 fn test_extract_block_number_missing() {
373 let log_json = json!({
374 "removed": null,
375 "log_index": null,
376 "transaction_index": null,
377 "transaction_hash": null,
378 "block_hash": null,
379 "block_number": null,
380 "address": "0x88e6a0c2ddd26feeb64f039a2c41296fcb3f5640",
381 "data": "0x",
382 "topics": []
383 });
384 let log: Log = serde_json::from_value(log_json).expect("Failed to deserialize log");
385
386 let result = extract_block_number(&log);
387 assert!(result.is_err());
388 assert_eq!(
389 result.unwrap_err().to_string(),
390 "Missing block number in the log"
391 );
392 }
393}