use crate::solana_lib::solana_program::program_error::ProgramError;
use crate::solana_lib::solana_program::program_pack::{IsInitialized, Pack, Sealed};
use crate::solana_lib::spl::token_swap::curve::calculator::{
map_zero_to_none, CurveCalculator, DynPack, RoundDirection, SwapWithoutFeesResult,
TradeDirection, TradingTokenResult,
};
use crate::solana_lib::spl::token_swap::curve::math::{CheckedCeilDiv, PreciseNumber};
use crate::solana_lib::spl::token_swap::error::SwapError;
#[derive(Clone, Debug, Default, PartialEq)]
pub struct ConstantProductCurve;
pub fn swap(
source_amount: u128,
swap_source_amount: u128,
swap_destination_amount: u128,
) -> Option<SwapWithoutFeesResult> {
let invariant = swap_source_amount.checked_mul(swap_destination_amount)?;
let new_swap_source_amount = swap_source_amount.checked_add(source_amount)?;
let (new_swap_destination_amount, new_swap_source_amount) =
invariant.checked_ceil_div(new_swap_source_amount)?;
let source_amount_swapped = new_swap_source_amount.checked_sub(swap_source_amount)?;
let destination_amount_swapped =
map_zero_to_none(swap_destination_amount.checked_sub(new_swap_destination_amount)?)?;
Some(SwapWithoutFeesResult {
source_amount_swapped,
destination_amount_swapped,
})
}
pub fn pool_tokens_to_trading_tokens(
pool_tokens: u128,
pool_token_supply: u128,
swap_token_a_amount: u128,
swap_token_b_amount: u128,
round_direction: RoundDirection,
) -> Option<TradingTokenResult> {
let mut token_a_amount = pool_tokens
.checked_mul(swap_token_a_amount)?
.checked_div(pool_token_supply)?;
let mut token_b_amount = pool_tokens
.checked_mul(swap_token_b_amount)?
.checked_div(pool_token_supply)?;
let (token_a_amount, token_b_amount) = match round_direction {
RoundDirection::Floor => (token_a_amount, token_b_amount),
RoundDirection::Ceiling => {
let token_a_remainder = pool_tokens
.checked_mul(swap_token_a_amount)?
.checked_rem(pool_token_supply)?;
if token_a_remainder > 0 && token_a_amount > 0 {
token_a_amount += 1;
}
let token_b_remainder = pool_tokens
.checked_mul(swap_token_b_amount)?
.checked_rem(pool_token_supply)?;
if token_b_remainder > 0 && token_b_amount > 0 {
token_b_amount += 1;
}
(token_a_amount, token_b_amount)
}
};
Some(TradingTokenResult {
token_a_amount,
token_b_amount,
})
}
pub fn deposit_single_token_type(
source_amount: u128,
swap_token_a_amount: u128,
swap_token_b_amount: u128,
pool_supply: u128,
trade_direction: TradeDirection,
round_direction: RoundDirection,
) -> Option<u128> {
let swap_source_amount = match trade_direction {
TradeDirection::AtoB => swap_token_a_amount,
TradeDirection::BtoA => swap_token_b_amount,
};
let swap_source_amount = PreciseNumber::new(swap_source_amount)?;
let source_amount = PreciseNumber::new(source_amount)?;
let ratio = source_amount.checked_div(&swap_source_amount)?;
let one = PreciseNumber::new(1)?;
let base = one.checked_add(&ratio)?;
let root = base.sqrt()?.checked_sub(&one)?;
let pool_supply = PreciseNumber::new(pool_supply)?;
let pool_tokens = pool_supply.checked_mul(&root)?;
match round_direction {
RoundDirection::Floor => pool_tokens.floor()?.to_imprecise(),
RoundDirection::Ceiling => pool_tokens.ceiling()?.to_imprecise(),
}
}
pub fn withdraw_single_token_type_exact_out(
source_amount: u128,
swap_token_a_amount: u128,
swap_token_b_amount: u128,
pool_supply: u128,
trade_direction: TradeDirection,
round_direction: RoundDirection,
) -> Option<u128> {
let swap_source_amount = match trade_direction {
TradeDirection::AtoB => swap_token_a_amount,
TradeDirection::BtoA => swap_token_b_amount,
};
let swap_source_amount = PreciseNumber::new(swap_source_amount)?;
let source_amount = PreciseNumber::new(source_amount)?;
let ratio = source_amount.checked_div(&swap_source_amount)?;
let one = PreciseNumber::new(1)?;
let base = one.checked_sub(&ratio)?;
let root = one.checked_sub(&base.sqrt()?)?;
let pool_supply = PreciseNumber::new(pool_supply)?;
let pool_tokens = pool_supply.checked_mul(&root)?;
match round_direction {
RoundDirection::Floor => pool_tokens.floor()?.to_imprecise(),
RoundDirection::Ceiling => pool_tokens.ceiling()?.to_imprecise(),
}
}
pub fn normalized_value(
swap_token_a_amount: u128,
swap_token_b_amount: u128,
) -> Option<PreciseNumber> {
let swap_token_a_amount = PreciseNumber::new(swap_token_a_amount)?;
let swap_token_b_amount = PreciseNumber::new(swap_token_b_amount)?;
swap_token_a_amount
.checked_mul(&swap_token_b_amount)?
.sqrt()
}
impl CurveCalculator for ConstantProductCurve {
fn swap_without_fees(
&self,
source_amount: u128,
swap_source_amount: u128,
swap_destination_amount: u128,
_trade_direction: TradeDirection,
) -> Option<SwapWithoutFeesResult> {
swap(source_amount, swap_source_amount, swap_destination_amount)
}
fn pool_tokens_to_trading_tokens(
&self,
pool_tokens: u128,
pool_token_supply: u128,
swap_token_a_amount: u128,
swap_token_b_amount: u128,
round_direction: RoundDirection,
) -> Option<TradingTokenResult> {
pool_tokens_to_trading_tokens(
pool_tokens,
pool_token_supply,
swap_token_a_amount,
swap_token_b_amount,
round_direction,
)
}
fn deposit_single_token_type(
&self,
source_amount: u128,
swap_token_a_amount: u128,
swap_token_b_amount: u128,
pool_supply: u128,
trade_direction: TradeDirection,
) -> Option<u128> {
deposit_single_token_type(
source_amount,
swap_token_a_amount,
swap_token_b_amount,
pool_supply,
trade_direction,
RoundDirection::Floor,
)
}
fn withdraw_single_token_type_exact_out(
&self,
source_amount: u128,
swap_token_a_amount: u128,
swap_token_b_amount: u128,
pool_supply: u128,
trade_direction: TradeDirection,
) -> Option<u128> {
withdraw_single_token_type_exact_out(
source_amount,
swap_token_a_amount,
swap_token_b_amount,
pool_supply,
trade_direction,
RoundDirection::Ceiling,
)
}
fn normalized_value(
&self,
swap_token_a_amount: u128,
swap_token_b_amount: u128,
) -> Option<PreciseNumber> {
normalized_value(swap_token_a_amount, swap_token_b_amount)
}
fn validate(&self) -> Result<(), SwapError> {
Ok(())
}
}
impl IsInitialized for ConstantProductCurve {
fn is_initialized(&self) -> bool {
true
}
}
impl Sealed for ConstantProductCurve {}
impl Pack for ConstantProductCurve {
const LEN: usize = 0;
fn pack_into_slice(&self, output: &mut [u8]) {
(self as &dyn DynPack).pack_into_slice(output);
}
fn unpack_from_slice(_input: &[u8]) -> Result<ConstantProductCurve, ProgramError> {
Ok(Self {})
}
}
impl DynPack for ConstantProductCurve {
fn pack_into_slice(&self, _output: &mut [u8]) {}
}