diff --git a/substrate/frame/contracts/fixtures/contracts/common/src/lib.rs b/substrate/frame/contracts/fixtures/contracts/common/src/lib.rs
index 6904ab2f504caa0c9e0c2f031362c71db95249d5..80e1f543a7b53ba6585e0d8a453cacce02fdac3d 100644
--- a/substrate/frame/contracts/fixtures/contracts/common/src/lib.rs
+++ b/substrate/frame/contracts/fixtures/contracts/common/src/lib.rs
@@ -149,3 +149,13 @@ macro_rules! output {
 		$host_fn($($arg,)* $output);
 	};
 }
+
+/// Similar to `output!` but unwraps the result.
+#[macro_export]
+macro_rules! unwrap_output {
+	($output: ident, $buffer: expr, $host_fn:path, $($arg:expr),*) => {
+		let mut $output = $buffer;
+		let $output = &mut &mut $output[..];
+		$host_fn($($arg,)* $output).unwrap();
+	};
+}
diff --git a/substrate/frame/contracts/fixtures/contracts/storage.rs b/substrate/frame/contracts/fixtures/contracts/storage.rs
new file mode 100644
index 0000000000000000000000000000000000000000..6fa97224c7c88330b5fccad900c668d0aa7bdaae
--- /dev/null
+++ b/substrate/frame/contracts/fixtures/contracts/storage.rs
@@ -0,0 +1,63 @@
+// This file is part of Substrate.
+
+// Copyright (C) Parity Technologies (UK) Ltd.
+// SPDX-License-Identifier: Apache-2.0
+
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// 	http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+//! This contract tests the storage APIs. It sets and clears storage values using the different
+//! versions of the storage APIs.
+#![no_std]
+#![no_main]
+
+use common::unwrap_output;
+use uapi::{HostFn, HostFnImpl as api};
+
+#[no_mangle]
+#[polkavm_derive::polkavm_export]
+pub extern "C" fn deploy() {}
+
+#[no_mangle]
+#[polkavm_derive::polkavm_export]
+pub extern "C" fn call() {
+	const KEY: [u8; 32] = [1u8; 32];
+	const VALUE_1: [u8; 4] = [1u8; 4];
+	const VALUE_2: [u8; 4] = [2u8; 4];
+	const VALUE_3: [u8; 4] = [3u8; 4];
+
+	api::set_storage(&KEY, &VALUE_1);
+	assert_eq!(api::contains_storage(&KEY), Some(VALUE_1.len() as _));
+	unwrap_output!(val, [0u8; 4], api::get_storage, &KEY);
+	assert_eq!(**val, VALUE_1);
+
+	let existing = api::set_storage_v1(&KEY, &VALUE_2);
+	assert_eq!(existing, Some(VALUE_1.len() as _));
+	unwrap_output!(val, [0u8; 4], api::get_storage, &KEY);
+	assert_eq!(**val, VALUE_2);
+
+	api::clear_storage(&KEY);
+	assert_eq!(api::contains_storage(&KEY), None);
+
+	let existing = api::set_storage_v2(&KEY, &VALUE_3);
+	assert_eq!(existing, None);
+	assert_eq!(api::contains_storage_v1(&KEY), Some(VALUE_1.len() as _));
+	unwrap_output!(val, [0u8; 32], api::get_storage_v1, &KEY);
+	assert_eq!(**val, VALUE_3);
+
+	api::clear_storage_v1(&KEY);
+	assert_eq!(api::contains_storage_v1(&KEY), None);
+	let existing = api::set_storage_v2(&KEY, &VALUE_3);
+	assert_eq!(existing, None);
+	unwrap_output!(val, [0u8; 32], api::take_storage, &KEY);
+	assert_eq!(**val, VALUE_3);
+}
diff --git a/substrate/frame/contracts/src/tests.rs b/substrate/frame/contracts/src/tests.rs
index dbf10e4279da751c1fa5f853de2370bc0625beab..d7ec710e768e1ab57978aac837437d25a1770397 100644
--- a/substrate/frame/contracts/src/tests.rs
+++ b/substrate/frame/contracts/src/tests.rs
@@ -913,6 +913,21 @@ fn instantiate_unique_trie_id() {
 	});
 }
 
+#[test]
+fn storage_work() {
+	let (code, _code_hash) = compile_module::<Test>("storage").unwrap();
+
+	ExtBuilder::default().build().execute_with(|| {
+		let _ = <Test as Config>::Currency::set_balance(&ALICE, 1_000_000);
+		let min_balance = Contracts::min_balance();
+		let addr = builder::bare_instantiate(Code::Upload(code))
+			.value(min_balance * 100)
+			.build_and_unwrap_account_id();
+
+		builder::bare_call(addr).build_and_unwrap_result();
+	});
+}
+
 #[test]
 fn storage_max_value_limit() {
 	let (wasm, _code_hash) = compile_module::<Test>("storage_size").unwrap();
@@ -1385,19 +1400,7 @@ fn crypto_hashes() {
 			// We offset data in the contract tables by 1.
 			let mut params = vec![(n + 1) as u8];
 			params.extend_from_slice(input);
-			let result = <Pallet<Test>>::bare_call(
-				ALICE,
-				addr.clone(),
-				0,
-				GAS_LIMIT,
-				None,
-				params,
-				DebugInfo::Skip,
-				CollectEvents::Skip,
-				Determinism::Enforced,
-			)
-			.result
-			.unwrap();
+			let result = builder::bare_call(addr.clone()).data(params).build_and_unwrap_result();
 			assert!(!result.did_revert());
 			let expected = hash_fn(input.as_ref());
 			assert_eq!(&result.data[..*expected_size], &*expected);
@@ -2277,19 +2280,7 @@ fn ecdsa_recover() {
 		params.extend_from_slice(&signature);
 		params.extend_from_slice(&message_hash);
 		assert!(params.len() == 65 + 32);
-		let result = <Pallet<Test>>::bare_call(
-			ALICE,
-			addr.clone(),
-			0,
-			GAS_LIMIT,
-			None,
-			params,
-			DebugInfo::Skip,
-			CollectEvents::Skip,
-			Determinism::Enforced,
-		)
-		.result
-		.unwrap();
+		let result = builder::bare_call(addr.clone()).data(params).build_and_unwrap_result();
 		assert!(!result.did_revert());
 		assert_eq!(result.data, EXPECTED_COMPRESSED_PUBLIC_KEY);
 	})
@@ -2404,19 +2395,7 @@ fn sr25519_verify() {
 			params.extend_from_slice(&public_key);
 			params.extend_from_slice(message);
 
-			<Pallet<Test>>::bare_call(
-				ALICE,
-				addr.clone(),
-				0,
-				GAS_LIMIT,
-				None,
-				params,
-				DebugInfo::Skip,
-				CollectEvents::Skip,
-				Determinism::Enforced,
-			)
-			.result
-			.unwrap()
+			builder::bare_call(addr.clone()).data(params).build_and_unwrap_result()
 		};
 
 		// verification should succeed for "hello world"
@@ -3692,35 +3671,13 @@ fn cannot_instantiate_indeterministic_code() {
 
 		// Try to instantiate `code_hash` from another contract in deterministic mode
 		assert_err!(
-			<Pallet<Test>>::bare_call(
-				ALICE,
-				addr.clone(),
-				0,
-				GAS_LIMIT,
-				None,
-				code_hash.encode(),
-				DebugInfo::Skip,
-				CollectEvents::Skip,
-				Determinism::Enforced,
-			)
-			.result,
+			builder::bare_call(addr.clone()).data(code_hash.encode()).build().result,
 			<Error<Test>>::Indeterministic,
 		);
 
 		// Instantiations are not allowed even in non-determinism mode
 		assert_err!(
-			<Pallet<Test>>::bare_call(
-				ALICE,
-				addr.clone(),
-				0,
-				GAS_LIMIT,
-				None,
-				code_hash.encode(),
-				DebugInfo::Skip,
-				CollectEvents::Skip,
-				Determinism::Relaxed,
-			)
-			.result,
+			builder::bare_call(addr.clone()).data(code_hash.encode()).build().result,
 			<Error<Test>>::Indeterministic,
 		);
 	});
@@ -3747,18 +3704,7 @@ fn cannot_set_code_indeterministic_code() {
 
 		// We do not allow to set the code hash to a non-deterministic wasm
 		assert_err!(
-			<Pallet<Test>>::bare_call(
-				ALICE,
-				caller_addr.clone(),
-				0,
-				GAS_LIMIT,
-				None,
-				code_hash.encode(),
-				DebugInfo::Skip,
-				CollectEvents::Skip,
-				Determinism::Relaxed,
-			)
-			.result,
+			builder::bare_call(caller_addr.clone()).data(code_hash.encode()).build().result,
 			<Error<Test>>::Indeterministic,
 		);
 	});
@@ -3785,35 +3731,17 @@ fn delegate_call_indeterministic_code() {
 
 		// The delegate call will fail in deterministic mode
 		assert_err!(
-			<Pallet<Test>>::bare_call(
-				ALICE,
-				caller_addr.clone(),
-				0,
-				GAS_LIMIT,
-				None,
-				code_hash.encode(),
-				DebugInfo::Skip,
-				CollectEvents::Skip,
-				Determinism::Enforced,
-			)
-			.result,
+			builder::bare_call(caller_addr.clone()).data(code_hash.encode()).build().result,
 			<Error<Test>>::Indeterministic,
 		);
 
 		// The delegate call will work on non-deterministic mode
 		assert_ok!(
-			<Pallet<Test>>::bare_call(
-				ALICE,
-				caller_addr.clone(),
-				0,
-				GAS_LIMIT,
-				None,
-				code_hash.encode(),
-				DebugInfo::Skip,
-				CollectEvents::Skip,
-				Determinism::Relaxed,
-			)
-			.result
+			builder::bare_call(caller_addr.clone())
+				.data(code_hash.encode())
+				.determinism(Determinism::Relaxed)
+				.build()
+				.result
 		);
 	});
 }
@@ -3845,19 +3773,8 @@ fn locking_delegate_dependency_works() {
 
 	// Call contract with the given input.
 	let call = |addr_caller: &AccountId32, input: &(u32, H256)| {
-		<Pallet<Test>>::bare_call(
-			ALICE,
-			addr_caller.clone(),
-			0,
-			GAS_LIMIT,
-			None,
-			input.encode(),
-			DebugInfo::UnsafeDebug,
-			CollectEvents::Skip,
-			Determinism::Enforced,
-		)
+		builder::bare_call(addr_caller.clone()).data(input.encode()).build()
 	};
-
 	const ED: u64 = 2000;
 	ExtBuilder::default().existential_deposit(ED).build().execute_with(|| {
 		let _ = Balances::set_balance(&ALICE, 1_000_000);
@@ -4023,19 +3940,9 @@ fn native_dependency_deposit_works() {
 			assert_eq!(res.storage_deposit.charge_or_zero(), deposit);
 
 			// call set_code_hash
-			<Pallet<Test>>::bare_call(
-				ALICE,
-				addr.clone(),
-				0,
-				GAS_LIMIT,
-				None,
-				dummy_code_hash.encode(),
-				DebugInfo::Skip,
-				CollectEvents::Skip,
-				Determinism::Enforced,
-			)
-			.result
-			.unwrap();
+			builder::bare_call(addr.clone())
+				.data(dummy_code_hash.encode())
+				.build_and_unwrap_result();
 
 			// Check updated storage_deposit
 			let code_deposit = test_utils::get_code_deposit(&dummy_code_hash);
diff --git a/substrate/frame/contracts/src/wasm/mod.rs b/substrate/frame/contracts/src/wasm/mod.rs
index e6af62c72891d4cfb42a4b7d65127391e0eff9a0..cae0b7c40206edca051473516aec2b47359d5615 100644
--- a/substrate/frame/contracts/src/wasm/mod.rs
+++ b/substrate/frame/contracts/src/wasm/mod.rs
@@ -243,9 +243,10 @@ impl<T: Config> WasmBlob<T> {
 			.define("env", "memory", memory)
 			.expect("We just created the Linker. It has no definitions with this name; qed");
 
-		let instance = linker
-			.instantiate(&mut store, &contract.module)
-			.map_err(|_| "can't instantiate module with provided definitions")?;
+		let instance = linker.instantiate(&mut store, &contract.module).map_err(|err| {
+			log::debug!(target: LOG_TARGET, "failed to instantiate module: {:?}", err);
+			"can't instantiate module with provided definitions"
+		})?;
 
 		Ok((store, memory, instance))
 	}
diff --git a/substrate/frame/contracts/uapi/src/host/wasm32.rs b/substrate/frame/contracts/uapi/src/host/wasm32.rs
index bc697238061ab16e47d304bd9583f51f7eb99c0b..cb5435bfc014ddd268b4b36c6dd86ce534be9042 100644
--- a/substrate/frame/contracts/uapi/src/host/wasm32.rs
+++ b/substrate/frame/contracts/uapi/src/host/wasm32.rs
@@ -59,7 +59,7 @@ mod sys {
 
 		pub fn caller_is_root() -> ReturnCode;
 
-		pub fn clear_storage(key_ptr: *const u8, key_len: u32) -> ReturnCode;
+		pub fn clear_storage(key_ptr: *const u8);
 
 		pub fn code_hash(
 			account_id_ptr: *const u8,
@@ -67,7 +67,7 @@ mod sys {
 			output_len_ptr: *mut u32,
 		) -> ReturnCode;
 
-		pub fn contains_storage(key_ptr: *const u8, key_len: u32) -> ReturnCode;
+		pub fn contains_storage(key_ptr: *const u8) -> ReturnCode;
 
 		pub fn debug_message(str_ptr: *const u8, str_len: u32) -> ReturnCode;
 
@@ -599,7 +599,7 @@ impl HostFn for HostFnImpl {
 	}
 
 	fn clear_storage(key: &[u8]) {
-		unsafe { sys::clear_storage(key.as_ptr(), key.len() as u32) };
+		unsafe { sys::clear_storage(key.as_ptr()) };
 	}
 
 	fn clear_storage_v1(key: &[u8]) -> Option<u32> {
@@ -656,7 +656,7 @@ impl HostFn for HostFnImpl {
 	}
 
 	fn contains_storage(key: &[u8]) -> Option<u32> {
-		let ret_code = unsafe { sys::contains_storage(key.as_ptr(), key.len() as u32) };
+		let ret_code = unsafe { sys::contains_storage(key.as_ptr()) };
 		ret_code.into()
 	}