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 }