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 creation_block: u64,
309 token0: Token,
310 token1: Token,
311 fee: Option<u32>,
312 tick_spacing: Option<u32>,
313 ts_init: u64,
314 ) -> PyResult<Self> {
315 let address = address.parse().map_err(to_pyvalue_err)?;
316 Ok(Self::new(
317 Arc::new(chain),
318 Arc::new(dex),
319 address,
320 creation_block,
321 token0,
322 token1,
323 fee,
324 tick_spacing,
325 ts_init.into(),
326 ))
327 }
328
329 fn __str__(&self) -> String {
330 self.to_string()
331 }
332
333 fn __repr__(&self) -> String {
334 format!("{self:?}")
335 }
336
337 fn __hash__(&self) -> u64 {
338 let mut hasher = DefaultHasher::new();
339 self.chain.chain_id.hash(&mut hasher);
340 self.address.hash(&mut hasher);
341 hasher.finish()
342 }
343
344 fn __richcmp__(&self, other: &Self, op: CompareOp) -> bool {
345 match op {
346 CompareOp::Eq => self == other,
347 CompareOp::Ne => self != other,
348 _ => panic!("Unsupported comparison for Pool"),
349 }
350 }
351
352 #[getter]
353 #[pyo3(name = "chain")]
354 fn py_chain(&self) -> PyResult<Chain> {
355 Ok(self.chain.as_ref().clone())
356 }
357
358 #[getter]
359 #[pyo3(name = "dex")]
360 fn py_dex(&self) -> PyResult<Dex> {
361 Ok(self.dex.as_ref().clone())
362 }
363
364 #[getter]
365 #[pyo3(name = "instrument_id")]
366 fn py_instrument_id(&self) -> InstrumentId {
367 self.instrument_id
368 }
369
370 #[getter]
371 #[pyo3(name = "address")]
372 fn py_address(&self) -> String {
373 self.address.to_string()
374 }
375
376 #[getter]
377 #[pyo3(name = "creation_block")]
378 fn py_creation_block(&self) -> u64 {
379 self.creation_block
380 }
381
382 #[getter]
383 #[pyo3(name = "token0")]
384 fn py_token0(&self) -> Token {
385 self.token0.clone()
386 }
387
388 #[getter]
389 #[pyo3(name = "token1")]
390 fn py_token1(&self) -> Token {
391 self.token1.clone()
392 }
393
394 #[getter]
395 #[pyo3(name = "fee")]
396 fn py_fee(&self) -> Option<u32> {
397 self.fee
398 }
399
400 #[getter]
401 #[pyo3(name = "tick_spacing")]
402 fn py_tick_spacing(&self) -> Option<u32> {
403 self.tick_spacing
404 }
405
406 #[getter]
407 #[pyo3(name = "ts_init")]
408 fn py_ts_init(&self) -> u64 {
409 self.ts_init.as_u64()
410 }
411}