{"record":{"id":"a14b98b1be927fcf","repo":"nautechsystems/nautilus_trader","slug":"approve-authorization-does-not-match-the-transacti","errorCode":null,"errorMessage":"Approve authorization does not match the transaction call","messagePattern":"Approve authorization does not match the transaction call","errorType":"validation","errorClass":null,"httpStatus":null,"severity":"error","filePath":"crates/adapters/blockchain/src/execution/client.rs","lineNumber":2013,"sourceCode":"        None => Ok(()),\n        Some(TransactionAuthorization::Wrap { weth }) => {\n            anyhow::ensure!(\n                to == *weth && !value.is_zero() && input == WETH9::depositCall::SELECTOR,\n                \"Wrap authorization does not match the transaction call\"\n            );\n            Ok(())\n        }\n        Some(TransactionAuthorization::Approve {\n            token,\n            router,\n            amount,\n        }) => {\n            let expected = ERC20::approveCall {\n                spender: *router,\n                amount: *amount,\n            }\n            .abi_encode();\n            anyhow::ensure!(\n                to == *token && value.is_zero() && input == expected,\n                \"Approve authorization does not match the transaction call\"\n            );\n            Ok(())\n        }\n    }\n}\n\nfn verification_decision<T>(\n    verified: &Verified<T>,\n    height_start: Option<u64>,\n    height_end: Option<u64>,\n) -> ExecutionVerificationDecision {\n    ExecutionVerificationDecision {\n        read_class: verified.read.as_str(),\n        height_start,\n        height_end,\n        normalized_value_digest: verified.normalized_value_digest.to_string(),","sourceCodeStart":1995,"sourceCodeEnd":2031,"githubUrl":"https://github.com/nautechsystems/nautilus_trader/blob/18893faf8b356be3320add8de2f861b0b647cf06/crates/adapters/blockchain/src/execution/client.rs#L1995-L2031","documentation":"The client verifies that an authorized Approve operation exactly matches the signed transaction: `to` must be the authorized ERC20 token, value zero, and calldata must be the ABI-encoded approve(spender, amount) with the authorized router and amount. Mismatch is rejected to prevent signing calls beyond the granted approval authorization.","triggerScenarios":"prepare/sign path invoked with TransactionAuthorization::Approve but the transaction's `to` is not the authorized token, `value` is nonzero, or `input` is not exactly the encoded approveCall{spender: router, amount} — e.g. the approval amount or spender differs from what was authorized.","commonSituations":"Approval authorization generated for one router but the transaction targets a different spender; amount drift between plan creation and tx building; token address mismatch (wrong ERC20 in config); accidentally sending value with an approve call.","solutions":["Ensure the call is `to == token`, `value == 0`, and input is abi_encode of ERC20::approveCall{spender: router, amount} matching the authorization exactly.","Build the authorization and the transaction from the same source parameters so router/amount cannot diverge.","Verify the token and router addresses in config match the intended chain deployments.","If the approval intent changed, re-issue a new authorization rather than editing the transaction."],"exampleFix":"// before: amount differs from authorization\nlet input = ERC20::approveCall { spender: router, amount: amount * 2 }.abi_encode();\nverify_approve(auth, token, U256::zero(), &input)?; // mismatch\n// after\nlet input = ERC20::approveCall { spender: auth.router, amount: auth.amount }.abi_encode();\nverify_approve(auth, token, U256::zero(), &input)?;","handlingStrategy":"validation","validationCode":"let expected = ERC20::approveCall { spender: router, amount }.abi_encode();\nassert!(value.is_zero() && to == token && input == expected, \"approve call mismatch\");","typeGuard":"fn is_approve_auth(auth: &TransactionAuthorization) -> Option<(Address, Address, U256)> {\n    if let TransactionAuthorization::Approve { token, router, amount } = auth { Some((*token, *router, *amount)) } else { None }\n}","tryCatchPattern":"match result { Err(e) if e.to_string().contains(\"Approve authorization does not match\") => { reissue_authorization_and_retry(); } Ok(v) => v }","preventionTips":["Keep approval authorization and approve tx derived from one code path","Never send value with approve calls","Validate token/router addresses per chain before authorizing"],"tags":["security","authorization","erc20","approval"],"backgroundTag":"invalid-argument-value","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"}