1use alloy::primitives::{I256, U256};
2use fastnum::{
3 bint,
4 decimal::{Context, Decimal, RoundingMode, UnsignedDecimal},
5};
6
7pub const FEE_SCALE: u8 = 5;
11
12pub fn fee_converter() -> Converter { Converter::new(FEE_SCALE) }
14
15#[derive(Clone, Copy, Debug, Default)]
17pub struct Converter {
18 decimals: i32,
19}
20
21impl Converter {
22 pub fn new(decimals: u8) -> Self { Self { decimals: decimals as i32 } }
25
26 pub fn decimals(&self) -> u8 { self.decimals as u8 }
27
28 pub fn scale<const N: usize>(&self) -> UnsignedDecimal<N> {
29 UnsignedDecimal::<N>::from_parts(
30 bint::UInt::ONE,
31 self.decimals,
32 Context::default().with_rounding_mode(RoundingMode::Floor),
33 )
34 }
35
36 pub fn from_unsigned<const N: usize>(&self, value: U256) -> UnsignedDecimal<N> {
37 let unscaled = bint::UInt::<N>::from_le_slice(value.as_le_slice())
38 .expect("Converter: U256 -> UInt::<N>");
39 UnsignedDecimal::<N>::from_parts(
40 unscaled,
41 -self.decimals,
42 Context::default().with_rounding_mode(RoundingMode::Floor),
43 )
44 }
45
46 pub fn from_u64<const N: usize>(&self, value: u64) -> UnsignedDecimal<N> {
47 UnsignedDecimal::<N>::from_parts(
48 bint::UInt::from_u64(value),
49 -self.decimals,
50 Context::default().with_rounding_mode(RoundingMode::Floor),
51 )
52 }
53
54 pub fn from_signed<const N: usize>(&self, value: I256) -> Decimal<N> {
55 let unscaled = bint::UInt::<N>::from_le_slice(value.unsigned_abs().as_le_slice())
56 .expect("Converter: abs(I256) -> UInt::<N>");
57 Decimal::<N>::from_parts(
58 unscaled,
59 -self.decimals,
60 match value.sign() {
61 alloy::primitives::Sign::Negative => fastnum::decimal::Sign::Minus,
62 alloy::primitives::Sign::Positive => fastnum::decimal::Sign::Plus,
63 },
64 Context::default().with_rounding_mode(RoundingMode::Floor),
65 )
66 }
67
68 pub fn from_i64<const N: usize>(&self, value: i64) -> Decimal<N> {
69 Decimal::<N>::from_parts(
70 bint::UInt::from_u64(value.unsigned_abs()),
71 -self.decimals,
72 if value < 0 { fastnum::decimal::Sign::Minus } else { fastnum::decimal::Sign::Plus },
73 Context::default().with_rounding_mode(RoundingMode::Floor),
74 )
75 }
76
77 pub fn to_unsigned<const N: usize>(&self, value: UnsignedDecimal<N>) -> U256 {
78 let rescaled = value.rescale(self.decimals as i16);
79 U256::from_le_slice(rescaled.digits().to_radix_le(256).as_slice())
80 }
81
82 pub fn to_signed<const N: usize>(&self, value: Decimal<N>) -> I256 {
83 let rescaled = value.rescale(self.decimals as i16);
84 let mut res = I256::try_from_le_slice(rescaled.digits().to_radix_le(256).as_slice())
85 .unwrap_or_default();
86 if value.is_negative() {
87 res = res.saturating_neg();
88 }
89 res
90 }
91}
92
93#[cfg(test)]
94mod tests {
95 use fastnum::{dec256, udec256};
96
97 use super::*;
98
99 #[test]
100 fn test_numeric_converter_from_unsigned() {
101 assert_eq!(Converter::new(0).from_unsigned(U256::from(1234567890)), udec256!(1234567890));
102 assert_eq!(Converter::new(6).from_unsigned(U256::from(1234567890)), udec256!(1234.56789));
103 assert_eq!(
104 Converter::new(12).from_unsigned(U256::from(1234567890)),
105 udec256!(0.00123456789)
106 );
107 }
108
109 #[test]
110 fn test_numeric_converter_from_signed() {
111 assert_eq!(
112 Converter::new(0).from_signed(I256::try_from(1234567890).unwrap()),
113 dec256!(1234567890)
114 );
115 assert_eq!(
116 Converter::new(6).from_signed(I256::try_from(1234567890).unwrap()),
117 dec256!(1234.56789)
118 );
119 assert_eq!(
120 Converter::new(12).from_signed(I256::try_from(1234567890).unwrap()),
121 dec256!(0.00123456789)
122 );
123
124 assert_eq!(
125 Converter::new(0).from_signed(I256::try_from(-1234567890).unwrap()),
126 dec256!(-1234567890)
127 );
128 assert_eq!(
129 Converter::new(6).from_signed(I256::try_from(-1234567890).unwrap()),
130 dec256!(-1234.56789)
131 );
132 assert_eq!(
133 Converter::new(12).from_signed(I256::try_from(-1234567890).unwrap()),
134 dec256!(-0.00123456789)
135 );
136 }
137
138 #[test]
139 fn test_numeric_converter_to_unsigned() {
140 assert_eq!(Converter::new(0).to_unsigned(udec256!(1234567890)), U256::from(1234567890));
141 assert_eq!(Converter::new(6).to_unsigned(udec256!(1234.56789)), U256::from(1234567890));
142 assert_eq!(Converter::new(12).to_unsigned(udec256!(0.00123456789)), U256::from(1234567890));
143 }
144
145 #[test]
146 fn test_numeric_converter_to_signed() {
147 assert_eq!(
148 Converter::new(0).to_signed(dec256!(1234567890)),
149 I256::try_from(1234567890).unwrap(),
150 );
151 assert_eq!(
152 Converter::new(6).to_signed(dec256!(1234.56789)),
153 I256::try_from(1234567890).unwrap(),
154 );
155 assert_eq!(
156 Converter::new(12).to_signed(dec256!(0.00123456789)),
157 I256::try_from(1234567890).unwrap(),
158 );
159
160 assert_eq!(
161 Converter::new(0).to_signed(dec256!(-1234567890)),
162 I256::try_from(-1234567890).unwrap(),
163 );
164 assert_eq!(
165 Converter::new(6).to_signed(dec256!(-1234.56789)),
166 I256::try_from(-1234567890).unwrap(),
167 );
168 assert_eq!(
169 Converter::new(12).to_signed(dec256!(-0.00123456789)),
170 I256::try_from(-1234567890).unwrap(),
171 );
172 }
173}