diff --git a/substrate/Cargo.lock b/substrate/Cargo.lock
index 6d1b19e701a5039b43b2b785426a44a823c9ae16..3dd5bc52b71c4347fac8f7283e07a52683b46c96 100644
--- a/substrate/Cargo.lock
+++ b/substrate/Cargo.lock
@@ -2594,6 +2594,7 @@ dependencies = [
  "array-bytes",
  "assert_matches",
  "bitflags",
+ "docify",
  "environmental",
  "frame-metadata",
  "frame-support-procedural",
diff --git a/substrate/frame/democracy/src/migrations/unlock_and_unreserve_all_funds.rs b/substrate/frame/democracy/src/migrations/unlock_and_unreserve_all_funds.rs
index 1f46070d1d7fc83c512f94f531b1bb1b858539b9..188c475f64d0ef3e55e6daf3f86f9423e93a4efa 100644
--- a/substrate/frame/democracy/src/migrations/unlock_and_unreserve_all_funds.rs
+++ b/substrate/frame/democracy/src/migrations/unlock_and_unreserve_all_funds.rs
@@ -18,15 +18,67 @@
 //! A migration that unreserves all deposit and unlocks all stake held in the context of this
 //! pallet.
 
-use crate::{BalanceOf, DEMOCRACY_ID};
+use crate::{PropIndex, Voting, DEMOCRACY_ID};
 use core::iter::Sum;
-use frame_support::traits::{LockableCurrency, OnRuntimeUpgrade, ReservableCurrency};
+use frame_support::{
+	pallet_prelude::ValueQuery,
+	storage_alias,
+	traits::{Currency, LockableCurrency, OnRuntimeUpgrade, ReservableCurrency},
+	weights::RuntimeDbWeight,
+	Parameter, Twox64Concat,
+};
 use sp_core::Get;
-use sp_runtime::{traits::Zero, Saturating};
+use sp_runtime::{traits::Zero, BoundedVec, Saturating};
 use sp_std::{collections::btree_map::BTreeMap, vec::Vec};
 
 const LOG_TARGET: &str = "runtime::democracy::migrations::unlock_and_unreserve_all_funds";
 
+type BalanceOf<T> =
+	<<T as UnlockConfig>::Currency as Currency<<T as UnlockConfig>::AccountId>>::Balance;
+
+/// The configuration for [`UnlockAndUnreserveAllFunds`].
+pub trait UnlockConfig: 'static {
+	/// The account ID used in the runtime.
+	type AccountId: Parameter + Ord;
+	/// The currency type used in the runtime.
+	///
+	/// Should match the currency type previously used for the pallet, if applicable.
+	type Currency: LockableCurrency<Self::AccountId> + ReservableCurrency<Self::AccountId>;
+	/// The name of the pallet as previously configured in
+	/// [`construct_runtime!`](frame_support::construct_runtime).
+	type PalletName: Get<&'static str>;
+	/// The maximum number of votes as configured previously in the runtime.
+	type MaxVotes: Get<u32>;
+	/// The maximum deposit as configured previously in the runtime.
+	type MaxDeposits: Get<u32>;
+	/// The DB weight as configured in the runtime to calculate the correct weight.
+	type DbWeight: Get<RuntimeDbWeight>;
+	/// The block number as configured in the runtime.
+	type BlockNumber: Parameter + Zero + Copy + Ord;
+}
+
+#[storage_alias(dynamic)]
+type DepositOf<T: UnlockConfig> = StorageMap<
+	<T as UnlockConfig>::PalletName,
+	Twox64Concat,
+	PropIndex,
+	(BoundedVec<<T as UnlockConfig>::AccountId, <T as UnlockConfig>::MaxDeposits>, BalanceOf<T>),
+>;
+
+#[storage_alias(dynamic)]
+type VotingOf<T: UnlockConfig> = StorageMap<
+	<T as UnlockConfig>::PalletName,
+	Twox64Concat,
+	<T as UnlockConfig>::AccountId,
+	Voting<
+		BalanceOf<T>,
+		<T as UnlockConfig>::AccountId,
+		<T as UnlockConfig>::BlockNumber,
+		<T as UnlockConfig>::MaxVotes,
+	>,
+	ValueQuery,
+>;
+
 /// A migration that unreserves all deposit and unlocks all stake held in the context of this
 /// pallet.
 ///
@@ -35,9 +87,9 @@ const LOG_TARGET: &str = "runtime::democracy::migrations::unlock_and_unreserve_a
 /// The pallet should be made inoperable before this migration is run.
 ///
 /// (See also [`RemovePallet`][frame_support::migrations::RemovePallet])
-pub struct UnlockAndUnreserveAllFunds<T: crate::Config>(sp_std::marker::PhantomData<T>);
+pub struct UnlockAndUnreserveAllFunds<T: UnlockConfig>(sp_std::marker::PhantomData<T>);
 
-impl<T: crate::Config> UnlockAndUnreserveAllFunds<T> {
+impl<T: UnlockConfig> UnlockAndUnreserveAllFunds<T> {
 	/// Calculates and returns the total amounts reserved by each account by this pallet, and all
 	/// accounts with locks in the context of this pallet.
 	///
@@ -63,7 +115,7 @@ impl<T: crate::Config> UnlockAndUnreserveAllFunds<T> {
 
 		// Get all deposits (reserved).
 		let mut total_voting_vec_entries: u64 = 0;
-		let account_deposits: BTreeMap<T::AccountId, BalanceOf<T>> = crate::DepositOf::<T>::iter()
+		let account_deposits: BTreeMap<T::AccountId, BalanceOf<T>> = DepositOf::<T>::iter()
 			.flat_map(|(_prop_index, (accounts, balance))| {
 				// Count the number of deposits
 				deposit_of_len.saturating_inc();
@@ -81,7 +133,7 @@ impl<T: crate::Config> UnlockAndUnreserveAllFunds<T> {
 			});
 
 		// Voter accounts have amounts locked.
-		let account_stakes: BTreeMap<T::AccountId, BalanceOf<T>> = crate::VotingOf::<T>::iter()
+		let account_stakes: BTreeMap<T::AccountId, BalanceOf<T>> = VotingOf::<T>::iter()
 			.map(|(account_id, voting)| (account_id, voting.locked_balance()))
 			.collect();
 		let voting_of_len = account_stakes.len() as u64;
@@ -100,7 +152,7 @@ impl<T: crate::Config> UnlockAndUnreserveAllFunds<T> {
 	}
 }
 
-impl<T: crate::Config> OnRuntimeUpgrade for UnlockAndUnreserveAllFunds<T>
+impl<T: UnlockConfig> OnRuntimeUpgrade for UnlockAndUnreserveAllFunds<T>
 where
 	BalanceOf<T>: Sum,
 {
@@ -241,14 +293,32 @@ where
 mod test {
 	use super::*;
 	use crate::{
-		tests::{new_test_ext, Test},
+		tests::{new_test_ext, Balances, Test},
 		DepositOf, Voting, VotingOf,
 	};
 	use frame_support::{
-		assert_ok,
+		assert_ok, parameter_types,
 		traits::{Currency, OnRuntimeUpgrade, ReservableCurrency, WithdrawReasons},
 		BoundedVec,
 	};
+	use frame_system::pallet_prelude::BlockNumberFor;
+	use sp_core::ConstU32;
+
+	parameter_types! {
+		const PalletName: &'static str = "Democracy";
+	}
+
+	struct UnlockConfigImpl;
+
+	impl super::UnlockConfig for UnlockConfigImpl {
+		type Currency = Balances;
+		type MaxVotes = ConstU32<100>;
+		type MaxDeposits = ConstU32<1000>;
+		type AccountId = u64;
+		type BlockNumber = BlockNumberFor<Test>;
+		type DbWeight = ();
+		type PalletName = PalletName;
+	}
 
 	#[test]
 	fn unreserve_works_for_depositer() {
@@ -288,10 +358,10 @@ mod test {
 			);
 
 			// Run the migration.
-			let bytes = UnlockAndUnreserveAllFunds::<Test>::pre_upgrade()
+			let bytes = UnlockAndUnreserveAllFunds::<UnlockConfigImpl>::pre_upgrade()
 				.unwrap_or_else(|e| panic!("pre_upgrade failed: {:?}", e));
-			UnlockAndUnreserveAllFunds::<Test>::on_runtime_upgrade();
-			assert_ok!(UnlockAndUnreserveAllFunds::<Test>::post_upgrade(bytes));
+			UnlockAndUnreserveAllFunds::<UnlockConfigImpl>::on_runtime_upgrade();
+			assert_ok!(UnlockAndUnreserveAllFunds::<UnlockConfigImpl>::post_upgrade(bytes));
 
 			// Assert the reserved balance was reduced by the expected amount.
 			assert_eq!(
@@ -342,10 +412,10 @@ mod test {
 			);
 
 			// Run the migration.
-			let bytes = UnlockAndUnreserveAllFunds::<Test>::pre_upgrade()
+			let bytes = UnlockAndUnreserveAllFunds::<UnlockConfigImpl>::pre_upgrade()
 				.unwrap_or_else(|e| panic!("pre_upgrade failed: {:?}", e));
-			UnlockAndUnreserveAllFunds::<Test>::on_runtime_upgrade();
-			assert_ok!(UnlockAndUnreserveAllFunds::<Test>::post_upgrade(bytes));
+			UnlockAndUnreserveAllFunds::<UnlockConfigImpl>::on_runtime_upgrade();
+			assert_ok!(UnlockAndUnreserveAllFunds::<UnlockConfigImpl>::post_upgrade(bytes));
 
 			// Assert the voter lock was removed
 			assert_eq!(
diff --git a/substrate/frame/support/Cargo.toml b/substrate/frame/support/Cargo.toml
index fab6167f3d50f5207d2c1a2b355c835c05d173bd..da6ee19e04a0dc4c8db41bb66828017742462169 100644
--- a/substrate/frame/support/Cargo.toml
+++ b/substrate/frame/support/Cargo.toml
@@ -42,6 +42,7 @@ k256 = { version = "0.13.1", default-features = false, features = ["ecdsa"] }
 environmental = { version = "1.1.4", default-features = false }
 sp-genesis-builder = { version = "0.1.0", default-features=false, path = "../../primitives/genesis-builder" }
 serde_json = { version = "1.0.85", default-features = false, features = ["alloc"] }
+docify = "0.2.1"
 
 aquamarine = { version = "0.3.2" }
 
diff --git a/substrate/frame/support/procedural/src/lib.rs b/substrate/frame/support/procedural/src/lib.rs
index f0d4c30a939c137716dfb089784cda19dbfc02ee..2a46696ed4f7070cdf738bac3e1a0ba90137ef9b 100644
--- a/substrate/frame/support/procedural/src/lib.rs
+++ b/substrate/frame/support/procedural/src/lib.rs
@@ -547,8 +547,8 @@ pub fn __create_tt_macro(input: TokenStream) -> TokenStream {
 }
 
 #[proc_macro_attribute]
-pub fn storage_alias(_: TokenStream, input: TokenStream) -> TokenStream {
-	storage_alias::storage_alias(input.into())
+pub fn storage_alias(attributes: TokenStream, input: TokenStream) -> TokenStream {
+	storage_alias::storage_alias(attributes.into(), input.into())
 		.unwrap_or_else(|r| r.into_compile_error())
 		.into()
 }
diff --git a/substrate/frame/support/procedural/src/storage_alias.rs b/substrate/frame/support/procedural/src/storage_alias.rs
index b44a7ee997fe2bc7f45bd384bc6db91dd75e43a6..d1d1aba47998eec75976e744d662c0631e150202 100644
--- a/substrate/frame/support/procedural/src/storage_alias.rs
+++ b/substrate/frame/support/procedural/src/storage_alias.rs
@@ -22,78 +22,48 @@ use frame_support_procedural_tools::generate_crate_access_2018;
 use proc_macro2::{Span, TokenStream};
 use quote::{quote, ToTokens};
 use syn::{
-	ext::IdentExt,
 	parenthesized,
 	parse::{Parse, ParseStream},
 	punctuated::Punctuated,
-	token, Attribute, Error, Ident, Result, Token, Type, TypeParam, Visibility, WhereClause,
+	spanned::Spanned,
+	token,
+	visit::Visit,
+	Attribute, Error, Ident, Result, Token, Type, TypeParam, Visibility, WhereClause,
 };
 
-/// Represents a path that only consists of [`Ident`] separated by `::`.
-struct SimplePath {
-	leading_colon: Option<Token![::]>,
-	segments: Punctuated<Ident, Token![::]>,
+/// Extension trait for [`Type`].
+trait TypeExt {
+	fn get_ident(&self) -> Option<&Ident>;
+	fn contains_ident(&self, ident: &Ident) -> bool;
 }
 
-impl SimplePath {
-	/// Returns the [`Ident`] of this path.
-	///
-	/// It only returns `Some(_)` if there is exactly one element and no leading colon.
+impl TypeExt for Type {
 	fn get_ident(&self) -> Option<&Ident> {
-		if self.segments.len() != 1 || self.leading_colon.is_some() {
-			None
-		} else {
-			self.segments.first()
+		match self {
+			Type::Path(p) => match &p.qself {
+				Some(qself) => qself.ty.get_ident(),
+				None => p.path.get_ident(),
+			},
+			_ => None,
 		}
 	}
-}
-
-impl Parse for SimplePath {
-	fn parse(input: ParseStream<'_>) -> Result<Self> {
-		Ok(Self {
-			leading_colon: if input.peek(Token![::]) { Some(input.parse()?) } else { None },
-			segments: Punctuated::parse_separated_nonempty_with(input, |p| Ident::parse_any(p))?,
-		})
-	}
-}
-
-impl ToTokens for SimplePath {
-	fn to_tokens(&self, tokens: &mut TokenStream) {
-		self.leading_colon.to_tokens(tokens);
-		self.segments.to_tokens(tokens);
-	}
-}
-
-/// Represents generics which only support [`Ident`] separated by commas as you would pass it to a
-/// type.
-struct TypeGenerics {
-	lt_token: Token![<],
-	params: Punctuated<Ident, token::Comma>,
-	gt_token: Token![>],
-}
 
-impl TypeGenerics {
-	/// Returns the generics for types declarations etc.
-	fn iter(&self) -> impl Iterator<Item = &Ident> {
-		self.params.iter()
-	}
-}
+	fn contains_ident(&self, ident: &Ident) -> bool {
+		struct ContainsIdent<'a> {
+			ident: &'a Ident,
+			found: bool,
+		}
+		impl<'a, 'ast> Visit<'ast> for ContainsIdent<'a> {
+			fn visit_ident(&mut self, i: &'ast Ident) {
+				if i == self.ident {
+					self.found = true;
+				}
+			}
+		}
 
-impl Parse for TypeGenerics {
-	fn parse(input: ParseStream<'_>) -> Result<Self> {
-		Ok(Self {
-			lt_token: input.parse()?,
-			params: Punctuated::parse_separated_nonempty(input)?,
-			gt_token: input.parse()?,
-		})
-	}
-}
-
-impl ToTokens for TypeGenerics {
-	fn to_tokens(&self, tokens: &mut TokenStream) {
-		self.lt_token.to_tokens(tokens);
-		self.params.to_tokens(tokens);
-		self.gt_token.to_tokens(tokens);
+		let mut visitor = ContainsIdent { ident, found: false };
+		syn::visit::visit_type(&mut visitor, self);
+		visitor.found
 	}
 }
 
@@ -142,13 +112,22 @@ mod storage_types {
 	syn::custom_keyword!(StorageNMap);
 }
 
+/// The types of prefixes the storage alias macro supports.
+mod prefix_types {
+	// Use the verbatim/unmodified input name as the prefix.
+	syn::custom_keyword!(verbatim);
+	// The input type is a pallet and its pallet name should be used as the prefix.
+	syn::custom_keyword!(pallet_name);
+	// The input type implements `Get<'static str>` and this `str` should be used as the prefix.
+	syn::custom_keyword!(dynamic);
+}
+
 /// The supported storage types
 enum StorageType {
 	Value {
 		_kw: storage_types::StorageValue,
 		_lt_token: Token![<],
-		prefix: SimplePath,
-		prefix_generics: Option<TypeGenerics>,
+		prefix: Type,
 		_value_comma: Token![,],
 		value_ty: Type,
 		query_type: Option<(Token![,], Type)>,
@@ -158,8 +137,7 @@ enum StorageType {
 	Map {
 		_kw: storage_types::StorageMap,
 		_lt_token: Token![<],
-		prefix: SimplePath,
-		prefix_generics: Option<TypeGenerics>,
+		prefix: Type,
 		_hasher_comma: Token![,],
 		hasher_ty: Type,
 		_key_comma: Token![,],
@@ -173,8 +151,7 @@ enum StorageType {
 	CountedMap {
 		_kw: storage_types::CountedStorageMap,
 		_lt_token: Token![<],
-		prefix: SimplePath,
-		prefix_generics: Option<TypeGenerics>,
+		prefix: Type,
 		_hasher_comma: Token![,],
 		hasher_ty: Type,
 		_key_comma: Token![,],
@@ -188,8 +165,7 @@ enum StorageType {
 	DoubleMap {
 		_kw: storage_types::StorageDoubleMap,
 		_lt_token: Token![<],
-		prefix: SimplePath,
-		prefix_generics: Option<TypeGenerics>,
+		prefix: Type,
 		_hasher1_comma: Token![,],
 		hasher1_ty: Type,
 		_key1_comma: Token![,],
@@ -207,8 +183,7 @@ enum StorageType {
 	NMap {
 		_kw: storage_types::StorageNMap,
 		_lt_token: Token![<],
-		prefix: SimplePath,
-		prefix_generics: Option<TypeGenerics>,
+		prefix: Type,
 		_paren_comma: Token![,],
 		_paren_token: token::Paren,
 		key_types: Punctuated<Type, Token![,]>,
@@ -231,6 +206,7 @@ impl StorageType {
 		visibility: &Visibility,
 		attributes: &[Attribute],
 	) -> TokenStream {
+		let storage_instance_generics = &storage_instance.generics;
 		let storage_instance = &storage_instance.name;
 		let attributes = attributes.iter();
 		let storage_generics = storage_generics.map(|g| {
@@ -240,22 +216,20 @@ impl StorageType {
 		});
 
 		match self {
-			Self::Value { value_ty, query_type, prefix_generics, .. } => {
+			Self::Value { value_ty, query_type, .. } => {
 				let query_type = query_type.as_ref().map(|(c, t)| quote!(#c #t));
 
 				quote! {
 					#( #attributes )*
 					#visibility type #storage_name #storage_generics = #crate_::storage::types::StorageValue<
-						#storage_instance #prefix_generics,
+						#storage_instance #storage_instance_generics,
 						#value_ty
 						#query_type
 					>;
 				}
 			},
-			Self::CountedMap {
-				value_ty, query_type, hasher_ty, key_ty, prefix_generics, ..
-			} |
-			Self::Map { value_ty, query_type, hasher_ty, key_ty, prefix_generics, .. } => {
+			Self::CountedMap { value_ty, query_type, hasher_ty, key_ty, .. } |
+			Self::Map { value_ty, query_type, hasher_ty, key_ty, .. } => {
 				let query_type = query_type.as_ref().map(|(c, t)| quote!(#c #t));
 				let map_type = Ident::new(
 					match self {
@@ -268,7 +242,7 @@ impl StorageType {
 				quote! {
 					#( #attributes )*
 					#visibility type #storage_name #storage_generics = #crate_::storage::types::#map_type<
-						#storage_instance #prefix_generics,
+						#storage_instance #storage_instance_generics,
 						#hasher_ty,
 						#key_ty,
 						#value_ty
@@ -283,7 +257,6 @@ impl StorageType {
 				key1_ty,
 				hasher2_ty,
 				key2_ty,
-				prefix_generics,
 				..
 			} => {
 				let query_type = query_type.as_ref().map(|(c, t)| quote!(#c #t));
@@ -291,7 +264,7 @@ impl StorageType {
 				quote! {
 					#( #attributes )*
 					#visibility type #storage_name #storage_generics = #crate_::storage::types::StorageDoubleMap<
-						#storage_instance #prefix_generics,
+						#storage_instance #storage_instance_generics,
 						#hasher1_ty,
 						#key1_ty,
 						#hasher2_ty,
@@ -301,14 +274,14 @@ impl StorageType {
 					>;
 				}
 			},
-			Self::NMap { value_ty, query_type, key_types, prefix_generics, .. } => {
+			Self::NMap { value_ty, query_type, key_types, .. } => {
 				let query_type = query_type.as_ref().map(|(c, t)| quote!(#c #t));
 				let key_types = key_types.iter();
 
 				quote! {
 					#( #attributes )*
 					#visibility type #storage_name #storage_generics = #crate_::storage::types::StorageNMap<
-						#storage_instance #prefix_generics,
+						#storage_instance #storage_instance_generics,
 						( #( #key_types ),* ),
 						#value_ty
 						#query_type
@@ -319,7 +292,7 @@ impl StorageType {
 	}
 
 	/// The prefix for this storage type.
-	fn prefix(&self) -> &SimplePath {
+	fn prefix(&self) -> &Type {
 		match self {
 			Self::Value { prefix, .. } |
 			Self::Map { prefix, .. } |
@@ -328,17 +301,6 @@ impl StorageType {
 			Self::DoubleMap { prefix, .. } => prefix,
 		}
 	}
-
-	/// The prefix generics for this storage type.
-	fn prefix_generics(&self) -> Option<&TypeGenerics> {
-		match self {
-			Self::Value { prefix_generics, .. } |
-			Self::Map { prefix_generics, .. } |
-			Self::CountedMap { prefix_generics, .. } |
-			Self::NMap { prefix_generics, .. } |
-			Self::DoubleMap { prefix_generics, .. } => prefix_generics.as_ref(),
-		}
-	}
 }
 
 impl Parse for StorageType {
@@ -353,23 +315,11 @@ impl Parse for StorageType {
 			}
 		};
 
-		let parse_pallet_generics = |input: ParseStream<'_>| -> Result<Option<TypeGenerics>> {
-			let lookahead = input.lookahead1();
-			if lookahead.peek(Token![<]) {
-				Ok(Some(input.parse()?))
-			} else if lookahead.peek(Token![,]) {
-				Ok(None)
-			} else {
-				Err(lookahead.error())
-			}
-		};
-
 		if lookahead.peek(storage_types::StorageValue) {
 			Ok(Self::Value {
 				_kw: input.parse()?,
 				_lt_token: input.parse()?,
 				prefix: input.parse()?,
-				prefix_generics: parse_pallet_generics(input)?,
 				_value_comma: input.parse()?,
 				value_ty: input.parse()?,
 				query_type: parse_query_type(input)?,
@@ -381,7 +331,6 @@ impl Parse for StorageType {
 				_kw: input.parse()?,
 				_lt_token: input.parse()?,
 				prefix: input.parse()?,
-				prefix_generics: parse_pallet_generics(input)?,
 				_hasher_comma: input.parse()?,
 				hasher_ty: input.parse()?,
 				_key_comma: input.parse()?,
@@ -397,7 +346,6 @@ impl Parse for StorageType {
 				_kw: input.parse()?,
 				_lt_token: input.parse()?,
 				prefix: input.parse()?,
-				prefix_generics: parse_pallet_generics(input)?,
 				_hasher_comma: input.parse()?,
 				hasher_ty: input.parse()?,
 				_key_comma: input.parse()?,
@@ -413,7 +361,6 @@ impl Parse for StorageType {
 				_kw: input.parse()?,
 				_lt_token: input.parse()?,
 				prefix: input.parse()?,
-				prefix_generics: parse_pallet_generics(input)?,
 				_hasher1_comma: input.parse()?,
 				hasher1_ty: input.parse()?,
 				_key1_comma: input.parse()?,
@@ -434,7 +381,6 @@ impl Parse for StorageType {
 				_kw: input.parse()?,
 				_lt_token: input.parse()?,
 				prefix: input.parse()?,
-				prefix_generics: parse_pallet_generics(input)?,
 				_paren_comma: input.parse()?,
 				_paren_token: parenthesized!(content in input),
 				key_types: Punctuated::parse_terminated(&content)?,
@@ -508,20 +454,50 @@ impl Parse for Input {
 	}
 }
 
+/// Defines which type of prefix the storage alias is using.
+#[derive(Clone, Copy)]
+enum PrefixType {
+	/// An appropriate prefix will be determined automatically.
+	///
+	/// If generics are passed, this is assumed to be a pallet and the pallet name should be used.
+	/// Otherwise use the verbatim passed name as prefix.
+	Compatibility,
+	/// The provided ident/name will be used as the prefix.
+	Verbatim,
+	/// The provided type will be used to determine the prefix. This type must
+	/// implement `PalletInfoAccess` which specifies the proper name. This
+	/// name is then used as the prefix.
+	PalletName,
+	/// Uses the provided type implementing `Get<'static str>` to determine the prefix.
+	Dynamic,
+}
+
 /// Implementation of the `storage_alias` attribute macro.
-pub fn storage_alias(input: TokenStream) -> Result<TokenStream> {
+pub fn storage_alias(attributes: TokenStream, input: TokenStream) -> Result<TokenStream> {
 	let input = syn::parse2::<Input>(input)?;
 	let crate_ = generate_crate_access_2018("frame-support")?;
 
+	let prefix_type = if attributes.is_empty() {
+		PrefixType::Compatibility
+	} else if syn::parse2::<prefix_types::verbatim>(attributes.clone()).is_ok() {
+		PrefixType::Verbatim
+	} else if syn::parse2::<prefix_types::pallet_name>(attributes.clone()).is_ok() {
+		PrefixType::PalletName
+	} else if syn::parse2::<prefix_types::dynamic>(attributes.clone()).is_ok() {
+		PrefixType::Dynamic
+	} else {
+		return Err(Error::new(attributes.span(), "Unknown attributes"))
+	};
+
 	let storage_instance = generate_storage_instance(
 		&crate_,
 		&input.storage_name,
 		input.storage_generics.as_ref(),
 		input.where_clause.as_ref(),
 		input.storage_type.prefix(),
-		input.storage_type.prefix_generics(),
 		&input.visibility,
 		matches!(input.storage_type, StorageType::CountedMap { .. }),
+		prefix_type,
 	)?;
 
 	let definition = input.storage_type.generate_type_declaration(
@@ -545,6 +521,7 @@ pub fn storage_alias(input: TokenStream) -> Result<TokenStream> {
 /// The storage instance to use for the storage alias.
 struct StorageInstance {
 	name: Ident,
+	generics: TokenStream,
 	code: TokenStream,
 }
 
@@ -554,42 +531,84 @@ fn generate_storage_instance(
 	storage_name: &Ident,
 	storage_generics: Option<&SimpleGenerics>,
 	storage_where_clause: Option<&WhereClause>,
-	prefix: &SimplePath,
-	prefix_generics: Option<&TypeGenerics>,
+	prefix: &Type,
 	visibility: &Visibility,
 	is_counted_map: bool,
+	prefix_type: PrefixType,
 ) -> Result<StorageInstance> {
-	if let Some(ident) = prefix.get_ident().filter(|i| *i == "_") {
-		return Err(Error::new(ident.span(), "`_` is not allowed as prefix by `storage_alias`."))
+	if let Type::Infer(_) = prefix {
+		return Err(Error::new(prefix.span(), "`_` is not allowed as prefix by `storage_alias`."))
 	}
 
-	let (pallet_prefix, impl_generics, type_generics) =
-		if let Some((prefix_generics, storage_generics)) =
-			prefix_generics.and_then(|p| storage_generics.map(|s| (p, s)))
-		{
-			let type_generics = prefix_generics.iter();
-			let type_generics2 = prefix_generics.iter();
-			let impl_generics = storage_generics
-				.impl_generics()
-				.filter(|g| prefix_generics.params.iter().any(|pg| *pg == g.ident));
+	let impl_generics_used_by_prefix = storage_generics
+		.as_ref()
+		.map(|g| {
+			g.impl_generics()
+				.filter(|g| prefix.contains_ident(&g.ident))
+				.collect::<Vec<_>>()
+		})
+		.unwrap_or_default();
+
+	let (pallet_prefix, impl_generics, type_generics) = match prefix_type {
+		PrefixType::Compatibility =>
+			if !impl_generics_used_by_prefix.is_empty() {
+				let type_generics = impl_generics_used_by_prefix.iter().map(|g| &g.ident);
+				let impl_generics = impl_generics_used_by_prefix.iter();
+
+				(
+					quote! {
+						< #prefix as #crate_::traits::PalletInfoAccess>::name()
+					},
+					quote!( #( #impl_generics ),* ),
+					quote!( #( #type_generics ),* ),
+				)
+			} else if let Some(prefix) = prefix.get_ident() {
+				let prefix_str = prefix.to_string();
+
+				(quote!(#prefix_str), quote!(), quote!())
+			} else {
+				return Err(Error::new_spanned(
+					prefix,
+					"If there are no generics, the prefix is only allowed to be an identifier.",
+				))
+			},
+		PrefixType::Verbatim => {
+			let prefix_str = match prefix.get_ident() {
+				Some(p) => p.to_string(),
+				None =>
+					return Err(Error::new_spanned(
+						prefix,
+						"Prefix type `verbatim` requires that the prefix is an ident.",
+					)),
+			};
+
+			(quote!(#prefix_str), quote!(), quote!())
+		},
+		PrefixType::PalletName => {
+			let type_generics = impl_generics_used_by_prefix.iter().map(|g| &g.ident);
+			let impl_generics = impl_generics_used_by_prefix.iter();
 
 			(
 				quote! {
-					<#prefix < #( #type_generics2 ),* > as #crate_::traits::PalletInfoAccess>::name()
+					<#prefix as #crate_::traits::PalletInfoAccess>::name()
 				},
 				quote!( #( #impl_generics ),* ),
 				quote!( #( #type_generics ),* ),
 			)
-		} else if let Some(prefix) = prefix.get_ident() {
-			let prefix_str = prefix.to_string();
+		},
+		PrefixType::Dynamic => {
+			let type_generics = impl_generics_used_by_prefix.iter().map(|g| &g.ident);
+			let impl_generics = impl_generics_used_by_prefix.iter();
 
-			(quote!(#prefix_str), quote!(), quote!())
-		} else {
-			return Err(Error::new_spanned(
-				prefix,
-				"If there are no generics, the prefix is only allowed to be an identifier.",
-			))
-		};
+			(
+				quote! {
+					<#prefix as #crate_::traits::Get<_>>::get()
+				},
+				quote!( #( #impl_generics ),* ),
+				quote!( #( #type_generics ),* ),
+			)
+		},
+	};
 
 	let where_clause = storage_where_clause.map(|w| quote!(#w)).unwrap_or_default();
 
@@ -644,5 +663,5 @@ fn generate_storage_instance(
 		#counter_code
 	};
 
-	Ok(StorageInstance { name, code })
+	Ok(StorageInstance { name, code, generics: quote!( < #type_generics > ) })
 }
diff --git a/substrate/frame/support/src/lib.rs b/substrate/frame/support/src/lib.rs
index d37490c341724642696d86182f4cfea7d0ea4ba1..4cbbe17a9968b47da05bc96e0229d22bb2114a9b 100644
--- a/substrate/frame/support/src/lib.rs
+++ b/substrate/frame/support/src/lib.rs
@@ -74,6 +74,8 @@ pub mod inherent;
 pub mod instances;
 pub mod migrations;
 pub mod storage;
+#[cfg(test)]
+mod tests;
 pub mod traits;
 pub mod weights;
 #[doc(hidden)]
@@ -127,80 +129,50 @@ impl TypeId for PalletId {
 	const TYPE_ID: [u8; 4] = *b"modl";
 }
 
-/// Generate a new type alias for [`storage::types::StorageValue`],
-/// [`storage::types::StorageMap`], [`storage::types::StorageDoubleMap`]
-/// and [`storage::types::StorageNMap`].
+/// Generate a [`#[pallet::storage]`](pallet_macros::storage) alias outside of a pallet.
 ///
-/// Useful for creating a *storage-like* struct for test and migrations.
+/// This storage alias works similarly to the [`#[pallet::storage]`](pallet_macros::storage)
+/// attribute macro. It supports [`StorageValue`](storage::types::StorageValue),
+/// [`StorageMap`](storage::types::StorageMap),
+/// [`StorageDoubleMap`](storage::types::StorageDoubleMap) and
+/// [`StorageNMap`](storage::types::StorageNMap). The main difference to the normal
+/// [`#[pallet::storage]`](pallet_macros::storage) is the flexibility around declaring the
+/// storage prefix to use. The storage prefix determines where to find the value in the
+/// storage. [`#[pallet::storage]`](pallet_macros::storage) uses the name of the pallet as
+/// declared in [`construct_runtime!`].
 ///
-/// ```
-/// # use frame_support::storage_alias;
-/// use frame_support::codec;
-/// use frame_support::Twox64Concat;
-/// // generate a storage value with type u32.
-/// #[storage_alias]
-/// type StorageName = StorageValue<Prefix, u32>;
-///
-/// // generate a double map from `(u32, u32)` (with hashers `Twox64Concat` for each key)
-/// // to `Vec<u8>`
-/// #[storage_alias]
-/// type OtherStorageName = StorageDoubleMap<
-/// 	OtherPrefix,
-/// 	Twox64Concat,
-/// 	u32,
-/// 	Twox64Concat,
-/// 	u32,
-/// 	Vec<u8>,
-/// >;
-///
-/// // optionally specify the query type
-/// use frame_support::pallet_prelude::{ValueQuery, OptionQuery};
-/// #[storage_alias]
-/// type ValueName = StorageValue<Prefix, u32, OptionQuery>;
-/// #[storage_alias]
-/// type SomeStorageName = StorageMap<
-/// 	Prefix,
-/// 	Twox64Concat,
-/// 	u32,
-/// 	Vec<u8>,
-/// 	ValueQuery,
-/// >;
-///
-/// // generate a map from `Config::AccountId` (with hasher `Twox64Concat`) to `Vec<u8>`
-/// trait Config { type AccountId: codec::FullCodec; }
-/// #[storage_alias]
-/// type GenericStorage<T> = StorageMap<Prefix, Twox64Concat, <T as Config>::AccountId, Vec<u8>>;
-///
-/// // It also supports NMap
-/// use frame_support::storage::types::Key as NMapKey;
-///
-/// #[storage_alias]
-/// type SomeNMap = StorageNMap<Prefix, (NMapKey<Twox64Concat, u32>, NMapKey<Twox64Concat, u64>), Vec<u8>>;
-///
-/// // Using pallet name as prefix.
-/// //
-/// // When the first generic argument is taking generic arguments it is expected to be a pallet.
-/// // The prefix will then be the pallet name as configured in the runtime through
-/// // `construct_runtime!`.
-///
-/// # struct Pallet<T: Config, I = ()>(std::marker::PhantomData<(T, I)>);
-/// # impl<T: Config, I: 'static> frame_support::traits::PalletInfoAccess for Pallet<T, I> {
-/// # 	fn index() -> usize { 0 }
-/// # 	fn name() -> &'static str { "pallet" }
-/// # 	fn module_name() -> &'static str { "module" }
-/// # 	fn crate_version() -> frame_support::traits::CrateVersion { unimplemented!() }
-/// # }
-///
-/// #[storage_alias]
-/// type SomeValue<T: Config> = StorageValue<Pallet<T>, u64>;
-///
-/// // Pallet with instance
-///
-/// #[storage_alias]
-/// type SomeValue2<T: Config, I: 'static> = StorageValue<Pallet<T, I>, u64>;
-///
-/// # fn main() {}
-/// ```
+/// The flexibility around declaring the storage prefix makes this macro very useful for
+/// writing migrations etc.
+///
+/// # Examples
+///
+/// There are different ways to declare the `prefix` to use. The `prefix` type can either be
+/// declared explicetly by passing it to the macro as an attribute or by letting the macro
+/// guess on what the `prefix` type is. The `prefix` is always passed as the first generic
+/// argument to the type declaration. When using [`#[pallet::storage]`](pallet_macros::storage)
+/// this first generic argument is always `_`. Besides declaring the `prefix`, the rest of the
+/// type declaration works as with [`#[pallet::storage]`](pallet_macros::storage).
+///
+/// 1. Use the `verbatim` prefix type. This prefix type uses the given identifier as the
+/// `prefix`:
+#[doc = docify::embed!("src/tests/storage_alias.rs", verbatim_attribute)]
+///
+/// 2. Use the `pallet_name` prefix type. This prefix type uses the name of the pallet as
+/// configured in    [`construct_runtime!`] as the `prefix`:
+#[doc = docify::embed!("src/tests/storage_alias.rs", pallet_name_attribute)]
+/// It requires that the given prefix type implements
+/// [`PalletInfoAccess`](traits::PalletInfoAccess) (which is always the case for FRAME pallet
+/// structs). In the example above, `Pallet<T>` is the prefix type.
+///
+/// 3. Use the `dynamic` prefix type. This prefix type calls [`Get::get()`](traits::Get::get)
+///    to get the `prefix`:
+#[doc = docify::embed!("src/tests/storage_alias.rs", dynamic_attribute)]
+/// It requires that the given prefix type implements [`Get<'static str>`](traits::Get).
+///
+/// 4. Let the macro "guess" what kind of prefix type to use. This only supports verbatim or
+///    pallet name. The macro uses the presence of generic arguments to the prefix type as
+///    an indication that it should use the pallet name as the `prefix`:
+#[doc = docify::embed!("src/tests/storage_alias.rs", storage_alias_guess)]
 pub use frame_support_procedural::storage_alias;
 
 pub use frame_support_procedural::derive_impl;
@@ -818,703 +790,6 @@ pub use serde::{Deserialize, Serialize};
 #[cfg(not(no_std))]
 pub use macro_magic;
 
-#[cfg(test)]
-pub mod tests {
-	use super::*;
-	use crate::metadata_ir::{
-		PalletStorageMetadataIR, StorageEntryMetadataIR, StorageEntryModifierIR,
-		StorageEntryTypeIR, StorageHasherIR,
-	};
-	use sp_io::{MultiRemovalResults, TestExternalities};
-	use sp_runtime::{generic, traits::BlakeTwo256, BuildStorage};
-	use sp_std::result;
-
-	pub use self::frame_system::{pallet_prelude::*, Config, Pallet};
-
-	#[pallet]
-	pub mod frame_system {
-		#[allow(unused)]
-		use super::{frame_system, frame_system::pallet_prelude::*};
-		pub use crate::dispatch::RawOrigin;
-		use crate::pallet_prelude::*;
-
-		#[pallet::pallet]
-		pub struct Pallet<T>(_);
-
-		#[pallet::config]
-		#[pallet::disable_frame_system_supertrait_check]
-		pub trait Config: 'static {
-			type Block: Parameter + sp_runtime::traits::Block;
-			type AccountId;
-			type BaseCallFilter: crate::traits::Contains<Self::RuntimeCall>;
-			type RuntimeOrigin;
-			type RuntimeCall;
-			type PalletInfo: crate::traits::PalletInfo;
-			type DbWeight: Get<crate::weights::RuntimeDbWeight>;
-		}
-
-		#[pallet::error]
-		pub enum Error<T> {
-			/// Required by construct_runtime
-			CallFiltered,
-		}
-
-		#[pallet::origin]
-		pub type Origin<T> = RawOrigin<<T as Config>::AccountId>;
-
-		#[pallet::call]
-		impl<T: Config> Pallet<T> {}
-
-		#[pallet::storage]
-		pub type Data<T> = StorageMap<_, Twox64Concat, u32, u64, ValueQuery>;
-
-		#[pallet::storage]
-		pub type OptionLinkedMap<T> = StorageMap<_, Blake2_128Concat, u32, u32, OptionQuery>;
-
-		#[pallet::storage]
-		#[pallet::getter(fn generic_data)]
-		pub type GenericData<T: Config> =
-			StorageMap<_, Identity, BlockNumberFor<T>, BlockNumberFor<T>, ValueQuery>;
-
-		#[pallet::storage]
-		#[pallet::getter(fn generic_data2)]
-		pub type GenericData2<T: Config> =
-			StorageMap<_, Blake2_128Concat, BlockNumberFor<T>, BlockNumberFor<T>, OptionQuery>;
-
-		#[pallet::storage]
-		pub type DataDM<T> =
-			StorageDoubleMap<_, Twox64Concat, u32, Blake2_128Concat, u32, u64, ValueQuery>;
-
-		#[pallet::storage]
-		pub type GenericDataDM<T: Config> = StorageDoubleMap<
-			_,
-			Blake2_128Concat,
-			BlockNumberFor<T>,
-			Identity,
-			BlockNumberFor<T>,
-			BlockNumberFor<T>,
-			ValueQuery,
-		>;
-
-		#[pallet::storage]
-		pub type GenericData2DM<T: Config> = StorageDoubleMap<
-			_,
-			Blake2_128Concat,
-			BlockNumberFor<T>,
-			Twox64Concat,
-			BlockNumberFor<T>,
-			BlockNumberFor<T>,
-			OptionQuery,
-		>;
-
-		#[pallet::storage]
-		#[pallet::unbounded]
-		pub type AppendableDM<T: Config> = StorageDoubleMap<
-			_,
-			Blake2_128Concat,
-			u32,
-			Blake2_128Concat,
-			BlockNumberFor<T>,
-			Vec<u32>,
-			ValueQuery,
-		>;
-
-		#[pallet::genesis_config]
-		pub struct GenesisConfig<T: Config> {
-			pub data: Vec<(u32, u64)>,
-			pub test_config: Vec<(u32, u32, u64)>,
-			#[serde(skip)]
-			pub _config: sp_std::marker::PhantomData<T>,
-		}
-
-		impl<T: Config> Default for GenesisConfig<T> {
-			fn default() -> Self {
-				Self {
-					_config: Default::default(),
-					data: vec![(15u32, 42u64)],
-					test_config: vec![(15u32, 16u32, 42u64)],
-				}
-			}
-		}
-
-		#[pallet::genesis_build]
-		impl<T: Config> BuildGenesisConfig for GenesisConfig<T> {
-			fn build(&self) {
-				for (k, v) in &self.data {
-					<Data<T>>::insert(k, v);
-				}
-				for (k1, k2, v) in &self.test_config {
-					<DataDM<T>>::insert(k1, k2, v);
-				}
-			}
-		}
-
-		pub mod pallet_prelude {
-			pub type OriginFor<T> = <T as super::Config>::RuntimeOrigin;
-
-			pub type HeaderFor<T> =
-				<<T as super::Config>::Block as sp_runtime::traits::HeaderProvider>::HeaderT;
-
-			pub type BlockNumberFor<T> = <HeaderFor<T> as sp_runtime::traits::Header>::Number;
-		}
-	}
-
-	type BlockNumber = u32;
-	type AccountId = u32;
-	type Header = generic::Header<BlockNumber, BlakeTwo256>;
-	type UncheckedExtrinsic = generic::UncheckedExtrinsic<u32, RuntimeCall, (), ()>;
-	type Block = generic::Block<Header, UncheckedExtrinsic>;
-
-	crate::construct_runtime!(
-		pub enum Runtime
-		{
-			System: self::frame_system,
-		}
-	);
-
-	impl Config for Runtime {
-		type Block = Block;
-		type AccountId = AccountId;
-		type BaseCallFilter = crate::traits::Everything;
-		type RuntimeOrigin = RuntimeOrigin;
-		type RuntimeCall = RuntimeCall;
-		type PalletInfo = PalletInfo;
-		type DbWeight = ();
-	}
-
-	fn new_test_ext() -> TestExternalities {
-		RuntimeGenesisConfig::default().build_storage().unwrap().into()
-	}
-
-	trait Sorted {
-		fn sorted(self) -> Self;
-	}
-
-	impl<T: Ord> Sorted for Vec<T> {
-		fn sorted(mut self) -> Self {
-			self.sort();
-			self
-		}
-	}
-
-	#[test]
-	fn storage_alias_works() {
-		new_test_ext().execute_with(|| {
-			#[crate::storage_alias]
-			type GenericData2<T> =
-				StorageMap<System, Blake2_128Concat, BlockNumberFor<T>, BlockNumberFor<T>>;
-
-			assert_eq!(Pallet::<Runtime>::generic_data2(5), None);
-			GenericData2::<Runtime>::insert(5, 5);
-			assert_eq!(Pallet::<Runtime>::generic_data2(5), Some(5));
-
-			/// Some random docs that ensure that docs are accepted
-			#[crate::storage_alias]
-			pub type GenericData<T> =
-				StorageMap<Test2, Blake2_128Concat, BlockNumberFor<T>, BlockNumberFor<T>>;
-		});
-	}
-
-	#[test]
-	fn storage_value_mutate_exists_should_work() {
-		new_test_ext().execute_with(|| {
-			#[crate::storage_alias]
-			pub type Value = StorageValue<Test, u32>;
-
-			assert!(!Value::exists());
-
-			Value::mutate_exists(|v| *v = Some(1));
-			assert!(Value::exists());
-			assert_eq!(Value::get(), Some(1));
-
-			// removed if mutated to `None`
-			Value::mutate_exists(|v| *v = None);
-			assert!(!Value::exists());
-		});
-	}
-
-	#[test]
-	fn storage_value_try_mutate_exists_should_work() {
-		new_test_ext().execute_with(|| {
-			#[crate::storage_alias]
-			pub type Value = StorageValue<Test, u32>;
-
-			type TestResult = result::Result<(), &'static str>;
-
-			assert!(!Value::exists());
-
-			// mutated if `Ok`
-			assert_ok!(Value::try_mutate_exists(|v| -> TestResult {
-				*v = Some(1);
-				Ok(())
-			}));
-			assert!(Value::exists());
-			assert_eq!(Value::get(), Some(1));
-
-			// no-op if `Err`
-			assert_noop!(
-				Value::try_mutate_exists(|v| -> TestResult {
-					*v = Some(2);
-					Err("nah")
-				}),
-				"nah"
-			);
-			assert_eq!(Value::get(), Some(1));
-
-			// removed if mutated to`None`
-			assert_ok!(Value::try_mutate_exists(|v| -> TestResult {
-				*v = None;
-				Ok(())
-			}));
-			assert!(!Value::exists());
-		});
-	}
-
-	#[test]
-	fn map_issue_3318() {
-		new_test_ext().execute_with(|| {
-			type OptionLinkedMap = self::frame_system::OptionLinkedMap<Runtime>;
-
-			OptionLinkedMap::insert(1, 1);
-			assert_eq!(OptionLinkedMap::get(1), Some(1));
-			OptionLinkedMap::insert(1, 2);
-			assert_eq!(OptionLinkedMap::get(1), Some(2));
-		});
-	}
-
-	#[test]
-	fn map_swap_works() {
-		new_test_ext().execute_with(|| {
-			type OptionLinkedMap = self::frame_system::OptionLinkedMap<Runtime>;
-
-			OptionLinkedMap::insert(0, 0);
-			OptionLinkedMap::insert(1, 1);
-			OptionLinkedMap::insert(2, 2);
-			OptionLinkedMap::insert(3, 3);
-
-			let collect = || OptionLinkedMap::iter().collect::<Vec<_>>().sorted();
-			assert_eq!(collect(), vec![(0, 0), (1, 1), (2, 2), (3, 3)]);
-
-			// Two existing
-			OptionLinkedMap::swap(1, 2);
-			assert_eq!(collect(), vec![(0, 0), (1, 2), (2, 1), (3, 3)]);
-
-			// Back to normal
-			OptionLinkedMap::swap(2, 1);
-			assert_eq!(collect(), vec![(0, 0), (1, 1), (2, 2), (3, 3)]);
-
-			// Left existing
-			OptionLinkedMap::swap(2, 5);
-			assert_eq!(collect(), vec![(0, 0), (1, 1), (3, 3), (5, 2)]);
-
-			// Right existing
-			OptionLinkedMap::swap(5, 2);
-			assert_eq!(collect(), vec![(0, 0), (1, 1), (2, 2), (3, 3)]);
-		});
-	}
-
-	#[test]
-	fn double_map_swap_works() {
-		new_test_ext().execute_with(|| {
-			type DataDM = self::frame_system::DataDM<Runtime>;
-
-			DataDM::insert(0, 1, 1);
-			DataDM::insert(1, 0, 2);
-			DataDM::insert(1, 1, 3);
-
-			let get_all = || {
-				vec![
-					DataDM::get(0, 1),
-					DataDM::get(1, 0),
-					DataDM::get(1, 1),
-					DataDM::get(2, 0),
-					DataDM::get(2, 1),
-				]
-			};
-			assert_eq!(get_all(), vec![1, 2, 3, 0, 0]);
-
-			// Two existing
-			DataDM::swap(0, 1, 1, 0);
-			assert_eq!(get_all(), vec![2, 1, 3, 0, 0]);
-
-			// Left existing
-			DataDM::swap(1, 0, 2, 0);
-			assert_eq!(get_all(), vec![2, 0, 3, 1, 0]);
-
-			// Right existing
-			DataDM::swap(2, 1, 1, 1);
-			assert_eq!(get_all(), vec![2, 0, 0, 1, 3]);
-		});
-	}
-
-	#[test]
-	fn map_basic_insert_remove_should_work() {
-		new_test_ext().execute_with(|| {
-			type Map = self::frame_system::Data<Runtime>;
-
-			// initialized during genesis
-			assert_eq!(Map::get(&15u32), 42u64);
-
-			// get / insert / take
-			let key = 17u32;
-			assert_eq!(Map::get(&key), 0u64);
-			Map::insert(key, 4u64);
-			assert_eq!(Map::get(&key), 4u64);
-			assert_eq!(Map::take(&key), 4u64);
-			assert_eq!(Map::get(&key), 0u64);
-
-			// mutate
-			Map::mutate(&key, |val| {
-				*val = 15;
-			});
-			assert_eq!(Map::get(&key), 15u64);
-
-			// remove
-			Map::remove(&key);
-			assert_eq!(Map::get(&key), 0u64);
-		});
-	}
-
-	#[test]
-	fn map_iteration_should_work() {
-		new_test_ext().execute_with(|| {
-			type Map = self::frame_system::Data<Runtime>;
-
-			assert_eq!(Map::iter().collect::<Vec<_>>().sorted(), vec![(15, 42)]);
-			// insert / remove
-			let key = 17u32;
-			Map::insert(key, 4u64);
-			assert_eq!(Map::iter().collect::<Vec<_>>().sorted(), vec![(15, 42), (key, 4)]);
-			assert_eq!(Map::take(&15), 42u64);
-			assert_eq!(Map::take(&key), 4u64);
-			assert_eq!(Map::iter().collect::<Vec<_>>().sorted(), vec![]);
-
-			// Add couple of more elements
-			Map::insert(key, 42u64);
-			assert_eq!(Map::iter().collect::<Vec<_>>().sorted(), vec![(key, 42)]);
-			Map::insert(key + 1, 43u64);
-			assert_eq!(Map::iter().collect::<Vec<_>>().sorted(), vec![(key, 42), (key + 1, 43)]);
-
-			// mutate
-			let key = key + 2;
-			Map::mutate(&key, |val| {
-				*val = 15;
-			});
-			assert_eq!(
-				Map::iter().collect::<Vec<_>>().sorted(),
-				vec![(key - 2, 42), (key - 1, 43), (key, 15)]
-			);
-			Map::mutate(&key, |val| {
-				*val = 17;
-			});
-			assert_eq!(
-				Map::iter().collect::<Vec<_>>().sorted(),
-				vec![(key - 2, 42), (key - 1, 43), (key, 17)]
-			);
-
-			// remove first
-			Map::remove(&key);
-			assert_eq!(
-				Map::iter().collect::<Vec<_>>().sorted(),
-				vec![(key - 2, 42), (key - 1, 43)]
-			);
-
-			// remove last from the list
-			Map::remove(&(key - 2));
-			assert_eq!(Map::iter().collect::<Vec<_>>().sorted(), vec![(key - 1, 43)]);
-
-			// remove the last element
-			Map::remove(&(key - 1));
-			assert_eq!(Map::iter().collect::<Vec<_>>().sorted(), vec![]);
-		});
-	}
-
-	#[test]
-	fn double_map_basic_insert_remove_remove_prefix_with_commit_should_work() {
-		let key1 = 17u32;
-		let key2 = 18u32;
-		type DoubleMap = self::frame_system::DataDM<Runtime>;
-		let mut e = new_test_ext();
-		e.execute_with(|| {
-			// initialized during genesis
-			assert_eq!(DoubleMap::get(&15u32, &16u32), 42u64);
-
-			// get / insert / take
-			assert_eq!(DoubleMap::get(&key1, &key2), 0u64);
-			DoubleMap::insert(&key1, &key2, &4u64);
-			assert_eq!(DoubleMap::get(&key1, &key2), 4u64);
-			assert_eq!(DoubleMap::take(&key1, &key2), 4u64);
-			assert_eq!(DoubleMap::get(&key1, &key2), 0u64);
-
-			// mutate
-			DoubleMap::mutate(&key1, &key2, |val| *val = 15);
-			assert_eq!(DoubleMap::get(&key1, &key2), 15u64);
-
-			// remove
-			DoubleMap::remove(&key1, &key2);
-			assert_eq!(DoubleMap::get(&key1, &key2), 0u64);
-
-			// remove prefix
-			DoubleMap::insert(&key1, &key2, &4u64);
-			DoubleMap::insert(&key1, &(key2 + 1), &4u64);
-			DoubleMap::insert(&(key1 + 1), &key2, &4u64);
-			DoubleMap::insert(&(key1 + 1), &(key2 + 1), &4u64);
-		});
-		e.commit_all().unwrap();
-		e.execute_with(|| {
-			assert!(matches!(
-				DoubleMap::clear_prefix(&key1, u32::max_value(), None),
-				MultiRemovalResults { maybe_cursor: None, backend: 2, unique: 2, loops: 2 }
-			));
-			assert_eq!(DoubleMap::get(&key1, &key2), 0u64);
-			assert_eq!(DoubleMap::get(&key1, &(key2 + 1)), 0u64);
-			assert_eq!(DoubleMap::get(&(key1 + 1), &key2), 4u64);
-			assert_eq!(DoubleMap::get(&(key1 + 1), &(key2 + 1)), 4u64);
-		});
-	}
-
-	#[test]
-	fn double_map_basic_insert_remove_remove_prefix_should_work() {
-		new_test_ext().execute_with(|| {
-			let key1 = 17u32;
-			let key2 = 18u32;
-			type DoubleMap = self::frame_system::DataDM<Runtime>;
-
-			// initialized during genesis
-			assert_eq!(DoubleMap::get(&15u32, &16u32), 42u64);
-
-			// get / insert / take
-			assert_eq!(DoubleMap::get(&key1, &key2), 0u64);
-			DoubleMap::insert(&key1, &key2, &4u64);
-			assert_eq!(DoubleMap::get(&key1, &key2), 4u64);
-			assert_eq!(DoubleMap::take(&key1, &key2), 4u64);
-			assert_eq!(DoubleMap::get(&key1, &key2), 0u64);
-
-			// mutate
-			DoubleMap::mutate(&key1, &key2, |val| *val = 15);
-			assert_eq!(DoubleMap::get(&key1, &key2), 15u64);
-
-			// remove
-			DoubleMap::remove(&key1, &key2);
-			assert_eq!(DoubleMap::get(&key1, &key2), 0u64);
-
-			// remove prefix
-			DoubleMap::insert(&key1, &key2, &4u64);
-			DoubleMap::insert(&key1, &(key2 + 1), &4u64);
-			DoubleMap::insert(&(key1 + 1), &key2, &4u64);
-			DoubleMap::insert(&(key1 + 1), &(key2 + 1), &4u64);
-			// all in overlay
-			assert!(matches!(
-				DoubleMap::clear_prefix(&key1, u32::max_value(), None),
-				MultiRemovalResults { maybe_cursor: None, backend: 0, unique: 0, loops: 0 }
-			));
-			// Note this is the incorrect answer (for now), since we are using v2 of
-			// `clear_prefix`.
-			// When we switch to v3, then this will become:
-			//   MultiRemovalResults:: { maybe_cursor: None, backend: 0, unique: 2, loops: 2 },
-			assert!(matches!(
-				DoubleMap::clear_prefix(&key1, u32::max_value(), None),
-				MultiRemovalResults { maybe_cursor: None, backend: 0, unique: 0, loops: 0 }
-			));
-			assert_eq!(DoubleMap::get(&key1, &key2), 0u64);
-			assert_eq!(DoubleMap::get(&key1, &(key2 + 1)), 0u64);
-			assert_eq!(DoubleMap::get(&(key1 + 1), &key2), 4u64);
-			assert_eq!(DoubleMap::get(&(key1 + 1), &(key2 + 1)), 4u64);
-		});
-	}
-
-	#[test]
-	fn double_map_append_should_work() {
-		new_test_ext().execute_with(|| {
-			type DoubleMap = self::frame_system::AppendableDM<Runtime>;
-
-			let key1 = 17u32;
-			let key2 = 18u32;
-
-			DoubleMap::insert(&key1, &key2, &vec![1]);
-			DoubleMap::append(&key1, &key2, 2);
-			assert_eq!(DoubleMap::get(&key1, &key2), &[1, 2]);
-		});
-	}
-
-	#[test]
-	fn double_map_mutate_exists_should_work() {
-		new_test_ext().execute_with(|| {
-			type DoubleMap = self::frame_system::DataDM<Runtime>;
-
-			let (key1, key2) = (11, 13);
-
-			// mutated
-			DoubleMap::mutate_exists(key1, key2, |v| *v = Some(1));
-			assert_eq!(DoubleMap::get(&key1, key2), 1);
-
-			// removed if mutated to `None`
-			DoubleMap::mutate_exists(key1, key2, |v| *v = None);
-			assert!(!DoubleMap::contains_key(&key1, key2));
-		});
-	}
-
-	#[test]
-	fn double_map_try_mutate_exists_should_work() {
-		new_test_ext().execute_with(|| {
-			type DoubleMap = self::frame_system::DataDM<Runtime>;
-			type TestResult = Result<(), &'static str>;
-
-			let (key1, key2) = (11, 13);
-
-			// mutated if `Ok`
-			assert_ok!(DoubleMap::try_mutate_exists(key1, key2, |v| -> TestResult {
-				*v = Some(1);
-				Ok(())
-			}));
-			assert_eq!(DoubleMap::get(&key1, key2), 1);
-
-			// no-op if `Err`
-			assert_noop!(
-				DoubleMap::try_mutate_exists(key1, key2, |v| -> TestResult {
-					*v = Some(2);
-					Err("nah")
-				}),
-				"nah"
-			);
-
-			// removed if mutated to`None`
-			assert_ok!(DoubleMap::try_mutate_exists(key1, key2, |v| -> TestResult {
-				*v = None;
-				Ok(())
-			}));
-			assert!(!DoubleMap::contains_key(&key1, key2));
-		});
-	}
-
-	fn expected_metadata() -> PalletStorageMetadataIR {
-		PalletStorageMetadataIR {
-			prefix: "System",
-			entries: vec![
-				StorageEntryMetadataIR {
-					name: "Data",
-					modifier: StorageEntryModifierIR::Default,
-					ty: StorageEntryTypeIR::Map {
-						hashers: vec![StorageHasherIR::Twox64Concat],
-						key: scale_info::meta_type::<u32>(),
-						value: scale_info::meta_type::<u64>(),
-					},
-					default: vec![0, 0, 0, 0, 0, 0, 0, 0],
-					docs: vec![],
-				},
-				StorageEntryMetadataIR {
-					name: "OptionLinkedMap",
-					modifier: StorageEntryModifierIR::Optional,
-					ty: StorageEntryTypeIR::Map {
-						hashers: vec![StorageHasherIR::Blake2_128Concat],
-						key: scale_info::meta_type::<u32>(),
-						value: scale_info::meta_type::<u32>(),
-					},
-					default: vec![0],
-					docs: vec![],
-				},
-				StorageEntryMetadataIR {
-					name: "GenericData",
-					modifier: StorageEntryModifierIR::Default,
-					ty: StorageEntryTypeIR::Map {
-						hashers: vec![StorageHasherIR::Identity],
-						key: scale_info::meta_type::<u32>(),
-						value: scale_info::meta_type::<u32>(),
-					},
-					default: vec![0, 0, 0, 0],
-					docs: vec![],
-				},
-				StorageEntryMetadataIR {
-					name: "GenericData2",
-					modifier: StorageEntryModifierIR::Optional,
-					ty: StorageEntryTypeIR::Map {
-						hashers: vec![StorageHasherIR::Blake2_128Concat],
-						key: scale_info::meta_type::<u32>(),
-						value: scale_info::meta_type::<u32>(),
-					},
-					default: vec![0],
-					docs: vec![],
-				},
-				StorageEntryMetadataIR {
-					name: "DataDM",
-					modifier: StorageEntryModifierIR::Default,
-					ty: StorageEntryTypeIR::Map {
-						hashers: vec![
-							StorageHasherIR::Twox64Concat,
-							StorageHasherIR::Blake2_128Concat,
-						],
-						key: scale_info::meta_type::<(u32, u32)>(),
-						value: scale_info::meta_type::<u64>(),
-					},
-					default: vec![0, 0, 0, 0, 0, 0, 0, 0],
-					docs: vec![],
-				},
-				StorageEntryMetadataIR {
-					name: "GenericDataDM",
-					modifier: StorageEntryModifierIR::Default,
-					ty: StorageEntryTypeIR::Map {
-						hashers: vec![StorageHasherIR::Blake2_128Concat, StorageHasherIR::Identity],
-						key: scale_info::meta_type::<(u32, u32)>(),
-						value: scale_info::meta_type::<u32>(),
-					},
-					default: vec![0, 0, 0, 0],
-					docs: vec![],
-				},
-				StorageEntryMetadataIR {
-					name: "GenericData2DM",
-					modifier: StorageEntryModifierIR::Optional,
-					ty: StorageEntryTypeIR::Map {
-						hashers: vec![
-							StorageHasherIR::Blake2_128Concat,
-							StorageHasherIR::Twox64Concat,
-						],
-						key: scale_info::meta_type::<(u32, u32)>(),
-						value: scale_info::meta_type::<u32>(),
-					},
-					default: vec![0],
-					docs: vec![],
-				},
-				StorageEntryMetadataIR {
-					name: "AppendableDM",
-					modifier: StorageEntryModifierIR::Default,
-					ty: StorageEntryTypeIR::Map {
-						hashers: vec![
-							StorageHasherIR::Blake2_128Concat,
-							StorageHasherIR::Blake2_128Concat,
-						],
-						key: scale_info::meta_type::<(u32, u32)>(),
-						value: scale_info::meta_type::<Vec<u32>>(),
-					},
-					default: vec![0],
-					docs: vec![],
-				},
-			],
-		}
-	}
-
-	#[test]
-	fn store_metadata() {
-		let metadata = Pallet::<Runtime>::storage_metadata();
-		pretty_assertions::assert_eq!(expected_metadata(), metadata);
-	}
-
-	parameter_types! {
-		storage StorageParameter: u64 = 10;
-	}
-
-	#[test]
-	fn check_storage_parameter_type_works() {
-		TestExternalities::default().execute_with(|| {
-			assert_eq!(sp_io::hashing::twox_128(b":StorageParameter:"), StorageParameter::key());
-
-			assert_eq!(10, StorageParameter::get());
-
-			StorageParameter::set(&300);
-			assert_eq!(300, StorageParameter::get());
-		})
-	}
-}
-
 /// Private module re-exporting items used by frame support macros.
 #[doc(hidden)]
 pub mod _private {
diff --git a/substrate/frame/support/src/tests/mod.rs b/substrate/frame/support/src/tests/mod.rs
new file mode 100644
index 0000000000000000000000000000000000000000..cb4b4e82418b166a159340ef7878bf13fc8f4b8c
--- /dev/null
+++ b/substrate/frame/support/src/tests/mod.rs
@@ -0,0 +1,629 @@
+// This file is part of Substrate.
+
+// Copyright (C) Parity Technologies (UK) Ltd.
+// SPDX-License-Identifier: Apache-2.0
+
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// 	http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+use super::*;
+use crate::metadata_ir::{
+	PalletStorageMetadataIR, StorageEntryMetadataIR, StorageEntryModifierIR, StorageEntryTypeIR,
+	StorageHasherIR,
+};
+use sp_io::{MultiRemovalResults, TestExternalities};
+use sp_runtime::{generic, traits::BlakeTwo256, BuildStorage};
+
+pub use self::frame_system::{pallet_prelude::*, Config, Pallet};
+
+mod storage_alias;
+
+#[pallet]
+pub mod frame_system {
+	#[allow(unused)]
+	use super::{frame_system, frame_system::pallet_prelude::*};
+	pub use crate::dispatch::RawOrigin;
+	use crate::pallet_prelude::*;
+
+	#[pallet::pallet]
+	pub struct Pallet<T>(_);
+
+	#[pallet::config]
+	#[pallet::disable_frame_system_supertrait_check]
+	pub trait Config: 'static {
+		type Block: Parameter + sp_runtime::traits::Block;
+		type AccountId;
+		type BaseCallFilter: crate::traits::Contains<Self::RuntimeCall>;
+		type RuntimeOrigin;
+		type RuntimeCall;
+		type PalletInfo: crate::traits::PalletInfo;
+		type DbWeight: Get<crate::weights::RuntimeDbWeight>;
+	}
+
+	#[pallet::error]
+	pub enum Error<T> {
+		/// Required by construct_runtime
+		CallFiltered,
+	}
+
+	#[pallet::origin]
+	pub type Origin<T> = RawOrigin<<T as Config>::AccountId>;
+
+	#[pallet::call]
+	impl<T: Config> Pallet<T> {}
+
+	#[pallet::storage]
+	pub type Data<T> = StorageMap<_, Twox64Concat, u32, u64, ValueQuery>;
+
+	#[pallet::storage]
+	pub type OptionLinkedMap<T> = StorageMap<_, Blake2_128Concat, u32, u32, OptionQuery>;
+
+	#[pallet::storage]
+	#[pallet::getter(fn generic_data)]
+	pub type GenericData<T: Config> =
+		StorageMap<_, Identity, BlockNumberFor<T>, BlockNumberFor<T>, ValueQuery>;
+
+	#[pallet::storage]
+	#[pallet::getter(fn generic_data2)]
+	pub type GenericData2<T: Config> =
+		StorageMap<_, Blake2_128Concat, BlockNumberFor<T>, BlockNumberFor<T>, OptionQuery>;
+
+	#[pallet::storage]
+	pub type DataDM<T> =
+		StorageDoubleMap<_, Twox64Concat, u32, Blake2_128Concat, u32, u64, ValueQuery>;
+
+	#[pallet::storage]
+	pub type GenericDataDM<T: Config> = StorageDoubleMap<
+		_,
+		Blake2_128Concat,
+		BlockNumberFor<T>,
+		Identity,
+		BlockNumberFor<T>,
+		BlockNumberFor<T>,
+		ValueQuery,
+	>;
+
+	#[pallet::storage]
+	pub type GenericData2DM<T: Config> = StorageDoubleMap<
+		_,
+		Blake2_128Concat,
+		BlockNumberFor<T>,
+		Twox64Concat,
+		BlockNumberFor<T>,
+		BlockNumberFor<T>,
+		OptionQuery,
+	>;
+
+	#[pallet::storage]
+	#[pallet::unbounded]
+	pub type AppendableDM<T: Config> = StorageDoubleMap<
+		_,
+		Blake2_128Concat,
+		u32,
+		Blake2_128Concat,
+		BlockNumberFor<T>,
+		Vec<u32>,
+		ValueQuery,
+	>;
+
+	#[pallet::genesis_config]
+	pub struct GenesisConfig<T: Config> {
+		pub data: Vec<(u32, u64)>,
+		pub test_config: Vec<(u32, u32, u64)>,
+		#[serde(skip)]
+		pub _config: sp_std::marker::PhantomData<T>,
+	}
+
+	impl<T: Config> Default for GenesisConfig<T> {
+		fn default() -> Self {
+			Self {
+				_config: Default::default(),
+				data: vec![(15u32, 42u64)],
+				test_config: vec![(15u32, 16u32, 42u64)],
+			}
+		}
+	}
+
+	#[pallet::genesis_build]
+	impl<T: Config> BuildGenesisConfig for GenesisConfig<T> {
+		fn build(&self) {
+			for (k, v) in &self.data {
+				<Data<T>>::insert(k, v);
+			}
+			for (k1, k2, v) in &self.test_config {
+				<DataDM<T>>::insert(k1, k2, v);
+			}
+		}
+	}
+
+	pub mod pallet_prelude {
+		pub type OriginFor<T> = <T as super::Config>::RuntimeOrigin;
+
+		pub type HeaderFor<T> =
+			<<T as super::Config>::Block as sp_runtime::traits::HeaderProvider>::HeaderT;
+
+		pub type BlockNumberFor<T> = <HeaderFor<T> as sp_runtime::traits::Header>::Number;
+	}
+}
+
+type BlockNumber = u32;
+type AccountId = u32;
+type Header = generic::Header<BlockNumber, BlakeTwo256>;
+type UncheckedExtrinsic = generic::UncheckedExtrinsic<u32, RuntimeCall, (), ()>;
+type Block = generic::Block<Header, UncheckedExtrinsic>;
+
+crate::construct_runtime!(
+	pub enum Runtime
+	{
+		System: self::frame_system,
+	}
+);
+
+impl Config for Runtime {
+	type Block = Block;
+	type AccountId = AccountId;
+	type BaseCallFilter = crate::traits::Everything;
+	type RuntimeOrigin = RuntimeOrigin;
+	type RuntimeCall = RuntimeCall;
+	type PalletInfo = PalletInfo;
+	type DbWeight = ();
+}
+
+fn new_test_ext() -> TestExternalities {
+	RuntimeGenesisConfig::default().build_storage().unwrap().into()
+}
+
+trait Sorted {
+	fn sorted(self) -> Self;
+}
+
+impl<T: Ord> Sorted for Vec<T> {
+	fn sorted(mut self) -> Self {
+		self.sort();
+		self
+	}
+}
+
+#[test]
+fn map_issue_3318() {
+	new_test_ext().execute_with(|| {
+		type OptionLinkedMap = self::frame_system::OptionLinkedMap<Runtime>;
+
+		OptionLinkedMap::insert(1, 1);
+		assert_eq!(OptionLinkedMap::get(1), Some(1));
+		OptionLinkedMap::insert(1, 2);
+		assert_eq!(OptionLinkedMap::get(1), Some(2));
+	});
+}
+
+#[test]
+fn map_swap_works() {
+	new_test_ext().execute_with(|| {
+		type OptionLinkedMap = self::frame_system::OptionLinkedMap<Runtime>;
+
+		OptionLinkedMap::insert(0, 0);
+		OptionLinkedMap::insert(1, 1);
+		OptionLinkedMap::insert(2, 2);
+		OptionLinkedMap::insert(3, 3);
+
+		let collect = || OptionLinkedMap::iter().collect::<Vec<_>>().sorted();
+		assert_eq!(collect(), vec![(0, 0), (1, 1), (2, 2), (3, 3)]);
+
+		// Two existing
+		OptionLinkedMap::swap(1, 2);
+		assert_eq!(collect(), vec![(0, 0), (1, 2), (2, 1), (3, 3)]);
+
+		// Back to normal
+		OptionLinkedMap::swap(2, 1);
+		assert_eq!(collect(), vec![(0, 0), (1, 1), (2, 2), (3, 3)]);
+
+		// Left existing
+		OptionLinkedMap::swap(2, 5);
+		assert_eq!(collect(), vec![(0, 0), (1, 1), (3, 3), (5, 2)]);
+
+		// Right existing
+		OptionLinkedMap::swap(5, 2);
+		assert_eq!(collect(), vec![(0, 0), (1, 1), (2, 2), (3, 3)]);
+	});
+}
+
+#[test]
+fn double_map_swap_works() {
+	new_test_ext().execute_with(|| {
+		type DataDM = self::frame_system::DataDM<Runtime>;
+
+		DataDM::insert(0, 1, 1);
+		DataDM::insert(1, 0, 2);
+		DataDM::insert(1, 1, 3);
+
+		let get_all = || {
+			vec![
+				DataDM::get(0, 1),
+				DataDM::get(1, 0),
+				DataDM::get(1, 1),
+				DataDM::get(2, 0),
+				DataDM::get(2, 1),
+			]
+		};
+		assert_eq!(get_all(), vec![1, 2, 3, 0, 0]);
+
+		// Two existing
+		DataDM::swap(0, 1, 1, 0);
+		assert_eq!(get_all(), vec![2, 1, 3, 0, 0]);
+
+		// Left existing
+		DataDM::swap(1, 0, 2, 0);
+		assert_eq!(get_all(), vec![2, 0, 3, 1, 0]);
+
+		// Right existing
+		DataDM::swap(2, 1, 1, 1);
+		assert_eq!(get_all(), vec![2, 0, 0, 1, 3]);
+	});
+}
+
+#[test]
+fn map_basic_insert_remove_should_work() {
+	new_test_ext().execute_with(|| {
+		type Map = self::frame_system::Data<Runtime>;
+
+		// initialized during genesis
+		assert_eq!(Map::get(&15u32), 42u64);
+
+		// get / insert / take
+		let key = 17u32;
+		assert_eq!(Map::get(&key), 0u64);
+		Map::insert(key, 4u64);
+		assert_eq!(Map::get(&key), 4u64);
+		assert_eq!(Map::take(&key), 4u64);
+		assert_eq!(Map::get(&key), 0u64);
+
+		// mutate
+		Map::mutate(&key, |val| {
+			*val = 15;
+		});
+		assert_eq!(Map::get(&key), 15u64);
+
+		// remove
+		Map::remove(&key);
+		assert_eq!(Map::get(&key), 0u64);
+	});
+}
+
+#[test]
+fn map_iteration_should_work() {
+	new_test_ext().execute_with(|| {
+		type Map = self::frame_system::Data<Runtime>;
+
+		assert_eq!(Map::iter().collect::<Vec<_>>().sorted(), vec![(15, 42)]);
+		// insert / remove
+		let key = 17u32;
+		Map::insert(key, 4u64);
+		assert_eq!(Map::iter().collect::<Vec<_>>().sorted(), vec![(15, 42), (key, 4)]);
+		assert_eq!(Map::take(&15), 42u64);
+		assert_eq!(Map::take(&key), 4u64);
+		assert_eq!(Map::iter().collect::<Vec<_>>().sorted(), vec![]);
+
+		// Add couple of more elements
+		Map::insert(key, 42u64);
+		assert_eq!(Map::iter().collect::<Vec<_>>().sorted(), vec![(key, 42)]);
+		Map::insert(key + 1, 43u64);
+		assert_eq!(Map::iter().collect::<Vec<_>>().sorted(), vec![(key, 42), (key + 1, 43)]);
+
+		// mutate
+		let key = key + 2;
+		Map::mutate(&key, |val| {
+			*val = 15;
+		});
+		assert_eq!(
+			Map::iter().collect::<Vec<_>>().sorted(),
+			vec![(key - 2, 42), (key - 1, 43), (key, 15)]
+		);
+		Map::mutate(&key, |val| {
+			*val = 17;
+		});
+		assert_eq!(
+			Map::iter().collect::<Vec<_>>().sorted(),
+			vec![(key - 2, 42), (key - 1, 43), (key, 17)]
+		);
+
+		// remove first
+		Map::remove(&key);
+		assert_eq!(Map::iter().collect::<Vec<_>>().sorted(), vec![(key - 2, 42), (key - 1, 43)]);
+
+		// remove last from the list
+		Map::remove(&(key - 2));
+		assert_eq!(Map::iter().collect::<Vec<_>>().sorted(), vec![(key - 1, 43)]);
+
+		// remove the last element
+		Map::remove(&(key - 1));
+		assert_eq!(Map::iter().collect::<Vec<_>>().sorted(), vec![]);
+	});
+}
+
+#[test]
+fn double_map_basic_insert_remove_remove_prefix_with_commit_should_work() {
+	let key1 = 17u32;
+	let key2 = 18u32;
+	type DoubleMap = self::frame_system::DataDM<Runtime>;
+	let mut e = new_test_ext();
+	e.execute_with(|| {
+		// initialized during genesis
+		assert_eq!(DoubleMap::get(&15u32, &16u32), 42u64);
+
+		// get / insert / take
+		assert_eq!(DoubleMap::get(&key1, &key2), 0u64);
+		DoubleMap::insert(&key1, &key2, &4u64);
+		assert_eq!(DoubleMap::get(&key1, &key2), 4u64);
+		assert_eq!(DoubleMap::take(&key1, &key2), 4u64);
+		assert_eq!(DoubleMap::get(&key1, &key2), 0u64);
+
+		// mutate
+		DoubleMap::mutate(&key1, &key2, |val| *val = 15);
+		assert_eq!(DoubleMap::get(&key1, &key2), 15u64);
+
+		// remove
+		DoubleMap::remove(&key1, &key2);
+		assert_eq!(DoubleMap::get(&key1, &key2), 0u64);
+
+		// remove prefix
+		DoubleMap::insert(&key1, &key2, &4u64);
+		DoubleMap::insert(&key1, &(key2 + 1), &4u64);
+		DoubleMap::insert(&(key1 + 1), &key2, &4u64);
+		DoubleMap::insert(&(key1 + 1), &(key2 + 1), &4u64);
+	});
+	e.commit_all().unwrap();
+	e.execute_with(|| {
+		assert!(matches!(
+			DoubleMap::clear_prefix(&key1, u32::max_value(), None),
+			MultiRemovalResults { maybe_cursor: None, backend: 2, unique: 2, loops: 2 }
+		));
+		assert_eq!(DoubleMap::get(&key1, &key2), 0u64);
+		assert_eq!(DoubleMap::get(&key1, &(key2 + 1)), 0u64);
+		assert_eq!(DoubleMap::get(&(key1 + 1), &key2), 4u64);
+		assert_eq!(DoubleMap::get(&(key1 + 1), &(key2 + 1)), 4u64);
+	});
+}
+
+#[test]
+fn double_map_basic_insert_remove_remove_prefix_should_work() {
+	new_test_ext().execute_with(|| {
+		let key1 = 17u32;
+		let key2 = 18u32;
+		type DoubleMap = self::frame_system::DataDM<Runtime>;
+
+		// initialized during genesis
+		assert_eq!(DoubleMap::get(&15u32, &16u32), 42u64);
+
+		// get / insert / take
+		assert_eq!(DoubleMap::get(&key1, &key2), 0u64);
+		DoubleMap::insert(&key1, &key2, &4u64);
+		assert_eq!(DoubleMap::get(&key1, &key2), 4u64);
+		assert_eq!(DoubleMap::take(&key1, &key2), 4u64);
+		assert_eq!(DoubleMap::get(&key1, &key2), 0u64);
+
+		// mutate
+		DoubleMap::mutate(&key1, &key2, |val| *val = 15);
+		assert_eq!(DoubleMap::get(&key1, &key2), 15u64);
+
+		// remove
+		DoubleMap::remove(&key1, &key2);
+		assert_eq!(DoubleMap::get(&key1, &key2), 0u64);
+
+		// remove prefix
+		DoubleMap::insert(&key1, &key2, &4u64);
+		DoubleMap::insert(&key1, &(key2 + 1), &4u64);
+		DoubleMap::insert(&(key1 + 1), &key2, &4u64);
+		DoubleMap::insert(&(key1 + 1), &(key2 + 1), &4u64);
+		// all in overlay
+		assert!(matches!(
+			DoubleMap::clear_prefix(&key1, u32::max_value(), None),
+			MultiRemovalResults { maybe_cursor: None, backend: 0, unique: 0, loops: 0 }
+		));
+		// Note this is the incorrect answer (for now), since we are using v2 of
+		// `clear_prefix`.
+		// When we switch to v3, then this will become:
+		//   MultiRemovalResults:: { maybe_cursor: None, backend: 0, unique: 2, loops: 2 },
+		assert!(matches!(
+			DoubleMap::clear_prefix(&key1, u32::max_value(), None),
+			MultiRemovalResults { maybe_cursor: None, backend: 0, unique: 0, loops: 0 }
+		));
+		assert_eq!(DoubleMap::get(&key1, &key2), 0u64);
+		assert_eq!(DoubleMap::get(&key1, &(key2 + 1)), 0u64);
+		assert_eq!(DoubleMap::get(&(key1 + 1), &key2), 4u64);
+		assert_eq!(DoubleMap::get(&(key1 + 1), &(key2 + 1)), 4u64);
+	});
+}
+
+#[test]
+fn double_map_append_should_work() {
+	new_test_ext().execute_with(|| {
+		type DoubleMap = self::frame_system::AppendableDM<Runtime>;
+
+		let key1 = 17u32;
+		let key2 = 18u32;
+
+		DoubleMap::insert(&key1, &key2, &vec![1]);
+		DoubleMap::append(&key1, &key2, 2);
+		assert_eq!(DoubleMap::get(&key1, &key2), &[1, 2]);
+	});
+}
+
+#[test]
+fn double_map_mutate_exists_should_work() {
+	new_test_ext().execute_with(|| {
+		type DoubleMap = self::frame_system::DataDM<Runtime>;
+
+		let (key1, key2) = (11, 13);
+
+		// mutated
+		DoubleMap::mutate_exists(key1, key2, |v| *v = Some(1));
+		assert_eq!(DoubleMap::get(&key1, key2), 1);
+
+		// removed if mutated to `None`
+		DoubleMap::mutate_exists(key1, key2, |v| *v = None);
+		assert!(!DoubleMap::contains_key(&key1, key2));
+	});
+}
+
+#[test]
+fn double_map_try_mutate_exists_should_work() {
+	new_test_ext().execute_with(|| {
+		type DoubleMap = self::frame_system::DataDM<Runtime>;
+		type TestResult = Result<(), &'static str>;
+
+		let (key1, key2) = (11, 13);
+
+		// mutated if `Ok`
+		assert_ok!(DoubleMap::try_mutate_exists(key1, key2, |v| -> TestResult {
+			*v = Some(1);
+			Ok(())
+		}));
+		assert_eq!(DoubleMap::get(&key1, key2), 1);
+
+		// no-op if `Err`
+		assert_noop!(
+			DoubleMap::try_mutate_exists(key1, key2, |v| -> TestResult {
+				*v = Some(2);
+				Err("nah")
+			}),
+			"nah"
+		);
+
+		// removed if mutated to`None`
+		assert_ok!(DoubleMap::try_mutate_exists(key1, key2, |v| -> TestResult {
+			*v = None;
+			Ok(())
+		}));
+		assert!(!DoubleMap::contains_key(&key1, key2));
+	});
+}
+
+fn expected_metadata() -> PalletStorageMetadataIR {
+	PalletStorageMetadataIR {
+		prefix: "System",
+		entries: vec![
+			StorageEntryMetadataIR {
+				name: "Data",
+				modifier: StorageEntryModifierIR::Default,
+				ty: StorageEntryTypeIR::Map {
+					hashers: vec![StorageHasherIR::Twox64Concat],
+					key: scale_info::meta_type::<u32>(),
+					value: scale_info::meta_type::<u64>(),
+				},
+				default: vec![0, 0, 0, 0, 0, 0, 0, 0],
+				docs: vec![],
+			},
+			StorageEntryMetadataIR {
+				name: "OptionLinkedMap",
+				modifier: StorageEntryModifierIR::Optional,
+				ty: StorageEntryTypeIR::Map {
+					hashers: vec![StorageHasherIR::Blake2_128Concat],
+					key: scale_info::meta_type::<u32>(),
+					value: scale_info::meta_type::<u32>(),
+				},
+				default: vec![0],
+				docs: vec![],
+			},
+			StorageEntryMetadataIR {
+				name: "GenericData",
+				modifier: StorageEntryModifierIR::Default,
+				ty: StorageEntryTypeIR::Map {
+					hashers: vec![StorageHasherIR::Identity],
+					key: scale_info::meta_type::<u32>(),
+					value: scale_info::meta_type::<u32>(),
+				},
+				default: vec![0, 0, 0, 0],
+				docs: vec![],
+			},
+			StorageEntryMetadataIR {
+				name: "GenericData2",
+				modifier: StorageEntryModifierIR::Optional,
+				ty: StorageEntryTypeIR::Map {
+					hashers: vec![StorageHasherIR::Blake2_128Concat],
+					key: scale_info::meta_type::<u32>(),
+					value: scale_info::meta_type::<u32>(),
+				},
+				default: vec![0],
+				docs: vec![],
+			},
+			StorageEntryMetadataIR {
+				name: "DataDM",
+				modifier: StorageEntryModifierIR::Default,
+				ty: StorageEntryTypeIR::Map {
+					hashers: vec![StorageHasherIR::Twox64Concat, StorageHasherIR::Blake2_128Concat],
+					key: scale_info::meta_type::<(u32, u32)>(),
+					value: scale_info::meta_type::<u64>(),
+				},
+				default: vec![0, 0, 0, 0, 0, 0, 0, 0],
+				docs: vec![],
+			},
+			StorageEntryMetadataIR {
+				name: "GenericDataDM",
+				modifier: StorageEntryModifierIR::Default,
+				ty: StorageEntryTypeIR::Map {
+					hashers: vec![StorageHasherIR::Blake2_128Concat, StorageHasherIR::Identity],
+					key: scale_info::meta_type::<(u32, u32)>(),
+					value: scale_info::meta_type::<u32>(),
+				},
+				default: vec![0, 0, 0, 0],
+				docs: vec![],
+			},
+			StorageEntryMetadataIR {
+				name: "GenericData2DM",
+				modifier: StorageEntryModifierIR::Optional,
+				ty: StorageEntryTypeIR::Map {
+					hashers: vec![StorageHasherIR::Blake2_128Concat, StorageHasherIR::Twox64Concat],
+					key: scale_info::meta_type::<(u32, u32)>(),
+					value: scale_info::meta_type::<u32>(),
+				},
+				default: vec![0],
+				docs: vec![],
+			},
+			StorageEntryMetadataIR {
+				name: "AppendableDM",
+				modifier: StorageEntryModifierIR::Default,
+				ty: StorageEntryTypeIR::Map {
+					hashers: vec![
+						StorageHasherIR::Blake2_128Concat,
+						StorageHasherIR::Blake2_128Concat,
+					],
+					key: scale_info::meta_type::<(u32, u32)>(),
+					value: scale_info::meta_type::<Vec<u32>>(),
+				},
+				default: vec![0],
+				docs: vec![],
+			},
+		],
+	}
+}
+
+#[test]
+fn store_metadata() {
+	let metadata = Pallet::<Runtime>::storage_metadata();
+	pretty_assertions::assert_eq!(expected_metadata(), metadata);
+}
+
+parameter_types! {
+	storage StorageParameter: u64 = 10;
+}
+
+#[test]
+fn check_storage_parameter_type_works() {
+	TestExternalities::default().execute_with(|| {
+		assert_eq!(sp_io::hashing::twox_128(b":StorageParameter:"), StorageParameter::key());
+
+		assert_eq!(10, StorageParameter::get());
+
+		StorageParameter::set(&300);
+		assert_eq!(300, StorageParameter::get());
+	})
+}
diff --git a/substrate/frame/support/src/tests/storage_alias.rs b/substrate/frame/support/src/tests/storage_alias.rs
new file mode 100644
index 0000000000000000000000000000000000000000..05ea1b5f712c631bc731f9444621d7f8c20f8b81
--- /dev/null
+++ b/substrate/frame/support/src/tests/storage_alias.rs
@@ -0,0 +1,192 @@
+// This file is part of Substrate.
+
+// Copyright (C) Parity Technologies (UK) Ltd.
+// SPDX-License-Identifier: Apache-2.0
+
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// 	http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+use sp_core::Get;
+
+use super::{new_test_ext, BlockNumberFor, Config, Pallet, Runtime};
+use crate::{
+	assert_noop, assert_ok, parameter_types, storage::generator::StorageValue, Blake2_128Concat,
+};
+
+#[test]
+fn storage_alias_works() {
+	new_test_ext().execute_with(|| {
+		#[crate::storage_alias]
+		type GenericData2<T> =
+			StorageMap<System, Blake2_128Concat, BlockNumberFor<T>, BlockNumberFor<T>>;
+
+		assert_eq!(Pallet::<Runtime>::generic_data2(5), None);
+		GenericData2::<Runtime>::insert(5, 5);
+		assert_eq!(Pallet::<Runtime>::generic_data2(5), Some(5));
+
+		/// Some random docs that ensure that docs are accepted
+		#[crate::storage_alias]
+		pub type GenericData<T> =
+			StorageMap<Test2, Blake2_128Concat, BlockNumberFor<T>, BlockNumberFor<T>>;
+
+		#[crate::storage_alias]
+		pub type GenericDataPallet<T: Config> =
+			StorageMap<Pallet<T>, Blake2_128Concat, BlockNumberFor<T>, BlockNumberFor<T>>;
+	});
+}
+
+#[test]
+fn storage_value_mutate_exists_should_work() {
+	new_test_ext().execute_with(|| {
+		#[crate::storage_alias]
+		pub type Value = StorageValue<Test, u32>;
+
+		assert!(!Value::exists());
+
+		Value::mutate_exists(|v| *v = Some(1));
+		assert!(Value::exists());
+		assert_eq!(Value::get(), Some(1));
+
+		// removed if mutated to `None`
+		Value::mutate_exists(|v| *v = None);
+		assert!(!Value::exists());
+	});
+}
+
+#[test]
+fn storage_value_try_mutate_exists_should_work() {
+	new_test_ext().execute_with(|| {
+		#[crate::storage_alias]
+		pub type Value = StorageValue<Test, u32>;
+
+		type TestResult = std::result::Result<(), &'static str>;
+
+		assert!(!Value::exists());
+
+		// mutated if `Ok`
+		assert_ok!(Value::try_mutate_exists(|v| -> TestResult {
+			*v = Some(1);
+			Ok(())
+		}));
+		assert!(Value::exists());
+		assert_eq!(Value::get(), Some(1));
+
+		// no-op if `Err`
+		assert_noop!(
+			Value::try_mutate_exists(|v| -> TestResult {
+				*v = Some(2);
+				Err("nah")
+			}),
+			"nah"
+		);
+		assert_eq!(Value::get(), Some(1));
+
+		// removed if mutated to`None`
+		assert_ok!(Value::try_mutate_exists(|v| -> TestResult {
+			*v = None;
+			Ok(())
+		}));
+		assert!(!Value::exists());
+	});
+}
+
+#[docify::export]
+#[test]
+fn verbatim_attribute() {
+	new_test_ext().execute_with(|| {
+		// Declare the alias that will use the verbatim identifier as prefix.
+		#[crate::storage_alias(verbatim)]
+		pub type Value = StorageValue<Test, u32>;
+
+		// Check that it works as expected.
+		Value::put(1);
+		assert_eq!(1, Value::get().unwrap());
+
+		// The prefix is the one we declared above.
+		assert_eq!(&b"Test"[..], Value::module_prefix());
+	});
+}
+
+#[docify::export]
+#[test]
+fn pallet_name_attribute() {
+	new_test_ext().execute_with(|| {
+		// Declare the alias that will use the pallet name as prefix.
+		#[crate::storage_alias(pallet_name)]
+		pub type Value<T: Config> = StorageValue<Pallet<T>, u32>;
+
+		// Check that it works as expected.
+		Value::<Runtime>::put(1);
+		assert_eq!(1, Value::<Runtime>::get().unwrap());
+
+		// The prefix is the pallet name. In this case the pallet name is `System` as declared in
+		// `construct_runtime!`.
+		assert_eq!(&b"System"[..], Value::<Runtime>::module_prefix());
+	});
+}
+
+#[docify::export]
+#[test]
+fn dynamic_attribute() {
+	new_test_ext().execute_with(|| {
+		// First let's declare our prefix.
+		//
+		// It could be any type that, as long as it implements `Get<&'static str>`.
+		parameter_types! {
+			pub Prefix: &'static str = "Hello";
+		}
+
+		// Declare the alias that will use the dynamic `Get` as prefix.
+		#[crate::storage_alias(dynamic)]
+		pub type Value<T: Get<&'static str>> = StorageValue<T, u32>;
+
+		// Check that it works as expected.
+		Value::<Prefix>::put(1);
+		assert_eq!(1, Value::<Prefix>::get().unwrap());
+
+		// The prefix is the one we declared above.
+		assert_eq!(&b"Hello"[..], Value::<Prefix>::module_prefix());
+	});
+}
+
+#[docify::export]
+#[test]
+fn storage_alias_guess() {
+	new_test_ext().execute_with(|| {
+		// The macro will use `Test` as prefix.
+		#[crate::storage_alias]
+		pub type Value = StorageValue<Test, u32>;
+
+		assert_eq!(&b"Test"[..], Value::module_prefix());
+
+		// The macro will use the pallet name as prefix.
+		#[crate::storage_alias]
+		pub type PalletValue<T: Config> = StorageValue<Pallet<T>, u32>;
+
+		assert_eq!(&b"System"[..], PalletValue::<Runtime>::module_prefix());
+	});
+}
+
+#[test]
+fn dynamic_attribute_without_generics_works() {
+	new_test_ext().execute_with(|| {
+		parameter_types! {
+			pub Prefix: &'static str = "Hello";
+		}
+
+		#[crate::storage_alias(dynamic)]
+		pub type Value = StorageValue<Prefix, u32>;
+
+		Value::put(1);
+		assert_eq!(1, Value::get().unwrap())
+	});
+}