accounts/abi: abigen v2

This commit is contained in:
Sina Mahmoodi 2023-01-06 12:58:43 +01:00 committed by Jared Wasinger
parent 8c73523812
commit 29d90ae528
6 changed files with 503 additions and 87 deletions

View File

@ -156,10 +156,6 @@ func DeployContract(opts *TransactOpts, abi abi.ABI, bytecode []byte, backend Co
// returns, a slice of interfaces for anonymous returns and a struct for named
// returns.
func (c *BoundContract) Call(opts *CallOpts, results *[]interface{}, method string, params ...interface{}) error {
// Don't crash on a lazy user
if opts == nil {
opts = new(CallOpts)
}
if results == nil {
results = new([]interface{})
}
@ -168,59 +164,10 @@ func (c *BoundContract) Call(opts *CallOpts, results *[]interface{}, method stri
if err != nil {
return err
}
var (
msg = ethereum.CallMsg{From: opts.From, To: &c.address, Data: input}
ctx = ensureContext(opts.Context)
code []byte
output []byte
)
if opts.Pending {
pb, ok := c.caller.(PendingContractCaller)
if !ok {
return ErrNoPendingState
}
output, err = pb.PendingCallContract(ctx, msg)
if err != nil {
return err
}
if len(output) == 0 {
// Make sure we have a contract to operate on, and bail out otherwise.
if code, err = pb.PendingCodeAt(ctx, c.address); err != nil {
return err
} else if len(code) == 0 {
return ErrNoCode
}
}
} else if opts.BlockHash != (common.Hash{}) {
bh, ok := c.caller.(BlockHashContractCaller)
if !ok {
return ErrNoBlockHashState
}
output, err = bh.CallContractAtHash(ctx, msg, opts.BlockHash)
if err != nil {
return err
}
if len(output) == 0 {
// Make sure we have a contract to operate on, and bail out otherwise.
if code, err = bh.CodeAtHash(ctx, c.address, opts.BlockHash); err != nil {
return err
} else if len(code) == 0 {
return ErrNoCode
}
}
} else {
output, err = c.caller.CallContract(ctx, msg, opts.BlockNumber)
if err != nil {
return err
}
if len(output) == 0 {
// Make sure we have a contract to operate on, and bail out otherwise.
if code, err = c.caller.CodeAt(ctx, c.address, opts.BlockNumber); err != nil {
return err
} else if len(code) == 0 {
return ErrNoCode
}
}
output, err := c.call(opts, input)
if err != nil {
return err
}
if len(*results) == 0 {
@ -232,6 +179,69 @@ func (c *BoundContract) Call(opts *CallOpts, results *[]interface{}, method stri
return c.abi.UnpackIntoInterface(res[0], method, output)
}
func (c *BoundContract) call(opts *CallOpts, input []byte) ([]byte, error) {
// Don't crash on a lazy user
if opts == nil {
opts = new(CallOpts)
}
var (
msg = ethereum.CallMsg{From: opts.From, To: &c.address, Data: input}
ctx = ensureContext(opts.Context)
code []byte
output []byte
err error
)
if opts.Pending {
pb, ok := c.caller.(PendingContractCaller)
if !ok {
return nil, ErrNoPendingState
}
output, err = pb.PendingCallContract(ctx, msg)
if err != nil {
return nil, err
}
if len(output) == 0 {
// Make sure we have a contract to operate on, and bail out otherwise.
if code, err = pb.PendingCodeAt(ctx, c.address); err != nil {
return nil, err
} else if len(code) == 0 {
return nil, ErrNoCode
}
}
} else if opts.BlockHash != (common.Hash{}) {
bh, ok := c.caller.(BlockHashContractCaller)
if !ok {
return nil, ErrNoBlockHashState
}
output, err = bh.CallContractAtHash(ctx, msg, opts.BlockHash)
if err != nil {
return nil, err
}
if len(output) == 0 {
// Make sure we have a contract to operate on, and bail out otherwise.
if code, err = bh.CodeAtHash(ctx, c.address, opts.BlockHash); err != nil {
return nil, err
} else if len(code) == 0 {
return nil, ErrNoCode
}
}
} else {
output, err = c.caller.CallContract(ctx, msg, opts.BlockNumber)
if err != nil {
return nil, err
}
if len(output) == 0 {
// Make sure we have a contract to operate on, and bail out otherwise.
if code, err = c.caller.CodeAt(ctx, c.address, opts.BlockNumber); err != nil {
return nil, err
} else if len(code) == 0 {
return nil, ErrNoCode
}
}
}
return output, nil
}
// Transact invokes the (paid) contract method with params as input values.
func (c *BoundContract) Transact(opts *TransactOpts, method string, params ...interface{}) (*types.Transaction, error) {
// Otherwise pack up the parameters and invoke the contract
@ -434,13 +444,16 @@ func (c *BoundContract) transact(opts *TransactOpts, contract *common.Address, i
// FilterLogs filters contract logs for past blocks, returning the necessary
// channels to construct a strongly typed bound iterator on top of them.
func (c *BoundContract) FilterLogs(opts *FilterOpts, name string, query ...[]interface{}) (chan types.Log, event.Subscription, error) {
return c.filterLogs(opts, c.abi.Events[name].ID, query...)
}
func (c *BoundContract) filterLogs(opts *FilterOpts, eventID common.Hash, query ...[]interface{}) (chan types.Log, event.Subscription, error) {
// Don't crash on a lazy user
if opts == nil {
opts = new(FilterOpts)
}
// Append the event selector to the query parameters and construct the topic set
query = append([][]interface{}{{c.abi.Events[name].ID}}, query...)
query = append([][]interface{}{{eventID}}, query...)
topics, err := abi.MakeTopics(query...)
if err != nil {
return nil, nil, err
@ -480,12 +493,16 @@ func (c *BoundContract) FilterLogs(opts *FilterOpts, name string, query ...[]int
// WatchLogs filters subscribes to contract logs for future blocks, returning a
// subscription object that can be used to tear down the watcher.
func (c *BoundContract) WatchLogs(opts *WatchOpts, name string, query ...[]interface{}) (chan types.Log, event.Subscription, error) {
return c.watchLogs(opts, c.abi.Events[name].ID, query...)
}
func (c *BoundContract) watchLogs(opts *WatchOpts, eventID common.Hash, query ...[]interface{}) (chan types.Log, event.Subscription, error) {
// Don't crash on a lazy user
if opts == nil {
opts = new(WatchOpts)
}
// Append the event selector to the query parameters and construct the topic set
query = append([][]interface{}{{c.abi.Events[name].ID}}, query...)
query = append([][]interface{}{{eventID}}, query...)
topics, err := abi.MakeTopics(query...)
if err != nil {

View File

@ -82,6 +82,100 @@ func isKeyWord(arg string) bool {
// enforces compile time type safety and naming convention as opposed to having to
// manually maintain hard coded strings that break on runtime.
func Bind(types []string, abis []string, bytecodes []string, fsigs []map[string]string, pkg string, lang Lang, libs map[string]string, aliases map[string]string) (string, error) {
data, err := bind(types, abis, bytecodes, fsigs, pkg, lang, libs, aliases)
if err != nil {
return "", err
}
buffer := new(bytes.Buffer)
funcs := map[string]interface{}{
"bindtype": bindType[lang],
"bindtopictype": bindTopicType[lang],
"namedtype": namedType[lang],
"capitalise": capitalise,
"decapitalise": decapitalise,
}
tmpl := template.Must(template.New("").Funcs(funcs).Parse(tmplSource[lang]))
if err := tmpl.Execute(buffer, data); err != nil {
return "", err
}
// For Go bindings pass the code through gofmt to clean it up
if lang == LangGo {
code, err := format.Source(buffer.Bytes())
if err != nil {
return "", fmt.Errorf("%v\n%s", err, buffer)
}
return string(code), nil
}
// For all others just return as is for now
return buffer.String(), nil
}
func BindV2(types []string, abis []string, bytecodes []string, fsigs []map[string]string, pkg string, lang Lang, libs map[string]string, aliases map[string]string) (string, error) {
data, err := bind(types, abis, bytecodes, fsigs, pkg, lang, libs, aliases)
if err != nil {
return "", err
}
for _, c := range data.Contracts {
// We want pack/unpack methods for all existing methods.
for name, t := range c.Transacts {
c.Calls[name] = t
}
c.Transacts = nil
// Make sure we return one argument. If multiple exist
// merge them into a struct.
for _, call := range c.Calls {
if call.Structured {
continue
}
if len(call.Normalized.Outputs) == 1 {
continue
}
// Build up dictionary of existing arg names.
keys := make(map[string]struct{})
for _, o := range call.Normalized.Outputs {
if o.Name != "" {
keys[strings.ToLower(o.Name)] = struct{}{}
}
}
// Assign names to anonymous fields.
for i, o := range call.Normalized.Outputs {
if o.Name != "" {
continue
}
o.Name = capitalise(abi.ResolveNameConflict("arg", func(name string) bool { _, ok := keys[name]; return ok }))
call.Normalized.Outputs[i] = o
keys[strings.ToLower(o.Name)] = struct{}{}
}
call.Structured = true
}
}
buffer := new(bytes.Buffer)
funcs := map[string]interface{}{
"bindtype": bindType[lang],
"bindtopictype": bindTopicType[lang],
"namedtype": namedType[lang],
"capitalise": capitalise,
"decapitalise": decapitalise,
}
tmpl := template.Must(template.New("").Funcs(funcs).Parse(tmplSourceV2[lang]))
if err := tmpl.Execute(buffer, data); err != nil {
return "", err
}
// For Go bindings pass the code through gofmt to clean it up
if lang == LangGo {
code, err := format.Source(buffer.Bytes())
if err != nil {
return "", fmt.Errorf("%v\n%s", err, buffer)
}
return string(code), nil
}
// For all others just return as is for now
return buffer.String(), nil
}
func bind(types []string, abis []string, bytecodes []string, fsigs []map[string]string, pkg string, lang Lang, libs map[string]string, aliases map[string]string) (*tmplData, error) {
var (
// contracts is the map of each individual contract requested binding
contracts = make(map[string]*tmplContract)
@ -96,7 +190,7 @@ func Bind(types []string, abis []string, bytecodes []string, fsigs []map[string]
// Parse the actual ABI to generate the binding for
evmABI, err := abi.JSON(strings.NewReader(abis[i]))
if err != nil {
return "", err
return nil, err
}
// Strip any whitespace from the JSON ABI
strippedABI := strings.Map(func(r rune) rune {
@ -147,7 +241,7 @@ func Bind(types []string, abis []string, bytecodes []string, fsigs []map[string]
})
}
if identifiers[normalizedName] {
return "", fmt.Errorf("duplicated identifier \"%s\"(normalized \"%s\"), use --alias for renaming", original.Name, normalizedName)
return nil, fmt.Errorf("duplicated identifier \"%s\"(normalized \"%s\"), use --alias for renaming", original.Name, normalizedName)
}
identifiers[normalizedName] = true
@ -198,7 +292,7 @@ func Bind(types []string, abis []string, bytecodes []string, fsigs []map[string]
})
}
if eventIdentifiers[normalizedName] {
return "", fmt.Errorf("duplicated identifier \"%s\"(normalized \"%s\"), use --alias for renaming", original.Name, normalizedName)
return nil, fmt.Errorf("duplicated identifier \"%s\"(normalized \"%s\"), use --alias for renaming", original.Name, normalizedName)
}
eventIdentifiers[normalizedName] = true
normalized.Name = normalizedName
@ -233,6 +327,7 @@ func Bind(types []string, abis []string, bytecodes []string, fsigs []map[string]
if evmABI.HasReceive() {
receive = &tmplMethod{Original: evmABI.Receive}
}
contracts[types[i]] = &tmplContract{
Type: capitalise(types[i]),
InputABI: strings.ReplaceAll(strippedABI, "\"", "\\\""),
@ -277,29 +372,7 @@ func Bind(types []string, abis []string, bytecodes []string, fsigs []map[string]
Libraries: libs,
Structs: structs,
}
buffer := new(bytes.Buffer)
funcs := map[string]interface{}{
"bindtype": bindType[lang],
"bindtopictype": bindTopicType[lang],
"namedtype": namedType[lang],
"capitalise": capitalise,
"decapitalise": decapitalise,
}
tmpl := template.Must(template.New("").Funcs(funcs).Parse(tmplSource[lang]))
if err := tmpl.Execute(buffer, data); err != nil {
return "", err
}
// For Go bindings pass the code through gofmt to clean it up
if lang == LangGo {
code, err := format.Source(buffer.Bytes())
if err != nil {
return "", fmt.Errorf("%v\n%s", err, buffer)
}
return string(code), nil
}
// For all others just return as is for now
return buffer.String(), nil
return data, nil
}
// bindType is a set of type binders that convert Solidity types to some supported

152
accounts/abi/bind/lib.go Normal file
View File

@ -0,0 +1,152 @@
package bind
import (
"github.com/ethereum/go-ethereum"
"github.com/ethereum/go-ethereum/accounts/abi"
"github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/core/types"
"github.com/ethereum/go-ethereum/crypto"
"github.com/ethereum/go-ethereum/event"
)
func DeployContract2(opts *TransactOpts, bytecode []byte, input []byte, backend ContractBackend) (common.Address, *types.Transaction, error) {
c := NewBoundContract(common.Address{}, abi.ABI{}, backend, backend, backend)
tx, err := c.transact(opts, nil, append(bytecode, input...))
if err != nil {
return common.Address{}, nil, err
}
address := crypto.CreateAddress(opts.From, tx.Nonce())
return address, tx, nil
}
func Call2[T any](opts *CallOpts, addr common.Address, input []byte, backend ContractBackend, unpack func([]byte) (T, error)) (arg T, err error) {
var data []byte
data, err = CallRaw(opts, addr, input, backend)
if err != nil {
return
}
return unpack(data)
}
func CallRaw(opts *CallOpts, addr common.Address, input []byte, backend ContractBackend) ([]byte, error) {
c := NewBoundContract(addr, abi.ABI{}, backend, backend, backend)
return c.call(opts, input)
}
func Transact2(opts *TransactOpts, addr common.Address, input []byte, backend ContractBackend) (*types.Transaction, error) {
c := NewBoundContract(addr, abi.ABI{}, backend, backend, backend)
return c.transact(opts, &addr, input)
}
func Transfer2(opts *TransactOpts, addr common.Address, backend ContractBackend) (*types.Transaction, error) {
c := NewBoundContract(addr, abi.ABI{}, backend, backend, backend)
return c.Transfer(opts)
}
func FilterLogs[T any](opts *FilterOpts, addr common.Address, backend ContractBackend, eventID common.Hash, unpack func(types.Log) (*T, error), topics ...[]any) (*EventIterator[T], error) {
c := NewBoundContract(addr, abi.ABI{}, backend, backend, backend)
logs, sub, err := c.filterLogs(opts, eventID, topics...)
if err != nil {
return nil, err
}
return &EventIterator[T]{unpack: unpack, logs: logs, sub: sub}, nil
}
func WatchLogs[T any](opts *WatchOpts, addr common.Address, backend ContractBackend, eventID common.Hash, unpack func(types.Log) (*T, error), sink chan<- *T, topics ...[]any) (event.Subscription, error) {
c := NewBoundContract(addr, abi.ABI{}, backend, backend, backend)
logs, sub, err := c.watchLogs(opts, eventID, topics...)
if err != nil {
return nil, err
}
return event.NewSubscription(func(quit <-chan struct{}) error {
defer sub.Unsubscribe()
for {
select {
case log := <-logs:
// New log arrived, parse the event and forward to the user
ev, err := unpack(log)
if err != nil {
return err
}
select {
case sink <- ev:
case err := <-sub.Err():
return err
case <-quit:
return nil
}
case err := <-sub.Err():
return err
case <-quit:
return nil
}
}
}), nil
}
// EventIterator is returned from FilterLogs and is used to iterate over the raw logs and unpacked data for events.
type EventIterator[T any] struct {
Event *T // Event containing the contract specifics and raw log
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
done bool // Whether the subscription completed delivering logs
fail error // Occurred error to stop iteration
}
// Next advances the iterator to the subsequent event, returning whether there
// are any more events found. In case of a retrieval or parsing error, false is
// returned and Error() can be queried for the exact failure.
func (it *EventIterator[T]) Next() bool {
// If the iterator failed, stop iterating
if it.fail != nil {
return false
}
// If the iterator completed, deliver directly whatever's available
if it.done {
select {
case log := <-it.logs:
res, err := it.unpack(log)
if err != nil {
it.fail = err
return false
}
it.Event = res
return true
default:
return false
}
}
// Iterator still in progress, wait for either a data or an error event
select {
case log := <-it.logs:
res, err := it.unpack(log)
if err != nil {
it.fail = err
return false
}
it.Event = res
return true
case err := <-it.sub.Err():
it.done = true
it.fail = err
return it.Next()
}
}
// Error returns any retrieval or parsing error occurred during filtering.
func (it *EventIterator[T]) Error() error {
return it.fail
}
// Close terminates the iteration process, releasing any pending underlying
// resources.
func (it *EventIterator[T]) Close() error {
it.sub.Unsubscribe()
return nil
}

View File

@ -82,6 +82,10 @@ var tmplSource = map[Lang]string{
LangGo: tmplSourceGo,
}
var tmplSourceV2 = map[Lang]string{
LangGo: tmplSourceGoV2,
}
// tmplSourceGo is the Go source template that the generated Go contract binding
// is based on.
//

View File

@ -0,0 +1,157 @@
package bind
// tmplSourceGo is the Go source template that the generated Go contract binding
// is based on.
const tmplSourceGoV2 = `
// Code generated via abigen V2 - DO NOT EDIT.
// This file is a generated binding and any manual changes will be lost.
package {{.Package}}
import (
"fmt"
"math/big"
"strings"
"errors"
ethereum "github.com/ethereum/go-ethereum"
"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"
"github.com/ethereum/go-ethereum/event"
)
// Reference imports to suppress errors if they are not otherwise used.
var (
_ = errors.New
_ = big.NewInt
_ = strings.NewReader
_ = ethereum.NotFound
_ = bind.Bind
_ = common.Big1
_ = types.BloomLookup
_ = event.NewSubscription
_ = abi.ConvertType
)
{{$structs := .Structs}}
{{range $structs}}
// {{.Name}} is an auto generated low-level Go binding around an user-defined struct.
type {{.Name}} struct {
{{range $field := .Fields}}
{{$field.Name}} {{$field.Type}}{{end}}
}
{{end}}
{{range $contract := .Contracts}}
// {{.Type}}MetaData contains all meta data concerning the {{.Type}} contract.
var {{.Type}}MetaData = &bind.MetaData{
ABI: "{{.InputABI}}",
{{if $contract.FuncSigs -}}
Sigs: map[string]string{
{{range $strsig, $binsig := .FuncSigs}}"{{$binsig}}": "{{$strsig}}",
{{end}}
},
{{end -}}
{{if .InputBin -}}
Bin: "0x{{.InputBin}}",
{{end}}
}
// {{.Type}} is an auto generated Go binding around an Ethereum contract.
type {{.Type}} struct {
abi abi.ABI
}
// {{.Type}}Instance represents a deployed instance of the {{.Type}} contract.
type {{.Type}}Instance struct {
{{.Type}}
address common.Address
}
func (i *{{$contract.Type}}Instance) Address() common.Address {
return i.address
}
// New{{.Type}} creates a new instance of {{.Type}}.
func New{{.Type}}() (*{{.Type}}, error) {
parsed, err := {{.Type}}MetaData.GetAbi()
if err != nil {
return nil, err
}
return &{{.Type}}{abi: *parsed}, nil
}
func (_{{$contract.Type}} *{{$contract.Type}}) PackConstructor({{range .Constructor.Inputs}}, {{.Name}} {{bindtype .Type $structs}} {{end}}) ([]byte, error) {
return _{{$contract.Type}}.abi.Pack("" {{range .Constructor.Inputs}}, {{.Name}}{{end}})
}
{{range .Calls}}
// {{.Normalized.Name}} is a free data retrieval call binding the contract method 0x{{printf "%x" .Original.ID}}.
//
// Solidity: {{.Original.String}}
func (_{{$contract.Type}} *{{$contract.Type}}) Pack{{.Normalized.Name}}({{range .Normalized.Inputs}} {{.Name}} {{bindtype .Type $structs}}, {{end}}) ([]byte, error) {
return _{{$contract.Type}}.abi.Pack("{{.Original.Name}}" {{range .Normalized.Inputs}}, {{.Name}}{{end}})
}
func (_{{$contract.Type}} *{{$contract.Type}}) Unpack{{.Normalized.Name}}(data []byte) ({{if .Structured}}struct{ {{range .Normalized.Outputs}}{{.Name}} {{bindtype .Type $structs}};{{end}} },{{else}}{{range .Normalized.Outputs}}{{bindtype .Type $structs}},{{end}}{{end}} error) {
out, err := _{{$contract.Type}}.abi.Unpack("{{.Original.Name}}", data)
{{if .Structured}}
outstruct := new(struct{ {{range .Normalized.Outputs}} {{.Name}} {{bindtype .Type $structs}}; {{end}} })
if err != nil {
return *outstruct, err
}
{{range $i, $t := .Normalized.Outputs}}
outstruct.{{.Name}} = *abi.ConvertType(out[{{$i}}], new({{bindtype .Type $structs}})).(*{{bindtype .Type $structs}}){{end}}
return *outstruct, err
{{else}}
if err != nil {
return {{range $i, $_ := .Normalized.Outputs}}*new({{bindtype .Type $structs}}), {{end}} err
}
{{range $i, $t := .Normalized.Outputs}}
out{{$i}} := *abi.ConvertType(out[{{$i}}], new({{bindtype .Type $structs}})).(*{{bindtype .Type $structs}}){{end}}
return {{range $i, $t := .Normalized.Outputs}}out{{$i}}, {{end}} err
{{end}}
}
{{end}}
{{range .Events}}
// {{$contract.Type}}{{.Normalized.Name}} represents a {{.Normalized.Name}} event raised by the {{$contract.Type}} contract.
type {{$contract.Type}}{{.Normalized.Name}} struct { {{range .Normalized.Inputs}}
{{capitalise .Name}} {{if .Indexed}}{{bindtopictype .Type $structs}}{{else}}{{bindtype .Type $structs}}{{end}}; {{end}}
Raw types.Log // Blockchain specific contextual infos
}
func (_{{$contract.Type}} *{{$contract.Type}}) {{.Normalized.Name}}EventID() common.Hash {
return common.HexToHash("{{.Original.ID}}")
}
func (_{{$contract.Type}} *{{$contract.Type}}) Unpack{{.Normalized.Name}}Event(log types.Log) (*{{$contract.Type}}{{.Normalized.Name}}, error) {
event := "{{.Normalized.Name}}"
if log.Topics[0] != _{{$contract.Type}}.abi.Events[event].ID {
return nil, fmt.Errorf("event signature mismatch")
}
out := new({{$contract.Type}}{{.Normalized.Name}})
if len(log.Data) > 0 {
if err := _{{$contract.Type}}.abi.UnpackIntoInterface(out, event, log.Data); err != nil {
return nil, err
}
}
var indexed abi.Arguments
for _, arg := range _{{$contract.Type}}.abi.Events[event].Inputs {
if arg.Indexed {
indexed = append(indexed, arg)
}
}
if err := abi.ParseTopics(out, indexed, log.Topics[1:]); err != nil {
return nil, err
}
out.Raw = log
return out, nil
}
{{end}}
{{end}}
`

View File

@ -72,6 +72,10 @@ var (
Name: "alias",
Usage: "Comma separated aliases for function and event renaming, e.g. original1=alias1, original2=alias2",
}
v2Flag = &cli.BoolFlag{
Name: "v2",
Usage: "Generates v2 bindings",
}
)
var app = flags.NewApp("Ethereum ABI wrapper code generator")
@ -88,6 +92,7 @@ func init() {
outFlag,
langFlag,
aliasFlag,
v2Flag,
}
app.Action = abigen
}
@ -216,7 +221,15 @@ func abigen(c *cli.Context) error {
}
}
// Generate the contract binding
code, err := bind.Bind(types, abis, bins, sigs, c.String(pkgFlag.Name), lang, libs, aliases)
var (
code string
err error
)
if c.IsSet(v2Flag.Name) {
code, err = bind.BindV2(types, abis, bins, sigs, c.String(pkgFlag.Name), lang, libs, aliases)
} else {
code, err = bind.Bind(types, abis, bins, sigs, c.String(pkgFlag.Name), lang, libs, aliases)
}
if err != nil {
utils.Fatalf("Failed to generate ABI binding: %v", err)
}