mod exo;
mod lst;
use anchor_lang::prelude::*;
use fix::prelude::*;
pub use self::exo::ExoExchangeContext;
pub use self::lst::LstExchangeContext;
use crate::conversion::SwapConversion;
use crate::error::CoreError::{
DestinationFeeStablecoin, LevercoinNav, RebalanceBuySideTarget,
RebalanceSellSideLiquidity, RequestedStablecoinOverMaxMintable,
StabilityValidation,
};
use crate::exchange_math::{
collateral_ratio, depeg_stablecoin_nav, max_mintable_stablecoin,
max_swappable_stablecoin, next_levercoin_mint_nav, next_levercoin_redeem_nav,
total_value_locked,
};
use crate::fee_controller::{FeeExtract, LevercoinFees};
use crate::pyth::{OraclePrice, PriceRange};
use crate::rebalance_math::{max_buyable_collateral, max_sellable_collateral};
use crate::rebalance_pricing::{
BuyPriceCurve, RebalanceCurveConfig, SellPriceCurve,
};
use crate::stability_mode::{StabilityController, StabilityMode};
pub fn validate_stability_thresholds(
stability_threshold_1: UFix64<N2>,
stability_threshold_2: UFix64<N2>,
) -> Result<()> {
(stability_threshold_1 > stability_threshold_2)
.then_some(())
.ok_or(StabilityValidation.into())
}
pub trait ExchangeContext {
fn total_collateral(&self) -> UFix64<N9>;
fn collateral_usd_price(&self) -> PriceRange<N9>;
fn collateral_oracle_price(&self) -> OraclePrice;
fn sell_curve_config(&self) -> &RebalanceCurveConfig;
fn buy_curve_config(&self) -> &RebalanceCurveConfig;
fn rebalance_sell_curve(&self) -> Result<SellPriceCurve> {
SellPriceCurve::new(
self.collateral_oracle_price(),
self.sell_curve_config(),
)
}
fn rebalance_buy_curve(&self) -> Result<BuyPriceCurve> {
BuyPriceCurve::new(self.collateral_oracle_price(), self.buy_curve_config())
}
fn rebalance_sell_liquidity(&self) -> Result<UFix64<N9>> {
let target_cr = self.stability_controller().stability_threshold_1;
let virtual_stablecoin = self.virtual_stablecoin_supply()?;
let collateral_usd_price = self.collateral_oracle_price().spot;
let total_collateral = self.total_collateral();
max_sellable_collateral(
target_cr,
virtual_stablecoin,
collateral_usd_price,
total_collateral,
)
.ok_or(RebalanceSellSideLiquidity.into())
}
fn rebalance_buy_target(&self) -> Result<UFix64<N9>> {
let target_cr = self.stability_controller().stability_threshold_1;
let virtual_stablecoin = self.virtual_stablecoin_supply()?;
let collateral_usd_price = self.collateral_oracle_price().spot;
let total_collateral = self.total_collateral();
max_buyable_collateral(
target_cr,
virtual_stablecoin,
collateral_usd_price,
total_collateral,
)
.ok_or(RebalanceBuySideTarget.into())
}
fn virtual_stablecoin_supply(&self) -> Result<UFix64<N6>>;
fn levercoin_supply(&self) -> Result<UFix64<N6>>;
fn stability_controller(&self) -> &StabilityController;
fn stability_mode(&self) -> StabilityMode;
fn collateral_ratio(&self) -> UFix64<N9>;
fn levercoin_fees(&self) -> &LevercoinFees;
fn total_value_locked(&self) -> Result<UFix64<N9>> {
total_value_locked(
self.total_collateral(),
self.collateral_usd_price().lower,
)
}
fn stablecoin_nav(&self) -> Result<UFix64<N9>> {
match self.stability_mode() {
StabilityMode::Depeg => depeg_stablecoin_nav(
self.total_collateral(),
self.collateral_usd_price().lower,
self.virtual_stablecoin_supply()?,
),
_ => Ok(UFix64::one()),
}
}
fn levercoin_mint_nav(&self) -> Result<UFix64<N9>> {
next_levercoin_mint_nav(
self.total_collateral(),
self.collateral_usd_price(),
self.virtual_stablecoin_supply()?,
self.stablecoin_nav()?,
self.levercoin_supply()?,
)
.ok_or(LevercoinNav.into())
}
fn levercoin_redeem_nav(&self) -> Result<UFix64<N9>> {
next_levercoin_redeem_nav(
self.total_collateral(),
self.collateral_usd_price(),
self.virtual_stablecoin_supply()?,
self.stablecoin_nav()?,
self.levercoin_supply()?,
)
.ok_or(LevercoinNav.into())
}
fn projected_stability_mode(
&self,
new_total: UFix64<N9>,
new_stablecoin: UFix64<N6>,
) -> Result<StabilityMode> {
let projected_cr = collateral_ratio(
new_total,
self.collateral_usd_price().lower,
new_stablecoin,
)?;
self.stability_controller().stability_mode(projected_cr)
}
fn select_stability_mode_for_fees(
&self,
projected: StabilityMode,
) -> StabilityMode {
if projected < self.stability_mode() {
self.stability_mode()
} else {
projected
}
}
fn swap_conversion(&self) -> Result<SwapConversion> {
let levercoin_nav =
PriceRange::new(self.levercoin_redeem_nav()?, self.levercoin_mint_nav()?);
Ok(SwapConversion::new(self.stablecoin_nav()?, levercoin_nav))
}
fn max_mintable_stablecoin(&self) -> Result<UFix64<N6>> {
max_mintable_stablecoin(
self.stability_controller().min_stability_threshold(),
self.total_collateral(),
self.collateral_usd_price().upper,
self.virtual_stablecoin_supply()?,
)
}
fn max_swappable_stablecoin(&self) -> Result<UFix64<N6>> {
max_swappable_stablecoin(
self.stability_controller().min_stability_threshold(),
self.total_value_locked()?,
self.virtual_stablecoin_supply()?,
)
}
fn validate_stablecoin_amount(
&self,
requested: UFix64<N6>,
) -> Result<UFix64<N6>> {
let max = self.max_mintable_stablecoin()?;
if requested <= max {
Ok(requested)
} else {
Err(RequestedStablecoinOverMaxMintable.into())
}
}
fn validate_stablecoin_swap_amount(
&self,
requested: UFix64<N6>,
) -> Result<UFix64<N6>> {
let max = self.max_swappable_stablecoin()?;
if requested <= max {
Ok(requested)
} else {
Err(RequestedStablecoinOverMaxMintable.into())
}
}
fn levercoin_to_stablecoin_fee(
&self,
amount_stablecoin: UFix64<N6>,
) -> Result<FeeExtract<N6>> {
let new_stablecoin = self
.virtual_stablecoin_supply()?
.checked_add(&amount_stablecoin)
.ok_or(DestinationFeeStablecoin)?;
let projected =
self.projected_stability_mode(self.total_collateral(), new_stablecoin)?;
let mode = self.select_stability_mode_for_fees(projected);
let fee = self.levercoin_fees().swap_to_stablecoin_fee(mode)?;
FeeExtract::new(fee, amount_stablecoin)
}
fn stablecoin_to_levercoin_fee(
&self,
amount_stablecoin: UFix64<N6>,
) -> Result<FeeExtract<N6>> {
let new_stablecoin = self
.virtual_stablecoin_supply()?
.checked_sub(&amount_stablecoin)
.ok_or(DestinationFeeStablecoin)?;
let projected =
self.projected_stability_mode(self.total_collateral(), new_stablecoin)?;
let mode = self.select_stability_mode_for_fees(projected);
let fee = self.levercoin_fees().swap_from_stablecoin_fee(mode)?;
FeeExtract::new(fee, amount_stablecoin)
}
}