From d5f5127354987cab47690b848128ea18e0b441b9 Mon Sep 17 00:00:00 2001
From: Shawn Tabrizi <shawntabrizi@gmail.com>
Date: Sat, 12 Feb 2022 18:16:22 +0100
Subject: [PATCH] Allow two Parachains to swap (#4772)

* add support for parachain to parachain swap

* enable swaps on kusama

* sanity test in paras_registrar

* express more errors

* finish up tests

* fmt

* make fields pub

* refactor integration tests to use real accounts

* Update Crowdloan Account to FundIndex (#4824)

* update fund account to use index

* fix integration tests

* Update runtime/common/src/crowdloan.rs

* finish parachain swap test

* format

* fix warning

* fix spacing

* fix formatting

* write migrations

* add migration

* fixes

* more fixes to migration

* Update runtime/common/src/crowdloan/mod.rs

Co-authored-by: Zeke Mostov <z.mostov@gmail.com>

* Update runtime/common/src/paras_registrar.rs

* Update migration.rs

* extract swap function

Co-authored-by: Zeke Mostov <z.mostov@gmail.com>
---
 .../runtime/common/src/crowdloan/migration.rs | 130 +++++
 .../src/{crowdloan.rs => crowdloan/mod.rs}    | 118 ++--
 .../runtime/common/src/integration_tests.rs   | 502 ++++++++++++------
 .../runtime/common/src/paras_registrar.rs     | 222 ++++++--
 polkadot/runtime/kusama/src/lib.rs            |  26 +-
 polkadot/runtime/polkadot/src/lib.rs          |  20 +-
 polkadot/runtime/rococo/src/lib.rs            |  20 +-
 polkadot/runtime/westend/src/lib.rs           |  20 +-
 8 files changed, 811 insertions(+), 247 deletions(-)
 create mode 100644 polkadot/runtime/common/src/crowdloan/migration.rs
 rename polkadot/runtime/common/src/{crowdloan.rs => crowdloan/mod.rs} (96%)

diff --git a/polkadot/runtime/common/src/crowdloan/migration.rs b/polkadot/runtime/common/src/crowdloan/migration.rs
new file mode 100644
index 00000000000..478471454f3
--- /dev/null
+++ b/polkadot/runtime/common/src/crowdloan/migration.rs
@@ -0,0 +1,130 @@
+// Copyright 2017-2020 Parity Technologies (UK) Ltd.
+// This file is part of Polkadot.
+
+// Polkadot is free software: you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+
+// Polkadot is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+// GNU General Public License for more details.
+
+// You should have received a copy of the GNU General Public License
+// along with Polkadot.  If not, see <http://www.gnu.org/licenses/>.
+
+use super::*;
+use frame_support::generate_storage_alias;
+
+/// Migrations for using fund index to create fund accounts instead of para ID.
+pub mod crowdloan_index_migration {
+	use super::*;
+
+	// The old way we generated fund accounts.
+	fn old_fund_account_id<T: Config>(index: ParaId) -> T::AccountId {
+		T::PalletId::get().into_sub_account(index)
+	}
+
+	pub fn pre_migrate<T: Config>() -> Result<(), &'static str> {
+		// `NextTrieIndex` should have a value.
+		generate_storage_alias!(Crowdloan, NextTrieIndex => Value<FundIndex>);
+		let next_index = NextTrieIndex::get().unwrap_or_default();
+		ensure!(next_index > 0, "Next index is zero, which implies no migration is needed.");
+
+		log::info!(
+			target: "runtime",
+			"next trie index: {:?}",
+			next_index,
+		);
+
+		// Each fund should have some non-zero balance.
+		for (para_id, fund) in Funds::<T>::iter() {
+			let old_fund_account = old_fund_account_id::<T>(para_id);
+			let total_balance = CurrencyOf::<T>::total_balance(&old_fund_account);
+
+			log::info!(
+				target: "runtime",
+				"para_id={:?}, old_fund_account={:?}, total_balance={:?}, fund.raised={:?}",
+				para_id, old_fund_account, total_balance, fund.raised
+			);
+
+			ensure!(
+				total_balance >= fund.raised,
+				"Total balance is not equal to the funds raised."
+			);
+			ensure!(total_balance > Zero::zero(), "Total balance is equal to zero.");
+		}
+
+		Ok(())
+	}
+
+	/// This migration converts crowdloans to use a crowdloan index rather than the parachain id as a
+	/// unique identifier. This makes it easier to swap two crowdloans between parachains.
+	pub fn migrate<T: Config>() -> frame_support::weights::Weight {
+		let mut weight = 0;
+
+		// First migrate `NextTrieIndex` counter to `NextFundIndex`.
+		generate_storage_alias!(Crowdloan, NextTrieIndex => Value<FundIndex>);
+
+		let next_index = NextTrieIndex::take().unwrap_or_default();
+		NextFundIndex::<T>::set(next_index);
+
+		weight = weight.saturating_add(T::DbWeight::get().reads_writes(1, 2));
+
+		// Migrate all accounts from `old_fund_account` to `fund_account` using `fund_index`.
+		for (para_id, fund) in Funds::<T>::iter() {
+			let old_fund_account = old_fund_account_id::<T>(para_id);
+			let new_fund_account = Pallet::<T>::fund_account_id(fund.fund_index);
+
+			// Funds should only have a free balance and a reserve balance. Both of these are in the
+			// `Account` storage item, so we just swap them.
+			let account_info = frame_system::Account::<T>::take(old_fund_account);
+			frame_system::Account::<T>::insert(new_fund_account, account_info);
+
+			weight = weight.saturating_add(T::DbWeight::get().reads_writes(1, 2));
+		}
+
+		weight
+	}
+
+	pub fn post_migrate<T: Config>() -> Result<(), &'static str> {
+		// `NextTrieIndex` should not have a value, and `NextFundIndex` should.
+		generate_storage_alias!(Crowdloan, NextTrieIndex => Value<FundIndex>);
+		ensure!(NextTrieIndex::get().is_none(), "NextTrieIndex still has a value.");
+
+		let next_index = NextFundIndex::<T>::get();
+		log::info!(
+			target: "runtime",
+			"next fund index: {:?}",
+			next_index,
+		);
+
+		ensure!(
+			next_index > 0,
+			"NextFundIndex was not migrated or is zero. We assume it cannot be zero else no migration is needed."
+		);
+
+		// Each fund should have balance migrated correctly.
+		for (para_id, fund) in Funds::<T>::iter() {
+			// Old fund account is deleted.
+			let old_fund_account = old_fund_account_id::<T>(para_id);
+			ensure!(
+				frame_system::Account::<T>::get(&old_fund_account) == Default::default(),
+				"Old account wasn't reset to default value."
+			);
+
+			// New fund account has the correct balance.
+			let new_fund_account = Pallet::<T>::fund_account_id(fund.fund_index);
+			let total_balance = CurrencyOf::<T>::total_balance(&new_fund_account);
+
+			ensure!(
+				total_balance >= fund.raised,
+				"Total balance in new account is different than the funds raised."
+			);
+			ensure!(total_balance > Zero::zero(), "Total balance in the account is zero.");
+		}
+
+		Ok(())
+	}
+}
diff --git a/polkadot/runtime/common/src/crowdloan.rs b/polkadot/runtime/common/src/crowdloan/mod.rs
similarity index 96%
rename from polkadot/runtime/common/src/crowdloan.rs
rename to polkadot/runtime/common/src/crowdloan/mod.rs
index 84b1be08f7e..44d45e6ec79 100644
--- a/polkadot/runtime/common/src/crowdloan.rs
+++ b/polkadot/runtime/common/src/crowdloan/mod.rs
@@ -49,6 +49,8 @@
 //! the parachain remains active. Users can withdraw their funds once the slot is completed and funds are
 //! returned to the crowdloan account.
 
+pub mod migration;
+
 use crate::{
 	slot_range::SlotRange,
 	traits::{Auctioneer, Registrar},
@@ -87,7 +89,7 @@ type BalanceOf<T> = <CurrencyOf<T> as Currency<<T as frame_system::Config>::Acco
 type NegativeImbalanceOf<T> =
 	<CurrencyOf<T> as Currency<<T as frame_system::Config>::AccountId>>::NegativeImbalance;
 
-type TrieIndex = u32;
+type FundIndex = u32;
 
 pub trait WeightInfo {
 	fn create() -> Weight;
@@ -145,33 +147,33 @@ pub enum LastContribution<BlockNumber> {
 #[codec(dumb_trait_bound)]
 pub struct FundInfo<AccountId, Balance, BlockNumber, LeasePeriod> {
 	/// The owning account who placed the deposit.
-	depositor: AccountId,
+	pub depositor: AccountId,
 	/// An optional verifier. If exists, contributions must be signed by verifier.
-	verifier: Option<MultiSigner>,
+	pub verifier: Option<MultiSigner>,
 	/// The amount of deposit placed.
-	deposit: Balance,
+	pub deposit: Balance,
 	/// The total amount raised.
-	raised: Balance,
+	pub raised: Balance,
 	/// Block number after which the funding must have succeeded. If not successful at this number
 	/// then everyone may withdraw their funds.
-	end: BlockNumber,
+	pub end: BlockNumber,
 	/// A hard-cap on the amount that may be contributed.
-	cap: Balance,
+	pub cap: Balance,
 	/// The most recent block that this had a contribution. Determines if we make a bid or not.
 	/// If this is `Never`, this fund has never received a contribution.
 	/// If this is `PreEnding(n)`, this fund received a contribution sometime in auction
 	/// number `n` before the ending period.
 	/// If this is `Ending(n)`, this fund received a contribution during the current ending period,
 	/// where `n` is how far into the ending period the contribution was made.
-	last_contribution: LastContribution<BlockNumber>,
+	pub last_contribution: LastContribution<BlockNumber>,
 	/// First lease period in range to bid on; it's actually a `LeasePeriod`, but that's the same type
 	/// as `BlockNumber`.
-	first_period: LeasePeriod,
+	pub first_period: LeasePeriod,
 	/// Last lease period in range to bid on; it's actually a `LeasePeriod`, but that's the same type
 	/// as `BlockNumber`.
-	last_period: LeasePeriod,
-	/// Index used for the child trie of this fund
-	trie_index: TrieIndex,
+	pub last_period: LeasePeriod,
+	/// Unique index used to represent this fund.
+	pub fund_index: FundIndex,
 }
 
 #[frame_support::pallet]
@@ -244,10 +246,10 @@ pub mod pallet {
 	#[pallet::getter(fn endings_count)]
 	pub(super) type EndingsCount<T> = StorageValue<_, u32, ValueQuery>;
 
-	/// Tracker for the next available trie index
+	/// Tracker for the next available fund index
 	#[pallet::storage]
-	#[pallet::getter(fn next_trie_index)]
-	pub(super) type NextTrieIndex<T> = StorageValue<_, u32, ValueQuery>;
+	#[pallet::getter(fn next_fund_index)]
+	pub(super) type NextFundIndex<T> = StorageValue<_, u32, ValueQuery>;
 
 	#[pallet::event]
 	#[pallet::generate_deposit(pub(super) fn deposit_event)]
@@ -342,7 +344,7 @@ pub mod pallet {
 					// Care needs to be taken by the crowdloan creator that this function will succeed given
 					// the crowdloaning configuration. We do some checks ahead of time in crowdloan `create`.
 					let result = T::Auctioneer::place_bid(
-						Self::fund_account_id(para_id),
+						Self::fund_account_id(fund.fund_index),
 						para_id,
 						fund.first_period,
 						fund.last_period,
@@ -408,8 +410,8 @@ pub mod pallet {
 			ensure!(depositor == manager, Error::<T>::InvalidOrigin);
 			ensure!(T::Registrar::is_registered(index), Error::<T>::InvalidParaId);
 
-			let trie_index = Self::next_trie_index();
-			let new_trie_index = trie_index.checked_add(1).ok_or(Error::<T>::Overflow)?;
+			let fund_index = Self::next_fund_index();
+			let new_fund_index = fund_index.checked_add(1).ok_or(Error::<T>::Overflow)?;
 
 			let deposit = T::SubmissionDeposit::get();
 
@@ -427,11 +429,11 @@ pub mod pallet {
 					last_contribution: LastContribution::Never,
 					first_period,
 					last_period,
-					trie_index,
+					fund_index,
 				},
 			);
 
-			NextTrieIndex::<T>::put(new_trie_index);
+			NextFundIndex::<T>::put(new_fund_index);
 			// Add a lock to the para so that the configuration cannot be changed.
 			T::Registrar::apply_lock(index);
 
@@ -479,15 +481,15 @@ pub mod pallet {
 
 			let mut fund = Self::funds(index).ok_or(Error::<T>::InvalidParaId)?;
 			let now = frame_system::Pallet::<T>::block_number();
-			let fund_account = Self::fund_account_id(index);
+			let fund_account = Self::fund_account_id(fund.fund_index);
 			Self::ensure_crowdloan_ended(now, &fund_account, &fund)?;
 
-			let (balance, _) = Self::contribution_get(fund.trie_index, &who);
+			let (balance, _) = Self::contribution_get(fund.fund_index, &who);
 			ensure!(balance > Zero::zero(), Error::<T>::NoContributions);
 
 			CurrencyOf::<T>::transfer(&fund_account, &who, balance, AllowDeath)?;
 
-			Self::contribution_kill(fund.trie_index, &who);
+			Self::contribution_kill(fund.fund_index, &who);
 			fund.raised = fund.raised.saturating_sub(balance);
 
 			Funds::<T>::insert(index, &fund);
@@ -510,12 +512,12 @@ pub mod pallet {
 
 			let mut fund = Self::funds(index).ok_or(Error::<T>::InvalidParaId)?;
 			let now = frame_system::Pallet::<T>::block_number();
-			let fund_account = Self::fund_account_id(index);
+			let fund_account = Self::fund_account_id(fund.fund_index);
 			Self::ensure_crowdloan_ended(now, &fund_account, &fund)?;
 
 			let mut refund_count = 0u32;
 			// Try killing the crowdloan child trie
-			let contributions = Self::contribution_iterator(fund.trie_index);
+			let contributions = Self::contribution_iterator(fund.fund_index);
 			// Assume everyone will be refunded.
 			let mut all_refunded = true;
 			for (who, (balance, _)) in contributions {
@@ -525,7 +527,7 @@ pub mod pallet {
 					break
 				}
 				CurrencyOf::<T>::transfer(&fund_account, &who, balance, AllowDeath)?;
-				Self::contribution_kill(fund.trie_index, &who);
+				Self::contribution_kill(fund.fund_index, &who);
 				fund.raised = fund.raised.saturating_sub(balance);
 				refund_count += 1;
 			}
@@ -561,7 +563,7 @@ pub mod pallet {
 			// Assuming state is not corrupted, the child trie should already be cleaned up
 			// and all funds in the crowdloan account have been returned. If not, governance
 			// can take care of that.
-			debug_assert!(Self::contribution_iterator(fund.trie_index).count().is_zero());
+			debug_assert!(Self::contribution_iterator(fund.fund_index).count().is_zero());
 
 			CurrencyOf::<T>::unreserve(&fund.depositor, fund.deposit);
 			Funds::<T>::remove(index);
@@ -598,7 +600,7 @@ pub mod pallet {
 					last_contribution: fund.last_contribution,
 					first_period,
 					last_period,
-					trie_index: fund.trie_index,
+					fund_index: fund.fund_index,
 				},
 			);
 
@@ -616,10 +618,10 @@ pub mod pallet {
 			ensure!(memo.len() <= T::MaxMemoLength::get().into(), Error::<T>::MemoTooLarge);
 			let fund = Self::funds(index).ok_or(Error::<T>::InvalidParaId)?;
 
-			let (balance, _) = Self::contribution_get(fund.trie_index, &who);
+			let (balance, _) = Self::contribution_get(fund.fund_index, &who);
 			ensure!(balance > Zero::zero(), Error::<T>::NoContributions);
 
-			Self::contribution_put(fund.trie_index, &who, &balance, &memo);
+			Self::contribution_put(fund.fund_index, &who, &balance, &memo);
 			Self::deposit_event(Event::<T>::MemoUpdated(who, index, memo));
 			Ok(())
 		}
@@ -658,11 +660,11 @@ impl<T: Config> Pallet<T> {
 	///
 	/// This actually does computation. If you need to keep using it, then make sure you cache the
 	/// value and only call this once.
-	pub fn fund_account_id(index: ParaId) -> T::AccountId {
+	pub fn fund_account_id(index: FundIndex) -> T::AccountId {
 		T::PalletId::get().into_sub_account(index)
 	}
 
-	pub fn id_from_index(index: TrieIndex) -> child::ChildInfo {
+	pub fn id_from_index(index: FundIndex) -> child::ChildInfo {
 		let mut buf = Vec::new();
 		buf.extend_from_slice(b"crowdloan");
 		buf.extend_from_slice(&index.encode()[..]);
@@ -670,7 +672,7 @@ impl<T: Config> Pallet<T> {
 	}
 
 	pub fn contribution_put(
-		index: TrieIndex,
+		index: FundIndex,
 		who: &T::AccountId,
 		balance: &BalanceOf<T>,
 		memo: &[u8],
@@ -678,22 +680,22 @@ impl<T: Config> Pallet<T> {
 		who.using_encoded(|b| child::put(&Self::id_from_index(index), b, &(balance, memo)));
 	}
 
-	pub fn contribution_get(index: TrieIndex, who: &T::AccountId) -> (BalanceOf<T>, Vec<u8>) {
+	pub fn contribution_get(index: FundIndex, who: &T::AccountId) -> (BalanceOf<T>, Vec<u8>) {
 		who.using_encoded(|b| {
 			child::get_or_default::<(BalanceOf<T>, Vec<u8>)>(&Self::id_from_index(index), b)
 		})
 	}
 
-	pub fn contribution_kill(index: TrieIndex, who: &T::AccountId) {
+	pub fn contribution_kill(index: FundIndex, who: &T::AccountId) {
 		who.using_encoded(|b| child::kill(&Self::id_from_index(index), b));
 	}
 
-	pub fn crowdloan_kill(index: TrieIndex) -> child::KillStorageResult {
+	pub fn crowdloan_kill(index: FundIndex) -> child::KillStorageResult {
 		child::kill_storage(&Self::id_from_index(index), Some(T::RemoveKeysLimit::get()))
 	}
 
 	pub fn contribution_iterator(
-		index: TrieIndex,
+		index: FundIndex,
 	) -> ChildTriePrefixIterator<(T::AccountId, (BalanceOf<T>, Vec<u8>))> {
 		ChildTriePrefixIterator::<_>::with_prefix_over_key::<Identity>(
 			&Self::id_from_index(index),
@@ -752,7 +754,7 @@ impl<T: Config> Pallet<T> {
 		ensure!(current_lease_period <= fund.first_period, Error::<T>::ContributionPeriodOver);
 
 		// Make sure crowdloan has not already won.
-		let fund_account = Self::fund_account_id(index);
+		let fund_account = Self::fund_account_id(fund.fund_index);
 		ensure!(
 			!T::Auctioneer::has_won_an_auction(index, &fund_account),
 			Error::<T>::BidOrLeaseActive
@@ -762,7 +764,7 @@ impl<T: Config> Pallet<T> {
 		// contributions into the auction when it would not impact the outcome.
 		ensure!(!T::Auctioneer::auction_status(now).is_vrf(), Error::<T>::VrfDelayInProgress);
 
-		let (old_balance, memo) = Self::contribution_get(fund.trie_index, &who);
+		let (old_balance, memo) = Self::contribution_get(fund.fund_index, &who);
 
 		if let Some(ref verifier) = fund.verifier {
 			let signature = signature.ok_or(Error::<T>::InvalidSignature)?;
@@ -776,7 +778,7 @@ impl<T: Config> Pallet<T> {
 		CurrencyOf::<T>::transfer(&who, &fund_account, value, existence)?;
 
 		let balance = old_balance.saturating_add(value);
-		Self::contribution_put(fund.trie_index, &who, &balance, &memo);
+		Self::contribution_put(fund.fund_index, &who, &balance, &memo);
 
 		if T::Auctioneer::auction_status(now).is_ending().is_some() {
 			match fund.last_contribution {
@@ -966,7 +968,8 @@ mod tests {
 	// Emulate what would happen if we won an auction:
 	// balance is reserved and a deposit_held is recorded
 	fn set_winner(para: ParaId, who: u64, winner: bool) {
-		let account_id = Crowdloan::fund_account_id(para);
+		let fund = Funds::<Test>::get(para).unwrap();
+		let account_id = Crowdloan::fund_account_id(fund.fund_index);
 		if winner {
 			let free_balance = Balances::free_balance(&account_id);
 			Balances::reserve(&account_id, free_balance)
@@ -1177,7 +1180,7 @@ mod tests {
 				last_contribution: LastContribution::Never,
 				first_period: 1,
 				last_period: 4,
-				trie_index: 0,
+				fund_index: 0,
 			};
 			assert_eq!(Crowdloan::funds(para), Some(fund_info));
 			// User has deposit removed from their free balance
@@ -1217,7 +1220,7 @@ mod tests {
 				last_contribution: LastContribution::Never,
 				first_period: 1,
 				last_period: 4,
-				trie_index: 0,
+				fund_index: 0,
 			};
 			assert_eq!(Crowdloan::funds(ParaId::from(0)), Some(fund_info));
 			// User has deposit removed from their free balance
@@ -1270,6 +1273,7 @@ mod tests {
 	fn contribute_works() {
 		new_test_ext().execute_with(|| {
 			let para = new_para();
+			let index = NextFundIndex::<Test>::get();
 
 			// Set up a crowdloan
 			assert_ok!(Crowdloan::create(Origin::signed(1), para, 1000, 1, 4, 9, None));
@@ -1284,7 +1288,7 @@ mod tests {
 			// Contributions are stored in the trie
 			assert_eq!(Crowdloan::contribution_get(u32::from(para), &1).0, 49);
 			// Contributions appear in free balance of crowdloan
-			assert_eq!(Balances::free_balance(Crowdloan::fund_account_id(para)), 49);
+			assert_eq!(Balances::free_balance(Crowdloan::fund_account_id(index)), 49);
 			// Crowdloan is added to NewRaise
 			assert_eq!(Crowdloan::new_raise(), vec![para]);
 
@@ -1300,6 +1304,7 @@ mod tests {
 	fn contribute_with_verifier_works() {
 		new_test_ext().execute_with(|| {
 			let para = new_para();
+			let index = NextFundIndex::<Test>::get();
 			let pubkey = crypto::create_ed25519_pubkey(b"//verifier".to_vec());
 			// Set up a crowdloan
 			assert_ok!(Crowdloan::create(
@@ -1364,7 +1369,7 @@ mod tests {
 			assert_ok!(Crowdloan::contribute(Origin::signed(1), para, 10, Some(valid_signature_2)));
 
 			// Contributions appear in free balance of crowdloan
-			assert_eq!(Balances::free_balance(Crowdloan::fund_account_id(para)), 59);
+			assert_eq!(Balances::free_balance(Crowdloan::fund_account_id(index)), 59);
 
 			// Contribution amount is correct
 			let fund = Crowdloan::funds(para).unwrap();
@@ -1409,9 +1414,10 @@ mod tests {
 
 			// If a crowdloan has already won, it should not allow contributions.
 			let para_2 = new_para();
+			let index = NextFundIndex::<Test>::get();
 			assert_ok!(Crowdloan::create(Origin::signed(1), para_2, 1000, 1, 4, 40, None));
 			// Emulate a win by leasing out and putting a deposit. Slots pallet would normally do this.
-			let crowdloan_account = Crowdloan::fund_account_id(para_2);
+			let crowdloan_account = Crowdloan::fund_account_id(index);
 			set_winner(para_2, crowdloan_account, true);
 			assert_noop!(
 				Crowdloan::contribute(Origin::signed(1), para_2, 49, None),
@@ -1478,6 +1484,7 @@ mod tests {
 	fn bidding_works() {
 		new_test_ext().execute_with(|| {
 			let para = new_para();
+			let index = NextFundIndex::<Test>::get();
 			let first_period = 1;
 			let last_period = 4;
 
@@ -1493,7 +1500,7 @@ mod tests {
 				9,
 				None
 			));
-			let bidder = Crowdloan::fund_account_id(para);
+			let bidder = Crowdloan::fund_account_id(index);
 
 			// Fund crowdloan
 			run_to_block(1);
@@ -1524,6 +1531,7 @@ mod tests {
 	fn withdraw_from_failed_works() {
 		new_test_ext().execute_with(|| {
 			let para = new_para();
+			let index = NextFundIndex::<Test>::get();
 
 			// Set up a crowdloan
 			assert_ok!(Crowdloan::create(Origin::signed(1), para, 1000, 1, 1, 9, None));
@@ -1531,7 +1539,7 @@ mod tests {
 			assert_ok!(Crowdloan::contribute(Origin::signed(3), para, 50, None));
 
 			run_to_block(10);
-			let account_id = Crowdloan::fund_account_id(para);
+			let account_id = Crowdloan::fund_account_id(index);
 			// para has no reserved funds, indicating it did not win the auction.
 			assert_eq!(Balances::reserved_balance(&account_id), 0);
 			// but there's still the funds in its balance.
@@ -1553,13 +1561,14 @@ mod tests {
 	fn withdraw_cannot_be_griefed() {
 		new_test_ext().execute_with(|| {
 			let para = new_para();
+			let index = NextFundIndex::<Test>::get();
 
 			// Set up a crowdloan
 			assert_ok!(Crowdloan::create(Origin::signed(1), para, 1000, 1, 1, 9, None));
 			assert_ok!(Crowdloan::contribute(Origin::signed(2), para, 100, None));
 
 			run_to_block(10);
-			let account_id = Crowdloan::fund_account_id(para);
+			let account_id = Crowdloan::fund_account_id(index);
 
 			// user sends the crowdloan funds trying to make an accounting error
 			assert_ok!(Balances::transfer(Origin::signed(1), account_id, 10));
@@ -1583,7 +1592,8 @@ mod tests {
 	fn refund_works() {
 		new_test_ext().execute_with(|| {
 			let para = new_para();
-			let account_id = Crowdloan::fund_account_id(para);
+			let index = NextFundIndex::<Test>::get();
+			let account_id = Crowdloan::fund_account_id(index);
 
 			// Set up a crowdloan ending on 9
 			assert_ok!(Crowdloan::create(Origin::signed(1), para, 1000, 1, 1, 9, None));
@@ -1617,7 +1627,8 @@ mod tests {
 	fn multiple_refund_works() {
 		new_test_ext().execute_with(|| {
 			let para = new_para();
-			let account_id = Crowdloan::fund_account_id(para);
+			let index = NextFundIndex::<Test>::get();
+			let account_id = Crowdloan::fund_account_id(index);
 
 			// Set up a crowdloan ending on 9
 			assert_ok!(Crowdloan::create(Origin::signed(1), para, 100000, 1, 1, 9, None));
@@ -1727,7 +1738,8 @@ mod tests {
 	fn withdraw_from_finished_works() {
 		new_test_ext().execute_with(|| {
 			let para = new_para();
-			let account_id = Crowdloan::fund_account_id(para);
+			let index = NextFundIndex::<Test>::get();
+			let account_id = Crowdloan::fund_account_id(index);
 
 			// Set up a crowdloan
 			assert_ok!(Crowdloan::create(Origin::signed(1), para, 1000, 1, 1, 9, None));
@@ -2079,7 +2091,7 @@ mod benchmarking {
 		verify {
 			let fund = Funds::<T>::get(fund_index).expect("fund was created...");
 			assert_eq!(
-				Crowdloan::<T>::contribution_get(fund.trie_index, &caller),
+				Crowdloan::<T>::contribution_get(fund.fund_index, &caller),
 				(T::MinContribution::get(), worst_memo),
 			);
 		}
diff --git a/polkadot/runtime/common/src/integration_tests.rs b/polkadot/runtime/common/src/integration_tests.rs
index 370073fa4de..7032c6badc8 100644
--- a/polkadot/runtime/common/src/integration_tests.rs
+++ b/polkadot/runtime/common/src/integration_tests.rs
@@ -20,7 +20,7 @@ use crate::{
 	auctions, crowdloan, paras_registrar,
 	slot_range::SlotRange,
 	slots,
-	traits::{AuctionStatus, Auctioneer, Registrar as RegistrarT},
+	traits::{AuctionStatus, Auctioneer, Leaser, Registrar as RegistrarT},
 };
 use frame_support::{
 	assert_noop, assert_ok, parameter_types,
@@ -29,6 +29,7 @@ use frame_support::{
 };
 use frame_support_test::TestRandomness;
 use frame_system::EnsureRoot;
+use parity_scale_codec::Encode;
 use primitives::v1::{
 	BlockNumber, HeadData, Header, Id as ParaId, ValidationCode, LOWEST_PUBLIC_ID,
 };
@@ -41,16 +42,29 @@ use sp_keystore::{testing::KeyStore, KeystoreExt};
 use sp_runtime::{
 	traits::{BlakeTwo256, IdentityLookup, One},
 	transaction_validity::TransactionPriority,
+	AccountId32,
 };
-use sp_std::sync::Arc;
+use sp_std::{convert::TryInto, sync::Arc};
 
 type UncheckedExtrinsic = frame_system::mocking::MockUncheckedExtrinsic<Test>;
 type Block = frame_system::mocking::MockBlock<Test>;
 
-type AccountId = u32;
+type AccountId = AccountId32;
 type Balance = u32;
 type Moment = u32;
 
+fn account_id(i: u32) -> AccountId32 {
+	let b4 = i.encode();
+	let b32 = [&b4[..], &b4[..], &b4[..], &b4[..], &b4[..], &b4[..], &b4[..], &b4[..]].concat();
+	let array: [u8; 32] = b32.try_into().unwrap();
+	array.into()
+}
+
+fn signed(i: u32) -> Origin {
+	let account_id = account_id(i);
+	Origin::signed(account_id)
+}
+
 frame_support::construct_runtime!(
 	pub enum Test where
 		Block = Block,
@@ -342,21 +356,21 @@ fn basic_end_to_end_works() {
 			let para_2 = LOWEST_PUBLIC_ID + 1;
 			assert!(System::block_number().is_one());
 			// User 1 and 2 will own parachains
-			Balances::make_free_balance_be(&1, 1_000_000_000);
-			Balances::make_free_balance_be(&2, 1_000_000_000);
+			Balances::make_free_balance_be(&account_id(1), 1_000_000_000);
+			Balances::make_free_balance_be(&account_id(2), 1_000_000_000);
 			// First register 2 parathreads
 			let genesis_head = Registrar::worst_head_data();
 			let validation_code = Registrar::worst_validation_code();
-			assert_ok!(Registrar::reserve(Origin::signed(1)));
+			assert_ok!(Registrar::reserve(signed(1)));
 			assert_ok!(Registrar::register(
-				Origin::signed(1),
+				signed(1),
 				ParaId::from(para_1),
 				genesis_head.clone(),
 				validation_code.clone(),
 			));
-			assert_ok!(Registrar::reserve(Origin::signed(2)));
+			assert_ok!(Registrar::reserve(signed(2)));
 			assert_ok!(Registrar::register(
-				Origin::signed(2),
+				signed(2),
 				ParaId::from(2001),
 				genesis_head,
 				validation_code,
@@ -379,7 +393,7 @@ fn basic_end_to_end_works() {
 			// Para 1 will bid directly for slot 1, 2
 			// Open a crowdloan for Para 2 for slot 3, 4
 			assert_ok!(Crowdloan::create(
-				Origin::signed(2),
+				signed(2),
 				ParaId::from(para_2),
 				1_000,                        // Cap
 				lease_period_index_start + 2, // First Slot
@@ -387,17 +401,18 @@ fn basic_end_to_end_works() {
 				200 + offset,                 // Block End
 				None,
 			));
-			let crowdloan_account = Crowdloan::fund_account_id(ParaId::from(para_2));
+			let fund_2 = Crowdloan::funds(ParaId::from(para_2)).unwrap();
+			let crowdloan_account = Crowdloan::fund_account_id(fund_2.fund_index);
 
 			// Auction ending begins on block 100 + offset, so we make a bid before then.
 			run_to_block(90 + offset);
 
-			Balances::make_free_balance_be(&10, 1_000_000_000);
-			Balances::make_free_balance_be(&20, 1_000_000_000);
+			Balances::make_free_balance_be(&account_id(10), 1_000_000_000);
+			Balances::make_free_balance_be(&account_id(20), 1_000_000_000);
 
 			// User 10 will bid directly for parachain 1
 			assert_ok!(Auctions::bid(
-				Origin::signed(10),
+				signed(10),
 				ParaId::from(para_1),
 				1,                            // Auction Index
 				lease_period_index_start + 0, // First Slot
@@ -406,8 +421,8 @@ fn basic_end_to_end_works() {
 			));
 
 			// User 2 will be a contribute to crowdloan for parachain 2
-			Balances::make_free_balance_be(&2, 1_000_000_000);
-			assert_ok!(Crowdloan::contribute(Origin::signed(2), ParaId::from(para_2), 920, None));
+			Balances::make_free_balance_be(&account_id(2), 1_000_000_000);
+			assert_ok!(Crowdloan::contribute(signed(2), ParaId::from(para_2), 920, None));
 
 			// Auction ends at block 110 + offset
 			run_to_block(109 + offset);
@@ -421,7 +436,7 @@ fn basic_end_to_end_works() {
 			assert_eq!(
 				slots::Leases::<Test>::get(ParaId::from(para_1)),
 				// -- 1 --- 2 --- 3 --------- 4 ------------ 5 --------
-				vec![None, None, None, Some((10, 910)), Some((10, 910))],
+				vec![None, None, None, Some((account_id(10), 910)), Some((account_id(10), 910))],
 			);
 			assert_eq!(
 				slots::Leases::<Test>::get(ParaId::from(para_2)),
@@ -432,15 +447,15 @@ fn basic_end_to_end_works() {
 					None,
 					None,
 					None,
-					Some((crowdloan_account, 920)),
-					Some((crowdloan_account, 920))
+					Some((crowdloan_account.clone(), 920)),
+					Some((crowdloan_account.clone(), 920))
 				],
 			);
 
 			// Should not be able to contribute to a winning crowdloan
-			Balances::make_free_balance_be(&3, 1_000_000_000);
+			Balances::make_free_balance_be(&account_id(3), 1_000_000_000);
 			assert_noop!(
-				Crowdloan::contribute(Origin::signed(3), ParaId::from(2001), 10, None),
+				Crowdloan::contribute(signed(3), ParaId::from(2001), 10, None),
 				CrowdloanError::<Test>::BidOrLeaseActive
 			);
 
@@ -508,21 +523,21 @@ fn basic_errors_fail() {
 		assert!(System::block_number().is_one());
 		let para_id = LOWEST_PUBLIC_ID;
 		// Can't double register
-		Balances::make_free_balance_be(&1, 1_000_000_000);
-		Balances::make_free_balance_be(&2, 1_000_000_000);
+		Balances::make_free_balance_be(&account_id(1), 1_000_000_000);
+		Balances::make_free_balance_be(&account_id(2), 1_000_000_000);
 
 		let genesis_head = Registrar::worst_head_data();
 		let validation_code = Registrar::worst_validation_code();
-		assert_ok!(Registrar::reserve(Origin::signed(1)));
+		assert_ok!(Registrar::reserve(signed(1)));
 		assert_ok!(Registrar::register(
-			Origin::signed(1),
+			signed(1),
 			para_id,
 			genesis_head.clone(),
 			validation_code.clone(),
 		));
-		assert_ok!(Registrar::reserve(Origin::signed(2)));
+		assert_ok!(Registrar::reserve(signed(2)));
 		assert_noop!(
-			Registrar::register(Origin::signed(2), para_id, genesis_head, validation_code,),
+			Registrar::register(signed(2), para_id, genesis_head, validation_code,),
 			paras_registrar::Error::<Test>::NotOwner
 		);
 
@@ -534,7 +549,7 @@ fn basic_errors_fail() {
 		// Cannot create a crowdloan if you do not own the para
 		assert_noop!(
 			Crowdloan::create(
-				Origin::signed(2),
+				signed(2),
 				para_id,
 				1_000,                        // Cap
 				lease_period_index_start + 2, // First Slot
@@ -557,12 +572,12 @@ fn competing_slots() {
 
 		// Create n paras and owners
 		for n in 1..=max_bids {
-			Balances::make_free_balance_be(&n, 1_000_000_000);
+			Balances::make_free_balance_be(&account_id(n), 1_000_000_000);
 			let genesis_head = Registrar::worst_head_data();
 			let validation_code = Registrar::worst_validation_code();
-			assert_ok!(Registrar::reserve(Origin::signed(n)));
+			assert_ok!(Registrar::reserve(signed(n)));
 			assert_ok!(Registrar::register(
-				Origin::signed(n),
+				signed(n),
 				para_id + n - 1,
 				genesis_head,
 				validation_code,
@@ -581,7 +596,7 @@ fn competing_slots() {
 			// Increment block number
 			run_to_block(System::block_number() + 10);
 
-			Balances::make_free_balance_be(&(n * 10), n * 1_000);
+			Balances::make_free_balance_be(&account_id(n * 10), n * 1_000);
 
 			let (start, end) = match n {
 				1 => (0, 0),
@@ -599,7 +614,7 @@ fn competing_slots() {
 
 			// Users will bid directly for parachain
 			assert_ok!(Auctions::bid(
-				Origin::signed(n * 10),
+				signed(n * 10),
 				para_id + n - 1,
 				1,                                // Auction Index
 				lease_period_index_start + start, // First Slot
@@ -617,18 +632,26 @@ fn competing_slots() {
 		assert_eq!(
 			slots::Leases::<Test>::get(para_id),
 			// -- 1 --- 2 --- 3 ---------- 4 ------
-			vec![None, None, None, Some((10, 900))],
+			vec![None, None, None, Some((account_id(10), 900))],
 		);
 		assert_eq!(
 			slots::Leases::<Test>::get(para_id + 4),
 			// -- 1 --- 2 --- 3 --- 4 ---------- 5 -------
-			vec![None, None, None, None, Some((50, 4500))],
+			vec![None, None, None, None, Some((account_id(50), 4500))],
 		);
 		// TODO: Is this right?
 		assert_eq!(
 			slots::Leases::<Test>::get(para_id + 8),
 			// -- 1 --- 2 --- 3 --- 4 --- 5 ---------- 6 --------------- 7 -------
-			vec![None, None, None, None, None, Some((90, 8100)), Some((90, 8100))],
+			vec![
+				None,
+				None,
+				None,
+				None,
+				None,
+				Some((account_id(90), 8100)),
+				Some((account_id(90), 8100))
+			],
 		);
 	});
 }
@@ -642,12 +665,12 @@ fn competing_bids() {
 		let start_para = LOWEST_PUBLIC_ID - 1;
 		// Create 3 paras and owners
 		for n in 1..=3 {
-			Balances::make_free_balance_be(&n, 1_000_000_000);
+			Balances::make_free_balance_be(&account_id(n), 1_000_000_000);
 			let genesis_head = Registrar::worst_head_data();
 			let validation_code = Registrar::worst_validation_code();
-			assert_ok!(Registrar::reserve(Origin::signed(n)));
+			assert_ok!(Registrar::reserve(signed(n)));
 			assert_ok!(Registrar::register(
-				Origin::signed(n),
+				signed(n),
 				ParaId::from(start_para + n),
 				genesis_head,
 				validation_code,
@@ -666,7 +689,7 @@ fn competing_bids() {
 		for n in 1..=3 {
 			// Create a crowdloan for each para
 			assert_ok!(Crowdloan::create(
-				Origin::signed(n),
+				signed(n),
 				ParaId::from(start_para + n),
 				100_000,                      // Cap
 				lease_period_index_start + 2, // First Slot
@@ -680,14 +703,14 @@ fn competing_bids() {
 			// Increment block number
 			run_to_block(starting_block + n * 10);
 
-			Balances::make_free_balance_be(&(n * 10), n * 1_000);
+			Balances::make_free_balance_be(&account_id(n * 10), n * 1_000);
 
 			let para = start_para + n % 3 + 1;
 
 			if n % 2 == 0 {
 				// User 10 will bid directly for parachain 1
 				assert_ok!(Auctions::bid(
-					Origin::signed(n * 10),
+					signed(n * 10),
 					ParaId::from(para),
 					1,                            // Auction Index
 					lease_period_index_start + 0, // First Slot
@@ -697,7 +720,7 @@ fn competing_bids() {
 			} else {
 				// User 20 will be a contribute to crowdloan for parachain 2
 				assert_ok!(Crowdloan::contribute(
-					Origin::signed(n * 10),
+					signed(n * 10),
 					ParaId::from(para),
 					n + 900,
 					None,
@@ -709,7 +732,8 @@ fn competing_bids() {
 		run_to_block(starting_block + 110);
 
 		// Appropriate Paras should have won slots
-		let crowdloan_2 = Crowdloan::fund_account_id(ParaId::from(2001));
+		let fund_1 = Crowdloan::funds(ParaId::from(2000)).unwrap();
+		let crowdloan_1 = Crowdloan::fund_account_id(fund_1.fund_index);
 		assert_eq!(
 			slots::Leases::<Test>::get(ParaId::from(2000)),
 			// -- 1 --- 2 --- 3 --- 4 --- 5 ------------- 6 ------------------------ 7 -------------
@@ -719,14 +743,14 @@ fn competing_bids() {
 				None,
 				None,
 				None,
-				Some((crowdloan_2, 1812)),
-				Some((crowdloan_2, 1812))
+				Some((crowdloan_1.clone(), 1812)),
+				Some((crowdloan_1.clone(), 1812))
 			],
 		);
 		assert_eq!(
 			slots::Leases::<Test>::get(ParaId::from(2002)),
 			// -- 1 --- 2 --- 3 ---------- 4 --------------- 5 -------
-			vec![None, None, None, Some((80, 7200)), Some((80, 7200))],
+			vec![None, None, None, Some((account_id(80), 7200)), Some((account_id(80), 7200))],
 		);
 	});
 }
@@ -735,21 +759,21 @@ fn competing_bids() {
 fn basic_swap_works() {
 	// This test will test a swap between a parachain and parathread works successfully.
 	new_test_ext().execute_with(|| {
-		assert!(System::block_number().is_one()); // So events are emitted
-										  // User 1 and 2 will own paras
-		Balances::make_free_balance_be(&1, 1_000_000_000);
-		Balances::make_free_balance_be(&2, 1_000_000_000);
+		assert!(System::block_number().is_one()); /* So events are emitted */
+		// User 1 and 2 will own paras
+		Balances::make_free_balance_be(&account_id(1), 1_000_000_000);
+		Balances::make_free_balance_be(&account_id(2), 1_000_000_000);
 		// First register 2 parathreads with different data
-		assert_ok!(Registrar::reserve(Origin::signed(1)));
+		assert_ok!(Registrar::reserve(signed(1)));
 		assert_ok!(Registrar::register(
-			Origin::signed(1),
+			signed(1),
 			ParaId::from(2000),
 			test_genesis_head(10),
 			test_validation_code(10),
 		));
-		assert_ok!(Registrar::reserve(Origin::signed(2)));
+		assert_ok!(Registrar::reserve(signed(2)));
 		assert_ok!(Registrar::register(
-			Origin::signed(2),
+			signed(2),
 			ParaId::from(2001),
 			test_genesis_head(20),
 			test_validation_code(20),
@@ -771,7 +795,7 @@ fn basic_swap_works() {
 
 		// Open a crowdloan for Para 1 for slots 0-3
 		assert_ok!(Crowdloan::create(
-			Origin::signed(1),
+			signed(1),
 			ParaId::from(2000),
 			1_000_000,                    // Cap
 			lease_period_index_start + 0, // First Slot
@@ -779,13 +803,14 @@ fn basic_swap_works() {
 			200,                          // Block End
 			None,
 		));
-		let crowdloan_account = Crowdloan::fund_account_id(ParaId::from(2000));
+		let fund = Crowdloan::funds(ParaId::from(2000)).unwrap();
+		let crowdloan_account = Crowdloan::fund_account_id(fund.fund_index);
 
 		// Bunch of contributions
 		let mut total = 0;
 		for i in 10..20 {
-			Balances::make_free_balance_be(&i, 1_000_000_000);
-			assert_ok!(Crowdloan::contribute(Origin::signed(i), ParaId::from(2000), 900 - i, None));
+			Balances::make_free_balance_be(&account_id(i), 1_000_000_000);
+			assert_ok!(Crowdloan::contribute(signed(i), ParaId::from(2000), 900 - i, None));
 			total += 900 - i;
 		}
 		assert!(total > 0);
@@ -796,8 +821,8 @@ fn basic_swap_works() {
 
 		// Deposit is appropriately taken
 		// ----------------------------------------- para deposit --- crowdloan
-		assert_eq!(Balances::reserved_balance(&1), (500 + 10 * 2 * 1) + 100);
-		assert_eq!(Balances::reserved_balance(&2), 500 + 20 * 2 * 1);
+		assert_eq!(Balances::reserved_balance(&account_id(1)), (500 + 10 * 2 * 1) + 100);
+		assert_eq!(Balances::reserved_balance(&account_id(2)), 500 + 20 * 2 * 1);
 		assert_eq!(Balances::reserved_balance(&crowdloan_account), total);
 		// Crowdloan is appropriately set
 		assert!(Crowdloan::funds(ParaId::from(2000)).is_some());
@@ -839,8 +864,8 @@ fn basic_swap_works() {
 		// Deregister parathread
 		assert_ok!(Registrar::deregister(para_origin(2000).into(), ParaId::from(2000)));
 		// Correct deposit is unreserved
-		assert_eq!(Balances::reserved_balance(&1), 100); // crowdloan deposit left over
-		assert_eq!(Balances::reserved_balance(&2), 500 + 20 * 2 * 1);
+		assert_eq!(Balances::reserved_balance(&account_id(1)), 100); // crowdloan deposit left over
+		assert_eq!(Balances::reserved_balance(&account_id(2)), 500 + 20 * 2 * 1);
 		// Crowdloan ownership is swapped
 		assert!(Crowdloan::funds(ParaId::from(2000)).is_none());
 		assert!(Crowdloan::funds(ParaId::from(2001)).is_some());
@@ -850,11 +875,11 @@ fn basic_swap_works() {
 
 		// Cant dissolve
 		assert_noop!(
-			Crowdloan::dissolve(Origin::signed(1), ParaId::from(2000)),
+			Crowdloan::dissolve(signed(1), ParaId::from(2000)),
 			CrowdloanError::<Test>::InvalidParaId
 		);
 		assert_noop!(
-			Crowdloan::dissolve(Origin::signed(2), ParaId::from(2001)),
+			Crowdloan::dissolve(signed(2), ParaId::from(2001)),
 			CrowdloanError::<Test>::NotReadyToDissolve
 		);
 
@@ -864,39 +889,198 @@ fn basic_swap_works() {
 		// Withdraw of contributions works
 		assert_eq!(Balances::free_balance(&crowdloan_account), total);
 		for i in 10..20 {
-			assert_ok!(Crowdloan::withdraw(Origin::signed(i), i, ParaId::from(2001)));
+			assert_ok!(Crowdloan::withdraw(signed(i), account_id(i), ParaId::from(2001)));
 		}
 		assert_eq!(Balances::free_balance(&crowdloan_account), 0);
 
 		// Dissolve returns the balance of the person who put a deposit for crowdloan
-		assert_ok!(Crowdloan::dissolve(Origin::signed(1), ParaId::from(2001)));
-		assert_eq!(Balances::reserved_balance(&1), 0);
-		assert_eq!(Balances::reserved_balance(&2), 500 + 20 * 2 * 1);
+		assert_ok!(Crowdloan::dissolve(signed(1), ParaId::from(2001)));
+		assert_eq!(Balances::reserved_balance(&account_id(1)), 0);
+		assert_eq!(Balances::reserved_balance(&account_id(2)), 500 + 20 * 2 * 1);
 
 		// Final deregister sets everything back to the start
 		assert_ok!(Registrar::deregister(para_origin(2001).into(), ParaId::from(2001)));
-		assert_eq!(Balances::reserved_balance(&2), 0);
+		assert_eq!(Balances::reserved_balance(&account_id(2)), 0);
+	})
+}
+
+#[test]
+fn parachain_swap_works() {
+	// This test will test a swap between two parachains works successfully.
+	new_test_ext().execute_with(|| {
+		assert!(System::block_number().is_one()); /* So events are emitted */
+		// User 1 and 2 will own paras
+		Balances::make_free_balance_be(&account_id(1), 1_000_000_000);
+		Balances::make_free_balance_be(&account_id(2), 1_000_000_000);
+		// First register 2 parathreads with different data
+		assert_ok!(Registrar::reserve(signed(1)));
+		assert_ok!(Registrar::register(
+			signed(1),
+			ParaId::from(2000),
+			test_genesis_head(10),
+			test_validation_code(10),
+		));
+		assert_ok!(Registrar::reserve(signed(2)));
+		assert_ok!(Registrar::register(
+			signed(2),
+			ParaId::from(2001),
+			test_genesis_head(20),
+			test_validation_code(20),
+		));
+
+		// Paras should be onboarding
+		assert_eq!(Paras::lifecycle(ParaId::from(2000)), Some(ParaLifecycle::Onboarding));
+		assert_eq!(Paras::lifecycle(ParaId::from(2001)), Some(ParaLifecycle::Onboarding));
+
+		assert_eq!(
+			Balances::total_balance(&Crowdloan::fund_account_id(Crowdloan::next_fund_index())),
+			0
+		);
+
+		// Start a new auction in the future
+		let start_auction = |lease_period_index_start, winner, end| {
+			let unique_id = winner - 1999u32;
+			let starting_block = System::block_number();
+			let duration = 99u32;
+			assert_ok!(Auctions::new_auction(Origin::root(), duration, lease_period_index_start));
+
+			// 2 sessions later they are parathreads
+			run_to_block(starting_block + 20);
+			assert_eq!(Paras::lifecycle(ParaId::from(winner)), Some(ParaLifecycle::Parathread));
+
+			// Open a crowdloan for Para 1 for slots 0-3
+			assert_ok!(Crowdloan::create(
+				signed(unique_id),
+				ParaId::from(winner),
+				1_000_000,                    // Cap
+				lease_period_index_start + 0, // First Slot
+				lease_period_index_start + 7, // Last Slot
+				end,                          // Block End
+				None,
+			));
+			let winner_fund = Crowdloan::funds(ParaId::from(winner)).unwrap();
+			let crowdloan_account = Crowdloan::fund_account_id(winner_fund.fund_index);
+
+			// Bunch of contributions
+			let mut total = 0;
+			for i in (unique_id * 10)..(unique_id + 1) * 10 {
+				Balances::make_free_balance_be(&account_id(i), 1_000_000_000);
+				assert_ok!(Crowdloan::contribute(signed(i), ParaId::from(winner), 900 - i, None));
+				total += 900 - i;
+			}
+			assert!(total > 0);
+			assert_eq!(Balances::free_balance(&crowdloan_account), total);
+
+			// Go to end of auction where everyone won their slots
+			run_to_block(end);
+
+			// Crowdloan is appropriately set
+			assert!(Crowdloan::funds(ParaId::from(winner)).is_some());
+
+			// New leases will start on block lease period index * 100
+			let lease_start_block = lease_period_index_start * 100;
+			run_to_block(lease_start_block);
+		};
+
+		start_auction(4u32, 2000, 200);
+		// Slots are won by Para 1
+		assert!(!Slots::lease(ParaId::from(2000)).is_empty());
+		assert!(Slots::lease(ParaId::from(2001)).is_empty());
+
+		// 2 sessions later it is a parachain
+		run_to_block(4 * 100 + 20);
+		assert_eq!(Paras::lifecycle(ParaId::from(2000)), Some(ParaLifecycle::Parachain));
+		assert_eq!(Paras::lifecycle(ParaId::from(2001)), Some(ParaLifecycle::Parathread));
+
+		// Let's repeat the process now for another parachain.
+		start_auction(6u32, 2001, 500);
+		// Slots are won by Para 1
+		assert!(!Slots::lease(ParaId::from(2000)).is_empty());
+		assert!(!Slots::lease(ParaId::from(2001)).is_empty());
+
+		// 2 sessions later it is a parachain
+		run_to_block(6 * 100 + 20);
+		assert_eq!(Paras::lifecycle(ParaId::from(2000)), Some(ParaLifecycle::Parachain));
+		assert_eq!(Paras::lifecycle(ParaId::from(2001)), Some(ParaLifecycle::Parachain));
+
+		// Currently we are on lease 6
+		assert_eq!(
+			<Slots as Leaser<_>>::lease_period_index(System::block_number()),
+			Some((6u32, false))
+		);
+
+		// This means that parachain 1 should only have 6 slots left, and parachain 2 has all 8.
+		assert_eq!(slots::Leases::<Test>::get(ParaId::from(2000)).len(), 6);
+		assert_eq!(slots::Leases::<Test>::get(ParaId::from(2001)).len(), 8);
+
+		let fund_2000 = Crowdloan::funds(ParaId::from(2000)).unwrap();
+		assert_eq!(fund_2000.fund_index, 0);
+		assert_eq!(
+			Balances::reserved_balance(&Crowdloan::fund_account_id(fund_2000.fund_index)),
+			fund_2000.raised
+		);
+
+		let fund_2001 = Crowdloan::funds(ParaId::from(2001)).unwrap();
+		assert_eq!(fund_2001.fund_index, 1);
+		assert_eq!(
+			Balances::reserved_balance(&Crowdloan::fund_account_id(fund_2001.fund_index)),
+			fund_2001.raised
+		);
+
+		assert_eq!(Slots::lease(ParaId::from(2000)).len(), 6);
+		assert_eq!(Slots::lease(ParaId::from(2001)).len(), 8);
+
+		// Now we swap them.
+		assert_ok!(Registrar::swap(
+			para_origin(2000).into(),
+			ParaId::from(2000),
+			ParaId::from(2001)
+		));
+		assert_ok!(Registrar::swap(
+			para_origin(2001).into(),
+			ParaId::from(2001),
+			ParaId::from(2000)
+		));
+
+		// Crowdloan Swapped
+		let fund_2000 = Crowdloan::funds(ParaId::from(2000)).unwrap();
+		assert_eq!(fund_2000.fund_index, 1);
+		assert_eq!(
+			Balances::reserved_balance(&Crowdloan::fund_account_id(fund_2000.fund_index)),
+			fund_2000.raised
+		);
+
+		let fund_2001 = Crowdloan::funds(ParaId::from(2001)).unwrap();
+		assert_eq!(fund_2001.fund_index, 0);
+		assert_eq!(
+			Balances::reserved_balance(&Crowdloan::fund_account_id(fund_2001.fund_index)),
+			fund_2001.raised
+		);
+
+		// Slots Swapped
+		assert_eq!(Slots::lease(ParaId::from(2000)).len(), 8);
+		assert_eq!(Slots::lease(ParaId::from(2001)).len(), 6);
 	})
 }
 
 #[test]
 fn crowdloan_ending_period_bid() {
 	new_test_ext().execute_with(|| {
-		assert!(System::block_number().is_one()); // So events are emitted
-										  // User 1 and 2 will own paras
-		Balances::make_free_balance_be(&1, 1_000_000_000);
-		Balances::make_free_balance_be(&2, 1_000_000_000);
+		assert!(System::block_number().is_one()); /* So events are emitted */
+		// User 1 and 2 will own paras
+		Balances::make_free_balance_be(&account_id(1), 1_000_000_000);
+		Balances::make_free_balance_be(&account_id(2), 1_000_000_000);
 		// First register 2 parathreads
-		assert_ok!(Registrar::reserve(Origin::signed(1)));
+		assert_ok!(Registrar::reserve(signed(1)));
 		assert_ok!(Registrar::register(
-			Origin::signed(1),
+			signed(1),
 			ParaId::from(2000),
 			test_genesis_head(10),
 			test_validation_code(10),
 		));
-		assert_ok!(Registrar::reserve(Origin::signed(2)));
+		assert_ok!(Registrar::reserve(signed(2)));
 		assert_ok!(Registrar::register(
-			Origin::signed(2),
+			signed(2),
 			ParaId::from(2001),
 			test_genesis_head(20),
 			test_validation_code(20),
@@ -918,7 +1102,7 @@ fn crowdloan_ending_period_bid() {
 
 		// Open a crowdloan for Para 1 for slots 0-3
 		assert_ok!(Crowdloan::create(
-			Origin::signed(1),
+			signed(1),
 			ParaId::from(2000),
 			1_000_000,                    // Cap
 			lease_period_index_start + 0, // First Slot
@@ -926,22 +1110,23 @@ fn crowdloan_ending_period_bid() {
 			200,                          // Block End
 			None,
 		));
-		let crowdloan_account = Crowdloan::fund_account_id(ParaId::from(2000));
+		let fund = Crowdloan::funds(ParaId::from(2000)).unwrap();
+		let crowdloan_account = Crowdloan::fund_account_id(fund.fund_index);
 
 		// Bunch of contributions
 		let mut total = 0;
 		for i in 10..20 {
-			Balances::make_free_balance_be(&i, 1_000_000_000);
-			assert_ok!(Crowdloan::contribute(Origin::signed(i), ParaId::from(2000), 900 - i, None));
+			Balances::make_free_balance_be(&account_id(i), 1_000_000_000);
+			assert_ok!(Crowdloan::contribute(signed(i), ParaId::from(2000), 900 - i, None));
 			total += 900 - i;
 		}
 		assert!(total > 0);
 		assert_eq!(Balances::free_balance(&crowdloan_account), total);
 
 		// Bid for para 2 directly
-		Balances::make_free_balance_be(&2, 1_000_000_000);
+		Balances::make_free_balance_be(&account_id(2), 1_000_000_000);
 		assert_ok!(Auctions::bid(
-			Origin::signed(2),
+			signed(2),
 			ParaId::from(2001),
 			1,                            // Auction Index
 			lease_period_index_start + 0, // First Slot
@@ -953,24 +1138,25 @@ fn crowdloan_ending_period_bid() {
 		run_to_block(100);
 
 		assert_eq!(Auctions::auction_status(100), AuctionStatus::<u32>::EndingPeriod(0, 0));
-		let mut winning = [None; SlotRange::SLOT_RANGE_COUNT];
-		winning[SlotRange::ZeroOne as u8 as usize] = Some((2, ParaId::from(2001), 900));
+		let mut winning = [(); SlotRange::SLOT_RANGE_COUNT].map(|_| None);
+
+		winning[SlotRange::ZeroOne as u8 as usize] = Some((account_id(2), ParaId::from(2001), 900));
 		winning[SlotRange::ZeroThree as u8 as usize] =
-			Some((crowdloan_account, ParaId::from(2000), total));
+			Some((crowdloan_account.clone(), ParaId::from(2000), total));
 
 		assert_eq!(Auctions::winning(0), Some(winning));
 
 		run_to_block(101);
 
-		Balances::make_free_balance_be(&1234, 1_000_000_000);
-		assert_ok!(Crowdloan::contribute(Origin::signed(1234), ParaId::from(2000), 900, None));
+		Balances::make_free_balance_be(&account_id(1234), 1_000_000_000);
+		assert_ok!(Crowdloan::contribute(signed(1234), ParaId::from(2000), 900, None));
 
 		// Data propagates correctly
 		run_to_block(102);
-		let mut winning = [None; SlotRange::SLOT_RANGE_COUNT];
-		winning[SlotRange::ZeroOne as u8 as usize] = Some((2, ParaId::from(2001), 900));
+		let mut winning = [(); SlotRange::SLOT_RANGE_COUNT].map(|_| None);
+		winning[SlotRange::ZeroOne as u8 as usize] = Some((account_id(2), ParaId::from(2001), 900));
 		winning[SlotRange::ZeroThree as u8 as usize] =
-			Some((crowdloan_account, ParaId::from(2000), total + 900));
+			Some((crowdloan_account.clone(), ParaId::from(2000), total + 900));
 		assert_eq!(Auctions::winning(2), Some(winning));
 	})
 }
@@ -978,7 +1164,7 @@ fn crowdloan_ending_period_bid() {
 #[test]
 fn auction_bid_requires_registered_para() {
 	new_test_ext().execute_with(|| {
-		assert!(System::block_number().is_one()); // So events are emitted
+		assert!(System::block_number().is_one()); /* So events are emitted */
 
 		// Start a new auction in the future
 		let duration = 99u32;
@@ -986,10 +1172,10 @@ fn auction_bid_requires_registered_para() {
 		assert_ok!(Auctions::new_auction(Origin::root(), duration, lease_period_index_start));
 
 		// Can't bid with non-registered paras
-		Balances::make_free_balance_be(&1, 1_000_000_000);
+		Balances::make_free_balance_be(&account_id(1), 1_000_000_000);
 		assert_noop!(
 			Auctions::bid(
-				Origin::signed(1),
+				signed(1),
 				ParaId::from(2000),
 				1,                            // Auction Index
 				lease_period_index_start + 0, // First Slot
@@ -1000,9 +1186,9 @@ fn auction_bid_requires_registered_para() {
 		);
 
 		// Now we register the para
-		assert_ok!(Registrar::reserve(Origin::signed(1)));
+		assert_ok!(Registrar::reserve(signed(1)));
 		assert_ok!(Registrar::register(
-			Origin::signed(1),
+			signed(1),
 			ParaId::from(2000),
 			test_genesis_head(10),
 			test_validation_code(10),
@@ -1011,7 +1197,7 @@ fn auction_bid_requires_registered_para() {
 		// Still can't bid until it is fully onboarded
 		assert_noop!(
 			Auctions::bid(
-				Origin::signed(1),
+				signed(1),
 				ParaId::from(2000),
 				1,                            // Auction Index
 				lease_period_index_start + 0, // First Slot
@@ -1025,9 +1211,9 @@ fn auction_bid_requires_registered_para() {
 		run_to_session(2);
 
 		// Success
-		Balances::make_free_balance_be(&1, 1_000_000_000);
+		Balances::make_free_balance_be(&account_id(1), 1_000_000_000);
 		assert_ok!(Auctions::bid(
-			Origin::signed(1),
+			signed(1),
 			ParaId::from(2000),
 			1,                            // Auction Index
 			lease_period_index_start + 0, // First Slot
@@ -1040,26 +1226,26 @@ fn auction_bid_requires_registered_para() {
 #[test]
 fn gap_bids_work() {
 	new_test_ext().execute_with(|| {
-		assert!(System::block_number().is_one()); // So events are emitted
+		assert!(System::block_number().is_one()); /* So events are emitted */
 
 		// Start a new auction in the future
 		let duration = 99u32;
 		let lease_period_index_start = 4u32;
 		assert_ok!(Auctions::new_auction(Origin::root(), duration, lease_period_index_start));
-		Balances::make_free_balance_be(&1, 1_000_000_000);
-		Balances::make_free_balance_be(&2, 1_000_000_000);
+		Balances::make_free_balance_be(&account_id(1), 1_000_000_000);
+		Balances::make_free_balance_be(&account_id(2), 1_000_000_000);
 
 		// Now register 2 paras
-		assert_ok!(Registrar::reserve(Origin::signed(1)));
+		assert_ok!(Registrar::reserve(signed(1)));
 		assert_ok!(Registrar::register(
-			Origin::signed(1),
+			signed(1),
 			ParaId::from(2000),
 			test_genesis_head(10),
 			test_validation_code(10),
 		));
-		assert_ok!(Registrar::reserve(Origin::signed(2)));
+		assert_ok!(Registrar::reserve(signed(2)));
 		assert_ok!(Registrar::register(
-			Origin::signed(2),
+			signed(2),
 			ParaId::from(2001),
 			test_genesis_head(10),
 			test_validation_code(10),
@@ -1069,11 +1255,11 @@ fn gap_bids_work() {
 		run_to_session(2);
 
 		// Make bids
-		Balances::make_free_balance_be(&10, 1_000_000_000);
-		Balances::make_free_balance_be(&20, 1_000_000_000);
+		Balances::make_free_balance_be(&account_id(10), 1_000_000_000);
+		Balances::make_free_balance_be(&account_id(20), 1_000_000_000);
 		// Slot 1 for 100 from 10
 		assert_ok!(Auctions::bid(
-			Origin::signed(10),
+			signed(10),
 			ParaId::from(2000),
 			1,                            // Auction Index
 			lease_period_index_start + 0, // First Slot
@@ -1082,7 +1268,7 @@ fn gap_bids_work() {
 		));
 		// Slot 4 for 400 from 10
 		assert_ok!(Auctions::bid(
-			Origin::signed(10),
+			signed(10),
 			ParaId::from(2000),
 			1,                            // Auction Index
 			lease_period_index_start + 3, // First Slot
@@ -1092,18 +1278,18 @@ fn gap_bids_work() {
 
 		// A bid for another para is counted separately.
 		assert_ok!(Auctions::bid(
-			Origin::signed(10),
+			signed(10),
 			ParaId::from(2001),
 			1,                            // Auction Index
 			lease_period_index_start + 1, // First Slot
 			lease_period_index_start + 1, // Last slot
 			555,                          // Amount
 		));
-		assert_eq!(Balances::reserved_balance(&10), 400 + 555);
+		assert_eq!(Balances::reserved_balance(&account_id(10)), 400 + 555);
 
 		// Slot 2 for 800 from 20, overtaking 10's bid
 		assert_ok!(Auctions::bid(
-			Origin::signed(20),
+			signed(20),
 			ParaId::from(2000),
 			1,                            // Auction Index
 			lease_period_index_start + 1, // First Slot
@@ -1112,7 +1298,7 @@ fn gap_bids_work() {
 		));
 		// Slot 3 for 200 from 20
 		assert_ok!(Auctions::bid(
-			Origin::signed(20),
+			signed(20),
 			ParaId::from(2000),
 			1,                            // Auction Index
 			lease_period_index_start + 2, // First Slot
@@ -1131,16 +1317,16 @@ fn gap_bids_work() {
 				None,
 				None,
 				None,
-				Some((10, 100)),
-				Some((20, 800)),
-				Some((20, 200)),
-				Some((10, 400))
+				Some((account_id(10), 100)),
+				Some((account_id(20), 800)),
+				Some((account_id(20), 200)),
+				Some((account_id(10), 400))
 			],
 		);
 		// Appropriate amount is reserved (largest of the values)
-		assert_eq!(Balances::reserved_balance(&10), 400);
+		assert_eq!(Balances::reserved_balance(&account_id(10)), 400);
 		// Appropriate amount is reserved (largest of the values)
-		assert_eq!(Balances::reserved_balance(&20), 800);
+		assert_eq!(Balances::reserved_balance(&account_id(20)), 800);
 
 		// Progress through the leases and note the correct amount of balance is reserved.
 
@@ -1148,48 +1334,57 @@ fn gap_bids_work() {
 		assert_eq!(
 			slots::Leases::<Test>::get(ParaId::from(2000)),
 			// --------- 4 -------------- 5 -------------- 6 -------------- 7 -------
-			vec![Some((10, 100)), Some((20, 800)), Some((20, 200)), Some((10, 400))],
+			vec![
+				Some((account_id(10), 100)),
+				Some((account_id(20), 800)),
+				Some((account_id(20), 200)),
+				Some((account_id(10), 400))
+			],
 		);
 		// Nothing changed.
-		assert_eq!(Balances::reserved_balance(&10), 400);
-		assert_eq!(Balances::reserved_balance(&20), 800);
+		assert_eq!(Balances::reserved_balance(&account_id(10)), 400);
+		assert_eq!(Balances::reserved_balance(&account_id(20)), 800);
 
 		// Lease period 4 is done, but nothing is unreserved since user 1 has a debt on lease 7
 		run_to_block(500);
 		assert_eq!(
 			slots::Leases::<Test>::get(ParaId::from(2000)),
 			// --------- 5 -------------- 6 -------------- 7 -------
-			vec![Some((20, 800)), Some((20, 200)), Some((10, 400))],
+			vec![
+				Some((account_id(20), 800)),
+				Some((account_id(20), 200)),
+				Some((account_id(10), 400))
+			],
 		);
 		// Nothing changed.
-		assert_eq!(Balances::reserved_balance(&10), 400);
-		assert_eq!(Balances::reserved_balance(&20), 800);
+		assert_eq!(Balances::reserved_balance(&account_id(10)), 400);
+		assert_eq!(Balances::reserved_balance(&account_id(20)), 800);
 
 		// Lease period 5 is done, and 20 will unreserve down to 200.
 		run_to_block(600);
 		assert_eq!(
 			slots::Leases::<Test>::get(ParaId::from(2000)),
 			// --------- 6 -------------- 7 -------
-			vec![Some((20, 200)), Some((10, 400))],
+			vec![Some((account_id(20), 200)), Some((account_id(10), 400))],
 		);
-		assert_eq!(Balances::reserved_balance(&10), 400);
-		assert_eq!(Balances::reserved_balance(&20), 200);
+		assert_eq!(Balances::reserved_balance(&account_id(10)), 400);
+		assert_eq!(Balances::reserved_balance(&account_id(20)), 200);
 
 		// Lease period 6 is done, and 20 will unreserve everything.
 		run_to_block(700);
 		assert_eq!(
 			slots::Leases::<Test>::get(ParaId::from(2000)),
 			// --------- 7 -------
-			vec![Some((10, 400))],
+			vec![Some((account_id(10), 400))],
 		);
-		assert_eq!(Balances::reserved_balance(&10), 400);
-		assert_eq!(Balances::reserved_balance(&20), 0);
+		assert_eq!(Balances::reserved_balance(&account_id(10)), 400);
+		assert_eq!(Balances::reserved_balance(&account_id(20)), 0);
 
 		// All leases are done. Everything is unreserved.
 		run_to_block(800);
 		assert_eq!(slots::Leases::<Test>::get(ParaId::from(2000)), vec![]);
-		assert_eq!(Balances::reserved_balance(&10), 0);
-		assert_eq!(Balances::reserved_balance(&20), 0);
+		assert_eq!(Balances::reserved_balance(&account_id(10)), 0);
+		assert_eq!(Balances::reserved_balance(&account_id(20)), 0);
 	});
 }
 
@@ -1198,12 +1393,12 @@ fn gap_bids_work() {
 #[test]
 fn cant_bid_on_existing_lease_periods() {
 	new_test_ext().execute_with(|| {
-		assert!(System::block_number().is_one()); // So events are emitted
-		Balances::make_free_balance_be(&1, 1_000_000_000);
+		assert!(System::block_number().is_one()); /* So events are emitted */
+		Balances::make_free_balance_be(&account_id(1), 1_000_000_000);
 		// First register a parathread
-		assert_ok!(Registrar::reserve(Origin::signed(1)));
+		assert_ok!(Registrar::reserve(signed(1)));
 		assert_ok!(Registrar::register(
-			Origin::signed(1),
+			signed(1),
 			ParaId::from(2000),
 			test_genesis_head(10),
 			test_validation_code(10),
@@ -1220,7 +1415,7 @@ fn cant_bid_on_existing_lease_periods() {
 
 		// Open a crowdloan for Para 1 for slots 0-3
 		assert_ok!(Crowdloan::create(
-			Origin::signed(1),
+			signed(1),
 			ParaId::from(2000),
 			1_000_000,                    // Cap
 			lease_period_index_start + 0, // First Slot
@@ -1228,13 +1423,14 @@ fn cant_bid_on_existing_lease_periods() {
 			400,                          // Long block end
 			None,
 		));
-		let crowdloan_account = Crowdloan::fund_account_id(ParaId::from(2000));
+		let fund = Crowdloan::funds(ParaId::from(2000)).unwrap();
+		let crowdloan_account = Crowdloan::fund_account_id(fund.fund_index);
 
 		// Bunch of contributions
 		let mut total = 0;
 		for i in 10..20 {
-			Balances::make_free_balance_be(&i, 1_000_000_000);
-			assert_ok!(Crowdloan::contribute(Origin::signed(i), ParaId::from(2000), 900 - i, None));
+			Balances::make_free_balance_be(&account_id(i), 1_000_000_000);
+			assert_ok!(Crowdloan::contribute(signed(i), ParaId::from(2000), 900 - i, None));
 			total += 900 - i;
 		}
 		assert!(total > 0);
@@ -1251,8 +1447,8 @@ fn cant_bid_on_existing_lease_periods() {
 				None,
 				None,
 				None,
-				Some((crowdloan_account, 8855)),
-				Some((crowdloan_account, 8855))
+				Some((crowdloan_account.clone(), 8855)),
+				Some((crowdloan_account.clone(), 8855))
 			],
 		);
 
@@ -1263,7 +1459,7 @@ fn cant_bid_on_existing_lease_periods() {
 		assert_ok!(Auctions::new_auction(Origin::root(), duration, lease_period_index_start));
 
 		// Poke the crowdloan into `NewRaise`
-		assert_ok!(Crowdloan::poke(Origin::signed(1), ParaId::from(2000)));
+		assert_ok!(Crowdloan::poke(signed(1), ParaId::from(2000)));
 		assert_eq!(Crowdloan::new_raise(), vec![ParaId::from(2000)]);
 
 		// Beginning of ending block.
@@ -1272,7 +1468,7 @@ fn cant_bid_on_existing_lease_periods() {
 		// Bids cannot be made which intersect
 		assert_noop!(
 			Auctions::bid(
-				Origin::signed(crowdloan_account),
+				Origin::signed(crowdloan_account.clone()),
 				ParaId::from(2000),
 				2,
 				lease_period_index_start + 0,
@@ -1284,7 +1480,7 @@ fn cant_bid_on_existing_lease_periods() {
 
 		assert_noop!(
 			Auctions::bid(
-				Origin::signed(crowdloan_account),
+				Origin::signed(crowdloan_account.clone()),
 				ParaId::from(2000),
 				2,
 				lease_period_index_start + 1,
@@ -1296,7 +1492,7 @@ fn cant_bid_on_existing_lease_periods() {
 
 		assert_noop!(
 			Auctions::bid(
-				Origin::signed(crowdloan_account),
+				Origin::signed(crowdloan_account.clone()),
 				ParaId::from(2000),
 				2,
 				lease_period_index_start - 1,
@@ -1308,7 +1504,7 @@ fn cant_bid_on_existing_lease_periods() {
 
 		assert_noop!(
 			Auctions::bid(
-				Origin::signed(crowdloan_account),
+				Origin::signed(crowdloan_account.clone()),
 				ParaId::from(2000),
 				2,
 				lease_period_index_start + 0,
@@ -1320,7 +1516,7 @@ fn cant_bid_on_existing_lease_periods() {
 
 		assert_noop!(
 			Auctions::bid(
-				Origin::signed(crowdloan_account),
+				Origin::signed(crowdloan_account.clone()),
 				ParaId::from(2000),
 				2,
 				lease_period_index_start + 1,
@@ -1332,7 +1528,7 @@ fn cant_bid_on_existing_lease_periods() {
 
 		assert_noop!(
 			Auctions::bid(
-				Origin::signed(crowdloan_account),
+				Origin::signed(crowdloan_account.clone()),
 				ParaId::from(2000),
 				2,
 				lease_period_index_start - 1,
@@ -1344,7 +1540,7 @@ fn cant_bid_on_existing_lease_periods() {
 
 		// Will work when not overlapping
 		assert_ok!(Auctions::bid(
-			Origin::signed(crowdloan_account),
+			Origin::signed(crowdloan_account.clone()),
 			ParaId::from(2000),
 			2,
 			lease_period_index_start + 2,
diff --git a/polkadot/runtime/common/src/paras_registrar.rs b/polkadot/runtime/common/src/paras_registrar.rs
index 3946e7d9aec..88828283cee 100644
--- a/polkadot/runtime/common/src/paras_registrar.rs
+++ b/polkadot/runtime/common/src/paras_registrar.rs
@@ -160,6 +160,9 @@ pub mod pallet {
 		NotReserved,
 		/// Registering parachain with empty code is not allowed.
 		EmptyCode,
+		/// Cannot perform a parachain slot / lifecycle swap. Check that the state of both paras are
+		/// correct for the swap to work.
+		CannotSwap,
 	}
 
 	/// Pending swap operations.
@@ -271,31 +274,40 @@ pub mod pallet {
 		pub fn swap(origin: OriginFor<T>, id: ParaId, other: ParaId) -> DispatchResult {
 			Self::ensure_root_para_or_owner(origin, id)?;
 
+			// If `id` and `other` is the same id, we treat this as a "clear" function, and exit
+			// early, since swapping the same id would otherwise be a noop.
+			if id == other {
+				PendingSwap::<T>::remove(id);
+				return Ok(())
+			}
+
+			// Sanity check that `id` is even a para.
+			let id_lifecycle =
+				paras::Pallet::<T>::lifecycle(id).ok_or(Error::<T>::NotRegistered)?;
+
 			if PendingSwap::<T>::get(other) == Some(id) {
-				if let Some(other_lifecycle) = paras::Pallet::<T>::lifecycle(other) {
-					if let Some(id_lifecycle) = paras::Pallet::<T>::lifecycle(id) {
-						// identify which is a parachain and which is a parathread
-						if id_lifecycle.is_parachain() && other_lifecycle.is_parathread() {
-							// We check that both paras are in an appropriate lifecycle for a swap,
-							// so these should never fail.
-							let res1 = runtime_parachains::schedule_parachain_downgrade::<T>(id);
-							debug_assert!(res1.is_ok());
-							let res2 = runtime_parachains::schedule_parathread_upgrade::<T>(other);
-							debug_assert!(res2.is_ok());
-							T::OnSwap::on_swap(id, other);
-						} else if id_lifecycle.is_parathread() && other_lifecycle.is_parachain() {
-							// We check that both paras are in an appropriate lifecycle for a swap,
-							// so these should never fail.
-							let res1 = runtime_parachains::schedule_parachain_downgrade::<T>(other);
-							debug_assert!(res1.is_ok());
-							let res2 = runtime_parachains::schedule_parathread_upgrade::<T>(id);
-							debug_assert!(res2.is_ok());
-							T::OnSwap::on_swap(id, other);
-						}
-
-						PendingSwap::<T>::remove(other);
-					}
+				let other_lifecycle =
+					paras::Pallet::<T>::lifecycle(other).ok_or(Error::<T>::NotRegistered)?;
+				// identify which is a parachain and which is a parathread
+				if id_lifecycle == ParaLifecycle::Parachain &&
+					other_lifecycle == ParaLifecycle::Parathread
+				{
+					Self::do_thread_and_chain_swap(id, other);
+				} else if id_lifecycle == ParaLifecycle::Parathread &&
+					other_lifecycle == ParaLifecycle::Parachain
+				{
+					Self::do_thread_and_chain_swap(other, id);
+				} else if id_lifecycle == ParaLifecycle::Parachain &&
+					other_lifecycle == ParaLifecycle::Parachain
+				{
+					// If both chains are currently parachains, there is nothing funny we
+					// need to do for their lifecycle management, just swap the underlying
+					// data.
+					T::OnSwap::on_swap(id, other);
+				} else {
+					return Err(Error::<T>::CannotSwap.into())
 				}
+				PendingSwap::<T>::remove(other);
 			} else {
 				PendingSwap::<T>::insert(id, other);
 			}
@@ -564,6 +576,15 @@ impl<T: Config> Pallet<T> {
 
 		Ok((ParaGenesisArgs { genesis_head, validation_code, parachain }, deposit))
 	}
+
+	/// Swap a parachain and parathread, which involves scheduling an appropriate lifecycle update.
+	fn do_thread_and_chain_swap(to_downgrade: ParaId, to_upgrade: ParaId) {
+		let res1 = runtime_parachains::schedule_parachain_downgrade::<T>(to_downgrade);
+		debug_assert!(res1.is_ok());
+		let res2 = runtime_parachains::schedule_parathread_upgrade::<T>(to_upgrade);
+		debug_assert!(res2.is_ok());
+		T::OnSwap::on_swap(to_upgrade, to_downgrade);
+	}
 }
 
 #[cfg(test)]
@@ -587,6 +608,7 @@ mod tests {
 		transaction_validity::TransactionPriority,
 		Perbill,
 	};
+	use sp_std::collections::btree_map::BTreeMap;
 
 	type UncheckedExtrinsic = frame_system::mocking::MockUncheckedExtrinsic<Test>;
 	type Block = frame_system::mocking::MockBlock<Test>;
@@ -696,7 +718,7 @@ mod tests {
 		type Event = Event;
 		type Origin = Origin;
 		type Currency = Balances;
-		type OnSwap = ();
+		type OnSwap = MockSwap;
 		type ParaDeposit = ParaDeposit;
 		type DataDepositPerByte = DataDepositPerByte;
 		type WeightInfo = TestWeightInfo;
@@ -724,6 +746,22 @@ mod tests {
 		t.into()
 	}
 
+	parameter_types! {
+		pub static SwapData: BTreeMap<ParaId, u64> = BTreeMap::new();
+	}
+
+	pub struct MockSwap;
+	impl OnSwap for MockSwap {
+		fn on_swap(one: ParaId, other: ParaId) {
+			let mut swap_data = SwapData::get();
+			let one_data = swap_data.remove(&one).unwrap_or_default();
+			let other_data = swap_data.remove(&other).unwrap_or_default();
+			swap_data.insert(one, other_data);
+			swap_data.insert(other, one_data);
+			SwapData::set(swap_data);
+		}
+	}
+
 	const BLOCKS_PER_SESSION: u32 = 3;
 
 	fn run_to_block(n: BlockNumber) {
@@ -997,9 +1035,15 @@ mod tests {
 			));
 			run_to_session(2);
 
-			// Upgrade 1023 into a parachain
+			// Upgrade para 1 into a parachain
 			assert_ok!(Registrar::make_parachain(para_1));
 
+			// Set some mock swap data.
+			let mut swap_data = SwapData::get();
+			swap_data.insert(para_1, 69);
+			swap_data.insert(para_2, 1337);
+			SwapData::set(swap_data);
+
 			run_to_session(4);
 
 			// Roles are as we expect
@@ -1014,20 +1058,15 @@ mod tests {
 
 			run_to_session(6);
 
-			// Deregister a parathread that was originally a parachain
-			assert_eq!(Parachains::lifecycle(para_1), Some(ParaLifecycle::Parathread));
-			assert_ok!(Registrar::deregister(
-				runtime_parachains::Origin::Parachain(para_1).into(),
-				para_1
-			));
-
-			run_to_block(21);
-
 			// Roles are swapped
 			assert!(!Parachains::is_parachain(para_1));
 			assert!(Parachains::is_parathread(para_1));
 			assert!(Parachains::is_parachain(para_2));
 			assert!(!Parachains::is_parathread(para_2));
+
+			// Data is swapped
+			assert_eq!(SwapData::get().get(&para_1).unwrap(), &1337);
+			assert_eq!(SwapData::get().get(&para_2).unwrap(), &69);
 		});
 	}
 
@@ -1059,6 +1098,121 @@ mod tests {
 			assert_noop!(Registrar::swap(Origin::signed(1), para_id, para_id + 2), BadOrigin);
 		});
 	}
+
+	#[test]
+	fn swap_handles_bad_states() {
+		new_test_ext().execute_with(|| {
+			let para_1 = LOWEST_PUBLIC_ID;
+			let para_2 = LOWEST_PUBLIC_ID + 1;
+			run_to_block(1);
+			// paras are not yet registered
+			assert!(!Parachains::is_parathread(para_1));
+			assert!(!Parachains::is_parathread(para_2));
+
+			// Cannot even start a swap
+			assert_noop!(
+				Registrar::swap(Origin::root(), para_1, para_2),
+				Error::<Test>::NotRegistered
+			);
+
+			// We register Paras 1 and 2
+			assert_ok!(Registrar::reserve(Origin::signed(1)));
+			assert_ok!(Registrar::reserve(Origin::signed(2)));
+			assert_ok!(Registrar::register(
+				Origin::signed(1),
+				para_1,
+				test_genesis_head(32),
+				test_validation_code(32),
+			));
+			assert_ok!(Registrar::register(
+				Origin::signed(2),
+				para_2,
+				test_genesis_head(32),
+				test_validation_code(32),
+			));
+
+			// Cannot swap
+			assert_ok!(Registrar::swap(Origin::root(), para_1, para_2));
+			assert_noop!(
+				Registrar::swap(Origin::root(), para_2, para_1),
+				Error::<Test>::CannotSwap
+			);
+
+			run_to_session(2);
+
+			// They are now a parathread.
+			assert!(Parachains::is_parathread(para_1));
+			assert!(Parachains::is_parathread(para_2));
+
+			// Cannot swap
+			assert_ok!(Registrar::swap(Origin::root(), para_1, para_2));
+			assert_noop!(
+				Registrar::swap(Origin::root(), para_2, para_1),
+				Error::<Test>::CannotSwap
+			);
+
+			// Some other external process will elevate one parathread to parachain
+			assert_ok!(Registrar::make_parachain(para_1));
+
+			// Cannot swap
+			assert_ok!(Registrar::swap(Origin::root(), para_1, para_2));
+			assert_noop!(
+				Registrar::swap(Origin::root(), para_2, para_1),
+				Error::<Test>::CannotSwap
+			);
+
+			run_to_session(3);
+
+			// Cannot swap
+			assert_ok!(Registrar::swap(Origin::root(), para_1, para_2));
+			assert_noop!(
+				Registrar::swap(Origin::root(), para_2, para_1),
+				Error::<Test>::CannotSwap
+			);
+
+			run_to_session(4);
+
+			// It is now a parachain.
+			assert!(Parachains::is_parachain(para_1));
+			assert!(Parachains::is_parathread(para_2));
+
+			// Swap works here.
+			assert_ok!(Registrar::swap(Origin::root(), para_1, para_2));
+			assert_ok!(Registrar::swap(Origin::root(), para_2, para_1));
+
+			run_to_session(5);
+
+			// Cannot swap
+			assert_ok!(Registrar::swap(Origin::root(), para_1, para_2));
+			assert_noop!(
+				Registrar::swap(Origin::root(), para_2, para_1),
+				Error::<Test>::CannotSwap
+			);
+
+			run_to_session(6);
+
+			// Swap worked!
+			assert!(Parachains::is_parachain(para_2));
+			assert!(Parachains::is_parathread(para_1));
+
+			// Something starts to downgrade a para
+			assert_ok!(Registrar::make_parathread(para_2));
+
+			run_to_session(7);
+
+			// Cannot swap
+			assert_ok!(Registrar::swap(Origin::root(), para_1, para_2));
+			assert_noop!(
+				Registrar::swap(Origin::root(), para_2, para_1),
+				Error::<Test>::CannotSwap
+			);
+
+			run_to_session(8);
+
+			assert!(Parachains::is_parathread(para_1));
+			assert!(Parachains::is_parathread(para_2));
+		});
+	}
 }
 
 #[cfg(feature = "runtime-benchmarks")]
diff --git a/polkadot/runtime/kusama/src/lib.rs b/polkadot/runtime/kusama/src/lib.rs
index 7fa952e5452..b1ade62ec00 100644
--- a/polkadot/runtime/kusama/src/lib.rs
+++ b/polkadot/runtime/kusama/src/lib.rs
@@ -138,11 +138,11 @@ pub fn native_version() -> NativeVersion {
 	NativeVersion { runtime_version: VERSION, can_author_with: Default::default() }
 }
 
-/// Don't allow swaps until parathread story is more mature.
+/// We currently allow all calls.
 pub struct BaseFilter;
 impl Contains<Call> for BaseFilter {
-	fn contains(c: &Call) -> bool {
-		!matches!(c, Call::Registrar(paras_registrar::Call::swap { .. }))
+	fn contains(_c: &Call) -> bool {
+		true
 	}
 }
 
@@ -1497,7 +1497,7 @@ pub type Executive = frame_executive::Executive<
 	frame_system::ChainContext<Runtime>,
 	Runtime,
 	AllPalletsWithSystem,
-	(SchedulerMigrationV3, RefundNickPalletDeposit),
+	(SchedulerMigrationV3, RefundNickPalletDeposit, CrowdloanIndexMigration),
 >;
 /// The payload being signed in the transactions.
 pub type SignedPayload = generic::SignedPayload<Call, SignedExtra>;
@@ -2929,6 +2929,24 @@ impl OnRuntimeUpgrade for SchedulerMigrationV3 {
 	}
 }
 
+// Migration for crowdloan pallet to use fund index for account generation.
+pub struct CrowdloanIndexMigration;
+impl OnRuntimeUpgrade for CrowdloanIndexMigration {
+	fn on_runtime_upgrade() -> frame_support::weights::Weight {
+		crowdloan::migration::crowdloan_index_migration::migrate::<Runtime>()
+	}
+
+	#[cfg(feature = "try-runtime")]
+	fn pre_upgrade() -> Result<(), &'static str> {
+		crowdloan::migration::crowdloan_index_migration::pre_migrate::<Runtime>()
+	}
+
+	#[cfg(feature = "try-runtime")]
+	fn post_upgrade() -> Result<(), &'static str> {
+		crowdloan::migration::crowdloan_index_migration::post_migrate::<Runtime>()
+	}
+}
+
 /// Migrate session-historical from `Session` to the new pallet prefix `Historical`
 pub struct SessionHistoricalPalletPrefixMigration;
 
diff --git a/polkadot/runtime/polkadot/src/lib.rs b/polkadot/runtime/polkadot/src/lib.rs
index 507a072a4c0..402e6d06120 100644
--- a/polkadot/runtime/polkadot/src/lib.rs
+++ b/polkadot/runtime/polkadot/src/lib.rs
@@ -1446,11 +1446,29 @@ pub type Executive = frame_executive::Executive<
 	frame_system::ChainContext<Runtime>,
 	Runtime,
 	AllPalletsWithSystem,
-	(SchedulerMigrationV3, FixCouncilDepositMigration),
+	(SchedulerMigrationV3, FixCouncilDepositMigration, CrowdloanIndexMigration),
 >;
 /// The payload being signed in transactions.
 pub type SignedPayload = generic::SignedPayload<Call, SignedExtra>;
 
+// Migration for crowdloan pallet to use fund index for account generation.
+pub struct CrowdloanIndexMigration;
+impl OnRuntimeUpgrade for CrowdloanIndexMigration {
+	fn on_runtime_upgrade() -> frame_support::weights::Weight {
+		crowdloan::migration::crowdloan_index_migration::migrate::<Runtime>()
+	}
+
+	#[cfg(feature = "try-runtime")]
+	fn pre_upgrade() -> Result<(), &'static str> {
+		crowdloan::migration::crowdloan_index_migration::pre_migrate::<Runtime>()
+	}
+
+	#[cfg(feature = "try-runtime")]
+	fn post_upgrade() -> Result<(), &'static str> {
+		crowdloan::migration::crowdloan_index_migration::post_migrate::<Runtime>()
+	}
+}
+
 /// A migration struct to fix some deposits in the council election pallet.
 ///
 /// See more details here: https://github.com/paritytech/polkadot/issues/4160
diff --git a/polkadot/runtime/rococo/src/lib.rs b/polkadot/runtime/rococo/src/lib.rs
index fa65abcc23c..021d66c3741 100644
--- a/polkadot/runtime/rococo/src/lib.rs
+++ b/polkadot/runtime/rococo/src/lib.rs
@@ -154,11 +154,29 @@ pub type Executive = frame_executive::Executive<
 	frame_system::ChainContext<Runtime>,
 	Runtime,
 	AllPalletsWithSystem,
-	(SessionHistoricalModulePrefixMigration,),
+	(SessionHistoricalModulePrefixMigration, CrowdloanIndexMigration),
 >;
 /// The payload being signed in transactions.
 pub type SignedPayload = generic::SignedPayload<Call, SignedExtra>;
 
+// Migration for crowdloan pallet to use fund index for account generation.
+pub struct CrowdloanIndexMigration;
+impl OnRuntimeUpgrade for CrowdloanIndexMigration {
+	fn on_runtime_upgrade() -> frame_support::weights::Weight {
+		crowdloan::migration::crowdloan_index_migration::migrate::<Runtime>()
+	}
+
+	#[cfg(feature = "try-runtime")]
+	fn pre_upgrade() -> Result<(), &'static str> {
+		crowdloan::migration::crowdloan_index_migration::pre_migrate::<Runtime>()
+	}
+
+	#[cfg(feature = "try-runtime")]
+	fn post_upgrade() -> Result<(), &'static str> {
+		crowdloan::migration::crowdloan_index_migration::post_migrate::<Runtime>()
+	}
+}
+
 /// Migrate session-historical from `Session` to the new pallet prefix `Historical`
 pub struct SessionHistoricalModulePrefixMigration;
 
diff --git a/polkadot/runtime/westend/src/lib.rs b/polkadot/runtime/westend/src/lib.rs
index 99644576dc1..dcbfa5e3dea 100644
--- a/polkadot/runtime/westend/src/lib.rs
+++ b/polkadot/runtime/westend/src/lib.rs
@@ -1084,11 +1084,29 @@ pub type Executive = frame_executive::Executive<
 	frame_system::ChainContext<Runtime>,
 	Runtime,
 	AllPalletsWithSystem,
-	(SessionHistoricalPalletPrefixMigration, SchedulerMigrationV3),
+	(SessionHistoricalPalletPrefixMigration, SchedulerMigrationV3, CrowdloanIndexMigration),
 >;
 /// The payload being signed in transactions.
 pub type SignedPayload = generic::SignedPayload<Call, SignedExtra>;
 
+// Migration for crowdloan pallet to use fund index for account generation.
+pub struct CrowdloanIndexMigration;
+impl OnRuntimeUpgrade for CrowdloanIndexMigration {
+	fn on_runtime_upgrade() -> frame_support::weights::Weight {
+		crowdloan::migration::crowdloan_index_migration::migrate::<Runtime>()
+	}
+
+	#[cfg(feature = "try-runtime")]
+	fn pre_upgrade() -> Result<(), &'static str> {
+		crowdloan::migration::crowdloan_index_migration::pre_migrate::<Runtime>()
+	}
+
+	#[cfg(feature = "try-runtime")]
+	fn post_upgrade() -> Result<(), &'static str> {
+		crowdloan::migration::crowdloan_index_migration::post_migrate::<Runtime>()
+	}
+}
+
 // Migration for scheduler pallet to move from a plain Call to a CallOrHash.
 pub struct SchedulerMigrationV3;
 
-- 
GitLab