accounts/abi/abigen, account/abi/bind/v2: polish code

This commit is contained in:
Gary Rong 2025-02-06 11:59:41 +08:00
parent 6680b836f6
commit 8a4a5a60fb
10 changed files with 162 additions and 141 deletions

View file

@ -351,8 +351,8 @@ func bindTopicType(kind abi.Type, structs map[string]*tmplStruct) string {
}
// bindStructType converts a Solidity tuple type to a Go one and records the mapping
// in the given map.
// Notably, this function will resolve and record nested struct recursively.
// in the given map. Notably, this function will resolve and record nested struct
// recursively.
func bindStructType(kind abi.Type, structs map[string]*tmplStruct) string {
switch kind.T {
case abi.TupleTy:
@ -374,7 +374,11 @@ func bindStructType(kind abi.Type, structs map[string]*tmplStruct) string {
name := abi.ToCamelCase(kind.TupleRawNames[i])
name = abi.ResolveNameConflict(name, func(s string) bool { return names[s] })
names[name] = true
fields = append(fields, &tmplField{Type: bindStructType(*elem, structs), Name: name, SolKind: *elem})
fields = append(fields, &tmplField{
Type: bindStructType(*elem, structs),
Name: name,
SolKind: *elem,
})
}
name := kind.TupleRawName
if name == "" {
@ -410,7 +414,6 @@ func decapitalise(input string) string {
if len(input) == 0 {
return input
}
goForm := abi.ToCamelCase(input)
return strings.ToLower(goForm[:1]) + goForm[1:]
}

View file

@ -31,7 +31,8 @@ import (
"github.com/ethereum/go-ethereum/accounts/abi"
)
// underlyingBindType returns the underlying Go type represented by the given type, panicking if it is not a pointer type.
// underlyingBindType returns the underlying Go type represented by the given
// type, panicking if it is not a pointer type.
func underlyingBindType(typ abi.Type) string {
goType := typ.GetType()
if goType.Kind() != reflect.Pointer {
@ -45,20 +46,23 @@ func isPointerType(typ abi.Type) bool {
return typ.GetType().Kind() == reflect.Pointer
}
// binder is used during the conversion of an ABI definition into Go bindings (as part of the execution of BindV2)
// in contrast to contractBinder, binder contains binding-generation-state that is shared between contracts:
// binder is used during the conversion of an ABI definition into Go bindings
// (as part of the execution of BindV2). In contrast to contractBinder, binder
// contains binding-generation-state that is shared between contracts:
//
// a global struct map of structs emitted by all contracts is tracked and expanded. Structs generated in the bindings
// are not prefixed with the contract name that uses them (to keep the generated bindings less verbose).
// a global struct map of structs emitted by all contracts is tracked and expanded.
// Structs generated in the bindings are not prefixed with the contract name
// that uses them (to keep the generated bindings less verbose).
//
// This contrasts to other per-contract
// state (constructor/method/event/error pack/unpack methods) which are guaranteed to be unique because of their association
// with the uniquely-named owning contract (whether prefixed in the generated symbol name, or as a member method on a
// contract struct).
// This contrasts to other per-contract state (constructor/method/event/error,
// pack/unpack methods) which are guaranteed to be unique because of their
// association with the uniquely-named owning contract (whether prefixed in the
// generated symbol name, or as a member method on a contract struct).
//
// In addition, binder contains the input alias map.
// In BindV2, a binder is instantiated to produce a set of tmplContractV2 and tmplStruct objects from the provided ABI
// definition. These are used as part of the input to rendering the binding template.
// In addition, binder contains the input alias map. In BindV2, a binder is
// instantiated to produce a set of tmplContractV2 and tmplStruct objects from
// the provided ABI definition. These are used as part of the input to rendering
// the binding template.
type binder struct {
// contracts is the map of each individual contract requested binding.
// It is keyed by the contract name provided in the ABI definition.
@ -69,8 +73,9 @@ type binder struct {
// and the solidity type signature of the struct
structs map[string]*tmplStruct
// aliases is a map for renaming instances of named events/functions/errors to specified values.
// it is keyed by source symbol name, and values are what the replacement name should be.
// aliases is a map for renaming instances of named events/functions/errors
// to specified values. it is keyed by source symbol name, and values are
// what the replacement name should be.
aliases map[string]string
}
@ -80,9 +85,10 @@ func (b *binder) BindStructType(typ abi.Type) {
bindStructType(typ, b.structs)
}
// contractBinder holds state for binding of a single contract.
// It is a type registry for compiling maps of identifiers that will be emitted in generated bindings.
// It also sanitizes/converts information contained in the ABI definition into a data-format that is amenable for rendering the templates.
// contractBinder holds state for binding of a single contract. It is a type
// registry for compiling maps of identifiers that will be emitted in generated
// bindings. It also sanitizes/converts information contained in the ABI
// definition into a data-format that is amenable for rendering the templates.
type contractBinder struct {
binder *binder
@ -108,10 +114,12 @@ func newContractBinder(binder *binder) *contractBinder {
}
}
// registerIdentifier applies alias renaming, name normalization (conversion to camel case), and registers the normalized
// name in the specified identifier map. It returns an error if the normalized name already exists in the map.
// registerIdentifier applies alias renaming, name normalization (conversion to
// camel case), and registers the normalized name in the specified identifier map.
// It returns an error if the normalized name already exists in the map.
func (cb *contractBinder) registerIdentifier(identifiers map[string]bool, original string) (normalized string, err error) {
normalized = abi.ToCamelCase(alias(cb.binder.aliases, original))
// Name shouldn't start with a digit. It will make the generated code invalid.
if len(normalized) > 0 && unicode.IsDigit(rune(normalized[0])) {
normalized = fmt.Sprintf("E%s", normalized)
@ -120,7 +128,6 @@ func (cb *contractBinder) registerIdentifier(identifiers map[string]bool, origin
return ok
})
}
if _, ok := identifiers[normalized]; ok {
return "", fmt.Errorf("duplicate symbol '%s'", normalized)
}
@ -128,19 +135,18 @@ func (cb *contractBinder) registerIdentifier(identifiers map[string]bool, origin
return normalized, nil
}
// bindMethod registers a method to be emitted in the bindings.
// The name, inputs and outputs are normalized. If any inputs are
// struct-type their structs are registered to be emitted in the bindings.
// Any methods that return more than one output have their result coalesced
// into a struct.
// bindMethod registers a method to be emitted in the bindings. The name, inputs
// and outputs are normalized. If any inputs are struct-type their structs are
// registered to be emitted in the bindings. Any methods that return more than
// one output have their result coalesced into a struct.
func (cb *contractBinder) bindMethod(original abi.Method) error {
normalized := original
normalizedName, err := cb.registerIdentifier(cb.callIdentifiers, original.Name)
if err != nil {
return err
}
normalized.Name = normalizedName
normalized.Inputs = normalizeArgs(original.Inputs)
for _, input := range normalized.Inputs {
if hasStruct(input.Type) {
@ -157,17 +163,22 @@ func (cb *contractBinder) bindMethod(original abi.Method) error {
}
}
isStructured := structured(original.Outputs)
// if the call returns multiple values, coallesce them into a struct
// If the call returns multiple values, coalesced them into a struct
if len(normalized.Outputs) > 1 {
isStructured = true
}
cb.calls[original.Name] = &tmplMethod{Original: original, Normalized: normalized, Structured: isStructured}
cb.calls[original.Name] = &tmplMethod{
Original: original,
Normalized: normalized,
Structured: isStructured,
}
return nil
}
// normalize a set of arguments by stripping underscores, giving a generic name in the case where
// the arg name collides with a reserved Go keyword, and finally converting to camel-case.
// normalize a set of arguments by stripping underscores, giving a generic name
// in the case where the arg name collides with a reserved Go keyword, and finally
// converting to camel-case.
func normalizeArgs(args abi.Arguments) abi.Arguments {
args = slices.Clone(args)
used := make(map[string]bool)
@ -246,8 +257,9 @@ func parseLibraryDeps(unlinkedCode string) (res []string) {
return res
}
// iterSorted iterates the map in the lexicographic order of the keys calling onItem on each. If the callback returns
// an error, iteration is halted and the error is returned from iterSorted.
// iterSorted iterates the map in the lexicographic order of the keys calling
// onItem on each. If the callback returns an error, iteration is halted and
// the error is returned from iterSorted.
func iterSorted[V any](inp map[string]V, onItem func(string, V) error) error {
var sortedKeys []string
for key := range inp {
@ -293,21 +305,18 @@ func BindV2(types []string, abis []string, bytecodes []string, pkg string, libs
if err != nil {
return "", err
}
err = iterSorted(evmABI.Events, func(_ string, original abi.Event) error {
return cb.bindEvent(original)
})
if err != nil {
return "", err
}
err = iterSorted(evmABI.Errors, func(_ string, original abi.Error) error {
return cb.bindError(original)
})
if err != nil {
return "", err
}
b.contracts[types[i]] = newTmplContractV2(types[i], abis[i], bytecodes[i], evmABI.Constructor, cb)
}
@ -315,7 +324,6 @@ func BindV2(types []string, abis []string, bytecodes []string, pkg string, libs
for pattern, name := range libs {
invertedLibs[name] = pattern
}
data := tmplDataV2{
Package: pkg,
Contracts: b.contracts,

View file

@ -252,6 +252,7 @@ type MetaData struct {
func (m *MetaData) GetAbi() (*abi.ABI, error) {
m.mu.Lock()
defer m.mu.Unlock()
if m.parsedABI != nil {
return m.parsedABI, nil
}

View file

@ -44,9 +44,9 @@ var (
// an empty contract behind.
ErrNoCodeAfterDeploy = errors.New("no contract code after deployment")
// Returned by WaitDeployed when the receipt for the transaction hash does not contain
// a contract address. This error may indicated that the transaction hash was not a
// CREATE transaction.
// ErrNoAddressInReceipt is returned by WaitDeployed when the receipt for the
// transaction hash does not contain a contract address. This error may indicate
// that the transaction hash was not a CREATE transaction.
ErrNoAddressInReceipt = errors.New("no contract address in receipt")
)

View file

@ -23,11 +23,10 @@ import (
"strings"
"sync"
"github.com/ethereum/go-ethereum/event"
"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/event"
)
const basefeeWiggleMultiplier = 2
@ -91,17 +90,17 @@ type MetaData struct {
ABI string // the raw ABI definition (JSON)
Deps []*MetaData // library dependencies of the contract
// For bindings that were compiled from combined-json ID is the Solidity library pattern: a 34 character prefix
// of the hex encoding of the keccak256
// For bindings that were compiled from combined-json ID is the Solidity
// library pattern: a 34 character prefix of the hex encoding of the keccak256
// hash of the fully qualified 'library name', i.e. the path of the source file.
//
// For contracts compiled from the ABI definition alone, this is the type name of the contract (as specified
// in the ABI definition or overridden via the --type flag).
// For contracts compiled from the ABI definition alone, this is the type name
// of the contract (as specified in the ABI definition or overridden via the
// --type flag).
//
// This is a unique identifier of a contract within a compilation unit. When used as part of a multi-contract
// deployment with library dependencies, the ID is used to link
// contracts during deployment using
// [LinkAndDeploy].
// This is a unique identifier of a contract within a compilation unit. When
// used as part of a multi-contract deployment with library dependencies, the
// ID is used to link contracts during deployment using [LinkAndDeploy].
ID string
mu sync.Mutex
@ -112,6 +111,7 @@ type MetaData struct {
func (m *MetaData) ParseABI() (*abi.ABI, error) {
m.mu.Lock()
defer m.mu.Unlock()
if m.parsedABI != nil {
return m.parsedABI, nil
}

View file

@ -37,9 +37,10 @@ func NewBoundContractV1(address common.Address, abi abi.ABI, caller ContractCall
}
// Call invokes the (constant) contract method with params as input values and
// sets the output to result. The result type might be a single field for simple
// returns, a slice of interfaces for anonymous returns and a struct for named
// returns.
// sets the output to result.
//
// The result type might be a single field for simple returns, a slice of interfaces
// for anonymous returns and a struct for named returns.
func (c *BoundContractV1) Call(opts *CallOpts, results *[]any, method string, params ...any) error {
if results == nil {
results = new([]any)
@ -49,23 +50,24 @@ func (c *BoundContractV1) Call(opts *CallOpts, results *[]any, method string, pa
if err != nil {
return err
}
output, err := c.call(opts, input)
if err != nil {
return err
}
if len(*results) == 0 {
res, err := c.abi.Unpack(method, output)
if err != nil {
return err
}
*results = res
return err
return nil
}
res := *results
return c.abi.UnpackIntoInterface(res[0], method, output)
}
// CallRaw executes an eth_call against the contract with the raw calldata as
// input. It returns the call's return data or an error.
// CallRaw executes an eth_call against the contract with the raw call data as
// input. It returns the call's return data or an error.
func (c *BoundContractV1) CallRaw(opts *CallOpts, input []byte) ([]byte, error) {
return c.call(opts, input)
}
@ -143,13 +145,13 @@ func (c *BoundContractV1) Transact(opts *TransactOpts, method string, params ...
return c.transact(opts, &c.address, input)
}
// RawTransact initiates a transaction with the given raw calldata as the input.
// It's usually used to initiate transactions for invoking **Fallback** function.
// RawTransact initiates a transaction with the given raw call data as the input.
func (c *BoundContractV1) RawTransact(opts *TransactOpts, calldata []byte) (*types.Transaction, error) {
return c.transact(opts, &c.address, calldata)
}
// RawCreationTransact initiates a contract-creation transaction with the given raw calldata as the input.
// RawCreationTransact initiates a contract-creation transaction with the given
// raw call data as the input.
func (c *BoundContractV1) RawCreationTransact(opts *TransactOpts, calldata []byte) (*types.Transaction, error) {
return c.transact(opts, nil, calldata)
}
@ -282,9 +284,8 @@ func (c *BoundContractV1) estimateGasLimit(opts *TransactOpts, contract *common.
func (c *BoundContractV1) getNonce(opts *TransactOpts) (uint64, error) {
if opts.Nonce == nil {
return c.transactor.PendingNonceAt(ensureContext(opts.Context), opts.From)
} else {
return opts.Nonce.Uint64(), nil
}
return opts.Nonce.Uint64(), nil
}
// transact executes an actual transaction invocation, first deriving any missing

View file

@ -1,3 +1,19 @@
// Copyright 2025 The go-ethereum Authors
// This file is part of the go-ethereum library.
//
// The go-ethereum library is free software: you can redistribute it and/or modify
// it under the terms of the GNU Lesser General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// The go-ethereum library is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU Lesser General Public License for more details.
//
// You should have received a copy of the GNU Lesser General Public License
// along with the go-ethereum library. If not, see <http://www.gnu.org/licenses/>.
package bind
import (
@ -10,9 +26,9 @@ import (
"github.com/ethereum/go-ethereum/core/types"
)
// DeploymentParams represents parameters needed to deploy a
// set of contracts. It takes an optional override
// list to specify contracts/libraries that have already been deployed on-chain.
// DeploymentParams represents parameters needed to deploy a set of contracts.
// It takes an optional override list to specify contracts/libraries that have
// already been deployed on-chain.
type DeploymentParams struct {
Contracts []*MetaData
@ -25,7 +41,8 @@ type DeploymentParams struct {
Overrides map[string]common.Address
}
// validate determines whether the contracts specified in the DeploymentParams instance have provided deployer bytecode.
// validate determines whether the contracts specified in the DeploymentParams
// instance have provided deployer bytecode.
func (d *DeploymentParams) validate() error {
for _, meta := range d.Contracts {
if meta.Bin == "" {
@ -39,19 +56,20 @@ func (d *DeploymentParams) validate() error {
// of a set of contracts: the pending deployment transactions, and the addresses
// where the contracts will be deployed at.
type DeploymentResult struct {
// map of contract MetaData Id to deploy transaction
// Map of contract MetaData Id to deploy transaction
Txs map[string]*types.Transaction
// map of contract MetaData Id to deployed contract address
Addrs map[string]common.Address
// Map of contract MetaData Id to deployed contract address
Addresses map[string]common.Address
}
// DeployFn deploys a contract given a deployer and optional input. It returns
// 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
// order. A depTreeDeployer cannot be used after calling LinkAndDeploy other than to retrieve the deployment result.
// depTreeDeployer is responsible for taking a dependency, deploying-and-linking
// its components in the proper order. A depTreeDeployer cannot be used after
// calling LinkAndDeploy other than to retrieve the deployment result.
type depTreeDeployer struct {
deployedAddrs map[string]common.Address
deployerTxs map[string]*types.Transaction
@ -68,7 +86,6 @@ func newDepTreeDeployer(deployParams *DeploymentParams, deployFn DeployFn) *depT
if inputs == nil {
inputs = make(map[string][]byte)
}
return &depTreeDeployer{
deployFn: deployFn,
deployedAddrs: deployedAddrs,
@ -77,21 +94,23 @@ func newDepTreeDeployer(deployParams *DeploymentParams, deployFn DeployFn) *depT
}
}
// linkAndDeploy recursively deploys a contract and its dependencies: starting by linking/deploying its dependencies.
// The deployment result (deploy addresses/txs or an error) is stored in the depTreeDeployer object.
// linkAndDeploy recursively deploys a contract and its dependencies: starting by
// linking/deploying its dependencies. The deployment result (deploy addresses/txs
// or an error) is stored in the depTreeDeployer object.
func (d *depTreeDeployer) linkAndDeploy(metadata *MetaData) (common.Address, error) {
// Don't deploy already deployed contracts
if addr, ok := d.deployedAddrs[metadata.ID]; ok {
return addr, nil
}
// if this contract/library depends on other libraries deploy them (and their dependencies) first
// If this contract/library depends on other libraries deploy them
// (and their dependencies) first
deployerCode := metadata.Bin
for _, dep := range metadata.Deps {
addr, err := d.linkAndDeploy(dep)
if err != nil {
return common.Address{}, err
}
// link their deployed addresses into the bytecode to produce
// Link their deployed addresses into the bytecode to produce
deployerCode = strings.ReplaceAll(deployerCode, "__$"+dep.ID+"$__", strings.ToLower(addr.String()[2:]))
}
// Finally, deploy the contract.
@ -110,29 +129,30 @@ func (d *depTreeDeployer) linkAndDeploy(metadata *MetaData) (common.Address, err
// result returns a result for this deployment, or an error if it failed.
func (d *depTreeDeployer) result() *DeploymentResult {
// remove the override addresses from the resulting deployedAddrs
// remove the override addresses from the resulting deployed addresses
for pattern := range d.deployedAddrs {
if _, ok := d.deployerTxs[pattern]; !ok {
delete(d.deployedAddrs, pattern)
}
}
return &DeploymentResult{
Txs: d.deployerTxs,
Addrs: d.deployedAddrs,
Txs: d.deployerTxs,
Addresses: d.deployedAddrs,
}
}
// LinkAndDeploy deploys a specified set of contracts and their dependent
// libraries. If an error occurs, only contracts which were successfully
// libraries. If an error occurs, only contracts which were successfully
// deployed are returned in the result.
//
// In the case where multiple contracts share a common dependency: the shared dependency will only be deployed once.
func LinkAndDeploy(deployParams *DeploymentParams, deploy DeployFn) (res *DeploymentResult, err error) {
if err := deployParams.validate(); err != nil {
// In the case where multiple contracts share a common dependency: the shared
// dependency will only be deployed once.
func LinkAndDeploy(params *DeploymentParams, deploy DeployFn) (*DeploymentResult, error) {
if err := params.validate(); err != nil {
return nil, err
}
deployer := newDepTreeDeployer(deployParams, deploy)
for _, contract := range deployParams.Contracts {
deployer := newDepTreeDeployer(params, deploy)
for _, contract := range params.Contracts {
if _, err := deployer.linkAndDeploy(contract); err != nil {
return deployer.result(), err
}

View file

@ -19,7 +19,6 @@ package bind
import (
"fmt"
"regexp"
"sync"
"testing"
"github.com/ethereum/go-ethereum/common"
@ -39,19 +38,18 @@ type linkTestCase struct {
func copyMetaData(m *MetaData) *MetaData {
m.mu.Lock()
defer m.mu.Unlock()
var deps []*MetaData
if len(m.Deps) > 0 {
for _, dep := range m.Deps {
deps = append(deps, copyMetaData(dep))
}
}
return &MetaData{
Bin: m.Bin,
ABI: m.ABI,
Deps: deps,
ID: m.ID,
mu: sync.Mutex{},
parsedABI: m.parsedABI,
}
}
@ -137,10 +135,11 @@ func linkDeps(deps map[string]*MetaData) []*MetaData {
return rootMetadatas
}
// internalLinkDeps is the internal recursing logic of linkDeps: It links the contract referred to by MetaData
// given the depMap (map of solidity link pattern to contract metadata object), deleting contract entries from the roots
// map if they were referenced as dependencies. It returns a new MetaData object which is the linked version of metadata
// parameter.
// internalLinkDeps is the internal recursing logic of linkDeps:
// It links the contract referred to by MetaData given the depMap (map of solidity
// link pattern to contract metadata object), deleting contract entries from the
// roots map if they were referenced as dependencies. It returns a new MetaData
// object which is the linked version of metadata parameter.
func internalLinkDeps(metadata *MetaData, depMap map[string]*MetaData, roots *map[string]struct{}) *MetaData {
linked := copyMetaData(metadata)
depPatterns := parseLibraryDeps(metadata.Bin)
@ -153,13 +152,13 @@ func internalLinkDeps(metadata *MetaData, depMap map[string]*MetaData, roots *ma
}
func testLinkCase(tcInput linkTestCaseInput) error {
testAddr := crypto.PubkeyToAddress(testKey.PublicKey)
var testAddrNonce uint64
overridesAddrs := make(map[common.Address]struct{})
var (
testAddr = crypto.PubkeyToAddress(testKey.PublicKey)
overridesAddrs = make(map[common.Address]struct{})
overrideAddrs = make(map[rune]common.Address)
)
// generate deterministic addresses for the override set.
rand.Seed(42)
overrideAddrs := make(map[rune]common.Address)
for contract := range tcInput.overrides {
var addr common.Address
rand.Read(addr[:])
@ -177,6 +176,7 @@ func testLinkCase(tcInput linkTestCaseInput) error {
}
}
var testAddrNonce uint64
mockDeploy := func(input []byte, deployer []byte) (common.Address, *types.Transaction, error) {
contractAddr := crypto.CreateAddress(testAddr, testAddrNonce)
testAddrNonce++
@ -225,11 +225,11 @@ func testLinkCase(tcInput linkTestCaseInput) error {
}
if len(res.Txs) != len(tcInput.expectDeployed) {
return fmt.Errorf("got %d deployed contracts. expected %d.\n", len(res.Addrs), len(tcInput.expectDeployed))
return fmt.Errorf("got %d deployed contracts. expected %d.\n", len(res.Addresses), len(tcInput.expectDeployed))
}
for contract := range tcInput.expectDeployed {
pattern := crypto.Keccak256Hash([]byte(string(contract))).String()[2:36]
if _, ok := res.Addrs[pattern]; !ok {
if _, ok := res.Addresses[pattern]; !ok {
return fmt.Errorf("expected contract %s was not deployed\n", string(contract))
}
}

View file

@ -80,7 +80,8 @@ func WatchEvents[Ev ContractEvent](c BoundContract, opts *WatchOpts, unpack func
}), nil
}
// EventIterator is returned from FilterLogs and is used to iterate over the raw logs and unpacked data for events.
// 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
@ -182,7 +183,7 @@ func Transact(c BoundContract, opt *TransactOpts, packedInput []byte) (*types.Tr
}
// DeployContract deploys a contract onto the Ethereum blockchain and binds the
// deployment address with a Go wrapper. It expects its parameters to be abi-encoded
// deployment address with a Go wrapper. It expects its parameters to be abi-encoded
// bytes.
func DeployContract(opts *TransactOpts, bytecode []byte, backend ContractBackend, packedParams []byte) (common.Address, *types.Transaction, error) {
c := NewBoundContractV1(common.Address{}, abi.ABI{}, backend, backend, backend)
@ -190,11 +191,11 @@ func DeployContract(opts *TransactOpts, bytecode []byte, backend ContractBackend
if err != nil {
return common.Address{}, nil, err
}
address := crypto.CreateAddress(opts.From, tx.Nonce())
return address, tx, nil
return crypto.CreateAddress(opts.From, tx.Nonce()), tx, nil
}
// DefaultDeployer returns a DeployFn that signs and submits creation transactions using the given signer.
// DefaultDeployer returns a DeployFn that signs and submits creation transactions
// using the given signer.
func DefaultDeployer(ctx context.Context, from common.Address, backend ContractBackend, signer SignerFn) DeployFn {
opts := &TransactOpts{
From: from,

View file

@ -18,13 +18,10 @@ package bind_test
import (
"context"
"encoding/json"
"io"
"math/big"
"testing"
"time"
"github.com/ethereum/go-ethereum/accounts/abi"
"github.com/ethereum/go-ethereum/accounts/abi/bind/backends"
"github.com/ethereum/go-ethereum/accounts/abi/bind/v2"
"github.com/ethereum/go-ethereum/accounts/abi/bind/v2/internal/contracts/events"
@ -43,17 +40,6 @@ import (
var testKey, _ = crypto.HexToECDSA("b71c71a67e1177ad4e901695e1b4b9ee17ae16c6668d313eac2f96dbcda3f291")
var testAddr = crypto.PubkeyToAddress(testKey.PublicKey)
// 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 testSetup() (*backends.SimulatedBackend, error) {
backend := simulated.NewBackend(
types.GenesisAlloc{
@ -65,7 +51,7 @@ func testSetup() (*backends.SimulatedBackend, error) {
)
// we should just be able to use the backend directly, instead of using
// this deprecated interface. However, the simulated backend no longer
// this deprecated interface. However, the simulated backend no longer
// implements backends.SimulatedBackend...
bindBackend := backends.SimulatedBackend{
Backend: backend,
@ -87,7 +73,6 @@ func makeTestDeployer(backend bind.ContractBackend) func(input, deployer []byte)
}
return signedTx, nil
}
return bind.DefaultDeployer(context.Background(), testAddr, backend, sign)
}
@ -112,8 +97,8 @@ func TestDeploymentLibraries(t *testing.T) {
}
bindBackend.Commit()
if len(res.Addrs) != 5 {
t.Fatalf("deployment should have generated 5 addresses. got %d", len(res.Addrs))
if len(res.Addresses) != 5 {
t.Fatalf("deployment should have generated 5 addresses. got %d", len(res.Addresses))
}
for _, tx := range res.Txs {
_, err = bind.WaitDeployed(context.Background(), bindBackend, tx.Hash())
@ -123,7 +108,7 @@ func TestDeploymentLibraries(t *testing.T) {
}
doInput := c.PackDo(big.NewInt(1))
contractAddr := res.Addrs[nested_libraries.C1MetaData.ID]
contractAddr := res.Addresses[nested_libraries.C1MetaData.ID]
callOpts := &bind.CallOpts{From: common.Address{}, Context: context.Background()}
instance := c.Instance(bindBackend, contractAddr)
internalCallCount, err := bind.Call(instance, callOpts, doInput, c.UnpackDo)
@ -154,8 +139,8 @@ func TestDeploymentWithOverrides(t *testing.T) {
}
bindBackend.Commit()
if len(res.Addrs) != 4 {
t.Fatalf("deployment should have generated 4 addresses. got %d", len(res.Addrs))
if len(res.Addresses) != 4 {
t.Fatalf("deployment should have generated 4 addresses. got %d", len(res.Addresses))
}
for _, tx := range res.Txs {
_, err = bind.WaitDeployed(context.Background(), bindBackend, tx.Hash())
@ -166,7 +151,7 @@ func TestDeploymentWithOverrides(t *testing.T) {
c := nested_libraries.NewC1()
constructorInput := c.PackConstructor(big.NewInt(42), big.NewInt(1))
overrides := res.Addrs
overrides := res.Addresses
// deploy the contract
deploymentParams = &bind.DeploymentParams{
@ -180,8 +165,8 @@ func TestDeploymentWithOverrides(t *testing.T) {
}
bindBackend.Commit()
if len(res.Addrs) != 1 {
t.Fatalf("deployment should have generated 1 address. got %d", len(res.Addrs))
if len(res.Addresses) != 1 {
t.Fatalf("deployment should have generated 1 address. got %d", len(res.Addresses))
}
for _, tx := range res.Txs {
_, err = bind.WaitDeployed(context.Background(), bindBackend, tx.Hash())
@ -192,7 +177,7 @@ func TestDeploymentWithOverrides(t *testing.T) {
// call the deployed contract and make sure it returns the correct result
doInput := c.PackDo(big.NewInt(1))
instance := c.Instance(bindBackend, res.Addrs[nested_libraries.C1MetaData.ID])
instance := c.Instance(bindBackend, res.Addresses[nested_libraries.C1MetaData.ID])
callOpts := new(bind.CallOpts)
internalCallCount, err := bind.Call(instance, callOpts, doInput, c.UnpackDo)
if err != nil {
@ -231,8 +216,9 @@ func TestEvents(t *testing.T) {
if err != nil {
t.Fatalf("error setting up testing env: %v", err)
}
deploymentParams := &bind.DeploymentParams{Contracts: []*bind.MetaData{&events.CMetaData}}
deploymentParams := &bind.DeploymentParams{
Contracts: []*bind.MetaData{&events.CMetaData},
}
res, err := bind.LinkAndDeploy(deploymentParams, makeTestDeployer(backend))
if err != nil {
t.Fatalf("error deploying contract for testing: %v", err)
@ -244,7 +230,7 @@ func TestEvents(t *testing.T) {
}
c := events.NewC()
instance := c.Instance(backend, res.Addrs[events.CMetaData.ID])
instance := c.Instance(backend, res.Addresses[events.CMetaData.ID])
newCBasic1Ch := make(chan *events.CBasic1)
newCBasic2Ch := make(chan *events.CBasic2)
@ -330,8 +316,9 @@ func TestErrors(t *testing.T) {
if err != nil {
t.Fatalf("error setting up testing env: %v", err)
}
deploymentParams := &bind.DeploymentParams{Contracts: []*bind.MetaData{&solc_errors.CMetaData}}
deploymentParams := &bind.DeploymentParams{
Contracts: []*bind.MetaData{&solc_errors.CMetaData},
}
res, err := bind.LinkAndDeploy(deploymentParams, makeTestDeployer(backend))
if err != nil {
t.Fatalf("error deploying contract for testing: %v", err)
@ -343,9 +330,9 @@ func TestErrors(t *testing.T) {
}
c := solc_errors.NewC()
instance := c.Instance(backend, res.Addrs[solc_errors.CMetaData.ID])
instance := c.Instance(backend, res.Addresses[solc_errors.CMetaData.ID])
packedInput := c.PackFoo()
opts := &bind.CallOpts{From: res.Addrs[solc_errors.CMetaData.ID]}
opts := &bind.CallOpts{From: res.Addresses[solc_errors.CMetaData.ID]}
_, err = bind.Call[struct{}](instance, opts, packedInput, nil)
if err == nil {
t.Fatalf("expected call to fail")