mirror of
https://github.com/0glabs/0g-chain.git
synced 2024-11-10 10:05:18 +00:00
add debt tracking to auctions
This commit is contained in:
parent
27f3e76da3
commit
61e5de556c
@ -25,7 +25,7 @@ func TestKeeper_EndBlocker(t *testing.T) {
|
||||
|
||||
tApp := app.NewTestApp()
|
||||
sellerAcc := supply.NewEmptyModuleAccount(sellerModName)
|
||||
require.NoError(t, sellerAcc.SetCoins(cs(c("token1", 100), c("token2", 100))))
|
||||
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),
|
||||
@ -36,7 +36,7 @@ func TestKeeper_EndBlocker(t *testing.T) {
|
||||
ctx := tApp.NewContext(true, abci.Header{})
|
||||
keeper := tApp.GetAuctionKeeper()
|
||||
|
||||
auctionID, err := keeper.StartCollateralAuction(ctx, sellerModName, c("token1", 20), c("token2", 50), returnAddrs, returnWeights)
|
||||
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)))
|
||||
|
||||
|
@ -31,13 +31,14 @@ func (k Keeper) StartSurplusAuction(ctx sdk.Context, seller string, lot sdk.Coin
|
||||
}
|
||||
|
||||
// StartDebtAuction starts a new debt (reverse) auction.
|
||||
func (k Keeper) StartDebtAuction(ctx sdk.Context, buyer string, bid sdk.Coin, initialLot sdk.Coin) (uint64, sdk.Error) {
|
||||
func (k Keeper) StartDebtAuction(ctx sdk.Context, buyer string, bid sdk.Coin, initialLot sdk.Coin, debt sdk.Coin) (uint64, sdk.Error) {
|
||||
|
||||
auction := types.NewDebtAuction(
|
||||
buyer,
|
||||
bid,
|
||||
initialLot,
|
||||
ctx.BlockTime().Add(k.GetParams(ctx).MaxAuctionDuration))
|
||||
ctx.BlockTime().Add(k.GetParams(ctx).MaxAuctionDuration),
|
||||
debt)
|
||||
|
||||
// This auction type mints coins at close. Need to check module account has minting privileges to avoid potential err in endblocker.
|
||||
macc := k.supplyKeeper.GetModuleAccount(ctx, buyer)
|
||||
@ -45,6 +46,11 @@ func (k Keeper) StartDebtAuction(ctx sdk.Context, buyer string, bid sdk.Coin, in
|
||||
return 0, sdk.ErrInternal("module does not have minting permissions")
|
||||
}
|
||||
|
||||
err := k.supplyKeeper.SendCoinsFromModuleToModule(ctx, buyer, types.ModuleName, sdk.NewCoins(debt))
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
|
||||
auctionID, err := k.StoreNewAuction(ctx, auction)
|
||||
if err != nil {
|
||||
return 0, err
|
||||
@ -53,18 +59,28 @@ func (k Keeper) StartDebtAuction(ctx sdk.Context, buyer string, bid sdk.Coin, in
|
||||
}
|
||||
|
||||
// StartCollateralAuction starts a new collateral (2-phase) auction.
|
||||
func (k Keeper) StartCollateralAuction(ctx sdk.Context, seller string, lot sdk.Coin, maxBid sdk.Coin, lotReturnAddrs []sdk.AccAddress, lotReturnWeights []sdk.Int) (uint64, sdk.Error) {
|
||||
func (k Keeper) StartCollateralAuction(ctx sdk.Context, seller string, lot sdk.Coin, maxBid sdk.Coin, lotReturnAddrs []sdk.AccAddress, lotReturnWeights []sdk.Int, debt sdk.Coin) (uint64, sdk.Error) {
|
||||
|
||||
weightedAddresses, err := types.NewWeightedAddresses(lotReturnAddrs, lotReturnWeights)
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
auction := types.NewCollateralAuction(seller, lot, ctx.BlockTime().Add(types.DefaultMaxAuctionDuration), maxBid, weightedAddresses)
|
||||
auction := types.NewCollateralAuction(
|
||||
seller,
|
||||
lot,
|
||||
ctx.BlockTime().Add(types.DefaultMaxAuctionDuration),
|
||||
maxBid,
|
||||
weightedAddresses,
|
||||
debt)
|
||||
|
||||
err = k.supplyKeeper.SendCoinsFromModuleToModule(ctx, seller, types.ModuleName, sdk.NewCoins(lot))
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
err = k.supplyKeeper.SendCoinsFromModuleToModule(ctx, seller, types.ModuleName, sdk.NewCoins(debt))
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
|
||||
auctionID, err := k.StoreNewAuction(ctx, auction)
|
||||
if err != nil {
|
||||
@ -184,10 +200,24 @@ func (k Keeper) PlaceForwardBidCollateral(ctx sdk.Context, a types.CollateralAuc
|
||||
}
|
||||
}
|
||||
// Increase in bid sent to auction initiator
|
||||
err := k.supplyKeeper.SendCoinsFromAccountToModule(ctx, bidder, a.Initiator, sdk.NewCoins(bid.Sub(a.Bid)))
|
||||
bidIncrement := bid.Sub(a.Bid)
|
||||
err := k.supplyKeeper.SendCoinsFromAccountToModule(ctx, bidder, a.Initiator, sdk.NewCoins(bidIncrement))
|
||||
if err != nil {
|
||||
return a, err
|
||||
}
|
||||
// Debt coins are sent to liquidator (until there is no CorrespondingDebt left). Amount sent is equal to bidIncrement.
|
||||
if a.CorrespondingDebt.IsPositive() {
|
||||
|
||||
debtAmountToReturn := sdk.MinInt(bidIncrement.Amount, a.CorrespondingDebt.Amount)
|
||||
debtToReturn := sdk.NewCoin(a.CorrespondingDebt.Denom, debtAmountToReturn)
|
||||
|
||||
err = k.supplyKeeper.SendCoinsFromModuleToModule(ctx, types.ModuleName, a.Initiator, sdk.NewCoins(debtToReturn))
|
||||
if err != nil {
|
||||
return a, err
|
||||
}
|
||||
a.CorrespondingDebt = a.CorrespondingDebt.Sub(debtToReturn) // debtToReturn will always be ≤ a.CorrespondingDebt from the MinInt above
|
||||
// TODO optionally burn out debt and stable just returned to liquidator
|
||||
}
|
||||
|
||||
// Update Auction
|
||||
a.Bidder = bidder
|
||||
@ -271,6 +301,19 @@ func (k Keeper) PlaceBidDebt(ctx sdk.Context, a types.DebtAuction, bidder sdk.Ac
|
||||
return a, err
|
||||
}
|
||||
}
|
||||
// Debt coins are sent to liquidator the first time a bid is placed. Amount sent is equal to Bid.
|
||||
if a.Bidder.Equals(supply.NewModuleAddress(a.Initiator)) {
|
||||
|
||||
debtAmountToReturn := sdk.MinInt(a.Bid.Amount, a.CorrespondingDebt.Amount)
|
||||
debtToReturn := sdk.NewCoin(a.CorrespondingDebt.Denom, debtAmountToReturn)
|
||||
|
||||
err := k.supplyKeeper.SendCoinsFromModuleToModule(ctx, types.ModuleName, a.Initiator, sdk.NewCoins(debtToReturn))
|
||||
if err != nil {
|
||||
return a, err
|
||||
}
|
||||
a.CorrespondingDebt = a.CorrespondingDebt.Sub(debtToReturn) // debtToReturn will always be ≤ a.CorrespondingDebt from the MinInt above
|
||||
// TODO optionally burn out debt and stable just returned to liquidator
|
||||
}
|
||||
|
||||
// Update Auction
|
||||
a.Bidder = bidder
|
||||
@ -324,6 +367,12 @@ func (k Keeper) PayoutDebtAuction(ctx sdk.Context, a types.DebtAuction) sdk.Erro
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if a.CorrespondingDebt.IsPositive() {
|
||||
err = k.supplyKeeper.SendCoinsFromModuleToModule(ctx, types.ModuleName, a.Initiator, sdk.NewCoins(a.CorrespondingDebt))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
@ -342,6 +391,12 @@ func (k Keeper) PayoutCollateralAuction(ctx sdk.Context, a types.CollateralAucti
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if a.CorrespondingDebt.IsPositive() {
|
||||
err = k.supplyKeeper.SendCoinsFromModuleToModule(ctx, types.ModuleName, a.Initiator, sdk.NewCoins(a.CorrespondingDebt))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
|
@ -69,27 +69,29 @@ func TestDebtAuctionBasic(t *testing.T) {
|
||||
|
||||
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),
|
||||
supply.NewEmptyModuleAccount(buyerModName, supply.Minter), // reverse auctions mint payout
|
||||
buyerAcc,
|
||||
}),
|
||||
)
|
||||
ctx := tApp.NewContext(false, abci.Header{})
|
||||
keeper := tApp.GetAuctionKeeper()
|
||||
|
||||
// Start auction
|
||||
auctionID, err := keeper.StartDebtAuction(ctx, buyerModName, c("token1", 20), c("token2", 99999)) // buyer, bid, initialLot
|
||||
auctionID, err := keeper.StartDebtAuction(ctx, buyerModName, c("token1", 20), c("token2", 99999), c("debt", 20))
|
||||
require.NoError(t, err)
|
||||
// Check buyer's coins have not decreased, as lot is minted at the end
|
||||
tApp.CheckBalance(t, ctx, buyerAddr, nil) // zero coins
|
||||
// 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", 80), c("token2", 100)))
|
||||
// Check buyer's coins have increased
|
||||
tApp.CheckBalance(t, ctx, buyerAddr, cs(c("token1", 20)))
|
||||
tApp.CheckBalance(t, ctx, buyerAddr, cs(c("token1", 20), c("debt", 100)))
|
||||
|
||||
// Close auction at just after auction expiry
|
||||
ctx = ctx.WithBlockTime(ctx.BlockTime().Add(types.DefaultBidDuration))
|
||||
@ -109,7 +111,7 @@ func TestCollateralAuctionBasic(t *testing.T) {
|
||||
|
||||
tApp := app.NewTestApp()
|
||||
sellerAcc := supply.NewEmptyModuleAccount(sellerModName)
|
||||
require.NoError(t, sellerAcc.SetCoins(cs(c("token1", 100), c("token2", 100))))
|
||||
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),
|
||||
@ -123,17 +125,17 @@ func TestCollateralAuctionBasic(t *testing.T) {
|
||||
keeper := tApp.GetAuctionKeeper()
|
||||
|
||||
// Start auction
|
||||
auctionID, err := keeper.StartCollateralAuction(ctx, sellerModName, c("token1", 20), c("token2", 50), returnAddrs, returnWeights) // seller, lot, maxBid, otherPerson
|
||||
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)))
|
||||
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)))
|
||||
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)))
|
||||
@ -145,7 +147,7 @@ func TestCollateralAuctionBasic(t *testing.T) {
|
||||
// Check bidder's coins have decreased
|
||||
tApp.CheckBalance(t, ctx, buyer, cs(c("token1", 100), c("token2", 50)))
|
||||
// Check seller's coins have increased
|
||||
tApp.CheckBalance(t, ctx, sellerAddr, cs(c("token1", 80), c("token2", 150)))
|
||||
tApp.CheckBalance(t, ctx, sellerAddr, cs(c("token1", 80), c("token2", 150), c("debt", 100)))
|
||||
// Check return addresses have received coins
|
||||
tApp.CheckBalance(t, ctx, returnAddrs[0], cs(c("token1", 102), c("token2", 100)))
|
||||
tApp.CheckBalance(t, ctx, returnAddrs[1], cs(c("token1", 102), c("token2", 100)))
|
||||
|
@ -74,8 +74,8 @@ func TestIterateAuctions(t *testing.T) {
|
||||
|
||||
auctions := []types.Auction{
|
||||
types.NewSurplusAuction("sellerMod", c("denom", 12345678), "anotherdenom", time.Date(1998, time.January, 1, 0, 0, 0, 0, time.UTC)).WithID(0),
|
||||
types.NewDebtAuction("buyerMod", c("denom", 12345678), c("anotherdenom", 12345678), time.Date(1998, time.January, 1, 0, 0, 0, 0, time.UTC)).WithID(1),
|
||||
types.NewCollateralAuction("sellerMod", c("denom", 12345678), time.Date(1998, time.January, 1, 0, 0, 0, 0, time.UTC), c("anotherdenom", 12345678), types.WeightedAddresses{}).WithID(2),
|
||||
types.NewDebtAuction("buyerMod", c("denom", 12345678), c("anotherdenom", 12345678), time.Date(1998, time.January, 1, 0, 0, 0, 0, time.UTC), c("debt", 12345678)).WithID(1),
|
||||
types.NewCollateralAuction("sellerMod", c("denom", 12345678), time.Date(1998, time.January, 1, 0, 0, 0, 0, time.UTC), c("anotherdenom", 12345678), types.WeightedAddresses{}, c("debt", 12345678)).WithID(2),
|
||||
}
|
||||
for _, a := range auctions {
|
||||
keeper.SetAuction(ctx, a)
|
||||
|
@ -73,25 +73,28 @@ func NewSurplusAuction(seller string, lot sdk.Coin, bidDenom string, endTime tim
|
||||
// It is normally used to acquire pegged asset to cover the CDP system's debts that were not covered by selling collateral.
|
||||
type DebtAuction struct {
|
||||
BaseAuction
|
||||
CorrespondingDebt sdk.Coin
|
||||
}
|
||||
|
||||
// WithID returns an auction with the ID set.
|
||||
func (a DebtAuction) WithID(id uint64) Auction { a.ID = id; return a }
|
||||
|
||||
// NewDebtAuction returns a new debt auction.
|
||||
func NewDebtAuction(buyerModAccName string, bid sdk.Coin, initialLot sdk.Coin, EndTime time.Time) DebtAuction {
|
||||
func NewDebtAuction(buyerModAccName string, bid sdk.Coin, initialLot sdk.Coin, EndTime time.Time, debt sdk.Coin) DebtAuction {
|
||||
// Note: Bidder is set to the initiator's module account address instead of module name. (when the first bid is placed, it is paid out to the initiator)
|
||||
// Setting to the module account address bypasses calling supply.SendCoinsFromModuleToModule, instead calls SendCoinsFromModuleToAccount.
|
||||
// This isn't a problem currently, but if additional logic/validation was added for sending to coins to Module Accounts, it would be bypassed.
|
||||
auction := DebtAuction{BaseAuction{
|
||||
// no ID
|
||||
Initiator: buyerModAccName,
|
||||
Lot: initialLot,
|
||||
Bidder: supply.NewModuleAddress(buyerModAccName), // send proceeds from the first bid to the buyer.
|
||||
Bid: bid, // amount that the buyer is buying - doesn't change over course of auction
|
||||
EndTime: EndTime,
|
||||
MaxEndTime: EndTime,
|
||||
}}
|
||||
auction := DebtAuction{
|
||||
BaseAuction: BaseAuction{
|
||||
// no ID
|
||||
Initiator: buyerModAccName,
|
||||
Lot: initialLot,
|
||||
Bidder: supply.NewModuleAddress(buyerModAccName), // send proceeds from the first bid to the buyer.
|
||||
Bid: bid, // amount that the buyer is buying - doesn't change over course of auction
|
||||
EndTime: EndTime,
|
||||
MaxEndTime: EndTime},
|
||||
CorrespondingDebt: debt,
|
||||
}
|
||||
return auction
|
||||
}
|
||||
|
||||
@ -102,8 +105,9 @@ func NewDebtAuction(buyerModAccName string, bid sdk.Coin, initialLot sdk.Coin, E
|
||||
// Collateral auctions are normally used to sell off collateral seized from CDPs.
|
||||
type CollateralAuction struct {
|
||||
BaseAuction
|
||||
MaxBid sdk.Coin
|
||||
LotReturns WeightedAddresses
|
||||
CorrespondingDebt sdk.Coin
|
||||
MaxBid sdk.Coin
|
||||
LotReturns WeightedAddresses
|
||||
}
|
||||
|
||||
// WithID returns an auction with the ID set.
|
||||
@ -132,7 +136,7 @@ func (a CollateralAuction) String() string {
|
||||
}
|
||||
|
||||
// NewCollateralAuction returns a new collateral auction.
|
||||
func NewCollateralAuction(seller string, lot sdk.Coin, EndTime time.Time, maxBid sdk.Coin, lotReturns WeightedAddresses) CollateralAuction {
|
||||
func NewCollateralAuction(seller string, lot sdk.Coin, EndTime time.Time, maxBid sdk.Coin, lotReturns WeightedAddresses, debt sdk.Coin) CollateralAuction {
|
||||
auction := CollateralAuction{
|
||||
BaseAuction: BaseAuction{
|
||||
// no ID
|
||||
@ -142,8 +146,9 @@ func NewCollateralAuction(seller string, lot sdk.Coin, EndTime time.Time, maxBid
|
||||
Bid: sdk.NewInt64Coin(maxBid.Denom, 0),
|
||||
EndTime: EndTime,
|
||||
MaxEndTime: EndTime},
|
||||
MaxBid: maxBid,
|
||||
LotReturns: lotReturns,
|
||||
CorrespondingDebt: debt,
|
||||
MaxBid: maxBid,
|
||||
LotReturns: lotReturns,
|
||||
}
|
||||
return auction
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user