Skip to content

Commit

Permalink
Init remote contract generation
Browse files Browse the repository at this point in the history
  • Loading branch information
spacesailor24 committed Dec 14, 2023
1 parent 1544353 commit 68a0655
Show file tree
Hide file tree
Showing 6 changed files with 632 additions and 1 deletion.
28 changes: 28 additions & 0 deletions op-bindings/Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,8 @@ monorepo-base := $(shell dirname $(realpath .))
contracts-dir := $(monorepo-base)/packages/contracts-bedrock
contracts-list := ./artifacts.json
log-level := info
ETHERSCAN_APIKEY_ETH ?=
ETHERSCAN_APIKEY_OP ?=

all: version mkdir bindings

Expand All @@ -21,6 +23,21 @@ bindings: bindgen-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-generate-local:
Expand All @@ -34,6 +51,17 @@ bindgen-generate-local:
--source-maps-list MIPS,PreimageOracle \
--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 -p $(pkg)

Expand Down
99 changes: 99 additions & 0 deletions op-bindings/artifacts.json
Original file line number Diff line number Diff line change
Expand Up @@ -46,5 +46,104 @@
"ISemver",
"StorageSetter",
"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",
"verified": true,
"deployments": {
"eth": "0x3E5c63644E683549055b9Be8653de26E0B4CD36E",
"op": "0xfb1bffC9d739B8D520DaF37dF666da4C687191EA"
},
"deploymentSalt": "0000000000000000000000000000000000000000000000000000000000000000"
},
{
"name": "MultiSendCallOnly",
"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",
"verified": true,
"deployments": {
"op": "0x998739BFdAAdde7C933B942a68053933098f9EDa"
},
"deploymentSalt": "0000000000000000000000000000000000000000000000000000000000000000"
},
{
"name": "Permit2",
"verified": true,
"deployments": {
"eth": "0x000000000022D473030F116dDEE9F6B43aC78BA3",
"op": "0x000000000022D473030F116dDEE9F6B43aC78BA3"
},
"deploymentSalt": "0000000000000000000000000000000000000000d3af2663da51c10215000000",
"deployerAddress": "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"
}
]
}
118 changes: 118 additions & 0 deletions op-bindings/bindgen/generator_remote.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,118 @@
package main

import (
"context"
"fmt"
"os"

"github.com/ethereum-optimism/optimism/op-bindings/etherscan"
)

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 remoteContract struct {
Name string `json:"name"`
Verified bool `json:"verified"`
Deployments map[string]string `json:"deployments"`
DeploymentSalt string `json:"deploymentSalt"`
DeployerAddress string `json:"deployerAddress"`
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", "MultiSendCallOnly",
"EntryPoint", "SafeSingletonFactory", "DeterministicDeploymentProxy":
err = generator.standardHandler(&contractMetadata)
case "Create2Deployer":
err = generator.create2DeployerHandler(&contractMetadata)
case "MultiSend":
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.DeployerAddress = contract.DeployerAddress
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
}
75 changes: 75 additions & 0 deletions op-bindings/bindgen/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import (
"fmt"
"os"

"github.com/ethereum-optimism/optimism/op-bindings/etherscan"
"github.com/ethereum-optimism/optimism/op-e2e/config"
oplog "github.com/ethereum-optimism/optimism/op-service/log"
"github.com/ethereum/go-ethereum/log"
Expand All @@ -27,6 +28,12 @@ const (
// Local Contracts Flags
SourceMapsListFlagName = "source-maps-list"
ForgeArtifactsFlagName = "forge-artifacts"

// Remote Contracts Flags
EtherscanApiKeyEthFlagName = "etherscan.apikey.eth"
EtherscanApiKeyOpFlagName = "etherscan.apikey.op"
RpcUrlEthFlagName = "rpc.url.eth"
RpcUrlOpFlagName = "rpc.url.op"
)

func main() {
Expand All @@ -41,12 +48,24 @@ func main() {
Usage: "Generate contract bindings",
Flags: baseFlags(),
Subcommands: []*cli.Command{
{
Name: "all",
Usage: "Generate bindings for local and remote contracts",
Flags: append(localFlags(), remoteFlags()...),
Action: generateBindings,
},
{
Name: "local",
Usage: "Generate bindings for locally sourced contracts",
Flags: localFlags(),
Action: generateBindings,
},
{
Name: "remote",
Usage: "Generate bindings for remotely sourced contracts",
Flags: remoteFlags(),
Action: generateBindings,
},
},
},
},
Expand All @@ -67,6 +86,24 @@ func generateBindings(c *cli.Context) error {
logger := setupLogger(c)

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":
localBindingsGenerator, err := parseConfigLocal(logger, c)
if err != nil {
Expand All @@ -76,6 +113,15 @@ func generateBindings(c *cli.Context) error {
return fmt.Errorf("error generating local bindings: %w", err)
}
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:
return fmt.Errorf("unknown command: %s", c.Command.Name)
}
Expand Down Expand Up @@ -113,6 +159,20 @@ func parseConfigLocal(logger log.Logger, c *cli.Context) (bindGenGeneratorLocal,
}, 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 {
baseFlags := []cli.Flag{
&cli.StringFlag{
Expand Down Expand Up @@ -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,
},
}
}
Loading

0 comments on commit 68a0655

Please sign in to comment.