From 6517520521d682a8babd9f0f8990d769cc7cbc71 Mon Sep 17 00:00:00 2001 From: DrPing Date: Tue, 14 Jan 2025 13:07:51 +0700 Subject: [PATCH 1/9] feat(commission): add delay for changing commission and add maximum rate change --- pallets/parachain-staking/src/lib.rs | 75 +++++++++++++--- pallets/parachain-staking/src/mock.rs | 7 +- pallets/parachain-staking/src/tests.rs | 119 +++++++++++++++++++++++++ runtime/krest/src/lib.rs | 4 + runtime/peaq-dev/src/lib.rs | 5 ++ runtime/peaq/src/lib.rs | 4 + 6 files changed, 200 insertions(+), 14 deletions(-) diff --git a/pallets/parachain-staking/src/lib.rs b/pallets/parachain-staking/src/lib.rs index d36dba909..7909298a9 100644 --- a/pallets/parachain-staking/src/lib.rs +++ b/pallets/parachain-staking/src/lib.rs @@ -337,6 +337,14 @@ pub mod pallet { /// Weight information for extrinsics in this pallet. type WeightInfo: WeightInfo; + + /// The maximum permill that a commission can change in one go. + #[pallet::constant] + type MaxCommissionChange: Get; + + /// The minimum interval between two commission changes. + #[pallet::constant] + type CommissionChangeInterval: Get>; } #[pallet::error] @@ -432,6 +440,10 @@ pub mod pallet { CommissionTooHigh, /// Sudo cannot force new round if payouts are ongoing PayoutsOngoing, + /// The commission change is too high. + CommissionChangeTooHigh, + /// The commission change is too frequent. + CommissionChangeTooEarly, } #[pallet::event] @@ -677,6 +689,11 @@ pub mod pallet { pub(crate) type DelayedPayoutInfo = StorageValue<_, DelayedPayoutInfoT>, OptionQuery>; + #[pallet::storage] + #[pallet::getter(fn last_commission_change)] + pub type LastCommissionChange = + StorageMap<_, Blake2_128Concat, T::AccountId, BlockNumberFor, ValueQuery>; + #[pallet::genesis_config] pub struct GenesisConfig { pub stakers: GenesisStaker, @@ -1969,22 +1986,54 @@ pub mod pallet { ))] pub fn set_commission(origin: OriginFor, commission: Permill) -> DispatchResult { let collator = ensure_signed(origin)?; - CandidatePool::::get(&collator).ok_or(Error::::CandidateNotFound)?; - if commission > Permill::from_percent(100) { - return Err(Error::::CommissionTooHigh.into()) - } + let current_block = >::block_number(); - >::mutate(&collator, |maybe_candidate| { - if let Some(candidate) = maybe_candidate { - candidate.set_commission(commission); - } - }); + // Check if the collator exists + let mut candidate = + CandidatePool::::get(&collator).ok_or(Error::::CandidateNotFound)?; - // Emit an event that the commission was updated. - Self::deposit_event(crate::pallet::Event::CollatorCommissionChanged( - collator, commission, - )); + // Check the time since the last commission change + let last_change = LastCommissionChange::::get(&collator); + ensure!( + current_block >= last_change + T::CommissionChangeInterval::get(), + Error::::CommissionChangeTooEarly + ); + + // Check the maximum change commission + let max_change = T::MaxCommissionChange::get(); + let current_commission = candidate.commission; + let change = if commission > current_commission { + commission - current_commission + } else { + current_commission - commission + }; + ensure!(change <= max_change, Error::::CommissionChangeTooHigh); + + // Update the commission and the last change time + candidate.set_commission(commission); + CandidatePool::::insert(&collator, candidate); + LastCommissionChange::::insert(&collator, current_block); + + // Emit an event that the commission was updated + Self::deposit_event(Event::CollatorCommissionChanged(collator, commission)); Ok(()) + // let collator = ensure_signed(origin)?; + // CandidatePool::::get(&collator).ok_or(Error::::CandidateNotFound)?; + // if commission > Permill::from_percent(100) { + // return Err(Error::::CommissionTooHigh.into()) + // } + + // >::mutate(&collator, |maybe_candidate| { + // if let Some(candidate) = maybe_candidate { + // candidate.set_commission(commission); + // } + // }); + + // // Emit an event that the commission was updated. + // Self::deposit_event(crate::pallet::Event::CollatorCommissionChanged( + // collator, commission, + // )); + // Ok(()) } } diff --git a/pallets/parachain-staking/src/mock.rs b/pallets/parachain-staking/src/mock.rs index b1816d153..108280641 100644 --- a/pallets/parachain-staking/src/mock.rs +++ b/pallets/parachain-staking/src/mock.rs @@ -32,7 +32,7 @@ use sp_runtime::{ impl_opaque_keys, testing::UintAuthorityId, traits::{BlakeTwo256, ConvertInto, IdentityLookup, OpaqueKeys}, - BuildStorage, Perbill, + BuildStorage, Perbill, Permill, }; use sp_std::fmt::Debug; @@ -154,6 +154,9 @@ parameter_types! { pub const MinDelegatorStake: Balance = 5; pub const MinDelegation: Balance = 3; pub const MaxUnstakeRequests: u32 = 6; + pub const MaxCommissionChange: Permill = Permill::from_percent(10); + pub const CommissionChangeInterval: BlockNumber = 1; + } impl Config for Test { @@ -177,6 +180,8 @@ impl Config for Test { type MaxUnstakeRequests = MaxUnstakeRequests; type PotId = PotId; type WeightInfo = crate::weights::WeightInfo; + type CommissionChangeInterval = CommissionChangeInterval; + type MaxCommissionChange = MaxCommissionChange; } impl_opaque_keys! { diff --git a/pallets/parachain-staking/src/tests.rs b/pallets/parachain-staking/src/tests.rs index 0b3dff9f6..81b9cf181 100644 --- a/pallets/parachain-staking/src/tests.rs +++ b/pallets/parachain-staking/src/tests.rs @@ -3990,3 +3990,122 @@ fn check_snapshot_is_cleared() { assert_eq!(at_stake.len(), 0); }); } + +#[test] +fn change_commission_too_frequently() { + ExtBuilder::default() + .with_balances(vec![(1, 1000), (2, 1000), (3, 1000)]) + .with_collators(vec![(1, 500)]) + .with_delegators(vec![(2, 1, 600), (3, 1, 400)]) + .build() + .execute_with(|| { + assert!(System::events().is_empty()); + + assert_ok!(Balances::force_set_balance( + RawOrigin::Root.into(), + StakePallet::account_id(), + 1000, + )); + + assert_ok!(StakePallet::set_commission( + RuntimeOrigin::signed(1), + Permill::from_percent(10) + )); + let state = CandidatePool::::get(1).unwrap(); + assert_eq!(state.commission, Permill::from_percent(10)); + assert_eq!( + StakePallet::candidate_pool(1).unwrap().commission, + Permill::from_percent(10) + ); + + // change commission too frequently + assert_noop!( + StakePallet::set_commission(RuntimeOrigin::signed(1), Permill::from_percent(20)), + Error::::CommissionChangeTooEarly + ); + // change commission too frequently + assert_noop!( + StakePallet::set_commission(RuntimeOrigin::signed(1), Permill::from_percent(30)), + Error::::CommissionChangeTooEarly + ); + }); +} + +#[test] +fn change_commission_after_while() { + ExtBuilder::default() + .with_balances(vec![(1, 1000), (2, 1000), (3, 1000)]) + .with_collators(vec![(1, 500)]) + .with_delegators(vec![(2, 1, 600), (3, 1, 400)]) + .build() + .execute_with(|| { + assert!(System::events().is_empty()); + + assert_ok!(Balances::force_set_balance( + RawOrigin::Root.into(), + StakePallet::account_id(), + 1000, + )); + + assert_ok!(StakePallet::set_commission( + RuntimeOrigin::signed(1), + Permill::from_percent(10) + )); + let state = CandidatePool::::get(1).unwrap(); + assert_eq!(state.commission, Permill::from_percent(10)); + assert_eq!( + StakePallet::candidate_pool(1).unwrap().commission, + Permill::from_percent(10) + ); + + // change commission after a while + roll_to(10, vec![]); + assert_ok!(StakePallet::set_commission( + RuntimeOrigin::signed(1), + Permill::from_percent(20) + )); + let state = CandidatePool::::get(1).unwrap(); + assert_eq!(state.commission, Permill::from_percent(20)); + assert_eq!( + StakePallet::candidate_pool(1).unwrap().commission, + Permill::from_percent(20) + ); + }); +} + +#[test] +fn change_commission_by_too_much() { + ExtBuilder::default() + .with_balances(vec![(1, 1000), (2, 1000), (3, 1000)]) + .with_collators(vec![(1, 500)]) + .with_delegators(vec![(2, 1, 600), (3, 1, 400)]) + .build() + .execute_with(|| { + assert!(System::events().is_empty()); + + assert_ok!(Balances::force_set_balance( + RawOrigin::Root.into(), + StakePallet::account_id(), + 1000, + )); + + assert_ok!(StakePallet::set_commission( + RuntimeOrigin::signed(1), + Permill::from_percent(10) + )); + let state = CandidatePool::::get(1).unwrap(); + assert_eq!(state.commission, Permill::from_percent(10)); + assert_eq!( + StakePallet::candidate_pool(1).unwrap().commission, + Permill::from_percent(10) + ); + + roll_to(10, vec![]); + + // change commission by too much + assert_noop!( + StakePallet::set_commission(RuntimeOrigin::signed(1), Permill::from_percent(30)), + Error::::CommissionChangeTooHigh + ); + }); +} diff --git a/runtime/krest/src/lib.rs b/runtime/krest/src/lib.rs index cc0f535cd..f5dc3d02a 100644 --- a/runtime/krest/src/lib.rs +++ b/runtime/krest/src/lib.rs @@ -825,6 +825,8 @@ pub mod staking { pub const MaxCollatorCandidates: u32 = 128; /// Maximum number of concurrent requests to unlock unstaked balance pub const MaxUnstakeRequests: u32 = 10; + pub const MaxCommissionChange: Permill = Permill::from_percent(10); // Maximum 10% change + pub const CommissionChangeInterval: BlockNumber = DAYS; // 24 hours } } @@ -851,6 +853,8 @@ impl parachain_staking::Config for Runtime { type MaxUnstakeRequests = staking::MaxUnstakeRequests; type WeightInfo = parachain_staking::weights::WeightInfo; + type CommissionChangeInterval = staking::CommissionChangeInterval; + type MaxCommissionChange = staking::MaxCommissionChange; } /// Implements the adapters for depositing unbalanced tokens on pots diff --git a/runtime/peaq-dev/src/lib.rs b/runtime/peaq-dev/src/lib.rs index d23d2d4e9..debd048d6 100644 --- a/runtime/peaq-dev/src/lib.rs +++ b/runtime/peaq-dev/src/lib.rs @@ -831,6 +831,9 @@ pub mod staking { pub const MaxCollatorCandidates: u32 = 64; /// Maximum number of concurrent requests to unlock unstaked balance pub const MaxUnstakeRequests: u32 = 10; + + pub const MaxCommissionChange: Permill = Permill::from_percent(10); // Maximum 10% change + pub const CommissionChangeInterval: BlockNumber = DAYS; // 24 hours } } @@ -857,6 +860,8 @@ impl parachain_staking::Config for Runtime { type MaxUnstakeRequests = staking::MaxUnstakeRequests; type WeightInfo = parachain_staking::weights::WeightInfo; + type CommissionChangeInterval = staking::CommissionChangeInterval; + type MaxCommissionChange = staking::MaxCommissionChange; } /// Implements the adapters for depositing unbalanced tokens on pots diff --git a/runtime/peaq/src/lib.rs b/runtime/peaq/src/lib.rs index e93b780c4..1bc46f713 100644 --- a/runtime/peaq/src/lib.rs +++ b/runtime/peaq/src/lib.rs @@ -848,6 +848,8 @@ pub mod staking { pub const MaxCollatorCandidates: u32 = 64; /// Maximum number of concurrent requests to unlock unstaked balance pub const MaxUnstakeRequests: u32 = 10; + pub const MaxCommissionChange: Permill = Permill::from_percent(10); // Maximum 10% change + pub const CommissionChangeInterval: BlockNumber = DAYS; // 24 hours } } @@ -874,6 +876,8 @@ impl parachain_staking::Config for Runtime { type MaxUnstakeRequests = staking::MaxUnstakeRequests; type WeightInfo = parachain_staking::weights::WeightInfo; + type CommissionChangeInterval = staking::CommissionChangeInterval; + type MaxCommissionChange = staking::MaxCommissionChange; } /// Implements the adapters for depositing unbalanced tokens on pots From b8b20bfd9063386200ca542714de5ac1bb149ed9 Mon Sep 17 00:00:00 2001 From: DrPing Date: Tue, 14 Jan 2025 13:10:26 +0700 Subject: [PATCH 2/9] fix(commission): remove commented code --- pallets/parachain-staking/src/lib.rs | 17 ----------------- 1 file changed, 17 deletions(-) diff --git a/pallets/parachain-staking/src/lib.rs b/pallets/parachain-staking/src/lib.rs index 7909298a9..23022b9a6 100644 --- a/pallets/parachain-staking/src/lib.rs +++ b/pallets/parachain-staking/src/lib.rs @@ -2017,23 +2017,6 @@ pub mod pallet { // Emit an event that the commission was updated Self::deposit_event(Event::CollatorCommissionChanged(collator, commission)); Ok(()) - // let collator = ensure_signed(origin)?; - // CandidatePool::::get(&collator).ok_or(Error::::CandidateNotFound)?; - // if commission > Permill::from_percent(100) { - // return Err(Error::::CommissionTooHigh.into()) - // } - - // >::mutate(&collator, |maybe_candidate| { - // if let Some(candidate) = maybe_candidate { - // candidate.set_commission(commission); - // } - // }); - - // // Emit an event that the commission was updated. - // Self::deposit_event(crate::pallet::Event::CollatorCommissionChanged( - // collator, commission, - // )); - // Ok(()) } } From 8c4bf3a3284b7dbec6ec24cac2ff67f27c27e73b Mon Sep 17 00:00:00 2001 From: DrPing Date: Tue, 14 Jan 2025 13:53:34 +0700 Subject: [PATCH 3/9] fix(commission): add commission delay and max change to mock precompile --- precompiles/parachain-staking/src/mock.rs | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/precompiles/parachain-staking/src/mock.rs b/precompiles/parachain-staking/src/mock.rs index 836477843..333a346d9 100644 --- a/precompiles/parachain-staking/src/mock.rs +++ b/precompiles/parachain-staking/src/mock.rs @@ -34,7 +34,7 @@ use sp_runtime::{ impl_opaque_keys, testing::UintAuthorityId, traits::{BlakeTwo256, ConvertInto, IdentityLookup, OpaqueKeys}, - BuildStorage, Perbill, + BuildStorage, Perbill, Permill, }; use sp_std::fmt::Debug; @@ -206,6 +206,8 @@ parameter_types! { pub const MinDelegatorStake: Balance = 5; pub const MinDelegation: Balance = 3; pub const MaxUnstakeRequests: u32 = 6; + pub const MaxCommissionChange: Permill = Permill::from_percent(10); + pub const CommissionChangeInterval: BlockNumber = 1; } impl parachain_staking::Config for Test { @@ -229,6 +231,8 @@ impl parachain_staking::Config for Test { type MaxUnstakeRequests = MaxUnstakeRequests; type PotId = PotId; type WeightInfo = parachain_staking::weights::WeightInfo; + type CommissionChangeInterval = CommissionChangeInterval; + type MaxCommissionChange = MaxCommissionChange; } impl_opaque_keys! { From 1bfdab2d0bd1b4790104bd77cedc7b396b237c6c Mon Sep 17 00:00:00 2001 From: DrPing Date: Wed, 15 Jan 2025 14:27:52 +0700 Subject: [PATCH 4/9] feat(commission): remove MaxCommissionChange from constant and make sudo user able to change it --- node/src/parachain/dev_chain_spec.rs | 3 +- node/src/parachain/krest_chain_spec.rs | 3 +- node/src/parachain/peaq_chain_spec.rs | 3 +- pallets/parachain-staking/src/lib.rs | 38 +++++++++++++++++---- pallets/parachain-staking/src/mock.rs | 12 ++++--- pallets/parachain-staking/src/weightinfo.rs | 1 + pallets/parachain-staking/src/weights.rs | 14 ++++++++ precompiles/parachain-staking/src/mock.rs | 2 +- runtime/krest/src/lib.rs | 3 +- runtime/peaq-dev/src/lib.rs | 4 +-- runtime/peaq/src/lib.rs | 3 +- 11 files changed, 64 insertions(+), 22 deletions(-) diff --git a/node/src/parachain/dev_chain_spec.rs b/node/src/parachain/dev_chain_spec.rs index 0f0f920e2..56a2877af 100644 --- a/node/src/parachain/dev_chain_spec.rs +++ b/node/src/parachain/dev_chain_spec.rs @@ -12,7 +12,7 @@ use sp_consensus_aura::sr25519::AuthorityId as AuraId; use sp_core::{sr25519, Pair, Public}; use sp_runtime::{ traits::{IdentifyAccount, Verify}, - Perbill, + Perbill, Permill, }; /// Specialized `ChainSpec`. This is a specialization of the general Substrate ChainSpec type. @@ -133,6 +133,7 @@ fn configure_genesis( parachain_staking: ParachainStakingConfig { stakers, max_candidate_stake: staking::MAX_COLLATOR_STAKE, + max_commission_change: Permill::from_percent(100), }, inflation_manager: Default::default(), block_reward: BlockRewardConfig { diff --git a/node/src/parachain/krest_chain_spec.rs b/node/src/parachain/krest_chain_spec.rs index 347386ae4..389bbfbe7 100644 --- a/node/src/parachain/krest_chain_spec.rs +++ b/node/src/parachain/krest_chain_spec.rs @@ -9,7 +9,7 @@ use peaq_primitives_xcm::{AccountId, Balance}; use runtime_common::TOKEN_DECIMALS; use sc_service::{ChainType, Properties}; use sp_consensus_aura::sr25519::AuthorityId as AuraId; -use sp_runtime::Perbill; +use sp_runtime::{Perbill, Permill}; use crate::parachain::dev_chain_spec::{authority_keys_from_seed, get_account_id_from_seed}; @@ -118,6 +118,7 @@ fn configure_genesis( parachain_staking: ParachainStakingConfig { stakers, max_candidate_stake: staking::MAX_COLLATOR_STAKE, + max_commission_change: Permill::from_percent(100), }, inflation_manager: Default::default(), block_reward: BlockRewardConfig { diff --git a/node/src/parachain/peaq_chain_spec.rs b/node/src/parachain/peaq_chain_spec.rs index c74e3f230..34fe6744e 100644 --- a/node/src/parachain/peaq_chain_spec.rs +++ b/node/src/parachain/peaq_chain_spec.rs @@ -9,7 +9,7 @@ use peaq_runtime::{ use runtime_common::TOKEN_DECIMALS; use sc_service::{ChainType, Properties}; use sp_consensus_aura::sr25519::AuthorityId as AuraId; -use sp_runtime::Perbill; +use sp_runtime::{Perbill, Permill}; use crate::parachain::dev_chain_spec::{authority_keys_from_seed, get_account_id_from_seed}; @@ -122,6 +122,7 @@ fn configure_genesis( parachain_staking: ParachainStakingConfig { stakers, max_candidate_stake: staking::MAX_COLLATOR_STAKE, + max_commission_change: Permill::from_percent(100), }, inflation_manager: Default::default(), block_reward: BlockRewardConfig { diff --git a/pallets/parachain-staking/src/lib.rs b/pallets/parachain-staking/src/lib.rs index 23022b9a6..f33b3d573 100644 --- a/pallets/parachain-staking/src/lib.rs +++ b/pallets/parachain-staking/src/lib.rs @@ -338,10 +338,6 @@ pub mod pallet { /// Weight information for extrinsics in this pallet. type WeightInfo: WeightInfo; - /// The maximum permill that a commission can change in one go. - #[pallet::constant] - type MaxCommissionChange: Get; - /// The minimum interval between two commission changes. #[pallet::constant] type CommissionChangeInterval: Get>; @@ -531,6 +527,9 @@ pub mod pallet { /// The commission for a collator has been changed. /// \[collator's account, new commission\] CollatorCommissionChanged(T::AccountId, Permill), + /// The commission maximum change has been changed. + /// \[new value\] + MaxCommissionChangeUpdated(Permill), } #[pallet::hooks] @@ -694,15 +693,24 @@ pub mod pallet { pub type LastCommissionChange = StorageMap<_, Blake2_128Concat, T::AccountId, BlockNumberFor, ValueQuery>; + #[pallet::storage] + #[pallet::getter(fn max_commission_change)] + pub type MaxCommissionChange = StorageValue<_, Permill, ValueQuery>; + #[pallet::genesis_config] pub struct GenesisConfig { pub stakers: GenesisStaker, pub max_candidate_stake: BalanceOf, + pub max_commission_change: Permill, } impl Default for GenesisConfig { fn default() -> Self { - Self { stakers: Default::default(), max_candidate_stake: Default::default() } + Self { + stakers: Default::default(), + max_candidate_stake: Default::default(), + max_commission_change: Permill::from_percent(100), + } } } @@ -739,6 +747,8 @@ pub mod pallet { let round: RoundInfo> = RoundInfo::new(0u32, 0u32.into(), T::DefaultBlocksPerRound::get()); >::put(round); + + MaxCommissionChange::::put(self.max_commission_change); } } @@ -2000,7 +2010,7 @@ pub mod pallet { ); // Check the maximum change commission - let max_change = T::MaxCommissionChange::get(); + let max_change = MaxCommissionChange::::get(); let current_commission = candidate.commission; let change = if commission > current_commission { commission - current_commission @@ -2018,6 +2028,22 @@ pub mod pallet { Self::deposit_event(Event::CollatorCommissionChanged(collator, commission)); Ok(()) } + + #[pallet::call_index(20)] + #[pallet::weight(::WeightInfo::set_max_commission_change( + Permill::from_percent(100).deconstruct() + ))] + pub fn set_max_commission_change( + origin: OriginFor, + new_max_commission_change: Permill, + ) -> DispatchResult { + ensure_root(origin)?; + + MaxCommissionChange::::put(new_max_commission_change); + + Self::deposit_event(Event::MaxCommissionChangeUpdated(new_max_commission_change)); + Ok(()) + } } impl Pallet { diff --git a/pallets/parachain-staking/src/mock.rs b/pallets/parachain-staking/src/mock.rs index 108280641..f14370f00 100644 --- a/pallets/parachain-staking/src/mock.rs +++ b/pallets/parachain-staking/src/mock.rs @@ -154,7 +154,6 @@ parameter_types! { pub const MinDelegatorStake: Balance = 5; pub const MinDelegation: Balance = 3; pub const MaxUnstakeRequests: u32 = 6; - pub const MaxCommissionChange: Permill = Permill::from_percent(10); pub const CommissionChangeInterval: BlockNumber = 1; } @@ -181,7 +180,6 @@ impl Config for Test { type PotId = PotId; type WeightInfo = crate::weights::WeightInfo; type CommissionChangeInterval = CommissionChangeInterval; - type MaxCommissionChange = MaxCommissionChange; } impl_opaque_keys! { @@ -283,9 +281,13 @@ impl ExtBuilder { for delegator in self.delegators.clone() { stakers.push((delegator.0, Some(delegator.1), delegator.2)); } - stake::GenesisConfig:: { stakers, max_candidate_stake: 160_000_000 * DECIMALS } - .assimilate_storage(&mut t) - .expect("Parachain Staking's storage can be assimilated"); + stake::GenesisConfig:: { + stakers, + max_candidate_stake: 160_000_000 * DECIMALS, + max_commission_change: Permill::from_percent(10), + } + .assimilate_storage(&mut t) + .expect("Parachain Staking's storage can be assimilated"); // stashes are the AccountId let session_keys: Vec<_> = self diff --git a/pallets/parachain-staking/src/weightinfo.rs b/pallets/parachain-staking/src/weightinfo.rs index 4c8420d15..2998a4b61 100644 --- a/pallets/parachain-staking/src/weightinfo.rs +++ b/pallets/parachain-staking/src/weightinfo.rs @@ -24,4 +24,5 @@ pub trait WeightInfo { fn unlock_unstaked(u: u32) -> Weight; fn set_max_candidate_stake() -> Weight; fn set_commission(n: u32, m: u32) -> Weight; + fn set_max_commission_change(n: u32) -> Weight; } diff --git a/pallets/parachain-staking/src/weights.rs b/pallets/parachain-staking/src/weights.rs index 89557e0b0..6dce0937f 100644 --- a/pallets/parachain-staking/src/weights.rs +++ b/pallets/parachain-staking/src/weights.rs @@ -537,4 +537,18 @@ impl crate::WeightInfo for WeightInfo { .saturating_add(T::DbWeight::get().reads(1)) .saturating_add(T::DbWeight::get().writes(1)) } + + /// Storage: `ParachainStaking::MaxCommissionChange` (r:1 w:1) + /// Proof: `ParachainStaking::MaxCommissionChange` (`max_values`: None, `max_size`: Some(1314), added: 3789, mode: `MaxEncodedLen`) + /// The range of component `m` is `[0, 1000000]`. + fn set_max_commission_change(_m: u32) -> Weight { + // Proof Size summary in bytes: + // Measured: `393` + // Estimated: `4779` + // Minimum execution time: 19_960_000 picoseconds. + Weight::from_parts(20_647_875, 0) + .saturating_add(Weight::from_parts(0, 4779)) + .saturating_add(T::DbWeight::get().reads(1)) + .saturating_add(T::DbWeight::get().writes(1)) + } } diff --git a/precompiles/parachain-staking/src/mock.rs b/precompiles/parachain-staking/src/mock.rs index 333a346d9..7f385f63f 100644 --- a/precompiles/parachain-staking/src/mock.rs +++ b/precompiles/parachain-staking/src/mock.rs @@ -232,7 +232,6 @@ impl parachain_staking::Config for Test { type PotId = PotId; type WeightInfo = parachain_staking::weights::WeightInfo; type CommissionChangeInterval = CommissionChangeInterval; - type MaxCommissionChange = MaxCommissionChange; } impl_opaque_keys! { @@ -337,6 +336,7 @@ impl ExtBuilder { parachain_staking::GenesisConfig:: { stakers, max_candidate_stake: 160_000_000 * DECIMALS, + max_commission_change: Permill::from_percent(100), } .assimilate_storage(&mut t) .expect("Parachain Staking's storage can be assimilated"); diff --git a/runtime/krest/src/lib.rs b/runtime/krest/src/lib.rs index f5dc3d02a..e3f5df91f 100644 --- a/runtime/krest/src/lib.rs +++ b/runtime/krest/src/lib.rs @@ -825,7 +825,7 @@ pub mod staking { pub const MaxCollatorCandidates: u32 = 128; /// Maximum number of concurrent requests to unlock unstaked balance pub const MaxUnstakeRequests: u32 = 10; - pub const MaxCommissionChange: Permill = Permill::from_percent(10); // Maximum 10% change + /// Minimum time between commission changes pub const CommissionChangeInterval: BlockNumber = DAYS; // 24 hours } } @@ -854,7 +854,6 @@ impl parachain_staking::Config for Runtime { type WeightInfo = parachain_staking::weights::WeightInfo; type CommissionChangeInterval = staking::CommissionChangeInterval; - type MaxCommissionChange = staking::MaxCommissionChange; } /// Implements the adapters for depositing unbalanced tokens on pots diff --git a/runtime/peaq-dev/src/lib.rs b/runtime/peaq-dev/src/lib.rs index debd048d6..33cd4869c 100644 --- a/runtime/peaq-dev/src/lib.rs +++ b/runtime/peaq-dev/src/lib.rs @@ -831,8 +831,7 @@ pub mod staking { pub const MaxCollatorCandidates: u32 = 64; /// Maximum number of concurrent requests to unlock unstaked balance pub const MaxUnstakeRequests: u32 = 10; - - pub const MaxCommissionChange: Permill = Permill::from_percent(10); // Maximum 10% change + /// Minimum time between commission changes pub const CommissionChangeInterval: BlockNumber = DAYS; // 24 hours } } @@ -861,7 +860,6 @@ impl parachain_staking::Config for Runtime { type WeightInfo = parachain_staking::weights::WeightInfo; type CommissionChangeInterval = staking::CommissionChangeInterval; - type MaxCommissionChange = staking::MaxCommissionChange; } /// Implements the adapters for depositing unbalanced tokens on pots diff --git a/runtime/peaq/src/lib.rs b/runtime/peaq/src/lib.rs index 1bc46f713..0dd00b941 100644 --- a/runtime/peaq/src/lib.rs +++ b/runtime/peaq/src/lib.rs @@ -848,7 +848,7 @@ pub mod staking { pub const MaxCollatorCandidates: u32 = 64; /// Maximum number of concurrent requests to unlock unstaked balance pub const MaxUnstakeRequests: u32 = 10; - pub const MaxCommissionChange: Permill = Permill::from_percent(10); // Maximum 10% change + /// Minimum time between commission changes pub const CommissionChangeInterval: BlockNumber = DAYS; // 24 hours } } @@ -877,7 +877,6 @@ impl parachain_staking::Config for Runtime { type WeightInfo = parachain_staking::weights::WeightInfo; type CommissionChangeInterval = staking::CommissionChangeInterval; - type MaxCommissionChange = staking::MaxCommissionChange; } /// Implements the adapters for depositing unbalanced tokens on pots From 211314786a78fb7dea938902dbadf03aadeabb93 Mon Sep 17 00:00:00 2001 From: DrPing Date: Fri, 17 Jan 2025 11:52:42 +0700 Subject: [PATCH 5/9] feat(commission): add migration code for next runtime upgrade --- pallets/parachain-staking/src/migrations.rs | 12 ++++++++++-- 1 file changed, 10 insertions(+), 2 deletions(-) diff --git a/pallets/parachain-staking/src/migrations.rs b/pallets/parachain-staking/src/migrations.rs index 6c7a9b55b..2972f3608 100644 --- a/pallets/parachain-staking/src/migrations.rs +++ b/pallets/parachain-staking/src/migrations.rs @@ -20,8 +20,9 @@ pub enum Versions { _V8 = 8, V9 = 9, V10 = 10, - #[default] V11 = 11, + #[default] + V12 = 12, } pub(crate) fn on_runtime_upgrade() -> Weight { @@ -30,7 +31,9 @@ pub(crate) fn on_runtime_upgrade() -> Weight { mod upgrade { - use super::*; + use crate::MaxCommissionChange; + +use super::*; /// Migration implementation that deletes the old reward rate config and changes the staking ID. pub struct Migrate(sp_std::marker::PhantomData); @@ -102,6 +105,11 @@ mod upgrade { log::info!("V11 Migrating Done."); } + + if onchain_storage_version < StorageVersion::new(Versions::V12 as u16) { + // Set the value of MaxCommissionChange to 10% + MaxCommissionChange::::put(Permill::from_percent(10)); + } // update onchain storage version StorageVersion::new(Versions::default() as u16).put::>(); weight_writes += 1; From 9f4069646b7fb53abc97bce567501c26795d8acf Mon Sep 17 00:00:00 2001 From: DrPing Date: Fri, 17 Jan 2025 11:53:37 +0700 Subject: [PATCH 6/9] fix(commission): fix cargo fmt --- pallets/parachain-staking/src/migrations.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pallets/parachain-staking/src/migrations.rs b/pallets/parachain-staking/src/migrations.rs index 2972f3608..afb125bd6 100644 --- a/pallets/parachain-staking/src/migrations.rs +++ b/pallets/parachain-staking/src/migrations.rs @@ -33,7 +33,7 @@ mod upgrade { use crate::MaxCommissionChange; -use super::*; + use super::*; /// Migration implementation that deletes the old reward rate config and changes the staking ID. pub struct Migrate(sp_std::marker::PhantomData); From cfd9fe58eb5c3fb12a2640ccb0cfbb712aae4a7a Mon Sep 17 00:00:00 2001 From: DrPing Date: Tue, 21 Jan 2025 22:42:36 +0900 Subject: [PATCH 7/9] feat(commission): reduce commission change interval for agung network --- runtime/peaq-dev/src/lib.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/runtime/peaq-dev/src/lib.rs b/runtime/peaq-dev/src/lib.rs index 33cd4869c..33744d151 100644 --- a/runtime/peaq-dev/src/lib.rs +++ b/runtime/peaq-dev/src/lib.rs @@ -832,7 +832,7 @@ pub mod staking { /// Maximum number of concurrent requests to unlock unstaked balance pub const MaxUnstakeRequests: u32 = 10; /// Minimum time between commission changes - pub const CommissionChangeInterval: BlockNumber = DAYS; // 24 hours + pub const CommissionChangeInterval: BlockNumber = 10; } } From 8afb1b31b576eff5ae6c05ed16d2eb9442a062fb Mon Sep 17 00:00:00 2001 From: DrPing Date: Tue, 3 Jun 2025 13:39:09 +0900 Subject: [PATCH 8/9] feat(commission): add the possibility to change minimum interval commission change from a sudo account --- node/src/parachain/dev_chain_spec.rs | 1 + node/src/parachain/krest_chain_spec.rs | 1 + node/src/parachain/peaq_chain_spec.rs | 1 + pallets/parachain-staking/src/lib.rs | 33 +++++++++++++++++---- pallets/parachain-staking/src/migrations.rs | 6 ++-- pallets/parachain-staking/src/mock.rs | 3 +- pallets/parachain-staking/src/tests.rs | 21 +++++++++++++ pallets/parachain-staking/src/weightinfo.rs | 1 + pallets/parachain-staking/src/weights.rs | 13 ++++++++ precompiles/parachain-staking/src/mock.rs | 2 +- runtime/krest/src/lib.rs | 1 - runtime/peaq-dev/src/lib.rs | 1 - runtime/peaq/src/lib.rs | 1 - 13 files changed, 71 insertions(+), 14 deletions(-) diff --git a/node/src/parachain/dev_chain_spec.rs b/node/src/parachain/dev_chain_spec.rs index 56a2877af..c9b077401 100644 --- a/node/src/parachain/dev_chain_spec.rs +++ b/node/src/parachain/dev_chain_spec.rs @@ -134,6 +134,7 @@ fn configure_genesis( stakers, max_candidate_stake: staking::MAX_COLLATOR_STAKE, max_commission_change: Permill::from_percent(100), + min_commission_change_interval: 0, }, inflation_manager: Default::default(), block_reward: BlockRewardConfig { diff --git a/node/src/parachain/krest_chain_spec.rs b/node/src/parachain/krest_chain_spec.rs index 389bbfbe7..a361a7d4d 100644 --- a/node/src/parachain/krest_chain_spec.rs +++ b/node/src/parachain/krest_chain_spec.rs @@ -119,6 +119,7 @@ fn configure_genesis( stakers, max_candidate_stake: staking::MAX_COLLATOR_STAKE, max_commission_change: Permill::from_percent(100), + min_commission_change_interval: 0, }, inflation_manager: Default::default(), block_reward: BlockRewardConfig { diff --git a/node/src/parachain/peaq_chain_spec.rs b/node/src/parachain/peaq_chain_spec.rs index 34fe6744e..728043ec1 100644 --- a/node/src/parachain/peaq_chain_spec.rs +++ b/node/src/parachain/peaq_chain_spec.rs @@ -123,6 +123,7 @@ fn configure_genesis( stakers, max_candidate_stake: staking::MAX_COLLATOR_STAKE, max_commission_change: Permill::from_percent(100), + min_commission_change_interval: 0, }, inflation_manager: Default::default(), block_reward: BlockRewardConfig { diff --git a/pallets/parachain-staking/src/lib.rs b/pallets/parachain-staking/src/lib.rs index 79e6eb7a7..0dc780fa7 100644 --- a/pallets/parachain-staking/src/lib.rs +++ b/pallets/parachain-staking/src/lib.rs @@ -337,10 +337,6 @@ pub mod pallet { /// Weight information for extrinsics in this pallet. type WeightInfo: WeightInfo; - - /// The minimum interval between two commission changes. - #[pallet::constant] - type CommissionChangeInterval: Get>; } #[pallet::error] @@ -530,6 +526,9 @@ pub mod pallet { /// The commission maximum change has been changed. /// \[new value\] MaxCommissionChangeUpdated(Permill), + /// The delay between two commission changes has been changed. + /// \[new value\] + CommissionChangeIntervalUpdated(BlockNumberFor), } #[pallet::hooks] @@ -697,11 +696,17 @@ pub mod pallet { #[pallet::getter(fn max_commission_change)] pub type MaxCommissionChange = StorageValue<_, Permill, ValueQuery>; + #[pallet::storage] + #[pallet::getter(fn min_commission_change_interval)] + pub type MinCommissionChangeInterval = + StorageValue<_, BlockNumberFor, ValueQuery>; + #[pallet::genesis_config] pub struct GenesisConfig { pub stakers: GenesisStaker, pub max_candidate_stake: BalanceOf, pub max_commission_change: Permill, + pub min_commission_change_interval: BlockNumberFor, } impl Default for GenesisConfig { @@ -710,6 +715,7 @@ pub mod pallet { stakers: Default::default(), max_candidate_stake: Default::default(), max_commission_change: Permill::from_percent(100), + min_commission_change_interval: 0u32.into(), } } } @@ -749,6 +755,7 @@ pub mod pallet { >::put(round); MaxCommissionChange::::put(self.max_commission_change); + MinCommissionChangeInterval::::put(self.min_commission_change_interval); } } @@ -2005,7 +2012,7 @@ pub mod pallet { // Check the time since the last commission change let last_change = LastCommissionChange::::get(&collator); ensure!( - current_block >= last_change + T::CommissionChangeInterval::get(), + current_block >= last_change + MinCommissionChangeInterval::::get(), Error::::CommissionChangeTooEarly ); @@ -2044,6 +2051,22 @@ pub mod pallet { Self::deposit_event(Event::MaxCommissionChangeUpdated(new_max_commission_change)); Ok(()) } + + #[pallet::call_index(21)] + #[pallet::weight(::WeightInfo::set_min_commission_change_interval())] + pub fn set_min_commission_change_interval( + origin: OriginFor, + new_min_commission_change_interval: BlockNumberFor, + ) -> DispatchResult { + ensure_root(origin)?; + + MinCommissionChangeInterval::::put(new_min_commission_change_interval); + + Self::deposit_event(Event::CommissionChangeIntervalUpdated( + new_min_commission_change_interval, + )); + Ok(()) + } } impl Pallet { diff --git a/pallets/parachain-staking/src/migrations.rs b/pallets/parachain-staking/src/migrations.rs index afb125bd6..d4896dd13 100644 --- a/pallets/parachain-staking/src/migrations.rs +++ b/pallets/parachain-staking/src/migrations.rs @@ -3,13 +3,14 @@ use crate::{ pallet::{Config, Pallet, OLD_STAKING_ID, STAKING_ID}, types::{Candidate, OldCandidate}, - CandidatePool, ForceNewRound, Round, + CandidatePool, ForceNewRound, MaxCommissionChange, MinCommissionChangeInterval, Round, }; use frame_support::{ pallet_prelude::{GetStorageVersion, StorageVersion}, traits::{Get, LockableCurrency, WithdrawReasons}, weights::Weight, }; +use frame_system::pallet_prelude::BlockNumberFor; use pallet_balances::Locks; use sp_runtime::Permill; @@ -31,8 +32,6 @@ pub(crate) fn on_runtime_upgrade() -> Weight { mod upgrade { - use crate::MaxCommissionChange; - use super::*; /// Migration implementation that deletes the old reward rate config and changes the staking ID. @@ -109,6 +108,7 @@ mod upgrade { if onchain_storage_version < StorageVersion::new(Versions::V12 as u16) { // Set the value of MaxCommissionChange to 10% MaxCommissionChange::::put(Permill::from_percent(10)); + MinCommissionChangeInterval::::put(BlockNumberFor::::from(0u32)); } // update onchain storage version StorageVersion::new(Versions::default() as u16).put::>(); diff --git a/pallets/parachain-staking/src/mock.rs b/pallets/parachain-staking/src/mock.rs index f14370f00..a87f3a499 100644 --- a/pallets/parachain-staking/src/mock.rs +++ b/pallets/parachain-staking/src/mock.rs @@ -154,7 +154,6 @@ parameter_types! { pub const MinDelegatorStake: Balance = 5; pub const MinDelegation: Balance = 3; pub const MaxUnstakeRequests: u32 = 6; - pub const CommissionChangeInterval: BlockNumber = 1; } @@ -179,7 +178,6 @@ impl Config for Test { type MaxUnstakeRequests = MaxUnstakeRequests; type PotId = PotId; type WeightInfo = crate::weights::WeightInfo; - type CommissionChangeInterval = CommissionChangeInterval; } impl_opaque_keys! { @@ -285,6 +283,7 @@ impl ExtBuilder { stakers, max_candidate_stake: 160_000_000 * DECIMALS, max_commission_change: Permill::from_percent(10), + min_commission_change_interval: 1, } .assimilate_storage(&mut t) .expect("Parachain Staking's storage can be assimilated"); diff --git a/pallets/parachain-staking/src/tests.rs b/pallets/parachain-staking/src/tests.rs index 81b9cf181..9857b3a24 100644 --- a/pallets/parachain-staking/src/tests.rs +++ b/pallets/parachain-staking/src/tests.rs @@ -3991,6 +3991,27 @@ fn check_snapshot_is_cleared() { }); } +#[test] +fn change_commission_change_interval() { + ExtBuilder::default() + .with_balances(vec![(1, 1000), (2, 1000), (3, 1000)]) + .with_collators(vec![(1, 500)]) + .with_delegators(vec![(2, 1, 600), (3, 1, 400)]) + .build() + .execute_with(|| { + assert!(System::events().is_empty()); + + assert_ok!(Balances::force_set_balance( + RawOrigin::Root.into(), + StakePallet::account_id(), + 1000, + )); + + assert_ok!(StakePallet::set_min_commission_change_interval(RuntimeOrigin::root(), 10)); + assert_eq!(StakePallet::min_commission_change_interval(), 10); + }); +} + #[test] fn change_commission_too_frequently() { ExtBuilder::default() diff --git a/pallets/parachain-staking/src/weightinfo.rs b/pallets/parachain-staking/src/weightinfo.rs index 2998a4b61..afc215326 100644 --- a/pallets/parachain-staking/src/weightinfo.rs +++ b/pallets/parachain-staking/src/weightinfo.rs @@ -25,4 +25,5 @@ pub trait WeightInfo { fn set_max_candidate_stake() -> Weight; fn set_commission(n: u32, m: u32) -> Weight; fn set_max_commission_change(n: u32) -> Weight; + fn set_min_commission_change_interval() -> Weight; } diff --git a/pallets/parachain-staking/src/weights.rs b/pallets/parachain-staking/src/weights.rs index 6dce0937f..5e6113b98 100644 --- a/pallets/parachain-staking/src/weights.rs +++ b/pallets/parachain-staking/src/weights.rs @@ -551,4 +551,17 @@ impl crate::WeightInfo for WeightInfo { .saturating_add(T::DbWeight::get().reads(1)) .saturating_add(T::DbWeight::get().writes(1)) } + /// Storage: `ParachainStaking::MaxCommissionChangeInterval` (r:1 w:1) + /// Proof: `ParachainStaking::MaxCommissionChangeInterval` (`max_values`: None, `max_size`: Some(1314), added: 3789, mode: `MaxEncodedLen`) + /// The range of component `n` is `[0, 1000000]`. + fn set_min_commission_change_interval() -> Weight { + // Proof Size summary in bytes: + // Measured: `393` + // Estimated: `4779` + // Minimum execution time: 19_960_000 picoseconds. + Weight::from_parts(20_647_875, 0) + .saturating_add(Weight::from_parts(0, 4779)) + .saturating_add(T::DbWeight::get().reads(1)) + .saturating_add(T::DbWeight::get().writes(1)) + } } diff --git a/precompiles/parachain-staking/src/mock.rs b/precompiles/parachain-staking/src/mock.rs index 7f385f63f..a03786908 100644 --- a/precompiles/parachain-staking/src/mock.rs +++ b/precompiles/parachain-staking/src/mock.rs @@ -231,7 +231,6 @@ impl parachain_staking::Config for Test { type MaxUnstakeRequests = MaxUnstakeRequests; type PotId = PotId; type WeightInfo = parachain_staking::weights::WeightInfo; - type CommissionChangeInterval = CommissionChangeInterval; } impl_opaque_keys! { @@ -337,6 +336,7 @@ impl ExtBuilder { stakers, max_candidate_stake: 160_000_000 * DECIMALS, max_commission_change: Permill::from_percent(100), + min_commission_change_interval: 1, } .assimilate_storage(&mut t) .expect("Parachain Staking's storage can be assimilated"); diff --git a/runtime/krest/src/lib.rs b/runtime/krest/src/lib.rs index 3968d1329..4b0f19fa2 100644 --- a/runtime/krest/src/lib.rs +++ b/runtime/krest/src/lib.rs @@ -869,7 +869,6 @@ impl parachain_staking::Config for Runtime { type MaxUnstakeRequests = staking::MaxUnstakeRequests; type WeightInfo = parachain_staking::weights::WeightInfo; - type CommissionChangeInterval = staking::CommissionChangeInterval; } /// Implements the adapters for depositing unbalanced tokens on pots diff --git a/runtime/peaq-dev/src/lib.rs b/runtime/peaq-dev/src/lib.rs index d5c89475e..e22a912bc 100644 --- a/runtime/peaq-dev/src/lib.rs +++ b/runtime/peaq-dev/src/lib.rs @@ -874,7 +874,6 @@ impl parachain_staking::Config for Runtime { type MaxUnstakeRequests = staking::MaxUnstakeRequests; type WeightInfo = parachain_staking::weights::WeightInfo; - type CommissionChangeInterval = staking::CommissionChangeInterval; } /// Implements the adapters for depositing unbalanced tokens on pots diff --git a/runtime/peaq/src/lib.rs b/runtime/peaq/src/lib.rs index 3760f931a..2f4c510b3 100644 --- a/runtime/peaq/src/lib.rs +++ b/runtime/peaq/src/lib.rs @@ -893,7 +893,6 @@ impl parachain_staking::Config for Runtime { type MaxUnstakeRequests = staking::MaxUnstakeRequests; type WeightInfo = parachain_staking::weights::WeightInfo; - type CommissionChangeInterval = staking::CommissionChangeInterval; } /// Implements the adapters for depositing unbalanced tokens on pots From d4d8705e8fbba432edecfcea8f65b2e7422eb004 Mon Sep 17 00:00:00 2001 From: DrPing Date: Tue, 3 Jun 2025 14:15:12 +0900 Subject: [PATCH 9/9] fix(commission): remove unused parameter types --- precompiles/parachain-staking/src/mock.rs | 2 -- runtime/krest/src/lib.rs | 2 -- runtime/peaq-dev/src/lib.rs | 2 -- runtime/peaq/src/lib.rs | 2 -- 4 files changed, 8 deletions(-) diff --git a/precompiles/parachain-staking/src/mock.rs b/precompiles/parachain-staking/src/mock.rs index a03786908..f63f267b0 100644 --- a/precompiles/parachain-staking/src/mock.rs +++ b/precompiles/parachain-staking/src/mock.rs @@ -206,8 +206,6 @@ parameter_types! { pub const MinDelegatorStake: Balance = 5; pub const MinDelegation: Balance = 3; pub const MaxUnstakeRequests: u32 = 6; - pub const MaxCommissionChange: Permill = Permill::from_percent(10); - pub const CommissionChangeInterval: BlockNumber = 1; } impl parachain_staking::Config for Test { diff --git a/runtime/krest/src/lib.rs b/runtime/krest/src/lib.rs index 4b0f19fa2..cf2dbc738 100644 --- a/runtime/krest/src/lib.rs +++ b/runtime/krest/src/lib.rs @@ -841,8 +841,6 @@ pub mod staking { pub const MaxCollatorCandidates: u32 = 128; /// Maximum number of concurrent requests to unlock unstaked balance pub const MaxUnstakeRequests: u32 = 10; - /// Minimum time between commission changes - pub const CommissionChangeInterval: BlockNumber = DAYS; // 24 hours } } diff --git a/runtime/peaq-dev/src/lib.rs b/runtime/peaq-dev/src/lib.rs index e22a912bc..15d5bb926 100644 --- a/runtime/peaq-dev/src/lib.rs +++ b/runtime/peaq-dev/src/lib.rs @@ -846,8 +846,6 @@ pub mod staking { pub const MaxCollatorCandidates: u32 = 64; /// Maximum number of concurrent requests to unlock unstaked balance pub const MaxUnstakeRequests: u32 = 10; - /// Minimum time between commission changes - pub const CommissionChangeInterval: BlockNumber = 10; } } diff --git a/runtime/peaq/src/lib.rs b/runtime/peaq/src/lib.rs index 2f4c510b3..2781709e9 100644 --- a/runtime/peaq/src/lib.rs +++ b/runtime/peaq/src/lib.rs @@ -865,8 +865,6 @@ pub mod staking { pub const MaxCollatorCandidates: u32 = 64; /// Maximum number of concurrent requests to unlock unstaked balance pub const MaxUnstakeRequests: u32 = 10; - /// Minimum time between commission changes - pub const CommissionChangeInterval: BlockNumber = DAYS; // 24 hours } }