Skip to main content

pump_rust_client/math/
utils.rs

1//! Generic numeric helpers shared by the quote builders. Pure functions —
2//! no domain types, no I/O.
3
4/// `value * (10_000 + bps) / 10_000`. Saturates on the rare case the
5/// multiplication itself overflows `u64`.
6#[inline]
7pub fn add_slippage(value: u64, bps: u16) -> u64 {
8    (((value as u128) * (10_000 + bps as u128)) / 10_000) as u64
9}
10
11/// `value * (10_000 - bps) / 10_000`. `bps` larger than 10_000 saturates to 0.
12#[inline]
13pub fn sub_slippage(value: u64, bps: u16) -> u64 {
14    let bps = bps.min(10_000) as u128;
15    (((value as u128) * (10_000 - bps)) / 10_000) as u64
16}
17
18/// `(value - delta, value + delta)` where `delta = value * bps / 10_000`.
19/// Returns `None` if any intermediate overflows `u128` — callers can map that
20/// to [`crate::math::QuoteError::MathOverflow`]. Mirrors `handle_bps` from
21/// `mayhem-program::math` so both `validate_market_cap` paths can share a
22/// slippage envelope.
23#[inline]
24pub fn slippage_bounds(value: u128, bps: u16) -> Option<(u128, u128)> {
25    let delta = value.checked_mul(u128::from(bps))?.checked_div(10_000)?;
26    Some((value.checked_sub(delta)?, value.checked_add(delta)?))
27}
28
29#[cfg(test)]
30mod tests {
31    use super::*;
32
33    #[test]
34    fn slippage_helpers_apply_correct_direction() {
35        // Sub: 100 * (10_000 - 250) / 10_000 = 97
36        assert_eq!(sub_slippage(100, 250), 97);
37        // Add: 100 * (10_000 + 250) / 10_000 = 102
38        assert_eq!(add_slippage(100, 250), 102);
39        // Saturates at full slippage
40        assert_eq!(sub_slippage(1_000_000, 10_000), 0);
41        assert_eq!(sub_slippage(1_000_000, 20_000), 0);
42    }
43
44    #[test]
45    fn slippage_bounds_brackets_value() {
46        // 1_000_000 * 250 / 10_000 = 25_000
47        assert_eq!(slippage_bounds(1_000_000, 250), Some((975_000, 1_025_000)));
48        // bps=0 collapses to a point envelope
49        assert_eq!(slippage_bounds(42, 0), Some((42, 42)));
50    }
51
52    #[test]
53    fn slippage_bounds_overflow_returns_none() {
54        // Multiplication overflows u128.
55        assert_eq!(slippage_bounds(u128::MAX, 1), None);
56    }
57}