mirror of
https://github.com/ethereum/go-ethereum.git
synced 2026-08-17 01:13:45 +00:00
cmd/abigen, accounts/abi: add support for log subscriptions
This commit is contained in:
parent
c0a1f1c907
commit
7943c71912
5 changed files with 109 additions and 9 deletions
|
|
@ -90,8 +90,14 @@ type ContractTransactor interface {
|
|||
SendTransaction(ctx context.Context, tx *types.Transaction) error
|
||||
}
|
||||
|
||||
// ContractEventer defines methods to listen for events raised by the contract.
|
||||
type ContractEventer interface {
|
||||
SubscribeFilterLogs(ctx context.Context, q ethereum.FilterQuery, ch chan<- types.Log) (ethereum.Subscription, error)
|
||||
}
|
||||
|
||||
// ContractBackend defines the methods needed to work with contracts on a read-write basis.
|
||||
type ContractBackend interface {
|
||||
ContractCaller
|
||||
ContractTransactor
|
||||
ContractEventer
|
||||
}
|
||||
|
|
|
|||
|
|
@ -63,16 +63,18 @@ type BoundContract struct {
|
|||
abi abi.ABI // Reflect based ABI to access the correct Ethereum methods
|
||||
caller ContractCaller // Read interface to interact with the blockchain
|
||||
transactor ContractTransactor // Write interface to interact with the blockchain
|
||||
eventer ContractEventer // Listen for events raised by the contract
|
||||
}
|
||||
|
||||
// NewBoundContract creates a low level contract interface through which calls
|
||||
// and transactions may be made through.
|
||||
func NewBoundContract(address common.Address, abi abi.ABI, caller ContractCaller, transactor ContractTransactor) *BoundContract {
|
||||
func NewBoundContract(address common.Address, abi abi.ABI, caller ContractCaller, transactor ContractTransactor, eventer ContractEventer) *BoundContract {
|
||||
return &BoundContract{
|
||||
address: address,
|
||||
abi: abi,
|
||||
caller: caller,
|
||||
transactor: transactor,
|
||||
eventer: eventer,
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -80,7 +82,7 @@ func NewBoundContract(address common.Address, abi abi.ABI, caller ContractCaller
|
|||
// deployment address with a Go wrapper.
|
||||
func DeployContract(opts *TransactOpts, abi abi.ABI, bytecode []byte, backend ContractBackend, params ...interface{}) (common.Address, *types.Transaction, *BoundContract, error) {
|
||||
// Otherwise try to deploy the contract
|
||||
c := NewBoundContract(common.Address{}, abi, backend, backend)
|
||||
c := NewBoundContract(common.Address{}, abi, backend, backend, backend)
|
||||
|
||||
input, err := c.abi.Pack("", params...)
|
||||
if err != nil {
|
||||
|
|
@ -225,6 +227,18 @@ func (c *BoundContract) transact(opts *TransactOpts, contract *common.Address, i
|
|||
return signedTx, nil
|
||||
}
|
||||
|
||||
// SubscribeFilterLogs creates a log subscription that streams logs raised by this contract
|
||||
// with topics matching
|
||||
func (c *BoundContract) SubscribeFilterLogs(opts *CallOpts, topics [][]common.Hash, ch chan<- types.Log) (ethereum.Subscription, error) {
|
||||
ctx := ensureContext(opts.Context)
|
||||
q := ethereum.FilterQuery{
|
||||
Addresses: []common.Address{c.address},
|
||||
Topics: topics,
|
||||
}
|
||||
|
||||
return c.eventer.SubscribeFilterLogs(ctx, q, ch)
|
||||
}
|
||||
|
||||
func ensureContext(ctx context.Context) context.Context {
|
||||
if ctx == nil {
|
||||
return context.TODO()
|
||||
|
|
|
|||
|
|
@ -67,6 +67,7 @@ func Bind(types []string, abis []string, bytecodes []string, pkg string, lang La
|
|||
var (
|
||||
calls = make(map[string]*tmplMethod)
|
||||
transacts = make(map[string]*tmplMethod)
|
||||
events = make(map[string]*tmplEvent)
|
||||
)
|
||||
for _, original := range evmABI.Methods {
|
||||
// Normalize the method for capital cases and non-anonymous inputs/outputs
|
||||
|
|
@ -94,6 +95,16 @@ func Bind(types []string, abis []string, bytecodes []string, pkg string, lang La
|
|||
transacts[original.Name] = &tmplMethod{Original: original, Normalized: normalized, Structured: structured(original)}
|
||||
}
|
||||
}
|
||||
|
||||
for _, e := range evmABI.Events {
|
||||
events[e.Name] = &tmplEvent{
|
||||
Name: e.Name,
|
||||
Inputs: e.Inputs,
|
||||
ID: fmt.Sprintf("%x", e.Id()),
|
||||
Anonymous: e.Anonymous,
|
||||
}
|
||||
}
|
||||
|
||||
contracts[types[i]] = &tmplContract{
|
||||
Type: capitalise(types[i]),
|
||||
InputABI: strings.Replace(strippedABI, "\"", "\\\"", -1),
|
||||
|
|
@ -101,6 +112,7 @@ func Bind(types []string, abis []string, bytecodes []string, pkg string, lang La
|
|||
Constructor: evmABI.Constructor,
|
||||
Calls: calls,
|
||||
Transacts: transacts,
|
||||
Events: events, // TODO: add event subscription support to the java template
|
||||
}
|
||||
}
|
||||
// Generate the contract template data content and render it
|
||||
|
|
@ -116,6 +128,7 @@ func Bind(types []string, abis []string, bytecodes []string, pkg string, lang La
|
|||
"capitalise": capitalise,
|
||||
"decapitalise": decapitalise,
|
||||
}
|
||||
|
||||
tmpl := template.Must(template.New("").Funcs(funcs).Parse(tmplSource[lang]))
|
||||
if err := tmpl.Execute(buffer, data); err != nil {
|
||||
return "", err
|
||||
|
|
|
|||
|
|
@ -16,7 +16,12 @@
|
|||
|
||||
package bind
|
||||
|
||||
import "github.com/ethereum/go-ethereum/accounts/abi"
|
||||
import (
|
||||
"fmt"
|
||||
"strings"
|
||||
|
||||
"github.com/ethereum/go-ethereum/accounts/abi"
|
||||
)
|
||||
|
||||
// tmplData is the data structure required to fill the binding template.
|
||||
type tmplData struct {
|
||||
|
|
@ -32,6 +37,7 @@ type tmplContract struct {
|
|||
Constructor abi.Method // Contract constructor for deploy parametrization
|
||||
Calls map[string]*tmplMethod // Contract calls that only read state data
|
||||
Transacts map[string]*tmplMethod // Contract calls that write state data
|
||||
Events map[string]*tmplEvent // Contract events
|
||||
}
|
||||
|
||||
// tmplMethod is a wrapper around an abi.Method that contains a few preprocessed
|
||||
|
|
@ -42,6 +48,39 @@ type tmplMethod struct {
|
|||
Structured bool // Whether the returns should be accumulated into a contract
|
||||
}
|
||||
|
||||
// tmplEvent is a wrapper around an abi.Event that contains a few preprocessed
|
||||
// and cached data fields.
|
||||
type tmplEvent struct {
|
||||
Name string
|
||||
Inputs []abi.Argument
|
||||
ID string
|
||||
Anonymous bool
|
||||
}
|
||||
|
||||
func (e tmplEvent) Canonical() string {
|
||||
var (
|
||||
args []string
|
||||
anonymous string
|
||||
)
|
||||
|
||||
for _, a := range e.Inputs {
|
||||
indexed := ""
|
||||
if a.Indexed {
|
||||
indexed = " indexed"
|
||||
}
|
||||
if a.Name != "" {
|
||||
args = append(args, fmt.Sprintf("%s%s %s", a.Type, indexed, a.Name))
|
||||
} else {
|
||||
args = append(args, fmt.Sprintf("%s%s", a.Type, indexed))
|
||||
}
|
||||
}
|
||||
|
||||
if e.Anonymous {
|
||||
anonymous = " anonymous"
|
||||
}
|
||||
return fmt.Sprintf("%s(%s)%s", e.Name, strings.Join(args, ", "), anonymous)
|
||||
}
|
||||
|
||||
// tmplSource is language to template mapping containing all the supported
|
||||
// programming languages the package can generate to.
|
||||
var tmplSource = map[Lang]string{
|
||||
|
|
@ -83,6 +122,7 @@ package {{.Package}}
|
|||
type {{.Type}} struct {
|
||||
{{.Type}}Caller // Read-only binding to the contract
|
||||
{{.Type}}Transactor // Write-only binding to the contract
|
||||
{{.Type}}Eventer // Event listener binding to the contract
|
||||
}
|
||||
|
||||
// {{.Type}}Caller is an auto generated read-only Go binding around an Ethereum contract.
|
||||
|
|
@ -95,6 +135,12 @@ package {{.Package}}
|
|||
contract *bind.BoundContract // Generic contract wrapper for the low level calls
|
||||
}
|
||||
|
||||
// {{.Type}}Eventer is an auto generated write-only Go binding around an Ethereum contract.
|
||||
type {{.Type}}Eventer struct {
|
||||
contract *bind.BoundContract // Generic contract wrapper for the low level calls
|
||||
address common.Address // Contract address
|
||||
}
|
||||
|
||||
// {{.Type}}Session is an auto generated Go binding around an Ethereum contract,
|
||||
// with pre-set call and transact options.
|
||||
type {{.Type}}Session struct {
|
||||
|
|
@ -134,7 +180,7 @@ package {{.Package}}
|
|||
|
||||
// New{{.Type}} creates a new instance of {{.Type}}, bound to a specific deployed contract.
|
||||
func New{{.Type}}(address common.Address, backend bind.ContractBackend) (*{{.Type}}, error) {
|
||||
contract, err := bind{{.Type}}(address, backend, backend)
|
||||
contract, err := bind{{.Type}}(address, backend, backend, backend)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
|
@ -143,7 +189,7 @@ package {{.Package}}
|
|||
|
||||
// New{{.Type}}Caller creates a new read-only instance of {{.Type}}, bound to a specific deployed contract.
|
||||
func New{{.Type}}Caller(address common.Address, caller bind.ContractCaller) (*{{.Type}}Caller, error) {
|
||||
contract, err := bind{{.Type}}(address, caller, nil)
|
||||
contract, err := bind{{.Type}}(address, caller, nil, nil)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
|
@ -152,20 +198,29 @@ package {{.Package}}
|
|||
|
||||
// New{{.Type}}Transactor creates a new write-only instance of {{.Type}}, bound to a specific deployed contract.
|
||||
func New{{.Type}}Transactor(address common.Address, transactor bind.ContractTransactor) (*{{.Type}}Transactor, error) {
|
||||
contract, err := bind{{.Type}}(address, nil, transactor)
|
||||
contract, err := bind{{.Type}}(address, nil, transactor, nil)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return &{{.Type}}Transactor{contract: contract}, nil
|
||||
}
|
||||
|
||||
// New{{.Type}}Eventer creates a new listen only instance of {{.Type}}, bound to a specific deployed contract.
|
||||
func New{{.Type}}Eventer(address common.Address, eventer bind.ContractEventer) (*{{.Type}}Eventer, error) {
|
||||
contract, err := bind{{.Type}}(address, nil, nil, eventer)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return &{{.Type}}Eventer{contract: contract, address: address}, nil
|
||||
}
|
||||
|
||||
// bind{{.Type}} binds a generic wrapper to an already deployed contract.
|
||||
func bind{{.Type}}(address common.Address, caller bind.ContractCaller, transactor bind.ContractTransactor) (*bind.BoundContract, error) {
|
||||
func bind{{.Type}}(address common.Address, caller bind.ContractCaller, transactor bind.ContractTransactor, eventer bind.ContractEventer) (*bind.BoundContract, error) {
|
||||
parsed, err := abi.JSON(strings.NewReader({{.Type}}ABI))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return bind.NewBoundContract(address, parsed, caller, transactor), nil
|
||||
return bind.NewBoundContract(address, parsed, caller, transactor, eventer), nil
|
||||
}
|
||||
|
||||
// Call invokes the (constant) contract method with params as input values and
|
||||
|
|
@ -263,6 +318,18 @@ package {{.Package}}
|
|||
return _{{$contract.Type}}.Contract.{{.Normalized.Name}}(&_{{$contract.Type}}.TransactOpts {{range $i, $_ := .Normalized.Inputs}}, {{.Name}}{{end}})
|
||||
}
|
||||
{{end}}
|
||||
|
||||
{{range .Events}}
|
||||
// Solidity: event {{.Canonical}}
|
||||
//
|
||||
// {{if not .Anonymous}}Note: this method will fill in the Event ID topic{{end}}
|
||||
func (_{{$contract.Type}} *{{$contract.Type}}Caller) Subscribe{{.Name}}(opts *bind.CallOpts, ch chan<- types.Log, topics ...[]common.Hash) (ethereum.Subscription, error) {
|
||||
{{if not .Anonymous}}id := []common.Hash{common.HexToHash("{{.ID}}")}
|
||||
topics = append([][]common.Hash{id}, topics...)
|
||||
{{end}}
|
||||
return _{{$contract.Type}}.contract.SubscribeFilterLogs(opts, topics, ch)
|
||||
}
|
||||
{{end}}
|
||||
{{end}}
|
||||
`
|
||||
|
||||
|
|
|
|||
|
|
@ -138,7 +138,7 @@ func BindContract(address *Address, abiJSON string, client *EthereumClient) (con
|
|||
return nil, err
|
||||
}
|
||||
return &BoundContract{
|
||||
contract: bind.NewBoundContract(address.address, parsed, client.client, client.client),
|
||||
contract: bind.NewBoundContract(address.address, parsed, client.client, client.client, client.client),
|
||||
address: address.address,
|
||||
}, nil
|
||||
}
|
||||
|
|
|
|||
Loading…
Reference in a new issue