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

  1. Set proxy.target_address to the real implementation contract address (nonzero, different from the proxy address)
  2. 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
  3. 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

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


AI-assisted analysis of nautechsystems/nautilus_trader@18893faf8b (2026-09-08). Data as JSON: /api/errors/3e10c90f9fac9aed. Report an issue: GitHub.