nautilus_blockchain/exchanges/parsing/uniswap_v3/
initialize.rs1use alloy::{dyn_abi::SolType, primitives::Address, sol};
17use nautilus_model::defi::{PoolIdentifier, SharedDex, rpc::RpcLog};
18use ustr::Ustr;
19
20use crate::{
21 events::initialize::InitializeEvent,
22 hypersync::{HypersyncLog, log::validate_event_signature_hash},
23 rpc::log as rpc_log,
24};
25
26const INITIALIZE_EVENT_SIGNATURE_HASH: &str =
27 "98636036cb66a9c19a37435efc1e90142190214e8abeb821bdba3f2990dd4c95";
28
29sol! {
33 struct InitializeEventData {
34 uint160 sqrt_price_x96;
35 int24 tick;
36 }
37}
38
39pub fn parse_initialize_event_hypersync(
49 dex: SharedDex,
50 log: &HypersyncLog,
51) -> anyhow::Result<InitializeEvent> {
52 validate_event_signature_hash("InitializeEvent", INITIALIZE_EVENT_SIGNATURE_HASH, log)?;
53
54 if let Some(data) = &log.data {
55 let data_bytes = data.as_ref();
56
57 if data_bytes.len() < 2 * 32 {
59 anyhow::bail!("Initialize event data is too short");
60 }
61
62 let decoded = match <InitializeEventData as SolType>::abi_decode(data_bytes) {
64 Ok(decoded) => decoded,
65 Err(e) => anyhow::bail!("Failed to decode initialize event data: {e}"),
66 };
67
68 let pool_address = Address::from_slice(
69 log.address
70 .clone()
71 .expect("Contract address should be set in logs")
72 .as_ref(),
73 );
74 let pool_identifier = PoolIdentifier::Address(Ustr::from(&pool_address.to_string()));
75
76 Ok(InitializeEvent::new(
77 dex,
78 pool_identifier,
79 decoded.sqrt_price_x96,
80 i32::try_from(decoded.tick)?,
81 ))
82 } else {
83 Err(anyhow::anyhow!("Missing data in initialize event log"))
84 }
85}
86
87pub fn parse_initialize_event_rpc(dex: SharedDex, log: &RpcLog) -> anyhow::Result<InitializeEvent> {
93 rpc_log::validate_event_signature(log, INITIALIZE_EVENT_SIGNATURE_HASH, "Initialize")?;
94
95 let data_bytes = rpc_log::extract_data_bytes(log)?;
96
97 if data_bytes.len() < 2 * 32 {
99 anyhow::bail!("Initialize event data is too short");
100 }
101
102 let decoded = match <InitializeEventData as SolType>::abi_decode(&data_bytes) {
104 Ok(decoded) => decoded,
105 Err(e) => anyhow::bail!("Failed to decode initialize event data: {e}"),
106 };
107
108 let pool_address = rpc_log::extract_address(log)?;
109 let pool_identifier = PoolIdentifier::Address(Ustr::from(&pool_address.to_string()));
110 Ok(InitializeEvent::new(
111 dex,
112 pool_identifier,
113 decoded.sqrt_price_x96,
114 i32::try_from(decoded.tick)?,
115 ))
116}
117
118#[cfg(test)]
119mod tests {
120 use std::sync::Arc;
121
122 use alloy::primitives::U160;
123 use rstest::*;
124 use serde_json::json;
125
126 use super::*;
127 use crate::exchanges::arbitrum;
128
129 #[fixture]
134 fn hypersync_log() -> HypersyncLog {
135 let log_json = json!({
136 "removed": null,
137 "log_index": "0x4",
138 "transaction_index": "0x3",
139 "transaction_hash": "0x8f91d60156ea7a34a6bf1d411852f3ef2ad255ec84e493c9e902e4a1ff4a46af",
140 "block_hash": null,
141 "block_number": "0x175122df",
142 "address": "0xd13040d4fe917ee704158cfcb3338dcd2838b245",
143 "data": "0x0000000000000000000000000000000000000000003d409fc4ca983d2e3df335fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffddf06",
144 "topics": [
145 "0x98636036cb66a9c19a37435efc1e90142190214e8abeb821bdba3f2990dd4c95",
146 null,
147 null,
148 null
149 ]
150 });
151 serde_json::from_value(log_json).expect("Failed to deserialize HyperSync log")
152 }
153
154 #[fixture]
156 fn rpc_log() -> RpcLog {
157 let log_json = json!({
158 "removed": false,
159 "logIndex": "0x4",
160 "transactionIndex": "0x3",
161 "transactionHash": "0x8f91d60156ea7a34a6bf1d411852f3ef2ad255ec84e493c9e902e4a1ff4a46af",
162 "blockHash": "0xfc49f94161e2cdef8339c0b430868d64ee1f5d0bd8b8b6e45a25487958d68b25",
163 "blockNumber": "0x175122df",
164 "address": "0xd13040d4fe917ee704158cfcb3338dcd2838b245",
165 "data": "0x0000000000000000000000000000000000000000003d409fc4ca983d2e3df335fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffddf06",
166 "topics": [
167 "0x98636036cb66a9c19a37435efc1e90142190214e8abeb821bdba3f2990dd4c95"
168 ]
169 });
170 serde_json::from_value(log_json).expect("Failed to deserialize RPC log")
171 }
172
173 #[rstest]
174 fn test_parse_initialize_event_hypersync(hypersync_log: HypersyncLog) {
175 let dex = Arc::clone(&arbitrum::UNISWAP_V3.dex);
176 let event = parse_initialize_event_hypersync(dex, &hypersync_log).unwrap();
177
178 assert_eq!(
179 event.pool_identifier.to_string(),
180 "0xd13040d4fe917EE704158CfCB3338dCd2838B245"
181 );
182 let expected_sqrt_price = U160::from_str_radix("3d409fc4ca983d2e3df335", 16).unwrap();
183 assert_eq!(event.sqrt_price_x96, expected_sqrt_price);
184 assert_eq!(event.tick, -139514);
185 }
186
187 #[rstest]
188 fn test_parse_initialize_event_rpc(rpc_log: RpcLog) {
189 let dex = Arc::clone(&arbitrum::UNISWAP_V3.dex);
190 let event = parse_initialize_event_rpc(dex, &rpc_log).unwrap();
191
192 assert_eq!(
193 event.pool_identifier.to_string(),
194 "0xd13040d4fe917EE704158CfCB3338dCd2838B245"
195 );
196 let expected_sqrt_price = U160::from_str_radix("3d409fc4ca983d2e3df335", 16).unwrap();
197 assert_eq!(event.sqrt_price_x96, expected_sqrt_price);
198 assert_eq!(event.tick, -139514);
199 }
200
201 #[rstest]
202 fn test_hypersync_rpc_match(hypersync_log: HypersyncLog, rpc_log: RpcLog) {
203 let dex = Arc::clone(&arbitrum::UNISWAP_V3.dex);
204 let event_hypersync =
205 parse_initialize_event_hypersync(Arc::clone(&dex), &hypersync_log).unwrap();
206 let event_rpc = parse_initialize_event_rpc(dex, &rpc_log).unwrap();
207
208 assert_eq!(event_hypersync.pool_identifier, event_rpc.pool_identifier);
210 assert_eq!(event_hypersync.sqrt_price_x96, event_rpc.sqrt_price_x96);
211 assert_eq!(event_hypersync.tick, event_rpc.tick);
212 }
213}