mirror of
				https://github.com/0glabs/0g-chain.git
				synced 2025-11-04 03:17:27 +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() {
 | 
				
			||||||
	if err != nil {
 | 
							return sdk.ErrInternal("auction has closed")
 | 
				
			||||||
		return err
 | 
					 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
	// TODO this will fail if someone tries to update their bid without the full bid amount sitting in their account
 | 
					
 | 
				
			||||||
	// sub outputs
 | 
						var err sdk.Error
 | 
				
			||||||
	for _, output := range coinOutputs {
 | 
						var a types.Auction
 | 
				
			||||||
		_, 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
 | 
						switch auc := auction.(type) {
 | 
				
			||||||
 | 
						case types.ForwardAuction:
 | 
				
			||||||
 | 
							a, err = k.PlaceBidForward(ctx, auc, bidder, bid)
 | 
				
			||||||
		if err != nil {
 | 
							if err != nil {
 | 
				
			||||||
			panic(err)
 | 
								return err
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
	}
 | 
						case types.ReverseAuction:
 | 
				
			||||||
	// add inputs
 | 
							a, err = k.PlaceBidReverse(ctx, auc, bidder, 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
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
 | 
						case types.ForwardReverseAuction:
 | 
				
			||||||
 | 
							a, err = k.PlaceBidForwardReverse(ctx, auc, bidder, bid, lot)
 | 
				
			||||||
 | 
							if err != nil {
 | 
				
			||||||
 | 
								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,14 +292,25 @@ 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) {
 | 
				
			||||||
	if err != nil {
 | 
						case types.ForwardAuction, types.ForwardReverseAuction:
 | 
				
			||||||
		return err
 | 
							err = k.PayoutAuctionLot(ctx, auc)
 | 
				
			||||||
 | 
							if err != nil {
 | 
				
			||||||
 | 
								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)
 | 
				
			||||||
@ -149,7 +318,26 @@ func (k Keeper) CloseAuction(ctx sdk.Context, auctionID types.ID) sdk.Error {
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
	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