diff --git a/ioctl/newcmd/bc/bc.go b/ioctl/newcmd/bc/bc.go
index 003e3e4f0e..dcbbe8a10f 100644
--- a/ioctl/newcmd/bc/bc.go
+++ b/ioctl/newcmd/bc/bc.go
@@ -17,6 +17,7 @@ import (
 	"github.com/spf13/cobra"
 	"google.golang.org/grpc/codes"
 	"google.golang.org/grpc/status"
+	"google.golang.org/protobuf/proto"
 
 	"github.com/iotexproject/iotex-core/ioctl"
 	"github.com/iotexproject/iotex-core/ioctl/config"
@@ -129,3 +130,46 @@ func GetProbationList(client ioctl.Client, epochNum uint64, epochStartHeight uin
 	}
 	return response, nil
 }
+
+// GetBucketList get bucket list
+func GetBucketList(
+	client ioctl.Client,
+	methodName iotexapi.ReadStakingDataMethod_Name,
+	readStakingDataRequest *iotexapi.ReadStakingDataRequest,
+) (*iotextypes.VoteBucketList, error) {
+	apiServiceClient, err := client.APIServiceClient()
+	if err != nil {
+		return nil, err
+	}
+	methodData, err := proto.Marshal(&iotexapi.ReadStakingDataMethod{Method: methodName})
+	if err != nil {
+		return nil, errors.Wrap(err, "failed to marshal read staking data method")
+	}
+	requestData, err := proto.Marshal(readStakingDataRequest)
+	if err != nil {
+		return nil, errors.Wrap(err, "failed to marshal read staking data request")
+	}
+	request := &iotexapi.ReadStateRequest{
+		ProtocolID: []byte("staking"),
+		MethodName: methodData,
+		Arguments:  [][]byte{requestData},
+	}
+	ctx := context.Background()
+	if jwtMD, err := util.JwtAuth(); err == nil {
+		ctx = metautils.NiceMD(jwtMD).ToOutgoing(ctx)
+	}
+
+	response, err := apiServiceClient.ReadState(ctx, request)
+	if err != nil {
+		sta, ok := status.FromError(err)
+		if ok {
+			return nil, errors.New(sta.Message())
+		}
+		return nil, errors.Wrap(err, "failed to invoke ReadState api")
+	}
+	bucketlist := iotextypes.VoteBucketList{}
+	if err := proto.Unmarshal(response.Data, &bucketlist); err != nil {
+		return nil, errors.Wrap(err, "failed to unmarshal response")
+	}
+	return &bucketlist, nil
+}
diff --git a/ioctl/newcmd/bc/bc_test.go b/ioctl/newcmd/bc/bc_test.go
new file mode 100644
index 0000000000..54768eadd2
--- /dev/null
+++ b/ioctl/newcmd/bc/bc_test.go
@@ -0,0 +1,49 @@
+// Copyright (c) 2022 IoTeX Foundation
+// This is an alpha (internal) release and is not suitable for production. This source code is provided 'as is' and no
+// warranties are given as to title or non-infringement, merchantability or fitness for purpose and, to the extent
+// permitted by law, all liability for your use of the code is disclaimed. This source code is governed by Apache
+// License 2.0 that can be found in the LICENSE file.
+
+package bc
+
+import (
+	"testing"
+
+	"github.com/golang/mock/gomock"
+	"github.com/iotexproject/iotex-proto/golang/iotexapi"
+	"github.com/iotexproject/iotex-proto/golang/iotexapi/mock_iotexapi"
+	"github.com/iotexproject/iotex-proto/golang/iotextypes"
+	"github.com/pkg/errors"
+	"github.com/stretchr/testify/require"
+
+	"github.com/iotexproject/iotex-core/test/mock/mock_ioctlclient"
+)
+
+func TestGetBucketList(t *testing.T) {
+	require := require.New(t)
+	ctrl := gomock.NewController(t)
+	client := mock_ioctlclient.NewMockClient(ctrl)
+	apiServiceClient := mock_iotexapi.NewMockAPIServiceClient(ctrl)
+	request := &iotexapi.ReadStakingDataRequest{}
+	response := &iotexapi.ReadStateResponse{}
+	client.EXPECT().APIServiceClient().Return(apiServiceClient, nil).Times(2)
+
+	t.Run("get bucket list", func(t *testing.T) {
+		expectedValue := &iotextypes.VoteBucketList{
+			Buckets: []*iotextypes.VoteBucket{},
+		}
+		apiServiceClient.EXPECT().ReadState(gomock.Any(), gomock.Any()).Return(response, nil)
+
+		result, err := GetBucketList(client, iotexapi.ReadStakingDataMethod_BUCKETS_BY_CANDIDATE, request)
+		require.NoError(err)
+		require.Contains(result.String(), expectedValue.String())
+	})
+
+	t.Run("failed to invoke ReadState api", func(t *testing.T) {
+		expectedErr := errors.New("failed to invoke ReadState api")
+		apiServiceClient.EXPECT().ReadState(gomock.Any(), gomock.Any()).Return(nil, expectedErr)
+
+		_, err := GetBucketList(client, iotexapi.ReadStakingDataMethod_BUCKETS_BY_VOTER, request)
+		require.Contains(err.Error(), expectedErr.Error())
+	})
+}
diff --git a/ioctl/newcmd/bc/bcbucketlist.go b/ioctl/newcmd/bc/bcbucketlist.go
new file mode 100644
index 0000000000..6bb4f07ac9
--- /dev/null
+++ b/ioctl/newcmd/bc/bcbucketlist.go
@@ -0,0 +1,145 @@
+// Copyright (c) 2022 IoTeX Foundation
+// This is an alpha (internal) release and is not suitable for production. This source code is provided 'as is' and no
+// warranties are given as to title or non-infringement, merchantability or fitness for purpose and, to the extent
+// permitted by law, all liability for your use of the code is disclaimed. This source code is governed by Apache
+// License 2.0 that can be found in the LICENSE file.
+
+package bc
+
+import (
+	"strconv"
+	"strings"
+
+	"github.com/iotexproject/iotex-proto/golang/iotexapi"
+	"github.com/iotexproject/iotex-proto/golang/iotextypes"
+	"github.com/pkg/errors"
+	"github.com/spf13/cobra"
+
+	"github.com/iotexproject/iotex-core/ioctl"
+	"github.com/iotexproject/iotex-core/ioctl/config"
+)
+
+// constants
+const (
+	MethodVoter     = "voter"
+	MethodCandidate = "cand"
+)
+
+// Multi-language support
+var (
+	_bcBucketListCmdShorts = map[config.Language]string{
+		config.English: "Get bucket list with method and arg(s) on IoTeX blockchain",
+		config.Chinese: "根据方法和参数在IoTeX区块链上读取投票列表",
+	}
+	_bcBucketListCmdUses = map[config.Language]string{
+		config.English: "bucketlist <method> [arguments]",
+		config.Chinese: "bucketlist <方法> [参数]",
+	}
+	_bcBucketListCmdLongs = map[config.Language]string{
+		config.English: "Read bucket list\nValid methods: [" +
+			MethodVoter + ", " + MethodCandidate + "]",
+		config.Chinese: "根据方法和参数在IoTeX区块链上读取投票列表\n可用方法有:" +
+			MethodVoter + "," + MethodCandidate,
+	}
+)
+
+// NewBCBucketListCmd represents the bc bucketlist command
+func NewBCBucketListCmd(client ioctl.Client) *cobra.Command {
+	use, _ := client.SelectTranslation(_bcBucketListCmdUses)
+	short, _ := client.SelectTranslation(_bcBucketListCmdShorts)
+	long, _ := client.SelectTranslation(_bcBucketListCmdLongs)
+
+	return &cobra.Command{
+		Use:   use,
+		Short: short,
+		Long:  long,
+		Args:  cobra.MinimumNArgs(2),
+		Example: `ioctl bc bucketlist voter [VOTER_ADDRESS] [OFFSET] [LIMIT]
+	ioctl bc bucketlist cand [CANDIDATE_NAME] [OFFSET] [LIMIT]`,
+		RunE: func(cmd *cobra.Command, args []string) error {
+			cmd.SilenceUsage = true
+
+			var (
+				bl      *iotextypes.VoteBucketList
+				address string
+				err     error
+			)
+
+			offset, limit := uint64(0), uint64(1000)
+			method, addr := args[0], args[1]
+			s := args[2:]
+
+			if len(s) > 0 {
+				offset, err = strconv.ParseUint(s[0], 10, 64)
+				if err != nil {
+					return errors.Wrap(err, "invalid offset")
+				}
+			}
+			if len(s) > 1 {
+				limit, err = strconv.ParseUint(s[1], 10, 64)
+				if err != nil {
+					return errors.Wrap(err, "invalid limit")
+				}
+			}
+			switch method {
+			case MethodVoter:
+				address, err = client.AddressWithDefaultIfNotExist(addr)
+				if err != nil {
+					return err
+				}
+				bl, err = getBucketListByVoterAddress(client, address, uint32(offset), uint32(limit))
+			case MethodCandidate:
+				bl, err = getBucketListByCandidateName(client, addr, uint32(offset), uint32(limit))
+			default:
+				return errors.New("unknown <method>")
+			}
+			if err != nil {
+				return err
+			}
+			var lines []string
+			if len(bl.Buckets) == 0 {
+				lines = append(lines, "Empty bucketlist with given address")
+			} else {
+				for _, b := range bl.Buckets {
+					bucket, err := newBucket(b)
+					if err != nil {
+						return err
+					}
+					lines = append(lines, bucket.String())
+				}
+			}
+			cmd.Println(strings.Join(lines, "\n"))
+			return nil
+		},
+	}
+}
+
+func getBucketListByVoterAddress(client ioctl.Client, addr string, offset, limit uint32) (*iotextypes.VoteBucketList, error) {
+	readStakingdataRequest := &iotexapi.ReadStakingDataRequest{
+		Request: &iotexapi.ReadStakingDataRequest_BucketsByVoter{
+			BucketsByVoter: &iotexapi.ReadStakingDataRequest_VoteBucketsByVoter{
+				VoterAddress: addr,
+				Pagination: &iotexapi.PaginationParam{
+					Offset: offset,
+					Limit:  limit,
+				},
+			},
+		},
+	}
+	return GetBucketList(client, iotexapi.ReadStakingDataMethod_BUCKETS_BY_VOTER, readStakingdataRequest)
+}
+
+func getBucketListByCandidateName(client ioctl.Client, candName string, offset, limit uint32) (*iotextypes.VoteBucketList, error) {
+	readStakingDataRequest := &iotexapi.ReadStakingDataRequest{
+		Request: &iotexapi.ReadStakingDataRequest_BucketsByCandidate{
+			BucketsByCandidate: &iotexapi.ReadStakingDataRequest_VoteBucketsByCandidate{
+				CandName: candName,
+				Pagination: &iotexapi.PaginationParam{
+					Offset: offset,
+					Limit:  limit,
+				},
+			},
+		},
+	}
+	return GetBucketList(client, iotexapi.ReadStakingDataMethod_BUCKETS_BY_CANDIDATE, readStakingDataRequest)
+}
diff --git a/ioctl/newcmd/bc/bcbucketlist_test.go b/ioctl/newcmd/bc/bcbucketlist_test.go
new file mode 100644
index 0000000000..facec37302
--- /dev/null
+++ b/ioctl/newcmd/bc/bcbucketlist_test.go
@@ -0,0 +1,104 @@
+// Copyright (c) 2022 IoTeX Foundation
+// This is an alpha (internal) release and is not suitable for production. This source code is provided 'as is' and no
+// warranties are given as to title or non-infringement, merchantability or fitness for purpose and, to the extent
+// permitted by law, all liability for your use of the code is disclaimed. This source code is governed by Apache
+// License 2.0 that can be found in the LICENSE file.
+
+package bc
+
+import (
+	"testing"
+
+	"github.com/golang/mock/gomock"
+	"github.com/iotexproject/iotex-proto/golang/iotexapi"
+	"github.com/iotexproject/iotex-proto/golang/iotexapi/mock_iotexapi"
+	"github.com/iotexproject/iotex-proto/golang/iotextypes"
+	"github.com/pkg/errors"
+	"github.com/stretchr/testify/require"
+	"google.golang.org/protobuf/proto"
+	"google.golang.org/protobuf/types/known/timestamppb"
+
+	"github.com/iotexproject/iotex-core/ioctl/config"
+	"github.com/iotexproject/iotex-core/ioctl/util"
+	"github.com/iotexproject/iotex-core/test/mock/mock_ioctlclient"
+	"github.com/iotexproject/iotex-core/testutil"
+)
+
+func TestNewBCBucketListCmd(t *testing.T) {
+	require := require.New(t)
+	ctrl := gomock.NewController(t)
+	client := mock_ioctlclient.NewMockClient(ctrl)
+	apiServiceClient := mock_iotexapi.NewMockAPIServiceClient(ctrl)
+
+	client.EXPECT().SelectTranslation(gomock.Any()).Return("", config.English).Times(21)
+	client.EXPECT().APIServiceClient().Return(apiServiceClient, nil).Times(2)
+	client.EXPECT().Config().Return(config.Config{}).Times(2)
+
+	t.Run("get bucket list by voter", func(t *testing.T) {
+		client.EXPECT().AddressWithDefaultIfNotExist(gomock.Any()).Return("io1uwnr55vqmhf3xeg5phgurlyl702af6eju542sx", nil)
+		vblist, err := proto.Marshal(&iotextypes.VoteBucketList{
+			Buckets: []*iotextypes.VoteBucket{
+				{
+					Index:            1,
+					StakedAmount:     "10",
+					UnstakeStartTime: timestamppb.New(testutil.TimestampNow()),
+				},
+				{
+					Index:            2,
+					StakedAmount:     "20",
+					UnstakeStartTime: timestamppb.New(testutil.TimestampNow()),
+				},
+			},
+		})
+		require.NoError(err)
+		apiServiceClient.EXPECT().ReadState(gomock.Any(), gomock.All()).Return(&iotexapi.ReadStateResponse{
+			Data: vblist,
+		}, nil)
+
+		cmd := NewBCBucketListCmd(client)
+		result, err := util.ExecuteCmd(cmd, "voter", "io1uwnr55vqmhf3xeg5phgurlyl702af6eju542sx")
+		require.NoError(err)
+		require.Contains(result,
+			"index: 1",
+			"stakedAmount: 0.00000000000000001 IOTX",
+			"index: 2",
+			"stakedAmount: 0.00000000000000002 IOTX")
+	})
+
+	t.Run("get bucket list by candidate", func(t *testing.T) {
+		apiServiceClient.EXPECT().ReadState(gomock.Any(), gomock.All()).Return(&iotexapi.ReadStateResponse{}, nil)
+		cmd := NewBCBucketListCmd(client)
+		result, err := util.ExecuteCmd(cmd, "cand", "io1uwnr55vqmhf3xeg5phgurlyl702af6eju542sx")
+		require.NoError(err)
+		require.Equal("Empty bucketlist with given address\n", result)
+	})
+
+	t.Run("invalid voter address", func(t *testing.T) {
+		expectedErr := errors.New("cannot find address for alias test")
+		client.EXPECT().AddressWithDefaultIfNotExist(gomock.Any()).Return("", expectedErr)
+		cmd := NewBCBucketListCmd(client)
+		_, err := util.ExecuteCmd(cmd, "voter", "test")
+		require.Contains(err.Error(), expectedErr.Error())
+	})
+
+	t.Run("unknown method", func(t *testing.T) {
+		expectedErr := errors.New("unknown <method>")
+		cmd := NewBCBucketListCmd(client)
+		_, err := util.ExecuteCmd(cmd, "unknown", "io1uwnr55vqmhf3xeg5phgurlyl702af6eju542sx")
+		require.Equal(expectedErr.Error(), err.Error())
+	})
+
+	t.Run("invalid offset", func(t *testing.T) {
+		expectedErr := errors.New("invalid offset")
+		cmd := NewBCBucketListCmd(client)
+		_, err := util.ExecuteCmd(cmd, "voter", "io1uwnr55vqmhf3xeg5phgurlyl702af6eju542sx", "test")
+		require.Contains(err.Error(), expectedErr.Error())
+	})
+
+	t.Run("invalid limit", func(t *testing.T) {
+		expectedErr := errors.New("invalid limit")
+		cmd := NewBCBucketListCmd(client)
+		_, err := util.ExecuteCmd(cmd, "voter", "io1uwnr55vqmhf3xeg5phgurlyl702af6eju542sx", "0", "test")
+		require.Contains(err.Error(), expectedErr.Error())
+	})
+}