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