nautechsystems/nautilus_trader · error

mul_div_rounding_up failed

Error message

mul_div_rounding_up failed

What it means

get_next_sqrt_price_from_amount0_rounding_up computes the next sqrt price when adding token0, using mul_div_rounding_up(numerator, sqrt_price_x96, denominator). The expect panics when that operation fails, i.e. the multiplication numerator * sqrt_price_x96 overflows even after the overflow-safe fallback check, or the denominator arithmetic wraps (denominator < numerator).

Source

Thrown at crates/model/src/defi/tick_map/sqrt_price_math.rs:108

    sqrt_price_x96: U160,
    liquidity: u128,
    amount: U256,
    add: bool,
) -> U160 {
    if amount.is_zero() {
        return sqrt_price_x96;
    }
    let numerator = U256::from(liquidity) << 96;
    let sqrt_price_x96 = U256::from(sqrt_price_x96);
    let product = amount * sqrt_price_x96;

    if add {
        if product / amount == sqrt_price_x96 {
            let denominator = numerator + product;
            if denominator >= numerator {
                // always fit to 160bits
                let result = FullMath::mul_div_rounding_up(numerator, sqrt_price_x96, denominator)
                    .expect("mul_div_rounding_up failed");
                return U160::from(result);
            }
        }

        // Fallback: divRoundingUp(numerator1, (numerator1 / sqrtPX96).add(amount))
        let fallback_denominator = (numerator / sqrt_price_x96) + amount;
        let result = FullMath::div_rounding_up(numerator, fallback_denominator)
            .expect("div_rounding_up failed");

        // Check if result fits in U160
        assert!(result <= U256::from(U160::MAX), "Result overflows U160");
        U160::from(result)
    } else {
        // require((product = amount * sqrtPX96) / amount == sqrtPX96 && numerator1 > product);
        assert!(
            (product / amount) == sqrt_price_x96 && numerator > product,
            "Invalid conditions for amount0 removal: overflow or underflow detected"
        );

View on GitHub (pinned to 18893faf8b)

Solutions

  1. Bound the swap amount so the resulting price stays within representable U160/Q96.64 range before calling get_next_sqrt_price_from_input.
  2. Replace expect with error propagation (anyhow) so callers can reject the swap rather than panic.
  3. Validate pool sqrt_price_x96 sanity (within Q64.96 bounds) before simulation.

Example fix

// before
let result = FullMath::mul_div_rounding_up(numerator, sqrt_price_x96, denominator)
    .expect("mul_div_rounding_up failed");
// after
let result = FullMath::mul_div_rounding_up(numerator, sqrt_price_x96, denominator)
    .ok_or_else(|| anyhow::anyhow!("next sqrt price computation overflowed"))?;
Defensive patterns

Strategy: validation

Validate before calling

let max = max_swap_amount_for_price(sqrt_price_x96);
if amount > max { return Err(anyhow!("amount exceeds representable next price")); }

Type guard

fn price_in_bounds(sqrt_price_x96: U160) -> bool { sqrt_price_x96 >= MIN_SQRT_RATIO && sqrt_price_x96 <= MAX_SQRT_RATIO }

Try / catch

match FullMath::mul_div_rounding_up(numerator, sqrt_price_x96, denominator) {
    Some(r) => U160::from(r),
    None => return Err(anyhow!("next sqrt price overflow")),
}

Prevention

When it happens

Trigger: Trading token0 amounts so extreme that numerator + product overflows (denominator >= numerator fails) yet the code path still reaches the primary mul_div_rounding_up, or sqrt_price_x96 and amount combinations where product/amount no longer equals sqrt_price_x96 but the overflow-checked multiply still cannot be represented.

Common situations: Swap simulations with near-max U256 amounts; corrupted or malicious pool state with an inflated sqrt price; fuzz/tests exercising Uniswap V3's NextInitializedTickWithinOneWord-equivalent math at boundary values.

Understand the failure class

Background: "value must be between 0 and 1" / "out of range" / "must not be negative" errors: fixing range-validation failures across open-source libraries — this error's family across 42 libraries.

Related errors


AI-assisted analysis of nautechsystems/nautilus_trader@18893faf8b (2026-09-08). Data as JSON: /api/errors/785e64beacd59fad. Report an issue: GitHub.