nautechsystems/nautilus_trader · error

EIP-712 signing failed: {e}

Error message

EIP-712 signing failed: {e}

What it means

build_and_sign assembles the Polymarket order and then signs it with the EIP-712 order signer via order_signer.sign_order. Any failure from the signer (invalid private key, wallet/secp256k1 errors, malformed signing payload) is wrapped as "EIP-712 signing failed: {e}".

Source

Thrown at crates/adapters/polymarket/src/execution/order_builder.rs:429

            salt,
            maker: self.maker_address.clone(),
            signer: self.order_signer_address(),
            token_id: Ustr::from(token_id),
            maker_amount,
            taker_amount,
            side,
            signature_type: self.signature_type,
            expiration: expiration.to_string(),
            timestamp: timestamp_ms.to_string(),
            metadata: ZERO_BYTES32.to_string(),
            builder: POLYMARKET_NAUTILUS_BUILDER_CODE.to_string(),
            signature: SecretString::default(),
        };

        let signature = self
            .order_signer
            .sign_order(&poly_order, neg_risk)
            .map_err(|e| anyhow::anyhow!("EIP-712 signing failed: {e}"))?;
        poly_order.signature = SecretString::from(signature);

        Ok(poly_order)
    }

    fn order_signer_address(&self) -> String {
        match self.signature_type {
            SignatureType::Poly1271 => self.maker_address.clone(),
            _ => self.signer_address.clone(),
        }
    }
}

fn validation_failed(detail: impl Into<String>) -> OrderDeniedReason {
    OrderDeniedReason::ValidationFailed {
        detail: detail.into(),
    }
}

View on GitHub (pinned to 18893faf8b)

Solutions

  1. Inspect the wrapped {e} message; it distinguishes key-decoding failures from payload-encoding failures.
  2. Verify the signing private key is a valid 32-byte hex secret and matches the maker/signer address configured on the builder.
  3. Confirm environment/config supplies the key (e.g. private key env var set and not a placeholder) before initializing the execution client.

Example fix

// before
POLYMARKET_PRIVATE_KEY="changeme"  // invalid hex -> signing fails
// after
POLYMARKET_PRIVATE_KEY="0x<64 hex chars>"  // valid secp256k1 key matching maker address
Defensive patterns

Strategy: validation

Validate before calling

let key = std::env::var("POLYMARKET_PRIVATE_KEY")?;
let key = key.trim_start_matches("0x");
hex::decode(key).map_err(|e| format!("invalid private key hex: {e}"))?;
if key.len() != 64 { return Err("private key must be 64 hex chars".into()); }

Type guard

fn is_valid_private_key(s: &str) -> bool {
    let s = s.trim_start_matches("0x");
    s.len() == 64 && s.chars().all(|c| c.is_ascii_hexdigit())
}

Try / catch

let signed = match builder.build_limit_order(&order, ...) {
    Ok(o) => o,
    Err(e) if e.to_string().contains("EIP-712 signing failed") => {
        tracing::error!("signing failed, check wallet key/config: {e:#}");
        return Err(e);
    }
    Err(e) => return Err(e),
};

Prevention

When it happens

Trigger: Calling build_limit_order, build_limit_order_from_collateral, or build_market_order when the configured private key is invalid/empty, the key cannot produce a secp256k1 signature, or the order payload encoding fails inside sign_order.

Common situations: Missing or malformed POLYMARKET private key in env/config (wrong length, not hex, placeholder value); key does not match the configured maker/signer address; dependency or chain-id mismatch producing an unencodable payload.

Related errors


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