use alloy::{dyn_abi::SolType, primitives::Address, sol};
use nautilus_model::defi::{PoolIdentifier, SharedDex, rpc::RpcLog};
use ustr::Ustr;
use crate::{
events::fee_protocol_update::FeeProtocolUpdateEvent,
hypersync::{
HypersyncLog,
helpers::{
extract_block_number, extract_log_index, extract_transaction_hash,
extract_transaction_index, validate_event_signature_hash,
},
},
rpc::helpers as rpc_helpers,
};
const FEE_PROTOCOL_UPDATE_EVENT_SIGNATURE_HASH: &str =
"973d8d92bb299f4af6ce49b52a8adb85ae46b9f214c4c4fc06ac77401237b133";
sol! {
struct SetFeeProtocolEventData {
uint8 fee_protocol0_old;
uint8 fee_protocol1_old;
uint8 fee_protocol0_new;
uint8 fee_protocol1_new;
}
}
pub fn parse_fee_protocol_update_event_hypersync(
dex: SharedDex,
log: &HypersyncLog,
) -> anyhow::Result<FeeProtocolUpdateEvent> {
validate_event_signature_hash(
"SetFeeProtocolEvent",
FEE_PROTOCOL_UPDATE_EVENT_SIGNATURE_HASH,
log,
)?;
if let Some(data) = &log.data {
let data_bytes = data.as_ref();
if data_bytes.len() < 4 * 32 {
anyhow::bail!("SetFeeProtocol event data is too short");
}
let decoded = match <SetFeeProtocolEventData as SolType>::abi_decode(data_bytes) {
Ok(decoded) => decoded,
Err(e) => anyhow::bail!("Failed to decode SetFeeProtocol event data: {e}"),
};
let pool_address = Address::from_slice(
log.address
.clone()
.expect("Contract address should be set in logs")
.as_ref(),
);
let pool_identifier = PoolIdentifier::Address(Ustr::from(&pool_address.to_string()));
Ok(FeeProtocolUpdateEvent::new(
dex,
pool_identifier,
extract_block_number(log)?,
extract_transaction_hash(log)?,
extract_transaction_index(log)?,
extract_log_index(log)?,
decoded.fee_protocol0_new,
decoded.fee_protocol1_new,
))
} else {
anyhow::bail!("Missing data in SetFeeProtocol event log");
}
}
pub fn parse_fee_protocol_update_event_rpc(
dex: SharedDex,
log: &RpcLog,
) -> anyhow::Result<FeeProtocolUpdateEvent> {
rpc_helpers::validate_event_signature(
log,
FEE_PROTOCOL_UPDATE_EVENT_SIGNATURE_HASH,
"SetFeeProtocol",
)?;
let data_bytes = rpc_helpers::extract_data_bytes(log)?;
if data_bytes.len() < 4 * 32 {
anyhow::bail!("SetFeeProtocol event data is too short");
}
let decoded = match <SetFeeProtocolEventData as SolType>::abi_decode(&data_bytes) {
Ok(decoded) => decoded,
Err(e) => anyhow::bail!("Failed to decode SetFeeProtocol event data: {e}"),
};
let pool_address = rpc_helpers::extract_address(log)?;
let pool_identifier = PoolIdentifier::Address(Ustr::from(&pool_address.to_string()));
Ok(FeeProtocolUpdateEvent::new(
dex,
pool_identifier,
rpc_helpers::extract_block_number(log)?,
rpc_helpers::extract_transaction_hash(log)?,
rpc_helpers::extract_transaction_index(log)?,
rpc_helpers::extract_log_index(log)?,
decoded.fee_protocol0_new,
decoded.fee_protocol1_new,
))
}
#[cfg(test)]
mod tests {
use rstest::*;
use super::*;
use crate::exchanges::arbitrum;
const HYPERSYNC_LOG: &str =
include_str!("../../../../test_data/uniswap_v3_set_fee_protocol_hypersync.json");
const RPC_LOG: &str =
include_str!("../../../../test_data/uniswap_v3_set_fee_protocol_rpc.json");
#[fixture]
fn hypersync_log() -> HypersyncLog {
serde_json::from_str(HYPERSYNC_LOG).expect("Failed to deserialize HyperSync log")
}
#[fixture]
fn rpc_log() -> RpcLog {
serde_json::from_str(RPC_LOG).expect("Failed to deserialize RPC log")
}
#[rstest]
fn test_parse_fee_protocol_update_event_hypersync(hypersync_log: HypersyncLog) {
let dex = arbitrum::UNISWAP_V3.dex.clone();
let event = parse_fee_protocol_update_event_hypersync(dex, &hypersync_log).unwrap();
assert_eq!(
event.pool_identifier.to_string(),
"0x0d500E0f1d159E75f3771Fb5e6aB86DE19A8abD4"
);
assert_eq!(event.fee_protocol0_new, 6);
assert_eq!(event.fee_protocol1_new, 6);
assert_eq!(event.block_number, 3_106_049);
assert_eq!(event.transaction_index, 0);
assert_eq!(event.log_index, 0);
}
#[rstest]
fn test_parse_fee_protocol_update_event_rpc(rpc_log: RpcLog) {
let dex = arbitrum::UNISWAP_V3.dex.clone();
let event = parse_fee_protocol_update_event_rpc(dex, &rpc_log).unwrap();
assert_eq!(
event.pool_identifier.to_string(),
"0x0d500E0f1d159E75f3771Fb5e6aB86DE19A8abD4"
);
assert_eq!(event.fee_protocol0_new, 6);
assert_eq!(event.fee_protocol1_new, 6);
assert_eq!(event.block_number, 3_106_049);
}
#[rstest]
fn test_hypersync_rpc_match(hypersync_log: HypersyncLog, rpc_log: RpcLog) {
let dex = arbitrum::UNISWAP_V3.dex.clone();
let event_hypersync =
parse_fee_protocol_update_event_hypersync(dex.clone(), &hypersync_log).unwrap();
let event_rpc = parse_fee_protocol_update_event_rpc(dex, &rpc_log).unwrap();
assert_eq!(event_hypersync.pool_identifier, event_rpc.pool_identifier);
assert_eq!(
event_hypersync.fee_protocol0_new,
event_rpc.fee_protocol0_new
);
assert_eq!(
event_hypersync.fee_protocol1_new,
event_rpc.fee_protocol1_new
);
assert_eq!(event_hypersync.block_number, event_rpc.block_number);
assert_eq!(event_hypersync.transaction_hash, event_rpc.transaction_hash);
assert_eq!(
event_hypersync.transaction_index,
event_rpc.transaction_index
);
assert_eq!(event_hypersync.log_index, event_rpc.log_index);
}
}