diff --git a/CODEOWNERS b/CODEOWNERS index 059ede2ac..4cda9ed69 100644 --- a/CODEOWNERS +++ b/CODEOWNERS @@ -16,8 +16,6 @@ /zrml/orderbook/ @Chralt98 /zrml/parimutuel/ @Chralt98 /zrml/prediction-markets/ @maltekliemann -/zrml/rikiddo/ @sea212 -/zrml/simple-disputes/ @Chralt98 /zrml/styx/ @yornaath /zrml/swaps/ @maltekliemann diff --git a/Cargo.lock b/Cargo.lock index 279e2b189..559793792 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -876,7 +876,6 @@ dependencies = [ "staging-xcm", "staging-xcm-builder", "staging-xcm-executor", - "substrate-fixed", "substrate-wasm-builder", "test-case", "xcm-emulator", @@ -885,14 +884,11 @@ dependencies = [ "zrml-court", "zrml-global-disputes", "zrml-hybrid-router", - "zrml-liquidity-mining", "zrml-market-commons", "zrml-neo-swaps", "zrml-orderbook", "zrml-parimutuel", "zrml-prediction-markets", - "zrml-rikiddo", - "zrml-simple-disputes", "zrml-styx", "zrml-swaps", "zrml-swaps-runtime-api", @@ -12823,7 +12819,6 @@ source = "git+https://github.com/encointer/substrate-fixed#879c58bcc6fd676a74315 dependencies = [ "parity-scale-codec", "scale-info", - "serde", "substrate-typenum", ] @@ -14925,7 +14920,6 @@ dependencies = [ "url", "zeitgeist-primitives", "zeitgeist-runtime", - "zrml-liquidity-mining", "zrml-swaps-rpc", ] @@ -15040,7 +15034,6 @@ dependencies = [ "staging-xcm", "staging-xcm-builder", "staging-xcm-executor", - "substrate-fixed", "substrate-wasm-builder", "test-case", "xcm-emulator", @@ -15049,14 +15042,11 @@ dependencies = [ "zrml-court", "zrml-global-disputes", "zrml-hybrid-router", - "zrml-liquidity-mining", "zrml-market-commons", "zrml-neo-swaps", "zrml-orderbook", "zrml-parimutuel", "zrml-prediction-markets", - "zrml-rikiddo", - "zrml-simple-disputes", "zrml-styx", "zrml-swaps", "zrml-swaps-runtime-api", @@ -15200,32 +15190,10 @@ dependencies = [ "zrml-court", "zrml-global-disputes", "zrml-hybrid-router", - "zrml-liquidity-mining", "zrml-market-commons", "zrml-neo-swaps", "zrml-orderbook", "zrml-prediction-markets", - "zrml-simple-disputes", -] - -[[package]] -name = "zrml-liquidity-mining" -version = "0.5.3" -dependencies = [ - "env_logger 0.10.2", - "frame-benchmarking", - "frame-support", - "frame-system", - "log", - "pallet-balances", - "pallet-timestamp", - "parity-scale-codec", - "scale-info", - "serde", - "sp-io", - "sp-runtime", - "zeitgeist-primitives", - "zrml-market-commons", ] [[package]] @@ -15283,12 +15251,10 @@ dependencies = [ "zrml-authorized", "zrml-court", "zrml-global-disputes", - "zrml-liquidity-mining", "zrml-market-commons", "zrml-neo-swaps", "zrml-prediction-markets", "zrml-prediction-markets-runtime-api", - "zrml-simple-disputes", ] [[package]] @@ -15381,11 +15347,9 @@ dependencies = [ "zrml-authorized", "zrml-court", "zrml-global-disputes", - "zrml-liquidity-mining", "zrml-market-commons", "zrml-prediction-markets", "zrml-prediction-markets-runtime-api", - "zrml-simple-disputes", ] [[package]] @@ -15398,7 +15362,6 @@ dependencies = [ "sp-arithmetic 16.0.0 (git+https://github.com/paritytech/polkadot-sdk?branch=release-polkadot-v1.1.0)", "zeitgeist-primitives", "zrml-prediction-markets", - "zrml-simple-disputes", ] [[package]] @@ -15410,61 +15373,6 @@ dependencies = [ "zeitgeist-primitives", ] -[[package]] -name = "zrml-rikiddo" -version = "0.5.3" -dependencies = [ - "arbitrary", - "cfg-if", - "env_logger 0.10.2", - "frame-support", - "frame-system", - "hashbrown 0.14.5", - "pallet-balances", - "pallet-timestamp", - "parity-scale-codec", - "scale-info", - "sp-core", - "sp-io", - "sp-runtime", - "substrate-fixed", - "zeitgeist-primitives", - "zrml-rikiddo", -] - -[[package]] -name = "zrml-rikiddo-fuzz" -version = "0.0.0" -dependencies = [ - "arbitrary", - "frame-support", - "frame-system", - "libfuzzer-sys", - "substrate-fixed", - "zrml-rikiddo", -] - -[[package]] -name = "zrml-simple-disputes" -version = "0.5.3" -dependencies = [ - "env_logger 0.10.2", - "frame-benchmarking", - "frame-support", - "frame-system", - "orml-currencies", - "orml-tokens", - "orml-traits", - "pallet-balances", - "pallet-timestamp", - "parity-scale-codec", - "scale-info", - "sp-io", - "sp-runtime", - "zeitgeist-primitives", - "zrml-market-commons", -] - [[package]] name = "zrml-styx" version = "0.5.3" diff --git a/Cargo.toml b/Cargo.toml index 784f637e3..d04e185ec 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -17,15 +17,12 @@ default-members = [ "zrml/court", "zrml/hybrid-router", "zrml/global-disputes", - "zrml/liquidity-mining", "zrml/market-commons", "zrml/neo-swaps", "zrml/orderbook", "zrml/parimutuel", "zrml/prediction-markets", "zrml/prediction-markets/runtime-api", - "zrml/rikiddo", - "zrml/simple-disputes", "zrml/swaps", "zrml/swaps/rpc", "zrml/swaps/runtime-api", @@ -42,7 +39,6 @@ members = [ "zrml/court", "zrml/hybrid-router", "zrml/global-disputes", - "zrml/liquidity-mining", "zrml/market-commons", "zrml/neo-swaps", "zrml/orderbook", @@ -51,9 +47,6 @@ members = [ "zrml/prediction-markets", "zrml/prediction-markets/fuzz", "zrml/prediction-markets/runtime-api", - "zrml/rikiddo", - "zrml/rikiddo/fuzz", - "zrml/simple-disputes", "zrml/swaps", "zrml/swaps/fuzz", "zrml/swaps/rpc", @@ -219,7 +212,6 @@ sp-session = { git = "https://github.com/paritytech/polkadot-sdk", branch = "rel sp-std = { git = "https://github.com/paritytech/polkadot-sdk", branch = "release-polkadot-v1.1.0", default-features = false } sp-transaction-pool = { git = "https://github.com/paritytech/polkadot-sdk", branch = "release-polkadot-v1.1.0", default-features = false } sp-version = { git = "https://github.com/paritytech/polkadot-sdk", branch = "release-polkadot-v1.1.0", default-features = false } -substrate-fixed = { git = "https://github.com/encointer/substrate-fixed", default-features = false } # Polkadot / XCM (client) polkadot-cli = { git = "https://github.com/paritytech/polkadot-sdk", branch = "release-polkadot-v1.1.0" } @@ -244,7 +236,6 @@ xcm-executor = { package = "staging-xcm-executor", git = "https://github.com/par battery-station-runtime = { path = "runtime/battery-station" } zeitgeist-runtime = { path = "runtime/zeitgeist" } zrml-prediction-markets-fuzz = { path = "zrml/prediction-markets/fuzz" } -zrml-rikiddo-fuzz = { path = "zrml/rikkido/fuzz" } zrml-swaps-fuzz = { path = "zrml/swaps/fuzz" } zrml-swaps-rpc = { path = "zrml/swaps/rpc" } @@ -256,15 +247,12 @@ zrml-authorized = { path = "zrml/authorized", default-features = false } zrml-court = { path = "zrml/court", default-features = false } zrml-global-disputes = { path = "zrml/global-disputes", default-features = false } zrml-hybrid-router = { path = "zrml/hybrid-router", default-features = false } -zrml-liquidity-mining = { path = "zrml/liquidity-mining", default-features = false } zrml-market-commons = { path = "zrml/market-commons", default-features = false } zrml-neo-swaps = { path = "zrml/neo-swaps", default-features = false } zrml-orderbook = { path = "zrml/orderbook", default-features = false } zrml-parimutuel = { path = "zrml/parimutuel", default-features = false } zrml-prediction-markets = { path = "zrml/prediction-markets", default-features = false } zrml-prediction-markets-runtime-api = { path = "zrml/prediction-markets/runtime-api", default-features = false } -zrml-rikiddo = { path = "zrml/rikiddo", default-features = false } -zrml-simple-disputes = { path = "zrml/simple-disputes", default-features = false } zrml-styx = { path = "zrml/styx", default-features = false } zrml-swaps = { path = "zrml/swaps", default-features = false } zrml-swaps-runtime-api = { path = "zrml/swaps/runtime-api", default-features = false } diff --git a/README.md b/README.md index 7350560e5..e2dffb8b8 100644 --- a/README.md +++ b/README.md @@ -39,10 +39,6 @@ decentralized court. user to create pools, provide liquidity or swap assets. - [primitives](./zrml/primitives) - Contains custom and common types, traits and constants. -- [rikiddo](./zrml/rikiddo) - The module contains a completely modular - implementation of our novel market maker [Rikiddo][rikiddo]. It also offers a - pallet that other pallets can use to utilize the Rikiddo market maker. Rikiddo - can be used by the automated market maker to determine swap prices. ## How to Build and Run a Zeitgeist Node diff --git a/docs/STYLE_GUIDE.md b/docs/STYLE_GUIDE.md index 4974fb753..3ea7ea52d 100644 --- a/docs/STYLE_GUIDE.md +++ b/docs/STYLE_GUIDE.md @@ -138,10 +138,11 @@ duplicating documentation. - Merges require one review. Additional reviews may be requested. - Every merge into a feature branch requires a review. +- Feature branches are merged into `develop`, which is merged into + `release-vX.Y.Z` branches when we're publishing a release. - Aim for at most 500 LOC added per PR. Only exceed 1,000 LOC lines added in a PR in exceptional circumstances. Plan ahead and break a large PR into smaller - PRs targeting a feature branch. Feature branches are exempt from this rule. -- Reviews take priority over most other tasks. + PRs. - Reviewing a PR should not take longer than two business days. Aim for shorter PRs if the changes are complex. - A PR should not be in flight (going from first `s:ready-for-review` to @@ -163,7 +164,7 @@ duplicating documentation. // In code logic zeitgeist_macros::unreachable_non_terminating!(condition, log_target, message) ``` - + ```rust // In test #[test] diff --git a/docs/changelog_for_devs.md b/docs/changelog_for_devs.md index 69c4109a4..2ee376831 100644 --- a/docs/changelog_for_devs.md +++ b/docs/changelog_for_devs.md @@ -12,6 +12,11 @@ As of 0.3.9, the changelog's format is based on components which query the chain's storage, the extrinsics or the runtime APIs/RPC interface. +## v0.5.4 + +- ⚠️ Remove zrml-liquidity-mining from code base. +- ⚠️ Remove zrml-rikiddo from the code base. + ## v0.5.2 [#1310]: https://github.com/zeitgeistpm/zeitgeist/pull/1310 diff --git a/node/Cargo.toml b/node/Cargo.toml index 2067dac5f..54e0cc69d 100644 --- a/node/Cargo.toml +++ b/node/Cargo.toml @@ -114,7 +114,6 @@ url = { workspace = true } battery-station-runtime = { workspace = true, optional = true } zeitgeist-primitives = { workspace = true, features = ["default"] } zeitgeist-runtime = { workspace = true, optional = true } -zrml-liquidity-mining = { workspace = true, features = ["default"] } zrml-swaps-rpc = { workspace = true } [features] diff --git a/node/src/chain_spec/battery_station.rs b/node/src/chain_spec/battery_station.rs index 9d8a2e8f1..03a832ff4 100644 --- a/node/src/chain_spec/battery_station.rs +++ b/node/src/chain_spec/battery_station.rs @@ -24,13 +24,7 @@ use battery_station_runtime::parameters::SS58Prefix; use hex_literal::hex; use sc_service::ChainType; use sp_core::crypto::UncheckedInto; -use zeitgeist_primitives::{ - constants::{ - ztg::{LIQUIDITY_MINING, LIQUIDITY_MINING_PTD}, - BASE, - }, - types::AccountId, -}; +use zeitgeist_primitives::{constants::BASE, types::AccountId}; #[cfg(feature = "parachain")] use { diff --git a/node/src/chain_spec/dev.rs b/node/src/chain_spec/dev.rs index 4acb1d5eb..3d5c4e6ce 100644 --- a/node/src/chain_spec/dev.rs +++ b/node/src/chain_spec/dev.rs @@ -30,10 +30,7 @@ use battery_station_runtime::{ }; use sc_service::ChainType; use sp_core::sr25519; -use zeitgeist_primitives::{ - constants::ztg::{LIQUIDITY_MINING, LIQUIDITY_MINING_PTD}, - types::Balance, -}; +use zeitgeist_primitives::types::Balance; #[cfg(feature = "parachain")] use { super::battery_station::inflation_config, diff --git a/node/src/chain_spec/mod.rs b/node/src/chain_spec/mod.rs index 28a40c705..91c32fe8e 100644 --- a/node/src/chain_spec/mod.rs +++ b/node/src/chain_spec/mod.rs @@ -150,10 +150,6 @@ macro_rules! generate_generic_genesis_function { authorities: acs.initial_authorities.iter().map(|x| (x.1.clone(), 1)).collect(), ..Default::default() }, - liquidity_mining: $runtime::LiquidityMiningConfig { - initial_balance: LIQUIDITY_MINING, - per_block_distribution: LIQUIDITY_MINING_PTD.mul_ceil(LIQUIDITY_MINING), - }, #[cfg(feature = "parachain")] parachain_info: $runtime::ParachainInfoConfig { parachain_id: acs.parachain_id, diff --git a/node/src/chain_spec/zeitgeist.rs b/node/src/chain_spec/zeitgeist.rs index 6d056113b..fd56e30de 100644 --- a/node/src/chain_spec/zeitgeist.rs +++ b/node/src/chain_spec/zeitgeist.rs @@ -27,8 +27,6 @@ use sc_service::ChainType; use sp_core::crypto::UncheckedInto; use zeitgeist_runtime::parameters::SS58Prefix; -use zeitgeist_primitives::constants::ztg::{LIQUIDITY_MINING, LIQUIDITY_MINING_PTD}; - #[cfg(feature = "parachain")] use { super::{generate_inflation_config_function, Extensions}, diff --git a/primitives/src/constants.rs b/primitives/src/constants.rs index b48768d01..bdfc39226 100644 --- a/primitives/src/constants.rs +++ b/primitives/src/constants.rs @@ -51,8 +51,8 @@ pub const fn deposit(items: u32, bytes: u32) -> Balance { items as Balance * 150 * CENT + (bytes as Balance) * 75 * MICRO } -// Rikiddo and TokensConfig parameter_types! { + // Returns the number of decimals used on chain. pub const BalanceFractionalDecimals: u8 = { let mut base = BASE; let mut counter: u8 = 0; @@ -114,9 +114,6 @@ pub const MAX_ORACLE_DURATION: BlockNumber = 14 * BLOCKS_PER_DAY; /// Pallet identifier, mainly used for named balance reserves. pub const PM_PALLET_ID: PalletId = PalletId(*b"zge/pred"); -// Simple Disputes -pub const SD_PALLET_ID: PalletId = PalletId(*b"zge/sedp"); - // Swaps /// Max. assets in a swap pool. pub const MAX_ASSETS: u16 = MAX_CATEGORIES + 1; diff --git a/primitives/src/constants/mock.rs b/primitives/src/constants/mock.rs index 4f2b16655..215bdeade 100644 --- a/primitives/src/constants/mock.rs +++ b/primitives/src/constants/mock.rs @@ -72,11 +72,6 @@ parameter_types! { pub const MaxOrders: u32 = 100; } -// Liquidity Mining parameters -parameter_types! { - pub const LiquidityMiningPalletId: PalletId = PalletId(*b"zge/lymg"); -} - // NeoSwaps parameter_types! { pub storage NeoExitFee: Balance = CENT; @@ -114,13 +109,6 @@ parameter_types! { pub const ValidityBond: Balance = 50 * CENT; } -// Simple disputes parameters -parameter_types! { - pub const SimpleDisputesPalletId: PalletId = PalletId(*b"zge/sedp"); - pub const OutcomeBond: Balance = 5 * BASE; - pub const OutcomeFactor: Balance = 2 * BASE; -} - // Swaps parameters parameter_types! { pub const ExitFee: Balance = 3 * BASE / 1000; // 0.3% diff --git a/primitives/src/constants/ztg.rs b/primitives/src/constants/ztg.rs index c46c63420..95b42fb77 100644 --- a/primitives/src/constants/ztg.rs +++ b/primitives/src/constants/ztg.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. @@ -26,9 +26,6 @@ pub const COMMUNITY_INCENTIVES: u128 = 2_000_000; /// Total ZTG amount for collators. pub const COLLATORS: u128 = 0; -/// Total ZTG amount for liquidity mining. -pub const LIQUIDITY_MINING: u128 = 0; - /// Total ZTG amount for parachain lease. pub const PARACHAIN_LEASE: u128 = 40_000_000; @@ -58,8 +55,5 @@ pub const TOTAL_INITIAL_ZTG: u128 = COMMUNITY_INCENTIVES // Inflation -/// Perthousand liquidity mining inflation. 0% -pub const LIQUIDITY_MINING_PTD: Perbill = Perbill::from_perthousand(0); - /// Perthousand collator staking inflation. 5% pub const STAKING_PTD: Perbill = Perbill::from_perthousand(50); diff --git a/primitives/src/market.rs b/primitives/src/market.rs index 3b727f4e6..c7f10a4c6 100644 --- a/primitives/src/market.rs +++ b/primitives/src/market.rs @@ -298,7 +298,6 @@ pub struct MarketDispute { pub enum MarketDisputeMechanism { Authorized, Court, - SimpleDisputes, } /// Defines whether the period is represented as a blocknumber or a timestamp. diff --git a/primitives/src/math/fixed.rs b/primitives/src/math/fixed.rs index 930a26b43..1bbfa75bf 100644 --- a/primitives/src/math/fixed.rs +++ b/primitives/src/math/fixed.rs @@ -851,7 +851,6 @@ mod tests { #[test_case(20751874964, 10, 2.075_187_496_394_219)] #[test_case(123456789876543210, 10, 12_345_678.987_654_32)] #[test_case(99999999999999999999999, 10, 9999999999999.9999999999)] - // Tests taken from Rikiddo pallet #[test_case(1, 10, 0.000_000_000_1)] #[test_case(123_456_789, 10, 0.012_345_678_9)] #[test_case(9_999, 2, 99.99)] @@ -883,7 +882,6 @@ mod tests { #[test_case(12_345_678.987_654_32, 10, 123456789876543210)] #[test_case(99.999999999999, 10, 1_000_000_000_000)] #[test_case(9999999999999.9999999999, 10, 99999999999999999999999)] - // Tests taken from Rikiddo pallet #[test_case(32.5, 0, 33)] #[test_case(32.25, 0, 32)] #[test_case(200.0, 8, 20_000_000_000)] diff --git a/runtime/battery-station/Cargo.toml b/runtime/battery-station/Cargo.toml index 9d12fc821..e5234efdd 100644 --- a/runtime/battery-station/Cargo.toml +++ b/runtime/battery-station/Cargo.toml @@ -45,7 +45,6 @@ sp-session = { workspace = true } sp-std = { workspace = true } sp-transaction-pool = { workspace = true } sp-version = { workspace = true } -substrate-fixed = { workspace = true, features = ["serde"] } # Try-Runtime @@ -113,14 +112,11 @@ zrml-authorized = { workspace = true } zrml-court = { workspace = true } zrml-global-disputes = { workspace = true, optional = true } zrml-hybrid-router = { workspace = true } -zrml-liquidity-mining = { workspace = true } zrml-market-commons = { workspace = true } zrml-neo-swaps = { workspace = true } zrml-orderbook = { workspace = true } zrml-parimutuel = { workspace = true } zrml-prediction-markets = { workspace = true } -zrml-rikiddo = { workspace = true } -zrml-simple-disputes = { workspace = true } zrml-styx = { workspace = true } zrml-swaps = { workspace = true } zrml-swaps-runtime-api = { workspace = true } @@ -220,11 +216,9 @@ runtime-benchmarks = [ "zrml-authorized/runtime-benchmarks", "zrml-court/runtime-benchmarks", "zrml-hybrid-router/runtime-benchmarks", - "zrml-liquidity-mining/runtime-benchmarks", "zrml-neo-swaps/runtime-benchmarks", "zrml-parimutuel/runtime-benchmarks", "zrml-prediction-markets/runtime-benchmarks", - "zrml-simple-disputes/runtime-benchmarks", "zrml-global-disputes/runtime-benchmarks", "zrml-styx/runtime-benchmarks", "zrml-swaps/runtime-benchmarks", @@ -274,7 +268,6 @@ std = [ "sp-session/std", "sp-transaction-pool/std", "sp-version/std", - "substrate-fixed/std", # Try-Runtime "frame-try-runtime/std", @@ -336,13 +329,10 @@ std = [ "zrml-authorized/std", "zrml-court/std", "zrml-hybrid-router/std", - "zrml-liquidity-mining/std", "zrml-market-commons/std", "zrml-neo-swaps/std", "zrml-parimutuel/std", "zrml-prediction-markets/std", - "zrml-rikiddo/std", - "zrml-simple-disputes/std", "zrml-global-disputes/std", "zrml-styx/std", "zrml-swaps-runtime-api/std", @@ -393,13 +383,10 @@ try-runtime = [ "zrml-authorized/try-runtime", "zrml-court/try-runtime", "zrml-hybrid-router/try-runtime", - "zrml-liquidity-mining/try-runtime", "zrml-market-commons/try-runtime", "zrml-neo-swaps/try-runtime", "zrml-parimutuel/try-runtime", "zrml-prediction-markets/try-runtime", - "zrml-rikiddo/try-runtime", - "zrml-simple-disputes/try-runtime", "zrml-global-disputes/try-runtime", "zrml-styx/try-runtime", "zrml-swaps/try-runtime", diff --git a/runtime/battery-station/src/lib.rs b/runtime/battery-station/src/lib.rs index b1d287da2..6f2069d77 100644 --- a/runtime/battery-station/src/lib.rs +++ b/runtime/battery-station/src/lib.rs @@ -51,12 +51,10 @@ use pallet_collective::{EnsureProportionAtLeast, PrimeDefaultVote}; use sp_runtime::traits::{AccountIdConversion, AccountIdLookup, BlakeTwo256}; #[cfg(feature = "std")] use sp_version::NativeVersion; -use substrate_fixed::{types::extra::U33, FixedI128, FixedU128}; -use zeitgeist_primitives::{constants::*, types::*}; +use zeitgeist_primitives::types::*; use zrml_prediction_markets::Call::{ buy_complete_set, create_market, dispute, edit_market, redeem_shares, report, sell_complete_set, }; -use zrml_rikiddo::types::{EmaMarketVolume, FeeSigmoid, RikiddoSigmoidMV}; use zrml_swaps::Call::{ force_pool_exit, pool_exit, pool_exit_with_exact_asset_amount, pool_exit_with_exact_pool_amount, pool_join, pool_join_with_exact_asset_amount, @@ -167,19 +165,6 @@ impl Contains for IsCallable { fn contains(call: &RuntimeCall) -> bool { #[allow(clippy::match_like_matches_macro)] match call { - RuntimeCall::SimpleDisputes(_) => false, - RuntimeCall::LiquidityMining(_) => false, - RuntimeCall::PredictionMarkets(inner_call) => match inner_call { - create_market { - dispute_mechanism: Some(MarketDisputeMechanism::SimpleDisputes), - .. - } => false, - edit_market { - dispute_mechanism: Some(MarketDisputeMechanism::SimpleDisputes), - .. - } => false, - _ => true, - }, RuntimeCall::Swaps(inner_call) => match inner_call { force_pool_exit { .. } => true, _ => false, diff --git a/runtime/battery-station/src/parameters.rs b/runtime/battery-station/src/parameters.rs index adb216b19..076941ac8 100644 --- a/runtime/battery-station/src/parameters.rs +++ b/runtime/battery-station/src/parameters.rs @@ -187,10 +187,6 @@ parameter_types! { /// be another trie item whose value is the size of an account ID plus 32 bytes. pub const SubAccountDeposit: Balance = deposit(1, 53); - // Liquidity Mining parameters - /// Pallet identifier, mainly used for named balance reserves. - pub const LiquidityMiningPalletId: PalletId = LM_PALLET_ID; - // Multisig // One storage item; key size is 32; value is size 4+4+16+32 bytes = 56 bytes. pub const DepositBase: Balance = deposit(1, 88); @@ -245,9 +241,6 @@ parameter_types! { pub const MaxOracleDuration: BlockNumber = MAX_ORACLE_DURATION; /// Maximum string length allowed for reject reason. pub const MaxRejectReasonLen: u32 = 1024; - // 2_678_400_000 = 31 days. - /// Maximum number of milliseconds a Rikiddo market can be in subsidy gathering phase. - pub const MaxSubsidyPeriod: Moment = 2_678_400_000; /// Minimum number of categories. The trivial minimum is 2, which represents a binary market. pub const MinCategories: u16 = 2; /// The dispute_duration is time where users can dispute the outcome. @@ -255,9 +248,6 @@ parameter_types! { pub const MinDisputeDuration: BlockNumber = MIN_DISPUTE_DURATION; /// Minimum block period for a oracle_duration. pub const MinOracleDuration: BlockNumber = MIN_ORACLE_DURATION; - // 60_000 = 1 minute. Should be raised to something more reasonable in the future. - /// Minimum number of milliseconds a Rikiddo market must be in subsidy gathering phase. - pub const MinSubsidyPeriod: Moment = 60_000; /// (Slashable) The orcale bond. Slashed in case the final outcome does not match the /// outcome the oracle reported. pub const OracleBond: Balance = 50 * CENT; @@ -296,16 +286,6 @@ parameter_types! { pub const MaxScheduledPerBlock: u32 = 50; pub const NoPreimagePostponement: Option = Some(5 * BLOCKS_PER_MINUTE); - // Simple disputes parameters - /// Pallet identifier, mainly used for named balance reserves. - pub const SimpleDisputesPalletId: PalletId = SD_PALLET_ID; - /// (Slashable) Bond that is provided for overriding the last outcome addition. - /// Slashed in case the final outcome does not match the dispute for which the `OutcomeBond` - /// was deposited. - pub const OutcomeBond: Balance = 5 * BASE; - /// `OutcomeBond` is increased by this factor after every new outcome addition. - pub const OutcomeFactor: Balance = 2 * BASE; - // Swaps parameters /// A precentage from the withdrawal amount a liquidity provider wants to withdraw /// from a pool before the pool is closed. @@ -320,10 +300,6 @@ parameter_types! { pub const MaxTotalWeight: Balance = MaxWeight::get() * 2; /// The maximum weight a single asset can have. pub const MaxWeight: Balance = 64 * BASE; - /// Minimum subsidy required to launch a Rikiddo pool. - pub const MinSubsidy: Balance = 100 * BASE; - /// Minimum subsidy a single account can provide. - pub const MinSubsidyPerAccount: Balance = MinSubsidy::get(); /// Minimum weight a single asset can have. pub const MinWeight: Balance = BASE; /// Pallet identifier, mainly used for named balance reserves. diff --git a/runtime/common/src/lib.rs b/runtime/common/src/lib.rs index 49465d932..d64d0c01b 100644 --- a/runtime/common/src/lib.rs +++ b/runtime/common/src/lib.rs @@ -86,102 +86,13 @@ macro_rules! decl_common_types { generic, DispatchError, DispatchResult, RuntimeDebug, SaturatedConversion, }; use zeitgeist_primitives::traits::{DeployPoolApi, DistributeFees, MarketCommonsPalletApi}; + use zrml_market_commons::migrations::MigrateDisputeMechanism; pub type Block = generic::Block; type Address = sp_runtime::MultiAddress; - struct FixStorageVersions; - - impl OnRuntimeUpgrade for FixStorageVersions { - fn on_runtime_upgrade() -> frame_support::weights::Weight { - log::info!("FixStorageVersions: Starting..."); - StorageVersion::new(4).put::(); - StorageVersion::new(4).put::(); - StorageVersion::new(1).put::(); - StorageVersion::new(4).put::(); - StorageVersion::new(4).put::(); - StorageVersion::new(4).put::(); - StorageVersion::new(4).put::(); - StorageVersion::new(4).put::(); - StorageVersion::new(15).put::(); - log::info!("FixStorageVersions: Done!"); - ::DbWeight::get().writes(9) - } - - #[cfg(feature = "try-runtime")] - fn pre_upgrade() -> Result, DispatchError> { - Ok(vec![]) - } - - #[cfg(feature = "try-runtime")] - fn post_upgrade(_: Vec) -> Result<(), DispatchError> { - Ok(()) - } - } - - type TrieId = BoundedVec>; - - // `ContractInfo` struct that we need for `ClearContractsChildTries` but pallet-contracts - // doesn't expose publicly. - #[derive(Encode, Decode, Clone, PartialEq, Eq, RuntimeDebug, TypeInfo, MaxEncodedLen)] - pub struct ContractInfo { - pub trie_id: TrieId, - pub code_hash: ::Hash, - pub storage_bytes: u32, - pub storage_items: u32, - pub storage_byte_deposit: Balance, - pub storage_item_deposit: Balance, - pub storage_base_deposit: Balance, - } - - struct ClearContractsChildTries; - - impl OnRuntimeUpgrade for ClearContractsChildTries { - fn on_runtime_upgrade() -> frame_support::weights::Weight { - log::info!("ClearContractsChildTries: Starting..."); - let mut total_reads = 0u64; - let mut total_writes = 0u64; - for (_, contract_info) in storage_key_iter::( - b"Contracts", - b"ContractInfoOf", - ) { - let trie_id = contract_info.trie_id; - let inner_trie_id = trie_id.into_inner(); - let child_info = ChildInfo::new_default(&inner_trie_id); - let multi_removal_result = child::clear_storage(&child_info, None, None); - let writes = multi_removal_result.loops as u64; - log::info!( - "ClearContractsChildTries: Cleared trie {:?} in {:?} loops", - inner_trie_id, - writes - ); - total_reads = total_reads.saturating_add(1); - total_writes = total_writes.saturating_add(writes); - } - log::info!("ClearContractsChildTries: Done!"); - ::DbWeight::get() - .reads_writes(total_reads, total_writes) - } - - #[cfg(feature = "try-runtime")] - fn pre_upgrade() -> Result, DispatchError> { - Ok(vec![]) - } - - #[cfg(feature = "try-runtime")] - fn post_upgrade(_: Vec) -> Result<(), DispatchError> { - Ok(()) - } - } - - parameter_types! { - pub const ContractsPalletStr: &'static str = "Contracts"; - } - - type ResetContracts = RemovePallet; - - type Migrations = (ClearContractsChildTries, ResetContracts, FixStorageVersions); + type Migrations = (MigrateDisputeMechanism); pub type Executive = frame_executive::Executive< Runtime, @@ -194,7 +105,6 @@ macro_rules! decl_common_types { pub type Header = generic::Header; pub(crate) type NodeBlock = generic::Block; - type RikiddoSigmoidFeeMarketVolumeEma = zrml_rikiddo::Instance1; pub type SignedExtra = ( CheckNonZeroSender, CheckSpecVersion, @@ -311,11 +221,9 @@ macro_rules! decl_common_types { CourtPalletId::get(), GlobalDisputesPalletId::get(), HybridRouterPalletId::get(), - LiquidityMiningPalletId::get(), OrderbookPalletId::get(), ParimutuelPalletId::get(), PmPalletId::get(), - SimpleDisputesPalletId::get(), SwapsPalletId::get(), TreasuryPalletId::get(), ]; @@ -376,8 +284,8 @@ macro_rules! create_runtime { use alloc::{boxed::Box, vec::Vec}; // Pallets are enumerated based on the dependency graph. // - // For example, `PredictionMarkets` is pĺaced after `SimpleDisputes` because - // `PredictionMarkets` depends on `SimpleDisputes`. + // For example, `PredictionMarkets` is pĺaced after `MarketCommons` because + // `PredictionMarkets` depends on `MarketCommons`. construct_runtime!( pub enum Runtime { @@ -420,9 +328,6 @@ macro_rules! create_runtime { MarketCommons: zrml_market_commons::{Pallet, Storage} = 50, Authorized: zrml_authorized::{Call, Event, Pallet, Storage} = 51, Court: zrml_court::{Call, Event, Pallet, Storage} = 52, - LiquidityMining: zrml_liquidity_mining::{Call, Config, Event, Pallet, Storage} = 53, - RikiddoSigmoidFeeMarketEma: zrml_rikiddo::::{Pallet, Storage} = 54, - SimpleDisputes: zrml_simple_disputes::{Call, Event, Pallet, Storage} = 55, Swaps: zrml_swaps::{Call, Event, Pallet, Storage} = 56, PredictionMarkets: zrml_prediction_markets::{Call, Event, Pallet, Storage} = 57, Styx: zrml_styx::{Call, Event, Pallet, Storage} = 58, @@ -1038,7 +943,9 @@ macro_rules! impl_config_traits { ), ProxyType::Trading => matches!( c, - RuntimeCall::NeoSwaps(zrml_neo_swaps::Call::buy { .. }) + RuntimeCall::HybridRouter(zrml_hybrid_router::Call::buy { .. }) + | RuntimeCall::HybridRouter(zrml_hybrid_router::Call::sell { .. }) + | RuntimeCall::NeoSwaps(zrml_neo_swaps::Call::buy { .. }) | RuntimeCall::NeoSwaps(zrml_neo_swaps::Call::sell { .. }) | RuntimeCall::Orderbook(zrml_orderbook::Call::place_order { .. }) | RuntimeCall::Orderbook(zrml_orderbook::Call::fill_order { .. }) @@ -1046,7 +953,9 @@ macro_rules! impl_config_traits { ), ProxyType::HandleAssets => matches!( c, - RuntimeCall::NeoSwaps(zrml_neo_swaps::Call::join { .. }) + RuntimeCall::HybridRouter(zrml_hybrid_router::Call::buy { .. }) + | RuntimeCall::HybridRouter(zrml_hybrid_router::Call::sell { .. }) + | RuntimeCall::NeoSwaps(zrml_neo_swaps::Call::join { .. }) | RuntimeCall::NeoSwaps(zrml_neo_swaps::Call::exit { .. }) | RuntimeCall::NeoSwaps(zrml_neo_swaps::Call::buy { .. }) | RuntimeCall::NeoSwaps(zrml_neo_swaps::Call::sell { .. }) @@ -1264,42 +1173,12 @@ macro_rules! impl_config_traits { type WeightInfo = zrml_court::weights::WeightInfo; } - impl zrml_liquidity_mining::Config for Runtime { - type RuntimeEvent = RuntimeEvent; - type Currency = Balances; - type MarketCommons = MarketCommons; - type MarketId = MarketId; - type PalletId = LiquidityMiningPalletId; - type WeightInfo = zrml_liquidity_mining::weights::WeightInfo; - } - impl zrml_market_commons::Config for Runtime { type Balance = Balance; type MarketId = MarketId; type Timestamp = Timestamp; } - // NoopLiquidityMining implements LiquidityMiningPalletApi with no-ops. - // Has to be public because it will be exposed by Runtime. - pub struct NoopLiquidityMining; - - impl zrml_liquidity_mining::LiquidityMiningPalletApi for NoopLiquidityMining { - type AccountId = AccountId; - type Balance = Balance; - type BlockNumber = BlockNumber; - type MarketId = MarketId; - - fn add_shares(_: Self::AccountId, _: Self::MarketId, _: Self::Balance) {} - - fn distribute_market_incentives( - _: &Self::MarketId, - ) -> frame_support::pallet_prelude::DispatchResult { - Ok(()) - } - - fn remove_shares(_: &Self::AccountId, _: &Self::MarketId, _: Self::Balance) {} - } - impl zrml_prediction_markets::Config for Runtime { type AdvisoryBond = AdvisoryBond; type AdvisoryBondSlashPercentage = AdvisoryBondSlashPercentage; @@ -1317,10 +1196,6 @@ macro_rules! impl_config_traits { type DisputeBond = DisputeBond; type RuntimeEvent = RuntimeEvent; type GlobalDisputes = GlobalDisputes; - // LiquidityMining is currently unstable. - // NoopLiquidityMining will be applied only to mainnet once runtimes are separated. - type LiquidityMining = NoopLiquidityMining; - // type LiquidityMining = LiquidityMining; type MaxCategories = MaxCategories; type MaxCreatorFee = MaxCreatorFee; type MaxDisputes = MaxDisputes; @@ -1344,39 +1219,11 @@ macro_rules! impl_config_traits { type AssetManager = AssetManager; #[cfg(feature = "parachain")] type AssetRegistry = AssetRegistry; - type SimpleDisputes = SimpleDisputes; type Slash = Treasury; type ValidityBond = ValidityBond; type WeightInfo = zrml_prediction_markets::weights::WeightInfo; } - impl zrml_rikiddo::Config for Runtime { - type Timestamp = Timestamp; - type Balance = Balance; - type FixedTypeU = FixedU128; - type FixedTypeS = FixedI128; - type BalanceFractionalDecimals = BalanceFractionalDecimals; - type PoolId = PoolId; - type Rikiddo = RikiddoSigmoidMV< - Self::FixedTypeU, - Self::FixedTypeS, - FeeSigmoid, - EmaMarketVolume, - >; - } - - impl zrml_simple_disputes::Config for Runtime { - type Currency = Balances; - type OutcomeBond = OutcomeBond; - type OutcomeFactor = OutcomeFactor; - type DisputeResolution = zrml_prediction_markets::Pallet; - type RuntimeEvent = RuntimeEvent; - type MarketCommons = MarketCommons; - type MaxDisputes = MaxDisputes; - type PalletId = SimpleDisputesPalletId; - type WeightInfo = zrml_simple_disputes::weights::WeightInfo; - } - impl zrml_global_disputes::Config for Runtime { type AddOutcomePeriod = AddOutcomePeriod; type Currency = Balances; @@ -1579,14 +1426,12 @@ macro_rules! create_runtime_api { list_benchmark!(list, extra, zrml_swaps, Swaps); list_benchmark!(list, extra, zrml_authorized, Authorized); list_benchmark!(list, extra, zrml_court, Court); - list_benchmark!(list, extra, zrml_simple_disputes, SimpleDisputes); list_benchmark!(list, extra, zrml_global_disputes, GlobalDisputes); list_benchmark!(list, extra, zrml_orderbook, Orderbook); list_benchmark!(list, extra, zrml_parimutuel, Parimutuel); list_benchmark!(list, extra, zrml_hybrid_router, HybridRouter); #[cfg(not(feature = "parachain"))] list_benchmark!(list, extra, zrml_prediction_markets, PredictionMarkets); - list_benchmark!(list, extra, zrml_liquidity_mining, LiquidityMining); list_benchmark!(list, extra, zrml_styx, Styx); list_benchmark!(list, extra, zrml_neo_swaps, NeoSwaps); @@ -1670,14 +1515,12 @@ macro_rules! create_runtime_api { add_benchmark!(params, batches, zrml_swaps, Swaps); add_benchmark!(params, batches, zrml_authorized, Authorized); add_benchmark!(params, batches, zrml_court, Court); - add_benchmark!(params, batches, zrml_simple_disputes, SimpleDisputes); add_benchmark!(params, batches, zrml_global_disputes, GlobalDisputes); add_benchmark!(params, batches, zrml_orderbook, Orderbook); add_benchmark!(params, batches, zrml_parimutuel, Parimutuel); add_benchmark!(params, batches, zrml_hybrid_router, HybridRouter); #[cfg(not(feature = "parachain"))] add_benchmark!(params, batches, zrml_prediction_markets, PredictionMarkets); - add_benchmark!(params, batches, zrml_liquidity_mining, LiquidityMining); add_benchmark!(params, batches, zrml_styx, Styx); add_benchmark!(params, batches, zrml_neo_swaps, NeoSwaps); @@ -2321,9 +2164,7 @@ macro_rules! create_common_tests { #[test_case(AuthorizedPalletId::get(); "authorized")] #[test_case(CourtPalletId::get(); "court")] - #[test_case(LiquidityMiningPalletId::get(); "liquidity_mining")] #[test_case(PmPalletId::get(); "prediction_markets")] - #[test_case(SimpleDisputesPalletId::get(); "simple_disputes")] #[test_case(SwapsPalletId::get(); "swaps")] #[test_case(TreasuryPalletId::get(); "treasury")] fn whitelisted_pallet_accounts_dont_get_reaped(pallet_id: PalletId) { diff --git a/runtime/zeitgeist/Cargo.toml b/runtime/zeitgeist/Cargo.toml index f02b27715..f6eb9e241 100644 --- a/runtime/zeitgeist/Cargo.toml +++ b/runtime/zeitgeist/Cargo.toml @@ -44,7 +44,6 @@ sp-session = { workspace = true } sp-std = { workspace = true } sp-transaction-pool = { workspace = true } sp-version = { workspace = true } -substrate-fixed = { workspace = true, features = ["serde"] } # Try-Runtime @@ -112,14 +111,11 @@ zrml-authorized = { workspace = true } zrml-court = { workspace = true } zrml-global-disputes = { workspace = true, optional = true } zrml-hybrid-router = { workspace = true } -zrml-liquidity-mining = { workspace = true } zrml-market-commons = { workspace = true } zrml-neo-swaps = { workspace = true } zrml-orderbook = { workspace = true } zrml-parimutuel = { workspace = true } zrml-prediction-markets = { workspace = true } -zrml-rikiddo = { workspace = true } -zrml-simple-disputes = { workspace = true } zrml-styx = { workspace = true } zrml-swaps = { workspace = true } zrml-swaps-runtime-api = { workspace = true } @@ -217,11 +213,9 @@ runtime-benchmarks = [ "zrml-authorized/runtime-benchmarks", "zrml-court/runtime-benchmarks", "zrml-hybrid-router/runtime-benchmarks", - "zrml-liquidity-mining/runtime-benchmarks", "zrml-neo-swaps/runtime-benchmarks", "zrml-parimutuel/runtime-benchmarks", "zrml-prediction-markets/runtime-benchmarks", - "zrml-simple-disputes/runtime-benchmarks", "zrml-global-disputes/runtime-benchmarks", "zrml-styx/runtime-benchmarks", "zrml-swaps/runtime-benchmarks", @@ -272,7 +266,6 @@ std = [ "sp-session/std", "sp-transaction-pool/std", "sp-version/std", - "substrate-fixed/std", # Benchmark @@ -325,13 +318,10 @@ std = [ "zrml-authorized/std", "zrml-court/std", "zrml-hybrid-router/std", - "zrml-liquidity-mining/std", "zrml-market-commons/std", "zrml-neo-swaps/std", "zrml-parimutuel/std", "zrml-prediction-markets/std", - "zrml-rikiddo/std", - "zrml-simple-disputes/std", "zrml-global-disputes/std", "zrml-swaps-runtime-api/std", "zrml-styx/std", @@ -381,13 +371,10 @@ try-runtime = [ "zrml-authorized/try-runtime", "zrml-court/try-runtime", "zrml-hybrid-router/try-runtime", - "zrml-liquidity-mining/try-runtime", "zrml-market-commons/try-runtime", "zrml-neo-swaps/try-runtime", "zrml-parimutuel/try-runtime", "zrml-prediction-markets/try-runtime", - "zrml-rikiddo/try-runtime", - "zrml-simple-disputes/try-runtime", "zrml-global-disputes/try-runtime", "zrml-styx/try-runtime", "zrml-swaps/try-runtime", diff --git a/runtime/zeitgeist/src/lib.rs b/runtime/zeitgeist/src/lib.rs index 35d5adf80..a29bb28f1 100644 --- a/runtime/zeitgeist/src/lib.rs +++ b/runtime/zeitgeist/src/lib.rs @@ -49,9 +49,7 @@ use pallet_collective::{EnsureProportionAtLeast, EnsureProportionMoreThan, Prime use sp_runtime::traits::{AccountIdConversion, AccountIdLookup, BlakeTwo256}; #[cfg(feature = "std")] use sp_version::NativeVersion; -use substrate_fixed::{types::extra::U33, FixedI128, FixedU128}; -use zeitgeist_primitives::{constants::*, types::*}; -use zrml_rikiddo::types::{EmaMarketVolume, FeeSigmoid, RikiddoSigmoidMV}; +use zeitgeist_primitives::types::*; #[cfg(feature = "parachain")] use { frame_support::traits::{AsEnsureOriginWithArg, Everything}, @@ -101,8 +99,6 @@ pub type ContractsCallfilter = Nothing; #[derive(scale_info::TypeInfo)] pub struct IsCallable; -// Currently disables Rikiddo and creation of markets using SimpleDisputes -// dispute mechanism. impl Contains for IsCallable { fn contains(runtime_call: &RuntimeCall) -> bool { #[cfg(feature = "parachain")] @@ -118,9 +114,8 @@ impl Contains for IsCallable { set_code as set_code_contracts, }; use pallet_vesting::Call::force_vested_transfer; - use zeitgeist_primitives::types::MarketDisputeMechanism::SimpleDisputes; use zrml_prediction_markets::Call::{ - admin_move_market_to_closed, admin_move_market_to_resolved, create_market, edit_market, + admin_move_market_to_closed, admin_move_market_to_resolved, }; use zrml_swaps::Call::force_pool_exit; @@ -158,18 +153,11 @@ impl Contains for IsCallable { RuntimeCall::Council(set_members { .. }) => false, #[cfg(feature = "parachain")] RuntimeCall::DmpQueue(service_overweight { .. }) => false, - RuntimeCall::LiquidityMining(_) => false, - RuntimeCall::PredictionMarkets(inner_call) => { - match inner_call { - // Disable SimpleDisputes dispute resolution mechanism - create_market { dispute_mechanism: Some(SimpleDisputes), .. } => false, - edit_market { dispute_mechanism: Some(SimpleDisputes), .. } => false, - admin_move_market_to_closed { .. } => false, - admin_move_market_to_resolved { .. } => false, - _ => true, - } - } - RuntimeCall::SimpleDisputes(_) => false, + RuntimeCall::PredictionMarkets(inner_call) => match inner_call { + admin_move_market_to_closed { .. } => false, + admin_move_market_to_resolved { .. } => false, + _ => true, + }, RuntimeCall::Swaps(inner_call) => match inner_call { force_pool_exit { .. } => true, _ => false, diff --git a/runtime/zeitgeist/src/parameters.rs b/runtime/zeitgeist/src/parameters.rs index 94d54089e..a9bdfb485 100644 --- a/runtime/zeitgeist/src/parameters.rs +++ b/runtime/zeitgeist/src/parameters.rs @@ -187,10 +187,6 @@ parameter_types! { /// be another trie item whose value is the size of an account ID plus 32 bytes. pub const SubAccountDeposit: Balance = deposit(1, 53); - // Liquidity Mining parameters - /// Pallet identifier, mainly used for named balance reserves. DO NOT CHANGE. - pub const LiquidityMiningPalletId: PalletId = LM_PALLET_ID; - // Multisig // One storage item; key size is 32; value is size 4+4+16+32 bytes = 56 bytes. pub const DepositBase: Balance = deposit(1, 88); @@ -245,9 +241,6 @@ parameter_types! { pub const MaxOracleDuration: BlockNumber = MAX_ORACLE_DURATION; /// Maximum string length allowed for reject reason. pub const MaxRejectReasonLen: u32 = 1024; - // 2_678_400_000 = 31 days. - /// Maximum number of milliseconds a Rikiddo market can be in subsidy gathering phase. - pub const MaxSubsidyPeriod: Moment = 2_678_400_000; /// Minimum number of categories. The trivial minimum is 2, which represents a binary market. pub const MinCategories: u16 = 2; /// The dispute_duration is time where users can dispute the outcome. @@ -255,9 +248,6 @@ parameter_types! { pub const MinDisputeDuration: BlockNumber = MIN_DISPUTE_DURATION; /// Minimum block period for an oracle_duration. pub const MinOracleDuration: BlockNumber = MIN_ORACLE_DURATION; - // 60_000 = 1 minute. Should be raised to something more reasonable in the future. - /// Minimum number of milliseconds a Rikiddo market must be in subsidy gathering phase. - pub const MinSubsidyPeriod: Moment = 60_000; /// (Slashable) The orcale bond. Slashed in case the final outcome does not match the /// outcome the oracle reported. pub const OracleBond: Balance = 200 * BASE; @@ -296,16 +286,6 @@ parameter_types! { pub const MaxScheduledPerBlock: u32 = 50; pub const NoPreimagePostponement: Option = Some(5 * BLOCKS_PER_MINUTE); - // Simple disputes parameters - /// Pallet identifier, mainly used for named balance reserves. DO NOT CHANGE. - pub const SimpleDisputesPalletId: PalletId = SD_PALLET_ID; - /// (Slashable) Bond that is provided for overriding the last outcome addition. - /// Slashed in case the final outcome does not match the dispute for which the `OutcomeBond` - /// was deposited. - pub const OutcomeBond: Balance = 2_000 * BASE; - /// `OutcomeBond` is increased by this factor after every new outcome addition. - pub const OutcomeFactor: Balance = 2 * BASE; - // Swaps parameters /// A precentage from the withdrawal amount a liquidity provider wants to withdraw /// from a pool before the pool is closed. @@ -320,10 +300,6 @@ parameter_types! { pub const MaxTotalWeight: Balance = MaxWeight::get() * 2; /// The maximum weight a single asset can have. pub const MaxWeight: Balance = 64 * BASE; - /// Minimum subsidy required to launch a Rikiddo pool. - pub const MinSubsidy: Balance = 100 * BASE; - /// Minimum subsidy a single account can provide. - pub const MinSubsidyPerAccount: Balance = MinSubsidy::get(); /// Minimum weight a single asset can have. pub const MinWeight: Balance = BASE; /// Pallet identifier, mainly used for named balance reserves. DO NOT CHANGE. diff --git a/scripts/benchmarks/configuration.sh b/scripts/benchmarks/configuration.sh index 9f13d1edf..f363f34ec 100644 --- a/scripts/benchmarks/configuration.sh +++ b/scripts/benchmarks/configuration.sh @@ -8,7 +8,7 @@ export FRAME_PALLETS=( pallet_democracy pallet_identity pallet_membership pallet_multisig pallet_preimage \ pallet_proxy pallet_scheduler pallet_timestamp pallet_treasury pallet_utility \ pallet_vesting \ -) # pallet_grandpa ) +) export FRAME_PALLETS_RUNS="${FRAME_PALLETS_RUNS:-20}" export FRAME_PALLETS_STEPS="${FRAME_PALLETS_STEPS:-50}" @@ -27,8 +27,8 @@ export ORML_PALLETS_STEPS="${ORML_PALLETS_STEPS:-50}" export ORML_WEIGHT_TEMPLATE="./misc/orml_weight_template.hbs" export ZEITGEIST_PALLETS=( - zrml_authorized zrml_court zrml_global_disputes zrml_hybrid_router zrml_liquidity_mining \ - zrml_neo_swaps zrml_orderbook zrml_parimutuel zrml_prediction_markets zrml_swaps zrml_styx \ + zrml_authorized zrml_court zrml_global_disputes zrml_hybrid_router zrml_neo_swaps \ + zrml_orderbook zrml_parimutuel zrml_prediction_markets zrml_swaps zrml_styx \ ) export ZEITGEIST_PALLETS_RUNS="${ZEITGEIST_PALLETS_RUNS:-20}" export ZEITGEIST_PALLETS_STEPS="${ZEITGEIST_PALLETS_STEPS:-50}" diff --git a/scripts/tests/fuzz.sh b/scripts/tests/fuzz.sh index f0c9eb67b..beff95f26 100755 --- a/scripts/tests/fuzz.sh +++ b/scripts/tests/fuzz.sh @@ -29,7 +29,7 @@ fi # Using specific_run = (RUN * FUZZ_FACT) / BASE allows us to specify # a hardware- and fuzz target specific run count. BASE=1000 -RUNS="${RUNS:-50000}" +RUNS=1 CREATE_POOL_FACT=500 POOL_JOIN_FACT=150 @@ -41,19 +41,6 @@ POOL_EXIT_WITH_EXACT_ASSET_AMOUNT_FACT=150 POOL_EXIT_WITH_EXACT_POOL_AMOUNT_FACT=150 POOL_EXIT_FACT=150 -FEE_SIGMOID_FACT=50000 -FIXEDI_TO_FIXEDU_FACT=100000 -FIXEDU_TO_FIXEDI_FACT=100000 -BALANCE_TO_FIXEDU_FACT=8000 -FIXEDU_TO_BALANCE_FACT=4000 -EMA_MARKET_VOLUME_FIRST_STATE_FACT=5000 -EMA_MARKET_VOLUME_SECOND_STATE_FACT=7000 -EMA_MARKET_VOLUME_THIRD_STATE_FACT=7000 -EMA_MARKET_VOLUME_ESTIMATE_EMA_FACT=7000 -RIKIDDO_WITH_INITIAL_FEE_FACT=2300 -RIKIDDO_WITH_CALCULATED_FEE_FACT=1750 -RIKIDDO_PALLET_FACT=1000 - # --- Prediction Market Pallet fuzz tests --- cargo fuzz run --release --fuzz-dir zrml/prediction-markets/fuzz pm_full_workflow -- -runs=$RUNS @@ -70,20 +57,3 @@ cargo fuzz run --release --fuzz-dir zrml/swaps/fuzz pool_exit -- -runs=$(($(($RU # --- Orderbook-v1 Pallet fuzz tests --- cargo fuzz run --release --fuzz-dir zrml/orderbook/fuzz orderbook_v1_full_workflow -- -runs=$RUNS - -# --- Rikiddo Pallet fuzz tests --- -# Profile release is required here since it triggers debug assertions otherwise -# Using the default RUNS multiplier, each fuzz test needs approx. 6-7 seconds -cargo fuzz run --release --fuzz-dir zrml/rikiddo/fuzz fee_sigmoid -- -runs=$(($(($RUNS * $FEE_SIGMOID_FACT)) / $BASE)) -cargo fuzz run --release --fuzz-dir zrml/rikiddo/fuzz fixedi_to_fixedu_conversion -- -runs=$(($(($RUNS * $FIXEDI_TO_FIXEDU_FACT)) / $BASE)) -cargo fuzz run --release --fuzz-dir zrml/rikiddo/fuzz fixedu_to_fixedi_conversion -- -runs=$(($(($RUNS * $FIXEDU_TO_FIXEDI_FACT)) / $BASE)) -cargo fuzz run --release --fuzz-dir zrml/rikiddo/fuzz balance_to_fixedu_conversion -- -runs=$(($(($RUNS * $BALANCE_TO_FIXEDU_FACT)) / $BASE)) -cargo fuzz run --release --fuzz-dir zrml/rikiddo/fuzz fixedu_to_balance_conversion -- -runs=$(($(($RUNS * $FIXEDU_TO_BALANCE_FACT)) / $BASE)) -cargo fuzz run --release --fuzz-dir zrml/rikiddo/fuzz ema_market_volume_first_state -- -runs=$(($(($RUNS * $EMA_MARKET_VOLUME_FIRST_STATE_FACT)) / $BASE)) -cargo fuzz run --release --fuzz-dir zrml/rikiddo/fuzz ema_market_volume_second_state -- -runs=$(($(($RUNS * $EMA_MARKET_VOLUME_SECOND_STATE_FACT)) / $BASE)) -cargo fuzz run --release --fuzz-dir zrml/rikiddo/fuzz ema_market_volume_third_state -- -runs=$(($(($RUNS * $EMA_MARKET_VOLUME_THIRD_STATE_FACT)) / $BASE)) -cargo fuzz run --release --fuzz-dir zrml/rikiddo/fuzz ema_market_volume_estimate_ema -- -runs=$(($(($RUNS * $EMA_MARKET_VOLUME_ESTIMATE_EMA_FACT)) / $BASE)) -cargo fuzz run --release --fuzz-dir zrml/rikiddo/fuzz rikiddo_with_initial_fee -- -runs=$(($(($RUNS * $RIKIDDO_WITH_INITIAL_FEE_FACT)) / $BASE)) -cargo fuzz run --release --fuzz-dir zrml/rikiddo/fuzz rikiddo_with_calculated_fee -- -runs=$(($(($RUNS * $RIKIDDO_WITH_CALCULATED_FEE_FACT)) / $BASE)) -# This actually needs approx. 107 seconds. Need to find a way to optimize fuzzing on-chain -cargo fuzz run --release --fuzz-dir zrml/rikiddo/fuzz rikiddo_pallet -- -runs=$(($(($RUNS * $RIKIDDO_PALLET_FACT)) / $BASE)) diff --git a/zrml/court/src/tests.rs b/zrml/court/src/tests.rs index 3e9450f49..d8da54ca5 100644 --- a/zrml/court/src/tests.rs +++ b/zrml/court/src/tests.rs @@ -1744,7 +1744,7 @@ fn check_appealable_market_fails_if_dispute_mechanism_wrong() { let market_id = >::get(court_id).unwrap(); MarketCommons::mutate_market(&market_id, |market| { - market.dispute_mechanism = Some(MarketDisputeMechanism::SimpleDisputes); + market.dispute_mechanism = Some(MarketDisputeMechanism::Authorized); Ok(()) }) .unwrap(); @@ -2454,7 +2454,7 @@ fn reassign_court_stakes_rewards_treasury_if_no_winner() { fn on_dispute_denies_non_court_markets() { ExtBuilder::default().build().execute_with(|| { let mut market = DEFAULT_MARKET; - market.dispute_mechanism = Some(MarketDisputeMechanism::SimpleDisputes); + market.dispute_mechanism = Some(MarketDisputeMechanism::Authorized); assert_noop!( Court::on_dispute(&0, &market), Error::::MarketDoesNotHaveCourtMechanism @@ -2491,7 +2491,7 @@ fn on_resolution_fails_if_court_not_found() { fn on_resolution_denies_non_court_markets() { ExtBuilder::default().build().execute_with(|| { let mut market = DEFAULT_MARKET; - market.dispute_mechanism = Some(MarketDisputeMechanism::SimpleDisputes); + market.dispute_mechanism = Some(MarketDisputeMechanism::Authorized); assert_noop!( Court::on_resolution(&0, &market), Error::::MarketDoesNotHaveCourtMechanism @@ -2503,7 +2503,7 @@ fn on_resolution_denies_non_court_markets() { fn exchange_fails_if_non_court_markets() { ExtBuilder::default().build().execute_with(|| { let mut market = DEFAULT_MARKET; - market.dispute_mechanism = Some(MarketDisputeMechanism::SimpleDisputes); + market.dispute_mechanism = Some(MarketDisputeMechanism::Authorized); assert_noop!( Court::exchange(&0, &market, &ORACLE_REPORT, NegativeImbalance::::zero()), Error::::MarketDoesNotHaveCourtMechanism @@ -2614,7 +2614,7 @@ fn on_global_dispute_removes_draws() { fn on_global_dispute_fails_if_wrong_dispute_mechanism() { ExtBuilder::default().build().execute_with(|| { let mut market = DEFAULT_MARKET; - market.dispute_mechanism = Some(MarketDisputeMechanism::SimpleDisputes); + market.dispute_mechanism = Some(MarketDisputeMechanism::Authorized); assert_noop!( Court::on_global_dispute(&0, &market), Error::::MarketDoesNotHaveCourtMechanism diff --git a/zrml/global-disputes/src/lib.rs b/zrml/global-disputes/src/lib.rs index 4449880b4..c111bc7d7 100644 --- a/zrml/global-disputes/src/lib.rs +++ b/zrml/global-disputes/src/lib.rs @@ -79,9 +79,6 @@ mod pallet { pub type LockInfoOf = BoundedVec<(MarketIdOf, BalanceOf), ::MaxGlobalDisputeVotes>; - // TODO(#968): to remove after the storage migration - pub type WinnerInfoOf = OldWinnerInfo, OwnerInfoOf>; - /// The current storage version. const STORAGE_VERSION: StorageVersion = StorageVersion::new(1); const LOG_TARGET: &str = "runtime::zrml-global-disputes"; @@ -180,11 +177,6 @@ mod pallet { pub type GlobalDisputesInfo = StorageMap<_, Twox64Concat, MarketIdOf, GlobalDisputeInfoOf, OptionQuery>; - // TODO(#986): to remove after the storage migration - #[pallet::storage] - pub type Winners = - StorageMap<_, Twox64Concat, MarketIdOf, WinnerInfoOf, OptionQuery>; - #[pallet::event] #[pallet::generate_deposit(fn deposit_event)] pub enum Event diff --git a/zrml/global-disputes/src/types.rs b/zrml/global-disputes/src/types.rs index 10c83a49b..de9f4d14c 100644 --- a/zrml/global-disputes/src/types.rs +++ b/zrml/global-disputes/src/types.rs @@ -1,4 +1,4 @@ -// Copyright 2022-2023 Forecasting Technologies LTD. +// Copyright 2022-2024 Forecasting Technologies LTD. // // This file is part of Zeitgeist. // @@ -93,35 +93,6 @@ pub enum GdStatus { Destroyed, } -// TODO(#986): to remove after the storage migration - -/// The information about a voting outcome of a global dispute. -#[derive(Debug, TypeInfo, Decode, Encode, MaxEncodedLen, Clone, PartialEq, Eq)] -pub struct OldOutcomeInfo { - /// The current sum of all locks on this outcome. - pub outcome_sum: Balance, - /// The vector of owners of the outcome. - pub owners: OwnerInfo, -} - -/// The information about the current highest winning outcome. -#[derive(TypeInfo, Decode, Encode, MaxEncodedLen, Clone, PartialEq, Eq)] -pub struct OldWinnerInfo { - /// The outcome, which is in the lead. - pub outcome: OutcomeReport, - /// The information about the winning outcome. - pub outcome_info: OldOutcomeInfo, - /// Check, if the global dispute is finished. - pub is_finished: bool, -} - -impl OldWinnerInfo { - pub fn new(outcome: OutcomeReport, vote_sum: Balance) -> Self { - let outcome_info = OldOutcomeInfo { outcome_sum: vote_sum, owners: Default::default() }; - OldWinnerInfo { outcome, is_finished: false, outcome_info } - } -} - /// An initial vote outcome item with the outcome owner and the initial vote amount. pub struct InitialItem { /// The outcome which is added as initial global dispute vote possibility. diff --git a/zrml/global-disputes/src/utils.rs b/zrml/global-disputes/src/utils.rs index 277ec7189..d7005b724 100644 --- a/zrml/global-disputes/src/utils.rs +++ b/zrml/global-disputes/src/utils.rs @@ -43,9 +43,7 @@ where creator_fee: sp_runtime::Perbill::zero(), creator: T::GlobalDisputesPalletId::get().into_account_truncating(), market_type: zeitgeist_primitives::types::MarketType::Scalar(0..=u128::MAX), - dispute_mechanism: Some( - zeitgeist_primitives::types::MarketDisputeMechanism::SimpleDisputes, - ), + dispute_mechanism: Some(zeitgeist_primitives::types::MarketDisputeMechanism::Authorized), metadata: Default::default(), oracle: T::GlobalDisputesPalletId::get().into_account_truncating(), period: zeitgeist_primitives::types::MarketPeriod::Block(Default::default()), diff --git a/zrml/hybrid-router/Cargo.toml b/zrml/hybrid-router/Cargo.toml index 38899d9cf..21aede3a8 100644 --- a/zrml/hybrid-router/Cargo.toml +++ b/zrml/hybrid-router/Cargo.toml @@ -25,11 +25,9 @@ xcm-builder = { workspace = true, optional = true } zrml-authorized = { workspace = true, optional = true } zrml-court = { workspace = true, optional = true } zrml-global-disputes = { workspace = true, optional = true } -zrml-liquidity-mining = { workspace = true, optional = true } zrml-neo-swaps = { workspace = true, optional = true } zrml-orderbook = { workspace = true, optional = true } zrml-prediction-markets = { workspace = true, optional = true } -zrml-simple-disputes = { workspace = true, optional = true } [dev-dependencies] env_logger = { workspace = true } @@ -57,11 +55,9 @@ mock = [ "zrml-orderbook/default", "zrml-prediction-markets/default", "zrml-prediction-markets/mock", - "zrml-simple-disputes/default", "zrml-authorized/default", "zrml-court/default", "zrml-global-disputes/default", - "zrml-liquidity-mining/default", ] parachain = [ "zrml-prediction-markets/parachain", diff --git a/zrml/hybrid-router/src/mock.rs b/zrml/hybrid-router/src/mock.rs index 8246d648e..1a7a454ad 100644 --- a/zrml/hybrid-router/src/mock.rs +++ b/zrml/hybrid-router/src/mock.rs @@ -42,16 +42,15 @@ use zeitgeist_primitives::{ CloseEarlyProtectionBlockPeriod, CloseEarlyProtectionTimeFramePeriod, CloseEarlyRequestBond, CloseEarlyTimeFramePeriod, CorrectionPeriod, CourtPalletId, ExistentialDeposit, ExistentialDeposits, GdVotingPeriod, GetNativeCurrencyId, - GlobalDisputeLockId, GlobalDisputesPalletId, HybridRouterPalletId, InflationPeriod, - LiquidityMiningPalletId, LockId, MaxAppeals, MaxApprovals, MaxCourtParticipants, - MaxCreatorFee, MaxDelegations, MaxDisputeDuration, MaxDisputes, MaxEditReasonLen, - MaxGlobalDisputeVotes, MaxGracePeriod, MaxLiquidityTreeDepth, MaxLocks, MaxMarketLifetime, - MaxOracleDuration, MaxOrders, MaxOwners, MaxRejectReasonLen, MaxReserves, MaxSelectedDraws, - MaxYearlyInflation, MinCategories, MinDisputeDuration, MinJurorStake, MinOracleDuration, - MinOutcomeVoteAmount, MinimumPeriod, NeoMaxSwapFee, NeoSwapsPalletId, OrderbookPalletId, - OutcomeBond, OutcomeFactor, OutsiderBond, PmPalletId, RemoveKeysLimit, RequestInterval, - SimpleDisputesPalletId, TreasuryPalletId, VotePeriod, VotingOutcomeFee, BASE, CENT, - MAX_ASSETS, + GlobalDisputeLockId, GlobalDisputesPalletId, HybridRouterPalletId, InflationPeriod, LockId, + MaxAppeals, MaxApprovals, MaxCourtParticipants, MaxCreatorFee, MaxDelegations, + MaxDisputeDuration, MaxDisputes, MaxEditReasonLen, MaxGlobalDisputeVotes, MaxGracePeriod, + MaxLiquidityTreeDepth, MaxLocks, MaxMarketLifetime, MaxOracleDuration, MaxOrders, + MaxOwners, MaxRejectReasonLen, MaxReserves, MaxSelectedDraws, MaxYearlyInflation, + MinCategories, MinDisputeDuration, MinJurorStake, MinOracleDuration, MinOutcomeVoteAmount, + MinimumPeriod, NeoMaxSwapFee, NeoSwapsPalletId, OrderbookPalletId, OutsiderBond, + PmPalletId, RemoveKeysLimit, RequestInterval, TreasuryPalletId, VotePeriod, + VotingOutcomeFee, BASE, CENT, MAX_ASSETS, }, traits::DistributeFees, types::{ @@ -157,11 +156,9 @@ construct_runtime!( Balances: pallet_balances, Court: zrml_court, AssetManager: orml_currencies, - LiquidityMining: zrml_liquidity_mining, MarketCommons: zrml_market_commons, PredictionMarkets: zrml_prediction_markets, RandomnessCollectiveFlip: pallet_insecure_randomness_collective_flip, - SimpleDisputes: zrml_simple_disputes, GlobalDisputes: zrml_global_disputes, System: frame_system, Timestamp: pallet_timestamp, @@ -229,7 +226,6 @@ impl zrml_prediction_markets::Config for Runtime { type DisputeBond = DisputeBond; type RuntimeEvent = RuntimeEvent; type GlobalDisputes = GlobalDisputes; - type LiquidityMining = LiquidityMining; type MaxCategories = MaxCategories; type MaxDisputes = MaxDisputes; type MinDisputeDuration = MinDisputeDuration; @@ -249,7 +245,6 @@ impl zrml_prediction_markets::Config for Runtime { type RequestEditOrigin = EnsureSignedBy; type ResolveOrigin = EnsureSignedBy; type AssetManager = AssetManager; - type SimpleDisputes = SimpleDisputes; type Slash = Treasury; type ValidityBond = ValidityBond; type WeightInfo = zrml_prediction_markets::weights::WeightInfo; @@ -294,15 +289,6 @@ impl zrml_court::Config for Runtime { type WeightInfo = zrml_court::weights::WeightInfo; } -impl zrml_liquidity_mining::Config for Runtime { - type Currency = Balances; - type RuntimeEvent = RuntimeEvent; - type MarketCommons = MarketCommons; - type MarketId = MarketId; - type PalletId = LiquidityMiningPalletId; - type WeightInfo = zrml_liquidity_mining::weights::WeightInfo; -} - impl frame_system::Config for Runtime { type AccountData = pallet_balances::AccountData; type AccountId = AccountIdTest; @@ -408,18 +394,6 @@ impl pallet_timestamp::Config for Runtime { type WeightInfo = (); } -impl zrml_simple_disputes::Config for Runtime { - type Currency = Balances; - type RuntimeEvent = RuntimeEvent; - type OutcomeBond = OutcomeBond; - type OutcomeFactor = OutcomeFactor; - type DisputeResolution = zrml_prediction_markets::Pallet; - type MarketCommons = MarketCommons; - type MaxDisputes = MaxDisputes; - type PalletId = SimpleDisputesPalletId; - type WeightInfo = zrml_simple_disputes::weights::WeightInfo; -} - impl zrml_global_disputes::Config for Runtime { type AddOutcomePeriod = AddOutcomePeriod; type RuntimeEvent = RuntimeEvent; diff --git a/zrml/liquidity-mining/Cargo.toml b/zrml/liquidity-mining/Cargo.toml deleted file mode 100644 index e0d684b27..000000000 --- a/zrml/liquidity-mining/Cargo.toml +++ /dev/null @@ -1,45 +0,0 @@ -[dependencies] -frame-benchmarking = { workspace = true, optional = true } -frame-support = { workspace = true } -frame-system = { workspace = true } -log = { workspace = true } -parity-scale-codec = { workspace = true, features = ["derive", "max-encoded-len"] } -scale-info = { workspace = true, features = ["derive"] } -serde = { workspace = true, optional = true } -sp-runtime = { workspace = true } -zeitgeist-primitives = { workspace = true } -zrml-market-commons = { workspace = true } - -[dev-dependencies] -env_logger = { workspace = true } -pallet-balances = { workspace = true, features = ["default"] } -pallet-timestamp = { workspace = true, features = ["default"] } -sp-io = { workspace = true, features = ["default"] } -zeitgeist-primitives = { workspace = true, features = ["mock", "default"] } - -[features] -default = ["std"] -runtime-benchmarks = [ - "frame-benchmarking/runtime-benchmarks", - "frame-support/runtime-benchmarks", - "frame-system/runtime-benchmarks", -] -std = [ - "frame-benchmarking?/std", - "frame-support/std", - "frame-system/std", - "parity-scale-codec/std", - "serde/std", - "sp-runtime/std", - "zeitgeist-primitives/std", - "zrml-market-commons/std", -] -try-runtime = [ - "frame-support/try-runtime", -] - -[package] -authors = ["Zeitgeist PM "] -edition.workspace = true -name = "zrml-liquidity-mining" -version = "0.5.3" diff --git a/zrml/liquidity-mining/README.md b/zrml/liquidity-mining/README.md deleted file mode 100644 index c15177065..000000000 --- a/zrml/liquidity-mining/README.md +++ /dev/null @@ -1,12 +0,0 @@ -# Liquidity Mining Module - -## Overview - -Manages and distributes incentives to liquidity providers - -Each block has a maximum allowed amount of ZTG that is distributed among the -`PoolShare` owners of that same block. Over time this amount will increase until -a market closes and then all rewards will be distributed accordingly. - -This pallet is mostly self-contained and only need to know about the native -currency. To interact with its functionalities, please use the provided API. diff --git a/zrml/liquidity-mining/src/benchmarks.rs b/zrml/liquidity-mining/src/benchmarks.rs deleted file mode 100644 index 05e32b749..000000000 --- a/zrml/liquidity-mining/src/benchmarks.rs +++ /dev/null @@ -1,42 +0,0 @@ -// Copyright 2023 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 . - -#![allow( - // Auto-generated code is a no man's land - clippy::arithmetic_side_effects -)] -#![cfg(feature = "runtime-benchmarks")] - -use crate::pallet::{BalanceOf, Call, Config, Pallet}; -#[cfg(test)] -use crate::Pallet as LiquidityMining; -use frame_benchmarking::benchmarks; -use frame_system::RawOrigin; -use sp_runtime::traits::Bounded; - -benchmarks! { - set_per_block_distribution { - let balance = BalanceOf::::max_value(); - }: set_per_block_distribution(RawOrigin::Root, balance) - - impl_benchmark_test_suite!( - LiquidityMining, - crate::mock::ExtBuilder::default().build(), - crate::mock::Runtime, - ); -} diff --git a/zrml/liquidity-mining/src/lib.rs b/zrml/liquidity-mining/src/lib.rs deleted file mode 100644 index ad96e8317..000000000 --- a/zrml/liquidity-mining/src/lib.rs +++ /dev/null @@ -1,347 +0,0 @@ -// 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 . - -#![doc = include_str!("../README.md")] -#![cfg_attr(not(feature = "std"), no_std)] - -extern crate alloc; - -#[macro_use] -mod utils; - -mod benchmarks; -mod liquidity_mining_pallet_api; -mod mock; -mod owned_values_params; -mod tests; -mod track_incentives_based_on_bought_shares; -mod track_incentives_based_on_sold_shares; -pub mod weights; - -pub use liquidity_mining_pallet_api::LiquidityMiningPalletApi; -pub use pallet::*; - -#[frame_support::pallet] -mod pallet { - use crate::{ - owned_values_params::OwnedValuesParams, - track_incentives_based_on_bought_shares::TrackIncentivesBasedOnBoughtShares, - track_incentives_based_on_sold_shares::TrackIncentivesBasedOnSoldShares, - utils::{ - calculate_average_blocks_of_a_time_period, calculate_perthousand, - calculate_perthousand_value, - }, - weights::WeightInfoZeitgeist, - LiquidityMiningPalletApi, - }; - use alloc::vec::Vec; - use core::marker::PhantomData; - use frame_support::{ - dispatch::DispatchResult, - storage::{ - types::{StorageDoubleMap, StorageValue, ValueQuery}, - with_transaction, - }, - traits::{ - BuildGenesisConfig, Currency, ExistenceRequirement, Get, Hooks, IsType, StorageVersion, - WithdrawReasons, - }, - Blake2_128Concat, PalletId, Twox64Concat, - }; - use frame_system::{ - ensure_root, - pallet_prelude::{BlockNumberFor, OriginFor}, - }; - use sp_runtime::{ - traits::{AccountIdConversion, Saturating}, - TransactionOutcome, - }; - use zeitgeist_primitives::{ - traits::MarketId, - types::{MarketPeriod, MaxRuntimeUsize}, - }; - use zrml_market_commons::MarketCommonsPalletApi; - - /// The current storage version. - const STORAGE_VERSION: StorageVersion = StorageVersion::new(0); - const LOG_TARGET: &str = "runtime::zrml-liquidity-mining"; - - pub(crate) type AccountIdOf = ::AccountId; - pub(crate) type BalanceOf = <::Currency as Currency>>::Balance; - pub(crate) type MomentOf = <::MarketCommons as MarketCommonsPalletApi>::Moment; - - #[pallet::call] - impl Pallet { - #[pallet::call_index(0)] - #[pallet::weight(T::WeightInfo::set_per_block_distribution())] - // MARK(non-transactional): `set_per_block_distribution` is infallible. - pub fn set_per_block_distribution( - origin: OriginFor, - #[pallet::compact] per_block_distribution: BalanceOf, - ) -> DispatchResult { - ensure_root(origin)?; - >::put(per_block_distribution); - Ok(()) - } - } - - #[pallet::config] - pub trait Config: frame_system::Config { - type Currency: Currency; - - type RuntimeEvent: From> + IsType<::RuntimeEvent>; - - type MarketCommons: MarketCommonsPalletApi< - AccountId = Self::AccountId, - BlockNumber = BlockNumberFor, - MarketId = Self::MarketId, - Balance = BalanceOf, - >; - - type MarketId: MarketId; - - #[pallet::constant] - type PalletId: Get; - - type WeightInfo: WeightInfoZeitgeist; - } - - #[pallet::event] - #[pallet::generate_deposit(fn deposit_event)] - pub enum Event - where - T: Config, - { - /// The number of markets that received incentives in a block - AddedIncentives(MaxRuntimeUsize), - /// The total amount of incentives distributed to accounts along side the number - /// of accounts that received these incentives. - DistributedIncentives(BalanceOf, MaxRuntimeUsize), - /// The number of markets that subtracted incentives in a block - SubtractedIncentives(MaxRuntimeUsize), - } - - #[pallet::error] - pub enum Error { - /// Pallet account does not have enough funds - FundDoesNotHaveEnoughBalance, - } - - #[pallet::genesis_build] - impl BuildGenesisConfig for GenesisConfig { - fn build(&self) { - let _ = T::Currency::deposit_creating( - &Pallet::::pallet_account_id(), - self.initial_balance, - ); - >::put(self.per_block_distribution); - } - } - - #[derive(scale_info::TypeInfo, Debug)] - #[pallet::genesis_config] - pub struct GenesisConfig { - pub initial_balance: BalanceOf, - pub per_block_distribution: BalanceOf, - } - - impl Default for GenesisConfig - where - T: Config, - { - #[inline] - fn default() -> Self { - Self { - initial_balance: BalanceOf::::from(0u8), - per_block_distribution: BalanceOf::::from(0u8), - } - } - } - - #[pallet::hooks] - impl Hooks> for Pallet { - // Manages incentives on each block finalization. - fn on_finalize(block: BlockNumberFor) { - let fun = || { - let added_len = TrackIncentivesBasedOnBoughtShares::::exec(block)?; - if added_len > 0 { - Self::deposit_event(Event::AddedIncentives(added_len.into())); - } - let subtracted_len = TrackIncentivesBasedOnSoldShares::::exec(); - if subtracted_len > 0 { - Self::deposit_event(Event::SubtractedIncentives(subtracted_len.into())); - } - Some(()) - }; - let _ = with_transaction(|| match fun() { - None => { - log::error!(target: LOG_TARGET, "Block {:?} was not finalized", block); - TransactionOutcome::Rollback(Err("Block was not finalized")) - } - Some(_) => TransactionOutcome::Commit(Ok(())), - }); - } - } - - #[pallet::pallet] - #[pallet::storage_version(STORAGE_VERSION)] - pub struct Pallet(PhantomData); - - impl Pallet - where - T: Config, - { - // pot/fund account - #[inline] - pub(crate) fn pallet_account_id() -> T::AccountId { - T::PalletId::get().into_account_truncating() - } - } - - impl LiquidityMiningPalletApi for Pallet - where - T: Config, - { - type AccountId = T::AccountId; - type Balance = BalanceOf; - type BlockNumber = BlockNumberFor; - type MarketId = T::MarketId; - - fn add_shares( - account_id: Self::AccountId, - market_id: Self::MarketId, - shares: Self::Balance, - ) { - >::mutate(market_id, account_id, |total_shares| { - *total_shares = total_shares.saturating_add(shares); - }) - } - - fn distribute_market_incentives(market_id: &Self::MarketId) -> DispatchResult { - let pallet_account_id = Pallet::::pallet_account_id(); - let mut final_total_incentives = BalanceOf::::from(0u8); - - let values: Vec<_> = >::drain_prefix(market_id) - .filter_map( - |( - account_id, - OwnedValuesParams { - participated_blocks, - perpetual_incentives, - total_incentives: local_total_incentives, - .. - }, - )| { - let actual_perpetual_incentives = { - let opt = match T::MarketCommons::market(market_id).ok()?.period { - MarketPeriod::Block(range) => calculate_perthousand( - participated_blocks, - &range.end.saturating_sub(range.start), - ), - MarketPeriod::Timestamp(range) => calculate_perthousand( - participated_blocks, - &calculate_average_blocks_of_a_time_period::(&range), - ), - }; - let ptd_balance = opt.map(|ptd| ptd.into())?; - calculate_perthousand_value(ptd_balance, perpetual_incentives) - }; - let final_incentives = - actual_perpetual_incentives.saturating_add(local_total_incentives); - final_total_incentives = - final_total_incentives.saturating_add(final_incentives); - Some((account_id, final_incentives)) - }, - ) - .collect(); - - T::Currency::ensure_can_withdraw( - &pallet_account_id, - final_total_incentives, - WithdrawReasons::all(), - T::Currency::free_balance(&pallet_account_id) - .saturating_sub(final_total_incentives), - ) - .map_err(|_err| Error::::FundDoesNotHaveEnoughBalance)?; - - let accounts_len = values.len().into(); - for (account_id, incentives) in values { - T::Currency::transfer( - &pallet_account_id, - &account_id, - incentives, - ExistenceRequirement::AllowDeath, - ) - .map_err(|_err| Error::::FundDoesNotHaveEnoughBalance)?; - } - Self::deposit_event(Event::DistributedIncentives(final_total_incentives, accounts_len)); - Ok(()) - } - - fn remove_shares( - account_id: &Self::AccountId, - market_id: &Self::MarketId, - shares: Self::Balance, - ) { - >::mutate(market_id, account_id, |total_shares| { - *total_shares = total_shares.saturating_add(shares); - }) - } - } - - /// Shares bought in the current block being constructed. Automatically *erased* after each finalized block. - #[pallet::storage] - pub type BlockBoughtShares = StorageDoubleMap< - _, - Blake2_128Concat, - T::MarketId, - Twox64Concat, - T::AccountId, - BalanceOf, - ValueQuery, - >; - - /// Shares sold in the current block being constructed. Automatically *erased* after each finalized block. - #[pallet::storage] - pub type BlockSoldShares = StorageDoubleMap< - _, - Blake2_128Concat, - T::MarketId, - Twox64Concat, - T::AccountId, - BalanceOf, - ValueQuery, - >; - - /// Owned balances (not shares) that are going to be distributed as incentives. Automatically - /// *updated* after each finalized block. - #[pallet::storage] - pub type OwnedValues = StorageDoubleMap< - _, - Blake2_128Concat, - T::MarketId, - Twox64Concat, - T::AccountId, - OwnedValuesParams, BlockNumberFor>, - ValueQuery, - >; - - /// Per block distribution. How much each block will distribute across bought shares. - #[pallet::storage] - pub type PerBlockIncentive = StorageValue<_, BalanceOf, ValueQuery>; -} diff --git a/zrml/liquidity-mining/src/liquidity_mining_pallet_api.rs b/zrml/liquidity-mining/src/liquidity_mining_pallet_api.rs deleted file mode 100644 index a0bff48a9..000000000 --- a/zrml/liquidity-mining/src/liquidity_mining_pallet_api.rs +++ /dev/null @@ -1,44 +0,0 @@ -// 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 frame_support::dispatch::DispatchResult; - -/// Interface to interact with the Zeitgeist Liquidity Mining pallet. -pub trait LiquidityMiningPalletApi { - type AccountId; - type Balance; - type BlockNumber; - type MarketId; - - /// Increases the number of stored pool shares of an account for a given market. - /// - /// It is up to the caller to synchronize the amount of shares between different pallets - fn add_shares(account_id: Self::AccountId, market_id: Self::MarketId, shares: Self::Balance); - - /// Removes a given `market_id` period from the storage distributing incentives to all - /// related accounts. - fn distribute_market_incentives(market_id: &Self::MarketId) -> DispatchResult; - - /// Decreases the number of stored pool shares of an account on a given market. - /// - /// It is up to the caller to synchronize the amount of shares between different pallets - fn remove_shares( - account_id: &Self::AccountId, - market_id: &Self::MarketId, - shares: Self::Balance, - ); -} diff --git a/zrml/liquidity-mining/src/mock.rs b/zrml/liquidity-mining/src/mock.rs deleted file mode 100644 index 6cd49ddc1..000000000 --- a/zrml/liquidity-mining/src/mock.rs +++ /dev/null @@ -1,149 +0,0 @@ -// 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 . - -#![cfg(test)] - -use crate as zrml_liquidity_mining; -use frame_support::{construct_runtime, traits::Everything}; -use frame_system::mocking::MockBlock; -use sp_runtime::{ - traits::{BlakeTwo256, IdentityLookup}, - BuildStorage, -}; -use zeitgeist_primitives::{ - constants::mock::{ - BlockHashCount, ExistentialDeposit, LiquidityMiningPalletId, MaxLocks, MaxReserves, - MinimumPeriod, BASE, - }, - types::{AccountIdTest, Balance, Hash, MarketId, Moment}, -}; - -pub const ALICE: AccountIdTest = 0; -pub const BOB: AccountIdTest = 1; - -construct_runtime!( - pub enum Runtime { - Balances: pallet_balances, - LiquidityMining: zrml_liquidity_mining, - MarketCommons: zrml_market_commons, - System: frame_system, - Timestamp: pallet_timestamp, - } -); - -impl crate::Config for Runtime { - type Currency = Balances; - type RuntimeEvent = RuntimeEvent; - type MarketCommons = MarketCommons; - type MarketId = MarketId; - type PalletId = LiquidityMiningPalletId; - type WeightInfo = crate::weights::WeightInfo; -} - -impl frame_system::Config for Runtime { - type AccountData = pallet_balances::AccountData; - type AccountId = AccountIdTest; - type BaseCallFilter = Everything; - type Block = MockBlock; - type BlockHashCount = BlockHashCount; - type BlockLength = (); - type BlockWeights = (); - type RuntimeCall = RuntimeCall; - type DbWeight = (); - type RuntimeEvent = RuntimeEvent; - type Hash = Hash; - type Hashing = BlakeTwo256; - type Lookup = IdentityLookup; - type Nonce = u64; - type MaxConsumers = frame_support::traits::ConstU32<16>; - type OnKilledAccount = (); - type OnNewAccount = (); - type RuntimeOrigin = RuntimeOrigin; - type PalletInfo = PalletInfo; - type SS58Prefix = (); - type SystemWeightInfo = (); - type Version = (); - type OnSetCode = (); -} - -impl pallet_balances::Config for Runtime { - type AccountStore = System; - type Balance = Balance; - type DustRemoval = (); - type FreezeIdentifier = (); - type RuntimeHoldReason = (); - type RuntimeEvent = RuntimeEvent; - type ExistentialDeposit = ExistentialDeposit; - type MaxHolds = (); - type MaxFreezes = (); - type MaxLocks = MaxLocks; - type MaxReserves = MaxReserves; - type ReserveIdentifier = [u8; 8]; - type WeightInfo = (); -} - -impl zrml_market_commons::Config for Runtime { - type Balance = Balance; - type MarketId = MarketId; - type Timestamp = Timestamp; -} - -impl pallet_timestamp::Config for Runtime { - type MinimumPeriod = MinimumPeriod; - type Moment = Moment; - type OnTimestampSet = (); - type WeightInfo = (); -} - -pub struct ExtBuilder { - pub(crate) balances: Vec<(AccountIdTest, Balance)>, - pub(crate) initial_balance: Balance, - pub(crate) per_block_incentives: Balance, -} - -impl Default for ExtBuilder { - fn default() -> Self { - Self { - balances: vec![(ALICE, 1_000 * BASE)], - initial_balance: 100 * BASE, - per_block_incentives: BASE, - } - } -} - -impl ExtBuilder { - pub fn build(self) -> sp_io::TestExternalities { - let mut t = frame_system::GenesisConfig::::default().build_storage().unwrap(); - - // see the logs in tests when using `RUST_LOG=debug cargo test -- --nocapture` - let _ = env_logger::builder().is_test(true).try_init(); - - crate::GenesisConfig:: { - initial_balance: self.initial_balance, - per_block_distribution: self.per_block_incentives, - } - .assimilate_storage(&mut t) - .unwrap(); - - pallet_balances::GenesisConfig:: { balances: self.balances } - .assimilate_storage(&mut t) - .unwrap(); - - t.into() - } -} diff --git a/zrml/liquidity-mining/src/owned_values_params.rs b/zrml/liquidity-mining/src/owned_values_params.rs deleted file mode 100644 index 3150d2a66..000000000 --- a/zrml/liquidity-mining/src/owned_values_params.rs +++ /dev/null @@ -1,54 +0,0 @@ -// 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 parity_scale_codec::{Decode, Encode, MaxEncodedLen}; - -/// Parameters used by the `OwnedValues` storage. -/// -/// # Types -/// -/// * `BA`: BAlance -/// * `BN`: Block Number -#[cfg_attr(feature = "std", derive(serde::Deserialize, serde::Serialize))] -#[derive( - scale_info::TypeInfo, - Clone, - Debug, - Decode, - Default, - Encode, - Eq, - MaxEncodedLen, - Ord, - PartialEq, - PartialOrd, -)] -pub struct OwnedValuesParams -where - BA: MaxEncodedLen, - BN: MaxEncodedLen, -{ - /// The number of blocks an account participated in a market period. - pub participated_blocks: BN, - /// Owned amount of perpetual incentives. Won't go away when accounts exist early and is not - /// attached to any share - pub perpetual_incentives: BA, - /// Owned incentives. Related to the total number of shares. - pub total_incentives: BA, - /// Owned quantity of shares. Related to the total amount of incentives. - pub total_shares: BA, -} diff --git a/zrml/liquidity-mining/src/tests.rs b/zrml/liquidity-mining/src/tests.rs deleted file mode 100644 index 55b2aa4f3..000000000 --- a/zrml/liquidity-mining/src/tests.rs +++ /dev/null @@ -1,234 +0,0 @@ -// 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 . - -#![cfg(test)] - -use crate::{ - mock::{Balances, ExtBuilder, LiquidityMining, Runtime, RuntimeOrigin, System, ALICE, BOB}, - track_incentives_based_on_bought_shares::TrackIncentivesBasedOnBoughtShares, - track_incentives_based_on_sold_shares::TrackIncentivesBasedOnSoldShares, - BlockBoughtShares, BlockSoldShares, LiquidityMiningPalletApi as _, OwnedValues, -}; -use core::ops::Range; -use frame_support::{ - assert_err, assert_ok, - traits::{Currency, OnFinalize}, -}; -use frame_system::RawOrigin; -use sp_runtime::DispatchError; -use zeitgeist_primitives::types::{ - Asset, Deadlines, Market, MarketBonds, MarketCreation, MarketDisputeMechanism, MarketPeriod, - MarketStatus, MarketType, ScoringRule, -}; -use zrml_market_commons::Markets; - -#[test] -fn blocks_shares_are_updated_after_each_block() { - ExtBuilder::default().build().execute_with(|| { - LiquidityMining::add_shares(ALICE, 0, 1); - LiquidityMining::add_shares(ALICE, 1, 1); - LiquidityMining::remove_shares(&ALICE, &0, 1); - assert_eq!(>::iter().count(), 2); - assert_eq!(>::iter().count(), 1); - LiquidityMining::on_finalize(1); - assert_eq!(>::iter().count(), 0); - assert_eq!(>::iter().count(), 0); - }); -} - -#[test] -fn distribute_market_incentives_removes_market_and_distributes_all_incentives_to_lps() { - ExtBuilder::default().build().execute_with(|| { - let initial_alice_balance = Balances::free_balance(ALICE); - create_default_market(0, 1..5); - create_default_market(1, 1..5); - - LiquidityMining::add_shares(ALICE, 0, 10); - LiquidityMining::add_shares(ALICE, 1, 10); - LiquidityMining::on_finalize(2); - - assert_eq!(>::iter().count(), 2); - assert_ok!(LiquidityMining::distribute_market_incentives(&0)); - assert_ok!(LiquidityMining::distribute_market_incentives(&1)); - assert_eq!(>::iter().count(), 0); - - // In this case, each market have the same amount of incentives - let market_incentives = ExtBuilder::default().per_block_incentives / 2; - // Perpetual balance for the entire campaign - let entire_market_perpetual_balance = market_incentives / 1000; - // Account only stayed 1 block out of 4 (25%), i.e, lost 75% of the perpetual balance - let actual_market_perpetual_balance = entire_market_perpetual_balance / 4; - // Ordinary balance - let actual_market_incentives = market_incentives - entire_market_perpetual_balance; - - let new_balance = initial_alice_balance - + 2 * actual_market_incentives - + 2 * actual_market_perpetual_balance; - - assert_eq!(Balances::free_balance(ALICE), new_balance); - }); -} - -#[test] -fn genesis_has_lm_account_and_initial_per_block_distribution() { - ExtBuilder::default().build().execute_with(|| { - let pallet_account_id = crate::Pallet::::pallet_account_id(); - assert_eq!( - Balances::total_balance(&pallet_account_id), - ExtBuilder::default().initial_balance - ); - assert_eq!( - >::get(), - ExtBuilder::default().per_block_incentives - ); - }); -} - -#[test] -fn owned_balances_are_updated_after_bought_shares() { - ExtBuilder::default().build().execute_with(|| { - System::set_block_number(5); - create_default_market(0, 1..11); - create_default_market(1, 1..21); - - LiquidityMining::add_shares(ALICE, 0, 100); - LiquidityMining::add_shares(ALICE, 1, 200); - LiquidityMining::add_shares(BOB, 1, 300); - TrackIncentivesBasedOnBoughtShares::::exec(5); - - let vec = >::iter().collect::>(); - - assert_eq!(vec[2].2.total_shares, 100); - assert_eq!(vec[1].2.total_shares, 200); - assert_eq!(vec[0].2.total_shares, 300); - - // Market 0 has 42.8% of `per_block_incentives` - let market_incentives_0 = 4280000000; - // Market 1 has 57.1% of `per_block_incentives` - let market_incentives_1 = 5710000000; - - // 0.1% of `market_incentives_0`. - let perpetual_incentives_alice_0 = market_incentives_0 / 1000; - - // One share value for Market 1 - let market_1_one_share_value = one_share_value(market_incentives_1, 500); - // Raw incentives for ALICE on Market 1 - let raw_incentives_alice_1 = market_1_one_share_value * 200; - // Raw incentives for BOB on Market 1 - let raw_incentives_bob_1 = market_1_one_share_value * 300; - // 0.1% of `raw_incentives_ALICE_1` - let perpetual_incentives_alice_1 = raw_incentives_alice_1 / 1000; - // 0.1% of `raw_incentives_BOB_1` - let perpetual_incentives_bob_1 = raw_incentives_bob_1 / 1000; - - assert_eq!(vec[2].2.perpetual_incentives, perpetual_incentives_alice_0); - assert_eq!(vec[1].2.perpetual_incentives, perpetual_incentives_alice_1); - assert_eq!(vec[0].2.perpetual_incentives, perpetual_incentives_bob_1); - - assert_eq!(vec[2].2.total_incentives, market_incentives_0 - perpetual_incentives_alice_0); - assert_eq!( - vec[1].2.total_incentives, - raw_incentives_alice_1 - perpetual_incentives_alice_1 - ); - assert_eq!(vec[0].2.total_incentives, raw_incentives_bob_1 - perpetual_incentives_bob_1); - }); -} - -#[test] -fn owned_balances_are_updated_after_sold_shares() { - ExtBuilder::default().build().execute_with(|| { - System::set_block_number(5); - create_default_market(0, 1..11); - create_default_market(1, 1..21); - - LiquidityMining::add_shares(ALICE, 0, 100); - LiquidityMining::add_shares(ALICE, 1, 200); - LiquidityMining::on_finalize(5); - - LiquidityMining::remove_shares(&ALICE, &0, 100); - LiquidityMining::remove_shares(&ALICE, &1, 50); - TrackIncentivesBasedOnSoldShares::::exec(); - - let vec = >::iter().collect::>(); - - // Market 0 has 42.8% of `per_block_incentives` - let market_incentives_0 = 4280000000; - // Market 1 has 57.1% of `per_block_incentives` - let market_incentives_1 = 5710000000; - - // 0.1% of `market_incentives_0`. - let perpetual_incentives_alice_0 = market_incentives_0 / 1000; - // 0.1% of `market_incentives_1`. - let perpetual_incentives_alice_1 = market_incentives_1 / 1000; - // Alice sold 25% of her owned shares - let incentives_alice_1 = (market_incentives_1 - perpetual_incentives_alice_1) / 4 * 3; - - assert_eq!(vec[1].2.total_shares, 0); - assert_eq!(vec[1].2.total_incentives, 0); - assert_eq!(vec[1].2.perpetual_incentives, perpetual_incentives_alice_0); - - assert_eq!(vec[0].2.total_shares, 150); - assert_eq!(vec[0].2.total_incentives, incentives_alice_1); - assert_eq!(vec[0].2.perpetual_incentives, perpetual_incentives_alice_1); - }); -} - -#[test] -fn only_sudo_can_change_per_block_distribution() { - ExtBuilder::default().build().execute_with(|| { - assert_ok!(LiquidityMining::set_per_block_distribution(RawOrigin::Root.into(), 100)); - assert_err!( - LiquidityMining::set_per_block_distribution(RuntimeOrigin::signed(ALICE), 100), - DispatchError::BadOrigin - ); - }); -} - -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(), - creator: 0, - market_type: MarketType::Categorical(0), - dispute_mechanism: Some(MarketDisputeMechanism::SimpleDisputes), - metadata: vec![], - oracle: 0, - period: MarketPeriod::Block(period), - deadlines: Deadlines { - grace_period: 1_u64, - oracle_duration: 1_u64, - dispute_duration: 1_u64, - }, - report: None, - resolved_outcome: None, - status: MarketStatus::Closed, - scoring_rule: ScoringRule::AmmCdaHybrid, - bonds: MarketBonds::default(), - early_close: None, - }, - ); -} - -// One bought share value of a particular market in a particular block -fn one_share_value(market_incentives: u128, total_shares: u128) -> u128 { - market_incentives / total_shares -} diff --git a/zrml/liquidity-mining/src/track_incentives_based_on_bought_shares.rs b/zrml/liquidity-mining/src/track_incentives_based_on_bought_shares.rs deleted file mode 100644 index 208189b20..000000000 --- a/zrml/liquidity-mining/src/track_incentives_based_on_bought_shares.rs +++ /dev/null @@ -1,162 +0,0 @@ -// 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::{ - utils::{calculate_perthousand, calculate_perthousand_value}, - BalanceOf, BlockBoughtShares, MomentOf, OwnedValues, PerBlockIncentive, -}; -use alloc::{collections::BTreeSet, vec::Vec}; -use core::marker::PhantomData; -use frame_system::pallet_prelude::BlockNumberFor; -use sp_runtime::traits::{CheckedDiv, Saturating}; -use zeitgeist_primitives::types::MarketPeriod; -use zrml_market_commons::MarketCommonsPalletApi; - -// Per-thousand that every incentive should transfer to the perpetual balance. -// Currently is 0.1% -const PERPETUAL_PTD: u8 = 1; - -pub struct TrackIncentivesBasedOnBoughtShares(PhantomData); - -// No-one can have more balance than `Balance::MAX` so most functions saturate rewards. -impl TrackIncentivesBasedOnBoughtShares -where - T: crate::Config, -{ - pub(crate) fn exec(curr_block: BlockNumberFor) -> Option { - let per_block_incentives = >::get(); - let market_incentives = Self::markets_incentives(per_block_incentives, curr_block)?; - let market_incentives_len = market_incentives.len(); - let ppb = BalanceOf::::from(PERPETUAL_PTD); - - for (market_id, incentive) in market_incentives { - let mut total_bought_shares = BalanceOf::::from(0u8); - for (_, shares) in >::iter_prefix(market_id) { - total_bought_shares = total_bought_shares.saturating_add(shares); - } - let opt = Self::bought_share_value(&incentive, &total_bought_shares); - let share_value = if let Some(el) = opt { - el - } else { - continue; - }; - for (account_id, bought_shares) in >::iter_prefix(market_id) { - let raw_incentives = share_value.saturating_mul(bought_shares); - let perpetual_incentives = calculate_perthousand_value(ppb, raw_incentives); - let incentives = raw_incentives.saturating_sub(perpetual_incentives); - >::mutate(market_id, account_id, |values| { - let one = BlockNumberFor::::from(1u8); - - values.perpetual_incentives = - values.perpetual_incentives.saturating_add(perpetual_incentives); - values.total_incentives = values.total_incentives.saturating_add(incentives); - values.total_shares = values.total_shares.saturating_add(bought_shares); - values.participated_blocks = values.participated_blocks.saturating_add(one); - }); - } - } - - let _ = >::clear(u32::MAX, None); - Some(market_incentives_len) - } - - // ZTG value of one bought share for the current block being produced. Or in other words: - // Determines how much a share will be worth given the amount of ZTG for liquidity - // mining and the total number of bought shares for the current block. - // - // `None` result means no-one purchased a share. - #[inline] - fn bought_share_value( - incentive: &BalanceOf, - total_bought_shares: &BalanceOf, - ) -> Option> { - incentive.checked_div(total_bought_shares) - } - - // How much incentive each market will receive - #[allow( - // `normalized_total` variable needs to be evaluated first - clippy::needless_collect - )] - fn markets_incentives( - per_block_incentives: BalanceOf, - curr_block: BlockNumberFor, - ) -> Option)>> { - let mut normalized_total = BalanceOf::::from(0u8); - let markets_periods: BTreeSet<_> = >::iter().map(|el| el.0).collect(); - let now = T::MarketCommons::now(); - - let normalized_values: Vec<_> = markets_periods - .into_iter() - .filter_map(|market_id| { - let period = T::MarketCommons::market(&market_id).ok()?.period; - let normalized_value = Self::normalize_market(curr_block, now, &period); - normalized_total = normalized_total.saturating_add(normalized_value); - Some((market_id, normalized_value)) - }) - .collect(); - normalized_values - .into_iter() - .map(|(market_id, normalized_value)| { - let ptd = calculate_perthousand(normalized_value, &normalized_total)?.into(); - let incentive = calculate_perthousand_value(ptd, per_block_incentives); - Some((market_id, incentive)) - }) - .collect() - } - - // Takes any market parameter and outputs a number that will be used as a percentage - // to calculate how much incentives each individual market will receive. - // - // In this case, the output is the percentage of the remaining number of blocks to stimulate - // early liquidity providers. For example, a market starts at 0 and ends at 10. If the current - // block is at 7, then the output is 30. - // - // Another example: A market starts at 500, ends at 700 and the current block is 510. In other - // words, the normalized output will be near 100 (or 100%) because the current block is near - // the start of the market. - // - // ```rust - // let market_period = 500..=700 - // - // let market_total_blocks = 700 - 500 = 200; - // let market_remaining_blocks = 200 - 10 = 190 - // - // let _market_normalized_value = 190 * 100 / 200 = 95 - // ``` - // - // The greater the output, the more incentives the market will receive - fn normalize_market( - curr_block: BlockNumberFor, - now: MomentOf, - period: &MarketPeriod, MomentOf>, - ) -> BalanceOf { - let opt = match period { - MarketPeriod::Block(range) => { - let total_value = range.end.saturating_sub(range.start); - calculate_perthousand(range.end.saturating_sub(curr_block), &total_value) - } - MarketPeriod::Timestamp(range) => { - let total_value = range.end.saturating_sub(range.start); - let value = now.saturating_sub(range.start); - calculate_perthousand(value, &total_value) - } - }; - opt.map(|ptd| ptd.into()).unwrap_or_default() - } -} diff --git a/zrml/liquidity-mining/src/track_incentives_based_on_sold_shares.rs b/zrml/liquidity-mining/src/track_incentives_based_on_sold_shares.rs deleted file mode 100644 index d6cf15628..000000000 --- a/zrml/liquidity-mining/src/track_incentives_based_on_sold_shares.rs +++ /dev/null @@ -1,64 +0,0 @@ -// 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::{ - utils::{calculate_perthousand, calculate_perthousand_value}, - BlockSoldShares, OwnedValues, -}; -use core::marker::PhantomData; -use sp_runtime::traits::Saturating; - -pub struct TrackIncentivesBasedOnSoldShares(PhantomData); - -// No-one can have more balance than `Balance::MAX` so most functions saturate rewards. -impl TrackIncentivesBasedOnSoldShares -where - T: crate::Config, -{ - pub(crate) fn exec() -> usize { - let mut counter = 0; - for (market_id, account_id, sold_shares) in >::iter() { - let values = if let Ok(e) = >::try_get(market_id, account_id.clone()) { - e - } else { - // Trying to retrieve an account that doesn't have any bought shares - continue; - }; - - let ptd = if let Some(el) = calculate_perthousand(sold_shares, &values.total_shares) { - el.into() - } else { - // `total_shares` is zero - continue; - }; - - let balance_to_subtract = calculate_perthousand_value(ptd, values.total_incentives); - - >::mutate(market_id, account_id, |values| { - // `total_balance` or `total_shares` can be less than 0 but this possible - // scenario is ignored here. - values.total_incentives = - values.total_incentives.saturating_sub(balance_to_subtract); - values.total_shares = values.total_shares.saturating_sub(sold_shares); - }); - counter = counter.saturating_add(1); - } - let _ = >::clear(u32::MAX, None); - counter - } -} diff --git a/zrml/liquidity-mining/src/utils.rs b/zrml/liquidity-mining/src/utils.rs deleted file mode 100644 index eb5523c52..000000000 --- a/zrml/liquidity-mining/src/utils.rs +++ /dev/null @@ -1,68 +0,0 @@ -// Copyright 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::MomentOf; -use core::ops::{Div, Range}; -use frame_system::pallet_prelude::BlockNumberFor; -use sp_runtime::{ - traits::{CheckedDiv, Saturating, UniqueSaturatedInto}, - SaturatedConversion, -}; -use zeitgeist_primitives::constants::MILLISECS_PER_BLOCK; - -// Calculates the **average** number of blocks occurred between the starting and ending time period -// of a market. -// -// To convert the block number type to the moment type, is is necessary to first convert the -// block number value to `u32`, which caps the maximum output to `u32::MAX`. Since this function -// is only used to evaluate perpetual balances, such limitation shouldn't be a problem. -pub fn calculate_average_blocks_of_a_time_period(range: &Range>) -> BlockNumberFor -where - T: crate::Config, -{ - let total_value_time = range.end.saturating_sub(range.start); - let mpb_balance = MILLISECS_PER_BLOCK.into(); - // The following won't overflow because `MILLISECS_PER_BLOCK` is not zero. - let total_value_blocks = total_value_time / mpb_balance; - let total_value_blocks_u32: u32 = total_value_blocks.saturated_into(); - total_value_blocks_u32.into() -} - -// Per-thousand compared to `total_value` and a given `value`. For example, if total is 200, -// then 6 is 3% of 200. -// -// Results currently can't have more than 00.0% accuracy. -pub fn calculate_perthousand(value: T, total_value: &T) -> Option -where - T: CheckedDiv + From + Saturating + UniqueSaturatedInto, -{ - let _1000_balance = T::from(1000u16); - let opaque = value.saturating_mul(_1000_balance).checked_div(total_value)?; - Some(opaque.unique_saturated_into()) -} - -// The value compared to `total_value` and a given `perthousand`. For example, 3% of 200 is 6. -// -// Results currently can't have more than 00.0% accuracy. -pub fn calculate_perthousand_value(perthousand: T, total_value: T) -> T -where - T: Div + Saturating + From, -{ - let _1000_balance = T::from(1000u16); - total_value.saturating_mul(perthousand) / _1000_balance -} diff --git a/zrml/liquidity-mining/src/weights.rs b/zrml/liquidity-mining/src/weights.rs deleted file mode 100644 index d643d61dc..000000000 --- a/zrml/liquidity-mining/src/weights.rs +++ /dev/null @@ -1,67 +0,0 @@ -// 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 . - -//! Autogenerated weights for zrml_liquidity_mining -//! -//! THIS FILE WAS AUTO-GENERATED USING THE SUBSTRATE BENCHMARK CLI VERSION 4.0.0-dev -//! DATE: `2024-07-24`, STEPS: `50`, REPEAT: `20`, LOW RANGE: `[]`, HIGH RANGE: `[]` -//! WORST CASE MAP SIZE: `1000000` -//! HOSTNAME: `zeitgeist-benchmark`, CPU: `AMD EPYC 7601 32-Core Processor` -//! EXECUTION: ``, WASM-EXECUTION: `Compiled`, CHAIN: `Some("dev")`, DB CACHE: `1024` - -// Executed Command: -// ./target/production/zeitgeist -// benchmark -// pallet -// --chain=dev -// --steps=50 -// --repeat=20 -// --pallet=zrml_liquidity_mining -// --extrinsic=* -// --execution=wasm -// --wasm-execution=compiled -// --heap-pages=4096 -// --template=./misc/weight_template.hbs -// --header=./HEADER_GPL3 -// --output=./zrml/liquidity-mining/src/weights.rs - -#![allow(unused_parens)] -#![allow(unused_imports)] - -use core::marker::PhantomData; -use frame_support::{traits::Get, weights::Weight}; - -/// Trait containing the required functions for weight retrival within -/// zrml_liquidity_mining (automatically generated) -pub trait WeightInfoZeitgeist { - fn set_per_block_distribution() -> Weight; -} - -/// Weight functions for zrml_liquidity_mining (automatically generated) -pub struct WeightInfo(PhantomData); -impl WeightInfoZeitgeist for WeightInfo { - /// Storage: `LiquidityMining::PerBlockIncentive` (r:0 w:1) - /// Proof: `LiquidityMining::PerBlockIncentive` (`max_values`: Some(1), `max_size`: Some(16), added: 511, mode: `MaxEncodedLen`) - fn set_per_block_distribution() -> Weight { - // Proof Size summary in bytes: - // Measured: `0` - // Estimated: `0` - // Minimum execution time: 5_180 nanoseconds. - Weight::from_parts(6_820_000, 0).saturating_add(T::DbWeight::get().writes(1)) - } -} diff --git a/zrml/market-commons/src/migrations.rs b/zrml/market-commons/src/migrations.rs index 7bcc6e2e5..adf04862f 100644 --- a/zrml/market-commons/src/migrations.rs +++ b/zrml/market-commons/src/migrations.rs @@ -15,3 +15,390 @@ // // You should have received a copy of the GNU General Public License // along with Zeitgeist. If not, see . + +use crate::{AccountIdOf, BalanceOf, Config, MarketIdOf, MomentOf, Pallet as MarketCommons}; +use alloc::vec::Vec; +use core::marker::PhantomData; +use frame_support::{ + pallet_prelude::Weight, + traits::{Get, OnRuntimeUpgrade, StorageVersion}, +}; +use frame_system::pallet_prelude::BlockNumberFor; +use parity_scale_codec::{Decode, Encode, MaxEncodedLen}; +use scale_info::TypeInfo; +use sp_runtime::{Perbill, RuntimeDebug, Saturating}; +use zeitgeist_primitives::types::{ + Asset, Deadlines, EarlyClose, Market, MarketBonds, MarketCreation, MarketDisputeMechanism, + MarketPeriod, MarketStatus, MarketType, OutcomeReport, Report, ScoringRule, +}; + +#[cfg(feature = "try-runtime")] +use { + alloc::{collections::BTreeMap, format}, + frame_support::migration::storage_key_iter, + sp_runtime::DispatchError, +}; + +#[cfg(any(feature = "try-runtime", feature = "test"))] +use frame_support::Blake2_128Concat; + +#[cfg(any(feature = "try-runtime", test))] +const MARKET_COMMONS: &[u8] = b"MarketCommons"; +#[cfg(any(feature = "try-runtime", test))] +const MARKETS: &[u8] = b"Markets"; + +#[derive(Clone, Decode, Encode, Eq, PartialEq, RuntimeDebug, TypeInfo)] +pub struct OldMarket { + pub market_id: MarketId, + /// Base asset of the market. + pub base_asset: Asset, + /// Creator of this market. + pub creator: AccountId, + /// Creation type. + pub creation: MarketCreation, + /// A fee that is charged each trade and given to the market creator. + pub creator_fee: Perbill, + /// Oracle that reports the outcome of this market. + pub oracle: AccountId, + /// Metadata for the market, usually a content address of IPFS + /// hosted JSON. Currently limited to 66 bytes (see `MaxEncodedLen` implementation) + pub metadata: Vec, + /// The type of the market. + pub market_type: MarketType, + /// Market start and end + pub period: MarketPeriod, + /// Market deadlines. + pub deadlines: Deadlines, + /// The scoring rule used for the market. + pub scoring_rule: ScoringRule, + /// The current status of the market. + pub status: MarketStatus, + /// The report of the market. Only `Some` if it has been reported. + pub report: Option>, + /// The resolved outcome. + pub resolved_outcome: Option, + /// See [`MarketDisputeMechanism`]. + pub dispute_mechanism: Option, + /// The bonds reserved for this market. + pub bonds: MarketBonds, + /// The time at which the market was closed early. + pub early_close: Option>, +} + +type OldMarketOf = + OldMarket, BalanceOf, BlockNumberFor, MomentOf, MarketIdOf>; + +#[derive(TypeInfo, Clone, Copy, Encode, Eq, Decode, MaxEncodedLen, PartialEq, RuntimeDebug)] +pub enum OldMarketDisputeMechanism { + Authorized, + Court, + SimpleDisputes, +} + +const MARKET_COMMONS_REQUIRED_STORAGE_VERSION: u16 = 11; +const MARKET_COMMONS_NEXT_STORAGE_VERSION: u16 = 12; + +#[cfg(feature = "try-runtime")] +#[frame_support::storage_alias] +pub(crate) type Markets = + StorageMap, Blake2_128Concat, MarketIdOf, OldMarketOf>; + +pub struct MigrateDisputeMechanism(PhantomData); + +/// Removes the `SimpleDisputes` MDM by switching markets that use `SimpleDisputes` to `Authorized`. +/// Note that this migration does not unreserve any funds bonded with zrml-simple-dispute's reserve +/// ID. +impl OnRuntimeUpgrade for MigrateDisputeMechanism +where + T: Config, +{ + fn on_runtime_upgrade() -> Weight { + let mut total_weight = T::DbWeight::get().reads(1); + let market_commons_version = StorageVersion::get::>(); + if market_commons_version != MARKET_COMMONS_REQUIRED_STORAGE_VERSION { + log::info!( + "MigrateDisputeMechanism: market-commons version is {:?}, but {:?} is required", + market_commons_version, + MARKET_COMMONS_REQUIRED_STORAGE_VERSION, + ); + return total_weight; + } + log::info!("MigrateDisputeMechanism: Starting..."); + + let mut translated = 0u64; + crate::Markets::::translate::, _>(|_, old_market| { + translated.saturating_inc(); + let dispute_mechanism = match old_market.dispute_mechanism { + Some(OldMarketDisputeMechanism::Court) => Some(MarketDisputeMechanism::Court), + Some(_) => Some(MarketDisputeMechanism::Authorized), + None => None, + }; + let new_market = Market { + market_id: old_market.market_id, + base_asset: old_market.base_asset, + creator: old_market.creator, + creation: old_market.creation, + creator_fee: old_market.creator_fee, + oracle: old_market.oracle, + metadata: old_market.metadata, + market_type: old_market.market_type, + period: old_market.period, + deadlines: old_market.deadlines, + scoring_rule: old_market.scoring_rule, + status: old_market.status, + report: old_market.report, + resolved_outcome: old_market.resolved_outcome, + dispute_mechanism, + bonds: old_market.bonds, + early_close: old_market.early_close, + }; + Some(new_market) + }); + log::info!("MigrateDisputeMechanism: Upgraded {} markets.", translated); + total_weight = + total_weight.saturating_add(T::DbWeight::get().reads_writes(translated, translated)); + + StorageVersion::new(MARKET_COMMONS_NEXT_STORAGE_VERSION).put::>(); + total_weight = total_weight.saturating_add(T::DbWeight::get().writes(1)); + log::info!("MigrateDisputeMechanism: Done!"); + total_weight + } + + #[cfg(feature = "try-runtime")] + fn pre_upgrade() -> Result, DispatchError> { + let old_markets = storage_key_iter::, OldMarketOf, Blake2_128Concat>( + MARKET_COMMONS, + MARKETS, + ) + .collect::>(); + let markets = Markets::::iter_keys().count(); + let decodable_markets = Markets::::iter_values().count(); + if markets == decodable_markets { + log::info!("All {} markets could successfully be decoded.", markets); + } else { + log::error!( + "Can only decode {} of {} markets - others will be dropped.", + decodable_markets, + markets + ); + } + + Ok(old_markets.encode()) + } + + #[cfg(feature = "try-runtime")] + fn post_upgrade(previous_state: Vec) -> Result<(), DispatchError> { + let old_markets: BTreeMap, OldMarketOf> = + Decode::decode(&mut &previous_state[..]).unwrap(); + let old_market_count = old_markets.len(); + let new_market_count = crate::Markets::::iter().count(); + assert_eq!(old_market_count, new_market_count); + for (market_id, new_market) in crate::Markets::::iter() { + let old_market = old_markets + .get(&market_id) + .expect(&format!("Market {:?} not found", market_id)[..]); + assert_eq!(old_market.market_id, new_market.market_id); + assert_eq!(old_market.base_asset, new_market.base_asset); + assert_eq!(old_market.creator, new_market.creator); + assert_eq!(old_market.creation, new_market.creation); + assert_eq!(old_market.creator_fee, new_market.creator_fee); + assert_eq!(old_market.oracle, new_market.oracle); + assert_eq!(old_market.metadata, new_market.metadata); + assert_eq!(old_market.market_type, new_market.market_type); + assert_eq!(old_market.period, new_market.period); + assert_eq!(old_market.deadlines, new_market.deadlines); + assert_eq!(old_market.status, new_market.status); + assert_eq!(old_market.report, new_market.report); + assert_eq!(old_market.resolved_outcome, new_market.resolved_outcome); + assert_eq!(old_market.bonds, new_market.bonds); + assert_eq!(old_market.early_close, new_market.early_close); + } + log::info!("MigrateDisputeMechanism: Post-upgrade market count is {}!", new_market_count); + Ok(()) + } +} + +#[cfg(test)] +mod tests { + use super::*; + use crate::{ + mock::{ExtBuilder, Runtime}, + MarketOf, + }; + use alloc::fmt::Debug; + use frame_support::{migration::put_storage_value, Blake2_128Concat, StorageHasher}; + use parity_scale_codec::Encode; + use sp_io::storage::root as storage_root; + use sp_runtime::{Perbill, StateVersion}; + use test_case::test_case; + use zeitgeist_primitives::types::{Bond, EarlyCloseState, MarketId}; + + #[test] + fn on_runtime_upgrade_increments_the_storage_version() { + ExtBuilder::default().build().execute_with(|| { + set_up_version(); + MigrateDisputeMechanism::::on_runtime_upgrade(); + assert_eq!( + StorageVersion::get::>(), + MARKET_COMMONS_NEXT_STORAGE_VERSION + ); + }); + } + + #[test_case(None, None; "none")] + #[test_case( + Some(OldMarketDisputeMechanism::Authorized), + Some(MarketDisputeMechanism::Authorized) + )] + #[test_case(Some(OldMarketDisputeMechanism::Court), Some(MarketDisputeMechanism::Court))] + #[test_case( + Some(OldMarketDisputeMechanism::SimpleDisputes), + Some(MarketDisputeMechanism::Authorized) + )] + fn on_runtime_upgrade_works_as_expected( + old_scoring_rule: Option, + new_scoring_rule: Option, + ) { + ExtBuilder::default().build().execute_with(|| { + set_up_version(); + let (old_market, new_market) = + construct_old_new_tuple(old_scoring_rule, new_scoring_rule); + populate_test_data::>( + MARKET_COMMONS, + MARKETS, + vec![old_market], + ); + MigrateDisputeMechanism::::on_runtime_upgrade(); + assert_eq!(crate::Markets::::get(0).unwrap(), new_market); + }); + } + + #[test] + fn on_runtime_upgrade_is_noop_if_versions_are_not_correct() { + ExtBuilder::default().build().execute_with(|| { + StorageVersion::new(MARKET_COMMONS_NEXT_STORAGE_VERSION) + .put::>(); + let market = Market { + market_id: 7, + base_asset: Asset::Ztg, + creator: 1, + creation: MarketCreation::Permissionless, + creator_fee: Perbill::from_rational(2u32, 3u32), + oracle: 4, + metadata: vec![0x05; 50], + market_type: MarketType::Categorical(999), + period: MarketPeriod::, MomentOf>::Block(6..7), + deadlines: Deadlines { grace_period: 7, oracle_duration: 8, dispute_duration: 9 }, + scoring_rule: ScoringRule::AmmCdaHybrid, + status: MarketStatus::Active, + report: Some(Report { at: 13, by: 14, outcome: OutcomeReport::Categorical(10) }), + resolved_outcome: None, + dispute_mechanism: Some(MarketDisputeMechanism::Court), + bonds: MarketBonds { + creation: Some(Bond::new(11, 12)), + oracle: None, + outsider: None, + dispute: None, + close_dispute: None, + close_request: None, + }, + early_close: None, + }; + crate::Markets::::insert(333, market); + let tmp = storage_root(StateVersion::V1); + MigrateDisputeMechanism::::on_runtime_upgrade(); + assert_eq!(tmp, storage_root(StateVersion::V1)); + }); + } + + fn set_up_version() { + StorageVersion::new(MARKET_COMMONS_REQUIRED_STORAGE_VERSION) + .put::>(); + } + + fn construct_old_new_tuple( + old_dispute_mechanism: Option, + new_dispute_mechanism: Option, + ) -> (OldMarketOf, MarketOf) { + let market_id = 0; + let base_asset = Asset::Ztg; + let creator = 1; + let creation = MarketCreation::Advised; + let creator_fee = Perbill::from_rational(2u32, 3u32); + let oracle = 4; + let metadata = vec![5; 50]; + let market_type = MarketType::Scalar(6..=7); + let period = MarketPeriod::Block(8..9); + let deadlines = Deadlines { grace_period: 10, oracle_duration: 11, dispute_duration: 12 }; + let scoring_rule = ScoringRule::AmmCdaHybrid; + let status = MarketStatus::Resolved; + let report = Some(Report { at: 13, by: 14, outcome: OutcomeReport::Categorical(15) }); + let resolved_outcome = Some(OutcomeReport::Categorical(16)); + let bonds = MarketBonds { + creation: Some(Bond { who: 17, value: 18, is_settled: true }), + oracle: Some(Bond { who: 19, value: 20, is_settled: true }), + outsider: Some(Bond { who: 21, value: 22, is_settled: true }), + dispute: Some(Bond { who: 23, value: 24, is_settled: true }), + close_request: Some(Bond { who: 25, value: 26, is_settled: true }), + close_dispute: Some(Bond { who: 27, value: 28, is_settled: true }), + }; + let early_close = Some(EarlyClose { + old: MarketPeriod::Block(29..30), + new: MarketPeriod::Block(31..32), + state: EarlyCloseState::Disputed, + }); + let old_markets = OldMarket { + market_id, + base_asset, + creator, + creation: creation.clone(), + creator_fee, + oracle, + metadata: metadata.clone(), + market_type: market_type.clone(), + period: period.clone(), + deadlines, + scoring_rule, + status, + report: report.clone(), + resolved_outcome: resolved_outcome.clone(), + dispute_mechanism: old_dispute_mechanism, + bonds: bonds.clone(), + early_close: early_close.clone(), + }; + let new_market = Market { + market_id, + base_asset, + creator, + creation: creation.clone(), + creator_fee, + oracle, + metadata: metadata.clone(), + market_type: market_type.clone(), + period: period.clone(), + deadlines, + scoring_rule, + status, + report: report.clone(), + resolved_outcome: resolved_outcome.clone(), + dispute_mechanism: new_dispute_mechanism, + bonds: bonds.clone(), + early_close: early_close.clone(), + }; + (old_markets, new_market) + } + + #[allow(unused)] + fn populate_test_data(pallet: &[u8], prefix: &[u8], data: Vec) + where + H: StorageHasher, + K: TryFrom + Encode, + V: Encode + Clone, + >::Error: Debug, + { + for (key, value) in data.iter().enumerate() { + let storage_hash = K::try_from(key).unwrap().using_encoded(H::hash).as_ref().to_vec(); + put_storage_value::(pallet, prefix, &storage_hash, (*value).clone()); + } + } +} diff --git a/zrml/neo-swaps/Cargo.toml b/zrml/neo-swaps/Cargo.toml index b4c3eca3b..e2b194afb 100644 --- a/zrml/neo-swaps/Cargo.toml +++ b/zrml/neo-swaps/Cargo.toml @@ -33,10 +33,8 @@ xcm-builder = { workspace = true, optional = true } zrml-authorized = { workspace = true, optional = true } zrml-court = { workspace = true, optional = true } zrml-global-disputes = { workspace = true, optional = true } -zrml-liquidity-mining = { workspace = true, optional = true } zrml-prediction-markets = { workspace = true, optional = true } zrml-prediction-markets-runtime-api = { workspace = true, optional = true } -zrml-simple-disputes = { workspace = true, optional = true } [dev-dependencies] more-asserts = { workspace = true } @@ -66,8 +64,6 @@ mock = [ "sp-io/default", "zrml-court/std", "zrml-authorized/std", - "zrml-liquidity-mining/std", - "zrml-simple-disputes/std", "zrml-global-disputes/std", "zrml-prediction-markets/std", "zrml-prediction-markets/mock", diff --git a/zrml/neo-swaps/src/mock.rs b/zrml/neo-swaps/src/mock.rs index f066a31cd..05d17fdcf 100644 --- a/zrml/neo-swaps/src/mock.rs +++ b/zrml/neo-swaps/src/mock.rs @@ -47,15 +47,15 @@ use zeitgeist_primitives::{ CloseEarlyProtectionBlockPeriod, CloseEarlyProtectionTimeFramePeriod, CloseEarlyRequestBond, CloseEarlyTimeFramePeriod, CorrectionPeriod, CourtPalletId, ExistentialDeposit, ExistentialDeposits, GdVotingPeriod, GetNativeCurrencyId, - GlobalDisputeLockId, GlobalDisputesPalletId, InflationPeriod, LiquidityMiningPalletId, - LockId, MaxAppeals, MaxApprovals, MaxCourtParticipants, MaxCreatorFee, MaxDelegations, - MaxDisputeDuration, MaxDisputes, MaxEditReasonLen, MaxGlobalDisputeVotes, - MaxGracePeriod, MaxLiquidityTreeDepth, MaxLocks, MaxMarketLifetime, MaxOracleDuration, - MaxOwners, MaxRejectReasonLen, MaxReserves, MaxSelectedDraws, MaxYearlyInflation, - MinCategories, MinDisputeDuration, MinJurorStake, MinOracleDuration, - MinOutcomeVoteAmount, MinimumPeriod, NeoMaxSwapFee, NeoSwapsPalletId, OutcomeBond, - OutcomeFactor, OutsiderBond, PmPalletId, RemoveKeysLimit, RequestInterval, - SimpleDisputesPalletId, TreasuryPalletId, VotePeriod, VotingOutcomeFee, BASE, CENT, + GlobalDisputeLockId, GlobalDisputesPalletId, InflationPeriod, LockId, MaxAppeals, + MaxApprovals, MaxCourtParticipants, MaxCreatorFee, MaxDelegations, MaxDisputeDuration, + MaxDisputes, MaxEditReasonLen, MaxGlobalDisputeVotes, MaxGracePeriod, + MaxLiquidityTreeDepth, MaxLocks, MaxMarketLifetime, MaxOracleDuration, MaxOwners, + MaxRejectReasonLen, MaxReserves, MaxSelectedDraws, MaxYearlyInflation, MinCategories, + MinDisputeDuration, MinJurorStake, MinOracleDuration, MinOutcomeVoteAmount, + MinimumPeriod, NeoMaxSwapFee, NeoSwapsPalletId, OutsiderBond, PmPalletId, + RemoveKeysLimit, RequestInterval, TreasuryPalletId, VotePeriod, VotingOutcomeFee, BASE, + CENT, }, }, math::fixed::FixedMul, @@ -169,11 +169,9 @@ construct_runtime!( Authorized: zrml_authorized, Balances: pallet_balances, Court: zrml_court, - LiquidityMining: zrml_liquidity_mining, MarketCommons: zrml_market_commons, PredictionMarkets: zrml_prediction_markets, RandomnessCollectiveFlip: pallet_insecure_randomness_collective_flip, - SimpleDisputes: zrml_simple_disputes, GlobalDisputes: zrml_global_disputes, System: frame_system, Timestamp: pallet_timestamp, @@ -217,7 +215,6 @@ impl zrml_prediction_markets::Config for Runtime { type DisputeBond = DisputeBond; type RuntimeEvent = RuntimeEvent; type GlobalDisputes = GlobalDisputes; - type LiquidityMining = LiquidityMining; type MaxCategories = MaxCategories; type MaxDisputes = MaxDisputes; type MinDisputeDuration = MinDisputeDuration; @@ -237,7 +234,6 @@ impl zrml_prediction_markets::Config for Runtime { type RequestEditOrigin = EnsureSignedBy; type ResolveOrigin = EnsureSignedBy; type AssetManager = AssetManager; - type SimpleDisputes = SimpleDisputes; type Slash = Treasury; type ValidityBond = ValidityBond; type WeightInfo = zrml_prediction_markets::weights::WeightInfo; @@ -282,15 +278,6 @@ impl zrml_court::Config for Runtime { type WeightInfo = zrml_court::weights::WeightInfo; } -impl zrml_liquidity_mining::Config for Runtime { - type Currency = Balances; - type RuntimeEvent = RuntimeEvent; - type MarketCommons = MarketCommons; - type MarketId = MarketId; - type PalletId = LiquidityMiningPalletId; - type WeightInfo = zrml_liquidity_mining::weights::WeightInfo; -} - impl frame_system::Config for Runtime { type AccountData = pallet_balances::AccountData; type AccountId = AccountIdTest; @@ -367,18 +354,6 @@ impl pallet_timestamp::Config for Runtime { type WeightInfo = (); } -impl zrml_simple_disputes::Config for Runtime { - type Currency = Balances; - type RuntimeEvent = RuntimeEvent; - type OutcomeBond = OutcomeBond; - type OutcomeFactor = OutcomeFactor; - type DisputeResolution = zrml_prediction_markets::Pallet; - type MarketCommons = MarketCommons; - type MaxDisputes = MaxDisputes; - type PalletId = SimpleDisputesPalletId; - type WeightInfo = zrml_simple_disputes::weights::WeightInfo; -} - impl zrml_global_disputes::Config for Runtime { type AddOutcomePeriod = AddOutcomePeriod; type RuntimeEvent = RuntimeEvent; diff --git a/zrml/prediction-markets/Cargo.toml b/zrml/prediction-markets/Cargo.toml index bbbf67b87..12e810c8b 100644 --- a/zrml/prediction-markets/Cargo.toml +++ b/zrml/prediction-markets/Cargo.toml @@ -13,9 +13,7 @@ zeitgeist-primitives = { workspace = true } zrml-authorized = { workspace = true } zrml-court = { workspace = true } zrml-global-disputes = { workspace = true } -zrml-liquidity-mining = { workspace = true } zrml-market-commons = { workspace = true } -zrml-simple-disputes = { workspace = true } # Mock @@ -82,9 +80,7 @@ std = [ "zeitgeist-primitives/std", "zrml-authorized/std", "zrml-court/std", - "zrml-liquidity-mining/std", "zrml-market-commons/std", - "zrml-simple-disputes/std", "zrml-global-disputes/std", "xcm?/std", ] diff --git a/zrml/prediction-markets/fuzz/Cargo.toml b/zrml/prediction-markets/fuzz/Cargo.toml index 703a2f229..444dab50d 100644 --- a/zrml/prediction-markets/fuzz/Cargo.toml +++ b/zrml/prediction-markets/fuzz/Cargo.toml @@ -11,7 +11,6 @@ libfuzzer-sys = { workspace = true } sp-arithmetic = { workspace = true, features = ["default"] } zeitgeist-primitives = { workspace = true, features = ["arbitrary", "mock", "default"] } zrml-prediction-markets = { workspace = true, features = ["mock", "default"] } -zrml-simple-disputes = { workspace = true, features = ["default"] } [package] authors = ["Automatically generated"] diff --git a/zrml/prediction-markets/fuzz/pm_full_workflow.rs b/zrml/prediction-markets/fuzz/pm_full_workflow.rs index 506b9e5cd..7addf0b9c 100644 --- a/zrml/prediction-markets/fuzz/pm_full_workflow.rs +++ b/zrml/prediction-markets/fuzz/pm_full_workflow.rs @@ -134,7 +134,6 @@ fn market_dispute_mechanism(seed: u8) -> Option { match seed % 4 { 0 => Some(MarketDisputeMechanism::Authorized), 1 => Some(MarketDisputeMechanism::Court), - 2 => Some(MarketDisputeMechanism::SimpleDisputes), _ => None, } } diff --git a/zrml/prediction-markets/src/benchmarks.rs b/zrml/prediction-markets/src/benchmarks.rs index fa1782063..228b86195 100644 --- a/zrml/prediction-markets/src/benchmarks.rs +++ b/zrml/prediction-markets/src/benchmarks.rs @@ -229,7 +229,7 @@ fn create_spot_prices(asset_count: u16) -> Vec> { benchmarks! { where_clause { where - T: pallet_timestamp::Config + zrml_authorized::Config + zrml_simple_disputes::Config + zrml_court::Config, + T: pallet_timestamp::Config + zrml_authorized::Config + zrml_court::Config, <::MarketCommons as MarketCommonsPalletApi>::MarketId: From<::MarketId>, } diff --git a/zrml/prediction-markets/src/lib.rs b/zrml/prediction-markets/src/lib.rs index 3d6896889..6a07968e3 100644 --- a/zrml/prediction-markets/src/lib.rs +++ b/zrml/prediction-markets/src/lib.rs @@ -77,7 +77,6 @@ mod pallet { }, }; use zrml_global_disputes::{types::InitialItem, GlobalDisputesPalletApi}; - use zrml_liquidity_mining::LiquidityMiningPalletApi; use zrml_market_commons::{types::MarketBuilder, MarketCommonsPalletApi}; /// The current storage version. @@ -531,11 +530,7 @@ mod pallet { /// Complexity: `O(n)`, where `n` is the number of outstanding disputes. #[pallet::call_index(6)] #[pallet::weight( - T::WeightInfo::dispute_authorized().saturating_add( - T::Court::on_dispute_max_weight().saturating_add( - T::SimpleDisputes::on_dispute_max_weight() - ) - ) + T::WeightInfo::dispute_authorized().saturating_add(T::Court::on_dispute_max_weight()) )] #[transactional] pub fn dispute( @@ -560,12 +555,6 @@ mod pallet { .saturating_sub(T::Authorized::on_dispute_max_weight()) .saturating_add(court_weight) } - MarketDisputeMechanism::SimpleDisputes => { - let sd_weight = T::SimpleDisputes::on_dispute(&market_id, &market)?.weight; - T::WeightInfo::dispute_authorized() - .saturating_sub(T::Authorized::on_dispute_max_weight()) - .saturating_add(sd_weight) - } }; let dispute_bond = T::DisputeBond::get(); @@ -983,9 +972,6 @@ mod pallet { T::Authorized::has_failed(&market_id, &market)? } MarketDisputeMechanism::Court => T::Court::has_failed(&market_id, &market)?, - MarketDisputeMechanism::SimpleDisputes => { - T::SimpleDisputes::has_failed(&market_id, &market)? - } }; let has_failed = res_0.result; ensure!(has_failed, Error::::MarketDisputeMechanismNotFailed); @@ -995,9 +981,6 @@ mod pallet { T::Authorized::on_global_dispute(&market_id, &market)? } MarketDisputeMechanism::Court => T::Court::on_global_dispute(&market_id, &market)?, - MarketDisputeMechanism::SimpleDisputes => { - T::SimpleDisputes::on_global_dispute(&market_id, &market)? - } }; let mut initial_items: Vec> = Vec::new(); @@ -1603,13 +1586,6 @@ mod pallet { BlockNumberFor, >; - type LiquidityMining: LiquidityMiningPalletApi< - AccountId = Self::AccountId, - Balance = BalanceOf, - BlockNumber = BlockNumberFor, - MarketId = MarketIdOf, - >; - /// The maximum number of categories available for categorical markets. #[pallet::constant] type MaxCategories: Get; @@ -1694,17 +1670,6 @@ mod pallet { /// The origin that is allowed to resolve markets. type ResolveOrigin: EnsureOrigin; - /// See [`DisputeApi`]. - type SimpleDisputes: zrml_simple_disputes::SimpleDisputesPalletApi< - AccountId = Self::AccountId, - Balance = BalanceOf, - NegativeImbalance = NegativeImbalanceOf, - BlockNumber = BlockNumberFor, - MarketId = MarketIdOf, - Moment = MomentOf, - Origin = Self::RuntimeOrigin, - >; - /// Handler for slashed funds. type Slash: OnUnbalanced>; @@ -2235,9 +2200,6 @@ mod pallet { MarketDisputeMechanism::Court => { T::Court::get_auto_resolve(market_id, &market) } - MarketDisputeMechanism::SimpleDisputes => { - T::SimpleDisputes::get_auto_resolve(market_id, &market) - } }; if let Some(auto_resolve_block) = auto_resolve_block_opt { let ids_len = remove_auto_resolve::(market_id, auto_resolve_block); @@ -2597,17 +2559,6 @@ mod pallet { weight = weight.saturating_add(res.weight); remainder } - MarketDisputeMechanism::SimpleDisputes => { - let res = T::SimpleDisputes::exchange( - market_id, - market, - &resolved_outcome, - imbalance_left, - )?; - let remainder = res.result; - weight = weight.saturating_add(res.weight); - remainder - } }; T::Slash::on_unbalanced(remainder); @@ -2646,11 +2597,6 @@ mod pallet { weight = weight.saturating_add(res.weight); res.result } - MarketDisputeMechanism::SimpleDisputes => { - let res = T::SimpleDisputes::on_resolution(market_id, market)?; - weight = weight.saturating_add(res.weight); - res.result - } }; } @@ -2746,9 +2692,6 @@ mod pallet { } _ => return Err(Error::::InvalidMarketStatus.into()), }; - // TODO: https://github.com/zeitgeistpm/zeitgeist/issues/815 - // Following call should return weight consumed by it. - T::LiquidityMining::distribute_market_incentives(market_id)?; // NOTE: Currently we don't clean up outcome assets. // TODO(#792): Remove outcome assets for accounts! Delete "resolved" assets of `orml_tokens` with storage migration. diff --git a/zrml/prediction-markets/src/mock.rs b/zrml/prediction-markets/src/mock.rs index 8201292c4..056c52f04 100644 --- a/zrml/prediction-markets/src/mock.rs +++ b/zrml/prediction-markets/src/mock.rs @@ -41,15 +41,14 @@ use zeitgeist_primitives::{ CloseEarlyProtectionBlockPeriod, CloseEarlyProtectionTimeFramePeriod, CloseEarlyRequestBond, CloseEarlyTimeFramePeriod, CorrectionPeriod, CourtPalletId, ExistentialDeposit, ExistentialDeposits, GdVotingPeriod, GetNativeCurrencyId, - GlobalDisputeLockId, GlobalDisputesPalletId, InflationPeriod, LiquidityMiningPalletId, - LockId, MaxAppeals, MaxApprovals, MaxCategories, MaxCourtParticipants, MaxCreatorFee, - MaxDelegations, MaxDisputeDuration, MaxDisputes, MaxEditReasonLen, MaxGlobalDisputeVotes, - MaxGracePeriod, MaxLocks, MaxMarketLifetime, MaxOracleDuration, MaxOwners, - MaxRejectReasonLen, MaxReserves, MaxSelectedDraws, MaxYearlyInflation, MinCategories, - MinDisputeDuration, MinJurorStake, MinOracleDuration, MinOutcomeVoteAmount, MinimumPeriod, - OutcomeBond, OutcomeFactor, OutsiderBond, PmPalletId, RemoveKeysLimit, RequestInterval, - SimpleDisputesPalletId, TreasuryPalletId, VotePeriod, VotingOutcomeFee, BASE, CENT, - MILLISECS_PER_BLOCK, + GlobalDisputeLockId, GlobalDisputesPalletId, InflationPeriod, LockId, MaxAppeals, + MaxApprovals, MaxCategories, MaxCourtParticipants, MaxCreatorFee, MaxDelegations, + MaxDisputeDuration, MaxDisputes, MaxEditReasonLen, MaxGlobalDisputeVotes, MaxGracePeriod, + MaxLocks, MaxMarketLifetime, MaxOracleDuration, MaxOwners, MaxRejectReasonLen, MaxReserves, + MaxSelectedDraws, MaxYearlyInflation, MinCategories, MinDisputeDuration, MinJurorStake, + MinOracleDuration, MinOutcomeVoteAmount, MinimumPeriod, OutsiderBond, PmPalletId, + RemoveKeysLimit, RequestInterval, TreasuryPalletId, VotePeriod, VotingOutcomeFee, BASE, + CENT, MILLISECS_PER_BLOCK, }, traits::DeployPoolApi, types::{ @@ -174,11 +173,9 @@ construct_runtime!( Balances: pallet_balances, Court: zrml_court, AssetManager: orml_currencies, - LiquidityMining: zrml_liquidity_mining, MarketCommons: zrml_market_commons, PredictionMarkets: prediction_markets, RandomnessCollectiveFlip: pallet_insecure_randomness_collective_flip, - SimpleDisputes: zrml_simple_disputes, GlobalDisputes: zrml_global_disputes, System: frame_system, Timestamp: pallet_timestamp, @@ -207,7 +204,6 @@ impl crate::Config for Runtime { type DisputeBond = DisputeBond; type RuntimeEvent = RuntimeEvent; type GlobalDisputes = GlobalDisputes; - type LiquidityMining = LiquidityMining; type MaxCategories = MaxCategories; type MaxDisputes = MaxDisputes; type MinDisputeDuration = MinDisputeDuration; @@ -228,7 +224,6 @@ impl crate::Config for Runtime { type RequestEditOrigin = EnsureSignedBy; type ResolveOrigin = EnsureSignedBy; type AssetManager = AssetManager; - type SimpleDisputes = SimpleDisputes; type Slash = Treasury; type ValidityBond = ValidityBond; type WeightInfo = prediction_markets::weights::WeightInfo; @@ -386,33 +381,12 @@ impl zrml_court::Config for Runtime { type WeightInfo = zrml_court::weights::WeightInfo; } -impl zrml_liquidity_mining::Config for Runtime { - type Currency = Balances; - type RuntimeEvent = RuntimeEvent; - type MarketCommons = MarketCommons; - type MarketId = MarketId; - type PalletId = LiquidityMiningPalletId; - type WeightInfo = zrml_liquidity_mining::weights::WeightInfo; -} - impl zrml_market_commons::Config for Runtime { type Balance = Balance; type MarketId = MarketId; type Timestamp = Timestamp; } -impl zrml_simple_disputes::Config for Runtime { - type Currency = Balances; - type RuntimeEvent = RuntimeEvent; - type OutcomeBond = OutcomeBond; - type OutcomeFactor = OutcomeFactor; - type DisputeResolution = prediction_markets::Pallet; - type MarketCommons = MarketCommons; - type MaxDisputes = MaxDisputes; - type PalletId = SimpleDisputesPalletId; - type WeightInfo = zrml_simple_disputes::weights::WeightInfo; -} - impl zrml_global_disputes::Config for Runtime { type AddOutcomePeriod = AddOutcomePeriod; type RuntimeEvent = RuntimeEvent; diff --git a/zrml/prediction-markets/src/tests/admin_move_market_to_closed.rs b/zrml/prediction-markets/src/tests/admin_move_market_to_closed.rs index 5417f1e6a..7d7d3af0f 100644 --- a/zrml/prediction-markets/src/tests/admin_move_market_to_closed.rs +++ b/zrml/prediction-markets/src/tests/admin_move_market_to_closed.rs @@ -67,7 +67,7 @@ fn admin_move_market_to_closed_successfully_closes_market_and_sets_end_timestamp gen_metadata(2), MarketCreation::Permissionless, MarketType::Categorical(::MinCategories::get()), - Some(MarketDisputeMechanism::SimpleDisputes), + Some(MarketDisputeMechanism::Court), ScoringRule::AmmCdaHybrid )); let market_id = 0; @@ -148,7 +148,7 @@ fn admin_move_market_to_closed_correctly_clears_auto_close_blocks() { gen_metadata(50), MarketCreation::Permissionless, MarketType::Categorical(category_count), - Some(MarketDisputeMechanism::SimpleDisputes), + Some(MarketDisputeMechanism::Court), ScoringRule::AmmCdaHybrid, )); assert_ok!(PredictionMarkets::create_market( @@ -161,7 +161,7 @@ fn admin_move_market_to_closed_correctly_clears_auto_close_blocks() { gen_metadata(50), MarketCreation::Permissionless, MarketType::Categorical(category_count), - Some(MarketDisputeMechanism::SimpleDisputes), + Some(MarketDisputeMechanism::Court), ScoringRule::AmmCdaHybrid, )); assert_ok!(PredictionMarkets::admin_move_market_to_closed( diff --git a/zrml/prediction-markets/src/tests/approve_market.rs b/zrml/prediction-markets/src/tests/approve_market.rs index 6405e59db..aa322a826 100644 --- a/zrml/prediction-markets/src/tests/approve_market.rs +++ b/zrml/prediction-markets/src/tests/approve_market.rs @@ -125,7 +125,7 @@ fn approve_market_correctly_unreserves_advisory_bond() { gen_metadata(2), MarketCreation::Advised, MarketType::Categorical(2), - Some(MarketDisputeMechanism::SimpleDisputes), + Some(MarketDisputeMechanism::Court), ScoringRule::AmmCdaHybrid, )); let market_id = 0; diff --git a/zrml/prediction-markets/src/tests/create_market.rs b/zrml/prediction-markets/src/tests/create_market.rs index 2dd3c407c..e3235f692 100644 --- a/zrml/prediction-markets/src/tests/create_market.rs +++ b/zrml/prediction-markets/src/tests/create_market.rs @@ -52,7 +52,7 @@ fn fails_if_user_cannot_afford_bonds_advised( gen_metadata(2), market_creation, MarketType::Scalar(0..=1), - Some(MarketDisputeMechanism::SimpleDisputes), + Some(MarketDisputeMechanism::Court), ScoringRule::AmmCdaHybrid, ), pallet_balances::Error::::InsufficientBalance @@ -75,7 +75,7 @@ fn fails_on_fee_too_high() { gen_metadata(2), MarketCreation::Permissionless, MarketType::Scalar(0..=1), - Some(MarketDisputeMechanism::SimpleDisputes), + Some(MarketDisputeMechanism::Court), ScoringRule::AmmCdaHybrid, ), Error::::FeeTooHigh @@ -101,7 +101,7 @@ fn fails_on_invalid_multihash() { multihash, MarketCreation::Permissionless, MarketType::Scalar(0..=1), - Some(MarketDisputeMechanism::SimpleDisputes), + Some(MarketDisputeMechanism::Court), ScoringRule::AmmCdaHybrid, ), Error::::InvalidMultihash @@ -124,7 +124,7 @@ fn create_scalar_market_fails_on_invalid_range(range: RangeInclusive) { gen_metadata(2), MarketCreation::Permissionless, MarketType::Scalar(range), - Some(MarketDisputeMechanism::SimpleDisputes), + Some(MarketDisputeMechanism::Court), ScoringRule::AmmCdaHybrid, ), Error::::InvalidOutcomeRange @@ -151,7 +151,7 @@ fn create_market_fails_on_min_dispute_period() { gen_metadata(2), MarketCreation::Permissionless, MarketType::Categorical(2), - Some(MarketDisputeMechanism::SimpleDisputes), + Some(MarketDisputeMechanism::Court), ScoringRule::AmmCdaHybrid, ), Error::::DisputeDurationSmallerThanMinDisputeDuration @@ -178,7 +178,7 @@ fn create_market_fails_on_min_oracle_duration() { gen_metadata(2), MarketCreation::Permissionless, MarketType::Categorical(2), - Some(MarketDisputeMechanism::SimpleDisputes), + Some(MarketDisputeMechanism::Court), ScoringRule::AmmCdaHybrid, ), Error::::OracleDurationSmallerThanMinOracleDuration @@ -205,7 +205,7 @@ fn create_market_fails_on_max_dispute_period() { gen_metadata(2), MarketCreation::Permissionless, MarketType::Categorical(2), - Some(MarketDisputeMechanism::SimpleDisputes), + Some(MarketDisputeMechanism::Court), ScoringRule::AmmCdaHybrid, ), Error::::DisputeDurationGreaterThanMaxDisputeDuration @@ -232,7 +232,7 @@ fn create_market_fails_on_max_grace_period() { gen_metadata(2), MarketCreation::Permissionless, MarketType::Categorical(2), - Some(MarketDisputeMechanism::SimpleDisputes), + Some(MarketDisputeMechanism::Court), ScoringRule::AmmCdaHybrid, ), Error::::GracePeriodGreaterThanMaxGracePeriod @@ -259,7 +259,7 @@ fn create_market_fails_on_max_oracle_duration() { gen_metadata(2), MarketCreation::Permissionless, MarketType::Categorical(2), - Some(MarketDisputeMechanism::SimpleDisputes), + Some(MarketDisputeMechanism::Court), ScoringRule::AmmCdaHybrid, ), Error::::OracleDurationGreaterThanMaxOracleDuration @@ -291,7 +291,7 @@ fn create_market_with_foreign_assets() { gen_metadata(2), MarketCreation::Permissionless, MarketType::Categorical(2), - Some(MarketDisputeMechanism::SimpleDisputes), + Some(MarketDisputeMechanism::Court), ScoringRule::AmmCdaHybrid, ), Error::::InvalidBaseAsset, @@ -308,7 +308,7 @@ fn create_market_with_foreign_assets() { gen_metadata(2), MarketCreation::Permissionless, MarketType::Categorical(2), - Some(MarketDisputeMechanism::SimpleDisputes), + Some(MarketDisputeMechanism::Court), ScoringRule::AmmCdaHybrid, ), Error::::UnregisteredForeignAsset, @@ -324,7 +324,7 @@ fn create_market_with_foreign_assets() { gen_metadata(2), MarketCreation::Permissionless, MarketType::Categorical(2), - Some(MarketDisputeMechanism::SimpleDisputes), + Some(MarketDisputeMechanism::Court), ScoringRule::AmmCdaHybrid, )); let market = MarketCommons::market(&0).unwrap(); @@ -346,7 +346,7 @@ fn it_does_not_create_market_with_too_few_categories() { gen_metadata(2), MarketCreation::Advised, MarketType::Categorical(::MinCategories::get() - 1), - Some(MarketDisputeMechanism::SimpleDisputes), + Some(MarketDisputeMechanism::Court), ScoringRule::AmmCdaHybrid ), Error::::NotEnoughCategories @@ -368,7 +368,7 @@ fn it_does_not_create_market_with_too_many_categories() { gen_metadata(2), MarketCreation::Advised, MarketType::Categorical(::MaxCategories::get() + 1), - Some(MarketDisputeMechanism::SimpleDisputes), + Some(MarketDisputeMechanism::Court), ScoringRule::AmmCdaHybrid ), Error::::TooManyCategories diff --git a/zrml/prediction-markets/src/tests/edit_market.rs b/zrml/prediction-markets/src/tests/edit_market.rs index 553289ab7..524e08249 100644 --- a/zrml/prediction-markets/src/tests/edit_market.rs +++ b/zrml/prediction-markets/src/tests/edit_market.rs @@ -61,7 +61,7 @@ fn only_creator_can_edit_market() { get_deadlines(), gen_metadata(2), MarketType::Categorical(::MinCategories::get()), - Some(MarketDisputeMechanism::SimpleDisputes), + Some(MarketDisputeMechanism::Court), ScoringRule::AmmCdaHybrid ), Error::::EditorNotCreator @@ -104,7 +104,7 @@ fn edit_cycle_for_proposed_markets() { get_deadlines(), gen_metadata(2), MarketType::Categorical(::MinCategories::get()), - Some(MarketDisputeMechanism::SimpleDisputes), + Some(MarketDisputeMechanism::Court), ScoringRule::AmmCdaHybrid )); let edited_market = MarketCommons::market(&0).expect("Market not found"); @@ -155,7 +155,7 @@ fn edit_market_with_foreign_asset() { get_deadlines(), gen_metadata(2), MarketType::Categorical(::MinCategories::get()), - Some(MarketDisputeMechanism::SimpleDisputes), + Some(MarketDisputeMechanism::Court), ScoringRule::AmmCdaHybrid ), Error::::UnregisteredForeignAsset @@ -171,7 +171,7 @@ fn edit_market_with_foreign_asset() { get_deadlines(), gen_metadata(2), MarketType::Categorical(::MinCategories::get()), - Some(MarketDisputeMechanism::SimpleDisputes), + Some(MarketDisputeMechanism::Court), ScoringRule::AmmCdaHybrid ), Error::::InvalidBaseAsset, @@ -186,7 +186,7 @@ fn edit_market_with_foreign_asset() { get_deadlines(), gen_metadata(2), MarketType::Categorical(::MinCategories::get()), - Some(MarketDisputeMechanism::SimpleDisputes), + Some(MarketDisputeMechanism::Court), ScoringRule::AmmCdaHybrid )); let market = MarketCommons::market(&0).unwrap(); diff --git a/zrml/prediction-markets/src/tests/integration.rs b/zrml/prediction-markets/src/tests/integration.rs index 48e1a0db3..ea68a6119 100644 --- a/zrml/prediction-markets/src/tests/integration.rs +++ b/zrml/prediction-markets/src/tests/integration.rs @@ -150,7 +150,7 @@ fn the_entire_market_lifecycle_works_with_timestamps() { gen_metadata(2), MarketCreation::Permissionless, MarketType::Categorical(2), - Some(MarketDisputeMechanism::SimpleDisputes), + Some(MarketDisputeMechanism::Court), ScoringRule::AmmCdaHybrid )); @@ -190,7 +190,7 @@ fn full_scalar_market_lifecycle() { gen_metadata(3), MarketCreation::Permissionless, MarketType::Scalar(10..=30), - Some(MarketDisputeMechanism::SimpleDisputes), + Some(MarketDisputeMechanism::Authorized), ScoringRule::AmmCdaHybrid )); @@ -232,20 +232,15 @@ fn full_scalar_market_lifecycle() { // dispute assert_ok!(PredictionMarkets::dispute(RuntimeOrigin::signed(DAVE), 0)); - assert_ok!(SimpleDisputes::suggest_outcome( - RuntimeOrigin::signed(DAVE), + assert_ok!(Authorized::authorize_market_outcome( + RuntimeOrigin::signed(AuthorizedDisputeResolutionUser::get()), 0, OutcomeReport::Scalar(25) )); - let disputes = zrml_simple_disputes::Disputes::::get(0); - assert_eq!(disputes.len(), 1); - - run_blocks(market.deadlines.dispute_duration); + run_blocks(::CorrectionPeriod::get()); let market_after_resolve = MarketCommons::market(&0).unwrap(); assert_eq!(market_after_resolve.status, MarketStatus::Resolved); - let disputes = zrml_simple_disputes::Disputes::::get(0); - assert_eq!(disputes.len(), 0); // give EVE some shares assert_ok!(Tokens::transfer( @@ -436,8 +431,6 @@ fn authorized_correctly_resolves_disputed_market() { let market_after = MarketCommons::market(&0).unwrap(); assert_eq!(market_after.status, MarketStatus::Resolved); - let disputes = zrml_simple_disputes::Disputes::::get(0); - assert_eq!(disputes.len(), 0); assert_ok!(PredictionMarkets::redeem_shares(RuntimeOrigin::signed(CHARLIE), 0)); @@ -727,7 +720,7 @@ fn outsider_reports_wrong_outcome() { gen_metadata(2), MarketCreation::Permissionless, MarketType::Categorical(2), - Some(MarketDisputeMechanism::SimpleDisputes), + Some(MarketDisputeMechanism::Authorized), ScoringRule::AmmCdaHybrid, )); @@ -751,21 +744,17 @@ fn outsider_reports_wrong_outcome() { assert_ok!(PredictionMarkets::dispute(RuntimeOrigin::signed(EVE), 0,)); check_reserve(&EVE, ::DisputeBond::get()); - assert_ok!(SimpleDisputes::suggest_outcome( - RuntimeOrigin::signed(DAVE), + assert_ok!(Authorized::authorize_market_outcome( + RuntimeOrigin::signed(AuthorizedDisputeResolutionUser::get()), 0, OutcomeReport::Categorical(0) )); - let outcome_bond = zrml_simple_disputes::default_outcome_bond::(0); - - check_reserve(&DAVE, outcome_bond); - let eve_balance_before = Balances::free_balance(EVE); let dave_balance_before = Balances::free_balance(DAVE); // on_resolution called - run_blocks(market.deadlines.dispute_duration); + run_blocks(::CorrectionPeriod::get()); assert_eq!( Balances::free_balance(ALICE), @@ -784,7 +773,7 @@ fn outsider_reports_wrong_outcome() { + ::OracleBond::get() ); // DAVE gets his outcome bond back - assert_eq!(Balances::free_balance(DAVE), dave_balance_before + outcome_bond); + assert_eq!(Balances::free_balance(DAVE), dave_balance_before); }; ExtBuilder::default().build().execute_with(|| { test(Asset::Ztg); diff --git a/zrml/prediction-markets/src/tests/manually_close_market.rs b/zrml/prediction-markets/src/tests/manually_close_market.rs index b88cab214..6ea25296a 100644 --- a/zrml/prediction-markets/src/tests/manually_close_market.rs +++ b/zrml/prediction-markets/src/tests/manually_close_market.rs @@ -44,7 +44,7 @@ fn manually_close_market_after_long_stall() { gen_metadata(50), MarketCreation::Permissionless, MarketType::Categorical(category_count), - Some(MarketDisputeMechanism::SimpleDisputes), + Some(MarketDisputeMechanism::Court), ScoringRule::AmmCdaHybrid, )); assert_ok!(PredictionMarkets::create_market( @@ -57,7 +57,7 @@ fn manually_close_market_after_long_stall() { gen_metadata(50), MarketCreation::Permissionless, MarketType::Categorical(category_count), - Some(MarketDisputeMechanism::SimpleDisputes), + Some(MarketDisputeMechanism::Court), ScoringRule::AmmCdaHybrid, )); @@ -113,7 +113,7 @@ fn manually_close_market_fails_if_market_not_in_close_time_frame_list() { gen_metadata(50), MarketCreation::Permissionless, MarketType::Categorical(category_count), - Some(MarketDisputeMechanism::SimpleDisputes), + Some(MarketDisputeMechanism::Court), ScoringRule::AmmCdaHybrid, )); @@ -154,7 +154,7 @@ fn manually_close_market_fails_if_not_allowed_for_block_based_markets() { gen_metadata(50), MarketCreation::Permissionless, MarketType::Categorical(category_count), - Some(MarketDisputeMechanism::SimpleDisputes), + Some(MarketDisputeMechanism::Court), ScoringRule::AmmCdaHybrid, )); diff --git a/zrml/prediction-markets/src/tests/mod.rs b/zrml/prediction-markets/src/tests/mod.rs index 5954a1561..80a6962ee 100644 --- a/zrml/prediction-markets/src/tests/mod.rs +++ b/zrml/prediction-markets/src/tests/mod.rs @@ -115,7 +115,7 @@ fn simple_create_scalar_market( gen_metadata(2), creation, MarketType::Scalar(100..=200), - Some(MarketDisputeMechanism::SimpleDisputes), + Some(MarketDisputeMechanism::Court), scoring_rule )); } diff --git a/zrml/prediction-markets/src/tests/on_initialize.rs b/zrml/prediction-markets/src/tests/on_initialize.rs index b78388fbf..33d4d656f 100644 --- a/zrml/prediction-markets/src/tests/on_initialize.rs +++ b/zrml/prediction-markets/src/tests/on_initialize.rs @@ -39,7 +39,7 @@ fn on_initialize_skips_the_genesis_block() { gen_metadata(50), MarketCreation::Permissionless, MarketType::Categorical(category_count), - Some(MarketDisputeMechanism::SimpleDisputes), + Some(MarketDisputeMechanism::Court), ScoringRule::AmmCdaHybrid, )); diff --git a/zrml/prediction-markets/src/tests/on_market_close.rs b/zrml/prediction-markets/src/tests/on_market_close.rs index 63054872c..6a6cb73ae 100644 --- a/zrml/prediction-markets/src/tests/on_market_close.rs +++ b/zrml/prediction-markets/src/tests/on_market_close.rs @@ -142,7 +142,7 @@ fn on_market_close_successfully_auto_closes_market_with_blocks() { gen_metadata(50), MarketCreation::Permissionless, MarketType::Categorical(category_count), - Some(MarketDisputeMechanism::SimpleDisputes), + Some(MarketDisputeMechanism::Court), ScoringRule::AmmCdaHybrid, )); let market_id = 0; @@ -174,7 +174,7 @@ fn on_market_close_successfully_auto_closes_market_with_timestamps() { gen_metadata(50), MarketCreation::Permissionless, MarketType::Categorical(category_count), - Some(MarketDisputeMechanism::SimpleDisputes), + Some(MarketDisputeMechanism::Court), ScoringRule::AmmCdaHybrid, )); let market_id = 0; @@ -214,7 +214,7 @@ fn on_market_close_successfully_auto_closes_multiple_markets_after_stall() { gen_metadata(50), MarketCreation::Permissionless, MarketType::Categorical(category_count), - Some(MarketDisputeMechanism::SimpleDisputes), + Some(MarketDisputeMechanism::Court), ScoringRule::AmmCdaHybrid, )); assert_ok!(PredictionMarkets::create_market( @@ -227,7 +227,7 @@ fn on_market_close_successfully_auto_closes_multiple_markets_after_stall() { gen_metadata(50), MarketCreation::Permissionless, MarketType::Categorical(category_count), - Some(MarketDisputeMechanism::SimpleDisputes), + Some(MarketDisputeMechanism::Court), ScoringRule::AmmCdaHybrid, )); @@ -265,7 +265,7 @@ fn on_market_close_market_status_manager_exceeds_max_recovery_time_frames_after_ gen_metadata(50), MarketCreation::Permissionless, MarketType::Categorical(category_count), - Some(MarketDisputeMechanism::SimpleDisputes), + Some(MarketDisputeMechanism::Court), ScoringRule::AmmCdaHybrid, )); assert_ok!(PredictionMarkets::create_market( @@ -278,7 +278,7 @@ fn on_market_close_market_status_manager_exceeds_max_recovery_time_frames_after_ gen_metadata(50), MarketCreation::Permissionless, MarketType::Categorical(category_count), - Some(MarketDisputeMechanism::SimpleDisputes), + Some(MarketDisputeMechanism::Court), ScoringRule::AmmCdaHybrid, )); diff --git a/zrml/prediction-markets/src/tests/on_resolution.rs b/zrml/prediction-markets/src/tests/on_resolution.rs index 7e70a6730..02d92f214 100644 --- a/zrml/prediction-markets/src/tests/on_resolution.rs +++ b/zrml/prediction-markets/src/tests/on_resolution.rs @@ -98,7 +98,7 @@ fn on_resolution_correctly_reserves_and_unreserves_bonds_for_approved_advised_ma gen_metadata(2), MarketCreation::Advised, MarketType::Categorical(2), - Some(MarketDisputeMechanism::SimpleDisputes), + Some(MarketDisputeMechanism::Court), ScoringRule::AmmCdaHybrid, )); assert_ok!(PredictionMarkets::approve_market( @@ -147,7 +147,7 @@ fn on_resolution_correctly_reserves_and_unreserves_bonds_for_approved_advised_ma gen_metadata(2), MarketCreation::Advised, MarketType::Categorical(2), - Some(MarketDisputeMechanism::SimpleDisputes), + Some(MarketDisputeMechanism::Court), ScoringRule::AmmCdaHybrid, )); assert_ok!(PredictionMarkets::approve_market( @@ -197,7 +197,7 @@ fn on_resolution_correctly_reserves_and_unreserves_bonds_for_permissionless_mark gen_metadata(2), MarketCreation::Permissionless, MarketType::Categorical(2), - Some(MarketDisputeMechanism::SimpleDisputes), + Some(MarketDisputeMechanism::Authorized), ScoringRule::AmmCdaHybrid, )); let alice_balance_before = Balances::free_balance(ALICE); @@ -211,12 +211,12 @@ fn on_resolution_correctly_reserves_and_unreserves_bonds_for_permissionless_mark OutcomeReport::Categorical(0) )); assert_ok!(PredictionMarkets::dispute(RuntimeOrigin::signed(CHARLIE), 0,)); - assert_ok!(SimpleDisputes::suggest_outcome( - RuntimeOrigin::signed(CHARLIE), + assert_ok!(Authorized::authorize_market_outcome( + RuntimeOrigin::signed(AuthorizedDisputeResolutionUser::get()), 0, OutcomeReport::Categorical(1) )); - run_blocks(market.deadlines.dispute_duration); + run_blocks(::CorrectionPeriod::get()); check_reserve(&ALICE, 0); // ValidityBond bond is returned but OracleBond is slashed assert_eq!(Balances::free_balance(ALICE), alice_balance_before + ValidityBond::get()); @@ -248,7 +248,7 @@ fn on_resolution_correctly_reserves_and_unreserves_bonds_for_approved_advised_ma gen_metadata(2), MarketCreation::Advised, MarketType::Categorical(2), - Some(MarketDisputeMechanism::SimpleDisputes), + Some(MarketDisputeMechanism::Authorized), ScoringRule::AmmCdaHybrid, )); assert_ok!(PredictionMarkets::approve_market( @@ -266,12 +266,12 @@ fn on_resolution_correctly_reserves_and_unreserves_bonds_for_approved_advised_ma OutcomeReport::Categorical(0) )); assert_ok!(PredictionMarkets::dispute(RuntimeOrigin::signed(CHARLIE), 0,)); - assert_ok!(SimpleDisputes::suggest_outcome( - RuntimeOrigin::signed(CHARLIE), + assert_ok!(Authorized::authorize_market_outcome( + RuntimeOrigin::signed(AuthorizedDisputeResolutionUser::get()), 0, OutcomeReport::Categorical(1) )); - run_blocks(market.deadlines.dispute_duration); + run_blocks(::CorrectionPeriod::get()); check_reserve(&ALICE, 0); // ValidityBond bond is returned but OracleBond is slashed assert_eq!(Balances::free_balance(ALICE), alice_balance_before); @@ -303,7 +303,7 @@ fn on_resolution_correctly_reserves_and_unreserves_bonds_for_permissionless_mark gen_metadata(2), MarketCreation::Permissionless, MarketType::Categorical(2), - Some(MarketDisputeMechanism::SimpleDisputes), + Some(MarketDisputeMechanism::Authorized), ScoringRule::AmmCdaHybrid, )); let alice_balance_before = Balances::free_balance(ALICE); @@ -317,18 +317,12 @@ fn on_resolution_correctly_reserves_and_unreserves_bonds_for_permissionless_mark OutcomeReport::Categorical(0) )); assert_ok!(PredictionMarkets::dispute(RuntimeOrigin::signed(EVE), 0,)); - // EVE disputes with wrong outcome - assert_ok!(SimpleDisputes::suggest_outcome( - RuntimeOrigin::signed(EVE), - 0, - OutcomeReport::Categorical(1) - )); - assert_ok!(SimpleDisputes::suggest_outcome( - RuntimeOrigin::signed(CHARLIE), + assert_ok!(Authorized::authorize_market_outcome( + RuntimeOrigin::signed(AuthorizedDisputeResolutionUser::get()), 0, OutcomeReport::Categorical(0) )); - run_blocks(market.deadlines.dispute_duration); + run_blocks(::CorrectionPeriod::get()); check_reserve(&ALICE, 0); // ValidityBond bond is returned but OracleBond is not slashed assert_eq!( @@ -363,7 +357,7 @@ fn on_resolution_correctly_reserves_and_unreserves_bonds_for_advised_approved_ma gen_metadata(2), MarketCreation::Advised, MarketType::Categorical(2), - Some(MarketDisputeMechanism::SimpleDisputes), + Some(MarketDisputeMechanism::Authorized), ScoringRule::AmmCdaHybrid, )); assert_ok!(PredictionMarkets::approve_market( @@ -381,18 +375,12 @@ fn on_resolution_correctly_reserves_and_unreserves_bonds_for_advised_approved_ma OutcomeReport::Categorical(0) )); assert_ok!(PredictionMarkets::dispute(RuntimeOrigin::signed(EVE), 0,)); - // EVE disputes with wrong outcome - assert_ok!(SimpleDisputes::suggest_outcome( - RuntimeOrigin::signed(EVE), - 0, - OutcomeReport::Categorical(1) - )); - assert_ok!(SimpleDisputes::suggest_outcome( - RuntimeOrigin::signed(CHARLIE), + assert_ok!(Authorized::authorize_market_outcome( + RuntimeOrigin::signed(AuthorizedDisputeResolutionUser::get()), 0, OutcomeReport::Categorical(0) )); - run_blocks(market.deadlines.dispute_duration); + run_blocks(::CorrectionPeriod::get()); check_reserve(&ALICE, 0); // ValidityBond bond is returned but OracleBond is not slashed assert_eq!(Balances::free_balance(ALICE), alice_balance_before + OracleBond::get()); @@ -424,7 +412,7 @@ fn on_resolution_correctly_reserves_and_unreserves_bonds_for_permissionless_mark gen_metadata(2), MarketCreation::Permissionless, MarketType::Categorical(2), - Some(MarketDisputeMechanism::SimpleDisputes), + Some(MarketDisputeMechanism::Authorized), ScoringRule::AmmCdaHybrid, )); @@ -447,18 +435,12 @@ fn on_resolution_correctly_reserves_and_unreserves_bonds_for_permissionless_mark check_reserve(&outsider, ::OutsiderBond::get()); assert_ok!(PredictionMarkets::dispute(RuntimeOrigin::signed(EVE), 0,)); - // EVE disputes with wrong outcome - assert_ok!(SimpleDisputes::suggest_outcome( - RuntimeOrigin::signed(EVE), - 0, - OutcomeReport::Categorical(1) - )); - assert_ok!(SimpleDisputes::suggest_outcome( - RuntimeOrigin::signed(FRED), + assert_ok!(Authorized::authorize_market_outcome( + RuntimeOrigin::signed(AuthorizedDisputeResolutionUser::get()), 0, OutcomeReport::Categorical(0) )); - run_blocks(market.deadlines.dispute_duration); + run_blocks(::CorrectionPeriod::get()); check_reserve(&ALICE, 0); // ValidityBond bond is returned but OracleBond is slashed assert_eq!(Balances::free_balance(ALICE), alice_balance_before + ValidityBond::get()); @@ -496,7 +478,7 @@ fn on_resolution_correctly_reserves_and_unreserves_bonds_for_advised_approved_ma gen_metadata(2), MarketCreation::Advised, MarketType::Categorical(2), - Some(MarketDisputeMechanism::SimpleDisputes), + Some(MarketDisputeMechanism::Authorized), ScoringRule::AmmCdaHybrid, )); @@ -522,18 +504,12 @@ fn on_resolution_correctly_reserves_and_unreserves_bonds_for_advised_approved_ma check_reserve(&outsider, ::OutsiderBond::get()); assert_ok!(PredictionMarkets::dispute(RuntimeOrigin::signed(EVE), 0,)); - // EVE disputes with wrong outcome - assert_ok!(SimpleDisputes::suggest_outcome( - RuntimeOrigin::signed(EVE), - 0, - OutcomeReport::Categorical(1) - )); - assert_ok!(SimpleDisputes::suggest_outcome( - RuntimeOrigin::signed(FRED), + assert_ok!(Authorized::authorize_market_outcome( + RuntimeOrigin::signed(AuthorizedDisputeResolutionUser::get()), 0, OutcomeReport::Categorical(0) )); - run_blocks(market.deadlines.dispute_duration); + run_blocks(::CorrectionPeriod::get()); check_reserve(&ALICE, 0); // ValidityBond bond is returned but OracleBond is slashed assert_eq!(Balances::free_balance(ALICE), alice_balance_before); @@ -619,7 +595,7 @@ fn on_resolution_correctly_reserves_and_unreserves_bonds_for_permissionless_mark gen_metadata(2), MarketCreation::Permissionless, MarketType::Categorical(2), - Some(MarketDisputeMechanism::SimpleDisputes), + Some(MarketDisputeMechanism::Authorized), ScoringRule::AmmCdaHybrid, )); let alice_balance_before = Balances::free_balance(ALICE); @@ -665,7 +641,7 @@ fn on_resolution_correctly_reserves_and_unreserves_bonds_for_permissionless_mark gen_metadata(2), MarketCreation::Permissionless, MarketType::Categorical(2), - Some(MarketDisputeMechanism::SimpleDisputes), + Some(MarketDisputeMechanism::Court), ScoringRule::AmmCdaHybrid, )); let alice_balance_before = Balances::free_balance(ALICE); diff --git a/zrml/prediction-markets/src/tests/report.rs b/zrml/prediction-markets/src/tests/report.rs index 0cd5fe807..58b1a5b7f 100644 --- a/zrml/prediction-markets/src/tests/report.rs +++ b/zrml/prediction-markets/src/tests/report.rs @@ -141,7 +141,7 @@ fn it_allows_only_oracle_to_report_the_outcome_of_a_market_during_oracle_duratio gen_metadata(2), MarketCreation::Permissionless, MarketType::Categorical(2), - Some(MarketDisputeMechanism::SimpleDisputes), + Some(MarketDisputeMechanism::Court), ScoringRule::AmmCdaHybrid )); @@ -289,7 +289,7 @@ fn report_fails_on_market_state_proposed() { gen_metadata(2), MarketCreation::Advised, MarketType::Categorical(2), - Some(MarketDisputeMechanism::SimpleDisputes), + Some(MarketDisputeMechanism::Court), ScoringRule::AmmCdaHybrid )); assert_noop!( @@ -312,7 +312,7 @@ fn report_fails_on_market_state_closed_for_advised_market() { gen_metadata(2), MarketCreation::Advised, MarketType::Categorical(2), - Some(MarketDisputeMechanism::SimpleDisputes), + Some(MarketDisputeMechanism::Court), ScoringRule::AmmCdaHybrid )); assert_noop!( @@ -335,7 +335,7 @@ fn report_fails_on_market_state_active() { gen_metadata(2), MarketCreation::Permissionless, MarketType::Categorical(2), - Some(MarketDisputeMechanism::SimpleDisputes), + Some(MarketDisputeMechanism::Court), ScoringRule::AmmCdaHybrid )); assert_noop!( @@ -358,7 +358,7 @@ fn report_fails_on_market_state_resolved() { gen_metadata(2), MarketCreation::Advised, MarketType::Categorical(2), - Some(MarketDisputeMechanism::SimpleDisputes), + Some(MarketDisputeMechanism::Court), ScoringRule::AmmCdaHybrid )); let _ = MarketCommons::mutate_market(&0, |market| { @@ -385,7 +385,7 @@ fn report_fails_if_reporter_is_not_the_oracle() { gen_metadata(2), MarketCreation::Permissionless, MarketType::Categorical(2), - Some(MarketDisputeMechanism::SimpleDisputes), + Some(MarketDisputeMechanism::Court), ScoringRule::AmmCdaHybrid, )); let market = MarketCommons::market(&0).unwrap(); diff --git a/zrml/rikiddo/Cargo.toml b/zrml/rikiddo/Cargo.toml deleted file mode 100644 index 49f497771..000000000 --- a/zrml/rikiddo/Cargo.toml +++ /dev/null @@ -1,47 +0,0 @@ -[dependencies] -arbitrary = { workspace = true, features = ["derive"], optional = true } -cfg-if = { workspace = true } -env_logger = { workspace = true, optional = true } -frame-support = { workspace = true } -frame-system = { workspace = true } -hashbrown = { workspace = true } -pallet-balances = { workspace = true, optional = true } -pallet-timestamp = { workspace = true, optional = true } -parity-scale-codec = { workspace = true, features = ["derive", "max-encoded-len"] } -scale-info = { workspace = true, features = ["derive"] } -sp-core = { workspace = true } -sp-io = { workspace = true, optional = true } -sp-runtime = { workspace = true } -substrate-fixed = { workspace = true, features = ["serde"] } -zeitgeist-primitives = { workspace = true } - -[dev-dependencies] -zrml-rikiddo = { workspace = true, features = ["mock", "default"] } - -[features] -default = ["std"] -mock = [ - "pallet-balances/default", - "pallet-timestamp/default", - "sp-io/default", - "zeitgeist-primitives/mock", - "env_logger/default", -] -std = [ - "frame-support/std", - "frame-system/std", - "parity-scale-codec/std", - "sp-core/std", - "sp-runtime/std", - "substrate-fixed/std", - "zeitgeist-primitives/std", -] -try-runtime = [ - "frame-support/try-runtime", -] - -[package] -authors = ["Zeitgeist PM "] -edition.workspace = true -name = "zrml-rikiddo" -version = "0.5.3" diff --git a/zrml/rikiddo/README.md b/zrml/rikiddo/README.md deleted file mode 100644 index 5fb98d4df..000000000 --- a/zrml/rikiddo/README.md +++ /dev/null @@ -1,18 +0,0 @@ -# Rikiddo Module - -Generic and modular implemenation of Rikiddo market scoring rule. - -## Overview - -Provides traits and implementations for sigmoid fee caluclation, calculation of -ema based on market volume, LMSR and Rikiddo using sigmoid fee calculation and -two ema periods. - -Rikiddo is a liquidity-sensitive logarithm market scoring algorithm, which can -be used to determine the prices of event assets and their corresponding -probabilities. It incorporates historical trading data to optimize it's -reactiveness to abrupt and longer lasting changes in the market trend. More -information at [blog.zeitgeist.pm]. - -[blog.zeitgeist.pm]: - https://blog.zeitgeist.pm/introducing-zeitgeists-rikiddo-scoring-rule/ diff --git a/zrml/rikiddo/fuzz/Cargo.toml b/zrml/rikiddo/fuzz/Cargo.toml deleted file mode 100644 index aa397bbac..000000000 --- a/zrml/rikiddo/fuzz/Cargo.toml +++ /dev/null @@ -1,89 +0,0 @@ -[[bin]] -doc = false -name = "balance_to_fixedu_conversion" -path = "balance_to_fixedu_conversion.rs" -test = false - -[[bin]] -doc = false -name = "ema_market_volume_first_state" -path = "ema_market_volume_first_state.rs" -test = false - -[[bin]] -doc = false -name = "ema_market_volume_second_state" -path = "ema_market_volume_second_state.rs" -test = false - -[[bin]] -doc = false -name = "ema_market_volume_third_state" -path = "ema_market_volume_third_state.rs" -test = false - -[[bin]] -doc = false -name = "ema_market_volume_estimate_ema" -path = "ema_market_volume_estimate_ema.rs" -test = false - -[[bin]] -doc = false -name = "fee_sigmoid" -path = "fee_sigmoid.rs" -test = false - -[[bin]] -doc = false -name = "fixedi_to_fixedu_conversion" -path = "fixedi_to_fixedu_conversion.rs" -test = false - -[[bin]] -doc = false -name = "fixedu_to_balance_conversion" -path = "fixedu_to_balance_conversion.rs" -test = false - -[[bin]] -doc = false -name = "fixedu_to_fixedi_conversion" -path = "fixedu_to_fixedi_conversion.rs" -test = false - -[[bin]] -doc = false -name = "rikiddo_pallet" -path = "rikiddo_pallet.rs" -test = false - -[[bin]] -doc = false -name = "rikiddo_with_calculated_fee" -path = "rikiddo_with_calculated_fee.rs" -test = false - -[[bin]] -doc = false -name = "rikiddo_with_initial_fee" -path = "rikiddo_with_initial_fee.rs" -test = false - -[dependencies] -arbitrary = { workspace = true, features = ["derive"] } -frame-support = { workspace = true, features = ["default"] } -frame-system = { workspace = true, features = ["default"] } -libfuzzer-sys = { workspace = true } -substrate-fixed = { workspace = true } -zrml-rikiddo = { workspace = true, features = ["arbitrary", "mock", "default"] } - -[package] -authors = ["Automatically generated"] -edition.workspace = true -name = "zrml-rikiddo-fuzz" -publish = false -version = "0.0.0" - -[package.metadata] -cargo-fuzz = true diff --git a/zrml/rikiddo/fuzz/balance_to_fixedu_conversion.rs b/zrml/rikiddo/fuzz/balance_to_fixedu_conversion.rs deleted file mode 100644 index 2010927a1..000000000 --- a/zrml/rikiddo/fuzz/balance_to_fixedu_conversion.rs +++ /dev/null @@ -1,33 +0,0 @@ -// 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 . - -//! Fuzz test: Conversion Balance -> FixedU -#![allow( - // Mocks are only used for fuzzing and unit tests - clippy::arithmetic_side_effects -)] -#![no_main] - -use libfuzzer_sys::fuzz_target; -use substrate_fixed::{types::extra::U33, FixedU128}; -use zrml_rikiddo::traits::FromFixedDecimal; - -fuzz_target!(|balance: u128| { - for i in 0..12u8 { - let _ = >::from_fixed_decimal(balance, i); - } -}); diff --git a/zrml/rikiddo/fuzz/ema_market_volume_estimate_ema.rs b/zrml/rikiddo/fuzz/ema_market_volume_estimate_ema.rs deleted file mode 100644 index add40ca7f..000000000 --- a/zrml/rikiddo/fuzz/ema_market_volume_estimate_ema.rs +++ /dev/null @@ -1,62 +0,0 @@ -// 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 . - -//! Fuzz test: EmaMarketVolume is called with estimation mode activated -//! -> Configure the struct in a way that it estimates the ema at the second update, update -#![allow( - // Mocks are only used for fuzzing and unit tests - clippy::arithmetic_side_effects -)] -#![no_main] - -use arbitrary::Arbitrary; -use libfuzzer_sys::fuzz_target; - -mod shared; -use shared::fixed_from_u128; -use substrate_fixed::{ - traits::LossyInto, - types::extra::{U127, U33}, - FixedU128, -}; -use zrml_rikiddo::{ - traits::MarketAverage, - types::{EmaMarketVolume, Timespan, TimestampedVolume}, -}; - -fuzz_target!(|data: Data| { - let mut emv = data.ema_market_volume; - // We need smoothing-1 volumes to get into the third state - let between_zero_and_two = >::from_ne_bytes(data.smoothing.to_ne_bytes()); - emv.config.smoothing = between_zero_and_two.lossy_into(); - emv.config.ema_period_estimate_after = Some(Timespan::Seconds(0)); - let first_timestamped_volume = - TimestampedVolume { timestamp: 0, volume: fixed_from_u128(data.first_update_volume) }; - let second_timestamped_volume = - TimestampedVolume { timestamp: 1, volume: fixed_from_u128(data.first_update_volume) }; - let _ = emv.update_volume(&first_timestamped_volume); - let _ = emv.update_volume(&second_timestamped_volume); - let _ = emv.update_volume(&data.third_update_volume); -}); - -#[derive(Debug, Arbitrary)] -struct Data { - first_update_volume: u128, - smoothing: u128, - third_update_volume: TimestampedVolume>, - ema_market_volume: EmaMarketVolume>, -} diff --git a/zrml/rikiddo/fuzz/ema_market_volume_first_state.rs b/zrml/rikiddo/fuzz/ema_market_volume_first_state.rs deleted file mode 100644 index 122aa1124..000000000 --- a/zrml/rikiddo/fuzz/ema_market_volume_first_state.rs +++ /dev/null @@ -1,43 +0,0 @@ -// 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 . - -//! Fuzz test: EmaMarketVolume is called during first state -//! -> update_volume -#![allow( - // Mocks are only used for fuzzing and unit tests - clippy::arithmetic_side_effects -)] -#![no_main] - -use arbitrary::Arbitrary; -use libfuzzer_sys::fuzz_target; -use substrate_fixed::{types::extra::U33, FixedU128}; -use zrml_rikiddo::{ - traits::MarketAverage, - types::{EmaMarketVolume, TimestampedVolume}, -}; - -fuzz_target!(|data: Data| { - let mut emv = data.ema_market_volume; - let _ = emv.update_volume(&data.update_volume); -}); - -#[derive(Debug, Arbitrary)] -struct Data { - update_volume: TimestampedVolume>, - ema_market_volume: EmaMarketVolume>, -} diff --git a/zrml/rikiddo/fuzz/ema_market_volume_second_state.rs b/zrml/rikiddo/fuzz/ema_market_volume_second_state.rs deleted file mode 100644 index c6f8bf791..000000000 --- a/zrml/rikiddo/fuzz/ema_market_volume_second_state.rs +++ /dev/null @@ -1,50 +0,0 @@ -// 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 . - -//! Fuzz test: EmaMarketVolume is called during second state. -//! -> change state (update), update, get ema, clear -#![allow( - // Mocks are only used for fuzzing and unit tests - clippy::arithmetic_side_effects -)] -#![no_main] - -use arbitrary::Arbitrary; -use libfuzzer_sys::fuzz_target; -use substrate_fixed::{types::extra::U33, FixedU128}; -use zrml_rikiddo::{ - traits::MarketAverage, - types::{EmaMarketVolume, TimestampedVolume}, -}; - -mod shared; -use shared::fixed_from_u128; - -fuzz_target!(|data: Data| { - let mut emv = data.ema_market_volume; - let first_timestamped_volume = - TimestampedVolume { timestamp: 0, volume: fixed_from_u128(data.first_update_volume) }; - let _ = emv.update_volume(&first_timestamped_volume); - let _ = emv.update_volume(&data.second_update_volume); -}); - -#[derive(Debug, Arbitrary)] -struct Data { - first_update_volume: u128, - second_update_volume: TimestampedVolume>, - ema_market_volume: EmaMarketVolume>, -} diff --git a/zrml/rikiddo/fuzz/ema_market_volume_third_state.rs b/zrml/rikiddo/fuzz/ema_market_volume_third_state.rs deleted file mode 100644 index 8c26b8b8e..000000000 --- a/zrml/rikiddo/fuzz/ema_market_volume_third_state.rs +++ /dev/null @@ -1,63 +0,0 @@ -// 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 . - -//! Fuzz test: EmaMarketVolume is called during third and last state. -//! -> change state (two updates with specific configuration), update -#![allow( - // Mocks are only used for fuzzing and unit tests - clippy::arithmetic_side_effects -)] -#![no_main] - -use arbitrary::Arbitrary; -use libfuzzer_sys::fuzz_target; -use substrate_fixed::{ - traits::LossyInto, - types::extra::{U127, U33}, - FixedU128, -}; -use zrml_rikiddo::{ - traits::MarketAverage, - types::{EmaMarketVolume, Timespan, TimestampedVolume}, -}; - -mod shared; -use shared::fixed_from_u128; - -fuzz_target!(|data: Data| { - let mut emv = data.ema_market_volume; - // We need smoothing-1 volumes to get into the third state - let between_zero_and_two = >::from_ne_bytes(data.smoothing.to_ne_bytes()); - emv.config.smoothing = between_zero_and_two.lossy_into(); - emv.config.ema_period = Timespan::Seconds(0); - emv.config.ema_period_estimate_after = None; - let first_timestamped_volume = - TimestampedVolume { timestamp: 0, volume: fixed_from_u128(data.first_update_volume) }; - let second_timestamped_volume = - TimestampedVolume { timestamp: 1, volume: fixed_from_u128(data.first_update_volume) }; - let _ = emv.update_volume(&first_timestamped_volume); - let _ = emv.update_volume(&second_timestamped_volume); - let _ = emv.update_volume(&data.third_update_volume); -}); - -#[derive(Debug, Arbitrary)] -struct Data { - first_update_volume: u128, - smoothing: u128, - third_update_volume: TimestampedVolume>, - ema_market_volume: EmaMarketVolume>, -} diff --git a/zrml/rikiddo/fuzz/fee_sigmoid.rs b/zrml/rikiddo/fuzz/fee_sigmoid.rs deleted file mode 100644 index 6277c0b70..000000000 --- a/zrml/rikiddo/fuzz/fee_sigmoid.rs +++ /dev/null @@ -1,41 +0,0 @@ -// 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 . - -//! Fuzz test: FeeSigmoid.calculate() is called -#![allow( - // Mocks are only used for fuzzing and unit tests - clippy::arithmetic_side_effects -)] -#![no_main] - -use arbitrary::Arbitrary; -use libfuzzer_sys::fuzz_target; -use substrate_fixed::{types::extra::U33, FixedI128}; -use zrml_rikiddo::{traits::Fee, types::FeeSigmoid}; - -mod shared; -use shared::fixed_from_i128; - -fuzz_target!(|data: Data| { - let _ = data.sigmoid_fee.calculate_fee(fixed_from_i128(data.sigmoid_fee_calculate_r)); -}); - -#[derive(Debug, Arbitrary)] -struct Data { - sigmoid_fee_calculate_r: i128, - sigmoid_fee: FeeSigmoid>, -} diff --git a/zrml/rikiddo/fuzz/fixedi_to_fixedu_conversion.rs b/zrml/rikiddo/fuzz/fixedi_to_fixedu_conversion.rs deleted file mode 100644 index 98ff02f48..000000000 --- a/zrml/rikiddo/fuzz/fixedi_to_fixedu_conversion.rs +++ /dev/null @@ -1,34 +0,0 @@ -// 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 . - -//! Fuzz test: Conversion FixedI -> FixedU -#![allow( - // Mocks are only used for fuzzing and unit tests - clippy::arithmetic_side_effects -)] -#![no_main] - -use libfuzzer_sys::fuzz_target; -use substrate_fixed::{types::extra::U33, FixedU128}; -use zrml_rikiddo::utils::convert_to_unsigned; - -mod shared; -use shared::fixed_from_i128; - -fuzz_target!(|fixed_num: i128| { - let _: Result, &'static str> = convert_to_unsigned(fixed_from_i128(fixed_num)); -}); diff --git a/zrml/rikiddo/fuzz/fixedu_to_balance_conversion.rs b/zrml/rikiddo/fuzz/fixedu_to_balance_conversion.rs deleted file mode 100644 index cc2c3b5a1..000000000 --- a/zrml/rikiddo/fuzz/fixedu_to_balance_conversion.rs +++ /dev/null @@ -1,37 +0,0 @@ -// 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 . - -//! Fuzz test: Conversion Balance -> FixedU -#![allow( - // Mocks are only used for fuzzing and unit tests - clippy::arithmetic_side_effects -)] -#![no_main] - -use libfuzzer_sys::fuzz_target; -use zrml_rikiddo::traits::FromFixedToDecimal; - -mod shared; -use shared::fixed_from_u128; - -fuzz_target!(|fixed_num: u128| { - let num = fixed_from_u128(fixed_num); - - for fractional_places in 0..12u8 { - let _ = u128::from_fixed_to_fixed_decimal(num, fractional_places); - } -}); diff --git a/zrml/rikiddo/fuzz/fixedu_to_fixedi_conversion.rs b/zrml/rikiddo/fuzz/fixedu_to_fixedi_conversion.rs deleted file mode 100644 index 19792f328..000000000 --- a/zrml/rikiddo/fuzz/fixedu_to_fixedi_conversion.rs +++ /dev/null @@ -1,34 +0,0 @@ -// 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 . - -//! Fuzz test: Conversion FixedU -> FixedI -#![allow( - // Mocks are only used for fuzzing and unit tests - clippy::arithmetic_side_effects -)] -#![no_main] - -use libfuzzer_sys::fuzz_target; -use substrate_fixed::{types::extra::U33, FixedI128}; -use zrml_rikiddo::utils::convert_to_signed; - -mod shared; -use shared::fixed_from_u128; - -fuzz_target!(|fixed_num: u128| { - let _: Result, &'static str> = convert_to_signed(fixed_from_u128(fixed_num)); -}); diff --git a/zrml/rikiddo/fuzz/rikiddo_pallet.rs b/zrml/rikiddo/fuzz/rikiddo_pallet.rs deleted file mode 100644 index 5fd0afc0a..000000000 --- a/zrml/rikiddo/fuzz/rikiddo_pallet.rs +++ /dev/null @@ -1,94 +0,0 @@ -// 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 . - -//! Fuzz test: Rikiddo pallet is called with calculated fee -//! -> create, force fee by multiple update_volume, cost, price, all_prices, clear, destroy -#![allow( - // Mocks are only used for fuzzing and unit tests - clippy::arithmetic_side_effects -)] -#![allow(clippy::type_complexity)] -#![no_main] - -use arbitrary::Arbitrary; -use frame_support::traits::{OnFinalize, OnInitialize}; -use frame_system::RawOrigin; -use libfuzzer_sys::fuzz_target; -use substrate_fixed::{types::extra::U33, FixedI128, FixedU128}; -use zrml_rikiddo::{ - mock::*, - traits::RikiddoMVPallet, - types::{EmaMarketVolume, FeeSigmoid, RikiddoSigmoidMV, Timespan}, - Config, -}; - -fn run_to_block(n: u64) { - while System::block_number() < n { - Timestamp::on_finalize(System::block_number()); - Balances::on_finalize(System::block_number()); - Rikiddo::on_finalize(System::block_number()); - System::on_finalize(System::block_number()); - System::set_block_number(System::block_number() + 1); - System::on_initialize(System::block_number()); - Timestamp::on_initialize(System::block_number()); - Balances::on_initialize(System::block_number()); - Rikiddo::on_initialize(System::block_number()); - } -} - -fuzz_target!(|data: Data| { - let mut ext = ExtBuilder::default().build(); - ext.execute_with(|| { - let mut rikiddo: RikiddoSigmoidMV< - FixedU128, - FixedI128, - FeeSigmoid>, - EmaMarketVolume>, - > = Default::default(); - - rikiddo.ma_short.config.ema_period = Timespan::Seconds(0); - rikiddo.ma_long.config.ema_period = Timespan::Seconds(1); - let pool_id = 0; - let mut current_block = 0; - let _ = Rikiddo::create(pool_id, rikiddo); - ::Timestamp::set(RawOrigin::None.into(), 0).unwrap(); - - // Initialize ma_short and ma_long ema - for (idx, volume) in data.update_volumes.iter().enumerate() { - let _ = Rikiddo::update_volume(pool_id, *volume); - - if idx % 2 == 1 { - current_block += 1; - run_to_block(current_block); - ::Timestamp::set(RawOrigin::None.into(), current_block).unwrap(); - } - } - - let _ = Rikiddo::cost(pool_id, &data.asset_balances); - let _ = Rikiddo::price(pool_id, data.price_for, &data.asset_balances); - let _ = Rikiddo::all_prices(pool_id, &data.asset_balances); - let _ = Rikiddo::fee(pool_id); - }); - let _ = ext.commit_all(); -}); - -#[derive(Debug, Arbitrary)] -struct Data { - asset_balances: [::Balance; 8], - price_for: ::Balance, - update_volumes: [::Balance; 5], -} diff --git a/zrml/rikiddo/fuzz/rikiddo_with_calculated_fee.rs b/zrml/rikiddo/fuzz/rikiddo_with_calculated_fee.rs deleted file mode 100644 index 0ae9b4679..000000000 --- a/zrml/rikiddo/fuzz/rikiddo_with_calculated_fee.rs +++ /dev/null @@ -1,78 +0,0 @@ -// 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 . - -//! Fuzz test: Rikiddo is called with calculated fee -//! -> force EmaMarketVolume, cost, price, all_prices -#![allow( - // Mocks are only used for fuzzing and unit tests - clippy::arithmetic_side_effects -)] -#![allow(clippy::type_complexity)] -#![no_main] - -use arbitrary::Arbitrary; -use libfuzzer_sys::fuzz_target; - -mod shared; -use shared::fixed_from_u128; -use substrate_fixed::{types::extra::U33, FixedI128, FixedU128}; -use zrml_rikiddo::{ - traits::{Lmsr, RikiddoMV}, - types::{ - EmaMarketVolume, FeeSigmoid, RikiddoSigmoidMV, Timespan, TimestampedVolume, UnixTimestamp, - }, -}; - -fuzz_target!(|data: Data| { - let asset_balances_fixed: Vec> = - data.asset_balances.iter().map(|e| fixed_from_u128(*e)).collect(); - let price_for_fixed = fixed_from_u128(data.price_for); - let mut rikiddo = data.rikiddo; - // Use reasonable parameters for fee calculation and EMA - rikiddo.fees = Default::default(); - rikiddo.ma_short = Default::default(); - rikiddo.ma_long = Default::default(); - rikiddo.ma_short.config.ema_period = Timespan::Seconds(0); - rikiddo.ma_long.config.ema_period = Timespan::Seconds(1); - - // Initialize ma_short and ma_long ema - for (idx, volume) in data.update_volumes.iter().enumerate() { - let timestamped_volume = TimestampedVolume { - timestamp: (idx / 2) as UnixTimestamp, - volume: fixed_from_u128(*volume), - }; - let _ = rikiddo.update_volume(×tamped_volume); - } - - // Calculate cost and price using calculated fee from r = ma_short / ma_long - let _ = rikiddo.cost(&asset_balances_fixed[..]); - let _ = rikiddo.price(&asset_balances_fixed[..], &price_for_fixed); - let _ = rikiddo.all_prices(&asset_balances_fixed[..]); -}); - -#[derive(Debug, Arbitrary)] -struct Data { - asset_balances: [u128; 8], - price_for: u128, - rikiddo: RikiddoSigmoidMV< - FixedU128, - FixedI128, - FeeSigmoid>, - EmaMarketVolume>, - >, - update_volumes: [u128; 5], -} diff --git a/zrml/rikiddo/fuzz/rikiddo_with_initial_fee.rs b/zrml/rikiddo/fuzz/rikiddo_with_initial_fee.rs deleted file mode 100644 index 01ea3ab27..000000000 --- a/zrml/rikiddo/fuzz/rikiddo_with_initial_fee.rs +++ /dev/null @@ -1,56 +0,0 @@ -// 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 . - -//! Fuzz test: Rikiddo is called with initial fee -> cost, price, all_prices, clear -#![allow( - // Mocks are only used for fuzzing and unit tests - clippy::arithmetic_side_effects -)] -#![allow(clippy::type_complexity)] -#![no_main] - -use arbitrary::Arbitrary; -use libfuzzer_sys::fuzz_target; - -mod shared; -use shared::fixed_from_u128; -use substrate_fixed::{types::extra::U33, FixedI128, FixedU128}; -use zrml_rikiddo::{ - traits::Lmsr, - types::{EmaMarketVolume, FeeSigmoid, RikiddoSigmoidMV}, -}; - -fuzz_target!(|data: Data| { - let asset_balances_fixed: Vec> = - data.asset_balances.iter().map(|e| fixed_from_u128(*e)).collect(); - let price_for_fixed = fixed_from_u128(data.price_for); - let _ = data.rikiddo.cost(&asset_balances_fixed[..]); - let _ = data.rikiddo.price(&asset_balances_fixed[..], &price_for_fixed); - let _ = data.rikiddo.all_prices(&asset_balances_fixed[..]); -}); - -#[derive(Debug, Arbitrary)] -struct Data { - rikiddo: RikiddoSigmoidMV< - FixedU128, - FixedI128, - FeeSigmoid>, - EmaMarketVolume>, - >, - asset_balances: [u128; 8], - price_for: u128, -} diff --git a/zrml/rikiddo/fuzz/shared.rs b/zrml/rikiddo/fuzz/shared.rs deleted file mode 100644 index b001899fc..000000000 --- a/zrml/rikiddo/fuzz/shared.rs +++ /dev/null @@ -1,30 +0,0 @@ -// 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 . - -#![allow(dead_code)] - -use substrate_fixed::{types::extra::U33, FixedI128, FixedU128}; - -#[inline(always)] -pub(super) fn fixed_from_i128(from: i128) -> FixedI128 { - FixedI128::::from_ne_bytes(from.to_ne_bytes()) -} - -#[inline(always)] -pub(super) fn fixed_from_u128(from: u128) -> FixedU128 { - FixedU128::::from_ne_bytes(from.to_ne_bytes()) -} diff --git a/zrml/rikiddo/src/constants.rs b/zrml/rikiddo/src/constants.rs deleted file mode 100644 index 3ee27ac9d..000000000 --- a/zrml/rikiddo/src/constants.rs +++ /dev/null @@ -1,60 +0,0 @@ -// 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 . - -//! This module contains constants that are used during the processing of market data and -//! the calculation of fees. - -use crate::types::Timespan; -use substrate_fixed::{ - types::extra::{U24, U32}, - FixedI32, FixedU32, -}; - -// --- Default configuration for EmaConfig struct --- -/// Default short EMA period (in seconds) -/// One hour. -pub const EMA_SHORT: Timespan = Timespan::Hours(1); - -/// Default long EMA period (in seconds) -/// Six hours. -pub const EMA_LONG: Timespan = Timespan::Hours(6); - -/// Default smoothing factor for EMA calculation -/// 2.0 -pub const SMOOTHING: FixedU32 = >::from_bits(0x0200_0000); - -// --- Default configuration for FeeSigmoidConfig struct --- -/// m value -/// 0.01 -pub const M: FixedI32 = >::from_bits(0x0002_8F5C); - -/// p value -/// 2.0 -pub const P: FixedI32 = >::from_bits(0x0200_0000); - -/// n value -/// 0.0 -pub const N: FixedI32 = >::from_bits(0x0000_0000); - -// --- Default configuration for RikiddoConfig struct --- -/// Initial fee f -/// 0.005 -pub const INITIAL_FEE: FixedU32 = >::from_bits(0x0147_AE14); - -/// Minimal revenue w (proportion of initial fee f) -/// f * β = 0.005 * 0.7 = 0.0035 -pub const MINIMAL_REVENUE: FixedU32 = >::from_bits(0x00E5_6042); diff --git a/zrml/rikiddo/src/lib.rs b/zrml/rikiddo/src/lib.rs deleted file mode 100644 index 049885acd..000000000 --- a/zrml/rikiddo/src/lib.rs +++ /dev/null @@ -1,412 +0,0 @@ -// Copyright 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 . - -#![doc = include_str!("../README.md")] -#![cfg_attr(not(feature = "std"), no_std)] -// This is required to be able to use the derive(Arbitrary) macro. -#![cfg_attr(feature = "arbitrary", allow(clippy::arithmetic_side_effects))] -#![deny(missing_docs)] - -extern crate alloc; - -pub mod constants; -pub mod mock; -mod tests; -pub mod traits; -pub mod types; -pub mod utils; -pub use pallet::*; - -/// The pallet that bridges Rikiddo instances to pools. -/// -/// Abstracts Rikiddo's core functions to be used within a Substrate chain. -/// -/// This implementation of the Rikiddo pallet is solely a "bookkeeper" of Rikiddo instances, -/// i.e. it can spawn, update, destroy and use a specific instance to retrieve price -/// information for a given set of assets. Internally it uses a Rikiddo implementation that -/// is based on a port of the rust fixed library, [substrate-fixed], consequently it must -/// handle all conversions between the `Balance` type and the selected fixed point type. -/// -/// This pallet is highly configurable, you can select the balance type, the fixed point types -/// and the actual implementation of Rikiddo (for example ema or wma of market data) for your -/// specific use case. By using multiple instances, potentially multiple Rikiddo variants -/// can run simulatenously on one chain, which can be used to ease migrations. -/// -/// [substrate-fixed]: https://github.com/encointer/substrate-fixed - -// The allow(missing_docs) attribute seems to be necessary, because some attribute-like macros -// from the Substrate framework generate undocumented code. It seems to be impossible to move -// the code into an anonymous module to resolve this issue. -#[allow(missing_docs)] -#[frame_support::pallet] -pub mod pallet { - use crate::{ - traits::{FromFixedDecimal, FromFixedToDecimal, Lmsr, RikiddoMV, RikiddoMVPallet}, - types::{TimestampedVolume, UnixTimestamp}, - }; - use alloc::vec::Vec; - use core::{ - convert::TryFrom, - fmt::Debug, - marker::PhantomData, - ops::{AddAssign, BitOrAssign, ShlAssign}, - }; - use frame_support::{ - debug, - dispatch::DispatchResult, - pallet_prelude::StorageMap, - traits::{Get, Hooks, StorageVersion, Time}, - Twox64Concat, - }; - use frame_system::pallet_prelude::BlockNumberFor; - use parity_scale_codec::{Decode, Encode, FullCodec, FullEncode, MaxEncodedLen}; - use scale_info::TypeInfo; - use sp_runtime::DispatchError; - use substrate_fixed::{ - traits::{Fixed, FixedSigned, FixedUnsigned, LossyFrom, ToFixed}, - types::{ - extra::{U127, U128, U31, U32}, - I9F23, U1F127, - }, - FixedI128, FixedI32, FixedU128, FixedU32, - }; - - /// The current storage version. - const STORAGE_VERSION: StorageVersion = StorageVersion::new(0); - - #[pallet::config] - pub trait Config: frame_system::Config { - /// Defines the type of traded amounts. - type Balance: Copy + Into + TryFrom + Debug; - - /// Offers timestamping functionality. - type Timestamp: Time; - - /// Will be used for the fractional part of the fixed point numbers. - /// Calculation: Select FixedTYPE, such that TYPE = the type of Balance (i.e. FixedU128) - /// Select the generic UWIDTH = floor(log2(10.pow(fractional_decimals))). - type FixedTypeU: Decode - + Encode - + FixedUnsigned - + LossyFrom> - + LossyFrom>; - - /// Will be used for the fractional part of the fixed point numbers. - /// Calculation: Select FixedTYPE, such that it is the signed variant of FixedTypeU. - /// It is possible to reduce the fractional bit count by one, effectively eliminating - /// conversion overflows when the MSB of the unsigned fixed type is set, but in exchange - /// Reducing the fractional precision by one bit. - type FixedTypeS: Decode - + Encode - + FixedSigned - + From - + LossyFrom> - + LossyFrom - + LossyFrom> - + PartialOrd; - - /// Number of fractional decimal places for one unit of currency. - #[pallet::constant] - type BalanceFractionalDecimals: Get; - - /// Type that's used as an id for pools. - type PoolId: Copy + Decode + FullEncode + MaxEncodedLen + TypeInfo; - - /// Rikiddo variant. - type Rikiddo: RikiddoMV - + Decode - + FullCodec - + MaxEncodedLen - + TypeInfo; - } - - /// Potential errors within the Rikiddo pallet. - #[pallet::error] - pub enum Error { - /// Conversion between the `Balance` and the internal Rikiddo core type failed. - FixedConversionImpossible, - /// For a given `poolid`, no Rikiddo instance could be found. - RikiddoNotFoundForPool, - /// Trying to create a Rikiddo instance for a `poolid` that already has a Rikiddo instance. - RikiddoAlreadyExistsForPool, - } - - /// Storage that maps pool ids to Rikiddo instances. - #[pallet::storage] - pub type RikiddoPerPool, I: 'static = ()> = - StorageMap<_, Twox64Concat, T::PoolId, T::Rikiddo>; - - #[pallet::hooks] - impl, I: 'static> Hooks> for Pallet {} - - #[pallet::pallet] - #[pallet::storage_version(STORAGE_VERSION)] - pub struct Pallet(PhantomData, PhantomData); - - #[pallet::call] - impl, I: 'static> Pallet {} - - impl, I: 'static> Pallet { - pub(crate) fn get_rikiddo(poolid: &T::PoolId) -> Result { - if let Ok(rikiddo) = >::try_get(poolid) { - Ok(rikiddo) - } else { - Err(Error::::RikiddoNotFoundForPool.into()) - } - } - - fn convert_balance_to_fixed(balance: &T::Balance) -> Result { - match T::FixedTypeU::from_fixed_decimal(*balance, T::BalanceFractionalDecimals::get()) { - Ok(res) => Ok(res), - Err(err) => { - debug(&err); - Err(Error::::FixedConversionImpossible.into()) - } - } - } - - fn convert_fixed_to_balance(fixed: &T::FixedTypeU) -> Result { - match T::Balance::from_fixed_to_fixed_decimal( - *fixed, - T::BalanceFractionalDecimals::get(), - ) { - Ok(res) => Ok(res), - Err(err) => { - debug(&err); - Err(Error::::FixedConversionImpossible.into()) - } - } - } - - fn convert_balance_to_fixed_vector( - balance: &[T::Balance], - ) -> Result, DispatchError> { - balance - .iter() - .map(|e| Self::convert_balance_to_fixed(e)) - .collect::, DispatchError>>() - } - - fn convert_fixed_to_balance_vector( - fixed: &[T::FixedTypeU], - ) -> Result, DispatchError> { - fixed - .iter() - .map(|e| Self::convert_fixed_to_balance(e)) - .collect::, DispatchError>>() - } - } - - impl, I: 'static> RikiddoMVPallet for Pallet - where - ::Bits: Copy + ToFixed + AddAssign + BitOrAssign + ShlAssign, - ::Moment: Into, - { - type Balance = T::Balance; - type PoolId = T::PoolId; - type FU = T::FixedTypeU; - type Rikiddo = T::Rikiddo; - - /// Returns a vector of prices for a given set of assets (same order as `asset_balances`). - /// - /// # Arguments - /// - /// * `poolid`: The id of the asset pool for which all asset prices shall be calculated. - /// * `asset_balances`: The balance vector of the assets. - fn all_prices( - poolid: Self::PoolId, - asset_balances: &[Self::Balance], - ) -> Result, DispatchError> { - let rikiddo = Self::get_rikiddo(&poolid)?; - let balances_fixed = Self::convert_balance_to_fixed_vector(asset_balances)?; - - match rikiddo.all_prices(&balances_fixed) { - Ok(prices) => Self::convert_fixed_to_balance_vector(&prices), - Err(err) => { - debug(&err); - Err(err.into()) - } - } - } - - /// Clear market data for a specific asset pool. - /// - /// # Arguments - /// - /// * `poolid`: The id of the asset pool for which all asset prices shall be calculated. - fn clear(poolid: Self::PoolId) -> Result<(), DispatchError> { - let mut rikiddo = Self::get_rikiddo(&poolid)?; - rikiddo.clear(); - Ok(()) - } - - /// Returns the total cost for a specific vector of assets (see [LS-LMSR paper]). - /// - /// [LS-LMSR paper]: https://www.eecs.harvard.edu/cs286r/courses/fall12/papers/OPRS10.pdf - /// # Arguments - /// - /// * `poolid`: The id of the asset pool for which all asset prices shall be calculated. - /// * `asset_balances`: The balance vector of the assets. - fn cost( - poolid: Self::PoolId, - asset_balances: &[Self::Balance], - ) -> Result { - let rikiddo = Self::get_rikiddo(&poolid)?; - let balances_fixed = Self::convert_balance_to_fixed_vector(asset_balances)?; - - match rikiddo.cost(&balances_fixed) { - Ok(cost) => Self::convert_fixed_to_balance(&cost), - Err(err) => { - debug(&err); - Err(err.into()) - } - } - } - - /// Create Rikiddo instance for specifc asset pool. - /// - /// # Arguments - /// - /// * `poolid`: The id of the asset pool for which all asset prices shall be calculated. - /// * `rikiddo`: A specific type of Rikiddo as specified in the pallet's configuration. - fn create(poolid: Self::PoolId, rikiddo: Self::Rikiddo) -> DispatchResult { - if Self::get_rikiddo(&poolid).is_ok() { - return Err(Error::::RikiddoAlreadyExistsForPool.into()); - } - - >::insert(poolid, rikiddo); - Ok(()) - } - - /// Destroy Rikiddo instance for a specific pool. - /// - /// # Arguments - /// - /// * `poolid`: The id of the asset pool for which all asset prices shall be calculated. - fn destroy(poolid: Self::PoolId) -> DispatchResult { - let _ = Self::get_rikiddo(&poolid)?; - >::remove(poolid); - Ok(()) - } - - /// Returns the current fee. - /// - /// # Arguments - /// - /// * `poolid`: The id of the asset pool for which all asset prices shall be calculated. - /// * `rikiddo`: A specific type of Rikiddo as specified in the pallet's configuration. - fn fee(poolid: Self::PoolId) -> Result { - let rikiddo = Self::get_rikiddo(&poolid)?; - - match rikiddo.fee() { - Ok(fee) => Self::convert_fixed_to_balance(&fee), - Err(err) => { - debug(&err); - Err(err.into()) - } - } - } - - /// Returns the initial quantities of outstanding event outcome assets. - /// If 4 event outcome assets exist and this function returns 100, then the outstanding - /// shares for every single of those event outcome assets are 100. - /// - /// # Arguments - /// - /// * `poolid`: Id of the pool for which the outstanding shares shall be calculated. - /// * `num_assets`: The number of distinct outcome events. - /// * `subsidy`: The initial total subsidy gathered. - fn initial_outstanding_assets( - poolid: Self::PoolId, - num_assets: u32, - subsidy: Self::Balance, - ) -> Result { - let rikiddo = Self::get_rikiddo(&poolid)?; - let subsidy_fixed = Self::convert_balance_to_fixed(&subsidy)?; - - match rikiddo.initial_outstanding_assets(num_assets, subsidy_fixed) { - Ok(quantity) => Self::convert_fixed_to_balance(&quantity), - Err(err) => { - debug(&err); - Err(err.into()) - } - } - } - - /// Returns the price of one specific asset. - /// - /// # Arguments - /// - /// * `poolid`: The id of the asset pool for which all asset prices shall be calculated. - /// * `asset_in_question`: The balance of the asset for which the price should be returned. - /// * `asset_balances`: The balance vector of the assets. - fn price( - poolid: Self::PoolId, - asset_in_question: Self::Balance, - asset_balances: &[Self::Balance], - ) -> Result { - let rikiddo = Self::get_rikiddo(&poolid)?; - let balances_fixed = Self::convert_balance_to_fixed_vector(asset_balances)?; - let balance_in_question = Self::convert_balance_to_fixed(&asset_in_question)?; - - match rikiddo.price(&balances_fixed, &balance_in_question) { - Ok(price) => Self::convert_fixed_to_balance(&price), - Err(err) => { - debug(&err); - Err(err.into()) - } - } - } - - /// Update the market data by adding volume. - /// - /// # Arguments - /// - /// * `poolid`: The id of the asset pool for which all asset prices shall be calculated. - /// * `volume`: The volume that was traded in the pool with id `poolid`. - fn update_volume( - poolid: Self::PoolId, - volume: Self::Balance, - ) -> Result, DispatchError> { - // Convert to Fixed type - let timestamp: UnixTimestamp = T::Timestamp::now().into(); - let volume_fixed: Self::FU = Self::convert_balance_to_fixed(&volume)?; - - let timestamped_volume = TimestampedVolume { timestamp, volume: volume_fixed }; - let mut rikiddo = Self::get_rikiddo(&poolid)?; - - // Update rikiddo market data by adding the TimestampedVolume - let balance_fixed = match rikiddo.update_volume(×tamped_volume) { - Ok(Some(inner)) => inner, - Ok(None) => { - >::insert(poolid, rikiddo); - return Ok(None); - } - Err(err) => { - debug(&err); - return Err(err.into()); - } - }; - - // Convert result back into Balance type - let result = Self::convert_fixed_to_balance(&balance_fixed)?; - >::insert(poolid, rikiddo); - Ok(Some(result)) - } - } -} diff --git a/zrml/rikiddo/src/mock.rs b/zrml/rikiddo/src/mock.rs deleted file mode 100644 index 40a1431e3..000000000 --- a/zrml/rikiddo/src/mock.rs +++ /dev/null @@ -1,157 +0,0 @@ -// Copyright 2023-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 . - -#![allow( - // Mocks are only used for fuzzing and unit tests - clippy::arithmetic_side_effects -)] -#![cfg(feature = "mock")] -#![allow(missing_docs)] -#![doc(hidden)] - -use crate::types::{EmaMarketVolume, FeeSigmoid, RikiddoSigmoidMV}; -use frame_support::{construct_runtime, parameter_types, traits::Everything}; -use frame_system::mocking::MockBlock; -use sp_runtime::{ - traits::{BlakeTwo256, IdentityLookup}, - BuildStorage, -}; -use substrate_fixed::{types::extra::U33, FixedI128, FixedU128}; -use zeitgeist_primitives::{ - constants::mock::{BlockHashCount, ExistentialDeposit, MaxLocks, MaxReserves, BASE}, - types::{AccountIdTest, Balance, Hash, Moment, PoolId}, -}; - -pub const ALICE: AccountIdTest = 0; -pub const BOB: AccountIdTest = 1; -pub const CHARLIE: AccountIdTest = 2; -pub const DAVE: AccountIdTest = 3; -pub const EVE: AccountIdTest = 4; -pub const FRED: AccountIdTest = 5; - -parameter_types! { - pub const MinimumPeriod: u64 = 0; - pub const FractionalDecimalPlaces: u8 = 10; -} - -construct_runtime!( - pub enum Runtime { - Balances: pallet_balances, - Rikiddo: crate, - System: frame_system, - Timestamp: pallet_timestamp, - } -); - -impl crate::Config for Runtime { - type Timestamp = Timestamp; - type Balance = Balance; - type FixedTypeU = FixedU128; - type FixedTypeS = FixedI128; - type BalanceFractionalDecimals = FractionalDecimalPlaces; - type PoolId = PoolId; - type Rikiddo = RikiddoSigmoidMV< - Self::FixedTypeU, - Self::FixedTypeS, - FeeSigmoid, - EmaMarketVolume, - >; -} - -impl frame_system::Config for Runtime { - type AccountData = pallet_balances::AccountData; - type AccountId = AccountIdTest; - type BaseCallFilter = Everything; - type Block = MockBlock; - type BlockHashCount = BlockHashCount; - type BlockLength = (); - type BlockWeights = (); - type RuntimeCall = RuntimeCall; - type DbWeight = (); - type RuntimeEvent = RuntimeEvent; - type Hash = Hash; - type Hashing = BlakeTwo256; - type Lookup = IdentityLookup; - type Nonce = u64; - type MaxConsumers = frame_support::traits::ConstU32<16>; - type OnKilledAccount = (); - type OnNewAccount = (); - type RuntimeOrigin = RuntimeOrigin; - type PalletInfo = PalletInfo; - type SS58Prefix = (); - type SystemWeightInfo = (); - type Version = (); - type OnSetCode = (); -} - -impl pallet_balances::Config for Runtime { - type AccountStore = System; - type Balance = Balance; - type DustRemoval = (); - type FreezeIdentifier = (); - type RuntimeHoldReason = (); - type RuntimeEvent = RuntimeEvent; - type ExistentialDeposit = ExistentialDeposit; - type MaxHolds = (); - type MaxFreezes = (); - type MaxLocks = MaxLocks; - type MaxReserves = MaxReserves; - type ReserveIdentifier = [u8; 8]; - type WeightInfo = (); -} - -impl pallet_timestamp::Config for Runtime { - type MinimumPeriod = MinimumPeriod; - type Moment = Moment; - type OnTimestampSet = (); - type WeightInfo = (); -} - -pub struct ExtBuilder { - balances: Vec<(AccountIdTest, Balance)>, -} - -impl Default for ExtBuilder { - fn default() -> Self { - Self { - balances: vec![ - (ALICE, 1_000 * BASE), - (BOB, 1_000 * BASE), - (CHARLIE, 1_000 * BASE), - (DAVE, 1_000 * BASE), - (EVE, 1_000 * BASE), - (FRED, 1_000 * BASE), - ], - } - } -} - -impl ExtBuilder { - pub fn build(self) -> sp_io::TestExternalities { - let mut t = frame_system::GenesisConfig::::default().build_storage().unwrap(); - - // see the logs in tests when using `RUST_LOG=debug cargo test -- --nocapture` - let _ = env_logger::builder().is_test(true).try_init(); - - pallet_balances::GenesisConfig:: { balances: self.balances } - .assimilate_storage(&mut t) - .unwrap(); - - t.into() - } -} diff --git a/zrml/rikiddo/src/tests.rs b/zrml/rikiddo/src/tests.rs deleted file mode 100644 index 834c64ddd..000000000 --- a/zrml/rikiddo/src/tests.rs +++ /dev/null @@ -1,173 +0,0 @@ -// 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 . - -#![cfg(all(feature = "mock", test))] - -use frame_support::assert_err; -use substrate_fixed::{ - traits::ToFixed, - types::extra::{U1, U2, U3, U33, U7, U8}, - FixedI8, FixedU128, FixedU8, -}; - -use crate::{ - traits::{FromFixedDecimal, FromFixedToDecimal, IntoFixedDecimal, IntoFixedFromDecimal}, - utils::{convert_to_signed, convert_to_unsigned}, -}; - -mod ema_market_volume; -mod pallet; -mod rikiddo_sigmoid_mv; -mod sigmoid_fee; - -fn max_allowed_error(fractional_bits: u8) -> f64 { - 1.0 / (1u128 << (fractional_bits - 1)) as f64 -} - -#[test] -fn convert_signed_to_unsigned_fails() { - let num = >::from_num(-0.5f32); - assert_err!( - convert_to_unsigned::, FixedU8>(num), - "Cannot convert negative signed number into unsigned number" - ); -} - -#[test] -fn convert_number_does_not_fit_in_destination_type() { - let num = >::from_num(1); - assert_err!( - convert_to_signed::, FixedI8>(num), - "Fixed point conversion failed: FROM type does not fit in TO type" - ); -} - -#[test] -fn convert_unsigned_to_signed_returns_correct_result() -> Result<(), &'static str> { - // lossless - exact fit - let num1 = >::from_num(4.75); - let num1_converted: FixedI8 = convert_to_signed(num1)?; - assert_eq!(num1_converted, num1); - // lossy - loses fractional bits - let num2 = >::from_num(4.75); - let num2_converted: FixedI8 = convert_to_signed(num2)?; - assert_eq!(num2_converted.to_num::(), 4.5f32); - Ok(()) -} - -#[test] -fn convert_signed_to_unsigned_returns_correct_result() -> Result<(), &'static str> { - // lossless - exact fit - let num1 = >::from_num(4.75); - let num1_converted: FixedU8 = convert_to_unsigned(num1)?; - assert_eq!(num1_converted, num1); - // lossy - loses fractional bits - let num2 = >::from_num(4.75); - let num2_converted: FixedU8 = convert_to_unsigned(num2)?; - assert_eq!(num2_converted.to_num::(), 4.5f32); - Ok(()) -} - -#[test] -fn fixed_point_decimal_to_fixed_type_returns_correct_result() { - // This vector contains tuples of (fixed_point_decimal, fractional_decimal_places, correct_number) - let test_vector: Vec<(u128, u8, f64)> = vec![ - (0, 0, 0.0), - (10_000_000_000, 10, 1.0), - (1, 10, 0.000_000_000_1), - (123_456_789, 10, 0.012_345_678_9), - (9_999, 2, 99.99), - (736_101, 2, 7_361.01), - (133_733_333_333, 8, 1_337.333_333_33), - (1, 1, 0.1), - (55, 11, 0.000_000_000_6), // Rounding behavior - (34, 11, 0.000_000_000_3), // Rounding behavior - ]; - - for (fixed_point_decimal, places, expected) in test_vector.iter() { - let converted: FixedU128 = - fixed_point_decimal.to_fixed_from_fixed_decimal(*places).unwrap(); - assert_eq!(converted, >::from_num(*expected)); - } -} - -#[test] -fn fixed_point_decimal_from_fixed_type_returns_correct_result() { - // This vector contains tuples of (Fixed type, places, expected) - // The tuples tests every logical path - let test_vector: Vec<(FixedU128, u8, u128)> = vec![ - (32.5f64.to_fixed(), 0, 33), - (32.25f64.to_fixed(), 0, 32), - (200.to_fixed(), 8, 20_000_000_000), - (200.1234f64.to_fixed(), 8, 20_012_340_000), - (200.1234f64.to_fixed(), 2, 20_012), - (200.1254f64.to_fixed(), 2, 20_013), - (123.456f64.to_fixed(), 3, 123_456), - (123.to_fixed(), 0, 123), // No decimal places in float, and places = 0 - (0.to_fixed(), 0, 0), - ]; - - for (fixed, places, expected) in test_vector.iter() { - let converted: u128 = u128::from_fixed_to_fixed_decimal(*fixed, *places).unwrap(); - assert_eq!(converted, *expected); - } -} - -#[test] -fn fixed_type_to_fixed_point_decimal_returns_correct_result() { - // This vector contains tuples of (Fixed type, places, expected) - // The tuples tests every logical path - let test_vector: Vec<(FixedU128, u8, u128)> = vec![ - (32.5f64.to_fixed(), 0, 33), - (32.25f64.to_fixed(), 0, 32), - (200.to_fixed(), 8, 20_000_000_000), - (200.1234f64.to_fixed(), 8, 20_012_340_000), - (200.1234f64.to_fixed(), 2, 20_012), - (200.1254f64.to_fixed(), 2, 20_013), - (123.456f64.to_fixed(), 3, 123_456), - (123.to_fixed(), 0, 123), // No decimal places in float, and places = 0 - (0.to_fixed(), 0, 0), - ]; - - for (fixed, places, expected) in test_vector.iter() { - let converted: u128 = fixed.to_fixed_decimal(*places).unwrap(); - assert_eq!(converted, *expected); - } -} - -#[test] -fn fixed_type_from_fixed_point_decimal_returns_correct_result() { - // This vector contains tuples of (fixed_point_decimal, fractional_decimal_places, correct_number) - let test_vector: Vec<(u128, u8, f64)> = vec![ - (0, 0, 0.0), - (10_000_000_000, 10, 1.0), - (1, 10, 0.000_000_000_1), - (123_456_789, 10, 0.012_345_678_9), - (9_999, 2, 99.99), - (736_101, 2, 7_361.01), - (133_733_333_333, 8, 1_337.333_333_33), - (1, 1, 0.1), - (55, 11, 0.000_000_000_6), // Rounding behavior - (34, 11, 0.000_000_000_3), // Rounding behavior - ]; - - for (fixed_point_decimal, places, expected) in test_vector.iter() { - let converted = - >::from_fixed_decimal(*fixed_point_decimal, *places).unwrap(); - assert_eq!(converted, >::from_num(*expected)); - } -} diff --git a/zrml/rikiddo/src/tests/ema_market_volume.rs b/zrml/rikiddo/src/tests/ema_market_volume.rs deleted file mode 100644 index dde319ed4..000000000 --- a/zrml/rikiddo/src/tests/ema_market_volume.rs +++ /dev/null @@ -1,203 +0,0 @@ -// 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 super::max_allowed_error; -use crate::{ - traits::MarketAverage, - types::{EmaConfig, EmaMarketVolume, MarketVolumeState, Timespan, TimestampedVolume}, -}; -use frame_support::assert_err; -use substrate_fixed::{ - types::extra::{U64, U96}, - FixedU128, -}; - -pub(super) fn ema_create_test_struct( - period: u32, - smoothing: f64, -) -> EmaMarketVolume> { - let emv_cfg = EmaConfig::> { - ema_period: Timespan::Seconds(period), - ema_period_estimate_after: None, - smoothing: >::from_num(smoothing), - }; - - >>::new(emv_cfg) -} - -fn ema_get_multiplier(volumes_per_period: u64, smoothing: f64) -> f64 { - smoothing / (1 + volumes_per_period) as f64 -} - -fn ema_calculate(old_ema: f64, multiplier: f64, volume: f64) -> f64 { - volume * multiplier + old_ema * (1.0 - multiplier) -} - -#[test] -fn ema_state_transitions_work() { - let mut emv = ema_create_test_struct(2, 2.0); - assert_eq!(emv.state(), &MarketVolumeState::Uninitialized); - let _ = emv.update_volume(&TimestampedVolume { timestamp: 0, volume: 1u32.into() }).unwrap(); - assert_eq!(emv.state(), &MarketVolumeState::DataCollectionStarted); - let _ = emv.update_volume(&TimestampedVolume { timestamp: 3, volume: 1u32.into() }).unwrap(); - assert_eq!(emv.state(), &MarketVolumeState::DataCollected); -} - -#[test] -fn ema_returns_none_before_final_state() { - let mut emv = ema_create_test_struct(2, 2.0); - assert_eq!(emv.get(), None); - let _ = emv.update_volume(&TimestampedVolume { timestamp: 0, volume: 1u32.into() }).unwrap(); - assert_eq!(emv.get(), None); - let _ = emv.update_volume(&TimestampedVolume { timestamp: 3, volume: 1u32.into() }).unwrap(); - assert_ne!(emv.get(), None); -} - -#[test] -fn ema_returns_correct_ema() { - let mut emv = ema_create_test_struct(2, 2.0); - let _ = emv.update_volume(&TimestampedVolume { timestamp: 0, volume: 2u32.into() }).unwrap(); - let _ = emv.update_volume(&TimestampedVolume { timestamp: 1, volume: 6u32.into() }).unwrap(); - let _ = emv.update_volume(&TimestampedVolume { timestamp: 2, volume: 4u32.into() }).unwrap(); - // Currently it's a sma - let ema = emv.ema.to_num::(); - assert_eq!(ema, (2.0 + 6.0 + 4.0) / 3.0); - - let _ = emv.update_volume(&TimestampedVolume { timestamp: 3, volume: 20u32.into() }).unwrap(); - // Now it's an ema - let ema_fixed_f64: f64 = emv.ema.to_num(); - let multiplier = ema_get_multiplier(3, emv.config.smoothing.to_num()); - let ema_f64 = ema_calculate(ema, multiplier, 20f64); - let difference_abs = (ema_fixed_f64 - ema_f64).abs(); - assert!( - difference_abs <= max_allowed_error(64), - "\nFixed result: {}\nFloat result: {}\nDifference: {}\nMax_Allowed_Difference: {}", - ema_fixed_f64, - ema_f64, - difference_abs, - max_allowed_error(64) - ); - - // Repeat check using the get() function - let ema_fixed_f64: f64 = emv.get().unwrap().to_num(); - let difference_abs = (ema_fixed_f64 - ema_f64).abs(); - assert!( - difference_abs <= max_allowed_error(64), - "\nFixed result: {}\nFloat result: {}\nDifference: {}\nMax_Allowed_Difference: {}", - ema_fixed_f64, - ema_f64, - difference_abs, - max_allowed_error(64) - ); -} - -#[test] -fn ema_returns_correct_ema_after_estimated_period() { - let mut emv = EmaMarketVolume::new(EmaConfig::> { - ema_period: Timespan::Seconds(8), - ema_period_estimate_after: Some(Timespan::Seconds(2)), - smoothing: >::from_num(2.0), - }); - let _ = emv.update_volume(&TimestampedVolume { timestamp: 0, volume: 2u32.into() }).unwrap(); - let _ = emv.update_volume(&TimestampedVolume { timestamp: 1, volume: 6u32.into() }).unwrap(); - let _ = emv.update_volume(&TimestampedVolume { timestamp: 2, volume: 4u32.into() }).unwrap(); - // Currently it's a sma - let ema = emv.ema.to_num::(); - assert_eq!(ema, (2.0 + 6.0 + 4.0) / 3.0); - - let _ = emv.update_volume(&TimestampedVolume { timestamp: 3, volume: 20u32.into() }).unwrap(); - // Now it's an ema (using estimated transaction count per period) - let ema_fixed_f64: f64 = emv.ema.to_num(); - let extrapolation_factor = emv.config.ema_period.to_seconds() as f64 - / emv.config.ema_period_estimate_after.unwrap().to_seconds() as f64; - let multiplier = ema_get_multiplier( - (3f64 * extrapolation_factor).ceil() as u64, - emv.config.smoothing.to_num(), - ); - let ema_f64 = ema_calculate(ema, multiplier, 20f64); - let difference_abs = (ema_fixed_f64 - ema_f64).abs(); - assert!( - difference_abs <= max_allowed_error(64), - "\nFixed result: {}\nFloat result: {}\nDifference: {}\nMax_Allowed_Difference: {}", - ema_fixed_f64, - ema_f64, - difference_abs, - max_allowed_error(64) - ); -} - -#[test] -fn ema_clear_ereases_data() { - let mut emv = ema_create_test_struct(2, 2.0); - let _ = emv.update_volume(&TimestampedVolume { timestamp: 0, volume: 2u32.into() }).unwrap(); - let _ = emv.update_volume(&TimestampedVolume { timestamp: 3, volume: 6u32.into() }).unwrap(); - emv.clear(); - assert_eq!(emv.ema, >::from_num(0)); - assert_eq!(emv.multiplier(), &>::from_num(0)); - assert_eq!(emv.state(), &MarketVolumeState::Uninitialized); - assert_eq!(emv.start_time(), &0); - assert_eq!(emv.last_time(), &0); - assert_eq!(emv.volumes_per_period(), &0); -} - -#[test] -fn ema_added_volume_is_older_than_previous() { - let mut emv = ema_create_test_struct(2, 2.0); - let _ = emv.update_volume(&TimestampedVolume { timestamp: 2, volume: 2u32.into() }).unwrap(); - assert_err!( - emv.update_volume(&TimestampedVolume { timestamp: 1, volume: 2u32.into() }), - "[EmaMarketVolume] Incoming volume timestamp is older than previous timestamp" - ); -} - -#[test] -fn ema_overflow_sma_times_vpp() { - let mut emv = ema_create_test_struct(3, 2.0); - let _ = emv.update_volume(&TimestampedVolume { timestamp: 0, volume: 2u32.into() }).unwrap(); - let _ = emv.update_volume(&TimestampedVolume { timestamp: 1, volume: 6u32.into() }).unwrap(); - emv.ema = >::from_num(u64::MAX); - assert_err!( - emv.update_volume(&TimestampedVolume { timestamp: 3, volume: 6u32.into() }), - "[EmaMarketVolume] Overflow during calculation: sma * volumes_per_period" - ); -} - -#[test] -fn ema_overflow_sma_times_vpp_plus_volume() { - let mut emv = ema_create_test_struct(2, 2.0); - let _ = emv.update_volume(&TimestampedVolume { timestamp: 0, volume: 2u32.into() }).unwrap(); - let max_u64_fixed = >::from_num(u64::MAX); - assert_err!( - emv.update_volume(&TimestampedVolume { timestamp: 2, volume: max_u64_fixed }), - "[EmaMarketVolume] Overflow during calculation: sma * volumes_per_period + volume" - ); -} - -#[test] -fn ema_overflow_estimated_tx_per_period_does_not_fit() { - let mut emv = EmaMarketVolume::new(EmaConfig::> { - ema_period: Timespan::Hours(2_386_093), - ema_period_estimate_after: Some(Timespan::Seconds(0)), - smoothing: >::from_num(2.0), - }); - let _ = emv.update_volume(&TimestampedVolume { timestamp: 0, volume: 2u32.into() }).unwrap(); - let _ = emv.update_volume(&TimestampedVolume { timestamp: 0, volume: 2u32.into() }).unwrap(); - assert_err!( - emv.update_volume(&TimestampedVolume { timestamp: 1, volume: 6u32.into() }), - "[EmaMarketVolume] Overflow during estimation of transactions per period" - ); -} diff --git a/zrml/rikiddo/src/tests/pallet.rs b/zrml/rikiddo/src/tests/pallet.rs deleted file mode 100644 index c734068d2..000000000 --- a/zrml/rikiddo/src/tests/pallet.rs +++ /dev/null @@ -1,361 +0,0 @@ -// Copyright 2022 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 frame_support::{ - assert_noop, assert_ok, - traits::{OnFinalize, OnInitialize}, -}; -use frame_system::RawOrigin; - -type FixedS = ::FixedTypeS; -type Balance = ::Balance; -use crate::{ - mock::*, - tests::rikiddo_sigmoid_mv::{cost, initial_outstanding_assets, price}, - traits::{Fee, FromFixedDecimal, IntoFixedDecimal, RikiddoMVPallet}, - types::Timespan, - Config, -}; - -#[inline] -// Returns the maximum balance difference. If `frac_dec_places` is 10, and -// `max_percent_places_wrong` is 0.3, then the result is `10^3` = 1000 -fn max_balance_difference(frac_dec_places: u8, max_percent_places_wrong: f64) -> u128 { - 10u128.pow((frac_dec_places as f64 * max_percent_places_wrong).ceil() as u32) -} - -fn default_prepare_calculation() -> (u8, f64, Vec, Vec<::Balance>) { - let mut rikiddo = ::Rikiddo::default(); - let frac_dec_places = ::BalanceFractionalDecimals::get(); - let initial_fee: f64 = rikiddo.config.initial_fee.to_num(); - rikiddo.ma_short.config.ema_period = Timespan::Seconds(1); - rikiddo.ma_long.config.ema_period = Timespan::Seconds(1); - let asset_balances_f64 = vec![490f64, 510f64]; - let asset_balances: Vec<::Balance> = vec![ - asset_balances_f64[0] as u128 * 10u128.pow(frac_dec_places as u32), - asset_balances_f64[1] as u128 * 10u128.pow(frac_dec_places as u32), - ]; - assert_ok!(Rikiddo::create(0, rikiddo)); - (frac_dec_places, initial_fee, asset_balances_f64, asset_balances) -} - -// Adds volume for Timestamp 0 and 2 and returns the new sigmoid fee -fn default_fill_market_volume() -> f64 { - let frac_dec_places = ::BalanceFractionalDecimals::get(); - ::Timestamp::set(RawOrigin::None.into(), 0).unwrap(); - assert_ok!(Rikiddo::update_volume(0, 1000)); - run_to_block(1); - ::Timestamp::set(RawOrigin::None.into(), 2).unwrap(); - assert_eq!(Rikiddo::update_volume(0, 1000).unwrap(), Some(10u128.pow(frac_dec_places as u32))); - - let fee = Rikiddo::fee(0).unwrap(); - FixedS::from_fixed_decimal(fee, frac_dec_places).unwrap().to_num() -} - -fn run_to_block(n: u64) { - while System::block_number() < n { - Timestamp::on_finalize(System::block_number()); - Balances::on_finalize(System::block_number()); - Rikiddo::on_finalize(System::block_number()); - System::on_finalize(System::block_number()); - System::set_block_number(System::block_number() + 1); - System::on_initialize(System::block_number()); - Timestamp::on_initialize(System::block_number()); - Balances::on_initialize(System::block_number()); - Rikiddo::on_initialize(System::block_number()); - } -} - -#[test] -fn rikiddo_pallet_can_create_one_instance_per_pool() { - ExtBuilder::default().build().execute_with(|| { - let rikiddo = ::Rikiddo::default(); - assert_ok!(Rikiddo::create(0, rikiddo.clone())); - assert_noop!( - Rikiddo::create(0, rikiddo.clone()), - crate::Error::::RikiddoAlreadyExistsForPool - ); - assert_ok!(Rikiddo::create(1, rikiddo)); - }); -} - -#[test] -fn rikiddo_pallet_can_only_clear_existing_rikiddo_instances() { - ExtBuilder::default().build().execute_with(|| { - let rikiddo = ::Rikiddo::default(); - assert_noop!(Rikiddo::clear(0), crate::Error::::RikiddoNotFoundForPool); - assert_ok!(Rikiddo::create(0, rikiddo)); - assert_ok!(Rikiddo::clear(0)); - }); -} - -#[test] -fn rikiddo_pallet_can_only_destroy_existing_rikiddo_instances() { - ExtBuilder::default().build().execute_with(|| { - let rikiddo = ::Rikiddo::default(); - assert_noop!(Rikiddo::destroy(0), crate::Error::::RikiddoNotFoundForPool); - assert_ok!(Rikiddo::create(0, rikiddo)); - assert_ok!(Rikiddo::destroy(0)); - assert_noop!(Rikiddo::clear(0), crate::Error::::RikiddoNotFoundForPool); - }); -} - -#[test] -fn rikiddo_pallet_update_market_data_returns_correct_result() { - ExtBuilder::default().build().execute_with(|| { - let mut rikiddo = ::Rikiddo::default(); - rikiddo.ma_short.config.ema_period = Timespan::Seconds(1); - rikiddo.ma_long.config.ema_period = Timespan::Seconds(1); - assert_noop!( - Rikiddo::update_volume(0, 1000), - crate::Error::::RikiddoNotFoundForPool - ); - ::Timestamp::set(RawOrigin::None.into(), 0).unwrap(); - assert_ok!(Rikiddo::create(0, rikiddo)); - assert_ok!(Rikiddo::update_volume(0, 1000)); - run_to_block(1); - ::Timestamp::set(RawOrigin::None.into(), 2).unwrap(); - assert_eq!( - Rikiddo::update_volume(0, 1000).unwrap(), - Some(10u128.pow(::BalanceFractionalDecimals::get() as u32)) - ); - }); -} - -#[test] -fn rikiddo_pallet_fee_return_correct_result() { - ExtBuilder::default().build().execute_with(|| { - // First we check that the returned initial fee is correct - let mut rikiddo = ::Rikiddo::default(); - type FixedS = ::FixedTypeS; - type Balance = ::Balance; - let frac_dec_places = ::BalanceFractionalDecimals::get(); - let initial_fee: f64 = rikiddo.config.initial_fee.to_num(); - rikiddo.ma_short.config.ema_period = Timespan::Seconds(1); - rikiddo.ma_long.config.ema_period = Timespan::Seconds(1); - assert_noop!(Rikiddo::fee(0), crate::Error::::RikiddoNotFoundForPool); - ::Timestamp::set(RawOrigin::None.into(), 0).unwrap(); - assert_ok!(Rikiddo::create(0, rikiddo)); - let fee_reference_balance: Balance = - FixedS::from_num(initial_fee).to_fixed_decimal(frac_dec_places).unwrap(); - let fee_pallet_balance = Rikiddo::fee(0).unwrap(); - let difference_abs = - (fee_pallet_balance as i128 - fee_reference_balance as i128).unsigned_abs(); - let max_difference = max_balance_difference(frac_dec_places, 0.3); - assert!( - difference_abs <= max_difference, - "\nReference fee result (Balance): {}\nRikiddo pallet fee result (Balance): \ - {}\nDifference: {}\nMax_Allowed_Difference: {}", - fee_reference_balance, - fee_pallet_balance, - difference_abs, - max_difference, - ); - - // Now we check if the fee has changed, since enough volume data was collected - assert_ok!(Rikiddo::update_volume(0, 1000)); - run_to_block(1); - ::Timestamp::set(RawOrigin::None.into(), 2).unwrap(); - assert_ok!(Rikiddo::update_volume(0, 1000)); - assert_ne!(Rikiddo::fee(0).unwrap(), fee_pallet_balance); - }); -} - -#[test] -fn rikiddo_pallet_cost_returns_correct_result() { - ExtBuilder::default().build().execute_with(|| { - // The first part compares the result from the f64 reference cost function with - // what the pallet returns. It uses the initial fee. - let (frac_dec_places, initial_fee, asset_balances_f64, asset_balances) = - default_prepare_calculation(); - - let cost_pallet_balance = Rikiddo::cost(0, &asset_balances).unwrap(); - let cost_reference = cost(initial_fee, &asset_balances_f64); - let cost_reference_balance: Balance = - FixedS::from_num(cost_reference).to_fixed_decimal(frac_dec_places).unwrap(); - let difference_abs = - (cost_pallet_balance as i128 - cost_reference_balance as i128).unsigned_abs(); - let max_difference = max_balance_difference(frac_dec_places, 0.3); - assert!( - difference_abs <= max_difference, - "\nReference cost result (Balance): {}\nRikiddo pallet cost result (Balance): \ - {}\nDifference: {}\nMax_Allowed_Difference: {}", - cost_reference_balance, - cost_pallet_balance, - difference_abs, - max_difference, - ); - - // The second part also compares the cost results, but uses the sigmoid fee. - let fee = default_fill_market_volume(); - let cost_pallet_balance_with_fee = Rikiddo::cost(0, &asset_balances).unwrap(); - let cost_reference_with_fee = cost(fee, &asset_balances_f64); - let cost_reference_balance_with_fee: Balance = - FixedS::from_num(cost_reference_with_fee).to_fixed_decimal(frac_dec_places).unwrap(); - let difference_abs_with_fee = (cost_pallet_balance_with_fee as i128 - - cost_reference_balance_with_fee as i128) - .unsigned_abs(); - let max_difference_with_fee = max_balance_difference(frac_dec_places, 0.3); - assert!( - difference_abs_with_fee <= max_difference_with_fee, - "\nReference cost result (Balance): {}\nRikiddo pallet cost result (Balance): \ - {}\nDifference: {}\nMax_Allowed_Difference: {}", - cost_reference_balance_with_fee, - cost_pallet_balance_with_fee, - difference_abs_with_fee, - max_difference_with_fee, - ); - }); -} - -#[test] -fn rikiddo_pallet_initial_outstanding_assets_returns_correct_result() { - ExtBuilder::default().build().execute_with(|| { - let _ = default_prepare_calculation(); - let frac_places = Balance::from(::BalanceFractionalDecimals::get()); - let num_assets = 4u32; - let subsidy_f64 = 1000f64; - let subsidy = subsidy_f64 as u128 * frac_places.pow(10); - let fee: f64 = Rikiddo::get_rikiddo(&0).unwrap().fees.minimum_fee().to_num(); - let outstanding_assets = - Rikiddo::initial_outstanding_assets(0, num_assets, subsidy).unwrap(); - let outstanding_assets_shifted = - (outstanding_assets as f64) / (10f64.powf(frac_places as f64)); - let outstanding_assets_f64 = initial_outstanding_assets(num_assets, subsidy_f64, fee); - let difference_abs = (outstanding_assets_f64 - outstanding_assets_shifted).abs(); - assert!( - difference_abs <= 0.000001f64, - "\nFixed result: {}\nFloat result: {}\nDifference: {}\nMax_Allowed_Difference: {}", - outstanding_assets_shifted, - outstanding_assets_f64, - difference_abs, - 0.000001f64 - ); - }); -} - -#[test] -fn rikiddo_pallet_price_returns_correct_result() { - ExtBuilder::default().build().execute_with(|| { - // The first part compares the result from the f64 reference price function with - // what the pallet returns. It uses the initial fee. - let (frac_dec_places, initial_fee, asset_balances_f64, asset_balances) = - default_prepare_calculation(); - - let price_pallet_balance = Rikiddo::price(0, asset_balances[0], &asset_balances).unwrap(); - let price_reference = price(initial_fee, &asset_balances_f64, asset_balances_f64[0]); - let price_reference_balance: Balance = - FixedS::from_num(price_reference).to_fixed_decimal(frac_dec_places).unwrap(); - let difference_abs = - (price_reference_balance as i128 - price_pallet_balance as i128).unsigned_abs(); - let max_difference = max_balance_difference(frac_dec_places, 0.3); - assert!( - difference_abs <= max_difference, - "\nReference price result (Balance): {}\nRikiddo pallet price result (Balance): \ - {}\nDifference: {}\nMax_Allowed_Difference: {}", - price_reference_balance, - price_pallet_balance, - difference_abs, - max_difference, - ); - - // The second part also compares the price results, but uses the sigmoid fee. - let fee = default_fill_market_volume(); - let price_pallet_balance_fee = - Rikiddo::price(0, asset_balances[0], &asset_balances).unwrap(); - let price_reference_fee = price(fee, &asset_balances_f64, asset_balances_f64[0]); - let price_reference_balance_fee: Balance = - FixedS::from_num(price_reference_fee).to_fixed_decimal(frac_dec_places).unwrap(); - let difference_abs_fee = - (price_reference_balance_fee as i128 - price_pallet_balance_fee as i128).unsigned_abs(); - let max_difference_fee = max_balance_difference(frac_dec_places, 0.3); - assert!( - difference_abs_fee <= max_difference_fee, - "\nReference price result (Balance): {}\nRikiddo pallet price result (Balance): \ - {}\nDifference: {}\nMax_Allowed_Difference: {}", - price_reference_balance_fee, - price_pallet_balance_fee, - difference_abs_fee, - max_difference_fee, - ); - }); -} - -#[test] -fn rikiddo_pallet_all_prices_returns_correct_result() { - ExtBuilder::default().build().execute_with(|| { - // The first part compares the result from the f64 reference price function used - // on every asset, with what the pallet returns. It uses the initial fee. - let (frac_dec_places, initial_fee, asset_balances_f64, asset_balances) = - default_prepare_calculation(); - - let all_prices_pallet_balance = Rikiddo::all_prices(0, &asset_balances).unwrap(); - let all_prices_reference_balance: Vec = asset_balances_f64 - .iter() - .map(|e| { - let price_reference = price(initial_fee, &asset_balances_f64, *e); - FixedS::from_num(price_reference).to_fixed_decimal(frac_dec_places).unwrap() - }) - .collect(); - let difference_abs = all_prices_reference_balance - .iter() - .zip(all_prices_pallet_balance.iter()) - .fold(0u128, |acc, elems| acc + (*elems.0 as i128 - *elems.1 as i128).unsigned_abs()); - - let max_difference = - asset_balances.len() as u128 * max_balance_difference(frac_dec_places, 0.3); - assert!( - difference_abs <= max_difference, - "\nReference all_prices result (Balance): {:?}\nRikiddo all_prices result (Balance): \ - {:?}\nDifference: {}\nMax_Allowed_Difference: {}", - all_prices_reference_balance, - all_prices_pallet_balance, - difference_abs, - max_difference, - ); - - // The second part also compares the price results for all prices, but uses - // the sigmoid fee. - let fee = default_fill_market_volume(); - - let all_prices_pallet_balance_fee = Rikiddo::all_prices(0, &asset_balances).unwrap(); - let all_prices_reference_balance_fee: Vec = asset_balances_f64 - .iter() - .map(|e| { - let price_reference = price(fee, &asset_balances_f64, *e); - FixedS::from_num(price_reference).to_fixed_decimal(frac_dec_places).unwrap() - }) - .collect(); - let difference_abs_fee = all_prices_reference_balance_fee - .iter() - .zip(all_prices_pallet_balance_fee.iter()) - .fold(0u128, |acc, elems| acc + (*elems.0 as i128 - *elems.1 as i128).unsigned_abs()); - - let max_difference_fee = - asset_balances.len() as u128 * max_balance_difference(frac_dec_places, 0.3); - assert!( - difference_abs_fee <= max_difference_fee, - "\nReference all_prices result (Balance): {:?}\nRikiddo pallet all_prices result \ - (Balance): {:?}\nDifference: {}\nMax_Allowed_Difference: {}", - all_prices_reference_balance_fee, - all_prices_pallet_balance_fee, - difference_abs_fee, - max_difference_fee, - ); - }); -} diff --git a/zrml/rikiddo/src/tests/rikiddo_sigmoid_mv.rs b/zrml/rikiddo/src/tests/rikiddo_sigmoid_mv.rs deleted file mode 100644 index d31eeffcf..000000000 --- a/zrml/rikiddo/src/tests/rikiddo_sigmoid_mv.rs +++ /dev/null @@ -1,87 +0,0 @@ -// 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 substrate_fixed::{types::extra::U64, FixedI128, FixedU128}; - -use super::{ema_market_volume::ema_create_test_struct, max_allowed_error}; -use crate::types::{EmaMarketVolume, FeeSigmoid, RikiddoSigmoidMV}; - -mod cost; -mod fee; -mod market_volume; -mod misc; -mod price; - -type Rikiddo = RikiddoSigmoidMV< - FixedU128, - FixedI128, - FeeSigmoid>, - EmaMarketVolume>, ->; - -pub(super) fn initial_outstanding_assets(num_assets: u32, subsidy: f64, minimum_fee: f64) -> f64 { - let num_assets_f64: f64 = num_assets.into(); - let fee_times_num = minimum_fee * num_assets_f64; - subsidy / (fee_times_num * num_assets_f64.ln() + 1f64) -} - -fn ln_exp_sum(exponents: &[f64]) -> f64 { - exponents.iter().fold(0f64, |acc, val| acc + val.exp()).ln() -} - -pub(super) fn cost(fee: f64, balances: &[f64]) -> f64 { - let fee_times_sum = fee * balances.iter().sum::(); - let exponents = balances.iter().map(|e| e / fee_times_sum).collect::>(); - fee_times_sum * ln_exp_sum(&exponents) -} - -fn price_first_quotient(fee: f64, balances: &[f64], balance_in_question: f64) -> f64 { - let balance_sum = balances.iter().sum::(); - let fee_times_sum = fee * balance_sum; - let balance_exponential_results: Vec = - balances.iter().map(|qj| (qj / fee_times_sum).exp()).collect(); - let denominator: f64 = balance_exponential_results.iter().sum::() * balance_sum; - ((balance_in_question / fee_times_sum).exp() * balance_sum) / denominator -} - -fn price_second_quotient(fee: f64, balances: &[f64]) -> f64 { - let balance_sum = balances.iter().sum::(); - let fee_times_sum = fee * balance_sum; - let balance_exponential_results: Vec = - balances.iter().map(|qj| (qj / fee_times_sum).exp()).collect(); - let denominator: f64 = balance_exponential_results.iter().sum::() * balance_sum; - balance_exponential_results - .iter() - .enumerate() - .map(|(idx, val)| balances[idx] * val) - .sum::() - / denominator -} - -pub(super) fn price(fee: f64, balances: &[f64], balance_in_question: f64) -> f64 { - let balance_sum = balances.iter().sum::(); - let fee_times_sum = fee * balance_sum; - let balance_exponential_results: Vec = - balances.iter().map(|qj| (qj / fee_times_sum).exp()).collect(); - let left_from_addition = cost(fee, balances) / balance_sum; - let numerator_left_from_minus = (balance_in_question / fee_times_sum).exp() * balance_sum; - let numerator_right_from_minus: f64 = - balance_exponential_results.iter().enumerate().map(|(idx, val)| balances[idx] * val).sum(); - let numerator = numerator_left_from_minus - numerator_right_from_minus; - let denominator: f64 = balance_exponential_results.iter().sum::() * balance_sum; - left_from_addition + (numerator / denominator) -} diff --git a/zrml/rikiddo/src/tests/rikiddo_sigmoid_mv/cost.rs b/zrml/rikiddo/src/tests/rikiddo_sigmoid_mv/cost.rs deleted file mode 100644 index 953825267..000000000 --- a/zrml/rikiddo/src/tests/rikiddo_sigmoid_mv/cost.rs +++ /dev/null @@ -1,165 +0,0 @@ -// Copyright 2024 Forecasting Technologies LTD. -// Copyright 2021-2022, 2024 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 . - -#![allow(clippy::useless_vec)] - -use frame_support::assert_err; -use hashbrown::HashMap; -use substrate_fixed::{traits::ToFixed, types::extra::U64, FixedI128, FixedU128}; - -use super::{cost, max_allowed_error, Rikiddo}; -use crate::{traits::Lmsr, types::RikiddoFormulaComponents, utils::convert_to_signed}; - -#[test] -fn rikiddo_cost_function_rejects_empty_list() { - let rikiddo = Rikiddo::default(); - assert_err!(rikiddo.cost(&[]), "[RikiddoSigmoidMV] No asset balances provided"); -} - -#[test] -fn rikiddo_cost_function_overflow_during_summation_of_balances() { - let rikiddo = Rikiddo::default(); - let exponent = >::from_num(u64::MAX); - let param = vec![exponent, exponent]; - assert_err!( - rikiddo.cost(¶m), - "[RikiddoSigmoidMV] Overflow during summation of asset balances" - ); -} - -#[test] -fn rikiddo_cost_function_overflow_during_fee_times_balance_sum() { - let mut rikiddo = Rikiddo::default(); - rikiddo.config.initial_fee = >::from_num(i64::MAX); - let param = >::from_num(i64::MAX); - assert_err!( - rikiddo.cost(&[param]), - "[RikiddoSigmoidMV] Overflow during calculation: fee * total_asset_balance" - ); -} - -#[test] -fn rikiddo_cost_function_overflow_during_calculation_of_exponent() { - let mut rikiddo = Rikiddo::default(); - rikiddo.config.initial_fee = - >::from_bits(0x0000_0000_0000_0000_0000_0000_0000_0001); - let param = >::from_num(i64::MAX); - assert_err!( - rikiddo.cost(&[param]), - "[RikiddoSigmoidMV] Overflow during calculation: expontent_i = asset_balance_i / \ - denominator" - ); -} - -#[test] -fn rikiddo_cost_function_overflow_during_calculation_of_result() { - let mut rikiddo = Rikiddo::default(); - rikiddo.config.initial_fee = >::from_num(1); - rikiddo.config.log2_e = >::from_num(i64::MAX >> 1); - let param = >::from_num(i64::MAX as u64 >> 1); - assert_err!( - rikiddo.cost(&[param, param]), - "[RikiddoSigmoidMV] Overflow during calculation: fee * total_asset_balance * \ - ln(sum_i(e^i))" - ); -} - -#[test] -fn rikiddo_cost_function_correct_result() -> Result<(), &'static str> { - let mut rikiddo = Rikiddo::default(); - // Evaluate the cost using the optimized strategy - let balance0 = 3.5f64; - let balance1 = 3.6f64; - let balance2 = 3.7f64; - let param_f64 = vec![balance0, balance1, balance2]; - let param_fixed = vec![ - >::from_num(balance0), - >::from_num(balance1), - >::from_num(balance2), - ]; - let mut result_fixed = rikiddo.cost(¶m_fixed)?; - let mut result_f64: f64 = cost(rikiddo.config.initial_fee.to_num(), ¶m_f64); - let mut result_fixed_f64: f64 = result_fixed.to_num(); - let mut difference_abs = (result_f64 - result_fixed_f64).abs(); - assert!( - difference_abs <= 0.0000001f64, - "\nFixed result: {}\nFloat result: {}\nDifference: {}\nMax_Allowed_Difference: {}", - result_fixed_f64, - result_f64, - difference_abs, - 0.0000001f64 - ); - - // Evaluate the cost using the default strategy - rikiddo.config.initial_fee = 0.1.to_fixed(); - result_f64 = cost(rikiddo.config.initial_fee.to_num(), ¶m_f64); - result_fixed = rikiddo.cost(¶m_fixed)?; - result_fixed_f64 = result_fixed.to_num(); - difference_abs = (result_f64 - result_fixed_f64).abs(); - assert!( - difference_abs <= 0.0000001f64, - "\nFixed result: {}\nFloat result: {}\nDifference: {}\nMax_Allowed_Difference: {}", - result_fixed_f64, - result_f64, - difference_abs, - 0.0000001f64 - ); - Ok(()) -} - -#[test] -fn rikiddo_cost_helper_does_set_all_values() -> Result<(), &'static str> { - let rikiddo = Rikiddo::default(); - let param = >::from_num(1); - let mut formula_components = RikiddoFormulaComponents::default(); - let _ = rikiddo.cost_with_forumla(&[param, param], &mut formula_components, true, true)?; - let zero: FixedI128 = 0.to_fixed(); - assert_ne!(formula_components.one, zero); - assert_ne!(formula_components.fee, zero); - assert_ne!(formula_components.sum_balances, zero); - assert_ne!(formula_components.sum_times_fee, zero); - assert_ne!(formula_components.emax, zero); - assert_ne!(formula_components.sum_exp, zero); - assert_ne!(formula_components.exponents, HashMap::new()); - assert_ne!(formula_components.reduced_exponential_results, HashMap::new()); - Ok(()) -} - -#[test] -fn rikiddo_cost_helper_does_return_cost_minus_sum_quantities() -> Result<(), &'static str> { - let rikiddo = Rikiddo::default(); - let param = >::from_num(1); - let mut formula_components = RikiddoFormulaComponents::default(); - let quantities = &vec![param, param]; - let cost_without_sum_quantities = - rikiddo.cost_with_forumla(quantities, &mut formula_components, true, false)?; - let cost_from_price_formula_times_sum_quantities = - cost_without_sum_quantities * formula_components.sum_balances; - let cost: FixedI128 = convert_to_signed(rikiddo.cost(quantities)?)?; - let difference_abs = (cost - cost_from_price_formula_times_sum_quantities).abs(); - assert!( - difference_abs <= max_allowed_error(64), - "\nDirect cost result: {}\nReconstructed cost result: {}\nDifference: \ - {}\nMax_Allowed_Difference: {}", - cost, - cost_from_price_formula_times_sum_quantities, - difference_abs, - max_allowed_error(64) - ); - Ok(()) -} diff --git a/zrml/rikiddo/src/tests/rikiddo_sigmoid_mv/fee.rs b/zrml/rikiddo/src/tests/rikiddo_sigmoid_mv/fee.rs deleted file mode 100644 index 97c5da498..000000000 --- a/zrml/rikiddo/src/tests/rikiddo_sigmoid_mv/fee.rs +++ /dev/null @@ -1,87 +0,0 @@ -// 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 frame_support::assert_err; -use substrate_fixed::{types::extra::U64, FixedU128}; - -use super::{ema_create_test_struct, Rikiddo}; -use crate::{ - traits::{Lmsr, MarketAverage, RikiddoMV}, - types::{FeeSigmoid, RikiddoConfig, TimestampedVolume}, -}; - -#[test] -fn rikiddo_get_fee_catches_zero_divison() { - let emv = ema_create_test_struct(1, 0.0); - let mut rikiddo = - Rikiddo::new(RikiddoConfig::default(), FeeSigmoid::default(), emv.clone(), emv); - let _ = rikiddo.update_volume(&TimestampedVolume { timestamp: 0, volume: 0u32.into() }); - let _ = rikiddo.update_volume(&TimestampedVolume { timestamp: 2, volume: 0u32.into() }); - assert_err!( - rikiddo.fee(), - "[RikiddoSigmoidMV] Zero division error during calculation: ma_short / ma_long" - ); -} - -#[test] -fn rikiddo_get_fee_overflows_during_ratio_calculation() { - let emv = ema_create_test_struct(1, 2.0); - let mut rikiddo = - Rikiddo::new(RikiddoConfig::default(), FeeSigmoid::default(), emv.clone(), emv); - let _ = rikiddo.update_volume(&TimestampedVolume { timestamp: 0, volume: 0u32.into() }); - let _ = rikiddo.update_volume(&TimestampedVolume { timestamp: 2, volume: 0u32.into() }); - rikiddo.ma_short.ema = >::from_num(u64::MAX); - rikiddo.ma_long.ema = >::from_num(0.1f64); - assert_err!( - rikiddo.fee(), - "[RikiddoSigmoidMV] Overflow during calculation: ma_short / ma_long" - ); -} - -#[test] -fn rikiddo_get_fee_ratio_does_not_fit_in_type() { - let emv = ema_create_test_struct(1, 2.0); - let mut rikiddo = - Rikiddo::new(RikiddoConfig::default(), FeeSigmoid::default(), emv.clone(), emv); - let _ = rikiddo.update_volume(&TimestampedVolume { timestamp: 0, volume: 0u32.into() }); - let _ = rikiddo.update_volume(&TimestampedVolume { timestamp: 2, volume: 0u32.into() }); - rikiddo.ma_short.ema = >::from_num(u64::MAX); - rikiddo.ma_long.ema = >::from_num(1u64); - assert_err!(rikiddo.fee(), "Fixed point conversion failed: FROM type does not fit in TO type"); -} - -#[test] -fn rikiddo_get_fee_returns_the_correct_result() { - let emv_short = ema_create_test_struct(1, 2.0); - let emv_long = ema_create_test_struct(2, 2.0); - let mut rikiddo = - Rikiddo::new(RikiddoConfig::default(), FeeSigmoid::default(), emv_short, emv_long); - assert_eq!(rikiddo.ma_short.get(), None); - assert_eq!(rikiddo.ma_long.get(), None); - assert_eq!(rikiddo.fee().unwrap(), rikiddo.config.initial_fee); - let _ = rikiddo.update_volume(&TimestampedVolume { timestamp: 0, volume: 100u32.into() }); - let _ = rikiddo.update_volume(&TimestampedVolume { timestamp: 2, volume: 100u32.into() }); - assert_ne!(rikiddo.ma_short.get(), None); - assert_eq!(rikiddo.ma_long.get(), None); - assert_eq!(rikiddo.fee().unwrap(), rikiddo.config.initial_fee); - let _ = rikiddo.update_volume(&TimestampedVolume { timestamp: 3, volume: 100u32.into() }); - assert_ne!(rikiddo.ma_short.get(), None); - assert_ne!(rikiddo.ma_long.get(), None); - // We don't want to test the exact result (that is the responsibility of the fee module), - // but rather if rikiddo toggles properly between initial fee and the calculated fee - assert_ne!(rikiddo.fee().unwrap(), rikiddo.config.initial_fee); -} diff --git a/zrml/rikiddo/src/tests/rikiddo_sigmoid_mv/market_volume.rs b/zrml/rikiddo/src/tests/rikiddo_sigmoid_mv/market_volume.rs deleted file mode 100644 index cfb81e40d..000000000 --- a/zrml/rikiddo/src/tests/rikiddo_sigmoid_mv/market_volume.rs +++ /dev/null @@ -1,52 +0,0 @@ -// 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 substrate_fixed::{types::extra::U64, FixedU128}; - -use super::{ema_create_test_struct, Rikiddo}; -use crate::{ - traits::RikiddoMV, - types::{FeeSigmoid, RikiddoConfig, TimestampedVolume}, -}; - -#[test] -fn rikiddo_updates_mv_and_returns_some() { - let emv = ema_create_test_struct(1, 2.0); - let mut rikiddo = - Rikiddo::new(RikiddoConfig::default(), FeeSigmoid::default(), emv.clone(), emv); - rikiddo.update_volume(&TimestampedVolume { timestamp: 0, volume: 1u32.into() }).unwrap(); - let res = - rikiddo.update_volume(&TimestampedVolume { timestamp: 2, volume: 2u32.into() }).unwrap(); - assert_eq!(res, Some(1u32.into())); -} - -#[test] -fn rikiddo_updates_mv_and_returns_none() { - let mut rikiddo = Rikiddo::default(); - let vol = TimestampedVolume::default(); - assert_eq!(rikiddo.update_volume(&vol).unwrap(), None); -} - -#[test] -fn rikiddo_clear_clears_market_data() { - let mut rikiddo = Rikiddo::default(); - let rikiddo_clone = rikiddo.clone(); - let _ = rikiddo.update_volume(&>>::default()); - assert_ne!(rikiddo, rikiddo_clone); - rikiddo.clear(); - assert_eq!(rikiddo, rikiddo_clone); -} diff --git a/zrml/rikiddo/src/tests/rikiddo_sigmoid_mv/misc.rs b/zrml/rikiddo/src/tests/rikiddo_sigmoid_mv/misc.rs deleted file mode 100644 index a11438f4a..000000000 --- a/zrml/rikiddo/src/tests/rikiddo_sigmoid_mv/misc.rs +++ /dev/null @@ -1,147 +0,0 @@ -// Copyright 2024 Forecasting Technologies LTD. -// Copyright 2021-2022, 2024 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 . - -#![allow(clippy::useless_vec)] - -use frame_support::assert_err; -use substrate_fixed::{traits::ToFixed, types::extra::U64, FixedI128}; - -use super::{initial_outstanding_assets, ln_exp_sum, Rikiddo}; -use crate::{ - constants::INITIAL_FEE, - traits::{Fee, Lmsr}, - types::{EmaMarketVolume, FeeSigmoid, RikiddoConfig, RikiddoFormulaComponents}, - utils::convert_to_signed, -}; - -#[test] -fn rikiddo_default_does_not_panic() -> Result<(), &'static str> { - let default_rikiddo = Rikiddo::default(); - let rikiddo = Rikiddo::new( - RikiddoConfig::new(convert_to_signed(INITIAL_FEE)?), - FeeSigmoid::default(), - EmaMarketVolume::default(), - EmaMarketVolume::default(), - ); - assert_eq!(default_rikiddo, rikiddo); - Ok(()) -} - -#[test] -fn rikiddo_initial_outstanding_assets_returns_correct_result() { - let rikiddo = Rikiddo::default(); - let num_assets = 4u32; - let subsidy = 1000u32; - let outstanding_assets = - rikiddo.initial_outstanding_assets(num_assets, subsidy.to_fixed()).unwrap(); - let outstanding_assets_f64 = - initial_outstanding_assets(num_assets, subsidy.into(), rikiddo.fees.minimum_fee().to_num()); - let difference_abs = (outstanding_assets_f64 - outstanding_assets.to_num::()).abs(); - assert!( - difference_abs <= 0.000001f64, - "\nFixed result: {}\nFloat result: {}\nDifference: {}\nMax_Allowed_Difference: {}", - outstanding_assets, - outstanding_assets_f64, - difference_abs, - 0.000001f64 - ); -} - -#[test] -fn rikiddo_optimized_ln_sum_exp_strategy_exponent_subtract_overflow() { - let rikiddo = Rikiddo::default(); - let param = vec![>::from_num(1i64 << 63)]; - assert_err!( - rikiddo.optimized_cost_strategy( - ¶m, - &>::from_num(1i64 << 62), - &mut RikiddoFormulaComponents::default(), - false - ), - "[RikiddoSigmoidFee] Overflow during calculation: current_exponent - biggest_exponent" - ); -} - -#[test] -fn rikiddo_optimized_ln_sum_exp_strategy_sum_exp_i_overflow() { - let rikiddo = Rikiddo::default(); - let exponent = >::from_num(42.7f64); - let param = vec![exponent, exponent, exponent]; - assert_err!( - rikiddo.optimized_cost_strategy( - ¶m, - &>::from_num(0), - &mut RikiddoFormulaComponents::default(), - false - ), - "[RikiddoSigmoidFee] Overflow during calculation: sum_i(e^(i - biggest_exponent))" - ); -} - -#[test] -fn rikiddo_optimized_ln_sum_exp_strategy_result_overflow() { - let rikiddo = Rikiddo::default(); - let biggest_exponent = >::from_num(i64::MAX); - let exponent = biggest_exponent - >::from_num(0.0000001f64); - let param = vec![exponent, exponent, exponent]; - assert_err!( - rikiddo.optimized_cost_strategy( - ¶m, - &biggest_exponent, - &mut RikiddoFormulaComponents::default(), - false - ), - "[RikiddoSigmoidMV] Overflow during calculation: biggest_exponent + ln(exp_sum) \ - (optimized)" - ); -} - -#[test] -fn rikiddo_ln_sum_exp_strategies_return_correct_results() -> Result<(), &'static str> { - let rikiddo = Rikiddo::default(); - let exponent0 = 3.5f64; - let exponent1 = 4.5f64; - let exponent2 = 5.5f64; - let param_f64 = vec![exponent0, exponent1, exponent2]; - let param_fixed = vec![ - >::from_num(exponent0), - >::from_num(exponent1), - >::from_num(exponent2), - ]; - // Evaluate the result of the opimized cost strategy - let result_fixed = rikiddo.optimized_cost_strategy( - ¶m_fixed, - ¶m_fixed[2], - &mut RikiddoFormulaComponents::default(), - false, - )?; - let result_f64: f64 = ln_exp_sum(¶m_f64); - let result_fixed_f64: f64 = result_fixed.to_num(); - let difference_abs = (result_f64 - result_fixed_f64).abs(); - // The fixed calculation seems to be quite errorneous: Difference = 0.00000007886511177446209 - assert!( - difference_abs <= 0.000001f64, - "\nFixed result: {}\nFloat result: {}\nDifference: {}\nMax_Allowed_Difference: {}", - result_fixed_f64, - result_f64, - difference_abs, - 0.000001f64 - ); - - Ok(()) -} diff --git a/zrml/rikiddo/src/tests/rikiddo_sigmoid_mv/price.rs b/zrml/rikiddo/src/tests/rikiddo_sigmoid_mv/price.rs deleted file mode 100644 index 07cb4b0b5..000000000 --- a/zrml/rikiddo/src/tests/rikiddo_sigmoid_mv/price.rs +++ /dev/null @@ -1,259 +0,0 @@ -// 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 . - -#![allow(clippy::needless_late_init)] -#![allow(clippy::useless_vec)] - -use frame_support::assert_err; -use substrate_fixed::{traits::ToFixed, types::extra::U64, FixedI128, FixedU128}; - -use super::{max_allowed_error, price_first_quotient, Rikiddo}; -use crate::{ - tests::rikiddo_sigmoid_mv::{price, price_second_quotient}, - traits::Lmsr, - types::RikiddoFormulaComponents, -}; - -fn check_if_exponent_in_formula_components(helper: u8) { - let rikiddo = Rikiddo::default(); - let param = vec![>::from_num(100)]; - let result = { - if helper == 1 { - rikiddo.price_helper_first_quotient( - ¶m, - ¶m[0], - &RikiddoFormulaComponents::default(), - ) - } else { - rikiddo.price_helper_second_quotient(¶m, &RikiddoFormulaComponents::default()) - } - }; - assert_err!( - result, - "[RikiddoSigmoidMV] Cannot find exponent of asset balance in question in \ - RikiddoFormulaComponents HashMap" - ); -} - -fn check_price_helper_result(helper: u8) -> Result<(), &'static str> { - let rikiddo = Rikiddo::default(); - let formula_components = &mut >>::default(); - let param_f64 = vec![520.19, 480.81]; - let param = - vec![>::from_num(param_f64[0]), >::from_num(param_f64[1])]; - let param_u = - vec![>::from_num(param_f64[0]), >::from_num(param_f64[1])]; - // This fills the formula_components with the correct values - let _ = rikiddo.cost_with_forumla(¶m_u, formula_components, true, true); - let rikiddo_price; - let rikiddo_price_f64; - let error_msg_function; - - if helper == 1 { - rikiddo_price = - rikiddo.price_helper_first_quotient(¶m, ¶m[0], formula_components)?; - rikiddo_price_f64 = - price_first_quotient(rikiddo.config.initial_fee.to_num(), ¶m_f64, param_f64[0]); - error_msg_function = "price_helper_first_quotient" - } else { - rikiddo_price = rikiddo.price_helper_second_quotient(¶m, formula_components)?; - rikiddo_price_f64 = price_second_quotient(rikiddo.config.initial_fee.to_num(), ¶m_f64); - error_msg_function = "price_helper_second_quotient" - } - let difference_abs = (rikiddo_price.to_num::() - rikiddo_price_f64).abs(); - assert!( - difference_abs <= max_allowed_error(63), - "\n{} result (fixed): {}\n{} result (f64): {}\nDifference: {}\nMax_Allowed_Difference: {}", - error_msg_function, - rikiddo_price, - error_msg_function, - rikiddo_price_f64, - difference_abs, - max_allowed_error(64) - ); - Ok(()) -} - -// --- Tests for price_helper_first_quotient --- - -#[test] -fn rikiddo_price_helper_first_quotient_exponent_not_found() { - check_if_exponent_in_formula_components(1) -} - -#[test] -fn rikiddo_price_helper_first_quotient_overflow_exponent_sub_exp_qj() { - let rikiddo = Rikiddo::default(); - let formula_components = &mut >>::default(); - let param = vec![>::from(i64::MAX), >::from(-i64::MAX >> 5)]; - formula_components.exponents.insert(param[0], i64::MAX.to_fixed()); - formula_components.exponents.insert(param[1], >::from(-i64::MAX >> 5)); - formula_components.sum_times_fee = 0.1.to_fixed(); - assert_err!( - rikiddo.price_helper_first_quotient(¶m, ¶m[0], formula_components), - "[RikiddoSigmoidMV] Overflow during calculation: exponent - exponent_balance_in_question" - ); -} - -#[test] -fn rikiddo_price_helper_first_quotient_returns_correct_result() -> Result<(), &'static str> { - check_price_helper_result(1) -} - -// --- Tests for price_helper_second_quotient --- - -#[test] -fn rikiddo_price_helper_second_quotient_exponent_not_found() { - check_if_exponent_in_formula_components(2) -} - -#[test] -fn rikiddo_price_helper_second_quotient_reduced_exp_not_found() { - let rikiddo = Rikiddo::default(); - let param = vec![>::from_num(100)]; - let formula_components = &mut >>::default(); - formula_components.exponents.insert(param[0], 1.to_fixed()); - assert_err!( - rikiddo.price_helper_second_quotient(¶m, formula_components), - "[RikiddoSigmoidMV] Cannot find reduced exponential result of current element" - ); -} - -#[test] -fn rikiddo_price_helper_second_quotient_overflow_elem_times_reduced_exp() { - let rikiddo = Rikiddo::default(); - let param = vec![>::from_num(i64::MAX)]; - let formula_components = &mut >>::default(); - formula_components.exponents.insert(param[0], 1.to_fixed()); - formula_components.reduced_exponential_results.insert(1.to_fixed(), 2.to_fixed()); - assert_err!( - rikiddo.price_helper_second_quotient(¶m, formula_components), - "[RikiddoSigmoidMV] Overflow during calculation: element * reduced_exponential_result" - ); -} - -#[test] -fn rikiddo_price_helper_second_quotient_overflow_sum_j_plus_elem_time_reduced_exp() { - let rikiddo = Rikiddo::default(); - let param = vec![>::from_num(i64::MAX), >::from_num(1)]; - let formula_components = &mut >>::default(); - formula_components.exponents.insert(param[0], 1.to_fixed()); - formula_components.exponents.insert(param[1], 1.to_fixed()); - formula_components.reduced_exponential_results.insert(1.to_fixed(), 1.to_fixed()); - assert_err!( - rikiddo.price_helper_second_quotient(¶m, formula_components), - "[RikiddoSigmoidMV] Overflow during calculation: sum_j += \ - elem_times_reduced_exponential_result" - ); -} - -#[test] -fn rikiddo_price_helper_second_quotient_overflow_sum_balances_times_sum_exp() { - let rikiddo = Rikiddo::default(); - let param = vec![>::from_num(1)]; - let formula_components = &mut >>::default(); - formula_components.exponents.insert(param[0], 1.to_fixed()); - formula_components.reduced_exponential_results.insert(param[0], 1.to_fixed()); - formula_components.sum_exp = 2.to_fixed(); - formula_components.sum_balances = >::from_num(i64::MAX); - assert_err!( - rikiddo.price_helper_second_quotient(¶m, formula_components), - "[RikiddoSigmoidMV] Overflow during calculation: sum_balances * sum_exp" - ); -} - -#[test] -fn rikiddo_price_helper_second_quotient_overflow_numerator_div_denominator() { - let rikiddo = Rikiddo::default(); - let param = vec![>::from_num(1)]; - let formula_components = &mut >>::default(); - formula_components.exponents.insert(param[0], 1.to_fixed()); - formula_components.reduced_exponential_results.insert(param[0], 1.to_fixed()); - formula_components.sum_balances = 1.to_fixed(); - // The following parameter will lead to a zero division error - formula_components.sum_exp = 0.to_fixed(); - assert_err!( - rikiddo.price_helper_second_quotient(¶m, formula_components), - "[RikiddoSigmoidMV] Overflow during calculation (price helper 2): numerator / denominator" - ); -} - -#[test] -fn rikiddo_price_helper_second_quotient_returns_correct_result() -> Result<(), &'static str> { - check_price_helper_result(2) -} - -// --- Tests for price --- -#[test] -fn rikiddo_price_asset_in_question_not_contained_in_balance_array() { - let rikiddo = Rikiddo::default(); - let param = vec![>::from_num(1)]; - assert_err!( - rikiddo.price(¶m, &2.to_fixed()), - "[RikiddoSigmoidMV] asset_in_question_balance not found in asset_balances" - ) -} - -#[test] -fn rikiddo_price_returns_correct_result() -> Result<(), &'static str> { - let rikiddo = Rikiddo::default(); - let param_f64 = vec![520.19, 500.00, 480.81]; - let param: Vec> = - vec![param_f64[0].to_fixed(), param_f64[1].to_fixed(), param_f64[2].to_fixed()]; - let rikiddo_price = rikiddo.price(¶m, ¶m[1])?; - let rikiddo_price_f64 = price(rikiddo.config.initial_fee.to_num(), ¶m_f64, param_f64[1]); - let difference_abs = (rikiddo_price.to_num::() - rikiddo_price_f64).abs(); - assert!( - difference_abs <= max_allowed_error(34), - "\nRikiddo price result (fixed): {}\nRikiddo price result (f64): {}\nDifference: \ - {}\nMax_Allowed_Difference: {}", - rikiddo_price, - rikiddo_price_f64, - difference_abs, - max_allowed_error(64) - ); - Ok(()) -} - -#[test] -fn rikiddo_all_prices_returns_correct_result() -> Result<(), &'static str> { - let rikiddo = Rikiddo::default(); - let param_f64 = vec![520.19, 500.00, 480.81]; - let param: Vec> = - vec![param_f64[0].to_fixed(), param_f64[1].to_fixed(), param_f64[2].to_fixed()]; - let rikiddo_prices = rikiddo.all_prices(¶m)?; - let rikiddo_prices_f64 = [ - price(rikiddo.config.initial_fee.to_num(), ¶m_f64, param_f64[0]), - price(rikiddo.config.initial_fee.to_num(), ¶m_f64, param_f64[1]), - price(rikiddo.config.initial_fee.to_num(), ¶m_f64, param_f64[2]), - ]; - for (idx, price) in rikiddo_prices.iter().enumerate() { - let difference_abs = (price.to_num::() - rikiddo_prices_f64[idx]).abs(); - assert!( - difference_abs <= max_allowed_error(34), - "\nRikiddo price result (fixed): {}\nRikiddo price result (f64): {}\nDifference: \ - {}\nMax_Allowed_Difference: {}", - price, - rikiddo_prices_f64[idx], - difference_abs, - max_allowed_error(64) - ); - } - - Ok(()) -} diff --git a/zrml/rikiddo/src/tests/sigmoid_fee.rs b/zrml/rikiddo/src/tests/sigmoid_fee.rs deleted file mode 100644 index 4106388e8..000000000 --- a/zrml/rikiddo/src/tests/sigmoid_fee.rs +++ /dev/null @@ -1,119 +0,0 @@ -// 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 super::max_allowed_error; -use crate::{ - traits::Fee, - types::{FeeSigmoid, FeeSigmoidConfig}, -}; -use frame_support::assert_err; -use substrate_fixed::{types::extra::U64, FixedI128}; - -fn sigmoid_fee(m: f64, n: f64, p: f64, r: f64) -> f64 { - (m * (r - n)) / (p + (r - n).powi(2)).sqrt() -} - -fn init_default_sigmoid_fee_struct() -> (FeeSigmoid>, f64, f64, f64) { - let m = 0.01f64; - let n = 0f64; - let p = 2.0f64; - let initial_fee = 0.005; - let min_revenue = 0.0035; - - let config = FeeSigmoidConfig { - m: >::from_num(m), - n: >::from_num(n), - p: >::from_num(p), - initial_fee: >::from_num(initial_fee), - min_revenue: >::from_num(min_revenue), - }; - - let fee = FeeSigmoid { config }; - (fee, m, n, p) -} - -#[test] -fn fee_sigmoid_overflow_r_minus_n() { - let (mut fee, _, _, _) = init_default_sigmoid_fee_struct(); - let r = >::from_num(i64::MIN); - fee.config.n = >::from_num(i64::MAX); - assert_err!(fee.calculate_fee(r), "[FeeSigmoid] Overflow during calculation: r - n"); -} - -#[test] -fn fee_sigmoid_overflow_m_times_r_minus_n() { - let (mut fee, _, _, _) = init_default_sigmoid_fee_struct(); - let r = >::from_num(i64::MIN); - fee.config.n = >::from_num(0); - fee.config.m = >::from_num(i64::MAX); - assert_err!(fee.calculate_fee(r), "[FeeSigmoid] Overflow during calculation: m * (r-n)"); -} - -#[test] -fn fee_sigmoid_overflow_r_minus_n_squared() { - let (mut fee, _, _, _) = init_default_sigmoid_fee_struct(); - let r = >::from_num(i64::MIN); - fee.config.n = >::from_num(0); - assert_err!(fee.calculate_fee(r), "[FeeSigmoid] Overflow during calculation: (r-n)^2"); -} - -#[test] -fn fee_sigmoid_overflow_p_plus_r_minus_n_squared() { - let (mut fee, _, _, _) = init_default_sigmoid_fee_struct(); - let r = >::from_num(0); - fee.config.n = >::from_num(1); - fee.config.m = >::from_num(0); - fee.config.p = >::from_num(i64::MAX); - assert_err!(fee.calculate_fee(r), "[FeeSigmoid] Overflow during calculation: p + (r-n)^2"); -} - -#[test] -fn fee_sigmoid_overflow_numerator_div_denominator() { - let (mut fee, _, _, _) = init_default_sigmoid_fee_struct(); - let r = >::from_num(0.1); - fee.config.n = >::from_num(0); - fee.config.m = >::from_num(i64::MAX); - fee.config.p = >::from_num(-0.0099); - assert_err!( - fee.calculate_fee(r), - "[FeeSigmoid] Overflow during calculation: numerator / denominator" - ); -} - -#[test] -fn fee_sigmoid_correct_result() -> Result<(), &'static str> { - let r = 1.5f64; - let (mut fee, m, n, p) = init_default_sigmoid_fee_struct(); - let fee_f64 = fee.config.initial_fee.to_num::() + sigmoid_fee(m, n, p, r); - let r_fixed = >::from_num(r); - let fee_fixed = fee.calculate_fee(r_fixed)?; - let fee_fixed_f64: f64 = fee_fixed.to_num(); - let difference_abs = (fee_f64 - fee_fixed_f64).abs(); - - assert!( - difference_abs <= max_allowed_error(64), - "\nFixed result: {}\nFloat result: {}\nDifference: {}\nMax_Allowed_Difference: {}", - fee_fixed_f64, - fee_f64, - difference_abs, - max_allowed_error(64) - ); - - fee.config.min_revenue = >::from_num(1u64 << 62); - assert_eq!(fee.calculate_fee(r_fixed)?, fee.config.min_revenue); - Ok(()) -} diff --git a/zrml/rikiddo/src/traits.rs b/zrml/rikiddo/src/traits.rs deleted file mode 100644 index f2360ecfe..000000000 --- a/zrml/rikiddo/src/traits.rs +++ /dev/null @@ -1,270 +0,0 @@ -// 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 . - -//! This module contains a collection of traits for Rikiddo and its components. - -extern crate alloc; -use crate::types::TimestampedVolume; -use alloc::vec::Vec; -use core::convert::TryFrom; -use frame_support::dispatch::DispatchResult; -use sp_runtime::DispatchError; -use substrate_fixed::{ - traits::{Fixed, FixedUnsigned}, - ParseFixedError, -}; - -/// Fee calculating structures that take one argument. -pub trait Fee { - /// A fixed point type. - type FS: Fixed; - - /// Calculate fee. - /// - /// # Arguments - /// - /// * `r`: An external value that is incorporated into the fee calculation. - fn calculate_fee(&self, r: Self::FS) -> Result; - - /// Return the minimum fee - fn minimum_fee(&self) -> Self::FS; -} - -/// Market average specification for implementations such as EMA, SMA, median, WMA, etc. -pub trait MarketAverage { - /// An unsigned fixed point type. - type FU: FixedUnsigned; - - /// Get the market average (such es EMA, SMA, median, WMA, etc.) - fn get(&self) -> Option; - - /// Clear market data. - fn clear(&mut self); - - /// Update market data. - /// - /// # Arguments - /// - /// * `volume`: The timestamped volume that should be added to the market data. - fn update_volume( - &mut self, - volume: &TimestampedVolume, - ) -> Result, &'static str>; -} - -/// Logarithmic Market Scoring Rule (LMSR) specification. -pub trait Lmsr { - /// An unsigned fixed point type. - type FU: FixedUnsigned; - - /// Returns a vector of prices for a given set of assets (same order as `asset_balances`). - /// - /// # Arguments - /// - /// * `asset_balances`: The balance vector of the assets. - fn all_prices(&self, asset_balances: &[Self::FU]) -> Result, &'static str>; - - /// Returns the total cost for a specific vector of assets (see [LS-LMSR paper]). - /// - /// [LS-LMSR paper]: https://www.eecs.harvard.edu/cs286r/courses/fall12/papers/OPRS10.pdf - /// - /// # Arguments - /// - /// * `asset_balances`: The balance vector of the assets. - fn cost(&self, asset_balances: &[Self::FU]) -> Result; - - /// Returns the current fee. - fn fee(&self) -> Result; - - /// Returns the initial quantities of outstanding event outcome assets. - /// If 4 event outcome assets exist and this function returns 100, then the outstanding - /// shares for every single of those event outcome assets are 100. - /// - /// # Arguments - /// - /// * `num_assets`: The number of distinct outcome events. - /// * `subsidy`: The initial total subsidy gathered. - fn initial_outstanding_assets( - &self, - num_assets: u32, - subsidy: Self::FU, - ) -> Result; - - /// Returns the price of one specific asset. - /// - /// # Arguments - /// - /// * `asset_in_question`: The balance of the asset for which the price should be returned. - /// * `asset_balances`: The balance vector of the assets. - fn price( - &self, - asset_balances: &[Self::FU], - asset_in_question_balance: &Self::FU, - ) -> Result; -} - -/// A specification for any implementation of the Rikiddo variant that uses the market volume. -pub trait RikiddoMV: Lmsr { - /// Clear market data. - fn clear(&mut self); - - /// Update market data. - /// - /// # Arguments - /// - /// * `volume`: The timestamped volume that should be added to the market data. - fn update_volume( - &mut self, - volume: &TimestampedVolume, - ) -> Result, &'static str>; -} - -/// A specification that a pallet should follow if it wants to offer Rikiddo -/// functionality, that is based on the [`RikiddoMV`](trait@RikiddoMV) trait. -pub trait RikiddoMVPallet { - /// The type that represents on-chain balances. - type Balance; - /// The id of the pool of assets that's associated to one Rikiddo instance. - type PoolId: Copy; - /// An unsigned fixed point type. - type FU: FixedUnsigned; - /// A type that implements the RikiddoMV trait (LMSR + Rikiddo based on MarketVolume). - type Rikiddo: RikiddoMV; - - /// Returns a vector of prices for a given set of assets (same order as `asset_balances`). - /// - /// # Arguments - /// - /// * `poolid`: The id of the asset pool for which all asset prices shall be calculated. - /// * `asset_balances`: The balance vector of the assets. - fn all_prices( - poolid: Self::PoolId, - asset_balances: &[Self::Balance], - ) -> Result, DispatchError>; - - /// Clear market data for a specific asset pool. - /// - /// # Arguments - /// - /// * `poolid`: The id of the asset pool for which all asset prices shall be calculated. - fn clear(poolid: Self::PoolId) -> DispatchResult; - - /// Returns the total cost for a specific vector of assets (see [LS-LMSR paper]). - /// - /// [LS-LMSR paper]: https://www.eecs.harvard.edu/cs286r/courses/fall12/papers/OPRS10.pdf - /// - /// # Arguments - /// - /// * `poolid`: The id of the asset pool for which all asset prices shall be calculated. - /// * `asset_balances`: The balance vector of the assets. - fn cost( - poolid: Self::PoolId, - asset_balances: &[Self::Balance], - ) -> Result; - - /// Create Rikiddo instance for specifc asset pool. - /// - /// # Arguments - /// - /// * `poolid`: The id of the asset pool for which all asset prices shall be calculated. - /// * `rikiddo`: A specific type of Rikiddo as specified in the pallet's configuration. - fn create(poolid: Self::PoolId, rikiddo: Self::Rikiddo) -> DispatchResult; - - /// Destroy Rikiddo instance for a specific pool. - /// - /// # Arguments - /// - /// * `poolid`: The id of the asset pool for which all asset prices shall be calculated. - fn destroy(poolid: Self::PoolId) -> DispatchResult; - - /// Returns the current fee. - /// - /// # Arguments - /// - /// * `poolid`: The id of the asset pool for which all asset prices shall be calculated. - /// * `rikiddo`: A specific type of Rikiddo as specified in the pallet's configuration. - fn fee(poolid: Self::PoolId) -> Result; - - /// Returns the initial quantities of outstanding event outcome assets. - /// If 4 event outcome assets exist and this function returns 100, then the outstanding - /// shares for every single of those event outcome assets are 100. - /// - /// # Arguments - /// - /// * `poolid`: Id of the pool for which the outstanding shares shall be calculated. - /// * `num_assets`: The number of distinct outcome events. - /// * `subsidy`: The initial total subsidy gathered. - fn initial_outstanding_assets( - pool_id: Self::PoolId, - num_assets: u32, - subsidy: Self::Balance, - ) -> Result; - - /// Returns the price of one specific asset. - /// - /// # Arguments - /// - /// * `poolid`: The id of the asset pool for which all asset prices shall be calculated. - /// * `asset_in_question`: The balance of the asset for which the price should be returned. - /// * `asset_balances`: The balance vector of the assets. - fn price( - poolid: Self::PoolId, - asset_in_question: Self::Balance, - asset_balances: &[Self::Balance], - ) -> Result; - - /// Update the market data by adding volume. - /// - /// # Arguments - /// - /// * `poolid`: The id of the asset pool for which all asset prices shall be calculated. - /// * `volume`: The volume that was traded in the pool with id `poolid`. - fn update_volume( - poolid: Self::PoolId, - volume: Self::Balance, - ) -> Result, DispatchError>; -} - -/// Converts a fixed point decimal number into another type. -pub trait FromFixedDecimal> -where - Self: Sized, -{ - /// Craft a fixed point decimal number from `N`. - fn from_fixed_decimal(decimal: N, places: u8) -> Result; -} - -/// Converts a fixed point decimal number into another type. -pub trait IntoFixedFromDecimal { - /// Converts a fixed point decimal number into another type. - fn to_fixed_from_fixed_decimal(self, places: u8) -> Result; -} - -/// Converts a type into a fixed point decimal number. -pub trait FromFixedToDecimal -where - Self: Sized + TryFrom, -{ - /// Craft a fixed point decimal number from another type. - fn from_fixed_to_fixed_decimal(fixed: F, places: u8) -> Result; -} - -/// Converts a type into a fixed point decimal number. -pub trait IntoFixedDecimal> { - /// Converts a type into a fixed point decimal number. - fn to_fixed_decimal(self, places: u8) -> Result; -} diff --git a/zrml/rikiddo/src/types.rs b/zrml/rikiddo/src/types.rs deleted file mode 100644 index 5a5858e1e..000000000 --- a/zrml/rikiddo/src/types.rs +++ /dev/null @@ -1,138 +0,0 @@ -// Copyright 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 . - -//! This module contains a collection of types that are required to implement the Rikiddo core -//! functionality, as well as the Rikiddo core functionality itself. - -extern crate alloc; -#[cfg(feature = "arbitrary")] -use arbitrary::{Arbitrary, Result as ArbiraryResult, Unstructured}; -#[cfg(feature = "arbitrary")] -use core::mem; -use parity_scale_codec::{Decode, Encode, MaxEncodedLen}; -use scale_info::TypeInfo; -use sp_core::RuntimeDebug; -use substrate_fixed::traits::Fixed; -#[cfg(feature = "arbitrary")] -use substrate_fixed::{ - types::extra::{LeEqU128, LeEqU16, LeEqU32, LeEqU64, LeEqU8}, - FixedI128, FixedI16, FixedI32, FixedI64, FixedI8, FixedU128, FixedU16, FixedU32, FixedU64, - FixedU8, -}; - -mod ema_market_volume; -mod rikiddo_sigmoid_mv; -mod sigmoid_fee; - -pub use ema_market_volume::*; -pub use rikiddo_sigmoid_mv::*; -pub use sigmoid_fee::*; - -/// A timestamp that contains the seconds since January 1st, 1970 at UTC. -pub type UnixTimestamp = u64; - -/// A 2-tuple containing an unix timestamp and a volume. -#[derive(Clone, Decode, Default, Encode, Eq, MaxEncodedLen, PartialEq, RuntimeDebug, TypeInfo)] -pub struct TimestampedVolume { - /// The timestamp of the volume. - pub timestamp: UnixTimestamp, - /// The volume. - pub volume: F, -} - -#[cfg(feature = "arbitrary")] -macro_rules! impl_arbitrary_for_timestamped_volume { - ( $t:ident, $LeEqU:ident, $p:ty ) => { - #[allow(clippy::arithmetic_side_effects)] - impl<'a, Frac> Arbitrary<'a> for TimestampedVolume<$t> - where - Frac: $LeEqU, - $t: Fixed, - { - fn arbitrary(u: &mut Unstructured<'a>) -> ArbiraryResult { - return Ok(TimestampedVolume { - timestamp: >::arbitrary(u)?, - volume: <$t>::from_bits(<$p as Arbitrary<'a>>::arbitrary(u)?), - }); - } - - #[inline] - fn size_hint(_depth: usize) -> (usize, Option) { - let bytecount_fixed = mem::size_of::<$t>(); - let bytecount_timestamp = mem::size_of::(); - let required_bytes = bytecount_fixed + bytecount_timestamp; - (required_bytes, Some(required_bytes)) - } - } - }; -} - -cfg_if::cfg_if! { - if #[cfg(feature = "arbitrary")] { - impl_arbitrary_for_timestamped_volume! {FixedI8, LeEqU8, i8} - impl_arbitrary_for_timestamped_volume! {FixedI16, LeEqU16, i16} - impl_arbitrary_for_timestamped_volume! {FixedI32, LeEqU32, i32} - impl_arbitrary_for_timestamped_volume! {FixedI64, LeEqU64, i64} - impl_arbitrary_for_timestamped_volume! {FixedI128, LeEqU128, i128} - impl_arbitrary_for_timestamped_volume! {FixedU8, LeEqU8, u8} - impl_arbitrary_for_timestamped_volume! {FixedU16, LeEqU16, u16} - impl_arbitrary_for_timestamped_volume! {FixedU32, LeEqU32, u32} - impl_arbitrary_for_timestamped_volume! {FixedU64, LeEqU64, u64} - impl_arbitrary_for_timestamped_volume! {FixedU128, LeEqU128, u128} - } -} - -/// A enum that wrappes an amount of time in different units. -/// An enum that wrappes an amount of time in different units. -#[derive( - Clone, Copy, Decode, Encode, Eq, MaxEncodedLen, PartialEq, PartialOrd, RuntimeDebug, TypeInfo, -)] -#[cfg_attr(feature = "arbitrary", derive(Arbitrary))] -pub enum Timespan { - /// Contains seconds. - Seconds(u32), - /// Contains minutes. - Minutes(u32), - /// Contains hours. - Hours(u32), - /// Contains days. - Days(u16), - /// Contains weeks. - Weeks(u16), -} - -impl Timespan { - /// Convert the current `Timespan` into a number of seconds. - pub fn to_seconds(&self) -> u32 { - match *self { - // Any value that leads to a saturation is greater than - // 4294967295 seconds, which is about 136 years. - Timespan::Seconds(d) => d, - Timespan::Minutes(d) => d.saturating_mul(60), - Timespan::Hours(d) => d.saturating_mul(60).saturating_mul(60), - Timespan::Days(d) => { - u32::from(d).saturating_mul(60).saturating_mul(60).saturating_mul(24) - } - Timespan::Weeks(d) => u32::from(d) - .saturating_mul(60) - .saturating_mul(60) - .saturating_mul(24) - .saturating_mul(7), - } - } -} diff --git a/zrml/rikiddo/src/types/ema_market_volume.rs b/zrml/rikiddo/src/types/ema_market_volume.rs deleted file mode 100644 index 847f1dbc5..000000000 --- a/zrml/rikiddo/src/types/ema_market_volume.rs +++ /dev/null @@ -1,485 +0,0 @@ -// Copyright 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 . - -//! This module contains the structures used to calculate the exponential moving average. -use super::{Timespan, TimestampedVolume, UnixTimestamp}; -use crate::{ - constants::{EMA_SHORT, SMOOTHING}, - traits::MarketAverage, -}; -#[cfg(feature = "arbitrary")] -use arbitrary::{Arbitrary, Result as ArbitraryResult, Unstructured}; -#[cfg(feature = "arbitrary")] -use core::mem; -use parity_scale_codec::{Decode, Encode, MaxEncodedLen}; -use scale_info::TypeInfo; -use sp_core::RuntimeDebug; -use substrate_fixed::{ - traits::{Fixed, FixedUnsigned, LossyFrom, LossyInto, ToFixed}, - types::extra::{U24, U64}, - FixedU128, FixedU32, -}; -#[cfg(feature = "arbitrary")] -use substrate_fixed::{ - types::extra::{LeEqU128, LeEqU16, LeEqU32, LeEqU64, LeEqU8}, - FixedI128, FixedI16, FixedI32, FixedI64, FixedI8, FixedU16, FixedU64, FixedU8, -}; - -/// Configuration values used during the calculation of the exponenial moving average. -#[derive(Clone, Decode, Encode, Eq, MaxEncodedLen, PartialEq, RuntimeDebug, TypeInfo)] -pub struct EmaConfig { - /// The duration of one ema period. - pub ema_period: Timespan, - /// It is possible to estimate the values required to calculate the ema before `ema_period` - /// has passed. The count of added volumes after `ema_period` is estimated from the current - /// count of volumes. - pub ema_period_estimate_after: Option, - /// The smoothing factor that's used to calculate the multiplier - /// `k = smoothing / (1 + tx_count_per_period)`. - pub smoothing: FI, -} - -#[cfg(feature = "arbitrary")] -macro_rules! impl_arbitrary_for_ema_config { - ( $t:ident, $LeEqU:ident, $p:ty ) => { - impl<'a, Frac> Arbitrary<'a> for EmaConfig<$t> - where - Frac: $LeEqU, - $t: Fixed, - { - fn arbitrary(u: &mut Unstructured<'a>) -> ArbitraryResult { - Ok(EmaConfig::<$t> { - ema_period: >::arbitrary(u)?, - ema_period_estimate_after: Some(>::arbitrary(u)?), - smoothing: <$t>::from_bits(<$p as Arbitrary<'a>>::arbitrary(u)?), - }) - } - - #[inline] - fn size_hint(depth: usize) -> (usize, Option) { - let (min, max) = >::size_hint(depth); - let fsc_size = <$p as Arbitrary<'a>>::size_hint(depth); - let max_accumulated = max - .unwrap_or(min) - .saturating_mul(2) - .saturating_add(fsc_size.1.unwrap_or(fsc_size.0)); - let min_accumulated = min.saturating_mul(2).saturating_add(fsc_size.0); - - if max_accumulated == usize::MAX { - (min_accumulated, None) - } else { - (min_accumulated, Some(max_accumulated)) - } - } - } - }; -} - -cfg_if::cfg_if! { - if #[cfg(feature = "arbitrary")] { - impl_arbitrary_for_ema_config! {FixedI8, LeEqU8, i8} - impl_arbitrary_for_ema_config! {FixedI16, LeEqU16, i16} - impl_arbitrary_for_ema_config! {FixedI32, LeEqU32, i32} - impl_arbitrary_for_ema_config! {FixedI64, LeEqU64, i64} - impl_arbitrary_for_ema_config! {FixedI128, LeEqU128, i128} - impl_arbitrary_for_ema_config! {FixedU8, LeEqU8, u8} - impl_arbitrary_for_ema_config! {FixedU16, LeEqU16, u16} - impl_arbitrary_for_ema_config! {FixedU32, LeEqU32, u32} - impl_arbitrary_for_ema_config! {FixedU64, LeEqU64, u64} - impl_arbitrary_for_ema_config! {FixedU128, LeEqU128, u128} - } -} - -impl>> EmaConfig { - /// Create a new `EmaConfig` instance based on a [`EmaConfig`](struct@EmaConfig) - /// configuration. Use `default()` if uncertain which values to use. - /// - /// # Arguments - /// - /// * See [`EmaConfig`](struct@EmaConfig). - pub fn new( - ema_period: Timespan, - mut ema_period_estimate_after: Option, - smoothing: FU, - ) -> Self { - if let Some(res) = ema_period_estimate_after { - ema_period_estimate_after = if res >= ema_period { None } else { Some(res) }; - }; - - Self { ema_period, ema_period_estimate_after, smoothing } - } -} - -impl>> Default for EmaConfig { - fn default() -> Self { - Self::new(EMA_SHORT, None, SMOOTHING.lossy_into()) - } -} - -/// The current state of an instance of the [`EmaMarketVolume`](struct@EmaMarketVolume) struct. -#[derive(Clone, Decode, Encode, Eq, MaxEncodedLen, PartialEq, RuntimeDebug, TypeInfo)] -#[cfg_attr(feature = "arbitrary", derive(arbitrary::Arbitrary))] -pub enum MarketVolumeState { - /// The first state before any data was received. - Uninitialized, - /// The second state. Used as long not enough data was gathered. - DataCollectionStarted, - /// The final state, during which the evaluation of the data can happen. - DataCollected, -} - -/// The EmaMarketVolume `struct` offers functionality to collect and evaluate market volume data -/// into an exponential moving average value. -#[derive(Clone, Decode, Encode, Eq, MaxEncodedLen, PartialEq, RuntimeDebug, TypeInfo)] -pub struct EmaMarketVolume { - /// See [`EmaConfig`](struct@EmaConfig). - pub config: EmaConfig, - /// The current ema value or any intermediate value. - pub ema: FU, - multiplier: FU, - last_time: UnixTimestamp, - state: MarketVolumeState, - start_time: UnixTimestamp, - volumes_per_period: FU, -} - -#[cfg(feature = "arbitrary")] -macro_rules! impl_arbitrary_for_ema_market_volume { - ( $t:ident, $LeEqU:ident, $p:ty ) => { - impl<'a, Frac> Arbitrary<'a> for EmaMarketVolume<$t> - where - Frac: $LeEqU, - $t: FixedUnsigned + From, - { - fn arbitrary(u: &mut Unstructured<'a>) -> ArbitraryResult { - Ok(EmaMarketVolume::<$t>::new( - > as Arbitrary<'a>>::arbitrary(u)?, - )) - } - - #[inline] - fn size_hint(depth: usize) -> (usize, Option) { - let (min, max) = > as Arbitrary<'a>>::size_hint(depth); - let fixed_size = mem::size_of::<$t>(); - let ema_size = (fixed_size, fixed_size); - let multiplier_size = (fixed_size, fixed_size); - let last_time_size = >::size_hint(depth); - let state_size = >::size_hint(depth); - let start_time_size = >::size_hint(depth); - let volumes_per_period = (fixed_size, fixed_size); - - let max_accumulated = max - .unwrap_or(0) - .saturating_mul(2) - .saturating_add(ema_size.1) - .saturating_add(multiplier_size.1) - .saturating_add(last_time_size.1.unwrap_or(last_time_size.0)) - .saturating_add(state_size.1.unwrap_or(state_size.0)) - .saturating_add(start_time_size.1.unwrap_or(start_time_size.0)) - .saturating_add(volumes_per_period.0); - let min_accumulated = min - .saturating_add(ema_size.0) - .saturating_add(multiplier_size.0) - .saturating_add(last_time_size.0) - .saturating_add(state_size.0) - .saturating_add(start_time_size.0) - .saturating_add(volumes_per_period.0); - - if max_accumulated == usize::MAX { - (min_accumulated, None) - } else { - (min_accumulated, Some(max_accumulated)) - } - } - } - }; -} - -cfg_if::cfg_if! { - if #[cfg(feature = "arbitrary")] { - impl_arbitrary_for_ema_market_volume! {FixedI8, LeEqU8, i8} - impl_arbitrary_for_ema_market_volume! {FixedI16, LeEqU16, i16} - impl_arbitrary_for_ema_market_volume! {FixedI32, LeEqU32, i32} - impl_arbitrary_for_ema_market_volume! {FixedI64, LeEqU64, i64} - impl_arbitrary_for_ema_market_volume! {FixedI128, LeEqU128, i128} - impl_arbitrary_for_ema_market_volume! {FixedU8, LeEqU8, u8} - impl_arbitrary_for_ema_market_volume! {FixedU16, LeEqU16, u16} - impl_arbitrary_for_ema_market_volume! {FixedU32, LeEqU32, u32} - impl_arbitrary_for_ema_market_volume! {FixedU64, LeEqU64, u64} - impl_arbitrary_for_ema_market_volume! {FixedU128, LeEqU128, u128} - } -} - -impl> EmaMarketVolume { - /// Initialize the structure based on a configuration. - /// - /// # Arguments - /// - /// * See [`EmaConfig`](struct@EmaConfig) - pub fn new(config: EmaConfig) -> Self { - Self { - config, - ema: 0u8.into(), - state: MarketVolumeState::Uninitialized, - start_time: 0, - last_time: 0, - volumes_per_period: 0u8.into(), - multiplier: 0u8.into(), - } - } - - fn calculate_ema( - &mut self, - volume: &TimestampedVolume, - ) -> Result, &'static str> { - // Overflow is impossible here (the library ensures that multiplier ∊ [0,1]) - let volume_times_multiplier = if let Some(res) = volume.volume.checked_mul(self.multiplier) - { - res - } else { - return Err("[EmaMarketVolume] Overflow during calculation: volume * multiplier"); - }; - - // Overflow is impossible here. - let one_minus_multiplier = if let Some(res) = FU::from(1u8).checked_sub(self.multiplier) { - res - } else { - return Err("[EmaMarketVolume] Overflow during calculation: 1 - multiplier"); - }; - - // Overflow is impossible here. - let ema_times_one_minus_multiplier = - if let Some(res) = self.ema.checked_mul(one_minus_multiplier) { - res - } else { - return Err("[EmaMarketVolume] Overflow during calculation: ema * (1 - multiplier)"); - }; - - // Overflow is impossible here. - self.ema = if let Some(res) = - volume_times_multiplier.checked_add(ema_times_one_minus_multiplier) - { - res - } else { - return Err("[EmaMarketVolume] Overflow during calculation: ema = smoothed_new + \ - smoothed_previous"); - }; - - Ok(Some(self.ema)) - } - - fn calculate_sma( - &mut self, - volume: &TimestampedVolume, - ) -> Result, &'static str> { - // This can only overflow if the ema field is set manually - let sma_times_vpp = if let Some(res) = self.ema.checked_mul(self.volumes_per_period) { - res - } else { - return Err("[EmaMarketVolume] Overflow during calculation: sma * volumes_per_period"); - }; - - let sma_times_vpp_plus_volume = - if let Some(res) = sma_times_vpp.checked_add(volume.volume) { - res - } else { - return Err("[EmaMarketVolume] Overflow during calculation: sma * \ - volumes_per_period + volume"); - }; - - // This can't overflow. - self.ema = if let Some(res) = sma_times_vpp_plus_volume - .checked_div(self.volumes_per_period.saturating_add(FU::from(1u8))) - { - res - } else { - return Err( - "[EmaMarketVolume] Overflow during calculation: sma = numerator / denominator" - ); - }; - - Ok(Some(self.ema)) - } - - /// When the final state is reached, this function can be used to retrieve the multiplier. - pub fn multiplier(&self) -> &FU { - &self.multiplier - } - - /// Returns the `UnixTimestamp` of the last volume that was added. - pub fn last_time(&self) -> &UnixTimestamp { - &self.last_time - } - - /// Returns the current state of the data collection and evaluation automaton. - pub fn state(&self) -> &MarketVolumeState { - &self.state - } - - /// Returns the `UnixTimestamp` of the first volume that was added. - pub fn start_time(&self) -> &UnixTimestamp { - &self.start_time - } - - /// Returns how many separate volumes one ema period contains. - pub fn volumes_per_period(&self) -> &FU { - &self.volumes_per_period - } -} - -impl + LossyFrom>> Default for EmaMarketVolume { - fn default() -> Self { - EmaMarketVolume::new(EmaConfig::default()) - } -} - -impl> MarketAverage for EmaMarketVolume { - type FU = FU; - - /// Get the ema value if available, otherwise `None`. Using this function is prefered in most - /// cases, because directly accessing the `ema` field will also return a valid result during - /// the time when not enough data was gathered, but the `ema` value is still a - /// `sma` value at this time. - fn get(&self) -> Option { - match &self.state { - MarketVolumeState::DataCollected => Some(self.ema), - _ => None, - } - } - - /// Clear market data. - fn clear(&mut self) { - self.ema = 0u8.into(); - self.multiplier = 0u8.into(); - self.last_time = 0; - self.state = MarketVolumeState::Uninitialized; - self.start_time = 0; - self.volumes_per_period = 0u8.into(); - } - - /// Update market data. This implementation will count the - /// [`TimestampedVolume`](struct@TimestampedVolume)s for one period and then use the amount - /// to derive the multiplier based on the smoothing factor. It will continue counting volumes, - /// if the resulting multiplier is still greater than one (very rare case, could lead to - /// negative ema). Once enough data is gathered, the ema is updated after every new incoming - /// `volume`. - /// - /// # Arguments - /// - /// * `volume`: The timestamped volume that should be added to the market data. - fn update_volume( - &mut self, - volume: &TimestampedVolume, - ) -> Result, &'static str> { - if let Some(res) = volume.timestamp.checked_sub(self.last_time) { - res - } else { - return Err( - "[EmaMarketVolume] Incoming volume timestamp is older than previous timestamp" - ); - }; - - let mut result: Option = None; - - match self.state { - MarketVolumeState::Uninitialized => { - self.ema = volume.volume; - self.start_time = volume.timestamp; - self.last_time = volume.timestamp; - self.volumes_per_period = 1.into(); - self.state = MarketVolumeState::DataCollectionStarted; - } - MarketVolumeState::DataCollectionStarted => { - // This can never overflow, because every transactions timestamp is checked - // against the timestamp of the previous transaction. - let timestamp_sub_start_time = volume.timestamp.saturating_sub(self.start_time); - - let mut comparison_value = if let Some(res) = self.config.ema_period_estimate_after - { - res.to_seconds() - } else { - self.config.ema_period.to_seconds() - }; - - // It should not state transit, if the amount of gathered data is too low. - // This would result in a multiplier that is greater than 1, which can lead to - // a negative ema. The amount depends on the size of the smoothing factor. - if timestamp_sub_start_time > u64::from(comparison_value) - && (*self.volumes_per_period() + 1.into()) >= self.config.smoothing - { - // We extrapolate the txs per period - if self.config.ema_period_estimate_after.is_some() { - // Ensure that we don't divide by 0 - if comparison_value == 0 { - comparison_value = 1 - } - - let premature_time_fixed = >::from(comparison_value); - let mature_time_fixed = - >::from(self.config.ema_period.to_seconds()); - // Overflow impossible - let estimate_ratio = mature_time_fixed.saturating_div(premature_time_fixed); - - // Cannot fail as long as the From trait is required for FU and - // Timespan::to_seconds() returns u32. - let estimate_ratio_fu: FU = estimate_ratio - .checked_to_fixed() - .ok_or("[EmaMarketVolume] Estimate ratio does not fit in FU")?; - - self.volumes_per_period = if let Some(res) = - self.volumes_per_period.checked_mul(estimate_ratio_fu) - { - res.saturating_ceil() - } else { - return Err("[EmaMarketVolume] Overflow during estimation of \ - transactions per period"); - } - } - - // Overflow is impossible here. - self.multiplier = if let Some(res) = self - .config - .smoothing - .checked_div(self.volumes_per_period.saturating_add(FU::from(1))) - { - res - } else { - return Err("[EmaMarketVolume] Overflow during calculation: multiplier = \ - smoothing / (1 + volumes_per_period)"); - }; - - self.state = MarketVolumeState::DataCollected; - result = self.calculate_ema(volume)?; - } else { - // During this phase the ema is still a sma. - let _ = self.calculate_sma(volume)?; - // In the context of blockchains, overflowing here is irrelevant (technically - // not realizable). In other contexts, ensure that FU can represent a number - // that is equal to the number of incoming volumes during one period. - self.volumes_per_period = self.volumes_per_period.saturating_add(FU::from(1)); - } - } - MarketVolumeState::DataCollected => { - result = self.calculate_ema(volume)?; - } - } - - self.last_time = volume.timestamp; - Ok(result) - } -} diff --git a/zrml/rikiddo/src/types/rikiddo_sigmoid_mv.rs b/zrml/rikiddo/src/types/rikiddo_sigmoid_mv.rs deleted file mode 100644 index eba866310..000000000 --- a/zrml/rikiddo/src/types/rikiddo_sigmoid_mv.rs +++ /dev/null @@ -1,796 +0,0 @@ -// Copyright 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 . - -//! This module offers the Rikiddo core functionality. The implementation is modular in regards -//! to the caluclation of the fee and the evaluation of the collected market volumes. - -extern crate alloc; -use crate::{ - constants::INITIAL_FEE, - traits::{Fee, Lmsr, MarketAverage, RikiddoMV}, - utils::{convert_to_signed, convert_to_unsigned, fixed_zero, max_value_u128}, -}; -use alloc::vec::Vec; -#[cfg(feature = "arbitrary")] -use arbitrary::{Arbitrary, Result as ArbiraryResult, Unstructured}; -use core::ops::{AddAssign, BitOrAssign, ShlAssign}; -use hashbrown::HashMap; -use parity_scale_codec::{Decode, Encode, MaxEncodedLen}; -use scale_info::TypeInfo; -use sp_core::RuntimeDebug; -use substrate_fixed::{ - consts::LOG2_E, - traits::{Fixed, FixedSigned, FixedUnsigned, LossyFrom, LossyInto, ToFixed}, - transcendental::{exp, ln}, - types::{ - extra::{U127, U128, U31, U32}, - I9F23, U1F127, - }, - FixedI128, FixedI32, FixedU128, FixedU32, -}; -#[cfg(feature = "arbitrary")] -use substrate_fixed::{ - types::extra::{LeEqU128, LeEqU32, LeEqU64}, - FixedI64, FixedU64, -}; - -use super::TimestampedVolume; - -/// Configuration values used within the Rikiddo core functions. -#[derive(Clone, Decode, Encode, Eq, MaxEncodedLen, PartialEq, RuntimeDebug, TypeInfo)] -pub struct RikiddoConfig { - /// An initial fee that is used whenever the fee cannot be calculated. - pub initial_fee: FI, - pub(crate) log2_e: FI, -} - -#[cfg(feature = "arbitrary")] -macro_rules! impl_arbitrary_for_rikiddo_config { - ( $t:ident, $LeEqU:ident, $p:ty ) => { - impl<'a, Frac> Arbitrary<'a> for RikiddoConfig<$t> - where - Frac: $LeEqU, - $t: FixedSigned + LossyFrom> + LossyFrom, - { - fn arbitrary(u: &mut Unstructured<'a>) -> ArbiraryResult { - Ok(RikiddoConfig::<$t>::new(<$t>::from_bits( - <$p as Arbitrary<'a>>::arbitrary(u)?, - ))) - } - - #[inline] - fn size_hint(depth: usize) -> (usize, Option) { - <$p as Arbitrary<'a>>::size_hint(depth) - } - } - }; -} - -cfg_if::cfg_if! { - if #[cfg(feature = "arbitrary")] { - impl_arbitrary_for_rikiddo_config! {FixedI32, LeEqU32, i32} - impl_arbitrary_for_rikiddo_config! {FixedI64, LeEqU64, i64} - impl_arbitrary_for_rikiddo_config! {FixedI128, LeEqU128, i128} - } -} - -impl> + LossyFrom> RikiddoConfig { - /// Create a new `RikiddoConfig` instance based on a [`RikiddoConfig`](struct@RikiddoConfig) - /// configuration. Use `default()` if uncertain which values to use. - /// - /// # Arguments - /// - /// * See [`RikiddoConfig`](struct@RikiddoConfig). - pub fn new(initial_fee: FS) -> Self { - Self { initial_fee, log2_e: FS::lossy_from(LOG2_E) } - } -} - -impl> + LossyFrom> Default for RikiddoConfig { - fn default() -> Self { - // Potentially dangerous unwrap(), should be impossible to fail (tested). - let converted = convert_to_signed::, FixedI32>(INITIAL_FEE).unwrap(); - Self::new(converted.lossy_into()) - } -} - -// The RikiddoFormulaComponents contain all necessary duplicate information -// thorughout multiple calculations. -pub(crate) struct RikiddoFormulaComponents -where - FS: FixedSigned + From + LossyFrom> + LossyFrom, -{ - pub(crate) one: FS, - pub(crate) fee: FS, - pub(crate) sum_balances: FS, - pub(crate) sum_times_fee: FS, - pub(crate) emax: FS, - pub(crate) sum_exp: FS, - pub(crate) exponents: HashMap, - pub(crate) reduced_exponential_results: HashMap, -} - -impl Default for RikiddoFormulaComponents -where - FS: FixedSigned + From + From + LossyFrom> + LossyFrom, -{ - fn default() -> Self { - Self { - one: 1i8.into(), - fee: 0i8.into(), - sum_balances: 0i8.into(), - sum_times_fee: 0i8.into(), - emax: 0i8.into(), - sum_exp: 0i8.into(), - exponents: HashMap::new(), - reduced_exponential_results: HashMap::new(), - } - } -} - -/// Configuration values used within the Rikiddo core functions. -#[derive(Clone, Decode, Default, Encode, Eq, MaxEncodedLen, PartialEq, RuntimeDebug, TypeInfo)] -pub struct RikiddoSigmoidMV -where - FU: FixedUnsigned + LossyFrom>, - FS: FixedSigned + LossyFrom> + LossyFrom, - FE: Fee, - MA: MarketAverage, -{ - /// See [`RikiddoConfig`](struct@RikiddoConfig). - pub config: RikiddoConfig, - /// A structure that implements the [`Fee`](trait@Fee) trait. Used to calculate the fee. - pub fees: FE, - /// A structure that implements the [`MarketAverage`](trait@MarketAverage) trait. It is used - /// to calculate the ema for the shorter ema period. - pub ma_short: MA, - /// A structure that implements the [`MarketAverage`](trait@MarketAverage) trait. It is used - /// to calculate the ema for the longer ema period. - pub ma_long: MA, -} - -#[cfg(feature = "arbitrary")] -macro_rules! impl_arbitrary_for_rikiddo_sigmoid_mv { - ( $ts:ident, $LeEqUs:ident, $tu:ident, $LeEqUu:ident ) => { - impl<'a, FracU, FracS, FE, MA> Arbitrary<'a> - for RikiddoSigmoidMV<$tu, $ts, FE, MA> - where - FracU: $LeEqUu, - FracS: $LeEqUs, - $tu: FixedUnsigned + LossyFrom> + LossyFrom>, - $ts: FixedSigned - + From - + From - + LossyFrom> - + LossyFrom - + LossyFrom> - + PartialOrd, - <$ts as Fixed>::Bits: Copy + ToFixed + AddAssign + BitOrAssign + ShlAssign, - FE: Fee> + Arbitrary<'a>, - MA: MarketAverage> + Arbitrary<'a>, - { - fn arbitrary(u: &mut Unstructured<'a>) -> ArbiraryResult { - Ok(RikiddoSigmoidMV::new( - > as Arbitrary<'a>>::arbitrary(u)?, - >::arbitrary(u)?, - >::arbitrary(u)?, - >::arbitrary(u)?, - )) - } - - #[inline] - fn size_hint(depth: usize) -> (usize, Option) { - let (min, max) = > as Arbitrary<'a>>::size_hint(depth); - - let fe_size = >::size_hint(depth); - let ma_size = >::size_hint(depth); - - let max_accumulated = max - .unwrap_or(0) - .saturating_add(fe_size.1.unwrap_or(fe_size.0)) - .saturating_add(ma_size.1.unwrap_or(ma_size.0).saturating_mul(2)); - let min_accumulated = min.saturating_add(fe_size.0).saturating_add(ma_size.0); - - if max_accumulated == usize::MAX { - (min_accumulated, None) - } else { - (min_accumulated, Some(max_accumulated)) - } - } - } - }; -} - -cfg_if::cfg_if! { - if #[cfg(feature = "arbitrary")] { - impl_arbitrary_for_rikiddo_sigmoid_mv! {FixedI32, LeEqU32, FixedU32, LeEqU32} - impl_arbitrary_for_rikiddo_sigmoid_mv! {FixedI64, LeEqU64, FixedU64, LeEqU64} - impl_arbitrary_for_rikiddo_sigmoid_mv! {FixedI128, LeEqU128, FixedU128, LeEqU128} - } -} - -impl RikiddoSigmoidMV -where - FU: FixedUnsigned + LossyFrom> + LossyFrom>, - FS: FixedSigned - + From - + From - + LossyFrom> - + LossyFrom - + LossyFrom> - + PartialOrd, - FS::Bits: Copy + ToFixed + AddAssign + BitOrAssign + ShlAssign, - FE: Fee, - MA: MarketAverage, -{ - /// Initialize the structure based on a configuration. - /// - /// # Arguments - /// - /// * See [`RikiddoSigmoidMV`](struct@RikiddoSigmoidMV) - pub fn new(config: RikiddoConfig, fees: FE, ma_short: MA, ma_long: MA) -> Self { - Self { config, fees, ma_short, ma_long } - } - - // Cost function that returns the cost and parts of the formula that can be reused for the - // price calculation. - // Setting `for_price = true` returns the cost minus the sum of quantities. - // Setting `add_exponents = true` leads to saving the exponents and the reduced exponential - // results into the `formula_components` struct. - pub(crate) fn cost_with_forumla( - &self, - asset_balances: &[FU], - formula_components: &mut RikiddoFormulaComponents, - for_price: bool, - add_exponents: bool, - ) -> Result { - if asset_balances.is_empty() { - return Err("[RikiddoSigmoidMV] No asset balances provided"); - }; - - let fee = self.fee()?; - formula_components.fee = convert_to_signed(fee)?; - let mut total_balance = fixed_zero::()?; - - for elem in asset_balances { - if let Some(res) = total_balance.checked_add(*elem) { - total_balance = res; - } else { - return Err("[RikiddoSigmoidMV] Overflow during summation of asset balances"); - } - } - - formula_components.sum_balances = convert_to_signed(total_balance)?; - - let denominator = if let Some(res) = fee.checked_mul(total_balance) { - res - } else { - // Highly unlikely and only possible if fee > 100% - return Err("[RikiddoSigmoidMV] Overflow during calculation: fee * total_asset_balance"); - }; - - formula_components.sum_times_fee = convert_to_signed(denominator)?; - - let mut exponents: Vec = Vec::with_capacity(asset_balances.len()); - let mut biggest_exponent: FS = fixed_zero::()?; - - for elem in asset_balances { - let exponent = if let Some(res) = elem.checked_div(denominator) { - convert_to_signed::(res)? - } else { - // Highly unlikely - return Err("[RikiddoSigmoidMV] Overflow during calculation: expontent_i = \ - asset_balance_i / denominator"); - }; - - if exponent > biggest_exponent { - biggest_exponent = exponent; - } - - // Panic impossible - exponents.push(exponent); - - if add_exponents { - let elem_fs = convert_to_signed(*elem)?; - formula_components.exponents.insert(elem_fs, exponent); - } - } - - formula_components.emax = biggest_exponent; - - if max_value_u128::()? < asset_balances.len() as u128 { - return Err("[RikidoSigmoidMV] Number of assets does not fit in FS"); - } - - let ln_sum_e = self.optimized_cost_strategy( - &exponents, - &biggest_exponent, - formula_components, - add_exponents, - )?; - - if for_price { - if let Some(res) = formula_components.fee.checked_mul(ln_sum_e) { - return Ok(res); - } - Err("[RikiddoSigmoidMV] Overflow during calculation: fee * ln(sum_i(e^i))") - } else { - if let Some(res) = formula_components.sum_times_fee.checked_mul(ln_sum_e) { - return Ok(res); - } - - Err("[RikiddoSigmoidMV] Overflow during calculation: fee * total_asset_balance * \ - ln(sum_i(e^i))") - } - } - - // Calculates the first quotient in the price formula after the cost / sum_balances - pub(crate) fn price_helper_first_quotient( - &self, - asset_balances: &[FS], - asset_in_question_balance: &FS, - formula_components: &RikiddoFormulaComponents, - ) -> Result { - let exponent_of_balance_in_question = - if let Some(res) = formula_components.exponents.get(asset_in_question_balance) { - *res - } else { - return Err("[RikiddoSigmoidMV] Cannot find exponent of asset balance in \ - question in RikiddoFormulaComponents HashMap"); - }; - - let mut sum = fixed_zero::()?; - let mut skipped = false; - - for elem in asset_balances { - if elem == asset_in_question_balance && !skipped { - skipped = true; - continue; - } - - let elem_div_sum_fee = if let Some(res) = formula_components.exponents.get(elem) { - *res - } else { - return Err("[RikiddoSigmoidMV] Cannot find exponent of asset balance in \ - question RikiddoFormulaComponents HashMap"); - }; - - let exponent = - if let Some(res) = elem_div_sum_fee.checked_sub(exponent_of_balance_in_question) { - res - } else { - // Should be impossible (negative exponent not possible unless manually entered) - return Err("[RikiddoSigmoidMV] Overflow during calculation: exponent - \ - exponent_balance_in_question"); - }; - - let exponential_result = if let Ok(res) = exp::(exponent) { - res - } else { - // In that case the final result will not fit into the fractional bits - // and therefore is approximated to zero. Cannot panic. - fixed_zero::()? - }; - - sum = if let Some(res) = sum.checked_add(exponential_result) { - res - } else { - // In that case the final result will not fit into the fractional bits - // and therefore is approximated to zero. Cannot panic. - return fixed_zero::(); - }; - } - - sum = if let Some(res) = sum.checked_add(formula_components.one) { - res - } else { - // In that case the final result will not fit into the fractional bits - // and therefore is approximated to zero. Cannot panic. - return fixed_zero::(); - }; - - if let Some(res) = formula_components.one.checked_div(sum) { - Ok(res) - } else { - fixed_zero::() - } - } - - // Calculates the second quotient in the price formula after the cost / sum_balances - pub(crate) fn price_helper_second_quotient( - &self, - asset_balances: &[FS], - formula_components: &RikiddoFormulaComponents, - ) -> Result { - let mut numerator = fixed_zero::()?; - let mut skipped = false; - - for elem in asset_balances { - let exponent_of_balance_in_question = - if let Some(res) = formula_components.exponents.get(elem) { - *res - } else { - return Err("[RikiddoSigmoidMV] Cannot find exponent of asset balance in \ - question in RikiddoFormulaComponents HashMap"); - }; - - let elem_times_reduced_exponential_result; - - if exponent_of_balance_in_question == formula_components.emax && !skipped { - elem_times_reduced_exponential_result = *elem; - skipped = true; - } else { - let exponential_result = if let Some(res) = formula_components - .reduced_exponential_results - .get(&exponent_of_balance_in_question) - { - *res - } else { - return Err("[RikiddoSigmoidMV] Cannot find reduced exponential result of \ - current element"); - }; - - elem_times_reduced_exponential_result = - if let Some(res) = elem.checked_mul(exponential_result) { - res - } else { - // Should be impossible, since reduced_exponential_result ∈ ]0, 1] - return Err("[RikiddoSigmoidMV] Overflow during calculation: element * \ - reduced_exponential_result"); - }; - } - - numerator = - if let Some(res) = numerator.checked_add(elem_times_reduced_exponential_result) { - res - } else { - return Err("[RikiddoSigmoidMV] Overflow during calculation: sum_j += \ - elem_times_reduced_exponential_result"); - }; - } - - let denominator = if let Some(res) = - formula_components.sum_balances.checked_mul(formula_components.sum_exp) - { - res - } else { - return Err("[RikiddoSigmoidMV] Overflow during calculation: sum_balances * sum_exp"); - }; - - if let Some(res) = numerator.checked_div(denominator) { - Ok(res) - } else { - Err("[RikiddoSigmoidMV] Overflow during calculation (price helper 2): numerator / \ - denominator") - } - } - - // Calculates the price. - pub(crate) fn price_helper_combine_all_parts( - &self, - cost_part: FS, - first_quotient: FS, - second_quotient: FS, - ) -> Result { - let quotient_sub = if let Some(res) = first_quotient.checked_sub(second_quotient) { - res - } else { - // Should be impossible - return Err("[RikiddoSigmoidMV] Overflow during calculation of price: first_quotient \ - - second_quotient"); - }; - - if let Some(res) = cost_part.checked_add(quotient_sub) { - convert_to_unsigned(res) - } else { - // Should be impossible - Err("[RikiddoSigmoidMV] Overflow during calculation of price: cost_part + quotient_sub") - } - } - - pub(crate) fn optimized_cost_strategy( - &self, - exponents: &[FS], - biggest_exponent: &FS, - formula_components: &mut RikiddoFormulaComponents, - add_reduced_exponential_results: bool, - ) -> Result { - let mut biggest_exponent_used = false; - - if max_value_u128::()? < 1u128 { - // Impossible due to trait bounds (at least 1 sign bit and 8 integer bits) - return Err("[RikiddoSigmoidMV] Error, cannot initialize FS with one"); - } - - let mut exp_sum = formula_components.one; - let result = *biggest_exponent; - - for elem in exponents { - if !biggest_exponent_used && elem == biggest_exponent { - biggest_exponent_used = true; - continue; - } - - let exponent = if let Some(res) = elem.checked_sub(*biggest_exponent) { - res - } else { - // Should be impossible - return Err("[RikiddoSigmoidFee] Overflow during calculation: current_exponent - \ - biggest_exponent"); - }; - - let e_power_exponent = if let Ok(res) = exp::(exponent) { - res - } else { - // In this case the result is zero (or is too small to fit) and can be ignored - continue; - }; - - if add_reduced_exponential_results { - formula_components.reduced_exponential_results.insert(*elem, e_power_exponent); - } - - if let Some(res) = exp_sum.checked_add(e_power_exponent) { - exp_sum = res; - } else { - // Highly unlikely - return Err("[RikiddoSigmoidFee] Overflow during calculation: sum_i(e^(i - \ - biggest_exponent))"); - }; - } - - formula_components.sum_exp = exp_sum; - - let ln_exp_sum = if let Ok(res) = ln::(exp_sum) { - res - } else { - // Impossible - return Err("[RikiddoSigmoidMV] ln(exp_sum) (optimized), exp_sum <= 0"); - }; - - if let Some(res) = result.checked_add(ln_exp_sum) { - Ok(res) - } else { - // Highly unlikely - Err("[RikiddoSigmoidMV] Overflow during calculation: biggest_exponent + ln(exp_sum) \ - (optimized)") - } - } -} - -impl Lmsr for RikiddoSigmoidMV -where - FU: FixedUnsigned + LossyFrom> + LossyFrom>, - FS: FixedSigned - + From - + From - + LossyFrom> - + LossyFrom - + LossyFrom> - + PartialOrd, - FS::Bits: Copy + ToFixed + AddAssign + BitOrAssign + ShlAssign, - FE: Fee, - MA: MarketAverage, -{ - type FU = FU; - - /// Returns a vector of prices for a given set of assets (same order as `asset_balances`). - /// This function is significantly faster compared to sequentially invoking `price(...)` for - /// every asset. - /// - /// # Arguments - /// - /// * `asset_balances`: The balance vector of the assets. - fn all_prices(&self, asset_balances: &[Self::FU]) -> Result, &'static str> { - let mut formula_components = RikiddoFormulaComponents::default(); - let mut asset_balances_signed = Vec::with_capacity(asset_balances.len()); - - for asset_balance in asset_balances { - let signed_balance = convert_to_signed(*asset_balance)?; - asset_balances_signed.push(signed_balance); - } - - let cost_part = - self.cost_with_forumla(asset_balances, &mut formula_components, true, true)?; - - let mut result = Vec::with_capacity(asset_balances.len()); - - for asset_balance in &asset_balances_signed { - let first_quotient = self.price_helper_first_quotient( - &asset_balances_signed, - asset_balance, - &formula_components, - )?; - let second_quotient = - self.price_helper_second_quotient(&asset_balances_signed, &formula_components)?; - result.push(self.price_helper_combine_all_parts( - cost_part, - first_quotient, - second_quotient, - )?); - } - - Ok(result) - } - - /// Returns the total cost for a specific vector of assets (see [LS-LMSR paper]). - /// - /// [LS-LMSR paper]: https://www.eecs.harvard.edu/cs286r/courses/fall12/papers/OPRS10.pdf - /// - /// # Arguments - /// - /// * `asset_balances`: The balance vector of the assets. - fn cost(&self, asset_balances: &[Self::FU]) -> Result { - convert_to_unsigned(self.cost_with_forumla( - asset_balances, - &mut Default::default(), - false, - false, - )?) - } - - /// Returns the current fee. - fn fee(&self) -> Result { - let mas = if let Some(res) = self.ma_short.get() { - res - } else { - return convert_to_unsigned(self.config.initial_fee); - }; - - let mal = if let Some(res) = self.ma_long.get() { - res - } else { - return convert_to_unsigned(self.config.initial_fee); - }; - - if mal == fixed_zero::()? { - return Err( - "[RikiddoSigmoidMV] Zero division error during calculation: ma_short / ma_long" - ); - } - - let ratio = if let Some(res) = mas.checked_div(mal) { - res - } else { - return Err("[RikiddoSigmoidMV] Overflow during calculation: ma_short / ma_long"); - }; - - let ratio_signed = convert_to_signed(ratio)?; - convert_to_unsigned::(self.fees.calculate_fee(ratio_signed)?) - } - - /// Returns the initial quantities of outstanding event outcome assets. - /// If 4 event outcome assets exist and this function returns 100, then the outstanding - /// shares for every single of those event outcome assets are 100. - /// - /// # Arguments - /// - /// * `num_assets`: The number of distinct outcome events. - /// * `subsidy`: The initial total subsidy gathered. - fn initial_outstanding_assets( - &self, - num_assets: u32, - subsidy: Self::FU, - ) -> Result { - let fee = self.fees.minimum_fee(); - let conversion_error = "[RikidoSigmoidMV] Number of assets does not fit in FU"; - let fee_overflow = "[RikidoSigmoidMV] Overflow during calculation: fee * num_assets"; - let ln_error = "[RikiddoSigmoidMV] ln(num_assets) failed"; - let denom_error = - "[RikidoSigmoidMV] Overflow during calculation: fee * num_assets * ln(num_assets) + 1"; - let laste = "[RikidoSigmoidMV] Overflow during calculation: numerator / denominator"; - let num_assets_fs = num_assets.checked_to_fixed().ok_or(conversion_error)?; - let fee_times_num_assets = fee.checked_mul(num_assets_fs).ok_or(fee_overflow)?; - // This should not fail - let ln_num_assets: FS = ln(num_assets_fs).map_err(|_| ln_error)?; - let denominator = fee_times_num_assets - .checked_mul(ln_num_assets) - .ok_or(denom_error)? - .checked_add(1.checked_to_fixed().ok_or(conversion_error)?) - .ok_or(denom_error)?; - convert_to_unsigned( - convert_to_signed::(subsidy)?.checked_div(denominator).ok_or(laste)?, - ) - } - - /// Returns the price of one specific asset. - /// - /// # Arguments - /// - /// * `asset_in_question`: The balance of the asset for which the price should be returned. - /// * `asset_balances`: The balance vector of the assets. - fn price( - &self, - asset_balances: &[Self::FU], - asset_in_question_balance: &Self::FU, - ) -> Result { - let mut formula_components = RikiddoFormulaComponents::default(); - let mut asset_balances_signed = Vec::with_capacity(asset_balances.len()); - let mut asset_in_question_balance_signed = fixed_zero()?; - let mut asset_in_question_found = false; - - for asset_balance in asset_balances { - let signed_balance = convert_to_signed(*asset_balance)?; - asset_balances_signed.push(signed_balance); - - if asset_balance == asset_in_question_balance { - asset_in_question_balance_signed = signed_balance; - asset_in_question_found = true; - } - } - - if !asset_in_question_found { - return Err("[RikiddoSigmoidMV] asset_in_question_balance not found in asset_balances"); - } - - let cost_part = - self.cost_with_forumla(asset_balances, &mut formula_components, true, true)?; - let first_quotient = self.price_helper_first_quotient( - &asset_balances_signed, - &asset_in_question_balance_signed, - &formula_components, - )?; - let second_quotient = - self.price_helper_second_quotient(&asset_balances_signed, &formula_components)?; - self.price_helper_combine_all_parts(cost_part, first_quotient, second_quotient) - } -} - -impl RikiddoMV for RikiddoSigmoidMV -where - FU: FixedUnsigned + LossyFrom> + LossyFrom>, - FS: FixedSigned - + From - + From - + LossyFrom> - + LossyFrom - + LossyFrom> - + PartialOrd, - FS::Bits: Copy + ToFixed + AddAssign + BitOrAssign + ShlAssign, - FE: Fee, - MA: MarketAverage, -{ - /// Clear market data. - fn clear(&mut self) { - self.ma_short.clear(); - self.ma_long.clear(); - } - - /// Update market data. - /// - /// # Arguments - /// - /// * `volume`: The timestamped volume that should be added to the market data. - fn update_volume( - &mut self, - volume: &TimestampedVolume, - ) -> Result, &'static str> { - let mas = self.ma_short.update_volume(volume)?; - let mal = self.ma_long.update_volume(volume)?; - - if let Some(mas) = mas { - if let Some(mal) = mal { - if mal != fixed_zero::()? { - return Ok(Some(mas.saturating_div(mal))); - } - }; - }; - - Ok(None) - } -} diff --git a/zrml/rikiddo/src/types/sigmoid_fee.rs b/zrml/rikiddo/src/types/sigmoid_fee.rs deleted file mode 100644 index 97640057a..000000000 --- a/zrml/rikiddo/src/types/sigmoid_fee.rs +++ /dev/null @@ -1,250 +0,0 @@ -// Copyright 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 . - -//! This module contains the structures used to calculate the fee based on a sigmoid curve. - -use crate::{ - constants::{INITIAL_FEE, M, MINIMAL_REVENUE, N, P}, - traits::Fee, - utils::convert_to_signed, -}; -#[cfg(feature = "arbitrary")] -use arbitrary::{Arbitrary, Result as ArbiraryResult, Unstructured}; -#[cfg(feature = "arbitrary")] -use core::mem; -use parity_scale_codec::{Decode, Encode, MaxEncodedLen}; -use scale_info::TypeInfo; -use sp_core::RuntimeDebug; -use substrate_fixed::{ - traits::{FixedSigned, LossyFrom, LossyInto}, - transcendental::sqrt, - types::{ - extra::{U127, U24, U32}, - I9F23, - }, - FixedI128, FixedI32, FixedU32, -}; -#[cfg(feature = "arbitrary")] -use substrate_fixed::{ - types::extra::{LeEqU128, LeEqU32, LeEqU64}, - FixedI64, -}; - -/// Configurable values used to calculate a fee based on a sigmoid curve. The usage of the -/// configuration parameters is depicted in equation `z(r)` within the -/// [Dynamic Automated Market Making] paper from Andrew Nguyed et al. Use the `default()` -/// function if uncertain about which values to take. -/// -/// [Dynamic Automated Market Making]: https://files.kyber.network/DMM-Feb21.pdf -#[derive(Clone, Decode, Encode, Eq, MaxEncodedLen, PartialEq, RuntimeDebug, TypeInfo)] -pub struct FeeSigmoidConfig { - /// Parameter to fine tune the fee calcultation (refer to example in paper). - pub m: FS, - /// Parameter to fine tune the fee calcultation (refer to example in paper). - pub p: FS, - /// Break point used to encourage or discourage trade. - pub n: FS, - /// The initial fee, that is added to the sigmoid fee result. - pub initial_fee: FS, - /// The minimal revenue sets the lower bound for fees. - pub min_revenue: FS, -} - -#[cfg(feature = "arbitrary")] -macro_rules! impl_arbitrary_for_fee_sigmoid_config { - ( $t:ident, $LeEqU:ident, $p:ty ) => { - #[allow(clippy::arithmetic_side_effects)] - impl<'a, Frac> Arbitrary<'a> for FeeSigmoidConfig<$t> - where - Frac: $LeEqU, - $t: FixedSigned - + LossyFrom> - + PartialOrd - + LossyFrom>, - { - fn arbitrary(u: &mut Unstructured<'a>) -> ArbiraryResult { - Ok(FeeSigmoidConfig::<$t> { - m: <$t>::from_bits(<$p as Arbitrary<'a>>::arbitrary(u)?), - p: <$t>::from_bits(<$p as Arbitrary<'a>>::arbitrary(u)?), - n: <$t>::from_bits(<$p as Arbitrary<'a>>::arbitrary(u)?), - initial_fee: <$t>::from_bits(<$p as Arbitrary<'a>>::arbitrary(u)?), - min_revenue: <$t>::from_bits(<$p as Arbitrary<'a>>::arbitrary(u)?), - }) - } - - #[inline] - fn size_hint(_depth: usize) -> (usize, Option) { - let bytecount = mem::size_of::<$t>(); - (bytecount * 5, Some(bytecount * 5)) - } - } - }; -} - -cfg_if::cfg_if! { - if #[cfg(feature = "arbitrary")] { - impl_arbitrary_for_fee_sigmoid_config! {FixedI32, LeEqU32, i32} - impl_arbitrary_for_fee_sigmoid_config! {FixedI64, LeEqU64, i64} - impl_arbitrary_for_fee_sigmoid_config! {FixedI128, LeEqU128, i128} - } -} - -impl Default for FeeSigmoidConfig -where - FS: FixedSigned + LossyFrom> + LossyFrom>, -{ - fn default() -> Self { - Self { - m: M.lossy_into(), - p: P.lossy_into(), - n: N.lossy_into(), - // Only case this can panic is, when INITIAL_FEE is >= 1.0 and FS integer bits < 2 - initial_fee: convert_to_signed::, FS>(INITIAL_FEE.lossy_into()).unwrap(), - // Only case this can panic is, when MIN_REVENUE is >= 1.0 and FS integer bits < 2 - min_revenue: convert_to_signed::, FS>(MINIMAL_REVENUE.lossy_into()) - .unwrap(), - } - } -} - -/// Offers an implementation of `z(r)` as described in [Dynamic Automated Market Making] paper from -/// Andrew Nguyed et al. based on a predetermined set of -/// [configuration values](struct@FeeSigmoidConfig) -/// -/// [Dynamic Automated Market Making]: https://files.kyber.network/DMM-Feb21.pdf -#[derive(Clone, Decode, Default, Encode, Eq, MaxEncodedLen, PartialEq, RuntimeDebug, TypeInfo)] -pub struct FeeSigmoid -where - FS: FixedSigned + LossyFrom> + LossyFrom>, -{ - /// The constants used during the fee calculations. - pub config: FeeSigmoidConfig, -} - -#[cfg(feature = "arbitrary")] -macro_rules! impl_arbitrary_for_fee_sigmoid { - ( $t:ident, $LeEqU:ident, $p:ty ) => { - impl<'a, Frac> Arbitrary<'a> for FeeSigmoid<$t> - where - Frac: $LeEqU, - $t: FixedSigned - + LossyFrom> - + PartialOrd - + LossyFrom>, - { - fn arbitrary(u: &mut Unstructured<'a>) -> ArbiraryResult { - Ok(FeeSigmoid::new(> as Arbitrary<'a>>::arbitrary(u)?)) - } - - #[inline] - fn size_hint(depth: usize) -> (usize, Option) { - > as Arbitrary<'a>>::size_hint(depth) - } - } - }; -} - -#[cfg(feature = "arbitrary")] -cfg_if::cfg_if! { - if #[cfg(feature = "arbitrary")] { - impl_arbitrary_for_fee_sigmoid! {FixedI32, LeEqU32, i32} - impl_arbitrary_for_fee_sigmoid! {FixedI64, LeEqU64, i64} - impl_arbitrary_for_fee_sigmoid! {FixedI128, LeEqU128, i128} - } -} - -impl FeeSigmoid -where - FS: FixedSigned + LossyFrom> + LossyFrom>, -{ - /// Create a new `FeeSigmoid` instance based on a [`FeeSigmoidConfig`](struct@FeeSigmoidConfig) - /// configuration. Use `default()` if uncertain which values to use. - /// - /// # Arguments - /// - /// * `config`: Parameters used during the fee calculation. - pub fn new(config: FeeSigmoidConfig) -> Self { - Self { config } - } -} - -impl Fee for FeeSigmoid -where - FS: FixedSigned + LossyFrom> + PartialOrd + LossyFrom>, -{ - type FS = FS; - - /// Calculate fee: min(`min_revenue`, `initial_fee` + z(r)) - /// - /// # Arguments - /// - /// * `r`: Some kind of information about the market, for example an ema. - /// - /// [z(r)]: https://files.kyber.network/DMM-Feb21.pdf - fn calculate_fee(&self, r: Self::FS) -> Result { - let r_minus_n = if let Some(res) = r.checked_sub(self.config.n) { - res - } else { - return Err("[FeeSigmoid] Overflow during calculation: r - n"); - }; - - let numerator = if let Some(res) = r_minus_n.checked_mul(self.config.m) { - res - } else { - return Err("[FeeSigmoid] Overflow during calculation: m * (r-n)"); - }; - - let r_minus_n_squared = if let Some(res) = r_minus_n.checked_mul(r_minus_n) { - res - } else { - return Err("[FeeSigmoid] Overflow during calculation: (r-n)^2"); - }; - - let p_plus_r_minus_n_squared = - if let Some(res) = self.config.p.checked_add(r_minus_n_squared) { - res - } else { - return Err("[FeeSigmoid] Overflow during calculation: p + (r-n)^2"); - }; - - let denominator = sqrt::(p_plus_r_minus_n_squared)?; - - let sigmoid_result = if let Some(res) = numerator.checked_div(denominator) { - res - } else { - return Err("[FeeSigmoid] Overflow during calculation: numerator / denominator"); - }; - - let result = if let Some(res) = sigmoid_result.checked_add(self.config.initial_fee) { - res - } else { - return Err("[FeeSigmoid] initial_fee + sigmoid_result"); - }; - - if self.config.min_revenue >= result { - return Ok(self.config.min_revenue); - } - - Ok(result) - } - - /// Return the minimum fee - fn minimum_fee(&self) -> Self::FS { - self.config.min_revenue - } -} diff --git a/zrml/rikiddo/src/utils.rs b/zrml/rikiddo/src/utils.rs deleted file mode 100644 index 280416190..000000000 --- a/zrml/rikiddo/src/utils.rs +++ /dev/null @@ -1,230 +0,0 @@ -// 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 . - -//! This module contains utility functions for creating commonly used -//! fixed point type constants. - -use super::traits::{FromFixedDecimal, FromFixedToDecimal, IntoFixedDecimal, IntoFixedFromDecimal}; -use alloc::{borrow::ToOwned, string::ToString}; -use core::{cmp::Ordering, convert::TryFrom}; -use substrate_fixed::{ - traits::{Fixed, FixedSigned, FixedUnsigned, LossyFrom, LossyInto, ToFixed}, - types::extra::{U127, U128}, - FixedI128, FixedU128, ParseFixedError, -}; - -/// Create a fixed point number that represents 0 (zero). -pub fn fixed_zero() -> Result { - if let Some(res) = FixedType::checked_from_num(0) { - Ok(res) - } else { - Err("Unexpectedly failed to convert zero to fixed point type") - } -} - -/// Return the maximum value of FixedType as u128. -pub fn max_value_u128() -> Result { - if let Some(res) = FixedType::max_value().int().checked_to_num() { - Ok(res) - } else { - Err("Unexpectedly failed to convert max_value of fixed point type to u128") - } -} - -/// Returns integer part of `FROM`, if the msb is not set and and num does it into `FROM`. -fn convert_common(num: FROM) -> Result { - // Check if number is negatie - if num < fixed_zero::()? { - return Err("Cannot convert negative signed number into unsigned number"); - } - - // PartialOrd is bugged, therefore the workaround - // https://github.com/encointer/substrate-fixed/issues/9 - let num_u128: u128 = num.int().checked_to_num().ok_or("Conversion from FROM to u128 failed")?; - if max_value_u128::()? < num_u128 { - return Err("Fixed point conversion failed: FROM type does not fit in TO type"); - } - - num_u128.checked_to_fixed().ok_or("Integer part of fixed point number does not fit into u128") -} - -/// Converts an unsigned fixed point number into a signed fixed point number (fallible). -pub fn convert_to_signed>>( - num: FROM, -) -> Result { - let integer_part: TO = convert_common(num)?; - let fractional_part: FixedI128 = num - .frac() - .checked_to_fixed() - .ok_or("Fraction of fixed point number unexpectedly does not fit into FixedI128")?; - - if let Some(res) = integer_part.checked_add(fractional_part.lossy_into()) { - Ok(res) - } else { - // This error should be impossible to reach. - Err("Something went wrong during FixedUnsigned to FixedSigned type conversion") - } -} - -/// Converts a signed fixed point number into an unsigned fixed point number (fallible). -pub fn convert_to_unsigned>>( - num: FROM, -) -> Result { - // We can safely cast because until here we know that the msb is not set. - let integer_part: TO = convert_common(num)?; - let fractional_part: FixedU128 = num - .frac() - .checked_to_fixed() - .ok_or("Fraction of fixed point number unexpectedly does not fit into FixedU128")?; - if let Some(res) = integer_part.checked_add(fractional_part.lossy_into()) { - Ok(res) - } else { - // This error should be impossible to reach. - Err("Something went wrong during FixedSigned to FixedUnsigned type conversion") - } -} - -impl> FromFixedDecimal for F { - /// Craft a `Fixed` type from a fixed point decimal number of type `N` - fn from_fixed_decimal(decimal: N, places: u8) -> Result { - let decimal_u128 = decimal.into(); - let mut decimal_string = decimal_u128.to_string(); - - if decimal_string.len() <= places as usize { - // This can never underflow (places >= len). Saturating subtraction to satisfy clippy. - decimal_string = "0.".to_owned() - + &"0".repeat((places as usize).saturating_sub(decimal_string.len())) - + &decimal_string; - } else { - // This can never underflow (len >= places). Saturating subtraction to satisfy clippy. - decimal_string.insert(decimal_string.len().saturating_sub(places as usize), '.'); - } - - F::from_str(&decimal_string) - } -} - -impl IntoFixedFromDecimal for N -where - F: Fixed + FromFixedDecimal, - N: Into, -{ - /// Converts a fixed point decimal number into `Fixed` type (e.g. `Balance` -> `Fixed`). - fn to_fixed_from_fixed_decimal(self, places: u8) -> Result { - F::from_fixed_decimal(self, places) - } -} - -impl> FromFixedToDecimal for N { - /// Craft a fixed point decimal number from a `Fixed` type (e.g. `Fixed` -> `Balance`). - fn from_fixed_to_fixed_decimal(fixed: F, places: u8) -> Result { - if places == 0 { - let mut result = fixed - .checked_to_num::() - .ok_or("The fixed point number does not fit into a u128")?; - - // Arithmetic rounding (+1 if >= 0.5) - if F::frac_nbits() > 0 { - let two = F::checked_from_num(2) - .ok_or("Unexpectedly failed to convert 2 to fixed point number")?; - let one = F::checked_from_num(1) - .ok_or("Unexpectedly failed to convert 1 to fixed point number")?; - let half = - one.checked_div(two).ok_or("Unexpected overflow when dividing one by two")?; - - if fixed.frac() >= half { - result = result.saturating_add(1); - } - } - - if let Ok(res) = N::try_from(result) { - return Ok(res); - } else { - return Err( - "The parsed fixed decimal representation does not fit into the target type" - ); - } - } - - let mut fixed_str = fixed.to_string(); - let fixed_frac = fixed.frac(); - - let places_usize: usize = places.into(); - - if fixed_frac == 0 { - // Add `places` times 0 to pad all remaining fractional decimal places - fixed_str += &"0".repeat(places_usize); - } else { - let frac_string = fixed_frac.to_string(); - let frac_str = frac_string.get(2..).unwrap_or_default(); - - match frac_str.len().cmp(&places_usize) { - Ordering::Less => { - fixed_str.retain(|c| c != '.'); - // Padding to the right side up to `places`. Cannot underflow. - fixed_str += &"0".repeat(places_usize.saturating_sub(frac_str.len())); - } - Ordering::Greater => { - // Cutting down to `places` + arithmetic rounding of the last digit - let frac_plus_one_digit_str = - frac_str.get(..places_usize.saturating_add(1)).unwrap_or_default(); - - if let Ok(mut res) = frac_plus_one_digit_str.parse::() { - let last_digit = res % 10; - res /= 10; - - if last_digit >= 5 { - res = res.saturating_add(1); - } - - fixed_str = fixed.int().to_string() + &res.to_string() - } else { - // Impossible unless there is a bug in Fixed's to_string() - return Err( - "Error parsing the string representation of the fixed point number" - ); - }; - } - Ordering::Equal => fixed_str.retain(|c| c != '.'), - }; - } - - let result = if let Ok(res) = fixed_str[..].parse::() { - res - } else { - // Impossible unless there is a bug in Fixed's to_string() - return Err("Error parsing the string representation of the fixed point number"); - }; - - if let Ok(res) = N::try_from(result) { - Ok(res) - } else { - Err("The parsed fixed decimal representation does not fit into the target type") - } - } -} - -impl IntoFixedDecimal for F -where - F: Fixed, - N: FromFixedToDecimal, -{ - /// Converts a `Fixed` type into a fixed point decimal number. - fn to_fixed_decimal(self, places: u8) -> Result { - N::from_fixed_to_fixed_decimal(self, places) - } -} diff --git a/zrml/simple-disputes/Cargo.toml b/zrml/simple-disputes/Cargo.toml deleted file mode 100644 index 2d3ca8a22..000000000 --- a/zrml/simple-disputes/Cargo.toml +++ /dev/null @@ -1,45 +0,0 @@ -[dependencies] -frame-benchmarking = { workspace = true, optional = true } -frame-support = { workspace = true } -frame-system = { workspace = true } -orml-traits = { workspace = true } -parity-scale-codec = { workspace = true, features = ["derive", "max-encoded-len"] } -scale-info = { workspace = true, features = ["derive"] } -sp-runtime = { workspace = true } -zeitgeist-primitives = { workspace = true } -zrml-market-commons = { workspace = true } - -[dev-dependencies] -env_logger = { workspace = true } -orml-currencies = { workspace = true, features = ["default"] } -orml-tokens = { workspace = true, features = ["default"] } -pallet-balances = { workspace = true, features = ["default", "insecure_zero_ed"] } -pallet-timestamp = { workspace = true, features = ["default"] } -sp-io = { workspace = true, features = ["default"] } -zeitgeist-primitives = { workspace = true, features = ["mock", "default"] } - -[features] -default = ["std"] -runtime-benchmarks = [ - "frame-benchmarking/runtime-benchmarks", - "frame-support/runtime-benchmarks", - "frame-system/runtime-benchmarks", -] -std = [ - "frame-benchmarking?/std", - "frame-support/std", - "frame-system/std", - "parity-scale-codec/std", - "sp-runtime/std", - "zeitgeist-primitives/std", - "zrml-market-commons/std", -] -try-runtime = [ - "frame-support/try-runtime", -] - -[package] -authors = ["Zeitgeist PM "] -edition.workspace = true -name = "zrml-simple-disputes" -version = "0.5.3" diff --git a/zrml/simple-disputes/README.md b/zrml/simple-disputes/README.md deleted file mode 100644 index e178939ba..000000000 --- a/zrml/simple-disputes/README.md +++ /dev/null @@ -1,3 +0,0 @@ -# Simple Disputes Module - -Manages market disputes and resolutions. diff --git a/zrml/simple-disputes/src/benchmarks.rs b/zrml/simple-disputes/src/benchmarks.rs deleted file mode 100644 index 8df924268..000000000 --- a/zrml/simple-disputes/src/benchmarks.rs +++ /dev/null @@ -1,177 +0,0 @@ -// Copyright 2023-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 . - -#![allow( - // Auto-generated code is a no man's land - clippy::arithmetic_side_effects -)] -#![allow(clippy::type_complexity)] -#![cfg(feature = "runtime-benchmarks")] - -use crate::Pallet as SimpleDisputes; - -use super::*; -use frame_benchmarking::{account, benchmarks, whitelisted_caller}; -use frame_support::{ - dispatch::RawOrigin, - traits::{Currency, Get, Imbalance}, -}; -use frame_system::pallet_prelude::BlockNumberFor; -use sp_runtime::traits::{One, Saturating}; -use zrml_market_commons::MarketCommonsPalletApi; - -fn fill_disputes(market_id: MarketIdOf, d: u32) { - for i in 0..d { - let now = >::block_number(); - let disputor = account("disputor", i, 0); - let bond = default_outcome_bond::(i as usize); - let _ = T::Currency::deposit_creating(&disputor, bond); - let outcome = OutcomeReport::Scalar((2 + i).into()); - SimpleDisputes::::suggest_outcome( - RawOrigin::Signed(disputor).into(), - market_id, - outcome, - ) - .unwrap(); - >::set_block_number(now.saturating_add(BlockNumberFor::::one())); - } -} - -benchmarks! { - suggest_outcome { - let d in 1..(T::MaxDisputes::get() - 1); - let r in 1..63; - let e in 1..63; - - let caller: T::AccountId = whitelisted_caller(); - let market_id = 0u32.into(); - let market = market_mock::(); - T::MarketCommons::push_market(market.clone()).unwrap(); - - fill_disputes::(market_id, d); - let disputes = Disputes::::get(market_id); - 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(); - T::DisputeResolution::add_auto_resolve(&id, auto_resolve).unwrap(); - } - - let now = >::block_number(); - - 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(); - T::DisputeResolution::add_auto_resolve(&id, dispute_duration_ends_at_block).unwrap(); - } - - let outcome = OutcomeReport::Scalar(1); - let bond = default_outcome_bond::(T::MaxDisputes::get() as usize); - let _ = T::Currency::deposit_creating(&caller, bond); - }: _(RawOrigin::Signed(caller.clone()), market_id, outcome) - - on_dispute_weight { - let market_id = 0u32.into(); - let market = market_mock::(); - T::MarketCommons::push_market(market.clone()).unwrap(); - }: { - SimpleDisputes::::on_dispute(&market_id, &market).unwrap(); - } - - on_resolution_weight { - let d in 1..T::MaxDisputes::get(); - - let market_id = 0u32.into(); - let market = market_mock::(); - T::MarketCommons::push_market(market.clone()).unwrap(); - - fill_disputes::(market_id, d); - }: { - SimpleDisputes::::on_resolution(&market_id, &market).unwrap(); - } - - exchange_weight { - let d in 1..T::MaxDisputes::get(); - - let market_id = 0u32.into(); - let market = market_mock::(); - T::MarketCommons::push_market(market.clone()).unwrap(); - - fill_disputes::(market_id, d); - - let outcome = OutcomeReport::Scalar(1); - let imb = NegativeImbalanceOf::::zero(); - }: { - SimpleDisputes::::exchange(&market_id, &market, &outcome, imb).unwrap(); - } - - get_auto_resolve_weight { - let d in 1..T::MaxDisputes::get(); - - let market_id = 0u32.into(); - let market = market_mock::(); - T::MarketCommons::push_market(market.clone()).unwrap(); - - fill_disputes::(market_id, d); - }: { - SimpleDisputes::::get_auto_resolve(&market_id, &market); - } - - has_failed_weight { - let d in 1..T::MaxDisputes::get(); - - let market_id = 0u32.into(); - let market = market_mock::(); - T::MarketCommons::push_market(market.clone()).unwrap(); - - fill_disputes::(market_id, d); - }: { - SimpleDisputes::::has_failed(&market_id, &market).unwrap(); - } - - on_global_dispute_weight { - let d in 1..T::MaxDisputes::get(); - - let market_id = 0u32.into(); - let market = market_mock::(); - T::MarketCommons::push_market(market.clone()).unwrap(); - - fill_disputes::(market_id, d); - }: { - SimpleDisputes::::on_global_dispute(&market_id, &market).unwrap(); - } - - clear_weight { - let d in 1..T::MaxDisputes::get(); - - let market_id = 0u32.into(); - let market = market_mock::(); - T::MarketCommons::push_market(market.clone()).unwrap(); - - fill_disputes::(market_id, d); - }: { - SimpleDisputes::::clear(&market_id, &market).unwrap(); - } - - impl_benchmark_test_suite!( - SimpleDisputes, - crate::mock::ExtBuilder::default().build(), - crate::mock::Runtime, - ); -} diff --git a/zrml/simple-disputes/src/lib.rs b/zrml/simple-disputes/src/lib.rs deleted file mode 100644 index 02f4822c6..000000000 --- a/zrml/simple-disputes/src/lib.rs +++ /dev/null @@ -1,580 +0,0 @@ -// 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 . - -#![doc = include_str!("../README.md")] -#![cfg_attr(not(feature = "std"), no_std)] - -extern crate alloc; - -#[cfg(feature = "runtime-benchmarks")] -mod benchmarks; -mod mock; -mod simple_disputes_pallet_api; -mod tests; -pub mod weights; - -pub use pallet::*; -pub use simple_disputes_pallet_api::SimpleDisputesPalletApi; -use zeitgeist_primitives::{ - traits::{DisputeApi, DisputeMaxWeightApi, DisputeResolutionApi}, - types::{ - GlobalDisputeItem, Market, MarketDispute, MarketDisputeMechanism, MarketStatus, - OutcomeReport, Report, ResultWithWeightInfo, - }, -}; - -#[frame_support::pallet] -mod pallet { - use super::*; - use crate::{weights::WeightInfoZeitgeist, SimpleDisputesPalletApi}; - use alloc::vec::Vec; - use core::marker::PhantomData; - use frame_support::{ - dispatch::DispatchResult, - ensure, - pallet_prelude::{ - Blake2_128Concat, ConstU32, DispatchResultWithPostInfo, StorageMap, ValueQuery, Weight, - }, - traits::{Currency, Get, Hooks, Imbalance, IsType, NamedReservableCurrency}, - transactional, BoundedVec, PalletId, - }; - use frame_system::pallet_prelude::{BlockNumberFor, *}; - use sp_runtime::{ - traits::{CheckedDiv, Saturating, Zero}, - DispatchError, SaturatedConversion, - }; - use zrml_market_commons::MarketCommonsPalletApi; - - #[pallet::config] - pub trait Config: frame_system::Config { - type Currency: NamedReservableCurrency; - - /// Event - type RuntimeEvent: From> + IsType<::RuntimeEvent>; - - /// The base amount of currency that must be bonded in order to create a dispute. - #[pallet::constant] - type OutcomeBond: Get>; - - type DisputeResolution: DisputeResolutionApi< - AccountId = Self::AccountId, - BlockNumber = BlockNumberFor, - MarketId = MarketIdOf, - Moment = MomentOf, - >; - - /// The additional amount of currency that must be bonded when creating a subsequent - /// dispute. - #[pallet::constant] - type OutcomeFactor: Get>; - - /// The identifier of individual markets. - type MarketCommons: MarketCommonsPalletApi< - AccountId = Self::AccountId, - BlockNumber = BlockNumberFor, - Balance = BalanceOf, - >; - - /// The maximum number of disputes allowed on any single market. - #[pallet::constant] - type MaxDisputes: Get; - - /// The pallet identifier. - #[pallet::constant] - type PalletId: Get; - - /// Weights generated by benchmarks - type WeightInfo: WeightInfoZeitgeist; - } - - pub(crate) type BalanceOf = - <::Currency as Currency<::AccountId>>::Balance; - pub(crate) type NegativeImbalanceOf = <::Currency as Currency< - ::AccountId, - >>::NegativeImbalance; - pub type MarketIdOf = <::MarketCommons as MarketCommonsPalletApi>::MarketId; - pub(crate) type MomentOf = <::MarketCommons as MarketCommonsPalletApi>::Moment; - pub(crate) type MarketOf = Market< - ::AccountId, - BalanceOf, - BlockNumberFor, - MomentOf, - MarketIdOf, - >; - pub(crate) type DisputesOf = BoundedVec< - MarketDispute<::AccountId, BlockNumberFor, BalanceOf>, - ::MaxDisputes, - >; - pub type CacheSize = ConstU32<64>; - - #[pallet::pallet] - pub struct Pallet(PhantomData); - - /// For each market, this holds the dispute information for each dispute that's - /// been issued. - #[pallet::storage] - pub type Disputes = - StorageMap<_, Blake2_128Concat, MarketIdOf, DisputesOf, ValueQuery>; - - #[pallet::event] - #[pallet::generate_deposit(fn deposit_event)] - pub enum Event - where - T: Config, - { - OutcomeReserved { - market_id: MarketIdOf, - dispute: MarketDispute, BalanceOf>, - }, - } - - #[pallet::error] - pub enum Error { - /// 1. Any resolution must either have a `Disputed` or `Reported` market status - /// 2. If status is `Disputed`, then at least one dispute must exist - InvalidMarketStatus, - /// On dispute or resolution, someone tried to pass a non-simple-disputes market type - MarketDoesNotHaveSimpleDisputesMechanism, - StorageOverflow, - OutcomeMismatch, - CannotDisputeSameOutcome, - MarketIsNotReported, - /// The maximum number of disputes has been reached. - MaxDisputesReached, - } - - #[pallet::hooks] - impl Hooks> for Pallet {} - - #[pallet::call] - impl Pallet { - #[pallet::call_index(0)] - #[pallet::weight(T::WeightInfo::suggest_outcome( - T::MaxDisputes::get(), - CacheSize::get(), - CacheSize::get(), - ))] - #[transactional] - pub fn suggest_outcome( - origin: OriginFor, - #[pallet::compact] market_id: MarketIdOf, - outcome: OutcomeReport, - ) -> DispatchResultWithPostInfo { - let who = ensure_signed(origin)?; - let market = T::MarketCommons::market(&market_id)?; - Self::ensure_dispute_mechanism(&market)?; - ensure!(market.status == MarketStatus::Disputed, Error::::InvalidMarketStatus); - ensure!(market.matches_outcome_report(&outcome), Error::::OutcomeMismatch); - let report = market.report.as_ref().ok_or(Error::::MarketIsNotReported)?; - - let now = >::block_number(); - let disputes = Disputes::::get(market_id); - let num_disputes: u32 = disputes.len().saturated_into(); - - Self::ensure_can_not_dispute_the_same_outcome(&disputes, report, &outcome)?; - Self::ensure_disputes_does_not_exceed_max_disputes(num_disputes)?; - - let bond = default_outcome_bond::(disputes.len()); - - T::Currency::reserve_named(&Self::reserve_id(), &who, bond)?; - - let market_dispute = MarketDispute { at: now, by: who, outcome, bond }; - >::try_mutate(market_id, |disputes| { - disputes.try_push(market_dispute.clone()).map_err(|_| >::StorageOverflow) - })?; - - // each dispute resets dispute_duration - let r = Self::remove_auto_resolve(disputes.as_slice(), &market_id, &market); - let dispute_duration_ends_at_block = - now.saturating_add(market.deadlines.dispute_duration); - let e = - T::DisputeResolution::add_auto_resolve(&market_id, dispute_duration_ends_at_block)?; - - Self::deposit_event(Event::OutcomeReserved { market_id, dispute: market_dispute }); - - Ok((Some(T::WeightInfo::suggest_outcome(num_disputes, r, e))).into()) - } - } - - impl Pallet { - #[inline] - pub fn reserve_id() -> [u8; 8] { - T::PalletId::get().0 - } - - fn ensure_can_not_dispute_the_same_outcome( - disputes: &[MarketDispute, BalanceOf>], - report: &Report>, - outcome: &OutcomeReport, - ) -> DispatchResult { - if let Some(last_dispute) = disputes.last() { - ensure!(&last_dispute.outcome != outcome, Error::::CannotDisputeSameOutcome); - } else { - ensure!(&report.outcome != outcome, Error::::CannotDisputeSameOutcome); - } - - Ok(()) - } - - #[inline] - fn ensure_disputes_does_not_exceed_max_disputes(num_disputes: u32) -> DispatchResult { - ensure!(num_disputes < T::MaxDisputes::get(), Error::::MaxDisputesReached); - Ok(()) - } - - #[inline] - fn ensure_dispute_mechanism(market: &MarketOf) -> DispatchResult { - ensure!( - market.dispute_mechanism == Some(MarketDisputeMechanism::SimpleDisputes), - Error::::MarketDoesNotHaveSimpleDisputesMechanism - ); - Ok(()) - } - - fn get_auto_resolve( - disputes: &[MarketDispute, BalanceOf>], - market: &MarketOf, - ) -> Option> { - disputes.last().map(|last_dispute| { - last_dispute.at.saturating_add(market.deadlines.dispute_duration) - }) - } - - fn remove_auto_resolve( - disputes: &[MarketDispute, BalanceOf>], - market_id: &MarketIdOf, - market: &MarketOf, - ) -> u32 { - if let Some(dispute_duration_ends_at_block) = Self::get_auto_resolve(disputes, market) { - return T::DisputeResolution::remove_auto_resolve( - market_id, - dispute_duration_ends_at_block, - ); - } - 0u32 - } - } - - impl DisputeMaxWeightApi for Pallet - where - T: Config, - { - fn on_dispute_max_weight() -> Weight { - T::WeightInfo::on_dispute_weight() - } - - fn on_resolution_max_weight() -> Weight { - T::WeightInfo::on_resolution_weight(T::MaxDisputes::get()) - } - - fn exchange_max_weight() -> Weight { - T::WeightInfo::exchange_weight(T::MaxDisputes::get()) - } - - fn get_auto_resolve_max_weight() -> Weight { - T::WeightInfo::get_auto_resolve_weight(T::MaxDisputes::get()) - } - - fn has_failed_max_weight() -> Weight { - T::WeightInfo::has_failed_weight(T::MaxDisputes::get()) - } - - fn on_global_dispute_max_weight() -> Weight { - T::WeightInfo::on_global_dispute_weight(T::MaxDisputes::get()) - } - - fn clear_max_weight() -> Weight { - T::WeightInfo::clear_weight(T::MaxDisputes::get()) - } - } - - impl DisputeApi for Pallet - where - T: Config, - { - type AccountId = T::AccountId; - type Balance = BalanceOf; - type NegativeImbalance = NegativeImbalanceOf; - type BlockNumber = BlockNumberFor; - type MarketId = MarketIdOf; - type Moment = MomentOf; - type Origin = T::RuntimeOrigin; - - fn on_dispute( - _: &Self::MarketId, - market: &MarketOf, - ) -> Result, DispatchError> { - Self::ensure_dispute_mechanism(market)?; - - let res = - ResultWithWeightInfo { result: (), weight: T::WeightInfo::on_dispute_weight() }; - - Ok(res) - } - - fn on_resolution( - market_id: &Self::MarketId, - market: &MarketOf, - ) -> Result>, DispatchError> { - Self::ensure_dispute_mechanism(market)?; - ensure!(market.status == MarketStatus::Disputed, Error::::InvalidMarketStatus); - - let disputes = Disputes::::get(market_id); - - let last_dispute = match disputes.last() { - Some(l) => l, - // if there are no disputes, then the market is resolved with the default report - None => { - return Ok(ResultWithWeightInfo { - result: None, - weight: T::WeightInfo::on_resolution_weight(disputes.len() as u32), - }); - } - }; - - let res = ResultWithWeightInfo { - result: Some(last_dispute.outcome.clone()), - weight: T::WeightInfo::on_resolution_weight(disputes.len() as u32), - }; - - Ok(res) - } - - fn exchange( - market_id: &Self::MarketId, - market: &MarketOf, - resolved_outcome: &OutcomeReport, - mut overall_imbalance: NegativeImbalanceOf, - ) -> Result>, DispatchError> { - Self::ensure_dispute_mechanism(market)?; - ensure!(market.status == MarketStatus::Disputed, Error::::InvalidMarketStatus); - - let disputes = Disputes::::get(market_id); - - let mut correct_reporters: Vec = Vec::new(); - - for dispute in disputes.iter() { - if &dispute.outcome == resolved_outcome { - let missing = T::Currency::unreserve_named( - &Self::reserve_id(), - &dispute.by, - dispute.bond.saturated_into::().saturated_into(), - ); - debug_assert!( - missing.is_zero(), - "Could not unreserve all of the amount. reserve_id: {:?}, who: {:?}, \ - amount: {:?}, missing: {:?}", - Self::reserve_id(), - &dispute.by, - dispute.bond.saturated_into::(), - missing, - ); - - correct_reporters.push(dispute.by.clone()); - } else { - let (imbalance, missing) = T::Currency::slash_reserved_named( - &Self::reserve_id(), - &dispute.by, - dispute.bond.saturated_into::().saturated_into(), - ); - debug_assert!( - missing.is_zero(), - "Could not slash all of the amount. reserve_id {:?}, who: {:?}, amount: \ - {:?}.", - &Self::reserve_id(), - &dispute.by, - dispute.bond.saturated_into::(), - ); - overall_imbalance.subsume(imbalance); - } - } - - // Fold all the imbalances into one and reward the correct reporters. The - // number of correct reporters might be zero if the market defaults to the - // report after abandoned dispute. In that case, the rewards remain slashed. - if let Some(reward_per_each) = - overall_imbalance.peek().checked_div(&correct_reporters.len().saturated_into()) - { - for correct_reporter in &correct_reporters { - let (actual_reward, leftover) = overall_imbalance.split(reward_per_each); - overall_imbalance = leftover; - T::Currency::resolve_creating(correct_reporter, actual_reward); - } - } - - Disputes::::remove(market_id); - - let res = ResultWithWeightInfo { - result: overall_imbalance, - weight: T::WeightInfo::exchange_weight(disputes.len() as u32), - }; - - Ok(res) - } - - fn get_auto_resolve( - market_id: &Self::MarketId, - market: &MarketOf, - ) -> ResultWithWeightInfo> { - if market.dispute_mechanism != Some(MarketDisputeMechanism::SimpleDisputes) { - return ResultWithWeightInfo { - result: None, - weight: T::WeightInfo::get_auto_resolve_weight(T::MaxDisputes::get()), - }; - } - - let disputes = Disputes::::get(market_id); - - let res = ResultWithWeightInfo { - result: Self::get_auto_resolve(disputes.as_slice(), market), - weight: T::WeightInfo::get_auto_resolve_weight(disputes.len() as u32), - }; - - res - } - - fn has_failed( - market_id: &Self::MarketId, - market: &MarketOf, - ) -> Result, DispatchError> { - Self::ensure_dispute_mechanism(market)?; - let disputes = >::get(market_id); - let disputes_len = disputes.len() as u32; - - let res = ResultWithWeightInfo { - result: disputes_len == T::MaxDisputes::get(), - weight: T::WeightInfo::has_failed_weight(disputes_len), - }; - - Ok(res) - } - - fn on_global_dispute( - market_id: &Self::MarketId, - market: &MarketOf, - ) -> Result< - ResultWithWeightInfo>>, - DispatchError, - > { - Self::ensure_dispute_mechanism(market)?; - - let disputes_len = >::decode_len(market_id).unwrap_or(0) as u32; - - let res = ResultWithWeightInfo { - result: >::get(market_id) - .iter() - .map(|dispute| GlobalDisputeItem { - outcome: dispute.outcome.clone(), - owner: dispute.by.clone(), - initial_vote_amount: dispute.bond, - }) - .collect(), - weight: T::WeightInfo::on_global_dispute_weight(disputes_len), - }; - - Ok(res) - } - - fn clear( - market_id: &Self::MarketId, - market: &MarketOf, - ) -> Result, DispatchError> { - Self::ensure_dispute_mechanism(market)?; - - let mut disputes_len = 0u32; - // `Disputes` is emtpy unless the market is disputed, so this is just a defensive - // check. - if market.status == MarketStatus::Disputed { - disputes_len = Disputes::::decode_len(market_id).unwrap_or(0) as u32; - for dispute in Disputes::::take(market_id).iter() { - let missing = T::Currency::unreserve_named( - &Self::reserve_id(), - &dispute.by, - dispute.bond.saturated_into::().saturated_into(), - ); - debug_assert!( - missing.is_zero(), - "Could not unreserve all of the amount. reserve_id: {:?}, who: {:?}, \ - amount: {:?}, missing: {:?}", - Self::reserve_id(), - &dispute.by, - dispute.bond.saturated_into::(), - missing, - ); - } - } - - let res = ResultWithWeightInfo { - result: (), - weight: T::WeightInfo::clear_weight(disputes_len), - }; - - Ok(res) - } - } - - impl SimpleDisputesPalletApi for Pallet where T: Config {} - - // No-one can bound more than BalanceOf, therefore, this functions saturates - pub fn default_outcome_bond(n: usize) -> BalanceOf - where - T: Config, - { - T::OutcomeBond::get().saturating_add( - T::OutcomeFactor::get().saturating_mul(n.saturated_into::().into()), - ) - } -} - -#[cfg(feature = "runtime-benchmarks")] -pub(crate) fn market_mock() -> MarketOf -where - T: crate::Config, -{ - use frame_support::traits::Get; - use sp_runtime::{traits::AccountIdConversion, SaturatedConversion}; - use zeitgeist_primitives::types::{Asset, 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(), - creator: T::PalletId::get().into_account_truncating(), - market_type: zeitgeist_primitives::types::MarketType::Scalar(0..=100), - dispute_mechanism: Some(MarketDisputeMechanism::SimpleDisputes), - metadata: Default::default(), - oracle: T::PalletId::get().into_account_truncating(), - period: zeitgeist_primitives::types::MarketPeriod::Block(Default::default()), - deadlines: zeitgeist_primitives::types::Deadlines { - grace_period: 1_u32.into(), - oracle_duration: 1_u32.into(), - dispute_duration: 42_u32.into(), - }, - report: Some(zeitgeist_primitives::types::Report { - outcome: OutcomeReport::Scalar(0), - at: 0u64.saturated_into(), - by: T::PalletId::get().into_account_truncating(), - }), - resolved_outcome: None, - scoring_rule: ScoringRule::AmmCdaHybrid, - status: zeitgeist_primitives::types::MarketStatus::Disputed, - bonds: MarketBonds::default(), - early_close: None, - } -} diff --git a/zrml/simple-disputes/src/mock.rs b/zrml/simple-disputes/src/mock.rs deleted file mode 100644 index 6e6268df3..000000000 --- a/zrml/simple-disputes/src/mock.rs +++ /dev/null @@ -1,224 +0,0 @@ -// 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 . - -#![cfg(test)] - -use crate::{self as zrml_simple_disputes}; -use frame_support::{ - construct_runtime, ord_parameter_types, - pallet_prelude::{DispatchError, Weight}, - traits::Everything, -}; -use frame_system::mocking::MockBlock; -use sp_runtime::{ - traits::{BlakeTwo256, IdentityLookup}, - BuildStorage, -}; -use zeitgeist_primitives::{ - constants::mock::{ - BlockHashCount, ExistentialDeposits, GetNativeCurrencyId, MaxDisputes, MaxLocks, - MaxReserves, MinimumPeriod, OutcomeBond, OutcomeFactor, SimpleDisputesPalletId, BASE, - }, - traits::{DisputeResolutionApi, MarketOfDisputeResolutionApi}, - types::{ - AccountIdTest, Amount, Balance, BasicCurrencyAdapter, BlockNumber, CurrencyId, Hash, - MarketId, Moment, - }, -}; - -pub const ALICE: AccountIdTest = 0; -pub const BOB: AccountIdTest = 1; -pub const CHARLIE: AccountIdTest = 2; -pub const DAVE: AccountIdTest = 3; -pub const EVE: AccountIdTest = 4; -pub const FRED: AccountIdTest = 5; -pub const SUDO: AccountIdTest = 69; - -pub const INITIAL_BALANCE: u128 = 1_000 * BASE; - -ord_parameter_types! { - pub const Sudo: AccountIdTest = SUDO; -} - -construct_runtime!( - pub enum Runtime { - AssetManager: orml_currencies, - Balances: pallet_balances, - MarketCommons: zrml_market_commons, - SimpleDisputes: zrml_simple_disputes, - System: frame_system, - Timestamp: pallet_timestamp, - Tokens: orml_tokens, - } -); - -// NoopResolution implements DisputeResolutionApi with no-ops. -pub struct NoopResolution; - -impl DisputeResolutionApi for NoopResolution { - type AccountId = AccountIdTest; - type Balance = Balance; - type BlockNumber = BlockNumber; - type MarketId = MarketId; - type Moment = Moment; - - fn resolve( - _market_id: &Self::MarketId, - _market: &MarketOfDisputeResolutionApi, - ) -> Result { - Ok(Weight::zero()) - } - - fn add_auto_resolve( - _market_id: &Self::MarketId, - _resolve_at: Self::BlockNumber, - ) -> Result { - Ok(0u32) - } - - fn auto_resolve_exists(_market_id: &Self::MarketId, _resolve_at: Self::BlockNumber) -> bool { - false - } - - fn remove_auto_resolve(_market_id: &Self::MarketId, _resolve_at: Self::BlockNumber) -> u32 { - 0u32 - } -} - -impl crate::Config for Runtime { - type Currency = Balances; - type RuntimeEvent = RuntimeEvent; - type DisputeResolution = NoopResolution; - type MarketCommons = MarketCommons; - type MaxDisputes = MaxDisputes; - type OutcomeBond = OutcomeBond; - type OutcomeFactor = OutcomeFactor; - type PalletId = SimpleDisputesPalletId; - type WeightInfo = zrml_simple_disputes::weights::WeightInfo; -} - -impl frame_system::Config for Runtime { - type AccountData = pallet_balances::AccountData; - type AccountId = AccountIdTest; - type BaseCallFilter = Everything; - type Block = MockBlock; - type BlockHashCount = BlockHashCount; - type BlockLength = (); - type BlockWeights = (); - type RuntimeCall = RuntimeCall; - type DbWeight = (); - type RuntimeEvent = RuntimeEvent; - type Hash = Hash; - type Hashing = BlakeTwo256; - type Lookup = IdentityLookup; - type Nonce = u64; - type MaxConsumers = frame_support::traits::ConstU32<16>; - type OnKilledAccount = (); - type OnNewAccount = (); - type RuntimeOrigin = RuntimeOrigin; - type PalletInfo = PalletInfo; - type SS58Prefix = (); - type SystemWeightInfo = (); - type Version = (); - type OnSetCode = (); -} - -impl pallet_balances::Config for Runtime { - type AccountStore = System; - type Balance = Balance; - type DustRemoval = (); - type FreezeIdentifier = (); - type RuntimeHoldReason = (); - type RuntimeEvent = RuntimeEvent; - type ExistentialDeposit = (); - type MaxHolds = (); - type MaxFreezes = (); - type MaxLocks = MaxLocks; - type MaxReserves = MaxReserves; - type ReserveIdentifier = [u8; 8]; - type WeightInfo = (); -} - -impl orml_currencies::Config for Runtime { - type GetNativeCurrencyId = GetNativeCurrencyId; - type MultiCurrency = Tokens; - type NativeCurrency = BasicCurrencyAdapter; - type WeightInfo = (); -} - -impl orml_tokens::Config for Runtime { - type Amount = Amount; - type Balance = Balance; - type CurrencyId = CurrencyId; - type DustRemovalWhitelist = Everything; - type RuntimeEvent = RuntimeEvent; - type ExistentialDeposits = ExistentialDeposits; - type MaxLocks = (); - type MaxReserves = MaxReserves; - type CurrencyHooks = (); - type ReserveIdentifier = [u8; 8]; - type WeightInfo = (); -} - -impl zrml_market_commons::Config for Runtime { - type Balance = Balance; - type MarketId = MarketId; - type Timestamp = Timestamp; -} - -impl pallet_timestamp::Config for Runtime { - type MinimumPeriod = MinimumPeriod; - type Moment = Moment; - type OnTimestampSet = (); - type WeightInfo = (); -} - -pub struct ExtBuilder { - balances: Vec<(AccountIdTest, Balance)>, -} - -impl Default for ExtBuilder { - fn default() -> Self { - Self { - balances: vec![ - (ALICE, INITIAL_BALANCE), - (BOB, INITIAL_BALANCE), - (CHARLIE, INITIAL_BALANCE), - (DAVE, INITIAL_BALANCE), - (EVE, INITIAL_BALANCE), - (FRED, INITIAL_BALANCE), - (SUDO, INITIAL_BALANCE), - ], - } - } -} - -impl ExtBuilder { - pub fn build(self) -> sp_io::TestExternalities { - let mut t = frame_system::GenesisConfig::::default().build_storage().unwrap(); - - // see the logs in tests when using `RUST_LOG=debug cargo test -- --nocapture` - let _ = env_logger::builder().is_test(true).try_init(); - - pallet_balances::GenesisConfig:: { balances: self.balances } - .assimilate_storage(&mut t) - .unwrap(); - - t.into() - } -} diff --git a/zrml/simple-disputes/src/simple_disputes_pallet_api.rs b/zrml/simple-disputes/src/simple_disputes_pallet_api.rs deleted file mode 100644 index f01588aaa..000000000 --- a/zrml/simple-disputes/src/simple_disputes_pallet_api.rs +++ /dev/null @@ -1,21 +0,0 @@ -// Copyright 2023 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 zeitgeist_primitives::traits::{DisputeApi, DisputeMaxWeightApi}; - -pub trait SimpleDisputesPalletApi: DisputeApi + DisputeMaxWeightApi {} diff --git a/zrml/simple-disputes/src/tests.rs b/zrml/simple-disputes/src/tests.rs deleted file mode 100644 index fe0b7cfe0..000000000 --- a/zrml/simple-disputes/src/tests.rs +++ /dev/null @@ -1,117 +0,0 @@ -// 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 . - -#![cfg(test)] - -use crate::{ - mock::{ExtBuilder, Runtime, SimpleDisputes}, - Disputes, Error, MarketOf, -}; -use frame_support::{assert_noop, BoundedVec}; -use zeitgeist_primitives::{ - constants::mock::{OutcomeBond, OutcomeFactor}, - traits::DisputeApi, - types::{ - Asset, Deadlines, Market, MarketBonds, MarketCreation, MarketDispute, - MarketDisputeMechanism, MarketPeriod, MarketStatus, MarketType, OutcomeReport, ScoringRule, - }, -}; - -const DEFAULT_MARKET: MarketOf = Market { - market_id: 0, - base_asset: Asset::Ztg, - creation: MarketCreation::Permissionless, - creator_fee: sp_runtime::Perbill::zero(), - creator: 0, - market_type: MarketType::Scalar(0..=100), - dispute_mechanism: Some(MarketDisputeMechanism::SimpleDisputes), - 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::AmmCdaHybrid, - status: MarketStatus::Disputed, - bonds: MarketBonds { - creation: None, - oracle: None, - outsider: None, - dispute: None, - close_dispute: None, - close_request: None, - }, - early_close: None, -}; - -#[test] -fn on_dispute_denies_non_simple_disputes_markets() { - ExtBuilder::default().build().execute_with(|| { - let mut market = DEFAULT_MARKET; - market.dispute_mechanism = Some(MarketDisputeMechanism::Court); - assert_noop!( - SimpleDisputes::on_dispute(&0, &market), - Error::::MarketDoesNotHaveSimpleDisputesMechanism - ); - }); -} - -#[test] -fn on_resolution_denies_non_simple_disputes_markets() { - ExtBuilder::default().build().execute_with(|| { - let mut market = DEFAULT_MARKET; - market.dispute_mechanism = Some(MarketDisputeMechanism::Court); - assert_noop!( - SimpleDisputes::on_resolution(&0, &market), - Error::::MarketDoesNotHaveSimpleDisputesMechanism - ); - }); -} - -#[test] -fn on_resolution_sets_the_last_dispute_of_disputed_markets_as_the_canonical_outcome() { - ExtBuilder::default().build().execute_with(|| { - let mut market = DEFAULT_MARKET; - market.status = MarketStatus::Disputed; - let disputes = BoundedVec::try_from( - [ - MarketDispute { - at: 0, - by: 0, - outcome: OutcomeReport::Scalar(0), - bond: OutcomeBond::get(), - }, - MarketDispute { - at: 0, - by: 0, - outcome: OutcomeReport::Scalar(20), - bond: OutcomeFactor::get() * OutcomeBond::get(), - }, - ] - .to_vec(), - ) - .unwrap(); - Disputes::::insert(0, &disputes); - assert_eq!( - &SimpleDisputes::on_resolution(&0, &market).unwrap().result.unwrap(), - &disputes.last().unwrap().outcome - ) - }); -} - -// TODO test `suggest_outcome` functionality and API functionality diff --git a/zrml/simple-disputes/src/weights.rs b/zrml/simple-disputes/src/weights.rs deleted file mode 100644 index 4bc89e83a..000000000 --- a/zrml/simple-disputes/src/weights.rs +++ /dev/null @@ -1,133 +0,0 @@ -// Copyright 2023-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 . - -//! Autogenerated weights for zrml_simple_disputes -//! -//! THIS FILE WAS AUTO-GENERATED USING THE SUBSTRATE BENCHMARK CLI VERSION 4.0.0-dev -//! DATE: 2023-01-19, STEPS: `10`, REPEAT: 10, LOW RANGE: `[]`, HIGH RANGE: `[]` -//! EXECUTION: Some(Native), WASM-EXECUTION: Compiled, CHAIN: Some("dev"), DB CACHE: 1024 - -// Executed Command: -// ./target/debug/zeitgeist -// benchmark -// pallet -// --chain=dev -// --steps=10 -// --repeat=10 -// --pallet=zrml_simple_disputes -// --extrinsic=* -// --execution=Native -// --wasm-execution=compiled -// --heap-pages=4096 -// --output=./zrml/simple-disputes/src/weights2.rs -// --template=./misc/weight_template.hbs - -#![allow(unused_parens)] -#![allow(unused_imports)] - -use core::marker::PhantomData; -use frame_support::{traits::Get, weights::Weight}; - -/// Trait containing the required functions for weight retrival within -/// zrml_simple_disputes (automatically generated) -pub trait WeightInfoZeitgeist { - fn suggest_outcome(d: u32, r: u32, e: u32) -> Weight; - fn on_dispute_weight() -> Weight; - fn on_resolution_weight(d: u32) -> Weight; - fn exchange_weight(d: u32) -> Weight; - fn get_auto_resolve_weight(d: u32) -> Weight; - fn has_failed_weight(d: u32) -> Weight; - fn on_global_dispute_weight(d: u32) -> Weight; - fn clear_weight(d: u32) -> Weight; -} - -/// Weight functions for zrml_simple_disputes (automatically generated) -pub struct WeightInfo(PhantomData); -impl WeightInfoZeitgeist for WeightInfo { - // Storage: MarketCommons Markets (r:1 w:0) - // Storage: SimpleDisputes Disputes (r:1 w:1) - // Storage: Balances Reserves (r:1 w:1) - // Storage: PredictionMarkets MarketIdsPerDisputeBlock (r:2 w:2) - fn suggest_outcome(d: u32, r: u32, e: u32) -> Weight { - Weight::from_parts(400_160_000, 0) - // Standard Error: 1_302_000 - .saturating_add(Weight::from_parts(3_511_000, 0).saturating_mul(d.into())) - // Standard Error: 69_000 - .saturating_add(Weight::from_parts(324_000, 0).saturating_mul(r.into())) - // Standard Error: 69_000 - .saturating_add(Weight::from_parts(311_000, 0).saturating_mul(e.into())) - .saturating_add(T::DbWeight::get().reads(5)) - .saturating_add(T::DbWeight::get().writes(4)) - } - - fn on_dispute_weight() -> Weight { - Weight::from_all(0) - } - // Storage: SimpleDisputes Disputes (r:1 w:0) - fn on_resolution_weight(d: u32) -> Weight { - Weight::from_parts(5_464_000, 0) - // Standard Error: 3_000 - .saturating_add(Weight::from_parts(210_000, 0).saturating_mul(d.into())) - .saturating_add(T::DbWeight::get().reads(1)) - } - // Storage: SimpleDisputes Disputes (r:1 w:1) - // Storage: Balances Reserves (r:1 w:1) - // Storage: System Account (r:1 w:1) - fn exchange_weight(d: u32) -> Weight { - Weight::from_parts(18_573_000, 0) - // Standard Error: 14_000 - .saturating_add(Weight::from_parts(19_710_000, 0).saturating_mul(d.into())) - .saturating_add(T::DbWeight::get().reads(1)) - .saturating_add(T::DbWeight::get().reads((2_u64).saturating_mul(d.into()))) - .saturating_add(T::DbWeight::get().writes(1)) - .saturating_add(T::DbWeight::get().writes((2_u64).saturating_mul(d.into()))) - } - // Storage: SimpleDisputes Disputes (r:1 w:0) - fn get_auto_resolve_weight(d: u32) -> Weight { - Weight::from_parts(5_535_000, 0) - // Standard Error: 3_000 - .saturating_add(Weight::from_parts(145_000, 0).saturating_mul(d.into())) - .saturating_add(T::DbWeight::get().reads(1)) - } - // Storage: SimpleDisputes Disputes (r:1 w:0) - fn has_failed_weight(d: u32) -> Weight { - Weight::from_parts(5_685_000, 0) - // Standard Error: 2_000 - .saturating_add(Weight::from_parts(117_000, 0).saturating_mul(d.into())) - .saturating_add(T::DbWeight::get().reads(1)) - } - // Storage: SimpleDisputes Disputes (r:1 w:0) - fn on_global_dispute_weight(d: u32) -> Weight { - Weight::from_parts(5_815_000, 0) - // Standard Error: 2_000 - .saturating_add(Weight::from_parts(66_000, 0).saturating_mul(d.into())) - .saturating_add(T::DbWeight::get().reads(1)) - } - // Storage: SimpleDisputes Disputes (r:1 w:1) - // Storage: Balances Reserves (r:1 w:1) - // Storage: System Account (r:1 w:1) - fn clear_weight(d: u32) -> Weight { - Weight::from_parts(15_958_000, 0) - // Standard Error: 17_000 - .saturating_add(Weight::from_parts(13_085_000, 0).saturating_mul(d.into())) - .saturating_add(T::DbWeight::get().reads(1)) - .saturating_add(T::DbWeight::get().reads((2_u64).saturating_mul(d.into()))) - .saturating_add(T::DbWeight::get().writes(1)) - .saturating_add(T::DbWeight::get().writes((2_u64).saturating_mul(d.into()))) - } -}