# deploying contracts with forceDeployOnAddress will break contracts when callConstructor is false

forceDeployOnAddress leaves the bytecode hash in constructing state when callConstructor is false, making the deployed/upgraded contract uncallable and bricking it.

- Payout: Contest reward
- Severity: medium
- Program: zkSync
- Credited: unforgiven
- Disclosed: 2023-06-14
- Write-up quality: 6/10
- Original: https://github.com/code-423n4/2023-03-zksync-findings/issues/167
- Library page: https://bugbountylibrary.com/r/c4%3A2023-03-zksync%3AM-01
- Tags: domain:web3 impact:dos impact:funds-loss kind:contest platform:code4rena sev:medium tech:evm tech:solidity technique:source-review vuln:business-logic

**Target:** ContractDeployer.sol forceDeployOnAddress system contract deployment
**Root cause:** forceDeployOnAddress always calls _storeConstructingByteCodeHashOnAddress but only calls _constructContract (which transitions the hash to constructed) when callConstructor is true, so the address stays in constructing state.
**Steps:**
1. Call forceDeployOnAddress(_deployment, _sender) with _deployment.callConstructor = false.
2. Observe _storeConstructingByteCodeHashOnAddress sets the address bytecode hash with constructing flag (second byte = 1).
3. Since callConstructor is false, _constructContract is skipped and the hash is never set to constructed.
4. Any call to the deployed address behaves like EmptyContract/EOA; logic never executes and sent funds are lost.
**How found:** Code reading of forceDeployOnAddress: noticed _storeConstructingByteCodeHashOnAddress is unconditional while the constructed-state transition lives inside _constructContract, gated by callConstructor.
**Exploitation:** Requires the system/self caller to force-deploy or upgrade a contract (e.g. L1Messenger, L2EthToken, MsgValueSimulator) with callConstructor=false; the target becomes uncallable, breaking bridging/messaging/withdrawals and trapping funds sent to it.
**Insight:** When a state machine has paired enter/exit transitions, check every branch that sets the 'in-progress' state to confirm a matching exit exists on all paths, especially optional-flag branches.
