Repository navigation
bug(genesis): ProtocolConfig and Denylist builders accept proxy addresses the node never reads #496
Description
Summary
The genesis builder lets ProtocolConfig and Denylist be placed at any proxy.address, but the node reads both contracts from hardcoded addresses. schemaAdminProxy(defaultAddress) only supplies a default and never enforces it. A genesis built with a different address (or, for Denylist, with the default on a non-localdev chain) builds without error, and the node then silently reads the wrong place.
ValidatorManager.ts ("proxy.address only supports ...") and NativeFiatToken.ts already reject a mismatching address; these two builders do not.
Details
ProtocolConfig
scripts/genesis/ProtocolConfig.ts:proxy: schemaAdminProxy(DEFAULT_PROXY_ADDRESS)accepts any address, andbuildSystemContractAllocusesproxy.address ?? DEFAULT_PROXY_ADDRESS.- The node reads
ProtocolConfigonly at0x3600000000000000000000000000000000000001(crates/execution-config/src/protocol_config.rs,crates/eth-engine/src/constants.rs). - If placed elsewhere, the EL falls back to chainspec defaults and the CL swallows the read error (
unwrap_or_defaultindecided.rs). Fee, gas-limit, and consensus params — and every later controller update — have no effect; only an error log remains.
Denylist
scripts/genesis/Denylist.ts:DEFAULT_PROXY_ADDRESS = denylistAddressByNetwork.localdevregardless ofctx.chainId, and any explicitproxy.addressis accepted.- The node reads it from
ArcChainSpec::denylist_address(), which is fixed per chain (mainnet0x3600000000000000000000000000000000000004). - If the Denylist is placed elsewhere, the node still reads the fixed address. Normally nothing is deployed there, so every lookup reads empty storage and nobody is denylisted (fails open, no error). If some other contract occupies that address, the node reads its storage instead and the result is undefined.
denylist_address_matches_genesis_builder_for_every_networkonly compares the Rust constants withaddresses.ts, not with the builder output.
Scope
No shipped genesis is currently wrong (mainnet sets 0x36...04 explicitly). Devnet and testnet deploy the Denylist after genesis, so the Denylist part only affects genesis builds that include it: localdev, mainnet regenerations, and new networks. The ProtocolConfig part affects any genesis build.
Reproduction
Build a ProtocolConfig with proxy.address = 0x00000000000000000000000000000000DeaDBeef, and a Denylist for chain id 5042 without proxy.address:
| Case | Expected | Actual |
|---|---|---|
NativeFiatToken with proxy.address != 0x36..00 |
REJECT (control) | REJECT |
ProtocolConfig with proxy.address != 0x36..01 |
REJECT | PASS — alloc at 0x...DeaDBeef, 0x36..01 absent |
Denylist for chain 5042 without proxy.address |
use mainnet address 0x36..04 |
placed at localdev 0x3605...4221, node reads 0x36..04 |
Denylist with arbitrary proxy.address |
REJECT | PASS |
Suggested Fix
- ProtocolConfig: in
superRefine, rejectproxy.addressother than0x36...01(case-insensitive), mirroringValidatorManager. - Denylist: derive the expected address from
ctx.chainIdviadenylistAddressByNetwork, use it as the default, and reject a mismatch or unknown chain ID. - Optionally, a test that builds each
assets/*/config.jsonand checks system-contract addresses against the Rust constants.
I'd be happy to open a PR for this if assigned.
Activity
osr21 commentedon Oct 29, 2026
One implementation detail:
schemaDenylistis module-level and its default address is fixed todenylistAddressByNetwork.localdev; it has noctx, whilebuildDenylistGenesisAllocsdoes. A network-specific default therefore likely needs to be derived in the builder (or through a schema factory keyed by chain ID), then any explicit address checked against that expected address.For regression coverage, test generated allocations as well as schema validation: verify ProtocolConfig and Denylist are allocated at the canonical addresses the node reads for each supported genesis network, and reject mismatches or unsupported chain IDs. Since the current mainnet config explicitly sets the canonical Denylist address, this looks like a genesis-builder footgun rather than evidence that live mainnet is misconfigured.