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::MAXView on GitHub (pinned to 18893faf8b)
Solutions
- Pre-scale inputs so amount0 and amount1 are comparable magnitudes (adjust for token decimals) before encoding.
- Replace expect with match on the Option and fall back to a clamped/saturating computation or return an error.
- 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
- Scale amounts for token decimals before encoding prices.
- Reject zero or near-zero denominators early.
- Avoid pre-multiplied Q64.96 inputs to this function.
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
- mul_div_rounding_up failed
- div_rounding_up failed
- Liquidity addition overflow: x={current}, y={y}, delta={delt
- Contract address should be set in logs
- Contract address should be set in logs
AI-assisted analysis of nautechsystems/nautilus_trader@18893faf8b (2026-09-08).
Data as JSON: /api/errors/a1b2b8ba08697942.
Report an issue: GitHub.