nautechsystems/nautilus_trader · error

Persisted signed transaction is not EIP-1559

Error message

Persisted signed transaction is not EIP-1559

What it means

decode_signed_transaction decodes a persisted raw signed transaction and only supports EIP-1559 typed transactions. The bytes first must decode as a complete EIP-2718 typed envelope; if the envelope decodes but is any variant other than Eip1559 (legacy, EIP-2930 access-list, EIP-4844 blob, etc.), this bail fires. The library deliberately narrows to EIP-1559 because downstream identity/fee validation assumes 1559 fields.

Source

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

    pub chain_id: u64,
    pub nonce: u64,
    pub to: Address,
    pub value: U256,
    pub input: Bytes,
    pub gas_limit: u64,
    pub max_fee_per_gas: u128,
    pub max_priority_fee_per_gas: u128,
}

/// Decodes and authenticates one complete signed EIP-1559 transaction.
pub(super) fn decode_signed_transaction(
    raw_transaction: &[u8],
) -> 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"
    );

View on GitHub (pinned to 18893faf8b)

Solutions

  1. Inspect the first byte of the raw transaction: values other than 0x02 indicate a non-EIP-1559 typed envelope; re-sign or re-persist the transaction as EIP-1559 (type 0x02).
  2. Check how the transaction was produced upstream (signer/wallet config) and force EIP-1559 signing (no access list, no blob fields).
  3. If legacy transactions are legitimate for your chain, decode them with a legacy path instead of routing them into decode_signed_transaction.
  4. Verify you persisted the signed EIP-1559 tx and not a differently-typed artifact (e.g. compare the tx hash/type from the signing step).

Example fix

// before
let envelope = TxEnvelope::decode_2718_exact(raw)?; // may be Eip2930/Eip4844 from signer
// after
let envelope = TxEnvelope::decode_2718_exact(raw)?;
assert!(matches!(envelope, TxEnvelope::Eip1559(_)), "signer must produce EIP-1559 (type 0x02) txs");
decode_signed_transaction(raw)?;
Defensive patterns

Strategy: validation

Validate before calling

fn is_eip1559_envelope(raw: &[u8]) -> bool {
    TxEnvelope::decode_2718_exact(raw)
        .map(|e| matches!(e, TxEnvelope::Eip1559(_)))
        .unwrap_or(false)
}
// call decode_signed_transaction only if is_eip1559_envelope(raw)

Type guard

fn as_eip1559(envelope: &TxEnvelope) -> Option<&Signed<TxLegacyOr1559Check>> {
    match envelope { TxEnvelope::Eip1559(s) => Some(s), _ => None }
}

Try / catch

match decode_signed_transaction(raw) {
    Err(e) if e.to_string().contains("not EIP-1559") => /* re-sign as 1559 or route to legacy decoder */,
    Err(e) => return Err(e),
    Ok(decoded) => decoded,
}

Prevention

When it happens

Trigger: Calling decode_signed_transaction (directly or via validate_rpc_transaction_matches_payload, verify_finalized_transaction_identity, validate_signed_transaction) with raw_transaction bytes that are an EIP-2718 envelope of a non-1559 type: a legacy signed tx, an EIP-2930 tx, or an EIP-4844 blob tx.

Common situations: Persisting transactions produced by an older signing path or wallet that emits legacy transactions; a chain/wallet upgrade switching the adapter to EIP-2930 or EIP-4844 typed txs; storing the wrong artifact (e.g. a 2930 tx built with an access list) and later replaying it through this decoder.

Understand the failure class

Background: Invalid enum value errors: "Unknown type", "Invalid scope", "must be one of" — when a string is not on the library's allowed list — this error's family across 23 libraries.

Related errors


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