Skip to main content

pump_rust_client/math/
utils.rs

1//! Shared numeric helpers for quote math.
2
3use crate::math::{QuoteError, QuoteResult};
4
5/// `a * b / c` in `u128`, surfacing overflow as [`QuoteError::MathOverflow`].
6#[inline]
7pub fn mul_div_u128(a: u128, b: u128, c: u128) -> QuoteResult<u128> {
8    a.checked_mul(b)
9        .ok_or(QuoteError::MathOverflow)?
10        .checked_div(c)
11        .ok_or(QuoteError::MathOverflow)
12}
13
14/// `value * (10_000 + bps) / 10_000`. Saturates on the rare case the
15/// multiplication itself overflows `u64`.
16#[inline]
17pub fn add_slippage(value: u64, bps: u16) -> u64 {
18    (((value as u128) * (10_000 + bps as u128)) / 10_000) as u64
19}
20
21/// `value * (10_000 - bps) / 10_000`. `bps` larger than 10_000 saturates to 0.
22#[inline]
23pub fn sub_slippage(value: u64, bps: u16) -> u64 {
24    let bps = bps.min(10_000) as u128;
25    (((value as u128) * (10_000 - bps)) / 10_000) as u64
26}
27
28/// `(value - delta, value + delta)` with `delta = value * bps / 10_000`; `None` on overflow.
29#[inline]
30pub fn slippage_bounds(value: u128, bps: u16) -> Option<(u128, u128)> {
31    let delta = value.checked_mul(u128::from(bps))?.checked_div(10_000)?;
32    Some((value.checked_sub(delta)?, value.checked_add(delta)?))
33}
34
35#[cfg(test)]
36mod tests {
37    use super::*;
38
39    #[test]
40    fn slippage_helpers_apply_correct_direction() {
41        assert_eq!(sub_slippage(100, 250), 97);
42        assert_eq!(add_slippage(100, 250), 102);
43        assert_eq!(sub_slippage(1_000_000, 10_000), 0);
44        assert_eq!(sub_slippage(1_000_000, 20_000), 0);
45    }
46
47    #[test]
48    fn slippage_bounds_brackets_value() {
49        assert_eq!(slippage_bounds(1_000_000, 250), Some((975_000, 1_025_000)));
50        assert_eq!(slippage_bounds(42, 0), Some((42, 42)));
51    }
52
53    #[test]
54    fn slippage_bounds_overflow_returns_none() {
55        assert_eq!(slippage_bounds(u128::MAX, 1), None);
56    }
57}