use rust_decimal::Decimal;
use rust_decimal_macros::dec;
use serde::{Deserialize, Serialize};
pub trait BondingCurve: Send + Sync {
fn buy_price(&self, current_supply: Decimal, amount: Decimal) -> Decimal;
fn sell_price(&self, current_supply: Decimal, amount: Decimal) -> Decimal;
fn spot_price(&self, current_supply: Decimal) -> Decimal;
fn buy_price_impact(&self, current_supply: Decimal, amount: Decimal) -> Decimal {
if amount == dec!(0) {
return dec!(0);
}
let spot_before = self.spot_price(current_supply);
let spot_after = self.spot_price(current_supply + amount);
if spot_before == dec!(0) {
return dec!(0);
}
((spot_after - spot_before) / spot_before) * dec!(100)
}
fn sell_price_impact(&self, current_supply: Decimal, amount: Decimal) -> Decimal {
if amount == dec!(0) || current_supply <= amount {
return dec!(0);
}
let spot_before = self.spot_price(current_supply);
let spot_after = self.spot_price(current_supply - amount);
if spot_before == dec!(0) {
return dec!(0);
}
((spot_before - spot_after) / spot_before) * dec!(100)
}
fn avg_buy_price(&self, current_supply: Decimal, amount: Decimal) -> Decimal {
if amount == dec!(0) {
return dec!(0);
}
self.buy_price(current_supply, amount) / amount
}
fn avg_sell_price(&self, current_supply: Decimal, amount: Decimal) -> Decimal {
if amount == dec!(0) {
return dec!(0);
}
self.sell_price(current_supply, amount) / amount
}
fn check_buy_slippage(
&self,
current_supply: Decimal,
amount: Decimal,
max_price: Decimal,
) -> bool {
let avg_price = self.avg_buy_price(current_supply, amount);
avg_price <= max_price
}
fn check_sell_slippage(
&self,
current_supply: Decimal,
amount: Decimal,
min_price: Decimal,
) -> bool {
let avg_price = self.avg_sell_price(current_supply, amount);
avg_price >= min_price
}
}
#[derive(Debug, Clone, Serialize)]
pub struct PriceQuote {
pub amount: Decimal,
pub total_btc: Decimal,
pub avg_price_btc: Decimal,
pub spot_price_before: Decimal,
pub spot_price_after: Decimal,
pub price_impact_percent: Decimal,
}
impl PriceQuote {
pub fn is_high_impact(&self, threshold_percent: Decimal) -> bool {
self.price_impact_percent.abs() >= threshold_percent
}
pub fn effective_spread_percent(&self) -> Decimal {
if self.spot_price_before == dec!(0) {
return dec!(0);
}
((self.avg_price_btc - self.spot_price_before) / self.spot_price_before).abs() * dec!(100)
}
}
pub struct CurveValidator;
impl CurveValidator {
pub fn validate_initial_price(price: Decimal) -> Result<(), String> {
if price <= dec!(0) {
return Err("Initial price must be positive".to_string());
}
if price < dec!(0.00000001) {
return Err("Initial price is too small (min 0.00000001 BTC)".to_string());
}
if price > dec!(100) {
return Err("Initial price is too high (max 100 BTC)".to_string());
}
Ok(())
}
pub fn validate_growth_rate(rate: Decimal, curve_type: &str) -> Result<(), String> {
match curve_type {
"linear" => {
if rate < dec!(0) {
return Err("Linear increment cannot be negative".to_string());
}
if rate > dec!(1) {
return Err("Linear increment is too high (max 1 BTC)".to_string());
}
}
"exponential" => {
if rate <= dec!(0) {
return Err("Exponential growth rate must be positive".to_string());
}
if rate > dec!(0.1) {
return Err("Exponential growth rate is too high (max 0.1)".to_string());
}
}
"bancor" => {
if rate <= dec!(0) || rate > dec!(1) {
return Err("Reserve ratio must be between 0 and 1".to_string());
}
}
_ => {}
}
Ok(())
}
pub fn validate_scale_factor(factor: Decimal) -> Result<(), String> {
if factor <= dec!(0) {
return Err("Scale factor must be positive".to_string());
}
if factor < dec!(0.1) {
return Err("Scale factor is too small (min 0.1)".to_string());
}
Ok(())
}
}
pub struct CurveComparator;
impl CurveComparator {
pub fn compare_spot_prices<C1: BondingCurve, C2: BondingCurve>(
curve1: &C1,
curve2: &C2,
supply: Decimal,
) -> CurveComparison {
let price1 = curve1.spot_price(supply);
let price2 = curve2.spot_price(supply);
let difference = price1 - price2;
let percent_difference = if price2 != dec!(0) {
(difference / price2) * dec!(100)
} else {
dec!(0)
};
CurveComparison {
supply,
price1,
price2,
difference,
percent_difference,
}
}
pub fn calculate_elasticity<C: BondingCurve>(
curve: &C,
supply: Decimal,
delta_percent: Decimal,
) -> Decimal {
let delta = supply * delta_percent / dec!(100);
let new_supply = supply + delta;
let price_before = curve.spot_price(supply);
let price_after = curve.spot_price(new_supply);
if price_before == dec!(0) {
return dec!(0);
}
let price_change_percent = ((price_after - price_before) / price_before) * dec!(100);
if delta_percent != dec!(0) {
price_change_percent / delta_percent
} else {
dec!(0)
}
}
pub fn find_price_target<C: BondingCurve>(
curve: &C,
target_price: Decimal,
max_supply: Decimal,
tolerance: Decimal,
) -> Option<Decimal> {
let mut low = dec!(0);
let mut high = max_supply;
let mut iterations = 0;
let max_iterations = 100;
while iterations < max_iterations && (high - low) > tolerance {
let mid = (low + high) / dec!(2);
let price = curve.spot_price(mid);
if (price - target_price).abs() < tolerance {
return Some(mid);
}
if price < target_price {
low = mid;
} else {
high = mid;
}
iterations += 1;
}
None
}
}
#[derive(Debug, Clone, Serialize)]
pub struct CurveComparison {
pub supply: Decimal,
pub price1: Decimal,
pub price2: Decimal,
pub difference: Decimal,
pub percent_difference: Decimal,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct LinearBondingCurve {
pub initial_price: Decimal,
pub increment: Decimal,
}
impl LinearBondingCurve {
pub fn new(initial_price: Decimal, increment: Decimal) -> Self {
Self {
initial_price,
increment,
}
}
pub fn get_buy_quote(&self, current_supply: Decimal, amount: Decimal) -> PriceQuote {
let total = self.buy_price(current_supply, amount);
let spot_before = self.spot_price(current_supply);
let spot_after = self.spot_price(current_supply + amount);
PriceQuote {
amount,
total_btc: total,
avg_price_btc: if amount > dec!(0) {
total / amount
} else {
dec!(0)
},
spot_price_before: spot_before,
spot_price_after: spot_after,
price_impact_percent: self.buy_price_impact(current_supply, amount),
}
}
pub fn get_sell_quote(&self, current_supply: Decimal, amount: Decimal) -> PriceQuote {
let total = self.sell_price(current_supply, amount);
let spot_before = self.spot_price(current_supply);
let spot_after = self.spot_price(current_supply - amount);
PriceQuote {
amount,
total_btc: total,
avg_price_btc: if amount > dec!(0) {
total / amount
} else {
dec!(0)
},
spot_price_before: spot_before,
spot_price_after: spot_after,
price_impact_percent: self.sell_price_impact(current_supply, amount),
}
}
}
impl BondingCurve for LinearBondingCurve {
fn spot_price(&self, current_supply: Decimal) -> Decimal {
self.initial_price + (current_supply * self.increment)
}
fn buy_price(&self, current_supply: Decimal, amount: Decimal) -> Decimal {
let base_cost = amount * self.initial_price;
let incremental_cost =
self.increment * amount * (dec!(2) * current_supply + amount - dec!(1)) / dec!(2);
base_cost + incremental_cost
}
fn sell_price(&self, current_supply: Decimal, amount: Decimal) -> Decimal {
let new_supply = current_supply - amount;
let base_value = amount * self.initial_price;
let incremental_value =
self.increment * amount * (dec!(2) * new_supply + amount - dec!(1)) / dec!(2);
(base_value + incremental_value) * dec!(0.95) }
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct BancorBondingCurve {
pub reserve_balance: Decimal,
pub reserve_ratio: Decimal,
pub initial_supply: Decimal,
}
impl BancorBondingCurve {
pub fn new(reserve_balance: Decimal, reserve_ratio: Decimal, initial_supply: Decimal) -> Self {
Self {
reserve_balance,
reserve_ratio: reserve_ratio.clamp(dec!(0.05), dec!(1)),
initial_supply,
}
}
pub fn get_buy_quote(&self, current_supply: Decimal, amount: Decimal) -> PriceQuote {
let total = self.buy_price(current_supply, amount);
let spot_before = self.spot_price(current_supply);
let spot_after = self.spot_price(current_supply + amount);
PriceQuote {
amount,
total_btc: total,
avg_price_btc: if amount > dec!(0) {
total / amount
} else {
dec!(0)
},
spot_price_before: spot_before,
spot_price_after: spot_after,
price_impact_percent: self.buy_price_impact(current_supply, amount),
}
}
pub fn get_sell_quote(&self, current_supply: Decimal, amount: Decimal) -> PriceQuote {
let total = self.sell_price(current_supply, amount);
let spot_before = self.spot_price(current_supply);
let spot_after = if current_supply > amount {
self.spot_price(current_supply - amount)
} else {
dec!(0)
};
PriceQuote {
amount,
total_btc: total,
avg_price_btc: if amount > dec!(0) {
total / amount
} else {
dec!(0)
},
spot_price_before: spot_before,
spot_price_after: spot_after,
price_impact_percent: self.sell_price_impact(current_supply, amount),
}
}
fn power_approx(base: Decimal, exponent: Decimal) -> Decimal {
let x = base - dec!(1);
if x.abs() < dec!(0.5) {
let term1 = dec!(1);
let term2 = exponent * x;
let term3 = exponent * (exponent - dec!(1)) * x * x / dec!(2);
let term4 =
exponent * (exponent - dec!(1)) * (exponent - dec!(2)) * x * x * x / dec!(6);
(term1 + term2 + term3 + term4).max(dec!(0.001))
} else {
let base_f64: f64 = base.try_into().unwrap_or(1.0);
let exp_f64: f64 = exponent.try_into().unwrap_or(1.0);
let result = base_f64.powf(exp_f64);
Decimal::try_from(result).unwrap_or(dec!(1))
}
}
}
impl BondingCurve for BancorBondingCurve {
fn spot_price(&self, current_supply: Decimal) -> Decimal {
let effective_supply = self.initial_supply + current_supply;
if effective_supply == dec!(0) || self.reserve_ratio == dec!(0) {
return dec!(0);
}
self.reserve_balance / (effective_supply * self.reserve_ratio)
}
fn buy_price(&self, current_supply: Decimal, amount: Decimal) -> Decimal {
if amount <= dec!(0) {
return dec!(0);
}
let effective_supply = self.initial_supply + current_supply;
if effective_supply == dec!(0) {
return dec!(0);
}
let supply_ratio = dec!(1) + amount / effective_supply;
let exponent = dec!(1) / self.reserve_ratio;
let power = Self::power_approx(supply_ratio, exponent);
self.reserve_balance * (power - dec!(1))
}
fn sell_price(&self, current_supply: Decimal, amount: Decimal) -> Decimal {
if amount <= dec!(0) || amount > current_supply {
return dec!(0);
}
let effective_supply = self.initial_supply + current_supply;
let supply_ratio = dec!(1) - amount / effective_supply;
let exponent = dec!(1) / self.reserve_ratio;
let power = Self::power_approx(supply_ratio, exponent);
self.reserve_balance * (dec!(1) - power) * dec!(0.95)
}
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct ExponentialBondingCurve {
pub initial_price: Decimal,
pub growth_rate: Decimal,
}
impl ExponentialBondingCurve {
pub fn new(initial_price: Decimal, growth_rate: Decimal) -> Self {
Self {
initial_price,
growth_rate: growth_rate.clamp(dec!(0.0001), dec!(0.1)),
}
}
pub fn get_buy_quote(&self, current_supply: Decimal, amount: Decimal) -> PriceQuote {
let total = self.buy_price(current_supply, amount);
let spot_before = self.spot_price(current_supply);
let spot_after = self.spot_price(current_supply + amount);
PriceQuote {
amount,
total_btc: total,
avg_price_btc: if amount > dec!(0) {
total / amount
} else {
dec!(0)
},
spot_price_before: spot_before,
spot_price_after: spot_after,
price_impact_percent: self.buy_price_impact(current_supply, amount),
}
}
pub fn get_sell_quote(&self, current_supply: Decimal, amount: Decimal) -> PriceQuote {
let total = self.sell_price(current_supply, amount);
let spot_before = self.spot_price(current_supply);
let spot_after = if current_supply > amount {
self.spot_price(current_supply - amount)
} else {
dec!(0)
};
PriceQuote {
amount,
total_btc: total,
avg_price_btc: if amount > dec!(0) {
total / amount
} else {
dec!(0)
},
spot_price_before: spot_before,
spot_price_after: spot_after,
price_impact_percent: self.sell_price_impact(current_supply, amount),
}
}
fn exp_approx(x: Decimal) -> Decimal {
let x2 = x * x;
let x3 = x2 * x;
let x4 = x3 * x;
let x5 = x4 * x;
let result = dec!(1) + x + x2 / dec!(2) + x3 / dec!(6) + x4 / dec!(24) + x5 / dec!(120);
result.max(dec!(0.0001))
}
}
impl BondingCurve for ExponentialBondingCurve {
fn spot_price(&self, current_supply: Decimal) -> Decimal {
let exponent = self.growth_rate * current_supply;
let clamped_exp = exponent.clamp(dec!(-10), dec!(10));
self.initial_price * Self::exp_approx(clamped_exp)
}
fn buy_price(&self, current_supply: Decimal, amount: Decimal) -> Decimal {
if amount <= dec!(0) || self.growth_rate == dec!(0) {
return self.initial_price * amount;
}
let k = self.growth_rate;
let exp_start = Self::exp_approx((k * current_supply).clamp(dec!(-10), dec!(10)));
let exp_end = Self::exp_approx((k * (current_supply + amount)).clamp(dec!(-10), dec!(10)));
(self.initial_price / k) * (exp_end - exp_start)
}
fn sell_price(&self, current_supply: Decimal, amount: Decimal) -> Decimal {
if amount <= dec!(0) || amount > current_supply || self.growth_rate == dec!(0) {
return self.initial_price * amount * dec!(0.95);
}
let new_supply = current_supply - amount;
let k = self.growth_rate;
let exp_start = Self::exp_approx((k * new_supply).clamp(dec!(-10), dec!(10)));
let exp_end = Self::exp_approx((k * current_supply).clamp(dec!(-10), dec!(10)));
(self.initial_price / k) * (exp_end - exp_start) * dec!(0.95)
}
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct SigmoidBondingCurve {
pub max_price: Decimal,
pub growth_rate: Decimal,
pub midpoint: Decimal,
}
impl SigmoidBondingCurve {
pub fn new(max_price: Decimal, growth_rate: Decimal, midpoint: Decimal) -> Self {
Self {
max_price,
growth_rate: growth_rate.clamp(dec!(0.001), dec!(0.1)),
midpoint,
}
}
pub fn get_buy_quote(&self, current_supply: Decimal, amount: Decimal) -> PriceQuote {
let total = self.buy_price(current_supply, amount);
let spot_before = self.spot_price(current_supply);
let spot_after = self.spot_price(current_supply + amount);
PriceQuote {
amount,
total_btc: total,
avg_price_btc: if amount > dec!(0) {
total / amount
} else {
dec!(0)
},
spot_price_before: spot_before,
spot_price_after: spot_after,
price_impact_percent: self.buy_price_impact(current_supply, amount),
}
}
pub fn get_sell_quote(&self, current_supply: Decimal, amount: Decimal) -> PriceQuote {
let total = self.sell_price(current_supply, amount);
let spot_before = self.spot_price(current_supply);
let spot_after = if current_supply > amount {
self.spot_price(current_supply - amount)
} else {
dec!(0)
};
PriceQuote {
amount,
total_btc: total,
avg_price_btc: if amount > dec!(0) {
total / amount
} else {
dec!(0)
},
spot_price_before: spot_before,
spot_price_after: spot_after,
price_impact_percent: self.sell_price_impact(current_supply, amount),
}
}
fn sigmoid(x: Decimal) -> Decimal {
let clamped = x.clamp(dec!(-10), dec!(10));
let exp_neg_x = ExponentialBondingCurve::exp_approx(-clamped);
dec!(1) / (dec!(1) + exp_neg_x)
}
}
impl BondingCurve for SigmoidBondingCurve {
fn spot_price(&self, current_supply: Decimal) -> Decimal {
let x = self.growth_rate * (current_supply - self.midpoint);
self.max_price * Self::sigmoid(x)
}
fn buy_price(&self, current_supply: Decimal, amount: Decimal) -> Decimal {
if amount <= dec!(0) {
return dec!(0);
}
let steps = 100i32;
let step_size = amount / Decimal::from(steps);
let mut total = dec!(0);
for i in 0..steps {
let n = current_supply + step_size * Decimal::from(i);
let price_start = self.spot_price(n);
let price_end = self.spot_price(n + step_size);
total += (price_start + price_end) / dec!(2) * step_size;
}
total
}
fn sell_price(&self, current_supply: Decimal, amount: Decimal) -> Decimal {
if amount <= dec!(0) || amount > current_supply {
return dec!(0);
}
let new_supply = current_supply - amount;
let steps = 100i32;
let step_size = amount / Decimal::from(steps);
let mut total = dec!(0);
for i in 0..steps {
let n = new_supply + step_size * Decimal::from(i);
let price_start = self.spot_price(n);
let price_end = self.spot_price(n + step_size);
total += (price_start + price_end) / dec!(2) * step_size;
}
total * dec!(0.95)
}
}
#[derive(Debug, Clone, Copy, Serialize, Deserialize, PartialEq, Eq)]
pub enum CurvePhase {
EarlyAdopter,
Growth,
Maturity,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct AdaptiveBondingCurve {
pub initial_price: Decimal,
pub max_price: Decimal,
pub phase1_threshold: Decimal,
pub phase2_threshold: Decimal,
pub total_supply: Decimal,
}
impl AdaptiveBondingCurve {
pub fn new(
initial_price: Decimal,
max_price: Decimal,
phase1_threshold: Decimal,
phase2_threshold: Decimal,
total_supply: Decimal,
) -> Self {
Self {
initial_price,
max_price,
phase1_threshold: phase1_threshold.max(dec!(1)),
phase2_threshold: phase2_threshold.max(phase1_threshold + dec!(1)),
total_supply,
}
}
pub fn with_defaults(
initial_price: Decimal,
max_price: Decimal,
total_supply: Decimal,
) -> Self {
Self::new(
initial_price,
max_price,
total_supply * dec!(0.1), total_supply * dec!(0.5), total_supply,
)
}
pub fn get_phase(&self, current_supply: Decimal) -> CurvePhase {
if current_supply < self.phase1_threshold {
CurvePhase::EarlyAdopter
} else if current_supply < self.phase2_threshold {
CurvePhase::Growth
} else {
CurvePhase::Maturity
}
}
fn price_at_phase1(&self) -> Decimal {
self.early_adopter_price(self.phase1_threshold)
}
fn price_at_phase2(&self) -> Decimal {
self.growth_price(self.phase2_threshold)
}
fn early_adopter_price(&self, supply: Decimal) -> Decimal {
if supply <= dec!(0) {
return self.initial_price;
}
let growth_per_unit = dec!(1.1) / self.phase1_threshold.max(dec!(1));
let multiplier = dec!(1) + supply * growth_per_unit;
self.initial_price * multiplier.min(dec!(3))
}
fn growth_price(&self, supply: Decimal) -> Decimal {
let start_price = self.price_at_phase1();
let target_price = self.max_price * dec!(0.7);
let supply_in_phase = supply - self.phase1_threshold;
let phase_length = self.phase2_threshold - self.phase1_threshold;
if phase_length <= dec!(0) {
return start_price;
}
let progress = supply_in_phase / phase_length;
start_price + (target_price - start_price) * progress.min(dec!(1))
}
fn maturity_price(&self, supply: Decimal) -> Decimal {
let start_price = self.price_at_phase2();
let remaining_supply = self.total_supply - self.phase2_threshold;
if remaining_supply <= dec!(0) {
return start_price;
}
let supply_in_phase = supply - self.phase2_threshold;
let progress = supply_in_phase / remaining_supply;
let sigmoid_progress = if progress < dec!(0.5) {
dec!(2) * progress * progress
} else {
dec!(1) - dec!(2) * (dec!(1) - progress) * (dec!(1) - progress)
};
start_price + (self.max_price - start_price) * sigmoid_progress.min(dec!(1))
}
pub fn get_buy_quote(&self, current_supply: Decimal, amount: Decimal) -> PriceQuote {
let total = self.buy_price(current_supply, amount);
let spot_before = self.spot_price(current_supply);
let spot_after = self.spot_price(current_supply + amount);
PriceQuote {
amount,
total_btc: total,
avg_price_btc: if amount > dec!(0) {
total / amount
} else {
dec!(0)
},
spot_price_before: spot_before,
spot_price_after: spot_after,
price_impact_percent: self.buy_price_impact(current_supply, amount),
}
}
pub fn get_sell_quote(&self, current_supply: Decimal, amount: Decimal) -> PriceQuote {
let total = self.sell_price(current_supply, amount);
let spot_before = self.spot_price(current_supply);
let spot_after = if current_supply > amount {
self.spot_price(current_supply - amount)
} else {
dec!(0)
};
PriceQuote {
amount,
total_btc: total,
avg_price_btc: if amount > dec!(0) {
total / amount
} else {
dec!(0)
},
spot_price_before: spot_before,
spot_price_after: spot_after,
price_impact_percent: self.sell_price_impact(current_supply, amount),
}
}
}
impl BondingCurve for AdaptiveBondingCurve {
fn spot_price(&self, current_supply: Decimal) -> Decimal {
match self.get_phase(current_supply) {
CurvePhase::EarlyAdopter => self.early_adopter_price(current_supply),
CurvePhase::Growth => self.growth_price(current_supply),
CurvePhase::Maturity => self.maturity_price(current_supply),
}
}
fn buy_price(&self, current_supply: Decimal, amount: Decimal) -> Decimal {
if amount <= dec!(0) {
return dec!(0);
}
let steps = 100i32;
let step_size = amount / Decimal::from(steps);
let mut total = dec!(0);
for i in 0..steps {
let n = current_supply + step_size * Decimal::from(i);
let price_start = self.spot_price(n);
let price_end = self.spot_price(n + step_size);
total += (price_start + price_end) / dec!(2) * step_size;
}
total
}
fn sell_price(&self, current_supply: Decimal, amount: Decimal) -> Decimal {
if amount <= dec!(0) || amount > current_supply {
return dec!(0);
}
let new_supply = current_supply - amount;
let steps = 100i32;
let step_size = amount / Decimal::from(steps);
let mut total = dec!(0);
for i in 0..steps {
let n = new_supply + step_size * Decimal::from(i);
let price_start = self.spot_price(n);
let price_end = self.spot_price(n + step_size);
total += (price_start + price_end) / dec!(2) * step_size;
}
total * dec!(0.95)
}
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct SquareRootBondingCurve {
pub initial_price: Decimal,
pub scale_factor: Decimal,
}
impl SquareRootBondingCurve {
pub fn new(initial_price: Decimal, scale_factor: Decimal) -> Self {
Self {
initial_price,
scale_factor: scale_factor.max(dec!(1)),
}
}
pub fn get_buy_quote(&self, current_supply: Decimal, amount: Decimal) -> PriceQuote {
let total = self.buy_price(current_supply, amount);
let spot_before = self.spot_price(current_supply);
let spot_after = self.spot_price(current_supply + amount);
PriceQuote {
amount,
total_btc: total,
avg_price_btc: if amount > dec!(0) {
total / amount
} else {
dec!(0)
},
spot_price_before: spot_before,
spot_price_after: spot_after,
price_impact_percent: self.buy_price_impact(current_supply, amount),
}
}
pub fn get_sell_quote(&self, current_supply: Decimal, amount: Decimal) -> PriceQuote {
let total = self.sell_price(current_supply, amount);
let spot_before = self.spot_price(current_supply);
let spot_after = if current_supply > amount {
self.spot_price(current_supply - amount)
} else {
dec!(0)
};
PriceQuote {
amount,
total_btc: total,
avg_price_btc: if amount > dec!(0) {
total / amount
} else {
dec!(0)
},
spot_price_before: spot_before,
spot_price_after: spot_after,
price_impact_percent: self.sell_price_impact(current_supply, amount),
}
}
fn sqrt_approx(x: Decimal) -> Decimal {
if x <= dec!(0) {
return dec!(0);
}
if x == dec!(1) {
return dec!(1);
}
let x_f64: f64 = x.try_into().unwrap_or(1.0);
let result = x_f64.sqrt();
Decimal::try_from(result).unwrap_or(dec!(1))
}
fn sqrt_integral(&self, supply: Decimal, amount: Decimal) -> Decimal {
let init: f64 = self.initial_price.try_into().unwrap_or(0.0);
let k: f64 = self.scale_factor.try_into().unwrap_or(1.0);
let s0: f64 = supply.try_into().unwrap_or(0.0);
let amt: f64 = amount.try_into().unwrap_or(0.0);
if k <= 0.0 || amt <= 0.0 {
return dec!(0);
}
let f_upper = (1.0 + (s0 + amt) / k).powf(1.5);
let f_lower = (1.0 + s0 / k).powf(1.5);
let total = init * (2.0 * k / 3.0) * (f_upper - f_lower);
Decimal::try_from(total.max(0.0)).unwrap_or(dec!(0))
}
}
impl BondingCurve for SquareRootBondingCurve {
fn spot_price(&self, current_supply: Decimal) -> Decimal {
let normalized = dec!(1) + current_supply / self.scale_factor;
self.initial_price * Self::sqrt_approx(normalized)
}
fn buy_price(&self, current_supply: Decimal, amount: Decimal) -> Decimal {
if amount <= dec!(0) {
return dec!(0);
}
self.sqrt_integral(current_supply, amount)
}
fn sell_price(&self, current_supply: Decimal, amount: Decimal) -> Decimal {
if amount <= dec!(0) || amount > current_supply {
return dec!(0);
}
self.sqrt_integral(current_supply - amount, amount) * dec!(0.95)
}
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct LogarithmicBondingCurve {
pub initial_price: Decimal,
pub scale_factor: Decimal,
pub log_base: Decimal,
}
impl LogarithmicBondingCurve {
pub fn new(initial_price: Decimal, scale_factor: Decimal, log_base: Decimal) -> Self {
Self {
initial_price,
scale_factor: scale_factor.max(dec!(1)),
log_base: log_base.clamp(dec!(1.1), dec!(10)),
}
}
pub fn new_natural_log(initial_price: Decimal, scale_factor: Decimal) -> Self {
let e = Decimal::try_from(std::f64::consts::E).unwrap_or(dec!(2.71828182845904523536));
Self::new(initial_price, scale_factor, e)
}
pub fn get_buy_quote(&self, current_supply: Decimal, amount: Decimal) -> PriceQuote {
let total = self.buy_price(current_supply, amount);
let spot_before = self.spot_price(current_supply);
let spot_after = self.spot_price(current_supply + amount);
PriceQuote {
amount,
total_btc: total,
avg_price_btc: if amount > dec!(0) {
total / amount
} else {
dec!(0)
},
spot_price_before: spot_before,
spot_price_after: spot_after,
price_impact_percent: self.buy_price_impact(current_supply, amount),
}
}
pub fn get_sell_quote(&self, current_supply: Decimal, amount: Decimal) -> PriceQuote {
let total = self.sell_price(current_supply, amount);
let spot_before = self.spot_price(current_supply);
let spot_after = if current_supply > amount {
self.spot_price(current_supply - amount)
} else {
dec!(0)
};
PriceQuote {
amount,
total_btc: total,
avg_price_btc: if amount > dec!(0) {
total / amount
} else {
dec!(0)
},
spot_price_before: spot_before,
spot_price_after: spot_after,
price_impact_percent: self.sell_price_impact(current_supply, amount),
}
}
fn log_approx(x: Decimal, base: Decimal) -> Decimal {
if x <= dec!(0) {
return dec!(0);
}
if x == dec!(1) {
return dec!(0);
}
let x_f64: f64 = x.try_into().unwrap_or(1.0);
let base_f64: f64 = base.try_into().unwrap_or(std::f64::consts::E);
let result = x_f64.ln() / base_f64.ln();
Decimal::try_from(result).unwrap_or(dec!(0))
}
fn log_integral(&self, supply: Decimal, amount: Decimal) -> Decimal {
let init: f64 = self.initial_price.try_into().unwrap_or(0.0);
let k: f64 = self.scale_factor.try_into().unwrap_or(1.0);
let s0: f64 = supply.try_into().unwrap_or(0.0);
let amt: f64 = amount.try_into().unwrap_or(0.0);
let log_b: f64 = self.log_base.try_into().unwrap_or(std::f64::consts::E);
if k <= 0.0 || amt <= 0.0 || log_b <= 0.0 || log_b == 1.0 {
return dec!(0);
}
let ln_b = log_b.ln();
let antiderivative = |s: f64| -> f64 {
let arg = 1.0 + s / k;
if arg <= 0.0 {
return 0.0;
}
s + (s + k) / ln_b * (arg.ln() - 1.0)
};
let total = init * (antiderivative(s0 + amt) - antiderivative(s0));
Decimal::try_from(total.max(0.0)).unwrap_or(dec!(0))
}
}
impl BondingCurve for LogarithmicBondingCurve {
fn spot_price(&self, current_supply: Decimal) -> Decimal {
let normalized = dec!(1) + current_supply / self.scale_factor;
let log_term = Self::log_approx(normalized, self.log_base);
self.initial_price * (dec!(1) + log_term)
}
fn buy_price(&self, current_supply: Decimal, amount: Decimal) -> Decimal {
if amount <= dec!(0) {
return dec!(0);
}
self.log_integral(current_supply, amount)
}
fn sell_price(&self, current_supply: Decimal, amount: Decimal) -> Decimal {
if amount <= dec!(0) || amount > current_supply {
return dec!(0);
}
self.log_integral(current_supply - amount, amount) * dec!(0.95)
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_linear_spot_price() {
let curve = LinearBondingCurve::new(dec!(0.0001), dec!(0.00001));
assert_eq!(curve.spot_price(dec!(0)), dec!(0.0001));
assert_eq!(curve.spot_price(dec!(100)), dec!(0.0011));
}
#[test]
fn test_linear_buy_price() {
let curve = LinearBondingCurve::new(dec!(0.0001), dec!(0.00001));
let cost = curve.buy_price(dec!(0), dec!(10));
assert_eq!(cost, dec!(0.00145));
}
#[test]
fn test_linear_sell_price_less_than_buy() {
let curve = LinearBondingCurve::new(dec!(0.0001), dec!(0.00001));
let buy_cost = curve.buy_price(dec!(0), dec!(10));
let sell_proceeds = curve.sell_price(dec!(10), dec!(10));
assert!(sell_proceeds < buy_cost);
assert!(sell_proceeds > buy_cost * dec!(0.9)); }
#[test]
fn test_bancor_spot_price() {
let curve = BancorBondingCurve::new(dec!(0.1), dec!(0.5), dec!(1000));
let price = curve.spot_price(dec!(0));
assert_eq!(price, dec!(0.0002));
}
#[test]
fn test_exponential_increasing_price() {
let curve = ExponentialBondingCurve::new(dec!(0.0001), dec!(0.001));
let price_0 = curve.spot_price(dec!(0));
let price_100 = curve.spot_price(dec!(100));
let price_1000 = curve.spot_price(dec!(1000));
assert!(price_100 > price_0);
assert!(price_1000 > price_100);
}
#[test]
fn test_sigmoid_approaches_max() {
let curve = SigmoidBondingCurve::new(dec!(1), dec!(0.01), dec!(500));
let price_0 = curve.spot_price(dec!(0));
let price_500 = curve.spot_price(dec!(500));
let price_1000 = curve.spot_price(dec!(1000));
assert!(price_500 > dec!(0.4));
assert!(price_500 < dec!(0.6));
assert!(price_1000 > price_500);
assert!(price_1000 < dec!(1));
assert!(price_0 < dec!(0.1));
}
#[test]
fn test_adaptive_phase_transitions() {
let curve = AdaptiveBondingCurve::with_defaults(
dec!(0.0001), dec!(0.01), dec!(1000), );
assert_eq!(curve.get_phase(dec!(50)), CurvePhase::EarlyAdopter);
assert_eq!(curve.get_phase(dec!(100)), CurvePhase::Growth);
assert_eq!(curve.get_phase(dec!(300)), CurvePhase::Growth);
assert_eq!(curve.get_phase(dec!(500)), CurvePhase::Maturity);
assert_eq!(curve.get_phase(dec!(800)), CurvePhase::Maturity);
}
#[test]
fn test_adaptive_price_increases() {
let curve = AdaptiveBondingCurve::with_defaults(
dec!(0.0001), dec!(0.01), dec!(1000), );
let price_0 = curve.spot_price(dec!(0));
let price_50 = curve.spot_price(dec!(50));
let price_100 = curve.spot_price(dec!(100));
let price_300 = curve.spot_price(dec!(300));
let price_500 = curve.spot_price(dec!(500));
let price_900 = curve.spot_price(dec!(900));
assert!(price_50 > price_0);
assert!(price_100 > price_50);
assert!(price_300 > price_100);
assert!(price_500 > price_300);
assert!(price_900 > price_500);
assert!(price_900 < dec!(0.01));
}
#[test]
fn test_adaptive_sell_less_than_buy() {
let curve = AdaptiveBondingCurve::with_defaults(dec!(0.0001), dec!(0.01), dec!(1000));
let buy_cost = curve.buy_price(dec!(100), dec!(50));
let sell_proceeds = curve.sell_price(dec!(150), dec!(50));
assert!(sell_proceeds < buy_cost);
assert!(sell_proceeds > buy_cost * dec!(0.9));
}
#[test]
fn test_sqrt_curve_gentler_growth() {
let curve = SquareRootBondingCurve::new(dec!(0.0001), dec!(100));
let price_0 = curve.spot_price(dec!(0));
let price_100 = curve.spot_price(dec!(100));
let price_400 = curve.spot_price(dec!(400));
assert!(price_100 > price_0);
assert!(price_400 > price_100);
let growth_100 = price_100 / price_0;
let growth_400 = price_400 / price_0;
assert!(growth_400 < growth_100 * dec!(2));
}
#[test]
fn test_sqrt_sell_less_than_buy() {
let curve = SquareRootBondingCurve::new(dec!(0.0001), dec!(100));
let buy_cost = curve.buy_price(dec!(100), dec!(50));
let sell_proceeds = curve.sell_price(dec!(150), dec!(50));
assert!(sell_proceeds < buy_cost);
assert!(sell_proceeds > buy_cost * dec!(0.9));
}
#[test]
fn test_log_curve_basic_properties() {
let curve = LogarithmicBondingCurve::new_natural_log(dec!(0.0001), dec!(100));
let price_0 = curve.spot_price(dec!(0));
let price_100 = curve.spot_price(dec!(100));
let price_1000 = curve.spot_price(dec!(1000));
assert!(price_100 > price_0);
assert!(price_1000 > price_100);
assert!(price_0 > dec!(0));
assert!(price_100 > dec!(0));
assert!(price_1000 > dec!(0));
}
#[test]
fn test_log_sell_less_than_buy() {
let curve = LogarithmicBondingCurve::new_natural_log(dec!(0.0001), dec!(100));
let buy_cost = curve.buy_price(dec!(100), dec!(50));
let sell_proceeds = curve.sell_price(dec!(150), dec!(50));
assert!(sell_proceeds < buy_cost);
assert!(sell_proceeds > buy_cost * dec!(0.9));
}
#[test]
fn test_sqrt_curve_basic_properties() {
let curve = SquareRootBondingCurve::new(dec!(0.0001), dec!(100));
let price_0 = curve.spot_price(dec!(0));
let price_100 = curve.spot_price(dec!(100));
let price_400 = curve.spot_price(dec!(400));
assert!(price_100 > price_0);
assert!(price_400 > price_100);
assert!(price_0 > dec!(0));
assert!(price_100 > dec!(0));
assert!(price_400 > dec!(0));
}
#[test]
fn test_closed_form_matches_numerical_sqrt() {
let curve = SquareRootBondingCurve::new(dec!(0.0001), dec!(100));
let test_cases: &[(Decimal, Decimal)] = &[
(dec!(0), dec!(50)),
(dec!(100), dec!(50)),
(dec!(500), dec!(100)),
(dec!(1000), dec!(200)),
];
for &(supply, amount) in test_cases {
let steps = 100i32;
let step_size = amount / Decimal::from(steps);
let mut numerical = dec!(0);
for i in 0..steps {
let n = supply + step_size * Decimal::from(i);
let price_start = curve.spot_price(n);
let price_end = curve.spot_price(n + step_size);
numerical += (price_start + price_end) / dec!(2) * step_size;
}
let closed_form = curve.buy_price(supply, amount);
let relative_error = if numerical > dec!(0) {
((closed_form - numerical) / numerical).abs()
} else {
dec!(0)
};
assert!(
relative_error < dec!(0.0001),
"sqrt closed-form vs numerical: supply={supply}, amount={amount}, \
closed={closed_form}, numerical={numerical}, rel_err={relative_error}"
);
}
}
#[test]
fn test_closed_form_matches_numerical_log() {
let curve = LogarithmicBondingCurve::new_natural_log(dec!(0.0001), dec!(100));
let test_cases: &[(Decimal, Decimal)] = &[
(dec!(0), dec!(50)),
(dec!(100), dec!(50)),
(dec!(500), dec!(100)),
(dec!(1000), dec!(200)),
];
for &(supply, amount) in test_cases {
let steps = 100i32;
let step_size = amount / Decimal::from(steps);
let mut numerical = dec!(0);
for i in 0..steps {
let n = supply + step_size * Decimal::from(i);
let price_start = curve.spot_price(n);
let price_end = curve.spot_price(n + step_size);
numerical += (price_start + price_end) / dec!(2) * step_size;
}
let closed_form = curve.buy_price(supply, amount);
let relative_error = if numerical > dec!(0) {
((closed_form - numerical) / numerical).abs()
} else {
dec!(0)
};
assert!(
relative_error < dec!(0.0001),
"log closed-form vs numerical: supply={supply}, amount={amount}, \
closed={closed_form}, numerical={numerical}, rel_err={relative_error}"
);
}
}
}