1use std::sync::Arc;
19
20use nautilus_common::python::config_error_to_pyvalue_err;
21use nautilus_core::{
22 python::to_pyvalue_err,
23 string::secret::{REDACTED, SecretString},
24};
25use nautilus_infrastructure::sql::pg::PostgresConnectOptions;
26use nautilus_model::{
27 defi::{Chain, DexType},
28 identifiers::AccountId,
29};
30use nautilus_network::websocket::TransportBackend;
31use pyo3::prelude::*;
32
33use crate::config::{
34 BlockchainChainAnchorConfig, BlockchainDataClientConfig, BlockchainExecutionClientConfig,
35 BlockchainProviderIdentity, BlockchainVerificationConfig, BlockchainVerificationProviderConfig,
36 DexPoolFilters, QuoteSpendLimit,
37};
38
39#[pymethods]
40#[pyo3_stub_gen::derive::gen_stub_pymethods(module = "nautilus_trader.adapters.blockchain")]
41impl DexPoolFilters {
42 #[new]
44 #[must_use]
45 pub fn py_new(remove_pools_with_empty_erc20_fields: Option<bool>) -> Self {
46 Self::builder()
47 .maybe_remove_pools_with_empty_erc20fields(remove_pools_with_empty_erc20_fields)
48 .build()
49 }
50}
51
52#[pymethods]
53#[pyo3_stub_gen::derive::gen_stub_pymethods(module = "nautilus_trader.adapters.blockchain")]
54impl QuoteSpendLimit {
55 #[new]
57 #[must_use]
58 fn py_new(
59 token_in: String,
60 token_out: String,
61 spend_token: String,
62 spend_token_decimals: u8,
63 max_amount: String,
64 ) -> Self {
65 Self::builder()
66 .token_in(token_in)
67 .token_out(token_out)
68 .spend_token(spend_token)
69 .spend_token_decimals(spend_token_decimals)
70 .max_amount(max_amount)
71 .build()
72 }
73}
74
75#[pymethods]
76#[pyo3_stub_gen::derive::gen_stub_pymethods(module = "nautilus_trader.adapters.blockchain")]
77impl BlockchainProviderIdentity {
78 #[new]
80 #[must_use]
81 fn py_new(provider_id: String, operator_id: String, failure_domain_ids: Vec<String>) -> Self {
82 Self::builder()
83 .provider_id(provider_id)
84 .operator_id(operator_id)
85 .failure_domain_ids(failure_domain_ids)
86 .build()
87 }
88}
89
90#[pymethods]
91#[pyo3_stub_gen::derive::gen_stub_pymethods(module = "nautilus_trader.adapters.blockchain")]
92impl BlockchainVerificationProviderConfig {
93 #[new]
95 #[must_use]
96 fn py_new(identity: BlockchainProviderIdentity, http_rpc_url: String) -> Self {
97 Self::builder()
98 .identity(identity)
99 .http_rpc_url(SecretString::from(http_rpc_url))
100 .build()
101 }
102
103 fn __repr__(&self) -> String {
104 format!(
105 "BlockchainVerificationProviderConfig(identity={:?}, http_rpc_url={REDACTED})",
106 self.identity
107 )
108 }
109}
110
111#[pymethods]
112#[pyo3_stub_gen::derive::gen_stub_pymethods(module = "nautilus_trader.adapters.blockchain")]
113impl BlockchainChainAnchorConfig {
114 #[new]
116 #[expect(clippy::too_many_arguments)]
117 #[must_use]
118 fn py_new(
119 chain_id: u32,
120 chain_name: String,
121 checkpoint_height: u64,
122 checkpoint_hash: String,
123 checkpoint_timestamp: u64,
124 max_head_skew_blocks: u64,
125 max_head_age_secs: u64,
126 max_future_drift_secs: u64,
127 ) -> Self {
128 Self::builder()
129 .chain_id(chain_id)
130 .chain_name(chain_name)
131 .checkpoint_height(checkpoint_height)
132 .checkpoint_hash(checkpoint_hash)
133 .checkpoint_timestamp(checkpoint_timestamp)
134 .max_head_skew_blocks(max_head_skew_blocks)
135 .max_head_age_secs(max_head_age_secs)
136 .max_future_drift_secs(max_future_drift_secs)
137 .build()
138 }
139}
140
141#[pymethods]
142#[pyo3_stub_gen::derive::gen_stub_pymethods(module = "nautilus_trader.adapters.blockchain")]
143impl BlockchainVerificationConfig {
144 #[new]
146 fn py_new(
147 authoritative: BlockchainProviderIdentity,
148 verifiers: Vec<BlockchainVerificationProviderConfig>,
149 chain_anchor: BlockchainChainAnchorConfig,
150 manifest_version: String,
151 manifest_digest: String,
152 deployment_manifest_json: &str,
153 ) -> PyResult<Self> {
154 let deployment_manifest = serde_json::from_str(deployment_manifest_json)
155 .map_err(|_| to_pyvalue_err("Invalid deployment manifest JSON"))?;
156 Ok(Self::builder()
157 .authoritative(authoritative)
158 .verifiers(verifiers)
159 .chain_anchor(chain_anchor)
160 .manifest_version(manifest_version)
161 .manifest_digest(manifest_digest)
162 .deployment_manifest(deployment_manifest)
163 .build())
164 }
165
166 #[getter]
167 fn deployment_manifest_json(&self) -> PyResult<String> {
168 serde_json::to_string(&self.deployment_manifest).map_err(to_pyvalue_err)
169 }
170}
171
172#[pymethods]
173#[pyo3_stub_gen::derive::gen_stub_pymethods(module = "nautilus_trader.adapters.blockchain")]
174impl BlockchainDataClientConfig {
175 #[new]
177 #[expect(clippy::too_many_arguments)]
178 #[pyo3(signature = (chain, dex_ids, http_rpc_url, rpc_requests_per_second=None, multicall_calls_per_rpc_request=None, wss_rpc_url=None, use_hypersync_for_live_data=true, from_block=None, pool_filters=None, postgres_cache_database_config=None, proxy_url=None, transport_backend=None))]
179 fn py_new(
180 #[gen_stub(
181 override_type(
182 type_repr = "nautilus_trader.model.Chain",
183 imports = ("nautilus_trader.model",),
184 ),
185 )]
186 chain: &Chain,
187 #[gen_stub(
188 override_type(
189 type_repr = "typing.Sequence[nautilus_trader.model.DexType]",
190 imports = ("typing", "nautilus_trader.model"),
191 ),
192 )]
193 dex_ids: Vec<DexType>,
194 http_rpc_url: String,
195 rpc_requests_per_second: Option<u32>,
196 multicall_calls_per_rpc_request: Option<u32>,
197 wss_rpc_url: Option<String>,
198 use_hypersync_for_live_data: bool,
199 from_block: Option<u64>,
200 pool_filters: Option<DexPoolFilters>,
201 #[gen_stub(
202 override_type(
203 type_repr = "typing.Optional[nautilus_trader.infrastructure.PostgresConnectOptions]",
204 imports = ("typing", "nautilus_trader.infrastructure"),
205 ),
206 )]
207 postgres_cache_database_config: Option<PostgresConnectOptions>,
208 proxy_url: Option<String>,
209 transport_backend: Option<TransportBackend>,
210 ) -> PyResult<Self> {
211 let config = Self::builder()
212 .chain(Arc::new(chain.clone()))
213 .dex_ids(dex_ids)
214 .http_rpc_url(SecretString::from(http_rpc_url))
215 .maybe_rpc_requests_per_second(rpc_requests_per_second)
216 .maybe_multicall_calls_per_rpc_request(multicall_calls_per_rpc_request)
217 .maybe_wss_rpc_url(wss_rpc_url.map(SecretString::from))
218 .use_hypersync_for_live_data(use_hypersync_for_live_data)
219 .maybe_from_block(from_block)
220 .maybe_pool_filters(pool_filters)
221 .maybe_postgres_cache_database_config(postgres_cache_database_config)
222 .maybe_proxy_url(proxy_url.map(SecretString::from))
223 .transport_backend(transport_backend.unwrap_or_default())
224 .build();
225 config.validate().map_err(config_error_to_pyvalue_err)?;
226 Ok(config)
227 }
228
229 #[getter]
231 #[gen_stub(
232 override_return_type(
233 type_repr = "nautilus_trader.model.Chain",
234 imports = ("nautilus_trader.model",),
235 ),
236 )]
237 fn chain(&self) -> Chain {
238 (*self.chain).clone()
239 }
240
241 #[getter]
243 const fn rpc_requests_per_second(&self) -> Option<u32> {
244 self.rpc_requests_per_second
245 }
246
247 #[getter]
249 const fn use_hypersync_for_live_data(&self) -> bool {
250 self.use_hypersync_for_live_data
251 }
252
253 #[getter]
255 #[expect(clippy::wrong_self_convention)]
256 const fn from_block(&self) -> Option<u64> {
257 self.from_block
258 }
259
260 #[getter]
261 const fn has_postgres_cache_database_config(&self) -> bool {
262 self.postgres_cache_database_config.is_some()
263 }
264
265 #[getter]
266 const fn has_proxy_url(&self) -> bool {
267 self.proxy_url.is_some()
268 }
269
270 fn __repr__(&self) -> String {
272 format!(
273 "BlockchainDataClientConfig(chain={:?}, http_rpc_url={REDACTED}, wss_rpc_url={:?}, use_hypersync_for_live_data={}, from_block={:?})",
274 self.chain.name,
275 self.wss_rpc_url.as_ref().map(|_| REDACTED),
276 self.use_hypersync_for_live_data,
277 self.from_block
278 )
279 }
280}
281
282#[pymethods]
283#[pyo3_stub_gen::derive::gen_stub_pymethods(module = "nautilus_trader.adapters.blockchain")]
284impl BlockchainExecutionClientConfig {
285 #[new]
287 #[expect(clippy::too_many_arguments)]
288 #[pyo3(signature = (client_id, chain, wallet_address, http_rpc_url, signer_private_key_env, router_addresses, weth_address, max_fee_per_gas_wei, base_fee_buffer_bps, gas_limit, gas_buffer_bps, tokens=None, rpc_requests_per_second=None, unlimited_approval=false, postgres_cache_database_config=None, transport_backend=None, *, allowed_token_pairs=None, quote_spend_limits=None, slippage_bps=None, max_slippage_bps=None, max_order_amount=None, deadline_seconds=None, max_quote_age_blocks=None, receipt_timeout_secs=None, payload_key_env=None, payload_key_retired_env=None, payload_deployment_id=None, verification=None))]
289 fn py_new(
290 client_id: AccountId,
291 #[gen_stub(
292 override_type(
293 type_repr = "nautilus_trader.model.Chain",
294 imports = ("nautilus_trader.model",),
295 ),
296 )]
297 chain: &Chain,
298 wallet_address: String,
299 http_rpc_url: String,
300 signer_private_key_env: String,
301 router_addresses: Vec<String>,
302 weth_address: String,
303 max_fee_per_gas_wei: u64,
304 base_fee_buffer_bps: u32,
305 gas_limit: u64,
306 gas_buffer_bps: u32,
307 tokens: Option<Vec<String>>,
308 rpc_requests_per_second: Option<u32>,
309 unlimited_approval: bool,
310 #[gen_stub(
311 override_type(
312 type_repr = "typing.Optional[nautilus_trader.infrastructure.PostgresConnectOptions]",
313 imports = ("typing", "nautilus_trader.infrastructure"),
314 ),
315 )]
316 postgres_cache_database_config: Option<PostgresConnectOptions>,
317 transport_backend: Option<TransportBackend>,
318 allowed_token_pairs: Option<Vec<(String, String)>>,
319 quote_spend_limits: Option<Vec<QuoteSpendLimit>>,
320 slippage_bps: Option<u32>,
321 max_slippage_bps: Option<u32>,
322 max_order_amount: Option<u64>,
323 deadline_seconds: Option<u64>,
324 max_quote_age_blocks: Option<u64>,
325 receipt_timeout_secs: Option<u64>,
326 payload_key_env: Option<String>,
327 payload_key_retired_env: Option<Vec<String>>,
328 payload_deployment_id: Option<String>,
329 verification: Option<BlockchainVerificationConfig>,
330 ) -> PyResult<Self> {
331 let config = Self::builder()
332 .client_id(client_id)
333 .chain(chain.clone())
334 .wallet_address(wallet_address)
335 .http_rpc_url(SecretString::from(http_rpc_url))
336 .signer_private_key_env(signer_private_key_env)
337 .router_addresses(router_addresses)
338 .weth_address(weth_address)
339 .max_fee_per_gas_wei(max_fee_per_gas_wei)
340 .base_fee_buffer_bps(base_fee_buffer_bps)
341 .gas_limit(gas_limit)
342 .gas_buffer_bps(gas_buffer_bps)
343 .maybe_allowed_token_pairs(allowed_token_pairs)
344 .maybe_quote_spend_limits(quote_spend_limits)
345 .maybe_slippage_bps(slippage_bps)
346 .maybe_max_slippage_bps(max_slippage_bps)
347 .maybe_max_order_amount(max_order_amount)
348 .maybe_deadline_seconds(deadline_seconds)
349 .maybe_max_quote_age_blocks(max_quote_age_blocks)
350 .maybe_receipt_timeout_secs(receipt_timeout_secs)
351 .maybe_payload_key_env(payload_key_env)
352 .payload_key_retired_env(payload_key_retired_env.unwrap_or_default())
353 .maybe_payload_deployment_id(payload_deployment_id)
354 .maybe_verification(verification)
355 .maybe_tokens(tokens)
356 .maybe_rpc_requests_per_second(rpc_requests_per_second)
357 .unlimited_approval(unlimited_approval)
358 .maybe_postgres_cache_database_config(postgres_cache_database_config)
359 .transport_backend(transport_backend.unwrap_or_default())
360 .build();
361 config.validate().map_err(config_error_to_pyvalue_err)?;
362 Ok(config)
363 }
364
365 #[getter]
367 #[gen_stub(override_return_type(type_repr = "list[tuple[str, str]] | None",))]
368 fn allowed_token_pairs(&self) -> Option<Vec<(String, String)>> {
369 self.allowed_token_pairs.clone()
370 }
371
372 #[getter]
374 const fn client_id(&self) -> AccountId {
375 self.client_id
376 }
377
378 #[getter]
380 #[gen_stub(
381 override_return_type(
382 type_repr = "nautilus_trader.model.Chain",
383 imports = ("nautilus_trader.model",),
384 ),
385 )]
386 fn chain(&self) -> Chain {
387 self.chain.clone()
388 }
389
390 #[getter]
392 const fn rpc_requests_per_second(&self) -> Option<u32> {
393 self.rpc_requests_per_second
394 }
395
396 #[getter]
397 const fn has_postgres_cache_database_config(&self) -> bool {
398 self.postgres_cache_database_config.is_some()
399 }
400
401 fn __repr__(&self) -> String {
403 format!(
404 "BlockchainExecutionClientConfig(chain={:?}, wallet_address={}, http_rpc_url={REDACTED})",
405 self.chain.name, self.wallet_address
406 )
407 }
408}