Merge commit 'd803bfe2b1fe7f5e219e50ac20d6801a0a58ac75' as 'vendor/ruvector'
This commit is contained in:
245
vendor/ruvector/crates/ruvector-economy-wasm/src/curve.rs
vendored
Normal file
245
vendor/ruvector/crates/ruvector-economy-wasm/src/curve.rs
vendored
Normal file
@@ -0,0 +1,245 @@
|
||||
//! Contribution Curve for Early Adopter Rewards
|
||||
//!
|
||||
//! Implements an exponential decay curve that rewards early network participants
|
||||
//! with higher multipliers that decay as the network grows.
|
||||
//!
|
||||
//! ```text
|
||||
//! Multiplier
|
||||
//! 10x |*
|
||||
//! | *
|
||||
//! 8x | *
|
||||
//! | *
|
||||
//! 6x | *
|
||||
//! | *
|
||||
//! 4x | *
|
||||
//! | **
|
||||
//! 2x | ***
|
||||
//! | *****
|
||||
//! 1x | ****************************
|
||||
//! +--+--+--+--+--+--+--+--+--+--+--+--+--+--+---> Network Compute (M hours)
|
||||
//! 0 1 2 3 4 5 6 7 8 9 10
|
||||
//! ```
|
||||
|
||||
use wasm_bindgen::prelude::*;
|
||||
|
||||
/// Contribution curve calculator for early adopter rewards
|
||||
///
|
||||
/// The multiplier follows an exponential decay formula:
|
||||
/// ```text
|
||||
/// multiplier = 1 + (MAX_BONUS - 1) * e^(-network_compute / DECAY_CONSTANT)
|
||||
/// ```
|
||||
///
|
||||
/// This ensures:
|
||||
/// - Genesis contributors (0 compute) get MAX_BONUS (10x)
|
||||
/// - At DECAY_CONSTANT compute hours, bonus is ~37% remaining (~4.3x)
|
||||
/// - At very high compute, approaches baseline (1x)
|
||||
/// - Never goes below 1x
|
||||
pub struct ContributionCurve;
|
||||
|
||||
impl ContributionCurve {
|
||||
/// Maximum multiplier for genesis contributors
|
||||
pub const MAX_BONUS: f32 = 10.0;
|
||||
|
||||
/// Decay constant in CPU-hours (half-life of bonus decay)
|
||||
pub const DECAY_CONSTANT: f64 = 1_000_000.0;
|
||||
|
||||
/// Calculate current multiplier based on total network compute
|
||||
///
|
||||
/// # Arguments
|
||||
/// * `network_compute_hours` - Total CPU-hours contributed to the network
|
||||
///
|
||||
/// # Returns
|
||||
/// A multiplier between 1.0 (baseline) and MAX_BONUS (genesis)
|
||||
///
|
||||
/// # Example
|
||||
/// ```
|
||||
/// use ruvector_economy_wasm::ContributionCurve;
|
||||
///
|
||||
/// // Genesis: 10x multiplier
|
||||
/// assert!((ContributionCurve::current_multiplier(0.0) - 10.0).abs() < 0.01);
|
||||
///
|
||||
/// // At 1M hours: ~4.3x multiplier
|
||||
/// let mult = ContributionCurve::current_multiplier(1_000_000.0);
|
||||
/// assert!(mult > 4.0 && mult < 4.5);
|
||||
///
|
||||
/// // At 10M hours: ~1.0x multiplier
|
||||
/// let mult = ContributionCurve::current_multiplier(10_000_000.0);
|
||||
/// assert!(mult < 1.1);
|
||||
/// ```
|
||||
pub fn current_multiplier(network_compute_hours: f64) -> f32 {
|
||||
let decay = (-network_compute_hours / Self::DECAY_CONSTANT).exp();
|
||||
1.0 + (Self::MAX_BONUS - 1.0) * decay as f32
|
||||
}
|
||||
|
||||
/// Calculate reward with multiplier applied
|
||||
///
|
||||
/// # Arguments
|
||||
/// * `base_reward` - Base reward amount before multiplier
|
||||
/// * `network_compute_hours` - Total network compute for multiplier calculation
|
||||
///
|
||||
/// # Returns
|
||||
/// The reward amount with multiplier applied
|
||||
pub fn calculate_reward(base_reward: u64, network_compute_hours: f64) -> u64 {
|
||||
let multiplier = Self::current_multiplier(network_compute_hours);
|
||||
(base_reward as f32 * multiplier) as u64
|
||||
}
|
||||
|
||||
/// Get multiplier tier information for UI display
|
||||
///
|
||||
/// Returns a vector of (compute_hours, multiplier) tuples representing
|
||||
/// key milestones in the contribution curve.
|
||||
pub fn get_tiers() -> Vec<(f64, f32)> {
|
||||
vec![
|
||||
(0.0, 10.0),
|
||||
(100_000.0, 9.1),
|
||||
(500_000.0, 6.1),
|
||||
(1_000_000.0, 4.3),
|
||||
(2_000_000.0, 2.6),
|
||||
(5_000_000.0, 1.4),
|
||||
(10_000_000.0, 1.0),
|
||||
]
|
||||
}
|
||||
|
||||
/// Get the tier name based on network compute level
|
||||
pub fn get_tier_name(network_compute_hours: f64) -> &'static str {
|
||||
if network_compute_hours < 100_000.0 {
|
||||
"Genesis"
|
||||
} else if network_compute_hours < 500_000.0 {
|
||||
"Pioneer"
|
||||
} else if network_compute_hours < 1_000_000.0 {
|
||||
"Early Adopter"
|
||||
} else if network_compute_hours < 5_000_000.0 {
|
||||
"Established"
|
||||
} else {
|
||||
"Baseline"
|
||||
}
|
||||
}
|
||||
|
||||
/// Calculate time remaining until next tier
|
||||
///
|
||||
/// # Arguments
|
||||
/// * `current_compute` - Current network compute hours
|
||||
/// * `hourly_growth` - Estimated hourly compute growth rate
|
||||
///
|
||||
/// # Returns
|
||||
/// Hours until next tier boundary, or None if at baseline
|
||||
pub fn hours_until_next_tier(current_compute: f64, hourly_growth: f64) -> Option<f64> {
|
||||
if hourly_growth <= 0.0 {
|
||||
return None;
|
||||
}
|
||||
|
||||
let tiers = Self::get_tiers();
|
||||
for (threshold, _) in &tiers {
|
||||
if current_compute < *threshold {
|
||||
return Some((*threshold - current_compute) / hourly_growth);
|
||||
}
|
||||
}
|
||||
|
||||
None // Already at baseline
|
||||
}
|
||||
}
|
||||
|
||||
/// Calculate contribution multiplier (WASM export)
|
||||
///
|
||||
/// Returns the reward multiplier based on total network compute hours.
|
||||
/// Early adopters get up to 10x rewards, decaying to 1x as network grows.
|
||||
#[wasm_bindgen]
|
||||
pub fn contribution_multiplier(network_compute_hours: f64) -> f32 {
|
||||
ContributionCurve::current_multiplier(network_compute_hours)
|
||||
}
|
||||
|
||||
/// Calculate reward with multiplier (WASM export)
|
||||
#[wasm_bindgen]
|
||||
pub fn calculate_reward(base_reward: u64, network_compute_hours: f64) -> u64 {
|
||||
ContributionCurve::calculate_reward(base_reward, network_compute_hours)
|
||||
}
|
||||
|
||||
/// Get tier name based on compute level (WASM export)
|
||||
#[wasm_bindgen]
|
||||
pub fn get_tier_name(network_compute_hours: f64) -> String {
|
||||
ContributionCurve::get_tier_name(network_compute_hours).to_string()
|
||||
}
|
||||
|
||||
/// Get tier information as JSON (WASM export)
|
||||
#[wasm_bindgen]
|
||||
pub fn get_tiers_json() -> String {
|
||||
let tiers = ContributionCurve::get_tiers();
|
||||
let tier_objs: Vec<_> = tiers
|
||||
.iter()
|
||||
.map(|(hours, mult)| format!(r#"{{"hours":{},"multiplier":{:.1}}}"#, hours, mult))
|
||||
.collect();
|
||||
|
||||
format!("[{}]", tier_objs.join(","))
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
|
||||
#[test]
|
||||
fn test_genesis_multiplier() {
|
||||
let mult = ContributionCurve::current_multiplier(0.0);
|
||||
assert!(
|
||||
(mult - 10.0).abs() < 0.01,
|
||||
"Genesis should give 10x, got {}",
|
||||
mult
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_decay_constant_multiplier() {
|
||||
// At decay constant, e^(-1) ~= 0.368
|
||||
// So multiplier = 1 + 9 * 0.368 = 4.31
|
||||
let mult = ContributionCurve::current_multiplier(1_000_000.0);
|
||||
assert!(
|
||||
mult > 4.0 && mult < 4.5,
|
||||
"At decay constant should be ~4.3x, got {}",
|
||||
mult
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_high_compute_baseline() {
|
||||
let mult = ContributionCurve::current_multiplier(10_000_000.0);
|
||||
assert!(mult < 1.1, "High compute should approach 1x, got {}", mult);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_multiplier_never_below_one() {
|
||||
let mult = ContributionCurve::current_multiplier(100_000_000.0);
|
||||
assert!(
|
||||
mult >= 1.0,
|
||||
"Multiplier should never go below 1, got {}",
|
||||
mult
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_calculate_reward() {
|
||||
let base = 100;
|
||||
let reward = ContributionCurve::calculate_reward(base, 0.0);
|
||||
assert_eq!(
|
||||
reward, 1000,
|
||||
"Genesis 100 base should give 1000, got {}",
|
||||
reward
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_tier_names() {
|
||||
assert_eq!(ContributionCurve::get_tier_name(0.0), "Genesis");
|
||||
assert_eq!(ContributionCurve::get_tier_name(100_000.0), "Pioneer");
|
||||
assert_eq!(ContributionCurve::get_tier_name(500_000.0), "Early Adopter");
|
||||
assert_eq!(ContributionCurve::get_tier_name(1_000_000.0), "Established");
|
||||
assert_eq!(ContributionCurve::get_tier_name(10_000_000.0), "Baseline");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_wasm_export_functions() {
|
||||
assert!((contribution_multiplier(0.0) - 10.0).abs() < 0.01);
|
||||
assert_eq!(calculate_reward(100, 0.0), 1000);
|
||||
assert_eq!(get_tier_name(0.0), "Genesis");
|
||||
assert!(get_tiers_json().contains("Genesis") == false); // JSON format
|
||||
assert!(get_tiers_json().starts_with("["));
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user