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 helpers::{
25 extract_address_from_topic, extract_block_number, validate_event_signature_hash,
26 },
27 },
28 rpc::helpers as rpc_helpers,
29};
30
31const PAIR_CREATED_EVENT_SIGNATURE_HASH: &str =
32 "0d3648bd0f6ba80134a33ba9275ac585d9d315f0ad8355cddefde31afa28d0e9";
33
34pub fn parse_pool_created_event_hypersync(log: HypersyncLog) -> anyhow::Result<PoolCreatedEvent> {
50 validate_event_signature_hash("PairCreatedEvent", PAIR_CREATED_EVENT_SIGNATURE_HASH, &log)?;
51
52 let block_number = extract_block_number(&log)?;
53 let token0 = extract_address_from_topic(&log, 1, "token0")?;
54 let token1 = extract_address_from_topic(&log, 2, "token1")?;
55
56 if let Some(data) = log.data {
57 let data_bytes = data.as_ref();
59
60 anyhow::ensure!(
61 data_bytes.len() >= 32,
62 "PairCreated event data too short: expected at least 32 bytes, got {}",
63 data_bytes.len()
64 );
65
66 let pair_address = Address::from_slice(&data_bytes[12..32]);
68 let pool_identifier = PoolIdentifier::Address(Ustr::from(&pair_address.to_string()));
69
70 Ok(PoolCreatedEvent::new(
71 block_number,
72 token0,
73 token1,
74 pair_address,
75 pool_identifier, None, None, ))
79 } else {
80 Err(anyhow::anyhow!("Missing data in pair created event log"))
81 }
82}
83
84pub fn parse_pool_created_event_rpc(log: &RpcLog) -> anyhow::Result<PoolCreatedEvent> {
90 rpc_helpers::validate_event_signature(
91 log,
92 PAIR_CREATED_EVENT_SIGNATURE_HASH,
93 "PairCreatedEvent",
94 )?;
95
96 let block_number = rpc_helpers::extract_block_number(log)?;
97 let token0 = rpc_helpers::extract_address_from_topic(log, 1, "token0")?;
98 let token1 = rpc_helpers::extract_address_from_topic(log, 2, "token1")?;
99
100 let data_bytes = rpc_helpers::extract_data_bytes(log)?;
102
103 anyhow::ensure!(
104 data_bytes.len() >= 32,
105 "PairCreated event data too short: expected at least 32 bytes, got {}",
106 data_bytes.len()
107 );
108
109 let pair_address = Address::from_slice(&data_bytes[12..32]);
111 let pool_identifier = PoolIdentifier::Address(Ustr::from(&pair_address.to_string()));
112
113 Ok(PoolCreatedEvent::new(
114 block_number,
115 token0,
116 token1,
117 pair_address,
118 pool_identifier, None, None, ))
122}
123
124#[cfg(test)]
125mod tests {
126 use rstest::{fixture, rstest};
127 use serde_json::json;
128
129 use super::*;
130
131 #[fixture]
137 fn hypersync_log_weth_usdt() -> HypersyncLog {
138 let log_json = json!({
139 "removed": null,
140 "log_index": "0x0",
141 "transaction_index": "0x1",
142 "transaction_hash": "0xe7b5c25477c6dd2425c4bc07547ffb2777e018a12eed1d348d7bf553913d97b7",
143 "block_hash": null,
144 "block_number": "0x8fcb296",
145 "address": "0xf1d7cc64fb4452f05c498126312ebe29f30fbcf9",
146 "data": "0x000000000000000000000000f64dfe17c8b87f012fcf50fbda1d62bfa148366a0000000000000000000000000000000000000000000000000000000000000001",
147 "topics": [
148 "0x0d3648bd0f6ba80134a33ba9275ac585d9d315f0ad8355cddefde31afa28d0e9",
149 "0x00000000000000000000000082af49447d8a07e3bd95bd0d56f35241523fbab1",
150 "0x000000000000000000000000af88d065e77c8cc2239327c5edb3a432268e5831"
151 ]
152 });
153 serde_json::from_value(log_json).expect("Failed to deserialize HyperSync log")
154 }
155
156 #[fixture]
157 fn rpc_log_weth_usdt() -> RpcLog {
158 let log_json = json!({
159 "removed": false,
160 "logIndex": "0x0",
161 "transactionIndex": "0x1",
162 "transactionHash": "0xe7b5c25477c6dd2425c4bc07547ffb2777e018a12eed1d348d7bf553913d97b7",
163 "blockHash": "0x5053fe02da5bb0c2fc690a467c1cc36e791047fc48c3ea4fe8bbeed069f3f7ba",
164 "blockNumber": "0x8fcb296",
165 "address": "0xf1d7cc64fb4452f05c498126312ebe29f30fbcf9",
166 "data": "0x000000000000000000000000f64dfe17c8b87f012fcf50fbda1d62bfa148366a0000000000000000000000000000000000000000000000000000000000000001",
167 "topics": [
168 "0x0d3648bd0f6ba80134a33ba9275ac585d9d315f0ad8355cddefde31afa28d0e9",
169 "0x00000000000000000000000082af49447d8a07e3bd95bd0d56f35241523fbab1",
170 "0x000000000000000000000000af88d065e77c8cc2239327c5edb3a432268e5831"
171 ]
172 });
173 serde_json::from_value(log_json).expect("Failed to deserialize RPC log")
174 }
175
176 #[rstest]
179 fn test_parse_pair_created_hypersync(hypersync_log_weth_usdt: HypersyncLog) {
180 let event =
181 parse_pool_created_event_hypersync(hypersync_log_weth_usdt).expect("Failed to parse");
182
183 assert_eq!(event.block_number, 150778518);
184 assert_eq!(
185 event.token0.to_string().to_lowercase(),
186 "0x82af49447d8a07e3bd95bd0d56f35241523fbab1"
187 );
188 assert_eq!(
189 event.token1.to_string().to_lowercase(),
190 "0xaf88d065e77c8cc2239327c5edb3a432268e5831"
191 );
192 assert_eq!(
193 event.pool_identifier.to_string(),
194 "0xF64Dfe17C8b87F012FCf50FbDA1D62bfA148366a",
195 );
196 assert_eq!(event.fee, None);
197 assert_eq!(event.tick_spacing, None);
198 }
199
200 #[rstest]
203 fn test_parse_pair_created_rpc(rpc_log_weth_usdt: RpcLog) {
204 let event = parse_pool_created_event_rpc(&rpc_log_weth_usdt).expect("Failed to parse");
205
206 assert_eq!(event.block_number, 150778518);
207 assert_eq!(
208 event.token0.to_string().to_lowercase(),
209 "0x82af49447d8a07e3bd95bd0d56f35241523fbab1"
210 );
211 assert_eq!(
212 event.token1.to_string().to_lowercase(),
213 "0xaf88d065e77c8cc2239327c5edb3a432268e5831"
214 );
215 assert_eq!(
216 event.pool_identifier.to_string(),
217 "0xF64Dfe17C8b87F012FCf50FbDA1D62bfA148366a"
218 );
219 assert_eq!(event.fee, None);
220 assert_eq!(event.tick_spacing, None);
221 }
222
223 #[rstest]
224 fn test_hypersync_rpc_match(hypersync_log_weth_usdt: HypersyncLog, rpc_log_weth_usdt: RpcLog) {
225 let hypersync_event =
226 parse_pool_created_event_hypersync(hypersync_log_weth_usdt).expect("HyperSync parse");
227 let rpc_event = parse_pool_created_event_rpc(&rpc_log_weth_usdt).expect("RPC parse");
228
229 assert_eq!(hypersync_event.block_number, rpc_event.block_number);
230 assert_eq!(hypersync_event.token0, rpc_event.token0);
231 assert_eq!(hypersync_event.token1, rpc_event.token1);
232 assert_eq!(hypersync_event.pool_identifier, rpc_event.pool_identifier);
233 assert_eq!(hypersync_event.fee, rpc_event.fee);
234 assert_eq!(hypersync_event.tick_spacing, rpc_event.tick_spacing);
235 }
236}