-
Notifications
You must be signed in to change notification settings - Fork 21.3k
Description
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