use anyhow::Result;
use async_trait::async_trait;
use solana_sdk::pubkey::Pubkey;
use wp_solana_rpc::RpcContext;
use crate::types::{CurrencyAmount, Price};
#[derive(Debug, Clone)]
pub struct PositionMetadata {
pub position_mint: Pubkey,
pub position_address: Pubkey,
pub pool_address: Pubkey,
pub tick_lower: i32,
pub tick_upper: i32,
pub liquidity: u128,
}
#[derive(Debug, Clone)]
pub struct PositionValues {
pub position_mint: Pubkey,
pub amount_a: CurrencyAmount,
pub amount_b: CurrencyAmount,
pub price: Price,
pub fee_owed_a: CurrencyAmount,
pub fee_owed_b: CurrencyAmount,
pub rewards: Vec<RewardValue>,
}
#[derive(Debug, Clone)]
pub struct RewardValue {
pub mint: Pubkey,
pub amount: u64,
}
#[async_trait]
pub trait PositionViewer: Send + Sync {
fn position_metadata(&self) -> PositionMetadata;
async fn position_values(&self, ctx: &RpcContext) -> Result<PositionValues>;
async fn amounts(&self, ctx: &RpcContext) -> Result<(CurrencyAmount, CurrencyAmount)> {
let values = self.position_values(ctx).await?;
Ok((values.amount_a, values.amount_b))
}
async fn amounts_owed(&self, ctx: &RpcContext) -> Result<(CurrencyAmount, CurrencyAmount)> {
let values = self.position_values(ctx).await?;
Ok((values.fee_owed_a, values.fee_owed_b))
}
async fn values(&self, ctx: &RpcContext) -> Result<(CurrencyAmount, CurrencyAmount)> {
let values = self.position_values(ctx).await?;
let total_a = crate::common::price_utils::calculate_position_value(
values.amount_a.clone(),
values.amount_b.clone(),
Some(values.fee_owed_a.clone()),
Some(values.fee_owed_b.clone()),
values.price.clone(),
true,
)?;
let total_b = crate::common::price_utils::calculate_position_value(
values.amount_a,
values.amount_b,
Some(values.fee_owed_a),
Some(values.fee_owed_b),
values.price,
false,
)?;
Ok((total_a, total_b))
}
async fn values_without_amounts_owed(
&self,
ctx: &RpcContext,
) -> Result<(CurrencyAmount, CurrencyAmount)> {
let values = self.position_values(ctx).await?;
let value_a = crate::common::price_utils::calculate_position_value(
values.amount_a.clone(),
values.amount_b.clone(),
None,
None,
values.price.clone(),
true,
)?;
let value_b = crate::common::price_utils::calculate_position_value(
values.amount_a,
values.amount_b,
None,
None,
values.price,
false,
)?;
Ok((value_a, value_b))
}
}
#[async_trait]
pub trait MultiPositionViewer: Send + Sync {
fn position_metadata(&self, position_mint: Pubkey) -> Option<PositionMetadata>;
fn position_metadatas(&self) -> Vec<PositionMetadata>;
fn add_position(&mut self, metadata: PositionMetadata);
async fn add_position_with_mint(
&mut self,
ctx: &RpcContext,
position_mint: Pubkey,
) -> Result<()>;
async fn position_values(&self, ctx: &RpcContext) -> Result<Vec<PositionValues>>;
async fn amounts(&self, ctx: &RpcContext) -> Result<Vec<(CurrencyAmount, CurrencyAmount)>> {
let values = self.position_values(ctx).await?;
Ok(values.into_iter().map(|v| (v.amount_a, v.amount_b)).collect())
}
async fn amounts_owed(
&self,
ctx: &RpcContext,
) -> Result<Vec<(CurrencyAmount, CurrencyAmount)>> {
let values = self.position_values(ctx).await?;
Ok(values.into_iter().map(|v| (v.fee_owed_a, v.fee_owed_b)).collect())
}
async fn values(&self, ctx: &RpcContext) -> Result<Vec<(CurrencyAmount, CurrencyAmount)>> {
let values = self.position_values(ctx).await?;
let mut all_values = Vec::new();
for v in values {
let total_a = crate::common::price_utils::calculate_position_value(
v.amount_a.clone(),
v.amount_b.clone(),
Some(v.fee_owed_a.clone()),
Some(v.fee_owed_b.clone()),
v.price.clone(),
true,
)?;
let total_b = crate::common::price_utils::calculate_position_value(
v.amount_a,
v.amount_b,
Some(v.fee_owed_a),
Some(v.fee_owed_b),
v.price,
false,
)?;
all_values.push((total_a, total_b));
}
Ok(all_values)
}
async fn values_without_amounts_owed(
&self,
ctx: &RpcContext,
) -> Result<Vec<(CurrencyAmount, CurrencyAmount)>> {
let values = self.position_values(ctx).await?;
let mut all_values = Vec::new();
for v in values {
let value_a = crate::common::price_utils::calculate_position_value(
v.amount_a.clone(),
v.amount_b.clone(),
None,
None,
v.price.clone(),
true,
)?;
let value_b = crate::common::price_utils::calculate_position_value(
v.amount_a, v.amount_b, None, None, v.price, false,
)?;
all_values.push((value_a, value_b));
}
Ok(all_values)
}
async fn amounts_for_position(
&self,
ctx: &RpcContext,
position_mint: Pubkey,
) -> Result<(CurrencyAmount, CurrencyAmount)> {
let v = self.position_value_for(ctx, position_mint).await?;
Ok((v.amount_a, v.amount_b))
}
async fn amounts_owed_for_position(
&self,
ctx: &RpcContext,
position_mint: Pubkey,
) -> Result<(CurrencyAmount, CurrencyAmount)> {
let v = self.position_value_for(ctx, position_mint).await?;
Ok((v.fee_owed_a, v.fee_owed_b))
}
async fn values_for_position(
&self,
ctx: &RpcContext,
position_mint: Pubkey,
) -> Result<(CurrencyAmount, CurrencyAmount)> {
let v = self.position_value_for(ctx, position_mint).await?;
let total_a = crate::common::price_utils::calculate_position_value(
v.amount_a.clone(),
v.amount_b.clone(),
Some(v.fee_owed_a.clone()),
Some(v.fee_owed_b.clone()),
v.price.clone(),
true,
)?;
let total_b = crate::common::price_utils::calculate_position_value(
v.amount_a,
v.amount_b,
Some(v.fee_owed_a),
Some(v.fee_owed_b),
v.price,
false,
)?;
Ok((total_a, total_b))
}
async fn values_without_amounts_owed_for_position(
&self,
ctx: &RpcContext,
position_mint: Pubkey,
) -> Result<(CurrencyAmount, CurrencyAmount)> {
let v = self.position_value_for(ctx, position_mint).await?;
let value_a = crate::common::price_utils::calculate_position_value(
v.amount_a.clone(),
v.amount_b.clone(),
None,
None,
v.price.clone(),
true,
)?;
let value_b = crate::common::price_utils::calculate_position_value(
v.amount_a, v.amount_b, None, None, v.price, false,
)?;
Ok((value_a, value_b))
}
async fn position_value_for(
&self,
ctx: &RpcContext,
position_mint: Pubkey,
) -> Result<PositionValues> {
let values = self.position_values(ctx).await?;
values
.into_iter()
.find(|v| v.position_mint == position_mint)
.ok_or_else(|| anyhow::anyhow!("Position {} not found", position_mint))
}
}