Commit 5f97330d authored by Mark Tyneway's avatar Mark Tyneway Committed by Andreas Bigger

op-upgrade: cleanup

parent 3649f327
...@@ -20,11 +20,16 @@ type Clients struct { ...@@ -20,11 +20,16 @@ type Clients struct {
L2GethClient *gethclient.Client L2GethClient *gethclient.Client
} }
// NewClients will create new RPC clients from a CLI context // NewClientsFromContext will create new RPC clients from a CLI context
func NewClients(ctx *cli.Context) (*Clients, error) { func NewClientsFromContext(ctx *cli.Context) (*Clients, error) {
return NewClients(ctx.String("l1-rpc-url"), ctx.String("l2-rpc-url"))
}
// NewClients will create new RPC clients from given URLs.
func NewClients(l1RpcURL, l2RpcURL string) (*Clients, error) {
clients := Clients{} clients := Clients{}
if l1RpcURL := ctx.String("l1-rpc-url"); l1RpcURL != "" { if l1RpcURL != "" {
l1Client, l1RpcClient, l1GethClient, err := newClients(l1RpcURL) l1Client, l1RpcClient, l1GethClient, err := newClients(l1RpcURL)
if err != nil { if err != nil {
return nil, err return nil, err
...@@ -34,7 +39,7 @@ func NewClients(ctx *cli.Context) (*Clients, error) { ...@@ -34,7 +39,7 @@ func NewClients(ctx *cli.Context) (*Clients, error) {
clients.L1GethClient = l1GethClient clients.L1GethClient = l1GethClient
} }
if l2RpcURL := ctx.String("l2-rpc-url"); l2RpcURL != "" { if l2RpcURL != "" {
l2Client, l2RpcClient, l2GethClient, err := newClients(l2RpcURL) l2Client, l2RpcClient, l2GethClient, err := newClients(l2RpcURL)
if err != nil { if err != nil {
return nil, err return nil, err
......
# op-upgrade
A CLI tool for building Safe bundles that can upgrade many chains
at the same time. It will output a JSON file that is compatible with
the Safe UI. It is assumed that the implementations that are being
upgraded to have already been deployed and their contract addresses
exist inside of the `superchain-registry` repository. It is also
assumed that the semantic version file in the `superchain-registry`
has been updated. The tool will use semantic versioning to determine
which contract versions should be upgraded to and then build all of
the calldata.
### Configuration
#### L1 RPC URL
The L1 RPC URL is used to determine which superchain to target. All
L2s that are not based on top of the L1 chain that corresponds to the
L1 RPC URL are filtered out from being included. It also is used to
double check that the data in the `superchain-registry` is correct.
#### Chain IDs
A list of L2 chain IDs can be passed that will be used to filter which
L2 chains will have upgrades included in the bundle transaction. Omitting
this argument will result in all chains in the superchain being considered.
#### Deploy Config
The path to the `deploy-config` directory in the contracts package.
Since multiple L2 networks may be included in the bundle, the `deploy-config`
directory must be passed and then the particular deploy config files will
be read out of the directory as needed.
#### Outfile
The file that the bundle shoudl be written to. If omitted, the file
will be written to stdout.
...@@ -3,8 +3,12 @@ package main ...@@ -3,8 +3,12 @@ package main
import ( import (
"encoding/json" "encoding/json"
"fmt" "fmt"
"golang.org/x/exp/maps"
"os" "os"
"slices"
//"strings"
"github.com/ethereum/go-ethereum/ethclient"
"github.com/ethereum/go-ethereum/log" "github.com/ethereum/go-ethereum/log"
"github.com/mattn/go-isatty" "github.com/mattn/go-isatty"
...@@ -28,23 +32,22 @@ func main() { ...@@ -28,23 +32,22 @@ func main() {
&cli.StringFlag{ &cli.StringFlag{
Name: "l1-rpc-url", Name: "l1-rpc-url",
Value: "http://127.0.0.1:8545", Value: "http://127.0.0.1:8545",
Usage: "L1 RPC URL", Usage: "L1 RPC URL, the chain ID will be used to determine the superchain",
EnvVars: []string{"L1_RPC_URL"}, EnvVars: []string{"L1_RPC_URL"},
}, },
&cli.StringFlag{ &cli.Uint64SliceFlag{
Name: "l2-rpc-url", Name: "chain-ids",
Value: "http://127.0.0.1:9545", Usage: "L2 Chain IDs corresponding to chains to upgrade. Corresponds to all chains if empty",
Usage: "L2 RPC URL",
EnvVars: []string{"L2_RPC_URL"},
}, },
&cli.PathFlag{ &cli.PathFlag{
Name: "deploy-config", Name: "deploy-config",
Usage: "The path to the deploy config file",
Required: true, Required: true,
EnvVars: []string{"DEPLOY_CONFIG"}, EnvVars: []string{"DEPLOY_CONFIG"},
}, },
&cli.PathFlag{ &cli.PathFlag{
Name: "outfile", Name: "outfile",
Usage: "", Usage: "The file to write the output to. If not specified, output is written to stdout",
EnvVars: []string{"OUTFILE"}, EnvVars: []string{"OUTFILE"},
}, },
}, },
...@@ -56,70 +59,119 @@ func main() { ...@@ -56,70 +59,119 @@ func main() {
} }
} }
// entrypoint contains the main logic of the script
func entrypoint(ctx *cli.Context) error { func entrypoint(ctx *cli.Context) error {
config, err := genesis.NewDeployConfig(ctx.Path("deploy-config")) client, err := ethclient.Dial(ctx.String("l1-rpc-url"))
if err != nil {
return err
}
clients, err := clients.NewClients(ctx)
if err != nil { if err != nil {
return err return err
} }
l1ChainID, err := clients.L1Client.ChainID(ctx.Context) // Fetch the L1 chain ID to determine the superchain name
l1ChainID, err := client.ChainID(ctx.Context)
if err != nil { if err != nil {
return err return err
} }
l2ChainID, err := clients.L2Client.ChainID(ctx.Context) superchainName, err := toSuperchainName(l1ChainID.Uint64())
if err != nil { if err != nil {
return err return err
} }
log.Info("Chain IDs", "l1", l1ChainID, "l2", l2ChainID)
chainConfig, ok := superchain.OPChains[l2ChainID.Uint64()] chainIDs := ctx.Uint64Slice("chain-ids")
if !ok { deployConfig := ctx.Path("deploy-config")
return fmt.Errorf("no chain config for chain ID %d", l2ChainID.Uint64())
}
// Tracking the individual addresses can be deprecated once the system is upgraded // If no chain IDs are specified, upgrade all chains
// to the new contracts where the system config has a reference to each address. if len(chainIDs) == 0 {
addresses, ok := superchain.Addresses[l2ChainID.Uint64()] chainIDs = maps.Keys(superchain.OPChains)
if !ok {
return fmt.Errorf("no addresses for chain ID %d", l2ChainID.Uint64())
}
versions, err := upgrades.GetContractVersions(ctx.Context, addresses, chainConfig, clients.L1Client)
if err != nil {
return fmt.Errorf("error getting contract versions: %w", err)
} }
slices.Sort(chainIDs)
log.Info( targets := make([]*superchain.ChainConfig, 0)
"Current Versions", for _, chainConfig := range superchain.OPChains {
"L1CrossDomainMessenger", versions.L1CrossDomainMessenger, if chainConfig.Superchain == superchainName && slices.Contains(chainIDs, chainConfig.ChainID) {
"L1ERC721Bridge", versions.L1ERC721Bridge, targets = append(targets, chainConfig)
"L1StandardBridge", versions.L1StandardBridge, }
"L2OutputOracle", versions.L2OutputOracle,
"OptimismMintableERC20Factory", versions.OptimismMintableERC20Factory,
"OptimismPortal", versions.OptimismPortal,
"SystemConfig", versions.SystemConfig,
)
implementations, ok := superchain.Implementations[l1ChainID.Uint64()]
if !ok {
return fmt.Errorf("no implementations for chain ID %d", l1ChainID.Uint64())
} }
list, err := implementations.Resolve(superchain.SuperchainSemver) slices.SortFunc(targets, func(i, j *superchain.ChainConfig) int {
if err != nil { return int(i.ChainID) - int(j.ChainID)
return err })
}
if err := upgrades.CheckL1(ctx.Context, &list, clients.L1Client); err != nil {
return fmt.Errorf("error checking L1: %w", err)
}
// Create a batch of transactions
batch := safe.Batch{} batch := safe.Batch{}
if err := upgrades.L1(&batch, list, *addresses, config, chainConfig); err != nil {
return err for _, chainConfig := range targets {
name, err := toDeployConfigName(chainConfig)
if err != nil {
log.Warn("Skipping unsupported network", "name", chainConfig.Name)
continue
}
config, err := genesis.NewDeployConfigWithNetwork(name, deployConfig)
if err != nil {
return err
}
clients, err := clients.NewClients(ctx.String("l1-rpc-url"), chainConfig.PublicRPC)
if err != nil {
return err
}
l1ChainID, err := clients.L1Client.ChainID(ctx.Context)
if err != nil {
return err
}
l2ChainID, err := clients.L2Client.ChainID(ctx.Context)
if err != nil {
return err
}
log.Info(chainConfig.Name, "l1-chain-id", l1ChainID, "l2-chain-id", l2ChainID)
log.Info("Detecting on chain contracts")
// Tracking the individual addresses can be deprecated once the system is upgraded
// to the new contracts where the system config has a reference to each address.
addresses, ok := superchain.Addresses[l2ChainID.Uint64()]
if !ok {
return fmt.Errorf("no addresses for chain ID %d", l2ChainID.Uint64())
}
versions, err := upgrades.GetContractVersions(ctx.Context, addresses, chainConfig, clients.L1Client)
if err != nil {
return fmt.Errorf("error getting contract versions: %w", err)
}
log.Info("L1CrossDomainMessenger", "version", versions.L1CrossDomainMessenger, "address", addresses.L1CrossDomainMessengerProxy)
log.Info("L1ERC721Bridge", "version", versions.L1ERC721Bridge, "address", addresses.L1ERC721BridgeProxy)
log.Info("L1StandardBridge", "version", versions.L1StandardBridge, "address", addresses.L1StandardBridgeProxy)
log.Info("L2OutputOracle", "version", versions.L2OutputOracle, "address", addresses.L2OutputOracleProxy)
log.Info("OptimismMintableERC20Factory", "version", versions.OptimismMintableERC20Factory, "address", addresses.OptimismMintableERC20FactoryProxy)
log.Info("OptimismPortal", "version", versions.OptimismPortal, "address", addresses.OptimismPortalProxy)
log.Info("SystemConfig", "version", versions.SystemConfig, "address", chainConfig.SystemConfigAddr)
implementations, ok := superchain.Implementations[l1ChainID.Uint64()]
if !ok {
return fmt.Errorf("no implementations for chain ID %d", l1ChainID.Uint64())
}
list, err := implementations.Resolve(superchain.SuperchainSemver)
if err != nil {
return err
}
log.Info("Upgrading to the following versions")
log.Info("L1CrossDomainMessenger", "version", list.L1CrossDomainMessenger.Version, "address", list.L1CrossDomainMessenger.Address)
log.Info("L1ERC721Bridge", "version", list.L1ERC721Bridge.Version, "address", list.L1ERC721Bridge.Address)
log.Info("L1StandardBridge", "version", list.L1StandardBridge.Version, "address", list.L1StandardBridge.Address)
log.Info("L2OutputOracle", "version", list.L2OutputOracle.Version, "address", list.L2OutputOracle.Address)
log.Info("OptimismMintableERC20Factory", "version", list.OptimismMintableERC20Factory.Version, "address", list.OptimismMintableERC20Factory.Address)
log.Info("OptimismPortal", "version", list.OptimismPortal.Version, "address", list.OptimismPortal.Address)
log.Info("SystemConfig", "version", list.SystemConfig.Version, "address", list.SystemConfig.Address)
if err := upgrades.CheckL1(ctx.Context, &list, clients.L1Client); err != nil {
return fmt.Errorf("error checking L1: %w", err)
}
if err := upgrades.L1(&batch, list, *addresses, config, chainConfig); err != nil {
return err
}
} }
if outfile := ctx.Path("outfile"); outfile != "" { if outfile := ctx.Path("outfile"); outfile != "" {
...@@ -131,12 +183,49 @@ func entrypoint(ctx *cli.Context) error { ...@@ -131,12 +183,49 @@ func entrypoint(ctx *cli.Context) error {
if err != nil { if err != nil {
return err return err
} }
fmt.Println(data) fmt.Println(string(data))
} }
return nil return nil
} }
// toDeployConfigName is a temporary function that maps the chain config names
// to deploy config names. This should be able to be removed in the future
// with a canonical naming scheme. If an empty string is returned, then
// it means that the chain is not supported yet.
func toDeployConfigName(cfg *superchain.ChainConfig) (string, error) {
if cfg.Name == "OP-Sepolia" {
return "sepolia", nil
}
if cfg.Name == "OP-Goerli" {
return "goerli", nil
}
if cfg.Name == "PGN" {
return "pgn", nil
}
if cfg.Name == "Zora" {
return "zora", nil
}
if cfg.Name == "OP-Mainnet" {
return "mainnet", nil
}
return "", fmt.Errorf("unsupported chain name %s", cfg.Name)
}
// toSuperchainName turns a base layer chain id into a superchain
// network name.
func toSuperchainName(chainID uint64) (string, error) {
if chainID == 1 {
return "mainnet", nil
}
if chainID == 5 {
return "goerli", nil
}
if chainID == 11155111 {
return "sepolia", nil
}
return "", fmt.Errorf("unsupported chain ID %d", chainID)
}
func writeJSON(outfile string, input interface{}) error { func writeJSON(outfile string, input interface{}) error {
f, err := os.OpenFile(outfile, os.O_WRONLY|os.O_CREATE|os.O_TRUNC, 0o755) f, err := os.OpenFile(outfile, os.O_WRONLY|os.O_CREATE|os.O_TRUNC, 0o755)
if err != nil { if err != nil {
......
...@@ -17,6 +17,7 @@ import ( ...@@ -17,6 +17,7 @@ import (
"github.com/ethereum-optimism/superchain-registry/superchain" "github.com/ethereum-optimism/superchain-registry/superchain"
) )
// L1 will add calls for upgrading each of the L1 contracts.
func L1(batch *safe.Batch, implementations superchain.ImplementationList, list superchain.AddressList, config *genesis.DeployConfig, chainConfig *superchain.ChainConfig) error { func L1(batch *safe.Batch, implementations superchain.ImplementationList, list superchain.AddressList, config *genesis.DeployConfig, chainConfig *superchain.ChainConfig) error {
if err := L1CrossDomainMessenger(batch, implementations, list, config, chainConfig); err != nil { if err := L1CrossDomainMessenger(batch, implementations, list, config, chainConfig); err != nil {
return err return err
...@@ -42,6 +43,7 @@ func L1(batch *safe.Batch, implementations superchain.ImplementationList, list s ...@@ -42,6 +43,7 @@ func L1(batch *safe.Batch, implementations superchain.ImplementationList, list s
return nil return nil
} }
// L1CrossDomainMessenger will add a call to the batch that upgrades the L1CrossDomainMessenger.
func L1CrossDomainMessenger(batch *safe.Batch, implementations superchain.ImplementationList, list superchain.AddressList, config *genesis.DeployConfig, chainConfig *superchain.ChainConfig) error { func L1CrossDomainMessenger(batch *safe.Batch, implementations superchain.ImplementationList, list superchain.AddressList, config *genesis.DeployConfig, chainConfig *superchain.ChainConfig) error {
proxyAdminABI, err := bindings.ProxyAdminMetaData.GetAbi() proxyAdminABI, err := bindings.ProxyAdminMetaData.GetAbi()
if err != nil { if err != nil {
...@@ -80,6 +82,7 @@ func L1CrossDomainMessenger(batch *safe.Batch, implementations superchain.Implem ...@@ -80,6 +82,7 @@ func L1CrossDomainMessenger(batch *safe.Batch, implementations superchain.Implem
return nil return nil
} }
// L1ERC721Bridge will add a call to the batch that upgrades the L1ERC721Bridge.
func L1ERC721Bridge(batch *safe.Batch, implementations superchain.ImplementationList, list superchain.AddressList, config *genesis.DeployConfig, chainConfig *superchain.ChainConfig) error { func L1ERC721Bridge(batch *safe.Batch, implementations superchain.ImplementationList, list superchain.AddressList, config *genesis.DeployConfig, chainConfig *superchain.ChainConfig) error {
proxyAdminABI, err := bindings.ProxyAdminMetaData.GetAbi() proxyAdminABI, err := bindings.ProxyAdminMetaData.GetAbi()
if err != nil { if err != nil {
...@@ -118,6 +121,7 @@ func L1ERC721Bridge(batch *safe.Batch, implementations superchain.Implementation ...@@ -118,6 +121,7 @@ func L1ERC721Bridge(batch *safe.Batch, implementations superchain.Implementation
return nil return nil
} }
// L1StandardBridge will add a call to the batch that upgrades the L1StandardBridge.
func L1StandardBridge(batch *safe.Batch, implementations superchain.ImplementationList, list superchain.AddressList, config *genesis.DeployConfig, chainConfig *superchain.ChainConfig) error { func L1StandardBridge(batch *safe.Batch, implementations superchain.ImplementationList, list superchain.AddressList, config *genesis.DeployConfig, chainConfig *superchain.ChainConfig) error {
proxyAdminABI, err := bindings.ProxyAdminMetaData.GetAbi() proxyAdminABI, err := bindings.ProxyAdminMetaData.GetAbi()
if err != nil { if err != nil {
...@@ -156,6 +160,7 @@ func L1StandardBridge(batch *safe.Batch, implementations superchain.Implementati ...@@ -156,6 +160,7 @@ func L1StandardBridge(batch *safe.Batch, implementations superchain.Implementati
return nil return nil
} }
// L2OutputOracle will add a call to the batch that upgrades the L2OutputOracle.
func L2OutputOracle(batch *safe.Batch, implementations superchain.ImplementationList, list superchain.AddressList, config *genesis.DeployConfig, chainConfig *superchain.ChainConfig) error { func L2OutputOracle(batch *safe.Batch, implementations superchain.ImplementationList, list superchain.AddressList, config *genesis.DeployConfig, chainConfig *superchain.ChainConfig) error {
proxyAdminABI, err := bindings.ProxyAdminMetaData.GetAbi() proxyAdminABI, err := bindings.ProxyAdminMetaData.GetAbi()
if err != nil { if err != nil {
...@@ -203,6 +208,7 @@ func L2OutputOracle(batch *safe.Batch, implementations superchain.Implementation ...@@ -203,6 +208,7 @@ func L2OutputOracle(batch *safe.Batch, implementations superchain.Implementation
return nil return nil
} }
// OptimismMintableERC20Factory will add a call to the batch that upgrades the OptimismMintableERC20Factory.
func OptimismMintableERC20Factory(batch *safe.Batch, implementations superchain.ImplementationList, list superchain.AddressList, config *genesis.DeployConfig, chainConfig *superchain.ChainConfig) error { func OptimismMintableERC20Factory(batch *safe.Batch, implementations superchain.ImplementationList, list superchain.AddressList, config *genesis.DeployConfig, chainConfig *superchain.ChainConfig) error {
proxyAdminABI, err := bindings.ProxyAdminMetaData.GetAbi() proxyAdminABI, err := bindings.ProxyAdminMetaData.GetAbi()
if err != nil { if err != nil {
...@@ -241,6 +247,7 @@ func OptimismMintableERC20Factory(batch *safe.Batch, implementations superchain. ...@@ -241,6 +247,7 @@ func OptimismMintableERC20Factory(batch *safe.Batch, implementations superchain.
return nil return nil
} }
// OptimismPortal will add a call to the batch that upgrades the OptimismPortal.
func OptimismPortal(batch *safe.Batch, implementations superchain.ImplementationList, list superchain.AddressList, config *genesis.DeployConfig, chainConfig *superchain.ChainConfig) error { func OptimismPortal(batch *safe.Batch, implementations superchain.ImplementationList, list superchain.AddressList, config *genesis.DeployConfig, chainConfig *superchain.ChainConfig) error {
proxyAdminABI, err := bindings.ProxyAdminMetaData.GetAbi() proxyAdminABI, err := bindings.ProxyAdminMetaData.GetAbi()
if err != nil { if err != nil {
...@@ -282,6 +289,7 @@ func OptimismPortal(batch *safe.Batch, implementations superchain.Implementation ...@@ -282,6 +289,7 @@ func OptimismPortal(batch *safe.Batch, implementations superchain.Implementation
return nil return nil
} }
// SystemConfig will add a call to the batch that upgrades the SystemConfig.
func SystemConfig(batch *safe.Batch, implementations superchain.ImplementationList, list superchain.AddressList, config *genesis.DeployConfig, chainConfig *superchain.ChainConfig) error { func SystemConfig(batch *safe.Batch, implementations superchain.ImplementationList, list superchain.AddressList, config *genesis.DeployConfig, chainConfig *superchain.ChainConfig) error {
proxyAdminABI, err := bindings.ProxyAdminMetaData.GetAbi() proxyAdminABI, err := bindings.ProxyAdminMetaData.GetAbi()
if err != nil { if err != nil {
......
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