pool_sync_mantle/
rpc.rs

1use alloy::network::Network;
2use alloy::primitives::Address;
3use alloy::providers::Provider;
4use alloy::rpc::types::{Filter, Log};
5use alloy::sol_types::SolEvent;
6use alloy::transports::Transport;
7use anyhow::anyhow;
8use anyhow::Result;
9use futures::StreamExt;
10use indicatif::ProgressBar;
11use log::info;
12use rand::Rng;
13use std::collections::{BTreeMap, HashMap};
14use std::sync::Arc;
15use tokio::sync::{Mutex, Semaphore};
16use tokio::time::{interval, Duration};
17
18use crate::events::*;
19use crate::pools::pool_builder;
20use crate::pools::pool_structures::v3_structure::process_tick_data;
21use crate::pools::PoolFetcher;
22use crate::util::create_progress_bar;
23use crate::{Chain, Pool, PoolInfo, PoolType};
24
25// Retry constants
26const MAX_RETRIES: u32 = 5;
27const INITIAL_BACKOFF: u64 = 1000; // 1 second
28
29// Define event configurations
30#[derive(Debug)]
31struct EventConfig {
32    events: &'static [&'static str],
33    step_size: u64,
34    description: &'static str,
35    requires_initial_sync: bool,
36}
37
38pub struct Rpc;
39impl Rpc {
40    // Fetch all pool addresses for the protocol
41    pub async fn fetch_pool_addrs<P, T, N>(
42        start_block: u64,
43        end_block: u64,
44        provider: Arc<P>,
45        fetcher: Arc<dyn PoolFetcher>,
46        chain: Chain,
47        rate_limit: u64,
48    ) -> Result<Vec<Address>>
49    where
50        P: Provider<T, N> + 'static,
51        T: Transport + Clone + 'static,
52        N: Network,
53    {
54        // fetch all of the logs
55        let filter = Filter::new()
56            .address(fetcher.factory_address(chain))
57            .event(fetcher.pair_created_signature());
58
59        let step_size: u64 = 500;  // 降低步长以适应 RPC 端点限制
60        let num_tasks = (end_block - start_block) / step_size + 1;
61        let pb_info = format!(
62            "  📍 发现 {} 新池地址",
63            fetcher.pool_type()
64        );
65        let progress_bar = Arc::new(create_progress_bar(num_tasks, pb_info));
66
67        // fetch all of the logs
68        let logs = Rpc::fetch_event_logs(
69            start_block,
70            end_block,
71            step_size,  // 使用动态的步长而不是硬编码值
72            provider,
73            rate_limit,
74            progress_bar,
75            filter,
76        )
77        .await?;
78
79        // extract the addresses from the logs
80        let addresses: Vec<Address> = logs
81            .iter()
82            .map(|log| fetcher.log_to_address(&log.inner))
83            .collect();
84        anyhow::Ok(addresses)
85    }
86
87    pub async fn populate_pools<P, T, N>(
88        pool_addrs: Vec<Address>,
89        provider: Arc<P>,
90        pool: PoolType,
91        fetcher: Arc<dyn PoolFetcher>,
92        rate_limit: u64,
93        chain: Chain
94    ) -> Result<Vec<Pool>>
95    where
96        P: Provider<T, N> + 'static,
97        T: Transport + Clone + 'static,
98        N: Network,
99    {
100        // data batch size for contract calls
101        let batch_size = 50; // Standard batch size for V3 pools
102
103        // informational and rate limiting initialization
104        let total_tasks = (pool_addrs.len() + batch_size - 1) / batch_size; // Ceiling division
105        let progress_bar = create_progress_bar(total_tasks as u64, format!("  💾 加载 {} 池数据 ({} 个池)", pool, pool_addrs.len()));
106        let semaphore = Arc::new(Semaphore::new(rate_limit as usize));
107        let interval = Arc::new(tokio::sync::Mutex::new(interval(Duration::from_secs_f64(
108            1.0 / rate_limit as f64,
109        ))));
110
111        // break the addresses up into chunk
112        let addr_chunks: Vec<Vec<Address>> = pool_addrs
113            .chunks(batch_size)
114            .map(|chunk| chunk.to_vec())
115            .collect();
116
117        let mut stream = futures::stream::iter(addr_chunks.into_iter().map(|chunk| {
118            let provider = provider.clone();
119            let sem = semaphore.clone();
120            let pb = progress_bar.clone();
121            let fetcher = fetcher.clone();
122            let interval = interval.clone();
123            let data = fetcher.get_pool_repr();
124
125            async move {
126                let _permit = sem.acquire().await.unwrap();
127                interval.lock().await.tick().await;
128                let mut retry_count = 0;
129                let mut backoff = 1000; // Initial backoff of 1 second
130                loop {
131                    // try building pools from this set of addresses
132                    match pool_builder::build_pools(
133                        &provider,
134                        chunk.clone(),
135                        pool,
136                        data.clone(),
137                        chain
138                    )
139                    .await
140                    {
141                        Ok(populated_pools) if !populated_pools.is_empty() => {
142                            pb.inc(1);
143                            drop(provider);
144                            return anyhow::Ok::<Vec<Pool>>(populated_pools);
145                        }
146                        Err(e) => {
147                            if retry_count >= MAX_RETRIES {
148                                info!("Failed to populate pools data: {}", e);
149                                drop(provider);
150                                return Ok(Vec::new());
151                            }
152                            let jitter = rand::thread_rng().gen_range(0..=100);
153                            let sleep_duration = Duration::from_millis(backoff + jitter);
154                            tokio::time::sleep(sleep_duration).await;
155                            retry_count += 1;
156                            backoff *= 2; // Exponential backoff
157                        }
158                        _ => continue,
159                    }
160                }
161            }
162        }))
163        .buffer_unordered(rate_limit as usize);
164
165        let mut all_pools = Vec::new();
166
167        while let Some(pool_res) = stream.next().await {
168            match pool_res {
169                Ok(pool) => all_pools.extend(pool),
170                Err(e) => return Err(e),
171            }
172        }
173
174        progress_bar.finish_with_message(format!("✅ {} 池数据获取完成", pool));
175        Ok(all_pools)
176    }
177
178    pub async fn populate_liquidity<P, T, N>(
179        start_block: u64,
180        end_block: u64,
181        pools: &mut [Pool],
182        provider: Arc<P>,
183        pool_type: PoolType,
184        rate_limit: u64,
185        is_initial_sync: bool,
186    ) -> anyhow::Result<()>
187    where
188        P: Provider<T, N> + Sync + 'static,
189        T: Transport + Sync + Clone,
190        N: Network,
191    {
192        if pools.is_empty() {
193            return anyhow::Ok(());
194        }
195
196        let address_to_index: HashMap<Address, usize> = pools
197            .iter()
198            .enumerate()
199            .map(|(i, pool)| (pool.address(), i))
200            .collect();
201
202        let batch_size = 1_000_000;
203        let mut current_block = start_block;
204
205        // get the configuration for this sync and config we should sync
206        let config = Rpc::get_event_config(pool_type, is_initial_sync);
207        if is_initial_sync && config.requires_initial_sync {
208            return anyhow::Ok(());
209        }
210
211        // construct the progress bar
212        let num_tasks = (end_block - start_block) / config.step_size + 1;
213        let pb_info = format!(
214            "  🔄 同步 {} 流动性数据 ({} 个池)",
215            pool_type, pools.len()
216        );
217        let progress_bar = Arc::new(create_progress_bar(num_tasks, pb_info));
218
219        // sync in batches
220        while current_block <= end_block {
221            let batch_end = (current_block + batch_size).min(end_block);
222
223            let logs = Rpc::fetch_logs_for_config(
224                &config,
225                current_block,
226                batch_end,
227                provider.clone(),
228                progress_bar.clone(),
229                rate_limit,
230            )
231            .await?;
232
233            // Process logs immediately after fetching (without extra progress bar to reduce noise)
234            let mut ordered_logs: BTreeMap<u64, Vec<Log>> = BTreeMap::new();
235            for log in logs {
236                if let Some(block_number) = log.block_number {
237                    ordered_logs.entry(block_number).or_default().push(log);
238                }
239            }
240
241            // Process logs in order
242            let mut logs_processed = 0;
243            for (_, log_group) in ordered_logs {
244                for log in log_group {
245                    let address = log.address();
246                    if let Some(&index) = address_to_index.get(&address) {
247                        if let Some(pool) = pools.get_mut(index) {
248                            // Only process V3 pools
249                            process_tick_data(
250                                pool.get_v3_mut().unwrap(),
251                                log,
252                                pool_type,
253                                is_initial_sync,
254                            );
255                            logs_processed += 1;
256                        }
257                    }
258                }
259            }
260            
261            // Update the main progress bar
262            progress_bar.inc(1);
263            if logs_processed > 0 {
264                progress_bar.set_message(format!("已处理 {} 个事件", logs_processed));
265            }
266            current_block = batch_end + 1;
267        }
268        
269        progress_bar.finish_with_message(format!("完成"));
270        anyhow::Ok(())
271    }
272
273    pub async fn fetch_event_logs<T, N, P>(
274        start_block: u64,
275        end_block: u64,
276        step_size: u64,
277        provider: Arc<P>,
278        rate_limit: u64,
279        progress_bar: Arc<ProgressBar>,
280        filter: Filter,
281    ) -> anyhow::Result<Vec<Log>>
282    where
283        T: Transport + Clone,
284        N: Network,
285        P: Provider<T, N> + 'static,
286    {
287        // generate the block range for the sync and setup progress bar
288        let block_range = Rpc::get_block_range(step_size, start_block, end_block);
289
290        // semaphore and interval for rate limiting
291        let semaphore = Arc::new(Semaphore::new(rate_limit as usize));
292        let interval = Arc::new(Mutex::new(interval(Duration::from_secs_f64(
293            1.0 / rate_limit as f64,
294        ))));
295
296        // Create a stream of futures
297        let mut stream =
298            futures::stream::iter(block_range.into_iter().map(|(from_block, to_block)| {
299                let provider = provider.clone();
300                let sem = semaphore.clone();
301                let pb = progress_bar.clone();
302                let interval = interval.clone();
303                let filter = filter.clone();
304
305                async move {
306                    let _permit = sem.acquire().await.unwrap();
307                    interval.lock().await.tick().await;
308
309                    let filter = filter.from_block(from_block).to_block(to_block);
310                    let logs = Rpc::get_logs_with_retry(provider, &filter).await;
311                    if logs.is_ok() {
312                        pb.inc(1);
313                    }
314                    logs
315                }
316            }))
317            .buffer_unordered(rate_limit as usize); // Process up to rate_limit tasks concurrently
318
319        let mut all_logs = Vec::new();
320
321        // Process results as they complete
322        while let Some(result) = stream.next().await {
323            match result {
324                Ok(logs) => all_logs.extend(logs),
325                Err(e) => return Err(e),
326            }
327        }
328
329        progress_bar.finish_with_message(format!("完成 ({} 个事件)", all_logs.len()));
330        Ok(all_logs)
331    }
332
333    // Given a config and a range, fetch all the logs for it
334    // This is a top level call which will delegate to individual fetching
335    // functions to get the logs and to ensure retries on failure
336    async fn fetch_logs_for_config<P, T, N>(
337        config: &EventConfig,
338        start_block: u64,
339        end_block: u64,
340        provider: Arc<P>,
341        progress_bar: Arc<ProgressBar>,
342        rate_limit: u64,
343    ) -> Result<Vec<Log>>
344    where
345        P: Provider<T, N> + 'static,
346        T: Transport + Clone + 'static,
347        N: Network,
348    {
349        let filter = Filter::new().events(config.events.iter().copied());
350        Rpc::fetch_event_logs(
351            start_block,
352            end_block,
353            config.step_size,
354            provider,
355            rate_limit,
356            progress_bar,
357            filter,
358        )
359        .await
360    }
361
362    // Fetch logs with retry functionality
363    async fn get_logs_with_retry<P, T, N>(
364        provider: Arc<P>,
365        filter: &Filter,
366    ) -> anyhow::Result<Vec<Log>>
367    where
368        P: Provider<T, N> + 'static,
369        T: Transport + Clone + 'static,
370        N: Network,
371    {
372        let mut retry_count = 0;
373        let mut backoff = INITIAL_BACKOFF;
374
375        loop {
376            match provider.get_logs(filter).await {
377                Ok(logs) => {
378                    return anyhow::Ok(logs);
379                }
380                Err(e) => {
381                    if retry_count >= MAX_RETRIES {
382                        return Err(anyhow!(e));
383                    }
384                    let jitter = rand::thread_rng().gen_range(0..=100);
385                    let sleep_duration = Duration::from_millis(backoff + jitter);
386                    tokio::time::sleep(sleep_duration).await;
387                    retry_count += 1;
388                    backoff *= 2;
389                }
390            }
391        }
392    }
393
394    fn get_event_config(_pool_type: PoolType, is_initial_sync: bool) -> EventConfig {
395        // Only V3 pools are supported now
396        if is_initial_sync {
397            EventConfig {
398                events: &[DataEvents::Mint::SIGNATURE, DataEvents::Burn::SIGNATURE],
399                step_size: 1500,
400                description: "Tick sync",
401                requires_initial_sync: false, // Always fetch these
402            }
403        } else {
404            EventConfig {
405                events: &[
406                    DataEvents::Mint::SIGNATURE,
407                    DataEvents::Burn::SIGNATURE,
408                    DataEvents::Swap::SIGNATURE,
409                ],
410                step_size: 50,
411                description: "Full sync",
412                requires_initial_sync: true, // Always fetch these
413            }
414        }
415    }
416
417    // Generate a range of blocks of step size distance
418    pub fn get_block_range(step_size: u64, start_block: u64, end_block: u64) -> Vec<(u64, u64)> {
419        if start_block == end_block {
420            return vec![(start_block, end_block)];
421        }
422
423        let block_difference = end_block.saturating_sub(start_block);
424        let (_, step_size) = if block_difference < step_size {
425            (1, block_difference)
426        } else {
427            (
428                ((block_difference as f64) / (step_size as f64)).ceil() as u64,
429                step_size,
430            )
431        };
432        let block_ranges: Vec<(u64, u64)> = (start_block..=end_block)
433            .step_by(step_size as usize)
434            .map(|from_block| {
435                let to_block = (from_block + step_size - 1).min(end_block);
436                (from_block, to_block)
437            })
438            .collect();
439        block_ranges
440    }
441}