diff --git a/op-deployer/pkg/deployer/pipeline/init.go b/op-deployer/pkg/deployer/pipeline/init.go index 2b5d2396576ad..516edd991a9e1 100644 --- a/op-deployer/pkg/deployer/pipeline/init.go +++ b/op-deployer/pkg/deployer/pipeline/init.go @@ -38,25 +38,34 @@ func InitLiveStrategy(ctx context.Context, env *Env, intent *state.Intent, st *s return fmt.Errorf("unsupported L2 version: %s", intent.L2ContractsLocator.Tag) } - if isL1Tag && hasPredeployedOPCM { - superCfg, err := standard.SuperchainFor(intent.L1ChainID) + isStandardIntent := intent.ConfigType == state.IntentTypeStandard || + intent.ConfigType == state.IntentTypeStandardOverrides + if isL1Tag && hasPredeployedOPCM && isStandardIntent { + stdRoles, err := state.GetStandardSuperchainRoles(intent.L1ChainID) if err != nil { - return fmt.Errorf("error getting superchain config: %w", err) + return fmt.Errorf("error getting superchain roles: %w", err) } - proxyAdmin, err := standard.SuperchainProxyAdminAddrFor(intent.L1ChainID) - if err != nil { - return fmt.Errorf("error getting superchain proxy admin address: %w", err) - } - - st.SuperchainDeployment = &state.SuperchainDeployment{ - ProxyAdminAddress: proxyAdmin, - ProtocolVersionsProxyAddress: superCfg.ProtocolVersionsAddr, - SuperchainConfigProxyAddress: superCfg.SuperchainConfigAddr, - } - - st.ImplementationsDeployment = &state.ImplementationsDeployment{ - OpcmAddress: opcmAddress, + if *intent.SuperchainRoles == *stdRoles { + superCfg, err := standard.SuperchainFor(intent.L1ChainID) + if err != nil { + return fmt.Errorf("error getting superchain config: %w", err) + } + + proxyAdmin, err := standard.SuperchainProxyAdminAddrFor(intent.L1ChainID) + if err != nil { + return fmt.Errorf("error getting superchain proxy admin address: %w", err) + } + + st.SuperchainDeployment = &state.SuperchainDeployment{ + ProxyAdminAddress: proxyAdmin, + ProtocolVersionsProxyAddress: superCfg.ProtocolVersionsAddr, + SuperchainConfigProxyAddress: superCfg.SuperchainConfigAddr, + } + + st.ImplementationsDeployment = &state.ImplementationsDeployment{ + OpcmAddress: opcmAddress, + } } } diff --git a/op-deployer/pkg/deployer/pipeline/init_test.go b/op-deployer/pkg/deployer/pipeline/init_test.go new file mode 100644 index 0000000000000..121ca7b3d9d12 --- /dev/null +++ b/op-deployer/pkg/deployer/pipeline/init_test.go @@ -0,0 +1,171 @@ +package pipeline + +import ( + "context" + "log/slog" + "os" + "testing" + "time" + + "github.com/ethereum-optimism/optimism/op-deployer/pkg/deployer/artifacts" + "github.com/ethereum-optimism/optimism/op-deployer/pkg/deployer/standard" + "github.com/ethereum-optimism/optimism/op-deployer/pkg/deployer/state" + "github.com/ethereum-optimism/optimism/op-service/testlog" + "github.com/ethereum-optimism/optimism/op-service/testutils/devnet" + "github.com/ethereum/go-ethereum/common" + "github.com/ethereum/go-ethereum/ethclient" + "github.com/stretchr/testify/require" +) + +func TestInitLiveStrategy_OPCMReuseLogicSepolia(t *testing.T) { + rpcURL := os.Getenv("SEPOLIA_RPC_URL") + require.NotEmpty(t, rpcURL, "SEPOLIA_RPC_URL must be set") + + lgr := testlog.Logger(t, slog.LevelInfo) + retryProxy := devnet.NewRetryProxy(lgr, rpcURL) + require.NoError(t, retryProxy.Start()) + t.Cleanup(func() { + require.NoError(t, retryProxy.Stop()) + }) + + ctx, cancel := context.WithTimeout(context.Background(), 30*time.Second) + defer cancel() + + client, err := ethclient.Dial(retryProxy.Endpoint()) + require.NoError(t, err) + + l1ChainID := uint64(11155111) + t.Run("untagged L1 locator", func(t *testing.T) { + st := &state.State{ + Version: 1, + } + require.NoError(t, InitLiveStrategy( + ctx, + &Env{ + L1Client: client, + Logger: lgr, + }, + &state.Intent{ + L1ChainID: l1ChainID, + L1ContractsLocator: artifacts.MustNewLocatorFromURL("file:///not-a-path"), + L2ContractsLocator: artifacts.MustNewLocatorFromURL("file:///not-a-path"), + }, + st, + )) + + // Defining a file locator will always deploy a new superchain and OPCM + require.Nil(t, st.SuperchainDeployment) + require.Nil(t, st.ImplementationsDeployment) + }) + + t.Run("tagged L1 locator with standard intent types and standard roles", func(t *testing.T) { + runTest := func(configType state.IntentType) { + stdSuperchainRoles, err := state.GetStandardSuperchainRoles(l1ChainID) + require.NoError(t, err) + + intent := &state.Intent{ + ConfigType: configType, + L1ChainID: l1ChainID, + L1ContractsLocator: artifacts.DefaultL1ContractsLocator, + L2ContractsLocator: artifacts.DefaultL2ContractsLocator, + SuperchainRoles: stdSuperchainRoles, + } + st := &state.State{ + Version: 1, + } + require.NoError(t, InitLiveStrategy( + ctx, + &Env{ + L1Client: client, + Logger: lgr, + }, + intent, + st, + )) + + // Defining a file locator will always deploy a new superchain and OPCM + superCfg, err := standard.SuperchainFor(l1ChainID) + require.NoError(t, err) + proxyAdmin, err := standard.SuperchainProxyAdminAddrFor(l1ChainID) + require.NoError(t, err) + opcmAddr, err := standard.ManagerImplementationAddrFor(l1ChainID, intent.L1ContractsLocator.Tag) + require.NoError(t, err) + + expDeployment := &state.SuperchainDeployment{ + ProxyAdminAddress: proxyAdmin, + ProtocolVersionsProxyAddress: superCfg.ProtocolVersionsAddr, + SuperchainConfigProxyAddress: superCfg.SuperchainConfigAddr, + } + + // Tagged locator will reuse the existing superchain and OPCM + require.NotNil(t, st.SuperchainDeployment) + require.NotNil(t, st.ImplementationsDeployment) + require.Equal(t, *expDeployment, *st.SuperchainDeployment) + require.Equal(t, opcmAddr, st.ImplementationsDeployment.OpcmAddress) + } + + runTest(state.IntentTypeStandard) + runTest(state.IntentTypeStandardOverrides) + }) + + t.Run("tagged L1 locator with standard intent types and modified roles", func(t *testing.T) { + runTest := func(configType state.IntentType) { + intent := &state.Intent{ + ConfigType: configType, + L1ChainID: l1ChainID, + L1ContractsLocator: artifacts.DefaultL1ContractsLocator, + L2ContractsLocator: artifacts.DefaultL2ContractsLocator, + SuperchainRoles: &state.SuperchainRoles{ + Guardian: common.Address{0: 99}, + }, + } + st := &state.State{ + Version: 1, + } + require.NoError(t, InitLiveStrategy( + ctx, + &Env{ + L1Client: client, + Logger: lgr, + }, + intent, + st, + )) + + // Modified roles will cause a new superchain and OPCM to be deployed + require.Nil(t, st.SuperchainDeployment) + require.Nil(t, st.ImplementationsDeployment) + } + + runTest(state.IntentTypeStandard) + runTest(state.IntentTypeStandardOverrides) + }) + + t.Run("tagged locator with custom intent type", func(t *testing.T) { + intent := &state.Intent{ + ConfigType: state.IntentTypeCustom, + L1ChainID: l1ChainID, + L1ContractsLocator: artifacts.DefaultL1ContractsLocator, + L2ContractsLocator: artifacts.DefaultL2ContractsLocator, + SuperchainRoles: &state.SuperchainRoles{ + Guardian: common.Address{0: 99}, + }, + } + st := &state.State{ + Version: 1, + } + require.NoError(t, InitLiveStrategy( + ctx, + &Env{ + L1Client: client, + Logger: lgr, + }, + intent, + st, + )) + + // Custom intent types always deploy a new superchain and OPCM + require.Nil(t, st.SuperchainDeployment) + require.Nil(t, st.ImplementationsDeployment) + }) +} diff --git a/op-deployer/pkg/deployer/standard/standard.go b/op-deployer/pkg/deployer/standard/standard.go index 1f43b33fe1137..60cf1f89348c5 100644 --- a/op-deployer/pkg/deployer/standard/standard.go +++ b/op-deployer/pkg/deployer/standard/standard.go @@ -166,6 +166,17 @@ func L1ProxyAdminOwner(chainID uint64) (common.Address, error) { } } +func L2ProxyAdminOwner(chainID uint64) (common.Address, error) { + switch chainID { + case 1: + return common.Address(validation.StandardConfigRolesMainnet.L2ProxyAdminOwner), nil + case 11155111: + return common.Address(validation.StandardConfigRolesSepolia.L2ProxyAdminOwner), nil + default: + return common.Address{}, fmt.Errorf("unsupported chain ID: %d", chainID) + } +} + func ProtocolVersionsOwner(chainID uint64) (common.Address, error) { switch chainID { case 1: diff --git a/op-deployer/pkg/deployer/standard/standard_test.go b/op-deployer/pkg/deployer/standard/standard_test.go index a35d77e7f113f..e4cff671d82cf 100644 --- a/op-deployer/pkg/deployer/standard/standard_test.go +++ b/op-deployer/pkg/deployer/standard/standard_test.go @@ -6,6 +6,8 @@ import ( "strings" "testing" + "github.com/ethereum-optimism/superchain-registry/validation" + "github.com/ethereum/go-ethereum/common" "github.com/stretchr/testify/require" @@ -62,3 +64,24 @@ func TestStandardAddresses(t *testing.T) { }) } } + +func TestL2ProxyAdminOwner(t *testing.T) { + tests := []struct { + chainID uint64 + expAddr validation.Address + }{ + { + 1, + validation.StandardConfigRolesMainnet.L2ProxyAdminOwner, + }, + { + 11155111, + validation.StandardConfigRolesSepolia.L2ProxyAdminOwner, + }, + } + for _, test := range tests { + addr, err := L2ProxyAdminOwner(test.chainID) + require.NoError(t, err) + require.Equal(t, common.Address(test.expAddr), addr) + } +} diff --git a/op-deployer/pkg/deployer/state/intent.go b/op-deployer/pkg/deployer/state/intent.go index e142b59ec3917..edd4cbfd5ca82 100644 --- a/op-deployer/pkg/deployer/state/intent.go +++ b/op-deployer/pkg/deployer/state/intent.go @@ -121,7 +121,7 @@ func (c *Intent) validateStandardValues() error { return err } - standardSuperchainRoles, err := getStandardSuperchainRoles(c.L1ChainID) + standardSuperchainRoles, err := GetStandardSuperchainRoles(c.L1ChainID) if err != nil { return fmt.Errorf("error getting standard superchain roles: %w", err) } @@ -157,7 +157,7 @@ func (c *Intent) validateStandardValues() error { return nil } -func getStandardSuperchainRoles(l1ChainId uint64) (*SuperchainRoles, error) { +func GetStandardSuperchainRoles(l1ChainId uint64) (*SuperchainRoles, error) { proxyAdminOwner, err := standard.L1ProxyAdminOwner(l1ChainId) if err != nil { return nil, fmt.Errorf("error getting L1ProxyAdminOwner: %w", err) @@ -286,14 +286,24 @@ func NewIntentStandard(l1ChainId uint64, l2ChainIds []common.Hash) (Intent, erro L2ContractsLocator: artifacts.DefaultL2ContractsLocator, } - superchainRoles, err := getStandardSuperchainRoles(l1ChainId) + superchainRoles, err := GetStandardSuperchainRoles(l1ChainId) if err != nil { return Intent{}, fmt.Errorf("error getting standard superchain roles: %w", err) } intent.SuperchainRoles = superchainRoles - challenger, _ := standard.ChallengerAddressFor(l1ChainId) - l1ProxyAdminOwner, _ := standard.L1ProxyAdminOwner(l1ChainId) + challenger, err := standard.ChallengerAddressFor(l1ChainId) + if err != nil { + return Intent{}, fmt.Errorf("error getting challenger address: %w", err) + } + l1ProxyAdminOwner, err := standard.L1ProxyAdminOwner(l1ChainId) + if err != nil { + return Intent{}, fmt.Errorf("error getting L1ProxyAdminOwner: %w", err) + } + l2ProxyAdminOwner, err := standard.L2ProxyAdminOwner(l1ChainId) + if err != nil { + return Intent{}, fmt.Errorf("error getting L2ProxyAdminOwner: %w", err) + } for _, l2ChainID := range l2ChainIds { intent.Chains = append(intent.Chains, &ChainIntent{ @@ -304,6 +314,7 @@ func NewIntentStandard(l1ChainId uint64, l2ChainIds []common.Hash) (Intent, erro Roles: ChainRoles{ Challenger: challenger, L1ProxyAdminOwner: l1ProxyAdminOwner, + L2ProxyAdminOwner: l2ProxyAdminOwner, }, }) }