diff --git a/ethclient/ethclient.go b/ethclient/ethclient.go index e6a93c96f6..68389efbf4 100644 --- a/ethclient/ethclient.go +++ b/ethclient/ethclient.go @@ -456,6 +456,17 @@ func (ec *Client) CallContract(ctx context.Context, msg ethereum.CallMsg, blockN return hex, nil } +// CallContractAtHash is almost the same as CallContract except that it selects +// the block by block hash instead of block height. +func (ec *Client) CallContractAtHash(ctx context.Context, msg ethereum.CallMsg, blockHash common.Hash) ([]byte, error) { + var hex hexutil.Bytes + err := ec.c.CallContext(ctx, &hex, "eth_call", toCallArg(msg), rpc.BlockNumberOrHashWithHash(blockHash, false)) + if err != nil { + return nil, err + } + return hex, nil +} + // PendingCallContract executes a message call transaction using the EVM. // The state seen by the contract call is the pending state. func (ec *Client) PendingCallContract(ctx context.Context, msg ethereum.CallMsg) ([]byte, error) { diff --git a/ethclient/ethclient_test.go b/ethclient/ethclient_test.go index d56febc91d..4a8727b374 100644 --- a/ethclient/ethclient_test.go +++ b/ethclient/ethclient_test.go @@ -285,6 +285,9 @@ func TestEthClient(t *testing.T) { "CallContract": { func(t *testing.T) { testCallContract(t, client) }, }, + "CallContractAtHash": { + func(t *testing.T) { testCallContractAtHash(t, client) }, + }, "AtFunctions": { func(t *testing.T) { testAtFunctions(t, client) }, }, @@ -507,6 +510,33 @@ func testStatusFunctions(t *testing.T, client *rpc.Client) { } } +func testCallContractAtHash(t *testing.T, client *rpc.Client) { + ec := NewClient(client) + + // EstimateGas + msg := ethereum.CallMsg{ + From: testAddr, + To: &common.Address{}, + Gas: 21000, + Value: big.NewInt(1), + } + gas, err := ec.EstimateGas(context.Background(), msg) + if err != nil { + t.Fatalf("unexpected error: %v", err) + } + if gas != 21000 { + t.Fatalf("unexpected gas price: %v", gas) + } + block, err := ec.HeaderByNumber(context.Background(), big.NewInt(1)) + if err != nil { + t.Fatalf("BlockByNumber error: %v", err) + } + // CallContract + if _, err := ec.CallContractAtHash(context.Background(), msg, block.Hash()); err != nil { + t.Fatalf("unexpected error: %v", err) + } +} + func testCallContract(t *testing.T, client *rpc.Client) { ec := NewClient(client)