Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
10 changes: 8 additions & 2 deletions op-chain-ops/script/deploy.go
Original file line number Diff line number Diff line change
Expand Up @@ -211,9 +211,15 @@ func (b *forgeScriptBackendImpl) Deploy(artifact *foundry.Artifact, label string
b.host.AllowCheatcodes(address) // before constructor execution, give our script cheatcode access
b.host.state.MakeExcluded(address) // scripts are persistent across forks

// disable contract size constraints
// disable contract size constraints for script deployment
wasNoMaxCodeSize := b.host.noMaxCodeSize
b.host.EnforceMaxCodeSize(false)
defer b.host.EnforceMaxCodeSize(true)
defer func() {
// Only re-enable if it wasn't originally disabled
if !wasNoMaxCodeSize {
b.host.EnforceMaxCodeSize(true)
}
}()

// deploy the script
deployedAddr, err := b.host.Create(deployer, artifact.Bytecode.Object)
Expand Down
17 changes: 17 additions & 0 deletions op-chain-ops/script/script.go
Original file line number Diff line number Diff line change
Expand Up @@ -117,6 +117,9 @@ type Host struct {
// useCreate2Deployer uses the Create2Deployer for broadcasted
// create2 calls.
useCreate2Deployer bool

// noMaxCodeSize disables the maximum contract bytecode size check.
noMaxCodeSize bool
}

type HostOption func(h *Host)
Expand Down Expand Up @@ -161,6 +164,15 @@ func WithCreate2Deployer() HostOption {
}
}

// WithNoMaxCodeSize disables the maximum contract bytecode size check.
// This is useful for development environments where contracts may be compiled
// without optimizations and exceed the standard 24KB limit.
func WithNoMaxCodeSize() HostOption {
return func(h *Host) {
h.noMaxCodeSize = true
}
}

// NewHost creates a Host that can load contracts from the given Artifacts FS,
// and with an EVM initialized to the given executionContext.
// Optionally src-map loading may be enabled, by providing a non-nil srcFS to read sources from.
Expand Down Expand Up @@ -298,6 +310,11 @@ func NewHost(
h.env = WrapEVM(vm.NewEVM(blockContext, h.state, h.chainCfg, vmCfg))
h.env.SetTxContext(txContext)

// Apply noMaxCodeSize after EVM is initialized
if h.noMaxCodeSize {
h.EnforceMaxCodeSize(false)
}

return h
}

Expand Down
54 changes: 54 additions & 0 deletions op-chain-ops/script/script_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -472,3 +472,57 @@ func TestScriptErrorHandling(t *testing.T) {
})
}
}

func TestWithNoMaxCodeSize(t *testing.T) {
logger := testlog.Logger(t, log.LevelInfo)
af := foundry.OpenArtifactsDir("./testdata/test-artifacts")
scriptContext := DefaultContext
deployer := scriptContext.Sender

// Create init code that deploys a contract with >24KB runtime code
// Init code structure:
// PUSH2 0x6400 (25600 bytes = 25KB)
// PUSH1 0x0c (offset where runtime code starts)
// PUSH1 0x00 (memory destination)
// CODECOPY
// PUSH2 0x6400 (size to return)
// PUSH1 0x00 (memory offset)
// RETURN
runtimeSize := 25 * 1024 // 25KB runtime code
initCode := []byte{
0x61, 0x64, 0x00, // PUSH2 0x6400
0x60, 0x0c, // PUSH1 0x0c (12 bytes - length of this init code)
0x60, 0x00, // PUSH1 0x00
0x39, // CODECOPY
0x61, 0x64, 0x00, // PUSH2 0x6400
0x60, 0x00, // PUSH1 0x00
0xf3, // RETURN
}
// Append runtime code (can be any data, we'll use zeros)
runtimeCode := make([]byte, runtimeSize)
largeBytecode := append(initCode, runtimeCode...)

t.Run("WithNoMaxCodeSize allows large contracts", func(t *testing.T) {
h := NewHost(logger, af, nil, scriptContext, WithNoMaxCodeSize())
require.True(t, h.noMaxCodeSize, "noMaxCodeSize flag should be set")
require.True(t, h.env.Config().NoMaxCodeSize, "EVM should have NoMaxCodeSize enabled")

addr, err := h.Create(deployer, largeBytecode)
require.NoError(t, err, "Should deploy large contract when NoMaxCodeSize is enabled")
require.NotEqual(t, common.Address{}, addr, "Should return valid address")

// Verify the code was actually deployed
code := h.GetCode(addr)
require.NotEmpty(t, code, "Contract code should be deployed")
})

t.Run("Default behavior rejects large contracts", func(t *testing.T) {
h := NewHost(logger, af, nil, scriptContext)
require.False(t, h.noMaxCodeSize, "noMaxCodeSize flag should be false by default")
require.False(t, h.env.Config().NoMaxCodeSize, "EVM should enforce max code size by default")

_, err := h.Create(deployer, largeBytecode)
require.Error(t, err, "Should reject large contract by default")
require.Contains(t, err.Error(), "max code size", "Error should mention max code size")
})
}
10 changes: 8 additions & 2 deletions op-chain-ops/script/with.go
Original file line number Diff line number Diff line change
Expand Up @@ -44,9 +44,15 @@ func WithScript[B any](h *Host, name string, contract string) (b *B, cleanup fun
return nil, nil, fmt.Errorf("failed to make bindings: %w", err)
}

// Scripts can be very large
// Scripts can be very large - disable contract size constraints for script deployment
wasNoMaxCodeSize := h.noMaxCodeSize
h.EnforceMaxCodeSize(false)
defer h.EnforceMaxCodeSize(true)
defer func() {
// Only re-enable if it wasn't originally disabled
if !wasNoMaxCodeSize {
h.EnforceMaxCodeSize(true)
}
}()
// deploy the script contract
deployedAddr, err := h.Create(deployer, artifact.Bytecode.Object)
if err != nil {
Expand Down
1 change: 1 addition & 0 deletions op-deployer/pkg/deployer/apply.go
Original file line number Diff line number Diff line change
Expand Up @@ -316,6 +316,7 @@ func ApplyPipeline(
opts.Logger,
deployer,
bundle.L1,
script.WithNoMaxCodeSize(), // Allow unoptimized contracts from the forge lite profile in genesis deployments
)
if err != nil {
return fmt.Errorf("failed to create L1 script host: %w", err)
Expand Down