Skip to content

It would be helpful if abigen provided an easy way to manually call estimateGas #29798

Closed
@igorcrevar

Description

@igorcrevar

Rationale

Why should this feature exist?

Currently, gas estimation (if not already set) is called automatically internally inside the func (c *BoundContract) createLegacyTx or func (c *BoundContract) createDynamicTx functions.

gasLimit := opts.GasLimit
	if opts.GasLimit == 0 {
		var err error
		gasLimit, err = c.estimateGasLimit(opts, contract, input, gasPrice, nil, nil, value)
		if err != nil {
			return nil, err
		}
	}

This does not give the user the possibility to manually adjust this value. For example, a smart contract function can have a lot of conditionals (ifs), which can drastically change the gas needed if the transaction enters a block before or after some other transaction. Our solution usually was to multiply the base estimation by some arbitrary percentage value (170%) to ensure that the transaction execution will pass in any case.

What are the use-cases?

Sometimes, a user wants to manually adjust the returned gas estimation value or call it for multiple different possibilities. It would be nice if the structures generated by abigen provided that capability in an easy way.

Implementation

Do you have ideas regarding the implementation of this feature?

BoundContract can implement public method EstimateGas like this:

// EstimateGas estimates gas for method and params. GasPrice or GasFeeCap/GasTipCap should be set before calling this method
func (c *BoundContract) EstimateGas(opts *TransactOpts, method string, params ...interface{}) (uint64, error) {
	input, err := c.abi.Pack(method, params...)
	if err != nil {
		return uint64(0), err
	}

	value := opts.Value
	if value == nil {
		value = new(big.Int)
	}

	contract := &c.address

	if opts.GasPrice != nil {
		return c.estimateGasLimit(opts, contract, input, opts.GasPrice, nil, nil, value)
	}

	return c.estimateGasLimit(opts, contract, input, nil, opts.GasTipCap, opts.GasFeeCap, value)
}

Generated contract code can expose BoundCountract

func (_BridgeContract *BridgeContract) GetContract() *bind.BoundContract {
	return _BridgeContract.BridgeContractCaller.contract
}

After that, user can easily use estimation with something like this:

gasLimit, err := contract.GetContract().EstimateGas(opts, "submitSignedBatch", newSignedBatch)
if err != nil {
	return nil, err
}

// gasLimit = gasLimit * 170
tmp := new(big.Int).SetUint64(gasLimit)		
opts.GasLimit = new(big.Int).Div(tmp.Mul(tmp, big.NewInt(170)), big.NewInt(100)).Uint64()

return contract.SubmitSignedBatch(opts, newSignedBatch)

Are you willing to implement this feature?
yes

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