Commit 816e425c authored by Wyatt Barnes's avatar Wyatt Barnes Committed by GitHub

Init remote contract generation (#8281)

parent 54d5ab5b
...@@ -5,6 +5,8 @@ monorepo-base := $(shell dirname $(realpath .)) ...@@ -5,6 +5,8 @@ monorepo-base := $(shell dirname $(realpath .))
contracts-dir := $(monorepo-base)/packages/contracts-bedrock contracts-dir := $(monorepo-base)/packages/contracts-bedrock
contracts-list := ./artifacts.json contracts-list := ./artifacts.json
log-level := info log-level := info
ETHERSCAN_APIKEY_ETH ?=
ETHERSCAN_APIKEY_OP ?=
all: version mkdir bindings all: version mkdir bindings
...@@ -21,6 +23,21 @@ bindings: bindgen-local ...@@ -21,6 +23,21 @@ bindings: bindgen-local
bindings-build: bindgen-generate-local bindings-build: bindgen-generate-local
bindgen: compile bindgen-generate-all
bindgen-generate-all:
go run ./bindgen/ \
generate \
--metadata-out ./$(pkg) \
--bindings-package $(pkg) \
--contracts-list $(contracts-list) \
--log.level $(log-level) \
all \
--source-maps-list MIPS,PreimageOracle \
--forge-artifacts $(contracts-dir)/forge-artifacts \
--etherscan.apikey.eth $(ETHERSCAN_APIKEY_ETH) \
--etherscan.apikey.op $(ETHERSCAN_APIKEY_OP)
bindgen-local: compile bindgen-generate-local bindgen-local: compile bindgen-generate-local
bindgen-generate-local: bindgen-generate-local:
...@@ -34,6 +51,17 @@ bindgen-generate-local: ...@@ -34,6 +51,17 @@ bindgen-generate-local:
--source-maps-list MIPS,PreimageOracle \ --source-maps-list MIPS,PreimageOracle \
--forge-artifacts $(contracts-dir)/forge-artifacts --forge-artifacts $(contracts-dir)/forge-artifacts
bindgen-remote:
go run ./bindgen/ \
generate \
--metadata-out ./$(pkg) \
--bindings-package $(pkg) \
--contracts-list $(contracts-list) \
--log.level $(log-level) \
remote \
--etherscan.apikey.eth $(ETHERSCAN_APIKEY_ETH) \
--etherscan.apikey.op $(ETHERSCAN_APIKEY_OP)
mkdir: mkdir:
mkdir -p $(pkg) mkdir -p $(pkg)
......
...@@ -46,5 +46,104 @@ ...@@ -46,5 +46,104 @@
"ISemver", "ISemver",
"StorageSetter", "StorageSetter",
"SuperchainConfig" "SuperchainConfig"
],
"remote": [
{
"name": "MultiCall3",
"verified": true,
"deployments": {
"eth": "0xcA11bde05977b3631167028862bE2a173976CA11",
"op": "0xcA11bde05977b3631167028862bE2a173976CA11"
}
},
{
"name": "Create2Deployer",
"verified": true,
"deployments": {
"eth": "0xF49600926c7109BD66Ab97a2c036bf696e58Dbc2"
}
},
{
"name": "Safe_v130",
"verified": true,
"deployments": {
"eth": "0xd9Db270c1B5E3Bd161E8c8503c55cEABeE709552",
"op": "0x69f4D1788e39c87893C980c06EdF4b7f686e2938"
},
"deploymentSalt": "0000000000000000000000000000000000000000000000000000000000000000"
},
{
"name": "SafeL2_v130",
"verified": true,
"deployments": {
"eth": "0x3E5c63644E683549055b9Be8653de26E0B4CD36E",
"op": "0xfb1bffC9d739B8D520DaF37dF666da4C687191EA"
},
"deploymentSalt": "0000000000000000000000000000000000000000000000000000000000000000"
},
{
"name": "MultiSendCallOnly_v130",
"verified": true,
"deployments": {
"eth": "0x40A2aCCbd92BCA938b02010E17A5b8929b49130D",
"op": "0xA1dabEF33b3B82c7814B6D82A79e50F4AC44102B"
},
"deploymentSalt": "0000000000000000000000000000000000000000000000000000000000000000"
},
{
"name": "SafeSingletonFactory",
"verified": false,
"deployments": {
"eth": "0x914d7Fec6aaC8cd542e72Bca78B30650d45643d7",
"op": "0x914d7Fec6aaC8cd542e72Bca78B30650d45643d7"
},
"abi": "[{\"payable\":true,\"stateMutability\":\"payable\",\"type\":\"fallback\",\"inputs\":[{\"internalType\":\"bytes32\",\"name\":\"salt\",\"type\":\"bytes32\"},{\"internalType\":\"bytes32\",\"name\":\"creationCode\",\"type\":\"bytes\"}]}]"
},
{
"name": "DeterministicDeploymentProxy",
"verified": false,
"deployments": {
"eth": "0x4e59b44847b379578588920cA78FbF26c0B4956C",
"op": "0x4e59b44847b379578588920cA78FbF26c0B4956C"
},
"abi": "[{\"payable\":true,\"stateMutability\":\"payable\",\"type\":\"fallback\",\"inputs\":[{\"internalType\":\"bytes32\",\"name\":\"salt\",\"type\":\"bytes32\"},{\"internalType\":\"bytes32\",\"name\":\"creationCode\",\"type\":\"bytes\"}]}]"
},
{
"name": "MultiSend_v130",
"verified": true,
"deployments": {
"op": "0x998739BFdAAdde7C933B942a68053933098f9EDa"
},
"deploymentSalt": "0000000000000000000000000000000000000000000000000000000000000000"
},
{
"name": "Permit2",
"verified": true,
"deployments": {
"eth": "0x000000000022D473030F116dDEE9F6B43aC78BA3",
"op": "0x000000000022D473030F116dDEE9F6B43aC78BA3"
},
"deploymentSalt": "0000000000000000000000000000000000000000d3af2663da51c10215000000",
"deployer": "0x4e59b44847b379578588920cA78FbF26c0B4956C"
},
{
"name": "SenderCreator",
"verified": false,
"deployments": {
"eth": "0x7fc98430eaedbb6070b35b39d798725049088348",
"op": "0x7fc98430eaedbb6070b35b39d798725049088348"
},
"initBytecode": "0x6080806040523461001657610210908161001c8239f35b600080fdfe6080604052600436101561001257600080fd5b6000803560e01c63570e1a361461002857600080fd5b346100c95760207ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc3601126100c95760043567ffffffffffffffff918282116100c957366023830112156100c95781600401359283116100c95736602484840101116100c9576100c561009e84602485016100fc565b60405173ffffffffffffffffffffffffffffffffffffffff90911681529081906020820190565b0390f35b80fd5b507f4e487b7100000000000000000000000000000000000000000000000000000000600052604160045260246000fd5b90806014116101bb5767ffffffffffffffff917fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffec82018381116101cd575b604051937fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0603f81600b8701160116850190858210908211176101c0575b604052808452602084019036848401116101bb576020946000600c819682946014880187378301015251923560601c5af19060005191156101b557565b60009150565b600080fd5b6101c86100cc565b610178565b6101d56100cc565b61013a56fea26469706673582212201927e80b76ab9b71c952137dd676621a9fdf520c25928815636594036eb1c40364736f6c63430008110033",
"abi": "[{\"inputs\": [{\"internalType\": \"bytes\",\"name\": \"initCode\",\"type\": \"bytes\"}],\"name\": \"createSender\",\"outputs\": [{\"internalType\": \"address\",\"name\": \"sender\",\"type\": \"address\"}],\"stateMutability\": \"nonpayable\",\"type\": \"function\"}]"
},
{
"name": "EntryPoint",
"verified": true,
"deployments": {
"eth": "0x5FF137D4b0FDCD49DcA30c7CF57E578a026d2789",
"op": "0x5FF137D4b0FDCD49DcA30c7CF57E578a026d2789"
},
"deploymentSalt": "0000000000000000000000000000000000000000000000000000000000000000"
}
] ]
} }
package main
import (
"context"
"fmt"
"os"
"github.com/ethereum-optimism/optimism/op-bindings/etherscan"
"github.com/ethereum/go-ethereum/common"
)
type bindGenGeneratorRemote struct {
bindGenGeneratorBase
contractDataClients struct {
eth contractDataClient
op contractDataClient
}
tempArtifactsDir string
}
type contractDataClient interface {
FetchAbi(ctx context.Context, address string) (string, error)
FetchDeployedBytecode(ctx context.Context, address string) (string, error)
FetchDeploymentTxHash(ctx context.Context, address string) (string, error)
FetchDeploymentTx(ctx context.Context, txHash string) (etherscan.TxInfo, error)
}
type deployments struct {
Eth common.Address `json:"eth"`
Op common.Address `json:"op"`
}
type remoteContract struct {
Name string `json:"name"`
Verified bool `json:"verified"`
Deployments deployments `json:"deployments"`
DeploymentSalt string `json:"deploymentSalt"`
Deployer common.Address `json:"deployer"`
ABI string `json:"abi"`
InitBytecode string `json:"initBytecode"`
}
type remoteContractMetadata struct {
remoteContract
Package string
InitBin string
DeployedBin string
}
func (generator *bindGenGeneratorRemote) generateBindings() error {
contracts, err := readContractList(generator.logger, generator.contractsListPath)
if err != nil {
return fmt.Errorf("error reading contract list %s: %w", generator.contractsListPath, err)
}
if len(contracts.Remote) == 0 {
return fmt.Errorf("no contracts parsed from given contract list: %s", generator.contractsListPath)
}
return generator.processContracts(contracts.Remote)
}
func (generator *bindGenGeneratorRemote) processContracts(contracts []remoteContract) error {
var err error
generator.tempArtifactsDir, err = mkTempArtifactsDir(generator.logger)
if err != nil {
return err
}
defer func() {
err := os.RemoveAll(generator.tempArtifactsDir)
if err != nil {
generator.logger.Error("Error removing temporary artifact directory", "path", generator.tempArtifactsDir, "err", err.Error())
} else {
generator.logger.Debug("Successfully removed temporary artifact directory")
}
}()
for _, contract := range contracts {
generator.logger.Info("Generating bindings and metadata for remote contract", "contract", contract.Name)
contractMetadata := remoteContractMetadata{
remoteContract: remoteContract{
Name: contract.Name,
Deployments: contract.Deployments,
DeploymentSalt: contract.DeploymentSalt,
ABI: contract.ABI,
Verified: contract.Verified,
},
Package: generator.bindingsPackageName,
}
var err error
switch contract.Name {
case "MultiCall3", "Safe_v130", "SafeL2_v130", "MultiSendCallOnly_v130",
"EntryPoint", "SafeSingletonFactory", "DeterministicDeploymentProxy":
err = generator.standardHandler(&contractMetadata)
case "Create2Deployer":
err = generator.create2DeployerHandler(&contractMetadata)
case "MultiSend_v130":
err = generator.multiSendHandler(&contractMetadata)
case "SenderCreator":
// The SenderCreator contract is deployed by EntryPoint, so the transaction data
// from the deployment transaction is for the entire EntryPoint deployment.
// So, we're manually providing the initialization bytecode
contractMetadata.InitBin = contract.InitBytecode
err = generator.senderCreatorHandler(&contractMetadata)
case "Permit2":
// Permit2 has an immutable Solidity variable that resolves to block.chainid,
// so we can't use the deployed bytecode, and instead must generate it
// at some later point not handled by BindGen.
// DeployerAddress is intended to be used to help deploy Permit2 at it's deterministic address
// to a chain set with the required id to be able to obtain a diff minimized deployed bytecode
contractMetadata.Deployer = contract.Deployer
err = generator.permit2Handler(&contractMetadata)
default:
err = fmt.Errorf("unknown contract: %s, don't know how to handle it", contract.Name)
}
if err != nil {
return err
}
}
return nil
}
...@@ -4,6 +4,7 @@ import ( ...@@ -4,6 +4,7 @@ import (
"fmt" "fmt"
"os" "os"
"github.com/ethereum-optimism/optimism/op-bindings/etherscan"
"github.com/ethereum-optimism/optimism/op-e2e/config" "github.com/ethereum-optimism/optimism/op-e2e/config"
oplog "github.com/ethereum-optimism/optimism/op-service/log" oplog "github.com/ethereum-optimism/optimism/op-service/log"
"github.com/ethereum/go-ethereum/log" "github.com/ethereum/go-ethereum/log"
...@@ -27,6 +28,12 @@ const ( ...@@ -27,6 +28,12 @@ const (
// Local Contracts Flags // Local Contracts Flags
SourceMapsListFlagName = "source-maps-list" SourceMapsListFlagName = "source-maps-list"
ForgeArtifactsFlagName = "forge-artifacts" ForgeArtifactsFlagName = "forge-artifacts"
// Remote Contracts Flags
EtherscanApiKeyEthFlagName = "etherscan.apikey.eth"
EtherscanApiKeyOpFlagName = "etherscan.apikey.op"
RpcUrlEthFlagName = "rpc.url.eth"
RpcUrlOpFlagName = "rpc.url.op"
) )
func main() { func main() {
...@@ -41,12 +48,24 @@ func main() { ...@@ -41,12 +48,24 @@ func main() {
Usage: "Generate contract bindings", Usage: "Generate contract bindings",
Flags: baseFlags(), Flags: baseFlags(),
Subcommands: []*cli.Command{ Subcommands: []*cli.Command{
{
Name: "all",
Usage: "Generate bindings for local and remote contracts",
Flags: append(localFlags(), remoteFlags()...),
Action: generateBindings,
},
{ {
Name: "local", Name: "local",
Usage: "Generate bindings for locally sourced contracts", Usage: "Generate bindings for locally sourced contracts",
Flags: localFlags(), Flags: localFlags(),
Action: generateBindings, Action: generateBindings,
}, },
{
Name: "remote",
Usage: "Generate bindings for remotely sourced contracts",
Flags: remoteFlags(),
Action: generateBindings,
},
}, },
}, },
}, },
...@@ -67,6 +86,24 @@ func generateBindings(c *cli.Context) error { ...@@ -67,6 +86,24 @@ func generateBindings(c *cli.Context) error {
logger := setupLogger(c) logger := setupLogger(c)
switch c.Command.Name { switch c.Command.Name {
case "all":
localBindingsGenerator, err := parseConfigLocal(logger, c)
if err != nil {
return err
}
if err := localBindingsGenerator.generateBindings(); err != nil {
return fmt.Errorf("error generating local bindings: %w", err)
}
remoteBindingsGenerator, err := parseConfigRemote(logger, c)
if err != nil {
return err
}
if err := remoteBindingsGenerator.generateBindings(); err != nil {
return fmt.Errorf("error generating remote bindings: %w", err)
}
return nil
case "local": case "local":
localBindingsGenerator, err := parseConfigLocal(logger, c) localBindingsGenerator, err := parseConfigLocal(logger, c)
if err != nil { if err != nil {
...@@ -76,6 +113,15 @@ func generateBindings(c *cli.Context) error { ...@@ -76,6 +113,15 @@ func generateBindings(c *cli.Context) error {
return fmt.Errorf("error generating local bindings: %w", err) return fmt.Errorf("error generating local bindings: %w", err)
} }
return nil return nil
case "remote":
remoteBindingsGenerator, err := parseConfigRemote(logger, c)
if err != nil {
return err
}
if err := remoteBindingsGenerator.generateBindings(); err != nil {
return fmt.Errorf("error generating remote bindings: %w", err)
}
return nil
default: default:
return fmt.Errorf("unknown command: %s", c.Command.Name) return fmt.Errorf("unknown command: %s", c.Command.Name)
} }
...@@ -113,6 +159,20 @@ func parseConfigLocal(logger log.Logger, c *cli.Context) (bindGenGeneratorLocal, ...@@ -113,6 +159,20 @@ func parseConfigLocal(logger log.Logger, c *cli.Context) (bindGenGeneratorLocal,
}, nil }, nil
} }
func parseConfigRemote(logger log.Logger, c *cli.Context) (bindGenGeneratorRemote, error) {
baseConfig, err := parseConfigBase(logger, c)
if err != nil {
return bindGenGeneratorRemote{}, err
}
generator := bindGenGeneratorRemote{
bindGenGeneratorBase: baseConfig,
}
generator.contractDataClients.eth = etherscan.NewEthereumClient(c.String(EtherscanApiKeyEthFlagName))
generator.contractDataClients.op = etherscan.NewOptimismClient(c.String(EtherscanApiKeyOpFlagName))
return generator, nil
}
func baseFlags() []cli.Flag { func baseFlags() []cli.Flag {
baseFlags := []cli.Flag{ baseFlags := []cli.Flag{
&cli.StringFlag{ &cli.StringFlag{
...@@ -148,3 +208,18 @@ func localFlags() []cli.Flag { ...@@ -148,3 +208,18 @@ func localFlags() []cli.Flag {
}, },
} }
} }
func remoteFlags() []cli.Flag {
return []cli.Flag{
&cli.StringFlag{
Name: EtherscanApiKeyEthFlagName,
Usage: "API key to make queries to Etherscan for Ethereum",
Required: true,
},
&cli.StringFlag{
Name: EtherscanApiKeyOpFlagName,
Usage: "API key to make queries to Etherscan for Optimism",
Required: true,
},
}
}
This diff is collapsed.
...@@ -12,7 +12,8 @@ import ( ...@@ -12,7 +12,8 @@ import (
) )
type contractsList struct { type contractsList struct {
Local []string `json:"local"` Local []string `json:"local"`
Remote []remoteContract `json:"remote"`
} }
// readContractList reads a JSON file from the given `filePath` and unmarshals // readContractList reads a JSON file from the given `filePath` and unmarshals
......
...@@ -120,10 +120,7 @@ func (c *client) fetchEtherscanRpc(ctx context.Context, url string) (rpcResponse ...@@ -120,10 +120,7 @@ func (c *client) fetchEtherscanRpc(ctx context.Context, url string) (rpcResponse
} }
var resultString string var resultString string
err = json.Unmarshal(response.Result, &resultString) _ = json.Unmarshal(response.Result, &resultString)
if err != nil {
return rpcResponse{}, fmt.Errorf("failed to unmarshal response.Result as a string: %w", err)
}
if resultString == errRateLimited { if resultString == errRateLimited {
return rpcResponse{}, errors.New(errRateLimited) return rpcResponse{}, errors.New(errRateLimited)
} }
......
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