nautechsystems/nautilus_trader · error

mul_div overflow

Error message

mul_div overflow

What it means

encode_sqrt_ratio_x96 computes sqrt(amount0/amount1) * 2^96 using FullMath::mul_div; the expect panics when mul_div returns None. On the sqrt branch this happens when sqrt_amount0 * 2^96 overflows or sqrt_amount1 is effectively zero relative to the numerator, so the exact multiplication-division cannot be represented.

Source

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

    // To maintain precision, we'll calculate: sqrt(amount0 * 2^192 / amount1)
    // This is because: sqrt(amount0/amount1) * 2^96 = sqrt(amount0 * 2^192 / amount1)

    // First, scale amount0 by 2^192
    let q192 = U256::from(1u128) << 192;

    // Check if amount0 * 2^192 would overflow
    if amount0_u256 > U256::MAX / q192 {
        // If it would overflow, we need to handle it differently
        // We'll use: sqrt(amount0) * 2^96 / sqrt(amount1)
        let sqrt_amount0 = FullMath::sqrt(amount0_u256);
        let sqrt_amount1 = FullMath::sqrt(amount1_u256);

        assert!(!sqrt_amount1.is_zero(), "Division by zero in sqrt");

        let q96 = U256::from(1u128) << 96;

        // Use FullMath for precise division
        let result = FullMath::mul_div(sqrt_amount0, q96, sqrt_amount1).expect("mul_div overflow");

        // Convert to U160, truncating if necessary
        return if result > U256::from(U160::MAX) {
            U160::MAX
        } else {
            U160::from(result)
        };
    }

    // Standard path: calculate (amount0 * 2^192) / amount1, then sqrt
    let ratio_q192 = FullMath::mul_div(amount0_u256, q192, amount1_u256).expect("mul_div overflow");

    // Take the square root of the ratio
    let sqrt_result = FullMath::sqrt(ratio_q192);

    // Convert to U160, truncating if necessary
    if sqrt_result > U256::from(U160::MAX) {
        U160::MAX

View on GitHub (pinned to 18893faf8b)

Solutions

  1. Pre-scale inputs so amount0 and amount1 are comparable magnitudes (adjust for token decimals) before encoding.
  2. Replace expect with match on the Option and fall back to a clamped/saturating computation or return an error.
  3. Check for zero/near-zero denominator amounts before calling.

Example fix

// before
let result = FullMath::mul_div(sqrt_amount0, q96, sqrt_amount1).expect("mul_div overflow");
// after
let result = FullMath::mul_div(sqrt_amount0, q96, sqrt_amount1)
    .unwrap_or(U256::from(U160::MAX)); // or return an error to the caller
Defensive patterns

Strategy: validation

Validate before calling

if sqrt_amount1.is_zero() || sqrt_amount0.checked_mul(q96).is_none() { return Err(anyhow!("inputs out of representable range")); }

Type guard

fn encodable(a0: U256, a1: U256) -> bool { !a1.is_zero() && a0 <= U256::MAX >> 96 }

Try / catch

let result = FullMath::mul_div(sqrt_amount0, q96, sqrt_amount1)
    .unwrap_or_else(|| return default_sqrt_price());

Prevention

When it happens

Trigger: Calling encode_sqrt_ratio_x96 with extreme amount ratios — sqrt_amount0 * q96 not fitting in U256, or sqrt_amount1 near zero producing an unrepresentable quotient — commonly from hand-constructed extreme prices in tests or malformed token amounts.

Common situations: Tests or fixtures encoding prices for tokens with wildly different decimals; callers passing amounts that were not pre-scaled; price ratios beyond U256 fixed-point capacity.

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/a1b2b8ba08697942. Report an issue: GitHub.