eth-prices 0.0.8

A library for fetching Ethereum prices.
Documentation
//! Uniswap v3 quote sources.

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;

/// Quotes spot rates from a Uniswap v3 pool at a given block height.
#[derive(Debug, Clone)]
pub struct UniswapV3Quoter {
    /// Pool contract address.
    pub pool_address: Address,
    /// First token in pool order.
    pub token0: Address,
    /// Second token in pool order.
    pub token1: Address,
}

impl UniswapV3Quoter {
    /// Builds a quoter from a configured pool selector.
    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),
        })
    }
}