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 [\#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 ```plaintext
/v0_3/node_info /v0_3/node_info
/v0_3/auth/accounts/<address> /v0_3/auth/accounts/<address>

View File

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

View File

@ -31,12 +31,12 @@ func BeginBlocker(ctx sdk.Context, req abci.RequestBeginBlock, k Keeper) {
continue continue
} }
err := k.UpdateFeesForAllCdps(ctx, cp.Denom) err := k.UpdateFeesForAllCdps(ctx, cp.Type)
if err != nil { if err != nil {
panic(err) 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) { if err != nil && !errors.Is(err, pricefeedtypes.ErrNoValidPrice) {
panic(err) panic(err)
} }

View File

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

View File

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

View File

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

View File

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

View File

@ -16,10 +16,10 @@ import (
func registerQueryRoutes(cliCtx context.CLIContext, r *mux.Router) { func registerQueryRoutes(cliCtx context.CLIContext, r *mux.Router) {
r.HandleFunc("/cdp/accounts", getAccountsHandlerFn(cliCtx)).Methods("GET") r.HandleFunc("/cdp/accounts", getAccountsHandlerFn(cliCtx)).Methods("GET")
r.HandleFunc("/cdp/parameters", getParamsHandlerFn(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/cdp/{%s}/{%s}", types.RestOwner, types.RestCollateralType), queryCdpHandlerFn(cliCtx)).Methods("GET")
r.HandleFunc(fmt.Sprintf("/cdp/cdps/denom/{%s}", types.RestCollateralDenom), queryCdpsHandlerFn(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.RestCollateralDenom, types.RestRatio), queryCdpsByRatioHandlerFn(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.RestCollateralDenom), queryCdpDepositsHandlerFn(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 { func queryCdpHandlerFn(cliCtx context.CLIContext) http.HandlerFunc {
@ -31,7 +31,7 @@ func queryCdpHandlerFn(cliCtx context.CLIContext) http.HandlerFunc {
vars := mux.Vars(r) vars := mux.Vars(r)
ownerBech32 := vars[types.RestOwner] ownerBech32 := vars[types.RestOwner]
collateralDenom := vars[types.RestCollateralDenom] collateralType := vars[types.RestCollateralType]
owner, err := sdk.AccAddressFromBech32(ownerBech32) owner, err := sdk.AccAddressFromBech32(ownerBech32)
if err != nil { if err != nil {
@ -39,7 +39,7 @@ func queryCdpHandlerFn(cliCtx context.CLIContext) http.HandlerFunc {
return return
} }
params := types.NewQueryCdpParams(owner, collateralDenom) params := types.NewQueryCdpParams(owner, collateralType)
bz, err := cliCtx.Codec.MarshalJSON(params) bz, err := cliCtx.Codec.MarshalJSON(params)
if err != nil { if err != nil {
@ -66,9 +66,9 @@ func queryCdpsHandlerFn(cliCtx context.CLIContext) http.HandlerFunc {
} }
vars := mux.Vars(r) 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) bz, err := cliCtx.Codec.MarshalJSON(params)
if err != nil { if err != nil {
@ -95,7 +95,7 @@ func queryCdpsByRatioHandlerFn(cliCtx context.CLIContext) http.HandlerFunc {
} }
vars := mux.Vars(r) vars := mux.Vars(r)
collateralDenom := vars[types.RestCollateralDenom] collateralType := vars[types.RestCollateralType]
ratioStr := vars[types.RestRatio] ratioStr := vars[types.RestRatio]
ratioDec, sdkError := sdk.NewDecFromStr(ratioStr) ratioDec, sdkError := sdk.NewDecFromStr(ratioStr)
@ -104,7 +104,7 @@ func queryCdpsByRatioHandlerFn(cliCtx context.CLIContext) http.HandlerFunc {
return return
} }
params := types.NewQueryCdpsByRatioParams(collateralDenom, ratioDec) params := types.NewQueryCdpsByRatioParams(collateralType, ratioDec)
bz, err := cliCtx.Codec.MarshalJSON(params) bz, err := cliCtx.Codec.MarshalJSON(params)
if err != nil { if err != nil {
rest.WriteErrorResponse(w, http.StatusBadRequest, fmt.Sprintf("failed to marshal query params: %s", err)) 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) vars := mux.Vars(r)
ownerBech32 := vars[types.RestOwner] ownerBech32 := vars[types.RestOwner]
collateralDenom := vars[types.RestCollateralDenom] collateralType := vars[types.RestCollateralType]
owner, err := sdk.AccAddressFromBech32(ownerBech32) owner, err := sdk.AccAddressFromBech32(ownerBech32)
if err != nil { if err != nil {
@ -139,7 +139,7 @@ func queryCdpDepositsHandlerFn(cliCtx context.CLIContext) http.HandlerFunc {
return return
} }
params := types.NewQueryCdpDeposits(owner, collateralDenom) params := types.NewQueryCdpDeposits(owner, collateralType)
bz, err := cliCtx.Codec.MarshalJSON(params) bz, err := cliCtx.Codec.MarshalJSON(params)
if err != nil { 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. // PostCdpReq defines the properties of cdp request's body.
type PostCdpReq struct { type PostCdpReq struct {
BaseReq rest.BaseReq `json:"base_req" yaml:"base_req"` BaseReq rest.BaseReq `json:"base_req" yaml:"base_req"`
Sender sdk.AccAddress `json:"sender" yaml:"sender"` Sender sdk.AccAddress `json:"sender" yaml:"sender"`
Collateral sdk.Coin `json:"collateral" yaml:"collateral"` Collateral sdk.Coin `json:"collateral" yaml:"collateral"`
Principal sdk.Coin `json:"principal" yaml:"principal"` CollateralType string `json:"collateral_type" yaml:"collateral_type"`
Principal sdk.Coin `json:"principal" yaml:"principal"`
} }
// PostDepositReq defines the properties of cdp request's body. // PostDepositReq defines the properties of cdp request's body.
type PostDepositReq struct { type PostDepositReq struct {
BaseReq rest.BaseReq `json:"base_req" yaml:"base_req"` BaseReq rest.BaseReq `json:"base_req" yaml:"base_req"`
Owner sdk.AccAddress `json:"owner" yaml:"owner"` Owner sdk.AccAddress `json:"owner" yaml:"owner"`
Depositor sdk.AccAddress `json:"depositor" yaml:"depositor"` Depositor sdk.AccAddress `json:"depositor" yaml:"depositor"`
Collateral sdk.Coin `json:"collateral" yaml:"collateral"` Collateral sdk.Coin `json:"collateral" yaml:"collateral"`
CollateralType string `json:"collateral_type" yaml:"collateral_type"`
} }
// PostWithdrawalReq defines the properties of cdp request's body. // PostWithdrawalReq defines the properties of cdp request's body.
type PostWithdrawalReq struct { type PostWithdrawalReq struct {
BaseReq rest.BaseReq `json:"base_req" yaml:"base_req"` BaseReq rest.BaseReq `json:"base_req" yaml:"base_req"`
Owner sdk.AccAddress `json:"owner" yaml:"owner"` Owner sdk.AccAddress `json:"owner" yaml:"owner"`
Depositor sdk.AccAddress `json:"depositor" yaml:"depositor"` Depositor sdk.AccAddress `json:"depositor" yaml:"depositor"`
Collateral sdk.Coin `json:"collateral" yaml:"collateral"` Collateral sdk.Coin `json:"collateral" yaml:"collateral"`
CollateralType string `json:"collateral_type" yaml:"collateral_type"`
} }
// PostDrawReq defines the properties of cdp request's body. // PostDrawReq defines the properties of cdp request's body.
type PostDrawReq struct { type PostDrawReq struct {
BaseReq rest.BaseReq `json:"base_req" yaml:"base_req"` BaseReq rest.BaseReq `json:"base_req" yaml:"base_req"`
Owner sdk.AccAddress `json:"owner" yaml:"owner"` Owner sdk.AccAddress `json:"owner" yaml:"owner"`
Denom string `json:"denom" yaml:"denom"` CollateralType string `json:"collateral_type" yaml:"collateral_type"`
Principal sdk.Coin `json:"principal" yaml:"principal"` Principal sdk.Coin `json:"principal" yaml:"principal"`
} }
// PostRepayReq defines the properties of cdp request's body. // PostRepayReq defines the properties of cdp request's body.
type PostRepayReq struct { type PostRepayReq struct {
BaseReq rest.BaseReq `json:"base_req" yaml:"base_req"` BaseReq rest.BaseReq `json:"base_req" yaml:"base_req"`
Owner sdk.AccAddress `json:"owner" yaml:"owner"` Owner sdk.AccAddress `json:"owner" yaml:"owner"`
Denom string `json:"denom" yaml:"denom"` CollateralType string `json:"collateral_type" yaml:"collateral_type"`
Payment sdk.Coin `json:"payment" yaml:"payment"` Payment sdk.Coin `json:"payment" yaml:"payment"`
} }

View File

@ -16,10 +16,10 @@ import (
func registerTxRoutes(cliCtx context.CLIContext, r *mux.Router) { func registerTxRoutes(cliCtx context.CLIContext, r *mux.Router) {
r.HandleFunc("/cdp", postCdpHandlerFn(cliCtx)).Methods("POST") r.HandleFunc("/cdp", postCdpHandlerFn(cliCtx)).Methods("POST")
r.HandleFunc("/cdp/{owner}/{denom}/deposits", postDepositHandlerFn(cliCtx)).Methods("POST") r.HandleFunc("/cdp/{owner}/{collateralType}/deposits", postDepositHandlerFn(cliCtx)).Methods("POST")
r.HandleFunc("/cdp/{owner}/{denom}/withdraw", postWithdrawHandlerFn(cliCtx)).Methods("POST") r.HandleFunc("/cdp/{owner}/{collateralType}/withdraw", postWithdrawHandlerFn(cliCtx)).Methods("POST")
r.HandleFunc("/cdp/{owner}/{denom}/draw", postDrawHandlerFn(cliCtx)).Methods("POST") r.HandleFunc("/cdp/{owner}/{collateralType}/draw", postDrawHandlerFn(cliCtx)).Methods("POST")
r.HandleFunc("/cdp/{owner}/{denom}/repay", postRepayHandlerFn(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.Sender,
requestBody.Collateral, requestBody.Collateral,
requestBody.Principal, requestBody.Principal,
requestBody.CollateralType,
) )
if err := msg.ValidateBasic(); err != nil { if err := msg.ValidateBasic(); err != nil {
rest.WriteErrorResponse(w, http.StatusBadRequest, err.Error()) rest.WriteErrorResponse(w, http.StatusBadRequest, err.Error())
@ -76,6 +77,7 @@ func postDepositHandlerFn(cliCtx context.CLIContext) http.HandlerFunc {
requestBody.Owner, requestBody.Owner,
requestBody.Depositor, requestBody.Depositor,
requestBody.Collateral, requestBody.Collateral,
requestBody.CollateralType,
) )
if err := msg.ValidateBasic(); err != nil { if err := msg.ValidateBasic(); err != nil {
rest.WriteErrorResponse(w, http.StatusBadRequest, err.Error()) rest.WriteErrorResponse(w, http.StatusBadRequest, err.Error())
@ -102,6 +104,7 @@ func postWithdrawHandlerFn(cliCtx context.CLIContext) http.HandlerFunc {
requestBody.Owner, requestBody.Owner,
requestBody.Depositor, requestBody.Depositor,
requestBody.Collateral, requestBody.Collateral,
requestBody.CollateralType,
) )
if err := msg.ValidateBasic(); err != nil { if err := msg.ValidateBasic(); err != nil {
rest.WriteErrorResponse(w, http.StatusBadRequest, err.Error()) rest.WriteErrorResponse(w, http.StatusBadRequest, err.Error())
@ -137,7 +140,7 @@ func postDrawHandlerFn(cliCtx context.CLIContext) http.HandlerFunc {
msg := types.NewMsgDrawDebt( msg := types.NewMsgDrawDebt(
requestBody.Owner, requestBody.Owner,
requestBody.Denom, requestBody.CollateralType,
requestBody.Principal, requestBody.Principal,
) )
if err := msg.ValidateBasic(); err != nil { if err := msg.ValidateBasic(); err != nil {
@ -174,7 +177,7 @@ func postRepayHandlerFn(cliCtx context.CLIContext) http.HandlerFunc {
msg := types.NewMsgRepayDebt( msg := types.NewMsgRepayDebt(
requestBody.Owner, requestBody.Owner,
requestBody.Denom, requestBody.CollateralType,
requestBody.Payment, requestBody.Payment,
) )
if err := msg.ValidateBasic(); err != nil { 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 // set the per second fee rate for each collateral type
for _, cp := range gs.Params.CollateralParams { 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 // 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)) panic(fmt.Sprintf("error setting cdp: %v", err))
} }
k.IndexCdpByOwner(ctx, cdp) k.IndexCdpByOwner(ctx, cdp)
ratio := k.CalculateCollateralToDebtRatio(ctx, cdp.Collateral, cdp.GetTotalPrincipal()) ratio := k.CalculateCollateralToDebtRatio(ctx, cdp.Collateral, cdp.Type, cdp.GetTotalPrincipal())
k.IndexCdpByCollateralRatio(ctx, cdp.Collateral.Denom, cdp.ID, ratio) k.IndexCdpByCollateralRatio(ctx, cdp.Type, cdp.ID, ratio)
k.IncrementTotalPrincipal(ctx, cdp.Collateral.Denom, cdp.GetTotalPrincipal()) k.IncrementTotalPrincipal(ctx, cdp.Type, cdp.GetTotalPrincipal())
} }
k.SetNextCdpID(ctx, gs.StartingCdpID) 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) { 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 { if err != nil {
return nil, err 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()), 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{ return &sdk.Result{
Data: GetCdpIDBytes(id), 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) { 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 { if err != nil {
return nil, err 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) { 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 { if err != nil {
return nil, err 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) { 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 { if err != nil {
return nil, err 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) { 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 { if err != nil {
return nil, err return nil, err
} }

View File

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

View File

@ -49,6 +49,7 @@ func NewCDPGenState(asset string, liquidationRatio sdk.Dec) app.GenesisState {
CollateralParams: cdp.CollateralParams{ CollateralParams: cdp.CollateralParams{
{ {
Denom: asset, Denom: asset,
Type: asset + "-a",
LiquidationRatio: liquidationRatio, LiquidationRatio: liquidationRatio,
DebtLimit: sdk.NewInt64Coin("usdx", 1000000000000), DebtLimit: sdk.NewInt64Coin("usdx", 1000000000000),
StabilityFee: sdk.MustNewDecFromStr("1.000000001547125958"), // %5 apr StabilityFee: sdk.MustNewDecFromStr("1.000000001547125958"), // %5 apr
@ -114,6 +115,7 @@ func NewCDPGenStateMulti() app.GenesisState {
CollateralParams: cdp.CollateralParams{ CollateralParams: cdp.CollateralParams{
{ {
Denom: "xrp", Denom: "xrp",
Type: "xrp-a",
LiquidationRatio: sdk.MustNewDecFromStr("2.0"), LiquidationRatio: sdk.MustNewDecFromStr("2.0"),
DebtLimit: sdk.NewInt64Coin("usdx", 500000000000), DebtLimit: sdk.NewInt64Coin("usdx", 500000000000),
StabilityFee: sdk.MustNewDecFromStr("1.000000001547125958"), // %5 apr StabilityFee: sdk.MustNewDecFromStr("1.000000001547125958"), // %5 apr
@ -126,6 +128,7 @@ func NewCDPGenStateMulti() app.GenesisState {
}, },
{ {
Denom: "btc", Denom: "btc",
Type: "btc-a",
LiquidationRatio: sdk.MustNewDecFromStr("1.5"), LiquidationRatio: sdk.MustNewDecFromStr("1.5"),
DebtLimit: sdk.NewInt64Coin("usdx", 500000000000), DebtLimit: sdk.NewInt64Coin("usdx", 500000000000),
StabilityFee: sdk.MustNewDecFromStr("1.000000000782997609"), // %2.5 apr StabilityFee: sdk.MustNewDecFromStr("1.000000000782997609"), // %2.5 apr
@ -156,10 +159,10 @@ func NewCDPGenStateMulti() app.GenesisState {
func cdps() (cdps cdp.CDPs) { func cdps() (cdps cdp.CDPs) {
_, addrs := app.GeneratePrivKeyAddressPairs(3) _, 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())) 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)), sdk.NewCoin("usdx", sdk.NewInt(10000000)), 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)), 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)), sdk.NewCoin("usdx", sdk.NewInt(50000000)), 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) cdps = append(cdps, c1, c2, c3, c4)
return return
} }

View File

@ -12,14 +12,14 @@ const (
) )
// AuctionCollateral creates auctions from the input deposits which attempt to raise the corresponding amount of debt // 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() totalCollateral := deposits.SumCollateral()
for _, deposit := range deposits { for _, deposit := range deposits {
debtCoveredByDeposit := (sdk.NewDecFromInt(deposit.Amount.Amount).Quo(sdk.NewDecFromInt(totalCollateral))).Mul(sdk.NewDecFromInt(debt)).RoundInt() 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 { if err != nil {
return err return err
} }
@ -29,7 +29,7 @@ func (k Keeper) AuctionCollateral(ctx sdk.Context, deposits types.Deposits, debt
// CreateAuctionsFromDeposit creates auctions from the input deposit // CreateAuctionsFromDeposit creates auctions from the input deposit
func (k Keeper) CreateAuctionsFromDeposit( 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 { principalDenom string) error {
// number of auctions of auctionSize // number of auctions of auctionSize
@ -69,7 +69,7 @@ func (k Keeper) CreateAuctionsFromDeposit(
unallocatedDebt = unallocatedDebt.Sub(sdk.OneInt()) unallocatedDebt = unallocatedDebt.Sub(sdk.OneInt())
} }
penalty := k.ApplyLiquidationPenalty(ctx, collateral.Denom, debtAmount) penalty := k.ApplyLiquidationPenalty(ctx, collateralType, debtAmount)
_, err := k.auctionKeeper.StartCollateralAuction( _, err := k.auctionKeeper.StartCollateralAuction(
ctx, types.LiquidatorMacc, sdk.NewCoin(collateral.Denom, auctionSize), ctx, types.LiquidatorMacc, sdk.NewCoin(collateral.Denom, auctionSize),
@ -95,7 +95,7 @@ func (k Keeper) CreateAuctionsFromDeposit(
unallocatedDebt = unallocatedDebt.Sub(sdk.OneInt()) unallocatedDebt = unallocatedDebt.Sub(sdk.OneInt())
} }
penalty := k.ApplyLiquidationPenalty(ctx, collateral.Denom, lastAuctionDebt) penalty := k.ApplyLiquidationPenalty(ctx, collateralType, lastAuctionDebt)
_, err := k.auctionKeeper.StartCollateralAuction( _, err := k.auctionKeeper.StartCollateralAuction(
ctx, types.LiquidatorMacc, sdk.NewCoin(collateral.Denom, lastAuctionCollateral), 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))) err := sk.MintCoins(suite.ctx, types.LiquidatorMacc, cs(c("debt", 21000000000), c("bnb", 190000000000)))
suite.Require().NoError(err) suite.Require().NoError(err)
testDeposit := types.NewDeposit(1, suite.addrs[0], c("bnb", 190000000000)) 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) suite.Require().NoError(err)
} }

View File

@ -1,7 +1,6 @@
package keeper package keeper
import ( import (
"errors"
"fmt" "fmt"
"sort" "sort"
@ -16,13 +15,13 @@ import (
const BaseDigitFactor = 1000000000000000000 const BaseDigitFactor = 1000000000000000000
// AddCdp adds a cdp for a specific owner and collateral type // 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 // validation
err := k.ValidateCollateral(ctx, collateral) err := k.ValidateCollateral(ctx, collateral, collateralType)
if err != nil { if err != nil {
return err return err
} }
_, found := k.GetCdpByOwnerAndDenom(ctx, owner, collateral.Denom) _, found := k.GetCdpByOwnerAndCollateralType(ctx, owner, collateralType)
if found { if found {
return sdkerrors.Wrapf(types.ErrCdpAlreadyExists, "owner %s, denom %s", owner, collateral.Denom) 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 return err
} }
err = k.ValidateDebtLimit(ctx, collateral.Denom, principal) err = k.ValidateDebtLimit(ctx, collateralType, principal)
if err != nil { if err != nil {
return err 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 { if err != nil {
return err return err
} }
// send coins from the owners account to the cdp module // send coins from the owners account to the cdp module
id := k.GetNextCdpID(ctx) 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) deposit := types.NewDeposit(cdp.ID, owner, collateral)
err = k.supplyKeeper.SendCoinsFromAccountToModule(ctx, owner, types.ModuleName, sdk.NewCoins(collateral)) err = k.supplyKeeper.SendCoinsFromAccountToModule(ctx, owner, types.ModuleName, sdk.NewCoins(collateral))
if err != nil { 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 // 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 // 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) err = k.SetCdpAndCollateralRatioIndex(ctx, cdp, collateralToDebtRatio)
if err != nil { if err != nil {
return err return err
@ -109,7 +108,7 @@ func (k Keeper) SetCdpAndCollateralRatioIndex(ctx sdk.Context, cdp types.CDP, ra
if err != nil { if err != nil {
return err return err
} }
k.IndexCdpByCollateralRatio(ctx, cdp.Collateral.Denom, cdp.ID, ratio) k.IndexCdpByCollateralRatio(ctx, cdp.Type, cdp.ID, ratio)
return nil 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 // 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) cdpIDs, found := k.GetCdpIdsByOwner(ctx, owner)
if !found { if !found {
return 0, false return 0, false
} }
for _, id := range cdpIDs { for _, id := range cdpIDs {
_, found = k.GetCDP(ctx, denom, id) _, found = k.GetCDP(ctx, collateralType, id)
if found { if found {
return id, true return id, true
} }
@ -154,14 +153,14 @@ func (k Keeper) GetCdpIdsByOwner(ctx sdk.Context, owner sdk.AccAddress) ([]uint6
return cdpIDs, true return cdpIDs, true
} }
// GetCdpByOwnerAndDenom queries cdps owned by owner and returns the cdp with matching denom // GetCdpByOwnerAndCollateralType 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) { func (k Keeper) GetCdpByOwnerAndCollateralType(ctx sdk.Context, owner sdk.AccAddress, collateralType string) (types.CDP, bool) {
cdpIDs, found := k.GetCdpIdsByOwner(ctx, owner) cdpIDs, found := k.GetCdpIdsByOwner(ctx, owner)
if !found { if !found {
return types.CDP{}, false return types.CDP{}, false
} }
for _, id := range cdpIDs { for _, id := range cdpIDs {
cdp, found := k.GetCDP(ctx, denom, id) cdp, found := k.GetCDP(ctx, collateralType, id)
if found { if found {
return cdp, true 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 // 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 // get store
store := prefix.NewStore(ctx.KVStore(k.key), types.CdpKeyPrefix) store := prefix.NewStore(ctx.KVStore(k.key), types.CdpKeyPrefix)
db, found := k.GetDenomPrefix(ctx, collateralDenom) db, found := k.GetCollateralTypePrefix(ctx, collateralType)
if !found { if !found {
return types.CDP{}, false 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 // SetCDP sets a cdp in the store
func (k Keeper) SetCDP(ctx sdk.Context, cdp types.CDP) error { func (k Keeper) SetCDP(ctx sdk.Context, cdp types.CDP) error {
store := prefix.NewStore(ctx.KVStore(k.key), types.CdpKeyPrefix) 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 { if !found {
return sdkerrors.Wrapf(types.ErrDenomPrefixNotFound, "%s", cdp.Collateral.Denom) 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 // DeleteCDP deletes a cdp from the store
func (k Keeper) DeleteCDP(ctx sdk.Context, cdp types.CDP) error { func (k Keeper) DeleteCDP(ctx sdk.Context, cdp types.CDP) error {
store := prefix.NewStore(ctx.KVStore(k.key), types.CdpKeyPrefix) 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 { if !found {
return sdkerrors.Wrapf(types.ErrDenomPrefixNotFound, "%s", cdp.Collateral.Denom) return sdkerrors.Wrapf(types.ErrDenomPrefixNotFound, "%s", cdp.Collateral.Denom)
} }
@ -221,18 +220,18 @@ func (k Keeper) GetAllCdps(ctx sdk.Context) (cdps types.CDPs) {
return return
} }
// GetAllCdpsByDenom returns all cdps of a particular collateral type from the store // GetAllCdpsByCollateralType returns all cdps of a particular collateral type from the store
func (k Keeper) GetAllCdpsByDenom(ctx sdk.Context, denom string) (cdps types.CDPs) { func (k Keeper) GetAllCdpsByCollateralType(ctx sdk.Context, collateralType string) (cdps types.CDPs) {
k.IterateCdpsByDenom(ctx, denom, func(cdp types.CDP) bool { k.IterateCdpsByCollateralType(ctx, collateralType, func(cdp types.CDP) bool {
cdps = append(cdps, cdp) cdps = append(cdps, cdp)
return false return false
}) })
return return
} }
// GetAllCdpsByDenomAndRatio returns all cdps of a particular collateral type and below a certain collateralization ratio // GetAllCdpsByCollateralTypeAndRatio 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) { func (k Keeper) GetAllCdpsByCollateralTypeAndRatio(ctx sdk.Context, collateralType string, targetRatio sdk.Dec) (cdps types.CDPs) {
k.IterateCdpsByCollateralRatio(ctx, denom, targetRatio, func(cdp types.CDP) bool { k.IterateCdpsByCollateralRatio(ctx, collateralType, targetRatio, func(cdp types.CDP) bool {
cdps = append(cdps, cdp) cdps = append(cdps, cdp)
return false 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 // 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) store := prefix.NewStore(ctx.KVStore(k.key), types.CollateralRatioIndexPrefix)
db, found := k.GetDenomPrefix(ctx, denom) db, found := k.GetCollateralTypePrefix(ctx, collateralType)
if !found { 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)) 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 // 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) store := prefix.NewStore(ctx.KVStore(k.key), types.CollateralRatioIndexPrefix)
db, found := k.GetDenomPrefix(ctx, denom) db, found := k.GetCollateralTypePrefix(ctx, collateralType)
if !found { 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)) 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 // ValidateCollateral validates that a collateral is valid for use in cdps
func (k Keeper) ValidateCollateral(ctx sdk.Context, collateral sdk.Coin) error { func (k Keeper) ValidateCollateral(ctx sdk.Context, collateral sdk.Coin, collateralType string) error {
cp, found := k.GetCollateral(ctx, collateral.Denom) cp, found := k.GetCollateral(ctx, collateralType)
if !found { if !found {
return sdkerrors.Wrap(types.ErrCollateralNotSupported, collateral.Denom) 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 // 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 { func (k Keeper) ValidateDebtLimit(ctx sdk.Context, collateralType string, principal sdk.Coin) error {
cp, found := k.GetCollateral(ctx, collateralDenom) cp, found := k.GetCollateral(ctx, collateralType)
if !found { 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 collateralLimit := cp.DebtLimit.Amount
if totalPrincipal.GT(collateralLimit) { 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))) 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 // 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 { 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, principal, fees, spot) collateralizationRatio, err := k.CalculateCollateralizationRatio(ctx, collateral, collateralType, principal, fees, spot)
if err != nil { if err != nil {
return err return err
} }
liquidationRatio := k.getLiquidationRatio(ctx, collateral.Denom) liquidationRatio := k.getLiquidationRatio(ctx, collateralType)
if collateralizationRatio.LT(liquidationRatio) { if collateralizationRatio.LT(liquidationRatio) {
return sdkerrors.Wrapf(types.ErrInvalidCollateralRatio, "collateral %s, collateral ratio %s, liquidation ratio %s", collateral.Denom, collateralizationRatio, 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 // 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) debtTotal := k.convertDebtToBaseUnits(ctx, debt)
if debtTotal.IsZero() || debtTotal.GTE(types.MaxSortableDec) { if debtTotal.IsZero() || debtTotal.GTE(types.MaxSortableDec) {
return types.MaxSortableDec.Sub(sdk.SmallestDec()) return types.MaxSortableDec.Sub(sdk.SmallestDec())
} }
collateralBaseUnits := k.convertCollateralToBaseUnits(ctx, collateral) collateralBaseUnits := k.convertCollateralToBaseUnits(ctx, collateral, collateralType)
return collateralBaseUnits.Quo(debtTotal) return collateralBaseUnits.Quo(debtTotal)
} }
// LoadAugmentedCDP creates a new augmented CDP from an existing CDP // LoadAugmentedCDP creates a new augmented CDP from an existing CDP
func (k Keeper) LoadAugmentedCDP(ctx sdk.Context, cdp types.CDP) types.AugmentedCDP { func (k Keeper) LoadAugmentedCDP(ctx sdk.Context, cdp types.CDP) types.AugmentedCDP {
// calculate collateralization ratio // 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 { if err != nil {
return types.AugmentedCDP{CDP: cdp} 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 // 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() { if collateral.IsZero() {
return sdk.ZeroDec(), nil return sdk.ZeroDec(), nil
} }
var marketID string var marketID string
switch pfType { switch pfType {
case spot: case spot:
marketID = k.getSpotMarketID(ctx, collateral.Denom) marketID = k.getSpotMarketID(ctx, collateralType)
case liquidation: case liquidation:
marketID = k.getliquidationMarketID(ctx, collateral.Denom) marketID = k.getliquidationMarketID(ctx, collateralType)
default: default:
return sdk.Dec{}, pfType.IsValid() return sdk.Dec{}, pfType.IsValid()
} }
@ -464,7 +463,7 @@ func (k Keeper) CalculateCollateralizationRatio(ctx sdk.Context, collateral sdk.
if err != nil { if err != nil {
return sdk.Dec{}, err return sdk.Dec{}, err
} }
collateralBaseUnits := k.convertCollateralToBaseUnits(ctx, collateral) collateralBaseUnits := k.convertCollateralToBaseUnits(ctx, collateral, collateralType)
collateralValue := collateralBaseUnits.Mul(price.Price) collateralValue := collateralBaseUnits.Mul(price.Price)
prinicpalBaseUnits := k.convertDebtToBaseUnits(ctx, principal) 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 // 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 // get price of collateral
var marketID string var marketID string
switch pfType { switch pfType {
case spot: case spot:
marketID = k.getSpotMarketID(ctx, collateralDenom) marketID = k.getSpotMarketID(ctx, collateralType)
case liquidation: case liquidation:
marketID = k.getliquidationMarketID(ctx, collateralDenom) marketID = k.getliquidationMarketID(ctx, collateralType)
default: default:
return sdk.Dec{}, pfType.IsValid() 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)) // 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) { func (k Keeper) convertCollateralToBaseUnits(ctx sdk.Context, collateral sdk.Coin, collateralType string) (baseUnits sdk.Dec) {
cp, _ := k.GetCollateral(ctx, collateral.Denom) cp, _ := k.GetCollateral(ctx, collateralType)
return sdk.NewDecFromInt(collateral.Amount).Mul(sdk.NewDecFromIntWithPrec(sdk.OneInt(), cp.ConversionFactor.Int64())) 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: case spot, liquidation:
return nil 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 := ak.NewAccountWithAddress(suite.ctx, addrs[0])
acc.SetCoins(cs(c("xrp", 200000000), c("btc", 500000000))) acc.SetCoins(cs(c("xrp", 200000000), c("btc", 500000000)))
ak.SetAccount(suite.ctx, acc) 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)) 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 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)) suite.Require().True(errors.Is(err, types.ErrDebtNotSupported))
acc2 := ak.NewAccountWithAddress(suite.ctx, addrs[1]) acc2 := ak.NewAccountWithAddress(suite.ctx, addrs[1])
acc2.SetCoins(cs(c("btc", 500000000000))) acc2.SetCoins(cs(c("btc", 500000000000)))
ak.SetAccount(suite.ctx, acc2) 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)) suite.Require().True(errors.Is(err, types.ErrExceedsDebtLimit))
ctx := suite.ctx.WithBlockTime(suite.ctx.BlockTime().Add(time.Hour * 2)) ctx := suite.ctx.WithBlockTime(suite.ctx.BlockTime().Add(time.Hour * 2))
@ -66,18 +66,18 @@ func (suite *CdpTestSuite) TestAddCdp() {
suite.Error(err) suite.Error(err)
ok := suite.keeper.UpdatePricefeedStatus(ctx, "xrp:usd") ok := suite.keeper.UpdatePricefeedStatus(ctx, "xrp:usd")
suite.False(ok) 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)) suite.Require().True(errors.Is(err, types.ErrPricefeedDown))
err = pk.SetCurrentPrices(suite.ctx, "xrp:usd") err = pk.SetCurrentPrices(suite.ctx, "xrp:usd")
ok = suite.keeper.UpdatePricefeedStatus(suite.ctx, "xrp:usd") ok = suite.keeper.UpdatePricefeedStatus(suite.ctx, "xrp:usd")
suite.True(ok) suite.True(ok)
suite.NoError(err) 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) suite.NoError(err)
id := suite.keeper.GetNextCdpID(suite.ctx) id := suite.keeper.GetNextCdpID(suite.ctx)
suite.Equal(uint64(2), id) 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) suite.Equal(i(10000000), tp)
sk := suite.app.GetSupplyKeeper() sk := suite.app.GetSupplyKeeper()
macc := sk.GetModuleAccount(suite.ctx, types.ModuleName) macc := sk.GetModuleAccount(suite.ctx, types.ModuleName)
@ -85,30 +85,30 @@ func (suite *CdpTestSuite) TestAddCdp() {
acc = ak.GetAccount(suite.ctx, addrs[0]) acc = ak.GetAccount(suite.ctx, addrs[0])
suite.Equal(cs(c("usdx", 10000000), c("xrp", 100000000), c("btc", 500000000)), acc.GetCoins()) 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)) 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) suite.NoError(err)
id = suite.keeper.GetNextCdpID(suite.ctx) id = suite.keeper.GetNextCdpID(suite.ctx)
suite.Equal(uint64(3), id) 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) suite.Equal(i(100000000), tp)
macc = sk.GetModuleAccount(suite.ctx, types.ModuleName) macc = sk.GetModuleAccount(suite.ctx, types.ModuleName)
suite.Equal(cs(c("debt", 110000000), c("xrp", 100000000), c("btc", 500000000)), macc.GetCoins()) suite.Equal(cs(c("debt", 110000000), c("xrp", 100000000), c("btc", 500000000)), macc.GetCoins())
acc = ak.GetAccount(suite.ctx, addrs[0]) acc = ak.GetAccount(suite.ctx, addrs[0])
suite.Equal(cs(c("usdx", 110000000), c("xrp", 100000000)), acc.GetCoins()) 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)) 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)) suite.Require().True(errors.Is(err, types.ErrCdpAlreadyExists))
} }
func (suite *CdpTestSuite) TestGetSetDenomByte() { func (suite *CdpTestSuite) TestGetSetCollateralTypeByte() {
_, found := suite.keeper.GetDenomPrefix(suite.ctx, "lol") _, found := suite.keeper.GetCollateralTypePrefix(suite.ctx, "lol-a")
suite.False(found) suite.False(found)
db, found := suite.keeper.GetDenomPrefix(suite.ctx, "xrp") db, found := suite.keeper.GetCollateralTypePrefix(suite.ctx, "xrp-a")
suite.True(found) suite.True(found)
suite.Equal(byte(0x20), db) suite.Equal(byte(0x20), db)
} }
@ -129,66 +129,66 @@ func (suite *CdpTestSuite) TestGetNextCdpID() {
func (suite *CdpTestSuite) TestGetSetCdp() { func (suite *CdpTestSuite) TestGetSetCdp() {
_, addrs := app.GeneratePrivKeyAddressPairs(1) _, 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) err := suite.keeper.SetCDP(suite.ctx, cdp)
suite.NoError(err) 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.True(found)
suite.Equal(cdp, t) 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.False(found)
suite.keeper.DeleteCDP(suite.ctx, cdp) 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) suite.False(found)
} }
func (suite *CdpTestSuite) TestGetSetCdpId() { func (suite *CdpTestSuite) TestGetSetCdpId() {
_, addrs := app.GeneratePrivKeyAddressPairs(2) _, 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) err := suite.keeper.SetCDP(suite.ctx, cdp)
suite.NoError(err) suite.NoError(err)
suite.keeper.IndexCdpByOwner(suite.ctx, cdp) 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.True(found)
suite.Equal(types.DefaultCdpStartingID, id) 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) 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) suite.False(found)
} }
func (suite *CdpTestSuite) TestGetSetCdpByOwnerAndDenom() { func (suite *CdpTestSuite) TestGetSetCdpByOwnerAndCollateralType() {
_, addrs := app.GeneratePrivKeyAddressPairs(2) _, 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) err := suite.keeper.SetCDP(suite.ctx, cdp)
suite.NoError(err) suite.NoError(err)
suite.keeper.IndexCdpByOwner(suite.ctx, cdp) 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.True(found)
suite.Equal(cdp, t) 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) 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.False(found)
suite.NotPanics(func() { suite.keeper.IndexCdpByOwner(suite.ctx, cdp) }) suite.NotPanics(func() { suite.keeper.IndexCdpByOwner(suite.ctx, cdp) })
} }
func (suite *CdpTestSuite) TestCalculateCollateralToDebtRatio() { func (suite *CdpTestSuite) TestCalculateCollateralToDebtRatio() {
_, addrs := app.GeneratePrivKeyAddressPairs(1) _, addrs := app.GeneratePrivKeyAddressPairs(1)
cdp := types.NewCDP(types.DefaultCdpStartingID, addrs[0], c("xrp", 3), c("usdx", 1), tmtime.Canonical(time.Now())) 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.Principal) cr := suite.keeper.CalculateCollateralToDebtRatio(suite.ctx, cdp.Collateral, cdp.Type, cdp.Principal)
suite.Equal(sdk.MustNewDecFromStr("3.0"), cr) suite.Equal(sdk.MustNewDecFromStr("3.0"), cr)
cdp = types.NewCDP(types.DefaultCdpStartingID, addrs[0], c("xrp", 1), c("usdx", 2), tmtime.Canonical(time.Now())) 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.Principal) cr = suite.keeper.CalculateCollateralToDebtRatio(suite.ctx, cdp.Collateral, cdp.Type, cdp.Principal)
suite.Equal(sdk.MustNewDecFromStr("0.5"), cr) suite.Equal(sdk.MustNewDecFromStr("0.5"), cr)
} }
func (suite *CdpTestSuite) TestSetCdpByCollateralRatio() { func (suite *CdpTestSuite) TestSetCdpByCollateralRatio() {
_, addrs := app.GeneratePrivKeyAddressPairs(1) _, addrs := app.GeneratePrivKeyAddressPairs(1)
cdp := types.NewCDP(types.DefaultCdpStartingID, addrs[0], c("xrp", 3), c("usdx", 1), tmtime.Canonical(time.Now())) 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.Principal) cr := suite.keeper.CalculateCollateralToDebtRatio(suite.ctx, cdp.Collateral, cdp.Type, cdp.Principal)
suite.NotPanics(func() { suite.keeper.IndexCdpByCollateralRatio(suite.ctx, cdp.Collateral.Denom, cdp.ID, cr) }) suite.NotPanics(func() { suite.keeper.IndexCdpByCollateralRatio(suite.ctx, cdp.Type, cdp.ID, cr) })
} }
func (suite *CdpTestSuite) TestIterateCdps() { func (suite *CdpTestSuite) TestIterateCdps() {
@ -197,29 +197,29 @@ func (suite *CdpTestSuite) TestIterateCdps() {
err := suite.keeper.SetCDP(suite.ctx, c) err := suite.keeper.SetCDP(suite.ctx, c)
suite.NoError(err) suite.NoError(err)
suite.keeper.IndexCdpByOwner(suite.ctx, c) suite.keeper.IndexCdpByOwner(suite.ctx, c)
cr := suite.keeper.CalculateCollateralToDebtRatio(suite.ctx, c.Collateral, c.Principal) cr := suite.keeper.CalculateCollateralToDebtRatio(suite.ctx, c.Collateral, c.Type, c.Principal)
suite.keeper.IndexCdpByCollateralRatio(suite.ctx, c.Collateral.Denom, c.ID, cr) suite.keeper.IndexCdpByCollateralRatio(suite.ctx, c.Type, c.ID, cr)
} }
t := suite.keeper.GetAllCdps(suite.ctx) t := suite.keeper.GetAllCdps(suite.ctx)
suite.Equal(4, len(t)) suite.Equal(4, len(t))
} }
func (suite *CdpTestSuite) TestIterateCdpsByDenom() { func (suite *CdpTestSuite) TestIterateCdpsByCollateralType() {
cdps := cdps() cdps := cdps()
for _, c := range cdps { for _, c := range cdps {
err := suite.keeper.SetCDP(suite.ctx, c) err := suite.keeper.SetCDP(suite.ctx, c)
suite.NoError(err) suite.NoError(err)
suite.keeper.IndexCdpByOwner(suite.ctx, c) suite.keeper.IndexCdpByOwner(suite.ctx, c)
cr := suite.keeper.CalculateCollateralToDebtRatio(suite.ctx, c.Collateral, c.Principal) cr := suite.keeper.CalculateCollateralToDebtRatio(suite.ctx, c.Collateral, c.Type, c.Principal)
suite.keeper.IndexCdpByCollateralRatio(suite.ctx, c.Collateral.Denom, c.ID, cr) 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)) 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.Equal(1, len(btcCdps))
suite.keeper.DeleteCDP(suite.ctx, cdps[0]) suite.keeper.DeleteCDP(suite.ctx, cdps[0])
suite.keeper.RemoveCdpOwnerIndex(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.Equal(2, len(xrpCdps))
suite.keeper.DeleteCDP(suite.ctx, cdps[1]) suite.keeper.DeleteCDP(suite.ctx, cdps[1])
suite.keeper.RemoveCdpOwnerIndex(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) err := suite.keeper.SetCDP(suite.ctx, c)
suite.NoError(err) suite.NoError(err)
suite.keeper.IndexCdpByOwner(suite.ctx, c) suite.keeper.IndexCdpByOwner(suite.ctx, c)
cr := suite.keeper.CalculateCollateralToDebtRatio(suite.ctx, c.Collateral, c.Principal) cr := suite.keeper.CalculateCollateralToDebtRatio(suite.ctx, c.Collateral, c.Type, c.Principal)
suite.keeper.IndexCdpByCollateralRatio(suite.ctx, c.Collateral.Denom, c.ID, cr) 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)) 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)) 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)) 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.Equal(3, len(xrpCdps))
suite.keeper.DeleteCDP(suite.ctx, cdps[0]) suite.keeper.DeleteCDP(suite.ctx, cdps[0])
suite.keeper.RemoveCdpOwnerIndex(suite.ctx, cdps[0]) suite.keeper.RemoveCdpOwnerIndex(suite.ctx, cdps[0])
cr := suite.keeper.CalculateCollateralToDebtRatio(suite.ctx, cdps[0].Collateral, cdps[0].Principal) cr := suite.keeper.CalculateCollateralToDebtRatio(suite.ctx, cdps[0].Collateral, cdps[0].Type, cdps[0].Principal)
suite.keeper.RemoveCdpCollateralRatioIndex(suite.ctx, cdps[0].Collateral.Denom, cdps[0].ID, cr) suite.keeper.RemoveCdpCollateralRatioIndex(suite.ctx, cdps[0].Type, cdps[0].ID, cr)
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(1, len(xrpCdps)) suite.Equal(1, len(xrpCdps))
} }
func (suite *CdpTestSuite) TestValidateCollateral() { func (suite *CdpTestSuite) TestValidateCollateral() {
c := sdk.NewCoin("xrp", sdk.NewInt(1)) 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) suite.NoError(err)
c = sdk.NewCoin("lol", sdk.NewInt(1)) 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)) suite.Require().True(errors.Is(err, types.ErrCollateralNotSupported))
} }
@ -271,10 +271,10 @@ func (suite *CdpTestSuite) TestValidatePrincipal() {
err = suite.keeper.ValidatePrincipalAdd(suite.ctx, d) err = suite.keeper.ValidatePrincipalAdd(suite.ctx, d)
suite.Require().True(errors.Is(err, types.ErrDebtNotSupported)) suite.Require().True(errors.Is(err, types.ErrDebtNotSupported))
d = sdk.NewCoin("usdx", sdk.NewInt(1000000000001)) 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)) suite.Require().True(errors.Is(err, types.ErrExceedsDebtLimit))
d = sdk.NewCoin("usdx", sdk.NewInt(100000000)) 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) suite.NoError(err)
} }
@ -283,13 +283,13 @@ func (suite *CdpTestSuite) TestCalculateCollateralizationRatio() {
err := suite.keeper.SetCDP(suite.ctx, c) err := suite.keeper.SetCDP(suite.ctx, c)
suite.NoError(err) suite.NoError(err)
suite.keeper.IndexCdpByOwner(suite.ctx, c) suite.keeper.IndexCdpByOwner(suite.ctx, c)
cr := suite.keeper.CalculateCollateralToDebtRatio(suite.ctx, c.Collateral, c.Principal) cr := suite.keeper.CalculateCollateralToDebtRatio(suite.ctx, c.Collateral, c.Type, c.Principal)
suite.keeper.IndexCdpByCollateralRatio(suite.ctx, c.Collateral.Denom, c.ID, cr) suite.keeper.IndexCdpByCollateralRatio(suite.ctx, c.Type, c.ID, cr)
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.NoError(err)
suite.Equal(d("2.5"), cr) suite.Equal(d("2.5"), cr)
c.AccumulatedFees = sdk.NewCoin("usdx", i(10000000)) 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.NoError(err)
suite.Equal(d("1.25"), cr) suite.Equal(d("1.25"), cr)
} }

View File

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

View File

@ -44,7 +44,7 @@ func (suite *DepositTestSuite) SetupTest() {
suite.keeper = keeper suite.keeper = keeper
suite.ctx = ctx suite.ctx = ctx
suite.addrs = addrs 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) suite.NoError(err)
} }
@ -64,7 +64,7 @@ func (suite *DepositTestSuite) TestGetSetDeposit() {
} }
func (suite *DepositTestSuite) TestDepositCollateral() { 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) suite.NoError(err)
d, found := suite.keeper.GetDeposit(suite.ctx, uint64(1), suite.addrs[0]) d, found := suite.keeper.GetDeposit(suite.ctx, uint64(1), suite.addrs[0])
suite.True(found) suite.True(found)
@ -73,19 +73,19 @@ func (suite *DepositTestSuite) TestDepositCollateral() {
ds := suite.keeper.GetDeposits(suite.ctx, uint64(1)) ds := suite.keeper.GetDeposits(suite.ctx, uint64(1))
suite.Equal(1, len(ds)) suite.Equal(1, len(ds))
suite.True(ds[0].Equals(td)) 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) suite.Equal(c("xrp", 410000000), cd.Collateral)
ak := suite.app.GetAccountKeeper() ak := suite.app.GetAccountKeeper()
acc := ak.GetAccount(suite.ctx, suite.addrs[0]) acc := ak.GetAccount(suite.ctx, suite.addrs[0])
suite.Equal(i(90000000), acc.GetCoins().AmountOf("xrp")) 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)) 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)) 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) suite.NoError(err)
d, found = suite.keeper.GetDeposit(suite.ctx, uint64(1), suite.addrs[1]) d, found = suite.keeper.GetDeposit(suite.ctx, uint64(1), suite.addrs[1])
suite.True(found) suite.True(found)
@ -97,21 +97,21 @@ func (suite *DepositTestSuite) TestDepositCollateral() {
} }
func (suite *DepositTestSuite) TestWithdrawCollateral() { 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)) 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)) 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)) 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) cd.AccumulatedFees = c("usdx", 1)
err = suite.keeper.SetCDP(suite.ctx, cd) err = suite.keeper.SetCDP(suite.ctx, cd)
suite.NoError(err) 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)) 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) suite.NoError(err)
dep, _ := suite.keeper.GetDeposit(suite.ctx, uint64(1), suite.addrs[0]) dep, _ := suite.keeper.GetDeposit(suite.ctx, uint64(1), suite.addrs[0])
td := types.NewDeposit(uint64(1), suite.addrs[0], c("xrp", 390000000)) 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]) acc := ak.GetAccount(suite.ctx, suite.addrs[0])
suite.Equal(i(110000000), acc.GetCoins().AmountOf("xrp")) 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)) 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 // 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 // validation
cdp, found := k.GetCdpByOwnerAndDenom(ctx, owner, denom) cdp, found := k.GetCdpByOwnerAndCollateralType(ctx, owner, collateralType)
if !found { 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) err := k.ValidatePrincipalDraw(ctx, principal, cdp.Principal.Denom)
if err != nil { if err != nil {
return err return err
} }
err = k.ValidateDebtLimit(ctx, cdp.Collateral.Denom, principal) err = k.ValidateDebtLimit(ctx, cdp.Type, principal)
if err != nil { if err != nil {
return err 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 { if err != nil {
return err return err
} }
@ -57,27 +57,27 @@ func (k Keeper) AddPrincipal(ctx sdk.Context, owner sdk.AccAddress, denom string
) )
// remove old collateral:debt index // remove old collateral:debt index
oldCollateralToDebtRatio := k.CalculateCollateralToDebtRatio(ctx, cdp.Collateral, cdp.GetTotalPrincipal()) oldCollateralToDebtRatio := k.CalculateCollateralToDebtRatio(ctx, cdp.Collateral, cdp.Type, cdp.GetTotalPrincipal())
k.RemoveCdpCollateralRatioIndex(ctx, denom, cdp.ID, oldCollateralToDebtRatio) k.RemoveCdpCollateralRatioIndex(ctx, cdp.Type, cdp.ID, oldCollateralToDebtRatio)
// update cdp state // update cdp state
cdp.Principal = cdp.Principal.Add(principal) cdp.Principal = cdp.Principal.Add(principal)
// increment total principal for the input collateral type // 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 // 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) return k.SetCdpAndCollateralRatioIndex(ctx, cdp, collateralToDebtRatio)
} }
// RepayPrincipal removes debt from the cdp // 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 // 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 // validation
cdp, found := k.GetCdpByOwnerAndDenom(ctx, owner, denom) cdp, found := k.GetCdpByOwnerAndCollateralType(ctx, owner, collateralType)
if !found { 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) 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 // remove the old collateral:debt ratio index
oldCollateralToDebtRatio := k.CalculateCollateralToDebtRatio(ctx, cdp.Collateral, totalPrincipal) oldCollateralToDebtRatio := k.CalculateCollateralToDebtRatio(ctx, cdp.Collateral, cdp.Type, totalPrincipal)
k.RemoveCdpCollateralRatioIndex(ctx, denom, cdp.ID, oldCollateralToDebtRatio) k.RemoveCdpCollateralRatioIndex(ctx, cdp.Type, cdp.ID, oldCollateralToDebtRatio)
// update cdp state // update cdp state
if !principalPayment.IsZero() { 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) cdp.AccumulatedFees = cdp.AccumulatedFees.Sub(feePayment)
// decrement the total principal for the input collateral type // 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, // if the debt is fully paid, return collateral to depositors,
// and remove the cdp and indexes from the store // 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 // 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) return k.SetCdpAndCollateralRatioIndex(ctx, cdp, collateralToDebtRatio)
} }

View File

@ -46,72 +46,72 @@ func (suite *DrawTestSuite) SetupTest() {
suite.keeper = keeper suite.keeper = keeper
suite.ctx = ctx suite.ctx = ctx
suite.addrs = addrs 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) suite.NoError(err)
} }
func (suite *DrawTestSuite) TestAddRepayPrincipal() { 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) 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.True(found)
suite.Equal(c("usdx", 20000000), t.Principal) 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) 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)) 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) 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) suite.Equal(i(20000000), tp)
sk := suite.app.GetSupplyKeeper() sk := suite.app.GetSupplyKeeper()
acc := sk.GetModuleAccount(suite.ctx, types.ModuleName) acc := sk.GetModuleAccount(suite.ctx, types.ModuleName)
suite.Equal(cs(c("xrp", 400000000), c("debt", 20000000)), acc.GetCoins()) 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)) 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)) 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)) 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)) 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) 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.True(found)
suite.Equal(c("usdx", 10000000), t.Principal) 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) 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)) 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) suite.Equal(ts[0], t)
sk = suite.app.GetSupplyKeeper() sk = suite.app.GetSupplyKeeper()
acc = sk.GetModuleAccount(suite.ctx, types.ModuleName) acc = sk.GetModuleAccount(suite.ctx, types.ModuleName)
suite.Equal(cs(c("xrp", 400000000), c("debt", 10000000)), acc.GetCoins()) 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)) 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)) 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)) 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) 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) 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)) 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)) suite.Equal(0, len(ts))
sk = suite.app.GetSupplyKeeper() sk = suite.app.GetSupplyKeeper()
acc = sk.GetModuleAccount(suite.ctx, types.ModuleName) acc = sk.GetModuleAccount(suite.ctx, types.ModuleName)
@ -120,43 +120,43 @@ func (suite *DrawTestSuite) TestAddRepayPrincipal() {
} }
func (suite *DrawTestSuite) TestRepayPrincipalOverpay() { 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) suite.NoError(err)
ak := suite.app.GetAccountKeeper() ak := suite.app.GetAccountKeeper()
acc := ak.GetAccount(suite.ctx, suite.addrs[0]) acc := ak.GetAccount(suite.ctx, suite.addrs[0])
suite.Equal(i(10000000000), (acc.GetCoins().AmountOf("usdx"))) 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) suite.False(found)
} }
func (suite *DrawTestSuite) TestAddRepayPrincipalFees() { 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.NoError(err)
suite.ctx = suite.ctx.WithBlockTime(suite.ctx.BlockTime().Add(time.Minute * 10)) 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) 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) 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) 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) 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) 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) 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) 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.NoError(err)
suite.ctx = suite.ctx.WithBlockTime(suite.ctx.BlockTime().Add(time.Second * 31536000)) // move forward one year in time 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) 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) 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) 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)) ctx := suite.ctx.WithBlockTime(suite.ctx.BlockTime().Add(time.Hour * 2))
pfk := suite.app.GetPriceFeedKeeper() pfk := suite.app.GetPriceFeedKeeper()
pfk.SetCurrentPrices(ctx, "xrp:usd") 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) 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) suite.NoError(err)
} }
@ -177,7 +177,7 @@ func (suite *DrawTestSuite) TestModuleAccountFailure() {
acc := sk.GetModuleAccount(ctx, types.ModuleName) acc := sk.GetModuleAccount(ctx, types.ModuleName)
ak := suite.app.GetAccountKeeper() ak := suite.app.GetAccountKeeper()
ak.RemoveAccount(ctx, acc) 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 package keeper
import ( import (
"fmt"
"github.com/cosmos/cosmos-sdk/store/prefix" "github.com/cosmos/cosmos-sdk/store/prefix"
sdk "github.com/cosmos/cosmos-sdk/types" sdk "github.com/cosmos/cosmos-sdk/types"
@ -9,11 +11,11 @@ import (
// CalculateFees returns the fees accumulated since fees were last calculated based on // 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 // 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: // how fees are calculated:
// feesAccumulated = (outstandingDebt * (feeRate^periods)) - outstandingDebt // 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 // 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) scalar := sdk.NewInt(1000000000000000000)
feeRateInt := feePerSecond.Mul(sdk.NewDecFromInt(scalar)).TruncateInt() feeRateInt := feePerSecond.Mul(sdk.NewDecFromInt(scalar)).TruncateInt()
accumulator := sdk.NewDecFromInt(types.RelativePow(feeRateInt, periods, scalar)).Mul(sdk.SmallestDec()) 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 // 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 var iterationErr error
k.IterateCdpsByDenom(ctx, collateralDenom, func(cdp types.CDP) bool { k.IterateCdpsByCollateralType(ctx, collateralType, func(cdp types.CDP) bool {
oldCollateralToDebtRatio := k.CalculateCollateralToDebtRatio(ctx, cdp.Collateral, cdp.GetTotalPrincipal()) oldCollateralToDebtRatio := k.CalculateCollateralToDebtRatio(ctx, cdp.Collateral, cdp.Type, cdp.GetTotalPrincipal())
// periods = bblock timestamp - fees updated // periods = bblock timestamp - fees updated
periods := sdk.NewInt(ctx.BlockTime().Unix()).Sub(sdk.NewInt(cdp.FeesUpdated.Unix())) 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 // exit without updating fees if amount has rounded down to zero
// cdp will get updated next block when newFees, newFeesSavings, newFeesSurplus >0 // 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 return true
} }
previousDebt := k.GetTotalPrincipal(ctx, collateralDenom, dp.Denom) previousDebt := k.GetTotalPrincipal(ctx, cdp.Type, dp.Denom)
newDebt := previousDebt.Add(newFees.Amount) 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. // 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))) 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 // and set the fees updated time to the current block time since we just updated it
cdp.FeesUpdated = ctx.BlockTime() cdp.FeesUpdated = ctx.BlockTime()
collateralToDebtRatio := k.CalculateCollateralToDebtRatio(ctx, cdp.Collateral, cdp.GetTotalPrincipal()) collateralToDebtRatio := k.CalculateCollateralToDebtRatio(ctx, cdp.Collateral, cdp.Type, cdp.GetTotalPrincipal())
k.RemoveCdpCollateralRatioIndex(ctx, cdp.Collateral.Denom, cdp.ID, oldCollateralToDebtRatio) k.RemoveCdpCollateralRatioIndex(ctx, cdp.Type, cdp.ID, oldCollateralToDebtRatio)
err = k.SetCdpAndCollateralRatioIndex(ctx, cdp, collateralToDebtRatio) err = k.SetCdpAndCollateralRatioIndex(ctx, cdp, collateralToDebtRatio)
if err != nil { if err != nil {
iterationErr = err 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 // 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) { func (k Keeper) IncrementTotalPrincipal(ctx sdk.Context, collateralType string, principal sdk.Coin) {
total := k.GetTotalPrincipal(ctx, collateralDenom, principal.Denom) total := k.GetTotalPrincipal(ctx, collateralType, principal.Denom)
total = total.Add(principal.Amount) 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 // 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) { func (k Keeper) DecrementTotalPrincipal(ctx sdk.Context, collateralType string, principal sdk.Coin) {
total := k.GetTotalPrincipal(ctx, collateralDenom, principal.Denom) total := k.GetTotalPrincipal(ctx, collateralType, principal.Denom)
// NOTE: negative total principal can happen in tests due to rounding errors // NOTE: negative total principal can happen in tests due to rounding errors
// in fee calculation // in fee calculation
total = sdk.MaxInt(total.Sub(principal.Amount), sdk.ZeroInt()) 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 // 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) store := prefix.NewStore(ctx.KVStore(k.key), types.PrincipalKeyPrefix)
bz := store.Get([]byte(collateralDenom + principalDenom)) bz := store.Get([]byte(collateralType + principalDenom))
if bz == nil { if bz == nil {
k.SetTotalPrincipal(ctx, collateralDenom, principalDenom, sdk.ZeroInt()) k.SetTotalPrincipal(ctx, collateralType, principalDenom, sdk.ZeroInt())
return sdk.ZeroInt() return sdk.ZeroInt()
} }
k.cdc.MustUnmarshalBinaryLengthPrefixed(bz, &total) 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 // 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 := 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 // 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%) // 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 // 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 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 // use the other account to create a cdp that SHOULD NOT have fees updated - 500% collateralization
// create CDP for the second address // 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 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 // fees to accumulate, in this example 600 seconds
oldtime := suite.ctx.BlockTime() oldtime := suite.ctx.BlockTime()
suite.ctx = suite.ctx.WithBlockTime(suite.ctx.BlockTime().Add(time.Second * 600)) 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 suite.NoError(err) // check that we don't have any error
// cdp we expect fees to accumulate for // 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) suite.True(found)
// check fees are not zero // check fees are not zero
// check that the fees have been updated // 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(sdk.NewInt(22), cdp1.AccumulatedFees.Amount)
suite.Equal(suite.ctx.BlockTime(), cdp1.FeesUpdated) suite.Equal(suite.ctx.BlockTime(), cdp1.FeesUpdated)
// cdp we expect fees to not accumulate for because of rounding to zero // 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) suite.True(found)
// check fees are zero // check fees are zero
suite.True(cdp2.AccumulatedFees.IsZero()) suite.True(cdp2.AccumulatedFees.IsZero())

View File

@ -49,6 +49,7 @@ func NewCDPGenState(asset string, liquidationRatio sdk.Dec) app.GenesisState {
CollateralParams: cdp.CollateralParams{ CollateralParams: cdp.CollateralParams{
{ {
Denom: asset, Denom: asset,
Type: asset + "-a",
LiquidationRatio: liquidationRatio, LiquidationRatio: liquidationRatio,
DebtLimit: sdk.NewInt64Coin("usdx", 1000000000000), DebtLimit: sdk.NewInt64Coin("usdx", 1000000000000),
StabilityFee: sdk.MustNewDecFromStr("1.000000001547125958"), // %5 apr StabilityFee: sdk.MustNewDecFromStr("1.000000001547125958"), // %5 apr
@ -121,6 +122,7 @@ func NewCDPGenStateMulti() app.GenesisState {
CollateralParams: cdp.CollateralParams{ CollateralParams: cdp.CollateralParams{
{ {
Denom: "xrp", Denom: "xrp",
Type: "xrp-a",
LiquidationRatio: sdk.MustNewDecFromStr("2.0"), LiquidationRatio: sdk.MustNewDecFromStr("2.0"),
DebtLimit: sdk.NewInt64Coin("usdx", 500000000000), DebtLimit: sdk.NewInt64Coin("usdx", 500000000000),
StabilityFee: sdk.MustNewDecFromStr("1.000000001547125958"), // %5 apr StabilityFee: sdk.MustNewDecFromStr("1.000000001547125958"), // %5 apr
@ -133,6 +135,7 @@ func NewCDPGenStateMulti() app.GenesisState {
}, },
{ {
Denom: "btc", Denom: "btc",
Type: "btc-a",
LiquidationRatio: sdk.MustNewDecFromStr("1.5"), LiquidationRatio: sdk.MustNewDecFromStr("1.5"),
DebtLimit: sdk.NewInt64Coin("usdx", 500000000000), DebtLimit: sdk.NewInt64Coin("usdx", 500000000000),
StabilityFee: sdk.MustNewDecFromStr("1.000000000782997609"), // %2.5 apr StabilityFee: sdk.MustNewDecFromStr("1.000000000782997609"), // %2.5 apr
@ -145,6 +148,7 @@ func NewCDPGenStateMulti() app.GenesisState {
}, },
{ {
Denom: "bnb", Denom: "bnb",
Type: "bnb-a",
LiquidationRatio: sdk.MustNewDecFromStr("1.5"), LiquidationRatio: sdk.MustNewDecFromStr("1.5"),
DebtLimit: sdk.NewInt64Coin("usdx", 500000000000), DebtLimit: sdk.NewInt64Coin("usdx", 500000000000),
StabilityFee: sdk.MustNewDecFromStr("1.000000001547125958"), // %5 apr StabilityFee: sdk.MustNewDecFromStr("1.000000001547125958"), // %5 apr
@ -185,6 +189,7 @@ func NewCDPGenStateHighDebtLimit() app.GenesisState {
CollateralParams: cdp.CollateralParams{ CollateralParams: cdp.CollateralParams{
{ {
Denom: "xrp", Denom: "xrp",
Type: "xrp-a",
LiquidationRatio: sdk.MustNewDecFromStr("2.0"), LiquidationRatio: sdk.MustNewDecFromStr("2.0"),
DebtLimit: sdk.NewInt64Coin("usdx", 50000000000000), DebtLimit: sdk.NewInt64Coin("usdx", 50000000000000),
StabilityFee: sdk.MustNewDecFromStr("1.000000001547125958"), // %5 apr StabilityFee: sdk.MustNewDecFromStr("1.000000001547125958"), // %5 apr
@ -197,6 +202,7 @@ func NewCDPGenStateHighDebtLimit() app.GenesisState {
}, },
{ {
Denom: "btc", Denom: "btc",
Type: "btc-a",
LiquidationRatio: sdk.MustNewDecFromStr("1.5"), LiquidationRatio: sdk.MustNewDecFromStr("1.5"),
DebtLimit: sdk.NewInt64Coin("usdx", 50000000000000), DebtLimit: sdk.NewInt64Coin("usdx", 50000000000000),
StabilityFee: sdk.MustNewDecFromStr("1.000000000782997609"), // %2.5 apr StabilityFee: sdk.MustNewDecFromStr("1.000000000782997609"), // %2.5 apr
@ -227,10 +233,10 @@ func NewCDPGenStateHighDebtLimit() app.GenesisState {
func cdps() (cdps cdp.CDPs) { func cdps() (cdps cdp.CDPs) {
_, addrs := app.GeneratePrivKeyAddressPairs(3) _, 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())) 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)), sdk.NewCoin("usdx", sdk.NewInt(10000000)), 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)), 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)), sdk.NewCoin("usdx", sdk.NewInt(500000000)), 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) cdps = append(cdps, c1, c2, c3, c4)
return 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 // 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) store := prefix.NewStore(ctx.KVStore(k.key), types.CdpKeyPrefix)
db, found := k.GetDenomPrefix(ctx, denom) db, found := k.GetCollateralTypePrefix(ctx, collateralType)
if !found { 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)) return sdk.KVStorePrefixIterator(store, types.DenomIterKey(db))
} }
// CdpCollateralRatioIndexIterator returns an sdk.Iterator for all cdps that have collateral denom // CdpCollateralRatioIndexIterator returns an sdk.Iterator for all cdps that have collateral denom
// matching denom and collateral:debt ratio LESS THAN targetRatio // 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) store := prefix.NewStore(ctx.KVStore(k.key), types.CollateralRatioIndexPrefix)
db, found := k.GetDenomPrefix(ctx, denom) db, found := k.GetCollateralTypePrefix(ctx, collateralType)
if !found { 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)) 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 // IterateCdpsByCollateralType 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)) { func (k Keeper) IterateCdpsByCollateralType(ctx sdk.Context, collateralType string, cb func(cdp types.CDP) (stop bool)) {
iterator := k.CdpDenomIndexIterator(ctx, denom) iterator := k.CdpDenomIndexIterator(ctx, collateralType)
defer iterator.Close() defer iterator.Close()
for ; iterator.Valid(); iterator.Next() { 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 // IterateCdpsByCollateralRatio iterate over cdps with collateral denom equal to denom and
// collateral:debt ratio LESS THAN targetRatio and performs a callback function. // 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)) { func (k Keeper) IterateCdpsByCollateralRatio(ctx sdk.Context, collateralType string, targetRatio sdk.Dec, cb func(cdp types.CDP) (stop bool)) {
iterator := k.CdpCollateralRatioIndexIterator(ctx, denom, targetRatio) iterator := k.CdpCollateralRatioIndexIterator(ctx, collateralType, targetRatio)
defer iterator.Close() defer iterator.Close()
for ; iterator.Valid(); iterator.Next() { for ; iterator.Valid(); iterator.Next() {
db, id, _ := types.SplitCollateralRatioKey(iterator.Key()) _, id, _ := types.SplitCollateralRatioKey(iterator.Key())
d := k.getDenomFromByte(ctx, db) cdp, found := k.GetCDP(ctx, collateralType, id)
cdp, found := k.GetCDP(ctx, d, id)
if !found { if !found {
panic(fmt.Sprintf("cdp %d does not exist", id)) 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() cdpKeeper := tApp.GetCDPKeeper()
for i := 0; i < n; i++ { 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 { if err != nil {
panic("failed to create cdp") panic("failed to create cdp")
} }
@ -138,7 +138,7 @@ func BenchmarkCdpCreation(b *testing.B) {
cdpKeeper := tApp.GetCDPKeeper() cdpKeeper := tApp.GetCDPKeeper()
b.ResetTimer() b.ResetTimer()
for i := 0; i < b.N; i++ { 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 { if err != nil {
b.Error("unexpected error") 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 // 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) params := k.GetParams(ctx)
for _, cp := range params.CollateralParams { for _, cp := range params.CollateralParams {
if cp.Denom == denom { if cp.Type == collateralType {
return cp, true return cp, true
} }
} }
@ -40,11 +40,11 @@ func (k Keeper) GetDebtParam(ctx sdk.Context, denom string) (types.DebtParam, bo
return types.DebtParam{}, false return types.DebtParam{}, false
} }
// GetDenomPrefix returns the prefix of the matching denom // GetCollateralTypePrefix returns the prefix of the matching denom
func (k Keeper) GetDenomPrefix(ctx sdk.Context, denom string) (byte, bool) { func (k Keeper) GetCollateralTypePrefix(ctx sdk.Context, collateralType string) (byte, bool) {
params := k.GetParams(ctx) params := k.GetParams(ctx)
for _, cp := range params.CollateralParams { for _, cp := range params.CollateralParams {
if cp.Denom == denom { if cp.Type == collateralType {
return cp.Prefix, true 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)) panic(fmt.Sprintf("no collateral denom with prefix %b", db))
} }
func (k Keeper) getSpotMarketID(ctx sdk.Context, denom string) string { func (k Keeper) getSpotMarketID(ctx sdk.Context, collateralType string) string {
cp, found := k.GetCollateral(ctx, denom) cp, found := k.GetCollateral(ctx, collateralType)
if !found { if !found {
panic(fmt.Sprintf("collateral not found: %s", denom)) panic(fmt.Sprintf("collateral not found: %s", collateralType))
} }
return cp.SpotMarketID return cp.SpotMarketID
} }
func (k Keeper) getliquidationMarketID(ctx sdk.Context, denom string) string { func (k Keeper) getliquidationMarketID(ctx sdk.Context, collateralType string) string {
cp, found := k.GetCollateral(ctx, denom) cp, found := k.GetCollateral(ctx, collateralType)
if !found { if !found {
panic(fmt.Sprintf("collateral not found: %s", denom)) panic(fmt.Sprintf("collateral not found: %s", collateralType))
} }
return cp.LiquidationMarketID return cp.LiquidationMarketID
} }
func (k Keeper) getLiquidationRatio(ctx sdk.Context, denom string) sdk.Dec { func (k Keeper) getLiquidationRatio(ctx sdk.Context, collateralType string) sdk.Dec {
cp, found := k.GetCollateral(ctx, denom) cp, found := k.GetCollateral(ctx, collateralType)
if !found { if !found {
panic(fmt.Sprintf("collateral not found: %s", denom)) panic(fmt.Sprintf("collateral not found: %s", collateralType))
} }
return cp.LiquidationRatio return cp.LiquidationRatio
} }
func (k Keeper) getLiquidationPenalty(ctx sdk.Context, denom string) sdk.Dec { func (k Keeper) getLiquidationPenalty(ctx sdk.Context, collateralType string) sdk.Dec {
cp, found := k.GetCollateral(ctx, denom) cp, found := k.GetCollateral(ctx, collateralType)
if !found { if !found {
panic(fmt.Sprintf("collateral not found: %s", denom)) panic(fmt.Sprintf("collateral not found: %s", collateralType))
} }
return cp.LiquidationPenalty return cp.LiquidationPenalty
} }
func (k Keeper) getAuctionSize(ctx sdk.Context, denom string) sdk.Int { func (k Keeper) getAuctionSize(ctx sdk.Context, collateralType string) sdk.Int {
cp, found := k.GetCollateral(ctx, denom) cp, found := k.GetCollateral(ctx, collateralType)
if !found { if !found {
panic(fmt.Sprintf("collateral not found: %s", denom)) panic(fmt.Sprintf("collateral not found: %s", collateralType))
} }
return cp.AuctionSize return cp.AuctionSize
} }
// GetFeeRate returns the per second fee rate for the input denom // GetFeeRate returns the per second fee rate for the input denom
func (k Keeper) getFeeRate(ctx sdk.Context, denom string) (fee sdk.Dec) { func (k Keeper) getFeeRate(ctx sdk.Context, collateralType string) (fee sdk.Dec) {
collalateralParam, found := k.GetCollateral(ctx, denom) collalateralParam, found := k.GetCollateral(ctx, collateralType)
if !found { 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 return collalateralParam.StabilityFee
} }

View File

@ -18,7 +18,7 @@ func NewQuerier(keeper Keeper) sdk.Querier {
case types.QueryGetCdp: case types.QueryGetCdp:
return queryGetCdp(ctx, req, keeper) return queryGetCdp(ctx, req, keeper)
case types.QueryGetCdps: case types.QueryGetCdps:
return queryGetCdpsByDenom(ctx, req, keeper) return queryGetCdpsByCollateralType(ctx, req, keeper)
case types.QueryGetCdpsByCollateralization: case types.QueryGetCdpsByCollateralization:
return queryGetCdpsByRatio(ctx, req, keeper) return queryGetCdpsByRatio(ctx, req, keeper)
case types.QueryGetParams: 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()) return nil, sdkerrors.Wrap(sdkerrors.ErrJSONUnmarshal, err.Error())
} }
_, valid := keeper.GetDenomPrefix(ctx, requestParams.CollateralDenom) _, valid := keeper.GetCollateralTypePrefix(ctx, requestParams.CollateralType)
if !valid { 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 { 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) 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()) return nil, sdkerrors.Wrap(sdkerrors.ErrJSONUnmarshal, err.Error())
} }
_, valid := keeper.GetDenomPrefix(ctx, requestParams.CollateralDenom) _, valid := keeper.GetCollateralTypePrefix(ctx, requestParams.CollateralType)
if !valid { 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 { 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) deposits := keeper.GetDeposits(ctx, cdp.ID)
@ -96,17 +96,17 @@ func queryGetCdpsByRatio(ctx sdk.Context, req abci.RequestQuery, keeper Keeper)
if err != nil { if err != nil {
return nil, sdkerrors.Wrap(sdkerrors.ErrJSONUnmarshal, err.Error()) return nil, sdkerrors.Wrap(sdkerrors.ErrJSONUnmarshal, err.Error())
} }
_, valid := keeper.GetDenomPrefix(ctx, requestParams.CollateralDenom) _, valid := keeper.GetCollateralTypePrefix(ctx, requestParams.CollateralType)
if !valid { 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 { if err != nil {
return nil, sdkerrors.Wrap(err, "couldn't get collateralization ratio from absolute ratio") 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 // augment CDPs by adding collateral value and collateralization ratio
var augmentedCDPs types.AugmentedCDPs var augmentedCDPs types.AugmentedCDPs
for _, cdp := range cdps { 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 // 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 var requestParams types.QueryCdpsParams
err := types.ModuleCdc.UnmarshalJSON(req.Data, &requestParams) err := types.ModuleCdc.UnmarshalJSON(req.Data, &requestParams)
if err != nil { if err != nil {
return nil, sdkerrors.Wrap(sdkerrors.ErrJSONUnmarshal, err.Error()) return nil, sdkerrors.Wrap(sdkerrors.ErrJSONUnmarshal, err.Error())
} }
_, valid := keeper.GetDenomPrefix(ctx, requestParams.CollateralDenom) _, valid := keeper.GetCollateralTypePrefix(ctx, requestParams.CollateralType)
if !valid { 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 // augment CDPs by adding collateral value and collateralization ratio
var augmentedCDPs types.AugmentedCDPs var augmentedCDPs types.AugmentedCDPs
for _, cdp := range cdps { 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) amount = simulation.RandIntBetween(rand.New(rand.NewSource(int64(j))), 500000000, 5000000000)
debt = simulation.RandIntBetween(rand.New(rand.NewSource(int64(j))), 1000000000, 25000000000) 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) 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) suite.True(f)
cdps[j] = c cdps[j] = c
aCDP := suite.keeper.LoadAugmentedCDP(suite.ctx, c) aCDP := suite.keeper.LoadAugmentedCDP(suite.ctx, c)
@ -116,7 +116,7 @@ func (suite *QuerierTestSuite) TestQueryCdp() {
ctx := suite.ctx.WithIsCheckTx(false) ctx := suite.ctx.WithIsCheckTx(false)
query := abci.RequestQuery{ query := abci.RequestQuery{
Path: strings.Join([]string{custom, types.QuerierRoute, types.QueryGetCdp}, "/"), 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) bz, err := suite.querier(ctx, []string{types.QueryGetCdp}, query)
suite.Nil(err) suite.Nil(err)
@ -128,7 +128,7 @@ func (suite *QuerierTestSuite) TestQueryCdp() {
query = abci.RequestQuery{ query = abci.RequestQuery{
Path: strings.Join([]string{custom, types.QuerierRoute, types.QueryGetCdp}, "/"), 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) _, err = suite.querier(ctx, []string{types.QueryGetCdp}, query)
suite.Error(err) suite.Error(err)
@ -146,18 +146,18 @@ func (suite *QuerierTestSuite) TestQueryCdp() {
query = abci.RequestQuery{ query = abci.RequestQuery{
Path: strings.Join([]string{custom, types.QuerierRoute, types.QueryGetCdp}, "/"), 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) _, err = suite.querier(ctx, []string{types.QueryGetCdp}, query)
suite.Error(err) suite.Error(err)
} }
func (suite *QuerierTestSuite) TestQueryCdpsByDenom() { func (suite *QuerierTestSuite) TestQueryCdpsByCollateralType() {
ctx := suite.ctx.WithIsCheckTx(false) ctx := suite.ctx.WithIsCheckTx(false)
query := abci.RequestQuery{ query := abci.RequestQuery{
Path: strings.Join([]string{custom, types.QuerierRoute, types.QueryGetCdps}, "/"), 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) bz, err := suite.querier(ctx, []string{types.QueryGetCdps}, query)
suite.Nil(err) suite.Nil(err)
@ -169,7 +169,7 @@ func (suite *QuerierTestSuite) TestQueryCdpsByDenom() {
query = abci.RequestQuery{ query = abci.RequestQuery{
Path: strings.Join([]string{custom, types.QuerierRoute, types.QueryGetCdps}, "/"), 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) _, err = suite.querier(ctx, []string{types.QueryGetCdps}, query)
suite.Error(err) suite.Error(err)
@ -183,8 +183,8 @@ func (suite *QuerierTestSuite) TestQueryCdpsByRatio() {
expectedXrpIds := []int{} expectedXrpIds := []int{}
expectedBtcIds := []int{} expectedBtcIds := []int{}
for _, cdp := range suite.cdps { for _, cdp := range suite.cdps {
absoluteRatio := suite.keeper.CalculateCollateralToDebtRatio(suite.ctx, cdp.Collateral, cdp.Principal) absoluteRatio := suite.keeper.CalculateCollateralToDebtRatio(suite.ctx, cdp.Collateral, cdp.Type, cdp.Principal)
collateralizationRatio, err := suite.keeper.CalculateCollateralizationRatioFromAbsoluteRatio(suite.ctx, cdp.Collateral.Denom, absoluteRatio, "liquidation") collateralizationRatio, err := suite.keeper.CalculateCollateralizationRatioFromAbsoluteRatio(suite.ctx, cdp.Type, absoluteRatio, "liquidation")
suite.Nil(err) suite.Nil(err)
if cdp.Collateral.Denom == "xrp" { if cdp.Collateral.Denom == "xrp" {
if collateralizationRatio.LT(xrpRatio) { if collateralizationRatio.LT(xrpRatio) {
@ -202,7 +202,7 @@ func (suite *QuerierTestSuite) TestQueryCdpsByRatio() {
ctx := suite.ctx.WithIsCheckTx(false) ctx := suite.ctx.WithIsCheckTx(false)
query := abci.RequestQuery{ query := abci.RequestQuery{
Path: strings.Join([]string{custom, types.QuerierRoute, types.QueryGetCdpsByCollateralization}, "/"), 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) bz, err := suite.querier(ctx, []string{types.QueryGetCdpsByCollateralization}, query)
suite.Nil(err) suite.Nil(err)
@ -219,7 +219,7 @@ func (suite *QuerierTestSuite) TestQueryCdpsByRatio() {
query = abci.RequestQuery{ query = abci.RequestQuery{
Path: strings.Join([]string{custom, types.QuerierRoute, types.QueryGetCdpsByCollateralization}, "/"), 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) bz, err = suite.querier(ctx, []string{types.QueryGetCdpsByCollateralization}, query)
suite.Nil(err) suite.Nil(err)
@ -236,7 +236,7 @@ func (suite *QuerierTestSuite) TestQueryCdpsByRatio() {
query = abci.RequestQuery{ query = abci.RequestQuery{
Path: strings.Join([]string{custom, types.QuerierRoute, types.QueryGetCdpsByCollateralization}, "/"), 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) bz, err = suite.querier(ctx, []string{types.QueryGetCdpsByCollateralization}, query)
suite.Nil(err) suite.Nil(err)
@ -265,7 +265,7 @@ func (suite *QuerierTestSuite) TestQueryDeposits() {
ctx := suite.ctx.WithIsCheckTx(false) ctx := suite.ctx.WithIsCheckTx(false)
query := abci.RequestQuery{ query := abci.RequestQuery{
Path: strings.Join([]string{custom, types.QuerierRoute, types.QueryGetCdpDeposits}, "/"), 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) 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) // (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 { func (k Keeper) SeizeCollateral(ctx sdk.Context, cdp types.CDP) error {
// Calculate the previous collateral ratio // 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 // Move debt coins from cdp to liquidator account
deposits := k.GetDeposits(ctx, cdp.ID) 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 { if err != nil {
return err return err
} }
// Decrement total principal for this collateral type // Decrement total principal for this collateral type
coinsToDecrement := cdp.GetTotalPrincipal() coinsToDecrement := cdp.GetTotalPrincipal()
k.DecrementTotalPrincipal(ctx, cdp.Collateral.Denom, coinsToDecrement) k.DecrementTotalPrincipal(ctx, cdp.Type, coinsToDecrement)
// Delete CDP from state // Delete CDP from state
k.RemoveCdpOwnerIndex(ctx, cdp) 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) return k.DeleteCDP(ctx, cdp)
} }
// LiquidateCdps seizes collateral from all CDPs below the input liquidation ratio // 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) price, err := k.pricefeedKeeper.GetCurrentPrice(ctx, marketID)
if err != nil { if err != nil {
return err return err
@ -77,7 +77,7 @@ func (k Keeper) LiquidateCdps(ctx sdk.Context, marketID string, denom string, li
// liquidation ratio = 1.5 // liquidation ratio = 1.5
// normalizedRatio = (1/(0.5/1.5)) = 3 // normalizedRatio = (1/(0.5/1.5)) = 3
normalizedRatio := sdk.OneDec().Quo(priceDivLiqRatio) normalizedRatio := sdk.OneDec().Quo(priceDivLiqRatio)
cdpsToLiquidate := k.GetAllCdpsByDenomAndRatio(ctx, denom, normalizedRatio) cdpsToLiquidate := k.GetAllCdpsByCollateralTypeAndRatio(ctx, collateralType, normalizedRatio)
for _, c := range cdpsToLiquidate { for _, c := range cdpsToLiquidate {
err := k.SeizeCollateral(ctx, c) err := k.SeizeCollateral(ctx, c)
if err != nil { 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 // ApplyLiquidationPenalty multiplies the input debt amount by the liquidation penalty
func (k Keeper) ApplyLiquidationPenalty(ctx sdk.Context, denom string, debt sdk.Int) sdk.Int { func (k Keeper) ApplyLiquidationPenalty(ctx sdk.Context, collateralType string, debt sdk.Int) sdk.Int {
penalty := k.getLiquidationPenalty(ctx, denom) penalty := k.getLiquidationPenalty(ctx, collateralType)
return sdk.NewDecFromInt(debt).Mul(penalty).RoundInt() return sdk.NewDecFromInt(debt).Mul(penalty).RoundInt()
} }

View File

@ -105,9 +105,9 @@ func (suite *SeizeTestSuite) createCdps() {
tracker.debt += int64(debt) 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) 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) suite.True(f)
cdps[j] = c cdps[j] = c
} }
@ -131,14 +131,14 @@ func (suite *SeizeTestSuite) setPrice(price sdk.Dec, market string) {
func (suite *SeizeTestSuite) TestSeizeCollateral() { func (suite *SeizeTestSuite) TestSeizeCollateral() {
suite.createCdps() suite.createCdps()
sk := suite.app.GetSupplyKeeper() 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) suite.True(found)
p := cdp.Principal.Amount p := cdp.Principal.Amount
cl := cdp.Collateral.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) err := suite.keeper.SeizeCollateral(suite.ctx, cdp)
suite.NoError(err) 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) suite.Equal(tpb.Sub(tpa), p)
auctionKeeper := suite.app.GetAuctionKeeper() auctionKeeper := suite.app.GetAuctionKeeper()
_, found = auctionKeeper.GetAuction(suite.ctx, auction.DefaultNextAuctionID) _, found = auctionKeeper.GetAuction(suite.ctx, auction.DefaultNextAuctionID)
@ -148,34 +148,34 @@ func (suite *SeizeTestSuite) TestSeizeCollateral() {
ak := suite.app.GetAccountKeeper() ak := suite.app.GetAccountKeeper()
acc := ak.GetAccount(suite.ctx, suite.addrs[1]) acc := ak.GetAccount(suite.ctx, suite.addrs[1])
suite.Equal(p.Int64(), acc.GetCoins().AmountOf("usdx").Int64()) 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)) suite.Require().True(errors.Is(err, types.ErrCdpNotFound))
} }
func (suite *SeizeTestSuite) TestSeizeCollateralMultiDeposit() { func (suite *SeizeTestSuite) TestSeizeCollateralMultiDeposit() {
suite.createCdps() suite.createCdps()
sk := suite.app.GetSupplyKeeper() 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) 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) 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) suite.True(found)
deposits := suite.keeper.GetDeposits(suite.ctx, cdp.ID) deposits := suite.keeper.GetDeposits(suite.ctx, cdp.ID)
suite.Equal(2, len(deposits)) suite.Equal(2, len(deposits))
p := cdp.Principal.Amount p := cdp.Principal.Amount
cl := cdp.Collateral.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) err = suite.keeper.SeizeCollateral(suite.ctx, cdp)
suite.NoError(err) 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) suite.Equal(tpb.Sub(tpa), p)
auctionMacc := sk.GetModuleAccount(suite.ctx, auction.ModuleName) auctionMacc := sk.GetModuleAccount(suite.ctx, auction.ModuleName)
suite.Equal(cs(c("debt", p.Int64()), c("xrp", cl.Int64())), auctionMacc.GetCoins()) suite.Equal(cs(c("debt", p.Int64()), c("xrp", cl.Int64())), auctionMacc.GetCoins())
ak := suite.app.GetAccountKeeper() ak := suite.app.GetAccountKeeper()
acc := ak.GetAccount(suite.ctx, suite.addrs[1]) acc := ak.GetAccount(suite.ctx, suite.addrs[1])
suite.Equal(p.Int64(), acc.GetCoins().AmountOf("usdx").Int64()) 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)) suite.Require().True(errors.Is(err, types.ErrCdpNotFound))
} }
@ -185,9 +185,9 @@ func (suite *SeizeTestSuite) TestLiquidateCdps() {
acc := sk.GetModuleAccount(suite.ctx, types.ModuleName) acc := sk.GetModuleAccount(suite.ctx, types.ModuleName)
originalXrpCollateral := acc.GetCoins().AmountOf("xrp") originalXrpCollateral := acc.GetCoins().AmountOf("xrp")
suite.setPrice(d("0.2"), "xrp:usd") 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.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) acc = sk.GetModuleAccount(suite.ctx, types.ModuleName)
finalXrpCollateral := acc.GetCoins().AmountOf("xrp") finalXrpCollateral := acc.GetCoins().AmountOf("xrp")
seizedXrpCollateral := originalXrpCollateral.Sub(finalXrpCollateral) seizedXrpCollateral := originalXrpCollateral.Sub(finalXrpCollateral)
@ -196,13 +196,13 @@ func (suite *SeizeTestSuite) TestLiquidateCdps() {
} }
func (suite *SeizeTestSuite) TestApplyLiquidationPenalty() { 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) 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) 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.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) { 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{ CollateralParams: types.CollateralParams{
{ {
Denom: "xrp", Denom: "xrp",
Type: "xrp-a",
LiquidationRatio: sdk.MustNewDecFromStr("2.0"), LiquidationRatio: sdk.MustNewDecFromStr("2.0"),
DebtLimit: sdk.NewInt64Coin("usdx", 20000000000000), DebtLimit: sdk.NewInt64Coin("usdx", 20000000000000),
StabilityFee: sdk.MustNewDecFromStr("1.000000004431822130"), StabilityFee: sdk.MustNewDecFromStr("1.000000004431822130"),
@ -91,6 +92,7 @@ func randomCdpGenState(selection int) types.GenesisState {
}, },
{ {
Denom: "btc", Denom: "btc",
Type: "btc-a",
LiquidationRatio: sdk.MustNewDecFromStr("1.25"), LiquidationRatio: sdk.MustNewDecFromStr("1.25"),
DebtLimit: sdk.NewInt64Coin("usdx", 50000000000000), DebtLimit: sdk.NewInt64Coin("usdx", 50000000000000),
StabilityFee: sdk.MustNewDecFromStr("1.000000000782997609"), StabilityFee: sdk.MustNewDecFromStr("1.000000000782997609"),
@ -103,6 +105,7 @@ func randomCdpGenState(selection int) types.GenesisState {
}, },
{ {
Denom: "bnb", Denom: "bnb",
Type: "bnb-a",
LiquidationRatio: sdk.MustNewDecFromStr("1.5"), LiquidationRatio: sdk.MustNewDecFromStr("1.5"),
DebtLimit: sdk.NewInt64Coin("usdx", 30000000000000), DebtLimit: sdk.NewInt64Coin("usdx", 30000000000000),
StabilityFee: sdk.MustNewDecFromStr("1.000000002293273137"), StabilityFee: sdk.MustNewDecFromStr("1.000000002293273137"),
@ -140,6 +143,7 @@ func randomCdpGenState(selection int) types.GenesisState {
CollateralParams: types.CollateralParams{ CollateralParams: types.CollateralParams{
{ {
Denom: "bnb", Denom: "bnb",
Type: "bnb-a",
LiquidationRatio: sdk.MustNewDecFromStr("1.5"), LiquidationRatio: sdk.MustNewDecFromStr("1.5"),
DebtLimit: sdk.NewInt64Coin("usdx", 100000000000000), DebtLimit: sdk.NewInt64Coin("usdx", 100000000000000),
StabilityFee: sdk.MustNewDecFromStr("1.000000002293273137"), 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) spendableCoins = spendableCoins.Sub(fees)
existingCDP, found := k.GetCdpByOwnerAndDenom(ctx, acc.GetAddress(), randCollateralParam.Denom) existingCDP, found := k.GetCdpByOwnerAndCollateralType(ctx, acc.GetAddress(), randCollateralParam.Denom)
if !found { 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 // 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 // (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 // calculate the max amount of debt that could be drawn for the chosen deposit
maxDebtDraw := collateralDepositValue.Quo(randCollateralParam.LiquidationRatio).TruncateInt() maxDebtDraw := collateralDepositValue.Quo(randCollateralParam.LiquidationRatio).TruncateInt()
// check that the debt limit hasn't been reached // 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) { if availableAssetDebt.LTE(debtParam.DebtFloor) {
// debt limit has been reached // debt limit has been reached
return simulation.NewOperationMsgBasic(types.ModuleName, "no-operation", "debt limit reached, cannot open cdp", false, nil), nil, nil 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 // randomly select a debt draw amount
debtDraw := sdk.NewInt(int64(simulation.RandIntBetween(r, int(debtParam.DebtFloor.Int64()), int(maxDebtDraw.Int64())))) 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( tx := helpers.GenTx(
[]sdk.Msg{msg}, []sdk.Msg{msg},
@ -158,7 +158,7 @@ func SimulateMsgCdp(ak types.AccountKeeper, k keeper.Keeper, pfk types.Pricefeed
// deposit 25% of the time // deposit 25% of the time
if hasCoins(spendableCoins, randCollateralParam.Denom) && shouldDeposit(r) { if hasCoins(spendableCoins, randCollateralParam.Denom) && shouldDeposit(r) {
randDepositAmount := sdk.NewInt(int64(simulation.RandIntBetween(r, 1, int(spendableCoins.AmountOf(randCollateralParam.Denom).Int64())))) 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( tx := helpers.GenTx(
[]sdk.Msg{msg}, []sdk.Msg{msg},

View File

@ -12,20 +12,22 @@ import (
// CDP is the state of a single collateralized debt position. // CDP is the state of a single collateralized debt position.
type CDP struct { type CDP struct {
ID uint64 `json:"id" yaml:"id"` // unique id for 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 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 Type string `json:"type" yaml:"type"` // string representing the unique collateral type of the CDP
Principal sdk.Coin `json:"principal" yaml:"principal"` Collateral sdk.Coin `json:"collateral" yaml:"collateral"` // Amount of collateral stored in this CDP
AccumulatedFees sdk.Coin `json:"accumulated_fees" yaml:"accumulated_fees"` Principal sdk.Coin `json:"principal" yaml:"principal"` // Amount of debt drawn using the CDP
FeesUpdated time.Time `json:"fees_updated" yaml:"fees_updated"` // Amount of stable coin drawn from this 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 // 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()) fees := sdk.NewCoin(principal.Denom, sdk.ZeroInt())
return CDP{ return CDP{
ID: id, ID: id,
Owner: owner, Owner: owner,
Type: collateralType,
Collateral: collateral, Collateral: collateral,
Principal: principal, Principal: principal,
AccumulatedFees: fees, AccumulatedFees: fees,
@ -45,7 +47,7 @@ func (cdp CDP) String() string {
Fees Last Updated: %s`, Fees Last Updated: %s`,
cdp.Owner, cdp.Owner,
cdp.ID, cdp.ID,
cdp.Collateral.Denom, cdp.Type,
cdp.Collateral, cdp.Collateral,
cdp.Principal, cdp.Principal,
cdp.AccumulatedFees, cdp.AccumulatedFees,
@ -73,10 +75,13 @@ func (cdp CDP) Validate() error {
if cdp.FeesUpdated.IsZero() { if cdp.FeesUpdated.IsZero() {
return errors.New("cdp updated fee time cannot be zero") 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 return nil
} }
// GetTotalPrinciple returns the total principle for the cdp // GetTotalPrincipal returns the total principle for the cdp
func (cdp CDP) GetTotalPrincipal() sdk.Coin { func (cdp CDP) GetTotalPrincipal() sdk.Coin {
return cdp.Principal.Add(cdp.AccumulatedFees) return cdp.Principal.Add(cdp.AccumulatedFees)
} }

View File

@ -42,7 +42,7 @@ func (suite *CdpValidationSuite) TestCdpValidation() {
}{ }{
{ {
name: "valid cdp", 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{ errArgs: errArgs{
expectPass: true, expectPass: true,
contains: "", contains: "",
@ -50,7 +50,7 @@ func (suite *CdpValidationSuite) TestCdpValidation() {
}, },
{ {
name: "invalid cdp id", 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{ errArgs: errArgs{
expectPass: false, expectPass: false,
contains: "cdp id cannot be 0", contains: "cdp id cannot be 0",
@ -58,7 +58,7 @@ func (suite *CdpValidationSuite) TestCdpValidation() {
}, },
{ {
name: "invalid collateral", 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{ errArgs: errArgs{
expectPass: false, expectPass: false,
contains: "invalid coins: collateral", contains: "invalid coins: collateral",
@ -66,7 +66,7 @@ func (suite *CdpValidationSuite) TestCdpValidation() {
}, },
{ {
name: "invalid prinicpal", 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{ errArgs: errArgs{
expectPass: false, expectPass: false,
contains: "invalid coins: principal", contains: "invalid coins: principal",
@ -74,7 +74,7 @@ func (suite *CdpValidationSuite) TestCdpValidation() {
}, },
{ {
name: "invalid fees", 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{ errArgs: errArgs{
expectPass: false, expectPass: false,
contains: "invalid coins: accumulated fees", contains: "invalid coins: accumulated fees",
@ -82,12 +82,20 @@ func (suite *CdpValidationSuite) TestCdpValidation() {
}, },
{ {
name: "invalid fees updated", 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{ errArgs: errArgs{
expectPass: false, expectPass: false,
contains: "cdp updated fee time cannot be zero", 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 { for _, tc := range testCases {
@ -161,11 +169,11 @@ func (suite *CdpValidationSuite) TestDepositValidation() {
func (suite *CdpValidationSuite) TestCdpGetTotalPrinciple() { func (suite *CdpValidationSuite) TestCdpGetTotalPrinciple() {
principal := sdk.Coin{"usdx", sdk.NewInt(100500)} 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) { func TestCdpValidationSuite(t *testing.T) {

View File

@ -20,17 +20,19 @@ var (
// MsgCreateCDP creates a cdp // MsgCreateCDP creates a cdp
type MsgCreateCDP struct { type MsgCreateCDP struct {
Sender sdk.AccAddress `json:"sender" yaml:"sender"` Sender sdk.AccAddress `json:"sender" yaml:"sender"`
Collateral sdk.Coin `json:"collateral" yaml:"collateral"` Collateral sdk.Coin `json:"collateral" yaml:"collateral"`
Principal sdk.Coin `json:"principal" yaml:"principal"` Principal sdk.Coin `json:"principal" yaml:"principal"`
CollateralType string `json:"collateral_type" yaml:"collateral_type"`
} }
// NewMsgCreateCDP returns a new MsgPlaceBid. // 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{ return MsgCreateCDP{
Sender: sender, Sender: sender,
Collateral: collateral, Collateral: collateral,
Principal: principal, Principal: principal,
CollateralType: collateralType,
} }
} }
@ -51,6 +53,9 @@ func (msg MsgCreateCDP) ValidateBasic() error {
if msg.Principal.IsZero() || !msg.Principal.IsValid() { if msg.Principal.IsZero() || !msg.Principal.IsValid() {
return sdkerrors.Wrapf(sdkerrors.ErrInvalidCoins, "principal amount %s", msg.Principal) 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 return nil
} }
@ -76,17 +81,19 @@ func (msg MsgCreateCDP) String() string {
// MsgDeposit deposit collateral to an existing cdp. // MsgDeposit deposit collateral to an existing cdp.
type MsgDeposit struct { type MsgDeposit struct {
Depositor sdk.AccAddress `json:"depositor" yaml:"depositor"` Depositor sdk.AccAddress `json:"depositor" yaml:"depositor"`
Owner sdk.AccAddress `json:"owner" yaml:"owner"` Owner sdk.AccAddress `json:"owner" yaml:"owner"`
Collateral sdk.Coin `json:"collateral" yaml:"collateral"` Collateral sdk.Coin `json:"collateral" yaml:"collateral"`
CollateralType string `json:"collateral_type" yaml:"collateral_type"`
} }
// NewMsgDeposit returns a new MsgDeposit // 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{ return MsgDeposit{
Owner: owner, Owner: owner,
Depositor: depositor, Depositor: depositor,
Collateral: collateral, Collateral: collateral,
CollateralType: collateralType,
} }
} }
@ -107,6 +114,9 @@ func (msg MsgDeposit) ValidateBasic() error {
if !msg.Collateral.IsValid() || msg.Collateral.IsZero() { if !msg.Collateral.IsValid() || msg.Collateral.IsZero() {
return sdkerrors.Wrapf(sdkerrors.ErrInvalidCoins, "collateral amount %s", msg.Collateral) 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 return nil
} }
@ -127,22 +137,25 @@ func (msg MsgDeposit) String() string {
Sender: %s Sender: %s
Owner: %s Owner: %s
Collateral: %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. // MsgWithdraw withdraw collateral from an existing cdp.
type MsgWithdraw struct { type MsgWithdraw struct {
Depositor sdk.AccAddress `json:"depositor" yaml:"depositor"` Depositor sdk.AccAddress `json:"depositor" yaml:"depositor"`
Owner sdk.AccAddress `json:"owner" yaml:"owner"` Owner sdk.AccAddress `json:"owner" yaml:"owner"`
Collateral sdk.Coin `json:"collateral" yaml:"collateral"` Collateral sdk.Coin `json:"collateral" yaml:"collateral"`
CollateralType string `json:"collateral_type" yaml:"collateral_type"`
} }
// NewMsgWithdraw returns a new MsgDeposit // 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{ return MsgWithdraw{
Owner: owner, Owner: owner,
Depositor: depositor, Depositor: depositor,
Collateral: collateral, Collateral: collateral,
CollateralType: collateralType,
} }
} }
@ -163,6 +176,9 @@ func (msg MsgWithdraw) ValidateBasic() error {
if !msg.Collateral.IsValid() || msg.Collateral.IsZero() { if !msg.Collateral.IsValid() || msg.Collateral.IsZero() {
return sdkerrors.Wrapf(sdkerrors.ErrInvalidCoins, "collateral amount %s", msg.Collateral) 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 return nil
} }
@ -188,17 +204,17 @@ func (msg MsgWithdraw) String() string {
// MsgDrawDebt draw debt off of collateral in cdp // MsgDrawDebt draw debt off of collateral in cdp
type MsgDrawDebt struct { type MsgDrawDebt struct {
Sender sdk.AccAddress `json:"sender" yaml:"sender"` Sender sdk.AccAddress `json:"sender" yaml:"sender"`
CdpDenom string `json:"cdp_denom" yaml:"cdp_denom"` CollateralType string `json:"collateral_type" yaml:"collateral_type"`
Principal sdk.Coin `json:"principal" yaml:"principal"` Principal sdk.Coin `json:"principal" yaml:"principal"`
} }
// NewMsgDrawDebt returns a new MsgDrawDebt // 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{ return MsgDrawDebt{
Sender: sender, Sender: sender,
CdpDenom: denom, CollateralType: collateralType,
Principal: principal, Principal: principal,
} }
} }
@ -213,8 +229,8 @@ func (msg MsgDrawDebt) ValidateBasic() error {
if msg.Sender.Empty() { if msg.Sender.Empty() {
return sdkerrors.Wrap(sdkerrors.ErrInvalidAddress, "sender address cannot be empty") return sdkerrors.Wrap(sdkerrors.ErrInvalidAddress, "sender address cannot be empty")
} }
if strings.TrimSpace(msg.CdpDenom) == "" { if strings.TrimSpace(msg.CollateralType) == "" {
return errors.New("cdp denom cannot be blank") return errors.New("cdp collateral type cannot be blank")
} }
if msg.Principal.IsZero() || !msg.Principal.IsValid() { if msg.Principal.IsZero() || !msg.Principal.IsValid() {
return sdkerrors.Wrapf(sdkerrors.ErrInvalidCoins, "principal amount %s", msg.Principal) 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 { func (msg MsgDrawDebt) String() string {
return fmt.Sprintf(`Draw debt from CDP Message: return fmt.Sprintf(`Draw debt from CDP Message:
Sender: %s Sender: %s
CDP Denom: %s Collateral Type: %s
Principal: %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 // MsgRepayDebt repay debt drawn off the collateral in a CDP
type MsgRepayDebt struct { type MsgRepayDebt struct {
Sender sdk.AccAddress `json:"sender" yaml:"sender"` Sender sdk.AccAddress `json:"sender" yaml:"sender"`
CdpDenom string `json:"cdp_denom" yaml:"cdp_denom"` CollateralType string `json:"collateral_type" yaml:"collateral_type"`
Payment sdk.Coin `json:"payment" yaml:"payment"` Payment sdk.Coin `json:"payment" yaml:"payment"`
} }
// NewMsgRepayDebt returns a new MsgRepayDebt // 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{ return MsgRepayDebt{
Sender: sender, Sender: sender,
CdpDenom: denom, CollateralType: collateralType,
Payment: payment, Payment: payment,
} }
} }
@ -269,8 +285,8 @@ func (msg MsgRepayDebt) ValidateBasic() error {
if msg.Sender.Empty() { if msg.Sender.Empty() {
return sdkerrors.Wrap(sdkerrors.ErrInvalidAddress, "sender address cannot be empty") return sdkerrors.Wrap(sdkerrors.ErrInvalidAddress, "sender address cannot be empty")
} }
if strings.TrimSpace(msg.CdpDenom) == "" { if strings.TrimSpace(msg.CollateralType) == "" {
return errors.New("cdp denom cannot be blank") return errors.New("cdp collateral type cannot be blank")
} }
if msg.Payment.IsZero() || !msg.Payment.IsValid() { if msg.Payment.IsZero() || !msg.Payment.IsValid() {
return sdkerrors.Wrapf(sdkerrors.ErrInvalidCoins, "payment amount %s", msg.Payment) 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 { func (msg MsgRepayDebt) String() string {
return fmt.Sprintf(`Draw debt from CDP Message: return fmt.Sprintf(`Draw debt from CDP Message:
Sender: %s Sender: %s
CDP Denom: %s Collateral Type: %s
Payment: %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) { func TestMsgCreateCDP(t *testing.T) {
tests := []struct { tests := []struct {
description string description string
sender sdk.AccAddress sender sdk.AccAddress
collateral sdk.Coin collateral sdk.Coin
principal sdk.Coin principal sdk.Coin
expectPass bool collateralType string
expectPass bool
}{ }{
{"create cdp", addrs[0], coinsSingle, coinsSingle, true}, {"create cdp", addrs[0], coinsSingle, coinsSingle, "type-a", true},
{"create cdp no collateral", addrs[0], coinsZero, coinsSingle, false}, {"create cdp no collateral", addrs[0], coinsZero, coinsSingle, "type-a", false},
{"create cdp no debt", addrs[0], coinsSingle, coinsZero, false}, {"create cdp no debt", addrs[0], coinsSingle, coinsZero, "type-a", false},
{"create cdp empty owner", sdk.AccAddress{}, coinsSingle, coinsSingle, 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 { for _, tc := range tests {
@ -36,6 +38,7 @@ func TestMsgCreateCDP(t *testing.T) {
tc.sender, tc.sender,
tc.collateral, tc.collateral,
tc.principal, tc.principal,
tc.collateralType,
) )
if tc.expectPass { if tc.expectPass {
require.NoError(t, msg.ValidateBasic(), "test: %v", tc.description) require.NoError(t, msg.ValidateBasic(), "test: %v", tc.description)
@ -47,17 +50,19 @@ func TestMsgCreateCDP(t *testing.T) {
func TestMsgDeposit(t *testing.T) { func TestMsgDeposit(t *testing.T) {
tests := []struct { tests := []struct {
description string description string
sender sdk.AccAddress sender sdk.AccAddress
depositor sdk.AccAddress depositor sdk.AccAddress
collateral sdk.Coin collateral sdk.Coin
expectPass bool collateralType string
expectPass bool
}{ }{
{"deposit", addrs[0], addrs[1], coinsSingle, true}, {"deposit", addrs[0], addrs[1], coinsSingle, "type-a", true},
{"deposit", addrs[0], addrs[0], coinsSingle, true}, {"deposit same owner", addrs[0], addrs[0], coinsSingle, "type-a", true},
{"deposit no collateral", addrs[0], addrs[1], coinsZero, false}, {"deposit no collateral", addrs[0], addrs[1], coinsZero, "type-a", false},
{"deposit empty owner", sdk.AccAddress{}, addrs[1], coinsSingle, false}, {"deposit empty owner", sdk.AccAddress{}, addrs[1], coinsSingle, "type-a", false},
{"deposit empty depositor", addrs[0], sdk.AccAddress{}, coinsSingle, 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 { for _, tc := range tests {
@ -65,6 +70,7 @@ func TestMsgDeposit(t *testing.T) {
tc.sender, tc.sender,
tc.depositor, tc.depositor,
tc.collateral, tc.collateral,
tc.collateralType,
) )
if tc.expectPass { if tc.expectPass {
require.NoError(t, msg.ValidateBasic(), "test: %v", tc.description) require.NoError(t, msg.ValidateBasic(), "test: %v", tc.description)
@ -76,17 +82,19 @@ func TestMsgDeposit(t *testing.T) {
func TestMsgWithdraw(t *testing.T) { func TestMsgWithdraw(t *testing.T) {
tests := []struct { tests := []struct {
description string description string
sender sdk.AccAddress sender sdk.AccAddress
depositor sdk.AccAddress depositor sdk.AccAddress
collateral sdk.Coin collateral sdk.Coin
expectPass bool collateralType string
expectPass bool
}{ }{
{"withdraw", addrs[0], addrs[1], coinsSingle, true}, {"withdraw", addrs[0], addrs[1], coinsSingle, "type-a", true},
{"withdraw", addrs[0], addrs[0], coinsSingle, true}, {"withdraw", addrs[0], addrs[0], coinsSingle, "type-a", true},
{"withdraw no collateral", addrs[0], addrs[1], coinsZero, false}, {"withdraw no collateral", addrs[0], addrs[1], coinsZero, "type-a", false},
{"withdraw empty owner", sdk.AccAddress{}, addrs[1], coinsSingle, false}, {"withdraw empty owner", sdk.AccAddress{}, addrs[1], coinsSingle, "type-a", false},
{"withdraw empty depositor", addrs[0], sdk.AccAddress{}, coinsSingle, 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 { for _, tc := range tests {
@ -94,6 +102,7 @@ func TestMsgWithdraw(t *testing.T) {
tc.sender, tc.sender,
tc.depositor, tc.depositor,
tc.collateral, tc.collateral,
tc.collateralType,
) )
if tc.expectPass { if tc.expectPass {
require.NoError(t, msg.ValidateBasic(), "test: %v", tc.description) require.NoError(t, msg.ValidateBasic(), "test: %v", tc.description)
@ -105,11 +114,11 @@ func TestMsgWithdraw(t *testing.T) {
func TestMsgDrawDebt(t *testing.T) { func TestMsgDrawDebt(t *testing.T) {
tests := []struct { tests := []struct {
description string description string
sender sdk.AccAddress sender sdk.AccAddress
denom string collateralType string
principal sdk.Coin principal sdk.Coin
expectPass bool expectPass bool
}{ }{
{"draw debt", addrs[0], sdk.DefaultBondDenom, coinsSingle, true}, {"draw debt", addrs[0], sdk.DefaultBondDenom, coinsSingle, true},
{"draw debt no debt", addrs[0], sdk.DefaultBondDenom, coinsZero, false}, {"draw debt no debt", addrs[0], sdk.DefaultBondDenom, coinsZero, false},
@ -120,7 +129,7 @@ func TestMsgDrawDebt(t *testing.T) {
for _, tc := range tests { for _, tc := range tests {
msg := NewMsgDrawDebt( msg := NewMsgDrawDebt(
tc.sender, tc.sender,
tc.denom, tc.collateralType,
tc.principal, tc.principal,
) )
if tc.expectPass { if tc.expectPass {

View File

@ -107,7 +107,8 @@ func DefaultParams() Params {
// CollateralParam governance parameters for each collateral type within the cdp module // CollateralParam governance parameters for each collateral type within the cdp module
type CollateralParam struct { 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 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 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 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 { func (cp CollateralParam) String() string {
return fmt.Sprintf(`Collateral: return fmt.Sprintf(`Collateral:
Denom: %s Denom: %s
Type: %s
Liquidation Ratio: %s Liquidation Ratio: %s
Stability Fee: %s Stability Fee: %s
Liquidation Penalty: %s Liquidation Penalty: %s
@ -132,7 +134,7 @@ func (cp CollateralParam) String() string {
Spot Market ID: %s Spot Market ID: %s
Liquidation Market ID: %s Liquidation Market ID: %s
Conversion Factor: %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 // CollateralParams array of CollateralParam
@ -299,8 +301,8 @@ func validateCollateralParams(i interface{}) error {
return fmt.Errorf("invalid parameter type: %T", i) return fmt.Errorf("invalid parameter type: %T", i)
} }
collateralDupMap := make(map[string]bool)
prefixDupMap := make(map[int]bool) prefixDupMap := make(map[int]bool)
typeDupMap := make(map[string]bool)
for _, cp := range collateralParams { for _, cp := range collateralParams {
if err := sdk.ValidateDenom(cp.Denom); err != nil { if err := sdk.ValidateDenom(cp.Denom); err != nil {
return fmt.Errorf("collateral denom invalid %s", cp.Denom) 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) 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) == "" { if strings.TrimSpace(cp.LiquidationMarketID) == "" {
return fmt.Errorf("liquidation market id cannot be blank %s", cp) return fmt.Errorf("liquidation market id cannot be blank %s", cp)
} }
@ -326,12 +332,11 @@ func validateCollateralParams(i interface{}) error {
prefixDupMap[prefix] = true prefixDupMap[prefix] = true
_, found = collateralDupMap[cp.Denom] _, found = typeDupMap[cp.Type]
if found { if found {
return fmt.Errorf("duplicate collateral denom: %s", cp.Denom) return fmt.Errorf("duplicate cdp collateral type: %s", cp.Type)
} }
typeDupMap[cp.Type] = true
collateralDupMap[cp.Denom] = true
if !cp.DebtLimit.IsValid() { if !cp.DebtLimit.IsValid() {
return fmt.Errorf("debt limit for all collaterals should be positive, is %s for %s", cp.DebtLimit, cp.Denom) 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{ collateralParams: types.CollateralParams{
{ {
Denom: "bnb", Denom: "bnb",
Type: "bnb-a",
LiquidationRatio: sdk.MustNewDecFromStr("1.5"), LiquidationRatio: sdk.MustNewDecFromStr("1.5"),
DebtLimit: sdk.NewInt64Coin("usdx", 2000000000000), DebtLimit: sdk.NewInt64Coin("usdx", 2000000000000),
StabilityFee: sdk.MustNewDecFromStr("1.000000001547125958"), StabilityFee: sdk.MustNewDecFromStr("1.000000001547125958"),
@ -103,6 +104,7 @@ func (suite *ParamsTestSuite) TestParamValidation() {
collateralParams: types.CollateralParams{ collateralParams: types.CollateralParams{
{ {
Denom: "bnb", Denom: "bnb",
Type: "bnb-a",
LiquidationRatio: sdk.MustNewDecFromStr("1.5"), LiquidationRatio: sdk.MustNewDecFromStr("1.5"),
DebtLimit: sdk.NewInt64Coin("usdx", 2000000000000), DebtLimit: sdk.NewInt64Coin("usdx", 2000000000000),
StabilityFee: sdk.MustNewDecFromStr("1.000000001547125958"), StabilityFee: sdk.MustNewDecFromStr("1.000000001547125958"),
@ -140,6 +142,7 @@ func (suite *ParamsTestSuite) TestParamValidation() {
collateralParams: types.CollateralParams{ collateralParams: types.CollateralParams{
{ {
Denom: "bnb", Denom: "bnb",
Type: "bnb-a",
LiquidationRatio: sdk.MustNewDecFromStr("1.5"), LiquidationRatio: sdk.MustNewDecFromStr("1.5"),
DebtLimit: sdk.NewInt64Coin("usdx", 2000000000000), DebtLimit: sdk.NewInt64Coin("usdx", 2000000000000),
StabilityFee: sdk.MustNewDecFromStr("1.000000001547125958"), StabilityFee: sdk.MustNewDecFromStr("1.000000001547125958"),
@ -177,6 +180,7 @@ func (suite *ParamsTestSuite) TestParamValidation() {
collateralParams: types.CollateralParams{ collateralParams: types.CollateralParams{
{ {
Denom: "bnb", Denom: "bnb",
Type: "bnb-a",
LiquidationRatio: sdk.MustNewDecFromStr("1.5"), LiquidationRatio: sdk.MustNewDecFromStr("1.5"),
DebtLimit: sdk.NewInt64Coin("usdx", 2000000000000), DebtLimit: sdk.NewInt64Coin("usdx", 2000000000000),
StabilityFee: sdk.MustNewDecFromStr("1.000000001547125958"), StabilityFee: sdk.MustNewDecFromStr("1.000000001547125958"),
@ -189,6 +193,7 @@ func (suite *ParamsTestSuite) TestParamValidation() {
}, },
{ {
Denom: "xrp", Denom: "xrp",
Type: "xrp-a",
LiquidationRatio: sdk.MustNewDecFromStr("1.5"), LiquidationRatio: sdk.MustNewDecFromStr("1.5"),
DebtLimit: sdk.NewInt64Coin("usdx", 2000000000000), DebtLimit: sdk.NewInt64Coin("usdx", 2000000000000),
StabilityFee: sdk.MustNewDecFromStr("1.000000001547125958"), StabilityFee: sdk.MustNewDecFromStr("1.000000001547125958"),
@ -226,6 +231,7 @@ func (suite *ParamsTestSuite) TestParamValidation() {
collateralParams: types.CollateralParams{ collateralParams: types.CollateralParams{
{ {
Denom: "bnb", Denom: "bnb",
Type: "bnb-a",
LiquidationRatio: sdk.MustNewDecFromStr("1.5"), LiquidationRatio: sdk.MustNewDecFromStr("1.5"),
DebtLimit: sdk.NewInt64Coin("usdx", 2000000000000), DebtLimit: sdk.NewInt64Coin("usdx", 2000000000000),
StabilityFee: sdk.MustNewDecFromStr("1.000000001547125958"), StabilityFee: sdk.MustNewDecFromStr("1.000000001547125958"),
@ -238,6 +244,7 @@ func (suite *ParamsTestSuite) TestParamValidation() {
}, },
{ {
Denom: "xrp", Denom: "xrp",
Type: "xrp-a",
LiquidationRatio: sdk.MustNewDecFromStr("1.5"), LiquidationRatio: sdk.MustNewDecFromStr("1.5"),
DebtLimit: sdk.NewInt64Coin("usdx", 2000000000000), DebtLimit: sdk.NewInt64Coin("usdx", 2000000000000),
StabilityFee: sdk.MustNewDecFromStr("1.000000001547125958"), StabilityFee: sdk.MustNewDecFromStr("1.000000001547125958"),
@ -275,6 +282,7 @@ func (suite *ParamsTestSuite) TestParamValidation() {
collateralParams: types.CollateralParams{ collateralParams: types.CollateralParams{
{ {
Denom: "bnb", Denom: "bnb",
Type: "bnb-a",
LiquidationRatio: sdk.MustNewDecFromStr("1.5"), LiquidationRatio: sdk.MustNewDecFromStr("1.5"),
DebtLimit: sdk.NewInt64Coin("usdx", 2000000000000), DebtLimit: sdk.NewInt64Coin("usdx", 2000000000000),
StabilityFee: sdk.MustNewDecFromStr("1.000000001547125958"), StabilityFee: sdk.MustNewDecFromStr("1.000000001547125958"),
@ -287,6 +295,7 @@ func (suite *ParamsTestSuite) TestParamValidation() {
}, },
{ {
Denom: "xrp", Denom: "xrp",
Type: "xrp-a",
LiquidationRatio: sdk.MustNewDecFromStr("1.5"), LiquidationRatio: sdk.MustNewDecFromStr("1.5"),
DebtLimit: sdk.NewInt64Coin("susd", 2000000000000), DebtLimit: sdk.NewInt64Coin("susd", 2000000000000),
StabilityFee: sdk.MustNewDecFromStr("1.000000001547125958"), StabilityFee: sdk.MustNewDecFromStr("1.000000001547125958"),
@ -361,6 +370,7 @@ func (suite *ParamsTestSuite) TestParamValidation() {
collateralParams: types.CollateralParams{ collateralParams: types.CollateralParams{
{ {
Denom: "bnb", Denom: "bnb",
Type: "bnb-a",
LiquidationRatio: sdk.MustNewDecFromStr("1.5"), LiquidationRatio: sdk.MustNewDecFromStr("1.5"),
DebtLimit: sdk.NewInt64Coin("usdx", 2000000000000), DebtLimit: sdk.NewInt64Coin("usdx", 2000000000000),
StabilityFee: sdk.MustNewDecFromStr("1.000000001547125958"), 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{ args: args{
globalDebtLimit: sdk.NewInt64Coin("usdx", 2000000000000), globalDebtLimit: sdk.NewInt64Coin("usdx", 2000000000000),
collateralParams: types.CollateralParams{ collateralParams: types.CollateralParams{
{ {
Denom: "bnb", Denom: "bnb",
Type: "bnb-a",
LiquidationRatio: sdk.MustNewDecFromStr("1.5"), LiquidationRatio: sdk.MustNewDecFromStr("1.5"),
DebtLimit: sdk.NewInt64Coin("usdx", 1000000000000), DebtLimit: sdk.NewInt64Coin("usdx", 1000000000000),
StabilityFee: sdk.MustNewDecFromStr("1.000000001547125958"), StabilityFee: sdk.MustNewDecFromStr("1.000000001547125958"),
@ -410,6 +421,7 @@ func (suite *ParamsTestSuite) TestParamValidation() {
}, },
{ {
Denom: "bnb", Denom: "bnb",
Type: "bnb-a",
LiquidationRatio: sdk.MustNewDecFromStr("1.5"), LiquidationRatio: sdk.MustNewDecFromStr("1.5"),
DebtLimit: sdk.NewInt64Coin("usdx", 1000000000000), DebtLimit: sdk.NewInt64Coin("usdx", 1000000000000),
StabilityFee: sdk.MustNewDecFromStr("1.000000001547125958"), StabilityFee: sdk.MustNewDecFromStr("1.000000001547125958"),
@ -437,7 +449,58 @@ func (suite *ParamsTestSuite) TestParamValidation() {
}, },
errArgs: errArgs{ errArgs: errArgs{
expectPass: false, 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{ collateralParams: types.CollateralParams{
{ {
Denom: "bnb", Denom: "bnb",
Type: "bnb-a",
LiquidationRatio: sdk.MustNewDecFromStr("1.5"), LiquidationRatio: sdk.MustNewDecFromStr("1.5"),
DebtLimit: sdk.NewInt64Coin("usdx", 1000000000000), DebtLimit: sdk.NewInt64Coin("usdx", 1000000000000),
StabilityFee: sdk.MustNewDecFromStr("1.000000001547125958"), StabilityFee: sdk.MustNewDecFromStr("1.000000001547125958"),
@ -459,6 +523,7 @@ func (suite *ParamsTestSuite) TestParamValidation() {
}, },
{ {
Denom: "xrp", Denom: "xrp",
Type: "xrp-a",
LiquidationRatio: sdk.MustNewDecFromStr("1.5"), LiquidationRatio: sdk.MustNewDecFromStr("1.5"),
DebtLimit: sdk.NewInt64Coin("usdx", 1000000000000), DebtLimit: sdk.NewInt64Coin("usdx", 1000000000000),
StabilityFee: sdk.MustNewDecFromStr("1.000000001547125958"), StabilityFee: sdk.MustNewDecFromStr("1.000000001547125958"),
@ -496,6 +561,7 @@ func (suite *ParamsTestSuite) TestParamValidation() {
collateralParams: types.CollateralParams{ collateralParams: types.CollateralParams{
{ {
Denom: "bnb", Denom: "bnb",
Type: "bnb-a",
LiquidationRatio: sdk.MustNewDecFromStr("1.5"), LiquidationRatio: sdk.MustNewDecFromStr("1.5"),
DebtLimit: sdk.Coin{}, DebtLimit: sdk.Coin{},
StabilityFee: sdk.MustNewDecFromStr("1.000000001547125958"), StabilityFee: sdk.MustNewDecFromStr("1.000000001547125958"),
@ -533,6 +599,7 @@ func (suite *ParamsTestSuite) TestParamValidation() {
collateralParams: types.CollateralParams{ collateralParams: types.CollateralParams{
{ {
Denom: "bnb", Denom: "bnb",
Type: "bnb-a",
LiquidationRatio: sdk.MustNewDecFromStr("1.5"), LiquidationRatio: sdk.MustNewDecFromStr("1.5"),
DebtLimit: sdk.NewInt64Coin("usdx", 1000000000000), DebtLimit: sdk.NewInt64Coin("usdx", 1000000000000),
StabilityFee: sdk.MustNewDecFromStr("1.000000001547125958"), StabilityFee: sdk.MustNewDecFromStr("1.000000001547125958"),
@ -570,6 +637,7 @@ func (suite *ParamsTestSuite) TestParamValidation() {
collateralParams: types.CollateralParams{ collateralParams: types.CollateralParams{
{ {
Denom: "bnb", Denom: "bnb",
Type: "bnb-a",
LiquidationRatio: sdk.MustNewDecFromStr("1.5"), LiquidationRatio: sdk.MustNewDecFromStr("1.5"),
DebtLimit: sdk.NewInt64Coin("usdx", 1000000000000), DebtLimit: sdk.NewInt64Coin("usdx", 1000000000000),
StabilityFee: sdk.MustNewDecFromStr("1.000000001547125958"), StabilityFee: sdk.MustNewDecFromStr("1.000000001547125958"),
@ -607,6 +675,7 @@ func (suite *ParamsTestSuite) TestParamValidation() {
collateralParams: types.CollateralParams{ collateralParams: types.CollateralParams{
{ {
Denom: "bnb", Denom: "bnb",
Type: "bnb-a",
LiquidationRatio: sdk.MustNewDecFromStr("1.5"), LiquidationRatio: sdk.MustNewDecFromStr("1.5"),
DebtLimit: sdk.NewInt64Coin("usdx", 1000000000000), DebtLimit: sdk.NewInt64Coin("usdx", 1000000000000),
StabilityFee: sdk.MustNewDecFromStr("1.1"), StabilityFee: sdk.MustNewDecFromStr("1.1"),
@ -644,6 +713,7 @@ func (suite *ParamsTestSuite) TestParamValidation() {
collateralParams: types.CollateralParams{ collateralParams: types.CollateralParams{
{ {
Denom: "bnb", Denom: "bnb",
Type: "bnb-a",
LiquidationRatio: sdk.MustNewDecFromStr("1.5"), LiquidationRatio: sdk.MustNewDecFromStr("1.5"),
DebtLimit: sdk.NewInt64Coin("usdx", 2000000000000), DebtLimit: sdk.NewInt64Coin("usdx", 2000000000000),
StabilityFee: sdk.MustNewDecFromStr("1.000000001547125958"), StabilityFee: sdk.MustNewDecFromStr("1.000000001547125958"),
@ -681,6 +751,7 @@ func (suite *ParamsTestSuite) TestParamValidation() {
collateralParams: types.CollateralParams{ collateralParams: types.CollateralParams{
{ {
Denom: "bnb", Denom: "bnb",
Type: "bnb-a",
LiquidationRatio: sdk.MustNewDecFromStr("1.5"), LiquidationRatio: sdk.MustNewDecFromStr("1.5"),
DebtLimit: sdk.NewInt64Coin("usdx", 2000000000000), DebtLimit: sdk.NewInt64Coin("usdx", 2000000000000),
StabilityFee: sdk.MustNewDecFromStr("1.000000001547125958"), StabilityFee: sdk.MustNewDecFromStr("1.000000001547125958"),

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -25,7 +25,7 @@ func InitGenesis(ctx sdk.Context, k keeper.Keeper, supplyKeeper types.SupplyKeep
k.SetParams(ctx, gs.Params) k.SetParams(ctx, gs.Params)
for _, r := range gs.Params.Rewards { 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 // 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 { 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) { 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 { 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 { 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 { if err != nil {
return nil, err 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 // 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, denom string) (types.RewardPeriod, bool) { func (k Keeper) GetRewardPeriod(ctx sdk.Context, collateralType string) (types.RewardPeriod, bool) {
store := prefix.NewStore(ctx.KVStore(k.key), types.RewardPeriodKeyPrefix) store := prefix.NewStore(ctx.KVStore(k.key), types.RewardPeriodKeyPrefix)
bz := store.Get([]byte(denom)) bz := store.Get([]byte(collateralType))
if bz == nil { if bz == nil {
return types.RewardPeriod{}, false 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) { func (k Keeper) SetRewardPeriod(ctx sdk.Context, rp types.RewardPeriod) {
store := prefix.NewStore(ctx.KVStore(k.key), types.RewardPeriodKeyPrefix) store := prefix.NewStore(ctx.KVStore(k.key), types.RewardPeriodKeyPrefix)
bz := k.cdc.MustMarshalBinaryBare(rp) 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, // DeleteRewardPeriod deletes the reward period in the store for the input collateral type,
func (k Keeper) DeleteRewardPeriod(ctx sdk.Context, denom string) { func (k Keeper) DeleteRewardPeriod(ctx sdk.Context, collateralType string) {
store := prefix.NewStore(ctx.KVStore(k.key), types.RewardPeriodKeyPrefix) 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 // 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 return rps
} }
// GetNextClaimPeriodID returns the highest claim period id in the store for the input denom // GetNextClaimPeriodID returns the highest claim period id in the store for the input collateral type
func (k Keeper) GetNextClaimPeriodID(ctx sdk.Context, denom string) uint64 { func (k Keeper) GetNextClaimPeriodID(ctx sdk.Context, collateralType string) uint64 {
store := prefix.NewStore(ctx.KVStore(k.key), types.NextClaimPeriodIDPrefix) store := prefix.NewStore(ctx.KVStore(k.key), types.NextClaimPeriodIDPrefix)
bz := store.Get([]byte(denom)) bz := store.Get([]byte(collateralType))
if bz == nil { if bz == nil {
k.SetNextClaimPeriodID(ctx, denom, 1) k.SetNextClaimPeriodID(ctx, collateralType, 1)
return uint64(1) return uint64(1)
} }
return types.BytesToUint64(bz) return types.BytesToUint64(bz)
} }
// SetNextClaimPeriodID sets the highest claim period id in the store for the input denom // SetNextClaimPeriodID sets the highest claim period id in the store for the input collateral type
func (k Keeper) SetNextClaimPeriodID(ctx sdk.Context, denom string, id uint64) { func (k Keeper) SetNextClaimPeriodID(ctx sdk.Context, collateralType string, id uint64) {
store := prefix.NewStore(ctx.KVStore(k.key), types.NextClaimPeriodIDPrefix) 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 // 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(denom string, id uint64) (stop bool)) { func (k Keeper) IterateClaimPeriodIDKeysAndValues(ctx sdk.Context, cb func(collateralType string, id uint64) (stop bool)) {
store := prefix.NewStore(ctx.KVStore(k.key), types.NextClaimPeriodIDPrefix) store := prefix.NewStore(ctx.KVStore(k.key), types.NextClaimPeriodIDPrefix)
iterator := sdk.KVStorePrefixIterator(store, []byte{}) iterator := sdk.KVStorePrefixIterator(store, []byte{})
defer iterator.Close() defer iterator.Close()
for ; iterator.Valid(); iterator.Next() { for ; iterator.Valid(); iterator.Next() {
id := types.BytesToUint64(iterator.Value()) id := types.BytesToUint64(iterator.Value())
denom := string(iterator.Key()) collateralType := string(iterator.Key())
if cb(denom, id) { if cb(collateralType, id) {
break 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 { func (k Keeper) GetAllClaimPeriodIDPairs(ctx sdk.Context) types.GenesisClaimPeriodIDs {
ids := 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{ genID := types.GenesisClaimPeriodID{
Denom: denom, CollateralType: collateralType,
ID: id, ID: id,
} }
ids = append(ids, genID) ids = append(ids, genID)
return false return false
@ -131,11 +131,11 @@ func (k Keeper) GetAllClaimPeriodIDPairs(ctx sdk.Context) types.GenesisClaimPeri
return ids return ids
} }
// GetClaimPeriod returns claim period in the store for the input ID and denom and a boolean for if it was found // 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, denom string) (types.ClaimPeriod, bool) { func (k Keeper) GetClaimPeriod(ctx sdk.Context, id uint64, collateralType string) (types.ClaimPeriod, bool) {
var cp types.ClaimPeriod var cp types.ClaimPeriod
store := prefix.NewStore(ctx.KVStore(k.key), types.ClaimPeriodKeyPrefix) 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 { if bz == nil {
return types.ClaimPeriod{}, false return types.ClaimPeriod{}, false
} }
@ -143,17 +143,17 @@ func (k Keeper) GetClaimPeriod(ctx sdk.Context, id uint64, denom string) (types.
return cp, true 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) { func (k Keeper) SetClaimPeriod(ctx sdk.Context, cp types.ClaimPeriod) {
store := prefix.NewStore(ctx.KVStore(k.key), types.ClaimPeriodKeyPrefix) store := prefix.NewStore(ctx.KVStore(k.key), types.ClaimPeriodKeyPrefix)
bz := k.cdc.MustMarshalBinaryBare(cp) 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 // DeleteClaimPeriod deletes the claim period in the store for the input ID and collateral type
func (k Keeper) DeleteClaimPeriod(ctx sdk.Context, id uint64, denom string) { func (k Keeper) DeleteClaimPeriod(ctx sdk.Context, id uint64, collateralType string) {
store := prefix.NewStore(ctx.KVStore(k.key), types.ClaimPeriodKeyPrefix) 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 // 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 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 // 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, denom string, id uint64) (types.Claim, bool) { 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) 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 { if bz == nil {
return types.Claim{}, false return types.Claim{}, false
} }
@ -192,18 +192,18 @@ func (k Keeper) GetClaim(ctx sdk.Context, addr sdk.AccAddress, denom string, id
return c, true 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) { func (k Keeper) SetClaim(ctx sdk.Context, c types.Claim) {
store := prefix.NewStore(ctx.KVStore(k.key), types.ClaimKeyPrefix) store := prefix.NewStore(ctx.KVStore(k.key), types.ClaimKeyPrefix)
bz := k.cdc.MustMarshalBinaryBare(c) 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 // 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, denom string, id uint64) { func (k Keeper) DeleteClaim(ctx sdk.Context, owner sdk.AccAddress, collateralType string, id uint64) {
store := prefix.NewStore(ctx.KVStore(k.key), types.ClaimKeyPrefix) 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 // 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)) suite.Equal(2, len(claims))
var genIDs types.GenesisClaimPeriodIDs var genIDs types.GenesisClaimPeriodIDs
suite.keeper.IterateClaimPeriodIDKeysAndValues(suite.ctx, func(denom string, id uint64) (stop bool) { suite.keeper.IterateClaimPeriodIDKeysAndValues(suite.ctx, func(collateralType string, id uint64) (stop bool) {
genID := types.GenesisClaimPeriodID{Denom: denom, ID: id} genID := types.GenesisClaimPeriodID{CollateralType: collateralType, ID: id}
genIDs = append(genIDs, genID) genIDs = append(genIDs, genID)
return false return false
}) })

View File

@ -13,21 +13,21 @@ import (
) )
// PayoutClaim sends the timelocked claim coins to the input address // 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 { func (k Keeper) PayoutClaim(ctx sdk.Context, addr sdk.AccAddress, collateralType string, id uint64) error {
claim, found := k.GetClaim(ctx, addr, denom, id) claim, found := k.GetClaim(ctx, addr, collateralType, id)
if !found { 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 { 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())) err := k.SendTimeLockedCoinsToAccount(ctx, types.IncentiveMacc, addr, sdk.NewCoins(claim.Reward), int64(claimPeriod.TimeLock.Seconds()))
if err != nil { if err != nil {
return err return err
} }
k.DeleteClaim(ctx, addr, denom, id) k.DeleteClaim(ctx, addr, collateralType, id)
ctx.EventManager().EmitEvent( ctx.EventManager().EmitEvent(
sdk.NewEvent( sdk.NewEvent(
@ -97,13 +97,13 @@ func (k Keeper) DeleteExpiredClaimsAndClaimPeriods(ctx sdk.Context) {
return false return false
} }
k.IterateClaims(ctx, func(c types.Claim) (stop bool) { 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 return false
} }
k.DeleteClaim(ctx, c.Owner, c.Denom, c.ClaimPeriodID) k.DeleteClaim(ctx, c.Owner, c.CollateralType, c.ClaimPeriodID)
return false return false
}) })
k.DeleteClaimPeriod(ctx, cp.ID, cp.Denom) k.DeleteClaimPeriod(ctx, cp.ID, cp.CollateralType)
ctx.EventManager().EmitEvent( ctx.EventManager().EmitEvent(
sdk.NewEvent( sdk.NewEvent(
types.EventTypeClaimPeriodExpiry, 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 // GetClaimsByAddressAndCollateralType 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) { func (k Keeper) GetClaimsByAddressAndCollateralType(ctx sdk.Context, addr sdk.AccAddress, collateralType string) (claims types.Claims, found bool) {
found = false found = false
k.IterateClaimPeriods(ctx, func(cp types.ClaimPeriod) (stop bool) { k.IterateClaimPeriods(ctx, func(cp types.ClaimPeriod) (stop bool) {
if cp.Denom != denom { if cp.CollateralType != collateralType {
return false return false
} }
c, hasClaim := k.GetClaim(ctx, addr, cp.Denom, cp.ID) c, hasClaim := k.GetClaim(ctx, addr, cp.CollateralType, cp.ID)
if !hasClaim { if !hasClaim {
return false return false
} }

View File

@ -73,7 +73,7 @@ func queryGetClaims(ctx sdk.Context, req abci.RequestQuery, k Keeper) ([]byte, e
if err != nil { if err != nil {
return nil, sdkerrors.Wrap(sdkerrors.ErrJSONUnmarshal, err.Error()) 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) bz, err := codec.MarshalJSONIndent(k.cdc, claims)
if err != nil { 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 // 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) { 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 := prefix.NewStore(ctx.KVStore(k.key), types.RewardPeriodKeyPrefix)
store.Delete([]byte(rp.Denom)) store.Delete([]byte(rp.CollateralType))
return return
} }
@ -36,10 +36,10 @@ func (k Keeper) CreateAndDeleteRewardPeriods(ctx sdk.Context) {
params := k.GetParams(ctx) params := k.GetParams(ctx)
for _, r := range params.Rewards { 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 governance has made a reward inactive, delete the current period
if found && !r.Active { 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 a reward period for an active reward is not found, create one
if !found && r.Active { if !found && r.Active {
@ -61,7 +61,7 @@ func (k Keeper) ApplyRewardsToCdps(ctx sdk.Context) {
k.IterateRewardPeriods(ctx, func(rp types.RewardPeriod) bool { k.IterateRewardPeriods(ctx, func(rp types.RewardPeriod) bool {
expired := false expired := false
// the total amount of usdx created with the collateral type being incentivized // 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 // the number of seconds since last payout
timeElapsed := sdk.NewInt(ctx.BlockTime().Unix() - previousBlockTime.Unix()) timeElapsed := sdk.NewInt(ctx.BlockTime().Unix() - previousBlockTime.Unix())
if rp.End.Before(ctx.BlockTime()) { 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) // the amount of rewards to pay (rewardAmount * timeElapsed)
rewardsThisPeriod := rp.Reward.Amount.Mul(timeElapsed) rewardsThisPeriod := rp.Reward.Amount.Mul(timeElapsed)
id := k.GetNextClaimPeriodID(ctx, rp.Denom) id := k.GetNextClaimPeriodID(ctx, rp.CollateralType)
k.cdpKeeper.IterateCdpsByDenom(ctx, rp.Denom, func(cdp cdptypes.CDP) bool { k.cdpKeeper.IterateCdpsByCollateralType(ctx, rp.CollateralType, func(cdp cdptypes.CDP) bool {
rewardsShare := sdk.NewDecFromInt(cdp.GetTotalPrincipal().Amount).Quo(sdk.NewDecFromInt(totalPrincipal)) rewardsShare := sdk.NewDecFromInt(cdp.GetTotalPrincipal().Amount).Quo(sdk.NewDecFromInt(totalPrincipal))
// sanity check - don't create zero claims // sanity check - don't create zero claims
if rewardsShare.IsZero() { if rewardsShare.IsZero() {
return false return false
} }
rewardsEarned := rewardsShare.Mul(sdk.NewDecFromInt(rewardsThisPeriod)).RoundInt() 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 return false
}) })
if !expired { 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 // 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) { func (k Keeper) CreateUniqueClaimPeriod(ctx sdk.Context, collateralType string, end time.Time, timeLock time.Duration) {
id := k.GetNextClaimPeriodID(ctx, denom) id := k.GetNextClaimPeriodID(ctx, collateralType)
claimPeriod := types.NewClaimPeriod(denom, id, end, timeLock) claimPeriod := types.NewClaimPeriod(collateralType, id, end, timeLock)
ctx.EventManager().EmitEvent( ctx.EventManager().EmitEvent(
sdk.NewEvent( sdk.NewEvent(
types.EventTypeClaimPeriod, types.EventTypeClaimPeriod,
@ -103,16 +103,16 @@ func (k Keeper) CreateUniqueClaimPeriod(ctx sdk.Context, denom string, end time.
), ),
) )
k.SetClaimPeriod(ctx, claimPeriod) 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 // 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) { func (k Keeper) AddToClaim(ctx sdk.Context, addr sdk.AccAddress, collateralType string, id uint64, amount sdk.Coin) {
claim, found := k.GetClaim(ctx, addr, denom, id) claim, found := k.GetClaim(ctx, addr, collateralType, id)
if found { if found {
claim.Reward = claim.Reward.Add(amount) claim.Reward = claim.Reward.Add(amount)
} else { } else {
claim = types.NewClaim(addr, amount, denom, id) claim = types.NewClaim(addr, amount, collateralType, id)
} }
k.SetClaim(ctx, claim) k.SetClaim(ctx, claim)
} }

View File

@ -116,7 +116,7 @@ func (suite *KeeperTestSuite) TestApplyRewardsToCdps() {
}) })
suite.Equal(3, len(claims)) suite.Equal(3, len(claims))
// there should be no associated claim period, because the reward period has not ended yet // 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) 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 // 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 // delete the old reward period amd create a new one
suite.keeper.CreateAndDeleteRewardPeriods(suite.ctx) 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) suite.True(found)
testID := suite.keeper.GetNextClaimPeriodID(suite.ctx, "bnb") testID := suite.keeper.GetNextClaimPeriodID(suite.ctx, "bnb-a")
suite.Equal(uint64(2), testID) suite.Equal(uint64(2), testID)
// move the context forward by 100 periods // move the context forward by 100 periods
@ -186,6 +186,7 @@ func (suite *KeeperTestSuite) setupCdpChain() {
CollateralParams: cdp.CollateralParams{ CollateralParams: cdp.CollateralParams{
{ {
Denom: "bnb", Denom: "bnb",
Type: "bnb-a",
LiquidationRatio: sdk.MustNewDecFromStr("2.0"), LiquidationRatio: sdk.MustNewDecFromStr("2.0"),
DebtLimit: sdk.NewInt64Coin("usdx", 1000000000000), DebtLimit: sdk.NewInt64Coin("usdx", 1000000000000),
StabilityFee: sdk.MustNewDecFromStr("1.000000001547125958"), // %5 apr StabilityFee: sdk.MustNewDecFromStr("1.000000001547125958"), // %5 apr
@ -213,10 +214,10 @@ func (suite *KeeperTestSuite) setupCdpChain() {
} }
incentiveGS := types.NewGenesisState( incentiveGS := types.NewGenesisState(
types.NewParams( 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.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.ClaimPeriods{},
types.Claims{}, types.Claims{},
types.GenesisClaimPeriodIDs{}) types.GenesisClaimPeriodIDs{})
@ -242,11 +243,11 @@ func (suite *KeeperTestSuite) setupCdpChain() {
suite.ctx = ctx suite.ctx = ctx
// create 3 cdps // create 3 cdps
cdpKeeper := tApp.GetCDPKeeper() 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) 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) 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) suite.Require().NoError(err)
// total usd is 1110 // total usd is 1110

View File

@ -28,7 +28,7 @@ func RandomizedGenState(simState *module.SimulationState) {
simState.Cdc.MustUnmarshalJSON(simState.GenState[cdp.ModuleName], &cdpGenesis) simState.Cdc.MustUnmarshalJSON(simState.GenState[cdp.ModuleName], &cdpGenesis)
if len(CollateralDenoms) == 0 { if len(CollateralDenoms) == 0 {
for _, collateral := range cdpGenesis.Params.CollateralParams { 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() end := start.Add(reward.Duration).UTC()
baseRewardAmount := reward.AvailableRewards.Amount.Quo(sdk.NewInt(100)) // base period reward is 1/100 total reward baseRewardAmount := reward.AvailableRewards.Amount.Quo(sdk.NewInt(100)) // base period reward is 1/100 total reward
// Earlier periods have larger rewards // 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) claimEnd := end.Add(reward.ClaimDuration)
claimTimeLock := reward.TimeLock claimTimeLock := reward.TimeLock
// Create reward period and append to array // 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 // Update start time of next reward period
rewardPeriodStart = end rewardPeriodStart = end
} }
@ -105,7 +105,7 @@ func genClaimPeriods(rewardPeriods types.RewardPeriods) types.ClaimPeriods {
claimPeriods := make(types.ClaimPeriods, len(rewardPeriods)) claimPeriods := make(types.ClaimPeriods, len(rewardPeriods))
for i, rewardPeriod := range rewardPeriods { for i, rewardPeriod := range rewardPeriods {
// Increment reward period count for this denom (this is our claim period's ID) // Increment reward period count for this denom (this is our claim period's ID)
denom := rewardPeriod.Denom denom := rewardPeriod.CollateralType
numbRewardPeriods := denomRewardPeriodsCount[denom] + 1 numbRewardPeriods := denomRewardPeriodsCount[denom] + 1
denomRewardPeriodsCount[denom] = numbRewardPeriods denomRewardPeriodsCount[denom] = numbRewardPeriods
// Set end and timelock from the associated reward period // 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) mostRecentClaimPeriodByDenom := make(map[string]uint64)
var claimPeriodIDs types.GenesisClaimPeriodIDs var claimPeriodIDs types.GenesisClaimPeriodIDs
for _, cp := range cps { for _, cp := range cps {
if cp.ID <= mostRecentClaimPeriodByDenom[cp.Denom] { if cp.ID <= mostRecentClaimPeriodByDenom[cp.CollateralType] {
continue continue
} }
claimPeriodIDs = append(claimPeriodIDs, types.GenesisClaimPeriodID{Denom: cp.Denom, ID: cp.ID}) claimPeriodIDs = append(claimPeriodIDs, types.GenesisClaimPeriodID{CollateralType: cp.CollateralType, ID: cp.ID})
mostRecentClaimPeriodByDenom[cp.Denom] = cp.ID mostRecentClaimPeriodByDenom[cp.CollateralType] = cp.ID
} }
return claimPeriodIDs 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 { 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 validAccounts[acc.Address.String()] { // Address must be valid type
if claim.Owner.Equals(acc.Address) { // Account must be claim owner 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 if found { // found should always be true
var rewards sdk.Coins var rewards sdk.Coins
for _, individualClaim := range allClaims { 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) 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( tx := helpers.GenTx(
[]sdk.Msg{msg}, []sdk.Msg{msg},

View File

@ -7,9 +7,9 @@ import (
// DONTCOVER // DONTCOVER
var ( var (
ErrClaimNotFound = sdkerrors.Register(ModuleName, 1, "no claim with input id found for owner 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 denom") ErrClaimPeriodNotFound = sdkerrors.Register(ModuleName, 2, "no claim period found for id and collateral type")
ErrInvalidAccountType = sdkerrors.Register(ModuleName, 3, "account type not supported") 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") 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 // CdpKeeper defines the expected cdp keeper for interacting with cdps
type CdpKeeper interface { type CdpKeeper interface {
IterateCdpsByDenom(ctx sdk.Context, denom string, cb func(cdp cdptypes.CDP) (stop bool)) IterateCdpsByCollateralType(ctx sdk.Context, collateralType string, cb func(cdp cdptypes.CDP) (stop bool))
GetTotalPrincipal(ctx sdk.Context, collateralDenom string, principalDenom string) (total sdk.Int) GetTotalPrincipal(ctx sdk.Context, collateralType string, principalDenom string) (total sdk.Int)
} }
// AccountKeeper defines the expected keeper interface for interacting with account // AccountKeeper defines the expected keeper interface for interacting with account

View File

@ -4,15 +4,14 @@ import (
"bytes" "bytes"
"errors" "errors"
"fmt" "fmt"
"strings"
"time" "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 { type GenesisClaimPeriodID struct {
Denom string `json:"denom" yaml:"denom"` CollateralType string `json:"collateral_type" yaml:"collateral_type"`
ID uint64 `json:"id" yaml:"id"` ID uint64 `json:"id" yaml:"id"`
} }
// Validate performs a basic check of a GenesisClaimPeriodID fields. // Validate performs a basic check of a GenesisClaimPeriodID fields.
@ -20,7 +19,10 @@ func (gcp GenesisClaimPeriodID) Validate() error {
if gcp.ID == 0 { if gcp.ID == 0 {
return errors.New("genesis claim period id cannot be 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 // GenesisClaimPeriodIDs array of GenesisClaimPeriodID
@ -32,9 +34,9 @@ func (gcps GenesisClaimPeriodIDs) Validate() error {
seenIDS := make(map[string]bool) seenIDS := make(map[string]bool)
var key string var key string
for _, gcp := range gcps { for _, gcp := range gcps {
key = gcp.Denom + string(gcp.ID) key = gcp.CollateralType + string(gcp.ID)
if seenIDS[key] { 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 { if err := gcp.Validate(); err != nil {

View File

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

View File

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

View File

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

View File

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

View File

@ -2,6 +2,7 @@ package types
import ( import (
"fmt" "fmt"
"strings"
"time" "time"
sdk "github.com/cosmos/cosmos-sdk/types" 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. // Reward stores the specified state for a single reward period.
type Reward struct { type Reward struct {
Active bool `json:"active" yaml:"active"` // governance switch to disable a period 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 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 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 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 // 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{ return Reward{
Active: active, Active: active,
Denom: denom, CollateralType: collateralType,
AvailableRewards: reward, AvailableRewards: reward,
Duration: duration, Duration: duration,
TimeLock: timelock, TimeLock: timelock,
@ -116,32 +117,35 @@ func NewReward(active bool, denom string, reward sdk.Coin, duration time.Duratio
func (r Reward) String() string { func (r Reward) String() string {
return fmt.Sprintf(`Reward: return fmt.Sprintf(`Reward:
Active: %t, Active: %t,
Denom: %s, CollateralType: %s,
Available Rewards: %s, Available Rewards: %s,
Duration: %s, Duration: %s,
Time Lock: %s, Time Lock: %s,
Claim Duration: %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. // Validate performs a basic check of a reward fields.
func (r Reward) Validate() error { func (r Reward) Validate() error {
if !r.AvailableRewards.IsValid() { 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() { 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 { 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 { 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 { 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 // Rewards array of Reward
@ -150,17 +154,17 @@ type Rewards []Reward
// Validate checks if all the rewards are valid and there are no duplicated // Validate checks if all the rewards are valid and there are no duplicated
// entries. // entries.
func (rs Rewards) Validate() error { func (rs Rewards) Validate() error {
rewardDenoms := make(map[string]bool) rewardCollateralTypes := make(map[string]bool)
for _, r := range rs { for _, r := range rs {
if rewardDenoms[r.Denom] { if rewardCollateralTypes[r.CollateralType] {
return fmt.Errorf("cannot have duplicate reward denoms: %s", r.Denom) return fmt.Errorf("cannot have duplicate reward collateral types: %s", r.CollateralType)
} }
if err := r.Validate(); err != nil { if err := r.Validate(); err != nil {
return err return err
} }
rewardDenoms[r.Denom] = true rewardCollateralTypes[r.CollateralType] = true
} }
return nil return nil
} }

View File

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

View File

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

View File

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

View File

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