use alloy::primitives::Address;
pub fn extract_address_from_bytes(bytes: &[u8]) -> anyhow::Result<Address> {
anyhow::ensure!(
bytes.len() >= 32,
"Topic must be at least 32 bytes, got {}",
bytes.len()
);
Ok(Address::from_slice(&bytes[12..32]))
}
pub fn extract_u32_from_bytes(bytes: &[u8]) -> anyhow::Result<u32> {
anyhow::ensure!(
bytes.len() >= 32,
"Topic must be at least 32 bytes, got {}",
bytes.len()
);
Ok(u32::from_be_bytes(bytes[28..32].try_into()?))
}
pub fn extract_i32_from_bytes(bytes: &[u8]) -> anyhow::Result<i32> {
anyhow::ensure!(
bytes.len() >= 32,
"Topic must be at least 32 bytes, got {}",
bytes.len()
);
Ok(i32::from_be_bytes(bytes[28..32].try_into()?))
}
pub fn validate_signature_bytes(
actual: &[u8],
expected_hex: &str,
event_name: &str,
) -> anyhow::Result<()> {
let actual_hex = hex::encode(actual);
anyhow::ensure!(
actual_hex == expected_hex,
"Invalid event signature for '{event_name}': expected {expected_hex}, got {actual_hex}",
);
Ok(())
}
#[cfg(test)]
mod tests {
use rstest::rstest;
use super::*;
#[rstest]
fn test_extract_address_token0() {
let bytes = hex::decode("0000000000000000000000002e5353426c89f4ecd52d1036da822d47e73376c4")
.unwrap();
let address = extract_address_from_bytes(&bytes).unwrap();
assert_eq!(
address.to_string().to_lowercase(),
"0x2e5353426c89f4ecd52d1036da822d47e73376c4"
);
}
#[rstest]
fn test_extract_address_token1_block() {
let bytes = hex::decode("000000000000000000000000838930cfe7502dd36b0b1ebbef8001fbf94f3bfb")
.unwrap();
let address = extract_address_from_bytes(&bytes).unwrap();
assert_eq!(
address.to_string().to_lowercase(),
"0x838930cfe7502dd36b0b1ebbef8001fbf94f3bfb"
);
}
#[rstest]
fn test_extract_address_from_bytes_too_short() {
let bytes = vec![0u8; 31];
let result = extract_address_from_bytes(&bytes);
assert!(result.is_err());
assert!(
result
.unwrap_err()
.to_string()
.contains("Topic must be at least 32 bytes")
);
}
#[rstest]
fn test_extract_u32_fee_3000() {
let bytes = hex::decode("0000000000000000000000000000000000000000000000000000000000000bb8")
.unwrap();
let value = extract_u32_from_bytes(&bytes).unwrap();
assert_eq!(value, 3000);
}
#[rstest]
fn test_extract_u32_fee_500() {
let bytes = hex::decode("00000000000000000000000000000000000000000000000000000000000001f4")
.unwrap();
let value = extract_u32_from_bytes(&bytes).unwrap();
assert_eq!(value, 500);
}
#[rstest]
fn test_extract_i32_tick_spacing_60() {
let bytes = hex::decode("000000000000000000000000000000000000000000000000000000000000003c")
.unwrap();
let value = extract_i32_from_bytes(&bytes).unwrap();
assert_eq!(value, 60);
}
#[rstest]
fn test_extract_i32_tick_spacing_10() {
let bytes = hex::decode("000000000000000000000000000000000000000000000000000000000000000a")
.unwrap();
let value = extract_i32_from_bytes(&bytes).unwrap();
assert_eq!(value, 10);
}
#[rstest]
fn test_extract_i32_from_bytes_negative() {
let bytes = hex::decode("ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc4")
.unwrap();
let value = extract_i32_from_bytes(&bytes).unwrap();
assert_eq!(value, -60);
}
#[rstest]
fn test_validate_signature_pool_created() {
let pool_created_signature =
hex::decode("783cca1c0412dd0d695e784568c96da2e9c22ff989357a2e8b1d9b2b4e6b7118")
.unwrap();
let expected = "783cca1c0412dd0d695e784568c96da2e9c22ff989357a2e8b1d9b2b4e6b7118";
let result = validate_signature_bytes(&pool_created_signature, expected, "PoolCreated");
assert!(result.is_ok());
}
#[rstest]
fn test_validate_signature_bytes_mismatch() {
let pool_created_signature =
hex::decode("783cca1c0412dd0d695e784568c96da2e9c22ff989357a2e8b1d9b2b4e6b7118")
.unwrap();
let swap_expected = "c42079f94a6350d7e6235f29174924f928cc2ac818eb64fed8004e115fbcca67";
let result = validate_signature_bytes(&pool_created_signature, swap_expected, "Swap");
assert!(result.is_err());
assert!(
result
.unwrap_err()
.to_string()
.contains("Invalid event signature for 'Swap'")
);
}
}