From 1f25c68ec42a7794c040e214755633fac1f275c1 Mon Sep 17 00:00:00 2001 From: Jared Wasinger Date: Mon, 2 Dec 2024 11:51:45 +0700 Subject: [PATCH] add error unpacking. simplify WatchLogs api. --- accounts/abi/bind/source2.go.tpl | 20 ++++++++++ accounts/abi/bind/v2/lib.go | 13 +------ accounts/abi/bind/v2/lib_test.go | 66 ++++++++++++++++++++------------ 3 files changed, 63 insertions(+), 36 deletions(-) diff --git a/accounts/abi/bind/source2.go.tpl b/accounts/abi/bind/source2.go.tpl index 223b41da47..34bf50b8ea 100644 --- a/accounts/abi/bind/source2.go.tpl +++ b/accounts/abi/bind/source2.go.tpl @@ -148,4 +148,24 @@ var ( return out, nil } {{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}} \ No newline at end of file diff --git a/accounts/abi/bind/v2/lib.go b/accounts/abi/bind/v2/lib.go index eaef2832ea..9345662a1a 100644 --- a/accounts/abi/bind/v2/lib.go +++ b/accounts/abi/bind/v2/lib.go @@ -237,7 +237,7 @@ func FilterLogs[T any](instance *ContractInstance, opts *bind.FilterOpts, eventI // contract to be intercepted, unpacked, and forwarded to sink. If // unpack returns an error, the returned subscription is closed with the // 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 c := bind.NewBoundContract(instance.Address, abi, backend, backend, backend) 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 { select { case log := <-logs: - // New log arrived, parse the event and forward to the user - ev, err := unpack(&log) + err := onLog(&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: diff --git a/accounts/abi/bind/v2/lib_test.go b/accounts/abi/bind/v2/lib_test.go index eb6bf6ae09..3d09ac2a21 100644 --- a/accounts/abi/bind/v2/lib_test.go +++ b/accounts/abi/bind/v2/lib_test.go @@ -381,26 +381,28 @@ func TestEvents(t *testing.T) { 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) newCBasic2Ch := make(chan *events.CBasic2) watchOpts := &bind.WatchOpts{ Start: nil, Context: context.Background(), } - sub1, err := WatchLogs[events.CBasic1](&boundContract, *abi, watchOpts, events.CBasic1EventID(), unpackBasic, newCBasic1Ch) - sub2, err := WatchLogs[events.CBasic2](&boundContract, *abi, watchOpts, events.CBasic2EventID(), unpackBasic2, newCBasic2Ch) + sub1, err := WatchLogs(&boundContract, *abi, watchOpts, events.CBasic1EventID(), func(raw *types.Log) error { + 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 sub2.Unsubscribe() @@ -452,6 +454,18 @@ done: Start: 0, 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. it, err := FilterLogs[events.CBasic1](crtctInstance, filterOpts, events.CBasic1EventID(), unpackBasic) if err != nil { @@ -518,23 +532,25 @@ func TestEventsUnpackFailure(t *testing.T) { 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) 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{ Start: nil, Context: context.Background(), } - sub1, err := WatchLogs[events.CBasic1](&boundContract, *abi, watchOpts, events.CBasic1EventID(), unpackBasic, newCBasic1Ch) - sub2, err := WatchLogs[events.CBasic2](&boundContract, *abi, watchOpts, events.CBasic2EventID(), unpackBasic2, newCBasic2Ch) + sub1, err := WatchLogs(&boundContract, *abi, watchOpts, events.CBasic1EventID(), unpackBasic) + sub2, err := WatchLogs(&boundContract, *abi, watchOpts, events.CBasic2EventID(), unpackBasic2) defer sub1.Unsubscribe() defer sub2.Unsubscribe()