use crate::instruction::utils::bonk::accounts;
use super::common::clamp_slippage_basis_points_u128;
pub fn get_buy_token_amount_from_sol_amount(
amount_in: u64,
virtual_base: u128,
virtual_quote: u128,
real_base: u128,
real_quote: u128,
slippage_basis_points: u128,
) -> u64 {
let amount_in_u128 = amount_in as u128;
let bps = clamp_slippage_basis_points_u128(slippage_basis_points);
let protocol_fee = (amount_in_u128 * accounts::PROTOCOL_FEE_RATE / 10000) as u128;
let platform_fee = (amount_in_u128 * accounts::PLATFORM_FEE_RATE / 10000) as u128;
let share_fee = (amount_in_u128 * accounts::SHARE_FEE_RATE / 10000) as u128;
let amount_in_net = amount_in_u128
.checked_sub(protocol_fee)
.unwrap()
.checked_sub(platform_fee)
.unwrap()
.checked_sub(share_fee)
.unwrap();
let input_reserve = virtual_quote.checked_add(real_quote).unwrap();
let output_reserve = virtual_base.checked_sub(real_base).unwrap();
let numerator = amount_in_net.checked_mul(output_reserve).unwrap();
let denominator = input_reserve.checked_add(amount_in_net).unwrap();
let mut amount_out = numerator.checked_div(denominator).unwrap();
amount_out = amount_out - (amount_out * bps) / 10000;
amount_out as u64
}
pub fn get_sell_sol_amount_from_token_amount(
amount_in: u64,
virtual_base: u128,
virtual_quote: u128,
real_base: u128,
real_quote: u128,
slippage_basis_points: u128,
) -> u64 {
let amount_in_u128 = amount_in as u128;
let bps = clamp_slippage_basis_points_u128(slippage_basis_points);
let input_reserve = virtual_base.checked_sub(real_base).unwrap();
let output_reserve = virtual_quote.checked_add(real_quote).unwrap();
let numerator = amount_in_u128.checked_mul(output_reserve).unwrap();
let denominator = input_reserve.checked_add(amount_in_u128).unwrap();
let sol_amount_out = numerator.checked_div(denominator).unwrap();
let protocol_fee = (sol_amount_out * accounts::PROTOCOL_FEE_RATE / 10000) as u128;
let platform_fee = (sol_amount_out * accounts::PLATFORM_FEE_RATE / 10000) as u128;
let share_fee = (sol_amount_out * accounts::SHARE_FEE_RATE / 10000) as u128;
let sol_amount_net = sol_amount_out
.checked_sub(protocol_fee)
.unwrap()
.checked_sub(platform_fee)
.unwrap()
.checked_sub(share_fee)
.unwrap();
let final_amount = sol_amount_net - (sol_amount_net * bps) / 10000;
final_amount as u64
}
#[cfg(test)]
mod tests {
use super::super::common::MAX_SLIPPAGE_BASIS_POINTS;
use super::*;
const VIRTUAL_BASE: u128 = 1_073_025_605_596_382;
const VIRTUAL_QUOTE: u128 = 30_000_852_951;
#[test]
fn buy_slippage_at_10000_bps_does_not_zero_min_out() {
let with_max = get_buy_token_amount_from_sol_amount(
1_000_000,
VIRTUAL_BASE,
VIRTUAL_QUOTE,
0,
0,
10_000,
);
let with_clamp_cap = get_buy_token_amount_from_sol_amount(
1_000_000,
VIRTUAL_BASE,
VIRTUAL_QUOTE,
0,
0,
MAX_SLIPPAGE_BASIS_POINTS as u128,
);
assert_eq!(with_max, with_clamp_cap);
assert!(with_max > 0);
}
#[test]
fn sell_slippage_above_10000_bps_does_not_underflow() {
let with_overflow = get_sell_sol_amount_from_token_amount(
1_000_000_000,
VIRTUAL_BASE,
VIRTUAL_QUOTE,
0,
0,
50_000,
);
let with_clamp_cap = get_sell_sol_amount_from_token_amount(
1_000_000_000,
VIRTUAL_BASE,
VIRTUAL_QUOTE,
0,
0,
MAX_SLIPPAGE_BASIS_POINTS as u128,
);
assert_eq!(with_overflow, with_clamp_cap);
assert!(with_overflow > 0);
}
#[test]
fn normal_slippage_matches_uncapped_formula() {
const BPS: u128 = 100;
let buy_no_slip =
get_buy_token_amount_from_sol_amount(1_000_000, VIRTUAL_BASE, VIRTUAL_QUOTE, 0, 0, 0);
let buy_with_slip =
get_buy_token_amount_from_sol_amount(1_000_000, VIRTUAL_BASE, VIRTUAL_QUOTE, 0, 0, BPS);
let expected_buy = buy_no_slip - ((buy_no_slip as u128 * BPS) / 10_000) as u64;
assert_eq!(buy_with_slip, expected_buy);
assert!(buy_with_slip < buy_no_slip);
let sell_no_slip = get_sell_sol_amount_from_token_amount(
1_000_000_000,
VIRTUAL_BASE,
VIRTUAL_QUOTE,
0,
0,
0,
);
let sell_with_slip = get_sell_sol_amount_from_token_amount(
1_000_000_000,
VIRTUAL_BASE,
VIRTUAL_QUOTE,
0,
0,
BPS,
);
let expected_sell = sell_no_slip - ((sell_no_slip as u128 * BPS) / 10_000) as u64;
assert_eq!(sell_with_slip, expected_sell);
assert!(sell_with_slip < sell_no_slip);
}
}