{"record":{"id":"cdb455d78ae9051b","repo":"nautechsystems/nautilus_trader","slug":"quote-spend-limit-decimals-do-not-match-the-deploy","errorCode":null,"errorMessage":"Quote spend limit decimals do not match the deployment manifest","messagePattern":"Quote spend limit decimals do not match the deployment manifest","errorType":"validation","errorClass":"anyhow::Error","httpStatus":null,"severity":"error","filePath":"crates/adapters/blockchain/src/execution/client.rs","lineNumber":456,"sourceCode":"                    \"Configured token {address} has no deployment manifest identity\"\n                );\n            }\n        }\n\n        for (token_in, token_out) in config.allowed_token_pairs.as_deref().unwrap_or_default() {\n            let token_in = validate_address(token_in)?;\n            let token_out = validate_address(token_out)?;\n            anyhow::ensure!(\n                token_in != token_out\n                    && token_decimals.contains_key(&token_in)\n                    && token_decimals.contains_key(&token_out),\n                \"Allowed token pair {token_in} -> {token_out} is not fully pinned by the deployment manifest\"\n            );\n        }\n\n        for limit in config.quote_spend_limits.as_deref().unwrap_or_default() {\n            let spend_token = validate_address(&limit.spend_token)?;\n            anyhow::ensure!(\n                token_decimals.get(&spend_token) == Some(&limit.spend_token_decimals),\n                \"Quote spend limit decimals do not match the deployment manifest\"\n            );\n        }\n\n        let pool_contracts = role_addresses(BlockchainContractRole::Pool)?;\n\n        for pool in &manifest.pools {\n            let pool_address = Address::from_str(&pool.address)\n                .map_err(|_| anyhow::anyhow!(\"Deployment manifest pool address is invalid\"))?;\n            let pool_factory = Address::from_str(&pool.factory)\n                .map_err(|_| anyhow::anyhow!(\"Deployment manifest pool factory is invalid\"))?;\n            let pool_quote = Address::from_str(&pool.quote_contract).map_err(|_| {\n                anyhow::anyhow!(\"Deployment manifest pool quote contract is invalid\")\n            })?;\n            anyhow::ensure!(\n                pool_contracts.contains(&pool_address)\n                    && pool_factory == factory","sourceCodeStart":438,"sourceCodeEnd":474,"githubUrl":"https://github.com/nautechsystems/nautilus_trader/blob/18893faf8b356be3320add8de2f861b0b647cf06/crates/adapters/blockchain/src/execution/client.rs#L438-L474","documentation":"Each configured quote spend limit declares the decimals of its spend_token; validation requires these to exactly match the decimals recorded for that token in the deployment manifest. A mismatch means the limit amount would be interpreted at the wrong precision, so validation fails.","triggerScenarios":"Calling preflight/validate_manifest_contracts when a config.quote_spend_limits entry has spend_token_decimals differing from token_decimals[spend_token] in the manifest, or the spend_token is absent from the manifest entirely.","commonSituations":"Token redefined with different decimals (e.g. USDC 6 vs 18 on a test chain); hand-edited config after a manifest update; copy-pasted limit config from another chain.","solutions":["Update spend_token_decimals in config.quote_spend_limits to match the manifest value","Update the token's decimals in the deployment manifest if the manifest value is stale/wrong","Ensure spend_token is present in the manifest token registry at all"],"exampleFix":"// before\n{ spend_token: \"0xAAA...\", spend_token_decimals: 18 } // manifest says 6\n// after\n{ spend_token: \"0xAAA...\", spend_token_decimals: 6 }","handlingStrategy":"validation","validationCode":"for limit in config.quote_spend_limits {\n    let m = manifest.tokens.iter().find(|t| t.address == limit.spend_token)\n        .ok_or(\"spend token missing from manifest\")?;\n    assert_eq!(m.decimals, limit.spend_token_decimals, \"decimals mismatch for {}\", limit.spend_token);\n}","typeGuard":null,"tryCatchPattern":null,"preventionTips":["Generate spend-limit config from the manifest so decimals are never hand-typed","Re-validate limits after any manifest update","Record token decimals once per chain, never per config"],"tags":["config","validation","decimals","manifest"],"backgroundTag":"invalid-config-value","analyzedSha":"18893faf8b356be3320add8de2f861b0b647cf06","analyzedAt":"2026-09-08T20:49:34.690Z","contentChangedAt":"2026-09-08T20:49:34.690Z","schemaVersion":2},"datasetVersion":"2026-09-14T00:17:10.932Z"}