#[macro_export]
macro_rules! impl_position_viewer {
($struct_name:ident) => {
#[async_trait::async_trait]
impl $crate::traits::position_viewer::PositionViewer for $struct_name {
async fn position_values(
&self,
block_id: Option<alloy::eips::BlockId>,
) -> anyhow::Result<$crate::types::position_values::PositionValues> {
let values = self.position_values_internal(block_id).await?;
Ok(values)
}
fn position_metadata(&self) -> $crate::types::position_metadata::PositionMetadata {
self.position_metadata_internal().clone()
}
#[tracing::instrument(skip(self))]
async fn amounts(
&self,
block_id: Option<alloy::eips::BlockId>,
) -> anyhow::Result<(
uniswap_sdk_core::prelude::CurrencyAmount<uniswap_sdk_core::prelude::Currency>,
uniswap_sdk_core::prelude::CurrencyAmount<uniswap_sdk_core::prelude::Currency>,
)> {
let values = self.position_values(block_id).await?;
Ok((values.amount0, values.amount1))
}
#[tracing::instrument(skip(self))]
async fn amounts_owed(
&self,
block_id: Option<alloy::eips::BlockId>,
) -> anyhow::Result<(
uniswap_sdk_core::prelude::CurrencyAmount<uniswap_sdk_core::prelude::Currency>,
uniswap_sdk_core::prelude::CurrencyAmount<uniswap_sdk_core::prelude::Currency>,
)> {
let values = self.position_values(block_id).await?;
Ok((values.amount_owed0, values.amount_owed1))
}
#[tracing::instrument(skip(self))]
async fn values(
&self,
block_id: Option<alloy::eips::BlockId>,
) -> anyhow::Result<(
uniswap_sdk_core::prelude::CurrencyAmount<uniswap_sdk_core::prelude::Currency>,
uniswap_sdk_core::prelude::CurrencyAmount<uniswap_sdk_core::prelude::Currency>,
)> {
let values = self.position_values(block_id).await?;
let value0 = $crate::common::calculate_position_value(
values.amount0.clone(),
values.amount1.clone(),
Some(values.amount_owed0.clone()),
Some(values.amount_owed1.clone()),
values.price.clone(),
true,
)?;
let value1 = $crate::common::calculate_position_value(
values.amount0.clone(),
values.amount1.clone(),
Some(values.amount_owed0.clone()),
Some(values.amount_owed1.clone()),
values.price.clone(),
false,
)?;
Ok((value0, value1))
}
#[tracing::instrument(skip(self))]
async fn values_without_amounts_owed(
&self,
block_id: Option<alloy::eips::BlockId>,
) -> anyhow::Result<(
uniswap_sdk_core::prelude::CurrencyAmount<uniswap_sdk_core::prelude::Currency>,
uniswap_sdk_core::prelude::CurrencyAmount<uniswap_sdk_core::prelude::Currency>,
)> {
let values = self.position_values(block_id).await?;
let value0 = $crate::common::calculate_position_value(
values.amount0.clone(),
values.amount1.clone(),
None,
None,
values.price.clone(),
true,
)?;
let value1 = $crate::common::calculate_position_value(
values.amount0.clone(),
values.amount1.clone(),
None,
None,
values.price.clone(),
false,
)?;
Ok((value0, value1))
}
}
};
}
#[macro_export]
macro_rules! impl_position_predictor {
($struct_name:ident) => {
impl $crate::traits::position_viewer::PositionPredictor for $struct_name {
#[tracing::instrument(skip(self), fields(tick = ?tick))]
fn predict_amounts(
&self,
tick: alloy::primitives::aliases::I24,
) -> anyhow::Result<(
uniswap_sdk_core::prelude::CurrencyAmount<uniswap_sdk_core::prelude::Currency>,
uniswap_sdk_core::prelude::CurrencyAmount<uniswap_sdk_core::prelude::Currency>,
)> {
let sqrt_price_x96 = self.tick_to_sqrt_price_x96(tick)?;
let (amount0, amount1) = $crate::common::calculate_position_token_amounts(
self.liquidity(),
self.tick_lower(),
self.tick_upper(),
tick,
sqrt_price_x96,
)?;
let (amount0, amount1) = self.to_currency_amounts(amount0, amount1)?;
Ok((amount0, amount1))
}
#[tracing::instrument(skip(self), fields(tick = ?tick))]
fn predict_values_without_amounts_owed(
&self,
tick: alloy::primitives::aliases::I24,
) -> anyhow::Result<(
uniswap_sdk_core::prelude::CurrencyAmount<uniswap_sdk_core::prelude::Currency>,
uniswap_sdk_core::prelude::CurrencyAmount<uniswap_sdk_core::prelude::Currency>,
)> {
let (amount0, amount1) = self.predict_amounts(tick)?;
let price = self.tick_to_price(tick)?;
let value0 = $crate::common::calculate_position_value(
amount0.clone(),
amount1.clone(),
None,
None,
price.clone(),
true,
)?;
let value1 = $crate::common::calculate_position_value(
amount0.clone(),
amount1.clone(),
None,
None,
price,
false,
)?;
Ok((value0, value1))
}
}
};
}
#[macro_export]
macro_rules! impl_multi_position_predictor {
($struct_name:ident) => {
impl $crate::traits::position_viewer::MultiPositionPredictor for $struct_name {
#[tracing::instrument(skip(self), fields(tick = ?tick, token_id = ?token_id))]
fn predict_amounts_for_position(
&self,
tick: alloy::primitives::aliases::I24,
token_id: alloy::primitives::U256,
) -> anyhow::Result<(
uniswap_sdk_core::prelude::CurrencyAmount<uniswap_sdk_core::prelude::Currency>,
uniswap_sdk_core::prelude::CurrencyAmount<uniswap_sdk_core::prelude::Currency>,
)> {
let metadata =
self.position_metadatas.iter().find(|m| m.token_id == token_id).ok_or_else(
|| anyhow::anyhow!("Position with token_id {} not found", token_id),
)?;
let sqrt_price_x96 = self.tick_to_sqrt_price_x96(tick)?;
let (amount0, amount1) = $crate::common::calculate_position_token_amounts(
metadata.liquidity,
metadata.tick_lower,
metadata.tick_upper,
tick,
sqrt_price_x96,
)?;
let (amount0, amount1) = self.to_currency_amounts(amount0, amount1)?;
Ok((amount0, amount1))
}
#[tracing::instrument(skip(self), fields(tick = ?tick, token_id = ?token_id))]
fn predict_values_without_amounts_owed_for_position(
&self,
tick: alloy::primitives::aliases::I24,
token_id: alloy::primitives::U256,
) -> anyhow::Result<(
uniswap_sdk_core::prelude::CurrencyAmount<uniswap_sdk_core::prelude::Currency>,
uniswap_sdk_core::prelude::CurrencyAmount<uniswap_sdk_core::prelude::Currency>,
)> {
let (amount0, amount1) = self.predict_amounts_for_position(tick, token_id)?;
let price = self.tick_to_price(tick)?;
let value0 = $crate::common::calculate_position_value(
amount0.clone(),
amount1.clone(),
None,
None,
price.clone(),
true,
)?;
let value1 = $crate::common::calculate_position_value(
amount0.clone(),
amount1.clone(),
None,
None,
price,
false,
)?;
Ok((value0, value1))
}
#[tracing::instrument(skip(self), fields(tick = ?tick, index = ?index))]
fn predict_amounts_for_index(
&self,
tick: alloy::primitives::aliases::I24,
index: usize,
) -> anyhow::Result<(
uniswap_sdk_core::prelude::CurrencyAmount<uniswap_sdk_core::prelude::Currency>,
uniswap_sdk_core::prelude::CurrencyAmount<uniswap_sdk_core::prelude::Currency>,
)> {
let metadata = self
.position_metadatas
.get(index)
.ok_or_else(|| anyhow::anyhow!("Position at index {} not found", index))?;
let sqrt_price_x96 = self.tick_to_sqrt_price_x96(tick)?;
let (amount0, amount1) = $crate::common::calculate_position_token_amounts(
metadata.liquidity,
metadata.tick_lower,
metadata.tick_upper,
tick,
sqrt_price_x96,
)?;
let (amount0, amount1) = self.to_currency_amounts(amount0, amount1)?;
Ok((amount0, amount1))
}
#[tracing::instrument(skip(self), fields(tick = ?tick, index = ?index))]
fn predict_values_without_amounts_owed_for_index(
&self,
tick: alloy::primitives::aliases::I24,
index: usize,
) -> anyhow::Result<(
uniswap_sdk_core::prelude::CurrencyAmount<uniswap_sdk_core::prelude::Currency>,
uniswap_sdk_core::prelude::CurrencyAmount<uniswap_sdk_core::prelude::Currency>,
)> {
let (amount0, amount1) = self.predict_amounts_for_index(tick, index)?;
let price = self.tick_to_price(tick)?;
let value0 = $crate::common::calculate_position_value(
amount0.clone(),
amount1.clone(),
None,
None,
price.clone(),
true,
)?;
let value1 = $crate::common::calculate_position_value(
amount0.clone(),
amount1.clone(),
None,
None,
price,
false,
)?;
Ok((value0, value1))
}
}
};
}
#[macro_export]
macro_rules! impl_position_viewer_helpers {
($struct_name:ident) => {
impl $struct_name {
pub fn to_currency_amounts(
&self,
token0_amount: alloy::primitives::U256,
token1_amount: alloy::primitives::U256,
) -> anyhow::Result<(
uniswap_sdk_core::prelude::CurrencyAmount<uniswap_sdk_core::prelude::Currency>,
uniswap_sdk_core::prelude::CurrencyAmount<uniswap_sdk_core::prelude::Currency>,
)> {
use uniswap_sdk_core::prelude::ToBig;
let pool_key = self.pool_key();
let token0 = pool_key.token_a.clone();
let token1 = pool_key.token_b.clone();
let token0_amount = uniswap_sdk_core::prelude::CurrencyAmount::from_raw_amount(
token0,
token0_amount.to_big_int(),
)?;
let token1_amount = uniswap_sdk_core::prelude::CurrencyAmount::from_raw_amount(
token1,
token1_amount.to_big_int(),
)?;
Ok((token0_amount, token1_amount))
}
pub fn tick_to_sqrt_price_x96(
&self,
tick: alloy::primitives::aliases::I24,
) -> anyhow::Result<alloy::primitives::aliases::U160> {
let tick_i32 = {
let bytes = tick.to_le_bytes::<3>();
let sign_byte = if bytes[2] & 0x80 != 0 { 0xFF } else { 0x00 };
i32::from_le_bytes([bytes[0], bytes[1], bytes[2], sign_byte])
};
let sqrt_price_x96 = alloy::primitives::aliases::U160::from(
waterpump_evm_amm_math::tick_math::get_sqrt_ratio_at_tick(tick_i32)?,
);
Ok(sqrt_price_x96)
}
pub fn tick_to_price(
&self,
tick: alloy::primitives::aliases::I24,
) -> anyhow::Result<
uniswap_sdk_core::prelude::Price<
uniswap_sdk_core::prelude::Currency,
uniswap_sdk_core::prelude::Currency,
>,
> {
let token_a = self.pool_key().token_a.clone();
let token_b = self.pool_key().token_b.clone();
let token_a_inner = match token_a {
uniswap_sdk_core::prelude::Currency::Token(t) => t,
_ => return Err(anyhow::anyhow!("Token A must be a token")),
};
let token_b_inner = match token_b {
uniswap_sdk_core::prelude::Currency::Token(t) => t,
_ => return Err(anyhow::anyhow!("Token B must be a token")),
};
let price = uniswap_v3_sdk::prelude::tick_to_price(
token_a_inner.into(),
token_b_inner.into(),
tick,
)
.map_err(|e| anyhow::anyhow!("Failed to calculate price from tick: {}", e))?;
Ok(uniswap_sdk_core::prelude::Price::new(
price.base_currency.clone().into(),
price.quote_currency.clone().into(),
price.denominator.clone(),
price.numerator.clone(),
))
}
pub fn price_to_closest_tick(
&self,
price: uniswap_sdk_core::prelude::Price<
uniswap_sdk_core::prelude::Currency,
uniswap_sdk_core::prelude::Currency,
>,
) -> anyhow::Result<alloy::primitives::aliases::I24> {
let token_a_inner = match price.base_currency.clone() {
uniswap_sdk_core::prelude::Currency::Token(t) => t,
_ => return Err(anyhow::anyhow!("Base currency must be a token")),
};
let token_b_inner = match price.quote_currency.clone() {
uniswap_sdk_core::prelude::Currency::Token(t) => t,
_ => return Err(anyhow::anyhow!("Quote currency must be a token")),
};
let tick = uniswap_v3_sdk::prelude::price_to_closest_tick(
&uniswap_sdk_core::prelude::Price::new(
token_a_inner.into(),
token_b_inner.into(),
price.denominator.clone(),
price.numerator.clone(),
),
)
.map_err(|e| anyhow::anyhow!("Failed to calculate price to closest tick: {}", e))?;
Ok(tick)
}
}
};
}
#[macro_export]
macro_rules! impl_multi_position_viewer {
($struct_name:ident) => {
#[async_trait::async_trait]
impl $crate::traits::position_viewer::MultiPositionViewer for $struct_name {
fn position_metadata(
&self,
token_id: alloy::primitives::U256,
) -> $crate::types::position_metadata::PositionMetadata {
self.position_metadatas
.iter()
.find(|m| m.token_id == token_id)
.cloned()
.unwrap_or_else(|| {
$crate::types::position_metadata::PositionMetadata {
token_id,
liquidity: 0,
tick_lower: alloy::primitives::aliases::I24::ZERO,
tick_upper: alloy::primitives::aliases::I24::ZERO,
}
})
}
fn position_metadatas(
&self,
) -> Vec<$crate::types::position_metadata::PositionMetadata> {
self.position_metadatas_internal()
}
#[tracing::instrument(skip(self))]
async fn position_values(
&self,
block_id: Option<alloy::eips::BlockId>,
) -> anyhow::Result<Vec<$crate::types::position_values::PositionValues>> {
self.position_values_internal(block_id).await
}
#[tracing::instrument(skip(self))]
async fn amounts(
&self,
block_id: Option<alloy::eips::BlockId>,
) -> anyhow::Result<
Vec<(
uniswap_sdk_core::prelude::CurrencyAmount<uniswap_sdk_core::prelude::Currency>,
uniswap_sdk_core::prelude::CurrencyAmount<uniswap_sdk_core::prelude::Currency>,
)>,
> {
let position_values = self.position_values(block_id).await?;
let amounts = position_values
.iter()
.map(|position_value| {
(position_value.amount0.clone(), position_value.amount1.clone())
})
.collect::<Vec<(
uniswap_sdk_core::prelude::CurrencyAmount<
uniswap_sdk_core::prelude::Currency,
>,
uniswap_sdk_core::prelude::CurrencyAmount<
uniswap_sdk_core::prelude::Currency,
>,
)>>();
Ok(amounts)
}
#[tracing::instrument(skip(self))]
async fn amounts_owed(
&self,
block_id: Option<alloy::eips::BlockId>,
) -> anyhow::Result<
Vec<(
uniswap_sdk_core::prelude::CurrencyAmount<uniswap_sdk_core::prelude::Currency>,
uniswap_sdk_core::prelude::CurrencyAmount<uniswap_sdk_core::prelude::Currency>,
)>,
> {
let position_values = self.position_values(block_id).await?;
let amounts_owed = position_values
.iter()
.map(|position_value| {
(position_value.amount_owed0.clone(), position_value.amount_owed1.clone())
})
.collect::<Vec<(
uniswap_sdk_core::prelude::CurrencyAmount<
uniswap_sdk_core::prelude::Currency,
>,
uniswap_sdk_core::prelude::CurrencyAmount<
uniswap_sdk_core::prelude::Currency,
>,
)>>();
Ok(amounts_owed)
}
#[tracing::instrument(skip(self))]
async fn values(
&self,
block_id: Option<alloy::eips::BlockId>,
) -> anyhow::Result<
Vec<(
uniswap_sdk_core::prelude::CurrencyAmount<uniswap_sdk_core::prelude::Currency>,
uniswap_sdk_core::prelude::CurrencyAmount<uniswap_sdk_core::prelude::Currency>,
)>,
> {
let position_values = self.position_values(block_id).await?;
let mut values = Vec::with_capacity(position_values.len());
for position_value in &position_values {
let value0 = $crate::common::calculate_position_value(
position_value.amount0.clone(),
position_value.amount1.clone(),
Some(position_value.amount_owed0.clone()),
Some(position_value.amount_owed1.clone()),
position_value.price.clone(),
true,
)?;
let value1 = $crate::common::calculate_position_value(
position_value.amount0.clone(),
position_value.amount1.clone(),
Some(position_value.amount_owed0.clone()),
Some(position_value.amount_owed1.clone()),
position_value.price.clone(),
false,
)?;
values.push((value0, value1));
}
Ok(values)
}
#[tracing::instrument(skip(self))]
async fn values_without_amounts_owed(
&self,
block_id: Option<alloy::eips::BlockId>,
) -> anyhow::Result<
Vec<(
uniswap_sdk_core::prelude::CurrencyAmount<uniswap_sdk_core::prelude::Currency>,
uniswap_sdk_core::prelude::CurrencyAmount<uniswap_sdk_core::prelude::Currency>,
)>,
> {
let position_values = self.position_values(block_id).await?;
let values = position_values
.iter()
.map(|position_value| {
(position_value.amount0.clone(), position_value.amount1.clone())
})
.collect::<Vec<(
uniswap_sdk_core::prelude::CurrencyAmount<
uniswap_sdk_core::prelude::Currency,
>,
uniswap_sdk_core::prelude::CurrencyAmount<
uniswap_sdk_core::prelude::Currency,
>,
)>>();
Ok(values)
}
#[tracing::instrument(skip(self), fields(token_id = ?token_id))]
async fn amounts_for_position(
&self,
token_id: alloy::primitives::U256,
block_id: Option<alloy::eips::BlockId>,
) -> anyhow::Result<(
uniswap_sdk_core::prelude::CurrencyAmount<uniswap_sdk_core::prelude::Currency>,
uniswap_sdk_core::prelude::CurrencyAmount<uniswap_sdk_core::prelude::Currency>,
)> {
let position_values = self.position_values(block_id).await?;
let position_idx = self
.position_metadatas
.iter()
.position(|m| m.token_id == token_id)
.ok_or_else(|| {
anyhow::anyhow!("Position with token_id {} not found", token_id)
})?;
let position_value = position_values.get(position_idx).ok_or_else(|| {
anyhow::anyhow!("Position value not found for token_id {}", token_id)
})?;
Ok((position_value.amount0.clone(), position_value.amount1.clone()))
}
#[tracing::instrument(skip(self), fields(token_id = ?token_id))]
async fn amounts_owed_for_position(
&self,
token_id: alloy::primitives::U256,
block_id: Option<alloy::eips::BlockId>,
) -> anyhow::Result<(
uniswap_sdk_core::prelude::CurrencyAmount<uniswap_sdk_core::prelude::Currency>,
uniswap_sdk_core::prelude::CurrencyAmount<uniswap_sdk_core::prelude::Currency>,
)> {
let position_values = self.position_values(block_id).await?;
let position_idx = self
.position_metadatas
.iter()
.position(|m| m.token_id == token_id)
.ok_or_else(|| {
anyhow::anyhow!("Position with token_id {} not found", token_id)
})?;
let position_value = position_values.get(position_idx).ok_or_else(|| {
anyhow::anyhow!("Position value not found for token_id {}", token_id)
})?;
Ok((position_value.amount_owed0.clone(), position_value.amount_owed1.clone()))
}
#[tracing::instrument(skip(self), fields(token_id = ?token_id))]
async fn values_for_position(
&self,
token_id: alloy::primitives::U256,
block_id: Option<alloy::eips::BlockId>,
) -> anyhow::Result<(
uniswap_sdk_core::prelude::CurrencyAmount<uniswap_sdk_core::prelude::Currency>,
uniswap_sdk_core::prelude::CurrencyAmount<uniswap_sdk_core::prelude::Currency>,
)> {
let position_values = self.position_values(block_id).await?;
let position_idx = self
.position_metadatas
.iter()
.position(|m| m.token_id == token_id)
.ok_or_else(|| {
anyhow::anyhow!("Position with token_id {} not found", token_id)
})?;
let position_value = position_values.get(position_idx).ok_or_else(|| {
anyhow::anyhow!("Position value not found for token_id {}", token_id)
})?;
let value0 = $crate::common::calculate_position_value(
position_value.amount0.clone(),
position_value.amount1.clone(),
Some(position_value.amount_owed0.clone()),
Some(position_value.amount_owed1.clone()),
position_value.price.clone(),
true,
)?;
let value1 = $crate::common::calculate_position_value(
position_value.amount0.clone(),
position_value.amount1.clone(),
Some(position_value.amount_owed0.clone()),
Some(position_value.amount_owed1.clone()),
position_value.price.clone(),
false,
)?;
Ok((value0, value1))
}
#[tracing::instrument(skip(self), fields(token_id = ?token_id))]
async fn values_without_amounts_owed_for_position(
&self,
token_id: alloy::primitives::U256,
block_id: Option<alloy::eips::BlockId>,
) -> anyhow::Result<(
uniswap_sdk_core::prelude::CurrencyAmount<uniswap_sdk_core::prelude::Currency>,
uniswap_sdk_core::prelude::CurrencyAmount<uniswap_sdk_core::prelude::Currency>,
)> {
let position_values = self.position_values(block_id).await?;
let position_idx = self
.position_metadatas
.iter()
.position(|m| m.token_id == token_id)
.ok_or_else(|| {
anyhow::anyhow!("Position with token_id {} not found", token_id)
})?;
let position_value = position_values.get(position_idx).ok_or_else(|| {
anyhow::anyhow!("Position value not found for token_id {}", token_id)
})?;
Ok((position_value.amount0.clone(), position_value.amount1.clone()))
}
}
};
}