git.delta.rocks / unique-network / refs/commits / d8a735e3ebdb

difftreelog

refactor! decouple pallet-collator-selection from pallet-configuration (#962)

Pavel Orlov2023-06-30parent: #1ecd8fc.patch.diff
in: master
* feat!: decoupling configuration&collators pallets

BREAKING CHANGE: pallet `collator-selection` no longer has a tight coupling with the `configuration` pallet

* refactor(collator-selection): type bounds, tests

* refactor(collator-selection): tests

* add ci step for banchmark tests

* add opsFee tests

---------

15 files changed

modified.github/workflows/yarn-dev.ymldiffbeforeafterboth
--- a/.github/workflows/yarn-dev.yml
+++ b/.github/workflows/yarn-dev.yml
@@ -87,6 +87,18 @@
         run: |
           echo "url is ${{ steps.test-report.outputs.runHtmlUrl }}"
 
+      - name: Run benchmark mintFee tests
+        working-directory: tests
+        run: |
+          yarn install
+          npx ts-node --esm ./src/benchmarks/mintFee/index.ts
+
+      - name: Run benchmark opsFee tests
+        working-directory: tests
+        run: |
+          yarn install
+          npx ts-node --esm ./src/benchmarks/opsFee/index.ts
+
       - name: Stop running containers
         if: always()                   # run this step always
         run: docker-compose -f ".docker/docker-compose.${{ matrix.network }}.yml" down
modifiedCargo.lockdiffbeforeafterboth
--- a/Cargo.lock
+++ b/Cargo.lock
@@ -6371,7 +6371,7 @@
 
 [[package]]
 name = "pallet-collator-selection"
-version = "4.0.0"
+version = "5.0.0"
 dependencies = [
  "frame-benchmarking",
  "frame-support",
@@ -6380,7 +6380,6 @@
  "pallet-aura",
  "pallet-authorship",
  "pallet-balances",
- "pallet-configuration",
  "pallet-session",
  "pallet-timestamp",
  "parity-scale-codec",
@@ -6435,7 +6434,7 @@
 
 [[package]]
 name = "pallet-configuration"
-version = "0.1.3"
+version = "0.2.0"
 dependencies = [
  "fp-evm",
  "frame-benchmarking",
modifiedpallets/collator-selection/CHANGELOG.mddiffbeforeafterboth
--- a/pallets/collator-selection/CHANGELOG.md
+++ b/pallets/collator-selection/CHANGELOG.md
@@ -4,22 +4,28 @@
 
 <!-- bureaucracy goes here -->
 
+## [5.0.0] - 2023-06-27
+
+### Major change
+
+- The dependency (tight coupling) this pallet on the configuration pallet has been removed.
+
 ## [4.0.0] - 2022-12-29
 
 ### Added
 
 - Entire functionality moved over from cumulus/pallet-collator-selection (v3.0.0). Refactored business logic:
-   - Added an extra step for candidacy, `get_license`, at which payment happens. The number of license holders is unlimited. 
-      - Only licensed accounts can apply for candidacy with `onboard`. No extra deposits are made.
-      - Active candidates may `offboard`, but they will retain their license.
-      - Deposit is returned, and candidacy possibly removed with `release_license`.
-      - License can be forcibly forfeited and candidacy removed with `force_release_license`.
-   - Failing collators' deposits will now be fully slashed, and funds will be redirected to Treasury.
-   - Unify `MaxInvulnerables` and `MaxCandidates` into `MaxCollators`.
-   - Remove `MinCandidates`.
-   - Minimal amount of invulnerables is now 1.
-   - Both invulnerables and candidates count against the limits together, however invulnerables ignore `DesiredCollators`.
-   - `KickThreshold` is made configurable.
-   - `DesiredCollators`, `LicenseBond`, and `KickThreshold` are moved to and referenced from `pallet-configuration`.
-   - Naming changes to better reflect the new functionality.
-   - More minor changes, tests, benchmarks, etc.
\ No newline at end of file
+  - Added an extra step for candidacy, `get_license`, at which payment happens. The number of license holders is unlimited.
+    - Only licensed accounts can apply for candidacy with `onboard`. No extra deposits are made.
+    - Active candidates may `offboard`, but they will retain their license.
+    - Deposit is returned, and candidacy possibly removed with `release_license`.
+    - License can be forcibly forfeited and candidacy removed with `force_release_license`.
+  - Failing collators' deposits will now be fully slashed, and funds will be redirected to Treasury.
+  - Unify `MaxInvulnerables` and `MaxCandidates` into `MaxCollators`.
+  - Remove `MinCandidates`.
+  - Minimal amount of invulnerables is now 1.
+  - Both invulnerables and candidates count against the limits together, however invulnerables ignore `DesiredCollators`.
+  - `KickThreshold` is made configurable.
+  - `DesiredCollators`, `LicenseBond`, and `KickThreshold` are moved to and referenced from `pallet-configuration`.
+  - Naming changes to better reflect the new functionality.
+  - More minor changes, tests, benchmarks, etc.
modifiedpallets/collator-selection/Cargo.tomldiffbeforeafterboth
--- a/pallets/collator-selection/Cargo.toml
+++ b/pallets/collator-selection/Cargo.toml
@@ -6,7 +6,7 @@
 license = "GPLv3"
 name = "pallet-collator-selection"
 repository = "https://github.com/UniqueNetwork/unique-chain"
-version = "4.0.0"
+version = "5.0.0"
 
 [package.metadata.docs.rs]
 targets = ["x86_64-unknown-linux-gnu"]
@@ -23,7 +23,6 @@
 frame-support = { workspace = true }
 frame-system = { workspace = true }
 pallet-authorship = { workspace = true }
-pallet-configuration = { workspace = true }
 pallet-session = { workspace = true }
 sp-runtime = { workspace = true }
 sp-staking = { workspace = true }
@@ -55,8 +54,6 @@
 	"frame-system/std",
 	"log/std",
 	"pallet-authorship/std",
-	'pallet-aura/std',
-	'pallet-balances/std',
 	"pallet-session/std",
 	"rand/std",
 	"scale-info/std",
@@ -64,6 +61,8 @@
 	"sp-runtime/std",
 	"sp-staking/std",
 	"sp-std/std",
+	'pallet-aura/std',
+	'pallet-balances/std',
 ]
 
 try-runtime = ["frame-support/try-runtime"]
modifiedpallets/collator-selection/src/benchmarking.rsdiffbeforeafterboth
--- a/pallets/collator-selection/src/benchmarking.rs
+++ b/pallets/collator-selection/src/benchmarking.rs
@@ -35,7 +35,7 @@
 use super::*;
 
 #[allow(unused)]
-use crate::Pallet as CollatorSelection;
+use crate::{Pallet as CollatorSelection, BalanceOf};
 use frame_benchmarking::{account, benchmarks, impl_benchmark_test_suite, whitelisted_caller};
 use frame_support::{
 	assert_ok,
@@ -49,11 +49,6 @@
 use frame_system::{EventRecord, RawOrigin};
 use pallet_authorship::EventHandler;
 use pallet_session::{self as session, SessionManager};
-use pallet_configuration::{
-	self as configuration, BalanceOf,
-	CollatorSelectionDesiredCollatorsOverride as DesiredCollators,
-	CollatorSelectionLicenseBondOverride as LicenseBond,
-};
 use sp_std::prelude::*;
 
 const SEED: u32 = 0;
@@ -117,7 +112,7 @@
 	validators.into_iter().map(|(who, _)| who).collect()
 }
 
-fn register_invulnerables<T: Config + configuration::Config>(count: u32) {
+fn register_invulnerables<T: Config>(count: u32) {
 	let candidates = (0..count)
 		.map(|c| account("candidate", c, SEED))
 		.collect::<Vec<_>>();
@@ -131,33 +126,27 @@
 	}
 }
 
-fn register_candidates<T: Config + configuration::Config>(count: u32) {
+fn register_candidates<T: Config>(count: u32) {
 	let candidates = (0..count)
 		.map(|c| account("candidate", c, SEED))
 		.collect::<Vec<_>>();
-	assert!(
-		<LicenseBond<T>>::get() > 0u32.into(),
-		"Bond cannot be zero!"
-	);
+	assert!(T::LicenseBond::get() > 0u32.into(), "Bond cannot be zero!");
 
 	for who in candidates {
-		T::Currency::set_balance(&who, <LicenseBond<T>>::get() * 2u32.into());
+		T::Currency::set_balance(&who, T::LicenseBond::get() * 2u32.into());
 		<CollatorSelection<T>>::get_license(RawOrigin::Signed(who.clone()).into()).unwrap();
 		<CollatorSelection<T>>::onboard(RawOrigin::Signed(who).into()).unwrap();
 	}
 }
 
-fn get_licenses<T: Config + configuration::Config>(count: u32) {
+fn get_licenses<T: Config>(count: u32) {
 	let candidates = (0..count)
 		.map(|c| account("candidate", c, SEED))
 		.collect::<Vec<_>>();
-	assert!(
-		<LicenseBond<T>>::get() > 0u32.into(),
-		"Bond cannot be zero!"
-	);
+	assert!(T::LicenseBond::get() > 0u32.into(), "Bond cannot be zero!");
 
 	for who in candidates {
-		T::Currency::set_balance(&who, <LicenseBond<T>>::get() * 2u32.into());
+		T::Currency::set_balance(&who, T::LicenseBond::get() * 2u32.into());
 		<CollatorSelection<T>>::get_license(RawOrigin::Signed(who.clone()).into()).unwrap();
 	}
 }
@@ -165,7 +154,7 @@
 /// `Currency::minimum_balance` was used originally, but in unique-chain, we have
 /// zero existential deposit, thus triggering zero bond assertion.
 fn balance_unit<T: Config>() -> BalanceOf<T> {
-	200u32.into()
+	T::LicenseBond::get()
 }
 
 /// Our benchmarking environment already has invulnerables registered.
@@ -173,7 +162,7 @@
 
 benchmarks! {
 	where_clause { where
-		T: pallet_authorship::Config + session::Config + configuration::Config
+		T: Config + pallet_authorship::Config + session::Config
 	}
 
 	// todo:collator this and all the following do not work for some reason, going all the way up to 10 in length
@@ -188,7 +177,7 @@
 
 		let new_invulnerable: T::AccountId = whitelisted_caller();
 		let bond: BalanceOf<T> = balance_unit::<T>() * 2u32.into();
-		T::Currency::set_balance(&new_invulnerable, bond.clone());
+		<T as Config>::Currency::set_balance(&new_invulnerable, bond);
 
 		<session::Pallet<T>>::set_keys(
 			RawOrigin::Signed(new_invulnerable.clone()).into(),
@@ -225,15 +214,13 @@
 
 	get_license {
 		let c in 1 .. T::MaxCollators::get() - 1;
-
-		<LicenseBond<T>>::put(balance_unit::<T>());
 
 		register_validators::<T>(c);
 		get_licenses::<T>(c);
 
 		let caller: T::AccountId = whitelisted_caller();
 		let bond: BalanceOf<T> = balance_unit::<T>() * 2u32.into();
-		T::Currency::set_balance(&caller, bond.clone());
+		T::Currency::set_balance(&caller, bond);
 
 		<session::Pallet<T>>::set_keys(
 			RawOrigin::Signed(caller.clone()).into(),
@@ -251,15 +238,12 @@
 	onboard {
 		let c in 1 .. T::MaxCollators::get() - INITIAL_INVULNERABLES - 1;
 
-		<LicenseBond<T>>::put(balance_unit::<T>());
-		<DesiredCollators<T>>::put(c + INITIAL_INVULNERABLES + 1);
-
 		register_validators::<T>(c);
 		register_candidates::<T>(c);
 
 		let caller: T::AccountId = whitelisted_caller();
 		let bond: BalanceOf<T> = balance_unit::<T>() * 2u32.into();
-		T::Currency::set_balance(&caller, bond.clone());
+		T::Currency::set_balance(&caller, bond);
 
 		let origin = RawOrigin::Signed(caller.clone());
 
@@ -279,9 +263,7 @@
 
 	// worst case is the last candidate leaving.
 	offboard {
-		let c in 1 .. T::MaxCollators::get();
-		<LicenseBond<T>>::put(balance_unit::<T>());
-		<DesiredCollators<T>>::put(c + INITIAL_INVULNERABLES);
+		let c in 1 .. T::MaxCollators::get() - INITIAL_INVULNERABLES;
 
 		register_validators::<T>(c);
 		register_candidates::<T>(c);
@@ -295,10 +277,8 @@
 
 	// worst case is the last candidate leaving.
 	release_license {
-		let c in 1 .. T::MaxCollators::get();
+		let c in 1 .. T::MaxCollators::get() - INITIAL_INVULNERABLES;
 		let bond = balance_unit::<T>();
-		<LicenseBond<T>>::put(bond);
-		<DesiredCollators<T>>::put(c + INITIAL_INVULNERABLES);
 
 		register_validators::<T>(c);
 		register_candidates::<T>(c);
@@ -312,10 +292,8 @@
 
 	// worst case is the last candidate leaving.
 	force_release_license {
-		let c in 1 .. T::MaxCollators::get();
+		let c in 1 .. T::MaxCollators::get() - INITIAL_INVULNERABLES;
 		let bond = balance_unit::<T>();
-		<LicenseBond<T>>::put(bond);
-		<DesiredCollators<T>>::put(c + INITIAL_INVULNERABLES);
 
 		register_validators::<T>(c);
 		register_candidates::<T>(c);
@@ -334,7 +312,6 @@
 
 	// worst case is paying a non-existing candidate account.
 	note_author {
-		<LicenseBond<T>>::put(balance_unit::<T>());
 		T::Currency::set_balance(
 			&<CollatorSelection<T>>::account_id(),
 			balance_unit::<T>() * 4u32.into(),
@@ -353,11 +330,9 @@
 
 	// worst case for new session.
 	new_session {
-		let r in 1 .. T::MaxCollators::get();
-		let c in 1 .. T::MaxCollators::get();
+		let r in 1 .. T::MaxCollators::get() - INITIAL_INVULNERABLES;
+		let c in 1 .. T::MaxCollators::get() - INITIAL_INVULNERABLES;
 
-		<LicenseBond<T>>::put(balance_unit::<T>());
-		<DesiredCollators<T>>::put(c + INITIAL_INVULNERABLES);
 		frame_system::Pallet::<T>::set_block_number(0u32.into());
 
 		register_validators::<T>(c);
modifiedpallets/collator-selection/src/lib.rsdiffbeforeafterboth
before · pallets/collator-selection/src/lib.rs
1// Copyright 2019-2022 Unique Network (Gibraltar) Ltd.2// This file is part of Unique Network.34// Unique Network is free software: you can redistribute it and/or modify5// it under the terms of the GNU General Public License as published by6// the Free Software Foundation, either version 3 of the License, or7// (at your option) any later version.89// Unique Network is distributed in the hope that it will be useful,10// but WITHOUT ANY WARRANTY; without even the implied warranty of11// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the12// GNU General Public License for more details.1314// You should have received a copy of the GNU General Public License15// along with Unique Network. If not, see <http://www.gnu.org/licenses/>.1617// Original license:18// Copyright (C) 2021 Parity Technologies (UK) Ltd.19// SPDX-License-Identifier: Apache-2.02021// Licensed under the Apache License, Version 2.0 (the "License");22// you may not use this file except in compliance with the License.23// You may obtain a copy of the License at24//25// 	http://www.apache.org/licenses/LICENSE-2.026//27// Unless required by applicable law or agreed to in writing, software28// distributed under the License is distributed on an "AS IS" BASIS,29// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.30// See the License for the specific language governing permissions and31// limitations under the License.3233// todo:collator documentation34//! Collator Selection pallet.35//!36//! A pallet to manage collators in a parachain.37//!38//! ## Overview39//!40//! The Collator Selection pallet manages the collators of a parachain. **Collation is _not_ a41//! secure activity** and this pallet does not implement any game-theoretic mechanisms to meet BFT42//! safety assumptions of the chosen set.43//!44//! ## Terminology45//!46//! - Collator: A parachain block producer.47//! - Bond: An amount of `Balance` _reserved_ for candidate registration.48//! - Invulnerable: An account guaranteed to be in the collator set.49//!50//! ## Implementation51//!52//! The final `Collators` are aggregated from two individual lists:53//!54//! 1. [`Invulnerables`]: a set of collators appointed by governance. These accounts will always be55//!    collators.56//! 2. [`Candidates`]: these are *candidates to the collation task* and may or may not be elected as57//!    a final collator.58//!59//! The current implementation resolves congestion of [`Candidates`] in a first-come-first-serve60//! manner.61//!62//! Candidates will not be allowed to get kicked or leave_intent if the total number of candidates63//! fall below MinCandidates. This is for potential disaster recovery scenarios.64//!65//! ### Rewards66//!67//! The Collator Selection pallet maintains an on-chain account (the "Pot"). In each block, the68//! collator who authored it receives:69//!70//! - Half the value of the Pot.71//! - Half the value of the transaction fees within the block. The other half of the transaction72//!   fees are deposited into the Pot.73//!74//! To initiate rewards an ED needs to be transferred to the pot address.75//!76//! Note: Eventually the Pot distribution may be modified as discussed in77//! [this issue](https://github.com/paritytech/statemint/issues/21#issuecomment-810481073).7879#![cfg_attr(not(feature = "std"), no_std)]8081pub use pallet::*;8283#[cfg(test)]84mod mock;8586#[cfg(test)]87mod tests;8889#[cfg(feature = "runtime-benchmarks")]90mod benchmarking;91pub mod weights;9293#[frame_support::pallet]94pub mod pallet {95	use super::*;96	pub use crate::weights::WeightInfo;97	use core::ops::Div;98	use frame_support::{99		dispatch::{DispatchClass, DispatchResultWithPostInfo},100		inherent::Vec,101		pallet_prelude::*,102		sp_runtime::traits::{AccountIdConversion, CheckedSub, Saturating, Zero},103		traits::{104			EnsureOrigin,105			fungible::{Balanced, BalancedHold, Inspect, InspectHold, Mutate, MutateHold},106			ValidatorRegistration,107			tokens::{Precision, Preservation},108		},109		BoundedVec, PalletId,110	};111	use frame_system::pallet_prelude::*;112	use pallet_session::SessionManager;113	use sp_runtime::{Perbill, traits::Convert};114	use pallet_configuration::{115		CollatorSelectionDesiredCollatorsOverride as DesiredCollators,116		CollatorSelectionLicenseBondOverride as LicenseBond,117		CollatorSelectionKickThresholdOverride as KickThreshold, BalanceOf,118	};119	use sp_staking::SessionIndex;120121	/// A convertor from collators id. Since this pallet does not have stash/controller, this is122	/// just identity.123	pub struct IdentityCollator;124	impl<T> sp_runtime::traits::Convert<T, Option<T>> for IdentityCollator {125		fn convert(t: T) -> Option<T> {126			Some(t)127		}128	}129130	/// Configure the pallet by specifying the parameters and types on which it depends.131	#[pallet::config]132	pub trait Config: frame_system::Config + pallet_configuration::Config {133		/// Overarching event type.134		type RuntimeEvent: From<Event<Self>> + IsType<<Self as frame_system::Config>::RuntimeEvent>;135136		/// Origin that can dictate updating parameters of this pallet.137		type UpdateOrigin: EnsureOrigin<Self::RuntimeOrigin>;138139		/// Account Identifier that holds the chain's treasury.140		type TreasuryAccountId: Get<Self::AccountId>;141142		/// Account Identifier from which the internal Pot is generated.143		type PotId: Get<PalletId>;144145		/// Maximum number of candidates and invulnerables that we should have. This is enforced in code.146		type MaxCollators: Get<u32>;147148		/// If kicked, how much of the collator's deposit will be slashed and sent to the slash destination.149		type SlashRatio: Get<Perbill>;150151		/// A stable ID for a validator.152		type ValidatorId: Member + Parameter;153154		/// A conversion from account ID to validator ID.155		///156		/// Its cost must be at most one storage read.157		type ValidatorIdOf: Convert<Self::AccountId, Option<Self::ValidatorId>>;158159		/// Validate a user is registered160		type ValidatorRegistration: ValidatorRegistration<Self::ValidatorId>;161162		/// The weight information of this pallet.163		type WeightInfo: WeightInfo;164165		#[pallet::constant]166		type LicenceBondIdentifier: Get<<<Self as pallet_configuration::Config>::Currency as InspectHold<Self::AccountId>>::Reason>;167	}168169	#[pallet::pallet]170	pub struct Pallet<T>(_);171172	/// The invulnerable, fixed collators.173	#[pallet::storage]174	#[pallet::getter(fn invulnerables)]175	pub type Invulnerables<T: Config> =176		StorageValue<_, BoundedVec<T::AccountId, T::MaxCollators>, ValueQuery>;177178	/// The (community) collation license holders.179	#[pallet::storage]180	#[pallet::getter(fn license_deposit_of)]181	pub type LicenseDepositOf<T: Config> =182		StorageMap<_, Blake2_128Concat, T::AccountId, BalanceOf<T>, ValueQuery>;183184	/// The (community, limited) collation candidates.185	#[pallet::storage]186	#[pallet::getter(fn candidates)]187	pub type Candidates<T: Config> =188		StorageValue<_, BoundedVec<T::AccountId, T::MaxCollators>, ValueQuery>;189190	/// Last block authored by collator.191	#[pallet::storage]192	#[pallet::getter(fn last_authored_block)]193	pub type LastAuthoredBlock<T: Config> =194		StorageMap<_, Twox64Concat, T::AccountId, T::BlockNumber, ValueQuery>;195196	#[pallet::genesis_config]197	pub struct GenesisConfig<T: Config> {198		pub invulnerables: Vec<T::AccountId>,199	}200201	#[cfg(feature = "std")]202	impl<T: Config> Default for GenesisConfig<T> {203		fn default() -> Self {204			Self {205				invulnerables: Default::default(),206			}207		}208	}209210	#[pallet::genesis_build]211	impl<T: Config> GenesisBuild<T> for GenesisConfig<T> {212		fn build(&self) {213			let duplicate_invulnerables = self214				.invulnerables215				.iter()216				.collect::<std::collections::BTreeSet<_>>();217			assert!(218				duplicate_invulnerables.len() == self.invulnerables.len(),219				"duplicate invulnerables in genesis."220			);221222			let bounded_invulnerables =223				BoundedVec::<_, T::MaxCollators>::try_from(self.invulnerables.clone())224					.expect("genesis invulnerables are more than T::MaxCollators");225226			<Invulnerables<T>>::put(bounded_invulnerables);227		}228	}229230	#[pallet::event]231	#[pallet::generate_deposit(pub(super) fn deposit_event)]232	pub enum Event<T: Config> {233		InvulnerableAdded {234			invulnerable: T::AccountId,235		},236		InvulnerableRemoved {237			invulnerable: T::AccountId,238		},239		LicenseObtained {240			account_id: T::AccountId,241			deposit: BalanceOf<T>,242		},243		LicenseReleased {244			account_id: T::AccountId,245			deposit_returned: BalanceOf<T>,246		},247		CandidateAdded {248			account_id: T::AccountId,249		},250		CandidateRemoved {251			account_id: T::AccountId,252		},253	}254255	// Errors inform users that something went wrong.256	#[pallet::error]257	pub enum Error<T> {258		/// Too many candidates259		TooManyCandidates,260		/// Unknown error261		Unknown,262		/// Permission issue263		Permission,264		/// User already holds license to collate265		AlreadyHoldingLicense,266		/// User does not hold a license to collate267		NoLicense,268		/// User is already a candidate269		AlreadyCandidate,270		/// User is not a candidate271		NotCandidate,272		/// Too many invulnerables273		TooManyInvulnerables,274		/// Too few invulnerables275		TooFewInvulnerables,276		/// User is already an Invulnerable277		AlreadyInvulnerable,278		/// User is not an Invulnerable279		NotInvulnerable,280		/// Account has no associated validator ID281		NoAssociatedValidatorId,282		/// Validator ID is not yet registered283		ValidatorNotRegistered,284	}285286	#[pallet::hooks]287	impl<T: Config> Hooks<BlockNumberFor<T>> for Pallet<T> {}288289	#[pallet::call]290	impl<T: Config> Pallet<T> {291		/// Add a collator to the list of invulnerable (fixed) collators.292		#[pallet::call_index(0)]293		#[pallet::weight(<T as Config>::WeightInfo::add_invulnerable(T::MaxCollators::get()))]294		pub fn add_invulnerable(295			origin: OriginFor<T>,296			new: T::AccountId,297		) -> DispatchResultWithPostInfo {298			T::UpdateOrigin::ensure_origin(origin)?;299300			// check if the new invulnerable has associated validator keys before it is added301			let validator_key = T::ValidatorIdOf::convert(new.clone())302				.ok_or(Error::<T>::NoAssociatedValidatorId)?;303			ensure!(304				T::ValidatorRegistration::is_registered(&validator_key),305				Error::<T>::ValidatorNotRegistered306			);307			if Self::invulnerables().contains(&new) {308				return Ok(().into());309			}310311			<Invulnerables<T>>::try_append(new.clone())312				.map_err(|_| Error::<T>::TooManyInvulnerables)?;313314			// try to offboard the new invulnerable if it was a collator candidate before315			let _ = Self::try_remove_candidate(&new);316317			Self::deposit_event(Event::InvulnerableAdded { invulnerable: new });318			Ok(().into())319		}320321		/// Remove a collator from the list of invulnerable (fixed) collators.322		#[pallet::call_index(1)]323		#[pallet::weight(<T as Config>::WeightInfo::remove_invulnerable(T::MaxCollators::get()))]324		pub fn remove_invulnerable(325			origin: OriginFor<T>,326			who: T::AccountId,327		) -> DispatchResultWithPostInfo {328			T::UpdateOrigin::ensure_origin(origin)?;329330			<Invulnerables<T>>::try_mutate(|invulnerables| -> DispatchResult {331				if invulnerables.len() <= 1 {332					return Err(Error::<T>::TooFewInvulnerables.into());333				}334335				let index = invulnerables336					.into_iter()337					.position(|r| *r == who)338					.ok_or(Error::<T>::NotInvulnerable)?;339				invulnerables.remove(index);340				Ok(())341			})?;342			Self::deposit_event(Event::InvulnerableRemoved { invulnerable: who });343			Ok(().into())344		}345346		/// Purchase a license on block collation for this account.347		/// It does not make it a collator candidate, use `onboard` afterward. The account must348		/// (a) already have registered session keys and (b) be able to reserve the `LicenseBond`.349		///350		/// This call is not available to `Invulnerable` collators.351		#[pallet::call_index(2)]352		#[pallet::weight(<T as Config>::WeightInfo::get_license(T::MaxCollators::get()))]353		pub fn get_license(origin: OriginFor<T>) -> DispatchResultWithPostInfo {354			// register_as_candidate355			let who = ensure_signed(origin)?;356357			if LicenseDepositOf::<T>::contains_key(&who) {358				return Err(Error::<T>::AlreadyHoldingLicense.into());359			}360361			let validator_key = T::ValidatorIdOf::convert(who.clone())362				.ok_or(Error::<T>::NoAssociatedValidatorId)?;363			ensure!(364				T::ValidatorRegistration::is_registered(&validator_key),365				Error::<T>::ValidatorNotRegistered366			);367368			let deposit = <LicenseBond<T>>::get();369370			T::Currency::hold(&T::LicenceBondIdentifier::get(), &who, deposit)?;371			LicenseDepositOf::<T>::insert(who.clone(), deposit);372373			Self::deposit_event(Event::LicenseObtained {374				account_id: who,375				deposit,376			});377			Ok(().into()) // Some(T::WeightInfo::register_as_candidate(current_count as u32)).into())378		}379380		/// Register this account as a candidate for collators for next sessions.381		/// The account must already hold a license, and cannot offboard immediately during a session.382		///383		/// This call is not available to `Invulnerable` collators.384		#[pallet::call_index(3)]385		#[pallet::weight(<T as Config>::WeightInfo::onboard(T::MaxCollators::get()))]386		pub fn onboard(origin: OriginFor<T>) -> DispatchResultWithPostInfo {387			// register_as_candidate388			let who = ensure_signed(origin)?;389390			// ensure the user obtained the license.391			ensure!(392				LicenseDepositOf::<T>::contains_key(&who),393				Error::<T>::NoLicense394			);395			// ensure we are below limit.396			let length = <Candidates<T>>::decode_len().unwrap_or_default()397				+ <Invulnerables<T>>::decode_len().unwrap_or_default();398			ensure!(399				(length as u32) < <DesiredCollators<T>>::get(),400				Error::<T>::TooManyCandidates401			);402			ensure!(403				!Self::invulnerables().contains(&who),404				Error::<T>::AlreadyInvulnerable405			);406407			let current_count =408				<Candidates<T>>::try_mutate(|candidates| -> Result<usize, DispatchError> {409					if candidates.iter().any(|candidate| *candidate == who) {410						Err(Error::<T>::AlreadyCandidate)?411					} else {412						candidates413							.try_push(who.clone())414							.map_err(|_| Error::<T>::TooManyCandidates)?;415						// First authored block is current block plus kick threshold to handle session delay416						<LastAuthoredBlock<T>>::insert(417							who.clone(),418							frame_system::Pallet::<T>::block_number() + <KickThreshold<T>>::get(),419						);420						Ok(candidates.len())421					}422				})?;423424			Self::deposit_event(Event::CandidateAdded { account_id: who });425			Ok(Some(<T as Config>::WeightInfo::onboard(current_count as u32)).into())426		}427428		/// Deregister `origin` as a collator candidate. Note that the collator can only leave on429		/// session change. The license to `onboard` later at any other time will remain.430		#[pallet::call_index(4)]431		#[pallet::weight(<T as Config>::WeightInfo::offboard(T::MaxCollators::get()))]432		pub fn offboard(origin: OriginFor<T>) -> DispatchResultWithPostInfo {433			// leave_intent434			let who = ensure_signed(origin)?;435			let current_count = Self::try_remove_candidate(&who)?;436437			Ok(Some(<T as Config>::WeightInfo::offboard(current_count as u32)).into())438		}439440		/// Forfeit `origin`'s own license. The `LicenseBond` will be unreserved immediately.441		///442		/// This call is not available to `Invulnerable` collators.443		#[pallet::call_index(5)]444		#[pallet::weight(<T as Config>::WeightInfo::release_license(T::MaxCollators::get()))]445		pub fn release_license(origin: OriginFor<T>) -> DispatchResultWithPostInfo {446			// leave_intent447			let who = ensure_signed(origin)?;448449			let current_count = Self::try_remove_candidate_and_release_license(&who, false, true)?;450451			Ok(Some(<T as Config>::WeightInfo::release_license(452				current_count as u32,453			))454			.into())455		}456457		/// Force deregister `origin` as a collator candidate as a governing authority, and revoke its license.458		/// Note that the collator can only leave on session change.459		/// The `LicenseBond` will be unreserved and returned immediately.460		///461		/// This call is, of course, not applicable to `Invulnerable` collators.462		#[pallet::call_index(6)]463		#[pallet::weight(<T as Config>::WeightInfo::force_release_license(T::MaxCollators::get()))]464		pub fn force_release_license(465			origin: OriginFor<T>,466			who: T::AccountId,467		) -> DispatchResultWithPostInfo {468			// leave_intent469			T::UpdateOrigin::ensure_origin(origin)?;470471			let current_count = Self::try_remove_candidate_and_release_license(&who, false, true)?;472473			Ok(Some(<T as Config>::WeightInfo::force_release_license(474				current_count as u32,475			))476			.into())477		}478	}479480	impl<T: Config> Pallet<T> {481		/// Get a unique, inaccessible account id from the `PotId`.482		pub fn account_id() -> T::AccountId {483			T::PotId::get().into_account_truncating()484		}485486		/// Removes a candidate and their license, optionally slashed and optionally ignoring,487		/// whether or not they actually are a candidate.488		fn try_remove_candidate_and_release_license(489			who: &T::AccountId,490			should_slash: bool,491			ignore_if_not_candidate: bool,492		) -> Result<usize, DispatchError> {493			let current_count = Self::try_remove_candidate(who);494			let current_count = if ignore_if_not_candidate495				&& current_count == Err(Error::<T>::NotCandidate.into())496			{497				<Candidates<T>>::decode_len().unwrap_or_default()498			} else {499				current_count?500			};501			Self::try_release_license(who, should_slash)?;502			Ok(current_count)503		}504505		/// Removes a candidate from the collator pool for the next session if they exist.506		fn try_remove_candidate(who: &T::AccountId) -> Result<usize, DispatchError> {507			let current_count =508				<Candidates<T>>::try_mutate(|candidates| -> Result<usize, DispatchError> {509					let index = candidates510						.iter()511						.position(|candidate| *candidate == *who)512						.ok_or(Error::<T>::NotCandidate)?;513					candidates.remove(index);514					<LastAuthoredBlock<T>>::remove(who.clone());515					Ok(candidates.len())516				})?;517			Self::deposit_event(Event::CandidateRemoved {518				account_id: who.clone(),519			});520			Ok(current_count)521		}522523		/// Removes a candidate if they exist and sends them back their deposit, optionally slashed.524		fn try_release_license(who: &T::AccountId, should_slash: bool) -> DispatchResult {525			let mut deposit_returned = BalanceOf::<T>::default();526			LicenseDepositOf::<T>::try_mutate_exists(who, |deposit| -> DispatchResult {527				if let Some(deposit) = deposit.take() {528					if should_slash {529						let slashed = T::SlashRatio::get() * deposit;530						let remaining = deposit - slashed;531532						let (imbalance, _) =533							T::Currency::slash(&T::LicenceBondIdentifier::get(), who, slashed);534						deposit_returned = remaining;535536						T::Currency::resolve(&T::TreasuryAccountId::get(), imbalance)537							.map_err(|_| DispatchError::Other("Failed to deposit imbalance"))?;538					} else {539						deposit_returned = deposit;540					}541542					T::Currency::release(543						&T::LicenceBondIdentifier::get(),544						who,545						deposit_returned,546						Precision::Exact,547					)?;548					Ok(())549				} else {550					Err(Error::<T>::NoLicense.into())551				}552			})?;553			Self::deposit_event(Event::LicenseReleased {554				account_id: who.clone(),555				deposit_returned,556			});557			Ok(())558		}559560		/// Assemble the current set of candidates and invulnerables into the next collator set.561		///562		/// This is done on the fly, as frequent as we are told to do so, as the session manager.563		pub fn assemble_collators(564			candidates: BoundedVec<T::AccountId, T::MaxCollators>,565		) -> Vec<T::AccountId> {566			let mut collators = Self::invulnerables().to_vec();567			collators.extend(candidates);568			collators569		}570571		/// Kicks out candidates that did not produce a block in the kick threshold572		/// and **confiscates** their deposits to the treasury.573		pub fn kick_stale_candidates(574			candidates: BoundedVec<T::AccountId, T::MaxCollators>,575		) -> BoundedVec<T::AccountId, T::MaxCollators> {576			let now = frame_system::Pallet::<T>::block_number();577			let kick_threshold = <KickThreshold<T>>::get();578			candidates579				.into_iter()580				.filter_map(|c| {581					let last_block = <LastAuthoredBlock<T>>::get(c.clone());582					let since_last = now.saturating_sub(last_block);583					if since_last < kick_threshold {584						Some(c)585					} else {586						let outcome = Self::try_remove_candidate_and_release_license(&c, true, false);587						if let Err(why) = outcome {588							log::warn!("Failed to kick collator and release license {:?}", why);589							debug_assert!(false, "failed to kick collator and release license {why:?}");590						}591						None592					}593				})594				.collect::<Vec<_>>()595				.try_into()596				.expect("filter_map operation can't result in a bounded vec larger than its original; qed")597		}598	}599600	/// Keep track of number of authored blocks per authority, uncles are counted as well since601	/// they're a valid proof of being online.602	impl<T: Config + pallet_authorship::Config>603		pallet_authorship::EventHandler<T::AccountId, T::BlockNumber> for Pallet<T>604	{605		fn note_author(author: T::AccountId) {606			let pot = Self::account_id();607			// assumes an ED will be sent to pot.608			let reward = T::Currency::balance(&pot)609				.checked_sub(&T::Currency::minimum_balance())610				.unwrap_or_else(Zero::zero)611				.div(2u32.into());612613			if !reward.is_zero() {614				// `reward` is half of pot account minus ED, this should never fail.615				let _success = T::Currency::transfer(&pot, &author, reward, Preservation::Preserve);616				debug_assert!(_success.is_ok());617			}618			<LastAuthoredBlock<T>>::insert(author, frame_system::Pallet::<T>::block_number());619620			frame_system::Pallet::<T>::register_extra_weight_unchecked(621				<T as Config>::WeightInfo::note_author(),622				DispatchClass::Mandatory,623			);624		}625	}626627	/// Play the role of the session manager.628	impl<T: Config> SessionManager<T::AccountId> for Pallet<T> {629		fn new_session(index: SessionIndex) -> Option<Vec<T::AccountId>> {630			log::info!(631				"assembling new collators for new session {} at #{:?}",632				index,633				<frame_system::Pallet<T>>::block_number(),634			);635636			let candidates = Self::candidates();637			let candidates_len_before = candidates.len();638			let active_candidates = Self::kick_stale_candidates(candidates);639			let removed = candidates_len_before - active_candidates.len();640			let result = Self::assemble_collators(active_candidates);641642			frame_system::Pallet::<T>::register_extra_weight_unchecked(643				<T as Config>::WeightInfo::new_session(644					candidates_len_before as u32,645					removed as u32,646				),647				DispatchClass::Mandatory,648			);649			Some(result)650		}651		fn start_session(_: SessionIndex) {652			// we don't care.653		}654		fn end_session(_: SessionIndex) {655			// we don't care.656		}657	}658}
after · pallets/collator-selection/src/lib.rs
1// Copyright 2019-2022 Unique Network (Gibraltar) Ltd.2// This file is part of Unique Network.34// Unique Network is free software: you can redistribute it and/or modify5// it under the terms of the GNU General Public License as published by6// the Free Software Foundation, either version 3 of the License, or7// (at your option) any later version.89// Unique Network is distributed in the hope that it will be useful,10// but WITHOUT ANY WARRANTY; without even the implied warranty of11// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the12// GNU General Public License for more details.1314// You should have received a copy of the GNU General Public License15// along with Unique Network. If not, see <http://www.gnu.org/licenses/>.1617// Original license:18// Copyright (C) 2021 Parity Technologies (UK) Ltd.19// SPDX-License-Identifier: Apache-2.02021// Licensed under the Apache License, Version 2.0 (the "License");22// you may not use this file except in compliance with the License.23// You may obtain a copy of the License at24//25// 	http://www.apache.org/licenses/LICENSE-2.026//27// Unless required by applicable law or agreed to in writing, software28// distributed under the License is distributed on an "AS IS" BASIS,29// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.30// See the License for the specific language governing permissions and31// limitations under the License.3233// todo:collator documentation34//! Collator Selection pallet.35//!36//! A pallet to manage collators in a parachain.37//!38//! ## Overview39//!40//! The Collator Selection pallet manages the collators of a parachain. **Collation is _not_ a41//! secure activity** and this pallet does not implement any game-theoretic mechanisms to meet BFT42//! safety assumptions of the chosen set.43//!44//! ## Terminology45//!46//! - Collator: A parachain block producer.47//! - Bond: An amount of `Balance` _reserved_ for candidate registration.48//! - Invulnerable: An account guaranteed to be in the collator set.49//!50//! ## Implementation51//!52//! The final `Collators` are aggregated from two individual lists:53//!54//! 1. [`Invulnerables`]: a set of collators appointed by governance. These accounts will always be55//!    collators.56//! 2. [`Candidates`]: these are *candidates to the collation task* and may or may not be elected as57//!    a final collator.58//!59//! The current implementation resolves congestion of [`Candidates`] in a first-come-first-serve60//! manner.61//!62//! Candidates will not be allowed to get kicked or leave_intent if the total number of candidates63//! fall below MinCandidates. This is for potential disaster recovery scenarios.64//!65//! ### Rewards66//!67//! The Collator Selection pallet maintains an on-chain account (the "Pot"). In each block, the68//! collator who authored it receives:69//!70//! - Half the value of the Pot.71//! - Half the value of the transaction fees within the block. The other half of the transaction72//!   fees are deposited into the Pot.73//!74//! To initiate rewards an ED needs to be transferred to the pot address.75//!76//! Note: Eventually the Pot distribution may be modified as discussed in77//! [this issue](https://github.com/paritytech/statemint/issues/21#issuecomment-810481073).7879#![cfg_attr(not(feature = "std"), no_std)]8081pub use pallet::*;8283#[cfg(test)]84mod mock;8586#[cfg(test)]87mod tests;8889#[cfg(feature = "runtime-benchmarks")]90mod benchmarking;91pub mod weights;9293use frame_support::traits::fungible::Inspect;9495type BalanceOf<T> =96	<<T as Config>::Currency as Inspect<<T as frame_system::Config>::AccountId>>::Balance;97#[frame_support::pallet]98pub mod pallet {99	use super::*;100	pub use crate::weights::WeightInfo;101	use core::ops::Div;102	use frame_support::{103		dispatch::{DispatchClass, DispatchResultWithPostInfo},104		inherent::Vec,105		pallet_prelude::*,106		sp_runtime::traits::{AccountIdConversion, CheckedSub, Saturating, Zero},107		traits::{108			EnsureOrigin,109			fungible::{Balanced, BalancedHold, Inspect, InspectHold, Mutate, MutateHold},110			ValidatorRegistration,111			tokens::{Precision, Preservation},112		},113		BoundedVec, PalletId,114	};115	use frame_system::pallet_prelude::*;116	use pallet_session::SessionManager;117	use sp_runtime::{Perbill, traits::Convert};118	use sp_staking::SessionIndex;119120	/// A convertor from collators id. Since this pallet does not have stash/controller, this is121	/// just identity.122	pub struct IdentityCollator;123	impl<T> sp_runtime::traits::Convert<T, Option<T>> for IdentityCollator {124		fn convert(t: T) -> Option<T> {125			Some(t)126		}127	}128129	/// Configure the pallet by specifying the parameters and types on which it depends.130	#[pallet::config]131	pub trait Config: frame_system::Config {132		/// Overarching event type.133		type RuntimeEvent: From<Event<Self>> + IsType<<Self as frame_system::Config>::RuntimeEvent>;134		type Currency: Mutate<Self::AccountId>135			+ MutateHold<Self::AccountId>136			+ BalancedHold<Self::AccountId>;137138		/// Origin that can dictate updating parameters of this pallet.139		type UpdateOrigin: EnsureOrigin<Self::RuntimeOrigin>;140141		/// Account Identifier that holds the chain's treasury.142		type TreasuryAccountId: Get<Self::AccountId>;143144		/// Account Identifier from which the internal Pot is generated.145		type PotId: Get<PalletId>;146147		/// Maximum number of candidates and invulnerables that we should have. This is enforced in code.148		type MaxCollators: Get<u32>;149150		/// If kicked, how much of the collator's deposit will be slashed and sent to the slash destination.151		type SlashRatio: Get<Perbill>;152153		/// A stable ID for a validator.154		type ValidatorId: Member + Parameter;155156		/// A conversion from account ID to validator ID.157		///158		/// Its cost must be at most one storage read.159		type ValidatorIdOf: Convert<Self::AccountId, Option<Self::ValidatorId>>;160161		/// Validate a user is registered162		type ValidatorRegistration: ValidatorRegistration<Self::ValidatorId>;163164		/// The weight information of this pallet.165		type WeightInfo: WeightInfo;166167		#[pallet::constant]168		type LicenceBondIdentifier: Get<<Self::Currency as InspectHold<Self::AccountId>>::Reason>;169170		type DesiredCollators: Get<u32>;171172		type LicenseBond: Get<BalanceOf<Self>>;173174		type KickThreshold: Get<Self::BlockNumber>;175	}176177	#[pallet::pallet]178	pub struct Pallet<T>(_);179180	/// The invulnerable, fixed collators.181	#[pallet::storage]182	#[pallet::getter(fn invulnerables)]183	pub type Invulnerables<T: Config> =184		StorageValue<_, BoundedVec<T::AccountId, T::MaxCollators>, ValueQuery>;185186	/// The (community) collation license holders.187	#[pallet::storage]188	#[pallet::getter(fn license_deposit_of)]189	pub type LicenseDepositOf<T: Config> =190		StorageMap<_, Blake2_128Concat, T::AccountId, BalanceOf<T>, ValueQuery>;191192	/// The (community, limited) collation candidates.193	#[pallet::storage]194	#[pallet::getter(fn candidates)]195	pub type Candidates<T: Config> =196		StorageValue<_, BoundedVec<T::AccountId, T::MaxCollators>, ValueQuery>;197198	/// Last block authored by collator.199	#[pallet::storage]200	#[pallet::getter(fn last_authored_block)]201	pub type LastAuthoredBlock<T: Config> =202		StorageMap<_, Twox64Concat, T::AccountId, T::BlockNumber, ValueQuery>;203204	#[pallet::genesis_config]205	pub struct GenesisConfig<T: Config> {206		pub invulnerables: Vec<T::AccountId>,207	}208209	#[cfg(feature = "std")]210	impl<T: Config> Default for GenesisConfig<T> {211		fn default() -> Self {212			Self {213				invulnerables: Default::default(),214			}215		}216	}217218	#[pallet::genesis_build]219	impl<T: Config> GenesisBuild<T> for GenesisConfig<T> {220		fn build(&self) {221			let duplicate_invulnerables = self222				.invulnerables223				.iter()224				.collect::<std::collections::BTreeSet<_>>();225			assert!(226				duplicate_invulnerables.len() == self.invulnerables.len(),227				"duplicate invulnerables in genesis."228			);229230			let bounded_invulnerables =231				BoundedVec::<_, T::MaxCollators>::try_from(self.invulnerables.clone())232					.expect("genesis invulnerables are more than T::MaxCollators");233234			<Invulnerables<T>>::put(bounded_invulnerables);235		}236	}237238	#[pallet::event]239	#[pallet::generate_deposit(pub(super) fn deposit_event)]240	pub enum Event<T: Config> {241		InvulnerableAdded {242			invulnerable: T::AccountId,243		},244		InvulnerableRemoved {245			invulnerable: T::AccountId,246		},247		LicenseObtained {248			account_id: T::AccountId,249			deposit: BalanceOf<T>,250		},251		LicenseReleased {252			account_id: T::AccountId,253			deposit_returned: BalanceOf<T>,254		},255		CandidateAdded {256			account_id: T::AccountId,257		},258		CandidateRemoved {259			account_id: T::AccountId,260		},261	}262263	// Errors inform users that something went wrong.264	#[pallet::error]265	pub enum Error<T> {266		/// Too many candidates267		TooManyCandidates,268		/// Unknown error269		Unknown,270		/// Permission issue271		Permission,272		/// User already holds license to collate273		AlreadyHoldingLicense,274		/// User does not hold a license to collate275		NoLicense,276		/// User is already a candidate277		AlreadyCandidate,278		/// User is not a candidate279		NotCandidate,280		/// Too many invulnerables281		TooManyInvulnerables,282		/// Too few invulnerables283		TooFewInvulnerables,284		/// User is already an Invulnerable285		AlreadyInvulnerable,286		/// User is not an Invulnerable287		NotInvulnerable,288		/// Account has no associated validator ID289		NoAssociatedValidatorId,290		/// Validator ID is not yet registered291		ValidatorNotRegistered,292	}293294	#[pallet::hooks]295	impl<T: Config> Hooks<BlockNumberFor<T>> for Pallet<T> {}296297	#[pallet::call]298	impl<T: Config> Pallet<T> {299		/// Add a collator to the list of invulnerable (fixed) collators.300		#[pallet::call_index(0)]301		#[pallet::weight(<T as Config>::WeightInfo::add_invulnerable(T::MaxCollators::get()))]302		pub fn add_invulnerable(303			origin: OriginFor<T>,304			new: T::AccountId,305		) -> DispatchResultWithPostInfo {306			T::UpdateOrigin::ensure_origin(origin)?;307308			// check if the new invulnerable has associated validator keys before it is added309			let validator_key = T::ValidatorIdOf::convert(new.clone())310				.ok_or(Error::<T>::NoAssociatedValidatorId)?;311			ensure!(312				T::ValidatorRegistration::is_registered(&validator_key),313				Error::<T>::ValidatorNotRegistered314			);315			if Self::invulnerables().contains(&new) {316				return Ok(().into());317			}318319			<Invulnerables<T>>::try_append(new.clone())320				.map_err(|_| Error::<T>::TooManyInvulnerables)?;321322			// try to offboard the new invulnerable if it was a collator candidate before323			let _ = Self::try_remove_candidate(&new);324325			Self::deposit_event(Event::InvulnerableAdded { invulnerable: new });326			Ok(().into())327		}328329		/// Remove a collator from the list of invulnerable (fixed) collators.330		#[pallet::call_index(1)]331		#[pallet::weight(<T as Config>::WeightInfo::remove_invulnerable(T::MaxCollators::get()))]332		pub fn remove_invulnerable(333			origin: OriginFor<T>,334			who: T::AccountId,335		) -> DispatchResultWithPostInfo {336			T::UpdateOrigin::ensure_origin(origin)?;337338			<Invulnerables<T>>::try_mutate(|invulnerables| -> DispatchResult {339				if invulnerables.len() <= 1 {340					return Err(Error::<T>::TooFewInvulnerables.into());341				}342343				let index = invulnerables344					.into_iter()345					.position(|r| *r == who)346					.ok_or(Error::<T>::NotInvulnerable)?;347				invulnerables.remove(index);348				Ok(())349			})?;350			Self::deposit_event(Event::InvulnerableRemoved { invulnerable: who });351			Ok(().into())352		}353354		/// Purchase a license on block collation for this account.355		/// It does not make it a collator candidate, use `onboard` afterward. The account must356		/// (a) already have registered session keys and (b) be able to reserve the `LicenseBond`.357		///358		/// This call is not available to `Invulnerable` collators.359		#[pallet::call_index(2)]360		#[pallet::weight(<T as Config>::WeightInfo::get_license(T::MaxCollators::get()))]361		pub fn get_license(origin: OriginFor<T>) -> DispatchResultWithPostInfo {362			// register_as_candidate363			let who = ensure_signed(origin)?;364365			if LicenseDepositOf::<T>::contains_key(&who) {366				return Err(Error::<T>::AlreadyHoldingLicense.into());367			}368369			let validator_key = T::ValidatorIdOf::convert(who.clone())370				.ok_or(Error::<T>::NoAssociatedValidatorId)?;371			ensure!(372				T::ValidatorRegistration::is_registered(&validator_key),373				Error::<T>::ValidatorNotRegistered374			);375376			let deposit = T::LicenseBond::get();377378			T::Currency::hold(&T::LicenceBondIdentifier::get(), &who, deposit)?;379			LicenseDepositOf::<T>::insert(who.clone(), deposit);380381			Self::deposit_event(Event::LicenseObtained {382				account_id: who,383				deposit,384			});385			Ok(().into()) // Some(T::WeightInfo::register_as_candidate(current_count as u32)).into())386		}387388		/// Register this account as a candidate for collators for next sessions.389		/// The account must already hold a license, and cannot offboard immediately during a session.390		///391		/// This call is not available to `Invulnerable` collators.392		#[pallet::call_index(3)]393		#[pallet::weight(<T as Config>::WeightInfo::onboard(T::MaxCollators::get()))]394		pub fn onboard(origin: OriginFor<T>) -> DispatchResultWithPostInfo {395			// register_as_candidate396			let who = ensure_signed(origin)?;397398			// ensure the user obtained the license.399			ensure!(400				LicenseDepositOf::<T>::contains_key(&who),401				Error::<T>::NoLicense402			);403			// ensure we are below limit.404			let length = <Candidates<T>>::decode_len().unwrap_or_default()405				+ <Invulnerables<T>>::decode_len().unwrap_or_default();406			ensure!(407				(length as u32) < T::DesiredCollators::get(),408				Error::<T>::TooManyCandidates409			);410			ensure!(411				!Self::invulnerables().contains(&who),412				Error::<T>::AlreadyInvulnerable413			);414415			let current_count =416				<Candidates<T>>::try_mutate(|candidates| -> Result<usize, DispatchError> {417					if candidates.iter().any(|candidate| *candidate == who) {418						Err(Error::<T>::AlreadyCandidate)?419					} else {420						candidates421							.try_push(who.clone())422							.map_err(|_| Error::<T>::TooManyCandidates)?;423						// First authored block is current block plus kick threshold to handle session delay424						<LastAuthoredBlock<T>>::insert(425							who.clone(),426							frame_system::Pallet::<T>::block_number() + T::KickThreshold::get(),427						);428						Ok(candidates.len())429					}430				})?;431432			Self::deposit_event(Event::CandidateAdded { account_id: who });433			Ok(Some(<T as Config>::WeightInfo::onboard(current_count as u32)).into())434		}435436		/// Deregister `origin` as a collator candidate. Note that the collator can only leave on437		/// session change. The license to `onboard` later at any other time will remain.438		#[pallet::call_index(4)]439		#[pallet::weight(<T as Config>::WeightInfo::offboard(T::MaxCollators::get()))]440		pub fn offboard(origin: OriginFor<T>) -> DispatchResultWithPostInfo {441			// leave_intent442			let who = ensure_signed(origin)?;443			let current_count = Self::try_remove_candidate(&who)?;444445			Ok(Some(<T as Config>::WeightInfo::offboard(current_count as u32)).into())446		}447448		/// Forfeit `origin`'s own license. The `LicenseBond` will be unreserved immediately.449		///450		/// This call is not available to `Invulnerable` collators.451		#[pallet::call_index(5)]452		#[pallet::weight(<T as Config>::WeightInfo::release_license(T::MaxCollators::get()))]453		pub fn release_license(origin: OriginFor<T>) -> DispatchResultWithPostInfo {454			// leave_intent455			let who = ensure_signed(origin)?;456457			let current_count = Self::try_remove_candidate_and_release_license(&who, false, true)?;458459			Ok(Some(<T as Config>::WeightInfo::release_license(460				current_count as u32,461			))462			.into())463		}464465		/// Force deregister `origin` as a collator candidate as a governing authority, and revoke its license.466		/// Note that the collator can only leave on session change.467		/// The `LicenseBond` will be unreserved and returned immediately.468		///469		/// This call is, of course, not applicable to `Invulnerable` collators.470		#[pallet::call_index(6)]471		#[pallet::weight(<T as Config>::WeightInfo::force_release_license(T::MaxCollators::get()))]472		pub fn force_release_license(473			origin: OriginFor<T>,474			who: T::AccountId,475		) -> DispatchResultWithPostInfo {476			// leave_intent477			T::UpdateOrigin::ensure_origin(origin)?;478479			let current_count = Self::try_remove_candidate_and_release_license(&who, false, true)?;480481			Ok(Some(<T as Config>::WeightInfo::force_release_license(482				current_count as u32,483			))484			.into())485		}486	}487488	impl<T: Config> Pallet<T> {489		/// Get a unique, inaccessible account id from the `PotId`.490		pub fn account_id() -> T::AccountId {491			T::PotId::get().into_account_truncating()492		}493494		/// Removes a candidate and their license, optionally slashed and optionally ignoring,495		/// whether or not they actually are a candidate.496		fn try_remove_candidate_and_release_license(497			who: &T::AccountId,498			should_slash: bool,499			ignore_if_not_candidate: bool,500		) -> Result<usize, DispatchError> {501			let current_count = Self::try_remove_candidate(who);502			let current_count = if ignore_if_not_candidate503				&& current_count == Err(Error::<T>::NotCandidate.into())504			{505				<Candidates<T>>::decode_len().unwrap_or_default()506			} else {507				current_count?508			};509			Self::try_release_license(who, should_slash)?;510			Ok(current_count)511		}512513		/// Removes a candidate from the collator pool for the next session if they exist.514		fn try_remove_candidate(who: &T::AccountId) -> Result<usize, DispatchError> {515			let current_count =516				<Candidates<T>>::try_mutate(|candidates| -> Result<usize, DispatchError> {517					let index = candidates518						.iter()519						.position(|candidate| *candidate == *who)520						.ok_or(Error::<T>::NotCandidate)?;521					candidates.remove(index);522					<LastAuthoredBlock<T>>::remove(who.clone());523					Ok(candidates.len())524				})?;525			Self::deposit_event(Event::CandidateRemoved {526				account_id: who.clone(),527			});528			Ok(current_count)529		}530531		/// Removes a candidate if they exist and sends them back their deposit, optionally slashed.532		fn try_release_license(who: &T::AccountId, should_slash: bool) -> DispatchResult {533			let mut deposit_returned = BalanceOf::<T>::default();534			LicenseDepositOf::<T>::try_mutate_exists(who, |deposit| -> DispatchResult {535				if let Some(deposit) = deposit.take() {536					if should_slash {537						let slashed = T::SlashRatio::get() * deposit;538						let remaining = deposit - slashed;539540						let (imbalance, _) =541							T::Currency::slash(&T::LicenceBondIdentifier::get(), who, slashed);542						deposit_returned = remaining;543544						T::Currency::resolve(&T::TreasuryAccountId::get(), imbalance)545							.map_err(|_| DispatchError::Other("Failed to deposit imbalance"))?;546					} else {547						deposit_returned = deposit;548					}549550					T::Currency::release(551						&T::LicenceBondIdentifier::get(),552						who,553						deposit_returned,554						Precision::Exact,555					)?;556					Ok(())557				} else {558					Err(Error::<T>::NoLicense.into())559				}560			})?;561			Self::deposit_event(Event::LicenseReleased {562				account_id: who.clone(),563				deposit_returned,564			});565			Ok(())566		}567568		/// Assemble the current set of candidates and invulnerables into the next collator set.569		///570		/// This is done on the fly, as frequent as we are told to do so, as the session manager.571		pub fn assemble_collators(572			candidates: BoundedVec<T::AccountId, T::MaxCollators>,573		) -> Vec<T::AccountId> {574			let mut collators = Self::invulnerables().to_vec();575			collators.extend(candidates);576			collators577		}578579		/// Kicks out candidates that did not produce a block in the kick threshold580		/// and **confiscates** their deposits to the treasury.581		pub fn kick_stale_candidates(582			candidates: BoundedVec<T::AccountId, T::MaxCollators>,583		) -> BoundedVec<T::AccountId, T::MaxCollators> {584			let now = frame_system::Pallet::<T>::block_number();585			let kick_threshold = T::KickThreshold::get();586			candidates587				.into_iter()588				.filter_map(|c| {589					let last_block = <LastAuthoredBlock<T>>::get(c.clone());590					let since_last = now.saturating_sub(last_block);591					if since_last < kick_threshold {592						Some(c)593					} else {594						let outcome = Self::try_remove_candidate_and_release_license(&c, true, false);595						if let Err(why) = outcome {596							log::warn!("Failed to kick collator and release license {:?}", why);597							debug_assert!(false, "failed to kick collator and release license {why:?}");598						}599						None600					}601				})602				.collect::<Vec<_>>()603				.try_into()604				.expect("filter_map operation can't result in a bounded vec larger than its original; qed")605		}606	}607608	/// Keep track of number of authored blocks per authority, uncles are counted as well since609	/// they're a valid proof of being online.610	impl<T: Config + pallet_authorship::Config>611		pallet_authorship::EventHandler<T::AccountId, T::BlockNumber> for Pallet<T>612	{613		fn note_author(author: T::AccountId) {614			let pot = Self::account_id();615			// assumes an ED will be sent to pot.616			let reward = T::Currency::balance(&pot)617				.checked_sub(&T::Currency::minimum_balance())618				.unwrap_or_else(Zero::zero)619				.div(2u32.into());620621			if !reward.is_zero() {622				// `reward` is half of pot account minus ED, this should never fail.623				let _success = T::Currency::transfer(&pot, &author, reward, Preservation::Preserve);624				debug_assert!(_success.is_ok());625			}626			<LastAuthoredBlock<T>>::insert(author, frame_system::Pallet::<T>::block_number());627628			frame_system::Pallet::<T>::register_extra_weight_unchecked(629				<T as Config>::WeightInfo::note_author(),630				DispatchClass::Mandatory,631			);632		}633	}634635	/// Play the role of the session manager.636	impl<T: Config> SessionManager<T::AccountId> for Pallet<T> {637		fn new_session(index: SessionIndex) -> Option<Vec<T::AccountId>> {638			log::info!(639				"assembling new collators for new session {} at #{:?}",640				index,641				<frame_system::Pallet<T>>::block_number(),642			);643644			let candidates = Self::candidates();645			let candidates_len_before = candidates.len();646			let active_candidates = Self::kick_stale_candidates(candidates);647			let removed = candidates_len_before - active_candidates.len();648			let result = Self::assemble_collators(active_candidates);649650			frame_system::Pallet::<T>::register_extra_weight_unchecked(651				<T as Config>::WeightInfo::new_session(652					candidates_len_before as u32,653					removed as u32,654				),655				DispatchClass::Mandatory,656			);657			Some(result)658		}659		fn start_session(_: SessionIndex) {660			// we don't care.661		}662		fn end_session(_: SessionIndex) {663			// we don't care.664		}665	}666}
modifiedpallets/collator-selection/src/mock.rsdiffbeforeafterboth
--- a/pallets/collator-selection/src/mock.rs
+++ b/pallets/collator-selection/src/mock.rs
@@ -63,7 +63,6 @@
 		Balances: pallet_balances::{Pallet, Call, Storage, Config<T>, Event<T>},
 		CollatorSelection: collator_selection::{Pallet, Call, Storage, Event<T>},
 		Authorship: pallet_authorship::{Pallet, Storage},
-		Configuration: pallet_configuration::{Pallet, Call, Storage, Event<T>},
 	}
 );
 
@@ -216,21 +215,6 @@
 	pub AppPromotionDailyRate: Perbill = Perbill::from_rational(5u32, 10_000);
 	pub const DayRelayBlocks: u32 = 1;
 	pub const LicenceBondIdentifier: [u8; 16] = *b"licenceidentifie";
-}
-
-impl pallet_configuration::Config for Test {
-	type RuntimeEvent = RuntimeEvent;
-	type Currency = Balances;
-	type DefaultCollatorSelectionMaxCollators = MaxCollators;
-	type DefaultCollatorSelectionKickThreshold = KickThreshold;
-	type DefaultCollatorSelectionLicenseBond = LicenseBond;
-	// the following constants we don't care about
-	type DefaultWeightToFeeCoefficient = DefaultWeightToFeeCoefficient;
-	type DefaultMinGasPrice = DefaultMinGasPrice;
-	type MaxXcmAllowedLocations = MaxXcmAllowedLocations;
-	type AppPromotionDailyRate = AppPromotionDailyRate;
-	type DayRelayBlocks = DayRelayBlocks;
-	type WeightInfo = pallet_configuration::weights::SubstrateWeight<Self>;
 }
 
 ord_parameter_types! {
@@ -246,11 +230,7 @@
 pub struct IsRegistered;
 impl ValidatorRegistration<u64> for IsRegistered {
 	fn is_registered(id: &u64) -> bool {
-		if *id == 7u64 {
-			false
-		} else {
-			true
-		}
+		*id != 7u64
 	}
 }
 
@@ -265,6 +245,10 @@
 	type ValidatorIdOf = IdentityCollator;
 	type ValidatorRegistration = IsRegistered;
 	type LicenceBondIdentifier = LicenceBondIdentifier;
+	type Currency = Balances;
+	type DesiredCollators = MaxCollators;
+	type LicenseBond = LicenseBond;
+	type KickThreshold = KickThreshold;
 	type WeightInfo = ();
 }
 
@@ -277,7 +261,11 @@
 
 	let ed = <Test as pallet_balances::Config>::ExistentialDeposit::get();
 
-	let balances = vec![(1, 100), (2, 100), (3, 100), (4, 100), (5, 100), (33, ed)];
+	let balances: Vec<(u64, u64)> = (1..=<Test as Config>::DesiredCollators::get() as u64 + 1)
+		.map(|i| (i, 100))
+		.chain(core::iter::once((33, ed)))
+		.collect();
+
 	let keys = balances
 		.iter()
 		.map(|&(i, _)| {
modifiedpallets/collator-selection/src/tests.rsdiffbeforeafterboth
--- a/pallets/collator-selection/src/tests.rs
+++ b/pallets/collator-selection/src/tests.rs
@@ -30,19 +30,13 @@
 // See the License for the specific language governing permissions and
 // limitations under the License.
 
-use crate as collator_selection;
+use crate::{self as collator_selection, Config};
 use crate::{mock::*, Error};
 use frame_support::{
 	assert_noop, assert_ok,
 	traits::{fungible, GenesisBuild, OnInitialize},
 };
-use frame_system::RawOrigin;
 use sp_runtime::{traits::BadOrigin, TokenError};
-use pallet_configuration::{
-	CollatorSelectionDesiredCollatorsOverride as DesiredCollators,
-	CollatorSelectionKickThresholdOverride as KickThreshold,
-	CollatorSelectionLicenseBondOverride as LicenseBond,
-};
 use scale_info::prelude::*;
 
 fn get_license_and_onboard(account_id: <Test as frame_system::Config>::AccountId) {
@@ -57,9 +51,6 @@
 #[test]
 fn basic_setup_works() {
 	new_test_ext().execute_with(|| {
-		assert_eq!(<DesiredCollators<Test>>::get(), 5);
-		assert_eq!(<LicenseBond<Test>>::get(), 10);
-
 		assert!(CollatorSelection::candidates().is_empty());
 		assert_eq!(CollatorSelection::invulnerables(), vec![1, 2]);
 	});
@@ -120,51 +111,6 @@
 		assert_noop!(
 			CollatorSelection::remove_invulnerable(RuntimeOrigin::signed(RootAccount::get()), 1),
 			Error::<Test>::TooFewInvulnerables
-		);
-	});
-}
-
-#[test]
-fn set_desired_collators_works() {
-	new_test_ext().execute_with(|| {
-		// given
-		assert_eq!(<DesiredCollators<Test>>::get(), 5);
-
-		// can set
-		assert_ok!(Configuration::set_collator_selection_desired_collators(
-			RawOrigin::Root.into(),
-			Some(7)
-		));
-		assert_eq!(<DesiredCollators<Test>>::get(), 7);
-
-		// rejects bad origin
-		assert_noop!(
-			Configuration::set_collator_selection_desired_collators(
-				RuntimeOrigin::signed(1),
-				Some(8)
-			),
-			BadOrigin
-		);
-	});
-}
-
-#[test]
-fn set_license_bond() {
-	new_test_ext().execute_with(|| {
-		// given
-		assert_eq!(<LicenseBond<Test>>::get(), 10);
-
-		// can set
-		assert_ok!(Configuration::set_collator_selection_license_bond(
-			RawOrigin::Root.into(),
-			Some(7)
-		));
-		assert_eq!(<LicenseBond<Test>>::get(), 7);
-
-		// rejects bad origin.
-		assert_noop!(
-			Configuration::set_collator_selection_license_bond(RuntimeOrigin::signed(1), Some(8)),
-			BadOrigin
 		);
 	});
 }
@@ -187,26 +133,19 @@
 #[test]
 fn cannot_onboard_candidate_if_too_many() {
 	new_test_ext().execute_with(|| {
-		// reset desired candidates
-		<pallet_configuration::CollatorSelectionDesiredCollatorsOverride<Test>>::put(0);
-
-		// can still get a license.
-		assert_ok!(CollatorSelection::get_license(RuntimeOrigin::signed(4)));
-
-		// can't accept anyone anymore.
-		assert_noop!(
-			CollatorSelection::onboard(RuntimeOrigin::signed(4)),
-			Error::<Test>::TooManyCandidates,
-		);
-
-		// reset desired candidates to invulnerables + 1
-		<pallet_configuration::CollatorSelectionDesiredCollatorsOverride<Test>>::put(3);
-		assert_ok!(CollatorSelection::onboard(RuntimeOrigin::signed(4)));
+		// can accept desired value of collators.
+		for c in 3u64..=(<Test as Config>::DesiredCollators::get()).into() {
+			assert_ok!(CollatorSelection::get_license(RuntimeOrigin::signed(c)));
+			assert_ok!(CollatorSelection::onboard(RuntimeOrigin::signed(c)));
+		}
 
 		// but no more.
-		assert_ok!(CollatorSelection::get_license(RuntimeOrigin::signed(5)));
+		let undesired_collator = (<Test as Config>::DesiredCollators::get() + 1) as u64;
+		assert_ok!(CollatorSelection::get_license(RuntimeOrigin::signed(
+			undesired_collator
+		)));
 		assert_noop!(
-			CollatorSelection::onboard(RuntimeOrigin::signed(5)),
+			CollatorSelection::onboard(RuntimeOrigin::signed(undesired_collator)),
 			Error::<Test>::TooManyCandidates,
 		);
 	})
@@ -227,8 +166,8 @@
 fn cannot_obtain_license_if_poor() {
 	new_test_ext().execute_with(|| {
 		let ed = <Test as pallet_balances::Config>::ExistentialDeposit::get();
-		assert_eq!(Balances::free_balance(&3), 100);
-		assert_eq!(Balances::free_balance(&33), ed);
+		assert_eq!(Balances::free_balance(3), 100);
+		assert_eq!(Balances::free_balance(33), ed);
 
 		// works
 		assert_ok!(CollatorSelection::get_license(RuntimeOrigin::signed(3)));
@@ -266,21 +205,18 @@
 #[test]
 fn becoming_candidate_works() {
 	new_test_ext().execute_with(|| {
-		// given
-		assert_eq!(<DesiredCollators<Test>>::get(), 5);
-		assert_eq!(<LicenseBond<Test>>::get(), 10);
 		assert_eq!(CollatorSelection::candidates(), Vec::new());
 		assert_eq!(CollatorSelection::invulnerables(), vec![1, 2]);
 
 		// take two endowed, non-invulnerables accounts.
-		assert_eq!(Balances::free_balance(&3), 100);
-		assert_eq!(Balances::free_balance(&4), 100);
+		assert_eq!(Balances::free_balance(3), 100);
+		assert_eq!(Balances::free_balance(4), 100);
 
 		get_license_and_onboard(3);
 		get_license_and_onboard(4);
 
-		assert_eq!(Balances::free_balance(&3), 90);
-		assert_eq!(Balances::free_balance(&4), 90);
+		assert_eq!(Balances::free_balance(3), 90);
+		assert_eq!(Balances::free_balance(4), 90);
 
 		assert_eq!(CollatorSelection::candidates().len(), 2);
 	});
@@ -513,7 +449,7 @@
 		assert_eq!(SessionHandlerCollators::get(), vec![1, 2, 3, 4]);
 
 		assert_eq!(CollatorSelection::candidates(), vec![4]);
-		assert_eq!(<KickThreshold<Test>>::get(), 10);
+		// assert_eq!(<KickThreshold<Test>>::get(), 10);
 		assert_eq!(CollatorSelection::last_authored_block(4), 20);
 
 		initialize_to_block(30);
modifiedpallets/configuration/CHANGELOG.mddiffbeforeafterboth
--- a/pallets/configuration/CHANGELOG.md
+++ b/pallets/configuration/CHANGELOG.md
@@ -3,7 +3,12 @@
 All notable changes to this project will be documented in this file.
 
 <!-- bureaucrate goes here -->
+## [0.2.0] - 2023-06-27
+
+### Major change
 
+- Architecture fixed: in the configuration of this pallet, bounds on the associated type were determined not by the functional requirements for this pallet itself, but by the pallet that had tight coupling with it.
+
 ## [0.1.3] - 2022-12-29
 
 ### Added
@@ -20,4 +25,4 @@
 
 ### Other changes
 
-- build: Upgrade polkadot to v0.9.27 2c498572636f2b34d53b1c51b7283a761a7dc90a
\ No newline at end of file
+- build: Upgrade polkadot to v0.9.27 2c498572636f2b34d53b1c51b7283a761a7dc90a
modifiedpallets/configuration/Cargo.tomldiffbeforeafterboth
--- a/pallets/configuration/Cargo.toml
+++ b/pallets/configuration/Cargo.toml
@@ -1,7 +1,7 @@
 [package]
 edition = "2021"
 name = "pallet-configuration"
-version = "0.1.3"
+version = "0.2.0"
 
 [dependencies]
 # Note: `package = "parity-scale-codec"` must be supplied since the `Encode` macro searches for it.
@@ -22,11 +22,11 @@
 default = ["std"]
 runtime-benchmarks = ["frame-benchmarking"]
 std = [
+	"codec/std",
 	"fp-evm/std",
 	"frame-benchmarking/std",
 	"frame-support/std",
 	"frame-system/std",
-	"codec/std",
 	"sp-arithmetic/std",
 	"sp-core/std",
 	"sp-std/std",
modifiedpallets/configuration/src/benchmarking.rsdiffbeforeafterboth
--- a/pallets/configuration/src/benchmarking.rs
+++ b/pallets/configuration/src/benchmarking.rs
@@ -19,7 +19,7 @@
 use super::*;
 use frame_benchmarking::benchmarks;
 use frame_system::{EventRecord, RawOrigin};
-use frame_support::{assert_ok, traits::fungible::Inspect};
+use frame_support::assert_ok;
 
 fn assert_last_event<T: Config>(generic_event: <T as Config>::RuntimeEvent) {
 	let events = frame_system::Pallet::<T>::events();
@@ -30,7 +30,10 @@
 }
 
 benchmarks! {
-	where_clause { where T: Config }
+	where_clause { where
+		T: Config,
+		T::Balance: From<u32>
+	}
 
 	set_weight_to_fee_coefficient_override {
 		let coeff: u64 = 999;
@@ -60,7 +63,7 @@
 		let max: u32 = 999;
 	}: {
 		assert_ok!(
-			<Pallet<T>>::set_collator_selection_desired_collators(RawOrigin::Root.into(), Some(max.clone()))
+			<Pallet<T>>::set_collator_selection_desired_collators(RawOrigin::Root.into(), Some(max))
 		);
 	}
 	verify {
@@ -68,10 +71,10 @@
 	}
 
 	set_collator_selection_license_bond {
-		let bond_cost: Option<BalanceOf<T>> = Some(T::Currency::minimum_balance() * 10u32.into());
+		let bond_cost: Option<T::Balance> = Some(1000u32.into());
 	}: {
 		assert_ok!(
-			<Pallet<T>>::set_collator_selection_license_bond(RawOrigin::Root.into(), bond_cost.clone())
+			<Pallet<T>>::set_collator_selection_license_bond(RawOrigin::Root.into(), bond_cost)
 		);
 	}
 	verify {
@@ -82,7 +85,7 @@
 		let threshold: Option<T::BlockNumber> = Some(900u32.into());
 	}: {
 		assert_ok!(
-			<Pallet<T>>::set_collator_selection_kick_threshold(RawOrigin::Root.into(), threshold.clone())
+			<Pallet<T>>::set_collator_selection_kick_threshold(RawOrigin::Root.into(), threshold)
 		);
 	}
 	verify {
modifiedpallets/configuration/src/lib.rsdiffbeforeafterboth
--- a/pallets/configuration/src/lib.rs
+++ b/pallets/configuration/src/lib.rs
@@ -22,6 +22,7 @@
 	pallet,
 	weights::{WeightToFeePolynomial, WeightToFeeCoefficients, WeightToFeeCoefficient, Weight},
 	traits::Get,
+	Parameter,
 };
 use codec::{Decode, Encode, MaxEncodedLen};
 use scale_info::TypeInfo;
@@ -42,27 +43,33 @@
 mod pallet {
 	use super::*;
 	use frame_support::{
-		traits::{fungible, Get},
-		pallet_prelude::{StorageValue, ValueQuery, DispatchResult, IsType},
+		traits::{Get},
+		pallet_prelude::{
+			StorageValue, ValueQuery, DispatchResult, IsType, Member, MaybeSerializeDeserialize,
+		},
 		log,
+		dispatch::{Codec, fmt::Debug},
 	};
-	use frame_system::{pallet_prelude::OriginFor, ensure_root, Config as SystemConfig};
-
+	use frame_system::{pallet_prelude::OriginFor, ensure_root};
+	use sp_arithmetic::{FixedPointOperand, traits::AtLeast32BitUnsigned};
 	pub use crate::weights::WeightInfo;
-	pub type BalanceOf<T> =
-		<<T as Config>::Currency as fungible::Inspect<<T as SystemConfig>::AccountId>>::Balance;
 
 	#[pallet::config]
 	pub trait Config: frame_system::Config {
 		/// Overarching event type.
 		type RuntimeEvent: From<Event<Self>> + IsType<<Self as frame_system::Config>::RuntimeEvent>;
 
-		type Currency: fungible::Inspect<Self::AccountId>
-			+ fungible::Mutate<Self::AccountId>
-			+ fungible::MutateFreeze<Self::AccountId>
-			+ fungible::InspectHold<Self::AccountId>
-			+ fungible::MutateHold<Self::AccountId>
-			+ fungible::BalancedHold<Self::AccountId>;
+		type Balance: Parameter
+			+ Member
+			+ AtLeast32BitUnsigned
+			+ Codec
+			+ Default
+			+ Copy
+			+ MaybeSerializeDeserialize
+			+ Debug
+			+ MaxEncodedLen
+			+ TypeInfo
+			+ FixedPointOperand;
 
 		#[pallet::constant]
 		type DefaultWeightToFeeCoefficient: Get<u64>;
@@ -79,7 +86,7 @@
 		#[pallet::constant]
 		type DefaultCollatorSelectionMaxCollators: Get<u32>;
 		#[pallet::constant]
-		type DefaultCollatorSelectionLicenseBond: Get<BalanceOf<Self>>;
+		type DefaultCollatorSelectionLicenseBond: Get<Self::Balance>;
 		#[pallet::constant]
 		type DefaultCollatorSelectionKickThreshold: Get<Self::BlockNumber>;
 
@@ -94,7 +101,7 @@
 			desired_collators: Option<u32>,
 		},
 		NewCollatorLicenseBond {
-			bond_cost: Option<BalanceOf<T>>,
+			bond_cost: Option<T::Balance>,
 		},
 		NewCollatorKickThreshold {
 			length_in_blocks: Option<T::BlockNumber>,
@@ -130,7 +137,7 @@
 
 	#[pallet::storage]
 	pub type CollatorSelectionLicenseBondOverride<T: Config> = StorageValue<
-		Value = BalanceOf<T>,
+		Value = T::Balance,
 		QueryKind = ValueQuery,
 		OnEmpty = T::DefaultCollatorSelectionLicenseBond,
 	>;
@@ -221,7 +228,7 @@
 		#[pallet::weight(T::WeightInfo::set_collator_selection_license_bond())]
 		pub fn set_collator_selection_license_bond(
 			origin: OriginFor<T>,
-			amount: Option<BalanceOf<T>>,
+			amount: Option<<T as Config>::Balance>,
 		) -> DispatchResult {
 			ensure_root(origin)?;
 			if let Some(amount) = amount {
modifiedruntime/common/config/pallets/collator_selection.rsdiffbeforeafterboth
--- a/runtime/common/config/pallets/collator_selection.rs
+++ b/runtime/common/config/pallets/collator_selection.rs
@@ -23,7 +23,10 @@
 };
 use sp_runtime::Perbill;
 use up_common::constants::{UNIQUE, MILLIUNIQUE};
-
+use pallet_configuration::{
+	CollatorSelectionKickThresholdOverride, CollatorSelectionLicenseBondOverride,
+	CollatorSelectionDesiredCollatorsOverride,
+};
 parameter_types! {
 	pub const SessionOffset: BlockNumber = 0;
 }
@@ -60,6 +63,9 @@
 	pub const MaxAdditionalFields: u32 = 100;
 	pub const MaxRegistrars: u32 = 20;
 	pub const LicenceBondIdentifier: [u8; 16] = *b"licenceidentifie";
+	pub LicenseBond: Balance =  CollatorSelectionLicenseBondOverride::<Runtime>::get();
+	pub DesiredCollators: u32 = CollatorSelectionDesiredCollatorsOverride::<Runtime>::get();
+	pub KickThreshold: BlockNumber = CollatorSelectionKickThresholdOverride::<Runtime>::get();
 }
 
 impl pallet_identity::Config for Runtime {
@@ -84,6 +90,7 @@
 
 impl pallet_collator_selection::Config for Runtime {
 	type RuntimeEvent = RuntimeEvent;
+	type Currency = Balances;
 	// We allow root only to execute privileged collator selection operations.
 	type UpdateOrigin = EnsureRoot<AccountId>;
 	type TreasuryAccountId = TreasuryAccountId;
@@ -95,4 +102,7 @@
 	type ValidatorRegistration = Session;
 	type WeightInfo = pallet_collator_selection::weights::SubstrateWeight<Runtime>;
 	type LicenceBondIdentifier = LicenceBondIdentifier;
+	type DesiredCollators = DesiredCollators;
+	type LicenseBond = LicenseBond;
+	type KickThreshold = KickThreshold;
 }
modifiedruntime/common/config/pallets/mod.rsdiffbeforeafterboth
--- a/runtime/common/config/pallets/mod.rs
+++ b/runtime/common/config/pallets/mod.rs
@@ -129,7 +129,7 @@
 
 impl pallet_configuration::Config for Runtime {
 	type RuntimeEvent = RuntimeEvent;
-	type Currency = Balances;
+	type Balance = Balance;
 	type DefaultWeightToFeeCoefficient = ConstU64<{ up_common::constants::WEIGHT_TO_FEE_COEFF }>;
 	type DefaultMinGasPrice = ConstU64<{ up_common::constants::MIN_GAS_PRICE }>;
 	type DefaultCollatorSelectionMaxCollators = MaxCollators;
modifiedtests/src/benchmarks/mintFee/index.tsdiffbeforeafterboth
--- a/tests/src/benchmarks/mintFee/index.ts
+++ b/tests/src/benchmarks/mintFee/index.ts
@@ -360,7 +360,7 @@
           susbstrateReceiver.addressRaw,
           PROPERTIES.slice(0, setup.propertiesNumber),
         )
-        .send({from: ethSigner, gas: 25_000_000});
+        .send({from: ethSigner});
     },
   );