tokenomics_simulator/
user.rs1use rand::Rng;
7use rust_decimal::{prelude::*, Decimal};
8#[cfg(feature = "serde")]
9use serde::{Deserialize, Serialize};
10use uuid::Uuid;
11
12#[derive(Debug)]
14#[cfg_attr(feature = "serde", derive(Deserialize, Serialize))]
15pub struct User {
16 pub id: Uuid,
18
19 pub balance: Decimal,
21
22 pub behaviour: UserBehaviour,
24}
25
26#[derive(Debug)]
28#[cfg_attr(feature = "serde", derive(Deserialize, Serialize))]
29pub enum UserBehaviour {
30 Speculator,
32
33 Holder,
35
36 Trader,
38}
39
40impl User {
41 pub fn new(id: Uuid, balance: Decimal) -> Self {
52 User {
53 id,
54 balance,
55 behaviour: UserBehaviour::Trader,
56 }
57 }
58
59 pub fn generate(total_users: u64, supply: Decimal, price: Decimal, decimals: u32) -> Vec<User> {
72 #[cfg(feature = "log")]
73 log::debug!(
74 "Generating {} users with initial supply of {} and price of {}",
75 total_users,
76 supply,
77 price
78 );
79
80 let mut rng = rand::rng();
81 let mut users = vec![];
82
83 let mut total_balance = Decimal::default();
84 for _ in 0..total_users {
85 let balance = Decimal::from_f64(
86 rng.random_range(
87 0.0..(supply / Decimal::new(total_users as i64, 0))
88 .to_f64()
89 .unwrap(),
90 ),
91 )
92 .unwrap()
93 .round_dp(decimals);
94 total_balance += balance;
95
96 users.push(User {
97 id: Uuid::new_v4(),
98 balance,
99 behaviour: UserBehaviour::Trader,
100 });
101 }
102
103 let normalization_factor = supply / total_balance;
105 for user in &mut users {
106 user.balance *= normalization_factor;
107 user.balance = user.balance.round_dp(decimals);
108 }
109
110 for user in &mut users {
112 user.balance *= price;
113 user.balance = user.balance.round_dp(decimals);
114 }
115
116 let mut remaining_balance = supply - users.iter().map(|u| u.balance).sum::<Decimal>();
118 for user in &mut users {
119 if remaining_balance.is_zero() {
120 break;
121 }
122
123 let add_balance = Decimal::min(remaining_balance, Decimal::new(1, 4));
124 user.balance += add_balance;
125 remaining_balance -= add_balance;
126 }
127
128 users
129 }
130}
131
132#[cfg(test)]
133mod tests {
134 use super::*;
135
136 #[test]
137 fn test_user_new() {
138 let id = Uuid::new_v4();
139 let balance = Decimal::new(100, 0);
140
141 let user = User::new(id, balance);
142
143 assert_eq!(user.id, id);
144 assert_eq!(user.balance, balance);
145 }
146
147 #[test]
148 fn test_user_generate() {
149 let total_users = 10;
150 let decimals = 4;
151 let initial_supply = Decimal::new(1000, 0);
152 let initial_price = Decimal::new(1, 0);
153
154 let users = User::generate(total_users, initial_supply, initial_price, decimals);
155
156 assert_eq!(users.len(), total_users as usize);
157
158 let total_balance = users
159 .iter()
160 .map(|user| user.balance.round_dp(decimals))
161 .sum::<Decimal>();
162
163 assert_eq!(total_balance, initial_supply);
164 }
165}