use integer_cbrt::IntegerCubeRoot;
use integer_sqrt::IntegerSquareRoot;
use rust_decimal::prelude::ToPrimitive;
use rust_decimal::Decimal;
use schemars::JsonSchema;
use serde::{Deserialize, Serialize};
use std::str::FromStr;
use cosmwasm_std::{Decimal as StdDecimal, Uint128};
pub trait Curve {
fn spot_price(&self, supply: Uint128) -> StdDecimal;
fn reserve(&self, supply: Uint128) -> Uint128;
fn supply(&self, reserve: Uint128) -> Uint128;
}
pub fn decimal<T: Into<u128>>(num: T, scale: u32) -> Decimal {
Decimal::from_i128_with_scale(num.into() as i128, scale)
}
fn decimal_to_std(x: Decimal) -> StdDecimal {
StdDecimal::from_str(&x.to_string()).unwrap()
}
pub struct Constant {
pub value: Decimal,
pub normalize: DecimalPlaces,
}
impl Constant {
pub fn new(value: Decimal, normalize: DecimalPlaces) -> Self {
Self { value, normalize }
}
}
impl Curve for Constant {
fn spot_price(&self, _supply: Uint128) -> StdDecimal {
decimal_to_std(self.value)
}
fn reserve(&self, supply: Uint128) -> Uint128 {
let reserve = self.normalize.from_supply(supply) * self.value;
self.normalize.to_reserve(reserve)
}
fn supply(&self, reserve: Uint128) -> Uint128 {
let supply = self.normalize.from_reserve(reserve) / self.value;
self.normalize.to_supply(supply)
}
}
pub struct Linear {
pub slope: Decimal,
pub normalize: DecimalPlaces,
}
impl Linear {
pub fn new(slope: Decimal, normalize: DecimalPlaces) -> Self {
Self { slope, normalize }
}
}
impl Curve for Linear {
fn spot_price(&self, supply: Uint128) -> StdDecimal {
let out = self.normalize.from_supply(supply) * self.slope;
decimal_to_std(out)
}
fn reserve(&self, supply: Uint128) -> Uint128 {
let normalized = self.normalize.from_supply(supply);
let square = normalized * normalized;
let reserve = square * self.slope * Decimal::new(5, 1);
self.normalize.to_reserve(reserve)
}
fn supply(&self, reserve: Uint128) -> Uint128 {
let square = self.normalize.from_reserve(reserve + reserve) / self.slope;
let supply = square_root(square);
self.normalize.to_supply(supply)
}
}
pub struct SquareRoot {
pub slope: Decimal,
pub normalize: DecimalPlaces,
}
impl SquareRoot {
pub fn new(slope: Decimal, normalize: DecimalPlaces) -> Self {
Self { slope, normalize }
}
}
impl Curve for SquareRoot {
fn spot_price(&self, supply: Uint128) -> StdDecimal {
let square = self.normalize.from_supply(supply);
let root = square_root(square);
decimal_to_std(root * self.slope)
}
fn reserve(&self, supply: Uint128) -> Uint128 {
let normalized = self.normalize.from_supply(supply);
let root = square_root(normalized);
let reserve = self.slope * normalized * root / Decimal::new(15, 1);
self.normalize.to_reserve(reserve)
}
fn supply(&self, reserve: Uint128) -> Uint128 {
let base = self.normalize.from_reserve(reserve) * Decimal::new(15, 1) / self.slope;
let squared = base * base;
let supply = cube_root(squared);
self.normalize.to_supply(supply)
}
}
fn square_root(square: Decimal) -> Decimal {
const EXTRA_DIGITS: u32 = 12;
let multiplier = 10u128.saturating_pow(EXTRA_DIGITS);
let extended = square * decimal(multiplier, 0);
let extended = extended.floor().to_u128().unwrap();
let root = extended.integer_sqrt();
decimal(root, EXTRA_DIGITS / 2)
}
fn cube_root(cube: Decimal) -> Decimal {
const EXTRA_DIGITS: u32 = 9;
let multiplier = 10u128.saturating_pow(EXTRA_DIGITS);
let extended = cube * decimal(multiplier, 0);
let extended = extended.floor().to_u128().unwrap();
let root = extended.integer_cbrt();
decimal(root, EXTRA_DIGITS / 3)
}
#[derive(Serialize, Deserialize, Clone, Copy, Debug, PartialEq, JsonSchema, Default)]
pub struct DecimalPlaces {
pub supply: u32,
pub reserve: u32,
}
impl DecimalPlaces {
pub fn new(supply: u8, reserve: u8) -> Self {
DecimalPlaces {
supply: supply as u32,
reserve: reserve as u32,
}
}
pub fn to_reserve(&self, reserve: Decimal) -> Uint128 {
let factor = decimal(10u128.pow(self.reserve), 0);
let out = reserve * factor;
out.floor().to_u128().unwrap().into()
}
pub fn to_supply(&self, supply: Decimal) -> Uint128 {
let factor = decimal(10u128.pow(self.supply), 0);
let out = supply * factor;
out.floor().to_u128().unwrap().into()
}
pub fn from_supply(&self, supply: Uint128) -> Decimal {
decimal(supply, self.supply)
}
pub fn from_reserve(&self, reserve: Uint128) -> Decimal {
decimal(reserve, self.reserve)
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn constant_curve() {
let normalize = DecimalPlaces::new(9, 6);
let curve = Constant::new(decimal(15u128, 1), normalize);
assert_eq!(StdDecimal::percent(150), curve.spot_price(Uint128(123)));
let reserve = curve.reserve(Uint128(30_000_000_000));
assert_eq!(Uint128(45_000_000), reserve);
let supply = curve.supply(Uint128(36_000_000));
assert_eq!(Uint128(24_000_000_000), supply);
}
#[test]
fn linear_curve() {
let normalize = DecimalPlaces::new(2, 8);
let curve = Linear::new(decimal(1u128, 1), normalize);
assert_eq!(StdDecimal::permille(100), curve.spot_price(Uint128(100)));
assert_eq!(StdDecimal::permille(1700), curve.spot_price(Uint128(1700)));
assert_eq!(StdDecimal::permille(212), curve.spot_price(Uint128(212)));
let reserve = curve.reserve(Uint128(1000));
assert_eq!(Uint128(500_000_000), reserve);
let reserve = curve.reserve(Uint128(2000));
assert_eq!(Uint128(2_000_000_000), reserve);
let supply = curve.supply(Uint128(125_000_000));
assert_eq!(Uint128(500), supply);
let supply = curve.supply(Uint128(111_000_000));
assert_eq!(Uint128(471), supply);
}
#[test]
fn sqrt_curve() {
let normalize = DecimalPlaces::new(6, 2);
let curve = SquareRoot::new(decimal(35u128, 2), normalize);
assert_eq!(
StdDecimal::percent(35),
curve.spot_price(Uint128(1_000_000))
);
assert_eq!(
StdDecimal::percent(350),
curve.spot_price(Uint128(100_000_000))
);
assert_eq!(
StdDecimal::from_ratio(2347871365u128, 100_000_000u128),
curve.spot_price(Uint128(4_500_000_000))
);
let reserve = curve.reserve(Uint128(1_000_000));
assert_eq!(Uint128(23), reserve);
let reserve = curve.reserve(Uint128(100_000_000));
assert_eq!(Uint128(23_333), reserve);
let reserve = curve.reserve(Uint128(235_000_000));
assert_eq!(Uint128(84_057), reserve);
let supply = curve.supply(Uint128(23));
assert_eq!(Uint128(990_000), supply);
let supply = curve.supply(Uint128(84058));
assert_eq!(Uint128(235_000_000), supply);
}
}