kabu_broadcast_accounts/signers/
signers_component.rs1use eyre::{Result, eyre};
2use std::sync::Arc;
3use std::time::Duration;
4use tokio::sync::{RwLock, broadcast};
5use tracing::{debug, error, info, warn};
6
7use alloy_network::Network;
8use alloy_primitives::{Address, U256};
9use alloy_provider::Provider;
10
11use kabu_core_components::Component;
12use kabu_types_entities::{AccountNonceAndBalanceState, TxSigners};
13use kabu_types_events::{MessageSwapCompose, SwapComposeMessage};
14use kabu_types_swap::Swap;
15use reth_ethereum_primitives::EthPrimitives;
16use reth_node_types::NodePrimitives;
17use reth_tasks::TaskExecutor;
18use revm::DatabaseRef;
19
20pub struct SignersComponent<P, N, DB: Send + Sync + Clone + 'static, LDT: NodePrimitives + 'static = EthPrimitives> {
22 client: P,
24 signers: Arc<RwLock<TxSigners<LDT>>>,
26 account_state: Arc<RwLock<AccountNonceAndBalanceState>>,
28 swap_compose_rx: Option<broadcast::Receiver<MessageSwapCompose<DB, LDT>>>,
30 signed_tx_tx: Option<broadcast::Sender<MessageSwapCompose<DB, LDT>>>,
32 gas_price_buffer: u64,
34 _network: std::marker::PhantomData<N>,
36}
37
38impl<P, N, DB, LDT> SignersComponent<P, N, DB, LDT>
39where
40 P: Provider<N> + Send + Sync + Clone + 'static,
41 N: Network + 'static,
42 DB: DatabaseRef + Send + Sync + Clone + 'static,
43 LDT: NodePrimitives + 'static,
44{
45 pub fn new(
46 client: P,
47 signers: Arc<RwLock<TxSigners<LDT>>>,
48 account_state: Arc<RwLock<AccountNonceAndBalanceState>>,
49 gas_price_buffer: u64,
50 ) -> Self {
51 Self {
52 client,
53 signers,
54 account_state,
55 swap_compose_rx: None,
56 signed_tx_tx: None,
57 gas_price_buffer,
58 _network: std::marker::PhantomData,
59 }
60 }
61
62 pub fn with_channels(
63 mut self,
64 swap_compose_channel: broadcast::Sender<MessageSwapCompose<DB, LDT>>,
65 signed_tx_channel: broadcast::Sender<MessageSwapCompose<DB, LDT>>,
66 ) -> Self {
67 self.swap_compose_rx = Some(swap_compose_channel.subscribe());
68 self.signed_tx_tx = Some(signed_tx_channel);
69 self
70 }
71
72 async fn run(self) -> Result<()> {
73 info!("Starting signers component");
74
75 let signer_count = {
77 let signers = self.signers.read().await;
78 signers.get_address_vec().len()
79 };
80
81 if signer_count == 0 {
82 return Err(eyre!("No signers configured for signing component"));
83 }
84
85 info!("Signers component ready with {} signers", signer_count);
86
87 let mut swap_compose_rx = self.swap_compose_rx;
89 let signed_tx_tx = self.signed_tx_tx;
90 let signers = self.signers;
91 let account_state = self.account_state;
92 let client = self.client;
93 let gas_price_buffer = self.gas_price_buffer;
94
95 loop {
97 tokio::select! {
98 swap_compose_msg = recv_swap_compose_msg(&mut swap_compose_rx) => {
99 if let Some(swap_compose) = swap_compose_msg
100 && let Err(e) = handle_swap_compose_signing(
101 &client,
102 &signers,
103 &account_state,
104 &signed_tx_tx,
105 swap_compose,
106 gas_price_buffer,
107 ).await {
108 error!("Error signing swap compose: {}", e);
109 }
110 }
111 _ = tokio::time::sleep(Duration::from_secs(10)) => {
112 debug!("Signers component heartbeat");
113 }
114 }
115 }
116 }
117}
118
119async fn recv_swap_compose_msg<DB: Send + Sync + Clone + 'static, LDT: NodePrimitives>(
121 swap_compose_rx: &mut Option<broadcast::Receiver<MessageSwapCompose<DB, LDT>>>,
122) -> Option<MessageSwapCompose<DB, LDT>> {
123 if let Some(rx) = swap_compose_rx {
124 match rx.recv().await {
125 Ok(msg) => Some(msg),
126 Err(broadcast::error::RecvError::Lagged(missed)) => {
127 warn!("Signers missed {} swap compose messages", missed);
128 None
129 }
130 Err(broadcast::error::RecvError::Closed) => {
131 error!("Swap compose channel closed");
132 None
133 }
134 }
135 } else {
136 tokio::time::sleep(Duration::from_millis(100)).await;
138 None
139 }
140}
141
142async fn handle_swap_compose_signing<P, N, DB, LDT>(
144 client: &P,
145 signers: &Arc<RwLock<TxSigners<LDT>>>,
146 account_state: &Arc<RwLock<AccountNonceAndBalanceState>>,
147 signed_tx_tx: &Option<broadcast::Sender<MessageSwapCompose<DB, LDT>>>,
148 swap_compose_msg: MessageSwapCompose<DB, LDT>,
149 gas_price_buffer: u64,
150) -> Result<()>
151where
152 P: Provider<N> + Send + Sync + Clone + 'static,
153 N: Network + 'static,
154 DB: DatabaseRef + Send + Sync + Clone + 'static,
155 LDT: NodePrimitives + 'static,
156{
157 let swap_compose_data = match &swap_compose_msg.inner {
159 SwapComposeMessage::Ready(data) => data,
160 _ => {
161 debug!("Ignoring non-ready swap compose message");
162 return Ok(());
163 }
164 };
165
166 let swap = &swap_compose_data.swap;
167
168 let selected_signer = select_signer_for_swap(signers, account_state, swap).await?;
170
171 debug!("Selected signer {} for swap", selected_signer);
172
173 let current_nonce = {
175 let state = account_state.read().await;
176 state.get_account(&selected_signer).map(|acc| acc.get_nonce()).unwrap_or(0)
177 };
178
179 let base_gas_price = match client.get_gas_price().await {
181 Ok(price) => price,
182 Err(e) => {
183 warn!("Failed to fetch gas price, using default: {}", e);
184 20_000_000_000u128 }
186 };
187
188 let buffered_gas_price = base_gas_price + (base_gas_price * gas_price_buffer as u128 / 100);
189
190 let mut updated_compose_data = swap_compose_data.clone();
192
193 updated_compose_data.tx_compose.nonce = current_nonce;
195 updated_compose_data.tx_compose.priority_gas_fee = buffered_gas_price as u64;
196
197 {
199 let mut state = account_state.write().await;
200 if let Some(account_data) = state.get_mut_account(&selected_signer) {
201 account_data.set_nonce(current_nonce + 1);
202 debug!("Updated nonce for {} to {}", selected_signer, current_nonce + 1);
203 }
204 }
205
206 if let Some(tx_sender) = signed_tx_tx {
208 let ready_msg = MessageSwapCompose::ready(updated_compose_data);
209
210 if let Err(e) = tx_sender.send(ready_msg) {
211 warn!("Failed to send ready swap compose: {}", e);
212 } else {
213 debug!("Successfully processed and queued swap compose transaction");
214 }
215 }
216
217 Ok(())
218}
219
220async fn select_signer_for_swap<LDT: NodePrimitives>(
222 signers: &Arc<RwLock<TxSigners<LDT>>>,
223 account_state: &Arc<RwLock<AccountNonceAndBalanceState>>,
224 _swap: &Swap,
225) -> Result<Address> {
226 let signers_guard = signers.read().await;
227 let available_signers = signers_guard.get_address_vec();
228
229 if available_signers.is_empty() {
230 return Err(eyre!("No signers available"));
231 }
232
233 let state = account_state.read().await;
241
242 for &signer in &available_signers {
244 if let Some(account_data) = state.get_account(&signer) {
245 let eth_balance = account_data.get_eth_balance();
246 if eth_balance > U256::from(10_000_000_000_000_000u128) {
248 return Ok(signer);
250 }
251 }
252 }
253
254 Ok(available_signers[0])
257}
258
259impl<P, N, DB, LDT> Component for SignersComponent<P, N, DB, LDT>
260where
261 P: Provider<N> + Send + Sync + Clone + 'static,
262 N: Network + 'static,
263 DB: DatabaseRef + Send + Sync + Clone + 'static,
264 LDT: NodePrimitives + 'static,
265{
266 fn spawn(self, executor: TaskExecutor) -> Result<()> {
267 executor.spawn_critical(self.name(), async move {
268 if let Err(e) = self.run().await {
269 error!("Signers component failed: {}", e);
270 }
271 });
272 Ok(())
273 }
274 fn name(&self) -> &'static str {
275 "SignersComponent"
276 }
277}
278
279pub struct SignersComponentBuilder {
281 gas_price_buffer: u64,
282}
283
284impl SignersComponentBuilder {
285 pub fn new() -> Self {
286 Self {
287 gas_price_buffer: 10, }
289 }
290
291 pub fn with_gas_price_buffer(mut self, buffer_percent: u64) -> Self {
292 self.gas_price_buffer = buffer_percent;
293 self
294 }
295
296 pub fn build<P, N, DB, LDT>(
297 self,
298 client: P,
299 signers: Arc<RwLock<TxSigners<LDT>>>,
300 account_state: Arc<RwLock<AccountNonceAndBalanceState>>,
301 ) -> SignersComponent<P, N, DB, LDT>
302 where
303 P: Provider<N> + Send + Sync + Clone + 'static,
304 N: Network + 'static,
305 DB: DatabaseRef + Send + Sync + Clone + 'static,
306 LDT: NodePrimitives + 'static,
307 {
308 SignersComponent::new(client, signers, account_state, self.gas_price_buffer)
309 }
310}
311
312impl Default for SignersComponentBuilder {
313 fn default() -> Self {
314 Self::new()
315 }
316}