use alloy::{
eips::BlockId,
network::Ethereum,
primitives::{aliases::I24, Address, U256},
providers::DynProvider,
};
use anyhow::{Ok, Result};
use tracing::{debug, instrument, trace};
use waterpump_evm_core::pool_key::SlipstreamPoolKey;
use crate::{
common::{calculate_position_token_amounts, calculate_tokens_owed_unrealized},
impl_pool_base, impl_position_predictor, impl_position_viewer, impl_position_viewer_helpers,
impl_slipstream_pool_state,
pool_viewers::slipstream::utils::calculate_price_from_sqrt_price_x96,
types::{position_metadata::PositionMetadata, position_values::PositionValues},
};
#[derive(Clone, Debug)]
pub struct SlipstreamPositionViewer {
pub position_manager_address: Address,
pub pool_address: Address,
pub pool_key: SlipstreamPoolKey,
pub metadata: PositionMetadata,
pub provider: DynProvider<Ethereum>,
}
impl SlipstreamPositionViewer {
#[instrument(skip(pool_key), fields(pool_key = ?pool_key, token_id = ?metadata.token_id, position_manager = ?position_manager_address, tick_lower = ?metadata.tick_lower, tick_upper = ?metadata.tick_upper))]
pub fn new(
position_manager_address: Address,
pool_address: Address,
pool_key: SlipstreamPoolKey,
metadata: PositionMetadata,
provider: DynProvider<Ethereum>,
) -> Self {
debug!(
position_manager = ?position_manager_address,
token_id = ?metadata.token_id,
pool_address = ?pool_address,
tick_lower = ?metadata.tick_lower,
tick_upper = ?metadata.tick_upper,
"Creating SlipstreamPositionViewer"
);
Self { position_manager_address, pool_address, pool_key, metadata, provider }
}
pub async fn with_position_id(
provider: DynProvider<Ethereum>,
position_manager_address: Address,
pool_address: Address,
pool_key: SlipstreamPoolKey,
token_id: U256,
block_id: Option<BlockId>,
) -> Result<Self> {
let position_manager = waterpump_evm_slipstream_client::interfaces::INonfungiblePositionManager::INonfungiblePositionManagerInstance::new(
position_manager_address,
&provider,
);
let mut call_builder = position_manager.positions(token_id);
if let Some(block_id) = block_id {
call_builder = call_builder.block(block_id);
}
let position_data = call_builder.call().await?;
debug!(
liquidity = ?position_data.liquidity,
tick_lower = ?position_data.tickLower,
tick_upper = ?position_data.tickUpper,
"Position data retrieved"
);
Ok(Self {
position_manager_address,
pool_address,
pool_key,
metadata: PositionMetadata {
token_id,
liquidity: position_data.liquidity,
tick_lower: position_data.tickLower,
tick_upper: position_data.tickUpper,
},
provider,
})
}
}
impl SlipstreamPositionViewer {
pub fn pool_key(&self) -> &SlipstreamPoolKey { &self.pool_key }
pub fn pool_address(&self) -> Address { self.pool_address }
pub fn token_id(&self) -> U256 { self.metadata.token_id }
pub fn liquidity(&self) -> u128 { self.metadata.liquidity }
pub fn tick_lower(&self) -> I24 { self.metadata.tick_lower }
pub fn tick_upper(&self) -> I24 { self.metadata.tick_upper }
pub fn tick_range(&self) -> (I24, I24) { (self.metadata.tick_lower, self.metadata.tick_upper) }
pub fn position_metadata_internal(&self) -> PositionMetadata { self.metadata.clone() }
}
impl SlipstreamPositionViewer {
#[instrument(skip(self))]
pub async fn get_position(
&self,
block_id: Option<BlockId>,
) -> Result<
waterpump_evm_slipstream_client::interfaces::INonfungiblePositionManager::positionsReturn,
> {
let position_manager = waterpump_evm_slipstream_client::interfaces::INonfungiblePositionManager::INonfungiblePositionManagerInstance::new(
self.position_manager_address,
self.provider.clone(),
);
let block_id = block_id.unwrap_or(BlockId::latest());
let call_builder = position_manager.positions(self.metadata.token_id).block(block_id);
trace!("Calling positions() on position manager for fees");
let position_data = call_builder.call().await?;
Ok(position_data)
}
#[instrument(skip(self))]
pub async fn position_values_internal(
&self,
block_id: Option<BlockId>,
) -> Result<PositionValues> {
let liquidity = self.liquidity();
let block_id = block_id.unwrap_or(BlockId::latest());
let pool_contract =
waterpump_evm_slipstream_client::interfaces::ICLPool::ICLPoolInstance::new(
self.pool_address,
self.provider.clone(),
);
let position_manager = waterpump_evm_slipstream_client::interfaces::INonfungiblePositionManager::INonfungiblePositionManagerInstance::new(
self.position_manager_address,
self.provider.clone(),
);
let multicall = alloy::providers::Provider::multicall(&self.provider)
.add(position_manager.positions(self.metadata.token_id))
.add(pool_contract.slot0())
.add(pool_contract.feeGrowthGlobal0X128())
.add(pool_contract.feeGrowthGlobal1X128())
.add(pool_contract.ticks(self.metadata.tick_lower))
.add(pool_contract.ticks(self.metadata.tick_upper))
.block(block_id);
let (
position_data,
slot0,
fee_growth_global_0_x128,
fee_growth_global_1_x128,
tick_lower_data,
tick_upper_data,
) = multicall.aggregate().await?;
let (tokens_owed0_unrealized, tokens_owed1_unrealized) = calculate_tokens_owed_unrealized(
slot0.tick,
self.metadata.tick_lower,
self.metadata.tick_upper,
fee_growth_global_0_x128,
fee_growth_global_1_x128,
tick_lower_data.feeGrowthOutside0X128,
tick_lower_data.feeGrowthOutside1X128,
tick_upper_data.feeGrowthOutside0X128,
tick_upper_data.feeGrowthOutside1X128,
position_data.feeGrowthInside0LastX128,
position_data.feeGrowthInside1LastX128,
self.metadata.liquidity,
)?;
let amount_owed0 = tokens_owed0_unrealized + U256::from(position_data.tokensOwed0);
let amount_owed1 = tokens_owed1_unrealized + U256::from(position_data.tokensOwed1);
let (amount_owed0, amount_owed1) = self.to_currency_amounts(amount_owed0, amount_owed1)?;
let (amount0, amount1) = calculate_position_token_amounts(
liquidity,
self.metadata.tick_lower,
self.metadata.tick_upper,
slot0.tick,
slot0.sqrtPriceX96,
)?;
let (amount0, amount1) = self.to_currency_amounts(amount0, amount1)?;
let price = calculate_price_from_sqrt_price_x96(
slot0.sqrtPriceX96,
self.pool_key().token_a.clone(),
self.pool_key().token_b.clone(),
);
Ok(PositionValues { amount0, amount1, amount_owed0, amount_owed1, price })
}
}
impl_pool_base!(SlipstreamPositionViewer);
impl_position_viewer_helpers!(SlipstreamPositionViewer);
impl_position_viewer!(SlipstreamPositionViewer);
impl_position_predictor!(SlipstreamPositionViewer);
impl_slipstream_pool_state!(SlipstreamPositionViewer);