1use std::sync::Arc;
19
20use alloy_primitives::Address;
21use nautilus_core::UnixNanos;
22use serde::{Deserialize, Serialize};
23
24use crate::{
25 data::HasTsInit,
26 defi::{chain::SharedChain, dex::Dex, token::Token},
27 identifiers::InstrumentId,
28};
29
30#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
32pub struct Pool {
33 pub chain: SharedChain,
35 pub dex: Dex,
37 pub address: Address,
39 pub creation_block: u64,
41 pub token0: Token,
43 pub token1: Token,
45 pub fee: u32,
53 pub tick_spacing: u32,
55 pub ts_init: UnixNanos,
57}
58
59pub type SharedPool = Arc<Pool>;
61
62impl Pool {
63 #[must_use]
65 #[allow(clippy::too_many_arguments)]
66 pub fn new(
67 chain: SharedChain,
68 dex: Dex,
69 address: Address,
70 creation_block: u64,
71 token0: Token,
72 token1: Token,
73 fee: u32,
74 tick_spacing: u32,
75 ts_init: UnixNanos,
76 ) -> Self {
77 Self {
78 chain,
79 dex,
80 address,
81 creation_block,
82 token0,
83 token1,
84 fee,
85 tick_spacing,
86 ts_init,
87 }
88 }
89
90 #[must_use]
92 pub fn ticker(&self) -> String {
93 format!("{}/{}", self.token0.symbol, self.token1.symbol)
94 }
95
96 #[must_use]
98 pub fn instrument_id(&self) -> InstrumentId {
99 let id_string = format!("{}.{}", self.ticker(), self.dex.name);
100 InstrumentId::from(id_string.as_str())
101 }
102}
103
104impl std::fmt::Display for Pool {
105 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
106 write!(
107 f,
108 "Pool(ticker={}, dex={}, fee={}, address={})",
109 self.ticker(),
110 self.dex.name,
111 self.fee,
112 self.address
113 )
114 }
115}
116
117impl HasTsInit for Pool {
118 fn ts_init(&self) -> UnixNanos {
119 self.ts_init
120 }
121}
122
123#[cfg(test)]
124mod tests {
125 use std::sync::Arc;
126
127 use rstest::rstest;
128
129 use super::*;
130 use crate::defi::{
131 chain::chains,
132 dex::{AmmType, Dex},
133 token::Token,
134 };
135
136 #[rstest]
137 fn test_pool_constructor_and_methods() {
138 let chain = Arc::new(chains::ETHEREUM.clone());
139 let dex = Dex::new(
140 chains::ETHEREUM.clone(),
141 "UniswapV3",
142 "0x1F98431c8aD98523631AE4a59f267346ea31F984",
143 AmmType::CLAMM,
144 "PoolCreated(address,address,uint24,int24,address)",
145 "Swap(address,address,int256,int256,uint160,uint128,int24)",
146 "Mint(address,address,int24,int24,uint128,uint256,uint256)",
147 "Burn(address,int24,int24,uint128,uint256,uint256)",
148 );
149
150 let token0 = Token::new(
151 chain.clone(),
152 "0xA0b86a33E6441b936662bb6B5d1F8Fb0E2b57A5D"
153 .parse()
154 .unwrap(),
155 "Wrapped Ether".to_string(),
156 "WETH".to_string(),
157 18,
158 );
159
160 let token1 = Token::new(
161 chain.clone(),
162 "0xdAC17F958D2ee523a2206206994597C13D831ec7"
163 .parse()
164 .unwrap(),
165 "Tether USD".to_string(),
166 "USDT".to_string(),
167 6,
168 );
169
170 let pool_address = "0x11b815efB8f581194ae79006d24E0d814B7697F6"
171 .parse()
172 .unwrap();
173 let ts_init = UnixNanos::from(1_234_567_890_000_000_000u64);
174
175 let pool = Pool::new(
176 chain.clone(),
177 dex,
178 pool_address,
179 12345678,
180 token0,
181 token1,
182 3000,
183 60,
184 ts_init,
185 );
186
187 assert_eq!(pool.chain.chain_id, chain.chain_id);
188 assert_eq!(pool.dex.name, "UniswapV3");
189 assert_eq!(pool.address, pool_address);
190 assert_eq!(pool.creation_block, 12345678);
191 assert_eq!(pool.token0.symbol, "WETH");
192 assert_eq!(pool.token1.symbol, "USDT");
193 assert_eq!(pool.fee, 3000);
194 assert_eq!(pool.tick_spacing, 60);
195 assert_eq!(pool.ts_init, ts_init);
196 assert_eq!(pool.ticker(), "WETH/USDT");
197
198 let instrument_id = pool.instrument_id();
199 assert!(instrument_id.to_string().contains("WETH/USDT"));
200 assert!(instrument_id.to_string().contains("UniswapV3"));
201 }
202}