Merge pull request #273 from Kava-Labs/ro-remove-mock-from-tests

Tests Refactor v1: remove mock
This commit is contained in:
Kevin Davis 2019-12-10 10:54:38 -05:00 committed by GitHub
commit c5db0ff680
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
23 changed files with 821 additions and 934 deletions

View File

@ -4,6 +4,10 @@ import (
"io"
"os"
"github.com/kava-labs/kava/x/auction"
"github.com/kava-labs/kava/x/cdp"
"github.com/kava-labs/kava/x/liquidator"
"github.com/kava-labs/kava/x/pricefeed"
validatorvesting "github.com/kava-labs/kava/x/validator-vesting"
abci "github.com/tendermint/tendermint/abci/types"
@ -55,6 +59,10 @@ var (
crisis.AppModuleBasic{},
slashing.AppModuleBasic{},
supply.AppModuleBasic{},
auction.AppModuleBasic{},
cdp.AppModuleBasic{},
liquidator.AppModuleBasic{},
pricefeed.AppModuleBasic{},
)
// module account permissions
@ -92,6 +100,10 @@ type App struct {
crisisKeeper crisis.Keeper
paramsKeeper params.Keeper
vvKeeper validatorvesting.Keeper
auctionKeeper auction.Keeper
cdpKeeper cdp.Keeper
liquidatorKeeper liquidator.Keeper
pricefeedKeeper pricefeed.Keeper
// the module manager
mm *module.Manager
@ -115,6 +127,7 @@ func NewApp(logger log.Logger, db dbm.DB, traceStore io.Writer, loadLatest bool,
bam.MainStoreKey, auth.StoreKey, staking.StoreKey,
supply.StoreKey, mint.StoreKey, distr.StoreKey, slashing.StoreKey,
gov.StoreKey, params.StoreKey, validatorvesting.StoreKey,
auction.StoreKey, cdp.StoreKey, liquidator.StoreKey, pricefeed.StoreKey,
)
tkeys := sdk.NewTransientStoreKeys(params.TStoreKey)
@ -136,6 +149,10 @@ func NewApp(logger log.Logger, db dbm.DB, traceStore io.Writer, loadLatest bool,
slashingSubspace := app.paramsKeeper.Subspace(slashing.DefaultParamspace)
govSubspace := app.paramsKeeper.Subspace(gov.DefaultParamspace).WithKeyTable(gov.ParamKeyTable())
crisisSubspace := app.paramsKeeper.Subspace(crisis.DefaultParamspace)
auctionSubspace := app.paramsKeeper.Subspace(auction.DefaultParamspace)
cdpSubspace := app.paramsKeeper.Subspace(cdp.DefaultParamspace)
liquidatorSubspace := app.paramsKeeper.Subspace(liquidator.DefaultParamspace)
pricefeedSubspace := app.paramsKeeper.Subspace(pricefeed.DefaultParamspace)
// add keepers
app.accountKeeper = auth.NewAccountKeeper(
@ -207,6 +224,29 @@ func NewApp(logger log.Logger, db dbm.DB, traceStore io.Writer, loadLatest bool,
app.bankKeeper,
app.supplyKeeper,
&stakingKeeper)
app.pricefeedKeeper = pricefeed.NewKeeper(
app.cdc,
keys[pricefeed.StoreKey],
pricefeedSubspace,
pricefeed.DefaultCodespace)
app.cdpKeeper = cdp.NewKeeper(
app.cdc,
keys[cdp.StoreKey],
cdpSubspace,
app.pricefeedKeeper,
app.bankKeeper)
app.auctionKeeper = auction.NewKeeper(
app.cdc,
app.cdpKeeper, // CDP keeper standing in for bank
keys[auction.StoreKey],
auctionSubspace)
app.liquidatorKeeper = liquidator.NewKeeper(
app.cdc,
keys[liquidator.StoreKey],
liquidatorSubspace,
app.cdpKeeper,
app.auctionKeeper,
app.cdpKeeper) // CDP keeper standing in for bank
// register the staking hooks
// NOTE: stakingKeeper above is passed by reference, so that it will contain these hooks
@ -227,6 +267,10 @@ func NewApp(logger log.Logger, db dbm.DB, traceStore io.Writer, loadLatest bool,
slashing.NewAppModule(app.slashingKeeper, app.stakingKeeper),
staking.NewAppModule(app.stakingKeeper, app.accountKeeper, app.supplyKeeper),
validatorvesting.NewAppModule(app.vvKeeper, app.accountKeeper),
auction.NewAppModule(app.auctionKeeper),
cdp.NewAppModule(app.cdpKeeper, app.pricefeedKeeper),
liquidator.NewAppModule(app.liquidatorKeeper),
pricefeed.NewAppModule(app.pricefeedKeeper),
)
// During begin block slashing happens after distr.BeginBlocker so that
@ -234,7 +278,7 @@ func NewApp(logger log.Logger, db dbm.DB, traceStore io.Writer, loadLatest bool,
// CanWithdrawInvariant invariant.
app.mm.SetOrderBeginBlockers(mint.ModuleName, distr.ModuleName, slashing.ModuleName, validatorvesting.ModuleName)
app.mm.SetOrderEndBlockers(crisis.ModuleName, gov.ModuleName, staking.ModuleName)
app.mm.SetOrderEndBlockers(crisis.ModuleName, gov.ModuleName, staking.ModuleName, pricefeed.ModuleName, auction.ModuleName) // TODO is this correct order?
// Note: genutils must occur after staking so that pools are properly
// initialized with tokens from genesis accounts.
@ -245,6 +289,7 @@ func NewApp(logger log.Logger, db dbm.DB, traceStore io.Writer, loadLatest bool,
auth.ModuleName, validatorvesting.ModuleName, distr.ModuleName,
staking.ModuleName, bank.ModuleName, slashing.ModuleName,
gov.ModuleName, mint.ModuleName, supply.ModuleName, crisis.ModuleName, genutil.ModuleName,
pricefeed.ModuleName, cdp.ModuleName, auction.ModuleName, liquidator.ModuleName, // TODO is this order ok?
)
app.mm.RegisterInvariants(&app.crisisKeeper)

136
app/test_common.go Normal file
View File

@ -0,0 +1,136 @@
package app
import (
"math/rand"
"testing"
"github.com/stretchr/testify/require"
abci "github.com/tendermint/tendermint/abci/types"
"github.com/tendermint/tendermint/crypto"
"github.com/tendermint/tendermint/crypto/ed25519"
"github.com/tendermint/tendermint/crypto/secp256k1"
"github.com/tendermint/tendermint/libs/log"
tmdb "github.com/tendermint/tm-db"
"github.com/cosmos/cosmos-sdk/codec"
sdk "github.com/cosmos/cosmos-sdk/types"
"github.com/cosmos/cosmos-sdk/x/auth"
authexported "github.com/cosmos/cosmos-sdk/x/auth/exported"
"github.com/cosmos/cosmos-sdk/x/bank"
"github.com/cosmos/cosmos-sdk/x/crisis"
"github.com/cosmos/cosmos-sdk/x/distribution"
"github.com/cosmos/cosmos-sdk/x/gov"
"github.com/cosmos/cosmos-sdk/x/mint"
"github.com/cosmos/cosmos-sdk/x/params"
"github.com/cosmos/cosmos-sdk/x/slashing"
"github.com/cosmos/cosmos-sdk/x/staking"
"github.com/cosmos/cosmos-sdk/x/supply"
"github.com/kava-labs/kava/x/auction"
"github.com/kava-labs/kava/x/cdp"
"github.com/kava-labs/kava/x/liquidator"
"github.com/kava-labs/kava/x/pricefeed"
validatorvesting "github.com/kava-labs/kava/x/validator-vesting"
)
// TestApp is a simple wrapper around an App. It exposes internal keepers for use in integration tests.
// This file also contains test helpers. Ideally they would be in separate package.
// Basic Usage:
// Create a test app with NewTestApp, then all keepers and their methods can be accessed for test setup and execution.
// Advanced Usage:
// Some tests call for an app to be initialized with some state. This can be achieved through keeper method calls (ie keeper.SetParams(...)).
// However this leads to a lot of duplicated logic similar to InitGenesis methods.
// So TestApp.InitializeFromGenesisStates() will call InitGenesis with the default genesis state.
// and TestApp.InitializeFromGenesisStates(authState, cdpState) will do the same but overwrite the auth and cdp sections of the default genesis state
// Creating the genesis states can be combersome, but helper methods can make it easier such as NewAuthGenStateFromAccounts below.
type TestApp struct {
App
}
func NewTestApp() TestApp {
db := tmdb.NewMemDB()
app := NewApp(log.NewNopLogger(), db, nil, true, 0)
return TestApp{App: *app}
}
func (tApp TestApp) GetAccountKeeper() auth.AccountKeeper { return tApp.accountKeeper }
func (tApp TestApp) GetBankKeeper() bank.Keeper { return tApp.bankKeeper }
func (tApp TestApp) GetSupplyKeeper() supply.Keeper { return tApp.supplyKeeper }
func (tApp TestApp) GetStakingKeeper() staking.Keeper { return tApp.stakingKeeper }
func (tApp TestApp) GetSlashingKeeper() slashing.Keeper { return tApp.slashingKeeper }
func (tApp TestApp) GetMintKeeper() mint.Keeper { return tApp.mintKeeper }
func (tApp TestApp) GetDistrKeeper() distribution.Keeper { return tApp.distrKeeper }
func (tApp TestApp) GetGovKeeper() gov.Keeper { return tApp.govKeeper }
func (tApp TestApp) GetCrisisKeeper() crisis.Keeper { return tApp.crisisKeeper }
func (tApp TestApp) GetParamsKeeper() params.Keeper { return tApp.paramsKeeper }
func (tApp TestApp) GetVVKeeper() validatorvesting.Keeper { return tApp.vvKeeper }
func (tApp TestApp) GetAuctionKeeper() auction.Keeper { return tApp.auctionKeeper }
func (tApp TestApp) GetCDPKeeper() cdp.Keeper { return tApp.cdpKeeper }
func (tApp TestApp) GetLiquidatorKeeper() liquidator.Keeper { return tApp.liquidatorKeeper }
func (tApp TestApp) GetPriceFeedKeeper() pricefeed.Keeper { return tApp.pricefeedKeeper }
// This calls InitChain on the app using the default genesis state, overwitten with any passed in genesis states
func (tApp TestApp) InitializeFromGenesisStates(genesisStates ...GenesisState) TestApp {
// Create a default genesis state and overwrite with provided values
genesisState := NewDefaultGenesisState()
for _, state := range genesisStates {
for k, v := range state {
genesisState[k] = v
}
}
// Initialize the chain
stateBytes, err := codec.MarshalJSONIndent(tApp.cdc, genesisState)
if err != nil {
panic(err)
}
tApp.InitChain(
abci.RequestInitChain{
Validators: []abci.ValidatorUpdate{},
AppStateBytes: stateBytes,
},
)
tApp.Commit()
tApp.BeginBlock(abci.RequestBeginBlock{Header: abci.Header{Height: tApp.LastBlockHeight() + 1}})
return tApp
}
func (tApp TestApp) CheckBalance(t *testing.T, ctx sdk.Context, owner sdk.AccAddress, expectedCoins sdk.Coins) {
actualCoins := tApp.GetAccountKeeper().GetAccount(ctx, owner).GetCoins()
require.Equal(t, expectedCoins, actualCoins)
}
// Create a new auth genesis state from some addresses and coins. The state is returned marshalled into a map.
func NewAuthGenState(addresses []sdk.AccAddress, coins []sdk.Coins) GenesisState {
// Create GenAccounts
accounts := authexported.GenesisAccounts{}
for i := range addresses {
accounts = append(accounts, auth.NewBaseAccount(addresses[i], coins[i], nil, 0, 0))
}
// Create the auth genesis state
authGenesis := auth.NewGenesisState(auth.DefaultParams(), accounts)
return GenesisState{auth.ModuleName: auth.ModuleCdc.MustMarshalJSON(authGenesis)}
}
// GeneratePrivKeyAddressPairsFromRand generates (deterministically) a total of n private keys and addresses.
// TODO only generate secp256 keys?
func GeneratePrivKeyAddressPairs(n int) (keys []crypto.PrivKey, addrs []sdk.AccAddress) {
r := rand.New(rand.NewSource(12345)) // make the generation deterministic
keys = make([]crypto.PrivKey, n)
addrs = make([]sdk.AccAddress, n)
for i := 0; i < n; i++ {
secret := make([]byte, 32)
_, err := r.Read(secret)
if err != nil {
panic("Could not read randomness")
}
if r.Int63()%2 == 0 {
keys[i] = secp256k1.GenPrivKeySecp256k1(secret)
} else {
keys[i] = ed25519.GenPrivKeyFromSecret(secret)
}
addrs[i] = sdk.AccAddress(keys[i].PubKey().Address())
}
return
}

View File

@ -1,4 +1,4 @@
package auction
package auction_test
import (
"testing"
@ -6,23 +6,43 @@ import (
sdk "github.com/cosmos/cosmos-sdk/types"
"github.com/stretchr/testify/require"
abci "github.com/tendermint/tendermint/abci/types"
"github.com/kava-labs/kava/app"
"github.com/kava-labs/kava/x/auction"
)
func TestKeeper_EndBlocker(t *testing.T) {
// setup keeper and auction
mapp, keeper, addresses, _ := setUpMockApp()
header := abci.Header{Height: mapp.LastBlockHeight() + 1}
mapp.BeginBlock(abci.RequestBeginBlock{Header: header})
ctx := mapp.BaseApp.NewContext(false, header)
// Setup
_, addrs := app.GeneratePrivKeyAddressPairs(1)
seller := addrs[0]
seller := addresses[0]
keeper.StartForwardAuction(ctx, seller, sdk.NewInt64Coin("token1", 20), sdk.NewInt64Coin("token2", 0))
tApp := app.NewTestApp()
tApp.InitializeFromGenesisStates(
app.NewAuthGenState(addrs, []sdk.Coins{cs(c("token1", 100), c("token2", 100))}),
)
// run the endblocker, simulating a block height after auction expiry
expiryBlock := ctx.BlockHeight() + int64(DefaultMaxAuctionDuration)
EndBlocker(ctx.WithBlockHeight(expiryBlock), keeper)
ctx := tApp.NewContext(true, abci.Header{})
keeper := tApp.GetAuctionKeeper()
// check auction has been closed
_, found := keeper.GetAuction(ctx, 0)
auctionID, err := keeper.StartForwardAuction(ctx, seller, c("token1", 20), c("token2", 0))
require.NoError(t, err)
// Run the endblocker, simulating a block height just before auction expiry
preExpiryHeight := ctx.BlockHeight() + int64(auction.DefaultMaxAuctionDuration) - 1
auction.EndBlocker(ctx.WithBlockHeight(preExpiryHeight), keeper)
// Check auction has not been closed yet
_, found := keeper.GetAuction(ctx, auctionID)
require.True(t, found)
// Run the endblocker, simulating a block height just after auction expiry
expiryHeight := preExpiryHeight + 1
auction.EndBlocker(ctx.WithBlockHeight(expiryHeight), keeper)
// Check auction has been closed
_, found = keeper.GetAuction(ctx, auctionID)
require.False(t, found)
}
func c(denom string, amount int64) sdk.Coin { return sdk.NewInt64Coin(denom, amount) }
func cs(coins ...sdk.Coin) sdk.Coins { return sdk.NewCoins(coins...) }

View File

@ -1,171 +0,0 @@
package auction
import (
"testing"
sdk "github.com/cosmos/cosmos-sdk/types"
"github.com/cosmos/cosmos-sdk/x/bank"
"github.com/cosmos/cosmos-sdk/x/mock"
abci "github.com/tendermint/tendermint/abci/types"
"github.com/tendermint/tendermint/crypto"
)
// TestApp contans several basic integration tests of creating an auction, placing a bid, and the auction closing.
func TestApp_ForwardAuction(t *testing.T) {
// Setup
mapp, keeper, addresses, privKeys := setUpMockApp()
seller := addresses[0]
//sellerKey := privKeys[0]
buyer := addresses[1]
buyerKey := privKeys[1]
// Create a block where an auction is started (lot: 20 t1, initialBid: 0 t2)
header := abci.Header{Height: mapp.LastBlockHeight() + 1}
mapp.BeginBlock(abci.RequestBeginBlock{Header: header})
ctx := mapp.BaseApp.NewContext(false, header) // make sure first arg is false, otherwise no db writes
keeper.StartForwardAuction(ctx, seller, sdk.NewInt64Coin("token1", 20), sdk.NewInt64Coin("token2", 0)) // lot, initialBid
mapp.EndBlock(abci.RequestEndBlock{})
mapp.Commit()
// Check seller's coins have decreased
mock.CheckBalance(t, mapp, seller, sdk.NewCoins(sdk.NewInt64Coin("token1", 80), sdk.NewInt64Coin("token2", 100)))
// Deliver a block that contains a PlaceBid tx (bid: 10 t2, lot: same as starting)
msgs := []sdk.Msg{NewMsgPlaceBid(0, buyer, sdk.NewInt64Coin("token2", 10), sdk.NewInt64Coin("token1", 20))} // bid, lot
header = abci.Header{Height: mapp.LastBlockHeight() + 1}
mock.SignCheckDeliver(t, mapp.Cdc, mapp.BaseApp, header, msgs, []uint64{1}, []uint64{0}, true, true, buyerKey) // account number for the buyer account is 1
// Check buyer's coins have decreased
mock.CheckBalance(t, mapp, buyer, sdk.NewCoins(sdk.NewInt64Coin("token1", 100), sdk.NewInt64Coin("token2", 90)))
// Check seller's coins have increased
mock.CheckBalance(t, mapp, seller, sdk.NewCoins(sdk.NewInt64Coin("token1", 80), sdk.NewInt64Coin("token2", 110)))
// Deliver empty blocks until the auction should be closed (bid placed on block 3)
// TODO is there a way of skipping ahead? This takes a while and prints a lot.
for h := mapp.LastBlockHeight() + 1; h < int64(DefaultMaxBidDuration)+4; h++ {
mapp.BeginBlock(abci.RequestBeginBlock{Header: abci.Header{Height: h}})
mapp.EndBlock(abci.RequestEndBlock{Height: h})
mapp.Commit()
}
// Check buyer's coins increased
mock.CheckBalance(t, mapp, buyer, sdk.NewCoins(sdk.NewInt64Coin("token1", 120), sdk.NewInt64Coin("token2", 90)))
}
func TestApp_ReverseAuction(t *testing.T) {
// Setup
mapp, keeper, addresses, privKeys := setUpMockApp()
seller := addresses[0]
sellerKey := privKeys[0]
buyer := addresses[1]
//buyerKey := privKeys[1]
// Create a block where an auction is started
header := abci.Header{Height: mapp.LastBlockHeight() + 1}
mapp.BeginBlock(abci.RequestBeginBlock{Header: header})
ctx := mapp.BaseApp.NewContext(false, header)
keeper.StartReverseAuction(ctx, buyer, sdk.NewInt64Coin("token1", 20), sdk.NewInt64Coin("token2", 99)) // buyer, bid, initialLot
mapp.EndBlock(abci.RequestEndBlock{})
mapp.Commit()
// Check buyer's coins have decreased
mock.CheckBalance(t, mapp, buyer, sdk.NewCoins(sdk.NewInt64Coin("token1", 100), sdk.NewInt64Coin("token2", 1)))
// Deliver a block that contains a PlaceBid tx
msgs := []sdk.Msg{NewMsgPlaceBid(0, seller, sdk.NewInt64Coin("token1", 20), sdk.NewInt64Coin("token2", 10))} // bid, lot
header = abci.Header{Height: mapp.LastBlockHeight() + 1}
mock.SignCheckDeliver(t, mapp.Cdc, mapp.BaseApp, header, msgs, []uint64{0}, []uint64{0}, true, true, sellerKey)
// Check seller's coins have decreased
mock.CheckBalance(t, mapp, seller, sdk.NewCoins(sdk.NewInt64Coin("token1", 80), sdk.NewInt64Coin("token2", 100)))
// Check buyer's coins have increased
mock.CheckBalance(t, mapp, buyer, sdk.NewCoins(sdk.NewInt64Coin("token1", 120), sdk.NewInt64Coin("token2", 90)))
// Deliver empty blocks until the auction should be closed (bid placed on block 3)
for h := mapp.LastBlockHeight() + 1; h < int64(DefaultMaxBidDuration)+4; h++ {
mapp.BeginBlock(abci.RequestBeginBlock{Header: abci.Header{Height: h}})
mapp.EndBlock(abci.RequestEndBlock{Height: h})
mapp.Commit()
}
// Check seller's coins increased
mock.CheckBalance(t, mapp, seller, sdk.NewCoins(sdk.NewInt64Coin("token1", 80), sdk.NewInt64Coin("token2", 110)))
}
func TestApp_ForwardReverseAuction(t *testing.T) {
// Setup
mapp, keeper, addresses, privKeys := setUpMockApp()
seller := addresses[0]
//sellerKey := privKeys[0]
buyer := addresses[1]
buyerKey := privKeys[1]
recipient := addresses[2]
// Create a block where an auction is started
header := abci.Header{Height: mapp.LastBlockHeight() + 1}
mapp.BeginBlock(abci.RequestBeginBlock{Header: header})
ctx := mapp.BaseApp.NewContext(false, header)
keeper.StartForwardReverseAuction(ctx, seller, sdk.NewInt64Coin("token1", 20), sdk.NewInt64Coin("token2", 50), recipient) // seller, lot, maxBid, otherPerson
mapp.EndBlock(abci.RequestEndBlock{})
mapp.Commit()
// Check seller's coins have decreased
mock.CheckBalance(t, mapp, seller, sdk.NewCoins(sdk.NewInt64Coin("token1", 80), sdk.NewInt64Coin("token2", 100)))
// Deliver a block that contains a PlaceBid tx
msgs := []sdk.Msg{NewMsgPlaceBid(0, buyer, sdk.NewInt64Coin("token2", 50), sdk.NewInt64Coin("token1", 15))} // bid, lot
header = abci.Header{Height: mapp.LastBlockHeight() + 1}
mock.SignCheckDeliver(t, mapp.Cdc, mapp.BaseApp, header, msgs, []uint64{1}, []uint64{0}, true, true, buyerKey)
// Check bidder's coins have decreased
mock.CheckBalance(t, mapp, buyer, sdk.NewCoins(sdk.NewInt64Coin("token1", 100), sdk.NewInt64Coin("token2", 50)))
// Check seller's coins have increased
mock.CheckBalance(t, mapp, seller, sdk.NewCoins(sdk.NewInt64Coin("token1", 80), sdk.NewInt64Coin("token2", 150)))
// Check "recipient" has received coins
mock.CheckBalance(t, mapp, recipient, sdk.NewCoins(sdk.NewInt64Coin("token1", 105), sdk.NewInt64Coin("token2", 100)))
// Deliver empty blocks until the auction should be closed (bid placed on block 3)
for h := mapp.LastBlockHeight() + 1; h < int64(DefaultMaxBidDuration)+4; h++ {
mapp.BeginBlock(abci.RequestBeginBlock{Header: abci.Header{Height: h}})
mapp.EndBlock(abci.RequestEndBlock{Height: h})
mapp.Commit()
}
// Check buyer's coins increased
mock.CheckBalance(t, mapp, buyer, sdk.NewCoins(sdk.NewInt64Coin("token1", 115), sdk.NewInt64Coin("token2", 50)))
}
func setUpMockApp() (*mock.App, Keeper, []sdk.AccAddress, []crypto.PrivKey) {
// Create uninitialized mock app
mapp := mock.NewApp()
// Register codecs
RegisterCodec(mapp.Cdc)
// Create keepers
keyAuction := sdk.NewKVStoreKey("auction")
blacklistedAddrs := make(map[string]bool)
bankKeeper := bank.NewBaseKeeper(mapp.AccountKeeper, mapp.ParamsKeeper.Subspace(bank.DefaultParamspace), bank.DefaultCodespace, blacklistedAddrs)
auctionKeeper := NewKeeper(mapp.Cdc, bankKeeper, keyAuction, mapp.ParamsKeeper.Subspace(DefaultParamspace))
// Register routes
mapp.Router().AddRoute("auction", NewHandler(auctionKeeper))
// Add endblocker
mapp.SetEndBlocker(
func(ctx sdk.Context, req abci.RequestEndBlock) abci.ResponseEndBlock {
EndBlocker(ctx, auctionKeeper)
return abci.ResponseEndBlock{}
},
)
// Mount and load the stores
err := mapp.CompleteSetup(keyAuction)
if err != nil {
panic("mock app setup failed")
}
// Create a bunch (ie 10) of pre-funded accounts to use for tests
genAccs, addrs, _, privKeys := mock.CreateGenAccounts(10, sdk.NewCoins(sdk.NewInt64Coin("token1", 100), sdk.NewInt64Coin("token2", 100)))
mock.SetGenesis(mapp, genAccs)
return mapp, auctionKeeper, addrs, privKeys
}

View File

@ -144,8 +144,8 @@ func (k Keeper) CloseAuction(ctx sdk.Context, auctionID types.ID) sdk.Error {
return err
}
// delete auction from store (and queue)
k.deleteAuction(ctx, auctionID)
// Delete auction from store (and queue)
k.DeleteAuction(ctx, auctionID)
return nil
}
@ -204,7 +204,7 @@ func (k Keeper) SetAuction(ctx sdk.Context, auction types.Auction) {
store.Set(k.getAuctionKey(auction.GetID()), bz)
// add to the queue
k.insertIntoQueue(ctx, auction.GetEndTime(), auction.GetID())
k.InsertIntoQueue(ctx, auction.GetEndTime(), auction.GetID())
}
// getAuction gets an auction from the store by auctionID
@ -221,8 +221,8 @@ func (k Keeper) GetAuction(ctx sdk.Context, auctionID types.ID) (types.Auction,
return auction, true
}
// deleteAuction removes an auction from the store without any validation
func (k Keeper) deleteAuction(ctx sdk.Context, auctionID types.ID) {
// DeleteAuction removes an auction from the store without any validation
func (k Keeper) DeleteAuction(ctx sdk.Context, auctionID types.ID) {
// remove from queue
auction, found := k.GetAuction(ctx, auctionID)
if found {
@ -245,7 +245,7 @@ func (k Keeper) getAuctionKey(auctionID types.ID) []byte {
}
// Inserts a AuctionID into the queue at endTime
func (k Keeper) insertIntoQueue(ctx sdk.Context, endTime types.EndTime, auctionID types.ID) {
func (k Keeper) InsertIntoQueue(ctx sdk.Context, endTime types.EndTime, auctionID types.ID) {
// get the store
store := ctx.KVStore(k.storeKey)
// marshal thing to be inserted

View File

@ -1,21 +1,129 @@
package keeper
package keeper_test
import (
"testing"
sdk "github.com/cosmos/cosmos-sdk/types"
"github.com/kava-labs/kava/x/auction/types"
"github.com/stretchr/testify/require"
abci "github.com/tendermint/tendermint/abci/types"
"github.com/kava-labs/kava/app"
"github.com/kava-labs/kava/x/auction/keeper"
"github.com/kava-labs/kava/x/auction/types"
)
func TestKeeper_ForwardAuction(t *testing.T) {
// Setup
_, addrs := app.GeneratePrivKeyAddressPairs(2)
seller := addrs[0]
buyer := addrs[1]
tApp := app.NewTestApp()
tApp.InitializeFromGenesisStates(
app.NewAuthGenState(addrs, []sdk.Coins{cs(c("token1", 100), c("token2", 100)), cs(c("token1", 100), c("token2", 100))}),
)
ctx := tApp.NewContext(false, abci.Header{})
keeper := tApp.GetAuctionKeeper()
// Create an auction (lot: 20 t1, initialBid: 0 t2)
auctionID, err := keeper.StartForwardAuction(ctx, seller, c("token1", 20), c("token2", 0)) // lot, initialBid
require.NoError(t, err)
// Check seller's coins have decreased
tApp.CheckBalance(t, ctx, seller, cs(c("token1", 80), c("token2", 100)))
// PlaceBid (bid: 10 t2, lot: same as starting)
require.NoError(t, keeper.PlaceBid(ctx, 0, buyer, c("token2", 10), c("token1", 20))) // bid, lot
// Check buyer's coins have decreased
tApp.CheckBalance(t, ctx, buyer, cs(c("token1", 100), c("token2", 90)))
// Check seller's coins have increased
tApp.CheckBalance(t, ctx, seller, cs(c("token1", 80), c("token2", 110)))
// Close auction at just after auction expiry
ctx = ctx.WithBlockHeight(int64(types.DefaultMaxBidDuration))
require.NoError(t, keeper.CloseAuction(ctx, auctionID))
// Check buyer's coins increased
tApp.CheckBalance(t, ctx, buyer, cs(c("token1", 120), c("token2", 90)))
}
func TestKeeper_ReverseAuction(t *testing.T) {
// Setup
_, addrs := app.GeneratePrivKeyAddressPairs(2)
seller := addrs[0]
buyer := addrs[1]
tApp := app.NewTestApp()
tApp.InitializeFromGenesisStates(
app.NewAuthGenState(addrs, []sdk.Coins{cs(c("token1", 100), c("token2", 100)), cs(c("token1", 100), c("token2", 100))}),
)
ctx := tApp.NewContext(false, abci.Header{})
keeper := tApp.GetAuctionKeeper()
// Start auction
auctionID, err := keeper.StartReverseAuction(ctx, buyer, c("token1", 20), c("token2", 99)) // buyer, bid, initialLot
require.NoError(t, err)
// Check buyer's coins have decreased
tApp.CheckBalance(t, ctx, buyer, cs(c("token1", 100), c("token2", 1)))
// Place a bid
require.NoError(t, keeper.PlaceBid(ctx, 0, seller, c("token1", 20), c("token2", 10))) // bid, lot
// Check seller's coins have decreased
tApp.CheckBalance(t, ctx, seller, cs(c("token1", 80), c("token2", 100)))
// Check buyer's coins have increased
tApp.CheckBalance(t, ctx, buyer, cs(c("token1", 120), c("token2", 90)))
// Close auction at just after auction expiry
ctx = ctx.WithBlockHeight(int64(types.DefaultMaxBidDuration))
require.NoError(t, keeper.CloseAuction(ctx, auctionID))
// Check seller's coins increased
tApp.CheckBalance(t, ctx, seller, cs(c("token1", 80), c("token2", 110)))
}
func TestKeeper_ForwardReverseAuction(t *testing.T) {
// Setup
_, addrs := app.GeneratePrivKeyAddressPairs(3)
seller := addrs[0]
buyer := addrs[1]
recipient := addrs[2]
tApp := app.NewTestApp()
tApp.InitializeFromGenesisStates(
app.NewAuthGenState(addrs, []sdk.Coins{cs(c("token1", 100), c("token2", 100)), cs(c("token1", 100), c("token2", 100)), cs(c("token1", 100), c("token2", 100))}),
)
ctx := tApp.NewContext(false, abci.Header{})
keeper := tApp.GetAuctionKeeper()
// Start auction
auctionID, err := keeper.StartForwardReverseAuction(ctx, seller, c("token1", 20), c("token2", 50), recipient) // seller, lot, maxBid, otherPerson
require.NoError(t, err)
// Check seller's coins have decreased
tApp.CheckBalance(t, ctx, seller, cs(c("token1", 80), c("token2", 100)))
// Place a bid
require.NoError(t, keeper.PlaceBid(ctx, 0, buyer, c("token2", 50), c("token1", 15))) // bid, lot
// Check bidder's coins have decreased
tApp.CheckBalance(t, ctx, buyer, cs(c("token1", 100), c("token2", 50)))
// Check seller's coins have increased
tApp.CheckBalance(t, ctx, seller, cs(c("token1", 80), c("token2", 150)))
// Check "recipient" has received coins
tApp.CheckBalance(t, ctx, recipient, cs(c("token1", 105), c("token2", 100)))
// Close auction at just after auction expiry
ctx = ctx.WithBlockHeight(int64(types.DefaultMaxBidDuration))
require.NoError(t, keeper.CloseAuction(ctx, auctionID))
// Check buyer's coins increased
tApp.CheckBalance(t, ctx, buyer, cs(c("token1", 115), c("token2", 50)))
}
func TestKeeper_SetGetDeleteAuction(t *testing.T) {
// setup keeper, create auction
mapp, keeper, addresses, _ := setUpMockApp()
header := abci.Header{Height: mapp.LastBlockHeight() + 1}
mapp.BeginBlock(abci.RequestBeginBlock{Header: header}) // Without this it panics about "invalid memory address or nil pointer dereference"
ctx := mapp.BaseApp.NewContext(false, header)
auction, _ := types.NewForwardAuction(addresses[0], sdk.NewInt64Coin("usdx", 100), sdk.NewInt64Coin("kava", 0), types.EndTime(1000))
_, addrs := app.GeneratePrivKeyAddressPairs(1)
tApp := app.NewTestApp()
keeper := tApp.GetAuctionKeeper()
ctx := tApp.NewContext(true, abci.Header{})
auction, _ := types.NewForwardAuction(addrs[0], c("usdx", 100), c("kava", 0), types.EndTime(1000))
id := types.ID(5)
auction.SetID(id)
@ -26,15 +134,13 @@ func TestKeeper_SetGetDeleteAuction(t *testing.T) {
// check before and after match
require.True(t, found)
require.Equal(t, &auction, readAuction)
t.Log(auction)
t.Log(readAuction.GetID())
// check auction is in queue
iter := keeper.GetQueueIterator(ctx, 100000)
require.Equal(t, 1, len(convertIteratorToSlice(keeper, iter)))
iter.Close()
// delete auction
keeper.deleteAuction(ctx, id)
keeper.DeleteAuction(ctx, id)
// check auction does not exist
_, found = keeper.GetAuction(ctx, id)
@ -49,10 +155,10 @@ func TestKeeper_SetGetDeleteAuction(t *testing.T) {
// TODO convert to table driven test with more test cases
func TestKeeper_ExpiredAuctionQueue(t *testing.T) {
// setup keeper
mapp, keeper, _, _ := setUpMockApp()
header := abci.Header{Height: mapp.LastBlockHeight() + 1}
mapp.BeginBlock(abci.RequestBeginBlock{Header: header})
ctx := mapp.BaseApp.NewContext(false, header)
tApp := app.NewTestApp()
keeper := tApp.GetAuctionKeeper()
ctx := tApp.NewContext(true, abci.Header{})
// create an example queue
type queue []struct {
endTime types.EndTime
@ -62,7 +168,7 @@ func TestKeeper_ExpiredAuctionQueue(t *testing.T) {
// write and read queue
for _, v := range q {
keeper.insertIntoQueue(ctx, v.endTime, v.auctionID)
keeper.InsertIntoQueue(ctx, v.endTime, v.auctionID)
}
iter := keeper.GetQueueIterator(ctx, 1000)
@ -70,19 +176,22 @@ func TestKeeper_ExpiredAuctionQueue(t *testing.T) {
i := 0
for ; iter.Valid(); iter.Next() {
var auctionID types.ID
keeper.cdc.MustUnmarshalBinaryLengthPrefixed(iter.Value(), &auctionID)
tApp.Codec().MustUnmarshalBinaryLengthPrefixed(iter.Value(), &auctionID)
require.Equal(t, q[i].auctionID, auctionID)
i++
}
}
func convertIteratorToSlice(keeper Keeper, iterator sdk.Iterator) []types.ID {
func convertIteratorToSlice(keeper keeper.Keeper, iterator sdk.Iterator) []types.ID {
var queue []types.ID
for ; iterator.Valid(); iterator.Next() {
var auctionID types.ID
keeper.cdc.MustUnmarshalBinaryLengthPrefixed(iterator.Value(), &auctionID)
types.ModuleCdc.MustUnmarshalBinaryLengthPrefixed(iterator.Value(), &auctionID)
queue = append(queue, auctionID)
}
return queue
}
func c(denom string, amount int64) sdk.Coin { return sdk.NewInt64Coin(denom, amount) }
func cs(coins ...sdk.Coin) sdk.Coins { return sdk.NewCoins(coins...) }

View File

@ -1,35 +0,0 @@
package keeper
import (
sdk "github.com/cosmos/cosmos-sdk/types"
"github.com/cosmos/cosmos-sdk/x/bank"
"github.com/cosmos/cosmos-sdk/x/mock"
"github.com/kava-labs/kava/x/auction/types"
"github.com/tendermint/tendermint/crypto"
)
func setUpMockApp() (*mock.App, Keeper, []sdk.AccAddress, []crypto.PrivKey) {
// Create uninitialized mock app
mapp := mock.NewApp()
// Register codecs
types.RegisterCodec(mapp.Cdc)
// Create keepers
keyAuction := sdk.NewKVStoreKey("auction")
blacklistedAddrs := make(map[string]bool)
bankKeeper := bank.NewBaseKeeper(mapp.AccountKeeper, mapp.ParamsKeeper.Subspace(bank.DefaultParamspace), bank.DefaultCodespace, blacklistedAddrs)
auctionKeeper := NewKeeper(mapp.Cdc, bankKeeper, keyAuction, mapp.ParamsKeeper.Subspace(types.DefaultParamspace))
// Mount and load the stores
err := mapp.CompleteSetup(keyAuction)
if err != nil {
panic("mock app setup failed")
}
// Create a bunch (ie 10) of pre-funded accounts to use for tests
genAccs, addrs, _, privKeys := mock.CreateGenAccounts(10, sdk.NewCoins(sdk.NewInt64Coin("token1", 100), sdk.NewInt64Coin("token2", 100)))
mock.SetGenesis(mapp, genAccs)
return mapp, auctionKeeper, addrs, privKeys
}

View File

@ -1,73 +1,55 @@
package cdp
package cdp_test
import (
"testing"
"time"
"github.com/cosmos/cosmos-sdk/simapp"
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"
"github.com/kava-labs/kava/app"
"github.com/kava-labs/kava/x/cdp"
)
func TestApp_CreateModifyDeleteCDP(t *testing.T) {
// Setup
mapp, keeper, pfKeeper := setUpMockAppWithoutGenesis()
genAccs, addrs, _, privKeys := mock.CreateGenAccounts(1, cs(c("xrp", 100)))
tApp := app.NewTestApp()
privKeys, addrs := app.GeneratePrivKeyAddressPairs(1)
testAddr := addrs[0]
testPrivKey := privKeys[0]
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, Time: tmtime.Now()}
mapp.BeginBlock(abci.RequestBeginBlock{Header: header})
ctx := mapp.BaseApp.NewContext(false, header)
params := CdpParams{
GlobalDebtLimit: sdk.NewInt(100000),
CollateralParams: []CollateralParams{
{
Denom: "xrp",
LiquidationRatio: sdk.MustNewDecFromStr("1.5"),
DebtLimit: sdk.NewInt(10000),
},
},
StableDenoms: []string{"usdx"},
}
keeper.SetParams(ctx, params)
keeper.SetGlobalDebt(ctx, sdk.NewInt(0))
ap := pricefeed.Params{
Markets: []pricefeed.Market{
pricefeed.Market{
MarketID: "xrp", BaseAsset: "xrp",
QuoteAsset: "usd", Oracles: pricefeed.Oracles{}, Active: true},
},
}
pfKeeper.SetParams(ctx, ap)
pfKeeper.SetPrice(
ctx, sdk.AccAddress{}, "xrp",
sdk.MustNewDecFromStr("1.00"),
header.Time.Add(time.Hour*1))
pfKeeper.SetCurrentPrices(ctx, "xrp")
mapp.EndBlock(abci.RequestEndBlock{})
mapp.Commit()
tApp.InitializeFromGenesisStates(
app.NewAuthGenState(addrs, []sdk.Coins{cs(c("xrp", 100))}),
NewPFGenState("xrp", d("1.00")),
NewCDPGenState("xrp", d("1.5")),
)
// check balance
ctx := tApp.NewContext(false, abci.Header{})
tApp.CheckBalance(t, ctx, testAddr, cs(c("xrp", 100)))
tApp.EndBlock(abci.RequestEndBlock{})
tApp.Commit()
// Create CDP
msgs := []sdk.Msg{NewMsgCreateOrModifyCDP(testAddr, "xrp", i(10), i(5))}
mock.SignCheckDeliver(t, mapp.Cdc, mapp.BaseApp, abci.Header{Height: mapp.LastBlockHeight() + 1}, msgs, []uint64{0}, []uint64{0}, true, true, testPrivKey)
msgs := []sdk.Msg{cdp.NewMsgCreateOrModifyCDP(testAddr, "xrp", i(10), i(5))}
simapp.SignCheckDeliver(t, tApp.Codec(), tApp.BaseApp, abci.Header{Height: tApp.LastBlockHeight() + 1}, msgs, []uint64{0}, []uint64{0}, true, true, testPrivKey)
mock.CheckBalance(t, mapp, testAddr, cs(c("usdx", 5), c("xrp", 90)))
// check balance
ctx = tApp.NewContext(true, abci.Header{})
tApp.CheckBalance(t, ctx, testAddr, cs(c("usdx", 5), c("xrp", 90)))
// Modify CDP
msgs = []sdk.Msg{NewMsgCreateOrModifyCDP(testAddr, "xrp", i(40), i(5))}
mock.SignCheckDeliver(t, mapp.Cdc, mapp.BaseApp, abci.Header{Height: mapp.LastBlockHeight() + 1}, msgs, []uint64{0}, []uint64{1}, true, true, testPrivKey)
msgs = []sdk.Msg{cdp.NewMsgCreateOrModifyCDP(testAddr, "xrp", i(40), i(5))}
simapp.SignCheckDeliver(t, tApp.Codec(), tApp.BaseApp, abci.Header{Height: tApp.LastBlockHeight() + 1}, msgs, []uint64{0}, []uint64{1}, true, true, testPrivKey)
mock.CheckBalance(t, mapp, testAddr, cs(c("usdx", 10), c("xrp", 50)))
// check balance
ctx = tApp.NewContext(true, abci.Header{})
tApp.CheckBalance(t, ctx, testAddr, cs(c("usdx", 10), c("xrp", 50)))
// Delete CDP
msgs = []sdk.Msg{NewMsgCreateOrModifyCDP(testAddr, "xrp", i(-50), i(-10))}
mock.SignCheckDeliver(t, mapp.Cdc, mapp.BaseApp, abci.Header{Height: mapp.LastBlockHeight() + 1}, msgs, []uint64{0}, []uint64{2}, true, true, testPrivKey)
msgs = []sdk.Msg{cdp.NewMsgCreateOrModifyCDP(testAddr, "xrp", i(-50), i(-10))}
simapp.SignCheckDeliver(t, tApp.Codec(), tApp.BaseApp, abci.Header{Height: tApp.LastBlockHeight() + 1}, msgs, []uint64{0}, []uint64{2}, true, true, testPrivKey)
mock.CheckBalance(t, mapp, testAddr, cs(c("xrp", 100)))
// check balance
ctx = tApp.NewContext(true, abci.Header{})
tApp.CheckBalance(t, ctx, testAddr, cs(c("xrp", 100)))
}

56
x/cdp/integration_test.go Normal file
View File

@ -0,0 +1,56 @@
package cdp_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/pricefeed"
)
// Avoid cluttering test cases with long function name
func i(in int64) sdk.Int { return sdk.NewInt(in) }
func d(str string) sdk.Dec { return sdk.MustNewDecFromStr(str) }
func c(denom string, amount int64) sdk.Coin { return sdk.NewInt64Coin(denom, amount) }
func cs(coins ...sdk.Coin) sdk.Coins { return sdk.NewCoins(coins...) }
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.Now().Add(1 * time.Hour),
},
},
}
return app.GenesisState{pricefeed.ModuleName: pricefeed.ModuleCdc.MustMarshalJSON(pfGenesis)}
}
func NewCDPGenState(asset string, liquidationRatio sdk.Dec) app.GenesisState {
cdpGenesis := cdp.GenesisState{
Params: cdp.CdpParams{
GlobalDebtLimit: sdk.NewInt(1000000),
CollateralParams: []cdp.CollateralParams{
{
Denom: asset,
LiquidationRatio: liquidationRatio,
DebtLimit: sdk.NewInt(500000),
},
},
},
GlobalDebt: sdk.ZeroInt(),
CDPs: cdp.CDPs{},
}
return app.GenesisState{cdp.ModuleName: cdp.ModuleCdc.MustMarshalJSON(cdpGenesis)}
}

View File

@ -1,19 +1,19 @@
package keeper
package keeper_test
import (
"testing"
sdk "github.com/cosmos/cosmos-sdk/types"
"github.com/cosmos/cosmos-sdk/x/auth"
authexported "github.com/cosmos/cosmos-sdk/x/auth/exported"
"github.com/cosmos/cosmos-sdk/x/mock"
"github.com/stretchr/testify/require"
abci "github.com/tendermint/tendermint/abci/types"
"github.com/kava-labs/kava/app"
keep "github.com/kava-labs/kava/x/cdp/keeper"
)
// Test the bank functionality of the CDP keeper
func TestKeeper_AddSubtractGetCoins(t *testing.T) {
_, addrs := mock.GeneratePrivKeyAddressPairs(1)
_, addrs := app.GeneratePrivKeyAddressPairs(1)
normalAddr := addrs[0]
tests := []struct {
@ -25,28 +25,24 @@ func TestKeeper_AddSubtractGetCoins(t *testing.T) {
}{
{"addNormalAddress", normalAddr, true, cs(c("usdx", 53)), cs(c("usdx", 153), c("kava", 100))},
{"subNormalAddress", normalAddr, false, cs(c("usdx", 53)), cs(c("usdx", 47), c("kava", 100))},
{"addLiquidatorStable", LiquidatorAccountAddress, true, cs(c("usdx", 53)), cs(c("usdx", 153))},
{"subLiquidatorStable", LiquidatorAccountAddress, false, cs(c("usdx", 53)), cs(c("usdx", 47))},
{"addLiquidatorGov", LiquidatorAccountAddress, true, cs(c("kava", 53)), cs(c("usdx", 100))}, // no change to balance
{"subLiquidatorGov", LiquidatorAccountAddress, false, cs(c("kava", 53)), cs(c("usdx", 100))}, // no change to balance
{"addLiquidatorStable", keep.LiquidatorAccountAddress, true, cs(c("usdx", 53)), cs(c("usdx", 153))},
{"subLiquidatorStable", keep.LiquidatorAccountAddress, false, cs(c("usdx", 53)), cs(c("usdx", 47))},
{"addLiquidatorGov", keep.LiquidatorAccountAddress, true, cs(c("kava", 53)), cs(c("usdx", 100))}, // no change to balance
{"subLiquidatorGov", keep.LiquidatorAccountAddress, false, cs(c("kava", 53)), cs(c("usdx", 100))}, // no change to balance
}
for _, tc := range tests {
t.Run(tc.name, func(t *testing.T) {
// setup keeper
mapp, keeper, _, _ := setUpMockAppWithoutGenesis()
// initialize an account with coins
genAcc := auth.BaseAccount{
Address: normalAddr,
Coins: cs(c("usdx", 100), c("kava", 100)),
}
mock.SetGenesis(mapp, []authexported.Account{&genAcc})
// setup app with an account
tApp := app.NewTestApp()
tApp.InitializeFromGenesisStates(
app.NewAuthGenState([]sdk.AccAddress{normalAddr}, []sdk.Coins{cs(c("usdx", 100), c("kava", 100))}),
)
// create a new context and setup the liquidator account
header := abci.Header{Height: mapp.LastBlockHeight() + 1}
mapp.BeginBlock(abci.RequestBeginBlock{Header: header})
ctx := mapp.BaseApp.NewContext(false, header)
keeper.setLiquidatorModuleAccount(ctx, LiquidatorModuleAccount{cs(c("usdx", 100))}) // set gov coin "balance" to zero
ctx := tApp.NewContext(false, abci.Header{})
keeper := tApp.GetCDPKeeper()
keeper.SetLiquidatorModuleAccount(ctx, keep.LiquidatorModuleAccount{cs(c("usdx", 100))}) // set gov coin "balance" to zero
// perform the test action
var err sdk.Error
@ -56,9 +52,6 @@ func TestKeeper_AddSubtractGetCoins(t *testing.T) {
_, err = keeper.SubtractCoins(ctx, tc.address, tc.amount)
}
mapp.EndBlock(abci.RequestEndBlock{})
mapp.Commit()
// check balances are as expected
require.NoError(t, err)
require.Equal(t, tc.expectedCoins, keeper.GetCoins(ctx, tc.address))

View File

@ -0,0 +1,103 @@
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/cdp/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 d(str string) sdk.Dec { return sdk.MustNewDecFromStr(str) }
func c(denom string, amount int64) sdk.Coin { return sdk.NewInt64Coin(denom, amount) }
func cs(coins ...sdk.Coin) sdk.Coins { return sdk.NewCoins(coins...) }
func NewPricefeedGenState(asset string, price sdk.Dec) app.GenesisState {
pfGenesis := pricefeed.GenesisState{
Params: pricefeed.Params{
Markets: []pricefeed.Market{
pricefeed.Market{MarketID: asset, BaseAsset: asset, QuoteAsset: "usd", Oracles: pricefeed.Oracles{}, Active: true},
},
},
PostedPrices: []pricefeed.PostedPrice{
pricefeed.PostedPrice{
MarketID: asset,
OracleAddress: sdk.AccAddress{},
Price: price,
Expiry: time.Now().Add(1 * time.Hour),
},
},
}
return app.GenesisState{pricefeed.ModuleName: pricefeed.ModuleCdc.MustMarshalJSON(pfGenesis)}
}
func NewCDPGenState(asset string, liquidationRatio sdk.Dec) app.GenesisState {
cdpGenesis := cdp.GenesisState{
Params: cdp.CdpParams{
GlobalDebtLimit: sdk.NewInt(1000000),
CollateralParams: []cdp.CollateralParams{
{
Denom: asset,
LiquidationRatio: liquidationRatio,
DebtLimit: sdk.NewInt(500000),
},
},
},
GlobalDebt: sdk.ZeroInt(),
CDPs: cdp.CDPs{},
}
return app.GenesisState{cdp.ModuleName: cdp.ModuleCdc.MustMarshalJSON(cdpGenesis)}
}
func NewPricefeedGenStateMulti() app.GenesisState {
pfGenesis := pricefeed.GenesisState{
Params: pricefeed.Params{
Markets: []pricefeed.Market{
pricefeed.Market{MarketID: "btc", BaseAsset: "btc", QuoteAsset: "usd", Oracles: pricefeed.Oracles{}, Active: true},
pricefeed.Market{MarketID: "xrp", BaseAsset: "xrp", QuoteAsset: "usd", Oracles: pricefeed.Oracles{}, Active: true},
},
},
PostedPrices: []pricefeed.PostedPrice{
pricefeed.PostedPrice{
MarketID: "btc",
OracleAddress: sdk.AccAddress{},
Price: sdk.MustNewDecFromStr("8000.00"),
Expiry: time.Now().Add(1 * time.Hour),
},
pricefeed.PostedPrice{
MarketID: "xrp",
OracleAddress: sdk.AccAddress{},
Price: sdk.MustNewDecFromStr("0.25"),
Expiry: time.Now().Add(1 * time.Hour),
},
},
}
return app.GenesisState{pricefeed.ModuleName: pricefeed.ModuleCdc.MustMarshalJSON(pfGenesis)}
}
func NewCDPGenStateMulti() app.GenesisState {
cdpGenesis := cdp.GenesisState{
Params: cdp.CdpParams{
GlobalDebtLimit: sdk.NewInt(1000000),
CollateralParams: []types.CollateralParams{
{
Denom: "btc",
LiquidationRatio: sdk.MustNewDecFromStr("1.5"),
DebtLimit: sdk.NewInt(500000),
},
{
Denom: "xrp",
LiquidationRatio: sdk.MustNewDecFromStr("2.0"),
DebtLimit: sdk.NewInt(500000),
},
},
},
GlobalDebt: sdk.ZeroInt(),
CDPs: cdp.CDPs{},
}
return app.GenesisState{cdp.ModuleName: cdp.ModuleCdc.MustMarshalJSON(cdpGenesis)}
}

View File

@ -126,13 +126,13 @@ func (k Keeper) ModifyCDP(ctx sdk.Context, owner sdk.AccAddress, collateralDenom
}
// Set CDP
if cdp.CollateralAmount.IsZero() && cdp.Debt.IsZero() { // TODO maybe abstract this logic into SetCDP
k.deleteCDP(ctx, cdp)
k.DeleteCDP(ctx, cdp)
} else {
k.SetCDP(ctx, cdp)
}
// set total debts
k.SetGlobalDebt(ctx, gDebt)
k.setCollateralState(ctx, collateralState)
k.SetCollateralState(ctx, collateralState)
return nil
}
@ -195,11 +195,11 @@ func (k Keeper) PartialSeizeCDP(ctx sdk.Context, owner sdk.AccAddress, collatera
// Store updated state
if cdp.CollateralAmount.IsZero() && cdp.Debt.IsZero() { // TODO maybe abstract this logic into SetCDP
k.deleteCDP(ctx, cdp)
k.DeleteCDP(ctx, cdp)
} else {
k.SetCDP(ctx, cdp)
}
k.setCollateralState(ctx, collateralState)
k.SetCollateralState(ctx, collateralState)
return nil
}
@ -264,7 +264,7 @@ func (k Keeper) SetCDP(ctx sdk.Context, cdp types.CDP) {
bz := k.cdc.MustMarshalBinaryLengthPrefixed(cdp)
store.Set(k.getCDPKey(cdp.Owner, cdp.CollateralDenom), bz)
}
func (k Keeper) deleteCDP(ctx sdk.Context, cdp types.CDP) { // TODO should this id the cdp by passing in owner,collateralDenom pair?
func (k Keeper) DeleteCDP(ctx sdk.Context, cdp types.CDP) { // TODO should this id the cdp by passing in owner,collateralDenom pair?
// get store
store := ctx.KVStore(k.key)
// delete key
@ -354,7 +354,7 @@ func (k Keeper) GetCollateralState(ctx sdk.Context, collateralDenom string) (typ
k.cdc.MustUnmarshalBinaryLengthPrefixed(bz, &collateralState)
return collateralState, true
}
func (k Keeper) setCollateralState(ctx sdk.Context, collateralstate types.CollateralState) {
func (k Keeper) SetCollateralState(ctx sdk.Context, collateralstate types.CollateralState) {
// get store
store := ctx.KVStore(k.key)
// marshal and set
@ -422,7 +422,7 @@ func (k Keeper) AddCoins(ctx sdk.Context, address sdk.AccAddress, amount sdk.Coi
return amount, sdk.ErrInsufficientCoins(fmt.Sprintf("insufficient account funds; %s < %s", lma.Coins, amount))
}
lma.Coins = updatedCoins
k.setLiquidatorModuleAccount(ctx, lma)
k.SetLiquidatorModuleAccount(ctx, lma)
return updatedCoins, nil
} else {
return k.bankKeeper.AddCoins(ctx, address, amount)
@ -445,7 +445,7 @@ func (k Keeper) SubtractCoins(ctx sdk.Context, address sdk.AccAddress, amount sd
return amount, sdk.ErrInsufficientCoins(fmt.Sprintf("insufficient account funds; %s < %s", lma.Coins, amount))
}
lma.Coins = updatedCoins
k.setLiquidatorModuleAccount(ctx, lma)
k.SetLiquidatorModuleAccount(ctx, lma)
return updatedCoins, nil
} else {
return k.bankKeeper.SubtractCoins(ctx, address, amount)
@ -483,7 +483,7 @@ func (k Keeper) getLiquidatorModuleAccount(ctx sdk.Context) LiquidatorModuleAcco
k.cdc.MustUnmarshalBinaryLengthPrefixed(bz, &lma)
return lma
}
func (k Keeper) setLiquidatorModuleAccount(ctx sdk.Context, lma LiquidatorModuleAccount) {
func (k Keeper) SetLiquidatorModuleAccount(ctx sdk.Context, lma LiquidatorModuleAccount) {
store := ctx.KVStore(k.key)
bz := k.cdc.MustMarshalBinaryLengthPrefixed(lma)
store.Set(liquidatorAccountKey, bz)

View File

@ -1,4 +1,4 @@
package keeper
package keeper_test
import (
"fmt"
@ -6,24 +6,21 @@ import (
"time"
sdk "github.com/cosmos/cosmos-sdk/types"
"github.com/cosmos/cosmos-sdk/x/auth"
authexported "github.com/cosmos/cosmos-sdk/x/auth/exported"
"github.com/cosmos/cosmos-sdk/x/mock"
"github.com/kava-labs/kava/x/cdp/types"
"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"
"github.com/kava-labs/kava/app"
"github.com/kava-labs/kava/x/cdp/types"
)
// 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?
// These are more like app level tests - I think this is a symptom of having 'ModifyCDP' do a lot. Could be easier for testing purposes to break it down.
func TestKeeper_ModifyCDP(t *testing.T) {
_, addrs := mock.GeneratePrivKeyAddressPairs(1)
_, addrs := app.GeneratePrivKeyAddressPairs(1)
ownerAddr := addrs[0]
type state struct { // TODO this allows invalid state to be set up, should it?
type state struct {
CDP types.CDP
OwnerCoins sdk.Coins
GlobalDebt sdk.Int
@ -96,59 +93,32 @@ func TestKeeper_ModifyCDP(t *testing.T) {
}
for _, tc := range tests {
t.Run(tc.name, func(t *testing.T) {
// setup keeper
mapp, keeper, _, _ := setUpMockAppWithoutGenesis()
// initialize cdp owner account with coins
genAcc := auth.BaseAccount{
Address: ownerAddr,
Coins: tc.priorState.OwnerCoins,
}
mock.SetGenesis(mapp, []authexported.Account{&genAcc})
// create a new context
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 test app
tApp := app.NewTestApp()
// initialize cdp owner account with coins, and collateral with price and params
tApp.InitializeFromGenesisStates(
app.NewAuthGenState([]sdk.AccAddress{ownerAddr}, []sdk.Coins{tc.priorState.OwnerCoins}),
NewPricefeedGenState("xrp", d(tc.price)),
NewCDPGenState("xrp", d("2.0")),
)
// create a context for db access
ctx := tApp.NewContext(false, abci.Header{})
// setup store state
ap := pricefeed.Params{
Markets: []pricefeed.Market{
pricefeed.Market{
MarketID: "xrp", BaseAsset: "xrp",
QuoteAsset: "usd", Oracles: pricefeed.Oracles{}, Active: true},
},
}
keeper.pricefeedKeeper.SetParams(ctx, ap)
_, err := keeper.pricefeedKeeper.SetPrice(
ctx, ownerAddr, "xrp",
sdk.MustNewDecFromStr(tc.price),
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, "xrp")
if err != nil {
t.Log("test context height", ctx.BlockHeight())
t.Log(err)
t.Log(tc.name)
}
keeper := tApp.GetCDPKeeper()
if tc.priorState.CDP.CollateralDenom != "" { // check if the prior CDP should be created or not (see if an empty one was specified)
keeper.SetCDP(ctx, tc.priorState.CDP)
}
keeper.SetGlobalDebt(ctx, tc.priorState.GlobalDebt)
if tc.priorState.CollateralState.Denom != "" {
keeper.setCollateralState(ctx, tc.priorState.CollateralState)
keeper.SetCollateralState(ctx, tc.priorState.CollateralState)
}
// call func under test
err = keeper.ModifyCDP(ctx, tc.args.owner, tc.args.collateralDenom, tc.args.changeInCollateral, tc.args.changeInDebt)
mapp.EndBlock(abci.RequestEndBlock{})
mapp.Commit()
err := keeper.ModifyCDP(ctx, tc.args.owner, tc.args.collateralDenom, tc.args.changeInCollateral, tc.args.changeInDebt)
// get new state for verification
actualCDP, found := keeper.GetCDP(ctx, tc.args.owner, tc.args.collateralDenom)
// check for err
if tc.expectPass {
require.NoError(t, err, fmt.Sprint(err))
@ -167,47 +137,35 @@ func TestKeeper_ModifyCDP(t *testing.T) {
require.Equal(t, tc.expectedState.GlobalDebt, actualGDebt)
require.Equal(t, tc.expectedState.CollateralState, actualCstate)
// check owner balance
mock.CheckBalance(t, mapp, ownerAddr, tc.expectedState.OwnerCoins)
tApp.CheckBalance(t, ctx, ownerAddr, tc.expectedState.OwnerCoins)
})
}
}
// TODO change to table driven test to test more test cases
func TestKeeper_PartialSeizeCDP(t *testing.T) {
// Setup
const collateral = "xrp"
mapp, keeper, _, _ := setUpMockAppWithoutGenesis()
genAccs, addrs, _, _ := mock.CreateGenAccounts(1, cs(c(collateral, 100)))
_, addrs := app.GeneratePrivKeyAddressPairs(1)
testAddr := addrs[0]
mock.SetGenesis(mapp, genAccs)
// setup pricefeed
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.Params{
Markets: []pricefeed.Market{
pricefeed.Market{
MarketID: "xrp", BaseAsset: "xrp",
QuoteAsset: "usd", Oracles: pricefeed.Oracles{}, Active: true},
},
}
keeper.pricefeedKeeper.SetParams(ctx, ap)
keeper.pricefeedKeeper.SetPrice(
ctx, sdk.AccAddress{}, collateral,
sdk.MustNewDecFromStr("1.00"),
header.Time.Add(time.Hour*1))
keeper.pricefeedKeeper.SetCurrentPrices(ctx, collateral)
tApp := app.NewTestApp()
tApp.InitializeFromGenesisStates(
app.NewAuthGenState(addrs, []sdk.Coins{cs(c(collateral, 100))}),
NewPricefeedGenState(collateral, d("1.00")),
NewCDPGenState(collateral, d("2.00")),
)
ctx := tApp.NewContext(false, abci.Header{})
keeper := tApp.GetCDPKeeper()
// Create CDP
keeper.SetGlobalDebt(ctx, i(0))
err := keeper.ModifyCDP(ctx, testAddr, collateral, i(10), i(5))
require.NoError(t, err)
// Reduce price
keeper.pricefeedKeeper.SetPrice(
tApp.GetPriceFeedKeeper().SetPrice(
ctx, sdk.AccAddress{}, collateral,
sdk.MustNewDecFromStr("0.90"),
header.Time.Add(time.Hour*1))
keeper.pricefeedKeeper.SetCurrentPrices(ctx, collateral)
d("0.90"), time.Now().Add(1*time.Hour))
tApp.GetPriceFeedKeeper().SetCurrentPrices(ctx, collateral)
// Seize entire CDP
err = keeper.PartialSeizeCDP(ctx, testAddr, collateral, i(10), i(5))
@ -222,15 +180,16 @@ func TestKeeper_PartialSeizeCDP(t *testing.T) {
}
func TestKeeper_GetCDPs(t *testing.T) {
// setup keeper
mapp, keeper, _, _ := setUpMockAppWithoutGenesis()
mock.SetGenesis(mapp, []authexported.Account(nil))
header := abci.Header{Height: mapp.LastBlockHeight() + 1}
mapp.BeginBlock(abci.RequestBeginBlock{Header: header})
ctx := mapp.BaseApp.NewContext(false, header)
keeper.SetParams(ctx, defaultParamsMulti())
// setup test app
tApp := app.NewTestApp().InitializeFromGenesisStates(
NewPricefeedGenStateMulti(), // collateral needs to be in pricefeed for cdp InitGenesis to validate
NewCDPGenStateMulti(),
)
ctx := tApp.NewContext(true, abci.Header{})
keeper := tApp.GetCDPKeeper()
// setup CDPs
_, addrs := mock.GeneratePrivKeyAddressPairs(2)
_, addrs := app.GeneratePrivKeyAddressPairs(2)
cdps := types.CDPs{
{addrs[0], "xrp", i(4000), i(5)},
{addrs[1], "xrp", i(4000), i(2000)},
@ -288,7 +247,7 @@ func TestKeeper_GetCDPs(t *testing.T) {
_, err = keeper.GetCDPs(ctx, "", d("0.34023"))
require.Error(t, err)
// Check deleting a CDP removes it
keeper.deleteCDP(ctx, cdps[0])
keeper.DeleteCDP(ctx, cdps[0])
returnedCdps, err = keeper.GetCDPs(ctx, "", sdk.Dec{})
require.NoError(t, err)
require.Equal(t,
@ -298,14 +257,14 @@ func TestKeeper_GetCDPs(t *testing.T) {
returnedCdps,
)
}
func TestKeeper_GetSetDeleteCDP(t *testing.T) {
// setup keeper, create CDP
mapp, keeper, _, _ := setUpMockAppWithoutGenesis()
header := abci.Header{Height: mapp.LastBlockHeight() + 1}
mapp.BeginBlock(abci.RequestBeginBlock{Header: header})
ctx := mapp.BaseApp.NewContext(false, header)
keeper.SetParams(ctx, defaultParamsSingle())
_, addrs := mock.GeneratePrivKeyAddressPairs(1)
tApp := app.NewTestApp()
ctx := tApp.NewContext(true, abci.Header{})
keeper := tApp.GetCDPKeeper()
_, addrs := app.GeneratePrivKeyAddressPairs(1)
cdp := types.CDP{addrs[0], "xrp", i(412), i(56)}
// write and read from store
@ -317,7 +276,7 @@ func TestKeeper_GetSetDeleteCDP(t *testing.T) {
require.Equal(t, cdp, readCDP)
// delete auction
keeper.deleteCDP(ctx, cdp)
keeper.DeleteCDP(ctx, cdp)
// check auction does not exist
_, found = keeper.GetCDP(ctx, cdp.Owner, cdp.CollateralDenom)
@ -325,11 +284,10 @@ func TestKeeper_GetSetDeleteCDP(t *testing.T) {
}
func TestKeeper_GetSetGDebt(t *testing.T) {
// setup keeper, create GDebt
mapp, keeper, _, _ := setUpMockAppWithoutGenesis()
header := abci.Header{Height: mapp.LastBlockHeight() + 1}
mapp.BeginBlock(abci.RequestBeginBlock{Header: header})
ctx := mapp.BaseApp.NewContext(false, header)
keeper.SetParams(ctx, defaultParamsSingle())
tApp := app.NewTestApp()
ctx := tApp.NewContext(true, abci.Header{})
keeper := tApp.GetCDPKeeper()
gDebt := i(4120000)
// write and read from store
@ -342,15 +300,14 @@ func TestKeeper_GetSetGDebt(t *testing.T) {
func TestKeeper_GetSetCollateralState(t *testing.T) {
// setup keeper, create CState
mapp, keeper, _, _ := setUpMockAppWithoutGenesis()
header := abci.Header{Height: mapp.LastBlockHeight() + 1}
mapp.BeginBlock(abci.RequestBeginBlock{Header: header})
ctx := mapp.BaseApp.NewContext(false, header)
keeper.SetParams(ctx, defaultParamsSingle())
tApp := app.NewTestApp()
ctx := tApp.NewContext(true, abci.Header{})
keeper := tApp.GetCDPKeeper()
collateralState := types.CollateralState{"xrp", i(15400)}
// write and read from store
keeper.setCollateralState(ctx, collateralState)
keeper.SetCollateralState(ctx, collateralState)
readCState, found := keeper.GetCollateralState(ctx, collateralState.Denom)
// check before and after match

View File

@ -1,88 +0,0 @@
package keeper
import (
sdk "github.com/cosmos/cosmos-sdk/types"
"github.com/cosmos/cosmos-sdk/x/bank"
"github.com/cosmos/cosmos-sdk/x/mock"
"github.com/kava-labs/kava/x/cdp/types"
"github.com/kava-labs/kava/x/pricefeed"
"github.com/tendermint/tendermint/crypto"
)
// Mock app is an ABCI app with an in memory database.
// This function creates an app, setting up the keepers, routes, begin and end blockers.
// But leaves it to the tests to call InitChain (done by calling mock.SetGenesis)
// The app works by submitting ABCI messages.
// - InitChain sets up the app db from genesis.
// - BeginBlock starts the delivery of a new block
// - DeliverTx delivers a tx
// - EndBlock signals the end of a block
// - Commit ?
func setUpMockAppWithoutGenesis() (*mock.App, Keeper, []sdk.AccAddress, []crypto.PrivKey) {
// Create uninitialized mock app
mapp := mock.NewApp()
// Register codecs
types.RegisterCodec(mapp.Cdc)
// Create keepers
keyCDP := sdk.NewKVStoreKey("cdp")
keyPriceFeed := sdk.NewKVStoreKey(pricefeed.StoreKey)
pk := mapp.ParamsKeeper
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)
// Mount and load the stores
err := mapp.CompleteSetup(keyPriceFeed, keyCDP)
if err != nil {
panic("mock app setup failed")
}
// Create a bunch (ie 10) of pre-funded accounts to use for tests
genAccs, addrs, _, privKeys := mock.CreateGenAccounts(10, sdk.NewCoins(sdk.NewInt64Coin("token1", 100), sdk.NewInt64Coin("token2", 100)))
mock.SetGenesis(mapp, genAccs)
return mapp, cdpKeeper, addrs, privKeys
}
// Avoid cluttering test cases with long function name
func i(in int64) sdk.Int { return sdk.NewInt(in) }
func d(str string) sdk.Dec { return sdk.MustNewDecFromStr(str) }
func c(denom string, amount int64) sdk.Coin { return sdk.NewInt64Coin(denom, amount) }
func cs(coins ...sdk.Coin) sdk.Coins { return sdk.NewCoins(coins...) }
func defaultParamsMulti() types.CdpParams {
return types.CdpParams{
GlobalDebtLimit: sdk.NewInt(1000000),
CollateralParams: []types.CollateralParams{
{
Denom: "btc",
LiquidationRatio: sdk.MustNewDecFromStr("1.5"),
DebtLimit: sdk.NewInt(500000),
},
{
Denom: "xrp",
LiquidationRatio: sdk.MustNewDecFromStr("2.0"),
DebtLimit: sdk.NewInt(500000),
},
},
StableDenoms: []string{"usdx"},
}
}
func defaultParamsSingle() types.CdpParams {
return types.CdpParams{
GlobalDebtLimit: sdk.NewInt(1000000),
CollateralParams: []types.CollateralParams{
{
Denom: "xrp",
LiquidationRatio: sdk.MustNewDecFromStr("2.0"),
DebtLimit: sdk.NewInt(500000),
},
},
StableDenoms: []string{"usdx"},
}
}

View File

@ -1,52 +0,0 @@
package cdp
import (
sdk "github.com/cosmos/cosmos-sdk/types"
"github.com/cosmos/cosmos-sdk/x/bank"
"github.com/cosmos/cosmos-sdk/x/mock"
"github.com/kava-labs/kava/x/pricefeed"
)
// Mock app is an ABCI app with an in memory database.
// This function creates an app, setting up the keepers, routes, begin and end blockers.
// But leaves it to the tests to call InitChain (done by calling mock.SetGenesis)
// The app works by submitting ABCI messages.
// - InitChain sets up the app db from genesis.
// - BeginBlock starts the delivery of a new block
// - DeliverTx delivers a tx
// - EndBlock signals the end of a block
// - Commit ?
func setUpMockAppWithoutGenesis() (*mock.App, Keeper, PricefeedKeeper) {
// Create uninitialized mock app
mapp := mock.NewApp()
// Register codecs
RegisterCodec(mapp.Cdc)
// Create keepers
keyCDP := sdk.NewKVStoreKey("cdp")
keyPriceFeed := sdk.NewKVStoreKey(pricefeed.StoreKey)
pk := mapp.ParamsKeeper
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)
// Register routes
mapp.Router().AddRoute("cdp", NewHandler(cdpKeeper))
// Mount and load the stores
err := mapp.CompleteSetup(keyPriceFeed, keyCDP)
if err != nil {
panic("mock app setup failed")
}
return mapp, cdpKeeper, priceFeedKeeper
}
// Avoid cluttering test cases with long function name
func i(in int64) sdk.Int { return sdk.NewInt(in) }
func d(str string) sdk.Dec { return sdk.MustNewDecFromStr(str) }
func c(denom string, amount int64) sdk.Coin { return sdk.NewInt64Coin(denom, amount) }
func cs(coins ...sdk.Coin) sdk.Coins { return sdk.NewCoins(coins...) }

View File

@ -0,0 +1,72 @@
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 d(str string) sdk.Dec { return sdk.MustNewDecFromStr(str) }
func c(denom string, amount int64) sdk.Coin { return sdk.NewInt64Coin(denom, amount) }
func cs(coins ...sdk.Coin) sdk.Coins { return sdk.NewCoins(coins...) }
// Genesis states to initialize test apps
func NewPricefeedGenState(asset string, price sdk.Dec) app.GenesisState {
pfGenesis := pricefeed.GenesisState{
Params: pricefeed.Params{
Markets: []pricefeed.Market{
pricefeed.Market{MarketID: asset, BaseAsset: asset, QuoteAsset: "usd", Oracles: pricefeed.Oracles{}, Active: true}},
},
PostedPrices: []pricefeed.PostedPrice{
pricefeed.PostedPrice{
MarketID: asset,
OracleAddress: sdk.AccAddress{},
Price: price,
Expiry: time.Now().Add(1 * time.Hour),
},
},
}
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)}
}

View File

@ -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)

View File

@ -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(
app.NewAuthGenState(addrs, []sdk.Coins{cs(c("btc", 100))}),
NewPricefeedGenState("btc", d("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", d("7999.99"), time.Now().Add(1*time.Hour))
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(
app.NewAuthGenState(addrs, []sdk.Coins{cs(c("btc", 100))}),
NewPricefeedGenState("btc", d("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", d("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)

View File

@ -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(keyPriceFeed, cdc, 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),
},
},
}
}

View File

@ -21,7 +21,10 @@ func InitGenesis(ctx sdk.Context, keeper Keeper, data GenesisState) {
// Set the current price (if any) based on what's now in the store
for _, a := range data.Params.Markets {
if a.Active {
_ = keeper.SetCurrentPrices(ctx, a.MarketID)
err := keeper.SetCurrentPrices(ctx, a.MarketID)
if err != nil {
panic(err)
}
}
}
}

View File

@ -27,7 +27,7 @@ type Keeper struct {
// - adding oracles
// - adding/removing assets from the pricefeed
func NewKeeper(
storeKey sdk.StoreKey, cdc *codec.Codec, paramstore params.Subspace, codespace sdk.CodespaceType,
cdc *codec.Codec, storeKey sdk.StoreKey, paramstore params.Subspace, codespace sdk.CodespaceType,
) Keeper {
return Keeper{
paramstore: paramstore.WithKeyTable(types.ParamKeyTable()),

View File

@ -1,4 +1,4 @@
package keeper
package keeper_test
import (
"testing"
@ -7,31 +7,28 @@ import (
sdk "github.com/cosmos/cosmos-sdk/types"
"github.com/stretchr/testify/require"
abci "github.com/tendermint/tendermint/abci/types"
tmtime "github.com/tendermint/tendermint/types/time"
"github.com/kava-labs/kava/app"
"github.com/kava-labs/kava/x/pricefeed/types"
)
// TestKeeper_SetGetMarket tests adding markets to the pricefeed, getting markets from the store
func TestKeeper_SetGetMarket(t *testing.T) {
helper := getMockApp(t, 0, types.GenesisState{}, nil)
header := abci.Header{
Height: helper.mApp.LastBlockHeight() + 1,
Time: tmtime.Now()}
helper.mApp.BeginBlock(abci.RequestBeginBlock{Header: header})
ctx := helper.mApp.BaseApp.NewContext(false, header)
tApp := app.NewTestApp()
ctx := tApp.NewContext(true, abci.Header{})
keeper := tApp.GetPriceFeedKeeper()
mp := types.Params{
Markets: types.Markets{
types.Market{MarketID: "tstusd", BaseAsset: "tst", QuoteAsset: "usd", Oracles: types.Oracles{}, Active: true},
},
}
helper.keeper.SetParams(ctx, mp)
markets := helper.keeper.GetMarketParams(ctx)
keeper.SetParams(ctx, mp)
markets := keeper.GetMarketParams(ctx)
require.Equal(t, len(markets), 1)
require.Equal(t, markets[0].MarketID, "tstusd")
_, found := helper.keeper.GetMarket(ctx, "tstusd")
_, found := keeper.GetMarket(ctx, "tstusd")
require.Equal(t, found, true)
mp = types.Params{
@ -40,102 +37,99 @@ func TestKeeper_SetGetMarket(t *testing.T) {
types.Market{MarketID: "tst2usd", BaseAsset: "tst2", QuoteAsset: "usd", Oracles: types.Oracles{}, Active: true},
},
}
helper.keeper.SetParams(ctx, mp)
markets = helper.keeper.GetMarketParams(ctx)
keeper.SetParams(ctx, mp)
markets = keeper.GetMarketParams(ctx)
require.Equal(t, len(markets), 2)
require.Equal(t, markets[0].MarketID, "tstusd")
require.Equal(t, markets[1].MarketID, "tst2usd")
_, found = helper.keeper.GetMarket(ctx, "nan")
_, found = keeper.GetMarket(ctx, "nan")
require.Equal(t, found, false)
}
// TestKeeper_GetSetPrice Test Posting the price by an oracle
func TestKeeper_GetSetPrice(t *testing.T) {
helper := getMockApp(t, 2, types.GenesisState{}, nil)
header := abci.Header{
Height: helper.mApp.LastBlockHeight() + 1,
Time: tmtime.Now()}
helper.mApp.BeginBlock(abci.RequestBeginBlock{Header: header})
ctx := helper.mApp.BaseApp.NewContext(false, header)
_, addrs := app.GeneratePrivKeyAddressPairs(2)
tApp := app.NewTestApp()
ctx := tApp.NewContext(true, abci.Header{})
keeper := tApp.GetPriceFeedKeeper()
mp := types.Params{
Markets: types.Markets{
types.Market{MarketID: "tstusd", BaseAsset: "tst", QuoteAsset: "usd", Oracles: types.Oracles{}, Active: true},
},
}
helper.keeper.SetParams(ctx, mp)
keeper.SetParams(ctx, mp)
// Set price by oracle 1
_, err := helper.keeper.SetPrice(
ctx, helper.addrs[0], "tstusd",
_, err := keeper.SetPrice(
ctx, addrs[0], "tstusd",
sdk.MustNewDecFromStr("0.33"),
header.Time.Add(1*time.Hour))
time.Now().Add(1*time.Hour))
require.NoError(t, err)
// Get raw prices
rawPrices := helper.keeper.GetRawPrices(ctx, "tstusd")
rawPrices := keeper.GetRawPrices(ctx, "tstusd")
require.Equal(t, len(rawPrices), 1)
require.Equal(t, rawPrices[0].Price.Equal(sdk.MustNewDecFromStr("0.33")), true)
// Set price by oracle 2
_, err = helper.keeper.SetPrice(
ctx, helper.addrs[1], "tstusd",
_, err = keeper.SetPrice(
ctx, addrs[1], "tstusd",
sdk.MustNewDecFromStr("0.35"),
header.Time.Add(time.Hour*1))
time.Now().Add(time.Hour*1))
require.NoError(t, err)
rawPrices = helper.keeper.GetRawPrices(ctx, "tstusd")
rawPrices = keeper.GetRawPrices(ctx, "tstusd")
require.Equal(t, len(rawPrices), 2)
require.Equal(t, rawPrices[1].Price.Equal(sdk.MustNewDecFromStr("0.35")), true)
// Update Price by Oracle 1
_, err = helper.keeper.SetPrice(
ctx, helper.addrs[0], "tstusd",
_, err = keeper.SetPrice(
ctx, addrs[0], "tstusd",
sdk.MustNewDecFromStr("0.37"),
header.Time.Add(time.Hour*1))
time.Now().Add(time.Hour*1))
require.NoError(t, err)
rawPrices = helper.keeper.GetRawPrices(ctx, "tstusd")
rawPrices = keeper.GetRawPrices(ctx, "tstusd")
require.Equal(t, rawPrices[0].Price.Equal(sdk.MustNewDecFromStr("0.37")), true)
}
// TestKeeper_GetSetCurrentPrice Test Setting the median price of an Asset
func TestKeeper_GetSetCurrentPrice(t *testing.T) {
helper := getMockApp(t, 4, types.GenesisState{}, nil)
header := abci.Header{
Height: helper.mApp.LastBlockHeight() + 1,
Time: tmtime.Now()}
helper.mApp.BeginBlock(abci.RequestBeginBlock{Header: header})
ctx := helper.mApp.BaseApp.NewContext(false, header)
_, addrs := app.GeneratePrivKeyAddressPairs(4)
tApp := app.NewTestApp()
ctx := tApp.NewContext(true, abci.Header{})
keeper := tApp.GetPriceFeedKeeper()
mp := types.Params{
Markets: types.Markets{
types.Market{MarketID: "tstusd", BaseAsset: "tst", QuoteAsset: "usd", Oracles: types.Oracles{}, Active: true},
},
}
helper.keeper.SetParams(ctx, mp)
helper.keeper.SetPrice(
ctx, helper.addrs[0], "tstusd",
keeper.SetParams(ctx, mp)
keeper.SetPrice(
ctx, addrs[0], "tstusd",
sdk.MustNewDecFromStr("0.33"),
header.Time.Add(time.Hour*1))
helper.keeper.SetPrice(
ctx, helper.addrs[1], "tstusd",
time.Now().Add(time.Hour*1))
keeper.SetPrice(
ctx, addrs[1], "tstusd",
sdk.MustNewDecFromStr("0.35"),
header.Time.Add(time.Hour*1))
helper.keeper.SetPrice(
ctx, helper.addrs[2], "tstusd",
time.Now().Add(time.Hour*1))
keeper.SetPrice(
ctx, addrs[2], "tstusd",
sdk.MustNewDecFromStr("0.34"),
header.Time.Add(time.Hour*1))
time.Now().Add(time.Hour*1))
// Set current price
err := helper.keeper.SetCurrentPrices(ctx, "tstusd")
err := keeper.SetCurrentPrices(ctx, "tstusd")
require.NoError(t, err)
// Get Current price
price := helper.keeper.GetCurrentPrice(ctx, "tstusd")
price := keeper.GetCurrentPrice(ctx, "tstusd")
require.Equal(t, price.Price.Equal(sdk.MustNewDecFromStr("0.34")), true)
// Even number of oracles
helper.keeper.SetPrice(
ctx, helper.addrs[3], "tstusd",
keeper.SetPrice(
ctx, addrs[3], "tstusd",
sdk.MustNewDecFromStr("0.36"),
header.Time.Add(time.Hour*1))
err = helper.keeper.SetCurrentPrices(ctx, "tstusd")
time.Now().Add(time.Hour*1))
err = keeper.SetCurrentPrices(ctx, "tstusd")
require.NoError(t, err)
price = helper.keeper.GetCurrentPrice(ctx, "tstusd")
price = keeper.GetCurrentPrice(ctx, "tstusd")
require.Equal(t, price.Price.Equal(sdk.MustNewDecFromStr("0.345")), true)
}

View File

@ -1,46 +0,0 @@
package keeper
import (
"testing"
sdk "github.com/cosmos/cosmos-sdk/types"
authexported "github.com/cosmos/cosmos-sdk/x/auth/exported"
"github.com/cosmos/cosmos-sdk/x/mock"
"github.com/stretchr/testify/require"
"github.com/tendermint/tendermint/crypto"
"github.com/kava-labs/kava/x/pricefeed/types"
)
type testHelper struct {
mApp *mock.App
keeper Keeper
addrs []sdk.AccAddress
pubKeys []crypto.PubKey
privKeys []crypto.PrivKey
}
func getMockApp(t *testing.T, numGenAccs int, genState types.GenesisState, genAccs []authexported.Account) testHelper {
mApp := mock.NewApp()
types.RegisterCodec(mApp.Cdc)
keyPricefeed := sdk.NewKVStoreKey(types.StoreKey)
pk := mApp.ParamsKeeper
keeper := NewKeeper(keyPricefeed, mApp.Cdc, pk.Subspace(types.DefaultParamspace), types.DefaultCodespace)
require.NoError(t, mApp.CompleteSetup(keyPricefeed))
valTokens := sdk.TokensFromConsensusPower(42)
var (
addrs []sdk.AccAddress
pubKeys []crypto.PubKey
privKeys []crypto.PrivKey
)
if genAccs == nil || len(genAccs) == 0 {
genAccs, addrs, pubKeys, privKeys = mock.CreateGenAccounts(numGenAccs,
sdk.NewCoins(sdk.NewCoin(sdk.DefaultBondDenom, valTokens)))
}
mock.SetGenesis(mApp, genAccs)
return testHelper{mApp, keeper, addrs, pubKeys, privKeys}
}