From ec69b612bfa082ada07ceb7d8115e07f943f6815 Mon Sep 17 00:00:00 2001
From: davidk-pt <david.kazlauskas@parity.io>
Date: Sat, 14 Dec 2024 01:05:14 +0200
Subject: [PATCH] Add `unstable-api` feature flag to `pallet-revive` (#6866)
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

Follow up refactor to
https://github.com/paritytech/polkadot-sdk/pull/6844#pullrequestreview-2497225717

I still need to finish adding `#[cfg(feature = "unstable-api")]` to the
rest of the tests and make sure all tests pass, I want to make sure I'm
moving into right direction first

@athei @xermicus

---------

Co-authored-by: DavidK <davidk@parity.io>
Co-authored-by: Alexander Theißen <alex.theissen@me.com>
---
 prdoc/pr_6866.prdoc                           |  13 +
 .../frame/revive/fixtures/build/_Cargo.toml   |   2 +-
 substrate/frame/revive/src/wasm/runtime.rs    | 620 +++++++++---------
 substrate/frame/revive/uapi/Cargo.toml        |   1 +
 substrate/frame/revive/uapi/src/host.rs       | 527 ++++++++-------
 .../frame/revive/uapi/src/host/riscv64.rs     | 260 ++++----
 6 files changed, 747 insertions(+), 676 deletions(-)
 create mode 100644 prdoc/pr_6866.prdoc

diff --git a/prdoc/pr_6866.prdoc b/prdoc/pr_6866.prdoc
new file mode 100644
index 00000000000..fac40dc103d
--- /dev/null
+++ b/prdoc/pr_6866.prdoc
@@ -0,0 +1,13 @@
+title: Refactor `pallet-revive-uapi` pallet
+doc:
+- audience: Runtime Dev
+  description: Puts unstable host functions in `uapi` under
+    `unstable-api` feature while moving those functions after
+    stable functions.
+crates:
+- name: pallet-revive
+  bump: patch
+- name: pallet-revive-fixtures
+  bump: patch
+- name: pallet-revive-uapi
+  bump: major
diff --git a/substrate/frame/revive/fixtures/build/_Cargo.toml b/substrate/frame/revive/fixtures/build/_Cargo.toml
index beaabd83403..8dc38e14c14 100644
--- a/substrate/frame/revive/fixtures/build/_Cargo.toml
+++ b/substrate/frame/revive/fixtures/build/_Cargo.toml
@@ -12,7 +12,7 @@ edition = "2021"
 
 # All paths are injected dynamically by the build script.
 [dependencies]
-uapi = { package = 'pallet-revive-uapi', path = "", default-features = false }
+uapi = { package = 'pallet-revive-uapi', path = "", features = ["unstable-api"], default-features = false }
 common = { package = 'pallet-revive-fixtures-common', path = "" }
 polkavm-derive = { version = "0.17.0" }
 
diff --git a/substrate/frame/revive/src/wasm/runtime.rs b/substrate/frame/revive/src/wasm/runtime.rs
index ac499171c77..648a1621c19 100644
--- a/substrate/frame/revive/src/wasm/runtime.rs
+++ b/substrate/frame/revive/src/wasm/runtime.rs
@@ -1149,19 +1149,6 @@ pub mod env {
 		self.set_storage(memory, flags, key_ptr, key_len, value_ptr, value_len)
 	}
 
-	/// Clear the value at the given key in the contract storage.
-	/// See [`pallet_revive_uapi::HostFn::clear_storage`]
-	#[mutating]
-	fn clear_storage(
-		&mut self,
-		memory: &mut M,
-		flags: u32,
-		key_ptr: u32,
-		key_len: u32,
-	) -> Result<u32, TrapReason> {
-		self.clear_storage(memory, flags, key_ptr, key_len)
-	}
-
 	/// Retrieve the value under the given key from storage.
 	/// See [`pallet_revive_uapi::HostFn::get_storage`]
 	#[stable]
@@ -1177,33 +1164,6 @@ pub mod env {
 		self.get_storage(memory, flags, key_ptr, key_len, out_ptr, out_len_ptr)
 	}
 
-	/// Checks whether there is a value stored under the given key.
-	/// See [`pallet_revive_uapi::HostFn::contains_storage`]
-	fn contains_storage(
-		&mut self,
-		memory: &mut M,
-		flags: u32,
-		key_ptr: u32,
-		key_len: u32,
-	) -> Result<u32, TrapReason> {
-		self.contains_storage(memory, flags, key_ptr, key_len)
-	}
-
-	/// Retrieve and remove the value under the given key from storage.
-	/// See [`pallet_revive_uapi::HostFn::take_storage`]
-	#[mutating]
-	fn take_storage(
-		&mut self,
-		memory: &mut M,
-		flags: u32,
-		key_ptr: u32,
-		key_len: u32,
-		out_ptr: u32,
-		out_len_ptr: u32,
-	) -> Result<ReturnErrorCode, TrapReason> {
-		self.take_storage(memory, flags, key_ptr, key_len, out_ptr, out_len_ptr)
-	}
-
 	/// Make a call to another contract.
 	/// See [`pallet_revive_uapi::HostFn::call`].
 	#[stable]
@@ -1315,13 +1275,6 @@ pub mod env {
 		)?)
 	}
 
-	/// Remove the calling account and transfer remaining **free** balance.
-	/// See [`pallet_revive_uapi::HostFn::terminate`].
-	#[mutating]
-	fn terminate(&mut self, memory: &mut M, beneficiary_ptr: u32) -> Result<(), TrapReason> {
-		self.terminate(memory, beneficiary_ptr)
-	}
-
 	/// Stores the input passed by the caller into the supplied buffer.
 	/// See [`pallet_revive_uapi::HostFn::input`].
 	#[stable]
@@ -1412,14 +1365,6 @@ pub mod env {
 		)?)
 	}
 
-	/// Checks whether a specified address belongs to a contract.
-	/// See [`pallet_revive_uapi::HostFn::is_contract`].
-	fn is_contract(&mut self, memory: &mut M, account_ptr: u32) -> Result<u32, TrapReason> {
-		self.charge_gas(RuntimeCosts::IsContract)?;
-		let address = memory.read_h160(account_ptr)?;
-		Ok(self.ext.is_contract(&address) as u32)
-	}
-
 	/// Retrieve the code hash for a specified contract address.
 	/// See [`pallet_revive_uapi::HostFn::code_hash`].
 	#[stable]
@@ -1450,34 +1395,6 @@ pub mod env {
 		)?)
 	}
 
-	/// Retrieve the code hash of the currently executing contract.
-	/// See [`pallet_revive_uapi::HostFn::own_code_hash`].
-	fn own_code_hash(&mut self, memory: &mut M, out_ptr: u32) -> Result<(), TrapReason> {
-		self.charge_gas(RuntimeCosts::OwnCodeHash)?;
-		let code_hash = *self.ext.own_code_hash();
-		Ok(self.write_fixed_sandbox_output(
-			memory,
-			out_ptr,
-			code_hash.as_bytes(),
-			false,
-			already_charged,
-		)?)
-	}
-
-	/// Checks whether the caller of the current contract is the origin of the whole call stack.
-	/// See [`pallet_revive_uapi::HostFn::caller_is_origin`].
-	fn caller_is_origin(&mut self, _memory: &mut M) -> Result<u32, TrapReason> {
-		self.charge_gas(RuntimeCosts::CallerIsOrigin)?;
-		Ok(self.ext.caller_is_origin() as u32)
-	}
-
-	/// Checks whether the caller of the current contract is root.
-	/// See [`pallet_revive_uapi::HostFn::caller_is_root`].
-	fn caller_is_root(&mut self, _memory: &mut M) -> Result<u32, TrapReason> {
-		self.charge_gas(RuntimeCosts::CallerIsRoot)?;
-		Ok(self.ext.caller_is_root() as u32)
-	}
-
 	/// Stores the address of the current contract into the supplied buffer.
 	/// See [`pallet_revive_uapi::HostFn::address`].
 	#[stable]
@@ -1514,26 +1431,6 @@ pub mod env {
 		)?)
 	}
 
-	/// Stores the amount of weight left into the supplied buffer.
-	/// See [`pallet_revive_uapi::HostFn::weight_left`].
-	fn weight_left(
-		&mut self,
-		memory: &mut M,
-		out_ptr: u32,
-		out_len_ptr: u32,
-	) -> Result<(), TrapReason> {
-		self.charge_gas(RuntimeCosts::WeightLeft)?;
-		let gas_left = &self.ext.gas_meter().gas_left().encode();
-		Ok(self.write_sandbox_output(
-			memory,
-			out_ptr,
-			out_len_ptr,
-			gas_left,
-			false,
-			already_charged,
-		)?)
-	}
-
 	/// Stores the immutable data into the supplied buffer.
 	/// See [`pallet_revive_uapi::HostFn::get_immutable_data`].
 	#[stable]
@@ -1639,19 +1536,6 @@ pub mod env {
 		)?)
 	}
 
-	/// Stores the minimum balance (a.k.a. existential deposit) into the supplied buffer.
-	/// See [`pallet_revive_uapi::HostFn::minimum_balance`].
-	fn minimum_balance(&mut self, memory: &mut M, out_ptr: u32) -> Result<(), TrapReason> {
-		self.charge_gas(RuntimeCosts::MinimumBalance)?;
-		Ok(self.write_fixed_sandbox_output(
-			memory,
-			out_ptr,
-			&self.ext.minimum_balance().to_little_endian(),
-			false,
-			already_charged,
-		)?)
-	}
-
 	/// Deposit a contract event with the data buffer and optional list of topics.
 	/// See [pallet_revive_uapi::HostFn::deposit_event]
 	#[stable]
@@ -1727,21 +1611,6 @@ pub mod env {
 		)?)
 	}
 
-	/// Computes the SHA2 256-bit hash on the given input buffer.
-	/// See [`pallet_revive_uapi::HostFn::hash_sha2_256`].
-	fn hash_sha2_256(
-		&mut self,
-		memory: &mut M,
-		input_ptr: u32,
-		input_len: u32,
-		output_ptr: u32,
-	) -> Result<(), TrapReason> {
-		self.charge_gas(RuntimeCosts::HashSha256(input_len))?;
-		Ok(self.compute_hash_on_intermediate_buffer(
-			memory, sha2_256, input_ptr, input_len, output_ptr,
-		)?)
-	}
-
 	/// Computes the KECCAK 256-bit hash on the given input buffer.
 	/// See [`pallet_revive_uapi::HostFn::hash_keccak_256`].
 	#[stable]
@@ -1758,34 +1627,44 @@ pub mod env {
 		)?)
 	}
 
-	/// Computes the BLAKE2 256-bit hash on the given input buffer.
-	/// See [`pallet_revive_uapi::HostFn::hash_blake2_256`].
-	fn hash_blake2_256(
-		&mut self,
-		memory: &mut M,
-		input_ptr: u32,
-		input_len: u32,
-		output_ptr: u32,
-	) -> Result<(), TrapReason> {
-		self.charge_gas(RuntimeCosts::HashBlake256(input_len))?;
-		Ok(self.compute_hash_on_intermediate_buffer(
-			memory, blake2_256, input_ptr, input_len, output_ptr,
+	/// Stores the length of the data returned by the last call into the supplied buffer.
+	/// See [`pallet_revive_uapi::HostFn::return_data_size`].
+	#[stable]
+	fn return_data_size(&mut self, memory: &mut M, out_ptr: u32) -> Result<(), TrapReason> {
+		Ok(self.write_fixed_sandbox_output(
+			memory,
+			out_ptr,
+			&U256::from(self.ext.last_frame_output().data.len()).to_little_endian(),
+			false,
+			|len| Some(RuntimeCosts::CopyToContract(len)),
 		)?)
 	}
 
-	/// Computes the BLAKE2 128-bit hash on the given input buffer.
-	/// See [`pallet_revive_uapi::HostFn::hash_blake2_128`].
-	fn hash_blake2_128(
+	/// Stores data returned by the last call, starting from `offset`, into the supplied buffer.
+	/// See [`pallet_revive_uapi::HostFn::return_data`].
+	#[stable]
+	fn return_data_copy(
 		&mut self,
 		memory: &mut M,
-		input_ptr: u32,
-		input_len: u32,
-		output_ptr: u32,
+		out_ptr: u32,
+		out_len_ptr: u32,
+		offset: u32,
 	) -> Result<(), TrapReason> {
-		self.charge_gas(RuntimeCosts::HashBlake128(input_len))?;
-		Ok(self.compute_hash_on_intermediate_buffer(
-			memory, blake2_128, input_ptr, input_len, output_ptr,
-		)?)
+		let output = mem::take(self.ext.last_frame_output_mut());
+		let result = if offset as usize > output.data.len() {
+			Err(Error::<E::T>::OutOfBounds.into())
+		} else {
+			self.write_sandbox_output(
+				memory,
+				out_ptr,
+				out_len_ptr,
+				&output.data[offset as usize..],
+				false,
+				|len| Some(RuntimeCosts::CopyToContract(len)),
+			)
+		};
+		*self.ext.last_frame_output_mut() = output;
+		Ok(result?)
 	}
 
 	/// Call into the chain extension provided by the chain if any.
@@ -1818,27 +1697,6 @@ pub mod env {
 		ret
 	}
 
-	/// Emit a custom debug message.
-	/// See [`pallet_revive_uapi::HostFn::debug_message`].
-	fn debug_message(
-		&mut self,
-		memory: &mut M,
-		str_ptr: u32,
-		str_len: u32,
-	) -> Result<ReturnErrorCode, TrapReason> {
-		let str_len = str_len.min(limits::DEBUG_BUFFER_BYTES);
-		self.charge_gas(RuntimeCosts::DebugMessage(str_len))?;
-		if self.ext.append_debug_buffer("") {
-			let data = memory.read(str_ptr, str_len)?;
-			if let Some(msg) = core::str::from_utf8(&data).ok() {
-				self.ext.append_debug_buffer(msg);
-			}
-			Ok(ReturnErrorCode::Success)
-		} else {
-			Ok(ReturnErrorCode::LoggingDisabled)
-		}
-	}
-
 	/// Call some dispatchable of the runtime.
 	/// See [`frame_support::traits::call_runtime`].
 	#[mutating]
@@ -1858,80 +1716,63 @@ pub mod env {
 		)
 	}
 
-	/// Execute an XCM program locally, using the contract's address as the origin.
-	/// See [`pallet_revive_uapi::HostFn::execute_xcm`].
+	/// Checks whether the caller of the current contract is the origin of the whole call stack.
+	/// See [`pallet_revive_uapi::HostFn::caller_is_origin`].
+	fn caller_is_origin(&mut self, _memory: &mut M) -> Result<u32, TrapReason> {
+		self.charge_gas(RuntimeCosts::CallerIsOrigin)?;
+		Ok(self.ext.caller_is_origin() as u32)
+	}
+
+	/// Checks whether the caller of the current contract is root.
+	/// See [`pallet_revive_uapi::HostFn::caller_is_root`].
+	fn caller_is_root(&mut self, _memory: &mut M) -> Result<u32, TrapReason> {
+		self.charge_gas(RuntimeCosts::CallerIsRoot)?;
+		Ok(self.ext.caller_is_root() as u32)
+	}
+
+	/// Clear the value at the given key in the contract storage.
+	/// See [`pallet_revive_uapi::HostFn::clear_storage`]
 	#[mutating]
-	fn xcm_execute(
+	fn clear_storage(
 		&mut self,
 		memory: &mut M,
-		msg_ptr: u32,
-		msg_len: u32,
-	) -> Result<ReturnErrorCode, TrapReason> {
-		use frame_support::dispatch::DispatchInfo;
-		use xcm::VersionedXcm;
-		use xcm_builder::{ExecuteController, ExecuteControllerWeightInfo};
-
-		self.charge_gas(RuntimeCosts::CopyFromContract(msg_len))?;
-		let message: VersionedXcm<CallOf<E::T>> = memory.read_as_unbounded(msg_ptr, msg_len)?;
+		flags: u32,
+		key_ptr: u32,
+		key_len: u32,
+	) -> Result<u32, TrapReason> {
+		self.clear_storage(memory, flags, key_ptr, key_len)
+	}
 
-		let execute_weight =
-			<<E::T as Config>::Xcm as ExecuteController<_, _>>::WeightInfo::execute();
-		let weight = self.ext.gas_meter().gas_left().max(execute_weight);
-		let dispatch_info = DispatchInfo { call_weight: weight, ..Default::default() };
+	/// Checks whether there is a value stored under the given key.
+	/// See [`pallet_revive_uapi::HostFn::contains_storage`]
+	fn contains_storage(
+		&mut self,
+		memory: &mut M,
+		flags: u32,
+		key_ptr: u32,
+		key_len: u32,
+	) -> Result<u32, TrapReason> {
+		self.contains_storage(memory, flags, key_ptr, key_len)
+	}
 
-		self.call_dispatchable::<XcmExecutionFailed>(
-			dispatch_info,
-			RuntimeCosts::CallXcmExecute,
-			|runtime| {
-				let origin = crate::RawOrigin::Signed(runtime.ext.account_id().clone()).into();
-				let weight_used = <<E::T as Config>::Xcm>::execute(
-					origin,
-					Box::new(message),
-					weight.saturating_sub(execute_weight),
-				)?;
-
-				Ok(Some(weight_used.saturating_add(execute_weight)).into())
-			},
-		)
-	}
-
-	/// Send an XCM program from the contract to the specified destination.
-	/// See [`pallet_revive_uapi::HostFn::send_xcm`].
-	#[mutating]
-	fn xcm_send(
+	/// Emit a custom debug message.
+	/// See [`pallet_revive_uapi::HostFn::debug_message`].
+	fn debug_message(
 		&mut self,
 		memory: &mut M,
-		dest_ptr: u32,
-		dest_len: u32,
-		msg_ptr: u32,
-		msg_len: u32,
-		output_ptr: u32,
+		str_ptr: u32,
+		str_len: u32,
 	) -> Result<ReturnErrorCode, TrapReason> {
-		use xcm::{VersionedLocation, VersionedXcm};
-		use xcm_builder::{SendController, SendControllerWeightInfo};
-
-		self.charge_gas(RuntimeCosts::CopyFromContract(dest_len))?;
-		let dest: VersionedLocation = memory.read_as_unbounded(dest_ptr, dest_len)?;
-
-		self.charge_gas(RuntimeCosts::CopyFromContract(msg_len))?;
-		let message: VersionedXcm<()> = memory.read_as_unbounded(msg_ptr, msg_len)?;
-
-		let weight = <<E::T as Config>::Xcm as SendController<_>>::WeightInfo::send();
-		self.charge_gas(RuntimeCosts::CallRuntime(weight))?;
-		let origin = crate::RawOrigin::Signed(self.ext.account_id().clone()).into();
-
-		match <<E::T as Config>::Xcm>::send(origin, dest.into(), message.into()) {
-			Ok(message_id) => {
-				memory.write(output_ptr, &message_id.encode())?;
-				Ok(ReturnErrorCode::Success)
-			},
-			Err(e) => {
-				if self.ext.append_debug_buffer("") {
-					self.ext.append_debug_buffer("seal0::xcm_send failed with: ");
-					self.ext.append_debug_buffer(e.into());
-				};
-				Ok(ReturnErrorCode::XcmSendFailed)
-			},
+		let str_len = str_len.min(limits::DEBUG_BUFFER_BYTES);
+		self.charge_gas(RuntimeCosts::DebugMessage(str_len))?;
+		if self.ext.append_debug_buffer("") {
+			let data = memory.read(str_ptr, str_len)?;
+			if let Some(msg) = core::str::from_utf8(&data).ok() {
+				self.ext.append_debug_buffer(msg);
+			}
+			Ok(ReturnErrorCode::Success)
+		} else {
+			Ok(ReturnErrorCode::LoggingDisabled)
 		}
 	}
 
@@ -1965,46 +1806,6 @@ pub mod env {
 		}
 	}
 
-	/// Verify a sr25519 signature
-	/// See [`pallet_revive_uapi::HostFn::sr25519_verify`].
-	fn sr25519_verify(
-		&mut self,
-		memory: &mut M,
-		signature_ptr: u32,
-		pub_key_ptr: u32,
-		message_len: u32,
-		message_ptr: u32,
-	) -> Result<ReturnErrorCode, TrapReason> {
-		self.charge_gas(RuntimeCosts::Sr25519Verify(message_len))?;
-
-		let mut signature: [u8; 64] = [0; 64];
-		memory.read_into_buf(signature_ptr, &mut signature)?;
-
-		let mut pub_key: [u8; 32] = [0; 32];
-		memory.read_into_buf(pub_key_ptr, &mut pub_key)?;
-
-		let message: Vec<u8> = memory.read(message_ptr, message_len)?;
-
-		if self.ext.sr25519_verify(&signature, &message, &pub_key) {
-			Ok(ReturnErrorCode::Success)
-		} else {
-			Ok(ReturnErrorCode::Sr25519VerifyFailed)
-		}
-	}
-
-	/// Replace the contract code at the specified address with new code.
-	/// See [`pallet_revive_uapi::HostFn::set_code_hash`].
-	///
-	/// Disabled until the internal implementation takes care of collecting
-	/// the immutable data of the new code hash.
-	#[mutating]
-	fn set_code_hash(&mut self, memory: &mut M, code_hash_ptr: u32) -> Result<(), TrapReason> {
-		self.charge_gas(RuntimeCosts::SetCodeHash)?;
-		let code_hash: H256 = memory.read_h256(code_hash_ptr)?;
-		self.ext.set_code_hash(code_hash)?;
-		Ok(())
-	}
-
 	/// Calculates Ethereum address from the ECDSA compressed public key and stores
 	/// See [`pallet_revive_uapi::HostFn::ecdsa_to_eth_address`].
 	fn ecdsa_to_eth_address(
@@ -2026,6 +1827,59 @@ pub mod env {
 		}
 	}
 
+	/// Computes the BLAKE2 128-bit hash on the given input buffer.
+	/// See [`pallet_revive_uapi::HostFn::hash_blake2_128`].
+	fn hash_blake2_128(
+		&mut self,
+		memory: &mut M,
+		input_ptr: u32,
+		input_len: u32,
+		output_ptr: u32,
+	) -> Result<(), TrapReason> {
+		self.charge_gas(RuntimeCosts::HashBlake128(input_len))?;
+		Ok(self.compute_hash_on_intermediate_buffer(
+			memory, blake2_128, input_ptr, input_len, output_ptr,
+		)?)
+	}
+
+	/// Computes the BLAKE2 256-bit hash on the given input buffer.
+	/// See [`pallet_revive_uapi::HostFn::hash_blake2_256`].
+	fn hash_blake2_256(
+		&mut self,
+		memory: &mut M,
+		input_ptr: u32,
+		input_len: u32,
+		output_ptr: u32,
+	) -> Result<(), TrapReason> {
+		self.charge_gas(RuntimeCosts::HashBlake256(input_len))?;
+		Ok(self.compute_hash_on_intermediate_buffer(
+			memory, blake2_256, input_ptr, input_len, output_ptr,
+		)?)
+	}
+
+	/// Computes the SHA2 256-bit hash on the given input buffer.
+	/// See [`pallet_revive_uapi::HostFn::hash_sha2_256`].
+	fn hash_sha2_256(
+		&mut self,
+		memory: &mut M,
+		input_ptr: u32,
+		input_len: u32,
+		output_ptr: u32,
+	) -> Result<(), TrapReason> {
+		self.charge_gas(RuntimeCosts::HashSha256(input_len))?;
+		Ok(self.compute_hash_on_intermediate_buffer(
+			memory, sha2_256, input_ptr, input_len, output_ptr,
+		)?)
+	}
+
+	/// Checks whether a specified address belongs to a contract.
+	/// See [`pallet_revive_uapi::HostFn::is_contract`].
+	fn is_contract(&mut self, memory: &mut M, account_ptr: u32) -> Result<u32, TrapReason> {
+		self.charge_gas(RuntimeCosts::IsContract)?;
+		let address = memory.read_h160(account_ptr)?;
+		Ok(self.ext.is_contract(&address) as u32)
+	}
+
 	/// Adds a new delegate dependency to the contract.
 	/// See [`pallet_revive_uapi::HostFn::lock_delegate_dependency`].
 	#[mutating]
@@ -2040,6 +1894,73 @@ pub mod env {
 		Ok(())
 	}
 
+	/// Stores the minimum balance (a.k.a. existential deposit) into the supplied buffer.
+	/// See [`pallet_revive_uapi::HostFn::minimum_balance`].
+	fn minimum_balance(&mut self, memory: &mut M, out_ptr: u32) -> Result<(), TrapReason> {
+		self.charge_gas(RuntimeCosts::MinimumBalance)?;
+		Ok(self.write_fixed_sandbox_output(
+			memory,
+			out_ptr,
+			&self.ext.minimum_balance().to_little_endian(),
+			false,
+			already_charged,
+		)?)
+	}
+
+	/// Retrieve the code hash of the currently executing contract.
+	/// See [`pallet_revive_uapi::HostFn::own_code_hash`].
+	fn own_code_hash(&mut self, memory: &mut M, out_ptr: u32) -> Result<(), TrapReason> {
+		self.charge_gas(RuntimeCosts::OwnCodeHash)?;
+		let code_hash = *self.ext.own_code_hash();
+		Ok(self.write_fixed_sandbox_output(
+			memory,
+			out_ptr,
+			code_hash.as_bytes(),
+			false,
+			already_charged,
+		)?)
+	}
+
+	/// Replace the contract code at the specified address with new code.
+	/// See [`pallet_revive_uapi::HostFn::set_code_hash`].
+	///
+	/// Disabled until the internal implementation takes care of collecting
+	/// the immutable data of the new code hash.
+	#[mutating]
+	fn set_code_hash(&mut self, memory: &mut M, code_hash_ptr: u32) -> Result<(), TrapReason> {
+		self.charge_gas(RuntimeCosts::SetCodeHash)?;
+		let code_hash: H256 = memory.read_h256(code_hash_ptr)?;
+		self.ext.set_code_hash(code_hash)?;
+		Ok(())
+	}
+
+	/// Verify a sr25519 signature
+	/// See [`pallet_revive_uapi::HostFn::sr25519_verify`].
+	fn sr25519_verify(
+		&mut self,
+		memory: &mut M,
+		signature_ptr: u32,
+		pub_key_ptr: u32,
+		message_len: u32,
+		message_ptr: u32,
+	) -> Result<ReturnErrorCode, TrapReason> {
+		self.charge_gas(RuntimeCosts::Sr25519Verify(message_len))?;
+
+		let mut signature: [u8; 64] = [0; 64];
+		memory.read_into_buf(signature_ptr, &mut signature)?;
+
+		let mut pub_key: [u8; 32] = [0; 32];
+		memory.read_into_buf(pub_key_ptr, &mut pub_key)?;
+
+		let message: Vec<u8> = memory.read(message_ptr, message_len)?;
+
+		if self.ext.sr25519_verify(&signature, &message, &pub_key) {
+			Ok(ReturnErrorCode::Success)
+		} else {
+			Ok(ReturnErrorCode::Sr25519VerifyFailed)
+		}
+	}
+
 	/// Removes the delegate dependency from the contract.
 	/// see [`pallet_revive_uapi::HostFn::unlock_delegate_dependency`].
 	#[mutating]
@@ -2054,43 +1975,122 @@ pub mod env {
 		Ok(())
 	}
 
-	/// Stores the length of the data returned by the last call into the supplied buffer.
-	/// See [`pallet_revive_uapi::HostFn::return_data_size`].
-	#[stable]
-	fn return_data_size(&mut self, memory: &mut M, out_ptr: u32) -> Result<(), TrapReason> {
-		Ok(self.write_fixed_sandbox_output(
+	/// Retrieve and remove the value under the given key from storage.
+	/// See [`pallet_revive_uapi::HostFn::take_storage`]
+	#[mutating]
+	fn take_storage(
+		&mut self,
+		memory: &mut M,
+		flags: u32,
+		key_ptr: u32,
+		key_len: u32,
+		out_ptr: u32,
+		out_len_ptr: u32,
+	) -> Result<ReturnErrorCode, TrapReason> {
+		self.take_storage(memory, flags, key_ptr, key_len, out_ptr, out_len_ptr)
+	}
+
+	/// Remove the calling account and transfer remaining **free** balance.
+	/// See [`pallet_revive_uapi::HostFn::terminate`].
+	#[mutating]
+	fn terminate(&mut self, memory: &mut M, beneficiary_ptr: u32) -> Result<(), TrapReason> {
+		self.terminate(memory, beneficiary_ptr)
+	}
+
+	/// Stores the amount of weight left into the supplied buffer.
+	/// See [`pallet_revive_uapi::HostFn::weight_left`].
+	fn weight_left(
+		&mut self,
+		memory: &mut M,
+		out_ptr: u32,
+		out_len_ptr: u32,
+	) -> Result<(), TrapReason> {
+		self.charge_gas(RuntimeCosts::WeightLeft)?;
+		let gas_left = &self.ext.gas_meter().gas_left().encode();
+		Ok(self.write_sandbox_output(
 			memory,
 			out_ptr,
-			&U256::from(self.ext.last_frame_output().data.len()).to_little_endian(),
+			out_len_ptr,
+			gas_left,
 			false,
-			|len| Some(RuntimeCosts::CopyToContract(len)),
+			already_charged,
 		)?)
 	}
 
-	/// Stores data returned by the last call, starting from `offset`, into the supplied buffer.
-	/// See [`pallet_revive_uapi::HostFn::return_data`].
-	#[stable]
-	fn return_data_copy(
+	/// Execute an XCM program locally, using the contract's address as the origin.
+	/// See [`pallet_revive_uapi::HostFn::execute_xcm`].
+	#[mutating]
+	fn xcm_execute(
 		&mut self,
 		memory: &mut M,
-		out_ptr: u32,
-		out_len_ptr: u32,
-		offset: u32,
-	) -> Result<(), TrapReason> {
-		let output = mem::take(self.ext.last_frame_output_mut());
-		let result = if offset as usize > output.data.len() {
-			Err(Error::<E::T>::OutOfBounds.into())
-		} else {
-			self.write_sandbox_output(
-				memory,
-				out_ptr,
-				out_len_ptr,
-				&output.data[offset as usize..],
-				false,
-				|len| Some(RuntimeCosts::CopyToContract(len)),
-			)
-		};
-		*self.ext.last_frame_output_mut() = output;
-		Ok(result?)
+		msg_ptr: u32,
+		msg_len: u32,
+	) -> Result<ReturnErrorCode, TrapReason> {
+		use frame_support::dispatch::DispatchInfo;
+		use xcm::VersionedXcm;
+		use xcm_builder::{ExecuteController, ExecuteControllerWeightInfo};
+
+		self.charge_gas(RuntimeCosts::CopyFromContract(msg_len))?;
+		let message: VersionedXcm<CallOf<E::T>> = memory.read_as_unbounded(msg_ptr, msg_len)?;
+
+		let execute_weight =
+			<<E::T as Config>::Xcm as ExecuteController<_, _>>::WeightInfo::execute();
+		let weight = self.ext.gas_meter().gas_left().max(execute_weight);
+		let dispatch_info = DispatchInfo { call_weight: weight, ..Default::default() };
+
+		self.call_dispatchable::<XcmExecutionFailed>(
+			dispatch_info,
+			RuntimeCosts::CallXcmExecute,
+			|runtime| {
+				let origin = crate::RawOrigin::Signed(runtime.ext.account_id().clone()).into();
+				let weight_used = <<E::T as Config>::Xcm>::execute(
+					origin,
+					Box::new(message),
+					weight.saturating_sub(execute_weight),
+				)?;
+
+				Ok(Some(weight_used.saturating_add(execute_weight)).into())
+			},
+		)
+	}
+
+	/// Send an XCM program from the contract to the specified destination.
+	/// See [`pallet_revive_uapi::HostFn::send_xcm`].
+	#[mutating]
+	fn xcm_send(
+		&mut self,
+		memory: &mut M,
+		dest_ptr: u32,
+		dest_len: u32,
+		msg_ptr: u32,
+		msg_len: u32,
+		output_ptr: u32,
+	) -> Result<ReturnErrorCode, TrapReason> {
+		use xcm::{VersionedLocation, VersionedXcm};
+		use xcm_builder::{SendController, SendControllerWeightInfo};
+
+		self.charge_gas(RuntimeCosts::CopyFromContract(dest_len))?;
+		let dest: VersionedLocation = memory.read_as_unbounded(dest_ptr, dest_len)?;
+
+		self.charge_gas(RuntimeCosts::CopyFromContract(msg_len))?;
+		let message: VersionedXcm<()> = memory.read_as_unbounded(msg_ptr, msg_len)?;
+
+		let weight = <<E::T as Config>::Xcm as SendController<_>>::WeightInfo::send();
+		self.charge_gas(RuntimeCosts::CallRuntime(weight))?;
+		let origin = crate::RawOrigin::Signed(self.ext.account_id().clone()).into();
+
+		match <<E::T as Config>::Xcm>::send(origin, dest.into(), message.into()) {
+			Ok(message_id) => {
+				memory.write(output_ptr, &message_id.encode())?;
+				Ok(ReturnErrorCode::Success)
+			},
+			Err(e) => {
+				if self.ext.append_debug_buffer("") {
+					self.ext.append_debug_buffer("seal0::xcm_send failed with: ");
+					self.ext.append_debug_buffer(e.into());
+				};
+				Ok(ReturnErrorCode::XcmSendFailed)
+			},
+		}
 	}
 }
diff --git a/substrate/frame/revive/uapi/Cargo.toml b/substrate/frame/revive/uapi/Cargo.toml
index b55391dd5d6..1af5b327dfc 100644
--- a/substrate/frame/revive/uapi/Cargo.toml
+++ b/substrate/frame/revive/uapi/Cargo.toml
@@ -29,3 +29,4 @@ default-target = ["riscv64imac-unknown-none-elf"]
 [features]
 default = ["scale"]
 scale = ["dep:codec", "scale-info"]
+unstable-api = []
diff --git a/substrate/frame/revive/uapi/src/host.rs b/substrate/frame/revive/uapi/src/host.rs
index a8c8a924aee..aa320369789 100644
--- a/substrate/frame/revive/uapi/src/host.rs
+++ b/substrate/frame/revive/uapi/src/host.rs
@@ -45,17 +45,6 @@ pub trait HostFn: private::Sealed {
 	/// - `output`: A reference to the output data buffer to write the address.
 	fn address(output: &mut [u8; 20]);
 
-	/// Lock a new delegate dependency to the contract.
-	///
-	/// Traps if the maximum number of delegate_dependencies is reached or if
-	/// the delegate dependency already exists.
-	///
-	/// # Parameters
-	///
-	/// - `code_hash`: The code hash of the dependency. Should be decodable as an `T::Hash`. Traps
-	///   otherwise.
-	fn lock_delegate_dependency(code_hash: &[u8; 32]);
-
 	/// Get the contract immutable data.
 	///
 	/// Traps if:
@@ -105,21 +94,6 @@ pub trait HostFn: private::Sealed {
 	/// - `output`: A reference to the output data buffer to write the call data size.
 	fn call_data_size(output: &mut [u8; 32]);
 
-	/// Stores the current block number of the current contract into the supplied buffer.
-	///
-	/// # Parameters
-	///
-	/// - `output`: A reference to the output data buffer to write the block number.
-	fn block_number(output: &mut [u8; 32]);
-
-	/// Stores the block hash of the given block number into the supplied buffer.
-	///
-	/// # Parameters
-	///
-	/// - `block_number`: A reference to the block number buffer.
-	/// - `output`: A reference to the output data buffer to write the block number.
-	fn block_hash(block_number: &[u8; 32], output: &mut [u8; 32]);
-
 	/// Call (possibly transferring some amount of funds) into the specified account.
 	///
 	/// # Parameters
@@ -157,56 +131,6 @@ pub trait HostFn: private::Sealed {
 		output: Option<&mut &mut [u8]>,
 	) -> Result;
 
-	/// Call into the chain extension provided by the chain if any.
-	///
-	/// Handling of the input values is up to the specific chain extension and so is the
-	/// return value. The extension can decide to use the inputs as primitive inputs or as
-	/// in/out arguments by interpreting them as pointers. Any caller of this function
-	/// must therefore coordinate with the chain that it targets.
-	///
-	/// # Note
-	///
-	/// If no chain extension exists the contract will trap with the `NoChainExtension`
-	/// module error.
-	///
-	/// # Parameters
-	///
-	/// - `func_id`: The function id of the chain extension.
-	/// - `input`: The input data buffer.
-	/// - `output`: A reference to the output data buffer to write the call output buffer. If `None`
-	///   is provided then the output buffer is not copied.
-	///
-	/// # Return
-	///
-	/// The chain extension returned value, if executed successfully.
-	fn call_chain_extension(func_id: u32, input: &[u8], output: Option<&mut &mut [u8]>) -> u32;
-
-	/// Call some dispatchable of the runtime.
-	///
-	/// # Parameters
-	///
-	/// - `call`: The call data.
-	///
-	/// # Return
-	///
-	/// Returns `Error::Success` when the dispatchable was successfully executed and
-	/// returned `Ok`. When the dispatchable was executed but returned an error
-	/// `Error::CallRuntimeFailed` is returned. The full error is not
-	/// provided because it is not guaranteed to be stable.
-	///
-	/// # Comparison with `ChainExtension`
-	///
-	/// Just as a chain extension this API allows the runtime to extend the functionality
-	/// of contracts. While making use of this function is generally easier it cannot be
-	/// used in all cases. Consider writing a chain extension if you need to do perform
-	/// one of the following tasks:
-	///
-	/// - Return data.
-	/// - Provide functionality **exclusively** to contracts.
-	/// - Provide custom weights.
-	/// - Avoid the need to keep the `Call` data structure stable.
-	fn call_runtime(call: &[u8]) -> Result;
-
 	/// Stores the address of the caller into the supplied buffer.
 	///
 	/// If this is a top-level call (i.e. initiated by an extrinsic) the origin address of the
@@ -232,38 +156,6 @@ pub trait HostFn: private::Sealed {
 	/// - `output`: A reference to the output data buffer to write the origin's address.
 	fn origin(output: &mut [u8; 20]);
 
-	/// Checks whether the caller of the current contract is the origin of the whole call stack.
-	///
-	/// Prefer this over [`is_contract()`][`Self::is_contract`] when checking whether your contract
-	/// is being called by a contract or a plain account. The reason is that it performs better
-	/// since it does not need to do any storage lookups.
-	///
-	/// # Return
-	///
-	/// A return value of `true` indicates that this contract is being called by a plain account
-	/// and `false` indicates that the caller is another contract.
-	fn caller_is_origin() -> bool;
-
-	/// Checks whether the caller of the current contract is root.
-	///
-	/// Note that only the origin of the call stack can be root. Hence this function returning
-	/// `true` implies that the contract is being called by the origin.
-	///
-	/// A return value of `true` indicates that this contract is being called by a root origin,
-	/// and `false` indicates that the caller is a signed origin.
-	fn caller_is_root() -> u32;
-
-	/// Clear the value at the given key in the contract storage.
-	///
-	/// # Parameters
-	///
-	/// - `key`: The storage key.
-	///
-	/// # Return
-	///
-	/// Returns the size of the pre-existing value at the specified key if any.
-	fn clear_storage(flags: StorageFlags, key: &[u8]) -> Option<u32>;
-
 	/// Retrieve the code hash for a specified contract address.
 	///
 	/// # Parameters
@@ -290,37 +182,6 @@ pub trait HostFn: private::Sealed {
 	/// If `addr` is not a contract the `output` will be zero.
 	fn code_size(addr: &[u8; 20], output: &mut [u8; 32]);
 
-	/// Checks whether there is a value stored under the given key.
-	///
-	/// The key length must not exceed the maximum defined by the contracts module parameter.
-	///
-	/// # Parameters
-	/// - `key`: The storage key.
-	///
-	/// # Return
-	///
-	/// Returns the size of the pre-existing value at the specified key if any.
-	fn contains_storage(flags: StorageFlags, key: &[u8]) -> Option<u32>;
-
-	/// Emit a custom debug message.
-	///
-	/// No newlines are added to the supplied message.
-	/// Specifying invalid UTF-8 just drops the message with no trap.
-	///
-	/// This is a no-op if debug message recording is disabled which is always the case
-	/// when the code is executing on-chain. The message is interpreted as UTF-8 and
-	/// appended to the debug buffer which is then supplied to the calling RPC client.
-	///
-	/// # Note
-	///
-	/// Even though no action is taken when debug message recording is disabled there is still
-	/// a non trivial overhead (and weight cost) associated with calling this function. Contract
-	/// languages should remove calls to this function (either at runtime or compile time) when
-	/// not being executed as an RPC. For example, they could allow users to disable logging
-	/// through compile time flags (cargo features) for on-chain deployment. Additionally, the
-	/// return value of this function can be cached in order to prevent further calls at runtime.
-	fn debug_message(str: &[u8]) -> Result;
-
 	/// Execute code in the context (storage, caller, value) of the current contract.
 	///
 	/// Reentrancy protection is always disabled since the callee is allowed
@@ -369,49 +230,6 @@ pub trait HostFn: private::Sealed {
 	/// - `topics`: The topics list. It can't contain duplicates.
 	fn deposit_event(topics: &[[u8; 32]], data: &[u8]);
 
-	/// Recovers the ECDSA public key from the given message hash and signature.
-	///
-	/// Writes the public key into the given output buffer.
-	/// Assumes the secp256k1 curve.
-	///
-	/// # Parameters
-	///
-	/// - `signature`: The signature bytes.
-	/// - `message_hash`: The message hash bytes.
-	/// - `output`: A reference to the output data buffer to write the public key.
-	///
-	/// # Errors
-	///
-	/// - [EcdsaRecoveryFailed][`crate::ReturnErrorCode::EcdsaRecoveryFailed]
-	fn ecdsa_recover(
-		signature: &[u8; 65],
-		message_hash: &[u8; 32],
-		output: &mut [u8; 33],
-	) -> Result;
-
-	/// Calculates Ethereum address from the ECDSA compressed public key and stores
-	/// it into the supplied buffer.
-	///
-	/// # Parameters
-	///
-	/// - `pubkey`: The public key bytes.
-	/// - `output`: A reference to the output data buffer to write the address.
-	///
-	/// # Errors
-	///
-	/// - [EcdsaRecoveryFailed][`crate::ReturnErrorCode::EcdsaRecoveryFailed]
-	fn ecdsa_to_eth_address(pubkey: &[u8; 33], output: &mut [u8; 20]) -> Result;
-
-	/// Stores the amount of weight left into the supplied buffer.
-	/// The data is encoded as Weight.
-	///
-	/// If the available space in `output` is less than the size of the value a trap is triggered.
-	///
-	/// # Parameters
-	///
-	/// - `output`: A reference to the output data buffer to write the weight left.
-	fn weight_left(output: &mut &mut [u8]);
-
 	/// Retrieve the value under the given key from storage.
 	///
 	/// The key length must not exceed the maximum defined by the contracts module parameter.
@@ -425,10 +243,7 @@ pub trait HostFn: private::Sealed {
 	/// [KeyNotFound][`crate::ReturnErrorCode::KeyNotFound]
 	fn get_storage(flags: StorageFlags, key: &[u8], output: &mut &mut [u8]) -> Result;
 
-	hash_fn!(sha2_256, 32);
 	hash_fn!(keccak_256, 32);
-	hash_fn!(blake2_256, 32);
-	hash_fn!(blake2_128, 16);
 
 	/// Stores the input passed by the caller into the supplied buffer.
 	///
@@ -503,65 +318,294 @@ pub trait HostFn: private::Sealed {
 		salt: Option<&[u8; 32]>,
 	) -> Result;
 
-	/// Checks whether a specified address belongs to a contract.
+	/// Load the latest block timestamp into the supplied buffer
 	///
 	/// # Parameters
 	///
-	/// - `address`: The address to check
+	/// - `output`: A reference to the output data buffer to write the timestamp.
+	fn now(output: &mut [u8; 32]);
+
+	/// Cease contract execution and save a data buffer as a result of the execution.
+	///
+	/// This function never returns as it stops execution of the caller.
+	/// This is the only way to return a data buffer to the caller. Returning from
+	/// execution without calling this function is equivalent to calling:
+	/// ```nocompile
+	/// return_value(ReturnFlags::empty(), &[])
+	/// ```
+	///
+	/// Using an unnamed non empty `ReturnFlags` triggers a trap.
+	///
+	/// # Parameters
+	///
+	/// - `flags`: Flag used to signal special return conditions to the supervisor. See
+	///   [`ReturnFlags`] for a documentation of the supported flags.
+	/// - `return_value`: The return value buffer.
+	fn return_value(flags: ReturnFlags, return_value: &[u8]) -> !;
+
+	/// Set the value at the given key in the contract storage.
+	///
+	/// The key and value lengths must not exceed the maximums defined by the contracts module
+	/// parameters.
+	///
+	/// # Parameters
+	///
+	/// - `key`: The storage key.
+	/// - `encoded_value`: The storage value.
 	///
 	/// # Return
 	///
-	/// Returns `true` if the address belongs to a contract.
-	fn is_contract(address: &[u8; 20]) -> bool;
+	/// Returns the size of the pre-existing value at the specified key if any.
+	fn set_storage(flags: StorageFlags, key: &[u8], value: &[u8]) -> Option<u32>;
 
-	/// Stores the minimum balance (a.k.a. existential deposit) into the supplied buffer.
+	/// Stores the value transferred along with this call/instantiate into the supplied buffer.
 	///
 	/// # Parameters
 	///
-	/// - `output`: A reference to the output data buffer to write the minimum balance.
-	fn minimum_balance(output: &mut [u8; 32]);
+	/// - `output`: A reference to the output data buffer to write the transferred value.
+	fn value_transferred(output: &mut [u8; 32]);
 
-	/// Retrieve the code hash of the currently executing contract.
+	/// Stores the price for the specified amount of gas into the supplied buffer.
 	///
 	/// # Parameters
 	///
-	/// - `output`: A reference to the output data buffer to write the code hash.
-	fn own_code_hash(output: &mut [u8; 32]);
+	/// - `ref_time_limit`: The *ref_time* Weight limit to query the price for.
+	/// - `proof_size_limit`: The *proof_size* Weight limit to query the price for.
+	/// - `output`: A reference to the output data buffer to write the price.
+	fn weight_to_fee(ref_time_limit: u64, proof_size_limit: u64, output: &mut [u8; 32]);
 
-	/// Load the latest block timestamp into the supplied buffer
+	/// Stores the size of the returned data of the last contract call or instantiation.
 	///
 	/// # Parameters
 	///
-	/// - `output`: A reference to the output data buffer to write the timestamp.
-	fn now(output: &mut [u8; 32]);
+	/// - `output`: A reference to the output buffer to write the size.
+	fn return_data_size(output: &mut [u8; 32]);
 
-	/// Removes the delegate dependency from the contract.
+	/// Stores the returned data of the last contract call or contract instantiation.
 	///
-	/// Traps if the delegate dependency does not exist.
+	/// # Parameters
+	/// - `output`: A reference to the output buffer to write the data.
+	/// - `offset`: Byte offset into the returned data
+	fn return_data_copy(output: &mut &mut [u8], offset: u32);
+
+	/// Stores the current block number of the current contract into the supplied buffer.
+	///
+	/// # Parameters
+	///
+	/// - `output`: A reference to the output data buffer to write the block number.
+	#[cfg(feature = "unstable-api")]
+	fn block_number(output: &mut [u8; 32]);
+
+	/// Stores the block hash of the given block number into the supplied buffer.
+	///
+	/// # Parameters
+	///
+	/// - `block_number`: A reference to the block number buffer.
+	/// - `output`: A reference to the output data buffer to write the block number.
+	#[cfg(feature = "unstable-api")]
+	fn block_hash(block_number: &[u8; 32], output: &mut [u8; 32]);
+
+	/// Call into the chain extension provided by the chain if any.
+	///
+	/// Handling of the input values is up to the specific chain extension and so is the
+	/// return value. The extension can decide to use the inputs as primitive inputs or as
+	/// in/out arguments by interpreting them as pointers. Any caller of this function
+	/// must therefore coordinate with the chain that it targets.
+	///
+	/// # Note
+	///
+	/// If no chain extension exists the contract will trap with the `NoChainExtension`
+	/// module error.
+	///
+	/// # Parameters
+	///
+	/// - `func_id`: The function id of the chain extension.
+	/// - `input`: The input data buffer.
+	/// - `output`: A reference to the output data buffer to write the call output buffer. If `None`
+	///   is provided then the output buffer is not copied.
+	///
+	/// # Return
+	///
+	/// The chain extension returned value, if executed successfully.
+	#[cfg(feature = "unstable-api")]
+	fn call_chain_extension(func_id: u32, input: &[u8], output: Option<&mut &mut [u8]>) -> u32;
+
+	/// Call some dispatchable of the runtime.
+	///
+	/// # Parameters
+	///
+	/// - `call`: The call data.
+	///
+	/// # Return
+	///
+	/// Returns `Error::Success` when the dispatchable was successfully executed and
+	/// returned `Ok`. When the dispatchable was executed but returned an error
+	/// `Error::CallRuntimeFailed` is returned. The full error is not
+	/// provided because it is not guaranteed to be stable.
+	///
+	/// # Comparison with `ChainExtension`
+	///
+	/// Just as a chain extension this API allows the runtime to extend the functionality
+	/// of contracts. While making use of this function is generally easier it cannot be
+	/// used in all cases. Consider writing a chain extension if you need to do perform
+	/// one of the following tasks:
+	///
+	/// - Return data.
+	/// - Provide functionality **exclusively** to contracts.
+	/// - Provide custom weights.
+	/// - Avoid the need to keep the `Call` data structure stable.
+	#[cfg(feature = "unstable-api")]
+	fn call_runtime(call: &[u8]) -> Result;
+
+	/// Checks whether the caller of the current contract is the origin of the whole call stack.
+	///
+	/// Prefer this over [`is_contract()`][`Self::is_contract`] when checking whether your contract
+	/// is being called by a contract or a plain account. The reason is that it performs better
+	/// since it does not need to do any storage lookups.
+	///
+	/// # Return
+	///
+	/// A return value of `true` indicates that this contract is being called by a plain account
+	/// and `false` indicates that the caller is another contract.
+	#[cfg(feature = "unstable-api")]
+	fn caller_is_origin() -> bool;
+
+	/// Checks whether the caller of the current contract is root.
+	///
+	/// Note that only the origin of the call stack can be root. Hence this function returning
+	/// `true` implies that the contract is being called by the origin.
+	///
+	/// A return value of `true` indicates that this contract is being called by a root origin,
+	/// and `false` indicates that the caller is a signed origin.
+	#[cfg(feature = "unstable-api")]
+	fn caller_is_root() -> u32;
+
+	/// Clear the value at the given key in the contract storage.
+	///
+	/// # Parameters
+	///
+	/// - `key`: The storage key.
+	///
+	/// # Return
+	///
+	/// Returns the size of the pre-existing value at the specified key if any.
+	#[cfg(feature = "unstable-api")]
+	fn clear_storage(flags: StorageFlags, key: &[u8]) -> Option<u32>;
+
+	/// Checks whether there is a value stored under the given key.
+	///
+	/// The key length must not exceed the maximum defined by the contracts module parameter.
+	///
+	/// # Parameters
+	/// - `key`: The storage key.
+	///
+	/// # Return
+	///
+	/// Returns the size of the pre-existing value at the specified key if any.
+	#[cfg(feature = "unstable-api")]
+	fn contains_storage(flags: StorageFlags, key: &[u8]) -> Option<u32>;
+
+	/// Emit a custom debug message.
+	///
+	/// No newlines are added to the supplied message.
+	/// Specifying invalid UTF-8 just drops the message with no trap.
+	///
+	/// This is a no-op if debug message recording is disabled which is always the case
+	/// when the code is executing on-chain. The message is interpreted as UTF-8 and
+	/// appended to the debug buffer which is then supplied to the calling RPC client.
+	///
+	/// # Note
+	///
+	/// Even though no action is taken when debug message recording is disabled there is still
+	/// a non trivial overhead (and weight cost) associated with calling this function. Contract
+	/// languages should remove calls to this function (either at runtime or compile time) when
+	/// not being executed as an RPC. For example, they could allow users to disable logging
+	/// through compile time flags (cargo features) for on-chain deployment. Additionally, the
+	/// return value of this function can be cached in order to prevent further calls at runtime.
+	#[cfg(feature = "unstable-api")]
+	fn debug_message(str: &[u8]) -> Result;
+
+	/// Recovers the ECDSA public key from the given message hash and signature.
+	///
+	/// Writes the public key into the given output buffer.
+	/// Assumes the secp256k1 curve.
+	///
+	/// # Parameters
+	///
+	/// - `signature`: The signature bytes.
+	/// - `message_hash`: The message hash bytes.
+	/// - `output`: A reference to the output data buffer to write the public key.
+	///
+	/// # Errors
+	///
+	/// - [EcdsaRecoveryFailed][`crate::ReturnErrorCode::EcdsaRecoveryFailed]
+	#[cfg(feature = "unstable-api")]
+	fn ecdsa_recover(
+		signature: &[u8; 65],
+		message_hash: &[u8; 32],
+		output: &mut [u8; 33],
+	) -> Result;
+
+	/// Calculates Ethereum address from the ECDSA compressed public key and stores
+	/// it into the supplied buffer.
+	///
+	/// # Parameters
+	///
+	/// - `pubkey`: The public key bytes.
+	/// - `output`: A reference to the output data buffer to write the address.
+	///
+	/// # Errors
+	///
+	/// - [EcdsaRecoveryFailed][`crate::ReturnErrorCode::EcdsaRecoveryFailed]
+	#[cfg(feature = "unstable-api")]
+	fn ecdsa_to_eth_address(pubkey: &[u8; 33], output: &mut [u8; 20]) -> Result;
+
+	#[cfg(feature = "unstable-api")]
+	hash_fn!(sha2_256, 32);
+	#[cfg(feature = "unstable-api")]
+	hash_fn!(blake2_256, 32);
+	#[cfg(feature = "unstable-api")]
+	hash_fn!(blake2_128, 16);
+
+	/// Checks whether a specified address belongs to a contract.
+	///
+	/// # Parameters
+	///
+	/// - `address`: The address to check
+	///
+	/// # Return
+	///
+	/// Returns `true` if the address belongs to a contract.
+	#[cfg(feature = "unstable-api")]
+	fn is_contract(address: &[u8; 20]) -> bool;
+
+	/// Lock a new delegate dependency to the contract.
+	///
+	/// Traps if the maximum number of delegate_dependencies is reached or if
+	/// the delegate dependency already exists.
 	///
 	/// # Parameters
 	///
 	/// - `code_hash`: The code hash of the dependency. Should be decodable as an `T::Hash`. Traps
 	///   otherwise.
-	fn unlock_delegate_dependency(code_hash: &[u8; 32]);
+	#[cfg(feature = "unstable-api")]
+	fn lock_delegate_dependency(code_hash: &[u8; 32]);
 
-	/// Cease contract execution and save a data buffer as a result of the execution.
+	/// Stores the minimum balance (a.k.a. existential deposit) into the supplied buffer.
 	///
-	/// This function never returns as it stops execution of the caller.
-	/// This is the only way to return a data buffer to the caller. Returning from
-	/// execution without calling this function is equivalent to calling:
-	/// ```nocompile
-	/// return_value(ReturnFlags::empty(), &[])
-	/// ```
+	/// # Parameters
 	///
-	/// Using an unnamed non empty `ReturnFlags` triggers a trap.
+	/// - `output`: A reference to the output data buffer to write the minimum balance.
+	#[cfg(feature = "unstable-api")]
+	fn minimum_balance(output: &mut [u8; 32]);
+
+	/// Retrieve the code hash of the currently executing contract.
 	///
 	/// # Parameters
 	///
-	/// - `flags`: Flag used to signal special return conditions to the supervisor. See
-	///   [`ReturnFlags`] for a documentation of the supported flags.
-	/// - `return_value`: The return value buffer.
-	fn return_value(flags: ReturnFlags, return_value: &[u8]) -> !;
+	/// - `output`: A reference to the output data buffer to write the code hash.
+	#[cfg(feature = "unstable-api")]
+	fn own_code_hash(output: &mut [u8; 32]);
 
 	/// Replace the contract code at the specified address with new code.
 	///
@@ -591,23 +635,9 @@ pub trait HostFn: private::Sealed {
 	/// # Panics
 	///
 	/// Panics if there is no code on-chain with the specified hash.
+	#[cfg(feature = "unstable-api")]
 	fn set_code_hash(code_hash: &[u8; 32]);
 
-	/// Set the value at the given key in the contract storage.
-	///
-	/// The key and value lengths must not exceed the maximums defined by the contracts module
-	/// parameters.
-	///
-	/// # Parameters
-	///
-	/// - `key`: The storage key.
-	/// - `encoded_value`: The storage value.
-	///
-	/// # Return
-	///
-	/// Returns the size of the pre-existing value at the specified key if any.
-	fn set_storage(flags: StorageFlags, key: &[u8], value: &[u8]) -> Option<u32>;
-
 	/// Verify a sr25519 signature
 	///
 	/// # Parameters
@@ -618,6 +648,7 @@ pub trait HostFn: private::Sealed {
 	/// # Errors
 	///
 	/// - [Sr25519VerifyFailed][`crate::ReturnErrorCode::Sr25519VerifyFailed]
+	#[cfg(feature = "unstable-api")]
 	fn sr25519_verify(signature: &[u8; 64], message: &[u8], pub_key: &[u8; 32]) -> Result;
 
 	/// Retrieve and remove the value under the given key from storage.
@@ -629,6 +660,7 @@ pub trait HostFn: private::Sealed {
 	/// # Errors
 	///
 	/// [KeyNotFound][`crate::ReturnErrorCode::KeyNotFound]
+	#[cfg(feature = "unstable-api")]
 	fn take_storage(flags: StorageFlags, key: &[u8], output: &mut &mut [u8]) -> Result;
 
 	/// Remove the calling account and transfer remaining **free** balance.
@@ -646,23 +678,30 @@ pub trait HostFn: private::Sealed {
 	/// - The contract is live i.e is already on the call stack.
 	/// - Failed to send the balance to the beneficiary.
 	/// - The deletion queue is full.
+	#[cfg(feature = "unstable-api")]
 	fn terminate(beneficiary: &[u8; 20]) -> !;
 
-	/// Stores the value transferred along with this call/instantiate into the supplied buffer.
+	/// Removes the delegate dependency from the contract.
+	///
+	/// Traps if the delegate dependency does not exist.
 	///
 	/// # Parameters
 	///
-	/// - `output`: A reference to the output data buffer to write the transferred value.
-	fn value_transferred(output: &mut [u8; 32]);
+	/// - `code_hash`: The code hash of the dependency. Should be decodable as an `T::Hash`. Traps
+	///   otherwise.
+	#[cfg(feature = "unstable-api")]
+	fn unlock_delegate_dependency(code_hash: &[u8; 32]);
 
-	/// Stores the price for the specified amount of gas into the supplied buffer.
+	/// Stores the amount of weight left into the supplied buffer.
+	/// The data is encoded as Weight.
+	///
+	/// If the available space in `output` is less than the size of the value a trap is triggered.
 	///
 	/// # Parameters
 	///
-	/// - `ref_time_limit`: The *ref_time* Weight limit to query the price for.
-	/// - `proof_size_limit`: The *proof_size* Weight limit to query the price for.
-	/// - `output`: A reference to the output data buffer to write the price.
-	fn weight_to_fee(ref_time_limit: u64, proof_size_limit: u64, output: &mut [u8; 32]);
+	/// - `output`: A reference to the output data buffer to write the weight left.
+	#[cfg(feature = "unstable-api")]
+	fn weight_left(output: &mut &mut [u8]);
 
 	/// Execute an XCM program locally, using the contract's address as the origin.
 	/// This is equivalent to dispatching `pallet_xcm::execute` through call_runtime, except that
@@ -678,6 +717,7 @@ pub trait HostFn: private::Sealed {
 	///
 	/// Returns `Error::Success` when the XCM execution attempt is successful. When the XCM
 	/// execution fails, `ReturnCode::XcmExecutionFailed` is returned
+	#[cfg(feature = "unstable-api")]
 	fn xcm_execute(msg: &[u8]) -> Result;
 
 	/// Send an XCM program from the contract to the specified destination.
@@ -695,21 +735,8 @@ pub trait HostFn: private::Sealed {
 	///
 	/// Returns `ReturnCode::Success` when the message was successfully sent. When the XCM
 	/// execution fails, `ReturnErrorCode::XcmSendFailed` is returned.
+	#[cfg(feature = "unstable-api")]
 	fn xcm_send(dest: &[u8], msg: &[u8], output: &mut [u8; 32]) -> Result;
-
-	/// Stores the size of the returned data of the last contract call or instantiation.
-	///
-	/// # Parameters
-	///
-	/// - `output`: A reference to the output buffer to write the size.
-	fn return_data_size(output: &mut [u8; 32]);
-
-	/// Stores the returned data of the last contract call or contract instantiation.
-	///
-	/// # Parameters
-	/// - `output`: A reference to the output buffer to write the data.
-	/// - `offset`: Byte offset into the returned data
-	fn return_data_copy(output: &mut &mut [u8], offset: u32);
 }
 
 mod private {
diff --git a/substrate/frame/revive/uapi/src/host/riscv64.rs b/substrate/frame/revive/uapi/src/host/riscv64.rs
index 4e2cc125bbe..d5a695262a2 100644
--- a/substrate/frame/revive/uapi/src/host/riscv64.rs
+++ b/substrate/frame/revive/uapi/src/host/riscv64.rs
@@ -295,10 +295,6 @@ impl HostFn for HostFnImpl {
 		ret_code.into()
 	}
 
-	fn caller_is_root() -> u32 {
-		unsafe { sys::caller_is_root() }.into_u32()
-	}
-
 	fn delegate_call(
 		flags: CallFlags,
 		address: &[u8; 20],
@@ -368,17 +364,6 @@ impl HostFn for HostFnImpl {
 		ret_code.into()
 	}
 
-	fn clear_storage(flags: StorageFlags, key: &[u8]) -> Option<u32> {
-		let ret_code = unsafe { sys::clear_storage(flags.bits(), key.as_ptr(), key.len() as u32) };
-		ret_code.into()
-	}
-
-	fn contains_storage(flags: StorageFlags, key: &[u8]) -> Option<u32> {
-		let ret_code =
-			unsafe { sys::contains_storage(flags.bits(), key.as_ptr(), key.len() as u32) };
-		ret_code.into()
-	}
-
 	fn get_storage(flags: StorageFlags, key: &[u8], output: &mut &mut [u8]) -> Result {
 		let mut output_len = output.len() as u32;
 		let ret_code = {
@@ -396,33 +381,79 @@ impl HostFn for HostFnImpl {
 		ret_code.into()
 	}
 
-	fn take_storage(flags: StorageFlags, key: &[u8], output: &mut &mut [u8]) -> Result {
+	fn input(output: &mut &mut [u8]) {
 		let mut output_len = output.len() as u32;
-		let ret_code = {
-			unsafe {
-				sys::take_storage(
-					flags.bits(),
-					key.as_ptr(),
-					key.len() as u32,
-					output.as_mut_ptr(),
-					&mut output_len,
-				)
-			}
-		};
+		{
+			unsafe { sys::input(output.as_mut_ptr(), &mut output_len) };
+		}
 		extract_from_slice(output, output_len as usize);
-		ret_code.into()
 	}
 
-	fn debug_message(str: &[u8]) -> Result {
-		let ret_code = unsafe { sys::debug_message(str.as_ptr(), str.len() as u32) };
-		ret_code.into()
+	fn call_data_load(out_ptr: &mut [u8; 32], offset: u32) {
+		unsafe { sys::call_data_load(out_ptr.as_mut_ptr(), offset) };
 	}
 
-	fn terminate(beneficiary: &[u8; 20]) -> ! {
-		unsafe { sys::terminate(beneficiary.as_ptr()) }
-		panic!("terminate does not return");
+	fn return_value(flags: ReturnFlags, return_value: &[u8]) -> ! {
+		unsafe { sys::seal_return(flags.bits(), return_value.as_ptr(), return_value.len() as u32) }
+		panic!("seal_return does not return");
+	}
+
+	impl_wrapper_for! {
+		[u8; 32] => call_data_size, balance, value_transferred, now, chain_id;
+		[u8; 20] => address, caller, origin;
+	}
+
+	#[cfg(feature = "unstable-api")]
+	impl_wrapper_for! {
+		[u8; 32] => block_number, minimum_balance;
+	}
+
+	fn weight_to_fee(ref_time_limit: u64, proof_size_limit: u64, output: &mut [u8; 32]) {
+		unsafe { sys::weight_to_fee(ref_time_limit, proof_size_limit, output.as_mut_ptr()) };
+	}
+
+	impl_hash_fn!(keccak_256, 32);
+
+	fn get_immutable_data(output: &mut &mut [u8]) {
+		let mut output_len = output.len() as u32;
+		unsafe { sys::get_immutable_data(output.as_mut_ptr(), &mut output_len) };
+		extract_from_slice(output, output_len as usize);
+	}
+
+	fn set_immutable_data(data: &[u8]) {
+		unsafe { sys::set_immutable_data(data.as_ptr(), data.len() as u32) }
+	}
+
+	fn balance_of(address: &[u8; 20], output: &mut [u8; 32]) {
+		unsafe { sys::balance_of(address.as_ptr(), output.as_mut_ptr()) };
+	}
+
+	fn code_hash(address: &[u8; 20], output: &mut [u8; 32]) {
+		unsafe { sys::code_hash(address.as_ptr(), output.as_mut_ptr()) }
+	}
+
+	fn code_size(address: &[u8; 20], output: &mut [u8; 32]) {
+		unsafe { sys::code_size(address.as_ptr(), output.as_mut_ptr()) }
+	}
+
+	fn return_data_size(output: &mut [u8; 32]) {
+		unsafe { sys::return_data_size(output.as_mut_ptr()) };
+	}
+
+	fn return_data_copy(output: &mut &mut [u8], offset: u32) {
+		let mut output_len = output.len() as u32;
+		{
+			unsafe { sys::return_data_copy(output.as_mut_ptr(), &mut output_len, offset) };
+		}
+		extract_from_slice(output, output_len as usize);
+	}
+
+	#[cfg(feature = "unstable-api")]
+	fn block_hash(block_number_ptr: &[u8; 32], output: &mut [u8; 32]) {
+		unsafe { sys::block_hash(block_number_ptr.as_ptr(), output.as_mut_ptr()) };
 	}
 
+	#[cfg(feature = "unstable-api")]
 	fn call_chain_extension(func_id: u32, input: &[u8], mut output: Option<&mut &mut [u8]>) -> u32 {
 		let (output_ptr, mut output_len) = ptr_len_or_sentinel(&mut output);
 		let ret_code = {
@@ -443,48 +474,43 @@ impl HostFn for HostFnImpl {
 		ret_code.into_u32()
 	}
 
-	fn input(output: &mut &mut [u8]) {
-		let mut output_len = output.len() as u32;
-		{
-			unsafe { sys::input(output.as_mut_ptr(), &mut output_len) };
-		}
-		extract_from_slice(output, output_len as usize);
+	#[cfg(feature = "unstable-api")]
+	fn call_runtime(call: &[u8]) -> Result {
+		let ret_code = unsafe { sys::call_runtime(call.as_ptr(), call.len() as u32) };
+		ret_code.into()
 	}
 
-	fn call_data_load(out_ptr: &mut [u8; 32], offset: u32) {
-		unsafe { sys::call_data_load(out_ptr.as_mut_ptr(), offset) };
+	#[cfg(feature = "unstable-api")]
+	fn caller_is_origin() -> bool {
+		let ret_val = unsafe { sys::caller_is_origin() };
+		ret_val.into_bool()
 	}
 
-	fn return_value(flags: ReturnFlags, return_value: &[u8]) -> ! {
-		unsafe { sys::seal_return(flags.bits(), return_value.as_ptr(), return_value.len() as u32) }
-		panic!("seal_return does not return");
+	#[cfg(feature = "unstable-api")]
+	fn caller_is_root() -> u32 {
+		unsafe { sys::caller_is_root() }.into_u32()
 	}
 
-	fn call_runtime(call: &[u8]) -> Result {
-		let ret_code = unsafe { sys::call_runtime(call.as_ptr(), call.len() as u32) };
+	#[cfg(feature = "unstable-api")]
+	fn clear_storage(flags: StorageFlags, key: &[u8]) -> Option<u32> {
+		let ret_code = unsafe { sys::clear_storage(flags.bits(), key.as_ptr(), key.len() as u32) };
 		ret_code.into()
 	}
 
-	impl_wrapper_for! {
-		[u8; 32] => call_data_size, block_number, balance, value_transferred, now, minimum_balance, chain_id;
-		[u8; 20] => address, caller, origin;
-	}
-
-	fn weight_left(output: &mut &mut [u8]) {
-		let mut output_len = output.len() as u32;
-		unsafe { sys::weight_left(output.as_mut_ptr(), &mut output_len) }
-		extract_from_slice(output, output_len as usize)
+	#[cfg(feature = "unstable-api")]
+	fn contains_storage(flags: StorageFlags, key: &[u8]) -> Option<u32> {
+		let ret_code =
+			unsafe { sys::contains_storage(flags.bits(), key.as_ptr(), key.len() as u32) };
+		ret_code.into()
 	}
 
-	fn weight_to_fee(ref_time_limit: u64, proof_size_limit: u64, output: &mut [u8; 32]) {
-		unsafe { sys::weight_to_fee(ref_time_limit, proof_size_limit, output.as_mut_ptr()) };
+	#[cfg(feature = "unstable-api")]
+	fn debug_message(str: &[u8]) -> Result {
+		let ret_code = unsafe { sys::debug_message(str.as_ptr(), str.len() as u32) };
+		ret_code.into()
 	}
 
-	impl_hash_fn!(sha2_256, 32);
-	impl_hash_fn!(keccak_256, 32);
-	impl_hash_fn!(blake2_256, 32);
-	impl_hash_fn!(blake2_128, 16);
-
+	#[cfg(feature = "unstable-api")]
 	fn ecdsa_recover(
 		signature: &[u8; 65],
 		message_hash: &[u8; 32],
@@ -496,76 +522,96 @@ impl HostFn for HostFnImpl {
 		ret_code.into()
 	}
 
+	#[cfg(feature = "unstable-api")]
 	fn ecdsa_to_eth_address(pubkey: &[u8; 33], output: &mut [u8; 20]) -> Result {
 		let ret_code = unsafe { sys::ecdsa_to_eth_address(pubkey.as_ptr(), output.as_mut_ptr()) };
 		ret_code.into()
 	}
 
-	fn sr25519_verify(signature: &[u8; 64], message: &[u8], pub_key: &[u8; 32]) -> Result {
-		let ret_code = unsafe {
-			sys::sr25519_verify(
-				signature.as_ptr(),
-				pub_key.as_ptr(),
-				message.len() as u32,
-				message.as_ptr(),
-			)
-		};
-		ret_code.into()
-	}
+	#[cfg(feature = "unstable-api")]
+	impl_hash_fn!(sha2_256, 32);
+	#[cfg(feature = "unstable-api")]
+	impl_hash_fn!(blake2_256, 32);
+	#[cfg(feature = "unstable-api")]
+	impl_hash_fn!(blake2_128, 16);
 
+	#[cfg(feature = "unstable-api")]
 	fn is_contract(address: &[u8; 20]) -> bool {
 		let ret_val = unsafe { sys::is_contract(address.as_ptr()) };
 		ret_val.into_bool()
 	}
 
-	fn get_immutable_data(output: &mut &mut [u8]) {
-		let mut output_len = output.len() as u32;
-		unsafe { sys::get_immutable_data(output.as_mut_ptr(), &mut output_len) };
-		extract_from_slice(output, output_len as usize);
-	}
-
-	fn set_immutable_data(data: &[u8]) {
-		unsafe { sys::set_immutable_data(data.as_ptr(), data.len() as u32) }
-	}
-
-	fn balance_of(address: &[u8; 20], output: &mut [u8; 32]) {
-		unsafe { sys::balance_of(address.as_ptr(), output.as_mut_ptr()) };
+	#[cfg(feature = "unstable-api")]
+	fn lock_delegate_dependency(code_hash: &[u8; 32]) {
+		unsafe { sys::lock_delegate_dependency(code_hash.as_ptr()) }
 	}
 
-	fn caller_is_origin() -> bool {
-		let ret_val = unsafe { sys::caller_is_origin() };
-		ret_val.into_bool()
+	#[cfg(feature = "unstable-api")]
+	fn own_code_hash(output: &mut [u8; 32]) {
+		unsafe { sys::own_code_hash(output.as_mut_ptr()) }
 	}
 
+	#[cfg(feature = "unstable-api")]
 	fn set_code_hash(code_hash: &[u8; 32]) {
 		unsafe { sys::set_code_hash(code_hash.as_ptr()) }
 	}
 
-	fn code_hash(address: &[u8; 20], output: &mut [u8; 32]) {
-		unsafe { sys::code_hash(address.as_ptr(), output.as_mut_ptr()) }
-	}
-
-	fn code_size(address: &[u8; 20], output: &mut [u8; 32]) {
-		unsafe { sys::code_size(address.as_ptr(), output.as_mut_ptr()) }
+	#[cfg(feature = "unstable-api")]
+	fn sr25519_verify(signature: &[u8; 64], message: &[u8], pub_key: &[u8; 32]) -> Result {
+		let ret_code = unsafe {
+			sys::sr25519_verify(
+				signature.as_ptr(),
+				pub_key.as_ptr(),
+				message.len() as u32,
+				message.as_ptr(),
+			)
+		};
+		ret_code.into()
 	}
 
-	fn own_code_hash(output: &mut [u8; 32]) {
-		unsafe { sys::own_code_hash(output.as_mut_ptr()) }
+	#[cfg(feature = "unstable-api")]
+	fn take_storage(flags: StorageFlags, key: &[u8], output: &mut &mut [u8]) -> Result {
+		let mut output_len = output.len() as u32;
+		let ret_code = {
+			unsafe {
+				sys::take_storage(
+					flags.bits(),
+					key.as_ptr(),
+					key.len() as u32,
+					output.as_mut_ptr(),
+					&mut output_len,
+				)
+			}
+		};
+		extract_from_slice(output, output_len as usize);
+		ret_code.into()
 	}
 
-	fn lock_delegate_dependency(code_hash: &[u8; 32]) {
-		unsafe { sys::lock_delegate_dependency(code_hash.as_ptr()) }
+	#[cfg(feature = "unstable-api")]
+	fn terminate(beneficiary: &[u8; 20]) -> ! {
+		unsafe { sys::terminate(beneficiary.as_ptr()) }
+		panic!("terminate does not return");
 	}
 
+	#[cfg(feature = "unstable-api")]
 	fn unlock_delegate_dependency(code_hash: &[u8; 32]) {
 		unsafe { sys::unlock_delegate_dependency(code_hash.as_ptr()) }
 	}
 
+	#[cfg(feature = "unstable-api")]
+	fn weight_left(output: &mut &mut [u8]) {
+		let mut output_len = output.len() as u32;
+		unsafe { sys::weight_left(output.as_mut_ptr(), &mut output_len) }
+		extract_from_slice(output, output_len as usize)
+	}
+
+	#[cfg(feature = "unstable-api")]
 	fn xcm_execute(msg: &[u8]) -> Result {
 		let ret_code = unsafe { sys::xcm_execute(msg.as_ptr(), msg.len() as _) };
 		ret_code.into()
 	}
 
+	#[cfg(feature = "unstable-api")]
 	fn xcm_send(dest: &[u8], msg: &[u8], output: &mut [u8; 32]) -> Result {
 		let ret_code = unsafe {
 			sys::xcm_send(
@@ -578,20 +624,4 @@ impl HostFn for HostFnImpl {
 		};
 		ret_code.into()
 	}
-
-	fn return_data_size(output: &mut [u8; 32]) {
-		unsafe { sys::return_data_size(output.as_mut_ptr()) };
-	}
-
-	fn return_data_copy(output: &mut &mut [u8], offset: u32) {
-		let mut output_len = output.len() as u32;
-		{
-			unsafe { sys::return_data_copy(output.as_mut_ptr(), &mut output_len, offset) };
-		}
-		extract_from_slice(output, output_len as usize);
-	}
-
-	fn block_hash(block_number_ptr: &[u8; 32], output: &mut [u8; 32]) {
-		unsafe { sys::block_hash(block_number_ptr.as_ptr(), output.as_mut_ptr()) };
-	}
 }
-- 
GitLab