{"record":{"id":"e10d2a361520af66","repo":"nautechsystems/nautilus_trader","slug":"validated-proxy-slot","errorCode":null,"errorMessage":"validated proxy slot","messagePattern":"validated proxy slot","errorType":"panic","errorClass":null,"httpStatus":null,"severity":"error","filePath":"crates/adapters/blockchain/src/rpc/verification.rs","lineNumber":655,"sourceCode":"                VerificationOutcome::Unavailable(failure) => {\n                    return VerificationOutcome::Unavailable(failure);\n                }\n                VerificationOutcome::Retryable(failure) => {\n                    return VerificationOutcome::Retryable(failure);\n                }\n                VerificationOutcome::LocallyInvalid(failure) => {\n                    return VerificationOutcome::LocallyInvalid(failure);\n                }\n            };\n\n            if code.is_empty() || keccak256(&code) != expected_hash {\n                return VerificationOutcome::Disagreement(\n                    self.failure(VerificationRead::DeploymentIdentity),\n                );\n            }\n\n            if let Some(proxy) = &contract.proxy {\n                let slot = B256::from_str(&proxy.storage_slot).expect(\"validated proxy slot\");\n                let expected =\n                    B256::from_str(&proxy.storage_value).expect(\"validated proxy storage value\");\n                match self.verify_storage(&address, &slot, block).await {\n                    VerificationOutcome::Verified(verified) if verified.value == expected => {}\n                    VerificationOutcome::Verified(_) => {\n                        return VerificationOutcome::Disagreement(\n                            self.failure(VerificationRead::DeploymentIdentity),\n                        );\n                    }\n                    VerificationOutcome::Disagreement(failure) => {\n                        return VerificationOutcome::Disagreement(failure);\n                    }\n                    VerificationOutcome::Unavailable(failure) => {\n                        return VerificationOutcome::Unavailable(failure);\n                    }\n                    VerificationOutcome::Retryable(failure) => {\n                        return VerificationOutcome::Retryable(failure);\n                    }","sourceCodeStart":637,"sourceCodeEnd":673,"githubUrl":"https://github.com/nautechsystems/nautilus_trader/blob/18893faf8b356be3320add8de2f861b0b647cf06/crates/adapters/blockchain/src/rpc/verification.rs#L637-L673","documentation":"While verifying a deployment manifest, `verify_deployment_manifest` parses the proxy's `storage_slot` string into a `B256` with `.expect(\"validated proxy slot\")`. The manifest is pre-validated by `validate_config` (verification.rs:1291), which rejects any manifest whose `storage_slot` is not valid hex — so this `expect` is an internal invariant: it should be unreachable if every manifest passed through validation. Hitting it means a `BlockchainDeploymentManifest` reached the verifier without going through `validate_config`.","triggerScenarios":"Calling `verify_deployment_manifest` with a manifest whose `proxy.storage_slot` is not valid 32-byte hex (wrong length, missing 0x, non-hex characters) and that was never checked by `validate_config` — e.g. deserialized from an untrusted JSON file or built programmatically in tests.","commonSituations":"Hand-edited deployment manifest JSON with a malformed storage slot; a typo'd slot like a 20-byte address used instead of a 32-byte slot; constructing the manifest in tests bypassing the validation entry point; using a manifest from an older schema version.","solutions":["Run the manifest through `validate_config` before verification so invalid slots are rejected with a proper error.","Fix the `proxy.storage_slot` in the manifest to be a full 32-byte hex string (66 chars including 0x).","In your own code, parse the slot with `B256::from_str` first and return an error instead of relying on the panic.","Check the manifest source/generator for schema drift producing wrong-length slots."],"exampleFix":"// before\nlet slot = B256::from_str(&proxy.storage_slot).expect(\"validated proxy slot\");\n// after\nlet slot = B256::from_str(&proxy.storage_slot)\n    .map_err(|e| anyhow::anyhow!(\"invalid proxy storage_slot {:?}: {e}\", proxy.storage_slot))?;","handlingStrategy":"validation","validationCode":"fn validate_proxy_slot(proxy: &ProxyInfo) -> Result<B256, String> {\n    let s = proxy.storage_slot.trim();\n    let hex = s.strip_prefix(\"0x\").unwrap_or(s);\n    if hex.len() != 64 || !hex.chars().all(|c| c.is_ascii_hexdigit()) {\n        return Err(format!(\"storage_slot must be 32-byte hex, got {s:?}\"));\n    }\n    B256::from_str(s).map_err(|e| e.to_string())\n}","typeGuard":null,"tryCatchPattern":"// Always funnel manifests through validate_config before verification\nvalidate_config(&manifest).map_err(|e| anyhow::anyhow!(\"invalid manifest: {e}\"))?;","preventionTips":["Treat manifests as validated input: refuse to verify any manifest that skipped validate_config.","Store storage slots as full 32-byte hex in the manifest source of truth.","Add a deserializer-level check (try_from) so malformed hex never constructs the type."],"tags":["panic","rust","hex","manifest-validation","proxy"],"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-14T05:17:10.506Z"}