nautilus_dydx/python/
grpc.rs1use std::sync::Arc;
19
20use nautilus_core::{
21 hex,
22 python::{IntoPyObjectNautilusExt, to_pyruntime_err},
23};
24use pyo3::prelude::*;
25
26use crate::grpc::DydxGrpcClient;
27
28#[pyclass(name = "DydxGrpcClient", from_py_object)]
29#[pyo3_stub_gen::derive::gen_stub_pyclass(module = "nautilus_trader.dydx")]
30#[derive(Debug, Clone)]
31pub struct PyDydxGrpcClient {
32 pub(crate) inner: Arc<DydxGrpcClient>,
33}
34
35#[pymethods]
36#[pyo3_stub_gen::derive::gen_stub_pymethods]
37impl PyDydxGrpcClient {
38 #[staticmethod]
44 #[pyo3(name = "connect")]
45 pub fn py_connect(py: Python<'_>, grpc_url: String) -> PyResult<Bound<'_, PyAny>> {
46 pyo3_async_runtimes::tokio::future_into_py(py, async move {
47 let client = DydxGrpcClient::new(grpc_url)
48 .await
49 .map_err(to_pyruntime_err)?;
50
51 Ok(Self {
52 inner: Arc::new(client),
53 })
54 })
55 }
56
57 #[staticmethod]
63 #[pyo3(name = "connect_with_fallback")]
64 pub fn py_connect_with_fallback(
65 py: Python<'_>,
66 grpc_urls: Vec<String>,
67 ) -> PyResult<Bound<'_, PyAny>> {
68 pyo3_async_runtimes::tokio::future_into_py(py, async move {
69 let urls: Vec<&str> = grpc_urls.iter().map(String::as_str).collect();
70 let client = DydxGrpcClient::new_with_fallback(&urls)
71 .await
72 .map_err(to_pyruntime_err)?;
73
74 Ok(Self {
75 inner: Arc::new(client),
76 })
77 })
78 }
79
80 #[pyo3(name = "latest_block_height")]
86 pub fn py_latest_block_height<'py>(&self, py: Python<'py>) -> PyResult<Bound<'py, PyAny>> {
87 let client = self.inner.clone();
88 pyo3_async_runtimes::tokio::future_into_py(py, async move {
89 let mut client = (*client).clone();
90 let height = client
91 .latest_block_height()
92 .await
93 .map_err(to_pyruntime_err)?;
94 Ok(height.0 as u64)
95 })
96 }
97
98 #[pyo3(name = "get_account")]
104 pub fn py_get_account<'py>(
105 &self,
106 py: Python<'py>,
107 address: String,
108 ) -> PyResult<Bound<'py, PyAny>> {
109 let client = self.inner.clone();
110 pyo3_async_runtimes::tokio::future_into_py(py, async move {
111 let mut client = (*client).clone();
112 let account = client
113 .get_account(&address)
114 .await
115 .map_err(to_pyruntime_err)?;
116 Ok((account.account_number, account.sequence))
117 })
118 }
119
120 #[pyo3(name = "get_account_balances")]
126 pub fn py_get_account_balances<'py>(
127 &self,
128 py: Python<'py>,
129 address: String,
130 ) -> PyResult<Bound<'py, PyAny>> {
131 let client = self.inner.clone();
132 pyo3_async_runtimes::tokio::future_into_py(py, async move {
133 let mut client = (*client).clone();
134 let balances = client
135 .get_account_balances(&address)
136 .await
137 .map_err(to_pyruntime_err)?;
138 let result: Vec<(String, String)> =
139 balances.into_iter().map(|c| (c.denom, c.amount)).collect();
140 Ok(result)
141 })
142 }
143
144 #[pyo3(name = "get_subaccount")]
150 pub fn py_get_subaccount<'py>(
151 &self,
152 py: Python<'py>,
153 address: String,
154 subaccount_number: u32,
155 ) -> PyResult<Bound<'py, PyAny>> {
156 let client = self.inner.clone();
157 pyo3_async_runtimes::tokio::future_into_py(py, async move {
158 let mut client = (*client).clone();
159 let subaccount = client
160 .get_subaccount(&address, subaccount_number)
161 .await
162 .map_err(to_pyruntime_err)?;
163
164 let result: Vec<(String, String)> = subaccount
167 .asset_positions
168 .into_iter()
169 .map(|p| {
170 let quantums_str = if p.quantums.is_empty() {
171 "0".to_string()
172 } else {
173 hex::encode(&p.quantums)
175 };
176 (p.asset_id.to_string(), quantums_str)
177 })
178 .collect();
179 Ok(result)
180 })
181 }
182
183 #[pyo3(name = "get_node_info")]
189 pub fn py_get_node_info<'py>(&self, py: Python<'py>) -> PyResult<Bound<'py, PyAny>> {
190 let client = self.inner.clone();
191 pyo3_async_runtimes::tokio::future_into_py(py, async move {
192 let mut client = (*client).clone();
193 let info = client.get_node_info().await.map_err(to_pyruntime_err)?;
194
195 Python::attach(|py| {
197 use pyo3::types::PyDict;
198 let dict = PyDict::new(py);
199
200 if let Some(default_node_info) = info.default_node_info {
201 dict.set_item("network", default_node_info.network)?;
202 dict.set_item("moniker", default_node_info.moniker)?;
203 dict.set_item("version", default_node_info.version)?;
204 }
205
206 if let Some(app_info) = info.application_version {
207 dict.set_item("app_name", app_info.name)?;
208 dict.set_item("app_version", app_info.version)?;
209 }
210 Ok(dict.into_py_any_unwrap(py))
211 })
212 })
213 }
214
215 #[pyo3(name = "simulate_tx")]
221 pub fn py_simulate_tx<'py>(
222 &self,
223 py: Python<'py>,
224 tx_bytes: Vec<u8>,
225 ) -> PyResult<Bound<'py, PyAny>> {
226 let client = self.inner.clone();
227 pyo3_async_runtimes::tokio::future_into_py(py, async move {
228 let mut client = (*client).clone();
229 let gas_used = client
230 .simulate_tx(tx_bytes)
231 .await
232 .map_err(to_pyruntime_err)?;
233 Ok(gas_used)
234 })
235 }
236
237 #[pyo3(name = "get_tx")]
243 pub fn py_get_tx<'py>(&self, py: Python<'py>, hash: String) -> PyResult<Bound<'py, PyAny>> {
244 let client = self.inner.clone();
245 pyo3_async_runtimes::tokio::future_into_py(py, async move {
246 let mut client = (*client).clone();
247 let tx = client.get_tx(&hash).await.map_err(to_pyruntime_err)?;
248
249 let result = format!("Tx(body_bytes_len={})", tx.body.messages.len());
251 Ok(result)
252 })
253 }
254
255 fn __repr__(&self) -> String {
256 "DydxGrpcClient()".to_string()
257 }
258}