nautilus_model/defi/types/
price.rs1use alloy_primitives::U256;
19
20use crate::types::price::Price;
21
22impl Price {
23 #[must_use]
32 pub fn from_wei<U>(raw_wei: U) -> Self
33 where
34 U: Into<U256>,
35 {
36 let raw_u256: U256 = raw_wei.into();
37 let raw_u128: u128 = raw_u256
38 .try_into()
39 .expect("raw wei value exceeds 128-bit range");
40
41 assert!(
42 raw_u128 <= i128::MAX as u128,
43 "raw wei value exceeds signed 128-bit range"
44 );
45
46 let raw_i128: i128 = raw_u128 as i128;
47 Self::from_raw(raw_i128, 18)
48 }
49
50 #[must_use]
58 pub fn as_wei(&self) -> U256 {
59 assert!(
60 self.precision == 18,
61 "Failed to convert price with precision {} to wei (requires precision 18)",
62 self.precision
63 );
64
65 assert!(
66 !self.is_negative(),
67 "Failed to convert negative price to wei"
68 );
69
70 U256::from(self.raw() as u128)
72 }
73}
74
75#[cfg(test)]
76mod tests {
77 use rstest::rstest;
78 use rust_decimal_macros::dec;
79
80 use super::*;
81
82 #[rstest]
83 fn test_from_wei_basic() {
84 let price = Price::from_wei(U256::from(1_000_000_000_000_000_000_u128)); assert_eq!(price.precision, 18);
86 assert_eq!(price.as_decimal(), dec!(1.0));
87 }
88
89 #[rstest]
90 fn test_precision_18_requires_from_wei() {
91 let result = Price::new_checked(1.0, 18);
93 assert!(result.is_err());
94 assert!(
95 result
96 .unwrap_err()
97 .to_string()
98 .contains("use `Price::from_wei()` for wei values")
99 );
100
101 let price = Price::from_wei(U256::from(1_000_000_000_000_000_000_u128));
103 assert_eq!(price.precision, 18);
104 assert_eq!(price.as_decimal(), dec!(1.0));
105 }
106
107 #[rstest]
108 fn test_as_wei_basic() {
109 let price = Price::from_raw(1_000_000_000_000_000_000_i128, 18);
110 let wei = price.as_wei();
111 assert_eq!(wei, U256::from(1_000_000_000_000_000_000_u128));
112 }
113
114 #[rstest]
115 #[should_panic(
116 expected = "Failed to convert price with precision 2 to wei (requires precision 18)"
117 )]
118 fn test_as_wei_wrong_precision() {
119 let price = Price::new(1.23, 2);
120 let _ = price.as_wei();
121 }
122
123 #[rstest]
124 #[should_panic(expected = "Failed to convert negative price")]
125 fn test_as_wei_negative_price() {
126 let price = Price::from_raw(-1_000_000_000_000_000_000_i128, 18);
127 let _ = price.as_wei();
128 }
129
130 #[rstest]
131 fn test_wei_round_trip() {
132 let original_wei = U256::from(1_500_000_000_000_000_000_u128); let price = Price::from_wei(original_wei);
134 let converted_wei = price.as_wei();
135 assert_eq!(original_wei, converted_wei);
136 assert_eq!(price.as_decimal(), dec!(1.5));
137 }
138
139 #[rstest]
140 fn test_from_wei_zero() {
141 let price = Price::from_wei(U256::ZERO);
142 assert_eq!(price.precision, 18);
143 assert_eq!(price.as_decimal(), dec!(0));
144 assert_eq!(price.as_wei(), U256::ZERO);
145 }
146
147 #[rstest]
148 fn test_from_wei_very_large_value() {
149 let large_wei = U256::from(1_000_000_000_000_000_000_000_000_000_u128);
151 let price = Price::from_wei(large_wei);
152 assert_eq!(price.precision, 18);
153 assert_eq!(price.as_wei(), large_wei);
154 assert_eq!(price.as_decimal(), dec!(1000000000));
155 }
156
157 #[rstest]
158 #[should_panic(expected = "raw wei value exceeds 128-bit range")]
159 fn test_from_wei_overflow() {
160 let overflow_wei = U256::from(u128::MAX) + U256::from(1u64);
161 let _ = Price::from_wei(overflow_wei);
162 }
163
164 #[rstest]
165 fn test_checked_arith_accepts_wei_precision() {
166 let a = Price::from_wei(U256::from(1_000_000_000_000_000_000u128));
168 let b = Price::from_wei(U256::from(2_000_000_000_000_000_000u128));
169 let sum = a
170 .checked_add(b)
171 .expect("checked_add must accept wei prices");
172 assert_eq!(sum.as_decimal(), dec!(3));
173 let diff = b
174 .checked_sub(a)
175 .expect("checked_sub must accept wei prices");
176 assert_eq!(diff.as_decimal(), dec!(1));
177 }
178
179 #[rstest]
180 fn test_checked_arith_rejects_mixed_scale() {
181 let wei = Price::from_wei(U256::from(1_000_000_000_000_000_000u128));
185 let standard = Price::new(1.0, 0);
186 assert_eq!(wei.checked_add(standard), None);
187 assert_eq!(standard.checked_add(wei), None);
188 assert_eq!(wei.checked_sub(standard), None);
189 assert_eq!(standard.checked_sub(wei), None);
190 }
191
192 #[rstest]
193 fn test_checked_arith_rejects_mixed_defi_scales() {
194 let p17 = Price::from_raw(100_000_000_000_000_000_i128, 17);
197 let p18 = Price::from_wei(U256::from(1_000_000_000_000_000_000u128));
198 assert_eq!(p17.checked_add(p18), None);
199 assert_eq!(p18.checked_add(p17), None);
200 assert_eq!(p17.checked_sub(p18), None);
201 assert_eq!(p18.checked_sub(p17), None);
202 }
203
204 #[rstest]
205 fn test_from_wei_small_amounts() {
206 let test_cases = vec![
208 (1_u128, dec!(0.000000000000000001)), (1000_u128, dec!(0.000000000000001)), (1_000_000_u128, dec!(0.000000000001)), (1_000_000_000_u128, dec!(0.000000001)), ];
213
214 for (wei_amount, expected_decimal) in test_cases {
215 let price = Price::from_wei(U256::from(wei_amount));
216 assert_eq!(price.precision, 18);
217 assert_eq!(price.as_decimal(), expected_decimal);
218 assert_eq!(price.as_wei(), U256::from(wei_amount));
219 }
220 }
221
222 #[rstest]
223 fn test_from_wei_large_amounts() {
224 let test_cases = vec![
226 (1_000_000_000_000_000_000_u128, dec!(1)), (10_000_000_000_000_000_000_u128, dec!(10)), (100_000_000_000_000_000_000_u128, dec!(100)), (1_000_000_000_000_000_000_000_u128, dec!(1000)), ];
231
232 for (wei_amount, expected_decimal) in test_cases {
233 let price = Price::from_wei(U256::from(wei_amount));
234 assert_eq!(price.precision, 18);
235 assert_eq!(price.as_decimal(), expected_decimal);
236 assert_eq!(price.as_wei(), U256::from(wei_amount));
237 }
238 }
239
240 #[rstest]
241 fn test_as_wei_precision_validation() {
242 for precision in [2, 6, 8, 16] {
244 let price = Price::new(123.45, precision);
245 let result = std::panic::catch_unwind(|| price.as_wei());
246 assert!(
247 result.is_err(),
248 "as_wei() should panic for precision {precision}"
249 );
250 }
251 }
252
253 #[rstest]
254 fn test_arithmetic_operations_with_wei() {
255 let price1 = Price::from_wei(U256::from(1_000_000_000_000_000_000_u128)); let price2 = Price::from_wei(U256::from(500_000_000_000_000_000_u128)); let sum = price1 + price2;
260 assert_eq!(sum.precision, 18);
261 assert_eq!(sum.as_decimal(), dec!(1.5));
262
263 let diff = price1 - price2;
265 assert_eq!(diff.precision, 18);
266 assert_eq!(diff.as_decimal(), dec!(0.5));
267 }
268
269 #[rstest]
270 fn test_comparison_operations_with_wei() {
271 let price1 = Price::from_wei(U256::from(1_000_000_000_000_000_000_u128)); let price2 = Price::from_wei(U256::from(2_000_000_000_000_000_000_u128)); let price3 = Price::from_wei(U256::from(1_000_000_000_000_000_000_u128)); assert!(price1 < price2);
276 assert!(price2 > price1);
277 assert_eq!(price1, price3);
278 assert!(price1 <= price3);
279 assert!(price1 >= price3);
280 }
281}