diff --git a/Cargo.lock b/Cargo.lock index 7b00ec926..5e7685398 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -96,7 +96,6 @@ dependencies = [ "nutsfinance-stable-asset", "orml-auction", "orml-authority", - "orml-benchmarking", "orml-nft", "orml-oracle", "orml-oracle-runtime-api", @@ -154,6 +153,7 @@ dependencies = [ "sp-session", "sp-staking 39.0.0", "sp-std", + "sp-storage", "sp-transaction-pool", "sp-version 40.0.0", "staging-parachain-info", @@ -3899,7 +3899,6 @@ dependencies = [ "nutsfinance-stable-asset", "orml-auction", "orml-authority", - "orml-benchmarking", "orml-nft", "orml-oracle", "orml-oracle-runtime-api", @@ -3957,6 +3956,7 @@ dependencies = [ "sp-session", "sp-staking 39.0.0", "sp-std", + "sp-storage", "sp-transaction-pool", "sp-version 40.0.0", "staging-parachain-info", @@ -4361,7 +4361,6 @@ dependencies = [ "nutsfinance-stable-asset", "orml-auction", "orml-authority", - "orml-benchmarking", "orml-nft", "orml-oracle", "orml-oracle-runtime-api", @@ -4424,6 +4423,7 @@ dependencies = [ "sp-session", "sp-staking 39.0.0", "sp-std", + "sp-storage", "sp-transaction-pool", "sp-trie 40.0.0", "sp-version 40.0.0", @@ -4570,6 +4570,7 @@ name = "module-aggregated-dex" version = "2.31.0" dependencies = [ "acala-primitives", + "frame-benchmarking", "frame-support 41.0.0", "frame-system", "module-dex", @@ -4591,6 +4592,7 @@ name = "module-asset-registry" version = "2.31.0" dependencies = [ "acala-primitives", + "frame-benchmarking", "frame-support 41.0.0", "frame-system", "hex", @@ -4632,6 +4634,7 @@ name = "module-auction-manager" version = "2.31.0" dependencies = [ "acala-primitives", + "frame-benchmarking", "frame-support 41.0.0", "frame-system", "log", @@ -4656,6 +4659,7 @@ name = "module-cdp-engine" version = "2.31.0" dependencies = [ "acala-primitives", + "frame-benchmarking", "frame-support 41.0.0", "frame-system", "log", @@ -4688,6 +4692,7 @@ name = "module-cdp-treasury" version = "2.31.0" dependencies = [ "acala-primitives", + "frame-benchmarking", "frame-support 41.0.0", "frame-system", "log", @@ -4737,6 +4742,7 @@ name = "module-currencies" version = "2.31.0" dependencies = [ "acala-primitives", + "frame-benchmarking", "frame-support 41.0.0", "frame-system", "hex", @@ -4772,6 +4778,7 @@ name = "module-dex" version = "2.31.0" dependencies = [ "acala-primitives", + "frame-benchmarking", "frame-support 41.0.0", "frame-system", "log", @@ -4811,6 +4818,7 @@ name = "module-earning" version = "2.31.0" dependencies = [ "acala-primitives", + "frame-benchmarking", "frame-support 41.0.0", "frame-system", "module-support", @@ -4829,6 +4837,7 @@ name = "module-emergency-shutdown" version = "2.31.0" dependencies = [ "acala-primitives", + "frame-benchmarking", "frame-support 41.0.0", "frame-system", "module-cdp-treasury", @@ -4859,8 +4868,10 @@ dependencies = [ "hex", "hex-literal", "insta", + "libsecp256k1", "log", "module-dex", + "module-evm-accounts", "module-evm-utility", "module-idle-scheduler", "module-support", @@ -4883,7 +4894,6 @@ dependencies = [ "sp-io 41.0.1", "sp-runtime 42.0.0", "sp-std", - "staging-xcm-builder", "substrate-bn", "tiny-keccak", "wasm-bencher", @@ -4894,6 +4904,7 @@ name = "module-evm-accounts" version = "2.31.0" dependencies = [ "acala-primitives", + "frame-benchmarking", "frame-support 41.0.0", "frame-system", "libsecp256k1", @@ -4999,6 +5010,7 @@ name = "module-homa-validator-list" version = "2.31.0" dependencies = [ "acala-primitives", + "frame-benchmarking", "frame-support 41.0.0", "frame-system", "module-support", @@ -5020,6 +5032,7 @@ name = "module-honzon" version = "2.31.0" dependencies = [ "acala-primitives", + "frame-benchmarking", "frame-support 41.0.0", "frame-system", "module-cdp-engine", @@ -5047,6 +5060,7 @@ name = "module-honzon-bridge" version = "2.31.0" dependencies = [ "acala-primitives", + "frame-benchmarking", "frame-support 41.0.0", "frame-system", "hex", @@ -5073,6 +5087,7 @@ name = "module-idle-scheduler" version = "2.31.0" dependencies = [ "acala-primitives", + "frame-benchmarking", "frame-support 41.0.0", "frame-system", "log", @@ -5090,6 +5105,7 @@ name = "module-incentives" version = "2.31.0" dependencies = [ "acala-primitives", + "frame-benchmarking", "frame-support 41.0.0", "frame-system", "log", @@ -5111,6 +5127,7 @@ name = "module-liquid-crowdloan" version = "2.31.0" dependencies = [ "acala-primitives", + "frame-benchmarking", "frame-support 41.0.0", "frame-system", "module-currencies", @@ -5178,6 +5195,7 @@ name = "module-nominees-election" version = "2.31.0" dependencies = [ "acala-primitives", + "frame-benchmarking", "frame-support 41.0.0", "frame-system", "log", @@ -5199,6 +5217,7 @@ name = "module-prices" version = "2.31.0" dependencies = [ "acala-primitives", + "frame-benchmarking", "frame-support 41.0.0", "frame-system", "module-support", @@ -5216,6 +5235,7 @@ dependencies = [ name = "module-session-manager" version = "2.31.0" dependencies = [ + "frame-benchmarking", "frame-support 41.0.0", "frame-system", "log", @@ -5254,6 +5274,7 @@ name = "module-transaction-pause" version = "2.31.0" dependencies = [ "acala-primitives", + "frame-benchmarking", "frame-support 41.0.0", "frame-system", "hex-literal", @@ -5276,6 +5297,7 @@ name = "module-transaction-payment" version = "2.31.0" dependencies = [ "acala-primitives", + "frame-benchmarking", "frame-support 41.0.0", "frame-system", "log", @@ -5510,6 +5532,7 @@ dependencies = [ name = "nutsfinance-stable-asset" version = "0.1.0" dependencies = [ + "frame-benchmarking", "frame-support 41.0.0", "frame-system", "pallet-balances", @@ -5599,6 +5622,7 @@ dependencies = [ name = "orml-auction" version = "1.5.0" dependencies = [ + "frame-benchmarking", "frame-support 41.0.0", "frame-system", "orml-traits", @@ -5615,6 +5639,7 @@ dependencies = [ name = "orml-authority" version = "1.5.0" dependencies = [ + "frame-benchmarking", "frame-support 41.0.0", "frame-system", "pallet-preimage", @@ -5782,6 +5807,7 @@ dependencies = [ name = "orml-tokens" version = "1.5.0" dependencies = [ + "frame-benchmarking", "frame-support 41.0.0", "frame-system", "log", @@ -5866,6 +5892,7 @@ dependencies = [ name = "orml-vesting" version = "1.5.0" dependencies = [ + "frame-benchmarking", "frame-support 41.0.0", "frame-system", "pallet-balances", diff --git a/ecosystem-modules/stable-asset b/ecosystem-modules/stable-asset index db12a44a2..aa101e872 160000 --- a/ecosystem-modules/stable-asset +++ b/ecosystem-modules/stable-asset @@ -1 +1 @@ -Subproject commit db12a44a2f13a95e0e042bfeb6d18e485aabcbef +Subproject commit aa101e87234bd3d388c8de062f231d635d79cf7b diff --git a/modules/aggregated-dex/Cargo.toml b/modules/aggregated-dex/Cargo.toml index fdd533470..5306884ef 100644 --- a/modules/aggregated-dex/Cargo.toml +++ b/modules/aggregated-dex/Cargo.toml @@ -9,6 +9,7 @@ parity-scale-codec = { workspace = true, features = ["max-encoded-len"] } scale-info = { workspace = true } sp-core = { workspace = true } sp-runtime = { workspace = true } +frame-benchmarking = { workspace = true, optional = true } frame-support = { workspace = true } frame-system = { workspace = true } sp-std = { workspace = true } @@ -29,6 +30,7 @@ pallet-balances = { workspace = true, features = ["std"] } default = ["std"] std = [ "parity-scale-codec/std", + "frame-benchmarking?/std", "frame-support/std", "frame-system/std", "module-dex/std", @@ -41,6 +43,15 @@ std = [ "sp-std/std", "module-support/std", ] +runtime-benchmarks = [ + "frame-benchmarking/runtime-benchmarks", + "frame-support/runtime-benchmarks", + "frame-system/runtime-benchmarks", + "sp-runtime/runtime-benchmarks", + "orml-tokens/runtime-benchmarks", + "module-dex/runtime-benchmarks", + "nutsfinance-stable-asset/runtime-benchmarks", +] try-runtime = [ "frame-support/try-runtime", "frame-system/try-runtime", diff --git a/modules/aggregated-dex/src/benchmarking.rs b/modules/aggregated-dex/src/benchmarking.rs new file mode 100644 index 000000000..3ae02ef3c --- /dev/null +++ b/modules/aggregated-dex/src/benchmarking.rs @@ -0,0 +1,97 @@ +// This file is part of Acala. + +// Copyright (C) 2020-2025 Acala Foundation. +// SPDX-License-Identifier: GPL-3.0-or-later WITH Classpath-exception-2.0 + +// This program 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. + +// This program 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 this program. If not, see . + +use super::*; +use frame_benchmarking::v2::*; +use frame_system::RawOrigin; +use sp_std::vec; + +/// Helper trait for benchmarking. +pub trait BenchmarkHelper { + fn setup_currency_lists() -> Vec; + // return (path, supply_amount, target_amount) + fn setup_dex(u: u32, taker: AccountId) -> Option<(Vec, Balance, Balance)>; +} + +impl BenchmarkHelper for () { + fn setup_currency_lists() -> Vec { + vec![] + } + fn setup_dex(_u: u32, _taker: AccountId) -> Option<(Vec, Balance, Balance)> { + None + } +} + +#[benchmarks( + where + T: Config + module_dex::Config +)] +mod benchmarks { + use super::*; + + #[benchmark] + fn swap_with_exact_supply(u: Linear<2, { ::TradingPathLimit::get() }>) { + let taker: T::AccountId = account("taker", 0, 0); + + let (path, supply_amount, _target_amount) = + ::BenchmarkHelper::setup_dex(u, taker.clone()).unwrap(); + + #[extrinsic_call] + _( + RawOrigin::Signed(taker), + vec![SwapPath::Dex(path.clone())], + supply_amount, + 0, + ); + } + + #[benchmark] + fn swap_with_exact_target(u: Linear<2, { ::TradingPathLimit::get() }>) { + let taker: T::AccountId = account("taker", 0, 0); + + let (path, supply_amount, target_amount) = ::BenchmarkHelper::setup_dex(u, taker.clone()).unwrap(); + + #[extrinsic_call] + _( + RawOrigin::Signed(taker), + vec![SwapPath::Dex(path.clone())], + target_amount, + supply_amount, + ); + } + + #[benchmark] + fn update_aggregated_swap_paths( + n: Linear<0, { ::BenchmarkHelper::setup_currency_lists().len() as u32 }>, + ) { + let currency_lists = ::BenchmarkHelper::setup_currency_lists(); + let mut updates: Vec<((CurrencyId, CurrencyId), Option>)> = vec![]; + for i in 1..n { + let token_a = currency_lists[i as usize]; + updates.push(( + (token_a, currency_lists[0]), + Some(vec![SwapPath::Dex(vec![token_a, currency_lists[0]])]), + )); + } + + #[extrinsic_call] + _(RawOrigin::Root, updates); + } + + impl_benchmark_test_suite!(Pallet, crate::mock::ExtBuilder::default().build(), crate::mock::Runtime); +} diff --git a/modules/aggregated-dex/src/lib.rs b/modules/aggregated-dex/src/lib.rs index 89c8a7e53..3628be0b4 100644 --- a/modules/aggregated-dex/src/lib.rs +++ b/modules/aggregated-dex/src/lib.rs @@ -30,10 +30,14 @@ use primitives::{Balance, CurrencyId}; use sp_runtime::traits::{Convert, Zero}; use sp_std::{marker::PhantomData, vec::Vec}; +#[cfg(feature = "runtime-benchmarks")] +mod benchmarking; mod mock; mod tests; pub mod weights; +#[cfg(feature = "runtime-benchmarks")] +pub use benchmarking::BenchmarkHelper; pub use module::*; pub use weights::WeightInfo; @@ -69,6 +73,9 @@ pub mod module { type SwapPathLimit: Get; type WeightInfo: WeightInfo; + + #[cfg(feature = "runtime-benchmarks")] + type BenchmarkHelper: BenchmarkHelper; } #[pallet::error] @@ -157,7 +164,7 @@ pub mod module { origin: OriginFor, updates: Vec<((CurrencyId, CurrencyId), Option>)>, ) -> DispatchResult { - T::GovernanceOrigin::ensure_origin(origin)?; + T::GovernanceOrigin::ensure_origin_or_root(origin)?; for (key, maybe_paths) in updates { if let Some(paths) = maybe_paths { diff --git a/modules/aggregated-dex/src/mock.rs b/modules/aggregated-dex/src/mock.rs index dad819985..b004afb2a 100644 --- a/modules/aggregated-dex/src/mock.rs +++ b/modules/aggregated-dex/src/mock.rs @@ -22,7 +22,7 @@ use super::*; use frame_support::{ - derive_impl, match_types, ord_parameter_types, parameter_types, + assert_ok, derive_impl, match_types, ord_parameter_types, parameter_types, traits::{ConstU128, ConstU32, ConstU64, Nothing}, PalletId, }; @@ -72,6 +72,8 @@ impl orml_tokens::Config for Runtime { type MaxReserves = (); type ReserveIdentifier = [u8; 8]; type DustRemovalWhitelist = Nothing; + #[cfg(feature = "runtime-benchmarks")] + type BenchmarkHelper = (); } ord_parameter_types! { @@ -88,7 +90,7 @@ parameter_types! { impl module_dex::Config for Runtime { type Currency = Tokens; type GetExchangeFee = GetExchangeFee; - type TradingPathLimit = ConstU32<4>; + type TradingPathLimit = ConstU32<3>; type PalletId = DEXPalletId; type GetNativeCurrencyId = GetNativeCurrencyId; type Erc20InfoMapping = (); @@ -97,6 +99,8 @@ impl module_dex::Config for Runtime { type ListingOrigin = EnsureSignedBy; type ExtendedProvisioningBlocks = ConstU64<0>; type OnLiquidityPoolUpdated = (); + #[cfg(feature = "runtime-benchmarks")] + type BenchmarkHelper = (); } pub struct EnsurePoolAssetId; @@ -163,6 +167,8 @@ impl nutsfinance_stable_asset::Config for Runtime { type WeightInfo = (); type ListingOrigin = EnsureSignedBy; type EnsurePoolAssetId = EnsurePoolAssetId; + #[cfg(feature = "runtime-benchmarks")] + type BenchmarkHelper = (); } parameter_types! { @@ -170,6 +176,41 @@ parameter_types! { pub const GetLiquidCurrencyId: CurrencyId = LDOT; } +#[cfg(feature = "runtime-benchmarks")] +pub struct MockBenchmarkHelper; +#[cfg(feature = "runtime-benchmarks")] +impl BenchmarkHelper for MockBenchmarkHelper { + fn setup_currency_lists() -> Vec { + [DOT, LDOT, AUSD].to_vec() + } + fn setup_dex(u: u32, taker: AccountId) -> Option<(Vec, Balance, Balance)> { + let currency_list = Self::setup_currency_lists(); + let mut path: Vec = vec![]; + + for i in 1..u { + if i == 1 { + let cur0 = currency_list[0]; + let cur1 = currency_list[1]; + path.push(cur0); + path.push(cur1); + assert_ok!(inject_liquidity(cur0, cur1, 10_000, 10_000)); + } else { + path.push(currency_list[i as usize]); + assert_ok!(inject_liquidity( + currency_list[i as usize - 1], + currency_list[i as usize], + 10_000, + 10_000 + )); + } + } + + assert_ok!(Tokens::deposit(path[0], &taker, 1000)); + + Some((path, 1000, 10)) + } +} + impl Config for Runtime { type DEX = Dex; type StableAsset = StableAssetWrapper; @@ -177,6 +218,8 @@ impl Config for Runtime { type DexSwapJointList = DexSwapJointList; type SwapPathLimit = ConstU32<3>; type WeightInfo = (); + #[cfg(feature = "runtime-benchmarks")] + type BenchmarkHelper = MockBenchmarkHelper; } pub type StableAssetWrapper = @@ -226,3 +269,27 @@ impl ExtBuilder { t.into() } } + +pub fn inject_liquidity( + currency_id_a: CurrencyId, + currency_id_b: CurrencyId, + max_amount_a: Balance, + max_amount_b: Balance, +) -> Result<(), &'static str> { + // set balance + Tokens::deposit(currency_id_a, &BOB, max_amount_a)?; + Tokens::deposit(currency_id_b, &BOB, max_amount_b)?; + + let _ = Dex::enable_trading_pair(RuntimeOrigin::signed(BOB.clone()), currency_id_a, currency_id_b); + Dex::add_liquidity( + RuntimeOrigin::signed(BOB), + currency_id_a, + currency_id_b, + max_amount_a, + max_amount_b, + Default::default(), + false, + )?; + + Ok(()) +} diff --git a/modules/aggregated-dex/src/tests.rs b/modules/aggregated-dex/src/tests.rs index a5eb299b8..f08afc87b 100644 --- a/modules/aggregated-dex/src/tests.rs +++ b/modules/aggregated-dex/src/tests.rs @@ -30,30 +30,6 @@ fn set_dex_swap_joint_list(joints: Vec>) { DexSwapJointList::set(joints); } -fn inject_liquidity( - currency_id_a: CurrencyId, - currency_id_b: CurrencyId, - max_amount_a: Balance, - max_amount_b: Balance, -) -> Result<(), &'static str> { - // set balance - Tokens::deposit(currency_id_a, &BOB, max_amount_a)?; - Tokens::deposit(currency_id_b, &BOB, max_amount_b)?; - - let _ = Dex::enable_trading_pair(RuntimeOrigin::signed(BOB.clone()), currency_id_a, currency_id_b); - Dex::add_liquidity( - RuntimeOrigin::signed(BOB), - currency_id_a, - currency_id_b, - max_amount_a, - max_amount_b, - Default::default(), - false, - )?; - - Ok(()) -} - fn initial_taiga_dot_ldot_pool() -> DispatchResult { StableAssetWrapper::create_pool( STABLE_ASSET, diff --git a/modules/asset-registry/Cargo.toml b/modules/asset-registry/Cargo.toml index ac8fbfe82..d22a45cfc 100644 --- a/modules/asset-registry/Cargo.toml +++ b/modules/asset-registry/Cargo.toml @@ -10,6 +10,7 @@ scale-info = { workspace = true } parity-scale-codec = { workspace = true } sp-runtime = { workspace = true } sp-std = { workspace = true } +frame-benchmarking = { workspace = true, optional = true } frame-support = { workspace = true } frame-system = { workspace = true } primitives = { workspace = true } @@ -37,12 +38,16 @@ std = [ "scale-info/std", "sp-runtime/std", "sp-std/std", + "frame-benchmarking?/std", "frame-support/std", "frame-system/std", "primitives/std", "xcm/std", "module-support/std", ] +runtime-benchmarks = [ + "frame-benchmarking/runtime-benchmarks", +] try-runtime = [ "frame-support/try-runtime", "frame-system/try-runtime", diff --git a/modules/asset-registry/src/benchmarking.rs b/modules/asset-registry/src/benchmarking.rs new file mode 100644 index 000000000..a74c9ff90 --- /dev/null +++ b/modules/asset-registry/src/benchmarking.rs @@ -0,0 +1,170 @@ +// This file is part of Acala. + +// Copyright (C) 2020-2025 Acala Foundation. +// SPDX-License-Identifier: GPL-3.0-or-later WITH Classpath-exception-2.0 + +// This program 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. + +// This program 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 this program. If not, see . + +use super::*; +use frame_benchmarking::v2::*; +use frame_support::assert_ok; +use frame_system::RawOrigin; + +/// Helper trait for benchmarking. +pub trait BenchmarkHelper { + fn setup_deploy_contract() -> Option; +} + +impl BenchmarkHelper for () { + fn setup_deploy_contract() -> Option { + None + } +} + +#[benchmarks] +mod benchmarks { + use super::*; + + #[benchmark] + fn register_foreign_asset() { + let location = VersionedLocation::V5(Location::new(0, [Parachain(1000)])); + let asset_metadata = AssetMetadata { + name: b"Token Name".to_vec(), + symbol: b"TN".to_vec(), + decimals: 12u8, + minimal_balance: 1u32.into(), + }; + + #[extrinsic_call] + _(RawOrigin::Root, Box::new(location), Box::new(asset_metadata)); + } + + #[benchmark] + fn update_foreign_asset() { + let location = VersionedLocation::V5(Location::new(0, [Parachain(1000)])); + let asset_metadata = AssetMetadata { + name: b"Token Name".to_vec(), + symbol: b"TN".to_vec(), + decimals: 12u8, + minimal_balance: 1u32.into(), + }; + + assert_ok!(Pallet::::register_foreign_asset( + RawOrigin::Root.into(), + Box::new(location.clone()), + Box::new(asset_metadata.clone()) + )); + + #[extrinsic_call] + _(RawOrigin::Root, 0, Box::new(location), Box::new(asset_metadata)); + } + + #[benchmark] + fn register_stable_asset() { + let asset_metadata = AssetMetadata { + name: b"Token Name".to_vec(), + symbol: b"TN".to_vec(), + decimals: 12u8, + minimal_balance: 1u32.into(), + }; + + #[extrinsic_call] + _(RawOrigin::Root, Box::new(asset_metadata)); + } + + #[benchmark] + fn update_stable_asset() { + let asset_metadata = AssetMetadata { + name: b"Token Name".to_vec(), + symbol: b"TN".to_vec(), + decimals: 12u8, + minimal_balance: 1u32.into(), + }; + + assert_ok!(Pallet::::register_stable_asset( + RawOrigin::Root.into(), + Box::new(asset_metadata.clone()) + )); + + #[extrinsic_call] + _(RawOrigin::Root, 0, Box::new(asset_metadata)); + } + + #[benchmark] + fn register_erc20_asset() { + let erc20_address = T::BenchmarkHelper::setup_deploy_contract().unwrap(); + + #[extrinsic_call] + _(RawOrigin::Root, erc20_address, 1u32.into()); + } + + #[benchmark] + fn update_erc20_asset() { + let asset_metadata = AssetMetadata { + name: b"Token Name".to_vec(), + symbol: b"TN".to_vec(), + decimals: 12u8, + minimal_balance: 1u32.into(), + }; + + let erc20_address = T::BenchmarkHelper::setup_deploy_contract().unwrap(); + assert_ok!(Pallet::::register_erc20_asset( + RawOrigin::Root.into(), + erc20_address, + 1u32.into() + )); + + #[extrinsic_call] + _(RawOrigin::Root, erc20_address, Box::new(asset_metadata)); + } + + #[benchmark] + fn register_native_asset() { + let asset_metadata = AssetMetadata { + name: b"Token Name".to_vec(), + symbol: b"TN".to_vec(), + decimals: 12u8, + minimal_balance: 1u32.into(), + }; + + #[extrinsic_call] + _( + RawOrigin::Root, + CurrencyId::LiquidCrowdloan(0), + Box::new(asset_metadata), + ); + } + + #[benchmark] + fn update_native_asset() { + let currency_id = CurrencyId::LiquidCrowdloan(0); + let asset_metadata = AssetMetadata { + name: b"Token Name".to_vec(), + symbol: b"TN".to_vec(), + decimals: 12u8, + minimal_balance: 1u32.into(), + }; + + assert_ok!(Pallet::::register_native_asset( + RawOrigin::Root.into(), + currency_id, + Box::new(asset_metadata.clone()) + )); + + #[extrinsic_call] + _(RawOrigin::Root, currency_id, Box::new(asset_metadata)); + } + + impl_benchmark_test_suite!(Pallet, crate::mock::ExtBuilder::default().build(), crate::mock::Runtime); +} diff --git a/modules/asset-registry/src/lib.rs b/modules/asset-registry/src/lib.rs index 613d6e42d..473f4508f 100644 --- a/modules/asset-registry/src/lib.rs +++ b/modules/asset-registry/src/lib.rs @@ -49,10 +49,14 @@ use sp_std::{boxed::Box, vec::Vec}; use xcm::{v3, v4, v5::prelude::*, VersionedLocation}; +#[cfg(feature = "runtime-benchmarks")] +mod benchmarking; mod mock; mod tests; mod weights; +#[cfg(feature = "runtime-benchmarks")] +pub use benchmarking::BenchmarkHelper; pub use module::*; pub use weights::WeightInfo; @@ -80,6 +84,9 @@ pub mod module { /// Weight information for the extrinsics in this module. type WeightInfo: WeightInfo; + + #[cfg(feature = "runtime-benchmarks")] + type BenchmarkHelper: BenchmarkHelper; } #[pallet::error] @@ -201,7 +208,7 @@ pub mod module { location: Box, metadata: Box>>, ) -> DispatchResult { - T::RegisterOrigin::ensure_origin(origin)?; + T::RegisterOrigin::ensure_origin_or_root(origin)?; let location: Location = (*location).try_into().map_err(|()| Error::::BadLocation)?; let foreign_asset_id = Self::do_register_foreign_asset(&location, &metadata)?; @@ -222,7 +229,7 @@ pub mod module { location: Box, metadata: Box>>, ) -> DispatchResult { - T::RegisterOrigin::ensure_origin(origin)?; + T::RegisterOrigin::ensure_origin_or_root(origin)?; let location: Location = (*location).try_into().map_err(|()| Error::::BadLocation)?; Self::do_update_foreign_asset(foreign_asset_id, &location, &metadata)?; @@ -241,7 +248,7 @@ pub mod module { origin: OriginFor, metadata: Box>>, ) -> DispatchResult { - T::RegisterOrigin::ensure_origin(origin)?; + T::RegisterOrigin::ensure_origin_or_root(origin)?; let stable_asset_id = Self::do_register_stable_asset(&metadata)?; @@ -259,7 +266,7 @@ pub mod module { stable_asset_id: StableAssetPoolId, metadata: Box>>, ) -> DispatchResult { - T::RegisterOrigin::ensure_origin(origin)?; + T::RegisterOrigin::ensure_origin_or_root(origin)?; Self::do_update_stable_asset(&stable_asset_id, &metadata)?; @@ -277,7 +284,7 @@ pub mod module { contract: EvmAddress, minimal_balance: BalanceOf, ) -> DispatchResult { - T::RegisterOrigin::ensure_origin(origin)?; + T::RegisterOrigin::ensure_origin_or_root(origin)?; let metadata = Self::do_register_erc20_asset(contract, minimal_balance)?; @@ -295,7 +302,7 @@ pub mod module { contract: EvmAddress, metadata: Box>>, ) -> DispatchResult { - T::RegisterOrigin::ensure_origin(origin)?; + T::RegisterOrigin::ensure_origin_or_root(origin)?; Self::do_update_erc20_asset(contract, &metadata)?; @@ -313,7 +320,7 @@ pub mod module { currency_id: CurrencyId, metadata: Box>>, ) -> DispatchResult { - T::RegisterOrigin::ensure_origin(origin)?; + T::RegisterOrigin::ensure_origin_or_root(origin)?; Self::do_register_native_asset(currency_id, &metadata)?; @@ -331,7 +338,7 @@ pub mod module { currency_id: CurrencyId, metadata: Box>>, ) -> DispatchResult { - T::RegisterOrigin::ensure_origin(origin)?; + T::RegisterOrigin::ensure_origin_or_root(origin)?; Self::do_update_native_asset(currency_id, &metadata)?; diff --git a/modules/asset-registry/src/mock.rs b/modules/asset-registry/src/mock.rs index 17a60e50c..197279fb8 100644 --- a/modules/asset-registry/src/mock.rs +++ b/modules/asset-registry/src/mock.rs @@ -20,6 +20,7 @@ #![cfg(test)] +use super::*; use crate as asset_registry; use frame_support::{ assert_ok, construct_runtime, derive_impl, ord_parameter_types, parameter_types, @@ -114,12 +115,26 @@ impl module_evm_bridge::Config for Runtime { parameter_types! { pub const KSMCurrencyId: CurrencyId = CurrencyId::Token(TokenSymbol::KSM); } + +#[cfg(feature = "runtime-benchmarks")] +pub struct MockBenchmarkHelper; +#[cfg(feature = "runtime-benchmarks")] +impl BenchmarkHelper for MockBenchmarkHelper { + fn setup_deploy_contract() -> Option { + let _ = Balances::deposit_creating(&alice(), 1_000_000_000_000); + deploy_contracts(); + Some(erc20_address()) + } +} + impl asset_registry::Config for Runtime { type Currency = Balances; type StakingCurrencyId = KSMCurrencyId; type EVMBridge = module_evm_bridge::EVMBridge; type RegisterOrigin = EnsureSignedBy; type WeightInfo = (); + #[cfg(feature = "runtime-benchmarks")] + type BenchmarkHelper = MockBenchmarkHelper; } type Block = frame_system::mocking::MockBlock; diff --git a/modules/auction-manager/Cargo.toml b/modules/auction-manager/Cargo.toml index f416d9331..605ceb1af 100644 --- a/modules/auction-manager/Cargo.toml +++ b/modules/auction-manager/Cargo.toml @@ -8,6 +8,7 @@ edition = { workspace = true } log = { workspace = true } parity-scale-codec = { workspace = true } scale-info = { workspace = true } +frame-benchmarking = { workspace = true, optional = true } frame-support = { workspace = true } frame-system = { workspace = true } sp-io = { workspace = true } @@ -30,6 +31,7 @@ orml-tokens = { workspace = true, features = ["std"] } default = ["std"] std = [ "parity-scale-codec/std", + "frame-benchmarking?/std", "frame-support/std", "frame-system/std", "orml-traits/std", @@ -41,6 +43,16 @@ std = [ "sp-std/std", "module-support/std", ] +runtime-benchmarks = [ + "frame-benchmarking/runtime-benchmarks", + "frame-support/runtime-benchmarks", + "frame-system/runtime-benchmarks", + "sp-runtime/runtime-benchmarks", + "orml-tokens/runtime-benchmarks", + "orml-auction/runtime-benchmarks", + "module-cdp-treasury/runtime-benchmarks", + "module-dex/runtime-benchmarks", +] try-runtime = [ "frame-support/try-runtime", "frame-system/try-runtime", diff --git a/runtime/mandala/src/benchmarking/prices.rs b/modules/auction-manager/src/benchmarking.rs similarity index 55% rename from runtime/mandala/src/benchmarking/prices.rs rename to modules/auction-manager/src/benchmarking.rs index c876c4409..5786a7a3d 100644 --- a/runtime/mandala/src/benchmarking/prices.rs +++ b/modules/auction-manager/src/benchmarking.rs @@ -16,33 +16,34 @@ // You should have received a copy of the GNU General Public License // along with this program. If not, see . -use crate::{Prices, Runtime, RuntimeOrigin}; - -use super::utils::{dollar, feed_price, STAKING}; +use super::*; +use frame_benchmarking::v2::*; use frame_system::RawOrigin; -use orml_benchmarking::runtime_benchmarks; -use sp_std::vec; - -runtime_benchmarks! { - { Runtime, module_prices } - - lock_price { - // feed price - feed_price(vec![(STAKING, dollar(STAKING).into())])?; - }: _(RawOrigin::Root, STAKING) - - unlock_price { - // feed price - feed_price(vec![(STAKING, dollar(STAKING).into())])?; - Prices::lock_price(RuntimeOrigin::root(), STAKING)?; - }: _(RawOrigin::Root, STAKING) + +/// Helper trait for benchmarking. +pub trait BenchmarkHelper { + fn setup() -> Option; +} + +impl BenchmarkHelper for () { + fn setup() -> Option { + None + } } -#[cfg(test)] -mod tests { +#[benchmarks] +mod benchmarks { use super::*; - use crate::benchmarking::utils::tests::new_test_ext; - use orml_benchmarking::impl_benchmark_test_suite; - impl_benchmark_test_suite!(new_test_ext(),); + // `cancel` a collateral auction, worst case: + // auction have been already bid + #[benchmark] + fn cancel_collateral_auction() { + let auction_id: AuctionId = T::BenchmarkHelper::setup().unwrap(); + + #[extrinsic_call] + cancel(RawOrigin::None, auction_id); + } + + impl_benchmark_test_suite!(Pallet, crate::mock::ExtBuilder::default().build(), crate::mock::Runtime); } diff --git a/modules/auction-manager/src/lib.rs b/modules/auction-manager/src/lib.rs index 88b918631..1960137eb 100644 --- a/modules/auction-manager/src/lib.rs +++ b/modules/auction-manager/src/lib.rs @@ -57,10 +57,14 @@ use sp_runtime::{ }; use sp_std::prelude::*; +#[cfg(feature = "runtime-benchmarks")] +mod benchmarking; mod mock; mod tests; pub mod weights; +#[cfg(feature = "runtime-benchmarks")] +pub use benchmarking::BenchmarkHelper; pub use module::*; pub use weights::WeightInfo; @@ -175,6 +179,9 @@ pub mod module { /// Weight information for the extrinsics in this module. type WeightInfo: WeightInfo; + + #[cfg(feature = "runtime-benchmarks")] + type BenchmarkHelper: BenchmarkHelper; } #[pallet::error] diff --git a/modules/auction-manager/src/mock.rs b/modules/auction-manager/src/mock.rs index 2df993519..3578fde15 100644 --- a/modules/auction-manager/src/mock.rs +++ b/modules/auction-manager/src/mock.rs @@ -22,7 +22,7 @@ use super::*; use frame_support::{ - construct_runtime, derive_impl, ord_parameter_types, parameter_types, + assert_ok, construct_runtime, derive_impl, ord_parameter_types, parameter_types, traits::{ConstU32, ConstU64, Nothing}, PalletId, }; @@ -79,6 +79,8 @@ impl orml_tokens::Config for Runtime { type MaxReserves = (); type ReserveIdentifier = [u8; 8]; type DustRemovalWhitelist = Nothing; + #[cfg(feature = "runtime-benchmarks")] + type BenchmarkHelper = (); } impl orml_auction::Config for Runtime { @@ -86,6 +88,8 @@ impl orml_auction::Config for Runtime { type AuctionId = AuctionId; type Handler = AuctionManagerModule; type WeightInfo = (); + #[cfg(feature = "runtime-benchmarks")] + type BenchmarkHelper = (); } ord_parameter_types! { @@ -114,6 +118,8 @@ impl module_cdp_treasury::Config for Runtime { type TreasuryAccount = TreasuryAccount; type WeightInfo = (); type StableAsset = MockStableAsset; + #[cfg(feature = "runtime-benchmarks")] + type BenchmarkHelper = (); } parameter_types! { @@ -159,6 +165,8 @@ impl module_dex::Config for Runtime { type ListingOrigin = EnsureSignedBy; type ExtendedProvisioningBlocks = ConstU64<0>; type OnLiquidityPoolUpdated = (); + #[cfg(feature = "runtime-benchmarks")] + type BenchmarkHelper = (); } parameter_types! { @@ -180,6 +188,20 @@ parameter_types! { pub MinimumIncrementSize: Rate = Rate::saturating_from_rational(1, 20); } +#[cfg(feature = "runtime-benchmarks")] +pub struct MockBenchmarkHelper; +#[cfg(feature = "runtime-benchmarks")] +impl BenchmarkHelper for MockBenchmarkHelper { + fn setup() -> Option { + assert_ok!(AuctionManagerModule::new_collateral_auction(&ALICE, BTC, 10, 100)); + + assert_ok!(AuctionModule::bid(RuntimeOrigin::signed(BOB), 0, 80)); + + mock_shutdown(); + Some(0) + } +} + impl Config for Runtime { type Currency = Tokens; type Auction = AuctionModule; @@ -192,6 +214,8 @@ impl Config for Runtime { type UnsignedPriority = ConstU64<1048576>; // 1 << 20 type EmergencyShutdown = MockEmergencyShutdown; type WeightInfo = (); + #[cfg(feature = "runtime-benchmarks")] + type BenchmarkHelper = MockBenchmarkHelper; } pub type Block = sp_runtime::generic::Block; diff --git a/modules/cdp-engine/Cargo.toml b/modules/cdp-engine/Cargo.toml index aefe5319e..eb665b213 100644 --- a/modules/cdp-engine/Cargo.toml +++ b/modules/cdp-engine/Cargo.toml @@ -9,6 +9,7 @@ log = { workspace = true } rand_chacha = { workspace = true } parity-scale-codec = { workspace = true } scale-info = { workspace = true } +frame-benchmarking = { workspace = true, optional = true } frame-support = { workspace = true } frame-system = { workspace = true } sp-application-crypto = {workspace = true } @@ -17,6 +18,7 @@ orml-utilities = { workspace = true } sp-io = {workspace = true } sp-runtime = {workspace = true } sp-std = {workspace = true } +module-cdp-treasury = { workspace = true, optional = true } module-support = { workspace = true } module-loans = { workspace = true } primitives = { workspace = true } @@ -39,6 +41,7 @@ module-evm-bridge = { workspace = true, features = ["std"] } default = ["std"] std = [ "parity-scale-codec/std", + "frame-benchmarking?/std", "frame-support/std", "frame-system/std", "module-loans/std", @@ -54,6 +57,15 @@ std = [ "sp-std/std", "module-support/std", ] +runtime-benchmarks = [ + "frame-benchmarking/runtime-benchmarks", + "frame-support/runtime-benchmarks", + "frame-system/runtime-benchmarks", + "sp-runtime/runtime-benchmarks", + "orml-tokens/runtime-benchmarks", + "module-cdp-treasury/runtime-benchmarks", + "module-dex/runtime-benchmarks", +] try-runtime = [ "frame-support/try-runtime", "frame-system/try-runtime", diff --git a/modules/cdp-engine/src/benchmarking.rs b/modules/cdp-engine/src/benchmarking.rs new file mode 100644 index 000000000..29f08e174 --- /dev/null +++ b/modules/cdp-engine/src/benchmarking.rs @@ -0,0 +1,126 @@ +// This file is part of Acala. + +// Copyright (C) 2020-2025 Acala Foundation. +// SPDX-License-Identifier: GPL-3.0-or-later WITH Classpath-exception-2.0 + +// This program 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. + +// This program 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 this program. If not, see . + +use super::*; +use frame_benchmarking::v2::*; +use frame_support::assert_ok; +use frame_system::RawOrigin; + +/// Helper trait for benchmarking. +pub trait BenchmarkHelper { + fn setup_on_initialize(c: u32) -> Option; + fn setup_collateral_currency_ids() -> Vec; + fn setup_liquidate_by_auction(b: u32) -> Option<(CurrencyId, LookupSource)>; + fn setup_liquidate_by_dex() -> Option<(CurrencyId, LookupSource)>; + fn setup_settle() -> Option<(CurrencyId, LookupSource)>; +} + +impl BenchmarkHelper for () { + fn setup_on_initialize(_c: u32) -> Option { + None + } + fn setup_collateral_currency_ids() -> Vec { + vec![] + } + fn setup_liquidate_by_auction(_b: u32) -> Option<(CurrencyId, LookupSource)> { + None + } + fn setup_liquidate_by_dex() -> Option<(CurrencyId, LookupSource)> { + None + } + fn setup_settle() -> Option<(CurrencyId, LookupSource)> { + None + } +} + +#[benchmarks( + where T: Config + module_cdp_treasury::Config, +)] +mod benchmarks { + use super::*; + + #[benchmark] + fn on_initialize(c: Linear<0, { ::BenchmarkHelper::setup_collateral_currency_ids().len() as u32 }>) { + let block_number = ::BenchmarkHelper::setup_on_initialize(c).unwrap(); + + #[block] + { + Pallet::::on_initialize(block_number); + } + } + + #[benchmark] + fn set_collateral_params() { + #[extrinsic_call] + _( + RawOrigin::Root, + ::GetStableCurrencyId::get(), + Change::NewValue(Some(Rate::saturating_from_rational(1, 1000000))), + Change::NewValue(Some(Ratio::saturating_from_rational(150, 100))), + Change::NewValue(Some(Rate::saturating_from_rational(20, 100))), + Change::NewValue(Some(Ratio::saturating_from_rational(180, 100))), + Change::NewValue(100_000), + ); + } + + // `liquidate` by_auction + #[benchmark] + fn liquidate_by_auction(b: Linear<1, { T::MaxAuctionsCount::get() }>) { + let (staking_currency_id, owner_lookup) = + ::BenchmarkHelper::setup_liquidate_by_auction(b).unwrap(); + + #[extrinsic_call] + liquidate(RawOrigin::None, staking_currency_id, owner_lookup); + } + + // `liquidate` by dex + #[benchmark] + fn liquidate_by_dex() { + let (liquid_currency_id, owner_lookup) = ::BenchmarkHelper::setup_liquidate_by_dex().unwrap(); + + #[extrinsic_call] + liquidate(RawOrigin::None, liquid_currency_id, owner_lookup); + } + + #[benchmark] + fn settle() { + let (staking_currency_id, owner_lookup) = ::BenchmarkHelper::setup_settle().unwrap(); + + #[extrinsic_call] + _(RawOrigin::None, staking_currency_id, owner_lookup); + } + + #[benchmark] + fn register_liquidation_contract() { + #[extrinsic_call] + _(RawOrigin::Root, EvmAddress::default()); + } + + #[benchmark] + fn deregister_liquidation_contract() { + assert_ok!(Pallet::::register_liquidation_contract( + RawOrigin::Root.into(), + EvmAddress::default() + )); + + #[extrinsic_call] + _(RawOrigin::Root, EvmAddress::default()); + } + + impl_benchmark_test_suite!(Pallet, crate::mock::ExtBuilder::default().build(), crate::mock::Runtime); +} diff --git a/modules/cdp-engine/src/lib.rs b/modules/cdp-engine/src/lib.rs index 8644471a1..8ba76fff1 100644 --- a/modules/cdp-engine/src/lib.rs +++ b/modules/cdp-engine/src/lib.rs @@ -66,10 +66,14 @@ use sp_runtime::{ }; use sp_std::{marker::PhantomData, prelude::*}; +#[cfg(feature = "runtime-benchmarks")] +mod benchmarking; mod mock; mod tests; pub mod weights; +#[cfg(feature = "runtime-benchmarks")] +pub use benchmarking::BenchmarkHelper; pub use module::*; pub use weights::WeightInfo; @@ -219,6 +223,9 @@ pub mod module { /// Weight information for the extrinsics in this module. type WeightInfo: WeightInfo; + + #[cfg(feature = "runtime-benchmarks")] + type BenchmarkHelper: BenchmarkHelper, ::Source>; } #[pallet::error] @@ -497,7 +504,7 @@ pub mod module { required_collateral_ratio: ChangeOptionRatio, maximum_total_debit_value: ChangeBalance, ) -> DispatchResult { - T::UpdateOrigin::ensure_origin(origin)?; + T::UpdateOrigin::ensure_origin_or_root(origin)?; let mut collateral_params = Self::collateral_params(currency_id).unwrap_or_default(); if let Change::NewValue(maybe_rate) = interest_rate_per_sec { @@ -556,7 +563,7 @@ pub mod module { #[pallet::call_index(3)] #[pallet::weight(::WeightInfo::register_liquidation_contract())] pub fn register_liquidation_contract(origin: OriginFor, address: EvmAddress) -> DispatchResult { - T::LiquidationContractsUpdateOrigin::ensure_origin(origin)?; + T::LiquidationContractsUpdateOrigin::ensure_origin_or_root(origin)?; LiquidationContracts::::try_append(address).map_err(|()| Error::::TooManyLiquidationContracts)?; Self::deposit_event(Event::LiquidationContractRegistered { address }); Ok(()) @@ -565,7 +572,7 @@ pub mod module { #[pallet::call_index(4)] #[pallet::weight(::WeightInfo::deregister_liquidation_contract())] pub fn deregister_liquidation_contract(origin: OriginFor, address: EvmAddress) -> DispatchResult { - T::LiquidationContractsUpdateOrigin::ensure_origin(origin)?; + T::LiquidationContractsUpdateOrigin::ensure_origin_or_root(origin)?; LiquidationContracts::::mutate(|contracts| { contracts.retain(|c| c != &address); }); diff --git a/modules/cdp-engine/src/mock.rs b/modules/cdp-engine/src/mock.rs index 35fbcfc7f..0d4e36c3f 100644 --- a/modules/cdp-engine/src/mock.rs +++ b/modules/cdp-engine/src/mock.rs @@ -22,7 +22,7 @@ use super::*; use frame_support::{ - construct_runtime, derive_impl, ord_parameter_types, parameter_types, + assert_ok, construct_runtime, derive_impl, ord_parameter_types, parameter_types, traits::{ConstU128, ConstU32, ConstU64, Nothing}, PalletId, }; @@ -85,6 +85,8 @@ impl orml_tokens::Config for Runtime { type MaxReserves = (); type ReserveIdentifier = ReserveIdentifier; type DustRemovalWhitelist = Nothing; + #[cfg(feature = "runtime-benchmarks")] + type BenchmarkHelper = (); } impl pallet_balances::Config for Runtime { @@ -195,7 +197,7 @@ impl AuctionManager for MockAuctionManager { } fn get_total_collateral_in_auction(_id: Self::CurrencyId) -> Self::Balance { - Self::auction().map(|auction| auction.2).unwrap_or_default() + 0 } } @@ -220,6 +222,8 @@ impl module_cdp_treasury::Config for Runtime { type TreasuryAccount = TreasuryAccount; type WeightInfo = (); type StableAsset = MockStableAsset; + #[cfg(feature = "runtime-benchmarks")] + type BenchmarkHelper = (); } parameter_types! { @@ -246,6 +250,8 @@ impl module_dex::Config for Runtime { type ListingOrigin = EnsureSignedBy; type ExtendedProvisioningBlocks = ConstU64<0>; type OnLiquidityPoolUpdated = (); + #[cfg(feature = "runtime-benchmarks")] + type BenchmarkHelper = (); } impl pallet_timestamp::Config for Runtime { @@ -401,6 +407,106 @@ parameter_types! { pub const SettleErc20EvmOrigin: AccountId = AccountId32::new([255u8; 32]); } +#[cfg(feature = "runtime-benchmarks")] +pub struct MockBenchmarkHelper; +#[cfg(feature = "runtime-benchmarks")] +impl BenchmarkHelper for MockBenchmarkHelper { + fn setup_on_initialize(_c: u32) -> Option { + Some(1u64) + } + fn setup_collateral_currency_ids() -> Vec { + vec![BTC, AUSD] + } + fn setup_liquidate_by_auction(_b: u32) -> Option<(CurrencyId, AccountId)> { + assert_ok!(CDPEngineModule::set_collateral_params( + RuntimeOrigin::signed(ALICE), + BTC, + Change::NewValue(Some(Rate::saturating_from_rational(1, 100000))), + Change::NewValue(Some(Ratio::saturating_from_rational(3, 2))), + Change::NewValue(Some(Rate::saturating_from_rational(2, 10))), + Change::NewValue(Some(Ratio::saturating_from_rational(9, 5))), + Change::NewValue(10000), + )); + setup_default_collateral(AUSD); + assert_ok!(CDPEngineModule::adjust_position(&ALICE, BTC, 100, 500)); + assert_eq!(Currencies::free_balance(BTC, &ALICE), 900); + assert_eq!(Currencies::free_balance(AUSD, &ALICE), 50); + assert_eq!(LoansModule::positions(BTC, ALICE).debit, 500); + assert_eq!(LoansModule::positions(BTC, ALICE).collateral, 100); + + assert_ok!(CDPEngineModule::set_collateral_params( + RuntimeOrigin::signed(ALICE), + BTC, + Change::NoChange, + Change::NewValue(Some(Ratio::saturating_from_rational(3, 1))), + Change::NoChange, + Change::NoChange, + Change::NoChange, + )); + + Some((BTC, ALICE)) + } + fn setup_liquidate_by_dex() -> Option<(CurrencyId, AccountId)> { + assert_ok!(CDPEngineModule::set_collateral_params( + RuntimeOrigin::signed(ALICE), + BTC, + Change::NewValue(Some(Rate::saturating_from_rational(1, 100000))), + Change::NewValue(Some(Ratio::saturating_from_rational(3, 2))), + Change::NewValue(Some(Rate::saturating_from_rational(2, 10))), + Change::NewValue(Some(Ratio::saturating_from_rational(9, 5))), + Change::NewValue(10000), + )); + setup_default_collateral(AUSD); + assert_ok!(DEXModule::add_liquidity( + RuntimeOrigin::signed(CAROL), + BTC, + AUSD, + 100, + 121, + 0, + false + )); + assert_eq!(DEXModule::get_liquidity_pool(BTC, AUSD), (100, 121)); + + assert_ok!(CDPEngineModule::adjust_position(&ALICE, BTC, 100, 500)); + assert_eq!(Currencies::free_balance(BTC, &ALICE), 900); + assert_eq!(Currencies::free_balance(AUSD, &ALICE), 50); + assert_eq!(LoansModule::positions(BTC, ALICE).debit, 500); + assert_eq!(LoansModule::positions(BTC, ALICE).collateral, 100); + + assert_ok!(CDPEngineModule::set_collateral_params( + RuntimeOrigin::signed(ALICE), + BTC, + Change::NoChange, + Change::NewValue(Some(Ratio::max_value())), + Change::NoChange, + Change::NoChange, + Change::NoChange, + )); + + Some((BTC, ALICE)) + } + fn setup_settle() -> Option<(CurrencyId, AccountId)> { + setup_default_collateral(BTC); + + assert_ok!(CDPEngineModule::adjust_position(&ALICE, BTC, 100, 500)); + + // changes alice into unsafe position + assert_ok!(CDPEngineModule::set_collateral_params( + RuntimeOrigin::signed(ALICE), + BTC, + Change::NoChange, + Change::NewValue(Some(Ratio::saturating_from_rational(3, 1))), + Change::NoChange, + Change::NoChange, + Change::NoChange, + )); + + mock_shutdown(); + Some((BTC, ALICE)) + } +} + impl Config for Runtime { type PriceSource = MockPriceSource; type DefaultLiquidationRatio = DefaultLiquidationRatio; @@ -427,6 +533,8 @@ impl Config for Runtime { type EVMBridge = module_evm_bridge::EVMBridge; type SettleErc20EvmOrigin = SettleErc20EvmOrigin; type WeightInfo = (); + #[cfg(feature = "runtime-benchmarks")] + type BenchmarkHelper = MockBenchmarkHelper; } type UncheckedExtrinsic = frame_system::mocking::MockUncheckedExtrinsic; @@ -536,3 +644,15 @@ pub fn account_id_from_u32(num: u32) -> AccountId { data[0..4].copy_from_slice(&index[..]); AccountId::new(data) } + +pub fn setup_default_collateral(currency_id: CurrencyId) { + assert_ok!(CDPEngineModule::set_collateral_params( + RuntimeOrigin::signed(ALICE), + currency_id, + Change::NewValue(Some(Default::default())), + Change::NoChange, + Change::NoChange, + Change::NoChange, + Change::NewValue(10000), + )); +} diff --git a/modules/cdp-engine/src/tests.rs b/modules/cdp-engine/src/tests.rs index 7627a577c..561162d89 100644 --- a/modules/cdp-engine/src/tests.rs +++ b/modules/cdp-engine/src/tests.rs @@ -46,18 +46,6 @@ fn run_to_block_offchain(n: u64) { } } -fn setup_default_collateral(currency_id: CurrencyId) { - assert_ok!(CDPEngineModule::set_collateral_params( - RuntimeOrigin::signed(ALICE), - currency_id, - Change::NewValue(Some(Default::default())), - Change::NoChange, - Change::NoChange, - Change::NoChange, - Change::NewValue(10000), - )); -} - #[test] fn check_cdp_status_work() { ExtBuilder::default().build().execute_with(|| { diff --git a/modules/cdp-treasury/Cargo.toml b/modules/cdp-treasury/Cargo.toml index 0cf291d2c..0e1975507 100644 --- a/modules/cdp-treasury/Cargo.toml +++ b/modules/cdp-treasury/Cargo.toml @@ -9,6 +9,7 @@ log = { workspace = true } parity-scale-codec = { workspace = true } scale-info = { workspace = true } sp-runtime = { workspace = true } +frame-benchmarking = { workspace = true, optional = true } frame-support = { workspace = true } frame-system = { workspace = true } sp-std = { workspace = true } @@ -30,6 +31,7 @@ orml-tokens = { workspace = true, features = ["std"] } default = ["std"] std = [ "parity-scale-codec/std", + "frame-benchmarking?/std", "frame-support/std", "frame-system/std", "nutsfinance-stable-asset/std", @@ -40,6 +42,14 @@ std = [ "sp-std/std", "module-support/std", ] +runtime-benchmarks = [ + "frame-benchmarking/runtime-benchmarks", + "frame-support/runtime-benchmarks", + "frame-system/runtime-benchmarks", + "sp-runtime/runtime-benchmarks", + "orml-tokens/runtime-benchmarks", + "module-dex/runtime-benchmarks", +] try-runtime = [ "frame-support/try-runtime", "frame-system/try-runtime", diff --git a/modules/cdp-treasury/src/benchmarking.rs b/modules/cdp-treasury/src/benchmarking.rs new file mode 100644 index 000000000..010468ada --- /dev/null +++ b/modules/cdp-treasury/src/benchmarking.rs @@ -0,0 +1,117 @@ +// This file is part of Acala. + +// Copyright (C) 2020-2025 Acala Foundation. +// SPDX-License-Identifier: GPL-3.0-or-later WITH Classpath-exception-2.0 + +// This program 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. + +// This program 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 this program. If not, see . + +use super::*; +use frame_benchmarking::v2::*; +use frame_support::assert_ok; +use frame_system::RawOrigin; + +/// Helper trait for benchmarking. +pub trait BenchmarkHelper { + fn setup_dex_pools(caller: AccountId) -> Option; +} + +impl BenchmarkHelper for () { + fn setup_dex_pools(_caller: AccountId) -> Option { + None + } +} + +#[benchmarks] +mod benchmarks { + use super::*; + + #[benchmark] + fn auction_collateral(b: Linear<1, { T::MaxAuctionsCount::get() }>) { + let amount = 1_000_000_000_000_000u128; + let auction_size = (1_000 * amount) / b as u128; + + assert_ok!(Pallet::::set_expected_collateral_auction_size( + RawOrigin::Root.into(), + T::GetStableCurrencyId::get(), + auction_size + )); + + assert_ok!(T::Currency::deposit( + T::GetStableCurrencyId::get(), + &Pallet::::account_id(), + 10_000 * amount + )); + + #[extrinsic_call] + _( + RawOrigin::Root, + T::GetStableCurrencyId::get(), + 1_000 * amount, + 1_000 * amount, + true, + ); + } + + #[benchmark] + fn exchange_collateral_to_stable() { + let amount = 1_000_000_000_000_000u128; + let caller: T::AccountId = account("caller", 0, 0); + + let staking_currency_id = T::BenchmarkHelper::setup_dex_pools(caller.clone()).unwrap(); + + assert_ok!(T::Currency::deposit( + T::GetStableCurrencyId::get(), + &Pallet::::account_id(), + 10_000 * amount + )); + assert_ok!(T::Currency::deposit( + T::GetStableCurrencyId::get(), + &caller, + 1000 * amount + )); + assert_ok!(T::Currency::deposit(staking_currency_id, &caller, 1000 * amount)); + + assert_ok!(Pallet::::deposit_collateral( + &caller, + staking_currency_id, + 100 * amount + )); + + #[extrinsic_call] + _( + RawOrigin::Root, + staking_currency_id, + SwapLimit::ExactSupply(100 * amount, 0), + ); + } + + #[benchmark] + fn set_expected_collateral_auction_size() { + let amount = 1_000_000_000_000_000u128; + #[extrinsic_call] + _(RawOrigin::Root, T::GetStableCurrencyId::get(), amount); + } + + #[benchmark] + fn extract_surplus_to_treasury() { + let amount = 1_000_000_000_000_000u128; + + assert_ok!(Pallet::::on_system_surplus(5 * amount)); + + #[extrinsic_call] + _(RawOrigin::Root, amount); + } + + impl_benchmark_test_suite!(Pallet, crate::mock::ExtBuilder::default().build(), crate::mock::Runtime); +} diff --git a/modules/cdp-treasury/src/lib.rs b/modules/cdp-treasury/src/lib.rs index 1276063cf..cc527d4f0 100644 --- a/modules/cdp-treasury/src/lib.rs +++ b/modules/cdp-treasury/src/lib.rs @@ -43,10 +43,14 @@ use sp_runtime::{ }; use sp_std::prelude::*; +#[cfg(feature = "runtime-benchmarks")] +mod benchmarking; mod mock; mod tests; pub mod weights; +#[cfg(feature = "runtime-benchmarks")] +pub use benchmarking::BenchmarkHelper; pub use module::*; pub use weights::WeightInfo; @@ -100,6 +104,9 @@ pub mod module { /// Weight information for the extrinsics in this module. type WeightInfo: WeightInfo; + + #[cfg(feature = "runtime-benchmarks")] + type BenchmarkHelper: BenchmarkHelper; } #[pallet::error] @@ -187,7 +194,7 @@ pub mod module { #[pallet::call_index(0)] #[pallet::weight(T::WeightInfo::extract_surplus_to_treasury())] pub fn extract_surplus_to_treasury(origin: OriginFor, #[pallet::compact] amount: Balance) -> DispatchResult { - T::UpdateOrigin::ensure_origin(origin)?; + T::UpdateOrigin::ensure_origin_or_root(origin)?; T::Currency::transfer( T::GetStableCurrencyId::get(), &Self::account_id(), @@ -221,7 +228,7 @@ pub mod module { #[pallet::compact] target: Balance, split: bool, ) -> DispatchResultWithPostInfo { - T::UpdateOrigin::ensure_origin(origin)?; + T::UpdateOrigin::ensure_origin_or_root(origin)?; let created_auctions = >::create_collateral_auctions( currency_id, amount, @@ -245,7 +252,7 @@ pub mod module { currency_id: CurrencyId, swap_limit: SwapLimit, ) -> DispatchResult { - T::UpdateOrigin::ensure_origin(origin)?; + T::UpdateOrigin::ensure_origin_or_root(origin)?; // the supply collateral must not be occupied by the auction. Self::swap_collateral_to_stable(currency_id, swap_limit, false)?; Ok(()) @@ -265,7 +272,7 @@ pub mod module { currency_id: CurrencyId, #[pallet::compact] size: Balance, ) -> DispatchResult { - T::UpdateOrigin::ensure_origin(origin)?; + T::UpdateOrigin::ensure_origin_or_root(origin)?; ExpectedCollateralAuctionSize::::insert(currency_id, size); Self::deposit_event(Event::ExpectedCollateralAuctionSizeUpdated { collateral_type: currency_id, @@ -282,7 +289,7 @@ pub mod module { #[pallet::call_index(4)] #[pallet::weight((T::WeightInfo::set_expected_collateral_auction_size(), DispatchClass::Operational))] pub fn set_debit_offset_buffer(origin: OriginFor, #[pallet::compact] amount: Balance) -> DispatchResult { - T::UpdateOrigin::ensure_origin(origin)?; + T::UpdateOrigin::ensure_origin_or_root(origin)?; DebitOffsetBuffer::::mutate(|v| { if *v != amount { *v = amount; diff --git a/modules/cdp-treasury/src/mock.rs b/modules/cdp-treasury/src/mock.rs index 8471af3ec..b898e0748 100644 --- a/modules/cdp-treasury/src/mock.rs +++ b/modules/cdp-treasury/src/mock.rs @@ -22,7 +22,7 @@ use super::*; use frame_support::{ - construct_runtime, derive_impl, ord_parameter_types, parameter_types, + assert_ok, construct_runtime, derive_impl, ord_parameter_types, parameter_types, traits::{ConstU128, ConstU32, ConstU64, EitherOfDiverse, Nothing}, }; use frame_system::{EnsureRoot, EnsureSignedBy}; @@ -80,6 +80,8 @@ impl orml_tokens::Config for Runtime { type MaxReserves = (); type ReserveIdentifier = [u8; 8]; type DustRemovalWhitelist = Nothing; + #[cfg(feature = "runtime-benchmarks")] + type BenchmarkHelper = (); } impl pallet_balances::Config for Runtime { @@ -134,6 +136,8 @@ impl module_dex::Config for Runtime { type ListingOrigin = EnsureSignedBy; type ExtendedProvisioningBlocks = ConstU64<0>; type OnLiquidityPoolUpdated = (); + #[cfg(feature = "runtime-benchmarks")] + type BenchmarkHelper = (); } parameter_types! { @@ -183,6 +187,34 @@ parameter_types! { ]; } +#[cfg(feature = "runtime-benchmarks")] +pub struct MockBehchmarkHelper; +#[cfg(feature = "runtime-benchmarks")] +impl BenchmarkHelper for MockBehchmarkHelper { + fn setup_dex_pools(caller: AccountId) -> Option { + let amount = 1_000_000_000_000_000u128; + assert_ok!(Currencies::deposit(DOT, &caller, 1000 * amount)); + assert_ok!(Currencies::deposit(AUSD, &caller, 1000 * amount)); + assert_ok!(Currencies::deposit( + DOT, + &CDPTreasuryModule::account_id(), + 1000 * amount + )); + + assert_ok!(DEXModule::add_liquidity( + RuntimeOrigin::signed(caller), + DOT, + AUSD, + 1000 * amount, + 1000 * amount, + 0, + false + )); + + Some(DOT) + } +} + impl Config for Runtime { type Currency = Currencies; type GetStableCurrencyId = GetStableCurrencyId; @@ -195,6 +227,8 @@ impl Config for Runtime { type TreasuryAccount = TreasuryAccount; type WeightInfo = (); type StableAsset = MockStableAsset; + #[cfg(feature = "runtime-benchmarks")] + type BenchmarkHelper = MockBehchmarkHelper; } type Block = frame_system::mocking::MockBlock; diff --git a/modules/collator-selection/src/benchmarking.rs b/modules/collator-selection/src/benchmarking.rs new file mode 100644 index 000000000..8c91aa914 --- /dev/null +++ b/modules/collator-selection/src/benchmarking.rs @@ -0,0 +1,307 @@ +// This file is part of Acala. + +// Copyright (C) 2020-2025 Acala Foundation. +// SPDX-License-Identifier: GPL-3.0-or-later WITH Classpath-exception-2.0 + +// This program 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. + +// This program 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 this program. If not, see . + +use super::*; +use frame_benchmarking::v2::*; +use frame_support::{ + assert_ok, + pallet_prelude::Decode, + traits::{Currency, EstimateNextSessionRotation, Get, Hooks}, +}; +use frame_system::RawOrigin; +use pallet_authorship::EventHandler; +use pallet_session::SessionManager; +use sp_std::{vec, vec::Vec}; + +fn register_candidates(count: u32) +where + T: Config + pallet_session::Config, +{ + let candidates = (0..count).map(|c| account("candidate", c, 0)).collect::>(); + assert!(CandidacyBond::::get() > 0u32.into(), "Bond cannot be zero!"); + for (index, who) in candidates.iter().enumerate() { + T::Currency::make_free_balance_be(&who, CandidacyBond::::get() * 2u32.into()); + + let mut keys = [1u8; 128]; + keys[0..4].copy_from_slice(&(index as u32).to_be_bytes()); + let keys: ::Keys = Decode::decode(&mut &keys[..]).unwrap(); + assert_ok!(pallet_session::Pallet::::set_keys( + RawOrigin::Signed(who.clone()).into(), + keys, + vec![] + )); + + assert_ok!(Pallet::::register_as_candidate( + RawOrigin::Signed(who.clone()).into() + )); + } +} + +#[benchmarks( + where + T: Config + pallet_session::Config + pallet_authorship::Config, +)] +mod benchmarks { + use super::*; + + #[benchmark] + fn set_invulnerables(b: Linear<1, { T::MaxInvulnerables::get() }>) { + let new_invulnerables = (0..b).map(|c| account("candidate", c, 0)).collect::>(); + + #[extrinsic_call] + _(RawOrigin::Root, new_invulnerables.clone()); + + frame_system::Pallet::::assert_last_event( + Event::NewInvulnerables { + new_invulnerables: new_invulnerables, + } + .into(), + ); + } + + #[benchmark] + fn set_desired_candidates() { + let max: u32 = T::MaxInvulnerables::get(); + + #[extrinsic_call] + _(RawOrigin::Root, max.clone()); + + frame_system::Pallet::::assert_last_event( + Event::NewDesiredCandidates { + new_desired_candidates: max, + } + .into(), + ); + } + + #[benchmark] + fn set_candidacy_bond() { + let bond = T::Currency::minimum_balance() * 10u32.into(); + + #[extrinsic_call] + _(RawOrigin::Root, bond.clone()); + + frame_system::Pallet::::assert_last_event( + Event::NewCandidacyBond { + new_candidacy_bond: bond, + } + .into(), + ); + } + + // worse case is when we have all the max-candidate slots filled except one, and we fill that + // one. + #[benchmark] + fn register_as_candidate(c: Linear<{ T::MinCandidates::get() }, { T::MaxCandidates::get() }>) { + CandidacyBond::::put(T::Currency::minimum_balance()); + DesiredCandidates::::put(c); + register_candidates::(c - 1); + + let caller: T::AccountId = account("candidate", c, 0); + let bond = T::Currency::minimum_balance() * 2u32.into(); + T::Currency::make_free_balance_be(&caller, bond.clone()); + + let keys: ::Keys = Decode::decode(&mut &[0u8; 128][..]).unwrap(); + pallet_session::Pallet::::set_keys(RawOrigin::Signed(caller.clone()).into(), keys, vec![]).unwrap(); + + #[extrinsic_call] + _(RawOrigin::Signed(caller.clone())); + + frame_system::Pallet::::assert_last_event( + Event::CandidateAdded { + who: caller, + bond: bond / 2u32.into(), + } + .into(), + ); + } + + #[benchmark] + fn register_candidate(c: Linear<1, { T::MaxCandidates::get() }>) { + CandidacyBond::::put(T::Currency::minimum_balance()); + DesiredCandidates::::put(c); + register_candidates::(c - 1); + + let caller: T::AccountId = account("candidate", c, 0); + let bond = T::Currency::minimum_balance() * 2u32.into(); + T::Currency::make_free_balance_be(&caller, bond.clone()); + + let keys: ::Keys = Decode::decode(&mut &[0u8; 128][..]).unwrap(); + pallet_session::Pallet::::set_keys(RawOrigin::Signed(caller.clone()).into(), keys, vec![]).unwrap(); + + #[extrinsic_call] + _(RawOrigin::Root, caller.clone()); + + frame_system::Pallet::::assert_last_event( + Event::CandidateAdded { + who: caller, + bond: 0u32.into(), + } + .into(), + ); + } + + // worse case is the last candidate leaving. + #[benchmark] + fn leave_intent(c: Linear<{ T::MinCandidates::get() + 1 }, { T::MaxCandidates::get() }>) { + CandidacyBond::::put(T::Currency::minimum_balance()); + DesiredCandidates::::put(c); + register_candidates::(c); + + let leaving = Candidates::::get().into_iter().last().unwrap(); + whitelist_account!(leaving); + + #[extrinsic_call] + _(RawOrigin::Signed(leaving.clone())); + + frame_system::Pallet::::assert_last_event(Event::CandidateRemoved { who: leaving }.into()); + } + + #[benchmark] + fn withdraw_bond() { + let c = T::MaxCandidates::get(); + CandidacyBond::::put(T::Currency::minimum_balance()); + DesiredCandidates::::put(c); + register_candidates::(c); + + let leaving = Candidates::::get().into_iter().last().unwrap(); + whitelist_account!(leaving); + + assert_ok!(Pallet::::leave_intent(RawOrigin::Signed(leaving.clone()).into())); + + let session_duration = ::NextSessionRotation::average_session_length(); + + for i in 0..SESSION_DELAY { + pallet_session::Pallet::::on_initialize(session_duration * i.into()); + } + + #[extrinsic_call] + _(RawOrigin::Signed(leaving)); + } + + // worse case is paying a non-existing candidate account. + #[benchmark] + fn note_author() { + let c = T::MaxCandidates::get(); + CandidacyBond::::put(T::Currency::minimum_balance()); + DesiredCandidates::::put(c); + register_candidates::(c); + + T::Currency::make_free_balance_be(&Pallet::::account_id(), T::Currency::minimum_balance() * 2u32.into()); + let author = account("author", 0, 0); + T::Currency::make_free_balance_be(&author, T::Currency::minimum_balance()); + assert!(T::Currency::free_balance(&author) == T::Currency::minimum_balance()); + + #[block] + { + Pallet::::note_author(author); + } + } + + // worse case is on new session. + #[benchmark] + fn new_session() { + let c = T::MaxCandidates::get(); + CandidacyBond::::put(T::Currency::minimum_balance()); + DesiredCandidates::::put(c); + + frame_system::Pallet::::set_block_number(0u32.into()); + + register_candidates::(c); + + frame_system::Pallet::::set_block_number(20u32.into()); + + assert!(Candidates::::get().len() == c as usize); + + #[block] + { + Pallet::::new_session(0); + } + } + + #[benchmark] + fn start_session( + r: Linear<{ T::MinCandidates::get() }, { T::MaxCandidates::get() }>, + c: Linear<{ T::MinCandidates::get() }, { T::MaxCandidates::get() }>, + ) { + CandidacyBond::::put(T::Currency::minimum_balance()); + DesiredCandidates::::put(c); + + frame_system::Pallet::::set_block_number(0u32.into()); + register_candidates::(c); + + frame_system::Pallet::::set_block_number(20u32.into()); + + let session_duration = ::NextSessionRotation::average_session_length(); + pallet_session::Pallet::::on_initialize(session_duration); + pallet_session::Pallet::::on_initialize(session_duration * 2u32.into()); + + assert!(Candidates::::get().len() == c as usize); + + #[block] + { + Pallet::::start_session(2); + } + } + + #[benchmark] + fn end_session( + c: Linear<{ T::MinCandidates::get() }, { T::MaxCandidates::get() }>, + r: Linear<{ T::MinCandidates::get() }, { T::MaxCandidates::get() }>, + ) { + CandidacyBond::::put(T::Currency::minimum_balance()); + DesiredCandidates::::put(c); + + frame_system::Pallet::::set_block_number(0u32.into()); + register_candidates::(c); + + let candidates = Candidates::::get(); + let removals = c + .checked_sub(r) + .unwrap_or_default() + .checked_sub(T::MinCandidates::get()) + .unwrap_or_default(); + + let mut count = 0; + candidates.iter().for_each(|candidate| { + if count < removals { + // point = 0, will be removed. + SessionPoints::::insert(&candidate, 0); + } else { + SessionPoints::::insert( + &candidate, + T::CollatorKickThreshold::get().mul_floor(1000 * POINT_PER_BLOCK), + ); + } + count += 1; + }); + + frame_system::Pallet::::set_block_number(20u32.into()); + + assert!(Candidates::::get().len() == c as usize); + + #[block] + { + Pallet::::end_session(0); + } + + assert!(Candidates::::get().len() == (c - removals) as usize); + } + + impl_benchmark_test_suite!(Pallet, crate::mock::new_test_ext(), crate::mock::Test); +} diff --git a/modules/collator-selection/src/lib.rs b/modules/collator-selection/src/lib.rs index 30fb6f443..9fc54e15f 100644 --- a/modules/collator-selection/src/lib.rs +++ b/modules/collator-selection/src/lib.rs @@ -65,9 +65,10 @@ pub use pallet::*; +#[cfg(feature = "runtime-benchmarks")] +mod benchmarking; #[cfg(test)] mod mock; - #[cfg(test)] mod tests; @@ -287,7 +288,7 @@ pub mod pallet { #[pallet::call_index(0)] #[pallet::weight(T::WeightInfo::set_invulnerables(new.len() as u32))] pub fn set_invulnerables(origin: OriginFor, new: Vec) -> DispatchResult { - T::UpdateOrigin::ensure_origin(origin)?; + T::UpdateOrigin::ensure_origin_or_root(origin)?; let bounded_new: BoundedVec = new.try_into().map_err(|_| Error::::MaxInvulnerablesExceeded)?; >::put(&bounded_new); @@ -300,7 +301,7 @@ pub mod pallet { #[pallet::call_index(1)] #[pallet::weight(T::WeightInfo::set_desired_candidates())] pub fn set_desired_candidates(origin: OriginFor, #[pallet::compact] max: u32) -> DispatchResult { - T::UpdateOrigin::ensure_origin(origin)?; + T::UpdateOrigin::ensure_origin_or_root(origin)?; if max > T::MaxCandidates::get() { Err(Error::::MaxCandidatesExceeded)?; } @@ -314,7 +315,7 @@ pub mod pallet { #[pallet::call_index(2)] #[pallet::weight(T::WeightInfo::set_candidacy_bond())] pub fn set_candidacy_bond(origin: OriginFor, #[pallet::compact] bond: BalanceOf) -> DispatchResult { - T::UpdateOrigin::ensure_origin(origin)?; + T::UpdateOrigin::ensure_origin_or_root(origin)?; >::put(bond); Self::deposit_event(Event::NewCandidacyBond { new_candidacy_bond: bond, @@ -344,7 +345,7 @@ pub mod pallet { #[pallet::call_index(4)] #[pallet::weight(T::WeightInfo::register_candidate(T::MaxCandidates::get()))] pub fn register_candidate(origin: OriginFor, new_candidate: T::AccountId) -> DispatchResultWithPostInfo { - T::UpdateOrigin::ensure_origin(origin)?; + T::UpdateOrigin::ensure_origin_or_root(origin)?; let bounded_candidates_len = Self::do_register_candidate(&new_candidate, Zero::zero())?; diff --git a/modules/collator-selection/src/mock.rs b/modules/collator-selection/src/mock.rs index 050ca6713..a6f58dda0 100644 --- a/modules/collator-selection/src/mock.rs +++ b/modules/collator-selection/src/mock.rs @@ -154,7 +154,7 @@ ord_parameter_types! { parameter_types! { pub const PotId: PalletId = PalletId(*b"PotStake"); - pub const CollatorKickThreshold: Permill = Permill::from_percent(100); + pub const CollatorKickThreshold: Permill = Permill::from_percent(90); } impl Config for Test { diff --git a/modules/currencies/Cargo.toml b/modules/currencies/Cargo.toml index 6a89371bc..50f52a07e 100644 --- a/modules/currencies/Cargo.toml +++ b/modules/currencies/Cargo.toml @@ -12,9 +12,13 @@ sp-runtime = { workspace = true } sp-io = { workspace = true } sp-std = { workspace = true } +frame-benchmarking = { workspace = true, optional = true } frame-support = { workspace = true } frame-system = { workspace = true } +pallet-balances = { workspace = true, optional = true } + +orml-tokens = { workspace = true, optional = true } orml-traits = { workspace = true } primitives = { workspace = true } @@ -35,6 +39,7 @@ module-evm-bridge = { workspace = true, features = ["std"] } default = ["std"] std = [ "parity-scale-codec/std", + "frame-benchmarking?/std", "frame-support/std", "frame-system/std", "orml-traits/std", @@ -46,6 +51,11 @@ std = [ "sp-std/std", "module-support/std", ] +runtime-benchmarks = [ + "frame-benchmarking/runtime-benchmarks", + "pallet-balances/runtime-benchmarks", + "orml-tokens/runtime-benchmarks", +] try-runtime = [ "frame-support/try-runtime", "frame-system/try-runtime", diff --git a/modules/currencies/src/benchmarking.rs b/modules/currencies/src/benchmarking.rs new file mode 100644 index 000000000..e5d5631e3 --- /dev/null +++ b/modules/currencies/src/benchmarking.rs @@ -0,0 +1,270 @@ +// This file is part of Acala. + +// Copyright (C) 2020-2025 Acala Foundation. +// SPDX-License-Identifier: GPL-3.0-or-later WITH Classpath-exception-2.0 + +// This program 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. + +// This program 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 this program. If not, see . + +use super::*; +use frame_benchmarking::v2::*; +use frame_support::{assert_ok, traits::fungibles::Mutate}; +use frame_system::RawOrigin; +use sp_runtime::traits::UniqueSaturatedInto; +use sp_std::vec; + +/// Helper trait for benchmarking. +pub trait BenchmarkHelper { + fn setup_get_staking_currency_id_and_amount() -> Option<(CurrencyId, Balance)>; + fn setup_get_treasury_account() -> Option; +} + +impl BenchmarkHelper for () { + fn setup_get_staking_currency_id_and_amount() -> Option<(CurrencyId, Balance)> { + None + } + fn setup_get_treasury_account() -> Option { + None + } +} + +#[benchmarks( + where + T: Config + pallet_balances::Config> + orml_tokens::Config>, +)] +mod benchmarks { + use super::*; + + // `transfer` non-native currency + #[benchmark] + fn transfer_non_native_currency() { + let from: T::AccountId = account("from", 0, 0); + + let (staking_currency_id, amount) = + ::BenchmarkHelper::setup_get_staking_currency_id_and_amount().unwrap(); + + T::MultiCurrency::set_balance(staking_currency_id, &from, amount); + + let to: T::AccountId = account("to", 0, 0); + let to_lookup = T::Lookup::unlookup(to.clone()); + + #[extrinsic_call] + transfer(RawOrigin::Signed(from), to_lookup, staking_currency_id, amount); + + assert_eq!(Pallet::::total_balance(staking_currency_id, &to), amount); + } + + // `transfer_native_currency` in worst case + // * will create the `to` account. + // * will kill the `from` account. + #[benchmark] + fn transfer_native_currency() { + let from: T::AccountId = account("from", 0, 0); + let who_lookup = T::Lookup::unlookup(from.clone()); + + let existential_deposit = pallet_balances::Pallet::::minimum_balance(); + let amount = existential_deposit.saturating_mul(1000u32.into()); + let update_amount: AmountOf = amount.unique_saturated_into(); + + assert_ok!(Pallet::::update_balance( + RawOrigin::Root.into(), + who_lookup.clone(), + T::GetNativeCurrencyId::get(), + update_amount + )); + + let to: T::AccountId = account("to", 0, 0); + let to_lookup = T::Lookup::unlookup(to.clone()); + + #[extrinsic_call] + transfer( + RawOrigin::Signed(from), + to_lookup, + T::GetNativeCurrencyId::get(), + amount, + ); + + assert_eq!(Pallet::::total_balance(T::GetNativeCurrencyId::get(), &to), amount); + } + + // `update_balance` for non-native currency + #[benchmark] + fn update_balance_non_native_currency() { + let who: T::AccountId = account("who", 0, 0); + let who_lookup = T::Lookup::unlookup(who.clone()); + + let (staking_currency_id, amount) = + ::BenchmarkHelper::setup_get_staking_currency_id_and_amount().unwrap(); + + let update_amount = amount.unique_saturated_into(); + + #[extrinsic_call] + update_balance(RawOrigin::Root, who_lookup, staking_currency_id, update_amount); + + assert_eq!(Pallet::::total_balance(staking_currency_id, &who), amount); + } + + // `update_balance` for native currency + // * will create the `who` account. + #[benchmark] + fn update_balance_native_currency_creating() { + let who: T::AccountId = account("who", 0, 0); + let who_lookup = T::Lookup::unlookup(who.clone()); + + let existential_deposit = pallet_balances::Pallet::::minimum_balance(); + let balance = existential_deposit.saturating_mul(1000u32.into()); + let update_amount = balance.unique_saturated_into(); + + #[extrinsic_call] + update_balance( + RawOrigin::Root, + who_lookup, + T::GetNativeCurrencyId::get(), + update_amount, + ); + + assert_eq!(Pallet::::total_balance(T::GetNativeCurrencyId::get(), &who), balance); + } + + // `update_balance` for native currency + // * will kill the `who` account. + #[benchmark] + fn update_balance_native_currency_killing() { + let who: T::AccountId = account("who", 0, 0); + let who_lookup = T::Lookup::unlookup(who.clone()); + + let existential_deposit = pallet_balances::Pallet::::minimum_balance(); + let balance = existential_deposit.saturating_mul(1000u32.into()); + let update_amount: AmountOf = balance.unique_saturated_into(); + + assert_ok!(Pallet::::update_balance( + RawOrigin::Root.into(), + who_lookup.clone(), + T::GetNativeCurrencyId::get(), + update_amount + )); + + #[extrinsic_call] + update_balance( + RawOrigin::Root, + who_lookup, + T::GetNativeCurrencyId::get(), + -update_amount, + ); + + assert_eq!( + Pallet::::total_balance(T::GetNativeCurrencyId::get(), &who), + 0u32.into() + ); + } + + #[benchmark] + fn sweep_dust(c: Linear<1, 3>) { + let (staking_currency_id, amount) = + ::BenchmarkHelper::setup_get_staking_currency_id_and_amount().unwrap(); + + let treasury: T::AccountId = ::BenchmarkHelper::setup_get_treasury_account().unwrap(); + let accounts: Vec = vec!["alice", "bob", "charlie"] + .into_iter() + .map(|x| account(x, 0, 0)) + .collect(); + + let staking_currency_id_ed = orml_tokens::Pallet::::minimum_balance(staking_currency_id); + let dust_balance = staking_currency_id_ed.saturating_sub(1u32.into()); + + accounts.iter().for_each(|account| { + orml_tokens::Accounts::::insert( + account, + staking_currency_id, + orml_tokens::AccountData { + free: dust_balance, + frozen: 0u32.into(), + reserved: 0u32.into(), + }, + ); + }); + + T::MultiCurrency::set_balance(staking_currency_id, &treasury, amount); + + #[extrinsic_call] + _(RawOrigin::Root, staking_currency_id, (&accounts[..c as usize]).to_vec()); + + (&accounts[..c as usize]).iter().for_each(|account| { + assert_eq!( + orml_tokens::Accounts::::contains_key(account, staking_currency_id), + false + ); + }); + assert_eq!( + orml_tokens::Pallet::::free_balance(staking_currency_id, &treasury), + amount + (dust_balance * c.into()) + ); + } + + #[benchmark] + fn force_set_lock() { + let who: T::AccountId = account("who", 0, 0); + let who_lookup = T::Lookup::unlookup(who.clone()); + let lock_id: LockIdentifier = *b"aca/test"; + + let (staking_currency_id, amount) = + ::BenchmarkHelper::setup_get_staking_currency_id_and_amount().unwrap(); + + T::MultiCurrency::set_balance(staking_currency_id, &who, amount); + + #[extrinsic_call] + _(RawOrigin::Root, who_lookup, staking_currency_id, amount, lock_id); + + assert_eq!( + orml_tokens::Pallet::::locks(&who, staking_currency_id), + vec![orml_tokens::BalanceLock { + id: lock_id, + amount: amount + }] + ); + } + + #[benchmark] + fn force_remove_lock() { + let who: T::AccountId = account("who", 0, 0); + let who_lookup = T::Lookup::unlookup(who.clone()); + let lock_id: LockIdentifier = *b"aca/test"; + + let (staking_currency_id, amount) = + ::BenchmarkHelper::setup_get_staking_currency_id_and_amount().unwrap(); + + T::MultiCurrency::set_balance(staking_currency_id, &who, amount); + + assert_ok!(Pallet::::force_set_lock( + RawOrigin::Root.into(), + who_lookup.clone(), + staking_currency_id, + amount, + lock_id + )); + assert_eq!( + orml_tokens::Pallet::::locks(&who, staking_currency_id), + vec![orml_tokens::BalanceLock { + id: lock_id, + amount: amount + }] + ); + + #[extrinsic_call] + _(RawOrigin::Root, who_lookup, staking_currency_id, lock_id); + + assert_eq!(orml_tokens::Pallet::::locks(&who, staking_currency_id), vec![]); + } + + impl_benchmark_test_suite!(Pallet, crate::mock::ExtBuilder::default().build(), crate::mock::Runtime); +} diff --git a/modules/currencies/src/lib.rs b/modules/currencies/src/lib.rs index 1c5a788d1..c42f6a783 100644 --- a/modules/currencies/src/lib.rs +++ b/modules/currencies/src/lib.rs @@ -51,10 +51,14 @@ use sp_runtime::{ }; use sp_std::{fmt::Debug, marker, result, vec::Vec}; +#[cfg(feature = "runtime-benchmarks")] +mod benchmarking; mod mock; mod tests; pub mod weights; +#[cfg(feature = "runtime-benchmarks")] +pub use benchmarking::BenchmarkHelper; pub use module::*; pub use weights::WeightInfo; @@ -111,6 +115,9 @@ pub mod module { /// Handler to burn or transfer account's dust type OnDust: OnDust>; + + #[cfg(feature = "runtime-benchmarks")] + type BenchmarkHelper: BenchmarkHelper>; } #[pallet::error] @@ -231,7 +238,7 @@ pub mod module { currency_id: CurrencyId, accounts: Vec, ) -> DispatchResult { - T::SweepOrigin::ensure_origin(origin)?; + T::SweepOrigin::ensure_origin_or_root(origin)?; if let CurrencyId::Erc20(_) = currency_id { return Err(Error::::Erc20InvalidOperation.into()); } diff --git a/modules/currencies/src/mock.rs b/modules/currencies/src/mock.rs index 76bbb2d49..cf9021802 100644 --- a/modules/currencies/src/mock.rs +++ b/modules/currencies/src/mock.rs @@ -98,6 +98,8 @@ impl orml_tokens::Config for Runtime { type MaxReserves = (); type ReserveIdentifier = [u8; 8]; type DustRemovalWhitelist = Nothing; + #[cfg(feature = "runtime-benchmarks")] + type BenchmarkHelper = (); } pub const NATIVE_CURRENCY_ID: CurrencyId = CurrencyId::Token(TokenSymbol::ACA); @@ -212,6 +214,18 @@ parameter_types! { pub Erc20HoldingAccount: H160 = primitives::evm::ERC20_HOLDING_ACCOUNT; } +#[cfg(feature = "runtime-benchmarks")] +pub struct MockBenchmarkHelper; +#[cfg(feature = "runtime-benchmarks")] +impl BenchmarkHelper for MockBenchmarkHelper { + fn setup_get_staking_currency_id_and_amount() -> Option<(CurrencyId, Balance)> { + Some((DOT, 10000)) + } + fn setup_get_treasury_account() -> Option { + Some(DustAccount::get()) + } +} + impl Config for Runtime { type MultiCurrency = Tokens; type NativeCurrency = AdaptedBasicCurrency; @@ -223,6 +237,8 @@ impl Config for Runtime { type GasToWeight = GasToWeight; type SweepOrigin = EnsureSignedBy; type OnDust = crate::TransferDust; + #[cfg(feature = "runtime-benchmarks")] + type BenchmarkHelper = MockBenchmarkHelper; } pub type NativeCurrency = Currency; diff --git a/modules/dex-oracle/Cargo.toml b/modules/dex-oracle/Cargo.toml index 20f69850d..8769ab5a2 100644 --- a/modules/dex-oracle/Cargo.toml +++ b/modules/dex-oracle/Cargo.toml @@ -36,6 +36,12 @@ std = [ "sp-std/std", "module-support/std", ] +runtime-benchmarks = [ + "frame-benchmarking/runtime-benchmarks", + "frame-support/runtime-benchmarks", + "frame-system/runtime-benchmarks", + "sp-runtime/runtime-benchmarks", +] try-runtime = [ "frame-support/try-runtime", "frame-system/try-runtime", diff --git a/modules/dex-oracle/src/benchmarking.rs b/modules/dex-oracle/src/benchmarking.rs new file mode 100644 index 000000000..50a7dc2a6 --- /dev/null +++ b/modules/dex-oracle/src/benchmarking.rs @@ -0,0 +1,95 @@ +// This file is part of Acala. + +// Copyright (C) 2020-2025 Acala Foundation. +// SPDX-License-Identifier: GPL-3.0-or-later WITH Classpath-exception-2.0 + +// This program 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. + +// This program 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 this program. If not, see . + +use super::*; +use frame_benchmarking::v2::*; +use frame_support::assert_ok; +use frame_system::RawOrigin; + +/// Helper trait for benchmarking. +pub trait BenchmarkHelper { + fn setup_on_initialize(n: u32, u: u32); + fn setup_inject_liquidity() -> Option<(CurrencyId, CurrencyId, Moment)>; +} + +impl BenchmarkHelper for () { + fn setup_on_initialize(_n: u32, _u: u32) {} + fn setup_inject_liquidity() -> Option<(CurrencyId, CurrencyId, Moment)> { + None + } +} + +#[benchmarks] +mod benchmarks { + use super::*; + + #[benchmark] + fn on_initialize_with_update_average_prices(n: Linear<0, 3>, u: Linear<0, 3>) { + T::BenchmarkHelper::setup_on_initialize(n, u); + + #[block] + { + Pallet::::on_initialize(1u32.into()); + } + } + + #[benchmark] + fn enable_average_price() { + let (currency_id_a, currency_id_b, interval) = T::BenchmarkHelper::setup_inject_liquidity().unwrap(); + + #[extrinsic_call] + _(RawOrigin::Root, currency_id_a, currency_id_b, interval); + } + + #[benchmark] + fn disable_average_price() { + let (currency_id_a, currency_id_b, interval) = T::BenchmarkHelper::setup_inject_liquidity().unwrap(); + + assert_ok!(Pallet::::enable_average_price( + RawOrigin::Root.into(), + currency_id_a, + currency_id_b, + interval + )); + + #[extrinsic_call] + _(RawOrigin::Root, currency_id_a, currency_id_b); + } + + #[benchmark] + fn update_average_price_interval() { + let (currency_id_a, currency_id_b, interval) = T::BenchmarkHelper::setup_inject_liquidity().unwrap(); + + assert_ok!(Pallet::::enable_average_price( + RawOrigin::Root.into(), + currency_id_a, + currency_id_b, + interval + )); + + #[extrinsic_call] + _( + RawOrigin::Root, + currency_id_a, + currency_id_b, + interval.saturating_mul(10u32.into()), + ); + } + + impl_benchmark_test_suite!(Pallet, crate::mock::ExtBuilder::default().build(), crate::mock::Runtime); +} diff --git a/modules/dex-oracle/src/lib.rs b/modules/dex-oracle/src/lib.rs index b3a64cdf4..a3308e59b 100644 --- a/modules/dex-oracle/src/lib.rs +++ b/modules/dex-oracle/src/lib.rs @@ -34,10 +34,14 @@ use sp_runtime::{ }; use sp_std::marker::PhantomData; +#[cfg(feature = "runtime-benchmarks")] +mod benchmarking; mod mock; mod tests; pub mod weights; +#[cfg(feature = "runtime-benchmarks")] +pub use benchmarking::BenchmarkHelper; pub use module::*; pub use weights::WeightInfo; @@ -60,6 +64,9 @@ pub mod module { /// Weight information for the extrinsics in this module. type WeightInfo: WeightInfo; + + #[cfg(feature = "runtime-benchmarks")] + type BenchmarkHelper: BenchmarkHelper>; } #[pallet::error] @@ -173,7 +180,7 @@ pub mod module { currency_id_b: CurrencyId, interval: MomentOf, ) -> DispatchResult { - T::UpdateOrigin::ensure_origin(origin)?; + T::UpdateOrigin::ensure_origin_or_root(origin)?; let trading_pair = TradingPair::from_currency_ids(currency_id_a, currency_id_b).ok_or(Error::::InvalidCurrencyId)?; @@ -218,7 +225,7 @@ pub mod module { currency_id_a: CurrencyId, currency_id_b: CurrencyId, ) -> DispatchResult { - T::UpdateOrigin::ensure_origin(origin)?; + T::UpdateOrigin::ensure_origin_or_root(origin)?; let trading_pair = TradingPair::from_currency_ids(currency_id_a, currency_id_b).ok_or(Error::::InvalidCurrencyId)?; @@ -243,7 +250,7 @@ pub mod module { currency_id_b: CurrencyId, new_interval: MomentOf, ) -> DispatchResult { - T::UpdateOrigin::ensure_origin(origin)?; + T::UpdateOrigin::ensure_origin_or_root(origin)?; let trading_pair = TradingPair::from_currency_ids(currency_id_a, currency_id_b).ok_or(Error::::InvalidCurrencyId)?; diff --git a/modules/dex-oracle/src/mock.rs b/modules/dex-oracle/src/mock.rs index be720222b..a6ddd3746 100644 --- a/modules/dex-oracle/src/mock.rs +++ b/modules/dex-oracle/src/mock.rs @@ -46,6 +46,7 @@ mod dex_oracle { parameter_types! { pub static AUSDDOTPair: TradingPair = TradingPair::from_currency_ids(AUSD, DOT).unwrap(); pub static ACADOTPair: TradingPair = TradingPair::from_currency_ids(ACA, DOT).unwrap(); + pub static ACAAUSDPair: TradingPair = TradingPair::from_currency_ids(ACA, AUSD).unwrap(); } #[derive_impl(frame_system::config_preludes::TestDefaultConfig as frame_system::DefaultConfig)] @@ -66,6 +67,7 @@ impl pallet_timestamp::Config for Runtime { parameter_types! { static AusdDotPool: (Balance, Balance) = (Zero::zero(), Zero::zero()); static AcaDotPool: (Balance, Balance) = (Zero::zero(), Zero::zero()); + static AcaAusdPool: (Balance, Balance) = (Zero::zero(), Zero::zero()); } pub fn set_pool(trading_pair: &TradingPair, pool_0: Balance, pool_1: Balance) { @@ -73,6 +75,8 @@ pub fn set_pool(trading_pair: &TradingPair, pool_0: Balance, pool_1: Balance) { AusdDotPool::mutate(|v| *v = (pool_0, pool_1)); } else if *trading_pair == ACADOTPair::get() { AcaDotPool::mutate(|v| *v = (pool_0, pool_1)); + } else if *trading_pair == ACAAUSDPair::get() { + AcaAusdPool::mutate(|v| *v = (pool_0, pool_1)); } } @@ -85,6 +89,8 @@ impl DEXManager for MockDEX { AusdDotPool::get() } else if trading_pair == ACADOTPair::get() { AcaDotPool::get() + } else if trading_pair == ACAAUSDPair::get() { + AcaAusdPool::get() } else { (0, 0) } @@ -146,11 +152,51 @@ ord_parameter_types! { pub const One: AccountId = 1; } +#[cfg(feature = "runtime-benchmarks")] +pub struct MockBenchmarkHelper; +#[cfg(feature = "runtime-benchmarks")] +impl BenchmarkHelper for MockBenchmarkHelper { + fn setup_on_initialize(n: u32, u: u32) { + let trading_pair_list = vec![AUSDDOTPair::get(), ACADOTPair::get(), ACAAUSDPair::get()]; + + for i in 0..n { + let trading_pair = trading_pair_list[i as usize]; + set_pool(&trading_pair, 100, 1000); + DexOracle::enable_average_price( + RuntimeOrigin::signed(1), + trading_pair.first(), + trading_pair.second(), + 240000, + ) + .unwrap(); + } + for j in 0..u.min(n) { + let update_pair = trading_pair_list[j as usize]; + DexOracle::update_average_price_interval( + RuntimeOrigin::signed(1), + update_pair.first(), + update_pair.second(), + 24000, + ) + .unwrap(); + } + frame_system::Pallet::::set_block_number(1); + pallet_timestamp::Pallet::::set_timestamp(24000); + } + + fn setup_inject_liquidity() -> Option<(CurrencyId, CurrencyId, Moment)> { + set_pool(&AUSDDOTPair::get(), 1_000, 100); + Some((AUSD, DOT, 1000)) + } +} + impl Config for Runtime { type DEX = MockDEX; type Time = Timestamp; type UpdateOrigin = EnsureSignedBy; type WeightInfo = (); + #[cfg(feature = "runtime-benchmarks")] + type BenchmarkHelper = MockBenchmarkHelper; } type Block = frame_system::mocking::MockBlock; diff --git a/modules/dex/Cargo.toml b/modules/dex/Cargo.toml index b1f5c9a8c..5e35acecc 100644 --- a/modules/dex/Cargo.toml +++ b/modules/dex/Cargo.toml @@ -10,6 +10,7 @@ parity-scale-codec = { workspace = true, features = ["max-encoded-len"] } scale-info = { workspace = true } sp-core = { workspace = true } sp-runtime = { workspace = true } +frame-benchmarking = { workspace = true, optional = true } frame-support = { workspace = true } frame-system = { workspace = true } sp-std = { workspace = true } @@ -27,6 +28,7 @@ pallet-balances = { workspace = true, features = ["std"] } default = ["std"] std = [ "parity-scale-codec/std", + "frame-benchmarking?/std", "frame-support/std", "frame-system/std", "orml-traits/std", @@ -40,3 +42,9 @@ try-runtime = [ "frame-support/try-runtime", "frame-system/try-runtime", ] +runtime-benchmarks = [ + "frame-benchmarking/runtime-benchmarks", + "frame-support/runtime-benchmarks", + "frame-system/runtime-benchmarks", + "sp-runtime/runtime-benchmarks", +] diff --git a/modules/dex/src/benchmarking.rs b/modules/dex/src/benchmarking.rs new file mode 100644 index 000000000..55f93bc0d --- /dev/null +++ b/modules/dex/src/benchmarking.rs @@ -0,0 +1,744 @@ +// This file is part of Acala. + +// Copyright (C) 2020-2025 Acala Foundation. +// SPDX-License-Identifier: GPL-3.0-or-later WITH Classpath-exception-2.0 + +// This program 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. + +// This program 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 this program. If not, see . + +extern crate alloc; + +use super::*; +use crate::Pallet; +use frame_benchmarking::v2::*; +use frame_support::assert_ok; +use frame_system::RawOrigin; + +/// Helper trait for benchmarking. +pub trait BenchmarkHelper { + fn setup_stable_currency_id() -> Option; + fn setup_currency_lists() -> Vec; +} + +impl BenchmarkHelper for () { + fn setup_stable_currency_id() -> Option { + None + } + fn setup_currency_lists() -> Vec { + vec![] + } +} + +fn dollar(_currency_id: CurrencyId) -> Balance { + 10u128.saturating_pow(12) +} + +fn inject_liquidity( + maker: T::AccountId, + currency_id_a: CurrencyId, + currency_id_b: CurrencyId, + max_amount_a: Balance, + max_amount_b: Balance, + deposit: bool, +) { + // set balance + assert_ok!(>::update_balance( + currency_id_a, + &maker, + max_amount_a.saturated_into(), + )); + assert_ok!(>::update_balance( + currency_id_b, + &maker, + max_amount_b.saturated_into(), + )); + + assert_ok!(Pallet::::enable_trading_pair( + RawOrigin::Root.into(), + currency_id_a, + currency_id_b + )); + + assert_ok!(Pallet::::add_liquidity( + RawOrigin::Signed(maker.clone()).into(), + currency_id_a, + currency_id_b, + max_amount_a, + max_amount_b, + Default::default(), + deposit, + )); +} + +#[benchmarks] +mod benchmarks { + use super::*; + + // enable a Disabled trading pair + #[benchmark] + fn enable_trading_pair() { + let stable_currency_id = T::BenchmarkHelper::setup_stable_currency_id().unwrap(); + let trading_pair = TradingPair::from_currency_ids(stable_currency_id, T::GetNativeCurrencyId::get()).unwrap(); + if let TradingPairStatus::Enabled = Pallet::::trading_pair_statuses(trading_pair) { + assert_ok!(Pallet::::disable_trading_pair( + RawOrigin::Root.into(), + trading_pair.first(), + trading_pair.second() + )); + } + + #[extrinsic_call] + _(RawOrigin::Root, trading_pair.first(), trading_pair.second()); + + frame_system::Pallet::::assert_last_event( + Event::::EnableTradingPair { + trading_pair: trading_pair, + } + .into(), + ); + } + + // disable a Enabled trading pair + #[benchmark] + fn disable_trading_pair() { + let stable_currency_id = T::BenchmarkHelper::setup_stable_currency_id().unwrap(); + let trading_pair = TradingPair::from_currency_ids(stable_currency_id, T::GetNativeCurrencyId::get()).unwrap(); + if let TradingPairStatus::Disabled = Pallet::::trading_pair_statuses(trading_pair) { + assert_ok!(Pallet::::enable_trading_pair( + RawOrigin::Root.into(), + trading_pair.first(), + trading_pair.second() + )); + } + + #[extrinsic_call] + _(RawOrigin::Root, trading_pair.first(), trading_pair.second()); + + frame_system::Pallet::::assert_last_event( + Event::::DisableTradingPair { + trading_pair: trading_pair, + } + .into(), + ); + } + + // list a Provisioning trading pair + #[benchmark] + fn list_provisioning() { + let stable_currency_id = T::BenchmarkHelper::setup_stable_currency_id().unwrap(); + let trading_pair = TradingPair::from_currency_ids(stable_currency_id, T::GetNativeCurrencyId::get()).unwrap(); + if let TradingPairStatus::Enabled = Pallet::::trading_pair_statuses(trading_pair) { + assert_ok!(Pallet::::disable_trading_pair( + RawOrigin::Root.into(), + trading_pair.first(), + trading_pair.second() + )); + } + + #[extrinsic_call] + _( + RawOrigin::Root, + trading_pair.first(), + trading_pair.second(), + dollar(trading_pair.first()), + dollar(trading_pair.second()), + dollar(trading_pair.first()), + dollar(trading_pair.second()), + 10u32.into(), + ); + + frame_system::Pallet::::assert_last_event( + Event::::ListProvisioning { + trading_pair: trading_pair, + } + .into(), + ); + } + + // update parameters of a Provisioning trading pair + #[benchmark] + fn update_provisioning_parameters() { + let stable_currency_id = T::BenchmarkHelper::setup_stable_currency_id().unwrap(); + let trading_pair = TradingPair::from_currency_ids(stable_currency_id, T::GetNativeCurrencyId::get()).unwrap(); + if let TradingPairStatus::Enabled = Pallet::::trading_pair_statuses(trading_pair) { + assert_ok!(Pallet::::disable_trading_pair( + RawOrigin::Root.into(), + trading_pair.first(), + trading_pair.second() + )); + } + + assert_ok!(Pallet::::list_provisioning( + RawOrigin::Root.into(), + trading_pair.first(), + trading_pair.second(), + dollar(trading_pair.first()), + dollar(trading_pair.second()), + 100 * dollar(trading_pair.first()), + 1000 * dollar(trading_pair.second()), + 100u32.into(), + )); + + #[extrinsic_call] + _( + RawOrigin::Root, + trading_pair.first(), + trading_pair.second(), + 2 * dollar(trading_pair.first()), + 2 * dollar(trading_pair.second()), + 10 * dollar(trading_pair.first()), + 100 * dollar(trading_pair.second()), + 200u32.into(), + ); + } + + // end a Provisioning trading pair + #[benchmark] + fn end_provisioning() { + let founder: T::AccountId = whitelisted_caller(); + let stable_currency_id = T::BenchmarkHelper::setup_stable_currency_id().unwrap(); + let trading_pair = TradingPair::from_currency_ids(stable_currency_id, T::GetNativeCurrencyId::get()).unwrap(); + if let TradingPairStatus::Enabled = Pallet::::trading_pair_statuses(trading_pair) { + assert_ok!(Pallet::::disable_trading_pair( + RawOrigin::Root.into(), + trading_pair.first(), + trading_pair.second() + )); + } + + assert_ok!(Pallet::::list_provisioning( + RawOrigin::Root.into(), + trading_pair.first(), + trading_pair.second(), + dollar(trading_pair.first()), + dollar(trading_pair.second()), + 100 * dollar(trading_pair.first()), + 100 * dollar(trading_pair.second()), + 0u32.into(), + )); + + // set balance + assert_ok!(>::update_balance( + trading_pair.first(), + &founder, + (100 * dollar(trading_pair.first())).saturated_into(), + )); + assert_ok!(>::update_balance( + trading_pair.second(), + &founder, + (100 * dollar(trading_pair.second())).saturated_into(), + )); + + // add enough provision + assert_ok!(Pallet::::add_provision( + RawOrigin::Signed(founder.clone()).into(), + trading_pair.first(), + trading_pair.second(), + 100 * dollar(trading_pair.first()), + 100 * dollar(trading_pair.second()), + )); + + #[extrinsic_call] + _(RawOrigin::Signed(founder), trading_pair.first(), trading_pair.second()); + + frame_system::Pallet::::assert_last_event( + Event::::ProvisioningToEnabled { + trading_pair: trading_pair, + pool_0: 100 * dollar(trading_pair.first()), + pool_1: 100 * dollar(trading_pair.second()), + share_amount: 200 * dollar(trading_pair.first()), + } + .into(), + ); + } + + #[benchmark] + fn add_provision() { + let founder: T::AccountId = whitelisted_caller(); + let stable_currency_id = T::BenchmarkHelper::setup_stable_currency_id().unwrap(); + let trading_pair = TradingPair::from_currency_ids(stable_currency_id, T::GetNativeCurrencyId::get()).unwrap(); + if let TradingPairStatus::Enabled = Pallet::::trading_pair_statuses(trading_pair) { + assert_ok!(Pallet::::disable_trading_pair( + RawOrigin::Root.into(), + trading_pair.first(), + trading_pair.second() + )); + } + assert_ok!(Pallet::::list_provisioning( + RawOrigin::Root.into(), + trading_pair.first(), + trading_pair.second(), + dollar(trading_pair.first()), + dollar(trading_pair.second()), + 100 * dollar(trading_pair.first()), + 1000 * dollar(trading_pair.second()), + 0u32.into(), + )); + + // set balance + assert_ok!(>::update_balance( + trading_pair.first(), + &founder, + (10 * dollar(trading_pair.first())).saturated_into(), + )); + assert_ok!(>::update_balance( + trading_pair.second(), + &founder, + (10 * dollar(trading_pair.second())).saturated_into(), + )); + + #[extrinsic_call] + _( + RawOrigin::Signed(founder.clone()), + trading_pair.first(), + trading_pair.second(), + dollar(trading_pair.first()), + dollar(trading_pair.second()), + ); + + frame_system::Pallet::::assert_last_event( + Event::::AddProvision { + who: founder, + currency_0: trading_pair.first(), + contribution_0: dollar(trading_pair.first()), + currency_1: trading_pair.second(), + contribution_1: dollar(trading_pair.second()), + } + .into(), + ); + } + + #[benchmark] + fn claim_dex_share() { + let founder: T::AccountId = whitelisted_caller(); + let stable_currency_id = T::BenchmarkHelper::setup_stable_currency_id().unwrap(); + let trading_pair = TradingPair::from_currency_ids(stable_currency_id, T::GetNativeCurrencyId::get()).unwrap(); + if let TradingPairStatus::Enabled = Pallet::::trading_pair_statuses(trading_pair) { + assert_ok!(Pallet::::disable_trading_pair( + RawOrigin::Root.into(), + trading_pair.first(), + trading_pair.second() + )); + } + assert_ok!(Pallet::::list_provisioning( + RawOrigin::Root.into(), + trading_pair.first(), + trading_pair.second(), + dollar(trading_pair.first()), + dollar(trading_pair.second()), + 10 * dollar(trading_pair.first()), + 10 * dollar(trading_pair.second()), + 0u32.into(), + )); + + // set balance + assert_ok!(>::update_balance( + trading_pair.first(), + &founder, + (100 * dollar(trading_pair.first())).saturated_into() + )); + assert_ok!(>::update_balance( + trading_pair.second(), + &founder, + (100 * dollar(trading_pair.second())).saturated_into() + )); + + assert_ok!(Pallet::::add_provision( + RawOrigin::Signed(founder.clone()).into(), + trading_pair.first(), + trading_pair.second(), + dollar(trading_pair.first()), + 20 * dollar(trading_pair.second()) + )); + assert_ok!(Pallet::::end_provisioning( + RawOrigin::Signed(founder.clone()).into(), + trading_pair.first(), + trading_pair.second(), + )); + + #[extrinsic_call] + _( + RawOrigin::Signed(whitelisted_caller()), + founder.clone(), + trading_pair.first(), + trading_pair.second(), + ); + + assert_eq!( + T::Currency::free_balance(trading_pair.dex_share_currency_id(), &founder), + 2_000_000_000_000 + ); + } + + // add liquidity but don't staking lp + #[benchmark] + fn add_liquidity() { + let first_maker: T::AccountId = account("first_maker", 0, 0); + let second_maker: T::AccountId = whitelisted_caller(); + let stable_currency_id = T::BenchmarkHelper::setup_stable_currency_id().unwrap(); + let trading_pair = TradingPair::from_currency_ids(stable_currency_id, T::GetNativeCurrencyId::get()).unwrap(); + let amount_a = 100 * dollar(trading_pair.first()); + let amount_b = 10_000 * dollar(trading_pair.second()); + + // set balance + assert_ok!(>::update_balance( + trading_pair.first(), + &second_maker, + amount_a.saturated_into() + )); + assert_ok!(>::update_balance( + trading_pair.second(), + &second_maker, + amount_b.saturated_into() + )); + + // first maker inject liquidity + inject_liquidity::( + first_maker.clone(), + trading_pair.first(), + trading_pair.second(), + amount_a, + amount_b, + false, + ); + + #[extrinsic_call] + _( + RawOrigin::Signed(second_maker), + trading_pair.first(), + trading_pair.second(), + amount_a, + amount_b, + Default::default(), + false, + ); + } + + // worst: add liquidity and stake lp + #[benchmark] + fn add_liquidity_and_stake() { + let first_maker: T::AccountId = account("first_maker", 0, 0); + let second_maker: T::AccountId = whitelisted_caller(); + let stable_currency_id = T::BenchmarkHelper::setup_stable_currency_id().unwrap(); + let trading_pair = TradingPair::from_currency_ids(stable_currency_id, T::GetNativeCurrencyId::get()).unwrap(); + let amount_a = 100 * dollar(trading_pair.first()); + let amount_b = 10_000 * dollar(trading_pair.second()); + + // set balance + assert_ok!(>::update_balance( + trading_pair.first(), + &second_maker, + amount_a.saturated_into() + )); + assert_ok!(>::update_balance( + trading_pair.second(), + &second_maker, + amount_b.saturated_into() + )); + + // first maker inject liquidity + inject_liquidity::( + first_maker.clone(), + trading_pair.first(), + trading_pair.second(), + amount_a, + amount_b, + true, + ); + + #[extrinsic_call] + add_liquidity( + RawOrigin::Signed(second_maker), + trading_pair.first(), + trading_pair.second(), + amount_a, + amount_b, + Default::default(), + true, + ); + } + + // remove liquidity by liquid lp share + #[benchmark] + fn remove_liquidity() { + let maker: T::AccountId = whitelisted_caller(); + let stable_currency_id = T::BenchmarkHelper::setup_stable_currency_id().unwrap(); + let trading_pair = TradingPair::from_currency_ids(stable_currency_id, T::GetNativeCurrencyId::get()).unwrap(); + + inject_liquidity::( + maker.clone(), + trading_pair.first(), + trading_pair.second(), + 100 * dollar(trading_pair.first()), + 10_000 * dollar(trading_pair.second()), + false, + ); + + #[extrinsic_call] + remove_liquidity( + RawOrigin::Signed(maker), + trading_pair.first(), + trading_pair.second(), + 50 * dollar(trading_pair.first()), + Default::default(), + Default::default(), + false, + ); + } + + // remove liquidity by withdraw staking lp share + #[benchmark] + fn remove_liquidity_by_unstake() { + let maker: T::AccountId = whitelisted_caller(); + let stable_currency_id = T::BenchmarkHelper::setup_stable_currency_id().unwrap(); + let trading_pair = TradingPair::from_currency_ids(stable_currency_id, T::GetNativeCurrencyId::get()).unwrap(); + + inject_liquidity::( + maker.clone(), + trading_pair.first(), + trading_pair.second(), + 100 * dollar(trading_pair.first()), + 10_000 * dollar(trading_pair.second()), + true, + ); + + #[extrinsic_call] + remove_liquidity( + RawOrigin::Signed(maker), + trading_pair.first(), + trading_pair.second(), + 50 * dollar(trading_pair.first()), + Default::default(), + Default::default(), + true, + ); + } + + #[benchmark] + fn swap_with_exact_supply(u: Linear<2, { T::TradingPathLimit::get() }>) { + let maker: T::AccountId = account("maker", 0, 0); + let taker: T::AccountId = whitelisted_caller(); + + let currency_lists = T::BenchmarkHelper::setup_currency_lists(); + + let mut path: Vec = vec![]; + for i in 1..u { + if i == 1 { + let cur0 = currency_lists[0]; + let cur1 = currency_lists[1]; + path.push(cur0); + path.push(cur1); + inject_liquidity::( + maker.clone(), + cur0, + cur1, + 10_000 * dollar(cur0), + 10_000 * dollar(cur1), + false, + ); + } else { + path.push(currency_lists[i as usize]); + inject_liquidity::( + maker.clone(), + currency_lists[i as usize - 1], + currency_lists[i as usize], + 10_000 * dollar(currency_lists[i as usize - 1]), + 10_000 * dollar(currency_lists[i as usize]), + false, + ); + } + } + + assert_ok!(>::update_balance( + path[0], + &taker, + (10_000 * dollar(path[0])).saturated_into() + )); + + #[extrinsic_call] + swap_with_exact_supply(RawOrigin::Signed(taker.clone()), path.clone(), 100 * dollar(path[0]), 0); + + // would panic the benchmark anyways, must add new currencies to currency_lists for benchmarking to + // work + assert!(T::TradingPathLimit::get() < currency_lists.len() as u32); + } + + #[benchmark] + fn swap_with_exact_target(u: Linear<2, { T::TradingPathLimit::get() }>) { + let maker: T::AccountId = account("maker", 0, 0); + let taker: T::AccountId = whitelisted_caller(); + + let currency_lists = T::BenchmarkHelper::setup_currency_lists(); + + let mut path: Vec = vec![]; + for i in 1..u { + if i == 1 { + let cur0 = currency_lists[0]; + let cur1 = currency_lists[1]; + path.push(cur0); + path.push(cur1); + inject_liquidity::( + maker.clone(), + cur0, + cur1, + 10_000 * dollar(cur0), + 10_000 * dollar(cur1), + false, + ); + } else { + path.push(currency_lists[i as usize]); + inject_liquidity::( + maker.clone(), + currency_lists[i as usize - 1], + currency_lists[i as usize], + 10_000 * dollar(currency_lists[i as usize - 1]), + 10_000 * dollar(currency_lists[i as usize]), + false, + ); + } + } + + assert_ok!(>::update_balance( + path[0], + &taker, + (10_000 * dollar(path[0])).saturated_into() + )); + + #[extrinsic_call] + swap_with_exact_target( + RawOrigin::Signed(taker.clone()), + path.clone(), + 10 * dollar(path[path.len() - 1]), + 100 * dollar(path[0]), + ); + + // would panic the benchmark anyways, must add new currencies to currency_lists for benchmarking to + // work + assert!(T::TradingPathLimit::get() < currency_lists.len() as u32); + } + + #[benchmark] + fn refund_provision() { + let founder: T::AccountId = whitelisted_caller(); + let stable_currency_id = T::BenchmarkHelper::setup_stable_currency_id().unwrap(); + let trading_pair = TradingPair::from_currency_ids(stable_currency_id, T::GetNativeCurrencyId::get()).unwrap(); + if let TradingPairStatus::Enabled = Pallet::::trading_pair_statuses(trading_pair) { + assert_ok!(Pallet::::disable_trading_pair( + RawOrigin::Root.into(), + trading_pair.first(), + trading_pair.second() + )); + } + assert_ok!(Pallet::::list_provisioning( + RawOrigin::Root.into(), + trading_pair.first(), + trading_pair.second(), + dollar(trading_pair.first()), + dollar(trading_pair.second()), + 10 * dollar(trading_pair.first()), + 10 * dollar(trading_pair.second()), + 0u32.into(), + )); + + // set balance + assert_ok!(>::update_balance( + trading_pair.first(), + &founder, + (100 * dollar(trading_pair.first())).saturated_into(), + )); + assert_ok!(>::update_balance( + trading_pair.second(), + &founder, + (100 * dollar(trading_pair.second())).saturated_into(), + )); + + assert_ok!(Pallet::::add_provision( + RawOrigin::Signed(founder.clone()).into(), + trading_pair.first(), + trading_pair.second(), + dollar(trading_pair.first()), + dollar(trading_pair.second()) + )); + + frame_system::Pallet::::set_block_number(T::ExtendedProvisioningBlocks::get() + 1u32.into()); + assert_ok!(Pallet::::abort_provisioning( + RawOrigin::Signed(founder.clone()).into(), + trading_pair.first(), + trading_pair.second(), + )); + + #[extrinsic_call] + _( + RawOrigin::Signed(founder.clone()), + founder.clone(), + trading_pair.first(), + trading_pair.second(), + ); + } + + #[benchmark] + fn abort_provisioning() { + let founder: T::AccountId = whitelisted_caller(); + let stable_currency_id = T::BenchmarkHelper::setup_stable_currency_id().unwrap(); + let trading_pair = TradingPair::from_currency_ids(stable_currency_id, T::GetNativeCurrencyId::get()).unwrap(); + if let TradingPairStatus::Enabled = Pallet::::trading_pair_statuses(trading_pair) { + assert_ok!(Pallet::::disable_trading_pair( + RawOrigin::Root.into(), + trading_pair.first(), + trading_pair.second() + )); + } + + assert_ok!(Pallet::::list_provisioning( + RawOrigin::Root.into(), + trading_pair.first(), + trading_pair.second(), + dollar(trading_pair.first()), + dollar(trading_pair.second()), + 100 * dollar(trading_pair.first()), + 100 * dollar(trading_pair.second()), + 0u32.into(), + )); + + // set balance + assert_ok!(>::update_balance( + trading_pair.first(), + &founder, + (100 * dollar(trading_pair.first())).saturated_into() + )); + assert_ok!(>::update_balance( + trading_pair.second(), + &founder, + (100 * dollar(trading_pair.second())).saturated_into() + )); + + assert_ok!(Pallet::::add_provision( + RawOrigin::Signed(founder.clone()).into(), + trading_pair.first(), + trading_pair.second(), + 10 * dollar(trading_pair.first()), + 10 * dollar(trading_pair.second()), + )); + + frame_system::Pallet::::set_block_number(T::ExtendedProvisioningBlocks::get() + 1u32.into()); + + #[extrinsic_call] + _( + RawOrigin::Signed(whitelisted_caller()), + trading_pair.first(), + trading_pair.second(), + ); + } + + impl_benchmark_test_suite!(Pallet, crate::mock::ExtBuilder::default().build(), crate::mock::Runtime); +} diff --git a/modules/dex/src/lib.rs b/modules/dex/src/lib.rs index 15dd6c91f..962be6c70 100644 --- a/modules/dex/src/lib.rs +++ b/modules/dex/src/lib.rs @@ -47,10 +47,14 @@ use sp_runtime::{ }; use sp_std::{prelude::*, vec}; +#[cfg(feature = "runtime-benchmarks")] +mod benchmarking; mod mock; mod tests; pub mod weights; +#[cfg(feature = "runtime-benchmarks")] +pub use benchmarking::BenchmarkHelper; pub use module::*; pub use weights::WeightInfo; @@ -135,6 +139,9 @@ pub mod module { /// Event handler which calls when update liquidity pool. type OnLiquidityPoolUpdated: Happened<(TradingPair, Balance, Balance)>; + + #[cfg(feature = "runtime-benchmarks")] + type BenchmarkHelper: BenchmarkHelper; } #[pallet::error] @@ -516,7 +523,7 @@ pub mod module { #[pallet::compact] target_provision_b: Balance, #[pallet::compact] not_before: BlockNumberFor, ) -> DispatchResult { - T::ListingOrigin::ensure_origin(origin)?; + T::ListingOrigin::ensure_origin_or_root(origin)?; let trading_pair = TradingPair::from_currency_ids(currency_id_a, currency_id_b).ok_or(Error::::InvalidCurrencyId)?; @@ -583,7 +590,7 @@ pub mod module { #[pallet::compact] target_provision_b: Balance, #[pallet::compact] not_before: BlockNumberFor, ) -> DispatchResult { - T::ListingOrigin::ensure_origin(origin)?; + T::ListingOrigin::ensure_origin_or_root(origin)?; let trading_pair = TradingPair::from_currency_ids(currency_id_a, currency_id_b).ok_or(Error::::InvalidCurrencyId)?; @@ -703,7 +710,7 @@ pub mod module { currency_id_a: CurrencyId, currency_id_b: CurrencyId, ) -> DispatchResult { - T::ListingOrigin::ensure_origin(origin)?; + T::ListingOrigin::ensure_origin_or_root(origin)?; let trading_pair = TradingPair::from_currency_ids(currency_id_a, currency_id_b).ok_or(Error::::InvalidCurrencyId)?; match Self::trading_pair_statuses(trading_pair) { @@ -731,7 +738,7 @@ pub mod module { currency_id_a: CurrencyId, currency_id_b: CurrencyId, ) -> DispatchResult { - T::ListingOrigin::ensure_origin(origin)?; + T::ListingOrigin::ensure_origin_or_root(origin)?; let trading_pair = TradingPair::from_currency_ids(currency_id_a, currency_id_b).ok_or(Error::::InvalidCurrencyId)?; ensure!( diff --git a/modules/dex/src/mock.rs b/modules/dex/src/mock.rs index 8559b51b8..0779bb886 100644 --- a/modules/dex/src/mock.rs +++ b/modules/dex/src/mock.rs @@ -81,6 +81,8 @@ impl orml_tokens::Config for Runtime { type MaxReserves = (); type ReserveIdentifier = [u8; 8]; type DustRemovalWhitelist = Nothing; + #[cfg(feature = "runtime-benchmarks")] + type BenchmarkHelper = (); } pub struct MockDEXIncentives; @@ -122,6 +124,18 @@ impl Happened<(TradingPair, Balance, Balance)> for MockOnLiquidityPoolUpdated { } } +#[cfg(feature = "runtime-benchmarks")] +pub struct MockBenchmarkHelper; +#[cfg(feature = "runtime-benchmarks")] +impl BenchmarkHelper for MockBenchmarkHelper { + fn setup_stable_currency_id() -> Option { + Some(AUSD) + } + fn setup_currency_lists() -> Vec { + vec![ACA, AUSD, DOT, BTC] + } +} + impl Config for Runtime { type Currency = Tokens; type GetExchangeFee = GetExchangeFee; @@ -134,6 +148,8 @@ impl Config for Runtime { type ListingOrigin = EnsureSignedBy; type ExtendedProvisioningBlocks = ConstU64<2000>; type OnLiquidityPoolUpdated = MockOnLiquidityPoolUpdated; + #[cfg(feature = "runtime-benchmarks")] + type BenchmarkHelper = MockBenchmarkHelper; } parameter_types! { diff --git a/modules/earning/Cargo.toml b/modules/earning/Cargo.toml index 13aae7569..572bfafd7 100644 --- a/modules/earning/Cargo.toml +++ b/modules/earning/Cargo.toml @@ -7,6 +7,7 @@ edition = { workspace = true } [dependencies] parity-scale-codec = { workspace = true } scale-info = { workspace = true } +frame-benchmarking = { workspace = true, optional = true } frame-support = { workspace = true } frame-system = { workspace = true } sp-core = { workspace = true } @@ -25,6 +26,7 @@ pallet-balances = { workspace = true, features = ["std"] } default = ["std"] std = [ "parity-scale-codec/std", + "frame-benchmarking?/std", "frame-support/std", "frame-system/std", "orml-traits/std", @@ -35,6 +37,11 @@ std = [ "sp-std/std", "module-support/std", ] +runtime-benchmarks = [ + "frame-benchmarking/runtime-benchmarks", + "frame-support/runtime-benchmarks", + "frame-system/runtime-benchmarks", +] try-runtime = [ "frame-support/try-runtime", "frame-system/try-runtime", diff --git a/modules/earning/src/benchmarking.rs b/modules/earning/src/benchmarking.rs new file mode 100644 index 000000000..083d061bf --- /dev/null +++ b/modules/earning/src/benchmarking.rs @@ -0,0 +1,172 @@ +// This file is part of Acala. + +// Copyright (C) 2020-2025 Acala Foundation. +// SPDX-License-Identifier: GPL-3.0-or-later WITH Classpath-exception-2.0 + +// This program 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. + +// This program 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 this program. If not, see . + +use super::*; +use frame_benchmarking::v2::*; +use frame_support::assert_ok; +use frame_system::RawOrigin; + +/// Helper trait for benchmarking. +pub trait BenchmarkHelper { + fn setup_parameter_store(); +} + +impl BenchmarkHelper for () { + fn setup_parameter_store() {} +} + +fn make_max_unbonding_chunk(who: T::AccountId, amount: Balance) +where + T: Config + frame_system::Config, +{ + frame_system::Pallet::::set_block_number(0u32.into()); + let max_unlock_chunk: u32 = T::MaxUnbondingChunks::get(); + + assert_ok!(Pallet::::bond(RawOrigin::Signed(who.clone()).into(), 100 * amount)); + + for _ in 0..(max_unlock_chunk) { + frame_system::Pallet::::set_block_number(frame_system::Pallet::::block_number() + 1u32.into()); + assert_ok!(Pallet::::unbond(RawOrigin::Signed(who.clone()).into(), amount)); + } +} + +#[benchmarks] +mod benchmarks { + use super::*; + + #[benchmark] + fn bond() { + T::BenchmarkHelper::setup_parameter_store(); + + let caller: T::AccountId = account("caller", 0, 0); + let amount = T::MinBond::get(); + + let _ = T::Currency::make_free_balance_be(&caller, amount); + + #[extrinsic_call] + _(RawOrigin::Signed(caller.clone()), amount); + + frame_system::Pallet::::assert_last_event( + Event::Bonded { + who: caller.clone(), + amount, + } + .into(), + ); + } + + #[benchmark] + fn unbond() { + T::BenchmarkHelper::setup_parameter_store(); + + let caller: T::AccountId = account("caller", 0, 0); + let amount = T::MinBond::get(); + + let _ = T::Currency::make_free_balance_be(&caller, amount); + + assert_ok!(Pallet::::bond(RawOrigin::Signed(caller.clone()).into(), amount)); + + #[extrinsic_call] + _(RawOrigin::Signed(caller.clone()), amount); + + frame_system::Pallet::::assert_last_event( + Event::Unbonded { + who: caller.clone(), + amount, + } + .into(), + ); + } + + #[benchmark] + fn unbond_instant() { + T::BenchmarkHelper::setup_parameter_store(); + + let caller: T::AccountId = account("caller", 0, 0); + let amount = T::MinBond::get(); + + let _ = T::Currency::make_free_balance_be(&caller, amount); + + assert_ok!(Pallet::::bond(RawOrigin::Signed(caller.clone()).into(), amount)); + + #[extrinsic_call] + _(RawOrigin::Signed(caller.clone()), amount); + + let fee = Pallet::::get_instant_unstake_fee().unwrap().mul_ceil(amount); + + frame_system::Pallet::::assert_last_event( + Event::InstantUnbonded { + who: caller, + amount: amount - fee, + fee, + } + .into(), + ); + } + + #[benchmark] + fn rebond() { + T::BenchmarkHelper::setup_parameter_store(); + + let caller: T::AccountId = account("caller", 0, 0); + let amount = T::MinBond::get(); + + let _ = T::Currency::make_free_balance_be(&caller, 100 * amount); + + make_max_unbonding_chunk::(caller.clone(), amount); + + #[extrinsic_call] + _(RawOrigin::Signed(caller.clone()), 10 * amount); + + frame_system::Pallet::::assert_last_event( + Event::Rebonded { + who: caller.clone(), + amount: amount.saturating_mul(T::MaxUnbondingChunks::get().into()), + } + .into(), + ); + } + + #[benchmark] + fn withdraw_unbonded() { + T::BenchmarkHelper::setup_parameter_store(); + + let caller: T::AccountId = account("caller", 0, 0); + let amount = T::MinBond::get(); + + let _ = T::Currency::make_free_balance_be(&caller, 100 * amount); + + make_max_unbonding_chunk::(caller.clone(), amount); + + // large number to unlock all chunks + frame_system::Pallet::::set_block_number(1_000_000u32.into()); + + #[extrinsic_call] + _(RawOrigin::Signed(caller.clone())); + + frame_system::Pallet::::assert_last_event( + Event::Withdrawn { + who: caller, + amount: amount.saturating_mul(T::MaxUnbondingChunks::get().into()), + } + .into(), + ); + } + + impl_benchmark_test_suite!(Pallet, crate::mock::ExtBuilder::default().build(), crate::mock::Runtime); +} diff --git a/modules/earning/src/lib.rs b/modules/earning/src/lib.rs index b3fad6ab7..a99dbfa0c 100644 --- a/modules/earning/src/lib.rs +++ b/modules/earning/src/lib.rs @@ -37,12 +37,15 @@ use sp_runtime::{ DispatchError, Permill, }; -pub use module::*; - +#[cfg(feature = "runtime-benchmarks")] +mod benchmarking; mod mock; mod tests; pub mod weights; +#[cfg(feature = "runtime-benchmarks")] +pub use benchmarking::BenchmarkHelper; +pub use module::*; pub use weights::WeightInfo; define_parameters! { @@ -76,6 +79,9 @@ pub mod module { /// Weight information for the extrinsics in this module. type WeightInfo: WeightInfo; + + #[cfg(feature = "runtime-benchmarks")] + type BenchmarkHelper: BenchmarkHelper; } pub type BondingLedgerOf = bonding::BondingLedgerOf>; diff --git a/modules/earning/src/mock.rs b/modules/earning/src/mock.rs index e802061f3..97d52d065 100644 --- a/modules/earning/src/mock.rs +++ b/modules/earning/src/mock.rs @@ -104,6 +104,8 @@ impl Config for Runtime { type MaxUnbondingChunks = ConstU32<3>; type LockIdentifier = EarningLockIdentifier; type WeightInfo = (); + #[cfg(feature = "runtime-benchmarks")] + type BenchmarkHelper = (); } type Block = frame_system::mocking::MockBlock; diff --git a/modules/emergency-shutdown/Cargo.toml b/modules/emergency-shutdown/Cargo.toml index 8823f8b3b..b7324b014 100644 --- a/modules/emergency-shutdown/Cargo.toml +++ b/modules/emergency-shutdown/Cargo.toml @@ -7,6 +7,7 @@ edition = { workspace = true } [dependencies] parity-scale-codec = { workspace = true } scale-info = { workspace = true } +frame-benchmarking = { workspace = true, optional = true } frame-support = { workspace = true } frame-system = { workspace = true } sp-runtime = { workspace = true } @@ -28,6 +29,7 @@ module-cdp-treasury = { workspace = true, features = ["std"] } default = ["std"] std = [ "parity-scale-codec/std", + "frame-benchmarking?/std", "frame-support/std", "frame-system/std", "module-loans/std", @@ -37,6 +39,12 @@ std = [ "sp-std/std", "module-support/std", ] +runtime-benchmarks = [ + "frame-benchmarking/runtime-benchmarks", + "frame-support/runtime-benchmarks", + "frame-system/runtime-benchmarks", + "orml-tokens/runtime-benchmarks", +] try-runtime = [ "frame-support/try-runtime", "frame-system/try-runtime", diff --git a/modules/emergency-shutdown/src/benchmarking.rs b/modules/emergency-shutdown/src/benchmarking.rs new file mode 100644 index 000000000..e9509d593 --- /dev/null +++ b/modules/emergency-shutdown/src/benchmarking.rs @@ -0,0 +1,83 @@ +// This file is part of Acala. + +// Copyright (C) 2020-2025 Acala Foundation. +// SPDX-License-Identifier: GPL-3.0-or-later WITH Classpath-exception-2.0 + +// This program 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. + +// This program 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 this program. If not, see . + +use super::*; +use frame_benchmarking::v2::*; +use frame_support::assert_ok; +use frame_system::RawOrigin; + +/// Helper trait for benchmarking. +pub trait BenchmarkHelper { + fn setup_feed_price(c: u32); + fn setup_collateral_currency_ids() -> Vec; +} + +impl BenchmarkHelper for () { + fn setup_feed_price(_c: u32) {} + fn setup_collateral_currency_ids() -> Vec { + vec![] + } +} + +#[benchmarks] +mod benchmarks { + use super::*; + + #[benchmark] + fn emergency_shutdown(c: Linear<0, { T::BenchmarkHelper::setup_collateral_currency_ids().len() as u32 }>) { + T::BenchmarkHelper::setup_feed_price(c); + + #[extrinsic_call] + _(RawOrigin::Root); + } + + #[benchmark] + fn open_collateral_refund() { + assert_ok!(Pallet::::emergency_shutdown(RawOrigin::Root.into())); + + #[extrinsic_call] + _(RawOrigin::Root); + + frame_system::Pallet::::assert_last_event( + Event::OpenRefund { + block_number: >::block_number(), + } + .into(), + ); + } + + #[benchmark] + fn refund_collaterals(c: Linear<0, { T::BenchmarkHelper::setup_collateral_currency_ids().len() as u32 }>) { + T::BenchmarkHelper::setup_feed_price(c); + + let caller: T::AccountId = account("caller", 0, 0); + let funder: T::AccountId = account("funder", 0, 0); + let amount: Balance = 100_000_000_000_000_000_000u128.into(); + + assert_ok!(::CDPTreasury::issue_debit(&caller, amount, true)); + assert_ok!(::CDPTreasury::issue_debit(&funder, amount, true)); + + assert_ok!(Pallet::::emergency_shutdown(RawOrigin::Root.into())); + assert_ok!(Pallet::::open_collateral_refund(RawOrigin::Root.into())); + + #[extrinsic_call] + _(RawOrigin::Signed(caller), amount); + } + + impl_benchmark_test_suite!(Pallet, crate::mock::ExtBuilder::default().build(), crate::mock::Runtime); +} diff --git a/modules/emergency-shutdown/src/lib.rs b/modules/emergency-shutdown/src/lib.rs index 92db5b137..5ca4cce1e 100644 --- a/modules/emergency-shutdown/src/lib.rs +++ b/modules/emergency-shutdown/src/lib.rs @@ -39,10 +39,14 @@ use primitives::{Balance, CurrencyId}; use sp_runtime::{traits::Zero, FixedPointNumber}; use sp_std::prelude::*; +#[cfg(feature = "runtime-benchmarks")] +mod benchmarking; mod mock; mod tests; pub mod weights; +#[cfg(feature = "runtime-benchmarks")] +pub use benchmarking::BenchmarkHelper; pub use module::*; pub use weights::WeightInfo; @@ -71,6 +75,9 @@ pub mod module { /// Weight information for the extrinsics in this module. type WeightInfo: WeightInfo; + + #[cfg(feature = "runtime-benchmarks")] + type BenchmarkHelper: BenchmarkHelper; } #[pallet::error] @@ -130,7 +137,7 @@ pub mod module { #[pallet::call_index(0)] #[pallet::weight((T::WeightInfo::emergency_shutdown(T::CollateralCurrencyIds::get().len() as u32), DispatchClass::Operational))] pub fn emergency_shutdown(origin: OriginFor) -> DispatchResult { - T::ShutdownOrigin::ensure_origin(origin)?; + T::ShutdownOrigin::ensure_origin_or_root(origin)?; ensure!(!Self::is_shutdown(), Error::::AlreadyShutdown); // get all collateral types @@ -155,7 +162,7 @@ pub mod module { #[pallet::call_index(1)] #[pallet::weight((T::WeightInfo::open_collateral_refund(), DispatchClass::Operational))] pub fn open_collateral_refund(origin: OriginFor) -> DispatchResult { - T::ShutdownOrigin::ensure_origin(origin)?; + T::ShutdownOrigin::ensure_origin_or_root(origin)?; ensure!(Self::is_shutdown(), Error::::MustAfterShutdown); // must after shutdown // Ensure all debits of CDPs have been settled, and all collateral auction has diff --git a/modules/emergency-shutdown/src/mock.rs b/modules/emergency-shutdown/src/mock.rs index e5d958ee5..49dced7b1 100644 --- a/modules/emergency-shutdown/src/mock.rs +++ b/modules/emergency-shutdown/src/mock.rs @@ -75,6 +75,8 @@ impl orml_tokens::Config for Runtime { type MaxReserves = (); type ReserveIdentifier = [u8; 8]; type DustRemovalWhitelist = Nothing; + #[cfg(feature = "runtime-benchmarks")] + type BenchmarkHelper = (); } impl pallet_balances::Config for Runtime { @@ -200,6 +202,8 @@ impl module_cdp_treasury::Config for Runtime { type TreasuryAccount = TreasuryAccount; type WeightInfo = (); type StableAsset = MockStableAsset; + #[cfg(feature = "runtime-benchmarks")] + type BenchmarkHelper = (); } ord_parameter_types! { @@ -213,6 +217,8 @@ impl Config for Runtime { type AuctionManagerHandler = MockAuctionManager; type ShutdownOrigin = EnsureSignedBy; type WeightInfo = (); + #[cfg(feature = "runtime-benchmarks")] + type BenchmarkHelper = (); } type Block = frame_system::mocking::MockBlock; diff --git a/modules/evm-accounts/Cargo.toml b/modules/evm-accounts/Cargo.toml index 9320220dc..4a4c83a5d 100644 --- a/modules/evm-accounts/Cargo.toml +++ b/modules/evm-accounts/Cargo.toml @@ -9,6 +9,7 @@ parity-scale-codec = { workspace = true } libsecp256k1 = { workspace = true, features = ["hmac", "static-context"], optional = true } scale-info = { workspace = true } +frame-benchmarking = { workspace = true, optional = true } frame-support = { workspace = true } frame-system = { workspace = true } sp-core = { workspace = true } @@ -37,6 +38,7 @@ std = [ "sp-runtime/std", "sp-io/std", "sp-std/std", + "frame-benchmarking?/std", "frame-support/std", "frame-system/std", "primitives/std", @@ -45,6 +47,7 @@ std = [ ] runtime-benchmarks = [ "libsecp256k1/hmac", + "frame-benchmarking/runtime-benchmarks", "frame-support/runtime-benchmarks", "frame-system/runtime-benchmarks", "sp-runtime/runtime-benchmarks", diff --git a/modules/evm-accounts/src/benchmarking.rs b/modules/evm-accounts/src/benchmarking.rs new file mode 100644 index 000000000..99e82240c --- /dev/null +++ b/modules/evm-accounts/src/benchmarking.rs @@ -0,0 +1,78 @@ +// This file is part of Acala. + +// Copyright (C) 2020-2025 Acala Foundation. +// SPDX-License-Identifier: GPL-3.0-or-later WITH Classpath-exception-2.0 + +// This program 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. + +// This program 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 this program. If not, see . + +use super::*; +use frame_benchmarking::v2::*; +use frame_system::RawOrigin; + +fn alice() -> libsecp256k1::SecretKey { + libsecp256k1::SecretKey::parse(&keccak_256(b"Alice")).unwrap() +} + +type BalanceOf = <::Currency as Currency<::AccountId>>::Balance; + +#[benchmarks( +where + T::AccountId: From<[u8; 32]>, + BalanceOf: From, +)] +mod benchmarks { + use super::*; + + #[benchmark] + fn claim_account() { + let caller: T::AccountId = account("caller", 0, 0); + + let address = Pallet::::eth_address(&alice()); + let mut data = [0u8; 32]; + data[0..4].copy_from_slice(b"evm:"); + data[4..24].copy_from_slice(&address[..]); + let alice_account_id = T::AccountId::from(data); + + let amount: BalanceOf = 1_000_000_000_000_000_000u128.into(); + + let _ = T::Currency::make_free_balance_be(&alice_account_id, amount); + + #[extrinsic_call] + _( + RawOrigin::Signed(caller.clone()), + Pallet::::eth_address(&alice()), + Pallet::::eth_sign(&alice(), &caller), + ); + + frame_system::Pallet::::assert_last_event( + Event::ClaimAccount { + account_id: caller.clone(), + evm_address: Pallet::::eth_address(&alice()), + } + .into(), + ); + + assert_eq!(T::Currency::free_balance(&caller), amount); + } + + #[benchmark] + fn claim_default_account() { + let caller = account("caller", 0, 0); + + #[extrinsic_call] + _(RawOrigin::Signed(caller)); + } + + impl_benchmark_test_suite!(Pallet, crate::mock::ExtBuilder::default().build(), crate::mock::Runtime); +} diff --git a/modules/evm-accounts/src/lib.rs b/modules/evm-accounts/src/lib.rs index 3cb78b7f4..7c1878021 100644 --- a/modules/evm-accounts/src/lib.rs +++ b/modules/evm-accounts/src/lib.rs @@ -49,6 +49,8 @@ use sp_runtime::{ }; use sp_std::{marker::PhantomData, vec::Vec}; +#[cfg(feature = "runtime-benchmarks")] +mod benchmarking; mod mock; mod tests; pub mod weights; diff --git a/modules/evm-accounts/src/mock.rs b/modules/evm-accounts/src/mock.rs index d18bca2f0..a1f95e41a 100644 --- a/modules/evm-accounts/src/mock.rs +++ b/modules/evm-accounts/src/mock.rs @@ -83,6 +83,8 @@ impl orml_tokens::Config for Runtime { type MaxReserves = (); type ReserveIdentifier = [u8; 8]; type DustRemovalWhitelist = Nothing; + #[cfg(feature = "runtime-benchmarks")] + type BenchmarkHelper = (); } parameter_types! { diff --git a/modules/evm/Cargo.toml b/modules/evm/Cargo.toml index 481624e68..81de58e02 100644 --- a/modules/evm/Cargo.toml +++ b/modules/evm/Cargo.toml @@ -39,6 +39,7 @@ wasm-bencher = { workspace = true, optional = true } orml-currencies = { workspace = true, optional = true } orml-tokens = { workspace = true, optional = true } +module-evm-accounts = { workspace = true, optional = true } module-support = { workspace = true } module-evm-utility = { workspace = true } primitives = { workspace = true } @@ -46,10 +47,9 @@ module-idle-scheduler = { workspace = true, optional = true } module-transaction-payment = { workspace = true } module-dex = { workspace = true, optional = true } -xcm-builder = { workspace = true } - # we don't directly depends on this but need frame-benchmarking/std to fix build frame-benchmarking = { workspace = true, optional = true } +libsecp256k1 = { workspace = true, optional = true } [dev-dependencies] insta = { workspace = true } @@ -57,12 +57,14 @@ hex = { workspace = true, features = ["std"] } env_logger = { workspace = true } serde_json = { workspace = true, features = ["std"] } pallet-utility = { workspace = true, features = ["std"] } +module-evm-accounts = { workspace = true, features = ["std"] } [features] default = ["std"] std = [ "serde/std", "parity-scale-codec/std", + "frame-benchmarking?/std", "frame-support/std", "frame-system/std", "module-dex/std", @@ -86,10 +88,16 @@ std = [ "sp-io/std", "sp-runtime/std", "sp-std/std", - "xcm-builder/std", "frame-benchmarking/std", ] with-ethereum-compatibility = [] +runtime-benchmarks = [ + "libsecp256k1/hmac", + "frame-benchmarking/runtime-benchmarks", + "module-evm-accounts/runtime-benchmarks", + "orml-tokens/runtime-benchmarks", + "module-idle-scheduler/runtime-benchmarks", +] try-runtime = [ "frame-support/try-runtime", "frame-system/try-runtime", @@ -108,11 +116,10 @@ wasm-bench = [ "module-dex", "module-idle-scheduler", "orml-currencies", - "orml-tokens", + "orml-tokens/runtime-benchmarks", "pallet-balances", "serde_json", "sp-runtime/runtime-benchmarks", - "xcm-builder/runtime-benchmarks", ] evm-tests = [ "primitives/evm-tests" diff --git a/modules/evm/src/bench/mock.rs b/modules/evm/src/bench/mock.rs index d8e25720f..4dd271ec6 100644 --- a/modules/evm/src/bench/mock.rs +++ b/modules/evm/src/bench/mock.rs @@ -101,6 +101,8 @@ impl orml_tokens::Config for Runtime { type MaxReserves = (); type ReserveIdentifier = ReserveIdentifier; type DustRemovalWhitelist = Nothing; + #[cfg(feature = "wasm-bench")] + type BenchmarkHelper = (); } parameter_types! { diff --git a/modules/evm/src/benchmarking.rs b/modules/evm/src/benchmarking.rs new file mode 100644 index 000000000..22fee6b7d --- /dev/null +++ b/modules/evm/src/benchmarking.rs @@ -0,0 +1,357 @@ +// This file is part of Acala. + +// Copyright (C) 2020-2025 Acala Foundation. +// SPDX-License-Identifier: GPL-3.0-or-later WITH Classpath-exception-2.0 + +// This program 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. + +// This program 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 this program. If not, see . + +use super::*; +use core::str::FromStr; +use frame_benchmarking::v2::*; +use frame_support::assert_ok; +use frame_system::RawOrigin; +use module_support::EVMAccountsManager; +use sp_io::hashing::keccak_256; + +fn contract_addr() -> H160 { + H160::from_str("0x5e0b4bfa0b55932a3587e648c3552a6515ba56b1").unwrap() +} + +fn alice() -> libsecp256k1::SecretKey { + libsecp256k1::SecretKey::parse(&keccak_256(b"Alice")).unwrap() +} + +fn bob() -> libsecp256k1::SecretKey { + libsecp256k1::SecretKey::parse(&keccak_256(b"Bob")).unwrap() +} + +fn deploy_contract(caller: T::AccountId) -> H160 +where + T: Config + frame_system::Config + module_evm_accounts::Config, + T::AccountId: From<[u8; 32]>, +{ + frame_system::Pallet::::set_block_number(1u32.into()); + + let _ = ::Currency::deposit_creating(&caller, 1_000_000_000_000_000); + + assert_ok!(Pallet::::create( + RawOrigin::Signed(caller.clone()).into(), + FACTORY_CONTRACT.to_vec(), + 0, + 1000000000, + 20_000, + vec![], + )); + + frame_system::Pallet::::assert_last_event( + Event::Created { + from: module_evm_accounts::Pallet::::get_evm_address(&caller).unwrap(), + contract: contract_addr(), + logs: vec![], + used_gas: 132225, + used_storage: 10367, + } + .into(), + ); + contract_addr() +} + +pub fn alice_account_id() -> T::AccountId +where + T: Config + module_evm_accounts::Config, + T::AccountId: From<[u8; 32]>, +{ + let address = module_evm_accounts::Pallet::::eth_address(&alice()); + evm_to_account_id::(address) +} + +#[allow(dead_code)] +pub fn bob_account_id() -> T::AccountId +where + T: Config + module_evm_accounts::Config, + T::AccountId: From<[u8; 32]>, +{ + let address = module_evm_accounts::Pallet::::eth_address(&bob()); + evm_to_account_id::(address) +} + +fn evm_to_account_id(address: H160) -> T::AccountId +where + T: Config, + T::AccountId: From<[u8; 32]>, +{ + let mut data = [0u8; 32]; + data[0..4].copy_from_slice(b"evm:"); + data[4..24].copy_from_slice(&address[..]); + T::AccountId::from(Into::<[u8; 32]>::into(data)) +} + +// pragma solidity 0.8.2; +// +// contract Empty { } +const EMPTY_CONTRACT: [u8; 92] = hex_literal::hex!("6080604052348015600f57600080fd5b50603f80601d6000396000f3fe6080604052600080fdfea2646970667358221220e2641e5566296523edeafd776846b0e535aac278dfcf496804a865948b29646064736f6c63430008020033"); + +// pragma solidity 0.8.2; +// +// contract Storage { +// uint256 public number; +// +// function store(uint256 num) public { +// number = num; +// } +// } +const STORAGE_CONTRACT: [u8; 332] = hex_literal::hex!("608060405234801561001057600080fd5b5061012c806100206000396000f3fe6080604052348015600f57600080fd5b506004361060325760003560e01c80636057361d1460375780638381f58a14604f575b600080fd5b604d600480360381019060499190608c565b6069565b005b60556073565b6040516060919060bf565b60405180910390f35b8060008190555050565b60005481565b60008135905060868160e2565b92915050565b600060208284031215609d57600080fd5b600060a9848285016079565b91505092915050565b60b98160d8565b82525050565b600060208201905060d2600083018460b2565b92915050565b6000819050919050565b60e98160d8565b811460f357600080fd5b5056fea2646970667358221220b161a9e6cc3d4aac8bc0fd65e420da7555db59fefe6a1d4e8e7eea98e99b293b64736f6c63430008020033"); + +// pragma solidity ^0.5.0; +// +// contract Factory { +// Contract[] newContracts; +// +// function createContract () public payable { +// Contract newContract = new Contract(); +// newContracts.push(newContract); +// } +// } +// +// contract Contract {} +const FACTORY_CONTRACT: [u8; 399] = hex_literal::hex!("608060405234801561001057600080fd5b5061016f806100206000396000f3fe608060405260043610610041576000357c0100000000000000000000000000000000000000000000000000000000900463ffffffff168063412a5a6d14610046575b600080fd5b61004e610050565b005b600061005a6100e2565b604051809103906000f080158015610076573d6000803e3d6000fd5b50905060008190806001815401808255809150509060018203906000526020600020016000909192909190916101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff1602179055505050565b6040516052806100f28339019056fe6080604052348015600f57600080fd5b50603580601d6000396000f3fe6080604052600080fdfea165627a7a7230582092dc1966a8880ddf11e067f9dd56a632c11a78a4afd4a9f05924d427367958cc0029a165627a7a723058202b2cc7384e11c452cdbf39b68dada2d5e10a632cc0174a354b8b8c83237e28a40029"); + +#[benchmarks( + where T: Config + module_evm_accounts::Config, + T::AccountId: From<[u8; 32]> +)] +mod benchmarks { + use super::*; + + #[benchmark] + fn create() { + let alice_account = alice_account_id::(); + let _ = ::Currency::deposit_creating(&alice_account, 1_000_000_000_000_000); + + #[extrinsic_call] + _( + RawOrigin::Signed(alice_account), + EMPTY_CONTRACT.to_vec(), + 0, + 21_000_000, + 20_000, + vec![], + ); + + // contract address when it gets deployed + let code_hash = Pallet::::code_hash_at_address(&contract_addr()); + assert!(Codes::::contains_key(code_hash)); + } + + #[benchmark] + fn create2() { + let salt = H256::repeat_byte(1); + let alice_account = alice_account_id::(); + let _ = ::Currency::deposit_creating(&alice_account, 1_000_000_000_000_000); + + #[extrinsic_call] + _( + RawOrigin::Signed(alice_account), + EMPTY_CONTRACT.to_vec(), + salt, + 0, + 21_000_000, + 20_000, + vec![], + ); + + // contract address when it gets deployed + let contract_address = H160::from(hex_literal::hex!("f6930000a8679e0c96af73e73c02f163e34b9d70")); + let code_hash = Pallet::::code_hash_at_address(&contract_address); + assert!(Codes::::contains_key(code_hash)); + } + + #[benchmark] + fn create_nft_contract() { + let account_id = ::TreasuryAccount::get(); + let _ = ::Currency::deposit_creating(&account_id, 1_000_000_000_000_000); + let address = primitives::evm::MIRRORED_TOKENS_ADDRESS_START + | H160::from_low_u64_be(Pallet::::network_contract_index()); + + #[extrinsic_call] + _(RawOrigin::Root, EMPTY_CONTRACT.to_vec(), 0, 2_100_000, 15_000, vec![]); + + let code_hash = Pallet::::code_hash_at_address(&address); + assert!(Codes::::contains_key(code_hash)); + } + + #[benchmark] + fn create_predeploy_contract() { + let account_id = ::TreasuryAccount::get(); + let _ = ::Currency::deposit_creating(&account_id, 1_000_000_000_000_000); + let address = H160::from_low_u64_be(1); + + #[extrinsic_call] + _( + RawOrigin::Root, + address, + EMPTY_CONTRACT.to_vec(), + 0, + 2_100_000, + 15_000, + vec![], + ); + + let code_hash = Pallet::::code_hash_at_address(&address); + assert!(Codes::::contains_key(code_hash)); + } + + #[benchmark] + fn call() { + // Storage.store(1) + let input = + hex_literal::hex!("6057361d0000000000000000000000000000000000000000000000000000000000000001").to_vec(); + + let alice_account = alice_account_id::(); + let _ = ::Currency::deposit_creating(&alice_account, 1_000_000_000_000_000); + + // contract address when it gets deployed + let contract_address = H160::from(hex_literal::hex!("5e0b4bfa0b55932a3587e648c3552a6515ba56b1")); + + assert_ok!(Pallet::::create( + RawOrigin::Signed(alice_account.clone()).into(), + STORAGE_CONTRACT.to_vec(), + 0, + 21_000_000, + 20_000, + vec![] + )); + + let code_hash = Pallet::::code_hash_at_address(&contract_address); + assert!(Codes::::contains_key(code_hash)); + + // Storage::number + let hashed_key = AccountStorages::::hashed_key_for(&contract_address, H256::zero()); + frame_benchmarking::benchmarking::add_to_whitelist(hashed_key.into()); + + #[extrinsic_call] + _( + RawOrigin::Signed(alice_account), + contract_address, + input, + 0, + 21_000_000, + 20_000, + vec![], + ); + + assert_eq!( + AccountStorages::::get(&contract_address, H256::zero()), + H256::from_low_u64_be(1) + ); + } + + #[benchmark] + fn transfer_maintainer() { + let alice_account = alice_account_id::(); + + let contract = deploy_contract::(alice_account.clone()); + + let bob_address = module_evm_accounts::Pallet::::eth_address(&bob()); + + #[extrinsic_call] + _(RawOrigin::Signed(alice_account), contract, bob_address); + } + + #[benchmark] + fn publish_contract() { + let alice_account = alice_account_id::(); + let _ = ::Currency::deposit_creating(&alice_account, 1_000_000_000_000_000); + + assert_ok!(Pallet::::enable_contract_development( + RawOrigin::Signed(alice_account.clone()).into() + )); + + let contract = deploy_contract::(alice_account.clone()); + + #[extrinsic_call] + _(RawOrigin::Signed(alice_account), contract); + } + + #[benchmark] + fn publish_free() { + let alice_account = alice_account_id::(); + let _ = ::Currency::deposit_creating(&alice_account, 1_000_000_000_000_000); + + assert_ok!(Pallet::::enable_contract_development( + RawOrigin::Signed(alice_account.clone()).into() + )); + + let contract = deploy_contract::(alice_account.clone()); + + #[extrinsic_call] + _(RawOrigin::Root, contract); + } + + #[benchmark] + fn enable_contract_development() { + let alice_account = alice_account_id::(); + let _ = ::Currency::deposit_creating(&alice_account, 1_000_000_000_000_000); + + #[extrinsic_call] + _(RawOrigin::Signed(alice_account)); + } + + #[benchmark] + fn disable_contract_development() { + let alice_account = alice_account_id::(); + let _ = ::Currency::deposit_creating(&alice_account, 1_000_000_000_000_000); + + assert_ok!(Pallet::::enable_contract_development( + RawOrigin::Signed(alice_account.clone()).into() + )); + + #[extrinsic_call] + _(RawOrigin::Signed(alice_account)); + } + + #[benchmark] + fn set_code(c: Linear<0, { MaxCodeSize::get() }>) { + let alice_account = alice_account_id::(); + let _ = ::Currency::deposit_creating(&alice_account, 1_000_000_000_000_000); + + assert_ok!(Pallet::::enable_contract_development( + RawOrigin::Signed(alice_account.clone()).into() + )); + + let contract = deploy_contract::(alice_account.clone()); + + let new_contract = vec![0; c as usize]; + + #[extrinsic_call] + _(RawOrigin::Signed(alice_account), contract, new_contract); + } + + #[benchmark] + fn selfdestruct() { + let alice_account = alice_account_id::(); + let _ = ::Currency::deposit_creating(&alice_account, 1_000_000_000_000_000); + + assert_ok!(Pallet::::enable_contract_development( + RawOrigin::Signed(alice_account.clone()).into() + )); + + let contract = deploy_contract::(alice_account.clone()); + + #[extrinsic_call] + _(RawOrigin::Signed(alice_account), contract); + } + + impl_benchmark_test_suite!(Pallet, crate::mock::new_test_ext(), crate::mock::Runtime); +} diff --git a/modules/evm/src/lib.rs b/modules/evm/src/lib.rs index 72efecc04..1571f8594 100644 --- a/modules/evm/src/lib.rs +++ b/modules/evm/src/lib.rs @@ -87,6 +87,8 @@ pub mod runner; pub mod bench; +#[cfg(feature = "runtime-benchmarks")] +mod benchmarking; mod mock; mod tests; pub mod weights; @@ -994,7 +996,7 @@ pub mod module { #[pallet::compact] storage_limit: u32, access_list: Vec, ) -> DispatchResultWithPostInfo { - T::NetworkContractOrigin::ensure_origin(origin)?; + T::NetworkContractOrigin::ensure_origin_or_root(origin)?; let source = T::NetworkContractSource::get(); let source_account = T::AddressMapping::get_account_id(&source); @@ -1085,7 +1087,7 @@ pub mod module { #[pallet::compact] storage_limit: u32, access_list: Vec, ) -> DispatchResultWithPostInfo { - T::NetworkContractOrigin::ensure_origin(origin)?; + T::NetworkContractOrigin::ensure_origin_or_root(origin)?; ensure!(Self::accounts(target).is_none(), Error::::ContractAlreadyExisted); @@ -1200,7 +1202,7 @@ pub mod module { #[pallet::call_index(9)] #[pallet::weight(::WeightInfo::publish_free())] pub fn publish_free(origin: OriginFor, contract: EvmAddress) -> DispatchResultWithPostInfo { - T::FreePublicationOrigin::ensure_origin(origin)?; + T::FreePublicationOrigin::ensure_origin_or_root(origin)?; Self::mark_published(contract, None)?; Pallet::::deposit_event(Event::::ContractPublished { contract }); Ok(().into()) diff --git a/modules/evm/src/mock.rs b/modules/evm/src/mock.rs index 1cae7e358..89d23cb9f 100644 --- a/modules/evm/src/mock.rs +++ b/modules/evm/src/mock.rs @@ -26,6 +26,7 @@ use frame_support::{ ConsensusEngineId, }; use frame_system::EnsureSignedBy; +use module_evm_accounts::EvmAddressMapping; use module_support::mocks::{MockAddressMapping, TestRandomness}; use orml_traits::parameter_type_with_key; use primitives::{define_combined_task, Amount, BlockNumber, CurrencyId, ReserveIdentifier, TokenSymbol}; @@ -91,6 +92,8 @@ impl orml_tokens::Config for Runtime { type MaxReserves = (); type ReserveIdentifier = ReserveIdentifier; type DustRemovalWhitelist = Nothing; + #[cfg(any(feature = "wasm-bench", feature = "runtime-benchmarks"))] + type BenchmarkHelper = (); } parameter_types! { @@ -133,6 +136,8 @@ impl module_idle_scheduler::Config for Runtime { type MinimumWeightRemainInBlock = MinimumWeightRemainInBlock; type RelayChainBlockNumberProvider = MockBlockNumberProvider; type DisableBlockThreshold = ConstU32<6>; + #[cfg(feature = "runtime-benchmarks")] + type BenchmarkHelper = (); } impl pallet_utility::Config for Runtime { @@ -173,7 +178,7 @@ ord_parameter_types! { pub const StorageDepositPerByte: Balance = convert_decimals_to_evm(10); } -pub const NEW_CONTRACT_EXTRA_BYTES: u32 = 100; +pub const NEW_CONTRACT_EXTRA_BYTES: u32 = 10000; pub const DEVELOPER_DEPOSIT: u128 = 1000; pub const PUBLICATION_FEE: u128 = 200; impl Config for Runtime { @@ -204,6 +209,14 @@ impl Config for Runtime { type WeightInfo = (); } +impl module_evm_accounts::Config for Runtime { + type Currency = Balances; + type AddressMapping = EvmAddressMapping; + type ChainId = ConstU64<1>; + type TransferAll = (); + type WeightInfo = (); +} + type Block = frame_system::mocking::MockBlock; construct_runtime!( @@ -211,6 +224,7 @@ construct_runtime!( System: frame_system, Timestamp: pallet_timestamp, EVM: evm_mod, + EvmAccounts: module_evm_accounts, Tokens: orml_tokens, Balances: pallet_balances, Currencies: orml_currencies, diff --git a/modules/evm/src/tests.rs b/modules/evm/src/tests.rs index afcd9ae85..cb109e794 100644 --- a/modules/evm/src/tests.rs +++ b/modules/evm/src/tests.rs @@ -278,7 +278,7 @@ fn call_reverts_with_message() { assert_eq!(result.exit_reason, ExitReason::Succeed(ExitSucceed::Returned)); - let alice_balance = INITIAL_BALANCE - 323 * EVM::get_storage_deposit_per_byte(); + let alice_balance = INITIAL_BALANCE - 10223 * EVM::get_storage_deposit_per_byte(); assert_eq!(balance(alice()), alice_balance); @@ -352,9 +352,9 @@ fn should_publish_payable_contract() { let contract_address = result.value; assert_eq!(result.exit_reason, ExitReason::Succeed(ExitSucceed::Returned)); - assert_eq!(result.used_storage, 287); + assert_eq!(result.used_storage, 10187); - let alice_balance = INITIAL_BALANCE - amount - 287 * EVM::get_storage_deposit_per_byte(); + let alice_balance = INITIAL_BALANCE - amount - 10187 * EVM::get_storage_deposit_per_byte(); assert_eq!(balance(alice()), alice_balance); assert_eq!(balance(contract_address), amount); @@ -441,9 +441,9 @@ fn should_transfer_from_contract() { ) .expect("create shouldn't fail"); assert_eq!(result.exit_reason, ExitReason::Succeed(ExitSucceed::Returned)); - assert_eq!(result.used_storage, 1198); + assert_eq!(result.used_storage, 11098); - let alice_balance = INITIAL_BALANCE - 1198 * EVM::get_storage_deposit_per_byte(); + let alice_balance = INITIAL_BALANCE - 11098 * EVM::get_storage_deposit_per_byte(); assert_eq!(balance(alice()), alice_balance); assert_eq!( eth_balance(alice()), @@ -628,9 +628,9 @@ fn contract_should_publish_contracts() { ) .unwrap(); assert_eq!(result.exit_reason, ExitReason::Succeed(ExitSucceed::Returned)); - assert_eq!(result.used_storage, 467); + assert_eq!(result.used_storage, 10367); - let alice_balance = INITIAL_BALANCE - 467 * EVM::get_storage_deposit_per_byte(); + let alice_balance = INITIAL_BALANCE - 10367 * EVM::get_storage_deposit_per_byte(); assert_eq!(balance(alice()), alice_balance); let factory_contract_address = result.value; @@ -638,7 +638,7 @@ fn contract_should_publish_contracts() { assert_eq!(balance(factory_contract_address), 0); assert_eq!( reserved_balance(factory_contract_address), - 467 * EVM::get_storage_deposit_per_byte() + 10367 * EVM::get_storage_deposit_per_byte() ); // Factory.createContract @@ -658,22 +658,22 @@ fn contract_should_publish_contracts() { ) .unwrap(); assert_eq!(result.exit_reason, ExitReason::Succeed(ExitSucceed::Stopped)); - assert_eq!(result.used_storage, 281); + assert_eq!(result.used_storage, 10181); assert_eq!( balance(alice()), - alice_balance - amount - 281 * EVM::get_storage_deposit_per_byte() + alice_balance - amount - 10181 * EVM::get_storage_deposit_per_byte() ); assert_eq!(balance(factory_contract_address), amount); assert_eq!( reserved_balance(factory_contract_address), - (467 + 128) * EVM::get_storage_deposit_per_byte() + (10367 + 128) * EVM::get_storage_deposit_per_byte() ); let contract_address = H160::from_str("7b8f8ca099f6e33cf1817cf67d0556429cfc54e4").unwrap(); assert_eq!(balance(contract_address), 0); assert_eq!( reserved_balance(contract_address), - 153 * EVM::get_storage_deposit_per_byte() + 10053 * EVM::get_storage_deposit_per_byte() ); }); } @@ -709,12 +709,12 @@ fn contract_should_publish_contracts_without_payable() { .unwrap(); assert_eq!(result.exit_reason, ExitReason::Succeed(ExitSucceed::Returned)); - let alice_balance = INITIAL_BALANCE - 464 * EVM::get_storage_deposit_per_byte(); + let alice_balance = INITIAL_BALANCE - 10364 * EVM::get_storage_deposit_per_byte(); assert_eq!(balance(alice()), alice_balance); let factory_contract_address = result.value; assert_eq!(balance(factory_contract_address), 0); - assert_eq!(reserved_balance(factory_contract_address), 4640); + assert_eq!(reserved_balance(factory_contract_address), 103640); // Factory.createContract let create_contract = from_hex("0x412a5a6d").unwrap(); @@ -732,7 +732,7 @@ fn contract_should_publish_contracts_without_payable() { ) .unwrap(); assert_eq!(result.exit_reason, ExitReason::Succeed(ExitSucceed::Stopped)); - assert_eq!(result.used_storage, 290); + assert_eq!(result.used_storage, 10190); assert_eq!( balance(alice()), alice_balance - (result.used_storage as u128 * EVM::get_storage_deposit_per_byte()) @@ -740,7 +740,7 @@ fn contract_should_publish_contracts_without_payable() { assert_eq!(balance(factory_contract_address), 0); assert_eq!( reserved_balance(factory_contract_address), - (464 + 128) * EVM::get_storage_deposit_per_byte() + (10364 + 128) * EVM::get_storage_deposit_per_byte() ); }); } @@ -771,7 +771,7 @@ fn publish_factory() { contract, 0, 2_000_000, - 5000, + 30000, vec![], vec![], ::config(), @@ -779,7 +779,7 @@ fn publish_factory() { .unwrap(); assert_eq!(result.exit_reason, ExitReason::Succeed(ExitSucceed::Returned)); assert_eq!(result.used_gas.as_u64(), 155925); - assert_eq!(result.used_storage, 461); + assert_eq!(result.used_storage, 20261); assert_eq!( balance(alice()), INITIAL_BALANCE - (result.used_storage as u128 * EVM::get_storage_deposit_per_byte()) @@ -820,7 +820,7 @@ fn create_nft_contract_works() { contract: MIRRORED_TOKENS_ADDRESS_START | H160::from_low_u64_be(MIRRORED_NFT_ADDRESS_START), logs: vec![], used_gas: 93197, - used_storage: 284, + used_storage: 10184, })); assert_eq!(EVM::network_contract_index(), MIRRORED_NFT_ADDRESS_START + 1); }); @@ -890,7 +890,7 @@ fn create_predeploy_contract_works() { contract: addr, logs: vec![], used_gas: 93197, - used_storage: 284, + used_storage: 10184, })); assert_noop!( @@ -969,8 +969,8 @@ fn should_transfer_maintainer() { ) .unwrap(); assert_eq!(result.exit_reason, ExitReason::Succeed(ExitSucceed::Returned)); - assert_eq!(result.used_storage, 461); - let alice_balance = INITIAL_BALANCE - 461 * EVM::get_storage_deposit_per_byte(); + assert_eq!(result.used_storage, 20261); + let alice_balance = INITIAL_BALANCE - 20261 * EVM::get_storage_deposit_per_byte(); let contract_address = result.value; assert_eq!(balance(alice()), alice_balance); @@ -1046,8 +1046,8 @@ fn should_publish() { let result = ::Runner::create(alice(), contract, 0, 21_000_000, 21_000_000, vec![], vec![], ::config()).unwrap(); let contract_address = result.value; - assert_eq!(result.used_storage, 284); - let alice_balance = INITIAL_BALANCE - 284 * EVM::get_storage_deposit_per_byte() - DEVELOPER_DEPOSIT; + assert_eq!(result.used_storage, 10184); + let alice_balance = INITIAL_BALANCE - 10184 * EVM::get_storage_deposit_per_byte() - DEVELOPER_DEPOSIT; assert_eq!(balance(alice()), alice_balance); @@ -1295,11 +1295,11 @@ fn should_set_code() { ) .unwrap(); let contract_address = result.value; - assert_eq!(result.used_storage, 284); - let alice_balance = INITIAL_BALANCE - 284 * EVM::get_storage_deposit_per_byte() - DEVELOPER_DEPOSIT; + assert_eq!(result.used_storage, 10184); + let alice_balance = INITIAL_BALANCE - 10184 * EVM::get_storage_deposit_per_byte() - DEVELOPER_DEPOSIT; assert_eq!(balance(alice()), alice_balance); - assert_eq!(reserved_balance(contract_address), 2840); + assert_eq!(reserved_balance(contract_address), 101840); let code_hash = H256::from_str("164981e02df203a0fb32a0af7c2cd1cc7f9df7bb49a4d2b0219307bb68a4b603").unwrap(); assert_eq!( @@ -1336,7 +1336,7 @@ fn should_set_code() { )); assert_ok!(EVM::set_code(RuntimeOrigin::root(), contract_address, contract)); - assert_eq!(reserved_balance(contract_address), 4150); + assert_eq!(reserved_balance(contract_address), 202150); let new_code_hash = H256::from_str("9061d510f6235de4eae304e1a2a2ae22e1610ba893c018b7fabc1f1635f49877").unwrap(); assert_eq!( @@ -1381,7 +1381,7 @@ fn should_set_code() { ref_count: 1, }) ); - assert_eq!(reserved_balance(contract_address), 3000); + assert_eq!(reserved_balance(contract_address), 300000); assert_noop!( EVM::set_code( @@ -1449,8 +1449,8 @@ fn should_selfdestruct_without_schedule_task() { .unwrap(); let contract_address = result.value; - assert_eq!(result.used_storage, 287); - let alice_balance = INITIAL_BALANCE - 287 * EVM::get_storage_deposit_per_byte() - amount - DEVELOPER_DEPOSIT; + assert_eq!(result.used_storage, 10187); + let alice_balance = INITIAL_BALANCE - 10187 * EVM::get_storage_deposit_per_byte() - amount - DEVELOPER_DEPOSIT; assert_eq!(balance(alice()), alice_balance); @@ -1489,7 +1489,7 @@ fn should_selfdestruct_without_schedule_task() { assert!(!CodeInfos::::contains_key(&code_hash)); assert!(!Codes::::contains_key(&code_hash)); - let reserved_amount = 287 * EVM::get_storage_deposit_per_byte(); + let reserved_amount = 10187 * EVM::get_storage_deposit_per_byte(); // refund storage deposit assert_eq!(balance(alice()), alice_balance + amount + reserved_amount); @@ -1549,8 +1549,8 @@ fn should_selfdestruct_with_schedule_task() { .unwrap(); let contract_address = result.value; - assert_eq!(result.used_storage, 361); - let alice_balance = INITIAL_BALANCE - 361 * EVM::get_storage_deposit_per_byte() - amount - DEVELOPER_DEPOSIT; + assert_eq!(result.used_storage, 10261); + let alice_balance = INITIAL_BALANCE - 10261 * EVM::get_storage_deposit_per_byte() - amount - DEVELOPER_DEPOSIT; assert_eq!(balance(alice()), alice_balance); @@ -1657,7 +1657,7 @@ fn should_selfdestruct_with_schedule_task() { // refund storage deposit assert_eq!( balance(alice()), - alice_balance + amount + 361 * EVM::get_storage_deposit_per_byte() + alice_balance + amount + 10261 * EVM::get_storage_deposit_per_byte() ); assert_eq!(balance(contract_address), 0); assert_eq!(reserved_balance(contract_address), 0); @@ -1695,15 +1695,15 @@ fn storage_limit_should_work() { contract.clone(), 0, 200_000, - 1000, + 20000, vec![], vec![], ::config(), ) .unwrap(); assert_eq!(result.exit_reason, ExitReason::Succeed(ExitSucceed::Returned)); - assert_eq!(result.used_storage, 516); - let alice_balance = INITIAL_BALANCE - 516 * EVM::get_storage_deposit_per_byte(); + assert_eq!(result.used_storage, 10416); + let alice_balance = INITIAL_BALANCE - 10416 * EVM::get_storage_deposit_per_byte(); assert_eq!(balance(alice()), alice_balance); let factory_contract_address = result.value; @@ -1711,7 +1711,7 @@ fn storage_limit_should_work() { assert_eq!(balance(factory_contract_address), 0); assert_eq!( reserved_balance(factory_contract_address), - 516 * EVM::get_storage_deposit_per_byte() + 10416 * EVM::get_storage_deposit_per_byte() ); // Factory.createContract(1) @@ -1765,9 +1765,9 @@ fn storage_limit_should_work() { .unwrap(); assert_eq!(result.exit_reason, ExitReason::Succeed(ExitSucceed::Stopped)); - // code_size + array_update(2 items) + extra_size = 290, array_length is already set - let expected_used_storage = 62 + 2 * 64 + 100; - assert_eq!(expected_used_storage, 290); + // code_size + array_update(2 items) + extra_size = 10190, array_length is already set + let expected_used_storage = 62 + 2 * 64 + 10000; + assert_eq!(expected_used_storage, 10190); assert_eq!(result.used_storage, expected_used_storage); // Factory.createContract(2) @@ -1812,7 +1812,7 @@ fn storage_limit_should_work() { create_contract, amount, 1000000000, - 452, + 20252, vec![], vec![], ::config(), @@ -1820,10 +1820,10 @@ fn storage_limit_should_work() { .unwrap(); assert_eq!(result.exit_reason, ExitReason::Succeed(ExitSucceed::Stopped)); - // 2 * code_size + array_update(2 items) + extra_size = 452, array_length is already set - let expected_used_storage = 2 * 62 + 2 * 64 + 2 * 100; + // 2 * code_size + array_update(2 items) + extra_size = 20252, array_length is already set + let expected_used_storage = 2 * 62 + 2 * 64 + 2 * 10000; - assert_eq!(expected_used_storage, 452); + assert_eq!(expected_used_storage, 20252); assert_eq!(result.used_storage, expected_used_storage); }); } @@ -1849,7 +1849,7 @@ fn evm_execute_mode_should_work() { ).unwrap(); new_test_ext().execute_with(|| { - let mut alice_balance = INITIAL_BALANCE - 516 * EVM::get_storage_deposit_per_byte(); + let mut alice_balance = INITIAL_BALANCE - 10416 * EVM::get_storage_deposit_per_byte(); let result = ::Runner::create( alice(), @@ -1867,8 +1867,8 @@ fn evm_execute_mode_should_work() { let account = Accounts::::get(&result.value).unwrap(); let code_info = CodeInfos::::get(account.contract_info.unwrap().code_hash).unwrap(); - // code_size + extra_size = 516 - let expected_used_storage = 416 + 100; + // code_size + extra_size = 10416 + let expected_used_storage = 416 + 10000; assert_eq!(code_info.code_size, 416); assert_eq!(result.used_storage, expected_used_storage); assert_eq!(balance(alice()), alice_balance); @@ -1889,13 +1889,13 @@ fn evm_execute_mode_should_work() { create_contract, Default::default(), 2_100_000, - 1000, + 20000, ExecutionMode::EstimateGas, ) .unwrap(); - // code_size + array_update(1 item + length) + extra_size = 290 - let expected_used_storage = 62 + 2 * 64 + 100; - assert_eq!(expected_used_storage, 290); + // code_size + array_update(1 item + length) + extra_size = 10190 + let expected_used_storage = 62 + 2 * 64 + 10000; + assert_eq!(expected_used_storage, 10190); assert_eq!( result, CallInfo { @@ -1920,9 +1920,9 @@ fn evm_execute_mode_should_work() { ) .unwrap(); - // 2 * code_size + array_update(2 items + length) + extra_size = 516 - let expected_used_storage = 2 * 62 + 3 * 64 + 2 * 100; - assert_eq!(expected_used_storage, 516); + // 2 * code_size + array_update(2 items + length) + extra_size = 20316 + let expected_used_storage = 2 * 62 + 3 * 64 + 2 * 10000; + assert_eq!(expected_used_storage, 20316); assert_eq!( result, CallInfo { @@ -1980,9 +1980,9 @@ fn evm_execute_mode_should_work() { ) .unwrap(); - // code_size + array_update(1 item + length) + extra_size = 290 - let expected_used_storage = 62 + 2 * 64 + 100; - assert_eq!(expected_used_storage, 290); + // code_size + array_update(1 item + length) + extra_size = 10190 + let expected_used_storage = 62 + 2 * 64 + 10000; + assert_eq!(expected_used_storage, 10190); assert_eq!( result, CallInfo { @@ -2023,9 +2023,9 @@ fn evm_execute_mode_should_work() { ) .unwrap(); - // code_size + array_update(1 item) + extra_size = 226, array_length is already set - let expected_used_storage = 62 + 64 + 100; - assert_eq!(expected_used_storage, 226); + // code_size + array_update(1 item) + extra_size = 10126, array_length is already set + let expected_used_storage = 62 + 64 + 10000; + assert_eq!(expected_used_storage, 10126); assert_eq!( result, CallInfo { @@ -2312,7 +2312,7 @@ fn auto_publish_works() { code, 0, 2_100_000, - 10000, + 20000, vec![] )); @@ -2322,7 +2322,7 @@ fn auto_publish_works() { contract: factory, logs: vec![], used_gas: 596931, - used_storage: 2620, + used_storage: 12520, })); // call method `createContract()` @@ -2332,7 +2332,7 @@ fn auto_publish_works() { from_hex("0x412a5a6d").unwrap(), 0, 1000000, - 10000, + 30000, vec![], )); System::assert_last_event(RuntimeEvent::EVM(crate::Event::Executed { @@ -2357,7 +2357,7 @@ fn auto_publish_works() { }, ], used_gas: 389159, - used_storage: 1537, + used_storage: 21337, })); assert_eq!( @@ -2402,7 +2402,7 @@ fn auto_publish_works() { from_hex("0x412a5a6d").unwrap(), 0, 1000000, - 10000, + 30000, vec![], )); System::assert_last_event(RuntimeEvent::EVM(crate::Event::Executed { @@ -2427,7 +2427,7 @@ fn auto_publish_works() { }, ], used_gas: 372059, - used_storage: 1473, + used_storage: 21273, })); assert_eq!( @@ -2469,7 +2469,7 @@ fn auto_publish_works() { from_hex("0x0f24df3a").unwrap(), 0, 1000000, - 10000, + 30000, vec![], )); System::assert_last_event(RuntimeEvent::EVM(crate::Event::Executed { @@ -2484,7 +2484,7 @@ fn auto_publish_works() { data: vec![], }], used_gas: 145811, - used_storage: 400, + used_storage: 10300, })); assert_eq!( diff --git a/modules/homa-validator-list/Cargo.toml b/modules/homa-validator-list/Cargo.toml index 34f304bc4..900cb7ab8 100644 --- a/modules/homa-validator-list/Cargo.toml +++ b/modules/homa-validator-list/Cargo.toml @@ -10,6 +10,7 @@ parity-scale-codec = { workspace = true, features = ["max-encoded-len"] } scale-info = { workspace = true } sp-runtime = { workspace = true } sp-std = { workspace = true } +frame-benchmarking = { workspace = true, optional = true } frame-support = { workspace = true } frame-system = { workspace = true } orml-traits = { workspace = true } @@ -29,6 +30,7 @@ std = [ "serde", "parity-scale-codec/std", + "frame-benchmarking?/std", "frame-support/std", "frame-system/std", "orml-traits/std", @@ -38,6 +40,13 @@ std = [ "sp-std/std", "module-support/std", ] +runtime-benchmarks = [ + "frame-benchmarking/runtime-benchmarks", + "frame-support/runtime-benchmarks", + "frame-system/runtime-benchmarks", + "sp-runtime/runtime-benchmarks", + "orml-tokens/runtime-benchmarks", +] try-runtime = [ "frame-support/try-runtime", "frame-system/try-runtime", diff --git a/modules/homa-validator-list/src/benchmarking.rs b/modules/homa-validator-list/src/benchmarking.rs new file mode 100644 index 000000000..c46ce60ad --- /dev/null +++ b/modules/homa-validator-list/src/benchmarking.rs @@ -0,0 +1,251 @@ +// This file is part of Acala. + +// Copyright (C) 2020-2025 Acala Foundation. +// SPDX-License-Identifier: GPL-3.0-or-later WITH Classpath-exception-2.0 + +// This program 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. + +// This program 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 this program. If not, see . + +use super::*; +use frame_benchmarking::v2::*; +use frame_support::assert_ok; +use frame_system::RawOrigin; + +/// Helper trait for benchmarking. +pub trait BenchmarkHelper { + fn setup_homa_bump_era(era_index: EraIndex); +} + +impl BenchmarkHelper for () { + fn setup_homa_bump_era(_era_index: EraIndex) {} +} + +#[benchmarks] +mod benchmarks { + use super::*; + + #[benchmark] + fn bond() { + let caller: T::AccountId = account("caller", 0, 0); + let validator: T::ValidatorId = account("validator", 0, 0); + let amount = T::MinBondAmount::get(); + + assert_ok!(T::LiquidTokenCurrency::deposit(&caller, 2 * amount)); + + #[extrinsic_call] + _(RawOrigin::Signed(caller.clone()), validator.clone(), amount); + + frame_system::Pallet::::assert_last_event( + Event::BondGuarantee { + who: caller, + validator: validator.clone(), + bond: amount, + } + .into(), + ); + } + + #[benchmark] + fn unbond() { + let caller: T::AccountId = account("caller", 0, 0); + let validator: T::ValidatorId = account("validator", 0, 0); + let amount = T::MinBondAmount::get(); + + assert_ok!(T::LiquidTokenCurrency::deposit(&caller, 2 * amount)); + + assert_ok!(Pallet::::bond( + RawOrigin::Signed(caller.clone()).into(), + validator.clone(), + amount + )); + + #[extrinsic_call] + _(RawOrigin::Signed(caller.clone()), validator.clone(), amount); + + frame_system::Pallet::::assert_last_event( + Event::UnbondGuarantee { + who: caller, + validator: validator.clone(), + bond: amount, + } + .into(), + ); + } + + #[benchmark] + fn rebond() { + let caller: T::AccountId = account("caller", 0, 0); + let validator: T::ValidatorId = account("validator", 0, 0); + let amount = T::MinBondAmount::get(); + + assert_ok!(T::LiquidTokenCurrency::deposit(&caller, 10 * amount)); + + assert_ok!(Pallet::::bond( + RawOrigin::Signed(caller.clone()).into(), + validator.clone(), + 10 * amount + )); + + assert_ok!(Pallet::::unbond( + RawOrigin::Signed(caller.clone()).into(), + validator.clone(), + 5 * amount + )); + + #[extrinsic_call] + _(RawOrigin::Signed(caller.clone()), validator.clone(), 5 * amount); + + frame_system::Pallet::::assert_last_event( + Event::RebondGuarantee { + who: caller, + validator: validator.clone(), + bond: 5 * amount, + } + .into(), + ); + } + + #[benchmark] + fn withdraw_unbonded() { + let caller: T::AccountId = account("caller", 0, 0); + let validator: T::ValidatorId = account("validator", 0, 0); + let amount = T::MinBondAmount::get(); + + assert_ok!(T::LiquidTokenCurrency::deposit(&caller, 10 * amount)); + + assert_ok!(Pallet::::bond( + RawOrigin::Signed(caller.clone()).into(), + validator.clone(), + 10 * amount + )); + + assert_ok!(Pallet::::unbond( + RawOrigin::Signed(caller.clone()).into(), + validator.clone(), + 5 * amount + )); + + T::BenchmarkHelper::setup_homa_bump_era(T::CurrentEra::get().saturating_add(T::BondingDuration::get())); + + #[extrinsic_call] + _(RawOrigin::Signed(caller.clone()), validator.clone()); + + frame_system::Pallet::::assert_has_event( + Event::WithdrawnGuarantee { + who: caller, + validator: validator.clone(), + bond: 5 * amount, + } + .into(), + ); + } + + #[benchmark] + fn freeze(n: Linear<1, 10>) { + let caller: T::AccountId = account("caller", 0, 0); + let mut validators: Vec = vec![]; + let amount = T::MinBondAmount::get(); + + assert_ok!(T::LiquidTokenCurrency::deposit(&caller, 100 * amount)); + + for i in 0..n { + let validator: T::ValidatorId = account("validator", i, 0); + + assert_ok!(Pallet::::bond( + RawOrigin::Signed(caller.clone()).into(), + validator.clone(), + amount + )); + + validators.push(validator); + } + + #[extrinsic_call] + _(RawOrigin::Root, validators.clone()); + + for validator in validators { + frame_system::Pallet::::assert_has_event( + Event::FreezeValidator { + validator: validator.clone(), + } + .into(), + ); + } + } + + #[benchmark] + fn thaw(n: Linear<1, 10>) { + let caller: T::AccountId = account("caller", 0, 0); + let mut validators: Vec = vec![]; + let amount = T::MinBondAmount::get(); + + assert_ok!(T::LiquidTokenCurrency::deposit(&caller, 100 * amount)); + + for i in 0..n { + let validator: T::ValidatorId = account("validator", i, 0); + + assert_ok!(Pallet::::bond( + RawOrigin::Signed(caller.clone()).into(), + validator.clone(), + amount + )); + + validators.push(validator); + } + + assert_ok!(Pallet::::freeze(RawOrigin::Root.into(), validators.clone())); + + #[extrinsic_call] + _(RawOrigin::Root, validators.clone()); + + for validator in validators { + frame_system::Pallet::::assert_has_event( + Event::ThawValidator { + validator: validator.clone(), + } + .into(), + ); + } + } + + #[benchmark] + fn slash(n: Linear<1, 10>) { + let caller: T::AccountId = account("caller", 0, 0); + let mut slashes: Vec> = vec![]; + + assert_ok!(T::LiquidTokenCurrency::deposit( + &caller, + 100 * T::ValidatorInsuranceThreshold::get() + )); + + for i in 0..n { + let validator: T::ValidatorId = account("validator", i, 0); + + assert_ok!(Pallet::::bond( + RawOrigin::Signed(caller.clone()).into(), + validator.clone(), + T::ValidatorInsuranceThreshold::get() * 10 + )); + + slashes.push(SlashInfo { + validator, + token_amount: T::ValidatorInsuranceThreshold::get() * 9, + }); + } + + #[extrinsic_call] + _(RawOrigin::Root, slashes.clone()); + } + + impl_benchmark_test_suite!(Pallet, crate::mock::ExtBuilder::default().build(), crate::mock::Runtime); +} diff --git a/modules/homa-validator-list/src/lib.rs b/modules/homa-validator-list/src/lib.rs index 286a411ea..049e3bf46 100644 --- a/modules/homa-validator-list/src/lib.rs +++ b/modules/homa-validator-list/src/lib.rs @@ -45,10 +45,14 @@ use sp_runtime::{ }; use sp_std::{fmt::Debug, vec, vec::Vec}; +#[cfg(feature = "runtime-benchmarks")] +mod benchmarking; mod mock; mod tests; pub mod weights; +#[cfg(feature = "runtime-benchmarks")] +pub use benchmarking::BenchmarkHelper; pub use module::*; pub use weights::WeightInfo; @@ -167,6 +171,9 @@ pub mod module { type CurrentEra: Get; type WeightInfo: WeightInfo; + + #[cfg(feature = "runtime-benchmarks")] + type BenchmarkHelper: BenchmarkHelper; } #[pallet::error] diff --git a/modules/homa-validator-list/src/mock.rs b/modules/homa-validator-list/src/mock.rs index 29a86603f..ba319e540 100644 --- a/modules/homa-validator-list/src/mock.rs +++ b/modules/homa-validator-list/src/mock.rs @@ -72,6 +72,8 @@ impl orml_tokens::Config for Runtime { type MaxReserves = (); type ReserveIdentifier = [u8; 8]; type DustRemovalWhitelist = Nothing; + #[cfg(feature = "runtime-benchmarks")] + type BenchmarkHelper = (); } impl pallet_balances::Config for Runtime { @@ -119,6 +121,15 @@ parameter_types! { pub const BondingDuration: EraIndex = 28; } +#[cfg(feature = "runtime-benchmarks")] +pub struct MockBenchmarkHelper; +#[cfg(feature = "runtime-benchmarks")] +impl BenchmarkHelper for MockBenchmarkHelper { + fn setup_homa_bump_era(era_index: EraIndex) { + MockCurrentEra::set(era_index); + } +} + impl Config for Runtime { type ValidatorId = AccountId; type LiquidTokenCurrency = LDOTCurrency; @@ -129,6 +140,8 @@ impl Config for Runtime { type LiquidStakingExchangeRateProvider = MockLiquidStakingExchangeProvider; type CurrentEra = MockCurrentEra; type WeightInfo = (); + #[cfg(feature = "runtime-benchmarks")] + type BenchmarkHelper = MockBenchmarkHelper; } type Block = frame_system::mocking::MockBlock; diff --git a/modules/homa/Cargo.toml b/modules/homa/Cargo.toml index 46c568152..e698f66be 100644 --- a/modules/homa/Cargo.toml +++ b/modules/homa/Cargo.toml @@ -46,6 +46,8 @@ runtime-benchmarks = [ "frame-support/runtime-benchmarks", "frame-system/runtime-benchmarks", "sp-runtime/runtime-benchmarks", + "orml-tokens/runtime-benchmarks", + "module-currencies/runtime-benchmarks", ] try-runtime = [ "frame-support/try-runtime", diff --git a/modules/homa/src/benchmarking.rs b/modules/homa/src/benchmarking.rs new file mode 100644 index 000000000..03f9e6136 --- /dev/null +++ b/modules/homa/src/benchmarking.rs @@ -0,0 +1,250 @@ +// This file is part of Acala. + +// Copyright (C) 2020-2025 Acala Foundation. +// SPDX-License-Identifier: GPL-3.0-or-later WITH Classpath-exception-2.0 + +// This program 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. + +// This program 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 this program. If not, see . + +use super::*; +use frame_benchmarking::v2::*; +use frame_support::assert_ok; +use frame_system::RawOrigin; + +#[benchmarks] +mod benchmarks { + use super::*; + + #[benchmark] + fn on_initialize() { + #[block] + { + Pallet::::on_initialize(1u32.into()); + } + } + + #[benchmark] + fn on_initialize_with_bump_era(n: Linear<1, 50>) { + let minter: T::AccountId = account("minter", 0, 0); + let sub_account_index = T::ActiveSubAccountsIndexList::get().first().unwrap().clone(); + let mint_amount = T::MintThreshold::get(); + let redeem_amount = T::RedeemThreshold::get(); + + assert_ok!(T::Currency::deposit( + T::StakingCurrencyId::get(), + &minter, + 10 * mint_amount + )); + + for i in 0..n { + let redeemer = account("redeemer", i, 0); + assert_ok!(T::Currency::deposit( + T::LiquidCurrencyId::get(), + &redeemer, + redeem_amount + )); + } + + // need to process unlocking + assert_ok!(Pallet::::reset_ledgers( + RawOrigin::Root.into(), + vec![( + sub_account_index, + Some(mint_amount), + Some(vec![UnlockChunk { + value: redeem_amount, + era: 10 + }]) + )] + )); + assert_ok!(Pallet::::reset_current_era(RawOrigin::Root.into(), 9)); + + assert_ok!(Pallet::::update_homa_params( + RawOrigin::Root.into(), + Some(10 * mint_amount), + Some(Rate::saturating_from_rational(1, 100)), + Some(Rate::saturating_from_rational(20, 100)), + None, + None, + )); + T::RelayChainBlockNumber::set_block_number(10u32.into()); + + assert_ok!(Pallet::::update_bump_era_params( + RawOrigin::Root.into(), + None, + Some(1u32.into()) + )); + + // need to process to bond + assert_ok!(Pallet::::mint(RawOrigin::Signed(minter).into(), mint_amount)); + + // need to process redeem request + for i in 0..n { + let redeemer = account("redeemer", i, 0); + + assert_ok!(Pallet::::request_redeem( + RawOrigin::Signed(redeemer).into(), + redeem_amount, + false + )); + } + + #[block] + { + Pallet::::on_initialize(1u32.into()); + } + } + + #[benchmark] + fn mint() { + let caller: T::AccountId = account("caller", 0, 0); + let amount = T::MintThreshold::get(); + + assert_ok!(Pallet::::update_homa_params( + RawOrigin::Root.into(), + Some(amount * 10), + Some(Rate::saturating_from_rational(1, 10000)), + None, + None, + None, + )); + + assert_ok!(T::Currency::deposit(T::StakingCurrencyId::get(), &caller, 2 * amount)); + + #[extrinsic_call] + _(RawOrigin::Signed(caller.clone()), amount); + } + + #[benchmark] + fn request_redeem() { + let caller: T::AccountId = account("caller", 0, 0); + let amount = T::RedeemThreshold::get(); + + assert_ok!(T::Currency::deposit(T::LiquidCurrencyId::get(), &caller, 2 * amount)); + + #[extrinsic_call] + _(RawOrigin::Signed(caller.clone()), amount, true); + } + + #[benchmark] + fn fast_match_redeems(n: Linear<1, 50>) { + let caller: T::AccountId = account("caller", 0, 0); + let minter: T::AccountId = account("minter", 0, 0); + let mint_amount = 1_000_000_000_000_000; + + assert_ok!(T::Currency::deposit( + T::StakingCurrencyId::get(), + &minter, + 2 * mint_amount + )); + assert_ok!(Pallet::::update_homa_params( + RawOrigin::Root.into(), + Some(mint_amount * 10), + Some(Rate::saturating_from_rational(1, 10000)), + None, + None, + None, + )); + + assert_ok!(Pallet::::mint(RawOrigin::Signed(minter.clone()).into(), mint_amount)); + + let mut redeem_request_list: Vec = vec![]; + let redeem_amount = 10_000_000_000_000; + + for i in 0..n { + let redeemer = account("redeemer", i, 0); + assert_ok!(T::Currency::deposit( + T::LiquidCurrencyId::get(), + &redeemer, + 2 * redeem_amount + )); + + assert_ok!(Pallet::::request_redeem( + RawOrigin::Signed(redeemer.clone()).into(), + redeem_amount, + true + )); + redeem_request_list.push(redeemer); + } + + #[extrinsic_call] + _(RawOrigin::Signed(caller), redeem_request_list); + } + + #[benchmark] + fn claim_redemption() { + let caller: T::AccountId = account("caller", 0, 0); + let redeemer: T::AccountId = account("redeemer", 0, 0); + let redeption_amount = T::RedeemThreshold::get(); + + Unbondings::::insert(&redeemer, 1, redeption_amount); + UnclaimedRedemption::::put(redeption_amount); + + assert_ok!(T::Currency::deposit( + T::StakingCurrencyId::get(), + &Pallet::::account_id(), + redeption_amount + )); + + assert_ok!(Pallet::::reset_current_era(RawOrigin::Root.into(), 1)); + + #[extrinsic_call] + _(RawOrigin::Signed(caller), redeemer); + } + + #[benchmark] + fn update_homa_params() { + let amount = T::MintThreshold::get(); + + #[extrinsic_call] + _( + RawOrigin::Root, + Some(amount), + Some(Rate::saturating_from_rational(1, 100)), + Some(Rate::saturating_from_rational(1, 100)), + Some(Rate::saturating_from_rational(1, 100)), + Some(7), + ); + } + + #[benchmark] + fn update_bump_era_params() { + T::RelayChainBlockNumber::set_block_number(10000u32.into()); + + #[extrinsic_call] + _(RawOrigin::Root, Some(3000u32.into()), Some(7200u32.into())); + } + + #[benchmark] + fn reset_ledgers(n: Linear<0, 10>) { + let mut updates: Vec<(u16, Option, Option>)> = vec![]; + for i in 0..n { + updates.push(( + i.try_into().unwrap(), + Some(1), + Some(vec![UnlockChunk { value: 1, era: 1 }]), + )) + } + + #[extrinsic_call] + _(RawOrigin::Root, updates); + } + + #[benchmark] + fn reset_current_era() { + #[extrinsic_call] + _(RawOrigin::Root, 1); + } + + impl_benchmark_test_suite!(Pallet, crate::mock::ExtBuilder::default().build(), crate::mock::Runtime); +} diff --git a/modules/homa/src/lib.rs b/modules/homa/src/lib.rs index 0990388da..fa1cb22e6 100644 --- a/modules/homa/src/lib.rs +++ b/modules/homa/src/lib.rs @@ -42,6 +42,8 @@ use sp_std::{cmp::Ordering, convert::From, prelude::*, vec, vec::Vec}; pub use module::*; pub use weights::WeightInfo; +#[cfg(feature = "runtime-benchmarks")] +mod benchmarking; mod mock; mod tests; pub mod weights; @@ -515,7 +517,7 @@ pub mod module { fast_match_fee_rate: Option, nominate_interval_era: Option, ) -> DispatchResult { - T::GovernanceOrigin::ensure_origin(origin)?; + T::GovernanceOrigin::ensure_origin_or_root(origin)?; if let Some(cap_amount) = soft_bonded_cap_per_sub_account { SoftBondedCapPerSubAccount::::put(cap_amount); @@ -562,7 +564,7 @@ pub mod module { last_era_bumped_block: Option>, frequency: Option>, ) -> DispatchResult { - T::GovernanceOrigin::ensure_origin(origin)?; + T::GovernanceOrigin::ensure_origin_or_root(origin)?; if let Some(change) = frequency { BumpEraFrequency::::put(change); @@ -606,7 +608,7 @@ pub mod module { origin: OriginFor, updates: Vec<(u16, Option, Option>)>, ) -> DispatchResult { - T::GovernanceOrigin::ensure_origin(origin)?; + T::GovernanceOrigin::ensure_origin_or_root(origin)?; for (sub_account_index, bonded_change, unlocking_change) in updates { Self::do_update_ledger(sub_account_index, |ledger| -> DispatchResult { @@ -646,7 +648,7 @@ pub mod module { #[pallet::call_index(7)] #[pallet::weight(< T as Config >::WeightInfo::reset_current_era())] pub fn reset_current_era(origin: OriginFor, era_index: EraIndex) -> DispatchResult { - T::GovernanceOrigin::ensure_origin(origin)?; + T::GovernanceOrigin::ensure_origin_or_root(origin)?; RelayChainCurrentEra::::mutate(|current_era| { if *current_era != era_index { @@ -663,7 +665,7 @@ pub mod module { #[pallet::call_index(8)] #[pallet::weight(< T as Config >::WeightInfo::on_initialize_with_bump_era(T::ProcessRedeemRequestsLimit::get()))] pub fn force_bump_current_era(origin: OriginFor, bump_era: EraIndex) -> DispatchResultWithPostInfo { - T::GovernanceOrigin::ensure_origin(origin)?; + T::GovernanceOrigin::ensure_origin_or_root(origin)?; let res = Self::bump_current_era(bump_era); Ok(Some(T::WeightInfo::on_initialize_with_bump_era(res.unwrap_or_default())).into()) diff --git a/modules/homa/src/mock.rs b/modules/homa/src/mock.rs index 4b8c047e3..ae75f6d19 100644 --- a/modules/homa/src/mock.rs +++ b/modules/homa/src/mock.rs @@ -117,6 +117,8 @@ impl orml_tokens::Config for Runtime { type MaxReserves = (); type ReserveIdentifier = [u8; 8]; type DustRemovalWhitelist = Nothing; + #[cfg(feature = "runtime-benchmarks")] + type BenchmarkHelper = (); } impl pallet_balances::Config for Runtime { @@ -154,6 +156,8 @@ impl module_currencies::Config for Runtime { type GasToWeight = (); type SweepOrigin = EnsureRoot; type OnDust = (); + #[cfg(feature = "runtime-benchmarks")] + type BenchmarkHelper = (); } impl BlockNumberProvider for MockRelayBlockNumberProvider { @@ -162,6 +166,11 @@ impl BlockNumberProvider for MockRelayBlockNumberProvider { fn current_block_number() -> Self::BlockNumber { Self::get() } + + #[cfg(feature = "runtime-benchmarks")] + fn set_block_number(block: Self::BlockNumber) { + Self::set(block) + } } ord_parameter_types! { diff --git a/modules/honzon-bridge/Cargo.toml b/modules/honzon-bridge/Cargo.toml index bb9b6aa30..d6c10232a 100644 --- a/modules/honzon-bridge/Cargo.toml +++ b/modules/honzon-bridge/Cargo.toml @@ -9,6 +9,7 @@ scale-info = { workspace = true } parity-scale-codec = { workspace = true } sp-runtime = { workspace = true } sp-std = { workspace = true } +frame-benchmarking = { workspace = true, optional = true } frame-support = { workspace = true } frame-system = { workspace = true } primitives = { workspace = true } @@ -33,6 +34,7 @@ orml-tokens = { workspace = true, features = ["std"] } default = ["std"] std = [ "parity-scale-codec/std", + "frame-benchmarking?/std", "frame-support/std", "frame-system/std", "orml-traits/std", @@ -41,6 +43,14 @@ std = [ "sp-runtime/std", "sp-std/std", ] +runtime-benchmarks = [ + "frame-benchmarking/runtime-benchmarks", + "frame-support/runtime-benchmarks", + "frame-system/runtime-benchmarks", + "sp-runtime/runtime-benchmarks", + "orml-tokens/runtime-benchmarks", + "module-currencies/runtime-benchmarks", +] try-runtime = [ "frame-support/try-runtime", "frame-system/try-runtime", diff --git a/modules/honzon-bridge/src/benchmarking.rs b/modules/honzon-bridge/src/benchmarking.rs new file mode 100644 index 000000000..7fb4b03e9 --- /dev/null +++ b/modules/honzon-bridge/src/benchmarking.rs @@ -0,0 +1,61 @@ +// This file is part of Acala. + +// Copyright (C) 2020-2025 Acala Foundation. +// SPDX-License-Identifier: GPL-3.0-or-later WITH Classpath-exception-2.0 + +// This program 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. + +// This program 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 this program. If not, see . + +use super::*; +use frame_benchmarking::v2::*; +use frame_support::assert_ok; +use frame_system::RawOrigin; + +#[benchmarks] +mod benchmarks { + use super::*; + + #[benchmark] + fn set_bridged_stable_coin_address() { + #[extrinsic_call] + _(RawOrigin::Root, EvmAddress::default()); + } + + #[benchmark] + fn to_bridged() { + let caller: T::AccountId = account("caller", 0, 0); + + assert_ok!(Pallet::::set_bridged_stable_coin_address( + RawOrigin::Root.into(), + EvmAddress::default() + )); + + #[extrinsic_call] + _(RawOrigin::Signed(caller), 0); + } + + #[benchmark] + fn from_bridged() { + let caller: T::AccountId = account("caller", 0, 0); + + assert_ok!(Pallet::::set_bridged_stable_coin_address( + RawOrigin::Root.into(), + EvmAddress::default() + )); + + #[extrinsic_call] + _(RawOrigin::Signed(caller), 0); + } + + impl_benchmark_test_suite!(Pallet, crate::mock::ExtBuilder::default().build(), crate::mock::Runtime); +} diff --git a/modules/honzon-bridge/src/lib.rs b/modules/honzon-bridge/src/lib.rs index 7b3d04108..e7a386be0 100644 --- a/modules/honzon-bridge/src/lib.rs +++ b/modules/honzon-bridge/src/lib.rs @@ -30,6 +30,8 @@ use primitives::{currency::KUSD, evm::EvmAddress, Balance, CurrencyId}; use orml_traits::MultiCurrency; +#[cfg(feature = "runtime-benchmarks")] +mod benchmarking; mod mock; mod tests; pub mod weights; diff --git a/modules/honzon-bridge/src/mock.rs b/modules/honzon-bridge/src/mock.rs index 72219a849..baa67ba71 100644 --- a/modules/honzon-bridge/src/mock.rs +++ b/modules/honzon-bridge/src/mock.rs @@ -77,6 +77,8 @@ impl orml_tokens::Config for Runtime { type MaxReserves = ConstU32<50>; type ReserveIdentifier = [u8; 8]; type DustRemovalWhitelist = Nothing; + #[cfg(feature = "runtime-benchmarks")] + type BenchmarkHelper = (); } impl pallet_balances::Config for Runtime { @@ -121,6 +123,8 @@ impl module_currencies::Config for Runtime { type GasToWeight = (); type SweepOrigin = EnsureRoot; type OnDust = (); + #[cfg(feature = "runtime-benchmarks")] + type BenchmarkHelper = (); } parameter_types! { diff --git a/modules/honzon/Cargo.toml b/modules/honzon/Cargo.toml index 6a26e285a..117024cc3 100644 --- a/modules/honzon/Cargo.toml +++ b/modules/honzon/Cargo.toml @@ -9,6 +9,7 @@ parity-scale-codec = { workspace = true } scale-info = { workspace = true } sp-runtime = { workspace = true } sp-core = { workspace = true } +frame-benchmarking = { workspace = true, optional = true } frame-support = { workspace = true } frame-system = { workspace = true } sp-std = { workspace = true } @@ -16,6 +17,7 @@ module-cdp-engine = { workspace = true } module-loans = { workspace = true } module-support = { workspace = true } primitives = { workspace = true } +orml-tokens = { workspace = true, optional = true } [dev-dependencies] sp-io = { workspace = true, features = ["std"] } @@ -35,6 +37,7 @@ default = ["std"] std = [ "module-cdp-engine/std", "parity-scale-codec/std", + "frame-benchmarking?/std", "frame-support/std", "frame-system/std", "module-loans/std", @@ -45,6 +48,14 @@ std = [ "sp-std/std", "module-support/std", ] +runtime-benchmarks = [ + "frame-benchmarking/runtime-benchmarks", + "frame-support/runtime-benchmarks", + "frame-system/runtime-benchmarks", + "orml-tokens/runtime-benchmarks", + "module-cdp-treasury/runtime-benchmarks", + "module-cdp-engine/runtime-benchmarks", +] try-runtime = [ "module-cdp-engine/try-runtime", "frame-support/try-runtime", diff --git a/modules/honzon/src/benchmarking.rs b/modules/honzon/src/benchmarking.rs new file mode 100644 index 000000000..74e5bfda0 --- /dev/null +++ b/modules/honzon/src/benchmarking.rs @@ -0,0 +1,532 @@ +// This file is part of Acala. + +// Copyright (C) 2020-2025 Acala Foundation. +// SPDX-License-Identifier: GPL-3.0-or-later WITH Classpath-exception-2.0 + +// This program 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. + +// This program 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 this program. If not, see . + +use super::*; +use frame_benchmarking::v2::*; +use frame_support::assert_ok; +use frame_support::traits::Currency; +use frame_system::RawOrigin; +use module_support::{Price, Rate}; +use primitives::orml_traits::{Change, MultiCurrency}; +use sp_runtime::traits::UniqueSaturatedInto; +use sp_runtime::FixedPointNumber; + +/// Helper trait for benchmarking. +pub trait BenchmarkHelper { + fn setup_collateral_currency_ids() -> Vec; + fn setup_stable_currency_id_and_amount() -> Option<(CurrencyId, Balance)>; + fn setup_staking_currency_id_and_amount() -> Option<(CurrencyId, Balance)>; + fn setup_liquid_currency_id_and_amount() -> Option<(CurrencyId, Balance)>; + fn setup_dex_pools(maker: AccountId); + fn setup_feed_price(currency_id: CurrencyId, price: Price); +} + +impl BenchmarkHelper for () { + fn setup_collateral_currency_ids() -> Vec { + vec![] + } + fn setup_stable_currency_id_and_amount() -> Option<(CurrencyId, Balance)> { + None + } + fn setup_staking_currency_id_and_amount() -> Option<(CurrencyId, Balance)> { + None + } + fn setup_liquid_currency_id_and_amount() -> Option<(CurrencyId, Balance)> { + None + } + fn setup_dex_pools(_maker: AccountId) {} + fn setup_feed_price(_currency_id: CurrencyId, _price: Price) {} +} + +#[benchmarks( + where + T: Config + orml_tokens::Config, +)] +mod benchmarks { + use super::*; + + #[benchmark] + fn authorize() { + let caller: T::AccountId = account("caller", 0, 0); + let to: T::AccountId = account("to", 0, 0); + let to_lookup = T::Lookup::unlookup(to); + + // set balance + let _ = ::Currency::deposit_creating( + &caller, + T::DepositPerAuthorization::get() + ::Currency::minimum_balance(), + ); + + let currency_id = ::BenchmarkHelper::setup_collateral_currency_ids()[0]; + + #[extrinsic_call] + _(RawOrigin::Signed(caller.clone()), currency_id, to_lookup); + } + + #[benchmark] + fn unauthorize() { + let caller: T::AccountId = account("caller", 0, 0); + let to: T::AccountId = account("to", 0, 0); + let to_lookup = T::Lookup::unlookup(to); + + // set balance + let _ = ::Currency::deposit_creating( + &caller, + T::DepositPerAuthorization::get() + ::Currency::minimum_balance(), + ); + + let currency_id = ::BenchmarkHelper::setup_collateral_currency_ids()[0]; + + assert_ok!(Pallet::::authorize( + RawOrigin::Signed(caller.clone()).into(), + currency_id, + to_lookup.clone() + )); + + #[extrinsic_call] + _(RawOrigin::Signed(caller), currency_id, to_lookup); + } + + #[benchmark] + fn unauthorize_all(c: Linear<0, { ::BenchmarkHelper::setup_collateral_currency_ids().len() as u32 }>) { + let caller: T::AccountId = account("caller", 0, 0); + let to: T::AccountId = account("to", 0, 0); + let to_lookup = T::Lookup::unlookup(to); + + let currency_ids = ::BenchmarkHelper::setup_collateral_currency_ids(); + + // set balance + let _ = ::Currency::deposit_creating( + &caller, + T::DepositPerAuthorization::get().saturating_mul(c.into()) + ::Currency::minimum_balance(), + ); + + for i in 0..c { + assert_ok!(Pallet::::authorize( + RawOrigin::Signed(caller.clone()).into(), + currency_ids[i as usize], + to_lookup.clone(), + )); + } + + #[extrinsic_call] + _(RawOrigin::Signed(caller)); + } + + // `adjust_loan`, best case: + // adjust both collateral and debit + #[benchmark] + fn adjust_loan() { + let (_stable_currency_id, stable_amount) = + ::BenchmarkHelper::setup_stable_currency_id_and_amount().unwrap(); + let (staking_currency_id, staking_amount) = + ::BenchmarkHelper::setup_staking_currency_id_and_amount().unwrap(); + + let caller: T::AccountId = account("caller", 0, 0); + let debit_value = 100 * stable_amount; + let debit_exchange_rate = module_cdp_engine::Pallet::::get_debit_exchange_rate(staking_currency_id); + let debit_amount = debit_exchange_rate + .reciprocal() + .unwrap() + .saturating_mul_int(debit_value); + let debit_amount: Amount = debit_amount.unique_saturated_into(); + let collateral_value = 10 * debit_value; + let collateral_amount = + Price::saturating_from_rational(staking_amount, stable_amount).saturating_mul_int(collateral_value); + + // set balance + assert_ok!(orml_tokens::Pallet::::deposit( + staking_currency_id, + &caller, + collateral_amount * 2 + )); + + // feed price + ::BenchmarkHelper::setup_feed_price(staking_currency_id, Price::one()); + + // set risk params + assert_ok!(module_cdp_engine::Pallet::::set_collateral_params( + RawOrigin::Root.into(), + staking_currency_id, + Change::NoChange, + Change::NewValue(Some(Ratio::saturating_from_rational(150, 100))), + Change::NewValue(Some(Rate::saturating_from_rational(10, 100))), + Change::NewValue(Some(Ratio::saturating_from_rational(150, 100))), + Change::NewValue(debit_value * 100), + )); + + #[extrinsic_call] + _( + RawOrigin::Signed(caller), + staking_currency_id, + collateral_amount.try_into().unwrap(), + debit_amount, + ); + } + + #[benchmark] + fn transfer_loan_from() { + let (_stable_currency_id, stable_amount) = + ::BenchmarkHelper::setup_stable_currency_id_and_amount().unwrap(); + let (staking_currency_id, staking_amount) = + ::BenchmarkHelper::setup_staking_currency_id_and_amount().unwrap(); + + let caller: T::AccountId = account("caller", 0, 0); + let to: T::AccountId = whitelisted_caller(); + let to_lookup = T::Lookup::unlookup(to.clone()); + + let debit_value = 100 * stable_amount; + let debit_exchange_rate = module_cdp_engine::Pallet::::get_debit_exchange_rate(staking_currency_id); + let debit_amount = debit_exchange_rate + .reciprocal() + .unwrap() + .saturating_mul_int(debit_value); + let debit_amount: Amount = debit_amount.unique_saturated_into(); + let collateral_value = 10 * debit_value; + let collateral_amount = + Price::saturating_from_rational(staking_amount, stable_amount).saturating_mul_int(collateral_value); + + // set balance + assert_ok!(orml_tokens::Pallet::::deposit( + staking_currency_id, + &caller, + collateral_amount * 2 + )); + let _ = ::Currency::deposit_creating( + &caller, + T::DepositPerAuthorization::get() + ::Currency::minimum_balance(), + ); + + // feed price + ::BenchmarkHelper::setup_feed_price(staking_currency_id, Price::one()); + + // set risk params + assert_ok!(module_cdp_engine::Pallet::::set_collateral_params( + RawOrigin::Root.into(), + staking_currency_id, + Change::NoChange, + Change::NewValue(Some(Ratio::saturating_from_rational(150, 100))), + Change::NewValue(Some(Rate::saturating_from_rational(10, 100))), + Change::NewValue(Some(Ratio::saturating_from_rational(150, 100))), + Change::NewValue(debit_value * 100), + )); + + // initialize sender's loan + assert_ok!(Pallet::::adjust_loan( + RawOrigin::Signed(caller.clone()).into(), + staking_currency_id, + collateral_amount.try_into().unwrap(), + debit_amount, + )); + + // authorize receiver + assert_ok!(Pallet::::authorize( + RawOrigin::Signed(caller.clone()).into(), + staking_currency_id, + to_lookup.clone(), + )); + + #[extrinsic_call] + _(RawOrigin::Signed(to), staking_currency_id, to_lookup); + } + + #[benchmark] + fn close_loan_has_debit_by_dex() { + let (_stable_currency_id, stable_amount) = + ::BenchmarkHelper::setup_stable_currency_id_and_amount().unwrap(); + let (staking_currency_id, staking_amount) = + ::BenchmarkHelper::setup_staking_currency_id_and_amount().unwrap(); + + let caller: T::AccountId = account("caller", 0, 0); + let debit_value = 100 * stable_amount; + let debit_exchange_rate = module_cdp_engine::Pallet::::get_debit_exchange_rate(staking_currency_id); + let debit_amount = debit_exchange_rate + .reciprocal() + .unwrap() + .saturating_mul_int(debit_value); + let debit_amount: Amount = debit_amount.unique_saturated_into(); + let collateral_value = 10 * debit_value; + let collateral_amount = + Price::saturating_from_rational(staking_amount, stable_amount).saturating_mul_int(collateral_value); + + // set balance and inject liquidity + assert_ok!(orml_tokens::Pallet::::deposit( + staking_currency_id, + &caller, + (10 * collateral_amount) + orml_tokens::Pallet::::minimum_balance(staking_currency_id) + )); + + let maker: T::AccountId = account("maker", 0, 0); + ::BenchmarkHelper::setup_dex_pools(maker); + + ::BenchmarkHelper::setup_feed_price(staking_currency_id, Price::one()); + + // set risk params + assert_ok!(module_cdp_engine::Pallet::::set_collateral_params( + RawOrigin::Root.into(), + staking_currency_id, + Change::NoChange, + Change::NewValue(Some(Ratio::saturating_from_rational(150, 100))), + Change::NewValue(Some(Rate::saturating_from_rational(10, 100))), + Change::NewValue(Some(Ratio::saturating_from_rational(150, 100))), + Change::NewValue(debit_value * 100), + )); + + // initialize sender's loan + assert_ok!(Pallet::::adjust_loan( + RawOrigin::Signed(caller.clone()).into(), + staking_currency_id, + (10 * collateral_amount).try_into().unwrap(), + debit_amount, + )); + + #[extrinsic_call] + _(RawOrigin::Signed(caller), staking_currency_id, collateral_amount); + } + + #[benchmark] + fn expand_position_collateral() { + let (_stable_currency_id, stable_amount) = + ::BenchmarkHelper::setup_stable_currency_id_and_amount().unwrap(); + let (staking_currency_id, staking_amount) = + ::BenchmarkHelper::setup_staking_currency_id_and_amount().unwrap(); + + let caller: T::AccountId = account("caller", 0, 0); + let debit_value = 100 * stable_amount; + let debit_exchange_rate = module_cdp_engine::Pallet::::get_debit_exchange_rate(staking_currency_id); + let debit_amount = debit_exchange_rate + .reciprocal() + .unwrap() + .saturating_mul_int(debit_value); + let collateral_value = 10 * debit_value; + let collateral_amount = + Price::saturating_from_rational(staking_amount, stable_amount).saturating_mul_int(collateral_value); + + // set balance and inject liquidity for trading path + assert_ok!(orml_tokens::Pallet::::deposit( + staking_currency_id, + &caller, + (10 * collateral_amount) + orml_tokens::Pallet::::minimum_balance(staking_currency_id) + )); + + let maker: T::AccountId = account("maker", 0, 0); + ::BenchmarkHelper::setup_dex_pools(maker); + + ::BenchmarkHelper::setup_feed_price(staking_currency_id, Price::one()); + + // set risk params + assert_ok!(module_cdp_engine::Pallet::::set_collateral_params( + RawOrigin::Root.into(), + staking_currency_id, + Change::NoChange, + Change::NewValue(Some(Ratio::saturating_from_rational(150, 100))), + Change::NewValue(Some(Rate::saturating_from_rational(10, 100))), + Change::NewValue(Some(Ratio::saturating_from_rational(150, 100))), + Change::NewValue(debit_value * 100), + )); + + // initialize sender's loan + assert_ok!(Pallet::::adjust_loan( + RawOrigin::Signed(caller.clone()).into(), + staking_currency_id, + collateral_amount.try_into().unwrap(), + debit_amount.try_into().unwrap(), + )); + + #[extrinsic_call] + _(RawOrigin::Signed(caller), staking_currency_id, debit_value, 0); + } + + #[benchmark] + fn shrink_position_debit() { + let (_stable_currency_id, stable_amount) = + ::BenchmarkHelper::setup_stable_currency_id_and_amount().unwrap(); + let (staking_currency_id, staking_amount) = + ::BenchmarkHelper::setup_staking_currency_id_and_amount().unwrap(); + + let caller: T::AccountId = account("caller", 0, 0); + let debit_value = 100 * stable_amount; + let debit_exchange_rate = module_cdp_engine::Pallet::::get_debit_exchange_rate(staking_currency_id); + let debit_amount = debit_exchange_rate + .reciprocal() + .unwrap() + .saturating_mul_int(debit_value); + let collateral_value = 10 * debit_value; + let collateral_amount = Price::saturating_from_rational(1000 * staking_amount, 1000 * stable_amount) + .saturating_mul_int(collateral_value); + + // set balance and inject liquidity for trading path + assert_ok!(orml_tokens::Pallet::::deposit( + staking_currency_id, + &caller, + (10 * collateral_amount) + orml_tokens::Pallet::::minimum_balance(staking_currency_id) + )); + + let maker: T::AccountId = account("maker", 0, 0); + ::BenchmarkHelper::setup_dex_pools(maker); + + ::BenchmarkHelper::setup_feed_price(staking_currency_id, Price::one()); + + // set risk params + assert_ok!(module_cdp_engine::Pallet::::set_collateral_params( + RawOrigin::Root.into(), + staking_currency_id, + Change::NoChange, + Change::NewValue(Some(Ratio::saturating_from_rational(150, 100))), + Change::NewValue(Some(Rate::saturating_from_rational(10, 100))), + Change::NewValue(Some(Ratio::saturating_from_rational(150, 100))), + Change::NewValue(debit_value * 100), + )); + + // initialize sender's loan + assert_ok!(Pallet::::adjust_loan( + RawOrigin::Signed(caller.clone()).into(), + staking_currency_id, + collateral_amount.try_into().unwrap(), + debit_amount.try_into().unwrap(), + )); + + #[extrinsic_call] + _(RawOrigin::Signed(caller), staking_currency_id, collateral_amount / 5, 0); + } + + #[benchmark] + fn transfer_debit() { + let (_stable_currency_id, stable_amount) = + ::BenchmarkHelper::setup_stable_currency_id_and_amount().unwrap(); + let (staking_currency_id, staking_amount) = + ::BenchmarkHelper::setup_staking_currency_id_and_amount().unwrap(); + let (liquid_currency_id, liquid_amount) = + ::BenchmarkHelper::setup_liquid_currency_id_and_amount().unwrap(); + + let caller: T::AccountId = account("caller", 0, 0); + assert_ok!(orml_tokens::Pallet::::deposit( + staking_currency_id, + &caller, + 100_000 * staking_amount + )); + assert_ok!(orml_tokens::Pallet::::deposit( + liquid_currency_id, + &caller, + 100_000 * liquid_amount + )); + + assert_ok!(module_cdp_engine::Pallet::::set_collateral_params( + RawOrigin::Root.into(), + staking_currency_id, + Change::NoChange, + Change::NewValue(Some(Ratio::saturating_from_rational(150, 100))), + Change::NewValue(Some(Rate::saturating_from_rational(10, 100))), + Change::NewValue(Some(Ratio::saturating_from_rational(150, 100))), + Change::NewValue(10_000 * stable_amount), + )); + assert_ok!(module_cdp_engine::Pallet::::set_collateral_params( + RawOrigin::Root.into(), + liquid_currency_id, + Change::NoChange, + Change::NewValue(Some(Ratio::saturating_from_rational(150, 100))), + Change::NewValue(Some(Rate::saturating_from_rational(10, 100))), + Change::NewValue(Some(Ratio::saturating_from_rational(150, 100))), + Change::NewValue(10_000 * stable_amount), + )); + + ::BenchmarkHelper::setup_feed_price(staking_currency_id, Price::one()); + + assert_ok!(Pallet::::adjust_loan( + RawOrigin::Signed(caller.clone()).into(), + staking_currency_id, + (10_000 * staking_amount).try_into().unwrap(), + (1_000 * stable_amount).try_into().unwrap() + )); + assert_ok!(Pallet::::adjust_loan( + RawOrigin::Signed(caller.clone()).into(), + liquid_currency_id, + (10_000 * liquid_amount).try_into().unwrap(), + (1_000 * stable_amount).try_into().unwrap() + )); + + #[extrinsic_call] + _( + RawOrigin::Signed(caller), + liquid_currency_id, + staking_currency_id, + stable_amount, + ); + } + + #[benchmark] + fn precompile_get_current_collateral_ratio() { + let (_stable_currency_id, stable_amount) = + ::BenchmarkHelper::setup_stable_currency_id_and_amount().unwrap(); + let (staking_currency_id, _staking_amount) = + ::BenchmarkHelper::setup_staking_currency_id_and_amount().unwrap(); + let (liquid_currency_id, liquid_amount) = + ::BenchmarkHelper::setup_liquid_currency_id_and_amount().unwrap(); + + let caller: T::AccountId = account("caller", 0, 0); + + let debit_value = 100 * stable_amount; + let debit_exchange_rate = module_cdp_engine::Pallet::::get_debit_exchange_rate(liquid_currency_id); + let debit_amount = debit_exchange_rate + .reciprocal() + .unwrap() + .saturating_mul_int(debit_value); + let debit_amount: Amount = debit_amount.unique_saturated_into(); + let collateral_value = 10 * debit_value; + let collateral_amount = + Price::saturating_from_rational(liquid_amount, stable_amount).saturating_mul_int(collateral_value); + + // set balance and inject liquidity + assert_ok!(orml_tokens::Pallet::::deposit( + liquid_currency_id, + &caller, + (10 * collateral_amount) + orml_tokens::Pallet::::minimum_balance(liquid_currency_id) + )); + + let maker: T::AccountId = account("maker", 0, 0); + ::BenchmarkHelper::setup_dex_pools(maker); + + ::BenchmarkHelper::setup_feed_price(staking_currency_id, Price::one()); + + // set risk params + assert_ok!(module_cdp_engine::Pallet::::set_collateral_params( + RawOrigin::Root.into(), + liquid_currency_id, + Change::NoChange, + Change::NewValue(Some(Ratio::saturating_from_rational(150, 100))), + Change::NewValue(Some(Rate::saturating_from_rational(10, 100))), + Change::NewValue(Some(Ratio::saturating_from_rational(150, 100))), + Change::NewValue(debit_value * 100), + )); + + // initialize sender's loan + assert_ok!(Pallet::::adjust_loan( + RawOrigin::Signed(caller.clone()).into(), + liquid_currency_id, + (10 * collateral_amount).try_into().unwrap(), + debit_amount, + )); + + #[block] + { + Pallet::::get_current_collateral_ratio(&caller, liquid_currency_id); + } + } + + impl_benchmark_test_suite!(Pallet, crate::mock::ExtBuilder::default().build(), crate::mock::Runtime); +} diff --git a/modules/honzon/src/lib.rs b/modules/honzon/src/lib.rs index 15a9b8e82..40eccdc99 100644 --- a/modules/honzon/src/lib.rs +++ b/modules/honzon/src/lib.rs @@ -40,10 +40,14 @@ use sp_runtime::{ }; use sp_std::prelude::*; +#[cfg(feature = "runtime-benchmarks")] +mod benchmarking; mod mock; mod tests; pub mod weights; +#[cfg(feature = "runtime-benchmarks")] +pub use benchmarking::BenchmarkHelper; pub use module::*; pub use weights::WeightInfo; @@ -71,6 +75,9 @@ pub mod module { /// Weight information for the extrinsics in this module. type WeightInfo: WeightInfo; + + #[cfg(feature = "runtime-benchmarks")] + type BenchmarkHelper: BenchmarkHelper; } #[pallet::error] diff --git a/modules/honzon/src/mock.rs b/modules/honzon/src/mock.rs index 9a4eb9723..e43472e12 100644 --- a/modules/honzon/src/mock.rs +++ b/modules/honzon/src/mock.rs @@ -59,6 +59,7 @@ pub const ACA: CurrencyId = CurrencyId::Token(TokenSymbol::ACA); pub const AUSD: CurrencyId = CurrencyId::Token(TokenSymbol::AUSD); pub const BTC: CurrencyId = CurrencyId::ForeignAsset(255); pub const DOT: CurrencyId = CurrencyId::Token(TokenSymbol::DOT); +pub const LDOT: CurrencyId = CurrencyId::Token(TokenSymbol::LDOT); #[derive_impl(frame_system::config_preludes::TestDefaultConfig as frame_system::DefaultConfig)] impl frame_system::Config for Runtime { @@ -85,6 +86,8 @@ impl orml_tokens::Config for Runtime { type MaxReserves = (); type ReserveIdentifier = ReserveIdentifier; type DustRemovalWhitelist = Nothing; + #[cfg(feature = "runtime-benchmarks")] + type BenchmarkHelper = (); } impl pallet_balances::Config for Runtime { @@ -207,6 +210,8 @@ impl module_cdp_treasury::Config for Runtime { type TreasuryAccount = TreasuryAccount; type WeightInfo = (); type StableAsset = MockStableAsset; + #[cfg(feature = "runtime-benchmarks")] + type BenchmarkHelper = (); } impl pallet_timestamp::Config for Runtime { @@ -307,16 +312,40 @@ impl module_cdp_engine::Config for Runtime { type EVMBridge = module_evm_bridge::EVMBridge; type SettleErc20EvmOrigin = SettleErc20EvmOrigin; type WeightInfo = (); + #[cfg(feature = "runtime-benchmarks")] + type BenchmarkHelper = (); } type UncheckedExtrinsic = frame_system::mocking::MockUncheckedExtrinsic; type Block = frame_system::mocking::MockBlock; +#[cfg(feature = "runtime-benchmarks")] +pub struct MockBenchmarkHelper; +#[cfg(feature = "runtime-benchmarks")] +impl BenchmarkHelper for MockBenchmarkHelper { + fn setup_collateral_currency_ids() -> Vec { + vec![ACA, AUSD, DOT, BTC] + } + fn setup_stable_currency_id_and_amount() -> Option<(CurrencyId, Balance)> { + Some((AUSD, 100)) + } + fn setup_staking_currency_id_and_amount() -> Option<(CurrencyId, Balance)> { + Some((DOT, 100)) + } + fn setup_liquid_currency_id_and_amount() -> Option<(CurrencyId, Balance)> { + Some((LDOT, 100)) + } + fn setup_dex_pools(_maker: AccountId) {} + fn setup_feed_price(_currency_id: CurrencyId, _price: Price) {} +} + impl Config for Runtime { type Currency = PalletBalances; type DepositPerAuthorization = ConstU128<100>; type CollateralCurrencyIds = CollateralCurrencyIds; type WeightInfo = (); + #[cfg(feature = "runtime-benchmarks")] + type BenchmarkHelper = MockBenchmarkHelper; } construct_runtime!( diff --git a/modules/idle-scheduler/Cargo.toml b/modules/idle-scheduler/Cargo.toml index a07c3978c..6e90b5df7 100644 --- a/modules/idle-scheduler/Cargo.toml +++ b/modules/idle-scheduler/Cargo.toml @@ -10,6 +10,7 @@ scale-info = { workspace = true } parity-scale-codec = { workspace = true } sp-runtime = { workspace = true } sp-std = { workspace = true } +frame-benchmarking = { workspace = true, optional = true } frame-support = { workspace = true } frame-system = { workspace = true } primitives = { workspace = true } @@ -22,6 +23,7 @@ sp-io = { workspace = true, features = ["std"] } [features] default = ["std"] std = [ + "frame-benchmarking?/std", "primitives/std", "parity-scale-codec/std", "frame-support/std", @@ -31,6 +33,12 @@ std = [ "sp-runtime/std", "sp-std/std", ] +runtime-benchmarks = [ + "frame-benchmarking/runtime-benchmarks", + "frame-support/runtime-benchmarks", + "frame-system/runtime-benchmarks", + "sp-runtime/runtime-benchmarks", +] try-runtime = [ "frame-support/try-runtime", "frame-system/try-runtime", diff --git a/modules/idle-scheduler/src/benchmarking.rs b/modules/idle-scheduler/src/benchmarking.rs new file mode 100644 index 000000000..c02f220e1 --- /dev/null +++ b/modules/idle-scheduler/src/benchmarking.rs @@ -0,0 +1,90 @@ +// This file is part of Acala. + +// Copyright (C) 2020-2025 Acala Foundation. +// SPDX-License-Identifier: GPL-3.0-or-later WITH Classpath-exception-2.0 + +// This program 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. + +// This program 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 this program. If not, see . + +use super::*; +use frame_benchmarking::v2::*; +use frame_support::assert_ok; +use frame_system::RawOrigin; + +/// Helper trait for benchmarking. +pub trait BenchmarkHelper { + fn setup_schedule_task() -> Option; +} + +impl BenchmarkHelper for () { + fn setup_schedule_task() -> Option { + None + } +} + +#[benchmarks] +mod benchmarks { + use super::*; + + #[benchmark] + fn on_initialize() { + #[block] + { + Pallet::::on_initialize(1u32.into()); + } + } + + #[benchmark] + fn on_idle_base() { + let call = T::BenchmarkHelper::setup_schedule_task().unwrap(); + + assert_ok!(Pallet::::schedule_task(RawOrigin::Root.into(), call)); + + #[block] + { + Pallet::::on_idle(0u32.into(), Weight::from_parts(1_000_000_000, 0)); + } + } + + #[benchmark] + fn clear_tasks() { + let call = T::BenchmarkHelper::setup_schedule_task().unwrap(); + + let task_id = Pallet::::get_next_task_id().unwrap(); + assert_ok!(Pallet::::schedule_task(RawOrigin::Root.into(), call)); + + let completed_tasks = vec![( + task_id, + TaskResult { + result: Ok(()), + used_weight: Weight::zero(), + finished: true, + }, + )]; + + #[block] + { + Pallet::::remove_completed_tasks(completed_tasks); + } + } + + #[benchmark] + fn schedule_task() { + let call = T::BenchmarkHelper::setup_schedule_task().unwrap(); + + #[extrinsic_call] + _(RawOrigin::Root, call); + } + + impl_benchmark_test_suite!(Pallet, crate::mock::ExtBuilder::default().build(), crate::mock::Runtime); +} diff --git a/modules/idle-scheduler/src/lib.rs b/modules/idle-scheduler/src/lib.rs index e49157ebe..71f23a7ee 100644 --- a/modules/idle-scheduler/src/lib.rs +++ b/modules/idle-scheduler/src/lib.rs @@ -36,9 +36,14 @@ use sp_runtime::{ }; use sp_std::{cmp::PartialEq, fmt::Debug, prelude::*}; +#[cfg(feature = "runtime-benchmarks")] +mod benchmarking; mod mock; mod tests; mod weights; + +#[cfg(feature = "runtime-benchmarks")] +pub use benchmarking::BenchmarkHelper; pub use module::*; pub use weights::WeightInfo; @@ -69,6 +74,9 @@ pub mod module { /// relaychain blocks. #[pallet::constant] type DisableBlockThreshold: Get; + + #[cfg(feature = "runtime-benchmarks")] + type BenchmarkHelper: BenchmarkHelper; } #[pallet::event] diff --git a/modules/idle-scheduler/src/mock.rs b/modules/idle-scheduler/src/mock.rs index bfdd69c21..2311f8163 100644 --- a/modules/idle-scheduler/src/mock.rs +++ b/modules/idle-scheduler/src/mock.rs @@ -58,6 +58,15 @@ parameter_types! { pub MinimumWeightRemainInBlock: Weight = Weight::from_parts(100_000_000_000, 0); } +#[cfg(feature = "runtime-benchmarks")] +pub struct MockBenchmarkHelper; +#[cfg(feature = "runtime-benchmarks")] +impl BenchmarkHelper for MockBenchmarkHelper { + fn setup_schedule_task() -> Option { + Some(ScheduledTasks::BalancesTask(BalancesTask::OnIdle)) + } +} + impl module_idle_scheduler::Config for Runtime { type WeightInfo = (); type Index = Nonce; @@ -65,6 +74,8 @@ impl module_idle_scheduler::Config for Runtime { type MinimumWeightRemainInBlock = MinimumWeightRemainInBlock; type RelayChainBlockNumberProvider = MockBlockNumberProvider; type DisableBlockThreshold = ConstU32<6>; + #[cfg(feature = "runtime-benchmarks")] + type BenchmarkHelper = MockBenchmarkHelper; } // Mock dispatachable tasks diff --git a/modules/incentives/Cargo.toml b/modules/incentives/Cargo.toml index 388ed4391..ef3b19797 100644 --- a/modules/incentives/Cargo.toml +++ b/modules/incentives/Cargo.toml @@ -9,6 +9,7 @@ log = { workspace = true } parity-scale-codec = { workspace = true } scale-info = { workspace = true } sp-runtime = { workspace = true } +frame-benchmarking = { workspace = true, optional = true } frame-support = { workspace = true } frame-system = { workspace = true } sp-std = { workspace = true } @@ -28,6 +29,7 @@ orml-rewards = { workspace = true, features = ["std"] } default = ["std"] std = [ "parity-scale-codec/std", + "frame-benchmarking?/std", "frame-support/std", "frame-system/std", "orml-rewards/std", @@ -38,6 +40,11 @@ std = [ "sp-std/std", "module-support/std", ] +runtime-benchmarks = [ + "frame-benchmarking/runtime-benchmarks", + "frame-support/runtime-benchmarks", + "frame-system/runtime-benchmarks", +] try-runtime = [ "frame-support/try-runtime", "frame-system/try-runtime", diff --git a/modules/incentives/src/benchmarking.rs b/modules/incentives/src/benchmarking.rs new file mode 100644 index 000000000..6eb58b099 --- /dev/null +++ b/modules/incentives/src/benchmarking.rs @@ -0,0 +1,163 @@ +// This file is part of Acala. + +// Copyright (C) 2020-2025 Acala Foundation. +// SPDX-License-Identifier: GPL-3.0-or-later WITH Classpath-exception-2.0 + +// This program 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. + +// This program 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 this program. If not, see . + +use super::*; +use frame_benchmarking::v2::*; +use frame_support::assert_ok; +use frame_system::RawOrigin; + +/// Helper trait for benchmarking. +pub trait BenchmarkHelper { + fn setup_stable_currency_id_and_amount() -> Option<(CurrencyId, Balance)>; + fn setup_collateral_currency_ids() -> Vec<(CurrencyId, Balance)>; +} + +impl BenchmarkHelper for () { + fn setup_stable_currency_id_and_amount() -> Option<(CurrencyId, Balance)> { + None + } + fn setup_collateral_currency_ids() -> Vec<(CurrencyId, Balance)> { + vec![] + } +} + +#[benchmarks] +mod benchmarks { + use super::*; + + #[benchmark] + fn on_initialize(c: Linear<0, { T::BenchmarkHelper::setup_collateral_currency_ids().len() as u32 }>) { + let currency_ids = T::BenchmarkHelper::setup_collateral_currency_ids(); + let block_number = T::AccumulatePeriod::get(); + + for i in 0..c { + let (currency_id, amount) = currency_ids[i as usize]; + let pool_id = PoolId::Loans(currency_id); + + assert_ok!(Pallet::::update_incentive_rewards( + RawOrigin::Root.into(), + vec![(pool_id.clone(), vec![(T::NativeCurrencyId::get(), 100 * amount)])] + )); + orml_rewards::PoolInfos::::mutate(pool_id, |pool_info| { + pool_info.total_shares += 100; + }); + } + + Pallet::::on_initialize(1u32.into()); + frame_system::Pallet::::set_block_number(block_number); + + #[block] + { + Pallet::::on_initialize(frame_system::Pallet::::block_number()); + } + } + + #[benchmark] + fn deposit_dex_share() { + let caller: T::AccountId = account("caller", 0, 0); + let (stable_currency_id, amount) = T::BenchmarkHelper::setup_stable_currency_id_and_amount().unwrap(); + let native_stablecoin_lp = + CurrencyId::join_dex_share_currency_id(T::NativeCurrencyId::get(), stable_currency_id).unwrap(); + + let _ = T::Currency::deposit(native_stablecoin_lp, &caller, amount); + + #[extrinsic_call] + _(RawOrigin::Signed(caller.clone()), native_stablecoin_lp, amount); + } + + #[benchmark] + fn withdraw_dex_share() { + let caller: T::AccountId = account("caller", 0, 0); + let (stable_currency_id, amount) = T::BenchmarkHelper::setup_stable_currency_id_and_amount().unwrap(); + let native_stablecoin_lp = + CurrencyId::join_dex_share_currency_id(T::NativeCurrencyId::get(), stable_currency_id).unwrap(); + + let _ = T::Currency::deposit(native_stablecoin_lp, &caller, amount); + + assert_ok!(Pallet::::deposit_dex_share( + RawOrigin::Signed(caller.clone()).into(), + native_stablecoin_lp, + amount + )); + + #[extrinsic_call] + _(RawOrigin::Signed(caller.clone()), native_stablecoin_lp, amount); + } + + #[benchmark] + fn claim_rewards() { + let caller: T::AccountId = account("caller", 0, 0); + let (stable_currency_id, amount) = T::BenchmarkHelper::setup_stable_currency_id_and_amount().unwrap(); + let pool_id = PoolId::Loans(stable_currency_id); + + assert_ok!(orml_rewards::Pallet::::add_share(&caller, &pool_id, amount)); + + let _ = T::Currency::deposit(T::NativeCurrencyId::get(), &&Pallet::::account_id(), 80 * amount); + + assert_ok!(orml_rewards::Pallet::::accumulate_reward( + &pool_id, + T::NativeCurrencyId::get(), + 80 * amount + )); + + #[extrinsic_call] + _(RawOrigin::Signed(caller.clone()), pool_id); + } + + #[benchmark] + fn update_incentive_rewards(c: Linear<0, { T::BenchmarkHelper::setup_collateral_currency_ids().len() as u32 }>) { + let currency_ids = T::BenchmarkHelper::setup_collateral_currency_ids(); + let mut updates = vec![]; + + for i in 0..c { + let (currency_id, amount) = currency_ids[i as usize]; + updates.push((PoolId::Loans(currency_id), vec![(T::NativeCurrencyId::get(), amount)])); + } + + #[extrinsic_call] + _(RawOrigin::Root, updates); + } + + #[benchmark] + fn update_claim_reward_deduction_rates( + c: Linear<0, { T::BenchmarkHelper::setup_collateral_currency_ids().len() as u32 }>, + ) { + let currency_ids = T::BenchmarkHelper::setup_collateral_currency_ids(); + let mut updates = vec![]; + + for i in 0..c { + let (currency_id, _amount) = currency_ids[i as usize]; + updates.push((PoolId::Loans(currency_id), Rate::default())); + } + + #[extrinsic_call] + _(RawOrigin::Root, updates); + } + + #[benchmark] + fn update_claim_reward_deduction_currency() { + #[extrinsic_call] + _( + RawOrigin::Root, + PoolId::Earning(T::NativeCurrencyId::get()), + Some(T::NativeCurrencyId::get()), + ); + } + + impl_benchmark_test_suite!(Pallet, crate::mock::ExtBuilder::default().build(), crate::mock::Runtime); +} diff --git a/modules/incentives/src/lib.rs b/modules/incentives/src/lib.rs index 472a750f8..b31dc27a8 100644 --- a/modules/incentives/src/lib.rs +++ b/modules/incentives/src/lib.rs @@ -50,10 +50,14 @@ use sp_runtime::{ }; use sp_std::{collections::btree_map::BTreeMap, prelude::*}; +#[cfg(feature = "runtime-benchmarks")] +mod benchmarking; mod mock; mod tests; pub mod weights; +#[cfg(feature = "runtime-benchmarks")] +pub use benchmarking::BenchmarkHelper; pub use module::*; pub use weights::WeightInfo; @@ -93,6 +97,9 @@ pub mod module { /// Weight information for the extrinsics in this module. type WeightInfo: WeightInfo; + + #[cfg(feature = "runtime-benchmarks")] + type BenchmarkHelper: BenchmarkHelper; } #[pallet::error] @@ -278,7 +285,7 @@ pub mod module { origin: OriginFor, updates: Vec<(PoolId, Vec<(CurrencyId, Balance)>)>, ) -> DispatchResult { - T::UpdateOrigin::ensure_origin(origin)?; + T::UpdateOrigin::ensure_origin_or_root(origin)?; for (pool_id, update_list) in updates { if let PoolId::Dex(currency_id) = pool_id { ensure!(currency_id.is_dex_share_currency_id(), Error::::InvalidPoolId); @@ -318,7 +325,7 @@ pub mod module { origin: OriginFor, updates: Vec<(PoolId, Rate)>, ) -> DispatchResult { - T::UpdateOrigin::ensure_origin(origin)?; + T::UpdateOrigin::ensure_origin_or_root(origin)?; for (pool_id, deduction_rate) in updates { if let PoolId::Dex(currency_id) = pool_id { ensure!(currency_id.is_dex_share_currency_id(), Error::::InvalidPoolId); @@ -354,7 +361,7 @@ pub mod module { pool_id: PoolId, currency_id: Option, ) -> DispatchResult { - T::UpdateOrigin::ensure_origin(origin)?; + T::UpdateOrigin::ensure_origin_or_root(origin)?; ClaimRewardDeductionCurrency::::mutate_exists(pool_id, |c| *c = currency_id); Self::deposit_event(Event::ClaimRewardDeductionCurrencyUpdated { pool: pool_id, diff --git a/modules/incentives/src/mock.rs b/modules/incentives/src/mock.rs index 008819d26..df2233ca8 100644 --- a/modules/incentives/src/mock.rs +++ b/modules/incentives/src/mock.rs @@ -83,6 +83,8 @@ impl orml_tokens::Config for Runtime { type MaxReserves = (); type ReserveIdentifier = [u8; 8]; type DustRemovalWhitelist = Nothing; + #[cfg(feature = "runtime-benchmarks")] + type BenchmarkHelper = (); } parameter_types! { @@ -124,6 +126,18 @@ ord_parameter_types! { pub const Root: AccountId = ROOT::get(); } +#[cfg(feature = "runtime-benchmarks")] +pub struct MockBenchmarkHelper; +#[cfg(feature = "runtime-benchmarks")] +impl BenchmarkHelper for MockBenchmarkHelper { + fn setup_stable_currency_id_and_amount() -> Option<(CurrencyId, Balance)> { + Some((AUSD, 10000)) + } + fn setup_collateral_currency_ids() -> Vec<(CurrencyId, Balance)> { + vec![(ACA, 100), (AUSD, 100)] + } +} + impl Config for Runtime { type RewardsSource = RewardsSource; type AccumulatePeriod = ConstU64<10>; @@ -133,6 +147,8 @@ impl Config for Runtime { type EmergencyShutdown = MockEmergencyShutdown; type PalletId = IncentivesPalletId; type WeightInfo = (); + #[cfg(feature = "runtime-benchmarks")] + type BenchmarkHelper = MockBenchmarkHelper; } type Block = frame_system::mocking::MockBlock; diff --git a/modules/liquid-crowdloan/Cargo.toml b/modules/liquid-crowdloan/Cargo.toml index 78895c26c..0fc414112 100644 --- a/modules/liquid-crowdloan/Cargo.toml +++ b/modules/liquid-crowdloan/Cargo.toml @@ -10,6 +10,7 @@ parity-scale-codec = { workspace = true } sp-runtime = { workspace = true } sp-std = { workspace = true } +frame-benchmarking = { workspace = true, optional = true } frame-support = { workspace = true } frame-system = { workspace = true } @@ -27,6 +28,7 @@ orml-tokens = { workspace = true, features = ["std"] } [features] default = ["std"] std = [ + "frame-benchmarking?/std", "parity-scale-codec/std", "scale-info/std", "sp-runtime/std", @@ -37,6 +39,14 @@ std = [ "primitives/std", "module-support/std", ] +runtime-benchmarks = [ + "frame-benchmarking/runtime-benchmarks", + "frame-support/runtime-benchmarks", + "frame-system/runtime-benchmarks", + "sp-runtime/runtime-benchmarks", + "orml-tokens/runtime-benchmarks", + "module-currencies/runtime-benchmarks", +] try-runtime = [ "frame-support/try-runtime", "frame-system/try-runtime", diff --git a/modules/liquid-crowdloan/src/benchmarking.rs b/modules/liquid-crowdloan/src/benchmarking.rs new file mode 100644 index 000000000..c0b249c54 --- /dev/null +++ b/modules/liquid-crowdloan/src/benchmarking.rs @@ -0,0 +1,72 @@ +// This file is part of Acala. + +// Copyright (C) 2020-2025 Acala Foundation. +// SPDX-License-Identifier: GPL-3.0-or-later WITH Classpath-exception-2.0 + +// This program 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. + +// This program 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 this program. If not, see . + +use super::*; +use frame_benchmarking::v2::*; +use frame_support::assert_ok; +use frame_system::RawOrigin; + +type BalanceOf = <::Currency as MultiCurrency<::AccountId>>::Balance; + +#[benchmarks] +mod benchmarks { + use super::*; + + #[benchmark] + fn redeem() { + let caller: T::AccountId = account("caller", 0, 0); + let amount: BalanceOf = 1_000_000_000_000_000_000u128.into(); + + assert_ok!(>::deposit( + T::LiquidCrowdloanCurrencyId::get(), + &caller, + amount + )); + assert_ok!(>::deposit( + T::RelayChainCurrencyId::get(), + &Pallet::::account_id(), + amount + )); + + #[extrinsic_call] + _(RawOrigin::Signed(caller), amount); + + frame_system::Pallet::::assert_last_event( + Event::::Redeemed { + currency_id: T::RelayChainCurrencyId::get(), + amount, + } + .into(), + ); + } + + #[benchmark] + fn set_redeem_currency_id() { + #[extrinsic_call] + _(RawOrigin::Root, T::RelayChainCurrencyId::get()); + + frame_system::Pallet::::assert_last_event( + Event::::RedeemCurrencyIdUpdated { + currency_id: T::RelayChainCurrencyId::get(), + } + .into(), + ); + } + + impl_benchmark_test_suite!(Pallet, crate::mock::ExtBuilder::default().build(), crate::mock::Runtime); +} diff --git a/modules/liquid-crowdloan/src/lib.rs b/modules/liquid-crowdloan/src/lib.rs index 7d2b5ef97..2087f7c5c 100644 --- a/modules/liquid-crowdloan/src/lib.rs +++ b/modules/liquid-crowdloan/src/lib.rs @@ -29,6 +29,8 @@ use orml_traits::MultiCurrency; use primitives::{Balance, CurrencyId}; use sp_runtime::{traits::AccountIdConversion, ArithmeticError}; +#[cfg(feature = "runtime-benchmarks")] +mod benchmarking; mod mock; mod tests; pub mod weights; @@ -102,7 +104,7 @@ pub mod module { #[pallet::call_index(2)] #[pallet::weight(::WeightInfo::set_redeem_currency_id())] pub fn set_redeem_currency_id(origin: OriginFor, currency_id: CurrencyId) -> DispatchResult { - T::GovernanceOrigin::ensure_origin(origin)?; + T::GovernanceOrigin::ensure_origin_or_root(origin)?; >::put(currency_id); diff --git a/modules/liquid-crowdloan/src/mock.rs b/modules/liquid-crowdloan/src/mock.rs index 74600092b..dadab0699 100644 --- a/modules/liquid-crowdloan/src/mock.rs +++ b/modules/liquid-crowdloan/src/mock.rs @@ -70,6 +70,8 @@ impl orml_tokens::Config for Runtime { type MaxReserves = (); type ReserveIdentifier = [u8; 8]; type DustRemovalWhitelist = Nothing; + #[cfg(feature = "runtime-benchmarks")] + type BenchmarkHelper = (); } impl pallet_balances::Config for Runtime { @@ -111,6 +113,8 @@ impl module_currencies::Config for Runtime { type GasToWeight = (); type SweepOrigin = EnsureRoot; type OnDust = (); + #[cfg(feature = "runtime-benchmarks")] + type BenchmarkHelper = (); } parameter_types! { diff --git a/modules/loans/Cargo.toml b/modules/loans/Cargo.toml index 4479b9390..a8db7061b 100644 --- a/modules/loans/Cargo.toml +++ b/modules/loans/Cargo.toml @@ -39,6 +39,13 @@ std = [ "sp-std/std", "module-support/std", ] +runtime-benchmarks = [ + "frame-support/runtime-benchmarks", + "frame-system/runtime-benchmarks", + "sp-runtime/runtime-benchmarks", + "orml-tokens/runtime-benchmarks", + "module-cdp-treasury/runtime-benchmarks", +] try-runtime = [ "frame-support/try-runtime", "frame-system/try-runtime", diff --git a/modules/loans/src/mock.rs b/modules/loans/src/mock.rs index 1746e06a8..1ad38ae02 100644 --- a/modules/loans/src/mock.rs +++ b/modules/loans/src/mock.rs @@ -76,6 +76,8 @@ impl orml_tokens::Config for Runtime { type MaxReserves = (); type ReserveIdentifier = [u8; 8]; type DustRemovalWhitelist = Nothing; + #[cfg(feature = "runtime-benchmarks")] + type BenchmarkHelper = (); } impl pallet_balances::Config for Runtime { @@ -158,6 +160,8 @@ impl module_cdp_treasury::Config for Runtime { type TreasuryAccount = TreasuryAccount; type WeightInfo = (); type StableAsset = MockStableAsset; + #[cfg(feature = "runtime-benchmarks")] + type BenchmarkHelper = (); } // mock risk manager diff --git a/modules/nft/Cargo.toml b/modules/nft/Cargo.toml index 8321e8ac0..8a8feb8ac 100644 --- a/modules/nft/Cargo.toml +++ b/modules/nft/Cargo.toml @@ -52,6 +52,8 @@ runtime-benchmarks = [ "frame-system/runtime-benchmarks", "pallet-proxy/runtime-benchmarks", "sp-runtime/runtime-benchmarks", + "orml-tokens/runtime-benchmarks", + "module-currencies/runtime-benchmarks", ] try-runtime = [ "frame-support/try-runtime", diff --git a/modules/nft/src/benchmarking.rs b/modules/nft/src/benchmarking.rs index 40e414bcf..04293a346 100644 --- a/modules/nft/src/benchmarking.rs +++ b/modules/nft/src/benchmarking.rs @@ -18,23 +18,19 @@ //! Benchmarks for the nft module. -#![cfg(feature = "runtime-benchmarks")] - -use sp_std::vec; - -use frame_benchmarking::{account, benchmarks}; -use frame_support::{dispatch::DispatchClass, dispatch::DispatchErrorWithPostInfo, traits::Get}; +use super::*; +use frame_benchmarking::v2::*; +use frame_support::assert_ok; +use frame_support::{dispatch::DispatchClass, traits::Get}; use frame_system::RawOrigin; use sp_runtime::traits::{AccountIdConversion, StaticLookup, UniqueSaturatedInto}; use sp_std::collections::btree_map::BTreeMap; +use sp_std::vec; -pub use crate::*; use primitives::Balance; pub struct Module(crate::Pallet); -const SEED: u32 = 0; - fn dollar(d: u32) -> Balance { let d: Balance = d.into(); d.saturating_mul(1_000_000_000_000_000_000) @@ -48,13 +44,14 @@ fn test_attr() -> Attributes { attr } -fn create_token_class(caller: T::AccountId) -> Result { +fn create_token_class(caller: T::AccountId) -> T::AccountId { let base_currency_amount = dollar(1000); - ::Currency::make_free_balance_be(&caller, base_currency_amount.unique_saturated_into()); + ::Currency::make_free_balance_be(&caller, base_currency_amount.unique_saturated_into()); let module_account: T::AccountId = T::PalletId::get().into_sub_account_truncating(orml_nft::Pallet::::next_class_id()); - crate::Pallet::::create_class( + + assert_ok!(Pallet::::create_class( RawOrigin::Signed(caller).into(), vec![1], Properties( @@ -64,260 +61,147 @@ fn create_token_class(caller: T::AccountId) -> Result::Currency::make_free_balance_be( - &module_account, - base_currency_amount.unique_saturated_into(), - ); + ::Currency::make_free_balance_be(&module_account, base_currency_amount.unique_saturated_into()); - Ok(module_account) + module_account } -benchmarks! { +#[benchmarks] +mod benchmarks { + use super::*; + // create NFT class - create_class { - let caller: T::AccountId = account("caller", 0, SEED); + #[benchmark] + fn create_class() { + let caller: T::AccountId = account("caller", 0, 0); let base_currency_amount = dollar(1000); - ::Currency::make_free_balance_be(&caller, base_currency_amount.unique_saturated_into()); - }: _(RawOrigin::Signed(caller), vec![1], Properties(ClassProperty::Transferable | ClassProperty::Burnable), test_attr()) + ::Currency::make_free_balance_be(&caller, base_currency_amount.unique_saturated_into()); - // mint NFT token - mint { - let i in 1 .. 1000; + #[extrinsic_call] + _( + RawOrigin::Signed(caller), + vec![1], + Properties(ClassProperty::Transferable | ClassProperty::Burnable), + test_attr(), + ); + } - let caller: T::AccountId = account("caller", 0, SEED); - let to: T::AccountId = account("to", 0, SEED); + // mint NFT token + #[benchmark] + fn mint(i: Linear<1, 1000>) { + let caller: T::AccountId = account("caller", 0, 0); + let to: T::AccountId = account("to", 0, 0); let to_lookup = T::Lookup::unlookup(to); - let module_account = create_token_class::(caller)?; - }: _(RawOrigin::Signed(module_account), to_lookup, 0u32.into(), vec![1], test_attr(), i) + let module_account = create_token_class::(caller); + + #[extrinsic_call] + _( + RawOrigin::Signed(module_account), + to_lookup, + 0u32.into(), + vec![1], + test_attr(), + i, + ); + } // transfer NFT token to another account - transfer { - let caller: T::AccountId = account("caller", 0, SEED); + #[benchmark] + fn transfer() { + let caller: T::AccountId = account("caller", 0, 0); let caller_lookup = T::Lookup::unlookup(caller.clone()); - let to: T::AccountId = account("to", 0, SEED); + let to: T::AccountId = account("to", 0, 0); let to_lookup = T::Lookup::unlookup(to.clone()); - let module_account = create_token_class::(caller)?; + let module_account = create_token_class::(caller); - crate::Pallet::::mint(RawOrigin::Signed(module_account).into(), to_lookup, 0u32.into(), vec![1], test_attr(), 1)?; - }: _(RawOrigin::Signed(to), caller_lookup, (0u32.into(), 0u32.into())) + assert_ok!(Pallet::::mint( + RawOrigin::Signed(module_account).into(), + to_lookup, + 0u32.into(), + vec![1], + test_attr(), + 1 + )); + + #[extrinsic_call] + _(RawOrigin::Signed(to), caller_lookup, (0u32.into(), 0u32.into())); + } // burn NFT token - burn { - let caller: T::AccountId = account("caller", 0, SEED); - let to: T::AccountId = account("to", 0, SEED); + #[benchmark] + fn burn() { + let caller: T::AccountId = account("caller", 0, 0); + let to: T::AccountId = account("to", 0, 0); let to_lookup = T::Lookup::unlookup(to.clone()); - let module_account = create_token_class::(caller)?; + let module_account = create_token_class::(caller); + + assert_ok!(Pallet::::mint( + RawOrigin::Signed(module_account).into(), + to_lookup, + 0u32.into(), + vec![1], + test_attr(), + 1 + )); - crate::Pallet::::mint(RawOrigin::Signed(module_account).into(), to_lookup, 0u32.into(), vec![1], test_attr(), 1)?; - }: _(RawOrigin::Signed(to), (0u32.into(), 0u32.into())) + #[extrinsic_call] + _(RawOrigin::Signed(to), (0u32.into(), 0u32.into())); + } // burn NFT token with remark - burn_with_remark { - let b in 0 .. *T::BlockLength::get().max.get(DispatchClass::Normal) as u32; + #[benchmark] + fn burn_with_remark(b: Linear<0, { *T::BlockLength::get().max.get(DispatchClass::Normal) as u32 }>) { let remark_message = vec![1; b as usize]; - let caller: T::AccountId = account("caller", 0, SEED); - let to: T::AccountId = account("to", 0, SEED); + let caller: T::AccountId = account("caller", 0, 0); + let to: T::AccountId = account("to", 0, 0); let to_lookup = T::Lookup::unlookup(to.clone()); - let module_account = create_token_class::(caller)?; + let module_account = create_token_class::(caller); + + assert_ok!(Pallet::::mint( + RawOrigin::Signed(module_account).into(), + to_lookup, + 0u32.into(), + vec![1], + test_attr(), + 1 + )); - crate::Pallet::::mint(RawOrigin::Signed(module_account).into(), to_lookup, 0u32.into(), vec![1], test_attr(), 1)?; - }: _(RawOrigin::Signed(to), (0u32.into(), 0u32.into()), remark_message) + #[extrinsic_call] + _(RawOrigin::Signed(to), (0u32.into(), 0u32.into()), remark_message); + } // destroy NFT class - destroy_class { - let caller: T::AccountId = account("caller", 0, SEED); + #[benchmark] + fn destroy_class() { + let caller: T::AccountId = account("caller", 0, 0); let caller_lookup = T::Lookup::unlookup(caller.clone()); - let base_currency_amount = dollar(1000); - - let module_account = create_token_class::(caller)?; + let module_account = create_token_class::(caller); - }: _(RawOrigin::Signed(module_account), 0u32.into(), caller_lookup) - - update_class_properties { - let caller: T::AccountId = account("caller", 0, SEED); - let to: T::AccountId = account("to", 0, SEED); - let to_lookup = T::Lookup::unlookup(to); - - let module_account = create_token_class::(caller)?; - }: _(RawOrigin::Signed(module_account), 0u32.into(), Properties(ClassProperty::Transferable.into())) -} - -#[cfg(test)] -mod mock { - use super::*; - use crate as nft; - - use frame_support::{ - derive_impl, parameter_types, - traits::{ConstU128, ConstU32, Contains, InstanceFilter}, - PalletId, - }; - use parity_scale_codec::{Decode, DecodeWithMemTracking, Encode}; - use sp_core::crypto::AccountId32; - use sp_runtime::{ - traits::{BlakeTwo256, IdentityLookup}, - BuildStorage, RuntimeDebug, - }; - - pub type AccountId = AccountId32; - - #[derive_impl(frame_system::config_preludes::TestDefaultConfig as frame_system::DefaultConfig)] - impl frame_system::Config for Runtime { - type AccountId = AccountId; - type Lookup = IdentityLookup; - type Block = Block; - type AccountData = pallet_balances::AccountData; - } - impl pallet_balances::Config for Runtime { - type Balance = Balance; - type RuntimeEvent = RuntimeEvent; - type DustRemoval = (); - type ExistentialDeposit = ConstU128<1>; - type AccountStore = frame_system::Pallet; - type MaxLocks = (); - type MaxReserves = ConstU32<50>; - type ReserveIdentifier = ReserveIdentifier; - type WeightInfo = (); - type RuntimeHoldReason = RuntimeHoldReason; - type RuntimeFreezeReason = RuntimeFreezeReason; - type FreezeIdentifier = (); - type MaxFreezes = (); - type DoneSlashHandler = (); - } - impl pallet_utility::Config for Runtime { - type RuntimeEvent = RuntimeEvent; - type RuntimeCall = RuntimeCall; - type PalletsOrigin = OriginCaller; - type WeightInfo = (); - } - #[derive( - Copy, - Clone, - Eq, - PartialEq, - Ord, - PartialOrd, - Encode, - Decode, - DecodeWithMemTracking, - RuntimeDebug, - MaxEncodedLen, - TypeInfo, - )] - pub enum ProxyType { - Any, - JustTransfer, - JustUtility, - } - impl Default for ProxyType { - fn default() -> Self { - Self::Any - } - } - impl InstanceFilter for ProxyType { - fn filter(&self, c: &RuntimeCall) -> bool { - match self { - ProxyType::Any => true, - ProxyType::JustTransfer => matches!( - c, - RuntimeCall::Balances(pallet_balances::Call::transfer_allow_death { .. }) - ), - ProxyType::JustUtility => matches!(c, RuntimeCall::Utility(..)), - } - } - fn is_superset(&self, o: &Self) -> bool { - self == &ProxyType::Any || self == o - } - } - pub struct BaseFilter; - impl Contains for BaseFilter { - fn contains(c: &RuntimeCall) -> bool { - match *c { - // Remark is used as a no-op call in the benchmarking - RuntimeCall::System(SystemCall::remark { .. }) => true, - RuntimeCall::System(_) => false, - _ => true, - } - } - } - impl pallet_proxy::Config for Runtime { - type RuntimeEvent = RuntimeEvent; - type RuntimeCall = RuntimeCall; - type Currency = Balances; - type ProxyType = ProxyType; - type ProxyDepositBase = ConstU128<1>; - type ProxyDepositFactor = ConstU128<1>; - type MaxProxies = ConstU32<4>; - type WeightInfo = (); - type CallHasher = BlakeTwo256; - type MaxPending = ConstU32<2>; - type AnnouncementDepositBase = ConstU128<1>; - type AnnouncementDepositFactor = ConstU128<1>; - type BlockNumberProvider = System; - } - - parameter_types! { - pub const NftPalletId: PalletId = PalletId(*b"aca/aNFT"); + #[extrinsic_call] + _(RawOrigin::Signed(module_account), 0u32.into(), caller_lookup); } - impl crate::Config for Runtime { - type Currency = Balances; - type CreateClassDeposit = ConstU128<200>; - type CreateTokenDeposit = ConstU128<100>; - type DataDepositPerByte = ConstU128<10>; - type PalletId = NftPalletId; - type MaxAttributesBytes = ConstU32<2048>; - type WeightInfo = (); - } + #[benchmark] + fn update_class_properties() { + let caller: T::AccountId = account("caller", 0, 0); - impl orml_nft::Config for Runtime { - type ClassId = u32; - type TokenId = u64; - type ClassData = ClassData; - type TokenData = TokenData; - type MaxClassMetadata = ConstU32<1024>; - type MaxTokenMetadata = ConstU32<1024>; - } + let module_account = create_token_class::(caller); - type Block = frame_system::mocking::MockBlock; - - frame_support::construct_runtime!( - pub enum Runtime { - System: frame_system, - Utility: pallet_utility, - Balances: pallet_balances, - Proxy: pallet_proxy, - OrmlNFT: orml_nft, - NFT: nft, - } - ); - - use frame_system::Call as SystemCall; - - pub fn new_test_ext() -> sp_io::TestExternalities { - let t = frame_system::GenesisConfig::::default() - .build_storage() - .unwrap(); - - let mut ext = sp_io::TestExternalities::new(t); - ext.execute_with(|| System::set_block_number(1)); - ext + #[extrinsic_call] + _( + RawOrigin::Signed(module_account), + 0u32.into(), + Properties(ClassProperty::Transferable.into()), + ); } -} - -#[cfg(test)] -mod tests { - use super::mock::*; - use super::*; - use frame_benchmarking::impl_benchmark_test_suite; - impl_benchmark_test_suite!(Pallet, super::new_test_ext(), super::Runtime,); + impl_benchmark_test_suite!(Pallet, crate::mock::ExtBuilder::default().build(), crate::mock::Runtime); } diff --git a/modules/nft/src/lib.rs b/modules/nft/src/lib.rs index 113311ae9..7deab2ddf 100644 --- a/modules/nft/src/lib.rs +++ b/modules/nft/src/lib.rs @@ -48,6 +48,7 @@ use sp_runtime::{ }; use sp_std::prelude::*; +#[cfg(feature = "runtime-benchmarks")] pub mod benchmarking; mod mock; mod tests; diff --git a/modules/nft/src/mock.rs b/modules/nft/src/mock.rs index 9e6470f73..310468f47 100644 --- a/modules/nft/src/mock.rs +++ b/modules/nft/src/mock.rs @@ -149,6 +149,8 @@ impl orml_tokens::Config for Runtime { type MaxReserves = (); type ReserveIdentifier = [u8; 8]; type DustRemovalWhitelist = Nothing; + #[cfg(feature = "runtime-benchmarks")] + type BenchmarkHelper = (); } pub const NATIVE_CURRENCY_ID: CurrencyId = CurrencyId::Token(TokenSymbol::ACA); @@ -169,6 +171,8 @@ impl module_currencies::Config for Runtime { type GasToWeight = (); type SweepOrigin = EnsureSignedBy; type OnDust = (); + #[cfg(feature = "runtime-benchmarks")] + type BenchmarkHelper = (); } parameter_types! { @@ -177,7 +181,7 @@ parameter_types! { pub const CREATE_CLASS_DEPOSIT: u128 = 200; pub const CREATE_TOKEN_DEPOSIT: u128 = 100; pub const DATA_DEPOSIT_PER_BYTE: u128 = 10; -pub const MAX_ATTRIBUTES_BYTES: u32 = 10; +pub const MAX_ATTRIBUTES_BYTES: u32 = 2048; impl Config for Runtime { type Currency = Balances; type CreateClassDeposit = ConstU128; diff --git a/modules/nft/src/tests.rs b/modules/nft/src/tests.rs index 3bb0d23a3..cab0175de 100644 --- a/modules/nft/src/tests.rs +++ b/modules/nft/src/tests.rs @@ -99,7 +99,7 @@ fn create_class_should_fail() { ); let mut large_attr: Attributes = BTreeMap::new(); - large_attr.insert(vec![1, 2, 3, 4, 5], vec![6, 7, 8, 9, 10, 11]); + large_attr.insert(vec![0; 2048], vec![1]); assert_noop!( NFTModule::create_class( diff --git a/modules/nominees-election/Cargo.toml b/modules/nominees-election/Cargo.toml index 8afddd152..37e790e0e 100644 --- a/modules/nominees-election/Cargo.toml +++ b/modules/nominees-election/Cargo.toml @@ -9,6 +9,7 @@ log = { workspace = true } parity-scale-codec = { workspace = true, features = ["max-encoded-len"] } scale-info = { workspace = true } sp-runtime = { workspace = true } +frame-benchmarking = { workspace = true, optional = true } frame-support = { workspace = true } frame-system = { workspace = true } sp-std = { workspace = true } @@ -27,6 +28,7 @@ orml-tokens = { workspace = true, features = ["std"] } default = ["std"] std = [ "parity-scale-codec/std", + "frame-benchmarking?/std", "frame-support/std", "frame-system/std", "orml-traits/std", @@ -36,6 +38,11 @@ std = [ "sp-std/std", "module-support/std", ] +runtime-benchmarks = [ + "frame-benchmarking/runtime-benchmarks", + "frame-support/runtime-benchmarks", + "frame-system/runtime-benchmarks", +] try-runtime = [ "frame-support/try-runtime", "frame-system/try-runtime", diff --git a/modules/nominees-election/src/benchmarking.rs b/modules/nominees-election/src/benchmarking.rs new file mode 100644 index 000000000..cd172a68c --- /dev/null +++ b/modules/nominees-election/src/benchmarking.rs @@ -0,0 +1,186 @@ +// This file is part of Acala. + +// Copyright (C) 2020-2025 Acala Foundation. +// SPDX-License-Identifier: GPL-3.0-or-later WITH Classpath-exception-2.0 + +// This program 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. + +// This program 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 this program. If not, see . + +use super::*; +use frame_benchmarking::v2::*; +use frame_support::assert_ok; +use frame_system::RawOrigin; +use sp_runtime::SaturatedConversion; + +/// Helper trait for benchmarking. +pub trait BenchmarkHelper { + fn setup_homa_bump_era(era_index: EraIndex); + fn setup_homa_validators(caller: AccountId, targets: Vec); +} + +impl BenchmarkHelper for () { + fn setup_homa_bump_era(_era_index: EraIndex) {} + fn setup_homa_validators(_caller: AccountId, _targets: Vec) {} +} + +#[benchmarks] +mod benchmarks { + use super::*; + + #[benchmark] + fn bond() { + let caller: T::AccountId = account("caller", 0, 0); + let amount = T::MinBond::get(); + + assert_ok!(T::Currency::deposit(&caller, 2 * amount)); + + #[extrinsic_call] + _(RawOrigin::Signed(caller), amount); + } + + #[benchmark] + fn unbond() { + let caller: T::AccountId = account("caller", 0, 0); + let amount = T::MinBond::get(); + + assert_ok!(T::Currency::deposit(&caller, 2 * amount)); + + assert_ok!(Pallet::::bond(RawOrigin::Signed(caller.clone()).into(), amount)); + + #[extrinsic_call] + _(RawOrigin::Signed(caller.clone()), amount); + } + + #[benchmark] + fn rebond(c: Linear<1, { T::MaxUnbondingChunks::get() }>) { + let caller: T::AccountId = account("caller", 0, 0); + let amount = T::MinBond::get(); + + assert_ok!(T::Currency::deposit(&caller, 2 * amount)); + + assert_ok!(Pallet::::bond(RawOrigin::Signed(caller.clone()).into(), 2 * amount)); + + for _ in 0..c { + assert_ok!(Pallet::::unbond( + RawOrigin::Signed(caller.clone()).into(), + amount.saturating_div(c.into()) + )); + } + + #[extrinsic_call] + _(RawOrigin::Signed(caller), amount); + } + + #[benchmark] + fn withdraw_unbonded(c: Linear<1, { T::MaxUnbondingChunks::get() }>) { + let caller: T::AccountId = account("caller", 0, 0); + let amount = T::MinBond::get(); + + assert_ok!(T::Currency::deposit(&caller, 2 * amount)); + + assert_ok!(Pallet::::bond(RawOrigin::Signed(caller.clone()).into(), 2 * amount)); + + for _ in 0..c { + assert_ok!(Pallet::::unbond( + RawOrigin::Signed(caller.clone()).into(), + amount.saturating_div(c.into()) + )); + } + + T::BenchmarkHelper::setup_homa_bump_era(T::CurrentEra::get().saturating_add(T::BondingDuration::get())); + + #[extrinsic_call] + _(RawOrigin::Signed(caller.clone())); + + frame_system::Pallet::::assert_last_event( + Event::WithdrawUnbonded { + who: caller, + amount: amount.saturating_div(c.into()).saturating_mul(c.into()), + } + .into(), + ); + } + + #[benchmark] + fn nominate(c: Linear<1, { T::MaxNominateesCount::get() }>) { + let caller: T::AccountId = account("caller", 0, 0); + let amount = T::MinBond::get(); + let targets: Vec = (0..c).map(|c| account("nominatees", c, 0)).collect(); + + assert_ok!(T::Currency::deposit(&caller, 100_000_000 * amount)); + + T::BenchmarkHelper::setup_homa_validators(caller.clone(), targets.clone()); + + assert_ok!(Pallet::::bond(RawOrigin::Signed(caller.clone()).into(), amount)); + + #[extrinsic_call] + _(RawOrigin::Signed(caller.clone()), targets.clone()); + + let mut sorted_targets = targets.clone(); + sorted_targets.sort(); + + frame_system::Pallet::::assert_last_event( + Event::Nominate { + who: caller, + targets: sorted_targets, + } + .into(), + ); + } + + #[benchmark] + fn chill(c: Linear<1, { T::MaxNominateesCount::get() }>) { + let caller: T::AccountId = account("caller", 0, 0); + let amount = T::MinBond::get(); + let targets: Vec = (0..c).map(|c| account("nominatees", c, 0)).collect(); + + assert_ok!(T::Currency::deposit(&caller, 100_000_000 * amount)); + + T::BenchmarkHelper::setup_homa_validators(caller.clone(), targets.clone()); + + assert_ok!(Pallet::::bond(RawOrigin::Signed(caller.clone()).into(), amount)); + + assert_ok!(Pallet::::nominate(RawOrigin::Signed(caller.clone()).into(), targets)); + + #[extrinsic_call] + _(RawOrigin::Signed(caller.clone())); + + frame_system::Pallet::::assert_last_event( + Event::Nominate { + who: caller, + targets: vec![], + } + .into(), + ); + } + + #[benchmark] + fn reset_reserved_nominees(c: Linear<1, 4>) { + let updates: Vec<(u16, BoundedVec)> = (0..c) + .map(|c| { + let reserved: BoundedVec = (0..T::MaxNominateesCount::get()) + .map(|c| account("nominatees", c, 0)) + .collect::>() + .try_into() + .unwrap(); + + (c.saturated_into(), reserved) + }) + .collect(); + + #[extrinsic_call] + _(RawOrigin::Root, updates); + } + + impl_benchmark_test_suite!(Pallet, crate::mock::ExtBuilder::default().build(), crate::mock::Runtime); +} diff --git a/modules/nominees-election/src/lib.rs b/modules/nominees-election/src/lib.rs index 20548a82f..f32f04775 100644 --- a/modules/nominees-election/src/lib.rs +++ b/modules/nominees-election/src/lib.rs @@ -35,10 +35,14 @@ use primitives::{ use sp_runtime::traits::{MaybeDisplay, MaybeSerializeDeserialize, Member, Zero}; use sp_std::{fmt::Debug, prelude::*}; +#[cfg(feature = "runtime-benchmarks")] +pub mod benchmarking; mod mock; mod tests; pub mod weights; +#[cfg(feature = "runtime-benchmarks")] +pub use benchmarking::BenchmarkHelper; pub use module::*; pub use weights::WeightInfo; @@ -90,6 +94,9 @@ pub mod module { type CurrentEra: Get; type WeightInfo: WeightInfo; + + #[cfg(feature = "runtime-benchmarks")] + type BenchmarkHelper: BenchmarkHelper; } pub type BondingLedgerOf = bonding::BondingLedgerOf>; diff --git a/modules/nominees-election/src/mock.rs b/modules/nominees-election/src/mock.rs index 484e15dc1..02112d482 100644 --- a/modules/nominees-election/src/mock.rs +++ b/modules/nominees-election/src/mock.rs @@ -85,6 +85,8 @@ impl orml_tokens::Config for Runtime { type MaxReserves = (); type ReserveIdentifier = [u8; 8]; type DustRemovalWhitelist = Nothing; + #[cfg(feature = "runtime-benchmarks")] + type BenchmarkHelper = (); } impl pallet_balances::Config for Runtime { @@ -171,6 +173,16 @@ impl Contains for InvalidNominees { } } +#[cfg(feature = "runtime-benchmarks")] +pub struct MockBenchmarkHelper; +#[cfg(feature = "runtime-benchmarks")] +impl BenchmarkHelper for MockBenchmarkHelper { + fn setup_homa_bump_era(era: EraIndex) { + MockCurrentEra::set(era); + } + fn setup_homa_validators(_caller: AccountId, _targets: Vec) {} +} + impl Config for Runtime { type Currency = LDOTCurrency; type NomineeId = AccountId; @@ -185,6 +197,8 @@ impl Config for Runtime { type OnUnbonded = MockOnUnbonded; type CurrentEra = MockCurrentEra; type WeightInfo = (); + #[cfg(feature = "runtime-benchmarks")] + type BenchmarkHelper = MockBenchmarkHelper; } type Block = frame_system::mocking::MockBlock; diff --git a/modules/prices/Cargo.toml b/modules/prices/Cargo.toml index 2ca0a02f8..ba122c080 100644 --- a/modules/prices/Cargo.toml +++ b/modules/prices/Cargo.toml @@ -8,6 +8,7 @@ edition = { workspace = true } parity-scale-codec = { workspace = true } scale-info = { workspace = true } sp-runtime = { workspace = true } +frame-benchmarking = { workspace = true, optional = true } frame-support = { workspace = true } frame-system = { workspace = true } sp-core = { workspace = true } @@ -23,6 +24,7 @@ orml-tokens = { workspace = true, features = ["std"] } [features] default = ["std"] std = [ + "frame-benchmarking?/std", "parity-scale-codec/std", "frame-support/std", "frame-system/std", @@ -34,6 +36,12 @@ std = [ "sp-std/std", "module-support/std", ] +runtime-benchmarks = [ + "frame-benchmarking/runtime-benchmarks", + "frame-support/runtime-benchmarks", + "frame-system/runtime-benchmarks", + "sp-runtime/runtime-benchmarks", +] try-runtime = [ "frame-support/try-runtime", "frame-system/try-runtime", diff --git a/modules/prices/src/benchmarking.rs b/modules/prices/src/benchmarking.rs new file mode 100644 index 000000000..f8b07c435 --- /dev/null +++ b/modules/prices/src/benchmarking.rs @@ -0,0 +1,60 @@ +// This file is part of Acala. + +// Copyright (C) 2020-2025 Acala Foundation. +// SPDX-License-Identifier: GPL-3.0-or-later WITH Classpath-exception-2.0 + +// This program 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. + +// This program 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 this program. If not, see . + +use super::*; +use frame_benchmarking::v2::*; +use frame_support::assert_ok; +use frame_system::RawOrigin; + +/// Helper trait for benchmarking. +pub trait BenchmarkHelper { + fn setup_feed_price() -> Option; +} + +impl BenchmarkHelper for () { + fn setup_feed_price() -> Option { + None + } +} + +#[benchmarks] +mod benchmarks { + use super::*; + + #[benchmark] + fn lock_price() { + // feed price + let currency_id = T::BenchmarkHelper::setup_feed_price().unwrap(); + + #[extrinsic_call] + _(RawOrigin::Root, currency_id); + } + + #[benchmark] + fn unlock_price() { + // feed price + let currency_id = T::BenchmarkHelper::setup_feed_price().unwrap(); + + assert_ok!(Pallet::::lock_price(RawOrigin::Root.into(), currency_id)); + + #[extrinsic_call] + _(RawOrigin::Root, currency_id); + } + + impl_benchmark_test_suite!(Pallet, crate::mock::ExtBuilder::default().build(), crate::mock::Runtime); +} diff --git a/modules/prices/src/lib.rs b/modules/prices/src/lib.rs index 1c946dbe9..de995b006 100644 --- a/modules/prices/src/lib.rs +++ b/modules/prices/src/lib.rs @@ -41,10 +41,14 @@ use sp_runtime::{ }; use sp_std::marker::PhantomData; +#[cfg(feature = "runtime-benchmarks")] +mod benchmarking; mod mock; mod tests; pub mod weights; +#[cfg(feature = "runtime-benchmarks")] +pub use benchmarking::BenchmarkHelper; pub use module::*; pub use weights::WeightInfo; @@ -106,6 +110,9 @@ pub mod module { /// Weight information for the extrinsics in this module. type WeightInfo: WeightInfo; + + #[cfg(feature = "runtime-benchmarks")] + type BenchmarkHelper: BenchmarkHelper; } #[pallet::error] @@ -151,7 +158,7 @@ pub mod module { #[pallet::call_index(0)] #[pallet::weight((T::WeightInfo::lock_price(), DispatchClass::Operational))] pub fn lock_price(origin: OriginFor, currency_id: CurrencyId) -> DispatchResult { - T::LockOrigin::ensure_origin(origin)?; + T::LockOrigin::ensure_origin_or_root(origin)?; as LockablePrice>::lock_price(currency_id)?; Ok(()) } @@ -164,7 +171,7 @@ pub mod module { #[pallet::call_index(1)] #[pallet::weight((T::WeightInfo::unlock_price(), DispatchClass::Operational))] pub fn unlock_price(origin: OriginFor, currency_id: CurrencyId) -> DispatchResult { - T::LockOrigin::ensure_origin(origin)?; + T::LockOrigin::ensure_origin_or_root(origin)?; as LockablePrice>::unlock_price(currency_id)?; Ok(()) } diff --git a/modules/prices/src/mock.rs b/modules/prices/src/mock.rs index 2c241e195..98ae6fc47 100644 --- a/modules/prices/src/mock.rs +++ b/modules/prices/src/mock.rs @@ -186,6 +186,8 @@ impl orml_tokens::Config for Runtime { type MaxReserves = (); type ReserveIdentifier = [u8; 8]; type DustRemovalWhitelist = Nothing; + #[cfg(feature = "runtime-benchmarks")] + type BenchmarkHelper = (); } impl BlockNumberProvider for MockRelayBlockNumberProvider { @@ -230,6 +232,15 @@ parameter_types! { pub RewardRatePerRelaychainBlock: Rate = Rate::saturating_from_rational(1, 1000); } +#[cfg(feature = "runtime-benchmarks")] +pub struct MockBenchmarkHelper; +#[cfg(feature = "runtime-benchmarks")] +impl BenchmarkHelper for MockBenchmarkHelper { + fn setup_feed_price() -> Option { + Some(AUSD) + } +} + impl Config for Runtime { type Source = MockDataProvider; type GetStableCurrencyId = GetStableCurrencyId; @@ -246,6 +257,8 @@ impl Config for Runtime { type RewardRatePerRelaychainBlock = RewardRatePerRelaychainBlock; type PricingPegged = PricingPegged; type WeightInfo = (); + #[cfg(feature = "runtime-benchmarks")] + type BenchmarkHelper = MockBenchmarkHelper; } type Block = frame_system::mocking::MockBlock; diff --git a/modules/session-manager/Cargo.toml b/modules/session-manager/Cargo.toml index 1c90fd2a3..add4affb7 100644 --- a/modules/session-manager/Cargo.toml +++ b/modules/session-manager/Cargo.toml @@ -11,6 +11,7 @@ scale-info = { workspace = true } sp-runtime = { workspace = true } sp-std= { workspace = true } sp-staking = { workspace = true } +frame-benchmarking = { workspace = true, optional = true } frame-support = { workspace = true } frame-system = { workspace = true } pallet-session = { workspace = true } @@ -21,6 +22,7 @@ sp-io = { workspace = true, features = ["std"] } [features] default = ["std"] std = [ + "frame-benchmarking?/std", "parity-scale-codec/std", "frame-support/std", "frame-system/std", @@ -31,6 +33,11 @@ std = [ "sp-staking/std", "sp-std/std", ] +runtime-benchmarks = [ + "frame-benchmarking/runtime-benchmarks", + "frame-support/runtime-benchmarks", + "frame-system/runtime-benchmarks", +] try-runtime = [ "frame-support/try-runtime", "frame-system/try-runtime", diff --git a/modules/session-manager/src/benchmarking.rs b/modules/session-manager/src/benchmarking.rs new file mode 100644 index 000000000..ba1de1a39 --- /dev/null +++ b/modules/session-manager/src/benchmarking.rs @@ -0,0 +1,115 @@ +// This file is part of Acala. + +// Copyright (C) 2020-2025 Acala Foundation. +// SPDX-License-Identifier: GPL-3.0-or-later WITH Classpath-exception-2.0 + +// This program 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. + +// This program 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 this program. If not, see . + +use super::*; +use frame_benchmarking::v2::*; +use frame_support::assert_ok; +use frame_system::RawOrigin; + +#[benchmarks] +mod benchmarks { + use super::*; + + #[benchmark] + fn schedule_session_duration() { + let block_number: BlockNumberFor = 10u32.into(); + SessionDuration::::put(block_number); + + frame_system::Pallet::::set_block_number(2u32.into()); + + #[block] + { + assert_ok!(Pallet::::schedule_session_duration( + RawOrigin::Root.into(), + 1u32.into(), + 100u32.into() + )); + } + + frame_system::Pallet::::assert_last_event( + Event::ScheduledSessionDuration { + block_number: block_number, + session_index: 1u32.into(), + session_duration: 100u32.into(), + } + .into(), + ); + } + + #[benchmark] + fn on_initialize_skip() { + let block_number: BlockNumberFor = 10u32.into(); + SessionDuration::::put(block_number); + + frame_system::Pallet::::set_block_number(2u32.into()); + + assert_ok!(Pallet::::schedule_session_duration( + RawOrigin::Root.into(), + 1u32.into(), + 100u32.into() + )); + + #[block] + { + Pallet::::on_initialize(9u32.into()); + } + } + + #[benchmark] + fn on_initialize() { + let block_number: BlockNumberFor = 10u32.into(); + SessionDuration::::put(block_number); + + frame_system::Pallet::::set_block_number(2u32.into()); + + assert_ok!(Pallet::::schedule_session_duration( + RawOrigin::Root.into(), + 1u32.into(), + 100u32.into() + )); + + #[block] + { + Pallet::::on_initialize(10u32.into()); + } + } + + #[benchmark] + fn estimate_current_session_progress() { + let block_number: BlockNumberFor = 10u32.into(); + SessionDuration::::put(block_number); + + #[block] + { + Pallet::::estimate_current_session_progress(10u32.into()); + } + } + + #[benchmark] + fn estimate_next_session_rotation() { + let block_number: BlockNumberFor = 10u32.into(); + SessionDuration::::put(block_number); + + #[block] + { + Pallet::::estimate_next_session_rotation(10u32.into()); + } + } + + impl_benchmark_test_suite!(Pallet, crate::mock::new_test_ext(), crate::mock::Runtime); +} diff --git a/modules/session-manager/src/lib.rs b/modules/session-manager/src/lib.rs index 7b736d0b6..c7780999f 100644 --- a/modules/session-manager/src/lib.rs +++ b/modules/session-manager/src/lib.rs @@ -36,6 +36,8 @@ use sp_runtime::{ }; use sp_staking::SessionIndex; +#[cfg(feature = "runtime-benchmarks")] +mod benchmarking; mod mock; mod tests; pub mod weights; diff --git a/modules/transaction-pause/Cargo.toml b/modules/transaction-pause/Cargo.toml index cc3971a1e..9ed986a32 100644 --- a/modules/transaction-pause/Cargo.toml +++ b/modules/transaction-pause/Cargo.toml @@ -9,6 +9,7 @@ log = { workspace = true } hex-literal = { workspace = true } parity-scale-codec = { workspace = true } scale-info = { workspace = true } +frame-benchmarking = { workspace = true, optional = true } frame-support = { workspace = true } frame-system = { workspace = true } sp-core = { workspace = true } @@ -27,6 +28,7 @@ smallvec = { workspace = true } [features] default = ["std"] std = [ + "frame-benchmarking?/std", "parity-scale-codec/std", "frame-support/std", "frame-system/std", @@ -36,6 +38,12 @@ std = [ "sp-runtime/std", "sp-std/std", ] +runtime-benchmarks = [ + "frame-benchmarking/runtime-benchmarks", + "frame-support/runtime-benchmarks", + "frame-system/runtime-benchmarks", + "sp-runtime/runtime-benchmarks", +] try-runtime = [ "frame-support/try-runtime", "frame-system/try-runtime", diff --git a/modules/transaction-pause/src/benchmarking.rs b/modules/transaction-pause/src/benchmarking.rs new file mode 100644 index 000000000..7e2d96d73 --- /dev/null +++ b/modules/transaction-pause/src/benchmarking.rs @@ -0,0 +1,64 @@ +// This file is part of Acala. + +// Copyright (C) 2020-2025 Acala Foundation. +// SPDX-License-Identifier: GPL-3.0-or-later WITH Classpath-exception-2.0 + +// This program 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. + +// This program 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 this program. If not, see . + +use super::*; +use frame_benchmarking::v2::*; +use frame_support::assert_ok; +use frame_system::RawOrigin; + +#[benchmarks] +mod benchmarks { + use super::*; + + #[benchmark] + fn pause_transaction() { + #[extrinsic_call] + _(RawOrigin::Root, b"Balances".to_vec(), b"transfer".to_vec()); + } + + #[benchmark] + fn unpause_transaction() { + assert_ok!(Pallet::::pause_transaction( + RawOrigin::Root.into(), + b"Balances".to_vec(), + b"transfer".to_vec() + )); + + #[extrinsic_call] + _(RawOrigin::Root, b"Balances".to_vec(), b"transfer".to_vec()); + } + + #[benchmark] + fn pause_evm_precompile() { + #[extrinsic_call] + _(RawOrigin::Root, H160::from_low_u64_be(1)); + } + + #[benchmark] + fn unpause_evm_precompile() { + assert_ok!(Pallet::::pause_evm_precompile( + RawOrigin::Root.into(), + H160::from_low_u64_be(1) + )); + + #[extrinsic_call] + _(RawOrigin::Root, H160::from_low_u64_be(1)); + } + + impl_benchmark_test_suite!(Pallet, crate::mock::ExtBuilder::default().build(), crate::mock::Runtime); +} diff --git a/modules/transaction-pause/src/lib.rs b/modules/transaction-pause/src/lib.rs index 04874fbe2..393b862fb 100644 --- a/modules/transaction-pause/src/lib.rs +++ b/modules/transaction-pause/src/lib.rs @@ -28,6 +28,8 @@ use sp_core::H160; use sp_runtime::DispatchResult; use sp_std::{prelude::*, vec::Vec}; +#[cfg(feature = "runtime-benchmarks")] +mod benchmarking; mod mock; mod tests; pub mod weights; @@ -101,7 +103,7 @@ pub mod module { #[pallet::call_index(0)] #[pallet::weight(T::WeightInfo::pause_transaction())] pub fn pause_transaction(origin: OriginFor, pallet_name: Vec, function_name: Vec) -> DispatchResult { - T::UpdateOrigin::ensure_origin(origin)?; + T::UpdateOrigin::ensure_origin_or_root(origin)?; // not allowed to pause calls of this pallet to ensure safe let pallet_name_string = sp_std::str::from_utf8(&pallet_name).map_err(|_| Error::::InvalidCharacter)?; @@ -129,7 +131,7 @@ pub mod module { pallet_name: Vec, function_name: Vec, ) -> DispatchResult { - T::UpdateOrigin::ensure_origin(origin)?; + T::UpdateOrigin::ensure_origin_or_root(origin)?; if PausedTransactions::::take((&pallet_name, &function_name)).is_some() { Self::deposit_event(Event::TransactionUnpaused { pallet_name_bytes: pallet_name, @@ -142,7 +144,7 @@ pub mod module { #[pallet::call_index(2)] #[pallet::weight(T::WeightInfo::pause_evm_precompile())] pub fn pause_evm_precompile(origin: OriginFor, address: H160) -> DispatchResult { - T::UpdateOrigin::ensure_origin(origin)?; + T::UpdateOrigin::ensure_origin_or_root(origin)?; PausedEvmPrecompiles::::mutate_exists(address, |maybe_paused| { if maybe_paused.is_none() { *maybe_paused = Some(()); @@ -155,7 +157,7 @@ pub mod module { #[pallet::call_index(3)] #[pallet::weight(T::WeightInfo::unpause_evm_precompile())] pub fn unpause_evm_precompile(origin: OriginFor, address: H160) -> DispatchResult { - T::UpdateOrigin::ensure_origin(origin)?; + T::UpdateOrigin::ensure_origin_or_root(origin)?; if PausedEvmPrecompiles::::take(address).is_some() { Self::deposit_event(Event::EvmPrecompileUnpaused { address }); }; diff --git a/modules/transaction-pause/src/mock.rs b/modules/transaction-pause/src/mock.rs index 11a3de1d1..31bdc2bbd 100644 --- a/modules/transaction-pause/src/mock.rs +++ b/modules/transaction-pause/src/mock.rs @@ -80,6 +80,8 @@ impl orml_tokens::Config for Runtime { type MaxReserves = (); type ReserveIdentifier = [u8; 8]; type DustRemovalWhitelist = Nothing; + #[cfg(feature = "runtime-benchmarks")] + type BenchmarkHelper = (); } ord_parameter_types! { diff --git a/modules/transaction-payment/Cargo.toml b/modules/transaction-payment/Cargo.toml index b2339ab9a..575adac3a 100644 --- a/modules/transaction-payment/Cargo.toml +++ b/modules/transaction-payment/Cargo.toml @@ -9,14 +9,17 @@ log = { workspace = true } parity-scale-codec = { workspace = true } scale-info = { workspace = true } sp-runtime = { workspace = true } +frame-benchmarking = { workspace = true, optional = true } frame-support = { workspace = true } frame-system = { workspace = true } sp-std = { workspace = true } sp-io= { workspace = true } +pallet-balances = { workspace = true, optional = true } pallet-transaction-payment = { workspace = true } pallet-transaction-payment-rpc-runtime-api = { workspace = true } primitives = { workspace = true } module-support = { workspace = true } +orml-tokens = { workspace = true, optional = true } orml-traits = { workspace = true } xcm = { workspace = true } @@ -33,6 +36,7 @@ smallvec = { workspace = true } default = ["std"] std = [ "parity-scale-codec/std", + "frame-benchmarking?/std", "frame-support/std", "frame-system/std", "orml-traits/std", @@ -46,6 +50,16 @@ std = [ "module-support/std", "xcm/std", ] +runtime-benchmarks = [ + "frame-benchmarking/runtime-benchmarks", + "frame-support/runtime-benchmarks", + "frame-system/runtime-benchmarks", + "pallet-balances/runtime-benchmarks", + "orml-tokens/runtime-benchmarks", + "module-aggregated-dex/runtime-benchmarks", + "module-currencies/runtime-benchmarks", + "module-dex/runtime-benchmarks", +] try-runtime = [ "frame-support/try-runtime", "frame-system/try-runtime", diff --git a/modules/transaction-payment/src/benchmarking.rs b/modules/transaction-payment/src/benchmarking.rs new file mode 100644 index 000000000..7e330637d --- /dev/null +++ b/modules/transaction-payment/src/benchmarking.rs @@ -0,0 +1,234 @@ +// This file is part of Acala. + +// Copyright (C) 2020-2025 Acala Foundation. +// SPDX-License-Identifier: GPL-3.0-or-later WITH Classpath-exception-2.0 + +// This program 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. + +// This program 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 this program. If not, see . + +use super::*; +use frame_benchmarking::v2::*; +use frame_support::assert_ok; +use frame_system::RawOrigin; +use sp_runtime::traits::Hash; + +/// Helper trait for benchmarking. +pub trait BenchmarkHelper { + fn setup_stable_currency_id() -> Option; + fn setup_liquid_currency_id() -> Option; + fn setup_enable_fee_pool() -> Option<(AccountId, Balance, Balance, Balance)>; + fn setup_enable_stable_asset(); +} + +impl BenchmarkHelper for () { + fn setup_stable_currency_id() -> Option { + None + } + fn setup_liquid_currency_id() -> Option { + None + } + fn setup_enable_fee_pool() -> Option<(AccountId, Balance, Balance, Balance)> { + None + } + fn setup_enable_stable_asset() {} +} + +#[benchmarks( + where T: Config + pallet_balances::Config> + orml_tokens::Config>, + ::RuntimeCall: From> +)] +mod benchmarks { + use super::*; + + #[benchmark] + fn set_alternative_fee_swap_path() { + let caller: T::AccountId = account("caller", 0, 0); + let stable_currency_id = ::BenchmarkHelper::setup_stable_currency_id().unwrap(); + + let native_ed = pallet_balances::Pallet::::minimum_balance(); + let _ = T::Currency::deposit_creating(&caller, native_ed.saturating_mul(T::AlternativeFeeSwapDeposit::get())); + + #[extrinsic_call] + _( + RawOrigin::Signed(caller.clone()), + Some(vec![stable_currency_id, T::NativeCurrencyId::get()]), + ); + + assert_eq!( + Pallet::::alternative_fee_swap_path(&caller).unwrap().into_inner(), + vec![stable_currency_id, T::NativeCurrencyId::get()] + ); + } + + #[benchmark] + fn enable_charge_fee_pool() { + let stable_currency_id = ::BenchmarkHelper::setup_stable_currency_id().unwrap(); + + let (sub_account, stable_ed, pool_size, swap_threshold) = + ::BenchmarkHelper::setup_enable_fee_pool().unwrap(); + + #[extrinsic_call] + _(RawOrigin::Root, stable_currency_id, pool_size, swap_threshold); + + let exchange_rate = Pallet::::token_exchange_rate(stable_currency_id).unwrap(); + assert_eq!(Pallet::::pool_size(stable_currency_id), pool_size); + assert!(Pallet::::token_exchange_rate(stable_currency_id).is_some()); + assert_eq!( + orml_tokens::Pallet::::free_balance(stable_currency_id, &sub_account), + stable_ed + ); + assert_eq!(pallet_balances::Pallet::::free_balance(&sub_account), pool_size); + + frame_system::Pallet::::assert_has_event( + Event::ChargeFeePoolEnabled { + sub_account, + currency_id: stable_currency_id, + exchange_rate, + pool_size, + swap_threshold, + } + .into(), + ); + } + + #[benchmark] + fn disable_charge_fee_pool() { + let stable_currency_id = ::BenchmarkHelper::setup_stable_currency_id().unwrap(); + + let (_sub_account, stable_ed, pool_size, swap_threshold) = + ::BenchmarkHelper::setup_enable_fee_pool().unwrap(); + + assert_ok!(Pallet::::enable_charge_fee_pool( + RawOrigin::Root.into(), + stable_currency_id, + pool_size, + swap_threshold + )); + + #[extrinsic_call] + _(RawOrigin::Root, stable_currency_id); + + frame_system::Pallet::::assert_has_event( + Event::ChargeFeePoolDisabled { + currency_id: stable_currency_id, + foreign_amount: stable_ed, + native_amount: pool_size, + } + .into(), + ); + } + + #[benchmark] + fn with_fee_path() { + frame_system::Pallet::::set_block_number(1u32.into()); + let stable_currency_id = ::BenchmarkHelper::setup_stable_currency_id().unwrap(); + + let caller: T::AccountId = account("caller", 0, 0); + let call = Box::new(frame_system::Call::remark_with_event { remark: vec![] }.into()); + + let fee_swap_path: Vec = vec![stable_currency_id, T::NativeCurrencyId::get()]; + + #[extrinsic_call] + _(RawOrigin::Signed(caller.clone()), fee_swap_path.clone(), call); + + frame_system::Pallet::::assert_has_event( + frame_system::Event::::Remarked { + sender: caller, + hash: T::Hashing::hash(&vec![]), + } + .into(), + ); + } + + #[benchmark] + fn with_fee_currency() { + frame_system::Pallet::::set_block_number(1u32.into()); + let stable_currency_id = ::BenchmarkHelper::setup_stable_currency_id().unwrap(); + + let caller: T::AccountId = account("caller", 0, 0); + let call = Box::new(frame_system::Call::remark_with_event { remark: vec![] }.into()); + + let (sub_account, _stable_ed, pool_size, swap_threshold) = + ::BenchmarkHelper::setup_enable_fee_pool().unwrap(); + assert_ok!(Pallet::::enable_charge_fee_pool( + RawOrigin::Root.into(), + stable_currency_id, + pool_size, + swap_threshold + )); + + let exchange_rate = Pallet::::token_exchange_rate(stable_currency_id).unwrap(); + + frame_system::Pallet::::assert_has_event( + Event::ChargeFeePoolEnabled { + sub_account, + currency_id: stable_currency_id, + exchange_rate, + pool_size, + swap_threshold, + } + .into(), + ); + + #[extrinsic_call] + _(RawOrigin::Signed(caller.clone()), stable_currency_id, call); + + frame_system::Pallet::::assert_has_event( + frame_system::Event::::Remarked { + sender: caller, + hash: T::Hashing::hash(&vec![]), + } + .into(), + ); + } + + #[benchmark] + fn with_fee_aggregated_path() { + frame_system::Pallet::::set_block_number(1u32.into()); + let liquid_currency_id = ::BenchmarkHelper::setup_liquid_currency_id().unwrap(); + + let caller: T::AccountId = account("caller", 0, 0); + let call = Box::new(frame_system::Call::remark_with_event { remark: vec![] }.into()); + // set_balance(STAKING, &caller, 100 * dollar(STAKING)); + // set_balance(NATIVE, &caller, 100 * dollar(NATIVE)); + + ::BenchmarkHelper::setup_enable_stable_asset(); + + // Taiga(STAKING, LIQUID), Dex(LIQUID, NATIVE) + let fee_aggregated_path = vec![ + AggregatedSwapPath::::Taiga(0, 0, 1), + AggregatedSwapPath::::Dex(vec![liquid_currency_id, T::NativeCurrencyId::get()]), + ]; + + #[extrinsic_call] + _(RawOrigin::Signed(caller.clone()), fee_aggregated_path, call); + + frame_system::Pallet::::assert_has_event( + frame_system::Event::::Remarked { + sender: caller, + hash: T::Hashing::hash(&vec![]), + } + .into(), + ); + } + + #[benchmark] + fn on_finalize() { + #[block] + { + Pallet::::on_finalize(frame_system::Pallet::::block_number()); + } + } + + impl_benchmark_test_suite!(Pallet, crate::mock::ExtBuilder::default().build(), crate::mock::Runtime); +} diff --git a/modules/transaction-payment/src/lib.rs b/modules/transaction-payment/src/lib.rs index c9daace85..ea943a159 100644 --- a/modules/transaction-payment/src/lib.rs +++ b/modules/transaction-payment/src/lib.rs @@ -60,10 +60,14 @@ use xcm::v5::prelude::Location; const LOG_TARGET: &str = "transaction-payment"; +#[cfg(feature = "runtime-benchmarks")] +mod benchmarking; mod mock; mod tests; pub mod weights; +#[cfg(feature = "runtime-benchmarks")] +pub use benchmarking::BenchmarkHelper; pub use module::*; pub use weights::WeightInfo; @@ -392,6 +396,9 @@ pub mod module { /// The origin which change swap balance threshold or enable charge fee pool. type UpdateOrigin: EnsureOrigin; + + #[cfg(feature = "runtime-benchmarks")] + type BenchmarkHelper: BenchmarkHelper; } #[pallet::type_value] @@ -605,7 +612,7 @@ pub mod module { pool_size: Balance, swap_threshold: Balance, ) -> DispatchResult { - T::UpdateOrigin::ensure_origin(origin)?; + T::UpdateOrigin::ensure_origin_or_root(origin)?; Self::initialize_pool(currency_id, pool_size, swap_threshold) } @@ -613,7 +620,7 @@ pub mod module { #[pallet::call_index(2)] #[pallet::weight(::WeightInfo::disable_charge_fee_pool())] pub fn disable_charge_fee_pool(origin: OriginFor, currency_id: CurrencyId) -> DispatchResult { - T::UpdateOrigin::ensure_origin(origin)?; + T::UpdateOrigin::ensure_origin_or_root(origin)?; Self::disable_pool(currency_id) } diff --git a/modules/transaction-payment/src/mock.rs b/modules/transaction-payment/src/mock.rs index 25d2b859c..dcf5becd5 100644 --- a/modules/transaction-payment/src/mock.rs +++ b/modules/transaction-payment/src/mock.rs @@ -23,7 +23,7 @@ use super::*; pub use crate as transaction_payment; use frame_support::{ - construct_runtime, derive_impl, ord_parameter_types, parameter_types, + assert_ok, construct_runtime, derive_impl, ord_parameter_types, parameter_types, traits::{ConstU128, ConstU32, ConstU64, Nothing}, weights::{WeightToFee as WeightToFeeT, WeightToFeeCoefficients, WeightToFeePolynomial}, PalletId, @@ -38,7 +38,7 @@ use primitives::{Amount, ReserveIdentifier, TokenSymbol, TradingPair}; use smallvec::smallvec; use sp_core::{crypto::AccountId32, H160}; use sp_runtime::{ - traits::{AccountIdConversion, IdentityLookup, One}, + traits::{AccountIdConversion, IdentityLookup, One, UniqueSaturatedInto}, BuildStorage, Perbill, }; @@ -103,6 +103,8 @@ impl orml_tokens::Config for Runtime { type MaxReserves = (); type ReserveIdentifier = [u8; 8]; type DustRemovalWhitelist = Nothing; + #[cfg(feature = "runtime-benchmarks")] + type BenchmarkHelper = (); } impl pallet_balances::Config for Runtime { @@ -140,6 +142,8 @@ impl module_currencies::Config for Runtime { type GasToWeight = (); type SweepOrigin = EnsureSignedBy; type OnDust = (); + #[cfg(feature = "runtime-benchmarks")] + type BenchmarkHelper = (); } ord_parameter_types! { @@ -169,6 +173,8 @@ impl module_dex::Config for Runtime { type ListingOrigin = EnsureSignedBy; type ExtendedProvisioningBlocks = ConstU64<0>; type OnLiquidityPoolUpdated = (); + #[cfg(feature = "runtime-benchmarks")] + type BenchmarkHelper = (); } impl module_aggregated_dex::Config for Runtime { @@ -178,6 +184,8 @@ impl module_aggregated_dex::Config for Runtime { type DexSwapJointList = AlternativeSwapPathJointList; type SwapPathLimit = ConstU32<3>; type WeightInfo = (); + #[cfg(feature = "runtime-benchmarks")] + type BenchmarkHelper = (); } parameter_types! { @@ -253,6 +261,66 @@ impl WeightToFeeT for TransactionByteFee { } } +#[cfg(feature = "runtime-benchmarks")] +pub struct MockBenchmarkHelper; +#[cfg(feature = "runtime-benchmarks")] +impl BenchmarkHelper for MockBenchmarkHelper { + fn setup_stable_currency_id() -> Option { + Some(AUSD) + } + fn setup_liquid_currency_id() -> Option { + Some(LDOT) + } + fn setup_enable_fee_pool() -> Option<(AccountId, Balance, Balance, Balance)> { + let treasury_account: AccountId = TreasuryPalletId::get().into_account_truncating(); + let sub_account: AccountId = TransactionPaymentPalletId::get().into_sub_account_truncating(AUSD); + let native_ed = PalletBalances::minimum_balance(); + let stable_ed: Balance = >::minimum_balance(AUSD); + let pool_size: Balance = native_ed * 50; + let swap_threshold: Balance = native_ed * 2; + + assert_ok!(Currencies::update_balance( + RuntimeOrigin::root(), + treasury_account.clone(), + ACA, + (pool_size * 10).unique_saturated_into(), + )); + assert_ok!(Currencies::update_balance( + RuntimeOrigin::root(), + treasury_account.clone(), + AUSD, + (stable_ed * 10).unique_saturated_into(), + )); + + assert_ok!(Currencies::update_balance( + RuntimeOrigin::root(), + ALICE, + ACA, + 100000.unique_saturated_into(), + )); + assert_ok!(Currencies::update_balance( + RuntimeOrigin::root(), + ALICE, + AUSD, + 100000.unique_saturated_into(), + )); + + // enable dex + assert_ok!(DEXModule::add_liquidity( + RuntimeOrigin::signed(ALICE), + AUSD, + ACA, + 1000, + 10000, + 0, + false + )); + + Some((sub_account, stable_ed, pool_size, swap_threshold)) + } + fn setup_enable_stable_asset() {} +} + impl Config for Runtime { type RuntimeCall = RuntimeCall; type NativeCurrencyId = GetNativeCurrencyId; @@ -277,6 +345,8 @@ impl Config for Runtime { type CustomFeeSurplus = CustomFeeSurplus; type AlternativeFeeSurplus = AlternativeFeeSurplus; type DefaultFeeTokens = DefaultFeeTokens; + #[cfg(feature = "runtime-benchmarks")] + type BenchmarkHelper = MockBenchmarkHelper; } parameter_types! { diff --git a/orml b/orml index 0069a63c2..04eb581ec 160000 --- a/orml +++ b/orml @@ -1 +1 @@ -Subproject commit 0069a63c2b0b553be16c0bdfeacbd7a12236c620 +Subproject commit 04eb581ec2e116584b2ddc033313c2e6f1382c52 diff --git a/runtime/acala/Cargo.toml b/runtime/acala/Cargo.toml index f106d7acc..6ae846deb 100644 --- a/runtime/acala/Cargo.toml +++ b/runtime/acala/Cargo.toml @@ -54,6 +54,7 @@ sp-runtime = { workspace = true } sp-session = { workspace = true } sp-staking = { workspace = true } sp-std = { workspace = true } +sp-storage = { workspace = true } sp-transaction-pool = { workspace = true } sp-version = { workspace = true } @@ -81,7 +82,6 @@ parachains-common = { workspace = true } # orml orml-auction = { workspace = true } orml-authority = { workspace = true } -orml-benchmarking = { workspace = true, optional = true } orml-nft = { workspace = true } orml-oracle = { workspace = true } orml-oracle-runtime-api = { workspace = true } @@ -198,6 +198,7 @@ std = [ "sp-session/std", "sp-staking/std", "sp-std/std", + "sp-storage/std", "sp-transaction-pool/std", "sp-version/std", "substrate-wasm-builder", @@ -222,7 +223,6 @@ std = [ "orml-auction/std", "orml-authority/std", - "orml-benchmarking/std", "orml-nft/std", "orml-oracle-runtime-api/std", "orml-oracle/std", @@ -276,7 +276,6 @@ std = [ "nutsfinance-stable-asset/std", ] runtime-benchmarks = [ - "orml-benchmarking", "libsecp256k1/hmac", "frame-benchmarking/runtime-benchmarks", @@ -309,6 +308,7 @@ runtime-benchmarks = [ "xcm-builder/runtime-benchmarks", "xcm-executor/runtime-benchmarks", + "orml-auction/runtime-benchmarks", "orml-authority/runtime-benchmarks", "orml-oracle/runtime-benchmarks", "orml-parameters/runtime-benchmarks", @@ -316,11 +316,32 @@ runtime-benchmarks = [ "orml-vesting/runtime-benchmarks", "orml-xtokens/runtime-benchmarks", + "module-aggregated-dex/runtime-benchmarks", + "module-asset-registry/runtime-benchmarks", + "module-auction-manager/runtime-benchmarks", + "module-cdp-engine/runtime-benchmarks", + "module-cdp-treasury/runtime-benchmarks", "module-collator-selection/runtime-benchmarks", + "module-currencies/runtime-benchmarks", + "module-dex-oracle/runtime-benchmarks", + "module-dex/runtime-benchmarks", + "module-earning/runtime-benchmarks", + "module-emergency-shutdown/runtime-benchmarks", "module-evm-accounts/runtime-benchmarks", "module-evm-bridge/runtime-benchmarks", + "module-evm/runtime-benchmarks", "module-homa/runtime-benchmarks", + "module-homa-validator-list/runtime-benchmarks", + "module-honzon/runtime-benchmarks", + "module-idle-scheduler/runtime-benchmarks", + "module-incentives/runtime-benchmarks", + "module-liquid-crowdloan/runtime-benchmarks", "module-nft/runtime-benchmarks", + "module-nominees-election/runtime-benchmarks", + "module-prices/runtime-benchmarks", + "module-session-manager/runtime-benchmarks", + "module-transaction-pause/runtime-benchmarks", + "module-transaction-payment/runtime-benchmarks", "module-xcm-interface/runtime-benchmarks", "nutsfinance-stable-asset/runtime-benchmarks", diff --git a/runtime/acala/src/benchmarking/mod.rs b/runtime/acala/src/benchmarking/mod.rs deleted file mode 100644 index e1ae982e2..000000000 --- a/runtime/acala/src/benchmarking/mod.rs +++ /dev/null @@ -1,123 +0,0 @@ -// This file is part of Acala. - -// Copyright (C) 2020-2025 Acala Foundation. -// SPDX-License-Identifier: GPL-3.0-or-later WITH Classpath-exception-2.0 - -// This program 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. - -// This program 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 this program. If not, see . - -#![cfg(feature = "runtime-benchmarks")] - -use super::{CurrencyId, ACA, DOT, LCDOT, LDOT}; -use sp_std::prelude::*; - -pub mod utils { - include!("../../../mandala/src/benchmarking/utils.rs"); -} - -// module benchmarking -pub mod asset_registry { - include!("../../../mandala/src/benchmarking/asset_registry.rs"); -} -pub mod auction_manager { - include!("../../../mandala/src/benchmarking/auction_manager.rs"); -} -pub mod cdp_engine { - include!("../../../mandala/src/benchmarking/cdp_engine.rs"); -} -pub mod cdp_treasury { - include!("../../../mandala/src/benchmarking/cdp_treasury.rs"); -} -pub mod collator_selection { - include!("../../../mandala/src/benchmarking/collator_selection.rs"); -} -pub mod currencies { - include!("../../../mandala/src/benchmarking/currencies.rs"); -} -pub mod dex { - include!("../../../mandala/src/benchmarking/dex.rs"); -} -pub mod dex_oracle { - include!("../../../mandala/src/benchmarking/dex_oracle.rs"); -} -pub mod emergency_shutdown { - include!("../../../mandala/src/benchmarking/emergency_shutdown.rs"); -} -pub mod evm { - include!("../../../mandala/src/benchmarking/evm.rs"); -} -pub mod evm_accounts { - include!("../../../mandala/src/benchmarking/evm_accounts.rs"); -} -pub mod homa { - include!("../../../mandala/src/benchmarking/homa.rs"); -} -pub mod homa_validator_list { - include!("../../../mandala/src/benchmarking/homa_validator_list.rs"); -} -pub mod honzon { - include!("../../../mandala/src/benchmarking/honzon.rs"); -} -pub mod idle_scheduler { - include!("../../../mandala/src/benchmarking/idle_scheduler.rs"); -} -pub mod incentives { - include!("../../../mandala/src/benchmarking/incentives.rs"); -} -pub mod prices { - include!("../../../mandala/src/benchmarking/prices.rs"); -} -pub mod transaction_pause { - include!("../../../mandala/src/benchmarking/transaction_pause.rs"); -} -pub mod transaction_payment { - include!("../../../mandala/src/benchmarking/transaction_payment.rs"); -} -pub mod session_manager { - include!("../../../mandala/src/benchmarking/session_manager.rs"); -} -pub mod liquid_crowdloan { - include!("../../../mandala/src/benchmarking/liquid_crowdloan.rs"); -} - -pub mod nutsfinance_stable_asset { - include!("../../../mandala/src/benchmarking/nutsfinance_stable_asset.rs"); -} -pub mod aggregated_dex { - include!("../../../mandala/src/benchmarking/aggregated_dex.rs"); -} -pub mod nominees_election { - include!("../../../mandala/src/benchmarking/nominees_election.rs"); -} - -// orml benchmarking -pub mod auction { - include!("../../../mandala/src/benchmarking/auction.rs"); -} -pub mod authority { - include!("../../../mandala/src/benchmarking/authority.rs"); -} -pub mod tokens { - include!("../../../mandala/src/benchmarking/tokens.rs"); -} -pub mod vesting { - include!("../../../mandala/src/benchmarking/vesting.rs"); -} - -pub fn get_vesting_account() -> super::AccountId { - super::AcalaFoundationAccounts::get()[0].clone() -} - -pub fn get_benchmarking_collateral_currency_ids() -> Vec { - vec![ACA, DOT, LCDOT, LDOT] -} diff --git a/runtime/acala/src/benchmarks.rs b/runtime/acala/src/benchmarks.rs new file mode 100644 index 000000000..159d25241 --- /dev/null +++ b/runtime/acala/src/benchmarks.rs @@ -0,0 +1,32 @@ +// This file is part of Acala. + +// Copyright (C) 2020-2025 Acala Foundation. +// SPDX-License-Identifier: GPL-3.0-or-later WITH Classpath-exception-2.0 + +// This program 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. + +// This program 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 this program. If not, see . + +use crate::{AcalaFoundationAccounts, AccountId, CurrencyId, ACA, AUSD, DOT, LCDOT, LDOT}; +use sp_std::{vec, vec::Vec}; + +pub mod common { + include!("../../mandala/src/benchmark_common.rs"); +} + +pub fn get_vesting_account() -> AccountId { + AcalaFoundationAccounts::get()[0].clone() +} + +pub fn get_benchmarking_collateral_currency_ids() -> Vec { + vec![ACA, AUSD, DOT, LCDOT, LDOT] +} diff --git a/runtime/acala/src/genesis_config_presets.rs b/runtime/acala/src/genesis_config_presets.rs index e78288e08..96ca9bab8 100644 --- a/runtime/acala/src/genesis_config_presets.rs +++ b/runtime/acala/src/genesis_config_presets.rs @@ -18,7 +18,8 @@ use crate::{ AccountId, BalancesConfig, CollatorSelectionConfig, NativeTokenExistentialDeposit, ParachainInfoConfig, - PolkadotXcmConfig, RuntimeGenesisConfig, SessionConfig, SessionKeys, SudoConfig, XCM_VERSION, + PolkadotXcmConfig, RuntimeGenesisConfig, SessionConfig, SessionDuration, SessionKeys, SessionManagerConfig, + SudoConfig, }; use alloc::{vec, vec::Vec}; @@ -78,6 +79,9 @@ fn testnet_genesis( safe_xcm_version: Some(SAFE_XCM_VERSION) }, sudo: SudoConfig { key: Some(root) }, + session_manager: SessionManagerConfig { + session_duration: SessionDuration::get(), + } }) } diff --git a/runtime/acala/src/lib.rs b/runtime/acala/src/lib.rs index e54591262..7f33598a8 100644 --- a/runtime/acala/src/lib.rs +++ b/runtime/acala/src/lib.rs @@ -126,7 +126,8 @@ use runtime_common::{ }; mod authority; -mod benchmarking; +#[cfg(feature = "runtime-benchmarks")] +mod benchmarks; pub mod constants; #[cfg(feature = "genesis-builder")] mod genesis_config_presets; @@ -758,6 +759,8 @@ impl orml_auction::Config for Runtime { type AuctionId = AuctionId; type Handler = AuctionManager; type WeightInfo = weights::orml_auction::WeightInfo; + #[cfg(feature = "runtime-benchmarks")] + type BenchmarkHelper = benchmarks::common::BenchmarkHelper; } impl orml_authority::Config for Runtime { @@ -768,6 +771,8 @@ impl orml_authority::Config for Runtime { type AsOriginId = AuthoritysOriginId; type AuthorityConfig = AuthorityConfigImpl; type WeightInfo = weights::orml_authority::WeightInfo; + #[cfg(feature = "runtime-benchmarks")] + type BenchmarkHelper = benchmarks::common::BenchmarkHelper; } parameter_types! { @@ -777,15 +782,6 @@ parameter_types! { pub const MaxFeedValues: u32 = 10; // max 10 values allowed to feed in one call. } -#[cfg(feature = "runtime-benchmarks")] -pub struct BenchmarkHelper; -#[cfg(feature = "runtime-benchmarks")] -impl orml_oracle::BenchmarkHelper for BenchmarkHelper { - fn get_currency_id_value_pairs() -> sp_runtime::BoundedVec<(CurrencyId, Price), MaxFeedValues> { - sp_runtime::BoundedVec::default() - } -} - type AcalaDataProvider = orml_oracle::Instance1; impl orml_oracle::Config for Runtime { type OnNewData = (); @@ -799,7 +795,7 @@ impl orml_oracle::Config for Runtime { type WeightInfo = weights::orml_oracle::WeightInfo; type MaxFeedValues = MaxFeedValues; #[cfg(feature = "runtime-benchmarks")] - type BenchmarkHelper = BenchmarkHelper; + type BenchmarkHelper = benchmarks::common::BenchmarkInstanceHelper; } create_median_value_data_provider!( @@ -888,6 +884,8 @@ impl orml_tokens::Config for Runtime { type MaxReserves = MaxReserves; type ReserveIdentifier = ReserveIdentifier; type DustRemovalWhitelist = DustRemovalWhitelist; + #[cfg(feature = "runtime-benchmarks")] + type BenchmarkHelper = benchmarks::common::BenchmarkHelper; } parameter_type_with_key! { @@ -930,6 +928,8 @@ impl module_prices::Config for Runtime { type RewardRatePerRelaychainBlock = RewardRatePerRelaychainBlock; type PricingPegged = PricingPegged; type WeightInfo = weights::module_prices::WeightInfo; + #[cfg(feature = "runtime-benchmarks")] + type BenchmarkHelper = benchmarks::common::BenchmarkHelper; } parameter_types! { @@ -951,6 +951,8 @@ impl module_currencies::Config for Runtime { type GasToWeight = GasToWeight; type SweepOrigin = EnsureRootOrOneGeneralCouncil; type OnDust = module_currencies::TransferDust; + #[cfg(feature = "runtime-benchmarks")] + type BenchmarkHelper = benchmarks::common::BenchmarkHelper; } parameter_types! { @@ -995,6 +997,8 @@ impl orml_vesting::Config for Runtime { type WeightInfo = weights::orml_vesting::WeightInfo; type MaxVestingSchedules = ConstU32<100>; type BlockNumberProvider = RelaychainDataProvider; + #[cfg(feature = "runtime-benchmarks")] + type BenchmarkHelper = benchmarks::common::BenchmarkHelper; } parameter_types! { @@ -1052,6 +1056,8 @@ impl module_auction_manager::Config for Runtime { type UnsignedPriority = runtime_common::AuctionManagerUnsignedPriority; type EmergencyShutdown = EmergencyShutdown; type WeightInfo = weights::module_auction_manager::WeightInfo; + #[cfg(feature = "runtime-benchmarks")] + type BenchmarkHelper = benchmarks::common::BenchmarkHelper; } impl module_loans::Config for Runtime { @@ -1178,6 +1184,8 @@ impl module_cdp_engine::Config for Runtime { type EVMBridge = module_evm_bridge::EVMBridge; type SettleErc20EvmOrigin = SettleErc20EvmOrigin; type WeightInfo = weights::module_cdp_engine::WeightInfo; + #[cfg(feature = "runtime-benchmarks")] + type BenchmarkHelper = benchmarks::common::BenchmarkHelper; } parameter_types! { @@ -1189,6 +1197,8 @@ impl module_honzon::Config for Runtime { type DepositPerAuthorization = DepositPerAuthorization; type CollateralCurrencyIds = CollateralCurrencyIds; type WeightInfo = weights::module_honzon::WeightInfo; + #[cfg(feature = "runtime-benchmarks")] + type BenchmarkHelper = benchmarks::common::BenchmarkHelper; } impl module_emergency_shutdown::Config for Runtime { @@ -1198,6 +1208,8 @@ impl module_emergency_shutdown::Config for Runtime { type AuctionManagerHandler = AuctionManager; type ShutdownOrigin = EnsureRoot; type WeightInfo = weights::module_emergency_shutdown::WeightInfo; + #[cfg(feature = "runtime-benchmarks")] + type BenchmarkHelper = benchmarks::common::BenchmarkHelper; } parameter_types! { @@ -1218,6 +1230,8 @@ impl module_dex::Config for Runtime { type ListingOrigin = EnsureRootOrHalfGeneralCouncil; type ExtendedProvisioningBlocks = ExtendedProvisioningBlocks; type OnLiquidityPoolUpdated = (); + #[cfg(feature = "runtime-benchmarks")] + type BenchmarkHelper = benchmarks::common::BenchmarkHelper; } impl module_aggregated_dex::Config for Runtime { @@ -1227,6 +1241,8 @@ impl module_aggregated_dex::Config for Runtime { type DexSwapJointList = AlternativeSwapPathJointList; type SwapPathLimit = ConstU32<3>; type WeightInfo = weights::module_aggregated_dex::WeightInfo; + #[cfg(feature = "runtime-benchmarks")] + type BenchmarkHelper = benchmarks::common::BenchmarkHelper; } pub type RebasedStableAsset = module_support::RebasedStableAsset< @@ -1242,6 +1258,8 @@ impl module_dex_oracle::Config for Runtime { type Time = Timestamp; type UpdateOrigin = EnsureRootOrHalfGeneralCouncil; type WeightInfo = weights::module_dex_oracle::WeightInfo; + #[cfg(feature = "runtime-benchmarks")] + type BenchmarkHelper = benchmarks::common::BenchmarkHelper; } parameter_types! { @@ -1266,6 +1284,8 @@ impl module_cdp_treasury::Config for Runtime { type TreasuryAccount = HonzonTreasuryAccount; type WeightInfo = weights::module_cdp_treasury::WeightInfo; type StableAsset = RebasedStableAsset; + #[cfg(feature = "runtime-benchmarks")] + type BenchmarkHelper = benchmarks::common::BenchmarkHelper; } impl module_transaction_pause::Config for Runtime { @@ -1320,6 +1340,8 @@ impl module_transaction_payment::Config for Runtime { type CustomFeeSurplus = CustomFeeSurplus; type AlternativeFeeSurplus = AlternativeFeeSurplus; type DefaultFeeTokens = DefaultFeeTokens; + #[cfg(feature = "runtime-benchmarks")] + type BenchmarkHelper = benchmarks::common::BenchmarkHelper; } impl module_evm_accounts::Config for Runtime { @@ -1336,6 +1358,8 @@ impl module_asset_registry::Config for Runtime { type EVMBridge = module_evm_bridge::EVMBridge; type RegisterOrigin = EnsureRootOrHalfGeneralCouncil; type WeightInfo = weights::module_asset_registry::WeightInfo; + #[cfg(feature = "runtime-benchmarks")] + type BenchmarkHelper = benchmarks::common::BenchmarkHelper; } parameter_type_with_key! { @@ -1377,6 +1401,8 @@ impl module_incentives::Config for Runtime { type EmergencyShutdown = EmergencyShutdown; type PalletId = IncentivesPalletId; type WeightInfo = weights::module_incentives::WeightInfo; + #[cfg(feature = "runtime-benchmarks")] + type BenchmarkHelper = benchmarks::common::BenchmarkHelper; } parameter_types! { @@ -1645,6 +1671,8 @@ impl module_homa_validator_list::Config for Runtime { type LiquidStakingExchangeRateProvider = Homa; type CurrentEra = Homa; type WeightInfo = weights::module_homa_validator_list::WeightInfo; + #[cfg(feature = "runtime-benchmarks")] + type BenchmarkHelper = benchmarks::common::BenchmarkHelper; } parameter_types! { @@ -1666,6 +1694,8 @@ impl module_nominees_election::Config for Runtime { type OnUnbonded = module_incentives::OnNomineesElectionUnbonded; type CurrentEra = Homa; type WeightInfo = weights::module_nominees_election::WeightInfo; + #[cfg(feature = "runtime-benchmarks")] + type BenchmarkHelper = benchmarks::common::BenchmarkHelper; } pub struct SubAccountIndexAccountIdConvertor; @@ -1737,6 +1767,8 @@ impl module_idle_scheduler::Config for Runtime { // Number of relay chain blocks produced with no parachain blocks finalized, // once this number is reached idle scheduler is disabled as block production is slow type DisableBlockThreshold = ConstU32<6>; + #[cfg(feature = "runtime-benchmarks")] + type BenchmarkHelper = benchmarks::common::BenchmarkHelper; } pub struct EnsurePoolAssetId; @@ -1798,6 +1830,8 @@ impl nutsfinance_stable_asset::Config for Runtime { type WeightInfo = weights::nutsfinance_stable_asset::WeightInfo; type ListingOrigin = EnsureRootOrHalfGeneralCouncil; type EnsurePoolAssetId = EnsurePoolAssetId; + #[cfg(feature = "runtime-benchmarks")] + type BenchmarkHelper = benchmarks::common::BenchmarkHelper; } parameter_types!( @@ -1830,6 +1864,8 @@ impl module_earning::Config for Runtime { type MaxUnbondingChunks = ConstU32<10>; type LockIdentifier = EarningLockIdentifier; type WeightInfo = (); + #[cfg(feature = "runtime-benchmarks")] + type BenchmarkHelper = benchmarks::common::BenchmarkHelper; } define_aggregrated_parameters! { @@ -2007,49 +2043,55 @@ parameter_types! { #[allow(unused_parens)] type Migrations = (frame_support::migrations::RemoveStorage,); -#[cfg(feature = "runtime-benchmarks")] -#[macro_use] -extern crate orml_benchmarking; - #[cfg(feature = "runtime-benchmarks")] mod benches { - define_benchmarks!( - [module_dex, benchmarking::dex] - [module_dex_oracle, benchmarking::dex_oracle] - [module_asset_registry, benchmarking::asset_registry] - [module_auction_manager, benchmarking::auction_manager] - [module_cdp_engine, benchmarking::cdp_engine] - [module_emergency_shutdown, benchmarking::emergency_shutdown] - [module_evm, benchmarking::evm] - [module_homa, benchmarking::homa] - [module_homa_validator_list, benchmarking::homa_validator_list] - [module_honzon, benchmarking::honzon] - [module_cdp_treasury, benchmarking::cdp_treasury] - [module_collator_selection, benchmarking::collator_selection] - [module_transaction_pause, benchmarking::transaction_pause] - [module_transaction_payment, benchmarking::transaction_payment] - [module_incentives, benchmarking::incentives] - [module_prices, benchmarking::prices] - [module_evm_accounts, benchmarking::evm_accounts] - [module_currencies, benchmarking::currencies] - [module_session_manager, benchmarking::session_manager] - [orml_tokens, benchmarking::tokens] - [orml_vesting, benchmarking::vesting] - [orml_auction, benchmarking::auction] - [orml_authority, benchmarking::authority] - [nutsfinance_stable_asset, benchmarking::nutsfinance_stable_asset] - [module_idle_scheduler, benchmarking::idle_scheduler] - [module_aggregated_dex, benchmarking::aggregated_dex] - [module_liquid_crowdloan, benchmarking::liquid_crowdloan] - [module_nominees_election, benchmarking::nominees_election] + frame_benchmarking::define_benchmarks!( + [module_aggregated_dex, AggregatedDex] + [module_asset_registry, AssetRegistry] + [module_auction_manager, AuctionManager] + [module_cdp_engine, CdpEngine] + [module_cdp_treasury, CdpTreasury] + [module_collator_selection, CollatorSelection] + [module_currencies, Currencies] + [module_dex, Dex] + [module_dex_oracle, DexOracle] + [module_emergency_shutdown, EmergencyShutdown] + [module_evm, EVM] + [module_evm_accounts, EvmAccounts] + [module_homa, Homa] + [module_homa_validator_list, HomaValidatorList] + [module_honzon, Honzon] + [module_idle_scheduler, IdleScheduler] + [module_incentives, Incentives] + [module_liquid_crowdloan, LiquidCrowdloan] + [module_nft, NFT] + [module_nominees_election, NomineesElection] + [module_prices, Prices] + [module_session_manager, SessionManager] + [module_transaction_pause, TransactionPause] + [module_transaction_payment, TransactionPayment] + [orml_auction, Auction] + [orml_authority, Authority] + [orml_oracle, AcalaOracle] + [orml_tokens, Tokens] + [orml_vesting, Vesting] + // Acala Ecosystem Modules + [nutsfinance_stable_asset, StableAsset] + // XCM + // [pallet_xcm, PalletXcmExtrinsicsBenchmark::] ); - // frame_benchmarking::define_benchmarks!( - // // XCM - // [pallet_xcm, PalletXcmExtrinsicsBenchmark::] - // // TODO: add oracle - // ); + + pub use frame_benchmarking::{BenchmarkBatch, BenchmarkList}; + pub use frame_support::traits::{StorageInfoTrait, WhitelistedStorageKeys}; + // pub use frame_system_benchmarking::{ + // extensions::Pallet as SystemExtensionsBench, Pallet as SystemBench, + // }; + pub use sp_storage::TrackedStorageKey; } +#[cfg(feature = "runtime-benchmarks")] +use benches::*; + sp_api::impl_runtime_apis! { impl sp_api::Core for Runtime { fn version() -> RuntimeVersion { @@ -2436,105 +2478,16 @@ sp_api::impl_runtime_apis! { Vec, Vec, ) { - use frame_benchmarking::{list_benchmark as frame_list_benchmark, Benchmarking, BenchmarkList}; - use frame_support::traits::StorageInfoTrait; - use module_nft::benchmarking::Pallet as NftBench; - // use pallet_xcm::benchmarking::Pallet as PalletXcmExtrinsicsBenchmark; - let mut list = Vec::::new(); - - frame_list_benchmark!(list, extra, module_nft, NftBench::); list_benchmarks!(list, extra); let storage_info = AllPalletsWithSystem::storage_info(); - return (list, storage_info) } fn dispatch_benchmark( config: frame_benchmarking::BenchmarkConfig ) -> Result, alloc::string::String> { - use frame_benchmarking::{Benchmarking, BenchmarkBatch, BenchmarkError, add_benchmark as frame_add_benchmark}; - use module_nft::benchmarking::Pallet as NftBench; - use frame_support::traits::{WhitelistedStorageKeys, TrackedStorageKey}; - - // const UNITS: Balance = 1_000_000_000_000; - // const CENTS: Balance = UNITS / 100; - - // parameter_types! { - // pub FeeAssetId: AssetId = AssetId(Location::parent()); - // pub const BaseDeliveryFee: u128 = CENTS.saturating_mul(3); - // } - // pub type PriceForParentDelivery = polkadot_runtime_common::xcm_sender::ExponentialPrice< - // FeeAssetId, - // BaseDeliveryFee, - // TransactionByteFee, - // ParachainSystem, - // >; - - // use pallet_xcm::benchmarking::Pallet as PalletXcmExtrinsicsBenchmark; - // impl pallet_xcm::benchmarking::Config for Runtime { - // type DeliveryHelper = cumulus_primitives_utility::ToParentDeliveryHelper< - // xcm_config::XcmConfig, - // ExistentialDepositAsset, - // PriceForParentDelivery, - // >; - // fn reachable_dest() -> Option { - // Some(Parent.into()) - // } - - // fn teleportable_asset_and_dest() -> Option<(Asset, Location)> { - // Some(( - // Asset { - // fun: Fungible(NativeTokenExistentialDeposit::get()), - // id: AssetId(Parent.into()) - // }, - // Parent.into(), - // )) - // } - - // fn reserve_transferable_asset_and_dest() -> Option<(Asset, Location)> { - // None - // } - - // fn get_asset() -> Asset { - // Asset { - // id: AssetId(Location::parent()), - // fun: Fungible(UNITS), - // } - // } - // } - - // parameter_types! { - // pub ExistentialDepositAsset: Option = Some(( - // Location::parent(), - // NativeTokenExistentialDeposit::get() - // ).into()); - // } - - // impl pallet_xcm_benchmarks::Config for Runtime { - // type XcmConfig = xcm_config::XcmConfig; - // type AccountIdConverter = xcm_config::LocationToAccountId; - // type DeliveryHelper = cumulus_primitives_utility::ToParentDeliveryHelper< - // xcm_config::XcmConfig, - // ExistentialDepositAsset, - // PriceForParentDelivery, - // >; - // fn valid_destination() -> Result { - // Ok(Location::parent()) - // } - // fn worst_case_holding(_depositable_count: u32) -> Assets { - // // just concrete assets according to relay chain. - // let assets: Vec = vec![ - // Asset { - // id: AssetId(Location::parent()), - // fun: Fungible(1_000_000 * UNITS), - // } - // ]; - // assets.into() - // } - // } - let mut whitelist: Vec = AllPalletsWithSystem::whitelisted_storage_keys(); // Treasury Account @@ -2546,7 +2499,6 @@ sp_api::impl_runtime_apis! { let mut batches = Vec::::new(); let params = (&config, &whitelist); - frame_add_benchmark!(params, batches, module_nft, NftBench::); add_benchmarks!(params, batches); if batches.is_empty() { return Err("Benchmark not found for this module.".into()) } diff --git a/runtime/acala/src/weights/orml_auction.rs b/runtime/acala/src/weights/orml_auction.rs index 621e73668..dc857ca56 100644 --- a/runtime/acala/src/weights/orml_auction.rs +++ b/runtime/acala/src/weights/orml_auction.rs @@ -59,7 +59,7 @@ impl orml_auction::WeightInfo for WeightInfo { // Proof: `EvmAccounts::EvmAddresses` (`max_values`: None, `max_size`: Some(60), added: 2535, mode: `MaxEncodedLen`) // Storage: `Auction::AuctionEndTime` (r:0 w:2) // Proof: `Auction::AuctionEndTime` (`max_values`: None, `max_size`: Some(32), added: 2507, mode: `MaxEncodedLen`) - fn bid_collateral_auction() -> Weight { + fn bid() -> Weight { // Proof Size summary in bytes: // Measured: `2500` // Estimated: `8856` diff --git a/runtime/common/src/mock.rs b/runtime/common/src/mock.rs index f2b728f04..c0ebca5c2 100644 --- a/runtime/common/src/mock.rs +++ b/runtime/common/src/mock.rs @@ -100,6 +100,8 @@ impl orml_tokens::Config for TestRuntime { type MaxReserves = (); type ReserveIdentifier = ReserveIdentifier; type DustRemovalWhitelist = Nothing; + #[cfg(any(feature = "wasm-bench", feature = "runtime-benchmarks"))] + type BenchmarkHelper = (); } parameter_types! { @@ -142,6 +144,8 @@ impl module_idle_scheduler::Config for TestRuntime { type MinimumWeightRemainInBlock = MinimumWeightRemainInBlock; type RelayChainBlockNumberProvider = MockBlockNumberProvider; type DisableBlockThreshold = ConstU32<6>; + #[cfg(feature = "runtime-benchmarks")] + type BenchmarkHelper = (); } pub struct GasToWeight; diff --git a/runtime/common/src/precompile/mock.rs b/runtime/common/src/precompile/mock.rs index 64588fad4..ce4dc7d6e 100644 --- a/runtime/common/src/precompile/mock.rs +++ b/runtime/common/src/precompile/mock.rs @@ -140,6 +140,8 @@ impl orml_tokens::Config for Test { type MaxReserves = (); type ReserveIdentifier = [u8; 8]; type DustRemovalWhitelist = Nothing; + #[cfg(any(feature = "wasm-bench", feature = "runtime-benchmarks"))] + type BenchmarkHelper = (); } impl pallet_balances::Config for Test { @@ -183,6 +185,8 @@ impl module_currencies::Config for Test { type GasToWeight = (); type SweepOrigin = EnsureSignedBy; type OnDust = (); + #[cfg(feature = "runtime-benchmarks")] + type BenchmarkHelper = (); } impl module_evm_bridge::Config for Test { @@ -195,6 +199,8 @@ impl module_asset_registry::Config for Test { type EVMBridge = module_evm_bridge::EVMBridge; type RegisterOrigin = EnsureSignedBy; type WeightInfo = (); + #[cfg(feature = "runtime-benchmarks")] + type BenchmarkHelper = (); } define_combined_task! { @@ -225,6 +231,8 @@ impl module_idle_scheduler::Config for Test { type MinimumWeightRemainInBlock = MinimumWeightRemainInBlock; type RelayChainBlockNumberProvider = MockBlockNumberProvider; type DisableBlockThreshold = ConstU32<6>; + #[cfg(feature = "runtime-benchmarks")] + type BenchmarkHelper = (); } parameter_types! { @@ -284,6 +292,8 @@ impl module_transaction_payment::Config for Test { type CustomFeeSurplus = CustomFeeSurplus; type AlternativeFeeSurplus = AlternativeFeeSurplus; type DefaultFeeTokens = DefaultFeeTokens; + #[cfg(feature = "runtime-benchmarks")] + type BenchmarkHelper = (); } #[derive( @@ -401,6 +411,8 @@ impl module_dex::Config for Test { type ListingOrigin = EnsureSignedBy; type ExtendedProvisioningBlocks = ConstU32<0>; type OnLiquidityPoolUpdated = (); + #[cfg(feature = "runtime-benchmarks")] + type BenchmarkHelper = (); } parameter_types! { @@ -467,6 +479,8 @@ impl module_cdp_engine::Config for Test { type EVMBridge = module_evm_bridge::EVMBridge; type SettleErc20EvmOrigin = SettleErc20EvmOrigin; type WeightInfo = (); + #[cfg(feature = "runtime-benchmarks")] + type BenchmarkHelper = (); } pub struct MockAuctionManager; @@ -524,6 +538,8 @@ impl module_cdp_treasury::Config for Test { type WeightInfo = (); type StableAsset = MockStableAsset; type Swap = SpecificJointsSwap; + #[cfg(feature = "runtime-benchmarks")] + type BenchmarkHelper = (); } impl module_honzon::Config for Test { @@ -531,6 +547,8 @@ impl module_honzon::Config for Test { type DepositPerAuthorization = ConstU128<100>; type CollateralCurrencyIds = CollateralCurrencyIds; type WeightInfo = (); + #[cfg(feature = "runtime-benchmarks")] + type BenchmarkHelper = (); } parameter_types! { @@ -558,6 +576,8 @@ impl nutsfinance_stable_asset::Config for Test { type WeightInfo = (); type ListingOrigin = EnsureSignedBy; type EnsurePoolAssetId = EnsurePoolAssetId; + #[cfg(feature = "runtime-benchmarks")] + type BenchmarkHelper = (); } impl module_transaction_pause::Config for Test { @@ -676,6 +696,8 @@ impl module_prices::Config for Test { type RewardRatePerRelaychainBlock = RewardRatePerRelaychainBlock; type PricingPegged = PricingPegged; type WeightInfo = (); + #[cfg(feature = "runtime-benchmarks")] + type BenchmarkHelper = (); } /// mock XCM transfer. @@ -783,6 +805,8 @@ impl module_incentives::Config for Test { type EmergencyShutdown = MockEmergencyShutdown; type PalletId = IncentivesPalletId; type WeightInfo = (); + #[cfg(feature = "runtime-benchmarks")] + type BenchmarkHelper = (); } parameter_types! { @@ -983,6 +1007,8 @@ impl module_earning::Config for Test { type MaxUnbondingChunks = ConstU32<10>; type LockIdentifier = EarningLockIdentifier; type WeightInfo = (); + #[cfg(feature = "runtime-benchmarks")] + type BenchmarkHelper = (); } pub const ALICE: AccountId = AccountId::new([1u8; 32]); diff --git a/runtime/karura/Cargo.toml b/runtime/karura/Cargo.toml index 23d49c191..0124cd67e 100644 --- a/runtime/karura/Cargo.toml +++ b/runtime/karura/Cargo.toml @@ -54,6 +54,7 @@ sp-runtime = { workspace = true } sp-session = { workspace = true } sp-staking = { workspace = true } sp-std = { workspace = true } +sp-storage = { workspace = true } sp-transaction-pool = { workspace = true } sp-version = { workspace = true } @@ -81,7 +82,6 @@ parachains-common = { workspace = true } # orml orml-auction = { workspace = true } orml-authority = { workspace = true } -orml-benchmarking = { workspace = true, optional = true } orml-nft = { workspace = true } orml-oracle = { workspace = true } orml-oracle-runtime-api = { workspace = true } @@ -199,6 +199,7 @@ std = [ "sp-session/std", "sp-staking/std", "sp-std/std", + "sp-storage/std", "sp-transaction-pool/std", "sp-version/std", "substrate-wasm-builder", @@ -224,7 +225,6 @@ std = [ "orml-auction/std", "orml-authority/std", - "orml-benchmarking/std", "orml-nft/std", "orml-oracle-runtime-api/std", "orml-oracle/std", @@ -279,7 +279,6 @@ std = [ "nutsfinance-stable-asset/std", ] runtime-benchmarks = [ - "orml-benchmarking", "libsecp256k1/hmac", "frame-benchmarking/runtime-benchmarks", @@ -312,6 +311,7 @@ runtime-benchmarks = [ "xcm-builder/runtime-benchmarks", "xcm-executor/runtime-benchmarks", + "orml-auction/runtime-benchmarks", "orml-authority/runtime-benchmarks", "orml-oracle/runtime-benchmarks", "orml-parameters/runtime-benchmarks", @@ -319,11 +319,32 @@ runtime-benchmarks = [ "orml-vesting/runtime-benchmarks", "orml-xtokens/runtime-benchmarks", + "module-aggregated-dex/runtime-benchmarks", + "module-asset-registry/runtime-benchmarks", + "module-auction-manager/runtime-benchmarks", + "module-cdp-engine/runtime-benchmarks", + "module-cdp-treasury/runtime-benchmarks", "module-collator-selection/runtime-benchmarks", + "module-currencies/runtime-benchmarks", + "module-dex-oracle/runtime-benchmarks", + "module-dex/runtime-benchmarks", + "module-earning/runtime-benchmarks", + "module-emergency-shutdown/runtime-benchmarks", "module-evm-accounts/runtime-benchmarks", "module-evm-bridge/runtime-benchmarks", + "module-evm/runtime-benchmarks", "module-homa/runtime-benchmarks", + "module-homa-validator-list/runtime-benchmarks", + "module-honzon/runtime-benchmarks", + "module-honzon-bridge/runtime-benchmarks", + "module-idle-scheduler/runtime-benchmarks", + "module-incentives/runtime-benchmarks", "module-nft/runtime-benchmarks", + "module-nominees-election/runtime-benchmarks", + "module-prices/runtime-benchmarks", + "module-session-manager/runtime-benchmarks", + "module-transaction-pause/runtime-benchmarks", + "module-transaction-payment/runtime-benchmarks", "module-xcm-interface/runtime-benchmarks", "nutsfinance-stable-asset/runtime-benchmarks", diff --git a/runtime/karura/src/benchmarking/honzon_bridge.rs b/runtime/karura/src/benchmarking/honzon_bridge.rs deleted file mode 100644 index a55802808..000000000 --- a/runtime/karura/src/benchmarking/honzon_bridge.rs +++ /dev/null @@ -1,51 +0,0 @@ -// This file is part of Acala. - -// Copyright (C) 2020-2025 Acala Foundation. -// SPDX-License-Identifier: GPL-3.0-or-later WITH Classpath-exception-2.0 - -// This program 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. - -// This program 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 this program. If not, see . - -use crate::{AccountId, EvmAddress, HonzonBridge, Runtime}; - -use frame_benchmarking::account; -use frame_system::RawOrigin; -use orml_benchmarking::runtime_benchmarks; -use sp_std::prelude::*; - -runtime_benchmarks! { - { Runtime, module_honzon_bridge } - set_bridged_stable_coin_address { - }: _(RawOrigin::Root, EvmAddress::default()) - - to_bridged { - HonzonBridge::set_bridged_stable_coin_address(RawOrigin::Root.into(), EvmAddress::default())?; - - let caller: AccountId = account("caller", 0, 0); - }: _(RawOrigin::Signed(caller), 0) - - from_bridged { - HonzonBridge::set_bridged_stable_coin_address(RawOrigin::Root.into(), EvmAddress::default())?; - - let caller: AccountId = account("caller", 0, 0); - }: _(RawOrigin::Signed(caller), 0) -} - -#[cfg(test)] -mod tests { - use super::*; - use crate::benchmarking::utils::tests::new_test_ext; - use orml_benchmarking::impl_benchmark_test_suite; - - impl_benchmark_test_suite!(new_test_ext(),); -} diff --git a/runtime/karura/src/benchmarking/mod.rs b/runtime/karura/src/benchmarking/mod.rs deleted file mode 100644 index 69dfdbd9d..000000000 --- a/runtime/karura/src/benchmarking/mod.rs +++ /dev/null @@ -1,120 +0,0 @@ -// This file is part of Acala. - -// Copyright (C) 2020-2025 Acala Foundation. -// SPDX-License-Identifier: GPL-3.0-or-later WITH Classpath-exception-2.0 - -// This program 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. - -// This program 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 this program. If not, see . - -#![cfg(feature = "runtime-benchmarks")] - -use super::{CurrencyId, KAR, KSM, LKSM}; -use sp_std::prelude::*; - -pub mod utils { - include!("../../../mandala/src/benchmarking/utils.rs"); -} - -// module benchmarking -pub mod asset_registry { - include!("../../../mandala/src/benchmarking/asset_registry.rs"); -} -pub mod auction_manager { - include!("../../../mandala/src/benchmarking/auction_manager.rs"); -} -pub mod cdp_engine { - include!("../../../mandala/src/benchmarking/cdp_engine.rs"); -} -pub mod cdp_treasury { - include!("../../../mandala/src/benchmarking/cdp_treasury.rs"); -} -pub mod collator_selection { - include!("../../../mandala/src/benchmarking/collator_selection.rs"); -} -pub mod currencies { - include!("../../../mandala/src/benchmarking/currencies.rs"); -} -pub mod dex { - include!("../../../mandala/src/benchmarking/dex.rs"); -} -pub mod dex_oracle { - include!("../../../mandala/src/benchmarking/dex_oracle.rs"); -} -pub mod emergency_shutdown { - include!("../../../mandala/src/benchmarking/emergency_shutdown.rs"); -} -pub mod evm { - include!("../../../mandala/src/benchmarking/evm.rs"); -} -pub mod evm_accounts { - include!("../../../mandala/src/benchmarking/evm_accounts.rs"); -} -pub mod homa { - include!("../../../mandala/src/benchmarking/homa.rs"); -} -pub mod homa_validator_list { - include!("../../../mandala/src/benchmarking/homa_validator_list.rs"); -} -pub mod honzon { - include!("../../../mandala/src/benchmarking/honzon.rs"); -} -pub mod idle_scheduler { - include!("../../../mandala/src/benchmarking/idle_scheduler.rs"); -} -pub mod incentives { - include!("../../../mandala/src/benchmarking/incentives.rs"); -} -pub mod prices { - include!("../../../mandala/src/benchmarking/prices.rs"); -} -pub mod transaction_pause { - include!("../../../mandala/src/benchmarking/transaction_pause.rs"); -} -pub mod transaction_payment { - include!("../../../mandala/src/benchmarking/transaction_payment.rs"); -} -pub mod session_manager { - include!("../../../mandala/src/benchmarking/session_manager.rs"); -} -pub mod nutsfinance_stable_asset { - include!("../../../mandala/src/benchmarking/nutsfinance_stable_asset.rs"); -} -pub mod aggregated_dex { - include!("../../../mandala/src/benchmarking/aggregated_dex.rs"); -} -pub mod nominees_election { - include!("../../../mandala/src/benchmarking/nominees_election.rs"); -} - -// orml benchmarking -pub mod auction { - include!("../../../mandala/src/benchmarking/auction.rs"); -} -pub mod authority { - include!("../../../mandala/src/benchmarking/authority.rs"); -} -pub mod tokens { - include!("../../../mandala/src/benchmarking/tokens.rs"); -} -pub mod vesting { - include!("../../../mandala/src/benchmarking/vesting.rs"); -} -pub mod honzon_bridge; - -pub fn get_vesting_account() -> super::AccountId { - super::KaruraFoundationAccounts::get()[0].clone() -} - -pub fn get_benchmarking_collateral_currency_ids() -> Vec { - vec![KSM, LKSM, KAR, CurrencyId::StableAssetPoolToken(0)] -} diff --git a/runtime/karura/src/benchmarks.rs b/runtime/karura/src/benchmarks.rs new file mode 100644 index 000000000..d302fab95 --- /dev/null +++ b/runtime/karura/src/benchmarks.rs @@ -0,0 +1,32 @@ +// This file is part of Acala. + +// Copyright (C) 2020-2025 Acala Foundation. +// SPDX-License-Identifier: GPL-3.0-or-later WITH Classpath-exception-2.0 + +// This program 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. + +// This program 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 this program. If not, see . + +use crate::{AccountId, CurrencyId, KaruraFoundationAccounts, KAR, KSM, KUSD, LKSM}; +use sp_std::{vec, vec::Vec}; + +pub mod common { + include!("../../mandala/src/benchmark_common.rs"); +} + +pub fn get_vesting_account() -> AccountId { + KaruraFoundationAccounts::get()[0].clone() +} + +pub fn get_benchmarking_collateral_currency_ids() -> Vec { + vec![KAR, KUSD, KSM, LKSM, CurrencyId::StableAssetPoolToken(0)] +} diff --git a/runtime/karura/src/genesis_config_presets.rs b/runtime/karura/src/genesis_config_presets.rs index e6fd1a2a5..96ca9bab8 100644 --- a/runtime/karura/src/genesis_config_presets.rs +++ b/runtime/karura/src/genesis_config_presets.rs @@ -18,7 +18,8 @@ use crate::{ AccountId, BalancesConfig, CollatorSelectionConfig, NativeTokenExistentialDeposit, ParachainInfoConfig, - PolkadotXcmConfig, RuntimeGenesisConfig, SessionConfig, SessionKeys, SudoConfig, + PolkadotXcmConfig, RuntimeGenesisConfig, SessionConfig, SessionDuration, SessionKeys, SessionManagerConfig, + SudoConfig, }; use alloc::{vec, vec::Vec}; @@ -78,6 +79,9 @@ fn testnet_genesis( safe_xcm_version: Some(SAFE_XCM_VERSION) }, sudo: SudoConfig { key: Some(root) }, + session_manager: SessionManagerConfig { + session_duration: SessionDuration::get(), + } }) } diff --git a/runtime/karura/src/lib.rs b/runtime/karura/src/lib.rs index 7078b1619..abb762d6e 100644 --- a/runtime/karura/src/lib.rs +++ b/runtime/karura/src/lib.rs @@ -130,7 +130,8 @@ use runtime_common::{ pub use nutsfinance_stable_asset; mod authority; -mod benchmarking; +#[cfg(feature = "runtime-benchmarks")] +mod benchmarks; pub mod constants; #[cfg(feature = "genesis-builder")] mod genesis_config_presets; @@ -767,6 +768,8 @@ impl orml_auction::Config for Runtime { type AuctionId = AuctionId; type Handler = AuctionManager; type WeightInfo = weights::orml_auction::WeightInfo; + #[cfg(feature = "runtime-benchmarks")] + type BenchmarkHelper = benchmarks::common::BenchmarkHelper; } impl orml_authority::Config for Runtime { @@ -777,6 +780,8 @@ impl orml_authority::Config for Runtime { type AsOriginId = AuthoritysOriginId; type AuthorityConfig = AuthorityConfigImpl; type WeightInfo = weights::orml_authority::WeightInfo; + #[cfg(feature = "runtime-benchmarks")] + type BenchmarkHelper = benchmarks::common::BenchmarkHelper; } parameter_types! { @@ -786,15 +791,6 @@ parameter_types! { pub const MaxFeedValues: u32 = 10; // max 10 values allowed to feed in one call. } -#[cfg(feature = "runtime-benchmarks")] -pub struct BenchmarkHelper; -#[cfg(feature = "runtime-benchmarks")] -impl orml_oracle::BenchmarkHelper for BenchmarkHelper { - fn get_currency_id_value_pairs() -> sp_runtime::BoundedVec<(CurrencyId, Price), MaxFeedValues> { - sp_runtime::BoundedVec::default() - } -} - type AcalaDataProvider = orml_oracle::Instance1; impl orml_oracle::Config for Runtime { type OnNewData = (); @@ -808,7 +804,7 @@ impl orml_oracle::Config for Runtime { type WeightInfo = weights::orml_oracle::WeightInfo; type MaxFeedValues = MaxFeedValues; #[cfg(feature = "runtime-benchmarks")] - type BenchmarkHelper = BenchmarkHelper; + type BenchmarkHelper = benchmarks::common::BenchmarkInstanceHelper; } create_median_value_data_provider!( @@ -897,6 +893,8 @@ impl orml_tokens::Config for Runtime { type MaxReserves = MaxReserves; type ReserveIdentifier = ReserveIdentifier; type DustRemovalWhitelist = DustRemovalWhitelist; + #[cfg(feature = "runtime-benchmarks")] + type BenchmarkHelper = benchmarks::common::BenchmarkHelper; } parameter_type_with_key! { @@ -936,6 +934,8 @@ impl module_prices::Config for Runtime { type RewardRatePerRelaychainBlock = RewardRatePerRelaychainBlock; type PricingPegged = PricingPegged; type WeightInfo = weights::module_prices::WeightInfo; + #[cfg(feature = "runtime-benchmarks")] + type BenchmarkHelper = benchmarks::common::BenchmarkHelper; } parameter_types! { @@ -957,6 +957,8 @@ impl module_currencies::Config for Runtime { type GasToWeight = GasToWeight; type SweepOrigin = EnsureRootOrOneGeneralCouncil; type OnDust = module_currencies::TransferDust; + #[cfg(feature = "runtime-benchmarks")] + type BenchmarkHelper = benchmarks::common::BenchmarkHelper; } parameter_types! { @@ -1001,6 +1003,8 @@ impl orml_vesting::Config for Runtime { type WeightInfo = weights::orml_vesting::WeightInfo; type MaxVestingSchedules = ConstU32<100>; type BlockNumberProvider = RelaychainDataProvider; + #[cfg(feature = "runtime-benchmarks")] + type BenchmarkHelper = benchmarks::common::BenchmarkHelper; } parameter_types! { @@ -1058,6 +1062,8 @@ impl module_auction_manager::Config for Runtime { type UnsignedPriority = runtime_common::AuctionManagerUnsignedPriority; type EmergencyShutdown = EmergencyShutdown; type WeightInfo = weights::module_auction_manager::WeightInfo; + #[cfg(feature = "runtime-benchmarks")] + type BenchmarkHelper = benchmarks::common::BenchmarkHelper; } impl module_loans::Config for Runtime { @@ -1185,6 +1191,8 @@ impl module_cdp_engine::Config for Runtime { type EVMBridge = module_evm_bridge::EVMBridge; type SettleErc20EvmOrigin = SettleErc20EvmOrigin; type WeightInfo = weights::module_cdp_engine::WeightInfo; + #[cfg(feature = "runtime-benchmarks")] + type BenchmarkHelper = benchmarks::common::BenchmarkHelper; } parameter_types! { @@ -1196,6 +1204,8 @@ impl module_honzon::Config for Runtime { type DepositPerAuthorization = DepositPerAuthorization; type CollateralCurrencyIds = CollateralCurrencyIds; type WeightInfo = weights::module_honzon::WeightInfo; + #[cfg(feature = "runtime-benchmarks")] + type BenchmarkHelper = benchmarks::common::BenchmarkHelper; } impl module_emergency_shutdown::Config for Runtime { @@ -1205,6 +1215,8 @@ impl module_emergency_shutdown::Config for Runtime { type AuctionManagerHandler = AuctionManager; type ShutdownOrigin = EnsureRoot; type WeightInfo = weights::module_emergency_shutdown::WeightInfo; + #[cfg(feature = "runtime-benchmarks")] + type BenchmarkHelper = benchmarks::common::BenchmarkHelper; } parameter_types! { @@ -1225,6 +1237,8 @@ impl module_dex::Config for Runtime { type ListingOrigin = EnsureRootOrHalfGeneralCouncil; type ExtendedProvisioningBlocks = ExtendedProvisioningBlocks; type OnLiquidityPoolUpdated = (); + #[cfg(feature = "runtime-benchmarks")] + type BenchmarkHelper = benchmarks::common::BenchmarkHelper; } impl module_aggregated_dex::Config for Runtime { @@ -1234,6 +1248,8 @@ impl module_aggregated_dex::Config for Runtime { type DexSwapJointList = AlternativeSwapPathJointList; type SwapPathLimit = ConstU32<3>; type WeightInfo = weights::module_aggregated_dex::WeightInfo; + #[cfg(feature = "runtime-benchmarks")] + type BenchmarkHelper = benchmarks::common::BenchmarkHelper; } pub type RebasedStableAsset = module_support::RebasedStableAsset< @@ -1249,6 +1265,8 @@ impl module_dex_oracle::Config for Runtime { type Time = Timestamp; type UpdateOrigin = EnsureRootOrHalfGeneralCouncil; type WeightInfo = weights::module_dex_oracle::WeightInfo; + #[cfg(feature = "runtime-benchmarks")] + type BenchmarkHelper = benchmarks::common::BenchmarkHelper; } parameter_types! { @@ -1272,6 +1290,8 @@ impl module_cdp_treasury::Config for Runtime { type TreasuryAccount = HonzonTreasuryAccount; type WeightInfo = weights::module_cdp_treasury::WeightInfo; type StableAsset = RebasedStableAsset; + #[cfg(feature = "runtime-benchmarks")] + type BenchmarkHelper = benchmarks::common::BenchmarkHelper; } impl module_transaction_pause::Config for Runtime { @@ -1326,6 +1346,8 @@ impl module_transaction_payment::Config for Runtime { type CustomFeeSurplus = CustomFeeSurplus; type AlternativeFeeSurplus = AlternativeFeeSurplus; type DefaultFeeTokens = DefaultFeeTokens; + #[cfg(feature = "runtime-benchmarks")] + type BenchmarkHelper = benchmarks::common::BenchmarkHelper; } impl module_evm_accounts::Config for Runtime { @@ -1342,6 +1364,8 @@ impl module_asset_registry::Config for Runtime { type EVMBridge = module_evm_bridge::EVMBridge; type RegisterOrigin = EnsureRootOrHalfGeneralCouncil; type WeightInfo = weights::module_asset_registry::WeightInfo; + #[cfg(feature = "runtime-benchmarks")] + type BenchmarkHelper = benchmarks::common::BenchmarkHelper; } parameter_type_with_key! { @@ -1383,6 +1407,8 @@ impl module_incentives::Config for Runtime { type EmergencyShutdown = EmergencyShutdown; type PalletId = IncentivesPalletId; type WeightInfo = weights::module_incentives::WeightInfo; + #[cfg(feature = "runtime-benchmarks")] + type BenchmarkHelper = benchmarks::common::BenchmarkHelper; } parameter_types! { @@ -1669,6 +1695,8 @@ impl module_homa_validator_list::Config for Runtime { type LiquidStakingExchangeRateProvider = Homa; type CurrentEra = Homa; type WeightInfo = weights::module_homa_validator_list::WeightInfo; + #[cfg(feature = "runtime-benchmarks")] + type BenchmarkHelper = benchmarks::common::BenchmarkHelper; } parameter_types! { @@ -1690,6 +1718,8 @@ impl module_nominees_election::Config for Runtime { type OnUnbonded = module_incentives::OnNomineesElectionUnbonded; type CurrentEra = Homa; type WeightInfo = weights::module_nominees_election::WeightInfo; + #[cfg(feature = "runtime-benchmarks")] + type BenchmarkHelper = benchmarks::common::BenchmarkHelper; } pub struct SubAccountIndexAccountIdConvertor; @@ -1743,6 +1773,8 @@ impl module_idle_scheduler::Config for Runtime { // Number of relay chain blocks produced with no parachain blocks finalized, // once this number is reached idle scheduler is disabled as block production is slow type DisableBlockThreshold = ConstU32<6>; + #[cfg(feature = "runtime-benchmarks")] + type BenchmarkHelper = benchmarks::common::BenchmarkHelper; } parameter_types! { @@ -1817,6 +1849,8 @@ impl nutsfinance_stable_asset::Config for Runtime { type WeightInfo = weights::nutsfinance_stable_asset::WeightInfo; type ListingOrigin = EnsureRootOrHalfGeneralCouncil; type EnsurePoolAssetId = EnsurePoolAssetId; + #[cfg(feature = "runtime-benchmarks")] + type BenchmarkHelper = benchmarks::common::BenchmarkHelper; } parameter_types! { @@ -1836,6 +1870,8 @@ impl module_earning::Config for Runtime { type MaxUnbondingChunks = ConstU32<10>; type LockIdentifier = EarningLockIdentifier; type WeightInfo = (); + #[cfg(feature = "runtime-benchmarks")] + type BenchmarkHelper = benchmarks::common::BenchmarkHelper; } define_aggregrated_parameters! { @@ -2014,49 +2050,54 @@ parameter_types! { #[allow(unused_parens)] type Migrations = (frame_support::migrations::RemoveStorage,); -#[cfg(feature = "runtime-benchmarks")] -#[macro_use] -extern crate orml_benchmarking; - #[cfg(feature = "runtime-benchmarks")] mod benches { - define_benchmarks!( - [module_dex, benchmarking::dex] - [module_dex_oracle, benchmarking::dex_oracle] - [module_asset_registry, benchmarking::asset_registry] - [module_auction_manager, benchmarking::auction_manager] - [module_cdp_engine, benchmarking::cdp_engine] - [module_emergency_shutdown, benchmarking::emergency_shutdown] - [module_evm, benchmarking::evm] - [module_homa, benchmarking::homa] - [module_homa_validator_list, benchmarking::homa_validator_list] - [module_honzon, benchmarking::honzon] - [module_cdp_treasury, benchmarking::cdp_treasury] - [module_collator_selection, benchmarking::collator_selection] - [module_transaction_pause, benchmarking::transaction_pause] - [module_transaction_payment, benchmarking::transaction_payment] - [module_incentives, benchmarking::incentives] - [module_prices, benchmarking::prices] - [module_evm_accounts, benchmarking::evm_accounts] - [module_currencies, benchmarking::currencies] - [module_session_manager, benchmarking::session_manager] - [module_honzon_bridge, benchmarking::honzon_bridge] - [orml_tokens, benchmarking::tokens] - [orml_vesting, benchmarking::vesting] - [orml_auction, benchmarking::auction] - [orml_authority, benchmarking::authority] - [nutsfinance_stable_asset, benchmarking::nutsfinance_stable_asset] - [module_idle_scheduler, benchmarking::idle_scheduler] - [module_aggregated_dex, benchmarking::aggregated_dex] - [module_nominees_election, benchmarking::nominees_election] + frame_benchmarking::define_benchmarks!( + [module_aggregated_dex, AggregatedDex] + [module_asset_registry, AssetRegistry] + [module_auction_manager, AuctionManager] + [module_cdp_engine, CdpEngine] + [module_cdp_treasury, CdpTreasury] + [module_collator_selection, CollatorSelection] + [module_currencies, Currencies] + [module_dex, Dex] + [module_dex_oracle, DexOracle] + [module_emergency_shutdown, EmergencyShutdown] + [module_evm, EVM] + [module_evm_accounts, EvmAccounts] + [module_homa, Homa] + [module_homa_validator_list, HomaValidatorList] + [module_honzon, Honzon] + [module_honzon_bridge, HonzonBridge] + [module_idle_scheduler, IdleScheduler] + [module_incentives, Incentives] + [module_nominees_election, NomineesElection] + [module_prices, Prices] + [module_session_manager, SessionManager] + [module_transaction_pause, TransactionPause] + [module_transaction_payment, TransactionPayment] + [orml_auction, Auction] + [orml_authority, Authority] + [orml_tokens, Tokens] + [orml_vesting, Vesting] + // Acala Ecosystem Modules + [nutsfinance_stable_asset, StableAsset] + + // XCM + // [pallet_xcm, PalletXcmExtrinsicsBenchmark::] ); - // frame_benchmarking::define_benchmarks!( - // // XCM - // [pallet_xcm, PalletXcmExtrinsicsBenchmark::] - // // TODO: add oracle - // ); + + pub use frame_benchmarking::{BenchmarkBatch, BenchmarkList}; + pub use frame_support::traits::{StorageInfoTrait, WhitelistedStorageKeys}; + // pub use frame_system_benchmarking::{ + // extensions::Pallet as SystemExtensionsBench, Pallet as SystemBench, + // }; + pub use sp_storage::TrackedStorageKey; } +#[cfg(feature = "runtime-benchmarks")] +use benches::*; + impl_runtime_apis! { impl sp_api::Core for Runtime { fn version() -> RuntimeVersion { @@ -2441,105 +2482,16 @@ impl_runtime_apis! { Vec, Vec, ) { - use frame_benchmarking::{list_benchmark as frame_list_benchmark, Benchmarking, BenchmarkList}; - use frame_support::traits::StorageInfoTrait; - use module_nft::benchmarking::Pallet as NftBench; - // use pallet_xcm::benchmarking::Pallet as PalletXcmExtrinsicsBenchmark; - let mut list = Vec::::new(); - - frame_list_benchmark!(list, extra, module_nft, NftBench::); list_benchmarks!(list, extra); let storage_info = AllPalletsWithSystem::storage_info(); - return (list, storage_info) } fn dispatch_benchmark( config: frame_benchmarking::BenchmarkConfig ) -> Result, alloc::string::String> { - use frame_benchmarking::{Benchmarking, BenchmarkBatch, BenchmarkError, add_benchmark as frame_add_benchmark}; - use module_nft::benchmarking::Pallet as NftBench; - use frame_support::traits::{WhitelistedStorageKeys, TrackedStorageKey}; - - // const UNITS: Balance = 1_000_000_000_000; - // const CENTS: Balance = UNITS / 100; - - // parameter_types! { - // pub FeeAssetId: AssetId = AssetId(Location::parent()); - // pub const BaseDeliveryFee: u128 = CENTS.saturating_mul(3); - // } - // pub type PriceForParentDelivery = polkadot_runtime_common::xcm_sender::ExponentialPrice< - // FeeAssetId, - // BaseDeliveryFee, - // TransactionByteFee, - // ParachainSystem, - // >; - - // use pallet_xcm::benchmarking::Pallet as PalletXcmExtrinsicsBenchmark; - // impl pallet_xcm::benchmarking::Config for Runtime { - // type DeliveryHelper = cumulus_primitives_utility::ToParentDeliveryHelper< - // xcm_config::XcmConfig, - // ExistentialDepositAsset, - // PriceForParentDelivery, - // >; - // fn reachable_dest() -> Option { - // Some(Parent.into()) - // } - - // fn teleportable_asset_and_dest() -> Option<(Asset, Location)> { - // Some(( - // Asset { - // fun: Fungible(NativeTokenExistentialDeposit::get()), - // id: AssetId(Parent.into()) - // }, - // Parent.into(), - // )) - // } - - // fn reserve_transferable_asset_and_dest() -> Option<(Asset, Location)> { - // None - // } - - // fn get_asset() -> Asset { - // Asset { - // id: AssetId(Location::parent()), - // fun: Fungible(UNITS), - // } - // } - // } - - // parameter_types! { - // pub ExistentialDepositAsset: Option = Some(( - // Location::parent(), - // NativeTokenExistentialDeposit::get() - // ).into()); - // } - - // impl pallet_xcm_benchmarks::Config for Runtime { - // type XcmConfig = xcm_config::XcmConfig; - // type AccountIdConverter = xcm_config::LocationToAccountId; - // type DeliveryHelper = cumulus_primitives_utility::ToParentDeliveryHelper< - // xcm_config::XcmConfig, - // ExistentialDepositAsset, - // PriceForParentDelivery, - // >; - // fn valid_destination() -> Result { - // Ok(Location::parent()) - // } - // fn worst_case_holding(_depositable_count: u32) -> Assets { - // // just concrete assets according to relay chain. - // let assets: Vec = vec![ - // Asset { - // id: AssetId(Location::parent()), - // fun: Fungible(1_000_000 * UNITS), - // } - // ]; - // assets.into() - // } - // } - let mut whitelist: Vec = AllPalletsWithSystem::whitelisted_storage_keys(); // Treasury Account @@ -2551,7 +2503,6 @@ impl_runtime_apis! { let mut batches = Vec::::new(); let params = (&config, &whitelist); - frame_add_benchmark!(params, batches, module_nft, NftBench::); add_benchmarks!(params, batches); if batches.is_empty() { return Err("Benchmark not found for this module.".into()) } diff --git a/runtime/karura/src/weights/orml_auction.rs b/runtime/karura/src/weights/orml_auction.rs index c32546cd4..a73e5047f 100644 --- a/runtime/karura/src/weights/orml_auction.rs +++ b/runtime/karura/src/weights/orml_auction.rs @@ -59,7 +59,7 @@ impl orml_auction::WeightInfo for WeightInfo { // Proof: `EvmAccounts::EvmAddresses` (`max_values`: None, `max_size`: Some(60), added: 2535, mode: `MaxEncodedLen`) // Storage: `Auction::AuctionEndTime` (r:0 w:2) // Proof: `Auction::AuctionEndTime` (`max_values`: None, `max_size`: Some(32), added: 2507, mode: `MaxEncodedLen`) - fn bid_collateral_auction() -> Weight { + fn bid() -> Weight { // Proof Size summary in bytes: // Measured: `2470` // Estimated: `8856` diff --git a/runtime/mandala/Cargo.toml b/runtime/mandala/Cargo.toml index ccacca5cc..460e465b8 100644 --- a/runtime/mandala/Cargo.toml +++ b/runtime/mandala/Cargo.toml @@ -57,6 +57,7 @@ sp-runtime = { workspace = true } sp-session = { workspace = true } sp-staking = { workspace = true } sp-std = { workspace = true } +sp-storage = { workspace = true } sp-transaction-pool = { workspace = true } sp-version = { workspace = true } @@ -84,7 +85,6 @@ parachains-common = { workspace = true } # orml orml-auction = { workspace = true } orml-authority = { workspace = true } -orml-benchmarking = { workspace = true, optional = true } orml-nft = { workspace = true } orml-oracle = { workspace = true } orml-oracle-runtime-api = { workspace = true } @@ -209,6 +209,7 @@ std = [ "sp-session/std", "sp-staking/std", "sp-std/std", + "sp-storage/std", "sp-transaction-pool/std", "sp-version/std", "substrate-wasm-builder", @@ -234,7 +235,6 @@ std = [ "orml-auction/std", "orml-authority/std", - "orml-benchmarking/std", "orml-nft/std", "orml-oracle-runtime-api/std", "orml-oracle/std", @@ -289,7 +289,6 @@ std = [ "nutsfinance-stable-asset/std", ] runtime-benchmarks = [ - "orml-benchmarking", "libsecp256k1/hmac", "frame-benchmarking/runtime-benchmarks", @@ -324,6 +323,7 @@ runtime-benchmarks = [ "xcm-builder/runtime-benchmarks", "xcm-executor/runtime-benchmarks", + "orml-auction/runtime-benchmarks", "orml-authority/runtime-benchmarks", "orml-oracle/runtime-benchmarks", "orml-parameters/runtime-benchmarks", @@ -331,11 +331,32 @@ runtime-benchmarks = [ "orml-vesting/runtime-benchmarks", "orml-xtokens/runtime-benchmarks", + "module-aggregated-dex/runtime-benchmarks", + "module-asset-registry/runtime-benchmarks", + "module-auction-manager/runtime-benchmarks", + "module-cdp-engine/runtime-benchmarks", + "module-cdp-treasury/runtime-benchmarks", "module-collator-selection/runtime-benchmarks", + "module-currencies/runtime-benchmarks", + "module-dex-oracle/runtime-benchmarks", + "module-dex/runtime-benchmarks", + "module-earning/runtime-benchmarks", + "module-emergency-shutdown/runtime-benchmarks", "module-evm-accounts/runtime-benchmarks", "module-evm-bridge/runtime-benchmarks", + "module-evm/runtime-benchmarks", "module-homa/runtime-benchmarks", + "module-homa-validator-list/runtime-benchmarks", + "module-honzon/runtime-benchmarks", + "module-idle-scheduler/runtime-benchmarks", + "module-incentives/runtime-benchmarks", + "module-liquid-crowdloan/runtime-benchmarks", "module-nft/runtime-benchmarks", + "module-nominees-election/runtime-benchmarks", + "module-prices/runtime-benchmarks", + "module-session-manager/runtime-benchmarks", + "module-transaction-pause/runtime-benchmarks", + "module-transaction-payment/runtime-benchmarks", "module-xcm-interface/runtime-benchmarks", "nutsfinance-stable-asset/runtime-benchmarks", diff --git a/runtime/mandala/src/benchmark_common.rs b/runtime/mandala/src/benchmark_common.rs new file mode 100644 index 000000000..72677710c --- /dev/null +++ b/runtime/mandala/src/benchmark_common.rs @@ -0,0 +1,1116 @@ +// This file is part of Acala. + +// Copyright (C) 2020-2025 Acala Foundation. +// SPDX-License-Identifier: GPL-3.0-or-later WITH Classpath-exception-2.0 + +// This program 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. + +// This program 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 this program. If not, see . + +use crate::{ + AcalaOracle, AccountId, AccountIndex, AggregatedDex, Amount, AssetRegistry, Auction, AuctionId, AuctionManager, + AuctionTimeToClose, Aura, Balance, CdpEngine, CdpTreasury, Currencies, CurrencyId, Dex, DexOracle, + EmergencyShutdown, EraIndex, EvmTask, ExistentialDeposits, GetLiquidCurrencyId, GetNativeCurrencyId, + GetStableCurrencyId, GetStakingCurrencyId, Homa, HomaValidatorList, MinimumCount, MinimumDebitValue, Moment, + NativeTokenExistentialDeposit, OperatorMembershipAcala, Parameters, Permill, Price, Rate, Ratio, RawOrigin, + Runtime, RuntimeOrigin, RuntimeParameters, ScheduledTasks, StableAsset, System, Timestamp, TradingPair, + TreasuryPalletId, EVM, MILLISECS_PER_BLOCK +}; +use alloc::boxed::Box; +use alloc::vec::Vec; +use core::str::FromStr; +use frame_benchmarking::account; +use frame_support::{assert_ok, traits::fungibles, traits::Contains, traits::OnInitialize}; +use frame_system::pallet_prelude::BlockNumberFor; +use module_aggregated_dex::SwapPath; +use module_support::AddressMapping; +use module_support::DEXManager; +use module_support::Erc20InfoMapping; +use module_support::SwapLimit; +use module_support::{AuctionManager as AuctionManagerTrait, CDPTreasury}; +use orml_traits::Change; +use orml_traits::{GetByKey, MultiCurrency, MultiCurrencyExtended}; +use parity_scale_codec::Encode; +use primitives::{currency::AssetMetadata, evm::EvmAddress, AuthoritysOriginId}; +use runtime_common::{LCDOT, TokenInfo}; +use sp_consensus_aura::AURA_ENGINE_ID; +use sp_core::Get; +use sp_runtime::{ + traits::{AccountIdConversion, AccountIdLookup, One, StaticLookup, UniqueSaturatedInto}, + Digest, DigestItem, FixedPointNumber, FixedU128, MultiAddress, SaturatedConversion, +}; +use sp_std::vec; + +/// Helper struct for benchmarking. +pub struct BenchmarkHelper(sp_std::marker::PhantomData); + +/// Instance helper struct for benchmarking. +#[allow(dead_code)] +pub struct BenchmarkInstanceHelper(sp_std::marker::PhantomData<(T, I)>); + +impl orml_oracle::BenchmarkHelper + for BenchmarkInstanceHelper +where + T: orml_oracle::Config, +{ + fn get_currency_id_value_pairs() -> sp_runtime::BoundedVec<(T::OracleKey, T::OracleValue), T::MaxFeedValues> { + sp_runtime::BoundedVec::try_from(vec![ + (STAKING, FixedU128::saturating_from_rational(1, 1)), + (LIQUID, FixedU128::saturating_from_rational(2, 1)), + (STABLECOIN, FixedU128::saturating_from_rational(3, 1)), + ]) + .unwrap() + } +} + +impl module_aggregated_dex::BenchmarkHelper for BenchmarkHelper +where + T: module_aggregated_dex::Config, +{ + fn setup_currency_lists() -> Vec { + [NATIVE, STABLECOIN, LIQUID, STAKING].to_vec() + } + // return (path, supply_amount, target_amount) + fn setup_dex(u: u32, taker: AccountId) -> Option<(Vec, Balance, Balance)> { + let maker: AccountId = account("maker", 0, 0); + + let currency_list = Self::setup_currency_lists(); + let mut path: Vec = vec![]; + + for i in 1..u { + if i == 1 { + let cur0 = currency_list[0]; + let cur1 = currency_list[1]; + path.push(cur0); + path.push(cur1); + assert_ok!(inject_liquidity( + maker.clone(), + cur0, + cur1, + 10_000 * dollar(cur0), + 10_000 * dollar(cur1), + false, + )); + } else { + path.push(currency_list[i as usize]); + assert_ok!(inject_liquidity( + maker.clone(), + currency_list[i as usize - 1], + currency_list[i as usize], + 10_000 * dollar(currency_list[i as usize - 1]), + 10_000 * dollar(currency_list[i as usize]), + false, + )); + } + } + + set_balance(path[0], &taker, 10_000 * dollar(path[0])); + + Some((path.clone(), 1_000 * dollar(path[0]), 10 * dollar(path[path.len() - 1]))) + } +} + +impl module_asset_registry::BenchmarkHelper for BenchmarkHelper +where + T: module_asset_registry::Config, +{ + fn setup_deploy_contract() -> Option { + deploy_contract(); + Some(erc20_address()) + } +} + +impl module_auction_manager::BenchmarkHelper for BenchmarkHelper +where + T: module_auction_manager::Config, +{ + fn setup() -> Option { + let auction_id: AuctionId = 0; + let bidder: AccountId = account("bidder", 0, 0); + let funder: AccountId = account("funder", 0, 0); + + // set balance + assert_ok!(Currencies::deposit(STABLECOIN, &bidder, 80 * dollar(STABLECOIN))); + assert_ok!(Currencies::deposit(STAKING, &funder, dollar(STAKING))); + assert_ok!(CdpTreasury::deposit_collateral(&funder, STAKING, dollar(STAKING))); + + // feed price + feed_price(vec![(STAKING, Price::saturating_from_integer(120))]); + + // create collateral auction + assert_ok!(AuctionManager::new_collateral_auction( + &funder, + STAKING, + dollar(STAKING), + 100 * dollar(STABLECOIN) + )); + + // bid collateral auction + assert_ok!(AuctionManager::collateral_auction_bid_handler( + 1, + auction_id, + (bidder, 80 * dollar(STABLECOIN)), + None + )); + + // shutdown + assert_ok!(EmergencyShutdown::emergency_shutdown(RawOrigin::Root.into())); + + Some(auction_id) + } +} + +impl module_cdp_engine::BenchmarkHelper, MultiAddress> + for BenchmarkHelper +where + T: module_cdp_engine::Config, +{ + fn setup_on_initialize(c: u32) -> Option> { + let owner: AccountId = account("owner", 0, 0); + let currency_ids = super::get_benchmarking_collateral_currency_ids(); + let min_debit_value = T::MinimumDebitValue::get(); + let debit_exchange_rate = T::DefaultDebitExchangeRate::get(); + let min_debit_amount = debit_exchange_rate + .reciprocal() + .unwrap() + .saturating_mul_int(min_debit_value); + let min_debit_amount: Amount = min_debit_amount.unique_saturated_into(); + let collateral_value = 2 * min_debit_value; + + // feed price + let mut feed_data: Vec<(CurrencyId, Price)> = vec![]; + for i in 0..c { + let currency_id = currency_ids[i as usize]; + let collateral_price = Price::one(); + feed_data.push((currency_id, collateral_price)); + } + feed_price(feed_data); + + for i in 0..c { + let currency_id = currency_ids[i as usize]; + if matches!(currency_id, CurrencyId::StableAssetPoolToken(_)) { + continue; + } + let collateral_amount = Price::saturating_from_rational(dollar(currency_id), dollar(STABLECOIN)) + .saturating_mul_int(collateral_value); + + let ed = if NATIVE == currency_id { + NativeTokenExistentialDeposit::get() + } else { + ExistentialDeposits::get(¤cy_id) + }; + + // set balance + set_balance(currency_id, &owner, collateral_amount + ed); + + assert_ok!(CdpEngine::set_collateral_params( + RawOrigin::Root.into(), + currency_id, + Change::NoChange, + Change::NewValue(Some(Ratio::saturating_from_rational(0, 100))), + Change::NewValue(Some(Rate::saturating_from_rational(10, 100))), + Change::NewValue(Some(Ratio::saturating_from_rational(0, 100))), + Change::NewValue(min_debit_value * 100), + )); + + // adjust position + assert_ok!(CdpEngine::adjust_position( + &owner, + currency_id, + collateral_amount.try_into().unwrap(), + min_debit_amount + )); + } + + set_block_number_timestamp(2, MILLISECS_PER_BLOCK); + CdpEngine::on_initialize(2); + + set_block_number_timestamp(3, MILLISECS_PER_BLOCK * 2); + Some(3u32.into()) + } + fn setup_collateral_currency_ids() -> Vec { + super::get_benchmarking_collateral_currency_ids() + } + fn setup_liquidate_by_auction(b: u32) -> Option<(CurrencyId, MultiAddress)> { + let owner: AccountId = account("owner", 0, 0); + let owner_lookup = AccountIdLookup::unlookup(owner.clone()); + let min_debit_value = MinimumDebitValue::get(); + let debit_exchange_rate = CdpEngine::get_debit_exchange_rate(STAKING); + let collateral_price = Price::one(); // 1 USD + let min_debit_amount = debit_exchange_rate + .reciprocal() + .unwrap() + .saturating_mul_int(min_debit_value); + let min_debit_amount: Amount = min_debit_amount.unique_saturated_into(); + let collateral_value = 2 * min_debit_value; + let collateral_amount = + Price::saturating_from_rational(dollar(STAKING), dollar(STABLECOIN)).saturating_mul_int(collateral_value); + + // set balance + set_balance(STAKING, &owner, collateral_amount + ExistentialDeposits::get(&STAKING)); + + // feed price + feed_price(vec![(STAKING, collateral_price)]); + + // set risk params + assert_ok!(CdpEngine::set_collateral_params( + RawOrigin::Root.into(), + STAKING, + Change::NoChange, + Change::NewValue(Some(Ratio::saturating_from_rational(150, 100))), + Change::NewValue(Some(Rate::saturating_from_rational(10, 100))), + Change::NewValue(Some(Ratio::saturating_from_rational(150, 100))), + Change::NewValue(min_debit_value * 100), + )); + + let auction_size = collateral_amount / b as u128; + // adjust auction size so we hit MaxAuctionCount + assert_ok!(CdpTreasury::set_expected_collateral_auction_size( + RawOrigin::Root.into(), + STAKING, + auction_size + )); + // adjust position + assert_ok!(CdpEngine::adjust_position( + &owner, + STAKING, + collateral_amount.try_into().unwrap(), + min_debit_amount + )); + + // modify liquidation rate to make the cdp unsafe + assert_ok!(CdpEngine::set_collateral_params( + RawOrigin::Root.into(), + STAKING, + Change::NoChange, + Change::NewValue(Some(Ratio::saturating_from_rational(1000, 100))), + Change::NoChange, + Change::NoChange, + Change::NoChange, + )); + Some((STAKING, owner_lookup)) + } + fn setup_liquidate_by_dex() -> Option<(CurrencyId, MultiAddress)> { + let owner: AccountId = account("owner", 0, 0); + let owner_lookup = AccountIdLookup::unlookup(owner.clone()); + let funder: AccountId = account("funder", 0, 0); + let debit_value = 100 * dollar(STABLECOIN); + let debit_exchange_rate = CdpEngine::get_debit_exchange_rate(LIQUID); + let debit_amount = debit_exchange_rate + .reciprocal() + .unwrap() + .saturating_mul_int(debit_value); + let debit_amount: Amount = debit_amount.unique_saturated_into(); + let collateral_value = 2 * debit_value; + let collateral_amount = + Price::saturating_from_rational(dollar(LIQUID), dollar(STABLECOIN)).saturating_mul_int(collateral_value); + let collateral_price = Price::one(); // 1 USD + + set_balance( + LIQUID, + &owner, + (10 * collateral_amount) + ExistentialDeposits::get(&LIQUID), + ); + assert_ok!(inject_liquidity( + funder.clone(), + LIQUID, + STAKING, + 10_000 * dollar(LIQUID), + 10_000 * dollar(STAKING), + false, + )); + assert_ok!(inject_liquidity( + funder, + STAKING, + STABLECOIN, + 10_000 * dollar(STAKING), + 10_000 * dollar(STABLECOIN), + false, + )); + + // feed price + feed_price(vec![(STAKING, collateral_price)]); + + // set risk params + assert_ok!(CdpEngine::set_collateral_params( + RawOrigin::Root.into(), + LIQUID, + Change::NoChange, + Change::NewValue(Some(Ratio::saturating_from_rational(150, 100))), + Change::NewValue(Some(Rate::saturating_from_rational(10, 100))), + Change::NewValue(Some(Ratio::saturating_from_rational(150, 100))), + Change::NewValue(debit_value * 100), + )); + + // adjust position + assert_ok!(CdpEngine::adjust_position( + &owner, + LIQUID, + (10 * collateral_amount).try_into().unwrap(), + debit_amount + )); + + // modify liquidation rate to make the cdp unsafe + assert_ok!(CdpEngine::set_collateral_params( + RawOrigin::Root.into(), + LIQUID, + Change::NoChange, + Change::NewValue(Some(Ratio::saturating_from_rational(1000, 100))), + Change::NoChange, + Change::NoChange, + Change::NoChange, + )); + Some((LIQUID, owner_lookup)) + } + fn setup_settle() -> Option<(CurrencyId, MultiAddress)> { + let owner: AccountId = account("owner", 0, 0); + let owner_lookup = AccountIdLookup::unlookup(owner.clone()); + let min_debit_value = MinimumDebitValue::get(); + let debit_exchange_rate = CdpEngine::get_debit_exchange_rate(STAKING); + let collateral_price = Price::one(); // 1 USD + let min_debit_amount = debit_exchange_rate + .reciprocal() + .unwrap() + .saturating_mul_int(min_debit_value); + let min_debit_amount: Amount = min_debit_amount.unique_saturated_into(); + let collateral_value = 2 * min_debit_value; + let collateral_amount = Price::saturating_from_rational(1_000 * dollar(STAKING), 1000 * dollar(STABLECOIN)) + .saturating_mul_int(collateral_value); + + // set balance + set_balance(STAKING, &owner, collateral_amount + ExistentialDeposits::get(&STAKING)); + + // feed price + feed_price(vec![(STAKING, collateral_price)]); + + // set risk params + assert_ok!(CdpEngine::set_collateral_params( + RawOrigin::Root.into(), + STAKING, + Change::NoChange, + Change::NewValue(Some(Ratio::saturating_from_rational(150, 100))), + Change::NewValue(Some(Rate::saturating_from_rational(10, 100))), + Change::NewValue(Some(Ratio::saturating_from_rational(150, 100))), + Change::NewValue(min_debit_value * 100), + )); + + // adjust position + assert_ok!(CdpEngine::adjust_position( + &owner, + STAKING, + collateral_amount.try_into().unwrap(), + min_debit_amount + )); + + // shutdown + assert_ok!(EmergencyShutdown::emergency_shutdown(RawOrigin::Root.into())); + Some((STAKING, owner_lookup)) + } +} + +impl module_cdp_treasury::BenchmarkHelper for BenchmarkHelper +where + T: module_cdp_treasury::Config, +{ + fn setup_dex_pools(caller: AccountId) -> Option { + assert_ok!(initialize_swap_pools(caller)); + Some(STAKING) + } +} + +impl module_currencies::BenchmarkHelper for BenchmarkHelper +where + T: module_currencies::Config, +{ + fn setup_get_staking_currency_id_and_amount() -> Option<(CurrencyId, Balance)> { + Some((STAKING, 100 * dollar(STAKING))) + } + fn setup_get_treasury_account() -> Option { + Some(TreasuryPalletId::get().into_account_truncating()) + } +} + +impl module_dex::BenchmarkHelper for BenchmarkHelper +where + T: module_dex::Config, +{ + fn setup_stable_currency_id() -> Option { + Some(STABLECOIN) + } + fn setup_currency_lists() -> Vec { + super::get_benchmarking_collateral_currency_ids() + } +} + +impl module_dex_oracle::BenchmarkHelper for BenchmarkHelper +where + T: module_dex_oracle::Config, +{ + fn setup_on_initialize(n: u32, u: u32) { + let caller: AccountId = account("caller", 0, 0); + + let trading_pair_list = vec![ + TradingPair::from_currency_ids(NATIVE, STABLECOIN).unwrap(), + TradingPair::from_currency_ids(NATIVE, STAKING).unwrap(), + TradingPair::from_currency_ids(STAKING, STABLECOIN).unwrap(), + ]; + + for i in 0..n { + let trading_pair = trading_pair_list[i as usize]; + assert_ok!(inject_liquidity( + caller.clone(), + trading_pair.first(), + trading_pair.second(), + dollar(trading_pair.first()) * 100, + dollar(trading_pair.second()) * 1000, + false, + )); + assert_ok!(DexOracle::enable_average_price( + RawOrigin::Root.into(), + trading_pair.first(), + trading_pair.second(), + 240000 + )); + } + for j in 0..u.min(n) { + let update_pair = trading_pair_list[j as usize]; + assert_ok!(DexOracle::update_average_price_interval( + RawOrigin::Root.into(), + update_pair.first(), + update_pair.second(), + 24000 + )); + } + set_block_number_timestamp(1, 24000); + } + + fn setup_inject_liquidity() -> Option<(CurrencyId, CurrencyId, Moment)> { + let caller: AccountId = account("caller", 0, 0); + + assert_ok!(inject_liquidity( + caller.clone(), + NATIVE, + STABLECOIN, + dollar(NATIVE) * 100, + dollar(STABLECOIN) * 1000, + false, + )); + + Some((NATIVE, STABLECOIN, 24000)) + } +} + +impl module_earning::BenchmarkHelper for BenchmarkHelper +where + T: module_earning::Config, +{ + fn setup_parameter_store() { + assert_ok!(Parameters::set_parameter( + RawOrigin::Root.into(), + RuntimeParameters::Earning(module_earning::Parameters::InstantUnstakeFee( + module_earning::InstantUnstakeFee, + Some(Permill::from_percent(10)) + )) + )); + } +} + +impl module_emergency_shutdown::BenchmarkHelper for BenchmarkHelper +where + T: module_emergency_shutdown::Config, +{ + fn setup_feed_price(c: u32) { + let currency_ids = super::get_benchmarking_collateral_currency_ids(); + + let funder: AccountId = account("funder", 0, 0); + let mut values = vec![]; + + for i in 0..c { + let currency_id = currency_ids[i as usize]; + if matches!(currency_id, CurrencyId::StableAssetPoolToken(_)) { + continue; + } + values.push((currency_id, Price::one())); + set_balance(currency_id, &funder, 100 * dollar(currency_id)); + assert_ok!(CdpTreasury::deposit_collateral( + &funder, + currency_id, + 100 * dollar(currency_id) + )); + } + feed_price(values); + } + fn setup_collateral_currency_ids() -> Vec { + super::get_benchmarking_collateral_currency_ids() + } +} + +impl module_homa_validator_list::BenchmarkHelper for BenchmarkHelper +where + T: module_homa_validator_list::Config, +{ + fn setup_homa_bump_era(era_index: EraIndex) { + assert_ok!(Homa::force_bump_current_era(RawOrigin::Root.into(), era_index)); + } +} + +impl module_honzon::BenchmarkHelper for BenchmarkHelper +where + T: module_honzon::Config, +{ + fn setup_collateral_currency_ids() -> Vec { + super::get_benchmarking_collateral_currency_ids() + } + fn setup_stable_currency_id_and_amount() -> Option<(CurrencyId, Balance)> { + Some((GetStableCurrencyId::get(), dollar(STABLECOIN))) + } + fn setup_staking_currency_id_and_amount() -> Option<(CurrencyId, Balance)> { + Some((GetStakingCurrencyId::get(), dollar(STAKING))) + } + fn setup_liquid_currency_id_and_amount() -> Option<(CurrencyId, Balance)> { + Some((GetLiquidCurrencyId::get(), dollar(LIQUID))) + } + fn setup_dex_pools(maker: AccountId) { + assert_ok!(initialize_swap_pools(maker)); + } + fn setup_feed_price(currency_id: CurrencyId, price: Price) { + feed_price(vec![(currency_id, price)]); + } +} + +impl module_idle_scheduler::BenchmarkHelper for BenchmarkHelper +where + T: module_idle_scheduler::Config, +{ + fn setup_schedule_task() -> Option { + Some(ScheduledTasks::EvmTask(EvmTask::Remove { + caller: Default::default(), + contract: Default::default(), + maintainer: Default::default(), + })) + } +} + +impl module_incentives::BenchmarkHelper for BenchmarkHelper +where + T: module_incentives::Config, +{ + fn setup_stable_currency_id_and_amount() -> Option<(CurrencyId, Balance)> { + Some((STABLECOIN, dollar(STABLECOIN))) + } + fn setup_collateral_currency_ids() -> Vec<(CurrencyId, Balance)> { + let currency_ids = super::get_benchmarking_collateral_currency_ids(); + currency_ids + .iter() + .map(|currency_id| (*currency_id, dollar(NATIVE))) + .collect() + } +} + +impl module_nominees_election::BenchmarkHelper for BenchmarkHelper +where + T: module_nominees_election::Config, +{ + fn setup_homa_bump_era(era_index: EraIndex) { + assert_ok!(Homa::force_bump_current_era(RawOrigin::Root.into(), era_index)); + } + fn setup_homa_validators(caller: AccountId, targets: Vec) { + for validator in targets.iter() { + assert_ok!(HomaValidatorList::bond( + RawOrigin::Signed(caller.clone()).into(), + validator.clone(), + ::ValidatorInsuranceThreshold::get() + )); + } + } +} + +impl module_prices::BenchmarkHelper for BenchmarkHelper +where + T: module_prices::Config, +{ + fn setup_feed_price() -> Option { + feed_price(vec![(STAKING, dollar(STAKING).into())]); + Some(STAKING) + } +} + +impl module_transaction_payment::BenchmarkHelper for BenchmarkHelper +where + T: module_transaction_payment::Config, +{ + fn setup_stable_currency_id() -> Option { + Some(STABLECOIN) + } + fn setup_liquid_currency_id() -> Option { + Some(LIQUID) + } + fn setup_enable_fee_pool() -> Option<(AccountId, Balance, Balance, Balance)> { + let funder: AccountId = account("funder", 0, 0); + let treasury_account: AccountId = TreasuryPalletId::get().into_account_truncating(); + let sub_account: AccountId = + ::PalletId::get().into_sub_account_truncating(STABLECOIN); + let native_ed: Balance = >::minimum_balance(NATIVE); + let stable_ed: Balance = >::minimum_balance(STABLECOIN); + let pool_size: Balance = native_ed * 50; + let swap_threshold: Balance = native_ed * 2; + + assert_ok!(inject_liquidity( + funder.clone(), + STABLECOIN, + NATIVE, + 1_000 * dollar(STABLECOIN), + 10_000 * dollar(NATIVE), + false, + )); + assert!(Dex::get_swap_amount( + &vec![STABLECOIN, NATIVE], + SwapLimit::ExactTarget(Balance::MAX, native_ed) + ) + .is_some()); + assert_eq!( + Dex::get_liquidity_pool(STABLECOIN, NATIVE), + (1_000 * dollar(STABLECOIN), 10_000 * dollar(NATIVE)) + ); + + set_balance(NATIVE, &treasury_account, pool_size * 10); + set_balance(STABLECOIN, &treasury_account, stable_ed * 10); + Some((sub_account, stable_ed, pool_size, swap_threshold)) + } + fn setup_enable_stable_asset() { + let funder: AccountId = account("funder", 0, 0); + + set_balance(STAKING, &funder, 1000 * dollar(STAKING)); + set_balance(LIQUID, &funder, 1000 * dollar(LIQUID)); + set_balance(NATIVE, &funder, 1000 * dollar(NATIVE)); + + // create stable asset pool + let pool_asset = CurrencyId::StableAssetPoolToken(0); + assert_ok!(StableAsset::create_pool( + RuntimeOrigin::root(), + pool_asset, + vec![STAKING, LIQUID], + vec![1u128, 1u128], + 10_000_000u128, + 20_000_000u128, + 50_000_000u128, + 1_000u128, + funder.clone(), + funder.clone(), + 1_000_000_000_000u128, + )); + let asset_metadata = AssetMetadata { + name: b"Token Name".to_vec(), + symbol: b"TN".to_vec(), + decimals: 12, + minimal_balance: 1, + }; + assert_ok!(AssetRegistry::register_stable_asset( + RawOrigin::Root.into(), + Box::new(asset_metadata.clone()) + )); + assert_ok!(StableAsset::mint( + RuntimeOrigin::signed(funder.clone()), + 0, + vec![100 * dollar(STAKING), 100 * dollar(LIQUID)], + 0u128 + )); + + assert_ok!(inject_liquidity( + funder.clone(), + LIQUID, + NATIVE, + 100 * dollar(LIQUID), + 100 * dollar(NATIVE), + false, + )); + } +} + +impl orml_tokens::BenchmarkHelper for BenchmarkHelper +where + T: orml_tokens::Config, +{ + fn get_currency_id_and_amount() -> Option<(CurrencyId, Balance)> { + Some((STAKING, dollar(STAKING))) + } +} + +impl orml_vesting::BenchmarkHelper::Balance> for BenchmarkHelper +where + T: frame_system::Config + pallet_balances::Config + orml_vesting::Config, +{ + fn get_vesting_account_and_amount() -> Option<(AccountId, ::Balance)> { + Some((super::get_vesting_account(), dollar(NATIVE))) + } +} + +impl orml_auction::BenchmarkHelper, AccountId, Balance> for BenchmarkHelper +where + T: orml_auction::Config, +{ + fn setup_bid() -> Option<(AccountId, Balance)> { + let bidder: AccountId = account("bidder", 0, 0); + let previous_bidder: AccountId = account("previous_bidder", 0, 0); + let funder: AccountId = account("funder", 0, 0); + let collateral_amount: Balance = 100 * dollar(STAKING); + let target_amount: Balance = 10_000 * dollar(STABLECOIN); + let previous_bid_price: Balance = 5_000u128 * dollar(STABLECOIN); + let bid_price: Balance = 10_000u128 * dollar(STABLECOIN); + let auction_id: AuctionId = 0; + + set_balance(STAKING, &funder, collateral_amount); + set_balance(STABLECOIN, &bidder, bid_price); + set_balance(STABLECOIN, &previous_bidder, previous_bid_price); + assert_ok!(>::deposit_collateral( + &funder, + STAKING, + collateral_amount + )); + assert_ok!(AuctionManager::new_collateral_auction( + &funder, + STAKING, + collateral_amount, + target_amount + )); + assert_ok!(Auction::bid( + RawOrigin::Signed(previous_bidder).into(), + auction_id, + previous_bid_price + )); + + Some((bidder, bid_price)) + } + + fn setup_on_finalize(rand: u32) -> Option> { + let bidder = account("bidder", 0, 0); + let funder = account("funder", 0, 0); + let collateral_amount = 100 * dollar(STAKING); + let target_amount = 10_000 * dollar(STABLECOIN); + let bid_price = 5_000u128 * dollar(STABLECOIN); + + System::set_block_number(1); + for auction_id in 0..rand { + set_balance(STAKING, &funder, collateral_amount); + assert_ok!(>::deposit_collateral( + &funder, + STAKING, + collateral_amount + )); + assert_ok!(AuctionManager::new_collateral_auction( + &funder, + STAKING, + collateral_amount, + target_amount + )); + set_balance(STABLECOIN, &bidder, bid_price); + assert_ok!(Auction::bid( + RawOrigin::Signed(bidder.clone()).into(), + auction_id, + bid_price + )); + } + Some((System::block_number() + AuctionTimeToClose::get()).into()) + } +} + +impl orml_authority::BenchmarkHelper for BenchmarkHelper +where + T: orml_authority::Config, +{ + fn get_as_origin_id() -> Option { + Some(AuthoritysOriginId::Root) + } +} + +impl nutsfinance_stable_asset::BenchmarkHelper for BenchmarkHelper +where + T: nutsfinance_stable_asset::Config, +{ + fn setup_assets_and_pool_asset(u: u32) -> Option<(Vec, Vec, CurrencyId)> { + let currency_list = vec![ + NATIVE, + STABLECOIN, + LIQUID, + STAKING, + CurrencyId::join_dex_share_currency_id(LIQUID, STAKING).unwrap(), + ]; + + let pool_asset = CurrencyId::StableAssetPoolToken(0); + let mut assets = vec![]; + let mut precisions = vec![]; + for i in 0..u { + let i_idx: usize = usize::try_from(i).unwrap(); + let currency_id = currency_list[i_idx]; + assets.push(currency_id); + precisions.push(1u128); + } + + Some((assets, precisions, pool_asset)) + } + fn setup_create_stable_pool( + assets: Vec, + precisions: Vec, + _pool_asset: CurrencyId, + ) -> Option { + create_stable_pools(assets, precisions, 10000u128); + Some(0) + } +} + +pub const NATIVE: CurrencyId = GetNativeCurrencyId::get(); +pub const STABLECOIN: CurrencyId = GetStableCurrencyId::get(); +pub const LIQUID: CurrencyId = GetLiquidCurrencyId::get(); +pub const STAKING: CurrencyId = GetStakingCurrencyId::get(); + +pub fn register_native_asset(assets: Vec) { + assets.iter().for_each(|asset| { + let ed = if *asset == GetNativeCurrencyId::get() { + NativeTokenExistentialDeposit::get() + } else { + ExistentialDeposits::get(&asset) + }; + assert_ok!(AssetRegistry::register_native_asset( + RuntimeOrigin::root(), + *asset, + Box::new(AssetMetadata { + name: asset.name().unwrap().as_bytes().to_vec(), + symbol: asset.symbol().unwrap().as_bytes().to_vec(), + decimals: asset.decimals().unwrap(), + minimal_balance: ed, + }) + )); + }); +} + +pub fn set_balance(currency_id: CurrencyId, who: &AccountId, balance: Balance) { + assert_ok!(>::update_balance( + currency_id, + who, + balance.saturated_into() + )); +} + +pub fn feed_price(prices: Vec<(CurrencyId, Price)>) { + for i in 0..MinimumCount::get() { + let oracle: AccountId = account("oracle", 0, i); + if !OperatorMembershipAcala::contains(&oracle) { + assert_ok!(OperatorMembershipAcala::add_member( + RawOrigin::Root.into(), + MultiAddress::Id(oracle.clone()) + )); + } + assert_ok!(AcalaOracle::feed_values( + RawOrigin::Signed(oracle).into(), + prices.to_vec().try_into().unwrap() + )); + } +} + +pub fn set_block_number_timestamp(block_number: u32, timestamp: u64) { + let slot = timestamp / Aura::slot_duration(); + let digest = Digest { + logs: vec![DigestItem::PreRuntime(AURA_ENGINE_ID, slot.encode())], + }; + System::initialize(&block_number, &Default::default(), &digest); + Aura::on_initialize(block_number); + Timestamp::set_timestamp(timestamp); +} + +#[allow(dead_code)] +pub fn set_balance_fungibles(currency_id: CurrencyId, who: &AccountId, balance: Balance) { + assert_ok!( as fungibles::Mutate>::mint_into(currency_id, who, balance)); +} + +pub fn dollar(currency_id: CurrencyId) -> Balance { + if matches!(currency_id, CurrencyId::Token(_)) + && module_asset_registry::EvmErc20InfoMapping::::decimals(currency_id).is_none() + { + register_native_asset(vec![currency_id]); + } + if let Some(decimals) = module_asset_registry::EvmErc20InfoMapping::::decimals(currency_id) { + 10u128.saturating_pow(decimals.into()) + } else { + panic!("{:?} not support decimals", currency_id); + } +} + +pub fn inject_liquidity( + maker: AccountId, + currency_id_a: CurrencyId, + currency_id_b: CurrencyId, + max_amount_a: Balance, + max_amount_b: Balance, + deposit: bool, +) -> Result<(), &'static str> { + // set balance + >::update_balance( + currency_id_a, + &maker, + max_amount_a.unique_saturated_into(), + )?; + >::update_balance( + currency_id_b, + &maker, + max_amount_b.unique_saturated_into(), + )?; + + let _ = Dex::enable_trading_pair(RawOrigin::Root.into(), currency_id_a, currency_id_b); + + Dex::add_liquidity( + RawOrigin::Signed(maker.clone()).into(), + currency_id_a, + currency_id_b, + max_amount_a, + max_amount_b, + Default::default(), + deposit, + )?; + Ok(()) +} + +pub fn register_stable_asset() { + let asset_metadata = AssetMetadata { + name: b"Token Name".to_vec(), + symbol: b"TN".to_vec(), + decimals: 12, + minimal_balance: 1, + }; + assert_ok!(AssetRegistry::register_stable_asset( + RawOrigin::Root.into(), + Box::new(asset_metadata.clone()) + )); +} + +pub fn create_stable_pools(assets: Vec, precisions: Vec, initial_a: u128) { + let pool_asset = CurrencyId::StableAssetPoolToken(0); + let mint_fee = 2u128; + let swap_fee = 3u128; + let redeem_fee = 5u128; + let fee_recipient: AccountId = account("fee", 0, 0); + let yield_recipient: AccountId = account("yield", 1, 0); + + register_stable_asset(); + assert_ok!(StableAsset::create_pool( + RawOrigin::Root.into(), + pool_asset, + assets, + precisions, + mint_fee, + swap_fee, + redeem_fee, + initial_a, + fee_recipient, + yield_recipient, + 1000000000000000000u128, + )); +} + +/// Initializes all pools used in AggregatedDex `Swap` for trading to stablecoin +pub fn initialize_swap_pools(maker: AccountId) -> Result<(), &'static str> { + // Inject liquidity into all possible `AlternativeSwapPathJointList` + inject_liquidity( + maker.clone(), + LIQUID, + STABLECOIN, + 10_000 * dollar(LIQUID), + 10_000 * dollar(STABLECOIN), + false, + )?; + inject_liquidity( + maker.clone(), + STAKING, + LIQUID, + 10_000 * dollar(STAKING), + 10_000 * dollar(LIQUID), + false, + )?; + + // purposly inject too little liquidity to have failed path, still reads dexs to check for viable + // swap paths + inject_liquidity( + maker.clone(), + STAKING, + STABLECOIN, + 10 * dollar(STAKING), + 10 * dollar(STABLECOIN), + false, + )?; + inject_liquidity( + maker.clone(), + LCDOT, + STABLECOIN, + dollar(LCDOT), + dollar(STABLECOIN), + false, + )?; + inject_liquidity(maker.clone(), LCDOT, STAKING, dollar(LCDOT), dollar(STAKING), false)?; + + // Add and initialize stable pools, is manually added with changes to runtime + let assets_1 = vec![STAKING, LIQUID]; + create_stable_pools(assets_1.clone(), vec![1, 1], 10000u128); + for asset in assets_1 { + >::update_balance(asset, &maker, 1_000_000_000_000_000)?; + } + StableAsset::mint( + RawOrigin::Signed(maker.clone()).into(), + 0, + vec![1_000_000_000_000, 1_000_000_000_000], + 0, + )?; + + // Adds `AggregatedSwapPaths`, also mirrors runtimes state + AggregatedDex::update_aggregated_swap_paths( + RawOrigin::Root.into(), + vec![ + ( + (STAKING, STABLECOIN), + Some(vec![SwapPath::Taiga(0, 0, 1), SwapPath::Dex(vec![LIQUID, STABLECOIN])]), + ), + ( + (LIQUID, STABLECOIN), + Some(vec![SwapPath::Taiga(0, 1, 0), SwapPath::Dex(vec![STAKING, STABLECOIN])]), + ), + ], + )?; + + Ok(()) +} + +pub fn alice() -> AccountId { + ::AddressMapping::get_account_id(&alice_evm_addr()) +} +pub fn alice_evm_addr() -> EvmAddress { + EvmAddress::from_str("1000000000000000000000000000000000000001").unwrap() +} + +pub fn erc20_address() -> EvmAddress { + EvmAddress::from_str("0x5dddfce53ee040d9eb21afbc0ae1bb4dbb0ba643").unwrap() +} + +pub fn deploy_contract() { + //let alice_account = alice_account_id(); + set_balance(NATIVE, &alice(), 1_000_000 * dollar(NATIVE)); + + let json: serde_json::Value = + serde_json::from_str(include_str!("../../../ts-tests/build/Erc20DemoContract2.json")).unwrap(); + let code = hex::decode(json.get("bytecode").unwrap().as_str().unwrap()).unwrap(); + + assert_ok!(EVM::create( + RuntimeOrigin::signed(alice()), + code, + 0, + 2_100_000, + 1_000_000, + vec![] + )); +} diff --git a/runtime/mandala/src/benchmarking/aggregated_dex.rs b/runtime/mandala/src/benchmarking/aggregated_dex.rs deleted file mode 100644 index 854a9c536..000000000 --- a/runtime/mandala/src/benchmarking/aggregated_dex.rs +++ /dev/null @@ -1,101 +0,0 @@ -// This file is part of Acala. - -// Copyright (C) 2020-2025 Acala Foundation. -// SPDX-License-Identifier: GPL-3.0-or-later WITH Classpath-exception-2.0 - -// This program 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. - -// This program 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 this program. If not, see . - -use super::utils::{dollar, inject_liquidity, set_balance, LIQUID, NATIVE, STABLECOIN, STAKING}; -use crate::{AccountId, CurrencyId, Runtime}; -use module_aggregated_dex::SwapPath; -use runtime_common::{BNC, VSKSM}; - -use sp_std::prelude::*; - -use frame_benchmarking::{account, whitelisted_caller}; -use frame_system::RawOrigin; - -use orml_benchmarking::runtime_benchmarks; - -const CURRENCY_LIST: [CurrencyId; 6] = [NATIVE, STABLECOIN, LIQUID, STAKING, BNC, VSKSM]; - -runtime_benchmarks! { - { Runtime, module_aggregated_dex } - - swap_with_exact_supply { - let u in 2 .. ::TradingPathLimit::get(); - - let maker: AccountId = account("maker", 0, 0); - let taker: AccountId = whitelisted_caller(); - - let mut path: Vec = vec![]; - for i in 1 .. u { - if i == 1 { - let cur0 = CURRENCY_LIST[0]; - let cur1 = CURRENCY_LIST[1]; - path.push(cur0); - path.push(cur1); - inject_liquidity(maker.clone(), cur0, cur1, 10_000 * dollar(cur0), 10_000 * dollar(cur1), false)?; - } else { - path.push(CURRENCY_LIST[i as usize]); - inject_liquidity(maker.clone(), CURRENCY_LIST[i as usize - 1], CURRENCY_LIST[i as usize], 10_000 * dollar(CURRENCY_LIST[i as usize - 1]), 10_000 * dollar(CURRENCY_LIST[i as usize]), false)?; - } - } - - set_balance(path[0], &taker, 10_000 * dollar(path[0])); - }: swap_with_exact_supply(RawOrigin::Signed(taker), vec![SwapPath::Dex(path.clone())], 100 * dollar(path[0]), 0) - - swap_with_exact_target { - let u in 2 .. ::TradingPathLimit::get(); - - let maker: AccountId = account("maker", 0, 0); - let taker: AccountId = whitelisted_caller(); - - let mut path: Vec = vec![]; - for i in 1 .. u { - if i == 1 { - let cur0 = CURRENCY_LIST[0]; - let cur1 = CURRENCY_LIST[1]; - path.push(cur0); - path.push(cur1); - inject_liquidity(maker.clone(), cur0, cur1, 10_000 * dollar(cur0), 10_000 * dollar(cur1), false)?; - } else { - path.push(CURRENCY_LIST[i as usize]); - inject_liquidity(maker.clone(), CURRENCY_LIST[i as usize - 1], CURRENCY_LIST[i as usize], 10_000 * dollar(CURRENCY_LIST[i as usize - 1]), 10_000 * dollar(CURRENCY_LIST[i as usize]), false)?; - } - } - - set_balance(path[0], &taker, 10_000 * dollar(path[0])); - }: swap_with_exact_target(RawOrigin::Signed(taker), vec![SwapPath::Dex(path.clone())], 10 * dollar(path[path.len() - 1]), 1_000 * dollar(path[0])) - - update_aggregated_swap_paths { - let n in 0 .. CURRENCY_LIST.len() as u32; - let mut updates: Vec<((CurrencyId, CurrencyId), Option>)> = vec![]; - for i in 1..n { - let token_a = CURRENCY_LIST[i as usize]; - updates.push( - ((token_a, CURRENCY_LIST[0]), Some(vec![SwapPath::Dex(vec![token_a, CURRENCY_LIST[0]])])) - ); - } - }: _(RawOrigin::Root, updates) -} - -#[cfg(test)] -mod tests { - use super::*; - use crate::benchmarking::utils::tests::new_test_ext; - use orml_benchmarking::impl_benchmark_test_suite; - - impl_benchmark_test_suite!(new_test_ext(),); -} diff --git a/runtime/mandala/src/benchmarking/asset_registry.rs b/runtime/mandala/src/benchmarking/asset_registry.rs deleted file mode 100644 index c529e4d8d..000000000 --- a/runtime/mandala/src/benchmarking/asset_registry.rs +++ /dev/null @@ -1,156 +0,0 @@ -// This file is part of Acala. - -// Copyright (C) 2020-2025 Acala Foundation. -// SPDX-License-Identifier: GPL-3.0-or-later WITH Classpath-exception-2.0 - -// This program 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. - -// This program 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 this program. If not, see . - -use crate::{AccountId, AssetRegistry, CurrencyId, Runtime, RuntimeOrigin, EVM}; - -use super::utils::{dollar, set_balance, NATIVE}; -use frame_support::assert_ok; -use frame_system::RawOrigin; -use module_evm::EvmAddress; -use module_support::AddressMapping; -use orml_benchmarking::runtime_benchmarks; -use primitives::currency::AssetMetadata; -use sp_std::{boxed::Box, str::FromStr, vec}; -use xcm::{prelude::*, v5::Location}; - -pub fn alice() -> AccountId { - ::AddressMapping::get_account_id(&alice_evm_addr()) -} -pub fn alice_evm_addr() -> EvmAddress { - EvmAddress::from_str("1000000000000000000000000000000000000001").unwrap() -} - -pub fn erc20_address() -> EvmAddress { - EvmAddress::from_str("0x5dddfce53ee040d9eb21afbc0ae1bb4dbb0ba643").unwrap() -} - -pub fn deploy_contract() { - //let alice_account = alice_account_id(); - set_balance(NATIVE, &alice(), 1_000_000 * dollar(NATIVE)); - - let json: serde_json::Value = - serde_json::from_str(include_str!("../../../../ts-tests/build/Erc20DemoContract2.json")).unwrap(); - let code = hex::decode(json.get("bytecode").unwrap().as_str().unwrap()).unwrap(); - - assert_ok!(EVM::create( - RuntimeOrigin::signed(alice()), - code, - 0, - 2_100_000, - 1_000_000, - vec![] - )); -} - -runtime_benchmarks! { - { Runtime, module_asset_registry } - - register_foreign_asset { - let location = VersionedLocation::V5(Location::new( - 0, - [Parachain(1000)], - )); - let asset_metadata = AssetMetadata { - name: b"Token Name".to_vec(), - symbol: b"TN".to_vec(), - decimals: 12, - minimal_balance: 1, - }; - }: _(RawOrigin::Root, Box::new(location), Box::new(asset_metadata)) - - update_foreign_asset { - let location = VersionedLocation::V5(Location::new( - 0, - [Parachain(1000)], - )); - let asset_metadata = AssetMetadata { - name: b"Token Name".to_vec(), - symbol: b"TN".to_vec(), - decimals: 12, - minimal_balance: 1, - }; - - AssetRegistry::register_foreign_asset(RawOrigin::Root.into(), Box::new(location.clone()), Box::new(asset_metadata.clone()))?; - }: _(RawOrigin::Root, 0, Box::new(location), Box::new(asset_metadata)) - - register_stable_asset { - let asset_metadata = AssetMetadata { - name: b"Token Name".to_vec(), - symbol: b"TN".to_vec(), - decimals: 12, - minimal_balance: 1, - }; - }: _(RawOrigin::Root, Box::new(asset_metadata)) - - update_stable_asset { - let asset_metadata = AssetMetadata { - name: b"Token Name".to_vec(), - symbol: b"TN".to_vec(), - decimals: 12, - minimal_balance: 1, - }; - - AssetRegistry::register_stable_asset(RawOrigin::Root.into(), Box::new(asset_metadata.clone()))?; - }: _(RawOrigin::Root, 0, Box::new(asset_metadata)) - - register_erc20_asset { - deploy_contract(); - }: _(RawOrigin::Root, erc20_address(), 1) - - update_erc20_asset { - let asset_metadata = AssetMetadata { - name: b"Token Name".to_vec(), - symbol: b"TN".to_vec(), - decimals: 12, - minimal_balance: 1, - }; - - deploy_contract(); - AssetRegistry::register_erc20_asset(RawOrigin::Root.into(), erc20_address(), 1)?; - }: _(RawOrigin::Root, erc20_address(), Box::new(asset_metadata)) - - register_native_asset { - let asset_metadata = AssetMetadata { - name: b"Token Name".to_vec(), - symbol: b"TN".to_vec(), - decimals: 12, - minimal_balance: 1, - }; - }: _(RawOrigin::Root, CurrencyId::LiquidCrowdloan(0), Box::new(asset_metadata)) - - update_native_asset { - let currency_id = CurrencyId::LiquidCrowdloan(0); - let asset_metadata = AssetMetadata { - name: b"Token Name".to_vec(), - symbol: b"TN".to_vec(), - decimals: 12, - minimal_balance: 1, - }; - - AssetRegistry::register_native_asset(RawOrigin::Root.into(), currency_id, Box::new(asset_metadata.clone()))?; - }: _(RawOrigin::Root, currency_id, Box::new(asset_metadata)) -} - -#[cfg(test)] -mod tests { - use super::*; - use crate::benchmarking::utils::tests::new_test_ext; - use orml_benchmarking::impl_benchmark_test_suite; - - impl_benchmark_test_suite!(new_test_ext(),); -} diff --git a/runtime/mandala/src/benchmarking/auction.rs b/runtime/mandala/src/benchmarking/auction.rs deleted file mode 100644 index b458399a2..000000000 --- a/runtime/mandala/src/benchmarking/auction.rs +++ /dev/null @@ -1,104 +0,0 @@ -// This file is part of Acala. - -// Copyright (C) 2020-2025 Acala Foundation. -// SPDX-License-Identifier: GPL-3.0-or-later WITH Classpath-exception-2.0 - -// This program 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. - -// This program 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 this program. If not, see . - -use crate::{AccountId, Auction, AuctionId, AuctionManager, AuctionTimeToClose, CdpTreasury, Runtime, System}; - -use super::utils::{dollar, set_balance, STABLECOIN, STAKING}; -use frame_benchmarking::{account, whitelisted_caller}; -use frame_support::traits::OnFinalize; -use frame_system::RawOrigin; -use module_support::{AuctionManager as AuctionManagerTrait, CDPTreasury}; -use orml_benchmarking::runtime_benchmarks; -use sp_std::prelude::*; - -const SEED: u32 = 0; -const MAX_DOLLARS: u32 = 1000; -const MAX_AUCTION_ID: u32 = 100; - -runtime_benchmarks! { - { Runtime, orml_auction } - - // `bid` a collateral auction, best cases: - // there's no bidder before and bid price doesn't exceed target amount - #[extra] - bid_collateral_auction_as_first_bidder { - let d in 1 .. MAX_DOLLARS; - - let bidder: AccountId = whitelisted_caller(); - let funder = account("funder", 0, SEED); - let collateral_amount = 100 * dollar(STAKING); - let target_amount = 10_000 * dollar(STABLECOIN); - let bid_price = (5_000u128 + d as u128) * dollar(STABLECOIN); - let auction_id: AuctionId = 0; - - set_balance(STAKING, &funder, collateral_amount); - set_balance(STABLECOIN, &bidder, bid_price); - >::deposit_collateral(&funder, STAKING, collateral_amount)?; - AuctionManager::new_collateral_auction(&funder, STAKING, collateral_amount, target_amount)?; - }: bid(RawOrigin::Signed(bidder), auction_id, bid_price) - - // `bid` a collateral auction, worst cases: - // there's bidder before and bid price will exceed target amount - bid_collateral_auction { - let bidder: AccountId = whitelisted_caller(); - let previous_bidder = account("previous_bidder", 0, SEED); - let funder = account("funder", 0, SEED); - let collateral_amount = 100 * dollar(STAKING); - let target_amount = 10_000 * dollar(STABLECOIN); - let previous_bid_price = 5_000u128 * dollar(STABLECOIN); - let bid_price = 10_000u128 * dollar(STABLECOIN); - let auction_id: AuctionId = 0; - - set_balance(STAKING, &funder, collateral_amount); - set_balance(STABLECOIN, &bidder, bid_price); - set_balance(STABLECOIN, &previous_bidder, previous_bid_price); - >::deposit_collateral(&funder, STAKING, collateral_amount)?; - AuctionManager::new_collateral_auction(&funder, STAKING, collateral_amount, target_amount)?; - Auction::bid(RawOrigin::Signed(previous_bidder).into(), auction_id, previous_bid_price)?; - }: bid(RawOrigin::Signed(bidder), auction_id, bid_price) - - on_finalize { - let c in 1 .. MAX_AUCTION_ID; - - let bidder = account("bidder", 0, SEED); - let funder = account("funder", 0, SEED); - let collateral_amount = 100 * dollar(STAKING); - let target_amount = 10_000 * dollar(STABLECOIN); - let bid_price = 5_000u128 * dollar(STABLECOIN); - - System::set_block_number(1); - for auction_id in 0 .. c { - set_balance(STAKING, &funder, collateral_amount); - >::deposit_collateral(&funder, STAKING, collateral_amount)?; - AuctionManager::new_collateral_auction(&funder, STAKING, collateral_amount, target_amount)?; - set_balance(STABLECOIN, &bidder, bid_price); - Auction::bid(RawOrigin::Signed(bidder.clone()).into(), auction_id, bid_price)?; - } - }: { - Auction::on_finalize(System::block_number() + AuctionTimeToClose::get()); - } -} - -#[cfg(test)] -mod tests { - use super::*; - use crate::benchmarking::utils::tests::new_test_ext; - use orml_benchmarking::impl_benchmark_test_suite; - - impl_benchmark_test_suite!(new_test_ext(),); -} diff --git a/runtime/mandala/src/benchmarking/auction_manager.rs b/runtime/mandala/src/benchmarking/auction_manager.rs deleted file mode 100644 index 5e5c5f819..000000000 --- a/runtime/mandala/src/benchmarking/auction_manager.rs +++ /dev/null @@ -1,68 +0,0 @@ -// This file is part of Acala. - -// Copyright (C) 2020-2025 Acala Foundation. -// SPDX-License-Identifier: GPL-3.0-or-later WITH Classpath-exception-2.0 - -// This program 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. - -// This program 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 this program. If not, see . - -use crate::{AccountId, AuctionId, AuctionManager, CdpTreasury, Currencies, EmergencyShutdown, Price, Runtime}; - -use super::utils::{dollar, feed_price, STABLECOIN, STAKING}; -use frame_benchmarking::account; -use frame_system::RawOrigin; -use module_support::{AuctionManager as AuctionManagerTrait, CDPTreasury}; -use orml_benchmarking::runtime_benchmarks; -use orml_traits::MultiCurrency; -use sp_runtime::FixedPointNumber; -use sp_std::vec; - -const SEED: u32 = 0; - -runtime_benchmarks! { - { Runtime, module_auction_manager } - - // `cancel` a collateral auction, worst case: - // auction have been already bid - cancel_collateral_auction { - let bidder: AccountId = account("bidder", 0, SEED); - let funder: AccountId = account("funder", 0, SEED); - - // set balance - Currencies::deposit(STABLECOIN, &bidder, 80 * dollar(STABLECOIN))?; - Currencies::deposit(STAKING, &funder, dollar(STAKING))?; - CdpTreasury::deposit_collateral(&funder, STAKING, dollar(STAKING))?; - - // feed price - feed_price(vec![(STAKING, Price::saturating_from_integer(120))])?; - - // create collateral auction - AuctionManager::new_collateral_auction(&funder, STAKING, dollar(STAKING), 100 * dollar(STABLECOIN))?; - let auction_id: AuctionId = Default::default(); - - // bid collateral auction - AuctionManager::collateral_auction_bid_handler(1, auction_id, (bidder, 80 * dollar(STABLECOIN)), None)?; - - // shutdown - EmergencyShutdown::emergency_shutdown(RawOrigin::Root.into())?; - }: cancel(RawOrigin::None, auction_id) -} - -#[cfg(test)] -mod tests { - use super::*; - use crate::benchmarking::utils::tests::new_test_ext; - use orml_benchmarking::impl_benchmark_test_suite; - - impl_benchmark_test_suite!(new_test_ext(),); -} diff --git a/runtime/mandala/src/benchmarking/authority.rs b/runtime/mandala/src/benchmarking/authority.rs deleted file mode 100644 index 87102e415..000000000 --- a/runtime/mandala/src/benchmarking/authority.rs +++ /dev/null @@ -1,189 +0,0 @@ -// This file is part of Acala. - -// Copyright (C) 2020-2025 Acala Foundation. -// SPDX-License-Identifier: GPL-3.0-or-later WITH Classpath-exception-2.0 - -// This program 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. - -// This program 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 this program. If not, see . - -use crate::{AccountId, Authority, AuthoritysOriginId, BlockNumber, Runtime, RuntimeCall, RuntimeOrigin, System}; - -use parity_scale_codec::Encode; -use sp_runtime::traits::Hash; -use sp_std::prelude::*; - -use frame_support::{ - dispatch::GetDispatchInfo, - traits::{schedule::DispatchTime, Bounded, OriginTrait}, -}; -use frame_system::RawOrigin; -use orml_benchmarking::{runtime_benchmarks, whitelisted_caller}; - -fn runtime_call() -> Box { - let call = RuntimeCall::System(frame_system::Call::remark { remark: vec![] }); - Box::new(call) -} - -fn bounded_call(call: RuntimeCall) -> Box::Hashing>> { - let encoded_call = call.encode(); - Box::new(Bounded::Inline(encoded_call.try_into().unwrap())) -} - -runtime_benchmarks! { - { Runtime, orml_authority } - - // dispatch a dispatchable as other origin - dispatch_as { - }: _(RawOrigin::Root, AuthoritysOriginId::Root, runtime_call()) - - // schedule a dispatchable to be dispatched at later block. - schedule_dispatch_without_delay { - let call = RuntimeCall::Authority(orml_authority::Call::dispatch_as { - as_origin: AuthoritysOriginId::Root, - call: runtime_call(), - }); - }: schedule_dispatch(RawOrigin::Root, DispatchTime::At(2), 0, false, bounded_call(call)) - - // schedule a dispatchable to be dispatched at later block. - // ensure that the delay is reached when scheduling - schedule_dispatch_with_delay { - let call = RuntimeCall::Authority(orml_authority::Call::dispatch_as { - as_origin: AuthoritysOriginId::Root, - call: runtime_call(), - }); - }: schedule_dispatch(RawOrigin::Root, DispatchTime::At(2), 0, true, bounded_call(call)) - - // fast track a scheduled dispatchable. - fast_track_scheduled_dispatch { - let call = RuntimeCall::Authority(orml_authority::Call::dispatch_as { - as_origin: AuthoritysOriginId::Root, - call: runtime_call(), - }); - System::set_block_number(1u32); - Authority::schedule_dispatch( - RuntimeOrigin::root(), - DispatchTime::At(2), - 0, - true, - bounded_call(call) - )?; - let schedule_origin = { - let origin: ::RuntimeOrigin = From::from(RuntimeOrigin::root()); - let origin: ::RuntimeOrigin = - From::from(orml_authority::DelayedOrigin::::PalletsOrigin>::new( - 1, - Box::new(origin.caller().clone()), - )); - origin - }; - - let pallets_origin = schedule_origin.caller().clone(); - }: fast_track_scheduled_dispatch(RawOrigin::Root, Box::new(pallets_origin), 0, DispatchTime::At(4)) - - // delay a scheduled dispatchable. - delay_scheduled_dispatch { - let call = RuntimeCall::Authority(orml_authority::Call::dispatch_as { - as_origin: AuthoritysOriginId::Root, - call: runtime_call(), - }); - System::set_block_number(1u32); - Authority::schedule_dispatch( - RuntimeOrigin::root(), - DispatchTime::At(2), - 0, - true, - bounded_call(call) - )?; - let schedule_origin = { - let origin: ::RuntimeOrigin = From::from(RuntimeOrigin::root()); - let origin: ::RuntimeOrigin = - From::from(orml_authority::DelayedOrigin::::PalletsOrigin>::new( - 1, - Box::new(origin.caller().clone()), - )); - origin - }; - - let pallets_origin = schedule_origin.caller().clone(); - }: _(RawOrigin::Root, Box::new(pallets_origin), 0, 5) - - // cancel a scheduled dispatchable - cancel_scheduled_dispatch { - let call = RuntimeCall::Authority(orml_authority::Call::dispatch_as { - as_origin: AuthoritysOriginId::Root, - call: runtime_call(), - }); - System::set_block_number(1u32); - Authority::schedule_dispatch( - RuntimeOrigin::root(), - DispatchTime::At(2), - 0, - true, - bounded_call(call) - )?; - let schedule_origin = { - let origin: ::RuntimeOrigin = From::from(RuntimeOrigin::root()); - let origin: ::RuntimeOrigin = - From::from(orml_authority::DelayedOrigin::::PalletsOrigin>::new( - 1, - Box::new(origin.caller().clone()), - )); - origin - }; - - let pallets_origin = schedule_origin.caller().clone(); - }: _(RawOrigin::Root, Box::new(pallets_origin), 0) - - // authorize a call that can be triggered later - authorize_call { - let caller: AccountId = whitelisted_caller(); - let call = RuntimeCall::System(frame_system::Call::remark { remark: vec![] }); - let hash = ::Hashing::hash_of(&call); - System::set_block_number(1u32); - }: _(RawOrigin::Root, Box::new(call.clone()), Some(caller.clone())) - verify { - assert_eq!(Authority::saved_calls(&hash), Some((call, Some(caller)))); - } - - remove_authorized_call { - let caller: AccountId = whitelisted_caller(); - let call = RuntimeCall::System(frame_system::Call::remark { remark: vec![] }); - let hash = ::Hashing::hash_of(&call); - System::set_block_number(1u32); - Authority::authorize_call(RuntimeOrigin::root(), Box::new(call.clone()), Some(caller.clone()))?; - }: _(RawOrigin::Signed(caller), hash) - verify { - assert_eq!(Authority::saved_calls(&hash), None); - } - - trigger_call { - let caller: AccountId = whitelisted_caller(); - let call = RuntimeCall::System(frame_system::Call::remark { remark: vec![] }); - let hash = ::Hashing::hash_of(&call); - let call_weight_bound = call.get_dispatch_info().call_weight; - System::set_block_number(1u32); - Authority::authorize_call(RuntimeOrigin::root(), Box::new(call.clone()), Some(caller.clone()))?; - }: _(RawOrigin::Signed(caller), hash, call_weight_bound) - verify { - assert_eq!(Authority::saved_calls(&hash), None); - } -} - -#[cfg(test)] -mod tests { - use super::*; - use crate::benchmarking::utils::tests::new_test_ext; - use orml_benchmarking::impl_benchmark_test_suite; - - impl_benchmark_test_suite!(new_test_ext(),); -} diff --git a/runtime/mandala/src/benchmarking/cdp_engine.rs b/runtime/mandala/src/benchmarking/cdp_engine.rs deleted file mode 100644 index c8fa40b62..000000000 --- a/runtime/mandala/src/benchmarking/cdp_engine.rs +++ /dev/null @@ -1,272 +0,0 @@ -// This file is part of Acala. - -// Copyright (C) 2020-2025 Acala Foundation. -// SPDX-License-Identifier: GPL-3.0-or-later WITH Classpath-exception-2.0 - -// This program 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. - -// This program 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 this program. If not, see . - -use crate::{ - AccountId, Address, Amount, CdpEngine, CdpTreasury, CurrencyId, DefaultDebitExchangeRate, Dex, EmergencyShutdown, - ExistentialDeposits, MinimumDebitValue, NativeTokenExistentialDeposit, Price, Rate, Ratio, Runtime, H160, - MILLISECS_PER_BLOCK, -}; - -use super::{ - get_benchmarking_collateral_currency_ids, - utils::{ - dollar, feed_price, inject_liquidity, set_balance, set_block_number_timestamp, LIQUID, NATIVE, STABLECOIN, - STAKING, - }, -}; -use frame_benchmarking::account; -use frame_support::traits::{Get, OnInitialize}; -use frame_system::RawOrigin; -use module_support::DEXManager; -use orml_benchmarking::runtime_benchmarks; -use orml_traits::{Change, GetByKey}; -use sp_runtime::{ - traits::{AccountIdLookup, One, StaticLookup, UniqueSaturatedInto}, - FixedPointNumber, -}; -use sp_std::prelude::*; - -const SEED: u32 = 0; - -runtime_benchmarks! { - { Runtime, module_cdp_engine } - - on_initialize { - let c in 0 .. get_benchmarking_collateral_currency_ids().len() as u32; - let owner: AccountId = account("owner", 0, SEED); - let owner_lookup: Address = AccountIdLookup::unlookup(owner.clone()); - let currency_ids = get_benchmarking_collateral_currency_ids(); - let min_debit_value = MinimumDebitValue::get(); - let debit_exchange_rate = DefaultDebitExchangeRate::get(); - let min_debit_amount = debit_exchange_rate.reciprocal().unwrap().saturating_mul_int(min_debit_value); - let min_debit_amount: Amount = min_debit_amount.unique_saturated_into(); - let collateral_value = 2 * min_debit_value; - - // feed price - let mut feed_data: Vec<(CurrencyId, Price)> = vec![]; - for i in 0 .. c { - let currency_id = currency_ids[i as usize]; - let collateral_price = Price::one(); - feed_data.push((currency_id, collateral_price)); - } - feed_price(feed_data)?; - - for i in 0 .. c { - let currency_id = currency_ids[i as usize]; - if matches!(currency_id, CurrencyId::StableAssetPoolToken(_)) { - continue; - } - let collateral_amount = Price::saturating_from_rational(dollar(currency_id), dollar(STABLECOIN)).saturating_mul_int(collateral_value); - - let ed = if currency_id == NATIVE { - NativeTokenExistentialDeposit::get() - } else { - ExistentialDeposits::get(¤cy_id) - }; - - // set balance - set_balance(currency_id, &owner, collateral_amount + ed); - - CdpEngine::set_collateral_params( - RawOrigin::Root.into(), - currency_id, - Change::NoChange, - Change::NewValue(Some(Ratio::saturating_from_rational(0, 100))), - Change::NewValue(Some(Rate::saturating_from_rational(10, 100))), - Change::NewValue(Some(Ratio::saturating_from_rational(0, 100))), - Change::NewValue(min_debit_value * 100), - )?; - - // adjust position - CdpEngine::adjust_position(&owner, currency_id, collateral_amount.try_into().unwrap(), min_debit_amount)?; - } - - set_block_number_timestamp(2, MILLISECS_PER_BLOCK); - CdpEngine::on_initialize(2); - }: { - set_block_number_timestamp(3, MILLISECS_PER_BLOCK * 2); - CdpEngine::on_initialize(3); - } - - set_collateral_params { - }: _( - RawOrigin::Root, - STAKING, - Change::NewValue(Some(Rate::saturating_from_rational(1, 1000000))), - Change::NewValue(Some(Ratio::saturating_from_rational(150, 100))), - Change::NewValue(Some(Rate::saturating_from_rational(20, 100))), - Change::NewValue(Some(Ratio::saturating_from_rational(180, 100))), - Change::NewValue(100_000 * dollar(STABLECOIN)) - ) - - // `liquidate` by_auction - liquidate_by_auction { - let b in 1 .. ::MaxAuctionsCount::get(); - - let owner: AccountId = account("owner", 0, SEED); - let owner_lookup = AccountIdLookup::unlookup(owner.clone()); - let min_debit_value = MinimumDebitValue::get(); - let debit_exchange_rate = CdpEngine::get_debit_exchange_rate(STAKING); - let collateral_price = Price::one(); // 1 USD - let min_debit_amount = debit_exchange_rate.reciprocal().unwrap().saturating_mul_int(min_debit_value); - let min_debit_amount: Amount = min_debit_amount.unique_saturated_into(); - let collateral_value = 2 * min_debit_value; - let collateral_amount = Price::saturating_from_rational(dollar(STAKING), dollar(STABLECOIN)).saturating_mul_int(collateral_value); - - // set balance - set_balance(STAKING, &owner, collateral_amount + ExistentialDeposits::get(&STAKING)); - - // feed price - feed_price(vec![(STAKING, collateral_price)])?; - - // set risk params - CdpEngine::set_collateral_params( - RawOrigin::Root.into(), - STAKING, - Change::NoChange, - Change::NewValue(Some(Ratio::saturating_from_rational(150, 100))), - Change::NewValue(Some(Rate::saturating_from_rational(10, 100))), - Change::NewValue(Some(Ratio::saturating_from_rational(150, 100))), - Change::NewValue(min_debit_value * 100), - )?; - - let auction_size = collateral_amount / b as u128; - // adjust auction size so we hit MaxAuctionCount - CdpTreasury::set_expected_collateral_auction_size(RawOrigin::Root.into(), STAKING, auction_size)?; - // adjust position - CdpEngine::adjust_position(&owner, STAKING, collateral_amount.try_into().unwrap(), min_debit_amount)?; - - // modify liquidation rate to make the cdp unsafe - CdpEngine::set_collateral_params( - RawOrigin::Root.into(), - STAKING, - Change::NoChange, - Change::NewValue(Some(Ratio::saturating_from_rational(1000, 100))), - Change::NoChange, - Change::NoChange, - Change::NoChange, - )?; - }: liquidate(RawOrigin::None, STAKING, owner_lookup) - - // `liquidate` by dex - liquidate_by_dex { - let owner: AccountId = account("owner", 0, SEED); - let owner_lookup = AccountIdLookup::unlookup(owner.clone()); - let funder: AccountId = account("funder", 0, SEED); - let debit_value = 100 * dollar(STABLECOIN); - let debit_exchange_rate = CdpEngine::get_debit_exchange_rate(LIQUID); - let debit_amount = debit_exchange_rate.reciprocal().unwrap().saturating_mul_int(debit_value); - let debit_amount: Amount = debit_amount.unique_saturated_into(); - let collateral_value = 2 * debit_value; - let collateral_amount = Price::saturating_from_rational(dollar(LIQUID), dollar(STABLECOIN)).saturating_mul_int(collateral_value); - let collateral_price = Price::one(); // 1 USD - - set_balance(LIQUID, &owner, (10 * collateral_amount) + ExistentialDeposits::get(&LIQUID)); - inject_liquidity(funder.clone(), LIQUID, STAKING, 10_000 * dollar(LIQUID), 10_000 * dollar(STAKING), false)?; - inject_liquidity(funder, STAKING, STABLECOIN, 10_000 * dollar(STAKING), 10_000 * dollar(STABLECOIN), false)?; - - // feed price - feed_price(vec![(STAKING, collateral_price)])?; - - // set risk params - CdpEngine::set_collateral_params( - RawOrigin::Root.into(), - LIQUID, - Change::NoChange, - Change::NewValue(Some(Ratio::saturating_from_rational(150, 100))), - Change::NewValue(Some(Rate::saturating_from_rational(10, 100))), - Change::NewValue(Some(Ratio::saturating_from_rational(150, 100))), - Change::NewValue(debit_value * 100), - )?; - - // adjust position - CdpEngine::adjust_position(&owner, LIQUID, (10 * collateral_amount).try_into().unwrap(), debit_amount)?; - - // modify liquidation rate to make the cdp unsafe - CdpEngine::set_collateral_params( - RawOrigin::Root.into(), - LIQUID, - Change::NoChange, - Change::NewValue(Some(Ratio::saturating_from_rational(1000, 100))), - Change::NoChange, - Change::NoChange, - Change::NoChange, - )?; - }: liquidate(RawOrigin::None, LIQUID, owner_lookup) - verify { - let (_, stable_amount) = Dex::get_liquidity_pool(STAKING, STABLECOIN); - let (_, stable_amount_mandala) = Dex::get_liquidity_pool(LIQUID, STABLECOIN); - // paths of karura and acala are LIQUID => STAKING => STABLECOIN - #[cfg(any(feature = "with-karura-runtime", feature = "with-acala-runtime"))] - assert!(stable_amount < 10_000 * dollar(STABLECOIN)); - // path of mandala is LIQUID => STABLECOIN - #[cfg(feature = "with-mandala-runtime")] - assert!(stable_amount_mandala < 10_000 * dollar(STABLECOIN)); - } - - settle { - let owner: AccountId = account("owner", 0, SEED); - let owner_lookup = AccountIdLookup::unlookup(owner.clone()); - let min_debit_value = MinimumDebitValue::get(); - let debit_exchange_rate = CdpEngine::get_debit_exchange_rate(STAKING); - let collateral_price = Price::one(); // 1 USD - let min_debit_amount = debit_exchange_rate.reciprocal().unwrap().saturating_mul_int(min_debit_value); - let min_debit_amount: Amount = min_debit_amount.unique_saturated_into(); - let collateral_value = 2 * min_debit_value; - let collateral_amount = Price::saturating_from_rational(1_000 * dollar(STAKING), 1000 * dollar(STABLECOIN)).saturating_mul_int(collateral_value); - - // set balance - set_balance(STAKING, &owner, collateral_amount + ExistentialDeposits::get(&STAKING)); - - // feed price - feed_price(vec![(STAKING, collateral_price)])?; - - // set risk params - CdpEngine::set_collateral_params( - RawOrigin::Root.into(), - STAKING, - Change::NoChange, - Change::NewValue(Some(Ratio::saturating_from_rational(150, 100))), - Change::NewValue(Some(Rate::saturating_from_rational(10, 100))), - Change::NewValue(Some(Ratio::saturating_from_rational(150, 100))), - Change::NewValue(min_debit_value * 100), - )?; - - // adjust position - CdpEngine::adjust_position(&owner, STAKING, collateral_amount.try_into().unwrap(), min_debit_amount)?; - - // shutdown - EmergencyShutdown::emergency_shutdown(RawOrigin::Root.into())?; - }: _(RawOrigin::None, STAKING, owner_lookup) - - register_liquidation_contract { - }: _(RawOrigin::Root, H160::default()) - - deregister_liquidation_contract { - CdpEngine::register_liquidation_contract(RawOrigin::Root.into(), H160::default())?; - }: _(RawOrigin::Root, H160::default()) -} - -#[cfg(test)] -mod tests { - use super::*; - use crate::benchmarking::utils::tests::new_test_ext; - use orml_benchmarking::impl_benchmark_test_suite; - - impl_benchmark_test_suite!(new_test_ext(),); -} diff --git a/runtime/mandala/src/benchmarking/cdp_treasury.rs b/runtime/mandala/src/benchmarking/cdp_treasury.rs deleted file mode 100644 index 528a74d21..000000000 --- a/runtime/mandala/src/benchmarking/cdp_treasury.rs +++ /dev/null @@ -1,65 +0,0 @@ -// This file is part of Acala. - -// Copyright (C) 2020-2025 Acala Foundation. -// SPDX-License-Identifier: GPL-3.0-or-later WITH Classpath-exception-2.0 - -// This program 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. - -// This program 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 this program. If not, see . - -use crate::{AccountId, CdpTreasury, Currencies, Runtime}; - -use super::utils::{dollar, initialize_swap_pools, set_balance, STABLECOIN, STAKING}; -use frame_benchmarking::whitelisted_caller; -use frame_support::traits::Get; -use frame_system::RawOrigin; -use module_support::{CDPTreasury, SwapLimit}; -use orml_benchmarking::runtime_benchmarks; -use orml_traits::MultiCurrency; - -runtime_benchmarks! { - { Runtime, module_cdp_treasury } - - auction_collateral { - let b in 1 .. ::MaxAuctionsCount::get(); - - let auction_size = (1_000 * dollar(STAKING)) / b as u128; - CdpTreasury::set_expected_collateral_auction_size(RawOrigin::Root.into(), STAKING, auction_size)?; - - Currencies::deposit(STAKING, &CdpTreasury::account_id(), 10_000 * dollar(STAKING))?; - }: _(RawOrigin::Root, STAKING, 1_000 * dollar(STAKING), 1_000 * dollar(STABLECOIN), true) - - exchange_collateral_to_stable { - let caller: AccountId = whitelisted_caller(); - set_balance(STABLECOIN, &caller, 1000 * dollar(STABLECOIN)); - set_balance(STAKING, &caller, 1000 * dollar(STAKING)); - initialize_swap_pools(caller.clone())?; - - CdpTreasury::deposit_collateral(&caller, STAKING, 100 * dollar(STAKING))?; - }: _(RawOrigin::Root, STAKING, SwapLimit::ExactSupply(100 * dollar(STAKING), 0)) - - set_expected_collateral_auction_size { - }: _(RawOrigin::Root, STAKING, 200 * dollar(STAKING)) - - extract_surplus_to_treasury { - CdpTreasury::on_system_surplus(1_000 * dollar(STABLECOIN))?; - }: _(RawOrigin::Root, 200 * dollar(STABLECOIN)) -} - -#[cfg(test)] -mod tests { - use super::*; - use crate::benchmarking::utils::tests::new_test_ext; - use orml_benchmarking::impl_benchmark_test_suite; - - impl_benchmark_test_suite!(new_test_ext(),); -} diff --git a/runtime/mandala/src/benchmarking/collator_selection.rs b/runtime/mandala/src/benchmarking/collator_selection.rs deleted file mode 100644 index 62f91aed1..000000000 --- a/runtime/mandala/src/benchmarking/collator_selection.rs +++ /dev/null @@ -1,267 +0,0 @@ -// This file is part of Acala. - -// Copyright (C) 2020-2025 Acala Foundation. -// SPDX-License-Identifier: GPL-3.0-or-later WITH Classpath-exception-2.0 - -// This program 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. - -// This program 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 this program. If not, see . - -use crate::{ - AccountId, Balance, Balances, CollatorKickThreshold, CollatorSelection, Runtime, RuntimeEvent, Session, - SessionDuration, SessionKeys, System, -}; - -use frame_benchmarking::{account, whitelisted_caller}; -use frame_support::{ - assert_ok, - pallet_prelude::Decode, - traits::{Currency, Get, OnInitialize}, -}; -use frame_system::RawOrigin; -use module_collator_selection::POINT_PER_BLOCK; -use orml_benchmarking::{runtime_benchmarks, whitelist_account}; -use pallet_authorship::EventHandler; -use pallet_session::SessionManager; -use sp_std::prelude::*; - -const SEED: u32 = 0; - -fn assert_last_event(generic_event: RuntimeEvent) { - System::assert_last_event(generic_event.into()); -} - -fn register_candidates(count: u32) { - let candidates = (0..count).map(|c| account("candidate", c, SEED)).collect::>(); - assert!( - module_collator_selection::CandidacyBond::::get() > 0u32.into(), - "Bond cannot be zero!" - ); - for (index, who) in candidates.iter().enumerate() { - Balances::make_free_balance_be( - &who, - module_collator_selection::CandidacyBond::::get() - .checked_mul(2u32.into()) - .unwrap(), - ); - let mut keys = [1u8; 128]; - keys[0..4].copy_from_slice(&(index as u32).to_be_bytes()); - let keys: SessionKeys = Decode::decode(&mut &keys[..]).unwrap(); - Session::set_keys(RawOrigin::Signed(who.clone()).into(), keys, vec![]).unwrap(); - CollatorSelection::register_as_candidate(RawOrigin::Signed(who.clone()).into()).unwrap(); - } -} - -runtime_benchmarks! { - { Runtime, module_collator_selection } - - set_invulnerables { - let b in 1 .. ::MaxInvulnerables::get(); - let new_invulnerables = (0..b).map(|c| account("candidate", c, SEED)).collect::>(); - }: { - assert_ok!( - CollatorSelection::set_invulnerables(RawOrigin::Root.into(), new_invulnerables.clone()) - ); - } - verify { - assert_last_event(module_collator_selection::Event::NewInvulnerables{new_invulnerables: new_invulnerables}.into()); - } - - set_desired_candidates { - let max: u32 = ::MaxInvulnerables::get(); - }: { - assert_ok!( - CollatorSelection::set_desired_candidates(RawOrigin::Root.into(), max.clone()) - ); - } - verify { - assert_last_event(module_collator_selection::Event::NewDesiredCandidates{new_desired_candidates: max}.into()); - } - - set_candidacy_bond { - let bond: Balance = Balances::minimum_balance().checked_mul(10u32.into()).unwrap(); - }: { - assert_ok!( - CollatorSelection::set_candidacy_bond(RawOrigin::Root.into(), bond.clone()) - ); - } - verify { - assert_last_event(module_collator_selection::Event::NewCandidacyBond{new_candidacy_bond: bond}.into()); - } - - // worse case is when we have all the max-candidate slots filled except one, and we fill that - // one. - register_as_candidate { - // MinCandidates = 5, so begin with 5. - let c in 5 .. ::MaxCandidates::get(); - - module_collator_selection::CandidacyBond::::put(Balances::minimum_balance()); - module_collator_selection::DesiredCandidates::::put(c); - register_candidates(c-1); - - let caller: AccountId = whitelisted_caller(); - let bond: Balance = Balances::minimum_balance().checked_mul(2u32.into()).unwrap(); - Balances::make_free_balance_be(&caller, bond.clone()); - let keys: SessionKeys = Decode::decode(&mut &[0u8; 128][..]).unwrap(); - - Session::set_keys(RawOrigin::Signed(caller.clone()).into(), keys, vec![]).unwrap(); - }: _(RawOrigin::Signed(caller.clone())) - verify { - assert_last_event(module_collator_selection::Event::CandidateAdded{who: caller, bond: bond.saturating_div(2u32.into())}.into()); - } - - register_candidate { - let c in 1 .. ::MaxCandidates::get(); - - module_collator_selection::CandidacyBond::::put(Balances::minimum_balance()); - module_collator_selection::DesiredCandidates::::put(c); - register_candidates(c-1); - - let caller: AccountId = whitelisted_caller(); - let bond: Balance = Balances::minimum_balance().checked_mul(2u32.into()).unwrap(); - Balances::make_free_balance_be(&caller, bond.clone()); - let keys: SessionKeys = Decode::decode(&mut &[0u8; 128][..]).unwrap(); - - Session::set_keys(RawOrigin::Signed(caller.clone()).into(), keys, vec![]).unwrap(); - }: _(RawOrigin::Root, caller.clone()) - verify { - assert_last_event(module_collator_selection::Event::CandidateAdded{who: caller, bond: 0}.into()); - } - - // worse case is the last candidate leaving. - leave_intent { - // MinCandidates = 5, so begin with 6. - let c in 6 .. ::MaxCandidates::get(); - module_collator_selection::CandidacyBond::::put(Balances::minimum_balance()); - module_collator_selection::DesiredCandidates::::put(c); - register_candidates(c); - - let leaving = module_collator_selection::Candidates::::get().into_iter().last().unwrap(); - whitelist_account!(leaving); - }: _(RawOrigin::Signed(leaving.clone())) - verify { - assert_last_event(module_collator_selection::Event::CandidateRemoved{who: leaving}.into()); - } - - withdraw_bond { - // MinCandidates = 5, so begin with 6. - let c = ::MaxCandidates::get(); - module_collator_selection::CandidacyBond::::put(Balances::minimum_balance()); - module_collator_selection::DesiredCandidates::::put(c); - register_candidates(c); - - module_session_manager::SessionDuration::::put(SessionDuration::get()); - let leaving = module_collator_selection::Candidates::::get().into_iter().last().unwrap(); - whitelist_account!(leaving); - CollatorSelection::leave_intent(RawOrigin::Signed(leaving.clone()).into())?; - Session::on_initialize(SessionDuration::get()); - Session::on_initialize(2*SessionDuration::get()); - }: _(RawOrigin::Signed(leaving)) - - // worse case is paying a non-existing candidate account. - note_author { - let c = ::MaxCandidates::get(); - module_collator_selection::CandidacyBond::::put(Balances::minimum_balance()); - module_collator_selection::DesiredCandidates::::put(c); - register_candidates(c); - - Balances::make_free_balance_be( - &CollatorSelection::account_id(), - Balances::minimum_balance().checked_mul(2u32.into()).unwrap() - ); - let author = account("author", 0, SEED); - Balances::make_free_balance_be( - &author, - Balances::minimum_balance() - ); - assert!(Balances::free_balance(&author) == Balances::minimum_balance()); - }: { - CollatorSelection::note_author(author.clone()) - } - - // worse case is on new session. - new_session { - let c = ::MaxCandidates::get(); - module_collator_selection::CandidacyBond::::put(Balances::minimum_balance()); - module_collator_selection::DesiredCandidates::::put(c); - System::set_block_number(0u32.into()); - register_candidates(c); - - System::set_block_number(20u32.into()); - - assert!(module_collator_selection::Candidates::::get().len() == c as usize); - }: { - CollatorSelection::new_session(0) - } - - start_session { - // MinCandidates = 5, so begin with 5. - let r in 5 .. ::MaxCandidates::get(); - let c in 5 .. ::MaxCandidates::get(); - - module_collator_selection::CandidacyBond::::put(Balances::minimum_balance()); - module_collator_selection::DesiredCandidates::::put(c); - System::set_block_number(0u32.into()); - register_candidates(c); - - module_session_manager::SessionDuration::::put(SessionDuration::get()); - System::set_block_number(20u32.into()); - Session::on_initialize(SessionDuration::get()); - Session::on_initialize(2*SessionDuration::get()); - - assert!(module_collator_selection::Candidates::::get().len() == c as usize); - }: { - CollatorSelection::start_session(2) - } - - end_session { - // MinCandidates = 5, so begin with 5. - let r in 5 .. ::MaxCandidates::get(); - let c in 5 .. ::MaxCandidates::get(); - - module_collator_selection::CandidacyBond::::put(Balances::minimum_balance()); - module_collator_selection::DesiredCandidates::::put(c); - System::set_block_number(0u32.into()); - register_candidates(c); - - let candidates = module_collator_selection::Candidates::::get(); - let removals = c.checked_sub(r).unwrap_or_default().checked_sub(::MinCandidates::get()).unwrap_or_default(); - - let mut count = 0; - candidates.iter().for_each(|candidate| { - if count < removals { - // point = 0, will be removed. - module_collator_selection::SessionPoints::::insert(&candidate, 0); - } else { - module_collator_selection::SessionPoints::::insert(&candidate, CollatorKickThreshold::get().mul_floor(SessionDuration::get() * POINT_PER_BLOCK)); - } - count += 1; - }); - - System::set_block_number(20u32.into()); - - assert!(module_collator_selection::Candidates::::get().len() == c as usize); - }: { - CollatorSelection::end_session(0) - } verify { - assert!(module_collator_selection::Candidates::::get().len() == (c - removals) as usize); - } -} - -#[cfg(test)] -mod tests { - use super::*; - use crate::benchmarking::utils::tests::new_test_ext; - use orml_benchmarking::impl_benchmark_test_suite; - - impl_benchmark_test_suite!(new_test_ext(),); -} diff --git a/runtime/mandala/src/benchmarking/currencies.rs b/runtime/mandala/src/benchmarking/currencies.rs deleted file mode 100644 index 3b18f1cfa..000000000 --- a/runtime/mandala/src/benchmarking/currencies.rs +++ /dev/null @@ -1,175 +0,0 @@ -// This file is part of Acala. - -// Copyright (C) 2020-2025 Acala Foundation. -// SPDX-License-Identifier: GPL-3.0-or-later WITH Classpath-exception-2.0 - -// This program 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. - -// This program 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 this program. If not, see . - -use super::utils::{dollar, lookup_of_account, set_balance, NATIVE, STAKING}; -use crate::{AccountId, Amount, Balance, Currencies, NativeTokenExistentialDeposit, Runtime, Tokens, TreasuryPalletId}; - -use sp_std::prelude::*; - -use frame_benchmarking::{account, whitelisted_caller}; -use frame_system::RawOrigin; -use sp_runtime::traits::{AccountIdConversion, UniqueSaturatedInto}; - -use orml_benchmarking::runtime_benchmarks; -use orml_traits::{LockIdentifier, MultiCurrency}; - -const SEED: u32 = 0; - -runtime_benchmarks! { - { Runtime, module_currencies } - - // `transfer` non-native currency - transfer_non_native_currency { - let amount: Balance = 1_000 * dollar(STAKING); - let from: AccountId = whitelisted_caller(); - set_balance(STAKING, &from, amount); - - let to: AccountId = account("to", 0, SEED); - let to_lookup = lookup_of_account(to.clone()); - }: transfer(RawOrigin::Signed(from), to_lookup, STAKING, amount) - verify { - assert_eq!(>::total_balance(STAKING, &to), amount); - } - - // `transfer` native currency and in worst case - #[extra] - transfer_native_currency_worst_case { - let existential_deposit = NativeTokenExistentialDeposit::get(); - let amount: Balance = existential_deposit.saturating_mul(1000); - let from: AccountId = whitelisted_caller(); - set_balance(NATIVE, &from, amount); - - let to: AccountId = account("to", 0, SEED); - let to_lookup = lookup_of_account(to.clone()); - }: transfer(RawOrigin::Signed(from), to_lookup, NATIVE, amount) - verify { - assert_eq!(>::total_balance(NATIVE, &to), amount); - } - - // `transfer_native_currency` in worst case - // * will create the `to` account. - // * will kill the `from` account. - transfer_native_currency { - let existential_deposit = NativeTokenExistentialDeposit::get(); - let amount: Balance = existential_deposit.saturating_mul(1000); - let from: AccountId = whitelisted_caller(); - set_balance(NATIVE, &from, amount); - - let to: AccountId = account("to", 0, SEED); - let to_lookup = lookup_of_account(to.clone()); - }: _(RawOrigin::Signed(from), to_lookup, amount) - verify { - assert_eq!(>::total_balance(NATIVE, &to), amount); - } - - // `update_balance` for non-native currency - update_balance_non_native_currency { - let balance: Balance = 2 * dollar(STAKING); - let amount: Amount = balance.unique_saturated_into(); - let who: AccountId = account("who", 0, SEED); - let who_lookup = lookup_of_account(who.clone()); - }: update_balance(RawOrigin::Root, who_lookup, STAKING, amount) - verify { - assert_eq!(>::total_balance(STAKING, &who), balance); - } - - // `update_balance` for native currency - // * will create the `who` account. - update_balance_native_currency_creating { - let existential_deposit = NativeTokenExistentialDeposit::get(); - let balance: Balance = existential_deposit.saturating_mul(1000); - let amount: Amount = balance.unique_saturated_into(); - let who: AccountId = account("who", 0, SEED); - let who_lookup = lookup_of_account(who.clone()); - }: update_balance(RawOrigin::Root, who_lookup, NATIVE, amount) - verify { - assert_eq!(>::total_balance(NATIVE, &who), balance); - } - - // `update_balance` for native currency - // * will kill the `who` account. - update_balance_native_currency_killing { - let existential_deposit = NativeTokenExistentialDeposit::get(); - let balance: Balance = existential_deposit.saturating_mul(1000); - let amount: Amount = balance.unique_saturated_into(); - let who: AccountId = account("who", 0, SEED); - let who_lookup = lookup_of_account(who.clone()); - set_balance(NATIVE, &who, balance); - }: update_balance(RawOrigin::Root, who_lookup, NATIVE, -amount) - verify { - assert_eq!(>::free_balance(NATIVE, &who), 0); - } - - sweep_dust { - let c in 1..3u32; - let treasury: AccountId = TreasuryPalletId::get().into_account_truncating(); - let accounts: Vec = vec!["alice", "bob", "charlie"].into_iter().map(|x| account(x, 0, SEED)).collect(); - accounts.iter().for_each(|account| { - orml_tokens::Accounts::::insert(account, STAKING, orml_tokens::AccountData { - free: 100, - frozen: 0, - reserved: 0 - }); - }); - set_balance(STAKING, &treasury, dollar(STAKING)); - }: _(RawOrigin::Root, STAKING, (&accounts[..c as usize]).to_vec()) - verify { - (&accounts[..c as usize]).iter().for_each(|account| { - assert_eq!(orml_tokens::Accounts::::contains_key(account, STAKING), false); - }); - assert_eq!(Tokens::free_balance(STAKING, &treasury), dollar(STAKING) + (100 * c) as Balance); - } - - force_set_lock { - let amount: Balance = 1_000 * dollar(STAKING); - let who: AccountId = account("who", 0, SEED); - let who_lookup = lookup_of_account(who.clone()); - let lock_id: LockIdentifier = *b"aca/test"; - set_balance(STAKING, &who, amount); - }: _(RawOrigin::Root, who_lookup, STAKING, amount, lock_id) - verify { - assert_eq!( - Tokens::locks(&who, STAKING), - vec![orml_tokens::BalanceLock { id: lock_id, amount: amount }] - ); - } - - force_remove_lock { - let amount: Balance = 1_000 * dollar(STAKING); - let who: AccountId = account("who", 0, SEED); - let who_lookup = lookup_of_account(who.clone()); - let lock_id: LockIdentifier = *b"aca/test"; - set_balance(STAKING, &who, amount); - Currencies::force_set_lock(RawOrigin::Root.into(), who_lookup.clone(), STAKING, amount, lock_id)?; - }: _(RawOrigin::Root, who_lookup, STAKING, lock_id) - verify { - assert_eq!( - Tokens::locks(&who, STAKING), - vec![] - ); - } -} - -#[cfg(test)] -mod tests { - use super::*; - use crate::benchmarking::utils::tests::new_test_ext; - use orml_benchmarking::impl_benchmark_test_suite; - - impl_benchmark_test_suite!(new_test_ext(),); -} diff --git a/runtime/mandala/src/benchmarking/dex.rs b/runtime/mandala/src/benchmarking/dex.rs deleted file mode 100644 index ec4c7ca4a..000000000 --- a/runtime/mandala/src/benchmarking/dex.rs +++ /dev/null @@ -1,370 +0,0 @@ -// This file is part of Acala. - -// Copyright (C) 2020-2025 Acala Foundation. -// SPDX-License-Identifier: GPL-3.0-or-later WITH Classpath-exception-2.0 - -// This program 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. - -// This program 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 this program. If not, see . - -use super::utils::{dollar, inject_liquidity, LIQUID, NATIVE, STABLECOIN, STAKING}; -use crate::{AccountId, Currencies, CurrencyId, Dex, ExtendedProvisioningBlocks, Runtime, RuntimeEvent, System}; -use frame_benchmarking::{account, whitelisted_caller}; -use frame_system::RawOrigin; -use module_dex::TradingPairStatus; -use orml_benchmarking::runtime_benchmarks; -use orml_traits::{MultiCurrency, MultiCurrencyExtended}; -use primitives::TradingPair; -use runtime_common::{BNC, VSKSM}; -use sp_runtime::traits::UniqueSaturatedInto; -use sp_std::prelude::*; - -const SEED: u32 = 0; - -const CURRENCY_LIST: [CurrencyId; 6] = [NATIVE, STABLECOIN, LIQUID, STAKING, BNC, VSKSM]; - -fn assert_last_event(generic_event: RuntimeEvent) { - System::assert_last_event(generic_event.into()); -} - -runtime_benchmarks! { - { Runtime, module_dex } - - // enable a Disabled trading pair - enable_trading_pair { - let trading_pair = TradingPair::from_currency_ids(STABLECOIN, NATIVE).unwrap(); - if let TradingPairStatus::Enabled = Dex::trading_pair_statuses(trading_pair) { - let _ = Dex::disable_trading_pair(RawOrigin::Root.into(), trading_pair.first(), trading_pair.second()); - } - }: _(RawOrigin::Root, trading_pair.first(), trading_pair.second()) - verify { - assert_last_event(module_dex::Event::EnableTradingPair{trading_pair: trading_pair}.into()); - } - - // disable a Enabled trading pair - disable_trading_pair { - let trading_pair = TradingPair::from_currency_ids(STABLECOIN, NATIVE).unwrap(); - if let TradingPairStatus::Disabled = Dex::trading_pair_statuses(trading_pair) { - let _ = Dex::enable_trading_pair(RawOrigin::Root.into(), trading_pair.first(), trading_pair.second()); - } - }: _(RawOrigin::Root, trading_pair.first(), trading_pair.second()) - verify { - assert_last_event(module_dex::Event::DisableTradingPair{trading_pair}.into()); - } - - // list a Provisioning trading pair - list_provisioning { - let trading_pair = TradingPair::from_currency_ids(STABLECOIN, NATIVE).unwrap(); - if let TradingPairStatus::Enabled = Dex::trading_pair_statuses(trading_pair) { - Dex::disable_trading_pair(RawOrigin::Root.into(), trading_pair.first(), trading_pair.second())?; - } - }: _(RawOrigin::Root, trading_pair.first(), trading_pair.second(), dollar(trading_pair.first()), dollar(trading_pair.second()), dollar(trading_pair.first()), dollar(trading_pair.second()), 10) - verify { - assert_last_event(module_dex::Event::ListProvisioning{trading_pair: trading_pair}.into()); - } - - // update parameters of a Provisioning trading pair - update_provisioning_parameters { - let trading_pair = TradingPair::from_currency_ids(STABLECOIN, NATIVE).unwrap(); - if let TradingPairStatus::Enabled = Dex::trading_pair_statuses(trading_pair) { - Dex::disable_trading_pair(RawOrigin::Root.into(), trading_pair.first(), trading_pair.second())?; - } - Dex::list_provisioning( - RawOrigin::Root.into(), - trading_pair.first(), - trading_pair.second(), - dollar(trading_pair.first()), - dollar(trading_pair.second()), - 100 * dollar(trading_pair.first()), - 1000 * dollar(trading_pair.second()), - 100 - )?; - }: _(RawOrigin::Root, trading_pair.first(), trading_pair.second(), 2 * dollar(trading_pair.first()), 2 * dollar(trading_pair.second()), 10 * dollar(trading_pair.first()), 100 * dollar(trading_pair.second()), 200) - - // end a Provisioning trading pair - end_provisioning { - let founder: AccountId = whitelisted_caller(); - let trading_pair = TradingPair::from_currency_ids(STABLECOIN, NATIVE).unwrap(); - if let TradingPairStatus::Enabled = Dex::trading_pair_statuses(trading_pair) { - Dex::disable_trading_pair(RawOrigin::Root.into(), trading_pair.first(), trading_pair.second())?; - } - Dex::list_provisioning( - RawOrigin::Root.into(), - trading_pair.first(), - trading_pair.second(), - dollar(trading_pair.first()), - dollar(trading_pair.second()), - 100 * dollar(trading_pair.first()), - 100 * dollar(trading_pair.second()), - 0 - )?; - - // set balance - >::update_balance(trading_pair.first(), &founder, (100 * dollar(trading_pair.first())).unique_saturated_into())?; - >::update_balance(trading_pair.second(), &founder, (100 * dollar(trading_pair.second())).unique_saturated_into())?; - - // add enough provision - Dex::add_provision( - RawOrigin::Signed(founder.clone()).into(), - trading_pair.first(), - trading_pair.second(), - 100 * dollar(trading_pair.first()), - 100 * dollar(trading_pair.second()), - )?; - }: _(RawOrigin::Signed(founder), trading_pair.first(), trading_pair.second()) - verify { - assert_last_event(module_dex::Event::ProvisioningToEnabled{trading_pair, pool_0: 100 * dollar(trading_pair.first()), pool_1: 100 * dollar(trading_pair.second()), share_amount: 200 * dollar(trading_pair.first())}.into()) - } - - add_provision { - let founder: AccountId = whitelisted_caller(); - let trading_pair = TradingPair::from_currency_ids(STABLECOIN, NATIVE).unwrap(); - if let TradingPairStatus::Enabled = Dex::trading_pair_statuses(trading_pair) { - Dex::disable_trading_pair(RawOrigin::Root.into(), trading_pair.first(), trading_pair.second())?; - } - Dex::list_provisioning( - RawOrigin::Root.into(), - trading_pair.first(), - trading_pair.second(), - dollar(trading_pair.first()), - dollar(trading_pair.second()), - 100 * dollar(trading_pair.first()), - 1000 * dollar(trading_pair.second()), - 0 - )?; - - // set balance - >::update_balance(trading_pair.first(), &founder, (10 * dollar(trading_pair.first())).unique_saturated_into())?; - >::update_balance(trading_pair.second(), &founder, (10 * dollar(trading_pair.second())).unique_saturated_into())?; - }: _(RawOrigin::Signed(founder.clone()), trading_pair.first(), trading_pair.second(), dollar(trading_pair.first()), dollar(trading_pair.second())) - verify{ - assert_last_event(module_dex::Event::AddProvision{who: founder, currency_0: trading_pair.first(), contribution_0: dollar(trading_pair.first()), currency_1: trading_pair.second(), contribution_1: dollar(trading_pair.second())}.into()); - } - - claim_dex_share { - let founder: AccountId = whitelisted_caller(); - let trading_pair = TradingPair::from_currency_ids(STABLECOIN, NATIVE).unwrap(); - if let TradingPairStatus::Enabled = Dex::trading_pair_statuses(trading_pair) { - Dex::disable_trading_pair(RawOrigin::Root.into(), trading_pair.first(), trading_pair.second())?; - } - Dex::list_provisioning( - RawOrigin::Root.into(), - trading_pair.first(), - trading_pair.second(), - dollar(trading_pair.first()), - dollar(trading_pair.second()), - 10 * dollar(trading_pair.first()), - 10 * dollar(trading_pair.second()), - 0 - )?; - - // set balance - >::update_balance(trading_pair.first(), &founder, (100 * dollar(trading_pair.first())).unique_saturated_into())?; - >::update_balance(trading_pair.second(), &founder, (100 * dollar(trading_pair.second())).unique_saturated_into())?; - - Dex::add_provision( - RawOrigin::Signed(founder.clone()).into(), - trading_pair.first(), - trading_pair.second(), - dollar(trading_pair.first()), - 20 * dollar(trading_pair.second()) - )?; - Dex::end_provisioning( - RawOrigin::Signed(founder.clone()).into(), - trading_pair.first(), - trading_pair.second(), - )?; - }: _(RawOrigin::Signed(whitelisted_caller()), founder.clone(), trading_pair.first(), trading_pair.second()) - verify { - assert_eq!(Currencies::free_balance(trading_pair.dex_share_currency_id(), &founder), 2_000_000_000_000); - } - - // add liquidity but don't staking lp - add_liquidity { - let first_maker: AccountId = account("first_maker", 0, SEED); - let second_maker: AccountId = whitelisted_caller(); - let trading_pair = TradingPair::from_currency_ids(STABLECOIN, NATIVE).unwrap(); - let amount_a = 100 * dollar(trading_pair.first()); - let amount_b = 10_000 * dollar(trading_pair.second()); - - // set balance - >::update_balance(trading_pair.first(), &second_maker, amount_a.unique_saturated_into())?; - >::update_balance(trading_pair.second(), &second_maker, amount_b.unique_saturated_into())?; - - // first maker inject liquidity - inject_liquidity(first_maker.clone(), trading_pair.first(), trading_pair.second(), amount_a, amount_b, false)?; - }: add_liquidity(RawOrigin::Signed(second_maker), trading_pair.first(), trading_pair.second(), amount_a, amount_b, Default::default(), false) - - // worst: add liquidity and stake lp - add_liquidity_and_stake { - let first_maker: AccountId = account("first_maker", 0, SEED); - let second_maker: AccountId = whitelisted_caller(); - let trading_pair = TradingPair::from_currency_ids(STABLECOIN, NATIVE).unwrap(); - let amount_a = 100 * dollar(trading_pair.first()); - let amount_b = 10_000 * dollar(trading_pair.second()); - - // set balance - >::update_balance(trading_pair.first(), &second_maker, amount_a.unique_saturated_into())?; - >::update_balance(trading_pair.second(), &second_maker, amount_b.unique_saturated_into())?; - - // first maker inject liquidity - inject_liquidity(first_maker.clone(), trading_pair.first(), trading_pair.second(), amount_a, amount_b, true)?; - }: add_liquidity(RawOrigin::Signed(second_maker), trading_pair.first(), trading_pair.second(), amount_a, amount_b, Default::default(), true) - - // remove liquidity by liquid lp share - remove_liquidity { - let maker: AccountId = whitelisted_caller(); - let trading_pair = TradingPair::from_currency_ids(STABLECOIN, NATIVE).unwrap(); - inject_liquidity(maker.clone(), trading_pair.first(), trading_pair.second(), 100 * dollar(trading_pair.first()), 10_000 * dollar(trading_pair.second()), false)?; - }: remove_liquidity(RawOrigin::Signed(maker), trading_pair.first(), trading_pair.second(), 50 * dollar(trading_pair.first()), Default::default(), Default::default(), false) - - // remove liquidity by withdraw staking lp share - remove_liquidity_by_unstake { - let maker: AccountId = whitelisted_caller(); - let trading_pair = TradingPair::from_currency_ids(STABLECOIN, NATIVE).unwrap(); - inject_liquidity(maker.clone(), trading_pair.first(), trading_pair.second(), 100 * dollar(trading_pair.first()), 10_000 * dollar(trading_pair.second()), true)?; - }: remove_liquidity(RawOrigin::Signed(maker), trading_pair.first(), trading_pair.second(), 50 * dollar(trading_pair.first()), Default::default(), Default::default(), true) - - swap_with_exact_supply { - let u in 2 .. ::TradingPathLimit::get(); - - let maker: AccountId = account("maker", 0, SEED); - let taker: AccountId = whitelisted_caller(); - - let mut path: Vec = vec![]; - for i in 1 .. u { - if i == 1 { - let cur0 = CURRENCY_LIST[0]; - let cur1 = CURRENCY_LIST[1]; - path.push(cur0); - path.push(cur1); - inject_liquidity(maker.clone(), cur0, cur1, 10_000 * dollar(cur0), 10_000 * dollar(cur1), false)?; - } else { - path.push(CURRENCY_LIST[i as usize]); - inject_liquidity(maker.clone(), CURRENCY_LIST[i as usize - 1], CURRENCY_LIST[i as usize], 10_000 * dollar(CURRENCY_LIST[i as usize - 1]), 10_000 * dollar(CURRENCY_LIST[i as usize]), false)?; - } - } - - >::update_balance(path[0], &taker, (10_000 * dollar(path[0])).unique_saturated_into())?; - }: swap_with_exact_supply(RawOrigin::Signed(taker.clone()), path.clone(), 100 * dollar(path[0]), 0) - verify { - let path_limit: u32 = ::TradingPathLimit::get(); - // would panic the benchmark anyways, must add new currencies to CURRENCY_LIST for benchmarking to work - assert!( path_limit < CURRENCY_LIST.len() as u32); - } - - swap_with_exact_target { - let u in 2 .. ::TradingPathLimit::get(); - - let maker: AccountId = account("maker", 0, SEED); - let taker: AccountId = whitelisted_caller(); - - let mut path: Vec = vec![]; - for i in 1 .. u { - if i == 1 { - let cur0 = CURRENCY_LIST[0]; - let cur1 = CURRENCY_LIST[1]; - path.push(cur0); - path.push(cur1); - inject_liquidity(maker.clone(), cur0, cur1, 10_000 * dollar(cur0), 10_000 * dollar(cur1), false)?; - } else { - path.push(CURRENCY_LIST[i as usize]); - inject_liquidity(maker.clone(), CURRENCY_LIST[i as usize - 1], CURRENCY_LIST[i as usize], 10_000 * dollar(CURRENCY_LIST[i as usize - 1]), 10_000 * dollar(CURRENCY_LIST[i as usize]), false)?; - } - } - - >::update_balance(path[0], &taker, (10_000 * dollar(path[0])).unique_saturated_into())?; - }: swap_with_exact_target(RawOrigin::Signed(taker.clone()), path.clone(), 10 * dollar(path[path.len() - 1]), 100 * dollar(path[0])) - verify { - let path_limit: u32 = ::TradingPathLimit::get(); - // would panic the benchmark anyways, must add new currencies to CURRENCY_LIST for benchmarking to work - assert!(path_limit < CURRENCY_LIST.len() as u32); - } - - refund_provision { - let founder: AccountId = whitelisted_caller(); - let trading_pair = TradingPair::from_currency_ids(STABLECOIN, NATIVE).unwrap(); - if let TradingPairStatus::Enabled = Dex::trading_pair_statuses(trading_pair) { - Dex::disable_trading_pair(RawOrigin::Root.into(), trading_pair.first(), trading_pair.second())?; - } - Dex::list_provisioning( - RawOrigin::Root.into(), - trading_pair.first(), - trading_pair.second(), - dollar(trading_pair.first()), - dollar(trading_pair.second()), - 10 * dollar(trading_pair.first()), - 10 * dollar(trading_pair.second()), - 0 - )?; - - // set balance - >::update_balance(trading_pair.first(), &founder, (100 * dollar(trading_pair.first())).unique_saturated_into())?; - >::update_balance(trading_pair.second(), &founder, (100 * dollar(trading_pair.second())).unique_saturated_into())?; - - Dex::add_provision( - RawOrigin::Signed(founder.clone()).into(), - trading_pair.first(), - trading_pair.second(), - dollar(trading_pair.first()), - dollar(trading_pair.second()) - )?; - - System::set_block_number(ExtendedProvisioningBlocks::get() + 1); - Dex::abort_provisioning( - RawOrigin::Signed(founder.clone()).into(), - trading_pair.first(), - trading_pair.second(), - )?; - }: _(RawOrigin::Signed(founder.clone()), founder.clone(), trading_pair.first(), trading_pair.second()) - - abort_provisioning { - let founder: AccountId = whitelisted_caller(); - let trading_pair = TradingPair::from_currency_ids(STABLECOIN, NATIVE).unwrap(); - if let TradingPairStatus::Enabled = Dex::trading_pair_statuses(trading_pair) { - Dex::disable_trading_pair(RawOrigin::Root.into(), trading_pair.first(), trading_pair.second())?; - } - Dex::list_provisioning( - RawOrigin::Root.into(), - trading_pair.first(), - trading_pair.second(), - dollar(trading_pair.first()), - dollar(trading_pair.second()), - 100 * dollar(trading_pair.first()), - 100 * dollar(trading_pair.second()), - 0 - )?; - - // set balance - >::update_balance(trading_pair.first(), &founder, (100 * dollar(trading_pair.first())).unique_saturated_into())?; - >::update_balance(trading_pair.second(), &founder, (100 * dollar(trading_pair.second())).unique_saturated_into())?; - - Dex::add_provision( - RawOrigin::Signed(founder.clone()).into(), - trading_pair.first(), - trading_pair.second(), - 10 * dollar(trading_pair.first()), - 10 * dollar(trading_pair.second()), - )?; - - System::set_block_number(ExtendedProvisioningBlocks::get() + 1); - }: _(RawOrigin::Signed(whitelisted_caller()), trading_pair.first(), trading_pair.second()) -} - -#[cfg(test)] -mod tests { - use super::*; - use crate::benchmarking::utils::tests::new_test_ext; - use orml_benchmarking::impl_benchmark_test_suite; - - impl_benchmark_test_suite!(new_test_ext(),); -} diff --git a/runtime/mandala/src/benchmarking/dex_oracle.rs b/runtime/mandala/src/benchmarking/dex_oracle.rs deleted file mode 100644 index 0422b1109..000000000 --- a/runtime/mandala/src/benchmarking/dex_oracle.rs +++ /dev/null @@ -1,81 +0,0 @@ -// This file is part of Acala. - -// Copyright (C) 2020-2025 Acala Foundation. -// SPDX-License-Identifier: GPL-3.0-or-later WITH Classpath-exception-2.0 - -// This program 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. - -// This program 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 this program. If not, see . - -use super::utils::{dollar, inject_liquidity, set_block_number_timestamp, NATIVE, STABLECOIN, STAKING}; -use crate::{AccountId, DexOracle, Runtime}; -use frame_benchmarking::whitelisted_caller; -use frame_support::traits::OnInitialize; -use frame_system::RawOrigin; -use orml_benchmarking::runtime_benchmarks; -use primitives::TradingPair; -use sp_std::prelude::*; - -runtime_benchmarks! { - { Runtime, module_dex_oracle } - - on_initialize_with_update_average_prices { - let n in 0 .. 3; - let u in 0 .. 3; - let caller: AccountId = whitelisted_caller(); - let trading_pair_list = vec![ - TradingPair::from_currency_ids(NATIVE, STABLECOIN).unwrap(), - TradingPair::from_currency_ids(NATIVE, STAKING).unwrap(), - TradingPair::from_currency_ids(STAKING, STABLECOIN).unwrap(), - ]; - - for i in 0 .. n { - let trading_pair = trading_pair_list[i as usize]; - inject_liquidity(caller.clone(), trading_pair.first(), trading_pair.second(), dollar(trading_pair.first()) * 100, dollar(trading_pair.second()) * 1000, false)?; - DexOracle::enable_average_price(RawOrigin::Root.into(), trading_pair.first(), trading_pair.second(), 240000)?; - } - for j in 0 .. u.min(n) { - let update_pair = trading_pair_list[j as usize]; - DexOracle::update_average_price_interval(RawOrigin::Root.into(), update_pair.first(), update_pair.second(), 24000)?; - } - }: { - set_block_number_timestamp(1, 24000); - DexOracle::on_initialize(1) - } - - enable_average_price { - let caller: AccountId = whitelisted_caller(); - inject_liquidity(caller, NATIVE, STABLECOIN, dollar(NATIVE), dollar(STABLECOIN), false)?; - }: _(RawOrigin::Root, NATIVE, STABLECOIN, 24000) - - - disable_average_price { - let caller: AccountId = whitelisted_caller(); - inject_liquidity(caller, NATIVE, STABLECOIN, dollar(NATIVE) * 100, dollar(STABLECOIN) * 1000, false)?; - DexOracle::enable_average_price(RawOrigin::Root.into(), NATIVE, STABLECOIN, 24000)?; - }: _(RawOrigin::Root, NATIVE, STABLECOIN) - - update_average_price_interval { - let caller: AccountId = whitelisted_caller(); - inject_liquidity(caller, NATIVE, STABLECOIN, dollar(NATIVE) * 100, dollar(STABLECOIN) * 1000, false)?; - DexOracle::enable_average_price(RawOrigin::Root.into(), NATIVE, STABLECOIN, 24000)?; - }: _(RawOrigin::Root, NATIVE, STABLECOIN, 240000) -} - -#[cfg(test)] -mod tests { - use super::*; - use crate::benchmarking::utils::tests::new_test_ext; - use orml_benchmarking::impl_benchmark_test_suite; - - impl_benchmark_test_suite!(new_test_ext(),); -} diff --git a/runtime/mandala/src/benchmarking/earning.rs b/runtime/mandala/src/benchmarking/earning.rs deleted file mode 100644 index b195efaea..000000000 --- a/runtime/mandala/src/benchmarking/earning.rs +++ /dev/null @@ -1,86 +0,0 @@ -// This file is part of Acala. - -// Copyright (C) 2020-2025 Acala Foundation. -// SPDX-License-Identifier: GPL-3.0-or-later WITH Classpath-exception-2.0 - -// This program 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. - -// This program 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 this program. If not, see . - -use super::utils::{dollar, set_balance, NATIVE}; -use crate::{ - AccountId, DispatchResult, Earning, Get, NativeTokenExistentialDeposit, Parameters, Runtime, RuntimeOrigin, - RuntimeParameters, System, -}; -use frame_benchmarking::whitelisted_caller; -use frame_system::RawOrigin; -use orml_benchmarking::runtime_benchmarks; -use sp_runtime::Permill; - -fn make_max_unbonding_chunk(who: AccountId) -> DispatchResult { - System::set_block_number(0); - set_balance(NATIVE, &who, 100 * dollar(NATIVE)); - let max_unlock_chunk: u32 = ::MaxUnbondingChunks::get(); - Earning::bond(RuntimeOrigin::signed(who.clone()), 10 * dollar(NATIVE))?; - for _ in 0..(max_unlock_chunk) { - System::set_block_number(System::block_number() + 1); - Earning::unbond(RuntimeOrigin::signed(who.clone()), NativeTokenExistentialDeposit::get())?; - } - - Ok(()) -} - -runtime_benchmarks! { - {Runtime, module_earning} - - bond { - let caller: AccountId = whitelisted_caller(); - set_balance(NATIVE, &caller, dollar(NATIVE)); - }: _(RawOrigin::Signed(caller), NativeTokenExistentialDeposit::get()) - - unbond_instant { - let caller: AccountId = whitelisted_caller(); - set_balance(NATIVE, &caller, dollar(NATIVE)); - Parameters::set_parameter( - RawOrigin::Root.into(), - RuntimeParameters::Earning(module_earning::Parameters::InstantUnstakeFee(module_earning::InstantUnstakeFee, Some(Permill::from_percent(10)))) - )?; - Earning::bond(RuntimeOrigin::signed(caller.clone()), 2 * NativeTokenExistentialDeposit::get())?; - }: _(RawOrigin::Signed(caller), NativeTokenExistentialDeposit::get()) - - unbond { - let caller: AccountId = whitelisted_caller(); - set_balance(NATIVE, &caller, dollar(NATIVE)); - Earning::bond(RuntimeOrigin::signed(caller.clone()), dollar(NATIVE))?; - }: _(RawOrigin::Signed(caller), NativeTokenExistentialDeposit::get()) - - rebond { - let caller: AccountId = whitelisted_caller(); - make_max_unbonding_chunk(caller.clone())?; - }: _(RawOrigin::Signed(caller), 10 * dollar(NATIVE)) - - withdraw_unbonded { - let caller: AccountId = whitelisted_caller(); - make_max_unbonding_chunk(caller.clone())?; - // large number to unlock all chunks - System::set_block_number(1_000_000); - }: _(RawOrigin::Signed(caller)) -} - -#[cfg(test)] -mod tests { - use super::*; - use crate::benchmarking::utils::tests::new_test_ext; - use orml_benchmarking::impl_benchmark_test_suite; - - impl_benchmark_test_suite!(new_test_ext(),); -} diff --git a/runtime/mandala/src/benchmarking/emergency_shutdown.rs b/runtime/mandala/src/benchmarking/emergency_shutdown.rs deleted file mode 100644 index 167701687..000000000 --- a/runtime/mandala/src/benchmarking/emergency_shutdown.rs +++ /dev/null @@ -1,85 +0,0 @@ -// This file is part of Acala. - -// Copyright (C) 2020-2025 Acala Foundation. -// SPDX-License-Identifier: GPL-3.0-or-later WITH Classpath-exception-2.0 - -// This program 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. - -// This program 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 this program. If not, see . - -use crate::{AccountId, CdpTreasury, CurrencyId, EmergencyShutdown, Price, Runtime}; - -use super::{ - get_benchmarking_collateral_currency_ids, - utils::{dollar, feed_price, set_balance, STABLECOIN}, -}; -use frame_benchmarking::{account, whitelisted_caller}; -use frame_system::RawOrigin; -use module_support::CDPTreasury; -use orml_benchmarking::runtime_benchmarks; -use sp_runtime::traits::One; -use sp_std::vec; - -const SEED: u32 = 0; - -runtime_benchmarks! { - { Runtime, module_emergency_shutdown } - - emergency_shutdown { - let c in 0 .. get_benchmarking_collateral_currency_ids().len() as u32; - let currency_ids = get_benchmarking_collateral_currency_ids(); - let mut values = vec![]; - - for i in 0 .. c { - values.push((currency_ids[i as usize], Price::one())); - } - feed_price(values.try_into().unwrap())?; - }: _(RawOrigin::Root) - - open_collateral_refund { - EmergencyShutdown::emergency_shutdown(RawOrigin::Root.into())?; - }: _(RawOrigin::Root) - - refund_collaterals { - let c in 0 .. get_benchmarking_collateral_currency_ids().len() as u32; - let currency_ids = get_benchmarking_collateral_currency_ids(); - let funder: AccountId = account("funder", 0, SEED); - let caller: AccountId = whitelisted_caller(); - let mut values = vec![]; - - for i in 0 .. c { - let currency_id = currency_ids[i as usize]; - if matches!(currency_id, CurrencyId::StableAssetPoolToken(_)) { - continue; - } - values.push((currency_id, Price::one())); - set_balance(currency_id, &funder, 100 * dollar(currency_id)); - CdpTreasury::deposit_collateral(&funder, currency_id, 100 * dollar(currency_id))?; - } - feed_price(values)?; - - CdpTreasury::issue_debit(&caller, 1_000 * dollar(STABLECOIN), true)?; - CdpTreasury::issue_debit(&funder, 1_000 * dollar(STABLECOIN), true)?; - - EmergencyShutdown::emergency_shutdown(RawOrigin::Root.into())?; - EmergencyShutdown::open_collateral_refund(RawOrigin::Root.into())?; - }: _(RawOrigin::Signed(caller), 1_000 * dollar(STABLECOIN)) -} - -#[cfg(test)] -mod tests { - use super::*; - use crate::benchmarking::utils::tests::new_test_ext; - use orml_benchmarking::impl_benchmark_test_suite; - - impl_benchmark_test_suite!(new_test_ext(),); -} diff --git a/runtime/mandala/src/benchmarking/evm.rs b/runtime/mandala/src/benchmarking/evm.rs deleted file mode 100644 index b7a4a0513..000000000 --- a/runtime/mandala/src/benchmarking/evm.rs +++ /dev/null @@ -1,335 +0,0 @@ -// This file is part of Acala. - -// Copyright (C) 2020-2025 Acala Foundation. -// SPDX-License-Identifier: GPL-3.0-or-later WITH Classpath-exception-2.0 - -// This program 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. - -// This program 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 this program. If not, see . - -use crate::{AccountId, EvmAccounts, Runtime, RuntimeEvent, RuntimeOrigin, System, EVM}; - -use super::utils::{dollar, set_balance, NATIVE}; -use frame_system::RawOrigin; -use module_evm::MaxCodeSize; -use module_support::AddressMapping; -use orml_benchmarking::{runtime_benchmarks, whitelist_account}; -use sp_core::{H160, H256}; -use sp_io::hashing::keccak_256; -use sp_runtime::DispatchError; -use sp_std::{str::FromStr, vec}; - -fn contract_addr() -> H160 { - H160::from_str("0x5e0b4bfa0b55932a3587e648c3552a6515ba56b1").unwrap() -} - -fn alice() -> libsecp256k1::SecretKey { - libsecp256k1::SecretKey::parse(&keccak_256(b"Alice")).unwrap() -} - -fn bob() -> libsecp256k1::SecretKey { - libsecp256k1::SecretKey::parse(&keccak_256(b"Bob")).unwrap() -} - -fn deploy_contract(caller: AccountId) -> Result { - System::set_block_number(1); - EVM::create( - RuntimeOrigin::signed(caller.clone()), - FACTORY_CONTRACT.to_vec(), - 0, - 1000000000, - 1000000000, - vec![], - ) - .map_or_else(|e| Err(e.error), |_| Ok(()))?; - - System::assert_last_event(RuntimeEvent::EVM(module_evm::Event::Created { - from: module_evm_accounts::EvmAddressMapping::::get_evm_address(&caller).unwrap(), - contract: contract_addr(), - logs: vec![], - used_gas: 132225, - used_storage: 10367, - })); - Ok(contract_addr()) -} - -pub fn alice_account_id() -> AccountId { - let address = EvmAccounts::eth_address(&alice()); - evm_to_account_id(address) -} - -pub fn bob_account_id() -> AccountId { - let address = EvmAccounts::eth_address(&bob()); - evm_to_account_id(address) -} - -fn evm_to_account_id(address: H160) -> AccountId { - let mut data = [0u8; 32]; - data[0..4].copy_from_slice(b"evm:"); - data[4..24].copy_from_slice(&address[..]); - AccountId::from(Into::<[u8; 32]>::into(data)) -} - -// pragma solidity 0.8.2; -// -// contract Empty { } -const EMPTY_CONTRACT: [u8; 92] = hex_literal::hex!("6080604052348015600f57600080fd5b50603f80601d6000396000f3fe6080604052600080fdfea2646970667358221220e2641e5566296523edeafd776846b0e535aac278dfcf496804a865948b29646064736f6c63430008020033"); - -// pragma solidity 0.8.2; -// -// contract Storage { -// uint256 public number; -// -// function store(uint256 num) public { -// number = num; -// } -// } -const STORAGE_CONTRACT: [u8; 332] = hex_literal::hex!("608060405234801561001057600080fd5b5061012c806100206000396000f3fe6080604052348015600f57600080fd5b506004361060325760003560e01c80636057361d1460375780638381f58a14604f575b600080fd5b604d600480360381019060499190608c565b6069565b005b60556073565b6040516060919060bf565b60405180910390f35b8060008190555050565b60005481565b60008135905060868160e2565b92915050565b600060208284031215609d57600080fd5b600060a9848285016079565b91505092915050565b60b98160d8565b82525050565b600060208201905060d2600083018460b2565b92915050565b6000819050919050565b60e98160d8565b811460f357600080fd5b5056fea2646970667358221220b161a9e6cc3d4aac8bc0fd65e420da7555db59fefe6a1d4e8e7eea98e99b293b64736f6c63430008020033"); - -// pragma solidity ^0.5.0; -// -// contract Factory { -// Contract[] newContracts; -// -// function createContract () public payable { -// Contract newContract = new Contract(); -// newContracts.push(newContract); -// } -// } -// -// contract Contract {} -const FACTORY_CONTRACT: [u8; 399] = hex_literal::hex!("608060405234801561001057600080fd5b5061016f806100206000396000f3fe608060405260043610610041576000357c0100000000000000000000000000000000000000000000000000000000900463ffffffff168063412a5a6d14610046575b600080fd5b61004e610050565b005b600061005a6100e2565b604051809103906000f080158015610076573d6000803e3d6000fd5b50905060008190806001815401808255809150509060018203906000526020600020016000909192909190916101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff1602179055505050565b6040516052806100f28339019056fe6080604052348015600f57600080fd5b50603580601d6000396000f3fe6080604052600080fdfea165627a7a7230582092dc1966a8880ddf11e067f9dd56a632c11a78a4afd4a9f05924d427367958cc0029a165627a7a723058202b2cc7384e11c452cdbf39b68dada2d5e10a632cc0174a354b8b8c83237e28a40029"); - -runtime_benchmarks! { - { Runtime, module_evm } - - create { - let alice_account = alice_account_id(); - set_balance(NATIVE, &alice_account, 1_000_000 * dollar(NATIVE)); - }: _(RawOrigin::Signed(alice_account), EMPTY_CONTRACT.to_vec(), 0, 21_000_000, 100_000, vec![]) - verify { - // contract address when it gets deployed - let contract_address = H160::from(hex_literal::hex!("5e0b4bfa0b55932a3587e648c3552a6515ba56b1")); - let code_hash = EVM::code_hash_at_address(&contract_address); - assert!(module_evm::Codes::::contains_key(code_hash)); - } - - create2 { - let salt = H256::repeat_byte(1); - let alice_account = alice_account_id(); - set_balance(NATIVE, &alice_account, 1_000_000 * dollar(NATIVE)); - }: _(RawOrigin::Signed(alice_account), EMPTY_CONTRACT.to_vec(), salt, 0, 21_000_000, 100_000, vec![]) - verify { - // contract address when it gets deployed - let contract_address = H160::from(hex_literal::hex!("f6930000a8679e0c96af73e73c02f163e34b9d70")); - let code_hash = EVM::code_hash_at_address(&contract_address); - assert!(module_evm::Codes::::contains_key(code_hash)); - } - - create_nft_contract { - let account_id = ::TreasuryAccount::get(); - set_balance(NATIVE, &account_id, 1_000_000 * dollar(NATIVE)); - let address = primitives::evm::MIRRORED_TOKENS_ADDRESS_START | H160::from_low_u64_be(EVM::network_contract_index()); - }: _(RawOrigin::Root, EMPTY_CONTRACT.to_vec(), 0, 2_100_000, 15_000, vec![]) - verify { - let code_hash = EVM::code_hash_at_address(&address); - assert!(module_evm::Codes::::contains_key(code_hash)); - } - - create_predeploy_contract { - let account_id = ::TreasuryAccount::get(); - set_balance(NATIVE, &account_id, 1_000_000 * dollar(NATIVE)); - let address = H160::from_low_u64_be(1); - }: _(RawOrigin::Root, address, EMPTY_CONTRACT.to_vec(), 0, 2_100_000, 15_000, vec![]) - verify { - let code_hash = EVM::code_hash_at_address(&address); - assert!(module_evm::Codes::::contains_key(code_hash)); - } - - call { - // Storage.store(1) - let input = hex_literal::hex!("6057361d0000000000000000000000000000000000000000000000000000000000000001").to_vec(); - let alice_account = alice_account_id(); - set_balance(NATIVE, &alice_account, 1_000_000 * dollar(NATIVE)); - - // contract address when it gets deployed - let contract_address = H160::from(hex_literal::hex!("5e0b4bfa0b55932a3587e648c3552a6515ba56b1")); - - frame_support::assert_ok!(EVM::create(RuntimeOrigin::signed(alice_account.clone()), STORAGE_CONTRACT.to_vec(), 0, 21_000_000, 100_000, vec![])); - - let code_hash = EVM::code_hash_at_address(&contract_address); - assert!(module_evm::Codes::::contains_key(code_hash)); - - // Storage::number - let hashed_key = module_evm::AccountStorages::::hashed_key_for(&contract_address, H256::zero()); - frame_benchmarking::benchmarking::add_to_whitelist(hashed_key.into()); - - }: _(RawOrigin::Signed(alice_account), contract_address, input, 0, 21_000_000, 100_000, vec![]) - verify { - assert_eq!(module_evm::AccountStorages::::get(&contract_address, H256::zero()), H256::from_low_u64_be(1)); - } - - transfer_maintainer { - let alice_account = alice_account_id(); - - set_balance(NATIVE, &alice_account, 1_000_000 * dollar(NATIVE)); - set_balance(NATIVE, &bob_account_id(), 1_000 * dollar(NATIVE)); - let contract = deploy_contract(alice_account_id())?; - let bob_address = EvmAccounts::eth_address(&bob()); - - whitelist_account!(alice_account); - }: _(RawOrigin::Signed(alice_account_id()), contract, bob_address) - - publish_contract { - let alice_account = alice_account_id(); - - set_balance(NATIVE, &alice_account, 1_000_000_000 * dollar(NATIVE)); - set_balance(NATIVE, &bob_account_id(), 1_000 * dollar(NATIVE)); - - EVM::enable_contract_development(RuntimeOrigin::signed(alice_account_id()))?; - - let contract = deploy_contract(alice_account_id())?; - - whitelist_account!(alice_account); - }: _(RawOrigin::Signed(alice_account_id()), contract) - - publish_free { - let alice_account = alice_account_id(); - - set_balance(NATIVE, &alice_account, 1_000_000 * dollar(NATIVE)); - set_balance(NATIVE, &bob_account_id(), 1_000 * dollar(NATIVE)); - - EVM::enable_contract_development(RuntimeOrigin::signed(alice_account_id()))?; - let contract = deploy_contract(alice_account_id())?; - }: _(RawOrigin::Root, contract) - - enable_contract_development { - let alice_account = alice_account_id(); - - set_balance(NATIVE, &alice_account, 1_000 * dollar(NATIVE)); - - whitelist_account!(alice_account); - }: _(RawOrigin::Signed(alice_account_id())) - - disable_contract_development { - let alice_account = alice_account_id(); - - set_balance(NATIVE, &alice_account, 1_000 * dollar(NATIVE)); - EVM::enable_contract_development(RuntimeOrigin::signed(alice_account_id()))?; - - whitelist_account!(alice_account); - }: _(RawOrigin::Signed(alice_account_id())) - - set_code { - let c in 0..MaxCodeSize::get(); - let alice_account = alice_account_id(); - - set_balance(NATIVE, &alice_account, 1_000_000 * dollar(NATIVE)); - - EVM::enable_contract_development(RuntimeOrigin::signed(alice_account_id()))?; - let contract = deploy_contract(alice_account_id())?; - - let new_contract = vec![0; c as usize]; - - whitelist_account!(alice_account); - }: _(RawOrigin::Signed(alice_account_id()), contract, new_contract) - - selfdestruct { - let alice_account = alice_account_id(); - - set_balance(NATIVE, &alice_account, 1_000_000 * dollar(NATIVE)); - - EVM::enable_contract_development(RuntimeOrigin::signed(alice_account_id()))?; - let contract = deploy_contract(alice_account_id())?; - - whitelist_account!(alice_account); - }: _(RawOrigin::Signed(alice_account_id()), contract) -} - -#[cfg(test)] -mod tests { - use super::*; - use crate::benchmarking::utils::tests::new_test_ext; - use module_evm::Runner; - use orml_benchmarking::impl_benchmark_test_suite; - - impl_benchmark_test_suite!(new_test_ext(),); - - #[test] - fn create_gas_usage() { - new_test_ext().execute_with(|| { - let alice_account = alice_account_id(); - set_balance(NATIVE, &alice_account, 1_000_000 * dollar(NATIVE)); - let caller = module_evm_accounts::EvmAddressMapping::::get_or_create_evm_address(&alice_account); - let config = ::config(); - let result = ::Runner::create( - caller, - EMPTY_CONTRACT.to_vec(), - 0, - 1_000_000, - 100_000, - vec![], - vec![], - config, - ) - .unwrap(); - assert!(result.exit_reason.is_succeed()); - assert_eq!( - result.value, - H160::from_str("0x5e0b4bfa0b55932a3587e648c3552a6515ba56b1").unwrap() - ); - assert_eq!(result.used_gas.as_u64(), module_evm::BASE_CREATE_GAS); - }); - } - - #[test] - fn call_gas_usage() { - new_test_ext().execute_with(|| { - let alice_account = alice_account_id(); - set_balance(NATIVE, &alice_account, 1_000_000 * dollar(NATIVE)); - let caller = module_evm_accounts::EvmAddressMapping::::get_or_create_evm_address(&alice_account); - let config = ::config(); - let result = ::Runner::create( - caller, - STORAGE_CONTRACT.to_vec(), - 0, - 1_000_000, - 100_000, - vec![], - vec![], - config, - ) - .unwrap(); - let address = H160::from_str("0x5e0b4bfa0b55932a3587e648c3552a6515ba56b1").unwrap(); - assert!(result.exit_reason.is_succeed()); - assert_eq!(result.value, address); - - let input = - hex_literal::hex!("6057361d0000000000000000000000000000000000000000000000000000000000000001").to_vec(); - let result = ::Runner::call( - caller, - caller, - address, - input, - 0, - 1_000_000, - 100_000, - vec![], - vec![], - config, - ) - .unwrap(); - assert!(result.exit_reason.is_succeed()); - assert_eq!(result.used_gas.as_u64(), module_evm::BASE_CALL_GAS); - }); - } -} diff --git a/runtime/mandala/src/benchmarking/evm_accounts.rs b/runtime/mandala/src/benchmarking/evm_accounts.rs deleted file mode 100644 index 1a23736e8..000000000 --- a/runtime/mandala/src/benchmarking/evm_accounts.rs +++ /dev/null @@ -1,66 +0,0 @@ -// This file is part of Acala. - -// Copyright (C) 2020-2025 Acala Foundation. -// SPDX-License-Identifier: GPL-3.0-or-later WITH Classpath-exception-2.0 - -// This program 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. - -// This program 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 this program. If not, see . - -use crate::{AccountId, EvmAccounts, Runtime}; - -use super::utils::{dollar, set_balance, NATIVE}; -use frame_benchmarking::{account, whitelisted_caller}; -use frame_system::RawOrigin; -use orml_benchmarking::runtime_benchmarks; -use sp_io::hashing::keccak_256; - -const SEED: u32 = 0; - -fn alice() -> libsecp256k1::SecretKey { - libsecp256k1::SecretKey::parse(&keccak_256(b"Alice")).unwrap() -} - -fn bob() -> libsecp256k1::SecretKey { - libsecp256k1::SecretKey::parse(&keccak_256(b"Bob")).unwrap() -} - -pub fn bob_account_id() -> AccountId { - let address = EvmAccounts::eth_address(&bob()); - let mut data = [0u8; 32]; - data[0..4].copy_from_slice(b"evm:"); - data[4..24].copy_from_slice(&address[..]); - AccountId::from(Into::<[u8; 32]>::into(data)) -} - -runtime_benchmarks! { - { Runtime, module_evm_accounts } - - claim_account { - let caller: AccountId = whitelisted_caller(); - let eth: AccountId = account("eth", 0, SEED); - set_balance(NATIVE, &bob_account_id(), 1_000 * dollar(NATIVE)); - }: _(RawOrigin::Signed(caller), EvmAccounts::eth_address(&alice()), EvmAccounts::eth_sign(&alice(), &caller)) - - claim_default_account { - let caller = whitelisted_caller(); - }: _(RawOrigin::Signed(caller)) -} - -#[cfg(test)] -mod tests { - use super::*; - use crate::benchmarking::utils::tests::new_test_ext; - use orml_benchmarking::impl_benchmark_test_suite; - - impl_benchmark_test_suite!(new_test_ext(),); -} diff --git a/runtime/mandala/src/benchmarking/homa.rs b/runtime/mandala/src/benchmarking/homa.rs deleted file mode 100644 index 37d623d25..000000000 --- a/runtime/mandala/src/benchmarking/homa.rs +++ /dev/null @@ -1,177 +0,0 @@ -// This file is part of Acala. - -// Copyright (C) 2020-2025 Acala Foundation. -// SPDX-License-Identifier: GPL-3.0-or-later WITH Classpath-exception-2.0 - -// This program 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. - -// This program 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 this program. If not, see . - -use crate::{ - AccountId, ActiveSubAccountsIndexList, Balance, Currencies, Homa, Rate, RedeemThreshold, RelaychainDataProvider, - Runtime, -}; - -use super::utils::{set_balance, LIQUID, STAKING}; -use frame_benchmarking::{account, whitelisted_caller}; -use frame_support::traits::{ExistenceRequirement, OnInitialize}; -use frame_system::RawOrigin; -use module_homa::UnlockChunk; -use orml_benchmarking::runtime_benchmarks; -use orml_traits::MultiCurrency; -use sp_runtime::{traits::BlockNumberProvider, FixedPointNumber}; -use sp_std::prelude::*; - -const SEED: u32 = 0; - -runtime_benchmarks! { - { Runtime, module_homa } - - on_initialize { - }: { - Homa::on_initialize(1) - } - - on_initialize_with_bump_era { - let n in 1 .. 50; - let minter: AccountId = account("minter", 0, SEED); - let sub_account_index = ActiveSubAccountsIndexList::get().first().unwrap().clone(); - - set_balance(STAKING, &minter, 1_000_000_000_000_000); - - for i in 0 .. n { - let redeemer = account("redeemer", i, SEED); - set_balance(LIQUID, &redeemer, 1_000_000_000_000_000); - } - - // need to process unlocking - Homa::reset_ledgers( - RawOrigin::Root.into(), - vec![(sub_account_index, Some(1_000_000_000_000_000), Some(vec![UnlockChunk{value: 1_000_000_000_000, era: 10}]))] - )?; - Homa::reset_current_era(RawOrigin::Root.into(), 9)?; - - Homa::update_homa_params( - RawOrigin::Root.into(), - Some(10_000_000_000_000_000), - Some(Rate::saturating_from_rational(1, 100)), - Some(Rate::saturating_from_rational(20, 100)), - None, - None, - )?; - RelaychainDataProvider::::set_block_number(10); - Homa::update_bump_era_params(RawOrigin::Root.into(), None, Some(1))?; - - // need to process to bond - Homa::mint(RawOrigin::Signed(minter).into(), 100_000_000_000_000)?; - - // need to process redeem request - for i in 0 .. n { - let redeemer = account("redeemer", i, SEED); - Homa::request_redeem(RawOrigin::Signed(redeemer).into(), 100_000_000_000_000, false)?; - } - }: { - Homa::on_initialize(1) - } - - mint { - let caller: AccountId = whitelisted_caller(); - let amount = 10_000_000_000_000; - - Homa::update_homa_params( - RawOrigin::Root.into(), - Some(amount * 10), - Some(Rate::saturating_from_rational(1, 10000)), - None, - None, - None, - )?; - set_balance(STAKING, &caller, amount * 2); - }: _(RawOrigin::Signed(caller), amount) - - request_redeem { - let caller: AccountId = whitelisted_caller(); - let amount = 10_000_000_000_000; - - set_balance(LIQUID, &caller, amount * 2); - }: _(RawOrigin::Signed(caller), amount, true) - - fast_match_redeems { - let n in 1 .. 50; - let caller: AccountId = whitelisted_caller(); - let minter: AccountId = account("minter", 0, SEED); - let mint_amount = 1_000_000_000_000_000; - - set_balance(STAKING, &minter, mint_amount * 2); - Homa::update_homa_params( - RawOrigin::Root.into(), - Some(mint_amount * 10), - Some(Rate::saturating_from_rational(1, 10000)), - None, - None, - None, - )?; - Homa::mint(RawOrigin::Signed(minter.clone()).into(), mint_amount)?; - - let mut redeem_request_list: Vec = vec![]; - let redeem_amount = 10_000_000_000_000; - for i in 0 .. n { - let redeemer = account("redeemer", i, SEED); - >::transfer(LIQUID, &minter, &redeemer, redeem_amount * 2, ExistenceRequirement::AllowDeath)?; - Homa::request_redeem(RawOrigin::Signed(redeemer.clone()).into(), redeem_amount, true)?; - redeem_request_list.push(redeemer); - } - }: _(RawOrigin::Signed(caller), redeem_request_list) - - claim_redemption { - let caller: AccountId = whitelisted_caller(); - let redeemer: AccountId = account("redeemer", 0, SEED); - let redeption_amount = 1_000_000_000_000; - - module_homa::Unbondings::::insert(&redeemer, 1, redeption_amount); - set_balance(STAKING, &Homa::account_id(), redeption_amount); - module_homa::UnclaimedRedemption::::put(redeption_amount); - Homa::reset_current_era(RawOrigin::Root.into(), 1)?; - }: _(RawOrigin::Signed(caller), redeemer) - - update_homa_params {}: _( - RawOrigin::Root, - Some(1_000_000_000_000), - Some(Rate::saturating_from_rational(1, 100)), - Some(Rate::saturating_from_rational(1, 100)), - Some(Rate::saturating_from_rational(1, 100)), - Some(7) - ) - - update_bump_era_params { - RelaychainDataProvider::::set_block_number(10000); - }: _(RawOrigin::Root, Some(3000), Some(7200)) - - reset_ledgers { - let n in 0 .. 10; - let mut updates: Vec<(u16, Option, Option>)> = vec![]; - for i in 0..n { - updates.push((i.try_into().unwrap(), Some(1), Some(vec![UnlockChunk{value: 1, era: 1}]))) - } - }: _(RawOrigin::Root, updates) - - reset_current_era {}: _(RawOrigin::Root, 1) -} - -#[cfg(test)] -mod tests { - use super::*; - use crate::benchmarking::utils::tests::new_test_ext; - use orml_benchmarking::impl_benchmark_test_suite; - - impl_benchmark_test_suite!(new_test_ext(),); -} diff --git a/runtime/mandala/src/benchmarking/homa_validator_list.rs b/runtime/mandala/src/benchmarking/homa_validator_list.rs deleted file mode 100644 index e9215078f..000000000 --- a/runtime/mandala/src/benchmarking/homa_validator_list.rs +++ /dev/null @@ -1,152 +0,0 @@ -// This file is part of Acala. - -// Copyright (C) 2020-2025 Acala Foundation. -// SPDX-License-Identifier: GPL-3.0-or-later WITH Classpath-exception-2.0 - -// This program 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. - -// This program 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 this program. If not, see . - -use crate::{ - AccountId, Balance, BondingDuration, Homa, HomaValidatorList, MinBondAmount, Runtime, ValidatorInsuranceThreshold, -}; - -use super::utils::{set_balance, LIQUID}; -use frame_benchmarking::{account, whitelisted_caller}; -use frame_system::RawOrigin; -use module_homa_validator_list::SlashInfo; -use orml_benchmarking::runtime_benchmarks; -use sp_std::{prelude::*, vec}; - -const SEED: u32 = 0; - -runtime_benchmarks! { - { Runtime, module_homa_validator_list } - - bond { - let caller: AccountId = whitelisted_caller(); - let validator: AccountId = account("validator", 0, SEED); - set_balance(LIQUID, &caller, MinBondAmount::get() * 10); - }: _(RawOrigin::Signed(caller), validator, MinBondAmount::get()) - - unbond { - let caller: AccountId = whitelisted_caller(); - let validator: AccountId = account("validator", 0, SEED); - let amount: Balance = MinBondAmount::get() * 10; - - set_balance(LIQUID, &caller, amount); - HomaValidatorList::bond( - RawOrigin::Signed(caller.clone()).into(), - validator.clone(), - amount - )?; - }: _(RawOrigin::Signed(caller), validator, amount) - - rebond { - let caller: AccountId = whitelisted_caller(); - let validator: AccountId = account("validator", 0, SEED); - - set_balance(LIQUID, &caller, MinBondAmount::get() * 10); - HomaValidatorList::bond( - RawOrigin::Signed(caller.clone()).into(), - validator.clone(), - MinBondAmount::get() * 10 - )?; - HomaValidatorList::unbond( - RawOrigin::Signed(caller.clone()).into(), - validator.clone(), - MinBondAmount::get() * 5 - )?; - }: _(RawOrigin::Signed(caller), validator, MinBondAmount::get() * 5) - - withdraw_unbonded { - let caller: AccountId = whitelisted_caller(); - let validator: AccountId = account("validator", 0, SEED); - - set_balance(LIQUID, &caller, MinBondAmount::get() * 10); - HomaValidatorList::bond( - RawOrigin::Signed(caller.clone()).into(), - validator.clone(), - MinBondAmount::get() * 10 - )?; - HomaValidatorList::unbond( - RawOrigin::Signed(caller.clone()).into(), - validator.clone(), - MinBondAmount::get() * 5 - )?; - Homa::force_bump_current_era(RawOrigin::Root.into(), BondingDuration::get())?; - }: _(RawOrigin::Signed(caller), validator) - - freeze { - let n in 1 .. 10; - let caller: AccountId = whitelisted_caller(); - let mut validators: Vec = vec![]; - - set_balance(LIQUID, &caller, MinBondAmount::get() * 100); - for i in 0 .. n { - let validator: AccountId = account("validator", i, SEED); - HomaValidatorList::bond( - RawOrigin::Signed(caller.clone()).into(), - validator.clone(), - MinBondAmount::get() - )?; - validators.push(validator); - } - }: _(RawOrigin::Root, validators) - - thaw { - let n in 1 .. 10; - let caller: AccountId = whitelisted_caller(); - let mut validators: Vec = vec![]; - - set_balance(LIQUID, &caller, MinBondAmount::get() * 100); - for i in 0 .. n { - let validator: AccountId = account("validator", i, SEED); - HomaValidatorList::bond( - RawOrigin::Signed(caller.clone()).into(), - validator.clone(), - MinBondAmount::get() - )?; - validators.push(validator); - } - HomaValidatorList::freeze(RawOrigin::Root.into(), validators.clone())?; - }: _(RawOrigin::Root, validators) - - slash { - let n in 1 .. 10; - let caller: AccountId = whitelisted_caller(); - let mut slashes: Vec> = vec![]; - - set_balance(LIQUID, &caller, ValidatorInsuranceThreshold::get() * 100); - for i in 0 .. n { - let validator: AccountId = account("validator", i, SEED); - HomaValidatorList::bond( - RawOrigin::Signed(caller.clone()).into(), - validator.clone(), - ValidatorInsuranceThreshold::get() * 10 - )?; - slashes.push(SlashInfo{ - validator, - token_amount: ValidatorInsuranceThreshold::get() * 9 - }); - } - }: _(RawOrigin::Root, slashes) -} - -#[cfg(test)] -mod tests { - use super::*; - use crate::benchmarking::utils::tests::new_test_ext; - use orml_benchmarking::impl_benchmark_test_suite; - - impl_benchmark_test_suite!(new_test_ext(),); -} diff --git a/runtime/mandala/src/benchmarking/honzon.rs b/runtime/mandala/src/benchmarking/honzon.rs deleted file mode 100644 index f446ad825..000000000 --- a/runtime/mandala/src/benchmarking/honzon.rs +++ /dev/null @@ -1,353 +0,0 @@ -// This file is part of Acala. - -// Copyright (C) 2020-2025 Acala Foundation. -// SPDX-License-Identifier: GPL-3.0-or-later WITH Classpath-exception-2.0 - -// This program 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. - -// This program 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 this program. If not, see . - -use crate::{ - AccountId, Amount, CdpEngine, CurrencyId, DepositPerAuthorization, ExistentialDeposits, Honzon, - NativeTokenExistentialDeposit, Price, Rate, Ratio, Runtime, -}; - -use super::{ - get_benchmarking_collateral_currency_ids, - utils::{ - dollar, feed_price, initialize_swap_pools, inject_liquidity, set_balance, LIQUID, NATIVE, STABLECOIN, STAKING, - }, -}; -use frame_benchmarking::{account, whitelisted_caller}; -use frame_system::RawOrigin; -use module_support::HonzonManager; -use orml_benchmarking::runtime_benchmarks; -use orml_traits::{Change, GetByKey}; -use sp_runtime::{ - traits::{AccountIdLookup, One, StaticLookup, UniqueSaturatedInto}, - FixedPointNumber, -}; -use sp_std::prelude::*; - -const SEED: u32 = 0; - -runtime_benchmarks! { - { Runtime, module_honzon } - - authorize { - let caller: AccountId = whitelisted_caller(); - let to: AccountId = account("to", 0, SEED); - let to_lookup = AccountIdLookup::unlookup(to); - - // set balance - set_balance(NATIVE, &caller, DepositPerAuthorization::get() + NativeTokenExistentialDeposit::get()); - }: _(RawOrigin::Signed(caller), STAKING, to_lookup) - - unauthorize { - let caller: AccountId = whitelisted_caller(); - let to: AccountId = account("to", 0, SEED); - let to_lookup = AccountIdLookup::unlookup(to); - - // set balance - set_balance(NATIVE, &caller, DepositPerAuthorization::get() + NativeTokenExistentialDeposit::get()); - Honzon::authorize( - RawOrigin::Signed(caller.clone()).into(), - STAKING, - to_lookup.clone() - )?; - }: _(RawOrigin::Signed(caller), STAKING, to_lookup) - - unauthorize_all { - let c in 0 .. get_benchmarking_collateral_currency_ids().len() as u32; - - let caller: AccountId = whitelisted_caller(); - let currency_ids = get_benchmarking_collateral_currency_ids(); - let to: AccountId = account("to", 0, SEED); - let to_lookup = AccountIdLookup::unlookup(to); - - // set balance - set_balance(NATIVE, &caller, DepositPerAuthorization::get().saturating_mul(c.into()) + NativeTokenExistentialDeposit::get()); - for i in 0 .. c { - Honzon::authorize( - RawOrigin::Signed(caller.clone()).into(), - currency_ids[i as usize], - to_lookup.clone(), - )?; - } - }: _(RawOrigin::Signed(caller)) - - // `adjust_loan`, best case: - // adjust both collateral and debit - adjust_loan { - let caller: AccountId = whitelisted_caller(); - let currency_id: CurrencyId = get_benchmarking_collateral_currency_ids()[0]; - let collateral_price = Price::one(); // 1 USD - let debit_value = 100 * dollar(STABLECOIN); - let debit_exchange_rate = CdpEngine::get_debit_exchange_rate(currency_id); - let debit_amount = debit_exchange_rate.reciprocal().unwrap().saturating_mul_int(debit_value); - let debit_amount: Amount = debit_amount.unique_saturated_into(); - let collateral_value = 10 * debit_value; - let collateral_amount = Price::saturating_from_rational(dollar(currency_id), dollar(STABLECOIN)).saturating_mul_int(collateral_value); - - // set balance - set_balance(currency_id, &caller, collateral_amount * 2); - - // feed price - feed_price(vec![(currency_id, collateral_price)])?; - - // set risk params - CdpEngine::set_collateral_params( - RawOrigin::Root.into(), - currency_id, - Change::NoChange, - Change::NewValue(Some(Ratio::saturating_from_rational(150, 100))), - Change::NewValue(Some(Rate::saturating_from_rational(10, 100))), - Change::NewValue(Some(Ratio::saturating_from_rational(150, 100))), - Change::NewValue(debit_value * 100), - )?; - }: _(RawOrigin::Signed(caller), currency_id, collateral_amount.try_into().unwrap(), debit_amount) - - transfer_loan_from { - let currency_id: CurrencyId = get_benchmarking_collateral_currency_ids()[0]; - let sender: AccountId = account("sender", 0, SEED); - let sender_lookup = AccountIdLookup::unlookup(sender.clone()); - let receiver: AccountId = whitelisted_caller(); - let receiver_lookup = AccountIdLookup::unlookup(receiver.clone()); - - let debit_value = 100 * dollar(STABLECOIN); - let debit_exchange_rate = CdpEngine::get_debit_exchange_rate(currency_id); - let debit_amount = debit_exchange_rate.reciprocal().unwrap().saturating_mul_int(debit_value); - let debit_amount: Amount = debit_amount.unique_saturated_into(); - let collateral_value = 10 * debit_value; - let collateral_amount = Price::saturating_from_rational(dollar(currency_id), dollar(STABLECOIN)).saturating_mul_int(collateral_value); - - // set balance - set_balance(currency_id, &sender, collateral_amount * 2); - set_balance(NATIVE, &sender, DepositPerAuthorization::get() + NativeTokenExistentialDeposit::get()); - - // feed price - feed_price(vec![(currency_id, Price::one())])?; - - // set risk params - CdpEngine::set_collateral_params( - RawOrigin::Root.into(), - currency_id, - Change::NoChange, - Change::NewValue(Some(Ratio::saturating_from_rational(150, 100))), - Change::NewValue(Some(Rate::saturating_from_rational(10, 100))), - Change::NewValue(Some(Ratio::saturating_from_rational(150, 100))), - Change::NewValue(debit_value * 100), - )?; - - // initialize sender's loan - Honzon::adjust_loan( - RawOrigin::Signed(sender.clone()).into(), - currency_id, - collateral_amount.try_into().unwrap(), - debit_amount, - )?; - - // authorize receiver - Honzon::authorize( - RawOrigin::Signed(sender.clone()).into(), - currency_id, - receiver_lookup, - )?; - }: _(RawOrigin::Signed(receiver), currency_id, sender_lookup) - - close_loan_has_debit_by_dex { - let currency_id: CurrencyId = STAKING; - let sender: AccountId = whitelisted_caller(); - let maker: AccountId = account("maker", 0, SEED); - let debit_value = 100 * dollar(STABLECOIN); - let debit_exchange_rate = CdpEngine::get_debit_exchange_rate(STAKING); - let debit_amount = debit_exchange_rate.reciprocal().unwrap().saturating_mul_int(debit_value); - let debit_amount: Amount = debit_amount.unique_saturated_into(); - let collateral_value = 10 * debit_value; - let collateral_amount = Price::saturating_from_rational(dollar(STAKING), dollar(STABLECOIN)).saturating_mul_int(collateral_value); - - // set balance and inject liquidity - set_balance(STAKING, &sender, (10 * collateral_amount) + ExistentialDeposits::get(&STAKING)); - initialize_swap_pools(maker)?; - - feed_price(vec![(STAKING, Price::one())])?; - - // set risk params - CdpEngine::set_collateral_params( - RawOrigin::Root.into(), - STAKING, - Change::NoChange, - Change::NewValue(Some(Ratio::saturating_from_rational(150, 100))), - Change::NewValue(Some(Rate::saturating_from_rational(10, 100))), - Change::NewValue(Some(Ratio::saturating_from_rational(150, 100))), - Change::NewValue(debit_value * 100), - )?; - - // initialize sender's loan - Honzon::adjust_loan( - RawOrigin::Signed(sender.clone()).into(), - STAKING, - (10 * collateral_amount).try_into().unwrap(), - debit_amount, - )?; - }: _(RawOrigin::Signed(sender), STAKING, collateral_amount) - - expand_position_collateral { - let currency_id: CurrencyId = STAKING; - let sender: AccountId = whitelisted_caller(); - let maker: AccountId = account("maker", 0, SEED); - let debit_value = 100 * dollar(STABLECOIN); - let debit_exchange_rate = CdpEngine::get_debit_exchange_rate(currency_id); - let debit_amount = debit_exchange_rate.reciprocal().unwrap().saturating_mul_int(debit_value); - let collateral_value = 10 * debit_value; - let collateral_amount = Price::saturating_from_rational(dollar(currency_id), dollar(STABLECOIN)).saturating_mul_int(collateral_value); - - // set balance and inject liquidity for trading path - set_balance(currency_id, &sender, (10 * collateral_amount) + ExistentialDeposits::get(¤cy_id)); - initialize_swap_pools(maker)?; - - feed_price(vec![(currency_id, Price::one())])?; - - // set risk params - CdpEngine::set_collateral_params( - RawOrigin::Root.into(), - currency_id, - Change::NoChange, - Change::NewValue(Some(Ratio::saturating_from_rational(150, 100))), - Change::NewValue(Some(Rate::saturating_from_rational(10, 100))), - Change::NewValue(Some(Ratio::saturating_from_rational(150, 100))), - Change::NewValue(debit_value * 100), - )?; - - // initialize sender's loan - Honzon::adjust_loan( - RawOrigin::Signed(sender.clone()).into(), - currency_id, - collateral_amount.try_into().unwrap(), - debit_amount.try_into().unwrap(), - )?; - }: _(RawOrigin::Signed(sender), currency_id, debit_value, 0) - - shrink_position_debit { - let currency_id: CurrencyId = STAKING; - let sender: AccountId = whitelisted_caller(); - let maker: AccountId = account("maker", 0, SEED); - let debit_value = 100 * dollar(STABLECOIN); - let debit_exchange_rate = CdpEngine::get_debit_exchange_rate(currency_id); - let debit_amount = debit_exchange_rate.reciprocal().unwrap().saturating_mul_int(debit_value); - let collateral_value = 10 * debit_value; - let collateral_amount = Price::saturating_from_rational(1000 * dollar(currency_id), 1000 * dollar(STABLECOIN)).saturating_mul_int(collateral_value); - - // set balance and inject liquidity for trading path - set_balance(currency_id, &sender, (10 * collateral_amount) + ExistentialDeposits::get(¤cy_id)); - initialize_swap_pools(maker)?; - - feed_price(vec![(currency_id, Price::one())])?; - - // set risk params - CdpEngine::set_collateral_params( - RawOrigin::Root.into(), - currency_id, - Change::NoChange, - Change::NewValue(Some(Ratio::saturating_from_rational(150, 100))), - Change::NewValue(Some(Rate::saturating_from_rational(10, 100))), - Change::NewValue(Some(Ratio::saturating_from_rational(150, 100))), - Change::NewValue(debit_value * 100), - )?; - - // initialize sender's loan - Honzon::adjust_loan( - RawOrigin::Signed(sender.clone()).into(), - currency_id, - collateral_amount.try_into().unwrap(), - debit_amount.try_into().unwrap(), - )?; - }: _(RawOrigin::Signed(sender), currency_id, collateral_amount / 5, 0) - - transfer_debit { - let sender: AccountId = whitelisted_caller(); - set_balance(STAKING, &sender, 100_000 * dollar(STAKING)); - set_balance(LIQUID, &sender, 100_000 * dollar(LIQUID)); - - CdpEngine::set_collateral_params( - RawOrigin::Root.into(), - STAKING, - Change::NoChange, - Change::NewValue(Some(Ratio::saturating_from_rational(150, 100))), - Change::NewValue(Some(Rate::saturating_from_rational(10, 100))), - Change::NewValue(Some(Ratio::saturating_from_rational(150, 100))), - Change::NewValue(10_000 * dollar(STABLECOIN)), - )?; - CdpEngine::set_collateral_params( - RawOrigin::Root.into(), - LIQUID, - Change::NoChange, - Change::NewValue(Some(Ratio::saturating_from_rational(150, 100))), - Change::NewValue(Some(Rate::saturating_from_rational(10, 100))), - Change::NewValue(Some(Ratio::saturating_from_rational(150, 100))), - Change::NewValue(10_000 * dollar(STABLECOIN)), - )?; - feed_price(vec![(STAKING, Price::one())])?; - - Honzon::adjust_loan(RawOrigin::Signed(sender.clone()).into(), STAKING, (10_000 * dollar(STAKING)).try_into().unwrap(), (1_000 * dollar(STABLECOIN)).try_into().unwrap())?; - Honzon::adjust_loan(RawOrigin::Signed(sender.clone()).into(), LIQUID, (10_000 * dollar(LIQUID)).try_into().unwrap(), (1_000 * dollar(STABLECOIN)).try_into().unwrap())?; - }: _(RawOrigin::Signed(sender), LIQUID, STAKING, dollar(STABLECOIN)) - - precompile_get_current_collateral_ratio { - let currency_id: CurrencyId = LIQUID; - let sender: AccountId = whitelisted_caller(); - let maker: AccountId = account("maker", 0, SEED); - let debit_value = 100 * dollar(STABLECOIN); - let debit_exchange_rate = CdpEngine::get_debit_exchange_rate(LIQUID); - let debit_amount = debit_exchange_rate.reciprocal().unwrap().saturating_mul_int(debit_value); - let debit_amount: Amount = debit_amount.unique_saturated_into(); - let collateral_value = 10 * debit_value; - let collateral_amount = Price::saturating_from_rational(dollar(LIQUID), dollar(STABLECOIN)).saturating_mul_int(collateral_value); - - // set balance and inject liquidity - set_balance(LIQUID, &sender, (10 * collateral_amount) + ExistentialDeposits::get(&LIQUID)); - inject_liquidity(maker.clone(), LIQUID, STAKING, 10_000 * dollar(LIQUID), 10_000 * dollar(STAKING), false)?; - inject_liquidity(maker, STAKING, STABLECOIN, 10_000 * dollar(STAKING), 10_000 * dollar(STABLECOIN), false)?; - - feed_price(vec![(STAKING, Price::one())])?; - - // set risk params - CdpEngine::set_collateral_params( - RawOrigin::Root.into(), - LIQUID, - Change::NoChange, - Change::NewValue(Some(Ratio::saturating_from_rational(150, 100))), - Change::NewValue(Some(Rate::saturating_from_rational(10, 100))), - Change::NewValue(Some(Ratio::saturating_from_rational(150, 100))), - Change::NewValue(debit_value * 100), - )?; - - // initialize sender's loan - Honzon::adjust_loan( - RawOrigin::Signed(sender.clone()).into(), - LIQUID, - (10 * collateral_amount).try_into().unwrap(), - debit_amount, - )?; - }: { - Honzon::get_current_collateral_ratio(&sender, LIQUID); - } -} - -#[cfg(test)] -mod tests { - use super::*; - use crate::benchmarking::utils::tests::new_test_ext; - use orml_benchmarking::impl_benchmark_test_suite; - - impl_benchmark_test_suite!(new_test_ext(),); -} diff --git a/runtime/mandala/src/benchmarking/idle_scheduler.rs b/runtime/mandala/src/benchmarking/idle_scheduler.rs deleted file mode 100644 index 748822a28..000000000 --- a/runtime/mandala/src/benchmarking/idle_scheduler.rs +++ /dev/null @@ -1,59 +0,0 @@ -// This file is part of Acala. - -// Copyright (C) 2020-2025 Acala Foundation. -// SPDX-License-Identifier: GPL-3.0-or-later WITH Classpath-exception-2.0 - -// This program 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. - -// This program 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 this program. If not, see . - -use crate::{EvmTask, IdleScheduler, Runtime, RuntimeOrigin, ScheduledTasks, Weight, H160}; -use frame_support::traits::{OnIdle, OnInitialize}; -use orml_benchmarking::runtime_benchmarks; -use primitives::task::TaskResult; - -runtime_benchmarks! { - { Runtime, module_idle_scheduler} - - on_initialize { - }: { - IdleScheduler::on_initialize(1); - } - - on_idle_base { - }: { - IdleScheduler::on_idle(0, Weight::from_parts(1_000_000_000, 0)); - } - - clear_tasks { - let dummy_hash = [0; 20]; - let call = ScheduledTasks::EvmTask(EvmTask::Remove{caller: H160::from(&dummy_hash), contract: H160::from(&dummy_hash), maintainer: H160::from(&dummy_hash)}); - IdleScheduler::schedule_task(RuntimeOrigin::root(), call)?; - let completed_tasks = vec![(0, TaskResult{ result: Ok(()), used_weight: Weight::zero(), finished: true })]; - }: { - IdleScheduler::remove_completed_tasks(completed_tasks); - } - - schedule_task { - let dummy_hash = [0; 20]; - let call = ScheduledTasks::EvmTask(EvmTask::Remove{caller: H160::from(&dummy_hash), contract: H160::from(&dummy_hash), maintainer: H160::from(&dummy_hash)}); - }: _(RuntimeOrigin::root(), call) -} - -#[cfg(test)] -mod tests { - use super::*; - use crate::benchmarking::utils::tests::new_test_ext; - use orml_benchmarking::impl_benchmark_test_suite; - - impl_benchmark_test_suite!(new_test_ext(),); -} diff --git a/runtime/mandala/src/benchmarking/incentives.rs b/runtime/mandala/src/benchmarking/incentives.rs deleted file mode 100644 index 8300ea980..000000000 --- a/runtime/mandala/src/benchmarking/incentives.rs +++ /dev/null @@ -1,116 +0,0 @@ -// This file is part of Acala. - -// Copyright (C) 2020-2025 Acala Foundation. -// SPDX-License-Identifier: GPL-3.0-or-later WITH Classpath-exception-2.0 - -// This program 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. - -// This program 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 this program. If not, see . - -use crate::{AccountId, AccumulatePeriod, Currencies, CurrencyId, Incentives, Rate, Rewards, Runtime, System}; - -use super::{ - get_benchmarking_collateral_currency_ids, - utils::{dollar, set_balance, NATIVE, STABLECOIN, STAKING}, -}; -use frame_benchmarking::whitelisted_caller; -use frame_support::{assert_ok, traits::OnInitialize}; -use frame_system::RawOrigin; -use module_support::PoolId; -use orml_benchmarking::runtime_benchmarks; -use orml_traits::MultiCurrency; -use sp_std::prelude::*; - -runtime_benchmarks! { - { Runtime, module_incentives } - - on_initialize { - let c in 0 .. get_benchmarking_collateral_currency_ids().len() as u32; - let currency_ids = get_benchmarking_collateral_currency_ids(); - let block_number = AccumulatePeriod::get(); - - for i in 0 .. c { - let currency_id = currency_ids[i as usize]; - let pool_id = PoolId::Loans(currency_id); - - Incentives::update_incentive_rewards(RawOrigin::Root.into(), vec![(pool_id.clone(), vec![(NATIVE, 100 * dollar(NATIVE))])])?; - orml_rewards::PoolInfos::::mutate(pool_id, |pool_info| { - pool_info.total_shares += 100; - }); - } - - Incentives::on_initialize(1); - System::set_block_number(block_number); - }: { - Incentives::on_initialize(System::block_number()); - } - - deposit_dex_share { - let caller: AccountId = whitelisted_caller(); - let native_stablecoin_lp = CurrencyId::join_dex_share_currency_id(NATIVE, STABLECOIN).unwrap(); - set_balance(native_stablecoin_lp, &caller, 10_000 * dollar(STABLECOIN)); - }: _(RawOrigin::Signed(caller), native_stablecoin_lp, 10_000 * dollar(STABLECOIN)) - - withdraw_dex_share { - let caller: AccountId = whitelisted_caller(); - let native_stablecoin_lp = CurrencyId::join_dex_share_currency_id(NATIVE, STABLECOIN).unwrap(); - set_balance(native_stablecoin_lp, &caller, 10_000 * dollar(STABLECOIN)); - Incentives::deposit_dex_share( - RawOrigin::Signed(caller.clone()).into(), - native_stablecoin_lp, - 10_000 * dollar(STABLECOIN) - )?; - }: _(RawOrigin::Signed(caller), native_stablecoin_lp, 8000 * dollar(STABLECOIN)) - - claim_rewards { - let caller: AccountId = whitelisted_caller(); - let pool_id = PoolId::Loans(STAKING); - - assert_ok!(Rewards::add_share(&caller, &pool_id, dollar(NATIVE))); - Currencies::deposit(NATIVE, &Incentives::account_id(), 80 * dollar(NATIVE))?; - Rewards::accumulate_reward(&pool_id, NATIVE, 80 * dollar(NATIVE))?; - }: _(RawOrigin::Signed(caller), pool_id) - - update_incentive_rewards { - let c in 0 .. get_benchmarking_collateral_currency_ids().len() as u32; - let currency_ids = get_benchmarking_collateral_currency_ids(); - let mut updates = vec![]; - - for i in 0 .. c { - let currency_id = currency_ids[i as usize]; - updates.push((PoolId::Loans(currency_id), vec![(NATIVE, dollar(NATIVE))])); - } - }: _(RawOrigin::Root, updates) - - update_claim_reward_deduction_rates { - let c in 0 .. get_benchmarking_collateral_currency_ids().len() as u32; - let currency_ids = get_benchmarking_collateral_currency_ids(); - let mut updates = vec![]; - - for i in 0 .. c { - let currency_id = currency_ids[i as usize]; - updates.push((PoolId::Loans(currency_id), Rate::default())); - } - }: _(RawOrigin::Root, updates) - - update_claim_reward_deduction_currency { - }: _(RawOrigin::Root, PoolId::Earning(NATIVE), Some(NATIVE)) -} - -#[cfg(test)] -mod tests { - use super::*; - use crate::benchmarking::utils::tests::new_test_ext; - use orml_benchmarking::impl_benchmark_test_suite; - - impl_benchmark_test_suite!(new_test_ext(),); -} diff --git a/runtime/mandala/src/benchmarking/liquid_crowdloan.rs b/runtime/mandala/src/benchmarking/liquid_crowdloan.rs deleted file mode 100644 index 888b48e44..000000000 --- a/runtime/mandala/src/benchmarking/liquid_crowdloan.rs +++ /dev/null @@ -1,54 +0,0 @@ -// This file is part of Acala. - -// Copyright (C) 2020-2025 Acala Foundation. -// SPDX-License-Identifier: GPL-3.0-or-later WITH Classpath-exception-2.0 - -// This program 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. - -// This program 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 this program. If not, see . - -use crate::{ - AccountId, GetLiquidCurrencyId, GetStakingCurrencyId, LiquidCrowdloan, LiquidCrowdloanCurrencyId, PolkadotXcm, - Runtime, RuntimeOrigin, System, -}; - -use super::utils::{set_balance, STAKING}; -use frame_benchmarking::whitelisted_caller; -use frame_system::RawOrigin; -use orml_benchmarking::runtime_benchmarks; -use sp_std::prelude::*; - -runtime_benchmarks! { - { Runtime, module_liquid_crowdloan } - - redeem { - let caller: AccountId = whitelisted_caller(); - let amount = 100_000_000_000_000; - set_balance(LiquidCrowdloanCurrencyId::get(), &caller, amount); - set_balance(STAKING, &LiquidCrowdloan::account_id(), amount); - }: _(RawOrigin::Signed(caller), amount) - verify { - System::assert_last_event(module_liquid_crowdloan::Event::Redeemed { currency_id: GetStakingCurrencyId::get(), amount }.into()); - } - - set_redeem_currency_id { - }: _(RawOrigin::Root, GetLiquidCurrencyId::get()) -} - -#[cfg(test)] -mod tests { - use super::*; - use crate::benchmarking::utils::tests::new_test_ext; - use orml_benchmarking::impl_benchmark_test_suite; - - impl_benchmark_test_suite!(new_test_ext(),); -} diff --git a/runtime/mandala/src/benchmarking/mod.rs b/runtime/mandala/src/benchmarking/mod.rs deleted file mode 100644 index b140707f3..000000000 --- a/runtime/mandala/src/benchmarking/mod.rs +++ /dev/null @@ -1,67 +0,0 @@ -// This file is part of Acala. - -// Copyright (C) 2020-2025 Acala Foundation. -// SPDX-License-Identifier: GPL-3.0-or-later WITH Classpath-exception-2.0 - -// This program 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. - -// This program 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 this program. If not, see . - -#![cfg(feature = "runtime-benchmarks")] - -use sp_runtime::traits::AccountIdConversion; - -use super::{CurrencyId, ACA, DOT, LDOT}; -use sp_std::prelude::*; - -pub mod utils; - -// module benchmarking -pub mod aggregated_dex; -pub mod asset_registry; -pub mod auction_manager; -pub mod cdp_engine; -pub mod cdp_treasury; -pub mod collator_selection; -pub mod currencies; -pub mod dex; -pub mod dex_oracle; -pub mod earning; -pub mod emergency_shutdown; -pub mod evm; -pub mod evm_accounts; -pub mod homa; -pub mod homa_validator_list; -pub mod honzon; -pub mod idle_scheduler; -pub mod incentives; -pub mod liquid_crowdloan; -pub mod nominees_election; -pub mod nutsfinance_stable_asset; -pub mod prices; -pub mod session_manager; -pub mod transaction_pause; -pub mod transaction_payment; - -// orml benchmarking -pub mod auction; -pub mod authority; -pub mod tokens; -pub mod vesting; - -pub fn get_vesting_account() -> super::AccountId { - super::TreasuryPalletId::get().into_account_truncating() -} - -pub fn get_benchmarking_collateral_currency_ids() -> Vec { - vec![ACA, DOT, LDOT, CurrencyId::StableAssetPoolToken(0)] -} diff --git a/runtime/mandala/src/benchmarking/nominees_election.rs b/runtime/mandala/src/benchmarking/nominees_election.rs deleted file mode 100644 index e1095290b..000000000 --- a/runtime/mandala/src/benchmarking/nominees_election.rs +++ /dev/null @@ -1,117 +0,0 @@ -// This file is part of Acala. - -// Copyright (C) 2020-2025 Acala Foundation. -// SPDX-License-Identifier: GPL-3.0-or-later WITH Classpath-exception-2.0 - -// This program 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. - -// This program 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 this program. If not, see . - -use crate::{ - AccountId, Balance, BondingDuration, Homa, HomaValidatorList, MinNomineesElectionBondThreshold, NomineesElection, - Runtime, ValidatorInsuranceThreshold, -}; - -use super::utils::{set_balance, LIQUID}; -use frame_benchmarking::{account, whitelisted_caller}; -use frame_support::{traits::Get, BoundedVec}; -use frame_system::RawOrigin; -use orml_benchmarking::runtime_benchmarks; -use sp_runtime::SaturatedConversion; -use sp_std::prelude::*; - -const SEED: u32 = 0; - -runtime_benchmarks! { - { Runtime, module_nominees_election } - - bond { - let caller: AccountId = whitelisted_caller(); - set_balance(LIQUID, &caller, 2 * MinNomineesElectionBondThreshold::get()); - }: _(RawOrigin::Signed(caller), MinNomineesElectionBondThreshold::get()) - - unbond { - let caller: AccountId = whitelisted_caller(); - set_balance(LIQUID, &caller, 2 * MinNomineesElectionBondThreshold::get()); - NomineesElection::bond(RawOrigin::Signed(caller.clone()).into(), MinNomineesElectionBondThreshold::get())?; - }: _(RawOrigin::Signed(caller), MinNomineesElectionBondThreshold::get()) - - rebond { - let c in 1 .. ::MaxUnbondingChunks::get(); - - let caller: AccountId = whitelisted_caller(); - set_balance(LIQUID, &caller, 2 * MinNomineesElectionBondThreshold::get()); - NomineesElection::bond(RawOrigin::Signed(caller.clone()).into(), 2 * MinNomineesElectionBondThreshold::get())?; - for _ in 0..c { - NomineesElection::unbond(RawOrigin::Signed(caller.clone()).into(), MinNomineesElectionBondThreshold::get()/c as u128)?; - } - }: _(RawOrigin::Signed(caller), MinNomineesElectionBondThreshold::get()) - - withdraw_unbonded { - let c in 1 .. ::MaxUnbondingChunks::get(); - - let caller: AccountId = whitelisted_caller(); - set_balance(LIQUID, &caller, 2 * MinNomineesElectionBondThreshold::get()); - NomineesElection::bond(RawOrigin::Signed(caller.clone()).into(), 2 * MinNomineesElectionBondThreshold::get())?; - for _ in 0..c { - NomineesElection::unbond(RawOrigin::Signed(caller.clone()).into(), MinNomineesElectionBondThreshold::get()/c as u128)?; - } - Homa::force_bump_current_era(RawOrigin::Root.into(), BondingDuration::get())?; - }: _(RawOrigin::Signed(caller)) - - nominate { - let c in 1 .. ::MaxNominateesCount::get(); - let targets: Vec = (0..c).map(|c| account("nominatees", c, SEED)).collect(); - let caller: AccountId = whitelisted_caller(); - set_balance(LIQUID, &caller, 2 * MinNomineesElectionBondThreshold::get() + ValidatorInsuranceThreshold::get() * targets.len().saturated_into::()); - - for validator in targets.iter() { - HomaValidatorList::bond(RawOrigin::Signed(caller.clone()).into(), validator.clone(), ValidatorInsuranceThreshold::get())?; - } - - let caller: AccountId = whitelisted_caller(); - NomineesElection::bond(RawOrigin::Signed(caller.clone()).into(), MinNomineesElectionBondThreshold::get())?; - }: _(RawOrigin::Signed(caller), targets) - - chill { - let c in 1 .. ::MaxNominateesCount::get(); - let targets: Vec = (0..c).map(|c| account("nominatees", c, SEED)).collect(); - - let caller: AccountId = whitelisted_caller(); - set_balance(LIQUID, &caller, 2 * MinNomineesElectionBondThreshold::get() + ValidatorInsuranceThreshold::get() * targets.len().saturated_into::()); - - for validator in targets.iter() { - HomaValidatorList::bond(RawOrigin::Signed(caller.clone()).into(), validator.clone(), ValidatorInsuranceThreshold::get())?; - } - - NomineesElection::bond(RawOrigin::Signed(caller.clone()).into(), MinNomineesElectionBondThreshold::get())?; - NomineesElection::nominate(RawOrigin::Signed(caller.clone()).into(), targets)?; - }: _(RawOrigin::Signed(caller)) - - reset_reserved_nominees { - let c in 1 .. 4; - let updates: Vec<(u16, BoundedVec::MaxNominateesCount>)> = (0..c).map(|c| { - let reserved: BoundedVec::MaxNominateesCount> = - (0..::MaxNominateesCount::get()).map(|c| account("nominatees", c, SEED)).collect::>().try_into().unwrap(); - (c.saturated_into(), reserved) - }).collect(); - }: _(RawOrigin::Root, updates) -} - -#[cfg(test)] -mod tests { - use super::*; - use crate::benchmarking::utils::tests::new_test_ext; - use orml_benchmarking::impl_benchmark_test_suite; - - impl_benchmark_test_suite!(new_test_ext(),); -} diff --git a/runtime/mandala/src/benchmarking/nutsfinance_stable_asset.rs b/runtime/mandala/src/benchmarking/nutsfinance_stable_asset.rs deleted file mode 100644 index 3eeaf93d2..000000000 --- a/runtime/mandala/src/benchmarking/nutsfinance_stable_asset.rs +++ /dev/null @@ -1,200 +0,0 @@ -// This file is part of Acala. - -// Copyright (C) 2020-2025 Acala Foundation. -// SPDX-License-Identifier: GPL-3.0-or-later WITH Classpath-exception-2.0 - -// This program 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. - -// This program 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 this program. If not, see . - -use crate::{AccountId, Runtime, StableAsset}; - -use super::utils::{ - create_stable_pools, dollar, register_stable_asset, set_balance, LIQUID, NATIVE, STABLECOIN, STAKING, -}; -use frame_benchmarking::{account, whitelisted_caller}; -use frame_support::traits::Get; -use frame_system::RawOrigin; -use orml_benchmarking::runtime_benchmarks; -use primitives::currency::CurrencyId; -use sp_std::prelude::*; - -const SEED: u32 = 0; - -fn currency_list() -> Vec { - vec![ - NATIVE, - STABLECOIN, - LIQUID, - STAKING, - CurrencyId::join_dex_share_currency_id(LIQUID, STAKING).unwrap(), - ] -} - -runtime_benchmarks! { - { Runtime, nutsfinance_stable_asset } - - create_pool { - let pool_asset = CurrencyId::StableAssetPoolToken(0); - let assets = vec![LIQUID, STAKING]; - let precisions = vec![1u128, 1u128]; - let mint_fee = 10000000u128; - let swap_fee = 20000000u128; - let redeem_fee = 50000000u128; - let initial_a = 10000u128; - let fee_recipient: AccountId = account("fee", 0, SEED); - let yield_recipient: AccountId = account("yield", 1, SEED); - register_stable_asset()?; - }: _(RawOrigin::Root, pool_asset, assets, precisions, mint_fee, swap_fee, redeem_fee, initial_a, fee_recipient, yield_recipient, 1_000_000_000_000u128) - - modify_a { - let assets = vec![LIQUID, STAKING]; - let precisions = vec![1u128, 1u128]; - create_stable_pools(assets, precisions, 10000u128)?; - let pool_id = StableAsset::pool_count() - 1; - }: _(RawOrigin::Root, pool_id, 1000u128, 2629112370) - - modify_fees { - let assets = vec![LIQUID, STAKING]; - let precisions = vec![1u128, 1u128]; - create_stable_pools(assets, precisions, 10000u128)?; - let pool_id = StableAsset::pool_count() - 1; - }: _(RawOrigin::Root, pool_id, Some(100u128), Some(200u128), Some(300u128)) - - modify_recipients { - let assets = vec![LIQUID, STAKING]; - let precisions = vec![1u128, 1u128]; - create_stable_pools(assets, precisions, 10000u128)?; - let pool_id = StableAsset::pool_count() - 1; - }: _(RawOrigin::Root, pool_id, Some(account("fee-1", 3, SEED)), Some(account("yield-1", 4, SEED))) - - mint { - let tester: AccountId = whitelisted_caller(); - let u in 2u32 .. ::PoolAssetLimit::get(); - let mut assets = vec![]; - let mut precisions = vec![]; - let mut mint_args = vec![]; - for i in 0 .. u { - let i_idx: usize = usize::try_from(i).unwrap(); - let currency_id = currency_list()[i_idx]; - assets.push(currency_id); - precisions.push(1u128); - mint_args.push(dollar(currency_id)); - set_balance(currency_id, &tester, 10 * dollar(currency_id)); - } - create_stable_pools(assets, precisions, 10000u128)?; - let pool_id = StableAsset::pool_count() - 1; - }: _(RawOrigin::Signed(tester), pool_id, mint_args, 0u128) - - swap { - let tester: AccountId = whitelisted_caller(); - let u in 2u32 .. ::PoolAssetLimit::get(); - let mut assets = vec![]; - let mut precisions = vec![]; - for i in 0 .. u { - let i_idx: usize = usize::try_from(i).unwrap(); - let currency_id = currency_list()[i_idx]; - assets.push(currency_id); - precisions.push(1u128); - set_balance(currency_id, &tester, u128::MAX / 2); - } - let mint_args = match u { - 2 => vec![u128::MAX / 10, 1], - 3 => vec![u128::MAX / 10, 1, 1], - 4 => vec![u128::MAX / 100000, 10000, 10000, 10000], - 5 => vec![u128::MAX / 100000000, 100000000, 100000000, 100000000, 100000000], - _ => vec![] - }; - create_stable_pools(assets, precisions, 10000)?; - let pool_id = StableAsset::pool_count() - 1; - StableAsset::mint(RawOrigin::Signed(tester.clone()).into(), pool_id, mint_args.clone(), 0u128)?; - }: _(RawOrigin::Signed(tester), pool_id, 1, 0, 100000u128, 0u128, u) - - redeem_proportion { - let tester: AccountId = whitelisted_caller(); - let u in 2u32 .. ::PoolAssetLimit::get(); - let mut assets = vec![]; - let mut precisions = vec![]; - let mut mint_args = vec![]; - let mut redeem_args = vec![]; - for i in 0 .. u { - let i_idx: usize = usize::try_from(i).unwrap(); - let currency_id = currency_list()[i_idx]; - let multiple: u128 = (i + 1).into(); - assets.push(currency_id); - precisions.push(1u128); - mint_args.push(1000 * dollar(currency_id) * multiple); - redeem_args.push(0u128); - set_balance(currency_id, &tester, u128::MAX / 10); - } - create_stable_pools(assets, precisions, 10000u128)?; - let pool_id = StableAsset::pool_count() - 1; - StableAsset::mint(RawOrigin::Signed(tester.clone()).into(), pool_id, mint_args, 0u128)?; - }: _(RawOrigin::Signed(tester), pool_id, 1_000_000_000_000u128, redeem_args) - - redeem_single { - let tester: AccountId = whitelisted_caller(); - let pool_asset = CurrencyId::StableAssetPoolToken(0); - let u in 2u32 .. ::PoolAssetLimit::get(); - let mut assets = vec![]; - let mut precisions = vec![]; - for i in 0 .. u { - let i_idx: usize = usize::try_from(i).unwrap(); - let currency_id = currency_list()[i_idx]; - assets.push(currency_id); - precisions.push(1u128); - set_balance(currency_id, &tester, u128::MAX / 2); - } - let mint_args = match u { - 2 => vec![u128::MAX / 10, 1], - 3 => vec![u128::MAX / 10, 1, 1], - 4 => vec![u128::MAX / 100000, 10000, 10000, 10000], - 5 => vec![u128::MAX / 100000000, 100000000, 100000000, 100000000, 100000000], - _ => vec![] - }; - create_stable_pools(assets, precisions, 10000)?; - let pool_id = StableAsset::pool_count() - 1; - StableAsset::mint(RawOrigin::Signed(tester.clone()).into(), pool_id, mint_args, 0u128)?; - }: { - let _ = StableAsset::redeem_single(RawOrigin::Signed(tester).into(), pool_id, 10_000u128, 0u32, 0u128, u); - } - - redeem_multi { - let tester: AccountId = whitelisted_caller(); - let u in 2u32 .. ::PoolAssetLimit::get(); - let mut assets = vec![]; - let mut precisions = vec![]; - let mut mint_args = vec![]; - let mut redeem_args = vec![]; - for i in 0 .. u { - let i_idx: usize = usize::try_from(i).unwrap(); - let currency_id = currency_list()[i_idx]; - assets.push(currency_id); - precisions.push(1u128); - mint_args.push(100 * dollar(currency_id)); - redeem_args.push(dollar(currency_id)); - set_balance(currency_id, &tester, u128::MAX / 10); - } - create_stable_pools(assets, precisions, 10000u128)?; - let pool_id = StableAsset::pool_count() - 1; - StableAsset::mint(RawOrigin::Signed(tester.clone()).into(), pool_id, mint_args, 0u128)?; - }: _(RawOrigin::Signed(tester), pool_id, redeem_args, u128::MAX / 10) -} - -#[cfg(test)] -mod tests { - use super::*; - use crate::benchmarking::utils::tests::new_test_ext; - use orml_benchmarking::impl_benchmark_test_suite; - - impl_benchmark_test_suite!(new_test_ext(),); -} diff --git a/runtime/mandala/src/benchmarking/session_manager.rs b/runtime/mandala/src/benchmarking/session_manager.rs deleted file mode 100644 index aec84b62a..000000000 --- a/runtime/mandala/src/benchmarking/session_manager.rs +++ /dev/null @@ -1,83 +0,0 @@ -// This file is part of Acala. - -// Copyright (C) 2020-2025 Acala Foundation. -// SPDX-License-Identifier: GPL-3.0-or-later WITH Classpath-exception-2.0 - -// This program 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. - -// This program 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 this program. If not, see . - -use crate::{Runtime, RuntimeEvent, SessionManager, System}; - -use frame_support::{ - assert_ok, - traits::{EstimateNextSessionRotation, OnInitialize}, -}; -use frame_system::RawOrigin; -use orml_benchmarking::runtime_benchmarks; - -fn assert_last_event(generic_event: RuntimeEvent) { - System::assert_last_event(generic_event.into()); -} - -runtime_benchmarks! { - { Runtime, module_session_manager } - - schedule_session_duration { - System::set_block_number(2u32.into()); - module_session_manager::SessionDuration::::put(10); - }: { - assert_ok!( - SessionManager::schedule_session_duration(RawOrigin::Root.into(), 1, 100) - ); - } - verify { - assert_last_event(module_session_manager::Event::ScheduledSessionDuration{block_number: 10, session_index: 1, session_duration: 100}.into()); - } - - on_initialize_skip { - System::set_block_number(2u32.into()); - module_session_manager::SessionDuration::::put(10); - SessionManager::schedule_session_duration(RawOrigin::Root.into(), 1, 100)?; - }: { - SessionManager::on_initialize(9) - } - - on_initialize { - System::set_block_number(2u32.into()); - module_session_manager::SessionDuration::::put(10); - SessionManager::schedule_session_duration(RawOrigin::Root.into(), 1, 100)?; - }: { - SessionManager::on_initialize(10) - } - - estimate_current_session_progress { - module_session_manager::SessionDuration::::put(10); - }: { - SessionManager::estimate_current_session_progress(10) - } - - estimate_next_session_rotation { - module_session_manager::SessionDuration::::put(10); - }: { - SessionManager::estimate_next_session_rotation(10) - } -} - -#[cfg(test)] -mod tests { - use super::*; - use crate::benchmarking::utils::tests::new_test_ext; - use orml_benchmarking::impl_benchmark_test_suite; - - impl_benchmark_test_suite!(new_test_ext(),); -} diff --git a/runtime/mandala/src/benchmarking/tokens.rs b/runtime/mandala/src/benchmarking/tokens.rs deleted file mode 100644 index bb87c7d61..000000000 --- a/runtime/mandala/src/benchmarking/tokens.rs +++ /dev/null @@ -1,101 +0,0 @@ -// This file is part of Acala. - -// Copyright (C) 2020-2025 Acala Foundation. -// SPDX-License-Identifier: GPL-3.0-or-later WITH Classpath-exception-2.0 - -// This program 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. - -// This program 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 this program. If not, see . - -use super::utils::{dollar, lookup_of_account, set_balance as update_balance, STABLECOIN}; -use crate::{AccountId, Balance, Runtime, Tokens}; - -use sp_std::prelude::*; - -use frame_benchmarking::{account, whitelisted_caller}; -use frame_system::RawOrigin; - -use orml_benchmarking::runtime_benchmarks; -use orml_traits::MultiCurrency; - -const SEED: u32 = 0; - -runtime_benchmarks! { - { Runtime, orml_tokens } - - transfer { - let amount: Balance = dollar(STABLECOIN); - - let from: AccountId = whitelisted_caller(); - update_balance(STABLECOIN, &from, amount); - - let to: AccountId = account("to", 0, SEED); - let to_lookup = lookup_of_account(to.clone()); - }: _(RawOrigin::Signed(from), to_lookup, STABLECOIN, amount) - verify { - assert_eq!(>::total_balance(STABLECOIN, &to), amount); - } - - transfer_all { - let amount: Balance = dollar(STABLECOIN); - - let from: AccountId = whitelisted_caller(); - update_balance(STABLECOIN, &from, amount); - - let to: AccountId = account("to", 0, SEED); - let to_lookup = lookup_of_account(to); - }: _(RawOrigin::Signed(from.clone()), to_lookup, STABLECOIN, false) - verify { - assert_eq!(>::total_balance(STABLECOIN, &from), 0); - } - - transfer_keep_alive { - let from: AccountId = whitelisted_caller(); - update_balance(STABLECOIN, &from, 2 * dollar(STABLECOIN)); - - let to: AccountId = account("to", 0, SEED); - let to_lookup = lookup_of_account(to.clone()); - }: _(RawOrigin::Signed(from), to_lookup, STABLECOIN, dollar(STABLECOIN)) - verify { - assert_eq!(>::total_balance(STABLECOIN, &to), dollar(STABLECOIN)); - } - - force_transfer { - let from: AccountId = account("from", 0, SEED); - let from_lookup = lookup_of_account(from.clone()); - update_balance(STABLECOIN, &from, 2 * dollar(STABLECOIN)); - - let to: AccountId = account("to", 0, SEED); - let to_lookup = lookup_of_account(to.clone()); - }: _(RawOrigin::Root, from_lookup, to_lookup, STABLECOIN, dollar(STABLECOIN)) - verify { - assert_eq!(>::total_balance(STABLECOIN, &to), dollar(STABLECOIN)); - } - - set_balance { - let who: AccountId = account("who", 0, SEED); - let who_lookup = lookup_of_account(who.clone()); - - }: _(RawOrigin::Root, who_lookup, STABLECOIN, dollar(STABLECOIN), dollar(STABLECOIN)) - verify { - assert_eq!(>::total_balance(STABLECOIN, &who), 2 * dollar(STABLECOIN)); - } -} - -#[cfg(test)] -mod tests { - use super::*; - use crate::benchmarking::utils::tests::new_test_ext; - use orml_benchmarking::impl_benchmark_test_suite; - - impl_benchmark_test_suite!(new_test_ext(),); -} diff --git a/runtime/mandala/src/benchmarking/transaction_pause.rs b/runtime/mandala/src/benchmarking/transaction_pause.rs deleted file mode 100644 index 618a7f65f..000000000 --- a/runtime/mandala/src/benchmarking/transaction_pause.rs +++ /dev/null @@ -1,49 +0,0 @@ -// This file is part of Acala. - -// Copyright (C) 2020-2025 Acala Foundation. -// SPDX-License-Identifier: GPL-3.0-or-later WITH Classpath-exception-2.0 - -// This program 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. - -// This program 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 this program. If not, see . - -use crate::{Runtime, RuntimeOrigin, TransactionPause, H160}; - -use frame_system::RawOrigin; -use orml_benchmarking::runtime_benchmarks; - -runtime_benchmarks! { - { Runtime, module_transaction_pause } - - pause_transaction { - }: _(RawOrigin::Root, b"Balances".to_vec(), b"transfer".to_vec()) - - unpause_transaction { - TransactionPause::pause_transaction(RuntimeOrigin::root(), b"Balances".to_vec(), b"transfer".to_vec())?; - }: _(RawOrigin::Root, b"Balances".to_vec(), b"transfer".to_vec()) - - pause_evm_precompile { - }: _(RawOrigin::Root, H160::from_low_u64_be(1)) - - unpause_evm_precompile { - TransactionPause::pause_evm_precompile(RuntimeOrigin::root(), H160::from_low_u64_be(1))?; - }: _(RawOrigin::Root, H160::from_low_u64_be(1)) -} - -#[cfg(test)] -mod tests { - use super::*; - use crate::benchmarking::utils::tests::new_test_ext; - use orml_benchmarking::impl_benchmark_test_suite; - - impl_benchmark_test_suite!(new_test_ext(),); -} diff --git a/runtime/mandala/src/benchmarking/transaction_payment.rs b/runtime/mandala/src/benchmarking/transaction_payment.rs deleted file mode 100644 index 48b2d6376..000000000 --- a/runtime/mandala/src/benchmarking/transaction_payment.rs +++ /dev/null @@ -1,240 +0,0 @@ -// This file is part of Acala. - -// Copyright (C) 2020-2025 Acala Foundation. -// SPDX-License-Identifier: GPL-3.0-or-later WITH Classpath-exception-2.0 - -// This program 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. - -// This program 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 this program. If not, see . - -use super::utils::{dollar, inject_liquidity, set_balance, LIQUID, NATIVE, STABLECOIN, STAKING}; -use crate::{ - AccountId, AssetRegistry, Balance, Currencies, CurrencyId, Dex, NativeTokenExistentialDeposit, Runtime, - RuntimeEvent, RuntimeOrigin, StableAsset, System, TransactionPayment, TreasuryPalletId, -}; -use frame_benchmarking::{account, whitelisted_caller}; -use frame_support::{assert_ok, traits::OnFinalize}; -use frame_system::RawOrigin; -use module_support::{AggregatedSwapPath, DEXManager, Ratio, SwapLimit}; -use orml_benchmarking::runtime_benchmarks; -use orml_traits::MultiCurrency; -use primitives::currency::AssetMetadata; -use sp_runtime::traits::{AccountIdConversion, One}; -use sp_std::prelude::*; - -const SEED: u32 = 0; - -fn assert_has_event(generic_event: RuntimeEvent) { - System::assert_has_event(generic_event.into()); -} - -fn enable_fee_pool() -> (AccountId, Balance, Balance, Balance) { - let funder: AccountId = account("funder", 0, SEED); - let treasury_account: AccountId = TreasuryPalletId::get().into_account_truncating(); - let sub_account: AccountId = - ::PalletId::get().into_sub_account_truncating(STABLECOIN); - let native_ed: Balance = >::minimum_balance(NATIVE); - let stable_ed: Balance = >::minimum_balance(STABLECOIN); - let pool_size: Balance = native_ed * 50; - let swap_threshold: Balance = native_ed * 2; - - inject_liquidity( - funder.clone(), - STABLECOIN, - NATIVE, - 1_000 * dollar(STABLECOIN), - 10_000 * dollar(NATIVE), - false, - ) - .unwrap(); - assert!(Dex::get_swap_amount( - &vec![STABLECOIN, NATIVE], - SwapLimit::ExactTarget(Balance::MAX, native_ed) - ) - .is_some()); - assert_eq!( - Dex::get_liquidity_pool(STABLECOIN, NATIVE), - (1_000 * dollar(STABLECOIN), 10_000 * dollar(NATIVE)) - ); - - set_balance(NATIVE, &treasury_account, pool_size * 10); - set_balance(STABLECOIN, &treasury_account, stable_ed * 10); - (sub_account, stable_ed, pool_size, swap_threshold) -} - -fn enable_stable_asset() { - let funder: AccountId = account("funder", 0, SEED); - - set_balance(STAKING, &funder, 1000 * dollar(STAKING)); - set_balance(LIQUID, &funder, 1000 * dollar(LIQUID)); - set_balance(NATIVE, &funder, 1000 * dollar(NATIVE)); - - // create stable asset pool - let pool_asset = CurrencyId::StableAssetPoolToken(0); - assert_ok!(StableAsset::create_pool( - RuntimeOrigin::root(), - pool_asset, - vec![STAKING, LIQUID], - vec![1u128, 1u128], - 10_000_000u128, - 20_000_000u128, - 50_000_000u128, - 1_000u128, - funder.clone(), - funder.clone(), - 1_000_000_000_000u128, - )); - let asset_metadata = AssetMetadata { - name: b"Token Name".to_vec(), - symbol: b"TN".to_vec(), - decimals: 12, - minimal_balance: 1, - }; - assert_ok!(AssetRegistry::register_stable_asset( - RawOrigin::Root.into(), - Box::new(asset_metadata.clone()) - )); - assert_ok!(StableAsset::mint( - RuntimeOrigin::signed(funder.clone()), - 0, - vec![100 * dollar(STAKING), 100 * dollar(LIQUID)], - 0u128 - )); - - inject_liquidity( - funder.clone(), - LIQUID, - NATIVE, - 100 * dollar(LIQUID), - 100 * dollar(NATIVE), - false, - ) - .unwrap(); -} - -runtime_benchmarks! { - { Runtime, module_transaction_payment } - - set_alternative_fee_swap_path { - let caller: AccountId = whitelisted_caller(); - set_balance(NATIVE, &caller, 2 * NativeTokenExistentialDeposit::get()); - }: _(RawOrigin::Signed(caller.clone()), Some(vec![STABLECOIN, NATIVE])) - verify { - assert_eq!(TransactionPayment::alternative_fee_swap_path(&caller).unwrap().into_inner(), vec![STABLECOIN, NATIVE]); - } - - enable_charge_fee_pool { - let (sub_account, stable_ed, pool_size, swap_threshold) = enable_fee_pool(); - }: _(RawOrigin::Root, STABLECOIN, pool_size, swap_threshold) - verify { - let exchange_rate = TransactionPayment::token_exchange_rate(STABLECOIN).unwrap(); - assert_eq!(TransactionPayment::pool_size(STABLECOIN), pool_size); - assert!(TransactionPayment::token_exchange_rate(STABLECOIN).is_some()); - assert_eq!(>::free_balance(STABLECOIN, &sub_account), stable_ed); - assert_eq!(>::free_balance(NATIVE, &sub_account), pool_size); - assert_has_event(module_transaction_payment::Event::ChargeFeePoolEnabled { - sub_account, - currency_id: STABLECOIN, - exchange_rate, - pool_size, - swap_threshold - }.into()); - } - - disable_charge_fee_pool { - let treasury_account: AccountId = TreasuryPalletId::get().into_account_truncating(); - let sub_account: AccountId = ::PalletId::get().into_sub_account_truncating(STABLECOIN); - let native_ed: Balance = >::minimum_balance(NATIVE); - let stable_ed: Balance = >::minimum_balance(STABLECOIN); - let pool_size: Balance = native_ed * 50; - - set_balance(NATIVE, &sub_account, native_ed * 10); - set_balance(STABLECOIN, &sub_account, stable_ed * 10); - - module_transaction_payment::TokenExchangeRate::::insert(STABLECOIN, Ratio::one()); - }: _(RawOrigin::Root, STABLECOIN) - verify { - assert_has_event(module_transaction_payment::Event::ChargeFeePoolDisabled { - currency_id: STABLECOIN, - foreign_amount: stable_ed * 10, - native_amount: native_ed * 10, - }.into()); - assert_eq!(module_transaction_payment::TokenExchangeRate::::get(STABLECOIN), None); - assert_eq!(module_transaction_payment::GlobalFeeSwapPath::::get(STABLECOIN), None); - } - - with_fee_path { - System::set_block_number(1); - - let funder: AccountId = account("funder", 0, SEED); - inject_liquidity(funder.clone(), STABLECOIN, NATIVE, 100 * dollar(STABLECOIN), 100 * dollar(NATIVE), false)?; - - let caller: AccountId = whitelisted_caller(); - let call = Box::new(frame_system::Call::remark { remark: vec![] }.into()); - set_balance(STABLECOIN, &caller, 100 * dollar(STABLECOIN)); - set_balance(NATIVE, &caller, 100 * dollar(NATIVE)); - - let fee_swap_path: Vec = vec![STABLECOIN, NATIVE]; - }: _(RawOrigin::Signed(caller), fee_swap_path.clone(), call) - - with_fee_currency { - System::set_block_number(1); - - let caller: AccountId = whitelisted_caller(); - let call = Box::new(frame_system::Call::remark { remark: vec![] }.into()); - set_balance(STABLECOIN, &caller, 100 * dollar(STABLECOIN)); - set_balance(NATIVE, &caller, 100 * dollar(NATIVE)); - - let (sub_account, stable_ed, pool_size, swap_threshold) = enable_fee_pool(); - TransactionPayment::enable_charge_fee_pool(RawOrigin::Root.into(), STABLECOIN, pool_size, swap_threshold).unwrap(); - - let exchange_rate = TransactionPayment::token_exchange_rate(STABLECOIN).unwrap(); - assert_has_event(module_transaction_payment::Event::ChargeFeePoolEnabled { - sub_account, - currency_id: STABLECOIN, - exchange_rate, - pool_size, - swap_threshold - }.into()); - }: _(RawOrigin::Signed(caller.clone()), STABLECOIN, call) - - with_fee_aggregated_path { - System::set_block_number(1); - - let caller: AccountId = whitelisted_caller(); - let call = Box::new(frame_system::Call::remark { remark: vec![] }.into()); - set_balance(STAKING, &caller, 100 * dollar(STAKING)); - set_balance(NATIVE, &caller, 100 * dollar(NATIVE)); - - enable_stable_asset(); - - // Taiga(STAKING, LIQUID), Dex(LIQUID, NATIVE) - let fee_aggregated_path = vec![ - AggregatedSwapPath::::Taiga(0, 0, 1), - AggregatedSwapPath::::Dex(vec![LIQUID, NATIVE]), - ]; - }: _(RawOrigin::Signed(caller.clone()), fee_aggregated_path, call) - - on_finalize { - }: { - TransactionPayment::on_finalize(System::block_number()); - } -} - -#[cfg(test)] -mod tests { - use super::*; - use crate::benchmarking::utils::tests::new_test_ext; - use orml_benchmarking::impl_benchmark_test_suite; - - impl_benchmark_test_suite!(new_test_ext(),); -} diff --git a/runtime/mandala/src/benchmarking/utils.rs b/runtime/mandala/src/benchmarking/utils.rs deleted file mode 100644 index 6ec49f28d..000000000 --- a/runtime/mandala/src/benchmarking/utils.rs +++ /dev/null @@ -1,277 +0,0 @@ -// This file is part of Acala. - -// Copyright (C) 2020-2025 Acala Foundation. -// SPDX-License-Identifier: GPL-3.0-or-later WITH Classpath-exception-2.0 - -// This program 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. - -// This program 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 this program. If not, see . - -use crate::{ - AcalaOracle, AccountId, AggregatedDex, AssetRegistry, Aura, Balance, Currencies, CurrencyId, Dex, - ExistentialDeposits, GetLiquidCurrencyId, GetNativeCurrencyId, GetStableCurrencyId, GetStakingCurrencyId, - MinimumCount, NativeTokenExistentialDeposit, OperatorMembershipAcala, Price, Runtime, RuntimeOrigin, StableAsset, - System, Timestamp, -}; - -use frame_benchmarking::account; -use frame_support::{ - assert_ok, - traits::{tokens::fungibles, Contains, OnInitialize}, -}; -use frame_system::RawOrigin; -use module_support::{AggregatedSwapPath, Erc20InfoMapping}; -use orml_traits::{GetByKey, MultiCurrencyExtended}; -pub use parity_scale_codec::Encode; -use primitives::currency::AssetMetadata; -use runtime_common::{TokenInfo, LCDOT}; -use sp_consensus_aura::AURA_ENGINE_ID; -use sp_runtime::{ - traits::{SaturatedConversion, StaticLookup, UniqueSaturatedInto}, - Digest, DigestItem, DispatchResult, MultiAddress, -}; -use sp_std::prelude::*; - -pub type SwapPath = AggregatedSwapPath; - -pub const NATIVE: CurrencyId = GetNativeCurrencyId::get(); -pub const STABLECOIN: CurrencyId = GetStableCurrencyId::get(); -pub const LIQUID: CurrencyId = GetLiquidCurrencyId::get(); -pub const STAKING: CurrencyId = GetStakingCurrencyId::get(); -const SEED: u32 = 0; - -pub fn lookup_of_account(who: AccountId) -> <::Lookup as StaticLookup>::Source { - ::Lookup::unlookup(who) -} - -pub fn register_native_asset(assets: Vec) { - assets.iter().for_each(|asset| { - let ed = if *asset == GetNativeCurrencyId::get() { - NativeTokenExistentialDeposit::get() - } else { - ExistentialDeposits::get(&asset) - }; - assert_ok!(AssetRegistry::register_native_asset( - RuntimeOrigin::root(), - *asset, - Box::new(AssetMetadata { - name: asset.name().unwrap().as_bytes().to_vec(), - symbol: asset.symbol().unwrap().as_bytes().to_vec(), - decimals: asset.decimals().unwrap(), - minimal_balance: ed, - }) - )); - }); -} - -pub fn set_balance(currency_id: CurrencyId, who: &AccountId, balance: Balance) { - assert_ok!(>::update_balance( - currency_id, - who, - balance.saturated_into() - )); -} - -pub fn feed_price(prices: Vec<(CurrencyId, Price)>) -> DispatchResult { - for i in 0..MinimumCount::get() { - let oracle: AccountId = account("oracle", 0, i); - if !OperatorMembershipAcala::contains(&oracle) { - OperatorMembershipAcala::add_member(RawOrigin::Root.into(), MultiAddress::Id(oracle.clone())) - .map_or_else(|e| Err(e.error), |_| Ok(()))?; - } - AcalaOracle::feed_values(RawOrigin::Signed(oracle).into(), prices.to_vec().try_into().unwrap()) - .map_or_else(|e| Err(e.error), |_| Ok(()))?; - } - - Ok(()) -} - -pub fn set_block_number_timestamp(block_number: u32, timestamp: u64) { - let slot = timestamp / Aura::slot_duration(); - let digest = Digest { - logs: vec![DigestItem::PreRuntime(AURA_ENGINE_ID, slot.encode())], - }; - System::initialize(&block_number, &Default::default(), &digest); - Aura::on_initialize(block_number); - Timestamp::set_timestamp(timestamp); -} - -#[allow(dead_code)] -pub fn set_balance_fungibles(currency_id: CurrencyId, who: &AccountId, balance: Balance) { - assert_ok!( as fungibles::Mutate>::mint_into(currency_id, who, balance)); -} - -pub fn dollar(currency_id: CurrencyId) -> Balance { - if matches!(currency_id, CurrencyId::Token(_)) - && module_asset_registry::EvmErc20InfoMapping::::decimals(currency_id).is_none() - { - register_native_asset(vec![currency_id]); - } - if let Some(decimals) = module_asset_registry::EvmErc20InfoMapping::::decimals(currency_id) { - 10u128.saturating_pow(decimals.into()) - } else { - panic!("{:?} not support decimals", currency_id); - } -} - -pub fn inject_liquidity( - maker: AccountId, - currency_id_a: CurrencyId, - currency_id_b: CurrencyId, - max_amount_a: Balance, - max_amount_b: Balance, - deposit: bool, -) -> Result<(), &'static str> { - // set balance - >::update_balance( - currency_id_a, - &maker, - max_amount_a.unique_saturated_into(), - )?; - >::update_balance( - currency_id_b, - &maker, - max_amount_b.unique_saturated_into(), - )?; - - let _ = Dex::enable_trading_pair(RawOrigin::Root.into(), currency_id_a, currency_id_b); - - Dex::add_liquidity( - RawOrigin::Signed(maker.clone()).into(), - currency_id_a, - currency_id_b, - max_amount_a, - max_amount_b, - Default::default(), - deposit, - )?; - Ok(()) -} - -pub fn register_stable_asset() -> DispatchResult { - let asset_metadata = AssetMetadata { - name: b"Token Name".to_vec(), - symbol: b"TN".to_vec(), - decimals: 12, - minimal_balance: 1, - }; - AssetRegistry::register_stable_asset(RawOrigin::Root.into(), Box::new(asset_metadata.clone())) -} - -pub fn create_stable_pools(assets: Vec, precisions: Vec, initial_a: u128) -> DispatchResult { - let pool_asset = CurrencyId::StableAssetPoolToken(0); - let mint_fee = 2u128; - let swap_fee = 3u128; - let redeem_fee = 5u128; - let fee_recipient: AccountId = account("fee", 0, SEED); - let yield_recipient: AccountId = account("yield", 1, SEED); - - register_stable_asset()?; - StableAsset::create_pool( - RawOrigin::Root.into(), - pool_asset, - assets, - precisions, - mint_fee, - swap_fee, - redeem_fee, - initial_a, - fee_recipient, - yield_recipient, - 1000000000000000000u128, - )?; - - Ok(()) -} - -/// Initializes all pools used in AggregatedDex `Swap` for trading to stablecoin -pub fn initialize_swap_pools(maker: AccountId) -> Result<(), &'static str> { - // Inject liquidity into all possible `AlternativeSwapPathJointList` - inject_liquidity( - maker.clone(), - LIQUID, - STABLECOIN, - 10_000 * dollar(LIQUID), - 10_000 * dollar(STABLECOIN), - false, - )?; - inject_liquidity( - maker.clone(), - STAKING, - LIQUID, - 10_000 * dollar(STAKING), - 10_000 * dollar(LIQUID), - false, - )?; - - // purposly inject too little liquidity to have failed path, still reads dexs to check for viable - // swap paths - inject_liquidity( - maker.clone(), - STAKING, - STABLECOIN, - 10 * dollar(STAKING), - 10 * dollar(STABLECOIN), - false, - )?; - inject_liquidity( - maker.clone(), - LCDOT, - STABLECOIN, - dollar(LCDOT), - dollar(STABLECOIN), - false, - )?; - inject_liquidity(maker.clone(), LCDOT, STAKING, dollar(LCDOT), dollar(STAKING), false)?; - - // Add and initialize stable pools, is manually added with changes to runtime - let assets_1 = vec![STAKING, LIQUID]; - create_stable_pools(assets_1.clone(), vec![1, 1], 10000u128)?; - for asset in assets_1 { - >::update_balance(asset, &maker, 1_000_000_000_000_000)?; - } - StableAsset::mint( - RawOrigin::Signed(maker.clone()).into(), - 0, - vec![1_000_000_000_000, 1_000_000_000_000], - 0, - )?; - - // Adds `AggregatedSwapPaths`, also mirrors runtimes state - AggregatedDex::update_aggregated_swap_paths( - RawOrigin::Root.into(), - vec![ - ( - (STAKING, STABLECOIN), - Some(vec![SwapPath::Taiga(0, 0, 1), SwapPath::Dex(vec![LIQUID, STABLECOIN])]), - ), - ( - (LIQUID, STABLECOIN), - Some(vec![SwapPath::Taiga(0, 1, 0), SwapPath::Dex(vec![STAKING, STABLECOIN])]), - ), - ], - )?; - - Ok(()) -} - -#[cfg(test)] -pub mod tests { - use sp_runtime::BuildStorage; - - pub fn new_test_ext() -> sp_io::TestExternalities { - frame_system::GenesisConfig::::default() - .build_storage() - .unwrap() - .into() - } -} diff --git a/runtime/mandala/src/benchmarking/vesting.rs b/runtime/mandala/src/benchmarking/vesting.rs deleted file mode 100644 index 86a9be25a..000000000 --- a/runtime/mandala/src/benchmarking/vesting.rs +++ /dev/null @@ -1,129 +0,0 @@ -// This file is part of Acala. - -// Copyright (C) 2020-2025 Acala Foundation. -// SPDX-License-Identifier: GPL-3.0-or-later WITH Classpath-exception-2.0 - -// This program 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. - -// This program 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 this program. If not, see . - -use super::{ - get_vesting_account, - utils::{lookup_of_account, set_balance, NATIVE}, -}; -use crate::{dollar, AccountId, Balance, BlockNumber, Currencies, Runtime, System, Vesting}; - -use sp_std::prelude::*; - -use frame_benchmarking::{account, whitelisted_caller}; -use frame_support::traits::Get; -use frame_system::RawOrigin; - -use orml_benchmarking::runtime_benchmarks; -use orml_traits::MultiCurrency; -use orml_vesting::VestingSchedule; - -pub type Schedule = VestingSchedule; - -const SEED: u32 = 0; - -runtime_benchmarks! { - { Runtime, orml_vesting } - - vested_transfer { - let schedule = Schedule { - start: 0, - period: 2, - period_count: 3, - per_period: ::MinVestedTransfer::get(), - }; - - // extra 1 dollar to pay fees - let from: AccountId = get_vesting_account(); - set_balance(NATIVE, &from, schedule.total_amount().unwrap() + dollar(NATIVE)); - - let to: AccountId = account("to", 0, SEED); - let to_lookup = lookup_of_account(to.clone()); - }: _(RawOrigin::Signed(from), to_lookup, schedule.clone()) - verify { - assert_eq!( - >::total_balance(NATIVE, &to), - schedule.total_amount().unwrap() - ); - } - - claim { - let i in 1 .. ::MaxVestingSchedules::get(); - - let mut schedule = Schedule { - start: 0, - period: 2, - period_count: 3, - per_period: ::MinVestedTransfer::get(), - }; - - let from: AccountId = get_vesting_account(); - // extra 1 dollar to pay fees - set_balance(NATIVE, &from, schedule.total_amount().unwrap() * i as u128 + dollar(NATIVE)); - - let to: AccountId = whitelisted_caller(); - let to_lookup = lookup_of_account(to.clone()); - - for _ in 0..i { - schedule.start = i; - Vesting::vested_transfer(RawOrigin::Signed(from.clone()).into(), to_lookup.clone(), schedule.clone())?; - } - System::set_block_number(schedule.end().unwrap() + 1u32); - }: _(RawOrigin::Signed(to.clone())) - verify { - assert_eq!( - >::free_balance(NATIVE, &to), - schedule.total_amount().unwrap() * i as u128, - ); - } - - update_vesting_schedules { - let i in 1 .. ::MaxVestingSchedules::get(); - - let mut schedule = Schedule { - start: 0, - period: 2, - period_count: 3, - per_period: ::MinVestedTransfer::get(), - }; - - let to: AccountId = account("to", 0, SEED); - set_balance(NATIVE, &to, schedule.total_amount().unwrap() * i as u128); - let to_lookup = lookup_of_account(to.clone()); - - let mut schedules = vec![]; - for _ in 0..i { - schedule.start = i; - schedules.push(schedule.clone()); - } - }: _(RawOrigin::Root, to_lookup, schedules) - verify { - assert_eq!( - >::free_balance(NATIVE, &to), - schedule.total_amount().unwrap() * i as u128 - ); - } -} - -#[cfg(test)] -mod tests { - use super::*; - use crate::benchmarking::utils::tests::new_test_ext; - use orml_benchmarking::impl_benchmark_test_suite; - - impl_benchmark_test_suite!(new_test_ext(),); -} diff --git a/runtime/mandala/src/benchmarks.rs b/runtime/mandala/src/benchmarks.rs new file mode 100644 index 000000000..5614fb8fc --- /dev/null +++ b/runtime/mandala/src/benchmarks.rs @@ -0,0 +1,33 @@ +// This file is part of Acala. + +// Copyright (C) 2020-2025 Acala Foundation. +// SPDX-License-Identifier: GPL-3.0-or-later WITH Classpath-exception-2.0 + +// This program 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. + +// This program 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 this program. If not, see . + +use crate::{AccountId, CurrencyId, TreasuryPalletId, ACA, AUSD, DOT, LDOT}; +use sp_runtime::traits::AccountIdConversion; +use sp_std::{vec, vec::Vec}; + +pub mod common { + include!("./benchmark_common.rs"); +} + +fn get_vesting_account() -> AccountId { + TreasuryPalletId::get().into_account_truncating() +} + +fn get_benchmarking_collateral_currency_ids() -> Vec { + vec![ACA, AUSD, DOT, LDOT, CurrencyId::StableAssetPoolToken(0)] +} diff --git a/runtime/mandala/src/genesis_config_presets.rs b/runtime/mandala/src/genesis_config_presets.rs index e6fd1a2a5..96ca9bab8 100644 --- a/runtime/mandala/src/genesis_config_presets.rs +++ b/runtime/mandala/src/genesis_config_presets.rs @@ -18,7 +18,8 @@ use crate::{ AccountId, BalancesConfig, CollatorSelectionConfig, NativeTokenExistentialDeposit, ParachainInfoConfig, - PolkadotXcmConfig, RuntimeGenesisConfig, SessionConfig, SessionKeys, SudoConfig, + PolkadotXcmConfig, RuntimeGenesisConfig, SessionConfig, SessionDuration, SessionKeys, SessionManagerConfig, + SudoConfig, }; use alloc::{vec, vec::Vec}; @@ -78,6 +79,9 @@ fn testnet_genesis( safe_xcm_version: Some(SAFE_XCM_VERSION) }, sudo: SudoConfig { key: Some(root) }, + session_manager: SessionManagerConfig { + session_duration: SessionDuration::get(), + } }) } diff --git a/runtime/mandala/src/lib.rs b/runtime/mandala/src/lib.rs index 32ecbb79a..659f03076 100644 --- a/runtime/mandala/src/lib.rs +++ b/runtime/mandala/src/lib.rs @@ -129,7 +129,8 @@ use runtime_common::{ pub use nutsfinance_stable_asset; mod authority; -mod benchmarking; +#[cfg(feature = "runtime-benchmarks")] +mod benchmarks; pub mod constants; #[cfg(feature = "genesis-builder")] mod genesis_config_presets; @@ -733,6 +734,8 @@ impl orml_auction::Config for Runtime { type AuctionId = AuctionId; type Handler = AuctionManager; type WeightInfo = weights::orml_auction::WeightInfo; + #[cfg(feature = "runtime-benchmarks")] + type BenchmarkHelper = benchmarks::common::BenchmarkHelper; } impl orml_authority::Config for Runtime { @@ -743,6 +746,8 @@ impl orml_authority::Config for Runtime { type AsOriginId = AuthoritysOriginId; type AuthorityConfig = AuthorityConfigImpl; type WeightInfo = weights::orml_authority::WeightInfo; + #[cfg(feature = "runtime-benchmarks")] + type BenchmarkHelper = benchmarks::common::BenchmarkHelper; } pub struct PaymentsDisputeResolver; @@ -821,15 +826,6 @@ parameter_types! { pub const MaxFeedValues: u32 = 10; // max 10 values allowed to feed in one call. } -#[cfg(feature = "runtime-benchmarks")] -pub struct BenchmarkHelper; -#[cfg(feature = "runtime-benchmarks")] -impl orml_oracle::BenchmarkHelper for BenchmarkHelper { - fn get_currency_id_value_pairs() -> sp_runtime::BoundedVec<(CurrencyId, Price), MaxFeedValues> { - sp_runtime::BoundedVec::default() - } -} - type AcalaDataProvider = orml_oracle::Instance1; impl orml_oracle::Config for Runtime { type OnNewData = (); @@ -843,7 +839,7 @@ impl orml_oracle::Config for Runtime { type WeightInfo = weights::orml_oracle::WeightInfo; type MaxFeedValues = MaxFeedValues; #[cfg(feature = "runtime-benchmarks")] - type BenchmarkHelper = BenchmarkHelper; + type BenchmarkHelper = benchmarks::common::BenchmarkInstanceHelper; } create_median_value_data_provider!( @@ -931,6 +927,8 @@ impl orml_tokens::Config for Runtime { type MaxReserves = MaxReserves; type ReserveIdentifier = ReserveIdentifier; type DustRemovalWhitelist = DustRemovalWhitelist; + #[cfg(feature = "runtime-benchmarks")] + type BenchmarkHelper = benchmarks::common::BenchmarkHelper; } parameter_type_with_key! { @@ -970,6 +968,8 @@ impl module_prices::Config for Runtime { type RewardRatePerRelaychainBlock = RewardRatePerRelaychainBlock; type PricingPegged = PricingPegged; type WeightInfo = weights::module_prices::WeightInfo; + #[cfg(feature = "runtime-benchmarks")] + type BenchmarkHelper = benchmarks::common::BenchmarkHelper; } parameter_types! { @@ -989,6 +989,8 @@ impl module_currencies::Config for Runtime { type GasToWeight = GasToWeight; type SweepOrigin = EnsureRootOrOneGeneralCouncil; type OnDust = module_currencies::TransferDust; + #[cfg(feature = "runtime-benchmarks")] + type BenchmarkHelper = benchmarks::common::BenchmarkHelper; } pub struct EnsureRootOrTreasury; @@ -1024,6 +1026,8 @@ impl orml_vesting::Config for Runtime { type WeightInfo = weights::orml_vesting::WeightInfo; type MaxVestingSchedules = ConstU32<100>; type BlockNumberProvider = RelaychainDataProvider; + #[cfg(feature = "runtime-benchmarks")] + type BenchmarkHelper = benchmarks::common::BenchmarkHelper; } parameter_types! { @@ -1081,6 +1085,8 @@ impl module_auction_manager::Config for Runtime { type UnsignedPriority = runtime_common::AuctionManagerUnsignedPriority; type EmergencyShutdown = EmergencyShutdown; type WeightInfo = weights::module_auction_manager::WeightInfo; + #[cfg(feature = "runtime-benchmarks")] + type BenchmarkHelper = benchmarks::common::BenchmarkHelper; } impl module_loans::Config for Runtime { @@ -1207,6 +1213,8 @@ impl module_cdp_engine::Config for Runtime { type EVMBridge = module_evm_bridge::EVMBridge; type SettleErc20EvmOrigin = SettleErc20EvmOrigin; type WeightInfo = weights::module_cdp_engine::WeightInfo; + #[cfg(feature = "runtime-benchmarks")] + type BenchmarkHelper = benchmarks::common::BenchmarkHelper; } parameter_types! { @@ -1218,6 +1226,8 @@ impl module_honzon::Config for Runtime { type DepositPerAuthorization = DepositPerAuthorization; type CollateralCurrencyIds = CollateralCurrencyIds; type WeightInfo = weights::module_honzon::WeightInfo; + #[cfg(feature = "runtime-benchmarks")] + type BenchmarkHelper = benchmarks::common::BenchmarkHelper; } impl module_emergency_shutdown::Config for Runtime { @@ -1227,6 +1237,8 @@ impl module_emergency_shutdown::Config for Runtime { type AuctionManagerHandler = AuctionManager; type ShutdownOrigin = EnsureRootOrHalfGeneralCouncil; type WeightInfo = weights::module_emergency_shutdown::WeightInfo; + #[cfg(feature = "runtime-benchmarks")] + type BenchmarkHelper = benchmarks::common::BenchmarkHelper; } parameter_types! { @@ -1258,6 +1270,8 @@ impl module_dex::Config for Runtime { type ListingOrigin = EnsureRootOrHalfGeneralCouncil; type ExtendedProvisioningBlocks = ExtendedProvisioningBlocks; type OnLiquidityPoolUpdated = (); + #[cfg(feature = "runtime-benchmarks")] + type BenchmarkHelper = benchmarks::common::BenchmarkHelper; } impl module_aggregated_dex::Config for Runtime { @@ -1267,6 +1281,8 @@ impl module_aggregated_dex::Config for Runtime { type DexSwapJointList = AlternativeSwapPathJointList; type SwapPathLimit = ConstU32<3>; type WeightInfo = weights::module_aggregated_dex::WeightInfo; + #[cfg(feature = "runtime-benchmarks")] + type BenchmarkHelper = benchmarks::common::BenchmarkHelper; } pub type RebasedStableAsset = module_support::RebasedStableAsset< @@ -1282,6 +1298,8 @@ impl module_dex_oracle::Config for Runtime { type Time = Timestamp; type UpdateOrigin = EnsureRootOrHalfGeneralCouncil; type WeightInfo = weights::module_dex_oracle::WeightInfo; + #[cfg(feature = "runtime-benchmarks")] + type BenchmarkHelper = benchmarks::common::BenchmarkHelper; } parameter_types! { @@ -1300,6 +1318,8 @@ impl module_cdp_treasury::Config for Runtime { type TreasuryAccount = HonzonTreasuryAccount; type WeightInfo = weights::module_cdp_treasury::WeightInfo; type StableAsset = RebasedStableAsset; + #[cfg(feature = "runtime-benchmarks")] + type BenchmarkHelper = benchmarks::common::BenchmarkHelper; } impl module_transaction_pause::Config for Runtime { @@ -1359,6 +1379,13 @@ impl module_transaction_payment::Config for Runtime { type CustomFeeSurplus = CustomFeeSurplus; type AlternativeFeeSurplus = AlternativeFeeSurplus; type DefaultFeeTokens = DefaultFeeTokens; + #[cfg(feature = "runtime-benchmarks")] + type BenchmarkHelper = benchmarks::common::BenchmarkHelper; +} + +parameter_types! { + pub MinBond: Balance = 10 * dollar(ACA); + pub const UnbondingPeriod: BlockNumber = 8 * DAYS; } impl module_earning::Config for Runtime { @@ -1367,11 +1394,13 @@ impl module_earning::Config for Runtime { type OnBonded = module_incentives::OnEarningBonded; type OnUnbonded = module_incentives::OnEarningUnbonded; type OnUnstakeFee = Treasury; // fee goes to treasury - type MinBond = ConstU128<100>; - type UnbondingPeriod = ConstU32<3>; + type MinBond = MinBond; + type UnbondingPeriod = UnbondingPeriod; type MaxUnbondingChunks = ConstU32<3>; type LockIdentifier = EarningLockIdentifier; type WeightInfo = weights::module_earning::WeightInfo; + #[cfg(feature = "runtime-benchmarks")] + type BenchmarkHelper = benchmarks::common::BenchmarkHelper; } impl module_evm_accounts::Config for Runtime { @@ -1388,6 +1417,8 @@ impl module_asset_registry::Config for Runtime { type EVMBridge = module_evm_bridge::EVMBridge; type RegisterOrigin = EnsureRootOrHalfGeneralCouncil; type WeightInfo = weights::module_asset_registry::WeightInfo; + #[cfg(feature = "runtime-benchmarks")] + type BenchmarkHelper = benchmarks::common::BenchmarkHelper; } parameter_type_with_key! { @@ -1429,6 +1460,8 @@ impl module_incentives::Config for Runtime { type EmergencyShutdown = EmergencyShutdown; type PalletId = IncentivesPalletId; type WeightInfo = weights::module_incentives::WeightInfo; + #[cfg(feature = "runtime-benchmarks")] + type BenchmarkHelper = benchmarks::common::BenchmarkHelper; } parameter_types! { @@ -1481,6 +1514,8 @@ impl module_homa_validator_list::Config for Runtime { type LiquidStakingExchangeRateProvider = Homa; type CurrentEra = Homa; type WeightInfo = weights::module_homa_validator_list::WeightInfo; + #[cfg(feature = "runtime-benchmarks")] + type BenchmarkHelper = benchmarks::common::BenchmarkHelper; } parameter_types! { @@ -1502,6 +1537,8 @@ impl module_nominees_election::Config for Runtime { type OnUnbonded = module_incentives::OnNomineesElectionUnbonded; type CurrentEra = Homa; type WeightInfo = weights::module_nominees_election::WeightInfo; + #[cfg(feature = "runtime-benchmarks")] + type BenchmarkHelper = benchmarks::common::BenchmarkHelper; } parameter_types! { @@ -1848,6 +1885,8 @@ impl nutsfinance_stable_asset::Config for Runtime { type WeightInfo = weights::nutsfinance_stable_asset::WeightInfo; type ListingOrigin = EnsureRootOrHalfGeneralCouncil; type EnsurePoolAssetId = EnsurePoolAssetId; + #[cfg(feature = "runtime-benchmarks")] + type BenchmarkHelper = benchmarks::common::BenchmarkHelper; } define_combined_task! { @@ -1871,6 +1910,8 @@ impl module_idle_scheduler::Config for Runtime { // Number of relay chain blocks produced with no parachain blocks finalized, // once this number is reached idle scheduler is disabled as block production is slow type DisableBlockThreshold = ConstU32<6>; + #[cfg(feature = "runtime-benchmarks")] + type BenchmarkHelper = benchmarks::common::BenchmarkHelper; } impl cumulus_pallet_aura_ext::Config for Runtime {} @@ -2183,49 +2224,62 @@ construct_runtime!( } ); -#[cfg(feature = "runtime-benchmarks")] -#[macro_use] -extern crate orml_benchmarking; - #[cfg(feature = "runtime-benchmarks")] mod benches { - define_benchmarks!( - [module_dex, benchmarking::dex] - [module_dex_oracle, benchmarking::dex_oracle] - [module_asset_registry, benchmarking::asset_registry] - [module_auction_manager, benchmarking::auction_manager] - [module_cdp_engine, benchmarking::cdp_engine] - [module_earning, benchmarking::earning] - [module_emergency_shutdown, benchmarking::emergency_shutdown] - [module_evm, benchmarking::evm] - [module_homa, benchmarking::homa] - [module_homa_validator_list, benchmarking::homa_validator_list] - [module_honzon, benchmarking::honzon] - [module_cdp_treasury, benchmarking::cdp_treasury] - [module_collator_selection, benchmarking::collator_selection] - [module_nominees_election, benchmarking::nominees_election] - [module_transaction_pause, benchmarking::transaction_pause] - [module_transaction_payment, benchmarking::transaction_payment] - [module_incentives, benchmarking::incentives] - [module_prices, benchmarking::prices] - [module_evm_accounts, benchmarking::evm_accounts] - [module_currencies, benchmarking::currencies] - [module_session_manager, benchmarking::session_manager] - [module_liquid_crowdloan, benchmarking::liquid_crowdloan] - [orml_tokens, benchmarking::tokens] - [orml_vesting, benchmarking::vesting] - [orml_auction, benchmarking::auction] - [orml_authority, benchmarking::authority] - [nutsfinance_stable_asset, benchmarking::nutsfinance_stable_asset] - [module_idle_scheduler, benchmarking::idle_scheduler] - [module_aggregated_dex, benchmarking::aggregated_dex] + // use super::*; + // use alloc::boxed::Box; + // use frame_support::assert_ok; + // use kusama_runtime_constants::system_parachain::PeopleParaId; + // use system_parachains_constants::kusama::locations::PeopleLocation; + + frame_benchmarking::define_benchmarks!( + [module_aggregated_dex, AggregatedDex] + [module_asset_registry, AssetRegistry] + [module_auction_manager, AuctionManager] + [module_cdp_engine, CdpEngine] + [module_cdp_treasury, CdpTreasury] + [module_collator_selection, CollatorSelection] + [module_currencies, Currencies] + [module_dex, Dex] + [module_dex_oracle, DexOracle] + [module_earning, Earning] + [module_emergency_shutdown, EmergencyShutdown] + [module_evm_accounts, EvmAccounts] + [module_evm, EVM] + [module_homa, Homa] + [module_homa_validator_list, HomaValidatorList] + [module_honzon, Honzon] + [module_idle_scheduler, IdleScheduler] + [module_incentives, Incentives] + [module_liquid_crowdloan, LiquidCrowdloan] + [module_nft, NFT] + [module_nominees_election, NomineesElection] + [module_prices, Prices] + [module_session_manager, SessionManager] + [module_transaction_pause, TransactionPause] + [module_transaction_payment, TransactionPayment] + [orml_auction, Auction] + [orml_authority, Authority] + [orml_oracle, AcalaOracle] + [orml_tokens, Tokens] + [orml_vesting, Vesting] + // Acala Ecosystem Modules + [nutsfinance_stable_asset, StableAsset] + // XCM + /* [pallet_xcm, PalletXcmExtrinsicsBenchmark::] */ ); - // frame_benchmarking::define_benchmarks!( - // // XCM - // [pallet_xcm, PalletXcmExtrinsicsBenchmark::] - // ); + + pub use frame_benchmarking::{BenchmarkBatch, BenchmarkList}; + pub use frame_support::traits::{StorageInfoTrait, WhitelistedStorageKeys}; + // pub use frame_system_benchmarking::{ + // extensions::Pallet as SystemExtensionsBench, Pallet as SystemBench, + // }; + pub use sp_storage::TrackedStorageKey; } +#[cfg(feature = "runtime-benchmarks")] +use benches::*; + impl_runtime_apis! { impl sp_api::Core for Runtime { fn version() -> RuntimeVersion { @@ -2610,106 +2664,16 @@ impl_runtime_apis! { Vec, Vec, ) { - use frame_benchmarking::{list_benchmark as frame_list_benchmark, Benchmarking, BenchmarkList}; - use frame_support::traits::StorageInfoTrait; - - use module_nft::benchmarking::Pallet as NftBench; - // use pallet_xcm::benchmarking::Pallet as PalletXcmExtrinsicsBenchmark; - let mut list = Vec::::new(); - - frame_list_benchmark!(list, extra, module_nft, NftBench::); list_benchmarks!(list, extra); let storage_info = AllPalletsWithSystem::storage_info(); - return (list, storage_info) } fn dispatch_benchmark( config: frame_benchmarking::BenchmarkConfig ) -> Result, alloc::string::String> { - use frame_benchmarking::{Benchmarking, BenchmarkBatch, BenchmarkError, add_benchmark as frame_add_benchmark}; - use module_nft::benchmarking::Pallet as NftBench; - use frame_support::traits::{WhitelistedStorageKeys, TrackedStorageKey}; - - // const UNITS: Balance = 1_000_000_000_000; - // const CENTS: Balance = UNITS / 100; - - // parameter_types! { - // pub FeeAssetId: AssetId = AssetId(Location::parent()); - // pub const BaseDeliveryFee: u128 = CENTS.saturating_mul(3); - // } - // pub type PriceForParentDelivery = polkadot_runtime_common::xcm_sender::ExponentialPrice< - // FeeAssetId, - // BaseDeliveryFee, - // TransactionByteFee, - // ParachainSystem, - // >; - - // use pallet_xcm::benchmarking::Pallet as PalletXcmExtrinsicsBenchmark; - // impl pallet_xcm::benchmarking::Config for Runtime { - // type DeliveryHelper = cumulus_primitives_utility::ToParentDeliveryHelper< - // xcm_config::XcmConfig, - // ExistentialDepositAsset, - // PriceForParentDelivery, - // >; - // fn reachable_dest() -> Option { - // Some(Parent.into()) - // } - - // fn teleportable_asset_and_dest() -> Option<(Asset, Location)> { - // Some(( - // Asset { - // fun: Fungible(NativeTokenExistentialDeposit::get()), - // id: AssetId(Parent.into()) - // }, - // Parent.into(), - // )) - // } - - // fn reserve_transferable_asset_and_dest() -> Option<(Asset, Location)> { - // None - // } - - // fn get_asset() -> Asset { - // Asset { - // id: AssetId(Location::parent()), - // fun: Fungible(UNITS), - // } - // } - // } - - // parameter_types! { - // pub ExistentialDepositAsset: Option = Some(( - // Location::parent(), - // NativeTokenExistentialDeposit::get() - // ).into()); - // } - - // impl pallet_xcm_benchmarks::Config for Runtime { - // type XcmConfig = xcm_config::XcmConfig; - // type AccountIdConverter = xcm_config::LocationToAccountId; - // type DeliveryHelper = cumulus_primitives_utility::ToParentDeliveryHelper< - // xcm_config::XcmConfig, - // ExistentialDepositAsset, - // PriceForParentDelivery, - // >; - // fn valid_destination() -> Result { - // Ok(Location::parent()) - // } - // fn worst_case_holding(_depositable_count: u32) -> Assets { - // // just concrete assets according to relay chain. - // let assets: Vec = vec![ - // Asset { - // id: AssetId(Location::parent()), - // fun: Fungible(1_000_000 * UNITS), - // } - // ]; - // assets.into() - // } - // } - let mut whitelist: Vec = AllPalletsWithSystem::whitelisted_storage_keys(); // Treasury Account @@ -2721,7 +2685,6 @@ impl_runtime_apis! { let mut batches = Vec::::new(); let params = (&config, &whitelist); - frame_add_benchmark!(params, batches, module_nft, NftBench::); add_benchmarks!(params, batches); if batches.is_empty() { return Err("Benchmark not found for this module.".into()) } diff --git a/runtime/mandala/src/weights/orml_auction.rs b/runtime/mandala/src/weights/orml_auction.rs index 3131d8743..da804229b 100644 --- a/runtime/mandala/src/weights/orml_auction.rs +++ b/runtime/mandala/src/weights/orml_auction.rs @@ -59,7 +59,7 @@ impl orml_auction::WeightInfo for WeightInfo { // Proof: `EvmAccounts::EvmAddresses` (`max_values`: None, `max_size`: Some(60), added: 2535, mode: `MaxEncodedLen`) // Storage: `Auction::AuctionEndTime` (r:0 w:2) // Proof: `Auction::AuctionEndTime` (`max_values`: None, `max_size`: Some(32), added: 2507, mode: `MaxEncodedLen`) - fn bid_collateral_auction() -> Weight { + fn bid() -> Weight { // Proof Size summary in bytes: // Measured: `3551` // Estimated: `8856`