github.com/InjectiveLabs/sdk-go@v1.53.0/chain/exchange/types/proposal.go (about)

     1  package types
     2  
     3  import (
     4  	"fmt"
     5  
     6  	"cosmossdk.io/errors"
     7  	"cosmossdk.io/math"
     8  	sdkerrors "github.com/cosmos/cosmos-sdk/types/errors"
     9  	govtypes "github.com/cosmos/cosmos-sdk/x/gov/types/v1beta1"
    10  
    11  	sdk "github.com/cosmos/cosmos-sdk/types"
    12  	gov "github.com/cosmos/cosmos-sdk/x/gov/types"
    13  	"github.com/ethereum/go-ethereum/common"
    14  
    15  	oracletypes "github.com/InjectiveLabs/sdk-go/chain/oracle/types"
    16  )
    17  
    18  // constants
    19  const (
    20  	ProposalTypeExchangeEnable                     string = "ProposalTypeExchangeEnable"
    21  	ProposalTypeBatchExchangeModification          string = "ProposalTypeBatchExchangeModification"
    22  	ProposalTypeSpotMarketParamUpdate              string = "ProposalTypeSpotMarketParamUpdate"
    23  	ProposalTypeSpotMarketLaunch                   string = "ProposalTypeSpotMarketLaunch"
    24  	ProposalTypePerpetualMarketLaunch              string = "ProposalTypePerpetualMarketLaunch"
    25  	ProposalTypeExpiryFuturesMarketLaunch          string = "ProposalTypeExpiryFuturesMarketLaunch"
    26  	ProposalTypeDerivativeMarketParamUpdate        string = "ProposalTypeDerivativeMarketParamUpdate"
    27  	ProposalTypeMarketForcedSettlement             string = "ProposalTypeMarketForcedSettlement"
    28  	ProposalUpdateDenomDecimals                    string = "ProposalUpdateDenomDecimals"
    29  	ProposalTypeTradingRewardCampaign              string = "ProposalTypeTradingRewardCampaign"
    30  	ProposalTypeTradingRewardCampaignUpdate        string = "ProposalTypeTradingRewardCampaignUpdateProposal"
    31  	ProposalTypeTradingRewardPointsUpdate          string = "ProposalTypeTradingRewardPointsUpdateProposal"
    32  	ProposalTypeFeeDiscountProposal                string = "ProposalTypeFeeDiscountProposal"
    33  	ProposalTypeBatchCommunityPoolSpendProposal    string = "ProposalTypeBatchCommunityPoolSpendProposal"
    34  	ProposalTypeBinaryOptionsMarketLaunch          string = "ProposalTypeBinaryOptionsMarketLaunch"
    35  	ProposalTypeBinaryOptionsMarketParamUpdate     string = "ProposalTypeBinaryOptionsMarketParamUpdate"
    36  	ProposalAtomicMarketOrderFeeMultiplierSchedule string = "ProposalAtomicMarketOrderFeeMultiplierSchedule"
    37  )
    38  
    39  func init() {
    40  	govtypes.RegisterProposalType(ProposalTypeExchangeEnable)
    41  	govtypes.RegisterProposalType(ProposalTypeBatchExchangeModification)
    42  	govtypes.RegisterProposalType(ProposalTypeSpotMarketParamUpdate)
    43  	govtypes.RegisterProposalType(ProposalTypeSpotMarketLaunch)
    44  	govtypes.RegisterProposalType(ProposalTypePerpetualMarketLaunch)
    45  	govtypes.RegisterProposalType(ProposalTypeExpiryFuturesMarketLaunch)
    46  	govtypes.RegisterProposalType(ProposalTypeDerivativeMarketParamUpdate)
    47  	govtypes.RegisterProposalType(ProposalTypeMarketForcedSettlement)
    48  	govtypes.RegisterProposalType(ProposalUpdateDenomDecimals)
    49  	govtypes.RegisterProposalType(ProposalTypeTradingRewardCampaign)
    50  	govtypes.RegisterProposalType(ProposalTypeTradingRewardCampaignUpdate)
    51  	govtypes.RegisterProposalType(ProposalTypeTradingRewardPointsUpdate)
    52  	govtypes.RegisterProposalType(ProposalTypeFeeDiscountProposal)
    53  	govtypes.RegisterProposalType(ProposalTypeBatchCommunityPoolSpendProposal)
    54  	govtypes.RegisterProposalType(ProposalTypeBinaryOptionsMarketLaunch)
    55  	govtypes.RegisterProposalType(ProposalTypeBinaryOptionsMarketParamUpdate)
    56  	govtypes.RegisterProposalType(ProposalAtomicMarketOrderFeeMultiplierSchedule)
    57  }
    58  
    59  func SafeIsPositiveInt(v math.Int) bool {
    60  	if v.IsNil() {
    61  		return false
    62  	}
    63  
    64  	return v.IsPositive()
    65  }
    66  
    67  func SafeIsPositiveDec(v math.LegacyDec) bool {
    68  	if v.IsNil() {
    69  		return false
    70  	}
    71  
    72  	return v.IsPositive()
    73  }
    74  
    75  func SafeIsNonNegativeDec(v math.LegacyDec) bool {
    76  	if v.IsNil() {
    77  		return false
    78  	}
    79  
    80  	return !v.IsNegative()
    81  }
    82  
    83  func IsZeroOrNilInt(v math.Int) bool {
    84  	return v.IsNil() || v.IsZero()
    85  }
    86  
    87  func IsZeroOrNilDec(v math.LegacyDec) bool {
    88  	return v.IsNil() || v.IsZero()
    89  }
    90  
    91  // Implements Proposal Interface
    92  var _ govtypes.Content = &ExchangeEnableProposal{}
    93  
    94  // GetTitle returns the title of this proposal.
    95  func (p *ExchangeEnableProposal) GetTitle() string {
    96  	return p.Title
    97  }
    98  
    99  // GetDescription returns the description of this proposal.
   100  func (p *ExchangeEnableProposal) GetDescription() string {
   101  	return p.Description
   102  }
   103  
   104  // ProposalRoute returns router key of this proposal.
   105  func (p *ExchangeEnableProposal) ProposalRoute() string { return RouterKey }
   106  
   107  // ProposalType returns proposal type of this proposal.
   108  func (p *ExchangeEnableProposal) ProposalType() string {
   109  	return ProposalTypeExchangeEnable
   110  }
   111  
   112  // ValidateBasic returns ValidateBasic result of this proposal.
   113  func (p *ExchangeEnableProposal) ValidateBasic() error {
   114  
   115  	switch p.ExchangeType {
   116  	case ExchangeType_SPOT, ExchangeType_DERIVATIVES:
   117  	default:
   118  		return ErrBadField
   119  	}
   120  	return govtypes.ValidateAbstract(p)
   121  }
   122  
   123  // Implements Proposal Interface
   124  var _ govtypes.Content = &BatchExchangeModificationProposal{}
   125  
   126  // GetTitle returns the title of this proposal.
   127  func (p *BatchExchangeModificationProposal) GetTitle() string {
   128  	return p.Title
   129  }
   130  
   131  // GetDescription returns the description of this proposal.
   132  func (p *BatchExchangeModificationProposal) GetDescription() string {
   133  	return p.Description
   134  }
   135  
   136  // ProposalRoute returns router key of this proposal.
   137  func (p *BatchExchangeModificationProposal) ProposalRoute() string { return RouterKey }
   138  
   139  // ProposalType returns proposal type of this proposal.
   140  func (p *BatchExchangeModificationProposal) ProposalType() string {
   141  	return ProposalTypeBatchExchangeModification
   142  }
   143  
   144  // ValidateBasic returns ValidateBasic result of this proposal.
   145  func (p *BatchExchangeModificationProposal) ValidateBasic() error {
   146  	for _, proposal := range p.SpotMarketParamUpdateProposals {
   147  		if err := proposal.ValidateBasic(); err != nil {
   148  			return err
   149  		}
   150  	}
   151  
   152  	for _, proposal := range p.DerivativeMarketParamUpdateProposals {
   153  		if err := proposal.ValidateBasic(); err != nil {
   154  			return err
   155  		}
   156  	}
   157  
   158  	for _, proposal := range p.SpotMarketLaunchProposals {
   159  		if err := proposal.ValidateBasic(); err != nil {
   160  			return err
   161  		}
   162  	}
   163  
   164  	for _, proposal := range p.PerpetualMarketLaunchProposals {
   165  		if err := proposal.ValidateBasic(); err != nil {
   166  			return err
   167  		}
   168  	}
   169  
   170  	for _, proposal := range p.ExpiryFuturesMarketLaunchProposals {
   171  		if err := proposal.ValidateBasic(); err != nil {
   172  			return err
   173  		}
   174  	}
   175  
   176  	if p.TradingRewardCampaignUpdateProposal != nil {
   177  		if err := p.TradingRewardCampaignUpdateProposal.ValidateBasic(); err != nil {
   178  			return err
   179  		}
   180  	}
   181  
   182  	for _, proposal := range p.BinaryOptionsMarketLaunchProposals {
   183  		if err := proposal.ValidateBasic(); err != nil {
   184  			return err
   185  		}
   186  	}
   187  
   188  	for _, proposal := range p.BinaryOptionsParamUpdateProposals {
   189  		if err := proposal.ValidateBasic(); err != nil {
   190  			return err
   191  		}
   192  	}
   193  
   194  	if p.DenomDecimalsUpdateProposal != nil {
   195  		if err := p.DenomDecimalsUpdateProposal.ValidateBasic(); err != nil {
   196  			return err
   197  		}
   198  	}
   199  
   200  	if p.FeeDiscountProposal != nil {
   201  		if err := p.FeeDiscountProposal.ValidateBasic(); err != nil {
   202  			return err
   203  		}
   204  	}
   205  
   206  	for _, proposal := range p.MarketForcedSettlementProposals {
   207  		if err := proposal.ValidateBasic(); err != nil {
   208  			return err
   209  		}
   210  	}
   211  
   212  	return govtypes.ValidateAbstract(p)
   213  }
   214  
   215  // NewSpotMarketParamUpdateProposal returns new instance of SpotMarketParamUpdateProposal
   216  func NewSpotMarketParamUpdateProposal(title, description string, marketID common.Hash, makerFeeRate, takerFeeRate, relayerFeeShareRate, minPriceTickSize, minQuantityTickSize, minNotional *math.LegacyDec, status MarketStatus, ticker string) *SpotMarketParamUpdateProposal {
   217  	return &SpotMarketParamUpdateProposal{
   218  		title,
   219  		description,
   220  		marketID.Hex(),
   221  		makerFeeRate,
   222  		takerFeeRate,
   223  		relayerFeeShareRate,
   224  		minPriceTickSize,
   225  		minQuantityTickSize,
   226  		status,
   227  		ticker,
   228  		minNotional,
   229  		nil,
   230  	}
   231  }
   232  
   233  // Implements Proposal Interface
   234  var _ govtypes.Content = &SpotMarketParamUpdateProposal{}
   235  
   236  // GetTitle returns the title of this proposal.
   237  func (p *SpotMarketParamUpdateProposal) GetTitle() string {
   238  	return p.Title
   239  }
   240  
   241  // GetDescription returns the description of this proposal.
   242  func (p *SpotMarketParamUpdateProposal) GetDescription() string {
   243  	return p.Description
   244  }
   245  
   246  // ProposalRoute returns router key of this proposal.
   247  func (p *SpotMarketParamUpdateProposal) ProposalRoute() string { return RouterKey }
   248  
   249  // ProposalType returns proposal type of this proposal.
   250  func (p *SpotMarketParamUpdateProposal) ProposalType() string {
   251  	return ProposalTypeSpotMarketParamUpdate
   252  }
   253  
   254  // ValidateBasic returns ValidateBasic result of this proposal.
   255  func (p *SpotMarketParamUpdateProposal) ValidateBasic() error {
   256  	if !IsHexHash(p.MarketId) {
   257  		return errors.Wrap(ErrMarketInvalid, p.MarketId)
   258  	}
   259  	if p.MakerFeeRate == nil &&
   260  		p.TakerFeeRate == nil &&
   261  		p.RelayerFeeShareRate == nil &&
   262  		p.MinPriceTickSize == nil &&
   263  		p.MinQuantityTickSize == nil &&
   264  		p.MinNotional == nil &&
   265  		p.AdminInfo == nil &&
   266  		p.Status == MarketStatus_Unspecified {
   267  		return errors.Wrap(gov.ErrInvalidProposalContent, "At least one field should not be nil")
   268  	}
   269  
   270  	if p.MakerFeeRate != nil {
   271  		if err := ValidateMakerFee(*p.MakerFeeRate); err != nil {
   272  			return err
   273  		}
   274  	}
   275  	if p.TakerFeeRate != nil {
   276  		if err := ValidateFee(*p.TakerFeeRate); err != nil {
   277  			return err
   278  		}
   279  	}
   280  	if p.RelayerFeeShareRate != nil {
   281  		if err := ValidateFee(*p.RelayerFeeShareRate); err != nil {
   282  			return err
   283  		}
   284  	}
   285  
   286  	if p.MinPriceTickSize != nil {
   287  		if err := ValidateTickSize(*p.MinPriceTickSize); err != nil {
   288  			return errors.Wrap(ErrInvalidPriceTickSize, err.Error())
   289  		}
   290  	}
   291  	if p.MinQuantityTickSize != nil {
   292  		if err := ValidateTickSize(*p.MinQuantityTickSize); err != nil {
   293  			return errors.Wrap(ErrInvalidQuantityTickSize, err.Error())
   294  		}
   295  	}
   296  	if p.MinNotional != nil {
   297  		if err := ValidateMinNotional(*p.MinNotional); err != nil {
   298  			return errors.Wrap(ErrInvalidNotional, err.Error())
   299  		}
   300  	}
   301  
   302  	if p.AdminInfo != nil {
   303  		if p.AdminInfo.Admin != "" {
   304  			if _, err := sdk.AccAddressFromBech32(p.AdminInfo.Admin); err != nil {
   305  				return errors.Wrap(ErrInvalidAddress, err.Error())
   306  			}
   307  		}
   308  		if p.AdminInfo.AdminPermissions > MaxPerm {
   309  			return ErrInvalidPermissions
   310  		}
   311  	}
   312  
   313  	if len(p.Ticker) > MaxTickerLength {
   314  		return errors.Wrapf(ErrInvalidTicker, "ticker should not exceed %d characters", MaxTickerLength)
   315  	}
   316  
   317  	switch p.Status {
   318  	case
   319  		MarketStatus_Unspecified,
   320  		MarketStatus_Active,
   321  		MarketStatus_Paused,
   322  		MarketStatus_Demolished,
   323  		MarketStatus_Expired:
   324  
   325  	default:
   326  		return errors.Wrap(ErrInvalidMarketStatus, p.Status.String())
   327  	}
   328  
   329  	return govtypes.ValidateAbstract(p)
   330  }
   331  
   332  // NewSpotMarketLaunchProposal returns new instance of SpotMarketLaunchProposal
   333  func NewSpotMarketLaunchProposal(
   334  	title string,
   335  	description string,
   336  	ticker string,
   337  	baseDenom string,
   338  	quoteDenom string,
   339  	minPriceTickSize math.LegacyDec,
   340  	minQuantityTickSize math.LegacyDec,
   341  	minNotional math.LegacyDec,
   342  	makerFeeRate *math.LegacyDec,
   343  	takerFeeRate *math.LegacyDec,
   344  ) *SpotMarketLaunchProposal {
   345  	return &SpotMarketLaunchProposal{
   346  		Title:               title,
   347  		Description:         description,
   348  		Ticker:              ticker,
   349  		BaseDenom:           baseDenom,
   350  		QuoteDenom:          quoteDenom,
   351  		MinPriceTickSize:    minPriceTickSize,
   352  		MinQuantityTickSize: minQuantityTickSize,
   353  		MinNotional:         minNotional,
   354  		MakerFeeRate:        makerFeeRate,
   355  		TakerFeeRate:        takerFeeRate,
   356  	}
   357  }
   358  
   359  // Implements Proposal Interface
   360  var _ govtypes.Content = &SpotMarketLaunchProposal{}
   361  
   362  // GetTitle returns the title of this proposal.
   363  func (p *SpotMarketLaunchProposal) GetTitle() string {
   364  	return p.Title
   365  }
   366  
   367  // GetDescription returns the description of this proposal.
   368  func (p *SpotMarketLaunchProposal) GetDescription() string {
   369  	return p.Description
   370  }
   371  
   372  // ProposalRoute returns router key of this proposal.
   373  func (p *SpotMarketLaunchProposal) ProposalRoute() string { return RouterKey }
   374  
   375  // ProposalType returns proposal type of this proposal.
   376  func (p *SpotMarketLaunchProposal) ProposalType() string {
   377  	return ProposalTypeSpotMarketLaunch
   378  }
   379  
   380  // ValidateBasic returns ValidateBasic result of this proposal.
   381  func (p *SpotMarketLaunchProposal) ValidateBasic() error {
   382  	if p.Ticker == "" || len(p.Ticker) > MaxTickerLength {
   383  		return errors.Wrapf(ErrInvalidTicker, "ticker should not be empty or exceed %d characters", MaxTickerLength)
   384  	}
   385  	if p.BaseDenom == "" {
   386  		return errors.Wrap(ErrInvalidBaseDenom, "base denom should not be empty")
   387  	}
   388  	if p.QuoteDenom == "" {
   389  		return errors.Wrap(ErrInvalidQuoteDenom, "quote denom should not be empty")
   390  	}
   391  	if p.BaseDenom == p.QuoteDenom {
   392  		return ErrSameDenoms
   393  	}
   394  
   395  	if err := ValidateTickSize(p.MinPriceTickSize); err != nil {
   396  		return errors.Wrap(ErrInvalidPriceTickSize, err.Error())
   397  	}
   398  	if err := ValidateTickSize(p.MinQuantityTickSize); err != nil {
   399  		return errors.Wrap(ErrInvalidQuantityTickSize, err.Error())
   400  	}
   401  	if err := ValidateMinNotional(p.MinNotional); err != nil {
   402  		return errors.Wrap(ErrInvalidNotional, err.Error())
   403  	}
   404  
   405  	if p.MakerFeeRate != nil {
   406  		if err := ValidateMakerFee(*p.MakerFeeRate); err != nil {
   407  			return err
   408  		}
   409  	}
   410  
   411  	if p.TakerFeeRate != nil {
   412  		if err := ValidateFee(*p.TakerFeeRate); err != nil {
   413  			return err
   414  		}
   415  	}
   416  
   417  	if (p.MakerFeeRate == nil && p.TakerFeeRate != nil) || (p.MakerFeeRate != nil && p.TakerFeeRate == nil) {
   418  		return errors.Wrap(ErrFeeRatesRelation, "maker and taker fee rate must either be both nil or both specified")
   419  	}
   420  
   421  	if p.MakerFeeRate != nil && p.TakerFeeRate != nil {
   422  		if p.MakerFeeRate.GT(*p.TakerFeeRate) {
   423  			return ErrFeeRatesRelation
   424  		}
   425  	}
   426  
   427  	return govtypes.ValidateAbstract(p)
   428  }
   429  
   430  // NewDerivativeMarketParamUpdateProposal returns new instance of DerivativeMarketParamUpdateProposal
   431  func NewDerivativeMarketParamUpdateProposal(
   432  	title string,
   433  	description string,
   434  	marketID string,
   435  	initialMarginRatio *math.LegacyDec,
   436  	maintenanceMarginRatio *math.LegacyDec,
   437  	makerFeeRate *math.LegacyDec,
   438  	takerFeeRate *math.LegacyDec,
   439  	relayerFeeShareRate *math.LegacyDec,
   440  	minPriceTickSize *math.LegacyDec,
   441  	minQuantityTickSize *math.LegacyDec,
   442  	minNotional *math.LegacyDec,
   443  	hourlyInterestRate *math.LegacyDec,
   444  	hourlyFundingRateCap *math.LegacyDec,
   445  	status MarketStatus,
   446  	oracleParams *OracleParams,
   447  	ticker string,
   448  	adminInfo *AdminInfo,
   449  ) *DerivativeMarketParamUpdateProposal {
   450  	return &DerivativeMarketParamUpdateProposal{
   451  		Title:                  title,
   452  		Description:            description,
   453  		MarketId:               marketID,
   454  		InitialMarginRatio:     initialMarginRatio,
   455  		MaintenanceMarginRatio: maintenanceMarginRatio,
   456  		MakerFeeRate:           makerFeeRate,
   457  		TakerFeeRate:           takerFeeRate,
   458  		RelayerFeeShareRate:    relayerFeeShareRate,
   459  		MinPriceTickSize:       minPriceTickSize,
   460  		MinQuantityTickSize:    minQuantityTickSize,
   461  		HourlyInterestRate:     hourlyInterestRate,
   462  		HourlyFundingRateCap:   hourlyFundingRateCap,
   463  		Status:                 status,
   464  		OracleParams:           oracleParams,
   465  		Ticker:                 ticker,
   466  		MinNotional:            minNotional,
   467  		AdminInfo:              adminInfo,
   468  	}
   469  }
   470  
   471  // Implements Proposal Interface
   472  var _ govtypes.Content = &DerivativeMarketParamUpdateProposal{}
   473  
   474  // GetTitle returns the title of this proposal
   475  func (p *DerivativeMarketParamUpdateProposal) GetTitle() string {
   476  	return p.Title
   477  }
   478  
   479  // GetDescription returns the description of this proposal
   480  func (p *DerivativeMarketParamUpdateProposal) GetDescription() string {
   481  	return p.Description
   482  }
   483  
   484  // ProposalRoute returns router key of this proposal.
   485  func (p *DerivativeMarketParamUpdateProposal) ProposalRoute() string { return RouterKey }
   486  
   487  // ProposalType returns proposal type of this proposal.
   488  func (p *DerivativeMarketParamUpdateProposal) ProposalType() string {
   489  	return ProposalTypeDerivativeMarketParamUpdate
   490  }
   491  
   492  // ValidateBasic returns ValidateBasic result of this proposal.
   493  func (p *DerivativeMarketParamUpdateProposal) ValidateBasic() error {
   494  	if !IsHexHash(p.MarketId) {
   495  		return errors.Wrap(ErrMarketInvalid, p.MarketId)
   496  	}
   497  	if p.MakerFeeRate == nil &&
   498  		p.TakerFeeRate == nil &&
   499  		p.RelayerFeeShareRate == nil &&
   500  		p.MinPriceTickSize == nil &&
   501  		p.MinQuantityTickSize == nil &&
   502  		p.MinNotional == nil &&
   503  		p.InitialMarginRatio == nil &&
   504  		p.MaintenanceMarginRatio == nil &&
   505  		p.HourlyInterestRate == nil &&
   506  		p.HourlyFundingRateCap == nil &&
   507  		p.Status == MarketStatus_Unspecified &&
   508  		p.AdminInfo == nil &&
   509  		p.OracleParams == nil {
   510  		return errors.Wrap(gov.ErrInvalidProposalContent, "At least one field should not be nil")
   511  	}
   512  
   513  	if p.MakerFeeRate != nil {
   514  		if err := ValidateMakerFee(*p.MakerFeeRate); err != nil {
   515  			return err
   516  		}
   517  	}
   518  	if p.TakerFeeRate != nil {
   519  		if err := ValidateFee(*p.TakerFeeRate); err != nil {
   520  			return err
   521  		}
   522  	}
   523  
   524  	if p.RelayerFeeShareRate != nil {
   525  		if err := ValidateFee(*p.RelayerFeeShareRate); err != nil {
   526  			return err
   527  		}
   528  	}
   529  
   530  	if p.InitialMarginRatio != nil {
   531  		if err := ValidateMarginRatio(*p.InitialMarginRatio); err != nil {
   532  			return err
   533  		}
   534  	}
   535  	if p.MaintenanceMarginRatio != nil {
   536  		if err := ValidateMarginRatio(*p.MaintenanceMarginRatio); err != nil {
   537  			return err
   538  		}
   539  	}
   540  
   541  	if p.MinPriceTickSize != nil {
   542  		if err := ValidateTickSize(*p.MinPriceTickSize); err != nil {
   543  			return errors.Wrap(ErrInvalidPriceTickSize, err.Error())
   544  		}
   545  	}
   546  	if p.MinQuantityTickSize != nil {
   547  		if err := ValidateTickSize(*p.MinQuantityTickSize); err != nil {
   548  			return errors.Wrap(ErrInvalidQuantityTickSize, err.Error())
   549  		}
   550  	}
   551  	if p.MinNotional != nil {
   552  		if err := ValidateMinNotional(*p.MinNotional); err != nil {
   553  			return errors.Wrap(ErrInvalidNotional, err.Error())
   554  		}
   555  	}
   556  
   557  	if p.HourlyInterestRate != nil {
   558  		if err := ValidateHourlyInterestRate(*p.HourlyInterestRate); err != nil {
   559  			return errors.Wrap(ErrInvalidHourlyInterestRate, err.Error())
   560  		}
   561  	}
   562  
   563  	if p.HourlyFundingRateCap != nil {
   564  		if err := ValidateHourlyFundingRateCap(*p.HourlyFundingRateCap); err != nil {
   565  			return errors.Wrap(ErrInvalidHourlyFundingRateCap, err.Error())
   566  		}
   567  	}
   568  
   569  	if p.AdminInfo != nil {
   570  		if p.AdminInfo.Admin != "" {
   571  			if _, err := sdk.AccAddressFromBech32(p.AdminInfo.Admin); err != nil {
   572  				return errors.Wrap(ErrInvalidAddress, err.Error())
   573  			}
   574  		}
   575  		if p.AdminInfo.AdminPermissions > MaxPerm {
   576  			return ErrInvalidPermissions
   577  		}
   578  	}
   579  
   580  	if len(p.Ticker) > MaxTickerLength {
   581  		return errors.Wrapf(ErrInvalidTicker, "ticker should not exceed %d characters", MaxTickerLength)
   582  	}
   583  
   584  	switch p.Status {
   585  	case
   586  		MarketStatus_Unspecified,
   587  		MarketStatus_Active,
   588  		MarketStatus_Paused,
   589  		MarketStatus_Demolished,
   590  		MarketStatus_Expired:
   591  
   592  	default:
   593  		return errors.Wrap(ErrInvalidMarketStatus, p.Status.String())
   594  	}
   595  
   596  	if p.OracleParams != nil {
   597  		if err := p.OracleParams.ValidateBasic(); err != nil {
   598  			return err
   599  		}
   600  	}
   601  
   602  	return govtypes.ValidateAbstract(p)
   603  }
   604  
   605  // NewMarketForcedSettlementProposal returns new instance of MarketForcedSettlementProposal
   606  func NewMarketForcedSettlementProposal(
   607  	title, description string, marketID string,
   608  	settlementPrice *math.LegacyDec,
   609  ) *MarketForcedSettlementProposal {
   610  	return &MarketForcedSettlementProposal{
   611  		Title:           title,
   612  		Description:     description,
   613  		MarketId:        marketID,
   614  		SettlementPrice: settlementPrice,
   615  	}
   616  }
   617  
   618  // Implements Proposal Interface
   619  var _ govtypes.Content = &MarketForcedSettlementProposal{}
   620  
   621  // GetTitle returns the title of this proposal
   622  func (p *MarketForcedSettlementProposal) GetTitle() string {
   623  	return p.Title
   624  }
   625  
   626  // GetDescription returns the description of this proposal
   627  func (p *MarketForcedSettlementProposal) GetDescription() string {
   628  	return p.Description
   629  }
   630  
   631  // ProposalRoute returns router key of this proposal.
   632  func (p *MarketForcedSettlementProposal) ProposalRoute() string { return RouterKey }
   633  
   634  // ProposalType returns proposal type of this proposal.
   635  func (p *MarketForcedSettlementProposal) ProposalType() string {
   636  	return ProposalTypeMarketForcedSettlement
   637  }
   638  
   639  // ValidateBasic returns ValidateBasic result of this proposal.
   640  func (p *MarketForcedSettlementProposal) ValidateBasic() error {
   641  	if !IsHexHash(p.MarketId) {
   642  		return errors.Wrap(ErrMarketInvalid, p.MarketId)
   643  	}
   644  
   645  	if p.SettlementPrice != nil && !SafeIsPositiveDec(*p.SettlementPrice) {
   646  		return errors.Wrap(ErrInvalidSettlement, "settlement price must be positive for derivatives and nil for spot")
   647  	}
   648  
   649  	return govtypes.ValidateAbstract(p)
   650  }
   651  
   652  // NewUpdateDenomDecimalsProposal returns new instance of UpdateDenomDecimalsProposal
   653  func NewUpdateDenomDecimalsProposal(
   654  	title, description string,
   655  	denomDecimals []*DenomDecimals,
   656  ) *UpdateDenomDecimalsProposal {
   657  	return &UpdateDenomDecimalsProposal{
   658  		Title:         title,
   659  		Description:   description,
   660  		DenomDecimals: denomDecimals,
   661  	}
   662  }
   663  
   664  // Implements Proposal Interface
   665  var _ govtypes.Content = &UpdateDenomDecimalsProposal{}
   666  
   667  // GetTitle returns the title of this proposal
   668  func (p *UpdateDenomDecimalsProposal) GetTitle() string {
   669  	return p.Title
   670  }
   671  
   672  // GetDescription returns the description of this proposal
   673  func (p *UpdateDenomDecimalsProposal) GetDescription() string {
   674  	return p.Description
   675  }
   676  
   677  // ProposalRoute returns router key of this proposal.
   678  func (p *UpdateDenomDecimalsProposal) ProposalRoute() string { return RouterKey }
   679  
   680  // ProposalType returns proposal type of this proposal.
   681  func (p *UpdateDenomDecimalsProposal) ProposalType() string {
   682  	return ProposalUpdateDenomDecimals
   683  }
   684  
   685  // ValidateBasic returns ValidateBasic result of this proposal.
   686  func (p *UpdateDenomDecimalsProposal) ValidateBasic() error {
   687  	for _, d := range p.DenomDecimals {
   688  		if err := d.Validate(); err != nil {
   689  			return err
   690  		}
   691  	}
   692  	return govtypes.ValidateAbstract(p)
   693  }
   694  
   695  func (d *DenomDecimals) Validate() error {
   696  	if d.Denom == "" {
   697  		return errors.Wrap(sdkerrors.ErrInvalidCoins, d.Denom)
   698  	}
   699  
   700  	if d.Decimals <= 0 || d.Decimals > uint64(MaxOracleScaleFactor) {
   701  		return errors.Wrapf(ErrInvalidDenomDecimal, "invalid decimals passed: %d", d.Decimals)
   702  	}
   703  	return nil
   704  }
   705  
   706  func NewOracleParams(
   707  	oracleBase string,
   708  	oracleQuote string,
   709  	oracleScaleFactor uint32,
   710  	oracleType oracletypes.OracleType,
   711  ) *OracleParams {
   712  	return &OracleParams{
   713  		OracleBase:        oracleBase,
   714  		OracleQuote:       oracleQuote,
   715  		OracleScaleFactor: oracleScaleFactor,
   716  		OracleType:        oracleType,
   717  	}
   718  }
   719  
   720  func (p *OracleParams) ValidateBasic() error {
   721  	if p.OracleBase == "" {
   722  		return errors.Wrap(ErrInvalidOracle, "oracle base should not be empty")
   723  	}
   724  	if p.OracleQuote == "" {
   725  		return errors.Wrap(ErrInvalidOracle, "oracle quote should not be empty")
   726  	}
   727  	if p.OracleBase == p.OracleQuote {
   728  		return ErrSameOracles
   729  	}
   730  	switch p.OracleType {
   731  	case oracletypes.OracleType_Band, oracletypes.OracleType_PriceFeed, oracletypes.OracleType_Coinbase, oracletypes.OracleType_Chainlink, oracletypes.OracleType_Razor,
   732  		oracletypes.OracleType_Dia, oracletypes.OracleType_API3, oracletypes.OracleType_Uma, oracletypes.OracleType_Pyth, oracletypes.OracleType_BandIBC, oracletypes.OracleType_Provider,
   733  		oracletypes.OracleType_Stork:
   734  
   735  	default:
   736  		return errors.Wrap(ErrInvalidOracleType, p.OracleType.String())
   737  	}
   738  
   739  	if p.OracleScaleFactor > MaxOracleScaleFactor {
   740  		return ErrExceedsMaxOracleScaleFactor
   741  	}
   742  
   743  	return nil
   744  }
   745  
   746  func NewProviderOracleParams(
   747  	symbol string,
   748  	oracleProvider string,
   749  	oracleScaleFactor uint32,
   750  	oracleType oracletypes.OracleType,
   751  ) *ProviderOracleParams {
   752  	return &ProviderOracleParams{
   753  		Symbol:            symbol,
   754  		Provider:          oracleProvider,
   755  		OracleScaleFactor: oracleScaleFactor,
   756  		OracleType:        oracleType,
   757  	}
   758  }
   759  
   760  func (p *ProviderOracleParams) ValidateBasic() error {
   761  	if p.Symbol == "" {
   762  		return errors.Wrap(ErrInvalidOracle, "oracle symbol should not be empty")
   763  	}
   764  	if p.Provider == "" {
   765  		return errors.Wrap(ErrInvalidOracle, "oracle provider should not be empty")
   766  	}
   767  
   768  	if p.OracleType != oracletypes.OracleType_Provider {
   769  		return errors.Wrap(ErrInvalidOracleType, p.OracleType.String())
   770  	}
   771  
   772  	if p.OracleScaleFactor > MaxOracleScaleFactor {
   773  		return ErrExceedsMaxOracleScaleFactor
   774  	}
   775  
   776  	return nil
   777  }
   778  
   779  // NewPerpetualMarketLaunchProposal returns new instance of PerpetualMarketLaunchProposal
   780  func NewPerpetualMarketLaunchProposal(
   781  	title, description, ticker, quoteDenom,
   782  	oracleBase, oracleQuote string, oracleScaleFactor uint32, oracleType oracletypes.OracleType,
   783  	initialMarginRatio, maintenanceMarginRatio, makerFeeRate, takerFeeRate, minPriceTickSize, minQuantityTickSize, minNotional math.LegacyDec,
   784  ) *PerpetualMarketLaunchProposal {
   785  	return &PerpetualMarketLaunchProposal{
   786  		Title:                  title,
   787  		Description:            description,
   788  		Ticker:                 ticker,
   789  		QuoteDenom:             quoteDenom,
   790  		OracleBase:             oracleBase,
   791  		OracleQuote:            oracleQuote,
   792  		OracleScaleFactor:      oracleScaleFactor,
   793  		OracleType:             oracleType,
   794  		InitialMarginRatio:     initialMarginRatio,
   795  		MaintenanceMarginRatio: maintenanceMarginRatio,
   796  		MakerFeeRate:           makerFeeRate,
   797  		TakerFeeRate:           takerFeeRate,
   798  		MinPriceTickSize:       minPriceTickSize,
   799  		MinQuantityTickSize:    minQuantityTickSize,
   800  		MinNotional:            minNotional,
   801  	}
   802  }
   803  
   804  // Implements Proposal Interface
   805  var _ govtypes.Content = &PerpetualMarketLaunchProposal{}
   806  
   807  // GetTitle returns the title of this proposal.
   808  func (p *PerpetualMarketLaunchProposal) GetTitle() string {
   809  	return p.Title
   810  }
   811  
   812  // GetDescription returns the description of this proposal.
   813  func (p *PerpetualMarketLaunchProposal) GetDescription() string {
   814  	return p.Description
   815  }
   816  
   817  // ProposalRoute returns router key of this proposal.
   818  func (p *PerpetualMarketLaunchProposal) ProposalRoute() string { return RouterKey }
   819  
   820  // ProposalType returns proposal type of this proposal.
   821  func (p *PerpetualMarketLaunchProposal) ProposalType() string {
   822  	return ProposalTypePerpetualMarketLaunch
   823  }
   824  
   825  // ValidateBasic returns ValidateBasic result of a perpetual market launch proposal.
   826  func (p *PerpetualMarketLaunchProposal) ValidateBasic() error {
   827  	if p.Ticker == "" || len(p.Ticker) > MaxTickerLength {
   828  		return errors.Wrapf(ErrInvalidTicker, "ticker should not be empty or exceed %d characters", MaxTickerLength)
   829  	}
   830  	if p.QuoteDenom == "" {
   831  		return errors.Wrap(ErrInvalidQuoteDenom, "quote denom should not be empty")
   832  	}
   833  
   834  	oracleParams := NewOracleParams(p.OracleBase, p.OracleQuote, p.OracleScaleFactor, p.OracleType)
   835  	if err := oracleParams.ValidateBasic(); err != nil {
   836  		return err
   837  	}
   838  	if err := ValidateMakerFee(p.MakerFeeRate); err != nil {
   839  		return err
   840  	}
   841  	if err := ValidateFee(p.TakerFeeRate); err != nil {
   842  		return err
   843  	}
   844  	if err := ValidateMarginRatio(p.InitialMarginRatio); err != nil {
   845  		return err
   846  	}
   847  	if err := ValidateMarginRatio(p.MaintenanceMarginRatio); err != nil {
   848  		return err
   849  	}
   850  	if p.MakerFeeRate.GT(p.TakerFeeRate) {
   851  		return ErrFeeRatesRelation
   852  	}
   853  	if p.InitialMarginRatio.LT(p.MaintenanceMarginRatio) {
   854  		return ErrMarginsRelation
   855  	}
   856  
   857  	if err := ValidateTickSize(p.MinPriceTickSize); err != nil {
   858  		return errors.Wrap(ErrInvalidPriceTickSize, err.Error())
   859  	}
   860  	if err := ValidateTickSize(p.MinQuantityTickSize); err != nil {
   861  		return errors.Wrap(ErrInvalidQuantityTickSize, err.Error())
   862  	}
   863  	if err := ValidateMinNotional(p.MinNotional); err != nil {
   864  		return errors.Wrap(ErrInvalidNotional, err.Error())
   865  	}
   866  
   867  	return govtypes.ValidateAbstract(p)
   868  }
   869  
   870  // NewExpiryFuturesMarketLaunchProposal returns new instance of ExpiryFuturesMarketLaunchProposal
   871  func NewExpiryFuturesMarketLaunchProposal(
   872  	title, description, ticker, quoteDenom,
   873  	oracleBase, oracleQuote string, oracleScaleFactor uint32, oracleType oracletypes.OracleType, expiry int64,
   874  	initialMarginRatio, maintenanceMarginRatio, makerFeeRate, takerFeeRate, minPriceTickSize, minQuantityTickSize, minNotional math.LegacyDec,
   875  ) *ExpiryFuturesMarketLaunchProposal {
   876  	return &ExpiryFuturesMarketLaunchProposal{
   877  		Title:                  title,
   878  		Description:            description,
   879  		Ticker:                 ticker,
   880  		QuoteDenom:             quoteDenom,
   881  		OracleBase:             oracleBase,
   882  		OracleQuote:            oracleQuote,
   883  		OracleScaleFactor:      oracleScaleFactor,
   884  		OracleType:             oracleType,
   885  		Expiry:                 expiry,
   886  		InitialMarginRatio:     initialMarginRatio,
   887  		MaintenanceMarginRatio: maintenanceMarginRatio,
   888  		MakerFeeRate:           makerFeeRate,
   889  		TakerFeeRate:           takerFeeRate,
   890  		MinPriceTickSize:       minPriceTickSize,
   891  		MinQuantityTickSize:    minQuantityTickSize,
   892  		MinNotional:            minNotional,
   893  	}
   894  }
   895  
   896  // Implements Proposal Interface
   897  var _ govtypes.Content = &ExpiryFuturesMarketLaunchProposal{}
   898  
   899  // GetTitle returns the title of this proposal.
   900  func (p *ExpiryFuturesMarketLaunchProposal) GetTitle() string {
   901  	return p.Title
   902  }
   903  
   904  // GetDescription returns the description of this proposal.
   905  func (p *ExpiryFuturesMarketLaunchProposal) GetDescription() string {
   906  	return p.Description
   907  }
   908  
   909  // ProposalRoute returns router key of this proposal.
   910  func (p *ExpiryFuturesMarketLaunchProposal) ProposalRoute() string { return RouterKey }
   911  
   912  // ProposalType returns proposal type of this proposal.
   913  func (p *ExpiryFuturesMarketLaunchProposal) ProposalType() string {
   914  	return ProposalTypeExpiryFuturesMarketLaunch
   915  }
   916  
   917  // ValidateBasic returns ValidateBasic result of a perpetual market launch proposal.
   918  func (p *ExpiryFuturesMarketLaunchProposal) ValidateBasic() error {
   919  	if p.Ticker == "" || len(p.Ticker) > MaxTickerLength {
   920  		return errors.Wrapf(ErrInvalidTicker, "ticker should not be empty or exceed %d characters", MaxTickerLength)
   921  	}
   922  	if p.QuoteDenom == "" {
   923  		return errors.Wrap(ErrInvalidQuoteDenom, "quote denom should not be empty")
   924  	}
   925  
   926  	oracleParams := NewOracleParams(p.OracleBase, p.OracleQuote, p.OracleScaleFactor, p.OracleType)
   927  	if err := oracleParams.ValidateBasic(); err != nil {
   928  		return err
   929  	}
   930  	if p.Expiry <= 0 {
   931  		return errors.Wrap(ErrInvalidExpiry, "expiry should not be empty")
   932  	}
   933  	if err := ValidateMakerFee(p.MakerFeeRate); err != nil {
   934  		return err
   935  	}
   936  	if err := ValidateFee(p.TakerFeeRate); err != nil {
   937  		return err
   938  	}
   939  	if err := ValidateMarginRatio(p.InitialMarginRatio); err != nil {
   940  		return err
   941  	}
   942  	if err := ValidateMarginRatio(p.MaintenanceMarginRatio); err != nil {
   943  		return err
   944  	}
   945  	if p.MakerFeeRate.GT(p.TakerFeeRate) {
   946  		return ErrFeeRatesRelation
   947  	}
   948  	if p.InitialMarginRatio.LT(p.MaintenanceMarginRatio) {
   949  		return ErrMarginsRelation
   950  	}
   951  
   952  	if err := ValidateTickSize(p.MinPriceTickSize); err != nil {
   953  		return errors.Wrap(ErrInvalidPriceTickSize, err.Error())
   954  	}
   955  	if err := ValidateTickSize(p.MinQuantityTickSize); err != nil {
   956  		return errors.Wrap(ErrInvalidQuantityTickSize, err.Error())
   957  	}
   958  	if err := ValidateMinNotional(p.MinNotional); err != nil {
   959  		return errors.Wrap(ErrInvalidNotional, err.Error())
   960  	}
   961  
   962  	return govtypes.ValidateAbstract(p)
   963  }
   964  
   965  // NewTradingRewardCampaignUpdateProposal returns new instance of TradingRewardCampaignLaunchProposal
   966  func NewTradingRewardCampaignUpdateProposal(
   967  	title, description string,
   968  	campaignInfo *TradingRewardCampaignInfo,
   969  	rewardPoolsAdditions []*CampaignRewardPool,
   970  	rewardPoolsUpdates []*CampaignRewardPool,
   971  ) *TradingRewardCampaignUpdateProposal {
   972  	p := &TradingRewardCampaignUpdateProposal{
   973  		Title:                        title,
   974  		Description:                  description,
   975  		CampaignInfo:                 campaignInfo,
   976  		CampaignRewardPoolsAdditions: rewardPoolsAdditions,
   977  		CampaignRewardPoolsUpdates:   rewardPoolsUpdates,
   978  	}
   979  	if err := p.ValidateBasic(); err != nil {
   980  		panic(err)
   981  	}
   982  	return p
   983  }
   984  
   985  // Implements Proposal Interface
   986  var _ govtypes.Content = &TradingRewardCampaignUpdateProposal{}
   987  
   988  // GetTitle returns the title of this proposal
   989  func (p *TradingRewardCampaignUpdateProposal) GetTitle() string {
   990  	return p.Title
   991  }
   992  
   993  // GetDescription returns the description of this proposal
   994  func (p *TradingRewardCampaignUpdateProposal) GetDescription() string {
   995  	return p.Description
   996  }
   997  
   998  // ProposalRoute returns router key of this proposal.
   999  func (p *TradingRewardCampaignUpdateProposal) ProposalRoute() string { return RouterKey }
  1000  
  1001  // ProposalType returns proposal type of this proposal.
  1002  func (p *TradingRewardCampaignUpdateProposal) ProposalType() string {
  1003  	return ProposalTypeTradingRewardCampaign
  1004  }
  1005  
  1006  // ValidateBasic returns ValidateBasic result of this proposal.
  1007  func (p *TradingRewardCampaignUpdateProposal) ValidateBasic() error {
  1008  	var err error
  1009  
  1010  	if err := p.CampaignInfo.ValidateBasic(); err != nil {
  1011  		return err
  1012  	}
  1013  
  1014  	prevStartTimestamp := int64(0)
  1015  	for _, pool := range p.CampaignRewardPoolsAdditions {
  1016  		if pool == nil {
  1017  			return errors.Wrap(ErrInvalidTradingRewardCampaign, "campaign reward pool addition cannot be nil")
  1018  		}
  1019  
  1020  		prevStartTimestamp, err = validateCampaignRewardPool(pool, p.CampaignInfo.CampaignDurationSeconds, prevStartTimestamp)
  1021  		if err != nil {
  1022  			return err
  1023  		}
  1024  	}
  1025  
  1026  	prevStartTimestamp = int64(0)
  1027  	for _, pool := range p.CampaignRewardPoolsUpdates {
  1028  		prevStartTimestamp, err = validateCampaignRewardPool(pool, p.CampaignInfo.CampaignDurationSeconds, prevStartTimestamp)
  1029  		if err != nil {
  1030  			return err
  1031  		}
  1032  	}
  1033  
  1034  	return govtypes.ValidateAbstract(p)
  1035  }
  1036  
  1037  // Implements Proposal Interface
  1038  var _ govtypes.Content = &TradingRewardPendingPointsUpdateProposal{}
  1039  
  1040  // GetTitle returns the title of this proposal
  1041  func (p *TradingRewardPendingPointsUpdateProposal) GetTitle() string {
  1042  	return p.Title
  1043  }
  1044  
  1045  // GetDescription returns the description of this proposal
  1046  func (p *TradingRewardPendingPointsUpdateProposal) GetDescription() string {
  1047  	return p.Description
  1048  }
  1049  
  1050  // ProposalRoute returns router key of this proposal.
  1051  func (p *TradingRewardPendingPointsUpdateProposal) ProposalRoute() string { return RouterKey }
  1052  
  1053  // ProposalType returns proposal type of this proposal.
  1054  func (p *TradingRewardPendingPointsUpdateProposal) ProposalType() string {
  1055  	return ProposalTypeTradingRewardPointsUpdate
  1056  }
  1057  
  1058  // ValidateBasic returns ValidateBasic result of this proposal.
  1059  func (p *TradingRewardPendingPointsUpdateProposal) ValidateBasic() error {
  1060  	if len(p.RewardPointUpdates) == 0 {
  1061  		return errors.Wrap(ErrInvalidTradingRewardsPendingPointsUpdate, "reward pending points update cannot be nil")
  1062  	}
  1063  
  1064  	if p.PendingPoolTimestamp <= 0 {
  1065  		return errors.Wrap(ErrInvalidTradingRewardsPendingPointsUpdate, "pending pool timestamp cannot be zero")
  1066  	}
  1067  
  1068  	accountAddresses := make([]string, 0)
  1069  
  1070  	for _, rewardPointUpdate := range p.RewardPointUpdates {
  1071  		if rewardPointUpdate == nil {
  1072  			return errors.Wrap(ErrInvalidTradingRewardsPendingPointsUpdate, "reward pending point update cannot be nil")
  1073  		}
  1074  
  1075  		_, err := sdk.AccAddressFromBech32(rewardPointUpdate.AccountAddress)
  1076  
  1077  		if err != nil {
  1078  			return errors.Wrap(sdkerrors.ErrInvalidAddress, rewardPointUpdate.AccountAddress)
  1079  		}
  1080  
  1081  		accountAddresses = append(accountAddresses, rewardPointUpdate.AccountAddress)
  1082  
  1083  		if rewardPointUpdate.NewPoints.IsNil() {
  1084  			return errors.Wrap(ErrInvalidTradingRewardsPendingPointsUpdate, "reward pending points cannot be nil")
  1085  		}
  1086  
  1087  		if rewardPointUpdate.NewPoints.IsNegative() {
  1088  			return errors.Wrap(ErrInvalidTradingRewardsPendingPointsUpdate, "reward pending points cannot be negative")
  1089  		}
  1090  	}
  1091  
  1092  	hasDuplicateAccountAddresses := HasDuplicates(accountAddresses)
  1093  	if hasDuplicateAccountAddresses {
  1094  		return errors.Wrap(ErrInvalidTradingRewardsPendingPointsUpdate, "account address cannot have duplicates")
  1095  	}
  1096  
  1097  	return govtypes.ValidateAbstract(p)
  1098  }
  1099  
  1100  // NewTradingRewardCampaignLaunchProposal returns new instance of TradingRewardCampaignLaunchProposal
  1101  func NewTradingRewardCampaignLaunchProposal(
  1102  	title, description string,
  1103  	campaignInfo *TradingRewardCampaignInfo,
  1104  	campaignRewardPools []*CampaignRewardPool,
  1105  ) *TradingRewardCampaignLaunchProposal {
  1106  	p := &TradingRewardCampaignLaunchProposal{
  1107  		Title:               title,
  1108  		Description:         description,
  1109  		CampaignInfo:        campaignInfo,
  1110  		CampaignRewardPools: campaignRewardPools,
  1111  	}
  1112  	if err := p.ValidateBasic(); err != nil {
  1113  		panic(err)
  1114  	}
  1115  	return p
  1116  }
  1117  
  1118  // Implements Proposal Interface
  1119  var _ govtypes.Content = &TradingRewardCampaignLaunchProposal{}
  1120  
  1121  // GetTitle returns the title of this proposal
  1122  func (p *TradingRewardCampaignLaunchProposal) GetTitle() string {
  1123  	return p.Title
  1124  }
  1125  
  1126  // GetDescription returns the description of this proposal
  1127  func (p *TradingRewardCampaignLaunchProposal) GetDescription() string {
  1128  	return p.Description
  1129  }
  1130  
  1131  // ProposalRoute returns router key of this proposal.
  1132  func (p *TradingRewardCampaignLaunchProposal) ProposalRoute() string { return RouterKey }
  1133  
  1134  // ProposalType returns proposal type of this proposal.
  1135  func (p *TradingRewardCampaignLaunchProposal) ProposalType() string {
  1136  	return ProposalTypeTradingRewardCampaign
  1137  }
  1138  
  1139  // ValidateBasic returns ValidateBasic result of this proposal.
  1140  func (p *TradingRewardCampaignLaunchProposal) ValidateBasic() error {
  1141  	var err error
  1142  
  1143  	if p.CampaignInfo == nil {
  1144  		return errors.Wrap(ErrInvalidTradingRewardCampaign, "new campaign info cannot be nil")
  1145  	}
  1146  
  1147  	if len(p.CampaignRewardPools) == 0 {
  1148  		return errors.Wrap(ErrInvalidTradingRewardCampaign, "new campaign reward pools cannot be nil")
  1149  	}
  1150  
  1151  	err = p.CampaignInfo.ValidateBasic()
  1152  	if err != nil {
  1153  		return err
  1154  	}
  1155  
  1156  	prevStartTimestamp := int64(0)
  1157  	for _, pool := range p.CampaignRewardPools {
  1158  		prevStartTimestamp, err = validateCampaignRewardPool(pool, p.CampaignInfo.CampaignDurationSeconds, prevStartTimestamp)
  1159  		if err != nil {
  1160  			return err
  1161  		}
  1162  	}
  1163  
  1164  	return nil
  1165  }
  1166  
  1167  func (t *TradingRewardCampaignBoostInfo) ValidateBasic() error {
  1168  	if len(t.BoostedSpotMarketIds) != len(t.SpotMarketMultipliers) {
  1169  		return errors.Wrap(ErrInvalidTradingRewardCampaign, "boosted spot market ids is not matching spot market multipliers")
  1170  	}
  1171  
  1172  	for _, marketID := range t.BoostedSpotMarketIds {
  1173  		if !IsHexHash(marketID) {
  1174  			return errors.Wrap(ErrMarketInvalid, marketID)
  1175  		}
  1176  	}
  1177  
  1178  	for _, marketID := range t.BoostedDerivativeMarketIds {
  1179  		if !IsHexHash(marketID) {
  1180  			return errors.Wrap(ErrMarketInvalid, marketID)
  1181  		}
  1182  	}
  1183  
  1184  	if len(t.BoostedDerivativeMarketIds) != len(t.DerivativeMarketMultipliers) {
  1185  		return errors.Wrap(ErrInvalidTradingRewardCampaign, "boosted derivative market ids is not matching derivative market multipliers")
  1186  	}
  1187  
  1188  	hasDuplicatesInMarkets := HasDuplicates(t.BoostedSpotMarketIds) || HasDuplicates(t.BoostedDerivativeMarketIds)
  1189  	if hasDuplicatesInMarkets {
  1190  		return errors.Wrap(ErrInvalidTradingRewardCampaign, "campaign contains duplicate boosted market ids")
  1191  	}
  1192  
  1193  	for _, multiplier := range t.SpotMarketMultipliers {
  1194  		if IsZeroOrNilDec(multiplier.MakerPointsMultiplier) {
  1195  			return errors.Wrap(ErrInvalidTradingRewardCampaign, "spot market maker multiplier cannot be zero or nil")
  1196  		}
  1197  
  1198  		if IsZeroOrNilDec(multiplier.TakerPointsMultiplier) {
  1199  			return errors.Wrap(ErrInvalidTradingRewardCampaign, "spot market taker multiplier cannot be zero or nil")
  1200  		}
  1201  
  1202  		if !SafeIsPositiveDec(multiplier.MakerPointsMultiplier) {
  1203  			return errors.Wrap(ErrInvalidTradingRewardCampaign, "spot market maker multiplier cannot be negative")
  1204  		}
  1205  
  1206  		if !SafeIsPositiveDec(multiplier.TakerPointsMultiplier) {
  1207  			return errors.Wrap(ErrInvalidTradingRewardCampaign, "spot market taker multiplier cannot be negative")
  1208  		}
  1209  	}
  1210  
  1211  	for _, multiplier := range t.DerivativeMarketMultipliers {
  1212  		if IsZeroOrNilDec(multiplier.MakerPointsMultiplier) {
  1213  			return errors.Wrap(ErrInvalidTradingRewardCampaign, "derivative market maker multiplier cannot be zero or nil")
  1214  		}
  1215  
  1216  		if IsZeroOrNilDec(multiplier.TakerPointsMultiplier) {
  1217  			return errors.Wrap(ErrInvalidTradingRewardCampaign, "derivative market taker multiplier cannot be zero or nil")
  1218  		}
  1219  
  1220  		if !SafeIsPositiveDec(multiplier.MakerPointsMultiplier) {
  1221  			return errors.Wrap(ErrInvalidTradingRewardCampaign, "derivative market maker multiplier cannot be negative")
  1222  		}
  1223  
  1224  		if !SafeIsPositiveDec(multiplier.TakerPointsMultiplier) {
  1225  			return errors.Wrap(ErrInvalidTradingRewardCampaign, "derivative market taker multiplier cannot be negative")
  1226  		}
  1227  	}
  1228  	return nil
  1229  }
  1230  
  1231  func (c *TradingRewardCampaignInfo) ValidateBasic() error {
  1232  	if c == nil {
  1233  		return errors.Wrap(ErrInvalidTradingRewardCampaign, "campaign info cannot be nil")
  1234  	}
  1235  
  1236  	if c.CampaignDurationSeconds <= 0 {
  1237  		return errors.Wrap(ErrInvalidTradingRewardCampaign, "campaign duration cannot be zero")
  1238  	}
  1239  
  1240  	if len(c.QuoteDenoms) == 0 {
  1241  		return errors.Wrap(ErrInvalidTradingRewardCampaign, "campaign quote denoms cannot be nil")
  1242  	}
  1243  
  1244  	hasTradingRewardBoostInfoDefined := c != nil && c.TradingRewardBoostInfo != nil
  1245  	if hasTradingRewardBoostInfoDefined {
  1246  		if err := c.TradingRewardBoostInfo.ValidateBasic(); err != nil {
  1247  			return err
  1248  		}
  1249  	}
  1250  
  1251  	for _, marketID := range c.DisqualifiedMarketIds {
  1252  		if !IsHexHash(marketID) {
  1253  			return errors.Wrap(ErrMarketInvalid, marketID)
  1254  		}
  1255  	}
  1256  
  1257  	hasDuplicatesInDisqualifiedMarkets := c != nil && HasDuplicates(c.DisqualifiedMarketIds)
  1258  	if hasDuplicatesInDisqualifiedMarkets {
  1259  		return errors.Wrap(ErrInvalidTradingRewardCampaign, "campaign contains duplicate disqualified market ids")
  1260  	}
  1261  
  1262  	return nil
  1263  }
  1264  
  1265  func validateCampaignRewardPool(pool *CampaignRewardPool, campaignDurationSeconds, prevStartTimestamp int64) (int64, error) {
  1266  	if pool == nil {
  1267  		return 0, errors.Wrap(ErrInvalidTradingRewardCampaign, "new campaign reward pool cannot be nil")
  1268  	}
  1269  
  1270  	if pool.StartTimestamp <= prevStartTimestamp {
  1271  		return 0, errors.Wrap(ErrInvalidTradingRewardCampaign, "reward pool start timestamps must be in ascending order")
  1272  	}
  1273  
  1274  	hasValidStartTimestamp := prevStartTimestamp == 0 || pool.StartTimestamp == (prevStartTimestamp+campaignDurationSeconds)
  1275  	if !hasValidStartTimestamp {
  1276  		return 0, errors.Wrap(ErrInvalidTradingRewardCampaign, "start timestamps not matching campaign duration")
  1277  	}
  1278  
  1279  	prevStartTimestamp = pool.StartTimestamp
  1280  
  1281  	hasDuplicatesInEpochRewards := HasDuplicatesCoin(pool.MaxCampaignRewards)
  1282  	if hasDuplicatesInEpochRewards {
  1283  		return 0, errors.Wrap(ErrInvalidTradingRewardCampaign, "reward pool campaign contains duplicate market coins")
  1284  	}
  1285  
  1286  	for _, epochRewardDenom := range pool.MaxCampaignRewards {
  1287  		if !epochRewardDenom.IsValid() {
  1288  			return 0, errors.Wrap(sdkerrors.ErrInvalidCoins, epochRewardDenom.String())
  1289  		}
  1290  
  1291  		if IsZeroOrNilInt(epochRewardDenom.Amount) {
  1292  			return 0, errors.Wrap(ErrInvalidTradingRewardCampaign, "reward pool contains zero or nil reward amount")
  1293  		}
  1294  	}
  1295  
  1296  	return prevStartTimestamp, nil
  1297  }
  1298  
  1299  // NewFeeDiscountProposal returns new instance of FeeDiscountProposal
  1300  func NewFeeDiscountProposal(title, description string, schedule *FeeDiscountSchedule) *FeeDiscountProposal {
  1301  	return &FeeDiscountProposal{
  1302  		Title:       title,
  1303  		Description: description,
  1304  		Schedule:    schedule,
  1305  	}
  1306  }
  1307  
  1308  // Implements Proposal Interface
  1309  var _ govtypes.Content = &FeeDiscountProposal{}
  1310  
  1311  // GetTitle returns the title of this proposal
  1312  func (p *FeeDiscountProposal) GetTitle() string {
  1313  	return p.Title
  1314  }
  1315  
  1316  // GetDescription returns the description of this proposal
  1317  func (p *FeeDiscountProposal) GetDescription() string {
  1318  	return p.Description
  1319  }
  1320  
  1321  // ProposalRoute returns router key of this proposal.
  1322  func (p *FeeDiscountProposal) ProposalRoute() string { return RouterKey }
  1323  
  1324  // ProposalType returns proposal type of this proposal.
  1325  func (p *FeeDiscountProposal) ProposalType() string {
  1326  	return ProposalTypeFeeDiscountProposal
  1327  }
  1328  
  1329  // ValidateBasic returns ValidateBasic result of this proposal.
  1330  func (p *FeeDiscountProposal) ValidateBasic() error {
  1331  	if p.Schedule == nil {
  1332  		return errors.Wrap(ErrInvalidFeeDiscountSchedule, "new fee discount schedule cannot be nil")
  1333  	}
  1334  
  1335  	if p.Schedule.BucketCount < 2 {
  1336  		return errors.Wrap(ErrInvalidFeeDiscountSchedule, "new fee discount schedule must have at least 2 buckets")
  1337  	}
  1338  
  1339  	if p.Schedule.BucketDuration < 10 {
  1340  		return errors.Wrap(ErrInvalidFeeDiscountSchedule, "new fee discount schedule must have have bucket durations of at least 10 seconds")
  1341  	}
  1342  
  1343  	if HasDuplicates(p.Schedule.QuoteDenoms) {
  1344  		return errors.Wrap(ErrInvalidFeeDiscountSchedule, "new fee discount schedule cannot have duplicate quote denoms")
  1345  	}
  1346  
  1347  	for _, marketID := range p.Schedule.DisqualifiedMarketIds {
  1348  		if !IsHexHash(marketID) {
  1349  			return errors.Wrap(ErrMarketInvalid, marketID)
  1350  		}
  1351  	}
  1352  
  1353  	if HasDuplicates(p.Schedule.DisqualifiedMarketIds) {
  1354  		return errors.Wrap(ErrInvalidFeeDiscountSchedule, "new fee discount schedule cannot have duplicate disqualified market ids")
  1355  	}
  1356  
  1357  	if len(p.Schedule.TierInfos) < 1 {
  1358  		return errors.Wrap(ErrInvalidFeeDiscountSchedule, "new fee discount schedule must have at least one discount tier")
  1359  	}
  1360  
  1361  	for idx, tierInfo := range p.Schedule.TierInfos {
  1362  		if err := tierInfo.ValidateBasic(); err != nil {
  1363  			return err
  1364  		}
  1365  
  1366  		if idx > 0 {
  1367  			prevTierInfo := p.Schedule.TierInfos[idx-1]
  1368  
  1369  			if prevTierInfo.MakerDiscountRate.GT(tierInfo.MakerDiscountRate) {
  1370  				return errors.Wrap(ErrInvalidFeeDiscountSchedule, "successive MakerDiscountRates must be equal or larger than those of lower tiers")
  1371  			}
  1372  
  1373  			if prevTierInfo.TakerDiscountRate.GT(tierInfo.TakerDiscountRate) {
  1374  				return errors.Wrap(ErrInvalidFeeDiscountSchedule, "successive TakerDiscountRates must be equal or larger than those of lower tiers")
  1375  			}
  1376  
  1377  			if prevTierInfo.StakedAmount.GT(tierInfo.StakedAmount) {
  1378  				return errors.Wrap(ErrInvalidFeeDiscountSchedule, "successive StakedAmount must be equal or larger than those of lower tiers")
  1379  			}
  1380  
  1381  			if prevTierInfo.Volume.GT(tierInfo.Volume) {
  1382  				return errors.Wrap(ErrInvalidFeeDiscountSchedule, "successive Volume must be equal or larger than those of lower tiers")
  1383  			}
  1384  		}
  1385  	}
  1386  
  1387  	return govtypes.ValidateAbstract(p)
  1388  }
  1389  
  1390  func (t *FeeDiscountTierInfo) ValidateBasic() error {
  1391  	if !SafeIsNonNegativeDec(t.MakerDiscountRate) || t.MakerDiscountRate.GT(math.LegacyOneDec()) {
  1392  		return errors.Wrap(ErrInvalidFeeDiscountSchedule, "MakerDiscountRate must be between 0 and 1")
  1393  	}
  1394  
  1395  	if !SafeIsNonNegativeDec(t.TakerDiscountRate) || t.TakerDiscountRate.GT(math.LegacyOneDec()) {
  1396  		return errors.Wrap(ErrInvalidFeeDiscountSchedule, "TakerDiscountRate must be between 0 and 1")
  1397  	}
  1398  
  1399  	if !SafeIsPositiveInt(t.StakedAmount) {
  1400  		return errors.Wrap(ErrInvalidFeeDiscountSchedule, "StakedAmount must be non-negative")
  1401  	}
  1402  
  1403  	if !SafeIsPositiveDec(t.Volume) {
  1404  		return errors.Wrap(ErrInvalidFeeDiscountSchedule, "Volume must be non-negative")
  1405  	}
  1406  	return nil
  1407  }
  1408  
  1409  // Implements Proposal Interface
  1410  var _ govtypes.Content = &BatchCommunityPoolSpendProposal{}
  1411  
  1412  // GetTitle returns the title of this proposal.
  1413  func (p *BatchCommunityPoolSpendProposal) GetTitle() string {
  1414  	return p.Title
  1415  }
  1416  
  1417  // GetDescription returns the description of this proposal.
  1418  func (p *BatchCommunityPoolSpendProposal) GetDescription() string {
  1419  	return p.Description
  1420  }
  1421  
  1422  // ProposalRoute returns router key of this proposal.
  1423  func (p *BatchCommunityPoolSpendProposal) ProposalRoute() string { return RouterKey }
  1424  
  1425  // ProposalType returns proposal type of this proposal.
  1426  func (p *BatchCommunityPoolSpendProposal) ProposalType() string {
  1427  	return ProposalTypeBatchCommunityPoolSpendProposal
  1428  }
  1429  
  1430  // ValidateBasic returns ValidateBasic result of this proposal.
  1431  func (p *BatchCommunityPoolSpendProposal) ValidateBasic() error {
  1432  	for _, proposal := range p.Proposals {
  1433  		if err := proposal.ValidateBasic(); err != nil {
  1434  			return err
  1435  		}
  1436  	}
  1437  	return govtypes.ValidateAbstract(p)
  1438  }
  1439  
  1440  // NewBinaryOptionsMarketLaunchProposal returns new instance of BinaryOptionsMarketLaunchProposal
  1441  func NewBinaryOptionsMarketLaunchProposal(
  1442  	title, description, ticker, oracleSymbol, oracleProvider string,
  1443  	oracleType oracletypes.OracleType, oracleScaleFactor uint32,
  1444  	expirationTimestamp, settlementTimestamp int64,
  1445  	admin, quoteDenom string,
  1446  	makerFeeRate, takerFeeRate, minPriceTickSize, minQuantityTickSize, minNotional math.LegacyDec,
  1447  
  1448  ) *BinaryOptionsMarketLaunchProposal {
  1449  	return &BinaryOptionsMarketLaunchProposal{
  1450  		Title:               title,
  1451  		Description:         description,
  1452  		Ticker:              ticker,
  1453  		OracleSymbol:        oracleSymbol,
  1454  		OracleProvider:      oracleProvider,
  1455  		OracleType:          oracleType,
  1456  		OracleScaleFactor:   oracleScaleFactor,
  1457  		ExpirationTimestamp: expirationTimestamp,
  1458  		SettlementTimestamp: settlementTimestamp,
  1459  		Admin:               admin,
  1460  		QuoteDenom:          quoteDenom,
  1461  		MakerFeeRate:        makerFeeRate,
  1462  		TakerFeeRate:        takerFeeRate,
  1463  		MinPriceTickSize:    minPriceTickSize,
  1464  		MinQuantityTickSize: minQuantityTickSize,
  1465  		MinNotional:         minNotional,
  1466  	}
  1467  }
  1468  
  1469  // Implements Proposal Interface
  1470  var _ govtypes.Content = &BinaryOptionsMarketLaunchProposal{}
  1471  
  1472  // GetTitle returns the title of this proposal.
  1473  func (p *BinaryOptionsMarketLaunchProposal) GetTitle() string {
  1474  	return p.Title
  1475  }
  1476  
  1477  // GetDescription returns the description of this proposal.
  1478  func (p *BinaryOptionsMarketLaunchProposal) GetDescription() string {
  1479  	return p.Description
  1480  }
  1481  
  1482  // ProposalRoute returns router key of this proposal.
  1483  func (p *BinaryOptionsMarketLaunchProposal) ProposalRoute() string { return RouterKey }
  1484  
  1485  // ProposalType returns proposal type of this proposal.
  1486  func (p *BinaryOptionsMarketLaunchProposal) ProposalType() string {
  1487  	return ProposalTypeBinaryOptionsMarketLaunch
  1488  }
  1489  
  1490  // ValidateBasic returns ValidateBasic result of a perpetual market launch proposal.
  1491  func (p *BinaryOptionsMarketLaunchProposal) ValidateBasic() error {
  1492  	if p.Ticker == "" || len(p.Ticker) > MaxTickerLength {
  1493  		return errors.Wrapf(ErrInvalidTicker, "ticker should not be empty or exceed %d characters", MaxTickerLength)
  1494  	}
  1495  	if p.OracleSymbol == "" {
  1496  		return errors.Wrap(ErrInvalidOracle, "oracle symbol should not be empty")
  1497  	}
  1498  	if p.OracleProvider == "" {
  1499  		return errors.Wrap(ErrInvalidOracle, "oracle provider should not be empty")
  1500  	}
  1501  	if p.OracleType != oracletypes.OracleType_Provider {
  1502  		return errors.Wrap(ErrInvalidOracleType, p.OracleType.String())
  1503  	}
  1504  	if p.OracleScaleFactor > MaxOracleScaleFactor {
  1505  		return ErrExceedsMaxOracleScaleFactor
  1506  	}
  1507  
  1508  	if p.ExpirationTimestamp >= p.SettlementTimestamp || p.ExpirationTimestamp < 0 || p.SettlementTimestamp < 0 {
  1509  		return ErrInvalidExpiry
  1510  	}
  1511  
  1512  	if p.Admin != "" {
  1513  		_, err := sdk.AccAddressFromBech32(p.Admin)
  1514  		if err != nil {
  1515  			return errors.Wrap(sdkerrors.ErrInvalidAddress, p.Admin)
  1516  		}
  1517  	}
  1518  	if p.QuoteDenom == "" {
  1519  		return errors.Wrap(ErrInvalidQuoteDenom, "quote denom should not be empty")
  1520  	}
  1521  	if err := ValidateMakerFee(p.MakerFeeRate); err != nil {
  1522  		return err
  1523  	}
  1524  	if err := ValidateFee(p.TakerFeeRate); err != nil {
  1525  		return err
  1526  	}
  1527  
  1528  	if p.MakerFeeRate.GT(p.TakerFeeRate) {
  1529  		return ErrFeeRatesRelation
  1530  	}
  1531  
  1532  	if err := ValidateTickSize(p.MinPriceTickSize); err != nil {
  1533  		return errors.Wrap(ErrInvalidPriceTickSize, err.Error())
  1534  	}
  1535  	if err := ValidateTickSize(p.MinQuantityTickSize); err != nil {
  1536  		return errors.Wrap(ErrInvalidQuantityTickSize, err.Error())
  1537  	}
  1538  	if err := ValidateMinNotional(p.MinNotional); err != nil {
  1539  		return errors.Wrap(ErrInvalidNotional, err.Error())
  1540  	}
  1541  
  1542  	return govtypes.ValidateAbstract(p)
  1543  }
  1544  
  1545  // NewBinaryOptionsMarketParamUpdateProposal returns new instance of BinaryOptionsMarketParamUpdateProposal
  1546  func NewBinaryOptionsMarketParamUpdateProposal(
  1547  	title string,
  1548  	description string,
  1549  	marketID string,
  1550  	makerFeeRate, takerFeeRate, relayerFeeShareRate, minPriceTickSize, minQuantityTickSize, minNotional *math.LegacyDec,
  1551  	expirationTimestamp, settlementTimestamp int64,
  1552  	admin string,
  1553  	status MarketStatus,
  1554  	oracleParams *ProviderOracleParams,
  1555  	ticker string,
  1556  ) *BinaryOptionsMarketParamUpdateProposal {
  1557  	return &BinaryOptionsMarketParamUpdateProposal{
  1558  		Title:               title,
  1559  		Description:         description,
  1560  		MarketId:            marketID,
  1561  		MakerFeeRate:        makerFeeRate,
  1562  		TakerFeeRate:        takerFeeRate,
  1563  		RelayerFeeShareRate: relayerFeeShareRate,
  1564  		MinPriceTickSize:    minPriceTickSize,
  1565  		MinQuantityTickSize: minQuantityTickSize,
  1566  		MinNotional:         minNotional,
  1567  		ExpirationTimestamp: expirationTimestamp,
  1568  		SettlementTimestamp: settlementTimestamp,
  1569  		Admin:               admin,
  1570  		Status:              status,
  1571  		OracleParams:        oracleParams,
  1572  		Ticker:              ticker,
  1573  	}
  1574  }
  1575  
  1576  // Implements Proposal Interface
  1577  var _ govtypes.Content = &BinaryOptionsMarketParamUpdateProposal{}
  1578  
  1579  // GetTitle returns the title of this proposal
  1580  func (p *BinaryOptionsMarketParamUpdateProposal) GetTitle() string {
  1581  	return p.Title
  1582  }
  1583  
  1584  // GetDescription returns the description of this proposal
  1585  func (p *BinaryOptionsMarketParamUpdateProposal) GetDescription() string {
  1586  	return p.Description
  1587  }
  1588  
  1589  // ProposalRoute returns router key of this proposal.
  1590  func (p *BinaryOptionsMarketParamUpdateProposal) ProposalRoute() string { return RouterKey }
  1591  
  1592  // ProposalType returns proposal type of this proposal.
  1593  func (p *BinaryOptionsMarketParamUpdateProposal) ProposalType() string {
  1594  	return ProposalTypeBinaryOptionsMarketParamUpdate
  1595  }
  1596  
  1597  // ValidateBasic returns ValidateBasic result of this proposal.
  1598  func (p *BinaryOptionsMarketParamUpdateProposal) ValidateBasic() error {
  1599  	if !IsHexHash(p.MarketId) {
  1600  		return errors.Wrap(ErrMarketInvalid, p.MarketId)
  1601  	}
  1602  	if p.MakerFeeRate == nil &&
  1603  		p.TakerFeeRate == nil &&
  1604  		p.RelayerFeeShareRate == nil &&
  1605  		p.MinPriceTickSize == nil &&
  1606  		p.MinQuantityTickSize == nil &&
  1607  		p.MinNotional == nil &&
  1608  		p.Status == MarketStatus_Unspecified &&
  1609  		p.ExpirationTimestamp == 0 &&
  1610  		p.SettlementTimestamp == 0 &&
  1611  		p.SettlementPrice == nil &&
  1612  		p.Admin == "" &&
  1613  		p.OracleParams == nil {
  1614  		return errors.Wrap(gov.ErrInvalidProposalContent, "At least one field should not be nil")
  1615  	}
  1616  
  1617  	if p.MakerFeeRate != nil {
  1618  		if err := ValidateMakerFee(*p.MakerFeeRate); err != nil {
  1619  			return err
  1620  		}
  1621  	}
  1622  	if p.TakerFeeRate != nil {
  1623  		if err := ValidateFee(*p.TakerFeeRate); err != nil {
  1624  			return err
  1625  		}
  1626  	}
  1627  
  1628  	if p.RelayerFeeShareRate != nil {
  1629  		if err := ValidateFee(*p.RelayerFeeShareRate); err != nil {
  1630  			return err
  1631  		}
  1632  	}
  1633  
  1634  	if p.MinPriceTickSize != nil {
  1635  		if err := ValidateTickSize(*p.MinPriceTickSize); err != nil {
  1636  			return errors.Wrap(ErrInvalidPriceTickSize, err.Error())
  1637  		}
  1638  	}
  1639  
  1640  	if p.MinQuantityTickSize != nil {
  1641  		if err := ValidateTickSize(*p.MinQuantityTickSize); err != nil {
  1642  			return errors.Wrap(ErrInvalidQuantityTickSize, err.Error())
  1643  		}
  1644  	}
  1645  
  1646  	if p.MinNotional != nil {
  1647  		if err := ValidateMinNotional(*p.MinNotional); err != nil {
  1648  			return errors.Wrap(ErrInvalidNotional, err.Error())
  1649  		}
  1650  	}
  1651  
  1652  	if p.ExpirationTimestamp != 0 && p.SettlementTimestamp != 0 {
  1653  		if p.ExpirationTimestamp >= p.SettlementTimestamp || p.ExpirationTimestamp < 0 || p.SettlementTimestamp < 0 {
  1654  			return ErrInvalidExpiry
  1655  		}
  1656  	}
  1657  
  1658  	if p.SettlementTimestamp < 0 {
  1659  		return ErrInvalidSettlement
  1660  	}
  1661  
  1662  	if p.Admin != "" {
  1663  		if _, err := sdk.AccAddressFromBech32(p.Admin); err != nil {
  1664  			return err
  1665  		}
  1666  	}
  1667  
  1668  	if len(p.Ticker) > MaxTickerLength {
  1669  		return errors.Wrapf(ErrInvalidTicker, "ticker should not exceed %d characters", MaxTickerLength)
  1670  	}
  1671  
  1672  	// price is either nil (not set), -1 (demolish with refund) or [0..1] (demolish with settle)
  1673  	switch {
  1674  	case p.SettlementPrice == nil,
  1675  		p.SettlementPrice.IsNil():
  1676  		// ok
  1677  	case p.SettlementPrice.Equal(BinaryOptionsMarketRefundFlagPrice),
  1678  		p.SettlementPrice.GTE(math.LegacyZeroDec()) && p.SettlementPrice.LTE(MaxBinaryOptionsOrderPrice):
  1679  		if p.Status != MarketStatus_Demolished {
  1680  			return errors.Wrapf(ErrInvalidMarketStatus, "status should be set to demolished when the settlement price is set, status: %s", p.Status.String())
  1681  		}
  1682  		// ok
  1683  	default:
  1684  		return errors.Wrap(ErrInvalidPrice, p.SettlementPrice.String())
  1685  	}
  1686  
  1687  	switch p.Status {
  1688  	case
  1689  		MarketStatus_Unspecified,
  1690  		MarketStatus_Demolished:
  1691  	default:
  1692  		return errors.Wrap(ErrInvalidMarketStatus, p.Status.String())
  1693  	}
  1694  
  1695  	if p.OracleParams != nil {
  1696  		if err := p.OracleParams.ValidateBasic(); err != nil {
  1697  			return err
  1698  		}
  1699  	}
  1700  
  1701  	return govtypes.ValidateAbstract(p)
  1702  }
  1703  
  1704  // Implements Proposal Interface
  1705  var _ govtypes.Content = &AtomicMarketOrderFeeMultiplierScheduleProposal{}
  1706  
  1707  // GetTitle returns the title of this proposal
  1708  func (p *AtomicMarketOrderFeeMultiplierScheduleProposal) GetTitle() string {
  1709  	return p.Title
  1710  }
  1711  
  1712  // GetDescription returns the description of this proposal
  1713  func (p *AtomicMarketOrderFeeMultiplierScheduleProposal) GetDescription() string {
  1714  	return p.Description
  1715  }
  1716  
  1717  // ProposalRoute returns router key of this proposal.
  1718  func (p *AtomicMarketOrderFeeMultiplierScheduleProposal) ProposalRoute() string { return RouterKey }
  1719  
  1720  // ProposalType returns proposal type of this proposal.
  1721  func (p *AtomicMarketOrderFeeMultiplierScheduleProposal) ProposalType() string {
  1722  	return ProposalAtomicMarketOrderFeeMultiplierSchedule
  1723  }
  1724  
  1725  func (p *AtomicMarketOrderFeeMultiplierScheduleProposal) ValidateBasic() error {
  1726  	if len(p.MarketFeeMultipliers) == 0 {
  1727  		return errors.Wrap(gov.ErrInvalidProposalContent, "At least one fee multiplier should be provided")
  1728  	}
  1729  	for _, m := range p.MarketFeeMultipliers {
  1730  		if !IsHexHash(m.MarketId) {
  1731  			return errors.Wrap(ErrMarketInvalid, m.MarketId)
  1732  		}
  1733  		multiplier := m.FeeMultiplier
  1734  		if multiplier.IsNil() {
  1735  			return fmt.Errorf("atomic taker fee multiplier cannot be nil: %v", multiplier)
  1736  		}
  1737  
  1738  		if multiplier.LT(math.LegacyOneDec()) {
  1739  			return fmt.Errorf("atomic taker fee multiplier cannot be less than 1: %v", multiplier)
  1740  		}
  1741  
  1742  		if multiplier.GT(MaxFeeMultiplier) {
  1743  			return fmt.Errorf("atomicMarketOrderFeeMultiplier cannot be bigger than %v: %v", multiplier, MaxFeeMultiplier)
  1744  		}
  1745  	}
  1746  	return govtypes.ValidateAbstract(p)
  1747  }