tokenomics_simulator/
user.rsuse rand::Rng;
use rust_decimal::{prelude::*, Decimal};
use serde::{Deserialize, Serialize};
use uuid::Uuid;
use crate::DECIMAL_PRECISION;
#[derive(Debug, Deserialize, Serialize)]
pub struct User {
pub id: Uuid,
pub balance: Decimal,
}
impl User {
pub fn new(id: Uuid, balance: Decimal) -> Self {
User { id, balance }
}
pub fn generate(total_users: u64, supply: Decimal, price: Option<Decimal>) -> Vec<User> {
let mut rng = rand::thread_rng();
let mut users = vec![];
let mut total_balance = Decimal::default();
for _ in 0..total_users {
let balance = Decimal::from_f64(
rng.gen_range(
0.0..(supply / Decimal::new(total_users as i64, 0))
.to_f64()
.unwrap(),
),
)
.unwrap()
.round_dp(DECIMAL_PRECISION);
total_balance += balance;
users.push(User {
id: Uuid::new_v4(),
balance,
});
}
let normalization_factor = supply / total_balance;
for user in &mut users {
user.balance *= normalization_factor;
user.balance = user.balance.round_dp(DECIMAL_PRECISION);
}
if let Some(price) = price {
for user in &mut users {
user.balance *= price;
user.balance = user.balance.round_dp(DECIMAL_PRECISION);
}
}
let mut remaining_balance = supply - users.iter().map(|u| u.balance).sum::<Decimal>();
for user in &mut users {
if remaining_balance.is_zero() {
break;
}
let add_balance = Decimal::min(remaining_balance, Decimal::new(1, 4));
user.balance += add_balance;
remaining_balance -= add_balance;
}
users
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_user_new() {
let id = Uuid::new_v4();
let balance = Decimal::new(100, 0);
let user = User::new(id, balance);
assert_eq!(user.id, id);
assert_eq!(user.balance, balance);
}
#[test]
fn test_user_generate_with_initial_price() {
let total_users = 10;
let initial_supply = Decimal::new(1000, 0);
let initial_price = Some(Decimal::new(1, 0));
let users = User::generate(total_users, initial_supply, initial_price);
assert_eq!(users.len(), total_users as usize);
let total_balance = users
.iter()
.map(|user| user.balance.round_dp(DECIMAL_PRECISION))
.sum::<Decimal>();
assert_eq!(total_balance, initial_supply);
}
#[test]
fn test_user_generate_without_initial_price() {
let total_users = 10;
let initial_supply = Decimal::new(1000, 0);
let initial_price = None;
let users = User::generate(total_users, initial_supply, initial_price);
assert_eq!(users.len(), total_users as usize);
let total_balance = users
.iter()
.map(|user| user.balance.round_dp(DECIMAL_PRECISION))
.sum::<Decimal>();
assert_eq!(total_balance, initial_supply);
}
}