nautechsystems/nautilus_trader · error

Signed transaction calldata does not match persisted calldat

Error message

Signed transaction calldata does not match persisted calldata

What it means

`validate_signed_transaction` requires the signed transaction's `input` (calldata) to equal the calldata persisted in the intent. The intent records exactly which contract call was approved; different calldata means the signed bytes invoke a different function/arguments than authorized, so the library refuses to broadcast them.

Source

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

        "Signed transaction nonce {} does not match persisted nonce {}",
        tx.nonce,
        intent.nonce
    );
    anyhow::ensure!(
        tx.to == intent.to,
        "Signed transaction destination {} does not match persisted destination {}",
        tx.to,
        intent.to
    );
    anyhow::ensure!(
        tx.value == intent.value,
        "Signed transaction value does not match persisted value"
    );
    anyhow::ensure!(
        tx.input == intent.input,
        "Signed transaction calldata does not match persisted calldata"
    );
    anyhow::ensure!(
        tx.gas_limit <= intent.gas_limit,
        "Signed transaction gas limit {} exceeds configured ceiling {}",
        tx.gas_limit,
        intent.gas_limit
    );
    anyhow::ensure!(
        tx.max_fee_per_gas <= u128::from(intent.max_fee_per_gas),
        "Signed transaction max fee per gas {} wei exceeds configured ceiling {} wei",
        tx.max_fee_per_gas,
        intent.max_fee_per_gas
    );
    anyhow::ensure!(
        tx.max_priority_fee_per_gas <= tx.max_fee_per_gas,
        "Signed transaction priority fee per gas {} wei exceeds max fee per gas {} wei",
        tx.max_priority_fee_per_gas,
        tx.max_fee_per_gas
    );

View on GitHub (pinned to 18893faf8b)

Solutions

  1. Re-sign using calldata serialized once and carried verbatim from the intent (`intent.input.clone()`), then validate the fresh bytes.
  2. Use the same ABI-encoding code path/version in both intent creation and signing; never re-encode from parsed arguments at sign time.
  3. If the intended call legitimately changed, recreate the intent and re-sign it atomically.
  4. Diff the calldata bytes when debugging: differing prefix usually means a different function selector; differing tail means different arguments.

Example fix

// before
let input = router.encode_deploy(order_recomputed_at_sign_time); // re-encoded
let signed = sign(tx.set_data(input), &wallet);

// after
let input = intent.input.clone(); // persisted, approved calldata
let signed = sign(tx.set_data(input), &wallet);
validate_signed_transaction(&signed, &intent)?;
Defensive patterns

Strategy: validation

Validate before calling

fn calldata_matches(raw: &[u8], intent: &SignedTransactionIntent) -> bool {
    decode_signed_transaction(raw).map(|tx| tx.input == intent.input).unwrap_or(false)
}

Type guard

fn has_expected_calldata(d: &DecodedSignedTransaction, i: &SignedTransactionIntent) -> bool {
    d.input == i.input
}

Try / catch

if let Err(e) = validate_signed_transaction(&raw, &intent) {
    if e.to_string().contains("calldata") {
        tracing::error!("signed calldata diverges from approved intent; refusing broadcast");
        return Err(Error::CalldataMismatch(e));
    }
    return Err(e.into());
}

Prevention

When it happens

Trigger: `validate_signed_transaction` decodes signed bytes whose `tx.input` differs from `intent.input` — e.g. the signer re-encoded the ABI call with different arguments, a different function selector, or the persisted intent was written from an older calldata payload.

Common situations: ABI encoding differences between signer and intent writer (argument order, extra padding, differing encoder versions), order parameters recomputed at sign time instead of reusing the persisted ones, or replayed signed blobs from a previous order/epoch.

Understand the failure class

Background: Checksum mismatch errors: "checksum verification failed", "digest mismatch", "expected vs actual checksum" — what they mean and how to fix them — this error's family across 41 libraries.

Related errors


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