Commit 3056348b authored by protolambda's avatar protolambda Committed by GitHub

interop: interopgen v2 using OPSM (#11702)

* op-chain-ops/interopgen: OPSM powered interop genesis

* ci: make forge scripts available to op-e2e

* op-chain-ops: address interopgen review comments
parent 3ba35fab
...@@ -1591,8 +1591,8 @@ workflows: ...@@ -1591,8 +1591,8 @@ workflows:
- contracts-bedrock-build: - contracts-bedrock-build:
name: contracts-bedrock-build name: contracts-bedrock-build
build_command: | build_command: |
forge build --skip test --skip scripts # Note: scripts are included, to be available to op-e2e
forge build ./scripts/deploy/Deploy.s.sol forge build --skip test
- contracts-bedrock-tests: - contracts-bedrock-tests:
# Test everything except PreimageOracle.t.sol since it's slow. # Test everything except PreimageOracle.t.sol since it's slow.
name: contracts-bedrock-tests name: contracts-bedrock-tests
......
...@@ -230,9 +230,9 @@ type GasPriceOracleDeployConfig struct { ...@@ -230,9 +230,9 @@ type GasPriceOracleDeployConfig struct {
// Deprecated: Since Ecotone, this field is superseded by GasPriceOracleBaseFeeScalar and GasPriceOracleBlobBaseFeeScalar. // Deprecated: Since Ecotone, this field is superseded by GasPriceOracleBaseFeeScalar and GasPriceOracleBlobBaseFeeScalar.
GasPriceOracleScalar uint64 `json:"gasPriceOracleScalar"` GasPriceOracleScalar uint64 `json:"gasPriceOracleScalar"`
// GasPriceOracleBaseFeeScalar represents the value of the base fee scalar used for fee calculations. // GasPriceOracleBaseFeeScalar represents the value of the base fee scalar used for fee calculations.
GasPriceOracleBaseFeeScalar uint32 `json:"gasPriceOracleBaseFeeScalar"` GasPriceOracleBaseFeeScalar uint32 `json:"gasPriceOracleBaseFeeScalar" evm:"basefeeScalar"`
// GasPriceOracleBlobBaseFeeScalar represents the value of the blob base fee scalar used for fee calculations. // GasPriceOracleBlobBaseFeeScalar represents the value of the blob base fee scalar used for fee calculations.
GasPriceOracleBlobBaseFeeScalar uint32 `json:"gasPriceOracleBlobBaseFeeScalar"` GasPriceOracleBlobBaseFeeScalar uint32 `json:"gasPriceOracleBlobBaseFeeScalar" evm:"blobbasefeeScalar"`
} }
var _ ConfigChecker = (*GasPriceOracleDeployConfig)(nil) var _ ConfigChecker = (*GasPriceOracleDeployConfig)(nil)
...@@ -282,7 +282,7 @@ func (d *GasTokenDeployConfig) Check(log log.Logger) error { ...@@ -282,7 +282,7 @@ func (d *GasTokenDeployConfig) Check(log log.Logger) error {
// OperatorDeployConfig configures the hot-key addresses for operations such as sequencing and batch-submission. // OperatorDeployConfig configures the hot-key addresses for operations such as sequencing and batch-submission.
type OperatorDeployConfig struct { type OperatorDeployConfig struct {
// P2PSequencerAddress is the address of the key the sequencer uses to sign blocks on the P2P layer. // P2PSequencerAddress is the address of the key the sequencer uses to sign blocks on the P2P layer.
P2PSequencerAddress common.Address `json:"p2pSequencerAddress"` P2PSequencerAddress common.Address `json:"p2pSequencerAddress" evm:"p2pSequencerAddress"`
// BatchSenderAddress represents the initial sequencer account that authorizes batches. // BatchSenderAddress represents the initial sequencer account that authorizes batches.
// Transactions sent from this account to the batch inbox address are considered valid. // Transactions sent from this account to the batch inbox address are considered valid.
BatchSenderAddress common.Address `json:"batchSenderAddress"` BatchSenderAddress common.Address `json:"batchSenderAddress"`
...@@ -627,7 +627,7 @@ type OutputOracleDeployConfig struct { ...@@ -627,7 +627,7 @@ type OutputOracleDeployConfig struct {
L2OutputOracleSubmissionInterval uint64 `json:"l2OutputOracleSubmissionInterval"` L2OutputOracleSubmissionInterval uint64 `json:"l2OutputOracleSubmissionInterval"`
// L2OutputOracleStartingTimestamp is the starting timestamp for the L2OutputOracle. // L2OutputOracleStartingTimestamp is the starting timestamp for the L2OutputOracle.
// MUST be the same as the timestamp of the L2OO start block. // MUST be the same as the timestamp of the L2OO start block.
L2OutputOracleStartingTimestamp int `json:"l2OutputOracleStartingTimestamp"` L2OutputOracleStartingTimestamp int64 `json:"l2OutputOracleStartingTimestamp"`
// L2OutputOracleStartingBlockNumber is the starting block number for the L2OutputOracle. // L2OutputOracleStartingBlockNumber is the starting block number for the L2OutputOracle.
// Must be greater than or equal to the first Bedrock block. The first L2 output will correspond // Must be greater than or equal to the first Bedrock block. The first L2 output will correspond
// to this value plus the submission interval. // to this value plus the submission interval.
...@@ -808,7 +808,7 @@ type DeployConfig struct { ...@@ -808,7 +808,7 @@ type DeployConfig struct {
// The L2 genesis timestamp does not affect the initial L2 account state: // The L2 genesis timestamp does not affect the initial L2 account state:
// the storage of the L1Block contract at genesis is zeroed, since the adoption of // the storage of the L1Block contract at genesis is zeroed, since the adoption of
// the L2-genesis allocs-generation through solidity script. // the L2-genesis allocs-generation through solidity script.
L1StartingBlockTag *MarshalableRPCBlockNumberOrHash `json:"l1StartingBlockTag"` L1StartingBlockTag *MarshalableRPCBlockNumberOrHash `json:"l1StartingBlockTag" evm:"-"`
// L1 contracts configuration. // L1 contracts configuration.
// The deployer of the contracts chooses which sub-systems to deploy. // The deployer of the contracts chooses which sub-systems to deploy.
...@@ -820,7 +820,7 @@ type DeployConfig struct { ...@@ -820,7 +820,7 @@ type DeployConfig struct {
L1DependenciesConfig L1DependenciesConfig
// Legacy, ignored, here for strict-JSON decoding to be accepted. // Legacy, ignored, here for strict-JSON decoding to be accepted.
LegacyDeployConfig LegacyDeployConfig `evm:"-"`
} }
// Copy will deeply copy the DeployConfig. This does a JSON roundtrip to copy // Copy will deeply copy the DeployConfig. This does a JSON roundtrip to copy
......
...@@ -4,6 +4,8 @@ import ( ...@@ -4,6 +4,8 @@ import (
"encoding/json" "encoding/json"
"fmt" "fmt"
"strconv" "strconv"
"github.com/holiman/uint256"
) )
// WithdrawalNetwork represents the network that withdrawals are sent to. // WithdrawalNetwork represents the network that withdrawals are sent to.
...@@ -32,6 +34,11 @@ func (w *WithdrawalNetwork) ToUint8() uint8 { ...@@ -32,6 +34,11 @@ func (w *WithdrawalNetwork) ToUint8() uint8 {
} }
} }
func (w WithdrawalNetwork) ToABI() []byte {
out := uint256.NewInt(uint64(w.ToUint8())).Bytes32()
return out[:]
}
// FromUint8 converts a uint8 to a WithdrawalNetwork. // FromUint8 converts a uint8 to a WithdrawalNetwork.
func FromUint8(i uint8) WithdrawalNetwork { func FromUint8(i uint8) WithdrawalNetwork {
switch i { switch i {
......
package interopgen
import (
"errors"
"fmt"
"math/big"
"github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/log"
"github.com/ethereum-optimism/optimism/op-chain-ops/genesis"
)
type L1Config struct {
ChainID *big.Int
genesis.DevL1DeployConfig
Prefund map[common.Address]*big.Int
}
func (c *L1Config) Check(log log.Logger) error {
if c.ChainID == nil {
return errors.New("missing L1 chain ID")
}
// nothing to check on c.DevL1DeployConfig
return nil
}
type SuperFaultProofConfig struct {
WithdrawalDelaySeconds *big.Int
MinProposalSizeBytes *big.Int
ChallengePeriodSeconds *big.Int
ProofMaturityDelaySeconds *big.Int
DisputeGameFinalityDelaySeconds *big.Int
}
type OPSMImplementationsConfig struct {
Release string
FaultProof SuperFaultProofConfig
UseInterop bool // to deploy Interop implementation contracts, instead of the regular ones.
}
type SuperchainConfig struct {
Deployer common.Address
ProxyAdminOwner common.Address
ProtocolVersionsOwner common.Address
Paused bool
Implementations OPSMImplementationsConfig
genesis.SuperchainL1DeployConfig
}
func (c *SuperchainConfig) Check(log log.Logger) error {
if c.Deployer == (common.Address{}) {
return errors.New("missing superchain deployer address")
}
if c.ProxyAdminOwner == (common.Address{}) {
return errors.New("missing superchain ProxyAdminOwner address")
}
if err := c.SuperchainL1DeployConfig.Check(log); err != nil {
return err
}
return nil
}
type L2Config struct {
Deployer common.Address // account used to deploy contracts to L2
Proposer common.Address
Challenger common.Address
SystemConfigOwner common.Address
genesis.L2InitializationConfig
Prefund map[common.Address]*big.Int
}
func (c *L2Config) Check(log log.Logger) error {
if c.Deployer == (common.Address{}) {
return errors.New("missing L2 deployer address")
}
if err := c.L2InitializationConfig.Check(log); err != nil {
return err
}
return nil
}
type WorldConfig struct {
L1 *L1Config
Superchain *SuperchainConfig
L2s map[string]*L2Config
}
func (c *WorldConfig) Check(log log.Logger) error {
if err := c.L1.Check(log); err != nil {
return fmt.Errorf("invalid L1 config: %w", err)
}
if err := c.Superchain.Check(log); err != nil {
return fmt.Errorf("invalid Superchain config: %w", err)
}
for l2ChainID, l2Cfg := range c.L2s {
if err := l2Cfg.Check(log.New("l2", &l2ChainID)); err != nil {
return fmt.Errorf("invalid L2 (chain ID %s) config: %w", l2ChainID, err)
}
}
return nil
}
This diff is collapsed.
package deployers
import (
"fmt"
"math/big"
"github.com/ethereum/go-ethereum/common"
"github.com/ethereum-optimism/optimism/op-chain-ops/script"
)
type DeployImplementationsInput struct {
WithdrawalDelaySeconds *big.Int
MinProposalSizeBytes *big.Int
ChallengePeriodSeconds *big.Int
ProofMaturityDelaySeconds *big.Int
DisputeGameFinalityDelaySeconds *big.Int
// Release version to set OPSM implementations for, of the format `op-contracts/vX.Y.Z`.
Release string
SuperchainConfigProxy common.Address
ProtocolVersionsProxy common.Address
UseInterop bool // if true, deploy Interop implementations
}
func (input *DeployImplementationsInput) InputSet() bool {
return true
}
type DeployImplementationsOutput struct {
Opsm common.Address
DelayedWETHImpl common.Address
OptimismPortalImpl common.Address
PreimageOracleSingleton common.Address
MipsSingleton common.Address
SystemConfigImpl common.Address
L1CrossDomainMessengerImpl common.Address
L1ERC721BridgeImpl common.Address
L1StandardBridgeImpl common.Address
OptimismMintableERC20FactoryImpl common.Address
DisputeGameFactoryImpl common.Address
}
func (output *DeployImplementationsOutput) CheckOutput() error {
return nil
}
type DeployImplementationsScript struct {
Run func(input, output common.Address) error
}
func DeployImplementations(l1Host *script.Host, input *DeployImplementationsInput) (*DeployImplementationsOutput, error) {
output := &DeployImplementationsOutput{}
inputAddr := l1Host.NewScriptAddress()
outputAddr := l1Host.NewScriptAddress()
cleanupInput, err := script.WithPrecompileAtAddress[*DeployImplementationsInput](l1Host, inputAddr, input)
if err != nil {
return nil, fmt.Errorf("failed to insert DeployImplementationsInput precompile: %w", err)
}
defer cleanupInput()
cleanupOutput, err := script.WithPrecompileAtAddress[*DeployImplementationsOutput](l1Host, outputAddr, output,
script.WithFieldSetter[*DeployImplementationsOutput])
if err != nil {
return nil, fmt.Errorf("failed to insert DeployImplementationsOutput precompile: %w", err)
}
defer cleanupOutput()
implContract := "DeployImplementations"
if input.UseInterop {
implContract = "DeployImplementationsInterop"
}
deployScript, cleanupDeploy, err := script.WithScript[DeployImplementationsScript](l1Host, "DeployImplementations.s.sol", implContract)
if err != nil {
return nil, fmt.Errorf("failed to load %s script: %w", implContract, err)
}
defer cleanupDeploy()
opsmContract := "OPStackManager"
if input.UseInterop {
opsmContract = "OPStackManagerInterop"
}
if err := l1Host.RememberOnLabel("OPStackManager", opsmContract+".sol", opsmContract); err != nil {
return nil, fmt.Errorf("failed to link OPStackManager label: %w", err)
}
// So we can see in detail where the SystemConfig interop initializer fails
sysConfig := "SystemConfig"
if input.UseInterop {
sysConfig = "SystemConfigInterop"
}
if err := l1Host.RememberOnLabel("SystemConfigImpl", sysConfig+".sol", sysConfig); err != nil {
return nil, fmt.Errorf("failed to link SystemConfig label: %w", err)
}
if err := deployScript.Run(inputAddr, outputAddr); err != nil {
return nil, fmt.Errorf("failed to run %s script: %w", implContract, err)
}
return output, nil
}
package deployers
import (
"fmt"
"github.com/ethereum-optimism/optimism/op-chain-ops/genesis"
"github.com/ethereum-optimism/optimism/op-chain-ops/script"
"github.com/ethereum/go-ethereum/common"
)
var (
// address(uint160(uint256(keccak256(abi.encode("optimism.deployconfig"))))) - not a simple hash, due to ABI encode
deployConfigAddr = common.HexToAddress("0x9568d36E291c2C4c34fa5593fcE73715abEf6F9c")
)
type L1Deployments struct {
L1CrossDomainMessengerProxy common.Address
L1StandardBridgeProxy common.Address
L1ERC721BridgeProxy common.Address
}
type L2GenesisInput struct {
L1Deployments L1Deployments
L2Config genesis.L2InitializationConfig
}
type L2GenesisScript struct {
RunWithEnv func() error
}
func L2Genesis(l2Host *script.Host, input *L2GenesisInput) error {
l2Host.SetEnvVar("L2GENESIS_L1CrossDomainMessengerProxy", input.L1Deployments.L1CrossDomainMessengerProxy.String())
l2Host.SetEnvVar("L2GENESIS_L1StandardBridgeProxy", input.L1Deployments.L1StandardBridgeProxy.String())
l2Host.SetEnvVar("L2GENESIS_L1ERC721BridgeProxy", input.L1Deployments.L1ERC721BridgeProxy.String())
deployConfig := &genesis.DeployConfig{
L2InitializationConfig: input.L2Config,
}
cleanupDeployConfig, err := script.WithPrecompileAtAddress[*genesis.DeployConfig](l2Host, deployConfigAddr, deployConfig, script.WithFieldsOnly[*genesis.DeployConfig])
if err != nil {
return fmt.Errorf("failed to insert DeployConfig precompile: %w", err)
}
defer cleanupDeployConfig()
l2GenesisScript, cleanupL2Genesis, err := script.WithScript[L2GenesisScript](l2Host, "L2Genesis.s.sol", "L2Genesis")
if err != nil {
return fmt.Errorf("failed to load L2Genesis script: %w", err)
}
defer cleanupL2Genesis()
if err := l2GenesisScript.RunWithEnv(); err != nil {
return fmt.Errorf("failed to run L2 genesis script: %w", err)
}
return nil
}
package deployers
import (
"fmt"
"math/big"
"github.com/ethereum/go-ethereum/common"
"github.com/ethereum-optimism/optimism/op-chain-ops/script"
)
type DeployOPChainInput struct {
OpChainProxyAdminOwner common.Address
SystemConfigOwner common.Address
Batcher common.Address
UnsafeBlockSigner common.Address
Proposer common.Address
Challenger common.Address
BasefeeScalar uint32
BlobBaseFeeScalar uint32
L2ChainId *big.Int
Opsm common.Address
}
func (input *DeployOPChainInput) InputSet() bool {
return true
}
type DeployOPChainOutput struct {
OpChainProxyAdmin common.Address
AddressManager common.Address
L1ERC721BridgeProxy common.Address
SystemConfigProxy common.Address
OptimismMintableERC20FactoryProxy common.Address
L1StandardBridgeProxy common.Address
L1CrossDomainMessengerProxy common.Address
// Fault proof contracts below.
OptimismPortalProxy common.Address
DisputeGameFactoryProxy common.Address
DisputeGameFactoryImpl common.Address
AnchorStateRegistryProxy common.Address
AnchorStateRegistryImpl common.Address
FaultDisputeGame common.Address
PermissionedDisputeGame common.Address
DelayedWETHPermissionedGameProxy common.Address
DelayedWETHPermissionlessGameProxy common.Address
}
func (output *DeployOPChainOutput) CheckOutput() error {
return nil
}
type DeployOPChainScript struct {
Run func(input, output common.Address) error
}
func DeployOPChain(l1Host *script.Host, input *DeployOPChainInput) (*DeployOPChainOutput, error) {
output := &DeployOPChainOutput{}
inputAddr := l1Host.NewScriptAddress()
outputAddr := l1Host.NewScriptAddress()
cleanupInput, err := script.WithPrecompileAtAddress[*DeployOPChainInput](l1Host, inputAddr, input)
if err != nil {
return nil, fmt.Errorf("failed to insert DeployOPChainInput precompile: %w", err)
}
defer cleanupInput()
cleanupOutput, err := script.WithPrecompileAtAddress[*DeployOPChainOutput](l1Host, outputAddr, output,
script.WithFieldSetter[*DeployOPChainOutput])
if err != nil {
return nil, fmt.Errorf("failed to insert DeployOPChainOutput precompile: %w", err)
}
defer cleanupOutput()
deployScript, cleanupDeploy, err := script.WithScript[DeployOPChainScript](l1Host, "DeployOPChain.s.sol", "DeployOPChain")
if err != nil {
return nil, fmt.Errorf("failed to load DeployOPChain script: %w", err)
}
defer cleanupDeploy()
if err := deployScript.Run(inputAddr, outputAddr); err != nil {
return nil, fmt.Errorf("failed to run DeployOPChain script: %w", err)
}
return output, nil
}
package deployers
import (
"fmt"
"github.com/ethereum-optimism/optimism/op-chain-ops/script"
)
type PreinstallsScript struct {
SetPreinstalls func() error
}
func InsertPreinstalls(host *script.Host) error {
l2GenesisScript, cleanupL2Genesis, err := script.WithScript[PreinstallsScript](host, "SetPreinstalls.s.sol", "SetPreinstalls")
if err != nil {
return fmt.Errorf("failed to load SetPreinstalls script: %w", err)
}
defer cleanupL2Genesis()
if err := l2GenesisScript.SetPreinstalls(); err != nil {
return fmt.Errorf("failed to set preinstalls: %w", err)
}
return nil
}
package deployers
import (
"fmt"
"github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/params"
"github.com/ethereum-optimism/optimism/op-chain-ops/script"
)
type DeploySuperchainInput struct {
ProxyAdminOwner common.Address // TODO(#11783): also used as interop-dependency-set owner
ProtocolVersionsOwner common.Address
Guardian common.Address
Paused bool
RequiredProtocolVersion params.ProtocolVersion
RecommendedProtocolVersion params.ProtocolVersion
}
func (input *DeploySuperchainInput) InputSet() bool {
return true
}
type DeploySuperchainOutput struct {
SuperchainProxyAdmin common.Address
SuperchainConfigImpl common.Address
SuperchainConfigProxy common.Address
ProtocolVersionsImpl common.Address
ProtocolVersionsProxy common.Address
}
func (output *DeploySuperchainOutput) CheckOutput() error {
return nil
}
type DeploySuperchainScript struct {
Run func(input, output common.Address) error
}
func DeploySuperchain(l1Host *script.Host, input *DeploySuperchainInput) (*DeploySuperchainOutput, error) {
output := &DeploySuperchainOutput{}
inputAddr := l1Host.NewScriptAddress()
outputAddr := l1Host.NewScriptAddress()
cleanupInput, err := script.WithPrecompileAtAddress[*DeploySuperchainInput](l1Host, inputAddr, input)
if err != nil {
return nil, fmt.Errorf("failed to insert DeploySuperchainInput precompile: %w", err)
}
defer cleanupInput()
cleanupOutput, err := script.WithPrecompileAtAddress[*DeploySuperchainOutput](l1Host, outputAddr, output,
script.WithFieldSetter[*DeploySuperchainOutput])
if err != nil {
return nil, fmt.Errorf("failed to insert DeploySuperchainOutput precompile: %w", err)
}
defer cleanupOutput()
deployScript, cleanupDeploy, err := script.WithScript[DeploySuperchainScript](l1Host, "DeploySuperchain.s.sol", "DeploySuperchain")
if err != nil {
return nil, fmt.Errorf("failed to load DeploySuperchain script: %w", err)
}
defer cleanupDeploy()
if err := deployScript.Run(inputAddr, outputAddr); err != nil {
return nil, fmt.Errorf("failed to run DeploySuperchain script: %w", err)
}
return output, nil
}
package interopgen
import (
"github.com/ethereum/go-ethereum/common"
)
type L1Deployment struct {
// No global deployed contracts that aren't part of the superchain, yet.
}
type Implementations struct {
Opsm common.Address `json:"OPSM"` // not proxied
DelayedWETHImpl common.Address `json:"DelayedWETHImpl"`
OptimismPortalImpl common.Address `json:"OptimismPortalImpl"`
PreimageOracleSingleton common.Address `json:"PreimageOracleSingleton"`
MipsSingleton common.Address `json:"MipsSingleton"`
SystemConfigImpl common.Address `json:"SystemConfigImpl"`
L1CrossDomainMessengerImpl common.Address `json:"L1CrossDomainMessengerImpl"`
L1ERC721BridgeImpl common.Address `json:"L1ERC721BridgeImpl"`
L1StandardBridgeImpl common.Address `json:"L1StandardBridgeImpl"`
OptimismMintableERC20FactoryImpl common.Address `json:"OptimismMintableERC20FactoryImpl"`
DisputeGameFactoryImpl common.Address `json:"DisputeGameFactoryImpl"`
}
type SuperchainDeployment struct {
Implementations
ProxyAdmin common.Address `json:"ProxyAdmin"`
ProtocolVersions common.Address `json:"ProtocolVersions"`
ProtocolVersionsProxy common.Address `json:"ProtocolVersionsProxy"`
SuperchainConfig common.Address `json:"SuperchainConfig"`
SuperchainConfigProxy common.Address `json:"SuperchainConfigProxy"`
}
type L2OpchainDeployment struct {
OpChainProxyAdmin common.Address `json:"OpChainProxyAdmin"`
AddressManager common.Address `json:"AddressManager"`
L1ERC721BridgeProxy common.Address `json:"L1ERC721BridgeProxy"`
SystemConfigProxy common.Address `json:"SystemConfigProxy"`
OptimismMintableERC20FactoryProxy common.Address `json:"OptimismMintableERC20FactoryProxy"`
L1StandardBridgeProxy common.Address `json:"L1StandardBridgeProxy"`
L1CrossDomainMessengerProxy common.Address `json:"L1CrossDomainMessengerProxy"`
// Fault proof contracts below.
OptimismPortalProxy common.Address `json:"OptimismPortalProxy"`
DisputeGameFactoryProxy common.Address `json:"DisputeGameFactoryProxy"`
DisputeGameFactoryImpl common.Address `json:"DisputeGameFactoryImpl"`
AnchorStateRegistryProxy common.Address `json:"AnchorStateRegistryProxy"`
AnchorStateRegistryImpl common.Address `json:"AnchorStateRegistryImpl"`
FaultDisputeGame common.Address `json:"FaultDisputeGame"`
PermissionedDisputeGame common.Address `json:"PermissionedDisputeGame"`
DelayedWETHPermissionedGameProxy common.Address `json:"DelayedWETHPermissionedGameProxy"`
DelayedWETHPermissionlessGameProxy common.Address `json:"DelayedWETHPermissionlessGameProxy"`
}
type L2Deployment struct {
L2OpchainDeployment
// In the future this may contain optional extras,
// e.g. a Safe that will own the L2 chain contracts
}
type WorldDeployment struct {
L1 *L1Deployment `json:"L1"`
Superchain *SuperchainDeployment `json:"Superchain"`
L2s map[string]*L2Deployment `json:"L2s"`
}
package interopgen
import (
"github.com/ethereum/go-ethereum/core"
"github.com/ethereum-optimism/optimism/op-node/rollup"
)
type L1Output struct {
Genesis *core.Genesis
}
type L2Output struct {
Genesis *core.Genesis
RollupCfg *rollup.Config
}
type WorldOutput struct {
L1 *L1Output
L2s map[string]*L2Output
}
package interopgen
import (
"fmt"
"math/big"
"github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/common/hexutil"
"github.com/ethereum/go-ethereum/params"
"github.com/ethereum-optimism/optimism/op-chain-ops/devkeys"
"github.com/ethereum-optimism/optimism/op-chain-ops/genesis"
)
type InteropDevRecipe struct {
L1ChainID uint64
L2ChainIDs []uint64
GenesisTimestamp uint64
}
func (r *InteropDevRecipe) Build(addrs devkeys.Addresses) (*WorldConfig, error) {
// L1 genesis
l1Cfg := &L1Config{
ChainID: new(big.Int).SetUint64(r.L1ChainID),
DevL1DeployConfig: genesis.DevL1DeployConfig{
L1BlockTime: 6,
L1GenesisBlockTimestamp: hexutil.Uint64(r.GenesisTimestamp),
L1GenesisBlockGasLimit: 30_000_000,
},
Prefund: make(map[common.Address]*big.Int),
}
// TODO(#11887): consider making the number of prefunded keys configurable.
l1Users := devkeys.ChainUserKeys(l1Cfg.ChainID)
for i := uint64(0); i < 20; i++ {
userAddr, err := addrs.Address(l1Users(i))
if err != nil {
return nil, fmt.Errorf("failed to get L1 user addr %d: %w", i, err)
}
l1Cfg.Prefund[userAddr] = Ether(10_000_000)
}
superchainOps := devkeys.SuperchainOperatorKeys(l1Cfg.ChainID)
superchainDeployer, err := addrs.Address(superchainOps(devkeys.SuperchainDeployerKey))
if err != nil {
return nil, err
}
superchainProxyAdmin, err := addrs.Address(superchainOps(devkeys.SuperchainProxyAdminOwner))
if err != nil {
return nil, err
}
superchainProtocolVersionsOwner, err := addrs.Address(superchainOps(devkeys.SuperchainProtocolVersionsOwner))
if err != nil {
return nil, err
}
superchainConfigGuardian, err := addrs.Address(superchainOps(devkeys.SuperchainConfigGuardianKey))
if err != nil {
return nil, err
}
l1Cfg.Prefund[superchainDeployer] = Ether(10_000_000)
l1Cfg.Prefund[superchainProxyAdmin] = Ether(10_000_000)
l1Cfg.Prefund[superchainConfigGuardian] = Ether(10_000_000)
superchainCfg := &SuperchainConfig{
ProxyAdminOwner: superchainProxyAdmin,
ProtocolVersionsOwner: superchainProtocolVersionsOwner,
Deployer: superchainDeployer,
Implementations: OPSMImplementationsConfig{
Release: "op-contracts/0.0.1",
FaultProof: SuperFaultProofConfig{
WithdrawalDelaySeconds: big.NewInt(604800),
MinProposalSizeBytes: big.NewInt(10000),
ChallengePeriodSeconds: big.NewInt(120),
ProofMaturityDelaySeconds: big.NewInt(12),
DisputeGameFinalityDelaySeconds: big.NewInt(6),
},
UseInterop: true,
},
SuperchainL1DeployConfig: genesis.SuperchainL1DeployConfig{
RequiredProtocolVersion: params.OPStackSupport,
RecommendedProtocolVersion: params.OPStackSupport,
SuperchainConfigGuardian: superchainConfigGuardian,
},
}
world := &WorldConfig{
L1: l1Cfg,
Superchain: superchainCfg,
L2s: make(map[string]*L2Config),
}
for _, l2ChainID := range r.L2ChainIDs {
l2Cfg, err := InteropL2DevConfig(r.L1ChainID, l2ChainID, addrs)
if err != nil {
return nil, fmt.Errorf("failed to generate L2 config for chain %d: %w", l2ChainID, err)
}
if err := prefundL2Accounts(l1Cfg, l2Cfg, addrs); err != nil {
return nil, fmt.Errorf("failed to prefund addresses on L1 for L2 chain %d: %w", l2ChainID, err)
}
world.L2s[fmt.Sprintf("%d", l2ChainID)] = l2Cfg
}
return world, nil
}
func prefundL2Accounts(l1Cfg *L1Config, l2Cfg *L2Config, addrs devkeys.Addresses) error {
l1Cfg.Prefund[l2Cfg.BatchSenderAddress] = Ether(10_000_000)
l1Cfg.Prefund[l2Cfg.Deployer] = Ether(10_000_000)
l1Cfg.Prefund[l2Cfg.FinalSystemOwner] = Ether(10_000_000)
proposer, err := addrs.Address(devkeys.ChainOperatorKey{
ChainID: new(big.Int).SetUint64(l2Cfg.L2ChainID),
Role: devkeys.ProposerRole,
})
if err != nil {
return err
}
l1Cfg.Prefund[proposer] = Ether(10_000_000)
challenger, err := addrs.Address(devkeys.ChainOperatorKey{
ChainID: new(big.Int).SetUint64(l2Cfg.L2ChainID),
Role: devkeys.ChallengerRole,
})
if err != nil {
return err
}
l1Cfg.Prefund[challenger] = Ether(10_000_000)
return nil
}
func InteropL2DevConfig(l1ChainID, l2ChainID uint64, addrs devkeys.Addresses) (*L2Config, error) {
// Padded chain ID, hex encoded, prefixed with 0xff like inboxes, then 0x02 to signify devnet.
batchInboxAddress := common.HexToAddress(fmt.Sprintf("0xff02%016x", l2ChainID))
chainOps := devkeys.ChainOperatorKeys(new(big.Int).SetUint64(l2ChainID))
deployer, err := addrs.Address(chainOps(devkeys.DeployerRole))
if err != nil {
return nil, err
}
l1ProxyAdminOwner, err := addrs.Address(chainOps(devkeys.L1ProxyAdminOwnerRole))
if err != nil {
return nil, err
}
l2ProxyAdminOwner, err := addrs.Address(chainOps(devkeys.L2ProxyAdminOwnerRole))
if err != nil {
return nil, err
}
baseFeeVaultRecipient, err := addrs.Address(chainOps(devkeys.BaseFeeVaultRecipientRole))
if err != nil {
return nil, err
}
l1FeeVaultRecipient, err := addrs.Address(chainOps(devkeys.L1FeeVaultRecipientRole))
if err != nil {
return nil, err
}
sequencerFeeVaultRecipient, err := addrs.Address(chainOps(devkeys.SequencerFeeVaultRecipientRole))
if err != nil {
return nil, err
}
sequencerP2P, err := addrs.Address(chainOps(devkeys.SequencerP2PRole))
if err != nil {
return nil, err
}
batcher, err := addrs.Address(chainOps(devkeys.BatcherRole))
if err != nil {
return nil, err
}
proposer, err := addrs.Address(chainOps(devkeys.ProposerRole))
if err != nil {
return nil, err
}
challenger, err := addrs.Address(chainOps(devkeys.ChallengerRole))
if err != nil {
return nil, err
}
systemConfigOwner, err := addrs.Address(chainOps(devkeys.SystemConfigOwner))
if err != nil {
return nil, err
}
l2Cfg := &L2Config{
Deployer: deployer,
Proposer: proposer,
Challenger: challenger,
SystemConfigOwner: systemConfigOwner,
L2InitializationConfig: genesis.L2InitializationConfig{
DevDeployConfig: genesis.DevDeployConfig{
FundDevAccounts: true,
},
L2GenesisBlockDeployConfig: genesis.L2GenesisBlockDeployConfig{
L2GenesisBlockGasLimit: 30_000_000,
L2GenesisBlockBaseFeePerGas: (*hexutil.Big)(big.NewInt(params.InitialBaseFee)),
},
OwnershipDeployConfig: genesis.OwnershipDeployConfig{
ProxyAdminOwner: l2ProxyAdminOwner,
FinalSystemOwner: l1ProxyAdminOwner,
},
L2VaultsDeployConfig: genesis.L2VaultsDeployConfig{
BaseFeeVaultRecipient: baseFeeVaultRecipient,
L1FeeVaultRecipient: l1FeeVaultRecipient,
SequencerFeeVaultRecipient: sequencerFeeVaultRecipient,
BaseFeeVaultMinimumWithdrawalAmount: (*hexutil.Big)(Ether(10)),
L1FeeVaultMinimumWithdrawalAmount: (*hexutil.Big)(Ether(10)),
SequencerFeeVaultMinimumWithdrawalAmount: (*hexutil.Big)(Ether(10)),
BaseFeeVaultWithdrawalNetwork: "remote",
L1FeeVaultWithdrawalNetwork: "remote",
SequencerFeeVaultWithdrawalNetwork: "remote",
},
GovernanceDeployConfig: genesis.GovernanceDeployConfig{
EnableGovernance: false,
},
GasPriceOracleDeployConfig: genesis.GasPriceOracleDeployConfig{
GasPriceOracleBaseFeeScalar: 1368,
GasPriceOracleBlobBaseFeeScalar: 810949,
},
GasTokenDeployConfig: genesis.GasTokenDeployConfig{
UseCustomGasToken: false,
},
OperatorDeployConfig: genesis.OperatorDeployConfig{
P2PSequencerAddress: sequencerP2P,
BatchSenderAddress: batcher,
},
EIP1559DeployConfig: genesis.EIP1559DeployConfig{
EIP1559Elasticity: 6,
EIP1559Denominator: 50,
EIP1559DenominatorCanyon: 250,
},
UpgradeScheduleDeployConfig: genesis.UpgradeScheduleDeployConfig{
L2GenesisRegolithTimeOffset: new(hexutil.Uint64),
L2GenesisCanyonTimeOffset: new(hexutil.Uint64),
L2GenesisDeltaTimeOffset: new(hexutil.Uint64),
L2GenesisEcotoneTimeOffset: new(hexutil.Uint64),
L2GenesisFjordTimeOffset: new(hexutil.Uint64),
L2GenesisGraniteTimeOffset: new(hexutil.Uint64),
L2GenesisInteropTimeOffset: new(hexutil.Uint64),
L1CancunTimeOffset: new(hexutil.Uint64),
UseInterop: true,
},
L2CoreDeployConfig: genesis.L2CoreDeployConfig{
L1ChainID: l1ChainID,
L2ChainID: l2ChainID,
L2BlockTime: 2,
FinalizationPeriodSeconds: 2, // instant output finalization
MaxSequencerDrift: 300,
SequencerWindowSize: 200,
ChannelTimeoutBedrock: 120,
BatchInboxAddress: batchInboxAddress,
SystemConfigStartBlock: 0,
},
AltDADeployConfig: genesis.AltDADeployConfig{
UseAltDA: false,
},
},
Prefund: make(map[common.Address]*big.Int),
}
// TODO(#11887): consider making the number of prefunded keys configurable.
l2Users := devkeys.ChainUserKeys(new(big.Int).SetUint64(l2ChainID))
for i := uint64(0); i < 20; i++ {
userAddr, err := addrs.Address(l2Users(i))
if err != nil {
return nil, fmt.Errorf("failed to get L2 user addr %d: %w", i, err)
}
l2Cfg.Prefund[userAddr] = Ether(10_000_000)
}
l2Cfg.Prefund[l2ProxyAdminOwner] = Ether(10_000_000)
return l2Cfg, nil
}
var etherScalar = new(big.Int).Exp(big.NewInt(10), big.NewInt(18), nil)
// Ether converts a uint64 Ether amount into a *big.Int amount in wei units, for allocating test balances.
func Ether(v uint64) *big.Int {
return new(big.Int).Mul(new(big.Int).SetUint64(v), etherScalar)
}
package op_e2e
import (
"encoding/json"
"os"
"testing"
"github.com/stretchr/testify/require"
"github.com/ethereum/go-ethereum/log"
"github.com/ethereum-optimism/optimism/op-chain-ops/devkeys"
"github.com/ethereum-optimism/optimism/op-chain-ops/foundry"
"github.com/ethereum-optimism/optimism/op-chain-ops/interopgen"
"github.com/ethereum-optimism/optimism/op-service/testlog"
)
func TestInteropDevRecipe(t *testing.T) {
InitParallel(t)
rec := interopgen.InteropDevRecipe{
L1ChainID: 900100,
L2ChainIDs: []uint64{900200, 900201},
GenesisTimestamp: uint64(1234567),
}
hd, err := devkeys.NewMnemonicDevKeys(devkeys.TestMnemonic)
require.NoError(t, err)
worldCfg, err := rec.Build(hd)
require.NoError(t, err)
logger := testlog.Logger(t, log.LevelDebug)
require.NoError(t, worldCfg.Check(logger))
fa := foundry.OpenArtifactsDir("../packages/contracts-bedrock/forge-artifacts")
srcFS := foundry.NewSourceMapFS(os.DirFS("../packages/contracts-bedrock"))
worldDeployment, worldOutput, err := interopgen.Deploy(logger, fa, srcFS, worldCfg)
require.NoError(t, err)
enc := json.NewEncoder(os.Stdout)
enc.SetIndent(" ", " ")
require.NoError(t, enc.Encode(worldDeployment))
logger.Info("L1 output", "accounts", len(worldOutput.L1.Genesis.Alloc))
for id, l2Output := range worldOutput.L2s {
logger.Info("L2 output", "chain", &id, "accounts", len(l2Output.Genesis.Alloc))
}
}
...@@ -372,7 +372,7 @@ contract DeployImplementations is Script { ...@@ -372,7 +372,7 @@ contract DeployImplementations is Script {
vm.broadcast(msg.sender); vm.broadcast(msg.sender);
SystemConfig systemConfigImpl = new SystemConfig(); SystemConfig systemConfigImpl = new SystemConfig();
vm.label(address(systemConfigImpl), "systemConfigImpl"); vm.label(address(systemConfigImpl), "SystemConfigImpl");
_dio.set(_dio.systemConfigImpl.selector, address(systemConfigImpl)); _dio.set(_dio.systemConfigImpl.selector, address(systemConfigImpl));
} }
...@@ -612,7 +612,7 @@ contract DeployImplementationsInterop is DeployImplementations { ...@@ -612,7 +612,7 @@ contract DeployImplementationsInterop is DeployImplementations {
vm.broadcast(msg.sender); vm.broadcast(msg.sender);
SystemConfigInterop systemConfigImpl = new SystemConfigInterop(); SystemConfigInterop systemConfigImpl = new SystemConfigInterop();
vm.label(address(systemConfigImpl), "systemConfigImpl"); vm.label(address(systemConfigImpl), "SystemConfigImpl");
_dio.set(_dio.systemConfigImpl.selector, address(systemConfigImpl)); _dio.set(_dio.systemConfigImpl.selector, address(systemConfigImpl));
} }
......
...@@ -12,6 +12,7 @@ import { Config, OutputMode, OutputModeUtils, Fork, ForkUtils, LATEST_FORK } fro ...@@ -12,6 +12,7 @@ import { Config, OutputMode, OutputModeUtils, Fork, ForkUtils, LATEST_FORK } fro
import { Artifacts } from "scripts/Artifacts.s.sol"; import { Artifacts } from "scripts/Artifacts.s.sol";
import { DeployConfig } from "scripts/deploy/DeployConfig.s.sol"; import { DeployConfig } from "scripts/deploy/DeployConfig.s.sol";
import { Process } from "scripts/libraries/Process.sol"; import { Process } from "scripts/libraries/Process.sol";
import { SetPreinstalls } from "scripts/SetPreinstalls.s.sol";
// Contracts // Contracts
import { L2CrossDomainMessenger } from "src/L2/L2CrossDomainMessenger.sol"; import { L2CrossDomainMessenger } from "src/L2/L2CrossDomainMessenger.sol";
...@@ -131,6 +132,21 @@ contract L2Genesis is Deployer { ...@@ -131,6 +132,21 @@ contract L2Genesis is Deployer {
runWithOptions(OutputMode.ALL, LATEST_FORK, artifactDependencies()); runWithOptions(OutputMode.ALL, LATEST_FORK, artifactDependencies());
} }
/// @notice This is used by new experimental interop deploy tooling.
function runWithEnv() public {
// The setUp() is skipped (since we insert a custom DeployConfig, and do not use Artifacts)
deployer = makeAddr("deployer");
runWithOptions(
OutputMode.NONE,
Config.fork(),
L1Dependencies({
l1CrossDomainMessengerProxy: payable(vm.envAddress("L2GENESIS_L1CrossDomainMessengerProxy")),
l1StandardBridgeProxy: payable(vm.envAddress("L2GENESIS_L1StandardBridgeProxy")),
l1ERC721BridgeProxy: payable(vm.envAddress("L2GENESIS_L1ERC721BridgeProxy"))
})
);
}
/// @notice This is used by foundry tests to enable the latest fork with the /// @notice This is used by foundry tests to enable the latest fork with the
/// given L1 dependencies. /// given L1 dependencies.
function runWithLatestLocal(L1Dependencies memory _l1Dependencies) public { function runWithLatestLocal(L1Dependencies memory _l1Dependencies) public {
...@@ -540,30 +556,11 @@ contract L2Genesis is Deployer { ...@@ -540,30 +556,11 @@ contract L2Genesis is Deployer {
} }
/// @notice Sets all the preinstalls. /// @notice Sets all the preinstalls.
/// Warning: the creator-accounts of the preinstall contracts have 0 nonce values. function setPreinstalls() public {
/// When performing a regular user-initiated contract-creation of a preinstall, address tmpSetPreinstalls = address(uint160(uint256(keccak256("SetPreinstalls"))));
/// the creation will fail (but nonce will be bumped and not blocked). vm.etch(tmpSetPreinstalls, vm.getDeployedCode("SetPreinstalls.s.sol:SetPreinstalls"));
/// The preinstalls themselves are all inserted with a nonce of 1, reflecting regular user execution. SetPreinstalls(tmpSetPreinstalls).setPreinstalls();
function setPreinstalls() internal { vm.etch(tmpSetPreinstalls, "");
_setPreinstallCode(Preinstalls.MultiCall3);
_setPreinstallCode(Preinstalls.Create2Deployer);
_setPreinstallCode(Preinstalls.Safe_v130);
_setPreinstallCode(Preinstalls.SafeL2_v130);
_setPreinstallCode(Preinstalls.MultiSendCallOnly_v130);
_setPreinstallCode(Preinstalls.SafeSingletonFactory);
_setPreinstallCode(Preinstalls.DeterministicDeploymentProxy);
_setPreinstallCode(Preinstalls.MultiSend_v130);
_setPreinstallCode(Preinstalls.Permit2);
_setPreinstallCode(Preinstalls.SenderCreator_v060); // ERC 4337 v0.6.0
_setPreinstallCode(Preinstalls.EntryPoint_v060); // ERC 4337 v0.6.0
_setPreinstallCode(Preinstalls.SenderCreator_v070); // ERC 4337 v0.7.0
_setPreinstallCode(Preinstalls.EntryPoint_v070); // ERC 4337 v0.7.0
_setPreinstallCode(Preinstalls.BeaconBlockRoots);
_setPreinstallCode(Preinstalls.CreateX);
// 4788 sender nonce must be incremented, since it's part of later upgrade-transactions.
// For the upgrade-tx to not create a contract that conflicts with an already-existing copy,
// the nonce must be bumped.
vm.setNonce(Preinstalls.BeaconBlockRootsSender, 1);
} }
/// @notice Activate Ecotone network upgrade. /// @notice Activate Ecotone network upgrade.
...@@ -590,17 +587,6 @@ contract L2Genesis is Deployer { ...@@ -590,17 +587,6 @@ contract L2Genesis is Deployer {
return impl; return impl;
} }
/// @notice Sets the bytecode in state
function _setPreinstallCode(address _addr) internal {
string memory cname = Preinstalls.getName(_addr);
console.log("Setting %s preinstall code at: %s", cname, _addr);
vm.etch(_addr, Preinstalls.getDeployedCode(_addr, cfg.l2ChainID()));
// during testing in a shared L1/L2 account namespace some preinstalls may already have been inserted and used.
if (vm.getNonce(_addr) == 0) {
vm.setNonce(_addr, 1);
}
}
/// @notice Writes the genesis allocs, i.e. the state dump, to disk /// @notice Writes the genesis allocs, i.e. the state dump, to disk
function writeGenesisAllocs(string memory _path) public { function writeGenesisAllocs(string memory _path) public {
/// Reset so its not included state dump /// Reset so its not included state dump
......
// SPDX-License-Identifier: MIT
pragma solidity 0.8.15;
import { Script } from "forge-std/Script.sol";
import { console2 as console } from "forge-std/console2.sol";
import { Preinstalls } from "src/libraries/Preinstalls.sol";
/// @title SetPreinstalls
/// @notice Sets all preinstalls in the VM state. There is no default "run()" entrypoint,
/// as this is used in L2Genesis.s.sol, and standalone in the Go test setup for L1 state.
contract SetPreinstalls is Script {
/// @notice Sets all the preinstalls.
/// Warning: the creator-accounts of the preinstall contracts have 0 nonce values.
/// When performing a regular user-initiated contract-creation of a preinstall,
/// the creation will fail (but nonce will be bumped and not blocked).
/// The preinstalls themselves are all inserted with a nonce of 1, reflecting regular user execution.
function setPreinstalls() public {
_setPreinstallCode(Preinstalls.MultiCall3);
_setPreinstallCode(Preinstalls.Create2Deployer);
_setPreinstallCode(Preinstalls.Safe_v130);
_setPreinstallCode(Preinstalls.SafeL2_v130);
_setPreinstallCode(Preinstalls.MultiSendCallOnly_v130);
_setPreinstallCode(Preinstalls.SafeSingletonFactory);
_setPreinstallCode(Preinstalls.DeterministicDeploymentProxy);
_setPreinstallCode(Preinstalls.MultiSend_v130);
_setPreinstallCode(Preinstalls.Permit2);
_setPreinstallCode(Preinstalls.SenderCreator_v060); // ERC 4337 v0.6.0
_setPreinstallCode(Preinstalls.EntryPoint_v060); // ERC 4337 v0.6.0
_setPreinstallCode(Preinstalls.SenderCreator_v070); // ERC 4337 v0.7.0
_setPreinstallCode(Preinstalls.EntryPoint_v070); // ERC 4337 v0.7.0
_setPreinstallCode(Preinstalls.BeaconBlockRoots);
_setPreinstallCode(Preinstalls.CreateX);
// 4788 sender nonce must be incremented, since it's part of later upgrade-transactions.
// For the upgrade-tx to not create a contract that conflicts with an already-existing copy,
// the nonce must be bumped.
vm.setNonce(Preinstalls.BeaconBlockRootsSender, 1);
}
/// @notice Sets the bytecode in state
function _setPreinstallCode(address _addr) internal {
string memory cname = Preinstalls.getName(_addr);
console.log("Setting %s preinstall code at: %s", cname, _addr);
vm.etch(_addr, Preinstalls.getDeployedCode(_addr, block.chainid));
// during testing in a shared L1/L2 account namespace some preinstalls may already have been inserted and used.
if (vm.getNonce(_addr) == 0) {
vm.setNonce(_addr, 1);
}
}
}
...@@ -60,8 +60,8 @@ ...@@ -60,8 +60,8 @@
"sourceCodeHash": "0x06a50ac992175fdb434b13e8461893e83862c23ce399e697e6e8109728ad1a3d" "sourceCodeHash": "0x06a50ac992175fdb434b13e8461893e83862c23ce399e697e6e8109728ad1a3d"
}, },
"src/L1/SystemConfigInterop.sol": { "src/L1/SystemConfigInterop.sol": {
"initCodeHash": "0xc5a3ffc59dd7bf1ef238087414cfa04b37f0d83fc9a4f5e6d62a1059a23359f3", "initCodeHash": "0x1f500e310170769ffc747e08ad1d5b0de4b0f58534001bc4d4d563ec058bb331",
"sourceCodeHash": "0xd7b6ebf03ead541917b1bdfcf1293ca3c8a8e3865b8a8548bee69956a4ce71cc" "sourceCodeHash": "0xcb6008cb49a06f87eb5b6cb4651e5e4aafe0b1f33000eccd165226c04f6b63c6"
}, },
"src/L2/BaseFeeVault.sol": { "src/L2/BaseFeeVault.sol": {
"initCodeHash": "0x3bfcd57e25ad54b66c374f63e24e33a6cf107044aa8f5f69ef21202c380b5c5b", "initCodeHash": "0x3bfcd57e25ad54b66c374f63e24e33a6cf107044aa8f5f69ef21202c380b5c5b",
......
...@@ -52,8 +52,8 @@ contract SystemConfigInterop is SystemConfig { ...@@ -52,8 +52,8 @@ contract SystemConfigInterop is SystemConfig {
address _dependencyManager address _dependencyManager
) )
external external
initializer
{ {
// This method has an initializer modifier, and will revert if already initialized.
initialize({ initialize({
_owner: _owner, _owner: _owner,
_basefeeScalar: _basefeeScalar, _basefeeScalar: _basefeeScalar,
......
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