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, U256};
use crate::solana_lib::spl::token_swap::error::SwapError;
use arrayref::{array_mut_ref, array_ref};
pub fn trading_tokens_to_pool_tokens(
token_b_price: u64,
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 token_b_price = U256::from(token_b_price);
let given_value = match trade_direction {
TradeDirection::AtoB => U256::from(source_amount),
TradeDirection::BtoA => U256::from(source_amount).checked_mul(token_b_price)?,
};
let total_value = U256::from(swap_token_b_amount)
.checked_mul(token_b_price)?
.checked_add(U256::from(swap_token_a_amount))?;
let pool_supply = U256::from(pool_supply);
match round_direction {
RoundDirection::Floor => Some(
pool_supply
.checked_mul(given_value)?
.checked_div(total_value)?
.as_u128(),
),
RoundDirection::Ceiling => Some(
pool_supply
.checked_mul(given_value)?
.checked_ceil_div(total_value)?
.0
.as_u128(),
),
}
}
#[derive(Clone, Debug, Default, PartialEq)]
pub struct ConstantPriceCurve {
pub token_b_price: u64,
}
impl CurveCalculator for ConstantPriceCurve {
fn swap_without_fees(
&self,
source_amount: u128,
_swap_source_amount: u128,
_swap_destination_amount: u128,
trade_direction: TradeDirection,
) -> Option<SwapWithoutFeesResult> {
let token_b_price = self.token_b_price as u128;
let (source_amount_swapped, destination_amount_swapped) = match trade_direction {
TradeDirection::BtoA => (source_amount, source_amount.checked_mul(token_b_price)?),
TradeDirection::AtoB => {
let destination_amount_swapped = source_amount.checked_div(token_b_price)?;
let mut source_amount_swapped = source_amount;
let remainder = source_amount_swapped.checked_rem(token_b_price)?;
if remainder > 0 {
source_amount_swapped = source_amount.checked_sub(remainder)?;
}
(source_amount_swapped, destination_amount_swapped)
}
};
let source_amount_swapped = map_zero_to_none(source_amount_swapped)?;
let destination_amount_swapped = map_zero_to_none(destination_amount_swapped)?;
Some(SwapWithoutFeesResult {
source_amount_swapped,
destination_amount_swapped,
})
}
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> {
let token_b_price = self.token_b_price as u128;
let total_value = self
.normalized_value(swap_token_a_amount, swap_token_b_amount)?
.to_imprecise()?;
let (token_a_amount, token_b_amount) = match round_direction {
RoundDirection::Floor => {
let token_a_amount = pool_tokens
.checked_mul(total_value)?
.checked_div(pool_token_supply)?;
let token_b_amount = pool_tokens
.checked_mul(total_value)?
.checked_div(token_b_price)?
.checked_div(pool_token_supply)?;
(token_a_amount, token_b_amount)
}
RoundDirection::Ceiling => {
let (token_a_amount, _) = pool_tokens
.checked_mul(total_value)?
.checked_ceil_div(pool_token_supply)?;
let (pool_value_as_token_b, _) = pool_tokens
.checked_mul(total_value)?
.checked_ceil_div(token_b_price)?;
let (token_b_amount, _) =
pool_value_as_token_b.checked_ceil_div(pool_token_supply)?;
(token_a_amount, token_b_amount)
}
};
Some(TradingTokenResult {
token_a_amount,
token_b_amount,
})
}
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> {
trading_tokens_to_pool_tokens(
self.token_b_price,
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> {
trading_tokens_to_pool_tokens(
self.token_b_price,
source_amount,
swap_token_a_amount,
swap_token_b_amount,
pool_supply,
trade_direction,
RoundDirection::Ceiling,
)
}
fn validate(&self) -> Result<(), SwapError> {
if self.token_b_price == 0 {
Err(SwapError::InvalidCurve)
} else {
Ok(())
}
}
fn validate_supply(&self, token_a_amount: u64, _token_b_amount: u64) -> Result<(), SwapError> {
if token_a_amount == 0 {
return Err(SwapError::EmptySupply);
}
Ok(())
}
fn normalized_value(
&self,
swap_token_a_amount: u128,
swap_token_b_amount: u128,
) -> Option<PreciseNumber> {
let swap_token_b_value = swap_token_b_amount.checked_mul(self.token_b_price as u128)?;
let value = if swap_token_b_value.saturating_sub(std::u64::MAX.into())
> (std::u128::MAX.saturating_sub(std::u64::MAX.into()))
{
swap_token_b_value
.checked_div(2)?
.checked_add(swap_token_a_amount.checked_div(2)?)?
} else {
swap_token_a_amount
.checked_add(swap_token_b_value)?
.checked_div(2)?
};
PreciseNumber::new(value)
}
}
impl IsInitialized for ConstantPriceCurve {
fn is_initialized(&self) -> bool {
true
}
}
impl Sealed for ConstantPriceCurve {}
impl Pack for ConstantPriceCurve {
const LEN: usize = 8;
fn pack_into_slice(&self, output: &mut [u8]) {
(self as &dyn DynPack).pack_into_slice(output);
}
fn unpack_from_slice(input: &[u8]) -> Result<ConstantPriceCurve, ProgramError> {
let token_b_price = array_ref![input, 0, 8];
Ok(Self {
token_b_price: u64::from_le_bytes(*token_b_price),
})
}
}
impl DynPack for ConstantPriceCurve {
fn pack_into_slice(&self, output: &mut [u8]) {
let token_b_price = array_mut_ref![output, 0, 8];
*token_b_price = self.token_b_price.to_le_bytes();
}
}