nautilus_blockchain/
factories.rs

1// -------------------------------------------------------------------------------------------------
2//  Copyright (C) 2015-2025 Posei Systems Pty Ltd. All rights reserved.
3//  https://poseitrader.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, sync::Arc};
19
20use nautilus_common::{cache::Cache, clock::Clock};
21use posei_trader::client::DataClient;
22use nautilus_model::defi::chain::Chain;
23use nautilus_system::factories::{ClientConfig, DataClientFactory};
24
25use crate::{config::BlockchainAdapterConfig, data::BlockchainDataClient};
26
27/// Configuration for blockchain data clients.
28#[derive(Debug, Clone)]
29pub struct BlockchainClientConfig {
30    /// The blockchain adapter configuration.
31    pub adapter_config: BlockchainAdapterConfig,
32    /// The blockchain chain configuration.
33    pub chain: Arc<Chain>,
34}
35
36impl BlockchainClientConfig {
37    /// Creates a new [`BlockchainClientConfig`] instance.
38    #[must_use]
39    pub const fn new(adapter_config: BlockchainAdapterConfig, chain: Arc<Chain>) -> Self {
40        Self {
41            adapter_config,
42            chain,
43        }
44    }
45}
46
47impl ClientConfig for BlockchainClientConfig {
48    fn as_any(&self) -> &dyn Any {
49        self
50    }
51}
52
53/// Factory for creating blockchain data clients.
54///
55/// This factory creates `BlockchainDataClient` instances configured for different blockchain networks
56/// (Ethereum, Arbitrum, Base, Polygon) with appropriate RPC and HyperSync configurations.
57#[derive(Debug)]
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: Rc<RefCell<Cache>>,
80        _clock: Rc<RefCell<dyn Clock>>,
81    ) -> anyhow::Result<Box<dyn DataClient>> {
82        let blockchain_config = config
83            .as_any()
84            .downcast_ref::<BlockchainClientConfig>()
85            .ok_or_else(|| {
86                anyhow::anyhow!(
87                    "Invalid config type for BlockchainDataClientFactory. Expected BlockchainClientConfig, got {:?}",
88                    config
89                )
90            })?;
91
92        let client = BlockchainDataClient::new(
93            blockchain_config.chain.clone(),
94            blockchain_config.adapter_config.clone(),
95        );
96
97        Ok(Box::new(client))
98    }
99
100    fn name(&self) -> &'static str {
101        "BLOCKCHAIN"
102    }
103
104    fn config_type(&self) -> &'static str {
105        "BlockchainClientConfig"
106    }
107}
108
109#[cfg(test)]
110mod tests {
111    use std::sync::Arc;
112
113    use nautilus_model::defi::chain::{Blockchain, chains};
114    use nautilus_system::factories::DataClientFactory;
115    use rstest::rstest;
116
117    use crate::{
118        config::BlockchainAdapterConfig,
119        factories::{BlockchainClientConfig, BlockchainDataClientFactory},
120    };
121
122    #[rstest]
123    fn test_blockchain_client_config_creation() {
124        let adapter_config = BlockchainAdapterConfig::new(
125            "https://eth-mainnet.example.com".to_string(),
126            None,
127            None,
128            false,
129        );
130        let chain = Arc::new(chains::ETHEREUM.clone());
131
132        let config = BlockchainClientConfig::new(adapter_config, chain);
133
134        assert_eq!(config.chain.name, Blockchain::Ethereum);
135        assert_eq!(
136            config.adapter_config.http_rpc_url,
137            "https://eth-mainnet.example.com"
138        );
139    }
140
141    #[rstest]
142    fn test_factory_creation() {
143        let factory = BlockchainDataClientFactory::new();
144        assert_eq!(factory.name(), "BlockchainDataClientFactory");
145        assert_eq!(factory.config_type(), "BlockchainClientConfig");
146    }
147}