{"record":{"id":"1bdfd3f1412ec4c4","repo":"nautechsystems/nautilus_trader","slug":"finalized-swap-input-base-amount-does-not-match","errorCode":null,"errorMessage":"Finalized Swap input {base_amount} does not match the persisted amount {}","messagePattern":"Finalized Swap input (.+?) does not match the persisted amount (.+?)","errorType":"validation","errorClass":"anyhow::Error","httpStatus":null,"severity":"error","filePath":"crates/adapters/blockchain/src/execution/client.rs","lineNumber":4777,"sourceCode":"\n    let dex = crate::exchanges::get_dex_extended(plan.pool.chain.name, &plan.pool.dex.name)\n        .ok_or_else(|| {\n            anyhow::anyhow!(\n                \"No RPC Swap decoder for {}:{}\",\n                plan.pool.chain.name,\n                plan.pool.dex.name\n            )\n        })?;\n    let event = dex.parse_swap_event_rpc(log)?;\n    let (base_amount, quote_amount) =\n        if plan.pool.get_base_token().address == plan.pool.token0.address {\n            (event.amount0, event.amount1)\n        } else {\n            (event.amount1, event.amount0)\n        };\n    let last_qty = match plan.order.order_side() {\n        OrderSide::Sell => {\n            anyhow::ensure!(\n                base_amount.is_positive() && base_amount.unsigned_abs() == plan.amount_in,\n                \"Finalized Swap input {base_amount} does not match the persisted amount {}\",\n                plan.amount_in\n            );\n            plan.order.quantity()\n        }\n        OrderSide::Buy => {\n            anyhow::ensure!(\n                quote_amount.is_positive() && quote_amount.unsigned_abs() == plan.amount_in,\n                \"Finalized Swap input {quote_amount} does not match the persisted amount {}\",\n                plan.amount_in\n            );\n            anyhow::ensure!(\n                base_amount.is_negative(),\n                \"Finalized Swap base amount {base_amount} is not a BUY output\"\n            );\n            raw_amount_to_quantity(\n                base_amount.unsigned_abs(),","sourceCodeStart":4759,"sourceCodeEnd":4795,"githubUrl":"https://github.com/nautechsystems/nautilus_trader/blob/18893faf8b356be3320add8de2f861b0b647cf06/crates/adapters/blockchain/src/execution/client.rs#L4759-L4795","documentation":"For SELL orders, the finalized Swap event's base-side amount must be positive and equal in absolute value to the persisted plan.amount_in. A mismatch means the on-chain swap input differs from the amount the execution plan recorded, indicating the persisted record no longer describes the actual fill.","triggerScenarios":"Verifying a SELL swap where event.amount0/amount1 (selected as base per token ordering) is <= 0, or its unsigned_abs() differs from plan.amount_in — e.g. the persisted plan amount was quoted pre-slippage/fee while the chain executed a different input, or amount0/amount1 were assigned to the wrong sides.","commonSituations":"Plan amount stored with different decimals scaling than the decoded event; token0/token1 ordering mismatch so base/quote amounts are swapped; the tx actually executed a partial or altered input after being front-run or partially filled; plan amount updated after submission without re-verifying.","solutions":["Confirm base/quote side selection (amount0 vs amount1) matches the pool's token ordering used when persisting amount_in","Re-persist amount_in from the exact quote used at submission time (same decimals and scaling)","Verify the tx really was the plan's transaction (check tx hash and router path); a different tx's log will not match","If fees-on-transfer change effective input, compare against gross amount and account for the fee explicitly"],"exampleFix":null,"handlingStrategy":"validation","validationCode":"if order_side == OrderSide::Sell && !(base_amount.is_positive() && base_amount.unsigned_abs() == plan.amount_in) {\n    return Err(anyhow!(\"SELL input mismatch: event={} plan={}\", base_amount, plan.amount_in));\n}","typeGuard":null,"tryCatchPattern":"match verify_finalized_swap(...).await {\n    Err(e) if e.to_string().contains(\"does not match the persisted amount\") => requote_and_replan(),\n    other => other,\n}","preventionTips":["Persist amount_in from the exact executed quote with matching decimals","Derive base/quote side from the pool's canonical token0/token1 ordering","Assert tx hash and log index match the plan before amount comparison","Re-verify plan amounts after any manual mutation or partial-fill logic"],"tags":["blockchain","swap","amount-mismatch","invariant"],"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-23T08:17:48.524Z"}