Ro address auction todos (#284)

* make auctions not expire without bids

* add events

* improve genesis state validation

* add genesis tests

* Keeper auctions test, types auctions test, keeper bidding test

* Resolved TODOs, added querier test

* Removed 'import x/liquidator' from keeper_test package for circleci

* Fixes for lack of liquidator module account in tests

* update comment

Co-Authored-By: Kevin Davis <karzak@users.noreply.github.com>

* add more events attributes

* feat: add back bidding on closed auction test

* feat: test failed debt/collateral auctions

Co-authored-by: Ruaridh <rhuairahrighairidh@users.noreply.github.com>
Co-authored-by: Denali Marsh <denalimarsh@gmail.com>
This commit is contained in:
Kevin Davis 2020-01-14 16:04:47 +01:00 committed by GitHub
parent 22e168d06a
commit 8128a680cc
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
9 changed files with 845 additions and 9 deletions

View File

@ -294,9 +294,6 @@ func (k Keeper) PlaceReverseBidCollateral(ctx sdk.Context, a types.CollateralAuc
if !a.IsReversePhase() {
return a, sdk.ErrInternal("auction not in reverse phase")
}
if lot.IsNegative() {
return a, sdk.ErrInternal("can't bid negative amount")
}
if !lot.IsLT(a.Lot) {
return a, sdk.ErrInternal("auction in reverse phase, new bid not less than previous amount")
}
@ -354,9 +351,6 @@ func (k Keeper) PlaceBidDebt(ctx sdk.Context, a types.DebtAuction, bidder sdk.Ac
if lot.Denom != a.Lot.Denom {
return a, sdk.ErrInternal("lot denom doesn't match auction")
}
if lot.IsNegative() {
return a, sdk.ErrInternal("lot less than 0")
}
if !lot.IsLT(a.Lot) {
return a, sdk.ErrInternal("lot not smaller than last lot")
}

View File

@ -100,6 +100,48 @@ func TestDebtAuctionBasic(t *testing.T) {
tApp.CheckBalance(t, ctx, seller, cs(c("token1", 80), c("token2", 110)))
}
func TestDebtAuctionDebtRemaining(t *testing.T) {
// Setup
_, addrs := app.GeneratePrivKeyAddressPairs(1)
seller := addrs[0]
buyerModName := cdp.LiquidatorMacc
buyerAddr := supply.NewModuleAddress(buyerModName)
tApp := app.NewTestApp()
buyerAcc := supply.NewEmptyModuleAccount(buyerModName, supply.Minter) // reverse auctions mint payout
require.NoError(t, buyerAcc.SetCoins(cs(c("debt", 100))))
tApp.InitializeFromGenesisStates(
NewAuthGenStateFromAccs(authexported.GenesisAccounts{
auth.NewBaseAccount(seller, cs(c("token1", 100), c("token2", 100)), nil, 0, 0),
buyerAcc,
}),
)
ctx := tApp.NewContext(false, abci.Header{})
keeper := tApp.GetAuctionKeeper()
// Start auction
auctionID, err := keeper.StartDebtAuction(ctx, buyerModName, c("token1", 10), c("token2", 99999), c("debt", 20))
require.NoError(t, err)
// Check buyer's coins have not decreased (except for debt), as lot is minted at the end
tApp.CheckBalance(t, ctx, buyerAddr, cs(c("debt", 80)))
// Place a bid
require.NoError(t, keeper.PlaceBid(ctx, 0, seller, c("token2", 10)))
// Check seller's coins have decreased
tApp.CheckBalance(t, ctx, seller, cs(c("token1", 90), c("token2", 100)))
// Check buyer's coins have increased
tApp.CheckBalance(t, ctx, buyerAddr, cs(c("token1", 10), c("debt", 90)))
// Close auction at just after auction expiry
ctx = ctx.WithBlockTime(ctx.BlockTime().Add(types.DefaultBidDuration))
require.NoError(t, keeper.CloseAuction(ctx, auctionID))
// Check seller's coins increased
tApp.CheckBalance(t, ctx, seller, cs(c("token1", 90), c("token2", 110)))
// check that debt has increased due to corresponding debt being greater than bid
tApp.CheckBalance(t, ctx, buyerAddr, cs(c("token1", 10), c("debt", 100)))
}
func TestCollateralAuctionBasic(t *testing.T) {
// Setup
_, addrs := app.GeneratePrivKeyAddressPairs(4)
@ -160,6 +202,59 @@ func TestCollateralAuctionBasic(t *testing.T) {
tApp.CheckBalance(t, ctx, buyer, cs(c("token1", 115), c("token2", 50)))
}
func TestCollateralAuctionDebtRemaining(t *testing.T) {
// Setup
_, addrs := app.GeneratePrivKeyAddressPairs(4)
buyer := addrs[0]
returnAddrs := addrs[1:]
returnWeights := is(30, 20, 10)
sellerModName := cdp.LiquidatorMacc
sellerAddr := supply.NewModuleAddress(sellerModName)
tApp := app.NewTestApp()
sellerAcc := supply.NewEmptyModuleAccount(sellerModName)
require.NoError(t, sellerAcc.SetCoins(cs(c("token1", 100), c("token2", 100), c("debt", 100))))
tApp.InitializeFromGenesisStates(
NewAuthGenStateFromAccs(authexported.GenesisAccounts{
auth.NewBaseAccount(buyer, cs(c("token1", 100), c("token2", 100)), nil, 0, 0),
auth.NewBaseAccount(returnAddrs[0], cs(c("token1", 100), c("token2", 100)), nil, 0, 0),
auth.NewBaseAccount(returnAddrs[1], cs(c("token1", 100), c("token2", 100)), nil, 0, 0),
auth.NewBaseAccount(returnAddrs[2], cs(c("token1", 100), c("token2", 100)), nil, 0, 0),
sellerAcc,
}),
)
ctx := tApp.NewContext(false, abci.Header{})
keeper := tApp.GetAuctionKeeper()
// Start auction
auctionID, err := keeper.StartCollateralAuction(ctx, sellerModName, c("token1", 20), c("token2", 50), returnAddrs, returnWeights, c("debt", 40))
require.NoError(t, err)
// Check seller's coins have decreased
tApp.CheckBalance(t, ctx, sellerAddr, cs(c("token1", 80), c("token2", 100), c("debt", 60)))
// Place a forward bid
require.NoError(t, keeper.PlaceBid(ctx, 0, buyer, c("token2", 10)))
// Check bidder'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, sellerAddr, cs(c("token1", 80), c("token2", 110), c("debt", 70)))
// Check return addresses have not received coins
for _, ra := range returnAddrs {
tApp.CheckBalance(t, ctx, ra, cs(c("token1", 100), c("token2", 100)))
}
ctx = ctx.WithBlockTime(ctx.BlockTime().Add(types.DefaultBidDuration))
require.NoError(t, keeper.CloseAuction(ctx, auctionID))
// check that buyers coins have increased
tApp.CheckBalance(t, ctx, buyer, cs(c("token1", 120), c("token2", 90)))
// Check return addresses have not received coins
for _, ra := range returnAddrs {
tApp.CheckBalance(t, ctx, ra, cs(c("token1", 100), c("token2", 100)))
}
// check that token2 has increased by 10, debt by 40, for a net debt increase of 30 debt
tApp.CheckBalance(t, ctx, sellerAddr, cs(c("token1", 80), c("token2", 110), c("debt", 100)))
}
func TestStartSurplusAuction(t *testing.T) {
someTime := time.Date(1998, time.January, 1, 0, 0, 0, 0, time.UTC)
type args struct {
@ -247,3 +342,68 @@ func TestStartSurplusAuction(t *testing.T) {
})
}
}
func TestCloseAuction(t *testing.T) {
// Set up
_, addrs := app.GeneratePrivKeyAddressPairs(1)
buyer := addrs[0]
sellerModName := cdp.LiquidatorMacc
tApp := app.NewTestApp()
sellerAcc := supply.NewEmptyModuleAccount(sellerModName, supply.Burner) // forward auctions burn proceeds
require.NoError(t, sellerAcc.SetCoins(cs(c("token1", 100), c("token2", 100))))
tApp.InitializeFromGenesisStates(
NewAuthGenStateFromAccs(authexported.GenesisAccounts{
auth.NewBaseAccount(buyer, cs(c("token1", 100), c("token2", 100)), nil, 0, 0),
sellerAcc,
}),
)
ctx := tApp.NewContext(false, abci.Header{})
keeper := tApp.GetAuctionKeeper()
// Create an auction (lot: 20 token1, initialBid: 0 token2)
id, err := keeper.StartSurplusAuction(ctx, sellerModName, c("token1", 20), "token2") // lot, bid denom
require.NoError(t, err)
// Attempt to close the auction before EndTime
require.Error(t, keeper.CloseAuction(ctx, id))
// Attempt to close auction that does not exist
require.Error(t, keeper.CloseAuction(ctx, 999))
}
func TestCloseExpiredAuctions(t *testing.T) {
// Set up
_, addrs := app.GeneratePrivKeyAddressPairs(1)
buyer := addrs[0]
sellerModName := "liquidator"
tApp := app.NewTestApp()
sellerAcc := supply.NewEmptyModuleAccount(sellerModName, supply.Burner) // forward auctions burn proceeds
require.NoError(t, sellerAcc.SetCoins(cs(c("token1", 100), c("token2", 100))))
tApp.InitializeFromGenesisStates(
NewAuthGenStateFromAccs(authexported.GenesisAccounts{
auth.NewBaseAccount(buyer, cs(c("token1", 100), c("token2", 100)), nil, 0, 0),
sellerAcc,
}),
)
ctx := tApp.NewContext(false, abci.Header{})
keeper := tApp.GetAuctionKeeper()
// Start auction 1
_, err := keeper.StartSurplusAuction(ctx, sellerModName, c("token1", 20), "token2") // lot, bid denom
require.NoError(t, err)
// Start auction 2
_, err = keeper.StartSurplusAuction(ctx, sellerModName, c("token1", 20), "token2") // lot, bid denom
require.NoError(t, err)
// Fast forward the block time
ctx = ctx.WithBlockTime(ctx.BlockTime().Add(types.DefaultMaxAuctionDuration).Add(1))
// Close expired auctions
err = keeper.CloseExpiredAuctions(ctx)
require.NoError(t, err)
}

View File

@ -0,0 +1,349 @@
package keeper_test
import (
"strings"
"testing"
"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/supply"
"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/types"
)
type AuctionType int
const (
Invalid AuctionType = 0
Surplus AuctionType = 1
Debt AuctionType = 2
CollateralPhase1 AuctionType = 3
CollateralPhase2 AuctionType = 4
)
func TestAuctionBidding(t *testing.T) {
someTime := time.Date(0001, time.January, 1, 0, 0, 0, 0, time.UTC)
_, addrs := app.GeneratePrivKeyAddressPairs(5)
buyer := addrs[0]
secondBuyer := addrs[1]
modName := "liquidator"
collateralAddrs := addrs[2:]
collateralWeights := is(30, 20, 10)
tApp := app.NewTestApp()
// Set up seller account
sellerAcc := supply.NewEmptyModuleAccount(modName, supply.Minter, supply.Burner)
require.NoError(t, sellerAcc.SetCoins(cs(c("token1", 1000), c("token2", 1000), c("debt", 1000))))
// Initialize genesis accounts
tApp.InitializeFromGenesisStates(
NewAuthGenStateFromAccs(authexported.GenesisAccounts{
auth.NewBaseAccount(buyer, cs(c("token1", 1000), c("token2", 1000)), nil, 0, 0),
auth.NewBaseAccount(secondBuyer, cs(c("token1", 1000), c("token2", 1000)), nil, 0, 0),
auth.NewBaseAccount(collateralAddrs[0], cs(c("token1", 1000), c("token2", 1000)), nil, 0, 0),
auth.NewBaseAccount(collateralAddrs[1], cs(c("token1", 1000), c("token2", 1000)), nil, 0, 0),
auth.NewBaseAccount(collateralAddrs[2], cs(c("token1", 1000), c("token2", 1000)), nil, 0, 0),
sellerAcc,
}),
)
ctx := tApp.NewContext(false, abci.Header{})
keeper := tApp.GetAuctionKeeper()
type auctionArgs struct {
auctionType AuctionType
seller string
lot sdk.Coin
bid sdk.Coin
debt sdk.Coin
addresses []sdk.AccAddress
weights []sdk.Int
}
type bidArgs struct {
bidder sdk.AccAddress
amount sdk.Coin
secondBidder sdk.AccAddress
}
tests := []struct {
name string
auctionArgs auctionArgs
bidArgs bidArgs
expectedError string
expectedEndTime time.Time
expectedBidder sdk.AccAddress
expectedBid sdk.Coin
expectpass bool
}{
{
"basic: auction doesn't exist",
auctionArgs{Surplus, "", c("token1", 1), c("token2", 1), sdk.Coin{}, []sdk.AccAddress{}, []sdk.Int{}},
bidArgs{buyer, c("token2", 10), nil},
"auction doesn't exist",
someTime.Add(types.DefaultBidDuration),
buyer,
c("token2", 10),
false,
},
{
"surplus: normal",
auctionArgs{Surplus, modName, c("token1", 100), c("token2", 10), sdk.Coin{}, []sdk.AccAddress{}, []sdk.Int{}},
bidArgs{buyer, c("token2", 10), nil},
"",
someTime.Add(types.DefaultBidDuration),
buyer,
c("token2", 10),
true,
},
{
"surplus: second bidder",
auctionArgs{Surplus, modName, c("token1", 100), c("token2", 10), sdk.Coin{}, []sdk.AccAddress{}, []sdk.Int{}},
bidArgs{buyer, c("token2", 10), secondBuyer},
"",
someTime.Add(types.DefaultBidDuration),
secondBuyer,
c("token2", 11),
true,
},
{
"surplus: invalid bid denom",
auctionArgs{Surplus, modName, c("token1", 100), c("token2", 10), sdk.Coin{}, []sdk.AccAddress{}, []sdk.Int{}},
bidArgs{buyer, c("badtoken", 10), nil},
"bid denom doesn't match auction",
someTime.Add(types.DefaultBidDuration),
buyer,
c("token2", 10),
false,
},
{
"surplus: invalid bid (equal)",
auctionArgs{Surplus, modName, c("token1", 100), c("token2", 0), sdk.Coin{}, []sdk.AccAddress{}, []sdk.Int{}},
bidArgs{buyer, c("token2", 0), nil},
"bid not greater than last bid",
someTime.Add(types.DefaultBidDuration),
buyer,
c("token2", 10),
false,
},
{
"debt: normal",
auctionArgs{Debt, modName, c("token1", 20), c("token2", 100), c("debt", 20), []sdk.AccAddress{}, []sdk.Int{}}, // initial bid, lot
bidArgs{buyer, c("token1", 10), nil},
"",
someTime.Add(types.DefaultBidDuration),
buyer,
c("token2", 100),
true,
},
{
"debt: second bidder",
auctionArgs{Debt, modName, c("token1", 20), c("token2", 100), c("debt", 20), []sdk.AccAddress{}, []sdk.Int{}}, // initial bid, lot
bidArgs{buyer, c("token1", 10), secondBuyer},
"",
someTime.Add(types.DefaultBidDuration),
secondBuyer,
c("token2", 100),
true,
},
{
"debt: invalid lot denom",
auctionArgs{Debt, modName, c("token1", 20), c("token2", 100), c("debt", 20), []sdk.AccAddress{}, []sdk.Int{}}, // initial bid, lot
bidArgs{buyer, c("badtoken", 10), nil},
"lot denom doesn't match auction",
someTime.Add(types.DefaultBidDuration),
buyer,
c("token1", 20),
false,
},
{
"debt: invalid lot size (larger)",
auctionArgs{Debt, modName, c("token1", 20), c("token2", 100), c("debt", 20), []sdk.AccAddress{}, []sdk.Int{}},
bidArgs{buyer, c("token1", 21), nil},
"lot not smaller than last lot",
someTime.Add(types.DefaultBidDuration),
buyer,
c("token1", 20),
false,
},
{
"collateral [forward]: normal",
auctionArgs{CollateralPhase1, modName, c("token1", 20), c("token2", 100), c("debt", 50), collateralAddrs, collateralWeights}, // lot, max bid
bidArgs{buyer, c("token2", 10), nil},
"",
someTime.Add(types.DefaultBidDuration),
buyer,
c("token2", 10),
true,
},
{
"collateral [forward]: second bidder",
auctionArgs{CollateralPhase1, modName, c("token1", 20), c("token2", 100), c("debt", 50), collateralAddrs, collateralWeights}, // lot, max bid
bidArgs{buyer, c("token2", 10), secondBuyer},
"",
someTime.Add(types.DefaultBidDuration),
secondBuyer,
c("token2", 11),
true,
},
{
"collateral [forward]: invalid bid denom",
auctionArgs{CollateralPhase1, modName, c("token1", 20), c("token2", 100), c("debt", 50), collateralAddrs, collateralWeights}, // lot, max bid
bidArgs{buyer, c("badtoken", 10), nil},
"bid denom doesn't match auction",
someTime.Add(types.DefaultBidDuration),
buyer,
c("token2", 10),
false,
},
{
"collateral [forward]: invalid bid size (smaller)",
auctionArgs{CollateralPhase1, modName, c("token1", 20), c("token2", 100), c("debt", 50), collateralAddrs, collateralWeights}, // lot, max bid
bidArgs{buyer, c("token2", 0), nil}, // lot, bid
"auction in forward phase, new bid not higher than last bid",
someTime.Add(types.DefaultBidDuration),
buyer,
c("token2", 10),
false,
},
{
"collateral [forward]: invalid bid size (greater than max)",
auctionArgs{CollateralPhase1, modName, c("token1", 20), c("token2", 100), c("debt", 50), collateralAddrs, collateralWeights}, // lot, max bid
bidArgs{buyer, c("token2", 101), nil}, // lot, bid
"bid higher than max bid",
someTime.Add(types.DefaultBidDuration),
buyer,
c("token2", 10),
false,
},
{
"collateral [reverse]: normal",
auctionArgs{CollateralPhase2, modName, c("token1", 20), c("token2", 50), c("debt", 50), collateralAddrs, collateralWeights}, // lot, max bid
bidArgs{buyer, c("token1", 15), nil},
"",
someTime.Add(types.DefaultBidDuration),
buyer,
c("token2", 50),
true,
},
{
"collateral [reverse]: second bidder",
auctionArgs{CollateralPhase2, modName, c("token1", 20), c("token2", 50), c("debt", 50), collateralAddrs, collateralWeights}, // lot, max bid
bidArgs{buyer, c("token1", 15), secondBuyer},
"",
someTime.Add(types.DefaultBidDuration),
secondBuyer,
c("token2", 50),
true,
},
{
"collateral [reverse]: invalid lot denom",
auctionArgs{CollateralPhase2, modName, c("token1", 20), c("token2", 50), c("debt", 50), collateralAddrs, collateralWeights}, // lot, max bid
bidArgs{buyer, c("badtoken", 15), nil},
"lot denom doesn't match auction",
someTime.Add(types.DefaultBidDuration),
buyer,
c("token2", 50),
false,
},
{
"collateral [reverse]: invalid lot size (equal)",
auctionArgs{CollateralPhase2, modName, c("token1", 20), c("token2", 50), c("debt", 50), collateralAddrs, collateralWeights}, // lot, max bid
bidArgs{buyer, c("token1", 20), nil},
"auction in reverse phase, new bid not less than previous amount",
someTime.Add(types.DefaultBidDuration),
buyer,
c("token2", 50),
false,
},
{
"collateral [reverse]: invalid lot size (greater)",
auctionArgs{CollateralPhase2, modName, c("token1", 20), c("token2", 50), c("debt", 50), collateralAddrs, collateralWeights}, // lot, max bid
bidArgs{buyer, c("token1", 21), nil},
"auction in reverse phase, new bid not less than previous amount",
someTime.Add(types.DefaultBidDuration),
buyer,
c("token2", 50),
false,
},
{
"basic: closed auction",
auctionArgs{Surplus, modName, c("token1", 100), c("token2", 10), sdk.Coin{}, []sdk.AccAddress{}, []sdk.Int{}},
bidArgs{buyer, c("token2", 10), nil},
"auction has closed",
someTime.Add(types.DefaultBidDuration),
buyer,
c("token2", 10),
false,
},
}
for _, tc := range tests {
t.Run(tc.name, func(t *testing.T) {
// Start Auction
var id uint64
var err error
switch tc.auctionArgs.auctionType {
case Surplus:
id, _ = keeper.StartSurplusAuction(ctx, tc.auctionArgs.seller, tc.auctionArgs.lot, tc.auctionArgs.bid.Denom)
case Debt:
id, _ = keeper.StartDebtAuction(ctx, tc.auctionArgs.seller, tc.auctionArgs.bid, tc.auctionArgs.lot, tc.auctionArgs.debt)
case CollateralPhase1, CollateralPhase2:
id, _ = keeper.StartCollateralAuction(ctx, tc.auctionArgs.seller, tc.auctionArgs.lot, tc.auctionArgs.bid, tc.auctionArgs.addresses, tc.auctionArgs.weights, tc.auctionArgs.debt) // seller, lot, maxBid, otherPerson
// Move CollateralAuction to debt phase by placing max bid
if tc.auctionArgs.auctionType == CollateralPhase2 {
err = keeper.PlaceBid(ctx, id, tc.bidArgs.bidder, tc.auctionArgs.bid)
require.NoError(t, err)
}
default:
t.Fail()
}
// Close the auction early to test late bidding (if applicable)
if strings.Contains(tc.name, "closed") {
ctx = ctx.WithBlockTime(types.DistantFuture.Add(1))
}
// Place bid on auction
err = keeper.PlaceBid(ctx, id, tc.bidArgs.bidder, tc.bidArgs.amount)
// Place second bid from new bidder
if tc.bidArgs.secondBidder != nil {
// Set bid increase/decrease based on auction type, phase
var secondBid sdk.Coin
switch tc.auctionArgs.auctionType {
case Surplus, CollateralPhase1:
secondBid = tc.bidArgs.amount.Add(c(tc.bidArgs.amount.Denom, 1))
case Debt, CollateralPhase2:
secondBid = tc.bidArgs.amount.Sub(c(tc.bidArgs.amount.Denom, 1))
default:
t.Fail()
}
// Place the second bid
err2 := keeper.PlaceBid(ctx, id, tc.bidArgs.secondBidder, secondBid)
require.NoError(t, err2)
}
// Check success/failure
if tc.expectpass {
require.Nil(t, err)
// Get auction from store
auction, found := keeper.GetAuction(ctx, id)
require.True(t, found)
// Check auction values
require.Equal(t, modName, auction.GetInitiator())
require.Equal(t, tc.expectedBidder, auction.GetBidder())
require.Equal(t, tc.expectedBid, auction.GetBid())
require.Equal(t, tc.expectedEndTime, auction.GetEndTime())
} else {
// Check expected error message
require.Contains(t, err.Error(), tc.expectedError)
}
})
}
}

View File

@ -1,7 +1,6 @@
package keeper
import (
"fmt"
"github.com/cosmos/cosmos-sdk/codec"
sdk "github.com/cosmos/cosmos-sdk/types"
"github.com/kava-labs/kava/x/auction/types"
@ -21,10 +20,10 @@ func NewQuerier(keeper Keeper) sdk.Querier {
}
func queryAuctions(ctx sdk.Context, req abci.RequestQuery, keeper Keeper) (res []byte, err sdk.Error) {
var auctionsList types.QueryResAuctions
var auctionsList types.Auctions
keeper.IterateAuctions(ctx, func(a types.Auction) bool {
auctionsList = append(auctionsList, fmt.Sprintf("%+v", a)) // TODO formatting
auctionsList = append(auctionsList, a)
return false
})

View File

@ -0,0 +1,104 @@
package keeper_test
import (
"math/rand"
"strings"
"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/simulation"
"github.com/cosmos/cosmos-sdk/x/supply"
"github.com/kava-labs/kava/app"
"github.com/kava-labs/kava/x/auction/keeper"
"github.com/kava-labs/kava/x/auction/types"
"github.com/kava-labs/kava/x/cdp"
"github.com/stretchr/testify/suite"
abci "github.com/tendermint/tendermint/abci/types"
tmtime "github.com/tendermint/tendermint/types/time"
)
const (
custom = "custom"
TestAuctionCount = 10
)
type QuerierTestSuite struct {
suite.Suite
keeper keeper.Keeper
app app.TestApp
auctions types.Auctions
ctx sdk.Context
querier sdk.Querier
}
func (suite *QuerierTestSuite) SetupTest() {
tApp := app.NewTestApp()
ctx := tApp.NewContext(true, abci.Header{Height: 1, Time: tmtime.Now()})
_, addrs := app.GeneratePrivKeyAddressPairs(1)
buyer := addrs[0]
modName := cdp.LiquidatorMacc
// Set up seller account
sellerAcc := supply.NewEmptyModuleAccount(modName, supply.Minter, supply.Burner)
sellerAcc.SetCoins(cs(c("token1", 1000), c("token2", 1000), c("debt", 1000)))
// Initialize genesis accounts
tApp.InitializeFromGenesisStates(
NewAuthGenStateFromAccs(authexported.GenesisAccounts{
auth.NewBaseAccount(buyer, cs(c("token1", 1000), c("token2", 1000)), nil, 0, 0),
sellerAcc,
}),
)
suite.ctx = ctx
suite.app = tApp
suite.keeper = tApp.GetAuctionKeeper()
// Populate with auctions
for j := 0; j < TestAuctionCount; j++ {
lotAmount := simulation.RandIntBetween(rand.New(rand.NewSource(int64(j))), 10, 100)
id, err := suite.keeper.StartSurplusAuction(suite.ctx, modName, c("token1", int64(lotAmount)), "token2")
suite.Nil(err)
auc, found := suite.keeper.GetAuction(suite.ctx, id)
suite.True(found)
suite.auctions = append(suite.auctions, auc)
}
suite.querier = keeper.NewQuerier(suite.keeper)
}
func (suite *QuerierTestSuite) TestQueryAuctions() {
ctx := suite.ctx.WithIsCheckTx(false)
// Set up request query
query := abci.RequestQuery{
Path: strings.Join([]string{custom, types.QuerierRoute, types.QueryGetAuction}, "/"),
Data: types.ModuleCdc.MustMarshalJSON(types.NewQueryAllAuctionParams(1, TestAuctionCount)),
}
// Execute query and check the []byte result
bz, err := suite.querier(ctx, []string{types.QueryGetAuction}, query)
suite.NoError(err)
suite.NotNil(bz)
// Unmarshal the bytes into type Auctions
var auctions types.Auctions
suite.Nil(types.ModuleCdc.UnmarshalJSON(bz, &auctions))
// Check that each Auction has correct values
for i := 0; i < TestAuctionCount; i++ {
suite.Equal(suite.auctions[i].GetID(), auctions[i].GetID())
suite.Equal(suite.auctions[i].GetInitiator(), auctions[i].GetInitiator())
suite.Equal(suite.auctions[i].GetLot(), auctions[i].GetLot())
suite.Equal(suite.auctions[i].GetBid(), auctions[i].GetBid())
suite.Equal(suite.auctions[i].GetEndTime(), auctions[i].GetEndTime())
}
}
func TestQuerierTestSuite(t *testing.T) {
suite.Run(t, new(QuerierTestSuite))
}

View File

@ -17,9 +17,16 @@ var DistantFuture time.Time = time.Date(9000, 1, 1, 0, 0, 0, 0, time.UTC)
type Auction interface {
GetID() uint64
WithID(uint64) Auction
GetInitiator() string
GetLot() sdk.Coin
GetBidder() sdk.AccAddress
GetBid() sdk.Coin
GetEndTime() time.Time
}
// Auctions is a slice of auctions.
type Auctions []Auction
// BaseAuction is a common type shared by all Auctions.
type BaseAuction struct {
ID uint64
@ -35,6 +42,18 @@ type BaseAuction struct {
// GetID is a getter for auction ID.
func (a BaseAuction) GetID() uint64 { return a.ID }
// GetInitiator is a getter for auction Initiator.
func (a BaseAuction) GetInitiator() string { return a.Initiator }
// GetLot is a getter for auction Lot.
func (a BaseAuction) GetLot() sdk.Coin { return a.Lot }
// GetBidder is a getter for auction Bidder.
func (a BaseAuction) GetBidder() sdk.AccAddress { return a.Bidder }
// GetBid is a getter for auction Bid.
func (a BaseAuction) GetBid() sdk.Coin { return a.Bid }
// GetEndTime is a getter for auction end time.
func (a BaseAuction) GetEndTime() time.Time { return a.EndTime }

View File

@ -0,0 +1,195 @@
package types
import (
"testing"
"time"
sdk "github.com/cosmos/cosmos-sdk/types"
"github.com/stretchr/testify/require"
)
const (
TestInitiatorModuleName = "liquidator"
TestLotDenom = "usdx"
TestLotAmount = 100
TestBidDenom = "kava"
TestBidAmount = 20
TestDebtDenom = "debt"
TestDebtAmount1 = 20
TestDebtAmount2 = 15
TestExtraEndTime = 10000
TestAuctionID = 9999123
TestAccAddress1 = "kava1qcfdf69js922qrdr4yaww3ax7gjml6pd39p8lj"
TestAccAddress2 = "kava1pdfav2cjhry9k79nu6r8kgknnjtq6a7rcr0qlr"
)
func TestNewWeightedAddresses(t *testing.T) {
tests := []struct {
name string
addresses []sdk.AccAddress
weights []sdk.Int
expectpass bool
}{
{
"normal",
[]sdk.AccAddress{
sdk.AccAddress([]byte(TestAccAddress1)),
sdk.AccAddress([]byte(TestAccAddress2)),
},
[]sdk.Int{
sdk.NewInt(6),
sdk.NewInt(8),
},
true,
},
{
"mismatched",
[]sdk.AccAddress{
sdk.AccAddress([]byte(TestAccAddress1)),
sdk.AccAddress([]byte(TestAccAddress2)),
},
[]sdk.Int{
sdk.NewInt(6),
},
false,
},
{
"negativeWeight",
[]sdk.AccAddress{
sdk.AccAddress([]byte(TestAccAddress1)),
sdk.AccAddress([]byte(TestAccAddress2)),
},
[]sdk.Int{
sdk.NewInt(6),
sdk.NewInt(-8),
},
false,
},
}
// Run NewWeightedAdresses tests
for _, tc := range tests {
t.Run(tc.name, func(t *testing.T) {
// Attempt to instantiate new WeightedAddresses
weightedAddresses, err := NewWeightedAddresses(tc.addresses, tc.weights)
if tc.expectpass {
// Confirm there is no error
require.Nil(t, err)
// Check addresses, weights
require.Equal(t, tc.addresses, weightedAddresses.Addresses)
require.Equal(t, tc.weights, weightedAddresses.Weights)
} else {
// Confirm that there is an error
require.NotNil(t, err)
switch tc.name {
case "mismatched":
require.Contains(t, err.Error(), "number of addresses doesn't match number of weights")
case "negativeWeight":
require.Contains(t, err.Error(), "weights contain a negative amount")
default:
// Unexpected error state
t.Fail()
}
}
})
}
}
func TestBaseAuctionGetters(t *testing.T) {
endTime := time.Now().Add(TestExtraEndTime)
// Create a new BaseAuction (via SurplusAuction)
auction := NewSurplusAuction(
TestInitiatorModuleName,
c(TestLotDenom, TestLotAmount),
TestBidDenom, endTime,
)
auctionID := auction.GetID()
auctionBid := auction.GetBid()
auctionLot := auction.GetLot()
auctionEndTime := auction.GetEndTime()
auctionString := auction.String()
require.Equal(t, auction.ID, auctionID)
require.Equal(t, auction.Bid, auctionBid)
require.Equal(t, auction.Lot, auctionLot)
require.Equal(t, auction.EndTime, auctionEndTime)
require.NotNil(t, auctionString)
}
func TestNewSurplusAuction(t *testing.T) {
endTime := time.Now().Add(TestExtraEndTime)
// Create a new SurplusAuction
surplusAuction := NewSurplusAuction(
TestInitiatorModuleName,
c(TestLotDenom, TestLotAmount),
TestBidDenom, endTime,
)
require.Equal(t, surplusAuction.Initiator, TestInitiatorModuleName)
require.Equal(t, surplusAuction.Lot, c(TestLotDenom, TestLotAmount))
require.Equal(t, surplusAuction.Bid, c(TestBidDenom, 0))
require.Equal(t, surplusAuction.EndTime, endTime)
require.Equal(t, surplusAuction.MaxEndTime, endTime)
}
func TestNewDebtAuction(t *testing.T) {
endTime := time.Now().Add(TestExtraEndTime)
// Create a new DebtAuction
debtAuction := NewDebtAuction(
TestInitiatorModuleName,
c(TestBidDenom, TestBidAmount),
c(TestLotDenom, TestLotAmount),
endTime,
c(TestDebtDenom, TestDebtAmount1),
)
require.Equal(t, debtAuction.Initiator, TestInitiatorModuleName)
require.Equal(t, debtAuction.Lot, c(TestLotDenom, TestLotAmount))
require.Equal(t, debtAuction.Bid, c(TestBidDenom, TestBidAmount))
require.Equal(t, debtAuction.EndTime, endTime)
require.Equal(t, debtAuction.MaxEndTime, endTime)
require.Equal(t, debtAuction.CorrespondingDebt, c(TestDebtDenom, TestDebtAmount1))
}
func TestNewCollateralAuction(t *testing.T) {
// Set up WeightedAddresses
addresses := []sdk.AccAddress{
sdk.AccAddress([]byte(TestAccAddress1)),
sdk.AccAddress([]byte(TestAccAddress2)),
}
weights := []sdk.Int{
sdk.NewInt(6),
sdk.NewInt(8),
}
weightedAddresses, _ := NewWeightedAddresses(addresses, weights)
endTime := time.Now().Add(TestExtraEndTime)
collateralAuction := NewCollateralAuction(
TestInitiatorModuleName,
c(TestLotDenom, TestLotAmount),
endTime,
c(TestBidDenom, TestBidAmount),
weightedAddresses,
c(TestDebtDenom, TestDebtAmount2),
)
require.Equal(t, collateralAuction.BaseAuction.Initiator, TestInitiatorModuleName)
require.Equal(t, collateralAuction.BaseAuction.Lot, c(TestLotDenom, TestLotAmount))
require.Equal(t, collateralAuction.BaseAuction.Bid, c(TestBidDenom, 0))
require.Equal(t, collateralAuction.BaseAuction.EndTime, endTime)
require.Equal(t, collateralAuction.BaseAuction.MaxEndTime, endTime)
require.Equal(t, collateralAuction.MaxBid, c(TestBidDenom, TestBidAmount))
require.Equal(t, collateralAuction.LotReturns, weightedAddresses)
require.Equal(t, collateralAuction.CorrespondingDebt, c(TestDebtDenom, TestDebtAmount2))
}

View File

@ -19,6 +19,8 @@ const (
// DefaultParamspace default name for parameter store
DefaultParamspace = ModuleName
QuerierRoute = ModuleName
)
var (

View File

@ -16,3 +16,17 @@ type QueryResAuctions []string
func (n QueryResAuctions) String() string {
return strings.Join(n[:], "\n")
}
// QueryAllAuctionParams is the params for an auctions query
type QueryAllAuctionParams struct {
Page int `json"page:" yaml:"page"`
Limit int `json"limit:" yaml:"limit"`
}
// NewQueryAllAuctionParams creates a new QueryAllAuctionParams
func NewQueryAllAuctionParams(page int, limit int) QueryAllAuctionParams {
return QueryAllAuctionParams{
Page: page,
Limit: limit,
}
}