use fix::prelude::*;
use crate::error::CoreError;
use crate::error::CoreError::{
LevercoinMarketCapArithmetic, LevercoinMarketCapLimitInvalid,
LevercoinMarketCapLimitReached,
};
use crate::exchange_math::levercoin_market_cap;
const MIN_MARKET_CAP_LIMIT: UFix64<N9> =
UFix64::constant(1_000_000_000_000_000);
const MAX_MARKET_CAP_LIMIT: UFix64<N9> =
UFix64::constant(100_000_000_000_000_000);
pub fn validate_levercoin_market_cap_limit(
limit_raw: UFixValue64,
) -> Result<UFixValue64, CoreError> {
let limit: UFix64<N9> = limit_raw.try_into()?;
(MIN_MARKET_CAP_LIMIT..=MAX_MARKET_CAP_LIMIT)
.contains(&limit)
.then_some(limit_raw)
.ok_or(LevercoinMarketCapLimitInvalid)
}
pub struct LevercoinMarketCapLimiter {
pub market_cap_limit: UFix64<N9>,
pub levercoin_nav: UFix64<N9>,
pub levercoin_supply: UFix64<N6>,
}
impl LevercoinMarketCapLimiter {
#[must_use]
pub fn new(
market_cap_limit: UFix64<N9>,
levercoin_nav: UFix64<N9>,
levercoin_supply: UFix64<N6>,
) -> LevercoinMarketCapLimiter {
LevercoinMarketCapLimiter {
market_cap_limit,
levercoin_nav,
levercoin_supply,
}
}
fn target_market_cap(
&self,
levercoin_to_mint: UFix64<N6>,
) -> Result<UFix64<N9>, CoreError> {
let target_supply = self
.levercoin_supply
.checked_add(&levercoin_to_mint)
.ok_or(LevercoinMarketCapArithmetic)?;
levercoin_market_cap(target_supply, self.levercoin_nav)
}
pub fn validate_token_out(
&self,
levercoin_to_mint: UFix64<N6>,
) -> Result<UFix64<N6>, CoreError> {
let target_market_cap = self.target_market_cap(levercoin_to_mint)?;
if target_market_cap <= self.market_cap_limit {
Ok(levercoin_to_mint)
} else {
Err(LevercoinMarketCapLimitReached)
}
}
}
#[cfg(test)]
mod tests {
use super::*;
use crate::error::CoreError::{
LevercoinMarketCapArithmetic, LevercoinMarketCapLimitInvalid,
LevercoinMarketCapLimitReached,
};
#[test]
fn validate_limit_boundaries() -> Result<(), CoreError> {
let min = MIN_MARKET_CAP_LIMIT.into();
let max = MAX_MARKET_CAP_LIMIT.into();
let below = UFixValue64::new(MIN_MARKET_CAP_LIMIT.bits - 1, -9);
let above = UFixValue64::new(MAX_MARKET_CAP_LIMIT.bits + 1, -9);
assert_eq!(validate_levercoin_market_cap_limit(min)?, min);
assert_eq!(validate_levercoin_market_cap_limit(max)?, max);
assert_eq!(
validate_levercoin_market_cap_limit(below).err(),
Some(LevercoinMarketCapLimitInvalid)
);
assert_eq!(
validate_levercoin_market_cap_limit(above).err(),
Some(LevercoinMarketCapLimitInvalid)
);
Ok(())
}
#[test]
fn reject_limit_wrong_exp() {
let raw = UFixValue64::new(10_000_000_000_000, -6);
assert!(validate_levercoin_market_cap_limit(raw).is_err());
}
fn limiter() -> LevercoinMarketCapLimiter {
LevercoinMarketCapLimiter::new(
UFix64::new(10_000_000_000_000_000),
UFix64::new(1_000_000_000),
UFix64::new(5_000_000_000_000),
)
}
#[test]
fn accept_mint_under_limit() -> Result<(), CoreError> {
let to_mint = UFix64::new(1_000_000_000_000);
assert_eq!(limiter().validate_token_out(to_mint)?, to_mint);
Ok(())
}
#[test]
fn accept_mint_at_limit() -> Result<(), CoreError> {
let to_mint = UFix64::new(5_000_000_000_000);
assert_eq!(limiter().validate_token_out(to_mint)?, to_mint);
Ok(())
}
#[test]
fn reject_mint_one_unit_over_limit() {
let to_mint = UFix64::new(5_000_000_000_001);
assert_eq!(
limiter().validate_token_out(to_mint).err(),
Some(LevercoinMarketCapLimitReached)
);
}
#[test]
fn reject_mint_on_supply_overflow() {
let to_mint = UFix64::new(u64::MAX);
assert_eq!(
limiter().validate_token_out(to_mint).err(),
Some(LevercoinMarketCapArithmetic)
);
}
}