From c406d720d3ebb1771077577ce5ee5824e0c64b61 Mon Sep 17 00:00:00 2001 From: Malte Kliemann Date: Mon, 11 Nov 2024 22:50:56 +0100 Subject: [PATCH 1/3] Add some docs and remove TODOs --- README.md | 4 ++ .../src/traits/combinatorial_tokens_api.rs | 2 + .../combinatorial_tokens_benchmark_helper.rs | 2 + .../traits/combinatorial_tokens_unsafe_api.rs | 6 +++ zrml/combinatorial-tokens/src/lib.rs | 26 ++++++++++- zrml/futarchy/README.md | 45 +++++++++++++++++++ zrml/futarchy/src/lib.rs | 7 ++- zrml/futarchy/src/types/proposal.rs | 6 ++- 8 files changed, 95 insertions(+), 3 deletions(-) diff --git a/README.md b/README.md index e2dffb8b8..cf6899b58 100644 --- a/README.md +++ b/README.md @@ -19,8 +19,12 @@ decentralized court. ## Modules - [authorized](./zrml/authorized) - Offers authorized resolution of disputes. +- [combinatorial-tokens](./zrml/combinatorial-tokens) - The module responsible + for generating Zeitgeist 2.0 outcome tokens. - [court](./zrml/court) - An implementation of a court mechanism used to resolve disputes in a decentralized fashion. +- [futarchy](./zrml/futarchy) - A novel on-chain governance mechanism using + prediction markets. - [global-disputes](./zrml-global-disputes) - Global disputes sets one out of multiple outcomes with the most locked ZTG tokens as the canonical outcome. This is the default process if a dispute mechanism fails to resolve. diff --git a/primitives/src/traits/combinatorial_tokens_api.rs b/primitives/src/traits/combinatorial_tokens_api.rs index 33230d703..cfb6beb06 100644 --- a/primitives/src/traits/combinatorial_tokens_api.rs +++ b/primitives/src/traits/combinatorial_tokens_api.rs @@ -19,6 +19,8 @@ use crate::types::SplitPositionDispatchInfo; use alloc::vec::Vec; use sp_runtime::DispatchError; +/// Trait that can be used to expose the internal functionality of zrml-combinatorial-tokens to +/// other pallets. pub trait CombinatorialTokensApi { type AccountId; type Balance; diff --git a/primitives/src/traits/combinatorial_tokens_benchmark_helper.rs b/primitives/src/traits/combinatorial_tokens_benchmark_helper.rs index dbb8cf889..b1c3b3212 100644 --- a/primitives/src/traits/combinatorial_tokens_benchmark_helper.rs +++ b/primitives/src/traits/combinatorial_tokens_benchmark_helper.rs @@ -18,6 +18,8 @@ use alloc::vec::Vec; use sp_runtime::DispatchResult; +/// Trait used for setting up benchmarks of zrml-combinatorial-tokens. Must not be used in +/// production. pub trait CombinatorialTokensBenchmarkHelper { type Balance; type MarketId; diff --git a/primitives/src/traits/combinatorial_tokens_unsafe_api.rs b/primitives/src/traits/combinatorial_tokens_unsafe_api.rs index 4aca7c69f..317dca26b 100644 --- a/primitives/src/traits/combinatorial_tokens_unsafe_api.rs +++ b/primitives/src/traits/combinatorial_tokens_unsafe_api.rs @@ -26,6 +26,9 @@ pub trait CombinatorialTokensUnsafeApi { type Balance; type MarketId; + /// Transfers `amount` units of collateral from the user to the pallet's reserve and mints + /// `amount` units of each asset in `assets`. Can break the reserve or result in loss of funds + /// if the value of the elements in `assets` don't add up to exactly 1. fn split_position_unsafe( who: Self::AccountId, collateral: Asset, @@ -33,6 +36,9 @@ pub trait CombinatorialTokensUnsafeApi { amount: Self::Balance, ) -> DispatchResult; + /// Transfers `amount` units of collateral from the pallet's reserve to the user and burns + /// `amount` units of each asset in `assets`. Can break the reserve or result in loss of funds + /// if the value of the elements in `assets` don't add up to exactly 1. fn merge_position_unsafe( who: Self::AccountId, collateral: Asset, diff --git a/zrml/combinatorial-tokens/src/lib.rs b/zrml/combinatorial-tokens/src/lib.rs index 7feb249b3..291e628c5 100644 --- a/zrml/combinatorial-tokens/src/lib.rs +++ b/zrml/combinatorial-tokens/src/lib.rs @@ -76,6 +76,7 @@ mod pallet { MarketId = MarketIdOf, >; + /// Interface for calculating collection and position IDs. type CombinatorialIdManager: CombinatorialIdManager< Asset = AssetOf, MarketId = MarketIdOf, @@ -86,6 +87,7 @@ mod pallet { type MultiCurrency: MultiCurrency>; + /// Interface for acquiring the payout vector by market ID. type Payout: PayoutApi, MarketId = MarketIdOf>; type RuntimeEvent: From> + IsType<::RuntimeEvent>; @@ -195,6 +197,29 @@ mod pallet { #[pallet::call] impl Pallet { + /// Split `amount` units of the position specified by `parent_collection_id` over the market + /// with ID `market_id` according to the given `partition`. + /// + /// The `partition` is specified as a vector whose elements are equal-length `Vec`. A + /// `true` entry at the `i`th index of a partition element means that the `i`th outcome + /// token of the market is contained in this element of the partition. + /// + /// For each element `b` of the partition, the split mints a new outcome token which is made + /// up of the position to be split and the conjunction `(x|...|z)` where `x, ..., z` are the + /// items of `b`. The position to be split, in turn, is burned or transferred into the + /// pallet account, depending on whether or not it is a true combinatorial token or + /// collateral. + /// + /// If the `parent_collection_id` is `None`, then the position split is the collateral of the + /// market given by `market_id`. + /// + /// If the `parent_collection_id` is `Some(pid)`, then there are two cases: vertical and + /// horizontal split. If `partition` is complete (i.e. there is no index `i` so that `b[i]` + /// is `false` for all `b` in `partition`), the position split is the position obtained by + /// combining `pid` with the collateral of the market given by `market_id`. If `partition` + /// is not complete, the position split is the position made up of the + /// `parent_collection_id` and the conjunction `(x|...|z)` where `x, ..., z` are the items + /// covered by `partition`. #[pallet::call_index(0)] #[pallet::weight( T::WeightInfo::split_position_vertical_sans_parent(partition.len().saturated_into()) @@ -206,7 +231,6 @@ mod pallet { #[transactional] pub fn split_position( origin: OriginFor, - // TODO Abstract this into a separate type. parent_collection_id: Option>, market_id: MarketIdOf, partition: Vec>, diff --git a/zrml/futarchy/README.md b/zrml/futarchy/README.md index 19f6917fb..f1fe16f9b 100644 --- a/zrml/futarchy/README.md +++ b/zrml/futarchy/README.md @@ -1 +1,46 @@ # Futarchy Module + +The futarchy module provides a straightforward, "no bells and whistles" +implementation of the [futarchy governance +system](https://docs.zeitgeist.pm/docs/learn/futarchy). + +## Overview + +The futarchy module is essentially an oracle based governance system: When a +proposal is submitted, an oracle is specified which evaluates whether the +proposal should be executed. The type of the oracle is configured using the +associated type `Oracle`, which must implement `FutarchyOracle`. + +The typical oracle implementation for futarchy is the `DecisionMarketOracle` +implementation exposed by the neo-swaps module, which allows making decisions +based on prices in prediction markets. A `DecisionMarketOracle` is defined by +providing a pool ID and two outcomes, the _positive_ and _negative_ outcome. +The oracle evaluates positively (meaning that it will allow the proposal to +pass) if and only if the positive outcome is move valuable than the negative +outcome. + +The standard governance flow is the following: + +- A hard-coded origin (usually root) submits a proposal to be approved or + rejected via futarchy. If the origin is root, this is done by running a + governance proposal through + [pallet-democracy](https://github.com/paritytech/polkadot-sdk/tree/master/substrate/frame/democracy) + and calling into this pallet's sole extrinsic `submit_proposal`. Assuming + that the thesis of futarchy is correct and the market used to evaluate the + proposal is well-configured and sufficiently liquid, submitting a proposal to + futarchy rather than pallet-democracy gives a stronger guarantee on the + efficacy of the proposal. +- Wait until the `duration` specified in `submit_proposal` has passed. The + oracle will be automatically evaluated and will either schedule + `proposal.call` at `proposal.when` where `proposal` is the proposal specified + in `submit_proposal`. + +### Terminology + +- _Call_: Refers to an on-chain extrinsic call. +- _Oracle_: A means of making a decision about a proposal. At any block, an + oracle evaluates to `true` (proposal is accepted) or `false` (proposal is + rejected). +- _Proposal_: Consists of a call, an oracle and a time of execution. If and + only if the proposal is accepted, the call is scheduled for the specified + time of execution. diff --git a/zrml/futarchy/src/lib.rs b/zrml/futarchy/src/lib.rs index 500c61457..fa9f094a2 100644 --- a/zrml/futarchy/src/lib.rs +++ b/zrml/futarchy/src/lib.rs @@ -76,9 +76,10 @@ mod pallet { #[cfg(feature = "runtime-benchmarks")] type BenchmarkHelper: FutarchyBenchmarkHelper; + /// The minimum allowed duration between the creation of a proposal and its evaluation. type MinDuration: Get>; - // The type used to define the oracle for each proposal. + /// The type used to define the oracle a proposal. type Oracle: FutarchyOracle + Clone + Debug @@ -152,6 +153,10 @@ mod pallet { #[pallet::call] impl Pallet { + /// Submits a `proposal` for evaluation in `duration` blocks. + /// + /// If, after `duration` blocks, the oracle `proposal.oracle` is evaluated positively, the + /// proposal is scheduled for execution at `proposal.when`. #[pallet::call_index(0)] #[transactional] #[pallet::weight(T::WeightInfo::submit_proposal())] diff --git a/zrml/futarchy/src/types/proposal.rs b/zrml/futarchy/src/types/proposal.rs index 84ddf6748..3576be99c 100644 --- a/zrml/futarchy/src/types/proposal.rs +++ b/zrml/futarchy/src/types/proposal.rs @@ -21,7 +21,6 @@ use frame_system::pallet_prelude::BlockNumberFor; use parity_scale_codec::{Decode, Encode, MaxEncodedLen}; use scale_info::TypeInfo; -// TODO Make config a generic, keeps things simple. #[derive( CloneNoBound, Decode, Encode, Eq, MaxEncodedLen, PartialEqNoBound, RuntimeDebugNoBound, TypeInfo, )] @@ -30,7 +29,12 @@ pub struct Proposal where T: Config, { + /// The time at which the proposal will be enacted. pub when: BlockNumberFor, + + /// The proposed call. pub call: BoundedCallOf, + + /// The oracle that evaluates if the proposal should be enacted. pub oracle: OracleOf, } From 96195779adef055bfb9745a9480544fc5fb577cd Mon Sep 17 00:00:00 2001 From: Malte Kliemann Date: Wed, 13 Nov 2024 00:05:45 +0100 Subject: [PATCH 2/3] More documentation --- zrml/combinatorial-tokens/README.md | 39 ++++++- zrml/combinatorial-tokens/src/lib.rs | 41 +++++++ .../src/traits/combinatorial_id_manager.rs | 10 +- zrml/neo-swaps/src/lib.rs | 102 +++++++++++++++++- 4 files changed, 188 insertions(+), 4 deletions(-) diff --git a/zrml/combinatorial-tokens/README.md b/zrml/combinatorial-tokens/README.md index 1e0c36716..c31faec9b 100644 --- a/zrml/combinatorial-tokens/README.md +++ b/zrml/combinatorial-tokens/README.md @@ -1,3 +1,38 @@ -# Combo Module +# Combinatorial Tokens Module -The Combo module implements combinatorial tokens in substrate. +The combinatorial-tokens module implements modern Zeitgeist's method of +creating and destroying outcome tokens. + +## Overview + +In a categorical or scalar prediction market, one unit of a complete set (i.e. one unit of each outcome token of the market) always redeems for one unit of collateral. + +In a Yes/No market, for instance, holding `x` units of Yes and `x` units of No means that, when the market resolves, you will always receive `x` units of collateral. In a scalar market, on the other hand, `x` units of Long and `x` units of Short will always redeem to a total of `x` units of collateral, as well. + +This means that buying and selling collateral for complete sets should be allowed. For example, `x` units of collateral should fetch `x` units of complete set, and vice versa. Buying complete sets can be thought of as splitting collateral into outcome tokens, while selling complete sets can be thought of as merging outcome tokens back into collateral. + +The combinatorial-tokens module generalizes this approach to not only allow splitting and merging into collateral, but also splitting and merging into outcome tokens of multiple different markets. This allows us to create outcome tokens that combine multiple events. They are called _combinatorial tokens_. + +For example, splitting an `A` token from one categorical market using another categorical market with two outcomes `X` and `Y` yields `A & X` and `A & Y` tokens. They represent the event that `A` and `X` (resp. `Y`) occur. Splitting a Yes token from a binary market using a scalar market will give `Yes & Long` and `Yes & Short` tokens. They represent Long/Short tokens contingent on `Yes` occurring. + +In addition to splitting and merging, combinatorial tokens can be redeemed if one of the markets involved in creating them has been resolved. For example, if the `XY` market above resolves to `X`, then every unit of `X & A` redeems for a unit of `A` and `Y & A` is worthless. If the scalar market above resolves so that `Long` is valued at `.4` and `Short` at `.6`, then every unit of `Yes & Long` redeems for `.4` units of `Yes` and every unit of `Yes & Short` redeems for `.6`. + +And important distinction which we've so far neglected to make is the distinction between an abstract _collection_ like `X & A` or `Yes & Short` and a concrete _position_, which is a collection together with a collateral token against which it is valued. Collections are purely abstract and used in the implementation. Positions are actual tokens on the chain. + +Collections and position are identified using their IDs. When using the standard combinatorial ID Manager, this ID is a 256 bit value. The position ID of a certain token can be calculated using the collection ID and the collateral. + +### Terminology + +- _Combinatorial token_: Any instance of `zeitgeist_primitives::Asset::CombinatorialToken`. +- _Complete set (of a prediction market)_: An abstract set containing every outcome of a particular prediction market. One unit of a complete set is one unit of each outcome token from the market in question. After the market resolves, a complete set always redeems for exactly one unit of collateral. +- _Merge_: The prcoess of exchanging multiple tokens for a single token of equal value. +- _Split_: The process of exchanging a token for more complicated tokens of equal value. + +### Combinatorial ID Manager + +Calculating + +alt_bn128 + +combinatorial tokens, as [defined by +Gnosis](https://docs.gnosis.io/conditionaltokens/) in Substrate. diff --git a/zrml/combinatorial-tokens/src/lib.rs b/zrml/combinatorial-tokens/src/lib.rs index 291e628c5..36624d8ad 100644 --- a/zrml/combinatorial-tokens/src/lib.rs +++ b/zrml/combinatorial-tokens/src/lib.rs @@ -220,6 +220,9 @@ mod pallet { /// is not complete, the position split is the position made up of the /// `parent_collection_id` and the conjunction `(x|...|z)` where `x, ..., z` are the items /// covered by `partition`. + /// + /// The `force_max_work` parameter can be used to trigger the maximum amount of allowed work + /// for the combinatorial ID manager. Should only be used for benchmarking purposes. #[pallet::call_index(0)] #[pallet::weight( T::WeightInfo::split_position_vertical_sans_parent(partition.len().saturated_into()) @@ -251,6 +254,31 @@ mod pallet { DispatchResultWithPostInfo::Ok(post_dispatch_info) } + /// Merge `amount` units of the tokens obtained by splitting `parent_collection_id` using + /// `partition` into the position specified by `parent_collection_id` (vertical split) or + /// the position obtained by splitting `parent_collection_id` according to `partiton` over + /// the market with ID `market_id` (horizontal; see below for details). + /// + /// The `partition` is specified as a vector whose elements are equal-length `Vec`. A + /// `true` entry at the `i`th index of a partition element means that the `i`th outcome + /// token of the market is contained in this element of the partition. + /// + /// For each element `b` of the partition, the split burns the outcome tokens which are made + /// up of the position to be split and the conjunction `(x|...|z)` where `x, ..., z` are the + /// items of `b`. The position given by `parent_collection_id` is + /// + /// If the `parent_collection_id` is `None`, then the position split is the collateral of the + /// market given by `market_id`. + /// + /// If the `parent_collection_id` is `Some(pid)`, then there are two cases: vertical and + /// horizontal merge. If `partition` is complete (i.e. there is no index `i` so that `b[i]` + /// is `false` for all `b` in `partition`), the the result of the merge is the position + /// defined by `parent_collection_id`. If `partition` is not complete, the result of the + /// merge is the position made up of the `parent_collection_id` and the conjunction + /// `(x|...|z)` where `x, ..., z` are the items covered by `partition`. + /// + /// The `force_max_work` parameter can be used to trigger the maximum amount of allowed work + /// for the combinatorial ID manager. Should only be used for benchmarking purposes. #[pallet::call_index(1)] #[pallet::weight( T::WeightInfo::merge_position_vertical_sans_parent(partition.len().saturated_into()) @@ -279,6 +307,19 @@ mod pallet { ) } + /// (Partially) redeems a position if part of it belongs to a resolved market given by + /// `market_id`. + /// + /// The position to be redeemed is the position obtained by combining the position given by + /// `parent_collection_id` and `collateral` with the conjunction `(x|...|z)` where `x, ... + /// z` are the outcome tokens of the market `market_id` given by `partition`. + /// + /// The position to be redeemed is completely removed from the origin's wallet. According to + /// how much the conjunction `(x|...|z)` is valued, the user is paid in the position defined + /// by `parent_collection_id` and `collateral`. + /// + /// The `force_max_work` parameter can be used to trigger the maximum amount of allowed work + /// for the combinatorial ID manager. Should only be used for benchmarking purposes. #[pallet::call_index(2)] #[pallet::weight( T::WeightInfo::redeem_position_with_parent(index_set.len().saturated_into()) diff --git a/zrml/combinatorial-tokens/src/traits/combinatorial_id_manager.rs b/zrml/combinatorial-tokens/src/traits/combinatorial_id_manager.rs index 547d2f9d8..7240cbad3 100644 --- a/zrml/combinatorial-tokens/src/traits/combinatorial_id_manager.rs +++ b/zrml/combinatorial-tokens/src/traits/combinatorial_id_manager.rs @@ -24,12 +24,18 @@ use alloc::vec::Vec; +/// Handles calculations of combinatorial IDs. pub trait CombinatorialIdManager { type Asset; type MarketId; type CombinatorialId; - // TODO Replace `Vec` with a more effective bit mask type. + /// Calculate the collection ID obtained when splitting `parent_collection_id` over the market + /// given by `market_id` and the `index_set`. + /// + /// If `force_max_work` parameter is set, the calculation will use up the maximum amount of work + /// necessary, independent of the other parameters. Should only be used for benchmarking + /// purposes. fn get_collection_id( parent_collection_id: Option, market_id: Self::MarketId, @@ -37,6 +43,8 @@ pub trait CombinatorialIdManager { force_max_work: bool, ) -> Option; + /// Calculate the position ID belonging to the `collection_id` combined with `collateral` as + /// collateral. fn get_position_id( collateral: Self::Asset, collection_id: Self::CombinatorialId, diff --git a/zrml/neo-swaps/src/lib.rs b/zrml/neo-swaps/src/lib.rs index 2fc47f05a..17b4a0f53 100644 --- a/zrml/neo-swaps/src/lib.rs +++ b/zrml/neo-swaps/src/lib.rs @@ -127,8 +127,10 @@ mod pallet { #[pallet::config] pub trait Config: frame_system::Config { + /// Type of combinatorial ID used by the combinatorial tokens APIs. type CombinatorialId: Clone; + /// API used for calculating splits of tokens when creating combinatorial pools. type CombinatorialTokens: CombinatorialTokensApi< AccountId = Self::AccountId, Balance = BalanceOf, @@ -136,6 +138,7 @@ mod pallet { MarketId = MarketIdOf, >; + /// API for fast creation of tokens when buying or selling combinatorial tokens. type CombinatorialTokensUnsafe: CombinatorialTokensUnsafeApi< AccountId = Self::AccountId, Balance = BalanceOf, @@ -681,6 +684,34 @@ mod pallet { Ok(Some(T::WeightInfo::deploy_pool(spot_prices_len)).into()) } + /// Make a combinatorial bet on the specified pool. + /// + /// The `amount_in` is paid in collateral. The transaction fails if the amount of outcome + /// tokens received is smaller than `min_amount_out`. The user must correctly specify the + /// number of outcomes for benchmarking reasons. + /// + /// The user's collateral is used to mint complete sets of the combinatorial tokens in the + /// pool. The parameters `buy` and `sell` are used to specify which of these tokens the user + /// wants and doesn't want: The assets in `sell` are sold to buy more of `buy` from the + /// pool. The assets not contained in either of these will remain in the users wallet + /// unchanged. + /// + /// The function will error if certain numerical constraints are violated. + /// + /// # Parameters + /// + /// - `origin`: The origin account making the purchase. + /// - `pool_id`: Identifier for the pool used to trade on. + /// - `asset_count`: Number of assets in the pool. + /// - `buy`: The assets that the user want to have more of. Must not be empty. + /// - `sell`: The assets that the user doesn't want any of. Must not be empty. + /// - `amount_in`: Amount of collateral paid by the user. + /// - `min_amount_out`: Minimum number of outcome tokens the user expects to receive. + /// + /// # Complexity + /// + /// Depends on the implementation of `CombinatorialTokensUnsafeApi` and `ExternalFees`; when + /// using the canonical implementations, the runtime complexity is `O(asset_count)`. #[allow(clippy::too_many_arguments)] #[pallet::call_index(6)] #[pallet::weight(T::WeightInfo::combo_buy(asset_count.log_ceil().into()))] @@ -705,6 +736,36 @@ mod pallet { Self::do_combo_buy(who, pool_id, buy, sell, amount_in, min_amount_out) } + /// Cancel a combinatorial bet on the specified pool. + /// + /// The `buy`, `keep` and `sell` parameters are used to specify the amounts of the bet the + /// user wishes to cancel. The user must hold `amount_buy` units of each asset in `buy` and + /// `amount_keep` of each asset in `keep` in their wallet. If `keep` is empty, then + /// `amount_keep` must be zero. + /// + /// The transaction fails if the amount of outcome tokens received is smaller than + /// `min_amount_out`. The user must correctly specify the number of outcomes for + /// benchmarking reasons. + /// + /// The function will error if certain numerical constraints are violated. + /// + /// # Parameters + /// + /// - `origin`: The origin account making the purchase. + /// - `pool_id`: Identifier for the pool used to trade on. + /// - `asset_count`: Number of assets in the pool. + /// - `buy`: The `buy` of the bet that the user wishes to cancel. Must not be empty. + /// - `keep`: The tokens not contained in `buy` or `sell` of the bet that the user wishes to + /// cancel. May be empty. + /// - `sell`: The `sell` of the bet that the user wishes to cancel. Must not be empty. + /// - `amount_buy`: Amount of tokens in `buy` the user wishes to let go. + /// - `amount_keep`: Amount of tokens in `keep` the user wishes to let go. + /// - `min_amount_out`: Minimum number of outcome tokens the user expects to receive. + /// + /// # Complexity + /// + /// Depends on the implementation of `CombinatorialTokensUnsafeApi` and `ExternalFees`; when + /// using the canonical implementations, the runtime complexity is `O(asset_count)`. #[allow(clippy::too_many_arguments)] #[pallet::call_index(7)] #[pallet::weight(T::WeightInfo::combo_sell(asset_count.log_ceil().into()))] @@ -740,6 +801,46 @@ mod pallet { ) } + /// Deploy a combinatorial pool for the specified markets and provide liquidity. + /// + /// The tokens are split TODO + /// + /// The pool's assets are ordered by lexicographical order, using the ordering of tokens of + /// each individual market provided by the `MarketCommonsApi`. For example, if three markets + /// with outcomes `x_1, x_2`, `y_1, y_2` and `z_1, z_2` are involved, the outcomes of the + /// pool are (in order): + /// + /// x_1 & y_1 & z_1 + /// x_1 & y_1 & z_2 + /// x_1 & y_2 & z_1 + /// x_1 & y_2 & z_2 + /// x_2 & y_1 & z_1 + /// x_2 & y_1 & z_2 + /// x_2 & y_2 & z_1 + /// x_2 & y_2 & z_2 + /// + /// The sender specifies a vector of `spot_prices` for the assets of the new pool, in the + /// order as described above. + /// + /// Depending on the values in the `spot_prices`, the transaction will transfer different + /// amounts of each outcome to the pool. The sender specifies a maximum `amount` of outcome + /// tokens to spend. + /// + /// Unlike in the `deploy_pool` extrinsic, the sender need not acquire the outcome tokens + /// themselves. Instead, all they need is `amount` units of collateral. + /// + /// Deploying the pool will cost the signer an additional fee to the tune of the + /// collateral's existential deposit. This fee is placed in the pool account and ensures + /// that swap fees can be stored in the pool account without triggering dusting or failed + /// transfers. + /// + /// The `force_max_work` parameter can be used to force the `CombinatorialTokensApi` to + /// spend the maximum amount of work, independently of the parameters that it is called + /// with. This is useful for benchmarking purposes and should not be used in production. + /// + /// # Complexity + /// + /// `O(n)` where `n` is the number of splits required to create the pool. #[pallet::call_index(8)] #[pallet::weight(T::WeightInfo::deploy_combinatorial_pool(asset_count.log_ceil().into()))] #[transactional] @@ -1008,7 +1109,6 @@ mod pallet { ) -> DispatchResult { ensure!(pool_shares_amount != Zero::zero(), Error::::ZeroAmount); - // FIXME Should this also be made part of the `PoolStorage` interface? Pools::::try_mutate_exists(pool_id, |maybe_pool| { let pool = maybe_pool.as_mut().ok_or::(Error::::PoolNotFound.into())?; From ad42f61aecdd14268edace5e72d5f66cb5e122e7 Mon Sep 17 00:00:00 2001 From: Malte Kliemann Date: Sat, 16 Nov 2024 23:32:37 +0100 Subject: [PATCH 3/3] . --- zrml/neo-swaps/src/lib.rs | 4 +++- zrml/neo-swaps/src/types/decision_market_oracle.rs | 4 ++++ 2 files changed, 7 insertions(+), 1 deletion(-) diff --git a/zrml/neo-swaps/src/lib.rs b/zrml/neo-swaps/src/lib.rs index 17b4a0f53..89564b9d0 100644 --- a/zrml/neo-swaps/src/lib.rs +++ b/zrml/neo-swaps/src/lib.rs @@ -803,7 +803,9 @@ mod pallet { /// Deploy a combinatorial pool for the specified markets and provide liquidity. /// - /// The tokens are split TODO + /// The tokens of each of the markets specified by `market_ids` are split into atoms. For + /// each combination of outcome tokens `x, ..., z` from the markets, there is one + /// combinatorial token `x & ... & z` in the pool. /// /// The pool's assets are ordered by lexicographical order, using the ordering of tokens of /// each individual market provided by the `MarketCommonsApi`. For example, if three markets diff --git a/zrml/neo-swaps/src/types/decision_market_oracle.rs b/zrml/neo-swaps/src/types/decision_market_oracle.rs index 2d27f247b..a87d7b3e4 100644 --- a/zrml/neo-swaps/src/types/decision_market_oracle.rs +++ b/zrml/neo-swaps/src/types/decision_market_oracle.rs @@ -22,6 +22,10 @@ use scale_info::TypeInfo; use sp_runtime::DispatchError; use zeitgeist_primitives::traits::FutarchyOracle; +/// Struct that implements `FutarchyOracle` using price measurements from liquidity pools. +/// +/// The oracle evaluates to `true` if and only if the `positive_outcome` is more valuable than the +/// `negative_outcome` in the liquidity pool specified by `pool_id`. #[derive(Clone, Debug, Decode, Encode, Eq, MaxEncodedLen, PartialEq, TypeInfo)] pub struct DecisionMarketOracle where