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