use alloy::{
primitives::{Address, U256, U512},
providers::DynProvider,
};
use pool::UniswapV3Pool;
use crate::{
EthPricesError, Result,
asset::identity::AssetIdentifier,
network::Network,
quoter::{Quoter, RateDirection, uniswap_v3::factory::UniswapV3Selector},
};
pub mod factory;
pub mod pool;
#[derive(Debug, Clone)]
pub struct UniswapV3Quoter {
pub pool_address: Address,
pub token0: Address,
pub token1: Address,
}
impl UniswapV3Quoter {
pub async fn from_selector(
provider: &DynProvider,
selector: UniswapV3Selector,
) -> Result<Self> {
let pool_address = selector.resolve(provider).await?;
let pool = UniswapV3Pool::new(pool_address, provider);
let token0 = pool.token0().call().await?;
let token1 = pool.token1().call().await?;
Ok(Self {
pool_address,
token0,
token1,
})
}
}
#[cfg_attr(target_arch = "wasm32", async_trait::async_trait(?Send))]
#[cfg_attr(not(target_arch = "wasm32"), async_trait::async_trait)]
impl Quoter for UniswapV3Quoter {
fn identity(&self) -> String {
format!("uniswap_v3:{}", self.pool_address)
}
fn tokens(&self) -> (AssetIdentifier, AssetIdentifier) {
(self.token0.into(), self.token1.into())
}
async fn rate(
&self,
amount_in: U256,
direction: RateDirection,
network: &Network,
) -> Result<U256> {
let (_chain_id, block_number, provider) =
network
.as_evm()
.ok_or(EthPricesError::InvalidNetwork(format!(
"Network: {:?}",
network
)))?;
let pool = UniswapV3Pool::new(self.pool_address, provider);
let slot0 = pool
.slot0()
.block(alloy::eips::BlockId::Number(
alloy::eips::BlockNumberOrTag::Number(*block_number),
))
.call()
.await?;
let sqrt_price_x96 = U512::from(slot0.sqrtPriceX96);
let q192 = U512::from(1) << 192;
let sqrt_price_squared = sqrt_price_x96 * sqrt_price_x96;
let price0_in_1_raw = (sqrt_price_squared * U512::from(amount_in)) / q192;
let price1_in_0_raw = (q192 * U512::from(amount_in)) / sqrt_price_squared;
Ok(match direction {
RateDirection::Forward => U256::from(price0_in_1_raw),
RateDirection::Reverse => U256::from(price1_in_0_raw),
})
}
}