ethereum-optimism / ethereum-optimism/optimism
op-contracts: Blueprint Deployment Boundary Bug
- Dominant language
- Go
- Stars
- 6.5k
- Forks
- 4k
- Avg merge
- 2d 15h
- Merged PRs (30d)
- 145
Description
OP Stack contract deployment fails on **any fresh EVM environment** (local development, custom chains) due to issues around the 24KB contract size limit. This bug is hidden on Ethereum Mainnet and Sepolia because contracts are pre-deployed there.
### Two Related Failure Modes
| Scenario | Contract Size | Error | Cause |
| ----------------------------------------------------- | ------------- | ----------------------------------------------- | ---------------------------------------------------- |
| **Default v5.0.0 settings** (`optimizer_runs = 5000`) | > 24KB | `max code size exceeded` → `DeploymentFailed()` | Contracts too large for EVM |
| **Aggressive optimization** (`optimizer_runs = 200`) | < 24KB | `NotABlueprint()` | Single blueprint, `target2 = address(0)` not handled |
### The Catch-22
When a developer encounters `max code size exceeded`, the natural response is to lower `optimizer_runs`. This shrinks contracts below 24KB, requiring only 1 blueprint, which then triggers `NotABlueprint()` because `target2 = address(0)` is not handled. Developers are trapped between two bugs regardless of optimization settings.
---
## Affected Environments
| Environment | State | Bug Triggered? |
| ----------------------- | ------------ | -------------------- |
| Anvil / Hardhat / Fresh | Empty/Fresh | **Yes** - Always |
| Ethereum Mainnet | Pre-deployed | No (reuses existing) |
| Sepolia | Pre-deployed | No (reuses existing) |
---
## Root Cause
**File:** `packages/contracts-bedrock/src/libraries/Blueprint.sol`
The `deployFrom` function unconditionally parses `_target2` as a blueprint, even when it's `address(0)`:
```solidity
function deployFrom(address _target1, address _target2, bytes32 _salt, bytes memory _data)
internal returns (address newContract_)
{
Preamble memory preamble1 = parseBlueprintPreamble(address(_target1).code);
// ...validation...
// BUG: No check if _target2 == address(0)
// When contract fits in 1 blueprint, _target2 is address(0)
// address(0).code returns empty bytes → reverts with NotABlueprint()
Preamble memory preamble2 = parseBlueprintPreamble(address(_target2).code); // FAILS
// ...
}
```
### Why Hidden on Mainnet/Sepolia
```solidity
if (preComputedAddress.code.length > 0) {
return preComputedAddress; // Skip deployment → bug never triggered
}
deployFromBlueprint(...); // Only reaches here on fresh chains → BUG
```
---
## Steps to Reproduce
### Prerequisites
- Go 1.21+, Foundry, Git
### Quick Steps
```bash
# 1. Clone and build
git clone https://github.com/ethereum-optimism/optimism.git && cd optimism
git checkout op-contracts/v5.0.0 && git submodule update --init --recursive
cd packages/contracts-bedrock && forge build
# 2. Build op-deployer
cd ../../op-deployer && go build -o /tmp/op-deployer ./cmd/op-deployer
# 3. Start Anvil
anvil --host 0.0.0.0 --port 8545 --accounts 10 --balance 10000 --chain-id 31337
# 4. Initialize deployment
mkdir -p /tmp/anvil-deploy
/tmp/op-deployer init --l1-chain-id 31337 --l2-chain-ids 42069 --intent-type custom --workdir /tmp/anvil-deploy
```
### Configure intent.toml
```toml
configType = "custom"
l1ChainID = 31337
fundDevAccounts = true
# Use file:// locator pointing to built artifacts (bypasses embedded)
l1ContractsLocator = "file:///path/to/optimism/packages/contracts-bedrock/forge-artifacts"
l2ContractsLocator = "file:///path/to/optimism/packages/contracts-bedrock/forge-artifacts"
[superchainRoles]
SuperchainProxyAdminOwner = "0xf39Fd6e51aad88F6F4ce6aB8827279cffFb92266"
SuperchainGuardian = "0xf39Fd6e51aad88F6F4ce6aB8827279cffFb92266"
ProtocolVersionsOwner = "0xf39Fd6e51aad88F6F4ce6aB8827279cffFb92266"
Challenger = "0xf39Fd6e51aad88F6F4ce6aB8827279cffFb92266"
[[chains]]
id = "0x000000000000000000000000000000000000000000000000000000000000a455"
baseFeeVaultRecipient = "0x70997970C51812dc3A010C7d01b50e0d17dc79C8"
l1FeeVaultRecipient = "0x70997970C51812dc3A010C7d01b50e0d17dc79C8"
sequencerFeeVaultRecipient = "0x70997970C51812dc3A010C7d01b50e0d17dc79C8"
eip1559DenominatorCanyon = 250
eip1559Denominator = 50
eip1559Elasticity = 6
gasLimit = 60000000
[chains.roles]
l1ProxyAdminOwner = "0xf39Fd6e51aad88F6F4ce6aB8827279cffFb92266"
l2ProxyAdminOwner = "0xf39Fd6e51aad88F6F4ce6aB8827279cffFb92266"
systemConfigOwner = "0xf39Fd6e51aad88F6F4ce6aB8827279cffFb92266"
unsafeBlockSigner = "0x3C44CdDdB6a900fa2b585dd299e03d12FA4293BC"
batcher = "0x90F79bf6EB2c4f870365E785982E1f101E93b906"
proposer = "0x15d34AAf54267DB7D7c367839AAf71A00a2C6A65"
challenger = "0x9965507D1a55bcC2695C58ba16FB37d819B0A4dc"
```
### Run and Observe Error
```bash
/tmp/op-deployer apply \
--l1-rpc-url http://127.0.0.1:8545 \
--private-key 0xac0974bec39a17e36ba4a6b4d238ff944bacb478cbed5efcae784d7bf4f2ff80 \
--workdir /tmp/anvil-deploy \
--deployment-target live
```
**Error output:**
```
lvl=warn msg=Revert err="max code size exceeded"
lvl=warn msg=Revert label=OPContractsManagerDeployerImpl err="execution reverted" revertData=0x30116425
panic: revision id 103 cannot be reverted
```
---
## Reproduction Results
| Contract | Size | Margin | Blueprints |
| -------------------------- | ------ | ------- | ---------- |
| FaultDisputeGame | 25.9KB | -1.4KB | 2 needed |
| PermissionedDisputeGame | 26.6KB | -2.0KB | 2 needed |
| OPContractsManager | 11.4KB | +13.2KB | 1 needed |
| OPContractsManagerDeployer | 18.5KB | +6.1KB | 1 needed |
**Error chain:** `max code size exceeded` → `DeploymentFailed()` → `panic: revision id 103 cannot be reverted`
---
## Impact
| Metric | Value |
| ------------------ | ------------------------------------------------- |
| **Severity** | High |
| **Affected Users** | All local development, all custom EVM deployments |
| **Workaround** | None |
**Blocked:** Local dev (Anvil), custom chains, CI/CD pipelines, fresh testnet deployments.
---
## Related Files
- `packages/contracts-bedrock/src/libraries/Blueprint.sol`
- `packages/contracts-bedrock/scripts/deploy/DeployImplementations.s.sol`
- `packages/contracts-bedrock/src/L1/OPContractsManager.sol`
---
## Version Information
- **op-deployer:** v0.5.2
- **op-contracts:** v5.0.0
Contributor guide
Research direction
Start with packages/contracts-bedrock/src/libraries/Blueprint.sol, especially deployFrom, then inspect DeployImplementations.s.sol and OPContractsManager.sol for how blueprint targets are selected. Reproduce the failure using the provided Anvil and op-deployer steps; done means fresh deployments no longer hit either the contract-size or single-blueprint failure path.
Written by the indexing model from the issue text.
Assessment
- Tech stack
- go, solidity
- Domain
- blockchain, devops
- Issue type
- Bug
- Difficulty
- 4/5
- Estimated time
- 3-5 days
- Activity status
- Stale
- Clarity
- Clearly specified
- Newbie friendliness
- 45/100