Commit 644dc2b5 authored by Matthew Slipper's avatar Matthew Slipper Committed by GitHub

Cleanups for production, add standard addresses (#12169)

* Cleanups for production, add standard addresses

This PR:

- Cleans up the intent file to be more suitable for production deployments
- Fixes various bugs encountered while preparing `op-deployer` for use against predeployed OPCM contracts
- Adds a new CLI command to bootstrap a new OPCM deployment against an existing set of implementation contracts

Note on Solidity changes:

- Since the code for custom gas tokens is in the monorepo but isn't included in an official contracts release yet, we had to add interfaces for the pre-CGT contracts to the Solidity codebase.
- The `DeployImplementations` script looks at the release identifier to determine whether or not it should use the pre- or post-CGT interfaces.

* goimports

* lints

* fix tests

* revert tx manger changes

* Update packages/contracts-bedrock/src/L1/interfaces/IL1StandardBridgeV160.sol
Co-authored-by: default avatarMaurelian <john@oplabs.co>

* Update packages/contracts-bedrock/src/L1/interfaces/IL1CrossDomainMessengerV160.sol
Co-authored-by: default avatarMaurelian <john@oplabs.co>

* use new opcm

* fix test

* semver

* semver

* bump semver

* update manager deployment

* natspec

* SEMVER

---------
Co-authored-by: default avatarMaurelian <john@oplabs.co>
parent a96b2282
......@@ -4,6 +4,8 @@ import (
"fmt"
"os"
"github.com/ethereum-optimism/optimism/op-chain-ops/deployer/bootstrap"
"github.com/ethereum-optimism/optimism/op-chain-ops/deployer/version"
opservice "github.com/ethereum-optimism/optimism/op-service"
......@@ -41,6 +43,11 @@ func main() {
Flags: cliapp.ProtectFlags(deployer.ApplyFlags),
Action: deployer.ApplyCLI(),
},
{
Name: "bootstrap",
Usage: "bootstraps global contract instances",
Subcommands: bootstrap.Commands,
},
{
Name: "inspect",
Usage: "inspects the state of a deployment",
......
package bootstrap
import (
"context"
"crypto/ecdsa"
"crypto/rand"
"fmt"
"math/big"
"strings"
"github.com/ethereum-optimism/optimism/op-chain-ops/deployer"
"github.com/ethereum-optimism/optimism/op-chain-ops/deployer/opcm"
"github.com/ethereum-optimism/optimism/op-chain-ops/deployer/pipeline"
"github.com/ethereum-optimism/optimism/op-chain-ops/deployer/state"
"github.com/ethereum-optimism/optimism/op-chain-ops/script"
opcrypto "github.com/ethereum-optimism/optimism/op-service/crypto"
"github.com/ethereum-optimism/optimism/op-service/ctxinterrupt"
"github.com/ethereum-optimism/optimism/op-service/ioutil"
"github.com/ethereum-optimism/optimism/op-service/jsonutil"
oplog "github.com/ethereum-optimism/optimism/op-service/log"
"github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/core/types"
"github.com/ethereum/go-ethereum/crypto"
"github.com/ethereum/go-ethereum/ethclient"
"github.com/ethereum/go-ethereum/log"
"github.com/urfave/cli/v2"
)
type OPCMConfig struct {
L1RPCUrl string
PrivateKey string
Logger log.Logger
ArtifactsURL *state.ArtifactsURL
ContractsRelease string
privateKeyECDSA *ecdsa.PrivateKey
}
func (c *OPCMConfig) Check() error {
if c.L1RPCUrl == "" {
return fmt.Errorf("l1RPCUrl must be specified")
}
if c.PrivateKey == "" {
return fmt.Errorf("private key must be specified")
}
privECDSA, err := crypto.HexToECDSA(strings.TrimPrefix(c.PrivateKey, "0x"))
if err != nil {
return fmt.Errorf("failed to parse private key: %w", err)
}
c.privateKeyECDSA = privECDSA
if c.Logger == nil {
return fmt.Errorf("logger must be specified")
}
if c.ArtifactsURL == nil {
return fmt.Errorf("artifacts URL must be specified")
}
if c.ContractsRelease == "" {
return fmt.Errorf("contracts release must be specified")
}
return nil
}
func OPCMCLI(cliCtx *cli.Context) error {
logCfg := oplog.ReadCLIConfig(cliCtx)
l := oplog.NewLogger(oplog.AppOut(cliCtx), logCfg)
oplog.SetGlobalLogHandler(l.Handler())
l1RPCUrl := cliCtx.String(deployer.L1RPCURLFlagName)
privateKey := cliCtx.String(deployer.PrivateKeyFlagName)
artifactsURLStr := cliCtx.String(ArtifactsURLFlagName)
artifactsURL := new(state.ArtifactsURL)
if err := artifactsURL.UnmarshalText([]byte(artifactsURLStr)); err != nil {
return fmt.Errorf("failed to parse artifacts URL: %w", err)
}
contractsRelease := cliCtx.String(ContractsReleaseFlagName)
ctx := ctxinterrupt.WithCancelOnInterrupt(cliCtx.Context)
return OPCM(ctx, OPCMConfig{
L1RPCUrl: l1RPCUrl,
PrivateKey: privateKey,
Logger: l,
ArtifactsURL: artifactsURL,
ContractsRelease: contractsRelease,
})
}
func OPCM(ctx context.Context, cfg OPCMConfig) error {
if err := cfg.Check(); err != nil {
return fmt.Errorf("invalid config for OPCM: %w", err)
}
lgr := cfg.Logger
progressor := func(curr, total int64) {
lgr.Info("artifacts download progress", "current", curr, "total", total)
}
artifactsFS, cleanup, err := pipeline.DownloadArtifacts(ctx, cfg.ArtifactsURL, progressor)
if err != nil {
return fmt.Errorf("failed to download artifacts: %w", err)
}
defer func() {
if err := cleanup(); err != nil {
lgr.Warn("failed to clean up artifacts", "err", err)
}
}()
l1Client, err := ethclient.Dial(cfg.L1RPCUrl)
if err != nil {
return fmt.Errorf("failed to connect to L1 RPC: %w", err)
}
chainID, err := l1Client.ChainID(ctx)
if err != nil {
return fmt.Errorf("failed to get chain ID: %w", err)
}
chainIDU64 := chainID.Uint64()
superCfg, err := opcm.SuperchainFor(chainIDU64)
if err != nil {
return fmt.Errorf("error getting superchain config: %w", err)
}
standardVersionsTOML, err := opcm.StandardVersionsFor(chainIDU64)
if err != nil {
return fmt.Errorf("error getting standard versions TOML: %w", err)
}
opcmProxyOwnerAddr, err := opcm.ManagerOwnerAddrFor(chainIDU64)
if err != nil {
return fmt.Errorf("error getting superchain proxy admin: %w", err)
}
signer := opcrypto.SignerFnFromBind(opcrypto.PrivateKeySignerFn(cfg.privateKeyECDSA, chainID))
chainDeployer := crypto.PubkeyToAddress(cfg.privateKeyECDSA.PublicKey)
lgr.Info("deploying OPCM", "release", cfg.ContractsRelease)
var dio opcm.DeployImplementationsOutput
err = pipeline.CallScriptBroadcast(
ctx,
pipeline.CallScriptBroadcastOpts{
L1ChainID: chainID,
Logger: lgr,
ArtifactsFS: artifactsFS,
Deployer: chainDeployer,
Signer: signer,
Client: l1Client,
Broadcaster: pipeline.KeyedBroadcaster,
Handler: func(host *script.Host) error {
// We need to etch the Superchain addresses so that they have nonzero code
// and the checks in the OPCM constructor pass.
superchainConfigAddr := common.Address(*superCfg.Config.SuperchainConfigAddr)
protocolVersionsAddr := common.Address(*superCfg.Config.ProtocolVersionsAddr)
addresses := []common.Address{
superchainConfigAddr,
protocolVersionsAddr,
}
for _, addr := range addresses {
host.ImportAccount(addr, types.Account{
Code: []byte{0x00},
})
}
var salt common.Hash
_, err = rand.Read(salt[:])
if err != nil {
return fmt.Errorf("failed to generate CREATE2 salt: %w", err)
}
dio, err = opcm.DeployImplementations(
host,
opcm.DeployImplementationsInput{
Salt: salt,
WithdrawalDelaySeconds: big.NewInt(604800),
MinProposalSizeBytes: big.NewInt(126000),
ChallengePeriodSeconds: big.NewInt(86400),
ProofMaturityDelaySeconds: big.NewInt(604800),
DisputeGameFinalityDelaySeconds: big.NewInt(302400),
Release: cfg.ContractsRelease,
SuperchainConfigProxy: superchainConfigAddr,
ProtocolVersionsProxy: protocolVersionsAddr,
OpcmProxyOwner: opcmProxyOwnerAddr,
StandardVersionsToml: standardVersionsTOML,
UseInterop: false,
},
)
return err
},
},
)
if err != nil {
return fmt.Errorf("error deploying implementations: %w", err)
}
lgr.Info("deployed implementations")
if err := jsonutil.WriteJSON(dio, ioutil.ToStdOut()); err != nil {
return fmt.Errorf("failed to write output: %w", err)
}
return nil
}
package bootstrap
import (
"github.com/ethereum-optimism/optimism/op-chain-ops/deployer"
"github.com/ethereum-optimism/optimism/op-service/cliapp"
"github.com/urfave/cli/v2"
)
const (
ArtifactsURLFlagName = "artifacts-url"
ContractsReleaseFlagName = "contracts-release"
)
var (
ArtifactsURLFlag = &cli.StringFlag{
Name: ArtifactsURLFlagName,
Usage: "URL to the artifacts directory.",
EnvVars: deployer.PrefixEnvVar("ARTIFACTS_URL"),
}
ContractsReleaseFlag = &cli.StringFlag{
Name: ContractsReleaseFlagName,
Usage: "Release of the contracts to deploy.",
EnvVars: deployer.PrefixEnvVar("CONTRACTS_RELEASE"),
}
)
var OPCMFlags = []cli.Flag{
deployer.L1RPCURLFlag,
deployer.PrivateKeyFlag,
ArtifactsURLFlag,
ContractsReleaseFlag,
}
var Commands = []*cli.Command{
{
Name: "opcm",
Usage: "Bootstrap an instance of OPCM.",
Flags: cliapp.ProtectFlags(OPCMFlags),
Action: OPCMCLI,
},
}
......@@ -66,7 +66,7 @@ func NewKeyedBroadcaster(cfg KeyedBroadcasterOpts) (*KeyedBroadcaster, error) {
mgrCfg.FeeLimitMultiplier.Store(5)
mgrCfg.FeeLimitThreshold.Store(big.NewInt(100))
mgrCfg.MinTipCap.Store(minTipCap)
mgrCfg.MinTipCap.Store(minBaseFee)
mgrCfg.MinBaseFee.Store(minBaseFee)
txmLogger := log.NewLogger(log.DiscardHandler())
if cfg.TXManagerLogger != nil {
......
......@@ -30,28 +30,27 @@ var (
L1ChainIDFlag = &cli.Uint64Flag{
Name: L1ChainIDFlagName,
Usage: "Chain ID of the L1 chain.",
EnvVars: prefixEnvVar("L1_CHAIN_ID"),
EnvVars: PrefixEnvVar("L1_CHAIN_ID"),
Value: 900,
}
L2ChainIDsFlag = &cli.StringFlag{
Name: L2ChainIDsFlagName,
Usage: "Comma-separated list of L2 chain IDs to deploy.",
EnvVars: prefixEnvVar("L2_CHAIN_IDS"),
EnvVars: PrefixEnvVar("L2_CHAIN_IDS"),
}
WorkdirFlag = &cli.StringFlag{
Name: WorkdirFlagName,
Usage: "Directory storing intent and stage. Defaults to the current directory.",
EnvVars: prefixEnvVar("WORKDIR"),
EnvVars: PrefixEnvVar("WORKDIR"),
Value: cwd(),
Aliases: []string{
OutdirFlagName,
},
}
PrivateKeyFlag = &cli.StringFlag{
Name: PrivateKeyFlagName,
Usage: "Private key of the deployer account.",
EnvVars: prefixEnvVar("PRIVATE_KEY"),
EnvVars: PrefixEnvVar("PRIVATE_KEY"),
}
)
......@@ -69,7 +68,7 @@ var ApplyFlags = []cli.Flag{
PrivateKeyFlag,
}
func prefixEnvVar(name string) []string {
func PrefixEnvVar(name string) []string {
return op_service.PrefixEnvVar(EnvVarPrefix, name)
}
......
......@@ -41,8 +41,8 @@ func InitCLI() func(ctx *cli.Context) error {
outdir := ctx.String(OutdirFlagName)
l2ChainIDsRaw := ctx.String(L2ChainIDsFlagName)
l2ChainIDsStr := strings.Split(l2ChainIDsRaw, ",")
l2ChainIDs := make([]common.Hash, 0, len(l2ChainIDsStr))
l2ChainIDsStr := strings.Split(strings.TrimSpace(l2ChainIDsRaw), ",")
l2ChainIDs := make([]common.Hash, len(l2ChainIDsStr))
for _, idStr := range l2ChainIDsStr {
id, err := op_service.Parse256BitChainID(idStr)
if err != nil {
......@@ -66,7 +66,6 @@ func Init(cfg InitConfig) error {
intent := &state.Intent{
L1ChainID: cfg.L1ChainID,
UseFaultProofs: true,
FundDevAccounts: true,
ContractsRelease: "dev",
}
......
......@@ -198,7 +198,6 @@ func makeIntent(
ProtocolVersionsOwner: addrFor(devkeys.SuperchainDeployerKey.Key(l1ChainID)),
Guardian: addrFor(devkeys.SuperchainConfigGuardianKey.Key(l1ChainID)),
},
UseFaultProofs: true,
FundDevAccounts: true,
ContractArtifactsURL: (*state.ArtifactsURL)(artifactsURL),
ContractsRelease: "dev",
......@@ -239,7 +238,6 @@ func validateOPChainDeployment(t *testing.T, ctx context.Context, l1Client *ethc
{"OptimismPortalProxyAddress", chainState.OptimismPortalProxyAddress},
{"DisputeGameFactoryProxyAddress", chainState.DisputeGameFactoryProxyAddress},
{"AnchorStateRegistryProxyAddress", chainState.AnchorStateRegistryProxyAddress},
{"AnchorStateRegistryImplAddress", chainState.AnchorStateRegistryImplAddress},
{"FaultDisputeGameAddress", chainState.FaultDisputeGameAddress},
{"PermissionedDisputeGameAddress", chainState.PermissionedDisputeGameAddress},
{"DelayedWETHPermissionedGameProxyAddress", chainState.DelayedWETHPermissionedGameProxyAddress},
......
......@@ -22,7 +22,7 @@ type DeployImplementationsInput struct {
ProtocolVersionsProxy common.Address
UseInterop bool // if true, deploy Interop implementations
SuperchainProxyAdmin common.Address
OpcmProxyOwner common.Address
StandardVersionsToml string // contents of 'standard-versions-mainnet.toml' or 'standard-versions-sepolia.toml' file
}
......
......@@ -123,6 +123,7 @@ type opcmDeployInput struct {
BlobBasefeeScalar uint32
L2ChainId *big.Int
StartingAnchorRoots []byte
SaltMixer string
}
// decodeOutputABIJSON defines an ABI for a fake method called "decodeOutput" that returns the
......@@ -241,6 +242,7 @@ func DeployOPChainRaw(
BlobBasefeeScalar: input.BlobBaseFeeScalar,
L2ChainId: input.L2ChainId,
StartingAnchorRoots: input.StartingAnchorRoots(),
SaltMixer: input.SaltMixer,
})
if err != nil {
return out, fmt.Errorf("failed to pack deploy input: %w", err)
......
package opcm
import "embed"
import (
"embed"
"fmt"
"github.com/ethereum-optimism/superchain-registry/superchain"
"github.com/ethereum/go-ethereum/common"
)
//go:embed standard-versions-mainnet.toml
var StandardVersionsMainnetData string
......@@ -9,3 +15,48 @@ var StandardVersionsMainnetData string
var StandardVersionsSepoliaData string
var _ embed.FS
func StandardVersionsFor(chainID uint64) (string, error) {
switch chainID {
case 1:
return StandardVersionsMainnetData, nil
case 11155111:
return StandardVersionsSepoliaData, nil
default:
return "", fmt.Errorf("unsupported chain ID: %d", chainID)
}
}
func SuperchainFor(chainID uint64) (*superchain.Superchain, error) {
switch chainID {
case 1:
return superchain.Superchains["mainnet"], nil
case 11155111:
return superchain.Superchains["sepolia"], nil
default:
return nil, fmt.Errorf("unsupported chain ID: %d", chainID)
}
}
func ManagerImplementationAddrFor(chainID uint64) (common.Address, error) {
switch chainID {
case 11155111:
// Generated using the bootstrap command on 09/26/2024.
return common.HexToAddress("0x0dc727671d5c08e4e41e8909983ebfa6f57aa0bf"), nil
default:
return common.Address{}, fmt.Errorf("unsupported chain ID: %d", chainID)
}
}
func ManagerOwnerAddrFor(chainID uint64) (common.Address, error) {
switch chainID {
case 1:
// Set to superchain proxy admin
return common.HexToAddress("0x543bA4AADBAb8f9025686Bd03993043599c6fB04"), nil
case 11155111:
// Set to development multisig
return common.HexToAddress("0xDEe57160aAfCF04c34C887B5962D0a69676d3C8B"), nil
default:
return common.Address{}, fmt.Errorf("unsupported chain ID: %d", chainID)
}
}
......@@ -4,6 +4,7 @@ import (
"context"
"fmt"
"math/big"
"strings"
"github.com/ethereum-optimism/optimism/op-chain-ops/deployer/opcm"
"github.com/ethereum-optimism/optimism/op-chain-ops/deployer/state"
......@@ -21,9 +22,17 @@ func DeployImplementations(ctx context.Context, env *Env, artifactsFS foundry.St
lgr.Info("deploying implementations")
var standardVersionsTOML string
var err error
if strings.HasPrefix(intent.ContractsRelease, "op-contracts") {
standardVersionsTOML, err = opcm.StandardVersionsFor(intent.L1ChainID)
if err != nil {
return fmt.Errorf("error getting standard versions TOML: %w", err)
}
}
var dump *foundry.ForgeAllocs
var dio opcm.DeployImplementationsOutput
var err error
err = CallScriptBroadcast(
ctx,
CallScriptBroadcastOpts{
......@@ -35,8 +44,8 @@ func DeployImplementations(ctx context.Context, env *Env, artifactsFS foundry.St
Client: env.L1Client,
Broadcaster: KeyedBroadcaster,
Handler: func(host *script.Host) error {
host.SetEnvVar("IMPL_SALT", st.Create2Salt.Hex()[2:])
host.ImportState(st.SuperchainDeployment.StateDump)
dio, err = opcm.DeployImplementations(
host,
opcm.DeployImplementationsInput{
......@@ -49,8 +58,8 @@ func DeployImplementations(ctx context.Context, env *Env, artifactsFS foundry.St
Release: intent.ContractsRelease,
SuperchainConfigProxy: st.SuperchainDeployment.SuperchainConfigProxyAddress,
ProtocolVersionsProxy: st.SuperchainDeployment.ProtocolVersionsProxyAddress,
SuperchainProxyAdmin: st.SuperchainDeployment.ProxyAdminAddress,
StandardVersionsToml: opcm.StandardVersionsMainnetData,
OpcmProxyOwner: st.SuperchainDeployment.ProxyAdminAddress,
StandardVersionsToml: standardVersionsTOML,
UseInterop: false,
},
)
......
......@@ -4,6 +4,7 @@ import (
"context"
"crypto/rand"
"fmt"
"strings"
"github.com/ethereum-optimism/optimism/op-chain-ops/deployer/opcm"
"github.com/ethereum-optimism/optimism/op-chain-ops/foundry"
......@@ -19,7 +20,7 @@ func IsSupportedStateVersion(version int) bool {
return version == 1
}
func Init(ctx context.Context, env *Env, artifactsFS foundry.StatDirFs, intent *state.Intent, st *state.State) error {
func Init(ctx context.Context, env *Env, _ foundry.StatDirFs, intent *state.Intent, st *state.State) error {
lgr := env.Logger.New("stage", "init")
lgr.Info("initializing pipeline")
......@@ -35,37 +36,31 @@ func Init(ctx context.Context, env *Env, artifactsFS foundry.StatDirFs, intent *
}
}
if intent.OPCMAddress != (common.Address{}) {
env.Logger.Info("using provided OPCM address, populating state", "address", intent.OPCMAddress.Hex())
if intent.ContractsRelease == "dev" {
env.Logger.Warn("using dev release with existing OPCM, this field will be ignored")
}
opcmContract := opcm.NewContract(intent.OPCMAddress, env.L1Client)
protocolVersions, err := opcmContract.ProtocolVersions(ctx)
if strings.HasPrefix(intent.ContractsRelease, "op-contracts") {
superCfg, err := opcm.SuperchainFor(intent.L1ChainID)
if err != nil {
return fmt.Errorf("error getting protocol versions address: %w", err)
return fmt.Errorf("error getting superchain config: %w", err)
}
superchainConfig, err := opcmContract.SuperchainConfig(ctx)
proxyAdmin, err := opcm.ManagerOwnerAddrFor(intent.L1ChainID)
if err != nil {
return fmt.Errorf("error getting superchain config address: %w", err)
return fmt.Errorf("error getting superchain proxy admin address: %w", err)
}
env.Logger.Debug(
"populating protocol versions and superchain config addresses",
"protocolVersions", protocolVersions.Hex(),
"superchainConfig", superchainConfig.Hex(),
)
// The below fields are the only ones required to perform an OP Chain
// deployment via an existing OPCM contract. All the others are used
// for deploying the OPCM itself, which isn't necessary in this case.
// Have to do this weird pointer thing below because the Superchain Registry defines its
// own Address type.
st.SuperchainDeployment = &state.SuperchainDeployment{
ProtocolVersionsProxyAddress: protocolVersions,
SuperchainConfigProxyAddress: superchainConfig,
ProxyAdminAddress: proxyAdmin,
ProtocolVersionsProxyAddress: common.Address(*superCfg.Config.ProtocolVersionsAddr),
SuperchainConfigProxyAddress: common.Address(*superCfg.Config.SuperchainConfigAddr),
}
opcmProxy, err := opcm.ManagerImplementationAddrFor(intent.L1ChainID)
if err != nil {
return fmt.Errorf("error getting OPCM proxy address: %w", err)
}
st.ImplementationsDeployment = &state.ImplementationsDeployment{
OpcmProxyAddress: intent.OPCMAddress,
OpcmProxyAddress: opcmProxy,
}
}
......@@ -81,14 +76,6 @@ func Init(ctx context.Context, env *Env, artifactsFS foundry.StatDirFs, intent *
return immutableErr("L1ChainID", st.AppliedIntent.L1ChainID, intent.L1ChainID)
}
if st.AppliedIntent.UseFaultProofs != intent.UseFaultProofs {
return immutableErr("useFaultProofs", st.AppliedIntent.UseFaultProofs, intent.UseFaultProofs)
}
if st.AppliedIntent.UseAltDA != intent.UseAltDA {
return immutableErr("useAltDA", st.AppliedIntent.UseAltDA, intent.UseAltDA)
}
if st.AppliedIntent.FundDevAccounts != intent.FundDevAccounts {
return immutableErr("fundDevAccounts", st.AppliedIntent.FundDevAccounts, intent.FundDevAccounts)
}
......
......@@ -10,7 +10,6 @@ import (
"github.com/ethereum-optimism/optimism/op-chain-ops/deployer/opcm"
"github.com/ethereum-optimism/optimism/op-chain-ops/deployer/state"
"github.com/ethereum-optimism/optimism/op-chain-ops/foundry"
"github.com/ethereum-optimism/optimism/op-chain-ops/script"
"github.com/ethereum/go-ethereum/common"
)
......@@ -44,55 +43,27 @@ func DeployOPChain(ctx context.Context, env *Env, artifactsFS foundry.StatDirFs,
}
var dco opcm.DeployOPChainOutput
if intent.OPCMAddress == (common.Address{}) {
err = CallScriptBroadcast(
ctx,
CallScriptBroadcastOpts{
L1ChainID: big.NewInt(int64(intent.L1ChainID)),
Logger: lgr,
ArtifactsFS: artifactsFS,
Deployer: env.Deployer,
Signer: env.Signer,
Client: env.L1Client,
Broadcaster: KeyedBroadcaster,
Handler: func(host *script.Host) error {
host.ImportState(st.ImplementationsDeployment.StateDump)
dco, err = opcm.DeployOPChain(
host,
input,
)
return err
},
},
)
if err != nil {
return fmt.Errorf("error deploying OP chain: %w", err)
}
} else {
lgr.Info("deploying using existing OPCM", "address", intent.OPCMAddress.Hex())
bcaster, err := broadcaster.NewKeyedBroadcaster(broadcaster.KeyedBroadcasterOpts{
Logger: lgr,
ChainID: big.NewInt(int64(intent.L1ChainID)),
Client: env.L1Client,
Signer: env.Signer,
From: env.Deployer,
})
if err != nil {
return fmt.Errorf("failed to create broadcaster: %w", err)
}
dco, err = opcm.DeployOPChainRaw(
ctx,
env.L1Client,
bcaster,
env.Deployer,
artifactsFS,
input,
)
if err != nil {
return fmt.Errorf("error deploying OP chain: %w", err)
}
lgr.Info("deploying using existing OPCM", "address", st.ImplementationsDeployment.OpcmProxyAddress.Hex())
bcaster, err := broadcaster.NewKeyedBroadcaster(broadcaster.KeyedBroadcasterOpts{
Logger: lgr,
ChainID: big.NewInt(int64(intent.L1ChainID)),
Client: env.L1Client,
Signer: env.Signer,
From: env.Deployer,
})
if err != nil {
return fmt.Errorf("failed to create broadcaster: %w", err)
}
dco, err = opcm.DeployOPChainRaw(
ctx,
env.L1Client,
bcaster,
env.Deployer,
artifactsFS,
input,
)
if err != nil {
return fmt.Errorf("error deploying OP chain: %w", err)
}
st.Chains = append(st.Chains, &state.ChainState{
......
......@@ -65,6 +65,13 @@ func DefaultDeployConfig() genesis.DeployConfig {
SystemConfigStartBlock: 0,
},
},
FaultProofDeployConfig: genesis.FaultProofDeployConfig{
FaultGameWithdrawalDelay: 604800,
PreimageOracleMinProposalSize: 126000,
PreimageOracleChallengePeriod: 86400,
ProofMaturityDelaySeconds: 604800,
DisputeGameFinalityDelaySeconds: 302400,
},
}
}
......
......@@ -17,17 +17,11 @@ type Intent struct {
SuperchainRoles SuperchainRoles `json:"superchainRoles" toml:"superchainRoles"`
UseFaultProofs bool `json:"useFaultProofs" toml:"useFaultProofs"`
UseAltDA bool `json:"useAltDA" toml:"useAltDA"`
FundDevAccounts bool `json:"fundDevAccounts" toml:"fundDevAccounts"`
ContractArtifactsURL *ArtifactsURL `json:"contractArtifactsURL" toml:"contractArtifactsURL"`
ContractsRelease string `json:"contractsVersion" toml:"contractsVersion"`
OPCMAddress common.Address `json:"opcmAddress" toml:"opcmAddress"`
ContractsRelease string `json:"contractsRelease" toml:"contractsRelease"`
Chains []*ChainIntent `json:"chains" toml:"chains"`
......@@ -43,10 +37,28 @@ func (c *Intent) Check() error {
return fmt.Errorf("l1ChainID must be set")
}
if c.UseFaultProofs && c.UseAltDA {
return fmt.Errorf("cannot use both fault proofs and alt-DA")
if c.ContractsRelease == "dev" {
return c.checkDev()
}
return c.checkProd()
}
func (c *Intent) Chain(id common.Hash) (*ChainIntent, error) {
for i := range c.Chains {
if c.Chains[i].ID == id {
return c.Chains[i], nil
}
}
return nil, fmt.Errorf("chain %d not found", id)
}
func (c *Intent) WriteToFile(path string) error {
return jsonutil.WriteTOML(c, ioutil.ToAtomicFile(path, 0o755))
}
func (c *Intent) checkDev() error {
if c.SuperchainRoles.ProxyAdminOwner == emptyAddress {
return fmt.Errorf("proxyAdminOwner must be set")
}
......@@ -60,28 +72,18 @@ func (c *Intent) Check() error {
}
if c.ContractArtifactsURL == nil {
return fmt.Errorf("contractArtifactsURL must be set")
}
if c.ContractsRelease != "dev" && !strings.HasPrefix(c.ContractsRelease, "op-contracts/") {
return fmt.Errorf("contractsVersion must be either the literal \"dev\" or start with \"op-contracts/\"")
return fmt.Errorf("contractArtifactsURL must be set in dev mode")
}
return nil
}
func (c *Intent) Chain(id common.Hash) (*ChainIntent, error) {
for i := range c.Chains {
if c.Chains[i].ID == id {
return c.Chains[i], nil
}
func (c *Intent) checkProd() error {
if !strings.HasPrefix(c.ContractsRelease, "op-contracts/") {
return fmt.Errorf("contractsVersion must be either the literal \"dev\" or start with \"op-contracts/\"")
}
return nil, fmt.Errorf("chain %d not found", id)
}
func (c *Intent) WriteToFile(path string) error {
return jsonutil.WriteTOML(c, ioutil.ToAtomicFile(path, 0o755))
return nil
}
type SuperchainRoles struct {
......
......@@ -61,7 +61,7 @@ type SuperchainDeployment struct {
SuperchainConfigImplAddress common.Address `json:"superchainConfigImplAddress"`
ProtocolVersionsProxyAddress common.Address `json:"protocolVersionsProxyAddress"`
ProtocolVersionsImplAddress common.Address `json:"protocolVersionsImplAddress"`
StateDump *foundry.ForgeAllocs `json:"stateDump"`
StateDump *foundry.ForgeAllocs `json:"-"`
}
type ImplementationsDeployment struct {
......@@ -76,7 +76,7 @@ type ImplementationsDeployment struct {
L1StandardBridgeImplAddress common.Address `json:"l1StandardBridgeImplAddress"`
OptimismMintableERC20FactoryImplAddress common.Address `json:"optimismMintableERC20FactoryImplAddress"`
DisputeGameFactoryImplAddress common.Address `json:"disputeGameFactoryImplAddress"`
StateDump *foundry.ForgeAllocs `json:"stateDump"`
StateDump *foundry.ForgeAllocs `json:"-"`
}
type ChainState struct {
......
......@@ -169,7 +169,7 @@ func DeploySuperchainToL1(l1Host *script.Host, superCfg *SuperchainConfig) (*Sup
Release: superCfg.Implementations.Release,
SuperchainConfigProxy: superDeployment.SuperchainConfigProxy,
ProtocolVersionsProxy: superDeployment.ProtocolVersionsProxy,
SuperchainProxyAdmin: superDeployment.SuperchainProxyAdmin,
OpcmProxyOwner: superDeployment.SuperchainProxyAdmin,
UseInterop: superCfg.Implementations.UseInterop,
StandardVersionsToml: opcm.StandardVersionsMainnetData,
})
......
......@@ -391,12 +391,22 @@ func (h *Host) GetNonce(addr common.Address) uint64 {
// when importing.
func (h *Host) ImportState(allocs *foundry.ForgeAllocs) {
for addr, alloc := range allocs.Accounts {
h.state.SetBalance(addr, uint256.MustFromBig(alloc.Balance), tracing.BalanceChangeUnspecified)
h.state.SetNonce(addr, alloc.Nonce)
h.state.SetCode(addr, alloc.Code)
for key, value := range alloc.Storage {
h.state.SetState(addr, key, value)
}
h.ImportAccount(addr, alloc)
}
}
func (h *Host) ImportAccount(addr common.Address, account types.Account) {
var balance *uint256.Int
if account.Balance == nil {
balance = uint256.NewInt(0)
} else {
balance = uint256.MustFromBig(account.Balance)
}
h.state.SetBalance(addr, balance, tracing.BalanceChangeUnspecified)
h.state.SetNonce(addr, account.Nonce)
h.state.SetCode(addr, account.Code)
for key, value := range account.Storage {
h.state.SetState(addr, key, value)
}
}
......
......@@ -819,6 +819,12 @@ func (m *SimpleTxManager) increaseGasPrice(ctx context.Context, tx *types.Transa
"gasFeeCap", bumpedFee, "gasTipCap", bumpedTip)
}
if tx.Gas() > gas {
// Don't bump the gas limit down if the passed-in gas limit is higher than
// what was originally specified.
gas = tx.Gas()
}
var newTx *types.Transaction
if tx.Type() == types.BlobTxType {
// Blob transactions have an additional blob gas price we must specify, so we must make sure it is
......
......@@ -8,6 +8,8 @@ import { LibString } from "@solady/utils/LibString.sol";
import { IResourceMetering } from "src/L1/interfaces/IResourceMetering.sol";
import { ISuperchainConfig } from "src/L1/interfaces/ISuperchainConfig.sol";
import { ISystemConfigV160 } from "src/L1/interfaces/ISystemConfigV160.sol";
import { IL1CrossDomainMessengerV160 } from "src/L1/interfaces/IL1CrossDomainMessengerV160.sol";
import { IL1StandardBridgeV160 } from "src/L1/interfaces/IL1StandardBridgeV160.sol";
import { Constants } from "src/libraries/Constants.sol";
import { Predeploys } from "src/libraries/Predeploys.sol";
......@@ -65,6 +67,8 @@ contract DeployImplementationsInput is BaseDeployIO {
string internal _standardVersionsToml;
address internal _opcmProxyOwner;
function set(bytes4 _sel, uint256 _value) public {
require(_value != 0, "DeployImplementationsInput: cannot set zero value");
......@@ -95,6 +99,7 @@ contract DeployImplementationsInput is BaseDeployIO {
require(_addr != address(0), "DeployImplementationsInput: cannot set zero address");
if (_sel == this.superchainConfigProxy.selector) _superchainConfigProxy = SuperchainConfig(_addr);
else if (_sel == this.protocolVersionsProxy.selector) _protocolVersionsProxy = ProtocolVersions(_addr);
else if (_sel == this.opcmProxyOwner.selector) _opcmProxyOwner = _addr;
else revert("DeployImplementationsInput: unknown selector");
}
......@@ -156,13 +161,9 @@ contract DeployImplementationsInput is BaseDeployIO {
return _protocolVersionsProxy;
}
function superchainProxyAdmin() public returns (ProxyAdmin) {
SuperchainConfig proxy = this.superchainConfigProxy();
// Can infer the superchainProxyAdmin from the superchainConfigProxy.
vm.prank(address(0));
ProxyAdmin proxyAdmin = ProxyAdmin(Proxy(payable(address(proxy))).admin());
require(address(proxyAdmin) != address(0), "DeployImplementationsInput: not set");
return proxyAdmin;
function opcmProxyOwner() public view returns (address) {
require(address(_opcmProxyOwner) != address(0), "DeployImplementationsInput: not set");
return _opcmProxyOwner;
}
}
......@@ -308,7 +309,7 @@ contract DeployImplementationsOutput is BaseDeployIO {
Proxy proxy = Proxy(payable(address(opcmProxy())));
vm.prank(address(0));
address admin = proxy.admin();
require(admin == address(_dii.superchainProxyAdmin()), "OPCMP-10");
require(admin == address(_dii.opcmProxyOwner()), "OPCMP-10");
// Then we check the proxy as OPCM.
DeployUtils.assertInitialized({ _contractAddress: address(opcmProxy()), _slot: 0, _offset: 0 });
......@@ -504,6 +505,42 @@ contract DeployImplementations is Script {
});
}
function l1CrossDomainMessengerConfigSetter(
DeployImplementationsInput _dii,
DeployImplementationsOutput _dio
)
internal
view
virtual
returns (OPContractsManager.ImplementationSetter memory)
{
bytes4 selector = LibString.eq(_dii.release(), "op-contracts/v1.6.0")
? IL1CrossDomainMessengerV160.initialize.selector
: L1CrossDomainMessenger.initialize.selector;
return OPContractsManager.ImplementationSetter({
name: "L1CrossDomainMessenger",
info: OPContractsManager.Implementation(address(_dio.l1CrossDomainMessengerImpl()), selector)
});
}
function l1StandardBridgeConfigSetter(
DeployImplementationsInput _dii,
DeployImplementationsOutput _dio
)
internal
view
virtual
returns (OPContractsManager.ImplementationSetter memory)
{
bytes4 selector = LibString.eq(_dii.release(), "op-contracts/v1.6.0")
? IL1StandardBridgeV160.initialize.selector
: L1StandardBridge.initialize.selector;
return OPContractsManager.ImplementationSetter({
name: "L1StandardBridge",
info: OPContractsManager.Implementation(address(_dio.l1StandardBridgeImpl()), selector)
});
}
// Deploy and initialize a proxied OPContractsManager.
function createOPCMContract(
DeployImplementationsInput _dii,
......@@ -516,7 +553,7 @@ contract DeployImplementations is Script {
virtual
returns (OPContractsManager opcmProxy_)
{
ProxyAdmin proxyAdmin = _dii.superchainProxyAdmin();
address opcmProxyOwner = _dii.opcmProxyOwner();
vm.broadcast(msg.sender);
Proxy proxy = new Proxy(address(msg.sender));
......@@ -532,7 +569,7 @@ contract DeployImplementations is Script {
address(opcmImpl), abi.encodeWithSelector(opcmImpl.initialize.selector, initializerInputs)
);
proxy.changeAdmin(address(proxyAdmin)); // transfer ownership of Proxy contract to the ProxyAdmin contract
proxy.changeAdmin(address(opcmProxyOwner)); // transfer ownership of Proxy contract to the ProxyAdmin contract
vm.stopBroadcast();
opcmProxy_ = OPContractsManager(address(proxy));
......@@ -579,18 +616,8 @@ contract DeployImplementations is Script {
address(_dio.optimismMintableERC20FactoryImpl()), OptimismMintableERC20Factory.initialize.selector
)
});
setters[4] = OPContractsManager.ImplementationSetter({
name: "L1CrossDomainMessenger",
info: OPContractsManager.Implementation(
address(_dio.l1CrossDomainMessengerImpl()), L1CrossDomainMessenger.initialize.selector
)
});
setters[5] = OPContractsManager.ImplementationSetter({
name: "L1StandardBridge",
info: OPContractsManager.Implementation(
address(_dio.l1StandardBridgeImpl()), L1StandardBridge.initialize.selector
)
});
setters[4] = l1CrossDomainMessengerConfigSetter(_dii, _dio);
setters[5] = l1StandardBridgeConfigSetter(_dii, _dio);
setters[6] = OPContractsManager.ImplementationSetter({
name: "DisputeGameFactory",
info: OPContractsManager.Implementation(
......@@ -1036,7 +1063,7 @@ contract DeployImplementationsInterop is DeployImplementations {
override
returns (OPContractsManager opcmProxy_)
{
ProxyAdmin proxyAdmin = _dii.superchainProxyAdmin();
address opcmProxyOwner = _dii.opcmProxyOwner();
vm.broadcast(msg.sender);
Proxy proxy = new Proxy(address(msg.sender));
......@@ -1052,7 +1079,7 @@ contract DeployImplementationsInterop is DeployImplementations {
address(opcmImpl), abi.encodeWithSelector(opcmImpl.initialize.selector, initializerInputs)
);
proxy.changeAdmin(address(proxyAdmin)); // transfer ownership of Proxy contract to the ProxyAdmin contract
proxy.changeAdmin(opcmProxyOwner); // transfer ownership of Proxy contract to the ProxyAdmin contract
vm.stopBroadcast();
opcmProxy_ = OPContractsManagerInterop(address(proxy));
......
......@@ -471,7 +471,7 @@ contract DeployOPChainOutput is BaseDeployIO {
require(vm.load(address(portal), bytes32(uint256(61))) == bytes32(0));
}
function assertValidDisputeGameFactory(DeployOPChainInput) internal view {
function assertValidDisputeGameFactory(DeployOPChainInput _doi) internal view {
DisputeGameFactory factory = disputeGameFactoryProxy();
DeployUtils.assertInitialized({ _contractAddress: address(factory), _slot: 0, _offset: 0 });
......@@ -479,7 +479,7 @@ contract DeployOPChainOutput is BaseDeployIO {
require(
address(factory.gameImpls(GameTypes.PERMISSIONED_CANNON)) == address(permissionedDisputeGame()), "DF-10"
);
require(factory.owner() == address(opChainProxyAdmin()), "DF-20");
require(factory.owner() == address(_doi.opChainProxyAdminOwner()), "DF-20");
}
function assertValidDelayedWETHs(DeployOPChainInput) internal view {
......
......@@ -32,8 +32,8 @@
"sourceCodeHash": "0xde4df0f9633dc0cdb1c9f634003ea5b0f7c5c1aebc407bc1b2f44c0ecf938649"
},
"src/L1/OPContractsManager.sol": {
"initCodeHash": "0x944deadee322fdbae8a8fffd16deceb3766509cfb54da06adb8aa84473f79f53",
"sourceCodeHash": "0x1a48119cbc0b778a4dd3454179060b71361ba44b61af1ac6398cc9274bb5e89f"
"initCodeHash": "0x7c5d90928ce882ed5360939722271e9af36e81c394e4110ba32864b14c3d78be",
"sourceCodeHash": "0x25372ad554eaeb64d7512e19642210bb3736e4047ea97518b2992b3ab67e1a5d"
},
"src/L1/OptimismPortal.sol": {
"initCodeHash": "0xbe2c0c81b3459014f287d8c89cdc0d27dde5d1f44e5d024fa1e4773ddc47c190",
......
......@@ -128,8 +128,8 @@ contract OPContractsManager is ISemver, Initializable {
// -------- Constants and Variables --------
/// @custom:semver 1.0.0-beta.14
string public constant version = "1.0.0-beta.14";
/// @custom:semver 1.0.0-beta.15
string public constant version = "1.0.0-beta.15";
/// @notice Represents the interface version so consumers know how to decode the DeployOutput struct
/// that's emitted in the `Deployed` event. Whenever that struct changes, a new version should be used.
......@@ -342,7 +342,7 @@ contract OPContractsManager is ISemver, Initializable {
output.disputeGameFactoryProxy.setImplementation(
GameTypes.PERMISSIONED_CANNON, IDisputeGame(address(output.permissionedDisputeGame))
);
output.disputeGameFactoryProxy.transferOwnership(address(output.opChainProxyAdmin));
output.disputeGameFactoryProxy.transferOwnership(address(_input.roles.opChainProxyAdminOwner));
impl.logic = address(output.anchorStateRegistryImpl);
impl.initializer = AnchorStateRegistry.initialize.selector;
......
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;
import { ICrossDomainMessenger } from "src/universal/interfaces/ICrossDomainMessenger.sol";
import { ISuperchainConfig } from "src/L1/interfaces/ISuperchainConfig.sol";
import { IOptimismPortal } from "src/L1/interfaces/IOptimismPortal.sol";
import { ISystemConfig } from "src/L1/interfaces/ISystemConfig.sol";
/// @notice This interface corresponds to the op-contracts/v1.6.0 release of the L1CrossDomainMessenger
/// contract, which has a semver of 2.3.0 as specified in
/// https://github.com/ethereum-optimism/optimism/releases/tag/op-contracts%2Fv1.6.0
interface IL1CrossDomainMessengerV160 is ICrossDomainMessenger {
function PORTAL() external view returns (address);
function initialize(ISuperchainConfig _superchainConfig, IOptimismPortal _portal) external;
function portal() external view returns (address);
function superchainConfig() external view returns (address);
function systemConfig() external view returns (address);
function version() external view returns (string memory);
function __constructor__() external;
}
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;
import { IStandardBridge } from "src/universal/interfaces/IStandardBridge.sol";
import { ICrossDomainMessenger } from "src/universal/interfaces/ICrossDomainMessenger.sol";
import { ISuperchainConfig } from "src/L1/interfaces/ISuperchainConfig.sol";
import { ISystemConfig } from "src/L1/interfaces/ISystemConfig.sol";
/// @notice This interface corresponds to the op-contracts/v1.6.0 release of the L1StandardBridge
/// contract, which has a semver of 2.1.0 as specified in
/// https://github.com/ethereum-optimism/optimism/releases/tag/op-contracts%2Fv1.6.0
interface IL1StandardBridgeV160 is IStandardBridge {
event ERC20DepositInitiated(
address indexed l1Token,
address indexed l2Token,
address indexed from,
address to,
uint256 amount,
bytes extraData
);
event ERC20WithdrawalFinalized(
address indexed l1Token,
address indexed l2Token,
address indexed from,
address to,
uint256 amount,
bytes extraData
);
event ETHDepositInitiated(address indexed from, address indexed to, uint256 amount, bytes extraData);
event ETHWithdrawalFinalized(address indexed from, address indexed to, uint256 amount, bytes extraData);
function depositERC20(
address _l1Token,
address _l2Token,
uint256 _amount,
uint32 _minGasLimit,
bytes memory _extraData
)
external;
function depositERC20To(
address _l1Token,
address _l2Token,
address _to,
uint256 _amount,
uint32 _minGasLimit,
bytes memory _extraData
)
external;
function depositETH(uint32 _minGasLimit, bytes memory _extraData) external payable;
function depositETHTo(address _to, uint32 _minGasLimit, bytes memory _extraData) external payable;
function finalizeERC20Withdrawal(
address _l1Token,
address _l2Token,
address _from,
address _to,
uint256 _amount,
bytes memory _extraData
)
external;
function finalizeETHWithdrawal(
address _from,
address _to,
uint256 _amount,
bytes memory _extraData
)
external
payable;
function initialize(ICrossDomainMessenger _messenger, ISuperchainConfig _superchainConfig) external;
function l2TokenBridge() external view returns (address);
function superchainConfig() external view returns (ISuperchainConfig);
function systemConfig() external view returns (ISystemConfig);
function version() external view returns (string memory);
function __constructor__() external;
}
......@@ -48,8 +48,8 @@ interface ISystemConfigV160 {
function gasPayingTokenSymbol() external view returns (string memory symbol_);
function initialize(
address _owner,
uint32 _basefeeScalar,
uint32 _blobbasefeeScalar,
uint256 _basefeeScalar,
uint256 _blobbasefeeScalar,
bytes32 _batcherHash,
uint64 _gasLimit,
address _unsafeBlockSigner,
......
......@@ -69,31 +69,21 @@ contract DeployImplementationsInput_Test is Test {
dii.protocolVersionsProxy();
vm.expectRevert("DeployImplementationsInput: not set");
dii.superchainProxyAdmin();
dii.opcmProxyOwner();
vm.expectRevert("DeployImplementationsInput: not set");
dii.standardVersionsToml();
}
function test_superchainProxyAdmin_whenNotSet_reverts() public {
function test_opcmProxyOwner_whenNotSet_reverts() public {
vm.expectRevert("DeployImplementationsInput: not set");
dii.superchainProxyAdmin();
dii.set(dii.superchainConfigProxy.selector, address(superchainConfigProxy));
vm.expectRevert();
dii.superchainProxyAdmin();
Proxy noAdminProxy = new Proxy(address(0));
dii.set(dii.superchainConfigProxy.selector, address(noAdminProxy));
vm.expectRevert("DeployImplementationsInput: not set");
dii.superchainProxyAdmin();
dii.opcmProxyOwner();
}
function test_superchainProxyAdmin_succeeds() public {
Proxy proxyWithAdminSet = new Proxy(msg.sender);
dii.set(dii.superchainConfigProxy.selector, address(proxyWithAdminSet));
ProxyAdmin proxyAdmin = dii.superchainProxyAdmin();
assertEq(address(msg.sender), address(proxyAdmin), "100");
function test_opcmProxyOwner_succeeds() public {
dii.set(dii.opcmProxyOwner.selector, address(msg.sender));
address opcmProxyOwner = dii.opcmProxyOwner();
assertEq(address(msg.sender), address(opcmProxyOwner), "100");
}
}
......@@ -433,6 +423,7 @@ contract DeployImplementations_Test is Test {
dii.set(dii.release.selector, release);
dii.set(dii.superchainConfigProxy.selector, address(superchainConfigProxy));
dii.set(dii.protocolVersionsProxy.selector, address(protocolVersionsProxy));
dii.set(dii.opcmProxyOwner.selector, msg.sender);
deployImplementations.run(dii, dio);
......@@ -445,7 +436,7 @@ contract DeployImplementations_Test is Test {
assertEq(release, dii.release(), "525");
assertEq(address(superchainConfigProxy), address(dii.superchainConfigProxy()), "550");
assertEq(address(protocolVersionsProxy), address(dii.protocolVersionsProxy()), "575");
assertEq(address(superchainProxyAdmin), address(dii.superchainProxyAdmin()), "580");
assertEq(msg.sender, dii.opcmProxyOwner(), "580");
// Architecture assertions.
assertEq(address(dio.mipsSingleton().oracle()), address(dio.preimageOracleSingleton()), "600");
......
......@@ -417,6 +417,7 @@ contract DeployOPChain_TestBase is Test {
string.concat(vm.projectRoot(), "/test/fixtures/standard-versions.toml");
string memory standardVersionsToml = vm.readFile(standardVersionsTomlPath);
dii.set(dii.standardVersionsToml.selector, standardVersionsToml);
dii.set(dii.opcmProxyOwner.selector, address(1));
deployImplementations.run(dii, dio);
// Deploy DeployOpChain, but defer populating the input values to the test suites inheriting this contract.
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment