nautechsystems/nautilus_trader · error

Call edge references an unpinned contract

Error message

Call edge references an unpinned contract

What it means

Both the caller and target of every call edge must be pinned in the manifest's contract_addresses set. An edge that references a contract with no pinned identity is rejected, because un-pinned addresses cannot be verified against known bytecode and could point at arbitrary or hostile code.

Source

Thrown at crates/adapters/blockchain/src/rpc/verification.rs:1405

    }
    let mut call_edges = HashSet::new();
    for edge in &manifest.call_edges {
        anyhow::ensure!(
            matches!(
                edge.purpose.as_str(),
                "wrap" | "approve" | "swap_sell" | "swap_buy"
            ) && matches!(
                edge.call_type.as_str(),
                "call" | "staticcall" | "delegatecall" | "callcode"
            ),
            "Deployment manifest call edge is invalid"
        );
        let caller = Address::from_str(&edge.caller)
            .map_err(|_| anyhow::anyhow!("Call edge address is invalid"))?;
        let target = Address::from_str(&edge.target)
            .map_err(|_| anyhow::anyhow!("Call edge address is invalid"))?;
        for address in [caller, target] {
            anyhow::ensure!(
                contract_addresses.contains(&address),
                "Call edge references an unpinned contract"
            );
        }
        anyhow::ensure!(
            call_edges.insert((
                edge.purpose.as_str(),
                caller,
                target,
                edge.call_type.as_str()
            )),
            "Deployment manifest contains a duplicate call edge"
        );
    }

    for purpose in ["swap_sell", "swap_buy"] {
        anyhow::ensure!(
            manifest

View on GitHub (pinned to 18893faf8b)

Solutions

  1. Add a pinned contract entry (address, role, runtime_code_hash) for each address the edge references
  2. Update the edge's caller/target to the exact addresses of already-pinned contracts
  3. Delete stale edges pointing at contracts that are no longer part of this deployment
  4. Regenerate the manifest so edges and pinned contracts stay consistent

Example fix

// before
{"call_edges": [{"caller": "0xaaa...", "target": "0xbbb...", "purpose": "wrap"}]}
// contracts section lacks 0xbbb...
// after
{"contracts": [{"address": "0xbbb...", "role": "WrappedNative", "runtime_code_hash": "0x..."}], "call_edges": [{"caller": "0xaaa...", "target": "0xbbb...", "purpose": "wrap"}]}
Defensive patterns

Strategy: validation

Validate before calling

let pinned: HashSet<Address> = manifest.contracts.iter()
    .filter_map(|c| Address::from_str(&c.address).ok())
    .collect();
for edge in &manifest.call_edges {
    for addr in [&edge.caller, &edge.target] {
        let a = Address::from_str(addr)?;
        if !pinned.contains(&a) {
            return Err(format!("call edge references unpinned contract {addr}"));
        }
    }
}

Type guard

fn edge_is_pinned(edge: &CallEdge, pinned: &HashSet<Address>) -> bool {
    [&edge.caller, &edge.target].iter().all(|s|
        Address::from_str(s).map(|a| pinned.contains(&a)).unwrap_or(false))
}

Try / catch

match validate_deployment_manifest(&manifest) {
    Err(e) if e.to_string().contains("unpinned contract") => {
        eprintln!("Pin every edge endpoint in the contracts section: {e}");
    }
    Err(e) => return Err(e),
    Ok(()) => {}
}

Prevention

When it happens

Trigger: Validating a manifest where a call edge's caller or target parses as a valid address but is absent from the contracts section — e.g. an edge added for a new helper contract that was never declared with a role and code hash.

Common situations: Adding a call edge for a new router/approval contract without adding its contract entry; redeploying a contract so its address changed but the edge still names the old address; copying edges from a different chain's manifest.

Understand the failure class

Background: Schema validation failed / invalid input schema: payload rejected because its shape doesn't match the expected schema — this error's family across 28 libraries.

Related errors


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