diff --git a/ethclient/gethclient/gethclient.go b/ethclient/gethclient/gethclient.go index 6a0f5eb3123..9c732ea4739 100644 --- a/ethclient/gethclient/gethclient.go +++ b/ethclient/gethclient/gethclient.go @@ -166,6 +166,17 @@ func (ec *Client) CallContractWithBlockOverrides(ctx context.Context, msg ethere return hex, err } +// BlockReceipts returns the receipts of a given block number or hash. +// This wrapper preserves geth-specific flags such as requireCanonical. +func (ec *Client) BlockReceipts(ctx context.Context, blockNrOrHash rpc.BlockNumberOrHash) ([]*types.Receipt, error) { + var r []*types.Receipt + err := ec.c.CallContext(ctx, &r, "eth_getBlockReceipts", blockNrOrHash) + if err == nil && r == nil { + return nil, ethereum.NotFound + } + return r, err +} + // GCStats retrieves the current garbage collection stats from a geth node. func (ec *Client) GCStats(ctx context.Context) (*debug.GCStats, error) { var result debug.GCStats diff --git a/ethclient/gethclient/gethclient_test.go b/ethclient/gethclient/gethclient_test.go index 0eed63cacfe..47a5cd312fd 100644 --- a/ethclient/gethclient/gethclient_test.go +++ b/ethclient/gethclient/gethclient_test.go @@ -620,3 +620,44 @@ func testCallContractWithBlockOverrides(t *testing.T, client *rpc.Client) { t.Fatalf("unexpected result: %x", res) } } + +func TestGethClientBlockReceiptsPreservesRequireCanonical(t *testing.T) { + server := rpc.NewServer() + service := &testGethBlockReceiptsService{} + if err := server.RegisterName("eth", service); err != nil { + t.Fatalf("failed to register test service: %v", err) + } + defer server.Stop() + + rpcClient := rpc.DialInProc(server) + defer rpcClient.Close() + + client := New(rpcClient) + + hash := common.HexToHash("0x1111111111111111111111111111111111111111111111111111111111111111") + arg := rpc.BlockNumberOrHashWithHash(hash, true) + + if _, err := client.BlockReceipts(context.Background(), arg); err != nil { + t.Fatalf("BlockReceipts returned error: %v", err) + } + if !service.called { + t.Fatalf("expected GetBlockReceipts to be called") + } + if !service.lastArg.RequireCanonical { + t.Fatalf("requireCanonical flag was not preserved") + } + if service.lastArg.BlockHash == nil || *service.lastArg.BlockHash != hash { + t.Fatalf("unexpected block hash: %v", service.lastArg.BlockHash) + } +} + +type testGethBlockReceiptsService struct { + lastArg rpc.BlockNumberOrHash + called bool +} + +func (s *testGethBlockReceiptsService) GetBlockReceipts(ctx context.Context, arg rpc.BlockNumberOrHash) ([]*types.Receipt, error) { + s.lastArg = arg + s.called = true + return []*types.Receipt{}, nil +}