Skip to content

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

gzliudan added a commit to gzliudan/XDPoSChain that referenced this pull request Jan 24, 2025
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