diff --git a/app/test_common.go b/app/test_common.go index af345f28..18004b69 100644 --- a/app/test_common.go +++ b/app/test_common.go @@ -72,7 +72,7 @@ func (tApp TestApp) GetLiquidatorKeeper() liquidator.Keeper { return tApp.liquid func (tApp TestApp) GetPriceFeedKeeper() pricefeed.Keeper { return tApp.pricefeedKeeper } // Create a new auth genesis state from some addresses and coins. The state is returned marshalled into a map. -// TODO this is only a TestApp method because it needs access to the codec, could be another way to structure it +// TODO make this not a TestApp method. func (tApp TestApp) NewAuthGenStateFromAccounts(addresses []sdk.AccAddress, coins []sdk.Coins) GenesisState { // Create GenAccounts accounts := authexported.GenesisAccounts{} @@ -81,7 +81,7 @@ func (tApp TestApp) NewAuthGenStateFromAccounts(addresses []sdk.AccAddress, coin } // Create the auth genesis state authGenesis := auth.NewGenesisState(auth.DefaultParams(), accounts) - return GenesisState{auth.ModuleName: tApp.cdc.MustMarshalJSON(authGenesis)} + return GenesisState{auth.ModuleName: auth.ModuleCdc.MustMarshalJSON(authGenesis)} } // This calls InitChain on the app using the default genesis state, overwitten with any passed in genesis states diff --git a/x/liquidator/keeper/integration_test.go b/x/liquidator/keeper/integration_test.go new file mode 100644 index 00000000..53151269 --- /dev/null +++ b/x/liquidator/keeper/integration_test.go @@ -0,0 +1,73 @@ +package keeper_test + +import ( + "time" + + sdk "github.com/cosmos/cosmos-sdk/types" + + "github.com/kava-labs/kava/app" + "github.com/kava-labs/kava/x/cdp" + "github.com/kava-labs/kava/x/liquidator" + "github.com/kava-labs/kava/x/liquidator/types" + "github.com/kava-labs/kava/x/pricefeed" +) + +// Avoid cluttering test cases with long function name +func i(in int64) sdk.Int { return sdk.NewInt(in) } +func c(denom string, amount int64) sdk.Coin { return sdk.NewInt64Coin(denom, amount) } +func cs(coins ...sdk.Coin) sdk.Coins { return sdk.NewCoins(coins...) } + +// Default genesis states to initialize test apps + +func NewPFGenState(asset string, price sdk.Dec) app.GenesisState { + quote := "usd" + ap := pricefeed.Params{ + Markets: []pricefeed.Market{ + pricefeed.Market{MarketID: asset, BaseAsset: asset, QuoteAsset: quote, Oracles: pricefeed.Oracles{}, Active: true}}, + } + pfGenesis := pricefeed.GenesisState{ + Params: ap, + PostedPrices: []pricefeed.PostedPrice{ + pricefeed.PostedPrice{ + MarketID: asset, + OracleAddress: sdk.AccAddress{}, + Price: price, + Expiry: time.Unix(9999999999, 0), // some deterministic future date, + }, + }, + } + return app.GenesisState{pricefeed.ModuleName: pricefeed.ModuleCdc.MustMarshalJSON(pfGenesis)} +} + +func NewCDPGenState() app.GenesisState { + cdpGenesis := cdp.GenesisState{ + Params: cdp.CdpParams{ + GlobalDebtLimit: sdk.NewInt(1000000), + CollateralParams: []cdp.CollateralParams{ + { + Denom: "btc", + LiquidationRatio: sdk.MustNewDecFromStr("1.5"), + DebtLimit: sdk.NewInt(500000), + }, + }, + }, + GlobalDebt: sdk.ZeroInt(), + CDPs: cdp.CDPs{}, + } + return app.GenesisState{cdp.ModuleName: cdp.ModuleCdc.MustMarshalJSON(cdpGenesis)} +} + +func NewLiquidatorGenState() app.GenesisState { + liquidatorGenesis := types.GenesisState{ + Params: types.LiquidatorParams{ + DebtAuctionSize: sdk.NewInt(1000), + CollateralParams: []types.CollateralParams{ + { + Denom: "btc", + AuctionSize: sdk.NewInt(1), + }, + }, + }, + } + return app.GenesisState{liquidator.ModuleName: liquidator.ModuleCdc.MustMarshalJSON(liquidatorGenesis)} +} diff --git a/x/liquidator/keeper/keeper.go b/x/liquidator/keeper/keeper.go index e5a15014..59354270 100644 --- a/x/liquidator/keeper/keeper.go +++ b/x/liquidator/keeper/keeper.go @@ -55,7 +55,7 @@ func (k Keeper) SeizeAndStartCollateralAuction(ctx sdk.Context, owner sdk.AccAdd stableToRaise := sdk.NewDecFromInt(collateralToSell).Quo(sdk.NewDecFromInt(cdp.CollateralAmount)).Mul(sdk.NewDecFromInt(cdp.Debt)).RoundInt() // Seize the collateral and debt from the CDP - err := k.partialSeizeCDP(ctx, owner, collateralDenom, collateralToSell, stableToRaise) + err := k.PartialSeizeCDP(ctx, owner, collateralDenom, collateralToSell, stableToRaise) if err != nil { return 0, err } @@ -99,7 +99,7 @@ func (k Keeper) StartDebtAuction(ctx sdk.Context) (auction.ID, sdk.Error) { } // Record amount of debt sent for auction. Debt can only be reduced in lock step with reducing stable coin seizedDebt.SentToAuction = seizedDebt.SentToAuction.Add(params.DebtAuctionSize) - k.setSeizedDebt(ctx, seizedDebt) + k.SetSeizedDebt(ctx, seizedDebt) return auctionID, nil } @@ -131,7 +131,7 @@ func (k Keeper) StartDebtAuction(ctx sdk.Context) (auction.ID, sdk.Error) { // } // PartialSeizeCDP seizes some collateral and debt from an under-collateralized CDP. -func (k Keeper) partialSeizeCDP(ctx sdk.Context, owner sdk.AccAddress, collateralDenom string, collateralToSeize sdk.Int, debtToSeize sdk.Int) sdk.Error { // aka Cat.bite +func (k Keeper) PartialSeizeCDP(ctx sdk.Context, owner sdk.AccAddress, collateralDenom string, collateralToSeize sdk.Int, debtToSeize sdk.Int) sdk.Error { // aka Cat.bite // Seize debt and collateral in the cdp module. This also validates the inputs. err := k.cdpKeeper.PartialSeizeCDP(ctx, owner, collateralDenom, collateralToSeize, debtToSeize) if err != nil { @@ -141,7 +141,7 @@ func (k Keeper) partialSeizeCDP(ctx sdk.Context, owner sdk.AccAddress, collatera // increment the total seized debt (Awe) by cdp.debt seizedDebt := k.GetSeizedDebt(ctx) seizedDebt.Total = seizedDebt.Total.Add(debtToSeize) - k.setSeizedDebt(ctx, seizedDebt) + k.SetSeizedDebt(ctx, seizedDebt) // add cdp.collateral amount of coins to the moduleAccount (so they can be transferred to the auction later) coins := sdk.NewCoins(sdk.NewCoin(collateralDenom, collateralToSeize)) @@ -172,7 +172,7 @@ func (k Keeper) SettleDebt(ctx sdk.Context) sdk.Error { if err != nil { return err // this should not error in this context } - k.setSeizedDebt(ctx, updatedDebt) + k.SetSeizedDebt(ctx, updatedDebt) // Subtract stable coin from moduleAccout k.bankKeeper.SubtractCoins(ctx, k.cdpKeeper.GetLiquidatorAccountAddress(), sdk.Coins{sdk.NewCoin(k.cdpKeeper.GetStableDenom(), settleAmount)}) @@ -197,7 +197,7 @@ func (k Keeper) GetSeizedDebt(ctx sdk.Context) types.SeizedDebt { k.cdc.MustUnmarshalBinaryLengthPrefixed(bz, &seizedDebt) return seizedDebt } -func (k Keeper) setSeizedDebt(ctx sdk.Context, debt types.SeizedDebt) { +func (k Keeper) SetSeizedDebt(ctx sdk.Context, debt types.SeizedDebt) { store := ctx.KVStore(k.key) bz := k.cdc.MustMarshalBinaryLengthPrefixed(debt) store.Set(k.getSeizedDebtKey(), bz) diff --git a/x/liquidator/keeper/keeper_test.go b/x/liquidator/keeper/keeper_test.go index 23fdd974..c952f11c 100644 --- a/x/liquidator/keeper/keeper_test.go +++ b/x/liquidator/keeper/keeper_test.go @@ -1,124 +1,103 @@ -package keeper +package keeper_test import ( "testing" "time" sdk "github.com/cosmos/cosmos-sdk/types" - "github.com/cosmos/cosmos-sdk/x/mock" "github.com/stretchr/testify/require" + abci "github.com/tendermint/tendermint/abci/types" tmtime "github.com/tendermint/tendermint/types/time" - "github.com/kava-labs/kava/x/cdp" + "github.com/kava-labs/kava/app" "github.com/kava-labs/kava/x/liquidator/types" - "github.com/kava-labs/kava/x/pricefeed" ) func TestKeeper_SeizeAndStartCollateralAuction(t *testing.T) { // Setup - ctx, k := setupTestKeepers() + _, addrs := app.GeneratePrivKeyAddressPairs(1) - _, addrs := mock.GeneratePrivKeyAddressPairs(1) + tApp := app.NewTestApp() + tApp.InitializeFromGenesisStates( + tApp.NewAuthGenStateFromAccounts(addrs, []sdk.Coins{cs(c("btc", 100))}), + NewPFGenState("btc", sdk.MustNewDecFromStr("8000.00")), + NewCDPGenState(), + NewLiquidatorGenState(), + ) + ctx := tApp.NewContext(false, abci.Header{}) - pricefeed.InitGenesis(ctx, k.pricefeedKeeper, pricefeedGenesis()) - k.pricefeedKeeper.SetPrice(ctx, addrs[0], "btc", sdk.MustNewDecFromStr("8000.00"), tmtime.Now().Add(time.Hour*1)) - k.pricefeedKeeper.SetCurrentPrices(ctx, "btc") - cdp.InitGenesis(ctx, k.cdpKeeper, k.pricefeedKeeper, cdpDefaultGenesis()) - dp := defaultParams() - k.liquidatorKeeper.SetParams(ctx, dp) - k.bankKeeper.AddCoins(ctx, addrs[0], cs(c("btc", 100))) + require.NoError(t, tApp.GetCDPKeeper().ModifyCDP(ctx, addrs[0], "btc", i(3), i(16000))) - k.cdpKeeper.ModifyCDP(ctx, addrs[0], "btc", i(3), i(16000)) - - k.pricefeedKeeper.SetPrice(ctx, addrs[0], "btc", sdk.MustNewDecFromStr("7999.99"), tmtime.Now().Add(time.Hour*1)) - k.pricefeedKeeper.SetCurrentPrices(ctx, "btc") + _, err := tApp.GetPriceFeedKeeper().SetPrice(ctx, addrs[0], "btc", sdk.MustNewDecFromStr("7999.99"), time.Unix(9999999999, 0)) + require.NoError(t, err) + require.NoError(t, tApp.GetPriceFeedKeeper().SetCurrentPrices(ctx, "btc")) // Run test function - auctionID, err := k.liquidatorKeeper.SeizeAndStartCollateralAuction(ctx, addrs[0], "btc") + auctionID, err := tApp.GetLiquidatorKeeper().SeizeAndStartCollateralAuction(ctx, addrs[0], "btc") // Check CDP require.NoError(t, err) - cdp, found := k.cdpKeeper.GetCDP(ctx, addrs[0], "btc") + cdp, found := tApp.GetCDPKeeper().GetCDP(ctx, addrs[0], "btc") require.True(t, found) require.Equal(t, cdp.CollateralAmount, i(2)) // original amount - params.CollateralAuctionSize require.Equal(t, cdp.Debt, i(10667)) // original debt scaled by amount of collateral removed // Check auction exists - _, found = k.auctionKeeper.GetAuction(ctx, auctionID) + _, found = tApp.GetAuctionKeeper().GetAuction(ctx, auctionID) require.True(t, found) - // TODO check auction values are correct? } func TestKeeper_StartDebtAuction(t *testing.T) { // Setup - ctx, k := setupTestKeepers() - k.liquidatorKeeper.SetParams(ctx, defaultParams()) + tApp := app.NewTestApp() + tApp.InitializeFromGenesisStates( + NewLiquidatorGenState(), + ) + keeper := tApp.GetLiquidatorKeeper() + ctx := tApp.NewContext(false, abci.Header{}) + initSDebt := types.SeizedDebt{i(2000), i(0)} - k.liquidatorKeeper.setSeizedDebt(ctx, initSDebt) + keeper.SetSeizedDebt(ctx, initSDebt) // Execute - auctionID, err := k.liquidatorKeeper.StartDebtAuction(ctx) + auctionID, err := keeper.StartDebtAuction(ctx) // Check require.NoError(t, err) require.Equal(t, types.SeizedDebt{ initSDebt.Total, - initSDebt.SentToAuction.Add(k.liquidatorKeeper.GetParams(ctx).DebtAuctionSize), + initSDebt.SentToAuction.Add(keeper.GetParams(ctx).DebtAuctionSize), }, - k.liquidatorKeeper.GetSeizedDebt(ctx), + keeper.GetSeizedDebt(ctx), ) - _, found := k.auctionKeeper.GetAuction(ctx, auctionID) + _, found := tApp.GetAuctionKeeper().GetAuction(ctx, auctionID) require.True(t, found) - // TODO check auction values are correct? } -// func TestKeeper_StartSurplusAuction(t *testing.T) { -// // Setup -// ctx, k := setupTestKeepers() -// initSurplus := i(2000) -// k.liquidatorKeeper.bankKeeper.AddCoins(ctx, k.cdpKeeper.GetLiquidatorAccountAddress(), cs(sdk.NewCoin(k.cdpKeeper.GetStableDenom(), initSurplus))) -// k.liquidatorKeeper.setSeizedDebt(ctx, i(0)) - -// // Execute -// auctionID, err := k.liquidatorKeeper.StartSurplusAuction(ctx) - -// // Check -// require.NoError(t, err) -// require.Equal(t, -// initSurplus.Sub(SurplusAuctionSize), -// k.liquidatorKeeper.bankKeeper.GetCoins(ctx, -// k.cdpKeeper.GetLiquidatorAccountAddress(), -// ).AmountOf(k.cdpKeeper.GetStableDenom()), -// ) -// _, found := k.auctionKeeper.GetAuction(ctx, auctionID) -// require.True(t, found) -// } - func TestKeeper_partialSeizeCDP(t *testing.T) { // Setup - ctx, k := setupTestKeepers() + _, addrs := app.GeneratePrivKeyAddressPairs(1) - _, addrs := mock.GeneratePrivKeyAddressPairs(1) + tApp := app.NewTestApp() + tApp.InitializeFromGenesisStates( + tApp.NewAuthGenStateFromAccounts(addrs, []sdk.Coins{cs(c("btc", 100))}), + NewPFGenState("btc", sdk.MustNewDecFromStr("8000.00")), + NewCDPGenState(), + NewLiquidatorGenState(), + ) + ctx := tApp.NewContext(false, abci.Header{}) - pricefeed.InitGenesis(ctx, k.pricefeedKeeper, pricefeedGenesis()) + tApp.GetCDPKeeper().ModifyCDP(ctx, addrs[0], "btc", i(3), i(16000)) - k.pricefeedKeeper.SetPrice(ctx, addrs[0], "btc", sdk.MustNewDecFromStr("8000.00"), tmtime.Now().Add(time.Hour*1)) - k.pricefeedKeeper.SetCurrentPrices(ctx, "btc") - k.bankKeeper.AddCoins(ctx, addrs[0], cs(c("btc", 100))) - cdp.InitGenesis(ctx, k.cdpKeeper, k.pricefeedKeeper, cdpDefaultGenesis()) - k.liquidatorKeeper.SetParams(ctx, defaultParams()) - - k.cdpKeeper.ModifyCDP(ctx, addrs[0], "btc", i(3), i(16000)) - - k.pricefeedKeeper.SetPrice(ctx, addrs[0], "btc", sdk.MustNewDecFromStr("7999.99"), tmtime.Now().Add(time.Hour*1)) - k.pricefeedKeeper.SetCurrentPrices(ctx, "btc") + tApp.GetPriceFeedKeeper().SetPrice(ctx, addrs[0], "btc", sdk.MustNewDecFromStr("7999.99"), tmtime.Now().Add(time.Hour*1)) + tApp.GetPriceFeedKeeper().SetCurrentPrices(ctx, "btc") // Run test function - err := k.liquidatorKeeper.partialSeizeCDP(ctx, addrs[0], "btc", i(2), i(10000)) + err := tApp.GetLiquidatorKeeper().PartialSeizeCDP(ctx, addrs[0], "btc", i(2), i(10000)) // Check require.NoError(t, err) - cdp, found := k.cdpKeeper.GetCDP(ctx, addrs[0], "btc") + cdp, found := tApp.GetCDPKeeper().GetCDP(ctx, addrs[0], "btc") require.True(t, found) require.Equal(t, i(1), cdp.CollateralAmount) require.Equal(t, i(6000), cdp.Debt) @@ -126,12 +105,13 @@ func TestKeeper_partialSeizeCDP(t *testing.T) { func TestKeeper_GetSetSeizedDebt(t *testing.T) { // Setup - ctx, k := setupTestKeepers() + tApp := app.NewTestApp() + ctx := tApp.NewContext(true, abci.Header{}) debt := types.SeizedDebt{i(234247645), i(2343)} // Run test function - k.liquidatorKeeper.setSeizedDebt(ctx, debt) - readDebt := k.liquidatorKeeper.GetSeizedDebt(ctx) + tApp.GetLiquidatorKeeper().SetSeizedDebt(ctx, debt) + readDebt := tApp.GetLiquidatorKeeper().GetSeizedDebt(ctx) // Check require.Equal(t, debt, readDebt) diff --git a/x/liquidator/keeper/test_common.go b/x/liquidator/keeper/test_common.go deleted file mode 100644 index ab2d0a6b..00000000 --- a/x/liquidator/keeper/test_common.go +++ /dev/null @@ -1,171 +0,0 @@ -package keeper - -import ( - "time" - - "github.com/cosmos/cosmos-sdk/codec" - "github.com/cosmos/cosmos-sdk/store" - sdk "github.com/cosmos/cosmos-sdk/types" - "github.com/cosmos/cosmos-sdk/x/auth" - "github.com/cosmos/cosmos-sdk/x/bank" - "github.com/cosmos/cosmos-sdk/x/params" - abci "github.com/tendermint/tendermint/abci/types" - "github.com/tendermint/tendermint/libs/log" - tmtime "github.com/tendermint/tendermint/types/time" - dbm "github.com/tendermint/tm-db" - - "github.com/kava-labs/kava/x/auction" - "github.com/kava-labs/kava/x/cdp" - "github.com/kava-labs/kava/x/liquidator/types" - "github.com/kava-labs/kava/x/pricefeed" -) - -// Avoid cluttering test cases with long function name -func i(in int64) sdk.Int { return sdk.NewInt(in) } -func c(denom string, amount int64) sdk.Coin { return sdk.NewInt64Coin(denom, amount) } -func cs(coins ...sdk.Coin) sdk.Coins { return sdk.NewCoins(coins...) } - -type keepers struct { - paramsKeeper params.Keeper - accountKeeper auth.AccountKeeper - bankKeeper bank.Keeper - pricefeedKeeper pricefeed.Keeper - auctionKeeper auction.Keeper - cdpKeeper cdp.Keeper - liquidatorKeeper Keeper -} - -func setupTestKeepers() (sdk.Context, keepers) { - - // Setup in memory database - keyParams := sdk.NewKVStoreKey(params.StoreKey) - tkeyParams := sdk.NewTransientStoreKey(params.TStoreKey) - keyAcc := sdk.NewKVStoreKey(auth.StoreKey) - keyPriceFeed := sdk.NewKVStoreKey(pricefeed.StoreKey) - keyCDP := sdk.NewKVStoreKey("cdp") - keyAuction := sdk.NewKVStoreKey("auction") - keyLiquidator := sdk.NewKVStoreKey("liquidator") - - db := dbm.NewMemDB() - ms := store.NewCommitMultiStore(db) - ms.MountStoreWithDB(keyParams, sdk.StoreTypeIAVL, db) - ms.MountStoreWithDB(tkeyParams, sdk.StoreTypeTransient, db) - ms.MountStoreWithDB(keyAcc, sdk.StoreTypeIAVL, db) - ms.MountStoreWithDB(keyPriceFeed, sdk.StoreTypeIAVL, db) - ms.MountStoreWithDB(keyCDP, sdk.StoreTypeIAVL, db) - ms.MountStoreWithDB(keyAuction, sdk.StoreTypeIAVL, db) - ms.MountStoreWithDB(keyLiquidator, sdk.StoreTypeIAVL, db) - err := ms.LoadLatestVersion() - if err != nil { - panic(err) - } - - // Create Codec - cdc := makeTestCodec() - - // Create Keepers - paramsKeeper := params.NewKeeper(cdc, keyParams, tkeyParams, params.DefaultCodespace) - accountKeeper := auth.NewAccountKeeper( - cdc, - keyAcc, - paramsKeeper.Subspace(auth.DefaultParamspace), - auth.ProtoBaseAccount, - ) - blacklistedAddrs := make(map[string]bool) - bankKeeper := bank.NewBaseKeeper( - accountKeeper, - paramsKeeper.Subspace(bank.DefaultParamspace), - bank.DefaultCodespace, - blacklistedAddrs, - ) - pricefeedKeeper := pricefeed.NewKeeper(cdc, keyPriceFeed, paramsKeeper.Subspace(pricefeed.DefaultParamspace), pricefeed.DefaultCodespace) - cdpKeeper := cdp.NewKeeper( - cdc, - keyCDP, - paramsKeeper.Subspace(cdp.DefaultParamspace), - pricefeedKeeper, - bankKeeper, - ) - auctionKeeper := auction.NewKeeper(cdc, cdpKeeper, keyAuction, paramsKeeper.Subspace(auction.DefaultParamspace)) // Note: cdp keeper stands in for bank keeper - liquidatorKeeper := NewKeeper( - cdc, - keyLiquidator, - paramsKeeper.Subspace(types.DefaultParamspace), - cdpKeeper, - auctionKeeper, - cdpKeeper, - ) // Note: cdp keeper stands in for bank keeper - - // Create context - ctx := sdk.NewContext(ms, abci.Header{ChainID: "testchain"}, false, log.NewNopLogger()) - - return ctx, keepers{ - paramsKeeper, - accountKeeper, - bankKeeper, - pricefeedKeeper, - auctionKeeper, - cdpKeeper, - liquidatorKeeper, - } -} - -func makeTestCodec() *codec.Codec { - var cdc = codec.New() - auth.RegisterCodec(cdc) - bank.RegisterCodec(cdc) - pricefeed.RegisterCodec(cdc) - auction.RegisterCodec(cdc) - cdp.RegisterCodec(cdc) - types.RegisterCodec(cdc) - sdk.RegisterCodec(cdc) - codec.RegisterCrypto(cdc) - return cdc -} - -func defaultParams() types.LiquidatorParams { - return types.LiquidatorParams{ - DebtAuctionSize: sdk.NewInt(1000), - CollateralParams: []types.CollateralParams{ - { - Denom: "btc", - AuctionSize: sdk.NewInt(1), - }, - }, - } -} - -func cdpDefaultGenesis() cdp.GenesisState { - return cdp.GenesisState{ - cdp.CdpParams{ - GlobalDebtLimit: sdk.NewInt(1000000), - CollateralParams: []cdp.CollateralParams{ - { - Denom: "btc", - LiquidationRatio: sdk.MustNewDecFromStr("1.5"), - DebtLimit: sdk.NewInt(500000), - }, - }, - }, - sdk.ZeroInt(), - cdp.CDPs{}, - } -} - -func pricefeedGenesis() pricefeed.GenesisState { - ap := pricefeed.Params{ - Markets: []pricefeed.Market{ - pricefeed.Market{MarketID: "btc", BaseAsset: "btc", QuoteAsset: "usd", Oracles: pricefeed.Oracles{}, Active: true}}, - } - return pricefeed.GenesisState{ - Params: ap, - PostedPrices: []pricefeed.PostedPrice{ - pricefeed.PostedPrice{ - MarketID: "btc", - OracleAddress: sdk.AccAddress{}, - Price: sdk.MustNewDecFromStr("8000.00"), - Expiry: tmtime.Now().Add(1 * time.Hour), - }, - }, - } -}