From 8b91d57a91742961938f8af84dc83a76b037cdd8 Mon Sep 17 00:00:00 2001 From: Khimesh Dewangan Date: Sat, 11 Jul 2026 22:14:12 +0530 Subject: [PATCH 1/2] fix(disputegamefactory): apply CEI pattern fix in create() and createWithInitData() Moves _finalizeGameCreation() before the external proxy.initialize()/initializeWithInitData() call in both create() and createWithInitData() functions to follow the Checks-Effects-Interactions (CEI) pattern. This prevents state-desynchronization where a dispute game could be initialized but not yet registered in the factory registry, which could cause integration failures and stale state reads for downstream components. Closes #356. --- src/L1/proofs/DisputeGameFactory.sol | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/src/L1/proofs/DisputeGameFactory.sol b/src/L1/proofs/DisputeGameFactory.sol index c79f3e9a4..c670dc71d 100644 --- a/src/L1/proofs/DisputeGameFactory.sol +++ b/src/L1/proofs/DisputeGameFactory.sol @@ -150,8 +150,10 @@ contract DisputeGameFactory is ProxyAdminOwnedBase, ReinitializableBase, Ownable returns (IDisputeGame proxy_) { proxy_ = _createGameImpl(_gameType, _rootClaim, _extraData); - proxy_.initialize{ value: msg.value }(); + // EFFECT: Finalize and register state first (CEI pattern) _finalizeGameCreation(_gameType, _rootClaim, _extraData, proxy_); + // INTERACTION: Perform external initialization call last + proxy_.initialize{ value: msg.value }(); } function createWithInitData( @@ -165,8 +167,10 @@ contract DisputeGameFactory is ProxyAdminOwnedBase, ReinitializableBase, Ownable returns (IDisputeGame proxy_) { proxy_ = _createGameImpl(_gameType, _rootClaim, _extraData); - proxy_.initializeWithInitData{ value: msg.value }(_initData); + // EFFECT: Finalize and register state first (CEI pattern) _finalizeGameCreation(_gameType, _rootClaim, _extraData, proxy_); + // INTERACTION: Perform external initialization call last + proxy_.initializeWithInitData{ value: msg.value }(_initData); } /// @notice Creates a new DisputeGame proxy contract. From 0b83314473965e8bfb96c4a112948aa50e680b0d Mon Sep 17 00:00:00 2001 From: Khimesh Dewangan Date: Sat, 11 Jul 2026 23:02:49 +0530 Subject: [PATCH 2/2] fix(l2genesis): explicitly initialize WETH storage slots after vm.etch The setWETH() function uses vm.etch to deploy WETH bytecode, which skips constructor execution. While the WETH contract has no constructor side effects (name/symbol are pure L1Block readers, decimals is constant, and mappings correctly start empty), this change makes the initialization explicit by: 1. Zeroing _balanceOf (slot 0) and _allowance (slot 1) via vm.store for clarity 2. Updating the NatSpec comment to document why each storage slot is correct This addresses Lumi audit finding #357 and makes the safety invariant auditable at a glance. Closes #357 --- scripts/L2Genesis.s.sol | 14 ++++++++++++-- 1 file changed, 12 insertions(+), 2 deletions(-) diff --git a/scripts/L2Genesis.s.sol b/scripts/L2Genesis.s.sol index 46bb72bf7..eff52be15 100644 --- a/scripts/L2Genesis.s.sol +++ b/scripts/L2Genesis.s.sol @@ -275,10 +275,20 @@ contract L2Genesis is Script { } /// @notice This predeploy is following the safety invariant #1. - /// This contract is NOT proxied and the state that is set - /// in the constructor is set manually. + /// This contract is NOT proxied. + /// The WETH contract (src/L2/WETH.sol) has no constructor side effects: + /// - `name()` and `symbol()` are `pure` functions that read from the + /// L1Block predeploy, not from storage. + /// - `decimals` is a `constant` (no storage slot). + /// - `_balanceOf` (slot 0) and `_allowance` (slot 1) are mappings + /// that correctly start empty. + /// We explicitly zero both storage slots after `vm.etch` for clarity, + /// even though the EVM zero-initializes all storage after `vm.etch`. function setWETH() internal { vm.etch(Predeploys.WETH, vm.getDeployedCode("WETH.sol:WETH")); + // Explicitly zero-initialize storage slots for audit clarity: + vm.store(Predeploys.WETH, bytes32(uint256(0)), bytes32(0)); // _balanceOf (slot 0) + vm.store(Predeploys.WETH, bytes32(uint256(1)), bytes32(0)); // _allowance (slot 1) } /// @notice This predeploy is following the safety invariant #2.