{"record":{"id":"d42b13f22cb89e5e","repo":"nautechsystems/nautilus_trader","slug":"persisted-transaction-signature-is-not-eip-2-norma","errorCode":null,"errorMessage":"Persisted transaction signature is not EIP-2 normalized","messagePattern":"Persisted transaction signature is not EIP-2 normalized","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"crates/adapters/blockchain/src/execution/transaction.rs","lineNumber":219,"sourceCode":"        anyhow::anyhow!(\"Persisted signed transaction is not a complete EIP-2718 envelope\")\n    })?;\n    let TxEnvelope::Eip1559(signed) = envelope else {\n        anyhow::bail!(\"Persisted signed transaction is not EIP-1559\");\n    };\n    anyhow::ensure!(\n        signed.signature().normalize_s().is_none(),\n        \"Persisted transaction signature is not EIP-2 normalized\"\n    );\n    let signer = signed\n        .signature()\n        .recover_address_from_prehash(&signed.signature_hash())\n        .context(\"failed to recover persisted transaction signer\")?;\n    let hash = *signed.hash();\n    let tx = signed.tx();\n    let TxKind::Call(to) = tx.to else {\n        anyhow::bail!(\"Signed transaction creates a contract instead of calling a destination\");\n    };\n    anyhow::ensure!(\n        tx.access_list.is_empty(),\n        \"Signed transaction access list is not empty\"\n    );\n\n    Ok(DecodedSignedTransaction {\n        hash,\n        signer,\n        chain_id: tx.chain_id,\n        nonce: tx.nonce,\n        to,\n        value: tx.value,\n        input: tx.input.clone(),\n        gas_limit: tx.gas_limit,\n        max_fee_per_gas: tx.max_fee_per_gas,\n        max_priority_fee_per_gas: tx.max_priority_fee_per_gas,\n    })\n}\n","sourceCodeStart":201,"sourceCodeEnd":237,"githubUrl":"https://github.com/nautechsystems/nautilus_trader/blob/18893faf8b356be3320add8de2f861b0b647cf06/crates/adapters/blockchain/src/execution/transaction.rs#L201-L237","documentation":"decode_signed_transaction enforces that the recovered signature is EIP-2 normalized (s <= n/2). A non-canonical s value allows signature malleability — the same transaction could appear under two hashes — so the pipeline refuses to accept such a signature on a persisted transaction.","triggerScenarios":"decode_signed_transaction (via its three callers) encounters a signed EIP-1559 transaction whose signature fails normalize_s() (returns Some, meaning s was in the high half of the curve order) — typically bytes signed by a non-conforming signer or hand-crafted/mutated signatures.","commonSituations":"Custom or buggy signing code that does not apply EIP-2 low-s normalization; a signature mutated in storage (bit flip making s high); test fixtures generated with raw ECDSA without normalization.","solutions":["Re-sign the transaction with a signer that performs EIP-2 low-s normalization (all mainstream libraries do)","Do not manually normalize and persist — normalize_s() output must be re-hashed/re-verified as a new signature","Check whether stored bytes were mutated (compare against original persisted blob/hashes)","Regenerate the intent and signed transaction end-to-end if the signer cannot be fixed"],"exampleFix":null,"handlingStrategy":"try-catch","validationCode":null,"typeGuard":null,"tryCatchPattern":"match validate_signed_transaction(&raw, &intent) {\n    Ok(()) => (),\n    Err(e) if e.to_string().contains(\"EIP-2 normalized\") => {\n        // reject the payload; flag the signer as non-conforming, do not mutate bytes in place\n    }\n    Err(e) => return Err(e),\n}","preventionTips":["Use well-established signers (alloy, ethers) that low-s normalize by default","Never hand-construct or mutate ECDSA s values","Verify a signature round-trip (recover signer) before persisting signed transactions"],"tags":["blockchain","cryptography","signature-malleability"],"backgroundTag":"checksum-mismatch","analyzedSha":"18893faf8b356be3320add8de2f861b0b647cf06","analyzedAt":"2026-09-08T20:49:34.690Z","contentChangedAt":"2026-09-08T20:49:34.690Z","schemaVersion":2},"datasetVersion":"2026-09-14T00:17:10.932Z"}