mirror of
https://github.com/0glabs/0g-chain.git
synced 2024-12-26 00:05:18 +00:00
Fix: Check debt limit when adding cdp (#433)
* fix: check debt limit when opening cdp * fix: increase debt limit for querier tests
This commit is contained in:
parent
45e40fe357
commit
55b73e36ee
@ -27,6 +27,11 @@ func (k Keeper) AddCdp(ctx sdk.Context, owner sdk.AccAddress, collateral sdk.Coi
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
err = k.ValidateDebtLimit(ctx, collateral[0].Denom, principal)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
err = k.ValidateCollateralizationRatio(ctx, collateral, principal, sdk.NewCoins())
|
||||
if err != nil {
|
||||
return err
|
||||
@ -371,10 +376,18 @@ func (k Keeper) ValidatePrincipalDraw(ctx sdk.Context, principal sdk.Coins) sdk.
|
||||
return nil
|
||||
}
|
||||
|
||||
// ValidateDebtLimit validates that the input debt amount does not exceed the global debt limit
|
||||
// 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.Coins) sdk.Error {
|
||||
cp, found := k.GetCollateral(ctx, collateralDenom)
|
||||
if !found {
|
||||
return types.ErrCollateralNotSupported(k.codespace, collateralDenom)
|
||||
}
|
||||
for _, dc := range principal {
|
||||
totalPrincipal := k.GetTotalPrincipal(ctx, collateralDenom, dc.Denom).Add(dc.Amount)
|
||||
collateralLimit := cp.DebtLimit.AmountOf(dc.Denom)
|
||||
if totalPrincipal.GT(collateralLimit) {
|
||||
return types.ErrExceedsDebtLimit(k.codespace, sdk.NewCoins(sdk.NewCoin(dc.Denom, totalPrincipal)), sdk.NewCoins(sdk.NewCoin(dc.Denom, collateralLimit)))
|
||||
}
|
||||
globalLimit := k.GetParams(ctx).GlobalDebtLimit.AmountOf(dc.Denom)
|
||||
if totalPrincipal.GT(globalLimit) {
|
||||
return types.ErrExceedsDebtLimit(k.codespace, sdk.NewCoins(sdk.NewCoin(dc.Denom, totalPrincipal)), sdk.NewCoins(sdk.NewCoin(dc.Denom, globalLimit)))
|
||||
|
@ -38,7 +38,7 @@ func (suite *CdpTestSuite) SetupTest() {
|
||||
}
|
||||
|
||||
func (suite *CdpTestSuite) TestAddCdp() {
|
||||
_, addrs := app.GeneratePrivKeyAddressPairs(1)
|
||||
_, addrs := app.GeneratePrivKeyAddressPairs(2)
|
||||
ak := suite.app.GetAccountKeeper()
|
||||
acc := ak.NewAccountWithAddress(suite.ctx, addrs[0])
|
||||
acc.SetCoins(cs(c("xrp", 200000000), c("btc", 500000000)))
|
||||
@ -46,14 +46,21 @@ func (suite *CdpTestSuite) TestAddCdp() {
|
||||
err := suite.keeper.AddCdp(suite.ctx, addrs[0], cs(c("xrp", 200000000)), cs(c("usdx", 26000000)))
|
||||
suite.Equal(types.CodeInvalidCollateralRatio, err.Result().Code)
|
||||
err = suite.keeper.AddCdp(suite.ctx, addrs[0], cs(c("xrp", 500000000)), cs(c("usdx", 26000000)))
|
||||
suite.Error(err)
|
||||
suite.Error(err) // insufficient balance
|
||||
err = suite.keeper.AddCdp(suite.ctx, addrs[0], cs(c("xrp", 200000000)), cs(c("xusd", 10000000)))
|
||||
suite.Equal(types.CodeDebtNotSupported, err.Result().Code)
|
||||
|
||||
acc2 := ak.NewAccountWithAddress(suite.ctx, addrs[1])
|
||||
acc2.SetCoins(cs(c("btc", 500000000000)))
|
||||
ak.SetAccount(suite.ctx, acc2)
|
||||
err = suite.keeper.AddCdp(suite.ctx, addrs[1], cs(c("btc", 500000000000)), cs(c("usdx", 500000000001)))
|
||||
suite.Equal(types.CodeExceedsDebtLimit, err.Result().Code)
|
||||
|
||||
ctx := suite.ctx.WithBlockTime(suite.ctx.BlockTime().Add(time.Hour * 2))
|
||||
pk := suite.app.GetPriceFeedKeeper()
|
||||
_ = pk.SetCurrentPrices(ctx, "xrp:usd")
|
||||
err = suite.keeper.AddCdp(ctx, addrs[0], cs(c("xrp", 100000000)), cs(c("usdx", 10000000)))
|
||||
suite.Error(err)
|
||||
suite.Error(err) // no prices in pricefeed
|
||||
|
||||
_ = pk.SetCurrentPrices(suite.ctx, "xrp:usd")
|
||||
err = suite.keeper.AddCdp(suite.ctx, addrs[0], cs(c("xrp", 100000000)), cs(c("usdx", 10000000)))
|
||||
|
@ -159,6 +159,64 @@ func NewCDPGenStateMulti() app.GenesisState {
|
||||
return app.GenesisState{cdp.ModuleName: cdp.ModuleCdc.MustMarshalJSON(cdpGenesis)}
|
||||
}
|
||||
|
||||
func NewCDPGenStateHighDebtLimit() app.GenesisState {
|
||||
cdpGenesis := cdp.GenesisState{
|
||||
Params: cdp.Params{
|
||||
GlobalDebtLimit: sdk.NewCoins(sdk.NewInt64Coin("usdx", 100000000000000), sdk.NewInt64Coin("susd", 100000000000000)),
|
||||
SurplusAuctionThreshold: cdp.DefaultSurplusThreshold,
|
||||
DebtAuctionThreshold: cdp.DefaultDebtThreshold,
|
||||
SavingsDistributionFrequency: cdp.DefaultSavingsDistributionFrequency,
|
||||
CollateralParams: cdp.CollateralParams{
|
||||
{
|
||||
Denom: "xrp",
|
||||
LiquidationRatio: sdk.MustNewDecFromStr("2.0"),
|
||||
DebtLimit: sdk.NewCoins(sdk.NewInt64Coin("usdx", 50000000000000), sdk.NewInt64Coin("susd", 50000000000000)),
|
||||
StabilityFee: sdk.MustNewDecFromStr("1.000000001547125958"), // %5 apr
|
||||
LiquidationPenalty: d("0.05"),
|
||||
AuctionSize: i(7000000000),
|
||||
Prefix: 0x20,
|
||||
MarketID: "xrp:usd",
|
||||
ConversionFactor: i(6),
|
||||
},
|
||||
{
|
||||
Denom: "btc",
|
||||
LiquidationRatio: sdk.MustNewDecFromStr("1.5"),
|
||||
DebtLimit: sdk.NewCoins(sdk.NewInt64Coin("usdx", 50000000000000), sdk.NewInt64Coin("susd", 50000000000000)),
|
||||
StabilityFee: sdk.MustNewDecFromStr("1.000000000782997609"), // %2.5 apr
|
||||
LiquidationPenalty: d("0.025"),
|
||||
AuctionSize: i(10000000),
|
||||
Prefix: 0x21,
|
||||
MarketID: "btc:usd",
|
||||
ConversionFactor: i(8),
|
||||
},
|
||||
},
|
||||
DebtParams: cdp.DebtParams{
|
||||
{
|
||||
Denom: "usdx",
|
||||
ReferenceAsset: "usd",
|
||||
ConversionFactor: i(6),
|
||||
DebtFloor: i(10000000),
|
||||
SavingsRate: d("0.95"),
|
||||
},
|
||||
{
|
||||
Denom: "susd",
|
||||
ReferenceAsset: "usd",
|
||||
ConversionFactor: i(6),
|
||||
DebtFloor: i(10000000),
|
||||
SavingsRate: d("0.95"),
|
||||
},
|
||||
},
|
||||
},
|
||||
StartingCdpID: cdp.DefaultCdpStartingID,
|
||||
DebtDenom: cdp.DefaultDebtDenom,
|
||||
GovDenom: cdp.DefaultGovDenom,
|
||||
CDPs: cdp.CDPs{},
|
||||
PreviousBlockTime: cdp.DefaultPreviousBlockTime,
|
||||
PreviousDistributionTime: cdp.DefaultPreviousDistributionTime,
|
||||
}
|
||||
return app.GenesisState{cdp.ModuleName: cdp.ModuleCdc.MustMarshalJSON(cdpGenesis)}
|
||||
}
|
||||
|
||||
func cdps() (cdps cdp.CDPs) {
|
||||
_, addrs := app.GeneratePrivKeyAddressPairs(3)
|
||||
c1 := cdp.NewCDP(uint64(1), addrs[0], sdk.NewCoins(sdk.NewCoin("xrp", sdk.NewInt(10000000))), sdk.NewCoins(sdk.NewCoin("usdx", sdk.NewInt(8000000))), tmtime.Canonical(time.Now()))
|
||||
|
@ -53,7 +53,7 @@ func (suite *QuerierTestSuite) SetupTest() {
|
||||
tApp.InitializeFromGenesisStates(
|
||||
authGS,
|
||||
NewPricefeedGenStateMulti(),
|
||||
NewCDPGenStateMulti(),
|
||||
NewCDPGenStateHighDebtLimit(),
|
||||
)
|
||||
|
||||
suite.ctx = ctx
|
||||
@ -93,11 +93,13 @@ func (suite *QuerierTestSuite) SetupTest() {
|
||||
amount = simulation.RandIntBetween(rand.New(rand.NewSource(int64(j))), 500000000, 5000000000)
|
||||
debt = simulation.RandIntBetween(rand.New(rand.NewSource(int64(j))), 1000000000, 25000000000)
|
||||
}
|
||||
suite.Nil(suite.keeper.AddCdp(suite.ctx, addrs[j], cs(c(collateral, int64(amount))), cs(c("usdx", int64(debt)))))
|
||||
err = suite.keeper.AddCdp(suite.ctx, addrs[j], cs(c(collateral, int64(amount))), cs(c("usdx", int64(debt))))
|
||||
suite.NoError(err)
|
||||
c, f := suite.keeper.GetCDP(suite.ctx, collateral, uint64(j+1))
|
||||
suite.True(f)
|
||||
cdps[j] = c
|
||||
aCDP, _ := suite.keeper.LoadAugmentedCDP(suite.ctx, c)
|
||||
aCDP, err := suite.keeper.LoadAugmentedCDP(suite.ctx, c)
|
||||
suite.NoError(err)
|
||||
augmentedCDPs[j] = aCDP
|
||||
}
|
||||
|
||||
@ -250,7 +252,7 @@ func (suite *QuerierTestSuite) TestQueryParams() {
|
||||
var p types.Params
|
||||
suite.Nil(types.ModuleCdc.UnmarshalJSON(bz, &p))
|
||||
|
||||
cdpGS := NewCDPGenStateMulti()
|
||||
cdpGS := NewCDPGenStateHighDebtLimit()
|
||||
gs := types.GenesisState{}
|
||||
types.ModuleCdc.UnmarshalJSON(cdpGS["cdp"], &gs)
|
||||
suite.Equal(gs.Params, p)
|
||||
|
@ -79,7 +79,7 @@ func randomCdpGenState(selection int) types.GenesisState {
|
||||
DebtLimit: sdk.NewCoins(sdk.NewInt64Coin("usdx", 20000000000000)),
|
||||
StabilityFee: sdk.MustNewDecFromStr("1.000000004431822130"),
|
||||
LiquidationPenalty: sdk.MustNewDecFromStr("0.075"),
|
||||
AuctionSize: sdk.NewInt(10000000000),
|
||||
AuctionSize: sdk.NewInt(100000000000),
|
||||
Prefix: 0x20,
|
||||
MarketID: "xrp:usd",
|
||||
ConversionFactor: sdk.NewInt(6),
|
||||
@ -90,7 +90,7 @@ func randomCdpGenState(selection int) types.GenesisState {
|
||||
DebtLimit: sdk.NewCoins(sdk.NewInt64Coin("usdx", 50000000000000)),
|
||||
StabilityFee: sdk.MustNewDecFromStr("1.000000000782997609"),
|
||||
LiquidationPenalty: sdk.MustNewDecFromStr("0.05"),
|
||||
AuctionSize: sdk.NewInt(50000000),
|
||||
AuctionSize: sdk.NewInt(1000000000),
|
||||
Prefix: 0x21,
|
||||
MarketID: "btc:usd",
|
||||
ConversionFactor: sdk.NewInt(8),
|
||||
@ -101,7 +101,7 @@ func randomCdpGenState(selection int) types.GenesisState {
|
||||
DebtLimit: sdk.NewCoins(sdk.NewInt64Coin("usdx", 30000000000000)),
|
||||
StabilityFee: sdk.MustNewDecFromStr("1.000000002293273137"),
|
||||
LiquidationPenalty: sdk.MustNewDecFromStr("0.15"),
|
||||
AuctionSize: sdk.NewInt(10000000000),
|
||||
AuctionSize: sdk.NewInt(1000000000000),
|
||||
Prefix: 0x22,
|
||||
MarketID: "bnb:usd",
|
||||
ConversionFactor: sdk.NewInt(8),
|
||||
|
@ -52,9 +52,9 @@ func SimulateMsgCdp(ak auth.AccountKeeper, k cdp.Keeper, pfk pricefeed.Keeper) s
|
||||
minCollateralDeposit = ShiftDec(minCollateralDeposit, randCollateralParam.ConversionFactor)
|
||||
// convert to integer and always round up
|
||||
minCollateralDepositRounded := minCollateralDeposit.TruncateInt().Add(sdk.OneInt())
|
||||
// if the account has less than the min deposit, return
|
||||
if coins.AmountOf(randCollateralParam.Denom).LT(minCollateralDepositRounded) {
|
||||
return simulation.NoOpMsg(cdp.ModuleName), nil, nil
|
||||
// account doesn't have enough funds to open a cdp for the min debt amount
|
||||
return simulation.NewOperationMsgBasic(cdp.ModuleName, "no-operation", "insufficient funds to open cdp", false, nil), nil, nil
|
||||
}
|
||||
// set the max collateral deposit to the amount of coins in the account
|
||||
maxCollateralDeposit := coins.AmountOf(randCollateralParam.Denom)
|
||||
@ -65,6 +65,14 @@ func SimulateMsgCdp(ak auth.AccountKeeper, k cdp.Keeper, pfk pricefeed.Keeper) s
|
||||
collateralDepositValue := ShiftDec(sdk.NewDecFromInt(collateralDeposit), randCollateralParam.ConversionFactor.Neg()).Mul(priceShifted)
|
||||
// calculate the max amount of debt that could be drawn for the chosen deposit
|
||||
maxDebtDraw := collateralDepositValue.Quo(randCollateralParam.LiquidationRatio).TruncateInt()
|
||||
// check that the debt limit hasn't been reached
|
||||
availableAssetDebt := randCollateralParam.DebtLimit.AmountOf(randDebtParam.Denom).Sub(k.GetTotalPrincipal(ctx, randCollateralParam.Denom, randDebtParam.Denom))
|
||||
if availableAssetDebt.LTE(randDebtParam.DebtFloor) {
|
||||
// debt limit has been reached
|
||||
return simulation.NewOperationMsgBasic(cdp.ModuleName, "no-operation", "debt limit reached, cannot open cdp", false, nil), nil, nil
|
||||
}
|
||||
// ensure that the debt draw does not exceed the debt limit
|
||||
maxDebtDraw = sdk.MinInt(maxDebtDraw, availableAssetDebt)
|
||||
// randomly select a debt draw amount
|
||||
debtDraw := sdk.NewInt(int64(simulation.RandIntBetween(r, int(randDebtParam.DebtFloor.Int64()), int(maxDebtDraw.Int64()))))
|
||||
msg := cdp.NewMsgCreateCDP(acc.GetAddress(), sdk.NewCoins(sdk.NewCoin(randCollateralParam.Denom, collateralDeposit)), sdk.NewCoins(sdk.NewCoin(randDebtParam.Denom, debtDraw)))
|
||||
@ -114,10 +122,23 @@ func SimulateMsgCdp(ak auth.AccountKeeper, k cdp.Keeper, pfk pricefeed.Keeper) s
|
||||
if shouldDraw(r) {
|
||||
collateralShifted := ShiftDec(sdk.NewDecFromInt(existingCDP.Collateral.AmountOf(randCollateralParam.Denom)), randCollateralParam.ConversionFactor.Neg())
|
||||
collateralValue := collateralShifted.Mul(priceShifted)
|
||||
// given the current collateral value, calculate how much debt we could add while maintaining a valid liquidation ratio
|
||||
debt := (existingCDP.Principal.Add(existingCDP.AccumulatedFees)).AmountOf(randDebtParam.Denom)
|
||||
maxTotalDebt := collateralValue.Quo(randCollateralParam.LiquidationRatio)
|
||||
maxDebt := maxTotalDebt.Sub(sdk.NewDecFromInt(debt)).TruncateInt().Sub(sdk.OneInt())
|
||||
randDrawAmount := sdk.NewInt(int64(simulation.RandIntBetween(r, 1, int(maxDebt.Int64()))))
|
||||
maxDebt := maxTotalDebt.Sub(sdk.NewDecFromInt(debt)).TruncateInt()
|
||||
if maxDebt.LTE(sdk.OneInt()) {
|
||||
// debt in cdp is maxed out
|
||||
return simulation.NewOperationMsgBasic(cdp.ModuleName, "no-operation", "cdp debt maxed out, cannot draw more debt", false, nil), nil, nil
|
||||
}
|
||||
// check if the debt limit has been reached
|
||||
availableAssetDebt := randCollateralParam.DebtLimit.AmountOf(randDebtParam.Denom).Sub(k.GetTotalPrincipal(ctx, randCollateralParam.Denom, randDebtParam.Denom))
|
||||
if availableAssetDebt.LTE(sdk.OneInt()) {
|
||||
// debt limit has been reached
|
||||
return simulation.NewOperationMsgBasic(cdp.ModuleName, "no-operation", "debt limit reached, cannot draw more debt", false, nil), nil, nil
|
||||
}
|
||||
maxDraw := sdk.MinInt(maxDebt, availableAssetDebt)
|
||||
|
||||
randDrawAmount := sdk.NewInt(int64(simulation.RandIntBetween(r, 1, int(maxDraw.Int64()))))
|
||||
msg := cdp.NewMsgDrawDebt(acc.GetAddress(), randCollateralParam.Denom, sdk.NewCoins(sdk.NewCoin(randDebtParam.Denom, randDrawAmount)))
|
||||
err := msg.ValidateBasic()
|
||||
if err != nil {
|
||||
|
Loading…
Reference in New Issue
Block a user