nautilus_blockchain/
factories.rs1use 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#[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 #[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#[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 #[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 _clock: Rc<RefCell<dyn Clock>>,
135 ) -> anyhow::Result<Box<dyn ExecutionClient>> {
136 let blockchain_execution_config = config
137 .as_any()
138 .downcast_ref::<BlockchainExecutionClientConfig>()
139 .ok_or_else(|| {
140 anyhow::anyhow!(
141 "Invalid config type for BlockchainExecutionClientFactory. Expected `BlockchainExecutionClientConfig`, was {config:?}"
142 )
143 })?;
144
145 let core_execution_client = ExecutionClientCore::new(
146 trader_id,
147 ClientId::from(name),
148 *BLOCKCHAIN_VENUE,
149 OmsType::Netting,
150 blockchain_execution_config.client_id,
151 AccountType::Wallet,
152 None,
153 cache,
154 );
155
156 let client = BlockchainExecutionClient::new(
157 core_execution_client,
158 blockchain_execution_config.clone(),
159 )?;
160
161 Ok(Box::new(client))
162 }
163
164 fn name(&self) -> &'static str {
165 BLOCKCHAIN
166 }
167
168 fn config_type(&self) -> &'static str {
169 "BlockchainExecutionClientConfig"
170 }
171}
172
173#[cfg(test)]
174mod tests {
175 use std::sync::Arc;
176
177 use nautilus_common::factories::DataClientFactory;
178 use nautilus_model::defi::chain::{Blockchain, chains};
179 use rstest::rstest;
180
181 use crate::{
182 config::BlockchainDataClientConfig, constants::BLOCKCHAIN,
183 factories::BlockchainDataClientFactory,
184 };
185
186 #[rstest]
187 fn test_blockchain_data_client_config_creation() {
188 let chain = Arc::new(chains::ETHEREUM.clone());
189 let config = BlockchainDataClientConfig::builder()
190 .chain(chain)
191 .http_rpc_url("https://eth-mainnet.example.com".into())
192 .build();
193
194 assert_eq!(config.chain.name, Blockchain::Ethereum);
195 assert_eq!(
196 config.http_rpc_url.expose_secret(),
197 "https://eth-mainnet.example.com",
198 );
199 }
200
201 #[rstest]
202 fn test_factory_creation() {
203 let factory = BlockchainDataClientFactory::new();
204 assert_eq!(factory.name(), BLOCKCHAIN);
205 assert_eq!(factory.config_type(), "BlockchainDataClientConfig");
206 }
207}