mirror of
https://github.com/0glabs/0g-chain.git
synced 2024-12-26 16:25:21 +00:00
rough auction type refactor
This commit is contained in:
parent
f794ba1bf9
commit
d8a428e1d8
@ -24,7 +24,6 @@ const (
|
|||||||
var (
|
var (
|
||||||
// functions aliases
|
// functions aliases
|
||||||
NewIDFromString = types.NewIDFromString
|
NewIDFromString = types.NewIDFromString
|
||||||
NewBaseAuction = types.NewBaseAuction
|
|
||||||
NewForwardAuction = types.NewForwardAuction
|
NewForwardAuction = types.NewForwardAuction
|
||||||
NewReverseAuction = types.NewReverseAuction
|
NewReverseAuction = types.NewReverseAuction
|
||||||
NewForwardReverseAuction = types.NewForwardReverseAuction
|
NewForwardReverseAuction = types.NewForwardReverseAuction
|
||||||
|
@ -7,11 +7,13 @@ import (
|
|||||||
"github.com/cosmos/cosmos-sdk/codec"
|
"github.com/cosmos/cosmos-sdk/codec"
|
||||||
sdk "github.com/cosmos/cosmos-sdk/types"
|
sdk "github.com/cosmos/cosmos-sdk/types"
|
||||||
"github.com/cosmos/cosmos-sdk/x/params/subspace"
|
"github.com/cosmos/cosmos-sdk/x/params/subspace"
|
||||||
|
"github.com/cosmos/cosmos-sdk/x/supply"
|
||||||
|
|
||||||
"github.com/kava-labs/kava/x/auction/types"
|
"github.com/kava-labs/kava/x/auction/types"
|
||||||
)
|
)
|
||||||
|
|
||||||
type Keeper struct {
|
type Keeper struct {
|
||||||
bankKeeper types.BankKeeper
|
supplyKeeper types.SupplyKeeper
|
||||||
storeKey sdk.StoreKey
|
storeKey sdk.StoreKey
|
||||||
cdc *codec.Codec
|
cdc *codec.Codec
|
||||||
paramSubspace subspace.Subspace
|
paramSubspace subspace.Subspace
|
||||||
@ -19,23 +21,27 @@ type Keeper struct {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// NewKeeper returns a new auction keeper.
|
// NewKeeper returns a new auction keeper.
|
||||||
func NewKeeper(cdc *codec.Codec, bankKeeper types.BankKeeper, storeKey sdk.StoreKey, paramstore subspace.Subspace) Keeper {
|
func NewKeeper(cdc *codec.Codec, storeKey sdk.StoreKey, bankKeeper types.BankKeeper, supplyKeeper types.SupplyKeeper, paramstore subspace.Subspace) Keeper {
|
||||||
return Keeper{
|
return Keeper{
|
||||||
bankKeeper: bankKeeper,
|
supplyKeeper: supplyKeeper,
|
||||||
storeKey: storeKey,
|
storeKey: storeKey,
|
||||||
cdc: cdc,
|
cdc: cdc,
|
||||||
paramSubspace: paramstore.WithKeyTable(types.ParamKeyTable()),
|
paramSubspace: paramstore.WithKeyTable(types.ParamKeyTable()),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO these 3 start functions be combined or abstracted away?
|
|
||||||
|
|
||||||
// StartForwardAuction starts a normal auction. Known as flap in maker.
|
// StartForwardAuction starts a normal auction. Known as flap in maker.
|
||||||
func (k Keeper) StartForwardAuction(ctx sdk.Context, seller sdk.AccAddress, lot sdk.Coin, initialBid sdk.Coin) (types.ID, sdk.Error) {
|
func (k Keeper) StartForwardAuction(ctx sdk.Context, seller string, lot sdk.Coin, bidDenom string) (types.ID, sdk.Error) {
|
||||||
// create auction
|
// create auction
|
||||||
auction, initiatorOutput := types.NewForwardAuction(seller, lot, initialBid, types.EndTime(ctx.BlockHeight())+types.DefaultMaxAuctionDuration)
|
auction := types.NewForwardAuction(seller, lot, bidDenom, types.EndTime(ctx.BlockHeight())+types.DefaultMaxAuctionDuration)
|
||||||
// start the auction
|
|
||||||
auctionID, err := k.startAuction(ctx, &auction, initiatorOutput)
|
// take coins from module account
|
||||||
|
err := k.supplyKeeper.SendCoinsFromModuleToModule(ctx, seller, types.ModuleName, sdk.NewCoins(lot))
|
||||||
|
if err != nil {
|
||||||
|
return 0, err
|
||||||
|
}
|
||||||
|
// store the auction
|
||||||
|
auctionID, err := k.storeNewAuction(ctx, auction) // TODO does this need to be a pointer to satisfy the interface
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return 0, err
|
return 0, err
|
||||||
}
|
}
|
||||||
@ -43,11 +49,17 @@ func (k Keeper) StartForwardAuction(ctx sdk.Context, seller sdk.AccAddress, lot
|
|||||||
}
|
}
|
||||||
|
|
||||||
// StartReverseAuction starts an auction where sellers compete by offering decreasing prices. Known as flop in maker.
|
// StartReverseAuction starts an auction where sellers compete by offering decreasing prices. Known as flop in maker.
|
||||||
func (k Keeper) StartReverseAuction(ctx sdk.Context, buyer sdk.AccAddress, bid sdk.Coin, initialLot sdk.Coin) (types.ID, sdk.Error) {
|
func (k Keeper) StartReverseAuction(ctx sdk.Context, buyer string, bid sdk.Coin, initialLot sdk.Coin) (types.ID, sdk.Error) {
|
||||||
// create auction
|
// create auction
|
||||||
auction, initiatorOutput := types.NewReverseAuction(buyer, bid, initialLot, types.EndTime(ctx.BlockHeight())+types.DefaultMaxAuctionDuration)
|
auction := types.NewReverseAuction(buyer, bid, initialLot, types.EndTime(ctx.BlockHeight())+types.DefaultMaxAuctionDuration)
|
||||||
// start the auction
|
|
||||||
auctionID, err := k.startAuction(ctx, &auction, initiatorOutput)
|
// 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)
|
||||||
|
if !macc.HasPermission(supply.Minter) { // TODO ideally don't want to import supply
|
||||||
|
return 0, sdk.ErrInternal("module does not have minting permissions")
|
||||||
|
}
|
||||||
|
// store the auction
|
||||||
|
auctionID, err := k.storeNewAuction(ctx, &auction)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return 0, err
|
return 0, err
|
||||||
}
|
}
|
||||||
@ -55,19 +67,25 @@ func (k Keeper) StartReverseAuction(ctx sdk.Context, buyer sdk.AccAddress, bid s
|
|||||||
}
|
}
|
||||||
|
|
||||||
// StartForwardReverseAuction starts an auction where bidders bid up to a maxBid, then switch to bidding down on price. Known as flip in maker.
|
// StartForwardReverseAuction starts an auction where bidders bid up to a maxBid, then switch to bidding down on price. Known as flip in maker.
|
||||||
func (k Keeper) StartForwardReverseAuction(ctx sdk.Context, seller sdk.AccAddress, lot sdk.Coin, maxBid sdk.Coin, otherPerson sdk.AccAddress) (types.ID, sdk.Error) {
|
func (k Keeper) StartForwardReverseAuction(ctx sdk.Context, seller string, lot sdk.Coin, maxBid sdk.Coin, otherPerson sdk.AccAddress) (types.ID, sdk.Error) {
|
||||||
// create auction
|
// create auction
|
||||||
initialBid := sdk.NewInt64Coin(maxBid.Denom, 0) // set the bidding coin denomination from the specified max bid
|
auction := types.NewForwardReverseAuction(seller, lot, types.EndTime(ctx.BlockHeight())+types.DefaultMaxAuctionDuration, maxBid, otherPerson)
|
||||||
auction, initiatorOutput := types.NewForwardReverseAuction(seller, lot, initialBid, types.EndTime(ctx.BlockHeight())+types.DefaultMaxAuctionDuration, maxBid, otherPerson)
|
|
||||||
// start the auction
|
// take coins from module account
|
||||||
auctionID, err := k.startAuction(ctx, &auction, initiatorOutput)
|
err := k.supplyKeeper.SendCoinsFromModuleToModule(ctx, seller, types.ModuleName, sdk.Coins{lot})
|
||||||
|
if err != nil {
|
||||||
|
return 0, err
|
||||||
|
}
|
||||||
|
// store the auction
|
||||||
|
auctionID, err := k.storeNewAuction(ctx, &auction)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return 0, err
|
return 0, err
|
||||||
}
|
}
|
||||||
return auctionID, nil
|
return auctionID, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (k Keeper) startAuction(ctx sdk.Context, auction types.Auction, initiatorOutput types.BankOutput) (types.ID, sdk.Error) {
|
// set an auction in the store, adding a new ID, and setting indexes
|
||||||
|
func (k Keeper) storeNewAuction(ctx sdk.Context, auction types.Auction) (types.ID, sdk.Error) {
|
||||||
// get ID
|
// get ID
|
||||||
newAuctionID, err := k.getNextAuctionID(ctx)
|
newAuctionID, err := k.getNextAuctionID(ctx)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@ -76,18 +94,14 @@ func (k Keeper) startAuction(ctx sdk.Context, auction types.Auction, initiatorOu
|
|||||||
// set ID
|
// set ID
|
||||||
auction.SetID(newAuctionID)
|
auction.SetID(newAuctionID)
|
||||||
|
|
||||||
// subtract coins from initiator
|
|
||||||
_, err = k.bankKeeper.SubtractCoins(ctx, initiatorOutput.Address, sdk.NewCoins(initiatorOutput.Coin))
|
|
||||||
if err != nil {
|
|
||||||
return 0, err
|
|
||||||
}
|
|
||||||
|
|
||||||
// store auction
|
// store auction
|
||||||
k.SetAuction(ctx, auction)
|
k.SetAuction(ctx, auction)
|
||||||
k.incrementNextAuctionID(ctx)
|
k.incrementNextAuctionID(ctx)
|
||||||
return newAuctionID, nil
|
return newAuctionID, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// ==============================================================================================================================
|
||||||
|
|
||||||
// PlaceBid places a bid on any auction.
|
// PlaceBid places a bid on any auction.
|
||||||
func (k Keeper) PlaceBid(ctx sdk.Context, auctionID types.ID, bidder sdk.AccAddress, bid sdk.Coin, lot sdk.Coin) sdk.Error {
|
func (k Keeper) PlaceBid(ctx sdk.Context, auctionID types.ID, bidder sdk.AccAddress, bid sdk.Coin, lot sdk.Coin) sdk.Error {
|
||||||
|
|
||||||
@ -97,35 +111,179 @@ func (k Keeper) PlaceBid(ctx sdk.Context, auctionID types.ID, bidder sdk.AccAddr
|
|||||||
return sdk.ErrInternal("auction doesn't exist")
|
return sdk.ErrInternal("auction doesn't exist")
|
||||||
}
|
}
|
||||||
|
|
||||||
// place bid
|
// check end time
|
||||||
coinOutputs, coinInputs, err := auction.PlaceBid(types.EndTime(ctx.BlockHeight()), bidder, lot, bid) // update auction according to what type of auction it is // TODO should this return updated Auction to be more immutable?
|
if ctx.BlockHeight() > auction.GetEndTime() {
|
||||||
|
return sdk.ErrInternal("auction has closed")
|
||||||
|
}
|
||||||
|
|
||||||
|
var err sdk.Error
|
||||||
|
var a types.Auction
|
||||||
|
switch auc := auction.(type) {
|
||||||
|
case types.ForwardAuction:
|
||||||
|
a, err = k.PlaceBidForward(ctx, auc, bidder, bid)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
// TODO this will fail if someone tries to update their bid without the full bid amount sitting in their account
|
case types.ReverseAuction:
|
||||||
// sub outputs
|
a, err = k.PlaceBidReverse(ctx, auc, bidder, lot)
|
||||||
for _, output := range coinOutputs {
|
|
||||||
_, err = k.bankKeeper.SubtractCoins(ctx, output.Address, sdk.NewCoins(output.Coin)) // TODO handle errors properly here. All coin transfers should be atomic. InputOutputCoins may work
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
panic(err)
|
return err
|
||||||
}
|
}
|
||||||
}
|
case types.ForwardReverseAuction:
|
||||||
// add inputs
|
a, err = k.PlaceBidForwardReverse(ctx, auc, bidder, bid, lot)
|
||||||
for _, input := range coinInputs {
|
|
||||||
_, err = k.bankKeeper.AddCoins(ctx, input.Address, sdk.NewCoins(input.Coin)) // TODO errors
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
panic(err)
|
return err
|
||||||
}
|
}
|
||||||
|
default:
|
||||||
|
panic("unrecognized auction type")
|
||||||
}
|
}
|
||||||
|
|
||||||
// store updated auction
|
// store updated auction
|
||||||
k.SetAuction(ctx, auction)
|
k.SetAuction(ctx, a) // maybe move into above funcs
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// CloseAuction closes an auction and distributes funds to the seller and highest bidder.
|
func (k Keeper) PlaceBidForward(ctx sdk.Context, a types.ForwardAuction, bidder sdk.AccAddress, bid sdk.Coin) (types.ForwardAuction, sdk.Error) {
|
||||||
// TODO because this is called by the end blocker, it has to be valid for the duration of the EndTime block. Should maybe move this to a begin blocker?
|
// Valid New Bid
|
||||||
|
if bid.Denom != a.Bid.Denom {
|
||||||
|
return a, sdk.ErrInternal("bid denom doesn't match auction")
|
||||||
|
}
|
||||||
|
if !a.Bid.IsLT(bid) { // TODO add minimum bid size
|
||||||
|
return a, sdk.ErrInternal("bid not greater than last bid")
|
||||||
|
}
|
||||||
|
|
||||||
|
// Move Coins
|
||||||
|
increment := bid.Sub(a.Bid)
|
||||||
|
bidAmtToReturn := a.Bid
|
||||||
|
if bidder.Equals(a.Bidder) { // catch edge case of someone updating their bid with a low balance
|
||||||
|
bidAmtToReturn = sdk.NewInt64Coin(a.Bid.Denom, 0)
|
||||||
|
}
|
||||||
|
err := k.supplyKeeper.SendCoinsFromAccountToModule(ctx, bidder, types.ModuleName, sdk.NewCoins(bidAmtToReturn.Add(increment)))
|
||||||
|
if err != nil {
|
||||||
|
return a, err
|
||||||
|
}
|
||||||
|
err = k.supplyKeeper.SendCoinsFromModuleToAccount(ctx, types.ModuleName, bidder, sdk.NewCoins(bidAmtToReturn))
|
||||||
|
if err != nil {
|
||||||
|
return a, err
|
||||||
|
}
|
||||||
|
err = k.supplyKeeper.SendCoinsFromModuleToModule(ctx, types.ModuleName, a.Initiator, sdk.NewCoins(increment)) // increase in bid size is burned
|
||||||
|
if err != nil {
|
||||||
|
return a, err
|
||||||
|
}
|
||||||
|
err = k.supplyKeeper.BurnCoins(ctx, a.Initiator, sdk.NewCoins(increment))
|
||||||
|
if err != nil {
|
||||||
|
return a, err
|
||||||
|
}
|
||||||
|
|
||||||
|
// Update Auction
|
||||||
|
a.Bidder = bidder
|
||||||
|
a.Bid = bid
|
||||||
|
// increment timeout
|
||||||
|
a.EndTime = EndTime(min(int64(ctx.BlockHeight()+types.DefaultMaxBidDuration), int64(a.MaxEndTime)))
|
||||||
|
|
||||||
|
return a, nil
|
||||||
|
}
|
||||||
|
func (k Keeper) PlaceBidForwardReverse(ctx sdk.Context, a types.ForwardReverseAuction, bidder sdk.AccAddress, bid sdk.Coin, lot sdk.Coin) (types.ForwardReverseAuction, sdk.Error) {
|
||||||
|
// Validate New Bid // TODO min bid increments, make validation code less confusing
|
||||||
|
if !a.Bid.IsEqual(a.MaxBid) {
|
||||||
|
// Auction is in forward phase, a bid here can put the auction into forward or reverse phases
|
||||||
|
if !a.Bid.IsLT(bid) {
|
||||||
|
return a, sdk.ErrInternal("auction in forward phase, new bid not higher than last bid")
|
||||||
|
}
|
||||||
|
if a.MaxBid.IsLT(bid) {
|
||||||
|
return a, sdk.ErrInternal("bid higher than max bid")
|
||||||
|
}
|
||||||
|
if lot.IsNegative() || a.Lot.IsLT(lot) {
|
||||||
|
return a, sdk.ErrInternal("lot out of bounds")
|
||||||
|
}
|
||||||
|
if lot.IsLT(a.Lot) && !bid.IsEqual(a.MaxBid) {
|
||||||
|
return a, sdk.ErrInternal("auction cannot enter reverse phase without bidding max bid")
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// Auction is in reverse phase, it can never leave reverse phase
|
||||||
|
if !bid.IsEqual(a.MaxBid) {
|
||||||
|
return a, sdk.ErrInternal("") // not necessary
|
||||||
|
}
|
||||||
|
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")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Move Coins
|
||||||
|
bidIncrement := bid.Sub(a.Bid)
|
||||||
|
bidAmtToReturn := a.Bid
|
||||||
|
lotDecrement := a.Lot.Sub(lot)
|
||||||
|
if bidder.Equals(a.Bidder) { // catch edge case of someone updating their bid with a low balance
|
||||||
|
bidAmtToReturn = sdk.NewInt64Coin(a.Bid.Denom, 0)
|
||||||
|
}
|
||||||
|
err := k.supplyKeeper.SendCoinsFromAccountToModule(ctx, bidder, types.ModuleName, sdk.NewCoins(bidAmtToReturn.Add(bidIncrement)))
|
||||||
|
if err != nil {
|
||||||
|
return a, err
|
||||||
|
}
|
||||||
|
err = k.supplyKeeper.SendCoinsFromModuleToAccount(ctx, types.ModuleName, bidder, sdk.NewCoins(bidAmtToReturn))
|
||||||
|
if err != nil {
|
||||||
|
return a, err
|
||||||
|
}
|
||||||
|
err = k.supplyKeeper.SendCoinsFromModuleToModule(ctx, types.ModuleName, a.Initiator, sdk.NewCoins(bidIncrement))
|
||||||
|
if err != nil {
|
||||||
|
return a, err
|
||||||
|
}
|
||||||
|
err = k.supplyKeeper.SendCoinsFromModuleToAccount(ctx, types.ModuleName, a.OtherPerson, sdk.NewCoins(lotDecrement))
|
||||||
|
if err != nil {
|
||||||
|
return a, err
|
||||||
|
}
|
||||||
|
|
||||||
|
// Update Auction
|
||||||
|
a.Bidder = bidder
|
||||||
|
a.Lot = lot
|
||||||
|
a.Bid = bid
|
||||||
|
// increment timeout
|
||||||
|
a.EndTime = EndTime(min(int64(currentBlockHeight+DefaultMaxBidDuration), int64(a.MaxEndTime)))
|
||||||
|
|
||||||
|
return types.ForwardReverseAuction{}, nil
|
||||||
|
}
|
||||||
|
func (k Keeper) PlaceBidReverse(ctx sdk.Context, a types.ReverseAuction, bidder sdk.AccAddress, lot sdk.Coin) (types.ReverseAuction, sdk.Error) {
|
||||||
|
// Validate New Bid
|
||||||
|
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) { // TODO add min bid decrements
|
||||||
|
return a, sdk.ErrInternal("lot not smaller than last lot")
|
||||||
|
}
|
||||||
|
|
||||||
|
// Move Coins
|
||||||
|
bidAmtToReturn := a.Bid
|
||||||
|
if bidder.Equals(a.Bidder) { // catch edge case of someone updating their bid with a low balance
|
||||||
|
bidAmtToReturn = sdk.NewInt64Coin(a.Bid.Denom, 0)
|
||||||
|
}
|
||||||
|
err := k.supplyKeeper.SendCoinsFromAccountToModule(ctx, bidder, types.ModuleName, sdk.NewCoins(bidAmtToReturn))
|
||||||
|
if err != nil {
|
||||||
|
return a, err
|
||||||
|
}
|
||||||
|
err = k.supplyKeeper.SendCoinsFromModuleToAccount(ctx, types.ModuleName, bidder, sdk.NewCoins(bidAmtToReturn))
|
||||||
|
if err != nil {
|
||||||
|
return a, err
|
||||||
|
}
|
||||||
|
|
||||||
|
// Update Auction
|
||||||
|
a.Bidder = bidder
|
||||||
|
a.Lot = lot
|
||||||
|
// increment timeout
|
||||||
|
a.EndTime = EndTime(min(int64(ctx.BlockHeight()+types.DefaultMaxBidDuration), int64(a.MaxEndTime)))
|
||||||
|
|
||||||
|
return a, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// ==========================================================================================================
|
||||||
|
|
||||||
|
// CloseAuction closes an auction and distributes funds to the highest bidder.
|
||||||
func (k Keeper) CloseAuction(ctx sdk.Context, auctionID types.ID) sdk.Error {
|
func (k Keeper) CloseAuction(ctx sdk.Context, auctionID types.ID) sdk.Error {
|
||||||
|
|
||||||
// get the auction from the store
|
// get the auction from the store
|
||||||
@ -134,22 +292,52 @@ func (k Keeper) CloseAuction(ctx sdk.Context, auctionID types.ID) sdk.Error {
|
|||||||
return sdk.ErrInternal("auction doesn't exist")
|
return sdk.ErrInternal("auction doesn't exist")
|
||||||
}
|
}
|
||||||
// error if auction has not reached the end time
|
// error if auction has not reached the end time
|
||||||
if ctx.BlockHeight() < int64(auction.GetEndTime()) { // auctions close at the end of the block with blockheight == EndTime
|
if ctx.BlockHeight() < int64(auction.GetEndTime()) {
|
||||||
return sdk.ErrInternal(fmt.Sprintf("auction can't be closed as curent block height (%v) is under auction end time (%v)", ctx.BlockHeight(), auction.GetEndTime()))
|
return sdk.ErrInternal(fmt.Sprintf("auction can't be closed as curent block height (%v) is under auction end time (%v)", ctx.BlockHeight(), auction.GetEndTime()))
|
||||||
}
|
}
|
||||||
|
|
||||||
// payout to the last bidder
|
// payout to the last bidder
|
||||||
coinInput := auction.GetPayout()
|
var err sdk.Error
|
||||||
_, err := k.bankKeeper.AddCoins(ctx, coinInput.Address, sdk.NewCoins(coinInput.Coin))
|
switch auc := auction.(type) {
|
||||||
|
case types.ForwardAuction, types.ForwardReverseAuction:
|
||||||
|
err = k.PayoutAuctionLot(ctx, auc)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
case types.ReverseAuction:
|
||||||
|
err = k.MintAndPayoutAuctionLot(ctx, auc)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
default:
|
||||||
|
panic("unrecognized auction type")
|
||||||
|
}
|
||||||
|
|
||||||
// Delete auction from store (and queue)
|
// Delete auction from store (and queue)
|
||||||
k.DeleteAuction(ctx, auctionID)
|
k.DeleteAuction(ctx, auctionID)
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
func (k Keeper) MintAndPayoutAuctionLot(ctx sdk.Context, a types.ReverseAuction) sdk.Error {
|
||||||
|
err := k.supplyKeeper.MintCoins(ctx, a.Initiator, sdk.NewCoins(a.Lot))
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
err = k.supplyKeeper.SendCoinsFromModuleToAccount(ctx, a.Initiator, a.Bidder, sdk.NewCoins(a.Lot))
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
func (k Keeper) PayoutAuctionLot(ctx sdk.Context, a types.Auction) sdk.Error {
|
||||||
|
err := k.supplyKeeper.SendCoinsFromModuleToAccount(ctx, types.ModuleName, a.GetBid(), sdk.NewCoins(a.GetLot()))
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// =====================================================================================================================
|
||||||
// ---------- Store methods ----------
|
// ---------- Store methods ----------
|
||||||
// Use these to add and remove auction from the store.
|
// Use these to add and remove auction from the store.
|
||||||
|
|
||||||
@ -162,7 +350,7 @@ func (k Keeper) getNextAuctionID(ctx sdk.Context) (types.ID, sdk.Error) { // TOD
|
|||||||
// if not found, set the id at 0
|
// if not found, set the id at 0
|
||||||
bz = k.cdc.MustMarshalBinaryLengthPrefixed(types.ID(0))
|
bz = k.cdc.MustMarshalBinaryLengthPrefixed(types.ID(0))
|
||||||
store.Set(k.getNextAuctionIDKey(), bz)
|
store.Set(k.getNextAuctionIDKey(), bz)
|
||||||
// TODO Why does the gov module set the id in genesis? :
|
// TODO Set auction ID in genesis
|
||||||
//return 0, ErrInvalidGenesis(keeper.codespace, "InitialProposalID never set")
|
//return 0, ErrInvalidGenesis(keeper.codespace, "InitialProposalID never set")
|
||||||
}
|
}
|
||||||
var auctionID types.ID
|
var auctionID types.ID
|
||||||
@ -177,7 +365,7 @@ func (k Keeper) incrementNextAuctionID(ctx sdk.Context) sdk.Error {
|
|||||||
bz := store.Get(k.getNextAuctionIDKey())
|
bz := store.Get(k.getNextAuctionIDKey())
|
||||||
if bz == nil {
|
if bz == nil {
|
||||||
panic("initial auctionID never set in genesis")
|
panic("initial auctionID never set in genesis")
|
||||||
//return 0, ErrInvalidGenesis(keeper.codespace, "InitialProposalID never set") // TODO is this needed? Why not just set it zero here?
|
//return 0, ErrInvalidGenesis(keeper.codespace, "InitialProposalID never set") // TODO
|
||||||
}
|
}
|
||||||
var auctionID types.ID
|
var auctionID types.ID
|
||||||
k.cdc.MustUnmarshalBinaryLengthPrefixed(bz, &auctionID)
|
k.cdc.MustUnmarshalBinaryLengthPrefixed(bz, &auctionID)
|
||||||
@ -214,7 +402,7 @@ func (k Keeper) GetAuction(ctx sdk.Context, auctionID types.ID) (types.Auction,
|
|||||||
store := ctx.KVStore(k.storeKey)
|
store := ctx.KVStore(k.storeKey)
|
||||||
bz := store.Get(k.getAuctionKey(auctionID))
|
bz := store.Get(k.getAuctionKey(auctionID))
|
||||||
if bz == nil {
|
if bz == nil {
|
||||||
return auction, false // TODO what is the correct behavior when an auction is not found? gov module follows this pattern of returning a bool
|
return auction, false
|
||||||
}
|
}
|
||||||
|
|
||||||
k.cdc.MustUnmarshalBinaryLengthPrefixed(bz, &auction)
|
k.cdc.MustUnmarshalBinaryLengthPrefixed(bz, &auction)
|
||||||
|
@ -5,26 +5,26 @@ import (
|
|||||||
"strconv"
|
"strconv"
|
||||||
|
|
||||||
sdk "github.com/cosmos/cosmos-sdk/types"
|
sdk "github.com/cosmos/cosmos-sdk/types"
|
||||||
|
"github.com/cosmos/cosmos-sdk/x/supply"
|
||||||
)
|
)
|
||||||
|
|
||||||
// Auction is an interface to several types of auction.
|
// Auction is an interface to several types of auction.
|
||||||
type Auction interface {
|
type Auction interface {
|
||||||
GetID() ID
|
GetID() ID
|
||||||
SetID(ID)
|
SetID(ID)
|
||||||
PlaceBid(currentBlockHeight EndTime, bidder sdk.AccAddress, lot sdk.Coin, bid sdk.Coin) ([]BankOutput, []BankInput, sdk.Error)
|
// PlaceBid(currentBlockHeight EndTime, bidder sdk.AccAddress, lot sdk.Coin, bid sdk.Coin) ([]BankOutput, []BankInput, sdk.Error)
|
||||||
GetEndTime() EndTime // auctions close at the end of the block with blockheight EndTime (ie bids placed in that block are valid)
|
GetEndTime() EndTime // auctions close at the end of the block with blockheight EndTime (ie bids placed in that block are valid)
|
||||||
GetPayout() BankInput
|
// GetPayout() BankInput
|
||||||
String() string
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// BaseAuction type shared by all Auctions
|
// BaseAuction type shared by all Auctions
|
||||||
type BaseAuction struct {
|
type BaseAuction struct {
|
||||||
ID ID
|
ID ID
|
||||||
Initiator sdk.AccAddress // Person who starts the auction. Giving away Lot (aka seller in a forward auction)
|
Initiator string // Module who starts the auction. Giving away Lot (aka seller in a forward auction). Restricted to being a module account name rather than any account.
|
||||||
Lot sdk.Coin // Amount of coins up being given by initiator (FA - amount for sale by seller, RA - cost of good by buyer (bid))
|
Lot sdk.Coin // Amount of coins up being given by initiator (FA - amount for sale by seller, RA - cost of good by buyer (bid))
|
||||||
Bidder sdk.AccAddress // Person who bids in the auction. Receiver of Lot. (aka buyer in forward auction, seller in RA)
|
Bidder sdk.AccAddress // Person who bids in the auction. Receiver of Lot. (aka buyer in forward auction, seller in RA)
|
||||||
Bid sdk.Coin // Amount of coins being given by the bidder (FA - bid, RA - amount being sold)
|
Bid sdk.Coin // Amount of coins being given by the bidder (FA - bid, RA - amount being sold)
|
||||||
EndTime EndTime // Block height at which the auction closes. It closes at the end of this block
|
EndTime EndTime // Block height at which the auction closes. It closes at the end of this block // TODO change to time type
|
||||||
MaxEndTime EndTime // Maximum closing time. Auctions can close before this but never after.
|
MaxEndTime EndTime // Maximum closing time. Auctions can close before this but never after.
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -56,48 +56,30 @@ type BankOutput struct {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// GetID getter for auction ID
|
// GetID getter for auction ID
|
||||||
func (a BaseAuction) GetID() ID { return a.ID }
|
func (a *BaseAuction) GetID() ID { return a.ID }
|
||||||
|
|
||||||
// SetID setter for auction ID
|
// SetID setter for auction ID
|
||||||
func (a *BaseAuction) SetID(id ID) { a.ID = id }
|
func (a *BaseAuction) SetID(id ID) { a.ID = id }
|
||||||
|
|
||||||
|
// GetBid getter for auction bid
|
||||||
|
func (a *BaseAuction) GetBid() sdk.Coin { return a.Bid }
|
||||||
|
|
||||||
|
// GetLot getter for auction lot
|
||||||
|
func (a *BaseAuction) GetLot() sdk.Coin { return a.Lot }
|
||||||
|
|
||||||
// GetEndTime getter for auction end time
|
// GetEndTime getter for auction end time
|
||||||
func (a BaseAuction) GetEndTime() EndTime { return a.EndTime }
|
func (a *BaseAuction) GetEndTime() EndTime { return a.EndTime }
|
||||||
|
|
||||||
// GetPayout implements Auction
|
// GetPayout implements Auction
|
||||||
func (a BaseAuction) GetPayout() BankInput {
|
// func (a BaseAuction) GetPayout() BankInput {
|
||||||
return BankInput{a.Bidder, a.Lot}
|
// return BankInput{a.Bidder, a.Lot}
|
||||||
}
|
// }
|
||||||
|
|
||||||
// PlaceBid implements Auction
|
|
||||||
func (a *BaseAuction) PlaceBid(currentBlockHeight EndTime, bidder sdk.AccAddress, lot sdk.Coin, bid sdk.Coin) ([]BankOutput, []BankInput, sdk.Error) {
|
|
||||||
// TODO check lot size matches lot?
|
|
||||||
// check auction has not closed
|
|
||||||
if currentBlockHeight > a.EndTime {
|
|
||||||
return []BankOutput{}, []BankInput{}, sdk.ErrInternal("auction has closed")
|
|
||||||
}
|
|
||||||
// check bid is greater than last bid
|
|
||||||
if !a.Bid.IsLT(bid) { // TODO add minimum bid size
|
|
||||||
return []BankOutput{}, []BankInput{}, sdk.ErrInternal("bid not greater than last bid")
|
|
||||||
}
|
|
||||||
// calculate coin movements
|
|
||||||
outputs := []BankOutput{{bidder, bid}} // new bidder pays bid now
|
|
||||||
inputs := []BankInput{{a.Bidder, a.Bid}, {a.Initiator, bid.Sub(a.Bid)}} // old bidder is paid back, extra goes to seller
|
|
||||||
|
|
||||||
// update auction
|
|
||||||
a.Bidder = bidder
|
|
||||||
a.Bid = bid
|
|
||||||
// increment timeout // TODO into keeper?
|
|
||||||
a.EndTime = EndTime(min(int64(currentBlockHeight+DefaultMaxBidDuration), int64(a.MaxEndTime))) // TODO is there a better way to structure these types?
|
|
||||||
|
|
||||||
return outputs, inputs, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (e EndTime) String() string {
|
func (e EndTime) String() string {
|
||||||
return string(e)
|
return string(e)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (a BaseAuction) String() string {
|
func (a *BaseAuction) String() string {
|
||||||
return fmt.Sprintf(`Auction %d:
|
return fmt.Sprintf(`Auction %d:
|
||||||
Initiator: %s
|
Initiator: %s
|
||||||
Lot: %s
|
Lot: %s
|
||||||
@ -111,118 +93,108 @@ func (a BaseAuction) String() string {
|
|||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
// NewBaseAuction creates a new base auction
|
|
||||||
func NewBaseAuction(seller sdk.AccAddress, lot sdk.Coin, initialBid sdk.Coin, EndTime EndTime) BaseAuction {
|
|
||||||
auction := BaseAuction{
|
|
||||||
// no ID
|
|
||||||
Initiator: seller,
|
|
||||||
Lot: lot,
|
|
||||||
Bidder: seller, // send the proceeds from the first bid back to the seller
|
|
||||||
Bid: initialBid, // set this to zero most of the time
|
|
||||||
EndTime: EndTime,
|
|
||||||
MaxEndTime: EndTime,
|
|
||||||
}
|
|
||||||
return auction
|
|
||||||
}
|
|
||||||
|
|
||||||
// ForwardAuction type for forward auctions
|
// ForwardAuction type for forward auctions
|
||||||
type ForwardAuction struct {
|
type ForwardAuction struct {
|
||||||
BaseAuction
|
*BaseAuction
|
||||||
}
|
}
|
||||||
|
|
||||||
// NewForwardAuction creates a new forward auction
|
// NewForwardAuction creates a new forward auction
|
||||||
func NewForwardAuction(seller sdk.AccAddress, lot sdk.Coin, initialBid sdk.Coin, EndTime EndTime) (ForwardAuction, BankOutput) {
|
func NewForwardAuction(seller string, lot sdk.Coin, bidDenom string, EndTime EndTime) ForwardAuction {
|
||||||
auction := ForwardAuction{BaseAuction{
|
auction := ForwardAuction{&BaseAuction{
|
||||||
// no ID
|
// no ID
|
||||||
Initiator: seller,
|
Initiator: seller,
|
||||||
Lot: lot,
|
Lot: lot,
|
||||||
Bidder: seller, // send the proceeds from the first bid back to the seller
|
Bidder: nil, // TODO on the first place bid, 0 coins will be sent to this address, check if this causes problems or can be avoided
|
||||||
Bid: initialBid, // set this to zero most of the time
|
Bid: sdk.NewInt64Coin(bidDenom, 0),
|
||||||
EndTime: EndTime,
|
EndTime: EndTime,
|
||||||
MaxEndTime: EndTime,
|
MaxEndTime: EndTime,
|
||||||
}}
|
}}
|
||||||
output := BankOutput{seller, lot}
|
// output := BankOutput{seller, lot}
|
||||||
return auction, output
|
return auction
|
||||||
}
|
}
|
||||||
|
|
||||||
// PlaceBid implements Auction
|
// PlaceBid implements Auction
|
||||||
func (a *ForwardAuction) PlaceBid(currentBlockHeight EndTime, bidder sdk.AccAddress, lot sdk.Coin, bid sdk.Coin) ([]BankOutput, []BankInput, sdk.Error) {
|
// func (a *ForwardAuction) PlaceBid(currentBlockHeight EndTime, bidder sdk.AccAddress, lot sdk.Coin, bid sdk.Coin) ([]BankOutput, []BankInput, sdk.Error) {
|
||||||
// TODO check lot size matches lot?
|
// // TODO check lot size matches lot?
|
||||||
// check auction has not closed
|
// // check auction has not closed
|
||||||
if currentBlockHeight > a.EndTime {
|
// if currentBlockHeight > a.EndTime {
|
||||||
return []BankOutput{}, []BankInput{}, sdk.ErrInternal("auction has closed")
|
// return []BankOutput{}, []BankInput{}, sdk.ErrInternal("auction has closed")
|
||||||
}
|
// }
|
||||||
// check bid is greater than last bid
|
// // check bid is greater than last bid
|
||||||
if !a.Bid.IsLT(bid) { // TODO add minimum bid size
|
// if !a.Bid.IsLT(bid) { // TODO add minimum bid size
|
||||||
return []BankOutput{}, []BankInput{}, sdk.ErrInternal("bid not greater than last bid")
|
// return []BankOutput{}, []BankInput{}, sdk.ErrInternal("bid not greater than last bid")
|
||||||
}
|
// }
|
||||||
// calculate coin movements
|
// // calculate coin movements
|
||||||
outputs := []BankOutput{{bidder, bid}} // new bidder pays bid now
|
// outputs := []BankOutput{{bidder, bid}} // new bidder pays bid now
|
||||||
inputs := []BankInput{{a.Bidder, a.Bid}, {a.Initiator, bid.Sub(a.Bid)}} // old bidder is paid back, extra goes to seller
|
// inputs := []BankInput{{a.Bidder, a.Bid}, {a.Initiator, bid.Sub(a.Bid)}} // old bidder is paid back, extra goes to seller
|
||||||
|
|
||||||
// update auction
|
// // update auction
|
||||||
a.Bidder = bidder
|
// a.Bidder = bidder
|
||||||
a.Bid = bid
|
// a.Bid = bid
|
||||||
// increment timeout // TODO into keeper?
|
// // increment timeout // TODO into keeper?
|
||||||
a.EndTime = EndTime(min(int64(currentBlockHeight+DefaultMaxBidDuration), int64(a.MaxEndTime))) // TODO is there a better way to structure these types?
|
// a.EndTime = EndTime(min(int64(currentBlockHeight+DefaultMaxBidDuration), int64(a.MaxEndTime))) // TODO is there a better way to structure these types?
|
||||||
|
|
||||||
return outputs, inputs, nil
|
// return outputs, inputs, nil
|
||||||
}
|
// }
|
||||||
|
|
||||||
// ReverseAuction type for reverse auctions
|
// ReverseAuction type for reverse auctions
|
||||||
// TODO when exporting state and initializing a new genesis, we'll need a way to differentiate forward from reverse auctions
|
// TODO when exporting state and initializing a new genesis, we'll need a way to differentiate forward from reverse auctions
|
||||||
type ReverseAuction struct {
|
type ReverseAuction struct {
|
||||||
BaseAuction
|
*BaseAuction
|
||||||
}
|
}
|
||||||
|
|
||||||
// NewReverseAuction creates a new reverse auction
|
// NewReverseAuction creates a new reverse auction
|
||||||
func NewReverseAuction(buyer sdk.AccAddress, bid sdk.Coin, initialLot sdk.Coin, EndTime EndTime) (ReverseAuction, BankOutput) {
|
func NewReverseAuction(buyerModAccName string, bid sdk.Coin, initialLot sdk.Coin, EndTime EndTime) ReverseAuction {
|
||||||
auction := ReverseAuction{BaseAuction{
|
// Bidder set here receives the proceeds from the first bid placed. This is set to the address of the module account.
|
||||||
|
// When this happens it uses supply.SendCoinsFromModuleToAccount, rather than SendCoinsFromModuleToModule.
|
||||||
|
// Currently not a problem but if extra checks are added to module accounts this will skip them.
|
||||||
|
// TODO description
|
||||||
|
auction := ReverseAuction{&BaseAuction{
|
||||||
// no ID
|
// no ID
|
||||||
Initiator: buyer,
|
Initiator: buyerModAccName,
|
||||||
Lot: initialLot,
|
Lot: initialLot,
|
||||||
Bidder: buyer, // send proceeds from the first bid to the buyer
|
Bidder: supply.NewModuleAddress(buyerModAccName), // send proceeds from the first bid to the buyer.
|
||||||
Bid: bid, // amount that the buyer it buying - doesn't change over course of auction
|
Bid: bid, // amount that the buyer it buying - doesn't change over course of auction
|
||||||
EndTime: EndTime,
|
EndTime: EndTime,
|
||||||
MaxEndTime: EndTime,
|
MaxEndTime: EndTime,
|
||||||
}}
|
}}
|
||||||
output := BankOutput{buyer, initialLot}
|
//output := BankOutput{buyer, initialLot}
|
||||||
return auction, output
|
return auction
|
||||||
}
|
}
|
||||||
|
|
||||||
// PlaceBid implements Auction
|
// PlaceBid implements Auction
|
||||||
func (a *ReverseAuction) PlaceBid(currentBlockHeight EndTime, bidder sdk.AccAddress, lot sdk.Coin, bid sdk.Coin) ([]BankOutput, []BankInput, sdk.Error) {
|
// func (a *ReverseAuction) PlaceBid(currentBlockHeight EndTime, bidder sdk.AccAddress, lot sdk.Coin, bid sdk.Coin) ([]BankOutput, []BankInput, sdk.Error) {
|
||||||
|
|
||||||
// check bid size matches bid?
|
// // check bid size matches bid?
|
||||||
// check auction has not closed
|
// // check auction has not closed
|
||||||
if currentBlockHeight > a.EndTime {
|
// if currentBlockHeight > a.EndTime {
|
||||||
return []BankOutput{}, []BankInput{}, sdk.ErrInternal("auction has closed")
|
// return []BankOutput{}, []BankInput{}, sdk.ErrInternal("auction has closed")
|
||||||
}
|
// }
|
||||||
// check bid is less than last bid
|
// // check bid is less than last bid
|
||||||
if !lot.IsLT(a.Lot) { // TODO add min bid decrements
|
// if !lot.IsLT(a.Lot) { // TODO add min bid decrements
|
||||||
return []BankOutput{}, []BankInput{}, sdk.ErrInternal("lot not smaller than last lot")
|
// return []BankOutput{}, []BankInput{}, sdk.ErrInternal("lot not smaller than last lot")
|
||||||
}
|
// }
|
||||||
// calculate coin movements
|
// // calculate coin movements
|
||||||
outputs := []BankOutput{{bidder, a.Bid}} // new bidder pays bid now
|
// outputs := []BankOutput{{bidder, a.Bid}} // new bidder pays bid now
|
||||||
inputs := []BankInput{{a.Bidder, a.Bid}, {a.Initiator, a.Lot.Sub(lot)}} // old bidder is paid back, decrease in price for goes to buyer
|
// inputs := []BankInput{{a.Bidder, a.Bid}, {a.Initiator, a.Lot.Sub(lot)}} // old bidder is paid back, decrease in price for goes to buyer
|
||||||
|
|
||||||
// update auction
|
// // update auction
|
||||||
a.Bidder = bidder
|
// a.Bidder = bidder
|
||||||
a.Lot = lot
|
// a.Lot = lot
|
||||||
// increment timeout // TODO into keeper?
|
// // increment timeout // TODO into keeper?
|
||||||
a.EndTime = EndTime(min(int64(currentBlockHeight+DefaultMaxBidDuration), int64(a.MaxEndTime))) // TODO is there a better way to structure these types?
|
// a.EndTime = EndTime(min(int64(currentBlockHeight+DefaultMaxBidDuration), int64(a.MaxEndTime))) // TODO is there a better way to structure these types?
|
||||||
|
|
||||||
return outputs, inputs, nil
|
// return outputs, inputs, nil
|
||||||
}
|
// }
|
||||||
|
|
||||||
// ForwardReverseAuction type for forward reverse auction
|
// ForwardReverseAuction type for forward reverse auction
|
||||||
type ForwardReverseAuction struct {
|
type ForwardReverseAuction struct {
|
||||||
BaseAuction
|
*BaseAuction
|
||||||
MaxBid sdk.Coin
|
MaxBid sdk.Coin
|
||||||
OtherPerson sdk.AccAddress // TODO rename, this is normally the original CDP owner
|
OtherPerson sdk.AccAddress // TODO rename, this is normally the original CDP owner, will have to be updated to account for deposits
|
||||||
}
|
}
|
||||||
|
|
||||||
func (a ForwardReverseAuction) String() string {
|
func (a *ForwardReverseAuction) String() string {
|
||||||
return fmt.Sprintf(`Auction %d:
|
return fmt.Sprintf(`Auction %d:
|
||||||
Initiator: %s
|
Initiator: %s
|
||||||
Lot: %s
|
Lot: %s
|
||||||
@ -239,69 +211,69 @@ func (a ForwardReverseAuction) String() string {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// NewForwardReverseAuction creates a new forward reverse auction
|
// NewForwardReverseAuction creates a new forward reverse auction
|
||||||
func NewForwardReverseAuction(seller sdk.AccAddress, lot sdk.Coin, initialBid sdk.Coin, EndTime EndTime, maxBid sdk.Coin, otherPerson sdk.AccAddress) (ForwardReverseAuction, BankOutput) {
|
func NewForwardReverseAuction(seller string, lot sdk.Coin, EndTime EndTime, maxBid sdk.Coin, otherPerson sdk.AccAddress) ForwardReverseAuction {
|
||||||
auction := ForwardReverseAuction{
|
auction := ForwardReverseAuction{
|
||||||
BaseAuction: BaseAuction{
|
BaseAuction: &BaseAuction{
|
||||||
// no ID
|
// no ID
|
||||||
Initiator: seller,
|
Initiator: seller,
|
||||||
Lot: lot,
|
Lot: lot,
|
||||||
Bidder: seller, // send the proceeds from the first bid back to the seller
|
Bidder: nil, // TODO on the first place bid, 0 coins will be sent to this address, check if this causes problems or can be avoided
|
||||||
Bid: initialBid, // 0 most of the time
|
Bid: sdk.NewInt64Coin(maxBid.Denom, 0),
|
||||||
EndTime: EndTime,
|
EndTime: EndTime,
|
||||||
MaxEndTime: EndTime},
|
MaxEndTime: EndTime},
|
||||||
MaxBid: maxBid,
|
MaxBid: maxBid,
|
||||||
OtherPerson: otherPerson,
|
OtherPerson: otherPerson,
|
||||||
}
|
}
|
||||||
output := BankOutput{seller, lot}
|
//output := BankOutput{seller, lot}
|
||||||
return auction, output
|
return auction
|
||||||
}
|
}
|
||||||
|
|
||||||
// PlaceBid implements auction
|
// PlaceBid implements auction
|
||||||
func (a *ForwardReverseAuction) PlaceBid(currentBlockHeight EndTime, bidder sdk.AccAddress, lot sdk.Coin, bid sdk.Coin) (outputs []BankOutput, inputs []BankInput, err sdk.Error) {
|
// func (a *ForwardReverseAuction) PlaceBid(currentBlockHeight EndTime, bidder sdk.AccAddress, lot sdk.Coin, bid sdk.Coin) (outputs []BankOutput, inputs []BankInput, err sdk.Error) {
|
||||||
// check auction has not closed
|
// // check auction has not closed
|
||||||
if currentBlockHeight > a.EndTime {
|
// if currentBlockHeight > a.EndTime {
|
||||||
return []BankOutput{}, []BankInput{}, sdk.ErrInternal("auction has closed")
|
// return []BankOutput{}, []BankInput{}, sdk.ErrInternal("auction has closed")
|
||||||
}
|
// }
|
||||||
|
|
||||||
// determine phase of auction
|
// // determine phase of auction
|
||||||
switch {
|
// switch {
|
||||||
case a.Bid.IsLT(a.MaxBid) && bid.IsLT(a.MaxBid):
|
// case a.Bid.IsLT(a.MaxBid) && bid.IsLT(a.MaxBid):
|
||||||
// Forward auction phase
|
// // Forward auction phase
|
||||||
if !a.Bid.IsLT(bid) { // TODO add min bid increments
|
// if !a.Bid.IsLT(bid) { // TODO add min bid increments
|
||||||
return []BankOutput{}, []BankInput{}, sdk.ErrInternal("bid not greater than last bid")
|
// return []BankOutput{}, []BankInput{}, sdk.ErrInternal("bid not greater than last bid")
|
||||||
}
|
// }
|
||||||
outputs = []BankOutput{{bidder, bid}} // new bidder pays bid now
|
// outputs = []BankOutput{{bidder, bid}} // new bidder pays bid now
|
||||||
inputs = []BankInput{{a.Bidder, a.Bid}, {a.Initiator, bid.Sub(a.Bid)}} // old bidder is paid back, extra goes to seller
|
// inputs = []BankInput{{a.Bidder, a.Bid}, {a.Initiator, bid.Sub(a.Bid)}} // old bidder is paid back, extra goes to seller
|
||||||
case a.Bid.IsLT(a.MaxBid):
|
// case a.Bid.IsLT(a.MaxBid):
|
||||||
// Switch over phase
|
// // Switch over phase
|
||||||
if !bid.IsEqual(a.MaxBid) { // require bid == a.MaxBid
|
// if !bid.IsEqual(a.MaxBid) { // require bid == a.MaxBid
|
||||||
return []BankOutput{}, []BankInput{}, sdk.ErrInternal("bid greater than the max bid")
|
// return []BankOutput{}, []BankInput{}, sdk.ErrInternal("bid greater than the max bid")
|
||||||
}
|
// }
|
||||||
outputs = []BankOutput{{bidder, bid}} // new bidder pays bid now
|
// outputs = []BankOutput{{bidder, bid}} // new bidder pays bid now
|
||||||
inputs = []BankInput{
|
// inputs = []BankInput{
|
||||||
{a.Bidder, a.Bid}, // old bidder is paid back
|
// {a.Bidder, a.Bid}, // old bidder is paid back
|
||||||
{a.Initiator, bid.Sub(a.Bid)}, // extra goes to seller
|
// {a.Initiator, bid.Sub(a.Bid)}, // extra goes to seller
|
||||||
{a.OtherPerson, a.Lot.Sub(lot)}, //decrease in price for goes to original CDP owner
|
// {a.OtherPerson, a.Lot.Sub(lot)}, //decrease in price for goes to original CDP owner
|
||||||
}
|
// }
|
||||||
|
|
||||||
case a.Bid.IsEqual(a.MaxBid):
|
// case a.Bid.IsEqual(a.MaxBid):
|
||||||
// Reverse auction phase
|
// // Reverse auction phase
|
||||||
if !lot.IsLT(a.Lot) { // TODO add min bid decrements
|
// if !lot.IsLT(a.Lot) { // TODO add min bid decrements
|
||||||
return []BankOutput{}, []BankInput{}, sdk.ErrInternal("lot not smaller than last lot")
|
// return []BankOutput{}, []BankInput{}, sdk.ErrInternal("lot not smaller than last lot")
|
||||||
}
|
// }
|
||||||
outputs = []BankOutput{{bidder, a.Bid}} // new bidder pays bid now
|
// outputs = []BankOutput{{bidder, a.Bid}} // new bidder pays bid now
|
||||||
inputs = []BankInput{{a.Bidder, a.Bid}, {a.OtherPerson, a.Lot.Sub(lot)}} // old bidder is paid back, decrease in price for goes to original CDP owner
|
// inputs = []BankInput{{a.Bidder, a.Bid}, {a.OtherPerson, a.Lot.Sub(lot)}} // old bidder is paid back, decrease in price for goes to original CDP owner
|
||||||
default:
|
// default:
|
||||||
panic("should never be reached") // TODO
|
// panic("should never be reached") // TODO
|
||||||
}
|
// }
|
||||||
|
|
||||||
// update auction
|
// // update auction
|
||||||
a.Bidder = bidder
|
// a.Bidder = bidder
|
||||||
a.Lot = lot
|
// a.Lot = lot
|
||||||
a.Bid = bid
|
// a.Bid = bid
|
||||||
// increment timeout
|
// // increment timeout
|
||||||
// TODO use bid duration param
|
// // TODO use bid duration param
|
||||||
a.EndTime = EndTime(min(int64(currentBlockHeight+DefaultMaxBidDuration), int64(a.MaxEndTime))) // TODO is there a better way to structure these types?
|
// a.EndTime = EndTime(min(int64(currentBlockHeight+DefaultMaxBidDuration), int64(a.MaxEndTime))) // TODO is there a better way to structure these types?
|
||||||
|
|
||||||
return outputs, inputs, nil
|
// return outputs, inputs, nil
|
||||||
}
|
// }
|
||||||
|
@ -2,9 +2,20 @@ package types
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
sdk "github.com/cosmos/cosmos-sdk/types"
|
sdk "github.com/cosmos/cosmos-sdk/types"
|
||||||
|
supplyexported "github.com/cosmos/cosmos-sdk/x/supply/exported"
|
||||||
)
|
)
|
||||||
|
|
||||||
type BankKeeper interface {
|
// SupplyKeeper defines the expected supply Keeper
|
||||||
SubtractCoins(sdk.Context, sdk.AccAddress, sdk.Coins) (sdk.Coins, sdk.Error)
|
type SupplyKeeper interface {
|
||||||
AddCoins(sdk.Context, sdk.AccAddress, sdk.Coins) (sdk.Coins, sdk.Error)
|
//GetSupply(ctx sdk.Context) supplyexported.SupplyI
|
||||||
|
|
||||||
|
//GetModuleAddress(name string) sdk.AccAddress
|
||||||
|
GetModuleAccount(ctx sdk.Context, moduleName string) supplyexported.ModuleAccountI
|
||||||
|
|
||||||
|
SendCoinsFromModuleToModule(ctx sdk.Context, sender, recipient string, amt sdk.Coins) sdk.Error
|
||||||
|
SendCoinsFromModuleToAccount(ctx sdk.Context, senderModule string, recipientAddr sdk.AccAddress, amt sdk.Coins) sdk.Error
|
||||||
|
SendCoinsFromAccountToModule(ctx sdk.Context, senderAddr sdk.AccAddress, recipientModule string, amt sdk.Coins) sdk.Error
|
||||||
|
|
||||||
|
BurnCoins(ctx sdk.Context, name string, amt sdk.Coins) sdk.Error
|
||||||
|
MintCoins(ctx sdk.Context, name string, amt sdk.Coins) sdk.Error
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user