Skip to content

Commit

Permalink
Add ID to Market struct (#1310)
Browse files Browse the repository at this point in the history
* Add market ID to `Market` struct (#1248)

* Add market ID to `Market` struct

* Add market builder struct

* Use `PredictionMarketBuilder`

* Fix issues

* Fix copyright

* Make `build` return a `Result`

* Let `build` raise an error on incomplete data.

* Fix formatting

* Refactor `MarketBuilder`

* Add missing files

* AMM-CDA-1: Switch to AmmCdaHybrid enum field (#1274)

* switch to AmmCdaHybrid enum field

* Update zrml/parimutuel/src/tests/buy.rs

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

* Update zrml/parimutuel/src/tests/claim.rs

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

* Update zrml/parimutuel/src/tests/refund.rs

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

* Update storage version to 11 and add MigrateScoringRuleAmmCdaHybrid

* Update primitives/src/market.rs

* update migration

---------

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

* merge amm-cda-3 changes

* merge amm-cda-2 changes

* merge amm-cda-4 changes

* correct clippy

* merge amm-cda-5 changes

* merge amm-cda-6 changes

* Fix Hybrid Router clippy and tests (#1291)

* glue everything together

* add hybrid router to configuration

* fix tests

* fix conditional tests

* AMM-CDA-8 Add event information for fees and aggregated amount_out (#1293)

* add event info

* use slice as function parameter

* update test amm amount out of event

* fix order book tests

* update documentation

* removed dependency

* Merge `main` into `mkl-market-id-feature` (#1302)

* Merge `main` into `mkl-mkl-market-id-feature`

* Remove unused `outcomes`

* Remove old migrations (#1301)

* AMM-CDA-9 Adds soft and hard failure distinction for AMM and order book errors (#1294)

* add event info

* use slice as function parameter

* update test amm amount out of event

* wip

* handle soft and hard failure

* add order book soft failure

* fix clippy

* fix CI

* remove swaps pallet dependency

* add compact to order book struct

* fix recursion overflow

* Migration: Add Market ID to Market (#1257)

* Add market ID to `Market` struct

* Add market builder struct

* Use `PredictionMarketBuilder`

* Fix issues

* Fix copyright

* Make `build` return a `Result`

* Let `build` raise an error on incomplete data.

* Fix formatting

* Refactor `MarketBuilder`

* Add missing files

* Add migration to new market

* Fix migration

* Fix missing import

* Fix duplicate import

* Fix formatting

* Remove unused `types/`

* Fix of Hybrid Router after asset system merge (#1309)

* wip

* use asset conversions

* adapt hybrid router to new asset system

* apply review suggestions

* fmt

* rename Asset to Assets

* update copyrights

* Make minor fixes after merge

* update hybrid router crate version

* correct orderbook spelling

* add amount is zero tests

* add price limit too high test

* add max order exceeded test

* use saturated conversion

* use saturated conversion again

* remove unused code

* Update changelog

* add tests for soft failures

* add skip order test

* add changelog for devs description

* add amm soft failure test

* add numerical soft failure test for sell

* Remove ZeitgeistAssetManager trait

* correct failing test for parachain feature

* cover remaining is zero execution path

* Fix try-runtime test

---------

Co-authored-by: Chralt <chralt.developer@gmail.com>
Co-authored-by: Chralt98 <chralt98@gmail.com>
Co-authored-by: Harald Heckmann <mail@haraldheckmann.de>
  • Loading branch information
4 people committed Apr 12, 2024
1 parent 32eedc9 commit bcb1421
Show file tree
Hide file tree
Showing 41 changed files with 771 additions and 402 deletions.
9 changes: 9 additions & 0 deletions docs/changelog_for_devs.md
Original file line number Diff line number Diff line change
Expand Up @@ -14,10 +14,15 @@ APIs/RPC interface.

## v0.5.2

[#1310]: https://github.com/zeitgeistpm/zeitgeist/pull/1310
[#1307]: https://github.com/zeitgeistpm/zeitgeist/pull/1307

### Added

- ⚠️ [#1310] Add `market_id` field to `Market` struct.
- [#1310] Add `MarketBuilderTrait`, which is used to define
`MarketCommonsPalletApi::build_market`, which should be used for creating
markets in the future.
- [#1307] New hybrid router for managing the trade execution using the
`neo-swaps` automated market maker and order book

Expand All @@ -33,6 +38,10 @@ APIs/RPC interface.

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

### Deprectaed

- [#1310] `MarketCommonsPalletApi::push_market` is now deprecated.

## v0.5.1

[#1295]: https://github.com/zeitgeistpm/zeitgeist/pull/1295
Expand Down
69 changes: 36 additions & 33 deletions primitives/src/market.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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<AI, BA, BN, M, A> {
pub struct Market<AI, BA, BN, M, A, MI> {
pub market_id: MI,
/// Base asset of the market.
pub base_asset: A,
/// Creator of this market.
Expand Down Expand Up @@ -68,7 +70,10 @@ pub struct Market<AI, BA, BN, M, A> {
pub early_close: Option<EarlyClose<BN, M>>,
}

impl<AI, BA, BN, M, A> Market<AI, BA, BN, M, A> {
impl<AI, BA, BN, M, A, MI> Market<AI, BA, BN, M, A, MI>
where
MI: Copy + HasCompact + MaxEncodedLen,
{
/// Returns the `ResolutionMechanism` of market, currently either:
/// - `RedeemTokens`, which implies that the module that handles the state transitions of
/// a market is also responsible to provide means for redeeming rewards
Expand Down Expand Up @@ -111,21 +116,17 @@ impl<AI, BA, BN, M, A> Market<AI, BA, BN, M, A> {
}

/// Returns a `Vec` of all outcomes for `market_id`.
pub fn outcome_assets<MI: Copy + HasCompact + MaxEncodedLen>(
&self,
market_id: MI,
) -> Vec<MarketAssetClass<MI>> {
pub fn outcome_assets(&self) -> Vec<MarketAssetClass<MI>> {
match self.market_type {
MarketType::Categorical(categories) => {
let mut assets = Vec::new();

for i in 0..categories {
match self.scoring_rule {
ScoringRule::AmmCdaHybrid => {
assets.push(MarketAssetClass::<MI>::CategoricalOutcome(market_id, i))
}
ScoringRule::AmmCdaHybrid => assets
.push(MarketAssetClass::<MI>::CategoricalOutcome(self.market_id, i)),
ScoringRule::Parimutuel => {
assets.push(MarketAssetClass::<MI>::ParimutuelShare(market_id, i))
assets.push(MarketAssetClass::<MI>::ParimutuelShare(self.market_id, i))
}
};
}
Expand All @@ -134,8 +135,8 @@ impl<AI, BA, BN, M, A> Market<AI, BA, BN, M, A> {
}
MarketType::Scalar(_) => {
vec![
MarketAssetClass::<MI>::ScalarOutcome(market_id, ScalarPosition::Long),
MarketAssetClass::<MI>::ScalarOutcome(market_id, ScalarPosition::Short),
MarketAssetClass::<MI>::ScalarOutcome(self.market_id, ScalarPosition::Long),
MarketAssetClass::<MI>::ScalarOutcome(self.market_id, ScalarPosition::Short),
]
}
}
Expand All @@ -145,45 +146,38 @@ impl<AI, BA, BN, M, A> Market<AI, BA, BN, M, A> {
/// returns `None` if not possible. Cases where `None` is returned are:
/// - The reported outcome does not exist
/// - The reported outcome does not have a corresponding asset type
pub fn report_into_asset<MI: HasCompact + MaxEncodedLen>(
&self,
market_id: MI,
) -> Option<MarketAssetClass<MI>> {
pub fn report_into_asset(&self) -> Option<MarketAssetClass<MI>> {
let outcome = if let Some(ref report) = self.report {
&report.outcome
} else {
return None;
};

self.outcome_report_into_asset(market_id, outcome)
self.outcome_report_into_asset(outcome)
}

/// Tries to convert the resolved outcome for `market_id` into an asset,
/// returns `None` if not possible. Cases where `None` is returned are:
/// - The resolved outcome does not exist
/// - The resolved outcome does not have a corresponding asset type
pub fn resolved_outcome_into_asset<MI: HasCompact + MaxEncodedLen>(
&self,
market_id: MI,
) -> Option<MarketAssetClass<MI>> {
pub fn resolved_outcome_into_asset(&self) -> Option<MarketAssetClass<MI>> {
let outcome = self.resolved_outcome.as_ref()?;
self.outcome_report_into_asset(market_id, outcome)
self.outcome_report_into_asset(outcome)
}

/// Tries to convert a `outcome_report` for `market_id` into an asset,
/// returns `None` if not possible.
fn outcome_report_into_asset<MI: HasCompact + MaxEncodedLen>(
fn outcome_report_into_asset(
&self,
market_id: MI,
outcome_report: &OutcomeReport,
) -> Option<MarketAssetClass<MI>> {
match outcome_report {
OutcomeReport::Categorical(idx) => match self.scoring_rule {
ScoringRule::AmmCdaHybrid => {
Some(MarketAssetClass::<MI>::CategoricalOutcome(market_id, *idx))
Some(MarketAssetClass::<MI>::CategoricalOutcome(self.market_id, *idx))
}
ScoringRule::Parimutuel => {
Some(MarketAssetClass::<MI>::ParimutuelShare(market_id, *idx))
Some(MarketAssetClass::<MI>::ParimutuelShare(self.market_id, *idx))
}
},
OutcomeReport::Scalar(_) => None,
Expand Down Expand Up @@ -249,16 +243,18 @@ impl<AI, BA> Default for MarketBonds<AI, BA> {
}
}

impl<AI, BA, BN, M, A> MaxEncodedLen for Market<AI, BA, BN, M, A>
impl<AI, BA, BN, M, A, MI> MaxEncodedLen for Market<AI, BA, BN, M, A, MI>
where
AI: MaxEncodedLen,
BA: MaxEncodedLen,
BN: MaxEncodedLen,
M: MaxEncodedLen,
A: MaxEncodedLen,
MI: MaxEncodedLen,
{
fn max_encoded_len() -> usize {
AI::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())
Expand Down Expand Up @@ -433,7 +429,8 @@ mod tests {
types::{Asset, MarketAsset},
};
use test_case::test_case;
type Market = crate::market::Market<u32, u32, u32, u32, Asset<u32>>;
type MarketId = u128;
type Market = crate::market::Market<u32, u32, u32, u32, Asset<MarketId>, MarketId>;

#[test_case(
MarketType::Categorical(6),
Expand Down Expand Up @@ -489,13 +486,14 @@ mod tests {
expected: bool,
) {
let market = Market {
market_id: 9,
base_asset: Asset::Ztg,
creator: 1,
creation: MarketCreation::Permissionless,
creator_fee: Default::default(),
oracle: 3,
metadata: vec![4u8; 5],
market_type, // : MarketType::Categorical(6),
market_type,
period: MarketPeriod::Block(7..8),
deadlines: Deadlines {
grace_period: 1_u32,
Expand Down Expand Up @@ -528,7 +526,10 @@ mod tests {
#[test_case(
MarketType::Scalar(12..=34),
ScoringRule::AmmCdaHybrid,
vec![MarketAsset::ScalarOutcome(0, ScalarPosition::Long), MarketAsset::ScalarOutcome(0, ScalarPosition::Short)];
vec![
MarketAsset::ScalarOutcome(0, ScalarPosition::Long),
MarketAsset::ScalarOutcome(0, ScalarPosition::Short),
];
"scalar_market"
)]
fn provides_correct_list_of_assets(
Expand All @@ -537,6 +538,7 @@ mod tests {
expected: Vec<MarketAsset>,
) {
let market = Market {
market_id: 0,
base_asset: Asset::Ztg,
creator: 1,
creation: MarketCreation::Permissionless,
Expand All @@ -558,7 +560,7 @@ mod tests {
bonds: MarketBonds::default(),
early_close: None,
};
assert_eq!(market.outcome_assets(0), expected);
assert_eq!(market.outcome_assets(), expected);
}

#[test_case(
Expand Down Expand Up @@ -595,6 +597,7 @@ mod tests {
});

let market = Market {
market_id: 0,
base_asset: Asset::Ztg,
creator: 1,
creation: MarketCreation::Permissionless,
Expand All @@ -616,8 +619,8 @@ mod tests {
bonds: MarketBonds::default(),
early_close: None,
};
assert_eq!(market.resolved_outcome_into_asset(0), expected);
assert_eq!(market.report_into_asset(0), expected);
assert_eq!(market.resolved_outcome_into_asset(), expected);
assert_eq!(market.report_into_asset(), expected);
}

#[test]
Expand Down
24 changes: 13 additions & 11 deletions primitives/src/traits.rs
Original file line number Diff line number Diff line change
Expand Up @@ -22,22 +22,24 @@ mod dispute_api;
mod distribute_fees;
mod hybrid_router_amm_api;
mod hybrid_router_orderbook_api;
mod market_builder;
mod market_commons_pallet_api;
mod market_id;
mod market_transition_api;
mod swaps;
mod weights;
mod zeitgeist_asset;

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 hybrid_router_amm_api::HybridRouterAmmApi;
pub use hybrid_router_orderbook_api::HybridRouterOrderbookApi;
pub use market_commons_pallet_api::MarketCommonsPalletApi;
pub use market_id::MarketId;
pub use market_transition_api::MarketTransitionApi;
pub use swaps::Swaps;
pub use weights::CheckedDivPerComponent;
pub use complete_set_operations_api::*;
pub use deploy_pool_api::*;
pub use dispute_api::*;
pub use distribute_fees::*;
pub use hybrid_router_amm_api::*;
pub use hybrid_router_orderbook_api::*;
pub use market_builder::*;
pub use market_commons_pallet_api::*;
pub use market_id::*;
pub use market_transition_api::*;
pub use swaps::*;
pub use weights::*;
pub use zeitgeist_asset::*;
6 changes: 4 additions & 2 deletions primitives/src/traits/dispute_api.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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<T> = Market<
pub type MarketOfDisputeApi<T> = Market<
<T as DisputeApi>::AccountId,
<T as DisputeApi>::Balance,
<T as DisputeApi>::BlockNumber,
<T as DisputeApi>::Moment,
BaseAsset,
<T as DisputeApi>::MarketId,
>;

type GlobalDisputeItemOfDisputeApi<T> =
Expand Down Expand Up @@ -145,12 +146,13 @@ pub trait DisputeMaxWeightApi {
fn clear_max_weight() -> Weight;
}

type MarketOfDisputeResolutionApi<T> = Market<
pub type MarketOfDisputeResolutionApi<T> = Market<
<T as DisputeResolutionApi>::AccountId,
<T as DisputeResolutionApi>::Balance,
<T as DisputeResolutionApi>::BlockNumber,
<T as DisputeResolutionApi>::Moment,
BaseAsset,
<T as DisputeResolutionApi>::MarketId,
>;

pub trait DisputeResolutionApi {
Expand Down
4 changes: 2 additions & 2 deletions primitives/src/traits/hybrid_router_amm_api.rs
Original file line number Diff line number Diff line change
Expand Up @@ -19,10 +19,10 @@ use crate::hybrid_router_api_types::{AmmSoftFail, AmmTrade, ApiError};
use frame_support::dispatch::DispatchError;

/// A type alias for the return struct of AMM buy and sell.
pub type AmmTradeOf<T> = AmmTrade<<T as HybridRouterAmmApi>::Balance>;
type AmmTradeOf<T> = AmmTrade<<T as HybridRouterAmmApi>::Balance>;

/// A type alias for the error type of the AMM part of the hybrid router.
pub type ApiErrorOf = ApiError<AmmSoftFail>;
type ApiErrorOf = ApiError<AmmSoftFail>;

/// Trait for handling the AMM part of the hybrid router.
pub trait HybridRouterAmmApi {
Expand Down
4 changes: 2 additions & 2 deletions primitives/src/traits/hybrid_router_orderbook_api.rs
Original file line number Diff line number Diff line change
Expand Up @@ -20,13 +20,13 @@ use frame_support::dispatch::DispatchError;
use crate::hybrid_router_api_types::{ApiError, OrderbookSoftFail, OrderbookTrade};

/// A type alias for the return struct of orderbook trades.
pub type OrderbookTradeOf<T> = OrderbookTrade<
type OrderbookTradeOf<T> = OrderbookTrade<
<T as HybridRouterOrderbookApi>::AccountId,
<T as HybridRouterOrderbookApi>::Balance,
>;

/// A type alias for the error type of the orderbook part of the hybrid router.
pub type ApiErrorOf = ApiError<OrderbookSoftFail>;
type ApiErrorOf = ApiError<OrderbookSoftFail>;

/// Trait for handling the order book part of the hybrid router.
pub trait HybridRouterOrderbookApi {
Expand Down
61 changes: 61 additions & 0 deletions primitives/src/traits/market_builder.rs
Original file line number Diff line number Diff line change
@@ -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 <https://www.gnu.org/licenses/>.

use crate::types::{
Deadlines, EarlyClose, Market, MarketBonds, MarketCreation, MarketDisputeMechanism,
MarketPeriod, MarketStatus, MarketType, OutcomeReport, Report, ScoringRule,
};
use alloc::vec::Vec;
use sp_runtime::{DispatchError, 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();
/// builder.field1(value1).field2(value2);
/// builder.clone().build()
/// ```
pub trait MarketBuilderTrait<AI, BA, BN, M, A, MI> {
fn build(self) -> Result<Market<AI, BA, BN, M, A, MI>, DispatchError>;

builder_methods! {
market_id: MI,
base_asset: A,
creator: AI,
creation: MarketCreation,
creator_fee: Perbill,
oracle: AI,
metadata: Vec<u8>,
market_type: MarketType,
period: MarketPeriod<BN, M>,
deadlines: Deadlines<BN>,
scoring_rule: ScoringRule,
status: MarketStatus,
report: Option<Report<AI, BN>>,
resolved_outcome: Option<OutcomeReport>,
dispute_mechanism: Option<MarketDisputeMechanism>,
bonds: MarketBonds<AI, BA>,
early_close: Option<EarlyClose<BN, M>>,
}
}
Loading

0 comments on commit bcb1421

Please sign in to comment.