use pyra_types::{KaminoReserve, KAMINO_FRACTION_SCALE};
use crate::error::{MathError, MathResult};
const SPOT_WEIGHT_PRECISION: u128 = 10_000;
const PRICE_PRECISION: u128 = 1_000_000;
pub fn get_kamino_asset_weight(reserve: &KaminoReserve) -> u128 {
(reserve.config.loan_to_value_pct as u128)
.saturating_mul(SPOT_WEIGHT_PRECISION)
.saturating_div(100)
}
pub fn get_kamino_liability_weight(reserve: &KaminoReserve) -> MathResult<u128> {
let threshold = reserve.config.liquidation_threshold_pct as u128;
if threshold == 0 {
return Err(MathError::Overflow);
}
SPOT_WEIGHT_PRECISION
.checked_mul(100)
.ok_or(MathError::Overflow)?
.checked_div(threshold)
.ok_or(MathError::Overflow)
}
pub fn get_kamino_price(reserve: &KaminoReserve) -> MathResult<i128> {
let price = reserve
.liquidity
.market_price_sf
.checked_mul(PRICE_PRECISION)
.ok_or(MathError::Overflow)?
.checked_div(KAMINO_FRACTION_SCALE)
.ok_or(MathError::Overflow)?;
i128::try_from(price).map_err(|_| MathError::Overflow)
}
#[cfg(test)]
#[allow(
clippy::allow_attributes,
clippy::allow_attributes_without_reason,
clippy::unwrap_used,
clippy::expect_used,
clippy::panic,
clippy::arithmetic_side_effects,
reason = "test code"
)]
mod tests {
use super::*;
use pyra_types::{
KaminoBigFractionBytes, KaminoLastUpdate, KaminoReserveCollateral, KaminoReserveConfig,
KaminoReserveLiquidity,
};
use solana_pubkey::Pubkey;
fn make_reserve_with_config(ltv: u8, liq_threshold: u8, price_sf: u128) -> KaminoReserve {
KaminoReserve {
lending_market: Pubkey::default(),
liquidity: KaminoReserveLiquidity {
mint_pubkey: Pubkey::default(),
supply_vault: Pubkey::default(),
fee_vault: Pubkey::default(),
total_available_amount: 0,
borrowed_amount_sf: 0,
cumulative_borrow_rate_bsf: KaminoBigFractionBytes::default(),
mint_decimals: 6,
market_price_sf: price_sf,
accumulated_protocol_fees_sf: 0,
accumulated_referrer_fees_sf: 0,
pending_referrer_fees_sf: 0,
token_program: Pubkey::default(),
},
collateral: KaminoReserveCollateral {
mint_pubkey: Pubkey::default(),
supply_vault: Pubkey::default(),
mint_total_supply: 0,
},
config: KaminoReserveConfig {
loan_to_value_pct: ltv,
liquidation_threshold_pct: liq_threshold,
protocol_take_rate_pct: 0,
protocol_liquidation_fee_pct: 0,
borrow_factor_pct: 100,
deposit_limit: 0,
borrow_limit: 0,
fees: Default::default(),
borrow_rate_curve: Default::default(),
deposit_withdrawal_cap: Default::default(),
debt_withdrawal_cap: Default::default(),
elevation_groups: [0; 20],
},
last_update: KaminoLastUpdate::default(),
}
}
#[test]
fn asset_weight_80_ltv() {
let reserve = make_reserve_with_config(80, 85, 0);
assert_eq!(get_kamino_asset_weight(&reserve), 8_000);
}
#[test]
fn asset_weight_100_ltv() {
let reserve = make_reserve_with_config(100, 100, 0);
assert_eq!(get_kamino_asset_weight(&reserve), 10_000);
}
#[test]
fn asset_weight_0_ltv() {
let reserve = make_reserve_with_config(0, 85, 0);
assert_eq!(get_kamino_asset_weight(&reserve), 0);
}
#[test]
fn asset_weight_50_ltv() {
let reserve = make_reserve_with_config(50, 85, 0);
assert_eq!(get_kamino_asset_weight(&reserve), 5_000);
}
#[test]
fn liability_weight_85_threshold() {
let reserve = make_reserve_with_config(80, 85, 0);
assert_eq!(get_kamino_liability_weight(&reserve).unwrap(), 11_764);
}
#[test]
fn liability_weight_100_threshold() {
let reserve = make_reserve_with_config(80, 100, 0);
assert_eq!(get_kamino_liability_weight(&reserve).unwrap(), 10_000);
}
#[test]
fn liability_weight_50_threshold() {
let reserve = make_reserve_with_config(80, 50, 0);
assert_eq!(get_kamino_liability_weight(&reserve).unwrap(), 20_000);
}
#[test]
fn liability_weight_zero_threshold_errors() {
let reserve = make_reserve_with_config(80, 0, 0);
assert!(get_kamino_liability_weight(&reserve).is_err());
}
#[test]
fn price_one_dollar() {
let price_sf = 1u128 << 60; let reserve = make_reserve_with_config(80, 85, price_sf);
assert_eq!(get_kamino_price(&reserve).unwrap(), 1_000_000);
}
#[test]
fn price_hundred_dollars() {
let price_sf = 100u128 << 60;
let reserve = make_reserve_with_config(80, 85, price_sf);
assert_eq!(get_kamino_price(&reserve).unwrap(), 100_000_000);
}
#[test]
fn price_zero() {
let reserve = make_reserve_with_config(80, 85, 0);
assert_eq!(get_kamino_price(&reserve).unwrap(), 0);
}
#[test]
fn price_fractional() {
let price_sf = 1u128 << 59; let reserve = make_reserve_with_config(80, 85, price_sf);
assert_eq!(get_kamino_price(&reserve).unwrap(), 500_000);
}
}