diff --git a/op-bindings/Makefile b/op-bindings/Makefile index 7392f37e53a02..0aa8c2707fcef 100644 --- a/op-bindings/Makefile +++ b/op-bindings/Makefile @@ -7,6 +7,8 @@ contracts-list := ./artifacts.json log-level := info ETHERSCAN_APIKEY_ETH ?= ETHERSCAN_APIKEY_OP ?= +RPC_URL_ETH ?= +RPC_URL_OP ?= all: version mkdir bindings @@ -36,7 +38,9 @@ bindgen-generate-all: --source-maps-list MIPS,PreimageOracle \ --forge-artifacts $(contracts-dir)/forge-artifacts \ --etherscan.apikey.eth $(ETHERSCAN_APIKEY_ETH) \ - --etherscan.apikey.op $(ETHERSCAN_APIKEY_OP) + --etherscan.apikey.op $(ETHERSCAN_APIKEY_OP) \ + --rpc.url.eth $(RPC_URL_ETH) \ + --rpc.url.op $(RPC_URL_OP) bindgen-local: compile bindgen-generate-local @@ -60,7 +64,9 @@ bindgen-remote: --log.level $(log-level) \ remote \ --etherscan.apikey.eth $(ETHERSCAN_APIKEY_ETH) \ - --etherscan.apikey.op $(ETHERSCAN_APIKEY_OP) + --etherscan.apikey.op $(ETHERSCAN_APIKEY_OP) \ + --rpc.url.eth $(RPC_URL_ETH) \ + --rpc.url.op $(RPC_URL_OP) mkdir: mkdir -p $(pkg) diff --git a/op-bindings/bindgen/generator_remote.go b/op-bindings/bindgen/generator_remote.go index e059e7428fbb8..8e966e4922b3a 100644 --- a/op-bindings/bindgen/generator_remote.go +++ b/op-bindings/bindgen/generator_remote.go @@ -7,6 +7,7 @@ import ( "github.com/ethereum-optimism/optimism/op-bindings/etherscan" "github.com/ethereum/go-ethereum/common" + "github.com/ethereum/go-ethereum/ethclient" ) type bindGenGeneratorRemote struct { @@ -15,6 +16,10 @@ type bindGenGeneratorRemote struct { eth contractDataClient op contractDataClient } + rpcClients struct { + eth *ethclient.Client + op *ethclient.Client + } tempArtifactsDir string } diff --git a/op-bindings/bindgen/main.go b/op-bindings/bindgen/main.go index eb88e571193bd..632d3be3e62fb 100644 --- a/op-bindings/bindgen/main.go +++ b/op-bindings/bindgen/main.go @@ -7,6 +7,7 @@ import ( "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/ethclient" "github.com/ethereum/go-ethereum/log" "github.com/urfave/cli/v2" ) @@ -170,6 +171,13 @@ func parseConfigRemote(logger log.Logger, c *cli.Context) (bindGenGeneratorRemot generator.contractDataClients.eth = etherscan.NewEthereumClient(c.String(EtherscanApiKeyEthFlagName)) generator.contractDataClients.op = etherscan.NewOptimismClient(c.String(EtherscanApiKeyOpFlagName)) + + if generator.rpcClients.eth, err = ethclient.Dial(c.String(RpcUrlEthFlagName)); err != nil { + return bindGenGeneratorRemote{}, fmt.Errorf("error initializing Ethereum client: %w", err) + } + if generator.rpcClients.op, err = ethclient.Dial(c.String(RpcUrlOpFlagName)); err != nil { + return bindGenGeneratorRemote{}, fmt.Errorf("error initializing Optimism client: %w", err) + } return generator, nil } @@ -221,5 +229,15 @@ func remoteFlags() []cli.Flag { Usage: "API key to make queries to Etherscan for Optimism", Required: true, }, + &cli.StringFlag{ + Name: RpcUrlEthFlagName, + Usage: "RPC URL (with API key if required) to query Ethereum", + Required: true, + }, + &cli.StringFlag{ + Name: RpcUrlOpFlagName, + Usage: "RPC URL (with API key if required) to query Optimism", + Required: true, + }, } } diff --git a/op-bindings/bindgen/remote_handlers.go b/op-bindings/bindgen/remote_handlers.go index 74a44d98bd462..a182db04200ce 100644 --- a/op-bindings/bindgen/remote_handlers.go +++ b/op-bindings/bindgen/remote_handlers.go @@ -10,6 +10,8 @@ import ( "text/template" "github.com/ethereum-optimism/optimism/op-bindings/etherscan" + "github.com/ethereum/go-ethereum/common" + "github.com/ethereum/go-ethereum/ethclient" ) type contractData struct { @@ -25,6 +27,12 @@ func (generator *bindGenGeneratorRemote) standardHandler(contractMetadata *remot } contractMetadata.DeployedBin = fetchedData.deployedBin + if err = generator.compareDeployedBytecodeWithRpc(contractMetadata, "eth"); err != nil { + return err + } + if err = generator.compareDeployedBytecodeWithRpc(contractMetadata, "op"); err != nil { + return err + } // If ABI was explicitly provided by config, don't overwrite if contractMetadata.ABI == "" { @@ -76,6 +84,12 @@ func (generator *bindGenGeneratorRemote) multiSendHandler(contractMetadata *remo contractMetadata.ABI = fetchedData.abi contractMetadata.DeployedBin = fetchedData.deployedBin + if err = generator.compareDeployedBytecodeWithRpc(contractMetadata, "eth"); err != nil { + return err + } + if err = generator.compareDeployedBytecodeWithRpc(contractMetadata, "op"); err != nil { + return err + } if contractMetadata.InitBin, err = generator.removeDeploymentSalt(fetchedData.deploymentTx.Input, contractMetadata.DeploymentSalt); err != nil { return err } @@ -89,6 +103,12 @@ func (generator *bindGenGeneratorRemote) senderCreatorHandler(contractMetadata * if err != nil { return fmt.Errorf("error fetching deployed bytecode: %w", err) } + if err = generator.compareDeployedBytecodeWithRpc(contractMetadata, "eth"); err != nil { + return err + } + if err = generator.compareDeployedBytecodeWithRpc(contractMetadata, "op"); err != nil { + return err + } // The SenderCreator contract is deployed by EntryPoint, so the transaction data // from the deployment transaction is for the entire EntryPoint deployment. @@ -221,6 +241,41 @@ func (generator *bindGenGeneratorRemote) compareBytecodeWithOp(contractMetadataE return nil } +func (generator *bindGenGeneratorRemote) compareDeployedBytecodeWithRpc(contractMetadata *remoteContractMetadata, chain string) error { + var client *ethclient.Client + switch chain { + case "eth": + client = generator.rpcClients.eth + case "op": + client = generator.rpcClients.op + default: + return fmt.Errorf("unknown chain: %s, unable to retrieve a RPC client", chain) + } + + var deployment common.Address + switch chain { + case "eth": + deployment = contractMetadata.Deployments.Eth + case "op": + deployment = contractMetadata.Deployments.Op + default: + generator.logger.Warn("Unable to compare bytecode from Etherscan against RPC client, no deployment address provided for chain", "chain", chain) + } + + if deployment != (common.Address{}) { + bytecode, err := client.CodeAt(context.Background(), common.HexToAddress(deployment.Hex()), nil) + if err != nil { + return fmt.Errorf("error getting deployed bytecode from RPC on chain: %s err: %w", chain, err) + } + bytecodeHex := common.Bytes2Hex(bytecode) + if !strings.EqualFold(strings.TrimPrefix(contractMetadata.DeployedBin, "0x"), bytecodeHex) { + return fmt.Errorf("%s deployment bytecode from RPC doesn't match bytecode from Etherscan. rpcBytecode: %s etherscanBytecode: %s", contractMetadata.Name, bytecodeHex, contractMetadata.DeployedBin) + } + } + + return nil +} + func (generator *bindGenGeneratorRemote) writeAllOutputs(contractMetadata *remoteContractMetadata, fileTemplate string) error { abiFilePath, bytecodeFilePath, err := writeContractArtifacts( generator.logger, generator.tempArtifactsDir, contractMetadata.Name,