Skip to main content

nautilus_blockchain/python/
config.rs

1// -------------------------------------------------------------------------------------------------
2//  Copyright (C) 2015-2026 Nautech Systems Pty Ltd. All rights reserved.
3//  https://nautechsystems.io
4//
5//  Licensed under the GNU Lesser General Public License Version 3.0 (the "License");
6//  You may not use this file except in compliance with the License.
7//  You may obtain a copy of the License at https://www.gnu.org/licenses/lgpl-3.0.en.html
8//
9//  Unless required by applicable law or agreed to in writing, software
10//  distributed under the License is distributed on an "AS IS" BASIS,
11//  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12//  See the License for the specific language governing permissions and
13//  limitations under the License.
14// -------------------------------------------------------------------------------------------------
15
16//! Python bindings for blockchain configuration.
17
18use std::sync::Arc;
19
20use nautilus_core::{python::to_pyvalue_err, string::secret::REDACTED};
21use nautilus_infrastructure::sql::pg::PostgresConnectOptions;
22use nautilus_model::{
23    defi::{Chain, DexType},
24    identifiers::AccountId,
25};
26use nautilus_network::websocket::TransportBackend;
27use pyo3::prelude::*;
28
29use crate::config::{
30    BlockchainChainAnchorConfig, BlockchainDataClientConfig, BlockchainExecutionClientConfig,
31    BlockchainProviderIdentity, BlockchainVerificationConfig, BlockchainVerificationProviderConfig,
32    DexPoolFilters, QuoteSpendLimit,
33};
34
35#[pymethods]
36#[pyo3_stub_gen::derive::gen_stub_pymethods(module = "nautilus_trader.adapters.blockchain")]
37impl DexPoolFilters {
38    /// Defines filtering criteria for the DEX pool universe that the data client will operate on.
39    #[new]
40    #[must_use]
41    pub fn py_new(remove_pools_with_empty_erc20_fields: Option<bool>) -> Self {
42        Self::builder()
43            .maybe_remove_pools_with_empty_erc20fields(remove_pools_with_empty_erc20_fields)
44            .build()
45    }
46}
47
48#[pymethods]
49#[pyo3_stub_gen::derive::gen_stub_pymethods(module = "nautilus_trader.adapters.blockchain")]
50impl QuoteSpendLimit {
51    /// Defines the maximum quote-token spend for a directed BUY swap pair.
52    #[new]
53    #[must_use]
54    fn py_new(
55        token_in: String,
56        token_out: String,
57        spend_token: String,
58        spend_token_decimals: u8,
59        max_amount: String,
60    ) -> Self {
61        Self::builder()
62            .token_in(token_in)
63            .token_out(token_out)
64            .spend_token(spend_token)
65            .spend_token_decimals(spend_token_decimals)
66            .max_amount(max_amount)
67            .build()
68    }
69}
70
71#[pymethods]
72#[pyo3_stub_gen::derive::gen_stub_pymethods(module = "nautilus_trader.adapters.blockchain")]
73impl BlockchainProviderIdentity {
74    /// Stable local identity for one RPC provider and its failure domains.
75    #[new]
76    #[must_use]
77    fn py_new(provider_id: String, operator_id: String, failure_domain_ids: Vec<String>) -> Self {
78        Self::builder()
79            .provider_id(provider_id)
80            .operator_id(operator_id)
81            .failure_domain_ids(failure_domain_ids)
82            .build()
83    }
84}
85
86#[pymethods]
87#[pyo3_stub_gen::derive::gen_stub_pymethods(module = "nautilus_trader.adapters.blockchain")]
88impl BlockchainVerificationProviderConfig {
89    /// Configuration for one read-only verification RPC provider.
90    #[new]
91    #[must_use]
92    fn py_new(identity: BlockchainProviderIdentity, http_rpc_url: String) -> Self {
93        Self::builder()
94            .identity(identity)
95            .http_rpc_url(http_rpc_url)
96            .build()
97    }
98
99    fn __repr__(&self) -> String {
100        format!(
101            "BlockchainVerificationProviderConfig(identity={:?}, http_rpc_url={REDACTED})",
102            self.identity
103        )
104    }
105}
106
107#[pymethods]
108#[pyo3_stub_gen::derive::gen_stub_pymethods(module = "nautilus_trader.adapters.blockchain")]
109impl BlockchainChainAnchorConfig {
110    /// Locally trusted finalized chain checkpoint and freshness policy.
111    #[new]
112    #[expect(clippy::too_many_arguments)]
113    #[must_use]
114    fn py_new(
115        chain_id: u32,
116        chain_name: String,
117        checkpoint_height: u64,
118        checkpoint_hash: String,
119        checkpoint_timestamp: u64,
120        max_head_skew_blocks: u64,
121        max_head_age_secs: u64,
122        max_future_drift_secs: u64,
123    ) -> Self {
124        Self::builder()
125            .chain_id(chain_id)
126            .chain_name(chain_name)
127            .checkpoint_height(checkpoint_height)
128            .checkpoint_hash(checkpoint_hash)
129            .checkpoint_timestamp(checkpoint_timestamp)
130            .max_head_skew_blocks(max_head_skew_blocks)
131            .max_head_age_secs(max_head_age_secs)
132            .max_future_drift_secs(max_future_drift_secs)
133            .build()
134    }
135}
136
137#[pymethods]
138#[pyo3_stub_gen::derive::gen_stub_pymethods(module = "nautilus_trader.adapters.blockchain")]
139impl BlockchainVerificationConfig {
140    /// Independent verification topology and reviewed local deployment identity.
141    #[new]
142    fn py_new(
143        authoritative: BlockchainProviderIdentity,
144        verifiers: Vec<BlockchainVerificationProviderConfig>,
145        chain_anchor: BlockchainChainAnchorConfig,
146        manifest_version: String,
147        manifest_digest: String,
148        deployment_manifest_json: String,
149    ) -> PyResult<Self> {
150        let deployment_manifest = serde_json::from_str(&deployment_manifest_json)
151            .map_err(|_| to_pyvalue_err("Invalid deployment manifest JSON"))?;
152        Ok(Self::builder()
153            .authoritative(authoritative)
154            .verifiers(verifiers)
155            .chain_anchor(chain_anchor)
156            .manifest_version(manifest_version)
157            .manifest_digest(manifest_digest)
158            .deployment_manifest(deployment_manifest)
159            .build())
160    }
161
162    #[getter]
163    fn deployment_manifest_json(&self) -> PyResult<String> {
164        serde_json::to_string(&self.deployment_manifest).map_err(to_pyvalue_err)
165    }
166}
167
168#[pymethods]
169#[pyo3_stub_gen::derive::gen_stub_pymethods(module = "nautilus_trader.adapters.blockchain")]
170impl BlockchainDataClientConfig {
171    /// Configuration for blockchain data clients.
172    #[new]
173    #[expect(clippy::too_many_arguments)]
174    #[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))]
175    fn py_new(
176        #[gen_stub(
177            override_type(
178                type_repr = "nautilus_trader.model.Chain",
179                imports = ("nautilus_trader.model",),
180            ),
181        )]
182        chain: &Chain,
183        #[gen_stub(
184            override_type(
185                type_repr = "typing.Sequence[nautilus_trader.model.DexType]",
186                imports = ("typing", "nautilus_trader.model"),
187            ),
188        )]
189        dex_ids: Vec<DexType>,
190        http_rpc_url: String,
191        rpc_requests_per_second: Option<u32>,
192        multicall_calls_per_rpc_request: Option<u32>,
193        wss_rpc_url: Option<String>,
194        use_hypersync_for_live_data: bool,
195        from_block: Option<u64>,
196        pool_filters: Option<DexPoolFilters>,
197        #[gen_stub(
198            override_type(
199                type_repr = "typing.Optional[nautilus_trader.infrastructure.PostgresConnectOptions]",
200                imports = ("typing", "nautilus_trader.infrastructure"),
201            ),
202        )]
203        postgres_cache_database_config: Option<PostgresConnectOptions>,
204        proxy_url: Option<String>,
205        transport_backend: Option<TransportBackend>,
206    ) -> Self {
207        Self::builder()
208            .chain(Arc::new(chain.clone()))
209            .dex_ids(dex_ids)
210            .http_rpc_url(http_rpc_url)
211            .maybe_rpc_requests_per_second(rpc_requests_per_second)
212            .maybe_multicall_calls_per_rpc_request(multicall_calls_per_rpc_request)
213            .maybe_wss_rpc_url(wss_rpc_url)
214            .use_hypersync_for_live_data(use_hypersync_for_live_data)
215            .maybe_from_block(from_block)
216            .maybe_pool_filters(pool_filters)
217            .maybe_postgres_cache_database_config(postgres_cache_database_config)
218            .maybe_proxy_url(proxy_url)
219            .transport_backend(transport_backend.unwrap_or_default())
220            .build()
221    }
222
223    /// Returns the chain configuration.
224    #[getter]
225    #[gen_stub(
226        override_return_type(
227            type_repr = "nautilus_trader.model.Chain",
228            imports = ("nautilus_trader.model",),
229        ),
230    )]
231    fn chain(&self) -> Chain {
232        (*self.chain).clone()
233    }
234
235    /// Returns the HTTP RPC URL.
236    #[getter]
237    fn http_rpc_url(&self) -> String {
238        self.http_rpc_url.clone()
239    }
240
241    /// Returns the WebSocket RPC URL.
242    #[getter]
243    fn wss_rpc_url(&self) -> Option<String> {
244        self.wss_rpc_url.clone()
245    }
246
247    /// Returns the RPC requests per second limit.
248    #[getter]
249    const fn rpc_requests_per_second(&self) -> Option<u32> {
250        self.rpc_requests_per_second
251    }
252
253    /// Returns whether to use HyperSync for live data.
254    #[getter]
255    const fn use_hypersync_for_live_data(&self) -> bool {
256        self.use_hypersync_for_live_data
257    }
258
259    /// Returns the starting block for sync.
260    #[getter]
261    #[expect(clippy::wrong_self_convention)]
262    const fn from_block(&self) -> Option<u64> {
263        self.from_block
264    }
265
266    #[getter]
267    const fn has_postgres_cache_database_config(&self) -> bool {
268        self.postgres_cache_database_config.is_some()
269    }
270
271    #[getter]
272    const fn has_proxy_url(&self) -> bool {
273        self.proxy_url.is_some()
274    }
275
276    /// Returns a string representation of the configuration.
277    fn __repr__(&self) -> String {
278        format!(
279            "BlockchainDataClientConfig(chain={:?}, http_rpc_url={REDACTED}, wss_rpc_url={:?}, use_hypersync_for_live_data={}, from_block={:?})",
280            self.chain.name,
281            self.wss_rpc_url.as_ref().map(|_| REDACTED),
282            self.use_hypersync_for_live_data,
283            self.from_block
284        )
285    }
286}
287
288#[pymethods]
289#[pyo3_stub_gen::derive::gen_stub_pymethods(module = "nautilus_trader.adapters.blockchain")]
290impl BlockchainExecutionClientConfig {
291    /// Configuration for blockchain execution clients.
292    #[new]
293    #[expect(clippy::too_many_arguments)]
294    #[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))]
295    fn py_new(
296        client_id: AccountId,
297        #[gen_stub(
298            override_type(
299                type_repr = "nautilus_trader.model.Chain",
300                imports = ("nautilus_trader.model",),
301            ),
302        )]
303        chain: &Chain,
304        wallet_address: String,
305        http_rpc_url: String,
306        signer_private_key_env: String,
307        router_addresses: Vec<String>,
308        weth_address: String,
309        max_fee_per_gas_wei: u64,
310        base_fee_buffer_bps: u32,
311        gas_limit: u64,
312        gas_buffer_bps: u32,
313        tokens: Option<Vec<String>>,
314        rpc_requests_per_second: Option<u32>,
315        unlimited_approval: bool,
316        #[gen_stub(
317            override_type(
318                type_repr = "typing.Optional[nautilus_trader.infrastructure.PostgresConnectOptions]",
319                imports = ("typing", "nautilus_trader.infrastructure"),
320            ),
321        )]
322        postgres_cache_database_config: Option<PostgresConnectOptions>,
323        transport_backend: Option<TransportBackend>,
324        allowed_token_pairs: Option<Vec<(String, String)>>,
325        quote_spend_limits: Option<Vec<QuoteSpendLimit>>,
326        slippage_bps: Option<u32>,
327        max_slippage_bps: Option<u32>,
328        max_order_amount: Option<u64>,
329        deadline_seconds: Option<u64>,
330        max_quote_age_blocks: Option<u64>,
331        receipt_timeout_secs: Option<u64>,
332        payload_key_env: Option<String>,
333        payload_key_retired_env: Option<Vec<String>>,
334        payload_deployment_id: Option<String>,
335        verification: Option<BlockchainVerificationConfig>,
336    ) -> Self {
337        Self::builder()
338            .client_id(client_id)
339            .chain(chain.clone())
340            .wallet_address(wallet_address)
341            .http_rpc_url(http_rpc_url)
342            .signer_private_key_env(signer_private_key_env)
343            .router_addresses(router_addresses)
344            .weth_address(weth_address)
345            .max_fee_per_gas_wei(max_fee_per_gas_wei)
346            .base_fee_buffer_bps(base_fee_buffer_bps)
347            .gas_limit(gas_limit)
348            .gas_buffer_bps(gas_buffer_bps)
349            .maybe_allowed_token_pairs(allowed_token_pairs)
350            .maybe_quote_spend_limits(quote_spend_limits)
351            .maybe_slippage_bps(slippage_bps)
352            .maybe_max_slippage_bps(max_slippage_bps)
353            .maybe_max_order_amount(max_order_amount)
354            .maybe_deadline_seconds(deadline_seconds)
355            .maybe_max_quote_age_blocks(max_quote_age_blocks)
356            .maybe_receipt_timeout_secs(receipt_timeout_secs)
357            .maybe_payload_key_env(payload_key_env)
358            .payload_key_retired_env(payload_key_retired_env.unwrap_or_default())
359            .maybe_payload_deployment_id(payload_deployment_id)
360            .maybe_verification(verification)
361            .maybe_tokens(tokens)
362            .maybe_rpc_requests_per_second(rpc_requests_per_second)
363            .unlimited_approval(unlimited_approval)
364            .maybe_postgres_cache_database_config(postgres_cache_database_config)
365            .transport_backend(transport_backend.unwrap_or_default())
366            .build()
367    }
368
369    /// Returns the allowed (input token, output token) address pairs.
370    #[getter]
371    #[gen_stub(override_return_type(type_repr = "list[tuple[str, str]] | None",))]
372    fn allowed_token_pairs(&self) -> Option<Vec<(String, String)>> {
373        self.allowed_token_pairs.clone()
374    }
375
376    /// Returns the account ID.
377    #[getter]
378    const fn client_id(&self) -> AccountId {
379        self.client_id
380    }
381
382    /// Returns the chain configuration.
383    #[getter]
384    #[gen_stub(
385        override_return_type(
386            type_repr = "nautilus_trader.model.Chain",
387            imports = ("nautilus_trader.model",),
388        ),
389    )]
390    fn chain(&self) -> Chain {
391        self.chain.clone()
392    }
393
394    /// Returns the RPC requests per second limit.
395    #[getter]
396    const fn rpc_requests_per_second(&self) -> Option<u32> {
397        self.rpc_requests_per_second
398    }
399
400    #[getter]
401    const fn has_postgres_cache_database_config(&self) -> bool {
402        self.postgres_cache_database_config.is_some()
403    }
404
405    /// Returns a string representation of the configuration.
406    fn __repr__(&self) -> String {
407        format!(
408            "BlockchainExecutionClientConfig(chain={:?}, wallet_address={}, http_rpc_url={REDACTED})",
409            self.chain.name, self.wallet_address
410        )
411    }
412}