use cosmwasm_std::Uint128;
const LI_TOTAL_SUPPLY_TOKENS: u128 = 1_000_000_000_000_000;
const LI_DECIMALS: u128 = 1_000_000;
pub const LI_TOTAL_SUPPLY_ATOMIC: u128 = LI_TOTAL_SUPPLY_TOKENS * LI_DECIMALS;
pub const COMPONENT_ALLOC_ATOMIC: u128 = LI_TOTAL_SUPPLY_ATOMIC / 7;
const MAIN_PHASE_SHARE_NUM: f64 = 0.9;
const TAIL_MONTHLY_SHARE: f64 = 0.01;
const MONTH_DAYS: f64 = 30.0;
const INFINITE_COMPONENT_YEARS: f64 = 20.0;
pub const SECONDS_PER_DAY: f64 = 86400.0;
#[derive(Debug, Clone, Copy)]
struct FiniteProfile {
alloc: f64,
lambda: f64,
tail_k: f64,
crossover_t: f64,
emitted_at_crossover: f64,
remaining_at_crossover: f64,
}
pub fn emission_rate_per_second(genesis_time: u64, now: u64) -> Uint128 {
let elapsed_seconds = now.saturating_sub(genesis_time) as f64;
let rate = total_rate_at_time(elapsed_seconds);
let atomic = f64_to_u128_saturating(rate);
Uint128::from(atomic)
}
pub fn total_emitted_at_time(genesis_time: u64, now: u64) -> Uint128 {
let elapsed_seconds = now.saturating_sub(genesis_time) as f64;
let emitted = total_emitted_atomic(elapsed_seconds);
Uint128::from(f64_to_u128_saturating(emitted))
}
pub fn total_rate_at_time(elapsed_seconds: f64) -> f64 {
finite_component_rate(elapsed_seconds, 1.0)
+ finite_component_rate(elapsed_seconds, 7.0)
+ finite_component_rate(elapsed_seconds, 30.0)
+ finite_component_rate(elapsed_seconds, 90.0)
+ finite_component_rate(elapsed_seconds, 365.0)
+ finite_component_rate(elapsed_seconds, 1461.0)
+ infinite_component_rate(elapsed_seconds)
}
fn total_emitted_atomic(elapsed_seconds: f64) -> f64 {
finite_component_emitted(elapsed_seconds, 1.0)
+ finite_component_emitted(elapsed_seconds, 7.0)
+ finite_component_emitted(elapsed_seconds, 30.0)
+ finite_component_emitted(elapsed_seconds, 90.0)
+ finite_component_emitted(elapsed_seconds, 365.0)
+ finite_component_emitted(elapsed_seconds, 1461.0)
+ infinite_component_emitted(elapsed_seconds)
}
fn finite_component_emitted(elapsed_seconds: f64, period_days: f64) -> f64 {
let t = elapsed_seconds.max(0.0);
let p = finite_profile(period_days);
if t <= p.crossover_t {
MAIN_PHASE_SHARE_NUM * p.alloc * (1.0 - f64::exp(-p.lambda * t))
} else {
p.emitted_at_crossover
+ p.remaining_at_crossover * (1.0 - f64::exp(-p.tail_k * (t - p.crossover_t)))
}
}
pub fn finite_component_rate(elapsed_seconds: f64, period_days: f64) -> f64 {
let t = elapsed_seconds.max(0.0);
let p = finite_profile(period_days);
if t <= p.crossover_t {
MAIN_PHASE_SHARE_NUM * p.alloc * p.lambda * f64::exp(-p.lambda * t)
} else {
p.remaining_at_crossover * p.tail_k * f64::exp(-p.tail_k * (t - p.crossover_t))
}
}
fn infinite_component_emitted(elapsed_seconds: f64) -> f64 {
let alloc = COMPONENT_ALLOC_ATOMIC as f64;
let duration = infinite_component_duration_seconds();
let t = elapsed_seconds.max(0.0).min(duration);
alloc * (t / duration)
}
pub fn infinite_component_rate(elapsed_seconds: f64) -> f64 {
if elapsed_seconds >= infinite_component_duration_seconds() {
return 0.0;
}
let alloc = COMPONENT_ALLOC_ATOMIC as f64;
alloc / infinite_component_duration_seconds()
}
fn finite_profile(period_days: f64) -> FiniteProfile {
let alloc = COMPONENT_ALLOC_ATOMIC as f64;
let period_seconds = period_days * SECONDS_PER_DAY;
let lambda = f64::ln(10.0) / period_seconds;
let tail_k = TAIL_MONTHLY_SHARE / (MONTH_DAYS * SECONDS_PER_DAY);
let x = (tail_k / (9.0 * (lambda - tail_k))).clamp(0.0, 1.0);
let crossover_t = if x > 0.0 { -x.ln() / lambda } else { 0.0 };
let emitted_at_crossover =
MAIN_PHASE_SHARE_NUM * alloc * (1.0 - f64::exp(-lambda * crossover_t));
let remaining_at_crossover = (alloc - emitted_at_crossover).max(0.0);
FiniteProfile {
alloc,
lambda,
tail_k,
crossover_t,
emitted_at_crossover,
remaining_at_crossover,
}
}
fn infinite_component_duration_seconds() -> f64 {
INFINITE_COMPONENT_YEARS * 365.0 * SECONDS_PER_DAY
}
fn f64_to_u128_saturating(v: f64) -> u128 {
if !v.is_finite() || v <= 0.0 {
return 0;
}
if v >= u128::MAX as f64 {
return u128::MAX;
}
v.floor() as u128
}