Skip to content

Instantly share code, notes, and snippets.

@mubarizkyc
Created September 20, 2025 06:54
Show Gist options
  • Save mubarizkyc/e563ffcebfc5d15a6e4b8801a86dbe79 to your computer and use it in GitHub Desktop.
Save mubarizkyc/e563ffcebfc5d15a6e4b8801a86dbe79 to your computer and use it in GitHub Desktop.
Meteora Dlmm Quote Math
use super::bin_array::{Bin, BinArray};
use super::lb_pair::{LbPair, LbPairExtension};
use crate::commons::WSOL_MINT;
use arrayvec::ArrayVec;
use pinocchio::{
account_info::AccountInfo, msg, program_error::ProgramError, pubkey::Pubkey, sysvars::Sysvar,
sysvars::clock::Clock,
};
use spl_token_2022::{
self,
extension::{
BaseStateWithExtensions, StateWithExtensions,
transfer_fee::{MAX_FEE_BASIS_POINTS, TransferFee, TransferFeeConfig},
},
};
pub const MAX_EXPONENTIAL: u32 = 0x80000; // 1048576
pub const MAX_FEE_RATE: u64 = 100_000_000;
pub const ONE: u128 = 1u128 << SCALE_OFFSET;
pub const BASIS_POINT_MAX: i32 = 10000;
pub const SCALE_OFFSET: u8 = 64;
pub const FEE_PRECISION: u64 = 1_000_000_000;
pub const MAX_BIN_PER_ARRAY: usize = 70;
pub const NUM_REWARDS: usize = 2;
pub fn get_epoch_transfer_fee(
mint_info: &AccountInfo,
epoch: u64,
) -> Result<Option<TransferFee>, ProgramError> {
let token_mint_data = mint_info.try_borrow_data()?;
let token_mint_unpacked = StateWithExtensions::<spl_token_2022::state::Mint>::unpack(
&token_mint_data,
)
.map_err(|err| {
msg!("failed to unpack tocken mint");
ProgramError::InvalidAccountData
})?;
if let Ok(transfer_fee_config) = token_mint_unpacked.get_extension::<TransferFeeConfig>() {
return Ok(Some(*transfer_fee_config.get_epoch_fee(epoch)));
}
Ok(None)
}
pub fn calculate_transfer_fee_excluded_amount(
mint_account: &AccountInfo,
transfer_fee_included_amount: u64,
epoch: u64,
) -> Result<u64, ProgramError> {
if let Some(epoch_transfer_fee) = get_epoch_transfer_fee(mint_account, epoch)? {
let transfer_fee = epoch_transfer_fee
.calculate_fee(transfer_fee_included_amount)
.ok_or(ProgramError::ArithmeticOverflow)?;
let transfer_fee_excluded_amount = transfer_fee_included_amount
.checked_sub(transfer_fee)
.ok_or(ProgramError::ArithmeticOverflow)?;
return Ok(transfer_fee_excluded_amount);
}
Ok(transfer_fee_included_amount)
}
const ONE_IN_BASIS_POINTS: u128 = MAX_FEE_BASIS_POINTS as u128;
pub fn calculate_pre_fee_amount(transfer_fee: &TransferFee, post_fee_amount: u64) -> Option<u64> {
if post_fee_amount == 0 {
return Some(0);
}
let maximum_fee = u64::from(transfer_fee.maximum_fee);
let transfer_fee_basis_points = u16::from(transfer_fee.transfer_fee_basis_points) as u128;
if transfer_fee_basis_points == 0 {
Some(post_fee_amount)
} else if transfer_fee_basis_points == ONE_IN_BASIS_POINTS {
Some(maximum_fee.checked_add(post_fee_amount)?)
} else {
let numerator = (post_fee_amount as u128).checked_mul(ONE_IN_BASIS_POINTS)?;
let denominator = ONE_IN_BASIS_POINTS.checked_sub(transfer_fee_basis_points)?;
let raw_pre_fee_amount = numerator
.checked_add(denominator)?
.checked_sub(1)?
.checked_div(denominator)?;
if raw_pre_fee_amount.checked_sub(post_fee_amount as u128)? >= maximum_fee as u128 {
post_fee_amount.checked_add(maximum_fee)
} else {
u64::try_from(raw_pre_fee_amount).ok()
}
}
}
pub fn calculate_inverse_fee(transfer_fee: &TransferFee, post_fee_amount: u64) -> Option<u64> {
let pre_fee_amount = calculate_pre_fee_amount(transfer_fee, post_fee_amount)?;
transfer_fee.calculate_fee(pre_fee_amount)
}
pub fn calculate_transfer_fee_included_amount(
mint_account: &AccountInfo,
transfer_fee_excluded_amount: u64,
epoch: u64,
) -> Result<u64, ProgramError> {
if transfer_fee_excluded_amount == 0 {
return Ok(0);
}
if let Some(epoch_transfer_fee) = get_epoch_transfer_fee(mint_account, epoch)? {
let transfer_fee: u64 =
if u16::from(epoch_transfer_fee.transfer_fee_basis_points) == MAX_FEE_BASIS_POINTS {
u64::from(epoch_transfer_fee.maximum_fee)
} else {
calculate_inverse_fee(&epoch_transfer_fee, transfer_fee_excluded_amount)
.ok_or(ProgramError::ArithmeticOverflow)?
};
let transfer_fee_included_amount = transfer_fee_excluded_amount
.checked_add(transfer_fee)
.ok_or(ProgramError::ArithmeticOverflow)?;
return Ok(transfer_fee_included_amount);
}
Ok(transfer_fee_excluded_amount)
}pub fn swap_quote(
accounts: &[AccountInfo],
offset: usize,
sol_amount: u64,
) -> Result<(u64, u64), ProgramError> {
let lb_pair_data = accounts[offset + 3].try_borrow_data()?;
let mut lb_pair: LbPair = *bytemuck::from_bytes(&lb_pair_data[8..]);
let mut clock = Clock::get()?;
let mut epoch = clock.epoch;
let swap_for_y = *accounts[offset + 1].key() == WSOL_MINT;
let (in_idx, out_idx) = if swap_for_y { (1, 6) } else { (6, 1) };
lb_pair.update_references(clock.unix_timestamp)?;
let (in_mint_account, out_mint_account) = (&accounts[in_idx], &accounts[out_idx]);
// Collect bin arrays
let mut bin_arrays: ArrayVec<&AccountInfo, 3> = ArrayVec::new();
let bin_range = if swap_for_y {
[offset + 7, offset + 8, offset + 9]
} else {
[offset + 9, offset + 8, offset + 7]
};
for i in bin_range {
if i < accounts.len() && accounts[i].lamports() > 0 {
bin_arrays.push(&accounts[i]);
}
}
// === Forward: SOL → Token (Exact In) ===
let amount_in = calculate_transfer_fee_excluded_amount(in_mint_account, sol_amount, epoch)?;
let mut amount_left = amount_in;
let mut total_amount_out = 0u64;
for acc in &bin_arrays {
let data = acc.try_borrow_data()?;
let active_bin_array: &BinArray = bytemuck::from_bytes(&data[8..]);
while amount_left > 0 && active_bin_array.is_bin_id_within_range(lb_pair.active_id)? {
lb_pair.update_volatility_accumulator()?;
let mut active_bin = active_bin_array.get_bin_by_id(lb_pair.active_id)?;
let price = active_bin.get_or_store_bin_price(lb_pair.active_id, lb_pair.bin_step)?;
if !active_bin.is_empty(!swap_for_y) {
let (amount_in_with_fees, amount_out) =
active_bin.swap(amount_left, price, swap_for_y, &lb_pair)?;
amount_left = amount_left
.checked_sub(amount_in_with_fees)
.ok_or(ProgramError::ArithmeticOverflow)?;
total_amount_out = total_amount_out
.checked_add(amount_out)
.ok_or(ProgramError::ArithmeticOverflow)?;
}
if amount_left > 0 {
lb_pair.advance_active_bin(swap_for_y)?;
} else {
break;
}
}
if amount_left == 0 {
break;
}
}
let amount_out =
calculate_transfer_fee_excluded_amount(out_mint_account, total_amount_out, epoch)?;
// === Reverse: Token → SOL (Exact Out) ===
let mut lb_pair: LbPair = *bytemuck::from_bytes(&lb_pair_data[8..]);
lb_pair.update_references(clock.unix_timestamp)?;
let swap_for_y: bool = !swap_for_y;
let (in_mint_account, out_mint_account) = (&accounts[out_idx], &accounts[in_idx]); //revert accounts
let mut total_amount_in = 0u64;
let mut total_fee = 0u64;
let mut sol_amount_remaining = sol_amount; // SOL has no transfer fee
bin_arrays.reverse();
sol_amount_remaining =
calculate_transfer_fee_included_amount(out_mint_account, sol_amount_remaining, epoch)?;
for acc in &bin_arrays {
let data = acc.try_borrow_data()?;
let active_bin_array: &BinArray = bytemuck::from_bytes(&data[8..]);
while sol_amount_remaining > 0
&& active_bin_array.is_bin_id_within_range(lb_pair.active_id)?
{
lb_pair.update_volatility_accumulator()?;
let mut active_bin = active_bin_array.get_bin_by_id(lb_pair.active_id)?;
let price = active_bin.get_or_store_bin_price(lb_pair.active_id, lb_pair.bin_step)?;
if !active_bin.is_empty(!swap_for_y) {
let max_out = active_bin.get_max_amount_out(swap_for_y);
if sol_amount_remaining >= max_out {
let max_in = active_bin.get_max_amount_in(price, swap_for_y)?;
let fee = lb_pair.compute_fee(max_in)?;
total_amount_in = total_amount_in
.checked_add(max_in)
.ok_or(ProgramError::ArithmeticOverflow)?;
total_fee = total_fee
.checked_add(fee)
.ok_or(ProgramError::ArithmeticOverflow)?;
sol_amount_remaining = sol_amount_remaining
.checked_sub(max_out)
.ok_or(ProgramError::ArithmeticOverflow)?;
} else {
let amount_in = Bin::get_amount_in(sol_amount_remaining, price, swap_for_y)?;
let fee = lb_pair.compute_fee(amount_in)?;
total_amount_in = total_amount_in
.checked_add(amount_in)
.ok_or(ProgramError::ArithmeticOverflow)?;
total_fee = total_fee
.checked_add(fee)
.ok_or(ProgramError::ArithmeticOverflow)?;
sol_amount_remaining = 0;
}
}
if sol_amount_remaining > 0 {
lb_pair.advance_active_bin(swap_for_y)?;
}
}
if sol_amount_remaining == 0 {
break;
}
}
total_amount_in = total_amount_in
.checked_add(total_fee)
.ok_or(ProgramError::ArithmeticOverflow)?;
let total_amount_in =
calculate_transfer_fee_included_amount(in_mint_account, total_amount_in, epoch)?;
Ok((amount_out, total_amount_in))
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment