Skip to content

Commit 26a18f5

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 f81ad4f commit 26a18f5

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
@@ -640,9 +640,13 @@ func (ec *Client) FeeHistory(ctx context.Context, blockCount uint64, lastBlock *
640640
}
641641

642642
// EstimateGas tries to estimate the gas needed to execute a specific transaction based on
643-
// the current pending state of the backend blockchain. There is no guarantee that this is
644-
// the true gas limit requirement as other transactions may be added or removed by miners,
645-
// but it should provide a basis for setting a reasonable default.
643+
// the current state of the backend blockchain. There is no guarantee that this is the
644+
// true gas limit requirement as other transactions may be added or removed by miners, but
645+
// it should provide a basis for setting a reasonable default.
646+
//
647+
// Note that the state used by this method is implementation-defined by the remote RPC
648+
// server, but it's reasonable to assume that it will either be the pending or latest
649+
// state.
646650
func (ec *Client) EstimateGas(ctx context.Context, msg ethereum.CallMsg) (uint64, error) {
647651
var hex hexutil.Uint64
648652
err := ec.c.CallContext(ctx, &hex, "eth_estimateGas", toCallArg(msg))
@@ -652,6 +656,28 @@ func (ec *Client) EstimateGas(ctx context.Context, msg ethereum.CallMsg) (uint64
652656
return uint64(hex), nil
653657
}
654658

659+
// EstimateGasAtBlock is almost the same as EstimateGas except that it selects the block height
660+
// instead of using the remote RPC's default state for gas estimation.
661+
func (ec *Client) EstimateGasAtBlock(ctx context.Context, msg ethereum.CallMsg, blockNumber *big.Int) (uint64, error) {
662+
var hex hexutil.Uint64
663+
err := ec.c.CallContext(ctx, &hex, "eth_estimateGas", toCallArg(msg), toBlockNumArg(blockNumber))
664+
if err != nil {
665+
return 0, err
666+
}
667+
return uint64(hex), nil
668+
}
669+
670+
// EstimateGasAtBlockHash is almost the same as EstimateGas except that it selects the block
671+
// hash instead of using the remote RPC's default state for gas estimation.
672+
func (ec *Client) EstimateGasAtBlockHash(ctx context.Context, msg ethereum.CallMsg, blockHash common.Hash) (uint64, error) {
673+
var hex hexutil.Uint64
674+
err := ec.c.CallContext(ctx, &hex, "eth_estimateGas", toCallArg(msg), rpc.BlockNumberOrHashWithHash(blockHash, false))
675+
if err != nil {
676+
return 0, err
677+
}
678+
return uint64(hex), nil
679+
}
680+
655681
// SendTransaction injects a signed transaction into the pending pool for execution.
656682
//
657683
// 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
@@ -592,6 +592,33 @@ func testAtFunctions(t *testing.T, client *rpc.Client) {
592592
if !bytes.Equal(code, penCode) {
593593
t.Fatalf("unexpected code: %v %v", code, penCode)
594594
}
595+
// Use HeaderByNumber to get a header for EstimateGasAtBlock and EstimateGasAtBlockHash
596+
latestHeader, err := ec.HeaderByNumber(context.Background(), nil)
597+
if err != nil {
598+
t.Fatalf("unexpected error: %v", err)
599+
}
600+
// EstimateGasAtBlock
601+
msg := ethereum.CallMsg{
602+
From: testAddr,
603+
To: &common.Address{},
604+
Gas: 21000,
605+
Value: big.NewInt(1),
606+
}
607+
gas, err := ec.EstimateGasAtBlock(context.Background(), msg, latestHeader.Number)
608+
if err != nil {
609+
t.Fatalf("unexpected error: %v", err)
610+
}
611+
if gas != 21000 {
612+
t.Fatalf("unexpected gas limit: %v", gas)
613+
}
614+
// EstimateGasAtBlockHash
615+
gas, err = ec.EstimateGasAtBlockHash(context.Background(), msg, latestHeader.Hash())
616+
if err != nil {
617+
t.Fatalf("unexpected error: %v", err)
618+
}
619+
if gas != 21000 {
620+
t.Fatalf("unexpected gas limit: %v", gas)
621+
}
595622
}
596623

597624
func testTransactionSender(t *testing.T, client *rpc.Client) {

0 commit comments

Comments
 (0)