Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Use the deterministic deployer when broadcasting with CREATE2 #11915

Merged
merged 1 commit into from
Sep 15, 2024
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
9 changes: 9 additions & 0 deletions op-chain-ops/script/deterministic.go
Original file line number Diff line number Diff line change
@@ -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")
)
7 changes: 7 additions & 0 deletions op-chain-ops/script/prank.go
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand Down
14 changes: 14 additions & 0 deletions op-chain-ops/script/script.go
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand Down Expand Up @@ -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.
Expand Down
10 changes: 6 additions & 4 deletions op-chain-ops/script/script_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand All @@ -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)

Expand All @@ -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))
}