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
@ -81,17 +89,21 @@ type App struct {
tkeys map[string]*sdk.TransientStoreKey
// keepers from all the modules
accountKeeper auth.AccountKeeper
bankKeeper bank.Keeper
supplyKeeper supply.Keeper
stakingKeeper staking.Keeper
slashingKeeper slashing.Keeper
mintKeeper mint.Keeper
distrKeeper distr.Keeper
govKeeper gov.Keeper
crisisKeeper crisis.Keeper
paramsKeeper params.Keeper
vvKeeper validatorvesting.Keeper
accountKeeper auth.AccountKeeper
bankKeeper bank.Keeper
supplyKeeper supply.Keeper
stakingKeeper staking.Keeper
slashingKeeper slashing.Keeper
mintKeeper mint.Keeper
distrKeeper distr.Keeper
govKeeper gov.Keeper
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}
}