Skip to content

Commit d95bad1

Browse files
PlasmaPowerfjl
authored andcommitted
ethclient: Add EstimateGasAtBlock[Hash] to estimate against a specific block (ethereum#27508)
The main use case I see of this is that it allows users to estimate gas against the same state that they query for their nonce, and the same state they base the data of their transaction against. This helps ensure that gas estimation won't fail and the transaction won't revert on-chain because of a mismatch between the state used for gas estimation and the state used to generate the inputs to gas estimation or the transaction's nonce when submitted to the mempool. This PR also updates the EstimateGas comment based on the new geth `eth_estimateGas` default of using latest state as of v1.12.0: ethereum#24363 --------- Co-authored-by: Felix Lange <[email protected]>
1 parent b0197e3 commit d95bad1

File tree

2 files changed

+56
-3
lines changed

2 files changed

+56
-3
lines changed

ethclient/ethclient.go

Lines changed: 29 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -717,9 +717,13 @@ func (ec *Client) FeeHistory(ctx context.Context, blockCount uint64, lastBlock *
717717
}
718718

719719
// EstimateGas tries to estimate the gas needed to execute a specific transaction based on
720-
// the current pending state of the backend blockchain. There is no guarantee that this is
721-
// the true gas limit requirement as other transactions may be added or removed by miners,
722-
// but it should provide a basis for setting a reasonable default.
720+
// the current state of the backend blockchain. There is no guarantee that this is the
721+
// true gas limit requirement as other transactions may be added or removed by miners, but
722+
// it should provide a basis for setting a reasonable default.
723+
//
724+
// Note that the state used by this method is implementation-defined by the remote RPC
725+
// server, but it's reasonable to assume that it will either be the pending or latest
726+
// state.
723727
func (ec *Client) EstimateGas(ctx context.Context, msg ethereum.CallMsg) (uint64, error) {
724728
var hex hexutil.Uint64
725729
err := ec.c.CallContext(ctx, &hex, "eth_estimateGas", toCallArg(msg))
@@ -729,6 +733,28 @@ func (ec *Client) EstimateGas(ctx context.Context, msg ethereum.CallMsg) (uint64
729733
return uint64(hex), nil
730734
}
731735

736+
// EstimateGasAtBlock is almost the same as EstimateGas except that it selects the block height
737+
// instead of using the remote RPC's default state for gas estimation.
738+
func (ec *Client) EstimateGasAtBlock(ctx context.Context, msg ethereum.CallMsg, blockNumber *big.Int) (uint64, error) {
739+
var hex hexutil.Uint64
740+
err := ec.c.CallContext(ctx, &hex, "eth_estimateGas", toCallArg(msg), toBlockNumArg(blockNumber))
741+
if err != nil {
742+
return 0, err
743+
}
744+
return uint64(hex), nil
745+
}
746+
747+
// EstimateGasAtBlockHash is almost the same as EstimateGas except that it selects the block
748+
// hash instead of using the remote RPC's default state for gas estimation.
749+
func (ec *Client) EstimateGasAtBlockHash(ctx context.Context, msg ethereum.CallMsg, blockHash common.Hash) (uint64, error) {
750+
var hex hexutil.Uint64
751+
err := ec.c.CallContext(ctx, &hex, "eth_estimateGas", toCallArg(msg), rpc.BlockNumberOrHashWithHash(blockHash, false))
752+
if err != nil {
753+
return 0, err
754+
}
755+
return uint64(hex), nil
756+
}
757+
732758
// SendTransaction injects a signed transaction into the pending pool for execution.
733759
//
734760
// If the transaction was a contract creation use the TransactionReceipt method to get the

ethclient/ethclient_test.go

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -625,6 +625,33 @@ func testSendTransactionConditional(t *testing.T, client *rpc.Client) {
625625
if err := sendTransactionConditional(ec); err != nil {
626626
t.Fatalf("error: %v", err)
627627
}
628+
// Use HeaderByNumber to get a header for EstimateGasAtBlock and EstimateGasAtBlockHash
629+
latestHeader, err := ec.HeaderByNumber(context.Background(), nil)
630+
if err != nil {
631+
t.Fatalf("unexpected error: %v", err)
632+
}
633+
// EstimateGasAtBlock
634+
msg := ethereum.CallMsg{
635+
From: testAddr,
636+
To: &common.Address{},
637+
Gas: 21000,
638+
Value: big.NewInt(1),
639+
}
640+
gas, err := ec.EstimateGasAtBlock(context.Background(), msg, latestHeader.Number)
641+
if err != nil {
642+
t.Fatalf("unexpected error: %v", err)
643+
}
644+
if gas != 21000 {
645+
t.Fatalf("unexpected gas limit: %v", gas)
646+
}
647+
// EstimateGasAtBlockHash
648+
gas, err = ec.EstimateGasAtBlockHash(context.Background(), msg, latestHeader.Hash())
649+
if err != nil {
650+
t.Fatalf("unexpected error: %v", err)
651+
}
652+
if gas != 21000 {
653+
t.Fatalf("unexpected gas limit: %v", gas)
654+
}
628655
}
629656

630657
func sendTransactionConditional(ec *ethclient.Client) error {

0 commit comments

Comments
 (0)