{"record":{"id":"92e43d2c4b9ca6cd","repo":"nautechsystems/nautilus_trader","slug":"weth-balance-after-transaction-did-not-increase","errorCode":null,"errorMessage":"WETH balance after transaction {} did not increase by {amount_wei}: expected {expected_balance}, was {}","messagePattern":"WETH balance after transaction (.+?) did not increase by (.+?): expected (.+?), was (.+?)","errorType":"validation","errorClass":"anyhow::Error","httpStatus":null,"severity":"error","filePath":"crates/adapters/blockchain/src/execution/client.rs","lineNumber":4528,"sourceCode":"        \"wrapped balance after finality\",\n    )\n    .with_context(|| {\n        format!(\n            \"failed to verify WETH balance after included transaction {} at block {}\",\n            included.tx_hash, included.block_number\n        )\n    })?;\n    let expected_balance = balance_before\n        .value\n        .checked_add(amount_wei)\n        .ok_or_else(|| {\n            anyhow::anyhow!(\n                \"WETH balance overflow for included transaction {} at block {}\",\n                included.tx_hash,\n                included.block_number\n            )\n        })?;\n    anyhow::ensure!(\n        balance_after.value == expected_balance,\n        \"WETH balance after transaction {} did not increase by {amount_wei}: expected {expected_balance}, was {}\",\n        included.tx_hash,\n        balance_after.value\n    );\n\n    Ok(vec![\n        verification_decision(&balance_before, Some(previous_block), Some(previous_block)),\n        verification_decision(\n            &balance_after,\n            Some(included.block_number),\n            Some(included.block_number),\n        ),\n    ])\n}\n\nasync fn verify_approve_allowance(\n    executor: &TransactionExecutor,","sourceCodeStart":4510,"sourceCodeEnd":4546,"githubUrl":"https://github.com/nautechsystems/nautilus_trader/blob/18893faf8b356be3320add8de2f861b0b647cf06/crates/adapters/blockchain/src/execution/client.rs#L4510-L4546","documentation":"After a wrap transaction finalizes, the adapter re-verifies on-chain state: WETH balance at the inclusion block must equal the prior-block balance plus exactly the wrap amount. This ensure! fires when the observed post-transaction balance differs — the deposit did not credit the expected amount (or the balance moved unexpectedly between the two sampled blocks). It guards against verifying a transaction whose on-chain effect does not match the intended wrap.","triggerScenarios":"`balance_after.value != balance_before.value + amount_wei` at `included.block_number` — e.g. the wrap tx actually sent ETH somewhere else, the WETH contract is not the expected one, or concurrent balance changes occurred between the previous block and the inclusion block.","commonSituations":"Verifying against a forked/reorged chain where balances differ from the canonical history, querying a different WETH address than the transaction used, an RPC serving stale or divergent state, or the included transaction being a different operation than the wrap that was intended.","solutions":["Re-query balanceOf at both blocks from an independent trusted RPC and compare","Confirm the weth_address used for verification matches the contract the transaction interacted with","Check for chain reorgs: verify the inclusion block hash is still canonical at that height","Inspect the actual transaction's logs (Deposit event) to see what was really executed"],"exampleFix":"// before\nlet weth = Address::from_str(&config.weth)?;\n// after\nlet weth = Address::from_str(&config.weth)?;\nanyhow::ensure!(weth == plan.expected_weth_address, \"WETH address mismatch in verification config\");","handlingStrategy":"try-catch","validationCode":"async fn precheck_weth(rpc: &dyn Provider, weth: Address, wallet: Address, block: u64, expected: U256) -> anyhow::Result<()> {\n    let bal = ERC20::balanceOfCall { account: wallet }.abi_encode();\n    let v = rpc.call(None, weth, bal, Some(block.into())).await?;\n    let decoded = ERC20::balanceOfCall::abi_decode_returns(&v, false)?;\n    anyhow::ensure!(decoded == expected, \"precheck balance mismatch\");\n    Ok(())\n}","typeGuard":"fn balances_match(balance_after: U256, balance_before: U256, amount: U256) -> bool {\n    balance_before.checked_add(amount) == Some(balance_after)\n}","tryCatchPattern":"match verify_wrap_balance_increase(&executor, &weth, amount, &included).await {\n    Ok(d) => apply(d),\n    Err(e) if e.to_string().contains(\"did not increase by\") => {\n        tracing::warn!(\"balance divergence on wrap {} — checking reorg\", included.tx_hash);\n        if chain_reorged(included.block_number).await? { rebuild_inclusion(included).await?; }\n    }\n    Err(e) => return Err(e),\n}","preventionTips":["Pin weth_address in config and cross-check it against the tx's log addresses","Detect reorgs by re-checking the inclusion block hash before verification","Use a single consistent RPC/fork for all verification calls in a session","Log Deposit event amount from the receipt and compare with the planned wrap amount"],"tags":["blockchain","verification","balance-mismatch","state-divergence"],"backgroundTag":"unexpected-api-response-shape","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"}