From de4f55ea20e41d5b39887dca3a8b3929e4e4655f Mon Sep 17 00:00:00 2001 From: Kevin Davis Date: Tue, 7 Jan 2020 12:17:27 -0500 Subject: [PATCH] feat: add spec, update redundant type names --- x/auction/alias.go | 24 +++++++--- x/auction/genesis.go | 4 +- x/auction/keeper/auctions_test.go | 6 ++- x/auction/keeper/params.go | 6 +-- x/auction/spec/01_concepts.md | 13 ++++++ x/auction/spec/02_state.md | 76 +++++++++++++++++++++++++++++++ x/auction/spec/03_messages.md | 37 +++++++++++++++ x/auction/spec/04_events.md | 9 ++++ x/auction/spec/05_params.md | 12 +++++ x/auction/spec/06_begin_block.md | 20 ++++++++ x/auction/spec/README.md | 20 ++++++++ x/auction/types/auctions.go | 1 + x/auction/types/genesis.go | 16 +++---- x/auction/types/params.go | 36 +++++++-------- 14 files changed, 241 insertions(+), 39 deletions(-) create mode 100644 x/auction/spec/01_concepts.md create mode 100644 x/auction/spec/02_state.md create mode 100644 x/auction/spec/03_messages.md create mode 100644 x/auction/spec/04_events.md create mode 100644 x/auction/spec/05_params.md create mode 100644 x/auction/spec/06_begin_block.md create mode 100644 x/auction/spec/README.md diff --git a/x/auction/alias.go b/x/auction/alias.go index a3262826..ec4ad50f 100644 --- a/x/auction/alias.go +++ b/x/auction/alias.go @@ -25,21 +25,29 @@ var ( NewForwardAuction = types.NewForwardAuction NewReverseAuction = types.NewReverseAuction NewForwardReverseAuction = types.NewForwardReverseAuction + NewWeightedAddresses = types.NewWeightedAddresses RegisterCodec = types.RegisterCodec NewGenesisState = types.NewGenesisState DefaultGenesisState = types.DefaultGenesisState ValidateGenesis = types.ValidateGenesis + GetAuctionKey = types.GetAuctionKey + GetAuctionByTimeKey = types.GetAuctionByTimeKey + Uint64FromBytes = types.Uint64FromBytes + Uint64ToBytes = types.Uint64ToBytes NewMsgPlaceBid = types.NewMsgPlaceBid - NewAuctionParams = types.NewAuctionParams - DefaultAuctionParams = types.DefaultAuctionParams + NewParams = types.NewParams + DefaultParams = types.DefaultParams ParamKeyTable = types.ParamKeyTable NewKeeper = keeper.NewKeeper NewQuerier = keeper.NewQuerier // variable aliases - ModuleCdc = types.ModuleCdc - KeyAuctionBidDuration = types.KeyAuctionBidDuration - KeyAuctionDuration = types.KeyAuctionDuration + ModuleCdc = types.ModuleCdc + AuctionKeyPrefix = types.AuctionKeyPrefix + AuctionByTimeKeyPrefix = types.AuctionByTimeKeyPrefix + NextAuctionIDKey = types.NextAuctionIDKey + KeyAuctionBidDuration = types.KeyAuctionBidDuration + KeyAuctionDuration = types.KeyAuctionDuration ) type ( @@ -48,10 +56,12 @@ type ( ForwardAuction = types.ForwardAuction ReverseAuction = types.ReverseAuction ForwardReverseAuction = types.ForwardReverseAuction - GenesisAuctions = types.GenesisAuctions + WeightedAddresses = types.WeightedAddresses + SupplyKeeper = types.SupplyKeeper + Auctions = types.Auctions GenesisState = types.GenesisState MsgPlaceBid = types.MsgPlaceBid - AuctionParams = types.AuctionParams + Params = types.Params QueryResAuctions = types.QueryResAuctions Keeper = keeper.Keeper ) diff --git a/x/auction/genesis.go b/x/auction/genesis.go index 6136df9e..8b4a5f1e 100644 --- a/x/auction/genesis.go +++ b/x/auction/genesis.go @@ -8,7 +8,7 @@ import ( func InitGenesis(ctx sdk.Context, keeper Keeper, data GenesisState) { keeper.SetNextAuctionID(ctx, data.NextAuctionID) - keeper.SetParams(ctx, data.AuctionParams) + keeper.SetParams(ctx, data.Params) for _, a := range data.Auctions { keeper.SetAuction(ctx, a) @@ -24,7 +24,7 @@ func ExportGenesis(ctx sdk.Context, keeper Keeper) GenesisState { params := keeper.GetParams(ctx) - var genAuctions GenesisAuctions + var genAuctions Auctions keeper.IterateAuctions(ctx, func(a Auction) bool { genAuctions = append(genAuctions, a) return false diff --git a/x/auction/keeper/auctions_test.go b/x/auction/keeper/auctions_test.go index c5545476..696c3382 100644 --- a/x/auction/keeper/auctions_test.go +++ b/x/auction/keeper/auctions_test.go @@ -49,11 +49,15 @@ func TestForwardAuctionBasic(t *testing.T) { // Check seller's coins have not increased (because proceeds are burned) tApp.CheckBalance(t, ctx, sellerAddr, cs(c("token1", 80), c("token2", 100))) + // increment bid same bidder + err = keeper.PlaceBid(ctx, auctionID, buyer, c("token2", 20), c("token1", 20)) + require.NoError(t, err) + // Close auction at just at auction expiry time ctx = ctx.WithBlockTime(ctx.BlockTime().Add(types.DefaultBidDuration)) require.NoError(t, keeper.CloseAuction(ctx, auctionID)) // Check buyer's coins increased - tApp.CheckBalance(t, ctx, buyer, cs(c("token1", 120), c("token2", 90))) + tApp.CheckBalance(t, ctx, buyer, cs(c("token1", 120), c("token2", 80))) } func TestReverseAuctionBasic(t *testing.T) { diff --git a/x/auction/keeper/params.go b/x/auction/keeper/params.go index c832caf5..bd2d67de 100644 --- a/x/auction/keeper/params.go +++ b/x/auction/keeper/params.go @@ -6,12 +6,12 @@ import ( ) // SetParams sets the auth module's parameters. -func (k Keeper) SetParams(ctx sdk.Context, params types.AuctionParams) { +func (k Keeper) SetParams(ctx sdk.Context, params types.Params) { k.paramSubspace.SetParamSet(ctx, ¶ms) } // GetParams gets the auth module's parameters. -func (k Keeper) GetParams(ctx sdk.Context) (params types.AuctionParams) { +func (k Keeper) GetParams(ctx sdk.Context) (params types.Params) { k.paramSubspace.GetParamSet(ctx, ¶ms) return -} \ No newline at end of file +} diff --git a/x/auction/spec/01_concepts.md b/x/auction/spec/01_concepts.md new file mode 100644 index 00000000..f656b615 --- /dev/null +++ b/x/auction/spec/01_concepts.md @@ -0,0 +1,13 @@ + + +# Concepts + +Auctions are broken down into three distinct types, which correspond to three specific functionalities within the CDP system. + +* **Forward Auction:** An auction in which a fixed lot of coins (c1) is sold for increasing amounts of other coins (c2). Bidders increment the amount of c2 they are willing to pay for the lot of c1. After the completion of a forward auction, the winning bid of c2 is burned, and the bidder receives the lot of c1. As a concrete example, forward auction are used to sell a fixed amount of USDX stable coins in exchange for increasing bids of KAVA governance tokens. The governance tokens are then burned and the winner receives USDX. +* **Reverse Auction:** An auction in which a fixed amount of coins (c1) is bid for a decreasing lot of other coins (c2). Bidders decrement the lot of c2 they are willing to receive for the fixed amount of c1. As a concrete example, reverse auctions are used to raise a certain amount of USDX stable coins in exchange for decreasing lots of KAVA governance tokens. The USDX tokens are used to recapitalize the cdp system and the winner receives KAVA. +* **Forward Reverse Auction:** An two phase auction is which a fixed lot of coins (c1) is sold for increasing amounts of other coins (c2). Bidders increment the amount of c2 until a specific `maxBid` is reached. Once `maxBid` is reached, a fixed amount of c2 is bid for a decreasing lot of c1. In the second phase, bidders decrement the lot of c1 they are willing to receive for a fixed amount of c2. As a concrete example. forward reverse auctions are used to sell collateral (ATOM, for example) for up to a `maxBid` amount of USDX. The USDX tokens are used to recapitalize the cdp system and the winner receives the specified lot of ATOM. In the event that the winning lot is smaller than the total lot, the excess ATOM is ratably returned to the original owners of the liquidated CDPs that were collateralized with that ATOM. + +Auctions are always initiated by another module, and not directly by users. Auctions start with an expiry, the time at which the auction is guaranteed to end, even if there have been no bidders. After each bid, the auction is extended by a specific amount of time, `BidDuration`. In the case that increasing the auction time by `BidDuration` would cause the auction to go past its expiry, the expiry is chosen as the ending time. diff --git a/x/auction/spec/02_state.md b/x/auction/spec/02_state.md new file mode 100644 index 00000000..a1c39f84 --- /dev/null +++ b/x/auction/spec/02_state.md @@ -0,0 +1,76 @@ + + +# State + +## Parameters and genesis state + +`Paramaters` define the rules according to which auctions are run. There is only one active parameter set at any given time. Updates to the parameter set can be made via on-chain parameter update proposals. + +```go +// Params governance parameters for auction module +type Params struct { + MaxAuctionDuration time.Duration `json:"max_auction_duration" yaml:"max_auction_duration"` // max length of auction + MaxBidDuration time.Duration `json:"max_bid_duration" yaml:"max_bid_duration"` // additional time added to the auction end time after each bid, capped by the expiry. +} +``` + +`GenesisState` defines the state that must be persisted when the blockchain stops/restarts in order for normal function of the auction module to resume. + +```go +// GenesisState - auction state that must be provided at genesis +type GenesisState struct { + NextAuctionID uint64 `json:"next_auction_id" yaml:"next_auction_id"` // auctionID that will be used for the next created auction + Params Params `json:"auction_params" yaml:"auction_params"` // auction params + Auctions Auctions `json:"genesis_auctions" yaml:"genesis_auctions"` // auctions currently in the store +} +``` + +## Base types + +```go +// Auction is an interface to several types of auction. +type Auction interface { + GetID() uint64 + WithID(uint64) Auction + GetBidder() sdk.AccAddress + GetBid() sdk.Coin + GetLot() sdk.Coin + GetEndTime() time.Time +} + +// BaseAuction type shared by all Auctions +type BaseAuction struct { + ID uint64 + Initiator string // Module that 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)) + 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) + EndTime time.Time // Auction closing time. Triggers at the end of the block with time ≥ endTime (bids placed in that block are valid) // TODO ensure everything is consistent with this + MaxEndTime time.Time // Maximum closing time. Auctions can close before this but never after. +} + +// ForwardAuction type for forward auctions +type ForwardAuction struct { + BaseAuction +} + +// ReverseAuction type for reverse auctions +type ReverseAuction struct { + BaseAuction +} + +// WeightedAddresses type for storing an address and its associated weight +type WeightedAddresses struct { + Addresses []sdk.AccAddress + Weights []sdk.Int +} + +// ForwardReverseAuction type for forward reverse auction +type ForwardReverseAuction struct { + BaseAuction + MaxBid sdk.Coin + LotReturns WeightedAddresses // return addresses to pay out reductions in the lot amount to. Lot is bid down during reverse phase. +} +``` diff --git a/x/auction/spec/03_messages.md b/x/auction/spec/03_messages.md new file mode 100644 index 00000000..5c8ece91 --- /dev/null +++ b/x/auction/spec/03_messages.md @@ -0,0 +1,37 @@ + + +# Messages + +## Bidding + +Users can bid on auctions using the `MsgPlaceBid` message type. All auction types can be bid on using the same message type. + +```go +// MsgPlaceBid is the message type used to place a bid on any type of auction. +type MsgPlaceBid struct { + AuctionID uint64 + Bidder sdk.AccAddress + Bid sdk.Coin + Lot sdk.Coin +} +``` + +**State Modifications:** + +* Update bidder if different than previous bidder +* For forward auctions: + * Update Bid Amount + * Return bid coins to previous bidder + * Burn coins equal to the increment in the bid (CurrentBid - PreviousBid) +* For Reverse auctions: + * Update lot amount + * Return bid coins to previous bidder +* For Forward Reverse auctions: + * Return bid coins to previous bidder + * If in forward phase: + * Update bid amount + * If in reverse phase: + * Update lot amount +* Extend auction by `BidDuration`, or `MaxEndTime` diff --git a/x/auction/spec/04_events.md b/x/auction/spec/04_events.md new file mode 100644 index 00000000..3bc15051 --- /dev/null +++ b/x/auction/spec/04_events.md @@ -0,0 +1,9 @@ + + +# Events + + diff --git a/x/auction/spec/05_params.md b/x/auction/spec/05_params.md new file mode 100644 index 00000000..621d26aa --- /dev/null +++ b/x/auction/spec/05_params.md @@ -0,0 +1,12 @@ + + +# Parameters + +The auction module contains the following parameters: + +| Key | Type | Example | +| ------------------ | ---------------------- | -----------| +| MaxAuctionDuration | string (time.Duration) | "48h0m0s" | +| MaxBidDuration | string (time.Duration) | "3h0m0s" | diff --git a/x/auction/spec/06_begin_block.md b/x/auction/spec/06_begin_block.md new file mode 100644 index 00000000..2ecfcd54 --- /dev/null +++ b/x/auction/spec/06_begin_block.md @@ -0,0 +1,20 @@ + + +At the beginning of each block, auctions that have reached `EndTime` are closed. The logic to close auctions is as follows: + +```go +var expiredAuctions []uint64 + k.IterateAuctionsByTime(ctx, ctx.BlockTime(), func(id uint64) bool { + expiredAuctions = append(expiredAuctions, id) + return false + }) + + for _, id := range expiredAuctions { + err := k.CloseAuction(ctx, id) + if err != nil { + panic(err) + } + } +``` diff --git a/x/auction/spec/README.md b/x/auction/spec/README.md new file mode 100644 index 00000000..c68ab587 --- /dev/null +++ b/x/auction/spec/README.md @@ -0,0 +1,20 @@ + + +# `auction` + + +1. **[Concepts](01_concepts.md)** +2. **[State](02_state.md)** +3. **[Messages](03_messages.md)** +4. **[Events](04_events.md)** +5. **[Params](05_params.md)** +6. **[BeginBlock](06_begin_block.md)** + +## Abstract + +`x/auction` is an implementation of a Cosmos SDK Module that handles the creation, bidding, and payout of 3 distinct auction types. All auction types implement the `Auction` interface. Each auction type is used at different points during the normal functioning of the CDP system. diff --git a/x/auction/types/auctions.go b/x/auction/types/auctions.go index 5b9b079f..1aafaa36 100644 --- a/x/auction/types/auctions.go +++ b/x/auction/types/auctions.go @@ -149,6 +149,7 @@ func NewForwardReverseAuction(seller string, lot sdk.Coin, EndTime time.Time, ma return auction } +// WeightedAddresses type for storing an address and its associated weight type WeightedAddresses struct { Addresses []sdk.AccAddress Weights []sdk.Int diff --git a/x/auction/types/genesis.go b/x/auction/types/genesis.go index 34514250..423d12fd 100644 --- a/x/auction/types/genesis.go +++ b/x/auction/types/genesis.go @@ -4,28 +4,28 @@ import ( "bytes" ) -// GenesisAuctions type for an array of auctions -type GenesisAuctions []Auction +// Auctions type for an array of auctions +type Auctions []Auction // GenesisState - auction state that must be provided at genesis type GenesisState struct { NextAuctionID uint64 `json:"next_auction_id" yaml:"next_auction_id"` - AuctionParams AuctionParams `json:"auction_params" yaml:"auction_params"` - Auctions GenesisAuctions `json:"genesis_auctions" yaml:"genesis_auctions"` + Params Params `json:"auction_params" yaml:"auction_params"` + Auctions Auctions `json:"genesis_auctions" yaml:"genesis_auctions"` } // NewGenesisState returns a new genesis state object for auctions module -func NewGenesisState(nextID uint64, ap AuctionParams, ga GenesisAuctions) GenesisState { +func NewGenesisState(nextID uint64, ap Params, ga Auctions) GenesisState { return GenesisState{ NextAuctionID: nextID, - AuctionParams: ap, + Params: ap, Auctions: ga, } } // DefaultGenesisState defines default genesis state for auction module func DefaultGenesisState() GenesisState { - return NewGenesisState(0, DefaultAuctionParams(), GenesisAuctions{}) + return NewGenesisState(0, DefaultParams(), Auctions{}) } // Equal checks whether two GenesisState structs are equivalent @@ -42,7 +42,7 @@ func (data GenesisState) IsEmpty() bool { // ValidateGenesis validates genesis inputs. Returns error if validation of any input fails. func ValidateGenesis(data GenesisState) error { - if err := data.AuctionParams.Validate(); err != nil { + if err := data.Params.Validate(); err != nil { return err } return nil diff --git a/x/auction/types/params.go b/x/auction/types/params.go index 5b7cfdf0..1aa0276d 100644 --- a/x/auction/types/params.go +++ b/x/auction/types/params.go @@ -18,30 +18,30 @@ const ( // Parameter keys var ( - // ParamStoreKeyAuctionParams Param store key for auction params + // ParamStoreKeyParams Param store key for auction params KeyAuctionBidDuration = []byte("MaxBidDuration") KeyAuctionDuration = []byte("MaxAuctionDuration") ) -var _ subspace.ParamSet = &AuctionParams{} +var _ subspace.ParamSet = &Params{} -// AuctionParams governance parameters for auction module -type AuctionParams struct { - MaxAuctionDuration time.Duration `json:"max_auction_duration" yaml:"max_auction_duration"` // max length of auction, in blocks - MaxBidDuration time.Duration `json:"max_bid_duration" yaml:"max_bid_duration"` +// Params governance parameters for auction module +type Params struct { + MaxAuctionDuration time.Duration `json:"max_auction_duration" yaml:"max_auction_duration"` // max length of auction + MaxBidDuration time.Duration `json:"max_bid_duration" yaml:"max_bid_duration"` // additional time added to the auction end time after each bid, capped by the expiry. } -// NewAuctionParams creates a new AuctionParams object -func NewAuctionParams(maxAuctionDuration time.Duration, bidDuration time.Duration) AuctionParams { - return AuctionParams{ +// NewParams creates a new Params object +func NewParams(maxAuctionDuration time.Duration, bidDuration time.Duration) Params { + return Params{ MaxAuctionDuration: maxAuctionDuration, MaxBidDuration: bidDuration, } } -// DefaultAuctionParams default parameters for auctions -func DefaultAuctionParams() AuctionParams { - return NewAuctionParams( +// DefaultParams default parameters for auctions +func DefaultParams() Params { + return NewParams( DefaultMaxAuctionDuration, DefaultBidDuration, ) @@ -49,35 +49,35 @@ func DefaultAuctionParams() AuctionParams { // ParamKeyTable Key declaration for parameters func ParamKeyTable() subspace.KeyTable { - return subspace.NewKeyTable().RegisterParamSet(&AuctionParams{}) + return subspace.NewKeyTable().RegisterParamSet(&Params{}) } // ParamSetPairs implements the ParamSet interface and returns all the key/value pairs // pairs of auth module's parameters. // nolint -func (ap *AuctionParams) ParamSetPairs() subspace.ParamSetPairs { +func (ap *Params) ParamSetPairs() subspace.ParamSetPairs { return subspace.ParamSetPairs{ {KeyAuctionBidDuration, &ap.MaxBidDuration}, {KeyAuctionDuration, &ap.MaxAuctionDuration}, } } -// Equal returns a boolean determining if two AuctionParams types are identical. -func (ap AuctionParams) Equal(ap2 AuctionParams) bool { +// Equal returns a boolean determining if two Params types are identical. +func (ap Params) Equal(ap2 Params) bool { bz1 := ModuleCdc.MustMarshalBinaryLengthPrefixed(&ap) bz2 := ModuleCdc.MustMarshalBinaryLengthPrefixed(&ap2) return bytes.Equal(bz1, bz2) } // String implements stringer interface -func (ap AuctionParams) String() string { +func (ap Params) String() string { return fmt.Sprintf(`Auction Params: Max Auction Duration: %s Max Bid Duration: %s`, ap.MaxAuctionDuration, ap.MaxBidDuration) } // Validate checks that the parameters have valid values. -func (ap AuctionParams) Validate() error { +func (ap Params) Validate() error { // TODO check durations are within acceptable limits, if needed return nil }