nautechsystems/nautilus_trader · error

Signed transaction creates a contract instead of calling a d

Error message

Signed transaction creates a contract instead of calling a destination

What it means

After decoding the EIP-1559 transaction, the library requires tx.to to be TxKind::Call (a call to an existing address). If the transaction's `to` is TxKind::Create, it is a contract-creation transaction, which this code path does not support — identity and RPC matching checks assume a destination address exists.

Source

Thrown at crates/adapters/blockchain/src/execution/transaction.rs:217

) -> anyhow::Result<DecodedSignedTransaction> {
    let envelope = TxEnvelope::decode_2718_exact(raw_transaction).map_err(|_| {
        anyhow::anyhow!("Persisted signed transaction is not a complete EIP-2718 envelope")
    })?;
    let TxEnvelope::Eip1559(signed) = envelope else {
        anyhow::bail!("Persisted signed transaction is not EIP-1559");
    };
    anyhow::ensure!(
        signed.signature().normalize_s().is_none(),
        "Persisted transaction signature is not EIP-2 normalized"
    );
    let signer = signed
        .signature()
        .recover_address_from_prehash(&signed.signature_hash())
        .context("failed to recover persisted transaction signer")?;
    let hash = *signed.hash();
    let tx = signed.tx();
    let TxKind::Call(to) = tx.to else {
        anyhow::bail!("Signed transaction creates a contract instead of calling a destination");
    };
    anyhow::ensure!(
        tx.access_list.is_empty(),
        "Signed transaction access list is not empty"
    );

    Ok(DecodedSignedTransaction {
        hash,
        signer,
        chain_id: tx.chain_id,
        nonce: tx.nonce,
        to,
        value: tx.value,
        input: tx.input.clone(),
        gas_limit: tx.gas_limit,
        max_fee_per_gas: tx.max_fee_per_gas,
        max_priority_fee_per_gas: tx.max_priority_fee_per_gas,
    })

View on GitHub (pinned to 18893faf8b)

Solutions

  1. Set the `to` destination address on the transaction request before signing so the signed tx is TxKind::Call.
  2. Confirm you are not accidentally feeding a contract-deployment transaction into this call-oriented validation path.
  3. Trace where the transaction request is constructed and log the `to` field prior to signing to catch it being dropped/defaulted.
  4. If contract creation is intended, use a deployment flow instead of decode_signed_transaction/validate_signed_transaction.

Example fix

// before
let tx = TxRequest { input: calldata, ..Default::default() }; // to = None -> CREATE
// after
let tx = TxRequest { to: Address::from_str(contract)?, input: calldata, ..Default::default() };
Defensive patterns

Strategy: validation

Validate before calling

fn is_contract_call(tx: &TxEip1559) -> bool {
    matches!(tx.to, TxKind::Call(_))
}
// reject requests with to = None before signing

Type guard

fn destination(tx: &TxEip1559) -> Option<Address> {
    match tx.to { TxKind::Call(addr) => Some(addr), TxKind::Create => None }
}

Try / catch

match decode_signed_transaction(raw) {
    Err(e) if e.to_string().contains("creates a contract") => /* abort: deployment tx sent through call path */,
    Err(e) => return Err(e),
    Ok(decoded) => decoded,
}

Prevention

When it happens

Trigger: decode_signed_transaction encounters a signed EIP-1559 transaction whose `to` field is None/CREATE — i.e. the payload was built without a destination address, deploying bytecode instead of calling a contract.

Common situations: Building the transaction request without setting `to` (e.g. an empty or default-built TxRequest) which the signer serializes as contract creation; intentionally deploying a contract and then passing it through the adapter's call-validation path; a field-mapping bug dropping the destination address before signing.

Understand the failure class

Background: UnsupportedOperationException and "is not supported" errors: when a library deliberately refuses a call — this error's family across 30 libraries.

Related errors


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