mirror of
https://github.com/ethereum/go-ethereum.git
synced 2026-07-27 23:26:44 +00:00
accounts/abi/abigen, account/abi/bind/v2: polish code
This commit is contained in:
parent
6680b836f6
commit
8a4a5a60fb
10 changed files with 162 additions and 141 deletions
|
|
@ -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:]
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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,
|
||||
|
|
|
|||
|
|
@ -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
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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")
|
||||
)
|
||||
|
||||
|
|
|
|||
|
|
@ -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
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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))
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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,
|
||||
|
|
|
|||
|
|
@ -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")
|
||||
|
|
|
|||
Loading…
Reference in a new issue