mirror of
https://github.com/0glabs/0g-chain.git
synced 2024-11-10 10: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