mirror of
https://github.com/0glabs/0g-chain.git
synced 2024-12-24 23:35:19 +00:00
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:
parent
22e168d06a
commit
8128a680cc
@ -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")
|
||||
}
|
||||
|
@ -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)
|
||||
}
|
||||
|
349
x/auction/keeper/bidding_test.go
Normal file
349
x/auction/keeper/bidding_test.go
Normal 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)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
@ -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
|
||||
})
|
||||
|
||||
|
104
x/auction/keeper/querier_test.go
Normal file
104
x/auction/keeper/querier_test.go
Normal 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))
|
||||
}
|
@ -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 }
|
||||
|
||||
|
195
x/auction/types/auctions_test.go
Normal file
195
x/auction/types/auctions_test.go
Normal 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))
|
||||
}
|
@ -19,6 +19,8 @@ const (
|
||||
|
||||
// DefaultParamspace default name for parameter store
|
||||
DefaultParamspace = ModuleName
|
||||
|
||||
QuerierRoute = ModuleName
|
||||
)
|
||||
|
||||
var (
|
||||
|
@ -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,
|
||||
}
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user