chaincodec_evm/
fingerprint.rs1use chaincodec_core::event::EventFingerprint;
11use tiny_keccak::{Hasher, Keccak};
12
13pub fn keccak256_signature(signature: &str) -> EventFingerprint {
16 let mut hasher = Keccak::v256();
17 let mut output = [0u8; 32];
18 hasher.update(signature.as_bytes());
19 hasher.finalize(&mut output);
20 EventFingerprint::new(format!("0x{}", hex::encode(output)))
21}
22
23pub fn from_topics(topics: &[String]) -> Option<EventFingerprint> {
26 let first = topics.first()?;
27 let hex = first.strip_prefix("0x").unwrap_or(first);
29 if hex.len() == 64 && hex.chars().all(|c| c.is_ascii_hexdigit()) {
30 Some(EventFingerprint::new(first.clone()))
31 } else {
32 None
33 }
34}
35
36#[cfg(test)]
37mod tests {
38 use super::*;
39
40 #[test]
41 fn uniswap_v3_swap_fingerprint() {
42 let sig = "Swap(address,address,int256,int256,uint160,uint128,int24)";
44 let fp = keccak256_signature(sig);
45 assert_eq!(
46 fp.as_hex(),
47 "0xc42079f94a6350d7e6235f29174924f928cc2ac818eb64fed8004e115fbcca67"
48 );
49 }
50
51 #[test]
52 fn erc20_transfer_fingerprint() {
53 let sig = "Transfer(address,address,uint256)";
54 let fp = keccak256_signature(sig);
55 assert_eq!(
56 fp.as_hex(),
57 "0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef"
58 );
59 }
60
61 #[test]
62 fn from_topics_valid() {
63 let topics = vec![
64 "0xc42079f94a6350d7e6235f29174924f928cc2ac818eb64fed8004e115fbcca67".to_string(),
65 ];
66 let fp = from_topics(&topics);
67 assert!(fp.is_some());
68 }
69
70 #[test]
71 fn from_topics_empty() {
72 assert!(from_topics(&[]).is_none());
73 }
74}