use crate::{
math::{
bn::{mul_div_big_number, TWO_POWER_64},
casting::Cast,
safe_math::SafeMath,
tick::TickMath,
u256::safe_multiply_divide,
},
programs::vaults::accounts::{Branch, Tick, VaultState},
};
use anchor_lang::zero_copy;
use anyhow::Result;
use super::{
constants::{
EXCHANGE_PRICES_PRECISION, FOUR_DECIMALS, INITIAL_BRANCH_DEBT_FACTOR, RATE_OUTPUT_DECIMALS,
X30,
},
errors::ErrorCodes,
};
#[derive(Default)]
#[repr(C, packed)]
#[zero_copy]
pub struct CurrentLiquidity {
pub debt_remaining: u128, pub debt: u128, pub col: u128, pub total_debt_liq: u128, pub total_col_liq: u128, pub tick: i32, pub ratio: u128, pub tick_status: u8, pub ref_tick: i32, pub ref_ratio: u128, pub ref_tick_status: u8, }
impl CurrentLiquidity {
pub fn check_is_ref_partials_safe_for_tick(
&self,
existing_partials: u128,
partials: u128,
) -> Result<()> {
if existing_partials > 0 && existing_partials >= partials {
return Err(ErrorCodes::VaultLiquidationReverts.into());
}
Ok(())
}
pub fn get_final_ratio(&self, col_liquidated: u128, debt_liquidated: u128) -> Result<u128> {
let final_ratio: u128 = self
.debt
.safe_sub(debt_liquidated)?
.safe_mul(TickMath::ZERO_TICK_SCALED_RATIO)?
.safe_div(self.col.safe_sub(col_liquidated)?)?;
Ok(final_ratio)
}
pub fn is_perfect_tick(&self) -> bool {
self.tick_status == 1
}
pub fn is_ref_tick_liquidation_threshold(&self) -> bool {
self.ref_tick_status == 3
}
pub fn is_ref_tick_liquidated(&self) -> bool {
self.ref_tick_status == 2
}
pub fn get_debt_from_ratios(&self) -> Result<u128> {
Ok(self.ref_ratio.safe_mul(self.debt)?.safe_div(self.ratio)?)
}
pub fn get_col_from_ratios(&self, col_per_debt: u128) -> Result<u128> {
Ok(safe_multiply_divide(
col_per_debt,
self.ref_ratio,
TickMath::ZERO_TICK_SCALED_RATIO,
)?)
}
pub fn get_debt_liquidated(&self, col_per_debt: u128) -> Result<u128> {
let numerator = self
.debt
.safe_sub(self.get_debt_from_ratios()?)?
.safe_mul(10u128.pow(RATE_OUTPUT_DECIMALS))?;
let denominator = 10u128
.pow(RATE_OUTPUT_DECIMALS)
.safe_sub(self.get_col_from_ratios(col_per_debt)?)?;
let debt_liquidated = numerator.safe_div(denominator)?;
Ok(debt_liquidated)
}
fn get_normal_debt_liq(&self, borrow_ex_price: u128) -> Result<u128> {
Ok(self
.total_debt_liq
.safe_mul(borrow_ex_price)?
.safe_div(EXCHANGE_PRICES_PRECISION)?)
}
fn get_normal_col_liq(&self, supply_ex_price: u128) -> Result<u128> {
Ok(self
.total_col_liq
.safe_mul(supply_ex_price)?
.safe_div(EXCHANGE_PRICES_PRECISION)?)
}
pub fn get_actual_amounts(
&self,
borrow_ex_price: u128,
supply_ex_price: u128,
debt_amount: u128,
) -> Result<(u128, u128)> {
let mut actual_debt_amt = self.get_normal_debt_liq(borrow_ex_price)?;
let mut actual_col_amt = self.get_normal_col_liq(supply_ex_price)?;
if actual_debt_amt > debt_amount {
actual_col_amt = actual_col_amt
.safe_mul(debt_amount)?
.safe_div(actual_debt_amt)?;
actual_debt_amt = debt_amount;
}
if actual_debt_amt == 0 {
return Err(ErrorCodes::VaultInvalidLiquidation.into());
}
Ok((actual_debt_amt, actual_col_amt))
}
pub fn update_next_iterations_with_ref(&mut self) {
self.tick = self.ref_tick;
self.tick_status = self.ref_tick_status;
self.ratio = self.ref_ratio;
}
pub fn get_debt_factor(&self, debt_liquidated: u128) -> Result<u128> {
let debt_factor = TWO_POWER_64
.safe_mul(self.debt.safe_sub(debt_liquidated)?.cast()?)?
.safe_div(self.debt.cast()?)?;
Ok(debt_factor)
}
pub fn reduce_debt_remaining(&mut self, debt_liquidated: u128) -> Result<()> {
self.debt_remaining = self.debt_remaining.safe_sub(debt_liquidated)?;
Ok(())
}
fn reduce_col(&mut self, col_liquidated: u128) -> Result<()> {
self.col = self.col.safe_sub(col_liquidated)?;
Ok(())
}
fn increase_total_debt_liq(&mut self, debt_liquidated: u128) -> Result<()> {
self.total_debt_liq = self.total_debt_liq.safe_add(debt_liquidated)?;
Ok(())
}
fn increase_total_col_liq(&mut self, col_liquidated: u128) -> Result<()> {
self.total_col_liq = self.total_col_liq.safe_add(col_liquidated)?;
Ok(())
}
fn reduce_debt(&mut self, debt_liquidated: u128) -> Result<()> {
self.debt = self.debt.safe_sub(debt_liquidated)?;
Ok(())
}
pub fn update_totals(&mut self, debt_liquidated: u128, col_liquidated: u128) -> Result<()> {
self.reduce_col(col_liquidated)?;
self.increase_total_col_liq(col_liquidated)?;
self.increase_total_debt_liq(debt_liquidated)?;
self.reduce_debt(debt_liquidated)?;
Ok(())
}
}
#[derive(Default)]
#[repr(C, packed)]
#[zero_copy]
pub struct TickMemoryVars {
pub tick: i32,
pub partials: u128,
}
impl TickMemoryVars {
pub fn set_partials(&mut self, partials: u128) -> Result<()> {
if partials == 0 {
self.partials = 1;
} else if partials >= X30 {
self.partials = X30 - 1;
} else {
self.partials = partials;
}
Ok(())
}
pub fn set_tick(&mut self, tick: i32) {
self.tick = tick;
}
}
impl Tick {
pub fn set_fully_liquidated(&mut self) {
self.set_liquidated(0, 0);
self.is_fully_liquidated = 1;
}
pub fn set_liquidated(&mut self, branch_id: u32, debt_factor: u64) {
self.raw_debt = 0;
self.is_liquidated = 1;
self.debt_factor = debt_factor;
self.liquidation_branch_id = branch_id;
}
pub fn get_raw_debt(&self) -> Result<u128> {
Ok(self.raw_debt.cast()?)
}
}
#[derive(Default)]
#[repr(C, packed)]
#[zero_copy]
pub struct BranchState {
pub minima_tick: i32, pub minima_tick_partials: u32, pub debt_liquidity: u64,
pub debt_factor: u64,
pub connected_branch_id: u32, pub connected_minima_tick: i32, }
#[derive(Default)]
#[repr(C, packed)]
#[zero_copy]
pub struct BranchMemoryVars {
pub id: u32,
pub data: BranchState,
pub debt_factor: u64,
pub minima_tick: i32,
pub base_branch_data: BranchState,
}
impl BranchMemoryVars {
pub fn reset_branch_data(&mut self) {
self.data = BranchState::default();
self.data.minima_tick = TickMath::COLD_TICK;
self.data.connected_minima_tick = TickMath::COLD_TICK;
}
pub fn update_branch_to_base_branch(&mut self) {
self.id = self.data.connected_branch_id;
self.data = self.base_branch_data;
self.minima_tick = self.base_branch_data.connected_minima_tick;
}
pub fn update_branch_debt_factor(&mut self, debt_factor: u128) -> Result<()> {
self.debt_factor = mul_div_big_number(self.debt_factor, debt_factor.cast()?)?;
Ok(())
}
pub fn set_branch_data(&mut self, branch: &Branch) -> Result<()> {
self.data = BranchState {
minima_tick: branch.minima_tick,
minima_tick_partials: branch.minima_tick_partials,
debt_liquidity: branch.debt_liquidity,
debt_factor: branch.debt_factor,
connected_branch_id: branch.connected_branch_id,
connected_minima_tick: branch.connected_minima_tick,
};
Ok(())
}
pub fn set_branch_data_in_memory(&mut self, branch: &Branch) -> Result<()> {
self.id = branch.branch_id;
self.set_branch_data(branch)?;
self.debt_factor = self.data.debt_factor;
if self.debt_factor == 0 {
self.debt_factor = INITIAL_BRANCH_DEBT_FACTOR.cast()?;
}
self.minima_tick = self.data.connected_minima_tick;
Ok(())
}
pub fn set_base_branch_data(&mut self, branch: &Branch) -> Result<()> {
self.base_branch_data = BranchState {
minima_tick: branch.minima_tick,
minima_tick_partials: branch.minima_tick_partials,
debt_liquidity: branch.debt_liquidity,
debt_factor: branch.debt_factor,
connected_branch_id: branch.connected_branch_id,
connected_minima_tick: branch.connected_minima_tick,
};
Ok(())
}
pub fn get_current_ratio_from_minima_tick(&self) -> Result<u128> {
let ratio = TickMath::get_ratio_at_tick(self.minima_tick)?;
let ratio_one_less = ratio
.safe_mul(FOUR_DECIMALS)?
.safe_div(TickMath::TICK_SPACING)?;
let length = ratio.safe_sub(ratio_one_less)?;
let current_ratio = ratio_one_less.safe_add(
length
.safe_mul(self.data.minima_tick_partials.cast()?)?
.safe_div(X30)?,
)?;
Ok(current_ratio)
}
}
impl VaultState {
fn bump_total_branch_id(&mut self) {
self.total_branch_id += 1;
}
pub fn reset_top_tick(&mut self) {
self.topmost_tick = TickMath::COLD_TICK;
}
pub fn update_branch_info_by_one(&mut self) {
self.bump_total_branch_id();
self.reset_branch_liquidated();
self.current_branch_id = self.total_branch_id;
}
pub fn reset_branch_liquidated(&mut self) {
self.branch_liquidated = 0;
}
pub fn get_tick_status(&self) -> u8 {
if self.is_branch_liquidated() {
2
} else {
1
}
}
pub fn is_branch_liquidated(&self) -> bool {
self.branch_liquidated == 1
}
pub fn get_top_tick(&self) -> i32 {
self.topmost_tick
}
pub fn get_total_borrow(&self) -> Result<u128> {
Ok(self.total_borrow.cast()?)
}
pub fn reduce_total_borrow(&mut self, amount: u128) -> Result<()> {
self.total_borrow = self.total_borrow.safe_sub(amount.cast()?)?;
Ok(())
}
pub fn reduce_total_supply(&mut self, amount: u128) -> Result<()> {
self.total_supply = self.total_supply.safe_sub(amount.cast()?)?;
Ok(())
}
pub fn reset_absorbed_amounts(&mut self) {
self.absorbed_debt_amount = 0;
self.absorbed_col_amount = 0;
}
pub fn absorb_dust_amount_for_liquidate(
&mut self,
current_data: &mut CurrentLiquidity,
) -> Result<()> {
let absorbed_debt: u128 = self.absorbed_debt_amount;
let absorbed_col: u128 = self.absorbed_col_amount;
if absorbed_debt > current_data.debt_remaining {
current_data.total_col_liq = absorbed_col
.safe_mul(current_data.debt_remaining)?
.safe_div(absorbed_debt)?;
self.absorbed_col_amount = absorbed_col.safe_sub(current_data.total_col_liq)?;
current_data.total_debt_liq = current_data.debt_remaining;
self.absorbed_debt_amount = absorbed_debt.safe_sub(current_data.debt_remaining)?;
current_data.debt_remaining = 0;
} else {
self.reset_absorbed_amounts();
current_data.debt_remaining = current_data.debt_remaining.safe_sub(absorbed_debt)?;
current_data.total_debt_liq = absorbed_debt;
current_data.total_col_liq = absorbed_col;
}
Ok(())
}
pub fn set_branch_liquidated(&mut self) {
self.branch_liquidated = 1;
}
pub fn update_state_at_liq_end(&mut self, tick: i32, branch_id: u32) -> Result<()> {
self.set_branch_liquidated();
self.topmost_tick = tick;
self.current_branch_id = branch_id;
Ok(())
}
}
pub enum BranchStatus {
NotLiquidated,
Liquidated,
Merged,
Closed,
}
impl Branch {
pub fn set_state_after_absorb(&mut self, branch_data: &BranchState) {
self.set_status_as_closed();
self.minima_tick = branch_data.minima_tick;
self.minima_tick_partials = branch_data.minima_tick_partials;
self.debt_liquidity = branch_data.debt_liquidity;
self.debt_factor = branch_data.debt_factor;
self.connected_branch_id = branch_data.connected_branch_id;
self.connected_minima_tick = branch_data.connected_minima_tick;
}
pub fn set_status_as_closed(&mut self) {
self.status = BranchStatus::Closed as u8;
}
pub fn set_status_as_merged(&mut self) {
self.status = BranchStatus::Merged as u8;
}
pub fn merge_with_base_branch(&mut self, connection_factor: u64) -> Result<()> {
self.set_status_as_merged();
self.debt_factor = connection_factor;
self.minima_tick = TickMath::COLD_TICK;
self.minima_tick_partials = 0;
self.debt_liquidity = 0;
Ok(())
}
pub fn set_connections(&mut self, branch_id: u32, minima_tick: i32) -> Result<()> {
self.connected_branch_id = branch_id;
self.connected_minima_tick = minima_tick;
Ok(())
}
pub fn reset_branch_data(&mut self) {
self.status = 0;
self.minima_tick = TickMath::COLD_TICK;
self.minima_tick_partials = 0;
self.debt_liquidity = 0;
self.debt_factor = 0;
self.connected_branch_id = 0;
self.connected_minima_tick = TickMath::COLD_TICK;
}
pub fn set_status_as_liquidated(&mut self) {
self.status = BranchStatus::Liquidated as u8;
}
pub fn set_branch_debt(&mut self, new_debt_liquidity_raw: u128) -> Result<()> {
self.debt_liquidity = new_debt_liquidity_raw.cast()?;
Ok(())
}
pub fn update_state_at_liq_end(
&mut self,
tick: i32,
partials: u128,
debt: u128,
debt_factor: u128,
) -> Result<()> {
self.set_status_as_liquidated();
self.minima_tick = tick;
self.minima_tick_partials = partials.cast()?;
self.set_branch_debt(debt)?;
self.debt_factor = debt_factor.cast()?;
Ok(())
}
}