Skip to content

abigen generated bindings can be optimized by parsing abi once during an init function #22269

@bonedaddy

Description

@bonedaddy

Rationale

In heavy usage environments which frequently create temporary bindings for usage during a small period of time, a lot of time is spent running abi.JSON. For example I have a backend microservice that listens for new blocks to be mined, and when new blocks are mined instantiates a variety of different contract bindings.

While profiling this program, 7.2% of the recorded profile was spend running abi.JSON. Because the abi package uses reflection, this can lead to a lot of time parsing ABI definitions. Because thes ABI definitions are declared as const variables, a better solution would be to have an init function that does the ABI parsing once when the corresponding package is initialized.

There are some optimizations a user can implement in their programs, such as attempting to make instantiations of the contract bindings longer lived, however this is not always possible.

Implementation

For the sake of arguments lets consider generating bindings for an ERC20 contract. Each time a binding is instantiated the bindERC20 function is called:

// bindErc20 binds a generic wrapper to an already deployed contract.
func bindErc20(address common.Address, caller bind.ContractCaller, transactor bind.ContractTransactor, filterer bind.ContractFilterer) (*bind.BoundContract, error) {
	parsed, err := abi.JSON(strings.NewReader(Erc20ABI)) // <---- lots of reflection use <----
	if err != nil {
		return nil, err
	}
	return bind.NewBoundContract(address, parsed, caller, transactor, filterer), nil
}

This logic could be reworked such that abi.JSON is moved to an init function, and the program structure could be rewritten to:

var (
    parsedABI abi.ABI
)

func init() {
    var err error
    parsedABI, err = abi.JSON(strings.NewReader(Erc20ABI))
    if err != nil {
        panic(err) // maybe not use panic?
    }
}

// bindErc20 binds a generic wrapper to an already deployed contract.
func bindErc20(address common.Address, caller bind.ContractCaller, transactor bind.ContractTransactor, filterer bind.ContractFilterer) (*bind.BoundContract, error) {
	return bind.NewBoundContract(address, parsedABI, caller, transactor, filterer), nil
}

I haven't done a lot of work with Golang's templating engine, but I would be willing to take a stab at implementing this

Metadata

Metadata

Assignees

No one assigned

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions