Skip to content

Commit 9d7001c

Browse files
committed
accounts/abi/bind/v2: clean up lib.go:
* improve function and type comments (adding a TODO for FilterEvents/WatchEvents descriptions) * simplify EventIterator implementation, change signature of Next to return an error if it occurred. Don't prevent further iteration if the subscription was cancelled before all the logs could be read from the channel (this needs a test case).
1 parent 9d5003c commit 9d7001c

File tree

2 files changed

+75
-63
lines changed

2 files changed

+75
-63
lines changed

accounts/abi/bind/v2/lib.go

Lines changed: 58 additions & 61 deletions
Original file line numberDiff line numberDiff line change
@@ -32,7 +32,8 @@ type ContractEvent interface {
3232
ContractEventName() string
3333
}
3434

35-
// FilterEvents returns an EventIterator instance for filtering historical events based on the event id and a block range.
35+
// FilterEvents filters a historical block range for instances of emission of a
36+
// specific event type from a specified contract. It returns an error if... (TODO: enumerate error scenarios)
3637
func FilterEvents[Ev ContractEvent](c *BoundContract, opts *FilterOpts, unpack func(*types.Log) (*Ev, error), topics ...[]any) (*EventIterator[Ev], error) {
3738
var e Ev
3839
logs, sub, err := c.FilterLogs(opts, e.ContractEventName(), topics...)
@@ -42,10 +43,11 @@ func FilterEvents[Ev ContractEvent](c *BoundContract, opts *FilterOpts, unpack f
4243
return &EventIterator[Ev]{unpack: unpack, logs: logs, sub: sub}, nil
4344
}
4445

45-
// WatchEvents causes logs emitted with a given event id from a specified
46-
// contract to be intercepted, unpacked, and forwarded to sink. If
47-
// unpack returns an error, the returned subscription is closed with the
48-
// error.
46+
// WatchEvents creates an event subscription to notify when logs of the specified event type are emitted from the given contract.
47+
// Received logs are unpacked and forwarded to sink. If topics are specified, only events are forwarded which match the
48+
// topics.
49+
//
50+
// WatchEvents returns a subscription or an error if ... (TODO: enumerate error scenarios)
4951
func WatchEvents[Ev ContractEvent](c *BoundContract, opts *WatchOpts, unpack func(*types.Log) (*Ev, error), sink chan<- *Ev, topics ...[]any) (event.Subscription, error) {
5052
var e Ev
5153
logs, sub, err := c.WatchLogs(opts, e.ContractEventName(), topics...)
@@ -79,86 +81,81 @@ func WatchEvents[Ev ContractEvent](c *BoundContract, opts *WatchOpts, unpack fun
7981
}), nil
8082
}
8183

82-
// EventIterator is returned from FilterLogs and is used to iterate over the raw
83-
// logs and unpacked data for events.
84+
// EventIterator is an object for iterating over the results of a event log filter call.
8485
type EventIterator[T any] struct {
85-
event *T // event containing the contract specifics and raw log
86-
87-
unpack func(*types.Log) (*T, error) // Unpack function for the event
88-
89-
logs <-chan types.Log // Log channel receiving the found contract events
90-
sub ethereum.Subscription // Subscription for solc_errors, completion and termination
91-
done bool // Whether the subscription completed delivering logs
92-
fail error // Occurred error to stop iteration
86+
current *T
87+
unpack func(*types.Log) (*T, error)
88+
logs <-chan types.Log
89+
sub ethereum.Subscription
90+
fail error // error to hold reason for iteration failure
91+
closed bool // true if Close has been called
9392
}
9493

9594
// Value returns the current value of the iterator, or nil if there isn't one.
9695
func (it *EventIterator[T]) Value() *T {
97-
return it.event
96+
return it.current
9897
}
9998

100-
// Next advances the iterator to the subsequent event, returning whether there
101-
// are any more events found. In case of a retrieval or parsing error, false is
102-
// returned and Error() can be queried for the exact failure.
103-
func (it *EventIterator[T]) Next() bool {
104-
// If the iterator failed, stop iterating
105-
if it.fail != nil {
106-
return false
99+
// Next advances the iterator to the subsequent event (if there is one),
100+
// returning true if the iterator advanced.
101+
//
102+
// If the attempt to convert the raw log object to an instance of T using the
103+
// unpack function provided via FilterEvents returns an error: that error is returned and subsequent calls to Next will
104+
// not advance the iterator.
105+
func (it *EventIterator[T]) Next() (advanced bool, err error) {
106+
// If the iterator failed with an error, don't proceed
107+
if it.fail != nil || it.closed {
108+
return false, it.fail
107109
}
108-
// If the iterator completed, deliver directly whatever's available
109-
if it.done {
110+
// if the iterator is still active, block until a log is received or the
111+
// underlying subscription terminates.
112+
select {
113+
case log := <-it.logs:
114+
res, err := it.unpack(&log)
115+
if err != nil {
116+
it.fail = err
117+
return false, it.fail
118+
}
119+
it.current = res
120+
return true, it.fail
121+
case <-it.sub.Err():
122+
// regardless of how the subscription ends, still be able to iterate
123+
// over any unread logs.
110124
select {
111125
case log := <-it.logs:
112126
res, err := it.unpack(&log)
113127
if err != nil {
114128
it.fail = err
115-
return false
129+
return false, it.fail
116130
}
117-
it.event = res
118-
return true
119-
131+
it.current = res
132+
return true, it.fail
120133
default:
121-
return false
134+
return false, it.fail
122135
}
123136
}
124-
// Iterator still in progress, wait for either a data or an error event
125-
select {
126-
case log := <-it.logs:
127-
res, err := it.unpack(&log)
128-
if err != nil {
129-
it.fail = err
130-
return false
131-
}
132-
it.event = res
133-
return true
134-
135-
case err := <-it.sub.Err():
136-
it.done = true
137-
it.fail = err
138-
return it.Next()
139-
}
140137
}
141138

142-
// Error returns any retrieval or parsing error occurred during filtering.
139+
// Error returns an error if iteration has failed.
143140
func (it *EventIterator[T]) Error() error {
144141
return it.fail
145142
}
146143

147-
// Close terminates the iteration process, releasing any pending underlying
148-
// resources.
144+
// Close releases any pending underlying resources. Any subsequent calls to
145+
// Next will not advance the iterator, but the current value remains accessible.
149146
func (it *EventIterator[T]) Close() error {
147+
it.closed = true
150148
it.sub.Unsubscribe()
151149
return nil
152150
}
153151

154-
// Call performs an eth_call on the given bound contract instance, using the provided
155-
// ABI-encoded input.
152+
// Call performs an eth_call to a contract with optional call data.
156153
//
157154
// To call a function that doesn't return any output, pass nil as the unpack function.
158155
// This can be useful if you just want to check that the function doesn't revert.
159-
func Call[T any](c *BoundContract, opts *CallOpts, packedInput []byte, unpack func([]byte) (T, error)) (T, error) {
156+
func Call[T any](c *BoundContract, opts *CallOpts, calldata []byte, unpack func([]byte) (T, error)) (T, error) {
160157
var defaultResult T
161-
packedOutput, err := c.CallRaw(opts, packedInput)
158+
packedOutput, err := c.CallRaw(opts, calldata)
162159
if err != nil {
163160
return defaultResult, err
164161
}
@@ -175,19 +172,19 @@ func Call[T any](c *BoundContract, opts *CallOpts, packedInput []byte, unpack fu
175172
return res, err
176173
}
177174

178-
// Transact initiates a transaction with the given raw calldata as the input.
179-
func Transact(c *BoundContract, opt *TransactOpts, packedInput []byte) (*types.Transaction, error) {
175+
// Transact creates and submits a transaction to a contract with optional input data.
176+
func Transact(c *BoundContract, opt *TransactOpts, data []byte) (*types.Transaction, error) {
180177
addr := c.address
181-
return c.transact(opt, &addr, packedInput)
178+
return c.transact(opt, &addr, data)
182179
}
183180

184-
// DeployContract deploys a contract onto the Ethereum blockchain and binds the
185-
// deployment address with a Go wrapper. It expects its parameters to be abi-encoded
186-
// bytes.
187-
func DeployContract(opts *TransactOpts, bytecode []byte, backend ContractBackend, packedParams []byte) (common.Address, *types.Transaction, error) {
181+
// DeployContract creates and submits a deployment transaction based on the deployer bytecode and
182+
// optional ABI-encoded constructor input. It returns the address and creation transaction of the
183+
// pending contract, or an error if the creation failed.
184+
func DeployContract(opts *TransactOpts, bytecode []byte, backend ContractBackend, constructorInput []byte) (common.Address, *types.Transaction, error) {
188185
c := NewBoundContract(common.Address{}, abi.ABI{}, backend, backend, backend)
189186

190-
tx, err := c.RawCreationTransact(opts, append(bytecode, packedParams...))
187+
tx, err := c.RawCreationTransact(opts, append(bytecode, constructorInput...))
191188
if err != nil {
192189
return common.Address{}, nil, err
193190
}

accounts/abi/bind/v2/lib_test.go

Lines changed: 17 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -283,12 +283,27 @@ done:
283283
if err != nil {
284284
t.Fatalf("error filtering logs %v\n", err)
285285
}
286+
286287
e1Count = 0
287288
e2Count = 0
288-
for it.Next() {
289+
for {
290+
advanced, err := it.Next()
291+
if err != nil {
292+
t.Fatalf("got error while iterating events for e1: %v", err)
293+
}
294+
if !advanced {
295+
break
296+
}
289297
e1Count++
290298
}
291-
for it2.Next() {
299+
for {
300+
advanced, err := it2.Next()
301+
if err != nil {
302+
t.Fatalf("got error while iterating events for e2: %v", err)
303+
}
304+
if !advanced {
305+
break
306+
}
292307
e2Count++
293308
}
294309
if e1Count != 2 {

0 commit comments

Comments
 (0)