nautilus_model/python/defi/
types.rs1use std::{
19 collections::hash_map::DefaultHasher,
20 hash::{Hash, Hasher},
21 str::FromStr,
22 sync::Arc,
23};
24
25use nautilus_core::python::to_pyvalue_err;
26use pyo3::{basic::CompareOp, prelude::*};
27
28use crate::{
29 defi::{AmmType, Blockchain, Chain, Dex, DexType, Pool, Token, chain::chains},
30 identifiers::InstrumentId,
31};
32
33#[pymethods]
34impl Chain {
35 #[new]
36 fn py_new(name: Blockchain, chain_id: u32) -> Self {
37 Self::new(name, chain_id)
38 }
39
40 fn __str__(&self) -> String {
41 self.to_string()
42 }
43
44 fn __repr__(&self) -> String {
45 format!("{self:?}")
46 }
47
48 fn __hash__(&self) -> u64 {
49 let mut hasher = DefaultHasher::new();
50 self.chain_id.hash(&mut hasher);
51 hasher.finish()
52 }
53
54 fn __richcmp__(&self, other: &Self, op: CompareOp) -> bool {
55 match op {
56 CompareOp::Eq => self == other,
57 CompareOp::Ne => self != other,
58 _ => panic!("Unsupported comparison for Chain"),
59 }
60 }
61
62 #[getter]
63 #[pyo3(name = "name")]
64 fn py_name(&self) -> Blockchain {
65 self.name
66 }
67
68 #[getter]
69 #[pyo3(name = "chain_id")]
70 fn py_chain_id(&self) -> u32 {
71 self.chain_id
72 }
73
74 #[getter]
75 #[pyo3(name = "hypersync_url")]
76 fn py_hypersync_url(&self) -> &str {
77 &self.hypersync_url
78 }
79
80 #[getter]
81 #[pyo3(name = "rpc_url")]
82 fn py_rpc_url(&self) -> Option<&str> {
83 self.rpc_url.as_deref()
84 }
85
86 #[getter]
87 #[pyo3(name = "native_currency_decimals")]
88 fn py_native_currency_decimals(&self) -> u8 {
89 self.native_currency_decimals
90 }
91
92 #[pyo3(name = "set_rpc_url")]
93 fn py_set_rpc_url(&mut self, rpc_url: String) {
94 self.set_rpc_url(rpc_url);
95 }
96
97 #[staticmethod]
98 #[pyo3(name = "from_chain_name")]
99 fn py_from_chain_name(chain_name: &str) -> PyResult<Self> {
100 Self::from_chain_name(chain_name).cloned().ok_or_else(|| {
101 pyo3::exceptions::PyValueError::new_err(format!(
102 "`chain_name` '{chain_name}' is not recognized",
103 ))
104 })
105 }
106
107 #[staticmethod]
108 #[pyo3(name = "from_chain_id")]
109 fn py_from_chain_id(chain_id: u32) -> Option<Self> {
110 Self::from_chain_id(chain_id).cloned()
111 }
112
113 #[staticmethod]
114 #[pyo3(name = "ARBITRUM")]
115 fn py_arbitrum_chain() -> Self {
116 chains::ARBITRUM.clone()
117 }
118}
119
120#[pymethods]
121impl Token {
122 #[new]
123 fn py_new(
124 chain: Chain,
125 address: String,
126 name: String,
127 symbol: String,
128 decimals: u8,
129 ) -> PyResult<Self> {
130 let address = address.parse().map_err(to_pyvalue_err)?;
131 Ok(Self::new(Arc::new(chain), address, name, symbol, decimals))
132 }
133
134 fn __str__(&self) -> String {
135 self.to_string()
136 }
137
138 fn __repr__(&self) -> String {
139 format!("{self:?}")
140 }
141
142 fn __hash__(&self) -> u64 {
143 let mut hasher = DefaultHasher::new();
144 self.chain.chain_id.hash(&mut hasher);
145 self.address.hash(&mut hasher);
146 hasher.finish()
147 }
148
149 fn __richcmp__(&self, other: &Self, op: CompareOp) -> bool {
150 match op {
151 CompareOp::Eq => self == other,
152 CompareOp::Ne => self != other,
153 _ => panic!("Unsupported comparison for Token"),
154 }
155 }
156
157 #[getter]
158 #[pyo3(name = "chain")]
159 fn py_chain(&self) -> PyResult<Chain> {
160 Ok(self.chain.as_ref().clone())
161 }
162
163 #[getter]
164 #[pyo3(name = "address")]
165 fn py_address(&self) -> String {
166 self.address.to_string()
167 }
168
169 #[getter]
170 #[pyo3(name = "name")]
171 fn py_name(&self) -> &str {
172 &self.name
173 }
174
175 #[getter]
176 #[pyo3(name = "symbol")]
177 fn py_symbol(&self) -> &str {
178 &self.symbol
179 }
180
181 #[getter]
182 #[pyo3(name = "decimals")]
183 fn py_decimals(&self) -> u8 {
184 self.decimals
185 }
186}
187
188#[pymethods]
189impl Dex {
190 #[new]
191 #[allow(clippy::too_many_arguments)]
192 fn py_new(
193 chain: Chain,
194 name: String,
195 factory: String,
196 factory_creation_block: u64,
197 amm_type: String,
198 pool_created_event: &str,
199 swap_event: &str,
200 mint_event: &str,
201 burn_event: &str,
202 collect_event: &str,
203 ) -> PyResult<Self> {
204 let amm_type = AmmType::from_str(&amm_type).map_err(to_pyvalue_err)?;
205 let dex_type = DexType::from_dex_name(&name)
206 .ok_or_else(|| to_pyvalue_err(format!("Invalid DEX name: {name}")))?;
207 Ok(Self::new(
208 chain,
209 dex_type,
210 &factory,
211 factory_creation_block,
212 amm_type,
213 pool_created_event,
214 swap_event,
215 mint_event,
216 burn_event,
217 collect_event,
218 ))
219 }
220
221 fn __str__(&self) -> String {
222 self.to_string()
223 }
224
225 fn __repr__(&self) -> String {
226 format!("{self:?}")
227 }
228
229 fn __hash__(&self) -> u64 {
230 let mut hasher = DefaultHasher::new();
231 self.chain.chain_id.hash(&mut hasher);
232 self.name.hash(&mut hasher);
233 self.factory.hash(&mut hasher);
234 hasher.finish()
235 }
236
237 fn __richcmp__(&self, other: &Self, op: CompareOp) -> bool {
238 match op {
239 CompareOp::Eq => self == other,
240 CompareOp::Ne => self != other,
241 _ => panic!("Unsupported comparison for Dex"),
242 }
243 }
244
245 #[getter]
246 #[pyo3(name = "chain")]
247 fn py_chain(&self) -> Chain {
248 self.chain.clone()
249 }
250
251 #[getter]
252 #[pyo3(name = "name")]
253 fn py_name(&self) -> DexType {
254 self.name
255 }
256
257 #[getter]
258 #[pyo3(name = "factory")]
259 fn py_factory(&self) -> String {
260 self.factory.to_string()
261 }
262
263 #[getter]
264 #[pyo3(name = "factory_creation_block")]
265 fn py_factory_creation_block(&self) -> u64 {
266 self.factory_creation_block
267 }
268
269 #[getter]
270 #[pyo3(name = "pool_created_event")]
271 fn py_pool_created_event(&self) -> &str {
272 &self.pool_created_event
273 }
274
275 #[getter]
276 #[pyo3(name = "swap_created_event")]
277 fn py_swap_created_event(&self) -> &str {
278 &self.swap_created_event
279 }
280
281 #[getter]
282 #[pyo3(name = "mint_created_event")]
283 fn py_mint_created_event(&self) -> &str {
284 &self.mint_created_event
285 }
286
287 #[getter]
288 #[pyo3(name = "burn_created_event")]
289 fn py_burn_created_event(&self) -> &str {
290 &self.burn_created_event
291 }
292
293 #[getter]
294 #[pyo3(name = "amm_type")]
295 fn py_amm_type(&self) -> AmmType {
296 self.amm_type
297 }
298}
299
300#[pymethods]
301impl Pool {
302 #[new]
303 #[allow(clippy::too_many_arguments)]
304 fn py_new(
305 chain: Chain,
306 dex: Dex,
307 address: String,
308 pool_identifier: String,
309 creation_block: u64,
310 token0: Token,
311 token1: Token,
312 fee: Option<u32>,
313 tick_spacing: Option<u32>,
314 ts_init: u64,
315 ) -> PyResult<Self> {
316 let address = address.parse().map_err(to_pyvalue_err)?;
317 let pool_identifier = pool_identifier.parse().map_err(to_pyvalue_err)?;
318 Ok(Self::new(
319 Arc::new(chain),
320 Arc::new(dex),
321 address,
322 pool_identifier,
323 creation_block,
324 token0,
325 token1,
326 fee,
327 tick_spacing,
328 ts_init.into(),
329 ))
330 }
331
332 fn __str__(&self) -> String {
333 self.to_string()
334 }
335
336 fn __repr__(&self) -> String {
337 format!("{self:?}")
338 }
339
340 fn __hash__(&self) -> u64 {
341 let mut hasher = DefaultHasher::new();
342 self.chain.chain_id.hash(&mut hasher);
343 self.address.hash(&mut hasher);
344 hasher.finish()
345 }
346
347 fn __richcmp__(&self, other: &Self, op: CompareOp) -> bool {
348 match op {
349 CompareOp::Eq => self == other,
350 CompareOp::Ne => self != other,
351 _ => panic!("Unsupported comparison for Pool"),
352 }
353 }
354
355 #[getter]
356 #[pyo3(name = "chain")]
357 fn py_chain(&self) -> PyResult<Chain> {
358 Ok(self.chain.as_ref().clone())
359 }
360
361 #[getter]
362 #[pyo3(name = "dex")]
363 fn py_dex(&self) -> PyResult<Dex> {
364 Ok(self.dex.as_ref().clone())
365 }
366
367 #[getter]
368 #[pyo3(name = "instrument_id")]
369 fn py_instrument_id(&self) -> InstrumentId {
370 self.instrument_id
371 }
372
373 #[getter]
374 #[pyo3(name = "address")]
375 fn py_address(&self) -> String {
376 self.address.to_string()
377 }
378
379 #[getter]
380 #[pyo3(name = "creation_block")]
381 fn py_creation_block(&self) -> u64 {
382 self.creation_block
383 }
384
385 #[getter]
386 #[pyo3(name = "token0")]
387 fn py_token0(&self) -> Token {
388 self.token0.clone()
389 }
390
391 #[getter]
392 #[pyo3(name = "token1")]
393 fn py_token1(&self) -> Token {
394 self.token1.clone()
395 }
396
397 #[getter]
398 #[pyo3(name = "fee")]
399 fn py_fee(&self) -> Option<u32> {
400 self.fee
401 }
402
403 #[getter]
404 #[pyo3(name = "tick_spacing")]
405 fn py_tick_spacing(&self) -> Option<u32> {
406 self.tick_spacing
407 }
408
409 #[getter]
410 #[pyo3(name = "ts_init")]
411 fn py_ts_init(&self) -> u64 {
412 self.ts_init.as_u64()
413 }
414}