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