Deployment artifacts are not bound to the configured RPC chain
Deployment artifacts are not bound to the configured RPC chain. Final disposition: Fixed.
Tracker: issue #28 · Status: Fixed · Verified at: audit-v3-rereview-handoff
Affected files
script/deploy_base.sh#L51-L290 script/retire_superseded.sh#L22-L55
Description
The operational scripts do not establish that their RPC endpoint and deployment artifacts belong to the same chain. deploy_base.sh selects the artifact namespace independently through NETWORK_KEY, which defaults to base-sepolia. Its final chain check compares the staged chainId, generated from BASE_RPC, with cast chain-id from that same RPC. A healthy RPC for the wrong chain therefore passes, after which the script promotes the wrong-chain addresses into the Base Sepolia artifact namespace.
retire_superseded.sh hardcodes the Base Sepolia chain directory and address artifact, but performs its reads and privileged cast send operations through an unconstrained CHAIN_RPC_URL. The same deployer and nonce sequence can produce identical contract addresses on different chains, so the mismatched script can reach real contracts instead of failing on absent code.
Vulnerable Scenario:
An operator runs either script with a stale but healthy RPC URL. Deployment records chain 31337 under the default Base Sepolia namespace because both sides of its chain check query that RPC. In the retirement test, Base Sepolia history and exclusions applied through a local RPC reporting chain 8453 de-whitelisted the same-address market there and revoked its relayer role, while the intended chain 84532 remained unchanged. The two local chains produced the same contract addresses from the same deployer and nonce sequence.
Two disposable local Anvil harnesses invoked the unmodified deployment and retirement scripts. They produced:
deployment_wrong_namespace_chain=31337ChainBindingDeploy PASSwrong_rpc_source_mask_role=11/truewrong_rpc_target_mask_role=0/falsecorrect_rpc_source_mask_role=0/falseChainBindingRetire PASS
Impact
A stale or copied RPC value can replace the authoritative deployment artifact with addresses from another chain, disrupting client routing and signature-domain resolution until rollback. The retirement path can instead disable a live market on the wrong chain by clearing its settlement mask and revoking its relayer role. Recovery requires privileged reauthorization and may incur the badge activation delay. The reproduced scenarios did not demonstrate theft or direct loss of funds.
Recommendations
Define an explicit mapping from every supported NETWORK_KEY to its expected numeric chain ID. Before simulation, broadcast, any cast send, or artifact mutation, query the RPC chain and fail unless it matches that expected ID.
Derive the retirement history directory and address artifact from the selected network, then require their recorded chain association to match both the expected chain and the RPC. Treat missing, malformed, or conflicting chain metadata as an error. Add regression tests covering correct and stale RPC endpoints, unset or unsupported network keys, mismatched artifacts and history, and same-address deployments across chains. Adding a network should require an explicit chain mapping rather than inheriting a default namespace.
Remediation
Fixed. NETWORK_KEY maps to exactly one chain id in script/network.sh, and the RPC is then required to serve that id. The tautological comparison — both sides read from the same RPC — is gone.
Worth saying: you also closed the gap the tautology was hiding, by detecting a local development node via web3_clientVersion. A fork of the right chain passes every chain-id check, and that was not in the finding.
Verification provenance
Final disposition: Fixed. Verified at audit-v3-rereview-handoff, commit f020448976eb7c4895e1626ef3228876cff2b7d2. This earlier remediation disposition is carried forward from the prior re-review, not independently re-verified in the generation-d pass. The report-wide scope and evidence limitations remain applicable; see the final report overview.