Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: (x/bank) add spendable balances cmd #14045

Merged
merged 24 commits into from
Jan 5, 2023
Merged
Show file tree
Hide file tree
Changes from 11 commits
Commits
Show all changes
24 commits
Select commit Hold shift + click to select a range
c987390
feat: (x/bank) add spendable balances cmd
facundomedica Nov 28, 2022
c1c3dd3
cl++
facundomedica Nov 28, 2022
8130a3d
Merge branch 'main' into facu/spendable-balances-cmd
facundomedica Nov 28, 2022
7f37972
lint
facundomedica Nov 28, 2022
c0e5de7
Merge branch 'facu/spendable-balances-cmd' of https://github.com/cosm…
facundomedica Nov 28, 2022
6e54642
Merge branch 'main' into facu/spendable-balances-cmd
facundomedica Nov 29, 2022
5234194
fix from Likhita
facundomedica Nov 29, 2022
818721f
Merge branch 'facu/spendable-balances-cmd' of https://github.com/cosm…
facundomedica Nov 29, 2022
52addae
merge
facundomedica Dec 20, 2022
f204a13
finish spendable-balances command
facundomedica Dec 20, 2022
6a87cae
Merge branch 'main' into facu/spendable-balances-cmd
facundomedica Dec 20, 2022
3b17966
remove since annotations
facundomedica Dec 20, 2022
7221114
Merge branch 'main' into facu/spendable-balances-cmd
facundomedica Dec 21, 2022
91428ba
Revert "remove since annotations"
facundomedica Dec 22, 2022
1bb3377
Merge branch 'main' of https://github.com/cosmos/cosmos-sdk into facu…
facundomedica Dec 22, 2022
24c31cb
Merge branch 'main' into facu/spendable-balances-cmd
facundomedica Dec 22, 2022
a2ef3e1
Merge branch 'main' into facu/spendable-balances-cmd
facundomedica Dec 23, 2022
2a21cf8
Merge branch 'main' into facu/spendable-balances-cmd
facundomedica Jan 2, 2023
fb5039f
Merge branch 'main' into facu/spendable-balances-cmd
facundomedica Jan 3, 2023
b2b1843
Merge branch 'main' into facu/spendable-balances-cmd
alexanderbez Jan 5, 2023
7556c02
Merge branch 'main' into facu/spendable-balances-cmd
alexanderbez Jan 5, 2023
f908c6c
fix a bad merge
facundomedica Jan 5, 2023
a12ddb2
Merge branch 'main' into facu/spendable-balances-cmd
facundomedica Jan 5, 2023
c8e606c
Merge branch 'main' into facu/spendable-balances-cmd
facundomedica Jan 5, 2023
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 2 additions & 1 deletion CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -39,8 +39,9 @@ Ref: https://keepachangelog.com/en/1.0.0/

### Features

* (x/bank) [#14045](https://github.com/cosmos/cosmos-sdk/pull/14045) Add CLI command `spendable-balances`, which also accepts the flag `--denom`.
* (x/slashing, x/staking) [#14363](https://github.com/cosmos/cosmos-sdk/pull/14363) Add the infraction a validator commited type as an argument to a `SlashWithInfractionReason` keeper method.
* (x/distribution) [#14322](https://github.com/cosmos/cosmos-sdk/pull/14322) Introduce a new gRPC message handler, `DepositValidatorRewardsPool`, that allows explicit funding of a validator's reward pool.
* (x/slashing, x/staking) [#14363](https://github.com/cosmos/cosmos-sdk/pull/14363) Add the infraction a validator committed type as an argument to a `SlashWithInfractionReason` keeper method.
* (x/evidence) [#13740](https://github.com/cosmos/cosmos-sdk/pull/13740) Add new proto field `hash` of type `string` to `QueryEvidenceRequest` which helps to decode the hash properly while using query API.
* (core) [#13306](https://github.com/cosmos/cosmos-sdk/pull/13306) Add a `FormatCoins` function to in `core/coins` to format sdk Coins following the Value Renderers spec.
* (math) [#13306](https://github.com/cosmos/cosmos-sdk/pull/13306) Add `FormatInt` and `FormatDec` functions in `math` to format integers and decimals following the Value Renderers spec.
Expand Down
1,754 changes: 1,409 additions & 345 deletions api/cosmos/bank/v1beta1/query.pulsar.go

Large diffs are not rendered by default.

54 changes: 52 additions & 2 deletions api/cosmos/bank/v1beta1/query_grpc.pb.go

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

39 changes: 38 additions & 1 deletion proto/cosmos/bank/v1beta1/query.proto
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,7 @@ service Query {
option (google.api.http).get = "/cosmos/bank/v1beta1/balances/{address}";
}

// SpendableBalances queries the spenable balance of all coins for a single
// SpendableBalances queries the spendable balance of all coins for a single
// account.
//
// When called from another module, this query might consume a high amount of
Expand All @@ -41,6 +41,18 @@ service Query {
option (google.api.http).get = "/cosmos/bank/v1beta1/spendable_balances/{address}";
}

// SpendableBalanceByDenom queries the spendable balance of a single denom for
// a single account.
//
// When called from another module, this query might consume a high amount of
// gas if the pagination field is incorrectly set.
//
// Since: cosmos-sdk 0.47
tac0turtle marked this conversation as resolved.
Show resolved Hide resolved
rpc SpendableBalanceByDenom(QuerySpendableBalanceByDenomRequest) returns (QuerySpendableBalanceByDenomResponse) {
option (cosmos.query.v1.module_query_safe) = true;
option (google.api.http).get = "/cosmos/bank/v1beta1/spendable_balances/{address}/by_denom";
}

// TotalSupply queries the total supply of all coins.
//
// When called from another module, this query might consume a high amount of
Expand Down Expand Up @@ -178,6 +190,31 @@ message QuerySpendableBalancesResponse {
cosmos.base.query.v1beta1.PageResponse pagination = 2;
}

// QuerySpendableBalanceByDenomRequest defines the gRPC request structure for
// querying an account's spendable balance for a specific denom.
//
// Since: cosmos-sdk 0.47
facundomedica marked this conversation as resolved.
Show resolved Hide resolved
message QuerySpendableBalanceByDenomRequest {
option (gogoproto.equal) = false;
option (gogoproto.goproto_getters) = false;

// address is the address to query balances for.
string address = 1 [(cosmos_proto.scalar) = "cosmos.AddressString"];

// denom is the coin denom to query balances for.
string denom = 2;
}

// QuerySpendableBalanceByDenomResponse defines the gRPC response structure for
// querying an account's spendable balance for a specific denom.
//
// Since: cosmos-sdk 0.47
facundomedica marked this conversation as resolved.
Show resolved Hide resolved
message QuerySpendableBalanceByDenomResponse {
// balance is the balance of the coin.
cosmos.base.v1beta1.Coin balance = 1;
}


// QueryTotalSupplyRequest is the request type for the Query/TotalSupply RPC
// method.
message QueryTotalSupplyRequest {
Expand Down
61 changes: 61 additions & 0 deletions x/bank/client/cli/query.go
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,7 @@ func GetQueryCmd() *cobra.Command {

cmd.AddCommand(
GetBalancesCmd(),
GetSpendableBalancesCmd(),
GetCmdQueryTotalSupply(),
GetCmdDenomsMetadata(),
GetCmdQuerySendEnabled(),
Expand Down Expand Up @@ -108,6 +109,66 @@ Example:
return cmd
}

func GetSpendableBalancesCmd() *cobra.Command {
facundomedica marked this conversation as resolved.
Show resolved Hide resolved
cmd := &cobra.Command{
Use: "spendable-balances [address]",
Short: "Query for account spendable balances by address",
Example: fmt.Sprintf("$ %s query %s spendable-balances [address]", version.AppName, types.ModuleName),
Args: cobra.ExactArgs(1),
RunE: func(cmd *cobra.Command, args []string) error {
clientCtx, err := client.GetClientQueryContext(cmd)
if err != nil {
return err
}

denom, err := cmd.Flags().GetString(FlagDenom)
if err != nil {
return err
}

queryClient := types.NewQueryClient(clientCtx)

addr, err := sdk.AccAddressFromBech32(args[0])
if err != nil {
return err
}

pageReq, err := client.ReadPageRequest(cmd.Flags())
if err != nil {
return err
}

ctx := cmd.Context()

if denom == "" {
params := types.NewQuerySpendableBalancesRequest(addr, pageReq)

res, err := queryClient.SpendableBalances(ctx, params)
if err != nil {
return err
}

return clientCtx.PrintProto(res)
}

params := types.NewQuerySpendableBalanceByDenomRequest(addr, denom)

res, err := queryClient.SpendableBalanceByDenom(ctx, params)
if err != nil {
return err
}

return clientCtx.PrintProto(res)
},
}

cmd.Flags().String(FlagDenom, "", "The specific balance denomination to query for")
flags.AddQueryFlagsToCmd(cmd)
flags.AddPaginationFlagsToCmd(cmd, "spendable balances")

return cmd
}

// GetCmdDenomsMetadata defines the cobra command to query client denomination metadata.
func GetCmdDenomsMetadata() *cobra.Command {
cmd := &cobra.Command{
Expand Down
84 changes: 84 additions & 0 deletions x/bank/client/cli/query_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -120,6 +120,90 @@ func (s *CLITestSuite) TestGetBalancesCmd() {
}
}

func (s *CLITestSuite) TestGetSpendableBalancesCmd() {
accounts := testutil.CreateKeyringAccounts(s.T(), s.kr, 1)

cmd := cli.GetSpendableBalancesCmd()
cmd.SetOutput(io.Discard)

testCases := []struct {
name string
ctxGen func() client.Context
args []string
expectResult proto.Message
expectErr bool
}{
{
"valid query",
func() client.Context {
bz, _ := s.encCfg.Codec.Marshal(&types.QuerySpendableBalancesResponse{})
c := clitestutil.NewMockTendermintRPC(abci.ResponseQuery{
Value: bz,
})
return s.baseCtx.WithClient(c)
},
[]string{
accounts[0].Address.String(),
fmt.Sprintf("--%s=json", flags.FlagOutput),
},
&types.QuerySpendableBalancesResponse{},
false,
},
{
"valid query with denom flag",
func() client.Context {
bz, _ := s.encCfg.Codec.Marshal(&types.QuerySpendableBalanceByDenomRequest{})
c := clitestutil.NewMockTendermintRPC(abci.ResponseQuery{
Value: bz,
})
return s.baseCtx.WithClient(c)
},
[]string{
accounts[0].Address.String(),
fmt.Sprintf("--%s=json", flags.FlagOutput),
fmt.Sprintf("--%s=photon", cli.FlagDenom),
},
&types.QuerySpendableBalanceByDenomResponse{},
false,
},
{
facundomedica marked this conversation as resolved.
Show resolved Hide resolved
"invalid Address",
func() client.Context {
return s.baseCtx
},
[]string{
"foo",
},
nil,
true,
},
}

for _, tc := range testCases {
tc := tc

s.Run(tc.name, func() {
var outBuf bytes.Buffer

clientCtx := tc.ctxGen().WithOutput(&outBuf)
ctx := svrcmd.CreateExecuteContext(context.Background())

cmd.SetContext(ctx)
cmd.SetArgs(tc.args)

s.Require().NoError(client.SetCmdClientContextHandler(clientCtx, cmd))

err := cmd.Execute()
if tc.expectErr {
s.Require().Error(err)
} else {
s.Require().NoError(s.encCfg.Codec.UnmarshalJSON(outBuf.Bytes(), tc.expectResult))
s.Require().NoError(err)
}
})
}
}

func (s *CLITestSuite) TestGetCmdDenomsMetadata() {
cmd := cli.GetCmdDenomsMetadata()
cmd.SetOutput(io.Discard)
Expand Down
31 changes: 23 additions & 8 deletions x/bank/keeper/grpc_query.go
Original file line number Diff line number Diff line change
Expand Up @@ -22,10 +22,6 @@ func (k BaseKeeper) Balance(ctx context.Context, req *types.QueryBalanceRequest)
return nil, status.Error(codes.InvalidArgument, "empty request")
}

if req.Address == "" {
return nil, status.Error(codes.InvalidArgument, "address cannot be empty")
}

if req.Denom == "" {
return nil, status.Error(codes.InvalidArgument, "invalid denom")
}
Expand All @@ -47,10 +43,6 @@ func (k BaseKeeper) AllBalances(ctx context.Context, req *types.QueryAllBalances
return nil, status.Error(codes.InvalidArgument, "empty request")
}

if req.Address == "" {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

why these removals

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

return nil, status.Error(codes.InvalidArgument, "address cannot be empty")
}

addr, err := sdk.AccAddressFromBech32(req.Address)
if err != nil {
return nil, status.Errorf(codes.InvalidArgument, "invalid address: %s", err.Error())
Expand Down Expand Up @@ -113,6 +105,29 @@ func (k BaseKeeper) SpendableBalances(ctx context.Context, req *types.QuerySpend
return &types.QuerySpendableBalancesResponse{Balances: result, Pagination: pageRes}, nil
}

// SpendableBalanceByDenom implements a gRPC query handler for retrieving an account's
// spendable balance for a specific denom.
func (k BaseKeeper) SpendableBalanceByDenom(ctx context.Context, req *types.QuerySpendableBalanceByDenomRequest) (*types.QuerySpendableBalanceByDenomResponse, error) {
if req == nil {
return nil, status.Error(codes.InvalidArgument, "empty request")
}

addr, err := sdk.AccAddressFromBech32(req.Address)
if err != nil {
return nil, status.Errorf(codes.InvalidArgument, "invalid address: %s", err.Error())
}

if req.Denom == "" {
return nil, status.Error(codes.InvalidArgument, "invalid denom")
}

sdkCtx := sdk.UnwrapSDKContext(ctx)

spendable := k.SpendableCoin(sdkCtx, addr, req.Denom)

return &types.QuerySpendableBalanceByDenomResponse{Balance: &spendable}, nil
}

// TotalSupply implements the Query/TotalSupply gRPC method
func (k BaseKeeper) TotalSupply(ctx context.Context, req *types.QueryTotalSupplyRequest) (*types.QueryTotalSupplyResponse, error) {
sdkCtx := sdk.UnwrapSDKContext(ctx)
Expand Down
Loading