diff --git a/substrate/frame/contracts/proc-macro/src/lib.rs b/substrate/frame/contracts/proc-macro/src/lib.rs
index de7b9b881d305ca11563d55d9360509f2d1ec233..c5f52f43a7d87c17d3724ccc2cf2bf6b2b4bb647 100644
--- a/substrate/frame/contracts/proc-macro/src/lib.rs
+++ b/substrate/frame/contracts/proc-macro/src/lib.rs
@@ -25,10 +25,12 @@
 extern crate alloc;
 
 use alloc::{
+	collections::BTreeMap,
 	format,
 	string::{String, ToString},
 	vec::Vec,
 };
+use core::cmp::Reverse;
 use proc_macro::TokenStream;
 use proc_macro2::{Span, TokenStream as TokenStream2};
 use quote::{quote, quote_spanned, ToTokens};
@@ -160,7 +162,7 @@ struct EnvDef {
 /// Parsed host function definition.
 struct HostFn {
 	item: syn::ItemFn,
-	module: String,
+	version: u8,
 	name: String,
 	returns: HostFnReturn,
 	is_stable: bool,
@@ -208,7 +210,7 @@ impl HostFn {
 		let span = item.span();
 		let mut attrs = item.attrs.clone();
 		attrs.retain(|a| !a.path.is_ident("doc"));
-		let mut maybe_module = None;
+		let mut maybe_version = None;
 		let mut is_stable = true;
 		let mut alias_to = None;
 		let mut not_deprecated = true;
@@ -216,12 +218,11 @@ impl HostFn {
 			let ident = attr.path.get_ident().ok_or(err(span, msg))?.to_string();
 			match ident.as_str() {
 				"version" => {
-					if maybe_module.is_some() {
+					if maybe_version.is_some() {
 						return Err(err(span, "#[version] can only be specified once"))
 					}
-					let ver: u8 =
-						attr.parse_args::<syn::LitInt>().and_then(|lit| lit.base10_parse())?;
-					maybe_module = Some(format!("seal{}", ver));
+					maybe_version =
+						Some(attr.parse_args::<syn::LitInt>().and_then(|lit| lit.base10_parse())?);
 				},
 				"unstable" => {
 					if !is_stable {
@@ -341,7 +342,7 @@ impl HostFn {
 
 						Ok(Self {
 							item,
-							module: maybe_module.unwrap_or_else(|| "seal0".to_string()),
+							version: maybe_version.unwrap_or_default(),
 							name,
 							returns,
 							is_stable,
@@ -355,6 +356,10 @@ impl HostFn {
 			_ => Err(err(span, &msg)),
 		}
 	}
+
+	fn module(&self) -> String {
+		format!("seal{}", self.version)
+	}
 }
 
 impl EnvDef {
@@ -409,83 +414,116 @@ fn is_valid_special_arg(idx: usize, arg: &FnArg) -> bool {
 	matches!(*pat.ty, syn::Type::Infer(_))
 }
 
-/// Expands documentation for host functions.
-fn expand_docs(def: &mut EnvDef) -> TokenStream2 {
-	let mut modules = def.host_funcs.iter().map(|f| f.module.clone()).collect::<Vec<_>>();
-	modules.sort();
-	modules.dedup();
-
-	let doc_selector = |a: &syn::Attribute| a.path.is_ident("doc");
-	let docs = modules.iter().map(|m| {
-		let funcs = def.host_funcs.iter_mut().map(|f| {
-			if *m == f.module {
-				// Remove auxiliary args: `ctx: _` and `memory: _`
-				f.item.sig.inputs = f
-					.item
-					.sig
-					.inputs
-					.iter()
-					.skip(2)
-					.map(|p| p.clone())
-					.collect::<Punctuated<FnArg, Comma>>();
-				let func_decl = f.item.sig.to_token_stream();
-				let func_doc = if let Some(origin_fn) = &f.alias_to {
-					let alias_doc = format!(
-						"This is just an alias function to [`{0}()`][`Self::{0}`] with backwards-compatible prefixed identifier.",
-						origin_fn,
-					);
-					quote! { #[doc = #alias_doc] }
-
-				} else {
-					let func_docs = f.item.attrs.iter().filter(|a| doc_selector(a)).map(|d| {
-						let docs = d.to_token_stream();
-						quote! { #docs }
-					});
-					let unstable_notice = if !f.is_stable {
-						let warning = "\n # Unstable\n\n \
-									    This function is unstable and it is a subject to change (or removal) in the future.\n \
-									    Do not deploy a contract using it to a production chain.";
-						quote! { #[doc = #warning] }
-					} else {
-						quote! {}
-					};
-					quote! {
-						#( #func_docs )*
-						#unstable_notice
-					}
-				};
-				quote! {
-					#func_doc
-					#func_decl;
-				}
-			} else {
-				quote! {}
-			}
-		});
+fn expand_func_doc(func: &HostFn) -> TokenStream2 {
+	// Remove auxiliary args: `ctx: _` and `memory: _`
+	let func_decl = {
+		let mut sig = func.item.sig.clone();
+		sig.inputs = sig
+			.inputs
+			.iter()
+			.skip(2)
+			.map(|p| p.clone())
+			.collect::<Punctuated<FnArg, Comma>>();
+		sig.to_token_stream()
+	};
+	let func_doc = {
+		let func_docs = if let Some(origin_fn) = &func.alias_to {
+			let alias_doc = format!(
+				"This is just an alias function to [`{0}()`][`Self::{0}`] with backwards-compatible prefixed identifier.",
+				origin_fn,
+			);
+			quote! { #[doc = #alias_doc] }
+		} else {
+			let docs = func.item.attrs.iter().filter(|a| a.path.is_ident("doc")).map(|d| {
+				let docs = d.to_token_stream();
+				quote! { #docs }
+			});
+			quote! { #( #docs )* }
+		};
+		let deprecation_notice = if !func.not_deprecated {
+			let warning = "\n # Deprecated\n\n \
+								This function is deprecated and will be removed in future versions.\n \
+								No new code or contracts with this API can be deployed.";
+			quote! { #[doc = #warning] }
+		} else {
+			quote! {}
+		};
+		let import_notice = {
+			let info = format!(
+				"\n# Wasm Import Statement\n```wat\n(import \"seal{}\" \"{}\" (func ...))\n```",
+				func.version, func.name,
+			);
+			quote! { #[doc = #info] }
+		};
+		let unstable_notice = if !func.is_stable {
+			let warning = "\n # Unstable\n\n \
+								This function is unstable and it is a subject to change (or removal) in the future.\n \
+								Do not deploy a contract using it to a production chain.";
+			quote! { #[doc = #warning] }
+		} else {
+			quote! {}
+		};
+		quote! {
+			#deprecation_notice
+			#func_docs
+			#import_notice
+			#unstable_notice
+		}
+	};
+	quote! {
+		#func_doc
+		#func_decl;
+	}
+}
 
-		let module = Ident::new(m, Span::call_site());
-		let module_doc = format!(
-			"Documentation of the API available to contracts by importing `{}` WASM module.",
-			module
-		);
+/// Expands documentation for host functions.
+fn expand_docs(def: &EnvDef) -> TokenStream2 {
+	// Create the `Current` trait with only the newest versions
+	// we sort so that only the newest versions make it into `docs`
+	let mut current_docs = BTreeMap::new();
+	let mut funcs: Vec<_> = def.host_funcs.iter().filter(|f| f.alias_to.is_none()).collect();
+	funcs.sort_unstable_by_key(|func| Reverse(func.version));
+	for func in funcs {
+		if current_docs.contains_key(&func.name) {
+			continue
+		}
+		current_docs.insert(func.name.clone(), expand_func_doc(&func));
+	}
+	let current_docs = current_docs.values();
 
+	// Create the `legacy` module with all functions
+	// Maps from version to list of functions that have this version
+	let mut legacy_doc = BTreeMap::<u8, Vec<TokenStream2>>::new();
+	for func in def.host_funcs.iter() {
+		legacy_doc.entry(func.version).or_default().push(expand_func_doc(&func));
+	}
+	let legacy_doc = legacy_doc.into_iter().map(|(version, funcs)| {
+		let doc = format!("All functions available in the **seal{}** module", version);
+		let version = Ident::new(&format!("Version{version}"), Span::call_site());
 		quote! {
-			#[doc = #module_doc]
-			pub mod #module {
-				use crate::wasm::runtime::{TrapReason, ReturnCode};
-				/// Every function in this trait represents (at least) one function that can be imported by a contract.
-				///
-				/// The function's identifier is to be set as the name in the import definition.
-				/// Where it is specifically indicated, an _alias_ function having `seal_`-prefixed identifier and
-				/// just the same signature and body, is also available (for backwards-compatibility purposes).
-				pub trait Api {
-					#( #funcs )*
-				}
+			#[doc = #doc]
+			pub trait #version {
+				#( #funcs )*
 			}
 		}
 	});
+
 	quote! {
-		  #( #docs )*
+		/// Contains only the latest version of each function.
+		///
+		/// In reality there are more functions available but they are all obsolete: When a function
+		/// is updated a new **version** is added and the old versions stays available as-is.
+		/// We only list the newest version here. Some functions are available under additional
+		/// names (aliases) for historic reasons which are omitted here.
+		///
+		/// If you want an overview of all the functions available to a contact all you need
+		/// to look at is this trait. It contains only the latest version of each
+		/// function and no aliases. If you are writing a contract(language) from scratch
+		/// this is where you should look at.
+		pub trait Current {
+			#( #current_docs )*
+		}
+		#( #legacy_doc )*
 	}
 }
 
@@ -493,25 +531,26 @@ fn expand_docs(def: &mut EnvDef) -> TokenStream2 {
 /// Should generate source code for:
 ///  - implementations of the host functions to be added to the wasm runtime environment (see
 ///    `expand_impls()`).
-fn expand_env(def: &mut EnvDef, docs: bool) -> TokenStream2 {
+fn expand_env(def: &EnvDef, docs: bool) -> TokenStream2 {
 	let impls = expand_impls(def);
 	let docs = docs.then_some(expand_docs(def)).unwrap_or(TokenStream2::new());
 
 	quote! {
 		pub struct Env;
 		#impls
-		/// Contains the documentation of the API available to contracts.
+		/// Documentation of the API (host functions) available to contracts.
 		///
-		/// In order to generate this documentation, pass `doc` attribute to the [`#[define_env]`][`macro@define_env`] macro:
-		/// `#[define_env(doc)]`, and then run `cargo doc`.
+		/// The `Current` trait might be the most useful doc to look at. The versioned
+		/// traits only exist for reference: If trying to find out if a specific version of
+		/// `pallet-contracts` contains a certain function.
 		///
-		/// This module is not meant to be used by any code. Rather, it is meant to be consumed by humans through rustdoc.
+		/// # Note
 		///
-		/// Every function described in this module's sub module's traits uses this sub module's identifier
-		/// as its imported module name. The identifier of the function is the function's imported name.
-		/// According to the [WASM spec of imports](https://webassembly.github.io/spec/core/text/modules.html#text-import).
+		/// This module is not meant to be used by any code. Rather, it is meant to be
+		/// consumed by humans through rustdoc.
 		#[cfg(doc)]
 		pub mod api_doc {
+			use super::{TrapReason, ReturnCode};
 			#docs
 		}
 	}
@@ -520,7 +559,7 @@ fn expand_env(def: &mut EnvDef, docs: bool) -> TokenStream2 {
 /// Generates for every host function:
 ///   - real implementation, to register it in the contract execution environment;
 ///   - dummy implementation, to be used as mocks for contract validation step.
-fn expand_impls(def: &mut EnvDef) -> TokenStream2 {
+fn expand_impls(def: &EnvDef) -> TokenStream2 {
 	let impls = expand_functions(def, true, quote! { crate::wasm::Runtime<E> });
 	let dummy_impls = expand_functions(def, false, quote! { () });
 
@@ -553,16 +592,12 @@ fn expand_impls(def: &mut EnvDef) -> TokenStream2 {
 	}
 }
 
-fn expand_functions(
-	def: &mut EnvDef,
-	expand_blocks: bool,
-	host_state: TokenStream2,
-) -> TokenStream2 {
+fn expand_functions(def: &EnvDef, expand_blocks: bool, host_state: TokenStream2) -> TokenStream2 {
 	let impls = def.host_funcs.iter().map(|f| {
 		// skip the context and memory argument
 		let params = f.item.sig.inputs.iter().skip(2);
 		let (module, name, body, wasm_output, output) = (
-			&f.module,
+			f.module(),
 			&f.name,
 			&f.item.block,
 			f.returns.to_wasm_sig(),
diff --git a/substrate/frame/contracts/src/wasm/mod.rs b/substrate/frame/contracts/src/wasm/mod.rs
index 6b9cefcdd2d96f367d767750b51f40e029df9ef7..9bf36b47f0b065f01c2ba73d272e7fccc26fc059 100644
--- a/substrate/frame/contracts/src/wasm/mod.rs
+++ b/substrate/frame/contracts/src/wasm/mod.rs
@@ -24,8 +24,13 @@ mod runtime;
 
 #[cfg(feature = "runtime-benchmarks")]
 pub use crate::wasm::code_cache::reinstrument;
+
 #[cfg(doc)]
 pub use crate::wasm::runtime::api_doc;
+
+#[cfg(test)]
+pub use tests::MockExt;
+
 pub use crate::wasm::{
 	prepare::TryInstantiate,
 	runtime::{
@@ -45,8 +50,6 @@ use frame_support::dispatch::{DispatchError, DispatchResult};
 use sp_core::Get;
 use sp_runtime::RuntimeDebug;
 use sp_std::prelude::*;
-#[cfg(test)]
-pub use tests::MockExt;
 use wasmi::{
 	Config as WasmiConfig, Engine, Instance, Linker, Memory, MemoryType, Module, StackLimits, Store,
 };