diff --git a/cumulus/pallets/collator-selection/src/benchmarking.rs b/cumulus/pallets/collator-selection/src/benchmarking.rs
index 49999dc114df7bdc94dca2f8c563d7c1d8bb6083..fa95303495dd1039f93f79baf93dc5301a31840d 100644
--- a/cumulus/pallets/collator-selection/src/benchmarking.rs
+++ b/cumulus/pallets/collator-selection/src/benchmarking.rs
@@ -25,14 +25,11 @@ use codec::Decode;
 use frame_benchmarking::{
 	account, impl_benchmark_test_suite, v2::*, whitelisted_caller, BenchmarkError,
 };
-use frame_support::{
-	dispatch::DispatchResult,
-	traits::{Currency, EnsureOrigin, Get, ReservableCurrency},
-};
+use frame_support::traits::{Currency, EnsureOrigin, Get, ReservableCurrency};
 use frame_system::{pallet_prelude::BlockNumberFor, EventRecord, RawOrigin};
 use pallet_authorship::EventHandler;
 use pallet_session::{self as session, SessionManager};
-use sp_std::prelude::*;
+use sp_std::{cmp, prelude::*};
 
 pub type BalanceOf<T> =
 	<<T as Config>::Currency as Currency<<T as frame_system::Config>::AccountId>>::Balance;
@@ -94,7 +91,7 @@ fn register_candidates<T: Config>(count: u32) {
 	assert!(<CandidacyBond<T>>::get() > 0u32.into(), "Bond cannot be zero!");
 
 	for who in candidates {
-		T::Currency::make_free_balance_be(&who, <CandidacyBond<T>>::get() * 2u32.into());
+		T::Currency::make_free_balance_be(&who, <CandidacyBond<T>>::get() * 3u32.into());
 		<CollatorSelection<T>>::register_as_candidate(RawOrigin::Signed(who).into()).unwrap();
 	}
 }
@@ -107,8 +104,11 @@ fn min_candidates<T: Config>() -> u32 {
 
 fn min_invulnerables<T: Config>() -> u32 {
 	let min_collators = T::MinEligibleCollators::get();
-	let candidates_length = <Candidates<T>>::get().len();
-	min_collators.saturating_sub(candidates_length.try_into().unwrap())
+	let candidates_length = <CandidateList<T>>::decode_len()
+		.unwrap_or_default()
+		.try_into()
+		.unwrap_or_default();
+	min_collators.saturating_sub(candidates_length)
 }
 
 #[benchmarks(where T: pallet_authorship::Config + session::Config)]
@@ -160,22 +160,19 @@ mod benchmarks {
 				.unwrap();
 		}
 		// ... and register them.
-		for (who, _) in candidates {
+		for (who, _) in candidates.iter() {
 			let deposit = <CandidacyBond<T>>::get();
-			T::Currency::make_free_balance_be(&who, deposit * 1000_u32.into());
-			let incoming = CandidateInfo { who: who.clone(), deposit };
-			<Candidates<T>>::try_mutate(|candidates| -> DispatchResult {
-				if !candidates.iter().any(|candidate| candidate.who == who) {
-					T::Currency::reserve(&who, deposit)?;
-					candidates.try_push(incoming).expect("we've respected the bounded vec limit");
-					<LastAuthoredBlock<T>>::insert(
-						who.clone(),
-						frame_system::Pallet::<T>::block_number() + T::KickThreshold::get(),
-					);
-				}
-				Ok(())
+			T::Currency::make_free_balance_be(who, deposit * 1000_u32.into());
+			<CandidateList<T>>::try_mutate(|list| {
+				list.try_push(CandidateInfo { who: who.clone(), deposit }).unwrap();
+				Ok::<(), BenchmarkError>(())
 			})
-			.expect("only returns ok");
+			.unwrap();
+			T::Currency::reserve(who, deposit)?;
+			<LastAuthoredBlock<T>>::insert(
+				who.clone(),
+				frame_system::Pallet::<T>::block_number() + T::KickThreshold::get(),
+			);
 		}
 
 		// now we need to fill up invulnerables
@@ -226,10 +223,27 @@ mod benchmarks {
 	}
 
 	#[benchmark]
-	fn set_candidacy_bond() -> Result<(), BenchmarkError> {
-		let bond_amount: BalanceOf<T> = T::Currency::minimum_balance() * 10u32.into();
+	fn set_candidacy_bond(
+		c: Linear<0, { T::MaxCandidates::get() }>,
+		k: Linear<0, { T::MaxCandidates::get() }>,
+	) -> Result<(), BenchmarkError> {
+		let initial_bond_amount: BalanceOf<T> = T::Currency::minimum_balance() * 2u32.into();
+		<CandidacyBond<T>>::put(initial_bond_amount);
+		register_validators::<T>(c);
+		register_candidates::<T>(c);
+		let kicked = cmp::min(k, c);
 		let origin =
 			T::UpdateOrigin::try_successful_origin().map_err(|_| BenchmarkError::Weightless)?;
+		let bond_amount = if k > 0 {
+			<CandidateList<T>>::mutate(|candidates| {
+				for info in candidates.iter_mut().skip(kicked as usize) {
+					info.deposit = T::Currency::minimum_balance() * 3u32.into();
+				}
+			});
+			T::Currency::minimum_balance() * 3u32.into()
+		} else {
+			T::Currency::minimum_balance()
+		};
 
 		#[extrinsic_call]
 		_(origin as T::RuntimeOrigin, bond_amount);
@@ -238,6 +252,35 @@ mod benchmarks {
 		Ok(())
 	}
 
+	#[benchmark]
+	fn update_bond(
+		c: Linear<{ min_candidates::<T>() + 1 }, { T::MaxCandidates::get() }>,
+	) -> Result<(), BenchmarkError> {
+		<CandidacyBond<T>>::put(T::Currency::minimum_balance());
+		<DesiredCandidates<T>>::put(c);
+
+		register_validators::<T>(c);
+		register_candidates::<T>(c);
+
+		let caller = <CandidateList<T>>::get()[0].who.clone();
+		v2::whitelist!(caller);
+
+		let bond_amount: BalanceOf<T> =
+			T::Currency::minimum_balance() + T::Currency::minimum_balance();
+
+		#[extrinsic_call]
+		_(RawOrigin::Signed(caller.clone()), bond_amount);
+
+		assert_last_event::<T>(
+			Event::CandidateBondUpdated { account_id: caller, deposit: bond_amount }.into(),
+		);
+		assert!(
+			<CandidateList<T>>::get().iter().last().unwrap().deposit ==
+				T::Currency::minimum_balance() * 2u32.into()
+		);
+		Ok(())
+	}
+
 	// worse case is when we have all the max-candidate slots filled except one, and we fill that
 	// one.
 	#[benchmark]
@@ -267,6 +310,36 @@ mod benchmarks {
 		);
 	}
 
+	#[benchmark]
+	fn take_candidate_slot(c: Linear<{ min_candidates::<T>() + 1 }, { T::MaxCandidates::get() }>) {
+		<CandidacyBond<T>>::put(T::Currency::minimum_balance());
+		<DesiredCandidates<T>>::put(1);
+
+		register_validators::<T>(c);
+		register_candidates::<T>(c);
+
+		let caller: T::AccountId = whitelisted_caller();
+		let bond: BalanceOf<T> = T::Currency::minimum_balance() * 10u32.into();
+		T::Currency::make_free_balance_be(&caller, bond);
+
+		<session::Pallet<T>>::set_keys(
+			RawOrigin::Signed(caller.clone()).into(),
+			keys::<T>(c + 1),
+			Vec::new(),
+		)
+		.unwrap();
+
+		let target = <CandidateList<T>>::get().iter().last().unwrap().who.clone();
+
+		#[extrinsic_call]
+		_(RawOrigin::Signed(caller.clone()), bond / 2u32.into(), target.clone());
+
+		assert_last_event::<T>(
+			Event::CandidateReplaced { old: target, new: caller, deposit: bond / 2u32.into() }
+				.into(),
+		);
+	}
+
 	// worse case is the last candidate leaving.
 	#[benchmark]
 	fn leave_intent(c: Linear<{ min_candidates::<T>() + 1 }, { T::MaxCandidates::get() }>) {
@@ -276,7 +349,7 @@ mod benchmarks {
 		register_validators::<T>(c);
 		register_candidates::<T>(c);
 
-		let leaving = <Candidates<T>>::get().last().unwrap().who.clone();
+		let leaving = <CandidateList<T>>::get().iter().last().unwrap().who.clone();
 		v2::whitelist!(leaving);
 
 		#[extrinsic_call]
@@ -323,31 +396,37 @@ mod benchmarks {
 
 		let new_block: BlockNumberFor<T> = 1800u32.into();
 		let zero_block: BlockNumberFor<T> = 0u32.into();
-		let candidates = <Candidates<T>>::get();
+		let candidates: Vec<T::AccountId> = <CandidateList<T>>::get()
+			.iter()
+			.map(|candidate_info| candidate_info.who.clone())
+			.collect();
 
 		let non_removals = c.saturating_sub(r);
 
 		for i in 0..c {
-			<LastAuthoredBlock<T>>::insert(candidates[i as usize].who.clone(), zero_block);
+			<LastAuthoredBlock<T>>::insert(candidates[i as usize].clone(), zero_block);
 		}
 
 		if non_removals > 0 {
 			for i in 0..non_removals {
-				<LastAuthoredBlock<T>>::insert(candidates[i as usize].who.clone(), new_block);
+				<LastAuthoredBlock<T>>::insert(candidates[i as usize].clone(), new_block);
 			}
 		} else {
 			for i in 0..c {
-				<LastAuthoredBlock<T>>::insert(candidates[i as usize].who.clone(), new_block);
+				<LastAuthoredBlock<T>>::insert(candidates[i as usize].clone(), new_block);
 			}
 		}
 
 		let min_candidates = min_candidates::<T>();
-		let pre_length = <Candidates<T>>::get().len();
+		let pre_length = <CandidateList<T>>::decode_len().unwrap_or_default();
 
 		frame_system::Pallet::<T>::set_block_number(new_block);
 
-		assert!(<Candidates<T>>::get().len() == c as usize);
-
+		let current_length: u32 = <CandidateList<T>>::decode_len()
+			.unwrap_or_default()
+			.try_into()
+			.unwrap_or_default();
+		assert!(c == current_length);
 		#[block]
 		{
 			<CollatorSelection<T> as SessionManager<_>>::new_session(0);
@@ -357,16 +436,20 @@ mod benchmarks {
 			// candidates > removals and remaining candidates > min candidates
 			// => remaining candidates should be shorter than before removal, i.e. some were
 			//    actually removed.
-			assert!(<Candidates<T>>::get().len() < pre_length);
+			assert!(<CandidateList<T>>::decode_len().unwrap_or_default() < pre_length);
 		} else if c > r && non_removals < min_candidates {
 			// candidates > removals and remaining candidates would be less than min candidates
 			// => remaining candidates should equal min candidates, i.e. some were removed up to
 			//    the minimum, but then any more were "forced" to stay in candidates.
-			assert!(<Candidates<T>>::get().len() == min_candidates as usize);
+			let current_length: u32 = <CandidateList<T>>::decode_len()
+				.unwrap_or_default()
+				.try_into()
+				.unwrap_or_default();
+			assert!(min_candidates == current_length);
 		} else {
 			// removals >= candidates, non removals must == 0
 			// can't remove more than exist
-			assert!(<Candidates<T>>::get().len() == pre_length);
+			assert!(<CandidateList<T>>::decode_len().unwrap_or_default() == pre_length);
 		}
 	}
 
diff --git a/cumulus/pallets/collator-selection/src/lib.rs b/cumulus/pallets/collator-selection/src/lib.rs
index 24493ce9d9cdc7f451f2e997615962ee5f0662e0..7449f4d68c7eacc8b07fa45f2f991c13f3d5713b 100644
--- a/cumulus/pallets/collator-selection/src/lib.rs
+++ b/cumulus/pallets/collator-selection/src/lib.rs
@@ -35,16 +35,36 @@
 //!
 //! 1. [`Invulnerables`]: a set of collators appointed by governance. These accounts will always be
 //!    collators.
-//! 2. [`Candidates`]: these are *candidates to the collation task* and may or may not be elected as
-//!    a final collator.
+//! 2. [`CandidateList`]: these are *candidates to the collation task* and may or may not be elected
+//!    as a final collator.
 //!
-//! The current implementation resolves congestion of [`Candidates`] in a first-come-first-serve
-//! manner.
+//! The current implementation resolves congestion of [`CandidateList`] through a simple auction
+//! mechanism. Candidates bid for the collator slots and at the end of the session, the auction ends
+//! and the top candidates are selected to become collators. The number of selected candidates is
+//! determined by the value of `DesiredCandidates`.
+//!
+//! Before the list reaches full capacity, candidates can register by placing the minimum bond
+//! through `register_as_candidate`. Then, if an account wants to participate in the collator slot
+//! auction, they have to replace an existing candidate by placing a greater deposit through
+//! `take_candidate_slot`. Existing candidates can increase their bids through `update_bond`.
+//!
+//! At any point, an account can take the place of another account in the candidate list if they put
+//! up a greater deposit than the target. While new joiners would like to deposit as little as
+//! possible to participate in the auction, the replacement threat incentivizes candidates to bid as
+//! close to their budget as possible in order to avoid being replaced.
+//!
+//! Candidates which are not on "winning" slots in the list can also decrease their deposits through
+//! `update_bond`, but candidates who are on top slots and try to decrease their deposits will fail
+//! in order to enforce auction mechanics and have meaningful bids.
 //!
 //! Candidates will not be allowed to get kicked or `leave_intent` if the total number of collators
 //! would fall below `MinEligibleCollators`. This is to ensure that some collators will always
 //! exist, i.e. someone is eligible to produce a block.
 //!
+//! When a new session starts, candidates with the highest deposits will be selected in order until
+//! the desired number of collators is reached. Candidates can increase or decrease their deposits
+//! between sessions in order to ensure they receive a slot in the collator list.
+//!
 //! ### Rewards
 //!
 //! The Collator Selection pallet maintains an on-chain account (the "Pot"). In each block, the
@@ -56,8 +76,8 @@
 //!
 //! To initiate rewards, an ED needs to be transferred to the pot address.
 //!
-//! Note: Eventually the Pot distribution may be modified as discussed in
-//! [this issue](https://github.com/paritytech/statemint/issues/21#issuecomment-810481073).
+//! Note: Eventually the Pot distribution may be modified as discussed in [this
+//! issue](https://github.com/paritytech/statemint/issues/21#issuecomment-810481073).
 
 #![cfg_attr(not(feature = "std"), no_std)]
 
@@ -182,9 +202,12 @@ pub mod pallet {
 
 	/// The (community, limited) collation candidates. `Candidates` and `Invulnerables` should be
 	/// mutually exclusive.
+	///
+	/// This list is sorted in ascending order by deposit and when the deposits are equal, the least
+	/// recently updated is considered greater.
 	#[pallet::storage]
-	#[pallet::getter(fn candidates)]
-	pub type Candidates<T: Config> = StorageValue<
+	#[pallet::getter(fn candidate_list)]
+	pub type CandidateList<T: Config> = StorageValue<
 		_,
 		BoundedVec<CandidateInfo<T::AccountId, BalanceOf<T>>, T::MaxCandidates>,
 		ValueQuery,
@@ -261,8 +284,12 @@ pub mod pallet {
 		NewCandidacyBond { bond_amount: BalanceOf<T> },
 		/// A new candidate joined.
 		CandidateAdded { account_id: T::AccountId, deposit: BalanceOf<T> },
+		/// Bond of a candidate updated.
+		CandidateBondUpdated { account_id: T::AccountId, deposit: BalanceOf<T> },
 		/// A candidate was removed.
 		CandidateRemoved { account_id: T::AccountId },
+		/// An account was replaced in the candidate list by another one.
+		CandidateReplaced { old: T::AccountId, new: T::AccountId, deposit: BalanceOf<T> },
 		/// An account was unable to be added to the Invulnerables because they did not have keys
 		/// registered. Other Invulnerables may have been set.
 		InvalidInvulnerableSkipped { account_id: T::AccountId },
@@ -288,12 +315,38 @@ pub mod pallet {
 		NoAssociatedValidatorId,
 		/// Validator ID is not yet registered.
 		ValidatorNotRegistered,
+		/// Could not insert in the candidate list.
+		InsertToCandidateListFailed,
+		/// Could not remove from the candidate list.
+		RemoveFromCandidateListFailed,
+		/// New deposit amount would be below the minimum candidacy bond.
+		DepositTooLow,
+		/// Could not update the candidate list.
+		UpdateCandidateListFailed,
+		/// Deposit amount is too low to take the target's slot in the candidate list.
+		InsufficientBond,
+		/// The target account to be replaced in the candidate list is not a candidate.
+		TargetIsNotCandidate,
+		/// The updated deposit amount is equal to the amount already reserved.
+		IdenticalDeposit,
+		/// Cannot lower candidacy bond while occupying a future collator slot in the list.
+		InvalidUnreserve,
 	}
 
 	#[pallet::hooks]
 	impl<T: Config> Hooks<BlockNumberFor<T>> for Pallet<T> {
 		fn integrity_test() {
 			assert!(T::MinEligibleCollators::get() > 0, "chain must require at least one collator");
+			assert!(
+				T::MaxInvulnerables::get().saturating_add(T::MaxCandidates::get()) >=
+					T::MinEligibleCollators::get(),
+				"invulnerables and candidates must be able to satisfy collator demand"
+			);
+		}
+
+		#[cfg(feature = "try-runtime")]
+		fn try_state(_: BlockNumberFor<T>) -> Result<(), sp_runtime::TryRuntimeError> {
+			Self::do_try_state()
 		}
 	}
 
@@ -307,8 +360,8 @@ pub mod pallet {
 		/// acceptable Invulnerables, and is not proposing a _set_ of new Invulnerables.
 		///
 		/// This call does not maintain mutual exclusivity of `Invulnerables` and `Candidates`. It
-		/// is recommended to use a batch of `add_invulnerable` and `remove_invulnerable` instead.
-		/// A `batch_all` can also be used to enforce atomicity. If any candidates are included in
+		/// is recommended to use a batch of `add_invulnerable` and `remove_invulnerable` instead. A
+		/// `batch_all` can also be used to enforce atomicity. If any candidates are included in
 		/// `new`, they should be removed with `remove_invulnerable_candidate` after execution.
 		///
 		/// Must be called by the `UpdateOrigin`.
@@ -319,8 +372,9 @@ pub mod pallet {
 
 			// don't wipe out the collator set
 			if new.is_empty() {
+				// Casting `u32` to `usize` should be safe on all machines running this.
 				ensure!(
-					Candidates::<T>::decode_len().unwrap_or_default() >=
+					CandidateList::<T>::decode_len().unwrap_or_default() >=
 						T::MinEligibleCollators::get() as usize,
 					Error::<T>::TooFewEligibleCollators
 				);
@@ -401,17 +455,47 @@ pub mod pallet {
 
 		/// Set the candidacy bond amount.
 		///
+		/// If the candidacy bond is increased by this call, all current candidates which have a
+		/// deposit lower than the new bond will be kicked from the list and get their deposits
+		/// back.
+		///
 		/// The origin for this call must be the `UpdateOrigin`.
 		#[pallet::call_index(2)]
-		#[pallet::weight(T::WeightInfo::set_candidacy_bond())]
+		#[pallet::weight(T::WeightInfo::set_candidacy_bond(
+			T::MaxCandidates::get(),
+			T::MaxCandidates::get()
+		))]
 		pub fn set_candidacy_bond(
 			origin: OriginFor<T>,
 			bond: BalanceOf<T>,
 		) -> DispatchResultWithPostInfo {
 			T::UpdateOrigin::ensure_origin(origin)?;
-			<CandidacyBond<T>>::put(bond);
+			let bond_increased = <CandidacyBond<T>>::mutate(|old_bond| -> bool {
+				let bond_increased = *old_bond < bond;
+				*old_bond = bond;
+				bond_increased
+			});
+			let initial_len = <CandidateList<T>>::decode_len().unwrap_or_default();
+			let kicked = (bond_increased && initial_len > 0)
+				.then(|| {
+					// Closure below returns the number of candidates which were kicked because
+					// their deposits were lower than the new candidacy bond.
+					<CandidateList<T>>::mutate(|candidates| -> usize {
+						let first_safe_candidate = candidates
+							.iter()
+							.position(|candidate| candidate.deposit >= bond)
+							.unwrap_or(initial_len);
+						let kicked_candidates = candidates.drain(..first_safe_candidate);
+						for candidate in kicked_candidates {
+							T::Currency::unreserve(&candidate.who, candidate.deposit);
+							<LastAuthoredBlock<T>>::remove(candidate.who);
+						}
+						first_safe_candidate
+					})
+				})
+				.unwrap_or_default();
 			Self::deposit_event(Event::NewCandidacyBond { bond_amount: bond });
-			Ok(().into())
+			Ok(Some(T::WeightInfo::set_candidacy_bond(initial_len as u32, kicked as u32)).into())
 		}
 
 		/// Register this account as a collator candidate. The account must (a) already have
@@ -424,8 +508,11 @@ pub mod pallet {
 			let who = ensure_signed(origin)?;
 
 			// ensure we are below limit.
-			let length = <Candidates<T>>::decode_len().unwrap_or_default();
-			ensure!((length as u32) < Self::desired_candidates(), Error::<T>::TooManyCandidates);
+			let length: u32 = <CandidateList<T>>::decode_len()
+				.unwrap_or_default()
+				.try_into()
+				.unwrap_or_default();
+			ensure!(length < T::MaxCandidates::get(), Error::<T>::TooManyCandidates);
 			ensure!(!Self::invulnerables().contains(&who), Error::<T>::AlreadyInvulnerable);
 
 			let validator_key = T::ValidatorIdOf::convert(who.clone())
@@ -437,25 +524,27 @@ pub mod pallet {
 
 			let deposit = Self::candidacy_bond();
 			// First authored block is current block plus kick threshold to handle session delay
-			let incoming = CandidateInfo { who: who.clone(), deposit };
-
-			let current_count =
-				<Candidates<T>>::try_mutate(|candidates| -> Result<usize, DispatchError> {
-					if candidates.iter().any(|candidate| candidate.who == who) {
-						Err(Error::<T>::AlreadyCandidate)?
-					} else {
-						T::Currency::reserve(&who, deposit)?;
-						candidates.try_push(incoming).map_err(|_| Error::<T>::TooManyCandidates)?;
-						<LastAuthoredBlock<T>>::insert(
-							who.clone(),
-							frame_system::Pallet::<T>::block_number() + T::KickThreshold::get(),
-						);
-						Ok(candidates.len())
-					}
-				})?;
+			<CandidateList<T>>::try_mutate(|candidates| -> Result<(), DispatchError> {
+				ensure!(
+					!candidates.iter().any(|candidate_info| candidate_info.who == who),
+					Error::<T>::AlreadyCandidate
+				);
+				T::Currency::reserve(&who, deposit)?;
+				<LastAuthoredBlock<T>>::insert(
+					who.clone(),
+					frame_system::Pallet::<T>::block_number() + T::KickThreshold::get(),
+				);
+				candidates
+					.try_insert(0, CandidateInfo { who: who.clone(), deposit })
+					.map_err(|_| Error::<T>::InsertToCandidateListFailed)?;
+				Ok(())
+			})?;
 
 			Self::deposit_event(Event::CandidateAdded { account_id: who, deposit });
-			Ok(Some(T::WeightInfo::register_as_candidate(current_count as u32)).into())
+			// Safe to do unchecked add here because we ensure above that `length <
+			// T::MaxCandidates::get()`, and since `T::MaxCandidates` is `u32` it can be at most
+			// `u32::MAX`, therefore `length + 1` cannot overflow.
+			Ok(Some(T::WeightInfo::register_as_candidate(length + 1)).into())
 		}
 
 		/// Deregister `origin` as a collator candidate. Note that the collator can only leave on
@@ -468,13 +557,14 @@ pub mod pallet {
 		pub fn leave_intent(origin: OriginFor<T>) -> DispatchResultWithPostInfo {
 			let who = ensure_signed(origin)?;
 			ensure!(
-				Self::eligible_collators() > T::MinEligibleCollators::get() as usize,
+				Self::eligible_collators() > T::MinEligibleCollators::get(),
 				Error::<T>::TooFewEligibleCollators
 			);
+			let length = <CandidateList<T>>::decode_len().unwrap_or_default();
 			// Do remove their last authored block.
-			let current_count = Self::try_remove_candidate(&who, true)?;
+			Self::try_remove_candidate(&who, true)?;
 
-			Ok(Some(T::WeightInfo::leave_intent(current_count as u32)).into())
+			Ok(Some(T::WeightInfo::leave_intent(length.saturating_sub(1) as u32)).into())
 		}
 
 		/// Add a new account `who` to the list of `Invulnerables` collators. `who` must have
@@ -521,7 +611,7 @@ pub mod pallet {
 					.unwrap_or_default()
 					.try_into()
 					.unwrap_or(T::MaxInvulnerables::get().saturating_sub(1)),
-				Candidates::<T>::decode_len()
+				<CandidateList<T>>::decode_len()
 					.unwrap_or_default()
 					.try_into()
 					.unwrap_or(T::MaxCandidates::get()),
@@ -540,7 +630,7 @@ pub mod pallet {
 			T::UpdateOrigin::ensure_origin(origin)?;
 
 			ensure!(
-				Self::eligible_collators() > T::MinEligibleCollators::get() as usize,
+				Self::eligible_collators() > T::MinEligibleCollators::get(),
 				Error::<T>::TooFewEligibleCollators
 			);
 
@@ -554,6 +644,154 @@ pub mod pallet {
 			Self::deposit_event(Event::InvulnerableRemoved { account_id: who });
 			Ok(())
 		}
+
+		/// Update the candidacy bond of collator candidate `origin` to a new amount `new_deposit`.
+		///
+		/// Setting a `new_deposit` that is lower than the current deposit while `origin` is
+		/// occupying a top-`DesiredCandidates` slot is not allowed.
+		///
+		/// This call will fail if `origin` is not a collator candidate, the updated bond is lower
+		/// than the minimum candidacy bond, and/or the amount cannot be reserved.
+		#[pallet::call_index(7)]
+		#[pallet::weight(T::WeightInfo::update_bond(T::MaxCandidates::get()))]
+		pub fn update_bond(
+			origin: OriginFor<T>,
+			new_deposit: BalanceOf<T>,
+		) -> DispatchResultWithPostInfo {
+			let who = ensure_signed(origin)?;
+			ensure!(new_deposit >= <CandidacyBond<T>>::get(), Error::<T>::DepositTooLow);
+			// The function below will try to mutate the `CandidateList` entry for the caller to
+			// update their deposit to the new value of `new_deposit`. The return value is the
+			// position of the entry in the list, used for weight calculation.
+			let length =
+				<CandidateList<T>>::try_mutate(|candidates| -> Result<usize, DispatchError> {
+					let idx = candidates
+						.iter()
+						.position(|candidate_info| candidate_info.who == who)
+						.ok_or_else(|| Error::<T>::NotCandidate)?;
+					let candidate_count = candidates.len();
+					// Remove the candidate from the list.
+					let mut info = candidates.remove(idx);
+					let old_deposit = info.deposit;
+					if new_deposit > old_deposit {
+						T::Currency::reserve(&who, new_deposit - old_deposit)?;
+					} else if new_deposit < old_deposit {
+						// Casting `u32` to `usize` should be safe on all machines running this.
+						ensure!(
+							idx.saturating_add(<DesiredCandidates<T>>::get() as usize) <
+								candidate_count,
+							Error::<T>::InvalidUnreserve
+						);
+						T::Currency::unreserve(&who, old_deposit - new_deposit);
+					} else {
+						return Err(Error::<T>::IdenticalDeposit.into())
+					}
+
+					// Update the deposit and insert the candidate in the correct spot in the list.
+					info.deposit = new_deposit;
+					let new_pos = candidates
+						.iter()
+						.position(|candidate| candidate.deposit >= new_deposit)
+						.unwrap_or_else(|| candidates.len());
+					candidates
+						.try_insert(new_pos, info)
+						.map_err(|_| Error::<T>::InsertToCandidateListFailed)?;
+
+					Ok(candidate_count)
+				})?;
+
+			Self::deposit_event(Event::CandidateBondUpdated {
+				account_id: who,
+				deposit: new_deposit,
+			});
+			Ok(Some(T::WeightInfo::update_bond(length as u32)).into())
+		}
+
+		/// The caller `origin` replaces a candidate `target` in the collator candidate list by
+		/// reserving `deposit`. The amount `deposit` reserved by the caller must be greater than
+		/// the existing bond of the target it is trying to replace.
+		///
+		/// This call will fail if the caller is already a collator candidate or invulnerable, the
+		/// caller does not have registered session keys, the target is not a collator candidate,
+		/// and/or the `deposit` amount cannot be reserved.
+		#[pallet::call_index(8)]
+		#[pallet::weight(T::WeightInfo::take_candidate_slot(T::MaxCandidates::get()))]
+		pub fn take_candidate_slot(
+			origin: OriginFor<T>,
+			deposit: BalanceOf<T>,
+			target: T::AccountId,
+		) -> DispatchResultWithPostInfo {
+			let who = ensure_signed(origin)?;
+
+			ensure!(!Self::invulnerables().contains(&who), Error::<T>::AlreadyInvulnerable);
+			ensure!(deposit >= Self::candidacy_bond(), Error::<T>::InsufficientBond);
+
+			let validator_key = T::ValidatorIdOf::convert(who.clone())
+				.ok_or(Error::<T>::NoAssociatedValidatorId)?;
+			ensure!(
+				T::ValidatorRegistration::is_registered(&validator_key),
+				Error::<T>::ValidatorNotRegistered
+			);
+
+			let length = <CandidateList<T>>::decode_len().unwrap_or_default();
+			// The closure below iterates through all elements of the candidate list to ensure that
+			// the caller isn't already a candidate and to find the target it's trying to replace in
+			// the list. The return value is a tuple of the position of the candidate to be replaced
+			// in the list along with its candidate information.
+			let target_info = <CandidateList<T>>::try_mutate(
+				|candidates| -> Result<CandidateInfo<T::AccountId, BalanceOf<T>>, DispatchError> {
+					// Find the position in the list of the candidate that is being replaced.
+					let mut target_info_idx = None;
+					let mut new_info_idx = None;
+					for (idx, candidate_info) in candidates.iter().enumerate() {
+						// While iterating through the candidates trying to find the target,
+						// also ensure on the same pass that our caller isn't already a
+						// candidate.
+						ensure!(candidate_info.who != who, Error::<T>::AlreadyCandidate);
+						// If we find our target, update the position but do not stop the
+						// iteration since we're also checking that the caller isn't already a
+						// candidate.
+						if candidate_info.who == target {
+							target_info_idx = Some(idx);
+						}
+						// Find the spot where the new candidate would be inserted in the current
+						// version of the list.
+						if new_info_idx.is_none() && candidate_info.deposit >= deposit {
+							new_info_idx = Some(idx);
+						}
+					}
+					let target_info_idx =
+						target_info_idx.ok_or(Error::<T>::TargetIsNotCandidate)?;
+
+					// Remove the old candidate from the list.
+					let target_info = candidates.remove(target_info_idx);
+					ensure!(deposit > target_info.deposit, Error::<T>::InsufficientBond);
+
+					// We have removed one element before `new_info_idx`, so the position we have to
+					// insert to is reduced by 1.
+					let new_pos = new_info_idx
+						.map(|i| i.saturating_sub(1))
+						.unwrap_or_else(|| candidates.len());
+					let new_info = CandidateInfo { who: who.clone(), deposit };
+					// Insert the new candidate in the correct spot in the list.
+					candidates
+						.try_insert(new_pos, new_info)
+						.expect("candidate count previously decremented; qed");
+
+					Ok(target_info)
+				},
+			)?;
+			T::Currency::reserve(&who, deposit)?;
+			T::Currency::unreserve(&target_info.who, target_info.deposit);
+			<LastAuthoredBlock<T>>::remove(target_info.who.clone());
+			<LastAuthoredBlock<T>>::insert(
+				who.clone(),
+				frame_system::Pallet::<T>::block_number() + T::KickThreshold::get(),
+			);
+
+			Self::deposit_event(Event::CandidateReplaced { old: target, new: who, deposit });
+			Ok(Some(T::WeightInfo::take_candidate_slot(length as u32)).into())
+		}
 	}
 
 	impl<T: Config> Pallet<T> {
@@ -564,84 +802,122 @@ pub mod pallet {
 
 		/// Return the total number of accounts that are eligible collators (candidates and
 		/// invulnerables).
-		fn eligible_collators() -> usize {
-			Candidates::<T>::decode_len()
+		fn eligible_collators() -> u32 {
+			<CandidateList<T>>::decode_len()
 				.unwrap_or_default()
 				.saturating_add(Invulnerables::<T>::decode_len().unwrap_or_default())
+				.try_into()
+				.unwrap_or(u32::MAX)
 		}
 
 		/// Removes a candidate if they exist and sends them back their deposit.
 		fn try_remove_candidate(
 			who: &T::AccountId,
 			remove_last_authored: bool,
-		) -> Result<usize, DispatchError> {
-			let current_count =
-				<Candidates<T>>::try_mutate(|candidates| -> Result<usize, DispatchError> {
-					let index = candidates
-						.iter()
-						.position(|candidate| candidate.who == *who)
-						.ok_or(Error::<T>::NotCandidate)?;
-					let candidate = candidates.remove(index);
-					T::Currency::unreserve(who, candidate.deposit);
-					if remove_last_authored {
-						<LastAuthoredBlock<T>>::remove(who.clone())
-					};
-					Ok(candidates.len())
-				})?;
+		) -> Result<(), DispatchError> {
+			<CandidateList<T>>::try_mutate(|candidates| -> Result<(), DispatchError> {
+				let idx = candidates
+					.iter()
+					.position(|candidate_info| candidate_info.who == *who)
+					.ok_or(Error::<T>::NotCandidate)?;
+				let deposit = candidates[idx].deposit;
+				T::Currency::unreserve(who, deposit);
+				candidates.remove(idx);
+				if remove_last_authored {
+					<LastAuthoredBlock<T>>::remove(who.clone())
+				};
+				Ok(())
+			})?;
 			Self::deposit_event(Event::CandidateRemoved { account_id: who.clone() });
-			Ok(current_count)
+			Ok(())
 		}
 
 		/// Assemble the current set of candidates and invulnerables into the next collator set.
 		///
 		/// This is done on the fly, as frequent as we are told to do so, as the session manager.
-		pub fn assemble_collators(
-			candidates: BoundedVec<T::AccountId, T::MaxCandidates>,
-		) -> Vec<T::AccountId> {
+		pub fn assemble_collators() -> Vec<T::AccountId> {
+			// Casting `u32` to `usize` should be safe on all machines running this.
+			let desired_candidates = <DesiredCandidates<T>>::get() as usize;
 			let mut collators = Self::invulnerables().to_vec();
-			collators.extend(candidates);
+			collators.extend(
+				<CandidateList<T>>::get()
+					.iter()
+					.rev()
+					.cloned()
+					.take(desired_candidates)
+					.map(|candidate_info| candidate_info.who),
+			);
 			collators
 		}
 
 		/// Kicks out candidates that did not produce a block in the kick threshold and refunds
 		/// their deposits.
-		pub fn kick_stale_candidates(
-			candidates: BoundedVec<CandidateInfo<T::AccountId, BalanceOf<T>>, T::MaxCandidates>,
-		) -> BoundedVec<T::AccountId, T::MaxCandidates> {
+		///
+		/// Return value is the number of candidates left in the list.
+		pub fn kick_stale_candidates(candidates: impl IntoIterator<Item = T::AccountId>) -> u32 {
 			let now = frame_system::Pallet::<T>::block_number();
 			let kick_threshold = T::KickThreshold::get();
 			let min_collators = T::MinEligibleCollators::get();
 			candidates
 				.into_iter()
 				.filter_map(|c| {
-					let last_block = <LastAuthoredBlock<T>>::get(c.who.clone());
+					let last_block = <LastAuthoredBlock<T>>::get(c.clone());
 					let since_last = now.saturating_sub(last_block);
 
-					let is_invulnerable = Self::invulnerables().contains(&c.who);
+					let is_invulnerable = Self::invulnerables().contains(&c);
 					let is_lazy = since_last >= kick_threshold;
 
 					if is_invulnerable {
-						// They are invulnerable. No reason for them to be in Candidates also.
+						// They are invulnerable. No reason for them to be in `CandidateList` also.
 						// We don't even care about the min collators here, because an Account
 						// should not be a collator twice.
-						let _ = Self::try_remove_candidate(&c.who, false);
+						let _ = Self::try_remove_candidate(&c, false);
 						None
 					} else {
-						if Self::eligible_collators() <= min_collators as usize || !is_lazy {
+						if Self::eligible_collators() <= min_collators || !is_lazy {
 							// Either this is a good collator (not lazy) or we are at the minimum
 							// that the system needs. They get to stay.
-							Some(c.who)
+							Some(c)
 						} else {
 							// This collator has not produced a block recently enough. Bye bye.
-							let _ = Self::try_remove_candidate(&c.who, true);
+							let _ = Self::try_remove_candidate(&c, true);
 							None
 						}
 					}
 				})
-				.collect::<Vec<_>>()
+				.count()
 				.try_into()
 				.expect("filter_map operation can't result in a bounded vec larger than its original; qed")
 		}
+
+		/// Ensure the correctness of the state of this pallet.
+		///
+		/// This should be valid before or after each state transition of this pallet.
+		///
+		/// # Invariants
+		///
+		/// ## `DesiredCandidates`
+		///
+		/// * The current desired candidate count should not exceed the candidate list capacity.
+		/// * The number of selected candidates together with the invulnerables must be greater than
+		///   or equal to the minimum number of eligible collators.
+		#[cfg(any(test, feature = "try-runtime"))]
+		pub fn do_try_state() -> Result<(), sp_runtime::TryRuntimeError> {
+			let desired_candidates = <DesiredCandidates<T>>::get();
+
+			frame_support::ensure!(
+				desired_candidates <= T::MaxCandidates::get(),
+				"Shouldn't demand more candidates than the pallet config allows."
+			);
+
+			frame_support::ensure!(
+				desired_candidates.saturating_add(T::MaxInvulnerables::get()) >=
+					T::MinEligibleCollators::get(),
+				"Invulnerable set together with desired candidates should be able to meet the collator quota."
+			);
+
+			Ok(())
+		}
 	}
 
 	/// Keep track of number of authored blocks per authority, uncles are counted as well since
@@ -677,14 +953,23 @@ pub mod pallet {
 				<frame_system::Pallet<T>>::block_number(),
 			);
 
-			let candidates = Self::candidates();
-			let candidates_len_before = candidates.len();
-			let active_candidates = Self::kick_stale_candidates(candidates);
-			let removed = candidates_len_before - active_candidates.len();
-			let result = Self::assemble_collators(active_candidates);
+			// The `expect` below is safe because the list is a `BoundedVec` with a max size of
+			// `T::MaxCandidates`, which is a `u32`. When `decode_len` returns `Some(len)`, `len`
+			// must be valid and at most `u32::MAX`, which must always be able to convert to `u32`.
+			let candidates_len_before: u32 = <CandidateList<T>>::decode_len()
+				.unwrap_or_default()
+				.try_into()
+				.expect("length is at most `T::MaxCandidates`, so it must fit in `u32`; qed");
+			let active_candidates_count = Self::kick_stale_candidates(
+				<CandidateList<T>>::get()
+					.iter()
+					.map(|candidate_info| candidate_info.who.clone()),
+			);
+			let removed = candidates_len_before.saturating_sub(active_candidates_count);
+			let result = Self::assemble_collators();
 
 			frame_system::Pallet::<T>::register_extra_weight_unchecked(
-				T::WeightInfo::new_session(candidates_len_before as u32, removed as u32),
+				T::WeightInfo::new_session(candidates_len_before, removed),
 				DispatchClass::Mandatory,
 			);
 			Some(result)
diff --git a/cumulus/pallets/collator-selection/src/tests.rs b/cumulus/pallets/collator-selection/src/tests.rs
index d4dae513df375145c35a83d7af274e581a94a60b..ed2044ccdfad7def46ab92180e9b177643ac8679 100644
--- a/cumulus/pallets/collator-selection/src/tests.rs
+++ b/cumulus/pallets/collator-selection/src/tests.rs
@@ -28,7 +28,7 @@ fn basic_setup_works() {
 		assert_eq!(CollatorSelection::desired_candidates(), 2);
 		assert_eq!(CollatorSelection::candidacy_bond(), 10);
 
-		assert!(CollatorSelection::candidates().is_empty());
+		assert_eq!(<crate::CandidateList<Test>>::get().iter().count(), 0);
 		// genesis should sort input
 		assert_eq!(CollatorSelection::invulnerables(), vec![1, 2]);
 	});
@@ -202,7 +202,8 @@ fn candidate_to_invulnerable_works() {
 		initialize_to_block(1);
 		assert_eq!(CollatorSelection::desired_candidates(), 2);
 		assert_eq!(CollatorSelection::candidacy_bond(), 10);
-		assert_eq!(CollatorSelection::candidates(), Vec::new());
+
+		assert_eq!(<crate::CandidateList<Test>>::get().iter().count(), 0);
 		assert_eq!(CollatorSelection::invulnerables(), vec![1, 2]);
 
 		assert_eq!(Balances::free_balance(3), 100);
@@ -226,7 +227,7 @@ fn candidate_to_invulnerable_works() {
 		));
 		assert!(CollatorSelection::invulnerables().to_vec().contains(&3));
 		assert_eq!(Balances::free_balance(3), 100);
-		assert_eq!(CollatorSelection::candidates().len(), 1);
+		assert_eq!(<crate::CandidateList<Test>>::get().iter().count(), 1);
 
 		assert_ok!(CollatorSelection::add_invulnerable(
 			RuntimeOrigin::signed(RootAccount::get()),
@@ -240,7 +241,8 @@ fn candidate_to_invulnerable_works() {
 		));
 		assert!(CollatorSelection::invulnerables().to_vec().contains(&4));
 		assert_eq!(Balances::free_balance(4), 100);
-		assert_eq!(CollatorSelection::candidates().len(), 0);
+
+		assert_eq!(<crate::CandidateList<Test>>::get().iter().count(), 0);
 	});
 }
 
@@ -266,42 +268,230 @@ fn set_desired_candidates_works() {
 }
 
 #[test]
-fn set_candidacy_bond() {
+fn set_candidacy_bond_empty_candidate_list() {
 	new_test_ext().execute_with(|| {
 		// given
 		assert_eq!(CollatorSelection::candidacy_bond(), 10);
+		assert!(<crate::CandidateList<Test>>::get().is_empty());
 
-		// can set
+		// can decrease without candidates
 		assert_ok!(CollatorSelection::set_candidacy_bond(
 			RuntimeOrigin::signed(RootAccount::get()),
 			7
 		));
 		assert_eq!(CollatorSelection::candidacy_bond(), 7);
+		assert!(<crate::CandidateList<Test>>::get().is_empty());
 
 		// rejects bad origin.
 		assert_noop!(CollatorSelection::set_candidacy_bond(RuntimeOrigin::signed(1), 8), BadOrigin);
+
+		// can increase without candidates
+		assert_ok!(CollatorSelection::set_candidacy_bond(
+			RuntimeOrigin::signed(RootAccount::get()),
+			20
+		));
+		assert!(<crate::CandidateList<Test>>::get().is_empty());
+		assert_eq!(CollatorSelection::candidacy_bond(), 20);
 	});
 }
 
 #[test]
-fn cannot_register_candidate_if_too_many() {
+fn set_candidacy_bond_with_one_candidate() {
 	new_test_ext().execute_with(|| {
-		// reset desired candidates:
-		<crate::DesiredCandidates<Test>>::put(0);
+		// given
+		assert_eq!(CollatorSelection::candidacy_bond(), 10);
+		assert!(<crate::CandidateList<Test>>::get().is_empty());
 
-		// can't accept anyone anymore.
-		assert_noop!(
-			CollatorSelection::register_as_candidate(RuntimeOrigin::signed(3)),
-			Error::<Test>::TooManyCandidates,
+		let candidate_3 = CandidateInfo { who: 3, deposit: 10 };
+
+		assert_ok!(CollatorSelection::register_as_candidate(RuntimeOrigin::signed(3)));
+		assert_eq!(<crate::CandidateList<Test>>::get(), vec![candidate_3.clone()]);
+
+		// can decrease with one candidate
+		assert_ok!(CollatorSelection::set_candidacy_bond(
+			RuntimeOrigin::signed(RootAccount::get()),
+			7
+		));
+		assert_eq!(CollatorSelection::candidacy_bond(), 7);
+		assert_eq!(<crate::CandidateList<Test>>::get(), vec![candidate_3.clone()]);
+
+		// can increase up to initial deposit
+		assert_ok!(CollatorSelection::set_candidacy_bond(
+			RuntimeOrigin::signed(RootAccount::get()),
+			10
+		));
+		assert_eq!(CollatorSelection::candidacy_bond(), 10);
+		assert_eq!(<crate::CandidateList<Test>>::get(), vec![candidate_3.clone()]);
+
+		// can increase past initial deposit, should kick existing candidate
+		assert_ok!(CollatorSelection::set_candidacy_bond(
+			RuntimeOrigin::signed(RootAccount::get()),
+			20
+		));
+		assert!(<crate::CandidateList<Test>>::get().is_empty());
+	});
+}
+
+#[test]
+fn set_candidacy_bond_with_many_candidates_same_deposit() {
+	new_test_ext().execute_with(|| {
+		// given
+		assert_eq!(CollatorSelection::candidacy_bond(), 10);
+		assert!(<crate::CandidateList<Test>>::get().is_empty());
+
+		let candidate_3 = CandidateInfo { who: 3, deposit: 10 };
+		let candidate_4 = CandidateInfo { who: 4, deposit: 10 };
+		let candidate_5 = CandidateInfo { who: 5, deposit: 10 };
+
+		assert_ok!(CollatorSelection::register_as_candidate(RuntimeOrigin::signed(3)));
+		assert_ok!(CollatorSelection::register_as_candidate(RuntimeOrigin::signed(4)));
+		assert_ok!(CollatorSelection::register_as_candidate(RuntimeOrigin::signed(5)));
+		assert_eq!(
+			<crate::CandidateList<Test>>::get(),
+			vec![candidate_5.clone(), candidate_4.clone(), candidate_3.clone()]
 		);
 
-		// reset desired candidates:
-		<crate::DesiredCandidates<Test>>::put(1);
+		// can decrease with multiple candidates
+		assert_ok!(CollatorSelection::set_candidacy_bond(
+			RuntimeOrigin::signed(RootAccount::get()),
+			7
+		));
+		assert_eq!(CollatorSelection::candidacy_bond(), 7);
+		assert_eq!(
+			<crate::CandidateList<Test>>::get(),
+			vec![candidate_5.clone(), candidate_4.clone(), candidate_3.clone()]
+		);
+
+		// can increase up to initial deposit
+		assert_ok!(CollatorSelection::set_candidacy_bond(
+			RuntimeOrigin::signed(RootAccount::get()),
+			10
+		));
+		assert_eq!(CollatorSelection::candidacy_bond(), 10);
+		assert_eq!(
+			<crate::CandidateList<Test>>::get(),
+			vec![candidate_5.clone(), candidate_4.clone(), candidate_3.clone()]
+		);
+
+		// can increase past initial deposit, should kick existing candidates
+		assert_ok!(CollatorSelection::set_candidacy_bond(
+			RuntimeOrigin::signed(RootAccount::get()),
+			20
+		));
+		assert!(<crate::CandidateList<Test>>::get().is_empty());
+	});
+}
+
+#[test]
+fn set_candidacy_bond_with_many_candidates_different_deposits() {
+	new_test_ext().execute_with(|| {
+		// given
+		assert_eq!(CollatorSelection::candidacy_bond(), 10);
+		assert!(<crate::CandidateList<Test>>::get().is_empty());
+
+		let candidate_3 = CandidateInfo { who: 3, deposit: 10 };
+		let candidate_4 = CandidateInfo { who: 4, deposit: 20 };
+		let candidate_5 = CandidateInfo { who: 5, deposit: 30 };
+
+		assert_ok!(CollatorSelection::register_as_candidate(RuntimeOrigin::signed(3)));
 		assert_ok!(CollatorSelection::register_as_candidate(RuntimeOrigin::signed(4)));
+		assert_ok!(CollatorSelection::register_as_candidate(RuntimeOrigin::signed(5)));
+		assert_ok!(CollatorSelection::update_bond(RuntimeOrigin::signed(5), 30));
+		assert_ok!(CollatorSelection::update_bond(RuntimeOrigin::signed(4), 20));
+		assert_eq!(
+			<crate::CandidateList<Test>>::get(),
+			vec![candidate_3.clone(), candidate_4.clone(), candidate_5.clone()]
+		);
+
+		// can decrease with multiple candidates
+		assert_ok!(CollatorSelection::set_candidacy_bond(
+			RuntimeOrigin::signed(RootAccount::get()),
+			7
+		));
+		assert_eq!(CollatorSelection::candidacy_bond(), 7);
+		assert_eq!(
+			<crate::CandidateList<Test>>::get(),
+			vec![candidate_3.clone(), candidate_4.clone(), candidate_5.clone()]
+		);
+		// can increase up to initial deposit
+		assert_ok!(CollatorSelection::set_candidacy_bond(
+			RuntimeOrigin::signed(RootAccount::get()),
+			10
+		));
+		assert_eq!(CollatorSelection::candidacy_bond(), 10);
+		assert_eq!(
+			<crate::CandidateList<Test>>::get(),
+			vec![candidate_3.clone(), candidate_4.clone(), candidate_5.clone()]
+		);
+
+		// can increase to 4's deposit, should kick 3
+		assert_ok!(CollatorSelection::set_candidacy_bond(
+			RuntimeOrigin::signed(RootAccount::get()),
+			20
+		));
+		assert_eq!(CollatorSelection::candidacy_bond(), 20);
+		assert_eq!(
+			<crate::CandidateList<Test>>::get(),
+			vec![candidate_4.clone(), candidate_5.clone()]
+		);
+
+		// can increase past 4's deposit, should kick 4
+		assert_ok!(CollatorSelection::set_candidacy_bond(
+			RuntimeOrigin::signed(RootAccount::get()),
+			25
+		));
+		assert_eq!(CollatorSelection::candidacy_bond(), 25);
+		assert_eq!(<crate::CandidateList<Test>>::get(), vec![candidate_5.clone()]);
+
+		// lowering the minimum deposit should have no effect
+		assert_ok!(CollatorSelection::set_candidacy_bond(
+			RuntimeOrigin::signed(RootAccount::get()),
+			5
+		));
+		assert_eq!(CollatorSelection::candidacy_bond(), 5);
+		assert_eq!(<crate::CandidateList<Test>>::get(), vec![candidate_5.clone()]);
+
+		// add 3 and 4 back but with higher deposits than minimum
+		assert_ok!(CollatorSelection::register_as_candidate(RuntimeOrigin::signed(3)));
+		assert_ok!(CollatorSelection::register_as_candidate(RuntimeOrigin::signed(4)));
+		assert_ok!(CollatorSelection::update_bond(RuntimeOrigin::signed(3), 10));
+		assert_ok!(CollatorSelection::update_bond(RuntimeOrigin::signed(4), 20));
+		assert_eq!(
+			<crate::CandidateList<Test>>::get(),
+			vec![candidate_3.clone(), candidate_4.clone(), candidate_5.clone()]
+		);
+
+		// can increase the deposit above the current max in the list, all candidates should be
+		// kicked
+		assert_ok!(CollatorSelection::set_candidacy_bond(
+			RuntimeOrigin::signed(RootAccount::get()),
+			40
+		));
+		assert_eq!(CollatorSelection::candidacy_bond(), 40);
+		assert!(<crate::CandidateList<Test>>::get().is_empty());
+	});
+}
+
+#[test]
+fn cannot_register_candidate_if_too_many() {
+	new_test_ext().execute_with(|| {
+		<crate::DesiredCandidates<Test>>::put(1);
+
+		// MaxCandidates: u32 = 20
+		// Aside from 3, 4, and 5, create enough accounts to have 21 potential
+		// candidates.
+		for i in 6..=23 {
+			Balances::make_free_balance_be(&i, 100);
+			let key = MockSessionKeys { aura: UintAuthorityId(i) };
+			Session::set_keys(RuntimeOrigin::signed(i).into(), key, Vec::new()).unwrap();
+		}
+
+		for c in 3..=22 {
+			assert_ok!(CollatorSelection::register_as_candidate(RuntimeOrigin::signed(c)));
+		}
 
-		// but no more
 		assert_noop!(
-			CollatorSelection::register_as_candidate(RuntimeOrigin::signed(5)),
+			CollatorSelection::register_as_candidate(RuntimeOrigin::signed(23)),
 			Error::<Test>::TooManyCandidates,
 		);
 	})
@@ -310,7 +500,7 @@ fn cannot_register_candidate_if_too_many() {
 #[test]
 fn cannot_unregister_candidate_if_too_few() {
 	new_test_ext().execute_with(|| {
-		assert_eq!(CollatorSelection::candidates(), Vec::new());
+		assert_eq!(<crate::CandidateList<Test>>::get().iter().count(), 0);
 		assert_eq!(CollatorSelection::invulnerables(), vec![1, 2]);
 		assert_ok!(CollatorSelection::remove_invulnerable(
 			RuntimeOrigin::signed(RootAccount::get()),
@@ -368,8 +558,12 @@ fn cannot_register_dupe_candidate() {
 	new_test_ext().execute_with(|| {
 		// can add 3 as candidate
 		assert_ok!(CollatorSelection::register_as_candidate(RuntimeOrigin::signed(3)));
+		// tuple of (id, deposit).
 		let addition = CandidateInfo { who: 3, deposit: 10 };
-		assert_eq!(CollatorSelection::candidates(), vec![addition]);
+		assert_eq!(
+			<crate::CandidateList<Test>>::get().iter().cloned().collect::<Vec<_>>(),
+			vec![addition]
+		);
 		assert_eq!(CollatorSelection::last_authored_block(3), 10);
 		assert_eq!(Balances::free_balance(3), 90);
 
@@ -404,7 +598,8 @@ fn register_as_candidate_works() {
 		// given
 		assert_eq!(CollatorSelection::desired_candidates(), 2);
 		assert_eq!(CollatorSelection::candidacy_bond(), 10);
-		assert_eq!(CollatorSelection::candidates(), Vec::new());
+
+		assert_eq!(<crate::CandidateList<Test>>::get().iter().count(), 0);
 		assert_eq!(CollatorSelection::invulnerables(), vec![1, 2]);
 
 		// take two endowed, non-invulnerables accounts.
@@ -417,140 +612,888 @@ fn register_as_candidate_works() {
 		assert_eq!(Balances::free_balance(3), 90);
 		assert_eq!(Balances::free_balance(4), 90);
 
-		assert_eq!(CollatorSelection::candidates().len(), 2);
+		assert_eq!(<crate::CandidateList<Test>>::get().iter().count(), 2);
 	});
 }
 
 #[test]
-fn leave_intent() {
+fn cannot_take_candidate_slot_if_invulnerable() {
 	new_test_ext().execute_with(|| {
-		// register a candidate.
-		assert_ok!(CollatorSelection::register_as_candidate(RuntimeOrigin::signed(3)));
-		assert_eq!(Balances::free_balance(3), 90);
-
-		// register too so can leave above min candidates
-		assert_ok!(CollatorSelection::register_as_candidate(RuntimeOrigin::signed(5)));
-		assert_eq!(Balances::free_balance(5), 90);
+		assert_eq!(CollatorSelection::invulnerables(), vec![1, 2]);
 
-		// cannot leave if not candidate.
+		// can't 1 because it is invulnerable.
 		assert_noop!(
-			CollatorSelection::leave_intent(RuntimeOrigin::signed(4)),
-			Error::<Test>::NotCandidate
+			CollatorSelection::take_candidate_slot(RuntimeOrigin::signed(1), 50u64.into(), 2),
+			Error::<Test>::AlreadyInvulnerable,
 		);
-
-		// bond is returned
-		assert_ok!(CollatorSelection::leave_intent(RuntimeOrigin::signed(3)));
-		assert_eq!(Balances::free_balance(3), 100);
-		assert_eq!(CollatorSelection::last_authored_block(3), 0);
-	});
+	})
 }
 
 #[test]
-fn authorship_event_handler() {
+fn cannot_take_candidate_slot_if_keys_not_registered() {
 	new_test_ext().execute_with(|| {
-		// put 100 in the pot + 5 for ED
-		Balances::make_free_balance_be(&CollatorSelection::account_id(), 105);
+		assert_ok!(CollatorSelection::register_as_candidate(RuntimeOrigin::signed(3)));
+		assert_noop!(
+			CollatorSelection::take_candidate_slot(RuntimeOrigin::signed(42), 50u64.into(), 3),
+			Error::<Test>::ValidatorNotRegistered
+		);
+	})
+}
 
-		// 4 is the default author.
-		assert_eq!(Balances::free_balance(4), 100);
+#[test]
+fn cannot_take_candidate_slot_if_duplicate() {
+	new_test_ext().execute_with(|| {
+		// can add 3 as candidate
+		assert_ok!(CollatorSelection::register_as_candidate(RuntimeOrigin::signed(3)));
 		assert_ok!(CollatorSelection::register_as_candidate(RuntimeOrigin::signed(4)));
-		// triggers `note_author`
-		Authorship::on_initialize(1);
+		// tuple of (id, deposit).
+		let candidate_3 = CandidateInfo { who: 3, deposit: 10 };
+		let candidate_4 = CandidateInfo { who: 4, deposit: 10 };
+		let actual_candidates =
+			<crate::CandidateList<Test>>::get().iter().cloned().collect::<Vec<_>>();
+		assert_eq!(actual_candidates, vec![candidate_4, candidate_3]);
+		assert_eq!(CollatorSelection::last_authored_block(3), 10);
+		assert_eq!(CollatorSelection::last_authored_block(4), 10);
+		assert_eq!(Balances::free_balance(3), 90);
 
-		let collator = CandidateInfo { who: 4, deposit: 10 };
+		// but no more
+		assert_noop!(
+			CollatorSelection::take_candidate_slot(RuntimeOrigin::signed(3), 50u64.into(), 4),
+			Error::<Test>::AlreadyCandidate,
+		);
+	})
+}
 
-		assert_eq!(CollatorSelection::candidates(), vec![collator]);
-		assert_eq!(CollatorSelection::last_authored_block(4), 0);
+#[test]
+fn cannot_take_candidate_slot_if_target_invalid() {
+	new_test_ext().execute_with(|| {
+		// can add 3 as candidate
+		assert_ok!(CollatorSelection::register_as_candidate(RuntimeOrigin::signed(3)));
+		// tuple of (id, deposit).
+		let candidate_3 = CandidateInfo { who: 3, deposit: 10 };
+		assert_eq!(
+			<crate::CandidateList<Test>>::get().iter().cloned().collect::<Vec<_>>(),
+			vec![candidate_3]
+		);
+		assert_eq!(CollatorSelection::last_authored_block(3), 10);
+		assert_eq!(Balances::free_balance(3), 90);
+		assert_eq!(Balances::free_balance(4), 100);
 
-		// half of the pot goes to the collator who's the author (4 in tests).
-		assert_eq!(Balances::free_balance(4), 140);
-		// half + ED stays.
-		assert_eq!(Balances::free_balance(CollatorSelection::account_id()), 55);
-	});
+		assert_noop!(
+			CollatorSelection::take_candidate_slot(RuntimeOrigin::signed(4), 50u64.into(), 5),
+			Error::<Test>::TargetIsNotCandidate,
+		);
+	})
 }
 
 #[test]
-fn fees_edgecases() {
+fn cannot_take_candidate_slot_if_poor() {
 	new_test_ext().execute_with(|| {
-		// Nothing panics, no reward when no ED in balance
-		Authorship::on_initialize(1);
-		// put some money into the pot at ED
-		Balances::make_free_balance_be(&CollatorSelection::account_id(), 5);
-		// 4 is the default author.
-		assert_eq!(Balances::free_balance(4), 100);
 		assert_ok!(CollatorSelection::register_as_candidate(RuntimeOrigin::signed(4)));
-		// triggers `note_author`
-		Authorship::on_initialize(1);
+		assert_eq!(Balances::free_balance(3), 100);
+		assert_eq!(Balances::free_balance(33), 0);
 
-		let collator = CandidateInfo { who: 4, deposit: 10 };
+		// works
+		assert_ok!(CollatorSelection::take_candidate_slot(
+			RuntimeOrigin::signed(3),
+			20u64.into(),
+			4
+		));
 
-		assert_eq!(CollatorSelection::candidates(), vec![collator]);
-		assert_eq!(CollatorSelection::last_authored_block(4), 0);
-		// Nothing received
-		assert_eq!(Balances::free_balance(4), 90);
-		// all fee stays
-		assert_eq!(Balances::free_balance(CollatorSelection::account_id()), 5);
+		// poor
+		assert_noop!(
+			CollatorSelection::take_candidate_slot(RuntimeOrigin::signed(33), 30u64.into(), 3),
+			BalancesError::<Test>::InsufficientBalance,
+		);
 	});
 }
 
 #[test]
-fn session_management_works() {
+fn cannot_take_candidate_slot_if_insufficient_deposit() {
 	new_test_ext().execute_with(|| {
-		initialize_to_block(1);
-
-		assert_eq!(SessionChangeBlock::get(), 0);
-		assert_eq!(SessionHandlerCollators::get(), vec![1, 2]);
-
-		initialize_to_block(4);
-
-		assert_eq!(SessionChangeBlock::get(), 0);
-		assert_eq!(SessionHandlerCollators::get(), vec![1, 2]);
-
-		// add a new collator
 		assert_ok!(CollatorSelection::register_as_candidate(RuntimeOrigin::signed(3)));
-
-		// session won't see this.
-		assert_eq!(SessionHandlerCollators::get(), vec![1, 2]);
-		// but we have a new candidate.
-		assert_eq!(CollatorSelection::candidates().len(), 1);
-
-		initialize_to_block(10);
-		assert_eq!(SessionChangeBlock::get(), 10);
-		// pallet-session has 1 session delay; current validators are the same.
-		assert_eq!(Session::validators(), vec![1, 2]);
-		// queued ones are changed, and now we have 3.
-		assert_eq!(Session::queued_keys().len(), 3);
-		// session handlers (aura, et. al.) cannot see this yet.
-		assert_eq!(SessionHandlerCollators::get(), vec![1, 2]);
-
-		initialize_to_block(20);
-		assert_eq!(SessionChangeBlock::get(), 20);
-		// changed are now reflected to session handlers.
-		assert_eq!(SessionHandlerCollators::get(), vec![1, 2, 3]);
+		assert_ok!(CollatorSelection::update_bond(RuntimeOrigin::signed(3), 60u64.into()));
+		assert_eq!(Balances::free_balance(3), 40);
+		assert_eq!(Balances::free_balance(4), 100);
+		assert_noop!(
+			CollatorSelection::take_candidate_slot(RuntimeOrigin::signed(4), 5u64.into(), 3),
+			Error::<Test>::InsufficientBond,
+		);
+		assert_eq!(Balances::free_balance(3), 40);
+		assert_eq!(Balances::free_balance(4), 100);
 	});
 }
 
 #[test]
-fn kick_mechanism() {
+fn cannot_take_candidate_slot_if_deposit_less_than_target() {
 	new_test_ext().execute_with(|| {
-		// add a new collator
 		assert_ok!(CollatorSelection::register_as_candidate(RuntimeOrigin::signed(3)));
-		assert_ok!(CollatorSelection::register_as_candidate(RuntimeOrigin::signed(4)));
-		initialize_to_block(10);
-		assert_eq!(CollatorSelection::candidates().len(), 2);
-		initialize_to_block(20);
-		assert_eq!(SessionChangeBlock::get(), 20);
-		// 4 authored this block, gets to stay 3 was kicked
-		assert_eq!(CollatorSelection::candidates().len(), 1);
-		// 3 will be kicked after 1 session delay
-		assert_eq!(SessionHandlerCollators::get(), vec![1, 2, 3, 4]);
-		let collator = CandidateInfo { who: 4, deposit: 10 };
-		assert_eq!(CollatorSelection::candidates(), vec![collator]);
-		assert_eq!(CollatorSelection::last_authored_block(4), 20);
-		initialize_to_block(30);
-		// 3 gets kicked after 1 session delay
-		assert_eq!(SessionHandlerCollators::get(), vec![1, 2, 4]);
-		// kicked collator gets funds back
+		assert_ok!(CollatorSelection::update_bond(RuntimeOrigin::signed(3), 60u64.into()));
+		assert_eq!(Balances::free_balance(3), 40);
+		assert_eq!(Balances::free_balance(4), 100);
+		assert_noop!(
+			CollatorSelection::take_candidate_slot(RuntimeOrigin::signed(4), 20u64.into(), 3),
+			Error::<Test>::InsufficientBond,
+		);
+		assert_eq!(Balances::free_balance(3), 40);
+		assert_eq!(Balances::free_balance(4), 100);
+	});
+}
+
+#[test]
+fn take_candidate_slot_works() {
+	new_test_ext().execute_with(|| {
+		// given
+		assert_eq!(CollatorSelection::desired_candidates(), 2);
+		assert_eq!(CollatorSelection::candidacy_bond(), 10);
+
+		assert_eq!(<crate::CandidateList<Test>>::get().iter().count(), 0);
+		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(5), 100);
+
+		assert_ok!(CollatorSelection::register_as_candidate(RuntimeOrigin::signed(3)));
+		assert_ok!(CollatorSelection::register_as_candidate(RuntimeOrigin::signed(4)));
+		assert_ok!(CollatorSelection::register_as_candidate(RuntimeOrigin::signed(5)));
+
+		assert_eq!(Balances::free_balance(3), 90);
+		assert_eq!(Balances::free_balance(4), 90);
+		assert_eq!(Balances::free_balance(5), 90);
+
+		assert_eq!(<crate::CandidateList<Test>>::get().iter().count(), 3);
+
+		Balances::make_free_balance_be(&6, 100);
+		let key = MockSessionKeys { aura: UintAuthorityId(6) };
+		Session::set_keys(RuntimeOrigin::signed(6).into(), key, Vec::new()).unwrap();
+
+		assert_ok!(CollatorSelection::take_candidate_slot(
+			RuntimeOrigin::signed(6),
+			50u64.into(),
+			4
+		));
+
+		assert_eq!(Balances::free_balance(3), 90);
+		assert_eq!(Balances::free_balance(4), 100);
+		assert_eq!(Balances::free_balance(5), 90);
+		assert_eq!(Balances::free_balance(6), 50);
+
+		// tuple of (id, deposit).
+		let candidate_3 = CandidateInfo { who: 3, deposit: 10 };
+		let candidate_6 = CandidateInfo { who: 6, deposit: 50 };
+		let candidate_5 = CandidateInfo { who: 5, deposit: 10 };
+		let mut actual_candidates =
+			<crate::CandidateList<Test>>::get().iter().cloned().collect::<Vec<_>>();
+		actual_candidates.sort_by(|info_1, info_2| info_1.deposit.cmp(&info_2.deposit));
+		assert_eq!(
+			<crate::CandidateList<Test>>::get().iter().cloned().collect::<Vec<_>>(),
+			vec![candidate_5, candidate_3, candidate_6]
+		);
+	});
+}
+
+#[test]
+fn increase_candidacy_bond_non_candidate_account() {
+	new_test_ext().execute_with(|| {
+		// given
+		assert_eq!(CollatorSelection::desired_candidates(), 2);
+		assert_eq!(CollatorSelection::candidacy_bond(), 10);
+
+		assert_eq!(<crate::CandidateList<Test>>::get().iter().count(), 0);
+		assert_eq!(CollatorSelection::invulnerables(), vec![1, 2]);
+
+		assert_ok!(CollatorSelection::register_as_candidate(RuntimeOrigin::signed(3)));
+		assert_ok!(CollatorSelection::register_as_candidate(RuntimeOrigin::signed(4)));
+
+		assert_noop!(
+			CollatorSelection::update_bond(RuntimeOrigin::signed(5), 20),
+			Error::<Test>::NotCandidate
+		);
+	});
+}
+
+#[test]
+fn increase_candidacy_bond_insufficient_balance() {
+	new_test_ext().execute_with(|| {
+		// given
+		assert_eq!(CollatorSelection::desired_candidates(), 2);
+		assert_eq!(CollatorSelection::candidacy_bond(), 10);
+
+		assert_eq!(<crate::CandidateList<Test>>::get().iter().count(), 0);
+		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(5), 100);
+
+		assert_ok!(CollatorSelection::register_as_candidate(RuntimeOrigin::signed(3)));
+		assert_ok!(CollatorSelection::register_as_candidate(RuntimeOrigin::signed(4)));
+		assert_ok!(CollatorSelection::register_as_candidate(RuntimeOrigin::signed(5)));
+
+		assert_eq!(Balances::free_balance(3), 90);
+		assert_eq!(Balances::free_balance(4), 90);
+		assert_eq!(Balances::free_balance(5), 90);
+
+		assert_noop!(
+			CollatorSelection::update_bond(RuntimeOrigin::signed(3), 110),
+			BalancesError::<Test>::InsufficientBalance
+		);
+
+		assert_eq!(Balances::free_balance(3), 90);
+	});
+}
+
+#[test]
+fn increase_candidacy_bond_works() {
+	new_test_ext().execute_with(|| {
+		// given
+		assert_eq!(CollatorSelection::desired_candidates(), 2);
+		assert_eq!(CollatorSelection::candidacy_bond(), 10);
+
+		assert_eq!(<crate::CandidateList<Test>>::get().iter().count(), 0);
+		assert_eq!(CollatorSelection::invulnerables(), vec![1, 2]);
+
+		// take three endowed, non-invulnerables accounts.
+		assert_eq!(Balances::free_balance(3), 100);
+		assert_eq!(Balances::free_balance(4), 100);
+		assert_eq!(Balances::free_balance(5), 100);
+
+		assert_ok!(CollatorSelection::register_as_candidate(RuntimeOrigin::signed(3)));
+		assert_ok!(CollatorSelection::register_as_candidate(RuntimeOrigin::signed(4)));
+		assert_ok!(CollatorSelection::register_as_candidate(RuntimeOrigin::signed(5)));
+
+		assert_eq!(Balances::free_balance(3), 90);
+		assert_eq!(Balances::free_balance(4), 90);
+		assert_eq!(Balances::free_balance(5), 90);
+
+		assert_ok!(CollatorSelection::update_bond(RuntimeOrigin::signed(3), 20));
+		assert_ok!(CollatorSelection::update_bond(RuntimeOrigin::signed(4), 30));
+		assert_ok!(CollatorSelection::update_bond(RuntimeOrigin::signed(5), 40));
+
+		assert_eq!(<crate::CandidateList<Test>>::get().iter().count(), 3);
+		assert_eq!(Balances::free_balance(3), 80);
+		assert_eq!(Balances::free_balance(4), 70);
+		assert_eq!(Balances::free_balance(5), 60);
+
+		assert_ok!(CollatorSelection::update_bond(RuntimeOrigin::signed(3), 40));
+		assert_ok!(CollatorSelection::update_bond(RuntimeOrigin::signed(4), 60));
+
+		assert_eq!(<crate::CandidateList<Test>>::get().iter().count(), 3);
+		assert_eq!(Balances::free_balance(3), 60);
+		assert_eq!(Balances::free_balance(4), 40);
+		assert_eq!(Balances::free_balance(5), 60);
+	});
+}
+
+#[test]
+fn decrease_candidacy_bond_non_candidate_account() {
+	new_test_ext().execute_with(|| {
+		// given
+		assert_eq!(CollatorSelection::desired_candidates(), 2);
+		assert_eq!(CollatorSelection::candidacy_bond(), 10);
+
+		assert_eq!(<crate::CandidateList<Test>>::get().iter().count(), 0);
+		assert_eq!(CollatorSelection::invulnerables(), vec![1, 2]);
+
+		assert_ok!(CollatorSelection::register_as_candidate(RuntimeOrigin::signed(3)));
+		assert_ok!(CollatorSelection::register_as_candidate(RuntimeOrigin::signed(4)));
+
+		assert_eq!(Balances::free_balance(5), 100);
+		assert_noop!(
+			CollatorSelection::update_bond(RuntimeOrigin::signed(5), 10),
+			Error::<Test>::NotCandidate
+		);
+		assert_eq!(Balances::free_balance(5), 100);
+	});
+}
+
+#[test]
+fn decrease_candidacy_bond_insufficient_funds() {
+	new_test_ext().execute_with(|| {
+		// given
+		assert_eq!(CollatorSelection::desired_candidates(), 2);
+		assert_eq!(CollatorSelection::candidacy_bond(), 10);
+
+		assert_eq!(<crate::CandidateList<Test>>::get().iter().count(), 0);
+		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(5), 100);
+
+		assert_ok!(CollatorSelection::register_as_candidate(RuntimeOrigin::signed(3)));
+		assert_ok!(CollatorSelection::register_as_candidate(RuntimeOrigin::signed(4)));
+		assert_ok!(CollatorSelection::register_as_candidate(RuntimeOrigin::signed(5)));
+
+		assert_ok!(CollatorSelection::update_bond(RuntimeOrigin::signed(3), 60));
+		assert_ok!(CollatorSelection::update_bond(RuntimeOrigin::signed(4), 60));
+		assert_ok!(CollatorSelection::update_bond(RuntimeOrigin::signed(5), 60));
+
+		assert_eq!(Balances::free_balance(3), 40);
+		assert_eq!(Balances::free_balance(4), 40);
+		assert_eq!(Balances::free_balance(5), 40);
+
+		assert_noop!(
+			CollatorSelection::update_bond(RuntimeOrigin::signed(3), 0),
+			Error::<Test>::DepositTooLow
+		);
+
+		assert_noop!(
+			CollatorSelection::update_bond(RuntimeOrigin::signed(4), 5),
+			Error::<Test>::DepositTooLow
+		);
+
+		assert_noop!(
+			CollatorSelection::update_bond(RuntimeOrigin::signed(5), 9),
+			Error::<Test>::DepositTooLow
+		);
+
+		assert_eq!(Balances::free_balance(3), 40);
+		assert_eq!(Balances::free_balance(4), 40);
+		assert_eq!(Balances::free_balance(5), 40);
+	});
+}
+
+#[test]
+fn decrease_candidacy_bond_occupying_top_slot() {
+	new_test_ext().execute_with(|| {
+		assert_eq!(CollatorSelection::desired_candidates(), 2);
+		// Register 3 candidates.
+		assert_ok!(CollatorSelection::register_as_candidate(RuntimeOrigin::signed(3)));
+		assert_ok!(CollatorSelection::register_as_candidate(RuntimeOrigin::signed(4)));
+		assert_ok!(CollatorSelection::register_as_candidate(RuntimeOrigin::signed(5)));
+		// And update their bids.
+		assert_ok!(CollatorSelection::update_bond(RuntimeOrigin::signed(3), 30u64.into()));
+		assert_ok!(CollatorSelection::update_bond(RuntimeOrigin::signed(4), 30u64.into()));
+		assert_ok!(CollatorSelection::update_bond(RuntimeOrigin::signed(5), 60u64.into()));
+
+		// tuple of (id, deposit).
+		let candidate_3 = CandidateInfo { who: 3, deposit: 30 };
+		let candidate_4 = CandidateInfo { who: 4, deposit: 30 };
+		let candidate_5 = CandidateInfo { who: 5, deposit: 60 };
+		assert_eq!(
+			<crate::CandidateList<Test>>::get().iter().cloned().collect::<Vec<_>>(),
+			vec![candidate_4, candidate_3, candidate_5]
+		);
+
+		// Candidates 5 and 3 can't decrease their deposits because they are the 2 top candidates.
+		assert_noop!(
+			CollatorSelection::update_bond(RuntimeOrigin::signed(5), 29),
+			Error::<Test>::InvalidUnreserve,
+		);
+		assert_noop!(
+			CollatorSelection::update_bond(RuntimeOrigin::signed(3), 29),
+			Error::<Test>::InvalidUnreserve,
+		);
+		// But candidate 4 should have be able to decrease the deposit up to the minimum.
+		assert_ok!(CollatorSelection::update_bond(RuntimeOrigin::signed(4), 29u64.into()));
+
+		// Make candidate 4 outbid candidate 3, taking their spot as the second highest bid.
+		assert_ok!(CollatorSelection::update_bond(RuntimeOrigin::signed(4), 35u64.into()));
+
+		// tuple of (id, deposit).
+		let candidate_3 = CandidateInfo { who: 3, deposit: 30 };
+		let candidate_4 = CandidateInfo { who: 4, deposit: 35 };
+		let candidate_5 = CandidateInfo { who: 5, deposit: 60 };
+		assert_eq!(
+			<crate::CandidateList<Test>>::get().iter().cloned().collect::<Vec<_>>(),
+			vec![candidate_3, candidate_4, candidate_5]
+		);
+
+		// Now candidates 5 and 4 are the 2 top candidates, so they can't decrease their deposits.
+		assert_noop!(
+			CollatorSelection::update_bond(RuntimeOrigin::signed(5), 34),
+			Error::<Test>::InvalidUnreserve,
+		);
+		assert_noop!(
+			CollatorSelection::update_bond(RuntimeOrigin::signed(4), 34),
+			Error::<Test>::InvalidUnreserve,
+		);
+		// Candidate 3 should have be able to decrease the deposit up to the minimum now that
+		// they've fallen out of the top spots.
+		assert_ok!(CollatorSelection::update_bond(RuntimeOrigin::signed(3), 10u64.into()));
+	});
+}
+
+#[test]
+fn decrease_candidacy_bond_works() {
+	new_test_ext().execute_with(|| {
+		// given
+		assert_eq!(CollatorSelection::desired_candidates(), 2);
+		assert_eq!(CollatorSelection::candidacy_bond(), 10);
+
+		assert_eq!(<crate::CandidateList<Test>>::get().iter().count(), 0);
+		assert_eq!(CollatorSelection::invulnerables(), vec![1, 2]);
+
+		// take three endowed, non-invulnerables accounts.
+		assert_eq!(Balances::free_balance(3), 100);
+		assert_eq!(Balances::free_balance(4), 100);
+		assert_eq!(Balances::free_balance(5), 100);
+
+		assert_ok!(CollatorSelection::register_as_candidate(RuntimeOrigin::signed(3)));
+		assert_ok!(CollatorSelection::register_as_candidate(RuntimeOrigin::signed(4)));
+		assert_ok!(CollatorSelection::register_as_candidate(RuntimeOrigin::signed(5)));
+
+		assert_eq!(Balances::free_balance(3), 90);
+		assert_eq!(Balances::free_balance(4), 90);
+		assert_eq!(Balances::free_balance(5), 90);
+
+		assert_ok!(CollatorSelection::update_bond(RuntimeOrigin::signed(3), 20));
+		assert_ok!(CollatorSelection::update_bond(RuntimeOrigin::signed(4), 30));
+		assert_ok!(CollatorSelection::update_bond(RuntimeOrigin::signed(5), 40));
+
+		assert_eq!(<crate::CandidateList<Test>>::get().iter().count(), 3);
+		assert_eq!(Balances::free_balance(3), 80);
+		assert_eq!(Balances::free_balance(4), 70);
+		assert_eq!(Balances::free_balance(5), 60);
+
+		assert_ok!(CollatorSelection::update_bond(RuntimeOrigin::signed(3), 10));
+
+		assert_eq!(<crate::CandidateList<Test>>::get().iter().count(), 3);
+		assert_eq!(Balances::free_balance(3), 90);
+		assert_eq!(Balances::free_balance(4), 70);
+		assert_eq!(Balances::free_balance(5), 60);
+	});
+}
+
+#[test]
+fn update_candidacy_bond_with_identical_amount() {
+	new_test_ext().execute_with(|| {
+		// given
+		assert_eq!(CollatorSelection::desired_candidates(), 2);
+		assert_eq!(CollatorSelection::candidacy_bond(), 10);
+
+		assert_eq!(<crate::CandidateList<Test>>::get().iter().count(), 0);
+		assert_eq!(CollatorSelection::invulnerables(), vec![1, 2]);
+
+		// take three endowed, non-invulnerables accounts.
+		assert_eq!(Balances::free_balance(3), 100);
+		assert_eq!(Balances::free_balance(4), 100);
+		assert_eq!(Balances::free_balance(5), 100);
+
+		assert_ok!(CollatorSelection::register_as_candidate(RuntimeOrigin::signed(3)));
+		assert_ok!(CollatorSelection::register_as_candidate(RuntimeOrigin::signed(4)));
+		assert_ok!(CollatorSelection::register_as_candidate(RuntimeOrigin::signed(5)));
+
+		assert_eq!(Balances::free_balance(3), 90);
+		assert_eq!(Balances::free_balance(4), 90);
+		assert_eq!(Balances::free_balance(5), 90);
+
+		assert_ok!(CollatorSelection::update_bond(RuntimeOrigin::signed(3), 20));
+		assert_ok!(CollatorSelection::update_bond(RuntimeOrigin::signed(4), 30));
+		assert_ok!(CollatorSelection::update_bond(RuntimeOrigin::signed(5), 40));
+
+		assert_eq!(<crate::CandidateList<Test>>::get().iter().count(), 3);
+		assert_eq!(Balances::free_balance(3), 80);
+		assert_eq!(Balances::free_balance(4), 70);
+		assert_eq!(Balances::free_balance(5), 60);
+
+		assert_noop!(
+			CollatorSelection::update_bond(RuntimeOrigin::signed(3), 20),
+			Error::<Test>::IdenticalDeposit
+		);
+		assert_eq!(Balances::free_balance(3), 80);
+	});
+}
+
+#[test]
+fn candidate_list_works() {
+	new_test_ext().execute_with(|| {
+		// given
+		assert_eq!(CollatorSelection::desired_candidates(), 2);
+		assert_eq!(CollatorSelection::candidacy_bond(), 10);
+
+		assert_eq!(<crate::CandidateList<Test>>::get().iter().count(), 0);
+		assert_eq!(CollatorSelection::invulnerables(), vec![1, 2]);
+
+		// take three endowed, non-invulnerables accounts.
+		assert_eq!(Balances::free_balance(3), 100);
+		assert_eq!(Balances::free_balance(4), 100);
+		assert_eq!(Balances::free_balance(5), 100);
+
+		assert_ok!(CollatorSelection::register_as_candidate(RuntimeOrigin::signed(3)));
+		assert_ok!(CollatorSelection::register_as_candidate(RuntimeOrigin::signed(4)));
+		assert_ok!(CollatorSelection::register_as_candidate(RuntimeOrigin::signed(5)));
+
+		assert_ok!(CollatorSelection::update_bond(RuntimeOrigin::signed(5), 20));
+
+		assert_ok!(CollatorSelection::update_bond(RuntimeOrigin::signed(3), 30));
+
+		assert_ok!(CollatorSelection::update_bond(RuntimeOrigin::signed(4), 25));
+
+		assert_ok!(CollatorSelection::update_bond(RuntimeOrigin::signed(5), 10));
+
+		let candidate_3 = CandidateInfo { who: 3, deposit: 30 };
+		let candidate_4 = CandidateInfo { who: 4, deposit: 25 };
+		let candidate_5 = CandidateInfo { who: 5, deposit: 10 };
+		assert_eq!(
+			<crate::CandidateList<Test>>::get().iter().cloned().collect::<Vec<_>>(),
+			vec![candidate_5, candidate_4, candidate_3]
+		);
+	});
+}
+
+#[test]
+fn leave_intent() {
+	new_test_ext().execute_with(|| {
+		// register a candidate.
+		assert_ok!(CollatorSelection::register_as_candidate(RuntimeOrigin::signed(3)));
+		assert_eq!(Balances::free_balance(3), 90);
+
+		// register too so can leave above min candidates
+		assert_ok!(CollatorSelection::register_as_candidate(RuntimeOrigin::signed(5)));
+		assert_eq!(Balances::free_balance(5), 90);
+
+		// cannot leave if not candidate.
+		assert_noop!(
+			CollatorSelection::leave_intent(RuntimeOrigin::signed(4)),
+			Error::<Test>::NotCandidate
+		);
+
+		// bond is returned
+		assert_ok!(CollatorSelection::leave_intent(RuntimeOrigin::signed(3)));
+		assert_eq!(Balances::free_balance(3), 100);
+		assert_eq!(CollatorSelection::last_authored_block(3), 0);
+	});
+}
+
+#[test]
+fn authorship_event_handler() {
+	new_test_ext().execute_with(|| {
+		// put 100 in the pot + 5 for ED
+		Balances::make_free_balance_be(&CollatorSelection::account_id(), 105);
+
+		// 4 is the default author.
+		assert_eq!(Balances::free_balance(4), 100);
+		assert_ok!(CollatorSelection::register_as_candidate(RuntimeOrigin::signed(4)));
+		// triggers `note_author`
+		Authorship::on_initialize(1);
+
+		// tuple of (id, deposit).
+		let collator = CandidateInfo { who: 4, deposit: 10 };
+
+		assert_eq!(
+			<crate::CandidateList<Test>>::get().iter().cloned().collect::<Vec<_>>(),
+			vec![collator]
+		);
+		assert_eq!(CollatorSelection::last_authored_block(4), 0);
+
+		// half of the pot goes to the collator who's the author (4 in tests).
+		assert_eq!(Balances::free_balance(4), 140);
+		// half + ED stays.
+		assert_eq!(Balances::free_balance(CollatorSelection::account_id()), 55);
+	});
+}
+
+#[test]
+fn fees_edgecases() {
+	new_test_ext().execute_with(|| {
+		// Nothing panics, no reward when no ED in balance
+		Authorship::on_initialize(1);
+		// put some money into the pot at ED
+		Balances::make_free_balance_be(&CollatorSelection::account_id(), 5);
+		// 4 is the default author.
+		assert_eq!(Balances::free_balance(4), 100);
+		assert_ok!(CollatorSelection::register_as_candidate(RuntimeOrigin::signed(4)));
+		// triggers `note_author`
+		Authorship::on_initialize(1);
+
+		// tuple of (id, deposit).
+		let collator = CandidateInfo { who: 4, deposit: 10 };
+
+		assert_eq!(
+			<crate::CandidateList<Test>>::get().iter().cloned().collect::<Vec<_>>(),
+			vec![collator]
+		);
+		assert_eq!(CollatorSelection::last_authored_block(4), 0);
+		// Nothing received
+		assert_eq!(Balances::free_balance(4), 90);
+		// all fee stays
+		assert_eq!(Balances::free_balance(CollatorSelection::account_id()), 5);
+	});
+}
+
+#[test]
+fn session_management_single_candidate() {
+	new_test_ext().execute_with(|| {
+		initialize_to_block(1);
+
+		assert_eq!(SessionChangeBlock::get(), 0);
+		assert_eq!(SessionHandlerCollators::get(), vec![1, 2]);
+
+		initialize_to_block(4);
+
+		assert_eq!(SessionChangeBlock::get(), 0);
+		assert_eq!(SessionHandlerCollators::get(), vec![1, 2]);
+
+		// add a new collator
+		assert_ok!(CollatorSelection::register_as_candidate(RuntimeOrigin::signed(3)));
+
+		// session won't see this.
+		assert_eq!(SessionHandlerCollators::get(), vec![1, 2]);
+		// but we have a new candidate.
+		assert_eq!(<crate::CandidateList<Test>>::get().iter().count(), 1);
+
+		initialize_to_block(10);
+		assert_eq!(SessionChangeBlock::get(), 10);
+		// pallet-session has 1 session delay; current validators are the same.
+		assert_eq!(Session::validators(), vec![1, 2]);
+		// queued ones are changed, and now we have 3.
+		assert_eq!(Session::queued_keys().len(), 3);
+		// session handlers (aura, et. al.) cannot see this yet.
+		assert_eq!(SessionHandlerCollators::get(), vec![1, 2]);
+
+		initialize_to_block(20);
+		assert_eq!(SessionChangeBlock::get(), 20);
+		// changed are now reflected to session handlers.
+		assert_eq!(SessionHandlerCollators::get(), vec![1, 2, 3]);
+	});
+}
+
+#[test]
+fn session_management_max_candidates() {
+	new_test_ext().execute_with(|| {
+		initialize_to_block(1);
+
+		assert_eq!(SessionChangeBlock::get(), 0);
+		assert_eq!(SessionHandlerCollators::get(), vec![1, 2]);
+
+		initialize_to_block(4);
+
+		assert_eq!(SessionChangeBlock::get(), 0);
+		assert_eq!(SessionHandlerCollators::get(), vec![1, 2]);
+
+		assert_ok!(CollatorSelection::register_as_candidate(RuntimeOrigin::signed(3)));
+		assert_ok!(CollatorSelection::register_as_candidate(RuntimeOrigin::signed(4)));
+		assert_ok!(CollatorSelection::register_as_candidate(RuntimeOrigin::signed(5)));
+
+		// session won't see this.
+		assert_eq!(SessionHandlerCollators::get(), vec![1, 2]);
+		// but we have a new candidate.
+		assert_eq!(<crate::CandidateList<Test>>::get().iter().count(), 3);
+
+		initialize_to_block(10);
+		assert_eq!(SessionChangeBlock::get(), 10);
+		// pallet-session has 1 session delay; current validators are the same.
+		assert_eq!(Session::validators(), vec![1, 2]);
+		// queued ones are changed, and now we have 4.
+		assert_eq!(Session::queued_keys().len(), 4);
+		// session handlers (aura, et. al.) cannot see this yet.
+		assert_eq!(SessionHandlerCollators::get(), vec![1, 2]);
+
+		initialize_to_block(20);
+		assert_eq!(SessionChangeBlock::get(), 20);
+		// changed are now reflected to session handlers.
+		assert_eq!(SessionHandlerCollators::get(), vec![1, 2, 3, 4]);
+	});
+}
+
+#[test]
+fn session_management_increase_bid_with_list_update() {
+	new_test_ext().execute_with(|| {
+		initialize_to_block(1);
+
+		assert_eq!(SessionChangeBlock::get(), 0);
+		assert_eq!(SessionHandlerCollators::get(), vec![1, 2]);
+
+		initialize_to_block(4);
+
+		assert_eq!(SessionChangeBlock::get(), 0);
+		assert_eq!(SessionHandlerCollators::get(), vec![1, 2]);
+
+		assert_ok!(CollatorSelection::register_as_candidate(RuntimeOrigin::signed(3)));
+		assert_ok!(CollatorSelection::register_as_candidate(RuntimeOrigin::signed(4)));
+		assert_ok!(CollatorSelection::register_as_candidate(RuntimeOrigin::signed(5)));
+		assert_ok!(CollatorSelection::update_bond(RuntimeOrigin::signed(5), 60));
+
+		// session won't see this.
+		assert_eq!(SessionHandlerCollators::get(), vec![1, 2]);
+		// but we have a new candidate.
+		assert_eq!(<crate::CandidateList<Test>>::get().iter().count(), 3);
+
+		initialize_to_block(10);
+		assert_eq!(SessionChangeBlock::get(), 10);
+		// pallet-session has 1 session delay; current validators are the same.
+		assert_eq!(Session::validators(), vec![1, 2]);
+		// queued ones are changed, and now we have 4.
+		assert_eq!(Session::queued_keys().len(), 4);
+		// session handlers (aura, et. al.) cannot see this yet.
+		assert_eq!(SessionHandlerCollators::get(), vec![1, 2]);
+
+		initialize_to_block(20);
+		assert_eq!(SessionChangeBlock::get(), 20);
+		// changed are now reflected to session handlers.
+		assert_eq!(SessionHandlerCollators::get(), vec![1, 2, 5, 3]);
+	});
+}
+
+#[test]
+fn session_management_candidate_list_eager_sort() {
+	new_test_ext().execute_with(|| {
+		initialize_to_block(1);
+
+		assert_eq!(SessionChangeBlock::get(), 0);
+		assert_eq!(SessionHandlerCollators::get(), vec![1, 2]);
+
+		initialize_to_block(4);
+
+		assert_eq!(SessionChangeBlock::get(), 0);
+		assert_eq!(SessionHandlerCollators::get(), vec![1, 2]);
+
+		assert_ok!(CollatorSelection::register_as_candidate(RuntimeOrigin::signed(3)));
+		assert_ok!(CollatorSelection::register_as_candidate(RuntimeOrigin::signed(4)));
+		assert_ok!(CollatorSelection::register_as_candidate(RuntimeOrigin::signed(5)));
+		assert_ok!(CollatorSelection::update_bond(RuntimeOrigin::signed(5), 60));
+
+		// session won't see this.
+		assert_eq!(SessionHandlerCollators::get(), vec![1, 2]);
+		// but we have a new candidate.
+		assert_eq!(<crate::CandidateList<Test>>::get().iter().count(), 3);
+
+		initialize_to_block(10);
+		assert_eq!(SessionChangeBlock::get(), 10);
+		// pallet-session has 1 session delay; current validators are the same.
+		assert_eq!(Session::validators(), vec![1, 2]);
+		// queued ones are changed, and now we have 4.
+		assert_eq!(Session::queued_keys().len(), 4);
+		// session handlers (aura, et. al.) cannot see this yet.
+		assert_eq!(SessionHandlerCollators::get(), vec![1, 2]);
+
+		initialize_to_block(20);
+		assert_eq!(SessionChangeBlock::get(), 20);
+		// changed are now reflected to session handlers.
+		assert_eq!(SessionHandlerCollators::get(), vec![1, 2, 5, 3]);
+	});
+}
+
+#[test]
+fn session_management_reciprocal_outbidding() {
+	new_test_ext().execute_with(|| {
+		initialize_to_block(1);
+
+		assert_eq!(SessionChangeBlock::get(), 0);
+		assert_eq!(SessionHandlerCollators::get(), vec![1, 2]);
+
+		initialize_to_block(4);
+
+		assert_eq!(SessionChangeBlock::get(), 0);
+		assert_eq!(SessionHandlerCollators::get(), vec![1, 2]);
+
+		assert_ok!(CollatorSelection::register_as_candidate(RuntimeOrigin::signed(3)));
+		assert_ok!(CollatorSelection::register_as_candidate(RuntimeOrigin::signed(4)));
+		assert_ok!(CollatorSelection::register_as_candidate(RuntimeOrigin::signed(5)));
+
+		assert_ok!(CollatorSelection::update_bond(RuntimeOrigin::signed(5), 60));
+
+		initialize_to_block(5);
+
+		// candidates 3 and 4 saw they were outbid and preemptively bid more
+		// than 5 in the next block.
+		assert_ok!(CollatorSelection::update_bond(RuntimeOrigin::signed(4), 70));
+		assert_ok!(CollatorSelection::update_bond(RuntimeOrigin::signed(3), 70));
+
+		// session won't see this.
+		assert_eq!(SessionHandlerCollators::get(), vec![1, 2]);
+		// but we have a new candidate.
+		assert_eq!(<crate::CandidateList<Test>>::get().iter().count(), 3);
+
+		initialize_to_block(10);
+		assert_eq!(SessionChangeBlock::get(), 10);
+		// pallet-session has 1 session delay; current validators are the same.
+		assert_eq!(Session::validators(), vec![1, 2]);
+		// queued ones are changed, and now we have 4.
+		assert_eq!(Session::queued_keys().len(), 4);
+		// session handlers (aura, et. al.) cannot see this yet.
+		assert_eq!(SessionHandlerCollators::get(), vec![1, 2]);
+
+		initialize_to_block(20);
+		assert_eq!(SessionChangeBlock::get(), 20);
+		// changed are now reflected to session handlers.
+		assert_eq!(SessionHandlerCollators::get(), vec![1, 2, 4, 3]);
+	});
+}
+
+#[test]
+fn session_management_decrease_bid_after_auction() {
+	new_test_ext().execute_with(|| {
+		initialize_to_block(1);
+
+		assert_eq!(SessionChangeBlock::get(), 0);
+		assert_eq!(SessionHandlerCollators::get(), vec![1, 2]);
+
+		initialize_to_block(4);
+
+		assert_eq!(SessionChangeBlock::get(), 0);
+		assert_eq!(SessionHandlerCollators::get(), vec![1, 2]);
+
+		assert_ok!(CollatorSelection::register_as_candidate(RuntimeOrigin::signed(3)));
+		assert_ok!(CollatorSelection::register_as_candidate(RuntimeOrigin::signed(4)));
+		assert_ok!(CollatorSelection::register_as_candidate(RuntimeOrigin::signed(5)));
+
+		assert_ok!(CollatorSelection::update_bond(RuntimeOrigin::signed(5), 60));
+
+		initialize_to_block(5);
+
+		assert_ok!(CollatorSelection::update_bond(RuntimeOrigin::signed(4), 70));
+		assert_ok!(CollatorSelection::update_bond(RuntimeOrigin::signed(3), 70));
+
+		initialize_to_block(5);
+
+		// candidate 5 saw it was outbid and wants to take back its bid, but
+		// not entirely so they still keep their place in the candidate list
+		// in case there is an opportunity in the future.
+		assert_ok!(CollatorSelection::update_bond(RuntimeOrigin::signed(5), 10));
+
+		// session won't see this.
+		assert_eq!(SessionHandlerCollators::get(), vec![1, 2]);
+		// but we have a new candidate.
+		assert_eq!(<crate::CandidateList<Test>>::get().iter().count(), 3);
+
+		initialize_to_block(10);
+		assert_eq!(SessionChangeBlock::get(), 10);
+		// pallet-session has 1 session delay; current validators are the same.
+		assert_eq!(Session::validators(), vec![1, 2]);
+		// queued ones are changed, and now we have 4.
+		assert_eq!(Session::queued_keys().len(), 4);
+		// session handlers (aura, et. al.) cannot see this yet.
+		assert_eq!(SessionHandlerCollators::get(), vec![1, 2]);
+
+		initialize_to_block(20);
+		assert_eq!(SessionChangeBlock::get(), 20);
+		// changed are now reflected to session handlers.
+		assert_eq!(SessionHandlerCollators::get(), vec![1, 2, 4, 3]);
+	});
+}
+
+#[test]
+fn kick_mechanism() {
+	new_test_ext().execute_with(|| {
+		// add a new collator
+		assert_ok!(CollatorSelection::register_as_candidate(RuntimeOrigin::signed(3)));
+		assert_ok!(CollatorSelection::register_as_candidate(RuntimeOrigin::signed(4)));
+		initialize_to_block(10);
+		assert_eq!(<crate::CandidateList<Test>>::get().iter().count(), 2);
+		initialize_to_block(20);
+		assert_eq!(SessionChangeBlock::get(), 20);
+		// 4 authored this block, gets to stay 3 was kicked
+		assert_eq!(<crate::CandidateList<Test>>::get().iter().count(), 1);
+		// 3 will be kicked after 1 session delay
+		assert_eq!(SessionHandlerCollators::get(), vec![1, 2, 3, 4]);
+		// tuple of (id, deposit).
+		let collator = CandidateInfo { who: 4, deposit: 10 };
+		assert_eq!(
+			<crate::CandidateList<Test>>::get().iter().cloned().collect::<Vec<_>>(),
+			vec![collator]
+		);
+		assert_eq!(CollatorSelection::last_authored_block(4), 20);
+		initialize_to_block(30);
+		// 3 gets kicked after 1 session delay
+		assert_eq!(SessionHandlerCollators::get(), vec![1, 2, 4]);
+		// kicked collator gets funds back
 		assert_eq!(Balances::free_balance(3), 100);
 	});
 }
@@ -559,7 +1502,8 @@ fn kick_mechanism() {
 fn should_not_kick_mechanism_too_few() {
 	new_test_ext().execute_with(|| {
 		// remove the invulnerables and add new collators 3 and 5
-		assert_eq!(CollatorSelection::candidates(), Vec::new());
+
+		assert_eq!(<crate::CandidateList<Test>>::get().iter().count(), 0);
 		assert_eq!(CollatorSelection::invulnerables(), vec![1, 2]);
 		assert_ok!(CollatorSelection::remove_invulnerable(
 			RuntimeOrigin::signed(RootAccount::get()),
@@ -573,30 +1517,34 @@ fn should_not_kick_mechanism_too_few() {
 		));
 
 		initialize_to_block(10);
-		assert_eq!(CollatorSelection::candidates().len(), 2);
+		assert_eq!(<crate::CandidateList<Test>>::get().iter().count(), 2);
 
 		initialize_to_block(20);
 		assert_eq!(SessionChangeBlock::get(), 20);
 		// 4 authored this block, 3 is kicked, 5 stays because of too few collators
-		assert_eq!(CollatorSelection::candidates().len(), 1);
+		assert_eq!(<crate::CandidateList<Test>>::get().iter().count(), 1);
 		// 3 will be kicked after 1 session delay
 		assert_eq!(SessionHandlerCollators::get(), vec![3, 5]);
-		let collator = CandidateInfo { who: 5, deposit: 10 };
-		assert_eq!(CollatorSelection::candidates(), vec![collator]);
+		// tuple of (id, deposit).
+		let collator = CandidateInfo { who: 3, deposit: 10 };
+		assert_eq!(
+			<crate::CandidateList<Test>>::get().iter().cloned().collect::<Vec<_>>(),
+			vec![collator]
+		);
 		assert_eq!(CollatorSelection::last_authored_block(4), 20);
 
 		initialize_to_block(30);
 		// 3 gets kicked after 1 session delay
-		assert_eq!(SessionHandlerCollators::get(), vec![5]);
+		assert_eq!(SessionHandlerCollators::get(), vec![3]);
 		// kicked collator gets funds back
-		assert_eq!(Balances::free_balance(3), 100);
+		assert_eq!(Balances::free_balance(5), 100);
 	});
 }
 
 #[test]
 fn should_kick_invulnerables_from_candidates_on_session_change() {
 	new_test_ext().execute_with(|| {
-		assert_eq!(CollatorSelection::candidates(), Vec::new());
+		assert_eq!(<crate::CandidateList<Test>>::get().iter().count(), 0);
 		assert_ok!(CollatorSelection::register_as_candidate(RuntimeOrigin::signed(3)));
 		assert_ok!(CollatorSelection::register_as_candidate(RuntimeOrigin::signed(4)));
 		assert_eq!(Balances::free_balance(3), 90);
@@ -606,16 +1554,22 @@ fn should_kick_invulnerables_from_candidates_on_session_change() {
 			vec![1, 2, 3]
 		));
 
+		// tuple of (id, deposit).
 		let collator_3 = CandidateInfo { who: 3, deposit: 10 };
 		let collator_4 = CandidateInfo { who: 4, deposit: 10 };
 
-		assert_eq!(CollatorSelection::candidates(), vec![collator_3, collator_4.clone()]);
+		let actual_candidates =
+			<crate::CandidateList<Test>>::get().iter().cloned().collect::<Vec<_>>();
+		assert_eq!(actual_candidates, vec![collator_4.clone(), collator_3]);
 		assert_eq!(CollatorSelection::invulnerables(), vec![1, 2, 3]);
 
 		// session change
 		initialize_to_block(10);
 		// 3 is removed from candidates
-		assert_eq!(CollatorSelection::candidates(), vec![collator_4]);
+		assert_eq!(
+			<crate::CandidateList<Test>>::get().iter().cloned().collect::<Vec<_>>(),
+			vec![collator_4]
+		);
 		// but not from invulnerables
 		assert_eq!(CollatorSelection::invulnerables(), vec![1, 2, 3]);
 		// and it got its deposit back
diff --git a/cumulus/pallets/collator-selection/src/weights.rs b/cumulus/pallets/collator-selection/src/weights.rs
index f8f86fb7dec2c08601eebe4d8b345a0f7c930b70..1c01ad6cd6fe8e8ed4bc02c3c2d6703eb2882df4 100644
--- a/cumulus/pallets/collator-selection/src/weights.rs
+++ b/cumulus/pallets/collator-selection/src/weights.rs
@@ -30,9 +30,11 @@ pub trait WeightInfo {
 	fn add_invulnerable(_b: u32, _c: u32) -> Weight;
 	fn remove_invulnerable(_b: u32) -> Weight;
 	fn set_desired_candidates() -> Weight;
-	fn set_candidacy_bond() -> Weight;
+	fn set_candidacy_bond(_c: u32, _k: u32) -> Weight;
 	fn register_as_candidate(_c: u32) -> Weight;
 	fn leave_intent(_c: u32) -> Weight;
+	fn update_bond(_c: u32) -> Weight;
+	fn take_candidate_slot(_c: u32) -> Weight;
 	fn note_author() -> Weight;
 	fn new_session(_c: u32, _r: u32) -> Weight;
 }
@@ -49,7 +51,7 @@ impl<T: frame_system::Config> WeightInfo for SubstrateWeight<T> {
 	fn set_desired_candidates() -> Weight {
 		Weight::from_parts(16_363_000_u64, 0).saturating_add(T::DbWeight::get().writes(1_u64))
 	}
-	fn set_candidacy_bond() -> Weight {
+	fn set_candidacy_bond(_c: u32, _k: u32) -> Weight {
 		Weight::from_parts(16_840_000_u64, 0).saturating_add(T::DbWeight::get().writes(1_u64))
 	}
 	fn register_as_candidate(c: u32) -> Weight {
@@ -66,6 +68,20 @@ impl<T: frame_system::Config> WeightInfo for SubstrateWeight<T> {
 			.saturating_add(T::DbWeight::get().reads(1_u64))
 			.saturating_add(T::DbWeight::get().writes(2_u64))
 	}
+	fn update_bond(c: u32) -> Weight {
+		Weight::from_parts(55_336_000_u64, 0)
+			// Standard Error: 0
+			.saturating_add(Weight::from_parts(151_000_u64, 0).saturating_mul(c as u64))
+			.saturating_add(T::DbWeight::get().reads(1_u64))
+			.saturating_add(T::DbWeight::get().writes(2_u64))
+	}
+	fn take_candidate_slot(c: u32) -> Weight {
+		Weight::from_parts(71_196_000_u64, 0)
+			// Standard Error: 0
+			.saturating_add(Weight::from_parts(198_000_u64, 0).saturating_mul(c as u64))
+			.saturating_add(T::DbWeight::get().reads(4_u64))
+			.saturating_add(T::DbWeight::get().writes(2_u64))
+	}
 	fn note_author() -> Weight {
 		Weight::from_parts(71_461_000_u64, 0)
 			.saturating_add(T::DbWeight::get().reads(3_u64))
@@ -136,7 +152,7 @@ impl WeightInfo for () {
 	fn set_desired_candidates() -> Weight {
 		Weight::from_parts(16_363_000_u64, 0).saturating_add(RocksDbWeight::get().writes(1_u64))
 	}
-	fn set_candidacy_bond() -> Weight {
+	fn set_candidacy_bond(_c: u32, _k: u32) -> Weight {
 		Weight::from_parts(16_840_000_u64, 0).saturating_add(RocksDbWeight::get().writes(1_u64))
 	}
 	fn register_as_candidate(c: u32) -> Weight {
@@ -158,6 +174,20 @@ impl WeightInfo for () {
 			.saturating_add(RocksDbWeight::get().reads(3_u64))
 			.saturating_add(RocksDbWeight::get().writes(4_u64))
 	}
+	fn update_bond(c: u32) -> Weight {
+		Weight::from_parts(55_336_000_u64, 0)
+			// Standard Error: 0
+			.saturating_add(Weight::from_parts(151_000_u64, 0).saturating_mul(c as u64))
+			.saturating_add(RocksDbWeight::get().reads(3_u64))
+			.saturating_add(RocksDbWeight::get().writes(4_u64))
+	}
+	fn take_candidate_slot(c: u32) -> Weight {
+		Weight::from_parts(71_196_000_u64, 0)
+			// Standard Error: 0
+			.saturating_add(Weight::from_parts(198_000_u64, 0).saturating_mul(c as u64))
+			.saturating_add(RocksDbWeight::get().reads(3_u64))
+			.saturating_add(RocksDbWeight::get().writes(4_u64))
+	}
 	fn new_session(r: u32, c: u32) -> Weight {
 		Weight::from_parts(0_u64, 0)
 			// Standard Error: 1_010_000
diff --git a/cumulus/parachains/runtimes/assets/asset-hub-kusama/src/weights/pallet_collator_selection.rs b/cumulus/parachains/runtimes/assets/asset-hub-kusama/src/weights/pallet_collator_selection.rs
index 5c5a31eb348628cb38c1f58a2edf9c2460f6e20c..c686bd6134a7a6a489045416309f7f4c08dcbb43 100644
--- a/cumulus/parachains/runtimes/assets/asset-hub-kusama/src/weights/pallet_collator_selection.rs
+++ b/cumulus/parachains/runtimes/assets/asset-hub-kusama/src/weights/pallet_collator_selection.rs
@@ -123,7 +123,7 @@ impl<T: frame_system::Config> pallet_collator_selection::WeightInfo for WeightIn
 	}
 	/// Storage: `CollatorSelection::CandidacyBond` (r:0 w:1)
 	/// Proof: `CollatorSelection::CandidacyBond` (`max_values`: Some(1), `max_size`: Some(16), added: 511, mode: `MaxEncodedLen`)
-	fn set_candidacy_bond() -> Weight {
+	fn set_candidacy_bond(_c: u32, _k: u32) -> Weight {
 		// Proof Size summary in bytes:
 		//  Measured:  `0`
 		//  Estimated: `0`
@@ -177,6 +177,30 @@ impl<T: frame_system::Config> pallet_collator_selection::WeightInfo for WeightIn
 			.saturating_add(T::DbWeight::get().reads(2))
 			.saturating_add(T::DbWeight::get().writes(2))
 	}
+	fn update_bond(c: u32, ) -> Weight {
+		// Proof Size summary in bytes:
+		//  Measured:  `306 + c * (50 ±0)`
+		//  Estimated: `6287`
+		// Minimum execution time: 34_814_000 picoseconds.
+		Weight::from_parts(36_371_520, 0)
+			.saturating_add(Weight::from_parts(0, 6287))
+			// Standard Error: 2_391
+			.saturating_add(Weight::from_parts(201_700, 0).saturating_mul(c.into()))
+			.saturating_add(T::DbWeight::get().reads(2))
+			.saturating_add(T::DbWeight::get().writes(2))
+	}
+	fn take_candidate_slot(c: u32, ) -> Weight {
+		// Proof Size summary in bytes:
+		//  Measured:  `306 + c * (50 ±0)`
+		//  Estimated: `6287`
+		// Minimum execution time: 34_814_000 picoseconds.
+		Weight::from_parts(36_371_520, 0)
+			.saturating_add(Weight::from_parts(0, 6287))
+			// Standard Error: 2_391
+			.saturating_add(Weight::from_parts(201_700, 0).saturating_mul(c.into()))
+			.saturating_add(T::DbWeight::get().reads(2))
+			.saturating_add(T::DbWeight::get().writes(2))
+	}
 	/// Storage: `System::Account` (r:2 w:2)
 	/// Proof: `System::Account` (`max_values`: None, `max_size`: Some(128), added: 2603, mode: `MaxEncodedLen`)
 	/// Storage: `System::BlockWeight` (r:1 w:1)
diff --git a/cumulus/parachains/runtimes/assets/asset-hub-polkadot/src/weights/pallet_collator_selection.rs b/cumulus/parachains/runtimes/assets/asset-hub-polkadot/src/weights/pallet_collator_selection.rs
index c33e79970ff4774f1b94b218836e9d22fd6fe729..b3062984baf023c083f54bb3f7519c4fbb850b40 100644
--- a/cumulus/parachains/runtimes/assets/asset-hub-polkadot/src/weights/pallet_collator_selection.rs
+++ b/cumulus/parachains/runtimes/assets/asset-hub-polkadot/src/weights/pallet_collator_selection.rs
@@ -121,7 +121,7 @@ impl<T: frame_system::Config> pallet_collator_selection::WeightInfo for WeightIn
 	}
 	/// Storage: `CollatorSelection::CandidacyBond` (r:0 w:1)
 	/// Proof: `CollatorSelection::CandidacyBond` (`max_values`: Some(1), `max_size`: Some(16), added: 511, mode: `MaxEncodedLen`)
-	fn set_candidacy_bond() -> Weight {
+	fn set_candidacy_bond(_c: u32, _k: u32) -> Weight {
 		// Proof Size summary in bytes:
 		//  Measured:  `0`
 		//  Estimated: `0`
@@ -175,6 +175,30 @@ impl<T: frame_system::Config> pallet_collator_selection::WeightInfo for WeightIn
 			.saturating_add(T::DbWeight::get().reads(2))
 			.saturating_add(T::DbWeight::get().writes(2))
 	}
+	fn update_bond(c: u32, ) -> Weight {
+		// Proof Size summary in bytes:
+		//  Measured:  `306 + c * (50 ±0)`
+		//  Estimated: `6287`
+		// Minimum execution time: 34_814_000 picoseconds.
+		Weight::from_parts(36_371_520, 0)
+			.saturating_add(Weight::from_parts(0, 6287))
+			// Standard Error: 2_391
+			.saturating_add(Weight::from_parts(201_700, 0).saturating_mul(c.into()))
+			.saturating_add(T::DbWeight::get().reads(2))
+			.saturating_add(T::DbWeight::get().writes(2))
+	}
+	fn take_candidate_slot(c: u32, ) -> Weight {
+		// Proof Size summary in bytes:
+		//  Measured:  `306 + c * (50 ±0)`
+		//  Estimated: `6287`
+		// Minimum execution time: 34_814_000 picoseconds.
+		Weight::from_parts(36_371_520, 0)
+			.saturating_add(Weight::from_parts(0, 6287))
+			// Standard Error: 2_391
+			.saturating_add(Weight::from_parts(201_700, 0).saturating_mul(c.into()))
+			.saturating_add(T::DbWeight::get().reads(2))
+			.saturating_add(T::DbWeight::get().writes(2))
+	}
 	/// Storage: `System::Account` (r:2 w:2)
 	/// Proof: `System::Account` (`max_values`: None, `max_size`: Some(128), added: 2603, mode: `MaxEncodedLen`)
 	/// Storage: `System::BlockWeight` (r:1 w:1)
diff --git a/cumulus/parachains/runtimes/assets/asset-hub-rococo/src/weights/pallet_collator_selection.rs b/cumulus/parachains/runtimes/assets/asset-hub-rococo/src/weights/pallet_collator_selection.rs
index d98abbbc2d3db26bb757597f9e859752cfa8ed88..aeda7bbbb6a7e47d8d1e1f403c7419439f7f5953 100644
--- a/cumulus/parachains/runtimes/assets/asset-hub-rococo/src/weights/pallet_collator_selection.rs
+++ b/cumulus/parachains/runtimes/assets/asset-hub-rococo/src/weights/pallet_collator_selection.rs
@@ -124,7 +124,7 @@ impl<T: frame_system::Config> pallet_collator_selection::WeightInfo for WeightIn
 	}
 	/// Storage: `CollatorSelection::CandidacyBond` (r:0 w:1)
 	/// Proof: `CollatorSelection::CandidacyBond` (`max_values`: Some(1), `max_size`: Some(16), added: 511, mode: `MaxEncodedLen`)
-	fn set_candidacy_bond() -> Weight {
+	fn set_candidacy_bond(_c: u32, _k: u32) -> Weight {
 		// Proof Size summary in bytes:
 		//  Measured:  `0`
 		//  Estimated: `0`
@@ -178,6 +178,30 @@ impl<T: frame_system::Config> pallet_collator_selection::WeightInfo for WeightIn
 			.saturating_add(T::DbWeight::get().reads(2))
 			.saturating_add(T::DbWeight::get().writes(2))
 	}
+	fn update_bond(c: u32, ) -> Weight {
+		// Proof Size summary in bytes:
+		//  Measured:  `306 + c * (50 ±0)`
+		//  Estimated: `6287`
+		// Minimum execution time: 34_814_000 picoseconds.
+		Weight::from_parts(36_371_520, 0)
+			.saturating_add(Weight::from_parts(0, 6287))
+			// Standard Error: 2_391
+			.saturating_add(Weight::from_parts(201_700, 0).saturating_mul(c.into()))
+			.saturating_add(T::DbWeight::get().reads(2))
+			.saturating_add(T::DbWeight::get().writes(2))
+	}
+	fn take_candidate_slot(c: u32, ) -> Weight {
+		// Proof Size summary in bytes:
+		//  Measured:  `306 + c * (50 ±0)`
+		//  Estimated: `6287`
+		// Minimum execution time: 34_814_000 picoseconds.
+		Weight::from_parts(36_371_520, 0)
+			.saturating_add(Weight::from_parts(0, 6287))
+			// Standard Error: 2_391
+			.saturating_add(Weight::from_parts(201_700, 0).saturating_mul(c.into()))
+			.saturating_add(T::DbWeight::get().reads(2))
+			.saturating_add(T::DbWeight::get().writes(2))
+	}
 	/// Storage: `System::Account` (r:2 w:2)
 	/// Proof: `System::Account` (`max_values`: None, `max_size`: Some(128), added: 2603, mode: `MaxEncodedLen`)
 	/// Storage: `System::BlockWeight` (r:1 w:1)
diff --git a/cumulus/parachains/runtimes/assets/asset-hub-westend/src/weights/pallet_collator_selection.rs b/cumulus/parachains/runtimes/assets/asset-hub-westend/src/weights/pallet_collator_selection.rs
index 095e784cf66d41566a7d20d6d5d2f7348fd47999..1fac2d59ab9609a0220a8d39a3a711dd8bd98437 100644
--- a/cumulus/parachains/runtimes/assets/asset-hub-westend/src/weights/pallet_collator_selection.rs
+++ b/cumulus/parachains/runtimes/assets/asset-hub-westend/src/weights/pallet_collator_selection.rs
@@ -123,7 +123,7 @@ impl<T: frame_system::Config> pallet_collator_selection::WeightInfo for WeightIn
 	}
 	/// Storage: `CollatorSelection::CandidacyBond` (r:0 w:1)
 	/// Proof: `CollatorSelection::CandidacyBond` (`max_values`: Some(1), `max_size`: Some(16), added: 511, mode: `MaxEncodedLen`)
-	fn set_candidacy_bond() -> Weight {
+	fn set_candidacy_bond(_c: u32, _k: u32) -> Weight {
 		// Proof Size summary in bytes:
 		//  Measured:  `0`
 		//  Estimated: `0`
@@ -177,6 +177,30 @@ impl<T: frame_system::Config> pallet_collator_selection::WeightInfo for WeightIn
 			.saturating_add(T::DbWeight::get().reads(2))
 			.saturating_add(T::DbWeight::get().writes(2))
 	}
+	fn update_bond(c: u32, ) -> Weight {
+		// Proof Size summary in bytes:
+		//  Measured:  `306 + c * (50 ±0)`
+		//  Estimated: `6287`
+		// Minimum execution time: 34_814_000 picoseconds.
+		Weight::from_parts(36_371_520, 0)
+			.saturating_add(Weight::from_parts(0, 6287))
+			// Standard Error: 2_391
+			.saturating_add(Weight::from_parts(201_700, 0).saturating_mul(c.into()))
+			.saturating_add(T::DbWeight::get().reads(2))
+			.saturating_add(T::DbWeight::get().writes(2))
+	}
+	fn take_candidate_slot(c: u32, ) -> Weight {
+		// Proof Size summary in bytes:
+		//  Measured:  `306 + c * (50 ±0)`
+		//  Estimated: `6287`
+		// Minimum execution time: 34_814_000 picoseconds.
+		Weight::from_parts(36_371_520, 0)
+			.saturating_add(Weight::from_parts(0, 6287))
+			// Standard Error: 2_391
+			.saturating_add(Weight::from_parts(201_700, 0).saturating_mul(c.into()))
+			.saturating_add(T::DbWeight::get().reads(2))
+			.saturating_add(T::DbWeight::get().writes(2))
+	}
 	/// Storage: `System::Account` (r:2 w:2)
 	/// Proof: `System::Account` (`max_values`: None, `max_size`: Some(128), added: 2603, mode: `MaxEncodedLen`)
 	/// Storage: `System::BlockWeight` (r:1 w:1)
diff --git a/cumulus/parachains/runtimes/bridge-hubs/bridge-hub-kusama/src/weights/pallet_collator_selection.rs b/cumulus/parachains/runtimes/bridge-hubs/bridge-hub-kusama/src/weights/pallet_collator_selection.rs
index cccb7c60924a03c6ed9e8a6df4561e6578190b14..72d8ba4045a95ec6aba6d16670dfb3310c2114ff 100644
--- a/cumulus/parachains/runtimes/bridge-hubs/bridge-hub-kusama/src/weights/pallet_collator_selection.rs
+++ b/cumulus/parachains/runtimes/bridge-hubs/bridge-hub-kusama/src/weights/pallet_collator_selection.rs
@@ -123,7 +123,7 @@ impl<T: frame_system::Config> pallet_collator_selection::WeightInfo for WeightIn
 	}
 	/// Storage: `CollatorSelection::CandidacyBond` (r:0 w:1)
 	/// Proof: `CollatorSelection::CandidacyBond` (`max_values`: Some(1), `max_size`: Some(16), added: 511, mode: `MaxEncodedLen`)
-	fn set_candidacy_bond() -> Weight {
+	fn set_candidacy_bond(_c: u32, _k: u32) -> Weight {
 		// Proof Size summary in bytes:
 		//  Measured:  `0`
 		//  Estimated: `0`
@@ -177,6 +177,30 @@ impl<T: frame_system::Config> pallet_collator_selection::WeightInfo for WeightIn
 			.saturating_add(T::DbWeight::get().reads(2))
 			.saturating_add(T::DbWeight::get().writes(2))
 	}
+	fn update_bond(c: u32, ) -> Weight {
+		// Proof Size summary in bytes:
+		//  Measured:  `306 + c * (50 ±0)`
+		//  Estimated: `6287`
+		// Minimum execution time: 34_814_000 picoseconds.
+		Weight::from_parts(36_371_520, 0)
+			.saturating_add(Weight::from_parts(0, 6287))
+			// Standard Error: 2_391
+			.saturating_add(Weight::from_parts(201_700, 0).saturating_mul(c.into()))
+			.saturating_add(T::DbWeight::get().reads(2))
+			.saturating_add(T::DbWeight::get().writes(2))
+	}
+	fn take_candidate_slot(c: u32, ) -> Weight {
+		// Proof Size summary in bytes:
+		//  Measured:  `306 + c * (50 ±0)`
+		//  Estimated: `6287`
+		// Minimum execution time: 34_814_000 picoseconds.
+		Weight::from_parts(36_371_520, 0)
+			.saturating_add(Weight::from_parts(0, 6287))
+			// Standard Error: 2_391
+			.saturating_add(Weight::from_parts(201_700, 0).saturating_mul(c.into()))
+			.saturating_add(T::DbWeight::get().reads(2))
+			.saturating_add(T::DbWeight::get().writes(2))
+	}
 	/// Storage: `System::Account` (r:2 w:2)
 	/// Proof: `System::Account` (`max_values`: None, `max_size`: Some(128), added: 2603, mode: `MaxEncodedLen`)
 	/// Storage: `System::BlockWeight` (r:1 w:1)
diff --git a/cumulus/parachains/runtimes/bridge-hubs/bridge-hub-polkadot/src/weights/pallet_collator_selection.rs b/cumulus/parachains/runtimes/bridge-hubs/bridge-hub-polkadot/src/weights/pallet_collator_selection.rs
index 6ed2c42918692e7cdcf71a9b861ed7cbcbde2502..f7c78f7db82a0bcf55db5fbfb261e707a084cd9b 100644
--- a/cumulus/parachains/runtimes/bridge-hubs/bridge-hub-polkadot/src/weights/pallet_collator_selection.rs
+++ b/cumulus/parachains/runtimes/bridge-hubs/bridge-hub-polkadot/src/weights/pallet_collator_selection.rs
@@ -123,7 +123,7 @@ impl<T: frame_system::Config> pallet_collator_selection::WeightInfo for WeightIn
 	}
 	/// Storage: `CollatorSelection::CandidacyBond` (r:0 w:1)
 	/// Proof: `CollatorSelection::CandidacyBond` (`max_values`: Some(1), `max_size`: Some(16), added: 511, mode: `MaxEncodedLen`)
-	fn set_candidacy_bond() -> Weight {
+	fn set_candidacy_bond(_c: u32, _k: u32) -> Weight {
 		// Proof Size summary in bytes:
 		//  Measured:  `0`
 		//  Estimated: `0`
@@ -177,6 +177,30 @@ impl<T: frame_system::Config> pallet_collator_selection::WeightInfo for WeightIn
 			.saturating_add(T::DbWeight::get().reads(2))
 			.saturating_add(T::DbWeight::get().writes(2))
 	}
+	fn update_bond(c: u32, ) -> Weight {
+		// Proof Size summary in bytes:
+		//  Measured:  `306 + c * (50 ±0)`
+		//  Estimated: `6287`
+		// Minimum execution time: 34_814_000 picoseconds.
+		Weight::from_parts(36_371_520, 0)
+			.saturating_add(Weight::from_parts(0, 6287))
+			// Standard Error: 2_391
+			.saturating_add(Weight::from_parts(201_700, 0).saturating_mul(c.into()))
+			.saturating_add(T::DbWeight::get().reads(2))
+			.saturating_add(T::DbWeight::get().writes(2))
+	}
+	fn take_candidate_slot(c: u32, ) -> Weight {
+		// Proof Size summary in bytes:
+		//  Measured:  `306 + c * (50 ±0)`
+		//  Estimated: `6287`
+		// Minimum execution time: 34_814_000 picoseconds.
+		Weight::from_parts(36_371_520, 0)
+			.saturating_add(Weight::from_parts(0, 6287))
+			// Standard Error: 2_391
+			.saturating_add(Weight::from_parts(201_700, 0).saturating_mul(c.into()))
+			.saturating_add(T::DbWeight::get().reads(2))
+			.saturating_add(T::DbWeight::get().writes(2))
+	}
 	/// Storage: `System::Account` (r:2 w:2)
 	/// Proof: `System::Account` (`max_values`: None, `max_size`: Some(128), added: 2603, mode: `MaxEncodedLen`)
 	/// Storage: `System::BlockWeight` (r:1 w:1)
diff --git a/cumulus/parachains/runtimes/bridge-hubs/bridge-hub-rococo/src/weights/pallet_collator_selection.rs b/cumulus/parachains/runtimes/bridge-hubs/bridge-hub-rococo/src/weights/pallet_collator_selection.rs
index 1fb0b765c066a17c7c73a8eff0b3f5590504c5f2..f7e233189abb4443320c7bee6f28ee4856302452 100644
--- a/cumulus/parachains/runtimes/bridge-hubs/bridge-hub-rococo/src/weights/pallet_collator_selection.rs
+++ b/cumulus/parachains/runtimes/bridge-hubs/bridge-hub-rococo/src/weights/pallet_collator_selection.rs
@@ -123,7 +123,7 @@ impl<T: frame_system::Config> pallet_collator_selection::WeightInfo for WeightIn
 	}
 	/// Storage: `CollatorSelection::CandidacyBond` (r:0 w:1)
 	/// Proof: `CollatorSelection::CandidacyBond` (`max_values`: Some(1), `max_size`: Some(16), added: 511, mode: `MaxEncodedLen`)
-	fn set_candidacy_bond() -> Weight {
+	fn set_candidacy_bond(_c: u32, _k: u32) -> Weight {
 		// Proof Size summary in bytes:
 		//  Measured:  `0`
 		//  Estimated: `0`
@@ -177,6 +177,30 @@ impl<T: frame_system::Config> pallet_collator_selection::WeightInfo for WeightIn
 			.saturating_add(T::DbWeight::get().reads(2))
 			.saturating_add(T::DbWeight::get().writes(2))
 	}
+	fn update_bond(c: u32, ) -> Weight {
+		// Proof Size summary in bytes:
+		//  Measured:  `306 + c * (50 ±0)`
+		//  Estimated: `6287`
+		// Minimum execution time: 34_814_000 picoseconds.
+		Weight::from_parts(36_371_520, 0)
+			.saturating_add(Weight::from_parts(0, 6287))
+			// Standard Error: 2_391
+			.saturating_add(Weight::from_parts(201_700, 0).saturating_mul(c.into()))
+			.saturating_add(T::DbWeight::get().reads(2))
+			.saturating_add(T::DbWeight::get().writes(2))
+	}
+	fn take_candidate_slot(c: u32, ) -> Weight {
+		// Proof Size summary in bytes:
+		//  Measured:  `306 + c * (50 ±0)`
+		//  Estimated: `6287`
+		// Minimum execution time: 34_814_000 picoseconds.
+		Weight::from_parts(36_371_520, 0)
+			.saturating_add(Weight::from_parts(0, 6287))
+			// Standard Error: 2_391
+			.saturating_add(Weight::from_parts(201_700, 0).saturating_mul(c.into()))
+			.saturating_add(T::DbWeight::get().reads(2))
+			.saturating_add(T::DbWeight::get().writes(2))
+	}
 	/// Storage: `System::Account` (r:2 w:2)
 	/// Proof: `System::Account` (`max_values`: None, `max_size`: Some(128), added: 2603, mode: `MaxEncodedLen`)
 	/// Storage: `System::BlockWeight` (r:1 w:1)
diff --git a/cumulus/parachains/runtimes/bridge-hubs/bridge-hub-westend/src/weights/pallet_collator_selection.rs b/cumulus/parachains/runtimes/bridge-hubs/bridge-hub-westend/src/weights/pallet_collator_selection.rs
index 9cbfa6ce80e3e4b45338786c3705500bf27a220f..9dcee77082b99f586707a77a540cf6b13bd2be16 100644
--- a/cumulus/parachains/runtimes/bridge-hubs/bridge-hub-westend/src/weights/pallet_collator_selection.rs
+++ b/cumulus/parachains/runtimes/bridge-hubs/bridge-hub-westend/src/weights/pallet_collator_selection.rs
@@ -124,7 +124,7 @@ impl<T: frame_system::Config> pallet_collator_selection::WeightInfo for WeightIn
 	}
 	/// Storage: `CollatorSelection::CandidacyBond` (r:0 w:1)
 	/// Proof: `CollatorSelection::CandidacyBond` (`max_values`: Some(1), `max_size`: Some(16), added: 511, mode: `MaxEncodedLen`)
-	fn set_candidacy_bond() -> Weight {
+	fn set_candidacy_bond(_c: u32, _k: u32) -> Weight {
 		// Proof Size summary in bytes:
 		//  Measured:  `0`
 		//  Estimated: `0`
@@ -178,6 +178,30 @@ impl<T: frame_system::Config> pallet_collator_selection::WeightInfo for WeightIn
 			.saturating_add(T::DbWeight::get().reads(2))
 			.saturating_add(T::DbWeight::get().writes(2))
 	}
+	fn update_bond(c: u32, ) -> Weight {
+		// Proof Size summary in bytes:
+		//  Measured:  `306 + c * (50 ±0)`
+		//  Estimated: `6287`
+		// Minimum execution time: 34_814_000 picoseconds.
+		Weight::from_parts(36_371_520, 0)
+			.saturating_add(Weight::from_parts(0, 6287))
+			// Standard Error: 2_391
+			.saturating_add(Weight::from_parts(201_700, 0).saturating_mul(c.into()))
+			.saturating_add(T::DbWeight::get().reads(2))
+			.saturating_add(T::DbWeight::get().writes(2))
+	}
+	fn take_candidate_slot(c: u32, ) -> Weight {
+		// Proof Size summary in bytes:
+		//  Measured:  `306 + c * (50 ±0)`
+		//  Estimated: `6287`
+		// Minimum execution time: 34_814_000 picoseconds.
+		Weight::from_parts(36_371_520, 0)
+			.saturating_add(Weight::from_parts(0, 6287))
+			// Standard Error: 2_391
+			.saturating_add(Weight::from_parts(201_700, 0).saturating_mul(c.into()))
+			.saturating_add(T::DbWeight::get().reads(2))
+			.saturating_add(T::DbWeight::get().writes(2))
+	}
 	/// Storage: `System::Account` (r:2 w:2)
 	/// Proof: `System::Account` (`max_values`: None, `max_size`: Some(128), added: 2603, mode: `MaxEncodedLen`)
 	/// Storage: `System::BlockWeight` (r:1 w:1)
diff --git a/cumulus/parachains/runtimes/collectives/collectives-polkadot/src/weights/pallet_collator_selection.rs b/cumulus/parachains/runtimes/collectives/collectives-polkadot/src/weights/pallet_collator_selection.rs
index 2c729e8dc1078e70b1329c3b30ff05480109d121..03f3ff602a5b3e91c4e2ff90a4a3433d513079a1 100644
--- a/cumulus/parachains/runtimes/collectives/collectives-polkadot/src/weights/pallet_collator_selection.rs
+++ b/cumulus/parachains/runtimes/collectives/collectives-polkadot/src/weights/pallet_collator_selection.rs
@@ -121,7 +121,7 @@ impl<T: frame_system::Config> pallet_collator_selection::WeightInfo for WeightIn
 	}
 	/// Storage: `CollatorSelection::CandidacyBond` (r:0 w:1)
 	/// Proof: `CollatorSelection::CandidacyBond` (`max_values`: Some(1), `max_size`: Some(16), added: 511, mode: `MaxEncodedLen`)
-	fn set_candidacy_bond() -> Weight {
+	fn set_candidacy_bond(_c: u32, _k: u32) -> Weight {
 		// Proof Size summary in bytes:
 		//  Measured:  `0`
 		//  Estimated: `0`
@@ -175,6 +175,30 @@ impl<T: frame_system::Config> pallet_collator_selection::WeightInfo for WeightIn
 			.saturating_add(T::DbWeight::get().reads(2))
 			.saturating_add(T::DbWeight::get().writes(2))
 	}
+	fn update_bond(c: u32, ) -> Weight {
+		// Proof Size summary in bytes:
+		//  Measured:  `306 + c * (50 ±0)`
+		//  Estimated: `6287`
+		// Minimum execution time: 34_814_000 picoseconds.
+		Weight::from_parts(36_371_520, 0)
+			.saturating_add(Weight::from_parts(0, 6287))
+			// Standard Error: 2_391
+			.saturating_add(Weight::from_parts(201_700, 0).saturating_mul(c.into()))
+			.saturating_add(T::DbWeight::get().reads(2))
+			.saturating_add(T::DbWeight::get().writes(2))
+	}
+	fn take_candidate_slot(c: u32, ) -> Weight {
+		// Proof Size summary in bytes:
+		//  Measured:  `306 + c * (50 ±0)`
+		//  Estimated: `6287`
+		// Minimum execution time: 34_814_000 picoseconds.
+		Weight::from_parts(36_371_520, 0)
+			.saturating_add(Weight::from_parts(0, 6287))
+			// Standard Error: 2_391
+			.saturating_add(Weight::from_parts(201_700, 0).saturating_mul(c.into()))
+			.saturating_add(T::DbWeight::get().reads(2))
+			.saturating_add(T::DbWeight::get().writes(2))
+	}
 	/// Storage: `System::Account` (r:2 w:2)
 	/// Proof: `System::Account` (`max_values`: None, `max_size`: Some(128), added: 2603, mode: `MaxEncodedLen`)
 	/// Storage: `System::BlockWeight` (r:1 w:1)