mirror of
https://github.com/0glabs/0g-chain.git
synced 2024-12-26 00:05:18 +00:00
add v0.13 legacy types for kava-5 (#784)
This commit is contained in:
parent
6118876074
commit
dd3470260d
555
x/auction/legacy/v0_13/types.go
Normal file
555
x/auction/legacy/v0_13/types.go
Normal file
@ -0,0 +1,555 @@
|
|||||||
|
package v0_13
|
||||||
|
|
||||||
|
import (
|
||||||
|
"errors"
|
||||||
|
"fmt"
|
||||||
|
"strings"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
sdk "github.com/cosmos/cosmos-sdk/types"
|
||||||
|
"github.com/cosmos/cosmos-sdk/x/supply"
|
||||||
|
)
|
||||||
|
|
||||||
|
// Defaults for auction params
|
||||||
|
const (
|
||||||
|
// DefaultMaxAuctionDuration max length of auction
|
||||||
|
DefaultMaxAuctionDuration time.Duration = 2 * 24 * time.Hour
|
||||||
|
// DefaultBidDuration how long an auction gets extended when someone bids
|
||||||
|
DefaultBidDuration time.Duration = 1 * time.Hour
|
||||||
|
CollateralAuctionType = "collateral"
|
||||||
|
SurplusAuctionType = "surplus"
|
||||||
|
DebtAuctionType = "debt"
|
||||||
|
ForwardAuctionPhase = "forward"
|
||||||
|
ReverseAuctionPhase = "reverse"
|
||||||
|
DefaultNextAuctionID uint64 = 1
|
||||||
|
)
|
||||||
|
|
||||||
|
// module variables
|
||||||
|
var (
|
||||||
|
// DefaultIncrement is the smallest percent change a new bid must have from the old one
|
||||||
|
DefaultIncrement sdk.Dec = sdk.MustNewDecFromStr("0.05")
|
||||||
|
KeyBidDuration = []byte("BidDuration")
|
||||||
|
KeyMaxAuctionDuration = []byte("MaxAuctionDuration")
|
||||||
|
KeyIncrementSurplus = []byte("IncrementSurplus")
|
||||||
|
KeyIncrementDebt = []byte("IncrementDebt")
|
||||||
|
KeyIncrementCollateral = []byte("IncrementCollateral")
|
||||||
|
emptyDec = sdk.Dec{}
|
||||||
|
)
|
||||||
|
|
||||||
|
// GenesisAuction interface for auctions at genesis
|
||||||
|
type GenesisAuction interface {
|
||||||
|
Auction
|
||||||
|
GetModuleAccountCoins() sdk.Coins
|
||||||
|
Validate() error
|
||||||
|
}
|
||||||
|
|
||||||
|
// GenesisAuctions is a slice of genesis auctions.
|
||||||
|
type GenesisAuctions []GenesisAuction
|
||||||
|
|
||||||
|
// GenesisState is auction state that must be provided at chain genesis.
|
||||||
|
type GenesisState struct {
|
||||||
|
NextAuctionID uint64 `json:"next_auction_id" yaml:"next_auction_id"`
|
||||||
|
Params Params `json:"params" yaml:"params"`
|
||||||
|
Auctions GenesisAuctions `json:"auctions" yaml:"auctions"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewGenesisState returns a new genesis state object for auctions module.
|
||||||
|
func NewGenesisState(nextID uint64, ap Params, ga GenesisAuctions) GenesisState {
|
||||||
|
return GenesisState{
|
||||||
|
NextAuctionID: nextID,
|
||||||
|
Params: ap,
|
||||||
|
Auctions: ga,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// DefaultGenesisState returns the default genesis state for auction module.
|
||||||
|
func DefaultGenesisState() GenesisState {
|
||||||
|
return NewGenesisState(
|
||||||
|
DefaultNextAuctionID,
|
||||||
|
DefaultParams(),
|
||||||
|
GenesisAuctions{},
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Validate validates genesis inputs. It returns error if validation of any input fails.
|
||||||
|
func (gs GenesisState) Validate() error {
|
||||||
|
if err := gs.Params.Validate(); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
ids := map[uint64]bool{}
|
||||||
|
for _, a := range gs.Auctions {
|
||||||
|
|
||||||
|
if err := a.Validate(); err != nil {
|
||||||
|
return fmt.Errorf("found invalid auction: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
if ids[a.GetID()] {
|
||||||
|
return fmt.Errorf("found duplicate auction ID (%d)", a.GetID())
|
||||||
|
}
|
||||||
|
ids[a.GetID()] = true
|
||||||
|
|
||||||
|
if a.GetID() >= gs.NextAuctionID {
|
||||||
|
return fmt.Errorf("found auction ID ≥ the nextAuctionID (%d ≥ %d)", a.GetID(), gs.NextAuctionID)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// Params is the governance parameters for the auction module.
|
||||||
|
type Params struct {
|
||||||
|
MaxAuctionDuration time.Duration `json:"max_auction_duration" yaml:"max_auction_duration"` // max length of auction
|
||||||
|
BidDuration time.Duration `json:"bid_duration" yaml:"bid_duration"` // additional time added to the auction end time after each bid, capped by the expiry.
|
||||||
|
IncrementSurplus sdk.Dec `json:"increment_surplus" yaml:"increment_surplus"` // percentage change (of auc.Bid) required for a new bid on a surplus auction
|
||||||
|
IncrementDebt sdk.Dec `json:"increment_debt" yaml:"increment_debt"` // percentage change (of auc.Lot) required for a new bid on a debt auction
|
||||||
|
IncrementCollateral sdk.Dec `json:"increment_collateral" yaml:"increment_collateral"` // percentage change (of auc.Bid or auc.Lot) required for a new bid on a collateral auction
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewParams returns a new Params object.
|
||||||
|
func NewParams(maxAuctionDuration, bidDuration time.Duration, incrementSurplus, incrementDebt, incrementCollateral sdk.Dec) Params {
|
||||||
|
return Params{
|
||||||
|
MaxAuctionDuration: maxAuctionDuration,
|
||||||
|
BidDuration: bidDuration,
|
||||||
|
IncrementSurplus: incrementSurplus,
|
||||||
|
IncrementDebt: incrementDebt,
|
||||||
|
IncrementCollateral: incrementCollateral,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// DefaultParams returns the default parameters for auctions.
|
||||||
|
func DefaultParams() Params {
|
||||||
|
return NewParams(
|
||||||
|
DefaultMaxAuctionDuration,
|
||||||
|
DefaultBidDuration,
|
||||||
|
DefaultIncrement,
|
||||||
|
DefaultIncrement,
|
||||||
|
DefaultIncrement,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Validate checks that the parameters have valid values.
|
||||||
|
func (p Params) Validate() error {
|
||||||
|
if err := validateBidDurationParam(p.BidDuration); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := validateMaxAuctionDurationParam(p.MaxAuctionDuration); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
if p.BidDuration > p.MaxAuctionDuration {
|
||||||
|
return errors.New("bid duration param cannot be larger than max auction duration")
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := validateIncrementSurplusParam(p.IncrementSurplus); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := validateIncrementDebtParam(p.IncrementDebt); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
return validateIncrementCollateralParam(p.IncrementCollateral)
|
||||||
|
}
|
||||||
|
|
||||||
|
func validateBidDurationParam(i interface{}) error {
|
||||||
|
bidDuration, ok := i.(time.Duration)
|
||||||
|
if !ok {
|
||||||
|
return fmt.Errorf("invalid parameter type: %T", i)
|
||||||
|
}
|
||||||
|
|
||||||
|
if bidDuration < 0 {
|
||||||
|
return fmt.Errorf("bid duration cannot be negative %d", bidDuration)
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func validateMaxAuctionDurationParam(i interface{}) error {
|
||||||
|
maxAuctionDuration, ok := i.(time.Duration)
|
||||||
|
if !ok {
|
||||||
|
return fmt.Errorf("invalid parameter type: %T", i)
|
||||||
|
}
|
||||||
|
|
||||||
|
if maxAuctionDuration < 0 {
|
||||||
|
return fmt.Errorf("max auction duration cannot be negative %d", maxAuctionDuration)
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func validateIncrementSurplusParam(i interface{}) error {
|
||||||
|
incrementSurplus, ok := i.(sdk.Dec)
|
||||||
|
if !ok {
|
||||||
|
return fmt.Errorf("invalid parameter type: %T", i)
|
||||||
|
}
|
||||||
|
|
||||||
|
if incrementSurplus == emptyDec || incrementSurplus.IsNil() {
|
||||||
|
return errors.New("surplus auction increment cannot be nil or empty")
|
||||||
|
}
|
||||||
|
|
||||||
|
if incrementSurplus.IsNegative() {
|
||||||
|
return fmt.Errorf("surplus auction increment cannot be less than zero %s", incrementSurplus)
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func validateIncrementDebtParam(i interface{}) error {
|
||||||
|
incrementDebt, ok := i.(sdk.Dec)
|
||||||
|
if !ok {
|
||||||
|
return fmt.Errorf("invalid parameter type: %T", i)
|
||||||
|
}
|
||||||
|
|
||||||
|
if incrementDebt == emptyDec || incrementDebt.IsNil() {
|
||||||
|
return errors.New("debt auction increment cannot be nil or empty")
|
||||||
|
}
|
||||||
|
|
||||||
|
if incrementDebt.IsNegative() {
|
||||||
|
return fmt.Errorf("debt auction increment cannot be less than zero %s", incrementDebt)
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func validateIncrementCollateralParam(i interface{}) error {
|
||||||
|
incrementCollateral, ok := i.(sdk.Dec)
|
||||||
|
if !ok {
|
||||||
|
return fmt.Errorf("invalid parameter type: %T", i)
|
||||||
|
}
|
||||||
|
|
||||||
|
if incrementCollateral == emptyDec || incrementCollateral.IsNil() {
|
||||||
|
return errors.New("collateral auction increment cannot be nil or empty")
|
||||||
|
}
|
||||||
|
|
||||||
|
if incrementCollateral.IsNegative() {
|
||||||
|
return fmt.Errorf("collateral auction increment cannot be less than zero %s", incrementCollateral)
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// Auction is an interface for handling common actions on auctions.
|
||||||
|
type Auction interface {
|
||||||
|
GetID() uint64
|
||||||
|
WithID(uint64) Auction
|
||||||
|
|
||||||
|
GetInitiator() string
|
||||||
|
GetLot() sdk.Coin
|
||||||
|
GetBidder() sdk.AccAddress
|
||||||
|
GetBid() sdk.Coin
|
||||||
|
GetEndTime() time.Time
|
||||||
|
|
||||||
|
GetType() string
|
||||||
|
GetPhase() string
|
||||||
|
}
|
||||||
|
|
||||||
|
// Auctions is a slice of auctions.
|
||||||
|
type Auctions []Auction
|
||||||
|
|
||||||
|
// BaseAuction is a common type shared by all Auctions.
|
||||||
|
type BaseAuction struct {
|
||||||
|
ID uint64 `json:"id" yaml:"id"`
|
||||||
|
Initiator string `json:"initiator" yaml:"initiator"` // Module name that starts the auction. Pays out Lot.
|
||||||
|
Lot sdk.Coin `json:"lot" yaml:"lot"` // Coins that will paid out by Initiator to the winning bidder.
|
||||||
|
Bidder sdk.AccAddress `json:"bidder" yaml:"bidder"` // Latest bidder. Receiver of Lot.
|
||||||
|
Bid sdk.Coin `json:"bid" yaml:"bid"` // Coins paid into the auction the bidder.
|
||||||
|
HasReceivedBids bool `json:"has_received_bids" yaml:"has_received_bids"` // Whether the auction has received any bids or not.
|
||||||
|
EndTime time.Time `json:"end_time" yaml:"end_time"` // Current auction closing time. Triggers at the end of the block with time ≥ EndTime.
|
||||||
|
MaxEndTime time.Time `json:"max_end_time" yaml:"max_end_time"` // Maximum closing time. Auctions can close before this but never after.
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetID is a getter for auction ID.
|
||||||
|
func (a BaseAuction) GetID() uint64 { return a.ID }
|
||||||
|
|
||||||
|
// GetInitiator is a getter for auction Initiator.
|
||||||
|
func (a BaseAuction) GetInitiator() string { return a.Initiator }
|
||||||
|
|
||||||
|
// GetLot is a getter for auction Lot.
|
||||||
|
func (a BaseAuction) GetLot() sdk.Coin { return a.Lot }
|
||||||
|
|
||||||
|
// GetBidder is a getter for auction Bidder.
|
||||||
|
func (a BaseAuction) GetBidder() sdk.AccAddress { return a.Bidder }
|
||||||
|
|
||||||
|
// GetBid is a getter for auction Bid.
|
||||||
|
func (a BaseAuction) GetBid() sdk.Coin { return a.Bid }
|
||||||
|
|
||||||
|
// GetEndTime is a getter for auction end time.
|
||||||
|
func (a BaseAuction) GetEndTime() time.Time { return a.EndTime }
|
||||||
|
|
||||||
|
// GetType returns the auction type. Used to identify auctions in event attributes.
|
||||||
|
func (a BaseAuction) GetType() string { return "base" }
|
||||||
|
|
||||||
|
// Validate verifies that the auction end time is before max end time
|
||||||
|
func (a BaseAuction) Validate() error {
|
||||||
|
// ID can be 0 for surplus, debt and collateral auctions
|
||||||
|
if strings.TrimSpace(a.Initiator) == "" {
|
||||||
|
return errors.New("auction initiator cannot be blank")
|
||||||
|
}
|
||||||
|
if !a.Lot.IsValid() {
|
||||||
|
return fmt.Errorf("invalid lot: %s", a.Lot)
|
||||||
|
}
|
||||||
|
// NOTE: bidder can be empty for Surplus and Collateral auctions
|
||||||
|
if !a.Bidder.Empty() && len(a.Bidder) != sdk.AddrLen {
|
||||||
|
return fmt.Errorf("the expected bidder address length is %d, actual length is %d", sdk.AddrLen, len(a.Bidder))
|
||||||
|
}
|
||||||
|
if !a.Bid.IsValid() {
|
||||||
|
return fmt.Errorf("invalid bid: %s", a.Bid)
|
||||||
|
}
|
||||||
|
if a.EndTime.IsZero() || a.MaxEndTime.IsZero() {
|
||||||
|
return errors.New("end time cannot be zero")
|
||||||
|
}
|
||||||
|
if a.EndTime.After(a.MaxEndTime) {
|
||||||
|
return fmt.Errorf("MaxEndTime < EndTime (%s < %s)", a.MaxEndTime, a.EndTime)
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (a BaseAuction) String() string {
|
||||||
|
return fmt.Sprintf(`Auction %d:
|
||||||
|
Initiator: %s
|
||||||
|
Lot: %s
|
||||||
|
Bidder: %s
|
||||||
|
Bid: %s
|
||||||
|
End Time: %s
|
||||||
|
Max End Time: %s`,
|
||||||
|
a.GetID(), a.Initiator, a.Lot,
|
||||||
|
a.Bidder, a.Bid, a.GetEndTime().String(),
|
||||||
|
a.MaxEndTime.String(),
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
// SurplusAuction is a forward auction that burns what it receives from bids.
|
||||||
|
// It is normally used to sell off excess pegged asset acquired by the CDP system.
|
||||||
|
type SurplusAuction struct {
|
||||||
|
BaseAuction `json:"base_auction" yaml:"base_auction"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// WithID returns an auction with the ID set.
|
||||||
|
func (a SurplusAuction) WithID(id uint64) Auction { a.ID = id; return a }
|
||||||
|
|
||||||
|
// GetType returns the auction type. Used to identify auctions in event attributes.
|
||||||
|
func (a SurplusAuction) GetType() string { return SurplusAuctionType }
|
||||||
|
|
||||||
|
// GetModuleAccountCoins returns the total number of coins held in the module account for this auction.
|
||||||
|
// It is used in genesis initialize the module account correctly.
|
||||||
|
func (a SurplusAuction) GetModuleAccountCoins() sdk.Coins {
|
||||||
|
// a.Bid is paid out on bids, so is never stored in the module account
|
||||||
|
return sdk.NewCoins(a.Lot)
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetPhase returns the direction of a surplus auction, which never changes.
|
||||||
|
func (a SurplusAuction) GetPhase() string { return ForwardAuctionPhase }
|
||||||
|
|
||||||
|
// NewSurplusAuction returns a new surplus auction.
|
||||||
|
func NewSurplusAuction(seller string, lot sdk.Coin, bidDenom string, endTime time.Time) SurplusAuction {
|
||||||
|
auction := SurplusAuction{BaseAuction{
|
||||||
|
// no ID
|
||||||
|
Initiator: seller,
|
||||||
|
Lot: lot,
|
||||||
|
Bidder: nil,
|
||||||
|
Bid: sdk.NewInt64Coin(bidDenom, 0),
|
||||||
|
HasReceivedBids: false, // new auctions don't have any bids
|
||||||
|
EndTime: endTime,
|
||||||
|
MaxEndTime: endTime,
|
||||||
|
}}
|
||||||
|
return auction
|
||||||
|
}
|
||||||
|
|
||||||
|
// DebtAuction is a reverse auction that mints what it pays out.
|
||||||
|
// It is normally used to acquire pegged asset to cover the CDP system's debts that were not covered by selling collateral.
|
||||||
|
type DebtAuction struct {
|
||||||
|
BaseAuction `json:"base_auction" yaml:"base_auction"`
|
||||||
|
|
||||||
|
CorrespondingDebt sdk.Coin `json:"corresponding_debt" yaml:"corresponding_debt"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// WithID returns an auction with the ID set.
|
||||||
|
func (a DebtAuction) WithID(id uint64) Auction { a.ID = id; return a }
|
||||||
|
|
||||||
|
// GetType returns the auction type. Used to identify auctions in event attributes.
|
||||||
|
func (a DebtAuction) GetType() string { return DebtAuctionType }
|
||||||
|
|
||||||
|
// GetModuleAccountCoins returns the total number of coins held in the module account for this auction.
|
||||||
|
// It is used in genesis initialize the module account correctly.
|
||||||
|
func (a DebtAuction) GetModuleAccountCoins() sdk.Coins {
|
||||||
|
// a.Lot is minted at auction close, so is never stored in the module account
|
||||||
|
// a.Bid is paid out on bids, so is never stored in the module account
|
||||||
|
return sdk.NewCoins(a.CorrespondingDebt)
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetPhase returns the direction of a debt auction, which never changes.
|
||||||
|
func (a DebtAuction) GetPhase() string { return ReverseAuctionPhase }
|
||||||
|
|
||||||
|
// Validate validates the DebtAuction fields values.
|
||||||
|
func (a DebtAuction) Validate() error {
|
||||||
|
if !a.CorrespondingDebt.IsValid() {
|
||||||
|
return fmt.Errorf("invalid corresponding debt: %s", a.CorrespondingDebt)
|
||||||
|
}
|
||||||
|
return a.BaseAuction.Validate()
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewDebtAuction returns a new debt auction.
|
||||||
|
func NewDebtAuction(buyerModAccName string, bid sdk.Coin, initialLot sdk.Coin, endTime time.Time, debt sdk.Coin) DebtAuction {
|
||||||
|
// Note: Bidder is set to the initiator's module account address instead of module name. (when the first bid is placed, it is paid out to the initiator)
|
||||||
|
// Setting to the module account address bypasses calling supply.SendCoinsFromModuleToModule, instead calls SendCoinsFromModuleToAccount.
|
||||||
|
// This isn't a problem currently, but if additional logic/validation was added for sending to coins to Module Accounts, it would be bypassed.
|
||||||
|
auction := DebtAuction{
|
||||||
|
BaseAuction: BaseAuction{
|
||||||
|
// no ID
|
||||||
|
Initiator: buyerModAccName,
|
||||||
|
Lot: initialLot,
|
||||||
|
Bidder: supply.NewModuleAddress(buyerModAccName), // send proceeds from the first bid to the buyer.
|
||||||
|
Bid: bid, // amount that the buyer is buying - doesn't change over course of auction
|
||||||
|
HasReceivedBids: false, // new auctions don't have any bids
|
||||||
|
EndTime: endTime,
|
||||||
|
MaxEndTime: endTime,
|
||||||
|
},
|
||||||
|
CorrespondingDebt: debt,
|
||||||
|
}
|
||||||
|
return auction
|
||||||
|
}
|
||||||
|
|
||||||
|
// CollateralAuction is a two phase auction.
|
||||||
|
// Initially, in forward auction phase, bids can be placed up to a max bid.
|
||||||
|
// Then it switches to a reverse auction phase, where the initial amount up for auction is bid down.
|
||||||
|
// Unsold Lot is sent to LotReturns, being divided among the addresses by weight.
|
||||||
|
// Collateral auctions are normally used to sell off collateral seized from CDPs.
|
||||||
|
type CollateralAuction struct {
|
||||||
|
BaseAuction `json:"base_auction" yaml:"base_auction"`
|
||||||
|
|
||||||
|
CorrespondingDebt sdk.Coin `json:"corresponding_debt" yaml:"corresponding_debt"`
|
||||||
|
MaxBid sdk.Coin `json:"max_bid" yaml:"max_bid"`
|
||||||
|
LotReturns WeightedAddresses `json:"lot_returns" yaml:"lot_returns"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// WithID returns an auction with the ID set.
|
||||||
|
func (a CollateralAuction) WithID(id uint64) Auction { a.ID = id; return a }
|
||||||
|
|
||||||
|
// GetType returns the auction type. Used to identify auctions in event attributes.
|
||||||
|
func (a CollateralAuction) GetType() string { return CollateralAuctionType }
|
||||||
|
|
||||||
|
// GetModuleAccountCoins returns the total number of coins held in the module account for this auction.
|
||||||
|
// It is used in genesis initialize the module account correctly.
|
||||||
|
func (a CollateralAuction) GetModuleAccountCoins() sdk.Coins {
|
||||||
|
// a.Bid is paid out on bids, so is never stored in the module account
|
||||||
|
return sdk.NewCoins(a.Lot).Add(sdk.NewCoins(a.CorrespondingDebt)...)
|
||||||
|
}
|
||||||
|
|
||||||
|
// IsReversePhase returns whether the auction has switched over to reverse phase or not.
|
||||||
|
// CollateralAuctions initially start in forward phase.
|
||||||
|
func (a CollateralAuction) IsReversePhase() bool {
|
||||||
|
return a.Bid.IsEqual(a.MaxBid)
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetPhase returns the direction of a collateral auction.
|
||||||
|
func (a CollateralAuction) GetPhase() string {
|
||||||
|
if a.IsReversePhase() {
|
||||||
|
return ReverseAuctionPhase
|
||||||
|
}
|
||||||
|
return ForwardAuctionPhase
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetLotReturns returns a collateral auction's lot owners
|
||||||
|
func (a CollateralAuction) GetLotReturns() WeightedAddresses {
|
||||||
|
return a.LotReturns
|
||||||
|
}
|
||||||
|
|
||||||
|
// Validate validates the CollateralAuction fields values.
|
||||||
|
func (a CollateralAuction) Validate() error {
|
||||||
|
if !a.CorrespondingDebt.IsValid() {
|
||||||
|
return fmt.Errorf("invalid corresponding debt: %s", a.CorrespondingDebt)
|
||||||
|
}
|
||||||
|
if !a.MaxBid.IsValid() {
|
||||||
|
return fmt.Errorf("invalid max bid: %s", a.MaxBid)
|
||||||
|
}
|
||||||
|
if err := a.LotReturns.Validate(); err != nil {
|
||||||
|
return fmt.Errorf("invalid lot returns: %w", err)
|
||||||
|
}
|
||||||
|
return a.BaseAuction.Validate()
|
||||||
|
}
|
||||||
|
|
||||||
|
func (a CollateralAuction) String() string {
|
||||||
|
return fmt.Sprintf(`Auction %d:
|
||||||
|
Initiator: %s
|
||||||
|
Lot: %s
|
||||||
|
Bidder: %s
|
||||||
|
Bid: %s
|
||||||
|
End Time: %s
|
||||||
|
Max End Time: %s
|
||||||
|
Max Bid %s
|
||||||
|
LotReturns %s
|
||||||
|
Corresponding Debt %s`,
|
||||||
|
a.GetID(), a.Initiator, a.Lot,
|
||||||
|
a.Bidder, a.Bid, a.GetEndTime().String(),
|
||||||
|
a.MaxEndTime.String(), a.MaxBid, a.LotReturns, a.CorrespondingDebt,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewCollateralAuction returns a new collateral auction.
|
||||||
|
func NewCollateralAuction(seller string, lot sdk.Coin, endTime time.Time, maxBid sdk.Coin, lotReturns WeightedAddresses, debt sdk.Coin) CollateralAuction {
|
||||||
|
auction := CollateralAuction{
|
||||||
|
BaseAuction: BaseAuction{
|
||||||
|
// no ID
|
||||||
|
Initiator: seller,
|
||||||
|
Lot: lot,
|
||||||
|
Bidder: nil,
|
||||||
|
Bid: sdk.NewInt64Coin(maxBid.Denom, 0),
|
||||||
|
HasReceivedBids: false, // new auctions don't have any bids
|
||||||
|
EndTime: endTime,
|
||||||
|
MaxEndTime: endTime},
|
||||||
|
CorrespondingDebt: debt,
|
||||||
|
MaxBid: maxBid,
|
||||||
|
LotReturns: lotReturns,
|
||||||
|
}
|
||||||
|
return auction
|
||||||
|
}
|
||||||
|
|
||||||
|
// WeightedAddresses is a type for storing some addresses and associated weights.
|
||||||
|
type WeightedAddresses struct {
|
||||||
|
Addresses []sdk.AccAddress `json:"addresses" yaml:"addresses"`
|
||||||
|
Weights []sdk.Int `json:"weights" yaml:"weights"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewWeightedAddresses returns a new list addresses with weights.
|
||||||
|
func NewWeightedAddresses(addrs []sdk.AccAddress, weights []sdk.Int) (WeightedAddresses, error) {
|
||||||
|
wa := WeightedAddresses{
|
||||||
|
Addresses: addrs,
|
||||||
|
Weights: weights,
|
||||||
|
}
|
||||||
|
if err := wa.Validate(); err != nil {
|
||||||
|
return WeightedAddresses{}, err
|
||||||
|
}
|
||||||
|
return wa, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// Validate checks for that the weights are not negative, not all zero, and the lengths match.
|
||||||
|
func (wa WeightedAddresses) Validate() error {
|
||||||
|
if len(wa.Weights) < 1 {
|
||||||
|
return fmt.Errorf("must be at least 1 weighted address")
|
||||||
|
}
|
||||||
|
|
||||||
|
if len(wa.Addresses) != len(wa.Weights) {
|
||||||
|
return fmt.Errorf("number of addresses doesn't match number of weights, %d ≠ %d", len(wa.Addresses), len(wa.Weights))
|
||||||
|
}
|
||||||
|
|
||||||
|
totalWeight := sdk.ZeroInt()
|
||||||
|
for i := range wa.Addresses {
|
||||||
|
if wa.Addresses[i].Empty() {
|
||||||
|
return fmt.Errorf("address %d cannot be empty", i)
|
||||||
|
}
|
||||||
|
if len(wa.Addresses[i]) != sdk.AddrLen {
|
||||||
|
return fmt.Errorf("address %d has an invalid length: expected %d, got %d", i, sdk.AddrLen, len(wa.Addresses[i]))
|
||||||
|
}
|
||||||
|
if wa.Weights[i].IsNegative() {
|
||||||
|
return fmt.Errorf("weight %d contains a negative amount: %s", i, wa.Weights[i])
|
||||||
|
}
|
||||||
|
totalWeight = totalWeight.Add(wa.Weights[i])
|
||||||
|
}
|
||||||
|
|
||||||
|
if !totalWeight.IsPositive() {
|
||||||
|
return fmt.Errorf("total weight must be positive")
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
423
x/bep3/legacy/v0_13/types.go
Normal file
423
x/bep3/legacy/v0_13/types.go
Normal file
@ -0,0 +1,423 @@
|
|||||||
|
package v0_13
|
||||||
|
|
||||||
|
import (
|
||||||
|
"encoding/hex"
|
||||||
|
"errors"
|
||||||
|
"fmt"
|
||||||
|
"strings"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
sdk "github.com/cosmos/cosmos-sdk/types"
|
||||||
|
sdkerrors "github.com/cosmos/cosmos-sdk/types/errors"
|
||||||
|
|
||||||
|
"github.com/tendermint/tendermint/crypto/tmhash"
|
||||||
|
tmbytes "github.com/tendermint/tendermint/libs/bytes"
|
||||||
|
tmtime "github.com/tendermint/tendermint/types/time"
|
||||||
|
)
|
||||||
|
|
||||||
|
// module constants
|
||||||
|
const (
|
||||||
|
bech32MainPrefix = "kava"
|
||||||
|
NULL SwapStatus = 0x00
|
||||||
|
Open SwapStatus = 0x01
|
||||||
|
Completed SwapStatus = 0x02
|
||||||
|
Expired SwapStatus = 0x03
|
||||||
|
INVALID SwapDirection = 0x00
|
||||||
|
Incoming SwapDirection = 0x01
|
||||||
|
Outgoing SwapDirection = 0x02
|
||||||
|
|
||||||
|
CreateAtomicSwap = "createAtomicSwap"
|
||||||
|
ClaimAtomicSwap = "claimAtomicSwap"
|
||||||
|
RefundAtomicSwap = "refundAtomicSwap"
|
||||||
|
CalcSwapID = "calcSwapID"
|
||||||
|
Int64Size = 8
|
||||||
|
RandomNumberHashLength = 32
|
||||||
|
RandomNumberLength = 32
|
||||||
|
AddrByteCount = 20
|
||||||
|
MaxOtherChainAddrLength = 64
|
||||||
|
SwapIDLength = 32
|
||||||
|
MaxExpectedIncomeLength = 64
|
||||||
|
)
|
||||||
|
|
||||||
|
// Parameter keys
|
||||||
|
var (
|
||||||
|
KeyAssetParams = []byte("AssetParams")
|
||||||
|
|
||||||
|
DefaultBnbDeputyFixedFee sdk.Int = sdk.NewInt(1000) // 0.00001 BNB
|
||||||
|
DefaultMinAmount sdk.Int = sdk.ZeroInt()
|
||||||
|
DefaultMaxAmount sdk.Int = sdk.NewInt(1000000000000) // 10,000 BNB
|
||||||
|
DefaultMinBlockLock uint64 = 220
|
||||||
|
DefaultMaxBlockLock uint64 = 270
|
||||||
|
DefaultPreviousBlockTime = tmtime.Canonical(time.Unix(0, 0))
|
||||||
|
)
|
||||||
|
|
||||||
|
// GenesisState - all bep3 state that must be provided at genesis
|
||||||
|
type GenesisState struct {
|
||||||
|
Params Params `json:"params" yaml:"params"`
|
||||||
|
AtomicSwaps AtomicSwaps `json:"atomic_swaps" yaml:"atomic_swaps"`
|
||||||
|
Supplies AssetSupplies `json:"supplies" yaml:"supplies"`
|
||||||
|
PreviousBlockTime time.Time `json:"previous_block_time" yaml:"previous_block_time"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewGenesisState creates a new GenesisState object
|
||||||
|
func NewGenesisState(params Params, swaps AtomicSwaps, supplies AssetSupplies, previousBlockTime time.Time) GenesisState {
|
||||||
|
return GenesisState{
|
||||||
|
Params: params,
|
||||||
|
AtomicSwaps: swaps,
|
||||||
|
Supplies: supplies,
|
||||||
|
PreviousBlockTime: previousBlockTime,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// DefaultGenesisState - default GenesisState used by Cosmos Hub
|
||||||
|
func DefaultGenesisState() GenesisState {
|
||||||
|
return NewGenesisState(
|
||||||
|
DefaultParams(),
|
||||||
|
AtomicSwaps{},
|
||||||
|
AssetSupplies{},
|
||||||
|
DefaultPreviousBlockTime,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Validate validates genesis inputs. It returns error if validation of any input fails.
|
||||||
|
func (gs GenesisState) Validate() error {
|
||||||
|
if err := gs.Params.Validate(); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
ids := map[string]bool{}
|
||||||
|
for _, swap := range gs.AtomicSwaps {
|
||||||
|
if ids[hex.EncodeToString(swap.GetSwapID())] {
|
||||||
|
return fmt.Errorf("found duplicate atomic swap ID %s", hex.EncodeToString(swap.GetSwapID()))
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := swap.Validate(); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
ids[hex.EncodeToString(swap.GetSwapID())] = true
|
||||||
|
}
|
||||||
|
|
||||||
|
supplyDenoms := map[string]bool{}
|
||||||
|
for _, supply := range gs.Supplies {
|
||||||
|
if err := supply.Validate(); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
if supplyDenoms[supply.GetDenom()] {
|
||||||
|
return fmt.Errorf("found duplicate denom in asset supplies %s", supply.GetDenom())
|
||||||
|
}
|
||||||
|
supplyDenoms[supply.GetDenom()] = true
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// Params governance parameters for bep3 module
|
||||||
|
type Params struct {
|
||||||
|
AssetParams AssetParams `json:"asset_params" yaml:"asset_params"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewParams returns a new params object
|
||||||
|
func NewParams(ap AssetParams,
|
||||||
|
) Params {
|
||||||
|
return Params{
|
||||||
|
AssetParams: ap,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// DefaultParams returns default params for bep3 module
|
||||||
|
func DefaultParams() Params {
|
||||||
|
return NewParams(AssetParams{})
|
||||||
|
}
|
||||||
|
|
||||||
|
// AssetParam parameters that must be specified for each bep3 asset
|
||||||
|
type AssetParam struct {
|
||||||
|
Denom string `json:"denom" yaml:"denom"` // name of the asset
|
||||||
|
CoinID int `json:"coin_id" yaml:"coin_id"` // SLIP-0044 registered coin type - see https://github.com/satoshilabs/slips/blob/master/slip-0044.md
|
||||||
|
SupplyLimit SupplyLimit `json:"supply_limit" yaml:"supply_limit"` // asset supply limit
|
||||||
|
Active bool `json:"active" yaml:"active"` // denotes if asset is available or paused
|
||||||
|
DeputyAddress sdk.AccAddress `json:"deputy_address" yaml:"deputy_address"` // the address of the relayer process
|
||||||
|
FixedFee sdk.Int `json:"fixed_fee" yaml:"fixed_fee"` // the fixed fee charged by the relayer process for outgoing swaps
|
||||||
|
MinSwapAmount sdk.Int `json:"min_swap_amount" yaml:"min_swap_amount"` // Minimum swap amount
|
||||||
|
MaxSwapAmount sdk.Int `json:"max_swap_amount" yaml:"max_swap_amount"` // Maximum swap amount
|
||||||
|
MinBlockLock uint64 `json:"min_block_lock" yaml:"min_block_lock"` // Minimum swap block lock
|
||||||
|
MaxBlockLock uint64 `json:"max_block_lock" yaml:"max_block_lock"` // Maximum swap block lock
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewAssetParam returns a new AssetParam
|
||||||
|
func NewAssetParam(
|
||||||
|
denom string, coinID int, limit SupplyLimit, active bool,
|
||||||
|
deputyAddr sdk.AccAddress, fixedFee sdk.Int, minSwapAmount sdk.Int,
|
||||||
|
maxSwapAmount sdk.Int, minBlockLock uint64, maxBlockLock uint64,
|
||||||
|
) AssetParam {
|
||||||
|
return AssetParam{
|
||||||
|
Denom: denom,
|
||||||
|
CoinID: coinID,
|
||||||
|
SupplyLimit: limit,
|
||||||
|
Active: active,
|
||||||
|
DeputyAddress: deputyAddr,
|
||||||
|
FixedFee: fixedFee,
|
||||||
|
MinSwapAmount: minSwapAmount,
|
||||||
|
MaxSwapAmount: maxSwapAmount,
|
||||||
|
MinBlockLock: minBlockLock,
|
||||||
|
MaxBlockLock: maxBlockLock,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// AssetParams array of AssetParam
|
||||||
|
type AssetParams []AssetParam
|
||||||
|
|
||||||
|
// SupplyLimit parameters that control the absolute and time-based limits for an assets's supply
|
||||||
|
type SupplyLimit struct {
|
||||||
|
Limit sdk.Int `json:"limit" yaml:"limit"` // the absolute supply limit for an asset
|
||||||
|
TimeLimited bool `json:"time_limited" yaml:"time_limited"` // boolean for if the supply is also limited by time
|
||||||
|
TimePeriod time.Duration `json:"time_period" yaml:"time_period"` // the duration for which the supply time limit applies
|
||||||
|
TimeBasedLimit sdk.Int `json:"time_based_limit" yaml:"time_based_limit"` // the supply limit for an asset for each time period
|
||||||
|
}
|
||||||
|
|
||||||
|
// Validate ensure that params have valid values
|
||||||
|
func (p Params) Validate() error {
|
||||||
|
return validateAssetParams(p.AssetParams)
|
||||||
|
}
|
||||||
|
|
||||||
|
func validateAssetParams(i interface{}) error {
|
||||||
|
assetParams, ok := i.(AssetParams)
|
||||||
|
if !ok {
|
||||||
|
return fmt.Errorf("invalid parameter type: %T", i)
|
||||||
|
}
|
||||||
|
|
||||||
|
coinDenoms := make(map[string]bool)
|
||||||
|
for _, asset := range assetParams {
|
||||||
|
if err := sdk.ValidateDenom(asset.Denom); err != nil {
|
||||||
|
return fmt.Errorf(fmt.Sprintf("asset denom invalid: %s", asset.Denom))
|
||||||
|
}
|
||||||
|
|
||||||
|
if asset.CoinID < 0 {
|
||||||
|
return fmt.Errorf(fmt.Sprintf("asset %s coin id must be a non negative integer", asset.Denom))
|
||||||
|
}
|
||||||
|
|
||||||
|
if asset.SupplyLimit.Limit.IsNegative() {
|
||||||
|
return fmt.Errorf(fmt.Sprintf("asset %s has invalid (negative) supply limit: %s", asset.Denom, asset.SupplyLimit.Limit))
|
||||||
|
}
|
||||||
|
|
||||||
|
if asset.SupplyLimit.TimeBasedLimit.IsNegative() {
|
||||||
|
return fmt.Errorf(fmt.Sprintf("asset %s has invalid (negative) supply time limit: %s", asset.Denom, asset.SupplyLimit.TimeBasedLimit))
|
||||||
|
}
|
||||||
|
|
||||||
|
if asset.SupplyLimit.TimeBasedLimit.GT(asset.SupplyLimit.Limit) {
|
||||||
|
return fmt.Errorf(fmt.Sprintf("asset %s cannot have supply time limit > supply limit: %s>%s", asset.Denom, asset.SupplyLimit.TimeBasedLimit, asset.SupplyLimit.Limit))
|
||||||
|
}
|
||||||
|
|
||||||
|
_, found := coinDenoms[asset.Denom]
|
||||||
|
if found {
|
||||||
|
return fmt.Errorf(fmt.Sprintf("asset %s cannot have duplicate denom", asset.Denom))
|
||||||
|
}
|
||||||
|
|
||||||
|
coinDenoms[asset.Denom] = true
|
||||||
|
|
||||||
|
if asset.DeputyAddress.Empty() {
|
||||||
|
return fmt.Errorf("deputy address cannot be empty for %s", asset.Denom)
|
||||||
|
}
|
||||||
|
|
||||||
|
if len(asset.DeputyAddress.Bytes()) != sdk.AddrLen {
|
||||||
|
return fmt.Errorf("%s deputy address invalid bytes length got %d, want %d", asset.Denom, len(asset.DeputyAddress.Bytes()), sdk.AddrLen)
|
||||||
|
}
|
||||||
|
|
||||||
|
if asset.FixedFee.IsNegative() {
|
||||||
|
return fmt.Errorf("asset %s cannot have a negative fixed fee %s", asset.Denom, asset.FixedFee)
|
||||||
|
}
|
||||||
|
|
||||||
|
if asset.MinBlockLock > asset.MaxBlockLock {
|
||||||
|
return fmt.Errorf("asset %s has minimum block lock > maximum block lock %d > %d", asset.Denom, asset.MinBlockLock, asset.MaxBlockLock)
|
||||||
|
}
|
||||||
|
|
||||||
|
if !asset.MinSwapAmount.IsPositive() {
|
||||||
|
return fmt.Errorf(fmt.Sprintf("asset %s must have a positive minimum swap amount, got %s", asset.Denom, asset.MinSwapAmount))
|
||||||
|
}
|
||||||
|
|
||||||
|
if !asset.MaxSwapAmount.IsPositive() {
|
||||||
|
return fmt.Errorf(fmt.Sprintf("asset %s must have a positive maximum swap amount, got %s", asset.Denom, asset.MaxSwapAmount))
|
||||||
|
}
|
||||||
|
|
||||||
|
if asset.MinSwapAmount.GT(asset.MaxSwapAmount) {
|
||||||
|
return fmt.Errorf("asset %s has minimum swap amount > maximum swap amount %s > %s", asset.Denom, asset.MinSwapAmount, asset.MaxSwapAmount)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// AtomicSwap contains the information for an atomic swap
|
||||||
|
type AtomicSwap struct {
|
||||||
|
Amount sdk.Coins `json:"amount" yaml:"amount"`
|
||||||
|
RandomNumberHash tmbytes.HexBytes `json:"random_number_hash" yaml:"random_number_hash"`
|
||||||
|
ExpireHeight uint64 `json:"expire_height" yaml:"expire_height"`
|
||||||
|
Timestamp int64 `json:"timestamp" yaml:"timestamp"`
|
||||||
|
Sender sdk.AccAddress `json:"sender" yaml:"sender"`
|
||||||
|
Recipient sdk.AccAddress `json:"recipient" yaml:"recipient"`
|
||||||
|
SenderOtherChain string `json:"sender_other_chain" yaml:"sender_other_chain"`
|
||||||
|
RecipientOtherChain string `json:"recipient_other_chain" yaml:"recipient_other_chain"`
|
||||||
|
ClosedBlock int64 `json:"closed_block" yaml:"closed_block"`
|
||||||
|
Status SwapStatus `json:"status" yaml:"status"`
|
||||||
|
CrossChain bool `json:"cross_chain" yaml:"cross_chain"`
|
||||||
|
Direction SwapDirection `json:"direction" yaml:"direction"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewAtomicSwap returns a new AtomicSwap
|
||||||
|
func NewAtomicSwap(amount sdk.Coins, randomNumberHash tmbytes.HexBytes, expireHeight uint64, timestamp int64,
|
||||||
|
sender, recipient sdk.AccAddress, senderOtherChain string, recipientOtherChain string, closedBlock int64,
|
||||||
|
status SwapStatus, crossChain bool, direction SwapDirection) AtomicSwap {
|
||||||
|
return AtomicSwap{
|
||||||
|
Amount: amount,
|
||||||
|
RandomNumberHash: randomNumberHash,
|
||||||
|
ExpireHeight: expireHeight,
|
||||||
|
Timestamp: timestamp,
|
||||||
|
Sender: sender,
|
||||||
|
Recipient: recipient,
|
||||||
|
SenderOtherChain: senderOtherChain,
|
||||||
|
RecipientOtherChain: recipientOtherChain,
|
||||||
|
ClosedBlock: closedBlock,
|
||||||
|
Status: status,
|
||||||
|
CrossChain: crossChain,
|
||||||
|
Direction: direction,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetSwapID calculates the ID of an atomic swap
|
||||||
|
func (a AtomicSwap) GetSwapID() tmbytes.HexBytes {
|
||||||
|
return CalculateSwapID(a.RandomNumberHash, a.Sender, a.SenderOtherChain)
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetCoins returns the swap's amount as sdk.Coins
|
||||||
|
func (a AtomicSwap) GetCoins() sdk.Coins {
|
||||||
|
return sdk.NewCoins(a.Amount...)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Validate performs a basic validation of an atomic swap fields.
|
||||||
|
func (a AtomicSwap) Validate() error {
|
||||||
|
if !a.Amount.IsValid() {
|
||||||
|
return fmt.Errorf("invalid amount: %s", a.Amount)
|
||||||
|
}
|
||||||
|
if !a.Amount.IsAllPositive() {
|
||||||
|
return fmt.Errorf("the swapped out coin must be positive: %s", a.Amount)
|
||||||
|
}
|
||||||
|
if len(a.RandomNumberHash) != RandomNumberHashLength {
|
||||||
|
return fmt.Errorf("the length of random number hash should be %d", RandomNumberHashLength)
|
||||||
|
}
|
||||||
|
if a.ExpireHeight == 0 {
|
||||||
|
return errors.New("expire height cannot be 0")
|
||||||
|
}
|
||||||
|
if a.Timestamp == 0 {
|
||||||
|
return errors.New("timestamp cannot be 0")
|
||||||
|
}
|
||||||
|
if a.Sender.Empty() {
|
||||||
|
return sdkerrors.Wrap(sdkerrors.ErrInvalidAddress, "sender cannot be empty")
|
||||||
|
}
|
||||||
|
if a.Recipient.Empty() {
|
||||||
|
return sdkerrors.Wrap(sdkerrors.ErrInvalidAddress, "recipient cannot be empty")
|
||||||
|
}
|
||||||
|
if len(a.Sender) != AddrByteCount {
|
||||||
|
return fmt.Errorf("the expected address length is %d, actual length is %d", AddrByteCount, len(a.Sender))
|
||||||
|
}
|
||||||
|
if len(a.Recipient) != AddrByteCount {
|
||||||
|
return fmt.Errorf("the expected address length is %d, actual length is %d", AddrByteCount, len(a.Recipient))
|
||||||
|
}
|
||||||
|
// NOTE: These adresses may not have a bech32 prefix.
|
||||||
|
if strings.TrimSpace(a.SenderOtherChain) == "" {
|
||||||
|
return sdkerrors.Wrap(sdkerrors.ErrInvalidAddress, "sender other chain cannot be blank")
|
||||||
|
}
|
||||||
|
if strings.TrimSpace(a.RecipientOtherChain) == "" {
|
||||||
|
return sdkerrors.Wrap(sdkerrors.ErrInvalidAddress, "recipient other chain cannot be blank")
|
||||||
|
}
|
||||||
|
if a.Status == Completed && a.ClosedBlock == 0 {
|
||||||
|
return errors.New("closed block cannot be 0")
|
||||||
|
}
|
||||||
|
if a.Status == NULL || a.Status > 3 {
|
||||||
|
return errors.New("invalid swap status")
|
||||||
|
}
|
||||||
|
if a.Direction == INVALID || a.Direction > 2 {
|
||||||
|
return errors.New("invalid swap direction")
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// AtomicSwaps is a slice of AtomicSwap
|
||||||
|
type AtomicSwaps []AtomicSwap
|
||||||
|
|
||||||
|
type SwapStatus byte
|
||||||
|
|
||||||
|
// NewSwapStatusFromString converts string to SwapStatus type
|
||||||
|
func NewSwapStatusFromString(str string) SwapStatus {
|
||||||
|
switch str {
|
||||||
|
case "Open", "open":
|
||||||
|
return Open
|
||||||
|
case "Completed", "completed":
|
||||||
|
return Completed
|
||||||
|
case "Expired", "expired":
|
||||||
|
return Expired
|
||||||
|
default:
|
||||||
|
return NULL
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// AssetSupply contains information about an asset's supply
|
||||||
|
type AssetSupply struct {
|
||||||
|
IncomingSupply sdk.Coin `json:"incoming_supply" yaml:"incoming_supply"`
|
||||||
|
OutgoingSupply sdk.Coin `json:"outgoing_supply" yaml:"outgoing_supply"`
|
||||||
|
CurrentSupply sdk.Coin `json:"current_supply" yaml:"current_supply"`
|
||||||
|
TimeLimitedCurrentSupply sdk.Coin `json:"time_limited_current_supply" yaml:"time_limited_current_supply"`
|
||||||
|
TimeElapsed time.Duration `json:"time_elapsed" yaml:"time_elapsed"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewAssetSupply initializes a new AssetSupply
|
||||||
|
func NewAssetSupply(incomingSupply, outgoingSupply, currentSupply, timeLimitedSupply sdk.Coin, timeElapsed time.Duration) AssetSupply {
|
||||||
|
return AssetSupply{
|
||||||
|
IncomingSupply: incomingSupply,
|
||||||
|
OutgoingSupply: outgoingSupply,
|
||||||
|
CurrentSupply: currentSupply,
|
||||||
|
TimeLimitedCurrentSupply: timeLimitedSupply,
|
||||||
|
TimeElapsed: timeElapsed,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Validate performs a basic validation of an asset supply fields.
|
||||||
|
func (a AssetSupply) Validate() error {
|
||||||
|
if !a.IncomingSupply.IsValid() {
|
||||||
|
return sdkerrors.Wrapf(sdkerrors.ErrInvalidCoins, "incoming supply %s", a.IncomingSupply)
|
||||||
|
}
|
||||||
|
if !a.OutgoingSupply.IsValid() {
|
||||||
|
return sdkerrors.Wrapf(sdkerrors.ErrInvalidCoins, "outgoing supply %s", a.OutgoingSupply)
|
||||||
|
}
|
||||||
|
if !a.CurrentSupply.IsValid() {
|
||||||
|
return sdkerrors.Wrapf(sdkerrors.ErrInvalidCoins, "current supply %s", a.CurrentSupply)
|
||||||
|
}
|
||||||
|
if !a.TimeLimitedCurrentSupply.IsValid() {
|
||||||
|
return sdkerrors.Wrapf(sdkerrors.ErrInvalidCoins, "time-limited current supply %s", a.CurrentSupply)
|
||||||
|
}
|
||||||
|
denom := a.CurrentSupply.Denom
|
||||||
|
if (a.IncomingSupply.Denom != denom) ||
|
||||||
|
(a.OutgoingSupply.Denom != denom) ||
|
||||||
|
(a.TimeLimitedCurrentSupply.Denom != denom) {
|
||||||
|
return fmt.Errorf("asset supply denoms do not match %s %s %s %s", a.CurrentSupply.Denom, a.IncomingSupply.Denom, a.OutgoingSupply.Denom, a.TimeLimitedCurrentSupply.Denom)
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetDenom getter method for the denom of the asset supply
|
||||||
|
func (a AssetSupply) GetDenom() string {
|
||||||
|
return a.CurrentSupply.Denom
|
||||||
|
}
|
||||||
|
|
||||||
|
// AssetSupplies is a slice of AssetSupply
|
||||||
|
type AssetSupplies []AssetSupply
|
||||||
|
|
||||||
|
// SwapDirection is the direction of an AtomicSwap
|
||||||
|
type SwapDirection byte
|
||||||
|
|
||||||
|
// CalculateSwapID calculates the hash of a RandomNumberHash, sdk.AccAddress, and string
|
||||||
|
func CalculateSwapID(randomNumberHash []byte, sender sdk.AccAddress, senderOtherChain string) []byte {
|
||||||
|
senderOtherChain = strings.ToLower(senderOtherChain)
|
||||||
|
data := randomNumberHash
|
||||||
|
data = append(data, sender.Bytes()...)
|
||||||
|
data = append(data, []byte(senderOtherChain)...)
|
||||||
|
return tmhash.Sum(data)
|
||||||
|
}
|
798
x/committee/legacy/v0_13/types.go
Normal file
798
x/committee/legacy/v0_13/types.go
Normal file
@ -0,0 +1,798 @@
|
|||||||
|
package v0_13
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
yaml "gopkg.in/yaml.v2"
|
||||||
|
|
||||||
|
"github.com/cosmos/cosmos-sdk/codec"
|
||||||
|
sdk "github.com/cosmos/cosmos-sdk/types"
|
||||||
|
govtypes "github.com/cosmos/cosmos-sdk/x/gov/types"
|
||||||
|
"github.com/cosmos/cosmos-sdk/x/params"
|
||||||
|
paramstypes "github.com/cosmos/cosmos-sdk/x/params/types"
|
||||||
|
upgrade "github.com/cosmos/cosmos-sdk/x/upgrade"
|
||||||
|
|
||||||
|
bep3types "github.com/kava-labs/kava/x/bep3/types"
|
||||||
|
cdptypes "github.com/kava-labs/kava/x/cdp/legacy/v0_11"
|
||||||
|
"github.com/kava-labs/kava/x/pricefeed"
|
||||||
|
pricefeedtypes "github.com/kava-labs/kava/x/pricefeed/types"
|
||||||
|
)
|
||||||
|
|
||||||
|
const (
|
||||||
|
MaxCommitteeDescriptionLength int = 512
|
||||||
|
)
|
||||||
|
|
||||||
|
// Permission is anything with a method that validates whether a proposal is allowed by it or not.
|
||||||
|
type Permission interface {
|
||||||
|
Allows(sdk.Context, *codec.Codec, ParamKeeper, PubProposal) bool
|
||||||
|
}
|
||||||
|
|
||||||
|
type ParamKeeper interface {
|
||||||
|
GetSubspace(string) (params.Subspace, bool)
|
||||||
|
}
|
||||||
|
|
||||||
|
// A Committee is a collection of addresses that are allowed to vote and enact any governance proposal that passes their permissions.
|
||||||
|
type Committee struct {
|
||||||
|
ID uint64 `json:"id" yaml:"id"`
|
||||||
|
Description string `json:"description" yaml:"description"`
|
||||||
|
Members []sdk.AccAddress `json:"members" yaml:"members"`
|
||||||
|
Permissions []Permission `json:"permissions" yaml:"permissions"`
|
||||||
|
VoteThreshold sdk.Dec `json:"vote_threshold" yaml:"vote_threshold"` // Smallest percentage of members that must vote for a proposal to pass.
|
||||||
|
ProposalDuration time.Duration `json:"proposal_duration" yaml:"proposal_duration"` // The length of time a proposal remains active for. Proposals will close earlier if they get enough votes.
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewCommittee(id uint64, description string, members []sdk.AccAddress, permissions []Permission, threshold sdk.Dec, duration time.Duration) Committee {
|
||||||
|
return Committee{
|
||||||
|
ID: id,
|
||||||
|
Description: description,
|
||||||
|
Members: members,
|
||||||
|
Permissions: permissions,
|
||||||
|
VoteThreshold: threshold,
|
||||||
|
ProposalDuration: duration,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c Committee) HasMember(addr sdk.AccAddress) bool {
|
||||||
|
for _, m := range c.Members {
|
||||||
|
if m.Equals(addr) {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
// HasPermissionsFor returns whether the committee is authorized to enact a proposal.
|
||||||
|
// As long as one permission allows the proposal then it goes through. Its the OR of all permissions.
|
||||||
|
func (c Committee) HasPermissionsFor(ctx sdk.Context, appCdc *codec.Codec, pk ParamKeeper, proposal PubProposal) bool {
|
||||||
|
for _, p := range c.Permissions {
|
||||||
|
if p.Allows(ctx, appCdc, pk, proposal) {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c Committee) Validate() error {
|
||||||
|
|
||||||
|
addressMap := make(map[string]bool, len(c.Members))
|
||||||
|
for _, m := range c.Members {
|
||||||
|
// check there are no duplicate members
|
||||||
|
if _, ok := addressMap[m.String()]; ok {
|
||||||
|
return fmt.Errorf("committe cannot have duplicate members, %s", m)
|
||||||
|
}
|
||||||
|
// check for valid addresses
|
||||||
|
if m.Empty() {
|
||||||
|
return fmt.Errorf("committee cannot have empty member address")
|
||||||
|
}
|
||||||
|
addressMap[m.String()] = true
|
||||||
|
}
|
||||||
|
|
||||||
|
if len(c.Members) == 0 {
|
||||||
|
return fmt.Errorf("committee cannot have zero members")
|
||||||
|
}
|
||||||
|
|
||||||
|
if len(c.Description) > MaxCommitteeDescriptionLength {
|
||||||
|
return fmt.Errorf("description length %d longer than max allowed %d", len(c.Description), MaxCommitteeDescriptionLength)
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, p := range c.Permissions {
|
||||||
|
if p == nil {
|
||||||
|
return fmt.Errorf("committee cannot have a nil permission")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// threshold must be in the range (0,1]
|
||||||
|
if c.VoteThreshold.IsNil() || c.VoteThreshold.LTE(sdk.ZeroDec()) || c.VoteThreshold.GT(sdk.NewDec(1)) {
|
||||||
|
return fmt.Errorf("invalid threshold: %s", c.VoteThreshold)
|
||||||
|
}
|
||||||
|
|
||||||
|
if c.ProposalDuration < 0 {
|
||||||
|
return fmt.Errorf("invalid proposal duration: %s", c.ProposalDuration)
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// ------------------------------------------
|
||||||
|
// Proposals
|
||||||
|
// ------------------------------------------
|
||||||
|
|
||||||
|
// PubProposal is the interface that all proposals must fulfill to be submitted to a committee.
|
||||||
|
// Proposal types can be created external to this module. For example a ParamChangeProposal, or CommunityPoolSpendProposal.
|
||||||
|
// It is pinned to the equivalent type in the gov module to create compatibility between proposal types.
|
||||||
|
type PubProposal govtypes.Content
|
||||||
|
|
||||||
|
// Proposal is an internal record of a governance proposal submitted to a committee.
|
||||||
|
type Proposal struct {
|
||||||
|
PubProposal `json:"pub_proposal" yaml:"pub_proposal"`
|
||||||
|
ID uint64 `json:"id" yaml:"id"`
|
||||||
|
CommitteeID uint64 `json:"committee_id" yaml:"committee_id"`
|
||||||
|
Deadline time.Time `json:"deadline" yaml:"deadline"`
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewProposal(pubProposal PubProposal, id uint64, committeeID uint64, deadline time.Time) Proposal {
|
||||||
|
return Proposal{
|
||||||
|
PubProposal: pubProposal,
|
||||||
|
ID: id,
|
||||||
|
CommitteeID: committeeID,
|
||||||
|
Deadline: deadline,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// HasExpiredBy calculates if the proposal will have expired by a certain time.
|
||||||
|
// All votes must be cast before deadline, those cast at time == deadline are not valid
|
||||||
|
func (p Proposal) HasExpiredBy(time time.Time) bool {
|
||||||
|
return !time.Before(p.Deadline)
|
||||||
|
}
|
||||||
|
|
||||||
|
// String implements the fmt.Stringer interface, and importantly overrides the String methods inherited from the embedded PubProposal type.
|
||||||
|
func (p Proposal) String() string {
|
||||||
|
bz, _ := yaml.Marshal(p)
|
||||||
|
return string(bz)
|
||||||
|
}
|
||||||
|
|
||||||
|
// ------------------------------------------
|
||||||
|
// Votes
|
||||||
|
// ------------------------------------------
|
||||||
|
|
||||||
|
type Vote struct {
|
||||||
|
ProposalID uint64 `json:"proposal_id" yaml:"proposal_id"`
|
||||||
|
Voter sdk.AccAddress `json:"voter" yaml:"voter"`
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewVote(proposalID uint64, voter sdk.AccAddress) Vote {
|
||||||
|
return Vote{
|
||||||
|
ProposalID: proposalID,
|
||||||
|
Voter: voter,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (v Vote) Validate() error {
|
||||||
|
if v.Voter.Empty() {
|
||||||
|
return fmt.Errorf("voter address cannot be empty")
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// ------------------------------------------
|
||||||
|
// GodPermission
|
||||||
|
// ------------------------------------------
|
||||||
|
|
||||||
|
// GodPermission allows any governance proposal. It is used mainly for testing.
|
||||||
|
type GodPermission struct{}
|
||||||
|
|
||||||
|
var _ Permission = GodPermission{}
|
||||||
|
|
||||||
|
func (GodPermission) Allows(sdk.Context, *codec.Codec, ParamKeeper, PubProposal) bool { return true }
|
||||||
|
|
||||||
|
func (GodPermission) MarshalYAML() (interface{}, error) {
|
||||||
|
valueToMarshal := struct {
|
||||||
|
Type string `yaml:"type"`
|
||||||
|
}{
|
||||||
|
Type: "god_permission",
|
||||||
|
}
|
||||||
|
return valueToMarshal, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// ------------------------------------------
|
||||||
|
// SimpleParamChangePermission
|
||||||
|
// ------------------------------------------
|
||||||
|
|
||||||
|
// SimpleParamChangePermission only allows changes to certain params
|
||||||
|
type SimpleParamChangePermission struct {
|
||||||
|
AllowedParams AllowedParams `json:"allowed_params" yaml:"allowed_params"`
|
||||||
|
}
|
||||||
|
|
||||||
|
var _ Permission = SimpleParamChangePermission{}
|
||||||
|
|
||||||
|
func (perm SimpleParamChangePermission) Allows(_ sdk.Context, _ *codec.Codec, _ ParamKeeper, p PubProposal) bool {
|
||||||
|
proposal, ok := p.(paramstypes.ParameterChangeProposal)
|
||||||
|
if !ok {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
for _, change := range proposal.Changes {
|
||||||
|
if !perm.AllowedParams.Contains(change) {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
func (perm SimpleParamChangePermission) MarshalYAML() (interface{}, error) {
|
||||||
|
valueToMarshal := struct {
|
||||||
|
Type string `yaml:"type"`
|
||||||
|
AllowedParams AllowedParams `yaml:"allowed_params"`
|
||||||
|
}{
|
||||||
|
Type: "param_change_permission",
|
||||||
|
AllowedParams: perm.AllowedParams,
|
||||||
|
}
|
||||||
|
return valueToMarshal, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
type AllowedParam struct {
|
||||||
|
Subspace string `json:"subspace" yaml:"subspace"`
|
||||||
|
Key string `json:"key" yaml:"key"`
|
||||||
|
}
|
||||||
|
type AllowedParams []AllowedParam
|
||||||
|
|
||||||
|
func (allowed AllowedParams) Contains(paramChange paramstypes.ParamChange) bool {
|
||||||
|
for _, p := range allowed {
|
||||||
|
if paramChange.Subspace == p.Subspace && paramChange.Key == p.Key {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
// ------------------------------------------
|
||||||
|
// TextPermission
|
||||||
|
// ------------------------------------------
|
||||||
|
|
||||||
|
// TextPermission allows any text governance proposal.
|
||||||
|
type TextPermission struct{}
|
||||||
|
|
||||||
|
var _ Permission = TextPermission{}
|
||||||
|
|
||||||
|
func (TextPermission) Allows(_ sdk.Context, _ *codec.Codec, _ ParamKeeper, p PubProposal) bool {
|
||||||
|
_, ok := p.(govtypes.TextProposal)
|
||||||
|
return ok
|
||||||
|
}
|
||||||
|
|
||||||
|
func (TextPermission) MarshalYAML() (interface{}, error) {
|
||||||
|
valueToMarshal := struct {
|
||||||
|
Type string `yaml:"type"`
|
||||||
|
}{
|
||||||
|
Type: "text_permission",
|
||||||
|
}
|
||||||
|
return valueToMarshal, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// ------------------------------------------
|
||||||
|
// SoftwareUpgradePermission
|
||||||
|
// ------------------------------------------
|
||||||
|
|
||||||
|
type SoftwareUpgradePermission struct{}
|
||||||
|
|
||||||
|
var _ Permission = SoftwareUpgradePermission{}
|
||||||
|
|
||||||
|
func (SoftwareUpgradePermission) Allows(_ sdk.Context, _ *codec.Codec, _ ParamKeeper, p PubProposal) bool {
|
||||||
|
_, ok := p.(upgrade.SoftwareUpgradeProposal)
|
||||||
|
return ok
|
||||||
|
}
|
||||||
|
|
||||||
|
func (SoftwareUpgradePermission) MarshalYAML() (interface{}, error) {
|
||||||
|
valueToMarshal := struct {
|
||||||
|
Type string `yaml:"type"`
|
||||||
|
}{
|
||||||
|
Type: "software_upgrade_permission",
|
||||||
|
}
|
||||||
|
return valueToMarshal, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// ------------------------------------------
|
||||||
|
// SubParamChangePermission
|
||||||
|
// ------------------------------------------
|
||||||
|
|
||||||
|
// ParamChangeProposal only allows changes to certain params
|
||||||
|
type SubParamChangePermission struct {
|
||||||
|
AllowedParams AllowedParams `json:"allowed_params" yaml:"allowed_params"`
|
||||||
|
AllowedCollateralParams AllowedCollateralParams `json:"allowed_collateral_params" yaml:"allowed_collateral_params"`
|
||||||
|
AllowedDebtParam AllowedDebtParam `json:"allowed_debt_param" yaml:"allowed_debt_param"`
|
||||||
|
AllowedAssetParams AllowedAssetParams `json:"allowed_asset_params" yaml:"allowed_asset_params"`
|
||||||
|
AllowedMarkets AllowedMarkets `json:"allowed_markets" yaml:"allowed_markets"`
|
||||||
|
}
|
||||||
|
|
||||||
|
var _ Permission = SubParamChangePermission{}
|
||||||
|
|
||||||
|
func (perm SubParamChangePermission) MarshalYAML() (interface{}, error) {
|
||||||
|
valueToMarshal := struct {
|
||||||
|
Type string `yaml:"type"`
|
||||||
|
AllowedParams AllowedParams `yaml:"allowed_params"`
|
||||||
|
AllowedCollateralParams AllowedCollateralParams `yaml:"allowed_collateral_params"`
|
||||||
|
AllowedDebtParam AllowedDebtParam `yaml:"allowed_debt_param"`
|
||||||
|
AllowedAssetParams AllowedAssetParams `yaml:"allowed_asset_params"`
|
||||||
|
AllowedMarkets AllowedMarkets `yaml:"allowed_markets"`
|
||||||
|
}{
|
||||||
|
Type: "param_change_permission",
|
||||||
|
AllowedParams: perm.AllowedParams,
|
||||||
|
AllowedCollateralParams: perm.AllowedCollateralParams,
|
||||||
|
AllowedDebtParam: perm.AllowedDebtParam,
|
||||||
|
AllowedAssetParams: perm.AllowedAssetParams,
|
||||||
|
AllowedMarkets: perm.AllowedMarkets,
|
||||||
|
}
|
||||||
|
return valueToMarshal, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (perm SubParamChangePermission) Allows(ctx sdk.Context, appCdc *codec.Codec, pk ParamKeeper, p PubProposal) bool {
|
||||||
|
// Check pubproposal has correct type
|
||||||
|
proposal, ok := p.(paramstypes.ParameterChangeProposal)
|
||||||
|
if !ok {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
// Check the param changes match the allowed keys
|
||||||
|
for _, change := range proposal.Changes {
|
||||||
|
if !perm.AllowedParams.Contains(change) {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// Check any CollateralParam changes are allowed
|
||||||
|
|
||||||
|
// Get the incoming CollaterParams value
|
||||||
|
var foundIncomingCP bool
|
||||||
|
var incomingCP cdptypes.CollateralParams
|
||||||
|
for _, change := range proposal.Changes {
|
||||||
|
if !(change.Subspace == cdptypes.ModuleName && change.Key == string(cdptypes.KeyCollateralParams)) {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
// note: in case of duplicates take the last value
|
||||||
|
foundIncomingCP = true
|
||||||
|
if err := appCdc.UnmarshalJSON([]byte(change.Value), &incomingCP); err != nil {
|
||||||
|
return false // invalid json value, so just disallow
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// only check if there was a proposed change
|
||||||
|
if foundIncomingCP {
|
||||||
|
// Get the current value of the CollateralParams
|
||||||
|
cdpSubspace, found := pk.GetSubspace(cdptypes.ModuleName)
|
||||||
|
if !found {
|
||||||
|
return false // not using a panic to help avoid begin blocker panics
|
||||||
|
}
|
||||||
|
var currentCP cdptypes.CollateralParams
|
||||||
|
cdpSubspace.Get(ctx, cdptypes.KeyCollateralParams, ¤tCP) // panics if something goes wrong
|
||||||
|
|
||||||
|
// Check all the incoming changes in the CollateralParams are allowed
|
||||||
|
collateralParamChangesAllowed := perm.AllowedCollateralParams.Allows(currentCP, incomingCP)
|
||||||
|
if !collateralParamChangesAllowed {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check any DebtParam changes are allowed
|
||||||
|
|
||||||
|
// Get the incoming DebtParam value
|
||||||
|
var foundIncomingDP bool
|
||||||
|
var incomingDP cdptypes.DebtParam
|
||||||
|
for _, change := range proposal.Changes {
|
||||||
|
if !(change.Subspace == cdptypes.ModuleName && change.Key == string(cdptypes.KeyDebtParam)) {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
// note: in case of duplicates take the last value
|
||||||
|
foundIncomingDP = true
|
||||||
|
if err := appCdc.UnmarshalJSON([]byte(change.Value), &incomingDP); err != nil {
|
||||||
|
return false // invalid json value, so just disallow
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// only check if there was a proposed change
|
||||||
|
if foundIncomingDP {
|
||||||
|
// Get the current value of the DebtParams
|
||||||
|
cdpSubspace, found := pk.GetSubspace(cdptypes.ModuleName)
|
||||||
|
if !found {
|
||||||
|
return false // not using a panic to help avoid begin blocker panics
|
||||||
|
}
|
||||||
|
var currentDP cdptypes.DebtParam
|
||||||
|
cdpSubspace.Get(ctx, cdptypes.KeyDebtParam, ¤tDP) // panics if something goes wrong
|
||||||
|
|
||||||
|
// Check the incoming changes in the DebtParam are allowed
|
||||||
|
debtParamChangeAllowed := perm.AllowedDebtParam.Allows(currentDP, incomingDP)
|
||||||
|
if !debtParamChangeAllowed {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check any AssetParams changes are allowed
|
||||||
|
|
||||||
|
// Get the incoming AssetParams value
|
||||||
|
var foundIncomingAPs bool
|
||||||
|
var incomingAPs bep3types.AssetParams
|
||||||
|
for _, change := range proposal.Changes {
|
||||||
|
if !(change.Subspace == bep3types.ModuleName && change.Key == string(bep3types.KeyAssetParams)) {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
// note: in case of duplicates take the last value
|
||||||
|
foundIncomingAPs = true
|
||||||
|
if err := appCdc.UnmarshalJSON([]byte(change.Value), &incomingAPs); err != nil {
|
||||||
|
return false // invalid json value, so just disallow
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// only check if there was a proposed change
|
||||||
|
if foundIncomingAPs {
|
||||||
|
// Get the current value of the SupportedAssets
|
||||||
|
subspace, found := pk.GetSubspace(bep3types.ModuleName)
|
||||||
|
if !found {
|
||||||
|
return false // not using a panic to help avoid begin blocker panics
|
||||||
|
}
|
||||||
|
var currentAPs bep3types.AssetParams
|
||||||
|
subspace.Get(ctx, bep3types.KeyAssetParams, ¤tAPs) // panics if something goes wrong
|
||||||
|
|
||||||
|
// Check all the incoming changes in the CollateralParams are allowed
|
||||||
|
assetParamsChangesAllowed := perm.AllowedAssetParams.Allows(currentAPs, incomingAPs)
|
||||||
|
if !assetParamsChangesAllowed {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check any Markets changes are allowed
|
||||||
|
|
||||||
|
// Get the incoming Markets value
|
||||||
|
var foundIncomingMs bool
|
||||||
|
var incomingMs pricefeedtypes.Markets
|
||||||
|
for _, change := range proposal.Changes {
|
||||||
|
if !(change.Subspace == pricefeedtypes.ModuleName && change.Key == string(pricefeedtypes.KeyMarkets)) {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
// note: in case of duplicates take the last value
|
||||||
|
foundIncomingMs = true
|
||||||
|
if err := appCdc.UnmarshalJSON([]byte(change.Value), &incomingMs); err != nil {
|
||||||
|
return false // invalid json value, so just disallow
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// only check if there was a proposed change
|
||||||
|
if foundIncomingMs {
|
||||||
|
// Get the current value of the Markets
|
||||||
|
subspace, found := pk.GetSubspace(pricefeedtypes.ModuleName)
|
||||||
|
if !found {
|
||||||
|
return false // not using a panic to help avoid begin blocker panics
|
||||||
|
}
|
||||||
|
var currentMs pricefeedtypes.Markets
|
||||||
|
subspace.Get(ctx, pricefeedtypes.KeyMarkets, ¤tMs) // panics if something goes wrong
|
||||||
|
|
||||||
|
// Check all the incoming changes in the Markets are allowed
|
||||||
|
marketsChangesAllowed := perm.AllowedMarkets.Allows(currentMs, incomingMs)
|
||||||
|
if !marketsChangesAllowed {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
// AllowedCollateralParam cdp.CollateralParam fields that can be subject to committee governance
|
||||||
|
type AllowedCollateralParam struct {
|
||||||
|
Type string `json:"type" yaml:"type"`
|
||||||
|
Denom bool `json:"denom" yaml:"denom"`
|
||||||
|
LiquidationRatio bool `json:"liquidation_ratio" yaml:"liquidation_ratio"`
|
||||||
|
DebtLimit bool `json:"debt_limit" yaml:"debt_limit"`
|
||||||
|
StabilityFee bool `json:"stability_fee" yaml:"stability_fee"`
|
||||||
|
AuctionSize bool `json:"auction_size" yaml:"auction_size"`
|
||||||
|
LiquidationPenalty bool `json:"liquidation_penalty" yaml:"liquidation_penalty"`
|
||||||
|
Prefix bool `json:"prefix" yaml:"prefix"`
|
||||||
|
SpotMarketID bool `json:"spot_market_id" yaml:"spot_market_id"`
|
||||||
|
LiquidationMarketID bool `json:"liquidation_market_id" yaml:"liquidation_market_id"`
|
||||||
|
ConversionFactor bool `json:"conversion_factor" yaml:"conversion_factor"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type AllowedCollateralParams []AllowedCollateralParam
|
||||||
|
|
||||||
|
func (acps AllowedCollateralParams) Allows(current, incoming cdptypes.CollateralParams) bool {
|
||||||
|
allAllowed := true
|
||||||
|
|
||||||
|
// do not allow CollateralParams to be added or removed
|
||||||
|
// this checks both lists are the same size, then below checks each incoming matches a current
|
||||||
|
if len(incoming) != len(current) {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
// for each param struct, check it is allowed, and if it is not, check the value has not changed
|
||||||
|
for _, incomingCP := range incoming {
|
||||||
|
// 1) check incoming cp is in list of allowed cps
|
||||||
|
var foundAllowedCP bool
|
||||||
|
var allowedCP AllowedCollateralParam
|
||||||
|
for _, p := range acps {
|
||||||
|
if p.Type != incomingCP.Type {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
foundAllowedCP = true
|
||||||
|
allowedCP = p
|
||||||
|
}
|
||||||
|
if !foundAllowedCP {
|
||||||
|
// incoming had a CollateralParam that wasn't in the list of allowed ones
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
// 2) Check incoming changes are individually allowed
|
||||||
|
// find existing CollateralParam
|
||||||
|
var foundCurrentCP bool
|
||||||
|
var currentCP cdptypes.CollateralParam
|
||||||
|
for _, p := range current {
|
||||||
|
if p.Denom != incomingCP.Denom {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
foundCurrentCP = true
|
||||||
|
currentCP = p
|
||||||
|
}
|
||||||
|
if !foundCurrentCP {
|
||||||
|
return false // not allowed to add param to list
|
||||||
|
}
|
||||||
|
// check changed values are all allowed
|
||||||
|
allowed := allowedCP.Allows(currentCP, incomingCP)
|
||||||
|
|
||||||
|
allAllowed = allAllowed && allowed
|
||||||
|
}
|
||||||
|
return allAllowed
|
||||||
|
}
|
||||||
|
|
||||||
|
func (acp AllowedCollateralParam) Allows(current, incoming cdptypes.CollateralParam) bool {
|
||||||
|
allowed := ((acp.Type == current.Type) && (acp.Type == incoming.Type)) && // require collatreral types to be all equal
|
||||||
|
(current.Denom == incoming.Denom || acp.Denom) &&
|
||||||
|
(current.LiquidationRatio.Equal(incoming.LiquidationRatio) || acp.LiquidationRatio) &&
|
||||||
|
(current.DebtLimit.IsEqual(incoming.DebtLimit) || acp.DebtLimit) &&
|
||||||
|
(current.StabilityFee.Equal(incoming.StabilityFee) || acp.StabilityFee) &&
|
||||||
|
(current.AuctionSize.Equal(incoming.AuctionSize) || acp.AuctionSize) &&
|
||||||
|
(current.LiquidationPenalty.Equal(incoming.LiquidationPenalty) || acp.LiquidationPenalty) &&
|
||||||
|
((current.Prefix == incoming.Prefix) || acp.Prefix) &&
|
||||||
|
((current.SpotMarketID == incoming.SpotMarketID) || acp.SpotMarketID) &&
|
||||||
|
((current.LiquidationMarketID == incoming.LiquidationMarketID) || acp.LiquidationMarketID) &&
|
||||||
|
(current.ConversionFactor.Equal(incoming.ConversionFactor) || acp.ConversionFactor)
|
||||||
|
return allowed
|
||||||
|
}
|
||||||
|
|
||||||
|
type AllowedDebtParam struct {
|
||||||
|
Denom bool `json:"denom" yaml:"denom"`
|
||||||
|
ReferenceAsset bool `json:"reference_asset" yaml:"reference_asset"`
|
||||||
|
ConversionFactor bool `json:"conversion_factor" yaml:"conversion_factor"`
|
||||||
|
DebtFloor bool `json:"debt_floor" yaml:"debt_floor"`
|
||||||
|
SavingsRate bool `json:"savings_rate" yaml:"savings_rate"`
|
||||||
|
}
|
||||||
|
|
||||||
|
func (adp AllowedDebtParam) Allows(current, incoming cdptypes.DebtParam) bool {
|
||||||
|
allowed := ((current.Denom == incoming.Denom) || adp.Denom) &&
|
||||||
|
((current.ReferenceAsset == incoming.ReferenceAsset) || adp.ReferenceAsset) &&
|
||||||
|
(current.ConversionFactor.Equal(incoming.ConversionFactor) || adp.ConversionFactor) &&
|
||||||
|
(current.DebtFloor.Equal(incoming.DebtFloor) || adp.DebtFloor) &&
|
||||||
|
(current.SavingsRate.Equal(incoming.SavingsRate) || adp.SavingsRate)
|
||||||
|
return allowed
|
||||||
|
}
|
||||||
|
|
||||||
|
type AllowedAssetParams []AllowedAssetParam
|
||||||
|
|
||||||
|
func (aaps AllowedAssetParams) Allows(current, incoming bep3types.AssetParams) bool {
|
||||||
|
allAllowed := true
|
||||||
|
|
||||||
|
// do not allow AssetParams to be added or removed
|
||||||
|
// this checks both lists are the same size, then below checks each incoming matches a current
|
||||||
|
if len(incoming) != len(current) {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
// for each asset struct, check it is allowed, and if it is not, check the value has not changed
|
||||||
|
for _, incomingAP := range incoming {
|
||||||
|
// 1) check incoming ap is in list of allowed aps
|
||||||
|
var foundAllowedAP bool
|
||||||
|
var allowedAP AllowedAssetParam
|
||||||
|
for _, p := range aaps {
|
||||||
|
if p.Denom != incomingAP.Denom {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
foundAllowedAP = true
|
||||||
|
allowedAP = p
|
||||||
|
}
|
||||||
|
if !foundAllowedAP {
|
||||||
|
// incoming had a AssetParam that wasn't in the list of allowed ones
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
// 2) Check incoming changes are individually allowed
|
||||||
|
// find existing SupportedAsset
|
||||||
|
var foundCurrentAP bool
|
||||||
|
var currentAP bep3types.AssetParam
|
||||||
|
for _, p := range current {
|
||||||
|
if p.Denom != incomingAP.Denom {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
foundCurrentAP = true
|
||||||
|
currentAP = p
|
||||||
|
}
|
||||||
|
if !foundCurrentAP {
|
||||||
|
return false // not allowed to add asset to list
|
||||||
|
}
|
||||||
|
// check changed values are all allowed
|
||||||
|
allowed := allowedAP.Allows(currentAP, incomingAP)
|
||||||
|
|
||||||
|
allAllowed = allAllowed && allowed
|
||||||
|
}
|
||||||
|
return allAllowed
|
||||||
|
}
|
||||||
|
|
||||||
|
type AllowedAssetParam struct {
|
||||||
|
Denom string `json:"denom" yaml:"denom"`
|
||||||
|
CoinID bool `json:"coin_id" yaml:"coin_id"`
|
||||||
|
Limit bool `json:"limit" yaml:"limit"`
|
||||||
|
Active bool `json:"active" yaml:"active"`
|
||||||
|
}
|
||||||
|
|
||||||
|
func (aap AllowedAssetParam) Allows(current, incoming bep3types.AssetParam) bool {
|
||||||
|
|
||||||
|
allowed := ((aap.Denom == current.Denom) && (aap.Denom == incoming.Denom)) && // require denoms to be all equal
|
||||||
|
((current.CoinID == incoming.CoinID) || aap.CoinID) &&
|
||||||
|
(current.SupplyLimit.Equals(incoming.SupplyLimit) || aap.Limit) &&
|
||||||
|
((current.Active == incoming.Active) || aap.Active)
|
||||||
|
return allowed
|
||||||
|
}
|
||||||
|
|
||||||
|
type AllowedMarkets []AllowedMarket
|
||||||
|
|
||||||
|
func (ams AllowedMarkets) Allows(current, incoming pricefeedtypes.Markets) bool {
|
||||||
|
allAllowed := true
|
||||||
|
|
||||||
|
// do not allow Markets to be added or removed
|
||||||
|
// this checks both lists are the same size, then below checks each incoming matches a current
|
||||||
|
if len(incoming) != len(current) {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
// for each market struct, check it is allowed, and if it is not, check the value has not changed
|
||||||
|
for _, incomingM := range incoming {
|
||||||
|
// 1) check incoming market is in list of allowed markets
|
||||||
|
var foundAllowedM bool
|
||||||
|
var allowedM AllowedMarket
|
||||||
|
for _, p := range ams {
|
||||||
|
if p.MarketID != incomingM.MarketID {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
foundAllowedM = true
|
||||||
|
allowedM = p
|
||||||
|
}
|
||||||
|
if !foundAllowedM {
|
||||||
|
// incoming had a Market that wasn't in the list of allowed ones
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
// 2) Check incoming changes are individually allowed
|
||||||
|
// find existing SupportedAsset
|
||||||
|
var foundCurrentM bool
|
||||||
|
var currentM pricefeed.Market
|
||||||
|
for _, p := range current {
|
||||||
|
if p.MarketID != incomingM.MarketID {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
foundCurrentM = true
|
||||||
|
currentM = p
|
||||||
|
}
|
||||||
|
if !foundCurrentM {
|
||||||
|
return false // not allowed to add market to list
|
||||||
|
}
|
||||||
|
// check changed values are all allowed
|
||||||
|
allowed := allowedM.Allows(currentM, incomingM)
|
||||||
|
|
||||||
|
allAllowed = allAllowed && allowed
|
||||||
|
}
|
||||||
|
return allAllowed
|
||||||
|
}
|
||||||
|
|
||||||
|
type AllowedMarket struct {
|
||||||
|
MarketID string `json:"market_id" yaml:"market_id"`
|
||||||
|
BaseAsset bool `json:"base_asset" yaml:"base_asset"`
|
||||||
|
QuoteAsset bool `json:"quote_asset" yaml:"quote_asset"`
|
||||||
|
Oracles bool `json:"oracles" yaml:"oracles"`
|
||||||
|
Active bool `json:"active" yaml:"active"`
|
||||||
|
}
|
||||||
|
|
||||||
|
func (am AllowedMarket) Allows(current, incoming pricefeedtypes.Market) bool {
|
||||||
|
allowed := ((am.MarketID == current.MarketID) && (am.MarketID == incoming.MarketID)) && // require denoms to be all equal
|
||||||
|
((current.BaseAsset == incoming.BaseAsset) || am.BaseAsset) &&
|
||||||
|
((current.QuoteAsset == incoming.QuoteAsset) || am.QuoteAsset) &&
|
||||||
|
(addressesEqual(current.Oracles, incoming.Oracles) || am.Oracles) &&
|
||||||
|
((current.Active == incoming.Active) || am.Active)
|
||||||
|
return allowed
|
||||||
|
}
|
||||||
|
|
||||||
|
// addressesEqual check if slices of addresses are equal, the order matters
|
||||||
|
func addressesEqual(addrs1, addrs2 []sdk.AccAddress) bool {
|
||||||
|
if len(addrs1) != len(addrs2) {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
areEqual := true
|
||||||
|
for i := range addrs1 {
|
||||||
|
areEqual = areEqual && addrs1[i].Equals(addrs2[i])
|
||||||
|
}
|
||||||
|
return areEqual
|
||||||
|
}
|
||||||
|
|
||||||
|
// DefaultNextProposalID is the starting poiint for proposal IDs.
|
||||||
|
const DefaultNextProposalID uint64 = 1
|
||||||
|
|
||||||
|
// GenesisState is state that must be provided at chain genesis.
|
||||||
|
type GenesisState struct {
|
||||||
|
NextProposalID uint64 `json:"next_proposal_id" yaml:"next_proposal_id"`
|
||||||
|
Committees []Committee `json:"committees" yaml:"committees"`
|
||||||
|
Proposals []Proposal `json:"proposals" yaml:"proposals"`
|
||||||
|
Votes []Vote `json:"votes" yaml:"votes"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewGenesisState returns a new genesis state object for the module.
|
||||||
|
func NewGenesisState(nextProposalID uint64, committees []Committee, proposals []Proposal, votes []Vote) GenesisState {
|
||||||
|
return GenesisState{
|
||||||
|
NextProposalID: nextProposalID,
|
||||||
|
Committees: committees,
|
||||||
|
Proposals: proposals,
|
||||||
|
Votes: votes,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// DefaultGenesisState returns the default genesis state for the module.
|
||||||
|
func DefaultGenesisState() GenesisState {
|
||||||
|
return NewGenesisState(
|
||||||
|
DefaultNextProposalID,
|
||||||
|
[]Committee{},
|
||||||
|
[]Proposal{},
|
||||||
|
[]Vote{},
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Validate performs basic validation of genesis data.
|
||||||
|
func (gs GenesisState) Validate() error {
|
||||||
|
// validate committees
|
||||||
|
committeeMap := make(map[uint64]bool, len(gs.Committees))
|
||||||
|
for _, com := range gs.Committees {
|
||||||
|
// check there are no duplicate IDs
|
||||||
|
if _, ok := committeeMap[com.ID]; ok {
|
||||||
|
return fmt.Errorf("duplicate committee ID found in genesis state; id: %d", com.ID)
|
||||||
|
}
|
||||||
|
committeeMap[com.ID] = true
|
||||||
|
|
||||||
|
// validate committee
|
||||||
|
if err := com.Validate(); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// validate proposals
|
||||||
|
proposalMap := make(map[uint64]bool, len(gs.Proposals))
|
||||||
|
for _, p := range gs.Proposals {
|
||||||
|
// check there are no duplicate IDs
|
||||||
|
if _, ok := proposalMap[p.ID]; ok {
|
||||||
|
return fmt.Errorf("duplicate proposal ID found in genesis state; id: %d", p.ID)
|
||||||
|
}
|
||||||
|
proposalMap[p.ID] = true
|
||||||
|
|
||||||
|
// validate next proposal ID
|
||||||
|
if p.ID >= gs.NextProposalID {
|
||||||
|
return fmt.Errorf("NextProposalID is not greater than all proposal IDs; id: %d", p.ID)
|
||||||
|
}
|
||||||
|
|
||||||
|
// check committee exists
|
||||||
|
if !committeeMap[p.CommitteeID] {
|
||||||
|
return fmt.Errorf("proposal refers to non existent committee; proposal: %+v", p)
|
||||||
|
}
|
||||||
|
|
||||||
|
// validate pubProposal
|
||||||
|
if err := p.PubProposal.ValidateBasic(); err != nil {
|
||||||
|
return fmt.Errorf("proposal %d invalid: %w", p.ID, err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// validate votes
|
||||||
|
for _, v := range gs.Votes {
|
||||||
|
// validate committee
|
||||||
|
if err := v.Validate(); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
// check proposal exists
|
||||||
|
if !proposalMap[v.ProposalID] {
|
||||||
|
return fmt.Errorf("vote refers to non existent proposal; vote: %+v", v)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
187
x/issuance/legacy/v0_13/types.go
Normal file
187
x/issuance/legacy/v0_13/types.go
Normal file
@ -0,0 +1,187 @@
|
|||||||
|
package v0_13
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
sdk "github.com/cosmos/cosmos-sdk/types"
|
||||||
|
sdkerrors "github.com/cosmos/cosmos-sdk/types/errors"
|
||||||
|
)
|
||||||
|
|
||||||
|
const (
|
||||||
|
// ModuleName The name that will be used throughout the module
|
||||||
|
ModuleName = "issuance"
|
||||||
|
|
||||||
|
// StoreKey Top level store key where all module items will be stored
|
||||||
|
StoreKey = ModuleName
|
||||||
|
|
||||||
|
// RouterKey Top level router key
|
||||||
|
RouterKey = ModuleName
|
||||||
|
|
||||||
|
// DefaultParamspace default name for parameter store
|
||||||
|
DefaultParamspace = ModuleName
|
||||||
|
|
||||||
|
// QuerierRoute route used for abci queries
|
||||||
|
QuerierRoute = ModuleName
|
||||||
|
)
|
||||||
|
|
||||||
|
// Parameter keys and default values
|
||||||
|
var (
|
||||||
|
KeyAssets = []byte("Assets")
|
||||||
|
DefaultAssets = Assets{}
|
||||||
|
ModuleAccountName = ModuleName
|
||||||
|
AssetSupplyPrefix = []byte{0x01}
|
||||||
|
PreviousBlockTimeKey = []byte{0x02}
|
||||||
|
)
|
||||||
|
|
||||||
|
// Params governance parameters for the issuance module
|
||||||
|
type Params struct {
|
||||||
|
Assets Assets `json:"assets" yaml:"assets"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewParams returns a new params object
|
||||||
|
func NewParams(assets Assets) Params {
|
||||||
|
return Params{Assets: assets}
|
||||||
|
}
|
||||||
|
|
||||||
|
// DefaultParams returns default params for issuance module
|
||||||
|
func DefaultParams() Params {
|
||||||
|
return NewParams(DefaultAssets)
|
||||||
|
}
|
||||||
|
|
||||||
|
// GenesisState is the state that must be provided at genesis for the issuance module
|
||||||
|
type GenesisState struct {
|
||||||
|
Params Params `json:"params" yaml:"params"`
|
||||||
|
Supplies AssetSupplies `json:"supplies" yaml:"supplies"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewGenesisState returns a new GenesisState
|
||||||
|
func NewGenesisState(params Params, supplies AssetSupplies) GenesisState {
|
||||||
|
return GenesisState{
|
||||||
|
Params: params,
|
||||||
|
Supplies: supplies,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// DefaultGenesisState returns the default GenesisState for the issuance module
|
||||||
|
func DefaultGenesisState() GenesisState {
|
||||||
|
return GenesisState{
|
||||||
|
Params: DefaultParams(),
|
||||||
|
Supplies: AssetSupplies{},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// RateLimit parameters for rate-limiting the supply of an issued asset
|
||||||
|
type RateLimit struct {
|
||||||
|
Active bool `json:"active" yaml:"active"`
|
||||||
|
Limit sdk.Int `json:"limit" yaml:"limit"`
|
||||||
|
TimePeriod time.Duration `json:"time_period" yaml:"time_period"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewRateLimit initializes a new RateLimit
|
||||||
|
func NewRateLimit(active bool, limit sdk.Int, timePeriod time.Duration) RateLimit {
|
||||||
|
return RateLimit{
|
||||||
|
Active: active,
|
||||||
|
Limit: limit,
|
||||||
|
TimePeriod: timePeriod,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Assets array of Asset
|
||||||
|
type Assets []Asset
|
||||||
|
|
||||||
|
// Validate checks if all assets are valid and there are no duplicate entries
|
||||||
|
func (as Assets) Validate() error {
|
||||||
|
assetDenoms := make(map[string]bool)
|
||||||
|
for _, a := range as {
|
||||||
|
if assetDenoms[a.Denom] {
|
||||||
|
return fmt.Errorf("cannot have duplicate asset denoms: %s", a.Denom)
|
||||||
|
}
|
||||||
|
if err := a.Validate(); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
assetDenoms[a.Denom] = true
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// Asset type for assets in the issuance module
|
||||||
|
type Asset struct {
|
||||||
|
Owner sdk.AccAddress `json:"owner" yaml:"owner"`
|
||||||
|
Denom string `json:"denom" yaml:"denom"`
|
||||||
|
BlockedAddresses []sdk.AccAddress `json:"blocked_addresses" yaml:"blocked_addresses"`
|
||||||
|
Paused bool `json:"paused" yaml:"paused"`
|
||||||
|
Blockable bool `json:"blockable" yaml:"blockable"`
|
||||||
|
RateLimit RateLimit `json:"rate_limit" yaml:"rate_limit"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewAsset returns a new Asset
|
||||||
|
func NewAsset(owner sdk.AccAddress, denom string, blockedAddresses []sdk.AccAddress, paused bool, blockable bool, limit RateLimit) Asset {
|
||||||
|
return Asset{
|
||||||
|
Owner: owner,
|
||||||
|
Denom: denom,
|
||||||
|
BlockedAddresses: blockedAddresses,
|
||||||
|
Paused: paused,
|
||||||
|
Blockable: blockable,
|
||||||
|
RateLimit: limit,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Validate performs a basic check of asset fields
|
||||||
|
func (a Asset) Validate() error {
|
||||||
|
if a.Owner.Empty() {
|
||||||
|
return fmt.Errorf("owner must not be empty")
|
||||||
|
}
|
||||||
|
if !a.Blockable && len(a.BlockedAddresses) > 0 {
|
||||||
|
return fmt.Errorf("asset %s does not support blocking, blocked-list should be empty: %s", a.Denom, a.BlockedAddresses)
|
||||||
|
}
|
||||||
|
for _, address := range a.BlockedAddresses {
|
||||||
|
if address.Empty() {
|
||||||
|
return fmt.Errorf("blocked address must not be empty")
|
||||||
|
}
|
||||||
|
if a.Owner.Equals(address) {
|
||||||
|
return fmt.Errorf("asset owner cannot be blocked")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return sdk.ValidateDenom(a.Denom)
|
||||||
|
}
|
||||||
|
|
||||||
|
// AssetSupply contains information about an asset's rate-limited supply (the total supply of the asset is tracked in the top-level supply module)
|
||||||
|
type AssetSupply struct {
|
||||||
|
CurrentSupply sdk.Coin `json:"current_supply" yaml:"current_supply"`
|
||||||
|
TimeElapsed time.Duration `json:"time_elapsed" yaml:"time_elapsed"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewAssetSupply initializes a new AssetSupply
|
||||||
|
func NewAssetSupply(currentSupply sdk.Coin, timeElapsed time.Duration) AssetSupply {
|
||||||
|
return AssetSupply{
|
||||||
|
CurrentSupply: currentSupply,
|
||||||
|
TimeElapsed: timeElapsed,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Validate performs a basic validation of an asset supply fields.
|
||||||
|
func (a AssetSupply) Validate() error {
|
||||||
|
if !a.CurrentSupply.IsValid() {
|
||||||
|
return sdkerrors.Wrapf(sdkerrors.ErrInvalidCoins, "outgoing supply %s", a.CurrentSupply)
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// String implements stringer
|
||||||
|
func (a AssetSupply) String() string {
|
||||||
|
return fmt.Sprintf(`
|
||||||
|
asset supply:
|
||||||
|
Current supply: %s
|
||||||
|
Time elapsed: %s
|
||||||
|
`,
|
||||||
|
a.CurrentSupply, a.TimeElapsed)
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetDenom getter method for the denom of the asset supply
|
||||||
|
func (a AssetSupply) GetDenom() string {
|
||||||
|
return a.CurrentSupply.Denom
|
||||||
|
}
|
||||||
|
|
||||||
|
// AssetSupplies is a slice of AssetSupply
|
||||||
|
type AssetSupplies []AssetSupply
|
81
x/kavadist/legacy/v0_13/types.go
Normal file
81
x/kavadist/legacy/v0_13/types.go
Normal file
@ -0,0 +1,81 @@
|
|||||||
|
package v0_13
|
||||||
|
|
||||||
|
import (
|
||||||
|
"time"
|
||||||
|
|
||||||
|
sdk "github.com/cosmos/cosmos-sdk/types"
|
||||||
|
|
||||||
|
cdptypes "github.com/kava-labs/kava/x/cdp/legacy/v0_13"
|
||||||
|
|
||||||
|
tmtime "github.com/tendermint/tendermint/types/time"
|
||||||
|
)
|
||||||
|
|
||||||
|
// Parameter keys and default values
|
||||||
|
var (
|
||||||
|
KeyActive = []byte("Active")
|
||||||
|
KeyPeriods = []byte("Periods")
|
||||||
|
DefaultActive = false
|
||||||
|
DefaultPeriods = Periods{}
|
||||||
|
DefaultPreviousBlockTime = tmtime.Canonical(time.Unix(0, 0))
|
||||||
|
GovDenom = cdptypes.DefaultGovDenom
|
||||||
|
)
|
||||||
|
|
||||||
|
// GenesisState is the state that must be provided at genesis.
|
||||||
|
type GenesisState struct {
|
||||||
|
Params Params `json:"params" yaml:"params"`
|
||||||
|
PreviousBlockTime time.Time `json:"previous_block_time" yaml:"previous_block_time"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewGenesisState returns a new genesis state
|
||||||
|
func NewGenesisState(params Params, previousBlockTime time.Time) GenesisState {
|
||||||
|
return GenesisState{
|
||||||
|
Params: params,
|
||||||
|
PreviousBlockTime: previousBlockTime,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// DefaultGenesisState returns a default genesis state
|
||||||
|
func DefaultGenesisState() GenesisState {
|
||||||
|
return GenesisState{
|
||||||
|
Params: DefaultParams(),
|
||||||
|
PreviousBlockTime: DefaultPreviousBlockTime,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Params governance parameters for kavadist module
|
||||||
|
type Params struct {
|
||||||
|
Active bool `json:"active" yaml:"active"`
|
||||||
|
Periods Periods `json:"periods" yaml:"periods"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewParams returns a new params object
|
||||||
|
func NewParams(active bool, periods Periods) Params {
|
||||||
|
return Params{
|
||||||
|
Active: active,
|
||||||
|
Periods: periods,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// DefaultParams returns default params for kavadist module
|
||||||
|
func DefaultParams() Params {
|
||||||
|
return NewParams(DefaultActive, DefaultPeriods)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Period stores the specified start and end dates, and the inflation, expressed as a decimal representing the yearly APR of KAVA tokens that will be minted during that period
|
||||||
|
type Period struct {
|
||||||
|
Start time.Time `json:"start" yaml:"start"` // example "2020-03-01T15:20:00Z"
|
||||||
|
End time.Time `json:"end" yaml:"end"` // example "2020-06-01T15:20:00Z"
|
||||||
|
Inflation sdk.Dec `json:"inflation" yaml:"inflation"` // example "1.000000003022265980" - 10% inflation
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewPeriod returns a new instance of Period
|
||||||
|
func NewPeriod(start time.Time, end time.Time, inflation sdk.Dec) Period {
|
||||||
|
return Period{
|
||||||
|
Start: start,
|
||||||
|
End: end,
|
||||||
|
Inflation: inflation,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Periods array of Period
|
||||||
|
type Periods []Period
|
211
x/pricefeed/legacy/v0_13/types.go
Normal file
211
x/pricefeed/legacy/v0_13/types.go
Normal file
@ -0,0 +1,211 @@
|
|||||||
|
package v0_13
|
||||||
|
|
||||||
|
import (
|
||||||
|
"errors"
|
||||||
|
"fmt"
|
||||||
|
"strings"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
sdk "github.com/cosmos/cosmos-sdk/types"
|
||||||
|
)
|
||||||
|
|
||||||
|
// Parameter keys
|
||||||
|
var (
|
||||||
|
KeyMarkets = []byte("Markets")
|
||||||
|
DefaultMarkets = Markets{}
|
||||||
|
)
|
||||||
|
|
||||||
|
// GenesisState - pricefeed state that must be provided at genesis
|
||||||
|
type GenesisState struct {
|
||||||
|
Params Params `json:"params" yaml:"params"`
|
||||||
|
PostedPrices PostedPrices `json:"posted_prices" yaml:"posted_prices"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewGenesisState creates a new genesis state for the pricefeed module
|
||||||
|
func NewGenesisState(p Params, pp []PostedPrice) GenesisState {
|
||||||
|
return GenesisState{
|
||||||
|
Params: p,
|
||||||
|
PostedPrices: pp,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// DefaultGenesisState defines default GenesisState for pricefeed
|
||||||
|
func DefaultGenesisState() GenesisState {
|
||||||
|
return NewGenesisState(
|
||||||
|
DefaultParams(),
|
||||||
|
[]PostedPrice{},
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Validate performs basic validation of genesis data returning an
|
||||||
|
// error for any failed validation criteria.
|
||||||
|
func (gs GenesisState) Validate() error {
|
||||||
|
if err := gs.Params.Validate(); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
return gs.PostedPrices.Validate()
|
||||||
|
}
|
||||||
|
|
||||||
|
// Params params for pricefeed. Can be altered via governance
|
||||||
|
type Params struct {
|
||||||
|
Markets Markets `json:"markets" yaml:"markets"` // Array containing the markets supported by the pricefeed
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewParams creates a new AssetParams object
|
||||||
|
func NewParams(markets Markets) Params {
|
||||||
|
return Params{
|
||||||
|
Markets: markets,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// DefaultParams default params for pricefeed
|
||||||
|
func DefaultParams() Params {
|
||||||
|
return NewParams(DefaultMarkets)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Validate ensure that params have valid values
|
||||||
|
func (p Params) Validate() error {
|
||||||
|
return validateMarketParams(p.Markets)
|
||||||
|
}
|
||||||
|
|
||||||
|
func validateMarketParams(i interface{}) error {
|
||||||
|
markets, ok := i.(Markets)
|
||||||
|
if !ok {
|
||||||
|
return fmt.Errorf("invalid parameter type: %T", i)
|
||||||
|
}
|
||||||
|
|
||||||
|
return markets.Validate()
|
||||||
|
}
|
||||||
|
|
||||||
|
// Market an asset in the pricefeed
|
||||||
|
type Market struct {
|
||||||
|
MarketID string `json:"market_id" yaml:"market_id"`
|
||||||
|
BaseAsset string `json:"base_asset" yaml:"base_asset"`
|
||||||
|
QuoteAsset string `json:"quote_asset" yaml:"quote_asset"`
|
||||||
|
Oracles []sdk.AccAddress `json:"oracles" yaml:"oracles"`
|
||||||
|
Active bool `json:"active" yaml:"active"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewMarket returns a new Market
|
||||||
|
func NewMarket(id, base, quote string, oracles []sdk.AccAddress, active bool) Market {
|
||||||
|
return Market{
|
||||||
|
MarketID: id,
|
||||||
|
BaseAsset: base,
|
||||||
|
QuoteAsset: quote,
|
||||||
|
Oracles: oracles,
|
||||||
|
Active: active,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Validate performs a basic validation of the market params
|
||||||
|
func (m Market) Validate() error {
|
||||||
|
if strings.TrimSpace(m.MarketID) == "" {
|
||||||
|
return errors.New("market id cannot be blank")
|
||||||
|
}
|
||||||
|
if err := sdk.ValidateDenom(m.BaseAsset); err != nil {
|
||||||
|
return fmt.Errorf("invalid base asset: %w", err)
|
||||||
|
}
|
||||||
|
if err := sdk.ValidateDenom(m.QuoteAsset); err != nil {
|
||||||
|
return fmt.Errorf("invalid quote asset: %w", err)
|
||||||
|
}
|
||||||
|
seenOracles := make(map[string]bool)
|
||||||
|
for i, oracle := range m.Oracles {
|
||||||
|
if oracle.Empty() {
|
||||||
|
return fmt.Errorf("oracle %d is empty", i)
|
||||||
|
}
|
||||||
|
if seenOracles[oracle.String()] {
|
||||||
|
return fmt.Errorf("duplicated oracle %s", oracle)
|
||||||
|
}
|
||||||
|
seenOracles[oracle.String()] = true
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// Markets array type for oracle
|
||||||
|
type Markets []Market
|
||||||
|
|
||||||
|
// Validate checks if all the markets are valid and there are no duplicated
|
||||||
|
// entries.
|
||||||
|
func (ms Markets) Validate() error {
|
||||||
|
seenMarkets := make(map[string]bool)
|
||||||
|
for _, m := range ms {
|
||||||
|
if seenMarkets[m.MarketID] {
|
||||||
|
return fmt.Errorf("duplicated market %s", m.MarketID)
|
||||||
|
}
|
||||||
|
if err := m.Validate(); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
seenMarkets[m.MarketID] = true
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// CurrentPrice struct that contains the metadata of a current price for a particular market in the pricefeed module.
|
||||||
|
type CurrentPrice struct {
|
||||||
|
MarketID string `json:"market_id" yaml:"market_id"`
|
||||||
|
Price sdk.Dec `json:"price" yaml:"price"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewCurrentPrice returns an instance of CurrentPrice
|
||||||
|
func NewCurrentPrice(marketID string, price sdk.Dec) CurrentPrice {
|
||||||
|
return CurrentPrice{MarketID: marketID, Price: price}
|
||||||
|
}
|
||||||
|
|
||||||
|
// CurrentPrices type for an array of CurrentPrice
|
||||||
|
type CurrentPrices []CurrentPrice
|
||||||
|
|
||||||
|
// PostedPrice price for market posted by a specific oracle
|
||||||
|
type PostedPrice struct {
|
||||||
|
MarketID string `json:"market_id" yaml:"market_id"`
|
||||||
|
OracleAddress sdk.AccAddress `json:"oracle_address" yaml:"oracle_address"`
|
||||||
|
Price sdk.Dec `json:"price" yaml:"price"`
|
||||||
|
Expiry time.Time `json:"expiry" yaml:"expiry"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewPostedPrice returns a new PostedPrice
|
||||||
|
func NewPostedPrice(marketID string, oracle sdk.AccAddress, price sdk.Dec, expiry time.Time) PostedPrice {
|
||||||
|
return PostedPrice{
|
||||||
|
MarketID: marketID,
|
||||||
|
OracleAddress: oracle,
|
||||||
|
Price: price,
|
||||||
|
Expiry: expiry,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Validate performs a basic check of a PostedPrice params.
|
||||||
|
func (pp PostedPrice) Validate() error {
|
||||||
|
if strings.TrimSpace(pp.MarketID) == "" {
|
||||||
|
return errors.New("market id cannot be blank")
|
||||||
|
}
|
||||||
|
if pp.OracleAddress.Empty() {
|
||||||
|
return errors.New("oracle address cannot be empty")
|
||||||
|
}
|
||||||
|
if pp.Price.IsNegative() {
|
||||||
|
return fmt.Errorf("posted price cannot be negative %s", pp.Price)
|
||||||
|
}
|
||||||
|
if pp.Expiry.IsZero() {
|
||||||
|
return errors.New("expiry time cannot be zero")
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// PostedPrices type for an array of PostedPrice
|
||||||
|
type PostedPrices []PostedPrice
|
||||||
|
|
||||||
|
// Validate checks if all the posted prices are valid and there are no duplicated
|
||||||
|
// entries.
|
||||||
|
func (pps PostedPrices) Validate() error {
|
||||||
|
seenPrices := make(map[string]bool)
|
||||||
|
for _, pp := range pps {
|
||||||
|
if pp.OracleAddress != nil && seenPrices[pp.MarketID+pp.OracleAddress.String()] {
|
||||||
|
return fmt.Errorf("duplicated posted price for marked id %s and oracle address %s", pp.MarketID, pp.OracleAddress)
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := pp.Validate(); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
seenPrices[pp.MarketID+pp.OracleAddress.String()] = true
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
72
x/validator-vesting/legacy/v0_13/types.go
Normal file
72
x/validator-vesting/legacy/v0_13/types.go
Normal file
@ -0,0 +1,72 @@
|
|||||||
|
package v0_13
|
||||||
|
|
||||||
|
import (
|
||||||
|
"errors"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
sdk "github.com/cosmos/cosmos-sdk/types"
|
||||||
|
authexported "github.com/cosmos/cosmos-sdk/x/auth/exported"
|
||||||
|
vestexported "github.com/cosmos/cosmos-sdk/x/auth/vesting/exported"
|
||||||
|
vestingtypes "github.com/cosmos/cosmos-sdk/x/auth/vesting/types"
|
||||||
|
|
||||||
|
tmtime "github.com/tendermint/tendermint/types/time"
|
||||||
|
)
|
||||||
|
|
||||||
|
// Assert ValidatorVestingAccount implements the vestexported.VestingAccount interface
|
||||||
|
// Assert ValidatorVestingAccount implements the authexported.GenesisAccount interface
|
||||||
|
var _ vestexported.VestingAccount = (*ValidatorVestingAccount)(nil)
|
||||||
|
var _ authexported.GenesisAccount = (*ValidatorVestingAccount)(nil)
|
||||||
|
|
||||||
|
// GenesisState - all auth state that must be provided at genesis
|
||||||
|
type GenesisState struct {
|
||||||
|
PreviousBlockTime time.Time `json:"previous_block_time" yaml:"previous_block_time"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewGenesisState - Create a new genesis state
|
||||||
|
func NewGenesisState(prevBlockTime time.Time) GenesisState {
|
||||||
|
return GenesisState{
|
||||||
|
PreviousBlockTime: prevBlockTime,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// DefaultGenesisState - Return a default genesis state
|
||||||
|
func DefaultGenesisState() GenesisState {
|
||||||
|
return NewGenesisState(tmtime.Canonical(time.Unix(0, 0)))
|
||||||
|
}
|
||||||
|
|
||||||
|
// ValidateGenesis returns nil because accounts are validated by auth
|
||||||
|
func ValidateGenesis(data GenesisState) error {
|
||||||
|
if data.PreviousBlockTime.IsZero() {
|
||||||
|
return errors.New("previous block time cannot be zero")
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// ValidatorVestingAccount implements the VestingAccount interface. It
|
||||||
|
// conditionally vests by unlocking coins during each specified period, provided
|
||||||
|
// that the validator address has validated at least **SigningThreshold** blocks during
|
||||||
|
// the previous vesting period. The signing threshold takes values 0 to 100 are represents the
|
||||||
|
// percentage of blocks that must be signed each period for the vesting to complete successfully.
|
||||||
|
// If the validator has not signed at least the threshold percentage of blocks during a period,
|
||||||
|
// the coins are returned to the return address, or burned if the return address is null.
|
||||||
|
type ValidatorVestingAccount struct {
|
||||||
|
*vestingtypes.PeriodicVestingAccount
|
||||||
|
ValidatorAddress sdk.ConsAddress `json:"validator_address" yaml:"validator_address"`
|
||||||
|
ReturnAddress sdk.AccAddress `json:"return_address" yaml:"return_address"`
|
||||||
|
SigningThreshold int64 `json:"signing_threshold" yaml:"signing_threshold"`
|
||||||
|
CurrentPeriodProgress CurrentPeriodProgress `json:"current_period_progress" yaml:"current_period_progress"`
|
||||||
|
VestingPeriodProgress []VestingProgress `json:"vesting_period_progress" yaml:"vesting_period_progress"`
|
||||||
|
DebtAfterFailedVesting sdk.Coins `json:"debt_after_failed_vesting" yaml:"debt_after_failed_vesting"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// CurrentPeriodProgress tracks the progress of the current vesting period
|
||||||
|
type CurrentPeriodProgress struct {
|
||||||
|
MissedBlocks int64 `json:"missed_blocks" yaml:"missed_blocks"`
|
||||||
|
TotalBlocks int64 `json:"total_blocks" yaml:"total_blocks"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// VestingProgress tracks the status of each vesting period
|
||||||
|
type VestingProgress struct {
|
||||||
|
PeriodComplete bool `json:"period_complete" yaml:"period_complete"`
|
||||||
|
VestingSuccessful bool `json:"vesting_successful" yaml:"vesting_successful"`
|
||||||
|
}
|
Loading…
Reference in New Issue
Block a user