nautilus_blockchain/exchanges/parsing/uniswap_v2/
pool_created.rs1use alloy::primitives::Address;
17use nautilus_model::defi::{PoolIdentifier, rpc::RpcLog};
18use ustr::Ustr;
19
20use crate::{
21 events::pool_created::PoolCreatedEvent,
22 hypersync::{
23 HypersyncLog,
24 log::{extract_address_from_topic, extract_block_number, validate_event_signature_hash},
25 },
26 rpc::log as rpc_log,
27};
28
29const PAIR_CREATED_EVENT_SIGNATURE_HASH: &str =
30 "0d3648bd0f6ba80134a33ba9275ac585d9d315f0ad8355cddefde31afa28d0e9";
31
32pub fn parse_pool_created_event_hypersync(log: HypersyncLog) -> anyhow::Result<PoolCreatedEvent> {
44 validate_event_signature_hash("PairCreatedEvent", PAIR_CREATED_EVENT_SIGNATURE_HASH, &log)?;
45
46 let block_number = extract_block_number(&log)?;
47 let token0 = extract_address_from_topic(&log, 1, "token0")?;
48 let token1 = extract_address_from_topic(&log, 2, "token1")?;
49
50 if let Some(data) = log.data {
51 let data_bytes = data.as_ref();
53
54 anyhow::ensure!(
55 data_bytes.len() >= 32,
56 "PairCreated event data too short: expected at least 32 bytes, was {}",
57 data_bytes.len()
58 );
59
60 let pair_address = Address::from_slice(&data_bytes[12..32]);
62 let pool_identifier = PoolIdentifier::Address(Ustr::from(&pair_address.to_string()));
63
64 Ok(PoolCreatedEvent::new(
65 block_number,
66 token0,
67 token1,
68 pair_address,
69 pool_identifier, None, None, ))
73 } else {
74 Err(anyhow::anyhow!("Missing data in pair created event log"))
75 }
76}
77
78pub fn parse_pool_created_event_rpc(log: &RpcLog) -> anyhow::Result<PoolCreatedEvent> {
84 rpc_log::validate_event_signature(log, PAIR_CREATED_EVENT_SIGNATURE_HASH, "PairCreatedEvent")?;
85
86 let block_number = rpc_log::extract_block_number(log)?;
87 let token0 = rpc_log::extract_address_from_topic(log, 1, "token0")?;
88 let token1 = rpc_log::extract_address_from_topic(log, 2, "token1")?;
89
90 let data_bytes = rpc_log::extract_data_bytes(log)?;
92
93 anyhow::ensure!(
94 data_bytes.len() >= 32,
95 "PairCreated event data too short: expected at least 32 bytes, was {}",
96 data_bytes.len()
97 );
98
99 let pair_address = Address::from_slice(&data_bytes[12..32]);
101 let pool_identifier = PoolIdentifier::Address(Ustr::from(&pair_address.to_string()));
102
103 Ok(PoolCreatedEvent::new(
104 block_number,
105 token0,
106 token1,
107 pair_address,
108 pool_identifier, None, None, ))
112}
113
114#[cfg(test)]
115mod tests {
116 use rstest::{fixture, rstest};
117 use serde_json::json;
118
119 use super::*;
120
121 #[fixture]
127 fn hypersync_log_weth_usdt() -> HypersyncLog {
128 let log_json = json!({
129 "removed": null,
130 "log_index": "0x0",
131 "transaction_index": "0x1",
132 "transaction_hash": "0xe7b5c25477c6dd2425c4bc07547ffb2777e018a12eed1d348d7bf553913d97b7",
133 "block_hash": null,
134 "block_number": "0x8fcb296",
135 "address": "0xf1d7cc64fb4452f05c498126312ebe29f30fbcf9",
136 "data": "0x000000000000000000000000f64dfe17c8b87f012fcf50fbda1d62bfa148366a0000000000000000000000000000000000000000000000000000000000000001",
137 "topics": [
138 "0x0d3648bd0f6ba80134a33ba9275ac585d9d315f0ad8355cddefde31afa28d0e9",
139 "0x00000000000000000000000082af49447d8a07e3bd95bd0d56f35241523fbab1",
140 "0x000000000000000000000000af88d065e77c8cc2239327c5edb3a432268e5831"
141 ]
142 });
143 serde_json::from_value(log_json).expect("Failed to deserialize HyperSync log")
144 }
145
146 #[fixture]
147 fn rpc_log_weth_usdt() -> RpcLog {
148 let log_json = json!({
149 "removed": false,
150 "logIndex": "0x0",
151 "transactionIndex": "0x1",
152 "transactionHash": "0xe7b5c25477c6dd2425c4bc07547ffb2777e018a12eed1d348d7bf553913d97b7",
153 "blockHash": "0x5053fe02da5bb0c2fc690a467c1cc36e791047fc48c3ea4fe8bbeed069f3f7ba",
154 "blockNumber": "0x8fcb296",
155 "address": "0xf1d7cc64fb4452f05c498126312ebe29f30fbcf9",
156 "data": "0x000000000000000000000000f64dfe17c8b87f012fcf50fbda1d62bfa148366a0000000000000000000000000000000000000000000000000000000000000001",
157 "topics": [
158 "0x0d3648bd0f6ba80134a33ba9275ac585d9d315f0ad8355cddefde31afa28d0e9",
159 "0x00000000000000000000000082af49447d8a07e3bd95bd0d56f35241523fbab1",
160 "0x000000000000000000000000af88d065e77c8cc2239327c5edb3a432268e5831"
161 ]
162 });
163 serde_json::from_value(log_json).expect("Failed to deserialize RPC log")
164 }
165
166 #[rstest]
167 fn test_parse_pair_created_hypersync(hypersync_log_weth_usdt: HypersyncLog) {
168 let event =
169 parse_pool_created_event_hypersync(hypersync_log_weth_usdt).expect("Failed to parse");
170
171 assert_eq!(event.block_number, 150778518);
172 assert_eq!(
173 event.token0.to_string().to_lowercase(),
174 "0x82af49447d8a07e3bd95bd0d56f35241523fbab1"
175 );
176 assert_eq!(
177 event.token1.to_string().to_lowercase(),
178 "0xaf88d065e77c8cc2239327c5edb3a432268e5831"
179 );
180 assert_eq!(
181 event.pool_identifier.to_string(),
182 "0xF64Dfe17C8b87F012FCf50FbDA1D62bfA148366a",
183 );
184 assert_eq!(event.fee, None);
185 assert_eq!(event.tick_spacing, None);
186 }
187
188 #[rstest]
189 fn test_parse_pair_created_rpc(rpc_log_weth_usdt: RpcLog) {
190 let event = parse_pool_created_event_rpc(&rpc_log_weth_usdt).expect("Failed to parse");
191
192 assert_eq!(event.block_number, 150778518);
193 assert_eq!(
194 event.token0.to_string().to_lowercase(),
195 "0x82af49447d8a07e3bd95bd0d56f35241523fbab1"
196 );
197 assert_eq!(
198 event.token1.to_string().to_lowercase(),
199 "0xaf88d065e77c8cc2239327c5edb3a432268e5831"
200 );
201 assert_eq!(
202 event.pool_identifier.to_string(),
203 "0xF64Dfe17C8b87F012FCf50FbDA1D62bfA148366a"
204 );
205 assert_eq!(event.fee, None);
206 assert_eq!(event.tick_spacing, None);
207 }
208
209 #[rstest]
210 fn test_hypersync_rpc_match(hypersync_log_weth_usdt: HypersyncLog, rpc_log_weth_usdt: RpcLog) {
211 let hypersync_event =
212 parse_pool_created_event_hypersync(hypersync_log_weth_usdt).expect("HyperSync parse");
213 let rpc_event = parse_pool_created_event_rpc(&rpc_log_weth_usdt).expect("RPC parse");
214
215 assert_eq!(hypersync_event.block_number, rpc_event.block_number);
216 assert_eq!(hypersync_event.token0, rpc_event.token0);
217 assert_eq!(hypersync_event.token1, rpc_event.token1);
218 assert_eq!(hypersync_event.pool_identifier, rpc_event.pool_identifier);
219 assert_eq!(hypersync_event.fee, rpc_event.fee);
220 assert_eq!(hypersync_event.tick_spacing, rpc_event.tick_spacing);
221 }
222}