nautilus_blockchain/execution/
client.rs1use std::sync::Arc;
17
18use alloy::primitives::Address;
19use async_trait::async_trait;
20use nautilus_common::{
21 messages::{
22 ExecutionEvent,
23 execution::{
24 BatchCancelOrders, CancelAllOrders, CancelOrder, GenerateFillReports,
25 GenerateOrderStatusReport, GeneratePositionReports, ModifyOrder, QueryAccount,
26 QueryOrder, SubmitOrder, SubmitOrderList,
27 },
28 },
29 runner::get_exec_event_sender,
30};
31use nautilus_core::UnixNanos;
32use nautilus_execution::client::{ExecutionClient, base::ExecutionClientCore};
33use nautilus_live::execution::client::LiveExecutionClient;
34use nautilus_model::{
35 accounts::AccountAny,
36 defi::{SharedChain, validation::validate_address},
37 enums::OmsType,
38 identifiers::{AccountId, ClientId, Venue},
39 reports::{ExecutionMassStatus, FillReport, OrderStatusReport, PositionStatusReport},
40 types::{AccountBalance, MarginBalance, Money},
41};
42
43use crate::{config::BlockchainExecutionClientConfig, rpc::http::BlockchainHttpRpcClient};
44
45#[derive(Debug, Clone)]
46pub struct BlockchainExecutionClient {
47 core: ExecutionClientCore,
48 chain: SharedChain,
49 wallet_address: Address,
50 connected: bool,
51 http_rpc_client: Arc<BlockchainHttpRpcClient>,
52}
53
54impl BlockchainExecutionClient {
55 #[must_use]
61 pub fn new(core_client: ExecutionClientCore, config: BlockchainExecutionClientConfig) -> Self {
62 let http_rpc_client = Arc::new(BlockchainHttpRpcClient::new(
63 config.http_rpc_url.clone(),
64 config.rpc_requests_per_second,
65 ));
66 let wallet_address =
67 validate_address(config.wallet_address.as_str()).expect("Invalid wallet address");
68 Self {
69 core: core_client,
70 connected: false,
71 chain: Arc::new(config.chain),
72 http_rpc_client,
73 wallet_address,
74 }
75 }
76
77 async fn fetch_native_currency_balance(&self) -> anyhow::Result<Money> {
78 let balance_u256 = self
79 .http_rpc_client
80 .get_balance(&self.wallet_address, None)
81 .await?;
82
83 let native_currency = self.chain.native_currency();
84
85 let balance = Money::from_wei(balance_u256, native_currency);
87
88 Ok(balance)
89 }
90
91 async fn refresh_wallet_balances(&self) {
92 if let Ok(native_balance) = self.fetch_native_currency_balance().await {
93 tracing::info!(
94 "Blockchain wallet balance: {} {}",
95 native_balance.as_decimal(),
96 native_balance.currency
97 );
98 }
99 }
100}
101
102impl ExecutionClient for BlockchainExecutionClient {
103 fn is_connected(&self) -> bool {
104 self.connected
105 }
106
107 fn client_id(&self) -> ClientId {
108 self.core.client_id
109 }
110
111 fn account_id(&self) -> AccountId {
112 self.core.account_id
113 }
114
115 fn venue(&self) -> Venue {
116 self.core.venue
117 }
118
119 fn oms_type(&self) -> OmsType {
120 self.core.oms_type
121 }
122
123 fn get_account(&self) -> Option<AccountAny> {
124 todo!("implement get_account")
125 }
126
127 fn generate_account_state(
128 &self,
129 _balances: Vec<AccountBalance>,
130 _margins: Vec<MarginBalance>,
131 _reported: bool,
132 _ts_event: UnixNanos,
133 ) -> anyhow::Result<()> {
134 todo!("implement generate_account_state")
135 }
136
137 fn start(&mut self) -> anyhow::Result<()> {
138 todo!("implement start")
139 }
140
141 fn stop(&mut self) -> anyhow::Result<()> {
142 todo!("implement stop")
143 }
144
145 fn submit_order(&self, _cmd: &SubmitOrder) -> anyhow::Result<()> {
146 todo!("implement submit_order")
147 }
148
149 fn submit_order_list(&self, _cmd: &SubmitOrderList) -> anyhow::Result<()> {
150 todo!("implement submit_order_list")
151 }
152
153 fn modify_order(&self, _cmd: &ModifyOrder) -> anyhow::Result<()> {
154 todo!("implement modify_order")
155 }
156
157 fn cancel_order(&self, _cmd: &CancelOrder) -> anyhow::Result<()> {
158 todo!("implement cancel_order")
159 }
160
161 fn cancel_all_orders(&self, _cmd: &CancelAllOrders) -> anyhow::Result<()> {
162 todo!("implement cancel_all_orders")
163 }
164
165 fn batch_cancel_orders(&self, _cmd: &BatchCancelOrders) -> anyhow::Result<()> {
166 todo!("implement batch_cancel_orders")
167 }
168
169 fn query_account(&self, _cmd: &QueryAccount) -> anyhow::Result<()> {
170 todo!("implement query_account")
171 }
172
173 fn query_order(&self, _cmd: &QueryOrder) -> anyhow::Result<()> {
174 todo!("implement query_order")
175 }
176}
177
178#[async_trait(?Send)]
179impl LiveExecutionClient for BlockchainExecutionClient {
180 async fn connect(&mut self) -> anyhow::Result<()> {
181 if self.connected {
182 tracing::warn!("Blockchain execution client already connected");
183 return Ok(());
184 }
185
186 tracing::info!(
187 "Connecting to blockchain execution client on chain {}",
188 self.chain.name
189 );
190
191 self.refresh_wallet_balances().await;
192
193 self.connected = true;
194 tracing::info!(
195 "Blockchain execution client connected on chain {}",
196 self.chain.name
197 );
198 Ok(())
199 }
200
201 async fn disconnect(&mut self) -> anyhow::Result<()> {
202 todo!("implement disconnect")
203 }
204
205 fn get_message_channel(&self) -> tokio::sync::mpsc::UnboundedSender<ExecutionEvent> {
206 get_exec_event_sender()
207 }
208
209 fn get_clock(&self) -> std::cell::Ref<'_, dyn nautilus_common::clock::Clock> {
210 self.core.clock().borrow()
211 }
212
213 async fn generate_order_status_report(
214 &self,
215 _cmd: &GenerateOrderStatusReport,
216 ) -> anyhow::Result<Option<OrderStatusReport>> {
217 todo!("implement generate_order_status_report")
218 }
219
220 async fn generate_order_status_reports(
221 &self,
222 _cmd: &GenerateOrderStatusReport,
223 ) -> anyhow::Result<Vec<OrderStatusReport>> {
224 todo!("implement generate_order_status_reports")
225 }
226
227 async fn generate_fill_reports(
228 &self,
229 _cmd: GenerateFillReports,
230 ) -> anyhow::Result<Vec<FillReport>> {
231 todo!("implement generate_fill_reports")
232 }
233
234 async fn generate_position_status_reports(
235 &self,
236 _cmd: &GeneratePositionReports,
237 ) -> anyhow::Result<Vec<PositionStatusReport>> {
238 todo!("implement generate_position_status_reports")
239 }
240
241 async fn generate_mass_status(
242 &self,
243 _lookback_mins: Option<u64>,
244 ) -> anyhow::Result<Option<ExecutionMassStatus>> {
245 todo!("implement generate_mass_status")
246 }
247}