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