From 20d5e3584d4f77abb7b52da93d74ea85af472ca1 Mon Sep 17 00:00:00 2001
From: Ross Bulat <ross@parity.io>
Date: Wed, 15 Mar 2023 12:07:55 +0800
Subject: [PATCH] Nomination Pool Commission (#13128)
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

* + nomination pool commission

* fmt

* use register_update()

* Update frame/nomination-pools/src/lib.rs

Co-authored-by: Gonçalo Pestana <g6pestana@gmail.com>

* Update frame/nomination-pools/src/lib.rs

Co-authored-by: Gonçalo Pestana <g6pestana@gmail.com>

* fmt

* amend comments

* + test for set_commission

* fix

* Update frame/nomination-pools/fuzzer/src/call.rs

Co-authored-by: Kian Paimani <5588131+kianenigma@users.noreply.github.com>

* rm comment

* use PalletError

* some feedback item amendments

* update weights

* revert PalletError stuff

* ".git/.scripts/commands/fmt/fmt.sh"

* make pool_events_since_last_call more modular

* fmt

* fix call indexes + test

* add payout teste

* add event to max_commisson updating current

* begin refactor

* some debugging

* update

* more tests

* rewardpol not working

* commission refactor

* pending rewards returns commission

* fmt

* add claim_commission call

* + claim_commission

* fix benchmarks

* weight 0 for now

* + claim_commission benchmark

* fmt

* apply commission to benchmarks

* ".git/.scripts/commands/bench/bench.sh" pallet dev pallet_nomination_pools

* ".git/.scripts/commands/fmt/fmt.sh"

* clippy

* + pending

* add RewardPool.total_rewards_acounted

* fixes

* println

* more logs

* Fix plus cleanups

* fix assert

* tidy up

* tests work + tidy up

* rm unused

* clippy fix

* persist reward_pool update

* claim_commission_works tests

* .

* some test formatting

* add high level docs

* add calls

* docs

* rename

* rename

* docs

* rename

* fmt

* use matches!

* Update frame/nomination-pools/src/lib.rs

Co-authored-by: Gonçalo Pestana <g6pestana@gmail.com>

* Update frame/nomination-pools/src/lib.rs

Co-authored-by: Gonçalo Pestana <g6pestana@gmail.com>

* Update frame/nomination-pools/src/tests.rs

Co-authored-by: Gonçalo Pestana <g6pestana@gmail.com>

* comment

* Update frame/nomination-pools/src/lib.rs

Co-authored-by: Gonçalo Pestana <g6pestana@gmail.com>

* .

* weights order

* ".git/.scripts/commands/bench/bench.sh" pallet dev pallet_nomination_pools

* use from_parts

* comment

* ".git/.scripts/commands/fmt/fmt.sh"

* revert clippy suggestions on old migrations

* ".git/.scripts/commands/bench/bench.sh" pallet dev pallet_nomination_pools

* add InitialGlobalMaxCommission

* fix migration

* reward counter comments & explanations

* format

* add commission implementation note

* fmt

* revert InitialGlobalMaxCommission

* global max commission migration generic

* text

* 100% commission no payout test

* add commission_accumulates_on_multiple_rewards

* non-zero fuzzer GlobalMaxCommission

* add last_recorded_total_payouts_needs_commission

* commission event fix + claim commission test

---------

Co-authored-by: Gonçalo Pestana <g6pestana@gmail.com>
Co-authored-by: Kian Paimani <5588131+kianenigma@users.noreply.github.com>
Co-authored-by: command-bot <>
Co-authored-by: Bastian Köcher <info@kchr.de>
---
 .../nomination-pools/benchmarking/src/lib.rs  |  161 +-
 .../nomination-pools/benchmarking/src/mock.rs |    3 +-
 .../frame/nomination-pools/fuzzer/src/call.rs |    7 +-
 substrate/frame/nomination-pools/src/lib.rs   |  686 ++++++-
 .../frame/nomination-pools/src/migration.rs   |  101 +-
 substrate/frame/nomination-pools/src/mock.rs  |   39 +-
 substrate/frame/nomination-pools/src/tests.rs | 1602 ++++++++++++++++-
 .../frame/nomination-pools/src/weights.rs     |  590 +++---
 .../nomination-pools/test-staking/src/mock.rs |    3 +-
 9 files changed, 2730 insertions(+), 462 deletions(-)

diff --git a/substrate/frame/nomination-pools/benchmarking/src/lib.rs b/substrate/frame/nomination-pools/benchmarking/src/lib.rs
index 094289ee032..d58bbaf3d11 100644
--- a/substrate/frame/nomination-pools/benchmarking/src/lib.rs
+++ b/substrate/frame/nomination-pools/benchmarking/src/lib.rs
@@ -31,10 +31,14 @@ use frame_support::{assert_ok, ensure, traits::Get};
 use frame_system::RawOrigin as RuntimeOrigin;
 use pallet_nomination_pools::{
 	BalanceOf, BondExtra, BondedPoolInner, BondedPools, ClaimPermission, ClaimPermissions,
-	ConfigOp, MaxPoolMembers, MaxPoolMembersPerPool, MaxPools, Metadata, MinCreateBond,
-	MinJoinBond, Pallet as Pools, PoolMembers, PoolRoles, PoolState, RewardPools, SubPoolsStorage,
+	Commission, CommissionChangeRate, ConfigOp, GlobalMaxCommission, MaxPoolMembers,
+	MaxPoolMembersPerPool, MaxPools, Metadata, MinCreateBond, MinJoinBond, Pallet as Pools,
+	PoolMembers, PoolRoles, PoolState, RewardPools, SubPoolsStorage,
+};
+use sp_runtime::{
+	traits::{Bounded, StaticLookup, Zero},
+	Perbill,
 };
-use sp_runtime::traits::{Bounded, StaticLookup, Zero};
 use sp_staking::{EraIndex, StakingInterface};
 // `frame_benchmarking::benchmarks!` macro needs this
 use pallet_nomination_pools::Call;
@@ -69,6 +73,7 @@ fn create_funded_user_with_balance<T: pallet_nomination_pools::Config>(
 fn create_pool_account<T: pallet_nomination_pools::Config>(
 	n: u32,
 	balance: BalanceOf<T>,
+	commission: Option<Perbill>,
 ) -> (T::AccountId, T::AccountId) {
 	let ed = CurrencyOf::<T>::minimum_balance();
 	let pool_creator: T::AccountId =
@@ -84,6 +89,16 @@ fn create_pool_account<T: pallet_nomination_pools::Config>(
 	)
 	.unwrap();
 
+	if let Some(c) = commission {
+		let pool_id = pallet_nomination_pools::LastPoolId::<T>::get();
+		Pools::<T>::set_commission(
+			RuntimeOrigin::Signed(pool_creator.clone()).into(),
+			pool_id,
+			Some((c, pool_creator.clone())),
+		)
+		.expect("pool just created, commission can be set by root; qed");
+	}
+
 	let pool_account = pallet_nomination_pools::BondedPools::<T>::iter()
 		.find(|(_, bonded_pool)| bonded_pool.roles.depositor == pool_creator)
 		.map(|(pool_id, _)| Pools::<T>::create_bonded_account(pool_id))
@@ -134,14 +149,18 @@ impl<T: Config> ListScenario<T> {
 		sp_std::mem::forget(i);
 
 		// Create accounts with the origin weight
-		let (pool_creator1, pool_origin1) = create_pool_account::<T>(USER_SEED + 1, origin_weight);
+		let (pool_creator1, pool_origin1) =
+			create_pool_account::<T>(USER_SEED + 1, origin_weight, Some(Perbill::from_percent(50)));
+
 		T::Staking::nominate(
 			&pool_origin1,
 			// NOTE: these don't really need to be validators.
 			vec![account("random_validator", 0, USER_SEED)],
 		)?;
 
-		let (_, pool_origin2) = create_pool_account::<T>(USER_SEED + 2, origin_weight);
+		let (_, pool_origin2) =
+			create_pool_account::<T>(USER_SEED + 2, origin_weight, Some(Perbill::from_percent(50)));
+
 		T::Staking::nominate(
 			&pool_origin2,
 			vec![account("random_validator", 0, USER_SEED)].clone(),
@@ -157,7 +176,9 @@ impl<T: Config> ListScenario<T> {
 			dest_weight_as_vote.try_into().map_err(|_| "could not convert u64 to Balance")?;
 
 		// Create an account with the worst case destination weight
-		let (_, pool_dest1) = create_pool_account::<T>(USER_SEED + 3, dest_weight);
+		let (_, pool_dest1) =
+			create_pool_account::<T>(USER_SEED + 3, dest_weight, Some(Perbill::from_percent(50)));
+
 		T::Staking::nominate(&pool_dest1, vec![account("random_validator", 0, USER_SEED)])?;
 
 		let weight_of = pallet_staking::Pallet::<T>::weight_of_fn();
@@ -269,18 +290,19 @@ frame_benchmarking::benchmarks! {
 
 	}: _(RuntimeOrigin::Signed(claimer), T::Lookup::unlookup(scenario.creator1.clone()), BondExtra::Rewards)
 	verify {
+		 // commission of 50% deducted here.
 		assert!(
 			T::Staking::active_stake(&scenario.origin1).unwrap() >=
-			scenario.dest_weight
+			scenario.dest_weight / 2u32.into()
 		);
 	}
 
 	claim_payout {
 		let claimer: T::AccountId = account("claimer", USER_SEED + 4, 0);
-
+		let commission = Perbill::from_percent(50);
 		let origin_weight = Pools::<T>::depositor_min_bond() * 2u32.into();
 		let ed = CurrencyOf::<T>::minimum_balance();
-		let (depositor, pool_account) = create_pool_account::<T>(0, origin_weight);
+		let (depositor, pool_account) = create_pool_account::<T>(0, origin_weight, Some(commission));
 		let reward_account = Pools::<T>::create_reward_account(1);
 
 		// Send funds to the reward account of the pool
@@ -301,11 +323,11 @@ frame_benchmarking::benchmarks! {
 	verify {
 		assert_eq!(
 			CurrencyOf::<T>::free_balance(&depositor),
-			origin_weight * 2u32.into()
+			origin_weight + commission * origin_weight
 		);
 		assert_eq!(
 			CurrencyOf::<T>::free_balance(&reward_account),
-			ed + Zero::zero()
+			ed + commission * origin_weight
 		);
 	}
 
@@ -345,7 +367,7 @@ frame_benchmarking::benchmarks! {
 		let s in 0 .. MAX_SPANS;
 
 		let min_create_bond = Pools::<T>::depositor_min_bond();
-		let (depositor, pool_account) = create_pool_account::<T>(0, min_create_bond);
+		let (depositor, pool_account) = create_pool_account::<T>(0, min_create_bond, None);
 
 		// Add a new member
 		let min_join_bond = MinJoinBond::<T>::get().max(CurrencyOf::<T>::minimum_balance());
@@ -387,7 +409,7 @@ frame_benchmarking::benchmarks! {
 		let s in 0 .. MAX_SPANS;
 
 		let min_create_bond = Pools::<T>::depositor_min_bond();
-		let (depositor, pool_account) = create_pool_account::<T>(0, min_create_bond);
+		let (depositor, pool_account) = create_pool_account::<T>(0, min_create_bond, None);
 
 		// Add a new member
 		let min_join_bond = MinJoinBond::<T>::get().max(CurrencyOf::<T>::minimum_balance());
@@ -433,7 +455,7 @@ frame_benchmarking::benchmarks! {
 		let s in 0 .. MAX_SPANS;
 
 		let min_create_bond = Pools::<T>::depositor_min_bond();
-		let (depositor, pool_account) = create_pool_account::<T>(0, min_create_bond);
+		let (depositor, pool_account) = create_pool_account::<T>(0, min_create_bond, None);
 		let depositor_lookup = T::Lookup::unlookup(depositor.clone());
 
 		// We set the pool to the destroying state so the depositor can leave
@@ -523,15 +545,16 @@ frame_benchmarking::benchmarks! {
 		assert_eq!(
 			new_pool,
 			BondedPoolInner {
-				points: min_create_bond,
-				state: PoolState::Open,
+				commission: Commission::default(),
 				member_counter: 1,
+				points: min_create_bond,
 				roles: PoolRoles {
 					depositor: depositor.clone(),
 					root: Some(depositor.clone()),
 					nominator: Some(depositor.clone()),
 					bouncer: Some(depositor.clone()),
 				},
+				state: PoolState::Open,
 			}
 		);
 		assert_eq!(
@@ -545,7 +568,7 @@ frame_benchmarking::benchmarks! {
 
 		// Create a pool
 		let min_create_bond = Pools::<T>::depositor_min_bond() * 2u32.into();
-		let (depositor, pool_account) = create_pool_account::<T>(0, min_create_bond);
+		let (depositor, pool_account) = create_pool_account::<T>(0, min_create_bond, None);
 
 		// Create some accounts to nominate. For the sake of benchmarking they don't need to be
 		// actual validators
@@ -562,15 +585,16 @@ frame_benchmarking::benchmarks! {
 		assert_eq!(
 			new_pool,
 			BondedPoolInner {
-				points: min_create_bond,
-				state: PoolState::Open,
+				commission: Commission::default(),
 				member_counter: 1,
+				points: min_create_bond,
 				roles: PoolRoles {
 					depositor: depositor.clone(),
 					root: Some(depositor.clone()),
 					nominator: Some(depositor.clone()),
 					bouncer: Some(depositor.clone()),
-				}
+				},
+				state: PoolState::Open,
 			}
 		);
 		assert_eq!(
@@ -582,7 +606,7 @@ frame_benchmarking::benchmarks! {
 	set_state {
 		// Create a pool
 		let min_create_bond = Pools::<T>::depositor_min_bond();
-		let (depositor, pool_account) = create_pool_account::<T>(0, min_create_bond);
+		let (depositor, pool_account) = create_pool_account::<T>(0, min_create_bond, None);
 		BondedPools::<T>::mutate(&1, |maybe_pool| {
 			// Force the pool into an invalid state
 			maybe_pool.as_mut().map(|mut pool| pool.points = min_create_bond * 10u32.into());
@@ -599,7 +623,7 @@ frame_benchmarking::benchmarks! {
 		let n in 1 .. <T as pallet_nomination_pools::Config>::MaxMetadataLen::get();
 
 		// Create a pool
-		let (depositor, pool_account) = create_pool_account::<T>(0, Pools::<T>::depositor_min_bond() * 2u32.into());
+		let (depositor, pool_account) = create_pool_account::<T>(0, Pools::<T>::depositor_min_bond() * 2u32.into(), None);
 
 		// Create metadata of the max possible size
 		let metadata: Vec<u8> = (0..n).map(|_| 42).collect();
@@ -617,18 +641,20 @@ frame_benchmarking::benchmarks! {
 		ConfigOp::Set(BalanceOf::<T>::max_value()),
 		ConfigOp::Set(u32::MAX),
 		ConfigOp::Set(u32::MAX),
-		ConfigOp::Set(u32::MAX)
+		ConfigOp::Set(u32::MAX),
+		ConfigOp::Set(Perbill::max_value())
 	) verify {
 		assert_eq!(MinJoinBond::<T>::get(), BalanceOf::<T>::max_value());
 		assert_eq!(MinCreateBond::<T>::get(), BalanceOf::<T>::max_value());
 		assert_eq!(MaxPools::<T>::get(), Some(u32::MAX));
 		assert_eq!(MaxPoolMembers::<T>::get(), Some(u32::MAX));
 		assert_eq!(MaxPoolMembersPerPool::<T>::get(), Some(u32::MAX));
+		assert_eq!(GlobalMaxCommission::<T>::get(), Some(Perbill::max_value()));
 	}
 
 	update_roles {
 		let first_id = pallet_nomination_pools::LastPoolId::<T>::get() + 1;
-		let (root, _) = create_pool_account::<T>(0, Pools::<T>::depositor_min_bond() * 2u32.into());
+		let (root, _) = create_pool_account::<T>(0, Pools::<T>::depositor_min_bond() * 2u32.into(), None);
 		let random: T::AccountId = account("but is anything really random in computers..?", 0, USER_SEED);
 	}:_(
 		RuntimeOrigin::Signed(root.clone()),
@@ -650,7 +676,7 @@ frame_benchmarking::benchmarks! {
 
 	chill {
 		// Create a pool
-		let (depositor, pool_account) = create_pool_account::<T>(0, Pools::<T>::depositor_min_bond() * 2u32.into());
+		let (depositor, pool_account) = create_pool_account::<T>(0, Pools::<T>::depositor_min_bond() * 2u32.into(), None);
 
 		// Nominate with the pool.
 		 let validators: Vec<_> = (0..T::MaxNominations::get())
@@ -666,10 +692,68 @@ frame_benchmarking::benchmarks! {
 		assert!(T::Staking::nominations(Pools::<T>::create_bonded_account(1)).is_none());
 	}
 
+	set_commission {
+		// Create a pool - do not set a commission yet.
+		let (depositor, pool_account) = create_pool_account::<T>(0, Pools::<T>::depositor_min_bond() * 2u32.into(), None);
+		// set a max commission
+		Pools::<T>::set_commission_max(RuntimeOrigin::Signed(depositor.clone()).into(), 1u32.into(), Perbill::from_percent(50)).unwrap();
+		// set a change rate
+		Pools::<T>::set_commission_change_rate(RuntimeOrigin::Signed(depositor.clone()).into(), 1u32.into(), CommissionChangeRate {
+			max_increase: Perbill::from_percent(20),
+			min_delay: 0u32.into(),
+		}).unwrap();
+
+	}:_(RuntimeOrigin::Signed(depositor.clone()), 1u32.into(), Some((Perbill::from_percent(20), depositor.clone())))
+	verify {
+		assert_eq!(BondedPools::<T>::get(1).unwrap().commission, Commission {
+			current: Some((Perbill::from_percent(20), depositor)),
+			max: Some(Perbill::from_percent(50)),
+			change_rate: Some(CommissionChangeRate {
+					max_increase: Perbill::from_percent(20),
+					min_delay: 0u32.into()
+			}),
+			throttle_from: Some(1u32.into()),
+		});
+	}
+
+	set_commission_max {
+		// Create a pool, setting a commission that will update when max commission is set.
+		let (depositor, pool_account) = create_pool_account::<T>(0, Pools::<T>::depositor_min_bond() * 2u32.into(), Some(Perbill::from_percent(50)));
+	}:_(RuntimeOrigin::Signed(depositor.clone()), 1u32.into(), Perbill::from_percent(50))
+	verify {
+		assert_eq!(
+			BondedPools::<T>::get(1).unwrap().commission, Commission {
+			current: Some((Perbill::from_percent(50), depositor)),
+			max: Some(Perbill::from_percent(50)),
+			change_rate: None,
+			throttle_from: Some(0u32.into()),
+		});
+	}
+
+	set_commission_change_rate {
+		// Create a pool
+		let (depositor, pool_account) = create_pool_account::<T>(0, Pools::<T>::depositor_min_bond() * 2u32.into(), None);
+	}:_(RuntimeOrigin::Signed(depositor.clone()), 1u32.into(), CommissionChangeRate {
+		max_increase: Perbill::from_percent(50),
+		min_delay: 1000u32.into(),
+	})
+	verify {
+		assert_eq!(
+			BondedPools::<T>::get(1).unwrap().commission, Commission {
+			current: None,
+			max: None,
+			change_rate: Some(CommissionChangeRate {
+				max_increase: Perbill::from_percent(50),
+				min_delay: 1000u32.into(),
+			}),
+			throttle_from: Some(1_u32.into()),
+		});
+  }
+
 	set_claim_permission {
 		// Create a pool
 		let min_create_bond = Pools::<T>::depositor_min_bond();
-		let (depositor, pool_account) = create_pool_account::<T>(0, min_create_bond);
+		let (depositor, pool_account) = create_pool_account::<T>(0, min_create_bond, None);
 
 		// Join pool
 		let min_join_bond = MinJoinBond::<T>::get().max(CurrencyOf::<T>::minimum_balance());
@@ -688,6 +772,31 @@ frame_benchmarking::benchmarks! {
 		assert_eq!(ClaimPermissions::<T>::get(joiner), ClaimPermission::PermissionlessAll);
 	}
 
+	claim_commission {
+		let claimer: T::AccountId = account("claimer_member", USER_SEED + 4, 0);
+		let commission = Perbill::from_percent(50);
+		let origin_weight = Pools::<T>::depositor_min_bond() * 2u32.into();
+		let ed = CurrencyOf::<T>::minimum_balance();
+		let (depositor, pool_account) = create_pool_account::<T>(0, origin_weight, Some(commission));
+		let reward_account = Pools::<T>::create_reward_account(1);
+		CurrencyOf::<T>::make_free_balance_be(&reward_account, ed + origin_weight);
+
+		// member claims a payout to make some commission available.
+		let _ = Pools::<T>::claim_payout(RuntimeOrigin::Signed(claimer).into());
+
+		whitelist_account!(depositor);
+	}:_(RuntimeOrigin::Signed(depositor.clone()), 1u32.into())
+	verify {
+		assert_eq!(
+			CurrencyOf::<T>::free_balance(&depositor),
+			origin_weight + commission * origin_weight
+		);
+		assert_eq!(
+			CurrencyOf::<T>::free_balance(&reward_account),
+			ed + commission * origin_weight
+		);
+	}
+
 	impl_benchmark_test_suite!(
 		Pallet,
 		crate::mock::new_test_ext(),
diff --git a/substrate/frame/nomination-pools/benchmarking/src/mock.rs b/substrate/frame/nomination-pools/benchmarking/src/mock.rs
index f6b2022bce5..4a1a52868e3 100644
--- a/substrate/frame/nomination-pools/benchmarking/src/mock.rs
+++ b/substrate/frame/nomination-pools/benchmarking/src/mock.rs
@@ -20,7 +20,7 @@ use frame_election_provider_support::VoteWeight;
 use frame_support::{pallet_prelude::*, parameter_types, traits::ConstU64, PalletId};
 use sp_runtime::{
 	traits::{Convert, IdentityLookup},
-	FixedU128,
+	FixedU128, Perbill,
 };
 
 type AccountId = u128;
@@ -195,6 +195,7 @@ pub fn new_test_ext() -> sp_io::TestExternalities {
 		max_pools: Some(3),
 		max_members_per_pool: Some(3),
 		max_members: Some(3 * 3),
+		global_max_commission: Some(Perbill::from_percent(50)),
 	}
 	.assimilate_storage(&mut storage);
 	sp_io::TestExternalities::from(storage)
diff --git a/substrate/frame/nomination-pools/fuzzer/src/call.rs b/substrate/frame/nomination-pools/fuzzer/src/call.rs
index 5e824712015..027fb2b6913 100644
--- a/substrate/frame/nomination-pools/fuzzer/src/call.rs
+++ b/substrate/frame/nomination-pools/fuzzer/src/call.rs
@@ -33,11 +33,11 @@ use pallet_nomination_pools::{
 	mock::*,
 	pallet as pools,
 	pallet::{BondedPools, Call as PoolsCall, Event as PoolsEvents, PoolMembers},
-	BondExtra, BondedPool, LastPoolId, MaxPoolMembers, MaxPoolMembersPerPool, MaxPools,
-	MinCreateBond, MinJoinBond, PoolId,
+	BondExtra, BondedPool, GlobalMaxCommission, LastPoolId, MaxPoolMembers, MaxPoolMembersPerPool,
+	MaxPools, MinCreateBond, MinJoinBond, PoolId,
 };
 use rand::{seq::SliceRandom, Rng};
-use sp_runtime::{assert_eq_error_rate, Perquintill};
+use sp_runtime::{assert_eq_error_rate, Perbill, Perquintill};
 
 const ERA: BlockNumber = 1000;
 const MAX_ED_MULTIPLE: Balance = 10_000;
@@ -224,6 +224,7 @@ fn main() {
 		MaxPoolMembers::<T>::set(Some(10_000));
 		MaxPoolMembersPerPool::<T>::set(Some(1000));
 		MaxPools::<T>::set(Some(1_000));
+		GlobalMaxCommission::<T>::set(Some(Perbill::from_percent(25)));
 
 		MinCreateBond::<T>::set(10 * ExistentialDeposit::get());
 		MinJoinBond::<T>::set(5 * ExistentialDeposit::get());
diff --git a/substrate/frame/nomination-pools/src/lib.rs b/substrate/frame/nomination-pools/src/lib.rs
index c1dda69ab5d..e73d4b4173e 100644
--- a/substrate/frame/nomination-pools/src/lib.rs
+++ b/substrate/frame/nomination-pools/src/lib.rs
@@ -50,6 +50,11 @@
 //!   not nominating proper validators.
 //! * reward account: A similar key-less account, that is set as the `Payee` account for the bonded
 //!   account for all staking rewards.
+//! * change rate: The rate at which pool commission can be changed. A change rate consists of a
+//!   `max_increase` and `min_delay`, dictating the maximum percentage increase that can be applied
+//!   to the commission per number of blocks.
+//! * throttle: An attempted commission increase is throttled if the attempted change falls outside
+//!   the change rate bounds.
 //!
 //! ## Usage
 //!
@@ -125,8 +130,33 @@
 //!   other members have left. Once they fully withdraw their funds, the pool is destroyed.
 //! * Nominator: can select which validators the pool nominates.
 //! * Bouncer: can change the pools state and kick members if the pool is blocked.
-//! * Root: can change the nominator, bouncer, or itself and can perform any of the actions the
-//!   nominator or bouncer can.
+//! * Root: can change the nominator, bouncer, or itself, manage and claim commission, and can
+//!   perform any of the actions the nominator or bouncer can.
+//!
+//! ## Commission
+//!
+//! A pool can optionally have a commission configuration, via the `root` role, set with
+//! [`Call::set_commission`] and claimed with [`Call::claim_commission`]. A payee account must be
+//! supplied with the desired commission percentage. Beyond the commission itself, a pool can have a
+//! maximum commission and a change rate.
+//!
+//! Importantly, both max commission  [`Call::set_commission_max`] and change rate
+//! [`Call::set_commission_change_rate`] can not be removed once set, and can only be set to more
+//! restrictive values (i.e. a lower max commission or a slower change rate) in subsequent updates.
+//!
+//! If set, a pool's commission is bound to [`GlobalMaxCommission`] at the time it is applied to
+//! pending rewards. [`GlobalMaxCommission`] is intended to be updated only via governance.
+//!
+//! When a pool is dissolved, any outstanding pending commission that has not been claimed will be
+//! transferred to the depositor.
+//!
+//! Implementation note: Commission is analogous to a separate member account of the pool, with its
+//! own reward counter in the form of `current_pending_commission`.
+//!
+//! Crucially, commission is applied to rewards based on the current commission in effect at the
+//! time rewards are transferred into the reward pool. This is to prevent the malicious behaviour of
+//! changing the commission rate to a very high value after rewards are accumulated, and thus claim
+//! an unexpectedly high chunk of the reward.
 //!
 //! ### Dismantling
 //!
@@ -232,11 +262,14 @@
 //!
 //! ### Reward pool
 //!
-//! When a pool is first bonded it sets up an deterministic, inaccessible account as its reward
-//! destination.
+//! When a pool is first bonded it sets up a deterministic, inaccessible account as its reward
+//! destination. This reward account combined with `RewardPool` compose a reward pool.
 //!
-//! The reward pool is not really a pool anymore, as it does not track points anymore. Instead, it
-//! tracks, a virtual value called `reward_counter`, among a few other values.
+//! Reward pools are completely separate entities to bonded pools. Along with its account, a reward
+//! pool also tracks its outstanding and claimed rewards as counters, in addition to pending and
+//! claimed commission. These counters are updated with `RewardPool::update_records`. The current
+//! reward counter of the pool (the total outstanding rewards, in points) is also callable with the
+//! `RewardPool::current_reward_counter` method.
 //!
 //! See [this link](https://hackmd.io/PFGn6wI5TbCmBYoEA_f2Uw) for an in-depth explanation of the
 //! reward pool mechanism.
@@ -247,13 +280,12 @@
 //!
 //! ### Unbonding sub pools
 //!
-//! When a member unbonds, it's balance is unbonded in the bonded pool's account and tracked in
-//! an unbonding pool associated with the active era. If no such pool exists, one is created. To
-//! track which unbonding sub pool a member belongs too, a member tracks it's
-//! `unbonding_era`.
+//! When a member unbonds, it's balance is unbonded in the bonded pool's account and tracked in an
+//! unbonding pool associated with the active era. If no such pool exists, one is created. To track
+//! which unbonding sub pool a member belongs too, a member tracks it's `unbonding_era`.
 //!
-//! When a member initiates unbonding it's claim on the bonded pool
-//! (`balance_to_unbond`) is computed as:
+//! When a member initiates unbonding it's claim on the bonded pool (`balance_to_unbond`) is
+//! computed as:
 //!
 //! ```text
 //! balance_to_unbond = (bonded_pool.balance / bonded_pool.points) * member.points;
@@ -262,8 +294,8 @@
 //! If this is the first transfer into an unbonding pool arbitrary amount of points can be issued
 //! per balance. In this implementation unbonding pools are initialized with a 1 point to 1 balance
 //! ratio (see [`POINTS_TO_BALANCE_INIT_RATIO`]). Otherwise, the unbonding pools hold the same
-//! points to balance ratio properties as the bonded pool, so member points in the
-//! unbonding pool are issued based on
+//! points to balance ratio properties as the bonded pool, so member points in the unbonding pool
+//! are issued based on
 //!
 //! ```text
 //! new_points_issued = (points_before_transfer / balance_before_transfer) * balance_to_unbond;
@@ -296,14 +328,14 @@
 //! `pallet_staking::StakingLedger::slash`, which passes the information to this pallet via
 //! [`sp_staking::OnStakerSlash::on_slash`].
 //!
-//! Unbonding pools need to be slashed to ensure all nominators whom where in the bonded pool
-//! while it was backing a validator that equivocated are punished. Without these measures a
-//! member could unbond right after a validator equivocated with no consequences.
+//! Unbonding pools need to be slashed to ensure all nominators whom where in the bonded pool while
+//! it was backing a validator that equivocated are punished. Without these measures a member could
+//! unbond right after a validator equivocated with no consequences.
 //!
-//! This strategy is unfair to members who joined after the slash, because they get slashed as
-//! well, but spares members who unbond. The latter is much more important for security: if a
-//! pool's validators are attacking the network, their members need to unbond fast! Avoiding
-//! slashes gives them an incentive to do that if validators get repeatedly slashed.
+//! This strategy is unfair to members who joined after the slash, because they get slashed as well,
+//! but spares members who unbond. The latter is much more important for security: if a pool's
+//! validators are attacking the network, their members need to unbond fast! Avoiding slashes gives
+//! them an incentive to do that if validators get repeatedly slashed.
 //!
 //! To be fair to joiners, this implementation also need joining pools, which are actively staking,
 //! in addition to the unbonding pools. For maintenance simplicity these are not implemented.
@@ -332,21 +364,22 @@ use frame_support::{
 		Currency, Defensive, DefensiveOption, DefensiveResult, DefensiveSaturating,
 		ExistenceRequirement, Get,
 	},
-	DefaultNoBound,
+	DefaultNoBound, PalletError,
 };
 use scale_info::TypeInfo;
 use sp_core::U256;
 use sp_runtime::{
 	traits::{
-		AccountIdConversion, CheckedAdd, CheckedSub, Convert, Saturating, StaticLookup, Zero,
+		AccountIdConversion, Bounded, CheckedAdd, CheckedSub, Convert, Saturating, StaticLookup,
+		Zero,
 	},
-	FixedPointNumber,
+	FixedPointNumber, Perbill,
 };
 use sp_staking::{EraIndex, OnStakerSlash, StakingInterface};
 use sp_std::{collections::btree_map::BTreeMap, fmt::Debug, ops::Div, vec::Vec};
 
 /// The log target of this pallet.
-pub const LOG_TARGET: &'static str = "runtime::nomination-pools";
+pub const LOG_TARGET: &str = "runtime::nomination-pools";
 
 // syntactic sugar for logging.
 #[macro_export]
@@ -430,19 +463,11 @@ pub enum ClaimPermission {
 
 impl ClaimPermission {
 	fn can_bond_extra(&self) -> bool {
-		match self {
-			ClaimPermission::PermissionlessAll => true,
-			ClaimPermission::PermissionlessCompound => true,
-			_ => false,
-		}
+		matches!(self, ClaimPermission::PermissionlessAll | ClaimPermission::PermissionlessCompound)
 	}
 
 	fn can_claim_payout(&self) -> bool {
-		match self {
-			ClaimPermission::PermissionlessAll => true,
-			ClaimPermission::PermissionlessWithdraw => true,
-			_ => false,
-		}
+		matches!(self, ClaimPermission::PermissionlessAll | ClaimPermission::PermissionlessWithdraw)
 	}
 }
 
@@ -623,25 +648,232 @@ pub struct PoolRoles<AccountId> {
 	pub bouncer: Option<AccountId>,
 }
 
+/// Pool commission.
+///
+/// The pool `root` can set commission configuration after pool creation. By default, all commission
+/// values are `None`. Pool `root` can also set `max` and `change_rate` configurations before
+/// setting an initial `current` commission.
+///
+/// `current` is a tuple of the commission percentage and payee of commission. `throttle_from`
+/// keeps track of which block `current` was last updated. A `max` commission value can only be
+/// decreased after the initial value is set, to prevent commission from repeatedly increasing.
+///
+/// An optional commission `change_rate` allows the pool to set strict limits to how much commission
+/// can change in each update, and how often updates can take place.
+#[derive(
+	Encode, Decode, DefaultNoBound, MaxEncodedLen, TypeInfo, DebugNoBound, PartialEq, Copy, Clone,
+)]
+#[codec(mel_bound(T: Config))]
+#[scale_info(skip_type_params(T))]
+pub struct Commission<T: Config> {
+	/// Optional commission rate of the pool along with the account commission is paid to.
+	pub current: Option<(Perbill, T::AccountId)>,
+	/// Optional maximum commission that can be set by the pool `root`. Once set, this value can
+	/// only be updated to a decreased value.
+	pub max: Option<Perbill>,
+	/// Optional configuration around how often commission can be updated, and when the last
+	/// commission update took place.
+	pub change_rate: Option<CommissionChangeRate<T::BlockNumber>>,
+	/// The block from where throttling should be checked from. This value will be updated on all
+	/// commission updates and when setting an initial `change_rate`.
+	pub throttle_from: Option<T::BlockNumber>,
+}
+
+impl<T: Config> Commission<T> {
+	/// Returns true if the current commission updating to `to` would exhaust the change rate
+	/// limits.
+	///
+	/// A commission update will be throttled (disallowed) if:
+	/// 1. not enough blocks have passed since the `throttle_from` block, if exists, or
+	/// 2. the new commission is greater than the maximum allowed increase.
+	fn throttling(&self, to: &Perbill) -> bool {
+		if let Some(t) = self.change_rate.as_ref() {
+			let commission_as_percent =
+				self.current.as_ref().map(|(x, _)| *x).unwrap_or(Perbill::zero());
+
+			// do not throttle if `to` is the same or a decrease in commission.
+			if *to <= commission_as_percent {
+				return false
+			}
+			// Test for `max_increase` throttling.
+			//
+			// Throttled if the attempted increase in commission is greater than `max_increase`.
+			if (*to).saturating_sub(commission_as_percent) > t.max_increase {
+				return true
+			}
+
+			// Test for `min_delay` throttling.
+			//
+			// Note: matching `None` is defensive only. `throttle_from` should always exist where
+			// `change_rate` has already been set, so this scenario should never happen.
+			return self.throttle_from.map_or_else(
+				|| {
+					defensive!("throttle_from should exist if change_rate is set");
+					true
+				},
+				|f| {
+					// if `min_delay` is zero (no delay), not throttling.
+					if t.min_delay == Zero::zero() {
+						false
+					} else {
+						// throttling if blocks passed is less than `min_delay`.
+						let blocks_surpassed =
+							<frame_system::Pallet<T>>::block_number().saturating_sub(f);
+						blocks_surpassed < t.min_delay
+					}
+				},
+			)
+		}
+		false
+	}
+
+	/// Gets the pool's current commission, or returns Perbill::zero if none is set.
+	/// Bounded to global max if current is greater than `GlobalMaxCommission`.
+	fn current(&self) -> Perbill {
+		self.current
+			.as_ref()
+			.map_or(Perbill::zero(), |(c, _)| *c)
+			.min(GlobalMaxCommission::<T>::get().unwrap_or(Bounded::max_value()))
+	}
+
+	/// Set the pool's commission.
+	///
+	/// Update commission based on `current`. If a `None` is supplied, allow the commission to be
+	/// removed without any change rate restrictions. Updates `throttle_from` to the current block.
+	/// If the supplied commission is zero, `None` will be inserted and `payee` will be ignored.
+	fn try_update_current(&mut self, current: &Option<(Perbill, T::AccountId)>) -> DispatchResult {
+		self.current = match current {
+			None => None,
+			Some((commission, payee)) => {
+				ensure!(!self.throttling(commission), Error::<T>::CommissionChangeThrottled);
+				ensure!(
+					self.max.map_or(true, |m| commission <= &m),
+					Error::<T>::CommissionExceedsMaximum
+				);
+				if commission.is_zero() {
+					None
+				} else {
+					Some((*commission, payee.clone()))
+				}
+			},
+		};
+		self.register_update();
+		Ok(())
+	}
+
+	/// Set the pool's maximum commission.
+	///
+	/// The pool's maximum commission can initially be set to any value, and only smaller values
+	/// thereafter. If larger values are attempted, this function will return a dispatch error.
+	///
+	/// If `current.0` is larger than the updated max commission value, `current.0` will also be
+	/// updated to the new maximum. This will also register a `throttle_from` update.
+	/// A `PoolCommissionUpdated` event is triggered if `current.0` is updated.
+	fn try_update_max(&mut self, pool_id: PoolId, new_max: Perbill) -> DispatchResult {
+		if let Some(old) = self.max.as_mut() {
+			if new_max > *old {
+				return Err(Error::<T>::MaxCommissionRestricted.into())
+			}
+			*old = new_max;
+		} else {
+			self.max = Some(new_max)
+		};
+		let updated_current = self
+			.current
+			.as_mut()
+			.map(|(c, _)| {
+				let u = *c > new_max;
+				*c = (*c).min(new_max);
+				u
+			})
+			.unwrap_or(false);
+
+		if updated_current {
+			if let Some((_, payee)) = self.current.as_ref() {
+				Pallet::<T>::deposit_event(Event::<T>::PoolCommissionUpdated {
+					pool_id,
+					current: Some((new_max, payee.clone())),
+				});
+			}
+			self.register_update();
+		}
+		Ok(())
+	}
+
+	/// Set the pool's commission `change_rate`.
+	///
+	/// Once a change rate configuration has been set, only more restrictive values can be set
+	/// thereafter. These restrictions translate to increased `min_delay` values and decreased
+	/// `max_increase` values.
+	///
+	/// Update `throttle_from` to the current block upon setting change rate for the first time, so
+	/// throttling can be checked from this block.
+	fn try_update_change_rate(
+		&mut self,
+		change_rate: CommissionChangeRate<T::BlockNumber>,
+	) -> DispatchResult {
+		ensure!(!&self.less_restrictive(&change_rate), Error::<T>::CommissionChangeRateNotAllowed);
+
+		if self.change_rate.is_none() {
+			self.register_update();
+		}
+		self.change_rate = Some(change_rate);
+		Ok(())
+	}
+
+	/// Updates a commission's `throttle_from` field to the current block.
+	fn register_update(&mut self) {
+		self.throttle_from = Some(<frame_system::Pallet<T>>::block_number());
+	}
+
+	/// Checks whether a change rate is less restrictive than the current change rate, if any.
+	///
+	/// No change rate will always be less restrictive than some change rate, so where no
+	/// `change_rate` is currently set, `false` is returned.
+	fn less_restrictive(&self, new: &CommissionChangeRate<T::BlockNumber>) -> bool {
+		self.change_rate
+			.as_ref()
+			.map(|c| new.max_increase > c.max_increase || new.min_delay < c.min_delay)
+			.unwrap_or(false)
+	}
+}
+
+/// Pool commission change rate preferences.
+///
+/// The pool root is able to set a commission change rate for their pool. A commission change rate
+/// consists of 2 values; (1) the maximum allowed commission change, and (2) the minimum amount of
+/// blocks that must elapse before commission updates are allowed again.
+///
+/// Commission change rates are not applied to decreases in commission.
+#[derive(Encode, Decode, MaxEncodedLen, TypeInfo, Debug, PartialEq, Copy, Clone)]
+pub struct CommissionChangeRate<BlockNumber> {
+	/// The maximum amount the commission can be updated by per `min_delay` period.
+	pub max_increase: Perbill,
+	/// How often an update can take place.
+	pub min_delay: BlockNumber,
+}
+
 /// Pool permissions and state
 #[derive(Encode, Decode, MaxEncodedLen, TypeInfo, DebugNoBound, PartialEq, Clone)]
 #[codec(mel_bound(T: Config))]
 #[scale_info(skip_type_params(T))]
 pub struct BondedPoolInner<T: Config> {
-	/// Total points of all the members in the pool who are actively bonded.
-	pub points: BalanceOf<T>,
-	/// The current state of the pool.
-	pub state: PoolState,
+	/// The commission rate of the pool.
+	pub commission: Commission<T>,
 	/// Count of members that belong to the pool.
 	pub member_counter: u32,
+	/// Total points of all the members in the pool who are actively bonded.
+	pub points: BalanceOf<T>,
 	/// See [`PoolRoles`].
 	pub roles: PoolRoles<T::AccountId>,
+	/// The current state of the pool.
+	pub state: PoolState,
 }
 
 /// A wrapper for bonded pools, with utility functions.
 ///
-/// The main purpose of this is to wrap a [`BondedPoolInner`], with the account + id of the pool,
-/// for easier access.
+/// The main purpose of this is to wrap a [`BondedPoolInner`], with the account
+/// + id of the pool, for easier access.
 #[derive(RuntimeDebugNoBound)]
 #[cfg_attr(feature = "std", derive(Clone, PartialEq))]
 pub struct BondedPool<T: Config> {
@@ -670,10 +902,11 @@ impl<T: Config> BondedPool<T> {
 		Self {
 			id,
 			inner: BondedPoolInner {
+				commission: Commission::default(),
+				member_counter: Zero::zero(),
+				points: Zero::zero(),
 				roles,
 				state: PoolState::Open,
-				points: Zero::zero(),
-				member_counter: Zero::zero(),
 			},
 		}
 	}
@@ -695,7 +928,7 @@ impl<T: Config> BondedPool<T> {
 
 	/// Consume self and put into storage.
 	fn put(self) {
-		BondedPools::<T>::insert(self.id, BondedPoolInner { ..self.inner });
+		BondedPools::<T>::insert(self.id, self.inner);
 	}
 
 	/// Consume self and remove from storage.
@@ -800,6 +1033,10 @@ impl<T: Config> BondedPool<T> {
 		self.is_root(who) || self.is_bouncer(who)
 	}
 
+	fn can_manage_commission(&self, who: &T::AccountId) -> bool {
+		self.is_root(who)
+	}
+
 	fn is_destroying(&self) -> bool {
 		matches!(self.state, PoolState::Destroying)
 	}
@@ -952,7 +1189,7 @@ impl<T: Config> BondedPool<T> {
 		// Cache the value
 		let bonded_account = self.bonded_account();
 		T::Currency::transfer(
-			&who,
+			who,
 			&bonded_account,
 			amount,
 			match ty {
@@ -1011,6 +1248,10 @@ pub struct RewardPool<T: Config> {
 	last_recorded_total_payouts: BalanceOf<T>,
 	/// Total amount that this pool has paid out so far to the members.
 	total_rewards_claimed: BalanceOf<T>,
+	/// The amount of commission pending to be claimed.
+	total_commission_pending: BalanceOf<T>,
+	/// The amount of commission that has been claimed.
+	total_commission_claimed: BalanceOf<T>,
 }
 
 impl<T: Config> RewardPool<T> {
@@ -1024,13 +1265,40 @@ impl<T: Config> RewardPool<T> {
 		self.total_rewards_claimed = self.total_rewards_claimed.saturating_add(reward);
 	}
 
-	/// Update the recorded values of the pool.
-	fn update_records(&mut self, id: PoolId, bonded_points: BalanceOf<T>) -> Result<(), Error<T>> {
+	/// Update the recorded values of the reward pool.
+	///
+	/// This function MUST be called whenever the points in the bonded pool change, AND whenever the
+	/// the pools commission is updated. The reason for the former is that a change in pool points
+	/// will alter the share of the reward balance among pool members, and the reason for the latter
+	/// is that a change in commission will alter the share of the reward balance among the pool.
+	fn update_records(
+		&mut self,
+		id: PoolId,
+		bonded_points: BalanceOf<T>,
+		commission: Perbill,
+	) -> Result<(), Error<T>> {
 		let balance = Self::current_balance(id);
-		self.last_recorded_reward_counter = self.current_reward_counter(id, bonded_points)?;
+
+		let (current_reward_counter, new_pending_commission) =
+			self.current_reward_counter(id, bonded_points, commission)?;
+
+		// Store the reward counter at the time of this update. This is used in subsequent calls to
+		// `current_reward_counter`, whereby newly pending rewards (in points) are added to this
+		// value.
+		self.last_recorded_reward_counter = current_reward_counter;
+
+		// Add any new pending commission that has been calculated from `current_reward_counter` to
+		// determine the total pending commission at the time of this update.
+		self.total_commission_pending =
+			self.total_commission_pending.saturating_add(new_pending_commission);
+
+		// Store the total payouts at the time of this update. Total payouts are essentially the
+		// entire historical balance of the reward pool, equating to the current balance + the total
+		// rewards that have left the pool + the total commission that has left the pool.
 		self.last_recorded_total_payouts = balance
-			.checked_add(&self.total_rewards_claimed)
+			.checked_add(&self.total_rewards_claimed.saturating_add(self.total_commission_claimed))
 			.ok_or(Error::<T>::OverflowRisk)?;
+
 		Ok(())
 	}
 
@@ -1040,15 +1308,27 @@ impl<T: Config> RewardPool<T> {
 		&self,
 		id: PoolId,
 		bonded_points: BalanceOf<T>,
-	) -> Result<T::RewardCounter, Error<T>> {
+		commission: Perbill,
+	) -> Result<(T::RewardCounter, BalanceOf<T>), Error<T>> {
 		let balance = Self::current_balance(id);
-		let payouts_since_last_record = balance
+
+		// Calculate the current payout balance. The first 3 values of this calculation added
+		// together represent what the balance would be if no payouts were made. The
+		// `last_recorded_total_payouts` is then subtracted from this value to cancel out previously
+		// recorded payouts, leaving only the remaining payouts that have not been claimed.
+		let current_payout_balance = balance
 			.saturating_add(self.total_rewards_claimed)
+			.saturating_add(self.total_commission_claimed)
 			.saturating_sub(self.last_recorded_total_payouts);
 
+		// Split the `current_payout_balance` into claimable rewards and claimable commission
+		// according to the current commission rate.
+		let new_pending_commission = commission * current_payout_balance;
+		let new_pending_rewards = current_payout_balance.saturating_sub(new_pending_commission);
+
 		// * accuracy notes regarding the multiplication in `checked_from_rational`:
-		// `payouts_since_last_record` is a subset of the total_issuance at the very
-		// worse. `bonded_points` are similarly, in a non-slashed pool, have the same granularity as
+		// `current_payout_balance` is a subset of the total_issuance at the very worse.
+		// `bonded_points` are similarly, in a non-slashed pool, have the same granularity as
 		// balance, and are thus below within the range of total_issuance. In the worse case
 		// scenario, for `saturating_from_rational`, we have:
 		//
@@ -1066,22 +1346,26 @@ impl<T: Config> RewardPool<T> {
 		// represented as `FixedU128`, which means it is less than `total_issuance * 10^18`.
 		//
 		// * accuracy notes regarding `checked_from_rational` collapsing to zero, meaning that no
-		// reward can be claimed:
+		//   reward can be claimed:
 		//
-		// largest `bonded_points`, such that the reward counter is non-zero, with `FixedU128`
-		// will be when the payout is being computed. This essentially means `payout/bonded_points`
-		// needs to be more than 1/1^18. Thus, assuming that `bonded_points` will always be less
-		// than `10 * dot_total_issuance`, if the reward_counter is the smallest possible value,
-		// the value of the reward being calculated is:
+		// largest `bonded_points`, such that the reward counter is non-zero, with `FixedU128` will
+		// be when the payout is being computed. This essentially means `payout/bonded_points` needs
+		// to be more than 1/1^18. Thus, assuming that `bonded_points` will always be less than `10
+		// * dot_total_issuance`, if the reward_counter is the smallest possible value, the value of
+		//   the
+		// reward being calculated is:
 		//
 		// x / 10^20 = 1/ 10^18
 		//
 		// x = 100
 		//
 		// which is basically 10^-8 DOTs. See `smallest_claimable_reward` for an example of this.
-		T::RewardCounter::checked_from_rational(payouts_since_last_record, bonded_points)
-			.and_then(|ref r| self.last_recorded_reward_counter.checked_add(r))
-			.ok_or(Error::<T>::OverflowRisk)
+		let current_reward_counter =
+			T::RewardCounter::checked_from_rational(new_pending_rewards, bonded_points)
+				.and_then(|ref r| self.last_recorded_reward_counter.checked_add(r))
+				.ok_or(Error::<T>::OverflowRisk)?;
+
+		Ok((current_reward_counter, new_pending_commission))
 	}
 
 	/// Current free balance of the reward pool.
@@ -1209,9 +1493,10 @@ pub mod pallet {
 	use super::*;
 	use frame_support::traits::StorageVersion;
 	use frame_system::{ensure_signed, pallet_prelude::*};
+	use sp_runtime::Perbill;
 
 	/// The current storage version.
-	const STORAGE_VERSION: StorageVersion = StorageVersion::new(3);
+	const STORAGE_VERSION: StorageVersion = StorageVersion::new(4);
 
 	#[pallet::pallet]
 	#[pallet::storage_version(STORAGE_VERSION)]
@@ -1313,6 +1598,12 @@ pub mod pallet {
 	#[pallet::storage]
 	pub type MaxPoolMembersPerPool<T: Config> = StorageValue<_, u32, OptionQuery>;
 
+	/// The maximum commission that can be charged by a pool. Used on commission payouts to bound
+	/// pool commissions that are > `GlobalMaxCommission`, necessary if a future
+	/// `GlobalMaxCommission` is lower than some current pool commissions.
+	#[pallet::storage]
+	pub type GlobalMaxCommission<T: Config> = StorageValue<_, Perbill, OptionQuery>;
+
 	/// Active members.
 	///
 	/// TWOX-NOTE: SAFE since `AccountId` is a secure hash.
@@ -1326,13 +1617,13 @@ pub mod pallet {
 	pub type BondedPools<T: Config> =
 		CountedStorageMap<_, Twox64Concat, PoolId, BondedPoolInner<T>>;
 
-	/// Reward pools. This is where there rewards for each pool accumulate. When a members payout
-	/// is claimed, the balance comes out fo the reward pool. Keyed by the bonded pools account.
+	/// Reward pools. This is where there rewards for each pool accumulate. When a members payout is
+	/// claimed, the balance comes out fo the reward pool. Keyed by the bonded pools account.
 	#[pallet::storage]
 	pub type RewardPools<T: Config> = CountedStorageMap<_, Twox64Concat, PoolId, RewardPool<T>>;
 
-	/// Groups of unbonding pools. Each group of unbonding pools belongs to a bonded pool,
-	/// hence the name sub-pools. Keyed by the bonded pools account.
+	/// Groups of unbonding pools. Each group of unbonding pools belongs to a
+	/// bonded pool, hence the name sub-pools. Keyed by the bonded pools account.
 	#[pallet::storage]
 	pub type SubPoolsStorage<T: Config> = CountedStorageMap<_, Twox64Concat, PoolId, SubPools<T>>;
 
@@ -1365,6 +1656,7 @@ pub mod pallet {
 		pub max_pools: Option<u32>,
 		pub max_members_per_pool: Option<u32>,
 		pub max_members: Option<u32>,
+		pub global_max_commission: Option<Perbill>,
 	}
 
 	#[cfg(feature = "std")]
@@ -1376,6 +1668,7 @@ pub mod pallet {
 				max_pools: Some(16),
 				max_members_per_pool: Some(32),
 				max_members: Some(16 * 32),
+				global_max_commission: None,
 			}
 		}
 	}
@@ -1394,6 +1687,9 @@ pub mod pallet {
 			if let Some(max_members) = self.max_members {
 				MaxPoolMembers::<T>::put(max_members);
 			}
+			if let Some(global_max_commission) = self.global_max_commission {
+				GlobalMaxCommission::<T>::put(global_max_commission);
+			}
 		}
 	}
 
@@ -1456,6 +1752,17 @@ pub mod pallet {
 		PoolSlashed { pool_id: PoolId, balance: BalanceOf<T> },
 		/// The unbond pool at `era` of pool `pool_id` has been slashed to `balance`.
 		UnbondingPoolSlashed { pool_id: PoolId, era: EraIndex, balance: BalanceOf<T> },
+		/// A pool's commission setting has been changed.
+		PoolCommissionUpdated { pool_id: PoolId, current: Option<(Perbill, T::AccountId)> },
+		/// A pool's maximum commission setting has been changed.
+		PoolMaxCommissionUpdated { pool_id: PoolId, max_commission: Perbill },
+		/// A pool's commission `change_rate` has been changed.
+		PoolCommissionChangeRateUpdated {
+			pool_id: PoolId,
+			change_rate: CommissionChangeRate<T::BlockNumber>,
+		},
+		/// Pool commission has been claimed.
+		PoolCommissionClaimed { pool_id: PoolId, commission: BalanceOf<T> },
 	}
 
 	#[pallet::error]
@@ -1511,6 +1818,18 @@ pub mod pallet {
 		Defensive(DefensiveError),
 		/// Partial unbonding now allowed permissionlessly.
 		PartialUnbondNotAllowedPermissionlessly,
+		/// The pool's max commission cannot be set higher than the existing value.
+		MaxCommissionRestricted,
+		/// The supplied commission exceeds the max allowed commission.
+		CommissionExceedsMaximum,
+		/// Not enough blocks have surpassed since the last commission update.
+		CommissionChangeThrottled,
+		/// The submitted changes to commission change rate are not allowed.
+		CommissionChangeRateNotAllowed,
+		/// There is no pending commission to claim.
+		NoPendingCommission,
+		/// No commission current has been set.
+		NoCommissionCurrentSet,
 		/// Pool id currently in use.
 		PoolIdInUse,
 		/// Pool id provided is not correct/usable.
@@ -1519,7 +1838,7 @@ pub mod pallet {
 		BondExtraRestricted,
 	}
 
-	#[derive(Encode, Decode, PartialEq, TypeInfo, frame_support::PalletError, RuntimeDebug)]
+	#[derive(Encode, Decode, PartialEq, TypeInfo, PalletError, RuntimeDebug)]
 	pub enum DefensiveError {
 		/// There isn't enough space in the unbond pool.
 		NotEnoughSpaceInUnbondPool,
@@ -1571,7 +1890,11 @@ pub mod pallet {
 			let mut reward_pool = RewardPools::<T>::get(pool_id)
 				.defensive_ok_or::<Error<T>>(DefensiveError::RewardPoolNotFound.into())?;
 			// IMPORTANT: reward pool records must be updated with the old points.
-			reward_pool.update_records(pool_id, bonded_pool.points)?;
+			reward_pool.update_records(
+				pool_id,
+				bonded_pool.points,
+				bonded_pool.commission.current(),
+			)?;
 
 			bonded_pool.try_inc_members()?;
 			let points_issued = bonded_pool.try_bond_funds(&who, amount, BondType::Later)?;
@@ -1622,7 +1945,7 @@ pub mod pallet {
 		}
 
 		/// A bonded member can use this to claim their payout based on the rewards that the pool
-		/// has accumulated since their last claimed payout (OR since joining if this is there first
+		/// has accumulated since their last claimed payout (OR since joining if this is their first
 		/// time claiming rewards). The payout will be transferred to the member's account.
 		///
 		/// The member will earn rewards pro rata based on the members stake vs the sum of the
@@ -1684,7 +2007,11 @@ pub mod pallet {
 			// Claim the the payout prior to unbonding. Once the user is unbonding their points no
 			// longer exist in the bonded pool and thus they can no longer claim their payouts. It
 			// is not strictly necessary to claim the rewards, but we do it here for UX.
-			let _ = reward_pool.update_records(bonded_pool.id, bonded_pool.points)?;
+			reward_pool.update_records(
+				bonded_pool.id,
+				bonded_pool.points,
+				bonded_pool.commission.current(),
+			)?;
 			let _ = Self::do_reward_payout(&who, &mut member, &mut bonded_pool, &mut reward_pool)?;
 
 			let current_era = T::Staking::current_era();
@@ -1731,7 +2058,7 @@ pub mod pallet {
 			});
 
 			// Now that we know everything has worked write the items to storage.
-			SubPoolsStorage::insert(&member.pool_id, sub_pools);
+			SubPoolsStorage::insert(member.pool_id, sub_pools);
 			Self::put_member_with_pools(&member_account, member, bonded_pool, reward_pool);
 			Ok(())
 		}
@@ -1820,10 +2147,10 @@ pub mod pallet {
 				.iter()
 				.fold(BalanceOf::<T>::zero(), |accumulator, (era, unlocked_points)| {
 					sum_unlocked_points = sum_unlocked_points.saturating_add(*unlocked_points);
-					if let Some(era_pool) = sub_pools.with_era.get_mut(&era) {
+					if let Some(era_pool) = sub_pools.with_era.get_mut(era) {
 						let balance_to_unbond = era_pool.dissolve(*unlocked_points);
 						if era_pool.points.is_zero() {
-							sub_pools.with_era.remove(&era);
+							sub_pools.with_era.remove(era);
 						}
 						accumulator.saturating_add(balance_to_unbond)
 					} else {
@@ -1872,12 +2199,12 @@ pub mod pallet {
 					None
 				} else {
 					bonded_pool.dec_members().put();
-					SubPoolsStorage::<T>::insert(&member.pool_id, sub_pools);
+					SubPoolsStorage::<T>::insert(member.pool_id, sub_pools);
 					Some(T::WeightInfo::withdraw_unbonded_update(num_slashing_spans))
 				}
 			} else {
 				// we certainly don't need to delete any pools, because no one is being removed.
-				SubPoolsStorage::<T>::insert(&member.pool_id, sub_pools);
+				SubPoolsStorage::<T>::insert(member.pool_id, sub_pools);
 				PoolMembers::<T>::insert(&member_account, member);
 				Some(T::WeightInfo::withdraw_unbonded_update(num_slashing_spans))
 			};
@@ -2002,8 +2329,8 @@ pub mod pallet {
 
 		/// Set a new metadata for the pool.
 		///
-		/// The dispatch origin of this call must be signed by the bouncer, or the root role
-		/// of the pool.
+		/// The dispatch origin of this call must be signed by the bouncer, or the root role of the
+		/// pool.
 		#[pallet::call_index(10)]
 		#[pallet::weight(T::WeightInfo::set_metadata(metadata.len() as u32))]
 		pub fn set_metadata(
@@ -2036,6 +2363,7 @@ pub mod pallet {
 		/// * `max_pools` - Set [`MaxPools`].
 		/// * `max_members` - Set [`MaxPoolMembers`].
 		/// * `max_members_per_pool` - Set [`MaxPoolMembersPerPool`].
+		/// * `global_max_commission` - Set [`GlobalMaxCommission`].
 		#[pallet::call_index(11)]
 		#[pallet::weight(T::WeightInfo::set_configs())]
 		pub fn set_configs(
@@ -2045,6 +2373,7 @@ pub mod pallet {
 			max_pools: ConfigOp<u32>,
 			max_members: ConfigOp<u32>,
 			max_members_per_pool: ConfigOp<u32>,
+			global_max_commission: ConfigOp<Perbill>,
 		) -> DispatchResult {
 			ensure_root(origin)?;
 
@@ -2063,6 +2392,7 @@ pub mod pallet {
 			config_op_exp!(MaxPools::<T>, max_pools);
 			config_op_exp!(MaxPoolMembers::<T>, max_members);
 			config_op_exp!(MaxPoolMembersPerPool::<T>, max_members_per_pool);
+			config_op_exp!(GlobalMaxCommission::<T>, global_max_commission);
 			Ok(())
 		}
 
@@ -2195,6 +2525,103 @@ pub mod pallet {
 			let signer = ensure_signed(origin)?;
 			Self::do_claim_payout(signer, other)
 		}
+
+		/// Set the commission of a pool.
+		//
+		/// Both a commission percentage and a commission payee must be provided in the `current`
+		/// tuple. Where a `current` of `None` is provided, any current commission will be removed.
+		///
+		/// - If a `None` is supplied to `new_commission`, existing commission will be removed.
+		#[pallet::call_index(17)]
+		#[pallet::weight(T::WeightInfo::set_commission())]
+		pub fn set_commission(
+			origin: OriginFor<T>,
+			pool_id: PoolId,
+			new_commission: Option<(Perbill, T::AccountId)>,
+		) -> DispatchResult {
+			let who = ensure_signed(origin)?;
+			let mut bonded_pool = BondedPool::<T>::get(pool_id).ok_or(Error::<T>::PoolNotFound)?;
+			ensure!(bonded_pool.can_manage_commission(&who), Error::<T>::DoesNotHavePermission);
+
+			let mut reward_pool = RewardPools::<T>::get(pool_id)
+				.defensive_ok_or::<Error<T>>(DefensiveError::RewardPoolNotFound.into())?;
+			// IMPORTANT: make sure that everything up to this point is using the current commission
+			// before it updates. Note that `try_update_current` could still fail at this point.
+			reward_pool.update_records(
+				pool_id,
+				bonded_pool.points,
+				bonded_pool.commission.current(),
+			)?;
+			RewardPools::insert(pool_id, reward_pool);
+
+			bonded_pool.commission.try_update_current(&new_commission)?;
+			bonded_pool.put();
+			Self::deposit_event(Event::<T>::PoolCommissionUpdated {
+				pool_id,
+				current: new_commission,
+			});
+			Ok(())
+		}
+
+		/// Set the maximum commission of a pool.
+		///
+		/// - Initial max can be set to any `Perbill`, and only smaller values thereafter.
+		/// - Current commission will be lowered in the event it is higher than a new max
+		///   commission.
+		#[pallet::call_index(18)]
+		#[pallet::weight(T::WeightInfo::set_commission_max())]
+		pub fn set_commission_max(
+			origin: OriginFor<T>,
+			pool_id: PoolId,
+			max_commission: Perbill,
+		) -> DispatchResult {
+			let who = ensure_signed(origin)?;
+			let mut bonded_pool = BondedPool::<T>::get(pool_id).ok_or(Error::<T>::PoolNotFound)?;
+			ensure!(bonded_pool.can_manage_commission(&who), Error::<T>::DoesNotHavePermission);
+
+			bonded_pool.commission.try_update_max(pool_id, max_commission)?;
+			bonded_pool.put();
+
+			Self::deposit_event(Event::<T>::PoolMaxCommissionUpdated { pool_id, max_commission });
+			Ok(())
+		}
+
+		/// Set the commission change rate for a pool.
+		///
+		/// Initial change rate is not bounded, whereas subsequent updates can only be more
+		/// restrictive than the current.
+		#[pallet::call_index(19)]
+		#[pallet::weight(T::WeightInfo::set_commission_change_rate())]
+		pub fn set_commission_change_rate(
+			origin: OriginFor<T>,
+			pool_id: PoolId,
+			change_rate: CommissionChangeRate<T::BlockNumber>,
+		) -> DispatchResult {
+			let who = ensure_signed(origin)?;
+			let mut bonded_pool = BondedPool::<T>::get(pool_id).ok_or(Error::<T>::PoolNotFound)?;
+			ensure!(bonded_pool.can_manage_commission(&who), Error::<T>::DoesNotHavePermission);
+
+			bonded_pool.commission.try_update_change_rate(change_rate)?;
+			bonded_pool.put();
+
+			Self::deposit_event(Event::<T>::PoolCommissionChangeRateUpdated {
+				pool_id,
+				change_rate,
+			});
+			Ok(())
+		}
+
+		/// Claim pending commission.
+		///
+		/// The dispatch origin of this call must be signed by the `root` role of the pool. Pending
+		/// commission is paid out and added to total claimed commission`. Total pending commission
+		/// is reset to zero. the current.
+		#[pallet::call_index(20)]
+		#[pallet::weight(0)]
+		pub fn claim_commission(origin: OriginFor<T>, pool_id: PoolId) -> DispatchResult {
+			let who = ensure_signed(origin)?;
+			Self::do_claim_commission(who, pool_id)
+		}
 	}
 
 	#[pallet::hooks]
@@ -2259,7 +2686,8 @@ impl<T: Config> Pallet<T> {
 			Zero::zero()
 		);
 
-		// This shouldn't fail, but if it does we don't really care
+		// This shouldn't fail, but if it does we don't really care. Remaining balance can consist
+		// of unclaimed pending commission, errorneous transfers to the reward account, etc.
 		let reward_pool_remaining = T::Currency::free_balance(&reward_account);
 		let _ = T::Currency::transfer(
 			&reward_account,
@@ -2295,7 +2723,7 @@ impl<T: Config> Pallet<T> {
 	fn get_member_with_pools(
 		who: &T::AccountId,
 	) -> Result<(PoolMember<T>, BondedPool<T>, RewardPool<T>), Error<T>> {
-		let member = PoolMembers::<T>::get(&who).ok_or(Error::<T>::PoolMemberNotFound)?;
+		let member = PoolMembers::<T>::get(who).ok_or(Error::<T>::PoolMemberNotFound)?;
 		let bonded_pool =
 			BondedPool::<T>::get(member.pool_id).defensive_ok_or(DefensiveError::PoolNotFound)?;
 		let reward_pool =
@@ -2323,8 +2751,8 @@ impl<T: Config> Pallet<T> {
 		current_points: BalanceOf<T>,
 		new_funds: BalanceOf<T>,
 	) -> BalanceOf<T> {
-		let u256 = |x| T::BalanceToU256::convert(x);
-		let balance = |x| T::U256ToBalance::convert(x);
+		let u256 = T::BalanceToU256::convert;
+		let balance = T::U256ToBalance::convert;
 		match (current_balance.is_zero(), current_points.is_zero()) {
 			(_, true) => new_funds.saturating_mul(POINTS_TO_BALANCE_INIT_RATIO.into()),
 			(true, false) => {
@@ -2351,8 +2779,8 @@ impl<T: Config> Pallet<T> {
 		current_points: BalanceOf<T>,
 		points: BalanceOf<T>,
 	) -> BalanceOf<T> {
-		let u256 = |x| T::BalanceToU256::convert(x);
-		let balance = |x| T::U256ToBalance::convert(x);
+		let u256 = T::BalanceToU256::convert;
+		let balance = T::U256ToBalance::convert;
 		if current_balance.is_zero() || current_points.is_zero() || points.is_zero() {
 			// There is nothing to unbond
 			return Zero::zero()
@@ -2378,10 +2806,15 @@ impl<T: Config> Pallet<T> {
 		// a member who has no skin in the game anymore cannot claim any rewards.
 		ensure!(!member.active_points().is_zero(), Error::<T>::FullyUnbonding);
 
-		let current_reward_counter =
-			reward_pool.current_reward_counter(bonded_pool.id, bonded_pool.points)?;
-		let pending_rewards = member.pending_rewards(current_reward_counter)?;
+		let (current_reward_counter, _) = reward_pool.current_reward_counter(
+			bonded_pool.id,
+			bonded_pool.points,
+			bonded_pool.commission.current(),
+		)?;
 
+		// Determine the pending rewards. In scenarios where commission is 100%, `pending_rewards`
+		// will be zero.
+		let pending_rewards = member.pending_rewards(current_reward_counter)?;
 		if pending_rewards.is_zero() {
 			return Ok(pending_rewards)
 		}
@@ -2390,14 +2823,13 @@ impl<T: Config> Pallet<T> {
 		member.last_recorded_reward_counter = current_reward_counter;
 		reward_pool.register_claimed_reward(pending_rewards);
 
-		// Transfer payout to the member.
 		T::Currency::transfer(
 			&bonded_pool.reward_account(),
-			&member_account,
+			member_account,
 			pending_rewards,
 			// defensive: the depositor has put existential deposit into the pool and it stays
 			// untouched, reward account shall not die.
-			ExistenceRequirement::AllowDeath,
+			ExistenceRequirement::KeepAlive,
 		)?;
 
 		Self::deposit_event(Event::<T>::PaidOut {
@@ -2462,6 +2894,8 @@ impl<T: Config> Pallet<T> {
 				last_recorded_reward_counter: Zero::zero(),
 				last_recorded_total_payouts: Zero::zero(),
 				total_rewards_claimed: Zero::zero(),
+				total_commission_pending: Zero::zero(),
+				total_commission_claimed: Zero::zero(),
 			},
 		);
 		ReversePoolIdLookup::<T>::insert(bonded_pool.bonded_account(), pool_id);
@@ -2496,7 +2930,11 @@ impl<T: Config> Pallet<T> {
 
 		// payout related stuff: we must claim the payouts, and updated recorded payout data
 		// before updating the bonded pool points, similar to that of `join` transaction.
-		reward_pool.update_records(bonded_pool.id, bonded_pool.points)?;
+		reward_pool.update_records(
+			bonded_pool.id,
+			bonded_pool.points,
+			bonded_pool.commission.current(),
+		)?;
 		let claimed =
 			Self::do_reward_payout(&who, &mut member, &mut bonded_pool, &mut reward_pool)?;
 
@@ -2522,6 +2960,51 @@ impl<T: Config> Pallet<T> {
 		Ok(())
 	}
 
+	fn do_claim_commission(who: T::AccountId, pool_id: PoolId) -> DispatchResult {
+		let bonded_pool = BondedPool::<T>::get(pool_id).ok_or(Error::<T>::PoolNotFound)?;
+		ensure!(bonded_pool.can_manage_commission(&who), Error::<T>::DoesNotHavePermission);
+
+		let mut reward_pool = RewardPools::<T>::get(pool_id)
+			.defensive_ok_or::<Error<T>>(DefensiveError::RewardPoolNotFound.into())?;
+
+		// IMPORTANT: make sure that any newly pending commission not yet processed is added to
+		// `total_commission_pending`.
+		reward_pool.update_records(
+			pool_id,
+			bonded_pool.points,
+			bonded_pool.commission.current(),
+		)?;
+
+		let commission = reward_pool.total_commission_pending;
+		ensure!(!commission.is_zero(), Error::<T>::NoPendingCommission);
+
+		let payee = bonded_pool
+			.commission
+			.current
+			.as_ref()
+			.map(|(_, p)| p.clone())
+			.ok_or(Error::<T>::NoCommissionCurrentSet)?;
+
+		// Payout claimed commission.
+		T::Currency::transfer(
+			&bonded_pool.reward_account(),
+			&payee,
+			commission,
+			ExistenceRequirement::KeepAlive,
+		)?;
+
+		// Add pending commission to total claimed counter.
+		reward_pool.total_commission_claimed =
+			reward_pool.total_commission_claimed.saturating_add(commission);
+		// Reset total pending commission counter to zero.
+		reward_pool.total_commission_pending = Zero::zero();
+		// Commit reward pool updates
+		RewardPools::<T>::insert(pool_id, reward_pool);
+
+		Self::deposit_event(Event::<T>::PoolCommissionClaimed { pool_id, commission });
+		Ok(())
+	}
+
 	fn do_claim_payout(signer: T::AccountId, who: T::AccountId) -> DispatchResult {
 		if signer != who {
 			ensure!(
@@ -2608,16 +3091,18 @@ impl<T: Config> Pallet<T> {
 		let mut all_members = 0u32;
 		PoolMembers::<T>::iter().for_each(|(_, d)| {
 			let bonded_pool = BondedPools::<T>::get(d.pool_id).unwrap();
-			assert!(!d.total_points().is_zero(), "no member should have zero points: {:?}", d);
+			assert!(!d.total_points().is_zero(), "no member should have zero points: {d:?}");
 			*pools_members.entry(d.pool_id).or_default() += 1;
 			all_members += 1;
 
 			let reward_pool = RewardPools::<T>::get(d.pool_id).unwrap();
 			if !bonded_pool.points.is_zero() {
-				let current_rc =
-					reward_pool.current_reward_counter(d.pool_id, bonded_pool.points).unwrap();
-				*pools_members_pending_rewards.entry(d.pool_id).or_default() +=
-					d.pending_rewards(current_rc).unwrap();
+				let commission = bonded_pool.commission.current();
+				let (current_rc, _) = reward_pool
+					.current_reward_counter(d.pool_id, bonded_pool.points, commission)
+					.unwrap();
+				let pending_rewards = d.pending_rewards(current_rc).unwrap();
+				*pools_members_pending_rewards.entry(d.pool_id).or_default() += pending_rewards;
 			} // else this pool has been heavily slashed and cannot have any rewards anymore.
 		});
 
@@ -2633,14 +3118,14 @@ impl<T: Config> Pallet<T> {
 			);
 			assert!(
 				RewardPool::<T>::current_balance(id) >=
-					pools_members_pending_rewards.get(&id).map(|x| *x).unwrap_or_default()
+					pools_members_pending_rewards.get(&id).copied().unwrap_or_default()
 			)
 		});
 
 		BondedPools::<T>::iter().for_each(|(id, inner)| {
 			let bonded_pool = BondedPool { id, inner };
 			assert_eq!(
-				pools_members.get(&id).map(|x| *x).unwrap_or_default(),
+				pools_members.get(&id).copied().unwrap_or_default(),
 				bonded_pool.member_counter
 			);
 			assert!(MaxPoolMembersPerPool::<T>::get()
@@ -2706,8 +3191,9 @@ impl<T: Config> Pallet<T> {
 			if let Some((reward_pool, bonded_pool)) = RewardPools::<T>::get(pool_member.pool_id)
 				.zip(BondedPools::<T>::get(pool_member.pool_id))
 			{
-				let current_reward_counter = reward_pool
-					.current_reward_counter(pool_member.pool_id, bonded_pool.points)
+				let commission = bonded_pool.commission.current();
+				let (current_reward_counter, _) = reward_pool
+					.current_reward_counter(pool_member.pool_id, bonded_pool.points, commission)
 					.ok()?;
 				return pool_member.pending_rewards(current_reward_counter).ok()
 			}
diff --git a/substrate/frame/nomination-pools/src/migration.rs b/substrate/frame/nomination-pools/src/migration.rs
index a1696326989..f6f166d7f5d 100644
--- a/substrate/frame/nomination-pools/src/migration.rs
+++ b/substrate/frame/nomination-pools/src/migration.rs
@@ -52,9 +52,12 @@ pub mod v1 {
 
 	impl<T: Config> OldBondedPoolInner<T> {
 		fn migrate_to_v1(self) -> BondedPoolInner<T> {
+			// Note: `commission` field not introduced to `BondedPoolInner` until
+			// migration 4.
 			BondedPoolInner {
-				member_counter: self.member_counter,
 				points: self.points,
+				commission: Commission::default(),
+				member_counter: self.member_counter,
 				state: self.state,
 				roles: self.roles.migrate_to_v1(),
 			}
@@ -307,6 +310,8 @@ pub mod v2 {
 						last_recorded_reward_counter: Zero::zero(),
 						last_recorded_total_payouts: Zero::zero(),
 						total_rewards_claimed: Zero::zero(),
+						total_commission_claimed: Zero::zero(),
+						total_commission_pending: Zero::zero(),
 					})
 				},
 			);
@@ -449,3 +454,97 @@ pub mod v3 {
 		}
 	}
 }
+
+pub mod v4 {
+	use super::*;
+
+	#[derive(Decode)]
+	pub struct OldBondedPoolInner<T: Config> {
+		pub points: BalanceOf<T>,
+		pub state: PoolState,
+		pub member_counter: u32,
+		pub roles: PoolRoles<T::AccountId>,
+	}
+
+	impl<T: Config> OldBondedPoolInner<T> {
+		fn migrate_to_v4(self) -> BondedPoolInner<T> {
+			BondedPoolInner {
+				commission: Commission::default(),
+				member_counter: self.member_counter,
+				points: self.points,
+				state: self.state,
+				roles: self.roles,
+			}
+		}
+	}
+
+	/// This migration adds a `commission` field to every `BondedPoolInner`, if
+	/// any.
+	pub struct MigrateToV4<T, U>(sp_std::marker::PhantomData<(T, U)>);
+	impl<T: Config, U: Get<Perbill>> OnRuntimeUpgrade for MigrateToV4<T, U> {
+		fn on_runtime_upgrade() -> Weight {
+			let current = Pallet::<T>::current_storage_version();
+			let onchain = Pallet::<T>::on_chain_storage_version();
+
+			log!(
+				info,
+				"Running migration with current storage version {:?} / onchain {:?}",
+				current,
+				onchain
+			);
+
+			if current == 4 && onchain == 3 {
+				let initial_global_max_commission = U::get();
+				GlobalMaxCommission::<T>::set(Some(initial_global_max_commission));
+				log!(
+					info,
+					"Set initial global max commission to {:?}.",
+					initial_global_max_commission
+				);
+
+				let mut translated = 0u64;
+				BondedPools::<T>::translate::<OldBondedPoolInner<T>, _>(|_key, old_value| {
+					translated.saturating_inc();
+					Some(old_value.migrate_to_v4())
+				});
+
+				current.put::<Pallet<T>>();
+				log!(info, "Upgraded {} pools, storage to version {:?}", translated, current);
+
+				// reads: translated + onchain version.
+				// writes: translated + current.put + initial global commission.
+				T::DbWeight::get().reads_writes(translated + 1, translated + 2)
+			} else {
+				log!(info, "Migration did not execute. This probably should be removed");
+				T::DbWeight::get().reads(1)
+			}
+		}
+
+		#[cfg(feature = "try-runtime")]
+		fn pre_upgrade() -> Result<Vec<u8>, &'static str> {
+			ensure!(
+				Pallet::<T>::current_storage_version() > Pallet::<T>::on_chain_storage_version(),
+				"the on_chain version is equal or more than the current one"
+			);
+			Ok(Vec::new())
+		}
+
+		#[cfg(feature = "try-runtime")]
+		fn post_upgrade(_: Vec<u8>) -> Result<(), &'static str> {
+			// ensure all BondedPools items now contain an `inner.commission: Commission` field.
+			ensure!(
+				BondedPools::<T>::iter().all(|(_, inner)| inner.commission.current.is_none() &&
+					inner.commission.max.is_none() &&
+					inner.commission.change_rate.is_none() &&
+					inner.commission.throttle_from.is_none()),
+				"a commission value has been incorrectly set"
+			);
+			ensure!(
+				GlobalMaxCommission::<T>::get() == Some(U::get()),
+				"global maximum commission error"
+			);
+			ensure!(Pallet::<T>::on_chain_storage_version() == 4, "wrong storage version");
+			Ok(())
+		}
+	}
+}
diff --git a/substrate/frame/nomination-pools/src/mock.rs b/substrate/frame/nomination-pools/src/mock.rs
index e8924c0b9c0..c6b094f0aa0 100644
--- a/substrate/frame/nomination-pools/src/mock.rs
+++ b/substrate/frame/nomination-pools/src/mock.rs
@@ -118,8 +118,8 @@ impl sp_staking::StakingInterface for StakingMock {
 
 	fn stake(who: &Self::AccountId) -> Result<Stake<Self>, DispatchError> {
 		match (
-			UnbondingBalanceMap::get().get(who).map(|v| *v),
-			BondedBalanceMap::get().get(who).map(|v| *v),
+			UnbondingBalanceMap::get().get(who).copied(),
+			BondedBalanceMap::get().get(who).copied(),
 		) {
 			(None, None) => Err(DispatchError::Other("balance not found")),
 			(Some(v), None) => Ok(Stake { total: v, active: 0, stash: *who }),
@@ -251,11 +251,17 @@ pub struct ExtBuilder {
 	members: Vec<(AccountId, Balance)>,
 	max_members: Option<u32>,
 	max_members_per_pool: Option<u32>,
+	global_max_commission: Option<Perbill>,
 }
 
 impl Default for ExtBuilder {
 	fn default() -> Self {
-		Self { members: Default::default(), max_members: Some(4), max_members_per_pool: Some(3) }
+		Self {
+			members: Default::default(),
+			max_members: Some(4),
+			max_members_per_pool: Some(3),
+			global_max_commission: Some(Perbill::from_percent(90)),
+		}
 	}
 }
 
@@ -297,6 +303,11 @@ impl ExtBuilder {
 		self
 	}
 
+	pub fn global_max_commission(mut self, commission: Option<Perbill>) -> Self {
+		self.global_max_commission = commission;
+		self
+	}
+
 	pub fn build(self) -> sp_io::TestExternalities {
 		sp_tracing::try_init_simple();
 		let mut storage =
@@ -308,6 +319,7 @@ impl ExtBuilder {
 			max_pools: Some(2),
 			max_members_per_pool: self.max_members_per_pool,
 			max_members: self.max_members,
+			global_max_commission: self.global_max_commission,
 		}
 		.assimilate_storage(&mut storage);
 
@@ -332,7 +344,7 @@ impl ExtBuilder {
 		ext
 	}
 
-	pub fn build_and_execute(self, test: impl FnOnce() -> ()) {
+	pub fn build_and_execute(self, test: impl FnOnce()) {
 		self.build().execute_with(|| {
 			test();
 			Pools::do_try_state(CheckLevel::get()).unwrap();
@@ -354,6 +366,23 @@ parameter_types! {
 	storage BalancesEvents: u32 = 0;
 }
 
+/// Helper to run a specified amount of blocks.
+pub fn run_blocks(n: u64) {
+	let current_block = System::block_number();
+	run_to_block(n + current_block);
+}
+
+/// Helper to run to a specific block.
+pub fn run_to_block(n: u64) {
+	let current_block = System::block_number();
+	assert!(n > current_block);
+	while System::block_number() < n {
+		Pools::on_finalize(System::block_number());
+		System::set_block_number(System::block_number() + 1);
+		Pools::on_initialize(System::block_number());
+	}
+}
+
 /// All events of this pallet.
 pub fn pool_events_since_last_call() -> Vec<super::Event<Runtime>> {
 	let events = System::events()
@@ -380,7 +409,7 @@ pub fn balances_events_since_last_call() -> Vec<pallet_balances::Event<Runtime>>
 
 /// Same as `fully_unbond`, in permissioned setting.
 pub fn fully_unbond_permissioned(member: AccountId) -> DispatchResult {
-	let points = PoolMembers::<Runtime>::get(&member)
+	let points = PoolMembers::<Runtime>::get(member)
 		.map(|d| d.active_points())
 		.unwrap_or_default();
 	Pools::unbond(RuntimeOrigin::signed(member), member, points)
diff --git a/substrate/frame/nomination-pools/src/tests.rs b/substrate/frame/nomination-pools/src/tests.rs
index 33eee01948d..a4fda2e5d91 100644
--- a/substrate/frame/nomination-pools/src/tests.rs
+++ b/substrate/frame/nomination-pools/src/tests.rs
@@ -3,23 +3,23 @@
 // Copyright (C) Parity Technologies (UK) Ltd.
 // SPDX-License-Identifier: Apache-2.0
 
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
+// Licensed under the Apache License, Version 2.0 (the "License"); you may not
+// use this file except in compliance with the License. You may obtain a copy of
+// the License at
 //
-// 	http://www.apache.org/licenses/LICENSE-2.0
+//  http://www.apache.org/licenses/LICENSE-2.0
 //
 // Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
+// distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+// WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+// License for the specific language governing permissions and limitations under
+// the License.
 
 use super::*;
 use crate::{mock::*, Event};
 use frame_support::{assert_err, assert_noop, assert_ok, assert_storage_noop, bounded_btree_map};
 use pallet_balances::Event as BEvent;
-use sp_runtime::traits::Dispatchable;
+use sp_runtime::{traits::Dispatchable, FixedU128};
 
 macro_rules! unbonding_pools_with_era {
 	($($k:expr => $v:expr),* $(,)?) => {{
@@ -55,10 +55,11 @@ fn test_setup_works() {
 			BondedPool::<Runtime> {
 				id: last_pool,
 				inner: BondedPoolInner {
-					state: PoolState::Open,
-					points: 10,
+					commission: Commission::default(),
 					member_counter: 1,
-					roles: DEFAULT_ROLES
+					points: 10,
+					roles: DEFAULT_ROLES,
+					state: PoolState::Open,
 				},
 			}
 		);
@@ -67,7 +68,9 @@ fn test_setup_works() {
 			RewardPool::<Runtime> {
 				last_recorded_reward_counter: Zero::zero(),
 				last_recorded_total_payouts: 0,
-				total_rewards_claimed: 0
+				total_rewards_claimed: 0,
+				total_commission_claimed: 0,
+				total_commission_pending: 0,
 			}
 		);
 		assert_eq!(
@@ -98,10 +101,11 @@ mod bonded_pool {
 			let mut bonded_pool = BondedPool::<Runtime> {
 				id: 123123,
 				inner: BondedPoolInner {
-					state: PoolState::Open,
-					points: 100,
+					commission: Commission::default(),
 					member_counter: 1,
+					points: 100,
 					roles: DEFAULT_ROLES,
+					state: PoolState::Open,
 				},
 			};
 
@@ -153,10 +157,11 @@ mod bonded_pool {
 			let mut bonded_pool = BondedPool::<Runtime> {
 				id: 123123,
 				inner: BondedPoolInner {
-					state: PoolState::Open,
-					points: 100,
+					commission: Commission::default(),
 					member_counter: 1,
+					points: 100,
 					roles: DEFAULT_ROLES,
+					state: PoolState::Open,
 				},
 			};
 
@@ -241,10 +246,11 @@ mod bonded_pool {
 			let pool = BondedPool::<Runtime> {
 				id: 123,
 				inner: BondedPoolInner {
-					state: PoolState::Open,
-					points: 100,
+					commission: Commission::default(),
 					member_counter: 1,
+					points: 100,
 					roles: DEFAULT_ROLES,
+					state: PoolState::Open,
 				},
 			};
 
@@ -258,7 +264,7 @@ mod bonded_pool {
 			// Simulate a slashed pool at `MaxPointsToBalance` + 1 slashed pool
 			StakingMock::set_bonded_balance(
 				pool.bonded_account(),
-				max_points_to_balance.saturating_add(1).into(),
+				max_points_to_balance.saturating_add(1),
 			);
 			assert_ok!(pool.ok_to_join());
 
@@ -470,16 +476,17 @@ mod join {
 		let bonded = |points, member_counter| BondedPool::<Runtime> {
 			id: 1,
 			inner: BondedPoolInner {
-				state: PoolState::Open,
-				points,
+				commission: Commission::default(),
 				member_counter,
+				points,
 				roles: DEFAULT_ROLES,
+				state: PoolState::Open,
 			},
 		};
 		ExtBuilder::default().with_check(0).build_and_execute(|| {
 			// Given
 			Balances::make_free_balance_be(&11, ExistentialDeposit::get() + 2);
-			assert!(!PoolMembers::<Runtime>::contains_key(&11));
+			assert!(!PoolMembers::<Runtime>::contains_key(11));
 
 			// When
 			assert_ok!(Pools::join(RuntimeOrigin::signed(11), 2, 1));
@@ -496,7 +503,7 @@ mod join {
 			);
 
 			assert_eq!(
-				PoolMembers::<Runtime>::get(&11).unwrap(),
+				PoolMembers::<Runtime>::get(11).unwrap(),
 				PoolMember::<Runtime> { pool_id: 1, points: 2, ..Default::default() }
 			);
 			assert_eq!(BondedPool::<Runtime>::get(1).unwrap(), bonded(12, 2));
@@ -507,7 +514,7 @@ mod join {
 
 			// And
 			Balances::make_free_balance_be(&12, ExistentialDeposit::get() + 12);
-			assert!(!PoolMembers::<Runtime>::contains_key(&12));
+			assert!(!PoolMembers::<Runtime>::contains_key(12));
 
 			// When
 			assert_ok!(Pools::join(RuntimeOrigin::signed(12), 12, 1));
@@ -519,7 +526,7 @@ mod join {
 			);
 
 			assert_eq!(
-				PoolMembers::<Runtime>::get(&12).unwrap(),
+				PoolMembers::<Runtime>::get(12).unwrap(),
 				PoolMember::<Runtime> { pool_id: 1, points: 24, ..Default::default() }
 			);
 			assert_eq!(BondedPool::<Runtime>::get(1).unwrap(), bonded(12 + 24, 3));
@@ -530,7 +537,7 @@ mod join {
 	fn join_errors_correctly() {
 		ExtBuilder::default().with_check(0).build_and_execute(|| {
 			// 10 is already part of the default pool created.
-			assert_eq!(PoolMembers::<Runtime>::get(&10).unwrap().pool_id, 1);
+			assert_eq!(PoolMembers::<Runtime>::get(10).unwrap().pool_id, 1);
 
 			assert_noop!(
 				Pools::join(RuntimeOrigin::signed(10), 420, 123),
@@ -553,10 +560,11 @@ mod join {
 			BondedPool::<Runtime> {
 				id: 123,
 				inner: BondedPoolInner {
+					commission: Commission::default(),
 					member_counter: 1,
-					state: PoolState::Open,
 					points: 100,
 					roles: DEFAULT_ROLES,
+					state: PoolState::Open,
 				},
 			}
 			.put();
@@ -622,10 +630,11 @@ mod join {
 			BondedPool::<Runtime> {
 				id: 123,
 				inner: BondedPoolInner {
-					state: PoolState::Open,
-					points: 100,
+					commission: Commission::default(),
 					member_counter: 1,
+					points: 100,
 					roles: DEFAULT_ROLES,
+					state: PoolState::Open,
 				},
 			}
 			.put();
@@ -722,6 +731,8 @@ mod claim_payout {
 			last_recorded_reward_counter: last_recorded_reward_counter.into(),
 			last_recorded_total_payouts,
 			total_rewards_claimed,
+			total_commission_claimed: 0,
+			total_commission_pending: 0,
 		}
 	}
 
@@ -754,7 +765,7 @@ mod claim_payout {
 				assert_eq!(PoolMembers::<Runtime>::get(10).unwrap(), del(10, 1));
 				// pool's 'last_recorded_reward_counter' and 'last_recorded_total_payouts' don't
 				// really change unless if someone bonds/unbonds.
-				assert_eq!(RewardPools::<Runtime>::get(&1).unwrap(), rew(0, 0, 10));
+				assert_eq!(RewardPools::<Runtime>::get(1).unwrap(), rew(0, 0, 10));
 				assert_eq!(Balances::free_balance(&10), 10);
 				assert_eq!(Balances::free_balance(&default_reward_account()), ed + 90);
 
@@ -767,7 +778,7 @@ mod claim_payout {
 					vec![Event::PaidOut { member: 40, pool_id: 1, payout: 40 }]
 				);
 				assert_eq!(PoolMembers::<Runtime>::get(40).unwrap(), del(40, 1));
-				assert_eq!(RewardPools::<Runtime>::get(&1).unwrap(), rew(0, 0, 50));
+				assert_eq!(RewardPools::<Runtime>::get(1).unwrap(), rew(0, 0, 50));
 				assert_eq!(Balances::free_balance(&40), 40);
 				assert_eq!(Balances::free_balance(&default_reward_account()), ed + 50);
 
@@ -780,9 +791,9 @@ mod claim_payout {
 					vec![Event::PaidOut { member: 50, pool_id: 1, payout: 50 }]
 				);
 				assert_eq!(PoolMembers::<Runtime>::get(50).unwrap(), del(50, 1));
-				assert_eq!(RewardPools::<Runtime>::get(&1).unwrap(), rew(0, 0, 100));
+				assert_eq!(RewardPools::<Runtime>::get(1).unwrap(), rew(0, 0, 100));
 				assert_eq!(Balances::free_balance(&50), 50);
-				assert_eq!(Balances::free_balance(&default_reward_account()), ed + 0);
+				assert_eq!(Balances::free_balance(&default_reward_account()), ed);
 
 				// Given the reward pool has some new rewards
 				assert_ok!(Balances::mutate_account(&default_reward_account(), |a| a.free += 50));
@@ -796,7 +807,7 @@ mod claim_payout {
 					vec![Event::PaidOut { member: 10, pool_id: 1, payout: 5 }]
 				);
 				assert_eq!(PoolMembers::<Runtime>::get(10).unwrap(), del_float(10, 1.5));
-				assert_eq!(RewardPools::<Runtime>::get(&1).unwrap(), rew(0, 0, 105));
+				assert_eq!(RewardPools::<Runtime>::get(1).unwrap(), rew(0, 0, 105));
 				assert_eq!(Balances::free_balance(&10), 10 + 5);
 				assert_eq!(Balances::free_balance(&default_reward_account()), ed + 45);
 
@@ -809,7 +820,7 @@ mod claim_payout {
 					vec![Event::PaidOut { member: 40, pool_id: 1, payout: 20 }]
 				);
 				assert_eq!(PoolMembers::<Runtime>::get(40).unwrap(), del_float(40, 1.5));
-				assert_eq!(RewardPools::<Runtime>::get(&1).unwrap(), rew(0, 0, 125));
+				assert_eq!(RewardPools::<Runtime>::get(1).unwrap(), rew(0, 0, 125));
 				assert_eq!(Balances::free_balance(&40), 40 + 20);
 				assert_eq!(Balances::free_balance(&default_reward_account()), ed + 25);
 
@@ -826,7 +837,7 @@ mod claim_payout {
 					vec![Event::PaidOut { member: 50, pool_id: 1, payout: 50 }]
 				);
 				assert_eq!(PoolMembers::<Runtime>::get(50).unwrap(), del_float(50, 2.0));
-				assert_eq!(RewardPools::<Runtime>::get(&1).unwrap(), rew(0, 0, 175));
+				assert_eq!(RewardPools::<Runtime>::get(1).unwrap(), rew(0, 0, 175));
 				assert_eq!(Balances::free_balance(&50), 50 + 50);
 				assert_eq!(Balances::free_balance(&default_reward_account()), ed + 25);
 
@@ -839,7 +850,7 @@ mod claim_payout {
 					vec![Event::PaidOut { member: 10, pool_id: 1, payout: 5 }]
 				);
 				assert_eq!(PoolMembers::<Runtime>::get(10).unwrap(), del(10, 2));
-				assert_eq!(RewardPools::<Runtime>::get(&1).unwrap(), rew(0, 0, 180));
+				assert_eq!(RewardPools::<Runtime>::get(1).unwrap(), rew(0, 0, 180));
 				assert_eq!(Balances::free_balance(&10), 15 + 5);
 				assert_eq!(Balances::free_balance(&default_reward_account()), ed + 20);
 
@@ -858,7 +869,7 @@ mod claim_payout {
 
 				// We expect a payout of 40
 				assert_eq!(PoolMembers::<Runtime>::get(10).unwrap(), del(10, 6));
-				assert_eq!(RewardPools::<Runtime>::get(&1).unwrap(), rew(0, 0, 220));
+				assert_eq!(RewardPools::<Runtime>::get(1).unwrap(), rew(0, 0, 220));
 				assert_eq!(Balances::free_balance(&10), 20 + 40);
 				assert_eq!(Balances::free_balance(&default_reward_account()), ed + 380);
 
@@ -875,7 +886,7 @@ mod claim_payout {
 					vec![Event::PaidOut { member: 10, pool_id: 1, payout: 2 }]
 				);
 				assert_eq!(PoolMembers::<Runtime>::get(10).unwrap(), del_float(10, 6.2));
-				assert_eq!(RewardPools::<Runtime>::get(&1).unwrap(), rew(0, 0, 222));
+				assert_eq!(RewardPools::<Runtime>::get(1).unwrap(), rew(0, 0, 222));
 				assert_eq!(Balances::free_balance(&10), 60 + 2);
 				assert_eq!(Balances::free_balance(&default_reward_account()), ed + 398);
 
@@ -888,7 +899,7 @@ mod claim_payout {
 					vec![Event::PaidOut { member: 40, pool_id: 1, payout: 188 }]
 				);
 				assert_eq!(PoolMembers::<Runtime>::get(40).unwrap(), del_float(40, 6.2));
-				assert_eq!(RewardPools::<Runtime>::get(&1).unwrap(), rew(0, 0, 410));
+				assert_eq!(RewardPools::<Runtime>::get(1).unwrap(), rew(0, 0, 410));
 				assert_eq!(Balances::free_balance(&40), 60 + 188);
 				assert_eq!(Balances::free_balance(&default_reward_account()), ed + 210);
 
@@ -901,9 +912,9 @@ mod claim_payout {
 					vec![Event::PaidOut { member: 50, pool_id: 1, payout: 210 }]
 				);
 				assert_eq!(PoolMembers::<Runtime>::get(50).unwrap(), del_float(50, 6.2));
-				assert_eq!(RewardPools::<Runtime>::get(&1).unwrap(), rew(0, 0, 620));
+				assert_eq!(RewardPools::<Runtime>::get(1).unwrap(), rew(0, 0, 620));
 				assert_eq!(Balances::free_balance(&50), 100 + 210);
-				assert_eq!(Balances::free_balance(&default_reward_account()), ed + 0);
+				assert_eq!(Balances::free_balance(&default_reward_account()), ed);
 			});
 	}
 
@@ -930,6 +941,56 @@ mod claim_payout {
 		});
 	}
 
+	#[test]
+	fn claim_payout_bounds_commission_above_global() {
+		ExtBuilder::default().build_and_execute(|| {
+			let (mut member, bonded_pool, mut reward_pool) =
+				Pools::get_member_with_pools(&10).unwrap();
+
+			// top up commission payee account to existential deposit
+			let _ = Balances::deposit_creating(&2, 5);
+
+			// Set a commission pool 1 to 75%, with a payee set to `2`
+			assert_ok!(Pools::set_commission(
+				RuntimeOrigin::signed(900),
+				bonded_pool.id,
+				Some((Perbill::from_percent(75), 2)),
+			));
+
+			// re-introduce the global maximum to 50% - 25% lower than the current commission of the
+			// pool.
+			GlobalMaxCommission::<Runtime>::set(Some(Perbill::from_percent(50)));
+
+			assert_eq!(
+				pool_events_since_last_call(),
+				vec![
+					Event::Created { depositor: 10, pool_id: 1 },
+					Event::Bonded { member: 10, pool_id: 1, bonded: 10, joined: true },
+					Event::PoolCommissionUpdated {
+						pool_id: 1,
+						current: Some((Perbill::from_percent(75), 2))
+					}
+				]
+			);
+
+			// The pool earns 10 points
+			assert_ok!(Balances::mutate_account(&default_reward_account(), |a| a.free += 10));
+
+			assert_ok!(Pools::do_reward_payout(
+				&10,
+				&mut member,
+				&mut BondedPool::<Runtime>::get(1).unwrap(),
+				&mut reward_pool
+			));
+
+			// commission applied is 50%, not 75%. Has been bounded by `GlobalMaxCommission`.
+			assert_eq!(
+				pool_events_since_last_call(),
+				vec![Event::PaidOut { member: 10, pool_id: 1, payout: 5 },]
+			);
+		})
+	}
+
 	#[test]
 	fn do_reward_payout_works_with_a_pool_of_1() {
 		let del = |last_recorded_reward_counter| del_float(10, last_recorded_reward_counter);
@@ -987,7 +1048,7 @@ mod claim_payout {
 			assert_eq!(member, del(1.5));
 
 			// Given the pool has earned no new rewards
-			Balances::make_free_balance_be(&default_reward_account(), ed + 0);
+			Balances::make_free_balance_be(&default_reward_account(), ed);
 
 			// When
 			let payout =
@@ -2432,7 +2493,7 @@ mod unbond {
 
 			assert_eq!(
 				SubPoolsStorage::<Runtime>::get(1).unwrap().with_era,
-				unbonding_pools_with_era! { 0 + 3 => UnbondPool::<Runtime> { points: 10, balance: 10 }}
+				unbonding_pools_with_era! { 3 => UnbondPool::<Runtime> { points: 10, balance: 10 }}
 			);
 
 			assert_eq!(
@@ -2440,10 +2501,11 @@ mod unbond {
 				BondedPool {
 					id: 1,
 					inner: BondedPoolInner {
-						state: PoolState::Destroying,
-						points: 0,
+						commission: Commission::default(),
 						member_counter: 1,
+						points: 0,
 						roles: DEFAULT_ROLES,
+						state: PoolState::Destroying,
 					}
 				}
 			);
@@ -2469,17 +2531,18 @@ mod unbond {
 				// Then
 				assert_eq!(
 					SubPoolsStorage::<Runtime>::get(1).unwrap().with_era,
-					unbonding_pools_with_era! { 0 + 3 => UnbondPool { points: 6, balance: 6 }}
+					unbonding_pools_with_era! { 3 => UnbondPool { points: 6, balance: 6 }}
 				);
 				assert_eq!(
 					BondedPool::<Runtime>::get(1).unwrap(),
 					BondedPool {
 						id: 1,
 						inner: BondedPoolInner {
-							state: PoolState::Open,
-							points: 560,
+							commission: Commission::default(),
 							member_counter: 3,
+							points: 560,
 							roles: DEFAULT_ROLES,
+							state: PoolState::Open,
 						}
 					}
 				);
@@ -2498,7 +2561,7 @@ mod unbond {
 				assert_eq!(StakingMock::active_stake(&default_bonded_account()).unwrap(), 94);
 				assert_eq!(
 					PoolMembers::<Runtime>::get(40).unwrap().unbonding_eras,
-					member_unbonding_eras!(0 + 3 => 6)
+					member_unbonding_eras!(3 => 6)
 				);
 				assert_eq!(Balances::free_balance(&40), 40 + 40); // We claim rewards when unbonding
 
@@ -2508,25 +2571,26 @@ mod unbond {
 
 				// Then
 				assert_eq!(
-					SubPoolsStorage::<Runtime>::get(&1).unwrap().with_era,
-					unbonding_pools_with_era! { 0 + 3 => UnbondPool { points: 98, balance: 98 }}
+					SubPoolsStorage::<Runtime>::get(1).unwrap().with_era,
+					unbonding_pools_with_era! { 3 => UnbondPool { points: 98, balance: 98 }}
 				);
 				assert_eq!(
 					BondedPool::<Runtime>::get(1).unwrap(),
 					BondedPool {
 						id: 1,
 						inner: BondedPoolInner {
-							state: PoolState::Destroying,
-							points: 10,
+							commission: Commission::default(),
 							member_counter: 3,
-							roles: DEFAULT_ROLES
+							points: 10,
+							roles: DEFAULT_ROLES,
+							state: PoolState::Destroying,
 						}
 					}
 				);
 				assert_eq!(StakingMock::active_stake(&default_bonded_account()).unwrap(), 2);
 				assert_eq!(
 					PoolMembers::<Runtime>::get(550).unwrap().unbonding_eras,
-					member_unbonding_eras!(0 + 3 => 92)
+					member_unbonding_eras!(3 => 92)
 				);
 				assert_eq!(Balances::free_balance(&550), 550 + 550);
 				assert_eq!(
@@ -2559,10 +2623,11 @@ mod unbond {
 					BondedPool {
 						id: 1,
 						inner: BondedPoolInner {
-							state: PoolState::Destroying,
-							points: 0,
+							commission: Commission::default(),
 							member_counter: 1,
-							roles: DEFAULT_ROLES
+							points: 0,
+							roles: DEFAULT_ROLES,
+							state: PoolState::Destroying,
 						}
 					}
 				);
@@ -2593,7 +2658,7 @@ mod unbond {
 				SubPools {
 					no_era: Default::default(),
 					with_era: unbonding_pools_with_era! {
-						0 + 3 => UnbondPool { balance: 10, points: 100 },
+						3 => UnbondPool { balance: 10, points: 100 },
 						1 + 3 => UnbondPool { balance: 20, points: 20 },
 						2 + 3 => UnbondPool { balance: 101, points: 101}
 					},
@@ -2687,10 +2752,11 @@ mod unbond {
 					BondedPool {
 						id: 1,
 						inner: BondedPoolInner {
+							commission: Commission::default(),
+							member_counter: 3,
+							points: 10, // Only 10 points because 200 + 100 was unbonded
 							roles: DEFAULT_ROLES,
 							state: PoolState::Blocked,
-							points: 10, // Only 10 points because 200 + 100 was unbonded
-							member_counter: 3,
 						}
 					}
 				);
@@ -2700,7 +2766,7 @@ mod unbond {
 					SubPools {
 						no_era: Default::default(),
 						with_era: unbonding_pools_with_era! {
-							0 + 3 => UnbondPool { points: 100 + 200, balance: 100 + 200 }
+							3 => UnbondPool { points: 100 + 200, balance: 100 + 200 }
 						},
 					}
 				);
@@ -2837,10 +2903,11 @@ mod unbond {
 			BondedPool::<Runtime> {
 				id: 1,
 				inner: BondedPoolInner {
-					state: PoolState::Open,
-					points: 10,
+					commission: Commission::default(),
 					member_counter: 1,
+					points: 10,
 					roles: DEFAULT_ROLES,
+					state: PoolState::Open,
 				},
 			}
 			.put();
@@ -3356,7 +3423,7 @@ mod withdraw_unbonded {
 				assert_ok!(fully_unbond_permissioned(550));
 
 				assert_eq!(
-					SubPoolsStorage::<Runtime>::get(&1).unwrap().with_era,
+					SubPoolsStorage::<Runtime>::get(1).unwrap().with_era,
 					unbonding_pools_with_era! { 3 => UnbondPool { points: 550 / 2 + 40 / 2, balance: 550 / 2 + 40 / 2
 					}}
 				);
@@ -3406,7 +3473,7 @@ mod withdraw_unbonded {
 				);
 
 				assert_eq!(
-					SubPoolsStorage::<Runtime>::get(&1).unwrap().with_era,
+					SubPoolsStorage::<Runtime>::get(1).unwrap().with_era,
 					unbonding_pools_with_era! { 3 => UnbondPool { points: 550 / 2, balance: 550 / 2 }}
 				);
 
@@ -3425,7 +3492,7 @@ mod withdraw_unbonded {
 						Event::MemberRemoved { pool_id: 1, member: 550 }
 					]
 				);
-				assert!(SubPoolsStorage::<Runtime>::get(&1).unwrap().with_era.is_empty());
+				assert!(SubPoolsStorage::<Runtime>::get(1).unwrap().with_era.is_empty());
 
 				// now, finally, the depositor can take out its share.
 				unsafe_set_state(1, PoolState::Destroying);
@@ -3433,7 +3500,7 @@ mod withdraw_unbonded {
 
 				// because everyone else has left, the points
 				assert_eq!(
-					SubPoolsStorage::<Runtime>::get(&1).unwrap().with_era,
+					SubPoolsStorage::<Runtime>::get(1).unwrap().with_era,
 					unbonding_pools_with_era! { 6 => UnbondPool { points: 5, balance: 5 }}
 				);
 
@@ -3487,10 +3554,10 @@ mod withdraw_unbonded {
 			assert_eq!(
 				SubPoolsStorage::<Runtime>::get(1).unwrap().with_era,
 				//------------------------------balance decrease is not account for
-				unbonding_pools_with_era! { 0 + 3 => UnbondPool { points: 10, balance: 10 } }
+				unbonding_pools_with_era! { 3 => UnbondPool { points: 10, balance: 10 } }
 			);
 
-			CurrentEra::set(0 + 3);
+			CurrentEra::set(3);
 
 			// When
 			assert_ok!(Pools::withdraw_unbonded(RuntimeOrigin::signed(10), 10, 0));
@@ -3507,7 +3574,7 @@ mod withdraw_unbonded {
 			// Insert the sub-pool
 			let sub_pools = SubPools {
 				no_era: Default::default(),
-				with_era: unbonding_pools_with_era! { 0 + 3 => UnbondPool { points: 10, balance: 10  }},
+				with_era: unbonding_pools_with_era! { 3 => UnbondPool { points: 10, balance: 10  }},
 			};
 			SubPoolsStorage::<Runtime>::insert(1, sub_pools.clone());
 
@@ -3520,7 +3587,7 @@ mod withdraw_unbonded {
 			PoolMembers::<Runtime>::insert(11, member.clone());
 
 			// Simulate calling `unbond`
-			member.unbonding_eras = member_unbonding_eras!(3 + 0 => 10);
+			member.unbonding_eras = member_unbonding_eras!(3 => 10);
 			PoolMembers::<Runtime>::insert(11, member.clone());
 
 			// We are still in the bonding duration
@@ -3530,7 +3597,7 @@ mod withdraw_unbonded {
 			);
 
 			// If we error the member does not get removed
-			assert_eq!(PoolMembers::<Runtime>::get(&11), Some(member));
+			assert_eq!(PoolMembers::<Runtime>::get(11), Some(member));
 			// and the sub pools do not get updated.
 			assert_eq!(SubPoolsStorage::<Runtime>::get(1).unwrap(), sub_pools)
 		});
@@ -3549,10 +3616,11 @@ mod withdraw_unbonded {
 					BondedPool {
 						id: 1,
 						inner: BondedPoolInner {
+							commission: Commission::default(),
+							member_counter: 3,
 							points: 10,
+							roles: DEFAULT_ROLES,
 							state: PoolState::Open,
-							member_counter: 3,
-							roles: DEFAULT_ROLES
 						}
 					}
 				);
@@ -3629,10 +3697,11 @@ mod withdraw_unbonded {
 				BondedPool {
 					id: 1,
 					inner: BondedPoolInner {
-						points: 10,
-						state: PoolState::Open,
+						commission: Commission::default(),
 						member_counter: 2,
+						points: 10,
 						roles: DEFAULT_ROLES,
+						state: PoolState::Open,
 					}
 				}
 			);
@@ -4225,15 +4294,16 @@ mod create {
 				BondedPool {
 					id: 2,
 					inner: BondedPoolInner {
+						commission: Commission::default(),
 						points: StakingMock::minimum_nominator_bond(),
 						member_counter: 1,
-						state: PoolState::Open,
 						roles: PoolRoles {
 							depositor: 11,
 							root: Some(123),
 							nominator: Some(456),
 							bouncer: Some(789)
-						}
+						},
+						state: PoolState::Open,
 					}
 				}
 			);
@@ -4289,10 +4359,11 @@ mod create {
 			BondedPool::<Runtime> {
 				id: 2,
 				inner: BondedPoolInner {
-					state: PoolState::Open,
-					points: 10,
+					commission: Commission::default(),
 					member_counter: 1,
+					points: 10,
 					roles: DEFAULT_ROLES,
+					state: PoolState::Open,
 				},
 			}
 			.put();
@@ -4369,7 +4440,7 @@ fn set_claimable_actor_works() {
 	ExtBuilder::default().build_and_execute(|| {
 		// Given
 		Balances::make_free_balance_be(&11, ExistentialDeposit::get() + 2);
-		assert!(!PoolMembers::<Runtime>::contains_key(&11));
+		assert!(!PoolMembers::<Runtime>::contains_key(11));
 
 		// When
 		assert_ok!(Pools::join(RuntimeOrigin::signed(11), 2, 1));
@@ -4574,12 +4645,14 @@ mod set_configs {
 				ConfigOp::Set(3u32),
 				ConfigOp::Set(4u32),
 				ConfigOp::Set(5u32),
+				ConfigOp::Set(Perbill::from_percent(6))
 			));
 			assert_eq!(MinJoinBond::<Runtime>::get(), 1);
 			assert_eq!(MinCreateBond::<Runtime>::get(), 2);
 			assert_eq!(MaxPools::<Runtime>::get(), Some(3));
 			assert_eq!(MaxPoolMembers::<Runtime>::get(), Some(4));
 			assert_eq!(MaxPoolMembersPerPool::<Runtime>::get(), Some(5));
+			assert_eq!(GlobalMaxCommission::<Runtime>::get(), Some(Perbill::from_percent(6)));
 
 			// Noop does nothing
 			assert_storage_noop!(assert_ok!(Pools::set_configs(
@@ -4589,6 +4662,7 @@ mod set_configs {
 				ConfigOp::Noop,
 				ConfigOp::Noop,
 				ConfigOp::Noop,
+				ConfigOp::Noop,
 			)));
 
 			// Removing works
@@ -4599,12 +4673,14 @@ mod set_configs {
 				ConfigOp::Remove,
 				ConfigOp::Remove,
 				ConfigOp::Remove,
+				ConfigOp::Remove,
 			));
 			assert_eq!(MinJoinBond::<Runtime>::get(), 0);
 			assert_eq!(MinCreateBond::<Runtime>::get(), 0);
 			assert_eq!(MaxPools::<Runtime>::get(), None);
 			assert_eq!(MaxPoolMembers::<Runtime>::get(), None);
 			assert_eq!(MaxPoolMembersPerPool::<Runtime>::get(), None);
+			assert_eq!(GlobalMaxCommission::<Runtime>::get(), None);
 		});
 	}
 }
@@ -4949,8 +5025,6 @@ mod update_roles {
 }
 
 mod reward_counter_precision {
-	use sp_runtime::FixedU128;
-
 	use super::*;
 
 	const DOT: Balance = 10u128.pow(10u32);
@@ -4967,10 +5041,12 @@ mod reward_counter_precision {
 	}
 
 	fn default_pool_reward_counter() -> FixedU128 {
-		RewardPools::<T>::get(1)
+		let bonded_pool = BondedPools::<T>::get(1).unwrap();
+		RewardPools::<Runtime>::get(1)
 			.unwrap()
-			.current_reward_counter(1, BondedPools::<T>::get(1).unwrap().points)
+			.current_reward_counter(1, bonded_pool.points, bonded_pool.commission.current())
 			.unwrap()
+			.0
 	}
 
 	fn pending_rewards(of: AccountId) -> Option<BalanceOf<T>> {
@@ -5292,3 +5368,1353 @@ mod reward_counter_precision {
 			});
 	}
 }
+
+mod commission {
+	use super::*;
+
+	#[test]
+	fn set_commission_works() {
+		ExtBuilder::default().build_and_execute(|| {
+			let pool_id = 1;
+			let root = 900;
+
+			// Commission can be set by the `root` role.
+
+			// When:
+			assert_ok!(Pools::set_commission(
+				RuntimeOrigin::signed(root),
+				pool_id,
+				Some((Perbill::from_percent(50), root))
+			));
+
+			// Then:
+			assert_eq!(
+				pool_events_since_last_call(),
+				vec![
+					Event::Created { depositor: 10, pool_id },
+					Event::Bonded { member: 10, pool_id, bonded: 10, joined: true },
+					Event::PoolCommissionUpdated {
+						pool_id,
+						current: Some((Perbill::from_percent(50), root))
+					},
+				]
+			);
+
+			// Commission can be updated only, while keeping the same payee.
+
+			// When:
+			assert_ok!(Pools::set_commission(
+				RuntimeOrigin::signed(root),
+				1,
+				Some((Perbill::from_percent(25), root))
+			));
+
+			// Then:
+			assert_eq!(
+				pool_events_since_last_call(),
+				vec![Event::PoolCommissionUpdated {
+					pool_id,
+					current: Some((Perbill::from_percent(25), root))
+				},]
+			);
+
+			// Payee can be updated only, while keeping the same commission.
+
+			// Given:
+			let payee = 901;
+
+			// When:
+			assert_ok!(Pools::set_commission(
+				RuntimeOrigin::signed(root),
+				pool_id,
+				Some((Perbill::from_percent(25), payee))
+			));
+
+			// Then:
+			assert_eq!(
+				pool_events_since_last_call(),
+				vec![Event::PoolCommissionUpdated {
+					pool_id: 1,
+					current: Some((Perbill::from_percent(25), payee))
+				},]
+			);
+
+			// Pool earns 80 points and a payout is triggered.
+
+			// Given:
+			assert_ok!(Balances::mutate_account(&default_reward_account(), |a| a.free += 80));
+			assert_eq!(
+				PoolMembers::<Runtime>::get(10).unwrap(),
+				PoolMember::<Runtime> { pool_id, points: 10, ..Default::default() }
+			);
+
+			// When:
+			assert_ok!(Pools::claim_payout(RuntimeOrigin::signed(10)));
+
+			// Then:
+			assert_eq!(
+				pool_events_since_last_call(),
+				vec![Event::PaidOut { member: 10, pool_id, payout: 60 }]
+			);
+			assert_eq!(RewardPool::<Runtime>::current_balance(pool_id), 20);
+
+			// Pending pool commission can be claimed by the root role.
+
+			// When:
+			assert_ok!(Pools::claim_commission(RuntimeOrigin::signed(root), pool_id));
+
+			// Then:
+			assert_eq!(RewardPool::<Runtime>::current_balance(pool_id), 0);
+			assert_eq!(
+				pool_events_since_last_call(),
+				vec![Event::PoolCommissionClaimed { pool_id: 1, commission: 20 }]
+			);
+
+			// Commission can be removed from the pool completely.
+
+			// When:
+			assert_ok!(Pools::set_commission(RuntimeOrigin::signed(root), pool_id, None));
+
+			// Then:
+			assert_eq!(
+				pool_events_since_last_call(),
+				vec![Event::PoolCommissionUpdated { pool_id, current: None },]
+			);
+
+			// Given a pool now has a reward counter history, additional rewards and payouts can be
+			// made while maintaining a correct ledger of the reward pool. Pool earns 100 points,
+			// payout is triggered.
+			//
+			// Note that the `total_commission_pending` will not be updated until `update_records`
+			// is next called, which is not done in this test segment..
+
+			// Given:
+			assert_ok!(Balances::mutate_account(&default_reward_account(), |a| a.free += 100));
+
+			// When:
+			assert_ok!(Pools::claim_payout(RuntimeOrigin::signed(10)));
+
+			// Then:
+			assert_eq!(
+				pool_events_since_last_call(),
+				vec![Event::PaidOut { member: 10, pool_id, payout: 100 },]
+			);
+			assert_eq!(
+				RewardPools::<Runtime>::get(pool_id).unwrap(),
+				RewardPool {
+					last_recorded_reward_counter: FixedU128::from_float(6.0),
+					last_recorded_total_payouts: 80,
+					total_rewards_claimed: 160,
+					total_commission_pending: 0,
+					total_commission_claimed: 20
+				}
+			);
+
+			// When set commission is called again, update_records is called and
+			// `total_commission_pending` is updated, based on the current reward counter and pool
+			// balance.
+			//
+			// Note that commission is now 0%, so it should not come into play with subsequent
+			// payouts.
+
+			// When:
+			assert_ok!(Pools::set_commission(
+				RuntimeOrigin::signed(root),
+				1,
+				Some((Perbill::from_percent(10), root))
+			));
+
+			// Then:
+			assert_eq!(
+				pool_events_since_last_call(),
+				vec![Event::PoolCommissionUpdated {
+					pool_id,
+					current: Some((Perbill::from_percent(10), root))
+				},]
+			);
+			assert_eq!(
+				RewardPools::<Runtime>::get(pool_id).unwrap(),
+				RewardPool {
+					last_recorded_reward_counter: FixedU128::from_float(16.0),
+					last_recorded_total_payouts: 180,
+					total_rewards_claimed: 160,
+					total_commission_pending: 0,
+					total_commission_claimed: 20
+				}
+			);
+
+			// Supplying a 0% commission along with a payee results in a `None` current value.
+
+			// When:
+			assert_ok!(Pools::set_commission(
+				RuntimeOrigin::signed(root),
+				pool_id,
+				Some((Perbill::from_percent(0), root))
+			));
+
+			// Then:
+			assert_eq!(
+				BondedPool::<Runtime>::get(1).unwrap().commission,
+				Commission { current: None, max: None, change_rate: None, throttle_from: Some(1) }
+			);
+			assert_eq!(
+				pool_events_since_last_call(),
+				vec![Event::PoolCommissionUpdated {
+					pool_id,
+					current: Some((Perbill::from_percent(0), root))
+				},]
+			);
+
+			// The payee can be updated even when commission has reached maximum commission. Both
+			// commission and max commission are set to 10% to test this.
+
+			// Given:
+			assert_ok!(Pools::set_commission_max(
+				RuntimeOrigin::signed(root),
+				pool_id,
+				Perbill::from_percent(10)
+			));
+			assert_ok!(Pools::set_commission(
+				RuntimeOrigin::signed(root),
+				pool_id,
+				Some((Perbill::from_percent(10), root))
+			));
+
+			// When:
+			assert_ok!(Pools::set_commission(
+				RuntimeOrigin::signed(root),
+				pool_id,
+				Some((Perbill::from_percent(10), payee))
+			));
+
+			// Then:
+			assert_eq!(
+				pool_events_since_last_call(),
+				vec![
+					Event::PoolMaxCommissionUpdated {
+						pool_id,
+						max_commission: Perbill::from_percent(10)
+					},
+					Event::PoolCommissionUpdated {
+						pool_id,
+						current: Some((Perbill::from_percent(10), root))
+					},
+					Event::PoolCommissionUpdated {
+						pool_id,
+						current: Some((Perbill::from_percent(10), payee))
+					}
+				]
+			);
+		});
+	}
+
+	#[test]
+	fn commission_reward_counter_works_one_member() {
+		ExtBuilder::default().build_and_execute(|| {
+			let pool_id = 1;
+			let root = 900;
+			let member = 10;
+
+			// Set the pool commission to 10% to test commission shares. Pool is topped up 40 points
+			// and `member` immediately claims their pending rewards. Reward pooll should still have
+			// 10% share.
+
+			// Given:
+			assert_ok!(Pools::set_commission(
+				RuntimeOrigin::signed(root),
+				1,
+				Some((Perbill::from_percent(10), root)),
+			));
+			assert_ok!(Balances::mutate_account(&default_reward_account(), |a| a.free += 40));
+
+			// When:
+			assert_ok!(Pools::claim_payout(RuntimeOrigin::signed(10)));
+
+			// Then:
+			assert_eq!(RewardPool::<Runtime>::current_balance(pool_id), 4);
+
+			// Set pool commission to 20% and repeat the same process.
+
+			// When:
+			assert_ok!(Pools::set_commission(
+				RuntimeOrigin::signed(root),
+				1,
+				Some((Perbill::from_percent(20), root)),
+			));
+
+			// Then:
+			assert_eq!(
+				RewardPools::<Runtime>::get(pool_id).unwrap(),
+				RewardPool {
+					last_recorded_reward_counter: FixedU128::from_float(3.6),
+					last_recorded_total_payouts: 40,
+					total_rewards_claimed: 36,
+					total_commission_pending: 4,
+					total_commission_claimed: 0
+				}
+			);
+
+			// The current reward counter should yield the correct pending rewards of zero.
+
+			// Given:
+			let (current_reward_counter, _) = RewardPools::<Runtime>::get(pool_id)
+				.unwrap()
+				.current_reward_counter(
+					pool_id,
+					BondedPools::<Runtime>::get(pool_id).unwrap().points,
+					Perbill::from_percent(20),
+				)
+				.unwrap();
+
+			// Then:
+			assert_eq!(
+				PoolMembers::<Runtime>::get(member)
+					.unwrap()
+					.pending_rewards(current_reward_counter)
+					.unwrap(),
+				0
+			);
+
+			assert_eq!(
+				pool_events_since_last_call(),
+				vec![
+					Event::Created { depositor: 10, pool_id: 1 },
+					Event::Bonded { member: 10, pool_id: 1, bonded: 10, joined: true },
+					Event::PoolCommissionUpdated {
+						pool_id: 1,
+						current: Some((Perbill::from_percent(10), 900))
+					},
+					Event::PaidOut { member: 10, pool_id: 1, payout: 36 },
+					Event::PoolCommissionUpdated {
+						pool_id: 1,
+						current: Some((Perbill::from_percent(20), 900))
+					}
+				]
+			);
+		})
+	}
+
+	#[test]
+	fn set_commission_handles_errors() {
+		ExtBuilder::default().build_and_execute(|| {
+			assert_eq!(
+				pool_events_since_last_call(),
+				vec![
+					Event::Created { depositor: 10, pool_id: 1 },
+					Event::Bonded { member: 10, pool_id: 1, bonded: 10, joined: true },
+				]
+			);
+
+			// Provided pool does not exist.
+			assert_noop!(
+				Pools::set_commission(
+					RuntimeOrigin::signed(900),
+					9999,
+					Some((Perbill::from_percent(1), 900)),
+				),
+				Error::<Runtime>::PoolNotFound
+			);
+
+			// Sender does not have permission to set commission.
+			assert_noop!(
+				Pools::set_commission(
+					RuntimeOrigin::signed(1),
+					1,
+					Some((Perbill::from_percent(5), 900)),
+				),
+				Error::<Runtime>::DoesNotHavePermission
+			);
+
+			// Commission increases will be throttled if outside of change_rate allowance.
+			// Commission is set to 5%.
+			// Change rate is set to 1% max increase, 2 block delay.
+
+			// When:
+			assert_ok!(Pools::set_commission(
+				RuntimeOrigin::signed(900),
+				1,
+				Some((Perbill::from_percent(5), 900)),
+			));
+			assert_ok!(Pools::set_commission_change_rate(
+				RuntimeOrigin::signed(900),
+				1,
+				CommissionChangeRate { max_increase: Perbill::from_percent(1), min_delay: 2_u64 }
+			));
+			assert_eq!(
+				BondedPool::<Runtime>::get(1).unwrap().commission,
+				Commission {
+					current: Some((Perbill::from_percent(5), 900)),
+					max: None,
+					change_rate: Some(CommissionChangeRate {
+						max_increase: Perbill::from_percent(1),
+						min_delay: 2_u64
+					}),
+					throttle_from: Some(1_u64),
+				}
+			);
+			assert_eq!(
+				pool_events_since_last_call(),
+				vec![
+					Event::PoolCommissionUpdated {
+						pool_id: 1,
+						current: Some((Perbill::from_percent(5), 900))
+					},
+					Event::PoolCommissionChangeRateUpdated {
+						pool_id: 1,
+						change_rate: CommissionChangeRate {
+							max_increase: Perbill::from_percent(1),
+							min_delay: 2
+						}
+					}
+				]
+			);
+
+			// Now try to increase commission to 10% (5% increase). This should be throttled.
+			// Then:
+			assert_noop!(
+				Pools::set_commission(
+					RuntimeOrigin::signed(900),
+					1,
+					Some((Perbill::from_percent(10), 900))
+				),
+				Error::<Runtime>::CommissionChangeThrottled
+			);
+
+			run_blocks(2);
+
+			// Increase commission by 1% and provide an initial payee. This should succeed and set
+			// the `throttle_from` field.
+
+			// When:
+			assert_ok!(Pools::set_commission(
+				RuntimeOrigin::signed(900),
+				1,
+				Some((Perbill::from_percent(6), 900))
+			));
+			assert_eq!(
+				BondedPool::<Runtime>::get(1).unwrap().commission,
+				Commission {
+					current: Some((Perbill::from_percent(6), 900)),
+					max: None,
+					change_rate: Some(CommissionChangeRate {
+						max_increase: Perbill::from_percent(1),
+						min_delay: 2_u64
+					}),
+					throttle_from: Some(3_u64),
+				}
+			);
+			assert_eq!(
+				pool_events_since_last_call(),
+				vec![Event::PoolCommissionUpdated {
+					pool_id: 1,
+					current: Some((Perbill::from_percent(6), 900))
+				},]
+			);
+
+			// Attempt to increase the commission an additional 1% (now 7%). This will fail as
+			// `throttle_from` is now the current block. At least 2 blocks need to pass before we
+			// can set commission again.
+
+			// Then:
+			assert_noop!(
+				Pools::set_commission(
+					RuntimeOrigin::signed(900),
+					1,
+					Some((Perbill::from_percent(7), 900))
+				),
+				Error::<Runtime>::CommissionChangeThrottled
+			);
+
+			run_blocks(2);
+
+			// Can now successfully increase the commission again, to 7%.
+
+			// When:
+			assert_ok!(Pools::set_commission(
+				RuntimeOrigin::signed(900),
+				1,
+				Some((Perbill::from_percent(7), 900)),
+			));
+			assert_eq!(
+				pool_events_since_last_call(),
+				vec![Event::PoolCommissionUpdated {
+					pool_id: 1,
+					current: Some((Perbill::from_percent(7), 900))
+				},]
+			);
+
+			run_blocks(2);
+
+			// Now surpassed the `min_delay` threshold, but the `max_increase` threshold is
+			// still at play. An attempted commission change now to 8% (+2% increase) should fail.
+
+			// Then:
+			assert_noop!(
+				Pools::set_commission(
+					RuntimeOrigin::signed(900),
+					1,
+					Some((Perbill::from_percent(9), 900)),
+				),
+				Error::<Runtime>::CommissionChangeThrottled
+			);
+
+			// Now set a max commission to the current 5%. This will also update the current
+			// commission to 5%.
+
+			// When:
+			assert_ok!(Pools::set_commission_max(
+				RuntimeOrigin::signed(900),
+				1,
+				Perbill::from_percent(5)
+			));
+			assert_eq!(
+				BondedPool::<Runtime>::get(1).unwrap().commission,
+				Commission {
+					current: Some((Perbill::from_percent(5), 900)),
+					max: Some(Perbill::from_percent(5)),
+					change_rate: Some(CommissionChangeRate {
+						max_increase: Perbill::from_percent(1),
+						min_delay: 2
+					}),
+					throttle_from: Some(7)
+				}
+			);
+			assert_eq!(
+				pool_events_since_last_call(),
+				vec![
+					Event::PoolCommissionUpdated {
+						pool_id: 1,
+						current: Some((Perbill::from_percent(5), 900))
+					},
+					Event::PoolMaxCommissionUpdated {
+						pool_id: 1,
+						max_commission: Perbill::from_percent(5)
+					}
+				]
+			);
+
+			// Run 2 blocks into the future so we are eligible to update commission again.
+			run_blocks(2);
+
+			// Now attempt again to increase the commission by 1%, to 6%. This is within the change
+			// rate allowance, but `max_commission` will now prevent us from going any higher.
+
+			// Then:
+			assert_noop!(
+				Pools::set_commission(
+					RuntimeOrigin::signed(900),
+					1,
+					Some((Perbill::from_percent(6), 900)),
+				),
+				Error::<Runtime>::CommissionExceedsMaximum
+			);
+		});
+	}
+
+	#[test]
+	fn set_commission_max_works_with_error_tests() {
+		ExtBuilder::default().build_and_execute(|| {
+			// Provided pool does not exist
+			assert_noop!(
+				Pools::set_commission_max(
+					RuntimeOrigin::signed(900),
+					9999,
+					Perbill::from_percent(1)
+				),
+				Error::<Runtime>::PoolNotFound
+			);
+			// Sender does not have permission to set commission
+			assert_noop!(
+				Pools::set_commission_max(RuntimeOrigin::signed(1), 1, Perbill::from_percent(5)),
+				Error::<Runtime>::DoesNotHavePermission
+			);
+
+			// Set a max commission commission pool 1 to 80%
+			assert_ok!(Pools::set_commission_max(
+				RuntimeOrigin::signed(900),
+				1,
+				Perbill::from_percent(80)
+			));
+			assert_eq!(
+				BondedPools::<Runtime>::get(1).unwrap().commission.max,
+				Some(Perbill::from_percent(80))
+			);
+
+			// We attempt to increase the max commission to 90%, but increasing is
+			// disallowed due to pool's max commission.
+			assert_noop!(
+				Pools::set_commission_max(RuntimeOrigin::signed(900), 1, Perbill::from_percent(90)),
+				Error::<Runtime>::MaxCommissionRestricted
+			);
+
+			// We will now set a commission to 75% and then amend the max commission
+			// to 50%. The max commission change should decrease the current
+			// commission to 50%.
+			assert_ok!(Pools::set_commission(
+				RuntimeOrigin::signed(900),
+				1,
+				Some((Perbill::from_percent(75), 900))
+			));
+			assert_ok!(Pools::set_commission_max(
+				RuntimeOrigin::signed(900),
+				1,
+				Perbill::from_percent(50)
+			));
+			assert_eq!(
+				BondedPools::<Runtime>::get(1).unwrap().commission,
+				Commission {
+					current: Some((Perbill::from_percent(50), 900)),
+					max: Some(Perbill::from_percent(50)),
+					change_rate: None,
+					throttle_from: Some(1),
+				}
+			);
+
+			assert_eq!(
+				pool_events_since_last_call(),
+				vec![
+					Event::Created { depositor: 10, pool_id: 1 },
+					Event::Bonded { member: 10, pool_id: 1, bonded: 10, joined: true },
+					Event::PoolMaxCommissionUpdated {
+						pool_id: 1,
+						max_commission: Perbill::from_percent(80)
+					},
+					Event::PoolCommissionUpdated {
+						pool_id: 1,
+						current: Some((Perbill::from_percent(75), 900))
+					},
+					Event::PoolCommissionUpdated {
+						pool_id: 1,
+						current: Some((Perbill::from_percent(50), 900))
+					},
+					Event::PoolMaxCommissionUpdated {
+						pool_id: 1,
+						max_commission: Perbill::from_percent(50)
+					}
+				]
+			);
+		});
+	}
+
+	#[test]
+	fn set_commission_change_rate_works_with_errors() {
+		ExtBuilder::default().build_and_execute(|| {
+			// Provided pool does not exist
+			assert_noop!(
+				Pools::set_commission_change_rate(
+					RuntimeOrigin::signed(900),
+					9999,
+					CommissionChangeRate {
+						max_increase: Perbill::from_percent(5),
+						min_delay: 1000_u64
+					}
+				),
+				Error::<Runtime>::PoolNotFound
+			);
+			// Sender does not have permission to set commission
+			assert_noop!(
+				Pools::set_commission_change_rate(
+					RuntimeOrigin::signed(1),
+					1,
+					CommissionChangeRate {
+						max_increase: Perbill::from_percent(5),
+						min_delay: 1000_u64
+					}
+				),
+				Error::<Runtime>::DoesNotHavePermission
+			);
+
+			// Set a commission change rate for pool 1
+			assert_ok!(Pools::set_commission_change_rate(
+				RuntimeOrigin::signed(900),
+				1,
+				CommissionChangeRate { max_increase: Perbill::from_percent(5), min_delay: 10_u64 }
+			));
+			assert_eq!(
+				BondedPools::<Runtime>::get(1).unwrap().commission.change_rate,
+				Some(CommissionChangeRate {
+					max_increase: Perbill::from_percent(5),
+					min_delay: 10_u64
+				})
+			);
+			assert_eq!(
+				pool_events_since_last_call(),
+				vec![
+					Event::Created { depositor: 10, pool_id: 1 },
+					Event::Bonded { member: 10, pool_id: 1, bonded: 10, joined: true },
+					Event::PoolCommissionChangeRateUpdated {
+						pool_id: 1,
+						change_rate: CommissionChangeRate {
+							max_increase: Perbill::from_percent(5),
+							min_delay: 10
+						}
+					},
+				]
+			);
+
+			// We now try to half the min_delay - this will be disallowed. A greater delay between
+			// commission changes is seen as more restrictive.
+			assert_noop!(
+				Pools::set_commission_change_rate(
+					RuntimeOrigin::signed(900),
+					1,
+					CommissionChangeRate {
+						max_increase: Perbill::from_percent(5),
+						min_delay: 5_u64
+					}
+				),
+				Error::<Runtime>::CommissionChangeRateNotAllowed
+			);
+
+			// We now try to increase the allowed max_increase - this will fail. A smaller allowed
+			// commission change is seen as more restrictive.
+			assert_noop!(
+				Pools::set_commission_change_rate(
+					RuntimeOrigin::signed(900),
+					1,
+					CommissionChangeRate {
+						max_increase: Perbill::from_percent(10),
+						min_delay: 10_u64
+					}
+				),
+				Error::<Runtime>::CommissionChangeRateNotAllowed
+			);
+
+			// Successful more restrictive change of min_delay with the current max_increase
+			assert_ok!(Pools::set_commission_change_rate(
+				RuntimeOrigin::signed(900),
+				1,
+				CommissionChangeRate { max_increase: Perbill::from_percent(5), min_delay: 20_u64 }
+			));
+
+			// Successful more restrictive change of max_increase with the current min_delay
+			assert_ok!(Pools::set_commission_change_rate(
+				RuntimeOrigin::signed(900),
+				1,
+				CommissionChangeRate { max_increase: Perbill::from_percent(4), min_delay: 20_u64 }
+			));
+
+			// Successful more restrictive change of both max_increase and min_delay
+			assert_ok!(Pools::set_commission_change_rate(
+				RuntimeOrigin::signed(900),
+				1,
+				CommissionChangeRate { max_increase: Perbill::from_percent(3), min_delay: 30_u64 }
+			));
+
+			assert_eq!(
+				pool_events_since_last_call(),
+				vec![
+					Event::PoolCommissionChangeRateUpdated {
+						pool_id: 1,
+						change_rate: CommissionChangeRate {
+							max_increase: Perbill::from_percent(5),
+							min_delay: 20
+						}
+					},
+					Event::PoolCommissionChangeRateUpdated {
+						pool_id: 1,
+						change_rate: CommissionChangeRate {
+							max_increase: Perbill::from_percent(4),
+							min_delay: 20
+						}
+					},
+					Event::PoolCommissionChangeRateUpdated {
+						pool_id: 1,
+						change_rate: CommissionChangeRate {
+							max_increase: Perbill::from_percent(3),
+							min_delay: 30
+						}
+					}
+				]
+			);
+		});
+	}
+
+	#[test]
+	fn change_rate_does_not_apply_to_decreasing_commission() {
+		ExtBuilder::default().build_and_execute(|| {
+			// set initial commission of the pool to 10%.
+			assert_ok!(Pools::set_commission(
+				RuntimeOrigin::signed(900),
+				1,
+				Some((Perbill::from_percent(10), 900))
+			));
+
+			// Set a commission change rate for pool 1, 1% every 10 blocks
+			assert_ok!(Pools::set_commission_change_rate(
+				RuntimeOrigin::signed(900),
+				1,
+				CommissionChangeRate { max_increase: Perbill::from_percent(1), min_delay: 10_u64 }
+			));
+			assert_eq!(
+				BondedPools::<Runtime>::get(1).unwrap().commission.change_rate,
+				Some(CommissionChangeRate {
+					max_increase: Perbill::from_percent(1),
+					min_delay: 10_u64
+				})
+			);
+
+			// run `min_delay` blocks to allow a commission update.
+			run_blocks(10_u64);
+
+			// Test `max_increase`: attempt to decrease the commission by 5%. Should succeed.
+			assert_ok!(Pools::set_commission(
+				RuntimeOrigin::signed(900),
+				1,
+				Some((Perbill::from_percent(5), 900))
+			));
+
+			// Test `min_delay`: *immediately* attempt to decrease the commission by 2%. Should
+			// succeed.
+			assert_ok!(Pools::set_commission(
+				RuntimeOrigin::signed(900),
+				1,
+				Some((Perbill::from_percent(3), 900))
+			));
+
+			// Attempt to *increase* the commission by 5%. Should fail.
+			assert_noop!(
+				Pools::set_commission(
+					RuntimeOrigin::signed(900),
+					1,
+					Some((Perbill::from_percent(8), 900))
+				),
+				Error::<Runtime>::CommissionChangeThrottled
+			);
+
+			// Sanity check: the resulting pool Commission state.
+			assert_eq!(
+				BondedPools::<Runtime>::get(1).unwrap().commission,
+				Commission {
+					current: Some((Perbill::from_percent(3), 900)),
+					max: None,
+					change_rate: Some(CommissionChangeRate {
+						max_increase: Perbill::from_percent(1),
+						min_delay: 10_u64
+					}),
+					throttle_from: Some(11),
+				}
+			);
+
+			assert_eq!(
+				pool_events_since_last_call(),
+				vec![
+					Event::Created { depositor: 10, pool_id: 1 },
+					Event::Bonded { member: 10, pool_id: 1, bonded: 10, joined: true },
+					Event::PoolCommissionUpdated {
+						pool_id: 1,
+						current: Some((Perbill::from_percent(10), 900))
+					},
+					Event::PoolCommissionChangeRateUpdated {
+						pool_id: 1,
+						change_rate: CommissionChangeRate {
+							max_increase: Perbill::from_percent(1),
+							min_delay: 10
+						}
+					},
+					Event::PoolCommissionUpdated {
+						pool_id: 1,
+						current: Some((Perbill::from_percent(5), 900))
+					},
+					Event::PoolCommissionUpdated {
+						pool_id: 1,
+						current: Some((Perbill::from_percent(3), 900))
+					}
+				]
+			);
+		});
+	}
+
+	#[test]
+	fn set_commission_max_to_zero_works() {
+		ExtBuilder::default().build_and_execute(|| {
+			// 0% max commission test.
+			// set commission max 0%.
+			assert_ok!(Pools::set_commission_max(RuntimeOrigin::signed(900), 1, Zero::zero()));
+
+			// a max commission of 0% essentially freezes the current commission, even when None.
+			// All commission update attempts will fail.
+			assert_noop!(
+				Pools::set_commission(
+					RuntimeOrigin::signed(900),
+					1,
+					Some((Perbill::from_percent(1), 900))
+				),
+				Error::<Runtime>::CommissionExceedsMaximum
+			);
+		})
+	}
+
+	#[test]
+	fn set_commission_change_rate_zero_max_increase_works() {
+		ExtBuilder::default().build_and_execute(|| {
+			// set commission change rate to 0% per 10 blocks
+			assert_ok!(Pools::set_commission_change_rate(
+				RuntimeOrigin::signed(900),
+				1,
+				CommissionChangeRate { max_increase: Perbill::from_percent(0), min_delay: 10_u64 }
+			));
+
+			// even though there is a min delay of 10 blocks, a max increase of 0% essentially
+			// freezes the commission. All commission update attempts will fail.
+			assert_noop!(
+				Pools::set_commission(
+					RuntimeOrigin::signed(900),
+					1,
+					Some((Perbill::from_percent(1), 900))
+				),
+				Error::<Runtime>::CommissionChangeThrottled
+			);
+		})
+	}
+
+	#[test]
+	fn set_commission_change_rate_zero_min_delay_works() {
+		ExtBuilder::default().build_and_execute(|| {
+			// set commission change rate to 1% with a 0 block `min_delay`.
+			assert_ok!(Pools::set_commission_change_rate(
+				RuntimeOrigin::signed(900),
+				1,
+				CommissionChangeRate { max_increase: Perbill::from_percent(1), min_delay: 0_u64 }
+			));
+			assert_eq!(
+				BondedPools::<Runtime>::get(1).unwrap().commission,
+				Commission {
+					current: None,
+					max: None,
+					change_rate: Some(CommissionChangeRate {
+						max_increase: Perbill::from_percent(1),
+						min_delay: 0
+					}),
+					throttle_from: Some(1)
+				}
+			);
+
+			// since there is no min delay, we should be able to immediately set the commission.
+			assert_ok!(Pools::set_commission(
+				RuntimeOrigin::signed(900),
+				1,
+				Some((Perbill::from_percent(1), 900))
+			));
+
+			// sanity check: increasing again to more than +1% will fail.
+			assert_noop!(
+				Pools::set_commission(
+					RuntimeOrigin::signed(900),
+					1,
+					Some((Perbill::from_percent(3), 900))
+				),
+				Error::<Runtime>::CommissionChangeThrottled
+			);
+		})
+	}
+
+	#[test]
+	fn set_commission_change_rate_zero_value_works() {
+		ExtBuilder::default().build_and_execute(|| {
+			// Check zero values play nice. 0 `min_delay` and 0% max_increase test.
+			// set commission change rate to 0% per 0 blocks.
+			assert_ok!(Pools::set_commission_change_rate(
+				RuntimeOrigin::signed(900),
+				1,
+				CommissionChangeRate { max_increase: Perbill::from_percent(0), min_delay: 0_u64 }
+			));
+
+			// even though there is no min delay, a max increase of 0% essentially freezes the
+			// commission. All commission update attempts will fail.
+			assert_noop!(
+				Pools::set_commission(
+					RuntimeOrigin::signed(900),
+					1,
+					Some((Perbill::from_percent(1), 900))
+				),
+				Error::<Runtime>::CommissionChangeThrottled
+			);
+
+			assert_eq!(
+				pool_events_since_last_call(),
+				vec![
+					Event::Created { depositor: 10, pool_id: 1 },
+					Event::Bonded { member: 10, pool_id: 1, bonded: 10, joined: true },
+					Event::PoolCommissionChangeRateUpdated {
+						pool_id: 1,
+						change_rate: CommissionChangeRate {
+							max_increase: Perbill::from_percent(0),
+							min_delay: 0_u64
+						}
+					}
+				]
+			);
+		})
+	}
+
+	#[test]
+	fn do_reward_payout_with_various_commissions() {
+		ExtBuilder::default().build_and_execute(|| {
+			// turn off GlobalMaxCommission for this test.
+			GlobalMaxCommission::<Runtime>::set(None);
+			let pool_id = 1;
+
+			// top up commission payee account to existential deposit
+			let _ = Balances::deposit_creating(&2, 5);
+
+			// Set a commission pool 1 to 33%, with a payee set to `2`
+			assert_ok!(Pools::set_commission(
+				RuntimeOrigin::signed(900),
+				pool_id,
+				Some((Perbill::from_percent(33), 2)),
+			));
+			assert_eq!(
+				pool_events_since_last_call(),
+				vec![
+					Event::Created { depositor: 10, pool_id: 1 },
+					Event::Bonded { member: 10, pool_id: 1, bonded: 10, joined: true },
+					Event::PoolCommissionUpdated {
+						pool_id: 1,
+						current: Some((Perbill::from_percent(33), 2))
+					},
+				]
+			);
+
+			// The pool earns 10 points
+			assert_ok!(Balances::mutate_account(&default_reward_account(), |a| a.free += 10));
+			assert_ok!(Pools::claim_payout(RuntimeOrigin::signed(10)));
+
+			// Then:
+			assert_eq!(
+				pool_events_since_last_call(),
+				vec![Event::PaidOut { member: 10, pool_id: 1, payout: 7 },]
+			);
+
+			// The pool earns 17 points
+			assert_ok!(Balances::mutate_account(&default_reward_account(), |a| a.free += 17));
+			assert_ok!(Pools::claim_payout(RuntimeOrigin::signed(10)));
+
+			// Then:
+			assert_eq!(
+				pool_events_since_last_call(),
+				vec![Event::PaidOut { member: 10, pool_id: 1, payout: 11 },]
+			);
+
+			// The pool earns 50 points
+			assert_ok!(Balances::mutate_account(&default_reward_account(), |a| a.free += 50));
+			assert_ok!(Pools::claim_payout(RuntimeOrigin::signed(10)));
+
+			// Then:
+			assert_eq!(
+				pool_events_since_last_call(),
+				vec![Event::PaidOut { member: 10, pool_id: 1, payout: 34 },]
+			);
+
+			// The pool earns 10439 points
+			assert_ok!(Balances::mutate_account(&default_reward_account(), |a| a.free += 10439));
+			assert_ok!(Pools::claim_payout(RuntimeOrigin::signed(10)));
+
+			// Then:
+			assert_eq!(
+				pool_events_since_last_call(),
+				vec![Event::PaidOut { member: 10, pool_id: 1, payout: 6994 },]
+			);
+
+			// Set the commission to 100% and ensure the following payout to the pool member will
+			// not happen.
+
+			// When:
+			assert_ok!(Pools::set_commission(
+				RuntimeOrigin::signed(900),
+				pool_id,
+				Some((Perbill::from_percent(100), 2)),
+			));
+
+			// Given:
+			assert_ok!(Balances::mutate_account(&default_reward_account(), |a| a.free += 200));
+			assert_ok!(Pools::claim_payout(RuntimeOrigin::signed(10)));
+
+			// Then:
+			assert_eq!(
+				pool_events_since_last_call(),
+				vec![Event::PoolCommissionUpdated {
+					pool_id: 1,
+					current: Some((Perbill::from_percent(100), 2))
+				},]
+			);
+		})
+	}
+
+	#[test]
+	fn commission_accumulates_on_multiple_rewards() {
+		ExtBuilder::default().build_and_execute(|| {
+			let pool_id = 1;
+
+			// Given:
+
+			// Set initial commission of pool 1 to 10%.
+			assert_ok!(Pools::set_commission(
+				RuntimeOrigin::signed(900),
+				pool_id,
+				Some((Perbill::from_percent(10), 2)),
+			));
+			assert_eq!(
+				pool_events_since_last_call(),
+				vec![
+					Event::Created { depositor: 10, pool_id: 1 },
+					Event::Bonded { member: 10, pool_id: 1, bonded: 10, joined: true },
+					Event::PoolCommissionUpdated {
+						pool_id: 1,
+						current: Some((Perbill::from_percent(10), 2))
+					},
+				]
+			);
+
+			// When:
+
+			// The pool earns 100 points
+			assert_ok!(Balances::mutate_account(&default_reward_account(), |a| a.free += 100));
+
+			// Change commission to 20%
+			assert_ok!(Pools::set_commission(
+				RuntimeOrigin::signed(900),
+				pool_id,
+				Some((Perbill::from_percent(20), 2)),
+			));
+			assert_eq!(
+				pool_events_since_last_call(),
+				vec![Event::PoolCommissionUpdated {
+					pool_id: 1,
+					current: Some((Perbill::from_percent(20), 2))
+				},]
+			);
+
+			// The pool earns 100 points
+			assert_ok!(Balances::mutate_account(&default_reward_account(), |a| a.free += 100));
+
+			// Then:
+
+			// Claim payout:
+			assert_ok!(Pools::claim_payout(RuntimeOrigin::signed(10)));
+
+			// Claim commission:
+			assert_ok!(Pools::claim_commission(RuntimeOrigin::signed(900), pool_id));
+
+			// Then:
+			assert_eq!(
+				pool_events_since_last_call(),
+				vec![
+					Event::PaidOut { member: 10, pool_id: 1, payout: 90 + 80 },
+					Event::PoolCommissionClaimed { pool_id: 1, commission: 30 }
+				]
+			);
+		})
+	}
+
+	#[test]
+	fn last_recorded_total_payouts_needs_commission() {
+		ExtBuilder::default().build_and_execute(|| {
+			let pool_id = 1;
+
+			// Given:
+
+			// Set initial commission of pool 1 to 10%.
+			assert_ok!(Pools::set_commission(
+				RuntimeOrigin::signed(900),
+				pool_id,
+				Some((Perbill::from_percent(10), 2)),
+			));
+			assert_eq!(
+				pool_events_since_last_call(),
+				vec![
+					Event::Created { depositor: 10, pool_id: 1 },
+					Event::Bonded { member: 10, pool_id: 1, bonded: 10, joined: true },
+					Event::PoolCommissionUpdated {
+						pool_id: 1,
+						current: Some((Perbill::from_percent(10), 2))
+					},
+				]
+			);
+
+			// When:
+
+			// The pool earns 100 points
+			assert_ok!(Balances::mutate_account(&default_reward_account(), |a| a.free += 100));
+
+			// Claim payout:
+			assert_ok!(Pools::claim_payout(RuntimeOrigin::signed(10)));
+
+			// Claim commission:
+			assert_ok!(Pools::claim_commission(RuntimeOrigin::signed(900), pool_id));
+
+			// Then:
+
+			assert_eq!(
+				RewardPools::<Runtime>::get(1).unwrap().last_recorded_total_payouts,
+				90 + 10
+			);
+
+			assert_eq!(
+				pool_events_since_last_call(),
+				vec![
+					Event::PaidOut { member: 10, pool_id: 1, payout: 90 },
+					Event::PoolCommissionClaimed { pool_id: 1, commission: 10 }
+				]
+			);
+		})
+	}
+
+	#[test]
+	fn do_reward_payout_with_100_percent_commission() {
+		ExtBuilder::default().build_and_execute(|| {
+			// turn off GlobalMaxCommission for this test.
+			GlobalMaxCommission::<Runtime>::set(None);
+
+			let (mut member, bonded_pool, mut reward_pool) =
+				Pools::get_member_with_pools(&10).unwrap();
+
+			// top up commission payee account to existential deposit
+			let _ = Balances::deposit_creating(&2, 5);
+
+			// Set a commission pool 1 to 100%, with a payee set to `2`
+			assert_ok!(Pools::set_commission(
+				RuntimeOrigin::signed(900),
+				bonded_pool.id,
+				Some((Perbill::from_percent(100), 2)),
+			));
+
+			assert_eq!(
+				pool_events_since_last_call(),
+				vec![
+					Event::Created { depositor: 10, pool_id: 1 },
+					Event::Bonded { member: 10, pool_id: 1, bonded: 10, joined: true },
+					Event::PoolCommissionUpdated {
+						pool_id: 1,
+						current: Some((Perbill::from_percent(100), 2))
+					}
+				]
+			);
+
+			// The pool earns 10 points
+			assert_ok!(Balances::mutate_account(&default_reward_account(), |a| a.free += 10));
+
+			// execute the payout
+			assert_ok!(Pools::do_reward_payout(
+				&10,
+				&mut member,
+				&mut BondedPool::<Runtime>::get(1).unwrap(),
+				&mut reward_pool
+			));
+		})
+	}
+
+	#[test]
+	fn global_max_prevents_100_percent_commission_payout() {
+		ExtBuilder::default().build_and_execute(|| {
+			// Note: GlobalMaxCommission is set at 90%.
+
+			let (mut member, bonded_pool, mut reward_pool) =
+				Pools::get_member_with_pools(&10).unwrap();
+
+			// top up the commission payee account to existential deposit
+			let _ = Balances::deposit_creating(&2, 5);
+
+			// Set a commission pool 1 to 100%, with a payee set to `2`
+			assert_ok!(Pools::set_commission(
+				RuntimeOrigin::signed(900),
+				bonded_pool.id,
+				Some((Perbill::from_percent(100), 2)),
+			));
+			assert_eq!(
+				pool_events_since_last_call(),
+				vec![
+					Event::Created { depositor: 10, pool_id: 1 },
+					Event::Bonded { member: 10, pool_id: 1, bonded: 10, joined: true },
+					Event::PoolCommissionUpdated {
+						pool_id: 1,
+						current: Some((Perbill::from_percent(100), 2))
+					}
+				]
+			);
+
+			// The pool earns 10 points
+			assert_ok!(Balances::mutate_account(&default_reward_account(), |a| a.free += 10));
+
+			// execute the payout
+			assert_ok!(Pools::do_reward_payout(
+				&10,
+				&mut member,
+				&mut BondedPool::<Runtime>::get(1).unwrap(),
+				&mut reward_pool
+			));
+
+			// Confirm the commission was only 9 points out of 10 points, and the payout was 1 out
+			// of 10 points, reflecting the 90% global max commission.
+			assert_eq!(
+				pool_events_since_last_call(),
+				vec![Event::PaidOut { member: 10, pool_id: 1, payout: 1 },]
+			);
+		})
+	}
+
+	#[test]
+	fn claim_commission_works() {
+		ExtBuilder::default().build_and_execute(|| {
+			let pool_id = 1;
+
+			let _ = Balances::deposit_creating(&900, 5);
+			assert_ok!(Pools::set_commission(
+				RuntimeOrigin::signed(900),
+				pool_id,
+				Some((Perbill::from_percent(50), 900))
+			));
+			assert_eq!(
+				pool_events_since_last_call(),
+				vec![
+					Event::Created { depositor: 10, pool_id },
+					Event::Bonded { member: 10, pool_id, bonded: 10, joined: true },
+					Event::PoolCommissionUpdated {
+						pool_id,
+						current: Some((Perbill::from_percent(50), 900))
+					},
+				]
+			);
+
+			// Pool earns 80 points, payout is triggered.
+			assert_ok!(Balances::mutate_account(&default_reward_account(), |a| a.free += 80));
+			assert_eq!(
+				PoolMembers::<Runtime>::get(10).unwrap(),
+				PoolMember::<Runtime> { pool_id, points: 10, ..Default::default() }
+			);
+
+			assert_ok!(Pools::claim_payout(RuntimeOrigin::signed(10)));
+			assert_eq!(
+				pool_events_since_last_call(),
+				vec![Event::PaidOut { member: 10, pool_id, payout: 40 }]
+			);
+
+			// Given:
+			assert_eq!(RewardPool::<Runtime>::current_balance(pool_id), 40);
+
+			// Pool does not exist
+			assert_noop!(
+				Pools::claim_commission(RuntimeOrigin::signed(900), 9999,),
+				Error::<Runtime>::PoolNotFound
+			);
+
+			// Does not have permission.
+			assert_noop!(
+				Pools::claim_commission(RuntimeOrigin::signed(10), pool_id,),
+				Error::<Runtime>::DoesNotHavePermission
+			);
+
+			// When:
+			assert_ok!(Pools::claim_commission(RuntimeOrigin::signed(900), pool_id));
+
+			// Then:
+			assert_eq!(RewardPool::<Runtime>::current_balance(pool_id), 0);
+
+			// No more pending commission.
+			assert_noop!(
+				Pools::claim_commission(RuntimeOrigin::signed(900), pool_id,),
+				Error::<Runtime>::NoPendingCommission
+			);
+		})
+	}
+}
diff --git a/substrate/frame/nomination-pools/src/weights.rs b/substrate/frame/nomination-pools/src/weights.rs
index ca7de7be6e7..cf0048fa48d 100644
--- a/substrate/frame/nomination-pools/src/weights.rs
+++ b/substrate/frame/nomination-pools/src/weights.rs
@@ -18,9 +18,9 @@
 //! Autogenerated weights for pallet_nomination_pools
 //!
 //! THIS FILE WAS AUTO-GENERATED USING THE SUBSTRATE BENCHMARK CLI VERSION 4.0.0-dev
-//! DATE: 2023-02-21, STEPS: `50`, REPEAT: `20`, LOW RANGE: `[]`, HIGH RANGE: `[]`
+//! DATE: 2023-03-08, STEPS: `50`, REPEAT: `20`, LOW RANGE: `[]`, HIGH RANGE: `[]`
 //! WORST CASE MAP SIZE: `1000000`
-//! HOSTNAME: `runner-ehxwxxsd-project-145-concurrent-0`, CPU: `Intel(R) Xeon(R) CPU @ 2.60GHz`
+//! HOSTNAME: `bm3`, CPU: `Intel(R) Core(TM) i7-7700K CPU @ 4.20GHz`
 //! EXECUTION: Some(Wasm), WASM-EXECUTION: Compiled, CHAIN: Some("dev"), DB CACHE: 1024
 
 // Executed Command:
@@ -33,7 +33,7 @@
 // --execution=wasm
 // --wasm-execution=compiled
 // --heap-pages=4096
-// --json-file=/builds/parity/mirrors/substrate/.git/.artifacts/bench.json
+// --json-file=/var/lib/gitlab-runner/builds/zyw4fam_/0/parity/mirrors/substrate/.git/.artifacts/bench.json
 // --pallet=pallet_nomination_pools
 // --chain=dev
 // --header=./HEADER-APACHE2
@@ -64,7 +64,11 @@ pub trait WeightInfo {
 	fn set_configs() -> Weight;
 	fn update_roles() -> Weight;
 	fn chill() -> Weight;
+	fn set_commission() -> Weight;
+	fn set_commission_max() -> Weight;
+	fn set_commission_change_rate() -> Weight;
 	fn set_claim_permission() -> Weight;
+	fn claim_commission() -> Weight;
 }
 
 /// Weights for pallet_nomination_pools using the Substrate node and recommended hardware.
@@ -75,13 +79,15 @@ impl<T: frame_system::Config> WeightInfo for SubstrateWeight<T> {
 	/// Storage: NominationPools PoolMembers (r:1 w:1)
 	/// Proof: NominationPools PoolMembers (max_values: None, max_size: Some(237), added: 2712, mode: MaxEncodedLen)
 	/// Storage: NominationPools BondedPools (r:1 w:1)
-	/// Proof: NominationPools BondedPools (max_values: None, max_size: Some(164), added: 2639, mode: MaxEncodedLen)
+	/// Proof: NominationPools BondedPools (max_values: None, max_size: Some(220), added: 2695, mode: MaxEncodedLen)
 	/// Storage: Staking Bonded (r:1 w:0)
 	/// Proof: Staking Bonded (max_values: None, max_size: Some(72), added: 2547, mode: MaxEncodedLen)
 	/// Storage: Staking Ledger (r:1 w:1)
 	/// Proof: Staking Ledger (max_values: None, max_size: Some(1091), added: 3566, mode: MaxEncodedLen)
 	/// Storage: NominationPools RewardPools (r:1 w:1)
-	/// Proof: NominationPools RewardPools (max_values: None, max_size: Some(60), added: 2535, mode: MaxEncodedLen)
+	/// Proof: NominationPools RewardPools (max_values: None, max_size: Some(92), added: 2567, mode: MaxEncodedLen)
+	/// Storage: NominationPools GlobalMaxCommission (r:1 w:0)
+	/// Proof: NominationPools GlobalMaxCommission (max_values: Some(1), max_size: Some(4), added: 499, mode: MaxEncodedLen)
 	/// Storage: System Account (r:2 w:1)
 	/// Proof: System Account (max_values: None, max_size: Some(128), added: 2603, mode: MaxEncodedLen)
 	/// Storage: NominationPools MaxPoolMembersPerPool (r:1 w:0)
@@ -98,20 +104,21 @@ impl<T: frame_system::Config> WeightInfo for SubstrateWeight<T> {
 	/// Proof: VoterList ListBags (max_values: None, max_size: Some(82), added: 2557, mode: MaxEncodedLen)
 	fn join() -> Weight {
 		// Proof Size summary in bytes:
-		//  Measured:  `3573`
-		//  Estimated: `37988`
-		// Minimum execution time: 169_857 nanoseconds.
-		Weight::from_parts(173_895_000, 0)
-			.saturating_add(Weight::from_parts(0, 37988))
-			.saturating_add(T::DbWeight::get().reads(17_u64))
+		//  Measured:  `3650`
+		//  Estimated: `52435`
+		// Minimum execution time: 160_401_000 picoseconds.
+		Weight::from_parts(161_798_000, 52435)
+			.saturating_add(T::DbWeight::get().reads(18_u64))
 			.saturating_add(T::DbWeight::get().writes(12_u64))
 	}
 	/// Storage: NominationPools PoolMembers (r:1 w:1)
 	/// Proof: NominationPools PoolMembers (max_values: None, max_size: Some(237), added: 2712, mode: MaxEncodedLen)
 	/// Storage: NominationPools BondedPools (r:1 w:1)
-	/// Proof: NominationPools BondedPools (max_values: None, max_size: Some(164), added: 2639, mode: MaxEncodedLen)
+	/// Proof: NominationPools BondedPools (max_values: None, max_size: Some(220), added: 2695, mode: MaxEncodedLen)
 	/// Storage: NominationPools RewardPools (r:1 w:1)
-	/// Proof: NominationPools RewardPools (max_values: None, max_size: Some(60), added: 2535, mode: MaxEncodedLen)
+	/// Proof: NominationPools RewardPools (max_values: None, max_size: Some(92), added: 2567, mode: MaxEncodedLen)
+	/// Storage: NominationPools GlobalMaxCommission (r:1 w:0)
+	/// Proof: NominationPools GlobalMaxCommission (max_values: Some(1), max_size: Some(4), added: 499, mode: MaxEncodedLen)
 	/// Storage: System Account (r:3 w:2)
 	/// Proof: System Account (max_values: None, max_size: Some(128), added: 2603, mode: MaxEncodedLen)
 	/// Storage: Staking Bonded (r:1 w:0)
@@ -126,12 +133,11 @@ impl<T: frame_system::Config> WeightInfo for SubstrateWeight<T> {
 	/// Proof: VoterList ListBags (max_values: None, max_size: Some(82), added: 2557, mode: MaxEncodedLen)
 	fn bond_extra_transfer() -> Weight {
 		// Proof Size summary in bytes:
-		//  Measured:  `3615`
-		//  Estimated: `38583`
-		// Minimum execution time: 167_372 nanoseconds.
-		Weight::from_parts(168_776_000, 0)
-			.saturating_add(Weight::from_parts(0, 38583))
-			.saturating_add(T::DbWeight::get().reads(14_u64))
+		//  Measured:  `3692`
+		//  Estimated: `49070`
+		// Minimum execution time: 157_668_000 picoseconds.
+		Weight::from_parts(161_129_000, 49070)
+			.saturating_add(T::DbWeight::get().reads(15_u64))
 			.saturating_add(T::DbWeight::get().writes(12_u64))
 	}
 	/// Storage: NominationPools ClaimPermissions (r:1 w:0)
@@ -139,9 +145,11 @@ impl<T: frame_system::Config> WeightInfo for SubstrateWeight<T> {
 	/// Storage: NominationPools PoolMembers (r:1 w:1)
 	/// Proof: NominationPools PoolMembers (max_values: None, max_size: Some(237), added: 2712, mode: MaxEncodedLen)
 	/// Storage: NominationPools BondedPools (r:1 w:1)
-	/// Proof: NominationPools BondedPools (max_values: None, max_size: Some(164), added: 2639, mode: MaxEncodedLen)
+	/// Proof: NominationPools BondedPools (max_values: None, max_size: Some(220), added: 2695, mode: MaxEncodedLen)
 	/// Storage: NominationPools RewardPools (r:1 w:1)
-	/// Proof: NominationPools RewardPools (max_values: None, max_size: Some(60), added: 2535, mode: MaxEncodedLen)
+	/// Proof: NominationPools RewardPools (max_values: None, max_size: Some(92), added: 2567, mode: MaxEncodedLen)
+	/// Storage: NominationPools GlobalMaxCommission (r:1 w:0)
+	/// Proof: NominationPools GlobalMaxCommission (max_values: Some(1), max_size: Some(4), added: 499, mode: MaxEncodedLen)
 	/// Storage: System Account (r:3 w:3)
 	/// Proof: System Account (max_values: None, max_size: Some(128), added: 2603, mode: MaxEncodedLen)
 	/// Storage: Staking Bonded (r:1 w:0)
@@ -156,12 +164,11 @@ impl<T: frame_system::Config> WeightInfo for SubstrateWeight<T> {
 	/// Proof: VoterList ListBags (max_values: None, max_size: Some(82), added: 2557, mode: MaxEncodedLen)
 	fn bond_extra_other() -> Weight {
 		// Proof Size summary in bytes:
-		//  Measured:  `3680`
-		//  Estimated: `41099`
-		// Minimum execution time: 186_346 nanoseconds.
-		Weight::from_parts(191_308_000, 0)
-			.saturating_add(Weight::from_parts(0, 41099))
-			.saturating_add(T::DbWeight::get().reads(15_u64))
+		//  Measured:  `3757`
+		//  Estimated: `52576`
+		// Minimum execution time: 176_034_000 picoseconds.
+		Weight::from_parts(176_956_000, 52576)
+			.saturating_add(T::DbWeight::get().reads(16_u64))
 			.saturating_add(T::DbWeight::get().writes(13_u64))
 	}
 	/// Storage: NominationPools ClaimPermissions (r:1 w:0)
@@ -169,31 +176,34 @@ impl<T: frame_system::Config> WeightInfo for SubstrateWeight<T> {
 	/// Storage: NominationPools PoolMembers (r:1 w:1)
 	/// Proof: NominationPools PoolMembers (max_values: None, max_size: Some(237), added: 2712, mode: MaxEncodedLen)
 	/// Storage: NominationPools BondedPools (r:1 w:1)
-	/// Proof: NominationPools BondedPools (max_values: None, max_size: Some(164), added: 2639, mode: MaxEncodedLen)
+	/// Proof: NominationPools BondedPools (max_values: None, max_size: Some(220), added: 2695, mode: MaxEncodedLen)
 	/// Storage: NominationPools RewardPools (r:1 w:1)
-	/// Proof: NominationPools RewardPools (max_values: None, max_size: Some(60), added: 2535, mode: MaxEncodedLen)
+	/// Proof: NominationPools RewardPools (max_values: None, max_size: Some(92), added: 2567, mode: MaxEncodedLen)
+	/// Storage: NominationPools GlobalMaxCommission (r:1 w:0)
+	/// Proof: NominationPools GlobalMaxCommission (max_values: Some(1), max_size: Some(4), added: 499, mode: MaxEncodedLen)
 	/// Storage: System Account (r:1 w:1)
 	/// Proof: System Account (max_values: None, max_size: Some(128), added: 2603, mode: MaxEncodedLen)
 	fn claim_payout() -> Weight {
 		// Proof Size summary in bytes:
-		//  Measured:  `1254`
-		//  Estimated: `13005`
-		// Minimum execution time: 61_423 nanoseconds.
-		Weight::from_parts(63_219_000, 0)
-			.saturating_add(Weight::from_parts(0, 13005))
-			.saturating_add(T::DbWeight::get().reads(5_u64))
+		//  Measured:  `1331`
+		//  Estimated: `19532`
+		// Minimum execution time: 61_551_000 picoseconds.
+		Weight::from_parts(62_201_000, 19532)
+			.saturating_add(T::DbWeight::get().reads(6_u64))
 			.saturating_add(T::DbWeight::get().writes(4_u64))
 	}
 	/// Storage: NominationPools PoolMembers (r:1 w:1)
 	/// Proof: NominationPools PoolMembers (max_values: None, max_size: Some(237), added: 2712, mode: MaxEncodedLen)
 	/// Storage: NominationPools BondedPools (r:1 w:1)
-	/// Proof: NominationPools BondedPools (max_values: None, max_size: Some(164), added: 2639, mode: MaxEncodedLen)
+	/// Proof: NominationPools BondedPools (max_values: None, max_size: Some(220), added: 2695, mode: MaxEncodedLen)
 	/// Storage: NominationPools RewardPools (r:1 w:1)
-	/// Proof: NominationPools RewardPools (max_values: None, max_size: Some(60), added: 2535, mode: MaxEncodedLen)
+	/// Proof: NominationPools RewardPools (max_values: None, max_size: Some(92), added: 2567, mode: MaxEncodedLen)
 	/// Storage: Staking Bonded (r:1 w:0)
 	/// Proof: Staking Bonded (max_values: None, max_size: Some(72), added: 2547, mode: MaxEncodedLen)
 	/// Storage: Staking Ledger (r:1 w:1)
 	/// Proof: Staking Ledger (max_values: None, max_size: Some(1091), added: 3566, mode: MaxEncodedLen)
+	/// Storage: NominationPools GlobalMaxCommission (r:1 w:0)
+	/// Proof: NominationPools GlobalMaxCommission (max_values: Some(1), max_size: Some(4), added: 499, mode: MaxEncodedLen)
 	/// Storage: System Account (r:2 w:1)
 	/// Proof: System Account (max_values: None, max_size: Some(128), added: 2603, mode: MaxEncodedLen)
 	/// Storage: Staking CurrentEra (r:1 w:0)
@@ -212,20 +222,17 @@ impl<T: frame_system::Config> WeightInfo for SubstrateWeight<T> {
 	/// Proof: NominationPools SubPoolsStorage (max_values: None, max_size: Some(24382), added: 26857, mode: MaxEncodedLen)
 	/// Storage: NominationPools CounterForSubPoolsStorage (r:1 w:1)
 	/// Proof: NominationPools CounterForSubPoolsStorage (max_values: Some(1), max_size: Some(4), added: 499, mode: MaxEncodedLen)
-	/// Storage: NominationPools ClaimPermissions (r:0 w:1)
-	/// Proof: NominationPools ClaimPermissions (max_values: None, max_size: Some(41), added: 2516, mode: MaxEncodedLen)
 	fn unbond() -> Weight {
 		// Proof Size summary in bytes:
-		//  Measured:  `3858`
-		//  Estimated: `67379`
-		// Minimum execution time: 174_532 nanoseconds.
-		Weight::from_parts(180_032_000, 0)
-			.saturating_add(Weight::from_parts(0, 67379))
-			.saturating_add(T::DbWeight::get().reads(18_u64))
-			.saturating_add(T::DbWeight::get().writes(14_u64))
+		//  Measured:  `3935`
+		//  Estimated: `82816`
+		// Minimum execution time: 162_755_000 picoseconds.
+		Weight::from_parts(163_518_000, 82816)
+			.saturating_add(T::DbWeight::get().reads(19_u64))
+			.saturating_add(T::DbWeight::get().writes(13_u64))
 	}
 	/// Storage: NominationPools BondedPools (r:1 w:0)
-	/// Proof: NominationPools BondedPools (max_values: None, max_size: Some(164), added: 2639, mode: MaxEncodedLen)
+	/// Proof: NominationPools BondedPools (max_values: None, max_size: Some(220), added: 2695, mode: MaxEncodedLen)
 	/// Storage: Staking Bonded (r:1 w:0)
 	/// Proof: Staking Bonded (max_values: None, max_size: Some(72), added: 2547, mode: MaxEncodedLen)
 	/// Storage: Staking Ledger (r:1 w:1)
@@ -237,13 +244,12 @@ impl<T: frame_system::Config> WeightInfo for SubstrateWeight<T> {
 	/// The range of component `s` is `[0, 100]`.
 	fn pool_withdraw_unbonded(s: u32, ) -> Weight {
 		// Proof Size summary in bytes:
-		//  Measured:  `1779`
-		//  Estimated: `13025`
-		// Minimum execution time: 55_327 nanoseconds.
-		Weight::from_parts(58_947_746, 0)
-			.saturating_add(Weight::from_parts(0, 13025))
-			// Standard Error: 1_589
-			.saturating_add(Weight::from_parts(40_696, 0).saturating_mul(s.into()))
+		//  Measured:  `1783`
+		//  Estimated: `18031`
+		// Minimum execution time: 54_752_000 picoseconds.
+		Weight::from_parts(56_248_171, 18031)
+			// Standard Error: 1_891
+			.saturating_add(Weight::from_parts(4_767, 0).saturating_mul(s.into()))
 			.saturating_add(T::DbWeight::get().reads(5_u64))
 			.saturating_add(T::DbWeight::get().writes(2_u64))
 	}
@@ -252,7 +258,7 @@ impl<T: frame_system::Config> WeightInfo for SubstrateWeight<T> {
 	/// Storage: Staking CurrentEra (r:1 w:0)
 	/// Proof: Staking CurrentEra (max_values: Some(1), max_size: Some(4), added: 499, mode: MaxEncodedLen)
 	/// Storage: NominationPools BondedPools (r:1 w:1)
-	/// Proof: NominationPools BondedPools (max_values: None, max_size: Some(164), added: 2639, mode: MaxEncodedLen)
+	/// Proof: NominationPools BondedPools (max_values: None, max_size: Some(220), added: 2695, mode: MaxEncodedLen)
 	/// Storage: NominationPools SubPoolsStorage (r:1 w:1)
 	/// Proof: NominationPools SubPoolsStorage (max_values: None, max_size: Some(24382), added: 26857, mode: MaxEncodedLen)
 	/// Storage: Staking Bonded (r:1 w:0)
@@ -265,25 +271,26 @@ impl<T: frame_system::Config> WeightInfo for SubstrateWeight<T> {
 	/// Proof: System Account (max_values: None, max_size: Some(128), added: 2603, mode: MaxEncodedLen)
 	/// Storage: NominationPools CounterForPoolMembers (r:1 w:1)
 	/// Proof: NominationPools CounterForPoolMembers (max_values: Some(1), max_size: Some(4), added: 499, mode: MaxEncodedLen)
+	/// Storage: NominationPools ClaimPermissions (r:0 w:1)
+	/// Proof: NominationPools ClaimPermissions (max_values: None, max_size: Some(41), added: 2516, mode: MaxEncodedLen)
 	/// The range of component `s` is `[0, 100]`.
 	fn withdraw_unbonded_update(s: u32, ) -> Weight {
 		// Proof Size summary in bytes:
-		//  Measured:  `2303`
-		//  Estimated: `45696`
-		// Minimum execution time: 105_923 nanoseconds.
-		Weight::from_parts(110_572_476, 0)
-			.saturating_add(Weight::from_parts(0, 45696))
-			// Standard Error: 2_438
-			.saturating_add(Weight::from_parts(69_045, 0).saturating_mul(s.into()))
+		//  Measured:  `2307`
+		//  Estimated: `54662`
+		// Minimum execution time: 106_166_000 picoseconds.
+		Weight::from_parts(107_806_373, 54662)
+			// Standard Error: 6_985
+			.saturating_add(Weight::from_parts(18_449, 0).saturating_mul(s.into()))
 			.saturating_add(T::DbWeight::get().reads(9_u64))
-			.saturating_add(T::DbWeight::get().writes(7_u64))
+			.saturating_add(T::DbWeight::get().writes(8_u64))
 	}
 	/// Storage: NominationPools PoolMembers (r:1 w:1)
 	/// Proof: NominationPools PoolMembers (max_values: None, max_size: Some(237), added: 2712, mode: MaxEncodedLen)
 	/// Storage: Staking CurrentEra (r:1 w:0)
 	/// Proof: Staking CurrentEra (max_values: Some(1), max_size: Some(4), added: 499, mode: MaxEncodedLen)
 	/// Storage: NominationPools BondedPools (r:1 w:1)
-	/// Proof: NominationPools BondedPools (max_values: None, max_size: Some(164), added: 2639, mode: MaxEncodedLen)
+	/// Proof: NominationPools BondedPools (max_values: None, max_size: Some(220), added: 2695, mode: MaxEncodedLen)
 	/// Storage: NominationPools SubPoolsStorage (r:1 w:1)
 	/// Proof: NominationPools SubPoolsStorage (max_values: None, max_size: Some(24382), added: 26857, mode: MaxEncodedLen)
 	/// Storage: Staking Bonded (r:1 w:1)
@@ -307,7 +314,7 @@ impl<T: frame_system::Config> WeightInfo for SubstrateWeight<T> {
 	/// Storage: NominationPools CounterForReversePoolIdLookup (r:1 w:1)
 	/// Proof: NominationPools CounterForReversePoolIdLookup (max_values: Some(1), max_size: Some(4), added: 499, mode: MaxEncodedLen)
 	/// Storage: NominationPools RewardPools (r:1 w:1)
-	/// Proof: NominationPools RewardPools (max_values: None, max_size: Some(60), added: 2535, mode: MaxEncodedLen)
+	/// Proof: NominationPools RewardPools (max_values: None, max_size: Some(92), added: 2567, mode: MaxEncodedLen)
 	/// Storage: NominationPools CounterForRewardPools (r:1 w:1)
 	/// Proof: NominationPools CounterForRewardPools (max_values: Some(1), max_size: Some(4), added: 499, mode: MaxEncodedLen)
 	/// Storage: NominationPools CounterForSubPoolsStorage (r:1 w:1)
@@ -318,16 +325,19 @@ impl<T: frame_system::Config> WeightInfo for SubstrateWeight<T> {
 	/// Proof: NominationPools CounterForBondedPools (max_values: Some(1), max_size: Some(4), added: 499, mode: MaxEncodedLen)
 	/// Storage: Staking Payee (r:0 w:1)
 	/// Proof: Staking Payee (max_values: None, max_size: Some(73), added: 2548, mode: MaxEncodedLen)
+	/// Storage: NominationPools ClaimPermissions (r:0 w:1)
+	/// Proof: NominationPools ClaimPermissions (max_values: None, max_size: Some(41), added: 2516, mode: MaxEncodedLen)
 	/// The range of component `s` is `[0, 100]`.
-	fn withdraw_unbonded_kill(_s: u32, ) -> Weight {
+	fn withdraw_unbonded_kill(s: u32, ) -> Weight {
 		// Proof Size summary in bytes:
-		//  Measured:  `2690`
-		//  Estimated: `68812`
-		// Minimum execution time: 169_700 nanoseconds.
-		Weight::from_parts(178_693_541, 0)
-			.saturating_add(Weight::from_parts(0, 68812))
+		//  Measured:  `2694`
+		//  Estimated: `87714`
+		// Minimum execution time: 170_047_000 picoseconds.
+		Weight::from_parts(172_125_770, 87714)
+			// Standard Error: 2_599
+			.saturating_add(Weight::from_parts(9_964, 0).saturating_mul(s.into()))
 			.saturating_add(T::DbWeight::get().reads(20_u64))
-			.saturating_add(T::DbWeight::get().writes(17_u64))
+			.saturating_add(T::DbWeight::get().writes(18_u64))
 	}
 	/// Storage: NominationPools LastPoolId (r:1 w:1)
 	/// Proof: NominationPools LastPoolId (max_values: Some(1), max_size: Some(4), added: 499, mode: MaxEncodedLen)
@@ -360,7 +370,7 @@ impl<T: frame_system::Config> WeightInfo for SubstrateWeight<T> {
 	/// Storage: Balances Locks (r:1 w:1)
 	/// Proof: Balances Locks (max_values: None, max_size: Some(1299), added: 3774, mode: MaxEncodedLen)
 	/// Storage: NominationPools RewardPools (r:1 w:1)
-	/// Proof: NominationPools RewardPools (max_values: None, max_size: Some(60), added: 2535, mode: MaxEncodedLen)
+	/// Proof: NominationPools RewardPools (max_values: None, max_size: Some(92), added: 2567, mode: MaxEncodedLen)
 	/// Storage: NominationPools CounterForRewardPools (r:1 w:1)
 	/// Proof: NominationPools CounterForRewardPools (max_values: Some(1), max_size: Some(4), added: 499, mode: MaxEncodedLen)
 	/// Storage: NominationPools ReversePoolIdLookup (r:1 w:1)
@@ -368,21 +378,20 @@ impl<T: frame_system::Config> WeightInfo for SubstrateWeight<T> {
 	/// Storage: NominationPools CounterForReversePoolIdLookup (r:1 w:1)
 	/// Proof: NominationPools CounterForReversePoolIdLookup (max_values: Some(1), max_size: Some(4), added: 499, mode: MaxEncodedLen)
 	/// Storage: NominationPools BondedPools (r:1 w:1)
-	/// Proof: NominationPools BondedPools (max_values: None, max_size: Some(164), added: 2639, mode: MaxEncodedLen)
+	/// Proof: NominationPools BondedPools (max_values: None, max_size: Some(220), added: 2695, mode: MaxEncodedLen)
 	/// Storage: Staking Payee (r:0 w:1)
 	/// Proof: Staking Payee (max_values: None, max_size: Some(73), added: 2548, mode: MaxEncodedLen)
 	fn create() -> Weight {
 		// Proof Size summary in bytes:
 		//  Measured:  `1321`
-		//  Estimated: `31522`
-		// Minimum execution time: 145_976 nanoseconds.
-		Weight::from_parts(150_664_000, 0)
-			.saturating_add(Weight::from_parts(0, 31522))
+		//  Estimated: `51410`
+		// Minimum execution time: 149_672_000 picoseconds.
+		Weight::from_parts(153_613_000, 51410)
 			.saturating_add(T::DbWeight::get().reads(21_u64))
 			.saturating_add(T::DbWeight::get().writes(15_u64))
 	}
 	/// Storage: NominationPools BondedPools (r:1 w:0)
-	/// Proof: NominationPools BondedPools (max_values: None, max_size: Some(164), added: 2639, mode: MaxEncodedLen)
+	/// Proof: NominationPools BondedPools (max_values: None, max_size: Some(220), added: 2695, mode: MaxEncodedLen)
 	/// Storage: Staking Bonded (r:1 w:0)
 	/// Proof: Staking Bonded (max_values: None, max_size: Some(72), added: 2547, mode: MaxEncodedLen)
 	/// Storage: Staking Ledger (r:1 w:0)
@@ -408,36 +417,34 @@ impl<T: frame_system::Config> WeightInfo for SubstrateWeight<T> {
 	/// The range of component `n` is `[1, 16]`.
 	fn nominate(n: u32, ) -> Weight {
 		// Proof Size summary in bytes:
-		//  Measured:  `1909`
-		//  Estimated: `21998 + n * (2520 ±0)`
-		// Minimum execution time: 69_288 nanoseconds.
-		Weight::from_parts(71_075_293, 0)
-			.saturating_add(Weight::from_parts(0, 21998))
-			// Standard Error: 10_508
-			.saturating_add(Weight::from_parts(1_384_674, 0).saturating_mul(n.into()))
+		//  Measured:  `1913`
+		//  Estimated: `33934 + n * (2520 ±0)`
+		// Minimum execution time: 68_892_000 picoseconds.
+		Weight::from_parts(69_062_946, 33934)
+			// Standard Error: 6_448
+			.saturating_add(Weight::from_parts(1_422_774, 0).saturating_mul(n.into()))
 			.saturating_add(T::DbWeight::get().reads(12_u64))
 			.saturating_add(T::DbWeight::get().reads((1_u64).saturating_mul(n.into())))
 			.saturating_add(T::DbWeight::get().writes(5_u64))
 			.saturating_add(Weight::from_parts(0, 2520).saturating_mul(n.into()))
 	}
 	/// Storage: NominationPools BondedPools (r:1 w:1)
-	/// Proof: NominationPools BondedPools (max_values: None, max_size: Some(164), added: 2639, mode: MaxEncodedLen)
+	/// Proof: NominationPools BondedPools (max_values: None, max_size: Some(220), added: 2695, mode: MaxEncodedLen)
 	/// Storage: Staking Bonded (r:1 w:0)
 	/// Proof: Staking Bonded (max_values: None, max_size: Some(72), added: 2547, mode: MaxEncodedLen)
 	/// Storage: Staking Ledger (r:1 w:0)
 	/// Proof: Staking Ledger (max_values: None, max_size: Some(1091), added: 3566, mode: MaxEncodedLen)
 	fn set_state() -> Weight {
 		// Proof Size summary in bytes:
-		//  Measured:  `1498`
-		//  Estimated: `8752`
-		// Minimum execution time: 36_410 nanoseconds.
-		Weight::from_parts(37_585_000, 0)
-			.saturating_add(Weight::from_parts(0, 8752))
+		//  Measured:  `1502`
+		//  Estimated: `11778`
+		// Minimum execution time: 36_447_000 picoseconds.
+		Weight::from_parts(36_837_000, 11778)
 			.saturating_add(T::DbWeight::get().reads(3_u64))
 			.saturating_add(T::DbWeight::get().writes(1_u64))
 	}
 	/// Storage: NominationPools BondedPools (r:1 w:0)
-	/// Proof: NominationPools BondedPools (max_values: None, max_size: Some(164), added: 2639, mode: MaxEncodedLen)
+	/// Proof: NominationPools BondedPools (max_values: None, max_size: Some(220), added: 2695, mode: MaxEncodedLen)
 	/// Storage: NominationPools Metadata (r:1 w:1)
 	/// Proof: NominationPools Metadata (max_values: None, max_size: Some(270), added: 2745, mode: MaxEncodedLen)
 	/// Storage: NominationPools CounterForMetadata (r:1 w:1)
@@ -445,13 +452,12 @@ impl<T: frame_system::Config> WeightInfo for SubstrateWeight<T> {
 	/// The range of component `n` is `[1, 256]`.
 	fn set_metadata(n: u32, ) -> Weight {
 		// Proof Size summary in bytes:
-		//  Measured:  `559`
-		//  Estimated: `5883`
-		// Minimum execution time: 14_322 nanoseconds.
-		Weight::from_parts(15_328_204, 0)
-			.saturating_add(Weight::from_parts(0, 5883))
-			// Standard Error: 161
-			.saturating_add(Weight::from_parts(1_406, 0).saturating_mul(n.into()))
+		//  Measured:  `563`
+		//  Estimated: `8909`
+		// Minimum execution time: 15_221_000 picoseconds.
+		Weight::from_parts(15_632_286, 8909)
+			// Standard Error: 343
+			.saturating_add(Weight::from_parts(2_299, 0).saturating_mul(n.into()))
 			.saturating_add(T::DbWeight::get().reads(3_u64))
 			.saturating_add(T::DbWeight::get().writes(2_u64))
 	}
@@ -463,31 +469,31 @@ impl<T: frame_system::Config> WeightInfo for SubstrateWeight<T> {
 	/// Proof: NominationPools MaxPoolMembersPerPool (max_values: Some(1), max_size: Some(4), added: 499, mode: MaxEncodedLen)
 	/// Storage: NominationPools MinCreateBond (r:0 w:1)
 	/// Proof: NominationPools MinCreateBond (max_values: Some(1), max_size: Some(16), added: 511, mode: MaxEncodedLen)
+	/// Storage: NominationPools GlobalMaxCommission (r:0 w:1)
+	/// Proof: NominationPools GlobalMaxCommission (max_values: Some(1), max_size: Some(4), added: 499, mode: MaxEncodedLen)
 	/// Storage: NominationPools MaxPools (r:0 w:1)
 	/// Proof: NominationPools MaxPools (max_values: Some(1), max_size: Some(4), added: 499, mode: MaxEncodedLen)
 	fn set_configs() -> Weight {
 		// Proof Size summary in bytes:
 		//  Measured:  `0`
 		//  Estimated: `0`
-		// Minimum execution time: 5_968 nanoseconds.
-		Weight::from_parts(6_245_000, 0)
-			.saturating_add(Weight::from_parts(0, 0))
-			.saturating_add(T::DbWeight::get().writes(5_u64))
+		// Minimum execution time: 7_409_000 picoseconds.
+		Weight::from_parts(7_702_000, 0)
+			.saturating_add(T::DbWeight::get().writes(6_u64))
 	}
 	/// Storage: NominationPools BondedPools (r:1 w:1)
-	/// Proof: NominationPools BondedPools (max_values: None, max_size: Some(164), added: 2639, mode: MaxEncodedLen)
+	/// Proof: NominationPools BondedPools (max_values: None, max_size: Some(220), added: 2695, mode: MaxEncodedLen)
 	fn update_roles() -> Weight {
 		// Proof Size summary in bytes:
-		//  Measured:  `559`
-		//  Estimated: `2639`
-		// Minimum execution time: 18_979 nanoseconds.
-		Weight::from_parts(19_795_000, 0)
-			.saturating_add(Weight::from_parts(0, 2639))
+		//  Measured:  `563`
+		//  Estimated: `3685`
+		// Minimum execution time: 20_451_000 picoseconds.
+		Weight::from_parts(20_703_000, 3685)
 			.saturating_add(T::DbWeight::get().reads(1_u64))
 			.saturating_add(T::DbWeight::get().writes(1_u64))
 	}
 	/// Storage: NominationPools BondedPools (r:1 w:0)
-	/// Proof: NominationPools BondedPools (max_values: None, max_size: Some(164), added: 2639, mode: MaxEncodedLen)
+	/// Proof: NominationPools BondedPools (max_values: None, max_size: Some(220), added: 2695, mode: MaxEncodedLen)
 	/// Storage: Staking Bonded (r:1 w:0)
 	/// Proof: Staking Bonded (max_values: None, max_size: Some(72), added: 2547, mode: MaxEncodedLen)
 	/// Storage: Staking Ledger (r:1 w:0)
@@ -506,14 +512,52 @@ impl<T: frame_system::Config> WeightInfo for SubstrateWeight<T> {
 	/// Proof: VoterList CounterForListNodes (max_values: Some(1), max_size: Some(4), added: 499, mode: MaxEncodedLen)
 	fn chill() -> Weight {
 		// Proof Size summary in bytes:
-		//  Measured:  `2136`
-		//  Estimated: `20489`
-		// Minimum execution time: 68_145 nanoseconds.
-		Weight::from_parts(70_444_000, 0)
-			.saturating_add(Weight::from_parts(0, 20489))
+		//  Measured:  `2140`
+		//  Estimated: `29455`
+		// Minimum execution time: 66_001_000 picoseconds.
+		Weight::from_parts(66_894_000, 29455)
 			.saturating_add(T::DbWeight::get().reads(9_u64))
 			.saturating_add(T::DbWeight::get().writes(5_u64))
 	}
+	/// Storage: NominationPools BondedPools (r:1 w:1)
+	/// Proof: NominationPools BondedPools (max_values: None, max_size: Some(220), added: 2695, mode: MaxEncodedLen)
+	/// Storage: NominationPools RewardPools (r:1 w:1)
+	/// Proof: NominationPools RewardPools (max_values: None, max_size: Some(92), added: 2567, mode: MaxEncodedLen)
+	/// Storage: NominationPools GlobalMaxCommission (r:1 w:0)
+	/// Proof: NominationPools GlobalMaxCommission (max_values: Some(1), max_size: Some(4), added: 499, mode: MaxEncodedLen)
+	/// Storage: System Account (r:1 w:0)
+	/// Proof: System Account (max_values: None, max_size: Some(128), added: 2603, mode: MaxEncodedLen)
+	fn set_commission() -> Weight {
+		// Proof Size summary in bytes:
+		//  Measured:  `866`
+		//  Estimated: `12324`
+		// Minimum execution time: 34_011_000 picoseconds.
+		Weight::from_parts(34_521_000, 12324)
+			.saturating_add(T::DbWeight::get().reads(4_u64))
+			.saturating_add(T::DbWeight::get().writes(2_u64))
+	}
+	/// Storage: NominationPools BondedPools (r:1 w:1)
+	/// Proof: NominationPools BondedPools (max_values: None, max_size: Some(220), added: 2695, mode: MaxEncodedLen)
+	fn set_commission_max() -> Weight {
+		// Proof Size summary in bytes:
+		//  Measured:  `603`
+		//  Estimated: `3685`
+		// Minimum execution time: 19_524_000 picoseconds.
+		Weight::from_parts(19_855_000, 3685)
+			.saturating_add(T::DbWeight::get().reads(1_u64))
+			.saturating_add(T::DbWeight::get().writes(1_u64))
+	}
+	/// Storage: NominationPools BondedPools (r:1 w:1)
+	/// Proof: NominationPools BondedPools (max_values: None, max_size: Some(220), added: 2695, mode: MaxEncodedLen)
+	fn set_commission_change_rate() -> Weight {
+		// Proof Size summary in bytes:
+		//  Measured:  `563`
+		//  Estimated: `3685`
+		// Minimum execution time: 20_457_000 picoseconds.
+		Weight::from_parts(20_698_000, 3685)
+			.saturating_add(T::DbWeight::get().reads(1_u64))
+			.saturating_add(T::DbWeight::get().writes(1_u64))
+	}
 	/// Storage: NominationPools PoolMembers (r:1 w:0)
 	/// Proof: NominationPools PoolMembers (max_values: None, max_size: Some(237), added: 2712, mode: MaxEncodedLen)
 	/// Storage: NominationPools ClaimPermissions (r:1 w:1)
@@ -521,13 +565,29 @@ impl<T: frame_system::Config> WeightInfo for SubstrateWeight<T> {
 	fn set_claim_permission() -> Weight {
 		// Proof Size summary in bytes:
 		//  Measured:  `542`
-		//  Estimated: `5228`
-		// Minimum execution time: 15_112 nanoseconds.
-		Weight::from_parts(15_897_000, 0)
-			.saturating_add(Weight::from_parts(0, 5228))
+		//  Estimated: `7208`
+		// Minimum execution time: 15_183_000 picoseconds.
+		Weight::from_parts(15_597_000, 7208)
 			.saturating_add(T::DbWeight::get().reads(2_u64))
 			.saturating_add(T::DbWeight::get().writes(1_u64))
 	}
+	/// Storage: NominationPools BondedPools (r:1 w:0)
+	/// Proof: NominationPools BondedPools (max_values: None, max_size: Some(220), added: 2695, mode: MaxEncodedLen)
+	/// Storage: NominationPools RewardPools (r:1 w:1)
+	/// Proof: NominationPools RewardPools (max_values: None, max_size: Some(92), added: 2567, mode: MaxEncodedLen)
+	/// Storage: NominationPools GlobalMaxCommission (r:1 w:0)
+	/// Proof: NominationPools GlobalMaxCommission (max_values: Some(1), max_size: Some(4), added: 499, mode: MaxEncodedLen)
+	/// Storage: System Account (r:1 w:1)
+	/// Proof: System Account (max_values: None, max_size: Some(128), added: 2603, mode: MaxEncodedLen)
+	fn claim_commission() -> Weight {
+		// Proof Size summary in bytes:
+		//  Measured:  `1096`
+		//  Estimated: `12324`
+		// Minimum execution time: 48_957_000 picoseconds.
+		Weight::from_parts(50_207_000, 12324)
+			.saturating_add(T::DbWeight::get().reads(4_u64))
+			.saturating_add(T::DbWeight::get().writes(2_u64))
+	}
 }
 
 // For backwards compatibility and tests
@@ -537,13 +597,15 @@ impl WeightInfo for () {
 	/// Storage: NominationPools PoolMembers (r:1 w:1)
 	/// Proof: NominationPools PoolMembers (max_values: None, max_size: Some(237), added: 2712, mode: MaxEncodedLen)
 	/// Storage: NominationPools BondedPools (r:1 w:1)
-	/// Proof: NominationPools BondedPools (max_values: None, max_size: Some(164), added: 2639, mode: MaxEncodedLen)
+	/// Proof: NominationPools BondedPools (max_values: None, max_size: Some(220), added: 2695, mode: MaxEncodedLen)
 	/// Storage: Staking Bonded (r:1 w:0)
 	/// Proof: Staking Bonded (max_values: None, max_size: Some(72), added: 2547, mode: MaxEncodedLen)
 	/// Storage: Staking Ledger (r:1 w:1)
 	/// Proof: Staking Ledger (max_values: None, max_size: Some(1091), added: 3566, mode: MaxEncodedLen)
 	/// Storage: NominationPools RewardPools (r:1 w:1)
-	/// Proof: NominationPools RewardPools (max_values: None, max_size: Some(60), added: 2535, mode: MaxEncodedLen)
+	/// Proof: NominationPools RewardPools (max_values: None, max_size: Some(92), added: 2567, mode: MaxEncodedLen)
+	/// Storage: NominationPools GlobalMaxCommission (r:1 w:0)
+	/// Proof: NominationPools GlobalMaxCommission (max_values: Some(1), max_size: Some(4), added: 499, mode: MaxEncodedLen)
 	/// Storage: System Account (r:2 w:1)
 	/// Proof: System Account (max_values: None, max_size: Some(128), added: 2603, mode: MaxEncodedLen)
 	/// Storage: NominationPools MaxPoolMembersPerPool (r:1 w:0)
@@ -560,20 +622,21 @@ impl WeightInfo for () {
 	/// Proof: VoterList ListBags (max_values: None, max_size: Some(82), added: 2557, mode: MaxEncodedLen)
 	fn join() -> Weight {
 		// Proof Size summary in bytes:
-		//  Measured:  `3573`
-		//  Estimated: `37988`
-		// Minimum execution time: 169_857 nanoseconds.
-		Weight::from_parts(173_895_000, 0)
-			.saturating_add(Weight::from_parts(0, 37988))
-			.saturating_add(RocksDbWeight::get().reads(17_u64))
+		//  Measured:  `3650`
+		//  Estimated: `52435`
+		// Minimum execution time: 160_401_000 picoseconds.
+		Weight::from_parts(161_798_000, 52435)
+			.saturating_add(RocksDbWeight::get().reads(18_u64))
 			.saturating_add(RocksDbWeight::get().writes(12_u64))
 	}
 	/// Storage: NominationPools PoolMembers (r:1 w:1)
 	/// Proof: NominationPools PoolMembers (max_values: None, max_size: Some(237), added: 2712, mode: MaxEncodedLen)
 	/// Storage: NominationPools BondedPools (r:1 w:1)
-	/// Proof: NominationPools BondedPools (max_values: None, max_size: Some(164), added: 2639, mode: MaxEncodedLen)
+	/// Proof: NominationPools BondedPools (max_values: None, max_size: Some(220), added: 2695, mode: MaxEncodedLen)
 	/// Storage: NominationPools RewardPools (r:1 w:1)
-	/// Proof: NominationPools RewardPools (max_values: None, max_size: Some(60), added: 2535, mode: MaxEncodedLen)
+	/// Proof: NominationPools RewardPools (max_values: None, max_size: Some(92), added: 2567, mode: MaxEncodedLen)
+	/// Storage: NominationPools GlobalMaxCommission (r:1 w:0)
+	/// Proof: NominationPools GlobalMaxCommission (max_values: Some(1), max_size: Some(4), added: 499, mode: MaxEncodedLen)
 	/// Storage: System Account (r:3 w:2)
 	/// Proof: System Account (max_values: None, max_size: Some(128), added: 2603, mode: MaxEncodedLen)
 	/// Storage: Staking Bonded (r:1 w:0)
@@ -588,12 +651,11 @@ impl WeightInfo for () {
 	/// Proof: VoterList ListBags (max_values: None, max_size: Some(82), added: 2557, mode: MaxEncodedLen)
 	fn bond_extra_transfer() -> Weight {
 		// Proof Size summary in bytes:
-		//  Measured:  `3615`
-		//  Estimated: `38583`
-		// Minimum execution time: 167_372 nanoseconds.
-		Weight::from_parts(168_776_000, 0)
-			.saturating_add(Weight::from_parts(0, 38583))
-			.saturating_add(RocksDbWeight::get().reads(14_u64))
+		//  Measured:  `3692`
+		//  Estimated: `49070`
+		// Minimum execution time: 157_668_000 picoseconds.
+		Weight::from_parts(161_129_000, 49070)
+			.saturating_add(RocksDbWeight::get().reads(15_u64))
 			.saturating_add(RocksDbWeight::get().writes(12_u64))
 	}
 	/// Storage: NominationPools ClaimPermissions (r:1 w:0)
@@ -601,9 +663,11 @@ impl WeightInfo for () {
 	/// Storage: NominationPools PoolMembers (r:1 w:1)
 	/// Proof: NominationPools PoolMembers (max_values: None, max_size: Some(237), added: 2712, mode: MaxEncodedLen)
 	/// Storage: NominationPools BondedPools (r:1 w:1)
-	/// Proof: NominationPools BondedPools (max_values: None, max_size: Some(164), added: 2639, mode: MaxEncodedLen)
+	/// Proof: NominationPools BondedPools (max_values: None, max_size: Some(220), added: 2695, mode: MaxEncodedLen)
 	/// Storage: NominationPools RewardPools (r:1 w:1)
-	/// Proof: NominationPools RewardPools (max_values: None, max_size: Some(60), added: 2535, mode: MaxEncodedLen)
+	/// Proof: NominationPools RewardPools (max_values: None, max_size: Some(92), added: 2567, mode: MaxEncodedLen)
+	/// Storage: NominationPools GlobalMaxCommission (r:1 w:0)
+	/// Proof: NominationPools GlobalMaxCommission (max_values: Some(1), max_size: Some(4), added: 499, mode: MaxEncodedLen)
 	/// Storage: System Account (r:3 w:3)
 	/// Proof: System Account (max_values: None, max_size: Some(128), added: 2603, mode: MaxEncodedLen)
 	/// Storage: Staking Bonded (r:1 w:0)
@@ -618,12 +682,11 @@ impl WeightInfo for () {
 	/// Proof: VoterList ListBags (max_values: None, max_size: Some(82), added: 2557, mode: MaxEncodedLen)
 	fn bond_extra_other() -> Weight {
 		// Proof Size summary in bytes:
-		//  Measured:  `3680`
-		//  Estimated: `41099`
-		// Minimum execution time: 186_346 nanoseconds.
-		Weight::from_parts(191_308_000, 0)
-			.saturating_add(Weight::from_parts(0, 41099))
-			.saturating_add(RocksDbWeight::get().reads(15_u64))
+		//  Measured:  `3757`
+		//  Estimated: `52576`
+		// Minimum execution time: 176_034_000 picoseconds.
+		Weight::from_parts(176_956_000, 52576)
+			.saturating_add(RocksDbWeight::get().reads(16_u64))
 			.saturating_add(RocksDbWeight::get().writes(13_u64))
 	}
 	/// Storage: NominationPools ClaimPermissions (r:1 w:0)
@@ -631,31 +694,34 @@ impl WeightInfo for () {
 	/// Storage: NominationPools PoolMembers (r:1 w:1)
 	/// Proof: NominationPools PoolMembers (max_values: None, max_size: Some(237), added: 2712, mode: MaxEncodedLen)
 	/// Storage: NominationPools BondedPools (r:1 w:1)
-	/// Proof: NominationPools BondedPools (max_values: None, max_size: Some(164), added: 2639, mode: MaxEncodedLen)
+	/// Proof: NominationPools BondedPools (max_values: None, max_size: Some(220), added: 2695, mode: MaxEncodedLen)
 	/// Storage: NominationPools RewardPools (r:1 w:1)
-	/// Proof: NominationPools RewardPools (max_values: None, max_size: Some(60), added: 2535, mode: MaxEncodedLen)
+	/// Proof: NominationPools RewardPools (max_values: None, max_size: Some(92), added: 2567, mode: MaxEncodedLen)
+	/// Storage: NominationPools GlobalMaxCommission (r:1 w:0)
+	/// Proof: NominationPools GlobalMaxCommission (max_values: Some(1), max_size: Some(4), added: 499, mode: MaxEncodedLen)
 	/// Storage: System Account (r:1 w:1)
 	/// Proof: System Account (max_values: None, max_size: Some(128), added: 2603, mode: MaxEncodedLen)
 	fn claim_payout() -> Weight {
 		// Proof Size summary in bytes:
-		//  Measured:  `1254`
-		//  Estimated: `13005`
-		// Minimum execution time: 61_423 nanoseconds.
-		Weight::from_parts(63_219_000, 0)
-			.saturating_add(Weight::from_parts(0, 13005))
-			.saturating_add(RocksDbWeight::get().reads(5_u64))
+		//  Measured:  `1331`
+		//  Estimated: `19532`
+		// Minimum execution time: 61_551_000 picoseconds.
+		Weight::from_parts(62_201_000, 19532)
+			.saturating_add(RocksDbWeight::get().reads(6_u64))
 			.saturating_add(RocksDbWeight::get().writes(4_u64))
 	}
 	/// Storage: NominationPools PoolMembers (r:1 w:1)
 	/// Proof: NominationPools PoolMembers (max_values: None, max_size: Some(237), added: 2712, mode: MaxEncodedLen)
 	/// Storage: NominationPools BondedPools (r:1 w:1)
-	/// Proof: NominationPools BondedPools (max_values: None, max_size: Some(164), added: 2639, mode: MaxEncodedLen)
+	/// Proof: NominationPools BondedPools (max_values: None, max_size: Some(220), added: 2695, mode: MaxEncodedLen)
 	/// Storage: NominationPools RewardPools (r:1 w:1)
-	/// Proof: NominationPools RewardPools (max_values: None, max_size: Some(60), added: 2535, mode: MaxEncodedLen)
+	/// Proof: NominationPools RewardPools (max_values: None, max_size: Some(92), added: 2567, mode: MaxEncodedLen)
 	/// Storage: Staking Bonded (r:1 w:0)
 	/// Proof: Staking Bonded (max_values: None, max_size: Some(72), added: 2547, mode: MaxEncodedLen)
 	/// Storage: Staking Ledger (r:1 w:1)
 	/// Proof: Staking Ledger (max_values: None, max_size: Some(1091), added: 3566, mode: MaxEncodedLen)
+	/// Storage: NominationPools GlobalMaxCommission (r:1 w:0)
+	/// Proof: NominationPools GlobalMaxCommission (max_values: Some(1), max_size: Some(4), added: 499, mode: MaxEncodedLen)
 	/// Storage: System Account (r:2 w:1)
 	/// Proof: System Account (max_values: None, max_size: Some(128), added: 2603, mode: MaxEncodedLen)
 	/// Storage: Staking CurrentEra (r:1 w:0)
@@ -674,20 +740,17 @@ impl WeightInfo for () {
 	/// Proof: NominationPools SubPoolsStorage (max_values: None, max_size: Some(24382), added: 26857, mode: MaxEncodedLen)
 	/// Storage: NominationPools CounterForSubPoolsStorage (r:1 w:1)
 	/// Proof: NominationPools CounterForSubPoolsStorage (max_values: Some(1), max_size: Some(4), added: 499, mode: MaxEncodedLen)
-	/// Storage: NominationPools ClaimPermissions (r:0 w:1)
-	/// Proof: NominationPools ClaimPermissions (max_values: None, max_size: Some(41), added: 2516, mode: MaxEncodedLen)
 	fn unbond() -> Weight {
 		// Proof Size summary in bytes:
-		//  Measured:  `3858`
-		//  Estimated: `67379`
-		// Minimum execution time: 174_532 nanoseconds.
-		Weight::from_parts(180_032_000, 0)
-			.saturating_add(Weight::from_parts(0, 67379))
-			.saturating_add(RocksDbWeight::get().reads(18_u64))
-			.saturating_add(RocksDbWeight::get().writes(14_u64))
+		//  Measured:  `3935`
+		//  Estimated: `82816`
+		// Minimum execution time: 162_755_000 picoseconds.
+		Weight::from_parts(163_518_000, 82816)
+			.saturating_add(RocksDbWeight::get().reads(19_u64))
+			.saturating_add(RocksDbWeight::get().writes(13_u64))
 	}
 	/// Storage: NominationPools BondedPools (r:1 w:0)
-	/// Proof: NominationPools BondedPools (max_values: None, max_size: Some(164), added: 2639, mode: MaxEncodedLen)
+	/// Proof: NominationPools BondedPools (max_values: None, max_size: Some(220), added: 2695, mode: MaxEncodedLen)
 	/// Storage: Staking Bonded (r:1 w:0)
 	/// Proof: Staking Bonded (max_values: None, max_size: Some(72), added: 2547, mode: MaxEncodedLen)
 	/// Storage: Staking Ledger (r:1 w:1)
@@ -699,13 +762,12 @@ impl WeightInfo for () {
 	/// The range of component `s` is `[0, 100]`.
 	fn pool_withdraw_unbonded(s: u32, ) -> Weight {
 		// Proof Size summary in bytes:
-		//  Measured:  `1779`
-		//  Estimated: `13025`
-		// Minimum execution time: 55_327 nanoseconds.
-		Weight::from_parts(58_947_746, 0)
-			.saturating_add(Weight::from_parts(0, 13025))
-			// Standard Error: 1_589
-			.saturating_add(Weight::from_parts(40_696, 0).saturating_mul(s.into()))
+		//  Measured:  `1783`
+		//  Estimated: `18031`
+		// Minimum execution time: 54_752_000 picoseconds.
+		Weight::from_parts(56_248_171, 18031)
+			// Standard Error: 1_891
+			.saturating_add(Weight::from_parts(4_767, 0).saturating_mul(s.into()))
 			.saturating_add(RocksDbWeight::get().reads(5_u64))
 			.saturating_add(RocksDbWeight::get().writes(2_u64))
 	}
@@ -714,7 +776,7 @@ impl WeightInfo for () {
 	/// Storage: Staking CurrentEra (r:1 w:0)
 	/// Proof: Staking CurrentEra (max_values: Some(1), max_size: Some(4), added: 499, mode: MaxEncodedLen)
 	/// Storage: NominationPools BondedPools (r:1 w:1)
-	/// Proof: NominationPools BondedPools (max_values: None, max_size: Some(164), added: 2639, mode: MaxEncodedLen)
+	/// Proof: NominationPools BondedPools (max_values: None, max_size: Some(220), added: 2695, mode: MaxEncodedLen)
 	/// Storage: NominationPools SubPoolsStorage (r:1 w:1)
 	/// Proof: NominationPools SubPoolsStorage (max_values: None, max_size: Some(24382), added: 26857, mode: MaxEncodedLen)
 	/// Storage: Staking Bonded (r:1 w:0)
@@ -727,25 +789,26 @@ impl WeightInfo for () {
 	/// Proof: System Account (max_values: None, max_size: Some(128), added: 2603, mode: MaxEncodedLen)
 	/// Storage: NominationPools CounterForPoolMembers (r:1 w:1)
 	/// Proof: NominationPools CounterForPoolMembers (max_values: Some(1), max_size: Some(4), added: 499, mode: MaxEncodedLen)
+	/// Storage: NominationPools ClaimPermissions (r:0 w:1)
+	/// Proof: NominationPools ClaimPermissions (max_values: None, max_size: Some(41), added: 2516, mode: MaxEncodedLen)
 	/// The range of component `s` is `[0, 100]`.
 	fn withdraw_unbonded_update(s: u32, ) -> Weight {
 		// Proof Size summary in bytes:
-		//  Measured:  `2303`
-		//  Estimated: `45696`
-		// Minimum execution time: 105_923 nanoseconds.
-		Weight::from_parts(110_572_476, 0)
-			.saturating_add(Weight::from_parts(0, 45696))
-			// Standard Error: 2_438
-			.saturating_add(Weight::from_parts(69_045, 0).saturating_mul(s.into()))
+		//  Measured:  `2307`
+		//  Estimated: `54662`
+		// Minimum execution time: 106_166_000 picoseconds.
+		Weight::from_parts(107_806_373, 54662)
+			// Standard Error: 6_985
+			.saturating_add(Weight::from_parts(18_449, 0).saturating_mul(s.into()))
 			.saturating_add(RocksDbWeight::get().reads(9_u64))
-			.saturating_add(RocksDbWeight::get().writes(7_u64))
+			.saturating_add(RocksDbWeight::get().writes(8_u64))
 	}
 	/// Storage: NominationPools PoolMembers (r:1 w:1)
 	/// Proof: NominationPools PoolMembers (max_values: None, max_size: Some(237), added: 2712, mode: MaxEncodedLen)
 	/// Storage: Staking CurrentEra (r:1 w:0)
 	/// Proof: Staking CurrentEra (max_values: Some(1), max_size: Some(4), added: 499, mode: MaxEncodedLen)
 	/// Storage: NominationPools BondedPools (r:1 w:1)
-	/// Proof: NominationPools BondedPools (max_values: None, max_size: Some(164), added: 2639, mode: MaxEncodedLen)
+	/// Proof: NominationPools BondedPools (max_values: None, max_size: Some(220), added: 2695, mode: MaxEncodedLen)
 	/// Storage: NominationPools SubPoolsStorage (r:1 w:1)
 	/// Proof: NominationPools SubPoolsStorage (max_values: None, max_size: Some(24382), added: 26857, mode: MaxEncodedLen)
 	/// Storage: Staking Bonded (r:1 w:1)
@@ -769,7 +832,7 @@ impl WeightInfo for () {
 	/// Storage: NominationPools CounterForReversePoolIdLookup (r:1 w:1)
 	/// Proof: NominationPools CounterForReversePoolIdLookup (max_values: Some(1), max_size: Some(4), added: 499, mode: MaxEncodedLen)
 	/// Storage: NominationPools RewardPools (r:1 w:1)
-	/// Proof: NominationPools RewardPools (max_values: None, max_size: Some(60), added: 2535, mode: MaxEncodedLen)
+	/// Proof: NominationPools RewardPools (max_values: None, max_size: Some(92), added: 2567, mode: MaxEncodedLen)
 	/// Storage: NominationPools CounterForRewardPools (r:1 w:1)
 	/// Proof: NominationPools CounterForRewardPools (max_values: Some(1), max_size: Some(4), added: 499, mode: MaxEncodedLen)
 	/// Storage: NominationPools CounterForSubPoolsStorage (r:1 w:1)
@@ -780,16 +843,19 @@ impl WeightInfo for () {
 	/// Proof: NominationPools CounterForBondedPools (max_values: Some(1), max_size: Some(4), added: 499, mode: MaxEncodedLen)
 	/// Storage: Staking Payee (r:0 w:1)
 	/// Proof: Staking Payee (max_values: None, max_size: Some(73), added: 2548, mode: MaxEncodedLen)
+	/// Storage: NominationPools ClaimPermissions (r:0 w:1)
+	/// Proof: NominationPools ClaimPermissions (max_values: None, max_size: Some(41), added: 2516, mode: MaxEncodedLen)
 	/// The range of component `s` is `[0, 100]`.
-	fn withdraw_unbonded_kill(_s: u32, ) -> Weight {
+	fn withdraw_unbonded_kill(s: u32, ) -> Weight {
 		// Proof Size summary in bytes:
-		//  Measured:  `2690`
-		//  Estimated: `68812`
-		// Minimum execution time: 169_700 nanoseconds.
-		Weight::from_parts(178_693_541, 0)
-			.saturating_add(Weight::from_parts(0, 68812))
+		//  Measured:  `2694`
+		//  Estimated: `87714`
+		// Minimum execution time: 170_047_000 picoseconds.
+		Weight::from_parts(172_125_770, 87714)
+			// Standard Error: 2_599
+			.saturating_add(Weight::from_parts(9_964, 0).saturating_mul(s.into()))
 			.saturating_add(RocksDbWeight::get().reads(20_u64))
-			.saturating_add(RocksDbWeight::get().writes(17_u64))
+			.saturating_add(RocksDbWeight::get().writes(18_u64))
 	}
 	/// Storage: NominationPools LastPoolId (r:1 w:1)
 	/// Proof: NominationPools LastPoolId (max_values: Some(1), max_size: Some(4), added: 499, mode: MaxEncodedLen)
@@ -822,7 +888,7 @@ impl WeightInfo for () {
 	/// Storage: Balances Locks (r:1 w:1)
 	/// Proof: Balances Locks (max_values: None, max_size: Some(1299), added: 3774, mode: MaxEncodedLen)
 	/// Storage: NominationPools RewardPools (r:1 w:1)
-	/// Proof: NominationPools RewardPools (max_values: None, max_size: Some(60), added: 2535, mode: MaxEncodedLen)
+	/// Proof: NominationPools RewardPools (max_values: None, max_size: Some(92), added: 2567, mode: MaxEncodedLen)
 	/// Storage: NominationPools CounterForRewardPools (r:1 w:1)
 	/// Proof: NominationPools CounterForRewardPools (max_values: Some(1), max_size: Some(4), added: 499, mode: MaxEncodedLen)
 	/// Storage: NominationPools ReversePoolIdLookup (r:1 w:1)
@@ -830,21 +896,20 @@ impl WeightInfo for () {
 	/// Storage: NominationPools CounterForReversePoolIdLookup (r:1 w:1)
 	/// Proof: NominationPools CounterForReversePoolIdLookup (max_values: Some(1), max_size: Some(4), added: 499, mode: MaxEncodedLen)
 	/// Storage: NominationPools BondedPools (r:1 w:1)
-	/// Proof: NominationPools BondedPools (max_values: None, max_size: Some(164), added: 2639, mode: MaxEncodedLen)
+	/// Proof: NominationPools BondedPools (max_values: None, max_size: Some(220), added: 2695, mode: MaxEncodedLen)
 	/// Storage: Staking Payee (r:0 w:1)
 	/// Proof: Staking Payee (max_values: None, max_size: Some(73), added: 2548, mode: MaxEncodedLen)
 	fn create() -> Weight {
 		// Proof Size summary in bytes:
 		//  Measured:  `1321`
-		//  Estimated: `31522`
-		// Minimum execution time: 145_976 nanoseconds.
-		Weight::from_parts(150_664_000, 0)
-			.saturating_add(Weight::from_parts(0, 31522))
+		//  Estimated: `51410`
+		// Minimum execution time: 149_672_000 picoseconds.
+		Weight::from_parts(153_613_000, 51410)
 			.saturating_add(RocksDbWeight::get().reads(21_u64))
 			.saturating_add(RocksDbWeight::get().writes(15_u64))
 	}
 	/// Storage: NominationPools BondedPools (r:1 w:0)
-	/// Proof: NominationPools BondedPools (max_values: None, max_size: Some(164), added: 2639, mode: MaxEncodedLen)
+	/// Proof: NominationPools BondedPools (max_values: None, max_size: Some(220), added: 2695, mode: MaxEncodedLen)
 	/// Storage: Staking Bonded (r:1 w:0)
 	/// Proof: Staking Bonded (max_values: None, max_size: Some(72), added: 2547, mode: MaxEncodedLen)
 	/// Storage: Staking Ledger (r:1 w:0)
@@ -870,36 +935,34 @@ impl WeightInfo for () {
 	/// The range of component `n` is `[1, 16]`.
 	fn nominate(n: u32, ) -> Weight {
 		// Proof Size summary in bytes:
-		//  Measured:  `1909`
-		//  Estimated: `21998 + n * (2520 ±0)`
-		// Minimum execution time: 69_288 nanoseconds.
-		Weight::from_parts(71_075_293, 0)
-			.saturating_add(Weight::from_parts(0, 21998))
-			// Standard Error: 10_508
-			.saturating_add(Weight::from_parts(1_384_674, 0).saturating_mul(n.into()))
+		//  Measured:  `1913`
+		//  Estimated: `33934 + n * (2520 ±0)`
+		// Minimum execution time: 68_892_000 picoseconds.
+		Weight::from_parts(69_062_946, 33934)
+			// Standard Error: 6_448
+			.saturating_add(Weight::from_parts(1_422_774, 0).saturating_mul(n.into()))
 			.saturating_add(RocksDbWeight::get().reads(12_u64))
 			.saturating_add(RocksDbWeight::get().reads((1_u64).saturating_mul(n.into())))
 			.saturating_add(RocksDbWeight::get().writes(5_u64))
 			.saturating_add(Weight::from_parts(0, 2520).saturating_mul(n.into()))
 	}
 	/// Storage: NominationPools BondedPools (r:1 w:1)
-	/// Proof: NominationPools BondedPools (max_values: None, max_size: Some(164), added: 2639, mode: MaxEncodedLen)
+	/// Proof: NominationPools BondedPools (max_values: None, max_size: Some(220), added: 2695, mode: MaxEncodedLen)
 	/// Storage: Staking Bonded (r:1 w:0)
 	/// Proof: Staking Bonded (max_values: None, max_size: Some(72), added: 2547, mode: MaxEncodedLen)
 	/// Storage: Staking Ledger (r:1 w:0)
 	/// Proof: Staking Ledger (max_values: None, max_size: Some(1091), added: 3566, mode: MaxEncodedLen)
 	fn set_state() -> Weight {
 		// Proof Size summary in bytes:
-		//  Measured:  `1498`
-		//  Estimated: `8752`
-		// Minimum execution time: 36_410 nanoseconds.
-		Weight::from_parts(37_585_000, 0)
-			.saturating_add(Weight::from_parts(0, 8752))
+		//  Measured:  `1502`
+		//  Estimated: `11778`
+		// Minimum execution time: 36_447_000 picoseconds.
+		Weight::from_parts(36_837_000, 11778)
 			.saturating_add(RocksDbWeight::get().reads(3_u64))
 			.saturating_add(RocksDbWeight::get().writes(1_u64))
 	}
 	/// Storage: NominationPools BondedPools (r:1 w:0)
-	/// Proof: NominationPools BondedPools (max_values: None, max_size: Some(164), added: 2639, mode: MaxEncodedLen)
+	/// Proof: NominationPools BondedPools (max_values: None, max_size: Some(220), added: 2695, mode: MaxEncodedLen)
 	/// Storage: NominationPools Metadata (r:1 w:1)
 	/// Proof: NominationPools Metadata (max_values: None, max_size: Some(270), added: 2745, mode: MaxEncodedLen)
 	/// Storage: NominationPools CounterForMetadata (r:1 w:1)
@@ -907,13 +970,12 @@ impl WeightInfo for () {
 	/// The range of component `n` is `[1, 256]`.
 	fn set_metadata(n: u32, ) -> Weight {
 		// Proof Size summary in bytes:
-		//  Measured:  `559`
-		//  Estimated: `5883`
-		// Minimum execution time: 14_322 nanoseconds.
-		Weight::from_parts(15_328_204, 0)
-			.saturating_add(Weight::from_parts(0, 5883))
-			// Standard Error: 161
-			.saturating_add(Weight::from_parts(1_406, 0).saturating_mul(n.into()))
+		//  Measured:  `563`
+		//  Estimated: `8909`
+		// Minimum execution time: 15_221_000 picoseconds.
+		Weight::from_parts(15_632_286, 8909)
+			// Standard Error: 343
+			.saturating_add(Weight::from_parts(2_299, 0).saturating_mul(n.into()))
 			.saturating_add(RocksDbWeight::get().reads(3_u64))
 			.saturating_add(RocksDbWeight::get().writes(2_u64))
 	}
@@ -925,31 +987,31 @@ impl WeightInfo for () {
 	/// Proof: NominationPools MaxPoolMembersPerPool (max_values: Some(1), max_size: Some(4), added: 499, mode: MaxEncodedLen)
 	/// Storage: NominationPools MinCreateBond (r:0 w:1)
 	/// Proof: NominationPools MinCreateBond (max_values: Some(1), max_size: Some(16), added: 511, mode: MaxEncodedLen)
+	/// Storage: NominationPools GlobalMaxCommission (r:0 w:1)
+	/// Proof: NominationPools GlobalMaxCommission (max_values: Some(1), max_size: Some(4), added: 499, mode: MaxEncodedLen)
 	/// Storage: NominationPools MaxPools (r:0 w:1)
 	/// Proof: NominationPools MaxPools (max_values: Some(1), max_size: Some(4), added: 499, mode: MaxEncodedLen)
 	fn set_configs() -> Weight {
 		// Proof Size summary in bytes:
 		//  Measured:  `0`
 		//  Estimated: `0`
-		// Minimum execution time: 5_968 nanoseconds.
-		Weight::from_parts(6_245_000, 0)
-			.saturating_add(Weight::from_parts(0, 0))
-			.saturating_add(RocksDbWeight::get().writes(5_u64))
+		// Minimum execution time: 7_409_000 picoseconds.
+		Weight::from_parts(7_702_000, 0)
+			.saturating_add(RocksDbWeight::get().writes(6_u64))
 	}
 	/// Storage: NominationPools BondedPools (r:1 w:1)
-	/// Proof: NominationPools BondedPools (max_values: None, max_size: Some(164), added: 2639, mode: MaxEncodedLen)
+	/// Proof: NominationPools BondedPools (max_values: None, max_size: Some(220), added: 2695, mode: MaxEncodedLen)
 	fn update_roles() -> Weight {
 		// Proof Size summary in bytes:
-		//  Measured:  `559`
-		//  Estimated: `2639`
-		// Minimum execution time: 18_979 nanoseconds.
-		Weight::from_parts(19_795_000, 0)
-			.saturating_add(Weight::from_parts(0, 2639))
+		//  Measured:  `563`
+		//  Estimated: `3685`
+		// Minimum execution time: 20_451_000 picoseconds.
+		Weight::from_parts(20_703_000, 3685)
 			.saturating_add(RocksDbWeight::get().reads(1_u64))
 			.saturating_add(RocksDbWeight::get().writes(1_u64))
 	}
 	/// Storage: NominationPools BondedPools (r:1 w:0)
-	/// Proof: NominationPools BondedPools (max_values: None, max_size: Some(164), added: 2639, mode: MaxEncodedLen)
+	/// Proof: NominationPools BondedPools (max_values: None, max_size: Some(220), added: 2695, mode: MaxEncodedLen)
 	/// Storage: Staking Bonded (r:1 w:0)
 	/// Proof: Staking Bonded (max_values: None, max_size: Some(72), added: 2547, mode: MaxEncodedLen)
 	/// Storage: Staking Ledger (r:1 w:0)
@@ -968,14 +1030,52 @@ impl WeightInfo for () {
 	/// Proof: VoterList CounterForListNodes (max_values: Some(1), max_size: Some(4), added: 499, mode: MaxEncodedLen)
 	fn chill() -> Weight {
 		// Proof Size summary in bytes:
-		//  Measured:  `2136`
-		//  Estimated: `20489`
-		// Minimum execution time: 68_145 nanoseconds.
-		Weight::from_parts(70_444_000, 0)
-			.saturating_add(Weight::from_parts(0, 20489))
+		//  Measured:  `2140`
+		//  Estimated: `29455`
+		// Minimum execution time: 66_001_000 picoseconds.
+		Weight::from_parts(66_894_000, 29455)
 			.saturating_add(RocksDbWeight::get().reads(9_u64))
 			.saturating_add(RocksDbWeight::get().writes(5_u64))
 	}
+	/// Storage: NominationPools BondedPools (r:1 w:1)
+	/// Proof: NominationPools BondedPools (max_values: None, max_size: Some(220), added: 2695, mode: MaxEncodedLen)
+	/// Storage: NominationPools RewardPools (r:1 w:1)
+	/// Proof: NominationPools RewardPools (max_values: None, max_size: Some(92), added: 2567, mode: MaxEncodedLen)
+	/// Storage: NominationPools GlobalMaxCommission (r:1 w:0)
+	/// Proof: NominationPools GlobalMaxCommission (max_values: Some(1), max_size: Some(4), added: 499, mode: MaxEncodedLen)
+	/// Storage: System Account (r:1 w:0)
+	/// Proof: System Account (max_values: None, max_size: Some(128), added: 2603, mode: MaxEncodedLen)
+	fn set_commission() -> Weight {
+		// Proof Size summary in bytes:
+		//  Measured:  `866`
+		//  Estimated: `12324`
+		// Minimum execution time: 34_011_000 picoseconds.
+		Weight::from_parts(34_521_000, 12324)
+			.saturating_add(RocksDbWeight::get().reads(4_u64))
+			.saturating_add(RocksDbWeight::get().writes(2_u64))
+	}
+	/// Storage: NominationPools BondedPools (r:1 w:1)
+	/// Proof: NominationPools BondedPools (max_values: None, max_size: Some(220), added: 2695, mode: MaxEncodedLen)
+	fn set_commission_max() -> Weight {
+		// Proof Size summary in bytes:
+		//  Measured:  `603`
+		//  Estimated: `3685`
+		// Minimum execution time: 19_524_000 picoseconds.
+		Weight::from_parts(19_855_000, 3685)
+			.saturating_add(RocksDbWeight::get().reads(1_u64))
+			.saturating_add(RocksDbWeight::get().writes(1_u64))
+	}
+	/// Storage: NominationPools BondedPools (r:1 w:1)
+	/// Proof: NominationPools BondedPools (max_values: None, max_size: Some(220), added: 2695, mode: MaxEncodedLen)
+	fn set_commission_change_rate() -> Weight {
+		// Proof Size summary in bytes:
+		//  Measured:  `563`
+		//  Estimated: `3685`
+		// Minimum execution time: 20_457_000 picoseconds.
+		Weight::from_parts(20_698_000, 3685)
+			.saturating_add(RocksDbWeight::get().reads(1_u64))
+			.saturating_add(RocksDbWeight::get().writes(1_u64))
+	}
 	/// Storage: NominationPools PoolMembers (r:1 w:0)
 	/// Proof: NominationPools PoolMembers (max_values: None, max_size: Some(237), added: 2712, mode: MaxEncodedLen)
 	/// Storage: NominationPools ClaimPermissions (r:1 w:1)
@@ -983,11 +1083,27 @@ impl WeightInfo for () {
 	fn set_claim_permission() -> Weight {
 		// Proof Size summary in bytes:
 		//  Measured:  `542`
-		//  Estimated: `5228`
-		// Minimum execution time: 15_112 nanoseconds.
-		Weight::from_parts(15_897_000, 0)
-			.saturating_add(Weight::from_parts(0, 5228))
+		//  Estimated: `7208`
+		// Minimum execution time: 15_183_000 picoseconds.
+		Weight::from_parts(15_597_000, 7208)
 			.saturating_add(RocksDbWeight::get().reads(2_u64))
 			.saturating_add(RocksDbWeight::get().writes(1_u64))
 	}
+	/// Storage: NominationPools BondedPools (r:1 w:0)
+	/// Proof: NominationPools BondedPools (max_values: None, max_size: Some(220), added: 2695, mode: MaxEncodedLen)
+	/// Storage: NominationPools RewardPools (r:1 w:1)
+	/// Proof: NominationPools RewardPools (max_values: None, max_size: Some(92), added: 2567, mode: MaxEncodedLen)
+	/// Storage: NominationPools GlobalMaxCommission (r:1 w:0)
+	/// Proof: NominationPools GlobalMaxCommission (max_values: Some(1), max_size: Some(4), added: 499, mode: MaxEncodedLen)
+	/// Storage: System Account (r:1 w:1)
+	/// Proof: System Account (max_values: None, max_size: Some(128), added: 2603, mode: MaxEncodedLen)
+	fn claim_commission() -> Weight {
+		// Proof Size summary in bytes:
+		//  Measured:  `1096`
+		//  Estimated: `12324`
+		// Minimum execution time: 48_957_000 picoseconds.
+		Weight::from_parts(50_207_000, 12324)
+			.saturating_add(RocksDbWeight::get().reads(4_u64))
+			.saturating_add(RocksDbWeight::get().writes(2_u64))
+	}
 }
diff --git a/substrate/frame/nomination-pools/test-staking/src/mock.rs b/substrate/frame/nomination-pools/test-staking/src/mock.rs
index 85d0dd6c506..0550c8e0ca7 100644
--- a/substrate/frame/nomination-pools/test-staking/src/mock.rs
+++ b/substrate/frame/nomination-pools/test-staking/src/mock.rs
@@ -25,7 +25,7 @@ use frame_support::{
 };
 use sp_runtime::{
 	traits::{Convert, IdentityLookup},
-	FixedU128,
+	FixedU128, Perbill,
 };
 
 type AccountId = u128;
@@ -209,6 +209,7 @@ pub fn new_test_ext() -> sp_io::TestExternalities {
 		max_pools: Some(3),
 		max_members_per_pool: Some(5),
 		max_members: Some(3 * 5),
+		global_max_commission: Some(Perbill::from_percent(90)),
 	}
 	.assimilate_storage(&mut storage)
 	.unwrap();
-- 
GitLab