1use alloy_network::Network;
2use alloy_provider::Provider;
3use eyre::Result;
4use std::sync::Arc;
5use tokio::sync::broadcast;
6use tracing::{debug, error, info};
7
8use kabu_core_components::Component;
9use kabu_types_events::LoomTask;
10use kabu_types_market::{PoolClass, PoolId, PoolLoaders};
11use reth_ethereum_primitives::EthPrimitives;
12use reth_tasks::TaskExecutor;
13
14#[derive(Clone)]
16pub struct HistoryPoolLoaderComponent<P, PL, N>
17where
18 N: Network,
19 P: Provider<N> + Send + Sync + Clone + 'static,
20 PL: Provider<N> + Send + Sync + Clone + 'static,
21{
22 client: P,
24 #[allow(dead_code)]
26 pool_loaders: Arc<PoolLoaders<PL, N, EthPrimitives>>,
27 tasks_tx: Option<broadcast::Sender<LoomTask>>,
29 start_block: u64,
31 block_batch_size: u64,
33 max_batches: u64,
35}
36
37impl<P, PL, N> HistoryPoolLoaderComponent<P, PL, N>
38where
39 N: Network + 'static,
40 P: Provider<N> + Send + Sync + Clone + 'static,
41 PL: Provider<N> + Send + Sync + Clone + 'static,
42{
43 pub fn new(
44 client: P,
45 pool_loaders: Arc<PoolLoaders<PL, N, EthPrimitives>>,
46 start_block: u64,
47 block_batch_size: u64,
48 max_batches: u64,
49 ) -> Self {
50 Self { client, pool_loaders, tasks_tx: None, start_block, block_batch_size, max_batches }
51 }
52
53 pub fn with_task_channel(mut self, tasks_tx: broadcast::Sender<LoomTask>) -> Self {
54 self.tasks_tx = Some(tasks_tx);
55 self
56 }
57
58 async fn run(self) -> Result<()> {
59 info!("Starting history pool loader component");
60
61 let mut current_block = match self.client.get_block_number().await {
62 Ok(block_num) => {
63 if self.start_block > 0 {
64 self.start_block
65 } else {
66 block_num.saturating_sub(1000) }
68 }
69 Err(e) => {
70 error!("Failed to get current block number: {}", e);
71 return Err(e.into());
72 }
73 };
74
75 info!(
76 "History pool loader starting from block {}, batch size: {}, max batches: {}",
77 current_block, self.block_batch_size, self.max_batches
78 );
79
80 for batch in 0..self.max_batches {
81 let from_block = current_block;
82 let to_block = current_block + self.block_batch_size - 1;
83
84 match self.process_block_range(from_block, to_block).await {
85 Ok(tasks_sent) => {
86 debug!(
87 "Batch {}/{}: Processed blocks {}-{}, sent {} tasks",
88 batch + 1,
89 self.max_batches,
90 from_block,
91 to_block,
92 tasks_sent
93 );
94 }
95 Err(e) => {
96 error!("Error processing blocks {}-{}: {}", from_block, to_block, e);
97 }
99 }
100
101 current_block += self.block_batch_size;
102
103 tokio::time::sleep(tokio::time::Duration::from_millis(100)).await;
105 }
106
107 info!("History pool loader component completed");
108 Ok(())
109 }
110
111 async fn process_block_range(&self, from_block: u64, to_block: u64) -> Result<usize> {
112 if let Some(ref tasks_tx) = self.tasks_tx {
116 let example_pools = vec![
119 (PoolId::Address(alloy_primitives::Address::repeat_byte((from_block % 256) as u8)), PoolClass::UniswapV2),
120 (PoolId::Address(alloy_primitives::Address::repeat_byte(((from_block + 1) % 256) as u8)), PoolClass::UniswapV3),
121 ];
122
123 let task = LoomTask::FetchAndAddPools(example_pools);
124
125 if let Err(e) = tasks_tx.send(task) {
126 error!("Failed to send pool discovery task: {}", e);
127 return Err(e.into());
128 }
129
130 debug!("Sent pool discovery task for blocks {}-{}", from_block, to_block);
131
132 Ok(1)
134 } else {
135 debug!("No task channel configured, skipping block range {}-{}", from_block, to_block);
136 Ok(0)
137 }
138 }
139}
140
141impl<P, PL, N> Component for HistoryPoolLoaderComponent<P, PL, N>
142where
143 N: Network + 'static,
144 P: Provider<N> + Send + Sync + Clone + 'static,
145 PL: Provider<N> + Send + Sync + Clone + 'static,
146{
147 fn spawn(self, executor: TaskExecutor) -> Result<()> {
148 executor.spawn_critical(self.name(), async move {
149 if let Err(e) = self.run().await {
150 error!("History pool loader component failed: {}", e);
151 }
152 });
153 Ok(())
154 }
155 fn name(&self) -> &'static str {
156 "HistoryPoolLoaderComponent"
157 }
158}
159
160pub struct HistoryPoolLoaderComponentBuilder {
162 start_block: u64,
163 block_batch_size: u64,
164 max_batches: u64,
165}
166
167impl HistoryPoolLoaderComponentBuilder {
168 pub fn new() -> Self {
169 Self {
170 start_block: 0, block_batch_size: 5,
172 max_batches: 10000,
173 }
174 }
175
176 pub fn with_start_block(mut self, start_block: u64) -> Self {
177 self.start_block = start_block;
178 self
179 }
180
181 pub fn with_batch_size(mut self, batch_size: u64) -> Self {
182 self.block_batch_size = batch_size;
183 self
184 }
185
186 pub fn with_max_batches(mut self, max_batches: u64) -> Self {
187 self.max_batches = max_batches;
188 self
189 }
190
191 pub fn build<P, PL, N>(self, client: P, pool_loaders: Arc<PoolLoaders<PL, N, EthPrimitives>>) -> HistoryPoolLoaderComponent<P, PL, N>
192 where
193 N: Network + 'static,
194 P: Provider<N> + Send + Sync + Clone + 'static,
195 PL: Provider<N> + Send + Sync + Clone + 'static,
196 {
197 HistoryPoolLoaderComponent::new(client, pool_loaders, self.start_block, self.block_batch_size, self.max_batches)
198 }
199}
200
201impl Default for HistoryPoolLoaderComponentBuilder {
202 fn default() -> Self {
203 Self::new()
204 }
205}