accounts/abi: abigen v2
This commit is contained in:
parent
8c73523812
commit
29d90ae528
|
@ -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, a slice of interfaces for anonymous returns and a struct for named
|
||||||
// returns.
|
// returns.
|
||||||
func (c *BoundContract) Call(opts *CallOpts, results *[]interface{}, method string, params ...interface{}) error {
|
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 {
|
if results == nil {
|
||||||
results = new([]interface{})
|
results = new([]interface{})
|
||||||
}
|
}
|
||||||
|
@ -168,59 +164,10 @@ func (c *BoundContract) Call(opts *CallOpts, results *[]interface{}, method stri
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
var (
|
|
||||||
msg = ethereum.CallMsg{From: opts.From, To: &c.address, Data: input}
|
output, err := c.call(opts, input)
|
||||||
ctx = ensureContext(opts.Context)
|
if err != nil {
|
||||||
code []byte
|
return err
|
||||||
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
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if len(*results) == 0 {
|
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)
|
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.
|
// Transact invokes the (paid) contract method with params as input values.
|
||||||
func (c *BoundContract) Transact(opts *TransactOpts, method string, params ...interface{}) (*types.Transaction, error) {
|
func (c *BoundContract) Transact(opts *TransactOpts, method string, params ...interface{}) (*types.Transaction, error) {
|
||||||
// Otherwise pack up the parameters and invoke the contract
|
// 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
|
// FilterLogs filters contract logs for past blocks, returning the necessary
|
||||||
// channels to construct a strongly typed bound iterator on top of them.
|
// 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) {
|
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
|
// Don't crash on a lazy user
|
||||||
if opts == nil {
|
if opts == nil {
|
||||||
opts = new(FilterOpts)
|
opts = new(FilterOpts)
|
||||||
}
|
}
|
||||||
// Append the event selector to the query parameters and construct the topic set
|
// 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...)
|
topics, err := abi.MakeTopics(query...)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, nil, err
|
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
|
// WatchLogs filters subscribes to contract logs for future blocks, returning a
|
||||||
// subscription object that can be used to tear down the watcher.
|
// 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) {
|
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
|
// Don't crash on a lazy user
|
||||||
if opts == nil {
|
if opts == nil {
|
||||||
opts = new(WatchOpts)
|
opts = new(WatchOpts)
|
||||||
}
|
}
|
||||||
// Append the event selector to the query parameters and construct the topic set
|
// 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...)
|
topics, err := abi.MakeTopics(query...)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
|
|
@ -82,6 +82,100 @@ func isKeyWord(arg string) bool {
|
||||||
// enforces compile time type safety and naming convention as opposed to having to
|
// enforces compile time type safety and naming convention as opposed to having to
|
||||||
// manually maintain hard coded strings that break on runtime.
|
// 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) {
|
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 (
|
var (
|
||||||
// contracts is the map of each individual contract requested binding
|
// contracts is the map of each individual contract requested binding
|
||||||
contracts = make(map[string]*tmplContract)
|
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
|
// Parse the actual ABI to generate the binding for
|
||||||
evmABI, err := abi.JSON(strings.NewReader(abis[i]))
|
evmABI, err := abi.JSON(strings.NewReader(abis[i]))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return "", err
|
return nil, err
|
||||||
}
|
}
|
||||||
// Strip any whitespace from the JSON ABI
|
// Strip any whitespace from the JSON ABI
|
||||||
strippedABI := strings.Map(func(r rune) rune {
|
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] {
|
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
|
identifiers[normalizedName] = true
|
||||||
|
|
||||||
|
@ -198,7 +292,7 @@ func Bind(types []string, abis []string, bytecodes []string, fsigs []map[string]
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
if eventIdentifiers[normalizedName] {
|
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
|
eventIdentifiers[normalizedName] = true
|
||||||
normalized.Name = normalizedName
|
normalized.Name = normalizedName
|
||||||
|
@ -233,6 +327,7 @@ func Bind(types []string, abis []string, bytecodes []string, fsigs []map[string]
|
||||||
if evmABI.HasReceive() {
|
if evmABI.HasReceive() {
|
||||||
receive = &tmplMethod{Original: evmABI.Receive}
|
receive = &tmplMethod{Original: evmABI.Receive}
|
||||||
}
|
}
|
||||||
|
|
||||||
contracts[types[i]] = &tmplContract{
|
contracts[types[i]] = &tmplContract{
|
||||||
Type: capitalise(types[i]),
|
Type: capitalise(types[i]),
|
||||||
InputABI: strings.ReplaceAll(strippedABI, "\"", "\\\""),
|
InputABI: strings.ReplaceAll(strippedABI, "\"", "\\\""),
|
||||||
|
@ -277,29 +372,7 @@ func Bind(types []string, abis []string, bytecodes []string, fsigs []map[string]
|
||||||
Libraries: libs,
|
Libraries: libs,
|
||||||
Structs: structs,
|
Structs: structs,
|
||||||
}
|
}
|
||||||
buffer := new(bytes.Buffer)
|
return data, nil
|
||||||
|
|
||||||
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
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// bindType is a set of type binders that convert Solidity types to some supported
|
// bindType is a set of type binders that convert Solidity types to some supported
|
||||||
|
|
|
@ -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
|
||||||
|
}
|
|
@ -82,6 +82,10 @@ var tmplSource = map[Lang]string{
|
||||||
LangGo: tmplSourceGo,
|
LangGo: tmplSourceGo,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
var tmplSourceV2 = map[Lang]string{
|
||||||
|
LangGo: tmplSourceGoV2,
|
||||||
|
}
|
||||||
|
|
||||||
// tmplSourceGo is the Go source template that the generated Go contract binding
|
// tmplSourceGo is the Go source template that the generated Go contract binding
|
||||||
// is based on.
|
// is based on.
|
||||||
//
|
//
|
||||||
|
|
|
@ -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}}
|
||||||
|
`
|
|
@ -72,6 +72,10 @@ var (
|
||||||
Name: "alias",
|
Name: "alias",
|
||||||
Usage: "Comma separated aliases for function and event renaming, e.g. original1=alias1, original2=alias2",
|
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")
|
var app = flags.NewApp("Ethereum ABI wrapper code generator")
|
||||||
|
@ -88,6 +92,7 @@ func init() {
|
||||||
outFlag,
|
outFlag,
|
||||||
langFlag,
|
langFlag,
|
||||||
aliasFlag,
|
aliasFlag,
|
||||||
|
v2Flag,
|
||||||
}
|
}
|
||||||
app.Action = abigen
|
app.Action = abigen
|
||||||
}
|
}
|
||||||
|
@ -216,7 +221,15 @@ func abigen(c *cli.Context) error {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
// Generate the contract binding
|
// 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 {
|
if err != nil {
|
||||||
utils.Fatalf("Failed to generate ABI binding: %v", err)
|
utils.Fatalf("Failed to generate ABI binding: %v", err)
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue