Querier improvements: CDP and Auction priority 1 queries (#644)

* query auction by lot owner

* add SavingsRateDistributed to store

* v2cdps: filtered cdps query

* update v2cdps cli examples

* add savings rate dist counter to begin blocker

* implement savings rate dist cli query

* implement cdp REST queries

* minor auction CLI/REST updates

* fix auction querier bug

* update REST endpoint to 'cdps'

* update to savings-rate-dist

* update SavingsRateDistributed get/set

* update tests

* fix savings rate dist rounding errors

* 'collateralDenom' -> 'collateralType'

* refactor 'v2cdps' -> 'cdps', add ratio param

* fix augmented CDP type, msg string() method

* fix cdp querier test

* filter query results efficiently

* querier tests

* limit type iteration if owner defined

* improve savings rate dist genesis validation

* default sdk.Dec{} to sdk.ZeroDec in queries

* update condition logic for finding intersection

* fix cdp querier filtering

* Update kava-4 swagger (#653)

* add collateral_type, update cdp params

* savings rate, auctions, get cdps

* drop owner from AuctionResponse

* remove duplicate collateral denom

* update query paths with {collateral-type}
This commit is contained in:
Denali Marsh 2020-09-17 02:45:10 +02:00 committed by GitHub
parent 641d946ae7
commit e2f515ba9e
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
28 changed files with 984 additions and 214 deletions

View File

@ -410,6 +410,8 @@
$ref: '#/definitions/CoinCollateral'
principal:
$ref: '#/definitions/CoinPrincipal'
collateral_type:
$ref: '#/definitions/CollateralType'
responses:
200:
description: Tx was successfully generated
@ -419,7 +421,7 @@
description: Invalid request
500:
description: Server internal error
/cdp/{owner}/{denom}/deposits:
/cdp/{owner}/{collateral-type}/deposits:
post:
summary: Create a deposit to cdp transaction
tags:
@ -436,11 +438,11 @@
type: string
x-example: kava1ffv7nhd3z6sych2qpqkk03ec6hzkmufy0r2s4c
- in: path
name: denom
description: Collateral denom
name: collateral_type
description: Collateral type
required: true
type: string
x-example: xrp
x-example: xrp-a
- description: deposit cdp post parameters
name: post_deposit_req
in: body
@ -456,6 +458,8 @@
$ref: '#/definitions/Address'
collateral:
$ref: '#/definitions/CoinCollateral'
collateral_type:
$ref: '#/definitions/CollateralType'
responses:
200:
description: Tx was successfully generated
@ -465,7 +469,7 @@
description: Invalid request
500:
description: Server internal error
/cdp/{owner}/{denom}/withdraw:
/cdp/{owner}/{collateral-type}/withdraw:
post:
summary: create a withdraw collateral transaction
tags:
@ -482,11 +486,11 @@
type: string
x-example: kava1ffv7nhd3z6sych2qpqkk03ec6hzkmufy0r2s4c
- in: path
name: denom
description: Collateral denom
name: collateral_type
description: Collateral type
required: true
type: string
x-example: xrp
x-example: xrp-a
- description: withdraw cdp post parameters
name: post_withdraw_req
in: body
@ -502,6 +506,8 @@
$ref: '#/definitions/Address'
collateral:
$ref: '#/definitions/CoinCollateral'
collateral_type:
$ref: '#/definitions/CollateralType'
responses:
200:
description: Tx was successfully generated
@ -511,7 +517,7 @@
description: Invalid request
500:
description: Server internal error
/cdp/{owner}/{denom}/draw:
/cdp/{owner}/{collateral-type}/draw:
post:
summary: Create a draw debt transaction
tags:
@ -528,11 +534,11 @@
type: string
x-example: kava1ffv7nhd3z6sych2qpqkk03ec6hzkmufy0r2s4c
- in: path
name: denom
description: Collateral denom
name: collateral_type
description: Collateral type
required: true
type: string
x-example: xrp
x-example: xrp-a
- description: draw cdp post parameters
name: post_draw_req
in: body
@ -546,6 +552,8 @@
$ref: '#/definitions/Address'
principal:
$ref: '#/definitions/CoinPrincipal'
collateral_type:
$ref: '#/definitions/CollateralType'
responses:
200:
description: Tx was successfully generated
@ -555,7 +563,7 @@
description: Invalid request
500:
description: Server internal error
/cdp/{owner}/{denom}/repay:
/cdp/{owner}/{collateral-type}/repay:
post:
summary: Repay debt from a CDP
tags:
@ -571,12 +579,12 @@
required: true
type: string
x-example: kava1ffv7nhd3z6sych2qpqkk03ec6hzkmufy0r2s4c
- in: path
name: denom
description: Collateral denom
- in: path
name: collateral_type
description: Collateral type
required: true
type: string
x-example: xrp
x-example: xrp-a
- description: repay cdp post parameters
name: post_repay_req
in: body
@ -590,6 +598,8 @@
$ref: '#/definitions/Address'
payment:
$ref: '#/definitions/CoinPrincipal'
collateral_type:
$ref: '#/definitions/CollateralType'
responses:
200:
description: Tx was successfully generated
@ -640,7 +650,6 @@
type: number
500:
description: Server internal error
/cdp/parameters:
get:
summary: Get the parameters of the cdp module
@ -669,17 +678,26 @@
surplus_auction_threshold:
type: string
example: '1000000000'
surplus_auction_lot:
type: string
example: '10000000'
debt_auction_threshold:
type: string
example: '1000000000'
surplus_auction_lot:
type: string
example: '10000000'
savings_distribution_frequency:
type: string
example: '60000000'
circuit_breaker:
type: boolean
example: false
500:
description: Server internal error
/cdp/cdps/cdp/{owner}/{denom}:
/cdp/cdps/cdp/{owner}/{collateral-type}:
get:
summary: Get the cdp associated with the input owner address and collateral denom
summary: Get the cdp associated with the input owner address and collateral type
tags:
- CDP
produces:
@ -692,11 +710,11 @@
type: string
x-example: kava1ffv7nhd3z6sych2qpqkk03ec6hzkmufy0r2s4c
- in: path
name: denom
description: Collateral denom
name: type
description: Collateral type
required: true
type: string
x-example: xrp
x-example: xrp-a
responses:
200:
description: CDP associated with owner
@ -704,23 +722,23 @@
$ref: '#/definitions/CdpResponse'
500:
description: Server internal error
/cdp/cdps/denom/{denom}:
/cdp/cdps/denom/{collateral-type}:
get:
summary: Get all cdps with collateral type equal to the input collateral denom
summary: Get all cdps with collateral type equal to the input collateral type
tags:
- CDP
produces:
- application/json
parameters:
- in: path
name: denom
description: Collateral denom
name: type
description: Collateral Type
required: true
type: string
x-example: xrp
x-example: xrp-a
responses:
200:
description: All CDPs with the input collateral denom
description: All CDPs with the input collateral type
schema:
type: object
properties:
@ -734,20 +752,20 @@
$ref: '#/definitions/CdpResponse'
500:
description: Server internal error
/cdp/cdps/ratio/{denom}/{ratio}:
/cdp/cdps/ratio/{collateral-type}/{ratio}:
get:
summary: Get all cdps with collateral type equal to the input collateral denom and collateralization ratio strictly less than the input ratio
summary: Get all cdps with collateral type equal to the input collateral type and collateralization ratio strictly less than the input ratio
tags:
- CDP
produces:
- application/json
parameters:
- in: path
name: denom
description: Collateral denom
name: type
description: Collateral type
required: true
type: string
x-example: xrp
x-example: xrp-a
- in: path
name: ratio
description: Collateralization ratio
@ -756,7 +774,7 @@
x-example: "2.0"
responses:
200:
description: All CDPs with the input collateral denom and collateralization ratio less than the input ratio
description: All CDPs with the input collateral type and collateralization ratio less than the input ratio
schema:
type: object
properties:
@ -770,9 +788,9 @@
$ref: '#/definitions/CdpResponse'
500:
description: Server internal error
/cdp/cdps/cdp/deposits/{owner}/{denom}:
/cdp/cdps/cdp/deposits/{owner}/{collateral-type}:
get:
summary: Get the deposits associated with the cdp owned by the input owner address and with collateral type equal to the input collateral denom
summary: Get the deposits associated with the cdp owned by an address for a collateral type
tags:
- CDP
produces:
@ -785,11 +803,11 @@
type: string
x-example: kava1ffv7nhd3z6sych2qpqkk03ec6hzkmufy0r2s4c
- in: path
name: denom
description: Collateral denom
name: type
description: Collateral type
required: true
type: string
x-example: xrp
x-example: xrp-a
responses:
200:
description: Deposits associated with the cdp
@ -805,6 +823,73 @@
$ref: '#/definitions/CdpDepositResponse'
500:
description: Server internal error
/cdp/savings-rate-dist:
get:
summary: Get the total savings rate distributed by the cdp module
tags:
- CDP
produces:
- application/json
responses:
200:
description: The distributed savings rate
properties:
height:
type: string
example: "100"
result:
savings_rate_distributed:
type: string
example: "5000000000000"
500:
description: Server internal error
/cdp/cdps:
get:
summary: Query all active cdps
tags:
- CDP
produces:
- application/json
parameters:
- in: query
name: owner
description: Owner address in bech32 format
required: false
type: string
x-example: kava1ffv7nhd3z6sych2qpqkk03ec6hzkmufy0r2s4c
- in: query
name: collateral-type
description: Collateral type
required: false
type: string
x-example: xrp-a
- in: query
name: id
description: CDP ID
required: false
type: string
x-example: "4"
- in: query
name: ratio
description: Collateralization ratio
required: false
type: string
x-example: "2.75"
responses:
200:
description: Query cdps
schema:
type: object
properties:
height:
type: string
example: "100"
result:
type: array
items:
$ref: '#/definitions/CdpResponse'
500:
description: Internal Server Error
/bep3/swap/create:
post:
summary: Generate a create atomic swap transaction
@ -1273,7 +1358,7 @@
$ref: '#/definitions/BaseReq'
owner:
$ref: '#/definitions/Address'
denom:
type:
type: string
example: bnb
responses:
@ -1285,9 +1370,9 @@
description: Invalid request
500:
description: Internal server error
/incentive/claims/{owner}/{denom}:
/incentive/claims/{owner}/{collateral-type}:
get:
summary: Get outstanding claims for the input owner and denom
summary: Get outstanding claims for the input owner and collateral type
tags:
- Incentive
produces:
@ -1299,11 +1384,11 @@
type: string
x-example: kava1ffv7nhd3z6sych2qpqkk03ec6hzkmufy0r2s4c
- in: path
name: denom
description: Collateral denom
name: type
description: Collateral type
required: true
type: string
x-example: bnb
x-example: bnb-a
responses:
200:
description: USDX Incentive Claims
@ -3457,6 +3542,9 @@
amount:
type: string
example: '555555'
CollateralType:
type: string
example: xrp-a
CoinCollateral:
type: object
properties:
@ -3904,6 +3992,9 @@
example: kava1q53rwutgpzx7szcrgzqguxyccjpzt9j4cyctn9
collateral:
$ref: '#/definitions/CoinCollateral'
type:
type: string
example: "xrp-a"
principal:
$ref: '#/definitions/CoinPrincipal'
accumulated_fees:
@ -3987,6 +4078,15 @@
bid_duration:
type: string
example: 600000000000
increment_surplus:
type: string
example: "0.05"
increment_debt:
type: string
example: "0.05"
increment_collateral:
type: string
example: "0.05"
AuctionResponse:
type: object
properties:

View File

@ -22,6 +22,7 @@ const (
flagType = "type"
flagDenom = "denom"
flagPhase = "phase"
flagOwner = "owner"
)
// GetQueryCmd returns the cli query commands for this module
@ -77,6 +78,7 @@ func QueryGetAuctionsCmd(queryRoute string, cdc *codec.Codec) *cobra.Command {
Long: strings.TrimSpace(`Query for all paginated auctions that match optional filters:
Example:
$ kvcli q auction auctions --type=(collateral|surplus|debt)
$ kvcli q auction auctions --owner=kava1hatdq32u5x4wnxrtv5wzjzmq49sxgjgsj0mffm
$ kvcli q auction auctions --denom=bnb
$ kvcli q auction auctions --phase=(forward|reverse)
$ kvcli q auction auctions --page=2 --limit=100
@ -84,6 +86,7 @@ $ kvcli q auction auctions --page=2 --limit=100
),
RunE: func(cmd *cobra.Command, args []string) error {
strType := viper.GetString(flagType)
strOwner := viper.GetString(flagOwner)
strDenom := viper.GetString(flagDenom)
strPhase := viper.GetString(flagPhase)
page := viper.GetInt(flags.FlagPage)
@ -91,11 +94,12 @@ $ kvcli q auction auctions --page=2 --limit=100
var (
auctionType string
auctionOwner sdk.AccAddress
auctionDenom string
auctionPhase string
)
params := types.NewQueryAllAuctionParams(page, limit, auctionType, auctionDenom, auctionPhase)
params := types.NewQueryAllAuctionParams(page, limit, auctionType, auctionDenom, auctionPhase, auctionOwner)
if len(strType) != 0 {
auctionType = strings.ToLower(strings.TrimSpace(strType))
@ -107,6 +111,18 @@ $ kvcli q auction auctions --page=2 --limit=100
params.Type = auctionType
}
if len(auctionOwner) != 0 {
if auctionType != types.CollateralAuctionType {
return fmt.Errorf("cannot apply owner flag to non-collateral auction type")
}
auctionOwnerStr := strings.ToLower(strings.TrimSpace(strOwner))
auctionOwner, err := sdk.AccAddressFromBech32(auctionOwnerStr)
if err != nil {
return fmt.Errorf("cannot parse address from auction owner %s", auctionOwnerStr)
}
params.Owner = auctionOwner
}
if len(strDenom) != 0 {
auctionDenom := strings.TrimSpace(strDenom)
err := sdk.ValidateDenom(auctionDenom)
@ -160,6 +176,7 @@ $ kvcli q auction auctions --page=2 --limit=100
cmd.Flags().Int(flags.FlagPage, 1, "pagination page of auctions to to query for")
cmd.Flags().Int(flags.FlagLimit, 100, "pagination limit of auctions to query for")
cmd.Flags().String(flagType, "", "(optional) filter by auction type, type: collateral, debt, surplus")
cmd.Flags().String(flagOwner, "", "(optional) filter by collateral auction owner")
cmd.Flags().String(flagDenom, "", "(optional) filter by auction denom")
cmd.Flags().String(flagPhase, "", "(optional) filter by collateral auction phase, phase: forward/reverse")

View File

@ -69,6 +69,7 @@ func queryAuctionsHandlerFn(cliCtx context.CLIContext) http.HandlerFunc {
}
var auctionType string
var auctionOwner sdk.AccAddress
var auctionDenom string
var auctionPhase string
@ -82,6 +83,17 @@ func queryAuctionsHandlerFn(cliCtx context.CLIContext) http.HandlerFunc {
}
}
if x := r.URL.Query().Get(RestOwner); len(x) != 0 {
if auctionType != types.CollateralAuctionType {
rest.WriteErrorResponse(w, http.StatusBadRequest, "cannot apply owner flag to non-collateral auction type")
}
auctionOwnerStr := strings.ToLower(strings.TrimSpace(x))
auctionOwner, err = sdk.AccAddressFromBech32(auctionOwnerStr)
if err != nil {
rest.WriteErrorResponse(w, http.StatusBadRequest, fmt.Sprintf("cannot parse address from auction owner %s", auctionOwnerStr))
}
}
if x := r.URL.Query().Get(RestDenom); len(x) != 0 {
auctionDenom = strings.TrimSpace(x)
err := sdk.ValidateDenom(auctionDenom)
@ -103,7 +115,7 @@ func queryAuctionsHandlerFn(cliCtx context.CLIContext) http.HandlerFunc {
}
}
params := types.NewQueryAllAuctionParams(page, limit, auctionType, auctionDenom, auctionPhase)
params := types.NewQueryAllAuctionParams(page, limit, auctionType, auctionDenom, auctionPhase, auctionOwner)
bz, err := cliCtx.Codec.MarshalJSON(params)
if err != nil {
rest.WriteErrorResponse(w, http.StatusBadRequest, err.Error())

View File

@ -12,6 +12,7 @@ import (
// nolint
const (
RestType = "type"
RestOwner = "owner"
RestDenom = "denom"
RestPhase = "phase"
)

View File

@ -1,12 +1,11 @@
package keeper
import (
abci "github.com/tendermint/tendermint/abci/types"
"github.com/cosmos/cosmos-sdk/client"
"github.com/cosmos/cosmos-sdk/codec"
sdk "github.com/cosmos/cosmos-sdk/types"
sdkerrors "github.com/cosmos/cosmos-sdk/types/errors"
abci "github.com/tendermint/tendermint/abci/types"
"github.com/kava-labs/kava/x/auction/types"
)
@ -93,13 +92,29 @@ func filterAuctions(ctx sdk.Context, auctions types.Auctions, params types.Query
filteredAuctions := make(types.Auctions, 0, len(auctions))
for _, auc := range auctions {
matchType, matchDenom, matchPhase := true, true, true
matchType, matchOwner, matchDenom, matchPhase := true, true, true, true
// match auction type (if supplied)
if len(params.Type) > 0 {
matchType = auc.GetType() == params.Type
}
// match auction owner (if supplied)
if len(params.Owner) > 0 {
if cAuc, ok := auc.(types.CollateralAuction); ok {
foundOwnerAddr := false
for _, addr := range cAuc.GetLotReturns().Addresses {
if addr.Equals(params.Owner) {
foundOwnerAddr = true
break
}
}
if !foundOwnerAddr {
matchOwner = false
}
}
}
// match auction denom (if supplied)
if len(params.Denom) > 0 {
matchDenom = auc.GetBid().Denom == params.Denom || auc.GetLot().Denom == params.Denom
@ -110,7 +125,7 @@ func filterAuctions(ctx sdk.Context, auctions types.Auctions, params types.Query
matchPhase = auc.GetPhase() == params.Phase
}
if matchType && matchDenom && matchPhase {
if matchType && matchOwner && matchDenom && matchPhase {
filteredAuctions = append(filteredAuctions, auc)
}
}

View File

@ -41,7 +41,7 @@ func (suite *QuerierTestSuite) SetupTest() {
tApp := app.NewTestApp()
ctx := tApp.NewContext(true, abci.Header{Height: 1, Time: tmtime.Now()})
_, addrs := app.GeneratePrivKeyAddressPairs(1)
_, addrs := app.GeneratePrivKeyAddressPairs(10)
buyer := addrs[0]
modName := cdp.LiquidatorMacc
@ -64,8 +64,16 @@ func (suite *QuerierTestSuite) SetupTest() {
// Populate with auctions
randSrc := rand.New(rand.NewSource(int64(1234)))
for j := 0; j < TestAuctionCount; j++ {
lotAmount := simulation.RandIntBetween(randSrc, 10, 100)
id, err := suite.keeper.StartSurplusAuction(suite.ctx, modName, c("token1", int64(lotAmount)), "token2")
var id uint64
var err error
lotAmount := int64(simulation.RandIntBetween(randSrc, 10, 100))
ownerAddrIndex := simulation.RandIntBetween(randSrc, 1, 9)
if ownerAddrIndex%2 == 0 {
id, err = suite.keeper.StartSurplusAuction(suite.ctx, modName, c("token1", lotAmount), "token2")
} else {
id, err = suite.keeper.StartCollateralAuction(suite.ctx, modName, c("token1", lotAmount), c("usdx", int64(20)),
[]sdk.AccAddress{addrs[ownerAddrIndex]}, []sdk.Int{sdk.NewInt(lotAmount)}, c("debt", int64(10)))
}
suite.NoError(err)
auc, found := suite.keeper.GetAuction(suite.ctx, id)
@ -108,7 +116,7 @@ func (suite *QuerierTestSuite) TestQueryAuctions() {
query := abci.RequestQuery{
Path: strings.Join([]string{custom, types.QuerierRoute, types.QueryGetAuctions}, "/"),
Data: types.ModuleCdc.MustMarshalJSON(
types.NewQueryAllAuctionParams(int(1), int(TestAuctionCount), "", "", ""),
types.NewQueryAllAuctionParams(int(1), int(TestAuctionCount), "", "", "", nil),
),
}

View File

@ -244,6 +244,11 @@ func (a CollateralAuction) GetPhase() string {
return ForwardAuctionPhase
}
// GetLotReturns returns a collateral auction's lot owners
func (a CollateralAuction) GetLotReturns() WeightedAddresses {
return a.LotReturns
}
// Validate validates the CollateralAuction fields values.
func (a CollateralAuction) Validate() error {
if !a.CorrespondingDebt.IsValid() {

View File

@ -1,5 +1,9 @@
package types
import (
sdk "github.com/cosmos/cosmos-sdk/types"
)
const (
// QueryGetAuction is the query path for querying one auction
QueryGetAuction = "auction"
@ -25,19 +29,21 @@ func NewQueryAuctionParams(id uint64) QueryAuctionParams {
// QueryAllAuctionParams is the params for an auctions query
type QueryAllAuctionParams struct {
Page int `json:"page" yaml:"page"`
Limit int `json:"limit" yaml:"limit"`
Type string `json:"type" yaml:"type"`
Denom string `json:"denom" yaml:"denom"`
Phase string `json:"phase" yaml:"phase"`
Page int `json:"page" yaml:"page"`
Limit int `json:"limit" yaml:"limit"`
Type string `json:"type" yaml:"type"`
Owner sdk.AccAddress `json:"owner" yaml:"owner"`
Denom string `json:"denom" yaml:"denom"`
Phase string `json:"phase" yaml:"phase"`
}
// NewQueryAllAuctionParams creates a new QueryAllAuctionParams
func NewQueryAllAuctionParams(page, limit int, aucType, aucDenom, aucPhase string) QueryAllAuctionParams {
func NewQueryAllAuctionParams(page, limit int, aucType, aucDenom, aucPhase string, aucOwner sdk.AccAddress) QueryAllAuctionParams {
return QueryAllAuctionParams{
Page: page,
Limit: limit,
Type: aucType,
Owner: aucOwner,
Denom: aucDenom,
Phase: aucPhase,
}

View File

@ -137,7 +137,9 @@ var (
DefaultDebtLot = types.DefaultDebtLot
DefaultPreviousDistributionTime = types.DefaultPreviousDistributionTime
DefaultSavingsDistributionFrequency = types.DefaultSavingsDistributionFrequency
DefaultSavingsRateDistributed = types.DefaultSavingsRateDistributed
MaxSortableDec = types.MaxSortableDec
SavingsRateDistributedKey = types.SavingsRateDistributedKey
)
type (

View File

@ -2,9 +2,11 @@ package cli
import (
"fmt"
"strconv"
"strings"
"github.com/spf13/cobra"
"github.com/spf13/viper"
"github.com/cosmos/cosmos-sdk/client/context"
"github.com/cosmos/cosmos-sdk/client/flags"
@ -16,6 +18,14 @@ import (
"github.com/kava-labs/kava/x/cdp/types"
)
// Query CDP flags
const (
flagCollateralType = "collateral-type"
flagOwner = "owner"
flagID = "id"
flagRatio = "ratio" // returns CDPs under the given collateralization ratio threshold
)
// GetQueryCmd returns the cli query commands for this module
func GetQueryCmd(queryRoute string, cdc *codec.Codec) *cobra.Command {
// Group nameservice queries under a subcommand
@ -26,11 +36,11 @@ func GetQueryCmd(queryRoute string, cdc *codec.Codec) *cobra.Command {
cdpQueryCmd.AddCommand(flags.GetCommands(
QueryCdpCmd(queryRoute, cdc),
QueryCdpsByCollateralTypeCmd(queryRoute, cdc),
QueryCdpsByCollateralTypeAndRatioCmd(queryRoute, cdc),
QueryGetCdpsCmd(queryRoute, cdc),
QueryCdpDepositsCmd(queryRoute, cdc),
QueryParamsCmd(queryRoute, cdc),
QueryGetAccounts(queryRoute, cdc),
QueryGetSavingsRateDistributed(queryRoute, cdc),
)...)
return cdpQueryCmd
@ -79,85 +89,103 @@ $ %s query %s cdp kava15qdefkmwswysgg4qxgqpqr35k3m49pkx2jdfnw atom-a
}
}
// QueryCdpsByCollateralTypeCmd returns the command handler for querying cdps for a collateral type
func QueryCdpsByCollateralTypeCmd(queryRoute string, cdc *codec.Codec) *cobra.Command {
return &cobra.Command{
Use: "cdps [collateral-type]",
Short: "query CDPs by collateral",
Long: strings.TrimSpace(
fmt.Sprintf(`List all CDPs collateralized with the specified asset.
// QueryGetCdpsCmd queries the cdps in the store
func QueryGetCdpsCmd(queryRoute string, cdc *codec.Codec) *cobra.Command {
cmd := &cobra.Command{
Use: "cdps",
Short: "query cdps with optional filters",
Long: strings.TrimSpace(`Query for all paginated cdps that match optional filters:
Example:
$ %s query %s cdps atom-a
`, version.ClientName, types.ModuleName)),
Args: cobra.ExactArgs(1),
$ kvcli q cdp cdps --collateral-type=bnb
$ kvcli q cdp cdps --owner=kava1hatdq32u5x4wnxrtv5wzjzmq49sxgjgsj0mffm
$ kvcli q cdp cdps --id=21
$ kvcli q cdp cdps --ratio=2.75
$ kvcli q cdp cdps --page=2 --limit=100
`,
),
RunE: func(cmd *cobra.Command, args []string) error {
cliCtx := context.NewCLIContext().WithCodec(cdc)
strCollateralType := viper.GetString(flagCollateralType)
strOwner := viper.GetString(flagOwner)
strID := viper.GetString(flagID)
strRatio := viper.GetString(flagRatio)
page := viper.GetInt(flags.FlagPage)
limit := viper.GetInt(flags.FlagLimit)
// Prepare params for querier
bz, err := cdc.MarshalJSON(types.QueryCdpsParams{CollateralType: args[0]})
var (
cdpCollateralType string
cdpOwner sdk.AccAddress
cdpID uint64
cdpRatio sdk.Dec
)
params := types.NewQueryCdpsParams(page, limit, cdpCollateralType, cdpOwner, cdpID, cdpRatio)
if len(strCollateralType) != 0 {
cdpCollateralType = strings.ToLower(strings.TrimSpace(strCollateralType))
params.CollateralType = cdpCollateralType
}
if len(strOwner) != 0 {
cdpOwner, err := sdk.AccAddressFromBech32(strings.ToLower(strings.TrimSpace(strOwner)))
if err != nil {
return fmt.Errorf("cannot parse address from cdp owner %s", strOwner)
}
params.Owner = cdpOwner
}
if len(strID) != 0 {
cdpID, err := strconv.ParseUint(strID, 10, 64)
if err != nil {
return fmt.Errorf("cannot parse cdp ID %s", strID)
}
params.ID = cdpID
}
params.Ratio = sdk.ZeroDec()
if len(strRatio) != 0 {
cdpRatio, err := sdk.NewDecFromStr(strRatio)
if err != nil {
return fmt.Errorf("cannot parse cdp ratio %s", strRatio)
}
params.Ratio = cdpRatio
} else {
// Set to sdk.Dec(0) so that if not specified in params it doesn't panic when unmarshaled
params.Ratio = sdk.ZeroDec()
}
bz, err := cdc.MarshalJSON(params)
if err != nil {
return err
}
cliCtx := context.NewCLIContext().WithCodec(cdc)
// Query
route := fmt.Sprintf("custom/%s/%s", queryRoute, types.QueryGetCdps)
res, _, err := cliCtx.QueryWithData(route, bz)
res, height, err := cliCtx.QueryWithData(fmt.Sprintf("custom/%s/%s", queryRoute, types.QueryGetCdps), bz)
if err != nil {
return err
}
// Decode and print results
var cdps types.AugmentedCDPs
cdc.MustUnmarshalJSON(res, &cdps)
return cliCtx.PrintOutput(cdps)
var matchingCDPs types.AugmentedCDPs
cdc.MustUnmarshalJSON(res, &matchingCDPs)
if len(matchingCDPs) == 0 {
return fmt.Errorf("No matching CDPs found")
}
cliCtx = cliCtx.WithHeight(height)
return cliCtx.PrintOutput(matchingCDPs) // nolint:errcheck
},
}
}
// QueryCdpsByCollateralTypeAndRatioCmd returns the command handler for querying cdps
// that are under the specified collateral ratio
func QueryCdpsByCollateralTypeAndRatioCmd(queryRoute string, cdc *codec.Codec) *cobra.Command {
return &cobra.Command{
Use: "cdps-by-ratio [collateral-type] [collateralization-ratio]",
Short: "get cdps under a collateralization ratio",
Long: strings.TrimSpace(
fmt.Sprintf(`List all CDPs under a specified collateralization ratio.
Collateralization ratio is: collateral * price / debt.
cmd.Flags().Int(flags.FlagPage, 1, "pagination page of CDPs to to query for")
cmd.Flags().Int(flags.FlagLimit, 100, "pagination limit of CDPs to query for")
cmd.Flags().String(flagCollateralType, "", "(optional) filter by CDP collateral type")
cmd.Flags().String(flagOwner, "", "(optional) filter by CDP owner")
cmd.Flags().String(flagID, "", "(optional) filter by CDP ID")
cmd.Flags().String(flagRatio, "", "(optional) filter by CDP collateralization ratio threshold")
Example:
$ %s query %s cdps-by-ratio atom-a 1.6
`, 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
ratio, err := sdk.NewDecFromStr(args[1])
if err != nil {
return err
}
bz, err := cdc.MarshalJSON(types.QueryCdpsByRatioParams{
CollateralType: args[0],
Ratio: ratio,
})
if err != nil {
return err
}
// Query
route := fmt.Sprintf("custom/%s/%s", queryRoute, types.QueryGetCdpsByCollateralization)
res, _, err := cliCtx.QueryWithData(route, bz)
if err != nil {
return err
}
// Decode and print results
var cdps types.AugmentedCDPs
cdc.MustUnmarshalJSON(res, &cdps)
return cliCtx.PrintOutput(cdps)
},
}
return cmd
}
// QueryCdpDepositsCmd returns the command handler for querying the deposits of a particular cdp
@ -208,7 +236,7 @@ func QueryParamsCmd(queryRoute string, cdc *codec.Codec) *cobra.Command {
return &cobra.Command{
Use: "params",
Short: "get the cdp module parameters",
Long: "Get the current global cdp module parameters.",
Long: "get the current global cdp module parameters.",
Args: cobra.NoArgs,
RunE: func(cmd *cobra.Command, args []string) error {
cliCtx := context.NewCLIContext().WithCodec(cdc)
@ -228,11 +256,12 @@ func QueryParamsCmd(queryRoute string, cdc *codec.Codec) *cobra.Command {
}
}
// QueryGetAccounts queries CDP module accounts
func QueryGetAccounts(queryRoute string, cdc *codec.Codec) *cobra.Command {
return &cobra.Command{
Use: "accounts",
Short: "Get module accounts",
Long: "Get cdp module account addresses",
Short: "get module accounts",
Long: "get cdp module account addresses",
Args: cobra.NoArgs,
RunE: func(cmd *cobra.Command, args []string) error {
cliCtx := context.NewCLIContext().WithCodec(cdc)
@ -253,3 +282,30 @@ func QueryGetAccounts(queryRoute string, cdc *codec.Codec) *cobra.Command {
},
}
}
// QueryGetSavingsRateDistributed queries the total amount of savings rate distributed in USDX
func QueryGetSavingsRateDistributed(queryRoute string, cdc *codec.Codec) *cobra.Command {
return &cobra.Command{
Use: "savings-rate-dist",
Short: "get total amount of savings rate distributed in USDX",
Long: "get total amount of savings rate distributed",
Args: cobra.NoArgs,
RunE: func(cmd *cobra.Command, args []string) error {
cliCtx := context.NewCLIContext().WithCodec(cdc)
// Query
res, height, err := cliCtx.QueryWithData(fmt.Sprintf("custom/%s/%s", queryRoute, types.QueryGetSavingsRateDistributed), nil)
if err != nil {
return err
}
cliCtx = cliCtx.WithHeight(height)
// Decode and print results
var out sdk.Int
if err := cdc.UnmarshalJSON(res, &out); err != nil {
return fmt.Errorf("failed to unmarshal sdk.Int: %w", err)
}
return cliCtx.PrintOutput(out)
},
}
}

View File

@ -3,6 +3,8 @@ package rest
import (
"fmt"
"net/http"
"strconv"
"strings"
"github.com/cosmos/cosmos-sdk/client/context"
sdk "github.com/cosmos/cosmos-sdk/types"
@ -16,9 +18,11 @@ import (
func registerQueryRoutes(cliCtx context.CLIContext, r *mux.Router) {
r.HandleFunc("/cdp/accounts", getAccountsHandlerFn(cliCtx)).Methods("GET")
r.HandleFunc("/cdp/parameters", getParamsHandlerFn(cliCtx)).Methods("GET")
r.HandleFunc("/cdp/savingsRateDist", getSavingsRateDistributedHandler(cliCtx)).Methods("GET")
r.HandleFunc(fmt.Sprintf("/cdp/cdps/cdp/{%s}/{%s}", types.RestOwner, types.RestCollateralType), queryCdpHandlerFn(cliCtx)).Methods("GET")
r.HandleFunc(fmt.Sprintf("/cdp/cdps/collateralType/{%s}", types.RestCollateralType), queryCdpsHandlerFn(cliCtx)).Methods("GET")
r.HandleFunc(fmt.Sprintf("/cdp/cdps/ratio/{%s}/{%s}", types.RestCollateralType, types.RestRatio), queryCdpsByRatioHandlerFn(cliCtx)).Methods("GET")
r.HandleFunc(fmt.Sprintf("/cdp/cdps"), queryCdpsHandlerFn(cliCtx)).Methods("GET")
r.HandleFunc(fmt.Sprintf("/cdp/cdps/collateralType/{%s}", types.RestCollateralType), queryCdpsByCollateralTypeHandlerFn(cliCtx)).Methods("GET") // legacy
r.HandleFunc(fmt.Sprintf("/cdp/cdps/ratio/{%s}/{%s}", types.RestCollateralType, types.RestRatio), queryCdpsByRatioHandlerFn(cliCtx)).Methods("GET") // legacy
r.HandleFunc(fmt.Sprintf("/cdp/cdps/cdp/deposits/{%s}/{%s}", types.RestOwner, types.RestCollateralType), queryCdpDepositsHandlerFn(cliCtx)).Methods("GET")
}
@ -58,7 +62,7 @@ func queryCdpHandlerFn(cliCtx context.CLIContext) http.HandlerFunc {
}
}
func queryCdpsHandlerFn(cliCtx context.CLIContext) http.HandlerFunc {
func queryCdpsByCollateralTypeHandlerFn(cliCtx context.CLIContext) http.HandlerFunc {
return func(w http.ResponseWriter, r *http.Request) {
cliCtx, ok := rest.ParseQueryHeightOrReturnBadRequest(w, cliCtx, r)
if !ok {
@ -68,7 +72,7 @@ func queryCdpsHandlerFn(cliCtx context.CLIContext) http.HandlerFunc {
vars := mux.Vars(r)
collateralType := vars[types.RestCollateralType]
params := types.NewQueryCdpsParams(collateralType)
params := types.NewQueryCdpsByCollateralTypeParams(collateralType)
bz, err := cliCtx.Codec.MarshalJSON(params)
if err != nil {
@ -76,7 +80,7 @@ func queryCdpsHandlerFn(cliCtx context.CLIContext) http.HandlerFunc {
return
}
res, height, err := cliCtx.QueryWithData(fmt.Sprintf("custom/cdp/%s", types.QueryGetCdps), bz)
res, height, err := cliCtx.QueryWithData(fmt.Sprintf("custom/cdp/%s", types.QueryGetCdpsByCollateralType), bz)
if err != nil {
rest.WriteErrorResponse(w, http.StatusNotFound, err.Error())
return
@ -193,3 +197,91 @@ func getAccountsHandlerFn(cliCtx context.CLIContext) http.HandlerFunc {
rest.PostProcessResponse(w, cliCtx, res)
}
}
func getSavingsRateDistributedHandler(cliCtx context.CLIContext) http.HandlerFunc {
return func(w http.ResponseWriter, r *http.Request) {
cliCtx, ok := rest.ParseQueryHeightOrReturnBadRequest(w, cliCtx, r)
if !ok {
return
}
res, height, err := cliCtx.QueryWithData(fmt.Sprintf("custom/cdp/%s", types.QueryGetSavingsRateDistributed), nil)
cliCtx = cliCtx.WithHeight(height)
if err != nil {
rest.WriteErrorResponse(w, http.StatusInternalServerError, err.Error())
return
}
rest.PostProcessResponse(w, cliCtx, res)
}
}
func queryCdpsHandlerFn(cliCtx context.CLIContext) http.HandlerFunc {
return func(w http.ResponseWriter, r *http.Request) {
_, page, limit, err := rest.ParseHTTPArgsWithLimit(r, 0)
if err != nil {
rest.WriteErrorResponse(w, http.StatusBadRequest, err.Error())
return
}
// Parse the query height
cliCtx, ok := rest.ParseQueryHeightOrReturnBadRequest(w, cliCtx, r)
if !ok {
return
}
var cdpCollateralType string
var cdpOwner sdk.AccAddress
var cdpID uint64
var cdpRatio sdk.Dec
if x := r.URL.Query().Get(RestCollateralType); len(x) != 0 {
cdpCollateralType = strings.TrimSpace(x)
}
if x := r.URL.Query().Get(RestOwner); len(x) != 0 {
cdpOwnerStr := strings.ToLower(strings.TrimSpace(x))
cdpOwner, err = sdk.AccAddressFromBech32(cdpOwnerStr)
if err != nil {
rest.WriteErrorResponse(w, http.StatusBadRequest, fmt.Sprintf("cannot parse address from cdp owner %s", cdpOwnerStr))
return
}
}
if x := r.URL.Query().Get(RestID); len(x) != 0 {
cdpID, err = strconv.ParseUint(strings.TrimSpace(x), 10, 64)
if err != nil {
rest.WriteErrorResponse(w, http.StatusBadRequest, err.Error())
return
}
}
if x := r.URL.Query().Get(RestRatio); len(x) != 0 {
cdpRatio, err = sdk.NewDecFromStr(strings.TrimSpace(x))
if err != nil {
rest.WriteErrorResponse(w, http.StatusBadRequest, err.Error())
return
}
} else {
// Set to sdk.Dec(0) so that if not specified in params it doesn't panic when unmarshaled
cdpRatio = sdk.ZeroDec()
}
params := types.NewQueryCdpsParams(page, limit, cdpCollateralType, cdpOwner, cdpID, cdpRatio)
bz, err := cliCtx.Codec.MarshalJSON(params)
if err != nil {
rest.WriteErrorResponse(w, http.StatusBadRequest, err.Error())
return
}
route := fmt.Sprintf("custom/%s/%s", types.ModuleName, types.QueryGetCdps)
res, height, err := cliCtx.QueryWithData(route, bz)
cliCtx = cliCtx.WithHeight(height)
if err != nil {
rest.WriteErrorResponse(w, http.StatusInternalServerError, err.Error())
return
}
rest.PostProcessResponse(w, cliCtx, res)
}
}

View File

@ -8,6 +8,15 @@ import (
"github.com/cosmos/cosmos-sdk/types/rest"
)
// REST Variable names
// nolint
const (
RestOwner = "owner"
RestCollateralType = "collateral-type"
RestID = "id"
RestRatio = "ratio"
)
// RegisterRoutes - Central function to define routes that get registered by the main application
func RegisterRoutes(cliCtx context.CLIContext, r *mux.Router) {
registerQueryRoutes(cliCtx, r)

View File

@ -20,7 +20,6 @@ func registerTxRoutes(cliCtx context.CLIContext, r *mux.Router) {
r.HandleFunc("/cdp/{owner}/{collateralType}/withdraw", postWithdrawHandlerFn(cliCtx)).Methods("POST")
r.HandleFunc("/cdp/{owner}/{collateralType}/draw", postDrawHandlerFn(cliCtx)).Methods("POST")
r.HandleFunc("/cdp/{owner}/{collateralType}/repay", postRepayHandlerFn(cliCtx)).Methods("POST")
}
func postCdpHandlerFn(cliCtx context.CLIContext) http.HandlerFunc {

View File

@ -88,6 +88,8 @@ func InitGenesis(ctx sdk.Context, k Keeper, pk types.PricefeedKeeper, sk types.S
for _, d := range gs.Deposits {
k.SetDeposit(ctx, d)
}
k.SetSavingsRateDistributed(ctx, gs.SavingsRateDistributed)
}
// ExportGenesis export genesis state for cdp module
@ -108,11 +110,12 @@ func ExportGenesis(ctx sdk.Context, k Keeper) GenesisState {
cdpID := k.GetNextCdpID(ctx)
debtDenom := k.GetDebtDenom(ctx)
govDenom := k.GetGovDenom(ctx)
savingsRateDist := k.GetSavingsRateDistributed(ctx)
previousDistributionTime, found := k.GetPreviousSavingsDistribution(ctx)
if !found {
previousDistributionTime = DefaultPreviousDistributionTime
}
return NewGenesisState(params, cdps, deposits, cdpID, debtDenom, govDenom, previousDistributionTime)
return NewGenesisState(params, cdps, deposits, cdpID, debtDenom, govDenom, previousDistributionTime, savingsRateDist)
}

View File

@ -22,13 +22,14 @@ type GenesisTestSuite struct {
func (suite *GenesisTestSuite) TestInvalidGenState() {
type args struct {
params cdp.Params
cdps cdp.CDPs
deposits cdp.Deposits
startingID uint64
debtDenom string
govDenom string
prevDistTime time.Time
params cdp.Params
cdps cdp.CDPs
deposits cdp.Deposits
startingID uint64
debtDenom string
govDenom string
prevDistTime time.Time
savingsRateDist sdk.Int
}
type errArgs struct {
expectPass bool
@ -47,12 +48,13 @@ func (suite *GenesisTestSuite) TestInvalidGenState() {
{
name: "empty debt denom",
args: args{
params: cdp.DefaultParams(),
cdps: cdp.CDPs{},
deposits: cdp.Deposits{},
debtDenom: "",
govDenom: cdp.DefaultGovDenom,
prevDistTime: cdp.DefaultPreviousDistributionTime,
params: cdp.DefaultParams(),
cdps: cdp.CDPs{},
deposits: cdp.Deposits{},
debtDenom: "",
govDenom: cdp.DefaultGovDenom,
prevDistTime: cdp.DefaultPreviousDistributionTime,
savingsRateDist: cdp.DefaultSavingsRateDistributed,
},
errArgs: errArgs{
expectPass: false,
@ -62,12 +64,13 @@ func (suite *GenesisTestSuite) TestInvalidGenState() {
{
name: "empty gov denom",
args: args{
params: cdp.DefaultParams(),
cdps: cdp.CDPs{},
deposits: cdp.Deposits{},
debtDenom: cdp.DefaultDebtDenom,
govDenom: "",
prevDistTime: cdp.DefaultPreviousDistributionTime,
params: cdp.DefaultParams(),
cdps: cdp.CDPs{},
deposits: cdp.Deposits{},
debtDenom: cdp.DefaultDebtDenom,
govDenom: "",
prevDistTime: cdp.DefaultPreviousDistributionTime,
savingsRateDist: cdp.DefaultSavingsRateDistributed,
},
errArgs: errArgs{
expectPass: false,
@ -77,27 +80,46 @@ func (suite *GenesisTestSuite) TestInvalidGenState() {
{
name: "empty distribution time",
args: args{
params: cdp.DefaultParams(),
cdps: cdp.CDPs{},
deposits: cdp.Deposits{},
debtDenom: cdp.DefaultDebtDenom,
govDenom: cdp.DefaultGovDenom,
prevDistTime: time.Time{},
params: cdp.DefaultParams(),
cdps: cdp.CDPs{},
deposits: cdp.Deposits{},
debtDenom: cdp.DefaultDebtDenom,
govDenom: cdp.DefaultGovDenom,
prevDistTime: time.Time{},
savingsRateDist: cdp.DefaultSavingsRateDistributed,
},
errArgs: errArgs{
expectPass: false,
contains: "previous distribution time not set",
},
},
{
name: "negative savings rate distributed",
args: args{
params: cdp.DefaultParams(),
cdps: cdp.CDPs{},
deposits: cdp.Deposits{},
debtDenom: cdp.DefaultDebtDenom,
govDenom: cdp.DefaultGovDenom,
prevDistTime: cdp.DefaultPreviousDistributionTime,
savingsRateDist: sdk.NewInt(-100),
},
errArgs: errArgs{
expectPass: false,
contains: "savings rate distributed should not be negative",
},
},
}
for _, tc := range testCases {
suite.Run(tc.name, func() {
gs := cdp.NewGenesisState(tc.args.params, tc.args.cdps, tc.args.deposits, tc.args.startingID, tc.args.debtDenom, tc.args.govDenom, tc.args.prevDistTime)
gs := cdp.NewGenesisState(tc.args.params, tc.args.cdps, tc.args.deposits, tc.args.startingID,
tc.args.debtDenom, tc.args.govDenom, tc.args.prevDistTime, tc.args.savingsRateDist)
err := gs.Validate()
if tc.errArgs.expectPass {
suite.Require().NoError(err)
} else {
suite.Require().Error(err)
suite.T().Log(err)
suite.Require().True(strings.Contains(err.Error(), tc.errArgs.contains))
}
})

View File

@ -110,3 +110,23 @@ func (k Keeper) IterateCdpsByCollateralRatio(ctx sdk.Context, collateralType str
}
}
// SetSavingsRateDistributed sets the SavingsRateDistributed in the store
func (k Keeper) SetSavingsRateDistributed(ctx sdk.Context, totalDistributed sdk.Int) {
store := prefix.NewStore(ctx.KVStore(k.key), types.SavingsRateDistributedKey)
bz := k.cdc.MustMarshalBinaryLengthPrefixed(totalDistributed)
store.Set([]byte{}, bz)
}
// GetSavingsRateDistributed gets the SavingsRateDistributed from the store
func (k Keeper) GetSavingsRateDistributed(ctx sdk.Context) sdk.Int {
savingsRateDistributed := sdk.ZeroInt()
store := prefix.NewStore(ctx.KVStore(k.key), types.SavingsRateDistributedKey)
bz := store.Get([]byte{})
if bz == nil {
return savingsRateDistributed
}
k.cdc.MustUnmarshalBinaryLengthPrefixed(bz, &savingsRateDistributed)
return savingsRateDistributed
}

View File

@ -0,0 +1,48 @@
package keeper_test
import (
sdk "github.com/cosmos/cosmos-sdk/types"
"github.com/stretchr/testify/suite"
abci "github.com/tendermint/tendermint/abci/types"
tmtime "github.com/tendermint/tendermint/types/time"
"github.com/kava-labs/kava/app"
"github.com/kava-labs/kava/x/cdp/keeper"
)
type KeeperTestSuite struct {
suite.Suite
keeper keeper.Keeper
app app.TestApp
ctx sdk.Context
}
func (suite *KeeperTestSuite) SetupTest() {
config := sdk.GetConfig()
app.SetBech32AddressPrefixes(config)
suite.ResetChain()
return
}
func (suite *KeeperTestSuite) ResetChain() {
tApp := app.NewTestApp()
ctx := tApp.NewContext(true, abci.Header{Height: 1, Time: tmtime.Now()})
keeper := tApp.GetCDPKeeper()
suite.app = tApp
suite.ctx = ctx
suite.keeper = keeper
}
func (suite *KeeperTestSuite) TestGetSetSavingsRateDistributed() {
suite.ResetChain()
// Set savings rate distributed value
savingsRateDist := sdk.NewInt(555000555000)
suite.keeper.SetSavingsRateDistributed(suite.ctx, savingsRateDist)
// Check store's savings rate distributed value
s := suite.keeper.GetSavingsRateDistributed(suite.ctx)
suite.Equal(savingsRateDist, s)
}

View File

@ -31,6 +31,16 @@ func (k Keeper) GetCollateral(ctx sdk.Context, collateralType string) (types.Col
return types.CollateralParam{}, false
}
// GetCollateralTypes returns an array of collateral types
func (k Keeper) GetCollateralTypes(ctx sdk.Context) []string {
params := k.GetParams(ctx)
var denoms []string
for _, cp := range params.CollateralParams {
denoms = append(denoms, cp.Type)
}
return denoms
}
// GetDebtParam returns the debt param with matching denom
func (k Keeper) GetDebtParam(ctx sdk.Context, denom string) (types.DebtParam, bool) {
dp := k.GetParams(ctx).DebtParam

View File

@ -3,6 +3,7 @@ package keeper
import (
abci "github.com/tendermint/tendermint/abci/types"
"github.com/cosmos/cosmos-sdk/client"
"github.com/cosmos/cosmos-sdk/codec"
sdk "github.com/cosmos/cosmos-sdk/types"
sdkerrors "github.com/cosmos/cosmos-sdk/types/errors"
@ -18,15 +19,19 @@ func NewQuerier(keeper Keeper) sdk.Querier {
case types.QueryGetCdp:
return queryGetCdp(ctx, req, keeper)
case types.QueryGetCdps:
return queryGetCdps(ctx, req, keeper)
case types.QueryGetCdpDeposits:
return queryGetDeposits(ctx, req, keeper)
case types.QueryGetCdpsByCollateralType: // legacy, maintained for REST API
return queryGetCdpsByCollateralType(ctx, req, keeper)
case types.QueryGetCdpsByCollateralization:
case types.QueryGetCdpsByCollateralization: // legacy, maintained for REST API
return queryGetCdpsByRatio(ctx, req, keeper)
case types.QueryGetParams:
return queryGetParams(ctx, req, keeper)
case types.QueryGetCdpDeposits:
return queryGetDeposits(ctx, req, keeper)
case types.QueryGetAccounts:
return queryGetAccounts(ctx, req, keeper)
case types.QueryGetSavingsRateDistributed:
return queryGetSavingsRateDistributed(ctx, req, keeper)
default:
return nil, sdkerrors.Wrapf(sdkerrors.ErrUnknownRequest, "unknown %s query endpoint %s", types.ModuleName, path[0])
}
@ -43,7 +48,7 @@ func queryGetCdp(ctx sdk.Context, req abci.RequestQuery, keeper Keeper) ([]byte,
_, valid := keeper.GetCollateralTypePrefix(ctx, requestParams.CollateralType)
if !valid {
return nil, sdkerrors.Wrap(types.ErrCollateralNotSupported, requestParams.CollateralType)
return nil, sdkerrors.Wrap(types.ErrInvalidCollateral, requestParams.CollateralType)
}
cdp, found := keeper.GetCdpByOwnerAndCollateralType(ctx, requestParams.Owner, requestParams.CollateralType)
@ -71,7 +76,7 @@ func queryGetDeposits(ctx sdk.Context, req abci.RequestQuery, keeper Keeper) ([]
_, valid := keeper.GetCollateralTypePrefix(ctx, requestParams.CollateralType)
if !valid {
return nil, sdkerrors.Wrap(types.ErrCollateralNotSupported, requestParams.CollateralType)
return nil, sdkerrors.Wrap(types.ErrInvalidCollateral, requestParams.CollateralType)
}
cdp, found := keeper.GetCdpByOwnerAndCollateralType(ctx, requestParams.Owner, requestParams.CollateralType)
@ -98,7 +103,7 @@ func queryGetCdpsByRatio(ctx sdk.Context, req abci.RequestQuery, keeper Keeper)
}
_, valid := keeper.GetCollateralTypePrefix(ctx, requestParams.CollateralType)
if !valid {
return nil, sdkerrors.Wrap(types.ErrCollateralNotSupported, requestParams.CollateralType)
return nil, sdkerrors.Wrap(types.ErrInvalidCollateral, requestParams.CollateralType)
}
ratio, err := keeper.CalculateCollateralizationRatioFromAbsoluteRatio(ctx, requestParams.CollateralType, requestParams.Ratio, "liquidation")
@ -120,16 +125,16 @@ func queryGetCdpsByRatio(ctx sdk.Context, req abci.RequestQuery, keeper Keeper)
return bz, nil
}
// query all cdps with matching collateral denom
// query all cdps with matching collateral type
func queryGetCdpsByCollateralType(ctx sdk.Context, req abci.RequestQuery, keeper Keeper) ([]byte, error) {
var requestParams types.QueryCdpsParams
var requestParams types.QueryCdpsByCollateralTypeParams
err := types.ModuleCdc.UnmarshalJSON(req.Data, &requestParams)
if err != nil {
return nil, sdkerrors.Wrap(sdkerrors.ErrJSONUnmarshal, err.Error())
}
_, valid := keeper.GetCollateralTypePrefix(ctx, requestParams.CollateralType)
if !valid {
return nil, sdkerrors.Wrap(types.ErrCollateralNotSupported, requestParams.CollateralType)
return nil, sdkerrors.Wrap(types.ErrInvalidCollateral, requestParams.CollateralType)
}
cdps := keeper.GetAllCdpsByCollateralType(ctx, requestParams.CollateralType)
@ -178,3 +183,175 @@ func queryGetAccounts(ctx sdk.Context, req abci.RequestQuery, keeper Keeper) ([]
}
return bz, nil
}
// query get savings rate distributed in the cdp store
func queryGetSavingsRateDistributed(ctx sdk.Context, req abci.RequestQuery, keeper Keeper) ([]byte, error) {
// Get savings rate distributed
savingsRateDist := keeper.GetSavingsRateDistributed(ctx)
// Encode results
bz, err := codec.MarshalJSONIndent(types.ModuleCdc, savingsRateDist)
if err != nil {
return nil, sdkerrors.Wrap(sdkerrors.ErrJSONMarshal, err.Error())
}
return bz, nil
}
// query cdps in store and filter by request params
func queryGetCdps(ctx sdk.Context, req abci.RequestQuery, keeper Keeper) ([]byte, error) {
var params types.QueryCdpsParams
err := types.ModuleCdc.UnmarshalJSON(req.Data, &params)
if err != nil {
return nil, sdkerrors.Wrap(sdkerrors.ErrJSONUnmarshal, err.Error())
}
// Filter CDPs
filteredCDPs := FilterCDPs(ctx, keeper, params)
bz, err := codec.MarshalJSONIndent(keeper.cdc, filteredCDPs)
if err != nil {
return nil, sdkerrors.Wrap(sdkerrors.ErrJSONMarshal, err.Error())
}
return bz, nil
}
// FilterCDPs queries the store for all CDPs that match query params
func FilterCDPs(ctx sdk.Context, k Keeper, params types.QueryCdpsParams) types.AugmentedCDPs {
var matchCollateralType, matchOwner, matchID, matchRatio types.CDPs
// match cdp owner (if supplied)
if len(params.Owner) > 0 {
denoms := k.GetCollateralTypes(ctx)
for _, denom := range denoms {
cdp, found := k.GetCdpByOwnerAndCollateralType(ctx, params.Owner, denom)
if found {
matchOwner = append(matchOwner, cdp)
}
}
}
// match cdp collateral denom (if supplied)
if len(params.CollateralType) > 0 {
// if owner is specified only iterate over already matched cdps for efficiency
if len(params.Owner) > 0 {
for _, cdp := range matchOwner {
if cdp.Type == params.CollateralType {
matchCollateralType = append(matchCollateralType, cdp)
}
}
} else {
matchCollateralType = k.GetAllCdpsByCollateralType(ctx, params.CollateralType)
}
}
// match cdp ID (if supplied)
if params.ID != 0 {
denoms := k.GetCollateralTypes(ctx)
for _, denom := range denoms {
cdp, found := k.GetCDP(ctx, denom, params.ID)
if found {
matchID = append(matchID, cdp)
}
}
}
// match cdp ratio (if supplied)
if params.Ratio.GT(sdk.ZeroDec()) {
denoms := k.GetCollateralTypes(ctx)
for _, denom := range denoms {
ratio, err := k.CalculateCollateralizationRatioFromAbsoluteRatio(ctx, denom, params.Ratio, "liquidation")
if err != nil {
continue
}
cdpsUnderRatio := k.GetAllCdpsByCollateralTypeAndRatio(ctx, denom, ratio)
matchRatio = append(matchRatio, cdpsUnderRatio...)
}
}
var commonCDPs types.CDPs
// If no params specified, fetch all CDPs
if len(params.CollateralType) == 0 && len(params.Owner) == 0 && params.ID == 0 && params.Ratio.Equal(sdk.ZeroDec()) {
commonCDPs = k.GetAllCdps(ctx)
}
// Find the intersection of any matched CDPs
if len(params.CollateralType) > 0 {
if len(matchCollateralType) > 0 {
commonCDPs = matchCollateralType
} else {
return types.AugmentedCDPs{}
}
}
if len(params.Owner) > 0 {
if len(matchCollateralType) > 0 {
if len(commonCDPs) > 0 {
commonCDPs = FindIntersection(commonCDPs, matchOwner)
} else {
commonCDPs = matchOwner
}
} else {
commonCDPs = matchOwner
}
}
if params.ID != 0 {
if len(matchID) > 0 {
if len(commonCDPs) > 0 {
commonCDPs = FindIntersection(commonCDPs, matchID)
} else {
commonCDPs = matchID
}
} else {
return types.AugmentedCDPs{}
}
}
if params.Ratio.GT(sdk.ZeroDec()) {
if len(matchRatio) > 0 {
if len(commonCDPs) > 0 {
commonCDPs = FindIntersection(commonCDPs, matchRatio)
} else {
commonCDPs = matchRatio
}
} else {
return types.AugmentedCDPs{}
}
}
// Load augmented CDPs
var augmentedCDPs types.AugmentedCDPs
for _, cdp := range commonCDPs {
augmentedCDP := k.LoadAugmentedCDP(ctx, cdp)
augmentedCDPs = append(augmentedCDPs, augmentedCDP)
}
// Apply page and limit params
var paginatedCDPs types.AugmentedCDPs
start, end := client.Paginate(len(augmentedCDPs), params.Page, params.Limit, 100)
if start < 0 || end < 0 {
paginatedCDPs = types.AugmentedCDPs{}
} else {
paginatedCDPs = augmentedCDPs[start:end]
}
return paginatedCDPs
}
// FindIntersection finds the intersection of two CDP arrays in linear time complexity O(n + n)
func FindIntersection(x types.CDPs, y types.CDPs) types.CDPs {
cdpSet := make(types.CDPs, 0)
cdpMap := make(map[uint64]bool)
for i := 0; i < len(x); i++ {
cdp := x[i]
cdpMap[cdp.ID] = true
}
for i := 0; i < len(y); i++ {
cdp := y[i]
if _, found := cdpMap[cdp.ID]; found {
cdpSet = append(cdpSet, cdp)
}
}
return cdpSet
}

View File

@ -116,7 +116,7 @@ func (suite *QuerierTestSuite) TestQueryCdp() {
ctx := suite.ctx.WithIsCheckTx(false)
query := abci.RequestQuery{
Path: strings.Join([]string{custom, types.QuerierRoute, types.QueryGetCdp}, "/"),
Data: types.ModuleCdc.MustMarshalJSON(types.NewQueryCdpParams(suite.cdps[0].Owner, suite.cdps[0].Collateral.Denom+"-a")),
Data: types.ModuleCdc.MustMarshalJSON(types.NewQueryCdpParams(suite.cdps[0].Owner, suite.cdps[0].Type)),
}
bz, err := suite.querier(ctx, []string{types.QueryGetCdp}, query)
suite.Nil(err)
@ -156,10 +156,10 @@ func (suite *QuerierTestSuite) TestQueryCdp() {
func (suite *QuerierTestSuite) TestQueryCdpsByCollateralType() {
ctx := suite.ctx.WithIsCheckTx(false)
query := abci.RequestQuery{
Path: strings.Join([]string{custom, types.QuerierRoute, types.QueryGetCdps}, "/"),
Data: types.ModuleCdc.MustMarshalJSON(types.NewQueryCdpsParams(suite.cdps[0].Collateral.Denom + "-a")),
Path: strings.Join([]string{custom, types.QuerierRoute, types.QueryGetCdpsByCollateralType}, "/"),
Data: types.ModuleCdc.MustMarshalJSON(types.NewQueryCdpsByCollateralTypeParams(suite.cdps[0].Type)),
}
bz, err := suite.querier(ctx, []string{types.QueryGetCdps}, query)
bz, err := suite.querier(ctx, []string{types.QueryGetCdpsByCollateralType}, query)
suite.Nil(err)
suite.NotNil(bz)
@ -168,10 +168,10 @@ func (suite *QuerierTestSuite) TestQueryCdpsByCollateralType() {
suite.Equal(50, len(c))
query = abci.RequestQuery{
Path: strings.Join([]string{custom, types.QuerierRoute, types.QueryGetCdps}, "/"),
Data: types.ModuleCdc.MustMarshalJSON(types.NewQueryCdpsParams("lol-a")),
Path: strings.Join([]string{custom, types.QuerierRoute, types.QueryGetCdpsByCollateralType}, "/"),
Data: types.ModuleCdc.MustMarshalJSON(types.NewQueryCdpsByCollateralTypeParams("lol-a")),
}
_, err = suite.querier(ctx, []string{types.QueryGetCdps}, query)
_, err = suite.querier(ctx, []string{types.QueryGetCdpsByCollateralType}, query)
suite.Error(err)
}
@ -265,7 +265,7 @@ 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.Denom+"-a")),
Data: types.ModuleCdc.MustMarshalJSON(types.NewQueryCdpDeposits(suite.cdps[0].Owner, suite.cdps[0].Type)),
}
bz, err := suite.querier(ctx, []string{types.QueryGetCdpDeposits}, query)
@ -303,6 +303,89 @@ func (suite *QuerierTestSuite) TestQueryAccounts() {
suite.Require().True(findByName("savings"))
}
func (suite *QuerierTestSuite) TestQuerySavingsRateDistributed() {
ctx := suite.ctx.WithIsCheckTx(false)
bz, err := suite.querier(ctx, []string{types.QueryGetSavingsRateDistributed}, abci.RequestQuery{})
suite.Nil(err)
suite.NotNil(bz)
var distAmount sdk.Int
suite.Nil(types.ModuleCdc.UnmarshalJSON(bz, &distAmount))
suite.True(sdk.ZeroInt().Equal(distAmount))
}
func (suite *QuerierTestSuite) TestFindIntersection() {
a := types.CDPs{suite.cdps[0], suite.cdps[1], suite.cdps[2], suite.cdps[3], suite.cdps[4]}
b := types.CDPs{suite.cdps[3], suite.cdps[4], suite.cdps[5], suite.cdps[6], suite.cdps[7]}
expectedIntersection1 := types.CDPs{suite.cdps[3], suite.cdps[4]}
intersection1 := keeper.FindIntersection(a, b)
suite.Require().Equal(intersection1, expectedIntersection1)
c := types.CDPs{suite.cdps[0], suite.cdps[1], suite.cdps[2], suite.cdps[3], suite.cdps[4]}
d := types.CDPs{suite.cdps[5], suite.cdps[6], suite.cdps[7], suite.cdps[8], suite.cdps[9]}
expectedIntersection2 := types.CDPs{}
intersection2 := keeper.FindIntersection(c, d)
suite.Require().Equal(intersection2, expectedIntersection2)
e := types.CDPs{suite.cdps[0]}
f := types.CDPs{}
expectedIntersection3 := types.CDPs{}
intersection3 := keeper.FindIntersection(e, f)
suite.Require().Equal(intersection3, expectedIntersection3)
}
func (suite *QuerierTestSuite) TestFilterCDPs() {
paramsType := types.NewQueryCdpsParams(1, 100, "btc-a", sdk.AccAddress{}, 0, sdk.ZeroDec())
filteredCDPs1 := keeper.FilterCDPs(suite.ctx, suite.keeper, paramsType)
suite.Require().Equal(len(filteredCDPs1), 50)
paramsOwner := types.NewQueryCdpsParams(1, 100, "", suite.cdps[10].Owner, 0, sdk.ZeroDec())
filteredCDPs2 := keeper.FilterCDPs(suite.ctx, suite.keeper, paramsOwner)
suite.Require().Equal(len(filteredCDPs2), 1)
suite.Require().Equal(filteredCDPs2[0].Owner, suite.cdps[10].Owner)
paramsID := types.NewQueryCdpsParams(1, 100, "", sdk.AccAddress{}, 68, sdk.ZeroDec())
filteredCDPs3 := keeper.FilterCDPs(suite.ctx, suite.keeper, paramsID)
suite.Require().Equal(len(filteredCDPs3), 1)
suite.Require().Equal(filteredCDPs3[0].ID, suite.cdps[68-1].ID)
ratioCountBtc := 0
btcRatio := d("2500")
for _, cdp := range suite.cdps {
if cdp.Type == "btc-a" {
absoluteRatio := suite.keeper.CalculateCollateralToDebtRatio(suite.ctx, cdp.Collateral, cdp.Type, cdp.Principal)
collateralizationRatio, _ := suite.keeper.CalculateCollateralizationRatioFromAbsoluteRatio(suite.ctx, cdp.Type, absoluteRatio, "liquidation")
if collateralizationRatio.LT(btcRatio) {
ratioCountBtc += 1
}
}
}
paramsTypeAndRatio := types.NewQueryCdpsParams(1, 100, "btc-a", sdk.AccAddress{}, 0, sdk.NewDec(2500))
filteredCDPs4 := keeper.FilterCDPs(suite.ctx, suite.keeper, paramsTypeAndRatio)
suite.Require().Equal(len(filteredCDPs4), ratioCountBtc)
}
func (suite *QuerierTestSuite) TestQueryCdps() {
ctx := suite.ctx.WithIsCheckTx(false)
params := types.NewQueryCdpsParams(1, 100, "btc-a", sdk.AccAddress{}, 0, sdk.ZeroDec())
query := abci.RequestQuery{
Path: strings.Join([]string{custom, types.QuerierRoute, types.QueryGetCdps}, "/"),
Data: types.ModuleCdc.MustMarshalJSON(params),
}
bz, err := suite.querier(ctx, []string{types.QueryGetCdps}, query)
suite.Nil(err)
suite.NotNil(bz)
output := types.AugmentedCDPs{}
suite.Nil(types.ModuleCdc.UnmarshalJSON(bz, &output))
suite.Equal(50, len(output))
}
func TestQuerierTestSuite(t *testing.T) {
suite.Run(t, new(QuerierTestSuite))
}

View File

@ -58,6 +58,11 @@ func (k Keeper) DistributeSavingsRate(ctx sdk.Context, debtDenom string) error {
return false
}
// update total savings rate distributed by surplus to distribute
previousSavingsDistributed := k.GetSavingsRateDistributed(ctx)
newTotalDistributed := previousSavingsDistributed.Add(interest)
k.SetSavingsRateDistributed(ctx, newTotalDistributed)
interestCoins := sdk.NewCoins(sdk.NewCoin(debtDenom, interest))
err := k.supplyKeeper.SendCoinsFromModuleToAccount(ctx, types.SavingsRateMacc, acc.GetAddress(), interestCoins)
if err != nil {

View File

@ -18,15 +18,17 @@ import (
type SavingsTestSuite struct {
suite.Suite
keeper keeper.Keeper
app app.TestApp
ctx sdk.Context
addrs []sdk.AccAddress
keeper keeper.Keeper
app app.TestApp
ctx sdk.Context
addrs []sdk.AccAddress
amountToDistribute int64
}
func (suite *SavingsTestSuite) SetupTest() {
tApp := app.NewTestApp()
ctx := tApp.NewContext(true, abci.Header{Height: 1, Time: tmtime.Now()})
_, addrs := app.GeneratePrivKeyAddressPairs(3)
authGS := app.NewAuthGenState(
addrs,
@ -34,25 +36,33 @@ func (suite *SavingsTestSuite) SetupTest() {
cs(c("usdx", 100000)), cs(c("usdx", 50000)), cs(c("usdx", 50000)),
},
)
tApp.InitializeFromGenesisStates(
authGS,
NewPricefeedGenStateMulti(),
NewCDPGenStateMulti(),
)
sk := tApp.GetSupplyKeeper()
macc := sk.GetModuleAccount(ctx, types.SavingsRateMacc)
err := sk.MintCoins(ctx, macc.GetName(), cs(c("usdx", 10000)))
distAmount := int64(10000)
err := sk.MintCoins(ctx, macc.GetName(), cs(c("usdx", distAmount)))
suite.NoError(err)
keeper := tApp.GetCDPKeeper()
suite.app = tApp
suite.keeper = keeper
suite.ctx = ctx
suite.addrs = addrs
suite.amountToDistribute = distAmount
}
func (suite *SavingsTestSuite) TestApplySavingsRate() {
preSavingsRateDistAmount := suite.keeper.GetSavingsRateDistributed(suite.ctx)
err := suite.keeper.DistributeSavingsRate(suite.ctx, "usdx")
suite.NoError(err)
ak := suite.app.GetAccountKeeper()
acc0 := ak.GetAccount(suite.ctx, suite.addrs[0])
suite.Equal(cs(c("usdx", 105000)), acc0.GetCoins())
@ -60,9 +70,14 @@ func (suite *SavingsTestSuite) TestApplySavingsRate() {
suite.Equal(cs(c("usdx", 52500)), acc1.GetCoins())
acc2 := ak.GetAccount(suite.ctx, suite.addrs[2])
suite.Equal(cs(c("usdx", 52500)), acc2.GetCoins())
sk := suite.app.GetSupplyKeeper()
macc := sk.GetModuleAccount(suite.ctx, types.SavingsRateMacc)
suite.True(macc.GetCoins().AmountOf("usdx").IsZero())
expectedPostSavingsRateDistAmount := preSavingsRateDistAmount.Add(sdk.NewInt(suite.amountToDistribute))
postSavingsRateDistAmount := suite.keeper.GetSavingsRateDistributed(suite.ctx)
suite.True(expectedPostSavingsRateDistAmount.Equal(postSavingsRateDistAmount))
}
func (suite *SavingsTestSuite) TestGetSetPreviousDistributionTime() {
@ -76,7 +91,21 @@ func (suite *SavingsTestSuite) TestGetSetPreviousDistributionTime() {
pdt, f := suite.keeper.GetPreviousSavingsDistribution(suite.ctx)
suite.True(f)
suite.Equal(now, pdt)
}
func (suite *SavingsTestSuite) TestGetSetSavingsRateDistributed() {
// Savings rate dist set to 0 when the default genesis is used
preSavingsRateDistAmount := suite.keeper.GetSavingsRateDistributed(suite.ctx)
suite.True(preSavingsRateDistAmount.Equal(types.DefaultSavingsRateDistributed))
// Adding new dist amount to existing dist so default genesis value can be updated in the future
amountToDistribute := sdk.NewInt(9876543210)
newTotalDistributed := preSavingsRateDistAmount.Add(amountToDistribute)
suite.NotPanics(func() { suite.keeper.SetSavingsRateDistributed(suite.ctx, newTotalDistributed) })
postSavingsRateDistAmount := suite.keeper.GetSavingsRateDistributed(suite.ctx)
suite.Equal(newTotalDistributed, postSavingsRateDistAmount)
}
func TestSavingsTestSuite(t *testing.T) {

View File

@ -121,6 +121,7 @@ func NewAugmentedCDP(cdp CDP, collateralValue sdk.Coin, collateralizationRatio s
CDP: CDP{
ID: cdp.ID,
Owner: cdp.Owner,
Type: cdp.Type,
Collateral: cdp.Collateral,
Principal: cdp.Principal,
AccumulatedFees: cdp.AccumulatedFees,
@ -146,7 +147,7 @@ func (augCDP AugmentedCDP) String() string {
Collateralization ratio: %s`,
augCDP.Owner,
augCDP.ID,
augCDP.Collateral.Denom,
augCDP.Type,
augCDP.Collateral,
augCDP.CollateralValue,
augCDP.Principal,

View File

@ -17,10 +17,12 @@ type GenesisState struct {
DebtDenom string `json:"debt_denom" yaml:"debt_denom"`
GovDenom string `json:"gov_denom" yaml:"gov_denom"`
PreviousDistributionTime time.Time `json:"previous_distribution_time" yaml:"previous_distribution_time"`
SavingsRateDistributed sdk.Int `json:"savings_rate_distributed" yaml:"savings_rate_distributed"`
}
// NewGenesisState returns a new genesis state
func NewGenesisState(params Params, cdps CDPs, deposits Deposits, startingCdpID uint64, debtDenom, govDenom string, previousDistTime time.Time) GenesisState {
func NewGenesisState(params Params, cdps CDPs, deposits Deposits, startingCdpID uint64,
debtDenom, govDenom string, previousDistTime time.Time, savingsRateDist sdk.Int) GenesisState {
return GenesisState{
Params: params,
CDPs: cdps,
@ -29,6 +31,7 @@ func NewGenesisState(params Params, cdps CDPs, deposits Deposits, startingCdpID
DebtDenom: debtDenom,
GovDenom: govDenom,
PreviousDistributionTime: previousDistTime,
SavingsRateDistributed: savingsRateDist,
}
}
@ -42,6 +45,7 @@ func DefaultGenesisState() GenesisState {
DefaultDebtDenom,
DefaultGovDenom,
DefaultPreviousDistributionTime,
DefaultSavingsRateDistributed,
)
}
@ -65,6 +69,10 @@ func (gs GenesisState) Validate() error {
return fmt.Errorf("previous distribution time not set")
}
if err := validateSavingsRateDistributed(gs.SavingsRateDistributed); err != nil {
return err
}
if err := sdk.ValidateDenom(gs.DebtDenom); err != nil {
return fmt.Errorf(fmt.Sprintf("debt denom invalid: %v", err))
}
@ -76,6 +84,19 @@ func (gs GenesisState) Validate() error {
return nil
}
func validateSavingsRateDistributed(i interface{}) error {
savingsRateDist, ok := i.(sdk.Int)
if !ok {
return fmt.Errorf("invalid parameter type: %T", i)
}
if savingsRateDist.IsNegative() {
return fmt.Errorf("savings rate distributed should not be negative: %s", savingsRateDist)
}
return nil
}
// Equal checks whether two gov GenesisState structs are equivalent
func (gs GenesisState) Equal(gs2 GenesisState) bool {
b1 := ModuleCdc.MustMarshalBinaryBare(gs)

View File

@ -47,6 +47,7 @@ var sep = []byte(":")
// - 0x07<denom>:feeRate
// - 0x08:previousDistributionTime
// - 0x09<marketID>:downTime
// - 0x10:totalDistributed
// KVStore key prefixes
var (
@ -60,6 +61,7 @@ var (
PrincipalKeyPrefix = []byte{0x07}
PreviousDistributionTimeKey = []byte{0x08}
PricefeedStatusKeyPrefix = []byte{0x09}
SavingsRateDistributedKey = []byte{0x10}
)
// GetCdpIDBytes returns the byte representation of the cdpID

View File

@ -76,7 +76,8 @@ func (msg MsgCreateCDP) String() string {
Sender: %s
Collateral: %s
Principal: %s
`, msg.Sender, msg.Collateral, msg.Principal)
Collateral Type: %s
`, msg.Sender, msg.Collateral, msg.Principal, msg.CollateralType)
}
// MsgDeposit deposit collateral to an existing cdp.

View File

@ -14,19 +14,20 @@ import (
// Parameter keys
var (
KeyGlobalDebtLimit = []byte("GlobalDebtLimit")
KeyCollateralParams = []byte("CollateralParams")
KeyDebtParam = []byte("DebtParam")
KeyDistributionFrequency = []byte("DistributionFrequency")
KeyCircuitBreaker = []byte("CircuitBreaker")
KeyDebtThreshold = []byte("DebtThreshold")
KeyDebtLot = []byte("DebtLot")
KeySurplusThreshold = []byte("SurplusThreshold")
KeySurplusLot = []byte("SurplusLot")
DefaultGlobalDebt = sdk.NewCoin(DefaultStableDenom, sdk.ZeroInt())
DefaultCircuitBreaker = false
DefaultCollateralParams = CollateralParams{}
DefaultDebtParam = DebtParam{
KeyGlobalDebtLimit = []byte("GlobalDebtLimit")
KeyCollateralParams = []byte("CollateralParams")
KeyDebtParam = []byte("DebtParam")
KeyDistributionFrequency = []byte("DistributionFrequency")
KeyCircuitBreaker = []byte("CircuitBreaker")
KeyDebtThreshold = []byte("DebtThreshold")
KeyDebtLot = []byte("DebtLot")
KeySurplusThreshold = []byte("SurplusThreshold")
KeySurplusLot = []byte("SurplusLot")
KeySavingsRateDistributed = []byte("SavingsRateDistributed")
DefaultGlobalDebt = sdk.NewCoin(DefaultStableDenom, sdk.ZeroInt())
DefaultCircuitBreaker = false
DefaultCollateralParams = CollateralParams{}
DefaultDebtParam = DebtParam{
Denom: "usdx",
ReferenceAsset: "usd",
ConversionFactor: sdk.NewInt(6),
@ -43,6 +44,7 @@ var (
DefaultDebtLot = sdk.NewInt(10000000000)
DefaultPreviousDistributionTime = tmtime.Canonical(time.Unix(0, 0))
DefaultSavingsDistributionFrequency = time.Hour * 12
DefaultSavingsRateDistributed = sdk.NewInt(0)
minCollateralPrefix = 0
maxCollateralPrefix = 255
stabilityFeeMax = sdk.MustNewDecFromStr("1.000000051034942716") // 500% APR

View File

@ -7,28 +7,18 @@ import (
// Querier routes for the cdp module
const (
QueryGetCdp = "cdp"
QueryGetCdpDeposits = "deposits"
QueryGetCdps = "cdps"
QueryGetCdpsByCollateralization = "ratio"
QueryGetCdpDeposits = "deposits"
QueryGetCdpsByCollateralization = "ratio" // legacy query, maintained for REST API
QueryGetCdpsByCollateralType = "collateralType" // legacy query, maintained for REST API
QueryGetParams = "params"
QueryGetAccounts = "accounts"
QueryGetSavingsRateDistributed = "savings-rate-dist"
RestOwner = "owner"
RestCollateralType = "collateral-type"
RestRatio = "ratio"
)
// QueryCdpsParams params for query /cdp/cdps
type QueryCdpsParams struct {
CollateralType string // get CDPs with this collateral type
}
// NewQueryCdpsParams returns QueryCdpsParams
func NewQueryCdpsParams(collateralType string) QueryCdpsParams {
return QueryCdpsParams{
CollateralType: collateralType,
}
}
// QueryCdpParams params for query /cdp/cdp
type QueryCdpParams struct {
CollateralType string // get CDPs with this collateral type
@ -43,6 +33,28 @@ func NewQueryCdpParams(owner sdk.AccAddress, collateralType string) QueryCdpPara
}
}
// QueryCdpsParams is the params for a filtered CDP query
type QueryCdpsParams struct {
Page int `json:"page" yaml:"page"`
Limit int `json:"limit" yaml:"limit"`
CollateralType string `json:"collateral_type" yaml:"collateral_type"`
Owner sdk.AccAddress `json:"owner" yaml:"owner"`
ID uint64 `json:"id" yaml:"id"`
Ratio sdk.Dec `json:"ratio" yaml:"ratio"`
}
// NewQueryCdpsParams creates a new QueryCdpsParams
func NewQueryCdpsParams(page, limit int, collateralType string, owner sdk.AccAddress, id uint64, ratio sdk.Dec) QueryCdpsParams {
return QueryCdpsParams{
Page: page,
Limit: limit,
CollateralType: collateralType,
Owner: owner,
ID: id,
Ratio: ratio,
}
}
// QueryCdpDeposits params for query /cdp/deposits
type QueryCdpDeposits struct {
CollateralType string // get CDPs with this collateral type
@ -57,6 +69,18 @@ func NewQueryCdpDeposits(owner sdk.AccAddress, collateralType string) QueryCdpDe
}
}
// QueryCdpsByCollateralTypeParams params for query /cdp/cdps/{denom}
type QueryCdpsByCollateralTypeParams struct {
CollateralType string // get CDPs with this collateral type
}
// NewQueryCdpsByCollateralTypeParams returns QueryCdpsByCollateralTypeParams
func NewQueryCdpsByCollateralTypeParams(collateralType string) QueryCdpsByCollateralTypeParams {
return QueryCdpsByCollateralTypeParams{
CollateralType: collateralType,
}
}
// QueryCdpsByRatioParams params for query /cdp/cdps/ratio
type QueryCdpsByRatioParams struct {
CollateralType string