From 182db1a8102aa5444b3594a94b88ef1d0ed5a2c1 Mon Sep 17 00:00:00 2001 From: Malte Kliemann Date: Wed, 24 Jan 2024 21:16:24 +0100 Subject: [PATCH 01/10] Add market ID to `Market` struct --- primitives/src/market.rs | 15 +- primitives/src/traits.rs | 16 +- primitives/src/traits/dispute_api.rs | 6 +- .../src/traits/market_commons_pallet_api.rs | 13 +- zrml/authorized/src/benchmarks.rs | 2 +- zrml/authorized/src/lib.rs | 8 +- zrml/authorized/src/mock.rs | 14 +- zrml/court/src/benchmarks.rs | 3 +- zrml/court/src/lib.rs | 1 + zrml/court/src/mock.rs | 14 +- zrml/court/src/tests.rs | 7 +- zrml/global-disputes/src/mock.rs | 14 +- zrml/global-disputes/src/utils.rs | 2 + zrml/liquidity-mining/src/tests.rs | 1 + zrml/market-commons/src/lib.rs | 25 ++- zrml/market-commons/src/tests.rs | 182 ++++++++++-------- zrml/neo-swaps/src/benchmarking.rs | 5 +- zrml/orderbook/src/lib.rs | 1 + zrml/orderbook/src/utils.rs | 9 +- zrml/parimutuel/src/lib.rs | 10 +- zrml/parimutuel/src/utils.rs | 1 + zrml/prediction-markets/src/lib.rs | 7 +- zrml/simple-disputes/src/benchmarks.rs | 4 +- zrml/simple-disputes/src/lib.rs | 2 + zrml/simple-disputes/src/mock.rs | 14 +- zrml/simple-disputes/src/tests.rs | 1 + 26 files changed, 206 insertions(+), 171 deletions(-) diff --git a/primitives/src/market.rs b/primitives/src/market.rs index cae1eb2bc..f2139ce4d 100644 --- a/primitives/src/market.rs +++ b/primitives/src/market.rs @@ -31,8 +31,10 @@ use sp_runtime::RuntimeDebug; /// * `BN`: Block number /// * `M`: Moment (time moment) /// * `A`: Asset +/// * `MI`: Market ID #[derive(Clone, Decode, Encode, Eq, PartialEq, RuntimeDebug, TypeInfo)] -pub struct Market { +pub struct Market { + pub market_id: Option, /// Base asset of the market. pub base_asset: A, /// Creator of this market. @@ -68,7 +70,7 @@ pub struct Market { pub early_close: Option>, } -impl Market { +impl Market { pub fn resolution_mechanism(&self) -> ResolutionMechanism { match self.scoring_rule { ScoringRule::Lmsr | ScoringRule::Orderbook => ResolutionMechanism::RedeemTokens, @@ -139,7 +141,7 @@ impl Default for MarketBonds { } } -impl Market { +impl Market { // Returns the number of outcomes for a market. pub fn outcomes(&self) -> u16 { match self.market_type { @@ -165,16 +167,18 @@ impl Market { } } -impl MaxEncodedLen for Market +impl MaxEncodedLen for Market where AI: MaxEncodedLen, BA: MaxEncodedLen, BN: MaxEncodedLen, M: MaxEncodedLen, A: MaxEncodedLen, + MI: MaxEncodedLen, { fn max_encoded_len() -> usize { AI::max_encoded_len() + .saturating_add(Option::::max_encoded_len()) .saturating_add(A::max_encoded_len()) .saturating_add(MarketCreation::max_encoded_len()) .saturating_add(Perbill::max_encoded_len()) @@ -347,7 +351,7 @@ pub enum ResolutionMechanism { mod tests { use crate::{market::*, types::Asset}; use test_case::test_case; - type Market = crate::market::Market>; + type Market = crate::market::Market, u32>; #[test_case( MarketType::Categorical(6), @@ -403,6 +407,7 @@ mod tests { expected: bool, ) { let market = Market { + market_id: Some(9), base_asset: Asset::Ztg, creator: 1, creation: MarketCreation::Permissionless, diff --git a/primitives/src/traits.rs b/primitives/src/traits.rs index 00fce342a..1ee80d0b6 100644 --- a/primitives/src/traits.rs +++ b/primitives/src/traits.rs @@ -26,12 +26,12 @@ mod swaps; mod zeitgeist_asset; mod zeitgeist_multi_reservable_currency; -pub use complete_set_operations_api::CompleteSetOperationsApi; -pub use deploy_pool_api::DeployPoolApi; -pub use dispute_api::{DisputeApi, DisputeMaxWeightApi, DisputeResolutionApi}; -pub use distribute_fees::DistributeFees; -pub use market_commons_pallet_api::MarketCommonsPalletApi; -pub use market_id::MarketId; -pub use swaps::Swaps; +pub use complete_set_operations_api::*; +pub use deploy_pool_api::*; +pub use dispute_api::*; +pub use distribute_fees::*; +pub use market_commons_pallet_api::*; +pub use market_id::*; +pub use swaps::*; pub use zeitgeist_asset::*; -pub use zeitgeist_multi_reservable_currency::ZeitgeistAssetManager; +pub use zeitgeist_multi_reservable_currency::*; diff --git a/primitives/src/traits/dispute_api.rs b/primitives/src/traits/dispute_api.rs index 7c7accf1c..3a980b674 100644 --- a/primitives/src/traits/dispute_api.rs +++ b/primitives/src/traits/dispute_api.rs @@ -29,12 +29,13 @@ use sp_runtime::DispatchError; // Abstraction of the market type, which is not a part of `DisputeApi` because Rust doesn't support // type aliases in traits. -type MarketOfDisputeApi = Market< +pub type MarketOfDisputeApi = Market< ::AccountId, ::Balance, ::BlockNumber, ::Moment, Asset<::MarketId>, + ::MarketId, >; type GlobalDisputeItemOfDisputeApi = @@ -145,12 +146,13 @@ pub trait DisputeMaxWeightApi { fn clear_max_weight() -> Weight; } -type MarketOfDisputeResolutionApi = Market< +pub type MarketOfDisputeResolutionApi = Market< ::AccountId, ::Balance, ::BlockNumber, ::Moment, Asset<::MarketId>, + ::MarketId, >; pub trait DisputeResolutionApi { diff --git a/primitives/src/traits/market_commons_pallet_api.rs b/primitives/src/traits/market_commons_pallet_api.rs index 251365157..73ab323bf 100644 --- a/primitives/src/traits/market_commons_pallet_api.rs +++ b/primitives/src/traits/market_commons_pallet_api.rs @@ -36,6 +36,7 @@ type MarketOf = Market< ::BlockNumber, ::Moment, Asset<::MarketId>, + ::MarketId, >; /// Abstraction over storage operations for markets @@ -78,8 +79,16 @@ pub trait MarketCommonsPalletApi { where F: FnOnce(&mut MarketOf) -> DispatchResult; - /// Pushes a new market into the storage, returning its related auto-incremented ID. - fn push_market(market: MarketOf) -> Result; + /// Equips a market with a market ID, writes the market to storage and then returns the ID and + /// the modified market. + /// + /// This function is the only public means by which new IDs are issued. The market's `market_id` + /// field is expected to be `None`. If that's not the case, this function will raise an error to + /// avoid double-writes, which are always the result of an incorrect issuance process for market + /// IDs. + fn push_market( + market: MarketOf, + ) -> Result<(Self::MarketId, MarketOf), DispatchError>; /// Removes a market from the storage. fn remove_market(market_id: &Self::MarketId) -> DispatchResult; diff --git a/zrml/authorized/src/benchmarks.rs b/zrml/authorized/src/benchmarks.rs index db649941b..c7517102c 100644 --- a/zrml/authorized/src/benchmarks.rs +++ b/zrml/authorized/src/benchmarks.rs @@ -51,7 +51,7 @@ benchmarks! { let now = frame_system::Pallet::::block_number(); let correction_period_ends_at = now.saturating_add(T::CorrectionPeriod::get()); for _ in 1..=m { - let id = T::MarketCommons::push_market(market_mock::()).unwrap(); + let (id, _) = T::MarketCommons::push_market(market_mock::()).unwrap(); T::DisputeResolution::add_auto_resolve(&id, correction_period_ends_at).unwrap(); } diff --git a/zrml/authorized/src/lib.rs b/zrml/authorized/src/lib.rs index e491fc75e..6270b9b4e 100644 --- a/zrml/authorized/src/lib.rs +++ b/zrml/authorized/src/lib.rs @@ -73,6 +73,7 @@ mod pallet { ::BlockNumber, MomentOf, Asset>, + MarketIdOf, >; #[pallet::call] @@ -371,11 +372,12 @@ where use frame_support::traits::Get; use sp_runtime::{traits::AccountIdConversion, Perbill}; use zeitgeist_primitives::types::{ - Asset, Deadlines, MarketBonds, MarketCreation, MarketDisputeMechanism, MarketPeriod, - MarketStatus, MarketType, ScoringRule, + Asset, Deadlines, Market, MarketBonds, MarketCreation, MarketDisputeMechanism, + MarketPeriod, MarketStatus, MarketType, ScoringRule, }; - zeitgeist_primitives::types::Market { + Market { + market_id: Default::default(), base_asset: Asset::Ztg, creation: MarketCreation::Permissionless, creator_fee: Perbill::zero(), diff --git a/zrml/authorized/src/mock.rs b/zrml/authorized/src/mock.rs index 556f969c2..5669d8096 100644 --- a/zrml/authorized/src/mock.rs +++ b/zrml/authorized/src/mock.rs @@ -36,10 +36,10 @@ use zeitgeist_primitives::{ constants::mock::{ AuthorizedPalletId, BlockHashCount, CorrectionPeriod, MaxReserves, MinimumPeriod, BASE, }, - traits::DisputeResolutionApi, + traits::{DisputeResolutionApi, MarketOfDisputeResolutionApi}, types::{ - AccountIdTest, Asset, Balance, BlockNumber, BlockTest, Hash, Index, Market, MarketId, - Moment, UncheckedExtrinsicTest, + AccountIdTest, Balance, BlockNumber, BlockTest, Hash, Index, MarketId, Moment, + UncheckedExtrinsicTest, }, }; @@ -80,13 +80,7 @@ impl DisputeResolutionApi for MockResolution { fn resolve( _market_id: &Self::MarketId, - _market: &Market< - Self::AccountId, - Self::Balance, - Self::BlockNumber, - Self::Moment, - Asset, - >, + _market: &MarketOfDisputeResolutionApi, ) -> Result { Ok(Weight::zero()) } diff --git a/zrml/court/src/benchmarks.rs b/zrml/court/src/benchmarks.rs index 39a810659..858841fcd 100644 --- a/zrml/court/src/benchmarks.rs +++ b/zrml/court/src/benchmarks.rs @@ -54,6 +54,7 @@ where T: Config, { Market { + market_id: Default::default(), base_asset: Asset::Ztg, creation: MarketCreation::Permissionless, creator_fee: sp_runtime::Perbill::zero(), @@ -176,7 +177,7 @@ where let now = >::block_number(); >::put(now + 1u64.saturated_into::()); - let market_id = T::MarketCommons::push_market(get_market::()).unwrap(); + let (market_id, _) = T::MarketCommons::push_market(get_market::()).unwrap(); Court::::on_dispute(&market_id, &get_market::()).unwrap(); let court_id = >::get(market_id).unwrap(); diff --git a/zrml/court/src/lib.rs b/zrml/court/src/lib.rs index 10cf08bcb..43e1b44a0 100644 --- a/zrml/court/src/lib.rs +++ b/zrml/court/src/lib.rs @@ -220,6 +220,7 @@ mod pallet { ::BlockNumber, MomentOf, Asset>, + MarketIdOf, >; pub(crate) type HashOf = ::Hash; pub(crate) type AccountIdLookupOf = diff --git a/zrml/court/src/mock.rs b/zrml/court/src/mock.rs index 7407eac52..6af67d134 100644 --- a/zrml/court/src/mock.rs +++ b/zrml/court/src/mock.rs @@ -38,10 +38,10 @@ use zeitgeist_primitives::{ MaxReserves, MaxSelectedDraws, MaxYearlyInflation, MinJurorStake, MinimumPeriod, RequestInterval, VotePeriod, BASE, }, - traits::DisputeResolutionApi, + traits::{DisputeResolutionApi, MarketOfDisputeResolutionApi}, types::{ - AccountIdTest, Asset, Balance, BlockNumber, BlockTest, Hash, Index, Market, MarketId, - Moment, UncheckedExtrinsicTest, + AccountIdTest, Balance, BlockNumber, BlockTest, Hash, Index, MarketId, Moment, + UncheckedExtrinsicTest, }, }; @@ -96,13 +96,7 @@ impl DisputeResolutionApi for MockResolution { fn resolve( _market_id: &Self::MarketId, - _market: &Market< - Self::AccountId, - Self::Balance, - Self::BlockNumber, - Self::Moment, - Asset, - >, + _market: &MarketOfDisputeResolutionApi, ) -> Result { Ok(Weight::zero()) } diff --git a/zrml/court/src/tests.rs b/zrml/court/src/tests.rs index 4592413e3..270dd03fe 100644 --- a/zrml/court/src/tests.rs +++ b/zrml/court/src/tests.rs @@ -64,6 +64,7 @@ use zrml_market_commons::{Error as MError, MarketCommonsPalletApi}; const ORACLE_REPORT: OutcomeReport = OutcomeReport::Scalar(u128::MAX); const DEFAULT_MARKET: MarketOf = Market { + market_id: None, base_asset: Asset::Ztg, creation: MarketCreation::Permissionless, creator_fee: sp_runtime::Perbill::zero(), @@ -102,7 +103,7 @@ fn initialize_court() -> CourtId { Court::join_court(RuntimeOrigin::signed(CHARLIE), amount_charlie).unwrap(); Court::join_court(RuntimeOrigin::signed(DAVE), amount_dave).unwrap(); Court::join_court(RuntimeOrigin::signed(EVE), amount_eve).unwrap(); - let market_id = MarketCommons::push_market(DEFAULT_MARKET).unwrap(); + let (market_id, _) = MarketCommons::push_market(DEFAULT_MARKET).unwrap(); MarketCommons::mutate_market(&market_id, |market| { market.report = Some(Report { at: 1, by: BOB, outcome: ORACLE_REPORT }); Ok(()) @@ -2450,7 +2451,7 @@ fn on_resolution_sets_court_status() { #[test] fn on_resolution_fails_if_court_not_found() { ExtBuilder::default().build().execute_with(|| { - let market_id = MarketCommons::push_market(DEFAULT_MARKET).unwrap(); + let (market_id, _) = MarketCommons::push_market(DEFAULT_MARKET).unwrap(); let market = MarketCommons::market(&market_id).unwrap(); >::insert(market_id, 0); @@ -2857,7 +2858,7 @@ fn has_failed_returns_true_for_uninitialized_court() { ExtBuilder::default().build().execute_with(|| { // force empty jurors pool >::kill(); - let market_id = MarketCommons::push_market(DEFAULT_MARKET).unwrap(); + let (market_id, _) = MarketCommons::push_market(DEFAULT_MARKET).unwrap(); let report_block = 42; MarketCommons::mutate_market(&market_id, |market| { market.report = Some(Report { at: report_block, by: BOB, outcome: ORACLE_REPORT }); diff --git a/zrml/global-disputes/src/mock.rs b/zrml/global-disputes/src/mock.rs index c49705d5f..25132468a 100644 --- a/zrml/global-disputes/src/mock.rs +++ b/zrml/global-disputes/src/mock.rs @@ -34,10 +34,10 @@ use zeitgeist_primitives::{ GlobalDisputesPalletId, MaxReserves, MinOutcomeVoteAmount, MinimumPeriod, RemoveKeysLimit, VotingOutcomeFee, BASE, }, - traits::DisputeResolutionApi, + traits::{DisputeResolutionApi, MarketOfDisputeResolutionApi}, types::{ - AccountIdTest, Asset, Balance, BlockNumber, BlockTest, Hash, Index, Market, MarketId, - Moment, UncheckedExtrinsicTest, + AccountIdTest, Balance, BlockNumber, BlockTest, Hash, Index, MarketId, Moment, + UncheckedExtrinsicTest, }, }; @@ -75,13 +75,7 @@ impl DisputeResolutionApi for NoopResolution { fn resolve( _market_id: &Self::MarketId, - _market: &Market< - Self::AccountId, - Self::Balance, - Self::BlockNumber, - Self::Moment, - Asset, - >, + _market: &MarketOfDisputeResolutionApi, ) -> Result { Ok(Weight::zero()) } diff --git a/zrml/global-disputes/src/utils.rs b/zrml/global-disputes/src/utils.rs index e282ce2fd..967cb5303 100644 --- a/zrml/global-disputes/src/utils.rs +++ b/zrml/global-disputes/src/utils.rs @@ -25,6 +25,7 @@ type MarketOf = zeitgeist_primitives::types::Market< ::BlockNumber, MomentOf, zeitgeist_primitives::types::Asset>, + MarketIdOf, >; pub(crate) fn market_mock() -> MarketOf @@ -36,6 +37,7 @@ where use zeitgeist_primitives::types::ScoringRule; zeitgeist_primitives::types::Market { + market_id: Default::default(), base_asset: zeitgeist_primitives::types::Asset::Ztg, creation: zeitgeist_primitives::types::MarketCreation::Permissionless, creator_fee: sp_runtime::Perbill::zero(), diff --git a/zrml/liquidity-mining/src/tests.rs b/zrml/liquidity-mining/src/tests.rs index 420f0dae9..19d94980c 100644 --- a/zrml/liquidity-mining/src/tests.rs +++ b/zrml/liquidity-mining/src/tests.rs @@ -203,6 +203,7 @@ fn create_default_market(market_id: u128, period: Range) { Markets::::insert( market_id, Market { + market_id: Default::default(), base_asset: Asset::Ztg, creation: MarketCreation::Permissionless, creator_fee: sp_runtime::Perbill::zero(), diff --git a/zrml/market-commons/src/lib.rs b/zrml/market-commons/src/lib.rs index 5fb809959..ceb0987f0 100644 --- a/zrml/market-commons/src/lib.rs +++ b/zrml/market-commons/src/lib.rs @@ -60,8 +60,14 @@ mod pallet { pub(crate) type AssetOf = Asset>; pub(crate) type BalanceOf = ::Balance; pub(crate) type BlockNumberOf = ::BlockNumber; - pub(crate) type MarketOf = - Market, BalanceOf, BlockNumberOf, MomentOf, AssetOf>; + pub(crate) type MarketOf = Market< + AccountIdOf, + BalanceOf, + BlockNumberOf, + MomentOf, + AssetOf, + MarketIdOf, + >; pub type MarketIdOf = ::MarketId; pub type MomentOf = <::Timestamp as frame_support::traits::Time>::Moment; @@ -105,6 +111,8 @@ mod pallet { NoReport, /// There's a pool registered for this market already. PoolAlreadyExists, + /// Attempting to issue a market ID for a market object that already has an ID. + MarketIdDoubleWrite, } #[pallet::hooks] @@ -124,7 +132,7 @@ mod pallet { // on the storage so next following calls will return yet another incremented number. // // Returns `Err` if `MarketId` addition overflows. - pub fn next_market_id() -> Result { + fn next_market_id() -> Result { let id = MarketCounter::::get(); let new_counter = id.checked_add_res(&1u8.into())?; >::put(new_counter); @@ -175,10 +183,15 @@ mod pallet { }) } - fn push_market(market: MarketOf) -> Result { + fn push_market( + market: MarketOf, + ) -> Result<(Self::MarketId, MarketOf), DispatchError> { + ensure!(market.market_id.is_none(), Error::::MarketIdDoubleWrite); let market_id = Self::next_market_id()?; - >::insert(market_id, market); - Ok(market_id) + let mut mut_market = market; + mut_market.market_id = Some(market_id); + >::insert(market_id, mut_market.clone()); + Ok((market_id, mut_market)) } fn remove_market(market_id: &Self::MarketId) -> DispatchResult { diff --git a/zrml/market-commons/src/tests.rs b/zrml/market-commons/src/tests.rs index b9817d223..7dd4cf4f5 100644 --- a/zrml/market-commons/src/tests.rs +++ b/zrml/market-commons/src/tests.rs @@ -20,54 +20,75 @@ use crate::{ mock::{ExtBuilder, MarketCommons, Runtime}, - MarketCounter, Markets, + AccountIdOf, MarketCounter, MarketIdOf, MarketOf, Markets, }; use frame_support::{assert_err, assert_noop, assert_ok}; use sp_runtime::{DispatchError, Perbill}; use zeitgeist_primitives::{ traits::MarketCommonsPalletApi, types::{ - AccountIdTest, Asset, Balance, BlockNumber, Deadlines, Market, MarketBonds, MarketCreation, - MarketDisputeMechanism, MarketId, MarketPeriod, MarketStatus, MarketType, Moment, - ScoringRule, + Asset, Deadlines, Market, MarketBonds, MarketCreation, MarketDisputeMechanism, + MarketPeriod, MarketStatus, MarketType, ScoringRule, }, }; -const MARKET_DUMMY: Market> = Market { - base_asset: Asset::Ztg, - creation: MarketCreation::Permissionless, - creator_fee: Perbill::zero(), - creator: 0, - market_type: MarketType::Scalar(0..=100), - dispute_mechanism: Some(MarketDisputeMechanism::Authorized), - metadata: vec![], - oracle: 0, - period: MarketPeriod::Block(0..100), - deadlines: Deadlines { grace_period: 1_u64, oracle_duration: 1_u64, dispute_duration: 1_u64 }, - report: None, - resolved_outcome: None, - scoring_rule: ScoringRule::Lmsr, - status: MarketStatus::Disputed, - bonds: MarketBonds { - creation: None, - oracle: None, - outsider: None, - dispute: None, - close_dispute: None, - close_request: None, - }, - early_close: None, -}; +// We use the `oracle` parameter to tell test markets apart from each other. +fn construct_test_market( + market_id: Option>, + oracle: AccountIdOf, +) -> MarketOf { + Market { + market_id, + base_asset: Asset::Ztg, + creation: MarketCreation::Permissionless, + creator_fee: Perbill::zero(), + creator: 0, + market_type: MarketType::Scalar(0..=100), + dispute_mechanism: Some(MarketDisputeMechanism::Authorized), + metadata: vec![], + oracle, + period: MarketPeriod::Block(0..100), + deadlines: Deadlines { + grace_period: 1_u64, + oracle_duration: 1_u64, + dispute_duration: 1_u64, + }, + report: None, + resolved_outcome: None, + scoring_rule: ScoringRule::Lmsr, + status: MarketStatus::Disputed, + bonds: MarketBonds { + creation: None, + oracle: None, + outsider: None, + dispute: None, + close_dispute: None, + close_request: None, + }, + early_close: None, + } +} #[test] -fn latest_market_id_interacts_correctly_with_push_market() { +fn push_market_interacts_correct_with_latest_market_id_and_returns_correct_values() { ExtBuilder::default().build().execute_with(|| { - assert_ok!(MarketCommons::push_market(market_mock(0))); - assert_eq!(MarketCommons::latest_market_id().unwrap(), 0); - assert_ok!(MarketCommons::push_market(market_mock(1))); - assert_eq!(MarketCommons::latest_market_id().unwrap(), 1); - assert_ok!(MarketCommons::push_market(market_mock(1))); - assert_eq!(MarketCommons::latest_market_id().unwrap(), 2); + let (market_id, market) = + MarketCommons::push_market(construct_test_market(None, 111)).unwrap(); + assert_eq!(market_id, 0); + assert_eq!(market, construct_test_market(Some(market_id), 111)); + assert_eq!(MarketCommons::latest_market_id().unwrap(), market_id); + + let (market_id, market) = + MarketCommons::push_market(construct_test_market(None, 333)).unwrap(); + assert_eq!(market_id, 1); + assert_eq!(market, construct_test_market(Some(market_id), 333)); + assert_eq!(MarketCommons::latest_market_id().unwrap(), market_id); + + let (market_id, market) = + MarketCommons::push_market(construct_test_market(None, 222)).unwrap(); + assert_eq!(market_id, 2); + assert_eq!(market, construct_test_market(Some(market_id), 222)); + assert_eq!(MarketCommons::latest_market_id().unwrap(), market_id); }); } @@ -84,9 +105,9 @@ fn latest_market_id_fails_if_there_are_no_markets() { #[test] fn market_interacts_correctly_with_push_market() { ExtBuilder::default().build().execute_with(|| { - assert_ok!(MarketCommons::push_market(market_mock(0))); - assert_ok!(MarketCommons::push_market(market_mock(1))); - assert_ok!(MarketCommons::push_market(market_mock(2))); + assert_ok!(MarketCommons::push_market(construct_test_market(None, 0))); + assert_ok!(MarketCommons::push_market(construct_test_market(None, 1))); + assert_ok!(MarketCommons::push_market(construct_test_market(None, 2))); assert_eq!(MarketCommons::market(&0).unwrap().oracle, 0); assert_eq!(MarketCommons::market(&1).unwrap().oracle, 1); assert_eq!(MarketCommons::market(&2).unwrap().oracle, 2); @@ -96,9 +117,9 @@ fn market_interacts_correctly_with_push_market() { #[test] fn markets_storage_map_interacts_correctly_with_push_market() { ExtBuilder::default().build().execute_with(|| { - assert_ok!(MarketCommons::push_market(market_mock(0))); - assert_ok!(MarketCommons::push_market(market_mock(1))); - assert_ok!(MarketCommons::push_market(market_mock(2))); + assert_ok!(MarketCommons::push_market(construct_test_market(None, 0))); + assert_ok!(MarketCommons::push_market(construct_test_market(None, 1))); + assert_ok!(MarketCommons::push_market(construct_test_market(None, 2))); assert_eq!(>::get(0).unwrap().oracle, 0); assert_eq!(>::get(1).unwrap().oracle, 1); assert_eq!(>::get(2).unwrap().oracle, 2); @@ -109,9 +130,9 @@ fn markets_storage_map_interacts_correctly_with_push_market() { fn market_fails_if_market_does_not_exist() { ExtBuilder::default().build().execute_with(|| { assert_noop!(MarketCommons::market(&0), crate::Error::::MarketDoesNotExist); - assert_ok!(MarketCommons::push_market(MARKET_DUMMY)); - assert_ok!(MarketCommons::push_market(MARKET_DUMMY)); - assert_ok!(MarketCommons::push_market(MARKET_DUMMY)); + assert_ok!(MarketCommons::push_market(construct_test_market(None, 0))); + assert_ok!(MarketCommons::push_market(construct_test_market(None, 0))); + assert_ok!(MarketCommons::push_market(construct_test_market(None, 0))); assert_noop!(MarketCommons::market(&3), crate::Error::::MarketDoesNotExist); }); } @@ -119,7 +140,7 @@ fn market_fails_if_market_does_not_exist() { #[test] fn mutate_market_succeeds_if_closure_succeeds() { ExtBuilder::default().build().execute_with(|| { - assert_ok!(MarketCommons::push_market(market_mock(0))); + assert_ok!(MarketCommons::push_market(construct_test_market(None, 0))); assert_ok!(MarketCommons::mutate_market(&0, |market| { market.oracle = 1; Ok(()) @@ -135,9 +156,9 @@ fn mutate_market_fails_if_market_does_not_exist() { MarketCommons::mutate_market(&0, |_| Ok(())), crate::Error::::MarketDoesNotExist ); - assert_ok!(MarketCommons::push_market(MARKET_DUMMY)); - assert_ok!(MarketCommons::push_market(MARKET_DUMMY)); - assert_ok!(MarketCommons::push_market(MARKET_DUMMY)); + assert_ok!(MarketCommons::push_market(construct_test_market(None, 0))); + assert_ok!(MarketCommons::push_market(construct_test_market(None, 0))); + assert_ok!(MarketCommons::push_market(construct_test_market(None, 0))); assert_noop!( MarketCommons::mutate_market(&3, |_| Ok(())), crate::Error::::MarketDoesNotExist @@ -149,7 +170,7 @@ fn mutate_market_fails_if_market_does_not_exist() { fn mutate_market_is_noop_if_closure_fails() { ExtBuilder::default().build().execute_with(|| { let err = DispatchError::Other("foo"); - assert_ok!(MarketCommons::push_market(market_mock(0))); + assert_ok!(MarketCommons::push_market(construct_test_market(None, 0))); assert_noop!( // We change the market to check that `mutate_market` is no-op when it errors. MarketCommons::mutate_market(&0, |market| { @@ -164,9 +185,9 @@ fn mutate_market_is_noop_if_closure_fails() { #[test] fn remove_market_correctly_interacts_with_push_market() { ExtBuilder::default().build().execute_with(|| { - assert_ok!(MarketCommons::push_market(market_mock(0))); - assert_ok!(MarketCommons::push_market(market_mock(1))); - assert_ok!(MarketCommons::push_market(market_mock(2))); + assert_ok!(MarketCommons::push_market(construct_test_market(None, 0))); + assert_ok!(MarketCommons::push_market(construct_test_market(None, 1))); + assert_ok!(MarketCommons::push_market(construct_test_market(None, 2))); assert_ok!(MarketCommons::remove_market(&1)); assert_eq!(MarketCommons::market(&0).unwrap().oracle, 0); @@ -189,9 +210,9 @@ fn remove_market_correctly_interacts_with_push_market() { fn remove_market_fails_if_market_does_not_exist() { ExtBuilder::default().build().execute_with(|| { assert_noop!(MarketCommons::remove_market(&0), crate::Error::::MarketDoesNotExist); - assert_ok!(MarketCommons::push_market(MARKET_DUMMY)); - assert_ok!(MarketCommons::push_market(MARKET_DUMMY)); - assert_ok!(MarketCommons::push_market(MARKET_DUMMY)); + assert_ok!(MarketCommons::push_market(construct_test_market(None, 0))); + assert_ok!(MarketCommons::push_market(construct_test_market(None, 0))); + assert_ok!(MarketCommons::push_market(construct_test_market(None, 0))); assert_noop!(MarketCommons::remove_market(&3), crate::Error::::MarketDoesNotExist); }); } @@ -203,9 +224,9 @@ fn insert_market_pool_fails_if_market_does_not_exist() { MarketCommons::insert_market_pool(0, 15), crate::Error::::MarketDoesNotExist ); - assert_ok!(MarketCommons::push_market(MARKET_DUMMY)); - assert_ok!(MarketCommons::push_market(MARKET_DUMMY)); - assert_ok!(MarketCommons::push_market(MARKET_DUMMY)); + assert_ok!(MarketCommons::push_market(construct_test_market(None, 0))); + assert_ok!(MarketCommons::push_market(construct_test_market(None, 0))); + assert_ok!(MarketCommons::push_market(construct_test_market(None, 0))); assert_noop!( MarketCommons::insert_market_pool(3, 12), crate::Error::::MarketDoesNotExist @@ -216,7 +237,7 @@ fn insert_market_pool_fails_if_market_does_not_exist() { #[test] fn insert_market_pool_fails_if_market_has_a_pool() { ExtBuilder::default().build().execute_with(|| { - assert_ok!(MarketCommons::push_market(MARKET_DUMMY)); + assert_ok!(MarketCommons::push_market(construct_test_market(None, 0))); assert_ok!(MarketCommons::insert_market_pool(0, 15)); assert_noop!( MarketCommons::insert_market_pool(0, 14), @@ -228,9 +249,9 @@ fn insert_market_pool_fails_if_market_has_a_pool() { #[test] fn market_pool_correctly_interacts_with_insert_market_pool() { ExtBuilder::default().build().execute_with(|| { - assert_ok!(MarketCommons::push_market(MARKET_DUMMY)); - assert_ok!(MarketCommons::push_market(MARKET_DUMMY)); - assert_ok!(MarketCommons::push_market(MARKET_DUMMY)); + assert_ok!(MarketCommons::push_market(construct_test_market(None, 0))); + assert_ok!(MarketCommons::push_market(construct_test_market(None, 0))); + assert_ok!(MarketCommons::push_market(construct_test_market(None, 0))); assert_ok!(MarketCommons::insert_market_pool(0, 15)); assert_ok!(MarketCommons::insert_market_pool(1, 14)); assert_ok!(MarketCommons::insert_market_pool(2, 13)); @@ -247,9 +268,9 @@ fn market_pool_fails_if_market_has_no_pool() { MarketCommons::market_pool(&0), crate::Error::::MarketPoolDoesNotExist ); - assert_ok!(MarketCommons::push_market(MARKET_DUMMY)); - assert_ok!(MarketCommons::push_market(MARKET_DUMMY)); - assert_ok!(MarketCommons::push_market(MARKET_DUMMY)); + assert_ok!(MarketCommons::push_market(construct_test_market(None, 0))); + assert_ok!(MarketCommons::push_market(construct_test_market(None, 0))); + assert_ok!(MarketCommons::push_market(construct_test_market(None, 0))); assert_ok!(MarketCommons::insert_market_pool(0, 15)); assert_ok!(MarketCommons::insert_market_pool(1, 14)); assert_ok!(MarketCommons::insert_market_pool(2, 13)); @@ -263,9 +284,9 @@ fn market_pool_fails_if_market_has_no_pool() { #[test] fn remove_market_pool_correctly_interacts_with_insert_market_pool() { ExtBuilder::default().build().execute_with(|| { - assert_ok!(MarketCommons::push_market(MARKET_DUMMY)); - assert_ok!(MarketCommons::push_market(MARKET_DUMMY)); - assert_ok!(MarketCommons::push_market(MARKET_DUMMY)); + assert_ok!(MarketCommons::push_market(construct_test_market(None, 0))); + assert_ok!(MarketCommons::push_market(construct_test_market(None, 0))); + assert_ok!(MarketCommons::push_market(construct_test_market(None, 0))); assert_ok!(MarketCommons::insert_market_pool(0, 15)); assert_ok!(MarketCommons::insert_market_pool(1, 14)); assert_ok!(MarketCommons::insert_market_pool(2, 13)); @@ -312,9 +333,9 @@ fn remove_market_pool_fails_if_market_has_no_pool() { MarketCommons::remove_market_pool(&0), crate::Error::::MarketPoolDoesNotExist ); - assert_ok!(MarketCommons::push_market(MARKET_DUMMY)); - assert_ok!(MarketCommons::push_market(MARKET_DUMMY)); - assert_ok!(MarketCommons::push_market(MARKET_DUMMY)); + assert_ok!(MarketCommons::push_market(construct_test_market(None, 0))); + assert_ok!(MarketCommons::push_market(construct_test_market(None, 0))); + assert_ok!(MarketCommons::push_market(construct_test_market(None, 0))); assert_ok!(MarketCommons::insert_market_pool(0, 15)); assert_ok!(MarketCommons::insert_market_pool(1, 14)); assert_ok!(MarketCommons::insert_market_pool(2, 13)); @@ -329,26 +350,17 @@ fn remove_market_pool_fails_if_market_has_no_pool() { fn market_counter_interacts_correctly_with_push_market_and_remove_market() { ExtBuilder::default().build().execute_with(|| { assert_eq!(>::get(), 0); - assert_ok!(MarketCommons::push_market(MARKET_DUMMY)); + assert_ok!(MarketCommons::push_market(construct_test_market(None, 0))); assert_eq!(>::get(), 1); - assert_ok!(MarketCommons::push_market(MARKET_DUMMY)); + assert_ok!(MarketCommons::push_market(construct_test_market(None, 0))); assert_eq!(>::get(), 2); - assert_ok!(MarketCommons::push_market(MARKET_DUMMY)); + assert_ok!(MarketCommons::push_market(construct_test_market(None, 0))); assert_eq!(>::get(), 3); assert_ok!(MarketCommons::remove_market(&1)); assert_eq!(>::get(), 3); assert_ok!(MarketCommons::remove_market(&2)); assert_eq!(>::get(), 3); - assert_ok!(MarketCommons::push_market(MARKET_DUMMY)); + assert_ok!(MarketCommons::push_market(construct_test_market(None, 0))); assert_eq!(>::get(), 4); }); } - -fn market_mock( - id: AccountIdTest, -) -> zeitgeist_primitives::types::Market> -{ - let mut market = MARKET_DUMMY; - market.oracle = id; - market -} diff --git a/zrml/neo-swaps/src/benchmarking.rs b/zrml/neo-swaps/src/benchmarking.rs index fde8b8c01..2e3c8a344 100644 --- a/zrml/neo-swaps/src/benchmarking.rs +++ b/zrml/neo-swaps/src/benchmarking.rs @@ -181,6 +181,7 @@ where T: Config, { let market = Market { + market_id: None, base_asset, creation: MarketCreation::Permissionless, creator_fee: Perbill::zero(), @@ -198,8 +199,8 @@ where bonds: Default::default(), early_close: None, }; - let maybe_market_id = T::MarketCommons::push_market(market); - maybe_market_id.unwrap() + let (market_id, _) = T::MarketCommons::push_market(market).unwrap(); + market_id } fn create_spot_prices(asset_count: u16) -> Vec> { diff --git a/zrml/orderbook/src/lib.rs b/zrml/orderbook/src/lib.rs index d6ab8b7c9..876e64bf3 100644 --- a/zrml/orderbook/src/lib.rs +++ b/zrml/orderbook/src/lib.rs @@ -111,6 +111,7 @@ mod pallet { ::BlockNumber, MomentOf, AssetOf, + MarketIdOf, >; #[pallet::pallet] diff --git a/zrml/orderbook/src/utils.rs b/zrml/orderbook/src/utils.rs index 3ec2fb88b..c4167dc2a 100644 --- a/zrml/orderbook/src/utils.rs +++ b/zrml/orderbook/src/utils.rs @@ -24,19 +24,12 @@ use zeitgeist_primitives::types::{ MarketType, ScoringRule, }; -type MarketOf = Market< - ::AccountId, - BalanceOf, - ::BlockNumber, - MomentOf, - Asset>, ->; - pub(crate) fn market_mock() -> MarketOf where T: crate::Config, { Market { + market_id: Default::default(), base_asset: Asset::Ztg, creation: MarketCreation::Permissionless, creator_fee: sp_runtime::Perbill::zero(), diff --git a/zrml/parimutuel/src/lib.rs b/zrml/parimutuel/src/lib.rs index 3bbcbd441..0b6554426 100644 --- a/zrml/parimutuel/src/lib.rs +++ b/zrml/parimutuel/src/lib.rs @@ -97,8 +97,14 @@ mod pallet { pub(crate) type MarketIdOf = <::MarketCommons as MarketCommonsPalletApi>::MarketId; pub(crate) type MomentOf = <::MarketCommons as MarketCommonsPalletApi>::Moment; - pub(crate) type MarketOf = - Market, BalanceOf, BlockNumberFor, MomentOf, Asset>>; + pub(crate) type MarketOf = Market< + AccountIdOf, + BalanceOf, + BlockNumberFor, + MomentOf, + Asset>, + MarketIdOf, + >; #[pallet::pallet] #[pallet::storage_version(STORAGE_VERSION)] diff --git a/zrml/parimutuel/src/utils.rs b/zrml/parimutuel/src/utils.rs index 3ac1f28cb..4bd61d96b 100644 --- a/zrml/parimutuel/src/utils.rs +++ b/zrml/parimutuel/src/utils.rs @@ -28,6 +28,7 @@ where }; zeitgeist_primitives::types::Market { + market_id: Default::default(), base_asset: Asset::Ztg, creation: MarketCreation::Permissionless, creator_fee: Perbill::zero(), diff --git a/zrml/prediction-markets/src/lib.rs b/zrml/prediction-markets/src/lib.rs index fd5766af5..02164fc43 100644 --- a/zrml/prediction-markets/src/lib.rs +++ b/zrml/prediction-markets/src/lib.rs @@ -101,6 +101,7 @@ mod pallet { ::BlockNumber, MomentOf, AssetOf, + MarketIdOf, >; pub(crate) type MomentOf = <::Timestamp as frame_support::traits::Time>::Moment; @@ -666,6 +667,7 @@ mod pallet { Self::clear_auto_close(&market_id)?; let edited_market = Self::construct_market( + Some(market_id), base_asset, old_market.creator, old_market.creator_fee, @@ -2105,6 +2107,7 @@ mod pallet { }; let market = Self::construct_market( + None, base_asset, who.clone(), creator_fee, @@ -2128,7 +2131,7 @@ mod pallet { bonds.total_amount_bonded(&who), )?; - let market_id = >::push_market(market.clone())?; + let (market_id, market) = >::push_market(market)?; let market_account = Self::market_account(market_id); let ids_amount: u32 = Self::insert_auto_close(&market_id)?; @@ -2873,6 +2876,7 @@ mod pallet { } fn construct_market( + market_id: Option>, base_asset: AssetOf, creator: T::AccountId, creator_fee: Perbill, @@ -2914,6 +2918,7 @@ mod pallet { MarketCreation::Advised => MarketStatus::Proposed, }; Ok(Market { + market_id, base_asset, creation, creator_fee, diff --git a/zrml/simple-disputes/src/benchmarks.rs b/zrml/simple-disputes/src/benchmarks.rs index 2f06b5652..9d9c7181c 100644 --- a/zrml/simple-disputes/src/benchmarks.rs +++ b/zrml/simple-disputes/src/benchmarks.rs @@ -67,7 +67,7 @@ benchmarks! { let last_dispute = disputes.last().unwrap(); let auto_resolve = last_dispute.at.saturating_add(market.deadlines.dispute_duration); for i in 0..r { - let id = T::MarketCommons::push_market(market_mock::()).unwrap(); + let (id, _) = T::MarketCommons::push_market(market_mock::()).unwrap(); T::DisputeResolution::add_auto_resolve(&id, auto_resolve).unwrap(); } @@ -76,7 +76,7 @@ benchmarks! { let dispute_duration_ends_at_block = now.saturating_add(market.deadlines.dispute_duration); for i in 0..e { - let id = T::MarketCommons::push_market(market_mock::()).unwrap(); + let (id, _) = T::MarketCommons::push_market(market_mock::()).unwrap(); T::DisputeResolution::add_auto_resolve(&id, dispute_duration_ends_at_block).unwrap(); } diff --git a/zrml/simple-disputes/src/lib.rs b/zrml/simple-disputes/src/lib.rs index c0466b4c7..c82889ca9 100644 --- a/zrml/simple-disputes/src/lib.rs +++ b/zrml/simple-disputes/src/lib.rs @@ -116,6 +116,7 @@ mod pallet { ::BlockNumber, MomentOf, Asset>, + MarketIdOf, >; pub(crate) type DisputesOf = BoundedVec< MarketDispute< @@ -556,6 +557,7 @@ where use zeitgeist_primitives::types::{MarketBonds, ScoringRule}; zeitgeist_primitives::types::Market { + market_id: Default::default(), base_asset: Asset::Ztg, creation: zeitgeist_primitives::types::MarketCreation::Permissionless, creator_fee: sp_runtime::Perbill::zero(), diff --git a/zrml/simple-disputes/src/mock.rs b/zrml/simple-disputes/src/mock.rs index 468589c52..4d1d40cdb 100644 --- a/zrml/simple-disputes/src/mock.rs +++ b/zrml/simple-disputes/src/mock.rs @@ -33,10 +33,10 @@ use zeitgeist_primitives::{ BlockHashCount, ExistentialDeposits, GetNativeCurrencyId, MaxDisputes, MaxReserves, MinimumPeriod, OutcomeBond, OutcomeFactor, SimpleDisputesPalletId, BASE, }, - traits::DisputeResolutionApi, + traits::{DisputeResolutionApi, MarketOfDisputeResolutionApi}, types::{ - AccountIdTest, Amount, Asset, Balance, BasicCurrencyAdapter, BlockNumber, BlockTest, - CurrencyId, Hash, Index, Market, MarketId, Moment, UncheckedExtrinsicTest, + AccountIdTest, Amount, Balance, BasicCurrencyAdapter, BlockNumber, BlockTest, CurrencyId, + Hash, Index, MarketId, Moment, UncheckedExtrinsicTest, }, }; @@ -83,13 +83,7 @@ impl DisputeResolutionApi for NoopResolution { fn resolve( _market_id: &Self::MarketId, - _market: &Market< - Self::AccountId, - Self::Balance, - Self::BlockNumber, - Self::Moment, - Asset, - >, + _market: &MarketOfDisputeResolutionApi, ) -> Result { Ok(Weight::zero()) } diff --git a/zrml/simple-disputes/src/tests.rs b/zrml/simple-disputes/src/tests.rs index 03ee1e578..75506401c 100644 --- a/zrml/simple-disputes/src/tests.rs +++ b/zrml/simple-disputes/src/tests.rs @@ -33,6 +33,7 @@ use zeitgeist_primitives::{ }; const DEFAULT_MARKET: MarketOf = Market { + market_id: None, base_asset: Asset::Ztg, creation: MarketCreation::Permissionless, creator_fee: sp_runtime::Perbill::zero(), From f3f7a7a39a66c1d1cfeca50c9886b57f5de48a57 Mon Sep 17 00:00:00 2001 From: Malte Kliemann Date: Sun, 28 Jan 2024 11:56:20 +0100 Subject: [PATCH 02/10] Add market builder struct --- primitives/src/market.rs | 6 +- primitives/src/traits.rs | 2 + primitives/src/traits/market_builder.rs | 61 +++++++ .../src/traits/market_commons_pallet_api.rs | 31 +++- primitives/src/types.rs | 169 ++---------------- primitives/src/types/multi_hash.rs | 50 ++++++ .../src/types/primitive_market_builder.rs | 122 +++++++++++++ .../src/types/result_with_weight_info.rs | 32 ++++ primitives/src/types/type_aliases.rs | 95 ++++++++++ primitives/src/types/xcm_metadata.rs | 71 ++++++++ zrml/authorized/src/benchmarks.rs | 2 +- zrml/court/src/benchmarks.rs | 4 +- zrml/court/src/lib.rs | 6 +- zrml/court/src/tests.rs | 8 +- zrml/market-commons/src/lib.rs | 34 +++- zrml/market-commons/src/tests.rs | 140 ++++++++------- zrml/neo-swaps/src/benchmarking.rs | 5 +- zrml/prediction-markets/src/lib.rs | 8 +- zrml/simple-disputes/src/benchmarks.rs | 4 +- zrml/simple-disputes/src/tests.rs | 2 +- 20 files changed, 589 insertions(+), 263 deletions(-) create mode 100644 primitives/src/traits/market_builder.rs create mode 100644 primitives/src/types/multi_hash.rs create mode 100644 primitives/src/types/primitive_market_builder.rs create mode 100644 primitives/src/types/result_with_weight_info.rs create mode 100644 primitives/src/types/type_aliases.rs create mode 100644 primitives/src/types/xcm_metadata.rs diff --git a/primitives/src/market.rs b/primitives/src/market.rs index f2139ce4d..33db98444 100644 --- a/primitives/src/market.rs +++ b/primitives/src/market.rs @@ -34,7 +34,7 @@ use sp_runtime::RuntimeDebug; /// * `MI`: Market ID #[derive(Clone, Decode, Encode, Eq, PartialEq, RuntimeDebug, TypeInfo)] pub struct Market { - pub market_id: Option, + pub market_id: MI, /// Base asset of the market. pub base_asset: A, /// Creator of this market. @@ -178,7 +178,7 @@ where { fn max_encoded_len() -> usize { AI::max_encoded_len() - .saturating_add(Option::::max_encoded_len()) + .saturating_add(MI::max_encoded_len()) .saturating_add(A::max_encoded_len()) .saturating_add(MarketCreation::max_encoded_len()) .saturating_add(Perbill::max_encoded_len()) @@ -407,7 +407,7 @@ mod tests { expected: bool, ) { let market = Market { - market_id: Some(9), + market_id: 9, base_asset: Asset::Ztg, creator: 1, creation: MarketCreation::Permissionless, diff --git a/primitives/src/traits.rs b/primitives/src/traits.rs index 1ee80d0b6..5b437452b 100644 --- a/primitives/src/traits.rs +++ b/primitives/src/traits.rs @@ -20,6 +20,7 @@ mod complete_set_operations_api; mod deploy_pool_api; mod dispute_api; mod distribute_fees; +mod market_builder; mod market_commons_pallet_api; mod market_id; mod swaps; @@ -30,6 +31,7 @@ pub use complete_set_operations_api::*; pub use deploy_pool_api::*; pub use dispute_api::*; pub use distribute_fees::*; +pub use market_builder::*; pub use market_commons_pallet_api::*; pub use market_id::*; pub use swaps::*; diff --git a/primitives/src/traits/market_builder.rs b/primitives/src/traits/market_builder.rs new file mode 100644 index 000000000..0ee08ed07 --- /dev/null +++ b/primitives/src/traits/market_builder.rs @@ -0,0 +1,61 @@ +// Copyright 2024 Forecasting Technologies LTD. +// +// This file is part of Zeitgeist. +// +// Zeitgeist is free software: you can redistribute it and/or modify it +// under the terms of the GNU General Public License as published by the +// Free Software Foundation, either version 3 of the License, or (at +// your option) any later version. +// +// Zeitgeist is distributed in the hope that it will be useful, but +// WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +// General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with Zeitgeist. If not, see . + +use alloc::vec::Vec; +use crate::types::{ + Deadlines, EarlyClose, Market, MarketBonds, MarketCreation, MarketDisputeMechanism, + MarketPeriod, MarketStatus, MarketType, OutcomeReport, Report, ScoringRule, +}; +use sp_runtime::Perbill; + +macro_rules! builder_methods { + ($($field:ident: $type:ty),*) => { + $(fn $field(&mut self, $field: $type) -> &mut Self;)* + } +} + +/// Mutably referenced builder struct for the `Market` object. The `build` call is pass-by-value, so +/// the usual calling pattern is: +/// +/// ```ignore +/// let builder = MarketBuilderImpl::new(args...); +/// builder.field1(value1).field2(value2); +/// builder.clone().build() +/// ``` +pub trait MarketBuilder { + fn build(self) -> Market; + + builder_methods! { + market_id: MI, + base_asset: A, + creator: AI, + creation: MarketCreation, + creator_fee: Perbill, + oracle: AI, + metadata: Vec, + market_type: MarketType, + period: MarketPeriod, + deadlines: Deadlines, + scoring_rule: ScoringRule, + status: MarketStatus, + report: Option>, + resolved_outcome: Option, + dispute_mechanism: Option, + bonds: MarketBonds, + early_close: Option> + } +} diff --git a/primitives/src/traits/market_commons_pallet_api.rs b/primitives/src/traits/market_commons_pallet_api.rs index 73ab323bf..987b0ebb1 100644 --- a/primitives/src/traits/market_commons_pallet_api.rs +++ b/primitives/src/traits/market_commons_pallet_api.rs @@ -18,7 +18,10 @@ #![allow(clippy::type_complexity)] -use crate::types::{Asset, Market, PoolId}; +use crate::{ + traits::MarketBuilder, + types::{Asset, Market, PoolId}, +}; use frame_support::{ dispatch::{fmt::Debug, DispatchError, DispatchResult}, pallet_prelude::{MaybeSerializeDeserialize, Member}, @@ -30,12 +33,13 @@ use sp_runtime::traits::{AtLeast32Bit, AtLeast32BitUnsigned}; // Abstraction of the market type, which is not a part of `MarketCommonsPalletApi` because Rust // doesn't support type aliases in traits. -type MarketOf = Market< +type AssetOf = Asset<::MarketId>; +pub type MarketOf = Market< ::AccountId, ::Balance, ::BlockNumber, ::Moment, - Asset<::MarketId>, + AssetOf, ::MarketId, >; @@ -79,16 +83,29 @@ pub trait MarketCommonsPalletApi { where F: FnOnce(&mut MarketOf) -> DispatchResult; + /// Add a `market` to the API's list of markets, overwrite its `market_id` field with a new ID + /// and return the market's new ID. + /// + /// Deprecated since v0.5.1. For testing purposes only; use `build_market` in production. + fn push_market(market: MarketOf) -> Result; + /// Equips a market with a market ID, writes the market to storage and then returns the ID and - /// the modified market. + /// the built market. /// /// This function is the only public means by which new IDs are issued. The market's `market_id` /// field is expected to be `None`. If that's not the case, this function will raise an error to /// avoid double-writes, which are always the result of an incorrect issuance process for market /// IDs. - fn push_market( - market: MarketOf, - ) -> Result<(Self::MarketId, MarketOf), DispatchError>; + fn build_market(market_builder: U) -> Result<(Self::MarketId, MarketOf), DispatchError> + where + U: MarketBuilder< + Self::AccountId, + Self::Balance, + Self::BlockNumber, + Self::Moment, + AssetOf, + Self::MarketId, + >; /// Removes a market from the storage. fn remove_market(market_id: &Self::MarketId) -> DispatchResult; diff --git a/primitives/src/types.rs b/primitives/src/types.rs index 417a71cc5..e2460cd9c 100644 --- a/primitives/src/types.rs +++ b/primitives/src/types.rs @@ -16,161 +16,14 @@ // You should have received a copy of the GNU General Public License // along with Zeitgeist. If not, see . -pub use crate::{ - asset::*, market::*, max_runtime_usize::*, outcome_report::OutcomeReport, proxy_type::*, - serde_wrapper::*, -}; -#[cfg(feature = "arbitrary")] -use arbitrary::{Arbitrary, Result, Unstructured}; -use frame_support::dispatch::Weight; -use parity_scale_codec::{Decode, Encode, MaxEncodedLen}; -use scale_info::TypeInfo; -use sp_runtime::{ - generic, - traits::{BlakeTwo256, IdentifyAccount, Verify}, - MultiSignature, OpaqueExtrinsic, -}; - -/// Signed counter-part of Balance -pub type Amount = i128; - -/// Some way of identifying an account on the chain. We intentionally make it equivalent -/// to the public key of our transaction signing scheme. -pub type AccountId = <::Signer as IdentifyAccount>::AccountId; - -/// The type for looking up accounts. We don't expect more than 4 billion of them, but you -/// never know... -pub type AccountIndex = u64; - -/// Balance of an account. -pub type Balance = u128; - -/// Block type. -pub type Block = generic::Block; - -/// An index to a block. -pub type BlockNumber = u64; - -/// The index of the category for a `CategoricalOutcome` asset. -pub type CategoryIndex = u16; - -/// Multihash for digest sizes up to 384 bit. -/// The multicodec encoding the hash algorithm uses only 1 byte, -/// effecitvely limiting the number of available hash types. -/// HashType (1B) + DigestSize (1B) + Hash (48B). -#[derive(TypeInfo, Clone, Debug, Decode, Encode, Eq, PartialEq)] -pub enum MultiHash { - Sha3_384([u8; 50]), -} - -// Implementation for the fuzzer -#[cfg(feature = "arbitrary")] -impl<'a> Arbitrary<'a> for MultiHash { - fn arbitrary(u: &mut Unstructured<'a>) -> Result { - let mut rand_bytes = <[u8; 50] as Arbitrary<'a>>::arbitrary(u)?; - rand_bytes[0] = 0x15; - rand_bytes[1] = 0x30; - Ok(MultiHash::Sha3_384(rand_bytes)) - } - - fn size_hint(_depth: usize) -> (usize, Option) { - (50, Some(50)) - } -} - -/// ORML adapter -pub type BasicCurrencyAdapter = orml_currencies::BasicCurrencyAdapter; - -pub type CurrencyId = Asset; - -/// The asset id specifically used for pallet_assets_tx_payment for -/// paying transaction fees in different assets. -/// Since the polkadot extension and wallets can't handle custom asset ids other than just u32, -/// we are using a u32 as on the asset-hubs here. -pub type TxPaymentAssetId = u32; - -/// Index of a transaction in the chain. -pub type Index = u64; - -/// A hash of some data used by the chain. -pub type Hash = sp_core::H256; - -/// Block header type as expected by this runtime. -pub type Header = generic::Header; - -/// Digest item type. -pub type DigestItem = generic::DigestItem; - -/// The market identifier type. -pub type MarketId = u128; - -/// Time -pub type Moment = u64; - -/// The identifier type for pools. -pub type PoolId = u128; - -/// Alias to 512-bit hash when used in the context of a transaction signature on the chain. -pub type Signature = MultiSignature; - -// Tests - -pub type AccountIdTest = u128; - -#[cfg(feature = "std")] -pub type BlockTest = frame_system::mocking::MockBlock; - -#[cfg(feature = "std")] -pub type UncheckedExtrinsicTest = frame_system::mocking::MockUncheckedExtrinsic; - -#[derive(sp_runtime::RuntimeDebug, Clone, Decode, Encode, Eq, PartialEq, TypeInfo)] -pub struct ResultWithWeightInfo { - pub result: R, - pub weight: Weight, -} - -#[derive( - Clone, - Copy, - Debug, - Decode, - Default, - Encode, - Eq, - MaxEncodedLen, - Ord, - PartialEq, - PartialOrd, - TypeInfo, -)] -/// Custom XC asset metadata -pub struct CustomMetadata { - /// XCM-related metadata. - pub xcm: XcmMetadata, - - /// Whether an asset can be used as base_asset in pools. - pub allow_as_base_asset: bool, -} - -#[derive( - Clone, - Copy, - Debug, - Decode, - Default, - Encode, - Eq, - MaxEncodedLen, - Ord, - PartialEq, - PartialOrd, - TypeInfo, -)] -pub struct XcmMetadata { - /// The factor used to determine the fee. - /// It is multiplied by the fee that would have been paid in native currency, so it represents - /// the ratio `native_price / other_asset_price`. It is a fixed point decimal number containing - /// as many fractional decimals as the asset it is used for contains. - /// Should be updated regularly. - pub fee_factor: Option, -} +pub mod multi_hash; +pub mod primitive_market_builder; +pub mod result_with_weight_info; +pub mod type_aliases; +pub mod xcm_metadata; + +pub use multi_hash::*; +pub use primitive_market_builder::*; +pub use result_with_weight_info::*; +pub use type_aliases::*; +pub use xcm_metadata::*; diff --git a/primitives/src/types/multi_hash.rs b/primitives/src/types/multi_hash.rs new file mode 100644 index 000000000..628bf2134 --- /dev/null +++ b/primitives/src/types/multi_hash.rs @@ -0,0 +1,50 @@ +// Copyright 2022-2024 Forecasting Technologies LTD. +// Copyright 2021-2022 Zeitgeist PM LLC. +// +// This file is part of Zeitgeist. +// +// Zeitgeist is free software: you can redistribute it and/or modify it +// under the terms of the GNU General Public License as published by the +// Free Software Foundation, either version 3 of the License, or (at +// your option) any later version. +// +// Zeitgeist is distributed in the hope that it will be useful, but +// WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +// General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with Zeitgeist. If not, see . + +pub use crate::{ + asset::*, market::*, max_runtime_usize::*, outcome_report::OutcomeReport, proxy_type::*, + serde_wrapper::*, +}; +#[cfg(feature = "arbitrary")] +use arbitrary::{Arbitrary, Result, Unstructured}; +use parity_scale_codec::{Decode, Encode}; +use scale_info::TypeInfo; + +/// Multihash for digest sizes up to 384 bit. +/// The multicodec encoding the hash algorithm uses only 1 byte, +/// effecitvely limiting the number of available hash types. +/// HashType (1B) + DigestSize (1B) + Hash (48B). +#[derive(TypeInfo, Clone, Debug, Decode, Encode, Eq, PartialEq)] +pub enum MultiHash { + Sha3_384([u8; 50]), +} + +// Implementation for the fuzzer +#[cfg(feature = "arbitrary")] +impl<'a> Arbitrary<'a> for MultiHash { + fn arbitrary(u: &mut Unstructured<'a>) -> Result { + let mut rand_bytes = <[u8; 50] as Arbitrary<'a>>::arbitrary(u)?; + rand_bytes[0] = 0x15; + rand_bytes[1] = 0x30; + Ok(MultiHash::Sha3_384(rand_bytes)) + } + + fn size_hint(_depth: usize) -> (usize, Option) { + (50, Some(50)) + } +} diff --git a/primitives/src/types/primitive_market_builder.rs b/primitives/src/types/primitive_market_builder.rs new file mode 100644 index 000000000..bfc0c1096 --- /dev/null +++ b/primitives/src/types/primitive_market_builder.rs @@ -0,0 +1,122 @@ +// Copyright 2024 Forecasting Technologies LTD. +// +// This file is part of Zeitgeist. +// +// Zeitgeist is free software: you can redistribute it and/or modify it +// under the terms of the GNU General Public License as published by the +// Free Software Foundation, either version 3 of the License, or (at +// your option) any later version. +// +// Zeitgeist is distributed in the hope that it will be useful, but +// WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +// General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with Zeitgeist. If not, see . + +use crate::{ + traits::MarketBuilder, + types::{ + Deadlines, EarlyClose, Market, MarketBonds, MarketCreation, MarketDisputeMechanism, + MarketPeriod, MarketStatus, MarketType, OutcomeReport, Report, ScoringRule, + }, +}; +use sp_runtime::Perbill; +use alloc::vec::Vec; + +/// A sample market builder struct used for testing. No verification is done when calling `build()`; +/// use at your own risk! +/// +/// Fields are deliberately kept public to allow the straightforward construction of builder objects +/// in spots where correctness isn't the primary concern. +/// +/// # Generics +/// +/// * `AI`: The account ID type. +/// * `BA`: The balance type. +/// * `BN`: The block number type. +/// * `M`: The moment/time type. +/// * `A`: The asset type. +/// * `MI`: The market ID type. +#[derive(Clone)] +pub struct PrimitiveMarketBuilder { + pub market_id: Option, + pub base_asset: A, + pub creator: AI, + pub creation: MarketCreation, + pub creator_fee: Perbill, + pub oracle: AI, + pub metadata: Vec, + pub market_type: MarketType, + pub period: MarketPeriod, + pub deadlines: Deadlines, + pub scoring_rule: ScoringRule, + pub status: MarketStatus, + pub report: Option>, + pub resolved_outcome: Option, + pub dispute_mechanism: Option, + pub bonds: MarketBonds, + pub early_close: Option>, +} + +macro_rules! impl_builder_methods { + ($($field:ident: $type:ty),*) => { + $( + fn $field(&mut self, $field: $type) -> &mut Self { + self.$field = $field; + self + } + )* + } +} + +impl MarketBuilder + for PrimitiveMarketBuilder +{ + fn build(self) -> Market { + Market { + market_id: self.market_id.unwrap(), + base_asset: self.base_asset, + creator: self.creator, + creation: self.creation, + creator_fee: self.creator_fee, + oracle: self.oracle, + metadata: self.metadata, + market_type: self.market_type, + period: self.period, + deadlines: self.deadlines, + scoring_rule: self.scoring_rule, + status: self.status, + report: self.report, + resolved_outcome: self.resolved_outcome, + dispute_mechanism: self.dispute_mechanism, + bonds: self.bonds, + early_close: self.early_close, + } + } + + fn market_id(&mut self, market_id: MI) -> &mut Self { + self.market_id = Some(market_id); + self + } + + impl_builder_methods! { + base_asset: A, + creator: AI, + creation: MarketCreation, + creator_fee: Perbill, + oracle: AI, + metadata: Vec, + market_type: MarketType, + period: MarketPeriod, + deadlines: Deadlines, + scoring_rule: ScoringRule, + status: MarketStatus, + report: Option>, + resolved_outcome: Option, + dispute_mechanism: Option, + bonds: MarketBonds, + early_close: Option> + } +} diff --git a/primitives/src/types/result_with_weight_info.rs b/primitives/src/types/result_with_weight_info.rs new file mode 100644 index 000000000..c2465df4a --- /dev/null +++ b/primitives/src/types/result_with_weight_info.rs @@ -0,0 +1,32 @@ +// Copyright 2022-2024 Forecasting Technologies LTD. +// Copyright 2021-2022 Zeitgeist PM LLC. +// +// This file is part of Zeitgeist. +// +// Zeitgeist is free software: you can redistribute it and/or modify it +// under the terms of the GNU General Public License as published by the +// Free Software Foundation, either version 3 of the License, or (at +// your option) any later version. +// +// Zeitgeist is distributed in the hope that it will be useful, but +// WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +// General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with Zeitgeist. If not, see . + + +pub use crate::{ + asset::*, market::*, max_runtime_usize::*, outcome_report::OutcomeReport, proxy_type::*, + serde_wrapper::*, +}; +use frame_support::dispatch::Weight; +use parity_scale_codec::{Decode, Encode}; +use scale_info::TypeInfo; + +#[derive(sp_runtime::RuntimeDebug, Clone, Decode, Encode, Eq, PartialEq, TypeInfo)] +pub struct ResultWithWeightInfo { + pub result: R, + pub weight: Weight, +} diff --git a/primitives/src/types/type_aliases.rs b/primitives/src/types/type_aliases.rs new file mode 100644 index 000000000..7365ca2d9 --- /dev/null +++ b/primitives/src/types/type_aliases.rs @@ -0,0 +1,95 @@ +// Copyright 2022-2024 Forecasting Technologies LTD. +// Copyright 2021-2022 Zeitgeist PM LLC. +// +// This file is part of Zeitgeist. +// +// Zeitgeist is free software: you can redistribute it and/or modify it +// under the terms of the GNU General Public License as published by the +// Free Software Foundation, either version 3 of the License, or (at +// your option) any later version. +// +// Zeitgeist is distributed in the hope that it will be useful, but +// WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +// General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with Zeitgeist. If not, see . + +pub use crate::{ + asset::*, market::*, max_runtime_usize::*, outcome_report::OutcomeReport, proxy_type::*, + serde_wrapper::*, +}; +use sp_runtime::{ + generic, + traits::{BlakeTwo256, IdentifyAccount, Verify}, + MultiSignature, OpaqueExtrinsic, +}; + +/// Signed counter-part of Balance +pub type Amount = i128; + +/// Some way of identifying an account on the chain. We intentionally make it equivalent +/// to the public key of our transaction signing scheme. +pub type AccountId = <::Signer as IdentifyAccount>::AccountId; + +/// The type for looking up accounts. We don't expect more than 4 billion of them, but you +/// never know... +pub type AccountIndex = u64; + +/// Balance of an account. +pub type Balance = u128; + +/// Block type. +pub type Block = generic::Block; + +/// An index to a block. +pub type BlockNumber = u64; + +/// The index of the category for a `CategoricalOutcome` asset. +pub type CategoryIndex = u16; + +/// ORML adapter +pub type BasicCurrencyAdapter = orml_currencies::BasicCurrencyAdapter; + +pub type CurrencyId = Asset; + +/// The asset id specifically used for pallet_assets_tx_payment for +/// paying transaction fees in different assets. +/// Since the polkadot extension and wallets can't handle custom asset ids other than just u32, +/// we are using a u32 as on the asset-hubs here. +pub type TxPaymentAssetId = u32; + +/// Index of a transaction in the chain. +pub type Index = u64; + +/// A hash of some data used by the chain. +pub type Hash = sp_core::H256; + +/// Block header type as expected by this runtime. +pub type Header = generic::Header; + +/// Digest item type. +pub type DigestItem = generic::DigestItem; + +/// The market identifier type. +pub type MarketId = u128; + +/// Time +pub type Moment = u64; + +/// The identifier type for pools. +pub type PoolId = u128; + +/// Alias to 512-bit hash when used in the context of a transaction signature on the chain. +pub type Signature = MultiSignature; + +// Tests + +pub type AccountIdTest = u128; + +#[cfg(feature = "std")] +pub type BlockTest = frame_system::mocking::MockBlock; + +#[cfg(feature = "std")] +pub type UncheckedExtrinsicTest = frame_system::mocking::MockUncheckedExtrinsic; diff --git a/primitives/src/types/xcm_metadata.rs b/primitives/src/types/xcm_metadata.rs new file mode 100644 index 000000000..75480c8b3 --- /dev/null +++ b/primitives/src/types/xcm_metadata.rs @@ -0,0 +1,71 @@ +// Copyright 2022-2024 Forecasting Technologies LTD. +// Copyright 2021-2022 Zeitgeist PM LLC. +// +// This file is part of Zeitgeist. +// +// Zeitgeist is free software: you can redistribute it and/or modify it +// under the terms of the GNU General Public License as published by the +// Free Software Foundation, either version 3 of the License, or (at +// your option) any later version. +// +// Zeitgeist is distributed in the hope that it will be useful, but +// WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +// General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with Zeitgeist. If not, see . + +use crate::types::Balance; +pub use crate::{ + asset::*, market::*, max_runtime_usize::*, outcome_report::OutcomeReport, proxy_type::*, + serde_wrapper::*, +}; +use parity_scale_codec::{Decode, Encode, MaxEncodedLen}; +use scale_info::TypeInfo; + +#[derive( + Clone, + Copy, + Debug, + Decode, + Default, + Encode, + Eq, + MaxEncodedLen, + Ord, + PartialEq, + PartialOrd, + TypeInfo, +)] +/// Custom XC asset metadata +pub struct CustomMetadata { + /// XCM-related metadata. + pub xcm: XcmMetadata, + + /// Whether an asset can be used as base_asset in pools. + pub allow_as_base_asset: bool, +} + +#[derive( + Clone, + Copy, + Debug, + Decode, + Default, + Encode, + Eq, + MaxEncodedLen, + Ord, + PartialEq, + PartialOrd, + TypeInfo, +)] +pub struct XcmMetadata { + /// The factor used to determine the fee. + /// It is multiplied by the fee that would have been paid in native currency, so it represents + /// the ratio `native_price / other_asset_price`. It is a fixed point decimal number containing + /// as many fractional decimals as the asset it is used for contains. + /// Should be updated regularly. + pub fee_factor: Option, +} diff --git a/zrml/authorized/src/benchmarks.rs b/zrml/authorized/src/benchmarks.rs index c7517102c..db649941b 100644 --- a/zrml/authorized/src/benchmarks.rs +++ b/zrml/authorized/src/benchmarks.rs @@ -51,7 +51,7 @@ benchmarks! { let now = frame_system::Pallet::::block_number(); let correction_period_ends_at = now.saturating_add(T::CorrectionPeriod::get()); for _ in 1..=m { - let (id, _) = T::MarketCommons::push_market(market_mock::()).unwrap(); + let id = T::MarketCommons::push_market(market_mock::()).unwrap(); T::DisputeResolution::add_auto_resolve(&id, correction_period_ends_at).unwrap(); } diff --git a/zrml/court/src/benchmarks.rs b/zrml/court/src/benchmarks.rs index 858841fcd..9e65841d5 100644 --- a/zrml/court/src/benchmarks.rs +++ b/zrml/court/src/benchmarks.rs @@ -54,7 +54,7 @@ where T: Config, { Market { - market_id: Default::default(), + market_id: 0u8.into(), base_asset: Asset::Ztg, creation: MarketCreation::Permissionless, creator_fee: sp_runtime::Perbill::zero(), @@ -177,7 +177,7 @@ where let now = >::block_number(); >::put(now + 1u64.saturated_into::()); - let (market_id, _) = T::MarketCommons::push_market(get_market::()).unwrap(); + let market_id = T::MarketCommons::push_market(get_market::()).unwrap(); Court::::on_dispute(&market_id, &get_market::()).unwrap(); let court_id = >::get(market_id).unwrap(); diff --git a/zrml/court/src/lib.rs b/zrml/court/src/lib.rs index 43e1b44a0..14ea2ce3a 100644 --- a/zrml/court/src/lib.rs +++ b/zrml/court/src/lib.rs @@ -208,7 +208,9 @@ mod pallet { const APPEAL_BOND_BASIS: u32 = 2; pub(crate) type AccountIdOf = ::AccountId; + pub(crate) type AssetOf = Asset>; pub(crate) type BalanceOf = <::Currency as Currency>>::Balance; + pub(crate) type BlockNumberOf = ::BlockNumber; pub(crate) type NegativeImbalanceOf = <::Currency as Currency>>::NegativeImbalance; pub(crate) type MarketIdOf = @@ -217,9 +219,9 @@ mod pallet { pub(crate) type MarketOf = Market< AccountIdOf, BalanceOf, - ::BlockNumber, + BlockNumberOf, MomentOf, - Asset>, + AssetOf, MarketIdOf, >; pub(crate) type HashOf = ::Hash; diff --git a/zrml/court/src/tests.rs b/zrml/court/src/tests.rs index 270dd03fe..da30e2141 100644 --- a/zrml/court/src/tests.rs +++ b/zrml/court/src/tests.rs @@ -64,7 +64,7 @@ use zrml_market_commons::{Error as MError, MarketCommonsPalletApi}; const ORACLE_REPORT: OutcomeReport = OutcomeReport::Scalar(u128::MAX); const DEFAULT_MARKET: MarketOf = Market { - market_id: None, + market_id: 0, base_asset: Asset::Ztg, creation: MarketCreation::Permissionless, creator_fee: sp_runtime::Perbill::zero(), @@ -103,7 +103,7 @@ fn initialize_court() -> CourtId { Court::join_court(RuntimeOrigin::signed(CHARLIE), amount_charlie).unwrap(); Court::join_court(RuntimeOrigin::signed(DAVE), amount_dave).unwrap(); Court::join_court(RuntimeOrigin::signed(EVE), amount_eve).unwrap(); - let (market_id, _) = MarketCommons::push_market(DEFAULT_MARKET).unwrap(); + let market_id = MarketCommons::push_market(DEFAULT_MARKET).unwrap(); MarketCommons::mutate_market(&market_id, |market| { market.report = Some(Report { at: 1, by: BOB, outcome: ORACLE_REPORT }); Ok(()) @@ -2451,7 +2451,7 @@ fn on_resolution_sets_court_status() { #[test] fn on_resolution_fails_if_court_not_found() { ExtBuilder::default().build().execute_with(|| { - let (market_id, _) = MarketCommons::push_market(DEFAULT_MARKET).unwrap(); + let market_id = MarketCommons::push_market(DEFAULT_MARKET).unwrap(); let market = MarketCommons::market(&market_id).unwrap(); >::insert(market_id, 0); @@ -2858,7 +2858,7 @@ fn has_failed_returns_true_for_uninitialized_court() { ExtBuilder::default().build().execute_with(|| { // force empty jurors pool >::kill(); - let (market_id, _) = MarketCommons::push_market(DEFAULT_MARKET).unwrap(); + let market_id = MarketCommons::push_market(DEFAULT_MARKET).unwrap(); let report_block = 42; MarketCommons::mutate_market(&market_id, |market| { market.report = Some(Report { at: report_block, by: BOB, outcome: ORACLE_REPORT }); diff --git a/zrml/market-commons/src/lib.rs b/zrml/market-commons/src/lib.rs index ceb0987f0..0ba36979d 100644 --- a/zrml/market-commons/src/lib.rs +++ b/zrml/market-commons/src/lib.rs @@ -18,6 +18,7 @@ #![doc = include_str!("../README.md")] #![cfg_attr(not(feature = "std"), no_std)] +#![feature(trait_alias)] extern crate alloc; @@ -50,6 +51,7 @@ mod pallet { }; use zeitgeist_primitives::{ math::checked_ops_res::CheckedAddRes, + traits::MarketBuilder, types::{Asset, Market, PoolId}, }; @@ -183,15 +185,31 @@ mod pallet { }) } - fn push_market( - market: MarketOf, - ) -> Result<(Self::MarketId, MarketOf), DispatchError> { - ensure!(market.market_id.is_none(), Error::::MarketIdDoubleWrite); + fn push_market(mut market: MarketOf) -> Result { let market_id = Self::next_market_id()?; - let mut mut_market = market; - mut_market.market_id = Some(market_id); - >::insert(market_id, mut_market.clone()); - Ok((market_id, mut_market)) + market.market_id = market_id; + Markets::::insert(market_id, market.clone()); + Ok(market_id) + } + + fn build_market( + mut market_builder: U, + ) -> Result<(Self::MarketId, MarketOf), DispatchError> + where + U: MarketBuilder< + Self::AccountId, + Self::Balance, + Self::BlockNumber, + Self::Moment, + AssetOf, + Self::MarketId, + >, + { + let market_id = Self::next_market_id()?; + market_builder.market_id(market_id); + let market = market_builder.build(); + >::insert(market_id, market.clone()); + Ok((market_id, market)) } fn remove_market(market_id: &Self::MarketId) -> DispatchResult { diff --git a/zrml/market-commons/src/tests.rs b/zrml/market-commons/src/tests.rs index 7dd4cf4f5..45cadcdfd 100644 --- a/zrml/market-commons/src/tests.rs +++ b/zrml/market-commons/src/tests.rs @@ -20,25 +20,32 @@ use crate::{ mock::{ExtBuilder, MarketCommons, Runtime}, - AccountIdOf, MarketCounter, MarketIdOf, MarketOf, Markets, + AccountIdOf, AssetOf, BalanceOf, BlockNumberOf, MarketCounter, MarketIdOf, Markets, MomentOf, }; use frame_support::{assert_err, assert_noop, assert_ok}; use sp_runtime::{DispatchError, Perbill}; use zeitgeist_primitives::{ - traits::MarketCommonsPalletApi, + traits::{MarketBuilder, MarketCommonsPalletApi}, types::{ - Asset, Deadlines, Market, MarketBonds, MarketCreation, MarketDisputeMechanism, - MarketPeriod, MarketStatus, MarketType, ScoringRule, + Asset, Deadlines, MarketBonds, MarketCreation, MarketDisputeMechanism, MarketPeriod, + MarketStatus, MarketType, PrimitiveMarketBuilder, ScoringRule, }, }; -// We use the `oracle` parameter to tell test markets apart from each other. -fn construct_test_market( - market_id: Option>, - oracle: AccountIdOf, -) -> MarketOf { - Market { - market_id, +type PrimitiveMarketBuilderOf = PrimitiveMarketBuilder< + AccountIdOf, + BalanceOf, + BlockNumberOf, + MomentOf, + AssetOf, + MarketIdOf, +>; + +// Creates a sample market builder. We use the `oracle` field to tell markets apart from each other. +// For testing purposes, we allow `market_id` to be defined, as well. +fn create_market_builder(oracle: AccountIdOf) -> PrimitiveMarketBuilderOf { + PrimitiveMarketBuilder { + market_id: None, base_asset: Asset::Ztg, creation: MarketCreation::Permissionless, creator_fee: Perbill::zero(), @@ -70,25 +77,22 @@ fn construct_test_market( } #[test] -fn push_market_interacts_correct_with_latest_market_id_and_returns_correct_values() { +fn build_market_interacts_correct_with_latest_market_id_and_returns_correct_values() { ExtBuilder::default().build().execute_with(|| { - let (market_id, market) = - MarketCommons::push_market(construct_test_market(None, 111)).unwrap(); + let mut builder = create_market_builder(3); + let (market_id, market) = MarketCommons::build_market(builder.clone()).unwrap(); assert_eq!(market_id, 0); - assert_eq!(market, construct_test_market(Some(market_id), 111)); - assert_eq!(MarketCommons::latest_market_id().unwrap(), market_id); + assert_eq!(market, builder.market_id(market_id).clone().build()); - let (market_id, market) = - MarketCommons::push_market(construct_test_market(None, 333)).unwrap(); + let mut builder = create_market_builder(6); + let (market_id, market) = MarketCommons::build_market(builder.clone()).unwrap(); assert_eq!(market_id, 1); - assert_eq!(market, construct_test_market(Some(market_id), 333)); - assert_eq!(MarketCommons::latest_market_id().unwrap(), market_id); + assert_eq!(market, builder.market_id(market_id).clone().build()); - let (market_id, market) = - MarketCommons::push_market(construct_test_market(None, 222)).unwrap(); + let mut builder = create_market_builder(9); + let (market_id, market) = MarketCommons::build_market(builder.clone()).unwrap(); assert_eq!(market_id, 2); - assert_eq!(market, construct_test_market(Some(market_id), 222)); - assert_eq!(MarketCommons::latest_market_id().unwrap(), market_id); + assert_eq!(market, builder.market_id(market_id).clone().build()); }); } @@ -103,11 +107,11 @@ fn latest_market_id_fails_if_there_are_no_markets() { } #[test] -fn market_interacts_correctly_with_push_market() { +fn market_interacts_correctly_with_build_market() { ExtBuilder::default().build().execute_with(|| { - assert_ok!(MarketCommons::push_market(construct_test_market(None, 0))); - assert_ok!(MarketCommons::push_market(construct_test_market(None, 1))); - assert_ok!(MarketCommons::push_market(construct_test_market(None, 2))); + assert_ok!(MarketCommons::build_market(create_market_builder(0))); + assert_ok!(MarketCommons::build_market(create_market_builder(1))); + assert_ok!(MarketCommons::build_market(create_market_builder(2))); assert_eq!(MarketCommons::market(&0).unwrap().oracle, 0); assert_eq!(MarketCommons::market(&1).unwrap().oracle, 1); assert_eq!(MarketCommons::market(&2).unwrap().oracle, 2); @@ -115,11 +119,11 @@ fn market_interacts_correctly_with_push_market() { } #[test] -fn markets_storage_map_interacts_correctly_with_push_market() { +fn markets_storage_map_interacts_correctly_with_build_market() { ExtBuilder::default().build().execute_with(|| { - assert_ok!(MarketCommons::push_market(construct_test_market(None, 0))); - assert_ok!(MarketCommons::push_market(construct_test_market(None, 1))); - assert_ok!(MarketCommons::push_market(construct_test_market(None, 2))); + assert_ok!(MarketCommons::build_market(create_market_builder(0))); + assert_ok!(MarketCommons::build_market(create_market_builder(1))); + assert_ok!(MarketCommons::build_market(create_market_builder(2))); assert_eq!(>::get(0).unwrap().oracle, 0); assert_eq!(>::get(1).unwrap().oracle, 1); assert_eq!(>::get(2).unwrap().oracle, 2); @@ -130,9 +134,9 @@ fn markets_storage_map_interacts_correctly_with_push_market() { fn market_fails_if_market_does_not_exist() { ExtBuilder::default().build().execute_with(|| { assert_noop!(MarketCommons::market(&0), crate::Error::::MarketDoesNotExist); - assert_ok!(MarketCommons::push_market(construct_test_market(None, 0))); - assert_ok!(MarketCommons::push_market(construct_test_market(None, 0))); - assert_ok!(MarketCommons::push_market(construct_test_market(None, 0))); + assert_ok!(MarketCommons::build_market(create_market_builder(0))); + assert_ok!(MarketCommons::build_market(create_market_builder(0))); + assert_ok!(MarketCommons::build_market(create_market_builder(0))); assert_noop!(MarketCommons::market(&3), crate::Error::::MarketDoesNotExist); }); } @@ -140,7 +144,7 @@ fn market_fails_if_market_does_not_exist() { #[test] fn mutate_market_succeeds_if_closure_succeeds() { ExtBuilder::default().build().execute_with(|| { - assert_ok!(MarketCommons::push_market(construct_test_market(None, 0))); + assert_ok!(MarketCommons::build_market(create_market_builder(0))); assert_ok!(MarketCommons::mutate_market(&0, |market| { market.oracle = 1; Ok(()) @@ -156,9 +160,9 @@ fn mutate_market_fails_if_market_does_not_exist() { MarketCommons::mutate_market(&0, |_| Ok(())), crate::Error::::MarketDoesNotExist ); - assert_ok!(MarketCommons::push_market(construct_test_market(None, 0))); - assert_ok!(MarketCommons::push_market(construct_test_market(None, 0))); - assert_ok!(MarketCommons::push_market(construct_test_market(None, 0))); + assert_ok!(MarketCommons::build_market(create_market_builder(0))); + assert_ok!(MarketCommons::build_market(create_market_builder(0))); + assert_ok!(MarketCommons::build_market(create_market_builder(0))); assert_noop!( MarketCommons::mutate_market(&3, |_| Ok(())), crate::Error::::MarketDoesNotExist @@ -170,7 +174,7 @@ fn mutate_market_fails_if_market_does_not_exist() { fn mutate_market_is_noop_if_closure_fails() { ExtBuilder::default().build().execute_with(|| { let err = DispatchError::Other("foo"); - assert_ok!(MarketCommons::push_market(construct_test_market(None, 0))); + assert_ok!(MarketCommons::build_market(create_market_builder(0))); assert_noop!( // We change the market to check that `mutate_market` is no-op when it errors. MarketCommons::mutate_market(&0, |market| { @@ -183,11 +187,11 @@ fn mutate_market_is_noop_if_closure_fails() { } #[test] -fn remove_market_correctly_interacts_with_push_market() { +fn remove_market_correctly_interacts_with_build_market() { ExtBuilder::default().build().execute_with(|| { - assert_ok!(MarketCommons::push_market(construct_test_market(None, 0))); - assert_ok!(MarketCommons::push_market(construct_test_market(None, 1))); - assert_ok!(MarketCommons::push_market(construct_test_market(None, 2))); + assert_ok!(MarketCommons::build_market(create_market_builder(0))); + assert_ok!(MarketCommons::build_market(create_market_builder(1))); + assert_ok!(MarketCommons::build_market(create_market_builder(2))); assert_ok!(MarketCommons::remove_market(&1)); assert_eq!(MarketCommons::market(&0).unwrap().oracle, 0); @@ -210,9 +214,9 @@ fn remove_market_correctly_interacts_with_push_market() { fn remove_market_fails_if_market_does_not_exist() { ExtBuilder::default().build().execute_with(|| { assert_noop!(MarketCommons::remove_market(&0), crate::Error::::MarketDoesNotExist); - assert_ok!(MarketCommons::push_market(construct_test_market(None, 0))); - assert_ok!(MarketCommons::push_market(construct_test_market(None, 0))); - assert_ok!(MarketCommons::push_market(construct_test_market(None, 0))); + assert_ok!(MarketCommons::build_market(create_market_builder(0))); + assert_ok!(MarketCommons::build_market(create_market_builder(0))); + assert_ok!(MarketCommons::build_market(create_market_builder(0))); assert_noop!(MarketCommons::remove_market(&3), crate::Error::::MarketDoesNotExist); }); } @@ -224,9 +228,9 @@ fn insert_market_pool_fails_if_market_does_not_exist() { MarketCommons::insert_market_pool(0, 15), crate::Error::::MarketDoesNotExist ); - assert_ok!(MarketCommons::push_market(construct_test_market(None, 0))); - assert_ok!(MarketCommons::push_market(construct_test_market(None, 0))); - assert_ok!(MarketCommons::push_market(construct_test_market(None, 0))); + assert_ok!(MarketCommons::build_market(create_market_builder(0))); + assert_ok!(MarketCommons::build_market(create_market_builder(0))); + assert_ok!(MarketCommons::build_market(create_market_builder(0))); assert_noop!( MarketCommons::insert_market_pool(3, 12), crate::Error::::MarketDoesNotExist @@ -237,7 +241,7 @@ fn insert_market_pool_fails_if_market_does_not_exist() { #[test] fn insert_market_pool_fails_if_market_has_a_pool() { ExtBuilder::default().build().execute_with(|| { - assert_ok!(MarketCommons::push_market(construct_test_market(None, 0))); + assert_ok!(MarketCommons::build_market(create_market_builder(0))); assert_ok!(MarketCommons::insert_market_pool(0, 15)); assert_noop!( MarketCommons::insert_market_pool(0, 14), @@ -249,9 +253,9 @@ fn insert_market_pool_fails_if_market_has_a_pool() { #[test] fn market_pool_correctly_interacts_with_insert_market_pool() { ExtBuilder::default().build().execute_with(|| { - assert_ok!(MarketCommons::push_market(construct_test_market(None, 0))); - assert_ok!(MarketCommons::push_market(construct_test_market(None, 0))); - assert_ok!(MarketCommons::push_market(construct_test_market(None, 0))); + assert_ok!(MarketCommons::build_market(create_market_builder(0))); + assert_ok!(MarketCommons::build_market(create_market_builder(0))); + assert_ok!(MarketCommons::build_market(create_market_builder(0))); assert_ok!(MarketCommons::insert_market_pool(0, 15)); assert_ok!(MarketCommons::insert_market_pool(1, 14)); assert_ok!(MarketCommons::insert_market_pool(2, 13)); @@ -268,9 +272,9 @@ fn market_pool_fails_if_market_has_no_pool() { MarketCommons::market_pool(&0), crate::Error::::MarketPoolDoesNotExist ); - assert_ok!(MarketCommons::push_market(construct_test_market(None, 0))); - assert_ok!(MarketCommons::push_market(construct_test_market(None, 0))); - assert_ok!(MarketCommons::push_market(construct_test_market(None, 0))); + assert_ok!(MarketCommons::build_market(create_market_builder(0))); + assert_ok!(MarketCommons::build_market(create_market_builder(0))); + assert_ok!(MarketCommons::build_market(create_market_builder(0))); assert_ok!(MarketCommons::insert_market_pool(0, 15)); assert_ok!(MarketCommons::insert_market_pool(1, 14)); assert_ok!(MarketCommons::insert_market_pool(2, 13)); @@ -284,9 +288,9 @@ fn market_pool_fails_if_market_has_no_pool() { #[test] fn remove_market_pool_correctly_interacts_with_insert_market_pool() { ExtBuilder::default().build().execute_with(|| { - assert_ok!(MarketCommons::push_market(construct_test_market(None, 0))); - assert_ok!(MarketCommons::push_market(construct_test_market(None, 0))); - assert_ok!(MarketCommons::push_market(construct_test_market(None, 0))); + assert_ok!(MarketCommons::build_market(create_market_builder(0))); + assert_ok!(MarketCommons::build_market(create_market_builder(0))); + assert_ok!(MarketCommons::build_market(create_market_builder(0))); assert_ok!(MarketCommons::insert_market_pool(0, 15)); assert_ok!(MarketCommons::insert_market_pool(1, 14)); assert_ok!(MarketCommons::insert_market_pool(2, 13)); @@ -333,9 +337,9 @@ fn remove_market_pool_fails_if_market_has_no_pool() { MarketCommons::remove_market_pool(&0), crate::Error::::MarketPoolDoesNotExist ); - assert_ok!(MarketCommons::push_market(construct_test_market(None, 0))); - assert_ok!(MarketCommons::push_market(construct_test_market(None, 0))); - assert_ok!(MarketCommons::push_market(construct_test_market(None, 0))); + assert_ok!(MarketCommons::build_market(create_market_builder(0))); + assert_ok!(MarketCommons::build_market(create_market_builder(0))); + assert_ok!(MarketCommons::build_market(create_market_builder(0))); assert_ok!(MarketCommons::insert_market_pool(0, 15)); assert_ok!(MarketCommons::insert_market_pool(1, 14)); assert_ok!(MarketCommons::insert_market_pool(2, 13)); @@ -347,20 +351,20 @@ fn remove_market_pool_fails_if_market_has_no_pool() { } #[test] -fn market_counter_interacts_correctly_with_push_market_and_remove_market() { +fn market_counter_interacts_correctly_with_build_market_and_remove_market() { ExtBuilder::default().build().execute_with(|| { assert_eq!(>::get(), 0); - assert_ok!(MarketCommons::push_market(construct_test_market(None, 0))); + assert_ok!(MarketCommons::build_market(create_market_builder(0))); assert_eq!(>::get(), 1); - assert_ok!(MarketCommons::push_market(construct_test_market(None, 0))); + assert_ok!(MarketCommons::build_market(create_market_builder(0))); assert_eq!(>::get(), 2); - assert_ok!(MarketCommons::push_market(construct_test_market(None, 0))); + assert_ok!(MarketCommons::build_market(create_market_builder(0))); assert_eq!(>::get(), 3); assert_ok!(MarketCommons::remove_market(&1)); assert_eq!(>::get(), 3); assert_ok!(MarketCommons::remove_market(&2)); assert_eq!(>::get(), 3); - assert_ok!(MarketCommons::push_market(construct_test_market(None, 0))); + assert_ok!(MarketCommons::build_market(create_market_builder(0))); assert_eq!(>::get(), 4); }); } diff --git a/zrml/neo-swaps/src/benchmarking.rs b/zrml/neo-swaps/src/benchmarking.rs index 2e3c8a344..e68d38f94 100644 --- a/zrml/neo-swaps/src/benchmarking.rs +++ b/zrml/neo-swaps/src/benchmarking.rs @@ -181,7 +181,7 @@ where T: Config, { let market = Market { - market_id: None, + market_id: 0u8.into(), base_asset, creation: MarketCreation::Permissionless, creator_fee: Perbill::zero(), @@ -199,8 +199,7 @@ where bonds: Default::default(), early_close: None, }; - let (market_id, _) = T::MarketCommons::push_market(market).unwrap(); - market_id + T::MarketCommons::push_market(market).unwrap() } fn create_spot_prices(asset_count: u16) -> Vec> { diff --git a/zrml/prediction-markets/src/lib.rs b/zrml/prediction-markets/src/lib.rs index 02164fc43..9d44f353c 100644 --- a/zrml/prediction-markets/src/lib.rs +++ b/zrml/prediction-markets/src/lib.rs @@ -667,7 +667,7 @@ mod pallet { Self::clear_auto_close(&market_id)?; let edited_market = Self::construct_market( - Some(market_id), + market_id, base_asset, old_market.creator, old_market.creator_fee, @@ -2107,7 +2107,7 @@ mod pallet { }; let market = Self::construct_market( - None, + 0u8.into(), base_asset, who.clone(), creator_fee, @@ -2131,7 +2131,7 @@ mod pallet { bonds.total_amount_bonded(&who), )?; - let (market_id, market) = >::push_market(market)?; + let market_id = >::push_market(market.clone())?; let market_account = Self::market_account(market_id); let ids_amount: u32 = Self::insert_auto_close(&market_id)?; @@ -2876,7 +2876,7 @@ mod pallet { } fn construct_market( - market_id: Option>, + market_id: MarketIdOf, base_asset: AssetOf, creator: T::AccountId, creator_fee: Perbill, diff --git a/zrml/simple-disputes/src/benchmarks.rs b/zrml/simple-disputes/src/benchmarks.rs index 9d9c7181c..2f06b5652 100644 --- a/zrml/simple-disputes/src/benchmarks.rs +++ b/zrml/simple-disputes/src/benchmarks.rs @@ -67,7 +67,7 @@ benchmarks! { let last_dispute = disputes.last().unwrap(); let auto_resolve = last_dispute.at.saturating_add(market.deadlines.dispute_duration); for i in 0..r { - let (id, _) = T::MarketCommons::push_market(market_mock::()).unwrap(); + let id = T::MarketCommons::push_market(market_mock::()).unwrap(); T::DisputeResolution::add_auto_resolve(&id, auto_resolve).unwrap(); } @@ -76,7 +76,7 @@ benchmarks! { let dispute_duration_ends_at_block = now.saturating_add(market.deadlines.dispute_duration); for i in 0..e { - let (id, _) = T::MarketCommons::push_market(market_mock::()).unwrap(); + let id = T::MarketCommons::push_market(market_mock::()).unwrap(); T::DisputeResolution::add_auto_resolve(&id, dispute_duration_ends_at_block).unwrap(); } diff --git a/zrml/simple-disputes/src/tests.rs b/zrml/simple-disputes/src/tests.rs index 75506401c..f4b90915f 100644 --- a/zrml/simple-disputes/src/tests.rs +++ b/zrml/simple-disputes/src/tests.rs @@ -33,7 +33,7 @@ use zeitgeist_primitives::{ }; const DEFAULT_MARKET: MarketOf = Market { - market_id: None, + market_id: 0, base_asset: Asset::Ztg, creation: MarketCreation::Permissionless, creator_fee: sp_runtime::Perbill::zero(), From 6a16b021c01d9866d75cdd0cdf1ab0600e732fc2 Mon Sep 17 00:00:00 2001 From: Malte Kliemann Date: Mon, 5 Feb 2024 22:29:02 +0100 Subject: [PATCH 03/10] Use `PredictionMarketBuilder` --- zrml/prediction-markets/src/lib.rs | 74 ++++++---- .../src/types/market_builder.rs | 137 ++++++++++++++++++ zrml/prediction-markets/src/types/mod.rs | 20 +++ 3 files changed, 202 insertions(+), 29 deletions(-) create mode 100644 zrml/prediction-markets/src/types/market_builder.rs create mode 100644 zrml/prediction-markets/src/types/mod.rs diff --git a/zrml/prediction-markets/src/lib.rs b/zrml/prediction-markets/src/lib.rs index 9d44f353c..451e65226 100644 --- a/zrml/prediction-markets/src/lib.rs +++ b/zrml/prediction-markets/src/lib.rs @@ -28,6 +28,7 @@ pub mod migrations; pub mod mock; pub mod orml_asset_registry; mod tests; +mod types; pub mod weights; pub use pallet::*; @@ -55,6 +56,7 @@ mod pallet { #[cfg(feature = "parachain")] use {orml_traits::asset_registry::Inspect, zeitgeist_primitives::types::CustomMetadata}; + use crate::types::PredictionMarketBuilder; use orml_traits::{MultiCurrency, NamedMultiReservableCurrency}; use sp_arithmetic::per_things::{Perbill, Percent}; use sp_runtime::{ @@ -65,7 +67,7 @@ mod pallet { constants::MILLISECS_PER_BLOCK, traits::{ CompleteSetOperationsApi, DeployPoolApi, DisputeApi, DisputeMaxWeightApi, - DisputeResolutionApi, ZeitgeistAssetManager, + DisputeResolutionApi, MarketBuilder, ZeitgeistAssetManager, }, types::{ Asset, Bond, Deadlines, EarlyClose, EarlyCloseState, GlobalDisputeItem, Market, @@ -90,6 +92,7 @@ mod pallet { pub(crate) type AccountIdOf = ::AccountId; pub(crate) type AssetOf = Asset>; pub(crate) type BalanceOf = ::Balance; + pub(crate) type BlockNumberOf = ::BlockNumber; pub(crate) type CacheSize = ConstU32<64>; pub(crate) type EditReason = BoundedVec::MaxEditReasonLen>; pub(crate) type InitialItemOf = InitialItem, BalanceOf>; @@ -98,7 +101,7 @@ mod pallet { pub(crate) type MarketOf = Market< AccountIdOf, BalanceOf, - ::BlockNumber, + BlockNumberOf, MomentOf, AssetOf, MarketIdOf, @@ -107,8 +110,16 @@ mod pallet { <::Timestamp as frame_support::traits::Time>::Moment; pub(crate) type NegativeImbalanceOf = <::Currency as Currency>>::NegativeImbalance; + pub(crate) type PredictionMarketBuilderOf = PredictionMarketBuilder< + AccountIdOf, + BalanceOf, + BlockNumberOf, + MomentOf, + AssetOf, + MarketIdOf, + >; pub(crate) type RejectReason = BoundedVec::MaxRejectReasonLen>; - pub(crate) type ReportOf = Report, ::BlockNumber>; + pub(crate) type ReportOf = Report, BlockNumberOf>; pub(crate) type TimeFrame = u64; macro_rules! impl_unreserve_bond { @@ -666,8 +677,8 @@ mod pallet { ensure!(old_market.status == MarketStatus::Proposed, Error::::InvalidMarketStatus); Self::clear_auto_close(&market_id)?; - let edited_market = Self::construct_market( - market_id, + let market_builder = Self::construct_market( + Some(market_id), base_asset, old_market.creator, old_market.creator_fee, @@ -683,6 +694,7 @@ mod pallet { old_market.resolved_outcome, old_market.bonds, )?; + let edited_market = market_builder.clone().build(); >::mutate_market(&market_id, |market| { *market = edited_market.clone(); Ok(()) @@ -2106,8 +2118,8 @@ mod pallet { }, }; - let market = Self::construct_market( - 0u8.into(), + let market_builder = Self::construct_market( + None, base_asset, who.clone(), creator_fee, @@ -2131,7 +2143,8 @@ mod pallet { bonds.total_amount_bonded(&who), )?; - let market_id = >::push_market(market.clone())?; + let (market_id, market) = + >::build_market(market_builder)?; let market_account = Self::market_account(market_id); let ids_amount: u32 = Self::insert_auto_close(&market_id)?; @@ -2876,7 +2889,7 @@ mod pallet { } fn construct_market( - market_id: MarketIdOf, + market_id: Option>, base_asset: AssetOf, creator: T::AccountId, creator_fee: Perbill, @@ -2891,7 +2904,7 @@ mod pallet { report: Option>, resolved_outcome: Option, bonds: MarketBondsOf, - ) -> Result, DispatchError> { + ) -> Result, DispatchError> { let valid_base_asset = match base_asset { Asset::Ztg => true, #[cfg(feature = "parachain")] @@ -2917,25 +2930,28 @@ mod pallet { MarketCreation::Permissionless => MarketStatus::Active, MarketCreation::Advised => MarketStatus::Proposed, }; - Ok(Market { - market_id, - base_asset, - creation, - creator_fee, - creator, - market_type, - dispute_mechanism, - metadata: Vec::from(multihash), - oracle, - period, - deadlines, - report, - resolved_outcome, - status, - scoring_rule, - bonds, - early_close: None, - }) + let mut market_builder = PredictionMarketBuilder::new(); + market_builder + .base_asset(base_asset) + .creator(creator) + .creator_fee(creator_fee) + .oracle(oracle) + .period(period) + .deadlines(deadlines) + .metadata(Vec::from(multihash)) + .creation(creation) + .market_type(market_type) + .dispute_mechanism(dispute_mechanism) + .status(status) + .scoring_rule(scoring_rule) + .report(report) + .resolved_outcome(resolved_outcome) + .bonds(bonds) + .early_close(None); + if let Some(market_id) = market_id { + market_builder.market_id(market_id); + } + Ok(market_builder) } fn report_market_with_dispute_mechanism( diff --git a/zrml/prediction-markets/src/types/market_builder.rs b/zrml/prediction-markets/src/types/market_builder.rs new file mode 100644 index 000000000..ade626a99 --- /dev/null +++ b/zrml/prediction-markets/src/types/market_builder.rs @@ -0,0 +1,137 @@ +// Copyright 2024 Forecasting Technologies LTD. +// +// This file is part of Zeitgeist. +// +// Zeitgeist is free software: you can redistribute it and/or modify it +// under the terms of the GNU General Public License as published by the +// Free Software Foundation, either version 3 of the License, or (at +// your option) any later version. +// +// Zeitgeist is distributed in the hope that it will be useful, but +// WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +// General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with Zeitgeist. If not, see . + +use alloc::vec::Vec; +use sp_runtime::Perbill; +use zeitgeist_primitives::{ + traits::MarketBuilder, + types::{ + Deadlines, EarlyClose, Market, MarketBonds, MarketCreation, MarketDisputeMechanism, + MarketPeriod, MarketStatus, MarketType, OutcomeReport, Report, ScoringRule, + }, +}; + +/// # Generics +/// +/// * `AI`: The account ID type. +/// * `BA`: The balance type. +/// * `BN`: The block number type. +/// * `M`: The moment/time type. +/// * `A`: The asset type. +/// * `MI`: The market ID type. +#[derive(Clone)] +pub struct PredictionMarketBuilder { + pub market_id: Option, + pub base_asset: Option, + pub creator: Option, + pub creation: Option, + pub creator_fee: Option, + pub oracle: Option, + pub metadata: Option>, + pub market_type: Option, + pub period: Option>, + pub deadlines: Option>, + pub scoring_rule: Option, + pub status: Option, + pub report: Option>>, + pub resolved_outcome: Option>, + pub dispute_mechanism: Option>, + pub bonds: Option>, + pub early_close: Option>>, +} + +impl PredictionMarketBuilder { + pub(crate) fn new() -> Self { + PredictionMarketBuilder { + market_id: None, + base_asset: None, + creator: None, + creation: None, + creator_fee: None, + oracle: None, + metadata: None, + market_type: None, + period: None, + deadlines: None, + scoring_rule: None, + status: None, + report: None, + resolved_outcome: None, + dispute_mechanism: None, + bonds: None, + early_close: None, + } + } +} + +macro_rules! impl_builder_methods { + ($($field:ident: $type:ty),*) => { + $( + fn $field(&mut self, $field: $type) -> &mut Self { + self.$field = Some($field); + self + } + )* + } +} + +impl MarketBuilder + for PredictionMarketBuilder +{ + fn build(self) -> Market { + // TODO Remove unwraps, make build return result + Market { + market_id: self.market_id.unwrap(), + base_asset: self.base_asset.unwrap(), + creator: self.creator.unwrap(), + creation: self.creation.unwrap(), + creator_fee: self.creator_fee.unwrap(), + oracle: self.oracle.unwrap(), + metadata: self.metadata.unwrap(), + market_type: self.market_type.unwrap(), + period: self.period.unwrap(), + deadlines: self.deadlines.unwrap(), + scoring_rule: self.scoring_rule.unwrap(), + status: self.status.unwrap(), + report: self.report.unwrap(), + resolved_outcome: self.resolved_outcome.unwrap(), + dispute_mechanism: self.dispute_mechanism.unwrap(), + bonds: self.bonds.unwrap(), + early_close: self.early_close.unwrap(), + } + } + + impl_builder_methods! { + market_id: MI, + base_asset: A, + creator: AI, + creation: MarketCreation, + creator_fee: Perbill, + oracle: AI, + metadata: Vec, + market_type: MarketType, + period: MarketPeriod, + deadlines: Deadlines, + scoring_rule: ScoringRule, + status: MarketStatus, + report: Option>, + resolved_outcome: Option, + dispute_mechanism: Option, + bonds: MarketBonds, + early_close: Option> + } +} diff --git a/zrml/prediction-markets/src/types/mod.rs b/zrml/prediction-markets/src/types/mod.rs new file mode 100644 index 000000000..20f97e4dc --- /dev/null +++ b/zrml/prediction-markets/src/types/mod.rs @@ -0,0 +1,20 @@ +// Copyright 2023-2024 Forecasting Technologies LTD. +// +// This file is part of Zeitgeist. +// +// Zeitgeist is free software: you can redistribute it and/or modify it +// under the terms of the GNU General Public License as published by the +// Free Software Foundation, either version 3 of the License, or (at +// your option) any later version. +// +// Zeitgeist is distributed in the hope that it will be useful, but +// WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +// General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with Zeitgeist. If not, see . + +mod market_builder; + +pub(crate) use market_builder::*; From 102da435c995729ba63689c2c276f31bfce26d69 Mon Sep 17 00:00:00 2001 From: Malte Kliemann Date: Mon, 5 Feb 2024 22:49:58 +0100 Subject: [PATCH 04/10] Fix issues --- primitives/src/traits/dispute_api.rs | 2 +- primitives/src/traits/market_builder.rs | 2 +- primitives/src/traits/market_commons_pallet_api.rs | 4 +++- primitives/src/types/primitive_market_builder.rs | 2 +- primitives/src/types/result_with_weight_info.rs | 1 - zrml/court/src/lib.rs | 8 +++----- zrml/market-commons/src/lib.rs | 3 --- zrml/orderbook/src/utils.rs | 2 +- zrml/parimutuel/src/utils.rs | 2 +- zrml/prediction-markets/src/types/market_builder.rs | 3 +++ 10 files changed, 14 insertions(+), 15 deletions(-) diff --git a/primitives/src/traits/dispute_api.rs b/primitives/src/traits/dispute_api.rs index 3a980b674..b5ec88d5d 100644 --- a/primitives/src/traits/dispute_api.rs +++ b/primitives/src/traits/dispute_api.rs @@ -1,4 +1,4 @@ -// Copyright 2023 Forecasting Technologies LTD. +// Copyright 2023-2024 Forecasting Technologies LTD. // Copyright 2021-2022 Zeitgeist PM LLC. // // This file is part of Zeitgeist. diff --git a/primitives/src/traits/market_builder.rs b/primitives/src/traits/market_builder.rs index 0ee08ed07..15ce88327 100644 --- a/primitives/src/traits/market_builder.rs +++ b/primitives/src/traits/market_builder.rs @@ -15,11 +15,11 @@ // You should have received a copy of the GNU General Public License // along with Zeitgeist. If not, see . -use alloc::vec::Vec; use crate::types::{ Deadlines, EarlyClose, Market, MarketBonds, MarketCreation, MarketDisputeMechanism, MarketPeriod, MarketStatus, MarketType, OutcomeReport, Report, ScoringRule, }; +use alloc::vec::Vec; use sp_runtime::Perbill; macro_rules! builder_methods { diff --git a/primitives/src/traits/market_commons_pallet_api.rs b/primitives/src/traits/market_commons_pallet_api.rs index 987b0ebb1..aa07cb2f5 100644 --- a/primitives/src/traits/market_commons_pallet_api.rs +++ b/primitives/src/traits/market_commons_pallet_api.rs @@ -96,7 +96,9 @@ pub trait MarketCommonsPalletApi { /// field is expected to be `None`. If that's not the case, this function will raise an error to /// avoid double-writes, which are always the result of an incorrect issuance process for market /// IDs. - fn build_market(market_builder: U) -> Result<(Self::MarketId, MarketOf), DispatchError> + fn build_market( + market_builder: U, + ) -> Result<(Self::MarketId, MarketOf), DispatchError> where U: MarketBuilder< Self::AccountId, diff --git a/primitives/src/types/primitive_market_builder.rs b/primitives/src/types/primitive_market_builder.rs index bfc0c1096..75017c9cc 100644 --- a/primitives/src/types/primitive_market_builder.rs +++ b/primitives/src/types/primitive_market_builder.rs @@ -22,8 +22,8 @@ use crate::{ MarketPeriod, MarketStatus, MarketType, OutcomeReport, Report, ScoringRule, }, }; -use sp_runtime::Perbill; use alloc::vec::Vec; +use sp_runtime::Perbill; /// A sample market builder struct used for testing. No verification is done when calling `build()`; /// use at your own risk! diff --git a/primitives/src/types/result_with_weight_info.rs b/primitives/src/types/result_with_weight_info.rs index c2465df4a..34ac8b493 100644 --- a/primitives/src/types/result_with_weight_info.rs +++ b/primitives/src/types/result_with_weight_info.rs @@ -16,7 +16,6 @@ // You should have received a copy of the GNU General Public License // along with Zeitgeist. If not, see . - pub use crate::{ asset::*, market::*, max_runtime_usize::*, outcome_report::OutcomeReport, proxy_type::*, serde_wrapper::*, diff --git a/zrml/court/src/lib.rs b/zrml/court/src/lib.rs index 14ea2ce3a..899e344d9 100644 --- a/zrml/court/src/lib.rs +++ b/zrml/court/src/lib.rs @@ -1,4 +1,4 @@ -// Copyright 2022-2023 Forecasting Technologies LTD. +// Copyright 2022-2024 Forecasting Technologies LTD. // Copyright 2021-2022 Zeitgeist PM LLC. // // This file is part of Zeitgeist. @@ -208,9 +208,7 @@ mod pallet { const APPEAL_BOND_BASIS: u32 = 2; pub(crate) type AccountIdOf = ::AccountId; - pub(crate) type AssetOf = Asset>; pub(crate) type BalanceOf = <::Currency as Currency>>::Balance; - pub(crate) type BlockNumberOf = ::BlockNumber; pub(crate) type NegativeImbalanceOf = <::Currency as Currency>>::NegativeImbalance; pub(crate) type MarketIdOf = @@ -219,9 +217,9 @@ mod pallet { pub(crate) type MarketOf = Market< AccountIdOf, BalanceOf, - BlockNumberOf, + ::BlockNumber, MomentOf, - AssetOf, + Asset>, MarketIdOf, >; pub(crate) type HashOf = ::Hash; diff --git a/zrml/market-commons/src/lib.rs b/zrml/market-commons/src/lib.rs index 0ba36979d..d0b3a3529 100644 --- a/zrml/market-commons/src/lib.rs +++ b/zrml/market-commons/src/lib.rs @@ -18,7 +18,6 @@ #![doc = include_str!("../README.md")] #![cfg_attr(not(feature = "std"), no_std)] -#![feature(trait_alias)] extern crate alloc; @@ -113,8 +112,6 @@ mod pallet { NoReport, /// There's a pool registered for this market already. PoolAlreadyExists, - /// Attempting to issue a market ID for a market object that already has an ID. - MarketIdDoubleWrite, } #[pallet::hooks] diff --git a/zrml/orderbook/src/utils.rs b/zrml/orderbook/src/utils.rs index c4167dc2a..0dc465645 100644 --- a/zrml/orderbook/src/utils.rs +++ b/zrml/orderbook/src/utils.rs @@ -1,4 +1,4 @@ -// Copyright 2023 Forecasting Technologies LTD. +// Copyright 2023-2024 Forecasting Technologies LTD. // // This file is part of Zeitgeist. // diff --git a/zrml/parimutuel/src/utils.rs b/zrml/parimutuel/src/utils.rs index 4bd61d96b..35b0422f6 100644 --- a/zrml/parimutuel/src/utils.rs +++ b/zrml/parimutuel/src/utils.rs @@ -1,4 +1,4 @@ -// Copyright 2023 Forecasting Technologies LTD. +// Copyright 2023-2024 Forecasting Technologies LTD. // // This file is part of Zeitgeist. // diff --git a/zrml/prediction-markets/src/types/market_builder.rs b/zrml/prediction-markets/src/types/market_builder.rs index ade626a99..dd47912a6 100644 --- a/zrml/prediction-markets/src/types/market_builder.rs +++ b/zrml/prediction-markets/src/types/market_builder.rs @@ -25,6 +25,8 @@ use zeitgeist_primitives::{ }, }; +/// Fully-fledged mutably referenced market builder struct. +/// /// # Generics /// /// * `AI`: The account ID type. @@ -51,6 +53,7 @@ pub struct PredictionMarketBuilder { pub resolved_outcome: Option>, pub dispute_mechanism: Option>, pub bonds: Option>, + pub early_close: Option>>, } From fdf77994531bd29f1ff776ff50aba231ef7609ba Mon Sep 17 00:00:00 2001 From: Malte Kliemann Date: Mon, 5 Feb 2024 22:52:07 +0100 Subject: [PATCH 05/10] Fix copyright --- zrml/prediction-markets/src/types/mod.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/zrml/prediction-markets/src/types/mod.rs b/zrml/prediction-markets/src/types/mod.rs index 20f97e4dc..47baa4a61 100644 --- a/zrml/prediction-markets/src/types/mod.rs +++ b/zrml/prediction-markets/src/types/mod.rs @@ -1,4 +1,4 @@ -// Copyright 2023-2024 Forecasting Technologies LTD. +// Copyright 2024 Forecasting Technologies LTD. // // This file is part of Zeitgeist. // From cfbe9d7fd3bc35b79a502aff1378697c720c9da3 Mon Sep 17 00:00:00 2001 From: Malte Kliemann Date: Wed, 7 Feb 2024 01:24:04 +0100 Subject: [PATCH 06/10] Make `build` return a `Result` --- primitives/src/traits/market_builder.rs | 4 +- .../src/types/primitive_market_builder.rs | 8 +-- zrml/market-commons/src/lib.rs | 2 +- zrml/market-commons/src/tests.rs | 6 +- zrml/prediction-markets/src/lib.rs | 46 +++++------- .../src/types/market_builder.rs | 72 ++++++++++++------- 6 files changed, 73 insertions(+), 65 deletions(-) diff --git a/primitives/src/traits/market_builder.rs b/primitives/src/traits/market_builder.rs index 15ce88327..c956bf7fc 100644 --- a/primitives/src/traits/market_builder.rs +++ b/primitives/src/traits/market_builder.rs @@ -20,7 +20,7 @@ use crate::types::{ MarketPeriod, MarketStatus, MarketType, OutcomeReport, Report, ScoringRule, }; use alloc::vec::Vec; -use sp_runtime::Perbill; +use sp_runtime::{DispatchError, Perbill}; macro_rules! builder_methods { ($($field:ident: $type:ty),*) => { @@ -37,7 +37,7 @@ macro_rules! builder_methods { /// builder.clone().build() /// ``` pub trait MarketBuilder { - fn build(self) -> Market; + fn build(self) -> Result, DispatchError>; builder_methods! { market_id: MI, diff --git a/primitives/src/types/primitive_market_builder.rs b/primitives/src/types/primitive_market_builder.rs index 75017c9cc..34b0ea959 100644 --- a/primitives/src/types/primitive_market_builder.rs +++ b/primitives/src/types/primitive_market_builder.rs @@ -23,7 +23,7 @@ use crate::{ }, }; use alloc::vec::Vec; -use sp_runtime::Perbill; +use sp_runtime::{DispatchError, Perbill}; /// A sample market builder struct used for testing. No verification is done when calling `build()`; /// use at your own risk! @@ -74,8 +74,8 @@ macro_rules! impl_builder_methods { impl MarketBuilder for PrimitiveMarketBuilder { - fn build(self) -> Market { - Market { + fn build(self) -> Result, DispatchError> { + Ok(Market { market_id: self.market_id.unwrap(), base_asset: self.base_asset, creator: self.creator, @@ -93,7 +93,7 @@ impl MarketBuilder dispute_mechanism: self.dispute_mechanism, bonds: self.bonds, early_close: self.early_close, - } + }) } fn market_id(&mut self, market_id: MI) -> &mut Self { diff --git a/zrml/market-commons/src/lib.rs b/zrml/market-commons/src/lib.rs index d0b3a3529..ef4470be5 100644 --- a/zrml/market-commons/src/lib.rs +++ b/zrml/market-commons/src/lib.rs @@ -204,7 +204,7 @@ mod pallet { { let market_id = Self::next_market_id()?; market_builder.market_id(market_id); - let market = market_builder.build(); + let market = market_builder.build()?; >::insert(market_id, market.clone()); Ok((market_id, market)) } diff --git a/zrml/market-commons/src/tests.rs b/zrml/market-commons/src/tests.rs index 45cadcdfd..b74fdfd77 100644 --- a/zrml/market-commons/src/tests.rs +++ b/zrml/market-commons/src/tests.rs @@ -82,17 +82,17 @@ fn build_market_interacts_correct_with_latest_market_id_and_returns_correct_valu let mut builder = create_market_builder(3); let (market_id, market) = MarketCommons::build_market(builder.clone()).unwrap(); assert_eq!(market_id, 0); - assert_eq!(market, builder.market_id(market_id).clone().build()); + assert_eq!(market, builder.market_id(market_id).clone().build().unwrap()); let mut builder = create_market_builder(6); let (market_id, market) = MarketCommons::build_market(builder.clone()).unwrap(); assert_eq!(market_id, 1); - assert_eq!(market, builder.market_id(market_id).clone().build()); + assert_eq!(market, builder.market_id(market_id).clone().build().unwrap()); let mut builder = create_market_builder(9); let (market_id, market) = MarketCommons::build_market(builder.clone()).unwrap(); assert_eq!(market_id, 2); - assert_eq!(market, builder.market_id(market_id).clone().build()); + assert_eq!(market, builder.market_id(market_id).clone().build().unwrap()); }); } diff --git a/zrml/prediction-markets/src/lib.rs b/zrml/prediction-markets/src/lib.rs index 451e65226..569b0b024 100644 --- a/zrml/prediction-markets/src/lib.rs +++ b/zrml/prediction-markets/src/lib.rs @@ -94,6 +94,8 @@ mod pallet { pub(crate) type BalanceOf = ::Balance; pub(crate) type BlockNumberOf = ::BlockNumber; pub(crate) type CacheSize = ConstU32<64>; + pub(crate) type DeadlinesOf = Deadlines>; + pub(crate) type EarlyCloseOf = EarlyClose, MomentOf>; pub(crate) type EditReason = BoundedVec::MaxEditReasonLen>; pub(crate) type InitialItemOf = InitialItem, BalanceOf>; pub(crate) type MarketBondsOf = MarketBonds, BalanceOf>; @@ -106,18 +108,11 @@ mod pallet { AssetOf, MarketIdOf, >; + pub(crate) type MarketPeriodOf = MarketPeriod, MomentOf>; pub(crate) type MomentOf = <::Timestamp as frame_support::traits::Time>::Moment; pub(crate) type NegativeImbalanceOf = <::Currency as Currency>>::NegativeImbalance; - pub(crate) type PredictionMarketBuilderOf = PredictionMarketBuilder< - AccountIdOf, - BalanceOf, - BlockNumberOf, - MomentOf, - AssetOf, - MarketIdOf, - >; pub(crate) type RejectReason = BoundedVec::MaxRejectReasonLen>; pub(crate) type ReportOf = Report, BlockNumberOf>; pub(crate) type TimeFrame = u64; @@ -608,8 +603,8 @@ mod pallet { base_asset: AssetOf, creator_fee: Perbill, oracle: T::AccountId, - period: MarketPeriod>, - deadlines: Deadlines, + period: MarketPeriodOf, + deadlines: DeadlinesOf, metadata: MultiHash, creation: MarketCreation, market_type: MarketType, @@ -660,8 +655,8 @@ mod pallet { base_asset: AssetOf, market_id: MarketIdOf, oracle: T::AccountId, - period: MarketPeriod>, - deadlines: Deadlines, + period: MarketPeriodOf, + deadlines: DeadlinesOf, metadata: MultiHash, market_type: MarketType, dispute_mechanism: Option, @@ -694,7 +689,7 @@ mod pallet { old_market.resolved_outcome, old_market.bonds, )?; - let edited_market = market_builder.clone().build(); + let edited_market = market_builder.clone().build()?; >::mutate_market(&market_id, |market| { *market = edited_market.clone(); Ok(()) @@ -1075,8 +1070,8 @@ mod pallet { base_asset: AssetOf, creator_fee: Perbill, oracle: T::AccountId, - period: MarketPeriod>, - deadlines: Deadlines, + period: MarketPeriodOf, + deadlines: DeadlinesOf, metadata: MultiHash, market_type: MarketType, dispute_mechanism: Option, @@ -1155,10 +1150,7 @@ mod pallet { let get_new_period = |block_period, time_frame_period| - -> Result< - MarketPeriod>, - DispatchError, - > { + -> Result, DispatchError> { match &market.period { MarketPeriod::Block(range) => { let close_time = now_block.saturating_add(block_period); @@ -2097,8 +2089,8 @@ mod pallet { base_asset: AssetOf, creator_fee: Perbill, oracle: T::AccountId, - period: MarketPeriod>, - deadlines: Deadlines, + period: MarketPeriodOf, + deadlines: DeadlinesOf, metadata: MultiHash, creation: MarketCreation, market_type: MarketType, @@ -2402,9 +2394,7 @@ mod pallet { Ok(()) } - fn ensure_market_period_is_valid( - period: &MarketPeriod>, - ) -> DispatchResult { + fn ensure_market_period_is_valid(period: &MarketPeriodOf) -> DispatchResult { // The start of the market is allowed to be in the past (this results in the market // being active immediately), but the market's end must be at least one block/time // frame in the future. @@ -2439,7 +2429,7 @@ mod pallet { } fn ensure_market_deadlines_are_valid( - deadlines: &Deadlines, + deadlines: &DeadlinesOf, trusted: bool, ) -> DispatchResult { ensure!( @@ -2894,8 +2884,8 @@ mod pallet { creator: T::AccountId, creator_fee: Perbill, oracle: T::AccountId, - period: MarketPeriod>, - deadlines: Deadlines, + period: MarketPeriodOf, + deadlines: DeadlinesOf, metadata: MultiHash, creation: MarketCreation, market_type: MarketType, @@ -2904,7 +2894,7 @@ mod pallet { report: Option>, resolved_outcome: Option, bonds: MarketBondsOf, - ) -> Result, DispatchError> { + ) -> Result, DispatchError> { let valid_base_asset = match base_asset { Asset::Ztg => true, #[cfg(feature = "parachain")] diff --git a/zrml/prediction-markets/src/types/market_builder.rs b/zrml/prediction-markets/src/types/market_builder.rs index dd47912a6..142814bdb 100644 --- a/zrml/prediction-markets/src/types/market_builder.rs +++ b/zrml/prediction-markets/src/types/market_builder.rs @@ -15,8 +15,12 @@ // You should have received a copy of the GNU General Public License // along with Zeitgeist. If not, see . +use crate::{ + AccountIdOf, AssetOf, BalanceOf, BlockNumberOf, Config, DeadlinesOf, EarlyCloseOf, + MarketBondsOf, MarketIdOf, MarketOf, MarketPeriodOf, MomentOf, ReportOf, +}; use alloc::vec::Vec; -use sp_runtime::Perbill; +use sp_runtime::{DispatchError, Perbill}; use zeitgeist_primitives::{ traits::MarketBuilder, types::{ @@ -36,28 +40,33 @@ use zeitgeist_primitives::{ /// * `A`: The asset type. /// * `MI`: The market ID type. #[derive(Clone)] -pub struct PredictionMarketBuilder { - pub market_id: Option, - pub base_asset: Option, - pub creator: Option, +pub struct PredictionMarketBuilder +where + T: Config, +{ + pub market_id: Option>, + pub base_asset: Option>, + pub creator: Option>, pub creation: Option, pub creator_fee: Option, - pub oracle: Option, + pub oracle: Option>, pub metadata: Option>, pub market_type: Option, - pub period: Option>, - pub deadlines: Option>, + pub period: Option>, + pub deadlines: Option>, pub scoring_rule: Option, pub status: Option, - pub report: Option>>, + pub report: Option>>, pub resolved_outcome: Option>, pub dispute_mechanism: Option>, - pub bonds: Option>, - - pub early_close: Option>>, + pub bonds: Option>, + pub early_close: Option>>, } -impl PredictionMarketBuilder { +impl PredictionMarketBuilder +where + T: Config, +{ pub(crate) fn new() -> Self { PredictionMarketBuilder { market_id: None, @@ -92,12 +101,21 @@ macro_rules! impl_builder_methods { } } -impl MarketBuilder - for PredictionMarketBuilder +impl + MarketBuilder< + AccountIdOf, + BalanceOf, + BlockNumberOf, + MomentOf, + AssetOf, + MarketIdOf, + > for PredictionMarketBuilder +where + T: Config, { - fn build(self) -> Market { + fn build(self) -> Result, DispatchError> { // TODO Remove unwraps, make build return result - Market { + Ok(Market { market_id: self.market_id.unwrap(), base_asset: self.base_asset.unwrap(), creator: self.creator.unwrap(), @@ -115,26 +133,26 @@ impl MarketBuilder dispute_mechanism: self.dispute_mechanism.unwrap(), bonds: self.bonds.unwrap(), early_close: self.early_close.unwrap(), - } + }) } impl_builder_methods! { - market_id: MI, - base_asset: A, - creator: AI, + market_id: MarketIdOf, + base_asset: AssetOf, + creator: AccountIdOf, creation: MarketCreation, creator_fee: Perbill, - oracle: AI, + oracle: AccountIdOf, metadata: Vec, market_type: MarketType, - period: MarketPeriod, - deadlines: Deadlines, + period: MarketPeriodOf, + deadlines: DeadlinesOf, scoring_rule: ScoringRule, status: MarketStatus, - report: Option>, + report: Option>, resolved_outcome: Option, dispute_mechanism: Option, - bonds: MarketBonds, - early_close: Option> + bonds: MarketBondsOf, + early_close: Option> } } From 86dc862c88dbea866ae440df351ff5e9801bc1dd Mon Sep 17 00:00:00 2001 From: Malte Kliemann Date: Wed, 7 Feb 2024 01:46:17 +0100 Subject: [PATCH 07/10] Let `build` raise an error on incomplete data. --- zrml/prediction-markets/src/lib.rs | 2 + .../src/types/market_builder.rs | 96 +++++++++---------- 2 files changed, 50 insertions(+), 48 deletions(-) diff --git a/zrml/prediction-markets/src/lib.rs b/zrml/prediction-markets/src/lib.rs index 569b0b024..fecbda3d3 100644 --- a/zrml/prediction-markets/src/lib.rs +++ b/zrml/prediction-markets/src/lib.rs @@ -1855,6 +1855,8 @@ mod pallet { MarketNotInCloseTimeFrameList, /// The market period end was not already reached yet. MarketPeriodEndNotAlreadyReachedYet, + /// Unexpectedly failed to build a market due to missing data. + IncompleteMarketBuilder, } #[pallet::event] diff --git a/zrml/prediction-markets/src/types/market_builder.rs b/zrml/prediction-markets/src/types/market_builder.rs index 142814bdb..4afdea804 100644 --- a/zrml/prediction-markets/src/types/market_builder.rs +++ b/zrml/prediction-markets/src/types/market_builder.rs @@ -16,7 +16,7 @@ // along with Zeitgeist. If not, see . use crate::{ - AccountIdOf, AssetOf, BalanceOf, BlockNumberOf, Config, DeadlinesOf, EarlyCloseOf, + AccountIdOf, AssetOf, BalanceOf, BlockNumberOf, Config, DeadlinesOf, EarlyCloseOf, Error, MarketBondsOf, MarketIdOf, MarketOf, MarketPeriodOf, MomentOf, ReportOf, }; use alloc::vec::Vec; @@ -24,43 +24,34 @@ use sp_runtime::{DispatchError, Perbill}; use zeitgeist_primitives::{ traits::MarketBuilder, types::{ - Deadlines, EarlyClose, Market, MarketBonds, MarketCreation, MarketDisputeMechanism, - MarketPeriod, MarketStatus, MarketType, OutcomeReport, Report, ScoringRule, + Market, MarketCreation, MarketDisputeMechanism, MarketStatus, MarketType, OutcomeReport, + ScoringRule, }, }; /// Fully-fledged mutably referenced market builder struct. -/// -/// # Generics -/// -/// * `AI`: The account ID type. -/// * `BA`: The balance type. -/// * `BN`: The block number type. -/// * `M`: The moment/time type. -/// * `A`: The asset type. -/// * `MI`: The market ID type. #[derive(Clone)] -pub struct PredictionMarketBuilder +pub(crate) struct PredictionMarketBuilder where T: Config, { - pub market_id: Option>, - pub base_asset: Option>, - pub creator: Option>, - pub creation: Option, - pub creator_fee: Option, - pub oracle: Option>, - pub metadata: Option>, - pub market_type: Option, - pub period: Option>, - pub deadlines: Option>, - pub scoring_rule: Option, - pub status: Option, - pub report: Option>>, - pub resolved_outcome: Option>, - pub dispute_mechanism: Option>, - pub bonds: Option>, - pub early_close: Option>>, + market_id: Option>, + base_asset: Option>, + creator: Option>, + creation: Option, + creator_fee: Option, + oracle: Option>, + metadata: Option>, + market_type: Option, + period: Option>, + deadlines: Option>, + scoring_rule: Option, + status: Option, + report: Option>>, + resolved_outcome: Option>, + dispute_mechanism: Option>, + bonds: Option>, + early_close: Option>>, } impl PredictionMarketBuilder @@ -90,6 +81,8 @@ where } } +/// Implements setter methods for a mutably referenced builder struct. Fields are specified using +/// the pattern `{ field: type, ... }`. macro_rules! impl_builder_methods { ($($field:ident: $type:ty),*) => { $( @@ -101,6 +94,14 @@ macro_rules! impl_builder_methods { } } +/// Unwraps `opt` and throws `IncompleteMarketBuilder` in case of failure. +fn ok_or_incomplete(opt: Option) -> Result +where + T: Config, +{ + opt.ok_or(Error::::IncompleteMarketBuilder.into()) +} + impl MarketBuilder< AccountIdOf, @@ -114,25 +115,24 @@ where T: Config, { fn build(self) -> Result, DispatchError> { - // TODO Remove unwraps, make build return result Ok(Market { - market_id: self.market_id.unwrap(), - base_asset: self.base_asset.unwrap(), - creator: self.creator.unwrap(), - creation: self.creation.unwrap(), - creator_fee: self.creator_fee.unwrap(), - oracle: self.oracle.unwrap(), - metadata: self.metadata.unwrap(), - market_type: self.market_type.unwrap(), - period: self.period.unwrap(), - deadlines: self.deadlines.unwrap(), - scoring_rule: self.scoring_rule.unwrap(), - status: self.status.unwrap(), - report: self.report.unwrap(), - resolved_outcome: self.resolved_outcome.unwrap(), - dispute_mechanism: self.dispute_mechanism.unwrap(), - bonds: self.bonds.unwrap(), - early_close: self.early_close.unwrap(), + market_id: ok_or_incomplete::(self.market_id)?, + base_asset: ok_or_incomplete::(self.base_asset)?, + creator: ok_or_incomplete::(self.creator)?, + creation: ok_or_incomplete::(self.creation)?, + creator_fee: ok_or_incomplete::(self.creator_fee)?, + oracle: ok_or_incomplete::(self.oracle)?, + metadata: ok_or_incomplete::(self.metadata)?, + market_type: ok_or_incomplete::(self.market_type)?, + period: ok_or_incomplete::(self.period)?, + deadlines: ok_or_incomplete::(self.deadlines)?, + scoring_rule: ok_or_incomplete::(self.scoring_rule)?, + status: ok_or_incomplete::(self.status)?, + report: ok_or_incomplete::(self.report)?, + resolved_outcome: ok_or_incomplete::(self.resolved_outcome)?, + dispute_mechanism: ok_or_incomplete::(self.dispute_mechanism)?, + bonds: ok_or_incomplete::(self.bonds)?, + early_close: ok_or_incomplete::(self.early_close)?, }) } From 5e403c6a7332bf0d2466b73d9b80e15fc1aaaf08 Mon Sep 17 00:00:00 2001 From: Malte Kliemann Date: Wed, 7 Feb 2024 01:46:50 +0100 Subject: [PATCH 08/10] Fix formatting --- zrml/prediction-markets/src/lib.rs | 29 ++++++++++++++--------------- 1 file changed, 14 insertions(+), 15 deletions(-) diff --git a/zrml/prediction-markets/src/lib.rs b/zrml/prediction-markets/src/lib.rs index fecbda3d3..814eae9cd 100644 --- a/zrml/prediction-markets/src/lib.rs +++ b/zrml/prediction-markets/src/lib.rs @@ -1148,22 +1148,21 @@ mod pallet { let now_block = >::block_number(); let now_time = >::now(); - let get_new_period = |block_period, - time_frame_period| - -> Result, DispatchError> { - match &market.period { - MarketPeriod::Block(range) => { - let close_time = now_block.saturating_add(block_period); - ensure!(close_time < range.end, Error::::EarlyCloseRequestTooLate); - Ok(MarketPeriod::Block(range.start..close_time)) - } - MarketPeriod::Timestamp(range) => { - let close_time = now_time.saturating_add(time_frame_period); - ensure!(close_time < range.end, Error::::EarlyCloseRequestTooLate); - Ok(MarketPeriod::Timestamp(range.start..close_time)) + let get_new_period = + |block_period, time_frame_period| -> Result, DispatchError> { + match &market.period { + MarketPeriod::Block(range) => { + let close_time = now_block.saturating_add(block_period); + ensure!(close_time < range.end, Error::::EarlyCloseRequestTooLate); + Ok(MarketPeriod::Block(range.start..close_time)) + } + MarketPeriod::Timestamp(range) => { + let close_time = now_time.saturating_add(time_frame_period); + ensure!(close_time < range.end, Error::::EarlyCloseRequestTooLate); + Ok(MarketPeriod::Timestamp(range.start..close_time)) + } } - } - }; + }; let new_period = if let Some(p) = &market.early_close { ensure!(is_authorized, Error::::OnlyAuthorizedCanScheduleEarlyClose); From 052b0476ff25e9005040ab19848f70c42818d958 Mon Sep 17 00:00:00 2001 From: Malte Kliemann Date: Fri, 9 Feb 2024 00:10:29 +0100 Subject: [PATCH 09/10] Refactor `MarketBuilder` --- primitives/src/traits/market_builder.rs | 8 +- .../src/traits/market_commons_pallet_api.rs | 4 +- primitives/src/types.rs | 2 - .../src/types/primitive_market_builder.rs | 122 -------------- zrml/market-commons/src/lib.rs | 18 +- zrml/market-commons/src/tests.rs | 61 +++---- zrml/prediction-markets/src/lib.rs | 13 +- .../src/types/market_builder.rs | 158 ------------------ zrml/prediction-markets/src/types/mod.rs | 20 --- 9 files changed, 49 insertions(+), 357 deletions(-) delete mode 100644 primitives/src/types/primitive_market_builder.rs delete mode 100644 zrml/prediction-markets/src/types/market_builder.rs delete mode 100644 zrml/prediction-markets/src/types/mod.rs diff --git a/primitives/src/traits/market_builder.rs b/primitives/src/traits/market_builder.rs index c956bf7fc..7f682c8d7 100644 --- a/primitives/src/traits/market_builder.rs +++ b/primitives/src/traits/market_builder.rs @@ -23,7 +23,7 @@ use alloc::vec::Vec; use sp_runtime::{DispatchError, Perbill}; macro_rules! builder_methods { - ($($field:ident: $type:ty),*) => { + ($($field:ident: $type:ty),* $(,)?) => { $(fn $field(&mut self, $field: $type) -> &mut Self;)* } } @@ -32,11 +32,11 @@ macro_rules! builder_methods { /// the usual calling pattern is: /// /// ```ignore -/// let builder = MarketBuilderImpl::new(args...); +/// let builder = MarketBuilderImpl::new(); /// builder.field1(value1).field2(value2); /// builder.clone().build() /// ``` -pub trait MarketBuilder { +pub trait MarketBuilderTrait { fn build(self) -> Result, DispatchError>; builder_methods! { @@ -56,6 +56,6 @@ pub trait MarketBuilder { resolved_outcome: Option, dispute_mechanism: Option, bonds: MarketBonds, - early_close: Option> + early_close: Option>, } } diff --git a/primitives/src/traits/market_commons_pallet_api.rs b/primitives/src/traits/market_commons_pallet_api.rs index aa07cb2f5..dac3402f9 100644 --- a/primitives/src/traits/market_commons_pallet_api.rs +++ b/primitives/src/traits/market_commons_pallet_api.rs @@ -19,7 +19,7 @@ #![allow(clippy::type_complexity)] use crate::{ - traits::MarketBuilder, + traits::MarketBuilderTrait, types::{Asset, Market, PoolId}, }; use frame_support::{ @@ -100,7 +100,7 @@ pub trait MarketCommonsPalletApi { market_builder: U, ) -> Result<(Self::MarketId, MarketOf), DispatchError> where - U: MarketBuilder< + U: MarketBuilderTrait< Self::AccountId, Self::Balance, Self::BlockNumber, diff --git a/primitives/src/types.rs b/primitives/src/types.rs index e2460cd9c..616357787 100644 --- a/primitives/src/types.rs +++ b/primitives/src/types.rs @@ -17,13 +17,11 @@ // along with Zeitgeist. If not, see . pub mod multi_hash; -pub mod primitive_market_builder; pub mod result_with_weight_info; pub mod type_aliases; pub mod xcm_metadata; pub use multi_hash::*; -pub use primitive_market_builder::*; pub use result_with_weight_info::*; pub use type_aliases::*; pub use xcm_metadata::*; diff --git a/primitives/src/types/primitive_market_builder.rs b/primitives/src/types/primitive_market_builder.rs deleted file mode 100644 index 34b0ea959..000000000 --- a/primitives/src/types/primitive_market_builder.rs +++ /dev/null @@ -1,122 +0,0 @@ -// Copyright 2024 Forecasting Technologies LTD. -// -// This file is part of Zeitgeist. -// -// Zeitgeist is free software: you can redistribute it and/or modify it -// under the terms of the GNU General Public License as published by the -// Free Software Foundation, either version 3 of the License, or (at -// your option) any later version. -// -// Zeitgeist is distributed in the hope that it will be useful, but -// WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU -// General Public License for more details. -// -// You should have received a copy of the GNU General Public License -// along with Zeitgeist. If not, see . - -use crate::{ - traits::MarketBuilder, - types::{ - Deadlines, EarlyClose, Market, MarketBonds, MarketCreation, MarketDisputeMechanism, - MarketPeriod, MarketStatus, MarketType, OutcomeReport, Report, ScoringRule, - }, -}; -use alloc::vec::Vec; -use sp_runtime::{DispatchError, Perbill}; - -/// A sample market builder struct used for testing. No verification is done when calling `build()`; -/// use at your own risk! -/// -/// Fields are deliberately kept public to allow the straightforward construction of builder objects -/// in spots where correctness isn't the primary concern. -/// -/// # Generics -/// -/// * `AI`: The account ID type. -/// * `BA`: The balance type. -/// * `BN`: The block number type. -/// * `M`: The moment/time type. -/// * `A`: The asset type. -/// * `MI`: The market ID type. -#[derive(Clone)] -pub struct PrimitiveMarketBuilder { - pub market_id: Option, - pub base_asset: A, - pub creator: AI, - pub creation: MarketCreation, - pub creator_fee: Perbill, - pub oracle: AI, - pub metadata: Vec, - pub market_type: MarketType, - pub period: MarketPeriod, - pub deadlines: Deadlines, - pub scoring_rule: ScoringRule, - pub status: MarketStatus, - pub report: Option>, - pub resolved_outcome: Option, - pub dispute_mechanism: Option, - pub bonds: MarketBonds, - pub early_close: Option>, -} - -macro_rules! impl_builder_methods { - ($($field:ident: $type:ty),*) => { - $( - fn $field(&mut self, $field: $type) -> &mut Self { - self.$field = $field; - self - } - )* - } -} - -impl MarketBuilder - for PrimitiveMarketBuilder -{ - fn build(self) -> Result, DispatchError> { - Ok(Market { - market_id: self.market_id.unwrap(), - base_asset: self.base_asset, - creator: self.creator, - creation: self.creation, - creator_fee: self.creator_fee, - oracle: self.oracle, - metadata: self.metadata, - market_type: self.market_type, - period: self.period, - deadlines: self.deadlines, - scoring_rule: self.scoring_rule, - status: self.status, - report: self.report, - resolved_outcome: self.resolved_outcome, - dispute_mechanism: self.dispute_mechanism, - bonds: self.bonds, - early_close: self.early_close, - }) - } - - fn market_id(&mut self, market_id: MI) -> &mut Self { - self.market_id = Some(market_id); - self - } - - impl_builder_methods! { - base_asset: A, - creator: AI, - creation: MarketCreation, - creator_fee: Perbill, - oracle: AI, - metadata: Vec, - market_type: MarketType, - period: MarketPeriod, - deadlines: Deadlines, - scoring_rule: ScoringRule, - status: MarketStatus, - report: Option>, - resolved_outcome: Option, - dispute_mechanism: Option, - bonds: MarketBonds, - early_close: Option> - } -} diff --git a/zrml/market-commons/src/lib.rs b/zrml/market-commons/src/lib.rs index ef4470be5..9d05179c2 100644 --- a/zrml/market-commons/src/lib.rs +++ b/zrml/market-commons/src/lib.rs @@ -24,6 +24,7 @@ extern crate alloc; pub mod migrations; mod mock; mod tests; +pub mod types; pub use pallet::*; pub use zeitgeist_primitives::traits::MarketCommonsPalletApi; @@ -50,8 +51,8 @@ mod pallet { }; use zeitgeist_primitives::{ math::checked_ops_res::CheckedAddRes, - traits::MarketBuilder, - types::{Asset, Market, PoolId}, + traits::MarketBuilderTrait, + types::{Asset, Deadlines, EarlyClose, Market, MarketBonds, MarketPeriod, PoolId, Report}, }; /// The current storage version. @@ -61,6 +62,7 @@ mod pallet { pub(crate) type AssetOf = Asset>; pub(crate) type BalanceOf = ::Balance; pub(crate) type BlockNumberOf = ::BlockNumber; + pub(crate) type MarketIdOf = ::MarketId; pub(crate) type MarketOf = Market< AccountIdOf, BalanceOf, @@ -69,8 +71,12 @@ mod pallet { AssetOf, MarketIdOf, >; - pub type MarketIdOf = ::MarketId; - pub type MomentOf = <::Timestamp as frame_support::traits::Time>::Moment; + pub(crate) type MomentOf = <::Timestamp as frame_support::traits::Time>::Moment; + pub(crate) type DeadlinesOf = Deadlines>; + pub(crate) type EarlyCloseOf = EarlyClose, MomentOf>; + pub(crate) type MarketBondsOf = MarketBonds, BalanceOf>; + pub(crate) type MarketPeriodOf = MarketPeriod, MomentOf>; + pub(crate) type ReportOf = Report, BlockNumberOf>; #[pallet::call] impl Pallet {} @@ -112,6 +118,8 @@ mod pallet { NoReport, /// There's a pool registered for this market already. PoolAlreadyExists, + /// Unexpectedly failed to build a market due to missing data. + IncompleteMarketBuilder, } #[pallet::hooks] @@ -193,7 +201,7 @@ mod pallet { mut market_builder: U, ) -> Result<(Self::MarketId, MarketOf), DispatchError> where - U: MarketBuilder< + U: MarketBuilderTrait< Self::AccountId, Self::Balance, Self::BlockNumber, diff --git a/zrml/market-commons/src/tests.rs b/zrml/market-commons/src/tests.rs index b74fdfd77..793559b44 100644 --- a/zrml/market-commons/src/tests.rs +++ b/zrml/market-commons/src/tests.rs @@ -20,60 +20,51 @@ use crate::{ mock::{ExtBuilder, MarketCommons, Runtime}, - AccountIdOf, AssetOf, BalanceOf, BlockNumberOf, MarketCounter, MarketIdOf, Markets, MomentOf, + types::MarketBuilder, + AccountIdOf, MarketCounter, Markets, }; use frame_support::{assert_err, assert_noop, assert_ok}; use sp_runtime::{DispatchError, Perbill}; use zeitgeist_primitives::{ - traits::{MarketBuilder, MarketCommonsPalletApi}, + traits::{MarketBuilderTrait, MarketCommonsPalletApi}, types::{ Asset, Deadlines, MarketBonds, MarketCreation, MarketDisputeMechanism, MarketPeriod, - MarketStatus, MarketType, PrimitiveMarketBuilder, ScoringRule, + MarketStatus, MarketType, ScoringRule, }, }; -type PrimitiveMarketBuilderOf = PrimitiveMarketBuilder< - AccountIdOf, - BalanceOf, - BlockNumberOf, - MomentOf, - AssetOf, - MarketIdOf, ->; - // Creates a sample market builder. We use the `oracle` field to tell markets apart from each other. -// For testing purposes, we allow `market_id` to be defined, as well. -fn create_market_builder(oracle: AccountIdOf) -> PrimitiveMarketBuilderOf { - PrimitiveMarketBuilder { - market_id: None, - base_asset: Asset::Ztg, - creation: MarketCreation::Permissionless, - creator_fee: Perbill::zero(), - creator: 0, - market_type: MarketType::Scalar(0..=100), - dispute_mechanism: Some(MarketDisputeMechanism::Authorized), - metadata: vec![], - oracle, - period: MarketPeriod::Block(0..100), - deadlines: Deadlines { +fn create_market_builder(oracle: AccountIdOf) -> MarketBuilder { + let mut market_builder = MarketBuilder::new(); + market_builder + .base_asset(Asset::Ztg) + .creation(MarketCreation::Permissionless) + .creator_fee(Perbill::zero()) + .creator(0) + .market_type(MarketType::Scalar(0..=100)) + .dispute_mechanism(Some(MarketDisputeMechanism::Authorized)) + .metadata(vec![]) + .oracle(oracle) + .period(MarketPeriod::Block(0..100)) + .deadlines(Deadlines { grace_period: 1_u64, oracle_duration: 1_u64, dispute_duration: 1_u64, - }, - report: None, - resolved_outcome: None, - scoring_rule: ScoringRule::Lmsr, - status: MarketStatus::Disputed, - bonds: MarketBonds { + }) + .report(None) + .resolved_outcome(None) + .scoring_rule(ScoringRule::Lmsr) + .status(MarketStatus::Disputed) + .bonds(MarketBonds { creation: None, oracle: None, outsider: None, dispute: None, close_dispute: None, close_request: None, - }, - early_close: None, - } + }) + .early_close(None); + market_builder } #[test] diff --git a/zrml/prediction-markets/src/lib.rs b/zrml/prediction-markets/src/lib.rs index 814eae9cd..851b6f1df 100644 --- a/zrml/prediction-markets/src/lib.rs +++ b/zrml/prediction-markets/src/lib.rs @@ -28,7 +28,6 @@ pub mod migrations; pub mod mock; pub mod orml_asset_registry; mod tests; -mod types; pub mod weights; pub use pallet::*; @@ -56,7 +55,6 @@ mod pallet { #[cfg(feature = "parachain")] use {orml_traits::asset_registry::Inspect, zeitgeist_primitives::types::CustomMetadata}; - use crate::types::PredictionMarketBuilder; use orml_traits::{MultiCurrency, NamedMultiReservableCurrency}; use sp_arithmetic::per_things::{Perbill, Percent}; use sp_runtime::{ @@ -67,7 +65,7 @@ mod pallet { constants::MILLISECS_PER_BLOCK, traits::{ CompleteSetOperationsApi, DeployPoolApi, DisputeApi, DisputeMaxWeightApi, - DisputeResolutionApi, MarketBuilder, ZeitgeistAssetManager, + DisputeResolutionApi, MarketBuilderTrait, ZeitgeistAssetManager, }, types::{ Asset, Bond, Deadlines, EarlyClose, EarlyCloseState, GlobalDisputeItem, Market, @@ -78,7 +76,7 @@ mod pallet { }; use zrml_global_disputes::{types::InitialItem, GlobalDisputesPalletApi}; use zrml_liquidity_mining::LiquidityMiningPalletApi; - use zrml_market_commons::MarketCommonsPalletApi; + use zrml_market_commons::{types::MarketBuilder, MarketCommonsPalletApi}; /// The current storage version. const STORAGE_VERSION: StorageVersion = StorageVersion::new(8); @@ -95,7 +93,6 @@ mod pallet { pub(crate) type BlockNumberOf = ::BlockNumber; pub(crate) type CacheSize = ConstU32<64>; pub(crate) type DeadlinesOf = Deadlines>; - pub(crate) type EarlyCloseOf = EarlyClose, MomentOf>; pub(crate) type EditReason = BoundedVec::MaxEditReasonLen>; pub(crate) type InitialItemOf = InitialItem, BalanceOf>; pub(crate) type MarketBondsOf = MarketBonds, BalanceOf>; @@ -1854,8 +1851,6 @@ mod pallet { MarketNotInCloseTimeFrameList, /// The market period end was not already reached yet. MarketPeriodEndNotAlreadyReachedYet, - /// Unexpectedly failed to build a market due to missing data. - IncompleteMarketBuilder, } #[pallet::event] @@ -2895,7 +2890,7 @@ mod pallet { report: Option>, resolved_outcome: Option, bonds: MarketBondsOf, - ) -> Result, DispatchError> { + ) -> Result, DispatchError> { let valid_base_asset = match base_asset { Asset::Ztg => true, #[cfg(feature = "parachain")] @@ -2921,7 +2916,7 @@ mod pallet { MarketCreation::Permissionless => MarketStatus::Active, MarketCreation::Advised => MarketStatus::Proposed, }; - let mut market_builder = PredictionMarketBuilder::new(); + let mut market_builder = MarketBuilder::new(); market_builder .base_asset(base_asset) .creator(creator) diff --git a/zrml/prediction-markets/src/types/market_builder.rs b/zrml/prediction-markets/src/types/market_builder.rs deleted file mode 100644 index 4afdea804..000000000 --- a/zrml/prediction-markets/src/types/market_builder.rs +++ /dev/null @@ -1,158 +0,0 @@ -// Copyright 2024 Forecasting Technologies LTD. -// -// This file is part of Zeitgeist. -// -// Zeitgeist is free software: you can redistribute it and/or modify it -// under the terms of the GNU General Public License as published by the -// Free Software Foundation, either version 3 of the License, or (at -// your option) any later version. -// -// Zeitgeist is distributed in the hope that it will be useful, but -// WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU -// General Public License for more details. -// -// You should have received a copy of the GNU General Public License -// along with Zeitgeist. If not, see . - -use crate::{ - AccountIdOf, AssetOf, BalanceOf, BlockNumberOf, Config, DeadlinesOf, EarlyCloseOf, Error, - MarketBondsOf, MarketIdOf, MarketOf, MarketPeriodOf, MomentOf, ReportOf, -}; -use alloc::vec::Vec; -use sp_runtime::{DispatchError, Perbill}; -use zeitgeist_primitives::{ - traits::MarketBuilder, - types::{ - Market, MarketCreation, MarketDisputeMechanism, MarketStatus, MarketType, OutcomeReport, - ScoringRule, - }, -}; - -/// Fully-fledged mutably referenced market builder struct. -#[derive(Clone)] -pub(crate) struct PredictionMarketBuilder -where - T: Config, -{ - market_id: Option>, - base_asset: Option>, - creator: Option>, - creation: Option, - creator_fee: Option, - oracle: Option>, - metadata: Option>, - market_type: Option, - period: Option>, - deadlines: Option>, - scoring_rule: Option, - status: Option, - report: Option>>, - resolved_outcome: Option>, - dispute_mechanism: Option>, - bonds: Option>, - early_close: Option>>, -} - -impl PredictionMarketBuilder -where - T: Config, -{ - pub(crate) fn new() -> Self { - PredictionMarketBuilder { - market_id: None, - base_asset: None, - creator: None, - creation: None, - creator_fee: None, - oracle: None, - metadata: None, - market_type: None, - period: None, - deadlines: None, - scoring_rule: None, - status: None, - report: None, - resolved_outcome: None, - dispute_mechanism: None, - bonds: None, - early_close: None, - } - } -} - -/// Implements setter methods for a mutably referenced builder struct. Fields are specified using -/// the pattern `{ field: type, ... }`. -macro_rules! impl_builder_methods { - ($($field:ident: $type:ty),*) => { - $( - fn $field(&mut self, $field: $type) -> &mut Self { - self.$field = Some($field); - self - } - )* - } -} - -/// Unwraps `opt` and throws `IncompleteMarketBuilder` in case of failure. -fn ok_or_incomplete(opt: Option) -> Result -where - T: Config, -{ - opt.ok_or(Error::::IncompleteMarketBuilder.into()) -} - -impl - MarketBuilder< - AccountIdOf, - BalanceOf, - BlockNumberOf, - MomentOf, - AssetOf, - MarketIdOf, - > for PredictionMarketBuilder -where - T: Config, -{ - fn build(self) -> Result, DispatchError> { - Ok(Market { - market_id: ok_or_incomplete::(self.market_id)?, - base_asset: ok_or_incomplete::(self.base_asset)?, - creator: ok_or_incomplete::(self.creator)?, - creation: ok_or_incomplete::(self.creation)?, - creator_fee: ok_or_incomplete::(self.creator_fee)?, - oracle: ok_or_incomplete::(self.oracle)?, - metadata: ok_or_incomplete::(self.metadata)?, - market_type: ok_or_incomplete::(self.market_type)?, - period: ok_or_incomplete::(self.period)?, - deadlines: ok_or_incomplete::(self.deadlines)?, - scoring_rule: ok_or_incomplete::(self.scoring_rule)?, - status: ok_or_incomplete::(self.status)?, - report: ok_or_incomplete::(self.report)?, - resolved_outcome: ok_or_incomplete::(self.resolved_outcome)?, - dispute_mechanism: ok_or_incomplete::(self.dispute_mechanism)?, - bonds: ok_or_incomplete::(self.bonds)?, - early_close: ok_or_incomplete::(self.early_close)?, - }) - } - - impl_builder_methods! { - market_id: MarketIdOf, - base_asset: AssetOf, - creator: AccountIdOf, - creation: MarketCreation, - creator_fee: Perbill, - oracle: AccountIdOf, - metadata: Vec, - market_type: MarketType, - period: MarketPeriodOf, - deadlines: DeadlinesOf, - scoring_rule: ScoringRule, - status: MarketStatus, - report: Option>, - resolved_outcome: Option, - dispute_mechanism: Option, - bonds: MarketBondsOf, - early_close: Option> - } -} diff --git a/zrml/prediction-markets/src/types/mod.rs b/zrml/prediction-markets/src/types/mod.rs deleted file mode 100644 index 47baa4a61..000000000 --- a/zrml/prediction-markets/src/types/mod.rs +++ /dev/null @@ -1,20 +0,0 @@ -// Copyright 2024 Forecasting Technologies LTD. -// -// This file is part of Zeitgeist. -// -// Zeitgeist is free software: you can redistribute it and/or modify it -// under the terms of the GNU General Public License as published by the -// Free Software Foundation, either version 3 of the License, or (at -// your option) any later version. -// -// Zeitgeist is distributed in the hope that it will be useful, but -// WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU -// General Public License for more details. -// -// You should have received a copy of the GNU General Public License -// along with Zeitgeist. If not, see . - -mod market_builder; - -pub(crate) use market_builder::*; From 6a22be8fb6f604a6f86f0f366547e5493ffdd50c Mon Sep 17 00:00:00 2001 From: Malte Kliemann Date: Fri, 9 Feb 2024 00:16:01 +0100 Subject: [PATCH 10/10] Add missing files --- .../src/types/market_builder.rs | 167 ++++++++++++++++++ zrml/market-commons/src/types/mod.rs | 20 +++ 2 files changed, 187 insertions(+) create mode 100644 zrml/market-commons/src/types/market_builder.rs create mode 100644 zrml/market-commons/src/types/mod.rs diff --git a/zrml/market-commons/src/types/market_builder.rs b/zrml/market-commons/src/types/market_builder.rs new file mode 100644 index 000000000..dcf28a2c4 --- /dev/null +++ b/zrml/market-commons/src/types/market_builder.rs @@ -0,0 +1,167 @@ +// Copyright 2024 Forecasting Technologies LTD. +// +// This file is part of Zeitgeist. +// +// Zeitgeist is free software: you can redistribute it and/or modify it +// under the terms of the GNU General Public License as published by the +// Free Software Foundation, either version 3 of the License, or (at +// your option) any later version. +// +// Zeitgeist is distributed in the hope that it will be useful, but +// WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +// General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with Zeitgeist. If not, see . + +use crate::{ + AccountIdOf, AssetOf, BalanceOf, BlockNumberOf, Config, DeadlinesOf, EarlyCloseOf, Error, + MarketBondsOf, MarketIdOf, MarketOf, MarketPeriodOf, MomentOf, ReportOf, +}; +use alloc::vec::Vec; +use sp_runtime::{DispatchError, Perbill}; +use zeitgeist_primitives::{ + traits::MarketBuilderTrait, + types::{ + Market, MarketCreation, MarketDisputeMechanism, MarketStatus, MarketType, OutcomeReport, + ScoringRule, + }, +}; + +/// Fully-fledged mutably referenced builder struct for `Market`. +#[derive(Clone)] +pub struct MarketBuilder +where + T: Config, +{ + market_id: Option>, + base_asset: Option>, + creator: Option>, + creation: Option, + creator_fee: Option, + oracle: Option>, + metadata: Option>, + market_type: Option, + period: Option>, + deadlines: Option>, + scoring_rule: Option, + status: Option, + report: Option>>, + resolved_outcome: Option>, + dispute_mechanism: Option>, + bonds: Option>, + early_close: Option>>, +} + +impl MarketBuilder +where + T: Config, +{ + pub fn new() -> Self { + MarketBuilder { + market_id: None, + base_asset: None, + creator: None, + creation: None, + creator_fee: None, + oracle: None, + metadata: None, + market_type: None, + period: None, + deadlines: None, + scoring_rule: None, + status: None, + report: None, + resolved_outcome: None, + dispute_mechanism: None, + bonds: None, + early_close: None, + } + } +} + +impl Default for MarketBuilder +where + T: Config, +{ + fn default() -> Self { + Self::new() + } +} + +/// Implements setter methods for a mutably referenced builder struct. Fields are specified using +/// the pattern `{ field: type, ... }`. +macro_rules! impl_builder_methods { + ($($field:ident: $type:ty),* $(,)?) => { + $( + fn $field(&mut self, $field: $type) -> &mut Self { + self.$field = Some($field); + self + } + )* + } +} + +/// Unwraps `opt` and throws `IncompleteMarketBuilder` in case of failure. +fn ok_or_incomplete(opt: Option) -> Result +where + T: Config, +{ + opt.ok_or(Error::::IncompleteMarketBuilder.into()) +} + +impl + MarketBuilderTrait< + AccountIdOf, + BalanceOf, + BlockNumberOf, + MomentOf, + AssetOf, + MarketIdOf, + > for MarketBuilder +where + T: Config, +{ + fn build(self) -> Result, DispatchError> { + Ok(Market { + market_id: ok_or_incomplete::(self.market_id)?, + base_asset: ok_or_incomplete::(self.base_asset)?, + creator: ok_or_incomplete::(self.creator)?, + creation: ok_or_incomplete::(self.creation)?, + creator_fee: ok_or_incomplete::(self.creator_fee)?, + oracle: ok_or_incomplete::(self.oracle)?, + metadata: ok_or_incomplete::(self.metadata)?, + market_type: ok_or_incomplete::(self.market_type)?, + period: ok_or_incomplete::(self.period)?, + deadlines: ok_or_incomplete::(self.deadlines)?, + scoring_rule: ok_or_incomplete::(self.scoring_rule)?, + status: ok_or_incomplete::(self.status)?, + report: ok_or_incomplete::(self.report)?, + resolved_outcome: ok_or_incomplete::(self.resolved_outcome)?, + dispute_mechanism: ok_or_incomplete::(self.dispute_mechanism)?, + bonds: ok_or_incomplete::(self.bonds)?, + early_close: ok_or_incomplete::(self.early_close)?, + }) + } + + impl_builder_methods! { + market_id: MarketIdOf, + base_asset: AssetOf, + creator: AccountIdOf, + creation: MarketCreation, + creator_fee: Perbill, + oracle: AccountIdOf, + metadata: Vec, + market_type: MarketType, + period: MarketPeriodOf, + deadlines: DeadlinesOf, + scoring_rule: ScoringRule, + status: MarketStatus, + report: Option>, + resolved_outcome: Option, + dispute_mechanism: Option, + bonds: MarketBondsOf, + early_close: Option>, + } +} diff --git a/zrml/market-commons/src/types/mod.rs b/zrml/market-commons/src/types/mod.rs new file mode 100644 index 000000000..699f70ab2 --- /dev/null +++ b/zrml/market-commons/src/types/mod.rs @@ -0,0 +1,20 @@ +// Copyright 2024 Forecasting Technologies LTD. +// +// This file is part of Zeitgeist. +// +// Zeitgeist is free software: you can redistribute it and/or modify it +// under the terms of the GNU General Public License as published by the +// Free Software Foundation, either version 3 of the License, or (at +// your option) any later version. +// +// Zeitgeist is distributed in the hope that it will be useful, but +// WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +// General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with Zeitgeist. If not, see . + +mod market_builder; + +pub use market_builder::*;