From 2bff2f84e3bda83041525c1160ebca14203394bd Mon Sep 17 00:00:00 2001
From: Tsvetomir Dimitrov <tsv.dimitrov@gmail.com>
Date: Sat, 13 Aug 2022 14:56:40 +0300
Subject: [PATCH] Runtime API versioning (#11779)
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

* Runtime API versioning

Related to issue #11577

Add support for multiple versions of a Runtime API. The purpose is to
have one main version of the API, which is considered stable and
multiple unstable (aka staging) ones.

How it works
===========
Some methods of the API trait can be tagged with `#[api_version(N)]`
attribute where N is version number bigger than the main one. Let's call
them **staging methods** for brevity.

The implementor of the API decides which version to implement.

Example (from https://github.com/paritytech/substrate/issues/11577#issuecomment-1145347025):

```
decl_runtime_apis! {
    #{api_version(10)]
    trait Test {
         fn something() -> Vec<u8>;
         #[api_version(11)]
         fn new_cool_function() -> u32;
    }
}
```

```
impl_runtime_apis! {
    #[api_version(11)]
    impl Test for Runtime {
         fn something() -> Vec<u8> { vec![1, 2, 3] }

         fn new_cool_function() -> u32 {
             10
         }
    }
}
```

Version safety checks (currently not implemented)
=================================================
By default in the API trait all staging methods has got default
implementation calling `unimplemented!()`. This is a problem because if
the developer wants to implement version 11 in the example above and
forgets to add `fn new_cool_function()` in `impl_runtime_apis!` the
runtime will crash when the function is executed.

Ideally a compilation error should be generated in such cases.

TODOs
=====

Things not working well at the moment:
[ ] Version safety check
[ ] Integration tests of `primitives/api` are messed up a bit. More
specifically `primitives/api/test/tests/decl_and_impl.rs`
[ ] Integration test covering the new functionality.
[ ] Some duplicated code

* Update primitives/api/proc-macro/src/impl_runtime_apis.rs

Code review feedback and formatting

Co-authored-by: asynchronous rob <rphmeier@gmail.com>

* Code review feedback

Applying suggestions from @bkchr

* fmt

* Apply suggestions from code review

Co-authored-by: Bastian Köcher <bkchr@users.noreply.github.com>

* Code review feedback

* dummy trait -> versioned trait

* Implement only versioned traits (not compiling)

* Remove native API calls (still not compiling)

* fmt

* Fix compilation

* Comments

* Remove unused code

* Remove native runtime tests

* Remove unused code

* Fix UI tests

* Code review feedback

* Code review feedback

* attribute_names -> common

* Rework `append_api_version`

* Code review feedback

* Apply suggestions from code review

Co-authored-by: Bastian Köcher <bkchr@users.noreply.github.com>

* Code review feedback

* Code review feedback

* Code review feedback

* Use type alias for the default trait - doesn't compile

* Fixes

* Better error for `method_api_ver < trait_api_version`

* fmt

* Rework how we call runtime functions

* Update UI tests

* Fix warnings

* Fix doctests

* Apply suggestions from code review

Co-authored-by: Bastian Köcher <bkchr@users.noreply.github.com>

* Apply suggestions from code review

Co-authored-by: Bastian Köcher <bkchr@users.noreply.github.com>

* Fix formatting and small compilation errors

* Update primitives/api/proc-macro/src/impl_runtime_apis.rs

Co-authored-by: Bastian Köcher <bkchr@users.noreply.github.com>

Co-authored-by: asynchronous rob <rphmeier@gmail.com>
Co-authored-by: Bastian Köcher <bkchr@users.noreply.github.com>
Co-authored-by: Bastian Köcher <info@kchr.de>
---
 .../primitives/api/proc-macro/src/common.rs   |  41 +
 .../api/proc-macro/src/decl_runtime_apis.rs   | 699 +++++++-----------
 .../api/proc-macro/src/impl_runtime_apis.rs   | 319 ++++----
 .../primitives/api/proc-macro/src/lib.rs      |   1 +
 .../proc-macro/src/mock_impl_runtime_apis.rs  | 134 +++-
 .../primitives/api/proc-macro/src/utils.rs    |  43 +-
 substrate/primitives/api/src/lib.rs           |  82 +-
 .../api/test/tests/decl_and_impl.rs           |  39 +-
 .../api/test/tests/runtime_calls.rs           |  24 +-
 .../primitives/api/test/tests/trybuild.rs     |   1 +
 .../ui/impl_incorrect_method_signature.stderr |  43 --
 .../api/test/tests/ui/impl_missing_version.rs |  40 +
 .../test/tests/ui/impl_missing_version.stderr |  14 +
 ...pi_version.rs => invalid_api_version_1.rs} |   0
 ...on.stderr => invalid_api_version_1.stderr} |   4 +-
 .../tests/ui/invalid_api_version_2.stderr     |   4 +-
 .../tests/ui/invalid_api_version_3.stderr     |   4 +-
 .../test/tests/ui/invalid_api_version_4.rs    |   8 +
 .../tests/ui/invalid_api_version_4.stderr     |   5 +
 .../ui/method_ver_lower_than_trait_ver.rs     |   9 +
 .../ui/method_ver_lower_than_trait_ver.stderr |  11 +
 .../test/tests/ui/missing_versioned_method.rs |  39 +
 .../tests/ui/missing_versioned_method.stderr  |   8 +
 .../missing_versioned_method_multiple_vers.rs |  42 ++
 ...sing_versioned_method_multiple_vers.stderr |   8 +
 .../ui/mock_advanced_block_id_by_value.rs     |   2 +-
 .../ui/mock_advanced_block_id_by_value.stderr |   4 +-
 .../tests/ui/mock_advanced_missing_blockid.rs |   2 +-
 .../ui/mock_advanced_missing_blockid.stderr   |   4 +-
 .../tests/ui/mock_only_self_reference.stderr  |  74 +-
 .../tests/ui/positive_cases/default_impls.rs  |  41 +
 ...reference_in_impl_runtime_apis_call.stderr |  43 --
 32 files changed, 937 insertions(+), 855 deletions(-)
 create mode 100644 substrate/primitives/api/proc-macro/src/common.rs
 create mode 100644 substrate/primitives/api/test/tests/ui/impl_missing_version.rs
 create mode 100644 substrate/primitives/api/test/tests/ui/impl_missing_version.stderr
 rename substrate/primitives/api/test/tests/ui/{invalid_api_version.rs => invalid_api_version_1.rs} (100%)
 rename substrate/primitives/api/test/tests/ui/{invalid_api_version.stderr => invalid_api_version_1.stderr} (65%)
 create mode 100644 substrate/primitives/api/test/tests/ui/invalid_api_version_4.rs
 create mode 100644 substrate/primitives/api/test/tests/ui/invalid_api_version_4.stderr
 create mode 100644 substrate/primitives/api/test/tests/ui/method_ver_lower_than_trait_ver.rs
 create mode 100644 substrate/primitives/api/test/tests/ui/method_ver_lower_than_trait_ver.stderr
 create mode 100644 substrate/primitives/api/test/tests/ui/missing_versioned_method.rs
 create mode 100644 substrate/primitives/api/test/tests/ui/missing_versioned_method.stderr
 create mode 100644 substrate/primitives/api/test/tests/ui/missing_versioned_method_multiple_vers.rs
 create mode 100644 substrate/primitives/api/test/tests/ui/missing_versioned_method_multiple_vers.stderr
 create mode 100644 substrate/primitives/api/test/tests/ui/positive_cases/default_impls.rs

diff --git a/substrate/primitives/api/proc-macro/src/common.rs b/substrate/primitives/api/proc-macro/src/common.rs
new file mode 100644
index 00000000000..10887be6132
--- /dev/null
+++ b/substrate/primitives/api/proc-macro/src/common.rs
@@ -0,0 +1,41 @@
+// This file is part of Substrate.
+
+// Copyright (C) 2018-2024 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.
+
+/// The ident used for the block generic parameter.
+pub const BLOCK_GENERIC_IDENT: &str = "Block";
+
+/// Unique identifier used to make the hidden includes unique for this macro.
+pub const HIDDEN_INCLUDES_ID: &str = "DECL_RUNTIME_APIS";
+
+/// The `core_trait` attribute.
+pub const CORE_TRAIT_ATTRIBUTE: &str = "core_trait";
+/// The `api_version` attribute.
+///
+/// Is used to set the current version of the trait.
+pub const API_VERSION_ATTRIBUTE: &str = "api_version";
+/// The `changed_in` attribute.
+///
+/// Is used when the function signature changed between different versions of a trait.
+/// This attribute should be placed on the old signature of the function.
+pub const CHANGED_IN_ATTRIBUTE: &str = "changed_in";
+/// The `renamed` attribute.
+///
+/// Is used when a trait method was renamed.
+pub const RENAMED_ATTRIBUTE: &str = "renamed";
+/// All attributes that we support in the declaration of a runtime api trait.
+pub const SUPPORTED_ATTRIBUTE_NAMES: &[&str] =
+	&[CORE_TRAIT_ATTRIBUTE, API_VERSION_ATTRIBUTE, CHANGED_IN_ATTRIBUTE, RENAMED_ATTRIBUTE];
diff --git a/substrate/primitives/api/proc-macro/src/decl_runtime_apis.rs b/substrate/primitives/api/proc-macro/src/decl_runtime_apis.rs
index b031c0f8bb1..aac4491720c 100644
--- a/substrate/primitives/api/proc-macro/src/decl_runtime_apis.rs
+++ b/substrate/primitives/api/proc-macro/src/decl_runtime_apis.rs
@@ -16,11 +16,15 @@
 // limitations under the License.
 
 use crate::utils::{
-	extract_parameter_names_types_and_borrows, fold_fn_decl_for_client_side,
-	generate_call_api_at_fn_name, generate_crate_access, generate_hidden_includes,
-	generate_method_runtime_api_impl_name, generate_native_call_generator_fn_name,
-	generate_runtime_mod_name_for_trait, prefix_function_with_trait,
-	replace_wild_card_parameter_names, return_type_extract_type, AllowSelfRefInParameters,
+	extract_parameter_names_types_and_borrows, fold_fn_decl_for_client_side, generate_crate_access,
+	generate_hidden_includes, generate_runtime_mod_name_for_trait, parse_runtime_api_version,
+	prefix_function_with_trait, replace_wild_card_parameter_names, return_type_extract_type,
+	versioned_trait_name, AllowSelfRefInParameters,
+};
+
+use crate::common::{
+	API_VERSION_ATTRIBUTE, BLOCK_GENERIC_IDENT, CHANGED_IN_ATTRIBUTE, CORE_TRAIT_ATTRIBUTE,
+	HIDDEN_INCLUDES_ID, RENAMED_ATTRIBUTE, SUPPORTED_ATTRIBUTE_NAMES,
 };
 
 use proc_macro2::{Span, TokenStream};
@@ -33,36 +37,11 @@ use syn::{
 	parse_macro_input, parse_quote,
 	spanned::Spanned,
 	visit::{self, Visit},
-	Attribute, FnArg, GenericParam, Generics, Ident, ItemTrait, Lit, Meta, NestedMeta, ReturnType,
-	TraitBound, TraitItem, TraitItemMethod, Type,
+	Attribute, FnArg, GenericParam, Generics, Ident, ItemTrait, Lit, Meta, NestedMeta, TraitBound,
+	TraitItem, TraitItemMethod,
 };
 
-use std::collections::HashMap;
-
-/// The ident used for the block generic parameter.
-const BLOCK_GENERIC_IDENT: &str = "Block";
-
-/// Unique identifier used to make the hidden includes unique for this macro.
-const HIDDEN_INCLUDES_ID: &str = "DECL_RUNTIME_APIS";
-
-/// The `core_trait` attribute.
-const CORE_TRAIT_ATTRIBUTE: &str = "core_trait";
-/// The `api_version` attribute.
-///
-/// Is used to set the current version of the trait.
-const API_VERSION_ATTRIBUTE: &str = "api_version";
-/// The `changed_in` attribute.
-///
-/// Is used when the function signature changed between different versions of a trait.
-/// This attribute should be placed on the old signature of the function.
-const CHANGED_IN_ATTRIBUTE: &str = "changed_in";
-/// The `renamed` attribute.
-///
-/// Is used when a trait method was renamed.
-const RENAMED_ATTRIBUTE: &str = "renamed";
-/// All attributes that we support in the declaration of a runtime api trait.
-const SUPPORTED_ATTRIBUTE_NAMES: &[&str] =
-	&[CORE_TRAIT_ATTRIBUTE, API_VERSION_ATTRIBUTE, CHANGED_IN_ATTRIBUTE, RENAMED_ATTRIBUTE];
+use std::collections::{BTreeMap, HashMap};
 
 /// The structure used for parsing the runtime api declarations.
 struct RuntimeApiDecls {
@@ -119,20 +98,6 @@ impl<'ast> Visit<'ast> for IsUsingBlock {
 	}
 }
 
-/// Visits the ast and checks if `Block` ident is used somewhere.
-fn type_is_using_block(ty: &Type) -> bool {
-	let mut visitor = IsUsingBlock { result: false };
-	visitor.visit_type(ty);
-	visitor.result
-}
-
-/// Visits the ast and checks if `Block` ident is used somewhere.
-fn return_type_is_using_block(ty: &ReturnType) -> bool {
-	let mut visitor = IsUsingBlock { result: false };
-	visitor.visit_return_type(ty);
-	visitor.result
-}
-
 /// Replace all occurrences of `Block` with `NodeBlock`
 struct ReplaceBlockWithNodeBlock {}
 
@@ -146,146 +111,69 @@ impl Fold for ReplaceBlockWithNodeBlock {
 	}
 }
 
-/// Replace all occurrences of `Block` with `NodeBlock`
-fn fn_arg_replace_block_with_node_block(fn_arg: FnArg) -> FnArg {
-	let mut replace = ReplaceBlockWithNodeBlock {};
-	fold::fold_fn_arg(&mut replace, fn_arg)
-}
-
-/// Replace all occurrences of `Block` with `NodeBlock`
-fn return_type_replace_block_with_node_block(return_type: ReturnType) -> ReturnType {
-	let mut replace = ReplaceBlockWithNodeBlock {};
-	fold::fold_return_type(&mut replace, return_type)
-}
-
-/// Generate the functions that generate the native call closure for each trait method.
-fn generate_native_call_generators(decl: &ItemTrait) -> Result<TokenStream> {
-	let fns = decl.items.iter().filter_map(|i| match i {
-		TraitItem::Method(ref m) => Some(&m.sig),
-		_ => None,
-	});
-
-	let mut result = Vec::new();
-	let trait_ = &decl.ident;
-	let crate_ = generate_crate_access(HIDDEN_INCLUDES_ID);
-
-	// Auxiliary function that is used to convert between types that use different block types.
-	// The function expects that both are convertible by encoding the one and decoding the other.
-	result.push(quote!(
-		#[cfg(any(feature = "std", test))]
-		fn convert_between_block_types
-			<I: #crate_::Encode, R: #crate_::Decode, F: FnOnce(#crate_::codec::Error) -> #crate_::ApiError>(
-				input: &I,
-				map_error: F,
-			) -> std::result::Result<R, #crate_::ApiError>
-		{
-			<R as #crate_::DecodeLimit>::decode_with_depth_limit(
-				#crate_::MAX_EXTRINSIC_DEPTH,
-				&mut &#crate_::Encode::encode(input)[..],
-			).map_err(map_error)
+/// Versioned API traits are used to catch missing methods when implementing a specific version of a
+/// versioned API. They contain all non-versioned methods (aka stable methods) from the main trait
+/// and all versioned methods for the specific version. This means that there is one trait for each
+/// version mentioned in the trait definition. For example:
+/// ```ignore
+/// // The trait version implicitly is 1
+/// decl_runtime_apis!(
+/// 	trait SomeApi {
+/// 		fn method1(); 	// this is a 'stable method'
+///
+/// 		#[api_version(2)]
+/// 		fn method2();
+///
+/// 		#[api_version(2)]
+/// 		fn method3();
+///
+/// 		#[api_version(3)]
+/// 		fn method4();
+/// 	}
+/// );
+/// ```
+/// This trait has got three different versions. The function below will generate the following
+/// code:
+/// ```
+/// trait SomeApiV1 {
+/// 	// in V1 only the stable methods are required. The rest has got default implementations.
+/// 	fn method1();
+/// }
+///
+/// trait SomeApiV2 {
+/// 	// V2 contains all methods from V1 and V2. V3 not required so they are skipped.
+/// 	fn method1();
+/// 	fn method2();
+/// 	fn method3();
+/// }
+///
+/// trait SomeApiV3 {
+/// 	// And V3 contains all methods from the trait.
+/// 	fn method1();
+/// 	fn method2();
+/// 	fn method3();
+/// 	fn method4();
+/// }
+/// ```
+fn generate_versioned_api_traits(
+	api: ItemTrait,
+	methods: BTreeMap<u64, Vec<TraitItemMethod>>,
+) -> Vec<ItemTrait> {
+	let mut result = Vec::<ItemTrait>::new();
+	for (version, _) in &methods {
+		let mut versioned_trait = api.clone();
+		versioned_trait.ident = versioned_trait_name(&versioned_trait.ident, *version);
+		versioned_trait.items = Vec::new();
+		// Add the methods from the current version and all previous one. Versions are sorted so
+		// it's safe to stop early.
+		for (_, m) in methods.iter().take_while(|(v, _)| v <= &version) {
+			versioned_trait.items.extend(m.iter().cloned().map(|m| TraitItem::Method(m)));
 		}
-	));
-
-	// Generate a native call generator for each function of the given trait.
-	for fn_ in fns {
-		let params = extract_parameter_names_types_and_borrows(fn_, AllowSelfRefInParameters::No)?;
-		let trait_fn_name = &fn_.ident;
-		let function_name_str = fn_.ident.to_string();
-		let fn_name = generate_native_call_generator_fn_name(&fn_.ident);
-		let output = return_type_replace_block_with_node_block(fn_.output.clone());
-		let output_ty = return_type_extract_type(&output);
-		let output = quote!( std::result::Result<#output_ty, #crate_::ApiError> );
-
-		// Every type that is using the `Block` generic parameter, we need to encode/decode,
-		// to make it compatible between the runtime/node.
-		let conversions = params.iter().filter(|v| type_is_using_block(&v.1)).map(|(n, t, _)| {
-			let param_name = quote!(#n).to_string();
-
-			quote!(
-				let #n: #t = convert_between_block_types(
-					&#n,
-					|e| #crate_::ApiError::FailedToConvertParameter {
-						function: #function_name_str,
-						parameter: #param_name,
-						error: e,
-					},
-				)?;
-			)
-		});
-		// Same as for the input types, we need to check if we also need to convert the output,
-		// before returning it.
-		let output_conversion = if return_type_is_using_block(&fn_.output) {
-			quote!(
-				convert_between_block_types(
-					&res,
-					|e| #crate_::ApiError::FailedToConvertReturnValue {
-						function: #function_name_str,
-						error: e,
-					},
-				)
-			)
-		} else {
-			quote!(Ok(res))
-		};
-
-		let input_names = params.iter().map(|v| &v.0);
-		// If the type is using the block generic type, we will encode/decode it to make it
-		// compatible. To ensure that we forward it by ref/value, we use the value given by the
-		// the user. Otherwise if it is not using the block, we don't need to add anything.
-		let input_borrows =
-			params.iter().map(|v| if type_is_using_block(&v.1) { v.2 } else { None });
-
-		// Replace all `Block` with `NodeBlock`, add `'a` lifetime to references and collect
-		// all the function inputs.
-		let fn_inputs = fn_
-			.inputs
-			.iter()
-			.map(|v| fn_arg_replace_block_with_node_block(v.clone()))
-			.map(|v| match v {
-				FnArg::Typed(ref arg) => {
-					let mut arg = arg.clone();
-					if let Type::Reference(ref mut r) = *arg.ty {
-						r.lifetime = Some(parse_quote!( 'a ));
-					}
-					FnArg::Typed(arg)
-				},
-				r => r,
-			});
-
-		let (impl_generics, ty_generics, where_clause) = decl.generics.split_for_impl();
-		// We need to parse them again, to get an easy access to the actual parameters.
-		let impl_generics: Generics = parse_quote!( #impl_generics );
-		let impl_generics_params = impl_generics.params.iter().map(|p| {
-			match p {
-				GenericParam::Type(ref ty) => {
-					let mut ty = ty.clone();
-					ty.bounds.push(parse_quote!( 'a ));
-					GenericParam::Type(ty)
-				},
-				// We should not see anything different than type params here.
-				r => r.clone(),
-			}
-		});
 
-		// Generate the generator function
-		result.push(quote!(
-			#[cfg(any(feature = "std", test))]
-			pub fn #fn_name<
-				'a, ApiImpl: #trait_ #ty_generics, NodeBlock: #crate_::BlockT
-				#(, #impl_generics_params)*
-			>(
-				#( #fn_inputs ),*
-			) -> impl FnOnce() -> #output + 'a #where_clause {
-				move || {
-					#( #conversions )*
-					let res = ApiImpl::#trait_fn_name(#( #input_borrows #input_names ),*);
-					#output_conversion
-				}
-			}
-		));
+		result.push(versioned_trait);
 	}
 
-	Ok(quote!( #( #result )* ))
+	result
 }
 
 /// Try to parse the given `Attribute` as `renamed` attribute.
@@ -323,126 +211,13 @@ fn parse_renamed_attribute(renamed: &Attribute) -> Result<(String, u32)> {
 	}
 }
 
-/// Generate the functions that call the api at a given block for a given trait method.
-fn generate_call_api_at_calls(decl: &ItemTrait) -> Result<TokenStream> {
-	let fns = decl.items.iter().filter_map(|i| match i {
-		TraitItem::Method(ref m) => Some((&m.attrs, &m.sig)),
-		_ => None,
-	});
-
-	let mut result = Vec::new();
-	let crate_ = generate_crate_access(HIDDEN_INCLUDES_ID);
-
-	// Generate a native call generator for each function of the given trait.
-	for (attrs, fn_) in fns {
-		let trait_name = &decl.ident;
-		let trait_fn_name = prefix_function_with_trait(trait_name, &fn_.ident);
-		let fn_name = generate_call_api_at_fn_name(&fn_.ident);
-
-		let attrs = remove_supported_attributes(&mut attrs.clone());
-
-		if attrs.contains_key(RENAMED_ATTRIBUTE) && attrs.contains_key(CHANGED_IN_ATTRIBUTE) {
-			return Err(Error::new(
-				fn_.span(),
-				format!(
-					"`{}` and `{}` are not supported at once.",
-					RENAMED_ATTRIBUTE, CHANGED_IN_ATTRIBUTE
-				),
-			))
-		}
-
-		// We do not need to generate this function for a method that signature was changed.
-		if attrs.contains_key(CHANGED_IN_ATTRIBUTE) {
-			continue
-		}
-
-		// Parse the renamed attributes.
-		let mut renames = Vec::new();
-		if let Some((_, a)) = attrs.iter().find(|a| a.0 == &RENAMED_ATTRIBUTE) {
-			let (old_name, version) = parse_renamed_attribute(a)?;
-			renames.push((version, prefix_function_with_trait(trait_name, &old_name)));
-		}
-
-		renames.sort_by(|l, r| r.cmp(l));
-		let (versions, old_names) = renames.into_iter().fold(
-			(Vec::new(), Vec::new()),
-			|(mut versions, mut old_names), (version, old_name)| {
-				versions.push(version);
-				old_names.push(old_name);
-				(versions, old_names)
-			},
-		);
-
-		// Generate the generator function
-		result.push(quote!(
-			#[cfg(any(feature = "std", test))]
-			#[allow(clippy::too_many_arguments)]
-			pub fn #fn_name<
-				R: #crate_::Encode + #crate_::Decode + std::cmp::PartialEq,
-				NC: FnOnce() -> std::result::Result<R, #crate_::ApiError> + std::panic::UnwindSafe,
-				Block: #crate_::BlockT,
-				T: #crate_::CallApiAt<Block>,
-			>(
-				call_runtime_at: &T,
-				at: &#crate_::BlockId<Block>,
-				args: std::vec::Vec<u8>,
-				changes: &std::cell::RefCell<#crate_::OverlayedChanges>,
-				storage_transaction_cache: &std::cell::RefCell<
-					#crate_::StorageTransactionCache<Block, T::StateBackend>
-				>,
-				native_call: std::option::Option<NC>,
-				context: #crate_::ExecutionContext,
-				recorder: &std::option::Option<#crate_::ProofRecorder<Block>>,
-			) -> std::result::Result<#crate_::NativeOrEncoded<R>, #crate_::ApiError> {
-				let version = call_runtime_at.runtime_version_at(at)?;
-
-				#(
-					// Check if we need to call the function by an old name.
-					if version.apis.iter().any(|(s, v)| {
-						s == &ID && *v < #versions
-					}) {
-						let params = #crate_::CallApiAtParams::<_, fn() -> _, _> {
-							at,
-							function: #old_names,
-							native_call: None,
-							arguments: args,
-							overlayed_changes: changes,
-							storage_transaction_cache,
-							context,
-							recorder,
-						};
-
-						let ret = #crate_::CallApiAt::<Block>::call_api_at(call_runtime_at, params)?;
-
-						return Ok(ret)
-					}
-				)*
-
-				let params = #crate_::CallApiAtParams {
-					at,
-					function: #trait_fn_name,
-					native_call,
-					arguments: args,
-					overlayed_changes: changes,
-					storage_transaction_cache,
-					context,
-					recorder,
-				};
-
-				#crate_::CallApiAt::<Block>::call_api_at(call_runtime_at, params)
-			}
-		));
-	}
-
-	Ok(quote!( #( #result )* ))
-}
-
 /// Generate the declaration of the trait for the runtime.
 fn generate_runtime_decls(decls: &[ItemTrait]) -> Result<TokenStream> {
 	let mut result = Vec::new();
 
 	for decl in decls {
 		let mut decl = decl.clone();
+		let decl_span = decl.span();
 		extend_generics_with_block(&mut decl.generics);
 		let mod_name = generate_runtime_mod_name_for_trait(&decl.ident);
 		let found_attributes = remove_supported_attributes(&mut decl.attrs);
@@ -450,30 +225,73 @@ fn generate_runtime_decls(decls: &[ItemTrait]) -> Result<TokenStream> {
 			get_api_version(&found_attributes).map(|v| generate_runtime_api_version(v as u32))?;
 		let id = generate_runtime_api_id(&decl.ident.to_string());
 
-		let call_api_at_calls = generate_call_api_at_calls(&decl)?;
-
-		// Remove methods that have the `changed_in` attribute as they are not required for the
-		// runtime anymore.
-		decl.items = decl
-			.items
-			.iter_mut()
-			.filter_map(|i| match i {
-				TraitItem::Method(ref mut method) => {
-					if remove_supported_attributes(&mut method.attrs)
-						.contains_key(CHANGED_IN_ATTRIBUTE)
-					{
-						None
-					} else {
-						// Make sure we replace all the wild card parameter names.
-						replace_wild_card_parameter_names(&mut method.sig);
-						Some(TraitItem::Method(method.clone()))
-					}
-				},
-				r => Some(r.clone()),
-			})
-			.collect();
+		let trait_api_version = get_api_version(&found_attributes)?;
+
+		let mut methods_by_version: BTreeMap<u64, Vec<TraitItemMethod>> = BTreeMap::new();
+
+		// Process the items in the declaration. The filter_map function below does a lot of stuff
+		// because the method attributes are stripped at this point
+		decl.items.iter_mut().for_each(|i| match i {
+			TraitItem::Method(ref mut method) => {
+				let method_attrs = remove_supported_attributes(&mut method.attrs);
+				let mut method_version = trait_api_version;
+				// validate the api version for the method (if any) and generate default
+				// implementation for versioned methods
+				if let Some(version_attribute) = method_attrs.get(API_VERSION_ATTRIBUTE) {
+					method_version = match parse_runtime_api_version(version_attribute) {
+						Ok(method_api_ver) if method_api_ver < trait_api_version => {
+							let method_ver = method_api_ver.to_string();
+							let trait_ver = trait_api_version.to_string();
+							let mut err1 = Error::new(
+								version_attribute.span(),
+								format!(
+										"Method version `{}` is older than (or equal to) trait version `{}`.\
+										 Methods can't define versions older than the trait version.",
+										method_ver,
+										trait_ver,
+									),
+							);
+
+							let err2 = match found_attributes.get(&API_VERSION_ATTRIBUTE) {
+								Some(attr) => Error::new(attr.span(), "Trait version is set here."),
+								None => Error::new(
+									decl_span,
+									"Trait version is not set so it is implicitly equal to 1.",
+								),
+							};
+							err1.combine(err2);
+							result.push(err1.to_compile_error());
+
+							trait_api_version
+						},
+						Ok(method_api_ver) => method_api_ver,
+						Err(e) => {
+							result.push(e.to_compile_error());
+							trait_api_version
+						},
+					};
+				}
 
-		let native_call_generators = generate_native_call_generators(&decl)?;
+				// Any method with the `changed_in` attribute isn't required for the runtime
+				// anymore.
+				if !method_attrs.contains_key(CHANGED_IN_ATTRIBUTE) {
+					// Make sure we replace all the wild card parameter names.
+					replace_wild_card_parameter_names(&mut method.sig);
+
+					// partition methods by api version
+					methods_by_version.entry(method_version).or_default().push(method.clone());
+				}
+			},
+			_ => (),
+		});
+
+		let versioned_api_traits = generate_versioned_api_traits(decl.clone(), methods_by_version);
+
+		let main_api_ident = decl.ident.clone();
+		let versioned_ident = &versioned_api_traits
+			.first()
+			.expect("There should always be at least one version.")
+			.ident;
 
 		result.push(quote!(
 			#[doc(hidden)]
@@ -482,15 +300,13 @@ fn generate_runtime_decls(decls: &[ItemTrait]) -> Result<TokenStream> {
 			pub mod #mod_name {
 				use super::*;
 
-				#decl
+				#( #versioned_api_traits )*
+
+				pub use #versioned_ident as #main_api_ident;
 
 				pub #api_version
 
 				pub #id
-
-				#native_call_generators
-
-				#call_api_at_calls
 			}
 		));
 	}
@@ -509,18 +325,45 @@ struct ToClientSideDecl<'a> {
 }
 
 impl<'a> ToClientSideDecl<'a> {
-	fn fold_item_trait_items(&mut self, items: Vec<TraitItem>) -> Vec<TraitItem> {
+	/// Process the given [`ItemTrait`].
+	fn process(mut self, decl: ItemTrait) -> ItemTrait {
+		let mut decl = self.fold_item_trait(decl);
+
+		let block_id = self.block_id;
+		let crate_ = self.crate_;
+
+		// Add the special method that will be implemented by the `impl_runtime_apis!` macro
+		// to enable functions to call into the runtime.
+		decl.items.push(parse_quote! {
+			/// !!INTERNAL USE ONLY!!
+			#[doc(hidden)]
+			fn __runtime_api_internal_call_api_at(
+				&self,
+				at: &#block_id,
+				context: #crate_::ExecutionContext,
+				params: std::vec::Vec<u8>,
+				fn_name: &dyn Fn(#crate_::RuntimeVersion) -> &'static str,
+			) -> std::result::Result<std::vec::Vec<u8>, #crate_::ApiError>;
+		});
+
+		decl
+	}
+}
+
+impl<'a> ToClientSideDecl<'a> {
+	fn fold_item_trait_items(
+		&mut self,
+		items: Vec<TraitItem>,
+		trait_generics_num: usize,
+	) -> Vec<TraitItem> {
 		let mut result = Vec::new();
 
 		items.into_iter().for_each(|i| match i {
 			TraitItem::Method(method) => {
-				let (fn_decl, fn_impl, fn_decl_ctx) = self.fold_trait_item_method(method);
+				let (fn_decl, fn_decl_ctx) =
+					self.fold_trait_item_method(method, trait_generics_num);
 				result.push(fn_decl.into());
 				result.push(fn_decl_ctx.into());
-
-				if let Some(fn_impl) = fn_impl {
-					result.push(fn_impl.into());
-				}
 			},
 			r => result.push(r),
 		});
@@ -531,20 +374,24 @@ impl<'a> ToClientSideDecl<'a> {
 	fn fold_trait_item_method(
 		&mut self,
 		method: TraitItemMethod,
-	) -> (TraitItemMethod, Option<TraitItemMethod>, TraitItemMethod) {
+		trait_generics_num: usize,
+	) -> (TraitItemMethod, TraitItemMethod) {
 		let crate_ = self.crate_;
 		let context = quote!( #crate_::ExecutionContext::OffchainCall(None) );
-		let fn_impl = self.create_method_runtime_api_impl(method.clone());
-		let fn_decl = self.create_method_decl(method.clone(), context);
-		let fn_decl_ctx = self.create_method_decl_with_context(method);
+		let fn_decl = self.create_method_decl(method.clone(), context, trait_generics_num);
+		let fn_decl_ctx = self.create_method_decl_with_context(method, trait_generics_num);
 
-		(fn_decl, fn_impl, fn_decl_ctx)
+		(fn_decl, fn_decl_ctx)
 	}
 
-	fn create_method_decl_with_context(&mut self, method: TraitItemMethod) -> TraitItemMethod {
+	fn create_method_decl_with_context(
+		&mut self,
+		method: TraitItemMethod,
+		trait_generics_num: usize,
+	) -> TraitItemMethod {
 		let crate_ = self.crate_;
 		let context_arg: syn::FnArg = parse_quote!( context: #crate_::ExecutionContext );
-		let mut fn_decl_ctx = self.create_method_decl(method, quote!(context));
+		let mut fn_decl_ctx = self.create_method_decl(method, quote!(context), trait_generics_num);
 		fn_decl_ctx.sig.ident =
 			Ident::new(&format!("{}_with_context", &fn_decl_ctx.sig.ident), Span::call_site());
 		fn_decl_ctx.sig.inputs.insert(2, context_arg);
@@ -552,52 +399,6 @@ impl<'a> ToClientSideDecl<'a> {
 		fn_decl_ctx
 	}
 
-	/// Takes the given method and creates a `method_runtime_api_impl` method that will be
-	/// implemented in the runtime for the client side.
-	fn create_method_runtime_api_impl(
-		&mut self,
-		mut method: TraitItemMethod,
-	) -> Option<TraitItemMethod> {
-		if remove_supported_attributes(&mut method.attrs).contains_key(CHANGED_IN_ATTRIBUTE) {
-			return None
-		}
-
-		let fn_sig = &method.sig;
-		let ret_type = return_type_extract_type(&fn_sig.output);
-
-		// Get types and if the value is borrowed from all parameters.
-		// If there is an error, we push it as the block to the user.
-		let param_types =
-			match extract_parameter_names_types_and_borrows(fn_sig, AllowSelfRefInParameters::No) {
-				Ok(res) => res
-					.into_iter()
-					.map(|v| {
-						let ty = v.1;
-						let borrow = v.2;
-						quote!( #borrow #ty )
-					})
-					.collect::<Vec<_>>(),
-				Err(e) => {
-					self.errors.push(e.to_compile_error());
-					Vec::new()
-				},
-			};
-		let name = generate_method_runtime_api_impl_name(self.trait_, &method.sig.ident);
-		let block_id = self.block_id;
-		let crate_ = self.crate_;
-
-		Some(parse_quote! {
-			#[doc(hidden)]
-			fn #name(
-				&self,
-				at: &#block_id,
-				context: #crate_::ExecutionContext,
-				params: Option<( #( #param_types ),* )>,
-				params_encoded: Vec<u8>,
-			) -> std::result::Result<#crate_::NativeOrEncoded<#ret_type>, #crate_::ApiError>;
-		})
-	}
-
 	/// Takes the method declared by the user and creates the declaration we require for the runtime
 	/// api client side. This method will call by default the `method_runtime_api_impl` for doing
 	/// the actual call into the runtime.
@@ -605,6 +406,7 @@ impl<'a> ToClientSideDecl<'a> {
 		&mut self,
 		mut method: TraitItemMethod,
 		context: TokenStream,
+		trait_generics_num: usize,
 	) -> TraitItemMethod {
 		let params = match extract_parameter_names_types_and_borrows(
 			&method.sig,
@@ -616,18 +418,42 @@ impl<'a> ToClientSideDecl<'a> {
 				Vec::new()
 			},
 		};
-		let params2 = params.clone();
 		let ret_type = return_type_extract_type(&method.sig.output);
 
 		fold_fn_decl_for_client_side(&mut method.sig, self.block_id, self.crate_);
 
-		let name_impl = generate_method_runtime_api_impl_name(self.trait_, &method.sig.ident);
 		let crate_ = self.crate_;
 
 		let found_attributes = remove_supported_attributes(&mut method.attrs);
+
+		// Parse the renamed attributes.
+		let mut renames = Vec::new();
+		for (_, a) in found_attributes.iter().filter(|a| a.0 == &RENAMED_ATTRIBUTE) {
+			match parse_renamed_attribute(a) {
+				Ok((old_name, version)) => {
+					renames.push((version, prefix_function_with_trait(&self.trait_, &old_name)));
+				},
+				Err(e) => self.errors.push(e.to_compile_error()),
+			}
+		}
+
+		renames.sort_by(|l, r| r.cmp(l));
+		let (versions, old_names) = renames.into_iter().fold(
+			(Vec::new(), Vec::new()),
+			|(mut versions, mut old_names), (version, old_name)| {
+				versions.push(version);
+				old_names.push(old_name);
+				(versions, old_names)
+			},
+		);
+
+		// Generate the function name before we may rename it below to
+		// `function_name_before_version_{}`.
+		let function_name = prefix_function_with_trait(&self.trait_, &method.sig.ident);
+
 		// If the method has a `changed_in` attribute, we need to alter the method name to
 		// `method_before_version_VERSION`.
-		let (native_handling, param_tuple) = match get_changed_in(&found_attributes) {
+		match get_changed_in(&found_attributes) {
 			Ok(Some(version)) => {
 				// Make sure that the `changed_in` version is at least the current `api_version`.
 				if get_api_version(self.found_attributes).ok() < Some(version) {
@@ -646,47 +472,51 @@ impl<'a> ToClientSideDecl<'a> {
 				);
 				method.sig.ident = ident;
 				method.attrs.push(parse_quote!( #[deprecated] ));
-
-				let panic =
-					format!("Calling `{}` should not return a native value!", method.sig.ident);
-				(quote!(panic!(#panic)), quote!(None))
 			},
-			Ok(None) => (quote!(Ok(n)), quote!( Some(( #( #params2 ),* )) )),
+			Ok(None) => {},
 			Err(e) => {
 				self.errors.push(e.to_compile_error());
-				(quote!(unimplemented!()), quote!(None))
 			},
 		};
 
-		let function_name = method.sig.ident.to_string();
+		// The module where the runtime relevant stuff is declared.
+		let trait_name = &self.trait_;
+		let runtime_mod = generate_runtime_mod_name_for_trait(trait_name);
+		let underscores = (0..trait_generics_num).map(|_| quote!(_));
 
 		// Generate the default implementation that calls the `method_runtime_api_impl` method.
 		method.default = Some(parse_quote! {
 			{
-				let runtime_api_impl_params_encoded =
+				let __runtime_api_impl_params_encoded__ =
 					#crate_::Encode::encode(&( #( &#params ),* ));
 
-				self.#name_impl(
+				<Self as #trait_name<#( #underscores ),*>>::__runtime_api_internal_call_api_at(
+					self,
 					__runtime_api_at_param__,
 					#context,
-					#param_tuple,
-					runtime_api_impl_params_encoded,
-				).and_then(|r|
-					match r {
-						#crate_::NativeOrEncoded::Native(n) => {
-							#native_handling
-						},
-						#crate_::NativeOrEncoded::Encoded(r) => {
-							std::result::Result::map_err(
-								<#ret_type as #crate_::Decode>::decode(&mut &r[..]),
-								|err| #crate_::ApiError::FailedToDecodeReturnValue {
-									function: #function_name,
-									error: err,
-								}
-							)
-						}
+					__runtime_api_impl_params_encoded__,
+					&|version| {
+						#(
+							// Check if we need to call the function by an old name.
+							if version.apis.iter().any(|(s, v)| {
+								s == &#runtime_mod::ID && *v < #versions
+							}) {
+								return #old_names
+							}
+						)*
+
+						#function_name
 					}
 				)
+				.and_then(|r|
+					std::result::Result::map_err(
+						<#ret_type as #crate_::Decode>::decode(&mut &r[..]),
+						|err| #crate_::ApiError::FailedToDecodeReturnValue {
+							function: #function_name,
+							error: err,
+						}
+					)
+				)
 			}
 		});
 
@@ -714,37 +544,12 @@ impl<'a> Fold for ToClientSideDecl<'a> {
 
 		// The client side trait is only required when compiling with the feature `std` or `test`.
 		input.attrs.push(parse_quote!( #[cfg(any(feature = "std", test))] ));
-		input.items = self.fold_item_trait_items(input.items);
+		input.items = self.fold_item_trait_items(input.items, input.generics.params.len());
 
 		fold::fold_item_trait(self, input)
 	}
 }
 
-/// Parse the given attribute as `API_VERSION_ATTRIBUTE`.
-fn parse_runtime_api_version(version: &Attribute) -> Result<u64> {
-	let meta = version.parse_meta()?;
-
-	let err = Err(Error::new(
-		meta.span(),
-		&format!(
-			"Unexpected `{api_version}` attribute. The supported format is `{api_version}(1)`",
-			api_version = API_VERSION_ATTRIBUTE
-		),
-	));
-
-	match meta {
-		Meta::List(list) =>
-			if list.nested.len() != 1 {
-				err
-			} else if let Some(NestedMeta::Lit(Lit::Int(i))) = list.nested.first() {
-				i.base10_parse()
-			} else {
-				err
-			},
-		_ => err,
-	}
-}
-
 /// Generates the identifier as const variable for the given `trait_name`
 /// by hashing the `trait_name`.
 fn generate_runtime_api_id(trait_name: &str) -> TokenStream {
@@ -821,16 +626,14 @@ fn generate_client_side_decls(decls: &[ItemTrait]) -> Result<TokenStream> {
 		let mut errors = Vec::new();
 		let trait_ = decl.ident.clone();
 
-		let decl = {
-			let mut to_client_side = ToClientSideDecl {
-				crate_: &crate_,
-				block_id: &block_id,
-				found_attributes: &mut found_attributes,
-				errors: &mut errors,
-				trait_: &trait_,
-			};
-			to_client_side.fold_item_trait(decl)
-		};
+		let decl = ToClientSideDecl {
+			crate_: &crate_,
+			block_id: &block_id,
+			found_attributes: &mut found_attributes,
+			errors: &mut errors,
+			trait_: &trait_,
+		}
+		.process(decl);
 
 		let api_version = get_api_version(&found_attributes);
 
diff --git a/substrate/primitives/api/proc-macro/src/impl_runtime_apis.rs b/substrate/primitives/api/proc-macro/src/impl_runtime_apis.rs
index 02ef37370ff..0087a1ad37a 100644
--- a/substrate/primitives/api/proc-macro/src/impl_runtime_apis.rs
+++ b/substrate/primitives/api/proc-macro/src/impl_runtime_apis.rs
@@ -17,13 +17,13 @@
 
 use crate::utils::{
 	extract_all_signature_types, extract_block_type_from_trait_path, extract_impl_trait,
-	extract_parameter_names_types_and_borrows, generate_call_api_at_fn_name, generate_crate_access,
-	generate_hidden_includes, generate_method_runtime_api_impl_name,
-	generate_native_call_generator_fn_name, generate_runtime_mod_name_for_trait,
-	prefix_function_with_trait, return_type_extract_type, AllowSelfRefInParameters,
-	RequireQualifiedTraitPath,
+	extract_parameter_names_types_and_borrows, generate_crate_access, generate_hidden_includes,
+	generate_runtime_mod_name_for_trait, parse_runtime_api_version, prefix_function_with_trait,
+	versioned_trait_name, AllowSelfRefInParameters, RequireQualifiedTraitPath,
 };
 
+use crate::common::API_VERSION_ATTRIBUTE;
+
 use proc_macro2::{Span, TokenStream};
 
 use quote::quote;
@@ -33,8 +33,7 @@ use syn::{
 	parse::{Error, Parse, ParseStream, Result},
 	parse_macro_input, parse_quote,
 	spanned::Spanned,
-	Attribute, GenericArgument, Ident, ImplItem, ItemImpl, Path, PathArguments, Signature, Type,
-	TypePath,
+	Attribute, Ident, ImplItem, ItemImpl, Path, Signature, Type, TypePath,
 };
 
 use std::collections::HashSet;
@@ -105,8 +104,10 @@ fn generate_impl_calls(
 	let mut impl_calls = Vec::new();
 
 	for impl_ in impls {
+		let trait_api_ver = extract_api_version(&impl_.attrs, impl_.span())?;
 		let impl_trait_path = extract_impl_trait(impl_, RequireQualifiedTraitPath::Yes)?;
 		let impl_trait = extend_with_runtime_decl_path(impl_trait_path.clone());
+		let impl_trait = extend_with_api_version(impl_trait, trait_api_ver);
 		let impl_trait_ident = &impl_trait_path
 			.segments
 			.last()
@@ -326,35 +327,6 @@ fn generate_runtime_api_base_structures() -> Result<TokenStream> {
 
 		#[cfg(any(feature = "std", test))]
 		impl<Block: #crate_::BlockT, C: #crate_::CallApiAt<Block>> RuntimeApiImpl<Block, C> {
-			fn call_api_at<
-				R: #crate_::Encode + #crate_::Decode + std::cmp::PartialEq,
-				F: FnOnce(
-					&C,
-					&std::cell::RefCell<#crate_::OverlayedChanges>,
-					&std::cell::RefCell<#crate_::StorageTransactionCache<Block, C::StateBackend>>,
-					&std::option::Option<#crate_::ProofRecorder<Block>>,
-				) -> std::result::Result<#crate_::NativeOrEncoded<R>, E>,
-				E,
-			>(
-				&self,
-				call_api_at: F,
-			) -> std::result::Result<#crate_::NativeOrEncoded<R>, E> {
-				if *std::cell::RefCell::borrow(&self.commit_on_success) {
-					#crate_::OverlayedChanges::start_transaction(
-						&mut std::cell::RefCell::borrow_mut(&self.changes)
-					);
-				}
-				let res = call_api_at(
-					&self.call,
-					&self.changes,
-					&self.storage_transaction_cache,
-					&self.recorder,
-				);
-
-				self.commit_or_rollback(std::result::Result::is_ok(&res));
-				res
-			}
-
 			fn commit_or_rollback(&self, commit: bool) {
 				let proof = "\
 					We only close a transaction when we opened one ourself.
@@ -398,6 +370,24 @@ fn extend_with_runtime_decl_path(mut trait_: Path) -> Path {
 	trait_
 }
 
+fn extend_with_api_version(mut trait_: Path, version: Option<u64>) -> Path {
+	let version = if let Some(v) = version {
+		v
+	} else {
+		// nothing to do
+		return trait_
+	};
+
+	let trait_name = &mut trait_
+		.segments
+		.last_mut()
+		.expect("Trait path should always contain at least one item; qed")
+		.ident;
+	*trait_name = versioned_trait_name(trait_name, version);
+
+	trait_
+}
+
 /// Generates the implementations of the apis for the runtime.
 fn generate_api_impl_for_runtime(impls: &[ItemImpl]) -> Result<TokenStream> {
 	let mut impls_prepared = Vec::new();
@@ -405,9 +395,12 @@ fn generate_api_impl_for_runtime(impls: &[ItemImpl]) -> Result<TokenStream> {
 	// We put `runtime` before each trait to get the trait that is intended for the runtime and
 	// we put the `RuntimeBlock` as first argument for the trait generics.
 	for impl_ in impls.iter() {
+		let trait_api_ver = extract_api_version(&impl_.attrs, impl_.span())?;
+
 		let mut impl_ = impl_.clone();
 		let trait_ = extract_impl_trait(&impl_, RequireQualifiedTraitPath::Yes)?.clone();
 		let trait_ = extend_with_runtime_decl_path(trait_);
+		let trait_ = extend_with_api_version(trait_, trait_api_ver);
 
 		impl_.trait_.as_mut().unwrap().1 = trait_;
 		impl_.attrs = filter_cfg_attrs(&impl_.attrs);
@@ -424,121 +417,70 @@ fn generate_api_impl_for_runtime(impls: &[ItemImpl]) -> Result<TokenStream> {
 /// with code that calls into the runtime.
 struct ApiRuntimeImplToApiRuntimeApiImpl<'a> {
 	runtime_block: &'a TypePath,
-	runtime_mod_path: &'a Path,
-	runtime_type: &'a Type,
-	trait_generic_arguments: &'a [GenericArgument],
-	impl_trait: &'a Ident,
 }
 
-impl<'a> Fold for ApiRuntimeImplToApiRuntimeApiImpl<'a> {
-	fn fold_type_path(&mut self, input: TypePath) -> TypePath {
-		let new_ty_path =
-			if input == *self.runtime_block { parse_quote!(__SR_API_BLOCK__) } else { input };
-
-		fold::fold_type_path(self, new_ty_path)
-	}
+impl<'a> ApiRuntimeImplToApiRuntimeApiImpl<'a> {
+	/// Process the given item implementation.
+	fn process(mut self, input: ItemImpl) -> ItemImpl {
+		let mut input = self.fold_item_impl(input);
 
-	fn fold_impl_item_method(&mut self, mut input: syn::ImplItemMethod) -> syn::ImplItemMethod {
-		let block = {
-			let runtime_mod_path = self.runtime_mod_path;
-			let runtime = self.runtime_type;
-			let native_call_generator_ident =
-				generate_native_call_generator_fn_name(&input.sig.ident);
-			let call_api_at_call = generate_call_api_at_fn_name(&input.sig.ident);
-			let trait_generic_arguments = self.trait_generic_arguments;
-			let crate_ = generate_crate_access(HIDDEN_INCLUDES_ID);
-
-			// Generate the access to the native parameters
-			let param_tuple_access = if input.sig.inputs.len() == 1 {
-				vec![quote!(p)]
-			} else {
-				input
-					.sig
-					.inputs
-					.iter()
-					.enumerate()
-					.map(|(i, _)| {
-						let i = syn::Index::from(i);
-						quote!( p.#i )
-					})
-					.collect::<Vec<_>>()
-			};
-
-			let (param_types, error) = match extract_parameter_names_types_and_borrows(
-				&input.sig,
-				AllowSelfRefInParameters::No,
-			) {
-				Ok(res) => (
-					res.into_iter()
-						.map(|v| {
-							let ty = v.1;
-							let borrow = v.2;
-							quote!( #borrow #ty )
-						})
-						.collect::<Vec<_>>(),
-					None,
-				),
-				Err(e) => (Vec::new(), Some(e.to_compile_error())),
-			};
+		let crate_ = generate_crate_access(HIDDEN_INCLUDES_ID);
 
-			// Rewrite the input parameters.
-			input.sig.inputs = parse_quote! {
+		// Delete all functions, because all of them are default implemented by
+		// `decl_runtime_apis!`. We only need to implement the `__runtime_api_internal_call_api_at`
+		// function.
+		input.items.clear();
+		input.items.push(parse_quote! {
+			fn __runtime_api_internal_call_api_at(
 				&self,
 				at: &#crate_::BlockId<__SR_API_BLOCK__>,
 				context: #crate_::ExecutionContext,
-				params: Option<( #( #param_types ),* )>,
-				params_encoded: Vec<u8>,
-			};
-
-			input.sig.ident =
-				generate_method_runtime_api_impl_name(self.impl_trait, &input.sig.ident);
-			let ret_type = return_type_extract_type(&input.sig.output);
-
-			// Generate the correct return type.
-			input.sig.output = parse_quote!(
-				-> std::result::Result<#crate_::NativeOrEncoded<#ret_type>, #crate_::ApiError>
-			);
-
-			// Generate the new method implementation that calls into the runtime.
-			parse_quote!(
-				{
-					// Get the error to the user (if we have one).
-					#error
-
-					self.call_api_at(
-						|
-							call_runtime_at,
-							changes,
-							storage_transaction_cache,
-							recorder
-						| {
-							#runtime_mod_path #call_api_at_call(
-								call_runtime_at,
-								at,
-								params_encoded,
-								changes,
-								storage_transaction_cache,
-								params.map(|p| {
-									#runtime_mod_path #native_call_generator_ident ::
-										<#runtime, __SR_API_BLOCK__ #(, #trait_generic_arguments )*> (
-										#( #param_tuple_access ),*
-									)
-								}),
-								context,
-								recorder,
-							)
-						}
-					)
+				params: std::vec::Vec<u8>,
+				fn_name: &dyn Fn(#crate_::RuntimeVersion) -> &'static str,
+			) -> std::result::Result<std::vec::Vec<u8>, #crate_::ApiError> {
+				if *std::cell::RefCell::borrow(&self.commit_on_success) {
+					#crate_::OverlayedChanges::start_transaction(
+						&mut std::cell::RefCell::borrow_mut(&self.changes)
+					);
 				}
-			)
-		};
 
-		let mut input = fold::fold_impl_item_method(self, input);
-		// We need to set the block, after we modified the rest of the ast, otherwise we would
-		// modify our generated block as well.
-		input.block = block;
+				let res = (|| {
+				let version = #crate_::CallApiAt::<__SR_API_BLOCK__>::runtime_version_at(self.call, at)?;
+
+				let params = #crate_::CallApiAtParams::<_, fn() -> _, _> {
+					at,
+					function: (*fn_name)(version),
+					native_call: None,
+					arguments: params,
+					overlayed_changes: &self.changes,
+					storage_transaction_cache: &self.storage_transaction_cache,
+					context,
+					recorder: &self.recorder,
+				};
+
+				#crate_::CallApiAt::<__SR_API_BLOCK__>::call_api_at::<#crate_::NeverNativeValue, _>(
+					self.call,
+					params,
+				)
+			})();
+
+				self.commit_or_rollback(std::result::Result::is_ok(&res));
+
+				res.map(#crate_::NativeOrEncoded::into_encoded)
+			}
+		});
+
 		input
 	}
+}
+
+impl<'a> Fold for ApiRuntimeImplToApiRuntimeApiImpl<'a> {
+	fn fold_type_path(&mut self, input: TypePath) -> TypePath {
+		let new_ty_path =
+			if input == *self.runtime_block { parse_quote!(__SR_API_BLOCK__) } else { input };
+
+		fold::fold_type_path(self, new_ty_path)
+	}
 
 	fn fold_item_impl(&mut self, mut input: ItemImpl) -> ItemImpl {
 		// All this `UnwindSafe` magic below here is required for this rust bug:
@@ -594,45 +536,55 @@ fn generate_api_impl_for_runtime_api(impls: &[ItemImpl]) -> Result<TokenStream>
 
 	for impl_ in impls {
 		let impl_trait_path = extract_impl_trait(impl_, RequireQualifiedTraitPath::Yes)?;
-		let impl_trait = &impl_trait_path
-			.segments
-			.last()
-			.ok_or_else(|| Error::new(impl_trait_path.span(), "Empty trait path not possible!"))?
-			.clone();
 		let runtime_block = extract_block_type_from_trait_path(impl_trait_path)?;
-		let runtime_type = &impl_.self_ty;
 		let mut runtime_mod_path = extend_with_runtime_decl_path(impl_trait_path.clone());
 		// remove the trait to get just the module path
 		runtime_mod_path.segments.pop();
 
-		let trait_generic_arguments = match impl_trait.arguments {
-			PathArguments::Parenthesized(_) | PathArguments::None => vec![],
-			PathArguments::AngleBracketed(ref b) => b.args.iter().cloned().collect(),
-		};
-
-		let mut visitor = ApiRuntimeImplToApiRuntimeApiImpl {
-			runtime_block,
-			runtime_mod_path: &runtime_mod_path,
-			runtime_type,
-			trait_generic_arguments: &trait_generic_arguments,
-			impl_trait: &impl_trait.ident,
-		};
+		let processed_impl =
+			ApiRuntimeImplToApiRuntimeApiImpl { runtime_block }.process(impl_.clone());
 
-		result.push(visitor.fold_item_impl(impl_.clone()));
+		result.push(processed_impl);
 	}
 	Ok(quote!( #( #result )* ))
 }
 
+fn populate_runtime_api_versions(
+	result: &mut Vec<TokenStream>,
+	sections: &mut Vec<TokenStream>,
+	attrs: Vec<Attribute>,
+	id: Path,
+	version: TokenStream,
+	crate_access: &TokenStream,
+) {
+	result.push(quote!(
+			#( #attrs )*
+			(#id, #version)
+	));
+
+	sections.push(quote!(
+		#( #attrs )*
+		const _: () = {
+			// All sections with the same name are going to be merged by concatenation.
+			#[cfg(not(feature = "std"))]
+			#[link_section = "runtime_apis"]
+			static SECTION_CONTENTS: [u8; 12] = #crate_access::serialize_runtime_api_info(#id, #version);
+		};
+	));
+}
+
 /// Generates `RUNTIME_API_VERSIONS` that holds all version information about the implemented
 /// runtime apis.
 fn generate_runtime_api_versions(impls: &[ItemImpl]) -> Result<TokenStream> {
-	let mut result = Vec::with_capacity(impls.len());
-	let mut sections = Vec::with_capacity(impls.len());
+	let mut result = Vec::<TokenStream>::with_capacity(impls.len());
+	let mut sections = Vec::<TokenStream>::with_capacity(impls.len());
 	let mut processed_traits = HashSet::new();
 
 	let c = generate_crate_access(HIDDEN_INCLUDES_ID);
 
 	for impl_ in impls {
+		let api_ver = extract_api_version(&impl_.attrs, impl_.span())?.map(|a| a as u32);
+
 		let mut path = extend_with_runtime_decl_path(
 			extract_impl_trait(impl_, RequireQualifiedTraitPath::Yes)?.clone(),
 		);
@@ -655,23 +607,11 @@ fn generate_runtime_api_versions(impls: &[ItemImpl]) -> Result<TokenStream> {
 		}
 
 		let id: Path = parse_quote!( #path ID );
-		let version: Path = parse_quote!( #path VERSION );
+		let version = quote!( #path VERSION );
 		let attrs = filter_cfg_attrs(&impl_.attrs);
 
-		result.push(quote!(
-			#( #attrs )*
-			(#id, #version)
-		));
-
-		sections.push(quote!(
-			#( #attrs )*
-			const _: () = {
-				// All sections with the same name are going to be merged by concatenation.
-				#[cfg(not(feature = "std"))]
-				#[link_section = "runtime_apis"]
-				static SECTION_CONTENTS: [u8; 12] = #c::serialize_runtime_api_info(#id, #version);
-			};
-		));
+		let api_ver = api_ver.map(|a| quote!( #a )).unwrap_or_else(|| version);
+		populate_runtime_api_versions(&mut result, &mut sections, attrs, id, api_ver, &c)
 	}
 
 	Ok(quote!(
@@ -726,6 +666,33 @@ fn filter_cfg_attrs(attrs: &[Attribute]) -> Vec<Attribute> {
 	attrs.iter().filter(|a| a.path.is_ident("cfg")).cloned().collect()
 }
 
+// Extracts the value of `API_VERSION_ATTRIBUTE` and handles errors.
+// Returns:
+// - Err if the version is malformed
+// - Some(u64) if the version is set
+// - None if the version is not set (this is valid).
+fn extract_api_version(attrs: &Vec<Attribute>, span: Span) -> Result<Option<u64>> {
+	// First fetch all `API_VERSION_ATTRIBUTE` values (should be only one)
+	let api_ver = attrs
+		.iter()
+		.filter(|a| a.path.is_ident(API_VERSION_ATTRIBUTE))
+		.collect::<Vec<_>>();
+
+	if api_ver.len() > 1 {
+		return Err(Error::new(
+			span,
+			format!(
+				"Found multiple #[{}] attributes for an API implementation. \
+				Each runtime API can have only one version.",
+				API_VERSION_ATTRIBUTE
+			),
+		))
+	}
+
+	// Parse the runtime version if there exists one.
+	api_ver.first().map(|v| parse_runtime_api_version(v)).transpose()
+}
+
 #[cfg(test)]
 mod tests {
 	use super::*;
diff --git a/substrate/primitives/api/proc-macro/src/lib.rs b/substrate/primitives/api/proc-macro/src/lib.rs
index 20a2f76f2c8..31636b8e2d5 100644
--- a/substrate/primitives/api/proc-macro/src/lib.rs
+++ b/substrate/primitives/api/proc-macro/src/lib.rs
@@ -21,6 +21,7 @@
 
 use proc_macro::TokenStream;
 
+mod common;
 mod decl_runtime_apis;
 mod impl_runtime_apis;
 mod mock_impl_runtime_apis;
diff --git a/substrate/primitives/api/proc-macro/src/mock_impl_runtime_apis.rs b/substrate/primitives/api/proc-macro/src/mock_impl_runtime_apis.rs
index 6098f8d6bd7..4de0f6b9e57 100644
--- a/substrate/primitives/api/proc-macro/src/mock_impl_runtime_apis.rs
+++ b/substrate/primitives/api/proc-macro/src/mock_impl_runtime_apis.rs
@@ -18,8 +18,7 @@
 use crate::utils::{
 	extract_block_type_from_trait_path, extract_impl_trait,
 	extract_parameter_names_types_and_borrows, generate_crate_access, generate_hidden_includes,
-	generate_method_runtime_api_impl_name, return_type_extract_type, AllowSelfRefInParameters,
-	RequireQualifiedTraitPath,
+	return_type_extract_type, AllowSelfRefInParameters, RequireQualifiedTraitPath,
 };
 
 use proc_macro2::{Span, TokenStream};
@@ -31,7 +30,7 @@ use syn::{
 	parse::{Error, Parse, ParseStream, Result},
 	parse_macro_input, parse_quote,
 	spanned::Spanned,
-	Attribute, Ident, ItemImpl, Pat, Type, TypePath,
+	Attribute, ItemImpl, Pat, Type, TypePath,
 };
 
 /// Unique identifier used to make the hidden includes unique for this macro.
@@ -40,7 +39,7 @@ const HIDDEN_INCLUDES_ID: &str = "MOCK_IMPL_RUNTIME_APIS";
 /// The `advanced` attribute.
 ///
 /// If this attribute is given to a function, the function gets access to the `BlockId` as first
-/// parameter and needs to return a `Result` with the appropiate error type.
+/// parameter and needs to return a `Result` with the appropriate error type.
 const ADVANCED_ATTRIBUTE: &str = "advanced";
 
 /// The structure used for parsing the runtime api implementations.
@@ -126,34 +125,63 @@ fn implement_common_api_traits(block_type: TypePath, self_ty: Type) -> Result<To
 		}
 
 		impl #crate_::Core<#block_type> for #self_ty {
-			fn Core_version_runtime_api_impl(
+			fn __runtime_api_internal_call_api_at(
 				&self,
 				_: &#crate_::BlockId<#block_type>,
 				_: #crate_::ExecutionContext,
-				_: Option<()>,
-				_: Vec<u8>,
-			) -> std::result::Result<#crate_::NativeOrEncoded<#crate_::RuntimeVersion>, #crate_::ApiError> {
-				unimplemented!("Not required for testing!")
+				_: std::vec::Vec<u8>,
+				_: &dyn Fn(#crate_::RuntimeVersion) -> &'static str,
+			) -> std::result::Result<std::vec::Vec<u8>, #crate_::ApiError> {
+				unimplemented!("`__runtime_api_internal_call_api_at` not implemented for runtime api mocks")
 			}
 
-			fn Core_execute_block_runtime_api_impl(
+			fn version(
+				&self,
+				_: &#crate_::BlockId<#block_type>,
+			) -> std::result::Result<#crate_::RuntimeVersion, #crate_::ApiError> {
+				unimplemented!("`Core::version` not implemented for runtime api mocks")
+			}
+
+			fn version_with_context(
+				&self,
+				_: &#crate_::BlockId<#block_type>,
+				_: #crate_::ExecutionContext,
+			) -> std::result::Result<#crate_::RuntimeVersion, #crate_::ApiError> {
+				unimplemented!("`Core::version` not implemented for runtime api mocks")
+			}
+
+			fn execute_block(
+				&self,
+				_: &#crate_::BlockId<#block_type>,
+				_: #block_type,
+			) -> std::result::Result<(), #crate_::ApiError> {
+				unimplemented!("`Core::execute_block` not implemented for runtime api mocks")
+			}
+
+			fn execute_block_with_context(
 				&self,
 				_: &#crate_::BlockId<#block_type>,
 				_: #crate_::ExecutionContext,
-				_: Option<#block_type>,
-				_: Vec<u8>,
-			) -> std::result::Result<#crate_::NativeOrEncoded<()>, #crate_::ApiError> {
-				unimplemented!("Not required for testing!")
+				_: #block_type,
+			) -> std::result::Result<(), #crate_::ApiError> {
+				unimplemented!("`Core::execute_block` not implemented for runtime api mocks")
 			}
 
-			fn Core_initialize_block_runtime_api_impl(
+			fn initialize_block(
+				&self,
+				_: &#crate_::BlockId<#block_type>,
+				_: &<#block_type as #crate_::BlockT>::Header,
+			) -> std::result::Result<(), #crate_::ApiError> {
+				unimplemented!("`Core::initialize_block` not implemented for runtime api mocks")
+			}
+
+			fn initialize_block_with_context(
 				&self,
 				_: &#crate_::BlockId<#block_type>,
 				_: #crate_::ExecutionContext,
-				_: Option<&<#block_type as #crate_::BlockT>::Header>,
-				_: Vec<u8>,
-			) -> std::result::Result<#crate_::NativeOrEncoded<()>, #crate_::ApiError> {
-				unimplemented!("Not required for testing!")
+				_: &<#block_type as #crate_::BlockT>::Header,
+			) -> std::result::Result<(), #crate_::ApiError> {
+				unimplemented!("`Core::initialize_block` not implemented for runtime api mocks")
 			}
 		}
 	))
@@ -216,14 +244,53 @@ fn get_at_param_name(
 	}
 }
 
-/// Auxialiry structure to fold a runtime api trait implementation into the expected format.
+/// Auxiliary structure to fold a runtime api trait implementation into the expected format.
 ///
 /// This renames the methods, changes the method parameters and extracts the error type.
 struct FoldRuntimeApiImpl<'a> {
 	/// The block type that is being used.
 	block_type: &'a TypePath,
-	/// The identifier of the trait being implemented.
-	impl_trait: &'a Ident,
+}
+
+impl<'a> FoldRuntimeApiImpl<'a> {
+	/// Process the given [`syn::ItemImpl`].
+	fn process(mut self, impl_item: syn::ItemImpl) -> syn::ItemImpl {
+		let mut impl_item = self.fold_item_impl(impl_item);
+
+		let crate_ = generate_crate_access(HIDDEN_INCLUDES_ID);
+
+		// We also need to overwrite all the `_with_context` methods. To do this,
+		// we clone all methods and add them again with the new name plus one more argument.
+		impl_item.items.extend(impl_item.items.clone().into_iter().filter_map(|i| {
+			if let syn::ImplItem::Method(mut m) = i {
+				m.sig.ident = quote::format_ident!("{}_with_context", m.sig.ident);
+				m.sig.inputs.insert(2, parse_quote!( _: #crate_::ExecutionContext ));
+
+				Some(m.into())
+			} else {
+				None
+			}
+		}));
+
+		let block_type = self.block_type;
+
+		impl_item.items.push(parse_quote! {
+			fn __runtime_api_internal_call_api_at(
+				&self,
+				_: &#crate_::BlockId<#block_type>,
+				_: #crate_::ExecutionContext,
+				_: std::vec::Vec<u8>,
+				_: &dyn Fn(#crate_::RuntimeVersion) -> &'static str,
+			) -> std::result::Result<std::vec::Vec<u8>, #crate_::ApiError> {
+				unimplemented!(
+					"`__runtime_api_internal_call_api_at` not implemented for runtime api mocks. \
+					 Calling deprecated methods is not supported by mocked runtime api."
+				)
+			}
+		});
+
+		impl_item
+	}
 }
 
 impl<'a> Fold for FoldRuntimeApiImpl<'a> {
@@ -277,14 +344,9 @@ impl<'a> Fold for FoldRuntimeApiImpl<'a> {
 			input.sig.inputs = parse_quote! {
 				&self,
 				#at_param_name: #block_id_type,
-				_: #crate_::ExecutionContext,
-				___params___sp___api___: Option<( #( #param_types ),* )>,
-				_: Vec<u8>,
+				#( #param_names: #param_types ),*
 			};
 
-			input.sig.ident =
-				generate_method_runtime_api_impl_name(self.impl_trait, &input.sig.ident);
-
 			// When using advanced, the user needs to declare the correct return type on its own,
 			// otherwise do it for the user.
 			if !is_advanced {
@@ -292,7 +354,7 @@ impl<'a> Fold for FoldRuntimeApiImpl<'a> {
 
 				// Generate the correct return type.
 				input.sig.output = parse_quote!(
-					-> std::result::Result<#crate_::NativeOrEncoded<#ret_type>, #crate_::ApiError>
+					-> std::result::Result<#ret_type, #crate_::ApiError>
 				);
 			}
 
@@ -304,7 +366,7 @@ impl<'a> Fold for FoldRuntimeApiImpl<'a> {
 				quote! {
 					let __fn_implementation__ = move || #orig_block;
 
-					Ok(#crate_::NativeOrEncoded::Native(__fn_implementation__()))
+					Ok(__fn_implementation__())
 				}
 			};
 
@@ -314,9 +376,6 @@ impl<'a> Fold for FoldRuntimeApiImpl<'a> {
 					// Get the error to the user (if we have one).
 					#( #errors )*
 
-					let (#( #param_names ),*) = ___params___sp___api___
-						.expect("Mocked runtime apis don't support calling deprecated api versions");
-
 					#construct_return_value
 				}
 			)
@@ -351,11 +410,6 @@ fn generate_runtime_api_impls(impls: &[ItemImpl]) -> Result<GeneratedRuntimeApiI
 
 	for impl_ in impls {
 		let impl_trait_path = extract_impl_trait(impl_, RequireQualifiedTraitPath::No)?;
-		let impl_trait = &impl_trait_path
-			.segments
-			.last()
-			.ok_or_else(|| Error::new(impl_trait_path.span(), "Empty trait path not possible!"))?
-			.clone();
 		let block_type = extract_block_type_from_trait_path(impl_trait_path)?;
 
 		self_ty = match self_ty.take() {
@@ -395,9 +449,7 @@ fn generate_runtime_api_impls(impls: &[ItemImpl]) -> Result<GeneratedRuntimeApiI
 			None => Some(block_type.clone()),
 		};
 
-		let mut visitor = FoldRuntimeApiImpl { block_type, impl_trait: &impl_trait.ident };
-
-		result.push(visitor.fold_item_impl(impl_.clone()));
+		result.push(FoldRuntimeApiImpl { block_type }.process(impl_.clone()));
 	}
 
 	Ok(GeneratedRuntimeApiImpls {
diff --git a/substrate/primitives/api/proc-macro/src/utils.rs b/substrate/primitives/api/proc-macro/src/utils.rs
index 97b456b62df..2ccd050cfb1 100644
--- a/substrate/primitives/api/proc-macro/src/utils.rs
+++ b/substrate/primitives/api/proc-macro/src/utils.rs
@@ -18,16 +18,18 @@
 use proc_macro2::{Span, TokenStream};
 
 use syn::{
-	parse_quote, spanned::Spanned, token::And, Error, FnArg, GenericArgument, Ident, ImplItem,
-	ItemImpl, Pat, Path, PathArguments, Result, ReturnType, Signature, Type, TypePath,
+	parse_quote, spanned::Spanned, token::And, Attribute, Error, FnArg, GenericArgument, Ident,
+	ImplItem, ItemImpl, Pat, Path, PathArguments, Result, ReturnType, Signature, Type, TypePath,
 };
 
-use quote::quote;
+use quote::{format_ident, quote};
 
 use std::env;
 
 use proc_macro_crate::{crate_name, FoundCrate};
 
+use crate::common::API_VERSION_ATTRIBUTE;
+
 fn generate_hidden_includes_mod_name(unique_id: &'static str) -> Ident {
 	Ident::new(&format!("sp_api_hidden_includes_{}", unique_id), Span::call_site())
 }
@@ -68,11 +70,6 @@ pub fn generate_runtime_mod_name_for_trait(trait_: &Ident) -> Ident {
 	Ident::new(&format!("runtime_decl_for_{}", trait_), Span::call_site())
 }
 
-/// Generates a name for a method that needs to be implemented in the runtime for the client side.
-pub fn generate_method_runtime_api_impl_name(trait_: &Ident, method: &Ident) -> Ident {
-	Ident::new(&format!("{}_{}_runtime_api_impl", trait_, method), Span::call_site())
-}
-
 /// Get the type of a `syn::ReturnType`.
 pub fn return_type_extract_type(rt: &ReturnType) -> Type {
 	match rt {
@@ -166,16 +163,6 @@ pub fn extract_parameter_names_types_and_borrows(
 	Ok(result)
 }
 
-/// Generates the name for the native call generator function.
-pub fn generate_native_call_generator_fn_name(fn_name: &Ident) -> Ident {
-	Ident::new(&format!("{}_native_call_generator", fn_name), Span::call_site())
-}
-
-/// Generates the name for the call api at function.
-pub fn generate_call_api_at_fn_name(fn_name: &Ident) -> Ident {
-	Ident::new(&format!("{}_call_api_at", fn_name), Span::call_site())
-}
-
 /// Prefix the given function with the trait name.
 pub fn prefix_function_with_trait<F: ToString>(trait_: &Ident, function: &F) -> String {
 	format!("{}_{}", trait_, function.to_string())
@@ -267,3 +254,23 @@ pub fn extract_impl_trait(impl_: &ItemImpl, require: RequireQualifiedTraitPath)
 			}
 		})
 }
+
+/// Parse the given attribute as `API_VERSION_ATTRIBUTE`.
+pub fn parse_runtime_api_version(version: &Attribute) -> Result<u64> {
+	let version = version.parse_args::<syn::LitInt>().map_err(|_| {
+		Error::new(
+			version.span(),
+			&format!(
+				"Unexpected `{api_version}` attribute. The supported format is `{api_version}(1)`",
+				api_version = API_VERSION_ATTRIBUTE
+			),
+		)
+	})?;
+
+	version.base10_parse()
+}
+
+// Each versioned trait is named 'ApiNameVN' where N is the specific version. E.g. ParachainHostV2
+pub fn versioned_trait_name(trait_ident: &Ident, version: u64) -> Ident {
+	format_ident!("{}V{}", trait_ident, version)
+}
diff --git a/substrate/primitives/api/src/lib.rs b/substrate/primitives/api/src/lib.rs
index 2635c81948f..a5230cfc5f1 100644
--- a/substrate/primitives/api/src/lib.rs
+++ b/substrate/primitives/api/src/lib.rs
@@ -78,12 +78,12 @@ pub use hash_db::Hasher;
 #[doc(hidden)]
 #[cfg(not(feature = "std"))]
 pub use sp_core::to_substrate_wasm_fn_return_value;
-#[doc(hidden)]
-#[cfg(feature = "std")]
-pub use sp_core::NativeOrEncoded;
 use sp_core::OpaqueMetadata;
 #[doc(hidden)]
 pub use sp_core::{offchain, ExecutionContext};
+#[doc(hidden)]
+#[cfg(feature = "std")]
+pub use sp_core::{NativeOrEncoded, NeverNativeValue};
 #[cfg(feature = "std")]
 pub use sp_runtime::StateVersion;
 #[doc(hidden)]
@@ -187,6 +187,56 @@ pub const MAX_EXTRINSIC_DEPTH: u32 = 256;
 /// To check if a given runtime implements a runtime api trait, the `RuntimeVersion` has the
 /// function `has_api<A>()`. Also the `ApiExt` provides a function `has_api<A>(at: &BlockId)`
 /// to check if the runtime at the given block id implements the requested runtime api trait.
+///
+/// # Declaring multiple api versions
+///
+/// Optionally multiple versions of the same api can be declared. This is useful for
+/// development purposes. For example you want to have a testing version of the api which is
+/// available only on a testnet. You can define one stable and one development version. This
+/// can be done like this:
+/// ```rust
+/// sp_api::decl_runtime_apis! {
+///     /// Declare the api trait.
+/// 	#[api_version(2)]
+///     pub trait Balance {
+///         /// Get the balance.
+///         fn get_balance() -> u64;
+///         /// Set the balance.
+///         fn set_balance(val: u64);
+///         /// Transfer the balance to another user id
+///         #[api_version(3)]
+///         fn transfer_balance(uid: u64);
+///     }
+/// }
+///
+/// # fn main() {}
+/// ```
+/// The example above defines two api versions - 2 and 3. Version 2 contains `get_balance` and
+/// `set_balance`. Version 3 additionally contains `transfer_balance`, which is not available
+/// in version 2. Version 2 in this case is considered the default/base version of the api.
+/// More than two versions can be defined this way. For example:
+/// ```rust
+/// sp_api::decl_runtime_apis! {
+///     /// Declare the api trait.
+///     #[api_version(2)]
+///     pub trait Balance {
+///         /// Get the balance.
+///         fn get_balance() -> u64;
+///         /// Set the balance.
+///         fn set_balance(val: u64);
+///         /// Transfer the balance to another user id
+///         #[api_version(3)]
+///         fn transfer_balance(uid: u64);
+///         /// Clears the balance
+///         #[api_version(4)]
+///         fn clear_balance();
+///     }
+/// }
+///
+/// # fn main() {}
+/// ```
+/// Note that the latest version (4 in our example above) always contains all methods from all
+/// the versions before.
 pub use sp_api_proc_macro::decl_runtime_apis;
 
 /// Tags given trait implementations as runtime apis.
@@ -276,6 +326,22 @@ pub use sp_api_proc_macro::decl_runtime_apis;
 ///
 /// # fn main() {}
 /// ```
+///
+/// # Implementing specific api version
+///
+/// If `decl_runtime_apis!` declares multiple versions for an api `impl_runtime_apis!`
+/// should specify which version it implements by adding `api_version` attribute to the
+/// `impl` block. If omitted - the base/default version is implemented. Here is an example:
+/// ```ignore
+/// sp_api::impl_runtime_apis! {
+///     #[api_version(3)]
+///     impl self::Balance<Block> for Runtime {
+///          // implementation
+///     }
+/// }
+/// ```
+/// In this case `Balance` api version 3 is being implemented for `Runtime`. The `impl` block
+/// must contain all methods declared in version 3 and below.
 pub use sp_api_proc_macro::impl_runtime_apis;
 
 /// Mocks given trait implementations as runtime apis.
@@ -341,15 +407,13 @@ pub use sp_api_proc_macro::impl_runtime_apis;
 /// using the `advanced` attribute, the macro expects that the first parameter of the function
 /// is this `at` parameter. Besides that the macro also doesn't do the automatic return value
 /// rewrite, which means that full return value must be specified. The full return value is
-/// constructed like [`Result`]`<`[`NativeOrEncoded`](sp_api::NativeOrEncoded)`<ReturnValue>,
-/// Error>` while `ReturnValue` being the return value that is specified in the trait
-/// declaration.
+/// constructed like [`Result`]`<<ReturnValue>, Error>` while `ReturnValue` being the return
+/// value that is specified in the trait declaration.
 ///
 /// ## Example
 /// ```rust
 /// # use sp_runtime::{traits::Block as BlockT, generic::BlockId};
 /// # use sp_test_primitives::Block;
-/// # use sp_core::NativeOrEncoded;
 /// # use codec;
 /// #
 /// # sp_api::decl_runtime_apis! {
@@ -368,13 +432,13 @@ pub use sp_api_proc_macro::impl_runtime_apis;
 /// sp_api::mock_impl_runtime_apis! {
 ///     impl Balance<Block> for MockApi {
 ///         #[advanced]
-///         fn get_balance(&self, at: &BlockId<Block>) -> Result<NativeOrEncoded<u64>, sp_api::ApiError> {
+///         fn get_balance(&self, at: &BlockId<Block>) -> Result<u64, sp_api::ApiError> {
 ///             println!("Being called at: {}", at);
 ///
 ///             Ok(self.balance.into())
 ///         }
 ///         #[advanced]
-///         fn set_balance(at: &BlockId<Block>, val: u64) -> Result<NativeOrEncoded<()>, sp_api::ApiError> {
+///         fn set_balance(at: &BlockId<Block>, val: u64) -> Result<(), sp_api::ApiError> {
 ///             if let BlockId::Number(1) = at {
 ///                 println!("Being called to set balance to: {}", val);
 ///             }
diff --git a/substrate/primitives/api/test/tests/decl_and_impl.rs b/substrate/primitives/api/test/tests/decl_and_impl.rs
index 1db416a1d3d..42628830cc7 100644
--- a/substrate/primitives/api/test/tests/decl_and_impl.rs
+++ b/substrate/primitives/api/test/tests/decl_and_impl.rs
@@ -18,7 +18,6 @@
 use sp_api::{
 	decl_runtime_apis, impl_runtime_apis, mock_impl_runtime_apis, ApiError, ApiExt, RuntimeApiInfo,
 };
-use sp_core::NativeOrEncoded;
 use sp_runtime::{
 	generic::BlockId,
 	traits::{Block as BlockT, GetNodeBlockType},
@@ -47,6 +46,15 @@ decl_runtime_apis! {
 		#[changed_in(2)]
 		fn same_name() -> String;
 	}
+
+	#[api_version(2)]
+	pub trait ApiWithMultipleVersions {
+		fn stable_one(data: u64);
+		#[api_version(3)]
+		fn new_one();
+		#[api_version(4)]
+		fn glory_one();
+	}
 }
 
 impl_runtime_apis! {
@@ -72,6 +80,13 @@ impl_runtime_apis! {
 		fn same_name() {}
 	}
 
+	#[api_version(3)]
+	impl self::ApiWithMultipleVersions<Block> for Runtime {
+		fn stable_one(_: u64) {}
+
+		fn new_one() {}
+	}
+
 	impl sp_api::Core<Block> for Runtime {
 		fn version() -> sp_version::RuntimeVersion {
 			unimplemented!()
@@ -104,22 +119,12 @@ mock_impl_runtime_apis! {
 		}
 
 		#[advanced]
-		fn same_name(_: &BlockId<Block>) ->
-			Result<
-				NativeOrEncoded<()>,
-				ApiError
-			>
-		{
+		fn same_name(_: &BlockId<Block>) -> Result<(), ApiError> {
 			Ok(().into())
 		}
 
 		#[advanced]
-		fn wild_card(at: &BlockId<Block>, _: u32) ->
-			Result<
-				NativeOrEncoded<()>,
-				ApiError
-			>
-		{
+		fn wild_card(at: &BlockId<Block>, _: u32) -> Result<(), ApiError> {
 			if let BlockId::Number(1337) = at {
 				// yeah
 				Ok(().into())
@@ -176,6 +181,9 @@ fn check_runtime_api_info() {
 		&runtime_decl_for_ApiWithCustomVersion::ID,
 	);
 	assert_eq!(<dyn ApiWithCustomVersion::<Block>>::VERSION, 2);
+
+	// The stable version of the API
+	assert_eq!(<dyn ApiWithMultipleVersions::<Block>>::VERSION, 2);
 }
 
 fn check_runtime_api_versions_contains<T: RuntimeApiInfo + ?Sized>() {
@@ -186,6 +194,9 @@ fn check_runtime_api_versions_contains<T: RuntimeApiInfo + ?Sized>() {
 fn check_runtime_api_versions() {
 	check_runtime_api_versions_contains::<dyn Api<Block>>();
 	check_runtime_api_versions_contains::<dyn ApiWithCustomVersion<Block>>();
+	assert!(RUNTIME_API_VERSIONS
+		.iter()
+		.any(|v| v == &(<dyn ApiWithMultipleVersions<Block>>::ID, 3)));
 	check_runtime_api_versions_contains::<dyn sp_api::Core<Block>>();
 }
 
@@ -198,7 +209,7 @@ fn mock_runtime_api_has_api() {
 }
 
 #[test]
-#[should_panic(expected = "Mocked runtime apis don't support calling deprecated api versions")]
+#[should_panic(expected = "Calling deprecated methods is not supported by mocked runtime api.")]
 fn mock_runtime_api_panics_on_calling_old_version() {
 	let mock = MockApi { block: None };
 
diff --git a/substrate/primitives/api/test/tests/runtime_calls.rs b/substrate/primitives/api/test/tests/runtime_calls.rs
index ba42b342377..968bda6885d 100644
--- a/substrate/primitives/api/test/tests/runtime_calls.rs
+++ b/substrate/primitives/api/test/tests/runtime_calls.rs
@@ -25,7 +25,7 @@ use sp_state_machine::{
 };
 use substrate_test_runtime_client::{
 	prelude::*,
-	runtime::{Block, DecodeFails, Header, TestAPI, Transfer},
+	runtime::{Block, Header, TestAPI, Transfer},
 	DefaultTestClientBuilderExt, TestClientBuilder,
 };
 
@@ -51,28 +51,6 @@ fn calling_wasm_runtime_function() {
 	calling_function_with_strat(ExecutionStrategy::AlwaysWasm);
 }
 
-#[test]
-#[should_panic(expected = "FailedToConvertParameter { function: \"fail_convert_parameter\"")]
-fn calling_native_runtime_function_with_non_decodable_parameter() {
-	let client = TestClientBuilder::new()
-		.set_execution_strategy(ExecutionStrategy::NativeWhenPossible)
-		.build();
-	let runtime_api = client.runtime_api();
-	let block_id = BlockId::Number(client.chain_info().best_number);
-	runtime_api.fail_convert_parameter(&block_id, DecodeFails::default()).unwrap();
-}
-
-#[test]
-#[should_panic(expected = "FailedToConvertReturnValue { function: \"fail_convert_return_value\"")]
-fn calling_native_runtime_function_with_non_decodable_return_value() {
-	let client = TestClientBuilder::new()
-		.set_execution_strategy(ExecutionStrategy::NativeWhenPossible)
-		.build();
-	let runtime_api = client.runtime_api();
-	let block_id = BlockId::Number(client.chain_info().best_number);
-	runtime_api.fail_convert_return_value(&block_id).unwrap();
-}
-
 #[test]
 fn calling_native_runtime_signature_changed_function() {
 	let client = TestClientBuilder::new()
diff --git a/substrate/primitives/api/test/tests/trybuild.rs b/substrate/primitives/api/test/tests/trybuild.rs
index f3d6aa59a03..13af1ded7dc 100644
--- a/substrate/primitives/api/test/tests/trybuild.rs
+++ b/substrate/primitives/api/test/tests/trybuild.rs
@@ -30,4 +30,5 @@ fn ui() {
 
 	let t = trybuild::TestCases::new();
 	t.compile_fail("tests/ui/*.rs");
+	t.pass("tests/ui/positive_cases/*.rs");
 }
diff --git a/substrate/primitives/api/test/tests/ui/impl_incorrect_method_signature.stderr b/substrate/primitives/api/test/tests/ui/impl_incorrect_method_signature.stderr
index b1478e2f533..2c47c2f480a 100644
--- a/substrate/primitives/api/test/tests/ui/impl_incorrect_method_signature.stderr
+++ b/substrate/primitives/api/test/tests/ui/impl_incorrect_method_signature.stderr
@@ -15,49 +15,6 @@ note: type in trait
    = note: expected fn pointer `fn(u64)`
               found fn pointer `fn(std::string::String)`
 
-error[E0053]: method `Api_test_runtime_api_impl` has an incompatible type for trait
-  --> tests/ui/impl_incorrect_method_signature.rs:17:1
-   |
-17 |   sp_api::impl_runtime_apis! {
-   |   -^^^^^^^^^^^^^^^^^^^^^^^^^
-   |   |
-   |  _expected `u64`, found struct `std::string::String`
-   | |
-18 | |     impl self::Api<Block> for Runtime {
-19 | |         fn test(data: String) {}
-20 | |     }
-...  |
-32 | |     }
-33 | | }
-   | |_- help: change the parameter type to match the trait: `std::option::Option<u64>`
-   |
-note: type in trait
-  --> tests/ui/impl_incorrect_method_signature.rs:11:1
-   |
-11 | / sp_api::decl_runtime_apis! {
-12 | |     pub trait Api {
-13 | |         fn test(data: u64);
-14 | |     }
-15 | | }
-   | |_^
-   = note: expected fn pointer `fn(&RuntimeApiImpl<__SR_API_BLOCK__, RuntimeApiImplCall>, &BlockId<__SR_API_BLOCK__>, ExecutionContext, std::option::Option<u64>, Vec<_>) -> Result<_, _>`
-              found fn pointer `fn(&RuntimeApiImpl<__SR_API_BLOCK__, RuntimeApiImplCall>, &BlockId<__SR_API_BLOCK__>, ExecutionContext, std::option::Option<std::string::String>, Vec<_>) -> Result<_, _>`
-   = note: this error originates in the macro `sp_api::impl_runtime_apis` (in Nightly builds, run with -Z macro-backtrace for more info)
-
-error[E0308]: mismatched types
-  --> tests/ui/impl_incorrect_method_signature.rs:17:1
-   |
-17 | / sp_api::impl_runtime_apis! {
-18 | |     impl self::Api<Block> for Runtime {
-19 | |         fn test(data: String) {}
-20 | |     }
-...  |
-32 | |     }
-33 | | }
-   | |_^ expected `u64`, found struct `std::string::String`
-   |
-   = note: this error originates in the macro `sp_api::impl_runtime_apis` (in Nightly builds, run with -Z macro-backtrace for more info)
-
 error[E0308]: mismatched types
   --> tests/ui/impl_incorrect_method_signature.rs:19:11
    |
diff --git a/substrate/primitives/api/test/tests/ui/impl_missing_version.rs b/substrate/primitives/api/test/tests/ui/impl_missing_version.rs
new file mode 100644
index 00000000000..63e0599622a
--- /dev/null
+++ b/substrate/primitives/api/test/tests/ui/impl_missing_version.rs
@@ -0,0 +1,40 @@
+use sp_runtime::traits::{Block as BlockT, GetNodeBlockType};
+use substrate_test_runtime_client::runtime::Block;
+
+struct Runtime {}
+impl GetNodeBlockType for Runtime {
+	type NodeBlock = Block;
+}
+
+sp_api::decl_runtime_apis! {
+	#[api_version(2)]
+	pub trait Api {
+		fn test1();
+		fn test2();
+		#[api_version(3)]
+		fn test3();
+	}
+}
+
+sp_api::impl_runtime_apis! {
+	#[api_version(4)]
+	impl self::Api<Block> for Runtime {
+		fn test1() {}
+		fn test2() {}
+		fn test3() {}
+	}
+
+	impl sp_api::Core<Block> for Runtime {
+		fn version() -> sp_version::RuntimeVersion {
+			unimplemented!()
+		}
+		fn execute_block(_: Block) {
+			unimplemented!()
+		}
+		fn initialize_block(_: &<Block as BlockT>::Header) {
+			unimplemented!()
+		}
+	}
+}
+
+fn main() {}
diff --git a/substrate/primitives/api/test/tests/ui/impl_missing_version.stderr b/substrate/primitives/api/test/tests/ui/impl_missing_version.stderr
new file mode 100644
index 00000000000..c0abeffe0cc
--- /dev/null
+++ b/substrate/primitives/api/test/tests/ui/impl_missing_version.stderr
@@ -0,0 +1,14 @@
+error[E0433]: failed to resolve: could not find `ApiV4` in `runtime_decl_for_Api`
+  --> tests/ui/impl_missing_version.rs:21:13
+   |
+21 |     impl self::Api<Block> for Runtime {
+   |                ^^^ could not find `ApiV4` in `runtime_decl_for_Api`
+
+error[E0405]: cannot find trait `ApiV4` in module `self::runtime_decl_for_Api`
+  --> tests/ui/impl_missing_version.rs:21:13
+   |
+11 |     pub trait Api {
+   |     ------------- similarly named trait `ApiV2` defined here
+...
+21 |     impl self::Api<Block> for Runtime {
+   |                ^^^ help: a trait with a similar name exists: `ApiV2`
diff --git a/substrate/primitives/api/test/tests/ui/invalid_api_version.rs b/substrate/primitives/api/test/tests/ui/invalid_api_version_1.rs
similarity index 100%
rename from substrate/primitives/api/test/tests/ui/invalid_api_version.rs
rename to substrate/primitives/api/test/tests/ui/invalid_api_version_1.rs
diff --git a/substrate/primitives/api/test/tests/ui/invalid_api_version.stderr b/substrate/primitives/api/test/tests/ui/invalid_api_version_1.stderr
similarity index 65%
rename from substrate/primitives/api/test/tests/ui/invalid_api_version.stderr
rename to substrate/primitives/api/test/tests/ui/invalid_api_version_1.stderr
index 7770bc70e72..53ffce959bb 100644
--- a/substrate/primitives/api/test/tests/ui/invalid_api_version.stderr
+++ b/substrate/primitives/api/test/tests/ui/invalid_api_version_1.stderr
@@ -1,5 +1,5 @@
 error: Unexpected `api_version` attribute. The supported format is `api_version(1)`
- --> $DIR/invalid_api_version.rs:2:4
+ --> tests/ui/invalid_api_version_1.rs:2:2
   |
 2 |     #[api_version]
-  |       ^^^^^^^^^^^
+  |     ^
diff --git a/substrate/primitives/api/test/tests/ui/invalid_api_version_2.stderr b/substrate/primitives/api/test/tests/ui/invalid_api_version_2.stderr
index 7ca6a7eebe4..0c5274d4680 100644
--- a/substrate/primitives/api/test/tests/ui/invalid_api_version_2.stderr
+++ b/substrate/primitives/api/test/tests/ui/invalid_api_version_2.stderr
@@ -1,5 +1,5 @@
 error: Unexpected `api_version` attribute. The supported format is `api_version(1)`
- --> $DIR/invalid_api_version_2.rs:2:4
+ --> tests/ui/invalid_api_version_2.rs:2:2
   |
 2 |     #[api_version("1")]
-  |       ^^^^^^^^^^^
+  |     ^
diff --git a/substrate/primitives/api/test/tests/ui/invalid_api_version_3.stderr b/substrate/primitives/api/test/tests/ui/invalid_api_version_3.stderr
index cef4763a6de..4a34a7aa9b4 100644
--- a/substrate/primitives/api/test/tests/ui/invalid_api_version_3.stderr
+++ b/substrate/primitives/api/test/tests/ui/invalid_api_version_3.stderr
@@ -1,5 +1,5 @@
 error: Unexpected `api_version` attribute. The supported format is `api_version(1)`
- --> $DIR/invalid_api_version_3.rs:2:4
+ --> tests/ui/invalid_api_version_3.rs:2:2
   |
 2 |     #[api_version()]
-  |       ^^^^^^^^^^^
+  |     ^
diff --git a/substrate/primitives/api/test/tests/ui/invalid_api_version_4.rs b/substrate/primitives/api/test/tests/ui/invalid_api_version_4.rs
new file mode 100644
index 00000000000..37b5b6ffa25
--- /dev/null
+++ b/substrate/primitives/api/test/tests/ui/invalid_api_version_4.rs
@@ -0,0 +1,8 @@
+sp_api::decl_runtime_apis! {
+	pub trait Api {
+		#[api_version("1")]
+		fn test(data: u64);
+	}
+}
+
+fn main() {}
diff --git a/substrate/primitives/api/test/tests/ui/invalid_api_version_4.stderr b/substrate/primitives/api/test/tests/ui/invalid_api_version_4.stderr
new file mode 100644
index 00000000000..57541a97f6c
--- /dev/null
+++ b/substrate/primitives/api/test/tests/ui/invalid_api_version_4.stderr
@@ -0,0 +1,5 @@
+error: Unexpected `api_version` attribute. The supported format is `api_version(1)`
+ --> tests/ui/invalid_api_version_4.rs:3:3
+  |
+3 |         #[api_version("1")]
+  |         ^
diff --git a/substrate/primitives/api/test/tests/ui/method_ver_lower_than_trait_ver.rs b/substrate/primitives/api/test/tests/ui/method_ver_lower_than_trait_ver.rs
new file mode 100644
index 00000000000..b4f43cd401b
--- /dev/null
+++ b/substrate/primitives/api/test/tests/ui/method_ver_lower_than_trait_ver.rs
@@ -0,0 +1,9 @@
+sp_api::decl_runtime_apis! {
+	#[api_version(2)]
+	pub trait Api {
+		#[api_version(1)]
+		fn test(data: u64);
+	}
+}
+
+fn main() {}
\ No newline at end of file
diff --git a/substrate/primitives/api/test/tests/ui/method_ver_lower_than_trait_ver.stderr b/substrate/primitives/api/test/tests/ui/method_ver_lower_than_trait_ver.stderr
new file mode 100644
index 00000000000..ec4b594023a
--- /dev/null
+++ b/substrate/primitives/api/test/tests/ui/method_ver_lower_than_trait_ver.stderr
@@ -0,0 +1,11 @@
+error: Method version `1` is older than (or equal to) trait version `2`.Methods can't define versions older than the trait version.
+ --> tests/ui/method_ver_lower_than_trait_ver.rs:4:3
+  |
+4 |         #[api_version(1)]
+  |         ^
+
+error: Trait version is set here.
+ --> tests/ui/method_ver_lower_than_trait_ver.rs:2:2
+  |
+2 |     #[api_version(2)]
+  |     ^
diff --git a/substrate/primitives/api/test/tests/ui/missing_versioned_method.rs b/substrate/primitives/api/test/tests/ui/missing_versioned_method.rs
new file mode 100644
index 00000000000..d973a94c210
--- /dev/null
+++ b/substrate/primitives/api/test/tests/ui/missing_versioned_method.rs
@@ -0,0 +1,39 @@
+use sp_runtime::traits::{Block as BlockT, GetNodeBlockType};
+use substrate_test_runtime_client::runtime::Block;
+
+struct Runtime {}
+impl GetNodeBlockType for Runtime {
+	type NodeBlock = Block;
+}
+
+sp_api::decl_runtime_apis! {
+	#[api_version(2)]
+	pub trait Api {
+		fn test1();
+		fn test2();
+		#[api_version(3)]
+		fn test3();
+	}
+}
+
+sp_api::impl_runtime_apis! {
+	#[api_version(3)]
+	impl self::Api<Block> for Runtime {
+		fn test1() {}
+		fn test2() {}
+	}
+
+	impl sp_api::Core<Block> for Runtime {
+		fn version() -> sp_version::RuntimeVersion {
+			unimplemented!()
+		}
+		fn execute_block(_: Block) {
+			unimplemented!()
+		}
+		fn initialize_block(_: &<Block as BlockT>::Header) {
+			unimplemented!()
+		}
+	}
+}
+
+fn main() {}
diff --git a/substrate/primitives/api/test/tests/ui/missing_versioned_method.stderr b/substrate/primitives/api/test/tests/ui/missing_versioned_method.stderr
new file mode 100644
index 00000000000..e3ace7979c2
--- /dev/null
+++ b/substrate/primitives/api/test/tests/ui/missing_versioned_method.stderr
@@ -0,0 +1,8 @@
+error[E0046]: not all trait items implemented, missing: `test3`
+  --> tests/ui/missing_versioned_method.rs:21:2
+   |
+15 |         fn test3();
+   |         ----------- `test3` from trait
+...
+21 |     impl self::Api<Block> for Runtime {
+   |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ missing `test3` in implementation
diff --git a/substrate/primitives/api/test/tests/ui/missing_versioned_method_multiple_vers.rs b/substrate/primitives/api/test/tests/ui/missing_versioned_method_multiple_vers.rs
new file mode 100644
index 00000000000..72358b99164
--- /dev/null
+++ b/substrate/primitives/api/test/tests/ui/missing_versioned_method_multiple_vers.rs
@@ -0,0 +1,42 @@
+use sp_runtime::traits::{Block as BlockT, GetNodeBlockType};
+use substrate_test_runtime_client::runtime::Block;
+
+struct Runtime {}
+impl GetNodeBlockType for Runtime {
+	type NodeBlock = Block;
+}
+
+sp_api::decl_runtime_apis! {
+	#[api_version(2)]
+	pub trait Api {
+		fn test1();
+		fn test2();
+		#[api_version(3)]
+		fn test3();
+		#[api_version(4)]
+		fn test4();
+	}
+}
+
+sp_api::impl_runtime_apis! {
+	#[api_version(4)]
+	impl self::Api<Block> for Runtime {
+		fn test1() {}
+		fn test2() {}
+		fn test4() {}
+	}
+
+	impl sp_api::Core<Block> for Runtime {
+		fn version() -> sp_version::RuntimeVersion {
+			unimplemented!()
+		}
+		fn execute_block(_: Block) {
+			unimplemented!()
+		}
+		fn initialize_block(_: &<Block as BlockT>::Header) {
+			unimplemented!()
+		}
+	}
+}
+
+fn main() {}
diff --git a/substrate/primitives/api/test/tests/ui/missing_versioned_method_multiple_vers.stderr b/substrate/primitives/api/test/tests/ui/missing_versioned_method_multiple_vers.stderr
new file mode 100644
index 00000000000..7354fbd537f
--- /dev/null
+++ b/substrate/primitives/api/test/tests/ui/missing_versioned_method_multiple_vers.stderr
@@ -0,0 +1,8 @@
+error[E0046]: not all trait items implemented, missing: `test3`
+  --> tests/ui/missing_versioned_method_multiple_vers.rs:23:2
+   |
+15 |         fn test3();
+   |         ----------- `test3` from trait
+...
+23 |     impl self::Api<Block> for Runtime {
+   |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ missing `test3` in implementation
diff --git a/substrate/primitives/api/test/tests/ui/mock_advanced_block_id_by_value.rs b/substrate/primitives/api/test/tests/ui/mock_advanced_block_id_by_value.rs
index fd654ffdc63..aeef40f4c77 100644
--- a/substrate/primitives/api/test/tests/ui/mock_advanced_block_id_by_value.rs
+++ b/substrate/primitives/api/test/tests/ui/mock_advanced_block_id_by_value.rs
@@ -12,7 +12,7 @@ struct MockApi;
 sp_api::mock_impl_runtime_apis! {
 	impl Api<Block> for MockApi {
 		#[advanced]
-		fn test(&self, _: BlockId<Block>) -> Result<sp_core::NativeOrEncoded<()>, ApiError> {
+		fn test(&self, _: BlockId<Block>) -> Result<(), ApiError> {
 			Ok(().into())
 		}
 	}
diff --git a/substrate/primitives/api/test/tests/ui/mock_advanced_block_id_by_value.stderr b/substrate/primitives/api/test/tests/ui/mock_advanced_block_id_by_value.stderr
index befe67c1d0b..3b3c4e94c31 100644
--- a/substrate/primitives/api/test/tests/ui/mock_advanced_block_id_by_value.stderr
+++ b/substrate/primitives/api/test/tests/ui/mock_advanced_block_id_by_value.stderr
@@ -1,10 +1,10 @@
 error: `BlockId` needs to be taken by reference and not by value!
-  --> $DIR/mock_advanced_block_id_by_value.rs:12:1
+  --> tests/ui/mock_advanced_block_id_by_value.rs:12:1
    |
 12 | / sp_api::mock_impl_runtime_apis! {
 13 | |     impl Api<Block> for MockApi {
 14 | |         #[advanced]
-15 | |         fn test(&self, _: BlockId<Block>) -> Result<sp_core::NativeOrEncoded<()>, ApiError> {
+15 | |         fn test(&self, _: BlockId<Block>) -> Result<(), ApiError> {
 ...  |
 18 | |     }
 19 | | }
diff --git a/substrate/primitives/api/test/tests/ui/mock_advanced_missing_blockid.rs b/substrate/primitives/api/test/tests/ui/mock_advanced_missing_blockid.rs
index a15ef133fa6..76bf5f1aa74 100644
--- a/substrate/primitives/api/test/tests/ui/mock_advanced_missing_blockid.rs
+++ b/substrate/primitives/api/test/tests/ui/mock_advanced_missing_blockid.rs
@@ -12,7 +12,7 @@ struct MockApi;
 sp_api::mock_impl_runtime_apis! {
 	impl Api<Block> for MockApi {
 		#[advanced]
-		fn test(&self) -> Result<sp_core::NativeOrEncoded<()>, ApiError> {
+		fn test(&self) -> Result<(), ApiError> {
 			Ok(().into())
 		}
 	}
diff --git a/substrate/primitives/api/test/tests/ui/mock_advanced_missing_blockid.stderr b/substrate/primitives/api/test/tests/ui/mock_advanced_missing_blockid.stderr
index 87d3660316b..b9ce7324b5c 100644
--- a/substrate/primitives/api/test/tests/ui/mock_advanced_missing_blockid.stderr
+++ b/substrate/primitives/api/test/tests/ui/mock_advanced_missing_blockid.stderr
@@ -1,5 +1,5 @@
 error: If using the `advanced` attribute, it is required that the function takes at least one argument, the `BlockId`.
-  --> $DIR/mock_advanced_missing_blockid.rs:15:3
+  --> tests/ui/mock_advanced_missing_blockid.rs:15:3
    |
-15 |         fn test(&self) -> Result<sp_core::NativeOrEncoded<()>, ApiError> {
+15 |         fn test(&self) -> Result<(), ApiError> {
    |         ^^
diff --git a/substrate/primitives/api/test/tests/ui/mock_only_self_reference.stderr b/substrate/primitives/api/test/tests/ui/mock_only_self_reference.stderr
index c67de70b9c1..430f63eee16 100644
--- a/substrate/primitives/api/test/tests/ui/mock_only_self_reference.stderr
+++ b/substrate/primitives/api/test/tests/ui/mock_only_self_reference.stderr
@@ -10,62 +10,80 @@ error: Only `&self` is supported!
 16 |         fn test2(&mut self, data: u64) {}
    |                  ^
 
-error[E0053]: method `Api_test_runtime_api_impl` has an incompatible type for trait
+error[E0050]: method `test` has 2 parameters but the declaration in trait `Api::test` has 3
   --> tests/ui/mock_only_self_reference.rs:12:1
    |
-12 |   sp_api::mock_impl_runtime_apis! {
-   |   -^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
-   |   |
-   |  _expected `u64`, found `()`
-   | |
+3  | / sp_api::decl_runtime_apis! {
+4  | |     pub trait Api {
+5  | |         fn test(data: u64);
+   | |_________________________- trait requires 3 parameters
+...
+12 | / sp_api::mock_impl_runtime_apis! {
 13 | |     impl Api<Block> for MockApi {
 14 | |         fn test(self, data: u64) {}
 15 | |
 16 | |         fn test2(&mut self, data: u64) {}
 17 | |     }
 18 | | }
-   | |_- help: change the parameter type to match the trait: `Option<u64>`
+   | |_^ expected 3 parameters, found 2
    |
-note: type in trait
-  --> tests/ui/mock_only_self_reference.rs:3:1
+   = note: this error originates in the macro `sp_api::mock_impl_runtime_apis` (in Nightly builds, run with -Z macro-backtrace for more info)
+
+error[E0050]: method `test2` has 2 parameters but the declaration in trait `Api::test2` has 3
+  --> tests/ui/mock_only_self_reference.rs:12:1
    |
 3  | / sp_api::decl_runtime_apis! {
 4  | |     pub trait Api {
 5  | |         fn test(data: u64);
 6  | |         fn test2(data: u64);
-7  | |     }
-8  | | }
-   | |_^
-   = note: expected fn pointer `fn(&MockApi, &BlockId<sp_runtime::generic::block::Block<sp_runtime::generic::header::Header<u64, sp_runtime::traits::BlakeTwo256>, Extrinsic>>, ExecutionContext, Option<u64>, Vec<_>) -> Result<_, _>`
-              found fn pointer `fn(&MockApi, &BlockId<sp_runtime::generic::block::Block<sp_runtime::generic::header::Header<u64, sp_runtime::traits::BlakeTwo256>, Extrinsic>>, ExecutionContext, Option<()>, Vec<_>) -> Result<_, _>`
+   | |__________________________- trait requires 3 parameters
+...
+12 | / sp_api::mock_impl_runtime_apis! {
+13 | |     impl Api<Block> for MockApi {
+14 | |         fn test(self, data: u64) {}
+15 | |
+16 | |         fn test2(&mut self, data: u64) {}
+17 | |     }
+18 | | }
+   | |_^ expected 3 parameters, found 2
+   |
    = note: this error originates in the macro `sp_api::mock_impl_runtime_apis` (in Nightly builds, run with -Z macro-backtrace for more info)
 
-error[E0053]: method `Api_test2_runtime_api_impl` has an incompatible type for trait
+error[E0050]: method `test_with_context` has 3 parameters but the declaration in trait `Api::test_with_context` has 4
   --> tests/ui/mock_only_self_reference.rs:12:1
    |
-12 |   sp_api::mock_impl_runtime_apis! {
-   |   -^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
-   |   |
-   |  _expected `u64`, found `()`
-   | |
+3  | / sp_api::decl_runtime_apis! {
+4  | |     pub trait Api {
+5  | |         fn test(data: u64);
+   | |_________________________- trait requires 4 parameters
+...
+12 | / sp_api::mock_impl_runtime_apis! {
 13 | |     impl Api<Block> for MockApi {
 14 | |         fn test(self, data: u64) {}
 15 | |
 16 | |         fn test2(&mut self, data: u64) {}
 17 | |     }
 18 | | }
-   | |_- help: change the parameter type to match the trait: `Option<u64>`
+   | |_^ expected 4 parameters, found 3
    |
-note: type in trait
-  --> tests/ui/mock_only_self_reference.rs:3:1
+   = note: this error originates in the macro `sp_api::mock_impl_runtime_apis` (in Nightly builds, run with -Z macro-backtrace for more info)
+
+error[E0050]: method `test2_with_context` has 3 parameters but the declaration in trait `Api::test2_with_context` has 4
+  --> tests/ui/mock_only_self_reference.rs:12:1
    |
 3  | / sp_api::decl_runtime_apis! {
 4  | |     pub trait Api {
 5  | |         fn test(data: u64);
 6  | |         fn test2(data: u64);
-7  | |     }
-8  | | }
-   | |_^
-   = note: expected fn pointer `fn(&MockApi, &BlockId<sp_runtime::generic::block::Block<sp_runtime::generic::header::Header<u64, sp_runtime::traits::BlakeTwo256>, Extrinsic>>, ExecutionContext, Option<u64>, Vec<_>) -> Result<_, _>`
-              found fn pointer `fn(&MockApi, &BlockId<sp_runtime::generic::block::Block<sp_runtime::generic::header::Header<u64, sp_runtime::traits::BlakeTwo256>, Extrinsic>>, ExecutionContext, Option<()>, Vec<_>) -> Result<_, _>`
+   | |__________________________- trait requires 4 parameters
+...
+12 | / sp_api::mock_impl_runtime_apis! {
+13 | |     impl Api<Block> for MockApi {
+14 | |         fn test(self, data: u64) {}
+15 | |
+16 | |         fn test2(&mut self, data: u64) {}
+17 | |     }
+18 | | }
+   | |_^ expected 4 parameters, found 3
+   |
    = note: this error originates in the macro `sp_api::mock_impl_runtime_apis` (in Nightly builds, run with -Z macro-backtrace for more info)
diff --git a/substrate/primitives/api/test/tests/ui/positive_cases/default_impls.rs b/substrate/primitives/api/test/tests/ui/positive_cases/default_impls.rs
new file mode 100644
index 00000000000..3434db1089f
--- /dev/null
+++ b/substrate/primitives/api/test/tests/ui/positive_cases/default_impls.rs
@@ -0,0 +1,41 @@
+use sp_runtime::traits::{Block as BlockT, GetNodeBlockType};
+use substrate_test_runtime_client::runtime::Block;
+
+struct Runtime {}
+impl GetNodeBlockType for Runtime {
+	type NodeBlock = Block;
+}
+
+sp_api::decl_runtime_apis! {
+	#[api_version(2)]
+	pub trait Api {
+		fn test1();
+		fn test2();
+		#[api_version(3)]
+		fn test3();
+		#[api_version(4)]
+		fn test4();
+	}
+}
+
+sp_api::impl_runtime_apis! {
+	#[api_version(2)]
+	impl self::Api<Block> for Runtime {
+		fn test1() {}
+		fn test2() {}
+	}
+
+	impl sp_api::Core<Block> for Runtime {
+		fn version() -> sp_version::RuntimeVersion {
+			unimplemented!()
+		}
+		fn execute_block(_: Block) {
+			unimplemented!()
+		}
+		fn initialize_block(_: &<Block as BlockT>::Header) {
+			unimplemented!()
+		}
+	}
+}
+
+fn main() {}
diff --git a/substrate/primitives/api/test/tests/ui/type_reference_in_impl_runtime_apis_call.stderr b/substrate/primitives/api/test/tests/ui/type_reference_in_impl_runtime_apis_call.stderr
index dbc0f6def3a..479e1cf05a9 100644
--- a/substrate/primitives/api/test/tests/ui/type_reference_in_impl_runtime_apis_call.stderr
+++ b/substrate/primitives/api/test/tests/ui/type_reference_in_impl_runtime_apis_call.stderr
@@ -15,49 +15,6 @@ note: type in trait
    = note: expected fn pointer `fn(u64)`
               found fn pointer `fn(&u64)`
 
-error[E0053]: method `Api_test_runtime_api_impl` has an incompatible type for trait
-  --> tests/ui/type_reference_in_impl_runtime_apis_call.rs:17:1
-   |
-17 |   sp_api::impl_runtime_apis! {
-   |   -^^^^^^^^^^^^^^^^^^^^^^^^^
-   |   |
-   |  _expected `u64`, found `&u64`
-   | |
-18 | |     impl self::Api<Block> for Runtime {
-19 | |         fn test(data: &u64) {
-20 | |             unimplemented!()
-...  |
-34 | |     }
-35 | | }
-   | |_- help: change the parameter type to match the trait: `std::option::Option<u64>`
-   |
-note: type in trait
-  --> tests/ui/type_reference_in_impl_runtime_apis_call.rs:11:1
-   |
-11 | / sp_api::decl_runtime_apis! {
-12 | |     pub trait Api {
-13 | |         fn test(data: u64);
-14 | |     }
-15 | | }
-   | |_^
-   = note: expected fn pointer `fn(&RuntimeApiImpl<__SR_API_BLOCK__, RuntimeApiImplCall>, &BlockId<__SR_API_BLOCK__>, ExecutionContext, std::option::Option<u64>, Vec<_>) -> Result<_, _>`
-              found fn pointer `fn(&RuntimeApiImpl<__SR_API_BLOCK__, RuntimeApiImplCall>, &BlockId<__SR_API_BLOCK__>, ExecutionContext, std::option::Option<&u64>, Vec<_>) -> Result<_, _>`
-   = note: this error originates in the macro `sp_api::impl_runtime_apis` (in Nightly builds, run with -Z macro-backtrace for more info)
-
-error[E0308]: mismatched types
-  --> tests/ui/type_reference_in_impl_runtime_apis_call.rs:17:1
-   |
-17 | / sp_api::impl_runtime_apis! {
-18 | |     impl self::Api<Block> for Runtime {
-19 | |         fn test(data: &u64) {
-20 | |             unimplemented!()
-...  |
-34 | |     }
-35 | | }
-   | |_^ expected `u64`, found `&u64`
-   |
-   = note: this error originates in the macro `sp_api::impl_runtime_apis` (in Nightly builds, run with -Z macro-backtrace for more info)
-
 error[E0308]: mismatched types
   --> tests/ui/type_reference_in_impl_runtime_apis_call.rs:19:11
    |
-- 
GitLab