mirror of
https://github.com/0glabs/0g-chain.git
synced 2024-12-24 15:25:18 +00:00
wip: cdp params and types
This commit is contained in:
parent
d5da161dd8
commit
e85d2f880b
@ -2,10 +2,12 @@ package cdp
|
||||
|
||||
import (
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
sdk "github.com/cosmos/cosmos-sdk/types"
|
||||
"github.com/cosmos/cosmos-sdk/x/mock"
|
||||
abci "github.com/tendermint/tendermint/abci/types"
|
||||
tmtime "github.com/tendermint/tendermint/types/time"
|
||||
|
||||
"github.com/kava-labs/kava/x/pricefeed"
|
||||
)
|
||||
@ -19,7 +21,7 @@ func TestApp_CreateModifyDeleteCDP(t *testing.T) {
|
||||
mock.SetGenesis(mapp, genAccs)
|
||||
mock.CheckBalance(t, mapp, testAddr, cs(c("xrp", 100)))
|
||||
// setup pricefeed, TODO can this be shortened a bit?
|
||||
header := abci.Header{Height: mapp.LastBlockHeight() + 1}
|
||||
header := abci.Header{Height: mapp.LastBlockHeight() + 1, Time: tmtime.Now()}
|
||||
mapp.BeginBlock(abci.RequestBeginBlock{Header: header})
|
||||
ctx := mapp.BaseApp.NewContext(false, header)
|
||||
params := CdpParams{
|
||||
@ -35,15 +37,19 @@ func TestApp_CreateModifyDeleteCDP(t *testing.T) {
|
||||
}
|
||||
keeper.SetParams(ctx, params)
|
||||
keeper.SetGlobalDebt(ctx, sdk.NewInt(0))
|
||||
ap := pricefeed.AssetParams{
|
||||
Assets: []pricefeed.Asset{pricefeed.Asset{AssetCode: "xrp", Description: ""}},
|
||||
ap := pricefeed.Params{
|
||||
Assets: []pricefeed.Asset{
|
||||
pricefeed.Asset{
|
||||
AssetCode: "xrp", BaseAsset: "xrp",
|
||||
QuoteAsset: "usd", Oracles: pricefeed.Oracles{}, Active: true},
|
||||
},
|
||||
}
|
||||
pfKeeper.SetAssetParams(ctx, ap)
|
||||
pfKeeper.SetParams(ctx, ap)
|
||||
pfKeeper.SetPrice(
|
||||
ctx, sdk.AccAddress{}, "xrp",
|
||||
sdk.MustNewDecFromStr("1.00"),
|
||||
sdk.NewInt(10))
|
||||
pfKeeper.SetCurrentPrices(ctx)
|
||||
header.Time.Add(time.Hour*1))
|
||||
pfKeeper.SetCurrentPrices(ctx, "xrp")
|
||||
mapp.EndBlock(abci.RequestEndBlock{})
|
||||
mapp.Commit()
|
||||
|
||||
|
@ -9,7 +9,7 @@ import (
|
||||
func InitGenesis(ctx sdk.Context, k Keeper, pk PricefeedKeeper, data GenesisState) {
|
||||
// validate denoms - check that any collaterals in the CdpParams are in the pricefeed, pricefeed needs to initgenesis before cdp
|
||||
collateralMap := make(map[string]int)
|
||||
ap := pk.GetAssetParams(ctx)
|
||||
ap := pk.GetParams(ctx)
|
||||
for _, a := range ap.Assets {
|
||||
collateralMap[a.AssetCode] = 1
|
||||
}
|
||||
|
@ -3,6 +3,7 @@ package keeper
|
||||
import (
|
||||
"fmt"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
sdk "github.com/cosmos/cosmos-sdk/types"
|
||||
"github.com/cosmos/cosmos-sdk/x/auth"
|
||||
@ -12,6 +13,7 @@ import (
|
||||
"github.com/kava-labs/kava/x/pricefeed"
|
||||
"github.com/stretchr/testify/require"
|
||||
abci "github.com/tendermint/tendermint/abci/types"
|
||||
tmtime "github.com/tendermint/tendermint/types/time"
|
||||
)
|
||||
|
||||
// How could one reduce the number of params in the test cases. Create a table driven test for each of the 4 add/withdraw collateral/debt?
|
||||
@ -103,27 +105,29 @@ func TestKeeper_ModifyCDP(t *testing.T) {
|
||||
}
|
||||
mock.SetGenesis(mapp, []authexported.Account{&genAcc})
|
||||
// create a new context
|
||||
header := abci.Header{Height: mapp.LastBlockHeight() + 1}
|
||||
header := abci.Header{Height: mapp.LastBlockHeight() + 1, Time: tmtime.Now()}
|
||||
mapp.BeginBlock(abci.RequestBeginBlock{Header: header})
|
||||
ctx := mapp.BaseApp.NewContext(false, header)
|
||||
keeper.SetParams(ctx, defaultParamsSingle())
|
||||
// setup store state
|
||||
ap := pricefeed.AssetParams{
|
||||
ap := pricefeed.Params{
|
||||
Assets: []pricefeed.Asset{
|
||||
pricefeed.Asset{AssetCode: "xrp", Description: ""},
|
||||
pricefeed.Asset{
|
||||
AssetCode: "xrp", BaseAsset: "xrp",
|
||||
QuoteAsset: "usd", Oracles: pricefeed.Oracles{}, Active: true},
|
||||
},
|
||||
}
|
||||
keeper.pricefeedKeeper.SetAssetParams(ctx, ap)
|
||||
keeper.pricefeedKeeper.SetParams(ctx, ap)
|
||||
_, err := keeper.pricefeedKeeper.SetPrice(
|
||||
ctx, ownerAddr, "xrp",
|
||||
sdk.MustNewDecFromStr(tc.price),
|
||||
sdk.NewInt(ctx.BlockHeight()+10))
|
||||
header.Time.Add(time.Hour*1))
|
||||
if err != nil {
|
||||
t.Log("test context height", ctx.BlockHeight())
|
||||
t.Log(err)
|
||||
t.Log(tc.name)
|
||||
}
|
||||
err = keeper.pricefeedKeeper.SetCurrentPrices(ctx)
|
||||
err = keeper.pricefeedKeeper.SetCurrentPrices(ctx, "xrp")
|
||||
if err != nil {
|
||||
t.Log("test context height", ctx.BlockHeight())
|
||||
t.Log(err)
|
||||
@ -144,9 +148,6 @@ func TestKeeper_ModifyCDP(t *testing.T) {
|
||||
|
||||
// get new state for verification
|
||||
actualCDP, found := keeper.GetCDP(ctx, tc.args.owner, tc.args.collateralDenom)
|
||||
if tc.name == "removeTooMuchCollateral" {
|
||||
t.Log(actualCDP.String())
|
||||
}
|
||||
|
||||
// check for err
|
||||
if tc.expectPass {
|
||||
@ -180,21 +181,23 @@ func TestKeeper_PartialSeizeCDP(t *testing.T) {
|
||||
testAddr := addrs[0]
|
||||
mock.SetGenesis(mapp, genAccs)
|
||||
// setup pricefeed
|
||||
header := abci.Header{Height: mapp.LastBlockHeight() + 1}
|
||||
header := abci.Header{Height: mapp.LastBlockHeight() + 1, Time: tmtime.Now()}
|
||||
mapp.BeginBlock(abci.RequestBeginBlock{Header: header})
|
||||
ctx := mapp.BaseApp.NewContext(false, header)
|
||||
keeper.SetParams(ctx, defaultParamsSingle())
|
||||
ap := pricefeed.AssetParams{
|
||||
ap := pricefeed.Params{
|
||||
Assets: []pricefeed.Asset{
|
||||
pricefeed.Asset{AssetCode: "xrp", Description: ""},
|
||||
pricefeed.Asset{
|
||||
AssetCode: "xrp", BaseAsset: "xrp",
|
||||
QuoteAsset: "usd", Oracles: pricefeed.Oracles{}, Active: true},
|
||||
},
|
||||
}
|
||||
keeper.pricefeedKeeper.SetAssetParams(ctx, ap)
|
||||
keeper.pricefeedKeeper.SetParams(ctx, ap)
|
||||
keeper.pricefeedKeeper.SetPrice(
|
||||
ctx, sdk.AccAddress{}, collateral,
|
||||
sdk.MustNewDecFromStr("1.00"),
|
||||
i(10))
|
||||
keeper.pricefeedKeeper.SetCurrentPrices(ctx)
|
||||
header.Time.Add(time.Hour*1))
|
||||
keeper.pricefeedKeeper.SetCurrentPrices(ctx, collateral)
|
||||
// Create CDP
|
||||
keeper.SetGlobalDebt(ctx, i(0))
|
||||
err := keeper.ModifyCDP(ctx, testAddr, collateral, i(10), i(5))
|
||||
@ -203,8 +206,8 @@ func TestKeeper_PartialSeizeCDP(t *testing.T) {
|
||||
keeper.pricefeedKeeper.SetPrice(
|
||||
ctx, sdk.AccAddress{}, collateral,
|
||||
sdk.MustNewDecFromStr("0.90"),
|
||||
i(10))
|
||||
keeper.pricefeedKeeper.SetCurrentPrices(ctx)
|
||||
header.Time.Add(time.Hour*1))
|
||||
keeper.pricefeedKeeper.SetCurrentPrices(ctx, collateral)
|
||||
|
||||
// Seize entire CDP
|
||||
err = keeper.PartialSeizeCDP(ctx, testAddr, collateral, i(10), i(5))
|
||||
|
@ -30,7 +30,7 @@ func setUpMockAppWithoutGenesis() (*mock.App, Keeper, []sdk.AccAddress, []crypto
|
||||
keyCDP := sdk.NewKVStoreKey("cdp")
|
||||
keyPriceFeed := sdk.NewKVStoreKey(pricefeed.StoreKey)
|
||||
pk := mapp.ParamsKeeper
|
||||
priceFeedKeeper := pricefeed.NewKeeper(keyPriceFeed, mapp.Cdc, pk.Subspace(pricefeed.DefaultParamspace).WithKeyTable(pricefeed.ParamKeyTable()), pricefeed.DefaultCodespace)
|
||||
priceFeedKeeper := pricefeed.NewKeeper(keyPriceFeed, mapp.Cdc, pk.Subspace(pricefeed.DefaultParamspace), pricefeed.DefaultCodespace)
|
||||
blacklistedAddrs := make(map[string]bool)
|
||||
bankKeeper := bank.NewBaseKeeper(mapp.AccountKeeper, pk.Subspace(bank.DefaultParamspace), bank.DefaultCodespace, blacklistedAddrs)
|
||||
cdpKeeper := NewKeeper(mapp.Cdc, keyCDP, pk.Subspace(types.DefaultParamspace), priceFeedKeeper, bankKeeper)
|
||||
|
@ -28,7 +28,7 @@ func setUpMockAppWithoutGenesis() (*mock.App, Keeper, PricefeedKeeper) {
|
||||
keyCDP := sdk.NewKVStoreKey("cdp")
|
||||
keyPriceFeed := sdk.NewKVStoreKey(pricefeed.StoreKey)
|
||||
pk := mapp.ParamsKeeper
|
||||
priceFeedKeeper := pricefeed.NewKeeper(keyPriceFeed, mapp.Cdc, pk.Subspace(pricefeed.DefaultParamspace).WithKeyTable(pricefeed.ParamKeyTable()), pricefeed.DefaultCodespace)
|
||||
priceFeedKeeper := pricefeed.NewKeeper(keyPriceFeed, mapp.Cdc, pk.Subspace(pricefeed.DefaultParamspace), pricefeed.DefaultCodespace)
|
||||
blacklistedAddrs := make(map[string]bool)
|
||||
bankKeeper := bank.NewBaseKeeper(mapp.AccountKeeper, pk.Subspace(bank.DefaultParamspace), bank.DefaultCodespace, blacklistedAddrs)
|
||||
cdpKeeper := NewKeeper(mapp.Cdc, keyCDP, pk.Subspace(DefaultParamspace), priceFeedKeeper, bankKeeper)
|
||||
|
@ -1,6 +1,8 @@
|
||||
package types
|
||||
|
||||
import (
|
||||
"time"
|
||||
|
||||
sdk "github.com/cosmos/cosmos-sdk/types"
|
||||
pftypes "github.com/kava-labs/kava/x/pricefeed/types"
|
||||
)
|
||||
@ -14,9 +16,9 @@ type BankKeeper interface {
|
||||
|
||||
type PricefeedKeeper interface {
|
||||
GetCurrentPrice(sdk.Context, string) pftypes.CurrentPrice
|
||||
GetAssetParams(sdk.Context) pftypes.AssetParams
|
||||
GetParams(sdk.Context) pftypes.Params
|
||||
// These are used for testing TODO replace mockApp with keeper in tests to remove these
|
||||
SetAssetParams(sdk.Context, pftypes.AssetParams)
|
||||
SetPrice(sdk.Context, sdk.AccAddress, string, sdk.Dec, sdk.Int) (pftypes.PostedPrice, sdk.Error)
|
||||
SetCurrentPrices(sdk.Context) sdk.Error
|
||||
SetParams(sdk.Context, pftypes.Params)
|
||||
SetPrice(sdk.Context, sdk.AccAddress, string, sdk.Dec, time.Time) (pftypes.PostedPrice, sdk.Error)
|
||||
SetCurrentPrices(sdk.Context, string) sdk.Error
|
||||
}
|
||||
|
@ -1,15 +1,10 @@
|
||||
package types
|
||||
|
||||
import (
|
||||
sdk "github.com/cosmos/cosmos-sdk/types"
|
||||
)
|
||||
|
||||
// GenesisState is the state that must be provided at genesis.
|
||||
// TODO What is globaldebt and is is separate from the global debt limit in CdpParams
|
||||
|
||||
type GenesisState struct {
|
||||
Params CdpParams `json:"params" yaml:"params"`
|
||||
GlobalDebt sdk.Int `json:"global_debt" yaml:"global_debt"`
|
||||
Params Params `json:"params" yaml:"params"`
|
||||
CDPs CDPs `json:"cdps" yaml:"cdps"`
|
||||
// don't need to setup CollateralStates as they are created as needed
|
||||
}
|
||||
@ -19,7 +14,6 @@ type GenesisState struct {
|
||||
func DefaultGenesisState() GenesisState {
|
||||
return GenesisState{
|
||||
Params: DefaultParams(),
|
||||
GlobalDebt: sdk.ZeroInt(),
|
||||
CDPs: CDPs{},
|
||||
}
|
||||
}
|
||||
|
@ -4,135 +4,166 @@ import (
|
||||
"fmt"
|
||||
|
||||
sdk "github.com/cosmos/cosmos-sdk/types"
|
||||
"github.com/cosmos/cosmos-sdk/x/params/subspace"
|
||||
"github.com/cosmos/cosmos-sdk/x/params"
|
||||
)
|
||||
|
||||
/*
|
||||
How this uses the sdk params module:
|
||||
- Put all the params for this module in one struct `CDPModuleParams`
|
||||
- Store this in the keeper's paramSubspace under one key
|
||||
- Provide a function to load the param struct all at once `keeper.GetParams(ctx)`
|
||||
It's possible to set individual key value pairs within a paramSubspace, but reading and setting them is awkward (an empty variable needs to be created, then Get writes the value into it)
|
||||
This approach will be awkward if we ever need to write individual parameters (because they're stored all together). If this happens do as the sdk modules do - store parameters separately with custom get/set func for each.
|
||||
*/
|
||||
|
||||
// CdpParams governance parameters for cdp module
|
||||
type CdpParams struct {
|
||||
GlobalDebtLimit sdk.Int
|
||||
CollateralParams []CollateralParams
|
||||
StableDenoms []string
|
||||
}
|
||||
|
||||
// CollateralParams governance parameters for each collateral type within the cdp module
|
||||
type CollateralParams struct {
|
||||
Denom string // Coin name of collateral type
|
||||
LiquidationRatio sdk.Dec // The ratio (Collateral (priced in stable coin) / Debt) under which a CDP will be liquidated
|
||||
DebtLimit sdk.Int // Maximum amount of debt allowed to be drawn from this collateral type
|
||||
//DebtFloor sdk.Int // used to prevent dust
|
||||
}
|
||||
|
||||
// Parameter keys
|
||||
var (
|
||||
// ParamStoreKeyAuctionParams Param store key for auction params
|
||||
KeyGlobalDebtLimit = []byte("GlobalDebtLimit")
|
||||
KeyCollateralParams = []byte("CollateralParams")
|
||||
KeyStableDenoms = []byte("StableDenoms")
|
||||
KeyDebtParams = []byte("DebtParams")
|
||||
DefaultGlobalDebt = sdk.Coins{}
|
||||
DefaultCircuitBreaker = false
|
||||
DefaultCollateralParams = CollateralParams{}
|
||||
DefaultDebtParams = DebtParams{}
|
||||
)
|
||||
|
||||
// Params governance parameters for cdp module
|
||||
type Params struct {
|
||||
CollateralParams CollateralParams `json:"collateral_params" yaml:"collateral_params"`
|
||||
DebtParams DebtParams `json:"debt_params" yaml:"debt_params"`
|
||||
GlobalDebtLimit sdk.Coins `json:"global_debt_limit" yaml:"global_debt_limit"`
|
||||
CircuitBreaker bool `json:"circuit_breaker" yaml:"circuit_breaker"`
|
||||
}
|
||||
|
||||
// String implements fmt.Stringer
|
||||
func (p Params) String() string {
|
||||
return fmt.Sprintf(`Params:
|
||||
Global Debt Limit: %s
|
||||
Collateral Params: %s
|
||||
Debt Params: %s
|
||||
Circuit Breaker: %t`,
|
||||
p.GlobalDebtLimit, p.CollateralParams, p.DebtParams, p.CircuitBreaker,
|
||||
)
|
||||
}
|
||||
|
||||
// NewParams returns a new params object
|
||||
func NewParams(debtLimit sdk.Coins, collateralParams CollateralParams, debtParams DebtParams, breaker bool) Params {
|
||||
return Params{
|
||||
GlobalDebtLimit: debtLimit,
|
||||
CollateralParams: collateralParams,
|
||||
DebtParams: debtParams,
|
||||
CircuitBreaker: breaker,
|
||||
}
|
||||
}
|
||||
|
||||
// DefaultParams returns default params for cdp module
|
||||
func DefaultParams() Params {
|
||||
return NewParams(DefaultGlobalDebt, DefaultCollateralParams, DefaultDebtParams, DefaultCircuitBreaker)
|
||||
}
|
||||
|
||||
// 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.Coins `json:"debt_limit" yaml:"debt_limit"` // Maximum amount of debt allowed to be drawn from this collateral type
|
||||
//DebtFloor sdk.Int // used to prevent dust
|
||||
}
|
||||
|
||||
// String implements fmt.Stringer
|
||||
func (cp CollateralParam) String() string {
|
||||
return fmt.Sprintf(`Collateral:
|
||||
Denom: %s
|
||||
LiquidationRatio: %s
|
||||
DebtLimit: %s`, cp.Denom, cp.LiquidationRatio, cp.DebtLimit)
|
||||
}
|
||||
|
||||
// CollateralParams array of CollateralParam
|
||||
type CollateralParams []CollateralParam
|
||||
|
||||
// String implements fmt.Stringer
|
||||
func (cps CollateralParams) String() string {
|
||||
out := "Collateral Params\n"
|
||||
for _, cp := range cps {
|
||||
out += fmt.Sprintf("%s\n", cp)
|
||||
}
|
||||
return out
|
||||
}
|
||||
|
||||
// DebtParam governance params for debt assets
|
||||
type DebtParam struct {
|
||||
Denom string `json:"denom" yaml:"denom"`
|
||||
ReferenceAsset string `json:"reference_asset" yaml:"reference_asset"`
|
||||
DebtLimit sdk.Coins `json:"debt_limit" yaml:"debt_limit"`
|
||||
}
|
||||
|
||||
func (dp DebtParam) String() string {
|
||||
return fmt.Sprintf(`Debt:
|
||||
Denom: %s
|
||||
ReferenceAsset: %s
|
||||
DebtLimit: %s`, dp.Denom, dp.ReferenceAsset, dp.DebtLimit)
|
||||
}
|
||||
|
||||
// DebtParams array of DebtParam
|
||||
type DebtParams []DebtParam
|
||||
|
||||
// String implements fmt.Stringer
|
||||
func (dps DebtParams) String() string {
|
||||
out := "Debt Params\n"
|
||||
for _, dp := range dps {
|
||||
out += fmt.Sprintf("%s\n", dp)
|
||||
}
|
||||
return out
|
||||
}
|
||||
|
||||
// ParamKeyTable Key declaration for parameters
|
||||
func ParamKeyTable() subspace.KeyTable {
|
||||
return subspace.NewKeyTable().RegisterParamSet(&CdpParams{})
|
||||
func ParamKeyTable() params.KeyTable {
|
||||
return params.NewKeyTable().RegisterParamSet(&Params{})
|
||||
}
|
||||
|
||||
// ParamSetPairs implements the ParamSet interface and returns all the key/value pairs
|
||||
// pairs of auth module's parameters.
|
||||
// nolint
|
||||
func (p *CdpParams) ParamSetPairs() subspace.ParamSetPairs {
|
||||
return subspace.ParamSetPairs{
|
||||
func (p *Params) ParamSetPairs() params.ParamSetPairs {
|
||||
return params.ParamSetPairs{
|
||||
{KeyGlobalDebtLimit, &p.GlobalDebtLimit},
|
||||
{KeyCollateralParams, &p.CollateralParams},
|
||||
{KeyStableDenoms, &p.StableDenoms},
|
||||
{KeyDebtParams, &p.DebtParams},
|
||||
}
|
||||
}
|
||||
|
||||
// String implements fmt.Stringer
|
||||
func (p CdpParams) String() string {
|
||||
out := fmt.Sprintf(`Params:
|
||||
Global Debt Limit: %s
|
||||
Collateral Params:`,
|
||||
p.GlobalDebtLimit,
|
||||
)
|
||||
for _, cp := range p.CollateralParams {
|
||||
out += fmt.Sprintf(`
|
||||
%s
|
||||
Liquidation Ratio: %s
|
||||
Debt Limit: %s`,
|
||||
cp.Denom,
|
||||
cp.LiquidationRatio,
|
||||
cp.DebtLimit,
|
||||
)
|
||||
}
|
||||
return out
|
||||
}
|
||||
|
||||
// GetCollateralParams returns params for a specific collateral denom
|
||||
func (p CdpParams) GetCollateralParams(collateralDenom string) CollateralParams {
|
||||
// search for matching denom, return
|
||||
for _, cp := range p.CollateralParams {
|
||||
if cp.Denom == collateralDenom {
|
||||
return cp
|
||||
}
|
||||
}
|
||||
// panic if not found, to be safe
|
||||
panic("collateral params not found in module params")
|
||||
}
|
||||
|
||||
// IsCollateralPresent returns true if the denom is among the collaterals in cdp module
|
||||
func (p CdpParams) IsCollateralPresent(collateralDenom string) bool {
|
||||
// search for matching denom, return
|
||||
for _, cp := range p.CollateralParams {
|
||||
if cp.Denom == collateralDenom {
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
// Validate checks that the parameters have valid values.
|
||||
func (p CdpParams) Validate() error {
|
||||
func (p Params) Validate() error {
|
||||
debtDenoms := make(map[string]int)
|
||||
debtParamsDebtLimit := sdk.Coins{}
|
||||
for _, dp := range p.DebtParams {
|
||||
_, found := debtDenoms[dp.Denom]
|
||||
if found {
|
||||
return fmt.Errorf("duplicate debt denom: %s", dp.Denom)
|
||||
}
|
||||
debtDenoms[dp.Denom] = 1
|
||||
if dp.DebtLimit.IsAnyNegative() {
|
||||
return fmt.Errorf("debt limit for all debt tokens should be positive, is %s for %s", dp.DebtLimit, dp.Denom)
|
||||
}
|
||||
debtParamsDebtLimit = debtParamsDebtLimit.Add(dp.DebtLimit)
|
||||
}
|
||||
if debtParamsDebtLimit.IsAnyGT(p.GlobalDebtLimit) {
|
||||
fmt.Errorf("debt limit exceeds global debt limit:\n\tglobal debt limit: %s\n\tdebt limits: %s",
|
||||
p.GlobalDebtLimit, debtParamsDebtLimit)
|
||||
}
|
||||
|
||||
collateralDupMap := make(map[string]int)
|
||||
denomDupMap := make(map[string]int)
|
||||
for _, collateral := range p.CollateralParams {
|
||||
_, found := collateralDupMap[collateral.Denom]
|
||||
collateralParamsDebtLimit := sdk.Coins{}
|
||||
for _, cp := range p.CollateralParams {
|
||||
_, found := collateralDupMap[cp.Denom]
|
||||
if found {
|
||||
return fmt.Errorf("duplicate denom: %s", collateral.Denom)
|
||||
return fmt.Errorf("duplicate collateral denom: %s", cp.Denom)
|
||||
}
|
||||
collateralDupMap[collateral.Denom] = 1
|
||||
collateralDupMap[cp.Denom] = 1
|
||||
|
||||
if collateral.DebtLimit.IsNegative() {
|
||||
return fmt.Errorf("debt limit should be positive, is %s for %s", collateral.DebtLimit, collateral.Denom)
|
||||
if cp.DebtLimit.IsAnyNegative() {
|
||||
return fmt.Errorf("debt limit for all collaterals should be positive, is %s for %s", cp.DebtLimit, cp.Denom)
|
||||
}
|
||||
collateralParamsDebtLimit = collateralParamsDebtLimit.Add(cp.DebtLimit)
|
||||
}
|
||||
if collateralParamsDebtLimit.IsAnyGT(p.GlobalDebtLimit) {
|
||||
fmt.Errorf("collateral debt limit exceeds global debt limit:\n\tglobal debt limit: %s\n\tcollateral debt limits: %s",
|
||||
p.GlobalDebtLimit, collateralParamsDebtLimit)
|
||||
}
|
||||
|
||||
// TODO do we want to enforce overcollateralization at this level? -- probably not, as it's technically a governance thing (kevin)
|
||||
}
|
||||
if p.GlobalDebtLimit.IsNegative() {
|
||||
return fmt.Errorf("global debt limit should be positive, is %s", p.GlobalDebtLimit)
|
||||
}
|
||||
|
||||
for _, denom := range p.StableDenoms {
|
||||
_, found := denomDupMap[denom]
|
||||
if found {
|
||||
return fmt.Errorf("duplicate stable denom: %s", denom)
|
||||
}
|
||||
denomDupMap[denom] = 1
|
||||
if p.GlobalDebtLimit.IsAnyNegative() {
|
||||
return fmt.Errorf("global debt limit should be positive for all debt tokens, is %s", p.GlobalDebtLimit)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func DefaultParams() CdpParams {
|
||||
return CdpParams{
|
||||
GlobalDebtLimit: sdk.NewInt(0),
|
||||
CollateralParams: []CollateralParams{},
|
||||
StableDenoms: []string{"usdx"},
|
||||
}
|
||||
}
|
||||
|
@ -3,6 +3,7 @@ package types
|
||||
import (
|
||||
"fmt"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
sdk "github.com/cosmos/cosmos-sdk/types"
|
||||
)
|
||||
@ -15,27 +16,41 @@ type CDP struct {
|
||||
//ID []byte // removing IDs for now to make things simpler
|
||||
Owner sdk.AccAddress `json:"owner" yaml:"owner"` // Account that authorizes changes to the CDP
|
||||
CollateralDenom string `json:"collateral_denom" yaml:"collateral_denom"` // Type of collateral stored in this CDP
|
||||
CollateralAmount sdk.Int `json:"collateral_amount" yaml:"collateral_amount"` // Amount of collateral stored in this CDP
|
||||
Debt sdk.Int `json:"debt" yaml:"debt"` // Amount of stable coin drawn from this CDP
|
||||
CollateralAmount sdk.Coins `json:"collateral_amount" yaml:"collateral_amount"` // Amount of collateral stored in this CDP
|
||||
Debt sdk.Coins `json:"debt" yaml:"debt"`
|
||||
AccumulatedFees sdk.Coins `json:"accumulated_fees" yaml:"accumulated_fees"`
|
||||
FeesUpdated time.Time `json:"fees_updated" yaml:"fees_updated"` // Amount of stable coin drawn from this CDP
|
||||
}
|
||||
|
||||
// IsUnderCollateralized checks if cdp is below the liquidation ratio
|
||||
func (cdp CDP) IsUnderCollateralized(price sdk.Dec, liquidationRatio sdk.Dec) bool {
|
||||
collateralValue := sdk.NewDecFromInt(cdp.CollateralAmount).Mul(price)
|
||||
minCollateralValue := liquidationRatio.Mul(sdk.NewDecFromInt(cdp.Debt))
|
||||
collateralValue := sdk.NewDecFromInt(cdp.CollateralAmount.AmountOf(cdp.CollateralDenom)).Mul(price)
|
||||
minCollateralValue := sdk.NewDec(0)
|
||||
for _, c := range cdp.Debt {
|
||||
minCollateralValue = minCollateralValue.Add(liquidationRatio.Mul(c.Amount.ToDec()))
|
||||
}
|
||||
return collateralValue.LT(minCollateralValue) // TODO LT or LTE?
|
||||
}
|
||||
|
||||
// String implements fmt.stringer
|
||||
func (cdp CDP) String() string {
|
||||
return strings.TrimSpace(fmt.Sprintf(`CDP:
|
||||
Owner: %s
|
||||
Collateral Type: %s
|
||||
Collateral: %s
|
||||
Debt: %s`,
|
||||
Debt: %s
|
||||
Fees: %s
|
||||
Fees Last Updated: %s`,
|
||||
cdp.Owner,
|
||||
sdk.NewCoin(cdp.CollateralDenom, cdp.CollateralAmount),
|
||||
sdk.NewCoin("usdx", cdp.Debt),
|
||||
cdp.CollateralDenom,
|
||||
cdp.CollateralAmount,
|
||||
cdp.Debt,
|
||||
cdp.AccumulatedFees,
|
||||
cdp.FeesUpdated,
|
||||
))
|
||||
}
|
||||
|
||||
// CDPs array of CDP
|
||||
type CDPs []CDP
|
||||
|
||||
// String implements stringer
|
||||
@ -52,22 +67,23 @@ type ByCollateralRatio CDPs
|
||||
|
||||
func (cdps ByCollateralRatio) Len() int { return len(cdps) }
|
||||
func (cdps ByCollateralRatio) Swap(i, j int) { cdps[i], cdps[j] = cdps[j], cdps[i] }
|
||||
func (cdps ByCollateralRatio) Less(i, j int) bool {
|
||||
// Sort by "collateral ratio" ie collateralAmount/Debt
|
||||
// The comparison is: collat_i/debt_i < collat_j/debt_j
|
||||
// But to avoid division this can be rearranged to: collat_i*debt_j < collat_j*debt_i
|
||||
// Provided the values are positive, so check for positive values.
|
||||
if cdps[i].CollateralAmount.IsNegative() ||
|
||||
cdps[i].Debt.IsNegative() ||
|
||||
cdps[j].CollateralAmount.IsNegative() ||
|
||||
cdps[j].Debt.IsNegative() {
|
||||
panic("negative collateral and debt not supported in CDPs")
|
||||
}
|
||||
// TODO overflows could cause panics
|
||||
left := cdps[i].CollateralAmount.Mul(cdps[j].Debt)
|
||||
right := cdps[j].CollateralAmount.Mul(cdps[i].Debt)
|
||||
return left.LT(right)
|
||||
}
|
||||
|
||||
// func (cdps ByCollateralRatio) Less(i, j int) bool {
|
||||
// // Sort by "collateral ratio" ie collateralAmount/Debt
|
||||
// // The comparison is: collat_i/debt_i < collat_j/debt_j
|
||||
// // But to avoid division this can be rearranged to: collat_i*debt_j < collat_j*debt_i
|
||||
// // Provided the values are positive, so check for positive values.
|
||||
// if cdps[i].CollateralAmount.IsNegative() ||
|
||||
// cdps[i].Debt.IsNegative() ||
|
||||
// cdps[j].CollateralAmount.IsNegative() ||
|
||||
// cdps[j].Debt.IsNegative() {
|
||||
// panic("negative collateral and debt not supported in CDPs")
|
||||
// }
|
||||
// // TODO overflows could cause panics
|
||||
// left := cdps[i].CollateralAmount.Mul(cdps[j].Debt)
|
||||
// right := cdps[j].CollateralAmount.Mul(cdps[i].Debt)
|
||||
// return left.LT(right)
|
||||
// }
|
||||
|
||||
// CollateralState stores global information tied to a particular collateral type.
|
||||
type CollateralState struct {
|
||||
|
@ -5,7 +5,7 @@ import (
|
||||
"strings"
|
||||
|
||||
sdk "github.com/cosmos/cosmos-sdk/types"
|
||||
params "github.com/cosmos/cosmos-sdk/x/params/subspace"
|
||||
"github.com/cosmos/cosmos-sdk/x/params"
|
||||
)
|
||||
|
||||
var (
|
||||
|
Loading…
Reference in New Issue
Block a user