Skip to content

Commit

Permalink
Merge branch 'main' into transactional
Browse files Browse the repository at this point in the history
  • Loading branch information
maltekliemann committed May 14, 2022
2 parents 83905a5 + b224bdd commit 8a8af05
Show file tree
Hide file tree
Showing 15 changed files with 915 additions and 139 deletions.
9 changes: 7 additions & 2 deletions docs/changelog_for_devs.md
Original file line number Diff line number Diff line change
Expand Up @@ -3,13 +3,18 @@
- Added a field for `market_account` to `MarketCreated` event and `pool_account`
field to `PoolCreate` event.

- Changed all call parameters of type `u16`, `BalanceOf`, `MarketId` and `PoolId`
in extrinsics to
- Changed all call parameters of type `u16`, `BalanceOf`, `MarketId` and
`PoolId` in extrinsics to
[compact encoding](https://docs.substrate.io/v3/advanced/scale-codec/#compactgeneral-integers).

- Removed the `cancel_pending_market` function and the corresponding event
`MarketCancelled`.

- Renamed `admin_set_pool_as_stale` to `admin_set_pool_to_stale` and changed the
call parameters: Instead of specifying a `market_type` and `pool_id`, we now
use a `market_id`. This fixes a problem where it's possible to specify an
incorrect market type.

# v0.3.1

- Removed function parameter `keep_outcome_assets` from dispatchables
Expand Down
1 change: 1 addition & 0 deletions primitives/src/constants.rs
Original file line number Diff line number Diff line change
Expand Up @@ -96,6 +96,7 @@ parameter_types! {
pub const MaxWeight: Balance = 50 * BASE;
pub const MinLiquidity: Balance = 100 * BASE;
pub const MinSubsidy: Balance = MinLiquidity::get();
pub const MinSubsidyPerAccount: Balance = MinSubsidy::get();
pub const MinWeight: Balance = BASE;
pub const SwapsPalletId: PalletId = PalletId(*b"zge/swap");
}
Expand Down
5 changes: 4 additions & 1 deletion primitives/src/traits/swaps.rs
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,9 @@ pub trait Swaps<AccountId> {
weights: Option<Vec<u128>>,
) -> Result<PoolId, DispatchError>;

/// Destroy CPMM pool, slash pool account assets and destroy pool shares of the liquidity providers.
fn destroy_pool(pool_id: PoolId) -> Result<Weight, DispatchError>;

/// Pool will be marked as `PoolStatus::Active`, if the market is currently in subsidy
/// state and all other conditions are met. Returns the result of the operation and
/// the total weight. If the result is false, not enough subsidy was gathered and the
Expand Down Expand Up @@ -106,7 +109,7 @@ pub trait Swaps<AccountId> {
/// * `pool_id`: Unique pool identifier associated with the pool to be made stale.
/// * `outcome_report`: The resulting outcome.
/// * `winner_payout_account`: The account that exchanges winning assets against rewards.
fn set_pool_as_stale(
fn set_pool_to_stale(
market_type: &MarketType,
pool_id: PoolId,
outcome_report: &OutcomeReport,
Expand Down
5 changes: 3 additions & 2 deletions primitives/src/traits/zeitgeist_multi_reservable_currency.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,12 +4,13 @@ use orml_traits::currency::MultiReservableCurrency;

/// Custom `MultiReservableCurrency` trait.
pub trait ZeitgeistMultiReservableCurrency<AccountId>: MultiReservableCurrency<AccountId> {
/// Returns the total number of accounts (1st value) and all users that hold a given `currency_id` (2nd value)
/// Return the total number of accounts that hold _any_ asset (first value) and all accounts
/// that hold assets of a given `currency_id` (second value).
fn accounts_by_currency_id(
currency_id: Self::CurrencyId,
) -> (usize, Vec<(AccountId, AccountData<Self::Balance>)>);

/// Destroys all assets of a given `currency_id` for all `accounts`.
/// Destroy all assets of a `currency_id` for the given `accounts`.
fn destroy_all<I>(currency_id: Self::CurrencyId, accounts: I)
where
I: Iterator<Item = (AccountId, AccountData<Self::Balance>)>;
Expand Down
2 changes: 2 additions & 0 deletions runtime/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -922,6 +922,7 @@ impl zrml_swaps::Config for Runtime {
// NoopLiquidityMining will be applied only to mainnet once runtimes are separated.
type LiquidityMining = NoopLiquidityMining;
// type LiquidityMining = LiquidityMining;
type MarketCommons = MarketCommons;
type MarketId = MarketId;
type MinAssets = MinAssets;
type MaxAssets = MaxAssets;
Expand All @@ -931,6 +932,7 @@ impl zrml_swaps::Config for Runtime {
type MaxWeight = MaxWeight;
type MinLiquidity = MinLiquidity;
type MinSubsidy = MinSubsidy;
type MinSubsidyPerAccount = MinSubsidyPerAccount;
type MinWeight = MinWeight;
type PalletId = SwapsPalletId;
type RikiddoSigmoidFeeMarketEma = RikiddoSigmoidFeeMarketEma;
Expand Down
8 changes: 4 additions & 4 deletions zrml/prediction-markets/src/benchmarks.rs
Original file line number Diff line number Diff line change
Expand Up @@ -249,9 +249,9 @@ benchmarks! {
let _ = T::SimpleDisputes::on_dispute(&disputes, &market_id, &market)?;
}

let approval_origin = T::ApprovalOrigin::successful_origin();
let destroy_origin = T::DestroyOrigin::successful_origin();
let call = Call::<T>::admin_destroy_market { market_id };
}: { call.dispatch_bypass_filter(approval_origin)? }
}: { call.dispatch_bypass_filter(destroy_origin)? }

admin_destroy_reported_market{
// a = total accounts
Expand All @@ -264,9 +264,9 @@ benchmarks! {

let c_u16 = c.saturated_into();
let (caller, market_id) = setup_resolve_common_categorical::<T>(a, b, c_u16)?;
let approval_origin = T::ApprovalOrigin::successful_origin();
let destroy_origin = T::DestroyOrigin::successful_origin();
let call = Call::<T>::admin_destroy_market { market_id };
}: { call.dispatch_bypass_filter(approval_origin)? }
}: { call.dispatch_bypass_filter(destroy_origin)? }

admin_move_market_to_closed {
let (caller, market_id) = create_market_common::<T>(
Expand Down
116 changes: 77 additions & 39 deletions zrml/prediction-markets/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,16 +4,18 @@
//!
//! ## Overview
//!
//! Prediction markets are speculative markets that trade on future outcomes. While
//! a prediction market is active, traders can permissionlessly trade on outcome assets.
//! Complete set of outcome assets can be generated by exchanging base currency (ZTG) at
//! a 1:1 exchange rate, meaning that 1 ZTG is used to generate 1 of each outcome asset
//! of the market. A complete set of outcome assets (an equal amount of each outcome asset
//! for a market), can be used to do the reverse exchange, being used to exchange 1:1 back to ZTG.
//! Prediction markets are speculative markets that trade on future outcomes. While a prediction
//! market is active, traders can permissionlessly trade on outcome assets. Complete set of outcome
//! assets can be generated by exchanging base currency (ZTG) at a 1:1 exchange rate, meaning that
//! 1 ZTG is used to generate 1 of each outcome asset of the market. A complete set of outcome
//! assets (an equal amount of each outcome asset for a market), can be used to do the reverse
//! exchange, being used to exchange 1:1 back to ZTG.
//!
//! This pallet implements a two types of prediction markets:
//! - **Categorical market** - A market that can trade up to 8 different categories of outcomes. In the most
//! simple case, when the outcomes are binary options (i.e. "YES" or "NO") the market has only two categories.
//!
//! - **Categorical market** - A market that can trade up to 8 different categories of outcomes. In
//! the most simple case, when the outcomes are binary options (i.e. "YES" or "NO") the market
//! has only two categories.
//! - **Scalar markets** - A market that trades `Long` or `Short` positions on a range of outcomes.
//!
//! ## Interface
Expand All @@ -24,31 +26,42 @@
//!
//! - `buy_complete_set` - Buys a complete set of outcome assets for a market.
//! - `create_categorical_market` - Creates a new categorical market.
//! - `create_cpmm_market_and_deploy_assets` - Create a market using CPMM scoring rule, buy a complete set of the assets used and deploy.
//! within and deploy an arbitrary amount of those that's greater than the minimum amount.
//! - `create_cpmm_market_and_deploy_assets` - Creates a market using CPMM scoring rule, buys a
//! complete set of the assets used and deploys the funds.
//! - `create_scalar_market` - Creates a new scalar market.
//! - `deploy_swap_pool_for_market` - Deploys a single "canonical" pool for a market.
//! - `deploy_swap_pool_and_additional_liquidity` - Deploys a single "canonical" pool for a market,
//! buys a complete set of the assets used and deploys the funds as specified.
//! - `dispute` - Submits a disputed outcome for a market.
//! - `redeem_shares` - Redeems the winning shares for a market.
//! - `report` - Reports an outcome for a market.
//! - `sell_complete_set` - Sells a complete set of outcome assets for a market.
//!
//! #### Admin Dispatches
//!
//! The administrative dispatches are used to perform admin functions on chain. Currently, the
//! admin functions can only be called by the `ApprovalOrigin`, `CloseOrigin`, `DestroyOrigin` and `ResolveOrigin`
//! The administrative dispatches are used to perform admin functions on chain:
//!
//! - `admin_destroy_market` - Destroys a market and all related assets, regardless of its state.
//! - `admin_move_market_to_closed` - Immediately moves a market that is an `Active` state to closed.
//! - `admin_move_market_to_resolved` - Immediately moves a market that is `Reported` or `Disputed` to resolved.
//! Can only be called by the `DestroyOrigin`.
//! - `admin_move_market_to_closed` - Immediately moves a market that is an `Active` state to
//! closed. Can only be called by `CloseOrigin`.
//! - `admin_move_market_to_resolved` - Immediately moves a market that is `Reported` or `Disputed`
//! to resolved. Can only be called by `ResolveOrigin`.
//!
//! The origins from which the admin functions are called (`CloseOrigin`, `DestroyOrigin`,
//! `ResolveOrigin`) are mainly minimum vote proportions from the advisory committee, the on-chain
//! governing body of Zeitgeist that is responsible for maintaining a list of high quality markets
//! and slash low quality markets.
//!
//! #### `ApprovalOrigin`, `CloseOrigin`, `DestroyOrigin` and `ResolveOrigin` Dispatches
//! #### `ApprovalOrigin` Dispatches
//!
//! Those origins are mainly minimum vote proportions from the advisory committee, the on-chain governing body of Zeitgeist
//! that is responsible for maintaining a list of high quality markets and slash low quality markets.
//! Users can also propose markets, which are subject to approval or rejection by the Advisory
//! Committee. The `AdvisoryOrigin` calls the following dispatches:
//!
//! - `approve_market` - Approves a `Proposed` market that is waiting approval from the Advisory Committee.
//! - `reject_market` - Rejects a `Proposed` market that is waiting approval from the Advisory Committee.
//! - `approve_market` - Approves a `Proposed` market that is waiting approval from the Advisory
//! Committee.
//! - `reject_market` - Rejects a `Proposed` market that is waiting approval from the Advisory
//! Committee.

#![cfg_attr(not(feature = "std"), no_std)]

Expand Down Expand Up @@ -97,7 +110,7 @@ mod pallet {
use zrml_liquidity_mining::LiquidityMiningPalletApi;
use zrml_market_commons::MarketCommonsPalletApi;

pub(crate) const RESERVE_ID: [u8; 8] = PmPalletId::get().0;
pub const RESERVE_ID: [u8; 8] = PmPalletId::get().0;

/// The current storage version.
const STORAGE_VERSION: StorageVersion = StorageVersion::new(0);
Expand All @@ -114,7 +127,10 @@ mod pallet {

#[pallet::call]
impl<T: Config> Pallet<T> {
/// Allows the `DestroyOrigin` to immediately destroy a market.
/// Destroy a market, including its outcome assets, market account and pool account.
///
/// Must be called by `DestroyOrigin`. Bonds (unless already returned) are slashed without
/// exception. Can currently only be used for destroying CPMM markets.
#[pallet::weight(
T::WeightInfo::admin_destroy_reported_market(
900,
Expand All @@ -131,36 +147,57 @@ mod pallet {
origin: OriginFor<T>,
#[pallet::compact] market_id: MarketIdOf<T>,
) -> DispatchResultWithPostInfo {
// TODO(#486)
// TODO(#618): Not implemented for Rikiddo!
T::DestroyOrigin::ensure_origin(origin)?;

let mut total_accounts = 0usize;
let mut share_accounts = 0usize;
let market = T::MarketCommons::market(&market_id)?;
ensure!(market.scoring_rule == ScoringRule::CPMM, Error::<T>::InvalidScoringRule);
let market_status = market.status;
let outcome_assets = Self::outcome_assets(market_id, &market);
let outcome_assets_amount = outcome_assets.len();
Self::clear_auto_resolve(&market_id)?;
T::MarketCommons::remove_market(&market_id)?;
Self::deposit_event(Event::MarketDestroyed(market_id));
let market_account = Self::market_account(market_id);

let mut outcome_assets_iter = outcome_assets.into_iter();
// Slash outstanding bonds; see
// https://github.com/zeitgeistpm/runtime-audit-1/issues/34#issuecomment-1120187097 for
// details.
let slash_market_creator = |amount| {
CurrencyOf::<T>::slash_reserved_named(&RESERVE_ID, &market.creator, amount);
};
if market_status == MarketStatus::Proposed {
slash_market_creator(T::AdvisoryBond::get());
}
if market_status != MarketStatus::Resolved
&& market_status != MarketStatus::InsufficientSubsidy
{
if market.creation == MarketCreation::Permissionless {
slash_market_creator(T::ValidityBond::get());
}
slash_market_creator(T::OracleBond::get());
}

// Delete of this market's outcome assets.
let mut manage_outcome_asset = |asset: Asset<_>| -> usize {
// Delete market's outcome assets, clear market and delete pool if necessary.
let mut destroy_asset = |asset: Asset<_>| -> usize {
let (total_accounts, accounts) = T::Shares::accounts_by_currency_id(asset);
share_accounts = share_accounts.saturating_add(accounts.len());
T::Shares::destroy_all(asset, accounts.iter().cloned());
total_accounts
};

if let Some(first_asset) = outcome_assets_iter.next() {
total_accounts = manage_outcome_asset(first_asset);
for asset in outcome_assets.into_iter() {
total_accounts = destroy_asset(asset);
}
for asset in outcome_assets_iter {
let _ = manage_outcome_asset(asset);
CurrencyOf::<T>::slash(&market_account, CurrencyOf::<T>::free_balance(&market_account));
if let Ok(pool_id) = T::MarketCommons::market_pool(&market_id) {
T::Swaps::destroy_pool(pool_id)?;
T::MarketCommons::remove_market_pool(&market_id)?;
}

Self::clear_auto_resolve(&market_id)?;
T::MarketCommons::remove_market(&market_id)?;

Self::deposit_event(Event::MarketDestroyed(market_id));

// Weight correction
if market_status == MarketStatus::Reported {
Ok(Some(T::WeightInfo::admin_destroy_reported_market(
Expand Down Expand Up @@ -899,16 +936,16 @@ mod pallet {
#[pallet::compact] market_id: MarketIdOf<T>,
) -> DispatchResult {
T::ApprovalOrigin::ensure_origin(origin)?;

let market = T::MarketCommons::market(&market_id)?;
ensure!(market.status == MarketStatus::Proposed, Error::<T>::InvalidMarketStatus);
let creator = market.creator;
let (imbalance, _) = CurrencyOf::<T>::slash_reserved_named(
&RESERVE_ID,
&creator,
T::AdvisoryBond::get(),
);
// Slashes the imbalance.
T::Slash::on_unbalanced(imbalance);
CurrencyOf::<T>::unreserve_named(&RESERVE_ID, &creator, T::OracleBond::get());
T::MarketCommons::remove_market(&market_id)?;
Self::deposit_event(Event::MarketRejected(market_id));
Self::deposit_event(Event::MarketDestroyed(market_id));
Expand All @@ -931,7 +968,7 @@ mod pallet {

T::MarketCommons::mutate_market(&market_id, |market| {
ensure!(market.report.is_none(), Error::<T>::MarketAlreadyReported);
Self::ensure_market_is_closed(&market.period)?;
Self::ensure_market_is_closed(market)?;
Self::ensure_outcome_matches_market_type(market, &market_report.outcome)?;

let mut should_check_origin = false;
Expand Down Expand Up @@ -1515,10 +1552,11 @@ mod pallet {

// Check that the market has reached the end of its period.
fn ensure_market_is_closed(
period: &MarketPeriod<T::BlockNumber, MomentOf<T>>,
market: &Market<T::AccountId, T::BlockNumber, MomentOf<T>>,
) -> DispatchResult {
ensure!(market.status == MarketStatus::Active, Error::<T>::MarketIsNotClosed);
ensure!(
match period {
match &market.period {
MarketPeriod::Block(range) => {
<frame_system::Pallet<T>>::block_number() >= range.end
}
Expand Down Expand Up @@ -1995,7 +2033,7 @@ mod pallet {
return Ok(T::DbWeight::get().reads(1));
};
let market_account = Self::market_account(*market_id);
let weight = T::Swaps::set_pool_as_stale(
let weight = T::Swaps::set_pool_to_stale(
&market.market_type,
pool_id,
outcome_report,
Expand Down
3 changes: 3 additions & 0 deletions zrml/prediction-markets/src/mock.rs
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,7 @@ ord_parameter_types! {
parameter_types! {
pub const DisputePeriod: BlockNumber = 10;
pub const TreasuryPalletId: PalletId = PalletId(*b"3.141592");
pub const MinSubsidyPerAccount: Balance = BASE;
pub const AdvisoryBond: Balance = 11 * CENT;
pub const OracleBond: Balance = 25 * CENT;
pub const ValidityBond: Balance = 53 * CENT;
Expand Down Expand Up @@ -234,6 +235,7 @@ impl zrml_swaps::Config for Runtime {
type FixedTypeU = <Runtime as zrml_rikiddo::Config>::FixedTypeU;
type FixedTypeS = <Runtime as zrml_rikiddo::Config>::FixedTypeS;
type LiquidityMining = LiquidityMining;
type MarketCommons = MarketCommons;
type MarketId = MarketId;
type MaxAssets = MaxAssets;
type MaxInRatio = MaxInRatio;
Expand All @@ -243,6 +245,7 @@ impl zrml_swaps::Config for Runtime {
type MinAssets = MinAssets;
type MinLiquidity = MinLiquidity;
type MinSubsidy = MinSubsidy;
type MinSubsidyPerAccount = MinSubsidyPerAccount;
type MinWeight = MinWeight;
type PalletId = SwapsPalletId;
type RikiddoSigmoidFeeMarketEma = RikiddoSigmoidFeeMarketEma;
Expand Down
Loading

0 comments on commit 8a8af05

Please sign in to comment.