From 9c086811aebf3e784af3b0ecf3a04655cfd494e0 Mon Sep 17 00:00:00 2001 From: Jared Wasinger Date: Mon, 4 Nov 2024 19:32:26 +0900 Subject: [PATCH] isolate V2 contract interaction API into its own package --- accounts/abi/bind/v2/lib.go | 135 ++++++++++++++++++++++++++++++++++++ 1 file changed, 135 insertions(+) create mode 100644 accounts/abi/bind/v2/lib.go diff --git a/accounts/abi/bind/v2/lib.go b/accounts/abi/bind/v2/lib.go new file mode 100644 index 0000000000..bd66202bef --- /dev/null +++ b/accounts/abi/bind/v2/lib.go @@ -0,0 +1,135 @@ +package v2 + +import ( + "github.com/ethereum/go-ethereum" + "github.com/ethereum/go-ethereum/accounts/abi" + "github.com/ethereum/go-ethereum/accounts/abi/bind" + "github.com/ethereum/go-ethereum/common" + "github.com/ethereum/go-ethereum/core/types" + "github.com/ethereum/go-ethereum/event" +) + +func FilterLogs[T any](instance bind.ContractInstance, opts *bind.FilterOpts, eventID common.Hash, unpack func(*types.Log) (*T, error), topics ...[]any) (*EventIterator[T], error) { + backend := instance.Backend() + c := bind.NewBoundContract(instance.Address(), abi.ABI{}, backend, backend, backend) + logs, sub, err := c.FilterLogs(opts, eventID.String(), topics...) + if err != nil { + return nil, err + } + return &EventIterator[T]{unpack: unpack, logs: logs, sub: sub}, nil +} + +func WatchLogs[T any](instance bind.ContractInstance, opts *bind.WatchOpts, eventID common.Hash, unpack func(*types.Log) (*T, error), sink chan<- *T, topics ...[]any) (event.Subscription, error) { + backend := instance.Backend() + c := bind.NewBoundContract(instance.Address(), abi.ABI{}, backend, backend, backend) + logs, sub, err := c.WatchLogs(opts, eventID.String(), topics...) + if err != nil { + return nil, err + } + return event.NewSubscription(func(quit <-chan struct{}) error { + defer sub.Unsubscribe() + for { + select { + case log := <-logs: + // New log arrived, parse the event and forward to the user + ev, err := unpack(&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: + return nil + } + } + }), nil +} + +// EventIterator is returned from FilterLogs and is used to iterate over the raw logs and unpacked data for events. +type EventIterator[T any] struct { + Event *T // Event containing the contract specifics and raw log + + unpack func(*types.Log) (*T, error) // Unpack function for the event + + logs chan types.Log // Log channel receiving the found contract events + sub ethereum.Subscription // Subscription for errors, completion and termination + done bool // Whether the subscription completed delivering logs + fail error // Occurred error to stop iteration +} + +// Next advances the iterator to the subsequent event, returning whether there +// are any more events found. In case of a retrieval or parsing error, false is +// returned and Error() can be queried for the exact failure. +func (it *EventIterator[T]) Next() bool { + // If the iterator failed, stop iterating + if it.fail != nil { + return false + } + // If the iterator completed, deliver directly whatever's available + if it.done { + select { + case log := <-it.logs: + res, err := it.unpack(&log) + if err != nil { + it.fail = err + return false + } + it.Event = res + return true + + default: + return false + } + } + // Iterator still in progress, wait for either a data or an error event + select { + case log := <-it.logs: + res, err := it.unpack(&log) + if err != nil { + it.fail = err + return false + } + it.Event = res + return true + + case err := <-it.sub.Err(): + it.done = true + it.fail = err + return it.Next() + } +} + +// Error returns any retrieval or parsing error occurred during filtering. +func (it *EventIterator[T]) Error() error { + return it.fail +} + +// Close terminates the iteration process, releasing any pending underlying +// resources. +func (it *EventIterator[T]) Close() error { + it.sub.Unsubscribe() + return nil +} + +func Transact(instance bind.ContractInstance, opts *bind.TransactOpts, input []byte) (*types.Transaction, error) { + var ( + addr = instance.Address() + backend = instance.Backend() + ) + c := bind.NewBoundContract(addr, abi.ABI{}, backend, backend, backend) + return c.RawTransact(opts, input) +} + +func Transfer(instance bind.ContractInstance, opts *bind.TransactOpts) (*types.Transaction, error) { + backend := instance.Backend() + c := bind.NewBoundContract(instance.Address(), abi.ABI{}, backend, backend, backend) + return c.Transfer(opts) +}