{"record":{"id":"8aebdd3b56cac754","repo":"nautechsystems/nautilus_trader","slug":"token-reports-unexpected-decimals","errorCode":null,"errorMessage":"Token {} reports unexpected decimals","messagePattern":"Token (.+?) reports unexpected decimals","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"crates/adapters/blockchain/src/execution/client.rs","lineNumber":5125,"sourceCode":"    ));\n\n    for token in [&plan.pool.token0, &plan.pool.token1] {\n        let decimals_call = ERC20::decimalsCall.abi_encode();\n        let decimals = required_verification(\n            executor\n                .verification\n                .verify_decoded_call(\n                    None,\n                    &token.address,\n                    U256::ZERO,\n                    &decimals_call,\n                    block,\n                    |result| ERC20::decimalsCall::abi_decode_returns(result).map_err(Into::into),\n                )\n                .await,\n            \"swap token decimals\",\n        )?;\n        anyhow::ensure!(\n            decimals.value == token.decimals,\n            \"Token {} reports unexpected decimals\",\n            token.address,\n        );\n        decisions.push(verification_decision(&decimals, Some(block), Some(block)));\n    }\n\n    let allowance_call = ERC20::allowanceCall {\n        owner: executor.wallet_address,\n        spender: plan.router,\n    }\n    .abi_encode();\n    let allowance = required_verification(\n        executor\n            .verification\n            .verify_decoded_call(\n                None,\n                &plan.token_in,","sourceCodeStart":5107,"sourceCodeEnd":5143,"githubUrl":"https://github.com/nautechsystems/nautilus_trader/blob/18893faf8b356be3320add8de2f861b0b647cf06/crates/adapters/blockchain/src/execution/client.rs#L5107-L5143","documentation":"Raised during pre-trade verification when an ERC20 token's on-chain decimals() disagrees with the token.decimals value recorded in the plan/manifest. Decimal mismatch would corrupt amount scaling (raw vs human units), so the library refuses to proceed. The check runs per swap token in the verification loop.","triggerScenarios":"Executing a swap plan for token.address where ERC20.decimals() != token.decimals — e.g. manifest recorded 18 for USDC (6) or the token contract changed decimals via upgrade/rebase mechanics.","commonSituations":"Hand-written deployment manifest with copied decimals from the wrong token, plan reused across chains where the same symbol maps to different contracts, fee-on-transfer/upgradable tokens that changed decimals, or address typo pulling a different token.","solutions":["Correct token.decimals in the deployment manifest to match the on-chain decimals() of token.address","Re-derive the manifest from on-chain reads (decimals, symbol, address) instead of manual entry","Verify token.address is the intended contract for the target chain (address typos cause cross-token reads)","If a token genuinely changed decimals, rebuild the plan and re-check all amount conversions"],"exampleFix":"// before\n\"token\": { \"address\": \"0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48\", \"decimals\": 18 } // USDC copied as 18\n// after\n\"token\": { \"address\": \"0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48\", \"decimals\": 6 }","handlingStrategy":"validation","validationCode":"async fn check_token_decimals(token: Address, manifest_decimals: u8) -> Result<(), String> {\n    let actual: u8 = erc20_decimals(token).await?;\n    if actual != manifest_decimals { return Err(format!(\"token {token} decimals {actual} != manifest {manifest_decimals}\")); }\n    Ok(())\n}","typeGuard":"fn decimals_match(manifest_decimals: u8, actual: u8) -> bool { manifest_decimals == actual }","tryCatchPattern":null,"preventionTips":["Read decimals on-chain when generating manifests; never copy from token lists blindly","Validate token.address against the official registry for the target chain","Re-verify decimals for upgradable tokens before each plan build","Keep amount conversions (raw <-> human) centralized so a decimals fix propagates everywhere"],"tags":["blockchain","verification","erc20","decimals","config"],"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"}