mirror of
https://github.com/0glabs/0g-chain.git
synced 2024-12-25 15:55:18 +00:00
Prevent panic-causing param values (#875)
* prevent cdp liquidation ratio being 0.0 * fix linter warning * prevent hard conversin factor being < 1 * add liquidation tests for different keeper rewards
This commit is contained in:
parent
2611d48b77
commit
20b3fa53e3
@ -615,7 +615,7 @@ type pricefeedType string
|
|||||||
|
|
||||||
const (
|
const (
|
||||||
spot pricefeedType = "spot"
|
spot pricefeedType = "spot"
|
||||||
liquidation = "liquidation"
|
liquidation pricefeedType = "liquidation"
|
||||||
)
|
)
|
||||||
|
|
||||||
func (pft pricefeedType) IsValid() error {
|
func (pft pricefeedType) IsValid() error {
|
||||||
|
@ -362,6 +362,10 @@ func validateCollateralParams(i interface{}) error {
|
|||||||
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)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if cp.LiquidationRatio.IsNil() || !cp.LiquidationRatio.IsPositive() {
|
||||||
|
return fmt.Errorf("liquidation ratio must be > 0")
|
||||||
|
}
|
||||||
|
|
||||||
if cp.LiquidationPenalty.LT(sdk.ZeroDec()) || cp.LiquidationPenalty.GT(sdk.OneDec()) {
|
if cp.LiquidationPenalty.LT(sdk.ZeroDec()) || cp.LiquidationPenalty.GT(sdk.OneDec()) {
|
||||||
return fmt.Errorf("liquidation penalty should be between 0 and 1, is %s for %s", cp.LiquidationPenalty, cp.Denom)
|
return fmt.Errorf("liquidation penalty should be between 0 and 1, is %s for %s", cp.LiquidationPenalty, cp.Denom)
|
||||||
}
|
}
|
||||||
|
@ -1,7 +1,6 @@
|
|||||||
package types_test
|
package types_test
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"strings"
|
|
||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
"github.com/stretchr/testify/suite"
|
"github.com/stretchr/testify/suite"
|
||||||
@ -715,6 +714,39 @@ func (suite *ParamsTestSuite) TestParamValidation() {
|
|||||||
contains: "stability fee must be ≥ 1.0",
|
contains: "stability fee must be ≥ 1.0",
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
name: "invalid collateral params zero liquidation ratio",
|
||||||
|
args: args{
|
||||||
|
globalDebtLimit: sdk.NewInt64Coin("usdx", 2000000000000),
|
||||||
|
collateralParams: types.CollateralParams{
|
||||||
|
{
|
||||||
|
Denom: "bnb",
|
||||||
|
Type: "bnb-a",
|
||||||
|
LiquidationRatio: sdk.MustNewDecFromStr("0.0"),
|
||||||
|
DebtLimit: sdk.NewInt64Coin("usdx", 1_000_000_000_000),
|
||||||
|
StabilityFee: sdk.MustNewDecFromStr("1.1"),
|
||||||
|
LiquidationPenalty: sdk.MustNewDecFromStr("0.05"),
|
||||||
|
AuctionSize: sdk.NewInt(50_000_000_000),
|
||||||
|
Prefix: 0x20,
|
||||||
|
SpotMarketID: "bnb:usd",
|
||||||
|
LiquidationMarketID: "bnb:usd",
|
||||||
|
KeeperRewardPercentage: sdk.MustNewDecFromStr("0.01"),
|
||||||
|
ConversionFactor: sdk.NewInt(8),
|
||||||
|
CheckCollateralizationIndexCount: sdk.NewInt(10),
|
||||||
|
},
|
||||||
|
},
|
||||||
|
debtParam: types.DefaultDebtParam,
|
||||||
|
surplusThreshold: types.DefaultSurplusThreshold,
|
||||||
|
surplusLot: types.DefaultSurplusLot,
|
||||||
|
debtThreshold: types.DefaultDebtThreshold,
|
||||||
|
debtLot: types.DefaultDebtLot,
|
||||||
|
breaker: types.DefaultCircuitBreaker,
|
||||||
|
},
|
||||||
|
errArgs: errArgs{
|
||||||
|
expectPass: false,
|
||||||
|
contains: "liquidation ratio must be > 0",
|
||||||
|
},
|
||||||
|
},
|
||||||
{
|
{
|
||||||
name: "invalid debt param empty denom",
|
name: "invalid debt param empty denom",
|
||||||
args: args{
|
args: args{
|
||||||
@ -847,7 +879,7 @@ func (suite *ParamsTestSuite) TestParamValidation() {
|
|||||||
suite.Require().NoError(err)
|
suite.Require().NoError(err)
|
||||||
} else {
|
} else {
|
||||||
suite.Require().Error(err)
|
suite.Require().Error(err)
|
||||||
suite.Require().True(strings.Contains(err.Error(), tc.errArgs.contains))
|
suite.Require().Contains(err.Error(), tc.errArgs.contains)
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
@ -113,7 +113,13 @@ func (k Keeper) SeizeDeposits(ctx sdk.Context, keeper sdk.AccAddress, deposit ty
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Loan-to-Value ratio after sending keeper their reward
|
// Loan-to-Value ratio after sending keeper their reward
|
||||||
ltv := borrowCoinValues.Sum().Quo(depositCoinValues.Sum())
|
depositUsdValue := depositCoinValues.Sum()
|
||||||
|
if depositUsdValue.IsZero() {
|
||||||
|
// Deposit value can be zero if params.KeeperRewardPercent is 1.0, or all deposit asset prices are zero.
|
||||||
|
// In this case the full deposit will be sent to the keeper and no auctions started.
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
ltv := borrowCoinValues.Sum().Quo(depositUsdValue)
|
||||||
|
|
||||||
liquidatedCoins, err := k.StartAuctions(ctx, deposit.Depositor, borrow.Amount, aucDeposits, depositCoinValues, borrowCoinValues, ltv, liqMap)
|
liquidatedCoins, err := k.StartAuctions(ctx, deposit.Depositor, borrow.Amount, aucDeposits, depositCoinValues, borrowCoinValues, ltv, liqMap)
|
||||||
// If some coins were liquidated and sent to auction prior to error, still need to emit liquidation event
|
// If some coins were liquidated and sent to auction prior to error, still need to emit liquidation event
|
||||||
|
@ -7,7 +7,6 @@ import (
|
|||||||
sdk "github.com/cosmos/cosmos-sdk/types"
|
sdk "github.com/cosmos/cosmos-sdk/types"
|
||||||
abci "github.com/tendermint/tendermint/abci/types"
|
abci "github.com/tendermint/tendermint/abci/types"
|
||||||
"github.com/tendermint/tendermint/crypto"
|
"github.com/tendermint/tendermint/crypto"
|
||||||
tmtime "github.com/tendermint/tendermint/types/time"
|
|
||||||
|
|
||||||
"github.com/kava-labs/kava/app"
|
"github.com/kava-labs/kava/app"
|
||||||
auctypes "github.com/kava-labs/kava/x/auction/types"
|
auctypes "github.com/kava-labs/kava/x/auction/types"
|
||||||
@ -26,7 +25,7 @@ func (suite *KeeperTestSuite) TestKeeperLiquidation() {
|
|||||||
initialKeeperCoins sdk.Coins
|
initialKeeperCoins sdk.Coins
|
||||||
depositCoins []sdk.Coin
|
depositCoins []sdk.Coin
|
||||||
borrowCoins sdk.Coins
|
borrowCoins sdk.Coins
|
||||||
liquidateAfter int64
|
liquidateAfter time.Duration
|
||||||
expectedTotalSuppliedCoins sdk.Coins
|
expectedTotalSuppliedCoins sdk.Coins
|
||||||
expectedTotalBorrowedCoins sdk.Coins
|
expectedTotalBorrowedCoins sdk.Coins
|
||||||
expectedKeeperCoins sdk.Coins // coins keeper address should have after successfully liquidating position
|
expectedKeeperCoins sdk.Coins // coins keeper address should have after successfully liquidating position
|
||||||
@ -48,7 +47,7 @@ func (suite *KeeperTestSuite) TestKeeperLiquidation() {
|
|||||||
// Set up test constants
|
// Set up test constants
|
||||||
model := types.NewInterestRateModel(sdk.MustNewDecFromStr("0"), sdk.MustNewDecFromStr("0.1"), sdk.MustNewDecFromStr("0.8"), sdk.MustNewDecFromStr("0.5"))
|
model := types.NewInterestRateModel(sdk.MustNewDecFromStr("0"), sdk.MustNewDecFromStr("0.1"), sdk.MustNewDecFromStr("0.8"), sdk.MustNewDecFromStr("0.5"))
|
||||||
reserveFactor := sdk.MustNewDecFromStr("0.05")
|
reserveFactor := sdk.MustNewDecFromStr("0.05")
|
||||||
oneMonthInSeconds := int64(2592000)
|
oneMonthInSeconds := time.Second * 30 * 24 * 3600
|
||||||
borrower := sdk.AccAddress(crypto.AddressHash([]byte("testborrower")))
|
borrower := sdk.AccAddress(crypto.AddressHash([]byte("testborrower")))
|
||||||
keeper := sdk.AccAddress(crypto.AddressHash([]byte("testkeeper")))
|
keeper := sdk.AccAddress(crypto.AddressHash([]byte("testkeeper")))
|
||||||
|
|
||||||
@ -99,6 +98,68 @@ func (suite *KeeperTestSuite) TestKeeperLiquidation() {
|
|||||||
contains: "",
|
contains: "",
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
"valid: 0% keeper rewards",
|
||||||
|
args{
|
||||||
|
borrower: borrower,
|
||||||
|
keeper: keeper,
|
||||||
|
keeperRewardPercent: sdk.MustNewDecFromStr("0.0"),
|
||||||
|
initialModuleCoins: sdk.NewCoins(sdk.NewCoin("ukava", sdk.NewInt(100*KAVA_CF))),
|
||||||
|
initialBorrowerCoins: sdk.NewCoins(sdk.NewCoin("ukava", sdk.NewInt(100*KAVA_CF))),
|
||||||
|
initialKeeperCoins: sdk.NewCoins(sdk.NewCoin("ukava", sdk.NewInt(100*KAVA_CF))),
|
||||||
|
depositCoins: sdk.NewCoins(sdk.NewCoin("ukava", sdk.NewInt(10*KAVA_CF))),
|
||||||
|
borrowCoins: sdk.NewCoins(sdk.NewCoin("ukava", sdk.NewInt(8*KAVA_CF))),
|
||||||
|
liquidateAfter: oneMonthInSeconds,
|
||||||
|
expectedTotalSuppliedCoins: sdk.NewCoins(sdk.NewInt64Coin("ukava", 100_004_117)),
|
||||||
|
expectedTotalBorrowedCoins: sdk.NewCoins(sdk.NewInt64Coin("ukava", 1)),
|
||||||
|
expectedKeeperCoins: sdk.NewCoins(sdk.NewCoin("ukava", sdk.NewInt(100*KAVA_CF))),
|
||||||
|
expectedBorrowerCoins: sdk.NewCoins(sdk.NewCoin("ukava", sdk.NewInt(98*KAVA_CF))), // initial - deposit + borrow + liquidation leftovers
|
||||||
|
expectedAuctions: auctypes.Auctions{
|
||||||
|
auctypes.CollateralAuction{
|
||||||
|
BaseAuction: auctypes.BaseAuction{
|
||||||
|
ID: 1,
|
||||||
|
Initiator: "hard",
|
||||||
|
Lot: sdk.NewInt64Coin("ukava", 10000411),
|
||||||
|
Bidder: nil,
|
||||||
|
Bid: sdk.NewInt64Coin("ukava", 0),
|
||||||
|
HasReceivedBids: false,
|
||||||
|
EndTime: endTime,
|
||||||
|
MaxEndTime: endTime,
|
||||||
|
},
|
||||||
|
CorrespondingDebt: sdk.NewInt64Coin("debt", 0),
|
||||||
|
MaxBid: sdk.NewInt64Coin("ukava", 8004765),
|
||||||
|
LotReturns: lotReturns,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
errArgs{
|
||||||
|
expectPass: true,
|
||||||
|
contains: "",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"valid: 100% keeper reward",
|
||||||
|
args{
|
||||||
|
borrower: borrower,
|
||||||
|
keeper: keeper,
|
||||||
|
keeperRewardPercent: sdk.MustNewDecFromStr("1.0"),
|
||||||
|
initialModuleCoins: sdk.NewCoins(sdk.NewCoin("ukava", sdk.NewInt(100*KAVA_CF))),
|
||||||
|
initialBorrowerCoins: sdk.NewCoins(sdk.NewCoin("ukava", sdk.NewInt(100*KAVA_CF))),
|
||||||
|
initialKeeperCoins: sdk.NewCoins(sdk.NewCoin("ukava", sdk.NewInt(100*KAVA_CF))),
|
||||||
|
depositCoins: sdk.NewCoins(sdk.NewCoin("ukava", sdk.NewInt(10*KAVA_CF))),
|
||||||
|
borrowCoins: sdk.NewCoins(sdk.NewCoin("ukava", sdk.NewInt(8*KAVA_CF))),
|
||||||
|
liquidateAfter: oneMonthInSeconds,
|
||||||
|
expectedTotalSuppliedCoins: sdk.NewCoins(sdk.NewInt64Coin("ukava", 100_004_117)),
|
||||||
|
expectedTotalBorrowedCoins: sdk.NewCoins(sdk.NewInt64Coin("ukava", 8_004_766)),
|
||||||
|
expectedKeeperCoins: sdk.NewCoins(sdk.NewCoin("ukava", sdk.NewInt(110_000_411))),
|
||||||
|
expectedBorrowerCoins: sdk.NewCoins(sdk.NewCoin("ukava", sdk.NewInt(98*KAVA_CF))), // initial - deposit + borrow + liquidation leftovers
|
||||||
|
expectedAuctions: nil,
|
||||||
|
},
|
||||||
|
errArgs{
|
||||||
|
expectPass: true,
|
||||||
|
contains: "",
|
||||||
|
},
|
||||||
|
},
|
||||||
{
|
{
|
||||||
"valid: single deposit, multiple borrows",
|
"valid: single deposit, multiple borrows",
|
||||||
args{
|
args{
|
||||||
@ -467,7 +528,7 @@ func (suite *KeeperTestSuite) TestKeeperLiquidation() {
|
|||||||
suite.Run(tc.name, func() {
|
suite.Run(tc.name, func() {
|
||||||
// Initialize test app and set context
|
// Initialize test app and set context
|
||||||
tApp := app.NewTestApp()
|
tApp := app.NewTestApp()
|
||||||
ctx := tApp.NewContext(true, abci.Header{Height: 1, Time: tmtime.Now()})
|
ctx := tApp.NewContext(true, abci.Header{Height: 1, Time: time.Date(1998, 1, 1, 0, 0, 0, 0, time.UTC)})
|
||||||
|
|
||||||
// account which will deposit "initial module account coins"
|
// account which will deposit "initial module account coins"
|
||||||
depositor := sdk.AccAddress(crypto.AddressHash([]byte("testdepositor")))
|
depositor := sdk.AccAddress(crypto.AddressHash([]byte("testdepositor")))
|
||||||
@ -626,7 +687,7 @@ func (suite *KeeperTestSuite) TestKeeperLiquidation() {
|
|||||||
suite.Require().NoError(err)
|
suite.Require().NoError(err)
|
||||||
|
|
||||||
// Set up liquidation chain context and run begin blocker
|
// Set up liquidation chain context and run begin blocker
|
||||||
runAtTime := time.Unix(suite.ctx.BlockTime().Unix()+(tc.args.liquidateAfter), 0)
|
runAtTime := suite.ctx.BlockTime().Add(tc.args.liquidateAfter)
|
||||||
liqCtx := suite.ctx.WithBlockTime(runAtTime)
|
liqCtx := suite.ctx.WithBlockTime(runAtTime)
|
||||||
hard.BeginBlocker(liqCtx, suite.keeper)
|
hard.BeginBlocker(liqCtx, suite.keeper)
|
||||||
|
|
||||||
@ -665,7 +726,6 @@ func (suite *KeeperTestSuite) TestKeeperLiquidation() {
|
|||||||
|
|
||||||
// Check that the expected auctions have been created
|
// Check that the expected auctions have been created
|
||||||
auctions := suite.auctionKeeper.GetAllAuctions(liqCtx)
|
auctions := suite.auctionKeeper.GetAllAuctions(liqCtx)
|
||||||
suite.Require().True(len(auctions) > 0)
|
|
||||||
suite.Require().Equal(tc.args.expectedAuctions, auctions)
|
suite.Require().Equal(tc.args.expectedAuctions, auctions)
|
||||||
|
|
||||||
// Check that supplied and borrowed coins have been updated post-liquidation
|
// Check that supplied and borrowed coins have been updated post-liquidation
|
||||||
|
@ -109,6 +109,10 @@ func (mm MoneyMarket) Validate() error {
|
|||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if mm.ConversionFactor.IsNil() || mm.ConversionFactor.LT(sdk.OneInt()) {
|
||||||
|
return fmt.Errorf("conversion '%s' factor must be ≥ one", mm.ConversionFactor)
|
||||||
|
}
|
||||||
|
|
||||||
if err := mm.InterestRateModel.Validate(); err != nil {
|
if err := mm.InterestRateModel.Validate(); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
@ -1,7 +1,6 @@
|
|||||||
package types_test
|
package types_test
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"strings"
|
|
||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
sdk "github.com/cosmos/cosmos-sdk/types"
|
sdk "github.com/cosmos/cosmos-sdk/types"
|
||||||
@ -34,6 +33,29 @@ func (suite *ParamTestSuite) TestParamValidation() {
|
|||||||
expectPass: true,
|
expectPass: true,
|
||||||
expectedErr: "",
|
expectedErr: "",
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
name: "invalid: conversion factor < one",
|
||||||
|
args: args{
|
||||||
|
minBorrowVal: types.DefaultMinimumBorrowUSDValue,
|
||||||
|
mms: types.MoneyMarkets{
|
||||||
|
{
|
||||||
|
Denom: "btcb",
|
||||||
|
BorrowLimit: types.NewBorrowLimit(
|
||||||
|
false,
|
||||||
|
sdk.MustNewDecFromStr("100000000000"),
|
||||||
|
sdk.MustNewDecFromStr("0.5"),
|
||||||
|
),
|
||||||
|
SpotMarketID: "btc:usd",
|
||||||
|
ConversionFactor: sdk.NewInt(0),
|
||||||
|
InterestRateModel: types.InterestRateModel{},
|
||||||
|
ReserveFactor: sdk.MustNewDecFromStr("0.05"),
|
||||||
|
KeeperRewardPercentage: sdk.MustNewDecFromStr("0.05"),
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
expectPass: false,
|
||||||
|
expectedErr: "conversion '0' factor must be ≥ one",
|
||||||
|
},
|
||||||
}
|
}
|
||||||
for _, tc := range testCases {
|
for _, tc := range testCases {
|
||||||
suite.Run(tc.name, func() {
|
suite.Run(tc.name, func() {
|
||||||
@ -43,7 +65,7 @@ func (suite *ParamTestSuite) TestParamValidation() {
|
|||||||
suite.NoError(err)
|
suite.NoError(err)
|
||||||
} else {
|
} else {
|
||||||
suite.Error(err)
|
suite.Error(err)
|
||||||
suite.Require().True(strings.Contains(err.Error(), tc.expectedErr))
|
suite.Require().Contains(err.Error(), tc.expectedErr)
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user