Skip to main content

nautilus_blockchain/
factories.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//! Factory for creating blockchain data clients.
17
18use std::{any::Any, cell::RefCell, rc::Rc};
19
20use nautilus_common::{
21    cache::Cache,
22    clients::{DataClient, ExecutionClient},
23    clock::Clock,
24    factories::{ClientConfig, DataClientFactory, ExecutionClientFactory},
25};
26use nautilus_live::ExecutionClientCore;
27use nautilus_model::{
28    enums::{AccountType, OmsType},
29    identifiers::ClientId,
30};
31
32use crate::{
33    config::{BlockchainDataClientConfig, BlockchainExecutionClientConfig},
34    constants::BLOCKCHAIN_VENUE,
35    data::client::BlockchainDataClient,
36    execution::client::BlockchainExecutionClient,
37};
38
39impl ClientConfig for BlockchainDataClientConfig {
40    fn as_any(&self) -> &dyn Any {
41        self
42    }
43}
44
45/// Factory for creating blockchain data clients.
46///
47/// This factory creates `BlockchainDataClient` instances configured for different blockchain networks
48/// (Ethereum, Arbitrum, Base, Polygon) with appropriate RPC and HyperSync configurations.
49#[derive(Debug, Clone)]
50#[cfg_attr(
51    feature = "python",
52    pyo3::pyclass(
53        module = "nautilus_trader.core.nautilus_pyo3.blockchain",
54        from_py_object
55    )
56)]
57#[cfg_attr(
58    feature = "python",
59    pyo3_stub_gen::derive::gen_stub_pyclass(module = "nautilus_trader.blockchain")
60)]
61pub struct BlockchainDataClientFactory;
62
63impl BlockchainDataClientFactory {
64    /// Creates a new [`BlockchainDataClientFactory`] instance.
65    #[must_use]
66    pub const fn new() -> Self {
67        Self
68    }
69}
70
71impl Default for BlockchainDataClientFactory {
72    fn default() -> Self {
73        Self::new()
74    }
75}
76
77impl DataClientFactory for BlockchainDataClientFactory {
78    fn create(
79        &self,
80        name: &str,
81        config: &dyn ClientConfig,
82        _cache: Rc<RefCell<Cache>>,
83        _clock: Rc<RefCell<dyn Clock>>,
84    ) -> anyhow::Result<Box<dyn DataClient>> {
85        let blockchain_config = config
86            .as_any()
87            .downcast_ref::<BlockchainDataClientConfig>()
88            .ok_or_else(|| {
89                anyhow::anyhow!(
90                    "Invalid config type for BlockchainDataClientFactory. Expected `BlockchainDataClientConfig`, was {config:?}"
91                )
92            })?;
93
94        let client = BlockchainDataClient::new(ClientId::from(name), blockchain_config.clone());
95
96        Ok(Box::new(client))
97    }
98
99    fn name(&self) -> &'static str {
100        "BLOCKCHAIN"
101    }
102
103    fn config_type(&self) -> &'static str {
104        "BlockchainDataClientConfig"
105    }
106}
107
108/// Factory for creating blockchain execution clients.
109#[derive(Debug, Clone)]
110#[cfg_attr(
111    feature = "python",
112    pyo3::pyclass(
113        module = "nautilus_trader.core.nautilus_pyo3.blockchain",
114        from_py_object
115    )
116)]
117#[cfg_attr(
118    feature = "python",
119    pyo3_stub_gen::derive::gen_stub_pyclass(module = "nautilus_trader.blockchain")
120)]
121pub struct BlockchainExecutionClientFactory;
122
123impl BlockchainExecutionClientFactory {
124    /// Creates a new [`BlockchainExecutionClientFactory`] instance.
125    #[must_use]
126    pub const fn new() -> Self {
127        Self
128    }
129}
130
131impl Default for BlockchainExecutionClientFactory {
132    fn default() -> Self {
133        Self::new()
134    }
135}
136
137impl ExecutionClientFactory for BlockchainExecutionClientFactory {
138    fn create(
139        &self,
140        name: &str,
141        config: &dyn ClientConfig,
142        cache: Rc<RefCell<Cache>>,
143    ) -> anyhow::Result<Box<dyn ExecutionClient>> {
144        let blockchain_execution_config = config
145            .as_any()
146            .downcast_ref::<BlockchainExecutionClientConfig>()
147            .ok_or_else(|| {
148                anyhow::anyhow!(
149                    "Invalid config type for BlockchainExecutionClientFactory. Expected `BlockchainExecutionClientConfig`, was {config:?}"
150                )
151            })?;
152
153        let core_execution_client = ExecutionClientCore::new(
154            blockchain_execution_config.trader_id,
155            ClientId::from(name),
156            *BLOCKCHAIN_VENUE,
157            OmsType::Netting,
158            blockchain_execution_config.client_id,
159            AccountType::Wallet,
160            None,
161            cache,
162        );
163
164        let client = BlockchainExecutionClient::new(
165            core_execution_client,
166            blockchain_execution_config.clone(),
167        )?;
168
169        Ok(Box::new(client))
170    }
171
172    fn name(&self) -> &'static str {
173        "BLOCKCHAIN"
174    }
175
176    fn config_type(&self) -> &'static str {
177        "BlockchainExecutionClientConfig"
178    }
179}
180
181#[cfg(test)]
182mod tests {
183    use std::sync::Arc;
184
185    use nautilus_common::factories::DataClientFactory;
186    use nautilus_model::defi::chain::{Blockchain, chains};
187    use rstest::rstest;
188
189    use crate::{config::BlockchainDataClientConfig, factories::BlockchainDataClientFactory};
190
191    #[rstest]
192    fn test_blockchain_data_client_config_creation() {
193        let chain = Arc::new(chains::ETHEREUM.clone());
194        let config = BlockchainDataClientConfig::builder()
195            .chain(chain)
196            .http_rpc_url("https://eth-mainnet.example.com".to_string())
197            .build();
198
199        assert_eq!(config.chain.name, Blockchain::Ethereum);
200        assert_eq!(config.http_rpc_url, "https://eth-mainnet.example.com");
201    }
202
203    #[rstest]
204    fn test_factory_creation() {
205        let factory = BlockchainDataClientFactory::new();
206        assert_eq!(factory.name(), "BLOCKCHAIN");
207        assert_eq!(factory.config_type(), "BlockchainDataClientConfig");
208    }
209}