add errors to emitted code.

This commit is contained in:
Jared Wasinger 2024-11-28 18:59:12 +07:00
parent 4f6ae72a66
commit c589b31073
11 changed files with 208 additions and 5 deletions

View File

@ -189,6 +189,7 @@ func bind(types []string, abis []string, bytecodes []string, fsigs []map[string]
calls = make(map[string]*tmplMethod)
transacts = make(map[string]*tmplMethod)
events = make(map[string]*tmplEvent)
errors = make(map[string]*tmplError)
fallback *tmplMethod
receive *tmplMethod
@ -304,6 +305,51 @@ func bind(types []string, abis []string, bytecodes []string, fsigs []map[string]
// Append the event to the accumulator list
events[original.Name] = &tmplEvent{Original: original, Normalized: normalized}
}
for _, original := range evmABI.Errors {
// TODO: I copied this from events. I think it should be correct but not totally sure
// even if it is correct, should consider deduplicating this into its own function.
// Normalize the event for capital cases and non-anonymous outputs
normalized := original
// Ensure there is no duplicated identifier
normalizedName := methodNormalizer(alias(aliases, original.Name))
// Name shouldn't start with a digit. It will make the generated code invalid.
if len(normalizedName) > 0 && unicode.IsDigit(rune(normalizedName[0])) {
normalizedName = fmt.Sprintf("E%s", normalizedName)
normalizedName = abi.ResolveNameConflict(normalizedName, func(name string) bool {
_, ok := eventIdentifiers[name]
return ok
})
}
if eventIdentifiers[normalizedName] {
return nil, fmt.Errorf("duplicated identifier \"%s\"(normalized \"%s\"), use --alias for renaming", original.Name, normalizedName)
}
eventIdentifiers[normalizedName] = true
normalized.Name = normalizedName
used := make(map[string]bool)
normalized.Inputs = make([]abi.Argument, len(original.Inputs))
copy(normalized.Inputs, original.Inputs)
for j, input := range normalized.Inputs {
if input.Name == "" || isKeyWord(input.Name) {
normalized.Inputs[j].Name = fmt.Sprintf("arg%d", j)
}
// Event is a bit special, we need to define event struct in binding,
// ensure there is no camel-case-style name conflict.
for index := 0; ; index++ {
if !used[capitalise(normalized.Inputs[j].Name)] {
used[capitalise(normalized.Inputs[j].Name)] = true
break
}
normalized.Inputs[j].Name = fmt.Sprintf("%s%d", normalized.Inputs[j].Name, index)
}
if hasStruct(input.Type) {
bindStructType(input.Type, structs)
}
}
errors[original.Name] = &tmplError{Original: original, Normalized: normalized}
}
// Add two special fallback functions if they exist
if evmABI.HasFallback() {
fallback = &tmplMethod{Original: evmABI.Fallback}
@ -324,6 +370,7 @@ func bind(types []string, abis []string, bytecodes []string, fsigs []map[string]
Events: events,
Libraries: make(map[string]string),
AllLibraries: make(map[string]string),
Errors: errors,
}
// Function 4-byte signatures are stored in the same sequence

View File

@ -46,6 +46,7 @@ type tmplContract struct {
Libraries map[string]string // Same as tmplData, but filtered to only keep direct deps that the contract needs
AllLibraries map[string]string // same as Libraries, but all direct/indirect library dependencies
Library bool // Indicator whether the contract is a library
Errors map[string]*tmplError
}
// tmplMethod is a wrapper around an abi.Method that contains a few preprocessed
@ -63,6 +64,11 @@ type tmplEvent struct {
Normalized abi.Event // Normalized version of the parsed fields
}
type tmplError struct {
Original abi.Error
Normalized abi.Error
}
// tmplField is a wrapper around a struct field with binding language
// struct type definition and relative filed name.
type tmplField struct {

View File

View File

@ -13,7 +13,7 @@ import (
"github.com/ethereum/go-ethereum/core/types"
)
// Reference imports to suppress errors if they are not otherwise used.
// Reference imports to suppress solc_errors if they are not otherwise used.
var (
_ = errors.New
_ = big.NewInt

View File

@ -13,7 +13,7 @@ import (
"github.com/ethereum/go-ethereum/core/types"
)
// Reference imports to suppress errors if they are not otherwise used.
// Reference imports to suppress solc_errors if they are not otherwise used.
var (
_ = errors.New
_ = big.NewInt

View File

@ -13,7 +13,7 @@ import (
"github.com/ethereum/go-ethereum/core/types"
)
// Reference imports to suppress errors if they are not otherwise used.
// Reference imports to suppress solc_errors if they are not otherwise used.
var (
_ = errors.New
_ = big.NewInt

View File

@ -0,0 +1,81 @@
// Code generated via abigen V2 - DO NOT EDIT.
// This file is a generated binding and any manual changes will be lost.
package solc_errors
import (
"errors"
"math/big"
"github.com/ethereum/go-ethereum/accounts/abi"
"github.com/ethereum/go-ethereum/accounts/abi/bind"
"github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/core/types"
)
// Reference imports to suppress errors if they are not otherwise used.
var (
_ = errors.New
_ = big.NewInt
_ = bind.Bind
_ = common.Big1
_ = types.BloomLookup
_ = abi.ConvertType
)
var CLibraryDeps = []*bind.MetaData{}
// TODO: convert this type to value type after everything works.
// CMetaData contains all meta data concerning the C contract.
var CMetaData = &bind.MetaData{
ABI: "[{\"inputs\":[{\"internalType\":\"uint256\",\"name\":\"arg1\",\"type\":\"uint256\"},{\"internalType\":\"uint256\",\"name\":\"arg2\",\"type\":\"uint256\"},{\"internalType\":\"uint256\",\"name\":\"arg3\",\"type\":\"uint256\"},{\"internalType\":\"bool\",\"name\":\"arg4\",\"type\":\"bool\"}],\"name\":\"BadThing\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"Foo\",\"outputs\":[],\"stateMutability\":\"pure\",\"type\":\"function\"}]",
Pattern: "55ef3c19a0ab1c1845f9e347540c1e51f5",
Bin: "0x6080604052348015600e575f80fd5b506101148061001c5f395ff3fe6080604052348015600e575f80fd5b50600436106026575f3560e01c8063bfb4ebcf14602a575b5f80fd5b60306032565b005b5f600160025f6040517fbb6a82f1000000000000000000000000000000000000000000000000000000008152600401606c949392919060a3565b60405180910390fd5b5f819050919050565b6085816075565b82525050565b5f8115159050919050565b609d81608b565b82525050565b5f60808201905060b45f830187607e565b60bf6020830186607e565b60ca6040830185607e565b60d560608301846096565b9594505050505056fea26469706673582212205ce065ab1cfe16beba2b766e14009fc67ac66c214872149c889f0589720b870a64736f6c634300081a0033",
}
// C is an auto generated Go binding around an Ethereum contract.
type C struct {
abi abi.ABI
}
// NewC creates a new instance of C.
func NewC() (*C, error) {
parsed, err := CMetaData.GetAbi()
if err != nil {
return nil, err
}
return &C{abi: *parsed}, nil
}
func (_C *C) PackConstructor() ([]byte, error) {
return _C.abi.Pack("")
}
// Foo is a free data retrieval call binding the contract method 0xbfb4ebcf.
//
// Solidity: function Foo() pure returns()
func (_C *C) PackFoo() ([]byte, error) {
return _C.abi.Pack("Foo")
}
// CBadThing represents a BadThing error raised by the C contract.
type CBadThing struct {
Arg1 *big.Int
Arg2 *big.Int
Arg3 *big.Int
Arg4 bool
}
func CBadThingErrorID() common.Hash {
return common.HexToHash("0xbb6a82f123854747ef4381e30e497f934a3854753fec99a69c35c30d4b46714d")
}
func (_C *C) UnpackBadThingError(raw []byte) (*CBadThing, error) {
errName := "BadThing"
out := new(CBadThing)
if err := _C.abi.UnpackIntoInterface(out, errName, raw); err != nil {
return nil, err
}
return out, nil
}

View File

@ -0,0 +1 @@
{"contracts":{"contract.sol:C":{"abi":[{"inputs":[{"internalType":"uint256","name":"arg1","type":"uint256"},{"internalType":"uint256","name":"arg2","type":"uint256"},{"internalType":"uint256","name":"arg3","type":"uint256"},{"internalType":"bool","name":"arg4","type":"bool"}],"name":"BadThing","type":"error"},{"inputs":[],"name":"Foo","outputs":[],"stateMutability":"pure","type":"function"}],"bin":"6080604052348015600e575f80fd5b506101148061001c5f395ff3fe6080604052348015600e575f80fd5b50600436106026575f3560e01c8063bfb4ebcf14602a575b5f80fd5b60306032565b005b5f600160025f6040517fbb6a82f1000000000000000000000000000000000000000000000000000000008152600401606c949392919060a3565b60405180910390fd5b5f819050919050565b6085816075565b82525050565b5f8115159050919050565b609d81608b565b82525050565b5f60808201905060b45f830187607e565b60bf6020830186607e565b60ca6040830185607e565b60d560608301846096565b9594505050505056fea26469706673582212205ce065ab1cfe16beba2b766e14009fc67ac66c214872149c889f0589720b870a64736f6c634300081a0033"}},"version":"0.8.26+commit.8a97fa7a.Darwin.appleclang"}

View File

@ -0,0 +1,15 @@
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.26;
error BadThing(uint256 arg1, uint256 arg2, uint256 arg3, bool arg4);
contract C {
function Foo() public pure {
revert BadThing({
arg1: uint256(0),
arg2: uint256(1),
arg3: uint256(2),
arg4: false
});
}
}

View File

@ -278,7 +278,7 @@ type EventIterator[T any] struct {
unpack func(*types.Log) (*T, error) // Unpack function for the event
logs chan types.Log // Log channel receiving the found contract events
sub ethereum.Subscription // Subscription for errors, completion and termination
sub ethereum.Subscription // Subscription for solc_errors, completion and termination
done bool // Whether the subscription completed delivering logs
fail error // Occurred error to stop iteration
}

View File

@ -25,6 +25,7 @@ import (
"github.com/ethereum/go-ethereum/accounts/abi/bind/backends"
"github.com/ethereum/go-ethereum/accounts/abi/bind/testdata/v2/events"
"github.com/ethereum/go-ethereum/accounts/abi/bind/testdata/v2/nested_libraries"
"github.com/ethereum/go-ethereum/accounts/abi/bind/testdata/v2/solc_errors"
"github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/core/types"
"github.com/ethereum/go-ethereum/crypto"
@ -98,8 +99,60 @@ func testSetup() (*bind.TransactOpts, *backends.SimulatedBackend, error) {
}
// test deployment and interaction for a basic contract with no library deps
func TestDeployment(t *testing.T) {
func TestErrors(t *testing.T) {
opts, bindBackend, err := testSetup()
if err != nil {
t.Fatalf("err setting up test: %v", err)
}
defer bindBackend.Backend.Close()
deploymentParams := DeploymentParams{
Contracts: []ContractDeployParams{
{
Meta: solc_errors.CMetaData,
},
},
}
res, err := LinkAndDeploy(opts, bindBackend, deploymentParams)
if err != nil {
t.Fatalf("err: %+v\n", err)
}
bindBackend.Commit()
if len(res.Addrs) != 1 {
t.Fatalf("deployment should have generated 1 addresses. got %d", len(res.Addrs))
}
for _, tx := range res.Txs {
_, err = bind.WaitDeployed(context.Background(), bindBackend, tx)
if err != nil {
t.Fatalf("error deploying library: %+v", err)
}
}
c, err := solc_errors.NewC()
if err != nil {
t.Fatalf("err is %v", err)
}
doInput, err := c.PackFoo()
if err != nil {
t.Fatalf("pack function input err: %v\n", doInput)
}
cABI, err := nested_libraries.C1MetaData.GetAbi()
if err != nil {
t.Fatalf("error getting abi object: %v", err)
}
contractAddr := res.Addrs[solc_errors.CMetaData.Pattern]
boundC := bind.NewBoundContract(contractAddr, *cABI, bindBackend, bindBackend, bindBackend)
callOpts := &bind.CallOpts{
From: common.Address{},
Context: context.Background(),
}
callRes, err := boundC.CallRaw(callOpts, doInput)
if err != nil {
fmt.Println(callRes)
t.Fatalf("err calling contract: %v", err)
}
_ = callRes
}
// test that deploying a contract with library dependencies works,