uniswap_sdk/
lib.rs

1pub mod abi;
2pub mod events;
3pub mod global;
4pub mod liquidity;
5pub mod price;
6pub mod tool;
7pub mod types;
8use crate::abi::{IPool, IPoolManager};
9use crate::analyze::FarmAnalyzer;
10use crate::events::{FarmEventListener, UniswapEventListener};
11use crate::farm::FarmService;
12use crate::router::Router;
13use crate::tool::address::str_to_address;
14use crate::tool::cal_price_from_sqrt_price_x96;
15use crate::types::{PairCreatedEvent, SwapEvent, TickInfo, V4PoolInfo, V4PositionInfo};
16use ethers::providers::Provider;
17use ethers::signers::Signer;
18use ethers::types::Bytes;
19use ethers::types::{Address, H256, U256};
20use evm_client::EvmType;
21use evm_sdk::Evm;
22use std::sync::Arc;
23pub mod analyze;
24pub mod factory;
25pub mod farm;
26pub mod oracle;
27pub mod risk;
28pub mod router;
29use evm_sdk::types::EvmError;
30
31/// Service for interacting with Uniswap V2 and V3 protocols
32pub struct UniswapService {
33    evm: Arc<Evm>,
34    router: Router,
35}
36
37impl UniswapService {
38    /// Create a new UniswapService instance
39    pub fn new(evm: Arc<Evm>) -> Self {
40        Self {
41            evm: evm.clone(),
42            router: Router::new(evm),
43        }
44    }
45
46    /// Get farm service instance
47    pub fn farm_service(&self) -> FarmService {
48        FarmService::new(self.evm.clone())
49    }
50
51    /// Get farm analyzer instance
52    pub fn farm_analyzer(&self) -> FarmAnalyzer {
53        FarmAnalyzer::new(self.evm.clone())
54    }
55
56    /// Get farm event listener instance
57    pub fn farm_event_listener(&self) -> FarmEventListener {
58        FarmEventListener::new(self.evm.clone())
59    }
60
61    /// Get the output amounts for a given input amount along a specified path
62    ///
63    /// # Example
64    /// ```
65    /// use ethers::types::{Address, U256};
66    ///
67    /// let amounts = uniswap_service.get_amounts_out(
68    ///     router_address,
69    ///     U256::from(1000000),
70    ///     vec![token_a, token_b]
71    /// ).await?;
72    /// ```
73    pub async fn get_amounts_out(
74        &self,
75        router_address: Address,
76        amount_in: U256,
77        path: Vec<Address>,
78    ) -> Result<Vec<U256>, EvmError> {
79        let router = self.router.v2_router(router_address);
80        router
81            .get_amounts_out(amount_in, path)
82            .call()
83            .await
84            .map_err(|e| EvmError::ContractError(format!("Failed to get amounts out: {}", e)))
85    }
86
87    /// Get the input amounts for a given output amount along a specified path
88    ///
89    /// # Example
90    /// ```
91    /// use ethers::types::{Address, U256};
92    ///
93    /// let amounts = uniswap_service.get_amounts_in(
94    ///     router_address,
95    ///     U256::from(1000000),
96    ///     vec![token_a, token_b]
97    /// ).await?;
98    /// ```
99    pub async fn get_amounts_in(
100        &self,
101        router_address: Address,
102        amount_out: U256,
103        path: Vec<Address>,
104    ) -> Result<Vec<U256>, EvmError> {
105        let router = self.router.v2_router(router_address);
106        router
107            .get_amounts_in(amount_out, path)
108            .call()
109            .await
110            .map_err(|e| EvmError::ContractError(format!("Failed to get amounts in: {}", e)))
111    }
112
113    /// Swap exact tokens for tokens
114    ///
115    /// # Example
116    /// ```
117    /// use ethers::types::{Address, U256};
118    ///
119    /// let tx_hash = uniswap_service.swap_exact_tokens_for_tokens(
120    ///     router_address,
121    ///     U256::from(1000000),
122    ///     U256::from(900000),
123    ///     vec![token_a, token_b],
124    ///     1698765432
125    /// ).await?;
126    /// ```
127    pub async fn swap_exact_tokens_for_tokens(
128        &self,
129        router_address: Address,
130        amount_in: U256,
131        amount_out_min: U256,
132        path: Vec<Address>,
133        deadline: u64,
134    ) -> Result<H256, EvmError> {
135        if self.evm.client.wallet.is_none() {
136            return Err(EvmError::WalletError("No wallet configured".to_string()));
137        }
138        let wallet_address = self.evm.client.wallet.as_ref().unwrap().address();
139        let router = self.router.v2_router(router_address);
140        let tx = router.swap_exact_tokens_for_tokens(
141            amount_in,
142            amount_out_min,
143            path,
144            wallet_address,
145            deadline.into(),
146        );
147        let pending_tx = tx
148            .send()
149            .await
150            .map_err(|e| EvmError::TransactionError(format!("Failed to swap tokens: {}", e)))?;
151        Ok(pending_tx.tx_hash())
152    }
153
154    /// Swap exact ETH for tokens
155    ///
156    /// # Example
157    /// ```
158    /// use ethers::types::{Address, U256};
159    ///
160    /// let tx_hash = uniswap_service.swap_exact_eth_for_tokens(
161    ///     router_address,
162    ///     U256::from(900000),
163    ///     vec![weth_address, token_b],
164    ///     U256::from(1000000000000000000u64),
165    ///     1698765432
166    /// ).await?;
167    /// ```
168    pub async fn swap_exact_eth_for_tokens(
169        &self,
170        router_address: Address,
171        amount_out_min: U256,
172        path: Vec<Address>,
173        value: U256,
174        deadline: u64,
175    ) -> Result<H256, EvmError> {
176        if self.evm.client.wallet.is_none() {
177            return Err(EvmError::WalletError("No wallet configured".to_string()));
178        }
179        let wallet_address = self.evm.client.wallet.as_ref().unwrap().address();
180        let router = self.router.v2_router(router_address);
181        let tx = router
182            .swap_exact_eth_for_tokens(amount_out_min, path, wallet_address, deadline.into())
183            .value(value);
184        let pending_tx = tx.send().await.map_err(|e| {
185            EvmError::TransactionError(format!("Failed to swap ETH for tokens: {}", e))
186        })?;
187        Ok(pending_tx.tx_hash())
188    }
189
190    /// Swap exact tokens for ETH
191    ///
192    /// # Example
193    /// ```
194    /// use ethers::types::{Address, U256};
195    ///
196    /// let tx_hash = uniswap_service.swap_exact_tokens_for_eth(
197    ///     router_address,
198    ///     U256::from(1000000),
199    ///     U256::from(900000000000000000u64),
200    ///     vec![token_a, weth_address],
201    ///     1698765432
202    /// ).await?;
203    /// ```
204    pub async fn swap_exact_tokens_for_eth(
205        &self,
206        router_address: Address,
207        amount_in: U256,
208        amount_out_min: U256,
209        path: Vec<Address>,
210        deadline: u64,
211    ) -> Result<H256, EvmError> {
212        if self.evm.client.wallet.is_none() {
213            return Err(EvmError::WalletError("No wallet configured".to_string()));
214        }
215        let wallet_address = self.evm.client.wallet.as_ref().unwrap().address();
216        let router = self.router.v2_router(router_address);
217        let tx = router.swap_exact_tokens_for_eth(
218            amount_in,
219            amount_out_min,
220            path,
221            wallet_address,
222            deadline.into(),
223        );
224        let pending_tx = tx.send().await.map_err(|e| {
225            EvmError::TransactionError(format!("Failed to swap tokens for ETH: {}", e))
226        })?;
227        Ok(pending_tx.tx_hash())
228    }
229
230    /// Add liquidity to a token pair
231    ///
232    /// # Example
233    /// ```
234    /// use ethers::types::{Address, U256};
235    ///
236    /// let tx_hash = uniswap_service.add_liquidity(
237    ///     router_address,
238    ///     token_a,
239    ///     token_b,
240    ///     U256::from(1000000),
241    ///     U256::from(2000000),
242    ///     U256::from(900000),
243    ///     U256::from(1800000),
244    ///     1698765432
245    /// ).await?;
246    /// ```
247    pub async fn add_liquidity(
248        &self,
249        router_address: Address,
250        token_a: Address,
251        token_b: Address,
252        amount_a_desired: U256,
253        amount_b_desired: U256,
254        amount_a_min: U256,
255        amount_b_min: U256,
256        deadline: u64,
257    ) -> Result<H256, EvmError> {
258        if self.evm.client.wallet.is_none() {
259            return Err(EvmError::WalletError("No wallet configured".to_string()));
260        }
261        let wallet_address = self.evm.client.wallet.as_ref().unwrap().address();
262        let router = self.router.v2_router(router_address);
263        let tx = router.add_liquidity(
264            token_a,
265            token_b,
266            amount_a_desired,
267            amount_b_desired,
268            amount_a_min,
269            amount_b_min,
270            wallet_address,
271            deadline.into(),
272        );
273        let pending_tx = tx
274            .send()
275            .await
276            .map_err(|e| EvmError::TransactionError(format!("Failed to add liquidity: {}", e)))?;
277        Ok(pending_tx.tx_hash())
278    }
279
280    /// Add liquidity with ETH
281    ///
282    /// # Example
283    /// ```
284    /// use ethers::types::{Address, U256};
285    ///
286    /// let tx_hash = uniswap_service.add_liquidity_eth(
287    ///     router_address,
288    ///     token_a,
289    ///     U256::from(1000000),
290    ///     U256::from(900000),
291    ///     U256::from(1000000000000000000u64),
292    ///     U256::from(1000000000000000000u64),
293    ///     1698765432
294    /// ).await?;
295    /// ```
296    pub async fn add_liquidity_eth(
297        &self,
298        router_address: Address,
299        token: Address,
300        amount_token_desired: U256,
301        amount_token_min: U256,
302        amount_eth_min: U256,
303        value: U256,
304        deadline: u64,
305    ) -> Result<H256, EvmError> {
306        if self.evm.client.wallet.is_none() {
307            return Err(EvmError::WalletError("No wallet configured".to_string()));
308        }
309        let wallet_address = self.evm.client.wallet.as_ref().unwrap().address();
310        let router = self.router.v2_router(router_address);
311        let tx = router
312            .add_liquidity_eth(
313                token,
314                amount_token_desired,
315                amount_token_min,
316                amount_eth_min,
317                wallet_address,
318                deadline.into(),
319            )
320            .value(value);
321        let pending_tx = tx.send().await.map_err(|e| {
322            EvmError::TransactionError(format!("Failed to add liquidity with ETH: {}", e))
323        })?;
324        Ok(pending_tx.tx_hash())
325    }
326
327    /// Execute exact input single swap on Uniswap V3
328    ///
329    /// # Example
330    /// ```
331    /// use ethers::types::{Address, U256};
332    ///
333    /// let tx_hash = uniswap_service.exact_input_single(
334    ///     router_address,
335    ///     token_a,
336    ///     token_b,
337    ///     3000,
338    ///     U256::from(1000000),
339    ///     U256::from(900000),
340    ///     U256::from(0),
341    ///     1698765432
342    /// ).await?;
343    /// ```
344    pub async fn exact_input_single(
345        &self,
346        router_address: Address,
347        token_in: Address,
348        token_out: Address,
349        fee: u32,
350        amount_in: U256,
351        amount_out_min: U256,
352        sqrt_price_limit_x96: U256,
353        deadline: u64,
354    ) -> Result<H256, EvmError> {
355        if self.evm.client.wallet.is_none() {
356            return Err(EvmError::WalletError("No wallet configured".to_string()));
357        }
358        let wallet_address = self.evm.client.wallet.as_ref().unwrap().address();
359        let router = self.router.v3_router(router_address);
360        let tx = router.exact_input_single(
361            token_in,
362            token_out,
363            fee,
364            wallet_address,
365            deadline.into(),
366            amount_in,
367            amount_out_min,
368            sqrt_price_limit_x96.into(),
369        );
370        let pending_tx = tx.send().await.map_err(|e| {
371            EvmError::TransactionError(format!("Failed to execute exact input single: {}", e))
372        })?;
373        Ok(pending_tx.tx_hash())
374    }
375
376    /// Execute exact input swap with path on Uniswap V3
377    pub async fn exact_input(
378        &self,
379        router_address: Address,
380        path: Bytes,
381        amount_in: U256,
382        amount_out_min: U256,
383        deadline: u64,
384    ) -> Result<H256, EvmError> {
385        if self.evm.client.wallet.is_none() {
386            return Err(EvmError::WalletError("No wallet configured".to_string()));
387        }
388        let wallet_address = self.evm.client.wallet.as_ref().unwrap().address();
389        let router = self.router.v3_router(router_address);
390        let tx = router.exact_input(
391            path,
392            wallet_address,
393            deadline.into(),
394            amount_in,
395            amount_out_min,
396        );
397        let pending_tx = tx.send().await.map_err(|e| {
398            EvmError::TransactionError(format!("Failed to execute exact input: {}", e))
399        })?;
400        Ok(pending_tx.tx_hash())
401    }
402
403    /// Execute exact output single swap on Uniswap V3
404    pub async fn exact_output_single(
405        &self,
406        router_address: Address,
407        token_in: Address,
408        token_out: Address,
409        fee: u32,
410        amount_out: U256,
411        amount_in_max: U256,
412        sqrt_price_limit_x96: U256,
413        deadline: u64,
414    ) -> Result<H256, EvmError> {
415        if self.evm.client.wallet.is_none() {
416            return Err(EvmError::WalletError("No wallet configured".to_string()));
417        }
418        let wallet_address = self.evm.client.wallet.as_ref().unwrap().address();
419        let router = self.router.v3_router(router_address);
420        let tx = router.exact_output_single(
421            token_in,
422            token_out,
423            fee,
424            wallet_address,
425            deadline.into(),
426            amount_out,
427            amount_in_max,
428            sqrt_price_limit_x96.into(),
429        );
430        let pending_tx = tx.send().await.map_err(|e| {
431            EvmError::TransactionError(format!("Failed to execute exact output single: {}", e))
432        })?;
433        Ok(pending_tx.tx_hash())
434    }
435
436    /// Remove liquidity from a token pair
437    ///
438    /// # Example
439    /// ```
440    /// use ethers::types::{Address, U256};
441    ///
442    /// let tx_hash = uniswap_service.remove_liquidity(
443    ///     router_address,
444    ///     token_a,
445    ///     token_b,
446    ///     U256::from(1000000),
447    ///     U256::from(900000),
448    ///     U256::from(800000),
449    ///     1698765432
450    /// ).await?;
451    /// ```
452    pub async fn remove_liquidity(
453        &self,
454        router_address: Address,
455        token_a: Address,
456        token_b: Address,
457        liquidity: U256,
458        amount_a_min: U256,
459        amount_b_min: U256,
460        deadline: u64,
461    ) -> Result<H256, EvmError> {
462        if self.evm.client.wallet.is_none() {
463            return Err(EvmError::WalletError("No wallet configured".to_string()));
464        }
465        let wallet_address = self.evm.client.wallet.as_ref().unwrap().address();
466        let router = self.router.v2_router(router_address);
467        let tx = router.remove_liquidity(
468            token_a,
469            token_b,
470            liquidity,
471            amount_a_min,
472            amount_b_min,
473            wallet_address,
474            deadline.into(),
475        );
476        let pending_tx = tx.send().await.map_err(|e| {
477            EvmError::TransactionError(format!("Failed to remove liquidity: {}", e))
478        })?;
479        Ok(pending_tx.tx_hash())
480    }
481
482    /// Remove liquidity with ETH
483    ///
484    /// # Example
485    /// ```
486    /// use ethers::types::{Address, U256};
487    ///
488    /// let tx_hash = uniswap_service.remove_liquidity_eth(
489    ///     router_address,
490    ///     token_a,
491    ///     U256::from(1000000),
492    ///     U256::from(900000),
493    ///     U256::from(1000000000000000000u64),
494    ///     1698765432
495    /// ).await?;
496    /// ```
497    pub async fn remove_liquidity_eth(
498        &self,
499        router_address: Address,
500        token: Address,
501        liquidity: U256,
502        amount_token_min: U256,
503        amount_eth_min: U256,
504        deadline: u64,
505    ) -> Result<H256, EvmError> {
506        if self.evm.client.wallet.is_none() {
507            return Err(EvmError::WalletError("No wallet configured".to_string()));
508        }
509        let wallet_address = self.evm.client.wallet.as_ref().unwrap().address();
510        let router = self.router.v2_router(router_address);
511        let tx = router.remove_liquidity_eth(
512            token,
513            liquidity,
514            amount_token_min,
515            amount_eth_min,
516            wallet_address,
517            deadline.into(),
518        );
519        let pending_tx = tx.send().await.map_err(|e| {
520            EvmError::TransactionError(format!("Failed to remove liquidity with ETH: {}", e))
521        })?;
522        Ok(pending_tx.tx_hash())
523    }
524
525    pub fn event_listener(&self) -> UniswapEventListener {
526        UniswapEventListener::new(self.evm.clone())
527    }
528
529    /// Start monitoring swap events for specified pairs
530    ///
531    /// # Example
532    /// ```
533    /// uniswap_service.start_monitoring_swaps(
534    ///     vec![pair_address1, pair_address2],
535    ///     |swap_event| {
536    ///         println!("Swap detected: {:?}", swap_event);
537    ///     }
538    /// ).await?;
539    /// ```
540    pub async fn start_monitoring_swaps(
541        &self,
542        pair_addresses: Vec<Address>,
543        on_swap: impl Fn(SwapEvent) + Send + Sync + 'static,
544    ) -> Result<(), EvmError> {
545        self.event_listener()
546            .start_swap_listener(pair_addresses, on_swap)
547            .await
548    }
549
550    /// Start monitoring new pair creation events
551    ///
552    /// # Example
553    /// ```
554    /// uniswap_service.start_monitoring_new_pairs(
555    ///     factory_address,
556    ///     |pair_created| {
557    ///         println!("New pair created: {:?}", pair_created);
558    ///     }
559    /// ).await?;
560    /// ```
561    pub async fn start_monitoring_new_pairs(
562        &self,
563        factory_address: Address,
564        on_pair_created: impl Fn(PairCreatedEvent) + Send + Sync + 'static,
565    ) -> Result<(), EvmError> {
566        self.event_listener()
567            .start_pair_created_listener(vec![factory_address], on_pair_created)
568            .await
569    }
570}
571
572/// Service for interacting with Uniswap V4 protocol
573pub struct UniswapV4Service {
574    evm: Arc<Evm>,
575}
576
577impl UniswapV4Service {
578    /// Create a new UniswapV4Service instance
579    pub fn new(evm: Arc<Evm>) -> Self {
580        Self { evm: evm }
581    }
582
583    fn pool_manager(
584        &self,
585        manager_address: Address,
586    ) -> IPoolManager<Provider<ethers::providers::Http>> {
587        IPoolManager::new(manager_address, self.evm.client.provider.clone())
588    }
589
590    fn v4_pool(&self, pool_address: Address) -> IPool<Provider<ethers::providers::Http>> {
591        IPool::new(pool_address, self.evm.client.provider.clone())
592    }
593
594    /// Initialize a new V4 pool
595    ///
596    /// # Example
597    /// ```
598    /// use ethers::types::{Address, U256};
599    ///
600    /// let pool_address = v4_service.initialize_pool(
601    ///     manager_address,
602    ///     currency0,
603    ///     currency1,
604    ///     3000,
605    ///     60,
606    ///     hooks_address,
607    ///     U256::from(79228162514264337593543950336u128),
608    ///     vec![]
609    /// ).await?;
610    /// ```
611    pub async fn initialize_pool(
612        &self,
613        manager_address: Address,
614        currency0: Address,
615        currency1: Address,
616        fee: u32,
617        tick_spacing: i32,
618        hooks: Address,
619        sqrt_price_x96: U256,
620        hook_data: Vec<u8>,
621    ) -> Result<Address, EvmError> {
622        if self.evm.client.wallet.is_none() {
623            return Err(EvmError::WalletError("No wallet configured".to_string()));
624        }
625        let pool_manager = self.pool_manager(manager_address);
626        let tx = pool_manager.initialize(
627            currency0,
628            currency1,
629            fee,
630            tick_spacing,
631            hooks,
632            sqrt_price_x96.into(),
633            hook_data.into(),
634        );
635        let pending_tx = tx.send().await.map_err(|e| {
636            EvmError::TransactionError(format!("Failed to initialize V4 pool: {}", e))
637        })?;
638        let receipt = pending_tx.await.map_err(|e| {
639            EvmError::TransactionError(format!("Failed to get transaction receipt: {}", e))
640        })?;
641        if let Some(log) = receipt.unwrap().logs.first() {
642            Ok(log.address)
643        } else {
644            Err(EvmError::ContractError(
645                "Failed to parse pool address from logs".to_string(),
646            ))
647        }
648    }
649
650    /// Execute a swap on V4 pool
651    ///
652    /// # Example
653    /// ```
654    /// let (amount0, amount1) = v4_service.swap(
655    ///     manager_address,
656    ///     currency0,
657    ///     currency1,
658    ///     3000,
659    ///     60,
660    ///     hooks_address,
661    ///     true,
662    ///     1000000,
663    ///     U256::from(0),
664    ///     vec![]
665    /// ).await?;
666    /// ```
667    pub async fn swap(
668        &self,
669        manager_address: Address,
670        currency0: Address,
671        currency1: Address,
672        fee: u32,
673        tick_spacing: i32,
674        hooks: Address,
675        zero_for_one: bool,
676        amount_specified: i128,
677        sqrt_price_limit_x96: U256,
678        hook_data: Vec<u8>,
679    ) -> Result<(i128, i128), EvmError> {
680        if self.evm.client.wallet.is_none() {
681            return Err(EvmError::WalletError("No wallet configured".to_string()));
682        }
683        let pool_manager = self.pool_manager(manager_address);
684        let tx = pool_manager.swap(
685            currency0,
686            currency1,
687            fee,
688            tick_spacing,
689            hooks,
690            zero_for_one,
691            amount_specified.into(),
692            sqrt_price_limit_x96.into(),
693            hook_data.into(),
694        );
695        let result = tx
696            .call()
697            .await
698            .map_err(|e| EvmError::ContractError(format!("Failed to execute V4 swap: {}", e)))?;
699        Ok((result.0.as_i128(), result.1.as_i128()))
700    }
701
702    /// Modify position in V4 pool
703    ///
704    /// # Example
705    /// ```
706    /// let (amount0, amount1) = v4_service.modify_position(
707    ///     manager_address,
708    ///     currency0,
709    ///     currency1,
710    ///     3000,
711    ///     60,
712    ///     hooks_address,
713    ///     -600,
714    ///     600,
715    ///     1000000,
716    ///     vec![]
717    /// ).await?;
718    /// ```
719    pub async fn modify_position(
720        &self,
721        manager_address: Address,
722        currency0: Address,
723        currency1: Address,
724        fee: u32,
725        tick_spacing: i32,
726        hooks: Address,
727        tick_lower: i32,
728        tick_upper: i32,
729        liquidity_delta: i128,
730        hook_data: Vec<u8>,
731    ) -> Result<(i128, i128), EvmError> {
732        if self.evm.client.wallet.is_none() {
733            return Err(EvmError::WalletError("No wallet configured".to_string()));
734        }
735        let pool_manager = self.pool_manager(manager_address);
736        let tx = pool_manager.modify_position(
737            currency0,
738            currency1,
739            fee,
740            tick_spacing,
741            hooks,
742            tick_lower,
743            tick_upper,
744            liquidity_delta.into(),
745            hook_data.into(),
746        );
747        let result = tx
748            .call()
749            .await
750            .map_err(|e| EvmError::ContractError(format!("Failed to modify V4 position: {}", e)))?;
751        Ok((result.0.as_i128(), result.1.as_i128()))
752    }
753
754    /// Get pool address for given parameters
755    ///
756    /// # Example
757    /// ```
758    /// let pool_address = v4_service.get_pool_address(
759    ///     manager_address,
760    ///     currency0,
761    ///     currency1,
762    ///     3000,
763    ///     60,
764    ///     hooks_address
765    /// ).await?;
766    /// ```
767    pub async fn get_pool_address(
768        &self,
769        manager_address: Address,
770        currency0: Address,
771        currency1: Address,
772        fee: u32,
773        tick_spacing: i32,
774        hooks: Address,
775    ) -> Result<Address, EvmError> {
776        let pool_manager = self.pool_manager(manager_address);
777        let pool_address = pool_manager
778            .get_pool(currency0, currency1, fee, tick_spacing, hooks)
779            .call()
780            .await
781            .map_err(|e| EvmError::ContractError(format!("Failed to get pool address: {}", e)))?;
782        Ok(pool_address)
783    }
784
785    /// Get pool information
786    ///
787    /// # Example
788    /// ```
789    /// let pool_info = v4_service.get_pool_info(pool_address).await?;
790    /// println!("Pool liquidity: {}", pool_info.liquidity);
791    /// println!("Current tick: {}", pool_info.current_tick);
792    /// ```
793    pub async fn get_pool_info(&self, pool_address: Address) -> Result<V4PoolInfo, EvmError> {
794        let pool = self.v4_pool(pool_address);
795        let slot0 = pool
796            .slot_0()
797            .call()
798            .await
799            .map_err(|e| EvmError::ContractError(format!("Failed to get pool slot0: {}", e)))?;
800        let liquidity =
801            pool.liquidity().call().await.map_err(|e| {
802                EvmError::ContractError(format!("Failed to get pool liquidity: {}", e))
803            })?;
804        let fee_growth_global0_x128 = pool.fee_growth_global_0x128().call().await.map_err(|e| {
805            EvmError::ContractError(format!("Failed to get fee growth global0: {}", e))
806        })?;
807
808        let fee_growth_global1_x128 = pool.fee_growth_global_1x128().call().await.map_err(|e| {
809            EvmError::ContractError(format!("Failed to get fee growth global1: {}", e))
810        })?;
811        let protocol_fees =
812            pool.protocol_fees().call().await.map_err(|e| {
813                EvmError::ContractError(format!("Failed to get protocol fees: {}", e))
814            })?;
815        Ok(V4PoolInfo {
816            pool_address,
817            currency0: Address::zero(),
818            currency1: Address::zero(),
819            fee: 0,
820            tick_spacing: 0,
821            hooks: Address::zero(),
822            sqrt_price_x96: slot0.0.into(),
823            current_tick: slot0.1,
824            liquidity: liquidity.into(),
825            fee_growth_global0_x128,
826            fee_growth_global1_x128,
827            protocol_fees_token0: protocol_fees.0.into(),
828            protocol_fees_token1: protocol_fees.1.into(),
829        })
830    }
831
832    /// Get current pool price
833    ///
834    /// # Example
835    /// ```
836    /// let price = v4_service.get_pool_price(pool_address).await?;
837    /// println!("Current pool price: {}", price);
838    /// ```
839    pub async fn get_pool_price(&self, pool_address: Address) -> Result<f64, EvmError> {
840        let pool = self.v4_pool(pool_address);
841        let slot0 = pool
842            .slot_0()
843            .call()
844            .await
845            .map_err(|e| EvmError::ContractError(format!("Failed to get pool slot0: {}", e)))?;
846        let price = cal_price_from_sqrt_price_x96(slot0.0.into());
847        Ok(price)
848    }
849
850    /// Donate to a V4 pool
851    pub async fn donate(
852        &self,
853        manager_address: Address,
854        currency0: Address,
855        currency1: Address,
856        fee: u32,
857        tick_spacing: i32,
858        hooks: Address,
859        amount0: U256,
860        amount1: U256,
861        hook_data: Vec<u8>,
862    ) -> Result<(i128, i128), EvmError> {
863        if self.evm.client.wallet.is_none() {
864            return Err(EvmError::WalletError("No wallet configured".to_string()));
865        }
866        let pool_manager = self.pool_manager(manager_address);
867        let tx = pool_manager.donate(
868            currency0,
869            currency1,
870            fee,
871            tick_spacing,
872            hooks,
873            amount0,
874            amount1,
875            hook_data.into(),
876        );
877        let result = tx
878            .call()
879            .await
880            .map_err(|e| EvmError::ContractError(format!("Failed to donate to V4 pool: {}", e)))?;
881
882        Ok((result.0.as_i128(), result.1.as_i128()))
883    }
884
885    /// Settle currency balance
886    pub async fn settle(
887        &self,
888        manager_address: Address,
889        currency: Address,
890    ) -> Result<U256, EvmError> {
891        if self.evm.client.wallet.is_none() {
892            return Err(EvmError::WalletError("No wallet configured".to_string()));
893        }
894        let pool_manager = self.pool_manager(manager_address);
895        let tx = pool_manager.settle(currency);
896        let result = tx
897            .call()
898            .await
899            .map_err(|e| EvmError::ContractError(format!("Failed to settle currency: {}", e)))?;
900        Ok(result)
901    }
902
903    /// Get currency balance for an account
904    pub async fn get_currency_balance(
905        &self,
906        manager_address: Address,
907        account: Address,
908        currency: Address,
909    ) -> Result<U256, EvmError> {
910        let pool_manager = self.pool_manager(manager_address);
911        let balance = U256::zero();
912        todo!();
913        Ok(balance)
914    }
915
916    /// Get position information
917    ///
918    /// # Example
919    /// ```
920    /// let position_info = v4_service.get_position_info(pool_address, position_key).await?;
921    /// println!("Position liquidity: {}", position_info.liquidity);
922    /// ```
923    pub async fn get_position_info(
924        &self,
925        pool_address: Address,
926        position_key: [u8; 32],
927    ) -> Result<V4PositionInfo, EvmError> {
928        let pool = self.v4_pool(pool_address);
929        let position = pool
930            .positions(position_key.into())
931            .call()
932            .await
933            .map_err(|e| EvmError::ContractError(format!("Failed to get position info: {}", e)))?;
934        Ok(V4PositionInfo {
935            position_key: position_key.into(),
936            owner: Address::zero(),
937            liquidity: position.0.into(),
938            fee_growth_inside0_last_x128: position.1,
939            fee_growth_inside1_last_x128: position.2,
940            tokens_owed0: position.3.into(),
941            tokens_owed1: position.4.into(),
942        })
943    }
944
945    /// Get tick information
946    ///
947    /// # Example
948    /// ```
949    /// let tick_info = v4_service.get_tick_info(pool_address, 1200).await?;
950    /// println!("Tick liquidity gross: {}", tick_info.liquidity_gross);
951    /// ```
952    pub async fn get_tick_info(
953        &self,
954        pool_address: Address,
955        tick: i32,
956    ) -> Result<TickInfo, EvmError> {
957        let pool = self.v4_pool(pool_address);
958        let tick_info = pool
959            .ticks(tick)
960            .call()
961            .await
962            .map_err(|e| EvmError::ContractError(format!("Failed to get tick info: {}", e)))?;
963        Ok(TickInfo {
964            liquidity_gross: tick_info.0.into(),
965            liquidity_net: tick_info.1 as i128,
966            fee_growth_outside0_x128: tick_info.2,
967            fee_growth_outside1_x128: tick_info.3,
968            tick_cumulative_outside: tick_info.4 as i64,
969            seconds_per_liquidity_outside_x128: tick_info.5,
970            seconds_outside: tick_info.6,
971            initialized: tick_info.7,
972        })
973    }
974}
975
976pub struct UniswapConfig;
977
978impl UniswapConfig {
979    pub fn v2_router_address(chain: EvmType) -> Result<Address, EvmError> {
980        match chain {
981            EvmType::ETHEREUM_MAINNET => Ok(str_to_address(
982                crate::global::ethereum::mainnet::dex::uniswap::ROUTER_V2_ADDRESS,
983            )
984            .unwrap()),
985            EvmType::ARB_MAINNET => Ok(str_to_address(
986                crate::global::arb::mainnet::dex::uniswap::ROUTER_V2_ADDRESS,
987            )
988            .unwrap()),
989            EvmType::BSC_MAINNET => Ok(str_to_address(
990                crate::global::bsc::mainnet::dex::uniswap::ROUTER_V2_ADDRESS,
991            )
992            .unwrap()),
993            EvmType::BASE_MAINNET => Ok(str_to_address(
994                crate::global::base::mainnet::dex::uniswap::ROUTER_V2_ADDRESS,
995            )
996            .unwrap()),
997            _ => Err(EvmError::ConfigError(
998                "Unsupported chain for Uniswap V2".to_string(),
999            )),
1000        }
1001    }
1002
1003    pub fn v2_factory_address(chain: EvmType) -> Result<Address, EvmError> {
1004        match chain {
1005            EvmType::ETHEREUM_MAINNET => {
1006                Ok(str_to_address("0x5C69bEe701ef814a2B6a3EDD4B1652CB9cc5aA6f").unwrap())
1007            }
1008            EvmType::ARB_MAINNET => {
1009                Ok(str_to_address("0xc35DADB65012eC5796536bD9864eD8773aBc74C4").unwrap())
1010            }
1011            EvmType::BSC_MAINNET => {
1012                Ok(str_to_address("0xcA143Ce32Fe78f1f7019d7d551a6402fC5350c73").unwrap())
1013            }
1014            EvmType::BASE_MAINNET => {
1015                Ok(str_to_address("0x8909Dc15e40173Ff4699343b6eB8132c65e18eC6").unwrap())
1016            }
1017            _ => Err(EvmError::ConfigError(
1018                "Unsupported chain for Uniswap V2".to_string(),
1019            )),
1020        }
1021    }
1022
1023    pub fn v3_router_address(chain: EvmType) -> Result<Address, EvmError> {
1024        match chain {
1025            EvmType::ETHEREUM_MAINNET => {
1026                Ok(str_to_address("0xE592427A0AEce92De3Edee1F18E0157C05861564").unwrap())
1027            }
1028            EvmType::ARB_MAINNET => {
1029                Ok(str_to_address("0xE592427A0AEce92De3Edee1F18E0157C05861564").unwrap())
1030            }
1031            EvmType::BSC_MAINNET => {
1032                Ok(str_to_address("0xB971eF87ede563556b2ED4b1C0b0019111Dd85d2").unwrap())
1033            }
1034            EvmType::BASE_MAINNET => {
1035                Ok(str_to_address("0x2626664c2603336E57B271c5C0b26F421741e481").unwrap())
1036            }
1037            _ => Err(EvmError::ConfigError(
1038                "Unsupported chain for Uniswap V3".to_string(),
1039            )),
1040        }
1041    }
1042}