accounts/abi: attempt to improve documentation throughout the PR. Make the method docs in generated bindings consistent.

This commit is contained in:
Jared Wasinger 2025-02-10 17:26:47 -08:00
parent 6868cd49d9
commit c88e3874bb
5 changed files with 69 additions and 46 deletions

View file

@ -31,8 +31,9 @@ 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 a string representation of the Go type
// that corresponds to the given ABI type, panicking if it is not a
// pointer.
func underlyingBindType(typ abi.Type) string {
goType := typ.GetType()
if goType.Kind() != reflect.Pointer {
@ -41,11 +42,12 @@ func underlyingBindType(typ abi.Type) string {
return goType.Elem().String()
}
// isPointerType returns true if the
// isPointerType returns true if the underlying type is a pointer.
func isPointerType(typ abi.Type) bool {
return typ.GetType().Kind() == reflect.Pointer
}
// OLD:
// 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:
@ -63,6 +65,18 @@ func isPointerType(typ abi.Type) bool {
// 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.
// NEW:
// binder is used to translate an ABI definition into a set of data-structures
// that will be used to render the template and produce Go bindings. This can
// be thought of as the "backend" that sanitizes the ABI definition to a format
// that can be directly rendered with minimal complexity in the template.
//
// The input data to the template rendering consists of:
// - the set of all contracts requested for binding, each containing
// methods/events/errors to emit pack/unpack methods for.
// - the set of structures defined by the contracts, and created
// as part of the binding process.
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.
@ -79,7 +93,7 @@ type binder struct {
aliases map[string]string
}
// BindStructType register the type to be emitted as a struct in the
// BindStructType registers the type to be emitted as a struct in the
// bindings.
func (b *binder) BindStructType(typ abi.Type) {
bindStructType(typ, b.structs)
@ -87,8 +101,7 @@ func (b *binder) BindStructType(typ abi.Type) {
// 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.
// bindings.
type contractBinder struct {
binder *binder
@ -114,8 +127,8 @@ 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.
// registerIdentifier applies alias renaming, name normalization (conversion
// from snake 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))
@ -138,7 +151,7 @@ func (cb *contractBinder) registerIdentifier(identifiers map[string]bool, origin
// 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.
// one output have their results gathered into a struct.
func (cb *contractBinder) bindMethod(original abi.Method) error {
normalized := original
normalizedName, err := cb.registerIdentifier(cb.callIdentifiers, original.Name)
@ -164,7 +177,7 @@ func (cb *contractBinder) bindMethod(original abi.Method) error {
}
isStructured := structured(original.Outputs)
// If the call returns multiple values, coalesced them into a struct
// If the call returns multiple values, gather them into a struct
if len(normalized.Outputs) > 1 {
isStructured = true
}
@ -202,8 +215,8 @@ func normalizeArgs(args abi.Arguments) abi.Arguments {
return args
}
// normalizeErrorOrEventFields normalizes errors/events for emitting through bindings:
// Any anonymous fields are given generated names.
// normalizeErrorOrEventFields normalizes errors/events for emitting through
// bindings: Any anonymous fields are given generated names.
func (cb *contractBinder) normalizeErrorOrEventFields(originalInputs abi.Arguments) abi.Arguments {
normalizedArguments := normalizeArgs(originalInputs)
for _, input := range normalizedArguments {
@ -246,6 +259,8 @@ func (cb *contractBinder) bindError(original abi.Error) error {
return nil
}
// parseLibraryDeps extracts references to library dependencies from the unlinked
// hex string deployment bytecode.
func parseLibraryDeps(unlinkedCode string) (res []string) {
reMatchSpecificPattern, err := regexp.Compile(`__\$([a-f0-9]+)\$__`)
if err != nil {

View file

@ -89,8 +89,8 @@ var (
{{ end }}
{{range .Calls}}
// {{.Normalized.Name}} is the Go binding used to pack the parameters required for calling
// the contract method 0x{{printf "%x" .Original.ID}}.
// Pack{{.Normalized.Name}} is the Go binding used to pack the parameters required for calling
// the contract method with ID 0x{{printf "%x" .Original.ID}}.
//
// Solidity: {{.Original.String}}
func ({{ decapitalise $contract.Type}} *{{$contract.Type}}) Pack{{.Normalized.Name}}({{range .Normalized.Inputs}} {{.Name}} {{bindtype .Type $structs}}, {{end}}) []byte {
@ -152,7 +152,7 @@ var (
{{end}}
{{range .Events}}
// {{$contract.Type}}{{.Normalized.Name}} represents a {{.Normalized.Name}} event raised by the {{$contract.Type}} contract.
// {{$contract.Type}}{{.Normalized.Name}} represents a {{.Original.Name}} event raised by the {{$contract.Type}} contract.
type {{$contract.Type}}{{.Normalized.Name}} struct {
{{- range .Normalized.Inputs}}
{{ capitalise .Name}}
@ -211,7 +211,7 @@ var (
{{ end }}
{{range .Errors}}
// {{$contract.Type}}{{.Normalized.Name}} represents a {{.Normalized.Name}} error raised by the {{$contract.Type}} contract.
// {{$contract.Type}}{{.Normalized.Name}} represents a {{.Original.Name}} error raised by the {{$contract.Type}} contract.
type {{$contract.Type}}{{.Normalized.Name}} struct { {{range .Normalized.Inputs}}
{{capitalise .Name}} {{if .Indexed}}{{bindtopictype .Type $structs}}{{else}}{{bindtype .Type $structs}}{{end}}; {{end}}
}

View file

@ -80,8 +80,8 @@ func newTmplContractV2(typ string, abiStr string, bytecode string, constructor a
}
type tmplDataV2 struct {
Package string // Name of the package to place the generated file in
Contracts map[string]*tmplContractV2 // List of contracts to generate into this file
Package string // Name of the package to use for the generated bindings
Contracts map[string]*tmplContractV2 // Contracts that will be emitted in the bindings (keyed by contract name)
Libraries map[string]string // Map of the contract's name to link pattern
Structs map[string]*tmplStruct // Contract struct type definitions
}

View file

@ -89,7 +89,7 @@ type WatchOpts struct {
// MetaData collects all metadata for a bound contract.
type MetaData struct {
Bin string // runtime bytecode (as a hex string)
Bin string // deployer bytecode (as a hex string)
ABI string // the raw ABI definition (JSON)
Deps []*MetaData // library dependencies of the contract
@ -110,7 +110,8 @@ type MetaData struct {
parsedABI *abi.ABI
}
// ParseABI returns the parsed ABI specification.
// ParseABI returns the parsed ABI specification, or an error if the string
// representation of the ABI set in the MetaData instance could not be parsed.
func (m *MetaData) ParseABI() (*abi.ABI, error) {
m.mu.Lock()
defer m.mu.Unlock()
@ -262,7 +263,8 @@ func (c *BoundContract) RawTransact(opts *TransactOpts, calldata []byte) (*types
return c.transact(opts, &c.address, calldata)
}
// RawTransact initiates a contract-creation transaction with the given raw calldata as the input.
// RawCreationTransact creates and submits a contract-creation transaction with
// the given calldata as the input.
func (c *BoundContract) RawCreationTransact(opts *TransactOpts, calldata []byte) (*types.Transaction, error) {
return c.transact(opts, nil, calldata)
}

View file

@ -26,23 +26,21 @@ 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 contains parameters needed to deploy one or more contracts via LinkAndDeploy
type DeploymentParams struct {
// list of all contracts targeted for the deployment
Contracts []*MetaData
// Map of solidity library pattern to constructor input.
// optional map of ABI-encoded constructor inputs keyed by the MetaData.ID.
Inputs map[string][]byte
// Overrides is an optional map of pattern to deployment address.
// Contracts/libraries that refer to dependencies in the override
// set are linked to the provided address (an already-deployed contract).
// optional map of override addresses for specifying already-deployed
// contracts. It is keyed by the MetaData.ID.
Overrides map[string]common.Address
}
// validate determines whether the contracts specified in the DeploymentParams
// instance have provided deployer bytecode.
// instance have embedded deployer code in their provided MetaData instances.
func (d *DeploymentParams) validate() error {
for _, meta := range d.Contracts {
if meta.Bin == "" {
@ -52,14 +50,13 @@ func (d *DeploymentParams) validate() error {
return nil
}
// DeploymentResult encapsulates information about the result of the deployment
// of a set of contracts: the pending deployment transactions, and the addresses
// where the contracts will be deployed at.
// DeploymentResult contains information about the result of a pending
// deployment made by LinkAndDeploy.
type DeploymentResult struct {
// Map of contract MetaData Id to deploy transaction
// Map of contract MetaData.ID to pending deployment transaction
Txs map[string]*types.Transaction
// Map of contract MetaData Id to deployed contract address
// Map of contract MetaData.ID to the address where it will be deployed
Addresses map[string]common.Address
}
@ -94,11 +91,12 @@ 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 deploys a contract and it's dependencies. Because libraries
// can in-turn have their own library dependencies, linkAndDeploy performs
// deployment recursively (deepest-dependency first). The address of the
// pending contract deployment for the top-level contract is returned.
func (d *depTreeDeployer) linkAndDeploy(metadata *MetaData) (common.Address, error) {
// Don't deploy already deployed contracts
// Don't re-deploy aliased or previously-deployed contracts
if addr, ok := d.deployedAddrs[metadata.ID]; ok {
return addr, nil
}
@ -113,7 +111,7 @@ func (d *depTreeDeployer) linkAndDeploy(metadata *MetaData) (common.Address, err
// Link their deployed addresses into the bytecode to produce
deployerCode = strings.ReplaceAll(deployerCode, "__$"+dep.ID+"$__", strings.ToLower(addr.String()[2:]))
}
// Finally, deploy the contract.
// Finally, deploy the top-level contract.
code, err := hex.DecodeString(deployerCode[2:])
if err != nil {
panic(fmt.Sprintf("error decoding contract deployer hex %s:\n%v", deployerCode[2:], err))
@ -127,9 +125,10 @@ func (d *depTreeDeployer) linkAndDeploy(metadata *MetaData) (common.Address, err
return addr, nil
}
// result returns a result for this deployment, or an error if it failed.
// result returns a DeploymentResult instance referencing contracts deployed
// and not including any overrides specified for this deployment.
func (d *depTreeDeployer) result() *DeploymentResult {
// remove the override addresses from the resulting deployed addresses
// filter the override addresses from the deployed address set.
for pattern := range d.deployedAddrs {
if _, ok := d.deployerTxs[pattern]; !ok {
delete(d.deployedAddrs, pattern)
@ -141,12 +140,19 @@ func (d *depTreeDeployer) result() *DeploymentResult {
}
}
// LinkAndDeploy deploys a specified set of contracts and their dependent
// libraries. If an error occurs, only contracts which were successfully
// deployed are returned in the result.
// LinkAndDeploy performs the contract deployment specified by params using the
// provided DeployFn to create, sign and submit transactions.
//
// In the case where multiple contracts share a common dependency: the shared
// dependency will only be deployed once.
// Contracts can depend on libraries, which in-turn can have their own library
// dependencies. Therefore, LinkAndDeploy performs the deployment recursively,
// starting with libraries (and contracts) that don't have dependencies, and
// progressing through the contracts that depend upon them.
//
// If an error is encountered, the returned DeploymentResult only contains
// entries for the contracts whose deployment submission succeeded.
//
// LinkAndDeploy performs creation and submission of creation transactions,
// but does not ensure that the contracts are included in the chain.
func LinkAndDeploy(params *DeploymentParams, deploy DeployFn) (*DeploymentResult, error) {
if err := params.validate(); err != nil {
return nil, err