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 to_pyvalue_err(format!("`chain_name` '{chain_name}' is not recognized",))
102 })
103 }
104
105 #[staticmethod]
106 #[pyo3(name = "from_chain_id")]
107 fn py_from_chain_id(chain_id: u32) -> Option<Self> {
108 Self::from_chain_id(chain_id).cloned()
109 }
110
111 #[staticmethod]
112 #[pyo3(name = "ARBITRUM")]
113 fn py_arbitrum_chain() -> Self {
114 chains::ARBITRUM.clone()
115 }
116}
117
118#[pymethods]
119impl Token {
120 #[new]
121 fn py_new(
122 chain: Chain,
123 address: String,
124 name: String,
125 symbol: String,
126 decimals: u8,
127 ) -> PyResult<Self> {
128 let address = address.parse().map_err(to_pyvalue_err)?;
129 Ok(Self::new(Arc::new(chain), address, name, symbol, decimals))
130 }
131
132 fn __str__(&self) -> String {
133 self.to_string()
134 }
135
136 fn __repr__(&self) -> String {
137 format!("{self:?}")
138 }
139
140 fn __hash__(&self) -> u64 {
141 let mut hasher = DefaultHasher::new();
142 self.chain.chain_id.hash(&mut hasher);
143 self.address.hash(&mut hasher);
144 hasher.finish()
145 }
146
147 fn __richcmp__(&self, other: &Self, op: CompareOp) -> bool {
148 match op {
149 CompareOp::Eq => self == other,
150 CompareOp::Ne => self != other,
151 _ => panic!("Unsupported comparison for Token"),
152 }
153 }
154
155 #[getter]
156 #[pyo3(name = "chain")]
157 fn py_chain(&self) -> PyResult<Chain> {
158 Ok(self.chain.as_ref().clone())
159 }
160
161 #[getter]
162 #[pyo3(name = "address")]
163 fn py_address(&self) -> String {
164 self.address.to_string()
165 }
166
167 #[getter]
168 #[pyo3(name = "name")]
169 fn py_name(&self) -> &str {
170 &self.name
171 }
172
173 #[getter]
174 #[pyo3(name = "symbol")]
175 fn py_symbol(&self) -> &str {
176 &self.symbol
177 }
178
179 #[getter]
180 #[pyo3(name = "decimals")]
181 fn py_decimals(&self) -> u8 {
182 self.decimals
183 }
184}
185
186#[pymethods]
187impl Dex {
188 #[new]
189 #[allow(clippy::too_many_arguments)]
190 fn py_new(
191 chain: Chain,
192 name: String,
193 factory: String,
194 factory_creation_block: u64,
195 amm_type: String,
196 pool_created_event: &str,
197 swap_event: &str,
198 mint_event: &str,
199 burn_event: &str,
200 collect_event: &str,
201 ) -> PyResult<Self> {
202 let amm_type = AmmType::from_str(&amm_type).map_err(to_pyvalue_err)?;
203 let dex_type = DexType::from_dex_name(&name)
204 .ok_or_else(|| to_pyvalue_err(format!("Invalid DEX name: {name}")))?;
205 Ok(Self::new(
206 chain,
207 dex_type,
208 &factory,
209 factory_creation_block,
210 amm_type,
211 pool_created_event,
212 swap_event,
213 mint_event,
214 burn_event,
215 collect_event,
216 ))
217 }
218
219 fn __str__(&self) -> String {
220 self.to_string()
221 }
222
223 fn __repr__(&self) -> String {
224 format!("{self:?}")
225 }
226
227 fn __hash__(&self) -> u64 {
228 let mut hasher = DefaultHasher::new();
229 self.chain.chain_id.hash(&mut hasher);
230 self.name.hash(&mut hasher);
231 self.factory.hash(&mut hasher);
232 hasher.finish()
233 }
234
235 fn __richcmp__(&self, other: &Self, op: CompareOp) -> bool {
236 match op {
237 CompareOp::Eq => self == other,
238 CompareOp::Ne => self != other,
239 _ => panic!("Unsupported comparison for Dex"),
240 }
241 }
242
243 #[getter]
244 #[pyo3(name = "chain")]
245 fn py_chain(&self) -> Chain {
246 self.chain.clone()
247 }
248
249 #[getter]
250 #[pyo3(name = "name")]
251 fn py_name(&self) -> DexType {
252 self.name
253 }
254
255 #[getter]
256 #[pyo3(name = "factory")]
257 fn py_factory(&self) -> String {
258 self.factory.to_string()
259 }
260
261 #[getter]
262 #[pyo3(name = "factory_creation_block")]
263 fn py_factory_creation_block(&self) -> u64 {
264 self.factory_creation_block
265 }
266
267 #[getter]
268 #[pyo3(name = "pool_created_event")]
269 fn py_pool_created_event(&self) -> &str {
270 &self.pool_created_event
271 }
272
273 #[getter]
274 #[pyo3(name = "swap_created_event")]
275 fn py_swap_created_event(&self) -> &str {
276 &self.swap_created_event
277 }
278
279 #[getter]
280 #[pyo3(name = "mint_created_event")]
281 fn py_mint_created_event(&self) -> &str {
282 &self.mint_created_event
283 }
284
285 #[getter]
286 #[pyo3(name = "burn_created_event")]
287 fn py_burn_created_event(&self) -> &str {
288 &self.burn_created_event
289 }
290
291 #[getter]
292 #[pyo3(name = "amm_type")]
293 fn py_amm_type(&self) -> AmmType {
294 self.amm_type
295 }
296}
297
298#[pymethods]
299impl Pool {
300 #[new]
301 #[allow(clippy::too_many_arguments)]
302 fn py_new(
303 chain: Chain,
304 dex: Dex,
305 address: String,
306 pool_identifier: String,
307 creation_block: u64,
308 token0: Token,
309 token1: Token,
310 fee: Option<u32>,
311 tick_spacing: Option<u32>,
312 ts_init: u64,
313 ) -> PyResult<Self> {
314 let address = address.parse().map_err(to_pyvalue_err)?;
315 let pool_identifier = pool_identifier.parse().map_err(to_pyvalue_err)?;
316 Ok(Self::new(
317 Arc::new(chain),
318 Arc::new(dex),
319 address,
320 pool_identifier,
321 creation_block,
322 token0,
323 token1,
324 fee,
325 tick_spacing,
326 ts_init.into(),
327 ))
328 }
329
330 fn __str__(&self) -> String {
331 self.to_string()
332 }
333
334 fn __repr__(&self) -> String {
335 format!("{self:?}")
336 }
337
338 fn __hash__(&self) -> u64 {
339 let mut hasher = DefaultHasher::new();
340 self.chain.chain_id.hash(&mut hasher);
341 self.address.hash(&mut hasher);
342 hasher.finish()
343 }
344
345 fn __richcmp__(&self, other: &Self, op: CompareOp) -> bool {
346 match op {
347 CompareOp::Eq => self == other,
348 CompareOp::Ne => self != other,
349 _ => panic!("Unsupported comparison for Pool"),
350 }
351 }
352
353 #[getter]
354 #[pyo3(name = "chain")]
355 fn py_chain(&self) -> PyResult<Chain> {
356 Ok(self.chain.as_ref().clone())
357 }
358
359 #[getter]
360 #[pyo3(name = "dex")]
361 fn py_dex(&self) -> PyResult<Dex> {
362 Ok(self.dex.as_ref().clone())
363 }
364
365 #[getter]
366 #[pyo3(name = "instrument_id")]
367 fn py_instrument_id(&self) -> InstrumentId {
368 self.instrument_id
369 }
370
371 #[getter]
372 #[pyo3(name = "address")]
373 fn py_address(&self) -> String {
374 self.address.to_string()
375 }
376
377 #[getter]
378 #[pyo3(name = "creation_block")]
379 fn py_creation_block(&self) -> u64 {
380 self.creation_block
381 }
382
383 #[getter]
384 #[pyo3(name = "token0")]
385 fn py_token0(&self) -> Token {
386 self.token0.clone()
387 }
388
389 #[getter]
390 #[pyo3(name = "token1")]
391 fn py_token1(&self) -> Token {
392 self.token1.clone()
393 }
394
395 #[getter]
396 #[pyo3(name = "fee")]
397 fn py_fee(&self) -> Option<u32> {
398 self.fee
399 }
400
401 #[getter]
402 #[pyo3(name = "tick_spacing")]
403 fn py_tick_spacing(&self) -> Option<u32> {
404 self.tick_spacing
405 }
406
407 #[getter]
408 #[pyo3(name = "ts_init")]
409 fn py_ts_init(&self) -> u64 {
410 self.ts_init.as_u64()
411 }
412}