mirror of
https://github.com/0glabs/0g-chain.git
synced 2024-11-10 10:05:18 +00:00
Add min bid increments (#380)
* refactor bidding test * add some more bid test cases * add balance checks to bid tests * add more checks to bid tests * add min bid increments * protect against negative lot amounts * fix params tests * change endblocker to beginblocker * update spec * fix params tests * fix: update alias Co-authored-by: Kevin Davis <karzak@users.noreply.github.com>
This commit is contained in:
parent
e72b20eb7d
commit
7eede47769
@ -271,9 +271,10 @@ func NewApp(logger log.Logger, db dbm.DB, traceStore io.Writer, loadLatest bool,
|
||||
// During begin block slashing happens after distr.BeginBlocker so that
|
||||
// there is nothing left over in the validator fee pool, so as to keep the
|
||||
// CanWithdrawInvariant invariant.
|
||||
app.mm.SetOrderBeginBlockers(mint.ModuleName, distr.ModuleName, slashing.ModuleName, validatorvesting.ModuleName, cdp.ModuleName)
|
||||
// Auction.BeginBlocker will close out expired auctions and pay debt back to cdp. So it should be run before cdp.BeginBlocker which cancels out debt with stable and starts more auctions.
|
||||
app.mm.SetOrderBeginBlockers(mint.ModuleName, distr.ModuleName, slashing.ModuleName, validatorvesting.ModuleName, auction.ModuleName, cdp.ModuleName)
|
||||
|
||||
app.mm.SetOrderEndBlockers(crisis.ModuleName, gov.ModuleName, staking.ModuleName, pricefeed.ModuleName, auction.ModuleName)
|
||||
app.mm.SetOrderEndBlockers(crisis.ModuleName, gov.ModuleName, staking.ModuleName, pricefeed.ModuleName)
|
||||
|
||||
// Note: genutils must occur after staking so that pools are properly
|
||||
// initialized with tokens from genesis accounts.
|
||||
|
@ -4,8 +4,8 @@ import (
|
||||
sdk "github.com/cosmos/cosmos-sdk/types"
|
||||
)
|
||||
|
||||
// EndBlocker runs at the end of every block.
|
||||
func EndBlocker(ctx sdk.Context, k Keeper) {
|
||||
// BeginBlocker runs at the start of every block.
|
||||
func BeginBlocker(ctx sdk.Context, k Keeper) {
|
||||
err := k.CloseExpiredAuctions(ctx)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
|
@ -15,7 +15,7 @@ import (
|
||||
"github.com/kava-labs/kava/x/cdp"
|
||||
)
|
||||
|
||||
func TestKeeper_EndBlocker(t *testing.T) {
|
||||
func TestKeeper_BeginBlocker(t *testing.T) {
|
||||
// Setup
|
||||
_, addrs := app.GeneratePrivKeyAddressPairs(2)
|
||||
buyer := addrs[0]
|
||||
@ -36,13 +36,14 @@ func TestKeeper_EndBlocker(t *testing.T) {
|
||||
ctx := tApp.NewContext(true, abci.Header{})
|
||||
keeper := tApp.GetAuctionKeeper()
|
||||
|
||||
// Start an auction and place a bid
|
||||
auctionID, err := keeper.StartCollateralAuction(ctx, sellerModName, c("token1", 20), c("token2", 50), returnAddrs, returnWeights, c("debt", 40))
|
||||
require.NoError(t, err)
|
||||
require.NoError(t, keeper.PlaceBid(ctx, auctionID, buyer, c("token2", 30)))
|
||||
|
||||
// Run the endblocker, simulating a block time 1ns before auction expiry
|
||||
// Run the beginblocker, simulating a block time 1ns before auction expiry
|
||||
preExpiryTime := ctx.BlockTime().Add(auction.DefaultBidDuration - 1)
|
||||
auction.EndBlocker(ctx.WithBlockTime(preExpiryTime), keeper)
|
||||
auction.BeginBlocker(ctx.WithBlockTime(preExpiryTime), keeper)
|
||||
|
||||
// Check auction has not been closed yet
|
||||
_, found := keeper.GetAuction(ctx, auctionID)
|
||||
@ -50,7 +51,7 @@ func TestKeeper_EndBlocker(t *testing.T) {
|
||||
|
||||
// Run the endblocker, simulating a block time equal to auction expiry
|
||||
expiryTime := ctx.BlockTime().Add(auction.DefaultBidDuration)
|
||||
auction.EndBlocker(ctx.WithBlockTime(expiryTime), keeper)
|
||||
auction.BeginBlocker(ctx.WithBlockTime(expiryTime), keeper)
|
||||
|
||||
// Check auction has been closed
|
||||
_, found = keeper.GetAuction(ctx, auctionID)
|
||||
|
@ -1,8 +1,8 @@
|
||||
// nolint
|
||||
// autogenerated code using github.com/rigelrozanski/multitool
|
||||
// aliases generated for the following subdirectories:
|
||||
// ALIASGEN: github.com/kava-labs/kava/x/auction/types/
|
||||
// ALIASGEN: github.com/kava-labs/kava/x/auction/keeper/
|
||||
// ALIASGEN: github.com/kava-labs/kava/x/auction/keeper
|
||||
// ALIASGEN: github.com/kava-labs/kava/x/auction/types
|
||||
package auction
|
||||
|
||||
import (
|
||||
@ -13,6 +13,7 @@ import (
|
||||
const (
|
||||
DefaultCodespace = types.DefaultCodespace
|
||||
CodeInvalidInitialAuctionID = types.CodeInvalidInitialAuctionID
|
||||
CodeInvalidModulePermissions = types.CodeInvalidModulePermissions
|
||||
CodeUnrecognizedAuctionType = types.CodeUnrecognizedAuctionType
|
||||
CodeAuctionNotFound = types.CodeAuctionNotFound
|
||||
CodeAuctionHasNotExpired = types.CodeAuctionHasNotExpired
|
||||
@ -21,60 +22,101 @@ const (
|
||||
CodeInvalidLotDenom = types.CodeInvalidLotDenom
|
||||
CodeBidTooSmall = types.CodeBidTooSmall
|
||||
CodeBidTooLarge = types.CodeBidTooLarge
|
||||
CodeLotTooSmall = types.CodeLotTooSmall
|
||||
CodeLotTooLarge = types.CodeLotTooLarge
|
||||
CodeCollateralAuctionIsInReversePhase = types.CodeCollateralAuctionIsInReversePhase
|
||||
CodeCollateralAuctionIsInForwardPhase = types.CodeCollateralAuctionIsInForwardPhase
|
||||
EventTypeAuctionStart = types.EventTypeAuctionStart
|
||||
EventTypeAuctionBid = types.EventTypeAuctionBid
|
||||
EventTypeAuctionClose = types.EventTypeAuctionClose
|
||||
AttributeValueCategory = types.AttributeValueCategory
|
||||
AttributeKeyAuctionID = types.AttributeKeyAuctionID
|
||||
AttributeKeyAuctionType = types.AttributeKeyAuctionType
|
||||
AttributeKeyBidder = types.AttributeKeyBidder
|
||||
AttributeKeyBidDenom = types.AttributeKeyBidDenom
|
||||
AttributeKeyLotDenom = types.AttributeKeyLotDenom
|
||||
AttributeKeyBidAmount = types.AttributeKeyBidAmount
|
||||
AttributeKeyLotAmount = types.AttributeKeyLotAmount
|
||||
AttributeKeyEndTime = types.AttributeKeyEndTime
|
||||
DefaultNextAuctionID = types.DefaultNextAuctionID
|
||||
ModuleName = types.ModuleName
|
||||
StoreKey = types.StoreKey
|
||||
RouterKey = types.RouterKey
|
||||
DefaultParamspace = types.DefaultParamspace
|
||||
QuerierRoute = types.QuerierRoute
|
||||
DefaultMaxAuctionDuration = types.DefaultMaxAuctionDuration
|
||||
DefaultBidDuration = types.DefaultBidDuration
|
||||
QueryGetAuction = types.QueryGetAuction
|
||||
DefaultNextAuctionID = types.DefaultNextAuctionID
|
||||
QueryGetAuctions = types.QueryGetAuctions
|
||||
QueryGetParams = types.QueryGetParams
|
||||
)
|
||||
|
||||
var (
|
||||
// functions aliases
|
||||
NewKeeper = keeper.NewKeeper
|
||||
NewQuerier = keeper.NewQuerier
|
||||
NewSurplusAuction = types.NewSurplusAuction
|
||||
NewDebtAuction = types.NewDebtAuction
|
||||
NewCollateralAuction = types.NewCollateralAuction
|
||||
NewWeightedAddresses = types.NewWeightedAddresses
|
||||
RegisterCodec = types.RegisterCodec
|
||||
ErrInvalidInitialAuctionID = types.ErrInvalidInitialAuctionID
|
||||
ErrInvalidModulePermissions = types.ErrInvalidModulePermissions
|
||||
ErrUnrecognizedAuctionType = types.ErrUnrecognizedAuctionType
|
||||
ErrAuctionNotFound = types.ErrAuctionNotFound
|
||||
ErrAuctionHasNotExpired = types.ErrAuctionHasNotExpired
|
||||
ErrAuctionHasExpired = types.ErrAuctionHasExpired
|
||||
ErrInvalidBidDenom = types.ErrInvalidBidDenom
|
||||
ErrInvalidLotDenom = types.ErrInvalidLotDenom
|
||||
ErrBidTooSmall = types.ErrBidTooSmall
|
||||
ErrBidTooLarge = types.ErrBidTooLarge
|
||||
ErrLotTooSmall = types.ErrLotTooSmall
|
||||
ErrLotTooLarge = types.ErrLotTooLarge
|
||||
ErrCollateralAuctionIsInReversePhase = types.ErrCollateralAuctionIsInReversePhase
|
||||
ErrCollateralAuctionIsInForwardPhase = types.ErrCollateralAuctionIsInForwardPhase
|
||||
NewGenesisState = types.NewGenesisState
|
||||
DefaultGenesisState = types.DefaultGenesisState
|
||||
GetAuctionKey = types.GetAuctionKey
|
||||
GetAuctionByTimeKey = types.GetAuctionByTimeKey
|
||||
Uint64FromBytes = types.Uint64FromBytes
|
||||
Uint64ToBytes = types.Uint64ToBytes
|
||||
Uint64FromBytes = types.Uint64FromBytes
|
||||
NewMsgPlaceBid = types.NewMsgPlaceBid
|
||||
NewParams = types.NewParams
|
||||
DefaultParams = types.DefaultParams
|
||||
ParamKeyTable = types.ParamKeyTable
|
||||
NewKeeper = keeper.NewKeeper
|
||||
NewQuerier = keeper.NewQuerier
|
||||
NewQueryAllAuctionParams = types.NewQueryAllAuctionParams
|
||||
NewAuctionWithPhase = types.NewAuctionWithPhase
|
||||
|
||||
// variable aliases
|
||||
DistantFuture = types.DistantFuture
|
||||
ModuleCdc = types.ModuleCdc
|
||||
AuctionKeyPrefix = types.AuctionKeyPrefix
|
||||
AuctionByTimeKeyPrefix = types.AuctionByTimeKeyPrefix
|
||||
NextAuctionIDKey = types.NextAuctionIDKey
|
||||
KeyAuctionBidDuration = types.KeyAuctionBidDuration
|
||||
KeyAuctionDuration = types.KeyAuctionDuration
|
||||
DefaultIncrement = types.DefaultIncrement
|
||||
KeyBidDuration = types.KeyBidDuration
|
||||
KeyMaxAuctionDuration = types.KeyMaxAuctionDuration
|
||||
KeyIncrementSurplus = types.KeyIncrementSurplus
|
||||
KeyIncrementDebt = types.KeyIncrementDebt
|
||||
KeyIncrementCollateral = types.KeyIncrementCollateral
|
||||
)
|
||||
|
||||
type (
|
||||
Keeper = keeper.Keeper
|
||||
Auction = types.Auction
|
||||
Auctions = types.Auctions
|
||||
BaseAuction = types.BaseAuction
|
||||
SurplusAuction = types.SurplusAuction
|
||||
DebtAuction = types.DebtAuction
|
||||
CollateralAuction = types.CollateralAuction
|
||||
WeightedAddresses = types.WeightedAddresses
|
||||
SupplyKeeper = types.SupplyKeeper
|
||||
GenesisAuctions = types.GenesisAuctions
|
||||
GenesisAuction = types.GenesisAuction
|
||||
GenesisAuctions = types.GenesisAuctions
|
||||
GenesisState = types.GenesisState
|
||||
MsgPlaceBid = types.MsgPlaceBid
|
||||
Params = types.Params
|
||||
Keeper = keeper.Keeper
|
||||
QueryAuctionParams = types.QueryAuctionParams
|
||||
QueryAllAuctionParams = types.QueryAllAuctionParams
|
||||
AuctionWithPhase = types.AuctionWithPhase
|
||||
)
|
||||
|
@ -168,8 +168,14 @@ func (k Keeper) PlaceBidSurplus(ctx sdk.Context, a types.SurplusAuction, bidder
|
||||
if bid.Denom != a.Bid.Denom {
|
||||
return a, types.ErrInvalidBidDenom(k.codespace, bid.Denom, a.Bid.Denom)
|
||||
}
|
||||
if !a.Bid.IsLT(bid) {
|
||||
return a, types.ErrBidTooSmall(k.codespace, bid, a.Bid)
|
||||
minNewBidAmt := a.Bid.Amount.Add( // new bids must be some % greater than old bid, and at least 1 larger to avoid replacing an old bid at no cost
|
||||
sdk.MaxInt(
|
||||
sdk.NewInt(1),
|
||||
sdk.NewDecFromInt(a.Bid.Amount).Mul(k.GetParams(ctx).IncrementSurplus).RoundInt(),
|
||||
),
|
||||
)
|
||||
if bid.Amount.LT(minNewBidAmt) {
|
||||
return a, types.ErrBidTooSmall(k.codespace, bid, sdk.NewCoin(a.Bid.Denom, minNewBidAmt))
|
||||
}
|
||||
|
||||
// New bidder pays back old bidder
|
||||
@ -225,8 +231,15 @@ func (k Keeper) PlaceForwardBidCollateral(ctx sdk.Context, a types.CollateralAuc
|
||||
if a.IsReversePhase() {
|
||||
return a, types.ErrCollateralAuctionIsInReversePhase(k.codespace, a.ID)
|
||||
}
|
||||
if !a.Bid.IsLT(bid) {
|
||||
return a, types.ErrBidTooSmall(k.codespace, bid, a.Bid)
|
||||
minNewBidAmt := a.Bid.Amount.Add( // new bids must be some % greater than old bid, and at least 1 larger to avoid replacing an old bid at no cost
|
||||
sdk.MaxInt(
|
||||
sdk.NewInt(1),
|
||||
sdk.NewDecFromInt(a.Bid.Amount).Mul(k.GetParams(ctx).IncrementCollateral).RoundInt(),
|
||||
),
|
||||
)
|
||||
minNewBidAmt = sdk.MinInt(minNewBidAmt, a.MaxBid.Amount) // allow new bids to hit MaxBid even though it may be less than the increment %
|
||||
if bid.Amount.LT(minNewBidAmt) {
|
||||
return a, types.ErrBidTooSmall(k.codespace, bid, sdk.NewCoin(a.Bid.Denom, minNewBidAmt))
|
||||
}
|
||||
if a.MaxBid.IsLT(bid) {
|
||||
return a, types.ErrBidTooLarge(k.codespace, bid, a.MaxBid)
|
||||
@ -294,8 +307,17 @@ func (k Keeper) PlaceReverseBidCollateral(ctx sdk.Context, a types.CollateralAuc
|
||||
if !a.IsReversePhase() {
|
||||
return a, types.ErrCollateralAuctionIsInForwardPhase(k.codespace, a.ID)
|
||||
}
|
||||
if !lot.IsLT(a.Lot) {
|
||||
return a, types.ErrLotTooLarge(k.codespace, lot, a.Lot)
|
||||
maxNewLotAmt := a.Lot.Amount.Sub( // new lot must be some % less than old lot, and at least 1 smaller to avoid replacing an old bid at no cost
|
||||
sdk.MaxInt(
|
||||
sdk.NewInt(1),
|
||||
sdk.NewDecFromInt(a.Lot.Amount).Mul(k.GetParams(ctx).IncrementCollateral).RoundInt(),
|
||||
),
|
||||
)
|
||||
if lot.Amount.GT(maxNewLotAmt) {
|
||||
return a, types.ErrLotTooLarge(k.codespace, lot, sdk.NewCoin(a.Lot.Denom, maxNewLotAmt))
|
||||
}
|
||||
if lot.IsNegative() {
|
||||
return a, types.ErrLotTooSmall(k.codespace, lot, sdk.NewCoin(a.Lot.Denom, sdk.ZeroInt()))
|
||||
}
|
||||
|
||||
// New bidder pays back old bidder
|
||||
@ -351,8 +373,17 @@ func (k Keeper) PlaceBidDebt(ctx sdk.Context, a types.DebtAuction, bidder sdk.Ac
|
||||
if lot.Denom != a.Lot.Denom {
|
||||
return a, types.ErrInvalidLotDenom(k.codespace, lot.Denom, a.Lot.Denom)
|
||||
}
|
||||
if !lot.IsLT(a.Lot) {
|
||||
return a, types.ErrLotTooLarge(k.codespace, lot, a.Lot)
|
||||
maxNewLotAmt := a.Lot.Amount.Sub( // new lot must be some % less than old lot, and at least 1 smaller to avoid replacing an old bid at no cost
|
||||
sdk.MaxInt(
|
||||
sdk.NewInt(1),
|
||||
sdk.NewDecFromInt(a.Lot.Amount).Mul(k.GetParams(ctx).IncrementDebt).RoundInt(),
|
||||
),
|
||||
)
|
||||
if lot.Amount.GT(maxNewLotAmt) {
|
||||
return a, types.ErrLotTooLarge(k.codespace, lot, sdk.NewCoin(a.Lot.Denom, maxNewLotAmt))
|
||||
}
|
||||
if lot.IsNegative() {
|
||||
return a, types.ErrLotTooSmall(k.codespace, lot, sdk.NewCoin(a.Lot.Denom, sdk.ZeroInt()))
|
||||
}
|
||||
|
||||
// New bidder pays back old bidder
|
||||
|
@ -22,8 +22,7 @@ const (
|
||||
Invalid AuctionType = 0
|
||||
Surplus AuctionType = 1
|
||||
Debt AuctionType = 2
|
||||
CollateralPhase1 AuctionType = 3
|
||||
CollateralPhase2 AuctionType = 4
|
||||
Collateral AuctionType = 3
|
||||
)
|
||||
|
||||
func TestAuctionBidding(t *testing.T) {
|
||||
@ -49,12 +48,12 @@ func TestAuctionBidding(t *testing.T) {
|
||||
type bidArgs struct {
|
||||
bidder sdk.AccAddress
|
||||
amount sdk.Coin
|
||||
secondBidder sdk.AccAddress
|
||||
}
|
||||
|
||||
tests := []struct {
|
||||
name string
|
||||
auctionArgs auctionArgs
|
||||
setupBids []bidArgs
|
||||
bidArgs bidArgs
|
||||
expectedError sdk.CodeType
|
||||
expectedEndTime time.Time
|
||||
@ -65,17 +64,30 @@ func TestAuctionBidding(t *testing.T) {
|
||||
{
|
||||
"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},
|
||||
nil,
|
||||
bidArgs{buyer, c("token2", 10)},
|
||||
types.CodeAuctionNotFound,
|
||||
someTime.Add(types.DefaultBidDuration),
|
||||
buyer,
|
||||
c("token2", 10),
|
||||
false,
|
||||
},
|
||||
{
|
||||
"basic: closed auction",
|
||||
auctionArgs{Surplus, modName, c("token1", 100), c("token2", 10), sdk.Coin{}, []sdk.AccAddress{}, []sdk.Int{}},
|
||||
nil,
|
||||
bidArgs{buyer, c("token2", 10)},
|
||||
types.CodeAuctionHasExpired,
|
||||
types.DistantFuture,
|
||||
nil,
|
||||
c("token2", 0),
|
||||
false,
|
||||
},
|
||||
{
|
||||
"surplus: normal",
|
||||
auctionArgs{Surplus, modName, c("token1", 100), c("token2", 10), sdk.Coin{}, []sdk.AccAddress{}, []sdk.Int{}},
|
||||
bidArgs{buyer, c("token2", 10), nil},
|
||||
nil,
|
||||
bidArgs{buyer, c("token2", 10)},
|
||||
sdk.CodeType(0),
|
||||
someTime.Add(types.DefaultBidDuration),
|
||||
buyer,
|
||||
@ -85,7 +97,8 @@ func TestAuctionBidding(t *testing.T) {
|
||||
{
|
||||
"surplus: second bidder",
|
||||
auctionArgs{Surplus, modName, c("token1", 100), c("token2", 10), sdk.Coin{}, []sdk.AccAddress{}, []sdk.Int{}},
|
||||
bidArgs{buyer, c("token2", 10), secondBuyer},
|
||||
[]bidArgs{{buyer, c("token2", 10)}},
|
||||
bidArgs{secondBuyer, c("token2", 11)},
|
||||
sdk.CodeType(0),
|
||||
someTime.Add(types.DefaultBidDuration),
|
||||
secondBuyer,
|
||||
@ -95,27 +108,52 @@ func TestAuctionBidding(t *testing.T) {
|
||||
{
|
||||
"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},
|
||||
nil,
|
||||
bidArgs{buyer, c("badtoken", 10)},
|
||||
types.CodeInvalidBidDenom,
|
||||
types.DistantFuture,
|
||||
nil, // surplus auctions are created with initial bidder as a nil address
|
||||
c("token2", 0),
|
||||
false,
|
||||
},
|
||||
{
|
||||
"surplus: invalid bid (less than)",
|
||||
auctionArgs{Surplus, modName, c("token1", 100), c("token2", 0), sdk.Coin{}, []sdk.AccAddress{}, []sdk.Int{}},
|
||||
[]bidArgs{{buyer, c("token2", 100)}},
|
||||
bidArgs{buyer, c("token2", 99)},
|
||||
types.CodeBidTooSmall,
|
||||
someTime.Add(types.DefaultBidDuration),
|
||||
buyer,
|
||||
c("token2", 10),
|
||||
c("token2", 100),
|
||||
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},
|
||||
nil,
|
||||
bidArgs{buyer, c("token2", 0)}, // min bid is technically 0 at default 5%, but it's capped at 1
|
||||
types.CodeBidTooSmall,
|
||||
types.DistantFuture,
|
||||
nil, // surplus auctions are created with initial bidder as a nil address
|
||||
c("token2", 0),
|
||||
false,
|
||||
},
|
||||
{
|
||||
"surplus: invalid bid (less than min increment)",
|
||||
auctionArgs{Surplus, modName, c("token1", 100), c("token2", 0), sdk.Coin{}, []sdk.AccAddress{}, []sdk.Int{}},
|
||||
[]bidArgs{{buyer, c("token2", 100)}},
|
||||
bidArgs{buyer, c("token2", 104)}, // min bid is 105 at default 5%
|
||||
types.CodeBidTooSmall,
|
||||
someTime.Add(types.DefaultBidDuration),
|
||||
buyer,
|
||||
c("token2", 10),
|
||||
c("token2", 100),
|
||||
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},
|
||||
auctionArgs{Debt, modName, c("token1", 20), c("token2", 100), c("debt", 100), []sdk.AccAddress{}, []sdk.Int{}}, // initial bid, lot
|
||||
nil,
|
||||
bidArgs{buyer, c("token1", 10)},
|
||||
sdk.CodeType(0),
|
||||
someTime.Add(types.DefaultBidDuration),
|
||||
buyer,
|
||||
@ -124,8 +162,9 @@ func TestAuctionBidding(t *testing.T) {
|
||||
},
|
||||
{
|
||||
"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},
|
||||
auctionArgs{Debt, modName, c("token1", 20), c("token2", 100), c("debt", 100), []sdk.AccAddress{}, []sdk.Int{}}, // initial bid, lot
|
||||
[]bidArgs{{buyer, c("token1", 10)}},
|
||||
bidArgs{secondBuyer, c("token1", 9)},
|
||||
sdk.CodeType(0),
|
||||
someTime.Add(types.DefaultBidDuration),
|
||||
secondBuyer,
|
||||
@ -134,28 +173,53 @@ func TestAuctionBidding(t *testing.T) {
|
||||
},
|
||||
{
|
||||
"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},
|
||||
auctionArgs{Debt, modName, c("token1", 20), c("token2", 100), c("debt", 100), []sdk.AccAddress{}, []sdk.Int{}}, // initial bid, lot
|
||||
nil,
|
||||
bidArgs{buyer, c("badtoken", 10)},
|
||||
types.CodeInvalidLotDenom,
|
||||
someTime.Add(types.DefaultBidDuration),
|
||||
buyer,
|
||||
c("token1", 20),
|
||||
types.DistantFuture,
|
||||
supply.NewModuleAddress(modName),
|
||||
c("token2", 100),
|
||||
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},
|
||||
auctionArgs{Debt, modName, c("token1", 20), c("token2", 100), c("debt", 100), []sdk.AccAddress{}, []sdk.Int{}},
|
||||
nil,
|
||||
bidArgs{buyer, c("token1", 21)},
|
||||
types.CodeLotTooLarge,
|
||||
someTime.Add(types.DefaultBidDuration),
|
||||
buyer,
|
||||
c("token1", 20),
|
||||
types.DistantFuture,
|
||||
supply.NewModuleAddress(modName),
|
||||
c("token2", 100),
|
||||
false,
|
||||
},
|
||||
{
|
||||
"debt: invalid lot size (equal)",
|
||||
auctionArgs{Debt, modName, c("token1", 20), c("token2", 100), c("debt", 100), []sdk.AccAddress{}, []sdk.Int{}},
|
||||
nil,
|
||||
bidArgs{buyer, c("token1", 20)},
|
||||
types.CodeLotTooLarge,
|
||||
types.DistantFuture,
|
||||
supply.NewModuleAddress(modName),
|
||||
c("token2", 100),
|
||||
false,
|
||||
},
|
||||
{
|
||||
"debt: invalid lot size (larger than min increment)",
|
||||
auctionArgs{Debt, modName, c("token1", 60), c("token2", 100), c("debt", 100), []sdk.AccAddress{}, []sdk.Int{}},
|
||||
nil,
|
||||
bidArgs{buyer, c("token1", 58)}, // max lot at default 5% is 57
|
||||
types.CodeLotTooLarge,
|
||||
types.DistantFuture,
|
||||
supply.NewModuleAddress(modName),
|
||||
c("token2", 100),
|
||||
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},
|
||||
auctionArgs{Collateral, modName, c("token1", 20), c("token2", 100), c("debt", 50), collateralAddrs, collateralWeights}, // lot, max bid
|
||||
nil,
|
||||
bidArgs{buyer, c("token2", 10)},
|
||||
sdk.CodeType(0),
|
||||
someTime.Add(types.DefaultBidDuration),
|
||||
buyer,
|
||||
@ -164,8 +228,9 @@ func TestAuctionBidding(t *testing.T) {
|
||||
},
|
||||
{
|
||||
"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},
|
||||
auctionArgs{Collateral, modName, c("token1", 20), c("token2", 100), c("debt", 50), collateralAddrs, collateralWeights}, // lot, max bid
|
||||
[]bidArgs{{buyer, c("token2", 10)}},
|
||||
bidArgs{secondBuyer, c("token2", 11)},
|
||||
sdk.CodeType(0),
|
||||
someTime.Add(types.DefaultBidDuration),
|
||||
secondBuyer,
|
||||
@ -174,18 +239,20 @@ func TestAuctionBidding(t *testing.T) {
|
||||
},
|
||||
{
|
||||
"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},
|
||||
auctionArgs{Collateral, modName, c("token1", 20), c("token2", 100), c("debt", 50), collateralAddrs, collateralWeights}, // lot, max bid
|
||||
nil,
|
||||
bidArgs{buyer, c("badtoken", 10)},
|
||||
types.CodeInvalidBidDenom,
|
||||
someTime.Add(types.DefaultBidDuration),
|
||||
buyer,
|
||||
c("token2", 10),
|
||||
types.DistantFuture,
|
||||
nil,
|
||||
c("token2", 0),
|
||||
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
|
||||
auctionArgs{Collateral, modName, c("token1", 20), c("token2", 100), c("debt", 50), collateralAddrs, collateralWeights}, // lot, max bid
|
||||
[]bidArgs{{buyer, c("token2", 10)}},
|
||||
bidArgs{buyer, c("token2", 9)},
|
||||
types.CodeBidTooSmall,
|
||||
someTime.Add(types.DefaultBidDuration),
|
||||
buyer,
|
||||
@ -193,19 +260,54 @@ func TestAuctionBidding(t *testing.T) {
|
||||
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
|
||||
types.CodeBidTooLarge,
|
||||
"collateral [forward]: invalid bid size (equal)",
|
||||
auctionArgs{Collateral, modName, c("token1", 20), c("token2", 100), c("debt", 50), collateralAddrs, collateralWeights}, // lot, max bid
|
||||
nil,
|
||||
bidArgs{buyer, c("token2", 0)},
|
||||
types.CodeBidTooSmall,
|
||||
types.DistantFuture,
|
||||
nil,
|
||||
c("token2", 0),
|
||||
false,
|
||||
},
|
||||
{
|
||||
"collateral [forward]: invalid bid size (less than min increment)",
|
||||
auctionArgs{Collateral, modName, c("token1", 20), c("token2", 100), c("debt", 50), collateralAddrs, collateralWeights}, // lot, max bid
|
||||
[]bidArgs{{buyer, c("token2", 50)}},
|
||||
bidArgs{buyer, c("token2", 51)},
|
||||
types.CodeBidTooSmall,
|
||||
someTime.Add(types.DefaultBidDuration),
|
||||
buyer,
|
||||
c("token2", 10),
|
||||
c("token2", 50),
|
||||
false,
|
||||
},
|
||||
{
|
||||
"collateral [forward]: less than min increment but equal to maxBid",
|
||||
auctionArgs{Collateral, modName, c("token1", 20), c("token2", 100), c("debt", 50), collateralAddrs, collateralWeights}, // lot, max bid
|
||||
[]bidArgs{{buyer, c("token2", 99)}},
|
||||
bidArgs{buyer, c("token2", 100)}, // min bid at default 5% is 104
|
||||
sdk.CodeType(0),
|
||||
someTime.Add(types.DefaultBidDuration),
|
||||
buyer,
|
||||
c("token2", 100),
|
||||
true,
|
||||
},
|
||||
{
|
||||
"collateral [forward]: invalid bid size (greater than max)",
|
||||
auctionArgs{Collateral, modName, c("token1", 20), c("token2", 100), c("debt", 50), collateralAddrs, collateralWeights}, // lot, max bid
|
||||
nil,
|
||||
bidArgs{buyer, c("token2", 101)},
|
||||
types.CodeBidTooLarge,
|
||||
types.DistantFuture,
|
||||
nil,
|
||||
c("token2", 0),
|
||||
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},
|
||||
auctionArgs{Collateral, modName, c("token1", 20), c("token2", 50), c("debt", 50), collateralAddrs, collateralWeights}, // lot, max bid
|
||||
[]bidArgs{{buyer, c("token2", 50)}}, // put auction into reverse phase
|
||||
bidArgs{buyer, c("token1", 15)},
|
||||
sdk.CodeType(0),
|
||||
someTime.Add(types.DefaultBidDuration),
|
||||
buyer,
|
||||
@ -214,8 +316,9 @@ func TestAuctionBidding(t *testing.T) {
|
||||
},
|
||||
{
|
||||
"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},
|
||||
auctionArgs{Collateral, modName, c("token1", 20), c("token2", 50), c("debt", 50), collateralAddrs, collateralWeights}, // lot, max bid
|
||||
[]bidArgs{{buyer, c("token2", 50)}, {buyer, c("token1", 15)}}, // put auction into reverse phase, and add a reverse phase bid
|
||||
bidArgs{secondBuyer, c("token1", 14)},
|
||||
sdk.CodeType(0),
|
||||
someTime.Add(types.DefaultBidDuration),
|
||||
secondBuyer,
|
||||
@ -224,28 +327,20 @@ func TestAuctionBidding(t *testing.T) {
|
||||
},
|
||||
{
|
||||
"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},
|
||||
auctionArgs{Collateral, modName, c("token1", 20), c("token2", 50), c("debt", 50), collateralAddrs, collateralWeights}, // lot, max bid
|
||||
[]bidArgs{{buyer, c("token2", 50)}}, // put auction into reverse phase
|
||||
bidArgs{buyer, c("badtoken", 15)},
|
||||
types.CodeInvalidLotDenom,
|
||||
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},
|
||||
types.CodeLotTooLarge,
|
||||
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},
|
||||
auctionArgs{Collateral, modName, c("token1", 20), c("token2", 50), c("debt", 50), collateralAddrs, collateralWeights}, // lot, max bid
|
||||
[]bidArgs{{buyer, c("token2", 50)}}, // put auction into reverse phase
|
||||
bidArgs{buyer, c("token1", 21)},
|
||||
types.CodeLotTooLarge,
|
||||
someTime.Add(types.DefaultBidDuration),
|
||||
buyer,
|
||||
@ -253,13 +348,25 @@ func TestAuctionBidding(t *testing.T) {
|
||||
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},
|
||||
types.CodeAuctionHasExpired,
|
||||
"collateral [reverse]: invalid lot size (equal)",
|
||||
auctionArgs{Collateral, modName, c("token1", 20), c("token2", 50), c("debt", 50), collateralAddrs, collateralWeights}, // lot, max bid
|
||||
[]bidArgs{{buyer, c("token2", 50)}}, // put auction into reverse phase
|
||||
bidArgs{buyer, c("token1", 20)},
|
||||
types.CodeLotTooLarge,
|
||||
someTime.Add(types.DefaultBidDuration),
|
||||
buyer,
|
||||
c("token2", 10),
|
||||
c("token2", 50),
|
||||
false,
|
||||
},
|
||||
{
|
||||
"collateral [reverse]: invalid lot size (larger than min increment)",
|
||||
auctionArgs{Collateral, modName, c("token1", 60), c("token2", 50), c("debt", 50), collateralAddrs, collateralWeights}, // lot, max bid
|
||||
[]bidArgs{{buyer, c("token2", 50)}}, // put auction into reverse phase
|
||||
bidArgs{buyer, c("token1", 58)}, // max lot at default 5% is 57
|
||||
types.CodeLotTooLarge,
|
||||
someTime.Add(types.DefaultBidDuration),
|
||||
buyer,
|
||||
c("token2", 50),
|
||||
false,
|
||||
},
|
||||
}
|
||||
@ -283,6 +390,7 @@ func TestAuctionBidding(t *testing.T) {
|
||||
)
|
||||
ctx := tApp.NewContext(false, abci.Header{})
|
||||
keeper := tApp.GetAuctionKeeper()
|
||||
bank := tApp.GetBankKeeper()
|
||||
|
||||
// Start Auction
|
||||
var id uint64
|
||||
@ -292,56 +400,84 @@ func TestAuctionBidding(t *testing.T) {
|
||||
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:
|
||||
case Collateral:
|
||||
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()
|
||||
}
|
||||
// Place setup bids
|
||||
for _, b := range tc.setupBids {
|
||||
require.NoError(t, keeper.PlaceBid(ctx, id, b.bidder, b.amount))
|
||||
}
|
||||
|
||||
// Close the auction early to test late bidding (if applicable)
|
||||
if strings.Contains(tc.name, "closed") {
|
||||
ctx = ctx.WithBlockTime(types.DistantFuture.Add(1))
|
||||
}
|
||||
|
||||
// Store some state for use in checks
|
||||
oldAuction, found := keeper.GetAuction(ctx, id)
|
||||
var oldBidder sdk.AccAddress
|
||||
if found {
|
||||
oldBidder = oldAuction.GetBidder()
|
||||
}
|
||||
oldBidderOldCoins := bank.GetCoins(ctx, oldBidder)
|
||||
newBidderOldCoins := bank.GetCoins(ctx, tc.bidArgs.bidder)
|
||||
|
||||
// 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)
|
||||
// Check auction was found
|
||||
newAuction, 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())
|
||||
require.Equal(t, modName, newAuction.GetInitiator())
|
||||
require.Equal(t, tc.expectedBidder, newAuction.GetBidder())
|
||||
require.Equal(t, tc.expectedBid, newAuction.GetBid())
|
||||
require.Equal(t, tc.expectedEndTime, newAuction.GetEndTime())
|
||||
|
||||
// Check coins have moved between bidder and previous bidder
|
||||
bidAmt := tc.bidArgs.amount
|
||||
switch tc.auctionArgs.auctionType {
|
||||
case Debt:
|
||||
bidAmt = oldAuction.GetBid()
|
||||
case Collateral:
|
||||
collatAuction, _ := oldAuction.(types.CollateralAuction)
|
||||
if collatAuction.IsReversePhase() {
|
||||
bidAmt = oldAuction.GetBid()
|
||||
}
|
||||
}
|
||||
if oldBidder.Equals(tc.bidArgs.bidder) { // same bidder
|
||||
require.Equal(t, newBidderOldCoins.Sub(cs(bidAmt.Sub(oldAuction.GetBid()))), bank.GetCoins(ctx, tc.bidArgs.bidder))
|
||||
} else {
|
||||
require.Equal(t, cs(newBidderOldCoins.Sub(cs(bidAmt))...), bank.GetCoins(ctx, tc.bidArgs.bidder)) // wrapping in cs() to avoid comparing nil and empty coins
|
||||
if oldBidder.Equals(supply.NewModuleAddress(oldAuction.GetInitiator())) { // handle checking debt coins for case debt auction has had no bids placed yet TODO make this less confusing
|
||||
require.Equal(t, cs(oldBidderOldCoins.Add(cs(oldAuction.GetBid()))...).Add(cs(c("debt", oldAuction.GetBid().Amount.Int64()))), bank.GetCoins(ctx, oldBidder))
|
||||
} else {
|
||||
require.Equal(t, cs(oldBidderOldCoins.Add(cs(oldAuction.GetBid()))...), bank.GetCoins(ctx, oldBidder))
|
||||
}
|
||||
}
|
||||
|
||||
} else {
|
||||
// Check expected error code type
|
||||
require.NotNil(t, err, "PlaceBid did not return an error") // catch nil values before they cause a panic below
|
||||
require.Equal(t, tc.expectedError, err.Result().Code)
|
||||
|
||||
// Check auction values
|
||||
newAuction, found := keeper.GetAuction(ctx, id)
|
||||
if found {
|
||||
require.Equal(t, modName, newAuction.GetInitiator())
|
||||
require.Equal(t, tc.expectedBidder, newAuction.GetBidder())
|
||||
require.Equal(t, tc.expectedBid, newAuction.GetBid())
|
||||
require.Equal(t, tc.expectedEndTime, newAuction.GetEndTime())
|
||||
}
|
||||
|
||||
// Check coins have not moved
|
||||
require.Equal(t, newBidderOldCoins, bank.GetCoins(ctx, tc.bidArgs.bidder))
|
||||
require.Equal(t, oldBidderOldCoins, bank.GetCoins(ctx, oldBidder))
|
||||
}
|
||||
})
|
||||
}
|
||||
|
@ -149,10 +149,11 @@ func (am AppModule) ExportGenesis(ctx sdk.Context) json.RawMessage {
|
||||
}
|
||||
|
||||
// BeginBlock module begin-block
|
||||
func (AppModule) BeginBlock(_ sdk.Context, _ abci.RequestBeginBlock) {}
|
||||
func (am AppModule) BeginBlock(ctx sdk.Context, _ abci.RequestBeginBlock) {
|
||||
BeginBlocker(ctx, am.keeper)
|
||||
}
|
||||
|
||||
// EndBlock module end-block
|
||||
func (am AppModule) EndBlock(ctx sdk.Context, _ abci.RequestEndBlock) []abci.ValidatorUpdate {
|
||||
EndBlocker(ctx, am.keeper)
|
||||
func (AppModule) EndBlock(_ sdk.Context, _ abci.RequestEndBlock) []abci.ValidatorUpdate {
|
||||
return []abci.ValidatorUpdate{}
|
||||
}
|
||||
|
@ -9,6 +9,9 @@
|
||||
type Params struct {
|
||||
MaxAuctionDuration time.Duration `json:"max_auction_duration" yaml:"max_auction_duration"` // max length of auction
|
||||
MaxBidDuration time.Duration `json:"max_bid_duration" yaml:"max_bid_duration"` // additional time added to the auction end time after each bid, capped by the expiry.
|
||||
IncrementSurplus sdk.Dec `json:"increment_surplus" yaml:"increment_surplus"` // percentage change (of auc.Bid) required for a new bid on a surplus auction
|
||||
IncrementDebt sdk.Dec `json:"increment_debt" yaml:"increment_debt"` // percentage change (of auc.Lot) required for a new bid on a debt auction
|
||||
IncrementCollateral sdk.Dec `json:"increment_collateral" yaml:"increment_collateral"` // percentage change (of auc.Bid or auc.Lot) required for a new bid on a collateral auction
|
||||
}
|
||||
```
|
||||
|
||||
|
@ -25,7 +25,7 @@ The `x/auction` module emits the following events:
|
||||
| message | module | auction |
|
||||
| message | sender | {sender address} |
|
||||
|
||||
## EndBlock
|
||||
## BeginBlock
|
||||
|
||||
| Type | Attribute Key | Attribute Value |
|
||||
|---------------|---------------|-----------------|
|
||||
|
@ -2,7 +2,10 @@
|
||||
|
||||
The auction module contains the following parameters:
|
||||
|
||||
| Key | Type | Example |
|
||||
| ------------------ | ---------------------- | -----------|
|
||||
| MaxAuctionDuration | string (time.Duration) | "48h0m0s" |
|
||||
| BidDuration | string (time.Duration) | "3h0m0s" |
|
||||
| Key | Type | Example | Description |
|
||||
|---------------------|------------------------|------------------------|---------------------------------------------------------------------------------------|
|
||||
| MaxAuctionDuration | string (time.Duration) | "48h0m0s" | |
|
||||
| BidDuration | string (time.Duration) | "3h0m0s" | |
|
||||
| IncrementSurplus | string (dec) | "0.050000000000000000" | percentage change in bid required for a new bid on a surplus auction |
|
||||
| IncrementDebt | string (dec) | "0.050000000000000000" | percentage change in lot required for a new bid on a debt auction |
|
||||
| IncrementCollateral | string (dec) | "0.050000000000000000" | percentage change in either bid or lot required for a new bid on a collateral auction |
|
||||
|
@ -1,6 +1,6 @@
|
||||
# End Block
|
||||
# Begin Block
|
||||
|
||||
At the end of each block, auctions that have reached `EndTime` are closed. The logic to close auctions is as follows:
|
||||
At the start of each block, auctions that have reached `EndTime` are closed. The logic to close auctions is as follows:
|
||||
|
||||
```go
|
||||
var expiredAuctions []uint64
|
@ -6,7 +6,7 @@
|
||||
3. **[Messages](03_messages.md)**
|
||||
4. **[Events](04_events.md)**
|
||||
5. **[Params](05_params.md)**
|
||||
6. **[EndBlock](06_end_block.md)**
|
||||
6. **[BeginBlock](06_begin_block.md)**
|
||||
|
||||
## Abstract
|
||||
|
||||
|
@ -61,7 +61,7 @@ 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 }
|
||||
|
||||
// GetType returns theauction type. Used to identify auctions in event attributes.
|
||||
// GetType returns the auction type. Used to identify auctions in event attributes.
|
||||
func (a BaseAuction) GetType() string { return "base" }
|
||||
|
||||
// Validate verifies that the auction end time is before max end time
|
||||
|
@ -21,9 +21,10 @@ const (
|
||||
CodeInvalidLotDenom sdk.CodeType = 8
|
||||
CodeBidTooSmall sdk.CodeType = 9
|
||||
CodeBidTooLarge sdk.CodeType = 10
|
||||
CodeLotTooLarge sdk.CodeType = 11
|
||||
CodeCollateralAuctionIsInReversePhase sdk.CodeType = 12
|
||||
CodeCollateralAuctionIsInForwardPhase sdk.CodeType = 13
|
||||
CodeLotTooSmall sdk.CodeType = 11
|
||||
CodeLotTooLarge sdk.CodeType = 12
|
||||
CodeCollateralAuctionIsInReversePhase sdk.CodeType = 13
|
||||
CodeCollateralAuctionIsInForwardPhase sdk.CodeType = 14
|
||||
)
|
||||
|
||||
// ErrInvalidInitialAuctionID error for when the initial auction ID hasn't been set
|
||||
@ -66,9 +67,9 @@ func ErrInvalidLotDenom(codespace sdk.CodespaceType, lotDenom string, auctionLot
|
||||
return sdk.NewError(codespace, CodeInvalidLotDenom, fmt.Sprintf("lot denom %s doesn't match auction lot denom %s", lotDenom, auctionLotDenom))
|
||||
}
|
||||
|
||||
// ErrBidTooSmall error for when bid is not greater than auction's last bid
|
||||
func ErrBidTooSmall(codespace sdk.CodespaceType, bid sdk.Coin, lastBid sdk.Coin) sdk.Error {
|
||||
return sdk.NewError(codespace, CodeBidTooSmall, fmt.Sprintf("bid %s is not greater than auction's last bid %s", bid.String(), lastBid.String()))
|
||||
// ErrBidTooSmall error for when bid is not greater than auction's min bid amount
|
||||
func ErrBidTooSmall(codespace sdk.CodespaceType, bid sdk.Coin, minBid sdk.Coin) sdk.Error {
|
||||
return sdk.NewError(codespace, CodeBidTooSmall, fmt.Sprintf("bid %s is not greater than auction's min new bid amount %s", bid.String(), minBid.String()))
|
||||
}
|
||||
|
||||
// ErrBidTooLarge error for when bid is larger than auction's maximum allowed bid
|
||||
@ -76,9 +77,14 @@ func ErrBidTooLarge(codespace sdk.CodespaceType, bid sdk.Coin, maxBid sdk.Coin)
|
||||
return sdk.NewError(codespace, CodeBidTooLarge, fmt.Sprintf("bid %s is greater than auction's max bid %s", bid.String(), maxBid.String()))
|
||||
}
|
||||
|
||||
// ErrLotTooLarge error for when lot is not smaller than auction's last lot
|
||||
func ErrLotTooLarge(codespace sdk.CodespaceType, lot sdk.Coin, lastLot sdk.Coin) sdk.Error {
|
||||
return sdk.NewError(codespace, CodeLotTooLarge, fmt.Sprintf("lot %s is not less than auction's last lot %s", lot.String(), lastLot.String()))
|
||||
// ErrLotToosmall error for when lot is less than zero
|
||||
func ErrLotTooSmall(codespace sdk.CodespaceType, lot sdk.Coin, minLot sdk.Coin) sdk.Error {
|
||||
return sdk.NewError(codespace, CodeLotTooSmall, fmt.Sprintf("lot %s is not greater than auction's min new lot amount %s", lot.String(), minLot.String()))
|
||||
}
|
||||
|
||||
// ErrLotTooLarge error for when lot is not smaller than auction's max new lot amount
|
||||
func ErrLotTooLarge(codespace sdk.CodespaceType, lot sdk.Coin, maxLot sdk.Coin) sdk.Error {
|
||||
return sdk.NewError(codespace, CodeLotTooLarge, fmt.Sprintf("lot %s is greater than auction's max new lot amount %s", lot.String(), maxLot.String()))
|
||||
}
|
||||
|
||||
// ErrCollateralAuctionIsInReversePhase error for when attempting to place a forward bid on a collateral auction in reverse phase
|
||||
|
@ -17,11 +17,15 @@ const (
|
||||
DefaultBidDuration time.Duration = 1 * time.Hour
|
||||
)
|
||||
|
||||
// Parameter keys
|
||||
var (
|
||||
// DefaultIncrement is the smallest percent change a new bid must have from the old one
|
||||
DefaultIncrement sdk.Dec = sdk.MustNewDecFromStr("0.05")
|
||||
// ParamStoreKeyParams Param store key for auction params
|
||||
KeyAuctionBidDuration = []byte("BidDuration")
|
||||
KeyAuctionDuration = []byte("MaxAuctionDuration")
|
||||
KeyBidDuration = []byte("BidDuration")
|
||||
KeyMaxAuctionDuration = []byte("MaxAuctionDuration")
|
||||
KeyIncrementSurplus = []byte("IncrementSurplus")
|
||||
KeyIncrementDebt = []byte("IncrementDebt")
|
||||
KeyIncrementCollateral = []byte("IncrementCollateral")
|
||||
)
|
||||
|
||||
var _ subspace.ParamSet = &Params{}
|
||||
@ -30,13 +34,19 @@ var _ subspace.ParamSet = &Params{}
|
||||
type Params struct {
|
||||
MaxAuctionDuration time.Duration `json:"max_auction_duration" yaml:"max_auction_duration"` // max length of auction
|
||||
BidDuration time.Duration `json:"bid_duration" yaml:"bid_duration"` // additional time added to the auction end time after each bid, capped by the expiry.
|
||||
IncrementSurplus sdk.Dec `json:"increment_surplus" yaml:"increment_surplus"` // percentage change (of auc.Bid) required for a new bid on a surplus auction
|
||||
IncrementDebt sdk.Dec `json:"increment_debt" yaml:"increment_debt"` // percentage change (of auc.Lot) required for a new bid on a debt auction
|
||||
IncrementCollateral sdk.Dec `json:"increment_collateral" yaml:"increment_collateral"` // percentage change (of auc.Bid or auc.Lot) required for a new bid on a collateral auction
|
||||
}
|
||||
|
||||
// NewParams returns a new Params object.
|
||||
func NewParams(maxAuctionDuration time.Duration, bidDuration time.Duration) Params {
|
||||
func NewParams(maxAuctionDuration, bidDuration time.Duration, incrementSurplus, incrementDebt, incrementCollateral sdk.Dec) Params {
|
||||
return Params{
|
||||
MaxAuctionDuration: maxAuctionDuration,
|
||||
BidDuration: bidDuration,
|
||||
IncrementSurplus: incrementSurplus,
|
||||
IncrementDebt: incrementDebt,
|
||||
IncrementCollateral: incrementCollateral,
|
||||
}
|
||||
}
|
||||
|
||||
@ -45,6 +55,9 @@ func DefaultParams() Params {
|
||||
return NewParams(
|
||||
DefaultMaxAuctionDuration,
|
||||
DefaultBidDuration,
|
||||
DefaultIncrement,
|
||||
DefaultIncrement,
|
||||
DefaultIncrement,
|
||||
)
|
||||
}
|
||||
|
||||
@ -56,8 +69,11 @@ func ParamKeyTable() subspace.KeyTable {
|
||||
// ParamSetPairs implements the ParamSet interface and returns all the key/value pairs.
|
||||
func (p *Params) ParamSetPairs() subspace.ParamSetPairs {
|
||||
return subspace.ParamSetPairs{
|
||||
{Key: KeyAuctionBidDuration, Value: &p.BidDuration},
|
||||
{Key: KeyAuctionDuration, Value: &p.MaxAuctionDuration},
|
||||
{Key: KeyBidDuration, Value: &p.BidDuration},
|
||||
{Key: KeyMaxAuctionDuration, Value: &p.MaxAuctionDuration},
|
||||
{Key: KeyIncrementSurplus, Value: &p.IncrementSurplus},
|
||||
{Key: KeyIncrementDebt, Value: &p.IncrementDebt},
|
||||
{Key: KeyIncrementCollateral, Value: &p.IncrementCollateral},
|
||||
}
|
||||
}
|
||||
|
||||
@ -72,7 +88,11 @@ func (p Params) Equal(p2 Params) bool {
|
||||
func (p Params) String() string {
|
||||
return fmt.Sprintf(`Auction Params:
|
||||
Max Auction Duration: %s
|
||||
Bid Duration: %s`, p.MaxAuctionDuration, p.BidDuration)
|
||||
Bid Duration: %s
|
||||
Increment Surplus: %s
|
||||
Increment Debt: %s
|
||||
Increment Collateral: %s`,
|
||||
p.MaxAuctionDuration, p.BidDuration, p.IncrementSurplus, p.IncrementDebt, p.IncrementCollateral)
|
||||
}
|
||||
|
||||
// Validate checks that the parameters have valid values.
|
||||
@ -86,5 +106,17 @@ func (p Params) Validate() error {
|
||||
if p.BidDuration > p.MaxAuctionDuration {
|
||||
return sdk.ErrInternal("bid duration param cannot be larger than max auction duration")
|
||||
}
|
||||
if p.IncrementSurplus == (sdk.Dec{}) || p.IncrementDebt == (sdk.Dec{}) || p.IncrementCollateral == (sdk.Dec{}) {
|
||||
return sdk.ErrInternal("auction increment values cannot be nil")
|
||||
}
|
||||
if p.IncrementSurplus.IsNegative() {
|
||||
return sdk.ErrInternal("surplus auction increment cannot be less than zero")
|
||||
}
|
||||
if p.IncrementDebt.IsNegative() {
|
||||
return sdk.ErrInternal("debt auction increment cannot be less than zero")
|
||||
}
|
||||
if p.IncrementCollateral.IsNegative() {
|
||||
return sdk.ErrInternal("collateral auction increment cannot be less than zero")
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
@ -1,9 +1,11 @@
|
||||
package types
|
||||
|
||||
import (
|
||||
"github.com/stretchr/testify/require"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
sdk "github.com/cosmos/cosmos-sdk/types"
|
||||
"github.com/stretchr/testify/require"
|
||||
)
|
||||
|
||||
func TestParams_Validate(t *testing.T) {
|
||||
@ -11,23 +13,89 @@ func TestParams_Validate(t *testing.T) {
|
||||
}
|
||||
testCases := []struct {
|
||||
name string
|
||||
MaxAuctionDuration time.Duration
|
||||
BidDuration time.Duration
|
||||
Params
|
||||
expectErr bool
|
||||
}{
|
||||
{"normal", 24 * time.Hour, 1 * time.Hour, false},
|
||||
{"negativeBid", 24 * time.Hour, -1 * time.Hour, true},
|
||||
{"negativeAuction", -24 * time.Hour, 1 * time.Hour, true},
|
||||
{"bid>auction", 1 * time.Hour, 24 * time.Hour, true},
|
||||
{"zeros", 0, 0, false},
|
||||
{
|
||||
"normal",
|
||||
DefaultParams(),
|
||||
false,
|
||||
},
|
||||
{
|
||||
"negativeBid",
|
||||
Params{
|
||||
MaxAuctionDuration: 24 * time.Hour,
|
||||
BidDuration: -1 * time.Hour,
|
||||
IncrementSurplus: d("0.05"),
|
||||
IncrementDebt: d("0.05"),
|
||||
IncrementCollateral: d("0.05"),
|
||||
},
|
||||
true,
|
||||
},
|
||||
{
|
||||
"negativeAuction",
|
||||
Params{
|
||||
MaxAuctionDuration: -24 * time.Hour,
|
||||
BidDuration: 1 * time.Hour,
|
||||
IncrementSurplus: d("0.05"),
|
||||
IncrementDebt: d("0.05"),
|
||||
IncrementCollateral: d("0.05"),
|
||||
},
|
||||
true,
|
||||
},
|
||||
{
|
||||
"bid>auction",
|
||||
Params{
|
||||
MaxAuctionDuration: 1 * time.Hour,
|
||||
BidDuration: 24 * time.Hour,
|
||||
IncrementSurplus: d("0.05"),
|
||||
IncrementDebt: d("0.05"),
|
||||
IncrementCollateral: d("0.05"),
|
||||
},
|
||||
true,
|
||||
},
|
||||
{
|
||||
"negative increment surplus",
|
||||
Params{
|
||||
MaxAuctionDuration: 24 * time.Hour,
|
||||
BidDuration: 1 * time.Hour,
|
||||
IncrementSurplus: d("-0.05"),
|
||||
IncrementDebt: d("0.05"),
|
||||
IncrementCollateral: d("0.05"),
|
||||
},
|
||||
true,
|
||||
},
|
||||
{
|
||||
"negative increment debt",
|
||||
Params{
|
||||
MaxAuctionDuration: 24 * time.Hour,
|
||||
BidDuration: 1 * time.Hour,
|
||||
IncrementSurplus: d("0.05"),
|
||||
IncrementDebt: d("-0.05"),
|
||||
IncrementCollateral: d("0.05"),
|
||||
},
|
||||
true,
|
||||
},
|
||||
{
|
||||
"negative increment collateral",
|
||||
Params{
|
||||
MaxAuctionDuration: 24 * time.Hour,
|
||||
BidDuration: 1 * time.Hour,
|
||||
IncrementSurplus: d("0.05"),
|
||||
IncrementDebt: d("0.05"),
|
||||
IncrementCollateral: d("-0.05"),
|
||||
},
|
||||
true,
|
||||
},
|
||||
{
|
||||
"zero value",
|
||||
Params{},
|
||||
true,
|
||||
},
|
||||
}
|
||||
for _, tc := range testCases {
|
||||
t.Run(tc.name, func(t *testing.T) {
|
||||
p := Params{
|
||||
MaxAuctionDuration: tc.MaxAuctionDuration,
|
||||
BidDuration: tc.BidDuration,
|
||||
}
|
||||
err := p.Validate()
|
||||
err := tc.Params.Validate()
|
||||
if tc.expectErr {
|
||||
require.Error(t, err)
|
||||
} else {
|
||||
@ -36,3 +104,5 @@ func TestParams_Validate(t *testing.T) {
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func d(amount string) sdk.Dec { return sdk.MustNewDecFromStr(amount) }
|
||||
|
Loading…
Reference in New Issue
Block a user