nautechsystems/nautilus_trader · error · anyhow::Error

Invalid fee denom: {e}

Error message

Invalid fee denom: {e}

What it means

The fee Coin's denom string (the on-chain token denomination, e.g. "adydx") is parsed into cosmrs's Denom type, which validates denomination rules. If self.fee_denom is not a valid Cosmos SDK coin denomination, parse fails and this error is returned.

Source

Thrown at crates/adapters/dydx/src/grpc/builder.rs:108

        // Gas price for dYdX (typically 0.025 adydx per gas)
        let gas_price = Decimal::new(25, 3); // 0.025
        let amount = (gas_price * gas_limit).ceil();

        let gas_limit_u64 = gas_limit
            .to_u64()
            .ok_or_else(|| anyhow::anyhow!("Failed converting gas limit to u64"))?;

        let amount_u128 = amount
            .to_u128()
            .ok_or_else(|| anyhow::anyhow!("Failed converting gas cost to u128"))?;

        Ok(Fee::from_amount_and_gas(
            Coin {
                amount: amount_u128,
                denom: self
                    .fee_denom
                    .parse()
                    .map_err(|e| anyhow::anyhow!("Invalid fee denom: {e}"))?,
            },
            gas_limit_u64,
        ))
    }

    /// Get default fee (zero fee).
    fn default_fee() -> Fee {
        Fee {
            amount: vec![],
            gas_limit: 0,
            payer: None,
            granter: None,
        }
    }

    /// Build a transaction for given messages.
    ///
    /// When `authenticator_ids` is provided, the transaction will include a `TxExtension`

View on GitHub (pinned to 18893faf8b)

Solutions

  1. Set fee_denom to the correct lowercase native denom, "adydx", in the client config.
  2. Check for empty/missing config: ensure the fee denom config field is populated (this will otherwise parse as an invalid empty denom).
  3. Match the denom exactly to what the dYdX chain expects for the target network (mainnet vs testnet).
  4. Validate the denom against Cosmos denom rules (lowercase alnum, may contain '/' and ':', 3-128 chars) before constructing the builder.

Example fix

// before
// fee_denom: "ADYDX".to_string(), // uppercase invalid
// after
fee_denom: "adydx".to_string(),
Defensive patterns

Strategy: validation

Validate before calling

fn is_valid_denom(d: &str) -> bool {
    !d.is_empty()
        && d.len() >= 3
        && d.len() <= 128
        && d.chars().all(|c| c.is_ascii_lowercase() || c.is_ascii_digit() || c == '/' || c == ':')
}
assert!(is_valid_denom(&fee_denom));

Try / catch

let fee = client.calculate_fee(instrument_id, gas_limit)
    .map_err(|e| {
        if e.to_string().contains("Invalid fee denom") {
            // fix fee_denom config to "adydx"
        }
        e
    })?;

Prevention

When it happens

Trigger: Calling calculate_fee when the builder's fee_denom field contains an invalid denom: empty string, uppercase letters, invalid characters, or a too-long string per Cosmos denom rules.

Common situations: Configuring the adapter with a typo'd fee denom (e.g. "ADYDX" instead of "adydx"); an empty or missing config field defaulting to an invalid denom; a network whose native denom differs from the configured one (mainnet adydx vs testnet variants).

Understand the failure class

Background: "Invalid value" and "allowed values are" config errors: what your library rejected and how to fix it — this error's family across 41 libraries.

Related errors


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