1use chrono::{DateTime, Utc};
2use rust_decimal::prelude::ToPrimitive;
3use rust_decimal::Decimal;
4use std::env;
5use std::error::Error;
6use std::fmt::{Display, Formatter};
7use web3::types::U256;
8
9pub fn datetime_from_u256_timestamp(timestamp: U256) -> Option<DateTime<Utc>> {
10 DateTime::from_timestamp(timestamp.as_u64() as i64, 0)
11}
12
13pub fn datetime_from_u256_with_option(timestamp: U256) -> Option<DateTime<Utc>> {
14 if timestamp.is_zero() {
15 None
16 } else {
17 datetime_from_u256_timestamp(timestamp)
18 }
19}
20
21pub fn get_env_bool_value(env_name: &str) -> bool {
22 env::var(env_name)
23 .map(|v| {
24 if v == "1" || v == "true" {
25 true
26 } else {
27 if v != "0" && v != "false" {
28 log::warn!("Invalid value for {}: {} assuming false", env_name, v);
29 }
30 false
31 }
32 })
33 .unwrap_or(false)
34}
35
36#[derive(Debug, Clone)]
37pub struct ConversionError {
38 pub msg: String,
39}
40
41impl ConversionError {
42 pub fn from(msg: String) -> Self {
43 Self { msg }
44 }
45}
46
47impl Display for ConversionError {
48 fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
49 write!(f, "Error during conversion: {}", self.msg)
50 }
51}
52
53impl Error for ConversionError {
54 fn description(&self) -> &str {
55 "Conversion error"
56 }
57}
58
59fn compute_base(num_decimals: u32) -> rust_decimal::Decimal {
60 if num_decimals == 18 {
61 Decimal::new(1000000000000000000, 0)
62 } else if num_decimals == 6 {
63 Decimal::new(1000000, 0)
64 } else {
65 Decimal::from(10_u128.pow(num_decimals))
66 }
67}
68
69fn rust_dec_to_u256_strict(
71 dec_amount: rust_decimal::Decimal,
72 decimals: Option<u32>,
73) -> Result<U256, ConversionError> {
74 let num_decimals = decimals.unwrap_or(18);
75 if num_decimals > 18 {
76 return Err(ConversionError {
77 msg: format!("Decimals: {num_decimals} cannot be greater than 18"),
78 });
79 }
80
81 let dec_base = compute_base(num_decimals);
82 let dec_mul = dec_amount.checked_mul(dec_base).ok_or(ConversionError {
85 msg: "Overflow during conversion".to_string(),
86 })?;
87 let dec_mul = dec_mul.normalize();
90 if dec_mul.fract() != Decimal::from(0) {
93 return Err(ConversionError::from(format!(
94 "Number cannot have a fractional part {dec_mul}"
95 )));
96 }
97 let u128 = dec_mul.to_u128().ok_or_else(|| {
98 ConversionError::from(format!("Number cannot be converted to u128 {dec_mul}"))
99 })?;
100 Ok(U256::from(u128))
101}
102
103#[derive(Debug, Clone, Copy)]
104pub enum Decimals {
105 Zero = 0,
106 Six = 6,
107 Nine = 9,
108 Eighteen = 18,
109}
110
111#[allow(dead_code)]
112fn rust_dec_to_u256(dec_amount: rust_decimal::Decimal, decimals: Decimals) -> U256 {
113 if dec_amount < Decimal::from(0) {
114 return U256::zero();
115 }
116 let num_decimals = match decimals {
117 Decimals::Zero => 0,
118 Decimals::Six => 6,
119 Decimals::Nine => 9,
120 Decimals::Eighteen => 18,
121 };
122
123 let dec_base = compute_base(num_decimals);
124 let dec_mul = match dec_amount.checked_mul(dec_base) {
127 Some(dec_mul) => dec_mul,
128 None => {
129 log::warn!(
130 "Overflow during multiplication dec_amount: {} dec_base: {}. Using saturated mul",
131 dec_amount,
132 dec_base
133 );
134 dec_amount.saturating_mul(dec_base)
135 }
136 };
137
138 let dec_mul = dec_mul.normalize();
141 if dec_mul.fract() != Decimal::from(0) {
144 log::warn!("Number have a fractional part which will be truncated {dec_mul}");
145 }
146 let u128 = dec_mul.to_u128().unwrap_or(
147 0,
149 );
150
151 U256::from(u128)
152}
153
154fn u256_to_rust_dec(
155 amount: U256,
156 decimals: Option<u32>,
157) -> Result<rust_decimal::Decimal, ConversionError> {
158 let num_decimals = decimals.unwrap_or(18);
159 if num_decimals > 18 {
160 return Err(ConversionError {
161 msg: format!("Decimals: {num_decimals} cannot be greater than 18"),
162 });
163 }
164
165 let dec_base = compute_base(num_decimals);
166
167 if amount >= U256::from(79228162514264337593543950336_u128) {
169 return Err(ConversionError {
170 msg: format!(
171 "Amount greater than max rust_decimal: {amount}>=79228162514264337593543950336"
172 ),
173 });
174 }
175
176 Ok(Decimal::from(amount.as_u128()) / dec_base)
177}
178
179fn u256_to_gwei(amount: U256) -> Result<Decimal, ConversionError> {
180 u256_to_rust_dec(amount, Some(9))
181}
182
183pub trait U256ConvExt {
184 fn to_gwei(&self) -> Result<Decimal, ConversionError>;
185 fn to_gwei_saturate(&self) -> Decimal;
186 fn to_eth(&self) -> Result<Decimal, ConversionError>;
187 fn to_eth_saturate(&self) -> Decimal;
188 fn to_gwei_str(&self) -> String;
189 fn to_eth_str(&self) -> String;
190 fn to_gwei_str_with_precision(&self, precision: u8) -> String;
191 fn to_eth_str_with_precision(&self, precision: u8) -> String;
192}
193
194impl U256ConvExt for U256 {
195 fn to_gwei(&self) -> Result<Decimal, ConversionError> {
196 u256_to_gwei(*self)
197 }
198 fn to_gwei_saturate(&self) -> Decimal {
199 u256_to_gwei(*self).unwrap_or(Decimal::from(10000000000000_u64))
200 }
201 fn to_eth(&self) -> Result<Decimal, ConversionError> {
202 u256_to_eth(*self)
203 }
204 fn to_eth_saturate(&self) -> Decimal {
205 u256_to_eth(*self).unwrap_or(Decimal::from(10000000000_u64))
206 }
207 fn to_gwei_str(&self) -> String {
208 u256_to_decimal_string(*self, Decimals::Nine, None)
209 }
210 fn to_eth_str(&self) -> String {
211 u256_to_decimal_string(*self, Decimals::Eighteen, None)
212 }
213 fn to_gwei_str_with_precision(&self, precision: u8) -> String {
214 u256_to_decimal_string(*self, Decimals::Nine, Some(precision as usize))
215 }
216 fn to_eth_str_with_precision(&self, precision: u8) -> String {
217 u256_to_decimal_string(*self, Decimals::Eighteen, Some(precision as usize))
218 }
219}
220
221pub trait StringConvExt {
222 fn to_gwei(&self) -> Result<Decimal, ConversionError>;
223 fn to_eth(&self) -> Result<Decimal, ConversionError>;
224 fn to_u256(&self) -> Result<U256, ConversionError>;
225}
226impl StringConvExt for String {
227 fn to_gwei(&self) -> Result<Decimal, ConversionError> {
228 self.to_u256()?.to_gwei()
229 }
230 fn to_eth(&self) -> Result<Decimal, ConversionError> {
231 self.to_u256()?.to_eth()
232 }
233
234 fn to_u256(&self) -> Result<U256, ConversionError> {
235 U256::from_dec_str(self).map_err(|err| {
236 ConversionError::from(format!("Invalid string when converting: {err:?}"))
237 })
238 }
239}
240
241pub trait DecimalConvExt {
242 fn to_u256_from_gwei(&self) -> Result<U256, ConversionError>;
243 fn to_u256_from_eth(&self) -> Result<U256, ConversionError>;
244}
245
246impl DecimalConvExt for Decimal {
247 fn to_u256_from_gwei(&self) -> Result<U256, ConversionError> {
248 rust_dec_to_u256_strict(*self, Some(9))
249 }
250 fn to_u256_from_eth(&self) -> Result<U256, ConversionError> {
251 rust_dec_to_u256_strict(*self, Some(18))
252 }
253}
254
255fn u256_to_eth(amount: U256) -> Result<Decimal, ConversionError> {
256 u256_to_rust_dec(amount, Some(18))
257}
258
259pub fn u256_eth_from_str(val: &str) -> Result<(U256, Decimal), ConversionError> {
260 let u256 = U256::from_dec_str(val)
261 .map_err(|err| ConversionError::from(format!("Invalid string when converting: {err:?}")))?;
262 let eth = u256_to_eth(u256)?;
263 Ok((u256, eth))
264}
265
266pub fn u256_gwei_from_str(val: &str) -> Result<(U256, Decimal), ConversionError> {
267 let u256 = U256::from_dec_str(val)
268 .map_err(|err| ConversionError::from(format!("Invalid string when converting: {err:?}")))?;
269 let gwei = u256_to_gwei(u256)?;
270 Ok((u256, gwei))
271}
272
273pub fn u256_to_decimal_string(
275 amount: U256,
276 decimals: Decimals,
277 precision: Option<usize>,
278) -> String {
279 let str = &amount.to_string();
280 let mut str_rev: Vec<char> = str.chars().rev().collect();
281 let precision = precision.map(|p| std::cmp::min(p, decimals as usize));
282
283 #[allow(clippy::same_item_push)]
284 for _ in 0..(decimals as usize) {
285 str_rev.push('0');
286 }
287
288 str_rev.insert(decimals as usize, '.');
289
290 let str: String = str_rev.iter().rev().collect();
291 let str = str.trim_matches('0').to_string();
292 let mut str = if str.starts_with('.') {
293 "0".to_string() + &str
294 } else {
295 str
296 };
297
298 let idx_of_dot = str.find('.').unwrap_or(str.len()) as i64;
299 let number_of_digit_at_right = str.len() as i64 - idx_of_dot - 1;
300
301 if let Some(precision) = precision {
302 let add_zeroes = precision as i64 - number_of_digit_at_right;
303 if add_zeroes > 0 {
304 for _ in 0..add_zeroes {
305 str.push('0');
306 }
307 } else {
308 for _ in 0..(-add_zeroes) {
309 str.pop();
310 }
311 }
312 }
313
314 str = str.trim_end_matches('.').to_string();
315
316 str
317}
318
319#[cfg(test)]
320mod tests {
321 use super::*;
322 use rand::Rng;
323 use std::str::FromStr;
324
325 #[test]
326 #[rustfmt::skip]
327 fn test_rust_u256_to_str() {
328 assert_eq!(u256_to_decimal_string(U256::from(0), Decimals::Zero, None), "0");
329 assert_eq!(u256_to_decimal_string(U256::from(0), Decimals::Six, None), "0");
330 assert_eq!(u256_to_decimal_string(U256::from(0), Decimals::Nine, None), "0");
331 assert_eq!(u256_to_decimal_string(U256::from(0), Decimals::Eighteen, None), "0");
332 assert_eq!(u256_to_decimal_string(U256::from(1), Decimals::Zero, None), "1");
333 assert_eq!(u256_to_decimal_string(U256::from(1), Decimals::Six, None), "0.000001");
334 assert_eq!(u256_to_decimal_string(U256::from(1), Decimals::Nine, None), "0.000000001");
335 assert_eq!(u256_to_decimal_string(U256::from(1), Decimals::Eighteen, None), "0.000000000000000001");
336 assert_eq!(u256_to_decimal_string(U256::from(1), Decimals::Six, Some(0)), "0");
337 assert_eq!(u256_to_decimal_string(U256::from(1), Decimals::Six, Some(3)), "0.000");
338 assert_eq!(u256_to_decimal_string(U256::from(1), Decimals::Six, Some(6)), "0.000001");
339 assert_eq!(u256_to_decimal_string(U256::from(1), Decimals::Six, Some(9)), "0.000001");
340
341 let max_u256_str = "115792089237316195423570985008687907853269984665640564039457584007913129639935";
342 assert_eq!(u256_to_decimal_string(U256::from_dec_str(max_u256_str).unwrap(), Decimals::Zero, Some(2)),
343 "115792089237316195423570985008687907853269984665640564039457584007913129639935");
344 assert_eq!(u256_to_decimal_string(U256::from_dec_str(max_u256_str).unwrap(), Decimals::Eighteen, Some(2)),
345 "115792089237316195423570985008687907853269984665640564039457.58");
346
347 let mut rng = rand::thread_rng();
348 for _ in 0..1000 {
349 let rand: u64 = rng.gen();
350
351 let u256 = U256::from(rand) / U256::from(1000000_u64);
352 assert_eq!(u256.to_string(), u256_to_decimal_string(U256::from(rand), Decimals::Six, Some(0)));
353 }
354
355 assert_eq!(u256_to_decimal_string(U256::from(1000000000000000000_u128), Decimals::Eighteen, None), "1");
356 assert_eq!(u256_to_decimal_string(U256::from(1000000000000000000000000000000000000_u128), Decimals::Eighteen, None), "1000000000000000000");
357 assert_eq!(u256_to_decimal_string(U256::from(1000000000000000000000000000000000000_u128), Decimals::Eighteen, Some(5)), "1000000000000000000.00000");
358 assert_eq!(u256_to_decimal_string(U256::from(1000000000000000000660000000000000000_u128), Decimals::Eighteen, None), "1000000000000000000.66");
359 assert_eq!(u256_to_decimal_string(U256::from(1000000000000000000660000000000000778_u128), Decimals::Eighteen, None), "1000000000000000000.660000000000000778");
360 assert_eq!(u256_to_decimal_string(U256::from(1000000000000000000660000000000000778_u128), Decimals::Eighteen, Some(16)), "1000000000000000000.6600000000000007");
361 }
362
363 #[test]
364 fn test_rust_decimal_conversion() {
365 let dec_gwei = Decimal::new(1, 18);
366 let res = rust_dec_to_u256_strict(dec_gwei, None).unwrap();
367 assert_eq!(res, U256::from(1));
368
369 let res = rust_dec_to_u256_strict(dec_gwei / Decimal::from(2), None);
370 println!("res: {res:?}");
371 assert!(res.err().unwrap().msg.contains("fractional"));
372
373 let res = rust_dec_to_u256_strict(dec_gwei / Decimal::from(2), Some(19));
374 println!("res: {res:?}");
375 assert!(res.err().unwrap().msg.contains("greater than 18"));
376
377 let res = rust_dec_to_u256_strict(Decimal::from(8777666555_u64), None).unwrap();
378 println!("res: {res:?}");
379 assert_eq!(
380 res,
381 U256::from(8777666555_u64) * U256::from(1000000000000000000_u64)
382 );
383
384 let res = rust_dec_to_u256_strict(Decimal::from(8777666555_u64) + dec_gwei, None).unwrap();
385 println!("res: {res:?}");
386 assert_eq!(res, U256::from(8777666555000000000000000001_u128));
387
388 let res = rust_dec_to_u256_strict(Decimal::from(0), None).unwrap();
389 println!("res: {res:?}");
390 assert_eq!(res, U256::from(0));
391
392 let res = rust_dec_to_u256_strict(Decimal::from(1), Some(0)).unwrap();
393 println!("res: {res:?}");
394 assert_eq!(res, U256::from(1));
395
396 let res = rust_dec_to_u256_strict(Decimal::from(1), Some(6)).unwrap();
397 println!("res: {res:?}");
398 assert_eq!(res, U256::from(1000000));
399
400 let res = rust_dec_to_u256_strict(Decimal::from(1), Some(9)).unwrap();
401 println!("res: {res:?}");
402 assert_eq!(res, U256::from(1000000000));
403
404 let res =
405 rust_dec_to_u256_strict(Decimal::from_str("123456789.123456789").unwrap(), Some(18))
406 .unwrap();
407 println!("res: {res:?}");
408 assert_eq!(
409 res,
410 U256::from_dec_str("123456789123456789000000000").unwrap()
411 );
412
413 let res = rust_dec_to_u256_strict(
415 Decimal::from_str("79228162514.264337593543950336").unwrap(),
416 Some(18),
417 );
418 println!("res: {res:?}");
419 assert!(res.err().unwrap().msg.to_lowercase().contains("overflow"));
420
421 let res = rust_dec_to_u256_strict(
423 Decimal::from_str("79228162514.264337593543950335").unwrap(),
424 Some(18),
425 )
426 .unwrap();
427 println!("res: {res:?}");
428 assert_eq!(res, U256::from(79228162514264337593543950335_u128));
429
430 let res = rust_dec_to_u256_strict(
432 Decimal::from_str("79228162514264337593543950335").unwrap(),
433 Some(0),
434 )
435 .unwrap();
436 println!("res: {res:?}");
437 assert_eq!(res, U256::from(79228162514264337593543950335_u128));
438
439 let res = rust_dec_to_u256_strict(
441 Decimal::from_str("79228162514264337593543.950335").unwrap(),
442 Some(6),
443 )
444 .unwrap();
445 println!("res: {res:?}");
446 assert_eq!(res, U256::from(79228162514264337593543950335_u128));
447
448 let res = rust_dec_to_u256_strict(
450 Decimal::from_str("792281625142643.37593543950335").unwrap(),
451 Some(14),
452 )
453 .unwrap();
454 println!("res: {res:?}");
455 assert_eq!(res, U256::from(79228162514264337593543950335_u128));
456 let res = rust_dec_to_u256(
459 Decimal::from_str("79228162514.264337593543950335").unwrap(),
460 Decimals::Eighteen,
461 );
462 assert_eq!(res, U256::from(79228162514264337593543950335_u128));
463
464 let res = rust_dec_to_u256(
465 Decimal::from_str("2514.26433759354395033559999").unwrap(),
466 Decimals::Eighteen,
467 );
468 assert_eq!(res, U256::from(2514264337593543950335_u128));
469 }
470}