From f840ee7abc52dd0c7b9bc138029baac0f65470cd Mon Sep 17 00:00:00 2001 From: Chralt98 Date: Tue, 26 Mar 2024 11:06:35 +0100 Subject: [PATCH] merge amm-cda-6 changes --- runtime/common/src/lib.rs | 2 + zrml/hybrid-router/src/benchmarking.rs | 260 +++++++++++++++++++++++++ zrml/hybrid-router/src/weights.rs | 137 +++++++++++++ 3 files changed, 399 insertions(+) create mode 100644 zrml/hybrid-router/src/benchmarking.rs create mode 100644 zrml/hybrid-router/src/weights.rs diff --git a/runtime/common/src/lib.rs b/runtime/common/src/lib.rs index a73fe2f1f..fdcebb6da 100644 --- a/runtime/common/src/lib.rs +++ b/runtime/common/src/lib.rs @@ -1399,6 +1399,7 @@ macro_rules! create_runtime_api { 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); @@ -1503,6 +1504,7 @@ macro_rules! create_runtime_api { 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); diff --git a/zrml/hybrid-router/src/benchmarking.rs b/zrml/hybrid-router/src/benchmarking.rs new file mode 100644 index 000000000..b50eac02f --- /dev/null +++ b/zrml/hybrid-router/src/benchmarking.rs @@ -0,0 +1,260 @@ +// Copyright 2024 Forecasting Technologies LTD. +// +// This file is part of Zeitgeist. +// +// Zeitgeist is free software: you can redistribute it and/or modify it +// under the terms of the GNU General Public License as published by the +// Free Software Foundation, either version 3 of the License, or (at +// your option) any later version. +// +// Zeitgeist is distributed in the hope that it will be useful, but +// WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +// General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with Zeitgeist. If not, see . + +#![allow( + // Auto-generated code is a no man's land + clippy::arithmetic_side_effects +)] +#![cfg(feature = "runtime-benchmarks")] + +#[cfg(test)] +use crate::Pallet as HybridRouter; + +use crate::*; +use frame_benchmarking::v2::*; +use frame_support::{ + assert_ok, + storage::{with_transaction, TransactionOutcome::*}, +}; +use frame_system::RawOrigin; +use orml_traits::MultiCurrency; +use sp_runtime::{Perbill, SaturatedConversion}; +use types::Strategy; +use zeitgeist_primitives::{ + constants::{base_multiples::*, CENT}, + math::fixed::{BaseProvider, FixedDiv, ZeitgeistBase}, + traits::{CompleteSetOperationsApi, DeployPoolApi, HybridRouterOrderbookApi}, + types::{Asset, Market, MarketCreation, MarketPeriod, MarketStatus, MarketType, ScoringRule}, +}; +use zrml_market_commons::MarketCommonsPalletApi; + +pub const MIN_SPOT_PRICE: u128 = CENT / 2; + +// Same behavior as `assert_ok!`, except that it wraps the call inside a transaction layer. Required +// when calling into functions marked `require_transactional` to avoid a `Transactional(NoLayer)` +// error. +macro_rules! assert_ok_with_transaction { + ($expr:expr) => {{ + assert_ok!(with_transaction(|| match $expr { + Ok(val) => Commit(Ok(val)), + Err(err) => Rollback(Err(err)), + })); + }}; +} + +fn create_spot_prices(asset_count: u16) -> Vec> { + let base = ZeitgeistBase::::get().unwrap(); + let amount = base / asset_count as u128; + let remainder = (base % asset_count as u128).saturated_into::>(); + + let mut amounts = vec![amount.saturated_into::>(); asset_count as usize]; + amounts[0] += remainder; + + amounts +} + +fn create_market(caller: T::AccountId, base_asset: AssetOf, asset_count: u16) -> MarketIdOf +where + T: Config, +{ + let market = Market { + base_asset, + creation: MarketCreation::Permissionless, + creator_fee: Perbill::zero(), + creator: caller.clone(), + oracle: caller, + metadata: vec![0, 50], + market_type: MarketType::Categorical(asset_count), + period: MarketPeriod::Block(0u32.into()..1u32.into()), + deadlines: Default::default(), + scoring_rule: ScoringRule::AmmCdaHybrid, + status: MarketStatus::Active, + report: None, + resolved_outcome: None, + dispute_mechanism: None, + bonds: Default::default(), + early_close: None, + }; + let maybe_market_id = T::MarketCommons::push_market(market); + maybe_market_id.unwrap() +} + +fn create_market_and_deploy_pool( + caller: T::AccountId, + base_asset: AssetOf, + asset_count: u16, + amount: BalanceOf, +) -> MarketIdOf +where + T: Config, +{ + let market_id = create_market::(caller.clone(), base_asset, asset_count); + let total_cost = amount + T::AssetManager::minimum_balance(base_asset); + assert_ok!(T::AssetManager::deposit(base_asset, &caller, total_cost)); + assert_ok_with_transaction!(T::CompleteSetOperations::buy_complete_set( + caller.clone(), + market_id, + amount + )); + assert_ok_with_transaction!(T::AmmPoolDeployer::deploy_pool( + caller, + market_id, + amount, + create_spot_prices::(asset_count), + CENT.saturated_into(), + )); + market_id +} + +#[benchmarks] +mod benchmarks { + use super::*; + + #[benchmark] + fn buy(n: Linear<2, 16>, o: Linear<0, 10>) { + let buyer: T::AccountId = whitelisted_caller(); + let base_asset = Asset::Ztg; + let asset_count = n.try_into().unwrap(); + let market_id = create_market_and_deploy_pool::( + buyer.clone(), + base_asset, + asset_count, + _100.saturated_into(), + ); + + let asset = Asset::CategoricalOutcome(market_id, 0u16); + let amount_in = _1000.saturated_into(); + assert_ok!(T::AssetManager::deposit(base_asset, &buyer, amount_in)); + + let spot_prices = create_spot_prices::(asset_count); + let first_spot_price = spot_prices[0]; + + let max_price = _9_10.saturated_into(); + let orders = (0u128..o as u128).collect::>(); + let maker_asset = asset; + let maker_amount = _20.saturated_into(); + let taker_asset = base_asset; + let taker_amount: BalanceOf = _11.saturated_into(); + assert!(taker_amount.bdiv_floor(maker_amount).unwrap() > first_spot_price); + for (i, order_id) in orders.iter().enumerate() { + let order_creator: T::AccountId = account("order_creator", *order_id as u32, 0); + let surplus = ((i + 1) as u128) * _1_2; + let taker_amount = taker_amount + surplus.saturated_into::>(); + assert_ok!(T::AssetManager::deposit(maker_asset, &order_creator, maker_amount)); + assert_ok!(T::OrderBook::place_order( + order_creator, + market_id, + maker_asset, + maker_amount, + taker_asset, + taker_amount, + )); + } + let strategy = Strategy::LimitOrder; + + #[extrinsic_call] + buy( + RawOrigin::Signed(buyer.clone()), + market_id, + asset_count, + asset, + amount_in, + max_price, + orders, + strategy, + ); + + let buyer_limit_order = T::OrderBook::order(o as u128).unwrap(); + assert_eq!(buyer_limit_order.market_id, market_id); + assert_eq!(buyer_limit_order.maker, buyer); + assert_eq!(buyer_limit_order.maker_asset, base_asset); + assert_eq!(buyer_limit_order.taker_asset, asset); + } + + #[benchmark] + fn sell(n: Linear<2, 10>, o: Linear<0, 10>) { + let seller: T::AccountId = whitelisted_caller(); + let base_asset = Asset::Ztg; + let asset_count = n.try_into().unwrap(); + let market_id = create_market_and_deploy_pool::( + seller.clone(), + base_asset, + asset_count, + _100.saturated_into(), + ); + + let asset = Asset::CategoricalOutcome(market_id, 0u16); + let amount_in = (_1000 * 100).saturated_into(); + assert_ok!(T::AssetManager::deposit(asset, &seller, amount_in)); + // seller base asset amount needs to exist, + // otherwise repatriate_reserved_named from order book fails + // with DeadAccount for base asset repatriate to seller beneficiary + let min_balance = T::AssetManager::minimum_balance(base_asset); + assert_ok!(T::AssetManager::deposit(base_asset, &seller, min_balance)); + + let spot_prices = create_spot_prices::(asset_count); + let first_spot_price = spot_prices[0]; + + let min_price = _1_100.saturated_into(); + let orders = (0u128..o as u128).collect::>(); + let maker_asset = base_asset; + let maker_amount: BalanceOf = _9.saturated_into(); + let taker_asset = asset; + let taker_amount = _100.saturated_into(); + assert!(maker_amount.bdiv_floor(taker_amount).unwrap() < first_spot_price); + for (i, order_id) in orders.iter().enumerate() { + let order_creator: T::AccountId = account("order_creator", *order_id as u32, 0); + let surplus = ((i + 1) as u128) * _1_2; + let taker_amount = taker_amount + surplus.saturated_into::>(); + assert_ok!(T::AssetManager::deposit(maker_asset, &order_creator, maker_amount)); + T::OrderBook::place_order( + order_creator, + market_id, + maker_asset, + maker_amount, + taker_asset, + taker_amount, + ) + .unwrap(); + } + let strategy = Strategy::LimitOrder; + + #[extrinsic_call] + sell( + RawOrigin::Signed(seller.clone()), + market_id, + asset_count, + asset, + amount_in, + min_price, + orders, + strategy, + ); + + let seller_limit_order = T::OrderBook::order(o as u128).unwrap(); + assert_eq!(seller_limit_order.market_id, market_id); + assert_eq!(seller_limit_order.maker, seller); + assert_eq!(seller_limit_order.maker_asset, asset); + assert_eq!(seller_limit_order.taker_asset, base_asset); + } + + impl_benchmark_test_suite!( + HybridRouter, + crate::mock::ExtBuilder::default().build(), + crate::mock::Runtime + ); +} diff --git a/zrml/hybrid-router/src/weights.rs b/zrml/hybrid-router/src/weights.rs new file mode 100644 index 000000000..df8fdbb0e --- /dev/null +++ b/zrml/hybrid-router/src/weights.rs @@ -0,0 +1,137 @@ +// 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_hybrid_router +//! +//! THIS FILE WAS AUTO-GENERATED USING THE SUBSTRATE BENCHMARK CLI VERSION 4.0.0-dev +//! DATE: `2024-03-25`, STEPS: `20`, REPEAT: `50`, LOW RANGE: `[]`, HIGH RANGE: `[]` +//! WORST CASE MAP SIZE: `1000000` +//! HOSTNAME: `msi-pro-b650-s`, CPU: `AMD Ryzen 9 7950X3D 16-Core Processor` +//! EXECUTION: `Some(Wasm)`, WASM-EXECUTION: `Compiled`, CHAIN: `Some("dev")`, DB CACHE: `1024` + +// Executed Command: +// ./target/release/zeitgeist +// benchmark +// pallet +// --chain=dev +// --steps=20 +// --repeat=50 +// --pallet=zrml_hybrid_router +// --extrinsic=* +// --execution=wasm +// --wasm-execution=compiled +// --heap-pages=4096 +// --template=./misc/weight_template.hbs +// --header=./HEADER_GPL3 +// --output=./zrml/hybrid-router/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_hybrid_router (automatically generated) +pub trait WeightInfoZeitgeist { + fn buy(n: u32, o: u32, ) -> Weight; + fn sell(n: u32, o: u32, ) -> Weight; +} + +/// Weight functions for zrml_hybrid_router (automatically generated) +pub struct WeightInfo(PhantomData); +impl WeightInfoZeitgeist for WeightInfo { + /// Storage: MarketCommons Markets (r:1 w:0) + /// Proof: MarketCommons Markets (max_values: None, max_size: Some(678), added: 3153, mode: MaxEncodedLen) + /// Storage: Orderbook Orders (r:10 w:11) + /// Proof: Orderbook Orders (max_values: None, max_size: Some(142), added: 2617, mode: MaxEncodedLen) + /// Storage: NeoSwaps Pools (r:1 w:1) + /// Proof: NeoSwaps Pools (max_values: None, max_size: Some(144745), added: 147220, mode: MaxEncodedLen) + /// Storage: System Account (r:12 w:12) + /// Proof: System Account (max_values: None, max_size: Some(132), added: 2607, mode: MaxEncodedLen) + /// Storage: Tokens Accounts (r:27 w:27) + /// Proof: Tokens Accounts (max_values: None, max_size: Some(123), added: 2598, mode: MaxEncodedLen) + /// Storage: Tokens TotalIssuance (r:16 w:16) + /// Proof: Tokens TotalIssuance (max_values: None, max_size: Some(43), added: 2518, mode: MaxEncodedLen) + /// Storage: Tokens Reserves (r:10 w:10) + /// Proof: Tokens Reserves (max_values: None, max_size: Some(1276), added: 3751, mode: MaxEncodedLen) + /// Storage: Orderbook NextOrderId (r:1 w:1) + /// Proof: Orderbook NextOrderId (max_values: Some(1), max_size: Some(16), added: 511, mode: MaxEncodedLen) + /// Storage: Balances Reserves (r:1 w:1) + /// Proof: Balances Reserves (max_values: None, max_size: Some(1249), added: 3724, mode: MaxEncodedLen) + /// The range of component `n` is `[2, 16]`. + /// The range of component `o` is `[0, 10]`. + fn buy(n: u32, o: u32, ) -> Weight { + // Proof Size summary in bytes: + // Measured: `2979 + n * (197 ±0) + o * (610 ±0)` + // Estimated: `162420 + o * (11573 ±0) + n * (5116 ±0)` + // Minimum execution time: 385_729 nanoseconds. + Weight::from_parts(391_339_000, 162420) + // Standard Error: 688_692 + .saturating_add(Weight::from_parts(19_248_287, 0).saturating_mul(n.into())) + // Standard Error: 1_123_878 + .saturating_add(Weight::from_parts(251_339_260, 0).saturating_mul(o.into())) + .saturating_add(T::DbWeight::get().reads(7)) + .saturating_add(T::DbWeight::get().reads((2_u64).saturating_mul(n.into()))) + .saturating_add(T::DbWeight::get().reads((4_u64).saturating_mul(o.into()))) + .saturating_add(T::DbWeight::get().writes(7)) + .saturating_add(T::DbWeight::get().writes((2_u64).saturating_mul(n.into()))) + .saturating_add(T::DbWeight::get().writes((4_u64).saturating_mul(o.into()))) + .saturating_add(Weight::from_parts(0, 11573).saturating_mul(o.into())) + .saturating_add(Weight::from_parts(0, 5116).saturating_mul(n.into())) + } + /// Storage: MarketCommons Markets (r:1 w:0) + /// Proof: MarketCommons Markets (max_values: None, max_size: Some(678), added: 3153, mode: MaxEncodedLen) + /// Storage: Tokens Accounts (r:21 w:21) + /// Proof: Tokens Accounts (max_values: None, max_size: Some(123), added: 2598, mode: MaxEncodedLen) + /// Storage: Orderbook Orders (r:10 w:11) + /// Proof: Orderbook Orders (max_values: None, max_size: Some(142), added: 2617, mode: MaxEncodedLen) + /// Storage: NeoSwaps Pools (r:1 w:1) + /// Proof: NeoSwaps Pools (max_values: None, max_size: Some(144745), added: 147220, mode: MaxEncodedLen) + /// Storage: System Account (r:12 w:12) + /// Proof: System Account (max_values: None, max_size: Some(132), added: 2607, mode: MaxEncodedLen) + /// Storage: Tokens TotalIssuance (r:10 w:10) + /// Proof: Tokens TotalIssuance (max_values: None, max_size: Some(43), added: 2518, mode: MaxEncodedLen) + /// Storage: Balances Reserves (r:10 w:10) + /// Proof: Balances Reserves (max_values: None, max_size: Some(1249), added: 3724, mode: MaxEncodedLen) + /// Storage: Orderbook NextOrderId (r:1 w:1) + /// Proof: Orderbook NextOrderId (max_values: Some(1), max_size: Some(16), added: 511, mode: MaxEncodedLen) + /// Storage: Tokens Reserves (r:1 w:1) + /// Proof: Tokens Reserves (max_values: None, max_size: Some(1276), added: 3751, mode: MaxEncodedLen) + /// The range of component `n` is `[2, 10]`. + /// The range of component `o` is `[0, 10]`. + fn sell(n: u32, o: u32, ) -> Weight { + // Proof Size summary in bytes: + // Measured: `3118 + n * (196 ±0) + o * (421 ±0)` + // Estimated: `162447 + n * (5116 ±0) + o * (11546 ±0)` + // Minimum execution time: 315_329 nanoseconds. + Weight::from_parts(319_350_000, 162447) + // Standard Error: 1_088_722 + .saturating_add(Weight::from_parts(30_773_014, 0).saturating_mul(n.into())) + // Standard Error: 1_117_505 + .saturating_add(Weight::from_parts(213_114_369, 0).saturating_mul(o.into())) + .saturating_add(T::DbWeight::get().reads(7)) + .saturating_add(T::DbWeight::get().reads((2_u64).saturating_mul(n.into()))) + .saturating_add(T::DbWeight::get().reads((4_u64).saturating_mul(o.into()))) + .saturating_add(T::DbWeight::get().writes(7)) + .saturating_add(T::DbWeight::get().writes((2_u64).saturating_mul(n.into()))) + .saturating_add(T::DbWeight::get().writes((4_u64).saturating_mul(o.into()))) + .saturating_add(Weight::from_parts(0, 5116).saturating_mul(n.into())) + .saturating_add(Weight::from_parts(0, 11546).saturating_mul(o.into())) + } +}