use crate::global::mainnet::{
DAI_ADDRESS, MAINNET_UNISWAP_V2_FACTORY_ADDRESS, MAINNET_UNISWAP_V3_FACTORY_ADDRESS,
USDC_ADDRESS, USDT_ADDRESS, WETH_ADDRESS,
};
use crate::tool::address::str_to_h160_1;
use crate::uniswap::abi::{IUniswapV2Factory, IUniswapV3Factory};
use crate::uniswap::types::*;
use crate::uniswap::types::{PoolInfo, TokenPriceInfo};
use crate::uniswap::v2::UniswapV2;
use crate::uniswap::v3::UniswapV3;
use ethers::prelude::*;
use ethers_providers::{Http, Provider};
use std::sync::Arc;
pub struct Price {
provider: Arc<Provider<Http>>,
v2_factory: Address,
v3_factory: Address,
}
impl Price {
pub fn new(provider: Arc<Provider<Http>>) -> Result<Self> {
let v2_factory = MAINNET_UNISWAP_V2_FACTORY_ADDRESS.parse().unwrap();
let v3_factory = MAINNET_UNISWAP_V3_FACTORY_ADDRESS.parse().unwrap();
Ok(Self {
provider: provider,
v2_factory,
v3_factory,
})
}
pub async fn get_token_prices_by_token_address(
&self,
token_address: Address,
) -> Result<TokenPriceInfo> {
let token_info = self.get_token_info(token_address).await?;
let mut price_info = TokenPriceInfo {
token_address,
token_symbol: token_info.symbol.clone(),
token_name: token_info.name.clone(),
decimals: token_info.decimals,
eth_price: None,
usd_price: None,
usdc_price: None,
usdt_price: None,
dai_price: None,
liquidity: U256::zero(),
price_source: "Unknown".to_string(),
last_updated: chrono::Utc::now().timestamp() as u64,
};
if let Ok(eth_price) = self.get_eth_price_with_decimals(&token_info).await {
price_info.eth_price = Some(eth_price);
price_info.price_source = "Uniswap V2".to_string();
} else if let Ok(eth_price) = self.get_eth_price_by_v3(&token_info).await {
price_info.eth_price = Some(eth_price);
price_info.price_source = "Uniswap V3".to_string();
}
if let Ok(usdc_price) = self.get_stablecoin_price(&token_info, "USDC").await {
price_info.usdc_price = Some(usdc_price);
}
if let Ok(usdt_price) = self.get_stablecoin_price(&token_info, "USDT").await {
price_info.usdt_price = Some(usdt_price);
}
if let Ok(dai_price) = self.get_stablecoin_price(&token_info, "DAI").await {
price_info.dai_price = Some(dai_price);
}
price_info.usd_price = self.calc_usd_price(&price_info).await;
Ok(price_info)
}
async fn get_eth_price_with_decimals(&self, token_info: &TokenInfo) -> Result<f64> {
let weth_address = str_to_h160_1(WETH_ADDRESS).unwrap();
let weth_info = self.get_token_info(weth_address).await.unwrap();
let factory = IUniswapV2Factory::new(self.v2_factory, self.provider.clone());
let pair_address = factory
.get_pair(token_info.address, weth_address)
.call()
.await
.map_err(|e| UniswapError::ContractError(format!("Factory error: {}", e)))?;
if pair_address == Address::zero() {
return Err(UniswapError::PoolNotFound);
}
let uniswap_v2 = UniswapV2::new_with_provider(self.provider.clone());
let pool_info = uniswap_v2.get_pool_info(pair_address).await?;
let price = self.calc_price(&pool_info, token_info, &weth_info)?;
Ok(price)
}
fn calc_price(
&self,
pool_info: &PoolInfo,
token_a: &TokenInfo,
token_b: &TokenInfo,
) -> Result<f64> {
let (reserve_a, reserve_b) = if pool_info.token0.address == token_a.address {
(pool_info.reserve0, pool_info.reserve1)
} else if pool_info.token1.address == token_a.address {
(pool_info.reserve1, pool_info.reserve0)
} else {
return Err(UniswapError::PoolNotFound);
};
let reserve_a_decimal = self.to_decimal(reserve_a, token_a.decimals);
let reserve_b_decimal = self.to_decimal(reserve_b, token_b.decimals);
if reserve_a_decimal == 0.0 {
println!("decimal error");
}
let price = reserve_b_decimal / reserve_a_decimal;
Ok(price)
}
fn to_decimal(&self, value: U256, decimals: u8) -> f64 {
let value_str = value.to_string();
let len = value_str.len();
if decimals == 0 {
return value.as_u128() as f64;
}
if len <= decimals as usize {
let mut decimal_str = "0.".to_string();
for _ in 0..(decimals as usize - len) {
decimal_str.push('0');
}
decimal_str.push_str(&value_str);
decimal_str.parse::<f64>().unwrap_or(0.0)
} else {
let split_index = len - decimals as usize;
let integer_part = &value_str[0..split_index];
let fractional_part = &value_str[split_index..];
let mut decimal_str = integer_part.to_string();
decimal_str.push('.');
decimal_str.push_str(fractional_part);
decimal_str.parse::<f64>().unwrap_or(0.0)
}
}
async fn get_eth_price_by_v3(&self, token_info: &TokenInfo) -> Result<f64> {
let weth_address = str_to_h160_1(WETH_ADDRESS).unwrap();
let weth_info = self.get_token_info(weth_address).await?;
let fees = [500, 3000, 10000];
for fee in fees.iter() {
let factory = IUniswapV3Factory::new(self.v3_factory, self.provider.clone());
let pool_address = factory
.get_pool(token_info.address, weth_address, *fee)
.call()
.await
.map_err(|e| UniswapError::ContractError(format!("V3 Factory error: {}", e)))?;
if pool_address != Address::zero() {
let uniswap_v3 = UniswapV3::new_with_provider(self.provider.clone());
if let Ok(price_data) = uniswap_v3
.get_price_by_token0_token1(pool_address, token_info, &weth_info)
.await
{
return Ok(price_data);
}
}
}
Err(UniswapError::PoolNotFound)
}
async fn get_stablecoin_price(&self, token_info: &TokenInfo, stablecoin: &str) -> Result<f64> {
let stablecoin_address: Address;
match stablecoin {
"USDC" => stablecoin_address = str_to_h160_1(USDC_ADDRESS).unwrap(),
"USDT" => stablecoin_address = str_to_h160_1(USDT_ADDRESS).unwrap(),
"DAI" => stablecoin_address = str_to_h160_1(DAI_ADDRESS).unwrap(),
_ => stablecoin_address = str_to_h160_1(USDT_ADDRESS).unwrap(),
}
if token_info.address == stablecoin_address {
return Ok(1.0);
}
let stablecoin_info = self.get_token_info(stablecoin_address).await.unwrap();
let factory = IUniswapV2Factory::new(self.v2_factory, self.provider.clone());
let pair_address = factory
.get_pair(token_info.address, stablecoin_address)
.call()
.await
.map_err(|e| UniswapError::ContractError(format!("Factory error: {}", e)))?;
if pair_address == Address::zero() {
return Err(UniswapError::PoolNotFound);
}
let uniswap_v2 = UniswapV2::new_with_provider(self.provider.clone());
let pool_info = uniswap_v2.get_pool_info(pair_address).await?;
let price = self.calc_price(&pool_info, token_info, &stablecoin_info)?;
Ok(price)
}
async fn calc_usd_price(&self, price_info: &TokenPriceInfo) -> Option<f64> {
if let Some(usdc_price) = price_info.usdc_price {
return Some(usdc_price);
}
if let Some(usdt_price) = price_info.usdt_price {
return Some(usdt_price);
}
if let Some(dai_price) = price_info.dai_price {
return Some(dai_price);
}
if let Some(eth_price) = price_info.eth_price {
if let Ok(eth_usd_price) = self.get_eth_usd_price().await {
return Some(eth_price * eth_usd_price);
}
}
None
}
async fn get_eth_usd_price(&self) -> Result<f64> {
let weth_address = str_to_h160_1(WETH_ADDRESS).unwrap();
let usdc_address = str_to_h160_1(USDC_ADDRESS).unwrap();
let weth_info = self.get_token_info(weth_address).await.unwrap();
let usdc_info = self.get_token_info(usdc_address).await.unwrap();
let factory = IUniswapV2Factory::new(self.v2_factory, self.provider.clone());
let pair_address = factory
.get_pair(weth_address, usdc_address)
.call()
.await
.map_err(|e| UniswapError::ContractError(format!("Factory error: {}", e)))?;
if pair_address == Address::zero() {
return Err(UniswapError::PoolNotFound);
}
let uniswap_v2 = UniswapV2::new_with_provider(self.provider.clone());
let pool_info = uniswap_v2.get_pool_info(pair_address).await?;
let price = self.calc_price(&pool_info, &weth_info, &usdc_info)?;
Ok(price)
}
async fn get_token_info(&self, address: Address) -> Result<TokenInfo> {
let uniswap_v2 = UniswapV2::new_with_provider(self.provider.clone());
uniswap_v2.get_token_info(address).await
}
pub async fn get_multiple_prices_by_token_address_vec(
&self,
token_addresses: Vec<Address>,
) -> Result<Vec<TokenPriceInfo>> {
let mut results = Vec::new();
for address in token_addresses {
match self.get_token_prices_by_token_address(address).await {
Ok(price_info) => results.push(price_info),
Err(e) => {
eprintln!("Failed to get price for {:?}: {}", address, e);
}
}
}
Ok(results)
}
pub async fn search_liquid_pools(&self, token_address: Address) -> Result<Vec<PoolInfo>> {
let mut pools = Vec::new();
let token_info = self.get_token_info(token_address).await?;
let base_tokens = vec![
str_to_h160_1(WETH_ADDRESS).unwrap(),
str_to_h160_1(USDC_ADDRESS).unwrap(),
str_to_h160_1(USDT_ADDRESS).unwrap(),
str_to_h160_1(DAI_ADDRESS).unwrap(),
];
for base_token in base_tokens {
let factory = IUniswapV2Factory::new(self.v2_factory, self.provider.clone());
let pair_address = factory
.get_pair(token_address, base_token)
.call()
.await
.map_err(|e| UniswapError::ContractError(format!("Factory error: {}", e)))?;
if pair_address != Address::zero() {
let uniswap_v2 = UniswapV2::new_with_provider(self.provider.clone());
if let Ok(pool_info) = uniswap_v2.get_pool_info(pair_address).await {
if !pool_info.reserve0.is_zero() && !pool_info.reserve1.is_zero() {
pools.push(pool_info);
}
}
}
}
Ok(pools)
}
}
impl UniswapV2 {
pub fn new_with_provider(provider: Arc<Provider<Http>>) -> Self {
Self { provider }
}
}
impl UniswapV3 {
pub fn new_with_provider(provider: Arc<Provider<Http>>) -> Self {
Self { provider }
}
}