use crate::error::CoreError;
use rust_decimal::Decimal;
use serde::{Deserialize, Serialize};
use std::time::{Duration, SystemTime};
#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
pub enum RebasePolicy {
TargetPrice,
TargetMarketCap,
VolatilityDampening,
Custom,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
pub enum SupplyChangeDirection {
Expansion,
Contraction,
NoChange,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct RebaseConfig {
pub target_price: Decimal,
pub rebase_interval: Duration,
pub max_rebase_percentage: Decimal,
pub min_rebase_percentage: Decimal,
pub policy: RebasePolicy,
pub dampening_factor: Decimal,
}
impl Default for RebaseConfig {
fn default() -> Self {
Self {
target_price: Decimal::ONE, rebase_interval: Duration::from_secs(86400), max_rebase_percentage: Decimal::new(10, 0), min_rebase_percentage: Decimal::new(1, 1), policy: RebasePolicy::TargetPrice,
dampening_factor: Decimal::new(5, 1), }
}
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct RebaseEvent {
pub event_id: String,
pub token_id: String,
pub timestamp: SystemTime,
pub old_supply: Decimal,
pub new_supply: Decimal,
pub supply_change: Decimal,
pub supply_change_percentage: Decimal,
pub direction: SupplyChangeDirection,
pub current_price: Decimal,
pub target_price: Decimal,
}
pub struct ElasticSupplyManager {
config: RebaseConfig,
last_rebase_time: SystemTime,
rebase_history: Vec<RebaseEvent>,
event_counter: u64,
}
impl ElasticSupplyManager {
pub fn new(config: RebaseConfig) -> Self {
Self {
config,
last_rebase_time: SystemTime::now(),
rebase_history: Vec::new(),
event_counter: 0,
}
}
pub fn with_defaults() -> Self {
Self::new(RebaseConfig::default())
}
pub fn calculate_rebase(
&self,
current_price: Decimal,
current_supply: Decimal,
) -> Result<RebaseCalculation, CoreError> {
let price_deviation = (current_price - self.config.target_price) / self.config.target_price;
let raw_rebase_pct = price_deviation * self.config.dampening_factor;
let capped_rebase_pct = if raw_rebase_pct.abs() > self.config.max_rebase_percentage {
if raw_rebase_pct.is_sign_positive() {
self.config.max_rebase_percentage
} else {
-self.config.max_rebase_percentage
}
} else if raw_rebase_pct.abs() < self.config.min_rebase_percentage {
Decimal::ZERO
} else {
raw_rebase_pct
};
let direction = if capped_rebase_pct > Decimal::ZERO {
SupplyChangeDirection::Expansion
} else if capped_rebase_pct < Decimal::ZERO {
SupplyChangeDirection::Contraction
} else {
SupplyChangeDirection::NoChange
};
let supply_change = current_supply * capped_rebase_pct / Decimal::new(100, 0);
let new_supply = current_supply + supply_change;
Ok(RebaseCalculation {
old_supply: current_supply,
new_supply,
supply_change,
supply_change_percentage: capped_rebase_pct,
direction,
price_deviation,
})
}
pub fn is_rebase_due(&self) -> bool {
let elapsed = SystemTime::now()
.duration_since(self.last_rebase_time)
.unwrap_or(Duration::ZERO);
elapsed >= self.config.rebase_interval
}
pub fn execute_rebase(
&mut self,
token_id: String,
current_price: Decimal,
current_supply: Decimal,
) -> Result<RebaseEvent, CoreError> {
if !self.is_rebase_due() {
return Err(CoreError::InvalidState(
"Rebase interval has not elapsed".to_string(),
));
}
let calculation = self.calculate_rebase(current_price, current_supply)?;
if calculation.direction == SupplyChangeDirection::NoChange {
return Err(CoreError::InvalidState(
"Price deviation too small for rebase".to_string(),
));
}
self.event_counter += 1;
let event = RebaseEvent {
event_id: format!("rebase_{}", self.event_counter),
token_id,
timestamp: SystemTime::now(),
old_supply: calculation.old_supply,
new_supply: calculation.new_supply,
supply_change: calculation.supply_change,
supply_change_percentage: calculation.supply_change_percentage,
direction: calculation.direction,
current_price,
target_price: self.config.target_price,
};
self.last_rebase_time = SystemTime::now();
self.rebase_history.push(event.clone());
Ok(event)
}
pub fn get_rebase_history(&self) -> &[RebaseEvent] {
&self.rebase_history
}
pub fn time_until_next_rebase(&self) -> Duration {
let elapsed = SystemTime::now()
.duration_since(self.last_rebase_time)
.unwrap_or(Duration::ZERO);
self.config.rebase_interval.saturating_sub(elapsed)
}
pub fn calculate_holder_balance(
&self,
initial_balance: Decimal,
since_timestamp: SystemTime,
) -> Decimal {
let mut effective_balance = initial_balance;
for event in &self.rebase_history {
if event.timestamp >= since_timestamp {
let rebase_multiplier = event.new_supply / event.old_supply;
effective_balance *= rebase_multiplier;
}
}
effective_balance
}
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct RebaseCalculation {
pub old_supply: Decimal,
pub new_supply: Decimal,
pub supply_change: Decimal,
pub supply_change_percentage: Decimal,
pub direction: SupplyChangeDirection,
pub price_deviation: Decimal,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct SupplyExpansionRules {
pub max_total_supply: Option<Decimal>,
pub expansion_triggers: Vec<ExpansionTrigger>,
pub max_expansion_per_day: Decimal,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub enum ExpansionTrigger {
PriceThreshold {
threshold: Decimal,
},
VolumeThreshold {
threshold: Decimal,
},
LiquidityRatio {
min_ratio: Decimal,
},
Scheduled {
interval: Duration,
},
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct SupplyContractionRules {
pub min_total_supply: Option<Decimal>,
pub contraction_triggers: Vec<ContractionTrigger>,
pub max_contraction_per_day: Decimal,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub enum ContractionTrigger {
PriceThreshold {
threshold: Decimal,
},
VolumeThreshold {
threshold: Decimal,
},
TransactionBurn {
burn_rate: Decimal,
},
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_rebase_expansion() {
let manager = ElasticSupplyManager::with_defaults();
let current_supply = Decimal::new(1000000, 0);
let current_price = Decimal::new(15, 1);
let calculation = manager
.calculate_rebase(current_price, current_supply)
.unwrap();
assert_eq!(calculation.direction, SupplyChangeDirection::Expansion);
assert!(calculation.new_supply > calculation.old_supply);
assert!(calculation.supply_change > Decimal::ZERO);
}
#[test]
fn test_rebase_contraction() {
let manager = ElasticSupplyManager::with_defaults();
let current_supply = Decimal::new(1000000, 0);
let current_price = Decimal::new(7, 1);
let calculation = manager
.calculate_rebase(current_price, current_supply)
.unwrap();
assert_eq!(calculation.direction, SupplyChangeDirection::Contraction);
assert!(calculation.new_supply < calculation.old_supply);
assert!(calculation.supply_change < Decimal::ZERO);
}
#[test]
fn test_rebase_no_change() {
let manager = ElasticSupplyManager::with_defaults();
let current_supply = Decimal::new(1000000, 0);
let current_price = Decimal::new(10005, 4);
let calculation = manager
.calculate_rebase(current_price, current_supply)
.unwrap();
assert_eq!(calculation.direction, SupplyChangeDirection::NoChange);
assert_eq!(calculation.supply_change, Decimal::ZERO);
}
#[test]
fn test_rebase_capping() {
let manager = ElasticSupplyManager::with_defaults();
let current_supply = Decimal::new(1000000, 0);
let current_price = Decimal::new(100, 0);
let calculation = manager
.calculate_rebase(current_price, current_supply)
.unwrap();
assert_eq!(calculation.supply_change_percentage, Decimal::new(10, 0));
}
#[test]
fn test_is_rebase_due() {
let manager = ElasticSupplyManager::with_defaults();
assert!(!manager.is_rebase_due());
}
#[test]
fn test_holder_balance_calculation() {
let mut manager = ElasticSupplyManager::with_defaults();
let initial_time = SystemTime::now() - Duration::from_secs(10);
let event = RebaseEvent {
event_id: "test_rebase_1".to_string(),
token_id: "token1".to_string(),
timestamp: SystemTime::now(),
old_supply: Decimal::new(1000000, 0),
new_supply: Decimal::new(2000000, 0),
supply_change: Decimal::new(1000000, 0),
supply_change_percentage: Decimal::new(100, 0),
direction: SupplyChangeDirection::Expansion,
current_price: Decimal::new(2, 0),
target_price: Decimal::ONE,
};
manager.rebase_history.push(event);
let initial_balance = Decimal::new(100, 0);
let effective_balance = manager.calculate_holder_balance(initial_balance, initial_time);
assert_eq!(effective_balance, Decimal::new(200, 0));
}
#[test]
fn test_execute_rebase_not_due() {
let mut manager = ElasticSupplyManager::with_defaults();
let result = manager.execute_rebase(
"token1".to_string(),
Decimal::new(15, 1),
Decimal::new(1000000, 0),
);
assert!(result.is_err());
}
}