From 6c70418313b56f96522d2dcf56f21931ce71a13d Mon Sep 17 00:00:00 2001 From: Jared Wasinger Date: Mon, 3 Feb 2025 20:00:45 -0800 Subject: [PATCH] accounts/abi/bind/v2: improvement on the previous commit: remove all publicly-exported member methods from v2 BoundContract. Most of these are only needed to implement the contract interaction methods in accounts/abi/bind/v2/lib.go . Add 'Transact' method to the contract interaction API. --- accounts/abi/abigen/source2.go.tpl | 2 +- accounts/abi/bind/v2/base.go | 44 ++++++++++++++++--------- accounts/abi/bind/v2/boundcontractv1.go | 2 +- accounts/abi/bind/v2/dep_tree.go | 2 +- accounts/abi/bind/v2/lib.go | 32 ++++++++---------- accounts/abi/bind/v2/lib_test.go | 2 +- 6 files changed, 46 insertions(+), 38 deletions(-) diff --git a/accounts/abi/abigen/source2.go.tpl b/accounts/abi/abigen/source2.go.tpl index fc28aa9a46..ea559473f6 100644 --- a/accounts/abi/abigen/source2.go.tpl +++ b/accounts/abi/abigen/source2.go.tpl @@ -69,7 +69,7 @@ var ( // Instance creates a wrapper for a deployed contract instance at the given address. // Use this to create the instance object passed to abigen v2 library functions Call, Transact, etc. - func (c *{{.Type}}) Instance(backend bind.ContractBackend, addr common.Address) *bind.BoundContract { + func (c *{{.Type}}) Instance(backend bind.ContractBackend, addr common.Address) bind.BoundContract { return bind.NewContractInstance(backend, addr, c.abi) } diff --git a/accounts/abi/bind/v2/base.go b/accounts/abi/bind/v2/base.go index 4c04c9e49e..8ec4dcd583 100644 --- a/accounts/abi/bind/v2/base.go +++ b/accounts/abi/bind/v2/base.go @@ -19,6 +19,7 @@ package bind import ( "context" "errors" + "github.com/ethereum/go-ethereum/event" "math/big" "strings" "sync" @@ -121,21 +122,34 @@ func (m *MetaData) ParseABI() (*abi.ABI, error) { return m.parsedABI, nil } -// BoundContract is the base wrapper object that reflects a contract on the -// Ethereum network. It contains a collection of methods that are used by the -// higher level contract bindings to operate. -type BoundContract struct { - address common.Address // Deployment address of the contract on the Ethereum blockchain - abi abi.ABI // Reflect based ABI to access the correct Ethereum methods - backend ContractBackend +// BoundContract represents a contract deployed on-chain. It does not export any methods, and is used in the low-level +// contract interaction API methods provided in the v2 package. +type BoundContract interface { + filterLogs(opts *FilterOpts, name string, query ...[]any) (chan types.Log, event.Subscription, error) + watchLogs(opts *WatchOpts, name string, query ...[]any) (chan types.Log, event.Subscription, error) + rawCreationTransact(opts *TransactOpts, calldata []byte) (*types.Transaction, error) + call(opts *CallOpts, input []byte) ([]byte, error) + transact(opts *TransactOpts, contract *common.Address, input []byte) (*types.Transaction, error) + addr() common.Address } -// NewBoundContract creates a low level contract interface through which calls -// and transactions may be made through. -func NewBoundContract(backend ContractBackend, address common.Address, abi abi.ABI) *BoundContract { - return &BoundContract{ - address, - abi, - backend, - } +// NewBoundContract creates a new BoundContract instance. +func NewBoundContract(backend ContractBackend, address common.Address, abi abi.ABI) BoundContract { + return NewBoundContractV1(address, abi, backend, backend, backend) +} + +func (c *BoundContractV1) filterLogs(opts *FilterOpts, name string, query ...[]any) (chan types.Log, event.Subscription, error) { + return c.FilterLogs(opts, name, query...) +} + +func (c *BoundContractV1) watchLogs(opts *WatchOpts, name string, query ...[]any) (chan types.Log, event.Subscription, error) { + return c.WatchLogs(opts, name, query...) +} + +func (c *BoundContractV1) rawCreationTransact(opts *TransactOpts, calldata []byte) (*types.Transaction, error) { + return c.RawCreationTransact(opts, calldata) +} + +func (c *BoundContractV1) addr() common.Address { + return c.address } diff --git a/accounts/abi/bind/v2/boundcontractv1.go b/accounts/abi/bind/v2/boundcontractv1.go index 29ca2d7b7b..565af66d21 100644 --- a/accounts/abi/bind/v2/boundcontractv1.go +++ b/accounts/abi/bind/v2/boundcontractv1.go @@ -168,7 +168,7 @@ func (c *BoundContractV1) RawTransact(opts *TransactOpts, calldata []byte) (*typ return c.transact(opts, &c.address, calldata) } -// RawTransact initiates a contract-creation transaction with the given raw calldata as the input. +// RawCreationTransact initiates a contract-creation transaction with the given raw calldata as the input. func (c *BoundContractV1) RawCreationTransact(opts *TransactOpts, calldata []byte) (*types.Transaction, error) { return c.transact(opts, nil, calldata) } diff --git a/accounts/abi/bind/v2/dep_tree.go b/accounts/abi/bind/v2/dep_tree.go index bd34cabfeb..dcf981b329 100644 --- a/accounts/abi/bind/v2/dep_tree.go +++ b/accounts/abi/bind/v2/dep_tree.go @@ -47,7 +47,7 @@ type DeploymentResult struct { } // DeployFn deploys a contract given a deployer and optional input. It returns -// the address and a pending transaction, or an error if the deployment failed. +// the address of the deployed contract and the deployment transaction, or an error if the deployment failed. type DeployFn func(input, deployer []byte) (common.Address, *types.Transaction, error) // depTreeDeployer is responsible for taking a dependency, deploying-and-linking its components in the proper diff --git a/accounts/abi/bind/v2/lib.go b/accounts/abi/bind/v2/lib.go index b7ad4c0abe..4cdd926f81 100644 --- a/accounts/abi/bind/v2/lib.go +++ b/accounts/abi/bind/v2/lib.go @@ -28,7 +28,7 @@ import ( ) // TODO: remove this and use NewBoundContract directly -func NewContractInstance(backend ContractBackend, addr common.Address, abi abi.ABI) *BoundContract { +func NewContractInstance(backend ContractBackend, addr common.Address, abi abi.ABI) BoundContract { return NewBoundContract(backend, addr, abi) } @@ -38,10 +38,9 @@ type ContractEvent interface { } // FilterEvents returns an EventIterator instance for filtering historical events based on the event id and a block range. -func FilterEvents[Ev ContractEvent](c *BoundContract, opts *FilterOpts, unpack func(*types.Log) (*Ev, error), topics ...[]any) (*EventIterator[Ev], error) { +func FilterEvents[Ev ContractEvent](c BoundContract, opts *FilterOpts, unpack func(*types.Log) (*Ev, error), topics ...[]any) (*EventIterator[Ev], error) { var e Ev - v1Instance := BoundContractV1{c.address, c.abi, c.backend, c.backend, c.backend} - logs, sub, err := v1Instance.FilterLogs(opts, e.ContractEventName(), topics...) + logs, sub, err := c.filterLogs(opts, e.ContractEventName(), topics...) if err != nil { return nil, err } @@ -52,10 +51,9 @@ func FilterEvents[Ev ContractEvent](c *BoundContract, opts *FilterOpts, unpack f // contract to be intercepted, unpacked, and forwarded to sink. If // unpack returns an error, the returned subscription is closed with the // error. -func WatchEvents[Ev ContractEvent](c *BoundContract, opts *WatchOpts, unpack func(*types.Log) (*Ev, error), sink chan<- *Ev, topics ...[]any) (event.Subscription, error) { +func WatchEvents[Ev ContractEvent](c BoundContract, opts *WatchOpts, unpack func(*types.Log) (*Ev, error), sink chan<- *Ev, topics ...[]any) (event.Subscription, error) { var e Ev - v1Instance := BoundContractV1{c.address, c.abi, c.backend, c.backend, c.backend} - logs, sub, err := v1Instance.WatchLogs(opts, e.ContractEventName(), topics...) + logs, sub, err := c.watchLogs(opts, e.ContractEventName(), topics...) if err != nil { return nil, err } @@ -162,10 +160,9 @@ func (it *EventIterator[T]) Close() error { // // To call a function that doesn't return any output, pass nil as the unpack function. // This can be useful if you just want to check that the function doesn't revert. -func Call[T any](c *BoundContract, opts *CallOpts, packedInput []byte, unpack func([]byte) (T, error)) (T, error) { +func Call[T any](c BoundContract, opts *CallOpts, packedInput []byte, unpack func([]byte) (T, error)) (T, error) { var defaultResult T - v1Instance := BoundContractV1{c.address, c.abi, c.backend, c.backend, c.backend} - packedOutput, err := v1Instance.CallRaw(opts, packedInput) + packedOutput, err := c.call(opts, packedInput) if err != nil { return defaultResult, err } @@ -182,11 +179,15 @@ func Call[T any](c *BoundContract, opts *CallOpts, packedInput []byte, unpack fu return res, err } +// Transact initiates a transaction with the given raw calldata as the input. +func Transact(c BoundContract, opt *TransactOpts, packedInput []byte) (*types.Transaction, error) { + addr := c.addr() + return c.transact(opt, &addr, packedInput) +} + // DeployContractRaw deploys a contract onto the Ethereum blockchain and binds the // deployment address with a Go wrapper. It expects its parameters to be abi-encoded // bytes. -// -// TODO: remove address return? we can calculate it from the returned tx func DeployContractRaw(opts *TransactOpts, bytecode []byte, backend ContractBackend, packedParams []byte) (common.Address, *types.Transaction, error) { c := NewBoundContractV1(common.Address{}, abi.ABI{}, backend, backend, backend) tx, err := c.RawCreationTransact(opts, append(bytecode, packedParams...)) @@ -196,10 +197,3 @@ func DeployContractRaw(opts *TransactOpts, bytecode []byte, backend ContractBack address := crypto.CreateAddress(opts.From, tx.Nonce()) return address, tx, nil } - -// RawTransact initiates a transaction with the given raw calldata as the input. -// It's usually used to initiate transactions for invoking **Fallback** function. -func (c *BoundContract) RawTransact(opts *TransactOpts, calldata []byte) (*types.Transaction, error) { - v1Instance := BoundContractV1{c.address, c.abi, c.backend, c.backend, c.backend} - return v1Instance.RawTransact(opts, calldata) -} diff --git a/accounts/abi/bind/v2/lib_test.go b/accounts/abi/bind/v2/lib_test.go index 72fe29954c..78d6fed55c 100644 --- a/accounts/abi/bind/v2/lib_test.go +++ b/accounts/abi/bind/v2/lib_test.go @@ -245,7 +245,7 @@ func TestEvents(t *testing.T) { defer sub2.Unsubscribe() packedInput := c.PackEmitMulti() - tx, err := instance.RawTransact(txAuth, packedInput) + tx, err := bind.Transact(instance, txAuth, packedInput) if err != nil { t.Fatalf("failed to send transaction: %v", err) }