add error unpacking. simplify WatchLogs api.

This commit is contained in:
Jared Wasinger 2024-12-02 11:51:45 +07:00 committed by Felix Lange
parent 4fc62e94b3
commit 1f25c68ec4
3 changed files with 63 additions and 36 deletions

View File

@ -148,4 +148,24 @@ var (
return out, nil return out, nil
} }
{{end}} {{end}}
{{range .Errors}}
// {{$contract.Type}}{{.Normalized.Name}} represents a {{.Normalized.Name}} error 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}}
}
func {{$contract.Type}}{{.Normalized.Name}}ErrorID() common.Hash {
return common.HexToHash("{{.Original.ID}}")
}
func (_{{$contract.Type}} *{{$contract.Type}}) Unpack{{.Normalized.Name}}Error(raw []byte) (*{{$contract.Type}}{{.Normalized.Name}}, error) {
errName := "{{.Normalized.Name}}"
out := new({{$contract.Type}}{{.Normalized.Name}})
if err := _{{$contract.Type}}.abi.UnpackIntoInterface(out, errName, raw); err != nil {
return nil, err
}
return out, nil
}
{{end}}
{{end}} {{end}}

View File

@ -237,7 +237,7 @@ func FilterLogs[T any](instance *ContractInstance, opts *bind.FilterOpts, eventI
// contract to be intercepted, unpacked, and forwarded to sink. If // contract to be intercepted, unpacked, and forwarded to sink. If
// unpack returns an error, the returned subscription is closed with the // unpack returns an error, the returned subscription is closed with the
// error. // error.
func WatchLogs[T any](instance *ContractInstance, abi abi.ABI, opts *bind.WatchOpts, eventID common.Hash, unpack func(*types.Log) (*T, error), sink chan<- *T, topics ...[]any) (event.Subscription, error) { func WatchLogs(instance *ContractInstance, abi abi.ABI, opts *bind.WatchOpts, eventID common.Hash, onLog func(*types.Log) error, topics ...[]any) (event.Subscription, error) {
backend := instance.Backend backend := instance.Backend
c := bind.NewBoundContract(instance.Address, abi, backend, backend, backend) c := bind.NewBoundContract(instance.Address, abi, backend, backend, backend)
logs, sub, err := c.WatchLogsForId(opts, eventID, topics...) logs, sub, err := c.WatchLogsForId(opts, eventID, topics...)
@ -249,19 +249,10 @@ func WatchLogs[T any](instance *ContractInstance, abi abi.ABI, opts *bind.WatchO
for { for {
select { select {
case log := <-logs: case log := <-logs:
// New log arrived, parse the event and forward to the user err := onLog(&log)
ev, err := unpack(&log)
if err != nil { if err != nil {
return err return err
} }
select {
case sink <- ev:
case err := <-sub.Err():
return err
case <-quit:
return nil
}
case err := <-sub.Err(): case err := <-sub.Err():
return err return err
case <-quit: case <-quit:

View File

@ -381,26 +381,28 @@ func TestEvents(t *testing.T) {
backend, backend,
} }
unpackBasic := func(raw *types.Log) (*events.CBasic1, error) {
return &events.CBasic1{
Id: (new(big.Int)).SetBytes(raw.Topics[0].Bytes()),
Data: (new(big.Int)).SetBytes(raw.Data),
}, nil
}
unpackBasic2 := func(raw *types.Log) (*events.CBasic2, error) {
return &events.CBasic2{
Flag: false, // TODO: how to unpack different types to go types? this should be exposed via abi package.
Data: (new(big.Int)).SetBytes(raw.Data),
}, nil
}
newCBasic1Ch := make(chan *events.CBasic1) newCBasic1Ch := make(chan *events.CBasic1)
newCBasic2Ch := make(chan *events.CBasic2) newCBasic2Ch := make(chan *events.CBasic2)
watchOpts := &bind.WatchOpts{ watchOpts := &bind.WatchOpts{
Start: nil, Start: nil,
Context: context.Background(), Context: context.Background(),
} }
sub1, err := WatchLogs[events.CBasic1](&boundContract, *abi, watchOpts, events.CBasic1EventID(), unpackBasic, newCBasic1Ch) sub1, err := WatchLogs(&boundContract, *abi, watchOpts, events.CBasic1EventID(), func(raw *types.Log) error {
sub2, err := WatchLogs[events.CBasic2](&boundContract, *abi, watchOpts, events.CBasic2EventID(), unpackBasic2, newCBasic2Ch) event := &events.CBasic1{
Id: (new(big.Int)).SetBytes(raw.Topics[0].Bytes()),
Data: (new(big.Int)).SetBytes(raw.Data),
}
newCBasic1Ch <- event
return nil
})
sub2, err := WatchLogs(&boundContract, *abi, watchOpts, events.CBasic2EventID(), func(raw *types.Log) error {
event := &events.CBasic2{
Flag: false, // TODO: how to unpack different types to go types? this should be exposed via abi package.
Data: (new(big.Int)).SetBytes(raw.Data),
}
newCBasic2Ch <- event
return nil
})
defer sub1.Unsubscribe() defer sub1.Unsubscribe()
defer sub2.Unsubscribe() defer sub2.Unsubscribe()
@ -452,6 +454,18 @@ done:
Start: 0, Start: 0,
Context: context.Background(), Context: context.Background(),
} }
unpackBasic := func(raw *types.Log) (*events.CBasic1, error) {
return &events.CBasic1{
Id: (new(big.Int)).SetBytes(raw.Topics[0].Bytes()),
Data: (new(big.Int)).SetBytes(raw.Data),
}, nil
}
unpackBasic2 := func(raw *types.Log) (*events.CBasic2, error) {
return &events.CBasic2{
Flag: false, // TODO: how to unpack different types to go types? this should be exposed via abi package.
Data: (new(big.Int)).SetBytes(raw.Data),
}, nil
}
// TODO: test that returning error from unpack prevents event from being received by sink. // TODO: test that returning error from unpack prevents event from being received by sink.
it, err := FilterLogs[events.CBasic1](crtctInstance, filterOpts, events.CBasic1EventID(), unpackBasic) it, err := FilterLogs[events.CBasic1](crtctInstance, filterOpts, events.CBasic1EventID(), unpackBasic)
if err != nil { if err != nil {
@ -518,23 +532,25 @@ func TestEventsUnpackFailure(t *testing.T) {
backend, backend,
} }
unpackBasic := func(raw *types.Log) (*events.CBasic1, error) {
return nil, fmt.Errorf("this error should stop the filter that uses this unpack.")
}
unpackBasic2 := func(raw *types.Log) (*events.CBasic2, error) {
return &events.CBasic2{
Flag: false, // TODO: how to unpack different types to go types? this should be exposed via abi package.
Data: (new(big.Int)).SetBytes(raw.Data),
}, nil
}
newCBasic1Ch := make(chan *events.CBasic1) newCBasic1Ch := make(chan *events.CBasic1)
newCBasic2Ch := make(chan *events.CBasic2) newCBasic2Ch := make(chan *events.CBasic2)
unpackBasic := func(raw *types.Log) error {
return fmt.Errorf("this error should stop the filter that uses this unpack.")
}
unpackBasic2 := func(raw *types.Log) error {
newCBasic2Ch <- &events.CBasic2{
Flag: false, // TODO: how to unpack different types to go types? this should be exposed via abi package.
Data: (new(big.Int)).SetBytes(raw.Data),
}
return nil
}
watchOpts := &bind.WatchOpts{ watchOpts := &bind.WatchOpts{
Start: nil, Start: nil,
Context: context.Background(), Context: context.Background(),
} }
sub1, err := WatchLogs[events.CBasic1](&boundContract, *abi, watchOpts, events.CBasic1EventID(), unpackBasic, newCBasic1Ch) sub1, err := WatchLogs(&boundContract, *abi, watchOpts, events.CBasic1EventID(), unpackBasic)
sub2, err := WatchLogs[events.CBasic2](&boundContract, *abi, watchOpts, events.CBasic2EventID(), unpackBasic2, newCBasic2Ch) sub2, err := WatchLogs(&boundContract, *abi, watchOpts, events.CBasic2EventID(), unpackBasic2)
defer sub1.Unsubscribe() defer sub1.Unsubscribe()
defer sub2.Unsubscribe() defer sub2.Unsubscribe()