1use alloy::primitives::{FixedBytes, Uint, U256};
10
11type U512 = Uint<512, 8>;
13
14pub const SCALE_OFFSET: u32 = 128;
18
19pub const SCALE: U256 = U256::from_limbs([0, 0, 1, 0]);
21
22pub const PRECISION: u128 = 1_000_000_000_000_000_000;
24
25pub const SQUARED_PRECISION: u128 = 0; pub const MAX_FEE: u128 = 100_000_000_000_000_000;
30
31pub const BASIS_POINT_MAX: u128 = 10_000;
33
34pub const REAL_ID_SHIFT: i32 = 1 << 23; pub fn get_base(bin_step: u16) -> U256 {
44 SCALE + (U256::from(bin_step) << SCALE_OFFSET) / U256::from(BASIS_POINT_MAX)
45}
46
47pub fn get_exponent(id: u32) -> i32 {
49 id as i32 - REAL_ID_SHIFT
50}
51
52pub fn get_price_from_id(id: u32, bin_step: u16) -> U256 {
57 let base = get_base(bin_step);
58 let exponent = get_exponent(id);
59 pow_128x128(base, exponent)
60}
61
62pub fn pow_128x128(x: U256, y: i32) -> U256 {
70 if y == 0 {
71 return SCALE;
72 }
73
74 let abs_y = y.unsigned_abs();
75 let mut invert = y < 0;
76
77 let mut squared = x;
79 if squared > SCALE {
80 squared = U256::MAX / squared;
81 invert = !invert;
82 }
83
84 let mut result = SCALE;
85
86 for bit in 0..20u32 {
88 if abs_y & (1 << bit) != 0 {
89 result = mul_128x128(result, squared);
90 }
91 squared = mul_128x128(squared, squared);
92 }
93
94 if result.is_zero() {
95 return U256::ZERO;
97 }
98
99 if invert {
100 U256::MAX / result
101 } else {
102 result
103 }
104}
105
106fn mul_128x128(a: U256, b: U256) -> U256 {
110 let product: U512 = a.widening_mul(b);
111 let shifted = product >> SCALE_OFFSET;
112 let limbs = shifted.as_limbs();
113 U256::from_limbs([limbs[0], limbs[1], limbs[2], limbs[3]])
114}
115
116pub fn get_base_fee(base_factor: u16, bin_step: u16) -> u128 {
123 base_factor as u128 * bin_step as u128 * 10_000_000_000u128
124}
125
126pub fn get_variable_fee(
131 volatility_accumulator: u32,
132 bin_step: u16,
133 variable_fee_control: u32,
134) -> u128 {
135 if variable_fee_control == 0 {
136 return 0;
137 }
138 let prod = volatility_accumulator as u128 * bin_step as u128;
139 (prod * prod * variable_fee_control as u128 + 99) / 100
143}
144
145pub fn get_total_fee(
149 base_factor: u16,
150 bin_step: u16,
151 volatility_accumulator: u32,
152 variable_fee_control: u32,
153) -> u128 {
154 let total = get_base_fee(base_factor, bin_step)
155 + get_variable_fee(volatility_accumulator, bin_step, variable_fee_control);
156 total.min(MAX_FEE)
157}
158
159pub fn get_fee_amount_from(amount_with_fees: u128, total_fee: u128) -> u128 {
166 debug_assert!(total_fee <= MAX_FEE, "Fee too large");
167 let numerator =
169 U256::from(amount_with_fees) * U256::from(total_fee) + U256::from(PRECISION - 1);
170 let result = numerator / U256::from(PRECISION);
171 result.to::<u128>()
172}
173
174pub fn get_fee_amount(amount: u128, total_fee: u128) -> u128 {
181 debug_assert!(total_fee <= MAX_FEE, "Fee too large");
182 let denominator = PRECISION - total_fee;
183 let numerator = U256::from(amount) * U256::from(total_fee) + U256::from(denominator - 1);
184 let result = numerator / U256::from(denominator);
185 result.to::<u128>()
186}
187
188pub fn decode_amounts(packed: FixedBytes<32>) -> (u128, u128) {
195 let bytes = packed.as_slice();
196 let y = u128::from_be_bytes(bytes[0..16].try_into().unwrap());
199 let x = u128::from_be_bytes(bytes[16..32].try_into().unwrap());
200 (x, y)
201}
202
203pub fn decode_amount(packed: FixedBytes<32>, decode_x: bool) -> u128 {
206 let (x, y) = decode_amounts(packed);
207 if decode_x {
208 x
209 } else {
210 y
211 }
212}
213
214pub fn mul_shift_round_down(x: U256, y: U256, offset: u32) -> U256 {
220 let product: U512 = x.widening_mul(y);
221 let shifted = product >> offset;
222 let limbs = shifted.as_limbs();
223 U256::from_limbs([limbs[0], limbs[1], limbs[2], limbs[3]])
224}
225
226pub fn mul_shift_round_up(x: U256, y: U256, offset: u32) -> U256 {
230 let result = mul_shift_round_down(x, y, offset);
231 let product: U512 = x.widening_mul(y);
232 let mask = if offset >= 512 {
233 U512::ZERO
234 } else {
235 (U512::from(1u64) << offset) - U512::from(1u64)
236 };
237 if (product & mask) > U512::ZERO {
238 result + U256::from(1u64)
239 } else {
240 result
241 }
242}
243
244pub fn shift_div_round_down(x: U256, offset: u32, denominator: U256) -> U256 {
248 if denominator.is_zero() {
249 return U256::ZERO;
250 }
251 let x_wide = U512::from(x);
252 let shifted = x_wide << offset;
253 let denom_wide = U512::from(denominator);
254 let result = shifted / denom_wide;
255 let limbs = result.as_limbs();
256 U256::from_limbs([limbs[0], limbs[1], limbs[2], limbs[3]])
257}
258
259pub fn shift_div_round_up(x: U256, offset: u32, denominator: U256) -> U256 {
263 if denominator.is_zero() {
264 return U256::ZERO;
265 }
266 let result = shift_div_round_down(x, offset, denominator);
267 let x_wide = U512::from(x);
268 let shifted = x_wide << offset;
269 let denom_wide = U512::from(denominator);
270 if shifted % denom_wide > U512::ZERO {
271 result + U256::from(1u64)
272 } else {
273 result
274 }
275}
276
277#[cfg(test)]
278mod tests {
279 use super::*;
280
281 #[test]
282 fn test_scale_constant() {
283 assert_eq!(SCALE, U256::from(1u64) << 128);
284 }
285
286 #[test]
287 fn test_get_base() {
288 let base = get_base(1);
290 assert!(base > SCALE);
292 let diff = base - SCALE;
294 let expected_diff = SCALE / U256::from(10000u64);
295 assert_eq!(diff, expected_diff);
296 }
297
298 #[test]
299 fn test_get_base_25() {
300 let base = get_base(25);
302 let diff = base - SCALE;
303 let expected_diff = U256::from(25u64) * SCALE / U256::from(10000u64);
304 assert_eq!(diff, expected_diff);
305 }
306
307 #[test]
308 fn test_pow_identity() {
309 assert_eq!(pow_128x128(get_base(25), 0), SCALE);
311 }
312
313 #[test]
314 fn test_pow_one() {
315 let base = get_base(25);
317 let result = pow_128x128(base, 1);
318 assert_eq!(result, base);
319 }
320
321 #[test]
322 fn test_pow_negative_one() {
323 let base = get_base(25);
325 let result = pow_128x128(base, -1);
326 let expected = U256::MAX / base;
327 let diff = if result > expected {
329 result - expected
330 } else {
331 expected - result
332 };
333 assert!(diff <= U256::from(1u64));
334 }
335
336 #[test]
337 fn test_price_at_center_bin() {
338 let price = get_price_from_id(REAL_ID_SHIFT as u32, 25);
340 assert_eq!(price, SCALE);
341 }
342
343 #[test]
344 fn test_price_monotonic() {
345 let center = REAL_ID_SHIFT as u32;
347 let p1 = get_price_from_id(center, 25);
348 let p2 = get_price_from_id(center + 1, 25);
349 let p3 = get_price_from_id(center + 10, 25);
350 assert!(p2 > p1);
351 assert!(p3 > p2);
352
353 let p0 = get_price_from_id(center - 1, 25);
354 assert!(p0 < p1);
355 }
356
357 #[test]
358 fn test_base_fee() {
359 let fee = get_base_fee(15, 25);
361 assert_eq!(fee, 3_750_000_000_000u128);
362 }
363
364 #[test]
365 fn test_variable_fee_zero_control() {
366 assert_eq!(get_variable_fee(1000, 25, 0), 0);
367 }
368
369 #[test]
370 fn test_total_fee_capped() {
371 let fee = get_total_fee(u16::MAX, u16::MAX, u32::MAX, u32::MAX);
373 assert!(fee <= MAX_FEE);
374 }
375
376 #[test]
377 fn test_fee_amount_from() {
378 let total_fee = 3_000_000_000_000_000u128; let amount_with_fees = 1_000_000_000_000_000_000_000u128; let fee = get_fee_amount_from(amount_with_fees, total_fee);
383 assert_eq!(fee, 3_000_000_000_000_000_000u128);
385 }
386
387 #[test]
388 fn test_decode_amounts() {
389 let mut bytes = [0u8; 32];
391 bytes[0..16].copy_from_slice(&200u128.to_be_bytes());
393 bytes[16..32].copy_from_slice(&100u128.to_be_bytes());
395 let packed = FixedBytes::<32>::from(bytes);
396 let (x, y) = decode_amounts(packed);
397 assert_eq!(x, 100);
398 assert_eq!(y, 200);
399 }
400
401 #[test]
402 fn test_mul_shift_round_down() {
403 let result = mul_shift_round_down(SCALE, SCALE, SCALE_OFFSET);
405 assert_eq!(result, SCALE);
406 }
407
408 #[test]
409 fn test_shift_div_round_down() {
410 let result = shift_div_round_down(U256::from(100u64), SCALE_OFFSET, SCALE);
412 assert_eq!(result, U256::from(100u64));
413 }
414}