nautilus_dydx/python/
grpc.rs1#![allow(clippy::missing_errors_doc)]
19
20use std::sync::Arc;
21
22use nautilus_core::python::{IntoPyObjectNautilusExt, to_pyruntime_err};
23use pyo3::prelude::*;
24
25use crate::grpc::DydxGrpcClient;
26
27#[pyclass(name = "DydxGrpcClient")]
28#[derive(Debug, Clone)]
29pub struct PyDydxGrpcClient {
30 pub(crate) inner: Arc<DydxGrpcClient>,
31}
32
33#[pymethods]
34impl PyDydxGrpcClient {
35 #[staticmethod]
41 #[pyo3(name = "connect")]
42 pub fn py_connect(py: Python<'_>, grpc_url: String) -> PyResult<Bound<'_, PyAny>> {
43 pyo3_async_runtimes::tokio::future_into_py(py, async move {
44 let client = DydxGrpcClient::new(grpc_url)
45 .await
46 .map_err(to_pyruntime_err)?;
47
48 Ok(Self {
49 inner: Arc::new(client),
50 })
51 })
52 }
53
54 #[staticmethod]
60 #[pyo3(name = "connect_with_fallback")]
61 pub fn py_connect_with_fallback(
62 py: Python<'_>,
63 grpc_urls: Vec<String>,
64 ) -> PyResult<Bound<'_, PyAny>> {
65 pyo3_async_runtimes::tokio::future_into_py(py, async move {
66 let urls: Vec<&str> = grpc_urls.iter().map(String::as_str).collect();
67 let client = DydxGrpcClient::new_with_fallback(&urls)
68 .await
69 .map_err(to_pyruntime_err)?;
70
71 Ok(Self {
72 inner: Arc::new(client),
73 })
74 })
75 }
76
77 #[pyo3(name = "latest_block_height")]
83 pub fn py_latest_block_height<'py>(&self, py: Python<'py>) -> PyResult<Bound<'py, PyAny>> {
84 let client = self.inner.clone();
85 pyo3_async_runtimes::tokio::future_into_py(py, async move {
86 let mut client = (*client).clone();
87 let height = client
88 .latest_block_height()
89 .await
90 .map_err(to_pyruntime_err)?;
91 Ok(height.0 as u64)
92 })
93 }
94
95 #[pyo3(name = "get_account")]
101 pub fn py_get_account<'py>(
102 &self,
103 py: Python<'py>,
104 address: String,
105 ) -> PyResult<Bound<'py, PyAny>> {
106 let client = self.inner.clone();
107 pyo3_async_runtimes::tokio::future_into_py(py, async move {
108 let mut client = (*client).clone();
109 let account = client
110 .get_account(&address)
111 .await
112 .map_err(to_pyruntime_err)?;
113 Ok((account.account_number, account.sequence))
114 })
115 }
116
117 #[pyo3(name = "get_account_balances")]
123 pub fn py_get_account_balances<'py>(
124 &self,
125 py: Python<'py>,
126 address: String,
127 ) -> PyResult<Bound<'py, PyAny>> {
128 let client = self.inner.clone();
129 pyo3_async_runtimes::tokio::future_into_py(py, async move {
130 let mut client = (*client).clone();
131 let balances = client
132 .get_account_balances(&address)
133 .await
134 .map_err(to_pyruntime_err)?;
135 let result: Vec<(String, String)> =
136 balances.into_iter().map(|c| (c.denom, c.amount)).collect();
137 Ok(result)
138 })
139 }
140
141 #[pyo3(name = "get_subaccount")]
147 pub fn py_get_subaccount<'py>(
148 &self,
149 py: Python<'py>,
150 address: String,
151 subaccount_number: u32,
152 ) -> PyResult<Bound<'py, PyAny>> {
153 let client = self.inner.clone();
154 pyo3_async_runtimes::tokio::future_into_py(py, async move {
155 let mut client = (*client).clone();
156 let subaccount = client
157 .get_subaccount(&address, subaccount_number)
158 .await
159 .map_err(to_pyruntime_err)?;
160
161 let result: Vec<(String, String)> = subaccount
164 .asset_positions
165 .into_iter()
166 .map(|p| {
167 let quantums_str = if p.quantums.is_empty() {
168 "0".to_string()
169 } else {
170 hex::encode(&p.quantums)
172 };
173 (p.asset_id.to_string(), quantums_str)
174 })
175 .collect();
176 Ok(result)
177 })
178 }
179
180 #[pyo3(name = "get_node_info")]
186 pub fn py_get_node_info<'py>(&self, py: Python<'py>) -> PyResult<Bound<'py, PyAny>> {
187 let client = self.inner.clone();
188 pyo3_async_runtimes::tokio::future_into_py(py, async move {
189 let mut client = (*client).clone();
190 let info = client.get_node_info().await.map_err(to_pyruntime_err)?;
191
192 Python::attach(|py| {
194 use pyo3::types::PyDict;
195 let dict = PyDict::new(py);
196 if let Some(default_node_info) = info.default_node_info {
197 dict.set_item("network", default_node_info.network)?;
198 dict.set_item("moniker", default_node_info.moniker)?;
199 dict.set_item("version", default_node_info.version)?;
200 }
201 if let Some(app_info) = info.application_version {
202 dict.set_item("app_name", app_info.name)?;
203 dict.set_item("app_version", app_info.version)?;
204 }
205 Ok(dict.into_py_any_unwrap(py))
206 })
207 })
208 }
209
210 #[pyo3(name = "simulate_tx")]
216 pub fn py_simulate_tx<'py>(
217 &self,
218 py: Python<'py>,
219 tx_bytes: Vec<u8>,
220 ) -> PyResult<Bound<'py, PyAny>> {
221 let client = self.inner.clone();
222 pyo3_async_runtimes::tokio::future_into_py(py, async move {
223 let mut client = (*client).clone();
224 let gas_used = client
225 .simulate_tx(tx_bytes)
226 .await
227 .map_err(to_pyruntime_err)?;
228 Ok(gas_used)
229 })
230 }
231
232 #[pyo3(name = "get_tx")]
238 pub fn py_get_tx<'py>(&self, py: Python<'py>, hash: String) -> PyResult<Bound<'py, PyAny>> {
239 let client = self.inner.clone();
240 pyo3_async_runtimes::tokio::future_into_py(py, async move {
241 let mut client = (*client).clone();
242 let tx = client.get_tx(&hash).await.map_err(to_pyruntime_err)?;
243
244 let result = format!("Tx(body_bytes_len={})", tx.body.messages.len());
246 Ok(result)
247 })
248 }
249
250 fn __repr__(&self) -> String {
251 "DydxGrpcClient()".to_string()
252 }
253}