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