use crate::{
math::{casting::Cast, safe_math::SafeMath, tick::TickMath},
programs::vaults::accounts::Tick,
};
use super::{
constants::{FOUR_DECIMALS, X30},
errors::ErrorCodes,
VaultProgram,
};
use anchor_lang::prelude::AccountMeta;
use anyhow::Result;
pub const MIN_TICK: i32 = -16383;
pub const MAX_TICK: i32 = 16383;
pub const TICK_HAS_DEBT_ARRAY_SIZE: usize = 8;
pub const TICK_HAS_DEBT_CHILDREN_SIZE: usize = 32; pub const BIT_PER_BYTE: usize = 8;
pub const TICK_HAS_DEBT_CHILDREN_SIZE_IN_BITS: usize = TICK_HAS_DEBT_CHILDREN_SIZE * BIT_PER_BYTE;
pub const TICKS_PER_TICK_HAS_DEBT: usize =
TICK_HAS_DEBT_ARRAY_SIZE * TICK_HAS_DEBT_CHILDREN_SIZE_IN_BITS;
pub const TOTAL_INDICES_NEEDED: usize = 16;
pub fn get_array_index_for_tick(tick: i32) -> Result<u8> {
if tick < MIN_TICK || tick > MAX_TICK {
return Err(ErrorCodes::VaultTickHasDebtOutOfRange.into());
}
let tick_offset = tick.safe_sub(MIN_TICK)?;
let index = (tick_offset / TICKS_PER_TICK_HAS_DEBT as i32) as u8;
Ok(index)
}
pub fn get_tick_indices_for_array(tick: i32, array_index: u8) -> Result<(u8, u8, u8)> {
if tick < MIN_TICK || tick > MAX_TICK {
return Err(ErrorCodes::VaultTickHasDebtOutOfRange.into());
}
let expected_index = get_array_index_for_tick(tick)?;
if expected_index != array_index {
return Err(ErrorCodes::VaultTickHasDebtIndexMismatch.into());
}
let first_tick_for_index = get_first_tick_for_array_index(array_index)?;
let tick_within_array = tick.safe_sub(first_tick_for_index)?;
if tick_within_array >= TICKS_PER_TICK_HAS_DEBT as i32 {
return Err(ErrorCodes::VaultTickHasDebtOutOfRange.into());
}
let map_index =
(tick_within_array / TICK_HAS_DEBT_CHILDREN_SIZE_IN_BITS as i32).cast::<u8>()?;
let tick_within_map = tick_within_array % TICK_HAS_DEBT_CHILDREN_SIZE_IN_BITS as i32;
let byte_index = (tick_within_map / BIT_PER_BYTE as i32).cast::<u8>()?;
let bit_index = (tick_within_map % BIT_PER_BYTE as i32).cast::<u8>()?;
Ok((map_index, byte_index, bit_index))
}
pub fn get_first_tick_for_array_index(index: u8) -> Result<i32> {
if index >= TOTAL_INDICES_NEEDED as u8 {
return Err(ErrorCodes::VaultTickHasDebtOutOfRange.into());
}
Ok(MIN_TICK.safe_add((index as i32).safe_mul(TICKS_PER_TICK_HAS_DEBT as i32)?)?)
}
pub fn get_tick_indices(tick: i32) -> Result<(u8, usize, usize, usize)> {
let array_index = get_array_index_for_tick(tick)?;
let (map_index, byte_index, bit_index) = get_tick_indices_for_array(tick, array_index)?;
Ok((
array_index,
map_index.cast()?,
byte_index.cast()?,
bit_index.cast()?,
))
}
pub fn get_next_ref_tick(
minima_tick: i32,
next_tick: i32,
liquidation_tick: i32,
) -> Result<(i32, u8)> {
if minima_tick > next_tick && minima_tick > liquidation_tick {
Ok((minima_tick, 2))
} else if next_tick > liquidation_tick {
Ok((next_tick, 1))
} else {
Ok((liquidation_tick, 3))
}
}
pub fn get_current_partials_ratio(minima_tick_partials: u32, ratio: u128) -> Result<(u128, u128)> {
let ratio_one_less = ratio
.safe_mul(FOUR_DECIMALS)?
.safe_div(TickMath::TICK_SPACING)?;
let length = ratio.safe_sub(ratio_one_less)?;
let partials = minima_tick_partials.cast()?;
let current_ratio = ratio_one_less.safe_add((length.safe_mul(partials)?).safe_div(X30)?)?;
Ok((current_ratio, partials))
}
pub async fn get_ticks_from_remaining_accounts(
remaining_accounts: &Vec<AccountMeta>,
remaining_accounts_indices: &Vec<u8>,
program: &VaultProgram,
) -> Result<Vec<Tick>> {
let total_ticks_length: usize = remaining_accounts_indices[2].cast::<usize>()?;
let start_index: usize = remaining_accounts_indices[0].cast::<usize>()?
+ remaining_accounts_indices[1].cast::<usize>()?;
let end_index: usize = start_index + total_ticks_length;
Ok(futures::future::try_join_all(
remaining_accounts
.iter()
.take(end_index)
.skip(start_index)
.map(|account| program.account::<Tick>(account.pubkey)),
)
.await?)
}
pub fn get_tick_partials(ratio_one_less: u128, final_ratio: u128) -> Result<u128> {
let ratio = ratio_one_less
.safe_mul(TickMath::TICK_SPACING)?
.safe_div(FOUR_DECIMALS)?;
let length = ratio.safe_sub(ratio_one_less)?;
let partials = (final_ratio.safe_sub(ratio_one_less)?)
.safe_mul(X30)?
.safe_div(length)?;
Ok(partials)
}