From aa651aecaba3bb35336f91524ff2d33fef7f9285 Mon Sep 17 00:00:00 2001 From: Jared Wasinger Date: Mon, 4 Nov 2024 22:50:47 +0900 Subject: [PATCH] move v2 contract interaction methods into v2 package. add test --- accounts/abi/bind/lib.go | 148 -------------------------------- accounts/abi/bind/v2/v2_test.go | 145 +++++++++++++++++++++++++++++++ 2 files changed, 145 insertions(+), 148 deletions(-) create mode 100644 accounts/abi/bind/v2/v2_test.go diff --git a/accounts/abi/bind/lib.go b/accounts/abi/bind/lib.go index 6d3ea71264..7570665f87 100644 --- a/accounts/abi/bind/lib.go +++ b/accounts/abi/bind/lib.go @@ -17,12 +17,8 @@ package bind import ( - "github.com/ethereum/go-ethereum" "github.com/ethereum/go-ethereum/accounts/abi" "github.com/ethereum/go-ethereum/common" - "github.com/ethereum/go-ethereum/core/types" - "github.com/ethereum/go-ethereum/crypto" - "github.com/ethereum/go-ethereum/event" ) // ContractInstance provides means to interact with @@ -32,152 +28,8 @@ type ContractInstance interface { Backend() ContractBackend } -func DeployContract2(opts *TransactOpts, bytecode []byte, input []byte, backend ContractBackend) (common.Address, *types.Transaction, error) { - c := NewBoundContract(common.Address{}, abi.ABI{}, backend, backend, backend) - tx, err := c.transact(opts, nil, append(bytecode, input...)) - if err != nil { - return common.Address{}, nil, err - } - address := crypto.CreateAddress(opts.From, tx.Nonce()) - return address, tx, nil -} - -func Call2[T any](instance ContractInstance, opts *CallOpts, input []byte, unpack func([]byte) (T, error)) (arg T, err error) { - var data []byte - data, err = CallRaw(instance, opts, input) - if err != nil { - return - } - return unpack(data) -} - func CallRaw(instance ContractInstance, opts *CallOpts, input []byte) ([]byte, error) { backend := instance.Backend() c := NewBoundContract(instance.Address(), abi.ABI{}, backend, backend, backend) return c.call(opts, input) } - -func Transact2(instance ContractInstance, opts *TransactOpts, input []byte) (*types.Transaction, error) { - var ( - addr = instance.Address() - backend = instance.Backend() - ) - c := NewBoundContract(addr, abi.ABI{}, backend, backend, backend) - return c.transact(opts, &addr, input) -} - -func Transfer2(instance ContractInstance, opts *TransactOpts) (*types.Transaction, error) { - backend := instance.Backend() - c := NewBoundContract(instance.Address(), abi.ABI{}, backend, backend, backend) - return c.Transfer(opts) -} - -func FilterLogs[T any](instance ContractInstance, opts *FilterOpts, eventID common.Hash, unpack func(*types.Log) (*T, error), topics ...[]any) (*EventIterator[T], error) { - backend := instance.Backend() - c := NewBoundContract(instance.Address(), abi.ABI{}, backend, backend, backend) - logs, sub, err := c.filterLogs(opts, eventID, topics...) - if err != nil { - return nil, err - } - return &EventIterator[T]{unpack: unpack, logs: logs, sub: sub}, nil -} - -func WatchLogs[T any](instance ContractInstance, opts *WatchOpts, eventID common.Hash, unpack func(*types.Log) (*T, error), sink chan<- *T, topics ...[]any) (event.Subscription, error) { - backend := instance.Backend() - c := NewBoundContract(instance.Address(), abi.ABI{}, backend, backend, backend) - logs, sub, err := c.watchLogs(opts, eventID, 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 -} diff --git a/accounts/abi/bind/v2/v2_test.go b/accounts/abi/bind/v2/v2_test.go new file mode 100644 index 0000000000..a6d16806f7 --- /dev/null +++ b/accounts/abi/bind/v2/v2_test.go @@ -0,0 +1,145 @@ +package v2 + +import ( + "context" + "encoding/json" + "github.com/ethereum/go-ethereum/accounts/abi/bind/backends" + "io" + + "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/crypto" + "github.com/ethereum/go-ethereum/ethclient/simulated" + "github.com/ethereum/go-ethereum/params" + "math/big" + "strings" + "testing" +) + +const deployer = "6080604052348015600e575f80fd5b506102098061001c5f395ff3fe608060405234801561000f575f80fd5b506004361061003f575f3560e01c80636da1cd55146100435780637b0cb83914610061578063bf54fad41461006b575b5f80fd5b61004b610087565b60405161005891906100e9565b60405180910390f35b61006961008f565b005b61008560048036038101906100809190610130565b6100c8565b005b5f8054905090565b607b7f72c79b1cb25b1b49ae522446226e1591b80634619cef7e71846da52b61b7061d6040516100be906101b5565b60405180910390a2565b805f8190555050565b5f819050919050565b6100e3816100d1565b82525050565b5f6020820190506100fc5f8301846100da565b92915050565b5f80fd5b61010f816100d1565b8114610119575f80fd5b50565b5f8135905061012a81610106565b92915050565b5f6020828403121561014557610144610102565b5b5f6101528482850161011c565b91505092915050565b5f82825260208201905092915050565b7f737472696e6700000000000000000000000000000000000000000000000000005f82015250565b5f61019f60068361015b565b91506101aa8261016b565b602082019050919050565b5f6020820190508181035f8301526101cc81610193565b905091905056fea2646970667358221220212a3a765a98254b596386fdfd10318f9a4bf19e8c9ca9ffa363f990c1798bf664736f6c634300081a0033" + +const contractABIStr = ` +[ + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "uint256", + "name": "firstArg", + "type": "uint256" + }, + { + "indexed": false, + "internalType": "string", + "name": "secondArg", + "type": "string" + } + ], + "name": "ExampleEvent", + "type": "event" + }, + { + "inputs": [], + "name": "emitEvent", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "uint256", + "name": "num", + "type": "uint256" + } + ], + "name": "mutateStorageVal", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [], + "name": "retrieveStorageVal", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "stateMutability": "view", + "type": "function" + } +] +` + +var testKey, _ = crypto.HexToECDSA("b71c71a67e1177ad4e901695e1b4b9ee17ae16c6668d313eac2f96dbcda3f291") + +// JSON returns a parsed ABI interface and error if it failed. +func JSON(reader io.Reader) (abi.ABI, error) { + dec := json.NewDecoder(reader) + + var instance abi.ABI + if err := dec.Decode(&instance); err != nil { + return abi.ABI{}, err + } + return instance, nil +} + +func TestV2(t *testing.T) { + testAddr := crypto.PubkeyToAddress(testKey.PublicKey) + backend := simulated.NewBackend( + types.GenesisAlloc{ + testAddr: {Balance: big.NewInt(10000000000000000)}, + }, + ) + defer backend.Close() + + contractABI, err := JSON(strings.NewReader(contractABIStr)) + if err != nil { + panic(err) + } + + signer := types.LatestSigner(params.AllDevChainProtocolChanges) + opts := bind.TransactOpts{ + From: testAddr, + Nonce: nil, + Signer: func(address common.Address, tx *types.Transaction) (*types.Transaction, error) { + signature, err := crypto.Sign(signer.Hash(tx).Bytes(), testKey) + if err != nil { + t.Fatal(err) + } + signedTx, err := tx.WithSignature(signer, signature) + if err != nil { + t.Fatal(err) + } + return signedTx, nil + }, + Context: context.Background(), + /* + Value: nil, + GasPrice: nil, + GasFeeCap: nil, + GasTipCap: nil, + GasLimit: 0, + AccessList: nil, + NoSend: false, + */ + } + // we should just be able to use the backend directly, instead of using + // this deprecated interface. However, the simulated backend no longer + // implements backends.SimulatedBackend... + bindBackend := backends.SimulatedBackend{ + Backend: backend, + Client: backend.Client(), + } + _, _, _, err = bind.DeployContract(&opts, contractABI, common.Hex2Bytes(deployer), &bindBackend) + if err != nil { + t.Fatal(err) + } + // send a balance to our contract (contract must accept ether by default) +}