Add collateral type to cdp (#629)

* add collateral type field to cdp and collateral  param

* fix upstream tests

* fix simulations

* fix validation logic

* update incentive to use collateral type instead of denom

* use collateral type instead of denom in cdp

* remove unused code

* address review comments
This commit is contained in:
Kevin Davis 2020-08-21 15:42:46 -04:00 committed by GitHub
parent 60d7f522e2
commit daa1b2bb83
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
70 changed files with 1101 additions and 809 deletions

View File

@ -46,6 +46,16 @@ Ref: https://keepachangelog.com/en/1.0.0/
[\#578](https://github.com/Kava-Labs/kava/pulls/578) Add v0.3 compatible REST client that supports
[\#629](https://github.com/Kava-Labs/kava/pulls/629) Add CDP collateral type as a field for CDPs and collateral parameters.
### Breaking changes
* CDPs have an additional field, Type, which is a string that represents the unique collateral type that this CDP holds. This enables, for example, a single denom such as 'bnb' to have two CDP types, 'bnb-a' and 'bnb-b'.
* CollateralParam has an additional field, Type, which is a string that represents the collateral type of CDPs that this collateral parameter governs. It must be non-empty at genesis or when altering CDP fields. It is UNSAFE to alter the type of an existing collateral param using unchain governance.
* CDP messages must specify the collateral type 'bnb-a', rather than the denom of the cdp.
* In the incentive module, fields previously named `Denom` have been changed to `CollateralType`. Previously, 'Denom' was validated to check that it satisfied `sdk.ValidateDenom`, now, the validation checks that the `CollateralType` is not blank.
* Incentive module messages now require the user to specify the collateral type ('bnb-a'), rather than the denom of the cdp ('bnb')
```plaintext
/v0_3/node_info
/v0_3/auth/accounts/<address>

View File

@ -121,6 +121,7 @@ func sendBtcCdp() {
addr,
sdk.NewInt64Coin("btc", 200000000),
sdk.NewInt64Coin("usdx", 10000000),
"btc-a",
)
// helper methods for transactions
@ -154,6 +155,7 @@ func sendXrpCdp() {
addr,
sdk.NewInt64Coin("xrp", 200000000),
sdk.NewInt64Coin("usdx", 10000000),
"xrp-a",
)
// helper methods for transactions

View File

@ -31,12 +31,12 @@ func BeginBlocker(ctx sdk.Context, req abci.RequestBeginBlock, k Keeper) {
continue
}
err := k.UpdateFeesForAllCdps(ctx, cp.Denom)
err := k.UpdateFeesForAllCdps(ctx, cp.Type)
if err != nil {
panic(err)
}
err = k.LiquidateCdps(ctx, cp.LiquidationMarketID, cp.Denom, cp.LiquidationRatio)
err = k.LiquidateCdps(ctx, cp.LiquidationMarketID, cp.Type, cp.LiquidationRatio)
if err != nil && !errors.Is(err, pricefeedtypes.ErrNoValidPrice) {
panic(err)
}

View File

@ -103,8 +103,8 @@ func (suite *ModuleTestSuite) createCdps() {
tracker.debt += int64(debt)
}
}
suite.Nil(suite.keeper.AddCdp(suite.ctx, addrs[j], c(collateral, int64(amount)), c("usdx", int64(debt))))
c, f := suite.keeper.GetCDP(suite.ctx, collateral, uint64(j+1))
suite.Nil(suite.keeper.AddCdp(suite.ctx, addrs[j], c(collateral, int64(amount)), c("usdx", int64(debt)), collateral+"-a"))
c, f := suite.keeper.GetCDP(suite.ctx, collateral+"-a", uint64(j+1))
suite.True(f)
cdps[j] = c
}
@ -153,9 +153,9 @@ func (suite *ModuleTestSuite) TestBeginBlock() {
}
func (suite *ModuleTestSuite) TestSeizeSingleCdpWithFees() {
err := suite.keeper.AddCdp(suite.ctx, suite.addrs[0], c("xrp", 10000000000), c("usdx", 1000000000))
err := suite.keeper.AddCdp(suite.ctx, suite.addrs[0], c("xrp", 10000000000), c("usdx", 1000000000), "xrp-a")
suite.NoError(err)
suite.Equal(i(1000000000), suite.keeper.GetTotalPrincipal(suite.ctx, "xrp", "usdx"))
suite.Equal(i(1000000000), suite.keeper.GetTotalPrincipal(suite.ctx, "xrp-a", "usdx"))
sk := suite.app.GetSupplyKeeper()
cdpMacc := sk.GetModuleAccount(suite.ctx, cdp.ModuleName)
suite.Equal(i(1000000000), cdpMacc.GetCoins().AmountOf("debt"))
@ -166,11 +166,11 @@ func (suite *ModuleTestSuite) TestSeizeSingleCdpWithFees() {
cdpMacc = sk.GetModuleAccount(suite.ctx, cdp.ModuleName)
suite.Equal(i(1000000900), (cdpMacc.GetCoins().AmountOf("debt")))
cdp, _ := suite.keeper.GetCDP(suite.ctx, "xrp", 1)
cdp, _ := suite.keeper.GetCDP(suite.ctx, "xrp-a", 1)
err = suite.keeper.SeizeCollateral(suite.ctx, cdp)
suite.NoError(err)
_, found := suite.keeper.GetCDP(suite.ctx, "xrp", 1)
_, found := suite.keeper.GetCDP(suite.ctx, "xrp-a", 1)
suite.False(found)
}

View File

@ -38,7 +38,7 @@ const (
QueryGetCdpsByCollateralization = types.QueryGetCdpsByCollateralization
QueryGetParams = types.QueryGetParams
RestOwner = types.RestOwner
RestCollateralDenom = types.RestCollateralDenom
RestCollateralType = types.RestCollateralType
RestRatio = types.RestRatio
)

View File

@ -26,8 +26,8 @@ func GetQueryCmd(queryRoute string, cdc *codec.Codec) *cobra.Command {
cdpQueryCmd.AddCommand(flags.GetCommands(
QueryCdpCmd(queryRoute, cdc),
QueryCdpsByDenomCmd(queryRoute, cdc),
QueryCdpsByDenomAndRatioCmd(queryRoute, cdc),
QueryCdpsByCollateralTypeCmd(queryRoute, cdc),
QueryCdpsByCollateralTypeAndRatioCmd(queryRoute, cdc),
QueryCdpDepositsCmd(queryRoute, cdc),
QueryParamsCmd(queryRoute, cdc),
QueryGetAccounts(queryRoute, cdc),
@ -39,13 +39,13 @@ func GetQueryCmd(queryRoute string, cdc *codec.Codec) *cobra.Command {
// QueryCdpCmd returns the command handler for querying a particular cdp
func QueryCdpCmd(queryRoute string, cdc *codec.Codec) *cobra.Command {
return &cobra.Command{
Use: "cdp [owner-addr] [collateral-name]",
Use: "cdp [owner-addr] [collateral-type]",
Short: "get info about a cdp",
Long: strings.TrimSpace(
fmt.Sprintf(`Get a CDP by the owner address and the collateral name.
Example:
$ %s query %s cdp kava15qdefkmwswysgg4qxgqpqr35k3m49pkx2jdfnw uatom
$ %s query %s cdp kava15qdefkmwswysgg4qxgqpqr35k3m49pkx2jdfnw atom-a
`, version.ClientName, types.ModuleName)),
Args: cobra.ExactArgs(2),
RunE: func(cmd *cobra.Command, args []string) error {
@ -57,8 +57,8 @@ $ %s query %s cdp kava15qdefkmwswysgg4qxgqpqr35k3m49pkx2jdfnw uatom
return err
}
bz, err := cdc.MarshalJSON(types.QueryCdpParams{
CollateralDenom: args[1],
Owner: ownerAddress,
CollateralType: args[1],
Owner: ownerAddress,
})
if err != nil {
return err
@ -79,23 +79,23 @@ $ %s query %s cdp kava15qdefkmwswysgg4qxgqpqr35k3m49pkx2jdfnw uatom
}
}
// QueryCdpsByDenomCmd returns the command handler for querying cdps for a collateral type
func QueryCdpsByDenomCmd(queryRoute string, cdc *codec.Codec) *cobra.Command {
// 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-name]",
Use: "cdps [collateral-type]",
Short: "query CDPs by collateral",
Long: strings.TrimSpace(
fmt.Sprintf(`List all CDPs collateralized with the specified asset.
Example:
$ %s query %s cdps uatom
$ %s query %s cdps atom-a
`, version.ClientName, types.ModuleName)),
Args: cobra.ExactArgs(1),
RunE: func(cmd *cobra.Command, args []string) error {
cliCtx := context.NewCLIContext().WithCodec(cdc)
// Prepare params for querier
bz, err := cdc.MarshalJSON(types.QueryCdpsParams{CollateralDenom: args[0]})
bz, err := cdc.MarshalJSON(types.QueryCdpsParams{CollateralType: args[0]})
if err != nil {
return err
}
@ -115,18 +115,18 @@ $ %s query %s cdps uatom
}
}
// QueryCdpsByDenomAndRatioCmd returns the command handler for querying cdps
// QueryCdpsByCollateralTypeAndRatioCmd returns the command handler for querying cdps
// that are under the specified collateral ratio
func QueryCdpsByDenomAndRatioCmd(queryRoute string, cdc *codec.Codec) *cobra.Command {
func QueryCdpsByCollateralTypeAndRatioCmd(queryRoute string, cdc *codec.Codec) *cobra.Command {
return &cobra.Command{
Use: "cdps-by-ratio [collateral-name] [collateralization-ratio]",
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.
Example:
$ %s query %s cdps-by-ratio uatom 1.5
$ %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 {
@ -138,8 +138,8 @@ $ %s query %s cdps-by-ratio uatom 1.5
return err
}
bz, err := cdc.MarshalJSON(types.QueryCdpsByRatioParams{
CollateralDenom: args[0],
Ratio: ratio,
CollateralType: args[0],
Ratio: ratio,
})
if err != nil {
return err
@ -163,13 +163,13 @@ $ %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]",
Use: "deposits [owner-addr] [collateral-type]",
Short: "get deposits for a cdp",
Long: strings.TrimSpace(
fmt.Sprintf(`Get the deposits of a CDP.
Example:
$ %s query %s deposits kava15qdefkmwswysgg4qxgqpqr35k3m49pkx2jdfnw uatom
$ %s query %s deposits kava15qdefkmwswysgg4qxgqpqr35k3m49pkx2jdfnw atom-a
`, version.ClientName, types.ModuleName)),
Args: cobra.ExactArgs(2),
RunE: func(cmd *cobra.Command, args []string) error {
@ -181,8 +181,8 @@ $ %s query %s deposits kava15qdefkmwswysgg4qxgqpqr35k3m49pkx2jdfnw uatom
return err
}
bz, err := cdc.MarshalJSON(types.QueryCdpParams{
CollateralDenom: args[1],
Owner: ownerAddress,
CollateralType: args[1],
Owner: ownerAddress,
})
if err != nil {
return err

View File

@ -39,15 +39,15 @@ func GetTxCmd(cdc *codec.Codec) *cobra.Command {
// GetCmdCreateCdp returns the command handler for creating a cdp
func GetCmdCreateCdp(cdc *codec.Codec) *cobra.Command {
return &cobra.Command{
Use: "create [collateral] [debt]",
Use: "create [collateral] [debt] [collateral-type]",
Short: "create a new cdp",
Long: strings.TrimSpace(
fmt.Sprintf(`Create a new cdp, depositing some collateral and drawing some debt.
Example:
$ %s tx %s create 10000000uatom 1000usdx --from myKeyName
$ %s tx %s create 10000000uatom 1000usdx atom-a --from myKeyName
`, version.ClientName, types.ModuleName)),
Args: cobra.ExactArgs(2),
Args: cobra.ExactArgs(3),
RunE: func(cmd *cobra.Command, args []string) error {
inBuf := bufio.NewReader(cmd.InOrStdin())
cliCtx := context.NewCLIContext().WithCodec(cdc)
@ -61,7 +61,7 @@ $ %s tx %s create 10000000uatom 1000usdx --from myKeyName
if err != nil {
return err
}
msg := types.NewMsgCreateCDP(cliCtx.GetFromAddress(), collateral, debt)
msg := types.NewMsgCreateCDP(cliCtx.GetFromAddress(), collateral, debt, args[2])
err = msg.ValidateBasic()
if err != nil {
return err
@ -74,15 +74,15 @@ $ %s tx %s create 10000000uatom 1000usdx --from myKeyName
// GetCmdDeposit cli command for depositing to a cdp.
func GetCmdDeposit(cdc *codec.Codec) *cobra.Command {
return &cobra.Command{
Use: "deposit [owner-addr] [collateral]",
Use: "deposit [owner-addr] [collateral] [collateral-type]",
Short: "deposit collateral to an existing cdp",
Long: strings.TrimSpace(
fmt.Sprintf(`Add collateral to an existing cdp.
Example:
$ %s tx %s deposit kava15qdefkmwswysgg4qxgqpqr35k3m49pkx2jdfnw 10000000uatom --from myKeyName
$ %s tx %s deposit kava15qdefkmwswysgg4qxgqpqr35k3m49pkx2jdfnw 10000000uatom atom-a --from myKeyName
`, version.ClientName, types.ModuleName)),
Args: cobra.ExactArgs(2),
Args: cobra.ExactArgs(3),
RunE: func(cmd *cobra.Command, args []string) error {
inBuf := bufio.NewReader(cmd.InOrStdin())
cliCtx := context.NewCLIContext().WithCodec(cdc)
@ -96,7 +96,7 @@ $ %s tx %s deposit kava15qdefkmwswysgg4qxgqpqr35k3m49pkx2jdfnw 10000000uatom --f
if err != nil {
return err
}
msg := types.NewMsgDeposit(owner, cliCtx.GetFromAddress(), collateral)
msg := types.NewMsgDeposit(owner, cliCtx.GetFromAddress(), collateral, args[2])
err = msg.ValidateBasic()
if err != nil {
return err
@ -109,13 +109,13 @@ $ %s tx %s deposit kava15qdefkmwswysgg4qxgqpqr35k3m49pkx2jdfnw 10000000uatom --f
// GetCmdWithdraw cli command for withdrawing from a cdp.
func GetCmdWithdraw(cdc *codec.Codec) *cobra.Command {
return &cobra.Command{
Use: "withdraw [owner-addr] [collateral]",
Use: "withdraw [owner-addr] [collateral] [collateral-type]",
Short: "withdraw collateral from an existing cdp",
Long: strings.TrimSpace(
fmt.Sprintf(`Remove collateral from an existing cdp.
Example:
$ %s tx %s withdraw kava15qdefkmwswysgg4qxgqpqr35k3m49pkx2jdfnw 10000000uatom --from myKeyName
$ %s tx %s withdraw kava15qdefkmwswysgg4qxgqpqr35k3m49pkx2jdfnw 10000000uatom atom-a --from myKeyName
`, version.ClientName, types.ModuleName)),
Args: cobra.ExactArgs(2),
RunE: func(cmd *cobra.Command, args []string) error {
@ -131,7 +131,7 @@ $ %s tx %s withdraw kava15qdefkmwswysgg4qxgqpqr35k3m49pkx2jdfnw 10000000uatom --
if err != nil {
return err
}
msg := types.NewMsgWithdraw(owner, cliCtx.GetFromAddress(), collateral)
msg := types.NewMsgWithdraw(owner, cliCtx.GetFromAddress(), collateral, args[2])
err = msg.ValidateBasic()
if err != nil {
return err
@ -144,13 +144,13 @@ $ %s tx %s withdraw kava15qdefkmwswysgg4qxgqpqr35k3m49pkx2jdfnw 10000000uatom --
// GetCmdDraw cli command for depositing to a cdp.
func GetCmdDraw(cdc *codec.Codec) *cobra.Command {
return &cobra.Command{
Use: "draw [collateral-name] [debt]",
Use: "draw [collateral-type] [debt]",
Short: "draw debt off an existing cdp",
Long: strings.TrimSpace(
fmt.Sprintf(`Create debt in an existing cdp and send the newly minted asset to your account.
Example:
$ %s tx %s draw uatom 1000usdx --from myKeyName
$ %s tx %s draw atom-a 1000usdx --from myKeyName
`, version.ClientName, types.ModuleName)),
Args: cobra.ExactArgs(2),
RunE: func(cmd *cobra.Command, args []string) error {
@ -181,7 +181,7 @@ func GetCmdRepay(cdc *codec.Codec) *cobra.Command {
fmt.Sprintf(`Cancel out debt in an existing cdp.
Example:
$ %s tx %s repay uatom 1000usdx --from myKeyName
$ %s tx %s repay atom-a 1000usdx --from myKeyName
`, version.ClientName, types.ModuleName)),
Args: cobra.ExactArgs(2),
RunE: func(cmd *cobra.Command, args []string) error {

View File

@ -16,10 +16,10 @@ 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(fmt.Sprintf("/cdp/cdps/cdp/{%s}/{%s}", types.RestOwner, types.RestCollateralDenom), queryCdpHandlerFn(cliCtx)).Methods("GET")
r.HandleFunc(fmt.Sprintf("/cdp/cdps/denom/{%s}", types.RestCollateralDenom), queryCdpsHandlerFn(cliCtx)).Methods("GET")
r.HandleFunc(fmt.Sprintf("/cdp/cdps/ratio/{%s}/{%s}", types.RestCollateralDenom, types.RestRatio), queryCdpsByRatioHandlerFn(cliCtx)).Methods("GET")
r.HandleFunc(fmt.Sprintf("/cdp/cdps/cdp/deposits/{%s}/{%s}", types.RestOwner, types.RestCollateralDenom), queryCdpDepositsHandlerFn(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/cdp/deposits/{%s}/{%s}", types.RestOwner, types.RestCollateralType), queryCdpDepositsHandlerFn(cliCtx)).Methods("GET")
}
func queryCdpHandlerFn(cliCtx context.CLIContext) http.HandlerFunc {
@ -31,7 +31,7 @@ func queryCdpHandlerFn(cliCtx context.CLIContext) http.HandlerFunc {
vars := mux.Vars(r)
ownerBech32 := vars[types.RestOwner]
collateralDenom := vars[types.RestCollateralDenom]
collateralType := vars[types.RestCollateralType]
owner, err := sdk.AccAddressFromBech32(ownerBech32)
if err != nil {
@ -39,7 +39,7 @@ func queryCdpHandlerFn(cliCtx context.CLIContext) http.HandlerFunc {
return
}
params := types.NewQueryCdpParams(owner, collateralDenom)
params := types.NewQueryCdpParams(owner, collateralType)
bz, err := cliCtx.Codec.MarshalJSON(params)
if err != nil {
@ -66,9 +66,9 @@ func queryCdpsHandlerFn(cliCtx context.CLIContext) http.HandlerFunc {
}
vars := mux.Vars(r)
collateralDenom := vars[types.RestCollateralDenom]
collateralType := vars[types.RestCollateralType]
params := types.NewQueryCdpsParams(collateralDenom)
params := types.NewQueryCdpsParams(collateralType)
bz, err := cliCtx.Codec.MarshalJSON(params)
if err != nil {
@ -95,7 +95,7 @@ func queryCdpsByRatioHandlerFn(cliCtx context.CLIContext) http.HandlerFunc {
}
vars := mux.Vars(r)
collateralDenom := vars[types.RestCollateralDenom]
collateralType := vars[types.RestCollateralType]
ratioStr := vars[types.RestRatio]
ratioDec, sdkError := sdk.NewDecFromStr(ratioStr)
@ -104,7 +104,7 @@ func queryCdpsByRatioHandlerFn(cliCtx context.CLIContext) http.HandlerFunc {
return
}
params := types.NewQueryCdpsByRatioParams(collateralDenom, ratioDec)
params := types.NewQueryCdpsByRatioParams(collateralType, ratioDec)
bz, err := cliCtx.Codec.MarshalJSON(params)
if err != nil {
rest.WriteErrorResponse(w, http.StatusBadRequest, fmt.Sprintf("failed to marshal query params: %s", err))
@ -131,7 +131,7 @@ func queryCdpDepositsHandlerFn(cliCtx context.CLIContext) http.HandlerFunc {
vars := mux.Vars(r)
ownerBech32 := vars[types.RestOwner]
collateralDenom := vars[types.RestCollateralDenom]
collateralType := vars[types.RestCollateralType]
owner, err := sdk.AccAddressFromBech32(ownerBech32)
if err != nil {
@ -139,7 +139,7 @@ func queryCdpDepositsHandlerFn(cliCtx context.CLIContext) http.HandlerFunc {
return
}
params := types.NewQueryCdpDeposits(owner, collateralDenom)
params := types.NewQueryCdpDeposits(owner, collateralType)
bz, err := cliCtx.Codec.MarshalJSON(params)
if err != nil {

View File

@ -16,40 +16,43 @@ func RegisterRoutes(cliCtx context.CLIContext, r *mux.Router) {
// PostCdpReq defines the properties of cdp request's body.
type PostCdpReq struct {
BaseReq rest.BaseReq `json:"base_req" yaml:"base_req"`
Sender sdk.AccAddress `json:"sender" yaml:"sender"`
Collateral sdk.Coin `json:"collateral" yaml:"collateral"`
Principal sdk.Coin `json:"principal" yaml:"principal"`
BaseReq rest.BaseReq `json:"base_req" yaml:"base_req"`
Sender sdk.AccAddress `json:"sender" yaml:"sender"`
Collateral sdk.Coin `json:"collateral" yaml:"collateral"`
CollateralType string `json:"collateral_type" yaml:"collateral_type"`
Principal sdk.Coin `json:"principal" yaml:"principal"`
}
// PostDepositReq defines the properties of cdp request's body.
type PostDepositReq struct {
BaseReq rest.BaseReq `json:"base_req" yaml:"base_req"`
Owner sdk.AccAddress `json:"owner" yaml:"owner"`
Depositor sdk.AccAddress `json:"depositor" yaml:"depositor"`
Collateral sdk.Coin `json:"collateral" yaml:"collateral"`
BaseReq rest.BaseReq `json:"base_req" yaml:"base_req"`
Owner sdk.AccAddress `json:"owner" yaml:"owner"`
Depositor sdk.AccAddress `json:"depositor" yaml:"depositor"`
Collateral sdk.Coin `json:"collateral" yaml:"collateral"`
CollateralType string `json:"collateral_type" yaml:"collateral_type"`
}
// PostWithdrawalReq defines the properties of cdp request's body.
type PostWithdrawalReq struct {
BaseReq rest.BaseReq `json:"base_req" yaml:"base_req"`
Owner sdk.AccAddress `json:"owner" yaml:"owner"`
Depositor sdk.AccAddress `json:"depositor" yaml:"depositor"`
Collateral sdk.Coin `json:"collateral" yaml:"collateral"`
BaseReq rest.BaseReq `json:"base_req" yaml:"base_req"`
Owner sdk.AccAddress `json:"owner" yaml:"owner"`
Depositor sdk.AccAddress `json:"depositor" yaml:"depositor"`
Collateral sdk.Coin `json:"collateral" yaml:"collateral"`
CollateralType string `json:"collateral_type" yaml:"collateral_type"`
}
// PostDrawReq defines the properties of cdp request's body.
type PostDrawReq struct {
BaseReq rest.BaseReq `json:"base_req" yaml:"base_req"`
Owner sdk.AccAddress `json:"owner" yaml:"owner"`
Denom string `json:"denom" yaml:"denom"`
Principal sdk.Coin `json:"principal" yaml:"principal"`
BaseReq rest.BaseReq `json:"base_req" yaml:"base_req"`
Owner sdk.AccAddress `json:"owner" yaml:"owner"`
CollateralType string `json:"collateral_type" yaml:"collateral_type"`
Principal sdk.Coin `json:"principal" yaml:"principal"`
}
// PostRepayReq defines the properties of cdp request's body.
type PostRepayReq struct {
BaseReq rest.BaseReq `json:"base_req" yaml:"base_req"`
Owner sdk.AccAddress `json:"owner" yaml:"owner"`
Denom string `json:"denom" yaml:"denom"`
Payment sdk.Coin `json:"payment" yaml:"payment"`
BaseReq rest.BaseReq `json:"base_req" yaml:"base_req"`
Owner sdk.AccAddress `json:"owner" yaml:"owner"`
CollateralType string `json:"collateral_type" yaml:"collateral_type"`
Payment sdk.Coin `json:"payment" yaml:"payment"`
}

View File

@ -16,10 +16,10 @@ import (
func registerTxRoutes(cliCtx context.CLIContext, r *mux.Router) {
r.HandleFunc("/cdp", postCdpHandlerFn(cliCtx)).Methods("POST")
r.HandleFunc("/cdp/{owner}/{denom}/deposits", postDepositHandlerFn(cliCtx)).Methods("POST")
r.HandleFunc("/cdp/{owner}/{denom}/withdraw", postWithdrawHandlerFn(cliCtx)).Methods("POST")
r.HandleFunc("/cdp/{owner}/{denom}/draw", postDrawHandlerFn(cliCtx)).Methods("POST")
r.HandleFunc("/cdp/{owner}/{denom}/repay", postRepayHandlerFn(cliCtx)).Methods("POST")
r.HandleFunc("/cdp/{owner}/{collateralType}/deposits", postDepositHandlerFn(cliCtx)).Methods("POST")
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")
}
@ -50,6 +50,7 @@ func postCdpHandlerFn(cliCtx context.CLIContext) http.HandlerFunc {
requestBody.Sender,
requestBody.Collateral,
requestBody.Principal,
requestBody.CollateralType,
)
if err := msg.ValidateBasic(); err != nil {
rest.WriteErrorResponse(w, http.StatusBadRequest, err.Error())
@ -76,6 +77,7 @@ func postDepositHandlerFn(cliCtx context.CLIContext) http.HandlerFunc {
requestBody.Owner,
requestBody.Depositor,
requestBody.Collateral,
requestBody.CollateralType,
)
if err := msg.ValidateBasic(); err != nil {
rest.WriteErrorResponse(w, http.StatusBadRequest, err.Error())
@ -102,6 +104,7 @@ func postWithdrawHandlerFn(cliCtx context.CLIContext) http.HandlerFunc {
requestBody.Owner,
requestBody.Depositor,
requestBody.Collateral,
requestBody.CollateralType,
)
if err := msg.ValidateBasic(); err != nil {
rest.WriteErrorResponse(w, http.StatusBadRequest, err.Error())
@ -137,7 +140,7 @@ func postDrawHandlerFn(cliCtx context.CLIContext) http.HandlerFunc {
msg := types.NewMsgDrawDebt(
requestBody.Owner,
requestBody.Denom,
requestBody.CollateralType,
requestBody.Principal,
)
if err := msg.ValidateBasic(); err != nil {
@ -174,7 +177,7 @@ func postRepayHandlerFn(cliCtx context.CLIContext) http.HandlerFunc {
msg := types.NewMsgRepayDebt(
requestBody.Owner,
requestBody.Denom,
requestBody.CollateralType,
requestBody.Payment,
)
if err := msg.ValidateBasic(); err != nil {

View File

@ -59,7 +59,7 @@ func InitGenesis(ctx sdk.Context, k Keeper, pk types.PricefeedKeeper, sk types.S
// set the per second fee rate for each collateral type
for _, cp := range gs.Params.CollateralParams {
k.SetTotalPrincipal(ctx, cp.Denom, gs.Params.DebtParam.Denom, sdk.ZeroInt())
k.SetTotalPrincipal(ctx, cp.Type, gs.Params.DebtParam.Denom, sdk.ZeroInt())
}
// add cdps
@ -72,9 +72,9 @@ func InitGenesis(ctx sdk.Context, k Keeper, pk types.PricefeedKeeper, sk types.S
panic(fmt.Sprintf("error setting cdp: %v", err))
}
k.IndexCdpByOwner(ctx, cdp)
ratio := k.CalculateCollateralToDebtRatio(ctx, cdp.Collateral, cdp.GetTotalPrincipal())
k.IndexCdpByCollateralRatio(ctx, cdp.Collateral.Denom, cdp.ID, ratio)
k.IncrementTotalPrincipal(ctx, cdp.Collateral.Denom, cdp.GetTotalPrincipal())
ratio := k.CalculateCollateralToDebtRatio(ctx, cdp.Collateral, cdp.Type, cdp.GetTotalPrincipal())
k.IndexCdpByCollateralRatio(ctx, cdp.Type, cdp.ID, ratio)
k.IncrementTotalPrincipal(ctx, cdp.Type, cdp.GetTotalPrincipal())
}
k.SetNextCdpID(ctx, gs.StartingCdpID)

View File

@ -27,7 +27,7 @@ func NewHandler(k Keeper) sdk.Handler {
}
func handleMsgCreateCDP(ctx sdk.Context, k Keeper, msg MsgCreateCDP) (*sdk.Result, error) {
err := k.AddCdp(ctx, msg.Sender, msg.Collateral, msg.Principal)
err := k.AddCdp(ctx, msg.Sender, msg.Collateral, msg.Principal, msg.CollateralType)
if err != nil {
return nil, err
}
@ -39,7 +39,7 @@ func handleMsgCreateCDP(ctx sdk.Context, k Keeper, msg MsgCreateCDP) (*sdk.Resul
sdk.NewAttribute(sdk.AttributeKeySender, msg.Sender.String()),
),
)
id, _ := k.GetCdpID(ctx, msg.Sender, msg.Collateral.Denom)
id, _ := k.GetCdpID(ctx, msg.Sender, msg.CollateralType)
return &sdk.Result{
Data: GetCdpIDBytes(id),
@ -48,7 +48,7 @@ func handleMsgCreateCDP(ctx sdk.Context, k Keeper, msg MsgCreateCDP) (*sdk.Resul
}
func handleMsgDeposit(ctx sdk.Context, k Keeper, msg MsgDeposit) (*sdk.Result, error) {
err := k.DepositCollateral(ctx, msg.Owner, msg.Depositor, msg.Collateral)
err := k.DepositCollateral(ctx, msg.Owner, msg.Depositor, msg.Collateral, msg.CollateralType)
if err != nil {
return nil, err
}
@ -64,7 +64,7 @@ func handleMsgDeposit(ctx sdk.Context, k Keeper, msg MsgDeposit) (*sdk.Result, e
}
func handleMsgWithdraw(ctx sdk.Context, k Keeper, msg MsgWithdraw) (*sdk.Result, error) {
err := k.WithdrawCollateral(ctx, msg.Owner, msg.Depositor, msg.Collateral)
err := k.WithdrawCollateral(ctx, msg.Owner, msg.Depositor, msg.Collateral, msg.CollateralType)
if err != nil {
return nil, err
}
@ -80,7 +80,7 @@ func handleMsgWithdraw(ctx sdk.Context, k Keeper, msg MsgWithdraw) (*sdk.Result,
}
func handleMsgDrawDebt(ctx sdk.Context, k Keeper, msg MsgDrawDebt) (*sdk.Result, error) {
err := k.AddPrincipal(ctx, msg.Sender, msg.CdpDenom, msg.Principal)
err := k.AddPrincipal(ctx, msg.Sender, msg.CollateralType, msg.Principal)
if err != nil {
return nil, err
}
@ -96,7 +96,7 @@ func handleMsgDrawDebt(ctx sdk.Context, k Keeper, msg MsgDrawDebt) (*sdk.Result,
}
func handleMsgRepayDebt(ctx sdk.Context, k Keeper, msg MsgRepayDebt) (*sdk.Result, error) {
err := k.RepayPrincipal(ctx, msg.Sender, msg.CdpDenom, msg.Payment)
err := k.RepayPrincipal(ctx, msg.Sender, msg.CollateralType, msg.Payment)
if err != nil {
return nil, err
}

View File

@ -47,6 +47,7 @@ func (suite *HandlerTestSuite) TestMsgCreateCdp() {
addrs[0],
c("xrp", 200000000),
c("usdx", 10000000),
"xrp-a",
)
res, err := suite.handler(suite.ctx, msg)
suite.Require().NoError(err)

View File

@ -49,6 +49,7 @@ func NewCDPGenState(asset string, liquidationRatio sdk.Dec) app.GenesisState {
CollateralParams: cdp.CollateralParams{
{
Denom: asset,
Type: asset + "-a",
LiquidationRatio: liquidationRatio,
DebtLimit: sdk.NewInt64Coin("usdx", 1000000000000),
StabilityFee: sdk.MustNewDecFromStr("1.000000001547125958"), // %5 apr
@ -114,6 +115,7 @@ func NewCDPGenStateMulti() app.GenesisState {
CollateralParams: cdp.CollateralParams{
{
Denom: "xrp",
Type: "xrp-a",
LiquidationRatio: sdk.MustNewDecFromStr("2.0"),
DebtLimit: sdk.NewInt64Coin("usdx", 500000000000),
StabilityFee: sdk.MustNewDecFromStr("1.000000001547125958"), // %5 apr
@ -126,6 +128,7 @@ func NewCDPGenStateMulti() app.GenesisState {
},
{
Denom: "btc",
Type: "btc-a",
LiquidationRatio: sdk.MustNewDecFromStr("1.5"),
DebtLimit: sdk.NewInt64Coin("usdx", 500000000000),
StabilityFee: sdk.MustNewDecFromStr("1.000000000782997609"), // %2.5 apr
@ -156,10 +159,10 @@ func NewCDPGenStateMulti() app.GenesisState {
func cdps() (cdps cdp.CDPs) {
_, addrs := app.GeneratePrivKeyAddressPairs(3)
c1 := cdp.NewCDP(uint64(1), addrs[0], sdk.NewCoin("xrp", sdk.NewInt(100000000)), sdk.NewCoin("usdx", sdk.NewInt(8000000)), tmtime.Canonical(time.Now()))
c2 := cdp.NewCDP(uint64(2), addrs[1], sdk.NewCoin("xrp", sdk.NewInt(100000000)), sdk.NewCoin("usdx", sdk.NewInt(10000000)), tmtime.Canonical(time.Now()))
c3 := cdp.NewCDP(uint64(3), addrs[1], sdk.NewCoin("btc", sdk.NewInt(1000000000)), sdk.NewCoin("usdx", sdk.NewInt(10000000)), tmtime.Canonical(time.Now()))
c4 := cdp.NewCDP(uint64(4), addrs[2], sdk.NewCoin("xrp", sdk.NewInt(1000000000)), sdk.NewCoin("usdx", sdk.NewInt(50000000)), tmtime.Canonical(time.Now()))
c1 := cdp.NewCDP(uint64(1), addrs[0], sdk.NewCoin("xrp", sdk.NewInt(100000000)), "xrp-a", sdk.NewCoin("usdx", sdk.NewInt(8000000)), tmtime.Canonical(time.Now()))
c2 := cdp.NewCDP(uint64(2), addrs[1], sdk.NewCoin("xrp", sdk.NewInt(100000000)), "xrp-a", sdk.NewCoin("usdx", sdk.NewInt(10000000)), tmtime.Canonical(time.Now()))
c3 := cdp.NewCDP(uint64(3), addrs[1], sdk.NewCoin("btc", sdk.NewInt(1000000000)), "btc-a", sdk.NewCoin("usdx", sdk.NewInt(10000000)), tmtime.Canonical(time.Now()))
c4 := cdp.NewCDP(uint64(4), addrs[2], sdk.NewCoin("xrp", sdk.NewInt(1000000000)), "xrp-a", sdk.NewCoin("usdx", sdk.NewInt(50000000)), tmtime.Canonical(time.Now()))
cdps = append(cdps, c1, c2, c3, c4)
return
}

View File

@ -12,14 +12,14 @@ const (
)
// AuctionCollateral creates auctions from the input deposits which attempt to raise the corresponding amount of debt
func (k Keeper) AuctionCollateral(ctx sdk.Context, deposits types.Deposits, debt sdk.Int, bidDenom string) error {
func (k Keeper) AuctionCollateral(ctx sdk.Context, deposits types.Deposits, collateralType string, debt sdk.Int, bidDenom string) error {
auctionSize := k.getAuctionSize(ctx, deposits[0].Amount.Denom)
auctionSize := k.getAuctionSize(ctx, collateralType)
totalCollateral := deposits.SumCollateral()
for _, deposit := range deposits {
debtCoveredByDeposit := (sdk.NewDecFromInt(deposit.Amount.Amount).Quo(sdk.NewDecFromInt(totalCollateral))).Mul(sdk.NewDecFromInt(debt)).RoundInt()
err := k.CreateAuctionsFromDeposit(ctx, deposit.Amount, deposit.Depositor, debtCoveredByDeposit, auctionSize, bidDenom)
err := k.CreateAuctionsFromDeposit(ctx, deposit.Amount, collateralType, deposit.Depositor, debtCoveredByDeposit, auctionSize, bidDenom)
if err != nil {
return err
}
@ -29,7 +29,7 @@ func (k Keeper) AuctionCollateral(ctx sdk.Context, deposits types.Deposits, debt
// CreateAuctionsFromDeposit creates auctions from the input deposit
func (k Keeper) CreateAuctionsFromDeposit(
ctx sdk.Context, collateral sdk.Coin, returnAddr sdk.AccAddress, debt, auctionSize sdk.Int,
ctx sdk.Context, collateral sdk.Coin, collateralType string, returnAddr sdk.AccAddress, debt, auctionSize sdk.Int,
principalDenom string) error {
// number of auctions of auctionSize
@ -69,7 +69,7 @@ func (k Keeper) CreateAuctionsFromDeposit(
unallocatedDebt = unallocatedDebt.Sub(sdk.OneInt())
}
penalty := k.ApplyLiquidationPenalty(ctx, collateral.Denom, debtAmount)
penalty := k.ApplyLiquidationPenalty(ctx, collateralType, debtAmount)
_, err := k.auctionKeeper.StartCollateralAuction(
ctx, types.LiquidatorMacc, sdk.NewCoin(collateral.Denom, auctionSize),
@ -95,7 +95,7 @@ func (k Keeper) CreateAuctionsFromDeposit(
unallocatedDebt = unallocatedDebt.Sub(sdk.OneInt())
}
penalty := k.ApplyLiquidationPenalty(ctx, collateral.Denom, lastAuctionDebt)
penalty := k.ApplyLiquidationPenalty(ctx, collateralType, lastAuctionDebt)
_, err := k.auctionKeeper.StartCollateralAuction(
ctx, types.LiquidatorMacc, sdk.NewCoin(collateral.Denom, lastAuctionCollateral),

View File

@ -62,7 +62,7 @@ func (suite *AuctionTestSuite) TestCollateralAuction() {
err := sk.MintCoins(suite.ctx, types.LiquidatorMacc, cs(c("debt", 21000000000), c("bnb", 190000000000)))
suite.Require().NoError(err)
testDeposit := types.NewDeposit(1, suite.addrs[0], c("bnb", 190000000000))
err = suite.keeper.AuctionCollateral(suite.ctx, types.Deposits{testDeposit}, i(21000000000), "usdx")
err = suite.keeper.AuctionCollateral(suite.ctx, types.Deposits{testDeposit}, "bnb-a", i(21000000000), "usdx")
suite.Require().NoError(err)
}

View File

@ -1,7 +1,6 @@
package keeper
import (
"errors"
"fmt"
"sort"
@ -16,13 +15,13 @@ import (
const BaseDigitFactor = 1000000000000000000
// AddCdp adds a cdp for a specific owner and collateral type
func (k Keeper) AddCdp(ctx sdk.Context, owner sdk.AccAddress, collateral sdk.Coin, principal sdk.Coin) error {
func (k Keeper) AddCdp(ctx sdk.Context, owner sdk.AccAddress, collateral sdk.Coin, principal sdk.Coin, collateralType string) error {
// validation
err := k.ValidateCollateral(ctx, collateral)
err := k.ValidateCollateral(ctx, collateral, collateralType)
if err != nil {
return err
}
_, found := k.GetCdpByOwnerAndDenom(ctx, owner, collateral.Denom)
_, found := k.GetCdpByOwnerAndCollateralType(ctx, owner, collateralType)
if found {
return sdkerrors.Wrapf(types.ErrCdpAlreadyExists, "owner %s, denom %s", owner, collateral.Denom)
}
@ -31,18 +30,18 @@ func (k Keeper) AddCdp(ctx sdk.Context, owner sdk.AccAddress, collateral sdk.Coi
return err
}
err = k.ValidateDebtLimit(ctx, collateral.Denom, principal)
err = k.ValidateDebtLimit(ctx, collateralType, principal)
if err != nil {
return err
}
err = k.ValidateCollateralizationRatio(ctx, collateral, principal, sdk.NewCoin(principal.Denom, sdk.ZeroInt()))
err = k.ValidateCollateralizationRatio(ctx, collateral, collateralType, principal, sdk.NewCoin(principal.Denom, sdk.ZeroInt()))
if err != nil {
return err
}
// send coins from the owners account to the cdp module
id := k.GetNextCdpID(ctx)
cdp := types.NewCDP(id, owner, collateral, principal, ctx.BlockHeader().Time)
cdp := types.NewCDP(id, owner, collateral, collateralType, principal, ctx.BlockHeader().Time)
deposit := types.NewDeposit(cdp.ID, owner, collateral)
err = k.supplyKeeper.SendCoinsFromAccountToModule(ctx, owner, types.ModuleName, sdk.NewCoins(collateral))
if err != nil {
@ -66,10 +65,10 @@ func (k Keeper) AddCdp(ctx sdk.Context, owner sdk.AccAddress, collateral sdk.Coi
}
// update total principal for input collateral type
k.IncrementTotalPrincipal(ctx, collateral.Denom, principal)
k.IncrementTotalPrincipal(ctx, collateralType, principal)
// set the cdp, deposit, and indexes in the store
collateralToDebtRatio := k.CalculateCollateralToDebtRatio(ctx, collateral, principal)
collateralToDebtRatio := k.CalculateCollateralToDebtRatio(ctx, collateral, cdp.Type, principal)
err = k.SetCdpAndCollateralRatioIndex(ctx, cdp, collateralToDebtRatio)
if err != nil {
return err
@ -109,7 +108,7 @@ func (k Keeper) SetCdpAndCollateralRatioIndex(ctx sdk.Context, cdp types.CDP, ra
if err != nil {
return err
}
k.IndexCdpByCollateralRatio(ctx, cdp.Collateral.Denom, cdp.ID, ratio)
k.IndexCdpByCollateralRatio(ctx, cdp.Type, cdp.ID, ratio)
return nil
}
@ -126,14 +125,14 @@ func (k Keeper) BurnDebtCoins(ctx sdk.Context, moduleAccount string, denom strin
}
// GetCdpID returns the id of the cdp corresponding to a specific owner and collateral denom
func (k Keeper) GetCdpID(ctx sdk.Context, owner sdk.AccAddress, denom string) (uint64, bool) {
func (k Keeper) GetCdpID(ctx sdk.Context, owner sdk.AccAddress, collateralType string) (uint64, bool) {
cdpIDs, found := k.GetCdpIdsByOwner(ctx, owner)
if !found {
return 0, false
}
for _, id := range cdpIDs {
_, found = k.GetCDP(ctx, denom, id)
_, found = k.GetCDP(ctx, collateralType, id)
if found {
return id, true
}
@ -154,14 +153,14 @@ func (k Keeper) GetCdpIdsByOwner(ctx sdk.Context, owner sdk.AccAddress) ([]uint6
return cdpIDs, true
}
// GetCdpByOwnerAndDenom queries cdps owned by owner and returns the cdp with matching denom
func (k Keeper) GetCdpByOwnerAndDenom(ctx sdk.Context, owner sdk.AccAddress, denom string) (types.CDP, bool) {
// GetCdpByOwnerAndCollateralType queries cdps owned by owner and returns the cdp with matching denom
func (k Keeper) GetCdpByOwnerAndCollateralType(ctx sdk.Context, owner sdk.AccAddress, collateralType string) (types.CDP, bool) {
cdpIDs, found := k.GetCdpIdsByOwner(ctx, owner)
if !found {
return types.CDP{}, false
}
for _, id := range cdpIDs {
cdp, found := k.GetCDP(ctx, denom, id)
cdp, found := k.GetCDP(ctx, collateralType, id)
if found {
return cdp, true
}
@ -170,10 +169,10 @@ func (k Keeper) GetCdpByOwnerAndDenom(ctx sdk.Context, owner sdk.AccAddress, den
}
// GetCDP returns the cdp associated with a particular collateral denom and id
func (k Keeper) GetCDP(ctx sdk.Context, collateralDenom string, cdpID uint64) (types.CDP, bool) {
func (k Keeper) GetCDP(ctx sdk.Context, collateralType string, cdpID uint64) (types.CDP, bool) {
// get store
store := prefix.NewStore(ctx.KVStore(k.key), types.CdpKeyPrefix)
db, found := k.GetDenomPrefix(ctx, collateralDenom)
db, found := k.GetCollateralTypePrefix(ctx, collateralType)
if !found {
return types.CDP{}, false
}
@ -191,7 +190,7 @@ func (k Keeper) GetCDP(ctx sdk.Context, collateralDenom string, cdpID uint64) (t
// SetCDP sets a cdp in the store
func (k Keeper) SetCDP(ctx sdk.Context, cdp types.CDP) error {
store := prefix.NewStore(ctx.KVStore(k.key), types.CdpKeyPrefix)
db, found := k.GetDenomPrefix(ctx, cdp.Collateral.Denom)
db, found := k.GetCollateralTypePrefix(ctx, cdp.Type)
if !found {
return sdkerrors.Wrapf(types.ErrDenomPrefixNotFound, "%s", cdp.Collateral.Denom)
}
@ -203,7 +202,7 @@ func (k Keeper) SetCDP(ctx sdk.Context, cdp types.CDP) error {
// DeleteCDP deletes a cdp from the store
func (k Keeper) DeleteCDP(ctx sdk.Context, cdp types.CDP) error {
store := prefix.NewStore(ctx.KVStore(k.key), types.CdpKeyPrefix)
db, found := k.GetDenomPrefix(ctx, cdp.Collateral.Denom)
db, found := k.GetCollateralTypePrefix(ctx, cdp.Type)
if !found {
return sdkerrors.Wrapf(types.ErrDenomPrefixNotFound, "%s", cdp.Collateral.Denom)
}
@ -221,18 +220,18 @@ func (k Keeper) GetAllCdps(ctx sdk.Context) (cdps types.CDPs) {
return
}
// GetAllCdpsByDenom returns all cdps of a particular collateral type from the store
func (k Keeper) GetAllCdpsByDenom(ctx sdk.Context, denom string) (cdps types.CDPs) {
k.IterateCdpsByDenom(ctx, denom, func(cdp types.CDP) bool {
// GetAllCdpsByCollateralType returns all cdps of a particular collateral type from the store
func (k Keeper) GetAllCdpsByCollateralType(ctx sdk.Context, collateralType string) (cdps types.CDPs) {
k.IterateCdpsByCollateralType(ctx, collateralType, func(cdp types.CDP) bool {
cdps = append(cdps, cdp)
return false
})
return
}
// GetAllCdpsByDenomAndRatio returns all cdps of a particular collateral type and below a certain collateralization ratio
func (k Keeper) GetAllCdpsByDenomAndRatio(ctx sdk.Context, denom string, targetRatio sdk.Dec) (cdps types.CDPs) {
k.IterateCdpsByCollateralRatio(ctx, denom, targetRatio, func(cdp types.CDP) bool {
// GetAllCdpsByCollateralTypeAndRatio returns all cdps of a particular collateral type and below a certain collateralization ratio
func (k Keeper) GetAllCdpsByCollateralTypeAndRatio(ctx sdk.Context, collateralType string, targetRatio sdk.Dec) (cdps types.CDPs) {
k.IterateCdpsByCollateralRatio(ctx, collateralType, targetRatio, func(cdp types.CDP) bool {
cdps = append(cdps, cdp)
return false
})
@ -292,21 +291,21 @@ func (k Keeper) RemoveCdpOwnerIndex(ctx sdk.Context, cdp types.CDP) {
}
// IndexCdpByCollateralRatio sets the cdp id in the store, indexed by the collateral type and collateral to debt ratio
func (k Keeper) IndexCdpByCollateralRatio(ctx sdk.Context, denom string, id uint64, collateralRatio sdk.Dec) {
func (k Keeper) IndexCdpByCollateralRatio(ctx sdk.Context, collateralType string, id uint64, collateralRatio sdk.Dec) {
store := prefix.NewStore(ctx.KVStore(k.key), types.CollateralRatioIndexPrefix)
db, found := k.GetDenomPrefix(ctx, denom)
db, found := k.GetCollateralTypePrefix(ctx, collateralType)
if !found {
panic(fmt.Sprintf("denom %s prefix not found", denom))
panic(fmt.Sprintf("denom %s prefix not found", collateralType))
}
store.Set(types.CollateralRatioKey(db, id, collateralRatio), types.GetCdpIDBytes(id))
}
// RemoveCdpCollateralRatioIndex deletes the cdp id from the store's index of cdps by collateral type and collateral to debt ratio
func (k Keeper) RemoveCdpCollateralRatioIndex(ctx sdk.Context, denom string, id uint64, collateralRatio sdk.Dec) {
func (k Keeper) RemoveCdpCollateralRatioIndex(ctx sdk.Context, collateralType string, id uint64, collateralRatio sdk.Dec) {
store := prefix.NewStore(ctx.KVStore(k.key), types.CollateralRatioIndexPrefix)
db, found := k.GetDenomPrefix(ctx, denom)
db, found := k.GetCollateralTypePrefix(ctx, collateralType)
if !found {
panic(fmt.Sprintf("denom %s prefix not found", denom))
panic(fmt.Sprintf("denom %s prefix not found", collateralType))
}
store.Delete(types.CollateralRatioKey(db, id, collateralRatio))
}
@ -346,8 +345,8 @@ func (k Keeper) SetGovDenom(ctx sdk.Context, denom string) {
}
// ValidateCollateral validates that a collateral is valid for use in cdps
func (k Keeper) ValidateCollateral(ctx sdk.Context, collateral sdk.Coin) error {
cp, found := k.GetCollateral(ctx, collateral.Denom)
func (k Keeper) ValidateCollateral(ctx sdk.Context, collateral sdk.Coin, collateralType string) error {
cp, found := k.GetCollateral(ctx, collateralType)
if !found {
return sdkerrors.Wrap(types.ErrCollateralNotSupported, collateral.Denom)
}
@ -387,12 +386,12 @@ func (k Keeper) ValidatePrincipalDraw(ctx sdk.Context, principal sdk.Coin, expec
}
// ValidateDebtLimit validates that the input debt amount does not exceed the global debt limit or the debt limit for that collateral
func (k Keeper) ValidateDebtLimit(ctx sdk.Context, collateralDenom string, principal sdk.Coin) error {
cp, found := k.GetCollateral(ctx, collateralDenom)
func (k Keeper) ValidateDebtLimit(ctx sdk.Context, collateralType string, principal sdk.Coin) error {
cp, found := k.GetCollateral(ctx, collateralType)
if !found {
return sdkerrors.Wrap(types.ErrCollateralNotSupported, collateralDenom)
return sdkerrors.Wrap(types.ErrCollateralNotSupported, collateralType)
}
totalPrincipal := k.GetTotalPrincipal(ctx, collateralDenom, principal.Denom).Add(principal.Amount)
totalPrincipal := k.GetTotalPrincipal(ctx, collateralType, principal.Denom).Add(principal.Amount)
collateralLimit := cp.DebtLimit.Amount
if totalPrincipal.GT(collateralLimit) {
return sdkerrors.Wrapf(types.ErrExceedsDebtLimit, "debt increase %s > collateral debt limit %s", sdk.NewCoins(sdk.NewCoin(principal.Denom, totalPrincipal)), sdk.NewCoins(sdk.NewCoin(principal.Denom, collateralLimit)))
@ -405,12 +404,12 @@ func (k Keeper) ValidateDebtLimit(ctx sdk.Context, collateralDenom string, princ
}
// ValidateCollateralizationRatio validate that adding the input principal doesn't put the cdp below the liquidation ratio
func (k Keeper) ValidateCollateralizationRatio(ctx sdk.Context, collateral sdk.Coin, principal sdk.Coin, fees sdk.Coin) error {
collateralizationRatio, err := k.CalculateCollateralizationRatio(ctx, collateral, principal, fees, spot)
func (k Keeper) ValidateCollateralizationRatio(ctx sdk.Context, collateral sdk.Coin, collateralType string, principal sdk.Coin, fees sdk.Coin) error {
collateralizationRatio, err := k.CalculateCollateralizationRatio(ctx, collateral, collateralType, principal, fees, spot)
if err != nil {
return err
}
liquidationRatio := k.getLiquidationRatio(ctx, collateral.Denom)
liquidationRatio := k.getLiquidationRatio(ctx, collateralType)
if collateralizationRatio.LT(liquidationRatio) {
return sdkerrors.Wrapf(types.ErrInvalidCollateralRatio, "collateral %s, collateral ratio %s, liquidation ratio %s", collateral.Denom, collateralizationRatio, liquidationRatio)
}
@ -418,21 +417,21 @@ func (k Keeper) ValidateCollateralizationRatio(ctx sdk.Context, collateral sdk.C
}
// CalculateCollateralToDebtRatio returns the collateral to debt ratio of the input collateral and debt amounts
func (k Keeper) CalculateCollateralToDebtRatio(ctx sdk.Context, collateral sdk.Coin, debt sdk.Coin) sdk.Dec {
func (k Keeper) CalculateCollateralToDebtRatio(ctx sdk.Context, collateral sdk.Coin, collateralType string, debt sdk.Coin) sdk.Dec {
debtTotal := k.convertDebtToBaseUnits(ctx, debt)
if debtTotal.IsZero() || debtTotal.GTE(types.MaxSortableDec) {
return types.MaxSortableDec.Sub(sdk.SmallestDec())
}
collateralBaseUnits := k.convertCollateralToBaseUnits(ctx, collateral)
collateralBaseUnits := k.convertCollateralToBaseUnits(ctx, collateral, collateralType)
return collateralBaseUnits.Quo(debtTotal)
}
// LoadAugmentedCDP creates a new augmented CDP from an existing CDP
func (k Keeper) LoadAugmentedCDP(ctx sdk.Context, cdp types.CDP) types.AugmentedCDP {
// calculate collateralization ratio
collateralizationRatio, err := k.CalculateCollateralizationRatio(ctx, cdp.Collateral, cdp.Principal, cdp.AccumulatedFees, liquidation)
collateralizationRatio, err := k.CalculateCollateralizationRatio(ctx, cdp.Collateral, cdp.Type, cdp.Principal, cdp.AccumulatedFees, liquidation)
if err != nil {
return types.AugmentedCDP{CDP: cdp}
}
@ -446,16 +445,16 @@ func (k Keeper) LoadAugmentedCDP(ctx sdk.Context, cdp types.CDP) types.Augmented
}
// CalculateCollateralizationRatio returns the collateralization ratio of the input collateral to the input debt plus fees
func (k Keeper) CalculateCollateralizationRatio(ctx sdk.Context, collateral sdk.Coin, principal sdk.Coin, fees sdk.Coin, pfType pricefeedType) (sdk.Dec, error) {
func (k Keeper) CalculateCollateralizationRatio(ctx sdk.Context, collateral sdk.Coin, collateralType string, principal sdk.Coin, fees sdk.Coin, pfType pricefeedType) (sdk.Dec, error) {
if collateral.IsZero() {
return sdk.ZeroDec(), nil
}
var marketID string
switch pfType {
case spot:
marketID = k.getSpotMarketID(ctx, collateral.Denom)
marketID = k.getSpotMarketID(ctx, collateralType)
case liquidation:
marketID = k.getliquidationMarketID(ctx, collateral.Denom)
marketID = k.getliquidationMarketID(ctx, collateralType)
default:
return sdk.Dec{}, pfType.IsValid()
}
@ -464,7 +463,7 @@ func (k Keeper) CalculateCollateralizationRatio(ctx sdk.Context, collateral sdk.
if err != nil {
return sdk.Dec{}, err
}
collateralBaseUnits := k.convertCollateralToBaseUnits(ctx, collateral)
collateralBaseUnits := k.convertCollateralToBaseUnits(ctx, collateral, collateralType)
collateralValue := collateralBaseUnits.Mul(price.Price)
prinicpalBaseUnits := k.convertDebtToBaseUnits(ctx, principal)
@ -477,14 +476,14 @@ func (k Keeper) CalculateCollateralizationRatio(ctx sdk.Context, collateral sdk.
}
// CalculateCollateralizationRatioFromAbsoluteRatio takes a coin's denom and an absolute ratio and returns the respective collateralization ratio
func (k Keeper) CalculateCollateralizationRatioFromAbsoluteRatio(ctx sdk.Context, collateralDenom string, absoluteRatio sdk.Dec, pfType pricefeedType) (sdk.Dec, error) {
func (k Keeper) CalculateCollateralizationRatioFromAbsoluteRatio(ctx sdk.Context, collateralType string, absoluteRatio sdk.Dec, pfType pricefeedType) (sdk.Dec, error) {
// get price of collateral
var marketID string
switch pfType {
case spot:
marketID = k.getSpotMarketID(ctx, collateralDenom)
marketID = k.getSpotMarketID(ctx, collateralType)
case liquidation:
marketID = k.getliquidationMarketID(ctx, collateralDenom)
marketID = k.getliquidationMarketID(ctx, collateralType)
default:
return sdk.Dec{}, pfType.IsValid()
}
@ -525,8 +524,8 @@ func (k Keeper) UpdatePricefeedStatus(ctx sdk.Context, marketID string) (ok bool
}
// converts the input collateral to base units (ie multiplies the input by 10^(-ConversionFactor))
func (k Keeper) convertCollateralToBaseUnits(ctx sdk.Context, collateral sdk.Coin) (baseUnits sdk.Dec) {
cp, _ := k.GetCollateral(ctx, collateral.Denom)
func (k Keeper) convertCollateralToBaseUnits(ctx sdk.Context, collateral sdk.Coin, collateralType string) (baseUnits sdk.Dec) {
cp, _ := k.GetCollateral(ctx, collateralType)
return sdk.NewDecFromInt(collateral.Amount).Mul(sdk.NewDecFromIntWithPrec(sdk.OneInt(), cp.ConversionFactor.Int64()))
}
@ -548,5 +547,5 @@ func (pft pricefeedType) IsValid() error {
case spot, liquidation:
return nil
}
return errors.New(fmt.Sprintf("invalid pricefeed type: %s", pft))
return fmt.Errorf("invalid pricefeed type: %s", pft)
}

View File

@ -47,17 +47,17 @@ func (suite *CdpTestSuite) TestAddCdp() {
acc := ak.NewAccountWithAddress(suite.ctx, addrs[0])
acc.SetCoins(cs(c("xrp", 200000000), c("btc", 500000000)))
ak.SetAccount(suite.ctx, acc)
err := suite.keeper.AddCdp(suite.ctx, addrs[0], c("xrp", 200000000), c("usdx", 26000000))
err := suite.keeper.AddCdp(suite.ctx, addrs[0], c("xrp", 200000000), c("usdx", 26000000), "xrp-a")
suite.Require().True(errors.Is(err, types.ErrInvalidCollateralRatio))
err = suite.keeper.AddCdp(suite.ctx, addrs[0], c("xrp", 500000000), c("usdx", 26000000))
err = suite.keeper.AddCdp(suite.ctx, addrs[0], c("xrp", 500000000), c("usdx", 26000000), "xrp-a")
suite.Error(err) // insufficient balance
err = suite.keeper.AddCdp(suite.ctx, addrs[0], c("xrp", 200000000), c("xusd", 10000000))
err = suite.keeper.AddCdp(suite.ctx, addrs[0], c("xrp", 200000000), c("xusd", 10000000), "xrp-a")
suite.Require().True(errors.Is(err, types.ErrDebtNotSupported))
acc2 := ak.NewAccountWithAddress(suite.ctx, addrs[1])
acc2.SetCoins(cs(c("btc", 500000000000)))
ak.SetAccount(suite.ctx, acc2)
err = suite.keeper.AddCdp(suite.ctx, addrs[1], c("btc", 500000000000), c("usdx", 500000000001))
err = suite.keeper.AddCdp(suite.ctx, addrs[1], c("btc", 500000000000), c("usdx", 500000000001), "btc-a")
suite.Require().True(errors.Is(err, types.ErrExceedsDebtLimit))
ctx := suite.ctx.WithBlockTime(suite.ctx.BlockTime().Add(time.Hour * 2))
@ -66,18 +66,18 @@ func (suite *CdpTestSuite) TestAddCdp() {
suite.Error(err)
ok := suite.keeper.UpdatePricefeedStatus(ctx, "xrp:usd")
suite.False(ok)
err = suite.keeper.AddCdp(ctx, addrs[0], c("xrp", 100000000), c("usdx", 10000000))
err = suite.keeper.AddCdp(ctx, addrs[0], c("xrp", 100000000), c("usdx", 10000000), "xrp-a")
suite.Require().True(errors.Is(err, types.ErrPricefeedDown))
err = pk.SetCurrentPrices(suite.ctx, "xrp:usd")
ok = suite.keeper.UpdatePricefeedStatus(suite.ctx, "xrp:usd")
suite.True(ok)
suite.NoError(err)
err = suite.keeper.AddCdp(suite.ctx, addrs[0], c("xrp", 100000000), c("usdx", 10000000))
err = suite.keeper.AddCdp(suite.ctx, addrs[0], c("xrp", 100000000), c("usdx", 10000000), "xrp-a")
suite.NoError(err)
id := suite.keeper.GetNextCdpID(suite.ctx)
suite.Equal(uint64(2), id)
tp := suite.keeper.GetTotalPrincipal(suite.ctx, "xrp", "usdx")
tp := suite.keeper.GetTotalPrincipal(suite.ctx, "xrp-a", "usdx")
suite.Equal(i(10000000), tp)
sk := suite.app.GetSupplyKeeper()
macc := sk.GetModuleAccount(suite.ctx, types.ModuleName)
@ -85,30 +85,30 @@ func (suite *CdpTestSuite) TestAddCdp() {
acc = ak.GetAccount(suite.ctx, addrs[0])
suite.Equal(cs(c("usdx", 10000000), c("xrp", 100000000), c("btc", 500000000)), acc.GetCoins())
err = suite.keeper.AddCdp(suite.ctx, addrs[0], c("btc", 500000000), c("usdx", 26667000000))
err = suite.keeper.AddCdp(suite.ctx, addrs[0], c("btc", 500000000), c("usdx", 26667000000), "btc-a")
suite.Require().True(errors.Is(err, types.ErrInvalidCollateralRatio))
err = suite.keeper.AddCdp(suite.ctx, addrs[0], c("btc", 500000000), c("usdx", 100000000))
err = suite.keeper.AddCdp(suite.ctx, addrs[0], c("btc", 500000000), c("usdx", 100000000), "btc-a")
suite.NoError(err)
id = suite.keeper.GetNextCdpID(suite.ctx)
suite.Equal(uint64(3), id)
tp = suite.keeper.GetTotalPrincipal(suite.ctx, "btc", "usdx")
tp = suite.keeper.GetTotalPrincipal(suite.ctx, "btc-a", "usdx")
suite.Equal(i(100000000), tp)
macc = sk.GetModuleAccount(suite.ctx, types.ModuleName)
suite.Equal(cs(c("debt", 110000000), c("xrp", 100000000), c("btc", 500000000)), macc.GetCoins())
acc = ak.GetAccount(suite.ctx, addrs[0])
suite.Equal(cs(c("usdx", 110000000), c("xrp", 100000000)), acc.GetCoins())
err = suite.keeper.AddCdp(suite.ctx, addrs[0], c("lol", 100), c("usdx", 10))
err = suite.keeper.AddCdp(suite.ctx, addrs[0], c("lol", 100), c("usdx", 10), "lol-a")
suite.Require().True(errors.Is(err, types.ErrCollateralNotSupported))
err = suite.keeper.AddCdp(suite.ctx, addrs[0], c("xrp", 100), c("usdx", 10))
err = suite.keeper.AddCdp(suite.ctx, addrs[0], c("xrp", 100), c("usdx", 10), "xrp-a")
suite.Require().True(errors.Is(err, types.ErrCdpAlreadyExists))
}
func (suite *CdpTestSuite) TestGetSetDenomByte() {
_, found := suite.keeper.GetDenomPrefix(suite.ctx, "lol")
func (suite *CdpTestSuite) TestGetSetCollateralTypeByte() {
_, found := suite.keeper.GetCollateralTypePrefix(suite.ctx, "lol-a")
suite.False(found)
db, found := suite.keeper.GetDenomPrefix(suite.ctx, "xrp")
db, found := suite.keeper.GetCollateralTypePrefix(suite.ctx, "xrp-a")
suite.True(found)
suite.Equal(byte(0x20), db)
}
@ -129,66 +129,66 @@ func (suite *CdpTestSuite) TestGetNextCdpID() {
func (suite *CdpTestSuite) TestGetSetCdp() {
_, addrs := app.GeneratePrivKeyAddressPairs(1)
cdp := types.NewCDP(types.DefaultCdpStartingID, addrs[0], c("xrp", 1), c("usdx", 1), tmtime.Canonical(time.Now()))
cdp := types.NewCDP(types.DefaultCdpStartingID, addrs[0], c("xrp", 1), "xrp-a", c("usdx", 1), tmtime.Canonical(time.Now()))
err := suite.keeper.SetCDP(suite.ctx, cdp)
suite.NoError(err)
t, found := suite.keeper.GetCDP(suite.ctx, "xrp", types.DefaultCdpStartingID)
t, found := suite.keeper.GetCDP(suite.ctx, "xrp-a", types.DefaultCdpStartingID)
suite.True(found)
suite.Equal(cdp, t)
_, found = suite.keeper.GetCDP(suite.ctx, "xrp", uint64(2))
_, found = suite.keeper.GetCDP(suite.ctx, "xrp-a", uint64(2))
suite.False(found)
suite.keeper.DeleteCDP(suite.ctx, cdp)
_, found = suite.keeper.GetCDP(suite.ctx, "btc", types.DefaultCdpStartingID)
_, found = suite.keeper.GetCDP(suite.ctx, "btc-a", types.DefaultCdpStartingID)
suite.False(found)
}
func (suite *CdpTestSuite) TestGetSetCdpId() {
_, addrs := app.GeneratePrivKeyAddressPairs(2)
cdp := types.NewCDP(types.DefaultCdpStartingID, addrs[0], c("xrp", 1), c("usdx", 1), tmtime.Canonical(time.Now()))
cdp := types.NewCDP(types.DefaultCdpStartingID, addrs[0], c("xrp", 1), "xrp-a", c("usdx", 1), tmtime.Canonical(time.Now()))
err := suite.keeper.SetCDP(suite.ctx, cdp)
suite.NoError(err)
suite.keeper.IndexCdpByOwner(suite.ctx, cdp)
id, found := suite.keeper.GetCdpID(suite.ctx, addrs[0], "xrp")
id, found := suite.keeper.GetCdpID(suite.ctx, addrs[0], "xrp-a")
suite.True(found)
suite.Equal(types.DefaultCdpStartingID, id)
_, found = suite.keeper.GetCdpID(suite.ctx, addrs[0], "lol")
_, found = suite.keeper.GetCdpID(suite.ctx, addrs[0], "lol-a")
suite.False(found)
_, found = suite.keeper.GetCdpID(suite.ctx, addrs[1], "xrp")
_, found = suite.keeper.GetCdpID(suite.ctx, addrs[1], "xrp-a")
suite.False(found)
}
func (suite *CdpTestSuite) TestGetSetCdpByOwnerAndDenom() {
func (suite *CdpTestSuite) TestGetSetCdpByOwnerAndCollateralType() {
_, addrs := app.GeneratePrivKeyAddressPairs(2)
cdp := types.NewCDP(types.DefaultCdpStartingID, addrs[0], c("xrp", 1), c("usdx", 1), tmtime.Canonical(time.Now()))
cdp := types.NewCDP(types.DefaultCdpStartingID, addrs[0], c("xrp", 1), "xrp-a", c("usdx", 1), tmtime.Canonical(time.Now()))
err := suite.keeper.SetCDP(suite.ctx, cdp)
suite.NoError(err)
suite.keeper.IndexCdpByOwner(suite.ctx, cdp)
t, found := suite.keeper.GetCdpByOwnerAndDenom(suite.ctx, addrs[0], "xrp")
t, found := suite.keeper.GetCdpByOwnerAndCollateralType(suite.ctx, addrs[0], "xrp-a")
suite.True(found)
suite.Equal(cdp, t)
_, found = suite.keeper.GetCdpByOwnerAndDenom(suite.ctx, addrs[0], "lol")
_, found = suite.keeper.GetCdpByOwnerAndCollateralType(suite.ctx, addrs[0], "lol-a")
suite.False(found)
_, found = suite.keeper.GetCdpByOwnerAndDenom(suite.ctx, addrs[1], "xrp")
_, found = suite.keeper.GetCdpByOwnerAndCollateralType(suite.ctx, addrs[1], "xrp-a")
suite.False(found)
suite.NotPanics(func() { suite.keeper.IndexCdpByOwner(suite.ctx, cdp) })
}
func (suite *CdpTestSuite) TestCalculateCollateralToDebtRatio() {
_, addrs := app.GeneratePrivKeyAddressPairs(1)
cdp := types.NewCDP(types.DefaultCdpStartingID, addrs[0], c("xrp", 3), c("usdx", 1), tmtime.Canonical(time.Now()))
cr := suite.keeper.CalculateCollateralToDebtRatio(suite.ctx, cdp.Collateral, cdp.Principal)
cdp := types.NewCDP(types.DefaultCdpStartingID, addrs[0], c("xrp", 3), "xrp-a", c("usdx", 1), tmtime.Canonical(time.Now()))
cr := suite.keeper.CalculateCollateralToDebtRatio(suite.ctx, cdp.Collateral, cdp.Type, cdp.Principal)
suite.Equal(sdk.MustNewDecFromStr("3.0"), cr)
cdp = types.NewCDP(types.DefaultCdpStartingID, addrs[0], c("xrp", 1), c("usdx", 2), tmtime.Canonical(time.Now()))
cr = suite.keeper.CalculateCollateralToDebtRatio(suite.ctx, cdp.Collateral, cdp.Principal)
cdp = types.NewCDP(types.DefaultCdpStartingID, addrs[0], c("xrp", 1), "xrp-a", c("usdx", 2), tmtime.Canonical(time.Now()))
cr = suite.keeper.CalculateCollateralToDebtRatio(suite.ctx, cdp.Collateral, cdp.Type, cdp.Principal)
suite.Equal(sdk.MustNewDecFromStr("0.5"), cr)
}
func (suite *CdpTestSuite) TestSetCdpByCollateralRatio() {
_, addrs := app.GeneratePrivKeyAddressPairs(1)
cdp := types.NewCDP(types.DefaultCdpStartingID, addrs[0], c("xrp", 3), c("usdx", 1), tmtime.Canonical(time.Now()))
cr := suite.keeper.CalculateCollateralToDebtRatio(suite.ctx, cdp.Collateral, cdp.Principal)
suite.NotPanics(func() { suite.keeper.IndexCdpByCollateralRatio(suite.ctx, cdp.Collateral.Denom, cdp.ID, cr) })
cdp := types.NewCDP(types.DefaultCdpStartingID, addrs[0], c("xrp", 3), "xrp-a", c("usdx", 1), tmtime.Canonical(time.Now()))
cr := suite.keeper.CalculateCollateralToDebtRatio(suite.ctx, cdp.Collateral, cdp.Type, cdp.Principal)
suite.NotPanics(func() { suite.keeper.IndexCdpByCollateralRatio(suite.ctx, cdp.Type, cdp.ID, cr) })
}
func (suite *CdpTestSuite) TestIterateCdps() {
@ -197,29 +197,29 @@ func (suite *CdpTestSuite) TestIterateCdps() {
err := suite.keeper.SetCDP(suite.ctx, c)
suite.NoError(err)
suite.keeper.IndexCdpByOwner(suite.ctx, c)
cr := suite.keeper.CalculateCollateralToDebtRatio(suite.ctx, c.Collateral, c.Principal)
suite.keeper.IndexCdpByCollateralRatio(suite.ctx, c.Collateral.Denom, c.ID, cr)
cr := suite.keeper.CalculateCollateralToDebtRatio(suite.ctx, c.Collateral, c.Type, c.Principal)
suite.keeper.IndexCdpByCollateralRatio(suite.ctx, c.Type, c.ID, cr)
}
t := suite.keeper.GetAllCdps(suite.ctx)
suite.Equal(4, len(t))
}
func (suite *CdpTestSuite) TestIterateCdpsByDenom() {
func (suite *CdpTestSuite) TestIterateCdpsByCollateralType() {
cdps := cdps()
for _, c := range cdps {
err := suite.keeper.SetCDP(suite.ctx, c)
suite.NoError(err)
suite.keeper.IndexCdpByOwner(suite.ctx, c)
cr := suite.keeper.CalculateCollateralToDebtRatio(suite.ctx, c.Collateral, c.Principal)
suite.keeper.IndexCdpByCollateralRatio(suite.ctx, c.Collateral.Denom, c.ID, cr)
cr := suite.keeper.CalculateCollateralToDebtRatio(suite.ctx, c.Collateral, c.Type, c.Principal)
suite.keeper.IndexCdpByCollateralRatio(suite.ctx, c.Type, c.ID, cr)
}
xrpCdps := suite.keeper.GetAllCdpsByDenom(suite.ctx, "xrp")
xrpCdps := suite.keeper.GetAllCdpsByCollateralType(suite.ctx, "xrp-a")
suite.Equal(3, len(xrpCdps))
btcCdps := suite.keeper.GetAllCdpsByDenom(suite.ctx, "btc")
btcCdps := suite.keeper.GetAllCdpsByCollateralType(suite.ctx, "btc-a")
suite.Equal(1, len(btcCdps))
suite.keeper.DeleteCDP(suite.ctx, cdps[0])
suite.keeper.RemoveCdpOwnerIndex(suite.ctx, cdps[0])
xrpCdps = suite.keeper.GetAllCdpsByDenom(suite.ctx, "xrp")
xrpCdps = suite.keeper.GetAllCdpsByCollateralType(suite.ctx, "xrp-a")
suite.Equal(2, len(xrpCdps))
suite.keeper.DeleteCDP(suite.ctx, cdps[1])
suite.keeper.RemoveCdpOwnerIndex(suite.ctx, cdps[1])
@ -235,31 +235,31 @@ func (suite *CdpTestSuite) TestIterateCdpsByCollateralRatio() {
err := suite.keeper.SetCDP(suite.ctx, c)
suite.NoError(err)
suite.keeper.IndexCdpByOwner(suite.ctx, c)
cr := suite.keeper.CalculateCollateralToDebtRatio(suite.ctx, c.Collateral, c.Principal)
suite.keeper.IndexCdpByCollateralRatio(suite.ctx, c.Collateral.Denom, c.ID, cr)
cr := suite.keeper.CalculateCollateralToDebtRatio(suite.ctx, c.Collateral, c.Type, c.Principal)
suite.keeper.IndexCdpByCollateralRatio(suite.ctx, c.Type, c.ID, cr)
}
xrpCdps := suite.keeper.GetAllCdpsByDenomAndRatio(suite.ctx, "xrp", d("1.25"))
xrpCdps := suite.keeper.GetAllCdpsByCollateralTypeAndRatio(suite.ctx, "xrp-a", d("1.25"))
suite.Equal(0, len(xrpCdps))
xrpCdps = suite.keeper.GetAllCdpsByDenomAndRatio(suite.ctx, "xrp", d("1.25").Add(sdk.SmallestDec()))
xrpCdps = suite.keeper.GetAllCdpsByCollateralTypeAndRatio(suite.ctx, "xrp-a", d("1.25").Add(sdk.SmallestDec()))
suite.Equal(1, len(xrpCdps))
xrpCdps = suite.keeper.GetAllCdpsByDenomAndRatio(suite.ctx, "xrp", d("2.0").Add(sdk.SmallestDec()))
xrpCdps = suite.keeper.GetAllCdpsByCollateralTypeAndRatio(suite.ctx, "xrp-a", d("2.0").Add(sdk.SmallestDec()))
suite.Equal(2, len(xrpCdps))
xrpCdps = suite.keeper.GetAllCdpsByDenomAndRatio(suite.ctx, "xrp", d("100.0").Add(sdk.SmallestDec()))
xrpCdps = suite.keeper.GetAllCdpsByCollateralTypeAndRatio(suite.ctx, "xrp-a", d("100.0").Add(sdk.SmallestDec()))
suite.Equal(3, len(xrpCdps))
suite.keeper.DeleteCDP(suite.ctx, cdps[0])
suite.keeper.RemoveCdpOwnerIndex(suite.ctx, cdps[0])
cr := suite.keeper.CalculateCollateralToDebtRatio(suite.ctx, cdps[0].Collateral, cdps[0].Principal)
suite.keeper.RemoveCdpCollateralRatioIndex(suite.ctx, cdps[0].Collateral.Denom, cdps[0].ID, cr)
xrpCdps = suite.keeper.GetAllCdpsByDenomAndRatio(suite.ctx, "xrp", d("2.0").Add(sdk.SmallestDec()))
cr := suite.keeper.CalculateCollateralToDebtRatio(suite.ctx, cdps[0].Collateral, cdps[0].Type, cdps[0].Principal)
suite.keeper.RemoveCdpCollateralRatioIndex(suite.ctx, cdps[0].Type, cdps[0].ID, cr)
xrpCdps = suite.keeper.GetAllCdpsByCollateralTypeAndRatio(suite.ctx, "xrp-a", d("2.0").Add(sdk.SmallestDec()))
suite.Equal(1, len(xrpCdps))
}
func (suite *CdpTestSuite) TestValidateCollateral() {
c := sdk.NewCoin("xrp", sdk.NewInt(1))
err := suite.keeper.ValidateCollateral(suite.ctx, c)
err := suite.keeper.ValidateCollateral(suite.ctx, c, "xrp-a")
suite.NoError(err)
c = sdk.NewCoin("lol", sdk.NewInt(1))
err = suite.keeper.ValidateCollateral(suite.ctx, c)
err = suite.keeper.ValidateCollateral(suite.ctx, c, "lol-a")
suite.Require().True(errors.Is(err, types.ErrCollateralNotSupported))
}
@ -271,10 +271,10 @@ func (suite *CdpTestSuite) TestValidatePrincipal() {
err = suite.keeper.ValidatePrincipalAdd(suite.ctx, d)
suite.Require().True(errors.Is(err, types.ErrDebtNotSupported))
d = sdk.NewCoin("usdx", sdk.NewInt(1000000000001))
err = suite.keeper.ValidateDebtLimit(suite.ctx, "xrp", d)
err = suite.keeper.ValidateDebtLimit(suite.ctx, "xrp-a", d)
suite.Require().True(errors.Is(err, types.ErrExceedsDebtLimit))
d = sdk.NewCoin("usdx", sdk.NewInt(100000000))
err = suite.keeper.ValidateDebtLimit(suite.ctx, "xrp", d)
err = suite.keeper.ValidateDebtLimit(suite.ctx, "xrp-a", d)
suite.NoError(err)
}
@ -283,13 +283,13 @@ func (suite *CdpTestSuite) TestCalculateCollateralizationRatio() {
err := suite.keeper.SetCDP(suite.ctx, c)
suite.NoError(err)
suite.keeper.IndexCdpByOwner(suite.ctx, c)
cr := suite.keeper.CalculateCollateralToDebtRatio(suite.ctx, c.Collateral, c.Principal)
suite.keeper.IndexCdpByCollateralRatio(suite.ctx, c.Collateral.Denom, c.ID, cr)
cr, err = suite.keeper.CalculateCollateralizationRatio(suite.ctx, c.Collateral, c.Principal, c.AccumulatedFees, "spot")
cr := suite.keeper.CalculateCollateralToDebtRatio(suite.ctx, c.Collateral, c.Type, c.Principal)
suite.keeper.IndexCdpByCollateralRatio(suite.ctx, c.Type, c.ID, cr)
cr, err = suite.keeper.CalculateCollateralizationRatio(suite.ctx, c.Collateral, c.Type, c.Principal, c.AccumulatedFees, "spot")
suite.NoError(err)
suite.Equal(d("2.5"), cr)
c.AccumulatedFees = sdk.NewCoin("usdx", i(10000000))
cr, err = suite.keeper.CalculateCollateralizationRatio(suite.ctx, c.Collateral, c.Principal, c.AccumulatedFees, "spot")
cr, err = suite.keeper.CalculateCollateralizationRatio(suite.ctx, c.Collateral, c.Type, c.Principal, c.AccumulatedFees, "spot")
suite.NoError(err)
suite.Equal(d("1.25"), cr)
}

View File

@ -11,15 +11,15 @@ import (
)
// DepositCollateral adds collateral to a cdp
func (k Keeper) DepositCollateral(ctx sdk.Context, owner, depositor sdk.AccAddress, collateral sdk.Coin) error {
func (k Keeper) DepositCollateral(ctx sdk.Context, owner, depositor sdk.AccAddress, collateral sdk.Coin, collateralType string) error {
// check that collateral exists and has a functioning pricefeed
err := k.ValidateCollateral(ctx, collateral)
err := k.ValidateCollateral(ctx, collateral, collateralType)
if err != nil {
return err
}
cdp, found := k.GetCdpByOwnerAndDenom(ctx, owner, collateral.Denom)
cdp, found := k.GetCdpByOwnerAndCollateralType(ctx, owner, collateralType)
if !found {
return sdkerrors.Wrapf(types.ErrCdpNotFound, "owner %s, collateral %s", owner, collateral.Denom)
return sdkerrors.Wrapf(types.ErrCdpNotFound, "owner %s, collateral %s", owner, collateralType)
}
deposit, found := k.GetDeposit(ctx, cdp.ID, depositor)
@ -42,37 +42,37 @@ func (k Keeper) DepositCollateral(ctx sdk.Context, owner, depositor sdk.AccAddre
k.SetDeposit(ctx, deposit)
oldCollateralToDebtRatio := k.CalculateCollateralToDebtRatio(ctx, cdp.Collateral, cdp.GetTotalPrincipal())
k.RemoveCdpCollateralRatioIndex(ctx, cdp.Collateral.Denom, cdp.ID, oldCollateralToDebtRatio)
oldCollateralToDebtRatio := k.CalculateCollateralToDebtRatio(ctx, cdp.Collateral, cdp.Type, cdp.GetTotalPrincipal())
k.RemoveCdpCollateralRatioIndex(ctx, cdp.Type, cdp.ID, oldCollateralToDebtRatio)
cdp.Collateral = cdp.Collateral.Add(collateral)
collateralToDebtRatio := k.CalculateCollateralToDebtRatio(ctx, cdp.Collateral, cdp.GetTotalPrincipal())
collateralToDebtRatio := k.CalculateCollateralToDebtRatio(ctx, cdp.Collateral, cdp.Type, cdp.GetTotalPrincipal())
return k.SetCdpAndCollateralRatioIndex(ctx, cdp, collateralToDebtRatio)
}
// WithdrawCollateral removes collateral from a cdp if it does not put the cdp below the liquidation ratio
func (k Keeper) WithdrawCollateral(ctx sdk.Context, owner, depositor sdk.AccAddress, collateral sdk.Coin) error {
err := k.ValidateCollateral(ctx, collateral)
func (k Keeper) WithdrawCollateral(ctx sdk.Context, owner, depositor sdk.AccAddress, collateral sdk.Coin, collateralType string) error {
err := k.ValidateCollateral(ctx, collateral, collateralType)
if err != nil {
return err
}
cdp, found := k.GetCdpByOwnerAndDenom(ctx, owner, collateral.Denom)
cdp, found := k.GetCdpByOwnerAndCollateralType(ctx, owner, collateralType)
if !found {
return sdkerrors.Wrapf(types.ErrCdpNotFound, "owner %s, collateral %s", owner, collateral.Denom)
}
deposit, found := k.GetDeposit(ctx, cdp.ID, depositor)
if !found {
return sdkerrors.Wrapf(types.ErrDepositNotFound, "depositor %s, collateral %s", depositor, collateral.Denom)
return sdkerrors.Wrapf(types.ErrDepositNotFound, "depositor %s, collateral %s %s", depositor, collateral.Denom, collateralType)
}
if collateral.Amount.GT(deposit.Amount.Amount) {
return sdkerrors.Wrapf(types.ErrInvalidWithdrawAmount, "collateral %s, deposit %s", collateral, deposit.Amount)
}
collateralizationRatio, err := k.CalculateCollateralizationRatio(ctx, cdp.Collateral.Sub(collateral), cdp.Principal, cdp.AccumulatedFees, spot)
collateralizationRatio, err := k.CalculateCollateralizationRatio(ctx, cdp.Collateral.Sub(collateral), cdp.Type, cdp.Principal, cdp.AccumulatedFees, spot)
if err != nil {
return err
}
liquidationRatio := k.getLiquidationRatio(ctx, collateral.Denom)
liquidationRatio := k.getLiquidationRatio(ctx, cdp.Type)
if collateralizationRatio.LT(liquidationRatio) {
return sdkerrors.Wrapf(types.ErrInvalidCollateralRatio, "collateral %s, collateral ratio %s, liquidation ration %s", collateral.Denom, collateralizationRatio, liquidationRatio)
}
@ -88,11 +88,11 @@ func (k Keeper) WithdrawCollateral(ctx sdk.Context, owner, depositor sdk.AccAddr
if err != nil {
panic(err)
}
oldCollateralToDebtRatio := k.CalculateCollateralToDebtRatio(ctx, cdp.Collateral, cdp.GetTotalPrincipal())
k.RemoveCdpCollateralRatioIndex(ctx, cdp.Collateral.Denom, cdp.ID, oldCollateralToDebtRatio)
oldCollateralToDebtRatio := k.CalculateCollateralToDebtRatio(ctx, cdp.Collateral, cdp.Type, cdp.GetTotalPrincipal())
k.RemoveCdpCollateralRatioIndex(ctx, cdp.Type, cdp.ID, oldCollateralToDebtRatio)
cdp.Collateral = cdp.Collateral.Sub(collateral)
collateralToDebtRatio := k.CalculateCollateralToDebtRatio(ctx, cdp.Collateral, cdp.GetTotalPrincipal())
collateralToDebtRatio := k.CalculateCollateralToDebtRatio(ctx, cdp.Collateral, cdp.Type, cdp.GetTotalPrincipal())
err = k.SetCdpAndCollateralRatioIndex(ctx, cdp, collateralToDebtRatio)
if err != nil {
return err

View File

@ -44,7 +44,7 @@ func (suite *DepositTestSuite) SetupTest() {
suite.keeper = keeper
suite.ctx = ctx
suite.addrs = addrs
err := suite.keeper.AddCdp(suite.ctx, addrs[0], c("xrp", 400000000), c("usdx", 10000000))
err := suite.keeper.AddCdp(suite.ctx, addrs[0], c("xrp", 400000000), c("usdx", 10000000), "xrp-a")
suite.NoError(err)
}
@ -64,7 +64,7 @@ func (suite *DepositTestSuite) TestGetSetDeposit() {
}
func (suite *DepositTestSuite) TestDepositCollateral() {
err := suite.keeper.DepositCollateral(suite.ctx, suite.addrs[0], suite.addrs[0], c("xrp", 10000000))
err := suite.keeper.DepositCollateral(suite.ctx, suite.addrs[0], suite.addrs[0], c("xrp", 10000000), "xrp-a")
suite.NoError(err)
d, found := suite.keeper.GetDeposit(suite.ctx, uint64(1), suite.addrs[0])
suite.True(found)
@ -73,19 +73,19 @@ func (suite *DepositTestSuite) TestDepositCollateral() {
ds := suite.keeper.GetDeposits(suite.ctx, uint64(1))
suite.Equal(1, len(ds))
suite.True(ds[0].Equals(td))
cd, _ := suite.keeper.GetCDP(suite.ctx, "xrp", uint64(1))
cd, _ := suite.keeper.GetCDP(suite.ctx, "xrp-a", uint64(1))
suite.Equal(c("xrp", 410000000), cd.Collateral)
ak := suite.app.GetAccountKeeper()
acc := ak.GetAccount(suite.ctx, suite.addrs[0])
suite.Equal(i(90000000), acc.GetCoins().AmountOf("xrp"))
err = suite.keeper.DepositCollateral(suite.ctx, suite.addrs[0], suite.addrs[0], c("btc", 1))
err = suite.keeper.DepositCollateral(suite.ctx, suite.addrs[0], suite.addrs[0], c("btc", 1), "btc-a")
suite.Require().True(errors.Is(err, types.ErrCdpNotFound))
err = suite.keeper.DepositCollateral(suite.ctx, suite.addrs[1], suite.addrs[0], c("xrp", 1))
err = suite.keeper.DepositCollateral(suite.ctx, suite.addrs[1], suite.addrs[0], c("xrp", 1), "xrp-a")
suite.Require().True(errors.Is(err, types.ErrCdpNotFound))
err = suite.keeper.DepositCollateral(suite.ctx, suite.addrs[0], suite.addrs[1], c("xrp", 10000000))
err = suite.keeper.DepositCollateral(suite.ctx, suite.addrs[0], suite.addrs[1], c("xrp", 10000000), "xrp-a")
suite.NoError(err)
d, found = suite.keeper.GetDeposit(suite.ctx, uint64(1), suite.addrs[1])
suite.True(found)
@ -97,21 +97,21 @@ func (suite *DepositTestSuite) TestDepositCollateral() {
}
func (suite *DepositTestSuite) TestWithdrawCollateral() {
err := suite.keeper.WithdrawCollateral(suite.ctx, suite.addrs[0], suite.addrs[0], c("xrp", 400000000))
err := suite.keeper.WithdrawCollateral(suite.ctx, suite.addrs[0], suite.addrs[0], c("xrp", 400000000), "xrp-a")
suite.Require().True(errors.Is(err, types.ErrInvalidCollateralRatio))
err = suite.keeper.WithdrawCollateral(suite.ctx, suite.addrs[0], suite.addrs[0], c("xrp", 321000000))
err = suite.keeper.WithdrawCollateral(suite.ctx, suite.addrs[0], suite.addrs[0], c("xrp", 321000000), "xrp-a")
suite.Require().True(errors.Is(err, types.ErrInvalidCollateralRatio))
err = suite.keeper.WithdrawCollateral(suite.ctx, suite.addrs[1], suite.addrs[0], c("xrp", 10000000))
err = suite.keeper.WithdrawCollateral(suite.ctx, suite.addrs[1], suite.addrs[0], c("xrp", 10000000), "xrp-a")
suite.Require().True(errors.Is(err, types.ErrCdpNotFound))
cd, _ := suite.keeper.GetCDP(suite.ctx, "xrp", uint64(1))
cd, _ := suite.keeper.GetCDP(suite.ctx, "xrp-a", uint64(1))
cd.AccumulatedFees = c("usdx", 1)
err = suite.keeper.SetCDP(suite.ctx, cd)
suite.NoError(err)
err = suite.keeper.WithdrawCollateral(suite.ctx, suite.addrs[0], suite.addrs[0], c("xrp", 320000000))
err = suite.keeper.WithdrawCollateral(suite.ctx, suite.addrs[0], suite.addrs[0], c("xrp", 320000000), "xrp-a")
suite.Require().True(errors.Is(err, types.ErrInvalidCollateralRatio))
err = suite.keeper.WithdrawCollateral(suite.ctx, suite.addrs[0], suite.addrs[0], c("xrp", 10000000))
err = suite.keeper.WithdrawCollateral(suite.ctx, suite.addrs[0], suite.addrs[0], c("xrp", 10000000), "xrp-a")
suite.NoError(err)
dep, _ := suite.keeper.GetDeposit(suite.ctx, uint64(1), suite.addrs[0])
td := types.NewDeposit(uint64(1), suite.addrs[0], c("xrp", 390000000))
@ -120,7 +120,7 @@ func (suite *DepositTestSuite) TestWithdrawCollateral() {
acc := ak.GetAccount(suite.ctx, suite.addrs[0])
suite.Equal(i(110000000), acc.GetCoins().AmountOf("xrp"))
err = suite.keeper.WithdrawCollateral(suite.ctx, suite.addrs[0], suite.addrs[1], c("xrp", 10000000))
err = suite.keeper.WithdrawCollateral(suite.ctx, suite.addrs[0], suite.addrs[1], c("xrp", 10000000), "xrp-a")
suite.Require().True(errors.Is(err, types.ErrDepositNotFound))
}

View File

@ -10,23 +10,23 @@ import (
)
// AddPrincipal adds debt to a cdp if the additional debt does not put the cdp below the liquidation ratio
func (k Keeper) AddPrincipal(ctx sdk.Context, owner sdk.AccAddress, denom string, principal sdk.Coin) error {
func (k Keeper) AddPrincipal(ctx sdk.Context, owner sdk.AccAddress, collateralType string, principal sdk.Coin) error {
// validation
cdp, found := k.GetCdpByOwnerAndDenom(ctx, owner, denom)
cdp, found := k.GetCdpByOwnerAndCollateralType(ctx, owner, collateralType)
if !found {
return sdkerrors.Wrapf(types.ErrCdpNotFound, "owner %s, denom %s", owner, denom)
return sdkerrors.Wrapf(types.ErrCdpNotFound, "owner %s, denom %s", owner, collateralType)
}
err := k.ValidatePrincipalDraw(ctx, principal, cdp.Principal.Denom)
if err != nil {
return err
}
err = k.ValidateDebtLimit(ctx, cdp.Collateral.Denom, principal)
err = k.ValidateDebtLimit(ctx, cdp.Type, principal)
if err != nil {
return err
}
err = k.ValidateCollateralizationRatio(ctx, cdp.Collateral, cdp.Principal.Add(principal), cdp.AccumulatedFees)
err = k.ValidateCollateralizationRatio(ctx, cdp.Collateral, cdp.Type, cdp.Principal.Add(principal), cdp.AccumulatedFees)
if err != nil {
return err
}
@ -57,27 +57,27 @@ func (k Keeper) AddPrincipal(ctx sdk.Context, owner sdk.AccAddress, denom string
)
// remove old collateral:debt index
oldCollateralToDebtRatio := k.CalculateCollateralToDebtRatio(ctx, cdp.Collateral, cdp.GetTotalPrincipal())
k.RemoveCdpCollateralRatioIndex(ctx, denom, cdp.ID, oldCollateralToDebtRatio)
oldCollateralToDebtRatio := k.CalculateCollateralToDebtRatio(ctx, cdp.Collateral, cdp.Type, cdp.GetTotalPrincipal())
k.RemoveCdpCollateralRatioIndex(ctx, cdp.Type, cdp.ID, oldCollateralToDebtRatio)
// update cdp state
cdp.Principal = cdp.Principal.Add(principal)
// increment total principal for the input collateral type
k.IncrementTotalPrincipal(ctx, cdp.Collateral.Denom, principal)
k.IncrementTotalPrincipal(ctx, cdp.Type, principal)
// set cdp state and indexes in the store
collateralToDebtRatio := k.CalculateCollateralToDebtRatio(ctx, cdp.Collateral, cdp.GetTotalPrincipal())
collateralToDebtRatio := k.CalculateCollateralToDebtRatio(ctx, cdp.Collateral, cdp.Type, cdp.GetTotalPrincipal())
return k.SetCdpAndCollateralRatioIndex(ctx, cdp, collateralToDebtRatio)
}
// RepayPrincipal removes debt from the cdp
// If all debt is repaid, the collateral is returned to depositors and the cdp is removed from the store
func (k Keeper) RepayPrincipal(ctx sdk.Context, owner sdk.AccAddress, denom string, payment sdk.Coin) error {
func (k Keeper) RepayPrincipal(ctx sdk.Context, owner sdk.AccAddress, collateralType string, payment sdk.Coin) error {
// validation
cdp, found := k.GetCdpByOwnerAndDenom(ctx, owner, denom)
cdp, found := k.GetCdpByOwnerAndCollateralType(ctx, owner, collateralType)
if !found {
return sdkerrors.Wrapf(types.ErrCdpNotFound, "owner %s, denom %s", owner, denom)
return sdkerrors.Wrapf(types.ErrCdpNotFound, "owner %s, denom %s", owner, collateralType)
}
err := k.ValidatePaymentCoins(ctx, cdp, payment)
@ -134,8 +134,8 @@ func (k Keeper) RepayPrincipal(ctx sdk.Context, owner sdk.AccAddress, denom stri
)
// remove the old collateral:debt ratio index
oldCollateralToDebtRatio := k.CalculateCollateralToDebtRatio(ctx, cdp.Collateral, totalPrincipal)
k.RemoveCdpCollateralRatioIndex(ctx, denom, cdp.ID, oldCollateralToDebtRatio)
oldCollateralToDebtRatio := k.CalculateCollateralToDebtRatio(ctx, cdp.Collateral, cdp.Type, totalPrincipal)
k.RemoveCdpCollateralRatioIndex(ctx, cdp.Type, cdp.ID, oldCollateralToDebtRatio)
// update cdp state
if !principalPayment.IsZero() {
@ -144,7 +144,7 @@ func (k Keeper) RepayPrincipal(ctx sdk.Context, owner sdk.AccAddress, denom stri
cdp.AccumulatedFees = cdp.AccumulatedFees.Sub(feePayment)
// decrement the total principal for the input collateral type
k.DecrementTotalPrincipal(ctx, denom, feePayment.Add(principalPayment))
k.DecrementTotalPrincipal(ctx, cdp.Type, feePayment.Add(principalPayment))
// if the debt is fully paid, return collateral to depositors,
// and remove the cdp and indexes from the store
@ -167,7 +167,7 @@ func (k Keeper) RepayPrincipal(ctx sdk.Context, owner sdk.AccAddress, denom stri
}
// set cdp state and update indexes
collateralToDebtRatio := k.CalculateCollateralToDebtRatio(ctx, cdp.Collateral, cdp.GetTotalPrincipal())
collateralToDebtRatio := k.CalculateCollateralToDebtRatio(ctx, cdp.Collateral, cdp.Type, cdp.GetTotalPrincipal())
return k.SetCdpAndCollateralRatioIndex(ctx, cdp, collateralToDebtRatio)
}

View File

@ -46,72 +46,72 @@ func (suite *DrawTestSuite) SetupTest() {
suite.keeper = keeper
suite.ctx = ctx
suite.addrs = addrs
err := suite.keeper.AddCdp(suite.ctx, addrs[0], c("xrp", 400000000), c("usdx", 10000000))
err := suite.keeper.AddCdp(suite.ctx, addrs[0], c("xrp", 400000000), c("usdx", 10000000), "xrp-a")
suite.NoError(err)
}
func (suite *DrawTestSuite) TestAddRepayPrincipal() {
err := suite.keeper.AddPrincipal(suite.ctx, suite.addrs[0], "xrp", c("usdx", 10000000))
err := suite.keeper.AddPrincipal(suite.ctx, suite.addrs[0], "xrp-a", c("usdx", 10000000))
suite.NoError(err)
t, found := suite.keeper.GetCDP(suite.ctx, "xrp", uint64(1))
t, found := suite.keeper.GetCDP(suite.ctx, "xrp-a", uint64(1))
suite.True(found)
suite.Equal(c("usdx", 20000000), t.Principal)
ctd := suite.keeper.CalculateCollateralToDebtRatio(suite.ctx, t.Collateral, t.Principal.Add(t.AccumulatedFees))
ctd := suite.keeper.CalculateCollateralToDebtRatio(suite.ctx, t.Collateral, "xrp-a", t.Principal.Add(t.AccumulatedFees))
suite.Equal(d("20.0"), ctd)
ts := suite.keeper.GetAllCdpsByDenomAndRatio(suite.ctx, "xrp", d("20.0"))
ts := suite.keeper.GetAllCdpsByCollateralTypeAndRatio(suite.ctx, "xrp-a", d("20.0"))
suite.Equal(0, len(ts))
ts = suite.keeper.GetAllCdpsByDenomAndRatio(suite.ctx, "xrp", d("20.0").Add(sdk.SmallestDec()))
ts = suite.keeper.GetAllCdpsByCollateralTypeAndRatio(suite.ctx, "xrp-a", d("20.0").Add(sdk.SmallestDec()))
suite.Equal(ts[0], t)
tp := suite.keeper.GetTotalPrincipal(suite.ctx, "xrp", "usdx")
tp := suite.keeper.GetTotalPrincipal(suite.ctx, "xrp-a", "usdx")
suite.Equal(i(20000000), tp)
sk := suite.app.GetSupplyKeeper()
acc := sk.GetModuleAccount(suite.ctx, types.ModuleName)
suite.Equal(cs(c("xrp", 400000000), c("debt", 20000000)), acc.GetCoins())
err = suite.keeper.AddPrincipal(suite.ctx, suite.addrs[0], "xrp", c("susd", 10000000))
err = suite.keeper.AddPrincipal(suite.ctx, suite.addrs[0], "xrp-a", c("susd", 10000000))
suite.Require().True(errors.Is(err, types.ErrInvalidDebtRequest))
err = suite.keeper.AddPrincipal(suite.ctx, suite.addrs[1], "xrp", c("usdx", 10000000))
err = suite.keeper.AddPrincipal(suite.ctx, suite.addrs[1], "xrp-a", c("usdx", 10000000))
suite.Require().True(errors.Is(err, types.ErrCdpNotFound))
err = suite.keeper.AddPrincipal(suite.ctx, suite.addrs[0], "xrp", c("xusd", 10000000))
err = suite.keeper.AddPrincipal(suite.ctx, suite.addrs[0], "xrp-a", c("xusd", 10000000))
suite.Require().True(errors.Is(err, types.ErrInvalidDebtRequest))
err = suite.keeper.AddPrincipal(suite.ctx, suite.addrs[0], "xrp", c("usdx", 311000000))
err = suite.keeper.AddPrincipal(suite.ctx, suite.addrs[0], "xrp-a", c("usdx", 311000000))
suite.Require().True(errors.Is(err, types.ErrInvalidCollateralRatio))
err = suite.keeper.RepayPrincipal(suite.ctx, suite.addrs[0], "xrp", c("usdx", 10000000))
err = suite.keeper.RepayPrincipal(suite.ctx, suite.addrs[0], "xrp-a", c("usdx", 10000000))
suite.NoError(err)
t, found = suite.keeper.GetCDP(suite.ctx, "xrp", uint64(1))
t, found = suite.keeper.GetCDP(suite.ctx, "xrp-a", uint64(1))
suite.True(found)
suite.Equal(c("usdx", 10000000), t.Principal)
ctd = suite.keeper.CalculateCollateralToDebtRatio(suite.ctx, t.Collateral, t.Principal.Add(t.AccumulatedFees))
ctd = suite.keeper.CalculateCollateralToDebtRatio(suite.ctx, t.Collateral, "xrp-a", t.Principal.Add(t.AccumulatedFees))
suite.Equal(d("40.0"), ctd)
ts = suite.keeper.GetAllCdpsByDenomAndRatio(suite.ctx, "xrp", d("40.0"))
ts = suite.keeper.GetAllCdpsByCollateralTypeAndRatio(suite.ctx, "xrp-a", d("40.0"))
suite.Equal(0, len(ts))
ts = suite.keeper.GetAllCdpsByDenomAndRatio(suite.ctx, "xrp", d("40.0").Add(sdk.SmallestDec()))
ts = suite.keeper.GetAllCdpsByCollateralTypeAndRatio(suite.ctx, "xrp-a", d("40.0").Add(sdk.SmallestDec()))
suite.Equal(ts[0], t)
sk = suite.app.GetSupplyKeeper()
acc = sk.GetModuleAccount(suite.ctx, types.ModuleName)
suite.Equal(cs(c("xrp", 400000000), c("debt", 10000000)), acc.GetCoins())
err = suite.keeper.RepayPrincipal(suite.ctx, suite.addrs[0], "xrp", c("xusd", 10000000))
err = suite.keeper.RepayPrincipal(suite.ctx, suite.addrs[0], "xrp-a", c("xusd", 10000000))
suite.Require().True(errors.Is(err, types.ErrInvalidPayment))
err = suite.keeper.RepayPrincipal(suite.ctx, suite.addrs[1], "xrp", c("xusd", 10000000))
err = suite.keeper.RepayPrincipal(suite.ctx, suite.addrs[1], "xrp-a", c("xusd", 10000000))
suite.Require().True(errors.Is(err, types.ErrCdpNotFound))
err = suite.keeper.RepayPrincipal(suite.ctx, suite.addrs[0], "xrp", c("usdx", 9000000))
err = suite.keeper.RepayPrincipal(suite.ctx, suite.addrs[0], "xrp-a", c("usdx", 9000000))
suite.Require().True(errors.Is(err, types.ErrBelowDebtFloor))
err = suite.keeper.RepayPrincipal(suite.ctx, suite.addrs[0], "xrp", c("usdx", 10000000))
err = suite.keeper.RepayPrincipal(suite.ctx, suite.addrs[0], "xrp-a", c("usdx", 10000000))
suite.NoError(err)
_, found = suite.keeper.GetCDP(suite.ctx, "xrp", uint64(1))
_, found = suite.keeper.GetCDP(suite.ctx, "xrp-a", uint64(1))
suite.False(found)
ts = suite.keeper.GetAllCdpsByDenomAndRatio(suite.ctx, "xrp", types.MaxSortableDec)
ts = suite.keeper.GetAllCdpsByCollateralTypeAndRatio(suite.ctx, "xrp-a", types.MaxSortableDec)
suite.Equal(0, len(ts))
ts = suite.keeper.GetAllCdpsByDenom(suite.ctx, "xrp")
ts = suite.keeper.GetAllCdpsByCollateralType(suite.ctx, "xrp-a")
suite.Equal(0, len(ts))
sk = suite.app.GetSupplyKeeper()
acc = sk.GetModuleAccount(suite.ctx, types.ModuleName)
@ -120,43 +120,43 @@ func (suite *DrawTestSuite) TestAddRepayPrincipal() {
}
func (suite *DrawTestSuite) TestRepayPrincipalOverpay() {
err := suite.keeper.RepayPrincipal(suite.ctx, suite.addrs[0], "xrp", c("usdx", 20000000))
err := suite.keeper.RepayPrincipal(suite.ctx, suite.addrs[0], "xrp-a", c("usdx", 20000000))
suite.NoError(err)
ak := suite.app.GetAccountKeeper()
acc := ak.GetAccount(suite.ctx, suite.addrs[0])
suite.Equal(i(10000000000), (acc.GetCoins().AmountOf("usdx")))
_, found := suite.keeper.GetCDP(suite.ctx, "xrp", 1)
_, found := suite.keeper.GetCDP(suite.ctx, "xrp-a", 1)
suite.False(found)
}
func (suite *DrawTestSuite) TestAddRepayPrincipalFees() {
err := suite.keeper.AddCdp(suite.ctx, suite.addrs[2], c("xrp", 1000000000000), c("usdx", 100000000000))
err := suite.keeper.AddCdp(suite.ctx, suite.addrs[2], c("xrp", 1000000000000), c("usdx", 100000000000), "xrp-a")
suite.NoError(err)
suite.ctx = suite.ctx.WithBlockTime(suite.ctx.BlockTime().Add(time.Minute * 10))
err = suite.keeper.UpdateFeesForAllCdps(suite.ctx, "xrp")
err = suite.keeper.UpdateFeesForAllCdps(suite.ctx, "xrp-a")
suite.NoError(err)
err = suite.keeper.AddPrincipal(suite.ctx, suite.addrs[2], "xrp", c("usdx", 10000000))
err = suite.keeper.AddPrincipal(suite.ctx, suite.addrs[2], "xrp-a", c("usdx", 10000000))
suite.NoError(err)
t, _ := suite.keeper.GetCDP(suite.ctx, "xrp", uint64(2))
t, _ := suite.keeper.GetCDP(suite.ctx, "xrp-a", uint64(2))
suite.Equal(c("usdx", 92827), t.AccumulatedFees)
err = suite.keeper.RepayPrincipal(suite.ctx, suite.addrs[2], "xrp", c("usdx", 100))
err = suite.keeper.RepayPrincipal(suite.ctx, suite.addrs[2], "xrp-a", c("usdx", 100))
suite.NoError(err)
t, _ = suite.keeper.GetCDP(suite.ctx, "xrp", uint64(2))
t, _ = suite.keeper.GetCDP(suite.ctx, "xrp-a", uint64(2))
suite.Equal(c("usdx", 92727), t.AccumulatedFees)
err = suite.keeper.RepayPrincipal(suite.ctx, suite.addrs[2], "xrp", c("usdx", 100010092727))
err = suite.keeper.RepayPrincipal(suite.ctx, suite.addrs[2], "xrp-a", c("usdx", 100010092727))
suite.NoError(err)
_, f := suite.keeper.GetCDP(suite.ctx, "xrp", uint64(2))
_, f := suite.keeper.GetCDP(suite.ctx, "xrp-a", uint64(2))
suite.False(f)
err = suite.keeper.AddCdp(suite.ctx, suite.addrs[2], c("xrp", 1000000000000), c("usdx", 100000000))
err = suite.keeper.AddCdp(suite.ctx, suite.addrs[2], c("xrp", 1000000000000), c("usdx", 100000000), "xrp-a")
suite.NoError(err)
suite.ctx = suite.ctx.WithBlockTime(suite.ctx.BlockTime().Add(time.Second * 31536000)) // move forward one year in time
err = suite.keeper.UpdateFeesForAllCdps(suite.ctx, "xrp")
err = suite.keeper.UpdateFeesForAllCdps(suite.ctx, "xrp-a")
suite.NoError(err)
err = suite.keeper.AddPrincipal(suite.ctx, suite.addrs[2], "xrp", c("usdx", 100000000))
err = suite.keeper.AddPrincipal(suite.ctx, suite.addrs[2], "xrp-a", c("usdx", 100000000))
suite.NoError(err)
t, _ = suite.keeper.GetCDP(suite.ctx, "xrp", uint64(3))
t, _ = suite.keeper.GetCDP(suite.ctx, "xrp-a", uint64(3))
suite.Equal(c("usdx", 5000000), t.AccumulatedFees)
}
@ -164,9 +164,9 @@ func (suite *DrawTestSuite) TestPricefeedFailure() {
ctx := suite.ctx.WithBlockTime(suite.ctx.BlockTime().Add(time.Hour * 2))
pfk := suite.app.GetPriceFeedKeeper()
pfk.SetCurrentPrices(ctx, "xrp:usd")
err := suite.keeper.AddPrincipal(ctx, suite.addrs[0], "xrp", c("usdx", 10000000))
err := suite.keeper.AddPrincipal(ctx, suite.addrs[0], "xrp-a", c("usdx", 10000000))
suite.Error(err)
err = suite.keeper.RepayPrincipal(ctx, suite.addrs[0], "xrp", c("usdx", 10000000))
err = suite.keeper.RepayPrincipal(ctx, suite.addrs[0], "xrp-a", c("usdx", 10000000))
suite.NoError(err)
}
@ -177,7 +177,7 @@ func (suite *DrawTestSuite) TestModuleAccountFailure() {
acc := sk.GetModuleAccount(ctx, types.ModuleName)
ak := suite.app.GetAccountKeeper()
ak.RemoveAccount(ctx, acc)
suite.keeper.RepayPrincipal(ctx, suite.addrs[0], "xrp", c("usdx", 10000000))
suite.keeper.RepayPrincipal(ctx, suite.addrs[0], "xrp-a", c("usdx", 10000000))
})
}

View File

@ -1,6 +1,8 @@
package keeper
import (
"fmt"
"github.com/cosmos/cosmos-sdk/store/prefix"
sdk "github.com/cosmos/cosmos-sdk/types"
@ -9,11 +11,11 @@ import (
// CalculateFees returns the fees accumulated since fees were last calculated based on
// the input amount of outstanding debt (principal) and the number of periods (seconds) that have passed
func (k Keeper) CalculateFees(ctx sdk.Context, principal sdk.Coin, periods sdk.Int, denom string) sdk.Coin {
func (k Keeper) CalculateFees(ctx sdk.Context, principal sdk.Coin, periods sdk.Int, collateralType string) sdk.Coin {
// how fees are calculated:
// feesAccumulated = (outstandingDebt * (feeRate^periods)) - outstandingDebt
// Note that since we can't do x^y using sdk.Decimal, we are converting to int and using RelativePow
feePerSecond := k.getFeeRate(ctx, denom)
feePerSecond := k.getFeeRate(ctx, collateralType)
scalar := sdk.NewInt(1000000000000000000)
feeRateInt := feePerSecond.Mul(sdk.NewDecFromInt(scalar)).TruncateInt()
accumulator := sdk.NewDecFromInt(types.RelativePow(feeRateInt, periods, scalar)).Mul(sdk.SmallestDec())
@ -23,14 +25,14 @@ func (k Keeper) CalculateFees(ctx sdk.Context, principal sdk.Coin, periods sdk.I
}
// UpdateFeesForAllCdps updates the fees for each of the CDPs
func (k Keeper) UpdateFeesForAllCdps(ctx sdk.Context, collateralDenom string) error {
func (k Keeper) UpdateFeesForAllCdps(ctx sdk.Context, collateralType string) error {
var iterationErr error
k.IterateCdpsByDenom(ctx, collateralDenom, func(cdp types.CDP) bool {
oldCollateralToDebtRatio := k.CalculateCollateralToDebtRatio(ctx, cdp.Collateral, cdp.GetTotalPrincipal())
k.IterateCdpsByCollateralType(ctx, collateralType, func(cdp types.CDP) bool {
oldCollateralToDebtRatio := k.CalculateCollateralToDebtRatio(ctx, cdp.Collateral, cdp.Type, cdp.GetTotalPrincipal())
// periods = bblock timestamp - fees updated
periods := sdk.NewInt(ctx.BlockTime().Unix()).Sub(sdk.NewInt(cdp.FeesUpdated.Unix()))
newFees := k.CalculateFees(ctx, cdp.Principal, periods, collateralDenom)
newFees := k.CalculateFees(ctx, cdp.Principal, periods, collateralType)
// exit without updating fees if amount has rounded down to zero
// cdp will get updated next block when newFees, newFeesSavings, newFeesSurplus >0
@ -62,9 +64,9 @@ func (k Keeper) UpdateFeesForAllCdps(ctx sdk.Context, collateralDenom string) er
return true
}
previousDebt := k.GetTotalPrincipal(ctx, collateralDenom, dp.Denom)
previousDebt := k.GetTotalPrincipal(ctx, cdp.Type, dp.Denom)
newDebt := previousDebt.Add(newFees.Amount)
k.SetTotalPrincipal(ctx, collateralDenom, dp.Denom, newDebt)
k.SetTotalPrincipal(ctx, cdp.Type, dp.Denom, newDebt)
// mint surplus coins divided between the liquidator and savings module accounts.
err = k.supplyKeeper.MintCoins(ctx, types.LiquidatorMacc, sdk.NewCoins(sdk.NewCoin(dp.Denom, newFeesSurplus)))
@ -84,8 +86,8 @@ func (k Keeper) UpdateFeesForAllCdps(ctx sdk.Context, collateralDenom string) er
// and set the fees updated time to the current block time since we just updated it
cdp.FeesUpdated = ctx.BlockTime()
collateralToDebtRatio := k.CalculateCollateralToDebtRatio(ctx, cdp.Collateral, cdp.GetTotalPrincipal())
k.RemoveCdpCollateralRatioIndex(ctx, cdp.Collateral.Denom, cdp.ID, oldCollateralToDebtRatio)
collateralToDebtRatio := k.CalculateCollateralToDebtRatio(ctx, cdp.Collateral, cdp.Type, cdp.GetTotalPrincipal())
k.RemoveCdpCollateralRatioIndex(ctx, cdp.Type, cdp.ID, oldCollateralToDebtRatio)
err = k.SetCdpAndCollateralRatioIndex(ctx, cdp, collateralToDebtRatio)
if err != nil {
iterationErr = err
@ -97,27 +99,27 @@ func (k Keeper) UpdateFeesForAllCdps(ctx sdk.Context, collateralDenom string) er
}
// IncrementTotalPrincipal increments the total amount of debt that has been drawn with that collateral type
func (k Keeper) IncrementTotalPrincipal(ctx sdk.Context, collateralDenom string, principal sdk.Coin) {
total := k.GetTotalPrincipal(ctx, collateralDenom, principal.Denom)
func (k Keeper) IncrementTotalPrincipal(ctx sdk.Context, collateralType string, principal sdk.Coin) {
total := k.GetTotalPrincipal(ctx, collateralType, principal.Denom)
total = total.Add(principal.Amount)
k.SetTotalPrincipal(ctx, collateralDenom, principal.Denom, total)
k.SetTotalPrincipal(ctx, collateralType, principal.Denom, total)
}
// DecrementTotalPrincipal decrements the total amount of debt that has been drawn for a particular collateral type
func (k Keeper) DecrementTotalPrincipal(ctx sdk.Context, collateralDenom string, principal sdk.Coin) {
total := k.GetTotalPrincipal(ctx, collateralDenom, principal.Denom)
func (k Keeper) DecrementTotalPrincipal(ctx sdk.Context, collateralType string, principal sdk.Coin) {
total := k.GetTotalPrincipal(ctx, collateralType, principal.Denom)
// NOTE: negative total principal can happen in tests due to rounding errors
// in fee calculation
total = sdk.MaxInt(total.Sub(principal.Amount), sdk.ZeroInt())
k.SetTotalPrincipal(ctx, collateralDenom, principal.Denom, total)
k.SetTotalPrincipal(ctx, collateralType, principal.Denom, total)
}
// GetTotalPrincipal returns the total amount of principal that has been drawn for a particular collateral
func (k Keeper) GetTotalPrincipal(ctx sdk.Context, collateralDenom string, principalDenom string) (total sdk.Int) {
func (k Keeper) GetTotalPrincipal(ctx sdk.Context, collateralType, principalDenom string) (total sdk.Int) {
store := prefix.NewStore(ctx.KVStore(k.key), types.PrincipalKeyPrefix)
bz := store.Get([]byte(collateralDenom + principalDenom))
bz := store.Get([]byte(collateralType + principalDenom))
if bz == nil {
k.SetTotalPrincipal(ctx, collateralDenom, principalDenom, sdk.ZeroInt())
k.SetTotalPrincipal(ctx, collateralType, principalDenom, sdk.ZeroInt())
return sdk.ZeroInt()
}
k.cdc.MustUnmarshalBinaryLengthPrefixed(bz, &total)
@ -125,7 +127,11 @@ func (k Keeper) GetTotalPrincipal(ctx sdk.Context, collateralDenom string, princ
}
// SetTotalPrincipal sets the total amount of principal that has been drawn for the input collateral
func (k Keeper) SetTotalPrincipal(ctx sdk.Context, collateralDenom string, principalDenom string, total sdk.Int) {
func (k Keeper) SetTotalPrincipal(ctx sdk.Context, collateralType, principalDenom string, total sdk.Int) {
store := prefix.NewStore(ctx.KVStore(k.key), types.PrincipalKeyPrefix)
store.Set([]byte(collateralDenom+principalDenom), k.cdc.MustMarshalBinaryLengthPrefixed(total))
_, found := k.GetCollateralTypePrefix(ctx, collateralType)
if !found {
panic(fmt.Sprintf("collateral not found: %s", collateralType))
}
store.Set([]byte(collateralType+principalDenom), k.cdc.MustMarshalBinaryLengthPrefixed(total))
}

View File

@ -56,12 +56,12 @@ func (suite *FeeTestSuite) createCdps() {
// use the created account to create a cdp that SHOULD have fees updated
// to get a ratio between 100 - 110% of liquidation ratio we can use 200xrp ($50) and 24 usdx (208% collateralization with liquidation ratio of 200%)
// create CDP for the first address
err := suite.keeper.AddCdp(suite.ctx, addrs[0], c("xrp", 200000000), c("usdx", 24000000))
err := suite.keeper.AddCdp(suite.ctx, addrs[0], c("xrp", 200000000), c("usdx", 24000000), "xrp-a")
suite.NoError(err) // check that no error was thrown
// use the other account to create a cdp that SHOULD NOT have fees updated - 500% collateralization
// create CDP for the second address
err = suite.keeper.AddCdp(suite.ctx, addrs[1], c("xrp", 200000000), c("usdx", 10000000))
err = suite.keeper.AddCdp(suite.ctx, addrs[1], c("xrp", 200000000), c("usdx", 10000000), "xrp-a")
suite.NoError(err) // check that no error was thrown
}
@ -76,11 +76,11 @@ func (suite *FeeTestSuite) TestUpdateFees() {
// fees to accumulate, in this example 600 seconds
oldtime := suite.ctx.BlockTime()
suite.ctx = suite.ctx.WithBlockTime(suite.ctx.BlockTime().Add(time.Second * 600))
err := suite.keeper.UpdateFeesForAllCdps(suite.ctx, "xrp")
err := suite.keeper.UpdateFeesForAllCdps(suite.ctx, "xrp-a")
suite.NoError(err) // check that we don't have any error
// cdp we expect fees to accumulate for
cdp1, found := suite.keeper.GetCDP(suite.ctx, "xrp", 1)
cdp1, found := suite.keeper.GetCDP(suite.ctx, "xrp-a", 1)
suite.True(found)
// check fees are not zero
// check that the fees have been updated
@ -89,7 +89,7 @@ func (suite *FeeTestSuite) TestUpdateFees() {
suite.Equal(sdk.NewInt(22), cdp1.AccumulatedFees.Amount)
suite.Equal(suite.ctx.BlockTime(), cdp1.FeesUpdated)
// cdp we expect fees to not accumulate for because of rounding to zero
cdp2, found := suite.keeper.GetCDP(suite.ctx, "xrp", 2)
cdp2, found := suite.keeper.GetCDP(suite.ctx, "xrp-a", 2)
suite.True(found)
// check fees are zero
suite.True(cdp2.AccumulatedFees.IsZero())

View File

@ -49,6 +49,7 @@ func NewCDPGenState(asset string, liquidationRatio sdk.Dec) app.GenesisState {
CollateralParams: cdp.CollateralParams{
{
Denom: asset,
Type: asset + "-a",
LiquidationRatio: liquidationRatio,
DebtLimit: sdk.NewInt64Coin("usdx", 1000000000000),
StabilityFee: sdk.MustNewDecFromStr("1.000000001547125958"), // %5 apr
@ -121,6 +122,7 @@ func NewCDPGenStateMulti() app.GenesisState {
CollateralParams: cdp.CollateralParams{
{
Denom: "xrp",
Type: "xrp-a",
LiquidationRatio: sdk.MustNewDecFromStr("2.0"),
DebtLimit: sdk.NewInt64Coin("usdx", 500000000000),
StabilityFee: sdk.MustNewDecFromStr("1.000000001547125958"), // %5 apr
@ -133,6 +135,7 @@ func NewCDPGenStateMulti() app.GenesisState {
},
{
Denom: "btc",
Type: "btc-a",
LiquidationRatio: sdk.MustNewDecFromStr("1.5"),
DebtLimit: sdk.NewInt64Coin("usdx", 500000000000),
StabilityFee: sdk.MustNewDecFromStr("1.000000000782997609"), // %2.5 apr
@ -145,6 +148,7 @@ func NewCDPGenStateMulti() app.GenesisState {
},
{
Denom: "bnb",
Type: "bnb-a",
LiquidationRatio: sdk.MustNewDecFromStr("1.5"),
DebtLimit: sdk.NewInt64Coin("usdx", 500000000000),
StabilityFee: sdk.MustNewDecFromStr("1.000000001547125958"), // %5 apr
@ -185,6 +189,7 @@ func NewCDPGenStateHighDebtLimit() app.GenesisState {
CollateralParams: cdp.CollateralParams{
{
Denom: "xrp",
Type: "xrp-a",
LiquidationRatio: sdk.MustNewDecFromStr("2.0"),
DebtLimit: sdk.NewInt64Coin("usdx", 50000000000000),
StabilityFee: sdk.MustNewDecFromStr("1.000000001547125958"), // %5 apr
@ -197,6 +202,7 @@ func NewCDPGenStateHighDebtLimit() app.GenesisState {
},
{
Denom: "btc",
Type: "btc-a",
LiquidationRatio: sdk.MustNewDecFromStr("1.5"),
DebtLimit: sdk.NewInt64Coin("usdx", 50000000000000),
StabilityFee: sdk.MustNewDecFromStr("1.000000000782997609"), // %2.5 apr
@ -227,10 +233,10 @@ func NewCDPGenStateHighDebtLimit() app.GenesisState {
func cdps() (cdps cdp.CDPs) {
_, addrs := app.GeneratePrivKeyAddressPairs(3)
c1 := cdp.NewCDP(uint64(1), addrs[0], sdk.NewCoin("xrp", sdk.NewInt(10000000)), sdk.NewCoin("usdx", sdk.NewInt(8000000)), tmtime.Canonical(time.Now()))
c2 := cdp.NewCDP(uint64(2), addrs[1], sdk.NewCoin("xrp", sdk.NewInt(100000000)), sdk.NewCoin("usdx", sdk.NewInt(10000000)), tmtime.Canonical(time.Now()))
c3 := cdp.NewCDP(uint64(3), addrs[1], sdk.NewCoin("btc", sdk.NewInt(1000000000)), sdk.NewCoin("usdx", sdk.NewInt(10000000)), tmtime.Canonical(time.Now()))
c4 := cdp.NewCDP(uint64(4), addrs[2], sdk.NewCoin("xrp", sdk.NewInt(1000000000)), sdk.NewCoin("usdx", sdk.NewInt(500000000)), tmtime.Canonical(time.Now()))
c1 := cdp.NewCDP(uint64(1), addrs[0], sdk.NewCoin("xrp", sdk.NewInt(10000000)), "xrp-a", sdk.NewCoin("usdx", sdk.NewInt(8000000)), tmtime.Canonical(time.Now()))
c2 := cdp.NewCDP(uint64(2), addrs[1], sdk.NewCoin("xrp", sdk.NewInt(100000000)), "xrp-a", sdk.NewCoin("usdx", sdk.NewInt(10000000)), tmtime.Canonical(time.Now()))
c3 := cdp.NewCDP(uint64(3), addrs[1], sdk.NewCoin("btc", sdk.NewInt(1000000000)), "btc-a", sdk.NewCoin("usdx", sdk.NewInt(10000000)), tmtime.Canonical(time.Now()))
c4 := cdp.NewCDP(uint64(4), addrs[2], sdk.NewCoin("xrp", sdk.NewInt(1000000000)), "xrp-a", sdk.NewCoin("usdx", sdk.NewInt(500000000)), tmtime.Canonical(time.Now()))
cdps = append(cdps, c1, c2, c3, c4)
return
}

View File

@ -43,22 +43,22 @@ func NewKeeper(cdc *codec.Codec, key sdk.StoreKey, paramstore subspace.Subspace,
}
// CdpDenomIndexIterator returns an sdk.Iterator for all cdps with matching collateral denom
func (k Keeper) CdpDenomIndexIterator(ctx sdk.Context, denom string) sdk.Iterator {
func (k Keeper) CdpDenomIndexIterator(ctx sdk.Context, collateralType string) sdk.Iterator {
store := prefix.NewStore(ctx.KVStore(k.key), types.CdpKeyPrefix)
db, found := k.GetDenomPrefix(ctx, denom)
db, found := k.GetCollateralTypePrefix(ctx, collateralType)
if !found {
panic(fmt.Sprintf("denom %s prefix not found", denom))
panic(fmt.Sprintf("denom %s prefix not found", collateralType))
}
return sdk.KVStorePrefixIterator(store, types.DenomIterKey(db))
}
// CdpCollateralRatioIndexIterator returns an sdk.Iterator for all cdps that have collateral denom
// matching denom and collateral:debt ratio LESS THAN targetRatio
func (k Keeper) CdpCollateralRatioIndexIterator(ctx sdk.Context, denom string, targetRatio sdk.Dec) sdk.Iterator {
func (k Keeper) CdpCollateralRatioIndexIterator(ctx sdk.Context, collateralType string, targetRatio sdk.Dec) sdk.Iterator {
store := prefix.NewStore(ctx.KVStore(k.key), types.CollateralRatioIndexPrefix)
db, found := k.GetDenomPrefix(ctx, denom)
db, found := k.GetCollateralTypePrefix(ctx, collateralType)
if !found {
panic(fmt.Sprintf("denom %s prefix not found", denom))
panic(fmt.Sprintf("denom %s prefix not found", collateralType))
}
return store.Iterator(types.CollateralRatioIterKey(db, sdk.ZeroDec()), types.CollateralRatioIterKey(db, targetRatio))
}
@ -78,9 +78,9 @@ func (k Keeper) IterateAllCdps(ctx sdk.Context, cb func(cdp types.CDP) (stop boo
}
}
// IterateCdpsByDenom iterates over cdps with matching denom and performs a callback function
func (k Keeper) IterateCdpsByDenom(ctx sdk.Context, denom string, cb func(cdp types.CDP) (stop bool)) {
iterator := k.CdpDenomIndexIterator(ctx, denom)
// IterateCdpsByCollateralType iterates over cdps with matching denom and performs a callback function
func (k Keeper) IterateCdpsByCollateralType(ctx sdk.Context, collateralType string, cb func(cdp types.CDP) (stop bool)) {
iterator := k.CdpDenomIndexIterator(ctx, collateralType)
defer iterator.Close()
for ; iterator.Valid(); iterator.Next() {
@ -94,14 +94,13 @@ func (k Keeper) IterateCdpsByDenom(ctx sdk.Context, denom string, cb func(cdp ty
// IterateCdpsByCollateralRatio iterate over cdps with collateral denom equal to denom and
// collateral:debt ratio LESS THAN targetRatio and performs a callback function.
func (k Keeper) IterateCdpsByCollateralRatio(ctx sdk.Context, denom string, targetRatio sdk.Dec, cb func(cdp types.CDP) (stop bool)) {
iterator := k.CdpCollateralRatioIndexIterator(ctx, denom, targetRatio)
func (k Keeper) IterateCdpsByCollateralRatio(ctx sdk.Context, collateralType string, targetRatio sdk.Dec, cb func(cdp types.CDP) (stop bool)) {
iterator := k.CdpCollateralRatioIndexIterator(ctx, collateralType, targetRatio)
defer iterator.Close()
for ; iterator.Valid(); iterator.Next() {
db, id, _ := types.SplitCollateralRatioKey(iterator.Key())
d := k.getDenomFromByte(ctx, db)
cdp, found := k.GetCDP(ctx, d, id)
_, id, _ := types.SplitCollateralRatioKey(iterator.Key())
cdp, found := k.GetCDP(ctx, collateralType, id)
if !found {
panic(fmt.Sprintf("cdp %d does not exist", id))
}

View File

@ -86,7 +86,7 @@ func createCdps(n int) (app.TestApp, sdk.Context, keeper.Keeper) {
)
cdpKeeper := tApp.GetCDPKeeper()
for i := 0; i < n; i++ {
err := cdpKeeper.AddCdp(ctx, addrs[i], coins[i][0], c("usdx", 100000000))
err := cdpKeeper.AddCdp(ctx, addrs[i], coins[i][0], c("usdx", 100000000), "btc-a")
if err != nil {
panic("failed to create cdp")
}
@ -138,7 +138,7 @@ func BenchmarkCdpCreation(b *testing.B) {
cdpKeeper := tApp.GetCDPKeeper()
b.ResetTimer()
for i := 0; i < b.N; i++ {
err := cdpKeeper.AddCdp(ctx, addrs[i], coins[i][0], c("usdx", 100000000))
err := cdpKeeper.AddCdp(ctx, addrs[i], coins[i][0], c("usdx", 100000000), "btc-a")
if err != nil {
b.Error("unexpected error")
}

View File

@ -21,10 +21,10 @@ func (k Keeper) SetParams(ctx sdk.Context, params types.Params) {
}
// GetCollateral returns the collateral param with corresponding denom
func (k Keeper) GetCollateral(ctx sdk.Context, denom string) (types.CollateralParam, bool) {
func (k Keeper) GetCollateral(ctx sdk.Context, collateralType string) (types.CollateralParam, bool) {
params := k.GetParams(ctx)
for _, cp := range params.CollateralParams {
if cp.Denom == denom {
if cp.Type == collateralType {
return cp, true
}
}
@ -40,11 +40,11 @@ func (k Keeper) GetDebtParam(ctx sdk.Context, denom string) (types.DebtParam, bo
return types.DebtParam{}, false
}
// GetDenomPrefix returns the prefix of the matching denom
func (k Keeper) GetDenomPrefix(ctx sdk.Context, denom string) (byte, bool) {
// GetCollateralTypePrefix returns the prefix of the matching denom
func (k Keeper) GetCollateralTypePrefix(ctx sdk.Context, collateralType string) (byte, bool) {
params := k.GetParams(ctx)
for _, cp := range params.CollateralParams {
if cp.Denom == denom {
if cp.Type == collateralType {
return cp.Prefix, true
}
}
@ -62,51 +62,51 @@ func (k Keeper) getDenomFromByte(ctx sdk.Context, db byte) string {
panic(fmt.Sprintf("no collateral denom with prefix %b", db))
}
func (k Keeper) getSpotMarketID(ctx sdk.Context, denom string) string {
cp, found := k.GetCollateral(ctx, denom)
func (k Keeper) getSpotMarketID(ctx sdk.Context, collateralType string) string {
cp, found := k.GetCollateral(ctx, collateralType)
if !found {
panic(fmt.Sprintf("collateral not found: %s", denom))
panic(fmt.Sprintf("collateral not found: %s", collateralType))
}
return cp.SpotMarketID
}
func (k Keeper) getliquidationMarketID(ctx sdk.Context, denom string) string {
cp, found := k.GetCollateral(ctx, denom)
func (k Keeper) getliquidationMarketID(ctx sdk.Context, collateralType string) string {
cp, found := k.GetCollateral(ctx, collateralType)
if !found {
panic(fmt.Sprintf("collateral not found: %s", denom))
panic(fmt.Sprintf("collateral not found: %s", collateralType))
}
return cp.LiquidationMarketID
}
func (k Keeper) getLiquidationRatio(ctx sdk.Context, denom string) sdk.Dec {
cp, found := k.GetCollateral(ctx, denom)
func (k Keeper) getLiquidationRatio(ctx sdk.Context, collateralType string) sdk.Dec {
cp, found := k.GetCollateral(ctx, collateralType)
if !found {
panic(fmt.Sprintf("collateral not found: %s", denom))
panic(fmt.Sprintf("collateral not found: %s", collateralType))
}
return cp.LiquidationRatio
}
func (k Keeper) getLiquidationPenalty(ctx sdk.Context, denom string) sdk.Dec {
cp, found := k.GetCollateral(ctx, denom)
func (k Keeper) getLiquidationPenalty(ctx sdk.Context, collateralType string) sdk.Dec {
cp, found := k.GetCollateral(ctx, collateralType)
if !found {
panic(fmt.Sprintf("collateral not found: %s", denom))
panic(fmt.Sprintf("collateral not found: %s", collateralType))
}
return cp.LiquidationPenalty
}
func (k Keeper) getAuctionSize(ctx sdk.Context, denom string) sdk.Int {
cp, found := k.GetCollateral(ctx, denom)
func (k Keeper) getAuctionSize(ctx sdk.Context, collateralType string) sdk.Int {
cp, found := k.GetCollateral(ctx, collateralType)
if !found {
panic(fmt.Sprintf("collateral not found: %s", denom))
panic(fmt.Sprintf("collateral not found: %s", collateralType))
}
return cp.AuctionSize
}
// GetFeeRate returns the per second fee rate for the input denom
func (k Keeper) getFeeRate(ctx sdk.Context, denom string) (fee sdk.Dec) {
collalateralParam, found := k.GetCollateral(ctx, denom)
func (k Keeper) getFeeRate(ctx sdk.Context, collateralType string) (fee sdk.Dec) {
collalateralParam, found := k.GetCollateral(ctx, collateralType)
if !found {
panic(fmt.Sprintf("could not get fee rate for %s, collateral not found", denom))
panic(fmt.Sprintf("could not get fee rate for %s, collateral not found", collateralType))
}
return collalateralParam.StabilityFee
}

View File

@ -18,7 +18,7 @@ func NewQuerier(keeper Keeper) sdk.Querier {
case types.QueryGetCdp:
return queryGetCdp(ctx, req, keeper)
case types.QueryGetCdps:
return queryGetCdpsByDenom(ctx, req, keeper)
return queryGetCdpsByCollateralType(ctx, req, keeper)
case types.QueryGetCdpsByCollateralization:
return queryGetCdpsByRatio(ctx, req, keeper)
case types.QueryGetParams:
@ -41,14 +41,14 @@ func queryGetCdp(ctx sdk.Context, req abci.RequestQuery, keeper Keeper) ([]byte,
return nil, sdkerrors.Wrap(sdkerrors.ErrJSONUnmarshal, err.Error())
}
_, valid := keeper.GetDenomPrefix(ctx, requestParams.CollateralDenom)
_, valid := keeper.GetCollateralTypePrefix(ctx, requestParams.CollateralType)
if !valid {
return nil, sdkerrors.Wrap(types.ErrCollateralNotSupported, requestParams.CollateralDenom)
return nil, sdkerrors.Wrap(types.ErrCollateralNotSupported, requestParams.CollateralType)
}
cdp, found := keeper.GetCdpByOwnerAndDenom(ctx, requestParams.Owner, requestParams.CollateralDenom)
cdp, found := keeper.GetCdpByOwnerAndCollateralType(ctx, requestParams.Owner, requestParams.CollateralType)
if !found {
return nil, sdkerrors.Wrapf(types.ErrCdpNotFound, "owner %s, denom %s", requestParams.Owner, requestParams.CollateralDenom)
return nil, sdkerrors.Wrapf(types.ErrCdpNotFound, "owner %s, denom %s", requestParams.Owner, requestParams.CollateralType)
}
augmentedCDP := keeper.LoadAugmentedCDP(ctx, cdp)
@ -69,14 +69,14 @@ func queryGetDeposits(ctx sdk.Context, req abci.RequestQuery, keeper Keeper) ([]
return nil, sdkerrors.Wrap(sdkerrors.ErrJSONUnmarshal, err.Error())
}
_, valid := keeper.GetDenomPrefix(ctx, requestParams.CollateralDenom)
_, valid := keeper.GetCollateralTypePrefix(ctx, requestParams.CollateralType)
if !valid {
return nil, sdkerrors.Wrap(types.ErrCollateralNotSupported, requestParams.CollateralDenom)
return nil, sdkerrors.Wrap(types.ErrCollateralNotSupported, requestParams.CollateralType)
}
cdp, found := keeper.GetCdpByOwnerAndDenom(ctx, requestParams.Owner, requestParams.CollateralDenom)
cdp, found := keeper.GetCdpByOwnerAndCollateralType(ctx, requestParams.Owner, requestParams.CollateralType)
if !found {
return nil, sdkerrors.Wrapf(types.ErrCdpNotFound, "owner %s, denom %s", requestParams.Owner, requestParams.CollateralDenom)
return nil, sdkerrors.Wrapf(types.ErrCdpNotFound, "owner %s, denom %s", requestParams.Owner, requestParams.CollateralType)
}
deposits := keeper.GetDeposits(ctx, cdp.ID)
@ -96,17 +96,17 @@ func queryGetCdpsByRatio(ctx sdk.Context, req abci.RequestQuery, keeper Keeper)
if err != nil {
return nil, sdkerrors.Wrap(sdkerrors.ErrJSONUnmarshal, err.Error())
}
_, valid := keeper.GetDenomPrefix(ctx, requestParams.CollateralDenom)
_, valid := keeper.GetCollateralTypePrefix(ctx, requestParams.CollateralType)
if !valid {
return nil, sdkerrors.Wrap(types.ErrCollateralNotSupported, requestParams.CollateralDenom)
return nil, sdkerrors.Wrap(types.ErrCollateralNotSupported, requestParams.CollateralType)
}
ratio, err := keeper.CalculateCollateralizationRatioFromAbsoluteRatio(ctx, requestParams.CollateralDenom, requestParams.Ratio, "liquidation")
ratio, err := keeper.CalculateCollateralizationRatioFromAbsoluteRatio(ctx, requestParams.CollateralType, requestParams.Ratio, "liquidation")
if err != nil {
return nil, sdkerrors.Wrap(err, "couldn't get collateralization ratio from absolute ratio")
}
cdps := keeper.GetAllCdpsByDenomAndRatio(ctx, requestParams.CollateralDenom, ratio)
cdps := keeper.GetAllCdpsByCollateralTypeAndRatio(ctx, requestParams.CollateralType, ratio)
// augment CDPs by adding collateral value and collateralization ratio
var augmentedCDPs types.AugmentedCDPs
for _, cdp := range cdps {
@ -121,18 +121,18 @@ func queryGetCdpsByRatio(ctx sdk.Context, req abci.RequestQuery, keeper Keeper)
}
// query all cdps with matching collateral denom
func queryGetCdpsByDenom(ctx sdk.Context, req abci.RequestQuery, keeper Keeper) ([]byte, error) {
func queryGetCdpsByCollateralType(ctx sdk.Context, req abci.RequestQuery, keeper Keeper) ([]byte, error) {
var requestParams types.QueryCdpsParams
err := types.ModuleCdc.UnmarshalJSON(req.Data, &requestParams)
if err != nil {
return nil, sdkerrors.Wrap(sdkerrors.ErrJSONUnmarshal, err.Error())
}
_, valid := keeper.GetDenomPrefix(ctx, requestParams.CollateralDenom)
_, valid := keeper.GetCollateralTypePrefix(ctx, requestParams.CollateralType)
if !valid {
return nil, sdkerrors.Wrap(types.ErrCollateralNotSupported, requestParams.CollateralDenom)
return nil, sdkerrors.Wrap(types.ErrCollateralNotSupported, requestParams.CollateralType)
}
cdps := keeper.GetAllCdpsByDenom(ctx, requestParams.CollateralDenom)
cdps := keeper.GetAllCdpsByCollateralType(ctx, requestParams.CollateralType)
// augment CDPs by adding collateral value and collateralization ratio
var augmentedCDPs types.AugmentedCDPs
for _, cdp := range cdps {

View File

@ -97,9 +97,9 @@ func (suite *QuerierTestSuite) SetupTest() {
amount = simulation.RandIntBetween(rand.New(rand.NewSource(int64(j))), 500000000, 5000000000)
debt = simulation.RandIntBetween(rand.New(rand.NewSource(int64(j))), 1000000000, 25000000000)
}
err = suite.keeper.AddCdp(suite.ctx, addrs[j], c(collateral, int64(amount)), c("usdx", int64(debt)))
err = suite.keeper.AddCdp(suite.ctx, addrs[j], c(collateral, int64(amount)), c("usdx", int64(debt)), collateral+"-a")
suite.NoError(err)
c, f := suite.keeper.GetCDP(suite.ctx, collateral, uint64(j+1))
c, f := suite.keeper.GetCDP(suite.ctx, collateral+"-a", uint64(j+1))
suite.True(f)
cdps[j] = c
aCDP := suite.keeper.LoadAugmentedCDP(suite.ctx, c)
@ -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)),
Data: types.ModuleCdc.MustMarshalJSON(types.NewQueryCdpParams(suite.cdps[0].Owner, suite.cdps[0].Collateral.Denom+"-a")),
}
bz, err := suite.querier(ctx, []string{types.QueryGetCdp}, query)
suite.Nil(err)
@ -128,7 +128,7 @@ func (suite *QuerierTestSuite) TestQueryCdp() {
query = abci.RequestQuery{
Path: strings.Join([]string{custom, types.QuerierRoute, types.QueryGetCdp}, "/"),
Data: types.ModuleCdc.MustMarshalJSON(types.NewQueryCdpParams(suite.cdps[0].Owner, "lol")),
Data: types.ModuleCdc.MustMarshalJSON(types.NewQueryCdpParams(suite.cdps[0].Owner, "lol-a")),
}
_, err = suite.querier(ctx, []string{types.QueryGetCdp}, query)
suite.Error(err)
@ -146,18 +146,18 @@ func (suite *QuerierTestSuite) TestQueryCdp() {
query = abci.RequestQuery{
Path: strings.Join([]string{custom, types.QuerierRoute, types.QueryGetCdp}, "/"),
Data: types.ModuleCdc.MustMarshalJSON(types.NewQueryCdpParams(suite.cdps[0].Owner, "xrp")),
Data: types.ModuleCdc.MustMarshalJSON(types.NewQueryCdpParams(suite.cdps[0].Owner, "xrp-a")),
}
_, err = suite.querier(ctx, []string{types.QueryGetCdp}, query)
suite.Error(err)
}
func (suite *QuerierTestSuite) TestQueryCdpsByDenom() {
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)),
Data: types.ModuleCdc.MustMarshalJSON(types.NewQueryCdpsParams(suite.cdps[0].Collateral.Denom + "-a")),
}
bz, err := suite.querier(ctx, []string{types.QueryGetCdps}, query)
suite.Nil(err)
@ -169,7 +169,7 @@ func (suite *QuerierTestSuite) TestQueryCdpsByDenom() {
query = abci.RequestQuery{
Path: strings.Join([]string{custom, types.QuerierRoute, types.QueryGetCdps}, "/"),
Data: types.ModuleCdc.MustMarshalJSON(types.NewQueryCdpsParams("lol")),
Data: types.ModuleCdc.MustMarshalJSON(types.NewQueryCdpsParams("lol-a")),
}
_, err = suite.querier(ctx, []string{types.QueryGetCdps}, query)
suite.Error(err)
@ -183,8 +183,8 @@ func (suite *QuerierTestSuite) TestQueryCdpsByRatio() {
expectedXrpIds := []int{}
expectedBtcIds := []int{}
for _, cdp := range suite.cdps {
absoluteRatio := suite.keeper.CalculateCollateralToDebtRatio(suite.ctx, cdp.Collateral, cdp.Principal)
collateralizationRatio, err := suite.keeper.CalculateCollateralizationRatioFromAbsoluteRatio(suite.ctx, cdp.Collateral.Denom, absoluteRatio, "liquidation")
absoluteRatio := suite.keeper.CalculateCollateralToDebtRatio(suite.ctx, cdp.Collateral, cdp.Type, cdp.Principal)
collateralizationRatio, err := suite.keeper.CalculateCollateralizationRatioFromAbsoluteRatio(suite.ctx, cdp.Type, absoluteRatio, "liquidation")
suite.Nil(err)
if cdp.Collateral.Denom == "xrp" {
if collateralizationRatio.LT(xrpRatio) {
@ -202,7 +202,7 @@ func (suite *QuerierTestSuite) TestQueryCdpsByRatio() {
ctx := suite.ctx.WithIsCheckTx(false)
query := abci.RequestQuery{
Path: strings.Join([]string{custom, types.QuerierRoute, types.QueryGetCdpsByCollateralization}, "/"),
Data: types.ModuleCdc.MustMarshalJSON(types.NewQueryCdpsByRatioParams("xrp", xrpRatio)),
Data: types.ModuleCdc.MustMarshalJSON(types.NewQueryCdpsByRatioParams("xrp-a", xrpRatio)),
}
bz, err := suite.querier(ctx, []string{types.QueryGetCdpsByCollateralization}, query)
suite.Nil(err)
@ -219,7 +219,7 @@ func (suite *QuerierTestSuite) TestQueryCdpsByRatio() {
query = abci.RequestQuery{
Path: strings.Join([]string{custom, types.QuerierRoute, types.QueryGetCdpsByCollateralization}, "/"),
Data: types.ModuleCdc.MustMarshalJSON(types.NewQueryCdpsByRatioParams("btc", btcRatio)),
Data: types.ModuleCdc.MustMarshalJSON(types.NewQueryCdpsByRatioParams("btc-a", btcRatio)),
}
bz, err = suite.querier(ctx, []string{types.QueryGetCdpsByCollateralization}, query)
suite.Nil(err)
@ -236,7 +236,7 @@ func (suite *QuerierTestSuite) TestQueryCdpsByRatio() {
query = abci.RequestQuery{
Path: strings.Join([]string{custom, types.QuerierRoute, types.QueryGetCdpsByCollateralization}, "/"),
Data: types.ModuleCdc.MustMarshalJSON(types.NewQueryCdpsByRatioParams("xrp", d("0.003"))),
Data: types.ModuleCdc.MustMarshalJSON(types.NewQueryCdpsByRatioParams("xrp-a", d("0.003"))),
}
bz, err = suite.querier(ctx, []string{types.QueryGetCdpsByCollateralization}, query)
suite.Nil(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)),
Data: types.ModuleCdc.MustMarshalJSON(types.NewQueryCdpDeposits(suite.cdps[0].Owner, suite.cdps[0].Collateral.Denom+"-a")),
}
bz, err := suite.querier(ctx, []string{types.QueryGetCdpDeposits}, query)

View File

@ -17,7 +17,7 @@ import (
// (this is the equivalent of saying that fees are no longer accumulated by a cdp once it gets liquidated)
func (k Keeper) SeizeCollateral(ctx sdk.Context, cdp types.CDP) error {
// Calculate the previous collateral ratio
oldCollateralToDebtRatio := k.CalculateCollateralToDebtRatio(ctx, cdp.Collateral, cdp.GetTotalPrincipal())
oldCollateralToDebtRatio := k.CalculateCollateralToDebtRatio(ctx, cdp.Collateral, cdp.Type, cdp.GetTotalPrincipal())
// Move debt coins from cdp to liquidator account
deposits := k.GetDeposits(ctx, cdp.ID)
@ -48,23 +48,23 @@ func (k Keeper) SeizeCollateral(ctx sdk.Context, cdp types.CDP) error {
)
}
err = k.AuctionCollateral(ctx, deposits, debt, cdp.Principal.Denom)
err = k.AuctionCollateral(ctx, deposits, cdp.Type, debt, cdp.Principal.Denom)
if err != nil {
return err
}
// Decrement total principal for this collateral type
coinsToDecrement := cdp.GetTotalPrincipal()
k.DecrementTotalPrincipal(ctx, cdp.Collateral.Denom, coinsToDecrement)
k.DecrementTotalPrincipal(ctx, cdp.Type, coinsToDecrement)
// Delete CDP from state
k.RemoveCdpOwnerIndex(ctx, cdp)
k.RemoveCdpCollateralRatioIndex(ctx, cdp.Collateral.Denom, cdp.ID, oldCollateralToDebtRatio)
k.RemoveCdpCollateralRatioIndex(ctx, cdp.Type, cdp.ID, oldCollateralToDebtRatio)
return k.DeleteCDP(ctx, cdp)
}
// LiquidateCdps seizes collateral from all CDPs below the input liquidation ratio
func (k Keeper) LiquidateCdps(ctx sdk.Context, marketID string, denom string, liquidationRatio sdk.Dec) error {
func (k Keeper) LiquidateCdps(ctx sdk.Context, marketID string, collateralType string, liquidationRatio sdk.Dec) error {
price, err := k.pricefeedKeeper.GetCurrentPrice(ctx, marketID)
if err != nil {
return err
@ -77,7 +77,7 @@ func (k Keeper) LiquidateCdps(ctx sdk.Context, marketID string, denom string, li
// liquidation ratio = 1.5
// normalizedRatio = (1/(0.5/1.5)) = 3
normalizedRatio := sdk.OneDec().Quo(priceDivLiqRatio)
cdpsToLiquidate := k.GetAllCdpsByDenomAndRatio(ctx, denom, normalizedRatio)
cdpsToLiquidate := k.GetAllCdpsByCollateralTypeAndRatio(ctx, collateralType, normalizedRatio)
for _, c := range cdpsToLiquidate {
err := k.SeizeCollateral(ctx, c)
if err != nil {
@ -88,8 +88,8 @@ func (k Keeper) LiquidateCdps(ctx sdk.Context, marketID string, denom string, li
}
// ApplyLiquidationPenalty multiplies the input debt amount by the liquidation penalty
func (k Keeper) ApplyLiquidationPenalty(ctx sdk.Context, denom string, debt sdk.Int) sdk.Int {
penalty := k.getLiquidationPenalty(ctx, denom)
func (k Keeper) ApplyLiquidationPenalty(ctx sdk.Context, collateralType string, debt sdk.Int) sdk.Int {
penalty := k.getLiquidationPenalty(ctx, collateralType)
return sdk.NewDecFromInt(debt).Mul(penalty).RoundInt()
}

View File

@ -105,9 +105,9 @@ func (suite *SeizeTestSuite) createCdps() {
tracker.debt += int64(debt)
}
}
err := suite.keeper.AddCdp(suite.ctx, addrs[j], c(collateral, int64(amount)), c("usdx", int64(debt)))
err := suite.keeper.AddCdp(suite.ctx, addrs[j], c(collateral, int64(amount)), c("usdx", int64(debt)), collateral+"-a")
suite.NoError(err)
c, f := suite.keeper.GetCDP(suite.ctx, collateral, uint64(j+1))
c, f := suite.keeper.GetCDP(suite.ctx, collateral+"-a", uint64(j+1))
suite.True(f)
cdps[j] = c
}
@ -131,14 +131,14 @@ func (suite *SeizeTestSuite) setPrice(price sdk.Dec, market string) {
func (suite *SeizeTestSuite) TestSeizeCollateral() {
suite.createCdps()
sk := suite.app.GetSupplyKeeper()
cdp, found := suite.keeper.GetCDP(suite.ctx, "xrp", uint64(2))
cdp, found := suite.keeper.GetCDP(suite.ctx, "xrp-a", uint64(2))
suite.True(found)
p := cdp.Principal.Amount
cl := cdp.Collateral.Amount
tpb := suite.keeper.GetTotalPrincipal(suite.ctx, "xrp", "usdx")
tpb := suite.keeper.GetTotalPrincipal(suite.ctx, "xrp-a", "usdx")
err := suite.keeper.SeizeCollateral(suite.ctx, cdp)
suite.NoError(err)
tpa := suite.keeper.GetTotalPrincipal(suite.ctx, "xrp", "usdx")
tpa := suite.keeper.GetTotalPrincipal(suite.ctx, "xrp-a", "usdx")
suite.Equal(tpb.Sub(tpa), p)
auctionKeeper := suite.app.GetAuctionKeeper()
_, found = auctionKeeper.GetAuction(suite.ctx, auction.DefaultNextAuctionID)
@ -148,34 +148,34 @@ func (suite *SeizeTestSuite) TestSeizeCollateral() {
ak := suite.app.GetAccountKeeper()
acc := ak.GetAccount(suite.ctx, suite.addrs[1])
suite.Equal(p.Int64(), acc.GetCoins().AmountOf("usdx").Int64())
err = suite.keeper.WithdrawCollateral(suite.ctx, suite.addrs[1], suite.addrs[1], c("xrp", 10))
err = suite.keeper.WithdrawCollateral(suite.ctx, suite.addrs[1], suite.addrs[1], c("xrp", 10), "xrp-a")
suite.Require().True(errors.Is(err, types.ErrCdpNotFound))
}
func (suite *SeizeTestSuite) TestSeizeCollateralMultiDeposit() {
suite.createCdps()
sk := suite.app.GetSupplyKeeper()
cdp, found := suite.keeper.GetCDP(suite.ctx, "xrp", uint64(2))
cdp, found := suite.keeper.GetCDP(suite.ctx, "xrp-a", uint64(2))
suite.True(found)
err := suite.keeper.DepositCollateral(suite.ctx, suite.addrs[1], suite.addrs[0], c("xrp", 6999000000))
err := suite.keeper.DepositCollateral(suite.ctx, suite.addrs[1], suite.addrs[0], c("xrp", 6999000000), "xrp-a")
suite.NoError(err)
cdp, found = suite.keeper.GetCDP(suite.ctx, "xrp", uint64(2))
cdp, found = suite.keeper.GetCDP(suite.ctx, "xrp-a", uint64(2))
suite.True(found)
deposits := suite.keeper.GetDeposits(suite.ctx, cdp.ID)
suite.Equal(2, len(deposits))
p := cdp.Principal.Amount
cl := cdp.Collateral.Amount
tpb := suite.keeper.GetTotalPrincipal(suite.ctx, "xrp", "usdx")
tpb := suite.keeper.GetTotalPrincipal(suite.ctx, "xrp-a", "usdx")
err = suite.keeper.SeizeCollateral(suite.ctx, cdp)
suite.NoError(err)
tpa := suite.keeper.GetTotalPrincipal(suite.ctx, "xrp", "usdx")
tpa := suite.keeper.GetTotalPrincipal(suite.ctx, "xrp-a", "usdx")
suite.Equal(tpb.Sub(tpa), p)
auctionMacc := sk.GetModuleAccount(suite.ctx, auction.ModuleName)
suite.Equal(cs(c("debt", p.Int64()), c("xrp", cl.Int64())), auctionMacc.GetCoins())
ak := suite.app.GetAccountKeeper()
acc := ak.GetAccount(suite.ctx, suite.addrs[1])
suite.Equal(p.Int64(), acc.GetCoins().AmountOf("usdx").Int64())
err = suite.keeper.WithdrawCollateral(suite.ctx, suite.addrs[1], suite.addrs[1], c("xrp", 10))
err = suite.keeper.WithdrawCollateral(suite.ctx, suite.addrs[1], suite.addrs[1], c("xrp", 10), "xrp-a")
suite.Require().True(errors.Is(err, types.ErrCdpNotFound))
}
@ -185,9 +185,9 @@ func (suite *SeizeTestSuite) TestLiquidateCdps() {
acc := sk.GetModuleAccount(suite.ctx, types.ModuleName)
originalXrpCollateral := acc.GetCoins().AmountOf("xrp")
suite.setPrice(d("0.2"), "xrp:usd")
p, found := suite.keeper.GetCollateral(suite.ctx, "xrp")
p, found := suite.keeper.GetCollateral(suite.ctx, "xrp-a")
suite.True(found)
suite.keeper.LiquidateCdps(suite.ctx, "xrp:usd", "xrp", p.LiquidationRatio)
suite.keeper.LiquidateCdps(suite.ctx, "xrp:usd", "xrp-a", p.LiquidationRatio)
acc = sk.GetModuleAccount(suite.ctx, types.ModuleName)
finalXrpCollateral := acc.GetCoins().AmountOf("xrp")
seizedXrpCollateral := originalXrpCollateral.Sub(finalXrpCollateral)
@ -196,13 +196,13 @@ func (suite *SeizeTestSuite) TestLiquidateCdps() {
}
func (suite *SeizeTestSuite) TestApplyLiquidationPenalty() {
penalty := suite.keeper.ApplyLiquidationPenalty(suite.ctx, "xrp", i(1000))
penalty := suite.keeper.ApplyLiquidationPenalty(suite.ctx, "xrp-a", i(1000))
suite.Equal(i(50), penalty)
penalty = suite.keeper.ApplyLiquidationPenalty(suite.ctx, "btc", i(1000))
penalty = suite.keeper.ApplyLiquidationPenalty(suite.ctx, "btc-a", i(1000))
suite.Equal(i(25), penalty)
penalty = suite.keeper.ApplyLiquidationPenalty(suite.ctx, "xrp", i(675760172))
penalty = suite.keeper.ApplyLiquidationPenalty(suite.ctx, "xrp-a", i(675760172))
suite.Equal(i(33788009), penalty)
suite.Panics(func() { suite.keeper.ApplyLiquidationPenalty(suite.ctx, "lol", i(1000)) })
suite.Panics(func() { suite.keeper.ApplyLiquidationPenalty(suite.ctx, "lol-a", i(1000)) })
}
func TestSeizeTestSuite(t *testing.T) {

116
x/cdp/legacy/v0_9/types.go Normal file
View File

@ -0,0 +1,116 @@
package v0_9
import (
"time"
sdk "github.com/cosmos/cosmos-sdk/types"
)
// CDP is the state of a single collateralized debt position.
type CDP struct {
ID uint64 `json:"id" yaml:"id"` // unique id for cdp
Owner sdk.AccAddress `json:"owner" yaml:"owner"` // Account that authorizes changes to the CDP
Collateral sdk.Coin `json:"collateral" yaml:"collateral"` // Amount of collateral stored in this CDP
Principal sdk.Coin `json:"principal" yaml:"principal"`
AccumulatedFees sdk.Coin `json:"accumulated_fees" yaml:"accumulated_fees"`
FeesUpdated time.Time `json:"fees_updated" yaml:"fees_updated"` // Amount of stable coin drawn from this CDP
}
// NewCDP creates a new CDP object
func NewCDP(id uint64, owner sdk.AccAddress, collateral sdk.Coin, principal sdk.Coin, time time.Time) CDP {
fees := sdk.NewCoin(principal.Denom, sdk.ZeroInt())
return CDP{
ID: id,
Owner: owner,
Collateral: collateral,
Principal: principal,
AccumulatedFees: fees,
FeesUpdated: time,
}
}
// CDPs a collection of CDP objects
type CDPs []CDP
// Deposit defines an amount of coins deposited by an account to a cdp
type Deposit struct {
CdpID uint64 `json:"cdp_id" yaml:"cdp_id"` // cdpID of the cdp
Depositor sdk.AccAddress `json:"depositor" yaml:"depositor"` // Address of the depositor
Amount sdk.Coin `json:"amount" yaml:"amount"` // Deposit amount
}
// NewDeposit creates a new Deposit object
func NewDeposit(cdpID uint64, depositor sdk.AccAddress, amount sdk.Coin) Deposit {
return Deposit{cdpID, depositor, amount}
}
// Deposits a collection of Deposit objects
type Deposits []Deposit
// GenesisState is the state that must be provided at genesis.
type GenesisState struct {
Params Params `json:"params" yaml:"params"`
CDPs CDPs `json:"cdps" yaml:"cdps"`
Deposits Deposits `json:"deposits" yaml:"deposits"`
StartingCdpID uint64 `json:"starting_cdp_id" yaml:"starting_cdp_id"`
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"`
}
// Params governance parameters for cdp module
type Params struct {
CollateralParams CollateralParams `json:"collateral_params" yaml:"collateral_params"`
DebtParam DebtParam `json:"debt_param" yaml:"debt_param"`
GlobalDebtLimit sdk.Coin `json:"global_debt_limit" yaml:"global_debt_limit"`
SurplusAuctionThreshold sdk.Int `json:"surplus_auction_threshold" yaml:"surplus_auction_threshold"`
SurplusAuctionLot sdk.Int `json:"surplus_auction_lot" yaml:"surplus_auction_lot"`
DebtAuctionThreshold sdk.Int `json:"debt_auction_threshold" yaml:"debt_auction_threshold"`
DebtAuctionLot sdk.Int `json:"debt_auction_lot" yaml:"debt_auction_lot"`
SavingsDistributionFrequency time.Duration `json:"savings_distribution_frequency" yaml:"savings_distribution_frequency"`
CircuitBreaker bool `json:"circuit_breaker" yaml:"circuit_breaker"`
}
// NewParams returns a new params object
func NewParams(
debtLimit sdk.Coin, collateralParams CollateralParams, debtParam DebtParam, surplusThreshold,
surplusLot, debtThreshold, debtLot sdk.Int, distributionFreq time.Duration, breaker bool,
) Params {
return Params{
GlobalDebtLimit: debtLimit,
CollateralParams: collateralParams,
DebtParam: debtParam,
SurplusAuctionThreshold: surplusThreshold,
SurplusAuctionLot: surplusLot,
DebtAuctionThreshold: debtThreshold,
DebtAuctionLot: debtLot,
SavingsDistributionFrequency: distributionFreq,
CircuitBreaker: breaker,
}
}
// CollateralParam governance parameters for each collateral type within the cdp module
type CollateralParam struct {
Denom string `json:"denom" yaml:"denom"` // Coin name of collateral type
LiquidationRatio sdk.Dec `json:"liquidation_ratio" yaml:"liquidation_ratio"` // The ratio (Collateral (priced in stable coin) / Debt) under which a CDP will be liquidated
DebtLimit sdk.Coin `json:"debt_limit" yaml:"debt_limit"` // Maximum amount of debt allowed to be drawn from this collateral type
StabilityFee sdk.Dec `json:"stability_fee" yaml:"stability_fee"` // per second stability fee for loans opened using this collateral
AuctionSize sdk.Int `json:"auction_size" yaml:"auction_size"` // Max amount of collateral to sell off in any one auction.
LiquidationPenalty sdk.Dec `json:"liquidation_penalty" yaml:"liquidation_penalty"` // percentage penalty (between [0, 1]) applied to a cdp if it is liquidated
Prefix byte `json:"prefix" yaml:"prefix"`
SpotMarketID string `json:"spot_market_id" yaml:"spot_market_id"` // marketID of the spot price of the asset from the pricefeed - used for opening CDPs, depositing, withdrawing
LiquidationMarketID string `json:"liquidation_market_id" yaml:"liquidation_market_id"` // marketID of the pricefeed used for liquidation
ConversionFactor sdk.Int `json:"conversion_factor" yaml:"conversion_factor"` // factor for converting internal units to one base unit of collateral
}
// CollateralParams array of CollateralParam
type CollateralParams []CollateralParam
// DebtParam governance params for debt assets
type DebtParam struct {
Denom string `json:"denom" yaml:"denom"`
ReferenceAsset string `json:"reference_asset" yaml:"reference_asset"`
ConversionFactor sdk.Int `json:"conversion_factor" yaml:"conversion_factor"`
DebtFloor sdk.Int `json:"debt_floor" yaml:"debt_floor"` // minimum active loan size, used to prevent dust
SavingsRate sdk.Dec `json:"savings_rate" yaml:"savings_rate"` // the percentage of stability fees that are redirected to savings rate
}

View File

@ -79,6 +79,7 @@ func randomCdpGenState(selection int) types.GenesisState {
CollateralParams: types.CollateralParams{
{
Denom: "xrp",
Type: "xrp-a",
LiquidationRatio: sdk.MustNewDecFromStr("2.0"),
DebtLimit: sdk.NewInt64Coin("usdx", 20000000000000),
StabilityFee: sdk.MustNewDecFromStr("1.000000004431822130"),
@ -91,6 +92,7 @@ func randomCdpGenState(selection int) types.GenesisState {
},
{
Denom: "btc",
Type: "btc-a",
LiquidationRatio: sdk.MustNewDecFromStr("1.25"),
DebtLimit: sdk.NewInt64Coin("usdx", 50000000000000),
StabilityFee: sdk.MustNewDecFromStr("1.000000000782997609"),
@ -103,6 +105,7 @@ func randomCdpGenState(selection int) types.GenesisState {
},
{
Denom: "bnb",
Type: "bnb-a",
LiquidationRatio: sdk.MustNewDecFromStr("1.5"),
DebtLimit: sdk.NewInt64Coin("usdx", 30000000000000),
StabilityFee: sdk.MustNewDecFromStr("1.000000002293273137"),
@ -140,6 +143,7 @@ func randomCdpGenState(selection int) types.GenesisState {
CollateralParams: types.CollateralParams{
{
Denom: "bnb",
Type: "bnb-a",
LiquidationRatio: sdk.MustNewDecFromStr("1.5"),
DebtLimit: sdk.NewInt64Coin("usdx", 100000000000000),
StabilityFee: sdk.MustNewDecFromStr("1.000000002293273137"),

View File

@ -78,7 +78,7 @@ func SimulateMsgCdp(ak types.AccountKeeper, k keeper.Keeper, pfk types.Pricefeed
}
spendableCoins = spendableCoins.Sub(fees)
existingCDP, found := k.GetCdpByOwnerAndDenom(ctx, acc.GetAddress(), randCollateralParam.Denom)
existingCDP, found := k.GetCdpByOwnerAndCollateralType(ctx, acc.GetAddress(), randCollateralParam.Denom)
if !found {
// calculate the minimum amount of collateral that is needed to create a cdp with the debt floor amount of debt and the minimum liquidation ratio
// (debtFloor * liquidationRatio)/priceShifted
@ -101,7 +101,7 @@ func SimulateMsgCdp(ak types.AccountKeeper, k keeper.Keeper, pfk types.Pricefeed
// calculate the max amount of debt that could be drawn for the chosen deposit
maxDebtDraw := collateralDepositValue.Quo(randCollateralParam.LiquidationRatio).TruncateInt()
// check that the debt limit hasn't been reached
availableAssetDebt := randCollateralParam.DebtLimit.Amount.Sub(k.GetTotalPrincipal(ctx, randCollateralParam.Denom, debtParam.Denom))
availableAssetDebt := randCollateralParam.DebtLimit.Amount.Sub(k.GetTotalPrincipal(ctx, randCollateralParam.Type, debtParam.Denom))
if availableAssetDebt.LTE(debtParam.DebtFloor) {
// debt limit has been reached
return simulation.NewOperationMsgBasic(types.ModuleName, "no-operation", "debt limit reached, cannot open cdp", false, nil), nil, nil
@ -111,7 +111,7 @@ func SimulateMsgCdp(ak types.AccountKeeper, k keeper.Keeper, pfk types.Pricefeed
// randomly select a debt draw amount
debtDraw := sdk.NewInt(int64(simulation.RandIntBetween(r, int(debtParam.DebtFloor.Int64()), int(maxDebtDraw.Int64()))))
msg := types.NewMsgCreateCDP(acc.GetAddress(), sdk.NewCoin(randCollateralParam.Denom, collateralDeposit), sdk.NewCoin(debtParam.Denom, debtDraw))
msg := types.NewMsgCreateCDP(acc.GetAddress(), sdk.NewCoin(randCollateralParam.Denom, collateralDeposit), sdk.NewCoin(debtParam.Denom, debtDraw), randCollateralParam.Type)
tx := helpers.GenTx(
[]sdk.Msg{msg},
@ -158,7 +158,7 @@ func SimulateMsgCdp(ak types.AccountKeeper, k keeper.Keeper, pfk types.Pricefeed
// deposit 25% of the time
if hasCoins(spendableCoins, randCollateralParam.Denom) && shouldDeposit(r) {
randDepositAmount := sdk.NewInt(int64(simulation.RandIntBetween(r, 1, int(spendableCoins.AmountOf(randCollateralParam.Denom).Int64()))))
msg := types.NewMsgDeposit(acc.GetAddress(), acc.GetAddress(), sdk.NewCoin(randCollateralParam.Denom, randDepositAmount))
msg := types.NewMsgDeposit(acc.GetAddress(), acc.GetAddress(), sdk.NewCoin(randCollateralParam.Denom, randDepositAmount), randCollateralParam.Type)
tx := helpers.GenTx(
[]sdk.Msg{msg},

View File

@ -12,20 +12,22 @@ import (
// CDP is the state of a single collateralized debt position.
type CDP struct {
ID uint64 `json:"id" yaml:"id"` // unique id for cdp
Owner sdk.AccAddress `json:"owner" yaml:"owner"` // Account that authorizes changes to the CDP
Collateral sdk.Coin `json:"collateral" yaml:"collateral"` // Amount of collateral stored in this CDP
Principal sdk.Coin `json:"principal" yaml:"principal"`
AccumulatedFees sdk.Coin `json:"accumulated_fees" yaml:"accumulated_fees"`
FeesUpdated time.Time `json:"fees_updated" yaml:"fees_updated"` // Amount of stable coin drawn from this CDP
ID uint64 `json:"id" yaml:"id"` // unique id for cdp
Owner sdk.AccAddress `json:"owner" yaml:"owner"` // Account that authorizes changes to the CDP
Type string `json:"type" yaml:"type"` // string representing the unique collateral type of the CDP
Collateral sdk.Coin `json:"collateral" yaml:"collateral"` // Amount of collateral stored in this CDP
Principal sdk.Coin `json:"principal" yaml:"principal"` // Amount of debt drawn using the CDP
AccumulatedFees sdk.Coin `json:"accumulated_fees" yaml:"accumulated_fees"` // Fees accumulated since the CDP was opened or debt was last repayed
FeesUpdated time.Time `json:"fees_updated" yaml:"fees_updated"` // Amount of stable coin drawn from this CDP
}
// NewCDP creates a new CDP object
func NewCDP(id uint64, owner sdk.AccAddress, collateral sdk.Coin, principal sdk.Coin, time time.Time) CDP {
func NewCDP(id uint64, owner sdk.AccAddress, collateral sdk.Coin, collateralType string, principal sdk.Coin, time time.Time) CDP {
fees := sdk.NewCoin(principal.Denom, sdk.ZeroInt())
return CDP{
ID: id,
Owner: owner,
Type: collateralType,
Collateral: collateral,
Principal: principal,
AccumulatedFees: fees,
@ -45,7 +47,7 @@ func (cdp CDP) String() string {
Fees Last Updated: %s`,
cdp.Owner,
cdp.ID,
cdp.Collateral.Denom,
cdp.Type,
cdp.Collateral,
cdp.Principal,
cdp.AccumulatedFees,
@ -73,10 +75,13 @@ func (cdp CDP) Validate() error {
if cdp.FeesUpdated.IsZero() {
return errors.New("cdp updated fee time cannot be zero")
}
if strings.TrimSpace(cdp.Type) == "" {
return fmt.Errorf("cdp type cannot be empty")
}
return nil
}
// GetTotalPrinciple returns the total principle for the cdp
// GetTotalPrincipal returns the total principle for the cdp
func (cdp CDP) GetTotalPrincipal() sdk.Coin {
return cdp.Principal.Add(cdp.AccumulatedFees)
}

View File

@ -42,7 +42,7 @@ func (suite *CdpValidationSuite) TestCdpValidation() {
}{
{
name: "valid cdp",
cdp: types.NewCDP(1, suite.addrs[0], sdk.NewInt64Coin("bnb", 100000), sdk.NewInt64Coin("usdx", 100000), tmtime.Now()),
cdp: types.NewCDP(1, suite.addrs[0], sdk.NewInt64Coin("bnb", 100000), "bnb-a", sdk.NewInt64Coin("usdx", 100000), tmtime.Now()),
errArgs: errArgs{
expectPass: true,
contains: "",
@ -50,7 +50,7 @@ func (suite *CdpValidationSuite) TestCdpValidation() {
},
{
name: "invalid cdp id",
cdp: types.NewCDP(0, suite.addrs[0], sdk.NewInt64Coin("bnb", 100000), sdk.NewInt64Coin("usdx", 100000), tmtime.Now()),
cdp: types.NewCDP(0, suite.addrs[0], sdk.NewInt64Coin("bnb", 100000), "bnb-a", sdk.NewInt64Coin("usdx", 100000), tmtime.Now()),
errArgs: errArgs{
expectPass: false,
contains: "cdp id cannot be 0",
@ -58,7 +58,7 @@ func (suite *CdpValidationSuite) TestCdpValidation() {
},
{
name: "invalid collateral",
cdp: types.CDP{1, suite.addrs[0], sdk.Coin{"", sdk.NewInt(100)}, sdk.Coin{"usdx", sdk.NewInt(100)}, sdk.Coin{"usdx", sdk.NewInt(0)}, tmtime.Now()},
cdp: types.CDP{1, suite.addrs[0], "bnb-a", sdk.Coin{"", sdk.NewInt(100)}, sdk.Coin{"usdx", sdk.NewInt(100)}, sdk.Coin{"usdx", sdk.NewInt(0)}, tmtime.Now()},
errArgs: errArgs{
expectPass: false,
contains: "invalid coins: collateral",
@ -66,7 +66,7 @@ func (suite *CdpValidationSuite) TestCdpValidation() {
},
{
name: "invalid prinicpal",
cdp: types.CDP{1, suite.addrs[0], sdk.Coin{"xrp", sdk.NewInt(100)}, sdk.Coin{"", sdk.NewInt(100)}, sdk.Coin{"usdx", sdk.NewInt(0)}, tmtime.Now()},
cdp: types.CDP{1, suite.addrs[0], "xrp-a", sdk.Coin{"xrp", sdk.NewInt(100)}, sdk.Coin{"", sdk.NewInt(100)}, sdk.Coin{"usdx", sdk.NewInt(0)}, tmtime.Now()},
errArgs: errArgs{
expectPass: false,
contains: "invalid coins: principal",
@ -74,7 +74,7 @@ func (suite *CdpValidationSuite) TestCdpValidation() {
},
{
name: "invalid fees",
cdp: types.CDP{1, suite.addrs[0], sdk.Coin{"xrp", sdk.NewInt(100)}, sdk.Coin{"usdx", sdk.NewInt(100)}, sdk.Coin{"", sdk.NewInt(0)}, tmtime.Now()},
cdp: types.CDP{1, suite.addrs[0], "xrp-a", sdk.Coin{"xrp", sdk.NewInt(100)}, sdk.Coin{"usdx", sdk.NewInt(100)}, sdk.Coin{"", sdk.NewInt(0)}, tmtime.Now()},
errArgs: errArgs{
expectPass: false,
contains: "invalid coins: accumulated fees",
@ -82,12 +82,20 @@ func (suite *CdpValidationSuite) TestCdpValidation() {
},
{
name: "invalid fees updated",
cdp: types.CDP{1, suite.addrs[0], sdk.Coin{"xrp", sdk.NewInt(100)}, sdk.Coin{"usdx", sdk.NewInt(100)}, sdk.Coin{"usdx", sdk.NewInt(0)}, time.Time{}},
cdp: types.CDP{1, suite.addrs[0], "xrp-a", sdk.Coin{"xrp", sdk.NewInt(100)}, sdk.Coin{"usdx", sdk.NewInt(100)}, sdk.Coin{"usdx", sdk.NewInt(0)}, time.Time{}},
errArgs: errArgs{
expectPass: false,
contains: "cdp updated fee time cannot be zero",
},
},
{
name: "invalid type",
cdp: types.CDP{1, suite.addrs[0], "", sdk.Coin{"xrp", sdk.NewInt(100)}, sdk.Coin{"usdx", sdk.NewInt(100)}, sdk.Coin{"usdx", sdk.NewInt(0)}, tmtime.Now()},
errArgs: errArgs{
expectPass: false,
contains: "cdp type cannot be empty",
},
},
}
for _, tc := range testCases {
@ -161,11 +169,11 @@ func (suite *CdpValidationSuite) TestDepositValidation() {
func (suite *CdpValidationSuite) TestCdpGetTotalPrinciple() {
principal := sdk.Coin{"usdx", sdk.NewInt(100500)}
acummulatedFees := sdk.Coin{"usdx", sdk.NewInt(25000)}
accumulatedFees := sdk.Coin{"usdx", sdk.NewInt(25000)}
cdp := types.CDP{Principal: principal, AccumulatedFees: acummulatedFees}
cdp := types.CDP{Principal: principal, AccumulatedFees: accumulatedFees}
suite.Require().Equal(cdp.GetTotalPrincipal(), principal.Add(acummulatedFees))
suite.Require().Equal(cdp.GetTotalPrincipal(), principal.Add(accumulatedFees))
}
func TestCdpValidationSuite(t *testing.T) {

View File

@ -20,17 +20,19 @@ var (
// MsgCreateCDP creates a cdp
type MsgCreateCDP struct {
Sender sdk.AccAddress `json:"sender" yaml:"sender"`
Collateral sdk.Coin `json:"collateral" yaml:"collateral"`
Principal sdk.Coin `json:"principal" yaml:"principal"`
Sender sdk.AccAddress `json:"sender" yaml:"sender"`
Collateral sdk.Coin `json:"collateral" yaml:"collateral"`
Principal sdk.Coin `json:"principal" yaml:"principal"`
CollateralType string `json:"collateral_type" yaml:"collateral_type"`
}
// NewMsgCreateCDP returns a new MsgPlaceBid.
func NewMsgCreateCDP(sender sdk.AccAddress, collateral sdk.Coin, principal sdk.Coin) MsgCreateCDP {
func NewMsgCreateCDP(sender sdk.AccAddress, collateral sdk.Coin, principal sdk.Coin, collateralType string) MsgCreateCDP {
return MsgCreateCDP{
Sender: sender,
Collateral: collateral,
Principal: principal,
Sender: sender,
Collateral: collateral,
Principal: principal,
CollateralType: collateralType,
}
}
@ -51,6 +53,9 @@ func (msg MsgCreateCDP) ValidateBasic() error {
if msg.Principal.IsZero() || !msg.Principal.IsValid() {
return sdkerrors.Wrapf(sdkerrors.ErrInvalidCoins, "principal amount %s", msg.Principal)
}
if strings.TrimSpace(msg.CollateralType) == "" {
return fmt.Errorf("collateral type cannot be empty")
}
return nil
}
@ -76,17 +81,19 @@ func (msg MsgCreateCDP) String() string {
// MsgDeposit deposit collateral to an existing cdp.
type MsgDeposit struct {
Depositor sdk.AccAddress `json:"depositor" yaml:"depositor"`
Owner sdk.AccAddress `json:"owner" yaml:"owner"`
Collateral sdk.Coin `json:"collateral" yaml:"collateral"`
Depositor sdk.AccAddress `json:"depositor" yaml:"depositor"`
Owner sdk.AccAddress `json:"owner" yaml:"owner"`
Collateral sdk.Coin `json:"collateral" yaml:"collateral"`
CollateralType string `json:"collateral_type" yaml:"collateral_type"`
}
// NewMsgDeposit returns a new MsgDeposit
func NewMsgDeposit(owner sdk.AccAddress, depositor sdk.AccAddress, collateral sdk.Coin) MsgDeposit {
func NewMsgDeposit(owner sdk.AccAddress, depositor sdk.AccAddress, collateral sdk.Coin, collateralType string) MsgDeposit {
return MsgDeposit{
Owner: owner,
Depositor: depositor,
Collateral: collateral,
Owner: owner,
Depositor: depositor,
Collateral: collateral,
CollateralType: collateralType,
}
}
@ -107,6 +114,9 @@ func (msg MsgDeposit) ValidateBasic() error {
if !msg.Collateral.IsValid() || msg.Collateral.IsZero() {
return sdkerrors.Wrapf(sdkerrors.ErrInvalidCoins, "collateral amount %s", msg.Collateral)
}
if strings.TrimSpace(msg.CollateralType) == "" {
return fmt.Errorf("collateral type cannot be empty")
}
return nil
}
@ -127,22 +137,25 @@ func (msg MsgDeposit) String() string {
Sender: %s
Owner: %s
Collateral: %s
`, msg.Owner, msg.Owner, msg.Collateral)
CollateralType: %s
`, msg.Owner, msg.Owner, msg.Collateral, msg.CollateralType)
}
// MsgWithdraw withdraw collateral from an existing cdp.
type MsgWithdraw struct {
Depositor sdk.AccAddress `json:"depositor" yaml:"depositor"`
Owner sdk.AccAddress `json:"owner" yaml:"owner"`
Collateral sdk.Coin `json:"collateral" yaml:"collateral"`
Depositor sdk.AccAddress `json:"depositor" yaml:"depositor"`
Owner sdk.AccAddress `json:"owner" yaml:"owner"`
Collateral sdk.Coin `json:"collateral" yaml:"collateral"`
CollateralType string `json:"collateral_type" yaml:"collateral_type"`
}
// NewMsgWithdraw returns a new MsgDeposit
func NewMsgWithdraw(owner sdk.AccAddress, depositor sdk.AccAddress, collateral sdk.Coin) MsgWithdraw {
func NewMsgWithdraw(owner sdk.AccAddress, depositor sdk.AccAddress, collateral sdk.Coin, collateralType string) MsgWithdraw {
return MsgWithdraw{
Owner: owner,
Depositor: depositor,
Collateral: collateral,
Owner: owner,
Depositor: depositor,
Collateral: collateral,
CollateralType: collateralType,
}
}
@ -163,6 +176,9 @@ func (msg MsgWithdraw) ValidateBasic() error {
if !msg.Collateral.IsValid() || msg.Collateral.IsZero() {
return sdkerrors.Wrapf(sdkerrors.ErrInvalidCoins, "collateral amount %s", msg.Collateral)
}
if strings.TrimSpace(msg.CollateralType) == "" {
return fmt.Errorf("collateral type cannot be empty")
}
return nil
}
@ -188,17 +204,17 @@ func (msg MsgWithdraw) String() string {
// MsgDrawDebt draw debt off of collateral in cdp
type MsgDrawDebt struct {
Sender sdk.AccAddress `json:"sender" yaml:"sender"`
CdpDenom string `json:"cdp_denom" yaml:"cdp_denom"`
Principal sdk.Coin `json:"principal" yaml:"principal"`
Sender sdk.AccAddress `json:"sender" yaml:"sender"`
CollateralType string `json:"collateral_type" yaml:"collateral_type"`
Principal sdk.Coin `json:"principal" yaml:"principal"`
}
// NewMsgDrawDebt returns a new MsgDrawDebt
func NewMsgDrawDebt(sender sdk.AccAddress, denom string, principal sdk.Coin) MsgDrawDebt {
func NewMsgDrawDebt(sender sdk.AccAddress, collateralType string, principal sdk.Coin) MsgDrawDebt {
return MsgDrawDebt{
Sender: sender,
CdpDenom: denom,
Principal: principal,
Sender: sender,
CollateralType: collateralType,
Principal: principal,
}
}
@ -213,8 +229,8 @@ func (msg MsgDrawDebt) ValidateBasic() error {
if msg.Sender.Empty() {
return sdkerrors.Wrap(sdkerrors.ErrInvalidAddress, "sender address cannot be empty")
}
if strings.TrimSpace(msg.CdpDenom) == "" {
return errors.New("cdp denom cannot be blank")
if strings.TrimSpace(msg.CollateralType) == "" {
return errors.New("cdp collateral type cannot be blank")
}
if msg.Principal.IsZero() || !msg.Principal.IsValid() {
return sdkerrors.Wrapf(sdkerrors.ErrInvalidCoins, "principal amount %s", msg.Principal)
@ -237,24 +253,24 @@ func (msg MsgDrawDebt) GetSigners() []sdk.AccAddress {
func (msg MsgDrawDebt) String() string {
return fmt.Sprintf(`Draw debt from CDP Message:
Sender: %s
CDP Denom: %s
Collateral Type: %s
Principal: %s
`, msg.Sender, msg.CdpDenom, msg.Principal)
`, msg.Sender, msg.CollateralType, msg.Principal)
}
// MsgRepayDebt repay debt drawn off the collateral in a CDP
type MsgRepayDebt struct {
Sender sdk.AccAddress `json:"sender" yaml:"sender"`
CdpDenom string `json:"cdp_denom" yaml:"cdp_denom"`
Payment sdk.Coin `json:"payment" yaml:"payment"`
Sender sdk.AccAddress `json:"sender" yaml:"sender"`
CollateralType string `json:"collateral_type" yaml:"collateral_type"`
Payment sdk.Coin `json:"payment" yaml:"payment"`
}
// NewMsgRepayDebt returns a new MsgRepayDebt
func NewMsgRepayDebt(sender sdk.AccAddress, denom string, payment sdk.Coin) MsgRepayDebt {
func NewMsgRepayDebt(sender sdk.AccAddress, collateralType string, payment sdk.Coin) MsgRepayDebt {
return MsgRepayDebt{
Sender: sender,
CdpDenom: denom,
Payment: payment,
Sender: sender,
CollateralType: collateralType,
Payment: payment,
}
}
@ -269,8 +285,8 @@ func (msg MsgRepayDebt) ValidateBasic() error {
if msg.Sender.Empty() {
return sdkerrors.Wrap(sdkerrors.ErrInvalidAddress, "sender address cannot be empty")
}
if strings.TrimSpace(msg.CdpDenom) == "" {
return errors.New("cdp denom cannot be blank")
if strings.TrimSpace(msg.CollateralType) == "" {
return errors.New("cdp collateral type cannot be blank")
}
if msg.Payment.IsZero() || !msg.Payment.IsValid() {
return sdkerrors.Wrapf(sdkerrors.ErrInvalidCoins, "payment amount %s", msg.Payment)
@ -293,7 +309,7 @@ func (msg MsgRepayDebt) GetSigners() []sdk.AccAddress {
func (msg MsgRepayDebt) String() string {
return fmt.Sprintf(`Draw debt from CDP Message:
Sender: %s
CDP Denom: %s
Collateral Type: %s
Payment: %s
`, msg.Sender, msg.CdpDenom, msg.Payment)
`, msg.Sender, msg.CollateralType, msg.Payment)
}

View File

@ -19,16 +19,18 @@ var (
func TestMsgCreateCDP(t *testing.T) {
tests := []struct {
description string
sender sdk.AccAddress
collateral sdk.Coin
principal sdk.Coin
expectPass bool
description string
sender sdk.AccAddress
collateral sdk.Coin
principal sdk.Coin
collateralType string
expectPass bool
}{
{"create cdp", addrs[0], coinsSingle, coinsSingle, true},
{"create cdp no collateral", addrs[0], coinsZero, coinsSingle, false},
{"create cdp no debt", addrs[0], coinsSingle, coinsZero, false},
{"create cdp empty owner", sdk.AccAddress{}, coinsSingle, coinsSingle, false},
{"create cdp", addrs[0], coinsSingle, coinsSingle, "type-a", true},
{"create cdp no collateral", addrs[0], coinsZero, coinsSingle, "type-a", false},
{"create cdp no debt", addrs[0], coinsSingle, coinsZero, "type-a", false},
{"create cdp empty owner", sdk.AccAddress{}, coinsSingle, coinsSingle, "type-a", false},
{"create cdp empty type", addrs[0], coinsSingle, coinsSingle, "", false},
}
for _, tc := range tests {
@ -36,6 +38,7 @@ func TestMsgCreateCDP(t *testing.T) {
tc.sender,
tc.collateral,
tc.principal,
tc.collateralType,
)
if tc.expectPass {
require.NoError(t, msg.ValidateBasic(), "test: %v", tc.description)
@ -47,17 +50,19 @@ func TestMsgCreateCDP(t *testing.T) {
func TestMsgDeposit(t *testing.T) {
tests := []struct {
description string
sender sdk.AccAddress
depositor sdk.AccAddress
collateral sdk.Coin
expectPass bool
description string
sender sdk.AccAddress
depositor sdk.AccAddress
collateral sdk.Coin
collateralType string
expectPass bool
}{
{"deposit", addrs[0], addrs[1], coinsSingle, true},
{"deposit", addrs[0], addrs[0], coinsSingle, true},
{"deposit no collateral", addrs[0], addrs[1], coinsZero, false},
{"deposit empty owner", sdk.AccAddress{}, addrs[1], coinsSingle, false},
{"deposit empty depositor", addrs[0], sdk.AccAddress{}, coinsSingle, false},
{"deposit", addrs[0], addrs[1], coinsSingle, "type-a", true},
{"deposit same owner", addrs[0], addrs[0], coinsSingle, "type-a", true},
{"deposit no collateral", addrs[0], addrs[1], coinsZero, "type-a", false},
{"deposit empty owner", sdk.AccAddress{}, addrs[1], coinsSingle, "type-a", false},
{"deposit empty depositor", addrs[0], sdk.AccAddress{}, coinsSingle, "type-a", false},
{"deposit empty type", addrs[0], addrs[0], coinsSingle, "", false},
}
for _, tc := range tests {
@ -65,6 +70,7 @@ func TestMsgDeposit(t *testing.T) {
tc.sender,
tc.depositor,
tc.collateral,
tc.collateralType,
)
if tc.expectPass {
require.NoError(t, msg.ValidateBasic(), "test: %v", tc.description)
@ -76,17 +82,19 @@ func TestMsgDeposit(t *testing.T) {
func TestMsgWithdraw(t *testing.T) {
tests := []struct {
description string
sender sdk.AccAddress
depositor sdk.AccAddress
collateral sdk.Coin
expectPass bool
description string
sender sdk.AccAddress
depositor sdk.AccAddress
collateral sdk.Coin
collateralType string
expectPass bool
}{
{"withdraw", addrs[0], addrs[1], coinsSingle, true},
{"withdraw", addrs[0], addrs[0], coinsSingle, true},
{"withdraw no collateral", addrs[0], addrs[1], coinsZero, false},
{"withdraw empty owner", sdk.AccAddress{}, addrs[1], coinsSingle, false},
{"withdraw empty depositor", addrs[0], sdk.AccAddress{}, coinsSingle, false},
{"withdraw", addrs[0], addrs[1], coinsSingle, "type-a", true},
{"withdraw", addrs[0], addrs[0], coinsSingle, "type-a", true},
{"withdraw no collateral", addrs[0], addrs[1], coinsZero, "type-a", false},
{"withdraw empty owner", sdk.AccAddress{}, addrs[1], coinsSingle, "type-a", false},
{"withdraw empty depositor", addrs[0], sdk.AccAddress{}, coinsSingle, "type-a", false},
{"withdraw empty type", addrs[0], addrs[0], coinsSingle, "", false},
}
for _, tc := range tests {
@ -94,6 +102,7 @@ func TestMsgWithdraw(t *testing.T) {
tc.sender,
tc.depositor,
tc.collateral,
tc.collateralType,
)
if tc.expectPass {
require.NoError(t, msg.ValidateBasic(), "test: %v", tc.description)
@ -105,11 +114,11 @@ func TestMsgWithdraw(t *testing.T) {
func TestMsgDrawDebt(t *testing.T) {
tests := []struct {
description string
sender sdk.AccAddress
denom string
principal sdk.Coin
expectPass bool
description string
sender sdk.AccAddress
collateralType string
principal sdk.Coin
expectPass bool
}{
{"draw debt", addrs[0], sdk.DefaultBondDenom, coinsSingle, true},
{"draw debt no debt", addrs[0], sdk.DefaultBondDenom, coinsZero, false},
@ -120,7 +129,7 @@ func TestMsgDrawDebt(t *testing.T) {
for _, tc := range tests {
msg := NewMsgDrawDebt(
tc.sender,
tc.denom,
tc.collateralType,
tc.principal,
)
if tc.expectPass {

View File

@ -107,7 +107,8 @@ func DefaultParams() Params {
// CollateralParam governance parameters for each collateral type within the cdp module
type CollateralParam struct {
Denom string `json:"denom" yaml:"denom"` // Coin name of collateral type
Denom string `json:"denom" yaml:"denom"` // Coin name of collateral type
Type string `json:"type" yaml:"type"`
LiquidationRatio sdk.Dec `json:"liquidation_ratio" yaml:"liquidation_ratio"` // The ratio (Collateral (priced in stable coin) / Debt) under which a CDP will be liquidated
DebtLimit sdk.Coin `json:"debt_limit" yaml:"debt_limit"` // Maximum amount of debt allowed to be drawn from this collateral type
StabilityFee sdk.Dec `json:"stability_fee" yaml:"stability_fee"` // per second stability fee for loans opened using this collateral
@ -123,6 +124,7 @@ type CollateralParam struct {
func (cp CollateralParam) String() string {
return fmt.Sprintf(`Collateral:
Denom: %s
Type: %s
Liquidation Ratio: %s
Stability Fee: %s
Liquidation Penalty: %s
@ -132,7 +134,7 @@ func (cp CollateralParam) String() string {
Spot Market ID: %s
Liquidation Market ID: %s
Conversion Factor: %s`,
cp.Denom, cp.LiquidationRatio, cp.StabilityFee, cp.LiquidationPenalty, cp.DebtLimit, cp.AuctionSize, cp.Prefix, cp.SpotMarketID, cp.LiquidationMarketID, cp.ConversionFactor)
cp.Denom, cp.Type, cp.LiquidationRatio, cp.StabilityFee, cp.LiquidationPenalty, cp.DebtLimit, cp.AuctionSize, cp.Prefix, cp.SpotMarketID, cp.LiquidationMarketID, cp.ConversionFactor)
}
// CollateralParams array of CollateralParam
@ -299,8 +301,8 @@ func validateCollateralParams(i interface{}) error {
return fmt.Errorf("invalid parameter type: %T", i)
}
collateralDupMap := make(map[string]bool)
prefixDupMap := make(map[int]bool)
typeDupMap := make(map[string]bool)
for _, cp := range collateralParams {
if err := sdk.ValidateDenom(cp.Denom); err != nil {
return fmt.Errorf("collateral denom invalid %s", cp.Denom)
@ -310,6 +312,10 @@ func validateCollateralParams(i interface{}) error {
return fmt.Errorf("spot market id cannot be blank %s", cp)
}
if strings.TrimSpace(cp.Type) == "" {
return fmt.Errorf("collateral type cannot be blank %s", cp)
}
if strings.TrimSpace(cp.LiquidationMarketID) == "" {
return fmt.Errorf("liquidation market id cannot be blank %s", cp)
}
@ -326,12 +332,11 @@ func validateCollateralParams(i interface{}) error {
prefixDupMap[prefix] = true
_, found = collateralDupMap[cp.Denom]
_, found = typeDupMap[cp.Type]
if found {
return fmt.Errorf("duplicate collateral denom: %s", cp.Denom)
return fmt.Errorf("duplicate cdp collateral type: %s", cp.Type)
}
collateralDupMap[cp.Denom] = true
typeDupMap[cp.Type] = true
if !cp.DebtLimit.IsValid() {
return fmt.Errorf("debt limit for all collaterals should be positive, is %s for %s", cp.DebtLimit, cp.Denom)

View File

@ -66,6 +66,7 @@ func (suite *ParamsTestSuite) TestParamValidation() {
collateralParams: types.CollateralParams{
{
Denom: "bnb",
Type: "bnb-a",
LiquidationRatio: sdk.MustNewDecFromStr("1.5"),
DebtLimit: sdk.NewInt64Coin("usdx", 2000000000000),
StabilityFee: sdk.MustNewDecFromStr("1.000000001547125958"),
@ -103,6 +104,7 @@ func (suite *ParamsTestSuite) TestParamValidation() {
collateralParams: types.CollateralParams{
{
Denom: "bnb",
Type: "bnb-a",
LiquidationRatio: sdk.MustNewDecFromStr("1.5"),
DebtLimit: sdk.NewInt64Coin("usdx", 2000000000000),
StabilityFee: sdk.MustNewDecFromStr("1.000000001547125958"),
@ -140,6 +142,7 @@ func (suite *ParamsTestSuite) TestParamValidation() {
collateralParams: types.CollateralParams{
{
Denom: "bnb",
Type: "bnb-a",
LiquidationRatio: sdk.MustNewDecFromStr("1.5"),
DebtLimit: sdk.NewInt64Coin("usdx", 2000000000000),
StabilityFee: sdk.MustNewDecFromStr("1.000000001547125958"),
@ -177,6 +180,7 @@ func (suite *ParamsTestSuite) TestParamValidation() {
collateralParams: types.CollateralParams{
{
Denom: "bnb",
Type: "bnb-a",
LiquidationRatio: sdk.MustNewDecFromStr("1.5"),
DebtLimit: sdk.NewInt64Coin("usdx", 2000000000000),
StabilityFee: sdk.MustNewDecFromStr("1.000000001547125958"),
@ -189,6 +193,7 @@ func (suite *ParamsTestSuite) TestParamValidation() {
},
{
Denom: "xrp",
Type: "xrp-a",
LiquidationRatio: sdk.MustNewDecFromStr("1.5"),
DebtLimit: sdk.NewInt64Coin("usdx", 2000000000000),
StabilityFee: sdk.MustNewDecFromStr("1.000000001547125958"),
@ -226,6 +231,7 @@ func (suite *ParamsTestSuite) TestParamValidation() {
collateralParams: types.CollateralParams{
{
Denom: "bnb",
Type: "bnb-a",
LiquidationRatio: sdk.MustNewDecFromStr("1.5"),
DebtLimit: sdk.NewInt64Coin("usdx", 2000000000000),
StabilityFee: sdk.MustNewDecFromStr("1.000000001547125958"),
@ -238,6 +244,7 @@ func (suite *ParamsTestSuite) TestParamValidation() {
},
{
Denom: "xrp",
Type: "xrp-a",
LiquidationRatio: sdk.MustNewDecFromStr("1.5"),
DebtLimit: sdk.NewInt64Coin("usdx", 2000000000000),
StabilityFee: sdk.MustNewDecFromStr("1.000000001547125958"),
@ -275,6 +282,7 @@ func (suite *ParamsTestSuite) TestParamValidation() {
collateralParams: types.CollateralParams{
{
Denom: "bnb",
Type: "bnb-a",
LiquidationRatio: sdk.MustNewDecFromStr("1.5"),
DebtLimit: sdk.NewInt64Coin("usdx", 2000000000000),
StabilityFee: sdk.MustNewDecFromStr("1.000000001547125958"),
@ -287,6 +295,7 @@ func (suite *ParamsTestSuite) TestParamValidation() {
},
{
Denom: "xrp",
Type: "xrp-a",
LiquidationRatio: sdk.MustNewDecFromStr("1.5"),
DebtLimit: sdk.NewInt64Coin("susd", 2000000000000),
StabilityFee: sdk.MustNewDecFromStr("1.000000001547125958"),
@ -361,6 +370,7 @@ func (suite *ParamsTestSuite) TestParamValidation() {
collateralParams: types.CollateralParams{
{
Denom: "bnb",
Type: "bnb-a",
LiquidationRatio: sdk.MustNewDecFromStr("1.5"),
DebtLimit: sdk.NewInt64Coin("usdx", 2000000000000),
StabilityFee: sdk.MustNewDecFromStr("1.000000001547125958"),
@ -392,12 +402,13 @@ func (suite *ParamsTestSuite) TestParamValidation() {
},
},
{
name: "invalid collateral params duplicate denom",
name: "invalid collateral params duplicate denom + type",
args: args{
globalDebtLimit: sdk.NewInt64Coin("usdx", 2000000000000),
collateralParams: types.CollateralParams{
{
Denom: "bnb",
Type: "bnb-a",
LiquidationRatio: sdk.MustNewDecFromStr("1.5"),
DebtLimit: sdk.NewInt64Coin("usdx", 1000000000000),
StabilityFee: sdk.MustNewDecFromStr("1.000000001547125958"),
@ -410,6 +421,7 @@ func (suite *ParamsTestSuite) TestParamValidation() {
},
{
Denom: "bnb",
Type: "bnb-a",
LiquidationRatio: sdk.MustNewDecFromStr("1.5"),
DebtLimit: sdk.NewInt64Coin("usdx", 1000000000000),
StabilityFee: sdk.MustNewDecFromStr("1.000000001547125958"),
@ -437,7 +449,58 @@ func (suite *ParamsTestSuite) TestParamValidation() {
},
errArgs: errArgs{
expectPass: false,
contains: "duplicate collateral denom",
contains: "duplicate cdp collateral type",
},
},
{
name: "valid collateral params duplicate denom + different type",
args: args{
globalDebtLimit: sdk.NewInt64Coin("usdx", 2000000000000),
collateralParams: types.CollateralParams{
{
Denom: "bnb",
Type: "bnb-a",
LiquidationRatio: sdk.MustNewDecFromStr("1.5"),
DebtLimit: sdk.NewInt64Coin("usdx", 1000000000000),
StabilityFee: sdk.MustNewDecFromStr("1.000000001547125958"),
LiquidationPenalty: sdk.MustNewDecFromStr("0.05"),
AuctionSize: sdk.NewInt(50000000000),
Prefix: 0x20,
SpotMarketID: "bnb:usd",
LiquidationMarketID: "bnb:usd",
ConversionFactor: sdk.NewInt(8),
},
{
Denom: "bnb",
Type: "bnb-b",
LiquidationRatio: sdk.MustNewDecFromStr("1.5"),
DebtLimit: sdk.NewInt64Coin("usdx", 1000000000000),
StabilityFee: sdk.MustNewDecFromStr("1.000000001547125958"),
LiquidationPenalty: sdk.MustNewDecFromStr("0.05"),
AuctionSize: sdk.NewInt(50000000000),
Prefix: 0x21,
SpotMarketID: "bnb:usd",
LiquidationMarketID: "bnb:usd",
ConversionFactor: sdk.NewInt(8),
},
},
debtParam: types.DebtParam{
Denom: "usdx",
ReferenceAsset: "usd",
ConversionFactor: sdk.NewInt(6),
DebtFloor: sdk.NewInt(10000000),
SavingsRate: sdk.MustNewDecFromStr("0.95"),
},
surplusThreshold: types.DefaultSurplusThreshold,
surplusLot: types.DefaultSurplusLot,
debtThreshold: types.DefaultDebtThreshold,
debtLot: types.DefaultDebtLot,
distributionFreq: types.DefaultSavingsDistributionFrequency,
breaker: types.DefaultCircuitBreaker,
},
errArgs: errArgs{
expectPass: true,
contains: "",
},
},
{
@ -447,6 +510,7 @@ func (suite *ParamsTestSuite) TestParamValidation() {
collateralParams: types.CollateralParams{
{
Denom: "bnb",
Type: "bnb-a",
LiquidationRatio: sdk.MustNewDecFromStr("1.5"),
DebtLimit: sdk.NewInt64Coin("usdx", 1000000000000),
StabilityFee: sdk.MustNewDecFromStr("1.000000001547125958"),
@ -459,6 +523,7 @@ func (suite *ParamsTestSuite) TestParamValidation() {
},
{
Denom: "xrp",
Type: "xrp-a",
LiquidationRatio: sdk.MustNewDecFromStr("1.5"),
DebtLimit: sdk.NewInt64Coin("usdx", 1000000000000),
StabilityFee: sdk.MustNewDecFromStr("1.000000001547125958"),
@ -496,6 +561,7 @@ func (suite *ParamsTestSuite) TestParamValidation() {
collateralParams: types.CollateralParams{
{
Denom: "bnb",
Type: "bnb-a",
LiquidationRatio: sdk.MustNewDecFromStr("1.5"),
DebtLimit: sdk.Coin{},
StabilityFee: sdk.MustNewDecFromStr("1.000000001547125958"),
@ -533,6 +599,7 @@ func (suite *ParamsTestSuite) TestParamValidation() {
collateralParams: types.CollateralParams{
{
Denom: "bnb",
Type: "bnb-a",
LiquidationRatio: sdk.MustNewDecFromStr("1.5"),
DebtLimit: sdk.NewInt64Coin("usdx", 1000000000000),
StabilityFee: sdk.MustNewDecFromStr("1.000000001547125958"),
@ -570,6 +637,7 @@ func (suite *ParamsTestSuite) TestParamValidation() {
collateralParams: types.CollateralParams{
{
Denom: "bnb",
Type: "bnb-a",
LiquidationRatio: sdk.MustNewDecFromStr("1.5"),
DebtLimit: sdk.NewInt64Coin("usdx", 1000000000000),
StabilityFee: sdk.MustNewDecFromStr("1.000000001547125958"),
@ -607,6 +675,7 @@ func (suite *ParamsTestSuite) TestParamValidation() {
collateralParams: types.CollateralParams{
{
Denom: "bnb",
Type: "bnb-a",
LiquidationRatio: sdk.MustNewDecFromStr("1.5"),
DebtLimit: sdk.NewInt64Coin("usdx", 1000000000000),
StabilityFee: sdk.MustNewDecFromStr("1.1"),
@ -644,6 +713,7 @@ func (suite *ParamsTestSuite) TestParamValidation() {
collateralParams: types.CollateralParams{
{
Denom: "bnb",
Type: "bnb-a",
LiquidationRatio: sdk.MustNewDecFromStr("1.5"),
DebtLimit: sdk.NewInt64Coin("usdx", 2000000000000),
StabilityFee: sdk.MustNewDecFromStr("1.000000001547125958"),
@ -681,6 +751,7 @@ func (suite *ParamsTestSuite) TestParamValidation() {
collateralParams: types.CollateralParams{
{
Denom: "bnb",
Type: "bnb-a",
LiquidationRatio: sdk.MustNewDecFromStr("1.5"),
DebtLimit: sdk.NewInt64Coin("usdx", 2000000000000),
StabilityFee: sdk.MustNewDecFromStr("1.000000001547125958"),

View File

@ -13,60 +13,60 @@ const (
QueryGetParams = "params"
QueryGetAccounts = "accounts"
RestOwner = "owner"
RestCollateralDenom = "collateral-denom"
RestCollateralType = "collateral-type"
RestRatio = "ratio"
)
// QueryCdpsParams params for query /cdp/cdps
type QueryCdpsParams struct {
CollateralDenom string // get CDPs with this collateral denom
CollateralType string // get CDPs with this collateral type
}
// NewQueryCdpsParams returns QueryCdpsParams
func NewQueryCdpsParams(denom string) QueryCdpsParams {
func NewQueryCdpsParams(collateralType string) QueryCdpsParams {
return QueryCdpsParams{
CollateralDenom: denom,
CollateralType: collateralType,
}
}
// QueryCdpParams params for query /cdp/cdp
type QueryCdpParams struct {
CollateralDenom string // get CDPs with this collateral denom
Owner sdk.AccAddress // get CDPs belonging to this owner
CollateralType string // get CDPs with this collateral type
Owner sdk.AccAddress // get CDPs belonging to this owner
}
// NewQueryCdpParams returns QueryCdpParams
func NewQueryCdpParams(owner sdk.AccAddress, denom string) QueryCdpParams {
func NewQueryCdpParams(owner sdk.AccAddress, collateralType string) QueryCdpParams {
return QueryCdpParams{
Owner: owner,
CollateralDenom: denom,
Owner: owner,
CollateralType: collateralType,
}
}
// 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
CollateralType string // get CDPs with this collateral type
Owner sdk.AccAddress // get CDPs belonging to this owner
}
// NewQueryCdpDeposits returns QueryCdpDeposits
func NewQueryCdpDeposits(owner sdk.AccAddress, denom string) QueryCdpDeposits {
func NewQueryCdpDeposits(owner sdk.AccAddress, collateralType string) QueryCdpDeposits {
return QueryCdpDeposits{
Owner: owner,
CollateralDenom: denom,
Owner: owner,
CollateralType: collateralType,
}
}
// QueryCdpsByRatioParams params for query /cdp/cdps/ratio
type QueryCdpsByRatioParams struct {
CollateralDenom string // get CDPs with this collateral denom
Ratio sdk.Dec // get CDPs below this collateral:debt ratio
CollateralType string
Ratio sdk.Dec // get CDPs below this collateral:debt ratio
}
// NewQueryCdpsByRatioParams returns QueryCdpsByRatioParams
func NewQueryCdpsByRatioParams(denom string, ratio sdk.Dec) QueryCdpsByRatioParams {
func NewQueryCdpsByRatioParams(collateralType string, ratio sdk.Dec) QueryCdpsByRatioParams {
return QueryCdpsByRatioParams{
CollateralDenom: denom,
Ratio: ratio,
CollateralType: collateralType,
Ratio: ratio,
}
}

View File

@ -32,6 +32,7 @@ func (suite *PermissionTestSuite) TestSubParamChangePermission_Allows() {
testCPs := cdptypes.CollateralParams{
{
Denom: "bnb",
Type: "bnb-a",
LiquidationRatio: d("2.0"),
DebtLimit: c("usdx", 1000000000000),
StabilityFee: d("1.000000001547125958"),
@ -44,6 +45,7 @@ func (suite *PermissionTestSuite) TestSubParamChangePermission_Allows() {
},
{
Denom: "btc",
Type: "btc-a",
LiquidationRatio: d("1.5"),
DebtLimit: c("usdx", 1000000000),
StabilityFee: d("1.000000001547125958"),

View File

@ -87,6 +87,7 @@ func (suite *KeeperTestSuite) TestSubmitProposal() {
testCP := cdptypes.CollateralParams{{
Denom: "bnb",
Type: "bnb-a",
LiquidationRatio: d("1.5"),
DebtLimit: c("usdx", 1000000000000),
StabilityFee: d("1.000000001547125958"), // %5 apr

View File

@ -28,7 +28,7 @@ const (
QuerierRoute = types.QuerierRoute
QueryGetClaims = types.QueryGetClaims
RestClaimOwner = types.RestClaimOwner
RestClaimDenom = types.RestClaimDenom
RestClaimCollateralType = types.RestClaimCollateralType
QueryGetParams = types.QueryGetParams
)

View File

@ -51,8 +51,8 @@ func queryClaimsCmd(queryRoute string, cdc *codec.Codec) *cobra.Command {
return err
}
bz, err := cdc.MarshalJSON(types.QueryClaimsParams{
Owner: ownerAddress,
Denom: args[1],
Owner: ownerAddress,
CollateralType: args[1],
})
if err != nil {
return err

View File

@ -34,7 +34,7 @@ func queryClaimsHandlerFn(cliCtx context.CLIContext) http.HandlerFunc {
vars := mux.Vars(r)
ownerBech32 := vars[types.RestClaimOwner]
denom := vars[types.RestClaimDenom]
denom := vars[types.RestClaimCollateralType]
owner, err := sdk.AccAddressFromBech32(ownerBech32)
if err != nil {

View File

@ -42,7 +42,7 @@ func postClaimHandlerFn(cliCtx context.CLIContext) http.HandlerFunc {
return
}
msg := types.NewMsgClaimReward(requestBody.Sender, requestBody.Denom)
msg := types.NewMsgClaimReward(requestBody.Sender, requestBody.CollateralType)
if err := msg.ValidateBasic(); err != nil {
rest.WriteErrorResponse(w, http.StatusBadRequest, err.Error())
return

View File

@ -25,7 +25,7 @@ func InitGenesis(ctx sdk.Context, k keeper.Keeper, supplyKeeper types.SupplyKeep
k.SetParams(ctx, gs.Params)
for _, r := range gs.Params.Rewards {
k.SetNextClaimPeriodID(ctx, r.Denom, 1)
k.SetNextClaimPeriodID(ctx, r.CollateralType, 1)
}
// only set the previous block time if it's different than default
@ -47,7 +47,7 @@ func InitGenesis(ctx sdk.Context, k keeper.Keeper, supplyKeeper types.SupplyKeep
}
for _, id := range gs.NextClaimPeriodIDs {
k.SetNextClaimPeriodID(ctx, id.Denom, id.ID)
k.SetNextClaimPeriodID(ctx, id.CollateralType, id.ID)
}
}

View File

@ -24,13 +24,13 @@ func NewHandler(k keeper.Keeper) sdk.Handler {
func handleMsgClaimReward(ctx sdk.Context, k keeper.Keeper, msg types.MsgClaimReward) (*sdk.Result, error) {
claims, found := k.GetClaimsByAddressAndDenom(ctx, msg.Sender, msg.Denom)
claims, found := k.GetClaimsByAddressAndCollateralType(ctx, msg.Sender, msg.CollateralType)
if !found {
return nil, sdkerrors.Wrapf(types.ErrNoClaimsFound, "address: %s, denom: %s", msg.Sender, msg.Denom)
return nil, sdkerrors.Wrapf(types.ErrNoClaimsFound, "address: %s, collateral type: %s", msg.Sender, msg.CollateralType)
}
for _, claim := range claims {
err := k.PayoutClaim(ctx, claim.Owner, claim.Denom, claim.ClaimPeriodID)
err := k.PayoutClaim(ctx, claim.Owner, claim.CollateralType, claim.ClaimPeriodID)
if err != nil {
return nil, err
}

View File

@ -37,10 +37,10 @@ func NewKeeper(
}
}
// GetRewardPeriod returns the reward period from the store for the input denom and a boolean for if it was found
func (k Keeper) GetRewardPeriod(ctx sdk.Context, denom string) (types.RewardPeriod, bool) {
// GetRewardPeriod returns the reward period from the store for the input collateral type and a boolean for if it was found
func (k Keeper) GetRewardPeriod(ctx sdk.Context, collateralType string) (types.RewardPeriod, bool) {
store := prefix.NewStore(ctx.KVStore(k.key), types.RewardPeriodKeyPrefix)
bz := store.Get([]byte(denom))
bz := store.Get([]byte(collateralType))
if bz == nil {
return types.RewardPeriod{}, false
}
@ -53,13 +53,13 @@ func (k Keeper) GetRewardPeriod(ctx sdk.Context, denom string) (types.RewardPeri
func (k Keeper) SetRewardPeriod(ctx sdk.Context, rp types.RewardPeriod) {
store := prefix.NewStore(ctx.KVStore(k.key), types.RewardPeriodKeyPrefix)
bz := k.cdc.MustMarshalBinaryBare(rp)
store.Set([]byte(rp.Denom), bz)
store.Set([]byte(rp.CollateralType), bz)
}
// DeleteRewardPeriod deletes the reward period in the store for the input denom,
func (k Keeper) DeleteRewardPeriod(ctx sdk.Context, denom string) {
// DeleteRewardPeriod deletes the reward period in the store for the input collateral type,
func (k Keeper) DeleteRewardPeriod(ctx sdk.Context, collateralType string) {
store := prefix.NewStore(ctx.KVStore(k.key), types.RewardPeriodKeyPrefix)
store.Delete([]byte(denom))
store.Delete([]byte(collateralType))
}
// IterateRewardPeriods iterates over all reward period objects in the store and preforms a callback function
@ -86,44 +86,44 @@ func (k Keeper) GetAllRewardPeriods(ctx sdk.Context) types.RewardPeriods {
return rps
}
// GetNextClaimPeriodID returns the highest claim period id in the store for the input denom
func (k Keeper) GetNextClaimPeriodID(ctx sdk.Context, denom string) uint64 {
// GetNextClaimPeriodID returns the highest claim period id in the store for the input collateral type
func (k Keeper) GetNextClaimPeriodID(ctx sdk.Context, collateralType string) uint64 {
store := prefix.NewStore(ctx.KVStore(k.key), types.NextClaimPeriodIDPrefix)
bz := store.Get([]byte(denom))
bz := store.Get([]byte(collateralType))
if bz == nil {
k.SetNextClaimPeriodID(ctx, denom, 1)
k.SetNextClaimPeriodID(ctx, collateralType, 1)
return uint64(1)
}
return types.BytesToUint64(bz)
}
// SetNextClaimPeriodID sets the highest claim period id in the store for the input denom
func (k Keeper) SetNextClaimPeriodID(ctx sdk.Context, denom string, id uint64) {
// SetNextClaimPeriodID sets the highest claim period id in the store for the input collateral type
func (k Keeper) SetNextClaimPeriodID(ctx sdk.Context, collateralType string, id uint64) {
store := prefix.NewStore(ctx.KVStore(k.key), types.NextClaimPeriodIDPrefix)
store.Set([]byte(denom), sdk.Uint64ToBigEndian(id))
store.Set([]byte(collateralType), sdk.Uint64ToBigEndian(id))
}
// IterateClaimPeriodIDKeysAndValues iterates over the claim period id (value) and denom (key) of each claim period id in the store and performs a callback function
func (k Keeper) IterateClaimPeriodIDKeysAndValues(ctx sdk.Context, cb func(denom string, id uint64) (stop bool)) {
// IterateClaimPeriodIDKeysAndValues iterates over the claim period id (value) and collateral type (key) of each claim period id in the store and performs a callback function
func (k Keeper) IterateClaimPeriodIDKeysAndValues(ctx sdk.Context, cb func(collateralType string, id uint64) (stop bool)) {
store := prefix.NewStore(ctx.KVStore(k.key), types.NextClaimPeriodIDPrefix)
iterator := sdk.KVStorePrefixIterator(store, []byte{})
defer iterator.Close()
for ; iterator.Valid(); iterator.Next() {
id := types.BytesToUint64(iterator.Value())
denom := string(iterator.Key())
if cb(denom, id) {
collateralType := string(iterator.Key())
if cb(collateralType, id) {
break
}
}
}
// GetAllClaimPeriodIDPairs returns all denom:nextClaimPeriodID pairs in the store
// GetAllClaimPeriodIDPairs returns all collateralType:nextClaimPeriodID pairs in the store
func (k Keeper) GetAllClaimPeriodIDPairs(ctx sdk.Context) types.GenesisClaimPeriodIDs {
ids := types.GenesisClaimPeriodIDs{}
k.IterateClaimPeriodIDKeysAndValues(ctx, func(denom string, id uint64) (stop bool) {
k.IterateClaimPeriodIDKeysAndValues(ctx, func(collateralType string, id uint64) (stop bool) {
genID := types.GenesisClaimPeriodID{
Denom: denom,
ID: id,
CollateralType: collateralType,
ID: id,
}
ids = append(ids, genID)
return false
@ -131,11 +131,11 @@ func (k Keeper) GetAllClaimPeriodIDPairs(ctx sdk.Context) types.GenesisClaimPeri
return ids
}
// GetClaimPeriod returns claim period in the store for the input ID and denom and a boolean for if it was found
func (k Keeper) GetClaimPeriod(ctx sdk.Context, id uint64, denom string) (types.ClaimPeriod, bool) {
// GetClaimPeriod returns claim period in the store for the input ID and collateral type and a boolean for if it was found
func (k Keeper) GetClaimPeriod(ctx sdk.Context, id uint64, collateralType string) (types.ClaimPeriod, bool) {
var cp types.ClaimPeriod
store := prefix.NewStore(ctx.KVStore(k.key), types.ClaimPeriodKeyPrefix)
bz := store.Get(types.GetClaimPeriodPrefix(denom, id))
bz := store.Get(types.GetClaimPeriodPrefix(collateralType, id))
if bz == nil {
return types.ClaimPeriod{}, false
}
@ -143,17 +143,17 @@ func (k Keeper) GetClaimPeriod(ctx sdk.Context, id uint64, denom string) (types.
return cp, true
}
// SetClaimPeriod sets the claim period in the store for the input ID and denom
// SetClaimPeriod sets the claim period in the store for the input ID and collateral type
func (k Keeper) SetClaimPeriod(ctx sdk.Context, cp types.ClaimPeriod) {
store := prefix.NewStore(ctx.KVStore(k.key), types.ClaimPeriodKeyPrefix)
bz := k.cdc.MustMarshalBinaryBare(cp)
store.Set(types.GetClaimPeriodPrefix(cp.Denom, cp.ID), bz)
store.Set(types.GetClaimPeriodPrefix(cp.CollateralType, cp.ID), bz)
}
// DeleteClaimPeriod deletes the claim period in the store for the input ID and denom
func (k Keeper) DeleteClaimPeriod(ctx sdk.Context, id uint64, denom string) {
// DeleteClaimPeriod deletes the claim period in the store for the input ID and collateral type
func (k Keeper) DeleteClaimPeriod(ctx sdk.Context, id uint64, collateralType string) {
store := prefix.NewStore(ctx.KVStore(k.key), types.ClaimPeriodKeyPrefix)
store.Delete(types.GetClaimPeriodPrefix(denom, id))
store.Delete(types.GetClaimPeriodPrefix(collateralType, id))
}
// IterateClaimPeriods iterates over all claim period objects in the store and preforms a callback function
@ -180,10 +180,10 @@ func (k Keeper) GetAllClaimPeriods(ctx sdk.Context) types.ClaimPeriods {
return cps
}
// GetClaim returns the claim in the store corresponding the the input address denom and id and a boolean for if the claim was found
func (k Keeper) GetClaim(ctx sdk.Context, addr sdk.AccAddress, denom string, id uint64) (types.Claim, bool) {
// GetClaim returns the claim in the store corresponding the the input address collateral type and id and a boolean for if the claim was found
func (k Keeper) GetClaim(ctx sdk.Context, addr sdk.AccAddress, collateralType string, id uint64) (types.Claim, bool) {
store := prefix.NewStore(ctx.KVStore(k.key), types.ClaimKeyPrefix)
bz := store.Get(types.GetClaimPrefix(addr, denom, id))
bz := store.Get(types.GetClaimPrefix(addr, collateralType, id))
if bz == nil {
return types.Claim{}, false
}
@ -192,18 +192,18 @@ func (k Keeper) GetClaim(ctx sdk.Context, addr sdk.AccAddress, denom string, id
return c, true
}
// SetClaim sets the claim in the store corresponding to the input address, denom, and id
// SetClaim sets the claim in the store corresponding to the input address, collateral type, and id
func (k Keeper) SetClaim(ctx sdk.Context, c types.Claim) {
store := prefix.NewStore(ctx.KVStore(k.key), types.ClaimKeyPrefix)
bz := k.cdc.MustMarshalBinaryBare(c)
store.Set(types.GetClaimPrefix(c.Owner, c.Denom, c.ClaimPeriodID), bz)
store.Set(types.GetClaimPrefix(c.Owner, c.CollateralType, c.ClaimPeriodID), bz)
}
// DeleteClaim deletes the claim in the store corresponding to the input address, denom, and id
func (k Keeper) DeleteClaim(ctx sdk.Context, owner sdk.AccAddress, denom string, id uint64) {
// DeleteClaim deletes the claim in the store corresponding to the input address, collateral type, and id
func (k Keeper) DeleteClaim(ctx sdk.Context, owner sdk.AccAddress, collateralType string, id uint64) {
store := prefix.NewStore(ctx.KVStore(k.key), types.ClaimKeyPrefix)
store.Delete(types.GetClaimPrefix(owner, denom, id))
store.Delete(types.GetClaimPrefix(owner, collateralType, id))
}
// IterateClaims iterates over all claim objects in the store and preforms a callback function

View File

@ -140,8 +140,8 @@ func (suite *KeeperTestSuite) TestIterateMethods() {
suite.Equal(2, len(claims))
var genIDs types.GenesisClaimPeriodIDs
suite.keeper.IterateClaimPeriodIDKeysAndValues(suite.ctx, func(denom string, id uint64) (stop bool) {
genID := types.GenesisClaimPeriodID{Denom: denom, ID: id}
suite.keeper.IterateClaimPeriodIDKeysAndValues(suite.ctx, func(collateralType string, id uint64) (stop bool) {
genID := types.GenesisClaimPeriodID{CollateralType: collateralType, ID: id}
genIDs = append(genIDs, genID)
return false
})

View File

@ -13,21 +13,21 @@ import (
)
// PayoutClaim sends the timelocked claim coins to the input address
func (k Keeper) PayoutClaim(ctx sdk.Context, addr sdk.AccAddress, denom string, id uint64) error {
claim, found := k.GetClaim(ctx, addr, denom, id)
func (k Keeper) PayoutClaim(ctx sdk.Context, addr sdk.AccAddress, collateralType string, id uint64) error {
claim, found := k.GetClaim(ctx, addr, collateralType, id)
if !found {
return sdkerrors.Wrapf(types.ErrClaimNotFound, "id: %d, denom %s, address: %s", id, denom, addr)
return sdkerrors.Wrapf(types.ErrClaimNotFound, "id: %d, collateral type %s, address: %s", id, collateralType, addr)
}
claimPeriod, found := k.GetClaimPeriod(ctx, id, denom)
claimPeriod, found := k.GetClaimPeriod(ctx, id, collateralType)
if !found {
return sdkerrors.Wrapf(types.ErrClaimPeriodNotFound, "id: %d, denom: %s", id, denom)
return sdkerrors.Wrapf(types.ErrClaimPeriodNotFound, "id: %d, collateral type: %s", id, collateralType)
}
err := k.SendTimeLockedCoinsToAccount(ctx, types.IncentiveMacc, addr, sdk.NewCoins(claim.Reward), int64(claimPeriod.TimeLock.Seconds()))
if err != nil {
return err
}
k.DeleteClaim(ctx, addr, denom, id)
k.DeleteClaim(ctx, addr, collateralType, id)
ctx.EventManager().EmitEvent(
sdk.NewEvent(
@ -97,13 +97,13 @@ func (k Keeper) DeleteExpiredClaimsAndClaimPeriods(ctx sdk.Context) {
return false
}
k.IterateClaims(ctx, func(c types.Claim) (stop bool) {
if !(c.Denom == cp.Denom && c.ClaimPeriodID == cp.ID) {
if !(c.CollateralType == cp.CollateralType && c.ClaimPeriodID == cp.ID) {
return false
}
k.DeleteClaim(ctx, c.Owner, c.Denom, c.ClaimPeriodID)
k.DeleteClaim(ctx, c.Owner, c.CollateralType, c.ClaimPeriodID)
return false
})
k.DeleteClaimPeriod(ctx, cp.ID, cp.Denom)
k.DeleteClaimPeriod(ctx, cp.ID, cp.CollateralType)
ctx.EventManager().EmitEvent(
sdk.NewEvent(
types.EventTypeClaimPeriodExpiry,
@ -115,14 +115,14 @@ func (k Keeper) DeleteExpiredClaimsAndClaimPeriods(ctx sdk.Context) {
})
}
// GetClaimsByAddressAndDenom returns all claims for a specific user and address and a bool for if any were found
func (k Keeper) GetClaimsByAddressAndDenom(ctx sdk.Context, addr sdk.AccAddress, denom string) (claims types.Claims, found bool) {
// GetClaimsByAddressAndCollateralType returns all claims for a specific user and address and a bool for if any were found
func (k Keeper) GetClaimsByAddressAndCollateralType(ctx sdk.Context, addr sdk.AccAddress, collateralType string) (claims types.Claims, found bool) {
found = false
k.IterateClaimPeriods(ctx, func(cp types.ClaimPeriod) (stop bool) {
if cp.Denom != denom {
if cp.CollateralType != collateralType {
return false
}
c, hasClaim := k.GetClaim(ctx, addr, cp.Denom, cp.ID)
c, hasClaim := k.GetClaim(ctx, addr, cp.CollateralType, cp.ID)
if !hasClaim {
return false
}

View File

@ -73,7 +73,7 @@ func queryGetClaims(ctx sdk.Context, req abci.RequestQuery, k Keeper) ([]byte, e
if err != nil {
return nil, sdkerrors.Wrap(sdkerrors.ErrJSONUnmarshal, err.Error())
}
claims, _ := k.GetClaimsByAddressAndDenom(ctx, requestParams.Owner, requestParams.Denom)
claims, _ := k.GetClaimsByAddressAndCollateralType(ctx, requestParams.Owner, requestParams.CollateralType)
bz, err := codec.MarshalJSONIndent(k.cdc, claims)
if err != nil {

View File

@ -12,9 +12,9 @@ import (
// HandleRewardPeriodExpiry deletes expired RewardPeriods from the store and creates a ClaimPeriod in the store for each expired RewardPeriod
func (k Keeper) HandleRewardPeriodExpiry(ctx sdk.Context, rp types.RewardPeriod) {
k.CreateUniqueClaimPeriod(ctx, rp.Denom, rp.ClaimEnd, rp.ClaimTimeLock)
k.CreateUniqueClaimPeriod(ctx, rp.CollateralType, rp.ClaimEnd, rp.ClaimTimeLock)
store := prefix.NewStore(ctx.KVStore(k.key), types.RewardPeriodKeyPrefix)
store.Delete([]byte(rp.Denom))
store.Delete([]byte(rp.CollateralType))
return
}
@ -36,10 +36,10 @@ func (k Keeper) CreateAndDeleteRewardPeriods(ctx sdk.Context) {
params := k.GetParams(ctx)
for _, r := range params.Rewards {
_, found := k.GetRewardPeriod(ctx, r.Denom)
_, found := k.GetRewardPeriod(ctx, r.CollateralType)
// if governance has made a reward inactive, delete the current period
if found && !r.Active {
k.DeleteRewardPeriod(ctx, r.Denom)
k.DeleteRewardPeriod(ctx, r.CollateralType)
}
// if a reward period for an active reward is not found, create one
if !found && r.Active {
@ -61,7 +61,7 @@ func (k Keeper) ApplyRewardsToCdps(ctx sdk.Context) {
k.IterateRewardPeriods(ctx, func(rp types.RewardPeriod) bool {
expired := false
// the total amount of usdx created with the collateral type being incentivized
totalPrincipal := k.cdpKeeper.GetTotalPrincipal(ctx, rp.Denom, types.PrincipalDenom)
totalPrincipal := k.cdpKeeper.GetTotalPrincipal(ctx, rp.CollateralType, types.PrincipalDenom)
// the number of seconds since last payout
timeElapsed := sdk.NewInt(ctx.BlockTime().Unix() - previousBlockTime.Unix())
if rp.End.Before(ctx.BlockTime()) {
@ -71,15 +71,15 @@ func (k Keeper) ApplyRewardsToCdps(ctx sdk.Context) {
// the amount of rewards to pay (rewardAmount * timeElapsed)
rewardsThisPeriod := rp.Reward.Amount.Mul(timeElapsed)
id := k.GetNextClaimPeriodID(ctx, rp.Denom)
k.cdpKeeper.IterateCdpsByDenom(ctx, rp.Denom, func(cdp cdptypes.CDP) bool {
id := k.GetNextClaimPeriodID(ctx, rp.CollateralType)
k.cdpKeeper.IterateCdpsByCollateralType(ctx, rp.CollateralType, func(cdp cdptypes.CDP) bool {
rewardsShare := sdk.NewDecFromInt(cdp.GetTotalPrincipal().Amount).Quo(sdk.NewDecFromInt(totalPrincipal))
// sanity check - don't create zero claims
if rewardsShare.IsZero() {
return false
}
rewardsEarned := rewardsShare.Mul(sdk.NewDecFromInt(rewardsThisPeriod)).RoundInt()
k.AddToClaim(ctx, cdp.Owner, rp.Denom, id, sdk.NewCoin(types.GovDenom, rewardsEarned))
k.AddToClaim(ctx, cdp.Owner, rp.CollateralType, id, sdk.NewCoin(types.GovDenom, rewardsEarned))
return false
})
if !expired {
@ -93,9 +93,9 @@ func (k Keeper) ApplyRewardsToCdps(ctx sdk.Context) {
}
// CreateUniqueClaimPeriod creates a new claim period in the store and updates the highest claim period id
func (k Keeper) CreateUniqueClaimPeriod(ctx sdk.Context, denom string, end time.Time, timeLock time.Duration) {
id := k.GetNextClaimPeriodID(ctx, denom)
claimPeriod := types.NewClaimPeriod(denom, id, end, timeLock)
func (k Keeper) CreateUniqueClaimPeriod(ctx sdk.Context, collateralType string, end time.Time, timeLock time.Duration) {
id := k.GetNextClaimPeriodID(ctx, collateralType)
claimPeriod := types.NewClaimPeriod(collateralType, id, end, timeLock)
ctx.EventManager().EmitEvent(
sdk.NewEvent(
types.EventTypeClaimPeriod,
@ -103,16 +103,16 @@ func (k Keeper) CreateUniqueClaimPeriod(ctx sdk.Context, denom string, end time.
),
)
k.SetClaimPeriod(ctx, claimPeriod)
k.SetNextClaimPeriodID(ctx, denom, id+1)
k.SetNextClaimPeriodID(ctx, collateralType, id+1)
}
// AddToClaim adds the amount to an existing claim or creates a new one for that amount
func (k Keeper) AddToClaim(ctx sdk.Context, addr sdk.AccAddress, denom string, id uint64, amount sdk.Coin) {
claim, found := k.GetClaim(ctx, addr, denom, id)
func (k Keeper) AddToClaim(ctx sdk.Context, addr sdk.AccAddress, collateralType string, id uint64, amount sdk.Coin) {
claim, found := k.GetClaim(ctx, addr, collateralType, id)
if found {
claim.Reward = claim.Reward.Add(amount)
} else {
claim = types.NewClaim(addr, amount, denom, id)
claim = types.NewClaim(addr, amount, collateralType, id)
}
k.SetClaim(ctx, claim)
}

View File

@ -116,7 +116,7 @@ func (suite *KeeperTestSuite) TestApplyRewardsToCdps() {
})
suite.Equal(3, len(claims))
// there should be no associated claim period, because the reward period has not ended yet
_, found := suite.keeper.GetClaimPeriod(suite.ctx, 1, "bnb")
_, found := suite.keeper.GetClaimPeriod(suite.ctx, 1, "bnb-a")
suite.False(found)
// move ctx to the reward period expiry and check that the claim period has been created and the next claim period id has increased
@ -128,9 +128,9 @@ func (suite *KeeperTestSuite) TestApplyRewardsToCdps() {
// delete the old reward period amd create a new one
suite.keeper.CreateAndDeleteRewardPeriods(suite.ctx)
})
_, found = suite.keeper.GetClaimPeriod(suite.ctx, 1, "bnb")
_, found = suite.keeper.GetClaimPeriod(suite.ctx, 1, "bnb-a")
suite.True(found)
testID := suite.keeper.GetNextClaimPeriodID(suite.ctx, "bnb")
testID := suite.keeper.GetNextClaimPeriodID(suite.ctx, "bnb-a")
suite.Equal(uint64(2), testID)
// move the context forward by 100 periods
@ -186,6 +186,7 @@ func (suite *KeeperTestSuite) setupCdpChain() {
CollateralParams: cdp.CollateralParams{
{
Denom: "bnb",
Type: "bnb-a",
LiquidationRatio: sdk.MustNewDecFromStr("2.0"),
DebtLimit: sdk.NewInt64Coin("usdx", 1000000000000),
StabilityFee: sdk.MustNewDecFromStr("1.000000001547125958"), // %5 apr
@ -213,10 +214,10 @@ func (suite *KeeperTestSuite) setupCdpChain() {
}
incentiveGS := types.NewGenesisState(
types.NewParams(
true, types.Rewards{types.NewReward(true, "bnb", c("ukava", 1000000000), time.Hour*7*24, time.Hour*24*365, time.Hour*7*24)},
true, types.Rewards{types.NewReward(true, "bnb-a", c("ukava", 1000000000), time.Hour*7*24, time.Hour*24*365, time.Hour*7*24)},
),
types.DefaultPreviousBlockTime,
types.RewardPeriods{types.NewRewardPeriod("bnb", ctx.BlockTime(), ctx.BlockTime().Add(time.Hour*7*24), c("ukava", 1000), ctx.BlockTime().Add(time.Hour*7*24*2), time.Hour*365*24)},
types.RewardPeriods{types.NewRewardPeriod("bnb-a", ctx.BlockTime(), ctx.BlockTime().Add(time.Hour*7*24), c("ukava", 1000), ctx.BlockTime().Add(time.Hour*7*24*2), time.Hour*365*24)},
types.ClaimPeriods{},
types.Claims{},
types.GenesisClaimPeriodIDs{})
@ -242,11 +243,11 @@ func (suite *KeeperTestSuite) setupCdpChain() {
suite.ctx = ctx
// create 3 cdps
cdpKeeper := tApp.GetCDPKeeper()
err := cdpKeeper.AddCdp(suite.ctx, addrs[0], c("bnb", 10000000000), c("usdx", 10000000))
err := cdpKeeper.AddCdp(suite.ctx, addrs[0], c("bnb", 10000000000), c("usdx", 10000000), "bnb-a")
suite.Require().NoError(err)
err = cdpKeeper.AddCdp(suite.ctx, addrs[1], c("bnb", 100000000000), c("usdx", 100000000))
err = cdpKeeper.AddCdp(suite.ctx, addrs[1], c("bnb", 100000000000), c("usdx", 100000000), "bnb-a")
suite.Require().NoError(err)
err = cdpKeeper.AddCdp(suite.ctx, addrs[2], c("bnb", 1000000000000), c("usdx", 1000000000))
err = cdpKeeper.AddCdp(suite.ctx, addrs[2], c("bnb", 1000000000000), c("usdx", 1000000000), "bnb-a")
suite.Require().NoError(err)
// total usd is 1110

View File

@ -28,7 +28,7 @@ func RandomizedGenState(simState *module.SimulationState) {
simState.Cdc.MustUnmarshalJSON(simState.GenState[cdp.ModuleName], &cdpGenesis)
if len(CollateralDenoms) == 0 {
for _, collateral := range cdpGenesis.Params.CollateralParams {
CollateralDenoms = append(CollateralDenoms, collateral.Denom)
CollateralDenoms = append(CollateralDenoms, collateral.Type)
}
}
@ -88,11 +88,11 @@ func genRewardPeriods(r *rand.Rand, timestamp time.Time, rewards types.Rewards)
end := start.Add(reward.Duration).UTC()
baseRewardAmount := reward.AvailableRewards.Amount.Quo(sdk.NewInt(100)) // base period reward is 1/100 total reward
// Earlier periods have larger rewards
amount := sdk.NewCoin(reward.Denom, baseRewardAmount.Mul(sdk.NewInt(int64(i))))
amount := sdk.NewCoin("ukava", baseRewardAmount.Mul(sdk.NewInt(int64(i))))
claimEnd := end.Add(reward.ClaimDuration)
claimTimeLock := reward.TimeLock
// Create reward period and append to array
rewardPeriods[i] = types.NewRewardPeriod(reward.Denom, start, end, amount, claimEnd, claimTimeLock)
rewardPeriods[i] = types.NewRewardPeriod(reward.CollateralType, start, end, amount, claimEnd, claimTimeLock)
// Update start time of next reward period
rewardPeriodStart = end
}
@ -105,7 +105,7 @@ func genClaimPeriods(rewardPeriods types.RewardPeriods) types.ClaimPeriods {
claimPeriods := make(types.ClaimPeriods, len(rewardPeriods))
for i, rewardPeriod := range rewardPeriods {
// Increment reward period count for this denom (this is our claim period's ID)
denom := rewardPeriod.Denom
denom := rewardPeriod.CollateralType
numbRewardPeriods := denomRewardPeriodsCount[denom] + 1
denomRewardPeriodsCount[denom] = numbRewardPeriods
// Set end and timelock from the associated reward period
@ -123,11 +123,11 @@ func genNextClaimPeriodIds(cps types.ClaimPeriods) types.GenesisClaimPeriodIDs {
mostRecentClaimPeriodByDenom := make(map[string]uint64)
var claimPeriodIDs types.GenesisClaimPeriodIDs
for _, cp := range cps {
if cp.ID <= mostRecentClaimPeriodByDenom[cp.Denom] {
if cp.ID <= mostRecentClaimPeriodByDenom[cp.CollateralType] {
continue
}
claimPeriodIDs = append(claimPeriodIDs, types.GenesisClaimPeriodID{Denom: cp.Denom, ID: cp.ID})
mostRecentClaimPeriodByDenom[cp.Denom] = cp.ID
claimPeriodIDs = append(claimPeriodIDs, types.GenesisClaimPeriodID{CollateralType: cp.CollateralType, ID: cp.ID})
mostRecentClaimPeriodByDenom[cp.CollateralType] = cp.ID
}
return claimPeriodIDs

View File

@ -78,7 +78,7 @@ func SimulateMsgClaimReward(ak auth.AccountKeeper, sk types.SupplyKeeper, k keep
claimer, claim, found := findValidAccountClaimPair(accs, openClaims, func(acc simulation.Account, claim types.Claim) bool {
if validAccounts[acc.Address.String()] { // Address must be valid type
if claim.Owner.Equals(acc.Address) { // Account must be claim owner
allClaims, found := k.GetClaimsByAddressAndDenom(ctx, claim.Owner, claim.Denom)
allClaims, found := k.GetClaimsByAddressAndCollateralType(ctx, claim.Owner, claim.CollateralType)
if found { // found should always be true
var rewards sdk.Coins
for _, individualClaim := range allClaims {
@ -105,7 +105,7 @@ func SimulateMsgClaimReward(ak auth.AccountKeeper, sk types.SupplyKeeper, k keep
return simulation.NoOpMsg(types.ModuleName), nil, fmt.Errorf("couldn't find account %s", claimer.Address)
}
msg := types.NewMsgClaimReward(claimer.Address, claim.Denom)
msg := types.NewMsgClaimReward(claimer.Address, claim.CollateralType)
tx := helpers.GenTx(
[]sdk.Msg{msg},

View File

@ -7,9 +7,9 @@ import (
// DONTCOVER
var (
ErrClaimNotFound = sdkerrors.Register(ModuleName, 1, "no claim with input id found for owner and denom")
ErrClaimPeriodNotFound = sdkerrors.Register(ModuleName, 2, "no claim period found for id and denom")
ErrClaimNotFound = sdkerrors.Register(ModuleName, 1, "no claim with input id found for owner and collateral type")
ErrClaimPeriodNotFound = sdkerrors.Register(ModuleName, 2, "no claim period found for id and collateral type")
ErrInvalidAccountType = sdkerrors.Register(ModuleName, 3, "account type not supported")
ErrNoClaimsFound = sdkerrors.Register(ModuleName, 4, "no claims with denom found for address")
ErrNoClaimsFound = sdkerrors.Register(ModuleName, 4, "no claims with collateral type found for address")
ErrInsufficientModAccountBalance = sdkerrors.Register(ModuleName, 5, "module account has insufficient balance to pay claim")
)

View File

@ -17,8 +17,8 @@ type SupplyKeeper interface {
// CdpKeeper defines the expected cdp keeper for interacting with cdps
type CdpKeeper interface {
IterateCdpsByDenom(ctx sdk.Context, denom string, cb func(cdp cdptypes.CDP) (stop bool))
GetTotalPrincipal(ctx sdk.Context, collateralDenom string, principalDenom string) (total sdk.Int)
IterateCdpsByCollateralType(ctx sdk.Context, collateralType string, cb func(cdp cdptypes.CDP) (stop bool))
GetTotalPrincipal(ctx sdk.Context, collateralType string, principalDenom string) (total sdk.Int)
}
// AccountKeeper defines the expected keeper interface for interacting with account

View File

@ -4,15 +4,14 @@ import (
"bytes"
"errors"
"fmt"
"strings"
"time"
sdk "github.com/cosmos/cosmos-sdk/types"
)
// GenesisClaimPeriodID stores the next claim id and its corresponding denom
// GenesisClaimPeriodID stores the next claim id and its corresponding collateral type
type GenesisClaimPeriodID struct {
Denom string `json:"denom" yaml:"denom"`
ID uint64 `json:"id" yaml:"id"`
CollateralType string `json:"collateral_type" yaml:"collateral_type"`
ID uint64 `json:"id" yaml:"id"`
}
// Validate performs a basic check of a GenesisClaimPeriodID fields.
@ -20,7 +19,10 @@ func (gcp GenesisClaimPeriodID) Validate() error {
if gcp.ID == 0 {
return errors.New("genesis claim period id cannot be 0")
}
return sdk.ValidateDenom(gcp.Denom)
if strings.TrimSpace(gcp.CollateralType) == "" {
return fmt.Errorf("collateral type cannot be blank: %v", gcp)
}
return nil
}
// GenesisClaimPeriodIDs array of GenesisClaimPeriodID
@ -32,9 +34,9 @@ func (gcps GenesisClaimPeriodIDs) Validate() error {
seenIDS := make(map[string]bool)
var key string
for _, gcp := range gcps {
key = gcp.Denom + string(gcp.ID)
key = gcp.CollateralType + string(gcp.ID)
if seenIDS[key] {
return fmt.Errorf("duplicated genesis claim period with id %d and denom %s", gcp.ID, gcp.Denom)
return fmt.Errorf("duplicated genesis claim period with id %d and collateral type %s", gcp.ID, gcp.CollateralType)
}
if err := gcp.Validate(); err != nil {

View File

@ -20,29 +20,29 @@ func TestGenesisClaimPeriodIDsValidate(t *testing.T) {
{
"valid",
GenesisClaimPeriodIDs{
{Denom: "bnb", ID: 1},
{CollateralType: "bnb", ID: 1},
},
true,
},
{
"invalid denom",
"invalid collateral type",
GenesisClaimPeriodIDs{
{Denom: "", ID: 1},
{CollateralType: "", ID: 1},
},
false,
},
{
"invalid ID",
GenesisClaimPeriodIDs{
{Denom: "bnb", ID: 0},
{CollateralType: "bnb", ID: 0},
},
false,
},
{
"duplicate",
GenesisClaimPeriodIDs{
{Denom: "bnb", ID: 1},
{Denom: "bnb", ID: 1},
{CollateralType: "bnb", ID: 1},
{CollateralType: "bnb", ID: 1},
},
false,
},
@ -74,7 +74,7 @@ func TestGenesisStateValidate(t *testing.T) {
rewardPeriods := RewardPeriods{NewRewardPeriod("bnb", now, now.Add(time.Hour), sdk.NewCoin("bnb", sdk.OneInt()), now, 10)}
claimPeriods := ClaimPeriods{NewClaimPeriod("bnb", 10, now, 100)}
claims := Claims{NewClaim(owner, sdk.NewCoin("bnb", sdk.OneInt()), "bnb", 10)}
gcps := GenesisClaimPeriodIDs{{Denom: "bnb", ID: 1}}
gcps := GenesisClaimPeriodIDs{{CollateralType: "bnb", ID: 1}}
testCases := []struct {
msg string

View File

@ -33,24 +33,24 @@ var (
)
// Keys
// 0x00:Denom <> RewardPeriod the current active reward period (max 1 reward period per denom)
// 0x01:Denom:ID <> ClaimPeriod object for that ID, indexed by denom and ID
// 0x02:Denom:ID:Owner <> Claim object, indexed by Denom, ID and owner
// 0x03:Denom <> NextClaimPeriodIDPrefix the ID of the next claim period, indexed by denom
// 0x00:CollateralType <> RewardPeriod the current active reward period (max 1 reward period per collateral type)
// 0x01:CollateralType:ID <> ClaimPeriod object for that ID, indexed by collateral type and ID
// 0x02:CollateralType:ID:Owner <> Claim object, indexed by collateral type, ID and owner
// 0x03:CollateralType <> NextClaimPeriodIDPrefix the ID of the next claim period, indexed by collateral type
// BytesToUint64 returns uint64 format from a byte array
func BytesToUint64(bz []byte) uint64 {
return binary.BigEndian.Uint64(bz)
}
// GetClaimPeriodPrefix returns the key (denom + id) for a claim prefix
func GetClaimPeriodPrefix(denom string, id uint64) []byte {
return createKey([]byte(denom), sdk.Uint64ToBigEndian(id))
// GetClaimPeriodPrefix returns the key (collateral type + id) for a claim prefix
func GetClaimPeriodPrefix(collateralType string, id uint64) []byte {
return createKey([]byte(collateralType), sdk.Uint64ToBigEndian(id))
}
// GetClaimPrefix returns the key (denom + id + address) for a claim
func GetClaimPrefix(addr sdk.AccAddress, denom string, id uint64) []byte {
return createKey([]byte(denom), sdk.Uint64ToBigEndian(id), addr)
// GetClaimPrefix returns the key (collateral type + id + address) for a claim
func GetClaimPrefix(addr sdk.AccAddress, collateralType string, id uint64) []byte {
return createKey([]byte(collateralType), sdk.Uint64ToBigEndian(id), addr)
}
func createKey(bytes ...[]byte) (r []byte) {

View File

@ -1,6 +1,9 @@
package types
import (
"fmt"
"strings"
sdk "github.com/cosmos/cosmos-sdk/types"
sdkerrors "github.com/cosmos/cosmos-sdk/types/errors"
)
@ -10,15 +13,15 @@ var _ sdk.Msg = &MsgClaimReward{}
// MsgClaimReward message type used to claim rewards
type MsgClaimReward struct {
Sender sdk.AccAddress `json:"sender" yaml:"sender"`
Denom string `json:"denom" yaml:"denom"`
Sender sdk.AccAddress `json:"sender" yaml:"sender"`
CollateralType string `json:"collateral_type" yaml:"collateral_type"`
}
// NewMsgClaimReward returns a new MsgClaimReward.
func NewMsgClaimReward(sender sdk.AccAddress, denom string) MsgClaimReward {
func NewMsgClaimReward(sender sdk.AccAddress, collateralType string) MsgClaimReward {
return MsgClaimReward{
Sender: sender,
Denom: denom,
Sender: sender,
CollateralType: collateralType,
}
}
@ -33,7 +36,10 @@ func (msg MsgClaimReward) ValidateBasic() error {
if msg.Sender.Empty() {
return sdkerrors.Wrap(sdkerrors.ErrInvalidAddress, "sender address cannot be empty")
}
return sdk.ValidateDenom(msg.Denom)
if strings.TrimSpace(msg.CollateralType) == "" {
return fmt.Errorf("collateral type cannot be blank")
}
return nil
}
// GetSignBytes gets the canonical byte representation of the Msg.

View File

@ -13,9 +13,9 @@ import (
)
type msgTest struct {
from sdk.AccAddress
denom string
expectPass bool
from sdk.AccAddress
collateralType string
expectPass bool
}
type MsgTestSuite struct {
@ -27,19 +27,19 @@ type MsgTestSuite struct {
func (suite *MsgTestSuite) SetupTest() {
tests := []msgTest{
{
from: sdk.AccAddress(crypto.AddressHash([]byte("KavaTest1"))),
denom: "bnb",
expectPass: true,
from: sdk.AccAddress(crypto.AddressHash([]byte("KavaTest1"))),
collateralType: "bnb",
expectPass: true,
},
{
from: sdk.AccAddress(crypto.AddressHash([]byte("KavaTest1"))),
denom: "",
expectPass: false,
from: sdk.AccAddress(crypto.AddressHash([]byte("KavaTest1"))),
collateralType: "",
expectPass: false,
},
{
from: sdk.AccAddress{},
denom: "bnb",
expectPass: false,
from: sdk.AccAddress{},
collateralType: "bnb",
expectPass: false,
},
}
suite.tests = tests
@ -47,7 +47,7 @@ func (suite *MsgTestSuite) SetupTest() {
func (suite *MsgTestSuite) TestMsgValidation() {
for _, t := range suite.tests {
msg := types.NewMsgClaimReward(t.from, t.denom)
msg := types.NewMsgClaimReward(t.from, t.collateralType)
err := msg.ValidateBasic()
if t.expectPass {
suite.Require().NoError(err)

View File

@ -2,6 +2,7 @@ package types
import (
"fmt"
"strings"
"time"
sdk "github.com/cosmos/cosmos-sdk/types"
@ -93,7 +94,7 @@ func validateRewardsParam(i interface{}) error {
// Reward stores the specified state for a single reward period.
type Reward struct {
Active bool `json:"active" yaml:"active"` // governance switch to disable a period
Denom string `json:"denom" yaml:"denom"` // the collateral denom rewards apply to, must be found in the cdp collaterals
CollateralType string `json:"collateral_type" yaml:"collateral_type"` // the collateral type rewards apply to, must be found in the cdp collaterals
AvailableRewards sdk.Coin `json:"available_rewards" yaml:"available_rewards"` // the total amount of coins distributed per period
Duration time.Duration `json:"duration" yaml:"duration"` // the duration of the period
TimeLock time.Duration `json:"time_lock" yaml:"time_lock"` // how long rewards for this period are timelocked
@ -101,10 +102,10 @@ type Reward struct {
}
// NewReward returns a new Reward
func NewReward(active bool, denom string, reward sdk.Coin, duration time.Duration, timelock time.Duration, claimDuration time.Duration) Reward {
func NewReward(active bool, collateralType string, reward sdk.Coin, duration time.Duration, timelock time.Duration, claimDuration time.Duration) Reward {
return Reward{
Active: active,
Denom: denom,
CollateralType: collateralType,
AvailableRewards: reward,
Duration: duration,
TimeLock: timelock,
@ -116,32 +117,35 @@ func NewReward(active bool, denom string, reward sdk.Coin, duration time.Duratio
func (r Reward) String() string {
return fmt.Sprintf(`Reward:
Active: %t,
Denom: %s,
CollateralType: %s,
Available Rewards: %s,
Duration: %s,
Time Lock: %s,
Claim Duration: %s`,
r.Active, r.Denom, r.AvailableRewards, r.Duration, r.TimeLock, r.ClaimDuration)
r.Active, r.CollateralType, r.AvailableRewards, r.Duration, r.TimeLock, r.ClaimDuration)
}
// Validate performs a basic check of a reward fields.
func (r Reward) Validate() error {
if !r.AvailableRewards.IsValid() {
return fmt.Errorf("invalid reward coins %s for %s", r.AvailableRewards, r.Denom)
return fmt.Errorf("invalid reward coins %s for %s", r.AvailableRewards, r.CollateralType)
}
if !r.AvailableRewards.IsPositive() {
return fmt.Errorf("reward amount must be positive, is %s for %s", r.AvailableRewards, r.Denom)
return fmt.Errorf("reward amount must be positive, is %s for %s", r.AvailableRewards, r.CollateralType)
}
if r.Duration <= 0 {
return fmt.Errorf("reward duration must be positive, is %s for %s", r.Duration, r.Denom)
return fmt.Errorf("reward duration must be positive, is %s for %s", r.Duration, r.CollateralType)
}
if r.TimeLock < 0 {
return fmt.Errorf("reward timelock must be non-negative, is %s for %s", r.TimeLock, r.Denom)
return fmt.Errorf("reward timelock must be non-negative, is %s for %s", r.TimeLock, r.CollateralType)
}
if r.ClaimDuration <= 0 {
return fmt.Errorf("claim duration must be positive, is %s for %s", r.ClaimDuration, r.Denom)
return fmt.Errorf("claim duration must be positive, is %s for %s", r.ClaimDuration, r.CollateralType)
}
return sdk.ValidateDenom(r.Denom)
if strings.TrimSpace(r.CollateralType) == "" {
return fmt.Errorf("collateral type cannot be blank: %s", r)
}
return nil
}
// Rewards array of Reward
@ -150,17 +154,17 @@ type Rewards []Reward
// Validate checks if all the rewards are valid and there are no duplicated
// entries.
func (rs Rewards) Validate() error {
rewardDenoms := make(map[string]bool)
rewardCollateralTypes := make(map[string]bool)
for _, r := range rs {
if rewardDenoms[r.Denom] {
return fmt.Errorf("cannot have duplicate reward denoms: %s", r.Denom)
if rewardCollateralTypes[r.CollateralType] {
return fmt.Errorf("cannot have duplicate reward collateral types: %s", r.CollateralType)
}
if err := r.Validate(); err != nil {
return err
}
rewardDenoms[r.Denom] = true
rewardCollateralTypes[r.CollateralType] = true
}
return nil
}

View File

@ -38,7 +38,7 @@ func (suite *ParamTestSuite) SetupTest() {
Rewards: types.Rewards{
types.Reward{
Active: true,
Denom: "bnb",
CollateralType: "bnb-a",
AvailableRewards: sdk.NewCoin("ukava", sdk.NewInt(10000000000)),
Duration: time.Hour * 24 * 7,
TimeLock: time.Hour * 8766,
@ -58,7 +58,7 @@ func (suite *ParamTestSuite) SetupTest() {
Rewards: types.Rewards{
types.Reward{
Active: true,
Denom: "bnb",
CollateralType: "bnb-a",
AvailableRewards: sdk.NewCoin("ukava", sdk.NewInt(10000000000)),
Duration: time.Hour * 24 * 7,
TimeLock: time.Hour * 8766,
@ -78,7 +78,7 @@ func (suite *ParamTestSuite) SetupTest() {
Rewards: types.Rewards{
types.Reward{
Active: true,
Denom: "bnb",
CollateralType: "bnb-a",
AvailableRewards: sdk.NewCoin("ukava", sdk.NewInt(10000000000)),
Duration: time.Hour * 24 * 7,
TimeLock: time.Hour * 8766,
@ -86,7 +86,7 @@ func (suite *ParamTestSuite) SetupTest() {
},
types.Reward{
Active: true,
Denom: "bnb",
CollateralType: "bnb-a",
AvailableRewards: sdk.NewCoin("ukava", sdk.NewInt(10000000000)),
Duration: time.Hour * 24 * 7,
TimeLock: time.Hour * 8766,
@ -96,7 +96,7 @@ func (suite *ParamTestSuite) SetupTest() {
},
errResult: errResult{
expectPass: false,
contains: "cannot have duplicate reward denoms",
contains: "cannot have duplicate reward collateral type",
},
},
{
@ -106,7 +106,7 @@ func (suite *ParamTestSuite) SetupTest() {
Rewards: types.Rewards{
types.Reward{
Active: true,
Denom: "bnb",
CollateralType: "bnb-a",
AvailableRewards: sdk.NewCoin("ukava", sdk.NewInt(10000000000)),
Duration: time.Hour * -24 * 7,
TimeLock: time.Hour * 8766,
@ -126,7 +126,7 @@ func (suite *ParamTestSuite) SetupTest() {
Rewards: types.Rewards{
types.Reward{
Active: true,
Denom: "bnb",
CollateralType: "bnb-a",
AvailableRewards: sdk.NewCoin("ukava", sdk.NewInt(10000000000)),
Duration: time.Hour * 24 * 7,
TimeLock: time.Hour * -8766,
@ -146,7 +146,7 @@ func (suite *ParamTestSuite) SetupTest() {
Rewards: types.Rewards{
types.Reward{
Active: true,
Denom: "bnb",
CollateralType: "bnb-a",
AvailableRewards: sdk.NewCoin("ukava", sdk.NewInt(10000000000)),
Duration: time.Hour * 24 * 7,
TimeLock: time.Hour * 8766,
@ -166,7 +166,7 @@ func (suite *ParamTestSuite) SetupTest() {
Rewards: types.Rewards{
types.Reward{
Active: true,
Denom: "bnb",
CollateralType: "bnb-a",
AvailableRewards: sdk.NewCoin("ukava", sdk.NewInt(0)),
Duration: time.Hour * 24 * 7,
TimeLock: time.Hour * 8766,
@ -180,13 +180,13 @@ func (suite *ParamTestSuite) SetupTest() {
},
},
{
name: "empty reward denom",
name: "empty reward collateral type",
params: types.Params{
Active: true,
Rewards: types.Rewards{
types.Reward{
Active: true,
Denom: "",
CollateralType: "",
AvailableRewards: sdk.NewCoin("ukava", sdk.NewInt(1)),
Duration: time.Hour * 24 * 7,
TimeLock: time.Hour * 8766,
@ -196,7 +196,7 @@ func (suite *ParamTestSuite) SetupTest() {
},
errResult: errResult{
expectPass: false,
contains: "invalid denom",
contains: "collateral type cannot be blank",
},
},
}

View File

@ -7,31 +7,31 @@ import (
// Querier routes for the incentive module
const (
QueryGetClaims = "claims"
RestClaimOwner = "owner"
RestClaimDenom = "denom"
QueryGetParams = "parameters"
QueryGetRewardPeriods = "reward-periods"
QueryGetClaimPeriods = "claim-periods"
QueryGetClaims = "claims"
RestClaimOwner = "owner"
RestClaimCollateralType = "collateral_type"
QueryGetParams = "parameters"
QueryGetRewardPeriods = "reward-periods"
QueryGetClaimPeriods = "claim-periods"
)
// QueryClaimsParams params for query /incentive/claims
type QueryClaimsParams struct {
Owner sdk.AccAddress
Denom string
Owner sdk.AccAddress
CollateralType string
}
// NewQueryClaimsParams returns QueryClaimsParams
func NewQueryClaimsParams(owner sdk.AccAddress, denom string) QueryClaimsParams {
func NewQueryClaimsParams(owner sdk.AccAddress, collateralType string) QueryClaimsParams {
return QueryClaimsParams{
Owner: owner,
Denom: denom,
Owner: owner,
CollateralType: collateralType,
}
}
// PostClaimReq defines the properties of claim transaction's request body.
type PostClaimReq struct {
BaseReq rest.BaseReq `json:"base_req" yaml:"base_req"`
Sender sdk.AccAddress `json:"sender" yaml:"sender"`
Denom string `json:"denom" yaml:"denom"`
BaseReq rest.BaseReq `json:"base_req" yaml:"base_req"`
Sender sdk.AccAddress `json:"sender" yaml:"sender"`
CollateralType string `json:"collateral_type" yaml:"collateral_type"`
}

View File

@ -3,6 +3,7 @@ package types
import (
"errors"
"fmt"
"strings"
"time"
sdk "github.com/cosmos/cosmos-sdk/types"
@ -10,35 +11,35 @@ import (
// RewardPeriod stores the state of an ongoing reward
type RewardPeriod struct {
Denom string `json:"denom" yaml:"denom"`
Start time.Time `json:"start" yaml:"start"`
End time.Time `json:"end" yaml:"end"`
Reward sdk.Coin `json:"reward" yaml:"reward"` // per second reward payouts
ClaimEnd time.Time `json:"claim_end" yaml:"claim_end"`
ClaimTimeLock time.Duration `json:"claim_time_lock" yaml:"claim_time_lock"` // the amount of time rewards are timelocked once they are sent to users
CollateralType string `json:"collateral_type" yaml:"collateral_type"`
Start time.Time `json:"start" yaml:"start"`
End time.Time `json:"end" yaml:"end"`
Reward sdk.Coin `json:"reward" yaml:"reward"` // per second reward payouts
ClaimEnd time.Time `json:"claim_end" yaml:"claim_end"`
ClaimTimeLock time.Duration `json:"claim_time_lock" yaml:"claim_time_lock"` // the amount of time rewards are timelocked once they are sent to users
}
// String implements fmt.Stringer
func (rp RewardPeriod) String() string {
return fmt.Sprintf(`Reward Period:
Denom: %s,
Collateral Type: %s,
Start: %s,
End: %s,
Reward: %s,
Claim End: %s,
Claim Time Lock: %s
`, rp.Denom, rp.Start, rp.End, rp.Reward, rp.ClaimEnd, rp.ClaimTimeLock)
`, rp.CollateralType, rp.Start, rp.End, rp.Reward, rp.ClaimEnd, rp.ClaimTimeLock)
}
// NewRewardPeriod returns a new RewardPeriod
func NewRewardPeriod(denom string, start time.Time, end time.Time, reward sdk.Coin, claimEnd time.Time, claimTimeLock time.Duration) RewardPeriod {
func NewRewardPeriod(collateralType string, start time.Time, end time.Time, reward sdk.Coin, claimEnd time.Time, claimTimeLock time.Duration) RewardPeriod {
return RewardPeriod{
Denom: denom,
Start: start,
End: end,
Reward: reward,
ClaimEnd: claimEnd,
ClaimTimeLock: claimTimeLock,
CollateralType: collateralType,
Start: start,
End: end,
Reward: reward,
ClaimEnd: claimEnd,
ClaimTimeLock: claimTimeLock,
}
}
@ -62,7 +63,10 @@ func (rp RewardPeriod) Validate() error {
if rp.ClaimTimeLock == 0 {
return errors.New("reward claim time lock cannot be 0")
}
return sdk.ValidateDenom(rp.Denom)
if strings.TrimSpace(rp.CollateralType) == "" {
return fmt.Errorf("reward period collateral type cannot be blank: %s", rp)
}
return nil
}
// RewardPeriods array of RewardPeriod
@ -73,14 +77,14 @@ type RewardPeriods []RewardPeriod
func (rps RewardPeriods) Validate() error {
seenPeriods := make(map[string]bool)
for _, rp := range rps {
if seenPeriods[rp.Denom] {
return fmt.Errorf("duplicated reward period with denom %s", rp.Denom)
if seenPeriods[rp.CollateralType] {
return fmt.Errorf("duplicated reward period with collateral type %s", rp.CollateralType)
}
if err := rp.Validate(); err != nil {
return err
}
seenPeriods[rp.Denom] = true
seenPeriods[rp.CollateralType] = true
}
return nil
@ -88,19 +92,19 @@ func (rps RewardPeriods) Validate() error {
// ClaimPeriod stores the state of an ongoing claim period
type ClaimPeriod struct {
Denom string `json:"denom" yaml:"denom"`
ID uint64 `json:"id" yaml:"id"`
End time.Time `json:"end" yaml:"end"`
TimeLock time.Duration `json:"time_lock" yaml:"time_lock"`
CollateralType string `json:"collateral_type" yaml:"collateral_type"`
ID uint64 `json:"id" yaml:"id"`
End time.Time `json:"end" yaml:"end"`
TimeLock time.Duration `json:"time_lock" yaml:"time_lock"`
}
// NewClaimPeriod returns a new ClaimPeriod
func NewClaimPeriod(denom string, id uint64, end time.Time, timeLock time.Duration) ClaimPeriod {
func NewClaimPeriod(collateralType string, id uint64, end time.Time, timeLock time.Duration) ClaimPeriod {
return ClaimPeriod{
Denom: denom,
ID: id,
End: end,
TimeLock: timeLock,
CollateralType: collateralType,
ID: id,
End: end,
TimeLock: timeLock,
}
}
@ -115,17 +119,20 @@ func (cp ClaimPeriod) Validate() error {
if cp.TimeLock == 0 {
return errors.New("claim period time lock cannot be 0")
}
return sdk.ValidateDenom(cp.Denom)
if strings.TrimSpace(cp.CollateralType) == "" {
return fmt.Errorf("claim period collateral type cannot be blank: %s", cp)
}
return nil
}
// String implements fmt.Stringer
func (cp ClaimPeriod) String() string {
return fmt.Sprintf(`Claim Period:
Denom: %s,
Collateral Type: %s,
ID: %d,
End: %s,
Claim Time Lock: %s
`, cp.Denom, cp.ID, cp.End, cp.TimeLock)
`, cp.CollateralType, cp.ID, cp.End, cp.TimeLock)
}
// ClaimPeriods array of ClaimPeriod
@ -137,9 +144,9 @@ func (cps ClaimPeriods) Validate() error {
seenPeriods := make(map[string]bool)
var key string
for _, cp := range cps {
key = cp.Denom + string(cp.ID)
key = cp.CollateralType + string(cp.ID)
if seenPeriods[key] {
return fmt.Errorf("duplicated claim period with id %d and denom %s", cp.ID, cp.Denom)
return fmt.Errorf("duplicated claim period with id %d and collateral type %s", cp.ID, cp.CollateralType)
}
if err := cp.Validate(); err != nil {
@ -153,19 +160,19 @@ func (cps ClaimPeriods) Validate() error {
// Claim stores the rewards that can be claimed by owner
type Claim struct {
Owner sdk.AccAddress `json:"owner" yaml:"owner"`
Reward sdk.Coin `json:"reward" yaml:"reward"`
Denom string `json:"denom" yaml:"denom"`
ClaimPeriodID uint64 `json:"claim_period_id" yaml:"claim_period_id"`
Owner sdk.AccAddress `json:"owner" yaml:"owner"`
Reward sdk.Coin `json:"reward" yaml:"reward"`
CollateralType string `json:"collateral_type" yaml:"collateral_type"`
ClaimPeriodID uint64 `json:"claim_period_id" yaml:"claim_period_id"`
}
// NewClaim returns a new Claim
func NewClaim(owner sdk.AccAddress, reward sdk.Coin, denom string, claimPeriodID uint64) Claim {
func NewClaim(owner sdk.AccAddress, reward sdk.Coin, collateralType string, claimPeriodID uint64) Claim {
return Claim{
Owner: owner,
Reward: reward,
Denom: denom,
ClaimPeriodID: claimPeriodID,
Owner: owner,
Reward: reward,
CollateralType: collateralType,
ClaimPeriodID: claimPeriodID,
}
}
@ -180,17 +187,20 @@ func (c Claim) Validate() error {
if c.ClaimPeriodID == 0 {
return errors.New("claim period id cannot be 0")
}
return sdk.ValidateDenom(c.Denom)
if strings.TrimSpace(c.CollateralType) == "" {
return fmt.Errorf("claim collateral type cannot be blank: %s", c)
}
return nil
}
// String implements fmt.Stringer
func (c Claim) String() string {
return fmt.Sprintf(`Claim:
Owner: %s,
Denom: %s,
Collateral Type: %s,
Reward: %s,
Claim Period ID: %d,
`, c.Owner, c.Denom, c.Reward, c.ClaimPeriodID)
`, c.Owner, c.CollateralType, c.Reward, c.ClaimPeriodID)
}
// Claims array of Claim
@ -202,9 +212,9 @@ func (cs Claims) Validate() error {
seemClaims := make(map[string]bool)
var key string
for _, c := range cs {
key = c.Denom + string(c.ClaimPeriodID) + c.Owner.String()
key = c.CollateralType + string(c.ClaimPeriodID) + c.Owner.String()
if c.Owner != nil && seemClaims[key] {
return fmt.Errorf("duplicated claim from owner %s and denom %s", c.Owner, c.Denom)
return fmt.Errorf("duplicated claim from owner %s and collateral type %s", c.Owner, c.CollateralType)
}
if err := c.Validate(); err != nil {
@ -222,11 +232,11 @@ func NewRewardPeriodFromReward(reward Reward, blockTime time.Time) RewardPeriod
rewardsPerSecond := sdk.NewDecFromInt(reward.AvailableRewards.Amount).Quo(sdk.NewDecFromInt(sdk.NewInt(int64(reward.Duration.Seconds())))).TruncateInt()
rewardCoinPerSecond := sdk.NewCoin(reward.AvailableRewards.Denom, rewardsPerSecond)
return RewardPeriod{
Denom: reward.Denom,
Start: blockTime,
End: blockTime.Add(reward.Duration),
Reward: rewardCoinPerSecond,
ClaimEnd: blockTime.Add(reward.Duration).Add(reward.ClaimDuration),
ClaimTimeLock: reward.TimeLock,
CollateralType: reward.CollateralType,
Start: blockTime,
End: blockTime.Add(reward.Duration),
Reward: rewardCoinPerSecond,
ClaimEnd: blockTime.Add(reward.Duration).Add(reward.ClaimDuration),
ClaimTimeLock: reward.TimeLock,
}
}

View File

@ -93,15 +93,15 @@ func TestRewardPeriodsValidate(t *testing.T) {
false,
},
{
"invalid denom",
"invalid collateral type",
RewardPeriods{
{
Start: now,
End: now.Add(time.Hour),
Reward: sdk.NewCoin("bnb", sdk.OneInt()),
ClaimEnd: now,
ClaimTimeLock: 10,
Denom: "",
Start: now,
End: now.Add(time.Hour),
Reward: sdk.NewCoin("bnb", sdk.OneInt()),
ClaimEnd: now,
ClaimTimeLock: 10,
CollateralType: "",
},
},
false,
@ -170,9 +170,9 @@ func TestClaimPeriodsValidate(t *testing.T) {
false,
},
{
"invalid denom",
"invalid collateral type",
ClaimPeriods{
{ID: 10, End: now, TimeLock: 100, Denom: ""},
{ID: 10, End: now, TimeLock: 100, CollateralType: ""},
},
false,
},
@ -243,13 +243,13 @@ func TestClaimsValidate(t *testing.T) {
false,
},
{
"invalid denom",
"invalid collateral type",
Claims{
{
Owner: owner,
Reward: sdk.NewCoin("bnb", sdk.OneInt()),
ClaimPeriodID: 10,
Denom: "",
Owner: owner,
Reward: sdk.NewCoin("bnb", sdk.OneInt()),
ClaimPeriodID: 10,
CollateralType: "",
},
},
false,