nautechsystems/nautilus_trader · error
Proxy target binding is invalid
Error message
Proxy target binding is invalid
What it means
This error is thrown by the deployment-manifest validator in the blockchain adapter when a contract's proxy entry fails the target-binding invariants: the target address must be nonzero, must differ from the proxy's own address, the target code hash must be nonzero, and the proxy storage value must be an EIP-1967-style slot value whose low 20 bytes equal the target address. It guarantees a proxy really points at a concrete implementation contract before the verifier uses the manifest.
Source
Thrown at crates/adapters/blockchain/src/rpc/verification.rs:1299
);
if let Some(proxy) = &contract.proxy {
anyhow::ensure!(
matches!(
proxy.kind.as_str(),
"eip1967_implementation" | "zeppelinos_implementation"
),
"Proxy kind is unsupported"
);
B256::from_str(&proxy.storage_slot)
.map_err(|_| anyhow::anyhow!("Proxy storage slot is invalid"))?;
let storage_value = B256::from_str(&proxy.storage_value)
.map_err(|_| anyhow::anyhow!("Proxy storage value is invalid"))?;
let target = Address::from_str(&proxy.target_address)
.map_err(|_| anyhow::anyhow!("Proxy target address is invalid"))?;
let target_hash = B256::from_str(&proxy.target_code_hash)
.map_err(|_| anyhow::anyhow!("Proxy target code hash is invalid"))?;
anyhow::ensure!(
target != Address::ZERO
&& target != address
&& target_hash != B256::ZERO
&& storage_value[..12].iter().all(|byte| *byte == 0)
&& storage_value[12..] == *target.as_slice(),
"Proxy target binding is invalid"
);
}
for probe in &contract.probes {
Bytes::from_str(&probe.call_data)
.map_err(|_| anyhow::anyhow!("Manifest probe call data is invalid"))?;
Bytes::from_str(&probe.expected_output)
.map_err(|_| anyhow::anyhow!("Manifest probe output is invalid"))?;
}
}
for contract in &manifest.contracts {View on GitHub (pinned to 18893faf8b)
Solutions
- Set proxy.target_address to the real implementation contract address (nonzero, different from the proxy address)
- Recompute proxy.storage_value as 32 bytes where the last 20 bytes are the target address (hex, left-padded with 12 zero bytes) and proxy.target_code_hash to the implementation's nonzero runtime code hash
- Regenerate the manifest from the deployment tooling instead of hand-editing, then recompute the manifest digest
Example fix
// before
"proxy": {"target_address": "0x0000000000000000000000000000000000000000", "storage_value": "0x...", "target_code_hash": "0x0..."}
// after
"proxy": {"target_address": "0x9f2c...impl", "storage_value": "0x0000000000000000000000009f2c...impl", "target_code_hash": "0xabc...32bytehash"} Defensive patterns
Strategy: validation
Validate before calling
use alloy_primitives::{Address, B256};
fn proxy_binding_ok(proxy_addr: &str, target: &str, code_hash: &str, storage_value: &str) -> bool {
let (Ok(target), Ok(hash), Ok(value), Ok(addr)) = (
Address::from_str(target), B256::from_str(code_hash),
B256::from_str(storage_value), Address::from_str(proxy_addr),
) else { return false };
target != Address::ZERO && target != addr && hash != B256::ZERO
&& value[..12].iter().all(|b| *b == 0) && value[12..] == *target.as_slice()
} Type guard
fn is_valid_proxy(p: &ProxyEntry) -> bool {
Address::from_str(&p.target_address).is_ok()
&& B256::from_str(&p.target_code_hash).is_ok()
&& B256::from_str(&p.storage_value).is_ok()
} Try / catch
match validate_manifest(&config) {
Err(e) if e.to_string().contains("Proxy target binding") => {
eprintln!("fix proxy target/storage_value fields: {e}");
}
Err(e) => return Err(e),
Ok(v) => v,
} Prevention
- Compute storage_value as the EIP-1967 implementation slot value: 12 zero bytes followed by the target address
- Never point a proxy at itself or the zero address
- Regenerate manifests with tooling instead of hand-editing hex
- Recompute the manifest digest after any fix so the digest check also passes
When it happens
Trigger: Calling the manifest verification routine with a deployment manifest whose contract.proxy has: a zero target_address, a target_address equal to the proxy contract's own address, a zero or malformed target_code_hash, or a storage_value that is not (12 zero bytes || target address) — e.g. an incorrectly computed EIP-1967 slot value or a value copied from the wrong proxy.
Common situations: Hand-editing deployment manifest JSON after deploy; regenerating a manifest with a script that writes the slot value in the wrong endianness or with the wrong padding; copying a proxy block between networks where the implementation address differs; typos in hex addresses; pointing the proxy at itself after a self-upgrade misconfiguration.
Understand the failure class
Background: "Invalid value" and "allowed values are" config errors: what your library rejected and how to fix it — this error's family across 41 libraries.
Related errors
- `router_addresses` must contain at least one router address
- Quote spend limit for {token_in} -> {token_out} is denominat
- Independent Blockchain execution verification is required
- Verification chain anchor ID does not match the configured c
- Verification chain anchor name does not match the configured
AI-assisted analysis of nautechsystems/nautilus_trader@18893faf8b (2026-09-08).
Data as JSON: /api/errors/3e10c90f9fac9aed.
Report an issue: GitHub.