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 .))
contracts-dir := $(monorepo-base)/packages/contracts-bedrock
contracts-list := ./artifacts.json
log-level := info
ETHERSCAN_APIKEY_ETH ?=
ETHERSCAN_APIKEY_OP ?=
all: version mkdir bindings
......@@ -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:
......@@ -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)
......
......@@ -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_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 (
"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"
......@@ -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() {
......@@ -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,
},
},
},
},
......@@ -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 {
......@@ -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)
}
......@@ -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{
......@@ -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,
},
}
}
package main
import (
"context"
"fmt"
"os"
"path/filepath"
"regexp"
"strings"
"text/template"
"github.com/ethereum-optimism/optimism/op-bindings/etherscan"
)
type contractData struct {
abi string
deployedBin string
deploymentTx etherscan.TxInfo
}
func (generator *bindGenGeneratorRemote) standardHandler(contractMetadata *remoteContractMetadata) error {
fetchedData, err := generator.fetchContractData(contractMetadata.Verified, "eth", contractMetadata.Deployments.Eth.Hex())
if err != nil {
return err
}
contractMetadata.DeployedBin = fetchedData.deployedBin
// If ABI was explicitly provided by config, don't overwrite
if contractMetadata.ABI == "" {
contractMetadata.ABI = fetchedData.abi
} else if fetchedData.abi != "" && contractMetadata.ABI != fetchedData.abi {
generator.logger.Debug("ABIs", "given", contractMetadata.ABI, "fetched", fetchedData.abi)
return fmt.Errorf("the given ABI for %s differs from what was fetched from Etherscan", contractMetadata.Name)
}
if contractMetadata.InitBin, err = generator.removeDeploymentSalt(fetchedData.deploymentTx.Input, contractMetadata.DeploymentSalt); err != nil {
return err
}
if err := generator.compareBytecodeWithOp(contractMetadata, true, true); err != nil {
return fmt.Errorf("error comparing contract bytecode for %s: %w", contractMetadata.Name, err)
}
return generator.writeAllOutputs(contractMetadata, remoteContractMetadataTemplate)
}
func (generator *bindGenGeneratorRemote) create2DeployerHandler(contractMetadata *remoteContractMetadata) error {
fetchedData, err := generator.fetchContractData(contractMetadata.Verified, "eth", contractMetadata.Deployments.Eth.Hex())
if err != nil {
return err
}
contractMetadata.ABI = fetchedData.abi
contractMetadata.DeployedBin = fetchedData.deployedBin
if contractMetadata.InitBin, err = generator.removeDeploymentSalt(fetchedData.deploymentTx.Input, contractMetadata.DeploymentSalt); err != nil {
return err
}
// We're not comparing the bytecode for Create2Deployer with deployment on OP,
// because we're predeploying a modified version of Create2Deployer that has not yet been
// deployed to OP.
// For context: https://github.com/ethereum-optimism/op-geth/pull/126
return generator.writeAllOutputs(contractMetadata, remoteContractMetadataTemplate)
}
func (generator *bindGenGeneratorRemote) multiSendHandler(contractMetadata *remoteContractMetadata) error {
// MultiSend has an immutable that resolves to this(address).
// Because we're predeploying MultiSend to the same address as on OP,
// we can use the deployed bytecode directly for the predeploy
fetchedData, err := generator.fetchContractData(contractMetadata.Verified, "op", contractMetadata.Deployments.Op.Hex())
if err != nil {
return err
}
contractMetadata.ABI = fetchedData.abi
contractMetadata.DeployedBin = fetchedData.deployedBin
if contractMetadata.InitBin, err = generator.removeDeploymentSalt(fetchedData.deploymentTx.Input, contractMetadata.DeploymentSalt); err != nil {
return err
}
return generator.writeAllOutputs(contractMetadata, remoteContractMetadataTemplate)
}
func (generator *bindGenGeneratorRemote) senderCreatorHandler(contractMetadata *remoteContractMetadata) error {
var err error
contractMetadata.DeployedBin, err = generator.contractDataClients.eth.FetchDeployedBytecode(context.Background(), contractMetadata.Deployments.Eth.Hex())
if err != nil {
return fmt.Errorf("error fetching deployed bytecode: %w", err)
}
// 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 and therefore it isn't being compared here
if err := generator.compareBytecodeWithOp(contractMetadata, false, true); err != nil {
return fmt.Errorf("error comparing contract bytecode for %s: %w", contractMetadata.Name, err)
}
return generator.writeAllOutputs(contractMetadata, remoteContractMetadataTemplate)
}
func (generator *bindGenGeneratorRemote) permit2Handler(contractMetadata *remoteContractMetadata) error {
fetchedData, err := generator.fetchContractData(contractMetadata.Verified, "eth", contractMetadata.Deployments.Eth.Hex())
if err != nil {
return err
}
contractMetadata.ABI = fetchedData.abi
if contractMetadata.InitBin, err = generator.removeDeploymentSalt(fetchedData.deploymentTx.Input, contractMetadata.DeploymentSalt); err != nil {
return err
}
if !strings.EqualFold(contractMetadata.Deployer.Hex(), fetchedData.deploymentTx.To) {
return fmt.Errorf(
"expected deployer address: %s doesn't match the to address: %s for Permit2's proxy deployment transaction",
contractMetadata.Deployer.Hex(),
fetchedData.deploymentTx.To,
)
}
// We're not comparing deployed bytecode because Permit2 has immutable Solidity variables that
// are dependent on block.chainid
if err := generator.compareBytecodeWithOp(contractMetadata, true, false); err != nil {
return fmt.Errorf("error comparing contract bytecode for %s: %w", contractMetadata.Name, err)
}
return generator.writeAllOutputs(contractMetadata, permit2MetadataTemplate)
}
func (generator *bindGenGeneratorRemote) fetchContractData(contractVerified bool, chain, deploymentAddress string) (contractData, error) {
var data contractData
var err error
var client contractDataClient
switch chain {
case "eth":
client = generator.contractDataClients.eth
case "op":
client = generator.contractDataClients.op
default:
return data, fmt.Errorf("unknown chain, unable to retrieve a contract data client for chain: %s", chain)
}
if contractVerified {
data.abi, err = client.FetchAbi(context.Background(), deploymentAddress)
if err != nil {
return contractData{}, fmt.Errorf("error fetching ABI: %w", err)
}
}
data.deployedBin, err = client.FetchDeployedBytecode(context.Background(), deploymentAddress)
if err != nil {
return contractData{}, fmt.Errorf("error fetching deployed bytecode: %w", err)
}
deploymentTxHash, err := client.FetchDeploymentTxHash(context.Background(), deploymentAddress)
if err != nil {
return contractData{}, fmt.Errorf("error fetching deployment transaction hash: %w", err)
}
data.deploymentTx, err = client.FetchDeploymentTx(context.Background(), deploymentTxHash)
if err != nil {
return contractData{}, fmt.Errorf("error fetching deployment transaction data: %w", err)
}
return data, nil
}
func (generator *bindGenGeneratorRemote) removeDeploymentSalt(deploymentData, deploymentSalt string) (string, error) {
if deploymentSalt == "" {
return deploymentData, nil
}
re, err := regexp.Compile(fmt.Sprintf("^0x(%s)", deploymentSalt))
if err != nil {
return "", fmt.Errorf("failed to compile regular expression: %w", err)
}
if !re.MatchString(deploymentData) {
return "", fmt.Errorf(
"expected salt: %s to be at the beginning of the contract initialization code: %s, but it wasn't",
deploymentSalt, deploymentData,
)
}
return re.ReplaceAllString(deploymentData, ""), nil
}
func (generator *bindGenGeneratorRemote) compareBytecodeWithOp(contractMetadataEth *remoteContractMetadata, compareInitialization, compareDeployment bool) error {
// Passing false here, because true will retrieve contract's ABI, but we don't need it for bytecode comparison
opContractData, err := generator.fetchContractData(false, "op", contractMetadataEth.Deployments.Op.Hex())
if err != nil {
return err
}
if compareInitialization {
if opContractData.deploymentTx.Input, err = generator.removeDeploymentSalt(opContractData.deploymentTx.Input, contractMetadataEth.DeploymentSalt); err != nil {
return err
}
if !strings.EqualFold(contractMetadataEth.InitBin, opContractData.deploymentTx.Input) {
return fmt.Errorf(
"initialization bytecode on Ethereum doesn't match bytecode on Optimism. contract=%s bytecodeEth=%s bytecodeOp=%s",
contractMetadataEth.Name,
contractMetadataEth.InitBin,
opContractData.deploymentTx.Input,
)
}
}
if compareDeployment {
if !strings.EqualFold(contractMetadataEth.DeployedBin, opContractData.deployedBin) {
return fmt.Errorf(
"deployed bytecode on Ethereum doesn't match bytecode on Optimism. contract=%s bytecodeEth=%s bytecodeOp=%s",
contractMetadataEth.Name,
contractMetadataEth.DeployedBin,
opContractData.deployedBin,
)
}
}
return nil
}
func (generator *bindGenGeneratorRemote) writeAllOutputs(contractMetadata *remoteContractMetadata, fileTemplate string) error {
abiFilePath, bytecodeFilePath, err := writeContractArtifacts(
generator.logger, generator.tempArtifactsDir, contractMetadata.Name,
[]byte(contractMetadata.ABI), []byte(contractMetadata.InitBin),
)
if err != nil {
return err
}
err = genContractBindings(generator.logger, abiFilePath, bytecodeFilePath, generator.bindingsPackageName, contractMetadata.Name)
if err != nil {
return err
}
return generator.writeContractMetadata(
contractMetadata,
template.Must(template.New("remoteContractMetadata").Parse(fileTemplate)),
)
}
func (generator *bindGenGeneratorRemote) writeContractMetadata(contractMetadata *remoteContractMetadata, fileTemplate *template.Template) error {
metadataFilePath := filepath.Join(generator.metadataOut, strings.ToLower(contractMetadata.Name)+"_more.go")
metadataFile, err := os.OpenFile(
metadataFilePath,
os.O_RDWR|os.O_CREATE|os.O_TRUNC,
0o600,
)
if err != nil {
return fmt.Errorf("error opening %s's metadata file at %s: %w", contractMetadata.Name, metadataFilePath, err)
}
defer metadataFile.Close()
if err := fileTemplate.Execute(metadataFile, contractMetadata); err != nil {
return fmt.Errorf("error writing %s's contract metadata at %s: %w", contractMetadata.Name, metadataFilePath, err)
}
generator.logger.Debug("Successfully wrote contract metadata", "contract", contractMetadata.Name, "path", metadataFilePath)
return nil
}
// remoteContractMetadataTemplate is a Go text template for generating the metadata
// associated with a remotely sourced contracts.
//
// The template expects the following data to be provided:
// - .Package: the name of the Go package.
// - .Name: the name of the contract.
// - .DeployedBin: the binary (hex-encoded) of the deployed contract.
var remoteContractMetadataTemplate = `// Code generated - DO NOT EDIT.
// This file is a generated binding and any manual changes will be lost.
package {{.Package}}
var {{.Name}}DeployedBin = "{{.DeployedBin}}"
func init() {
deployedBytecodes["{{.Name}}"] = {{.Name}}DeployedBin
}
`
// permit2MetadataTemplate is a Go text template used to generate metadata
// for remotely sourced Permit2 contract. Because Permit2 has an immutable
// Solidity variables that depends on block.chainid, we can't use the deployed
// bytecode, but instead need to generate it specifically for each chain.
// To help with this, the metadata contains the initialization bytecode, the
// deployer address, and the CREATE2 salt, so that deployment can be
// replicated as closely as possible.
//
// The template expects the following data to be provided:
// - .Package: the name of the Go package.
// - .Name: the name of the contract.
// - .InitBin: the binary (hex-encoded) of the contract's initialization code.
// - .DeploymentSalt: the salt used during the contract's deployment.
// - .Deployer: the Ethereum address of the contract's deployer.
var permit2MetadataTemplate = `// Code generated - DO NOT EDIT.
// This file is a generated binding and any manual changes will be lost.
package {{.Package}}
var {{.Name}}InitBin = "{{.InitBin}}"
var {{.Name}}DeploymentSalt = "{{.DeploymentSalt}}"
var {{.Name}}Deployer = "{{.Deployer}}"
func init() {
initBytecodes["{{.Name}}"] = {{.Name}}InitBin
deploymentSalts["{{.Name}}"] = {{.Name}}DeploymentSalt
deployers["{{.Name}}"] = {{.Name}}Deployer
}
`
......@@ -12,7 +12,8 @@ import (
)
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
......
......@@ -120,10 +120,7 @@ func (c *client) fetchEtherscanRpc(ctx context.Context, url string) (rpcResponse
}
var resultString string
err = json.Unmarshal(response.Result, &resultString)
if err != nil {
return rpcResponse{}, fmt.Errorf("failed to unmarshal response.Result as a string: %w", err)
}
_ = json.Unmarshal(response.Result, &resultString)
if resultString == 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