df_hyperliquid_core/client/evm/u256/
amount.rs1use crate::client::evm::u256::U256;
5use crate::utils::{Dec, Round};
6
7pub const NATIVE_DECIMALS: u32 = 18;
9
10#[derive(Debug, Clone, PartialEq, Eq, thiserror::Error)]
12pub enum BadAmount {
13 #[error("{0} is not a positive amount")]
14 NotPositive(Dec),
15 #[error("{value} has more than {decimals} decimal places")]
16 TooFine { value: Dec, decimals: u32 },
17 #[error("the amount is larger than this conversion can count")]
18 TooLarge,
19 #[error("{amount} at {decimals} decimals needs more places than a Dec keeps")]
22 Unkept { amount: U256, decimals: u32 },
23}
24
25const DEC_PLACES: u32 = crate::utils::dec::MAX_SCALE;
27
28impl U256 {
29 pub fn to_dec(self, decimals: u32) -> Result<Dec, BadAmount> {
33 let (units, scale) = match decimals.checked_sub(DEC_PLACES) {
34 None | Some(0) => (self, decimals),
35 Some(extra) => {
36 let unkept = BadAmount::Unkept { amount: self, decimals };
37 let step = U256::ten_to(extra).ok_or_else(|| unkept.clone())?;
38 let (quotient, rest) = self.div_rem(step).ok_or(BadAmount::TooLarge)?;
39 if !rest.is_zero() {
40 return Err(unkept);
41 }
42 (quotient, DEC_PLACES)
43 }
44 };
45 let units = units.to_u128().and_then(|units| i128::try_from(units).ok()).ok_or(BadAmount::TooLarge)?;
46 Dec::new(units, scale).ok_or(BadAmount::TooLarge)
47 }
48
49 pub fn from_dec(value: Dec, decimals: u32) -> Result<U256, BadAmount> {
52 if value.is_negative() {
53 return Err(BadAmount::NotPositive(value));
54 }
55 let places = value.places();
56 if places > decimals {
57 return Err(BadAmount::TooFine { value, decimals });
58 }
59 let units = value
60 .units_at(places, Round::Down)
61 .and_then(|units| u128::try_from(units).ok())
62 .ok_or(BadAmount::TooLarge)?;
63 U256::ten_to(decimals - places)
64 .and_then(|step| U256::from_u128(units).checked_mul(step))
65 .ok_or(BadAmount::TooLarge)
66 }
67}
68
69#[cfg(test)]
70#[path = "tests/amount.rs"]
71mod tests;