Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

accounts/abi/bind: parse ABI once #22583

Merged
merged 1 commit into from Jul 27, 2021

Conversation

MariusVanDerWijden
Copy link
Member

@MariusVanDerWijden MariusVanDerWijden commented Mar 26, 2021

This moves the initialization of the parsedABI object from the Deploy()
function into a var that gets executed on startup. This means deploying
the same contract multiple times becomes faster, as the ABI doesn't have
to be parsed every time. It does increase the memory usage by requiring
everyone to hold the ABI object in memory even if the contract is not
going to be deployed.

closes #22269

example: (updated)

// ReverterMetaData contains all meta data concerning the Reverter contract.
var ReverterMetaData = &bind.MetaData{
	ABI: "[{\"constant\":false,\"inputs\":[],\"name\":\"revert\",\"outputs\":[],\"payable\":false,\"stateMutability\":\"nonpayable\",\"type\":\"function\"}]",
	Sigs: map[string]string{
		"7da3c3ab": "revert()",
	},
	Bin: "0x6080604052348015600f57600080fd5b5060ab8061001e6000396000f3fe6080604052348015600f57600080fd5b506004361060285760003560e01c80637da3c3ab14602d575b600080fd5b60336035565b005b6040805162461bcd60e51b815260206004820152601160248201527072657665727420726561736f6e2031323360781b604482015290519081900360640190fdfea265627a7a72315820e8c3f899c32798b10b8d46594292b9e3d2271f011100fb17b6d3a6469f50c8c964736f6c63430005100032",
}

// ReverterABI is the input ABI used to generate the binding from.
// Deprecated: Use ReverterMetaData.ABI instead.
var ReverterABI = ReverterMetaData.ABI

// Deprecated: Use ReverterMetaData.Sigs instead.
// ReverterFuncSigs maps the 4-byte function signature to its string representation.
var ReverterFuncSigs = ReverterMetaData.Sigs

// ReverterBin is the compiled bytecode used for deploying new contracts.
// Deprecated: Use ReverterMetaData.Bin instead.
var ReverterBin = ReverterMetaData.Bin

// DeployReverter deploys a new Ethereum contract, binding an instance of Reverter to it.
func DeployReverter(auth *bind.TransactOpts, backend bind.ContractBackend) (common.Address, *types.Transaction, *Reverter, error) {
	parsed, err := ReverterMetaData.GetAbi()
	if err != nil {
		return common.Address{}, nil, nil, err
	}
	if parsed == nil {
		return common.Address{}, nil, nil, errors.New("GetABI returned nil")
	}

	address, tx, contract, err := bind.DeployContract(auth, *parsed, common.FromHex(ReverterBin), backend)
	if err != nil {
		return common.Address{}, nil, nil, err
	}
	return address, tx, &Reverter{ReverterCaller: ReverterCaller{contract: contract}, ReverterTransactor: ReverterTransactor{contract: contract}, ReverterFilterer: ReverterFilterer{contract: contract}}, nil
}

@holiman
Copy link
Contributor

holiman commented Mar 30, 2021

What is the full example? I'm wondering about the scope, really, because parsingErr -- is that in the global scope? If so, it will conflict with other contracts.

@MariusVanDerWijden
Copy link
Member Author

Oh you're right, that will clash with other definitions

@holiman
Copy link
Contributor

holiman commented Mar 31, 2021

How about something like this? That makes it still load dynamically, but does so thread-safe and does not clutter the global scope with a lot of random things. The parsedAbi definition would not be dynamically generated, but only defined once.

package foo

type parsedAbi struct {
	mu   sync.Mutex
	sigs map[string]string
	bin  []byte
	ab   *abi.ABI
}

func (p *parsedAbi) getAbi(source string) (*abi.ABI, error) {
	p.mu.Lock()
	defer p.mu.Unlock()
	if p.ab != nil {
		return p.ab
	}
	if parsed, err := abi.JSON(strings.NewReader(source)); err != nil {
		return nil, err
	} else {
		p.ab = parsed
	}
}

var reverter = &parsedAbi{
	sigs: map[string]string{"7da3c3ab": "revert()"},
	bin:  common.FromHex("0x6080604052348015600f57600080fd5b5060ab8061001e6000396000f3fe6080604052348015600f57600080fd5b506004361060285760003560e01c80637da3c3ab14602d575b600080fd5b60336035565b005b6040805162461bcd60e51b815260206004820152601160248201527072657665727420726561736f6e2031323360781b604482015290519081900360640190fdfea265627a7a72315820e8c3f899c32798b10b8d46594292b9e3d2271f011100fb17b6d3a6469f50c8c964736f6c63430005100032"),
}

// DeployReverter deploys a new Ethereum contract, binding an instance of Reverter to it.
func DeployReverter(auth *bind.TransactOpts, backend bind.ContractBackend) (common.Address, *types.Transaction, *Reverter, error) {
	rAbi, err := reverter.getAbi(ReverterABI)
	if err != nil {
		return common.Address{}, nil, nil, err
	}
	address, tx, contract, err := bind.DeployContract(auth, rAbi, rAbi.bin, backend)
	if err != nil {
		return common.Address{}, nil, nil, err
	}
	return address, tx, &Reverter{ReverterCaller: ReverterCaller{contract: contract}, ReverterTransactor: ReverterTransactor{contract: contract}, ReverterFilterer: ReverterFilterer{contract: contract}}, nil
}

@MariusVanDerWijden
Copy link
Member Author

@holiman I like it, however that would break the api as ReverterBin and ReverterFuncSigs are not available anymore and anyone depending on them would need to update their code.
I do like it though, maybe with a slight modification.

package foo

type ParsedAbi struct {
	mu   sync.Mutex
	Sigs map[string]string
	Bin  []byte
	ABI string
	ab   *abi.ABI
}

func (p *ParsedAbi) getAbi() (*abi.ABI, error) {
	p.mu.Lock()
	defer p.mu.Unlock()
	if p.ab != nil {
		return p.ab
	}
	if parsed, err := abi.JSON(strings.NewReader(p.ABI)); err != nil {
		return nil, err
	} else {
		p.ab = parsed
	}
}

// ReverterMetaData contains all meta data concerning the Reverter
var ReverterMetaData = &ParsedAbi{
	Sigs: map[string]string{"7da3c3ab": "revert()"},
	ABI: "[{\"constant\":false,\"inputs\":[],\"name\":\"revert\",\"outputs\":[],\"payable\":false,\"stateMutability\":\"nonpayable\",\"type\":\"function\"}]",
	Bin:  common.FromHex("0x6080604052348015600f57600080fd5b5060ab8061001e6000396000f3fe6080604052348015600f57600080fd5b506004361060285760003560e01c80637da3c3ab14602d575b600080fd5b60336035565b005b6040805162461bcd60e51b815260206004820152601160248201527072657665727420726561736f6e2031323360781b604482015290519081900360640190fdfea265627a7a72315820e8c3f899c32798b10b8d46594292b9e3d2271f011100fb17b6d3a6469f50c8c964736f6c63430005100032"),
}

// DeployReverter deploys a new Ethereum contract, binding an instance of Reverter to it.
func DeployReverter(auth *bind.TransactOpts, backend bind.ContractBackend) (common.Address, *types.Transaction, *Reverter, error) {
	rAbi, err := ReverterMetaData.getAbi()
	if err != nil {
		return common.Address{}, nil, nil, err
	}
	address, tx, contract, err := bind.DeployContract(auth, rAbi, rAbi.Bin, backend)
	if err != nil {
		return common.Address{}, nil, nil, err
	}
	return address, tx, &Reverter{ReverterCaller: ReverterCaller{contract: contract}, ReverterTransactor: ReverterTransactor{contract: contract}, ReverterFilterer: ReverterFilterer{contract: contract}}, nil
}

@holiman
Copy link
Contributor

holiman commented Apr 1, 2021

Yeah, that looks good to me. If you don't want to break the api, maybe keep them, but remove then at some point? Something like:

// @deprecated ReverterBin is deprecated and will be removed, use ReverterMetadata.Bin  instead
var ReverterBin = ReverterMetaData.Bin

@MariusVanDerWijden MariusVanDerWijden changed the title accounts/abi/bind: create ParsedABI object accounts/abi/bind: parse ABI once Apr 8, 2021
Copy link
Contributor

@holiman holiman left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

abigen generated bindings can be optimized by parsing abi once during an init function
2 participants