pool_sync_mantle/
pool_sync.rs

1//! PoolSync Core Implementation
2//!
3//! This module contains the core functionality for synchronizing pools across different
4//! blockchain networks and protocols. It includes the main `PoolSync` struct and its
5//! associated methods for configuring and executing the synchronization process.
6//!
7use alloy::providers::Provider;
8use alloy::providers::ProviderBuilder;
9use std::collections::HashMap;
10use std::sync::Arc;
11
12use crate::builder::PoolSyncBuilder;
13use crate::cache::{read_cache_file, write_cache_file, PoolCache};
14use crate::chain::Chain;
15use crate::errors::*;
16use crate::pools::*;
17use crate::rpc::Rpc;
18
19/// The main struct for pool synchronization
20pub struct PoolSync {
21    /// Map of pool types to their fetcher implementations
22    pub fetchers: HashMap<PoolType, Arc<dyn PoolFetcher>>,
23    /// The chain to sync on
24    pub chain: Chain,
25    /// The rate limit of the rpc
26    pub rate_limit: u64,
27    /// Optional starting block for synchronization (overrides cache)
28    pub start_block: Option<u64>,
29    /// Optional ending block for synchronization (overrides latest block)
30    pub end_block: Option<u64>,
31}
32
33impl PoolSync {
34    /// Construct a new builder to configure sync parameters
35    pub fn builder() -> PoolSyncBuilder {
36        PoolSyncBuilder::default()
37    }
38
39    /// Synchronizes all added pools for the specified chain
40    pub async fn sync_pools(&self) -> Result<(Vec<Pool>, u64), PoolSyncError> {
41        // load in the dotenv
42        dotenv::dotenv().ok();
43
44        // setup arvhice node provider
45        let archive = Arc::new(
46            ProviderBuilder::new()
47                .network::<alloy::network::AnyNetwork>()
48                .on_http(std::env::var("ARCHIVE").unwrap().parse().unwrap()),
49        );
50
51        // setup full node provider
52        let full = Arc::new(
53            ProviderBuilder::new()
54                .network::<alloy::network::AnyNetwork>()
55                .on_http(std::env::var("FULL").unwrap().parse().unwrap()),
56        );
57
58        // create the cache files
59        std::fs::create_dir_all("cache").unwrap();
60
61        // create all of the caches
62        let mut pool_caches: Vec<PoolCache> = self
63            .fetchers
64            .keys()
65            .map(|pool_type| read_cache_file(pool_type, self.chain).unwrap())
66            .collect();
67
68        let mut fully_synced = false;
69        let mut last_synced_block = 0;
70
71        while !fully_synced {
72            fully_synced = true;
73            
74            // Use custom end_block if specified, otherwise get latest block
75            let end_block = match self.end_block {
76                Some(end_block) => end_block,
77                None => full.get_block_number().await.unwrap(),
78            };
79
80            println!("\n🔄 开始同步轮次 - 目标区块: {}, 上次同步: {}", end_block, last_synced_block);
81            println!("📊 协议状态:");
82            for cache in &pool_caches {
83                println!("  {} - 缓存池数: {}, 上次同步区块: {}", 
84                    cache.pool_type, cache.pools.len(), cache.last_synced_block);
85            }
86            println!("");
87
88            for cache in &mut pool_caches {
89                // Use custom start_block if specified, otherwise use cache
90                let start_block = match self.start_block {
91                    Some(start_block) => {
92                        // 如果指定了自定义起始区块,只有在缓存还没达到这个区块时才使用
93                        if cache.last_synced_block < start_block {
94                            start_block
95                        } else {
96                            cache.last_synced_block + 1
97                        }
98                    },
99                    None => cache.last_synced_block + 1,
100                };
101                
102                if start_block <= end_block {
103                    fully_synced = false;
104                    
105                    println!("🔗 正在同步 {} 协议 (区块 {} → {})", cache.pool_type, start_block, end_block);
106
107                    let fetcher = self.fetchers[&cache.pool_type].clone();
108
109                    // fetch all of the pool addresses
110                    let pool_addrs = Rpc::fetch_pool_addrs(
111                        start_block,
112                        end_block,
113                        archive.clone(),
114                        fetcher.clone(),
115                        self.chain,
116                        self.rate_limit,
117                    )
118                    .await
119                    .expect(
120                        "Failed to fetch pool addresses. Exiting due to having inconclusive state",
121                    );
122
123                    // populate all of the pool data
124                    let mut new_pools = Rpc::populate_pools(
125                        pool_addrs,
126                        full.clone(),
127                        cache.pool_type,
128                        fetcher.clone(),
129                        self.rate_limit,
130                        self.chain,
131                    )
132                    .await
133                    .expect("Failed to sync pool data, Exiting due to haveing inconclusive state");
134
135
136                    // catch up all the old pools
137                    Rpc::populate_liquidity(
138                        start_block,
139                        end_block,
140                        &mut cache.pools,
141                        archive.clone(),
142                        cache.pool_type,
143                        self.rate_limit,
144                        cache.is_initial_sync,
145                    )
146                    .await
147                    .expect("Failed to populate liquidity information, Exiting due to having inconclusive state");
148
149                    // update the new pools
150                    if !new_pools.is_empty() {
151                        Rpc::populate_liquidity(
152                            start_block,
153                            end_block,
154                            &mut new_pools,
155                            archive.clone(),
156                            cache.pool_type,
157                            self.rate_limit,
158                            true,
159                        )
160                        .await
161                        .expect("Failed to populate liquidity information, Exiting due to having inconclusive state");
162                    }
163
164
165                    // merge old and new
166                    let new_pools_count = new_pools.len();
167                    cache.pools.extend(new_pools);
168
169
170                    // update info for cache
171                    cache.last_synced_block = end_block;
172                    last_synced_block = end_block;
173                    cache.is_initial_sync = false;
174                    
175                    println!("✅ {} 协议同步完成 - 总池数: {}, 新增池: {}, 同步至区块: {}", 
176                        cache.pool_type, cache.pools.len(), new_pools_count, end_block);
177                } else {
178                    println!("⏭️  {} 协议已为最新状态 (区块 {})", cache.pool_type, cache.last_synced_block);
179                }
180            }
181            
182            // 如果指定了自定义的end_block,检查是否所有协议都已同步完成
183            if let Some(target_end_block) = self.end_block {
184                let all_synced_to_target = pool_caches.iter().all(|cache| cache.last_synced_block >= target_end_block);
185                if all_synced_to_target {
186                    println!("🎯 所有协议已同步至目标区块 {}, 同步完成!", target_end_block);
187                    break;
188                }
189            }
190        }
191
192        println!("\n🎉 所有协议同步完成! 最终状态:");
193        for cache in &pool_caches {
194            println!("  {} - 总池数: {}, 最新区块: {}", 
195                cache.pool_type, cache.pools.len(), cache.last_synced_block);
196        }
197        println!("💾 正在保存缓存文件...\n");
198
199        // write all of the cache files
200        pool_caches
201            .iter()
202            .for_each(|cache| write_cache_file(cache, self.chain).unwrap());
203
204        // return all the pools
205        Ok((
206            pool_caches
207                .into_iter()
208                .flat_map(|cache| cache.pools)
209                .collect(),
210            last_synced_block,
211        ))
212    }
213}
214