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};
25use nautilus_live::ExecutionClientCore;
26use nautilus_model::{
27    enums::{AccountType, OmsType},
28    identifiers::ClientId,
29};
30use nautilus_system::{
31    ExecutionClientFactory,
32    factories::{ClientConfig, DataClientFactory},
33};
34
35use crate::{
36    config::{BlockchainDataClientConfig, BlockchainExecutionClientConfig},
37    constants::BLOCKCHAIN_VENUE,
38    data::client::BlockchainDataClient,
39    execution::client::BlockchainExecutionClient,
40};
41
42impl ClientConfig for BlockchainDataClientConfig {
43    fn as_any(&self) -> &dyn Any {
44        self
45    }
46}
47
48/// Factory for creating blockchain data clients.
49///
50/// This factory creates `BlockchainDataClient` instances configured for different blockchain networks
51/// (Ethereum, Arbitrum, Base, Polygon) with appropriate RPC and HyperSync configurations.
52#[derive(Debug, Clone)]
53#[cfg_attr(
54    feature = "python",
55    pyo3::pyclass(module = "nautilus_trader.core.nautilus_pyo3.blockchain")
56)]
57#[cfg_attr(
58    feature = "python",
59    pyo3_stub_gen::derive::gen_stub_pyclass(module = "nautilus_trader.adapters.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(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(module = "nautilus_trader.core.nautilus_pyo3.blockchain")
113)]
114#[cfg_attr(
115    feature = "python",
116    pyo3_stub_gen::derive::gen_stub_pyclass(module = "nautilus_trader.adapters.blockchain")
117)]
118pub struct BlockchainExecutionClientFactory;
119
120impl BlockchainExecutionClientFactory {
121    /// Creates a new [`BlockchainExecutionClientFactory`] instance.
122    #[must_use]
123    pub const fn new() -> Self {
124        Self
125    }
126}
127
128impl Default for BlockchainExecutionClientFactory {
129    fn default() -> Self {
130        Self::new()
131    }
132}
133
134impl ExecutionClientFactory for BlockchainExecutionClientFactory {
135    fn create(
136        &self,
137        name: &str,
138        config: &dyn ClientConfig,
139        cache: Rc<RefCell<Cache>>,
140        clock: Rc<RefCell<dyn Clock>>,
141    ) -> anyhow::Result<Box<dyn ExecutionClient>> {
142        let blockchain_execution_config = config
143            .as_any()
144            .downcast_ref::<BlockchainExecutionClientConfig>()
145            .ok_or_else(|| {
146                anyhow::anyhow!(
147                    "Invalid config type for BlockchainDataClientFactory. Expected `BlockchainDataClientConfig`, was {config:?}"
148                )
149            })?;
150
151        let core_execution_client = ExecutionClientCore::new(
152            blockchain_execution_config.trader_id,
153            ClientId::from(name),
154            *BLOCKCHAIN_VENUE,
155            OmsType::Netting,
156            blockchain_execution_config.client_id,
157            AccountType::Wallet,
158            None,
159            clock,
160            cache,
161        );
162
163        let client = BlockchainExecutionClient::new(
164            core_execution_client,
165            blockchain_execution_config.clone(),
166        )?;
167
168        Ok(Box::new(client))
169    }
170
171    fn name(&self) -> &'static str {
172        "BLOCKCHAIN"
173    }
174
175    fn config_type(&self) -> &'static str {
176        "BlockchainExecutionClientConfig"
177    }
178}
179
180#[cfg(test)]
181mod tests {
182    use std::sync::Arc;
183
184    use nautilus_model::defi::chain::{Blockchain, chains};
185    use nautilus_system::factories::DataClientFactory;
186    use rstest::rstest;
187
188    use crate::{config::BlockchainDataClientConfig, factories::BlockchainDataClientFactory};
189
190    #[rstest]
191    fn test_blockchain_data_client_config_creation() {
192        let chain = Arc::new(chains::ETHEREUM.clone());
193        let config = BlockchainDataClientConfig::new(
194            chain,
195            vec![],
196            "https://eth-mainnet.example.com".to_string(),
197            None,
198            None,
199            None,
200            false,
201            None,
202            None,
203            None,
204        );
205
206        assert_eq!(config.chain.name, Blockchain::Ethereum);
207        assert_eq!(config.http_rpc_url, "https://eth-mainnet.example.com");
208    }
209
210    #[rstest]
211    fn test_factory_creation() {
212        let factory = BlockchainDataClientFactory::new();
213        assert_eq!(factory.name(), "BLOCKCHAIN");
214        assert_eq!(factory.config_type(), "BlockchainDataClientConfig");
215    }
216}