nautechsystems/nautilus_trader · error

Scale 10^{} exceeds U256 while converting {type_name}

Error message

Scale 10^{} exceeds U256 while converting {type_name}

What it means

scale_u256_to_raw computes 10^(fixed_precision − decimals) as a U256 power when decimals < fixed_precision. If checked_pow overflows U256, the exponent is impossibly large and the conversion aborts with this message naming the type being converted (e.g. 'Quantity' or 'Price').

Source

Thrown at crates/adapters/blockchain/src/decode.rs:100

    )?;
    let raw = PriceRaw::try_from(raw)
        .map_err(|e| anyhow::anyhow!("Failed to convert Price raw value: {e}"))?;
    Ok(Price::from_raw_checked(raw, precision)?)
}

fn scale_u256_to_raw(
    amount: U256,
    decimals: u8,
    fixed_precision: u8,
    raw_max: U256,
    type_name: &str,
    raw_max_name: &str,
) -> anyhow::Result<U256> {
    let raw = if decimals < fixed_precision {
        let scale = U256::from(10)
            .checked_pow(U256::from(fixed_precision - decimals))
            .ok_or_else(|| {
                anyhow::anyhow!(
                    "Scale 10^{} exceeds U256 while converting {type_name}",
                    fixed_precision - decimals
                )
            })?;
        amount.checked_mul(scale).ok_or_else(|| {
            anyhow::anyhow!(
                "{type_name} amount {amount} overflows U256 while scaling from {decimals} to {fixed_precision} decimals"
            )
        })?
    } else if decimals > fixed_precision {
        round_u256_half_even(amount, decimals - fixed_precision, type_name)?
    } else {
        amount
    };

    check_raw_range(raw, raw_max, type_name, raw_max_name)?;
    Ok(raw)
}

View on GitHub (pinned to 18893faf8b)

Solutions

  1. Verify the decimals argument — fetch ERC20.decimals() on-chain rather than hardcoding 0.
  2. Sanity-check decimals ∈ 0..=18 (typical token range) before calling the conversion.
  3. If decimals is genuinely tiny and the value huge, the amount exceeds domain representability; handle at a higher level.
  4. Trace the value source: a decoded-decimals of 0 often indicates a wrong ABI or failed static call defaulting to zero.

Example fix

// before
let qty = u256_to_quantity(amount, 0, FIXED_PRECISION)?;

// after
let decimals = token.decimals().call().await?; // e.g. 6 or 18
debug_assert!(decimals <= 18);
let qty = u256_to_quantity(amount, decimals, FIXED_PRECISION)?;
Defensive patterns

Strategy: validation

Validate before calling

// reject implausible decimals before scaling
fn sane_decimals(d: u8) -> Result<u8, String> {
    match d {
        0..=18 => Ok(d),
        other => Err(format!("implausible token decimals: {other}")),
    }
}

Type guard

fn plausible_decimals(d: u8) -> bool {
    d <= 18
}

Prevention

When it happens

Trigger: Calling u256_to_quantity/u256_to_price with a decimals value drastically smaller than the fixed precision, such that 10^(fixed_precision − decimals) cannot even be represented in U256 (exponent ≳ 78).

Common situations: Passing decimals=0 or garbage decimals (e.g. a u8::MAX sentinel or uninitialized value) for tokens that actually have 6–18 decimals; mis-wired ABI decoding returning 0 for decimals.

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