Commit 9d13ae01 authored by Wyatt Barnes's avatar Wyatt Barnes Committed by GitHub

Refactor BindGen E2E tests (#8741)

parent 6c23fbee
......@@ -524,16 +524,16 @@ jobs:
working_directory: op-bindings
- notify-failures-on-develop
bindgen-test:
bindgen-test-e2e:
docker:
- image: <<pipeline.parameters.ci_builder_image>>
resource_class: xlarge
steps:
- checkout
- run:
name: bindgen test
name: bindgen test E2E
command: make test-bindgen-e2e
working_directory: op-bindings
working_directory: op-bindings-e2e
- notify-failures-on-develop
js-lint-test:
......@@ -2012,7 +2012,7 @@ workflows:
context:
- slack
- oplabs-etherscan
- bindgen-test:
- bindgen-test-e2e:
context:
- slack
- oplabs-etherscan
test-bindgen-e2e:
go test ./... -count=1
\ No newline at end of file
This source diff could not be displayed because it is too large. You can view the blob instead.
package bindgen
import (
"github.com/ethereum-optimism/optimism/op-bindings/bindgen"
"github.com/ethereum-optimism/optimism/op-bindings/etherscan"
"github.com/ethereum/go-ethereum/common"
)
var fetchContractDataTests = []struct {
name string
contractVerified bool
chain string
deploymentAddress string
expectedContractData bindgen.ContractData
}{
{
"MultiCall3 on ETH",
true,
"eth",
"0xcA11bde05977b3631167028862bE2a173976CA11",
bindgen.ContractData{
Abi: MultiCall3Abi,
DeployedBin: MultiCall3DeployedBytecode,
DeploymentTx: etherscan.Transaction{
Input: MultiCall3InitBytecode,
Hash: "0x00d9fcb7848f6f6b0aae4fb709c133d69262b902156c85a473ef23faa60760bd",
To: "",
},
},
},
{
"MultiCall3 on OP",
true,
"op",
"0xcA11bde05977b3631167028862bE2a173976CA11",
bindgen.ContractData{
Abi: MultiCall3Abi,
DeployedBin: MultiCall3DeployedBytecode,
DeploymentTx: etherscan.Transaction{
Input: MultiCall3InitBytecode,
Hash: "0xb62f9191a2cf399c0d2afd33f5b8baf7c6b52af6dd2386e44121b1bab91b80e5",
To: "",
},
},
},
{
"SafeSingletonFactory on ETH",
false,
"eth",
"0x914d7Fec6aaC8cd542e72Bca78B30650d45643d7",
bindgen.ContractData{
Abi: "",
DeployedBin: SafeSingletonFactoryDeployedBytecode,
DeploymentTx: etherscan.Transaction{
Input: SafeSingletonFactoryInitBytecode,
Hash: "0x69c275b5304db980105b7a6d731f9e1157a3fe29e7ff6ff95235297df53e9928",
To: "",
},
},
},
{
"Permit2 on ETH",
true,
"eth",
"0x000000000022D473030F116dDEE9F6B43aC78BA3",
bindgen.ContractData{
Abi: Permit2Abi,
DeployedBin: Permit2DeployedBytecode,
DeploymentTx: etherscan.Transaction{
Input: Permit2InitBytecode,
Hash: "0xf2f1fe96c16ee674bb7fcee166be52465a418927d124f5f1d231b36eae65d377",
To: "0x4e59b44847b379578588920ca78fbf26c0b4956c",
},
},
},
}
// Not currently being tested due to complexity of test setup:
// - FetchDeploymentTxHash failure
// Not being tested because the contract would need to have deployed bytecode to
// pass FetchDeployedBytecode, which means Etherscan should have indexed the deployment tx
// - FetchDeploymentTx failure
// Not being tested for the same reason and there would be no way to pass FetchDeploymentTxHash,
// but not be able to retrieve tx details
var fetchContractDataTestsFailures = []struct {
name string
contractVerified bool
chain string
deploymentAddress string
expectedError string
}{
{
"MultiCall3 on Foo",
true,
"foo",
"0xcA11bde05977b3631167028862bE2a173976CA11",
"unknown chain, unable to retrieve a contract data client for chain: foo",
},
{
// This test case is covering fetching an ABI for a non-verified contract that's we're saying is verified
"SafeSingletonFactory on ETH",
true,
"eth",
"0x914d7Fec6aaC8cd542e72Bca78B30650d45643d7",
"error fetching ABI: operation failed permanently after 3 attempts: there was an issue with the Etherscan request",
},
{
// This test case is covering fetching the deployed bytecode for a non-existent contract
"Nonexistent on ETH",
false,
"eth",
"0x914d7Fec6aaC8cd542e72Bca78B30650d455555",
"error fetching deployed bytecode: API response result is not expected bytecode string",
},
}
var compareInitBytecodeWithOpTests = []struct {
name string
contractMetadataEth bindgen.RemoteContractMetadata
initCodeShouldMatch bool
}{
{
name: "Safe_v130 Init Bytecode Should Match",
contractMetadataEth: bindgen.RemoteContractMetadata{
RemoteContract: bindgen.RemoteContract{
Name: "Safe_v130",
Verified: true,
Deployments: bindgen.Deployments{
Op: common.HexToAddress("0xd9Db270c1B5E3Bd161E8c8503c55cEABeE709552"),
Eth: common.HexToAddress("0x69f4D1788e39c87893C980c06EdF4b7f686e2938"),
},
DeploymentSalt: "0000000000000000000000000000000000000000000000000000000000000000",
Deployer: common.Address{},
ABI: "",
InitBytecode: "",
},
Package: "bindings",
InitBin: Safe_v130InitBytecodeNoSalt,
DeployedBin: "",
},
initCodeShouldMatch: true,
},
{
name: "Safe_v130 Compare Init Bytecode Only On OP",
contractMetadataEth: bindgen.RemoteContractMetadata{
RemoteContract: bindgen.RemoteContract{
Name: "Safe_v130",
Verified: true,
Deployments: bindgen.Deployments{
Op: common.HexToAddress("0x69f4D1788e39c87893C980c06EdF4b7f686e2938"),
},
DeploymentSalt: "0000000000000000000000000000000000000000000000000000000000000000",
Deployer: common.Address{},
ABI: "",
InitBytecode: "",
},
Package: "bindings",
InitBin: Safe_v130InitBytecodeNoSalt,
DeployedBin: "",
},
initCodeShouldMatch: true,
},
{
name: "Create2Deployer's Init Bytecode Should Not Match",
contractMetadataEth: bindgen.RemoteContractMetadata{
RemoteContract: bindgen.RemoteContract{
Name: "Create2Deployer",
Verified: true,
Deployments: bindgen.Deployments{
Op: common.HexToAddress("0x13b0D85CcB8bf860b6b79AF3029fCA081AE9beF2"),
Eth: common.HexToAddress("0xF49600926c7109BD66Ab97a2c036bf696e58Dbc2"),
},
Deployer: common.Address{},
ABI: "",
InitBytecode: "",
},
Package: "bindings",
InitBin: Create2DeployerInitBytecode,
DeployedBin: Create2DeployerDeployedBytecode,
},
initCodeShouldMatch: false,
},
}
var compareInitBytecodeWithOpTestsFailures = []struct {
name string
contractMetadataEth bindgen.RemoteContractMetadata
initCodeShouldMatch bool
expectedError string
}{
{
name: "Safe_v130 Mismatch Init Bytecode",
contractMetadataEth: bindgen.RemoteContractMetadata{
RemoteContract: bindgen.RemoteContract{
Name: "Safe_v130",
Verified: true,
Deployments: bindgen.Deployments{
Op: common.HexToAddress("0xd9Db270c1B5E3Bd161E8c8503c55cEABeE709552"),
Eth: common.HexToAddress("0x69f4D1788e39c87893C980c06EdF4b7f686e2938"),
},
DeploymentSalt: "0000000000000000000000000000000000000000000000000000000000000000",
Deployer: common.Address{},
ABI: "",
InitBytecode: "",
},
Package: "bindings",
InitBin: Permit2InitBytecodeNoSalt,
DeployedBin: "",
},
initCodeShouldMatch: true,
expectedError: "expected initialization bytecode to match on Ethereum and Optimism, but it doesn't.",
},
{
name: "Safe_v130 No Deployment on Optimism",
contractMetadataEth: bindgen.RemoteContractMetadata{
RemoteContract: bindgen.RemoteContract{
Name: "Safe_v130",
Verified: true,
Deployments: bindgen.Deployments{
Eth: common.HexToAddress("0x69f4D1788e39c87893C980c06EdF4b7f686e2938"),
},
DeploymentSalt: "0000000000000000000000000000000000000000000000000000000000000000",
Deployer: common.Address{},
ABI: "",
InitBytecode: "",
},
Package: "bindings",
InitBin: Safe_v130InitBytecode,
DeployedBin: Safe_v130DeployedBytecode,
},
initCodeShouldMatch: true,
expectedError: "no deployment address on Optimism provided for Safe_v130",
},
{
name: "MultiCall3 Expected Init Code Not to Match, but it Does",
contractMetadataEth: bindgen.RemoteContractMetadata{
RemoteContract: bindgen.RemoteContract{
Name: "MultiCall3",
Verified: true,
Deployments: bindgen.Deployments{
Op: common.HexToAddress("0xcA11bde05977b3631167028862bE2a173976CA11"),
Eth: common.HexToAddress("0xcA11bde05977b3631167028862bE2a173976CA11"),
},
Deployer: common.Address{},
ABI: "",
InitBytecode: "",
},
Package: "bindings",
InitBin: MultiCall3InitBytecode,
DeployedBin: MultiCall3DeployedBytecode,
},
initCodeShouldMatch: false,
expectedError: "expected initialization bytecode on Ethereum to not match on Optimism, but it did.",
},
{
name: "Safe_v130 No Init Bytecode Provided",
contractMetadataEth: bindgen.RemoteContractMetadata{
RemoteContract: bindgen.RemoteContract{
Name: "Safe_v130",
Verified: true,
Deployments: bindgen.Deployments{
Op: common.HexToAddress("0xd9Db270c1B5E3Bd161E8c8503c55cEABeE709552"),
Eth: common.HexToAddress("0x69f4D1788e39c87893C980c06EdF4b7f686e2938"),
},
DeploymentSalt: "0000000000000000000000000000000000000000000000000000000000000000",
Deployer: common.Address{},
ABI: "",
InitBytecode: "",
},
Package: "bindings",
InitBin: "",
DeployedBin: Safe_v130DeployedBytecode,
},
initCodeShouldMatch: false,
expectedError: "no initialization bytecode provided for ETH deployment for comparison",
},
}
var compareDeployedBytecodeWithOpTests = []struct {
name string
contractMetadataEth bindgen.RemoteContractMetadata
deployedCodeShouldMatch bool
}{
{
name: "Safe_v130 Deployed Bytecode Should Match",
contractMetadataEth: bindgen.RemoteContractMetadata{
RemoteContract: bindgen.RemoteContract{
Name: "Safe_v130",
Verified: true,
Deployments: bindgen.Deployments{
Op: common.HexToAddress("0xd9Db270c1B5E3Bd161E8c8503c55cEABeE709552"),
Eth: common.HexToAddress("0x69f4D1788e39c87893C980c06EdF4b7f686e2938"),
},
DeploymentSalt: "0000000000000000000000000000000000000000000000000000000000000000",
Deployer: common.Address{},
ABI: "",
InitBytecode: "",
},
Package: "bindings",
InitBin: "",
DeployedBin: Safe_v130DeployedBytecode,
},
deployedCodeShouldMatch: true,
},
{
name: "Safe_v130 Compare Deployed Bytecode Only On OP",
contractMetadataEth: bindgen.RemoteContractMetadata{
RemoteContract: bindgen.RemoteContract{
Name: "Safe_v130",
Verified: true,
Deployments: bindgen.Deployments{
Op: common.HexToAddress("0x69f4D1788e39c87893C980c06EdF4b7f686e2938"),
},
DeploymentSalt: "0000000000000000000000000000000000000000000000000000000000000000",
Deployer: common.Address{},
ABI: "",
InitBytecode: "",
},
Package: "bindings",
InitBin: Safe_v130InitBytecodeNoSalt,
DeployedBin: Safe_v130DeployedBytecode,
},
deployedCodeShouldMatch: true,
},
{
name: "Permit2's Deployed Bytecode Should Not Match",
contractMetadataEth: bindgen.RemoteContractMetadata{
RemoteContract: bindgen.RemoteContract{
Name: "Permit2",
Verified: true,
Deployments: bindgen.Deployments{
Op: common.HexToAddress("0x000000000022D473030F116dDEE9F6B43aC78BA3"),
Eth: common.HexToAddress("0x000000000022D473030F116dDEE9F6B43aC78BA3"),
},
Deployer: common.Address{},
ABI: "",
InitBytecode: "",
},
Package: "bindings",
InitBin: Permit2InitBytecode,
DeployedBin: Permit2DeployedBytecode,
},
deployedCodeShouldMatch: false,
},
}
var compareDeployedBytecodeWithOpTestsFailures = []struct {
name string
contractMetadataEth bindgen.RemoteContractMetadata
deployedCodeShouldMatch bool
expectedError string
}{
{
name: "Safe_v130 Mismatch Deplolyed Bytecode",
contractMetadataEth: bindgen.RemoteContractMetadata{
RemoteContract: bindgen.RemoteContract{
Name: "Safe_v130",
Verified: true,
Deployments: bindgen.Deployments{
Op: common.HexToAddress("0xd9Db270c1B5E3Bd161E8c8503c55cEABeE709552"),
Eth: common.HexToAddress("0x69f4D1788e39c87893C980c06EdF4b7f686e2938"),
},
DeploymentSalt: "0000000000000000000000000000000000000000000000000000000000000000",
Deployer: common.Address{},
ABI: "",
InitBytecode: "",
},
Package: "bindings",
InitBin: "",
DeployedBin: Permit2DeployedBytecode,
},
deployedCodeShouldMatch: true,
expectedError: "expected deployed bytecode to match on Ethereum and Optimism, but it doesn't.",
},
{
name: "Safe_v130 No Deployment on Optimism",
contractMetadataEth: bindgen.RemoteContractMetadata{
RemoteContract: bindgen.RemoteContract{
Name: "Safe_v130",
Verified: true,
Deployments: bindgen.Deployments{
Eth: common.HexToAddress("0x69f4D1788e39c87893C980c06EdF4b7f686e2938"),
},
DeploymentSalt: "0000000000000000000000000000000000000000000000000000000000000000",
Deployer: common.Address{},
ABI: "",
InitBytecode: "",
},
Package: "bindings",
InitBin: "",
DeployedBin: Permit2DeployedBytecode,
},
deployedCodeShouldMatch: true,
expectedError: "no deployment address on Optimism provided for Safe_v130",
},
{
name: "Safe_v130 Expected Deployed Code Not to Match, but it Does",
contractMetadataEth: bindgen.RemoteContractMetadata{
RemoteContract: bindgen.RemoteContract{
Name: "Safe_v130",
Verified: true,
Deployments: bindgen.Deployments{
Op: common.HexToAddress("0xd9Db270c1B5E3Bd161E8c8503c55cEABeE709552"),
Eth: common.HexToAddress("0x69f4D1788e39c87893C980c06EdF4b7f686e2938"),
},
DeploymentSalt: "0000000000000000000000000000000000000000000000000000000000000000",
Deployer: common.Address{},
ABI: "",
InitBytecode: "",
},
Package: "bindings",
InitBin: Safe_v130InitBytecode,
DeployedBin: Safe_v130DeployedBytecode,
},
deployedCodeShouldMatch: false,
expectedError: "expected deployed bytecode on Ethereum to not match on Optimism, but it does.",
},
{
name: "Safe_v130 No Deployed Bytecode Provided",
contractMetadataEth: bindgen.RemoteContractMetadata{
RemoteContract: bindgen.RemoteContract{
Name: "Safe_v130",
Verified: true,
Deployments: bindgen.Deployments{
Op: common.HexToAddress("0xd9Db270c1B5E3Bd161E8c8503c55cEABeE709552"),
Eth: common.HexToAddress("0x69f4D1788e39c87893C980c06EdF4b7f686e2938"),
},
DeploymentSalt: "0000000000000000000000000000000000000000000000000000000000000000",
Deployer: common.Address{},
ABI: "",
InitBytecode: "",
},
Package: "bindings",
InitBin: Safe_v130InitBytecode,
DeployedBin: "",
},
deployedCodeShouldMatch: false,
expectedError: "no deployed bytecode provided for ETH deployment for comparison",
},
}
var compareDeployedBytecodeWithRpcTests = []struct {
name string
contractMetadataEth bindgen.RemoteContractMetadata
chain string
}{
{
name: "Safe_v130 Compare Against ETH",
contractMetadataEth: bindgen.RemoteContractMetadata{
RemoteContract: bindgen.RemoteContract{
Name: "Safe_v130",
Verified: true,
Deployments: bindgen.Deployments{
Op: common.Address{},
Eth: common.HexToAddress("0x69f4D1788e39c87893C980c06EdF4b7f686e2938"),
},
DeploymentSalt: "0000000000000000000000000000000000000000000000000000000000000000",
Deployer: common.Address{},
ABI: "",
InitBytecode: "",
},
Package: "bindings",
InitBin: "",
DeployedBin: Safe_v130DeployedBytecode,
},
chain: "eth",
},
{
name: "Safe_v130 Compare Against OP",
contractMetadataEth: bindgen.RemoteContractMetadata{
RemoteContract: bindgen.RemoteContract{
Name: "Safe_v130",
Verified: true,
Deployments: bindgen.Deployments{
Op: common.HexToAddress("0xd9Db270c1B5E3Bd161E8c8503c55cEABeE709552"),
Eth: common.Address{},
},
DeploymentSalt: "0000000000000000000000000000000000000000000000000000000000000000",
Deployer: common.Address{},
ABI: "",
InitBytecode: "",
},
Package: "bindings",
InitBin: "",
DeployedBin: Safe_v130DeployedBytecode,
},
chain: "op",
},
}
var compareDeployedBytecodeWithRpcTestsFailures = []struct {
name string
contractMetadataEth bindgen.RemoteContractMetadata
chain string
expectedError string
}{
{
name: "Safe_v130 Compare Against foo",
contractMetadataEth: bindgen.RemoteContractMetadata{
RemoteContract: bindgen.RemoteContract{
Name: "Safe_v130",
Verified: true,
Deployments: bindgen.Deployments{
Op: common.Address{},
Eth: common.HexToAddress("0x69f4D1788e39c87893C980c06EdF4b7f686e2938"),
},
DeploymentSalt: "0000000000000000000000000000000000000000000000000000000000000000",
Deployer: common.Address{},
ABI: "",
InitBytecode: "",
},
Package: "bindings",
InitBin: "",
DeployedBin: "",
},
chain: "foo",
expectedError: "unknown chain: foo, unable to retrieve a RPC client",
},
{
name: "Safe_v130 Bytecode Mismatch",
contractMetadataEth: bindgen.RemoteContractMetadata{
RemoteContract: bindgen.RemoteContract{
Name: "Safe_v130",
Verified: true,
Deployments: bindgen.Deployments{
Op: common.Address{},
Eth: common.HexToAddress("0x69f4D1788e39c87893C980c06EdF4b7f686e2938"),
},
DeploymentSalt: "0000000000000000000000000000000000000000000000000000000000000000",
Deployer: common.Address{},
ABI: "",
InitBytecode: "",
},
Package: "bindings",
InitBin: "",
DeployedBin: Permit2DeployedBytecode,
},
chain: "eth",
expectedError: "Safe_v130 deployment bytecode from RPC doesn't match bytecode from Etherscan.",
},
}
package bindgen
import (
"fmt"
"os"
"reflect"
"strings"
"testing"
"github.com/ethereum-optimism/optimism/op-bindings/bindgen"
"github.com/ethereum-optimism/optimism/op-bindings/etherscan"
"github.com/ethereum/go-ethereum/ethclient"
)
var generator bindgen.BindGenGeneratorRemote = bindgen.BindGenGeneratorRemote{}
func configureGenerator(t *testing.T) error {
generator.ContractDataClients.Eth = etherscan.NewEthereumClient(os.Getenv("ETHERSCAN_APIKEY_ETH"))
generator.ContractDataClients.Op = etherscan.NewOptimismClient(os.Getenv("ETHERSCAN_APIKEY_OP"))
var err error
if generator.RpcClients.Eth, err = ethclient.Dial(os.Getenv("RPC_URL_ETH")); err != nil {
return fmt.Errorf("error initializing Ethereum client: %w", err)
}
if generator.RpcClients.Op, err = ethclient.Dial(os.Getenv("RPC_URL_OP")); err != nil {
return fmt.Errorf("error initializing Optimism client: %w", err)
}
return nil
}
func TestFetchContractData(t *testing.T) {
if err := configureGenerator(t); err != nil {
t.Error(err)
}
for _, tt := range fetchContractDataTests {
t.Run(tt.name, func(t *testing.T) {
contractData, err := generator.FetchContractData(tt.contractVerified, tt.chain, tt.deploymentAddress)
if err != nil {
t.Error(err)
}
if !reflect.DeepEqual(contractData, tt.expectedContractData) {
t.Errorf("Retrieved contract data doesn't match expected. Expected: %s Retrieved: %s", tt.expectedContractData, contractData)
}
})
}
}
func TestFetchContractDataFailures(t *testing.T) {
if err := configureGenerator(t); err != nil {
t.Error(err)
}
for _, tt := range fetchContractDataTestsFailures {
t.Run(tt.name, func(t *testing.T) {
_, err := generator.FetchContractData(tt.contractVerified, tt.chain, tt.deploymentAddress)
if err == nil {
t.Errorf("Expected error: %s but didn't receive it", tt.expectedError)
return
}
if !strings.Contains(err.Error(), tt.expectedError) {
t.Errorf("Expected error: %s Received: %s", tt.expectedError, err)
return
}
})
}
}
func TestCompareInitBytecodeWithOp(t *testing.T) {
if err := configureGenerator(t); err != nil {
t.Error(err)
}
for _, tt := range compareInitBytecodeWithOpTests {
t.Run(tt.name, func(t *testing.T) {
err := generator.CompareInitBytecodeWithOp(&tt.contractMetadataEth, tt.initCodeShouldMatch)
if err != nil {
t.Error(err)
}
})
}
}
func TestCompareInitBytecodeWithOpFailures(t *testing.T) {
if err := configureGenerator(t); err != nil {
t.Error(err)
}
for _, tt := range compareInitBytecodeWithOpTestsFailures {
t.Run(tt.name, func(t *testing.T) {
err := generator.CompareInitBytecodeWithOp(&tt.contractMetadataEth, tt.initCodeShouldMatch)
if err == nil {
t.Errorf("Expected error: %s but didn't receive it", tt.expectedError)
return
}
if !strings.Contains(err.Error(), tt.expectedError) {
t.Errorf("Expected error: %s Received: %s", tt.expectedError, err)
return
}
})
}
}
func TestCompareDeployedBytecodeWithOp(t *testing.T) {
if err := configureGenerator(t); err != nil {
t.Error(err)
}
for _, tt := range compareDeployedBytecodeWithOpTests {
t.Run(tt.name, func(t *testing.T) {
err := generator.CompareDeployedBytecodeWithOp(&tt.contractMetadataEth, tt.deployedCodeShouldMatch)
if err != nil {
t.Error(err)
}
})
}
}
func TestCompareDeployedBytecodeWithOpFailures(t *testing.T) {
if err := configureGenerator(t); err != nil {
t.Error(err)
}
for _, tt := range compareDeployedBytecodeWithOpTestsFailures {
t.Run(tt.name, func(t *testing.T) {
err := generator.CompareDeployedBytecodeWithOp(&tt.contractMetadataEth, tt.deployedCodeShouldMatch)
if err == nil {
t.Errorf("Expected error: %s but didn't receive it", tt.expectedError)
return
}
if !strings.Contains(err.Error(), tt.expectedError) {
t.Errorf("Expected error: %s Received: %s", tt.expectedError, err)
return
}
})
}
}
func TestCompareDeployedBytecodeWithRpc(t *testing.T) {
if err := configureGenerator(t); err != nil {
t.Error(err)
}
for _, tt := range compareDeployedBytecodeWithRpcTests {
t.Run(tt.name, func(t *testing.T) {
err := generator.CompareDeployedBytecodeWithRpc(&tt.contractMetadataEth, tt.chain)
if err != nil {
t.Error(err)
}
})
}
}
func TestCompareDeployedBytecodeWithRpcFailures(t *testing.T) {
if err := configureGenerator(t); err != nil {
t.Error(err)
}
for _, tt := range compareDeployedBytecodeWithRpcTestsFailures {
t.Run(tt.name, func(t *testing.T) {
err := generator.CompareDeployedBytecodeWithRpc(&tt.contractMetadataEth, tt.chain)
if err == nil {
t.Errorf("Expected error: %s but didn't receive it", tt.expectedError)
return
}
if !strings.Contains(err.Error(), tt.expectedError) {
t.Errorf("Expected error: %s Received: %s", tt.expectedError, err)
return
}
})
}
}
......@@ -28,7 +28,7 @@ bindings-build: bindgen-generate-local
bindgen: compile bindgen-generate-all
bindgen-generate-all:
go run ./bindgen/ \
go run ./cmd/ \
generate \
--metadata-out ./$(pkg) \
--bindings-package $(pkg) \
......@@ -45,7 +45,7 @@ bindgen-generate-all:
bindgen-local: compile bindgen-generate-local
bindgen-generate-local:
go run ./bindgen/ \
go run ./cmd/ \
generate \
--metadata-out ./$(pkg) \
--bindings-package $(pkg) \
......@@ -56,7 +56,7 @@ bindgen-generate-local:
--forge-artifacts $(contracts-dir)/forge-artifacts
bindgen-remote:
go run ./bindgen/ \
go run ./cmd/ \
generate \
--metadata-out ./$(pkg) \
--bindings-package $(pkg) \
......@@ -80,6 +80,3 @@ clean:
test:
go test ./...
test-bindgen-e2e:
RUN_E2E=true go test -count=1 ./bindgen/...
This source diff could not be displayed because it is too large. You can view the blob instead.
package main
import (
"github.com/ethereum-optimism/optimism/op-bindings/etherscan"
"github.com/ethereum/go-ethereum/common"
)
var fetchContractDataTests = []struct {
name string
contractVerified bool
chain string
deploymentAddress string
expectedContractData contractData
}{
{
"MultiCall3 on ETH",
true,
"eth",
"0xcA11bde05977b3631167028862bE2a173976CA11",
contractData{
MultiCall3Abi,
MultiCall3DeployedBytecode,
etherscan.Transaction{
Input: MultiCall3InitBytecode,
Hash: "0x00d9fcb7848f6f6b0aae4fb709c133d69262b902156c85a473ef23faa60760bd",
To: "",
},
},
},
{
"MultiCall3 on OP",
true,
"op",
"0xcA11bde05977b3631167028862bE2a173976CA11",
contractData{
MultiCall3Abi,
MultiCall3DeployedBytecode,
etherscan.Transaction{
Input: MultiCall3InitBytecode,
Hash: "0xb62f9191a2cf399c0d2afd33f5b8baf7c6b52af6dd2386e44121b1bab91b80e5",
To: "",
},
},
},
{
"SafeSingletonFactory on ETH",
false,
"eth",
"0x914d7Fec6aaC8cd542e72Bca78B30650d45643d7",
contractData{
"",
SafeSingletonFactoryDeployedBytecode,
etherscan.Transaction{
Input: SafeSingletonFactoryInitBytecode,
Hash: "0x69c275b5304db980105b7a6d731f9e1157a3fe29e7ff6ff95235297df53e9928",
To: "",
},
},
},
{
"Permit2 on ETH",
true,
"eth",
"0x000000000022D473030F116dDEE9F6B43aC78BA3",
contractData{
Permit2Abi,
Permit2DeployedBytecode,
etherscan.Transaction{
Input: Permit2InitBytecode,
Hash: "0xf2f1fe96c16ee674bb7fcee166be52465a418927d124f5f1d231b36eae65d377",
To: "0x4e59b44847b379578588920ca78fbf26c0b4956c",
},
},
},
}
// Not currently being tested due to complexity of test setup:
// - FetchDeploymentTxHash failure
// Not being tested because the contract would need to have deployed bytecode to
// pass FetchDeployedBytecode, which means Etherscan should have indexed the deployment tx
// - FetchDeploymentTx failure
// Not being tested for the same reason and there would be no way to pass FetchDeploymentTxHash,
// but not be able to retrieve tx details
var fetchContractDataTestsFailures = []struct {
name string
contractVerified bool
chain string
deploymentAddress string
expectedError string
}{
{
"MultiCall3 on Foo",
true,
"foo",
"0xcA11bde05977b3631167028862bE2a173976CA11",
"unknown chain, unable to retrieve a contract data client for chain: foo",
},
{
// This test case is covering fetching an ABI for a non-verified contract that's we're saying is verified
"SafeSingletonFactory on ETH",
true,
"eth",
"0x914d7Fec6aaC8cd542e72Bca78B30650d45643d7",
"error fetching ABI: operation failed permanently after 3 attempts: there was an issue with the Etherscan request",
},
{
// This test case is covering fetching the deployed bytecode for a non-existent contract
"Nonexistent on ETH",
false,
"eth",
"0x914d7Fec6aaC8cd542e72Bca78B30650d455555",
"error fetching deployed bytecode: API response result is not expected bytecode string",
},
}
package bindgen
// The Init bytecode used for these tests can either be sourced
// on-chain using the deployment tx of these contracts, or can be
......@@ -161,429 +48,3 @@ var removeDeploymentSaltTestsFailures = []struct {
"expected salt: 4567 to be at the beginning of the contract initialization code: 0x1234abc, but it wasn't",
},
}
var compareInitBytecodeWithOpTests = []struct {
name string
contractMetadataEth remoteContractMetadata
initCodeShouldMatch bool
}{
{
name: "Safe_v130 Init Bytecode Should Match",
contractMetadataEth: remoteContractMetadata{
remoteContract: remoteContract{
Name: "Safe_v130",
Verified: true,
Deployments: deployments{
Op: common.HexToAddress("0xd9Db270c1B5E3Bd161E8c8503c55cEABeE709552"),
Eth: common.HexToAddress("0x69f4D1788e39c87893C980c06EdF4b7f686e2938"),
},
DeploymentSalt: "0000000000000000000000000000000000000000000000000000000000000000",
Deployer: common.Address{},
ABI: "",
InitBytecode: "",
},
Package: "bindings",
InitBin: Safe_v130InitBytecodeNoSalt,
DeployedBin: "",
},
initCodeShouldMatch: true,
},
{
name: "Safe_v130 Compare Init Bytecode Only On OP",
contractMetadataEth: remoteContractMetadata{
remoteContract: remoteContract{
Name: "Safe_v130",
Verified: true,
Deployments: deployments{
Op: common.HexToAddress("0x69f4D1788e39c87893C980c06EdF4b7f686e2938"),
},
DeploymentSalt: "0000000000000000000000000000000000000000000000000000000000000000",
Deployer: common.Address{},
ABI: "",
InitBytecode: "",
},
Package: "bindings",
InitBin: Safe_v130InitBytecodeNoSalt,
DeployedBin: "",
},
initCodeShouldMatch: true,
},
{
name: "Create2Deployer's Init Bytecode Should Not Match",
contractMetadataEth: remoteContractMetadata{
remoteContract: remoteContract{
Name: "Create2Deployer",
Verified: true,
Deployments: deployments{
Op: common.HexToAddress("0x13b0D85CcB8bf860b6b79AF3029fCA081AE9beF2"),
Eth: common.HexToAddress("0xF49600926c7109BD66Ab97a2c036bf696e58Dbc2"),
},
Deployer: common.Address{},
ABI: "",
InitBytecode: "",
},
Package: "bindings",
InitBin: Create2DeployerInitBytecode,
DeployedBin: Create2DeployerDeployedBytecode,
},
initCodeShouldMatch: false,
},
}
var compareInitBytecodeWithOpTestsFailures = []struct {
name string
contractMetadataEth remoteContractMetadata
initCodeShouldMatch bool
expectedError string
}{
{
name: "Safe_v130 Mismatch Init Bytecode",
contractMetadataEth: remoteContractMetadata{
remoteContract: remoteContract{
Name: "Safe_v130",
Verified: true,
Deployments: deployments{
Op: common.HexToAddress("0xd9Db270c1B5E3Bd161E8c8503c55cEABeE709552"),
Eth: common.HexToAddress("0x69f4D1788e39c87893C980c06EdF4b7f686e2938"),
},
DeploymentSalt: "0000000000000000000000000000000000000000000000000000000000000000",
Deployer: common.Address{},
ABI: "",
InitBytecode: "",
},
Package: "bindings",
InitBin: Permit2InitBytecodeNoSalt,
DeployedBin: "",
},
initCodeShouldMatch: true,
expectedError: "expected initialization bytecode to match on Ethereum and Optimism, but it doesn't.",
},
{
name: "Safe_v130 No Deployment on Optimism",
contractMetadataEth: remoteContractMetadata{
remoteContract: remoteContract{
Name: "Safe_v130",
Verified: true,
Deployments: deployments{
Eth: common.HexToAddress("0x69f4D1788e39c87893C980c06EdF4b7f686e2938"),
},
DeploymentSalt: "0000000000000000000000000000000000000000000000000000000000000000",
Deployer: common.Address{},
ABI: "",
InitBytecode: "",
},
Package: "bindings",
InitBin: Safe_v130InitBytecode,
DeployedBin: Safe_v130DeployedBytecode,
},
initCodeShouldMatch: true,
expectedError: "no deployment address on Optimism provided for Safe_v130",
},
{
name: "MultiCall3 Expected Init Code Not to Match, but it Does",
contractMetadataEth: remoteContractMetadata{
remoteContract: remoteContract{
Name: "MultiCall3",
Verified: true,
Deployments: deployments{
Op: common.HexToAddress("0xcA11bde05977b3631167028862bE2a173976CA11"),
Eth: common.HexToAddress("0xcA11bde05977b3631167028862bE2a173976CA11"),
},
Deployer: common.Address{},
ABI: "",
InitBytecode: "",
},
Package: "bindings",
InitBin: MultiCall3InitBytecode,
DeployedBin: MultiCall3DeployedBytecode,
},
initCodeShouldMatch: false,
expectedError: "expected initialization bytecode on Ethereum to not match on Optimism, but it did.",
},
{
name: "Safe_v130 No Init Bytecode Provided",
contractMetadataEth: remoteContractMetadata{
remoteContract: remoteContract{
Name: "Safe_v130",
Verified: true,
Deployments: deployments{
Op: common.HexToAddress("0xd9Db270c1B5E3Bd161E8c8503c55cEABeE709552"),
Eth: common.HexToAddress("0x69f4D1788e39c87893C980c06EdF4b7f686e2938"),
},
DeploymentSalt: "0000000000000000000000000000000000000000000000000000000000000000",
Deployer: common.Address{},
ABI: "",
InitBytecode: "",
},
Package: "bindings",
InitBin: "",
DeployedBin: Safe_v130DeployedBytecode,
},
initCodeShouldMatch: false,
expectedError: "no initialization bytecode provided for ETH deployment for comparison",
},
}
var compareDeployedBytecodeWithOpTests = []struct {
name string
contractMetadataEth remoteContractMetadata
deployedCodeShouldMatch bool
}{
{
name: "Safe_v130 Deployed Bytecode Should Match",
contractMetadataEth: remoteContractMetadata{
remoteContract: remoteContract{
Name: "Safe_v130",
Verified: true,
Deployments: deployments{
Op: common.HexToAddress("0xd9Db270c1B5E3Bd161E8c8503c55cEABeE709552"),
Eth: common.HexToAddress("0x69f4D1788e39c87893C980c06EdF4b7f686e2938"),
},
DeploymentSalt: "0000000000000000000000000000000000000000000000000000000000000000",
Deployer: common.Address{},
ABI: "",
InitBytecode: "",
},
Package: "bindings",
InitBin: "",
DeployedBin: Safe_v130DeployedBytecode,
},
deployedCodeShouldMatch: true,
},
{
name: "Safe_v130 Compare Deployed Bytecode Only On OP",
contractMetadataEth: remoteContractMetadata{
remoteContract: remoteContract{
Name: "Safe_v130",
Verified: true,
Deployments: deployments{
Op: common.HexToAddress("0x69f4D1788e39c87893C980c06EdF4b7f686e2938"),
},
DeploymentSalt: "0000000000000000000000000000000000000000000000000000000000000000",
Deployer: common.Address{},
ABI: "",
InitBytecode: "",
},
Package: "bindings",
InitBin: Safe_v130InitBytecodeNoSalt,
DeployedBin: Safe_v130DeployedBytecode,
},
deployedCodeShouldMatch: true,
},
{
name: "Permit2's Deployed Bytecode Should Not Match",
contractMetadataEth: remoteContractMetadata{
remoteContract: remoteContract{
Name: "Permit2",
Verified: true,
Deployments: deployments{
Op: common.HexToAddress("0x000000000022D473030F116dDEE9F6B43aC78BA3"),
Eth: common.HexToAddress("0x000000000022D473030F116dDEE9F6B43aC78BA3"),
},
Deployer: common.Address{},
ABI: "",
InitBytecode: "",
},
Package: "bindings",
InitBin: Permit2InitBytecode,
DeployedBin: Permit2DeployedBytecode,
},
deployedCodeShouldMatch: false,
},
}
var compareDeployedBytecodeWithOpTestsFailures = []struct {
name string
contractMetadataEth remoteContractMetadata
deployedCodeShouldMatch bool
expectedError string
}{
{
name: "Safe_v130 Mismatch Deplolyed Bytecode",
contractMetadataEth: remoteContractMetadata{
remoteContract: remoteContract{
Name: "Safe_v130",
Verified: true,
Deployments: deployments{
Op: common.HexToAddress("0xd9Db270c1B5E3Bd161E8c8503c55cEABeE709552"),
Eth: common.HexToAddress("0x69f4D1788e39c87893C980c06EdF4b7f686e2938"),
},
DeploymentSalt: "0000000000000000000000000000000000000000000000000000000000000000",
Deployer: common.Address{},
ABI: "",
InitBytecode: "",
},
Package: "bindings",
InitBin: "",
DeployedBin: Permit2DeployedBytecode,
},
deployedCodeShouldMatch: true,
expectedError: "expected deployed bytecode to match on Ethereum and Optimism, but it doesn't.",
},
{
name: "Safe_v130 No Deployment on Optimism",
contractMetadataEth: remoteContractMetadata{
remoteContract: remoteContract{
Name: "Safe_v130",
Verified: true,
Deployments: deployments{
Eth: common.HexToAddress("0x69f4D1788e39c87893C980c06EdF4b7f686e2938"),
},
DeploymentSalt: "0000000000000000000000000000000000000000000000000000000000000000",
Deployer: common.Address{},
ABI: "",
InitBytecode: "",
},
Package: "bindings",
InitBin: "",
DeployedBin: Permit2DeployedBytecode,
},
deployedCodeShouldMatch: true,
expectedError: "no deployment address on Optimism provided for Safe_v130",
},
{
name: "Safe_v130 Expected Deployed Code Not to Match, but it Does",
contractMetadataEth: remoteContractMetadata{
remoteContract: remoteContract{
Name: "Safe_v130",
Verified: true,
Deployments: deployments{
Op: common.HexToAddress("0xd9Db270c1B5E3Bd161E8c8503c55cEABeE709552"),
Eth: common.HexToAddress("0x69f4D1788e39c87893C980c06EdF4b7f686e2938"),
},
DeploymentSalt: "0000000000000000000000000000000000000000000000000000000000000000",
Deployer: common.Address{},
ABI: "",
InitBytecode: "",
},
Package: "bindings",
InitBin: Safe_v130InitBytecode,
DeployedBin: Safe_v130DeployedBytecode,
},
deployedCodeShouldMatch: false,
expectedError: "expected deployed bytecode on Ethereum to not match on Optimism, but it does.",
},
{
name: "Safe_v130 No Deployed Bytecode Provided",
contractMetadataEth: remoteContractMetadata{
remoteContract: remoteContract{
Name: "Safe_v130",
Verified: true,
Deployments: deployments{
Op: common.HexToAddress("0xd9Db270c1B5E3Bd161E8c8503c55cEABeE709552"),
Eth: common.HexToAddress("0x69f4D1788e39c87893C980c06EdF4b7f686e2938"),
},
DeploymentSalt: "0000000000000000000000000000000000000000000000000000000000000000",
Deployer: common.Address{},
ABI: "",
InitBytecode: "",
},
Package: "bindings",
InitBin: Safe_v130InitBytecode,
DeployedBin: "",
},
deployedCodeShouldMatch: false,
expectedError: "no deployed bytecode provided for ETH deployment for comparison",
},
}
var compareDeployedBytecodeWithRpcTests = []struct {
name string
contractMetadataEth remoteContractMetadata
chain string
}{
{
name: "Safe_v130 Compare Against ETH",
contractMetadataEth: remoteContractMetadata{
remoteContract: remoteContract{
Name: "Safe_v130",
Verified: true,
Deployments: deployments{
Op: common.Address{},
Eth: common.HexToAddress("0x69f4D1788e39c87893C980c06EdF4b7f686e2938"),
},
DeploymentSalt: "0000000000000000000000000000000000000000000000000000000000000000",
Deployer: common.Address{},
ABI: "",
InitBytecode: "",
},
Package: "bindings",
InitBin: "",
DeployedBin: Safe_v130DeployedBytecode,
},
chain: "eth",
},
{
name: "Safe_v130 Compare Against OP",
contractMetadataEth: remoteContractMetadata{
remoteContract: remoteContract{
Name: "Safe_v130",
Verified: true,
Deployments: deployments{
Op: common.HexToAddress("0xd9Db270c1B5E3Bd161E8c8503c55cEABeE709552"),
Eth: common.Address{},
},
DeploymentSalt: "0000000000000000000000000000000000000000000000000000000000000000",
Deployer: common.Address{},
ABI: "",
InitBytecode: "",
},
Package: "bindings",
InitBin: "",
DeployedBin: Safe_v130DeployedBytecode,
},
chain: "op",
},
}
var compareDeployedBytecodeWithRpcTestsFailures = []struct {
name string
contractMetadataEth remoteContractMetadata
chain string
expectedError string
}{
{
name: "Safe_v130 Compare Against foo",
contractMetadataEth: remoteContractMetadata{
remoteContract: remoteContract{
Name: "Safe_v130",
Verified: true,
Deployments: deployments{
Op: common.Address{},
Eth: common.HexToAddress("0x69f4D1788e39c87893C980c06EdF4b7f686e2938"),
},
DeploymentSalt: "0000000000000000000000000000000000000000000000000000000000000000",
Deployer: common.Address{},
ABI: "",
InitBytecode: "",
},
Package: "bindings",
InitBin: "",
DeployedBin: "",
},
chain: "foo",
expectedError: "unknown chain: foo, unable to retrieve a RPC client",
},
{
name: "Safe_v130 Bytecode Mismatch",
contractMetadataEth: remoteContractMetadata{
remoteContract: remoteContract{
Name: "Safe_v130",
Verified: true,
Deployments: deployments{
Op: common.Address{},
Eth: common.HexToAddress("0x69f4D1788e39c87893C980c06EdF4b7f686e2938"),
},
DeploymentSalt: "0000000000000000000000000000000000000000000000000000000000000000",
Deployer: common.Address{},
ABI: "",
InitBytecode: "",
},
Package: "bindings",
InitBin: "",
DeployedBin: Permit2DeployedBytecode,
},
chain: "eth",
expectedError: "Safe_v130 deployment bytecode from RPC doesn't match bytecode from Etherscan.",
},
}
package main
package bindgen
import (
"encoding/json"
......@@ -15,10 +15,10 @@ import (
"github.com/ethereum-optimism/optimism/op-bindings/foundry"
)
type bindGenGeneratorLocal struct {
bindGenGeneratorBase
sourceMapsList string
forgeArtifactsPath string
type BindGenGeneratorLocal struct {
BindGenGeneratorBase
SourceMapsList string
ForgeArtifactsPath string
}
type localContractMetadata struct {
......@@ -30,33 +30,33 @@ type localContractMetadata struct {
HasImmutableReferences bool
}
func (generator *bindGenGeneratorLocal) generateBindings() error {
contracts, err := readContractList(generator.logger, generator.contractsListPath)
func (generator *BindGenGeneratorLocal) GenerateBindings() error {
contracts, err := readContractList(generator.Logger, generator.ContractsListPath)
if err != nil {
return fmt.Errorf("error reading contract list %s: %w", generator.contractsListPath, err)
return fmt.Errorf("error reading contract list %s: %w", generator.ContractsListPath, err)
}
if len(contracts.Local) == 0 {
return fmt.Errorf("no contracts parsed from given contract list: %s", generator.contractsListPath)
return fmt.Errorf("no contracts parsed from given contract list: %s", generator.ContractsListPath)
}
return generator.processContracts(contracts.Local)
}
func (generator *bindGenGeneratorLocal) processContracts(contracts []string) error {
tempArtifactsDir, err := mkTempArtifactsDir(generator.logger)
func (generator *BindGenGeneratorLocal) processContracts(contracts []string) error {
tempArtifactsDir, err := mkTempArtifactsDir(generator.Logger)
if err != nil {
return err
}
defer func() {
err := os.RemoveAll(tempArtifactsDir)
if err != nil {
generator.logger.Error("Error removing temporary artifact directory", "path", tempArtifactsDir, "err", err.Error())
generator.Logger.Error("Error removing temporary artifact directory", "path", tempArtifactsDir, "err", err.Error())
} else {
generator.logger.Debug("Successfully removed temporary artifact directory")
generator.Logger.Debug("Successfully removed temporary artifact directory")
}
}()
sourceMapsList := strings.Split(generator.sourceMapsList, ",")
sourceMapsList := strings.Split(generator.SourceMapsList, ",")
sourceMapsSet := make(map[string]struct{})
for _, k := range sourceMapsList {
sourceMapsSet[k] = struct{}{}
......@@ -70,19 +70,19 @@ func (generator *bindGenGeneratorLocal) processContracts(contracts []string) err
contractMetadataFileTemplate := template.Must(template.New("localContractMetadata").Parse(localContractMetadataTemplate))
for _, contractName := range contracts {
generator.logger.Info("Generating bindings and metadata for local contract", "contract", contractName)
generator.Logger.Info("Generating bindings and metadata for local contract", "contract", contractName)
forgeArtifact, err := generator.readForgeArtifact(contractName, contractArtifactPaths)
if err != nil {
return err
}
abiFilePath, bytecodeFilePath, err := writeContractArtifacts(generator.logger, tempArtifactsDir, contractName, forgeArtifact.Abi, []byte(forgeArtifact.Bytecode.Object.String()))
abiFilePath, bytecodeFilePath, err := writeContractArtifacts(generator.Logger, tempArtifactsDir, contractName, forgeArtifact.Abi, []byte(forgeArtifact.Bytecode.Object.String()))
if err != nil {
return err
}
err = genContractBindings(generator.logger, generator.monorepoBasePath, abiFilePath, bytecodeFilePath, generator.bindingsPackageName, contractName)
err = genContractBindings(generator.Logger, generator.MonorepoBasePath, abiFilePath, bytecodeFilePath, generator.BindingsPackageName, contractName)
if err != nil {
return err
}
......@@ -104,7 +104,7 @@ func (generator *bindGenGeneratorLocal) processContracts(contracts []string) err
Name: contractName,
StorageLayout: canonicalStorageStr,
DeployedBin: forgeArtifact.DeployedBytecode.Object.String(),
Package: generator.bindingsPackageName,
Package: generator.BindingsPackageName,
DeployedSourceMap: deployedSourceMap,
HasImmutableReferences: hasImmutables,
}
......@@ -117,14 +117,14 @@ func (generator *bindGenGeneratorLocal) processContracts(contracts []string) err
return nil
}
func (generator *bindGenGeneratorLocal) getContractArtifactPaths() (map[string]string, error) {
func (generator *BindGenGeneratorLocal) getContractArtifactPaths() (map[string]string, error) {
// If some contracts have the same name then the path to their
// artifact depends on their full import path. Scan over all artifacts
// and hold a mapping from the contract name to the contract path.
// Walk walks the directory deterministically, so the earliest instance
// of the contract with the same name will be used
artifactPaths := make(map[string]string)
if err := filepath.Walk(generator.forgeArtifactsPath,
if err := filepath.Walk(generator.ForgeArtifactsPath,
func(path string, info os.FileInfo, err error) error {
if err != nil {
return err
......@@ -141,7 +141,7 @@ func (generator *bindGenGeneratorLocal) getContractArtifactPaths() (map[string]s
if !ok {
artifactPaths[sanitized] = path
} else {
generator.logger.Warn("Multiple versions of forge artifacts exist, using lesser version", "contract", sanitized)
generator.Logger.Warn("Multiple versions of forge artifacts exist, using lesser version", "contract", sanitized)
}
}
return nil
......@@ -152,13 +152,13 @@ func (generator *bindGenGeneratorLocal) getContractArtifactPaths() (map[string]s
return artifactPaths, nil
}
func (generator *bindGenGeneratorLocal) readForgeArtifact(contractName string, contractArtifactPaths map[string]string) (foundry.Artifact, error) {
func (generator *BindGenGeneratorLocal) readForgeArtifact(contractName string, contractArtifactPaths map[string]string) (foundry.Artifact, error) {
var forgeArtifact foundry.Artifact
contractArtifactPath := path.Join(generator.forgeArtifactsPath, contractName+".sol", contractName+".json")
contractArtifactPath := path.Join(generator.ForgeArtifactsPath, contractName+".sol", contractName+".json")
forgeArtifactRaw, err := os.ReadFile(contractArtifactPath)
if errors.Is(err, os.ErrNotExist) {
generator.logger.Debug("Cannot find forge-artifact at standard path, trying provided path", "contract", contractName, "standardPath", contractArtifactPath, "providedPath", contractArtifactPaths[contractName])
generator.Logger.Debug("Cannot find forge-artifact at standard path, trying provided path", "contract", contractName, "standardPath", contractArtifactPath, "providedPath", contractArtifactPaths[contractName])
contractArtifactPath = contractArtifactPaths[contractName]
forgeArtifactRaw, err = os.ReadFile(contractArtifactPath)
if errors.Is(err, os.ErrNotExist) {
......@@ -166,7 +166,7 @@ func (generator *bindGenGeneratorLocal) readForgeArtifact(contractName string, c
}
}
generator.logger.Debug("Using forge-artifact", "path", contractArtifactPath)
generator.Logger.Debug("Using forge-artifact", "path", contractArtifactPath)
if err := json.Unmarshal(forgeArtifactRaw, &forgeArtifact); err != nil {
return forgeArtifact, fmt.Errorf("failed to parse forge artifact of %q: %w", contractName, err)
}
......@@ -174,9 +174,9 @@ func (generator *bindGenGeneratorLocal) readForgeArtifact(contractName string, c
return forgeArtifact, nil
}
func (generator *bindGenGeneratorLocal) canonicalizeStorageLayout(forgeArtifact foundry.Artifact, sourceMapsSet map[string]struct{}, contractName string) (string, string, error) {
func (generator *BindGenGeneratorLocal) canonicalizeStorageLayout(forgeArtifact foundry.Artifact, sourceMapsSet map[string]struct{}, contractName string) (string, string, error) {
artifactStorageStruct := forgeArtifact.StorageLayout
canonicalStorageStruct := ast.CanonicalizeASTIDs(&artifactStorageStruct, generator.monorepoBasePath)
canonicalStorageStruct := ast.CanonicalizeASTIDs(&artifactStorageStruct, generator.MonorepoBasePath)
canonicalStorageJson, err := json.Marshal(canonicalStorageStruct)
if err != nil {
return "", "", fmt.Errorf("error marshaling canonical storage: %w", err)
......@@ -191,8 +191,8 @@ func (generator *bindGenGeneratorLocal) canonicalizeStorageLayout(forgeArtifact
return deployedSourceMap, canonicalStorageStr, nil
}
func (generator *bindGenGeneratorLocal) writeContractMetadata(contractMetaData localContractMetadata, contractName string, fileTemplate *template.Template) error {
metadataFilePath := filepath.Join(generator.metadataOut, strings.ToLower(contractName)+"_more.go")
func (generator *BindGenGeneratorLocal) writeContractMetadata(contractMetaData localContractMetadata, contractName string, fileTemplate *template.Template) error {
metadataFilePath := filepath.Join(generator.MetadataOut, strings.ToLower(contractName)+"_more.go")
metadataFile, err := os.OpenFile(
metadataFilePath,
os.O_RDWR|os.O_CREATE|os.O_TRUNC,
......@@ -207,7 +207,7 @@ func (generator *bindGenGeneratorLocal) writeContractMetadata(contractMetaData l
return fmt.Errorf("error writing %s's contract metadata at %s: %w", contractName, metadataFilePath, err)
}
generator.logger.Debug("Successfully wrote contract metadata", "contract", contractName, "path", metadataFilePath)
generator.Logger.Debug("Successfully wrote contract metadata", "contract", contractName, "path", metadataFilePath)
return nil
}
......
package main
package bindgen
import (
"context"
......@@ -10,15 +10,15 @@ import (
"github.com/ethereum/go-ethereum/ethclient"
)
type bindGenGeneratorRemote struct {
bindGenGeneratorBase
contractDataClients struct {
eth contractDataClient
op contractDataClient
type BindGenGeneratorRemote struct {
BindGenGeneratorBase
ContractDataClients struct {
Eth contractDataClient
Op contractDataClient
}
rpcClients struct {
eth *ethclient.Client
op *ethclient.Client
RpcClients struct {
Eth *ethclient.Client
Op *ethclient.Client
}
tempArtifactsDir string
}
......@@ -30,67 +30,67 @@ type contractDataClient interface {
FetchDeploymentTx(ctx context.Context, txHash string) (etherscan.Transaction, error)
}
type deployments struct {
type Deployments struct {
Eth common.Address `json:"eth"`
Op common.Address `json:"op"`
}
type remoteContract struct {
type RemoteContract struct {
Name string `json:"name"`
Verified bool `json:"verified"`
Deployments deployments `json:"deployments"`
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
type RemoteContractMetadata struct {
RemoteContract
Package string
InitBin string
DeployedBin string
}
func (generator *bindGenGeneratorRemote) generateBindings() error {
contracts, err := readContractList(generator.logger, generator.contractsListPath)
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)
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 fmt.Errorf("no contracts parsed from given contract list: %s", generator.ContractsListPath)
}
return generator.processContracts(contracts.Remote)
}
func (generator *bindGenGeneratorRemote) processContracts(contracts []remoteContract) error {
func (generator *BindGenGeneratorRemote) processContracts(contracts []RemoteContract) error {
var err error
generator.tempArtifactsDir, err = mkTempArtifactsDir(generator.logger)
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())
generator.Logger.Error("Error removing temporary artifact directory", "path", generator.tempArtifactsDir, "err", err.Error())
} else {
generator.logger.Debug("Successfully removed temporary artifact directory")
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)
generator.Logger.Info("Generating bindings and metadata for remote contract", "contract", contract.Name)
contractMetadata := remoteContractMetadata{
remoteContract: remoteContract{
contractMetadata := RemoteContractMetadata{
RemoteContract: RemoteContract{
Name: contract.Name,
Deployments: contract.Deployments,
DeploymentSalt: contract.DeploymentSalt,
ABI: contract.ABI,
Verified: contract.Verified,
},
Package: generator.bindingsPackageName,
Package: generator.BindingsPackageName,
}
var err error
......
package main
package bindgen
import (
"bytes"
......@@ -15,57 +15,57 @@ import (
"github.com/ethereum/go-ethereum/ethclient"
)
type contractData struct {
abi string
deployedBin string
deploymentTx etherscan.Transaction
type ContractData struct {
Abi string
DeployedBin string
DeploymentTx etherscan.Transaction
}
func (generator *bindGenGeneratorRemote) standardHandler(contractMetadata *remoteContractMetadata) error {
fetchedData, err := generator.fetchContractData(contractMetadata.Verified, "eth", contractMetadata.Deployments.Eth.Hex())
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 err = generator.compareDeployedBytecodeWithRpc(contractMetadata, "eth"); err != nil {
contractMetadata.DeployedBin = fetchedData.DeployedBin
if err = generator.CompareDeployedBytecodeWithRpc(contractMetadata, "eth"); err != nil {
return err
}
if err = generator.compareDeployedBytecodeWithRpc(contractMetadata, "op"); err != nil {
if err = generator.CompareDeployedBytecodeWithRpc(contractMetadata, "op"); err != nil {
return err
}
// 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)
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 {
if contractMetadata.InitBin, err = generator.removeDeploymentSalt(fetchedData.DeploymentTx.Input, contractMetadata.DeploymentSalt); err != nil {
return err
}
if err := generator.compareInitBytecodeWithOp(contractMetadata, true); err != nil {
if err := generator.CompareInitBytecodeWithOp(contractMetadata, true); err != nil {
return fmt.Errorf("%s: %w", contractMetadata.Name, err)
}
if err := generator.compareDeployedBytecodeWithOp(contractMetadata, true); err != nil {
if err := generator.CompareDeployedBytecodeWithOp(contractMetadata, true); err != nil {
return fmt.Errorf("%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())
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 {
contractMetadata.ABI = fetchedData.Abi
contractMetadata.DeployedBin = fetchedData.DeployedBin
if contractMetadata.InitBin, err = generator.removeDeploymentSalt(fetchedData.DeploymentTx.Input, contractMetadata.DeploymentSalt); err != nil {
return err
}
......@@ -73,135 +73,135 @@ func (generator *bindGenGeneratorRemote) create2DeployerHandler(contractMetadata
// 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
if err := generator.compareInitBytecodeWithOp(contractMetadata, false); err != nil {
if err := generator.CompareInitBytecodeWithOp(contractMetadata, false); err != nil {
return fmt.Errorf("%s: %w", contractMetadata.Name, err)
}
if err := generator.compareDeployedBytecodeWithOp(contractMetadata, false); err != nil {
if err := generator.CompareDeployedBytecodeWithOp(contractMetadata, false); err != nil {
return fmt.Errorf("%s: %w", contractMetadata.Name, err)
}
return generator.writeAllOutputs(contractMetadata, remoteContractMetadataTemplate)
}
func (generator *bindGenGeneratorRemote) multiSendHandler(contractMetadata *remoteContractMetadata) error {
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())
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 err = generator.compareDeployedBytecodeWithRpc(contractMetadata, "op"); err != nil {
contractMetadata.ABI = fetchedData.Abi
contractMetadata.DeployedBin = fetchedData.DeployedBin
if err = generator.CompareDeployedBytecodeWithRpc(contractMetadata, "op"); err != nil {
return err
}
if contractMetadata.InitBin, err = generator.removeDeploymentSalt(fetchedData.deploymentTx.Input, contractMetadata.DeploymentSalt); err != nil {
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 {
func (generator *BindGenGeneratorRemote) senderCreatorHandler(contractMetadata *RemoteContractMetadata) error {
var err error
contractMetadata.DeployedBin, err = generator.contractDataClients.eth.FetchDeployedBytecode(context.Background(), contractMetadata.Deployments.Eth.Hex())
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)
}
if err = generator.compareDeployedBytecodeWithRpc(contractMetadata, "eth"); err != nil {
if err = generator.CompareDeployedBytecodeWithRpc(contractMetadata, "eth"); err != nil {
return err
}
if err = generator.compareDeployedBytecodeWithRpc(contractMetadata, "op"); err != nil {
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.
// So, we're manually providing the initialization bytecode and therefore it isn't being compared here
if err := generator.compareInitBytecodeWithOp(contractMetadata, false); err != nil {
if err := generator.CompareInitBytecodeWithOp(contractMetadata, false); err != nil {
return fmt.Errorf("%s: %w", contractMetadata.Name, err)
}
if err := generator.compareDeployedBytecodeWithOp(contractMetadata, true); err != nil {
if err := generator.CompareDeployedBytecodeWithOp(contractMetadata, true); err != nil {
return fmt.Errorf("%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())
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
contractMetadata.DeployedBin = fetchedData.deployedBin
if contractMetadata.InitBin, err = generator.removeDeploymentSalt(fetchedData.deploymentTx.Input, contractMetadata.DeploymentSalt); err != nil {
contractMetadata.ABI = fetchedData.Abi
contractMetadata.DeployedBin = fetchedData.DeployedBin
if contractMetadata.InitBin, err = generator.removeDeploymentSalt(fetchedData.DeploymentTx.Input, contractMetadata.DeploymentSalt); err != nil {
return err
}
if !strings.EqualFold(contractMetadata.Deployer.Hex(), fetchedData.deploymentTx.To) {
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,
fetchedData.DeploymentTx.To,
)
}
if err := generator.compareInitBytecodeWithOp(contractMetadata, true); err != nil {
if err := generator.CompareInitBytecodeWithOp(contractMetadata, true); err != nil {
return fmt.Errorf("%s: %w", contractMetadata.Name, err)
}
// We're asserting the deployed bytecode doesn't match, because Permit2 has immutable Solidity variables that
// are dependent on block.chainid
if err := generator.compareDeployedBytecodeWithOp(contractMetadata, false); err != nil {
if err := generator.CompareDeployedBytecodeWithOp(contractMetadata, false); err != nil {
return fmt.Errorf("%s: %w", contractMetadata.Name, err)
}
return generator.writeAllOutputs(contractMetadata, permit2MetadataTemplate)
}
func (generator *bindGenGeneratorRemote) fetchContractData(contractVerified bool, chain, deploymentAddress string) (contractData, error) {
var data contractData
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
client = generator.ContractDataClients.Eth
case "op":
client = generator.contractDataClients.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)
data.Abi, err = client.FetchAbi(context.Background(), deploymentAddress)
if err != nil {
return contractData{}, fmt.Errorf("error fetching ABI: %w", err)
return ContractData{}, fmt.Errorf("error fetching ABI: %w", err)
}
}
data.deployedBin, err = client.FetchDeployedBytecode(context.Background(), deploymentAddress)
data.DeployedBin, err = client.FetchDeployedBytecode(context.Background(), deploymentAddress)
if err != nil {
return contractData{}, fmt.Errorf("error fetching deployed bytecode: %w", err)
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)
return ContractData{}, fmt.Errorf("error fetching deployment transaction hash: %w", err)
}
data.deploymentTx, err = client.FetchDeploymentTx(context.Background(), deploymentTxHash)
data.DeploymentTx, err = client.FetchDeploymentTx(context.Background(), deploymentTxHash)
if err != nil {
return contractData{}, fmt.Errorf("error fetching deployment transaction data: %w", err)
return ContractData{}, fmt.Errorf("error fetching deployment transaction data: %w", err)
}
return data, nil
}
func (generator *bindGenGeneratorRemote) removeDeploymentSalt(deploymentData, deploymentSalt string) (string, error) {
func (generator *BindGenGeneratorRemote) removeDeploymentSalt(deploymentData, deploymentSalt string) (string, error) {
if deploymentSalt == "" {
return deploymentData, nil
}
......@@ -219,7 +219,7 @@ func (generator *bindGenGeneratorRemote) removeDeploymentSalt(deploymentData, de
return re.ReplaceAllString(deploymentData, ""), nil
}
func (generator *bindGenGeneratorRemote) compareInitBytecodeWithOp(contractMetadataEth *remoteContractMetadata, initCodeShouldMatch bool) error {
func (generator *BindGenGeneratorRemote) CompareInitBytecodeWithOp(contractMetadataEth *RemoteContractMetadata, initCodeShouldMatch bool) error {
if contractMetadataEth.InitBin == "" {
return fmt.Errorf("no initialization bytecode provided for ETH deployment for comparison")
}
......@@ -230,36 +230,36 @@ func (generator *bindGenGeneratorRemote) compareInitBytecodeWithOp(contractMetad
}
// 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())
opContractData, err := generator.FetchContractData(false, "op", contractMetadataEth.Deployments.Op.Hex())
if err != nil {
return err
}
if opContractData.deploymentTx.Input, err = generator.removeDeploymentSalt(opContractData.deploymentTx.Input, contractMetadataEth.DeploymentSalt); err != nil {
if opContractData.DeploymentTx.Input, err = generator.removeDeploymentSalt(opContractData.DeploymentTx.Input, contractMetadataEth.DeploymentSalt); err != nil {
return err
}
initCodeComparison := strings.EqualFold(contractMetadataEth.InitBin, opContractData.deploymentTx.Input)
initCodeComparison := strings.EqualFold(contractMetadataEth.InitBin, opContractData.DeploymentTx.Input)
if initCodeShouldMatch && !initCodeComparison {
return fmt.Errorf(
"expected initialization bytecode to match on Ethereum and Optimism, but it doesn't. contract=%s bytecodeEth=%s bytecodeOp=%s",
contractMetadataEth.Name,
contractMetadataEth.InitBin,
opContractData.deploymentTx.Input,
opContractData.DeploymentTx.Input,
)
} else if !initCodeShouldMatch && initCodeComparison {
return fmt.Errorf(
"expected initialization bytecode on Ethereum to not match on Optimism, but it did. contract=%s bytecodeEth=%s bytecodeOp=%s",
contractMetadataEth.Name,
contractMetadataEth.InitBin,
opContractData.deploymentTx.Input,
opContractData.DeploymentTx.Input,
)
}
return nil
}
func (generator *bindGenGeneratorRemote) compareDeployedBytecodeWithOp(contractMetadataEth *remoteContractMetadata, deployedCodeShouldMatch bool) error {
func (generator *BindGenGeneratorRemote) CompareDeployedBytecodeWithOp(contractMetadataEth *RemoteContractMetadata, deployedCodeShouldMatch bool) error {
if contractMetadataEth.DeployedBin == "" {
return fmt.Errorf("no deployed bytecode provided for ETH deployment for comparison")
}
......@@ -270,38 +270,38 @@ func (generator *bindGenGeneratorRemote) compareDeployedBytecodeWithOp(contractM
}
// 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())
opContractData, err := generator.FetchContractData(false, "op", contractMetadataEth.Deployments.Op.Hex())
if err != nil {
return err
}
deployedCodeComparison := strings.EqualFold(contractMetadataEth.DeployedBin, opContractData.deployedBin)
deployedCodeComparison := strings.EqualFold(contractMetadataEth.DeployedBin, opContractData.DeployedBin)
if deployedCodeShouldMatch && !deployedCodeComparison {
return fmt.Errorf(
"expected deployed bytecode to match on Ethereum and Optimism, but it doesn't. contract=%s bytecodeEth=%s bytecodeOp=%s",
contractMetadataEth.Name,
contractMetadataEth.DeployedBin,
opContractData.deployedBin,
opContractData.DeployedBin,
)
} else if !deployedCodeShouldMatch && deployedCodeComparison {
return fmt.Errorf(
"expected deployed bytecode on Ethereum to not match on Optimism, but it does. contract=%s bytecodeEth=%s bytecodeOp=%s",
contractMetadataEth.Name,
contractMetadataEth.DeployedBin,
opContractData.deployedBin,
opContractData.DeployedBin,
)
}
return nil
}
func (generator *bindGenGeneratorRemote) compareDeployedBytecodeWithRpc(contractMetadata *remoteContractMetadata, chain string) error {
func (generator *BindGenGeneratorRemote) CompareDeployedBytecodeWithRpc(contractMetadata *RemoteContractMetadata, chain string) error {
var client *ethclient.Client
switch chain {
case "eth":
client = generator.rpcClients.eth
client = generator.RpcClients.Eth
case "op":
client = generator.rpcClients.op
client = generator.RpcClients.Op
default:
return fmt.Errorf("unknown chain: %s, unable to retrieve a RPC client", chain)
}
......@@ -313,7 +313,7 @@ func (generator *bindGenGeneratorRemote) compareDeployedBytecodeWithRpc(contract
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)
generator.Logger.Warn("Unable to compare bytecode from Etherscan against RPC client, no deployment address provided for chain", "chain", chain)
}
if deployment != (common.Address{}) {
......@@ -330,28 +330,28 @@ func (generator *bindGenGeneratorRemote) compareDeployedBytecodeWithRpc(contract
return nil
}
func (generator *bindGenGeneratorRemote) writeAllOutputs(contractMetadata *remoteContractMetadata, fileTemplate string) error {
func (generator *BindGenGeneratorRemote) writeAllOutputs(contractMetadata *RemoteContractMetadata, fileTemplate string) error {
abiFilePath, bytecodeFilePath, err := writeContractArtifacts(
generator.logger, generator.tempArtifactsDir, contractMetadata.Name,
generator.Logger, generator.tempArtifactsDir, contractMetadata.Name,
[]byte(contractMetadata.ABI), []byte(contractMetadata.InitBin),
)
if err != nil {
return err
}
err = genContractBindings(generator.logger, generator.monorepoBasePath, abiFilePath, bytecodeFilePath, generator.bindingsPackageName, contractMetadata.Name)
err = genContractBindings(generator.Logger, generator.MonorepoBasePath, abiFilePath, bytecodeFilePath, generator.BindingsPackageName, contractMetadata.Name)
if err != nil {
return err
}
return generator.writeContractMetadata(
contractMetadata,
template.Must(template.New("remoteContractMetadata").Parse(fileTemplate)),
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")
func (generator *BindGenGeneratorRemote) writeContractMetadata(contractMetadata *RemoteContractMetadata, fileTemplate *template.Template) error {
metadataFilePath := filepath.Join(generator.MetadataOut, strings.ToLower(contractMetadata.Name)+"_more.go")
var existingOutput []byte
if _, err := os.Stat(metadataFilePath); err == nil {
......@@ -383,15 +383,15 @@ func (generator *bindGenGeneratorRemote) writeContractMetadata(contractMetadata
}
if bytes.Equal(existingOutput, newOutput) {
generator.logger.Debug("No changes detected in the contract metadata", "contract", contractMetadata.Name)
generator.Logger.Debug("No changes detected in the contract metadata", "contract", contractMetadata.Name)
} else {
generator.logger.Warn("Changes detected in the contract metadata, old metadata has been overwritten", "contract", contractMetadata.Name)
generator.Logger.Warn("Changes detected in the contract metadata, old metadata has been overwritten", "contract", contractMetadata.Name)
}
} else {
generator.logger.Debug("No existing contract metadata found, skipping comparison", "contract", contractMetadata.Name)
generator.Logger.Debug("No existing contract metadata found, skipping comparison", "contract", contractMetadata.Name)
}
generator.logger.Debug("Successfully wrote contract metadata", "contract", contractMetadata.Name, "path", metadataFilePath)
generator.Logger.Debug("Successfully wrote contract metadata", "contract", contractMetadata.Name, "path", metadataFilePath)
return nil
}
......
package main
package bindgen
import (
"fmt"
"os"
"reflect"
"strings"
"testing"
"github.com/ethereum-optimism/optimism/op-bindings/etherscan"
"github.com/ethereum/go-ethereum/ethclient"
"github.com/stretchr/testify/require"
)
var generator bindGenGeneratorRemote = bindGenGeneratorRemote{}
func configureGenerator(t *testing.T) error {
if os.Getenv("RUN_E2E") == "" {
t.Log("Not running test, RUN_E2E env not set")
t.Skip()
}
generator.contractDataClients.eth = etherscan.NewEthereumClient(os.Getenv("ETHERSCAN_APIKEY_ETH"))
generator.contractDataClients.op = etherscan.NewOptimismClient(os.Getenv("ETHERSCAN_APIKEY_OP"))
var err error
if generator.rpcClients.eth, err = ethclient.Dial(os.Getenv("RPC_URL_ETH")); err != nil {
return fmt.Errorf("error initializing Ethereum client: %w", err)
}
if generator.rpcClients.op, err = ethclient.Dial(os.Getenv("RPC_URL_OP")); err != nil {
return fmt.Errorf("error initializing Optimism client: %w", err)
}
return nil
}
func TestFetchContractData(t *testing.T) {
if err := configureGenerator(t); err != nil {
t.Error(err)
}
for _, tt := range fetchContractDataTests {
t.Run(tt.name, func(t *testing.T) {
contractData, err := generator.fetchContractData(tt.contractVerified, tt.chain, tt.deploymentAddress)
if err != nil {
t.Error(err)
}
if !reflect.DeepEqual(contractData, tt.expectedContractData) {
t.Errorf("Retrieved contract data doesn't match expected. Expected: %s Retrieved: %s", tt.expectedContractData, contractData)
}
})
}
}
func TestFetchContractDataFailures(t *testing.T) {
if err := configureGenerator(t); err != nil {
t.Error(err)
}
for _, tt := range fetchContractDataTestsFailures {
t.Run(tt.name, func(t *testing.T) {
_, err := generator.fetchContractData(tt.contractVerified, tt.chain, tt.deploymentAddress)
if err == nil {
t.Errorf("Expected error: %s but didn't receive it", tt.expectedError)
return
}
if !strings.Contains(err.Error(), tt.expectedError) {
t.Errorf("Expected error: %s Received: %s", tt.expectedError, err)
return
}
})
}
}
var generator BindGenGeneratorRemote = BindGenGeneratorRemote{}
func TestRemoveDeploymentSalt(t *testing.T) {
for _, tt := range removeDeploymentSaltTests {
......@@ -90,111 +25,3 @@ func TestRemoveDeploymentSaltFailures(t *testing.T) {
})
}
}
func TestCompareInitBytecodeWithOp(t *testing.T) {
if err := configureGenerator(t); err != nil {
t.Error(err)
}
for _, tt := range compareInitBytecodeWithOpTests {
t.Run(tt.name, func(t *testing.T) {
err := generator.compareInitBytecodeWithOp(&tt.contractMetadataEth, tt.initCodeShouldMatch)
if err != nil {
t.Error(err)
}
})
}
}
func TestCompareInitBytecodeWithOpFailures(t *testing.T) {
if err := configureGenerator(t); err != nil {
t.Error(err)
}
for _, tt := range compareInitBytecodeWithOpTestsFailures {
t.Run(tt.name, func(t *testing.T) {
err := generator.compareInitBytecodeWithOp(&tt.contractMetadataEth, tt.initCodeShouldMatch)
if err == nil {
t.Errorf("Expected error: %s but didn't receive it", tt.expectedError)
return
}
if !strings.Contains(err.Error(), tt.expectedError) {
t.Errorf("Expected error: %s Received: %s", tt.expectedError, err)
return
}
})
}
}
func TestCompareDeployedBytecodeWithOp(t *testing.T) {
if err := configureGenerator(t); err != nil {
t.Error(err)
}
for _, tt := range compareDeployedBytecodeWithOpTests {
t.Run(tt.name, func(t *testing.T) {
err := generator.compareDeployedBytecodeWithOp(&tt.contractMetadataEth, tt.deployedCodeShouldMatch)
if err != nil {
t.Error(err)
}
})
}
}
func TestCompareDeployedBytecodeWithOpFailures(t *testing.T) {
if err := configureGenerator(t); err != nil {
t.Error(err)
}
for _, tt := range compareDeployedBytecodeWithOpTestsFailures {
t.Run(tt.name, func(t *testing.T) {
err := generator.compareDeployedBytecodeWithOp(&tt.contractMetadataEth, tt.deployedCodeShouldMatch)
if err == nil {
t.Errorf("Expected error: %s but didn't receive it", tt.expectedError)
return
}
if !strings.Contains(err.Error(), tt.expectedError) {
t.Errorf("Expected error: %s Received: %s", tt.expectedError, err)
return
}
})
}
}
func TestCompareDeployedBytecodeWithRpc(t *testing.T) {
if err := configureGenerator(t); err != nil {
t.Error(err)
}
for _, tt := range compareDeployedBytecodeWithRpcTests {
t.Run(tt.name, func(t *testing.T) {
err := generator.compareDeployedBytecodeWithRpc(&tt.contractMetadataEth, tt.chain)
if err != nil {
t.Error(err)
}
})
}
}
func TestCompareDeployedBytecodeWithRpcFailures(t *testing.T) {
if err := configureGenerator(t); err != nil {
t.Error(err)
}
for _, tt := range compareDeployedBytecodeWithRpcTestsFailures {
t.Run(tt.name, func(t *testing.T) {
err := generator.compareDeployedBytecodeWithRpc(&tt.contractMetadataEth, tt.chain)
if err == nil {
t.Errorf("Expected error: %s but didn't receive it", tt.expectedError)
return
}
if !strings.Contains(err.Error(), tt.expectedError) {
t.Errorf("Expected error: %s Received: %s", tt.expectedError, err)
return
}
})
}
}
package main
package bindgen
import (
"bufio"
......@@ -13,9 +13,17 @@ import (
"github.com/ethereum/go-ethereum/log"
)
type BindGenGeneratorBase struct {
MetadataOut string
BindingsPackageName string
MonorepoBasePath string
ContractsListPath string
Logger log.Logger
}
type contractsList struct {
Local []string `json:"local"`
Remote []remoteContract `json:"remote"`
Remote []RemoteContract `json:"remote"`
}
// readContractList reads a JSON file from the given `filePath` and unmarshals
......
......@@ -4,6 +4,7 @@ import (
"fmt"
"os"
"github.com/ethereum-optimism/optimism/op-bindings/bindgen"
"github.com/ethereum-optimism/optimism/op-bindings/etherscan"
op_service "github.com/ethereum-optimism/optimism/op-service"
oplog "github.com/ethereum-optimism/optimism/op-service/log"
......@@ -12,14 +13,6 @@ import (
"github.com/urfave/cli/v2"
)
type bindGenGeneratorBase struct {
metadataOut string
bindingsPackageName string
monorepoBasePath string
contractsListPath string
logger log.Logger
}
const (
// Base Flags
MetadataOutFlagName = "metadata-out"
......@@ -92,7 +85,7 @@ func generateBindings(c *cli.Context) error {
if err != nil {
return err
}
if err := localBindingsGenerator.generateBindings(); err != nil {
if err := localBindingsGenerator.GenerateBindings(); err != nil {
return fmt.Errorf("error generating local bindings: %w", err)
}
......@@ -100,7 +93,7 @@ func generateBindings(c *cli.Context) error {
if err != nil {
return err
}
if err := remoteBindingsGenerator.generateBindings(); err != nil {
if err := remoteBindingsGenerator.GenerateBindings(); err != nil {
return fmt.Errorf("error generating remote bindings: %w", err)
}
......@@ -110,7 +103,7 @@ func generateBindings(c *cli.Context) error {
if err != nil {
return err
}
if err := localBindingsGenerator.generateBindings(); err != nil {
if err := localBindingsGenerator.GenerateBindings(); err != nil {
return fmt.Errorf("error generating local bindings: %w", err)
}
return nil
......@@ -119,7 +112,7 @@ func generateBindings(c *cli.Context) error {
if err != nil {
return err
}
if err := remoteBindingsGenerator.generateBindings(); err != nil {
if err := remoteBindingsGenerator.GenerateBindings(); err != nil {
return fmt.Errorf("error generating remote bindings: %w", err)
}
return nil
......@@ -128,55 +121,55 @@ func generateBindings(c *cli.Context) error {
}
}
func parseConfigBase(logger log.Logger, c *cli.Context) (bindGenGeneratorBase, error) {
func parseConfigBase(logger log.Logger, c *cli.Context) (bindgen.BindGenGeneratorBase, error) {
cwd, err := os.Getwd()
if err != nil {
return bindGenGeneratorBase{}, err
return bindgen.BindGenGeneratorBase{}, err
}
monoRepoPath, err := op_service.FindMonorepoRoot(cwd)
if err != nil {
return bindGenGeneratorBase{}, err
return bindgen.BindGenGeneratorBase{}, err
}
return bindGenGeneratorBase{
metadataOut: c.String(MetadataOutFlagName),
bindingsPackageName: c.String(BindingsPackageNameFlagName),
monorepoBasePath: monoRepoPath,
contractsListPath: c.String(ContractsListFlagName),
logger: logger,
return bindgen.BindGenGeneratorBase{
MetadataOut: c.String(MetadataOutFlagName),
BindingsPackageName: c.String(BindingsPackageNameFlagName),
MonorepoBasePath: monoRepoPath,
ContractsListPath: c.String(ContractsListFlagName),
Logger: logger,
}, nil
}
func parseConfigLocal(logger log.Logger, c *cli.Context) (bindGenGeneratorLocal, error) {
func parseConfigLocal(logger log.Logger, c *cli.Context) (bindgen.BindGenGeneratorLocal, error) {
baseConfig, err := parseConfigBase(logger, c)
if err != nil {
return bindGenGeneratorLocal{}, err
return bindgen.BindGenGeneratorLocal{}, err
}
return bindGenGeneratorLocal{
bindGenGeneratorBase: baseConfig,
sourceMapsList: c.String(SourceMapsListFlagName),
forgeArtifactsPath: c.String(ForgeArtifactsFlagName),
return bindgen.BindGenGeneratorLocal{
BindGenGeneratorBase: baseConfig,
SourceMapsList: c.String(SourceMapsListFlagName),
ForgeArtifactsPath: c.String(ForgeArtifactsFlagName),
}, nil
}
func parseConfigRemote(logger log.Logger, c *cli.Context) (bindGenGeneratorRemote, error) {
func parseConfigRemote(logger log.Logger, c *cli.Context) (bindgen.BindGenGeneratorRemote, error) {
baseConfig, err := parseConfigBase(logger, c)
if err != nil {
return bindGenGeneratorRemote{}, err
return bindgen.BindGenGeneratorRemote{}, err
}
generator := bindGenGeneratorRemote{
bindGenGeneratorBase: baseConfig,
generator := bindgen.BindGenGeneratorRemote{
BindGenGeneratorBase: baseConfig,
}
generator.contractDataClients.eth = etherscan.NewEthereumClient(c.String(EtherscanApiKeyEthFlagName))
generator.contractDataClients.op = etherscan.NewOptimismClient(c.String(EtherscanApiKeyOpFlagName))
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.Eth, err = ethclient.Dial(c.String(RpcUrlEthFlagName)); err != nil {
return bindgen.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)
if generator.RpcClients.Op, err = ethclient.Dial(c.String(RpcUrlOpFlagName)); err != nil {
return bindgen.BindGenGeneratorRemote{}, fmt.Errorf("error initializing Optimism client: %w", err)
}
return generator, 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