From 92ed64e171c6eb9c6a080c626640e8836f0653cc Mon Sep 17 00:00:00 2001 From: Matthew Slipper Date: Sun, 15 Sep 2024 00:15:26 -0600 Subject: [PATCH] Use the deterministic deployer when broadcasting with CREATE2 (#11915) --- op-chain-ops/script/deterministic.go | 9 +++++++++ op-chain-ops/script/prank.go | 7 +++++++ op-chain-ops/script/script.go | 14 ++++++++++++++ op-chain-ops/script/script_test.go | 10 ++++++---- 4 files changed, 36 insertions(+), 4 deletions(-) create mode 100644 op-chain-ops/script/deterministic.go diff --git a/op-chain-ops/script/deterministic.go b/op-chain-ops/script/deterministic.go new file mode 100644 index 000000000000..c7b02632a55e --- /dev/null +++ b/op-chain-ops/script/deterministic.go @@ -0,0 +1,9 @@ +package script + +import "github.com/ethereum/go-ethereum/common" + +var ( + // DeterministicDeployerAddress is the address of the deterministic deployer Forge uses + // to provide deterministic contract addresses. + DeterministicDeployerAddress = common.HexToAddress("0x4e59b44847b379578588920ca78fbf26c0b4956c") +) diff --git a/op-chain-ops/script/prank.go b/op-chain-ops/script/prank.go index eaf611e9e85d..b6a68f4c44b1 100644 --- a/op-chain-ops/script/prank.go +++ b/op-chain-ops/script/prank.go @@ -54,6 +54,13 @@ func (h *Host) handleCaller(caller vm.ContractRef) vm.ContractRef { if len(h.callStack) > 0 { parentCallFrame := h.callStack[len(h.callStack)-1] if parentCallFrame.Prank != nil && caller.Address() != VMAddr { // pranks do not apply to the cheatcode precompile + if parentCallFrame.Prank.Broadcast && parentCallFrame.LastOp == vm.CREATE2 && h.useCreate2Deployer { + return &prankRef{ + prank: DeterministicDeployerAddress, + ref: caller, + } + } + if parentCallFrame.Prank.Sender != nil { return &prankRef{ prank: *parentCallFrame.Prank.Sender, diff --git a/op-chain-ops/script/script.go b/op-chain-ops/script/script.go index 690ec6fdec65..5bbc0942a52b 100644 --- a/op-chain-ops/script/script.go +++ b/op-chain-ops/script/script.go @@ -105,6 +105,10 @@ type Host struct { // and prepare the ephemeral tx context again, // to make gas accounting of a broadcast sub-call more accurate. isolateBroadcasts bool + + // useCreate2Deployer uses the Create2Deployer for broadcasted + // create2 calls. + useCreate2Deployer bool } type HostOption func(h *Host) @@ -132,6 +136,16 @@ func WithIsolatedBroadcasts() HostOption { } } +// WithCreate2Deployer proxies each CREATE2 call through the CREATE2 deployer +// contract located at 0x4e59b44847b379578588920cA78FbF26c0B4956C. This is the Arachnid +// Create2Deployer contract Forge uses. See https://github.com/Arachnid/deterministic-deployment-proxy +// for the implementation. +func WithCreate2Deployer() HostOption { + return func(h *Host) { + h.useCreate2Deployer = 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. diff --git a/op-chain-ops/script/script_test.go b/op-chain-ops/script/script_test.go index 55dd70ff2eec..53415d1f0583 100644 --- a/op-chain-ops/script/script_test.go +++ b/op-chain-ops/script/script_test.go @@ -117,8 +117,8 @@ func TestScriptBroadcast(t *testing.T) { Nonce: 0, // first action of 0x123456 }, { - From: cafeAddr, - To: crypto.CreateAddress2(cafeAddr, salt, crypto.Keccak256(expectedInitCode)), + From: DeterministicDeployerAddress, + To: crypto.CreateAddress2(DeterministicDeployerAddress, salt, crypto.Keccak256(expectedInitCode)), Input: expectedInitCode, Value: (*hexutil.U256)(uint256.NewInt(0)), Type: BroadcastCreate2, @@ -141,7 +141,7 @@ func TestScriptBroadcast(t *testing.T) { hook := func(broadcast Broadcast) { broadcasts = append(broadcasts, broadcast) } - h := NewHost(logger, af, nil, DefaultContext, WithBroadcastHook(hook)) + h := NewHost(logger, af, nil, DefaultContext, WithBroadcastHook(hook), WithCreate2Deployer()) addr, err := h.LoadContract("ScriptExample.s.sol", "ScriptExample") require.NoError(t, err) @@ -164,5 +164,7 @@ func TestScriptBroadcast(t *testing.T) { require.EqualValues(t, 0, h.GetNonce(senderAddr)) require.EqualValues(t, 3, h.GetNonce(scriptAddr)) require.EqualValues(t, 2, h.GetNonce(coffeeAddr)) - require.EqualValues(t, 1, h.GetNonce(cafeAddr)) + // This is zero because the deterministic deployer is the + // address that actually deploys the contract using CREATE2. + require.EqualValues(t, 0, h.GetNonce(cafeAddr)) }