From 024001888cd969958595c1343b499d7e5c6eac5d Mon Sep 17 00:00:00 2001 From: Kevin Davis Date: Tue, 28 Jan 2020 14:08:17 -0600 Subject: [PATCH] Add query for cdp deposits (#346) * feat: query cdp deposits * feat: test querying deposits * fix: update comment * fix: use single word command --- x/cdp/client/cli/query.go | 44 ++++++++++++++++++++++++++++++++++++ x/cdp/client/rest/query.go | 33 +++++++++++++++++++++++++++ x/cdp/keeper/querier.go | 30 ++++++++++++++++++++++++ x/cdp/keeper/querier_test.go | 19 ++++++++++++++++ x/cdp/types/querier.go | 15 ++++++++++++ 5 files changed, 141 insertions(+) diff --git a/x/cdp/client/cli/query.go b/x/cdp/client/cli/query.go index 30a8ff08..095b3fcb 100644 --- a/x/cdp/client/cli/query.go +++ b/x/cdp/client/cli/query.go @@ -27,6 +27,7 @@ func GetQueryCmd(queryRoute string, cdc *codec.Codec) *cobra.Command { QueryCdpCmd(queryRoute, cdc), QueryCdpsByDenomCmd(queryRoute, cdc), QueryCdpsByDenomAndRatioCmd(queryRoute, cdc), + QueryCdpDepositsCmd(queryRoute, cdc), QueryParamsCmd(queryRoute, cdc), )...) @@ -157,6 +158,49 @@ $ %s query %s cdps-by-ratio uatom 1.5 } } +// QueryCdpDepositsCmd returns the command handler for querying the deposits of a particular cdp +func QueryCdpDepositsCmd(queryRoute string, cdc *codec.Codec) *cobra.Command { + return &cobra.Command{ + Use: "deposits [owner-addr] [collateral-name]", + Short: "get deposits for a cdp", + Long: strings.TrimSpace( + fmt.Sprintf(`Get the deposits of a CDP. + +Example: +$ %s query %s deposits kava15qdefkmwswysgg4qxgqpqr35k3m49pkx2jdfnw uatom +`, version.ClientName, types.ModuleName)), + Args: cobra.ExactArgs(2), + RunE: func(cmd *cobra.Command, args []string) error { + cliCtx := context.NewCLIContext().WithCodec(cdc) + + // Prepare params for querier + ownerAddress, err := sdk.AccAddressFromBech32(args[0]) + if err != nil { + return err + } + bz, err := cdc.MarshalJSON(types.QueryCdpParams{ + CollateralDenom: args[1], + Owner: ownerAddress, + }) + if err != nil { + return err + } + + // Query + route := fmt.Sprintf("custom/%s/%s", queryRoute, types.QueryGetCdpDeposits) + res, _, err := cliCtx.QueryWithData(route, bz) + if err != nil { + return err + } + + // Decode and print results + var deposits types.Deposits + cdc.MustUnmarshalJSON(res, &deposits) + return cliCtx.PrintOutput(deposits) + }, + } +} + // QueryParamsCmd returns the command handler for cdp parameter querying func QueryParamsCmd(queryRoute string, cdc *codec.Codec) *cobra.Command { return &cobra.Command{ diff --git a/x/cdp/client/rest/query.go b/x/cdp/client/rest/query.go index c3f00067..79bd5774 100644 --- a/x/cdp/client/rest/query.go +++ b/x/cdp/client/rest/query.go @@ -19,6 +19,7 @@ func registerQueryRoutes(cliCtx context.CLIContext, r *mux.Router) { r.HandleFunc(fmt.Sprintf("/cdp/{%s}/{%s}", types.RestOwner, types.RestCollateralDenom), queryCdpHandlerFn(cliCtx)).Methods("GET") r.HandleFunc(fmt.Sprintf("/cdp/{%s}", types.RestCollateralDenom), queryCdpsHandlerFn(cliCtx)).Methods("GET") r.HandleFunc(fmt.Sprintf("/cdp/{%s}/ratio/{%s}", types.RestCollateralDenom, types.RestRatio), queryCdpsByRatioHandlerFn(cliCtx)).Methods("GET") + r.HandleFunc(fmt.Sprintf("/cdp/deposits/{%s}/{%s}", types.RestOwner, types.RestCollateralDenom), queryCdpDepositsHandlerFn(cliCtx)).Methods("GET") r.HandleFunc("/cdps/params", getParamsHandlerFn(cliCtx)).Methods("GET") } @@ -110,6 +111,38 @@ func queryCdpsByRatioHandlerFn(cliCtx context.CLIContext) http.HandlerFunc { } } +func queryCdpDepositsHandlerFn(cliCtx context.CLIContext) http.HandlerFunc { + return func(w http.ResponseWriter, r *http.Request) { + vars := mux.Vars(r) + ownerBech32 := vars[types.RestOwner] + collateralDenom := vars[types.RestCollateralDenom] + + owner, err := sdk.AccAddressFromBech32(ownerBech32) + if err != nil { + rest.WriteErrorResponse(w, http.StatusBadRequest, err.Error()) + return + } + + params := types.NewQueryCdpDeposits(owner, collateralDenom) + + bz, err := cliCtx.Codec.MarshalJSON(params) + if err != nil { + rest.WriteErrorResponse(w, http.StatusBadRequest, err.Error()) + return + } + + res, height, err := cliCtx.QueryWithData(fmt.Sprintf("custom/cdp/%s", types.QueryGetCdpDeposits), bz) + if err != nil { + rest.WriteErrorResponse(w, http.StatusNotFound, err.Error()) + return + } + + cliCtx = cliCtx.WithHeight(height) + rest.PostProcessResponse(w, cliCtx, res) + + } +} + func getParamsHandlerFn(cliCtx context.CLIContext) http.HandlerFunc { return func(w http.ResponseWriter, r *http.Request) { // Get the params diff --git a/x/cdp/keeper/querier.go b/x/cdp/keeper/querier.go index fdd8c86f..440b4e1a 100644 --- a/x/cdp/keeper/querier.go +++ b/x/cdp/keeper/querier.go @@ -20,6 +20,8 @@ func NewQuerier(keeper Keeper) sdk.Querier { return queryGetCdpsByRatio(ctx, req, keeper) case types.QueryGetParams: return queryGetParams(ctx, req, keeper) + case types.QueryGetCdpDeposits: + return queryGetDeposits(ctx, req, keeper) default: return nil, sdk.ErrUnknownRequest("unknown cdp query endpoint") } @@ -57,6 +59,34 @@ func queryGetCdp(ctx sdk.Context, req abci.RequestQuery, keeper Keeper) ([]byte, } +// query deposits on a particular cdp +func queryGetDeposits(ctx sdk.Context, req abci.RequestQuery, keeper Keeper) ([]byte, sdk.Error) { + var requestParams types.QueryCdpDeposits + err := keeper.cdc.UnmarshalJSON(req.Data, &requestParams) + if err != nil { + return nil, sdk.ErrUnknownRequest(sdk.AppendMsgToErr("incorrectly formatted request data", err.Error())) + } + + _, valid := keeper.GetDenomPrefix(ctx, requestParams.CollateralDenom) + if !valid { + return nil, types.ErrInvalidCollateralDenom(keeper.codespace, requestParams.CollateralDenom) + } + + cdp, found := keeper.GetCdpByOwnerAndDenom(ctx, requestParams.Owner, requestParams.CollateralDenom) + if !found { + return nil, types.ErrCdpNotFound(keeper.codespace, requestParams.Owner, requestParams.CollateralDenom) + } + + deposits := keeper.GetDeposits(ctx, cdp.ID) + + bz, err := codec.MarshalJSONIndent(keeper.cdc, deposits) + if err != nil { + return nil, sdk.ErrInternal(sdk.AppendMsgToErr("could not marshal result to JSON", err.Error())) + } + return bz, nil + +} + // query cdps with matching denom and ratio LESS THAN the input ratio func queryGetCdpsByRatio(ctx sdk.Context, req abci.RequestQuery, keeper Keeper) ([]byte, sdk.Error) { var requestParams types.QueryCdpsByRatioParams diff --git a/x/cdp/keeper/querier_test.go b/x/cdp/keeper/querier_test.go index 0f2310e8..41b65467 100644 --- a/x/cdp/keeper/querier_test.go +++ b/x/cdp/keeper/querier_test.go @@ -256,6 +256,25 @@ func (suite *QuerierTestSuite) TestQueryParams() { suite.Equal(gs.Params, p) } +func (suite *QuerierTestSuite) TestQueryDeposits() { + ctx := suite.ctx.WithIsCheckTx(false) + query := abci.RequestQuery{ + Path: strings.Join([]string{custom, types.QuerierRoute, types.QueryGetCdpDeposits}, "/"), + Data: types.ModuleCdc.MustMarshalJSON(types.NewQueryCdpDeposits(suite.cdps[0].Owner, suite.cdps[0].Collateral[0].Denom)), + } + + bz, err := suite.querier(ctx, []string{types.QueryGetCdpDeposits}, query) + suite.Nil(err) + suite.NotNil(bz) + + deposits := suite.keeper.GetDeposits(ctx, suite.cdps[0].ID) + + var d types.Deposits + suite.Nil(types.ModuleCdc.UnmarshalJSON(bz, &d)) + suite.Equal(deposits, d) + +} + func TestQuerierTestSuite(t *testing.T) { suite.Run(t, new(QuerierTestSuite)) } diff --git a/x/cdp/types/querier.go b/x/cdp/types/querier.go index fa3c8ed9..77f74eaf 100644 --- a/x/cdp/types/querier.go +++ b/x/cdp/types/querier.go @@ -7,6 +7,7 @@ import ( // Querier routes for the cdp module const ( QueryGetCdp = "cdp" + QueryGetCdpDeposits = "deposits" QueryGetCdps = "cdps" QueryGetCdpsByCollateralization = "ratio" QueryGetParams = "params" @@ -41,6 +42,20 @@ func NewQueryCdpParams(owner sdk.AccAddress, denom string) QueryCdpParams { } } +// QueryCdpDeposits params for query /cdp/deposits +type QueryCdpDeposits struct { + CollateralDenom string // get CDPs with this collateral denom + Owner sdk.AccAddress // get CDPs belonging to this owner +} + +// NewQueryCdpDeposits returns QueryCdpDeposits +func NewQueryCdpDeposits(owner sdk.AccAddress, denom string) QueryCdpDeposits { + return QueryCdpDeposits{ + Owner: owner, + CollateralDenom: denom, + } +} + // QueryCdpsByRatioParams params for query /cdp/cdps/ratio type QueryCdpsByRatioParams struct { CollateralDenom string // get CDPs with this collateral denom