Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 6 additions & 0 deletions accounts/abi/bind/backend.go
Original file line number Diff line number Diff line change
Expand Up @@ -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
}
7 changes: 7 additions & 0 deletions accounts/abi/bind/backends/simulated.go
Original file line number Diff line number Diff line change
Expand Up @@ -303,6 +303,13 @@ func (b *SimulatedBackend) AdjustTime(adjustment time.Duration) error {
return nil
}

// SubscribeFilterLogs emits logs that match the given criteria over the given channel.
//
// Note: it is currently not implemented.
func (b *SimulatedBackend) SubscribeFilterLogs(ctx context.Context, q ethereum.FilterQuery, ch chan<- types.Log) (ethereum.Subscription, error) {
return nil, fmt.Errorf("not implemented")
}

// callmsg implements core.Message to allow passing it as a transaction simulator.
type callmsg struct {
ethereum.CallMsg
Expand Down
18 changes: 16 additions & 2 deletions accounts/abi/bind/base.go
Original file line number Diff line number Diff line change
Expand Up @@ -63,24 +63,26 @@ 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,
}
}

// DeployContract deploys a contract onto the Ethereum blockchain and binds the
// 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 {
Expand Down Expand Up @@ -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()
Expand Down
13 changes: 13 additions & 0 deletions accounts/abi/bind/bind.go
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -94,13 +95,24 @@ 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),
InputBin: strings.TrimSpace(bytecodes[i]),
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
Expand All @@ -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
Expand Down
79 changes: 73 additions & 6 deletions accounts/abi/bind/template.go
Original file line number Diff line number Diff line change
Expand Up @@ -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 {
Expand All @@ -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
Expand All @@ -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{
Expand Down Expand Up @@ -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.
Expand All @@ -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 {
Expand Down Expand Up @@ -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
}
Expand All @@ -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
}
Expand All @@ -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
Expand Down Expand Up @@ -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}}Eventer) 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}}
`

Expand Down
Loading