Skip to content

Commit

Permalink
Implement close markets early feature (#1127)
Browse files Browse the repository at this point in the history
* wip

* add rejected to match

* wip

* simplify early closing

* fmt

* write tests

* add comment

* wip

* fix major clippy issues

* wip

* wip

* wip

* update doc comments for txs

* fix migration

* document things

* fmt

* Update primitives/src/constants/mock.rs

Co-authored-by: Malte Kliemann <mail@maltekliemann.com>

* Update primitives/src/constants/mock.rs

Co-authored-by: Malte Kliemann <mail@maltekliemann.com>

* correct formatting

* use if let some instead of match

* make pm AC agnostic

* simplify origin checks

* fix clippy

* remove traling comma

* fix test duration

* improve changelogs for devs

* add test

* add early to config parameters

* rename premature to early

* fmt

* reorder errors

* use logging instead of just debug_assert

* remove backticks

* adjust battery station parameters early close

* fix parameters

* extend max_encoded_len

* Update zrml/prediction-markets/src/lib.rs

Co-authored-by: Harald Heckmann <mail@haraldheckmann.de>

* fix copy mistake

* adjust bond parameters

* fix after merge

---------

Co-authored-by: Malte Kliemann <mail@maltekliemann.com>
Co-authored-by: Harald Heckmann <mail@haraldheckmann.de>
  • Loading branch information
3 people authored Oct 26, 2023
1 parent 802600f commit c87ca64
Show file tree
Hide file tree
Showing 26 changed files with 1,913 additions and 76 deletions.
12 changes: 12 additions & 0 deletions docs/changelog_for_devs.md
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ APIs/RPC interface.

## v0.4.2

[#1127]: https://github.com/zeitgeistpm/zeitgeist/pull/1127
[#1148]: https://github.com/zeitgeistpm/zeitgeist/pull/1148
[#1138]: https://github.com/zeitgeistpm/zeitgeist/pull/1138

Expand All @@ -37,6 +38,17 @@ APIs/RPC interface.

For details, please refer to the `README.md` and the in-file documentation.

### Added

- Add extrinsics to zrml-prediction-markets ([#1127]):
- `schedule_early_close`: Schedule an early close of a market.
- `dispute_early_close`: Dispute a scheduled early close of a market.
- `reject_early_close`: Reject a scheduled early close of a market.
- Add events to zrml-prediction-markets ([#1127]):
- `MarketEarlyCloseScheduled`: A market's early close was scheduled.
- `MarketEarlyCloseDisputed`: A market's early close was disputed.
- `MarketEarlyCloseRejected`: A market's early close was rejected.

### Changed

- Modify event `MarketDisputed` to have an additional field for the disputant.
Expand Down
8 changes: 7 additions & 1 deletion primitives/src/constants/mock.rs
Original file line number Diff line number Diff line change
Expand Up @@ -80,6 +80,10 @@ parameter_types! {
// Prediction Market parameters
parameter_types! {
pub const AdvisoryBond: Balance = 25 * CENT;
pub const CloseEarlyProtectionTimeFramePeriod: Moment = 3 * MILLISECS_PER_BLOCK as u64;
pub const CloseEarlyProtectionBlockPeriod: BlockNumber = 3;
pub const CloseEarlyRequestBond: Balance = 5 * BASE;
pub const CloseEarlyDisputeBond: Balance = 10 * BASE;
pub const DisputeBond: Balance = 5 * BASE;
pub const DisputeFactor: Balance = 2 * BASE;
pub const MaxCategories: u16 = 10;
Expand All @@ -88,7 +92,7 @@ parameter_types! {
pub const MaxDisputes: u16 = 6;
pub const MaxEditReasonLen: u32 = 1024;
pub const MaxGracePeriod: BlockNumber = 20;
pub const MaxMarketLifetime: BlockNumber = 1_000_000;
pub const MaxMarketLifetime: BlockNumber = 100_000_000_000;
pub const MaxOracleDuration: BlockNumber = 30;
pub const MaxRejectReasonLen: u32 = 1024;
// 2_678_400_000 = 31 days.
Expand All @@ -101,6 +105,8 @@ parameter_types! {
pub const OracleBond: Balance = 50 * CENT;
pub const OutsiderBond: Balance = 2 * OracleBond::get();
pub const PmPalletId: PalletId = PalletId(*b"zge/pred");
pub const CloseEarlyBlockPeriod: BlockNumber = 6;
pub const CloseEarlyTimeFramePeriod: Moment = 6 * MILLISECS_PER_BLOCK as u64;
pub const ValidityBond: Balance = 50 * CENT;
}

Expand Down
32 changes: 31 additions & 1 deletion primitives/src/market.rs
Original file line number Diff line number Diff line change
Expand Up @@ -64,6 +64,8 @@ pub struct Market<AI, BA, BN, M, A> {
pub dispute_mechanism: Option<MarketDisputeMechanism>,
/// The bonds reserved for this market.
pub bonds: MarketBonds<AI, BA>,
/// The time at which the market was closed early.
pub early_close: Option<EarlyClose<BN, M>>,
}

impl<AI, BA, BN, M, A> Market<AI, BA, BN, M, A> {
Expand Down Expand Up @@ -106,6 +108,8 @@ pub struct MarketBonds<AI, BA> {
pub oracle: Option<Bond<AI, BA>>,
pub outsider: Option<Bond<AI, BA>>,
pub dispute: Option<Bond<AI, BA>>,
pub close_request: Option<Bond<AI, BA>>,
pub close_dispute: Option<Bond<AI, BA>>,
}

impl<AI: Ord, BA: frame_support::traits::tokens::Balance> MarketBonds<AI, BA> {
Expand All @@ -119,13 +123,22 @@ impl<AI: Ord, BA: frame_support::traits::tokens::Balance> MarketBonds<AI, BA> {
.saturating_add(value_or_default(&self.oracle))
.saturating_add(value_or_default(&self.outsider))
.saturating_add(value_or_default(&self.dispute))
.saturating_add(value_or_default(&self.close_request))
.saturating_add(value_or_default(&self.close_dispute))
}
}

// Used primarily for testing purposes.
impl<AI, BA> Default for MarketBonds<AI, BA> {
fn default() -> Self {
MarketBonds { creation: None, oracle: None, outsider: None, dispute: None }
MarketBonds {
creation: None,
oracle: None,
outsider: None,
dispute: None,
close_request: None,
close_dispute: None,
}
}
}

Expand Down Expand Up @@ -180,6 +193,7 @@ where
.saturating_add(<Option<OutcomeReport>>::max_encoded_len())
.saturating_add(<Option<MarketDisputeMechanism>>::max_encoded_len())
.saturating_add(<MarketBonds<AI, BA>>::max_encoded_len())
.saturating_add(<Option<EarlyClose<BN, M>>>::max_encoded_len())
}
}

Expand Down Expand Up @@ -255,6 +269,21 @@ impl<BN: MaxEncodedLen, M: MaxEncodedLen> MaxEncodedLen for MarketPeriod<BN, M>
}
}

#[derive(Clone, Decode, Encode, Eq, MaxEncodedLen, PartialEq, RuntimeDebug, TypeInfo)]
pub struct EarlyClose<BN, M> {
pub old: MarketPeriod<BN, M>,
pub new: MarketPeriod<BN, M>,
pub state: EarlyCloseState,
}

#[derive(Clone, Decode, Encode, Eq, PartialEq, MaxEncodedLen, RuntimeDebug, TypeInfo)]
pub enum EarlyCloseState {
ScheduledAsMarketCreator,
ScheduledAsOther,
Disputed,
Rejected,
}

/// Defines deadlines for market.
#[derive(
Clone, Copy, Decode, Default, Encode, Eq, MaxEncodedLen, PartialEq, RuntimeDebug, TypeInfo,
Expand Down Expand Up @@ -416,6 +445,7 @@ mod tests {
resolved_outcome: None,
dispute_mechanism: Some(MarketDisputeMechanism::Authorized),
bonds: MarketBonds::default(),
early_close: None,
};
assert_eq!(market.matches_outcome_report(&outcome_report), expected);
}
Expand Down
16 changes: 16 additions & 0 deletions runtime/battery-station/src/parameters.rs
Original file line number Diff line number Diff line change
Expand Up @@ -203,6 +203,16 @@ parameter_types! {
pub const AdvisoryBond: Balance = 25 * CENT;
/// The percentage of the advisory bond that gets slashed when a market is rejected.
pub const AdvisoryBondSlashPercentage: Percent = Percent::from_percent(0);
/// (Slashable) Bond that is provided for disputing an early market close by the market creator.
pub const CloseEarlyDisputeBond: Balance = BASE;
// Fat-finger protection for the advisory committe to reject
// the early market schedule.
pub const CloseEarlyProtectionTimeFramePeriod: Moment = CloseEarlyProtectionBlockPeriod::get() * MILLISECS_PER_BLOCK as u64;
// Fat-finger protection for the advisory committe to reject
// the early market schedule.
pub const CloseEarlyProtectionBlockPeriod: BlockNumber = BLOCKS_PER_HOUR;
/// (Slashable) Bond that is provided for scheduling an early market close.
pub const CloseEarlyRequestBond: Balance = BASE;
/// (Slashable) Bond that is provided for disputing the outcome.
/// Unreserved in case the dispute was justified otherwise slashed.
/// This is when the resolved outcome is different to the default (reported) outcome.
Expand Down Expand Up @@ -248,6 +258,12 @@ parameter_types! {
pub const OutsiderBond: Balance = 2 * OracleBond::get();
/// Pallet identifier, mainly used for named balance reserves.
pub const PmPalletId: PalletId = PM_PALLET_ID;
// Waiting time for market creator to close
// the market after an early close schedule.
pub const CloseEarlyBlockPeriod: BlockNumber = BLOCKS_PER_HOUR;
// Waiting time for market creator to close
// the market after an early close schedule.
pub const CloseEarlyTimeFramePeriod: Moment = CloseEarlyBlockPeriod::get() * MILLISECS_PER_BLOCK as u64;
/// (Slashable) A bond for creation markets that do not require approval. Slashed in case
/// the market is forcefully destroyed.
pub const ValidityBond: Balance = 50 * CENT;
Expand Down
11 changes: 9 additions & 2 deletions runtime/common/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -61,10 +61,10 @@ macro_rules! decl_common_types {
type Address = sp_runtime::MultiAddress<AccountId, ()>;

#[cfg(feature = "parachain")]
type Migrations = (zrml_prediction_markets::migrations::MigrateMarkets<Runtime>,);
type Migrations = (zrml_prediction_markets::migrations::AddEarlyCloseBonds<Runtime>,);

#[cfg(not(feature = "parachain"))]
type Migrations = (zrml_prediction_markets::migrations::MigrateMarkets<Runtime>,);
type Migrations = (zrml_prediction_markets::migrations::AddEarlyCloseBonds<Runtime>,);

pub type Executive = frame_executive::Executive<
Runtime,
Expand Down Expand Up @@ -1130,7 +1130,12 @@ macro_rules! impl_config_traits {
type Authorized = Authorized;
type Currency = Balances;
type Court = Court;
type CloseEarlyDisputeBond = CloseEarlyDisputeBond;
type CloseMarketEarlyOrigin = EnsureRootOrMoreThanOneThirdAdvisoryCommittee;
type CloseOrigin = EnsureRoot<AccountId>;
type CloseEarlyProtectionTimeFramePeriod = CloseEarlyProtectionTimeFramePeriod;
type CloseEarlyProtectionBlockPeriod = CloseEarlyProtectionBlockPeriod;
type CloseEarlyRequestBond = CloseEarlyRequestBond;
type DestroyOrigin = EnsureRootOrAllAdvisoryCommittee;
type DeployPool = NeoSwaps;
type DisputeBond = DisputeBond;
Expand All @@ -1157,6 +1162,8 @@ macro_rules! impl_config_traits {
type OracleBond = OracleBond;
type OutsiderBond = OutsiderBond;
type PalletId = PmPalletId;
type CloseEarlyBlockPeriod = CloseEarlyBlockPeriod;
type CloseEarlyTimeFramePeriod = CloseEarlyTimeFramePeriod;
type RejectOrigin = EnsureRootOrMoreThanTwoThirdsAdvisoryCommittee;
type RequestEditOrigin = EnsureRootOrMoreThanOneThirdAdvisoryCommittee;
type ResolveOrigin = EnsureRoot<AccountId>;
Expand Down
16 changes: 16 additions & 0 deletions runtime/zeitgeist/src/parameters.rs
Original file line number Diff line number Diff line change
Expand Up @@ -203,6 +203,16 @@ parameter_types! {
pub const AdvisoryBond: Balance = 200 * BASE;
/// The percentage of the advisory bond that gets slashed when a market is rejected.
pub const AdvisoryBondSlashPercentage: Percent = Percent::from_percent(0);
/// (Slashable) Bond that is provided for disputing an early market close by the market creator.
pub const CloseEarlyDisputeBond: Balance = 2_000 * BASE;
// Fat-finger protection for the advisory committe to reject
// the early market schedule.
pub const CloseEarlyProtectionTimeFramePeriod: Moment = CloseEarlyProtectionBlockPeriod::get() * MILLISECS_PER_BLOCK as u64;
// Fat-finger protection for the advisory committe to reject
// the early market schedule.
pub const CloseEarlyProtectionBlockPeriod: BlockNumber = 12 * BLOCKS_PER_HOUR;
/// (Slashable) Bond that is provided for scheduling an early market close.
pub const CloseEarlyRequestBond: Balance = 2_000 * BASE;
/// (Slashable) Bond that is provided for disputing the outcome.
/// Unreserved in case the dispute was justified otherwise slashed.
/// This is when the resolved outcome is different to the default (reported) outcome.
Expand Down Expand Up @@ -248,6 +258,12 @@ parameter_types! {
pub const OutsiderBond: Balance = 2 * OracleBond::get();
/// Pallet identifier, mainly used for named balance reserves. DO NOT CHANGE.
pub const PmPalletId: PalletId = PM_PALLET_ID;
// Waiting time for market creator to close
// the market after an early close schedule.
pub const CloseEarlyBlockPeriod: BlockNumber = 5 * BLOCKS_PER_DAY;
// Waiting time for market creator to close
// the market after an early close schedule.
pub const CloseEarlyTimeFramePeriod: Moment = CloseEarlyBlockPeriod::get() * MILLISECS_PER_BLOCK as u64;
/// (Slashable) A bond for creation markets that do not require approval. Slashed in case
/// the market is forcefully destroyed.
pub const ValidityBond: Balance = 1_000 * BASE;
Expand Down
1 change: 1 addition & 0 deletions zrml/authorized/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -395,5 +395,6 @@ where
scoring_rule: ScoringRule::CPMM,
status: MarketStatus::Disputed,
bonds: MarketBonds::default(),
early_close: None,
}
}
10 changes: 9 additions & 1 deletion zrml/court/src/benchmarks.rs
Original file line number Diff line number Diff line change
Expand Up @@ -78,7 +78,15 @@ where
resolved_outcome: None,
status: MarketStatus::Disputed,
scoring_rule: ScoringRule::CPMM,
bonds: MarketBonds { creation: None, oracle: None, outsider: None, dispute: None },
bonds: MarketBonds {
creation: None,
oracle: None,
outsider: None,
dispute: None,
close_dispute: None,
close_request: None,
},
early_close: None,
}
}

Expand Down
10 changes: 9 additions & 1 deletion zrml/court/src/tests.rs
Original file line number Diff line number Diff line change
Expand Up @@ -76,7 +76,15 @@ const DEFAULT_MARKET: MarketOf<Runtime> = Market {
resolved_outcome: None,
status: MarketStatus::Disputed,
scoring_rule: ScoringRule::CPMM,
bonds: MarketBonds { creation: None, oracle: None, outsider: None, dispute: None },
bonds: MarketBonds {
creation: None,
oracle: None,
outsider: None,
dispute: None,
close_dispute: None,
close_request: None,
},
early_close: None,
};

fn initialize_court() -> CourtId {
Expand Down
1 change: 1 addition & 0 deletions zrml/global-disputes/src/utils.rs
Original file line number Diff line number Diff line change
Expand Up @@ -57,5 +57,6 @@ where
scoring_rule: ScoringRule::CPMM,
status: zeitgeist_primitives::types::MarketStatus::Disputed,
bonds: Default::default(),
early_close: None,
}
}
1 change: 1 addition & 0 deletions zrml/liquidity-mining/src/tests.rs
Original file line number Diff line number Diff line change
Expand Up @@ -222,6 +222,7 @@ fn create_default_market(market_id: u128, period: Range<u64>) {
status: MarketStatus::Closed,
scoring_rule: ScoringRule::CPMM,
bonds: MarketBonds::default(),
early_close: None,
},
);
}
Expand Down
10 changes: 9 additions & 1 deletion zrml/market-commons/src/tests.rs
Original file line number Diff line number Diff line change
Expand Up @@ -48,7 +48,15 @@ const MARKET_DUMMY: Market<AccountIdTest, Balance, BlockNumber, Moment, Asset<Ma
resolved_outcome: None,
scoring_rule: ScoringRule::CPMM,
status: MarketStatus::Disputed,
bonds: MarketBonds { creation: None, oracle: None, outsider: None, dispute: None },
bonds: MarketBonds {
creation: None,
oracle: None,
outsider: None,
dispute: None,
close_dispute: None,
close_request: None,
},
early_close: None,
};

#[test]
Expand Down
1 change: 1 addition & 0 deletions zrml/neo-swaps/src/benchmarking.rs
Original file line number Diff line number Diff line change
Expand Up @@ -67,6 +67,7 @@ fn create_market<T: Config>(
resolved_outcome: None,
dispute_mechanism: None,
bonds: Default::default(),
early_close: None,
};
let maybe_market_id = T::MarketCommons::push_market(market);
maybe_market_id.unwrap()
Expand Down
34 changes: 22 additions & 12 deletions zrml/neo-swaps/src/mock.rs
Original file line number Diff line number Diff line change
Expand Up @@ -45,18 +45,21 @@ use zeitgeist_primitives::types::Asset;
use zeitgeist_primitives::{
constants::mock::{
AddOutcomePeriod, AggregationPeriod, AppealBond, AppealPeriod, AuthorizedPalletId,
BalanceFractionalDecimals, BlockHashCount, BlocksPerYear, CorrectionPeriod, CourtPalletId,
ExistentialDeposit, ExistentialDeposits, ExitFee, GdVotingPeriod, GetNativeCurrencyId,
GlobalDisputeLockId, GlobalDisputesPalletId, InflationPeriod, LiquidityMiningPalletId,
LockId, MaxAppeals, MaxApprovals, MaxAssets, MaxCourtParticipants, MaxCreatorFee,
MaxDelegations, MaxDisputeDuration, MaxDisputes, MaxEditReasonLen, MaxGlobalDisputeVotes,
MaxGracePeriod, MaxInRatio, MaxLocks, MaxMarketLifetime, MaxOracleDuration, MaxOutRatio,
MaxOwners, MaxRejectReasonLen, MaxReserves, MaxSelectedDraws, MaxSubsidyPeriod, MaxSwapFee,
MaxTotalWeight, MaxWeight, MinAssets, MinCategories, MinDisputeDuration, MinJurorStake,
MinOracleDuration, MinOutcomeVoteAmount, MinSubsidy, MinSubsidyPeriod, MinWeight,
MinimumPeriod, NeoMaxSwapFee, NeoSwapsPalletId, OutcomeBond, OutcomeFactor, OutsiderBond,
PmPalletId, RemoveKeysLimit, RequestInterval, SimpleDisputesPalletId, SwapsPalletId,
TreasuryPalletId, VotePeriod, VotingOutcomeFee, BASE, CENT,
BalanceFractionalDecimals, BlockHashCount, BlocksPerYear, CloseEarlyBlockPeriod,
CloseEarlyDisputeBond, CloseEarlyProtectionBlockPeriod,
CloseEarlyProtectionTimeFramePeriod, CloseEarlyRequestBond, CloseEarlyTimeFramePeriod,
CorrectionPeriod, CourtPalletId, ExistentialDeposit, ExistentialDeposits, ExitFee,
GdVotingPeriod, GetNativeCurrencyId, GlobalDisputeLockId, GlobalDisputesPalletId,
InflationPeriod, LiquidityMiningPalletId, LockId, MaxAppeals, MaxApprovals, MaxAssets,
MaxCourtParticipants, MaxCreatorFee, MaxDelegations, MaxDisputeDuration, MaxDisputes,
MaxEditReasonLen, MaxGlobalDisputeVotes, MaxGracePeriod, MaxInRatio, MaxLocks,
MaxMarketLifetime, MaxOracleDuration, MaxOutRatio, MaxOwners, MaxRejectReasonLen,
MaxReserves, MaxSelectedDraws, MaxSubsidyPeriod, MaxSwapFee, MaxTotalWeight, MaxWeight,
MinAssets, MinCategories, MinDisputeDuration, MinJurorStake, MinOracleDuration,
MinOutcomeVoteAmount, MinSubsidy, MinSubsidyPeriod, MinWeight, MinimumPeriod,
NeoMaxSwapFee, NeoSwapsPalletId, OutcomeBond, OutcomeFactor, OutsiderBond, PmPalletId,
RemoveKeysLimit, RequestInterval, SimpleDisputesPalletId, SwapsPalletId, TreasuryPalletId,
VotePeriod, VotingOutcomeFee, BASE, CENT,
},
traits::{DeployPoolApi, DistributeFees},
types::{
Expand Down Expand Up @@ -220,6 +223,13 @@ impl zrml_prediction_markets::Config for Runtime {
#[cfg(feature = "parachain")]
type AssetRegistry = MockRegistry;
type Authorized = Authorized;
type CloseEarlyBlockPeriod = CloseEarlyBlockPeriod;
type CloseEarlyDisputeBond = CloseEarlyDisputeBond;
type CloseEarlyTimeFramePeriod = CloseEarlyTimeFramePeriod;
type CloseEarlyProtectionBlockPeriod = CloseEarlyProtectionBlockPeriod;
type CloseEarlyProtectionTimeFramePeriod = CloseEarlyProtectionTimeFramePeriod;
type CloseEarlyRequestBond = CloseEarlyRequestBond;
type CloseMarketEarlyOrigin = EnsureSignedBy<Sudo, AccountIdTest>;
type CloseOrigin = EnsureSignedBy<Sudo, AccountIdTest>;
type Court = Court;
type Currency = Balances;
Expand Down
1 change: 1 addition & 0 deletions zrml/orderbook-v1/src/utils.rs
Original file line number Diff line number Diff line change
Expand Up @@ -56,5 +56,6 @@ where
scoring_rule: ScoringRule::Orderbook,
status: MarketStatus::Active,
bonds: Default::default(),
early_close: None,
}
}
1 change: 1 addition & 0 deletions zrml/parimutuel/src/utils.rs
Original file line number Diff line number Diff line change
Expand Up @@ -47,5 +47,6 @@ where
scoring_rule: ScoringRule::Parimutuel,
status: MarketStatus::Active,
bonds: MarketBonds::default(),
early_close: None,
}
}
Loading

0 comments on commit c87ca64

Please sign in to comment.