{"record":{"id":"f9f6dd78718842ca","repo":"nautechsystems/nautilus_trader","slug":"finalized-transaction-does-not-match-the-authentic","errorCode":null,"errorMessage":"Finalized transaction does not match the authenticated signed payload and persisted intent","messagePattern":"Finalized transaction does not match the authenticated signed payload and persisted intent","errorType":"validation","errorClass":"anyhow::Error","httpStatus":null,"severity":"critical","filePath":"crates/adapters/blockchain/src/execution/client.rs","lineNumber":4346,"sourceCode":"async fn verify_finalized_transaction_identity(\n    included: &IncludedTransaction,\n    intent: &ExecutionIntentRow,\n    nonce: u64,\n    raw_transaction: &[u8],\n    verification: &VerificationCoordinator,\n    wallet_address: Address,\n    chain_id: u32,\n    deployment_manifest: &BlockchainDeploymentManifest,\n    trace_purpose: &str,\n) -> anyhow::Result<Vec<ExecutionVerificationDecision>> {\n    let transaction_verification = required_verification(\n        verification.verify_transaction(&included.tx_hash).await,\n        \"finalized transaction\",\n    )?;\n    let transaction = &transaction_verification.value;\n    let signed = decode_signed_transaction(raw_transaction)?;\n    let (expected_to, expected_input, expected_value) = persisted_call_fields(intent)?;\n    anyhow::ensure!(\n        included.receipt.transaction_hash == included.tx_hash\n            && signed.hash == included.tx_hash\n            && signed.signer == wallet_address\n            && signed.chain_id == u64::from(chain_id)\n            && signed.nonce == nonce\n            && signed.to == expected_to\n            && signed.input == expected_input\n            && signed.value == expected_value,\n        \"Finalized transaction does not match the authenticated signed payload and persisted intent\"\n    );\n    validate_rpc_transaction_matches_payload(transaction, raw_transaction)\n        .context(\"finalized transaction identity mismatch\")?;\n\n    let trace_verification = required_verification(\n        verification.verify_call_trace(&included.tx_hash).await,\n        \"finalized call trace\",\n    )?;\n    validate_call_trace(","sourceCodeStart":4328,"sourceCodeEnd":4364,"githubUrl":"https://github.com/nautechsystems/nautilus_trader/blob/18893faf8b356be3320add8de2f861b0b647cf06/crates/adapters/blockchain/src/execution/client.rs#L4328-L4364","documentation":"This error is thrown by `verify_finalized_transaction_identity` in the blockchain execution client after a transaction is included on chain. It re-decodes the raw signed transaction that was submitted and ensures the on-chain receipt, the signed payload, and the persisted execution intent all agree on hash, signer, chain id, nonce, recipient (`to`), calldata (`input`), and value. It exists as a post-inclusion defense-in-depth check: if the chain finalized a transaction that does not exactly match what was signed and what was recorded as intent, execution must be treated as unverified and fail hard.","triggerScenarios":"`anyhow::ensure!` at client.rs:4346 fails when any of these hold: `included.receipt.transaction_hash != included.tx_hash`; the decoded `signed.hash` differs from `included.tx_hash`; `signed.signer != wallet_address` (signed by a different wallet); `signed.chain_id` mismatches the executor chain id (e.g. replayed or submitted to the wrong network); `signed.nonce` differs from the intended nonce (transaction replaced/rebroadcast with a different nonce); or `signed.to`/`signed.input`/`signed.value` differ from the fields persisted in the execution intent row (`persisted_call_fields`).","commonSituations":"A wallet or signing service was rotated or reconfigured so the transaction was signed with a different key; the same raw transaction was rebroadcast after a nonce mismatch/repacement with different fields; the persisted intent row in the database was mutated or written by a different code path/version than the transaction builder; the RPC node or indexer returned a receipt for the wrong transaction hash; a chain fork or wrong-endpoint (testnet vs mainnet) configuration causes chain-id mismatch.","solutions":["Compare the intended recipient, calldata, and value in the persisted execution intent against what was actually signed and submitted; if the intent row is stale or wrong, regenerate the intent and resubmit a fresh transaction.","Verify the signing wallet address matches `executor.wallet_address` and that the raw transaction bytes passed to this check are the exact bytes submitted (no re-signing or re-encoding in between).","Check that the configured chain id / RPC endpoint is the intended network and that the signed chain id matches it.","Check for nonce reuse or replacement: query the wallet's nonce history; if the transaction was replaced (e.g. by a speed-up with different fields), treat the earlier intent as superseded and re-run execution for the new intent.","If the RPC receipt/transaction lookups are suspect, re-fetch from a trusted or secondary node to rule out indexer corruption before retrying."],"exampleFix":"// before: submitting via a generic signer not tied to the configured executor wallet\nlet raw = signer.sign_transaction(tx).await?;\nexecutor.submit(&raw).await?; // executor.wallet_address != signer.address()\n\n// after: assert signing identity before submission so mismatches surface early\nanyhow::ensure!(signer.address() == executor.wallet_address, \"signer wallet does not match executor wallet\");\nlet raw = signer.sign_transaction(tx).await?;\nexecutor.submit(&raw).await?;","handlingStrategy":"validation","validationCode":"// Before finalization checks, confirm the submitted bytes still match the persisted intent.\nlet signed = decode_signed_transaction(&raw_transaction)?;\nlet (to, input, value) = persisted_call_fields(&intent)?;\nassert_eq!(signed.signer, wallet_address, \"wrong signing wallet\");\nassert_eq!(signed.chain_id, chain_id as u64, \"wrong chain\");\nassert_eq!(signed.nonce, nonce, \"nonce mismatch\");\nassert_eq!((signed.to, signed.input.clone(), signed.value), (to, input, value), \"payload drifted from intent\");","typeGuard":"fn matches_intent(signed: &DecodedSignedTransaction, intent: &ExecutionIntentRow, wallet: Address, chain_id: u32, nonce: u64) -> bool {\n    let Ok((to, input, value)) = persisted_call_fields(intent) else { return false };\n    signed.signer == wallet\n        && signed.chain_id == u64::from(chain_id)\n        && signed.nonce == nonce\n        && signed.to == to\n        && signed.input == input\n        && signed.value == value\n}","tryCatchPattern":"match verify_finalized_transaction(&included, &intent, nonce, &raw, &executor, purpose).await {\n    Ok(decisions) => apply(decisions),\n    Err(e) if e.to_string().contains(\"does not match the authenticated signed payload\") => {\n        alert_security_review(included.tx_hash); // do NOT auto-retry; investigate signer/nonce/intent drift\n    }\n    Err(e) => return Err(e),\n}","preventionTips":["Always sign with the exact wallet configured on the executor and assert the signer address before submission.","Submit the raw signed bytes unchanged; never re-encode or re-sign between signing and verification.","Persist the intent row (to/input/value/nonce/chain_id) atomically with the signing step.","Guard against nonce replacement (speed-up/cancel flows) so the verified intent corresponds to the actually mined transaction.","Pin the RPC endpoint to the intended network and assert chain id at startup."],"tags":["blockchain","transaction-verification","integrity-check","chain-id","nonce"],"backgroundTag":"internal-invariant-violation","analyzedSha":"18893faf8b356be3320add8de2f861b0b647cf06","analyzedAt":"2026-09-08T20:49:34.690Z","contentChangedAt":"2026-09-08T20:49:34.690Z","schemaVersion":2},"datasetVersion":"2026-09-14T05:17:10.506Z"}