From dd9924fad0cd9314f21eff621208dc2ec60e0cf5 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Alexander=20Thei=C3=9Fen?= <alex.theissen@me.com>
Date: Thu, 31 Oct 2024 13:18:04 +0100
Subject: [PATCH] Remove `riscv` feature flag (#6305)

Since https://github.com/paritytech/polkadot-sdk/pull/6266 we no longer
require a custom toolchain to build the `pallet-revive-fixtures`. Hence
we no longer have to guard the build behind a feature flag.

---------

Co-authored-by: GitHub Action <action@github.com>
---
 .config/zepter.yaml                           |    2 +-
 .github/scripts/cmd/test_cmd.py               |   26 +-
 .github/workflows/runtimes-matrix.json        |    2 +-
 .../workflows/tests-linux-stable-coverage.yml |    4 +-
 .github/workflows/tests-linux-stable.yml      |    4 +-
 prdoc/pr_6305.prdoc                           |   17 +
 scripts/generate-umbrella.py                  |    2 -
 substrate/bin/node/cli/Cargo.toml             |    1 -
 substrate/bin/node/runtime/Cargo.toml         |    1 -
 substrate/frame/revive/Cargo.toml             |    4 -
 substrate/frame/revive/fixtures/Cargo.toml    |    4 -
 substrate/frame/revive/fixtures/build.rs      |  348 +-
 substrate/frame/revive/fixtures/src/lib.rs    |    7 -
 .../frame/revive/mock-network/Cargo.toml      |    1 -
 .../frame/revive/mock-network/src/lib.rs      |    2 +-
 substrate/frame/revive/rpc/Cargo.toml         |   11 +-
 substrate/frame/revive/rpc/Dockerfile         |    1 +
 substrate/frame/revive/rpc/examples/README.md |    3 +-
 substrate/frame/revive/rpc/src/tests.rs       |    2 -
 .../frame/revive/src/benchmarking/mod.rs      |    4 +-
 .../frame/revive/src/benchmarking_dummy.rs    |   37 -
 substrate/frame/revive/src/evm/runtime.rs     |    1 -
 substrate/frame/revive/src/exec.rs            |    6 +-
 substrate/frame/revive/src/lib.rs             |    1 -
 substrate/frame/revive/src/storage.rs         |    1 -
 substrate/frame/revive/src/tests.rs           | 7080 ++++++++---------
 .../frame/revive/src/tests/test_debug.rs      |  248 +-
 substrate/frame/revive/src/wasm/mod.rs        |    2 +-
 umbrella/Cargo.toml                           |    6 -
 29 files changed, 3841 insertions(+), 3987 deletions(-)
 create mode 100644 prdoc/pr_6305.prdoc
 delete mode 100644 substrate/frame/revive/src/benchmarking_dummy.rs

diff --git a/.config/zepter.yaml b/.config/zepter.yaml
index 7a67ba2695c..24441e90b1a 100644
--- a/.config/zepter.yaml
+++ b/.config/zepter.yaml
@@ -27,7 +27,7 @@ workflows:
       ]
   # The umbrella crate uses more features, so we to check those too:
   check_umbrella:
-    - [ $check.0, '--features=serde,experimental,riscv,runtime,with-tracing,tuples-96,with-tracing', '-p=polkadot-sdk' ]
+    - [ $check.0, '--features=serde,experimental,runtime,with-tracing,tuples-96,with-tracing', '-p=polkadot-sdk' ]
   # Same as `check_*`, but with the `--fix` flag.
   default:
     - [ $check.0, '--fix' ]
diff --git a/.github/scripts/cmd/test_cmd.py b/.github/scripts/cmd/test_cmd.py
index faad3f261b9..7b29fbfe90d 100644
--- a/.github/scripts/cmd/test_cmd.py
+++ b/.github/scripts/cmd/test_cmd.py
@@ -13,7 +13,7 @@ mock_runtimes_matrix = [
         "path": "substrate/frame",
         "header": "substrate/HEADER-APACHE2",
         "template": "substrate/.maintain/frame-weight-template.hbs",
-        "bench_features": "runtime-benchmarks,riscv",
+        "bench_features": "runtime-benchmarks",
         "bench_flags": "--flag1 --flag2"
     },
     {
@@ -67,7 +67,7 @@ class TestCmd(unittest.TestCase):
         self.patcher6 = patch('importlib.util.spec_from_file_location', return_value=MagicMock())
         self.patcher7 = patch('importlib.util.module_from_spec', return_value=MagicMock())
         self.patcher8 = patch('cmd.generate_prdoc.main', return_value=0)
-        
+
         self.mock_open = self.patcher1.start()
         self.mock_json_load = self.patcher2.start()
         self.mock_parse_args = self.patcher3.start()
@@ -101,7 +101,7 @@ class TestCmd(unittest.TestCase):
             clean=False,
             image=None
         ), [])
-        
+
         self.mock_popen.return_value.read.side_effect = [
             "pallet_balances\npallet_staking\npallet_something\n",  # Output for dev runtime
             "pallet_balances\npallet_staking\npallet_something\n",  # Output for westend runtime
@@ -109,7 +109,7 @@ class TestCmd(unittest.TestCase):
             "pallet_balances\npallet_staking\npallet_something\n",  # Output for asset-hub-westend runtime
             "./substrate/frame/balances/Cargo.toml\n",                # Mock manifest path for dev -> pallet_balances
         ]
-        
+
         with patch('sys.exit') as mock_exit:
             import cmd
             cmd.main()
@@ -117,11 +117,11 @@ class TestCmd(unittest.TestCase):
 
             expected_calls = [
                 # Build calls
-                call("forklift cargo build -p kitchensink-runtime --profile release --features=runtime-benchmarks,riscv"),
+                call("forklift cargo build -p kitchensink-runtime --profile release --features=runtime-benchmarks"),
                 call("forklift cargo build -p westend-runtime --profile release --features=runtime-benchmarks"),
                 call("forklift cargo build -p rococo-runtime --profile release --features=runtime-benchmarks"),
                 call("forklift cargo build -p asset-hub-westend-runtime --profile release --features=runtime-benchmarks"),
-                
+
                 call(get_mock_bench_output(
                     runtime='kitchensink',
                     pallets='pallet_balances',
@@ -162,7 +162,7 @@ class TestCmd(unittest.TestCase):
         self.mock_popen.return_value.read.side_effect = [
             "pallet_balances\npallet_staking\npallet_something\n",  # Output for westend runtime
         ]
-        
+
         with patch('sys.exit') as mock_exit:
             import cmd
             cmd.main()
@@ -171,7 +171,7 @@ class TestCmd(unittest.TestCase):
             expected_calls = [
                 # Build calls
                 call("forklift cargo build -p westend-runtime --profile release --features=runtime-benchmarks"),
-                
+
                 # Westend runtime calls
                 call(get_mock_bench_output(
                     runtime='westend',
@@ -205,7 +205,7 @@ class TestCmd(unittest.TestCase):
         self.mock_popen.return_value.read.side_effect = [
             "pallet_balances\npallet_staking\npallet_something\npallet_xcm_benchmarks::generic\n",  # Output for westend runtime
         ]
-        
+
         with patch('sys.exit') as mock_exit:
             import cmd
             cmd.main()
@@ -214,7 +214,7 @@ class TestCmd(unittest.TestCase):
             expected_calls = [
                 # Build calls
                 call("forklift cargo build -p westend-runtime --profile release --features=runtime-benchmarks"),
-                
+
                 # Westend runtime calls
                 call(get_mock_bench_output(
                     runtime='westend',
@@ -241,7 +241,7 @@ class TestCmd(unittest.TestCase):
             "pallet_staking\npallet_balances\n",  # Output for westend runtime
             "pallet_staking\npallet_balances\n",  # Output for rococo runtime
         ]
-        
+
         with patch('sys.exit') as mock_exit:
             import cmd
             cmd.main()
@@ -309,7 +309,7 @@ class TestCmd(unittest.TestCase):
 
             expected_calls = [
                 # Build calls
-                call("forklift cargo build -p kitchensink-runtime --profile release --features=runtime-benchmarks,riscv"),
+                call("forklift cargo build -p kitchensink-runtime --profile release --features=runtime-benchmarks"),
                 # Westend runtime calls
                 call(get_mock_bench_output(
                     runtime='kitchensink',
@@ -429,4 +429,4 @@ class TestCmd(unittest.TestCase):
             self.mock_generate_prdoc_main.assert_called_with(mock_parse_args.return_value[0])
 
 if __name__ == '__main__':
-    unittest.main()
\ No newline at end of file
+    unittest.main()
diff --git a/.github/workflows/runtimes-matrix.json b/.github/workflows/runtimes-matrix.json
index e4e3a2dbe6d..f991db55b86 100644
--- a/.github/workflows/runtimes-matrix.json
+++ b/.github/workflows/runtimes-matrix.json
@@ -5,7 +5,7 @@
     "path": "substrate/frame",
     "header": "substrate/HEADER-APACHE2",
     "template": "substrate/.maintain/frame-weight-template.hbs",
-    "bench_features": "runtime-benchmarks,riscv",
+    "bench_features": "runtime-benchmarks",
     "bench_flags": "--genesis-builder-policy=none --exclude-pallets=pallet_xcm,pallet_xcm_benchmarks::fungible,pallet_xcm_benchmarks::generic,pallet_nomination_pools,pallet_remark,pallet_transaction_storage",
     "uri": null,
     "is_relay": false
diff --git a/.github/workflows/tests-linux-stable-coverage.yml b/.github/workflows/tests-linux-stable-coverage.yml
index 90d7bc34a92..c5af6bcae77 100644
--- a/.github/workflows/tests-linux-stable-coverage.yml
+++ b/.github/workflows/tests-linux-stable-coverage.yml
@@ -56,7 +56,7 @@ jobs:
           --no-report --release
           --workspace
           --locked --no-fail-fast
-          --features try-runtime,ci-only-tests,experimental,riscv
+          --features try-runtime,ci-only-tests,experimental
           --filter-expr "
           !test(/.*benchmark.*/)
           - test(/recovers_from_only_chunks_if_pov_large::case_1/)
@@ -120,4 +120,4 @@ jobs:
       - uses: actions/checkout@v4
       - uses: actions-ecosystem/action-remove-labels@v1
         with:
-          labels: GHA-coverage
\ No newline at end of file
+          labels: GHA-coverage
diff --git a/.github/workflows/tests-linux-stable.yml b/.github/workflows/tests-linux-stable.yml
index dd292d55e20..24b96219738 100644
--- a/.github/workflows/tests-linux-stable.yml
+++ b/.github/workflows/tests-linux-stable.yml
@@ -91,7 +91,7 @@ jobs:
             --release \
             --no-fail-fast \
             --cargo-quiet \
-            --features try-runtime,experimental,riscv,ci-only-tests \
+            --features try-runtime,experimental,ci-only-tests \
             --partition count:${{ matrix.partition }}
       # run runtime-api tests with `enable-staging-api` feature on the 1st node
       - name: runtime-api tests
@@ -129,7 +129,7 @@ jobs:
                    --release \
                    --no-fail-fast \
                    --cargo-quiet \
-                   --features experimental,riscv,ci-only-tests \
+                   --features experimental,ci-only-tests \
                    --filter-expr " !test(/all_security_features_work/) - test(/nonexistent_cache_dir/)" \
                    --partition count:${{ matrix.partition }} \
 
diff --git a/prdoc/pr_6305.prdoc b/prdoc/pr_6305.prdoc
new file mode 100644
index 00000000000..bfc6f06b19e
--- /dev/null
+++ b/prdoc/pr_6305.prdoc
@@ -0,0 +1,17 @@
+title: Remove `riscv` feature flag
+doc:
+- audience: Runtime Dev
+  description: Since https://github.com/paritytech/polkadot-sdk/pull/6266 we no longer
+    require a custom toolchain to build the `pallet-revive-fixtures`. Hence we no
+    longer have to guard the build behind a feature flag.
+crates:
+- name: pallet-revive
+  bump: major
+- name: pallet-revive-fixtures
+  bump: major
+- name: pallet-revive-mock-network
+  bump: major
+- name: pallet-revive-eth-rpc
+  bump: major
+- name: polkadot-sdk
+  bump: major
diff --git a/scripts/generate-umbrella.py b/scripts/generate-umbrella.py
index e1ef6de86f9..8326909c344 100644
--- a/scripts/generate-umbrella.py
+++ b/scripts/generate-umbrella.py
@@ -111,7 +111,6 @@ def main(path, version):
 		"runtime": list([f"{d.name}" for d, _ in runtime_crates]),
 		"node": ["std"] + list([f"{d.name}" for d, _ in std_crates]),
 		"tuples-96": [],
-		"riscv": [],
 	}
 
 	manifest = {
@@ -207,4 +206,3 @@ def parse_args():
 if __name__ == "__main__":
 	args = parse_args()
 	main(args.sdk, args.version)
-
diff --git a/substrate/bin/node/cli/Cargo.toml b/substrate/bin/node/cli/Cargo.toml
index 933406670e5..c179579c188 100644
--- a/substrate/bin/node/cli/Cargo.toml
+++ b/substrate/bin/node/cli/Cargo.toml
@@ -183,7 +183,6 @@ try-runtime = [
 	"polkadot-sdk/try-runtime",
 	"substrate-cli-test-utils/try-runtime",
 ]
-riscv = ["kitchensink-runtime/riscv", "polkadot-sdk/riscv"]
 
 [[bench]]
 name = "transaction_pool"
diff --git a/substrate/bin/node/runtime/Cargo.toml b/substrate/bin/node/runtime/Cargo.toml
index 7acf4294c51..3ad6315561d 100644
--- a/substrate/bin/node/runtime/Cargo.toml
+++ b/substrate/bin/node/runtime/Cargo.toml
@@ -74,4 +74,3 @@ experimental = [
 	"pallet-example-tasks/experimental",
 ]
 metadata-hash = ["substrate-wasm-builder/metadata-hash"]
-riscv = ["polkadot-sdk/riscv"]
diff --git a/substrate/frame/revive/Cargo.toml b/substrate/frame/revive/Cargo.toml
index c6e733477f3..67bc1809cad 100644
--- a/substrate/frame/revive/Cargo.toml
+++ b/substrate/frame/revive/Cargo.toml
@@ -79,10 +79,6 @@ xcm-builder = { workspace = true, default-features = true }
 
 [features]
 default = ["std"]
-# enabling this feature will require having a riscv toolchain installed
-# if no tests are ran and runtime benchmarks will not work
-# apart from this the pallet will stay functional
-riscv = ["pallet-revive-fixtures/riscv"]
 std = [
 	"codec/std",
 	"environmental/std",
diff --git a/substrate/frame/revive/fixtures/Cargo.toml b/substrate/frame/revive/fixtures/Cargo.toml
index 1e6c950addf..7a5452853d6 100644
--- a/substrate/frame/revive/fixtures/Cargo.toml
+++ b/substrate/frame/revive/fixtures/Cargo.toml
@@ -26,9 +26,5 @@ anyhow = { workspace = true, default-features = true }
 
 [features]
 default = ["std"]
-# only if the feature is set we are building the test fixtures
-# this is because it requires a custom toolchain supporting polkavm
-# we will remove this once there is an upstream toolchain
-riscv = []
 # only when std is enabled all fixtures are available
 std = ["anyhow", "frame-system", "log/std", "sp-core", "sp-io", "sp-runtime"]
diff --git a/substrate/frame/revive/fixtures/build.rs b/substrate/frame/revive/fixtures/build.rs
index 38d63621677..bbd986d9d44 100644
--- a/substrate/frame/revive/fixtures/build.rs
+++ b/substrate/frame/revive/fixtures/build.rs
@@ -18,218 +18,200 @@
 //! Compile text fixtures to PolkaVM binaries.
 use anyhow::Result;
 
-fn main() -> Result<()> {
-	build::run()
+use anyhow::{bail, Context};
+use std::{
+	cfg, env, fs,
+	path::{Path, PathBuf},
+	process::Command,
+};
+
+const OVERRIDE_RUSTUP_TOOLCHAIN_ENV_VAR: &str = "PALLET_REVIVE_FIXTURES_RUSTUP_TOOLCHAIN";
+const OVERRIDE_STRIP_ENV_VAR: &str = "PALLET_REVIVE_FIXTURES_STRIP";
+const OVERRIDE_OPTIMIZE_ENV_VAR: &str = "PALLET_REVIVE_FIXTURES_OPTIMIZE";
+
+/// A contract entry.
+struct Entry {
+	/// The path to the contract source file.
+	path: PathBuf,
 }
 
-#[cfg(feature = "riscv")]
-mod build {
-	use super::Result;
-	use anyhow::{bail, Context};
-	use std::{
-		cfg, env, fs,
-		path::{Path, PathBuf},
-		process::Command,
-	};
-
-	const OVERRIDE_RUSTUP_TOOLCHAIN_ENV_VAR: &str = "PALLET_REVIVE_FIXTURES_RUSTUP_TOOLCHAIN";
-	const OVERRIDE_STRIP_ENV_VAR: &str = "PALLET_REVIVE_FIXTURES_STRIP";
-	const OVERRIDE_OPTIMIZE_ENV_VAR: &str = "PALLET_REVIVE_FIXTURES_OPTIMIZE";
+impl Entry {
+	/// Create a new contract entry from the given path.
+	fn new(path: PathBuf) -> Self {
+		Self { path }
+	}
 
-	/// A contract entry.
-	struct Entry {
-		/// The path to the contract source file.
-		path: PathBuf,
+	/// Return the path to the contract source file.
+	fn path(&self) -> &str {
+		self.path.to_str().expect("path is valid unicode; qed")
 	}
 
-	impl Entry {
-		/// Create a new contract entry from the given path.
-		fn new(path: PathBuf) -> Self {
-			Self { path }
-		}
+	/// Return the name of the contract.
+	fn name(&self) -> &str {
+		self.path
+			.file_stem()
+			.expect("file exits; qed")
+			.to_str()
+			.expect("name is valid unicode; qed")
+	}
 
-		/// Return the path to the contract source file.
-		fn path(&self) -> &str {
-			self.path.to_str().expect("path is valid unicode; qed")
-		}
+	/// Return the name of the polkavm file.
+	fn out_filename(&self) -> String {
+		format!("{}.polkavm", self.name())
+	}
+}
 
-		/// Return the name of the contract.
-		fn name(&self) -> &str {
-			self.path
-				.file_stem()
-				.expect("file exits; qed")
-				.to_str()
-				.expect("name is valid unicode; qed")
-		}
+/// Collect all contract entries from the given source directory.
+fn collect_entries(contracts_dir: &Path) -> Vec<Entry> {
+	fs::read_dir(contracts_dir)
+		.expect("src dir exists; qed")
+		.filter_map(|file| {
+			let path = file.expect("file exists; qed").path();
+			if path.extension().map_or(true, |ext| ext != "rs") {
+				return None
+			}
 
-		/// Return the name of the polkavm file.
-		fn out_filename(&self) -> String {
-			format!("{}.polkavm", self.name())
-		}
-	}
+			Some(Entry::new(path))
+		})
+		.collect::<Vec<_>>()
+}
 
-	/// Collect all contract entries from the given source directory.
-	fn collect_entries(contracts_dir: &Path) -> Vec<Entry> {
-		fs::read_dir(contracts_dir)
-			.expect("src dir exists; qed")
-			.filter_map(|file| {
-				let path = file.expect("file exists; qed").path();
-				if path.extension().map_or(true, |ext| ext != "rs") {
-					return None
-				}
-
-				Some(Entry::new(path))
+/// Create a `Cargo.toml` to compile the given contract entries.
+fn create_cargo_toml<'a>(
+	fixtures_dir: &Path,
+	entries: impl Iterator<Item = &'a Entry>,
+	output_dir: &Path,
+) -> Result<()> {
+	let mut cargo_toml: toml::Value = toml::from_str(include_str!("./build/Cargo.toml"))?;
+	let mut set_dep = |name, path| -> Result<()> {
+		cargo_toml["dependencies"][name]["path"] = toml::Value::String(
+			fixtures_dir.join(path).canonicalize()?.to_str().unwrap().to_string(),
+		);
+		Ok(())
+	};
+	set_dep("uapi", "../uapi")?;
+	set_dep("common", "./contracts/common")?;
+
+	cargo_toml["bin"] = toml::Value::Array(
+		entries
+			.map(|entry| {
+				let name = entry.name();
+				let path = entry.path();
+				toml::Value::Table(toml::toml! {
+					name = name
+					path = path
+				})
 			})
-			.collect::<Vec<_>>()
-	}
+			.collect::<Vec<_>>(),
+	);
 
-	/// Create a `Cargo.toml` to compile the given contract entries.
-	fn create_cargo_toml<'a>(
-		fixtures_dir: &Path,
-		entries: impl Iterator<Item = &'a Entry>,
-		output_dir: &Path,
-	) -> Result<()> {
-		let mut cargo_toml: toml::Value = toml::from_str(include_str!("./build/Cargo.toml"))?;
-		let mut set_dep = |name, path| -> Result<()> {
-			cargo_toml["dependencies"][name]["path"] = toml::Value::String(
-				fixtures_dir.join(path).canonicalize()?.to_str().unwrap().to_string(),
-			);
-			Ok(())
-		};
-		set_dep("uapi", "../uapi")?;
-		set_dep("common", "./contracts/common")?;
-
-		cargo_toml["bin"] = toml::Value::Array(
-			entries
-				.map(|entry| {
-					let name = entry.name();
-					let path = entry.path();
-					toml::Value::Table(toml::toml! {
-						name = name
-						path = path
-					})
-				})
-				.collect::<Vec<_>>(),
-		);
+	let cargo_toml = toml::to_string_pretty(&cargo_toml)?;
+	fs::write(output_dir.join("Cargo.toml"), cargo_toml).map_err(Into::into)
+}
 
-		let cargo_toml = toml::to_string_pretty(&cargo_toml)?;
-		fs::write(output_dir.join("Cargo.toml"), cargo_toml).map_err(Into::into)
+fn invoke_build(target: &Path, current_dir: &Path) -> Result<()> {
+	let encoded_rustflags = ["-Dwarnings"].join("\x1f");
+
+	let mut build_command = Command::new(env::var("CARGO")?);
+	build_command
+		.current_dir(current_dir)
+		.env_clear()
+		.env("PATH", env::var("PATH").unwrap_or_default())
+		.env("CARGO_ENCODED_RUSTFLAGS", encoded_rustflags)
+		.env("RUSTC_BOOTSTRAP", "1")
+		.env("RUSTUP_HOME", env::var("RUSTUP_HOME").unwrap_or_default())
+		.args([
+			"build",
+			"--release",
+			"-Zbuild-std=core",
+			"-Zbuild-std-features=panic_immediate_abort",
+		])
+		.arg("--target")
+		.arg(target);
+
+	if let Ok(toolchain) = env::var(OVERRIDE_RUSTUP_TOOLCHAIN_ENV_VAR) {
+		build_command.env("RUSTUP_TOOLCHAIN", &toolchain);
 	}
 
-	fn invoke_build(target: &Path, current_dir: &Path) -> Result<()> {
-		let encoded_rustflags = ["-Dwarnings"].join("\x1f");
-
-		let mut build_command = Command::new(env::var("CARGO")?);
-		build_command
-			.current_dir(current_dir)
-			.env_clear()
-			.env("PATH", env::var("PATH").unwrap_or_default())
-			.env("CARGO_ENCODED_RUSTFLAGS", encoded_rustflags)
-			.env("RUSTC_BOOTSTRAP", "1")
-			.env("RUSTUP_HOME", env::var("RUSTUP_HOME").unwrap_or_default())
-			.args([
-				"build",
-				"--release",
-				"-Zbuild-std=core",
-				"-Zbuild-std-features=panic_immediate_abort",
-			])
-			.arg("--target")
-			.arg(target);
-
-		if let Ok(toolchain) = env::var(OVERRIDE_RUSTUP_TOOLCHAIN_ENV_VAR) {
-			build_command.env("RUSTUP_TOOLCHAIN", &toolchain);
-		}
+	let build_res = build_command.output().expect("failed to execute process");
 
-		let build_res = build_command.output().expect("failed to execute process");
+	if build_res.status.success() {
+		return Ok(())
+	}
 
-		if build_res.status.success() {
-			return Ok(())
-		}
+	let stderr = String::from_utf8_lossy(&build_res.stderr);
+	eprintln!("{}", stderr);
 
-		let stderr = String::from_utf8_lossy(&build_res.stderr);
-		eprintln!("{}", stderr);
+	bail!("Failed to build contracts");
+}
 
-		bail!("Failed to build contracts");
-	}
+/// Post-process the compiled code.
+fn post_process(input_path: &Path, output_path: &Path) -> Result<()> {
+	let strip = std::env::var(OVERRIDE_STRIP_ENV_VAR).map_or(false, |value| value == "1");
+	let optimize = std::env::var(OVERRIDE_OPTIMIZE_ENV_VAR).map_or(true, |value| value == "1");
+
+	let mut config = polkavm_linker::Config::default();
+	config.set_strip(strip);
+	config.set_optimize(optimize);
+	let orig = fs::read(input_path).with_context(|| format!("Failed to read {:?}", input_path))?;
+	let linked = polkavm_linker::program_from_elf(config, orig.as_ref())
+		.map_err(|err| anyhow::format_err!("Failed to link polkavm program: {}", err))?;
+	fs::write(output_path, linked).map_err(Into::into)
+}
 
-	/// Post-process the compiled code.
-	fn post_process(input_path: &Path, output_path: &Path) -> Result<()> {
-		let strip = std::env::var(OVERRIDE_STRIP_ENV_VAR).map_or(false, |value| value == "1");
-		let optimize = std::env::var(OVERRIDE_OPTIMIZE_ENV_VAR).map_or(true, |value| value == "1");
-
-		let mut config = polkavm_linker::Config::default();
-		config.set_strip(strip);
-		config.set_optimize(optimize);
-		let orig =
-			fs::read(input_path).with_context(|| format!("Failed to read {:?}", input_path))?;
-		let linked = polkavm_linker::program_from_elf(config, orig.as_ref())
-			.map_err(|err| anyhow::format_err!("Failed to link polkavm program: {}", err))?;
-		fs::write(output_path, linked).map_err(Into::into)
+/// Write the compiled contracts to the given output directory.
+fn write_output(build_dir: &Path, out_dir: &Path, entries: Vec<Entry>) -> Result<()> {
+	for entry in entries {
+		post_process(
+			&build_dir
+				.join("target/riscv32emac-unknown-none-polkavm/release")
+				.join(entry.name()),
+			&out_dir.join(entry.out_filename()),
+		)?;
 	}
 
-	/// Write the compiled contracts to the given output directory.
-	fn write_output(build_dir: &Path, out_dir: &Path, entries: Vec<Entry>) -> Result<()> {
-		for entry in entries {
-			post_process(
-				&build_dir
-					.join("target/riscv32emac-unknown-none-polkavm/release")
-					.join(entry.name()),
-				&out_dir.join(entry.out_filename()),
-			)?;
-		}
+	Ok(())
+}
 
-		Ok(())
+pub fn main() -> Result<()> {
+	let fixtures_dir: PathBuf = env::var("CARGO_MANIFEST_DIR")?.into();
+	let contracts_dir = fixtures_dir.join("contracts");
+	let out_dir: PathBuf = env::var("OUT_DIR")?.into();
+	let target = fixtures_dir.join("riscv32emac-unknown-none-polkavm.json");
+
+	println!("cargo::rerun-if-env-changed={OVERRIDE_RUSTUP_TOOLCHAIN_ENV_VAR}");
+	println!("cargo::rerun-if-env-changed={OVERRIDE_STRIP_ENV_VAR}");
+	println!("cargo::rerun-if-env-changed={OVERRIDE_OPTIMIZE_ENV_VAR}");
+
+	// the fixtures have a dependency on the uapi crate
+	println!("cargo::rerun-if-changed={}", fixtures_dir.display());
+	let uapi_dir = fixtures_dir.parent().expect("parent dir exits; qed").join("uapi");
+	if uapi_dir.exists() {
+		println!("cargo::rerun-if-changed={}", uapi_dir.display());
 	}
 
-	pub fn run() -> Result<()> {
-		let fixtures_dir: PathBuf = env::var("CARGO_MANIFEST_DIR")?.into();
-		let contracts_dir = fixtures_dir.join("contracts");
-		let out_dir: PathBuf = env::var("OUT_DIR")?.into();
-		let target = fixtures_dir.join("riscv32emac-unknown-none-polkavm.json");
-
-		println!("cargo::rerun-if-env-changed={OVERRIDE_RUSTUP_TOOLCHAIN_ENV_VAR}");
-		println!("cargo::rerun-if-env-changed={OVERRIDE_STRIP_ENV_VAR}");
-		println!("cargo::rerun-if-env-changed={OVERRIDE_OPTIMIZE_ENV_VAR}");
-
-		// the fixtures have a dependency on the uapi crate
-		println!("cargo::rerun-if-changed={}", fixtures_dir.display());
-		let uapi_dir = fixtures_dir.parent().expect("parent dir exits; qed").join("uapi");
-		if uapi_dir.exists() {
-			println!("cargo::rerun-if-changed={}", uapi_dir.display());
-		}
-
-		let entries = collect_entries(&contracts_dir);
-		if entries.is_empty() {
-			return Ok(())
-		}
+	let entries = collect_entries(&contracts_dir);
+	if entries.is_empty() {
+		return Ok(())
+	}
 
-		let tmp_dir = tempfile::tempdir()?;
-		let tmp_dir_path = tmp_dir.path();
+	let tmp_dir = tempfile::tempdir()?;
+	let tmp_dir_path = tmp_dir.path();
 
-		create_cargo_toml(&fixtures_dir, entries.iter(), tmp_dir.path())?;
-		invoke_build(&target, tmp_dir_path)?;
+	create_cargo_toml(&fixtures_dir, entries.iter(), tmp_dir.path())?;
+	invoke_build(&target, tmp_dir_path)?;
 
-		write_output(tmp_dir_path, &out_dir, entries)?;
+	write_output(tmp_dir_path, &out_dir, entries)?;
 
-		#[cfg(unix)]
-		if let Ok(symlink_dir) = env::var("CARGO_WORKSPACE_ROOT_DIR") {
-			let symlink_dir: PathBuf = symlink_dir.into();
-			let symlink_dir: PathBuf = symlink_dir.join("target").join("pallet-revive-fixtures");
-			if symlink_dir.is_symlink() {
-				fs::remove_file(&symlink_dir)?
-			}
-			std::os::unix::fs::symlink(&out_dir, &symlink_dir)?;
+	#[cfg(unix)]
+	if let Ok(symlink_dir) = env::var("CARGO_WORKSPACE_ROOT_DIR") {
+		let symlink_dir: PathBuf = symlink_dir.into();
+		let symlink_dir: PathBuf = symlink_dir.join("target").join("pallet-revive-fixtures");
+		if symlink_dir.is_symlink() {
+			fs::remove_file(&symlink_dir)?
 		}
-
-		Ok(())
+		std::os::unix::fs::symlink(&out_dir, &symlink_dir)?;
 	}
-}
-
-#[cfg(not(feature = "riscv"))]
-mod build {
-	use super::Result;
 
-	pub fn run() -> Result<()> {
-		Ok(())
-	}
+	Ok(())
 }
diff --git a/substrate/frame/revive/fixtures/src/lib.rs b/substrate/frame/revive/fixtures/src/lib.rs
index 5548dca66d0..cc84daec9b5 100644
--- a/substrate/frame/revive/fixtures/src/lib.rs
+++ b/substrate/frame/revive/fixtures/src/lib.rs
@@ -37,18 +37,11 @@ pub fn compile_module(fixture_name: &str) -> anyhow::Result<(Vec<u8>, sp_core::H
 pub mod bench {
 	use alloc::vec::Vec;
 
-	#[cfg(feature = "riscv")]
 	macro_rules! fixture {
 		($name: literal) => {
 			include_bytes!(concat!(env!("OUT_DIR"), "/", $name, ".polkavm"))
 		};
 	}
-	#[cfg(not(feature = "riscv"))]
-	macro_rules! fixture {
-		($name: literal) => {
-			&[]
-		};
-	}
 	pub const DUMMY: &[u8] = fixture!("dummy");
 	pub const NOOP: &[u8] = fixture!("noop");
 	pub const INSTR: &[u8] = fixture!("instr_benchmark");
diff --git a/substrate/frame/revive/mock-network/Cargo.toml b/substrate/frame/revive/mock-network/Cargo.toml
index 12de634b0b4..c5b18b3fa29 100644
--- a/substrate/frame/revive/mock-network/Cargo.toml
+++ b/substrate/frame/revive/mock-network/Cargo.toml
@@ -48,7 +48,6 @@ pallet-revive-fixtures = { workspace = true }
 
 [features]
 default = ["std"]
-riscv = ["pallet-revive-fixtures/riscv"]
 std = [
 	"codec/std",
 	"frame-support/std",
diff --git a/substrate/frame/revive/mock-network/src/lib.rs b/substrate/frame/revive/mock-network/src/lib.rs
index 84899465397..adfd0016b4d 100644
--- a/substrate/frame/revive/mock-network/src/lib.rs
+++ b/substrate/frame/revive/mock-network/src/lib.rs
@@ -19,7 +19,7 @@ pub mod parachain;
 pub mod primitives;
 pub mod relay_chain;
 
-#[cfg(all(test, feature = "riscv"))]
+#[cfg(test)]
 mod tests;
 
 use crate::primitives::{AccountId, UNITS};
diff --git a/substrate/frame/revive/rpc/Cargo.toml b/substrate/frame/revive/rpc/Cargo.toml
index ef7a7c1b28e..e6d8c38c04f 100644
--- a/substrate/frame/revive/rpc/Cargo.toml
+++ b/substrate/frame/revive/rpc/Cargo.toml
@@ -15,27 +15,27 @@ path = "src/main.rs"
 [[example]]
 name = "deploy"
 path = "examples/rust/deploy.rs"
-required-features = ["example", "riscv"]
+required-features = ["example"]
 
 [[example]]
 name = "transfer"
 path = "examples/rust/transfer.rs"
-required-features = ["example", "riscv"]
+required-features = ["example"]
 
 [[example]]
 name = "rpc-playground"
 path = "examples/rust/rpc-playground.rs"
-required-features = ["example", "riscv"]
+required-features = ["example"]
 
 [[example]]
 name = "extrinsic"
 path = "examples/rust/extrinsic.rs"
-required-features = ["example", "riscv"]
+required-features = ["example"]
 
 [[example]]
 name = "remark-extrinsic"
 path = "examples/rust/remark-extrinsic.rs"
-required-features = ["example", "riscv"]
+required-features = ["example"]
 
 [dependencies]
 clap = { workspace = true, features = ["derive"] }
@@ -72,7 +72,6 @@ env_logger = { workspace = true }
 
 [features]
 example = ["hex", "hex-literal", "rlp", "secp256k1", "subxt-signer"]
-riscv = ["pallet-revive/riscv"]
 
 [dev-dependencies]
 hex-literal = { workspace = true }
diff --git a/substrate/frame/revive/rpc/Dockerfile b/substrate/frame/revive/rpc/Dockerfile
index 981d5c19a15..fb867062a81 100644
--- a/substrate/frame/revive/rpc/Dockerfile
+++ b/substrate/frame/revive/rpc/Dockerfile
@@ -7,6 +7,7 @@ RUN apt-get update && \
 
 WORKDIR /polkadot
 COPY . /polkadot
+RUN rustup component add rust-src
 RUN cargo build --locked --profile production -p pallet-revive-eth-rpc --bin eth-rpc
 
 FROM docker.io/parity/base-bin:latest
diff --git a/substrate/frame/revive/rpc/examples/README.md b/substrate/frame/revive/rpc/examples/README.md
index 7c01dc0075e..bf30426648b 100644
--- a/substrate/frame/revive/rpc/examples/README.md
+++ b/substrate/frame/revive/rpc/examples/README.md
@@ -3,7 +3,7 @@
  Build `pallet-revive-fixture`, as we need some compiled contracts to exercise the RPC server.
 
 ```bash
-cargo build -p pallet-revive-fixtures --features riscv
+cargo build -p pallet-revive-fixtures
 ```
 
 ## Start the node
@@ -96,4 +96,3 @@ See [this guide][import-account] for more info on how to import an account.
 [add-network]: https://support.metamask.io/networks-and-sidechains/managing-networks/how-to-add-a-custom-network-rpc/#adding-a-network-manually
 [import-account]: https://support.metamask.io/managing-my-wallet/accounts-and-addresses/how-to-import-an-account/
 [reset-account]: https://support.metamask.io/managing-my-wallet/resetting-deleting-and-restoring/how-to-clear-your-account-activity-reset-account
-
diff --git a/substrate/frame/revive/rpc/src/tests.rs b/substrate/frame/revive/rpc/src/tests.rs
index f745bea6a5f..5d84e06e9e0 100644
--- a/substrate/frame/revive/rpc/src/tests.rs
+++ b/substrate/frame/revive/rpc/src/tests.rs
@@ -16,8 +16,6 @@
 // limitations under the License.
 //! Test the eth-rpc cli with the kitchensink node.
 
-// We require the `riscv` feature to get access to the compiled fixtures.
-#![cfg(feature = "riscv")]
 use crate::{
 	cli::{self, CliCommand},
 	example::{send_transaction, wait_for_receipt},
diff --git a/substrate/frame/revive/src/benchmarking/mod.rs b/substrate/frame/revive/src/benchmarking/mod.rs
index dd7e52327b6..3d1d7d2a224 100644
--- a/substrate/frame/revive/src/benchmarking/mod.rs
+++ b/substrate/frame/revive/src/benchmarking/mod.rs
@@ -15,9 +15,9 @@
 // See the License for the specific language governing permissions and
 // limitations under the License.
 
-//! Benchmarks for the contracts pallet
+//! Benchmarks for the revive pallet
 
-#![cfg(all(feature = "runtime-benchmarks", feature = "riscv"))]
+#![cfg(feature = "runtime-benchmarks")]
 
 mod call_builder;
 mod code;
diff --git a/substrate/frame/revive/src/benchmarking_dummy.rs b/substrate/frame/revive/src/benchmarking_dummy.rs
deleted file mode 100644
index 6bb46791127..00000000000
--- a/substrate/frame/revive/src/benchmarking_dummy.rs
+++ /dev/null
@@ -1,37 +0,0 @@
-// 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.
-//
-
-//! Defines a dummy benchmarking suite so that the build doesn't fail in case
-//! no RISC-V toolchain is available.
-
-#![cfg(feature = "runtime-benchmarks")]
-#![cfg(not(feature = "riscv"))]
-
-use crate::{Config, *};
-use frame_benchmarking::v2::*;
-
-#[benchmarks]
-mod benchmarks {
-	use super::*;
-
-	#[benchmark(pov_mode = Ignored)]
-	fn enable_riscv_feature_to_unlock_benchmarks() {
-		#[block]
-		{}
-	}
-}
diff --git a/substrate/frame/revive/src/evm/runtime.rs b/substrate/frame/revive/src/evm/runtime.rs
index 6db3f43857e..3acd67b32aa 100644
--- a/substrate/frame/revive/src/evm/runtime.rs
+++ b/substrate/frame/revive/src/evm/runtime.rs
@@ -396,7 +396,6 @@ pub trait EthExtra {
 	}
 }
 
-#[cfg(feature = "riscv")]
 #[cfg(test)]
 mod test {
 	use super::*;
diff --git a/substrate/frame/revive/src/exec.rs b/substrate/frame/revive/src/exec.rs
index 4b7198d570c..8629a21c4fd 100644
--- a/substrate/frame/revive/src/exec.rs
+++ b/substrate/frame/revive/src/exec.rs
@@ -819,7 +819,7 @@ where
 			.map(|_| (address, stack.first_frame.last_frame_output))
 	}
 
-	#[cfg(all(feature = "runtime-benchmarks", feature = "riscv"))]
+	#[cfg(feature = "runtime-benchmarks")]
 	pub fn bench_new_call(
 		dest: H160,
 		origin: Origin<T>,
@@ -1330,12 +1330,12 @@ where
 
 	/// Certain APIs, e.g. `{set,get}_immutable_data` behave differently depending
 	/// on the configured entry point. Thus, we allow setting the export manually.
-	#[cfg(all(feature = "runtime-benchmarks", feature = "riscv"))]
+	#[cfg(feature = "runtime-benchmarks")]
 	pub(crate) fn override_export(&mut self, export: ExportedFunction) {
 		self.top_frame_mut().entry_point = export;
 	}
 
-	#[cfg(all(feature = "runtime-benchmarks", feature = "riscv"))]
+	#[cfg(feature = "runtime-benchmarks")]
 	pub(crate) fn set_block_number(&mut self, block_number: BlockNumberFor<T>) {
 		self.block_number = block_number;
 	}
diff --git a/substrate/frame/revive/src/lib.rs b/substrate/frame/revive/src/lib.rs
index d50da45fc3a..51e9a8fa3f9 100644
--- a/substrate/frame/revive/src/lib.rs
+++ b/substrate/frame/revive/src/lib.rs
@@ -23,7 +23,6 @@
 extern crate alloc;
 mod address;
 mod benchmarking;
-mod benchmarking_dummy;
 mod exec;
 mod gas;
 mod limits;
diff --git a/substrate/frame/revive/src/storage.rs b/substrate/frame/revive/src/storage.rs
index db4db3e8eac..b7156588d44 100644
--- a/substrate/frame/revive/src/storage.rs
+++ b/substrate/frame/revive/src/storage.rs
@@ -505,7 +505,6 @@ impl<T: Config> DeletionQueueManager<T> {
 }
 
 #[cfg(test)]
-#[cfg(feature = "riscv")]
 impl<T: Config> DeletionQueueManager<T> {
 	pub fn from_test_values(insert_counter: u32, delete_counter: u32) -> Self {
 		Self { insert_counter, delete_counter, _phantom: Default::default() }
diff --git a/substrate/frame/revive/src/tests.rs b/substrate/frame/revive/src/tests.rs
index 7ce2e3d9bf3..2d9cae16c44 100644
--- a/substrate/frame/revive/src/tests.rs
+++ b/substrate/frame/revive/src/tests.rs
@@ -15,8 +15,6 @@
 // See the License for the specific language governing permissions and
 // limitations under the License.
 
-#![cfg_attr(not(feature = "riscv"), allow(dead_code, unused_imports, unused_macros))]
-
 mod pallet_dummy;
 mod test_debug;
 
@@ -64,6 +62,8 @@ use frame_system::{EventRecord, Phase};
 use pallet_revive_fixtures::{bench::dummy_unique, compile_module};
 use pallet_revive_uapi::ReturnErrorCode as RuntimeReturnCode;
 use pallet_transaction_payment::{ConstFeeMultiplier, Multiplier};
+use pretty_assertions::{assert_eq, assert_ne};
+use sp_core::U256;
 use sp_io::hashing::blake2_256;
 use sp_keystore::{testing::MemoryKeystore, KeystoreExt};
 use sp_runtime::{
@@ -624,1300 +624,1400 @@ impl Default for Origin<Test> {
 	}
 }
 
-/// We can only run the tests if we have a riscv toolchain installed
-#[cfg(feature = "riscv")]
-mod run_tests {
-	use super::*;
-	use pretty_assertions::{assert_eq, assert_ne};
-	use sp_core::U256;
+#[test]
+fn calling_plain_account_is_balance_transfer() {
+	ExtBuilder::default().build().execute_with(|| {
+		let _ = <Test as Config>::Currency::set_balance(&ALICE, 100_000_000);
+		assert!(!<ContractInfoOf<Test>>::contains_key(BOB_ADDR));
+		assert_eq!(test_utils::get_balance(&BOB_FALLBACK), 0);
+		let result = builder::bare_call(BOB_ADDR).value(42).build_and_unwrap_result();
+		assert_eq!(test_utils::get_balance(&BOB_FALLBACK), 42);
+		assert_eq!(result, Default::default());
+	});
+}
 
-	#[test]
-	fn calling_plain_account_is_balance_transfer() {
-		ExtBuilder::default().build().execute_with(|| {
-			let _ = <Test as Config>::Currency::set_balance(&ALICE, 100_000_000);
-			assert!(!<ContractInfoOf<Test>>::contains_key(BOB_ADDR));
-			assert_eq!(test_utils::get_balance(&BOB_FALLBACK), 0);
-			let result = builder::bare_call(BOB_ADDR).value(42).build_and_unwrap_result();
-			assert_eq!(test_utils::get_balance(&BOB_FALLBACK), 42);
-			assert_eq!(result, Default::default());
-		});
-	}
+#[test]
+fn instantiate_and_call_and_deposit_event() {
+	let (wasm, code_hash) = compile_module("event_and_return_on_deploy").unwrap();
 
-	#[test]
-	fn instantiate_and_call_and_deposit_event() {
-		let (wasm, code_hash) = compile_module("event_and_return_on_deploy").unwrap();
+	ExtBuilder::default().existential_deposit(1).build().execute_with(|| {
+		let _ = <Test as Config>::Currency::set_balance(&ALICE, 1_000_000);
+		let min_balance = Contracts::min_balance();
+		let value = 100;
 
-		ExtBuilder::default().existential_deposit(1).build().execute_with(|| {
-			let _ = <Test as Config>::Currency::set_balance(&ALICE, 1_000_000);
-			let min_balance = Contracts::min_balance();
-			let value = 100;
+		// We determine the storage deposit limit after uploading because it depends on ALICEs
+		// free balance which is changed by uploading a module.
+		assert_ok!(Contracts::upload_code(
+			RuntimeOrigin::signed(ALICE),
+			wasm,
+			deposit_limit::<Test>(),
+		));
+
+		// Drop previous events
+		initialize_block(2);
+
+		// Check at the end to get hash on error easily
+		let Contract { addr, account_id } = builder::bare_instantiate(Code::Existing(code_hash))
+			.value(value)
+			.build_and_unwrap_contract();
+		assert!(ContractInfoOf::<Test>::contains_key(&addr));
+
+		assert_eq!(
+			System::events(),
+			vec![
+				EventRecord {
+					phase: Phase::Initialization,
+					event: RuntimeEvent::System(frame_system::Event::NewAccount {
+						account: account_id.clone()
+					}),
+					topics: vec![],
+				},
+				EventRecord {
+					phase: Phase::Initialization,
+					event: RuntimeEvent::Balances(pallet_balances::Event::Endowed {
+						account: account_id.clone(),
+						free_balance: min_balance,
+					}),
+					topics: vec![],
+				},
+				EventRecord {
+					phase: Phase::Initialization,
+					event: RuntimeEvent::Balances(pallet_balances::Event::Transfer {
+						from: ALICE,
+						to: account_id.clone(),
+						amount: min_balance,
+					}),
+					topics: vec![],
+				},
+				EventRecord {
+					phase: Phase::Initialization,
+					event: RuntimeEvent::Balances(pallet_balances::Event::Transfer {
+						from: ALICE,
+						to: account_id.clone(),
+						amount: value,
+					}),
+					topics: vec![],
+				},
+				EventRecord {
+					phase: Phase::Initialization,
+					event: RuntimeEvent::Contracts(crate::Event::ContractEmitted {
+						contract: addr,
+						data: vec![1, 2, 3, 4],
+						topics: vec![H256::repeat_byte(42)],
+					}),
+					topics: vec![],
+				},
+				EventRecord {
+					phase: Phase::Initialization,
+					event: RuntimeEvent::Contracts(crate::Event::Instantiated {
+						deployer: ALICE_ADDR,
+						contract: addr
+					}),
+					topics: vec![],
+				},
+				EventRecord {
+					phase: Phase::Initialization,
+					event: RuntimeEvent::Contracts(
+						pallet_revive::Event::StorageDepositTransferredAndHeld {
+							from: ALICE_ADDR,
+							to: addr,
+							amount: test_utils::contract_info_storage_deposit(&addr),
+						}
+					),
+					topics: vec![],
+				},
+			]
+		);
+	});
+}
 
-			// We determine the storage deposit limit after uploading because it depends on ALICEs
-			// free balance which is changed by uploading a module.
-			assert_ok!(Contracts::upload_code(
-				RuntimeOrigin::signed(ALICE),
-				wasm,
-				deposit_limit::<Test>(),
-			));
+#[test]
+fn create1_address_from_extrinsic() {
+	let (wasm, code_hash) = compile_module("dummy").unwrap();
 
-			// Drop previous events
-			initialize_block(2);
+	ExtBuilder::default().existential_deposit(1).build().execute_with(|| {
+		let _ = <Test as Config>::Currency::set_balance(&ALICE, 1_000_000);
 
-			// Check at the end to get hash on error easily
-			let Contract { addr, account_id } =
-				builder::bare_instantiate(Code::Existing(code_hash))
-					.value(value)
-					.build_and_unwrap_contract();
-			assert!(ContractInfoOf::<Test>::contains_key(&addr));
+		assert_ok!(Contracts::upload_code(
+			RuntimeOrigin::signed(ALICE),
+			wasm.clone(),
+			deposit_limit::<Test>(),
+		));
+
+		assert_eq!(System::account_nonce(&ALICE), 0);
+		System::inc_account_nonce(&ALICE);
 
+		for nonce in 1..3 {
+			let Contract { addr, .. } = builder::bare_instantiate(Code::Existing(code_hash))
+				.salt(None)
+				.build_and_unwrap_contract();
+			assert!(ContractInfoOf::<Test>::contains_key(&addr));
 			assert_eq!(
-				System::events(),
-				vec![
-					EventRecord {
-						phase: Phase::Initialization,
-						event: RuntimeEvent::System(frame_system::Event::NewAccount {
-							account: account_id.clone()
-						}),
-						topics: vec![],
-					},
-					EventRecord {
-						phase: Phase::Initialization,
-						event: RuntimeEvent::Balances(pallet_balances::Event::Endowed {
-							account: account_id.clone(),
-							free_balance: min_balance,
-						}),
-						topics: vec![],
-					},
-					EventRecord {
-						phase: Phase::Initialization,
-						event: RuntimeEvent::Balances(pallet_balances::Event::Transfer {
-							from: ALICE,
-							to: account_id.clone(),
-							amount: min_balance,
-						}),
-						topics: vec![],
-					},
-					EventRecord {
-						phase: Phase::Initialization,
-						event: RuntimeEvent::Balances(pallet_balances::Event::Transfer {
-							from: ALICE,
-							to: account_id.clone(),
-							amount: value,
-						}),
-						topics: vec![],
-					},
-					EventRecord {
-						phase: Phase::Initialization,
-						event: RuntimeEvent::Contracts(crate::Event::ContractEmitted {
-							contract: addr,
-							data: vec![1, 2, 3, 4],
-							topics: vec![H256::repeat_byte(42)],
-						}),
-						topics: vec![],
-					},
-					EventRecord {
-						phase: Phase::Initialization,
-						event: RuntimeEvent::Contracts(crate::Event::Instantiated {
-							deployer: ALICE_ADDR,
-							contract: addr
-						}),
-						topics: vec![],
-					},
-					EventRecord {
-						phase: Phase::Initialization,
-						event: RuntimeEvent::Contracts(
-							pallet_revive::Event::StorageDepositTransferredAndHeld {
-								from: ALICE_ADDR,
-								to: addr,
-								amount: test_utils::contract_info_storage_deposit(&addr),
-							}
-						),
-						topics: vec![],
-					},
-				]
+				addr,
+				create1(&<Test as Config>::AddressMapper::to_address(&ALICE), nonce - 1)
 			);
-		});
-	}
+		}
+		assert_eq!(System::account_nonce(&ALICE), 3);
 
-	#[test]
-	fn create1_address_from_extrinsic() {
-		let (wasm, code_hash) = compile_module("dummy").unwrap();
+		for nonce in 3..6 {
+			let Contract { addr, .. } = builder::bare_instantiate(Code::Upload(wasm.clone()))
+				.salt(None)
+				.build_and_unwrap_contract();
+			assert!(ContractInfoOf::<Test>::contains_key(&addr));
+			assert_eq!(
+				addr,
+				create1(&<Test as Config>::AddressMapper::to_address(&ALICE), nonce - 1)
+			);
+		}
+		assert_eq!(System::account_nonce(&ALICE), 6);
+	});
+}
 
-		ExtBuilder::default().existential_deposit(1).build().execute_with(|| {
-			let _ = <Test as Config>::Currency::set_balance(&ALICE, 1_000_000);
+#[test]
+fn deposit_event_max_value_limit() {
+	let (wasm, _code_hash) = compile_module("event_size").unwrap();
+
+	ExtBuilder::default().existential_deposit(50).build().execute_with(|| {
+		// Create
+		let _ = <Test as Config>::Currency::set_balance(&ALICE, 1_000_000);
+		let Contract { addr, .. } = builder::bare_instantiate(Code::Upload(wasm))
+			.value(30_000)
+			.build_and_unwrap_contract();
+
+		// Call contract with allowed storage value.
+		assert_ok!(builder::call(addr)
+			.gas_limit(GAS_LIMIT.set_ref_time(GAS_LIMIT.ref_time() * 2)) // we are copying a huge buffer,
+			.data(limits::PAYLOAD_BYTES.encode())
+			.build());
+
+		// Call contract with too large a storage value.
+		assert_err_ignore_postinfo!(
+			builder::call(addr).data((limits::PAYLOAD_BYTES + 1).encode()).build(),
+			Error::<Test>::ValueTooLarge,
+		);
+	});
+}
 
-			assert_ok!(Contracts::upload_code(
-				RuntimeOrigin::signed(ALICE),
-				wasm.clone(),
-				deposit_limit::<Test>(),
-			));
+// Fail out of fuel (ref_time weight) in the engine.
+#[test]
+fn run_out_of_fuel_engine() {
+	let (wasm, _code_hash) = compile_module("run_out_of_gas").unwrap();
+	ExtBuilder::default().existential_deposit(50).build().execute_with(|| {
+		let min_balance = Contracts::min_balance();
+		let _ = <Test as Config>::Currency::set_balance(&ALICE, 1_000_000);
+
+		let Contract { addr, .. } = builder::bare_instantiate(Code::Upload(wasm))
+			.value(100 * min_balance)
+			.build_and_unwrap_contract();
+
+		// Call the contract with a fixed gas limit. It must run out of gas because it just
+		// loops forever.
+		assert_err_ignore_postinfo!(
+			builder::call(addr)
+				.gas_limit(Weight::from_parts(10_000_000_000, u64::MAX))
+				.build(),
+			Error::<Test>::OutOfGas,
+		);
+	});
+}
 
-			assert_eq!(System::account_nonce(&ALICE), 0);
-			System::inc_account_nonce(&ALICE);
+// Fail out of fuel (ref_time weight) in the host.
+#[test]
+fn run_out_of_fuel_host() {
+	let (code, _hash) = compile_module("chain_extension").unwrap();
+	ExtBuilder::default().existential_deposit(50).build().execute_with(|| {
+		let min_balance = Contracts::min_balance();
+		let _ = <Test as Config>::Currency::set_balance(&ALICE, 1000 * min_balance);
+
+		let Contract { addr, .. } = builder::bare_instantiate(Code::Upload(code))
+			.value(min_balance * 100)
+			.build_and_unwrap_contract();
+
+		let gas_limit = Weight::from_parts(u32::MAX as u64, GAS_LIMIT.proof_size());
+
+		// Use chain extension to charge more ref_time than it is available.
+		let result = builder::bare_call(addr)
+			.gas_limit(gas_limit)
+			.data(ExtensionInput { extension_id: 0, func_id: 2, extra: &u32::MAX.encode() }.into())
+			.build()
+			.result;
+		assert_err!(result, <Error<Test>>::OutOfGas);
+	});
+}
 
-			for nonce in 1..3 {
-				let Contract { addr, .. } = builder::bare_instantiate(Code::Existing(code_hash))
-					.salt(None)
-					.build_and_unwrap_contract();
-				assert!(ContractInfoOf::<Test>::contains_key(&addr));
-				assert_eq!(
-					addr,
-					create1(&<Test as Config>::AddressMapper::to_address(&ALICE), nonce - 1)
-				);
-			}
-			assert_eq!(System::account_nonce(&ALICE), 3);
+#[test]
+fn gas_syncs_work() {
+	let (code, _code_hash) = compile_module("caller_is_origin_n").unwrap();
+	ExtBuilder::default().existential_deposit(200).build().execute_with(|| {
+		let _ = <Test as Config>::Currency::set_balance(&ALICE, 1_000_000);
+		let contract = builder::bare_instantiate(Code::Upload(code)).build_and_unwrap_contract();
+
+		let result = builder::bare_call(contract.addr).data(0u32.encode()).build();
+		assert_ok!(result.result);
+		let engine_consumed_noop = result.gas_consumed.ref_time();
+
+		let result = builder::bare_call(contract.addr).data(1u32.encode()).build();
+		assert_ok!(result.result);
+		let gas_consumed_once = result.gas_consumed.ref_time();
+		let host_consumed_once = <Test as Config>::WeightInfo::seal_caller_is_origin().ref_time();
+		let engine_consumed_once = gas_consumed_once - host_consumed_once - engine_consumed_noop;
+
+		let result = builder::bare_call(contract.addr).data(2u32.encode()).build();
+		assert_ok!(result.result);
+		let gas_consumed_twice = result.gas_consumed.ref_time();
+		let host_consumed_twice = host_consumed_once * 2;
+		let engine_consumed_twice = gas_consumed_twice - host_consumed_twice - engine_consumed_noop;
+
+		// Second contract just repeats first contract's instructions twice.
+		// If runtime syncs gas with the engine properly, this should pass.
+		assert_eq!(engine_consumed_twice, engine_consumed_once * 2);
+	});
+}
 
-			for nonce in 3..6 {
-				let Contract { addr, .. } = builder::bare_instantiate(Code::Upload(wasm.clone()))
-					.salt(None)
-					.build_and_unwrap_contract();
-				assert!(ContractInfoOf::<Test>::contains_key(&addr));
-				assert_eq!(
-					addr,
-					create1(&<Test as Config>::AddressMapper::to_address(&ALICE), nonce - 1)
-				);
-			}
-			assert_eq!(System::account_nonce(&ALICE), 6);
-		});
-	}
+/// Check that contracts with the same account id have different trie ids.
+/// Check the `Nonce` storage item for more information.
+#[test]
+fn instantiate_unique_trie_id() {
+	let (wasm, code_hash) = compile_module("self_destruct").unwrap();
 
-	#[test]
-	fn deposit_event_max_value_limit() {
-		let (wasm, _code_hash) = compile_module("event_size").unwrap();
+	ExtBuilder::default().existential_deposit(500).build().execute_with(|| {
+		let _ = <Test as Config>::Currency::set_balance(&ALICE, 1_000_000);
+		Contracts::upload_code(RuntimeOrigin::signed(ALICE), wasm, deposit_limit::<Test>())
+			.unwrap();
 
-		ExtBuilder::default().existential_deposit(50).build().execute_with(|| {
-			// Create
-			let _ = <Test as Config>::Currency::set_balance(&ALICE, 1_000_000);
-			let Contract { addr, .. } = builder::bare_instantiate(Code::Upload(wasm))
-				.value(30_000)
-				.build_and_unwrap_contract();
+		// Instantiate the contract and store its trie id for later comparison.
+		let Contract { addr, .. } =
+			builder::bare_instantiate(Code::Existing(code_hash)).build_and_unwrap_contract();
+		let trie_id = get_contract(&addr).trie_id;
 
-			// Call contract with allowed storage value.
-			assert_ok!(builder::call(addr)
-				.gas_limit(GAS_LIMIT.set_ref_time(GAS_LIMIT.ref_time() * 2)) // we are copying a huge buffer,
-				.data(limits::PAYLOAD_BYTES.encode())
-				.build());
+		// Try to instantiate it again without termination should yield an error.
+		assert_err_ignore_postinfo!(
+			builder::instantiate(code_hash).build(),
+			<Error<Test>>::DuplicateContract,
+		);
 
-			// Call contract with too large a storage value.
-			assert_err_ignore_postinfo!(
-				builder::call(addr).data((limits::PAYLOAD_BYTES + 1).encode()).build(),
-				Error::<Test>::ValueTooLarge,
-			);
-		});
-	}
+		// Terminate the contract.
+		assert_ok!(builder::call(addr).build());
 
-	// Fail out of fuel (ref_time weight) in the engine.
-	#[test]
-	fn run_out_of_fuel_engine() {
-		let (wasm, _code_hash) = compile_module("run_out_of_gas").unwrap();
-		ExtBuilder::default().existential_deposit(50).build().execute_with(|| {
-			let min_balance = Contracts::min_balance();
-			let _ = <Test as Config>::Currency::set_balance(&ALICE, 1_000_000);
+		// Re-Instantiate after termination.
+		assert_ok!(builder::instantiate(code_hash).build());
 
-			let Contract { addr, .. } = builder::bare_instantiate(Code::Upload(wasm))
-				.value(100 * min_balance)
-				.build_and_unwrap_contract();
+		// Trie ids shouldn't match or we might have a collision
+		assert_ne!(trie_id, get_contract(&addr).trie_id);
+	});
+}
 
-			// Call the contract with a fixed gas limit. It must run out of gas because it just
-			// loops forever.
-			assert_err_ignore_postinfo!(
-				builder::call(addr)
-					.gas_limit(Weight::from_parts(10_000_000_000, u64::MAX))
-					.build(),
-				Error::<Test>::OutOfGas,
-			);
-		});
-	}
+#[test]
+fn storage_work() {
+	let (code, _code_hash) = compile_module("storage").unwrap();
 
-	// Fail out of fuel (ref_time weight) in the host.
-	#[test]
-	fn run_out_of_fuel_host() {
-		let (code, _hash) = compile_module("chain_extension").unwrap();
-		ExtBuilder::default().existential_deposit(50).build().execute_with(|| {
-			let min_balance = Contracts::min_balance();
-			let _ = <Test as Config>::Currency::set_balance(&ALICE, 1000 * min_balance);
+	ExtBuilder::default().build().execute_with(|| {
+		let _ = <Test as Config>::Currency::set_balance(&ALICE, 1_000_000);
+		let min_balance = Contracts::min_balance();
+		let Contract { addr, .. } = builder::bare_instantiate(Code::Upload(code))
+			.value(min_balance * 100)
+			.build_and_unwrap_contract();
 
-			let Contract { addr, .. } = builder::bare_instantiate(Code::Upload(code))
-				.value(min_balance * 100)
-				.build_and_unwrap_contract();
+		builder::bare_call(addr).build_and_unwrap_result();
+	});
+}
 
-			let gas_limit = Weight::from_parts(u32::MAX as u64, GAS_LIMIT.proof_size());
+#[test]
+fn storage_max_value_limit() {
+	let (wasm, _code_hash) = compile_module("storage_size").unwrap();
+
+	ExtBuilder::default().existential_deposit(50).build().execute_with(|| {
+		// Create
+		let _ = <Test as Config>::Currency::set_balance(&ALICE, 1_000_000);
+		let Contract { addr, .. } = builder::bare_instantiate(Code::Upload(wasm))
+			.value(30_000)
+			.build_and_unwrap_contract();
+		get_contract(&addr);
+
+		// Call contract with allowed storage value.
+		assert_ok!(builder::call(addr)
+			.gas_limit(GAS_LIMIT.set_ref_time(GAS_LIMIT.ref_time() * 2)) // we are copying a huge buffer
+			.data(limits::PAYLOAD_BYTES.encode())
+			.build());
+
+		// Call contract with too large a storage value.
+		assert_err_ignore_postinfo!(
+			builder::call(addr).data((limits::PAYLOAD_BYTES + 1).encode()).build(),
+			Error::<Test>::ValueTooLarge,
+		);
+	});
+}
 
-			// Use chain extension to charge more ref_time than it is available.
-			let result = builder::bare_call(addr)
-				.gas_limit(gas_limit)
-				.data(
-					ExtensionInput { extension_id: 0, func_id: 2, extra: &u32::MAX.encode() }
-						.into(),
-				)
-				.build()
-				.result;
-			assert_err!(result, <Error<Test>>::OutOfGas);
-		});
-	}
+#[test]
+fn transient_storage_work() {
+	let (code, _code_hash) = compile_module("transient_storage").unwrap();
 
-	#[test]
-	fn gas_syncs_work() {
-		let (code, _code_hash) = compile_module("caller_is_origin_n").unwrap();
-		ExtBuilder::default().existential_deposit(200).build().execute_with(|| {
-			let _ = <Test as Config>::Currency::set_balance(&ALICE, 1_000_000);
-			let contract =
-				builder::bare_instantiate(Code::Upload(code)).build_and_unwrap_contract();
-
-			let result = builder::bare_call(contract.addr).data(0u32.encode()).build();
-			assert_ok!(result.result);
-			let engine_consumed_noop = result.gas_consumed.ref_time();
-
-			let result = builder::bare_call(contract.addr).data(1u32.encode()).build();
-			assert_ok!(result.result);
-			let gas_consumed_once = result.gas_consumed.ref_time();
-			let host_consumed_once =
-				<Test as Config>::WeightInfo::seal_caller_is_origin().ref_time();
-			let engine_consumed_once =
-				gas_consumed_once - host_consumed_once - engine_consumed_noop;
-
-			let result = builder::bare_call(contract.addr).data(2u32.encode()).build();
-			assert_ok!(result.result);
-			let gas_consumed_twice = result.gas_consumed.ref_time();
-			let host_consumed_twice = host_consumed_once * 2;
-			let engine_consumed_twice =
-				gas_consumed_twice - host_consumed_twice - engine_consumed_noop;
-
-			// Second contract just repeats first contract's instructions twice.
-			// If runtime syncs gas with the engine properly, this should pass.
-			assert_eq!(engine_consumed_twice, engine_consumed_once * 2);
-		});
-	}
+	ExtBuilder::default().build().execute_with(|| {
+		let _ = <Test as Config>::Currency::set_balance(&ALICE, 1_000_000);
+		let min_balance = Contracts::min_balance();
+		let Contract { addr, .. } = builder::bare_instantiate(Code::Upload(code))
+			.value(min_balance * 100)
+			.build_and_unwrap_contract();
 
-	/// Check that contracts with the same account id have different trie ids.
-	/// Check the `Nonce` storage item for more information.
-	#[test]
-	fn instantiate_unique_trie_id() {
-		let (wasm, code_hash) = compile_module("self_destruct").unwrap();
+		builder::bare_call(addr).build_and_unwrap_result();
+	});
+}
 
-		ExtBuilder::default().existential_deposit(500).build().execute_with(|| {
-			let _ = <Test as Config>::Currency::set_balance(&ALICE, 1_000_000);
-			Contracts::upload_code(RuntimeOrigin::signed(ALICE), wasm, deposit_limit::<Test>())
-				.unwrap();
+#[test]
+fn transient_storage_limit_in_call() {
+	let (wasm_caller, _code_hash_caller) =
+		compile_module("create_transient_storage_and_call").unwrap();
+	let (wasm_callee, _code_hash_callee) = compile_module("set_transient_storage").unwrap();
+	ExtBuilder::default().build().execute_with(|| {
+		let _ = <Test as Config>::Currency::set_balance(&ALICE, 1_000_000);
+
+		// Create both contracts: Constructors do nothing.
+		let Contract { addr: addr_caller, .. } =
+			builder::bare_instantiate(Code::Upload(wasm_caller)).build_and_unwrap_contract();
+		let Contract { addr: addr_callee, .. } =
+			builder::bare_instantiate(Code::Upload(wasm_callee)).build_and_unwrap_contract();
+
+		// Call contracts with storage values within the limit.
+		// Caller and Callee contracts each set a transient storage value of size 100.
+		assert_ok!(builder::call(addr_caller)
+			.data((100u32, 100u32, &addr_callee).encode())
+			.build(),);
+
+		// Call a contract with a storage value that is too large.
+		// Limit exceeded in the caller contract.
+		assert_err_ignore_postinfo!(
+			builder::call(addr_caller)
+				.data((4u32 * 1024u32, 200u32, &addr_callee).encode())
+				.build(),
+			<Error<Test>>::OutOfTransientStorage,
+		);
 
-			// Instantiate the contract and store its trie id for later comparison.
-			let Contract { addr, .. } =
-				builder::bare_instantiate(Code::Existing(code_hash)).build_and_unwrap_contract();
-			let trie_id = get_contract(&addr).trie_id;
+		// Call a contract with a storage value that is too large.
+		// Limit exceeded in the callee contract.
+		assert_err_ignore_postinfo!(
+			builder::call(addr_caller)
+				.data((50u32, 4 * 1024u32, &addr_callee).encode())
+				.build(),
+			<Error<Test>>::ContractTrapped
+		);
+	});
+}
 
-			// Try to instantiate it again without termination should yield an error.
-			assert_err_ignore_postinfo!(
-				builder::instantiate(code_hash).build(),
-				<Error<Test>>::DuplicateContract,
-			);
+#[test]
+fn deploy_and_call_other_contract() {
+	let (caller_wasm, _caller_code_hash) = compile_module("caller_contract").unwrap();
+	let (callee_wasm, callee_code_hash) = compile_module("return_with_data").unwrap();
 
-			// Terminate the contract.
-			assert_ok!(builder::call(addr).build());
+	ExtBuilder::default().existential_deposit(1).build().execute_with(|| {
+		let min_balance = Contracts::min_balance();
 
-			// Re-Instantiate after termination.
-			assert_ok!(builder::instantiate(code_hash).build());
+		// Create
+		let _ = <Test as Config>::Currency::set_balance(&ALICE, 1_000_000);
+		let Contract { addr: caller_addr, account_id: caller_account } =
+			builder::bare_instantiate(Code::Upload(caller_wasm))
+				.value(100_000)
+				.build_and_unwrap_contract();
 
-			// Trie ids shouldn't match or we might have a collision
-			assert_ne!(trie_id, get_contract(&addr).trie_id);
-		});
-	}
+		let callee_addr = create2(
+			&caller_addr,
+			&callee_wasm,
+			&[0, 1, 34, 51, 68, 85, 102, 119], // hard coded in wasm
+			&[0u8; 32],
+		);
+		let callee_account = <Test as Config>::AddressMapper::to_account_id(&callee_addr);
 
-	#[test]
-	fn storage_work() {
-		let (code, _code_hash) = compile_module("storage").unwrap();
+		Contracts::upload_code(RuntimeOrigin::signed(ALICE), callee_wasm, deposit_limit::<Test>())
+			.unwrap();
 
-		ExtBuilder::default().build().execute_with(|| {
-			let _ = <Test as Config>::Currency::set_balance(&ALICE, 1_000_000);
-			let min_balance = Contracts::min_balance();
-			let Contract { addr, .. } = builder::bare_instantiate(Code::Upload(code))
-				.value(min_balance * 100)
-				.build_and_unwrap_contract();
+		// Drop previous events
+		initialize_block(2);
 
-			builder::bare_call(addr).build_and_unwrap_result();
-		});
-	}
+		// Call BOB contract, which attempts to instantiate and call the callee contract and
+		// makes various assertions on the results from those calls.
+		assert_ok!(builder::call(caller_addr).data(callee_code_hash.as_ref().to_vec()).build());
 
-	#[test]
-	fn storage_max_value_limit() {
-		let (wasm, _code_hash) = compile_module("storage_size").unwrap();
+		assert_eq!(
+			System::events(),
+			vec![
+				EventRecord {
+					phase: Phase::Initialization,
+					event: RuntimeEvent::System(frame_system::Event::NewAccount {
+						account: callee_account.clone()
+					}),
+					topics: vec![],
+				},
+				EventRecord {
+					phase: Phase::Initialization,
+					event: RuntimeEvent::Balances(pallet_balances::Event::Endowed {
+						account: callee_account.clone(),
+						free_balance: min_balance,
+					}),
+					topics: vec![],
+				},
+				EventRecord {
+					phase: Phase::Initialization,
+					event: RuntimeEvent::Balances(pallet_balances::Event::Transfer {
+						from: ALICE,
+						to: callee_account.clone(),
+						amount: min_balance,
+					}),
+					topics: vec![],
+				},
+				EventRecord {
+					phase: Phase::Initialization,
+					event: RuntimeEvent::Balances(pallet_balances::Event::Transfer {
+						from: caller_account.clone(),
+						to: callee_account.clone(),
+						amount: 32768 // hardcoded in wasm
+					}),
+					topics: vec![],
+				},
+				EventRecord {
+					phase: Phase::Initialization,
+					event: RuntimeEvent::Contracts(crate::Event::Instantiated {
+						deployer: caller_addr,
+						contract: callee_addr,
+					}),
+					topics: vec![],
+				},
+				EventRecord {
+					phase: Phase::Initialization,
+					event: RuntimeEvent::Balances(pallet_balances::Event::Transfer {
+						from: caller_account.clone(),
+						to: callee_account.clone(),
+						amount: 32768,
+					}),
+					topics: vec![],
+				},
+				EventRecord {
+					phase: Phase::Initialization,
+					event: RuntimeEvent::Contracts(crate::Event::Called {
+						caller: Origin::from_account_id(caller_account.clone()),
+						contract: callee_addr,
+					}),
+					topics: vec![],
+				},
+				EventRecord {
+					phase: Phase::Initialization,
+					event: RuntimeEvent::Contracts(crate::Event::Called {
+						caller: Origin::from_account_id(ALICE),
+						contract: caller_addr,
+					}),
+					topics: vec![],
+				},
+				EventRecord {
+					phase: Phase::Initialization,
+					event: RuntimeEvent::Contracts(
+						pallet_revive::Event::StorageDepositTransferredAndHeld {
+							from: ALICE_ADDR,
+							to: callee_addr,
+							amount: test_utils::contract_info_storage_deposit(&callee_addr),
+						}
+					),
+					topics: vec![],
+				},
+			]
+		);
+	});
+}
 
-		ExtBuilder::default().existential_deposit(50).build().execute_with(|| {
-			// Create
-			let _ = <Test as Config>::Currency::set_balance(&ALICE, 1_000_000);
-			let Contract { addr, .. } = builder::bare_instantiate(Code::Upload(wasm))
-				.value(30_000)
-				.build_and_unwrap_contract();
-			get_contract(&addr);
-
-			// Call contract with allowed storage value.
-			assert_ok!(builder::call(addr)
-				.gas_limit(GAS_LIMIT.set_ref_time(GAS_LIMIT.ref_time() * 2)) // we are copying a huge buffer
-				.data(limits::PAYLOAD_BYTES.encode())
-				.build());
-
-			// Call contract with too large a storage value.
-			assert_err_ignore_postinfo!(
-				builder::call(addr).data((limits::PAYLOAD_BYTES + 1).encode()).build(),
-				Error::<Test>::ValueTooLarge,
-			);
-		});
-	}
+#[test]
+fn delegate_call() {
+	let (caller_wasm, _caller_code_hash) = compile_module("delegate_call").unwrap();
+	let (callee_wasm, callee_code_hash) = compile_module("delegate_call_lib").unwrap();
 
-	#[test]
-	fn transient_storage_work() {
-		let (code, _code_hash) = compile_module("transient_storage").unwrap();
+	ExtBuilder::default().existential_deposit(500).build().execute_with(|| {
+		let _ = <Test as Config>::Currency::set_balance(&ALICE, 1_000_000);
 
-		ExtBuilder::default().build().execute_with(|| {
-			let _ = <Test as Config>::Currency::set_balance(&ALICE, 1_000_000);
-			let min_balance = Contracts::min_balance();
-			let Contract { addr, .. } = builder::bare_instantiate(Code::Upload(code))
-				.value(min_balance * 100)
+		// Instantiate the 'caller'
+		let Contract { addr: caller_addr, .. } =
+			builder::bare_instantiate(Code::Upload(caller_wasm))
+				.value(300_000)
 				.build_and_unwrap_contract();
+		// Only upload 'callee' code
+		assert_ok!(Contracts::upload_code(RuntimeOrigin::signed(ALICE), callee_wasm, 100_000,));
+
+		assert_ok!(builder::call(caller_addr)
+			.value(1337)
+			.data(callee_code_hash.as_ref().to_vec())
+			.build());
+	});
+}
 
-			builder::bare_call(addr).build_and_unwrap_result();
-		});
-	}
+#[test]
+fn transfer_expendable_cannot_kill_account() {
+	let (wasm, _code_hash) = compile_module("dummy").unwrap();
+	ExtBuilder::default().existential_deposit(200).build().execute_with(|| {
+		let _ = <Test as Config>::Currency::set_balance(&ALICE, 1_000_000);
 
-	#[test]
-	fn transient_storage_limit_in_call() {
-		let (wasm_caller, _code_hash_caller) =
-			compile_module("create_transient_storage_and_call").unwrap();
-		let (wasm_callee, _code_hash_callee) = compile_module("set_transient_storage").unwrap();
-		ExtBuilder::default().build().execute_with(|| {
-			let _ = <Test as Config>::Currency::set_balance(&ALICE, 1_000_000);
+		// Instantiate the BOB contract.
+		let Contract { addr, .. } = builder::bare_instantiate(Code::Upload(wasm))
+			.value(1_000)
+			.build_and_unwrap_contract();
 
-			// Create both contracts: Constructors do nothing.
-			let Contract { addr: addr_caller, .. } =
-				builder::bare_instantiate(Code::Upload(wasm_caller)).build_and_unwrap_contract();
-			let Contract { addr: addr_callee, .. } =
-				builder::bare_instantiate(Code::Upload(wasm_callee)).build_and_unwrap_contract();
-
-			// Call contracts with storage values within the limit.
-			// Caller and Callee contracts each set a transient storage value of size 100.
-			assert_ok!(builder::call(addr_caller)
-				.data((100u32, 100u32, &addr_callee).encode())
-				.build(),);
-
-			// Call a contract with a storage value that is too large.
-			// Limit exceeded in the caller contract.
-			assert_err_ignore_postinfo!(
-				builder::call(addr_caller)
-					.data((4u32 * 1024u32, 200u32, &addr_callee).encode())
-					.build(),
-				<Error<Test>>::OutOfTransientStorage,
-			);
+		// Check that the BOB contract has been instantiated.
+		get_contract(&addr);
 
-			// Call a contract with a storage value that is too large.
-			// Limit exceeded in the callee contract.
-			assert_err_ignore_postinfo!(
-				builder::call(addr_caller)
-					.data((50u32, 4 * 1024u32, &addr_callee).encode())
-					.build(),
-				<Error<Test>>::ContractTrapped
-			);
-		});
-	}
+		let account = <Test as Config>::AddressMapper::to_account_id(&addr);
+		let total_balance = <Test as Config>::Currency::total_balance(&account);
 
-	#[test]
-	fn deploy_and_call_other_contract() {
-		let (caller_wasm, _caller_code_hash) = compile_module("caller_contract").unwrap();
-		let (callee_wasm, callee_code_hash) = compile_module("return_with_data").unwrap();
+		assert_eq!(
+			test_utils::get_balance_on_hold(&HoldReason::StorageDepositReserve.into(), &account),
+			test_utils::contract_info_storage_deposit(&addr)
+		);
 
-		ExtBuilder::default().existential_deposit(1).build().execute_with(|| {
-			let min_balance = Contracts::min_balance();
+		// Some ot the total balance is held, so it can't be transferred.
+		assert_err!(
+			<<Test as Config>::Currency as Mutate<AccountId32>>::transfer(
+				&account,
+				&ALICE,
+				total_balance,
+				Preservation::Expendable,
+			),
+			TokenError::FundsUnavailable,
+		);
 
-			// Create
-			let _ = <Test as Config>::Currency::set_balance(&ALICE, 1_000_000);
-			let Contract { addr: caller_addr, account_id: caller_account } =
-				builder::bare_instantiate(Code::Upload(caller_wasm))
-					.value(100_000)
-					.build_and_unwrap_contract();
+		assert_eq!(<Test as Config>::Currency::total_balance(&account), total_balance);
+	});
+}
 
-			let callee_addr = create2(
-				&caller_addr,
-				&callee_wasm,
-				&[0, 1, 34, 51, 68, 85, 102, 119], // hard coded in wasm
-				&[0u8; 32],
-			);
-			let callee_account = <Test as Config>::AddressMapper::to_account_id(&callee_addr);
+#[test]
+fn cannot_self_destruct_through_draining() {
+	let (wasm, _code_hash) = compile_module("drain").unwrap();
+	ExtBuilder::default().existential_deposit(200).build().execute_with(|| {
+		let _ = <Test as Config>::Currency::set_balance(&ALICE, 1_000_000);
+		let value = 1_000;
+		let min_balance = Contracts::min_balance();
+
+		// Instantiate the BOB contract.
+		let Contract { addr, .. } = builder::bare_instantiate(Code::Upload(wasm))
+			.value(value)
+			.build_and_unwrap_contract();
+		let account = <Test as Config>::AddressMapper::to_account_id(&addr);
+
+		// Check that the BOB contract has been instantiated.
+		get_contract(&addr);
+
+		// Call BOB which makes it send all funds to the zero address
+		// The contract code asserts that the transfer fails with the correct error code
+		assert_ok!(builder::call(addr).build());
+
+		// Make sure the account wasn't remove by sending all free balance away.
+		assert_eq!(
+			<Test as Config>::Currency::total_balance(&account),
+			value + test_utils::contract_info_storage_deposit(&addr) + min_balance,
+		);
+	});
+}
 
-			Contracts::upload_code(
-				RuntimeOrigin::signed(ALICE),
-				callee_wasm,
-				deposit_limit::<Test>(),
-			)
-			.unwrap();
+#[test]
+fn cannot_self_destruct_through_storage_refund_after_price_change() {
+	let (wasm, _code_hash) = compile_module("store_call").unwrap();
+	ExtBuilder::default().existential_deposit(200).build().execute_with(|| {
+		let _ = <Test as Config>::Currency::set_balance(&ALICE, 1_000_000);
+		let min_balance = Contracts::min_balance();
+
+		// Instantiate the BOB contract.
+		let contract = builder::bare_instantiate(Code::Upload(wasm)).build_and_unwrap_contract();
+		let info_deposit = test_utils::contract_info_storage_deposit(&contract.addr);
+
+		// Check that the contract has been instantiated and has the minimum balance
+		assert_eq!(get_contract(&contract.addr).total_deposit(), info_deposit);
+		assert_eq!(get_contract(&contract.addr).extra_deposit(), 0);
+		assert_eq!(
+			<Test as Config>::Currency::total_balance(&contract.account_id),
+			info_deposit + min_balance
+		);
 
-			// Drop previous events
-			initialize_block(2);
+		// Create 100 bytes of storage with a price of per byte and a single storage item of
+		// price 2
+		assert_ok!(builder::call(contract.addr).data(100u32.to_le_bytes().to_vec()).build());
+		assert_eq!(get_contract(&contract.addr).total_deposit(), info_deposit + 102);
+
+		// Increase the byte price and trigger a refund. This should not have any influence
+		// because the removal is pro rata and exactly those 100 bytes should have been
+		// removed.
+		DEPOSIT_PER_BYTE.with(|c| *c.borrow_mut() = 500);
+		assert_ok!(builder::call(contract.addr).data(0u32.to_le_bytes().to_vec()).build());
+
+		// Make sure the account wasn't removed by the refund
+		assert_eq!(
+			<Test as Config>::Currency::total_balance(&contract.account_id),
+			get_contract(&contract.addr).total_deposit() + min_balance,
+		);
+		assert_eq!(get_contract(&contract.addr).extra_deposit(), 2);
+	});
+}
 
-			// Call BOB contract, which attempts to instantiate and call the callee contract and
-			// makes various assertions on the results from those calls.
-			assert_ok!(builder::call(caller_addr).data(callee_code_hash.as_ref().to_vec()).build());
+#[test]
+fn cannot_self_destruct_while_live() {
+	let (wasm, _code_hash) = compile_module("self_destruct").unwrap();
+	ExtBuilder::default().existential_deposit(50).build().execute_with(|| {
+		let _ = <Test as Config>::Currency::set_balance(&ALICE, 1_000_000);
+
+		// Instantiate the BOB contract.
+		let Contract { addr, .. } = builder::bare_instantiate(Code::Upload(wasm))
+			.value(100_000)
+			.build_and_unwrap_contract();
+
+		// Check that the BOB contract has been instantiated.
+		get_contract(&addr);
+
+		// Call BOB with input data, forcing it make a recursive call to itself to
+		// self-destruct, resulting in a trap.
+		assert_err_ignore_postinfo!(
+			builder::call(addr).data(vec![0]).build(),
+			Error::<Test>::ContractTrapped,
+		);
 
-			assert_eq!(
-				System::events(),
-				vec![
-					EventRecord {
-						phase: Phase::Initialization,
-						event: RuntimeEvent::System(frame_system::Event::NewAccount {
-							account: callee_account.clone()
-						}),
-						topics: vec![],
-					},
-					EventRecord {
-						phase: Phase::Initialization,
-						event: RuntimeEvent::Balances(pallet_balances::Event::Endowed {
-							account: callee_account.clone(),
-							free_balance: min_balance,
-						}),
-						topics: vec![],
-					},
-					EventRecord {
-						phase: Phase::Initialization,
-						event: RuntimeEvent::Balances(pallet_balances::Event::Transfer {
-							from: ALICE,
-							to: callee_account.clone(),
-							amount: min_balance,
-						}),
-						topics: vec![],
-					},
-					EventRecord {
-						phase: Phase::Initialization,
-						event: RuntimeEvent::Balances(pallet_balances::Event::Transfer {
-							from: caller_account.clone(),
-							to: callee_account.clone(),
-							amount: 32768 // hardcoded in wasm
-						}),
-						topics: vec![],
-					},
-					EventRecord {
-						phase: Phase::Initialization,
-						event: RuntimeEvent::Contracts(crate::Event::Instantiated {
-							deployer: caller_addr,
-							contract: callee_addr,
-						}),
-						topics: vec![],
-					},
-					EventRecord {
-						phase: Phase::Initialization,
-						event: RuntimeEvent::Balances(pallet_balances::Event::Transfer {
-							from: caller_account.clone(),
-							to: callee_account.clone(),
-							amount: 32768,
-						}),
-						topics: vec![],
-					},
-					EventRecord {
-						phase: Phase::Initialization,
-						event: RuntimeEvent::Contracts(crate::Event::Called {
-							caller: Origin::from_account_id(caller_account.clone()),
-							contract: callee_addr,
-						}),
-						topics: vec![],
-					},
-					EventRecord {
-						phase: Phase::Initialization,
-						event: RuntimeEvent::Contracts(crate::Event::Called {
-							caller: Origin::from_account_id(ALICE),
-							contract: caller_addr,
-						}),
-						topics: vec![],
-					},
-					EventRecord {
-						phase: Phase::Initialization,
-						event: RuntimeEvent::Contracts(
-							pallet_revive::Event::StorageDepositTransferredAndHeld {
-								from: ALICE_ADDR,
-								to: callee_addr,
-								amount: test_utils::contract_info_storage_deposit(&callee_addr),
-							}
-						),
-						topics: vec![],
-					},
-				]
-			);
-		});
-	}
+		// Check that BOB is still there.
+		get_contract(&addr);
+	});
+}
 
-	#[test]
-	fn delegate_call() {
-		let (caller_wasm, _caller_code_hash) = compile_module("delegate_call").unwrap();
-		let (callee_wasm, callee_code_hash) = compile_module("delegate_call_lib").unwrap();
+#[test]
+fn self_destruct_works() {
+	let (wasm, code_hash) = compile_module("self_destruct").unwrap();
+	ExtBuilder::default().existential_deposit(1_000).build().execute_with(|| {
+		let _ = <Test as Config>::Currency::set_balance(&ALICE, 1_000_000);
+		let _ = <Test as Config>::Currency::set_balance(&DJANGO_FALLBACK, 1_000_000);
+		let min_balance = Contracts::min_balance();
 
-		ExtBuilder::default().existential_deposit(500).build().execute_with(|| {
-			let _ = <Test as Config>::Currency::set_balance(&ALICE, 1_000_000);
+		// Instantiate the BOB contract.
+		let contract = builder::bare_instantiate(Code::Upload(wasm))
+			.value(100_000)
+			.build_and_unwrap_contract();
 
-			// Instantiate the 'caller'
-			let Contract { addr: caller_addr, .. } =
-				builder::bare_instantiate(Code::Upload(caller_wasm))
-					.value(300_000)
-					.build_and_unwrap_contract();
-			// Only upload 'callee' code
-			assert_ok!(Contracts::upload_code(RuntimeOrigin::signed(ALICE), callee_wasm, 100_000,));
+		// Check that the BOB contract has been instantiated.
+		let _ = get_contract(&contract.addr);
 
-			assert_ok!(builder::call(caller_addr)
-				.value(1337)
-				.data(callee_code_hash.as_ref().to_vec())
-				.build());
-		});
-	}
+		let info_deposit = test_utils::contract_info_storage_deposit(&contract.addr);
 
-	#[test]
-	fn transfer_expendable_cannot_kill_account() {
-		let (wasm, _code_hash) = compile_module("dummy").unwrap();
-		ExtBuilder::default().existential_deposit(200).build().execute_with(|| {
-			let _ = <Test as Config>::Currency::set_balance(&ALICE, 1_000_000);
+		// Drop all previous events
+		initialize_block(2);
 
-			// Instantiate the BOB contract.
-			let Contract { addr, .. } = builder::bare_instantiate(Code::Upload(wasm))
-				.value(1_000)
-				.build_and_unwrap_contract();
+		// Call BOB without input data which triggers termination.
+		assert_matches!(builder::call(contract.addr).build(), Ok(_));
 
-			// Check that the BOB contract has been instantiated.
-			get_contract(&addr);
+		// Check that code is still there but refcount dropped to zero.
+		assert_refcount!(&code_hash, 0);
 
-			let account = <Test as Config>::AddressMapper::to_account_id(&addr);
-			let total_balance = <Test as Config>::Currency::total_balance(&account);
+		// Check that account is gone
+		assert!(get_contract_checked(&contract.addr).is_none());
+		assert_eq!(<Test as Config>::Currency::total_balance(&contract.account_id), 0);
 
-			assert_eq!(
-				test_utils::get_balance_on_hold(
-					&HoldReason::StorageDepositReserve.into(),
-					&account
-				),
-				test_utils::contract_info_storage_deposit(&addr)
-			);
+		// Check that the beneficiary (django) got remaining balance.
+		assert_eq!(
+			<Test as Config>::Currency::free_balance(DJANGO_FALLBACK),
+			1_000_000 + 100_000 + min_balance
+		);
 
-			// Some ot the total balance is held, so it can't be transferred.
-			assert_err!(
-				<<Test as Config>::Currency as Mutate<AccountId32>>::transfer(
-					&account,
-					&ALICE,
-					total_balance,
-					Preservation::Expendable,
-				),
-				TokenError::FundsUnavailable,
-			);
+		// Check that the Alice is missing Django's benefit. Within ALICE's total balance
+		// there's also the code upload deposit held.
+		assert_eq!(
+			<Test as Config>::Currency::total_balance(&ALICE),
+			1_000_000 - (100_000 + min_balance)
+		);
 
-			assert_eq!(<Test as Config>::Currency::total_balance(&account), total_balance);
-		});
-	}
+		pretty_assertions::assert_eq!(
+			System::events(),
+			vec![
+				EventRecord {
+					phase: Phase::Initialization,
+					event: RuntimeEvent::Contracts(crate::Event::Terminated {
+						contract: contract.addr,
+						beneficiary: DJANGO_ADDR,
+					}),
+					topics: vec![],
+				},
+				EventRecord {
+					phase: Phase::Initialization,
+					event: RuntimeEvent::Contracts(crate::Event::Called {
+						caller: Origin::from_account_id(ALICE),
+						contract: contract.addr,
+					}),
+					topics: vec![],
+				},
+				EventRecord {
+					phase: Phase::Initialization,
+					event: RuntimeEvent::Contracts(
+						pallet_revive::Event::StorageDepositTransferredAndReleased {
+							from: contract.addr,
+							to: ALICE_ADDR,
+							amount: info_deposit,
+						}
+					),
+					topics: vec![],
+				},
+				EventRecord {
+					phase: Phase::Initialization,
+					event: RuntimeEvent::System(frame_system::Event::KilledAccount {
+						account: contract.account_id.clone()
+					}),
+					topics: vec![],
+				},
+				EventRecord {
+					phase: Phase::Initialization,
+					event: RuntimeEvent::Balances(pallet_balances::Event::Transfer {
+						from: contract.account_id.clone(),
+						to: DJANGO_FALLBACK,
+						amount: 100_000 + min_balance,
+					}),
+					topics: vec![],
+				},
+			],
+		);
+	});
+}
 
-	#[test]
-	fn cannot_self_destruct_through_draining() {
-		let (wasm, _code_hash) = compile_module("drain").unwrap();
-		ExtBuilder::default().existential_deposit(200).build().execute_with(|| {
-			let _ = <Test as Config>::Currency::set_balance(&ALICE, 1_000_000);
-			let value = 1_000;
-			let min_balance = Contracts::min_balance();
+// This tests that one contract cannot prevent another from self-destructing by sending it
+// additional funds after it has been drained.
+#[test]
+fn destroy_contract_and_transfer_funds() {
+	let (callee_wasm, callee_code_hash) = compile_module("self_destruct").unwrap();
+	let (caller_wasm, _caller_code_hash) = compile_module("destroy_and_transfer").unwrap();
+
+	ExtBuilder::default().existential_deposit(50).build().execute_with(|| {
+		// Create code hash for bob to instantiate
+		let _ = <Test as Config>::Currency::set_balance(&ALICE, 1_000_000);
+		Contracts::upload_code(
+			RuntimeOrigin::signed(ALICE),
+			callee_wasm.clone(),
+			deposit_limit::<Test>(),
+		)
+		.unwrap();
+
+		// This deploys the BOB contract, which in turn deploys the CHARLIE contract during
+		// construction.
+		let Contract { addr: addr_bob, .. } = builder::bare_instantiate(Code::Upload(caller_wasm))
+			.value(200_000)
+			.data(callee_code_hash.as_ref().to_vec())
+			.build_and_unwrap_contract();
+
+		// Check that the CHARLIE contract has been instantiated.
+		let salt = [47; 32]; // hard coded in fixture.
+		let addr_charlie = create2(&addr_bob, &callee_wasm, &[], &salt);
+		get_contract(&addr_charlie);
+
+		// Call BOB, which calls CHARLIE, forcing CHARLIE to self-destruct.
+		assert_ok!(builder::call(addr_bob).data(addr_charlie.encode()).build());
+
+		// Check that CHARLIE has moved on to the great beyond (ie. died).
+		assert!(get_contract_checked(&addr_charlie).is_none());
+	});
+}
 
-			// Instantiate the BOB contract.
-			let Contract { addr, .. } = builder::bare_instantiate(Code::Upload(wasm))
-				.value(value)
-				.build_and_unwrap_contract();
-			let account = <Test as Config>::AddressMapper::to_account_id(&addr);
+#[test]
+fn cannot_self_destruct_in_constructor() {
+	let (wasm, _) = compile_module("self_destructing_constructor").unwrap();
+	ExtBuilder::default().existential_deposit(50).build().execute_with(|| {
+		let _ = <Test as Config>::Currency::set_balance(&ALICE, 1_000_000);
 
-			// Check that the BOB contract has been instantiated.
-			get_contract(&addr);
+		// Fail to instantiate the BOB because the constructor calls seal_terminate.
+		assert_err_ignore_postinfo!(
+			builder::instantiate_with_code(wasm).value(100_000).build(),
+			Error::<Test>::TerminatedInConstructor,
+		);
+	});
+}
 
-			// Call BOB which makes it send all funds to the zero address
-			// The contract code asserts that the transfer fails with the correct error code
-			assert_ok!(builder::call(addr).build());
+#[test]
+fn crypto_hashes() {
+	let (wasm, _code_hash) = compile_module("crypto_hashes").unwrap();
+
+	ExtBuilder::default().existential_deposit(50).build().execute_with(|| {
+		let _ = <Test as Config>::Currency::set_balance(&ALICE, 1_000_000);
+
+		// Instantiate the CRYPTO_HASHES contract.
+		let Contract { addr, .. } = builder::bare_instantiate(Code::Upload(wasm))
+			.value(100_000)
+			.build_and_unwrap_contract();
+		// Perform the call.
+		let input = b"_DEAD_BEEF";
+		use sp_io::hashing::*;
+		// Wraps a hash function into a more dynamic form usable for testing.
+		macro_rules! dyn_hash_fn {
+			($name:ident) => {
+				Box::new(|input| $name(input).as_ref().to_vec().into_boxed_slice())
+			};
+		}
+		// All hash functions and their associated output byte lengths.
+		let test_cases: &[(Box<dyn Fn(&[u8]) -> Box<[u8]>>, usize)] = &[
+			(dyn_hash_fn!(sha2_256), 32),
+			(dyn_hash_fn!(keccak_256), 32),
+			(dyn_hash_fn!(blake2_256), 32),
+			(dyn_hash_fn!(blake2_128), 16),
+		];
+		// Test the given hash functions for the input: "_DEAD_BEEF"
+		for (n, (hash_fn, expected_size)) in test_cases.iter().enumerate() {
+			// We offset data in the contract tables by 1.
+			let mut params = vec![(n + 1) as u8];
+			params.extend_from_slice(input);
+			let result = builder::bare_call(addr).data(params).build_and_unwrap_result();
+			assert!(!result.did_revert());
+			let expected = hash_fn(input.as_ref());
+			assert_eq!(&result.data[..*expected_size], &*expected);
+		}
+	})
+}
 
-			// Make sure the account wasn't remove by sending all free balance away.
-			assert_eq!(
-				<Test as Config>::Currency::total_balance(&account),
-				value + test_utils::contract_info_storage_deposit(&addr) + min_balance,
-			);
-		});
-	}
+#[test]
+fn transfer_return_code() {
+	let (wasm, _code_hash) = compile_module("transfer_return_code").unwrap();
+	ExtBuilder::default().existential_deposit(50).build().execute_with(|| {
+		let min_balance = Contracts::min_balance();
+		let _ = <Test as Config>::Currency::set_balance(&ALICE, 1000 * min_balance);
+
+		let contract = builder::bare_instantiate(Code::Upload(wasm))
+			.value(min_balance * 100)
+			.build_and_unwrap_contract();
+
+		// Contract has only the minimal balance so any transfer will fail.
+		<Test as Config>::Currency::set_balance(&contract.account_id, min_balance);
+		let result = builder::bare_call(contract.addr).build_and_unwrap_result();
+		assert_return_code!(result, RuntimeReturnCode::TransferFailed);
+	});
+}
 
-	#[test]
-	fn cannot_self_destruct_through_storage_refund_after_price_change() {
-		let (wasm, _code_hash) = compile_module("store_call").unwrap();
-		ExtBuilder::default().existential_deposit(200).build().execute_with(|| {
-			let _ = <Test as Config>::Currency::set_balance(&ALICE, 1_000_000);
-			let min_balance = Contracts::min_balance();
+#[test]
+fn call_return_code() {
+	use test_utils::u256_bytes;
+
+	let (caller_code, _caller_hash) = compile_module("call_return_code").unwrap();
+	let (callee_code, _callee_hash) = compile_module("ok_trap_revert").unwrap();
+	ExtBuilder::default().existential_deposit(50).build().execute_with(|| {
+		let min_balance = Contracts::min_balance();
+		let _ = <Test as Config>::Currency::set_balance(&ALICE, 1000 * min_balance);
+		let _ = <Test as Config>::Currency::set_balance(&CHARLIE, 1000 * min_balance);
+
+		let bob = builder::bare_instantiate(Code::Upload(caller_code))
+			.value(min_balance * 100)
+			.build_and_unwrap_contract();
+
+		// Contract calls into Django which is no valid contract
+		// This will be a balance transfer into a new account
+		// with more than the contract has which will make the transfer fail
+		let result = builder::bare_call(bob.addr)
+			.data(
+				AsRef::<[u8]>::as_ref(&DJANGO_ADDR)
+					.iter()
+					.chain(&u256_bytes(min_balance * 200))
+					.cloned()
+					.collect(),
+			)
+			.build_and_unwrap_result();
+		assert_return_code!(result, RuntimeReturnCode::TransferFailed);
 
-			// Instantiate the BOB contract.
-			let contract =
-				builder::bare_instantiate(Code::Upload(wasm)).build_and_unwrap_contract();
-			let info_deposit = test_utils::contract_info_storage_deposit(&contract.addr);
+		// Sending less than the minimum balance will also make the transfer fail
+		let result = builder::bare_call(bob.addr)
+			.data(
+				AsRef::<[u8]>::as_ref(&DJANGO_ADDR)
+					.iter()
+					.chain(&u256_bytes(42))
+					.cloned()
+					.collect(),
+			)
+			.build_and_unwrap_result();
+		assert_return_code!(result, RuntimeReturnCode::TransferFailed);
+
+		// Sending at least the minimum balance should result in success but
+		// no code called.
+		assert_eq!(test_utils::get_balance(&DJANGO_FALLBACK), 0);
+		let result = builder::bare_call(bob.addr)
+			.data(
+				AsRef::<[u8]>::as_ref(&DJANGO_ADDR)
+					.iter()
+					.chain(&u256_bytes(55))
+					.cloned()
+					.collect(),
+			)
+			.build_and_unwrap_result();
+		assert_return_code!(result, RuntimeReturnCode::Success);
+		assert_eq!(test_utils::get_balance(&DJANGO_FALLBACK), 55);
+
+		let django = builder::bare_instantiate(Code::Upload(callee_code))
+			.origin(RuntimeOrigin::signed(CHARLIE))
+			.value(min_balance * 100)
+			.build_and_unwrap_contract();
+
+		// Sending more than the contract has will make the transfer fail.
+		let result = builder::bare_call(bob.addr)
+			.data(
+				AsRef::<[u8]>::as_ref(&django.addr)
+					.iter()
+					.chain(&u256_bytes(min_balance * 300))
+					.chain(&0u32.to_le_bytes())
+					.cloned()
+					.collect(),
+			)
+			.build_and_unwrap_result();
+		assert_return_code!(result, RuntimeReturnCode::TransferFailed);
+
+		// Contract has enough balance but callee reverts because "1" is passed.
+		<Test as Config>::Currency::set_balance(&bob.account_id, min_balance + 1000);
+		let result = builder::bare_call(bob.addr)
+			.data(
+				AsRef::<[u8]>::as_ref(&django.addr)
+					.iter()
+					.chain(&u256_bytes(5))
+					.chain(&1u32.to_le_bytes())
+					.cloned()
+					.collect(),
+			)
+			.build_and_unwrap_result();
+		assert_return_code!(result, RuntimeReturnCode::CalleeReverted);
 
-			// Check that the contract has been instantiated and has the minimum balance
-			assert_eq!(get_contract(&contract.addr).total_deposit(), info_deposit);
-			assert_eq!(get_contract(&contract.addr).extra_deposit(), 0);
-			assert_eq!(
-				<Test as Config>::Currency::total_balance(&contract.account_id),
-				info_deposit + min_balance
-			);
+		// Contract has enough balance but callee traps because "2" is passed.
+		let result = builder::bare_call(bob.addr)
+			.data(
+				AsRef::<[u8]>::as_ref(&django.addr)
+					.iter()
+					.chain(&u256_bytes(5))
+					.chain(&2u32.to_le_bytes())
+					.cloned()
+					.collect(),
+			)
+			.build_and_unwrap_result();
+		assert_return_code!(result, RuntimeReturnCode::CalleeTrapped);
+	});
+}
 
-			// Create 100 bytes of storage with a price of per byte and a single storage item of
-			// price 2
-			assert_ok!(builder::call(contract.addr).data(100u32.to_le_bytes().to_vec()).build());
-			assert_eq!(get_contract(&contract.addr).total_deposit(), info_deposit + 102);
-
-			// Increase the byte price and trigger a refund. This should not have any influence
-			// because the removal is pro rata and exactly those 100 bytes should have been
-			// removed.
-			DEPOSIT_PER_BYTE.with(|c| *c.borrow_mut() = 500);
-			assert_ok!(builder::call(contract.addr).data(0u32.to_le_bytes().to_vec()).build());
-
-			// Make sure the account wasn't removed by the refund
-			assert_eq!(
-				<Test as Config>::Currency::total_balance(&contract.account_id),
-				get_contract(&contract.addr).total_deposit() + min_balance,
-			);
-			assert_eq!(get_contract(&contract.addr).extra_deposit(), 2);
-		});
-	}
-
-	#[test]
-	fn cannot_self_destruct_while_live() {
-		let (wasm, _code_hash) = compile_module("self_destruct").unwrap();
-		ExtBuilder::default().existential_deposit(50).build().execute_with(|| {
-			let _ = <Test as Config>::Currency::set_balance(&ALICE, 1_000_000);
-
-			// Instantiate the BOB contract.
-			let Contract { addr, .. } = builder::bare_instantiate(Code::Upload(wasm))
-				.value(100_000)
-				.build_and_unwrap_contract();
-
-			// Check that the BOB contract has been instantiated.
-			get_contract(&addr);
-
-			// Call BOB with input data, forcing it make a recursive call to itself to
-			// self-destruct, resulting in a trap.
-			assert_err_ignore_postinfo!(
-				builder::call(addr).data(vec![0]).build(),
-				Error::<Test>::ContractTrapped,
-			);
-
-			// Check that BOB is still there.
-			get_contract(&addr);
-		});
-	}
-
-	#[test]
-	fn self_destruct_works() {
-		let (wasm, code_hash) = compile_module("self_destruct").unwrap();
-		ExtBuilder::default().existential_deposit(1_000).build().execute_with(|| {
-			let _ = <Test as Config>::Currency::set_balance(&ALICE, 1_000_000);
-			let _ = <Test as Config>::Currency::set_balance(&DJANGO_FALLBACK, 1_000_000);
-			let min_balance = Contracts::min_balance();
-
-			// Instantiate the BOB contract.
-			let contract = builder::bare_instantiate(Code::Upload(wasm))
-				.value(100_000)
-				.build_and_unwrap_contract();
-
-			// Check that the BOB contract has been instantiated.
-			let _ = get_contract(&contract.addr);
-
-			let info_deposit = test_utils::contract_info_storage_deposit(&contract.addr);
-
-			// Drop all previous events
-			initialize_block(2);
-
-			// Call BOB without input data which triggers termination.
-			assert_matches!(builder::call(contract.addr).build(), Ok(_));
-
-			// Check that code is still there but refcount dropped to zero.
-			assert_refcount!(&code_hash, 0);
-
-			// Check that account is gone
-			assert!(get_contract_checked(&contract.addr).is_none());
-			assert_eq!(<Test as Config>::Currency::total_balance(&contract.account_id), 0);
-
-			// Check that the beneficiary (django) got remaining balance.
-			assert_eq!(
-				<Test as Config>::Currency::free_balance(DJANGO_FALLBACK),
-				1_000_000 + 100_000 + min_balance
-			);
+#[test]
+fn instantiate_return_code() {
+	let (caller_code, _caller_hash) = compile_module("instantiate_return_code").unwrap();
+	let (callee_code, callee_hash) = compile_module("ok_trap_revert").unwrap();
+	ExtBuilder::default().existential_deposit(50).build().execute_with(|| {
+		let min_balance = Contracts::min_balance();
+		let _ = <Test as Config>::Currency::set_balance(&ALICE, 1000 * min_balance);
+		let _ = <Test as Config>::Currency::set_balance(&CHARLIE, 1000 * min_balance);
+		let callee_hash = callee_hash.as_ref().to_vec();
+
+		assert_ok!(builder::instantiate_with_code(callee_code).value(min_balance * 100).build());
+
+		let contract = builder::bare_instantiate(Code::Upload(caller_code))
+			.value(min_balance * 100)
+			.build_and_unwrap_contract();
+
+		// Contract has only the minimal balance so any transfer will fail.
+		<Test as Config>::Currency::set_balance(&contract.account_id, min_balance);
+		let result = builder::bare_call(contract.addr)
+			.data(callee_hash.clone())
+			.build_and_unwrap_result();
+		assert_return_code!(result, RuntimeReturnCode::TransferFailed);
+
+		// Contract has enough balance but the passed code hash is invalid
+		<Test as Config>::Currency::set_balance(&contract.account_id, min_balance + 10_000);
+		let result = builder::bare_call(contract.addr).data(vec![0; 33]).build_and_unwrap_result();
+		assert_return_code!(result, RuntimeReturnCode::CodeNotFound);
+
+		// Contract has enough balance but callee reverts because "1" is passed.
+		let result = builder::bare_call(contract.addr)
+			.data(callee_hash.iter().chain(&1u32.to_le_bytes()).cloned().collect())
+			.build_and_unwrap_result();
+		assert_return_code!(result, RuntimeReturnCode::CalleeReverted);
+
+		// Contract has enough balance but callee traps because "2" is passed.
+		let result = builder::bare_call(contract.addr)
+			.data(callee_hash.iter().chain(&2u32.to_le_bytes()).cloned().collect())
+			.build_and_unwrap_result();
+		assert_return_code!(result, RuntimeReturnCode::CalleeTrapped);
+	});
+}
 
-			// Check that the Alice is missing Django's benefit. Within ALICE's total balance
-			// there's also the code upload deposit held.
-			assert_eq!(
-				<Test as Config>::Currency::total_balance(&ALICE),
-				1_000_000 - (100_000 + min_balance)
-			);
+#[test]
+fn disabled_chain_extension_errors_on_call() {
+	let (code, _hash) = compile_module("chain_extension").unwrap();
+	ExtBuilder::default().existential_deposit(50).build().execute_with(|| {
+		let min_balance = Contracts::min_balance();
+		let _ = <Test as Config>::Currency::set_balance(&ALICE, 1000 * min_balance);
+		let contract = builder::bare_instantiate(Code::Upload(code))
+			.value(min_balance * 100)
+			.build_and_unwrap_contract();
+		TestExtension::disable();
+		assert_err_ignore_postinfo!(
+			builder::call(contract.addr).data(vec![7u8; 8]).build(),
+			Error::<Test>::NoChainExtension,
+		);
+	});
+}
 
-			pretty_assertions::assert_eq!(
-				System::events(),
-				vec![
-					EventRecord {
-						phase: Phase::Initialization,
-						event: RuntimeEvent::Contracts(crate::Event::Terminated {
-							contract: contract.addr,
-							beneficiary: DJANGO_ADDR,
-						}),
-						topics: vec![],
-					},
-					EventRecord {
-						phase: Phase::Initialization,
-						event: RuntimeEvent::Contracts(crate::Event::Called {
-							caller: Origin::from_account_id(ALICE),
-							contract: contract.addr,
-						}),
-						topics: vec![],
-					},
-					EventRecord {
-						phase: Phase::Initialization,
-						event: RuntimeEvent::Contracts(
-							pallet_revive::Event::StorageDepositTransferredAndReleased {
-								from: contract.addr,
-								to: ALICE_ADDR,
-								amount: info_deposit,
-							}
-						),
-						topics: vec![],
-					},
-					EventRecord {
-						phase: Phase::Initialization,
-						event: RuntimeEvent::System(frame_system::Event::KilledAccount {
-							account: contract.account_id.clone()
-						}),
-						topics: vec![],
-					},
-					EventRecord {
-						phase: Phase::Initialization,
-						event: RuntimeEvent::Balances(pallet_balances::Event::Transfer {
-							from: contract.account_id.clone(),
-							to: DJANGO_FALLBACK,
-							amount: 100_000 + min_balance,
-						}),
-						topics: vec![],
-					},
-				],
-			);
-		});
-	}
+#[test]
+fn chain_extension_works() {
+	let (code, _hash) = compile_module("chain_extension").unwrap();
+	ExtBuilder::default().existential_deposit(50).build().execute_with(|| {
+		let min_balance = Contracts::min_balance();
+		let _ = <Test as Config>::Currency::set_balance(&ALICE, 1000 * min_balance);
+		let contract = builder::bare_instantiate(Code::Upload(code))
+			.value(min_balance * 100)
+			.build_and_unwrap_contract();
+
+		// 0 = read input buffer and pass it through as output
+		let input: Vec<u8> = ExtensionInput { extension_id: 0, func_id: 0, extra: &[99] }.into();
+		let result = builder::bare_call(contract.addr).data(input.clone()).build();
+		assert_eq!(TestExtension::last_seen_buffer(), input);
+		assert_eq!(result.result.unwrap().data, input);
+
+		// 1 = treat inputs as integer primitives and store the supplied integers
+		builder::bare_call(contract.addr)
+			.data(ExtensionInput { extension_id: 0, func_id: 1, extra: &[] }.into())
+			.build_and_unwrap_result();
+		assert_eq!(TestExtension::last_seen_input_len(), 4);
+
+		// 2 = charge some extra weight (amount supplied in the fifth byte)
+		let result = builder::bare_call(contract.addr)
+			.data(ExtensionInput { extension_id: 0, func_id: 2, extra: &0u32.encode() }.into())
+			.build();
+		assert_ok!(result.result);
+		let gas_consumed = result.gas_consumed;
+		let result = builder::bare_call(contract.addr)
+			.data(ExtensionInput { extension_id: 0, func_id: 2, extra: &42u32.encode() }.into())
+			.build();
+		assert_ok!(result.result);
+		assert_eq!(result.gas_consumed.ref_time(), gas_consumed.ref_time() + 42);
+		let result = builder::bare_call(contract.addr)
+			.data(ExtensionInput { extension_id: 0, func_id: 2, extra: &95u32.encode() }.into())
+			.build();
+		assert_ok!(result.result);
+		assert_eq!(result.gas_consumed.ref_time(), gas_consumed.ref_time() + 95);
+
+		// 3 = diverging chain extension call that sets flags to 0x1 and returns a fixed buffer
+		let result = builder::bare_call(contract.addr)
+			.data(ExtensionInput { extension_id: 0, func_id: 3, extra: &[] }.into())
+			.build_and_unwrap_result();
+		assert_eq!(result.flags, ReturnFlags::REVERT);
+		assert_eq!(result.data, vec![42, 99]);
+
+		// diverging to second chain extension that sets flags to 0x1 and returns a fixed buffer
+		// We set the MSB part to 1 (instead of 0) which routes the request into the second
+		// extension
+		let result = builder::bare_call(contract.addr)
+			.data(ExtensionInput { extension_id: 1, func_id: 0, extra: &[] }.into())
+			.build_and_unwrap_result();
+		assert_eq!(result.flags, ReturnFlags::REVERT);
+		assert_eq!(result.data, vec![0x4B, 0x1D]);
+
+		// Diverging to third chain extension that is disabled
+		// We set the MSB part to 2 (instead of 0) which routes the request into the third
+		// extension
+		assert_err_ignore_postinfo!(
+			builder::call(contract.addr)
+				.data(ExtensionInput { extension_id: 2, func_id: 0, extra: &[] }.into())
+				.build(),
+			Error::<Test>::NoChainExtension,
+		);
+	});
+}
 
-	// This tests that one contract cannot prevent another from self-destructing by sending it
-	// additional funds after it has been drained.
-	#[test]
-	fn destroy_contract_and_transfer_funds() {
-		let (callee_wasm, callee_code_hash) = compile_module("self_destruct").unwrap();
-		let (caller_wasm, _caller_code_hash) = compile_module("destroy_and_transfer").unwrap();
+#[test]
+fn chain_extension_temp_storage_works() {
+	let (code, _hash) = compile_module("chain_extension_temp_storage").unwrap();
+	ExtBuilder::default().existential_deposit(50).build().execute_with(|| {
+		let min_balance = Contracts::min_balance();
+		let _ = <Test as Config>::Currency::set_balance(&ALICE, 1000 * min_balance);
+		let contract = builder::bare_instantiate(Code::Upload(code))
+			.value(min_balance * 100)
+			.build_and_unwrap_contract();
+
+		// Call func 0 and func 1 back to back.
+		let stop_recursion = 0u8;
+		let mut input: Vec<u8> = ExtensionInput { extension_id: 3, func_id: 0, extra: &[] }.into();
+		input.extend_from_slice(
+			ExtensionInput { extension_id: 3, func_id: 1, extra: &[stop_recursion] }
+				.to_vec()
+				.as_ref(),
+		);
 
-		ExtBuilder::default().existential_deposit(50).build().execute_with(|| {
-			// Create code hash for bob to instantiate
-			let _ = <Test as Config>::Currency::set_balance(&ALICE, 1_000_000);
-			Contracts::upload_code(
-				RuntimeOrigin::signed(ALICE),
-				callee_wasm.clone(),
-				deposit_limit::<Test>(),
-			)
-			.unwrap();
+		assert_ok!(builder::bare_call(contract.addr).data(input.clone()).build().result);
+	})
+}
 
-			// This deploys the BOB contract, which in turn deploys the CHARLIE contract during
-			// construction.
-			let Contract { addr: addr_bob, .. } =
-				builder::bare_instantiate(Code::Upload(caller_wasm))
-					.value(200_000)
-					.data(callee_code_hash.as_ref().to_vec())
-					.build_and_unwrap_contract();
+#[test]
+fn lazy_removal_works() {
+	let (code, _hash) = compile_module("self_destruct").unwrap();
+	ExtBuilder::default().existential_deposit(50).build().execute_with(|| {
+		let min_balance = Contracts::min_balance();
+		let _ = <Test as Config>::Currency::set_balance(&ALICE, 1000 * min_balance);
 
-			// Check that the CHARLIE contract has been instantiated.
-			let salt = [47; 32]; // hard coded in fixture.
-			let addr_charlie = create2(&addr_bob, &callee_wasm, &[], &salt);
-			get_contract(&addr_charlie);
+		let contract = builder::bare_instantiate(Code::Upload(code))
+			.value(min_balance * 100)
+			.build_and_unwrap_contract();
 
-			// Call BOB, which calls CHARLIE, forcing CHARLIE to self-destruct.
-			assert_ok!(builder::call(addr_bob).data(addr_charlie.encode()).build());
+		let info = get_contract(&contract.addr);
+		let trie = &info.child_trie_info();
 
-			// Check that CHARLIE has moved on to the great beyond (ie. died).
-			assert!(get_contract_checked(&addr_charlie).is_none());
-		});
-	}
+		// Put value into the contracts child trie
+		child::put(trie, &[99], &42);
 
-	#[test]
-	fn cannot_self_destruct_in_constructor() {
-		let (wasm, _) = compile_module("self_destructing_constructor").unwrap();
-		ExtBuilder::default().existential_deposit(50).build().execute_with(|| {
-			let _ = <Test as Config>::Currency::set_balance(&ALICE, 1_000_000);
+		// Terminate the contract
+		assert_ok!(builder::call(contract.addr).build());
 
-			// Fail to instantiate the BOB because the constructor calls seal_terminate.
-			assert_err_ignore_postinfo!(
-				builder::instantiate_with_code(wasm).value(100_000).build(),
-				Error::<Test>::TerminatedInConstructor,
-			);
-		});
-	}
+		// Contract info should be gone
+		assert!(!<ContractInfoOf::<Test>>::contains_key(&contract.addr));
 
-	#[test]
-	fn crypto_hashes() {
-		let (wasm, _code_hash) = compile_module("crypto_hashes").unwrap();
+		// But value should be still there as the lazy removal did not run, yet.
+		assert_matches!(child::get(trie, &[99]), Some(42));
 
-		ExtBuilder::default().existential_deposit(50).build().execute_with(|| {
-			let _ = <Test as Config>::Currency::set_balance(&ALICE, 1_000_000);
+		// Run the lazy removal
+		Contracts::on_idle(System::block_number(), Weight::MAX);
 
-			// Instantiate the CRYPTO_HASHES contract.
-			let Contract { addr, .. } = builder::bare_instantiate(Code::Upload(wasm))
-				.value(100_000)
-				.build_and_unwrap_contract();
-			// Perform the call.
-			let input = b"_DEAD_BEEF";
-			use sp_io::hashing::*;
-			// Wraps a hash function into a more dynamic form usable for testing.
-			macro_rules! dyn_hash_fn {
-				($name:ident) => {
-					Box::new(|input| $name(input).as_ref().to_vec().into_boxed_slice())
-				};
-			}
-			// All hash functions and their associated output byte lengths.
-			let test_cases: &[(Box<dyn Fn(&[u8]) -> Box<[u8]>>, usize)] = &[
-				(dyn_hash_fn!(sha2_256), 32),
-				(dyn_hash_fn!(keccak_256), 32),
-				(dyn_hash_fn!(blake2_256), 32),
-				(dyn_hash_fn!(blake2_128), 16),
-			];
-			// Test the given hash functions for the input: "_DEAD_BEEF"
-			for (n, (hash_fn, expected_size)) in test_cases.iter().enumerate() {
-				// We offset data in the contract tables by 1.
-				let mut params = vec![(n + 1) as u8];
-				params.extend_from_slice(input);
-				let result = builder::bare_call(addr).data(params).build_and_unwrap_result();
-				assert!(!result.did_revert());
-				let expected = hash_fn(input.as_ref());
-				assert_eq!(&result.data[..*expected_size], &*expected);
-			}
-		})
-	}
+		// Value should be gone now
+		assert_matches!(child::get::<i32>(trie, &[99]), None);
+	});
+}
 
-	#[test]
-	fn transfer_return_code() {
-		let (wasm, _code_hash) = compile_module("transfer_return_code").unwrap();
-		ExtBuilder::default().existential_deposit(50).build().execute_with(|| {
-			let min_balance = Contracts::min_balance();
-			let _ = <Test as Config>::Currency::set_balance(&ALICE, 1000 * min_balance);
+#[test]
+fn lazy_batch_removal_works() {
+	let (code, _hash) = compile_module("self_destruct").unwrap();
+	ExtBuilder::default().existential_deposit(50).build().execute_with(|| {
+		let min_balance = Contracts::min_balance();
+		let _ = <Test as Config>::Currency::set_balance(&ALICE, 1000 * min_balance);
+		let mut tries: Vec<child::ChildInfo> = vec![];
 
-			let contract = builder::bare_instantiate(Code::Upload(wasm))
+		for i in 0..3u8 {
+			let contract = builder::bare_instantiate(Code::Upload(code.clone()))
 				.value(min_balance * 100)
+				.salt(Some([i; 32]))
 				.build_and_unwrap_contract();
 
-			// Contract has only the minimal balance so any transfer will fail.
-			<Test as Config>::Currency::set_balance(&contract.account_id, min_balance);
-			let result = builder::bare_call(contract.addr).build_and_unwrap_result();
-			assert_return_code!(result, RuntimeReturnCode::TransferFailed);
-		});
-	}
+			let info = get_contract(&contract.addr);
+			let trie = &info.child_trie_info();
 
-	#[test]
-	fn call_return_code() {
-		use test_utils::u256_bytes;
+			// Put value into the contracts child trie
+			child::put(trie, &[99], &42);
 
-		let (caller_code, _caller_hash) = compile_module("call_return_code").unwrap();
-		let (callee_code, _callee_hash) = compile_module("ok_trap_revert").unwrap();
-		ExtBuilder::default().existential_deposit(50).build().execute_with(|| {
-			let min_balance = Contracts::min_balance();
-			let _ = <Test as Config>::Currency::set_balance(&ALICE, 1000 * min_balance);
-			let _ = <Test as Config>::Currency::set_balance(&CHARLIE, 1000 * min_balance);
+			// Terminate the contract. Contract info should be gone, but value should be still
+			// there as the lazy removal did not run, yet.
+			assert_ok!(builder::call(contract.addr).build());
 
-			let bob = builder::bare_instantiate(Code::Upload(caller_code))
-				.value(min_balance * 100)
-				.build_and_unwrap_contract();
+			assert!(!<ContractInfoOf::<Test>>::contains_key(&contract.addr));
+			assert_matches!(child::get(trie, &[99]), Some(42));
 
-			// Contract calls into Django which is no valid contract
-			// This will be a balance transfer into a new account
-			// with more than the contract has which will make the transfer fail
-			let result = builder::bare_call(bob.addr)
-				.data(
-					AsRef::<[u8]>::as_ref(&DJANGO_ADDR)
-						.iter()
-						.chain(&u256_bytes(min_balance * 200))
-						.cloned()
-						.collect(),
-				)
-				.build_and_unwrap_result();
-			assert_return_code!(result, RuntimeReturnCode::TransferFailed);
+			tries.push(trie.clone())
+		}
 
-			// Sending less than the minimum balance will also make the transfer fail
-			let result = builder::bare_call(bob.addr)
-				.data(
-					AsRef::<[u8]>::as_ref(&DJANGO_ADDR)
-						.iter()
-						.chain(&u256_bytes(42))
-						.cloned()
-						.collect(),
-				)
-				.build_and_unwrap_result();
-			assert_return_code!(result, RuntimeReturnCode::TransferFailed);
+		// Run single lazy removal
+		Contracts::on_idle(System::block_number(), Weight::MAX);
 
-			// Sending at least the minimum balance should result in success but
-			// no code called.
-			assert_eq!(test_utils::get_balance(&DJANGO_FALLBACK), 0);
-			let result = builder::bare_call(bob.addr)
-				.data(
-					AsRef::<[u8]>::as_ref(&DJANGO_ADDR)
-						.iter()
-						.chain(&u256_bytes(55))
-						.cloned()
-						.collect(),
-				)
-				.build_and_unwrap_result();
-			assert_return_code!(result, RuntimeReturnCode::Success);
-			assert_eq!(test_utils::get_balance(&DJANGO_FALLBACK), 55);
+		// The single lazy removal should have removed all queued tries
+		for trie in tries.iter() {
+			assert_matches!(child::get::<i32>(trie, &[99]), None);
+		}
+	});
+}
 
-			let django = builder::bare_instantiate(Code::Upload(callee_code))
-				.origin(RuntimeOrigin::signed(CHARLIE))
-				.value(min_balance * 100)
-				.build_and_unwrap_contract();
+#[test]
+fn lazy_removal_partial_remove_works() {
+	let (code, _hash) = compile_module("self_destruct").unwrap();
 
-			// Sending more than the contract has will make the transfer fail.
-			let result = builder::bare_call(bob.addr)
-				.data(
-					AsRef::<[u8]>::as_ref(&django.addr)
-						.iter()
-						.chain(&u256_bytes(min_balance * 300))
-						.chain(&0u32.to_le_bytes())
-						.cloned()
-						.collect(),
-				)
-				.build_and_unwrap_result();
-			assert_return_code!(result, RuntimeReturnCode::TransferFailed);
+	// We create a contract with some extra keys above the weight limit
+	let extra_keys = 7u32;
+	let mut meter = WeightMeter::with_limit(Weight::from_parts(5_000_000_000, 100 * 1024));
+	let (weight_per_key, max_keys) = ContractInfo::<Test>::deletion_budget(&meter);
+	let vals: Vec<_> = (0..max_keys + extra_keys)
+		.map(|i| (blake2_256(&i.encode()), (i as u32), (i as u32).encode()))
+		.collect();
 
-			// Contract has enough balance but callee reverts because "1" is passed.
-			<Test as Config>::Currency::set_balance(&bob.account_id, min_balance + 1000);
-			let result = builder::bare_call(bob.addr)
-				.data(
-					AsRef::<[u8]>::as_ref(&django.addr)
-						.iter()
-						.chain(&u256_bytes(5))
-						.chain(&1u32.to_le_bytes())
-						.cloned()
-						.collect(),
-				)
-				.build_and_unwrap_result();
-			assert_return_code!(result, RuntimeReturnCode::CalleeReverted);
+	let mut ext = ExtBuilder::default().existential_deposit(50).build();
 
-			// Contract has enough balance but callee traps because "2" is passed.
-			let result = builder::bare_call(bob.addr)
-				.data(
-					AsRef::<[u8]>::as_ref(&django.addr)
-						.iter()
-						.chain(&u256_bytes(5))
-						.chain(&2u32.to_le_bytes())
-						.cloned()
-						.collect(),
-				)
-				.build_and_unwrap_result();
-			assert_return_code!(result, RuntimeReturnCode::CalleeTrapped);
-		});
-	}
+	let trie = ext.execute_with(|| {
+		let min_balance = Contracts::min_balance();
+		let _ = <Test as Config>::Currency::set_balance(&ALICE, 1000 * min_balance);
 
-	#[test]
-	fn instantiate_return_code() {
-		let (caller_code, _caller_hash) = compile_module("instantiate_return_code").unwrap();
-		let (callee_code, callee_hash) = compile_module("ok_trap_revert").unwrap();
-		ExtBuilder::default().existential_deposit(50).build().execute_with(|| {
-			let min_balance = Contracts::min_balance();
-			let _ = <Test as Config>::Currency::set_balance(&ALICE, 1000 * min_balance);
-			let _ = <Test as Config>::Currency::set_balance(&CHARLIE, 1000 * min_balance);
-			let callee_hash = callee_hash.as_ref().to_vec();
-
-			assert_ok!(builder::instantiate_with_code(callee_code)
-				.value(min_balance * 100)
-				.build());
+		let Contract { addr, .. } = builder::bare_instantiate(Code::Upload(code))
+			.value(min_balance * 100)
+			.build_and_unwrap_contract();
 
-			let contract = builder::bare_instantiate(Code::Upload(caller_code))
-				.value(min_balance * 100)
-				.build_and_unwrap_contract();
+		let info = get_contract(&addr);
 
-			// Contract has only the minimal balance so any transfer will fail.
-			<Test as Config>::Currency::set_balance(&contract.account_id, min_balance);
-			let result = builder::bare_call(contract.addr)
-				.data(callee_hash.clone())
-				.build_and_unwrap_result();
-			assert_return_code!(result, RuntimeReturnCode::TransferFailed);
+		// Put value into the contracts child trie
+		for val in &vals {
+			info.write(&Key::Fix(val.0), Some(val.2.clone()), None, false).unwrap();
+		}
+		<ContractInfoOf<Test>>::insert(&addr, info.clone());
 
-			// Contract has enough balance but the passed code hash is invalid
-			<Test as Config>::Currency::set_balance(&contract.account_id, min_balance + 10_000);
-			let result =
-				builder::bare_call(contract.addr).data(vec![0; 33]).build_and_unwrap_result();
-			assert_return_code!(result, RuntimeReturnCode::CodeNotFound);
+		// Terminate the contract
+		assert_ok!(builder::call(addr).build());
 
-			// Contract has enough balance but callee reverts because "1" is passed.
-			let result = builder::bare_call(contract.addr)
-				.data(callee_hash.iter().chain(&1u32.to_le_bytes()).cloned().collect())
-				.build_and_unwrap_result();
-			assert_return_code!(result, RuntimeReturnCode::CalleeReverted);
+		// Contract info should be gone
+		assert!(!<ContractInfoOf::<Test>>::contains_key(&addr));
 
-			// Contract has enough balance but callee traps because "2" is passed.
-			let result = builder::bare_call(contract.addr)
-				.data(callee_hash.iter().chain(&2u32.to_le_bytes()).cloned().collect())
-				.build_and_unwrap_result();
-			assert_return_code!(result, RuntimeReturnCode::CalleeTrapped);
-		});
-	}
+		let trie = info.child_trie_info();
 
-	#[test]
-	fn disabled_chain_extension_errors_on_call() {
-		let (code, _hash) = compile_module("chain_extension").unwrap();
-		ExtBuilder::default().existential_deposit(50).build().execute_with(|| {
-			let min_balance = Contracts::min_balance();
-			let _ = <Test as Config>::Currency::set_balance(&ALICE, 1000 * min_balance);
-			let contract = builder::bare_instantiate(Code::Upload(code))
-				.value(min_balance * 100)
-				.build_and_unwrap_contract();
-			TestExtension::disable();
-			assert_err_ignore_postinfo!(
-				builder::call(contract.addr).data(vec![7u8; 8]).build(),
-				Error::<Test>::NoChainExtension,
-			);
-		});
-	}
+		// But value should be still there as the lazy removal did not run, yet.
+		for val in &vals {
+			assert_eq!(child::get::<u32>(&trie, &blake2_256(&val.0)), Some(val.1));
+		}
 
-	#[test]
-	fn chain_extension_works() {
-		let (code, _hash) = compile_module("chain_extension").unwrap();
-		ExtBuilder::default().existential_deposit(50).build().execute_with(|| {
-			let min_balance = Contracts::min_balance();
-			let _ = <Test as Config>::Currency::set_balance(&ALICE, 1000 * min_balance);
-			let contract = builder::bare_instantiate(Code::Upload(code))
-				.value(min_balance * 100)
-				.build_and_unwrap_contract();
+		trie.clone()
+	});
 
-			// 0 = read input buffer and pass it through as output
-			let input: Vec<u8> =
-				ExtensionInput { extension_id: 0, func_id: 0, extra: &[99] }.into();
-			let result = builder::bare_call(contract.addr).data(input.clone()).build();
-			assert_eq!(TestExtension::last_seen_buffer(), input);
-			assert_eq!(result.result.unwrap().data, input);
+	// The lazy removal limit only applies to the backend but not to the overlay.
+	// This commits all keys from the overlay to the backend.
+	ext.commit_all().unwrap();
 
-			// 1 = treat inputs as integer primitives and store the supplied integers
-			builder::bare_call(contract.addr)
-				.data(ExtensionInput { extension_id: 0, func_id: 1, extra: &[] }.into())
-				.build_and_unwrap_result();
-			assert_eq!(TestExtension::last_seen_input_len(), 4);
-
-			// 2 = charge some extra weight (amount supplied in the fifth byte)
-			let result = builder::bare_call(contract.addr)
-				.data(ExtensionInput { extension_id: 0, func_id: 2, extra: &0u32.encode() }.into())
-				.build();
-			assert_ok!(result.result);
-			let gas_consumed = result.gas_consumed;
-			let result = builder::bare_call(contract.addr)
-				.data(ExtensionInput { extension_id: 0, func_id: 2, extra: &42u32.encode() }.into())
-				.build();
-			assert_ok!(result.result);
-			assert_eq!(result.gas_consumed.ref_time(), gas_consumed.ref_time() + 42);
-			let result = builder::bare_call(contract.addr)
-				.data(ExtensionInput { extension_id: 0, func_id: 2, extra: &95u32.encode() }.into())
-				.build();
-			assert_ok!(result.result);
-			assert_eq!(result.gas_consumed.ref_time(), gas_consumed.ref_time() + 95);
-
-			// 3 = diverging chain extension call that sets flags to 0x1 and returns a fixed buffer
-			let result = builder::bare_call(contract.addr)
-				.data(ExtensionInput { extension_id: 0, func_id: 3, extra: &[] }.into())
-				.build_and_unwrap_result();
-			assert_eq!(result.flags, ReturnFlags::REVERT);
-			assert_eq!(result.data, vec![42, 99]);
-
-			// diverging to second chain extension that sets flags to 0x1 and returns a fixed buffer
-			// We set the MSB part to 1 (instead of 0) which routes the request into the second
-			// extension
-			let result = builder::bare_call(contract.addr)
-				.data(ExtensionInput { extension_id: 1, func_id: 0, extra: &[] }.into())
-				.build_and_unwrap_result();
-			assert_eq!(result.flags, ReturnFlags::REVERT);
-			assert_eq!(result.data, vec![0x4B, 0x1D]);
-
-			// Diverging to third chain extension that is disabled
-			// We set the MSB part to 2 (instead of 0) which routes the request into the third
-			// extension
-			assert_err_ignore_postinfo!(
-				builder::call(contract.addr)
-					.data(ExtensionInput { extension_id: 2, func_id: 0, extra: &[] }.into())
-					.build(),
-				Error::<Test>::NoChainExtension,
-			);
-		});
-	}
+	ext.execute_with(|| {
+		// Run the lazy removal
+		ContractInfo::<Test>::process_deletion_queue_batch(&mut meter);
 
-	#[test]
-	fn chain_extension_temp_storage_works() {
-		let (code, _hash) = compile_module("chain_extension_temp_storage").unwrap();
-		ExtBuilder::default().existential_deposit(50).build().execute_with(|| {
-			let min_balance = Contracts::min_balance();
-			let _ = <Test as Config>::Currency::set_balance(&ALICE, 1000 * min_balance);
-			let contract = builder::bare_instantiate(Code::Upload(code))
-				.value(min_balance * 100)
-				.build_and_unwrap_contract();
+		// Weight should be exhausted because we could not even delete all keys
+		assert!(!meter.can_consume(weight_per_key));
 
-			// Call func 0 and func 1 back to back.
-			let stop_recursion = 0u8;
-			let mut input: Vec<u8> =
-				ExtensionInput { extension_id: 3, func_id: 0, extra: &[] }.into();
-			input.extend_from_slice(
-				ExtensionInput { extension_id: 3, func_id: 1, extra: &[stop_recursion] }
-					.to_vec()
-					.as_ref(),
-			);
+		let mut num_deleted = 0u32;
+		let mut num_remaining = 0u32;
 
-			assert_ok!(builder::bare_call(contract.addr).data(input.clone()).build().result);
-		})
-	}
+		for val in &vals {
+			match child::get::<u32>(&trie, &blake2_256(&val.0)) {
+				None => num_deleted += 1,
+				Some(x) if x == val.1 => num_remaining += 1,
+				Some(_) => panic!("Unexpected value in contract storage"),
+			}
+		}
 
-	#[test]
-	fn lazy_removal_works() {
-		let (code, _hash) = compile_module("self_destruct").unwrap();
-		ExtBuilder::default().existential_deposit(50).build().execute_with(|| {
-			let min_balance = Contracts::min_balance();
-			let _ = <Test as Config>::Currency::set_balance(&ALICE, 1000 * min_balance);
+		// All but one key is removed
+		assert_eq!(num_deleted + num_remaining, vals.len() as u32);
+		assert_eq!(num_deleted, max_keys);
+		assert_eq!(num_remaining, extra_keys);
+	});
+}
 
-			let contract = builder::bare_instantiate(Code::Upload(code))
-				.value(min_balance * 100)
-				.build_and_unwrap_contract();
+#[test]
+fn lazy_removal_does_no_run_on_low_remaining_weight() {
+	let (code, _hash) = compile_module("self_destruct").unwrap();
+	ExtBuilder::default().existential_deposit(50).build().execute_with(|| {
+		let min_balance = Contracts::min_balance();
+		let _ = <Test as Config>::Currency::set_balance(&ALICE, 1000 * min_balance);
 
-			let info = get_contract(&contract.addr);
-			let trie = &info.child_trie_info();
+		let Contract { addr, .. } = builder::bare_instantiate(Code::Upload(code))
+			.value(min_balance * 100)
+			.build_and_unwrap_contract();
 
-			// Put value into the contracts child trie
-			child::put(trie, &[99], &42);
+		let info = get_contract(&addr);
+		let trie = &info.child_trie_info();
 
-			// Terminate the contract
-			assert_ok!(builder::call(contract.addr).build());
+		// Put value into the contracts child trie
+		child::put(trie, &[99], &42);
 
-			// Contract info should be gone
-			assert!(!<ContractInfoOf::<Test>>::contains_key(&contract.addr));
+		// Terminate the contract
+		assert_ok!(builder::call(addr).build());
 
-			// But value should be still there as the lazy removal did not run, yet.
-			assert_matches!(child::get(trie, &[99]), Some(42));
+		// Contract info should be gone
+		assert!(!<ContractInfoOf::<Test>>::contains_key(&addr));
 
-			// Run the lazy removal
-			Contracts::on_idle(System::block_number(), Weight::MAX);
+		// But value should be still there as the lazy removal did not run, yet.
+		assert_matches!(child::get(trie, &[99]), Some(42));
 
-			// Value should be gone now
-			assert_matches!(child::get::<i32>(trie, &[99]), None);
-		});
-	}
+		// Assign a remaining weight which is too low for a successful deletion of the contract
+		let low_remaining_weight =
+			<<Test as Config>::WeightInfo as WeightInfo>::on_process_deletion_queue_batch();
 
-	#[test]
-	fn lazy_batch_removal_works() {
-		let (code, _hash) = compile_module("self_destruct").unwrap();
-		ExtBuilder::default().existential_deposit(50).build().execute_with(|| {
-			let min_balance = Contracts::min_balance();
-			let _ = <Test as Config>::Currency::set_balance(&ALICE, 1000 * min_balance);
-			let mut tries: Vec<child::ChildInfo> = vec![];
-
-			for i in 0..3u8 {
-				let contract = builder::bare_instantiate(Code::Upload(code.clone()))
-					.value(min_balance * 100)
-					.salt(Some([i; 32]))
-					.build_and_unwrap_contract();
+		// Run the lazy removal
+		Contracts::on_idle(System::block_number(), low_remaining_weight);
 
-				let info = get_contract(&contract.addr);
-				let trie = &info.child_trie_info();
+		// Value should still be there, since remaining weight was too low for removal
+		assert_matches!(child::get::<i32>(trie, &[99]), Some(42));
 
-				// Put value into the contracts child trie
-				child::put(trie, &[99], &42);
+		// Run the lazy removal while deletion_queue is not full
+		Contracts::on_initialize(System::block_number());
 
-				// Terminate the contract. Contract info should be gone, but value should be still
-				// there as the lazy removal did not run, yet.
-				assert_ok!(builder::call(contract.addr).build());
+		// Value should still be there, since deletion_queue was not full
+		assert_matches!(child::get::<i32>(trie, &[99]), Some(42));
 
-				assert!(!<ContractInfoOf::<Test>>::contains_key(&contract.addr));
-				assert_matches!(child::get(trie, &[99]), Some(42));
+		// Run on_idle with max remaining weight, this should remove the value
+		Contracts::on_idle(System::block_number(), Weight::MAX);
 
-				tries.push(trie.clone())
-			}
+		// Value should be gone
+		assert_matches!(child::get::<i32>(trie, &[99]), None);
+	});
+}
 
-			// Run single lazy removal
-			Contracts::on_idle(System::block_number(), Weight::MAX);
+#[test]
+fn lazy_removal_does_not_use_all_weight() {
+	let (code, _hash) = compile_module("self_destruct").unwrap();
 
-			// The single lazy removal should have removed all queued tries
-			for trie in tries.iter() {
-				assert_matches!(child::get::<i32>(trie, &[99]), None);
-			}
-		});
-	}
+	let mut meter = WeightMeter::with_limit(Weight::from_parts(5_000_000_000, 100 * 1024));
+	let mut ext = ExtBuilder::default().existential_deposit(50).build();
 
-	#[test]
-	fn lazy_removal_partial_remove_works() {
-		let (code, _hash) = compile_module("self_destruct").unwrap();
+	let (trie, vals, weight_per_key) = ext.execute_with(|| {
+		let min_balance = Contracts::min_balance();
+		let _ = <Test as Config>::Currency::set_balance(&ALICE, 1000 * min_balance);
 
-		// We create a contract with some extra keys above the weight limit
-		let extra_keys = 7u32;
-		let mut meter = WeightMeter::with_limit(Weight::from_parts(5_000_000_000, 100 * 1024));
+		let Contract { addr, .. } = builder::bare_instantiate(Code::Upload(code))
+			.value(min_balance * 100)
+			.build_and_unwrap_contract();
+
+		let info = get_contract(&addr);
 		let (weight_per_key, max_keys) = ContractInfo::<Test>::deletion_budget(&meter);
-		let vals: Vec<_> = (0..max_keys + extra_keys)
+		assert!(max_keys > 0);
+
+		// We create a contract with one less storage item than we can remove within the limit
+		let vals: Vec<_> = (0..max_keys - 1)
 			.map(|i| (blake2_256(&i.encode()), (i as u32), (i as u32).encode()))
 			.collect();
 
-		let mut ext = ExtBuilder::default().existential_deposit(50).build();
-
-		let trie = ext.execute_with(|| {
-			let min_balance = Contracts::min_balance();
-			let _ = <Test as Config>::Currency::set_balance(&ALICE, 1000 * min_balance);
-
-			let Contract { addr, .. } = builder::bare_instantiate(Code::Upload(code))
-				.value(min_balance * 100)
-				.build_and_unwrap_contract();
-
-			let info = get_contract(&addr);
-
-			// Put value into the contracts child trie
-			for val in &vals {
-				info.write(&Key::Fix(val.0), Some(val.2.clone()), None, false).unwrap();
-			}
-			<ContractInfoOf<Test>>::insert(&addr, info.clone());
+		// Put value into the contracts child trie
+		for val in &vals {
+			info.write(&Key::Fix(val.0), Some(val.2.clone()), None, false).unwrap();
+		}
+		<ContractInfoOf<Test>>::insert(&addr, info.clone());
 
-			// Terminate the contract
-			assert_ok!(builder::call(addr).build());
+		// Terminate the contract
+		assert_ok!(builder::call(addr).build());
 
-			// Contract info should be gone
-			assert!(!<ContractInfoOf::<Test>>::contains_key(&addr));
+		// Contract info should be gone
+		assert!(!<ContractInfoOf::<Test>>::contains_key(&addr));
 
-			let trie = info.child_trie_info();
+		let trie = info.child_trie_info();
 
-			// But value should be still there as the lazy removal did not run, yet.
-			for val in &vals {
-				assert_eq!(child::get::<u32>(&trie, &blake2_256(&val.0)), Some(val.1));
-			}
+		// But value should be still there as the lazy removal did not run, yet.
+		for val in &vals {
+			assert_eq!(child::get::<u32>(&trie, &blake2_256(&val.0)), Some(val.1));
+		}
 
-			trie.clone()
-		});
+		(trie, vals, weight_per_key)
+	});
 
-		// The lazy removal limit only applies to the backend but not to the overlay.
-		// This commits all keys from the overlay to the backend.
-		ext.commit_all().unwrap();
+	// The lazy removal limit only applies to the backend but not to the overlay.
+	// This commits all keys from the overlay to the backend.
+	ext.commit_all().unwrap();
 
-		ext.execute_with(|| {
-			// Run the lazy removal
-			ContractInfo::<Test>::process_deletion_queue_batch(&mut meter);
+	ext.execute_with(|| {
+		// Run the lazy removal
+		ContractInfo::<Test>::process_deletion_queue_batch(&mut meter);
+		let base_weight =
+			<<Test as Config>::WeightInfo as WeightInfo>::on_process_deletion_queue_batch();
+		assert_eq!(meter.consumed(), weight_per_key.mul(vals.len() as _) + base_weight);
 
-			// Weight should be exhausted because we could not even delete all keys
-			assert!(!meter.can_consume(weight_per_key));
+		// All the keys are removed
+		for val in vals {
+			assert_eq!(child::get::<u32>(&trie, &blake2_256(&val.0)), None);
+		}
+	});
+}
 
-			let mut num_deleted = 0u32;
-			let mut num_remaining = 0u32;
+#[test]
+fn deletion_queue_ring_buffer_overflow() {
+	let (code, _hash) = compile_module("self_destruct").unwrap();
+	let mut ext = ExtBuilder::default().existential_deposit(50).build();
 
-			for val in &vals {
-				match child::get::<u32>(&trie, &blake2_256(&val.0)) {
-					None => num_deleted += 1,
-					Some(x) if x == val.1 => num_remaining += 1,
-					Some(_) => panic!("Unexpected value in contract storage"),
-				}
-			}
+	// setup the deletion queue with custom counters
+	ext.execute_with(|| {
+		let queue = DeletionQueueManager::from_test_values(u32::MAX - 1, u32::MAX - 1);
+		<DeletionQueueCounter<Test>>::set(queue);
+	});
 
-			// All but one key is removed
-			assert_eq!(num_deleted + num_remaining, vals.len() as u32);
-			assert_eq!(num_deleted, max_keys);
-			assert_eq!(num_remaining, extra_keys);
-		});
-	}
+	// commit the changes to the storage
+	ext.commit_all().unwrap();
 
-	#[test]
-	fn lazy_removal_does_no_run_on_low_remaining_weight() {
-		let (code, _hash) = compile_module("self_destruct").unwrap();
-		ExtBuilder::default().existential_deposit(50).build().execute_with(|| {
-			let min_balance = Contracts::min_balance();
-			let _ = <Test as Config>::Currency::set_balance(&ALICE, 1000 * min_balance);
+	ext.execute_with(|| {
+		let min_balance = Contracts::min_balance();
+		let _ = <Test as Config>::Currency::set_balance(&ALICE, 1000 * min_balance);
+		let mut tries: Vec<child::ChildInfo> = vec![];
 
-			let Contract { addr, .. } = builder::bare_instantiate(Code::Upload(code))
+		// add 3 contracts to the deletion queue
+		for i in 0..3u8 {
+			let Contract { addr, .. } = builder::bare_instantiate(Code::Upload(code.clone()))
 				.value(min_balance * 100)
+				.salt(Some([i; 32]))
 				.build_and_unwrap_contract();
 
 			let info = get_contract(&addr);
@@ -1926,669 +2026,816 @@ mod run_tests {
 			// Put value into the contracts child trie
 			child::put(trie, &[99], &42);
 
-			// Terminate the contract
+			// Terminate the contract. Contract info should be gone, but value should be still
+			// there as the lazy removal did not run, yet.
 			assert_ok!(builder::call(addr).build());
 
-			// Contract info should be gone
 			assert!(!<ContractInfoOf::<Test>>::contains_key(&addr));
-
-			// But value should be still there as the lazy removal did not run, yet.
 			assert_matches!(child::get(trie, &[99]), Some(42));
 
-			// Assign a remaining weight which is too low for a successful deletion of the contract
-			let low_remaining_weight =
-				<<Test as Config>::WeightInfo as WeightInfo>::on_process_deletion_queue_batch();
-
-			// Run the lazy removal
-			Contracts::on_idle(System::block_number(), low_remaining_weight);
+			tries.push(trie.clone())
+		}
 
-			// Value should still be there, since remaining weight was too low for removal
-			assert_matches!(child::get::<i32>(trie, &[99]), Some(42));
+		// Run single lazy removal
+		Contracts::on_idle(System::block_number(), Weight::MAX);
 
-			// Run the lazy removal while deletion_queue is not full
-			Contracts::on_initialize(System::block_number());
+		// The single lazy removal should have removed all queued tries
+		for trie in tries.iter() {
+			assert_matches!(child::get::<i32>(trie, &[99]), None);
+		}
 
-			// Value should still be there, since deletion_queue was not full
-			assert_matches!(child::get::<i32>(trie, &[99]), Some(42));
+		// insert and delete counter values should go from u32::MAX - 1 to 1
+		assert_eq!(<DeletionQueueCounter<Test>>::get().as_test_tuple(), (1, 1));
+	})
+}
+#[test]
+fn refcounter() {
+	let (wasm, code_hash) = compile_module("self_destruct").unwrap();
+	ExtBuilder::default().existential_deposit(50).build().execute_with(|| {
+		let _ = <Test as Config>::Currency::set_balance(&ALICE, 1_000_000);
+		let min_balance = Contracts::min_balance();
+
+		// Create two contracts with the same code and check that they do in fact share it.
+		let Contract { addr: addr0, .. } = builder::bare_instantiate(Code::Upload(wasm.clone()))
+			.value(min_balance * 100)
+			.salt(Some([0; 32]))
+			.build_and_unwrap_contract();
+		let Contract { addr: addr1, .. } = builder::bare_instantiate(Code::Upload(wasm.clone()))
+			.value(min_balance * 100)
+			.salt(Some([1; 32]))
+			.build_and_unwrap_contract();
+		assert_refcount!(code_hash, 2);
+
+		// Sharing should also work with the usual instantiate call
+		let Contract { addr: addr2, .. } = builder::bare_instantiate(Code::Existing(code_hash))
+			.value(min_balance * 100)
+			.salt(Some([2; 32]))
+			.build_and_unwrap_contract();
+		assert_refcount!(code_hash, 3);
+
+		// Terminating one contract should decrement the refcount
+		assert_ok!(builder::call(addr0).build());
+		assert_refcount!(code_hash, 2);
+
+		// remove another one
+		assert_ok!(builder::call(addr1).build());
+		assert_refcount!(code_hash, 1);
+
+		// Pristine code should still be there
+		PristineCode::<Test>::get(code_hash).unwrap();
+
+		// remove the last contract
+		assert_ok!(builder::call(addr2).build());
+		assert_refcount!(code_hash, 0);
+
+		// refcount is `0` but code should still exists because it needs to be removed manually
+		assert!(crate::PristineCode::<Test>::contains_key(&code_hash));
+	});
+}
 
-			// Run on_idle with max remaining weight, this should remove the value
-			Contracts::on_idle(System::block_number(), Weight::MAX);
+#[test]
+fn debug_message_works() {
+	let (wasm, _code_hash) = compile_module("debug_message_works").unwrap();
 
-			// Value should be gone
-			assert_matches!(child::get::<i32>(trie, &[99]), None);
-		});
-	}
+	ExtBuilder::default().existential_deposit(50).build().execute_with(|| {
+		let _ = <Test as Config>::Currency::set_balance(&ALICE, 1_000_000);
+		let Contract { addr, .. } = builder::bare_instantiate(Code::Upload(wasm))
+			.value(30_000)
+			.build_and_unwrap_contract();
+		let result = builder::bare_call(addr).debug(DebugInfo::UnsafeDebug).build();
 
-	#[test]
-	fn lazy_removal_does_not_use_all_weight() {
-		let (code, _hash) = compile_module("self_destruct").unwrap();
+		assert_matches!(result.result, Ok(_));
+		assert_eq!(std::str::from_utf8(&result.debug_message).unwrap(), "Hello World!");
+	});
+}
 
-		let mut meter = WeightMeter::with_limit(Weight::from_parts(5_000_000_000, 100 * 1024));
-		let mut ext = ExtBuilder::default().existential_deposit(50).build();
+#[test]
+fn debug_message_logging_disabled() {
+	let (wasm, _code_hash) = compile_module("debug_message_logging_disabled").unwrap();
+
+	ExtBuilder::default().existential_deposit(50).build().execute_with(|| {
+		let _ = <Test as Config>::Currency::set_balance(&ALICE, 1_000_000);
+		let Contract { addr, .. } = builder::bare_instantiate(Code::Upload(wasm))
+			.value(30_000)
+			.build_and_unwrap_contract();
+		// the dispatchables always run without debugging
+		assert_ok!(Contracts::call(
+			RuntimeOrigin::signed(ALICE),
+			addr,
+			0,
+			GAS_LIMIT,
+			deposit_limit::<Test>(),
+			vec![]
+		));
+	});
+}
 
-		let (trie, vals, weight_per_key) = ext.execute_with(|| {
-			let min_balance = Contracts::min_balance();
-			let _ = <Test as Config>::Currency::set_balance(&ALICE, 1000 * min_balance);
+#[test]
+fn debug_message_invalid_utf8() {
+	let (wasm, _code_hash) = compile_module("debug_message_invalid_utf8").unwrap();
+
+	ExtBuilder::default().existential_deposit(50).build().execute_with(|| {
+		let _ = <Test as Config>::Currency::set_balance(&ALICE, 1_000_000);
+		let Contract { addr, .. } = builder::bare_instantiate(Code::Upload(wasm))
+			.value(30_000)
+			.build_and_unwrap_contract();
+		let result = builder::bare_call(addr).debug(DebugInfo::UnsafeDebug).build();
+		assert_ok!(result.result);
+		assert!(result.debug_message.is_empty());
+	});
+}
 
-			let Contract { addr, .. } = builder::bare_instantiate(Code::Upload(code))
+#[test]
+fn gas_estimation_for_subcalls() {
+	let (caller_code, _caller_hash) = compile_module("call_with_limit").unwrap();
+	let (call_runtime_code, _caller_hash) = compile_module("call_runtime").unwrap();
+	let (dummy_code, _callee_hash) = compile_module("dummy").unwrap();
+	ExtBuilder::default().existential_deposit(50).build().execute_with(|| {
+		let min_balance = Contracts::min_balance();
+		let _ = <Test as Config>::Currency::set_balance(&ALICE, 2_000 * min_balance);
+
+		let Contract { addr: addr_caller, .. } =
+			builder::bare_instantiate(Code::Upload(caller_code))
 				.value(min_balance * 100)
 				.build_and_unwrap_contract();
 
-			let info = get_contract(&addr);
-			let (weight_per_key, max_keys) = ContractInfo::<Test>::deletion_budget(&meter);
-			assert!(max_keys > 0);
+		let Contract { addr: addr_dummy, .. } = builder::bare_instantiate(Code::Upload(dummy_code))
+			.value(min_balance * 100)
+			.build_and_unwrap_contract();
 
-			// We create a contract with one less storage item than we can remove within the limit
-			let vals: Vec<_> = (0..max_keys - 1)
-				.map(|i| (blake2_256(&i.encode()), (i as u32), (i as u32).encode()))
-				.collect();
+		let Contract { addr: addr_call_runtime, .. } =
+			builder::bare_instantiate(Code::Upload(call_runtime_code))
+				.value(min_balance * 100)
+				.build_and_unwrap_contract();
 
-			// Put value into the contracts child trie
-			for val in &vals {
-				info.write(&Key::Fix(val.0), Some(val.2.clone()), None, false).unwrap();
-			}
-			<ContractInfoOf<Test>>::insert(&addr, info.clone());
+		// Run the test for all of those weight limits for the subcall
+		let weights = [
+			Weight::zero(),
+			GAS_LIMIT,
+			GAS_LIMIT * 2,
+			GAS_LIMIT / 5,
+			Weight::from_parts(0, GAS_LIMIT.proof_size()),
+			Weight::from_parts(GAS_LIMIT.ref_time(), 0),
+		];
 
-			// Terminate the contract
-			assert_ok!(builder::call(addr).build());
+		// This call is passed to the sub call in order to create a large `required_weight`
+		let runtime_call = RuntimeCall::Dummy(pallet_dummy::Call::overestimate_pre_charge {
+			pre_charge: Weight::from_parts(10_000_000_000, 512 * 1024),
+			actual_weight: Weight::from_parts(1, 1),
+		})
+		.encode();
 
-			// Contract info should be gone
-			assert!(!<ContractInfoOf::<Test>>::contains_key(&addr));
+		// Encodes which contract should be sub called with which input
+		let sub_calls: [(&[u8], Vec<_>, bool); 2] = [
+			(addr_dummy.as_ref(), vec![], false),
+			(addr_call_runtime.as_ref(), runtime_call, true),
+		];
 
-			let trie = info.child_trie_info();
+		for weight in weights {
+			for (sub_addr, sub_input, out_of_gas_in_subcall) in &sub_calls {
+				let input: Vec<u8> = sub_addr
+					.iter()
+					.cloned()
+					.chain(weight.ref_time().to_le_bytes())
+					.chain(weight.proof_size().to_le_bytes())
+					.chain(sub_input.clone())
+					.collect();
+
+				// Call in order to determine the gas that is required for this call
+				let result_orig = builder::bare_call(addr_caller).data(input.clone()).build();
+				assert_ok!(&result_orig.result);
+
+				// If the out of gas happens in the subcall the caller contract
+				// will just trap. Otherwise we would need to forward an error
+				// code to signal that the sub contract ran out of gas.
+				let error: DispatchError = if *out_of_gas_in_subcall {
+					assert!(result_orig.gas_required.all_gt(result_orig.gas_consumed));
+					<Error<Test>>::ContractTrapped.into()
+				} else {
+					assert_eq!(result_orig.gas_required, result_orig.gas_consumed);
+					<Error<Test>>::OutOfGas.into()
+				};
 
-			// But value should be still there as the lazy removal did not run, yet.
-			for val in &vals {
-				assert_eq!(child::get::<u32>(&trie, &blake2_256(&val.0)), Some(val.1));
+				// Make the same call using the estimated gas. Should succeed.
+				let result = builder::bare_call(addr_caller)
+					.gas_limit(result_orig.gas_required)
+					.storage_deposit_limit(result_orig.storage_deposit.charge_or_zero())
+					.data(input.clone())
+					.build();
+				assert_ok!(&result.result);
+
+				// Check that it fails with too little ref_time
+				let result = builder::bare_call(addr_caller)
+					.gas_limit(result_orig.gas_required.sub_ref_time(1))
+					.storage_deposit_limit(result_orig.storage_deposit.charge_or_zero())
+					.data(input.clone())
+					.build();
+				assert_err!(result.result, error);
+
+				// Check that it fails with too little proof_size
+				let result = builder::bare_call(addr_caller)
+					.gas_limit(result_orig.gas_required.sub_proof_size(1))
+					.storage_deposit_limit(result_orig.storage_deposit.charge_or_zero())
+					.data(input.clone())
+					.build();
+				assert_err!(result.result, error);
 			}
+		}
+	});
+}
 
-			(trie, vals, weight_per_key)
-		});
+#[test]
+fn gas_estimation_call_runtime() {
+	let (caller_code, _caller_hash) = compile_module("call_runtime").unwrap();
+	ExtBuilder::default().existential_deposit(50).build().execute_with(|| {
+		let min_balance = Contracts::min_balance();
+		let _ = <Test as Config>::Currency::set_balance(&ALICE, 1000 * min_balance);
+		let _ = <Test as Config>::Currency::set_balance(&CHARLIE, 1000 * min_balance);
 
-		// The lazy removal limit only applies to the backend but not to the overlay.
-		// This commits all keys from the overlay to the backend.
-		ext.commit_all().unwrap();
+		let Contract { addr: addr_caller, .. } =
+			builder::bare_instantiate(Code::Upload(caller_code))
+				.value(min_balance * 100)
+				.salt(Some([0; 32]))
+				.build_and_unwrap_contract();
 
-		ext.execute_with(|| {
-			// Run the lazy removal
-			ContractInfo::<Test>::process_deletion_queue_batch(&mut meter);
-			let base_weight =
-				<<Test as Config>::WeightInfo as WeightInfo>::on_process_deletion_queue_batch();
-			assert_eq!(meter.consumed(), weight_per_key.mul(vals.len() as _) + base_weight);
-
-			// All the keys are removed
-			for val in vals {
-				assert_eq!(child::get::<u32>(&trie, &blake2_256(&val.0)), None);
-			}
+		// Call something trivial with a huge gas limit so that we can observe the effects
+		// of pre-charging. This should create a difference between consumed and required.
+		let call = RuntimeCall::Dummy(pallet_dummy::Call::overestimate_pre_charge {
+			pre_charge: Weight::from_parts(10_000_000, 1_000),
+			actual_weight: Weight::from_parts(100, 100),
 		});
-	}
+		let result = builder::bare_call(addr_caller).data(call.encode()).build();
+		// contract encodes the result of the dispatch runtime
+		let outcome = u32::decode(&mut result.result.unwrap().data.as_ref()).unwrap();
+		assert_eq!(outcome, 0);
+		assert!(result.gas_required.all_gt(result.gas_consumed));
+
+		// Make the same call using the required gas. Should succeed.
+		assert_ok!(
+			builder::bare_call(addr_caller)
+				.gas_limit(result.gas_required)
+				.data(call.encode())
+				.build()
+				.result
+		);
+	});
+}
 
-	#[test]
-	fn deletion_queue_ring_buffer_overflow() {
-		let (code, _hash) = compile_module("self_destruct").unwrap();
-		let mut ext = ExtBuilder::default().existential_deposit(50).build();
+#[test]
+fn call_runtime_reentrancy_guarded() {
+	let (caller_code, _caller_hash) = compile_module("call_runtime").unwrap();
+	let (callee_code, _callee_hash) = compile_module("dummy").unwrap();
+	ExtBuilder::default().existential_deposit(50).build().execute_with(|| {
+		let min_balance = Contracts::min_balance();
+		let _ = <Test as Config>::Currency::set_balance(&ALICE, 1000 * min_balance);
+		let _ = <Test as Config>::Currency::set_balance(&CHARLIE, 1000 * min_balance);
+
+		let Contract { addr: addr_caller, .. } =
+			builder::bare_instantiate(Code::Upload(caller_code))
+				.value(min_balance * 100)
+				.salt(Some([0; 32]))
+				.build_and_unwrap_contract();
 
-		// setup the deletion queue with custom counters
-		ext.execute_with(|| {
-			let queue = DeletionQueueManager::from_test_values(u32::MAX - 1, u32::MAX - 1);
-			<DeletionQueueCounter<Test>>::set(queue);
+		let Contract { addr: addr_callee, .. } =
+			builder::bare_instantiate(Code::Upload(callee_code))
+				.value(min_balance * 100)
+				.salt(Some([1; 32]))
+				.build_and_unwrap_contract();
+
+		// Call pallet_revive call() dispatchable
+		let call = RuntimeCall::Contracts(crate::Call::call {
+			dest: addr_callee,
+			value: 0,
+			gas_limit: GAS_LIMIT / 3,
+			storage_deposit_limit: deposit_limit::<Test>(),
+			data: vec![],
 		});
 
-		// commit the changes to the storage
-		ext.commit_all().unwrap();
+		// Call runtime to re-enter back to contracts engine by
+		// calling dummy contract
+		let result = builder::bare_call(addr_caller).data(call.encode()).build_and_unwrap_result();
+		// Call to runtime should fail because of the re-entrancy guard
+		assert_return_code!(result, RuntimeReturnCode::CallRuntimeFailed);
+	});
+}
 
-		ext.execute_with(|| {
-			let min_balance = Contracts::min_balance();
-			let _ = <Test as Config>::Currency::set_balance(&ALICE, 1000 * min_balance);
-			let mut tries: Vec<child::ChildInfo> = vec![];
-
-			// add 3 contracts to the deletion queue
-			for i in 0..3u8 {
-				let Contract { addr, .. } = builder::bare_instantiate(Code::Upload(code.clone()))
-					.value(min_balance * 100)
-					.salt(Some([i; 32]))
-					.build_and_unwrap_contract();
+#[test]
+fn ecdsa_recover() {
+	let (wasm, _code_hash) = compile_module("ecdsa_recover").unwrap();
 
-				let info = get_contract(&addr);
-				let trie = &info.child_trie_info();
+	ExtBuilder::default().existential_deposit(50).build().execute_with(|| {
+		let _ = <Test as Config>::Currency::set_balance(&ALICE, 1_000_000);
 
-				// Put value into the contracts child trie
-				child::put(trie, &[99], &42);
+		// Instantiate the ecdsa_recover contract.
+		let Contract { addr, .. } = builder::bare_instantiate(Code::Upload(wasm))
+			.value(100_000)
+			.build_and_unwrap_contract();
 
-				// Terminate the contract. Contract info should be gone, but value should be still
-				// there as the lazy removal did not run, yet.
-				assert_ok!(builder::call(addr).build());
+		#[rustfmt::skip]
+		let signature: [u8; 65] = [
+			161, 234, 203,  74, 147, 96,  51, 212,   5, 174, 231,   9, 142,  48, 137, 201,
+			162, 118, 192,  67, 239, 16,  71, 216, 125,  86, 167, 139,  70,   7,  86, 241,
+			 33,  87, 154, 251,  81, 29, 160,   4, 176, 239,  88, 211, 244, 232, 232,  52,
+			211, 234, 100, 115, 230, 47,  80,  44, 152, 166,  62,  50,   8,  13,  86, 175,
+			 28,
+		];
+		#[rustfmt::skip]
+		let message_hash: [u8; 32] = [
+			162, 28, 244, 179, 96, 76, 244, 178, 188,  83, 230, 248, 143, 106,  77, 117,
+			239, 95, 244, 171, 65, 95,  62, 153, 174, 166, 182,  28, 130,  73, 196, 208
+		];
+		#[rustfmt::skip]
+		const EXPECTED_COMPRESSED_PUBLIC_KEY: [u8; 33] = [
+			  2, 121, 190, 102, 126, 249, 220, 187, 172, 85, 160,  98, 149, 206, 135, 11,
+			  7,   2, 155, 252, 219,  45, 206,  40, 217, 89, 242, 129,  91,  22, 248, 23,
+			152,
+		];
+		let mut params = vec![];
+		params.extend_from_slice(&signature);
+		params.extend_from_slice(&message_hash);
+		assert!(params.len() == 65 + 32);
+		let result = builder::bare_call(addr).data(params).build_and_unwrap_result();
+		assert!(!result.did_revert());
+		assert_eq!(result.data, EXPECTED_COMPRESSED_PUBLIC_KEY);
+	})
+}
 
-				assert!(!<ContractInfoOf::<Test>>::contains_key(&addr));
-				assert_matches!(child::get(trie, &[99]), Some(42));
+#[test]
+fn bare_instantiate_returns_events() {
+	let (wasm, _code_hash) = compile_module("transfer_return_code").unwrap();
+	ExtBuilder::default().existential_deposit(50).build().execute_with(|| {
+		let min_balance = Contracts::min_balance();
+		let _ = <Test as Config>::Currency::set_balance(&ALICE, 1000 * min_balance);
+
+		let result = builder::bare_instantiate(Code::Upload(wasm))
+			.value(min_balance * 100)
+			.collect_events(CollectEvents::UnsafeCollect)
+			.build();
+
+		let events = result.events.unwrap();
+		assert!(!events.is_empty());
+		assert_eq!(events, System::events());
+	});
+}
 
-				tries.push(trie.clone())
-			}
+#[test]
+fn bare_instantiate_does_not_return_events() {
+	let (wasm, _code_hash) = compile_module("transfer_return_code").unwrap();
+	ExtBuilder::default().existential_deposit(50).build().execute_with(|| {
+		let min_balance = Contracts::min_balance();
+		let _ = <Test as Config>::Currency::set_balance(&ALICE, 1000 * min_balance);
 
-			// Run single lazy removal
-			Contracts::on_idle(System::block_number(), Weight::MAX);
+		let result = builder::bare_instantiate(Code::Upload(wasm)).value(min_balance * 100).build();
 
-			// The single lazy removal should have removed all queued tries
-			for trie in tries.iter() {
-				assert_matches!(child::get::<i32>(trie, &[99]), None);
-			}
+		let events = result.events;
+		assert!(!System::events().is_empty());
+		assert!(events.is_none());
+	});
+}
 
-			// insert and delete counter values should go from u32::MAX - 1 to 1
-			assert_eq!(<DeletionQueueCounter<Test>>::get().as_test_tuple(), (1, 1));
-		})
-	}
-	#[test]
-	fn refcounter() {
-		let (wasm, code_hash) = compile_module("self_destruct").unwrap();
-		ExtBuilder::default().existential_deposit(50).build().execute_with(|| {
-			let _ = <Test as Config>::Currency::set_balance(&ALICE, 1_000_000);
-			let min_balance = Contracts::min_balance();
+#[test]
+fn bare_call_returns_events() {
+	let (wasm, _code_hash) = compile_module("transfer_return_code").unwrap();
+	ExtBuilder::default().existential_deposit(50).build().execute_with(|| {
+		let min_balance = Contracts::min_balance();
+		let _ = <Test as Config>::Currency::set_balance(&ALICE, 1000 * min_balance);
 
-			// Create two contracts with the same code and check that they do in fact share it.
-			let Contract { addr: addr0, .. } =
-				builder::bare_instantiate(Code::Upload(wasm.clone()))
-					.value(min_balance * 100)
-					.salt(Some([0; 32]))
-					.build_and_unwrap_contract();
-			let Contract { addr: addr1, .. } =
-				builder::bare_instantiate(Code::Upload(wasm.clone()))
-					.value(min_balance * 100)
-					.salt(Some([1; 32]))
-					.build_and_unwrap_contract();
-			assert_refcount!(code_hash, 2);
+		let Contract { addr, .. } = builder::bare_instantiate(Code::Upload(wasm))
+			.value(min_balance * 100)
+			.build_and_unwrap_contract();
 
-			// Sharing should also work with the usual instantiate call
-			let Contract { addr: addr2, .. } = builder::bare_instantiate(Code::Existing(code_hash))
-				.value(min_balance * 100)
-				.salt(Some([2; 32]))
-				.build_and_unwrap_contract();
-			assert_refcount!(code_hash, 3);
+		let result = builder::bare_call(addr).collect_events(CollectEvents::UnsafeCollect).build();
 
-			// Terminating one contract should decrement the refcount
-			assert_ok!(builder::call(addr0).build());
-			assert_refcount!(code_hash, 2);
+		let events = result.events.unwrap();
+		assert_return_code!(&result.result.unwrap(), RuntimeReturnCode::Success);
+		assert!(!events.is_empty());
+		assert_eq!(events, System::events());
+	});
+}
 
-			// remove another one
-			assert_ok!(builder::call(addr1).build());
-			assert_refcount!(code_hash, 1);
+#[test]
+fn bare_call_does_not_return_events() {
+	let (wasm, _code_hash) = compile_module("transfer_return_code").unwrap();
+	ExtBuilder::default().existential_deposit(50).build().execute_with(|| {
+		let min_balance = Contracts::min_balance();
+		let _ = <Test as Config>::Currency::set_balance(&ALICE, 1000 * min_balance);
 
-			// Pristine code should still be there
-			PristineCode::<Test>::get(code_hash).unwrap();
+		let Contract { addr, .. } = builder::bare_instantiate(Code::Upload(wasm))
+			.value(min_balance * 100)
+			.build_and_unwrap_contract();
 
-			// remove the last contract
-			assert_ok!(builder::call(addr2).build());
-			assert_refcount!(code_hash, 0);
+		let result = builder::bare_call(addr).build();
 
-			// refcount is `0` but code should still exists because it needs to be removed manually
-			assert!(crate::PristineCode::<Test>::contains_key(&code_hash));
-		});
-	}
+		let events = result.events;
+		assert_return_code!(&result.result.unwrap(), RuntimeReturnCode::Success);
+		assert!(!System::events().is_empty());
+		assert!(events.is_none());
+	});
+}
 
-	#[test]
-	fn debug_message_works() {
-		let (wasm, _code_hash) = compile_module("debug_message_works").unwrap();
+#[test]
+fn sr25519_verify() {
+	let (wasm, _code_hash) = compile_module("sr25519_verify").unwrap();
 
-		ExtBuilder::default().existential_deposit(50).build().execute_with(|| {
-			let _ = <Test as Config>::Currency::set_balance(&ALICE, 1_000_000);
-			let Contract { addr, .. } = builder::bare_instantiate(Code::Upload(wasm))
-				.value(30_000)
-				.build_and_unwrap_contract();
-			let result = builder::bare_call(addr).debug(DebugInfo::UnsafeDebug).build();
+	ExtBuilder::default().existential_deposit(50).build().execute_with(|| {
+		let _ = <Test as Config>::Currency::set_balance(&ALICE, 1_000_000);
 
-			assert_matches!(result.result, Ok(_));
-			assert_eq!(std::str::from_utf8(&result.debug_message).unwrap(), "Hello World!");
-		});
-	}
-
-	#[test]
-	fn debug_message_logging_disabled() {
-		let (wasm, _code_hash) = compile_module("debug_message_logging_disabled").unwrap();
-
-		ExtBuilder::default().existential_deposit(50).build().execute_with(|| {
-			let _ = <Test as Config>::Currency::set_balance(&ALICE, 1_000_000);
-			let Contract { addr, .. } = builder::bare_instantiate(Code::Upload(wasm))
-				.value(30_000)
-				.build_and_unwrap_contract();
-			// the dispatchables always run without debugging
-			assert_ok!(Contracts::call(
-				RuntimeOrigin::signed(ALICE),
-				addr,
-				0,
-				GAS_LIMIT,
-				deposit_limit::<Test>(),
-				vec![]
-			));
-		});
-	}
+		// Instantiate the sr25519_verify contract.
+		let Contract { addr, .. } = builder::bare_instantiate(Code::Upload(wasm))
+			.value(100_000)
+			.build_and_unwrap_contract();
 
-	#[test]
-	fn debug_message_invalid_utf8() {
-		let (wasm, _code_hash) = compile_module("debug_message_invalid_utf8").unwrap();
+		let call_with = |message: &[u8; 11]| {
+			// Alice's signature for "hello world"
+			#[rustfmt::skip]
+			let signature: [u8; 64] = [
+				184, 49, 74, 238, 78, 165, 102, 252, 22, 92, 156, 176, 124, 118, 168, 116, 247,
+				99, 0, 94, 2, 45, 9, 170, 73, 222, 182, 74, 60, 32, 75, 64, 98, 174, 69, 55, 83,
+				85, 180, 98, 208, 75, 231, 57, 205, 62, 4, 105, 26, 136, 172, 17, 123, 99, 90, 255,
+				228, 54, 115, 63, 30, 207, 205, 131,
+			];
 
-		ExtBuilder::default().existential_deposit(50).build().execute_with(|| {
-			let _ = <Test as Config>::Currency::set_balance(&ALICE, 1_000_000);
-			let Contract { addr, .. } = builder::bare_instantiate(Code::Upload(wasm))
-				.value(30_000)
-				.build_and_unwrap_contract();
-			let result = builder::bare_call(addr).debug(DebugInfo::UnsafeDebug).build();
-			assert_ok!(result.result);
-			assert!(result.debug_message.is_empty());
-		});
-	}
+			// Alice's public key
+			#[rustfmt::skip]
+			let public_key: [u8; 32] = [
+				212, 53, 147, 199, 21, 253, 211, 28, 97, 20, 26, 189, 4, 169, 159, 214, 130, 44,
+				133, 88, 133, 76, 205, 227, 154, 86, 132, 231, 165, 109, 162, 125,
+			];
 
-	#[test]
-	fn gas_estimation_for_subcalls() {
-		let (caller_code, _caller_hash) = compile_module("call_with_limit").unwrap();
-		let (call_runtime_code, _caller_hash) = compile_module("call_runtime").unwrap();
-		let (dummy_code, _callee_hash) = compile_module("dummy").unwrap();
-		ExtBuilder::default().existential_deposit(50).build().execute_with(|| {
-			let min_balance = Contracts::min_balance();
-			let _ = <Test as Config>::Currency::set_balance(&ALICE, 2_000 * min_balance);
-
-			let Contract { addr: addr_caller, .. } =
-				builder::bare_instantiate(Code::Upload(caller_code))
-					.value(min_balance * 100)
-					.build_and_unwrap_contract();
+			let mut params = vec![];
+			params.extend_from_slice(&signature);
+			params.extend_from_slice(&public_key);
+			params.extend_from_slice(message);
 
-			let Contract { addr: addr_dummy, .. } =
-				builder::bare_instantiate(Code::Upload(dummy_code))
-					.value(min_balance * 100)
-					.build_and_unwrap_contract();
+			builder::bare_call(addr).data(params).build_and_unwrap_result()
+		};
 
-			let Contract { addr: addr_call_runtime, .. } =
-				builder::bare_instantiate(Code::Upload(call_runtime_code))
-					.value(min_balance * 100)
-					.build_and_unwrap_contract();
+		// verification should succeed for "hello world"
+		assert_return_code!(call_with(&b"hello world"), RuntimeReturnCode::Success);
 
-			// Run the test for all of those weight limits for the subcall
-			let weights = [
-				Weight::zero(),
-				GAS_LIMIT,
-				GAS_LIMIT * 2,
-				GAS_LIMIT / 5,
-				Weight::from_parts(0, GAS_LIMIT.proof_size()),
-				Weight::from_parts(GAS_LIMIT.ref_time(), 0),
-			];
+		// verification should fail for other messages
+		assert_return_code!(call_with(&b"hello worlD"), RuntimeReturnCode::Sr25519VerifyFailed);
+	});
+}
 
-			// This call is passed to the sub call in order to create a large `required_weight`
-			let runtime_call = RuntimeCall::Dummy(pallet_dummy::Call::overestimate_pre_charge {
-				pre_charge: Weight::from_parts(10_000_000_000, 512 * 1024),
-				actual_weight: Weight::from_parts(1, 1),
-			})
-			.encode();
-
-			// Encodes which contract should be sub called with which input
-			let sub_calls: [(&[u8], Vec<_>, bool); 2] = [
-				(addr_dummy.as_ref(), vec![], false),
-				(addr_call_runtime.as_ref(), runtime_call, true),
-			];
+#[test]
+fn failed_deposit_charge_should_roll_back_call() {
+	let (wasm_caller, _) = compile_module("call_runtime_and_call").unwrap();
+	let (wasm_callee, _) = compile_module("store_call").unwrap();
+	const ED: u64 = 200;
 
-			for weight in weights {
-				for (sub_addr, sub_input, out_of_gas_in_subcall) in &sub_calls {
-					let input: Vec<u8> = sub_addr
-						.iter()
-						.cloned()
-						.chain(weight.ref_time().to_le_bytes())
-						.chain(weight.proof_size().to_le_bytes())
-						.chain(sub_input.clone())
-						.collect();
-
-					// Call in order to determine the gas that is required for this call
-					let result_orig = builder::bare_call(addr_caller).data(input.clone()).build();
-					assert_ok!(&result_orig.result);
-
-					// If the out of gas happens in the subcall the caller contract
-					// will just trap. Otherwise we would need to forward an error
-					// code to signal that the sub contract ran out of gas.
-					let error: DispatchError = if *out_of_gas_in_subcall {
-						assert!(result_orig.gas_required.all_gt(result_orig.gas_consumed));
-						<Error<Test>>::ContractTrapped.into()
-					} else {
-						assert_eq!(result_orig.gas_required, result_orig.gas_consumed);
-						<Error<Test>>::OutOfGas.into()
-					};
-
-					// Make the same call using the estimated gas. Should succeed.
-					let result = builder::bare_call(addr_caller)
-						.gas_limit(result_orig.gas_required)
-						.storage_deposit_limit(result_orig.storage_deposit.charge_or_zero())
-						.data(input.clone())
-						.build();
-					assert_ok!(&result.result);
-
-					// Check that it fails with too little ref_time
-					let result = builder::bare_call(addr_caller)
-						.gas_limit(result_orig.gas_required.sub_ref_time(1))
-						.storage_deposit_limit(result_orig.storage_deposit.charge_or_zero())
-						.data(input.clone())
-						.build();
-					assert_err!(result.result, error);
-
-					// Check that it fails with too little proof_size
-					let result = builder::bare_call(addr_caller)
-						.gas_limit(result_orig.gas_required.sub_proof_size(1))
-						.storage_deposit_limit(result_orig.storage_deposit.charge_or_zero())
-						.data(input.clone())
-						.build();
-					assert_err!(result.result, error);
-				}
-			}
-		});
-	}
+	let execute = || {
+		ExtBuilder::default().existential_deposit(ED).build().execute_with(|| {
+			let _ = <Test as Config>::Currency::set_balance(&ALICE, 1_000_000);
 
-	#[test]
-	fn gas_estimation_call_runtime() {
-		let (caller_code, _caller_hash) = compile_module("call_runtime").unwrap();
-		ExtBuilder::default().existential_deposit(50).build().execute_with(|| {
-			let min_balance = Contracts::min_balance();
-			let _ = <Test as Config>::Currency::set_balance(&ALICE, 1000 * min_balance);
-			let _ = <Test as Config>::Currency::set_balance(&CHARLIE, 1000 * min_balance);
-
-			let Contract { addr: addr_caller, .. } =
-				builder::bare_instantiate(Code::Upload(caller_code))
-					.value(min_balance * 100)
-					.salt(Some([0; 32]))
+			// Instantiate both contracts.
+			let caller = builder::bare_instantiate(Code::Upload(wasm_caller.clone()))
+				.build_and_unwrap_contract();
+			let Contract { addr: addr_callee, .. } =
+				builder::bare_instantiate(Code::Upload(wasm_callee.clone()))
 					.build_and_unwrap_contract();
 
-			// Call something trivial with a huge gas limit so that we can observe the effects
-			// of pre-charging. This should create a difference between consumed and required.
-			let call = RuntimeCall::Dummy(pallet_dummy::Call::overestimate_pre_charge {
-				pre_charge: Weight::from_parts(10_000_000, 1_000),
-				actual_weight: Weight::from_parts(100, 100),
+			// Give caller proxy access to Alice.
+			assert_ok!(Proxy::add_proxy(
+				RuntimeOrigin::signed(ALICE),
+				caller.account_id.clone(),
+				(),
+				0
+			));
+
+			// Create a Proxy call that will attempt to transfer away Alice's balance.
+			let transfer_call =
+				Box::new(RuntimeCall::Balances(pallet_balances::Call::transfer_allow_death {
+					dest: CHARLIE,
+					value: pallet_balances::Pallet::<Test>::free_balance(&ALICE) - 2 * ED,
+				}));
+
+			// Wrap the transfer call in a proxy call.
+			let transfer_proxy_call = RuntimeCall::Proxy(pallet_proxy::Call::proxy {
+				real: ALICE,
+				force_proxy_type: Some(()),
+				call: transfer_call,
 			});
-			let result = builder::bare_call(addr_caller).data(call.encode()).build();
-			// contract encodes the result of the dispatch runtime
-			let outcome = u32::decode(&mut result.result.unwrap().data.as_ref()).unwrap();
-			assert_eq!(outcome, 0);
-			assert!(result.gas_required.all_gt(result.gas_consumed));
-
-			// Make the same call using the required gas. Should succeed.
-			assert_ok!(
-				builder::bare_call(addr_caller)
-					.gas_limit(result.gas_required)
-					.data(call.encode())
-					.build()
-					.result
+
+			let data = (
+				(ED - DepositPerItem::get()) as u32, // storage length
+				addr_callee,
+				transfer_proxy_call,
 			);
-		});
-	}
 
-	#[test]
-	fn call_runtime_reentrancy_guarded() {
-		let (caller_code, _caller_hash) = compile_module("call_runtime").unwrap();
-		let (callee_code, _callee_hash) = compile_module("dummy").unwrap();
-		ExtBuilder::default().existential_deposit(50).build().execute_with(|| {
-			let min_balance = Contracts::min_balance();
-			let _ = <Test as Config>::Currency::set_balance(&ALICE, 1000 * min_balance);
-			let _ = <Test as Config>::Currency::set_balance(&CHARLIE, 1000 * min_balance);
-
-			let Contract { addr: addr_caller, .. } =
-				builder::bare_instantiate(Code::Upload(caller_code))
-					.value(min_balance * 100)
-					.salt(Some([0; 32]))
-					.build_and_unwrap_contract();
+			builder::call(caller.addr).data(data.encode()).build()
+		})
+	};
 
-			let Contract { addr: addr_callee, .. } =
-				builder::bare_instantiate(Code::Upload(callee_code))
-					.value(min_balance * 100)
-					.salt(Some([1; 32]))
-					.build_and_unwrap_contract();
+	// With a low enough deposit per byte, the call should succeed.
+	let result = execute().unwrap();
 
-			// Call pallet_revive call() dispatchable
-			let call = RuntimeCall::Contracts(crate::Call::call {
-				dest: addr_callee,
-				value: 0,
-				gas_limit: GAS_LIMIT / 3,
-				storage_deposit_limit: deposit_limit::<Test>(),
-				data: vec![],
-			});
+	// Bump the deposit per byte to a high value to trigger a FundsUnavailable error.
+	DEPOSIT_PER_BYTE.with(|c| *c.borrow_mut() = 20);
+	assert_err_with_weight!(execute(), TokenError::FundsUnavailable, result.actual_weight);
+}
 
-			// Call runtime to re-enter back to contracts engine by
-			// calling dummy contract
-			let result =
-				builder::bare_call(addr_caller).data(call.encode()).build_and_unwrap_result();
-			// Call to runtime should fail because of the re-entrancy guard
-			assert_return_code!(result, RuntimeReturnCode::CallRuntimeFailed);
-		});
-	}
+#[test]
+fn upload_code_works() {
+	let (wasm, code_hash) = compile_module("dummy").unwrap();
+
+	ExtBuilder::default().existential_deposit(100).build().execute_with(|| {
+		let _ = <Test as Config>::Currency::set_balance(&ALICE, 1_000_000);
+
+		// Drop previous events
+		initialize_block(2);
+
+		assert!(!PristineCode::<Test>::contains_key(&code_hash));
+
+		assert_ok!(Contracts::upload_code(RuntimeOrigin::signed(ALICE), wasm, 1_000,));
+		// Ensure the contract was stored and get expected deposit amount to be reserved.
+		let deposit_expected = expected_deposit(ensure_stored(code_hash));
+
+		assert_eq!(
+			System::events(),
+			vec![EventRecord {
+				phase: Phase::Initialization,
+				event: RuntimeEvent::Contracts(crate::Event::CodeStored {
+					code_hash,
+					deposit_held: deposit_expected,
+					uploader: ALICE_ADDR
+				}),
+				topics: vec![],
+			},]
+		);
+	});
+}
 
-	#[test]
-	fn ecdsa_recover() {
-		let (wasm, _code_hash) = compile_module("ecdsa_recover").unwrap();
+#[test]
+fn upload_code_limit_too_low() {
+	let (wasm, _code_hash) = compile_module("dummy").unwrap();
+	let deposit_expected = expected_deposit(wasm.len());
+	let deposit_insufficient = deposit_expected.saturating_sub(1);
 
-		ExtBuilder::default().existential_deposit(50).build().execute_with(|| {
-			let _ = <Test as Config>::Currency::set_balance(&ALICE, 1_000_000);
+	ExtBuilder::default().existential_deposit(100).build().execute_with(|| {
+		let _ = <Test as Config>::Currency::set_balance(&ALICE, 1_000_000);
 
-			// Instantiate the ecdsa_recover contract.
-			let Contract { addr, .. } = builder::bare_instantiate(Code::Upload(wasm))
-				.value(100_000)
-				.build_and_unwrap_contract();
+		// Drop previous events
+		initialize_block(2);
 
-			#[rustfmt::skip]
-		let signature: [u8; 65] = [
-			161, 234, 203,  74, 147, 96,  51, 212,   5, 174, 231,   9, 142,  48, 137, 201,
-			162, 118, 192,  67, 239, 16,  71, 216, 125,  86, 167, 139,  70,   7,  86, 241,
-			 33,  87, 154, 251,  81, 29, 160,   4, 176, 239,  88, 211, 244, 232, 232,  52,
-			211, 234, 100, 115, 230, 47,  80,  44, 152, 166,  62,  50,   8,  13,  86, 175,
-			 28,
-		];
-			#[rustfmt::skip]
-		let message_hash: [u8; 32] = [
-			162, 28, 244, 179, 96, 76, 244, 178, 188,  83, 230, 248, 143, 106,  77, 117,
-			239, 95, 244, 171, 65, 95,  62, 153, 174, 166, 182,  28, 130,  73, 196, 208
-		];
-			#[rustfmt::skip]
-		const EXPECTED_COMPRESSED_PUBLIC_KEY: [u8; 33] = [
-			  2, 121, 190, 102, 126, 249, 220, 187, 172, 85, 160,  98, 149, 206, 135, 11,
-			  7,   2, 155, 252, 219,  45, 206,  40, 217, 89, 242, 129,  91,  22, 248, 23,
-			152,
-		];
-			let mut params = vec![];
-			params.extend_from_slice(&signature);
-			params.extend_from_slice(&message_hash);
-			assert!(params.len() == 65 + 32);
-			let result = builder::bare_call(addr).data(params).build_and_unwrap_result();
-			assert!(!result.did_revert());
-			assert_eq!(result.data, EXPECTED_COMPRESSED_PUBLIC_KEY);
-		})
-	}
+		assert_noop!(
+			Contracts::upload_code(RuntimeOrigin::signed(ALICE), wasm, deposit_insufficient,),
+			<Error<Test>>::StorageDepositLimitExhausted,
+		);
 
-	#[test]
-	fn bare_instantiate_returns_events() {
-		let (wasm, _code_hash) = compile_module("transfer_return_code").unwrap();
-		ExtBuilder::default().existential_deposit(50).build().execute_with(|| {
-			let min_balance = Contracts::min_balance();
-			let _ = <Test as Config>::Currency::set_balance(&ALICE, 1000 * min_balance);
+		assert_eq!(System::events(), vec![]);
+	});
+}
 
-			let result = builder::bare_instantiate(Code::Upload(wasm))
-				.value(min_balance * 100)
-				.collect_events(CollectEvents::UnsafeCollect)
-				.build();
+#[test]
+fn upload_code_not_enough_balance() {
+	let (wasm, _code_hash) = compile_module("dummy").unwrap();
+	let deposit_expected = expected_deposit(wasm.len());
+	let deposit_insufficient = deposit_expected.saturating_sub(1);
 
-			let events = result.events.unwrap();
-			assert!(!events.is_empty());
-			assert_eq!(events, System::events());
-		});
-	}
+	ExtBuilder::default().existential_deposit(100).build().execute_with(|| {
+		let _ = <Test as Config>::Currency::set_balance(&ALICE, deposit_insufficient);
 
-	#[test]
-	fn bare_instantiate_does_not_return_events() {
-		let (wasm, _code_hash) = compile_module("transfer_return_code").unwrap();
-		ExtBuilder::default().existential_deposit(50).build().execute_with(|| {
-			let min_balance = Contracts::min_balance();
-			let _ = <Test as Config>::Currency::set_balance(&ALICE, 1000 * min_balance);
+		// Drop previous events
+		initialize_block(2);
 
-			let result =
-				builder::bare_instantiate(Code::Upload(wasm)).value(min_balance * 100).build();
+		assert_noop!(
+			Contracts::upload_code(RuntimeOrigin::signed(ALICE), wasm, 1_000,),
+			<Error<Test>>::StorageDepositNotEnoughFunds,
+		);
 
-			let events = result.events;
-			assert!(!System::events().is_empty());
-			assert!(events.is_none());
-		});
-	}
+		assert_eq!(System::events(), vec![]);
+	});
+}
 
-	#[test]
-	fn bare_call_returns_events() {
-		let (wasm, _code_hash) = compile_module("transfer_return_code").unwrap();
-		ExtBuilder::default().existential_deposit(50).build().execute_with(|| {
-			let min_balance = Contracts::min_balance();
-			let _ = <Test as Config>::Currency::set_balance(&ALICE, 1000 * min_balance);
+#[test]
+fn remove_code_works() {
+	let (wasm, code_hash) = compile_module("dummy").unwrap();
 
-			let Contract { addr, .. } = builder::bare_instantiate(Code::Upload(wasm))
-				.value(min_balance * 100)
-				.build_and_unwrap_contract();
+	ExtBuilder::default().existential_deposit(100).build().execute_with(|| {
+		let _ = <Test as Config>::Currency::set_balance(&ALICE, 1_000_000);
 
-			let result =
-				builder::bare_call(addr).collect_events(CollectEvents::UnsafeCollect).build();
+		// Drop previous events
+		initialize_block(2);
 
-			let events = result.events.unwrap();
-			assert_return_code!(&result.result.unwrap(), RuntimeReturnCode::Success);
-			assert!(!events.is_empty());
-			assert_eq!(events, System::events());
-		});
-	}
+		assert_ok!(Contracts::upload_code(RuntimeOrigin::signed(ALICE), wasm, 1_000,));
+		// Ensure the contract was stored and get expected deposit amount to be reserved.
+		let deposit_expected = expected_deposit(ensure_stored(code_hash));
 
-	#[test]
-	fn bare_call_does_not_return_events() {
-		let (wasm, _code_hash) = compile_module("transfer_return_code").unwrap();
-		ExtBuilder::default().existential_deposit(50).build().execute_with(|| {
-			let min_balance = Contracts::min_balance();
-			let _ = <Test as Config>::Currency::set_balance(&ALICE, 1000 * min_balance);
+		assert_ok!(Contracts::remove_code(RuntimeOrigin::signed(ALICE), code_hash));
+		assert_eq!(
+			System::events(),
+			vec![
+				EventRecord {
+					phase: Phase::Initialization,
+					event: RuntimeEvent::Contracts(crate::Event::CodeStored {
+						code_hash,
+						deposit_held: deposit_expected,
+						uploader: ALICE_ADDR
+					}),
+					topics: vec![],
+				},
+				EventRecord {
+					phase: Phase::Initialization,
+					event: RuntimeEvent::Contracts(crate::Event::CodeRemoved {
+						code_hash,
+						deposit_released: deposit_expected,
+						remover: ALICE_ADDR
+					}),
+					topics: vec![],
+				},
+			]
+		);
+	});
+}
 
-			let Contract { addr, .. } = builder::bare_instantiate(Code::Upload(wasm))
-				.value(min_balance * 100)
-				.build_and_unwrap_contract();
+#[test]
+fn remove_code_wrong_origin() {
+	let (wasm, code_hash) = compile_module("dummy").unwrap();
 
-			let result = builder::bare_call(addr).build();
+	ExtBuilder::default().existential_deposit(100).build().execute_with(|| {
+		let _ = <Test as Config>::Currency::set_balance(&ALICE, 1_000_000);
 
-			let events = result.events;
-			assert_return_code!(&result.result.unwrap(), RuntimeReturnCode::Success);
-			assert!(!System::events().is_empty());
-			assert!(events.is_none());
-		});
-	}
+		// Drop previous events
+		initialize_block(2);
 
-	#[test]
-	fn sr25519_verify() {
-		let (wasm, _code_hash) = compile_module("sr25519_verify").unwrap();
+		assert_ok!(Contracts::upload_code(RuntimeOrigin::signed(ALICE), wasm, 1_000,));
+		// Ensure the contract was stored and get expected deposit amount to be reserved.
+		let deposit_expected = expected_deposit(ensure_stored(code_hash));
 
-		ExtBuilder::default().existential_deposit(50).build().execute_with(|| {
-			let _ = <Test as Config>::Currency::set_balance(&ALICE, 1_000_000);
+		assert_noop!(
+			Contracts::remove_code(RuntimeOrigin::signed(BOB), code_hash),
+			sp_runtime::traits::BadOrigin,
+		);
 
-			// Instantiate the sr25519_verify contract.
-			let Contract { addr, .. } = builder::bare_instantiate(Code::Upload(wasm))
-				.value(100_000)
-				.build_and_unwrap_contract();
+		assert_eq!(
+			System::events(),
+			vec![EventRecord {
+				phase: Phase::Initialization,
+				event: RuntimeEvent::Contracts(crate::Event::CodeStored {
+					code_hash,
+					deposit_held: deposit_expected,
+					uploader: ALICE_ADDR
+				}),
+				topics: vec![],
+			},]
+		);
+	});
+}
 
-			let call_with = |message: &[u8; 11]| {
-				// Alice's signature for "hello world"
-				#[rustfmt::skip]
-			let signature: [u8; 64] = [
-				184, 49, 74, 238, 78, 165, 102, 252, 22, 92, 156, 176, 124, 118, 168, 116, 247,
-				99, 0, 94, 2, 45, 9, 170, 73, 222, 182, 74, 60, 32, 75, 64, 98, 174, 69, 55, 83,
-				85, 180, 98, 208, 75, 231, 57, 205, 62, 4, 105, 26, 136, 172, 17, 123, 99, 90, 255,
-				228, 54, 115, 63, 30, 207, 205, 131,
-			];
+#[test]
+fn remove_code_in_use() {
+	let (wasm, code_hash) = compile_module("dummy").unwrap();
 
-				// Alice's public key
-				#[rustfmt::skip]
-			let public_key: [u8; 32] = [
-				212, 53, 147, 199, 21, 253, 211, 28, 97, 20, 26, 189, 4, 169, 159, 214, 130, 44,
-				133, 88, 133, 76, 205, 227, 154, 86, 132, 231, 165, 109, 162, 125,
-			];
+	ExtBuilder::default().existential_deposit(100).build().execute_with(|| {
+		let _ = <Test as Config>::Currency::set_balance(&ALICE, 1_000_000);
 
-				let mut params = vec![];
-				params.extend_from_slice(&signature);
-				params.extend_from_slice(&public_key);
-				params.extend_from_slice(message);
+		assert_ok!(builder::instantiate_with_code(wasm).build());
 
-				builder::bare_call(addr).data(params).build_and_unwrap_result()
-			};
+		// Drop previous events
+		initialize_block(2);
+
+		assert_noop!(
+			Contracts::remove_code(RuntimeOrigin::signed(ALICE), code_hash),
+			<Error<Test>>::CodeInUse,
+		);
 
-			// verification should succeed for "hello world"
-			assert_return_code!(call_with(&b"hello world"), RuntimeReturnCode::Success);
+		assert_eq!(System::events(), vec![]);
+	});
+}
 
-			// verification should fail for other messages
-			assert_return_code!(call_with(&b"hello worlD"), RuntimeReturnCode::Sr25519VerifyFailed);
-		});
-	}
+#[test]
+fn remove_code_not_found() {
+	let (_wasm, code_hash) = compile_module("dummy").unwrap();
 
-	#[test]
-	fn failed_deposit_charge_should_roll_back_call() {
-		let (wasm_caller, _) = compile_module("call_runtime_and_call").unwrap();
-		let (wasm_callee, _) = compile_module("store_call").unwrap();
-		const ED: u64 = 200;
+	ExtBuilder::default().existential_deposit(100).build().execute_with(|| {
+		let _ = <Test as Config>::Currency::set_balance(&ALICE, 1_000_000);
 
-		let execute = || {
-			ExtBuilder::default().existential_deposit(ED).build().execute_with(|| {
-				let _ = <Test as Config>::Currency::set_balance(&ALICE, 1_000_000);
+		// Drop previous events
+		initialize_block(2);
 
-				// Instantiate both contracts.
-				let caller = builder::bare_instantiate(Code::Upload(wasm_caller.clone()))
-					.build_and_unwrap_contract();
-				let Contract { addr: addr_callee, .. } =
-					builder::bare_instantiate(Code::Upload(wasm_callee.clone()))
-						.build_and_unwrap_contract();
-
-				// Give caller proxy access to Alice.
-				assert_ok!(Proxy::add_proxy(
-					RuntimeOrigin::signed(ALICE),
-					caller.account_id.clone(),
-					(),
-					0
-				));
-
-				// Create a Proxy call that will attempt to transfer away Alice's balance.
-				let transfer_call =
-					Box::new(RuntimeCall::Balances(pallet_balances::Call::transfer_allow_death {
-						dest: CHARLIE,
-						value: pallet_balances::Pallet::<Test>::free_balance(&ALICE) - 2 * ED,
-					}));
-
-				// Wrap the transfer call in a proxy call.
-				let transfer_proxy_call = RuntimeCall::Proxy(pallet_proxy::Call::proxy {
-					real: ALICE,
-					force_proxy_type: Some(()),
-					call: transfer_call,
-				});
-
-				let data = (
-					(ED - DepositPerItem::get()) as u32, // storage length
-					addr_callee,
-					transfer_proxy_call,
-				);
+		assert_noop!(
+			Contracts::remove_code(RuntimeOrigin::signed(ALICE), code_hash),
+			<Error<Test>>::CodeNotFound,
+		);
 
-				builder::call(caller.addr).data(data.encode()).build()
-			})
-		};
+		assert_eq!(System::events(), vec![]);
+	});
+}
 
-		// With a low enough deposit per byte, the call should succeed.
-		let result = execute().unwrap();
+#[test]
+fn instantiate_with_zero_balance_works() {
+	let (wasm, code_hash) = compile_module("dummy").unwrap();
+	ExtBuilder::default().existential_deposit(200).build().execute_with(|| {
+		let _ = <Test as Config>::Currency::set_balance(&ALICE, 1_000_000);
+		let min_balance = Contracts::min_balance();
 
-		// Bump the deposit per byte to a high value to trigger a FundsUnavailable error.
-		DEPOSIT_PER_BYTE.with(|c| *c.borrow_mut() = 20);
-		assert_err_with_weight!(execute(), TokenError::FundsUnavailable, result.actual_weight);
-	}
+		// Drop previous events
+		initialize_block(2);
 
-	#[test]
-	fn upload_code_works() {
-		let (wasm, code_hash) = compile_module("dummy").unwrap();
+		// Instantiate the BOB contract.
+		let Contract { addr, account_id } =
+			builder::bare_instantiate(Code::Upload(wasm)).build_and_unwrap_contract();
 
-		ExtBuilder::default().existential_deposit(100).build().execute_with(|| {
-			let _ = <Test as Config>::Currency::set_balance(&ALICE, 1_000_000);
+		// Ensure the contract was stored and get expected deposit amount to be reserved.
+		let deposit_expected = expected_deposit(ensure_stored(code_hash));
 
-			// Drop previous events
-			initialize_block(2);
+		// Make sure the account exists even though no free balance was send
+		assert_eq!(<Test as Config>::Currency::free_balance(&account_id), min_balance);
+		assert_eq!(
+			<Test as Config>::Currency::total_balance(&account_id),
+			min_balance + test_utils::contract_info_storage_deposit(&addr)
+		);
 
-			assert!(!PristineCode::<Test>::contains_key(&code_hash));
+		assert_eq!(
+			System::events(),
+			vec![
+				EventRecord {
+					phase: Phase::Initialization,
+					event: RuntimeEvent::Contracts(crate::Event::CodeStored {
+						code_hash,
+						deposit_held: deposit_expected,
+						uploader: ALICE_ADDR
+					}),
+					topics: vec![],
+				},
+				EventRecord {
+					phase: Phase::Initialization,
+					event: RuntimeEvent::System(frame_system::Event::NewAccount {
+						account: account_id.clone(),
+					}),
+					topics: vec![],
+				},
+				EventRecord {
+					phase: Phase::Initialization,
+					event: RuntimeEvent::Balances(pallet_balances::Event::Endowed {
+						account: account_id.clone(),
+						free_balance: min_balance,
+					}),
+					topics: vec![],
+				},
+				EventRecord {
+					phase: Phase::Initialization,
+					event: RuntimeEvent::Balances(pallet_balances::Event::Transfer {
+						from: ALICE,
+						to: account_id,
+						amount: min_balance,
+					}),
+					topics: vec![],
+				},
+				EventRecord {
+					phase: Phase::Initialization,
+					event: RuntimeEvent::Contracts(crate::Event::Instantiated {
+						deployer: ALICE_ADDR,
+						contract: addr,
+					}),
+					topics: vec![],
+				},
+				EventRecord {
+					phase: Phase::Initialization,
+					event: RuntimeEvent::Contracts(
+						pallet_revive::Event::StorageDepositTransferredAndHeld {
+							from: ALICE_ADDR,
+							to: addr,
+							amount: test_utils::contract_info_storage_deposit(&addr),
+						}
+					),
+					topics: vec![],
+				},
+			]
+		);
+	});
+}
 
-			assert_ok!(Contracts::upload_code(RuntimeOrigin::signed(ALICE), wasm, 1_000,));
-			// Ensure the contract was stored and get expected deposit amount to be reserved.
-			let deposit_expected = expected_deposit(ensure_stored(code_hash));
+#[test]
+fn instantiate_with_below_existential_deposit_works() {
+	let (wasm, code_hash) = compile_module("dummy").unwrap();
+	ExtBuilder::default().existential_deposit(200).build().execute_with(|| {
+		let _ = <Test as Config>::Currency::set_balance(&ALICE, 1_000_000);
+		let min_balance = Contracts::min_balance();
+		let value = 50;
+
+		// Drop previous events
+		initialize_block(2);
+
+		// Instantiate the BOB contract.
+		let Contract { addr, account_id } = builder::bare_instantiate(Code::Upload(wasm))
+			.value(value)
+			.build_and_unwrap_contract();
+
+		// Ensure the contract was stored and get expected deposit amount to be reserved.
+		let deposit_expected = expected_deposit(ensure_stored(code_hash));
+		// Make sure the account exists even though not enough free balance was send
+		assert_eq!(<Test as Config>::Currency::free_balance(&account_id), min_balance + value);
+		assert_eq!(
+			<Test as Config>::Currency::total_balance(&account_id),
+			min_balance + value + test_utils::contract_info_storage_deposit(&addr)
+		);
 
-			assert_eq!(
-				System::events(),
-				vec![EventRecord {
+		assert_eq!(
+			System::events(),
+			vec![
+				EventRecord {
 					phase: Phase::Initialization,
 					event: RuntimeEvent::Contracts(crate::Event::CodeStored {
 						code_hash,
@@ -2596,2057 +2843,1740 @@ mod run_tests {
 						uploader: ALICE_ADDR
 					}),
 					topics: vec![],
-				},]
-			);
-		});
-	}
+				},
+				EventRecord {
+					phase: Phase::Initialization,
+					event: RuntimeEvent::System(frame_system::Event::NewAccount {
+						account: account_id.clone()
+					}),
+					topics: vec![],
+				},
+				EventRecord {
+					phase: Phase::Initialization,
+					event: RuntimeEvent::Balances(pallet_balances::Event::Endowed {
+						account: account_id.clone(),
+						free_balance: min_balance,
+					}),
+					topics: vec![],
+				},
+				EventRecord {
+					phase: Phase::Initialization,
+					event: RuntimeEvent::Balances(pallet_balances::Event::Transfer {
+						from: ALICE,
+						to: account_id.clone(),
+						amount: min_balance,
+					}),
+					topics: vec![],
+				},
+				EventRecord {
+					phase: Phase::Initialization,
+					event: RuntimeEvent::Balances(pallet_balances::Event::Transfer {
+						from: ALICE,
+						to: account_id.clone(),
+						amount: 50,
+					}),
+					topics: vec![],
+				},
+				EventRecord {
+					phase: Phase::Initialization,
+					event: RuntimeEvent::Contracts(crate::Event::Instantiated {
+						deployer: ALICE_ADDR,
+						contract: addr,
+					}),
+					topics: vec![],
+				},
+				EventRecord {
+					phase: Phase::Initialization,
+					event: RuntimeEvent::Contracts(
+						pallet_revive::Event::StorageDepositTransferredAndHeld {
+							from: ALICE_ADDR,
+							to: addr,
+							amount: test_utils::contract_info_storage_deposit(&addr),
+						}
+					),
+					topics: vec![],
+				},
+			]
+		);
+	});
+}
 
-	#[test]
-	fn upload_code_limit_too_low() {
-		let (wasm, _code_hash) = compile_module("dummy").unwrap();
-		let deposit_expected = expected_deposit(wasm.len());
-		let deposit_insufficient = deposit_expected.saturating_sub(1);
+#[test]
+fn storage_deposit_works() {
+	let (wasm, _code_hash) = compile_module("multi_store").unwrap();
+	ExtBuilder::default().existential_deposit(200).build().execute_with(|| {
+		let _ = <Test as Config>::Currency::set_balance(&ALICE, 1_000_000);
+
+		let Contract { addr, account_id } =
+			builder::bare_instantiate(Code::Upload(wasm)).build_and_unwrap_contract();
+
+		let mut deposit = test_utils::contract_info_storage_deposit(&addr);
+
+		// Drop previous events
+		initialize_block(2);
+
+		// Create storage
+		assert_ok!(builder::call(addr).value(42).data((50u32, 20u32).encode()).build());
+		// 4 is for creating 2 storage items
+		let charged0 = 4 + 50 + 20;
+		deposit += charged0;
+		assert_eq!(get_contract(&addr).total_deposit(), deposit);
+
+		// Add more storage (but also remove some)
+		assert_ok!(builder::call(addr).data((100u32, 10u32).encode()).build());
+		let charged1 = 50 - 10;
+		deposit += charged1;
+		assert_eq!(get_contract(&addr).total_deposit(), deposit);
+
+		// Remove more storage (but also add some)
+		assert_ok!(builder::call(addr).data((10u32, 20u32).encode()).build());
+		// -1 for numeric instability
+		let refunded0 = 90 - 10 - 1;
+		deposit -= refunded0;
+		assert_eq!(get_contract(&addr).total_deposit(), deposit);
+
+		assert_eq!(
+			System::events(),
+			vec![
+				EventRecord {
+					phase: Phase::Initialization,
+					event: RuntimeEvent::Balances(pallet_balances::Event::Transfer {
+						from: ALICE,
+						to: account_id.clone(),
+						amount: 42,
+					}),
+					topics: vec![],
+				},
+				EventRecord {
+					phase: Phase::Initialization,
+					event: RuntimeEvent::Contracts(crate::Event::Called {
+						caller: Origin::from_account_id(ALICE),
+						contract: addr,
+					}),
+					topics: vec![],
+				},
+				EventRecord {
+					phase: Phase::Initialization,
+					event: RuntimeEvent::Contracts(
+						pallet_revive::Event::StorageDepositTransferredAndHeld {
+							from: ALICE_ADDR,
+							to: addr,
+							amount: charged0,
+						}
+					),
+					topics: vec![],
+				},
+				EventRecord {
+					phase: Phase::Initialization,
+					event: RuntimeEvent::Contracts(crate::Event::Called {
+						caller: Origin::from_account_id(ALICE),
+						contract: addr,
+					}),
+					topics: vec![],
+				},
+				EventRecord {
+					phase: Phase::Initialization,
+					event: RuntimeEvent::Contracts(
+						pallet_revive::Event::StorageDepositTransferredAndHeld {
+							from: ALICE_ADDR,
+							to: addr,
+							amount: charged1,
+						}
+					),
+					topics: vec![],
+				},
+				EventRecord {
+					phase: Phase::Initialization,
+					event: RuntimeEvent::Contracts(crate::Event::Called {
+						caller: Origin::from_account_id(ALICE),
+						contract: addr,
+					}),
+					topics: vec![],
+				},
+				EventRecord {
+					phase: Phase::Initialization,
+					event: RuntimeEvent::Contracts(
+						pallet_revive::Event::StorageDepositTransferredAndReleased {
+							from: addr,
+							to: ALICE_ADDR,
+							amount: refunded0,
+						}
+					),
+					topics: vec![],
+				},
+			]
+		);
+	});
+}
 
-		ExtBuilder::default().existential_deposit(100).build().execute_with(|| {
-			let _ = <Test as Config>::Currency::set_balance(&ALICE, 1_000_000);
+#[test]
+fn storage_deposit_callee_works() {
+	let (wasm_caller, _code_hash_caller) = compile_module("call").unwrap();
+	let (wasm_callee, _code_hash_callee) = compile_module("store_call").unwrap();
+	ExtBuilder::default().existential_deposit(200).build().execute_with(|| {
+		let _ = <Test as Config>::Currency::set_balance(&ALICE, 1_000_000);
+		let min_balance = Contracts::min_balance();
+
+		// Create both contracts: Constructors do nothing.
+		let Contract { addr: addr_caller, .. } =
+			builder::bare_instantiate(Code::Upload(wasm_caller)).build_and_unwrap_contract();
+		let Contract { addr: addr_callee, account_id } =
+			builder::bare_instantiate(Code::Upload(wasm_callee)).build_and_unwrap_contract();
+
+		assert_ok!(builder::call(addr_caller).data((100u32, &addr_callee).encode()).build());
+
+		let callee = get_contract(&addr_callee);
+		let deposit = DepositPerByte::get() * 100 + DepositPerItem::get() * 1;
+
+		assert_eq!(test_utils::get_balance(&account_id), min_balance);
+		assert_eq!(
+			callee.total_deposit(),
+			deposit + test_utils::contract_info_storage_deposit(&addr_callee)
+		);
+	});
+}
 
-			// Drop previous events
-			initialize_block(2);
+#[test]
+fn set_code_extrinsic() {
+	let (wasm, code_hash) = compile_module("dummy").unwrap();
+	let (new_wasm, new_code_hash) = compile_module("crypto_hashes").unwrap();
 
-			assert_noop!(
-				Contracts::upload_code(RuntimeOrigin::signed(ALICE), wasm, deposit_insufficient,),
-				<Error<Test>>::StorageDepositLimitExhausted,
-			);
+	assert_ne!(code_hash, new_code_hash);
 
-			assert_eq!(System::events(), vec![]);
-		});
-	}
+	ExtBuilder::default().existential_deposit(100).build().execute_with(|| {
+		let _ = <Test as Config>::Currency::set_balance(&ALICE, 1_000_000);
 
-	#[test]
-	fn upload_code_not_enough_balance() {
-		let (wasm, _code_hash) = compile_module("dummy").unwrap();
-		let deposit_expected = expected_deposit(wasm.len());
-		let deposit_insufficient = deposit_expected.saturating_sub(1);
+		let Contract { addr, .. } =
+			builder::bare_instantiate(Code::Upload(wasm)).build_and_unwrap_contract();
 
-		ExtBuilder::default().existential_deposit(100).build().execute_with(|| {
-			let _ = <Test as Config>::Currency::set_balance(&ALICE, deposit_insufficient);
+		assert_ok!(Contracts::upload_code(
+			RuntimeOrigin::signed(ALICE),
+			new_wasm,
+			deposit_limit::<Test>(),
+		));
 
-			// Drop previous events
-			initialize_block(2);
+		// Drop previous events
+		initialize_block(2);
 
-			assert_noop!(
-				Contracts::upload_code(RuntimeOrigin::signed(ALICE), wasm, 1_000,),
-				<Error<Test>>::StorageDepositNotEnoughFunds,
-			);
+		assert_eq!(get_contract(&addr).code_hash, code_hash);
+		assert_refcount!(&code_hash, 1);
+		assert_refcount!(&new_code_hash, 0);
 
-			assert_eq!(System::events(), vec![]);
-		});
-	}
+		// only root can execute this extrinsic
+		assert_noop!(
+			Contracts::set_code(RuntimeOrigin::signed(ALICE), addr, new_code_hash),
+			sp_runtime::traits::BadOrigin,
+		);
+		assert_eq!(get_contract(&addr).code_hash, code_hash);
+		assert_refcount!(&code_hash, 1);
+		assert_refcount!(&new_code_hash, 0);
+		assert_eq!(System::events(), vec![]);
+
+		// contract must exist
+		assert_noop!(
+			Contracts::set_code(RuntimeOrigin::root(), BOB_ADDR, new_code_hash),
+			<Error<Test>>::ContractNotFound,
+		);
+		assert_eq!(get_contract(&addr).code_hash, code_hash);
+		assert_refcount!(&code_hash, 1);
+		assert_refcount!(&new_code_hash, 0);
+		assert_eq!(System::events(), vec![]);
+
+		// new code hash must exist
+		assert_noop!(
+			Contracts::set_code(RuntimeOrigin::root(), addr, Default::default()),
+			<Error<Test>>::CodeNotFound,
+		);
+		assert_eq!(get_contract(&addr).code_hash, code_hash);
+		assert_refcount!(&code_hash, 1);
+		assert_refcount!(&new_code_hash, 0);
+		assert_eq!(System::events(), vec![]);
+
+		// successful call
+		assert_ok!(Contracts::set_code(RuntimeOrigin::root(), addr, new_code_hash));
+		assert_eq!(get_contract(&addr).code_hash, new_code_hash);
+		assert_refcount!(&code_hash, 0);
+		assert_refcount!(&new_code_hash, 1);
+		assert_eq!(
+			System::events(),
+			vec![EventRecord {
+				phase: Phase::Initialization,
+				event: RuntimeEvent::Contracts(pallet_revive::Event::ContractCodeUpdated {
+					contract: addr,
+					new_code_hash,
+					old_code_hash: code_hash,
+				}),
+				topics: vec![],
+			},]
+		);
+	});
+}
 
-	#[test]
-	fn remove_code_works() {
-		let (wasm, code_hash) = compile_module("dummy").unwrap();
+#[test]
+fn slash_cannot_kill_account() {
+	let (wasm, _code_hash) = compile_module("dummy").unwrap();
+	ExtBuilder::default().existential_deposit(200).build().execute_with(|| {
+		let value = 700;
+		let _ = <Test as Config>::Currency::set_balance(&ALICE, 1_000_000);
+		let min_balance = Contracts::min_balance();
 
-		ExtBuilder::default().existential_deposit(100).build().execute_with(|| {
-			let _ = <Test as Config>::Currency::set_balance(&ALICE, 1_000_000);
+		let Contract { addr, account_id } = builder::bare_instantiate(Code::Upload(wasm))
+			.value(value)
+			.build_and_unwrap_contract();
 
-			// Drop previous events
-			initialize_block(2);
+		// Drop previous events
+		initialize_block(2);
 
-			assert_ok!(Contracts::upload_code(RuntimeOrigin::signed(ALICE), wasm, 1_000,));
-			// Ensure the contract was stored and get expected deposit amount to be reserved.
-			let deposit_expected = expected_deposit(ensure_stored(code_hash));
+		let info_deposit = test_utils::contract_info_storage_deposit(&addr);
 
-			assert_ok!(Contracts::remove_code(RuntimeOrigin::signed(ALICE), code_hash));
-			assert_eq!(
-				System::events(),
-				vec![
-					EventRecord {
-						phase: Phase::Initialization,
-						event: RuntimeEvent::Contracts(crate::Event::CodeStored {
-							code_hash,
-							deposit_held: deposit_expected,
-							uploader: ALICE_ADDR
-						}),
-						topics: vec![],
-					},
-					EventRecord {
-						phase: Phase::Initialization,
-						event: RuntimeEvent::Contracts(crate::Event::CodeRemoved {
-							code_hash,
-							deposit_released: deposit_expected,
-							remover: ALICE_ADDR
-						}),
-						topics: vec![],
-					},
-				]
-			);
-		});
-	}
+		assert_eq!(
+			test_utils::get_balance_on_hold(&HoldReason::StorageDepositReserve.into(), &account_id),
+			info_deposit
+		);
 
-	#[test]
-	fn remove_code_wrong_origin() {
-		let (wasm, code_hash) = compile_module("dummy").unwrap();
+		assert_eq!(
+			<Test as Config>::Currency::total_balance(&account_id),
+			info_deposit + value + min_balance
+		);
 
-		ExtBuilder::default().existential_deposit(100).build().execute_with(|| {
-			let _ = <Test as Config>::Currency::set_balance(&ALICE, 1_000_000);
+		// Try to destroy the account of the contract by slashing the total balance.
+		// The account does not get destroyed because slashing only affects the balance held
+		// under certain `reason`. Slashing can for example happen if the contract takes part
+		// in staking.
+		let _ = <Test as Config>::Currency::slash(
+			&HoldReason::StorageDepositReserve.into(),
+			&account_id,
+			<Test as Config>::Currency::total_balance(&account_id),
+		);
 
-			// Drop previous events
-			initialize_block(2);
+		// Slashing only removed the balance held.
+		assert_eq!(<Test as Config>::Currency::total_balance(&account_id), value + min_balance);
+	});
+}
 
-			assert_ok!(Contracts::upload_code(RuntimeOrigin::signed(ALICE), wasm, 1_000,));
-			// Ensure the contract was stored and get expected deposit amount to be reserved.
-			let deposit_expected = expected_deposit(ensure_stored(code_hash));
+#[test]
+fn contract_reverted() {
+	let (wasm, code_hash) = compile_module("return_with_data").unwrap();
 
-			assert_noop!(
-				Contracts::remove_code(RuntimeOrigin::signed(BOB), code_hash),
-				sp_runtime::traits::BadOrigin,
-			);
+	ExtBuilder::default().existential_deposit(100).build().execute_with(|| {
+		let _ = <Test as Config>::Currency::set_balance(&ALICE, 1_000_000);
+		let flags = ReturnFlags::REVERT;
+		let buffer = [4u8, 8, 15, 16, 23, 42];
+		let input = (flags.bits(), buffer).encode();
 
-			assert_eq!(
-				System::events(),
-				vec![EventRecord {
-					phase: Phase::Initialization,
-					event: RuntimeEvent::Contracts(crate::Event::CodeStored {
-						code_hash,
-						deposit_held: deposit_expected,
-						uploader: ALICE_ADDR
-					}),
-					topics: vec![],
-				},]
-			);
-		});
-	}
-
-	#[test]
-	fn remove_code_in_use() {
-		let (wasm, code_hash) = compile_module("dummy").unwrap();
-
-		ExtBuilder::default().existential_deposit(100).build().execute_with(|| {
-			let _ = <Test as Config>::Currency::set_balance(&ALICE, 1_000_000);
-
-			assert_ok!(builder::instantiate_with_code(wasm).build());
-
-			// Drop previous events
-			initialize_block(2);
-
-			assert_noop!(
-				Contracts::remove_code(RuntimeOrigin::signed(ALICE), code_hash),
-				<Error<Test>>::CodeInUse,
-			);
-
-			assert_eq!(System::events(), vec![]);
-		});
-	}
-
-	#[test]
-	fn remove_code_not_found() {
-		let (_wasm, code_hash) = compile_module("dummy").unwrap();
-
-		ExtBuilder::default().existential_deposit(100).build().execute_with(|| {
-			let _ = <Test as Config>::Currency::set_balance(&ALICE, 1_000_000);
-
-			// Drop previous events
-			initialize_block(2);
-
-			assert_noop!(
-				Contracts::remove_code(RuntimeOrigin::signed(ALICE), code_hash),
-				<Error<Test>>::CodeNotFound,
-			);
-
-			assert_eq!(System::events(), vec![]);
-		});
-	}
-
-	#[test]
-	fn instantiate_with_zero_balance_works() {
-		let (wasm, code_hash) = compile_module("dummy").unwrap();
-		ExtBuilder::default().existential_deposit(200).build().execute_with(|| {
-			let _ = <Test as Config>::Currency::set_balance(&ALICE, 1_000_000);
-			let min_balance = Contracts::min_balance();
-
-			// Drop previous events
-			initialize_block(2);
-
-			// Instantiate the BOB contract.
-			let Contract { addr, account_id } =
-				builder::bare_instantiate(Code::Upload(wasm)).build_and_unwrap_contract();
-
-			// Ensure the contract was stored and get expected deposit amount to be reserved.
-			let deposit_expected = expected_deposit(ensure_stored(code_hash));
-
-			// Make sure the account exists even though no free balance was send
-			assert_eq!(<Test as Config>::Currency::free_balance(&account_id), min_balance);
-			assert_eq!(
-				<Test as Config>::Currency::total_balance(&account_id),
-				min_balance + test_utils::contract_info_storage_deposit(&addr)
-			);
-
-			assert_eq!(
-				System::events(),
-				vec![
-					EventRecord {
-						phase: Phase::Initialization,
-						event: RuntimeEvent::Contracts(crate::Event::CodeStored {
-							code_hash,
-							deposit_held: deposit_expected,
-							uploader: ALICE_ADDR
-						}),
-						topics: vec![],
-					},
-					EventRecord {
-						phase: Phase::Initialization,
-						event: RuntimeEvent::System(frame_system::Event::NewAccount {
-							account: account_id.clone(),
-						}),
-						topics: vec![],
-					},
-					EventRecord {
-						phase: Phase::Initialization,
-						event: RuntimeEvent::Balances(pallet_balances::Event::Endowed {
-							account: account_id.clone(),
-							free_balance: min_balance,
-						}),
-						topics: vec![],
-					},
-					EventRecord {
-						phase: Phase::Initialization,
-						event: RuntimeEvent::Balances(pallet_balances::Event::Transfer {
-							from: ALICE,
-							to: account_id,
-							amount: min_balance,
-						}),
-						topics: vec![],
-					},
-					EventRecord {
-						phase: Phase::Initialization,
-						event: RuntimeEvent::Contracts(crate::Event::Instantiated {
-							deployer: ALICE_ADDR,
-							contract: addr,
-						}),
-						topics: vec![],
-					},
-					EventRecord {
-						phase: Phase::Initialization,
-						event: RuntimeEvent::Contracts(
-							pallet_revive::Event::StorageDepositTransferredAndHeld {
-								from: ALICE_ADDR,
-								to: addr,
-								amount: test_utils::contract_info_storage_deposit(&addr),
-							}
-						),
-						topics: vec![],
-					},
-				]
-			);
-		});
-	}
-
-	#[test]
-	fn instantiate_with_below_existential_deposit_works() {
-		let (wasm, code_hash) = compile_module("dummy").unwrap();
-		ExtBuilder::default().existential_deposit(200).build().execute_with(|| {
-			let _ = <Test as Config>::Currency::set_balance(&ALICE, 1_000_000);
-			let min_balance = Contracts::min_balance();
-			let value = 50;
-
-			// Drop previous events
-			initialize_block(2);
-
-			// Instantiate the BOB contract.
-			let Contract { addr, account_id } = builder::bare_instantiate(Code::Upload(wasm))
-				.value(value)
-				.build_and_unwrap_contract();
-
-			// Ensure the contract was stored and get expected deposit amount to be reserved.
-			let deposit_expected = expected_deposit(ensure_stored(code_hash));
-			// Make sure the account exists even though not enough free balance was send
-			assert_eq!(<Test as Config>::Currency::free_balance(&account_id), min_balance + value);
-			assert_eq!(
-				<Test as Config>::Currency::total_balance(&account_id),
-				min_balance + value + test_utils::contract_info_storage_deposit(&addr)
-			);
-
-			assert_eq!(
-				System::events(),
-				vec![
-					EventRecord {
-						phase: Phase::Initialization,
-						event: RuntimeEvent::Contracts(crate::Event::CodeStored {
-							code_hash,
-							deposit_held: deposit_expected,
-							uploader: ALICE_ADDR
-						}),
-						topics: vec![],
-					},
-					EventRecord {
-						phase: Phase::Initialization,
-						event: RuntimeEvent::System(frame_system::Event::NewAccount {
-							account: account_id.clone()
-						}),
-						topics: vec![],
-					},
-					EventRecord {
-						phase: Phase::Initialization,
-						event: RuntimeEvent::Balances(pallet_balances::Event::Endowed {
-							account: account_id.clone(),
-							free_balance: min_balance,
-						}),
-						topics: vec![],
-					},
-					EventRecord {
-						phase: Phase::Initialization,
-						event: RuntimeEvent::Balances(pallet_balances::Event::Transfer {
-							from: ALICE,
-							to: account_id.clone(),
-							amount: min_balance,
-						}),
-						topics: vec![],
-					},
-					EventRecord {
-						phase: Phase::Initialization,
-						event: RuntimeEvent::Balances(pallet_balances::Event::Transfer {
-							from: ALICE,
-							to: account_id.clone(),
-							amount: 50,
-						}),
-						topics: vec![],
-					},
-					EventRecord {
-						phase: Phase::Initialization,
-						event: RuntimeEvent::Contracts(crate::Event::Instantiated {
-							deployer: ALICE_ADDR,
-							contract: addr,
-						}),
-						topics: vec![],
-					},
-					EventRecord {
-						phase: Phase::Initialization,
-						event: RuntimeEvent::Contracts(
-							pallet_revive::Event::StorageDepositTransferredAndHeld {
-								from: ALICE_ADDR,
-								to: addr,
-								amount: test_utils::contract_info_storage_deposit(&addr),
-							}
-						),
-						topics: vec![],
-					},
-				]
-			);
-		});
-	}
-
-	#[test]
-	fn storage_deposit_works() {
-		let (wasm, _code_hash) = compile_module("multi_store").unwrap();
-		ExtBuilder::default().existential_deposit(200).build().execute_with(|| {
-			let _ = <Test as Config>::Currency::set_balance(&ALICE, 1_000_000);
-
-			let Contract { addr, account_id } =
-				builder::bare_instantiate(Code::Upload(wasm)).build_and_unwrap_contract();
-
-			let mut deposit = test_utils::contract_info_storage_deposit(&addr);
-
-			// Drop previous events
-			initialize_block(2);
-
-			// Create storage
-			assert_ok!(builder::call(addr).value(42).data((50u32, 20u32).encode()).build());
-			// 4 is for creating 2 storage items
-			let charged0 = 4 + 50 + 20;
-			deposit += charged0;
-			assert_eq!(get_contract(&addr).total_deposit(), deposit);
-
-			// Add more storage (but also remove some)
-			assert_ok!(builder::call(addr).data((100u32, 10u32).encode()).build());
-			let charged1 = 50 - 10;
-			deposit += charged1;
-			assert_eq!(get_contract(&addr).total_deposit(), deposit);
-
-			// Remove more storage (but also add some)
-			assert_ok!(builder::call(addr).data((10u32, 20u32).encode()).build());
-			// -1 for numeric instability
-			let refunded0 = 90 - 10 - 1;
-			deposit -= refunded0;
-			assert_eq!(get_contract(&addr).total_deposit(), deposit);
-
-			assert_eq!(
-				System::events(),
-				vec![
-					EventRecord {
-						phase: Phase::Initialization,
-						event: RuntimeEvent::Balances(pallet_balances::Event::Transfer {
-							from: ALICE,
-							to: account_id.clone(),
-							amount: 42,
-						}),
-						topics: vec![],
-					},
-					EventRecord {
-						phase: Phase::Initialization,
-						event: RuntimeEvent::Contracts(crate::Event::Called {
-							caller: Origin::from_account_id(ALICE),
-							contract: addr,
-						}),
-						topics: vec![],
-					},
-					EventRecord {
-						phase: Phase::Initialization,
-						event: RuntimeEvent::Contracts(
-							pallet_revive::Event::StorageDepositTransferredAndHeld {
-								from: ALICE_ADDR,
-								to: addr,
-								amount: charged0,
-							}
-						),
-						topics: vec![],
-					},
-					EventRecord {
-						phase: Phase::Initialization,
-						event: RuntimeEvent::Contracts(crate::Event::Called {
-							caller: Origin::from_account_id(ALICE),
-							contract: addr,
-						}),
-						topics: vec![],
-					},
-					EventRecord {
-						phase: Phase::Initialization,
-						event: RuntimeEvent::Contracts(
-							pallet_revive::Event::StorageDepositTransferredAndHeld {
-								from: ALICE_ADDR,
-								to: addr,
-								amount: charged1,
-							}
-						),
-						topics: vec![],
-					},
-					EventRecord {
-						phase: Phase::Initialization,
-						event: RuntimeEvent::Contracts(crate::Event::Called {
-							caller: Origin::from_account_id(ALICE),
-							contract: addr,
-						}),
-						topics: vec![],
-					},
-					EventRecord {
-						phase: Phase::Initialization,
-						event: RuntimeEvent::Contracts(
-							pallet_revive::Event::StorageDepositTransferredAndReleased {
-								from: addr,
-								to: ALICE_ADDR,
-								amount: refunded0,
-							}
-						),
-						topics: vec![],
-					},
-				]
-			);
-		});
-	}
-
-	#[test]
-	fn storage_deposit_callee_works() {
-		let (wasm_caller, _code_hash_caller) = compile_module("call").unwrap();
-		let (wasm_callee, _code_hash_callee) = compile_module("store_call").unwrap();
-		ExtBuilder::default().existential_deposit(200).build().execute_with(|| {
-			let _ = <Test as Config>::Currency::set_balance(&ALICE, 1_000_000);
-			let min_balance = Contracts::min_balance();
-
-			// Create both contracts: Constructors do nothing.
-			let Contract { addr: addr_caller, .. } =
-				builder::bare_instantiate(Code::Upload(wasm_caller)).build_and_unwrap_contract();
-			let Contract { addr: addr_callee, account_id } =
-				builder::bare_instantiate(Code::Upload(wasm_callee)).build_and_unwrap_contract();
-
-			assert_ok!(builder::call(addr_caller).data((100u32, &addr_callee).encode()).build());
-
-			let callee = get_contract(&addr_callee);
-			let deposit = DepositPerByte::get() * 100 + DepositPerItem::get() * 1;
-
-			assert_eq!(test_utils::get_balance(&account_id), min_balance);
-			assert_eq!(
-				callee.total_deposit(),
-				deposit + test_utils::contract_info_storage_deposit(&addr_callee)
-			);
-		});
-	}
-
-	#[test]
-	fn set_code_extrinsic() {
-		let (wasm, code_hash) = compile_module("dummy").unwrap();
-		let (new_wasm, new_code_hash) = compile_module("crypto_hashes").unwrap();
-
-		assert_ne!(code_hash, new_code_hash);
+		// We just upload the code for later use
+		assert_ok!(Contracts::upload_code(
+			RuntimeOrigin::signed(ALICE),
+			wasm.clone(),
+			deposit_limit::<Test>(),
+		));
+
+		// Calling extrinsic: revert leads to an error
+		assert_err_ignore_postinfo!(
+			builder::instantiate(code_hash).data(input.clone()).build(),
+			<Error<Test>>::ContractReverted,
+		);
 
-		ExtBuilder::default().existential_deposit(100).build().execute_with(|| {
-			let _ = <Test as Config>::Currency::set_balance(&ALICE, 1_000_000);
+		// Calling extrinsic: revert leads to an error
+		assert_err_ignore_postinfo!(
+			builder::instantiate_with_code(wasm).data(input.clone()).build(),
+			<Error<Test>>::ContractReverted,
+		);
 
-			let Contract { addr, .. } =
-				builder::bare_instantiate(Code::Upload(wasm)).build_and_unwrap_contract();
+		// Calling directly: revert leads to success but the flags indicate the error
+		// This is just a different way of transporting the error that allows the read out
+		// the `data` which is only there on success. Obviously, the contract isn't
+		// instantiated.
+		let result = builder::bare_instantiate(Code::Existing(code_hash))
+			.data(input.clone())
+			.build_and_unwrap_result();
+		assert_eq!(result.result.flags, flags);
+		assert_eq!(result.result.data, buffer);
+		assert!(!<ContractInfoOf<Test>>::contains_key(result.addr));
+
+		// Pass empty flags and therefore successfully instantiate the contract for later use.
+		let Contract { addr, .. } = builder::bare_instantiate(Code::Existing(code_hash))
+			.data(ReturnFlags::empty().bits().encode())
+			.build_and_unwrap_contract();
+
+		// Calling extrinsic: revert leads to an error
+		assert_err_ignore_postinfo!(
+			builder::call(addr).data(input.clone()).build(),
+			<Error<Test>>::ContractReverted,
+		);
 
-			assert_ok!(Contracts::upload_code(
-				RuntimeOrigin::signed(ALICE),
-				new_wasm,
-				deposit_limit::<Test>(),
-			));
+		// Calling directly: revert leads to success but the flags indicate the error
+		let result = builder::bare_call(addr).data(input).build_and_unwrap_result();
+		assert_eq!(result.flags, flags);
+		assert_eq!(result.data, buffer);
+	});
+}
 
-			// Drop previous events
-			initialize_block(2);
+#[test]
+fn set_code_hash() {
+	let (wasm, code_hash) = compile_module("set_code_hash").unwrap();
+	let (new_wasm, new_code_hash) = compile_module("new_set_code_hash_contract").unwrap();
 
-			assert_eq!(get_contract(&addr).code_hash, code_hash);
-			assert_refcount!(&code_hash, 1);
-			assert_refcount!(&new_code_hash, 0);
+	ExtBuilder::default().existential_deposit(100).build().execute_with(|| {
+		let _ = <Test as Config>::Currency::set_balance(&ALICE, 1_000_000);
 
-			// only root can execute this extrinsic
-			assert_noop!(
-				Contracts::set_code(RuntimeOrigin::signed(ALICE), addr, new_code_hash),
-				sp_runtime::traits::BadOrigin,
-			);
-			assert_eq!(get_contract(&addr).code_hash, code_hash);
-			assert_refcount!(&code_hash, 1);
-			assert_refcount!(&new_code_hash, 0);
-			assert_eq!(System::events(), vec![]);
-
-			// contract must exist
-			assert_noop!(
-				Contracts::set_code(RuntimeOrigin::root(), BOB_ADDR, new_code_hash),
-				<Error<Test>>::ContractNotFound,
-			);
-			assert_eq!(get_contract(&addr).code_hash, code_hash);
-			assert_refcount!(&code_hash, 1);
-			assert_refcount!(&new_code_hash, 0);
-			assert_eq!(System::events(), vec![]);
-
-			// new code hash must exist
-			assert_noop!(
-				Contracts::set_code(RuntimeOrigin::root(), addr, Default::default()),
-				<Error<Test>>::CodeNotFound,
-			);
-			assert_eq!(get_contract(&addr).code_hash, code_hash);
-			assert_refcount!(&code_hash, 1);
-			assert_refcount!(&new_code_hash, 0);
-			assert_eq!(System::events(), vec![]);
-
-			// successful call
-			assert_ok!(Contracts::set_code(RuntimeOrigin::root(), addr, new_code_hash));
-			assert_eq!(get_contract(&addr).code_hash, new_code_hash);
-			assert_refcount!(&code_hash, 0);
-			assert_refcount!(&new_code_hash, 1);
-			assert_eq!(
-				System::events(),
-				vec![EventRecord {
+		// Instantiate the 'caller'
+		let Contract { addr: contract_addr, .. } = builder::bare_instantiate(Code::Upload(wasm))
+			.value(300_000)
+			.build_and_unwrap_contract();
+		// upload new code
+		assert_ok!(Contracts::upload_code(
+			RuntimeOrigin::signed(ALICE),
+			new_wasm.clone(),
+			deposit_limit::<Test>(),
+		));
+
+		System::reset_events();
+
+		// First call sets new code_hash and returns 1
+		let result = builder::bare_call(contract_addr)
+			.data(new_code_hash.as_ref().to_vec())
+			.debug(DebugInfo::UnsafeDebug)
+			.build_and_unwrap_result();
+		assert_return_code!(result, 1);
+
+		// Second calls new contract code that returns 2
+		let result = builder::bare_call(contract_addr)
+			.debug(DebugInfo::UnsafeDebug)
+			.build_and_unwrap_result();
+		assert_return_code!(result, 2);
+
+		// Checking for the last event only
+		assert_eq!(
+			&System::events(),
+			&[
+				EventRecord {
 					phase: Phase::Initialization,
-					event: RuntimeEvent::Contracts(pallet_revive::Event::ContractCodeUpdated {
-						contract: addr,
+					event: RuntimeEvent::Contracts(crate::Event::ContractCodeUpdated {
+						contract: contract_addr,
 						new_code_hash,
 						old_code_hash: code_hash,
 					}),
 					topics: vec![],
-				},]
-			);
-		});
-	}
-
-	#[test]
-	fn slash_cannot_kill_account() {
-		let (wasm, _code_hash) = compile_module("dummy").unwrap();
-		ExtBuilder::default().existential_deposit(200).build().execute_with(|| {
-			let value = 700;
-			let _ = <Test as Config>::Currency::set_balance(&ALICE, 1_000_000);
-			let min_balance = Contracts::min_balance();
-
-			let Contract { addr, account_id } = builder::bare_instantiate(Code::Upload(wasm))
-				.value(value)
-				.build_and_unwrap_contract();
-
-			// Drop previous events
-			initialize_block(2);
-
-			let info_deposit = test_utils::contract_info_storage_deposit(&addr);
-
-			assert_eq!(
-				test_utils::get_balance_on_hold(
-					&HoldReason::StorageDepositReserve.into(),
-					&account_id
-				),
-				info_deposit
-			);
-
-			assert_eq!(
-				<Test as Config>::Currency::total_balance(&account_id),
-				info_deposit + value + min_balance
-			);
-
-			// Try to destroy the account of the contract by slashing the total balance.
-			// The account does not get destroyed because slashing only affects the balance held
-			// under certain `reason`. Slashing can for example happen if the contract takes part
-			// in staking.
-			let _ = <Test as Config>::Currency::slash(
-				&HoldReason::StorageDepositReserve.into(),
-				&account_id,
-				<Test as Config>::Currency::total_balance(&account_id),
-			);
-
-			// Slashing only removed the balance held.
-			assert_eq!(<Test as Config>::Currency::total_balance(&account_id), value + min_balance);
-		});
-	}
-
-	#[test]
-	fn contract_reverted() {
-		let (wasm, code_hash) = compile_module("return_with_data").unwrap();
-
-		ExtBuilder::default().existential_deposit(100).build().execute_with(|| {
-			let _ = <Test as Config>::Currency::set_balance(&ALICE, 1_000_000);
-			let flags = ReturnFlags::REVERT;
-			let buffer = [4u8, 8, 15, 16, 23, 42];
-			let input = (flags.bits(), buffer).encode();
-
-			// We just upload the code for later use
-			assert_ok!(Contracts::upload_code(
-				RuntimeOrigin::signed(ALICE),
-				wasm.clone(),
-				deposit_limit::<Test>(),
-			));
-
-			// Calling extrinsic: revert leads to an error
-			assert_err_ignore_postinfo!(
-				builder::instantiate(code_hash).data(input.clone()).build(),
-				<Error<Test>>::ContractReverted,
-			);
-
-			// Calling extrinsic: revert leads to an error
-			assert_err_ignore_postinfo!(
-				builder::instantiate_with_code(wasm).data(input.clone()).build(),
-				<Error<Test>>::ContractReverted,
-			);
-
-			// Calling directly: revert leads to success but the flags indicate the error
-			// This is just a different way of transporting the error that allows the read out
-			// the `data` which is only there on success. Obviously, the contract isn't
-			// instantiated.
-			let result = builder::bare_instantiate(Code::Existing(code_hash))
-				.data(input.clone())
-				.build_and_unwrap_result();
-			assert_eq!(result.result.flags, flags);
-			assert_eq!(result.result.data, buffer);
-			assert!(!<ContractInfoOf<Test>>::contains_key(result.addr));
-
-			// Pass empty flags and therefore successfully instantiate the contract for later use.
-			let Contract { addr, .. } = builder::bare_instantiate(Code::Existing(code_hash))
-				.data(ReturnFlags::empty().bits().encode())
-				.build_and_unwrap_contract();
-
-			// Calling extrinsic: revert leads to an error
-			assert_err_ignore_postinfo!(
-				builder::call(addr).data(input.clone()).build(),
-				<Error<Test>>::ContractReverted,
-			);
-
-			// Calling directly: revert leads to success but the flags indicate the error
-			let result = builder::bare_call(addr).data(input).build_and_unwrap_result();
-			assert_eq!(result.flags, flags);
-			assert_eq!(result.data, buffer);
-		});
-	}
-
-	#[test]
-	fn set_code_hash() {
-		let (wasm, code_hash) = compile_module("set_code_hash").unwrap();
-		let (new_wasm, new_code_hash) = compile_module("new_set_code_hash_contract").unwrap();
-
-		ExtBuilder::default().existential_deposit(100).build().execute_with(|| {
-			let _ = <Test as Config>::Currency::set_balance(&ALICE, 1_000_000);
-
-			// Instantiate the 'caller'
-			let Contract { addr: contract_addr, .. } =
-				builder::bare_instantiate(Code::Upload(wasm))
-					.value(300_000)
-					.build_and_unwrap_contract();
-			// upload new code
-			assert_ok!(Contracts::upload_code(
-				RuntimeOrigin::signed(ALICE),
-				new_wasm.clone(),
-				deposit_limit::<Test>(),
-			));
-
-			System::reset_events();
-
-			// First call sets new code_hash and returns 1
-			let result = builder::bare_call(contract_addr)
-				.data(new_code_hash.as_ref().to_vec())
-				.debug(DebugInfo::UnsafeDebug)
-				.build_and_unwrap_result();
-			assert_return_code!(result, 1);
-
-			// Second calls new contract code that returns 2
-			let result = builder::bare_call(contract_addr)
-				.debug(DebugInfo::UnsafeDebug)
-				.build_and_unwrap_result();
-			assert_return_code!(result, 2);
-
-			// Checking for the last event only
-			assert_eq!(
-				&System::events(),
-				&[
-					EventRecord {
-						phase: Phase::Initialization,
-						event: RuntimeEvent::Contracts(crate::Event::ContractCodeUpdated {
-							contract: contract_addr,
-							new_code_hash,
-							old_code_hash: code_hash,
-						}),
-						topics: vec![],
-					},
-					EventRecord {
-						phase: Phase::Initialization,
-						event: RuntimeEvent::Contracts(crate::Event::Called {
-							caller: Origin::from_account_id(ALICE),
-							contract: contract_addr,
-						}),
-						topics: vec![],
-					},
-					EventRecord {
-						phase: Phase::Initialization,
-						event: RuntimeEvent::Contracts(crate::Event::Called {
-							caller: Origin::from_account_id(ALICE),
-							contract: contract_addr,
-						}),
-						topics: vec![],
-					},
-				],
-			);
-		});
-	}
-
-	#[test]
-	fn storage_deposit_limit_is_enforced() {
-		let (wasm, _code_hash) = compile_module("store_call").unwrap();
-		ExtBuilder::default().existential_deposit(200).build().execute_with(|| {
-			let _ = <Test as Config>::Currency::set_balance(&ALICE, 1_000_000);
-			let min_balance = Contracts::min_balance();
-
-			// Setting insufficient storage_deposit should fail.
-			assert_err!(
-				builder::bare_instantiate(Code::Upload(wasm.clone()))
-					// expected deposit is 2 * ed + 3 for the call
-					.storage_deposit_limit((2 * min_balance + 3 - 1).into())
-					.build()
-					.result,
-				<Error<Test>>::StorageDepositLimitExhausted,
-			);
+				},
+				EventRecord {
+					phase: Phase::Initialization,
+					event: RuntimeEvent::Contracts(crate::Event::Called {
+						caller: Origin::from_account_id(ALICE),
+						contract: contract_addr,
+					}),
+					topics: vec![],
+				},
+				EventRecord {
+					phase: Phase::Initialization,
+					event: RuntimeEvent::Contracts(crate::Event::Called {
+						caller: Origin::from_account_id(ALICE),
+						contract: contract_addr,
+					}),
+					topics: vec![],
+				},
+			],
+		);
+	});
+}
 
-			// Instantiate the BOB contract.
-			let Contract { addr, account_id } =
-				builder::bare_instantiate(Code::Upload(wasm)).build_and_unwrap_contract();
+#[test]
+fn storage_deposit_limit_is_enforced() {
+	let (wasm, _code_hash) = compile_module("store_call").unwrap();
+	ExtBuilder::default().existential_deposit(200).build().execute_with(|| {
+		let _ = <Test as Config>::Currency::set_balance(&ALICE, 1_000_000);
+		let min_balance = Contracts::min_balance();
+
+		// Setting insufficient storage_deposit should fail.
+		assert_err!(
+			builder::bare_instantiate(Code::Upload(wasm.clone()))
+				// expected deposit is 2 * ed + 3 for the call
+				.storage_deposit_limit((2 * min_balance + 3 - 1).into())
+				.build()
+				.result,
+			<Error<Test>>::StorageDepositLimitExhausted,
+		);
 
-			let info_deposit = test_utils::contract_info_storage_deposit(&addr);
-			// Check that the BOB contract has been instantiated and has the minimum balance
-			assert_eq!(get_contract(&addr).total_deposit(), info_deposit);
-			assert_eq!(
-				<Test as Config>::Currency::total_balance(&account_id),
-				info_deposit + min_balance
-			);
+		// Instantiate the BOB contract.
+		let Contract { addr, account_id } =
+			builder::bare_instantiate(Code::Upload(wasm)).build_and_unwrap_contract();
 
-			// Create 1 byte of storage with a price of per byte,
-			// setting insufficient deposit limit, as it requires 3 Balance:
-			// 2 for the item added + 1 for the new storage item.
-			assert_err_ignore_postinfo!(
-				builder::call(addr)
-					.storage_deposit_limit(2)
-					.data(1u32.to_le_bytes().to_vec())
-					.build(),
-				<Error<Test>>::StorageDepositLimitExhausted,
-			);
+		let info_deposit = test_utils::contract_info_storage_deposit(&addr);
+		// Check that the BOB contract has been instantiated and has the minimum balance
+		assert_eq!(get_contract(&addr).total_deposit(), info_deposit);
+		assert_eq!(
+			<Test as Config>::Currency::total_balance(&account_id),
+			info_deposit + min_balance
+		);
 
-			// Create 1 byte of storage, should cost 3 Balance:
-			// 2 for the item added + 1 for the new storage item.
-			// Should pass as it fallbacks to DefaultDepositLimit.
-			assert_ok!(builder::call(addr)
-				.storage_deposit_limit(3)
+		// Create 1 byte of storage with a price of per byte,
+		// setting insufficient deposit limit, as it requires 3 Balance:
+		// 2 for the item added + 1 for the new storage item.
+		assert_err_ignore_postinfo!(
+			builder::call(addr)
+				.storage_deposit_limit(2)
 				.data(1u32.to_le_bytes().to_vec())
-				.build());
-
-			// Use 4 more bytes of the storage for the same item, which requires 4 Balance.
-			// Should fail as DefaultDepositLimit is 3 and hence isn't enough.
-			assert_err_ignore_postinfo!(
-				builder::call(addr)
-					.storage_deposit_limit(3)
-					.data(5u32.to_le_bytes().to_vec())
-					.build(),
-				<Error<Test>>::StorageDepositLimitExhausted,
-			);
-		});
-	}
-
-	#[test]
-	fn deposit_limit_in_nested_calls() {
-		let (wasm_caller, _code_hash_caller) = compile_module("create_storage_and_call").unwrap();
-		let (wasm_callee, _code_hash_callee) = compile_module("store_call").unwrap();
-		ExtBuilder::default().existential_deposit(200).build().execute_with(|| {
-			let _ = <Test as Config>::Currency::set_balance(&ALICE, 1_000_000);
+				.build(),
+			<Error<Test>>::StorageDepositLimitExhausted,
+		);
 
-			// Create both contracts: Constructors do nothing.
-			let Contract { addr: addr_caller, .. } =
-				builder::bare_instantiate(Code::Upload(wasm_caller)).build_and_unwrap_contract();
-			let Contract { addr: addr_callee, .. } =
-				builder::bare_instantiate(Code::Upload(wasm_callee)).build_and_unwrap_contract();
+		// Create 1 byte of storage, should cost 3 Balance:
+		// 2 for the item added + 1 for the new storage item.
+		// Should pass as it fallbacks to DefaultDepositLimit.
+		assert_ok!(builder::call(addr)
+			.storage_deposit_limit(3)
+			.data(1u32.to_le_bytes().to_vec())
+			.build());
+
+		// Use 4 more bytes of the storage for the same item, which requires 4 Balance.
+		// Should fail as DefaultDepositLimit is 3 and hence isn't enough.
+		assert_err_ignore_postinfo!(
+			builder::call(addr)
+				.storage_deposit_limit(3)
+				.data(5u32.to_le_bytes().to_vec())
+				.build(),
+			<Error<Test>>::StorageDepositLimitExhausted,
+		);
+	});
+}
 
-			// Create 100 bytes of storage with a price of per byte
-			// This is 100 Balance + 2 Balance for the item
-			assert_ok!(builder::call(addr_callee)
-				.storage_deposit_limit(102)
-				.data(100u32.to_le_bytes().to_vec())
-				.build());
-
-			// We do not remove any storage but add a storage item of 12 bytes in the caller
-			// contract. This would cost 12 + 2 = 14 Balance.
-			// The nested call doesn't get a special limit, which is set by passing 0 to it.
-			// This should fail as the specified parent's limit is less than the cost: 13 <
-			// 14.
-			assert_err_ignore_postinfo!(
-				builder::call(addr_caller)
-					.storage_deposit_limit(13)
-					.data((100u32, &addr_callee, U256::from(0u64)).encode())
-					.build(),
-				<Error<Test>>::StorageDepositLimitExhausted,
-			);
+#[test]
+fn deposit_limit_in_nested_calls() {
+	let (wasm_caller, _code_hash_caller) = compile_module("create_storage_and_call").unwrap();
+	let (wasm_callee, _code_hash_callee) = compile_module("store_call").unwrap();
+	ExtBuilder::default().existential_deposit(200).build().execute_with(|| {
+		let _ = <Test as Config>::Currency::set_balance(&ALICE, 1_000_000);
+
+		// Create both contracts: Constructors do nothing.
+		let Contract { addr: addr_caller, .. } =
+			builder::bare_instantiate(Code::Upload(wasm_caller)).build_and_unwrap_contract();
+		let Contract { addr: addr_callee, .. } =
+			builder::bare_instantiate(Code::Upload(wasm_callee)).build_and_unwrap_contract();
+
+		// Create 100 bytes of storage with a price of per byte
+		// This is 100 Balance + 2 Balance for the item
+		assert_ok!(builder::call(addr_callee)
+			.storage_deposit_limit(102)
+			.data(100u32.to_le_bytes().to_vec())
+			.build());
+
+		// We do not remove any storage but add a storage item of 12 bytes in the caller
+		// contract. This would cost 12 + 2 = 14 Balance.
+		// The nested call doesn't get a special limit, which is set by passing 0 to it.
+		// This should fail as the specified parent's limit is less than the cost: 13 <
+		// 14.
+		assert_err_ignore_postinfo!(
+			builder::call(addr_caller)
+				.storage_deposit_limit(13)
+				.data((100u32, &addr_callee, U256::from(0u64)).encode())
+				.build(),
+			<Error<Test>>::StorageDepositLimitExhausted,
+		);
 
-			// Now we specify the parent's limit high enough to cover the caller's storage
-			// additions. However, we use a single byte more in the callee, hence the storage
-			// deposit should be 15 Balance.
-			// The nested call doesn't get a special limit, which is set by passing 0 to it.
-			// This should fail as the specified parent's limit is less than the cost: 14
-			// < 15.
-			assert_err_ignore_postinfo!(
-				builder::call(addr_caller)
-					.storage_deposit_limit(14)
-					.data((101u32, &addr_callee, U256::from(0u64)).encode())
-					.build(),
-				<Error<Test>>::StorageDepositLimitExhausted,
-			);
+		// Now we specify the parent's limit high enough to cover the caller's storage
+		// additions. However, we use a single byte more in the callee, hence the storage
+		// deposit should be 15 Balance.
+		// The nested call doesn't get a special limit, which is set by passing 0 to it.
+		// This should fail as the specified parent's limit is less than the cost: 14
+		// < 15.
+		assert_err_ignore_postinfo!(
+			builder::call(addr_caller)
+				.storage_deposit_limit(14)
+				.data((101u32, &addr_callee, U256::from(0u64)).encode())
+				.build(),
+			<Error<Test>>::StorageDepositLimitExhausted,
+		);
 
-			// Now we specify the parent's limit high enough to cover both the caller's and callee's
-			// storage additions. However, we set a special deposit limit of 1 Balance for the
-			// nested call. This should fail as callee adds up 2 bytes to the storage, meaning
-			// that the nested call should have a deposit limit of at least 2 Balance. The
-			// sub-call should be rolled back, which is covered by the next test case.
-			assert_err_ignore_postinfo!(
-				builder::call(addr_caller)
-					.storage_deposit_limit(16)
-					.data((102u32, &addr_callee, U256::from(1u64)).encode())
-					.build(),
-				<Error<Test>>::StorageDepositLimitExhausted,
-			);
+		// Now we specify the parent's limit high enough to cover both the caller's and callee's
+		// storage additions. However, we set a special deposit limit of 1 Balance for the
+		// nested call. This should fail as callee adds up 2 bytes to the storage, meaning
+		// that the nested call should have a deposit limit of at least 2 Balance. The
+		// sub-call should be rolled back, which is covered by the next test case.
+		assert_err_ignore_postinfo!(
+			builder::call(addr_caller)
+				.storage_deposit_limit(16)
+				.data((102u32, &addr_callee, U256::from(1u64)).encode())
+				.build(),
+			<Error<Test>>::StorageDepositLimitExhausted,
+		);
 
-			// Refund in the callee contract but not enough to cover the 14 Balance required by the
-			// caller. Note that if previous sub-call wouldn't roll back, this call would pass
-			// making the test case fail. We don't set a special limit for the nested call here.
-			assert_err_ignore_postinfo!(
-				builder::call(addr_caller)
-					.storage_deposit_limit(0)
-					.data((87u32, &addr_callee, U256::from(0u64)).encode())
-					.build(),
-				<Error<Test>>::StorageDepositLimitExhausted,
-			);
+		// Refund in the callee contract but not enough to cover the 14 Balance required by the
+		// caller. Note that if previous sub-call wouldn't roll back, this call would pass
+		// making the test case fail. We don't set a special limit for the nested call here.
+		assert_err_ignore_postinfo!(
+			builder::call(addr_caller)
+				.storage_deposit_limit(0)
+				.data((87u32, &addr_callee, U256::from(0u64)).encode())
+				.build(),
+			<Error<Test>>::StorageDepositLimitExhausted,
+		);
 
-			let _ = <Test as Config>::Currency::set_balance(&ALICE, 511);
+		let _ = <Test as Config>::Currency::set_balance(&ALICE, 511);
 
-			// Require more than the sender's balance.
-			// We don't set a special limit for the nested call.
-			assert_err_ignore_postinfo!(
-				builder::call(addr_caller)
-					.data((512u32, &addr_callee, U256::from(1u64)).encode())
-					.build(),
-				<Error<Test>>::StorageDepositLimitExhausted,
-			);
+		// Require more than the sender's balance.
+		// We don't set a special limit for the nested call.
+		assert_err_ignore_postinfo!(
+			builder::call(addr_caller)
+				.data((512u32, &addr_callee, U256::from(1u64)).encode())
+				.build(),
+			<Error<Test>>::StorageDepositLimitExhausted,
+		);
 
-			// Same as above but allow for the additional deposit of 1 Balance in parent.
-			// We set the special deposit limit of 1 Balance for the nested call, which isn't
-			// enforced as callee frees up storage. This should pass.
-			assert_ok!(builder::call(addr_caller)
-				.storage_deposit_limit(1)
-				.data((87u32, &addr_callee, U256::from(1u64)).encode())
-				.build());
-		});
-	}
+		// Same as above but allow for the additional deposit of 1 Balance in parent.
+		// We set the special deposit limit of 1 Balance for the nested call, which isn't
+		// enforced as callee frees up storage. This should pass.
+		assert_ok!(builder::call(addr_caller)
+			.storage_deposit_limit(1)
+			.data((87u32, &addr_callee, U256::from(1u64)).encode())
+			.build());
+	});
+}
 
-	#[test]
-	fn deposit_limit_in_nested_instantiate() {
-		let (wasm_caller, _code_hash_caller) =
-			compile_module("create_storage_and_instantiate").unwrap();
-		let (wasm_callee, code_hash_callee) = compile_module("store_deploy").unwrap();
-		const ED: u64 = 5;
-		ExtBuilder::default().existential_deposit(ED).build().execute_with(|| {
-			let _ = <Test as Config>::Currency::set_balance(&ALICE, 1_000_000);
-			let _ = <Test as Config>::Currency::set_balance(&BOB, 1_000_000);
-			// Create caller contract
-			let Contract { addr: addr_caller, account_id: caller_id } =
-				builder::bare_instantiate(Code::Upload(wasm_caller))
-					.value(10_000u64) // this balance is later passed to the deployed contract
-					.build_and_unwrap_contract();
-			// Deploy a contract to get its occupied storage size
-			let Contract { addr, .. } = builder::bare_instantiate(Code::Upload(wasm_callee))
-				.data(vec![0, 0, 0, 0])
+#[test]
+fn deposit_limit_in_nested_instantiate() {
+	let (wasm_caller, _code_hash_caller) =
+		compile_module("create_storage_and_instantiate").unwrap();
+	let (wasm_callee, code_hash_callee) = compile_module("store_deploy").unwrap();
+	const ED: u64 = 5;
+	ExtBuilder::default().existential_deposit(ED).build().execute_with(|| {
+		let _ = <Test as Config>::Currency::set_balance(&ALICE, 1_000_000);
+		let _ = <Test as Config>::Currency::set_balance(&BOB, 1_000_000);
+		// Create caller contract
+		let Contract { addr: addr_caller, account_id: caller_id } =
+			builder::bare_instantiate(Code::Upload(wasm_caller))
+				.value(10_000u64) // this balance is later passed to the deployed contract
 				.build_and_unwrap_contract();
-
-			let callee_info_len = ContractInfoOf::<Test>::get(&addr).unwrap().encoded_size() as u64;
-
-			// We don't set a special deposit limit for the nested instantiation.
-			//
-			// The deposit limit set for the parent is insufficient for the instantiation, which
-			// requires:
-			// - callee_info_len + 2 for storing the new contract info,
-			// - ED for deployed contract account,
-			// - 2 for the storage item of 0 bytes being created in the callee constructor
-			// or (callee_info_len + 2 + ED + 2) Balance in total.
-			//
-			// Provided the limit is set to be 1 Balance less,
-			// this call should fail on the return from the caller contract.
-			assert_err_ignore_postinfo!(
-				builder::call(addr_caller)
-					.origin(RuntimeOrigin::signed(BOB))
-					.storage_deposit_limit(callee_info_len + 2 + ED + 1)
-					.data((0u32, &code_hash_callee, U256::from(0u64)).encode())
-					.build(),
-				<Error<Test>>::StorageDepositLimitExhausted,
-			);
-			// The charges made on instantiation should be rolled back.
-			assert_eq!(<Test as Config>::Currency::free_balance(&BOB), 1_000_000);
-
-			// Now we give enough limit for the instantiation itself, but require for 1 more storage
-			// byte in the constructor. Hence +1 Balance to the limit is needed. This should fail on
-			// the return from constructor.
-			assert_err_ignore_postinfo!(
-				builder::call(addr_caller)
-					.origin(RuntimeOrigin::signed(BOB))
-					.storage_deposit_limit(callee_info_len + 2 + ED + 2)
-					.data((1u32, &code_hash_callee, U256::from(0u64)).encode())
-					.build(),
-				<Error<Test>>::StorageDepositLimitExhausted,
-			);
-			// The charges made on the instantiation should be rolled back.
-			assert_eq!(<Test as Config>::Currency::free_balance(&BOB), 1_000_000);
-
-			// Now we set enough limit in parent call, but an insufficient limit for child
-			// instantiate. This should fail during the charging for the instantiation in
-			// `RawMeter::charge_instantiate()`
-			assert_err_ignore_postinfo!(
-				builder::call(addr_caller)
-					.origin(RuntimeOrigin::signed(BOB))
-					.storage_deposit_limit(callee_info_len + 2 + ED + 2)
-					.data(
-						(0u32, &code_hash_callee, U256::from(callee_info_len + 2 + ED + 1))
-							.encode()
-					)
-					.build(),
-				<Error<Test>>::StorageDepositLimitExhausted,
-			);
-			// The charges made on the instantiation should be rolled back.
-			assert_eq!(<Test as Config>::Currency::free_balance(&BOB), 1_000_000);
-
-			// Same as above but requires for single added storage
-			// item of 1 byte to be covered by the limit, which implies 3 more Balance.
-			// Now we set enough limit for the parent call, but insufficient limit for child
-			// instantiate. This should fail right after the constructor execution.
-			assert_err_ignore_postinfo!(
-				builder::call(addr_caller)
-					.origin(RuntimeOrigin::signed(BOB))
-					.storage_deposit_limit(callee_info_len + 2 + ED + 3) // enough parent limit
-					.data(
-						(1u32, &code_hash_callee, U256::from(callee_info_len + 2 + ED + 2))
-							.encode()
-					)
-					.build(),
-				<Error<Test>>::StorageDepositLimitExhausted,
-			);
-			// The charges made on the instantiation should be rolled back.
-			assert_eq!(<Test as Config>::Currency::free_balance(&BOB), 1_000_000);
-
-			// Set enough deposit limit for the child instantiate. This should succeed.
-			let result = builder::bare_call(addr_caller)
+		// Deploy a contract to get its occupied storage size
+		let Contract { addr, .. } = builder::bare_instantiate(Code::Upload(wasm_callee))
+			.data(vec![0, 0, 0, 0])
+			.build_and_unwrap_contract();
+
+		let callee_info_len = ContractInfoOf::<Test>::get(&addr).unwrap().encoded_size() as u64;
+
+		// We don't set a special deposit limit for the nested instantiation.
+		//
+		// The deposit limit set for the parent is insufficient for the instantiation, which
+		// requires:
+		// - callee_info_len + 2 for storing the new contract info,
+		// - ED for deployed contract account,
+		// - 2 for the storage item of 0 bytes being created in the callee constructor
+		// or (callee_info_len + 2 + ED + 2) Balance in total.
+		//
+		// Provided the limit is set to be 1 Balance less,
+		// this call should fail on the return from the caller contract.
+		assert_err_ignore_postinfo!(
+			builder::call(addr_caller)
 				.origin(RuntimeOrigin::signed(BOB))
-				.storage_deposit_limit(callee_info_len + 2 + ED + 4 + 2)
-				.data(
-					(1u32, &code_hash_callee, U256::from(callee_info_len + 2 + ED + 3 + 2))
-						.encode(),
-				)
-				.build();
-
-			let returned = result.result.unwrap();
-			// All balance of the caller except ED has been transferred to the callee.
-			// No deposit has been taken from it.
-			assert_eq!(<Test as Config>::Currency::free_balance(&caller_id), ED);
-			// Get address of the deployed contract.
-			let addr_callee = H160::from_slice(&returned.data[0..20]);
-			let callee_account_id = <Test as Config>::AddressMapper::to_account_id(&addr_callee);
-			// 10_000 should be sent to callee from the caller contract, plus ED to be sent from the
-			// origin.
-			assert_eq!(<Test as Config>::Currency::free_balance(&callee_account_id), 10_000 + ED);
-			// The origin should be charged with:
-			//  - callee instantiation deposit = (callee_info_len + 2)
-			//  - callee account ED
-			//  - for writing an item of 1 byte to storage = 3 Balance
-			//  - Immutable data storage item deposit
-			assert_eq!(
-				<Test as Config>::Currency::free_balance(&BOB),
-				1_000_000 - (callee_info_len + 2 + ED + 3)
-			);
-			// Check that deposit due to be charged still includes these 3 Balance
-			assert_eq!(result.storage_deposit.charge_or_zero(), (callee_info_len + 2 + ED + 3))
-		});
-	}
-
-	#[test]
-	fn deposit_limit_honors_liquidity_restrictions() {
-		let (wasm, _code_hash) = compile_module("store_call").unwrap();
-		ExtBuilder::default().existential_deposit(200).build().execute_with(|| {
-			let bobs_balance = 1_000;
-			let _ = <Test as Config>::Currency::set_balance(&ALICE, 1_000_000);
-			let _ = <Test as Config>::Currency::set_balance(&BOB, bobs_balance);
-			let min_balance = Contracts::min_balance();
-
-			// Instantiate the BOB contract.
-			let Contract { addr, account_id } =
-				builder::bare_instantiate(Code::Upload(wasm)).build_and_unwrap_contract();
-
-			let info_deposit = test_utils::contract_info_storage_deposit(&addr);
-			// Check that the contract has been instantiated and has the minimum balance
-			assert_eq!(get_contract(&addr).total_deposit(), info_deposit);
-			assert_eq!(
-				<Test as Config>::Currency::total_balance(&account_id),
-				info_deposit + min_balance
-			);
-
-			// check that the hold is honored
-			<Test as Config>::Currency::hold(
-				&HoldReason::CodeUploadDepositReserve.into(),
-				&BOB,
-				bobs_balance - min_balance,
-			)
-			.unwrap();
-			assert_err_ignore_postinfo!(
-				builder::call(addr)
-					.origin(RuntimeOrigin::signed(BOB))
-					.storage_deposit_limit(10_000)
-					.data(100u32.to_le_bytes().to_vec())
-					.build(),
-				<Error<Test>>::StorageDepositLimitExhausted,
-			);
-			assert_eq!(<Test as Config>::Currency::free_balance(&BOB), min_balance);
-		});
-	}
-
-	#[test]
-	fn deposit_limit_honors_existential_deposit() {
-		let (wasm, _code_hash) = compile_module("store_call").unwrap();
-		ExtBuilder::default().existential_deposit(200).build().execute_with(|| {
-			let _ = <Test as Config>::Currency::set_balance(&ALICE, 1_000_000);
-			let _ = <Test as Config>::Currency::set_balance(&BOB, 300);
-			let min_balance = Contracts::min_balance();
-
-			// Instantiate the BOB contract.
-			let Contract { addr, account_id } =
-				builder::bare_instantiate(Code::Upload(wasm)).build_and_unwrap_contract();
-
-			let info_deposit = test_utils::contract_info_storage_deposit(&addr);
-
-			// Check that the contract has been instantiated and has the minimum balance
-			assert_eq!(get_contract(&addr).total_deposit(), info_deposit);
-			assert_eq!(
-				<Test as Config>::Currency::total_balance(&account_id),
-				min_balance + info_deposit
-			);
-
-			// check that the deposit can't bring the account below the existential deposit
-			assert_err_ignore_postinfo!(
-				builder::call(addr)
-					.origin(RuntimeOrigin::signed(BOB))
-					.storage_deposit_limit(10_000)
-					.data(100u32.to_le_bytes().to_vec())
-					.build(),
-				<Error<Test>>::StorageDepositLimitExhausted,
-			);
-			assert_eq!(<Test as Config>::Currency::free_balance(&BOB), 300);
-		});
-	}
-
-	#[test]
-	fn deposit_limit_honors_min_leftover() {
-		let (wasm, _code_hash) = compile_module("store_call").unwrap();
-		ExtBuilder::default().existential_deposit(200).build().execute_with(|| {
-			let _ = <Test as Config>::Currency::set_balance(&ALICE, 1_000_000);
-			let _ = <Test as Config>::Currency::set_balance(&BOB, 1_000);
-			let min_balance = Contracts::min_balance();
-
-			// Instantiate the BOB contract.
-			let Contract { addr, account_id } =
-				builder::bare_instantiate(Code::Upload(wasm)).build_and_unwrap_contract();
-
-			let info_deposit = test_utils::contract_info_storage_deposit(&addr);
-
-			// Check that the contract has been instantiated and has the minimum balance and the
-			// storage deposit
-			assert_eq!(get_contract(&addr).total_deposit(), info_deposit);
-			assert_eq!(
-				<Test as Config>::Currency::total_balance(&account_id),
-				info_deposit + min_balance
-			);
-
-			// check that the minimum leftover (value send) is considered
-			// given the minimum deposit of 200 sending 750 will only leave
-			// 50 for the storage deposit. Which is not enough to store the 50 bytes
-			// as we also need 2 bytes for the item
-			assert_err_ignore_postinfo!(
-				builder::call(addr)
-					.origin(RuntimeOrigin::signed(BOB))
-					.value(750)
-					.storage_deposit_limit(10_000)
-					.data(50u32.to_le_bytes().to_vec())
-					.build(),
-				<Error<Test>>::StorageDepositLimitExhausted,
-			);
-			assert_eq!(<Test as Config>::Currency::free_balance(&BOB), 1_000);
-		});
-	}
-
-	#[test]
-	fn locking_delegate_dependency_works() {
-		// set hash lock up deposit to 30%, to test deposit calculation.
-		CODE_HASH_LOCKUP_DEPOSIT_PERCENT.with(|c| *c.borrow_mut() = Perbill::from_percent(30));
-
-		let (wasm_caller, self_code_hash) = compile_module("locking_delegate_dependency").unwrap();
-		let callee_codes: Vec<_> =
-			(0..limits::DELEGATE_DEPENDENCIES + 1).map(|idx| dummy_unique(idx)).collect();
-		let callee_hashes: Vec<_> = callee_codes
-			.iter()
-			.map(|c| sp_core::H256(sp_io::hashing::keccak_256(c)))
-			.collect();
-
-		// Define inputs with various actions to test locking / unlocking delegate_dependencies.
-		// See the contract for more details.
-		let noop_input = (0u32, callee_hashes[0]);
-		let lock_delegate_dependency_input = (1u32, callee_hashes[0]);
-		let unlock_delegate_dependency_input = (2u32, callee_hashes[0]);
-		let terminate_input = (3u32, callee_hashes[0]);
-
-		// Instantiate the caller contract with the given input.
-		let instantiate = |input: &(u32, H256)| {
-			builder::bare_instantiate(Code::Upload(wasm_caller.clone()))
-				.origin(RuntimeOrigin::signed(ALICE_FALLBACK))
-				.data(input.encode())
-				.build()
-		};
-
-		// Call contract with the given input.
-		let call = |addr_caller: &H160, input: &(u32, H256)| {
-			builder::bare_call(*addr_caller)
-				.origin(RuntimeOrigin::signed(ALICE_FALLBACK))
-				.data(input.encode())
-				.build()
-		};
-		const ED: u64 = 2000;
-		ExtBuilder::default().existential_deposit(ED).build().execute_with(|| {
-			let _ = Balances::set_balance(&ALICE_FALLBACK, 1_000_000);
-
-			// Instantiate with lock_delegate_dependency should fail since the code is not yet on
-			// chain.
-			assert_err!(
-				instantiate(&lock_delegate_dependency_input).result,
-				Error::<Test>::CodeNotFound
-			);
-
-			// Upload all the delegated codes (they all have the same size)
-			let mut deposit = Default::default();
-			for code in callee_codes.iter() {
-				let CodeUploadReturnValue { deposit: deposit_per_code, .. } =
-					Contracts::bare_upload_code(
-						RuntimeOrigin::signed(ALICE_FALLBACK),
-						code.clone(),
-						deposit_limit::<Test>(),
-					)
-					.unwrap();
-				deposit = deposit_per_code;
-			}
-
-			// Instantiate should now work.
-			let addr_caller = instantiate(&lock_delegate_dependency_input).result.unwrap().addr;
-			let caller_account_id = <Test as Config>::AddressMapper::to_account_id(&addr_caller);
-
-			// There should be a dependency and a deposit.
-			let contract = test_utils::get_contract(&addr_caller);
-
-			let dependency_deposit = &CodeHashLockupDepositPercent::get().mul_ceil(deposit);
-			assert_eq!(
-				contract.delegate_dependencies().get(&callee_hashes[0]),
-				Some(dependency_deposit)
-			);
-			assert_eq!(
-				test_utils::get_balance_on_hold(
-					&HoldReason::StorageDepositReserve.into(),
-					&caller_account_id
-				),
-				dependency_deposit + contract.storage_base_deposit()
-			);
-
-			// Removing the code should fail, since we have added a dependency.
-			assert_err!(
-				Contracts::remove_code(RuntimeOrigin::signed(ALICE_FALLBACK), callee_hashes[0]),
-				<Error<Test>>::CodeInUse
-			);
-
-			// Locking an already existing dependency should fail.
-			assert_err!(
-				call(&addr_caller, &lock_delegate_dependency_input).result,
-				Error::<Test>::DelegateDependencyAlreadyExists
-			);
-
-			// Locking self should fail.
-			assert_err!(
-				call(&addr_caller, &(1u32, self_code_hash)).result,
-				Error::<Test>::CannotAddSelfAsDelegateDependency
-			);
-
-			// Locking more than the maximum allowed delegate_dependencies should fail.
-			for hash in &callee_hashes[1..callee_hashes.len() - 1] {
-				call(&addr_caller, &(1u32, *hash)).result.unwrap();
-			}
-			assert_err!(
-				call(&addr_caller, &(1u32, *callee_hashes.last().unwrap())).result,
-				Error::<Test>::MaxDelegateDependenciesReached
-			);
-
-			// Unlocking all dependency should work.
-			for hash in &callee_hashes[..callee_hashes.len() - 1] {
-				call(&addr_caller, &(2u32, *hash)).result.unwrap();
-			}
-
-			// Dependency should be removed, and deposit should be returned.
-			let contract = test_utils::get_contract(&addr_caller);
-			assert!(contract.delegate_dependencies().is_empty());
-			assert_eq!(
-				test_utils::get_balance_on_hold(
-					&HoldReason::StorageDepositReserve.into(),
-					&caller_account_id
-				),
-				contract.storage_base_deposit()
-			);
-
-			// Removing a nonexistent dependency should fail.
-			assert_err!(
-				call(&addr_caller, &unlock_delegate_dependency_input).result,
-				Error::<Test>::DelegateDependencyNotFound
-			);
-
-			// Locking a dependency with a storage limit too low should fail.
-			assert_err!(
-				builder::bare_call(addr_caller)
-					.storage_deposit_limit(dependency_deposit - 1)
-					.data(lock_delegate_dependency_input.encode())
-					.build()
-					.result,
-				Error::<Test>::StorageDepositLimitExhausted
-			);
+				.storage_deposit_limit(callee_info_len + 2 + ED + 1)
+				.data((0u32, &code_hash_callee, U256::from(0u64)).encode())
+				.build(),
+			<Error<Test>>::StorageDepositLimitExhausted,
+		);
+		// The charges made on instantiation should be rolled back.
+		assert_eq!(<Test as Config>::Currency::free_balance(&BOB), 1_000_000);
+
+		// Now we give enough limit for the instantiation itself, but require for 1 more storage
+		// byte in the constructor. Hence +1 Balance to the limit is needed. This should fail on
+		// the return from constructor.
+		assert_err_ignore_postinfo!(
+			builder::call(addr_caller)
+				.origin(RuntimeOrigin::signed(BOB))
+				.storage_deposit_limit(callee_info_len + 2 + ED + 2)
+				.data((1u32, &code_hash_callee, U256::from(0u64)).encode())
+				.build(),
+			<Error<Test>>::StorageDepositLimitExhausted,
+		);
+		// The charges made on the instantiation should be rolled back.
+		assert_eq!(<Test as Config>::Currency::free_balance(&BOB), 1_000_000);
+
+		// Now we set enough limit in parent call, but an insufficient limit for child
+		// instantiate. This should fail during the charging for the instantiation in
+		// `RawMeter::charge_instantiate()`
+		assert_err_ignore_postinfo!(
+			builder::call(addr_caller)
+				.origin(RuntimeOrigin::signed(BOB))
+				.storage_deposit_limit(callee_info_len + 2 + ED + 2)
+				.data((0u32, &code_hash_callee, U256::from(callee_info_len + 2 + ED + 1)).encode())
+				.build(),
+			<Error<Test>>::StorageDepositLimitExhausted,
+		);
+		// The charges made on the instantiation should be rolled back.
+		assert_eq!(<Test as Config>::Currency::free_balance(&BOB), 1_000_000);
+
+		// Same as above but requires for single added storage
+		// item of 1 byte to be covered by the limit, which implies 3 more Balance.
+		// Now we set enough limit for the parent call, but insufficient limit for child
+		// instantiate. This should fail right after the constructor execution.
+		assert_err_ignore_postinfo!(
+			builder::call(addr_caller)
+				.origin(RuntimeOrigin::signed(BOB))
+				.storage_deposit_limit(callee_info_len + 2 + ED + 3) // enough parent limit
+				.data((1u32, &code_hash_callee, U256::from(callee_info_len + 2 + ED + 2)).encode())
+				.build(),
+			<Error<Test>>::StorageDepositLimitExhausted,
+		);
+		// The charges made on the instantiation should be rolled back.
+		assert_eq!(<Test as Config>::Currency::free_balance(&BOB), 1_000_000);
+
+		// Set enough deposit limit for the child instantiate. This should succeed.
+		let result = builder::bare_call(addr_caller)
+			.origin(RuntimeOrigin::signed(BOB))
+			.storage_deposit_limit(callee_info_len + 2 + ED + 4 + 2)
+			.data((1u32, &code_hash_callee, U256::from(callee_info_len + 2 + ED + 3 + 2)).encode())
+			.build();
+
+		let returned = result.result.unwrap();
+		// All balance of the caller except ED has been transferred to the callee.
+		// No deposit has been taken from it.
+		assert_eq!(<Test as Config>::Currency::free_balance(&caller_id), ED);
+		// Get address of the deployed contract.
+		let addr_callee = H160::from_slice(&returned.data[0..20]);
+		let callee_account_id = <Test as Config>::AddressMapper::to_account_id(&addr_callee);
+		// 10_000 should be sent to callee from the caller contract, plus ED to be sent from the
+		// origin.
+		assert_eq!(<Test as Config>::Currency::free_balance(&callee_account_id), 10_000 + ED);
+		// The origin should be charged with:
+		//  - callee instantiation deposit = (callee_info_len + 2)
+		//  - callee account ED
+		//  - for writing an item of 1 byte to storage = 3 Balance
+		//  - Immutable data storage item deposit
+		assert_eq!(
+			<Test as Config>::Currency::free_balance(&BOB),
+			1_000_000 - (callee_info_len + 2 + ED + 3)
+		);
+		// Check that deposit due to be charged still includes these 3 Balance
+		assert_eq!(result.storage_deposit.charge_or_zero(), (callee_info_len + 2 + ED + 3))
+	});
+}
 
-			// Since we unlocked the dependency we should now be able to remove the code.
-			assert_ok!(Contracts::remove_code(
-				RuntimeOrigin::signed(ALICE_FALLBACK),
-				callee_hashes[0]
-			));
+#[test]
+fn deposit_limit_honors_liquidity_restrictions() {
+	let (wasm, _code_hash) = compile_module("store_call").unwrap();
+	ExtBuilder::default().existential_deposit(200).build().execute_with(|| {
+		let bobs_balance = 1_000;
+		let _ = <Test as Config>::Currency::set_balance(&ALICE, 1_000_000);
+		let _ = <Test as Config>::Currency::set_balance(&BOB, bobs_balance);
+		let min_balance = Contracts::min_balance();
+
+		// Instantiate the BOB contract.
+		let Contract { addr, account_id } =
+			builder::bare_instantiate(Code::Upload(wasm)).build_and_unwrap_contract();
+
+		let info_deposit = test_utils::contract_info_storage_deposit(&addr);
+		// Check that the contract has been instantiated and has the minimum balance
+		assert_eq!(get_contract(&addr).total_deposit(), info_deposit);
+		assert_eq!(
+			<Test as Config>::Currency::total_balance(&account_id),
+			info_deposit + min_balance
+		);
 
-			// Calling should fail since the delegated contract is not on chain anymore.
-			assert_err!(call(&addr_caller, &noop_input).result, Error::<Test>::ContractTrapped);
+		// check that the hold is honored
+		<Test as Config>::Currency::hold(
+			&HoldReason::CodeUploadDepositReserve.into(),
+			&BOB,
+			bobs_balance - min_balance,
+		)
+		.unwrap();
+		assert_err_ignore_postinfo!(
+			builder::call(addr)
+				.origin(RuntimeOrigin::signed(BOB))
+				.storage_deposit_limit(10_000)
+				.data(100u32.to_le_bytes().to_vec())
+				.build(),
+			<Error<Test>>::StorageDepositLimitExhausted,
+		);
+		assert_eq!(<Test as Config>::Currency::free_balance(&BOB), min_balance);
+	});
+}
 
-			// Add the dependency back.
-			Contracts::upload_code(
-				RuntimeOrigin::signed(ALICE_FALLBACK),
-				callee_codes[0].clone(),
-				deposit_limit::<Test>(),
-			)
-			.unwrap();
-			call(&addr_caller, &lock_delegate_dependency_input).result.unwrap();
+#[test]
+fn deposit_limit_honors_existential_deposit() {
+	let (wasm, _code_hash) = compile_module("store_call").unwrap();
+	ExtBuilder::default().existential_deposit(200).build().execute_with(|| {
+		let _ = <Test as Config>::Currency::set_balance(&ALICE, 1_000_000);
+		let _ = <Test as Config>::Currency::set_balance(&BOB, 300);
+		let min_balance = Contracts::min_balance();
+
+		// Instantiate the BOB contract.
+		let Contract { addr, account_id } =
+			builder::bare_instantiate(Code::Upload(wasm)).build_and_unwrap_contract();
+
+		let info_deposit = test_utils::contract_info_storage_deposit(&addr);
+
+		// Check that the contract has been instantiated and has the minimum balance
+		assert_eq!(get_contract(&addr).total_deposit(), info_deposit);
+		assert_eq!(
+			<Test as Config>::Currency::total_balance(&account_id),
+			min_balance + info_deposit
+		);
 
-			// Call terminate should work, and return the deposit.
-			let balance_before = test_utils::get_balance(&ALICE_FALLBACK);
-			assert_ok!(call(&addr_caller, &terminate_input).result);
-			assert_eq!(
-				test_utils::get_balance(&ALICE_FALLBACK),
-				ED + balance_before + contract.storage_base_deposit() + dependency_deposit
-			);
+		// check that the deposit can't bring the account below the existential deposit
+		assert_err_ignore_postinfo!(
+			builder::call(addr)
+				.origin(RuntimeOrigin::signed(BOB))
+				.storage_deposit_limit(10_000)
+				.data(100u32.to_le_bytes().to_vec())
+				.build(),
+			<Error<Test>>::StorageDepositLimitExhausted,
+		);
+		assert_eq!(<Test as Config>::Currency::free_balance(&BOB), 300);
+	});
+}
 
-			// Terminate should also remove the dependency, so we can remove the code.
-			assert_ok!(Contracts::remove_code(
-				RuntimeOrigin::signed(ALICE_FALLBACK),
-				callee_hashes[0]
-			));
-		});
-	}
+#[test]
+fn deposit_limit_honors_min_leftover() {
+	let (wasm, _code_hash) = compile_module("store_call").unwrap();
+	ExtBuilder::default().existential_deposit(200).build().execute_with(|| {
+		let _ = <Test as Config>::Currency::set_balance(&ALICE, 1_000_000);
+		let _ = <Test as Config>::Currency::set_balance(&BOB, 1_000);
+		let min_balance = Contracts::min_balance();
+
+		// Instantiate the BOB contract.
+		let Contract { addr, account_id } =
+			builder::bare_instantiate(Code::Upload(wasm)).build_and_unwrap_contract();
+
+		let info_deposit = test_utils::contract_info_storage_deposit(&addr);
+
+		// Check that the contract has been instantiated and has the minimum balance and the
+		// storage deposit
+		assert_eq!(get_contract(&addr).total_deposit(), info_deposit);
+		assert_eq!(
+			<Test as Config>::Currency::total_balance(&account_id),
+			info_deposit + min_balance
+		);
 
-	#[test]
-	fn native_dependency_deposit_works() {
-		let (wasm, code_hash) = compile_module("set_code_hash").unwrap();
-		let (dummy_wasm, dummy_code_hash) = compile_module("dummy").unwrap();
+		// check that the minimum leftover (value send) is considered
+		// given the minimum deposit of 200 sending 750 will only leave
+		// 50 for the storage deposit. Which is not enough to store the 50 bytes
+		// as we also need 2 bytes for the item
+		assert_err_ignore_postinfo!(
+			builder::call(addr)
+				.origin(RuntimeOrigin::signed(BOB))
+				.value(750)
+				.storage_deposit_limit(10_000)
+				.data(50u32.to_le_bytes().to_vec())
+				.build(),
+			<Error<Test>>::StorageDepositLimitExhausted,
+		);
+		assert_eq!(<Test as Config>::Currency::free_balance(&BOB), 1_000);
+	});
+}
 
-		// Set hash lock up deposit to 30%, to test deposit calculation.
-		CODE_HASH_LOCKUP_DEPOSIT_PERCENT.with(|c| *c.borrow_mut() = Perbill::from_percent(30));
+#[test]
+fn locking_delegate_dependency_works() {
+	// set hash lock up deposit to 30%, to test deposit calculation.
+	CODE_HASH_LOCKUP_DEPOSIT_PERCENT.with(|c| *c.borrow_mut() = Perbill::from_percent(30));
+
+	let (wasm_caller, self_code_hash) = compile_module("locking_delegate_dependency").unwrap();
+	let callee_codes: Vec<_> =
+		(0..limits::DELEGATE_DEPENDENCIES + 1).map(|idx| dummy_unique(idx)).collect();
+	let callee_hashes: Vec<_> = callee_codes
+		.iter()
+		.map(|c| sp_core::H256(sp_io::hashing::keccak_256(c)))
+		.collect();
+
+	// Define inputs with various actions to test locking / unlocking delegate_dependencies.
+	// See the contract for more details.
+	let noop_input = (0u32, callee_hashes[0]);
+	let lock_delegate_dependency_input = (1u32, callee_hashes[0]);
+	let unlock_delegate_dependency_input = (2u32, callee_hashes[0]);
+	let terminate_input = (3u32, callee_hashes[0]);
+
+	// Instantiate the caller contract with the given input.
+	let instantiate = |input: &(u32, H256)| {
+		builder::bare_instantiate(Code::Upload(wasm_caller.clone()))
+			.origin(RuntimeOrigin::signed(ALICE_FALLBACK))
+			.data(input.encode())
+			.build()
+	};
 
-		// Test with both existing and uploaded code
-		for code in [Code::Upload(wasm.clone()), Code::Existing(code_hash)] {
-			ExtBuilder::default().build().execute_with(|| {
-				let _ = Balances::set_balance(&ALICE, 1_000_000);
-				let lockup_deposit_percent = CodeHashLockupDepositPercent::get();
+	// Call contract with the given input.
+	let call = |addr_caller: &H160, input: &(u32, H256)| {
+		builder::bare_call(*addr_caller)
+			.origin(RuntimeOrigin::signed(ALICE_FALLBACK))
+			.data(input.encode())
+			.build()
+	};
+	const ED: u64 = 2000;
+	ExtBuilder::default().existential_deposit(ED).build().execute_with(|| {
+		let _ = Balances::set_balance(&ALICE_FALLBACK, 1_000_000);
+
+		// Instantiate with lock_delegate_dependency should fail since the code is not yet on
+		// chain.
+		assert_err!(
+			instantiate(&lock_delegate_dependency_input).result,
+			Error::<Test>::CodeNotFound
+		);
 
-				// Upload the dummy contract,
-				Contracts::upload_code(
-					RuntimeOrigin::signed(ALICE),
-					dummy_wasm.clone(),
+		// Upload all the delegated codes (they all have the same size)
+		let mut deposit = Default::default();
+		for code in callee_codes.iter() {
+			let CodeUploadReturnValue { deposit: deposit_per_code, .. } =
+				Contracts::bare_upload_code(
+					RuntimeOrigin::signed(ALICE_FALLBACK),
+					code.clone(),
 					deposit_limit::<Test>(),
 				)
 				.unwrap();
+			deposit = deposit_per_code;
+		}
 
-				// Upload `set_code_hash` contracts if using Code::Existing.
-				let add_upload_deposit = match code {
-					Code::Existing(_) => {
-						Contracts::upload_code(
-							RuntimeOrigin::signed(ALICE),
-							wasm.clone(),
-							deposit_limit::<Test>(),
-						)
-						.unwrap();
-						false
-					},
-					Code::Upload(_) => true,
-				};
-
-				// Instantiate the set_code_hash contract.
-				let res = builder::bare_instantiate(code).build();
+		// Instantiate should now work.
+		let addr_caller = instantiate(&lock_delegate_dependency_input).result.unwrap().addr;
+		let caller_account_id = <Test as Config>::AddressMapper::to_account_id(&addr_caller);
 
-				let addr = res.result.unwrap().addr;
-				let account_id = <Test as Config>::AddressMapper::to_account_id(&addr);
-				let base_deposit = test_utils::contract_info_storage_deposit(&addr);
-				let upload_deposit = test_utils::get_code_deposit(&code_hash);
-				let extra_deposit = add_upload_deposit.then(|| upload_deposit).unwrap_or_default();
+		// There should be a dependency and a deposit.
+		let contract = test_utils::get_contract(&addr_caller);
 
-				// Check initial storage_deposit
-				// The base deposit should be: contract_info_storage_deposit + 30% * deposit
-				let deposit =
-					extra_deposit + base_deposit + lockup_deposit_percent.mul_ceil(upload_deposit);
+		let dependency_deposit = &CodeHashLockupDepositPercent::get().mul_ceil(deposit);
+		assert_eq!(
+			contract.delegate_dependencies().get(&callee_hashes[0]),
+			Some(dependency_deposit)
+		);
+		assert_eq!(
+			test_utils::get_balance_on_hold(
+				&HoldReason::StorageDepositReserve.into(),
+				&caller_account_id
+			),
+			dependency_deposit + contract.storage_base_deposit()
+		);
 
-				assert_eq!(
-					res.storage_deposit.charge_or_zero(),
-					deposit + Contracts::min_balance()
-				);
+		// Removing the code should fail, since we have added a dependency.
+		assert_err!(
+			Contracts::remove_code(RuntimeOrigin::signed(ALICE_FALLBACK), callee_hashes[0]),
+			<Error<Test>>::CodeInUse
+		);
 
-				// call set_code_hash
-				builder::bare_call(addr)
-					.data(dummy_code_hash.encode())
-					.build_and_unwrap_result();
+		// Locking an already existing dependency should fail.
+		assert_err!(
+			call(&addr_caller, &lock_delegate_dependency_input).result,
+			Error::<Test>::DelegateDependencyAlreadyExists
+		);
 
-				// Check updated storage_deposit
-				let code_deposit = test_utils::get_code_deposit(&dummy_code_hash);
-				let deposit = base_deposit + lockup_deposit_percent.mul_ceil(code_deposit);
-				assert_eq!(test_utils::get_contract(&addr).storage_base_deposit(), deposit);
+		// Locking self should fail.
+		assert_err!(
+			call(&addr_caller, &(1u32, self_code_hash)).result,
+			Error::<Test>::CannotAddSelfAsDelegateDependency
+		);
 
-				assert_eq!(
-					test_utils::get_balance_on_hold(
-						&HoldReason::StorageDepositReserve.into(),
-						&account_id
-					),
-					deposit
-				);
-			});
+		// Locking more than the maximum allowed delegate_dependencies should fail.
+		for hash in &callee_hashes[1..callee_hashes.len() - 1] {
+			call(&addr_caller, &(1u32, *hash)).result.unwrap();
 		}
-	}
-
-	#[test]
-	fn root_cannot_upload_code() {
-		let (wasm, _) = compile_module("dummy").unwrap();
-
-		ExtBuilder::default().build().execute_with(|| {
-			assert_noop!(
-				Contracts::upload_code(RuntimeOrigin::root(), wasm, deposit_limit::<Test>()),
-				DispatchError::BadOrigin,
-			);
-		});
-	}
-
-	#[test]
-	fn root_cannot_remove_code() {
-		let (_, code_hash) = compile_module("dummy").unwrap();
-
-		ExtBuilder::default().build().execute_with(|| {
-			assert_noop!(
-				Contracts::remove_code(RuntimeOrigin::root(), code_hash),
-				DispatchError::BadOrigin,
-			);
-		});
-	}
-
-	#[test]
-	fn signed_cannot_set_code() {
-		let (_, code_hash) = compile_module("dummy").unwrap();
+		assert_err!(
+			call(&addr_caller, &(1u32, *callee_hashes.last().unwrap())).result,
+			Error::<Test>::MaxDelegateDependenciesReached
+		);
 
-		ExtBuilder::default().build().execute_with(|| {
-			assert_noop!(
-				Contracts::set_code(RuntimeOrigin::signed(ALICE), BOB_ADDR, code_hash),
-				DispatchError::BadOrigin,
-			);
-		});
-	}
+		// Unlocking all dependency should work.
+		for hash in &callee_hashes[..callee_hashes.len() - 1] {
+			call(&addr_caller, &(2u32, *hash)).result.unwrap();
+		}
 
-	#[test]
-	fn none_cannot_call_code() {
-		ExtBuilder::default().build().execute_with(|| {
-			assert_err_ignore_postinfo!(
-				builder::call(BOB_ADDR).origin(RuntimeOrigin::none()).build(),
-				DispatchError::BadOrigin,
-			);
-		});
-	}
+		// Dependency should be removed, and deposit should be returned.
+		let contract = test_utils::get_contract(&addr_caller);
+		assert!(contract.delegate_dependencies().is_empty());
+		assert_eq!(
+			test_utils::get_balance_on_hold(
+				&HoldReason::StorageDepositReserve.into(),
+				&caller_account_id
+			),
+			contract.storage_base_deposit()
+		);
 
-	#[test]
-	fn root_can_call() {
-		let (wasm, _) = compile_module("dummy").unwrap();
+		// Removing a nonexistent dependency should fail.
+		assert_err!(
+			call(&addr_caller, &unlock_delegate_dependency_input).result,
+			Error::<Test>::DelegateDependencyNotFound
+		);
 
-		ExtBuilder::default().existential_deposit(100).build().execute_with(|| {
-			let _ = <Test as Config>::Currency::set_balance(&ALICE, 1_000_000);
+		// Locking a dependency with a storage limit too low should fail.
+		assert_err!(
+			builder::bare_call(addr_caller)
+				.storage_deposit_limit(dependency_deposit - 1)
+				.data(lock_delegate_dependency_input.encode())
+				.build()
+				.result,
+			Error::<Test>::StorageDepositLimitExhausted
+		);
 
-			let Contract { addr, .. } =
-				builder::bare_instantiate(Code::Upload(wasm)).build_and_unwrap_contract();
+		// Since we unlocked the dependency we should now be able to remove the code.
+		assert_ok!(Contracts::remove_code(RuntimeOrigin::signed(ALICE_FALLBACK), callee_hashes[0]));
 
-			// Call the contract.
-			assert_ok!(builder::call(addr).origin(RuntimeOrigin::root()).build());
-		});
-	}
+		// Calling should fail since the delegated contract is not on chain anymore.
+		assert_err!(call(&addr_caller, &noop_input).result, Error::<Test>::ContractTrapped);
 
-	#[test]
-	fn root_cannot_instantiate_with_code() {
-		let (wasm, _) = compile_module("dummy").unwrap();
+		// Add the dependency back.
+		Contracts::upload_code(
+			RuntimeOrigin::signed(ALICE_FALLBACK),
+			callee_codes[0].clone(),
+			deposit_limit::<Test>(),
+		)
+		.unwrap();
+		call(&addr_caller, &lock_delegate_dependency_input).result.unwrap();
+
+		// Call terminate should work, and return the deposit.
+		let balance_before = test_utils::get_balance(&ALICE_FALLBACK);
+		assert_ok!(call(&addr_caller, &terminate_input).result);
+		assert_eq!(
+			test_utils::get_balance(&ALICE_FALLBACK),
+			ED + balance_before + contract.storage_base_deposit() + dependency_deposit
+		);
 
-		ExtBuilder::default().build().execute_with(|| {
-			assert_err_ignore_postinfo!(
-				builder::instantiate_with_code(wasm).origin(RuntimeOrigin::root()).build(),
-				DispatchError::BadOrigin
-			);
-		});
-	}
+		// Terminate should also remove the dependency, so we can remove the code.
+		assert_ok!(Contracts::remove_code(RuntimeOrigin::signed(ALICE_FALLBACK), callee_hashes[0]));
+	});
+}
 
-	#[test]
-	fn root_cannot_instantiate() {
-		let (_, code_hash) = compile_module("dummy").unwrap();
+#[test]
+fn native_dependency_deposit_works() {
+	let (wasm, code_hash) = compile_module("set_code_hash").unwrap();
+	let (dummy_wasm, dummy_code_hash) = compile_module("dummy").unwrap();
 
-		ExtBuilder::default().build().execute_with(|| {
-			assert_err_ignore_postinfo!(
-				builder::instantiate(code_hash).origin(RuntimeOrigin::root()).build(),
-				DispatchError::BadOrigin
-			);
-		});
-	}
+	// Set hash lock up deposit to 30%, to test deposit calculation.
+	CODE_HASH_LOCKUP_DEPOSIT_PERCENT.with(|c| *c.borrow_mut() = Perbill::from_percent(30));
 
-	#[test]
-	fn only_upload_origin_can_upload() {
-		let (wasm, _) = compile_module("dummy").unwrap();
-		UploadAccount::set(Some(ALICE));
+	// Test with both existing and uploaded code
+	for code in [Code::Upload(wasm.clone()), Code::Existing(code_hash)] {
 		ExtBuilder::default().build().execute_with(|| {
 			let _ = Balances::set_balance(&ALICE, 1_000_000);
-			let _ = Balances::set_balance(&BOB, 1_000_000);
-
-			assert_err!(
-				Contracts::upload_code(
-					RuntimeOrigin::root(),
-					wasm.clone(),
-					deposit_limit::<Test>(),
-				),
-				DispatchError::BadOrigin
-			);
-
-			assert_err!(
-				Contracts::upload_code(
-					RuntimeOrigin::signed(BOB),
-					wasm.clone(),
-					deposit_limit::<Test>(),
-				),
-				DispatchError::BadOrigin
-			);
+			let lockup_deposit_percent = CodeHashLockupDepositPercent::get();
 
-			// Only alice is allowed to upload contract code.
-			assert_ok!(Contracts::upload_code(
+			// Upload the dummy contract,
+			Contracts::upload_code(
 				RuntimeOrigin::signed(ALICE),
-				wasm.clone(),
+				dummy_wasm.clone(),
 				deposit_limit::<Test>(),
-			));
-		});
-	}
+			)
+			.unwrap();
 
-	#[test]
-	fn only_instantiation_origin_can_instantiate() {
-		let (code, code_hash) = compile_module("dummy").unwrap();
-		InstantiateAccount::set(Some(ALICE));
-		ExtBuilder::default().build().execute_with(|| {
-			let _ = Balances::set_balance(&ALICE, 1_000_000);
-			let _ = Balances::set_balance(&BOB, 1_000_000);
+			// Upload `set_code_hash` contracts if using Code::Existing.
+			let add_upload_deposit = match code {
+				Code::Existing(_) => {
+					Contracts::upload_code(
+						RuntimeOrigin::signed(ALICE),
+						wasm.clone(),
+						deposit_limit::<Test>(),
+					)
+					.unwrap();
+					false
+				},
+				Code::Upload(_) => true,
+			};
 
-			assert_err_ignore_postinfo!(
-				builder::instantiate_with_code(code.clone())
-					.origin(RuntimeOrigin::root())
-					.build(),
-				DispatchError::BadOrigin
-			);
+			// Instantiate the set_code_hash contract.
+			let res = builder::bare_instantiate(code).build();
 
-			assert_err_ignore_postinfo!(
-				builder::instantiate_with_code(code.clone())
-					.origin(RuntimeOrigin::signed(BOB))
-					.build(),
-				DispatchError::BadOrigin
-			);
+			let addr = res.result.unwrap().addr;
+			let account_id = <Test as Config>::AddressMapper::to_account_id(&addr);
+			let base_deposit = test_utils::contract_info_storage_deposit(&addr);
+			let upload_deposit = test_utils::get_code_deposit(&code_hash);
+			let extra_deposit = add_upload_deposit.then(|| upload_deposit).unwrap_or_default();
 
-			// Only Alice can instantiate
-			assert_ok!(builder::instantiate_with_code(code).build());
+			// Check initial storage_deposit
+			// The base deposit should be: contract_info_storage_deposit + 30% * deposit
+			let deposit =
+				extra_deposit + base_deposit + lockup_deposit_percent.mul_ceil(upload_deposit);
 
-			// Bob cannot instantiate with either `instantiate_with_code` or `instantiate`.
-			assert_err_ignore_postinfo!(
-				builder::instantiate(code_hash).origin(RuntimeOrigin::signed(BOB)).build(),
-				DispatchError::BadOrigin
-			);
-		});
-	}
+			assert_eq!(res.storage_deposit.charge_or_zero(), deposit + Contracts::min_balance());
 
-	#[test]
-	fn balance_of_api() {
-		let (wasm, _code_hash) = compile_module("balance_of").unwrap();
-		ExtBuilder::default().existential_deposit(200).build().execute_with(|| {
-			let _ = Balances::set_balance(&ALICE, 1_000_000);
-			let _ = Balances::set_balance(&ALICE_FALLBACK, 1_000_000);
-
-			let Contract { addr, .. } =
-				builder::bare_instantiate(Code::Upload(wasm.to_vec())).build_and_unwrap_contract();
-
-			// The fixture asserts a non-zero returned free balance of the account;
-			// The ALICE_FALLBACK account is endowed;
-			// Hence we should not revert
-			assert_ok!(builder::call(addr).data(ALICE_ADDR.0.to_vec()).build());
-
-			// The fixture asserts a non-zero returned free balance of the account;
-			// The ETH_BOB account is not endowed;
-			// Hence we should revert
-			assert_err_ignore_postinfo!(
-				builder::call(addr).data(BOB_ADDR.0.to_vec()).build(),
-				<Error<Test>>::ContractTrapped
+			// call set_code_hash
+			builder::bare_call(addr)
+				.data(dummy_code_hash.encode())
+				.build_and_unwrap_result();
+
+			// Check updated storage_deposit
+			let code_deposit = test_utils::get_code_deposit(&dummy_code_hash);
+			let deposit = base_deposit + lockup_deposit_percent.mul_ceil(code_deposit);
+			assert_eq!(test_utils::get_contract(&addr).storage_base_deposit(), deposit);
+
+			assert_eq!(
+				test_utils::get_balance_on_hold(
+					&HoldReason::StorageDepositReserve.into(),
+					&account_id
+				),
+				deposit
 			);
 		});
 	}
+}
 
-	#[test]
-	fn balance_api_returns_free_balance() {
-		let (wasm, _code_hash) = compile_module("balance").unwrap();
-		ExtBuilder::default().existential_deposit(200).build().execute_with(|| {
-			let _ = <Test as Config>::Currency::set_balance(&ALICE, 1_000_000);
+#[test]
+fn block_hash_works() {
+	let (code, _) = compile_module("block_hash").unwrap();
 
-			// Instantiate the BOB contract without any extra balance.
-			let Contract { addr, .. } =
-				builder::bare_instantiate(Code::Upload(wasm.to_vec())).build_and_unwrap_contract();
+	ExtBuilder::default().existential_deposit(1).build().execute_with(|| {
+		let _ = <Test as Config>::Currency::set_balance(&ALICE, 1_000_000);
 
-			let value = 0;
-			// Call BOB which makes it call the balance runtime API.
-			// The contract code asserts that the returned balance is 0.
-			assert_ok!(builder::call(addr).value(value).build());
+		let Contract { addr, .. } =
+			builder::bare_instantiate(Code::Upload(code)).build_and_unwrap_contract();
 
-			let value = 1;
-			// Calling with value will trap the contract.
-			assert_err_ignore_postinfo!(
-				builder::call(addr).value(value).build(),
-				<Error<Test>>::ContractTrapped
-			);
-		});
-	}
+		// The genesis config sets to the block number to 1
+		let block_hash = [1; 32];
+		frame_system::BlockHash::<Test>::insert(
+			&crate::BlockNumberFor::<Test>::from(0u32),
+			<Test as frame_system::Config>::Hash::from(&block_hash),
+		);
+		assert_ok!(builder::call(addr)
+			.data((U256::zero(), H256::from(block_hash)).encode())
+			.build());
 
-	#[test]
-	fn gas_consumed_is_linear_for_nested_calls() {
-		let (code, _code_hash) = compile_module("recurse").unwrap();
-		ExtBuilder::default().existential_deposit(200).build().execute_with(|| {
-			let _ = <Test as Config>::Currency::set_balance(&ALICE, 1_000_000);
+		// A block number out of range returns the zero value
+		assert_ok!(builder::call(addr).data((U256::from(1), H256::zero()).encode()).build());
+	});
+}
 
-			let Contract { addr, .. } =
-				builder::bare_instantiate(Code::Upload(code)).build_and_unwrap_contract();
+#[test]
+fn root_cannot_upload_code() {
+	let (wasm, _) = compile_module("dummy").unwrap();
 
-			let [gas_0, gas_1, gas_2, gas_max] = {
-				[0u32, 1u32, 2u32, limits::CALL_STACK_DEPTH]
-					.iter()
-					.map(|i| {
-						let result = builder::bare_call(addr).data(i.encode()).build();
-						assert_ok!(result.result);
-						result.gas_consumed
-					})
-					.collect::<Vec<_>>()
-					.try_into()
-					.unwrap()
-			};
+	ExtBuilder::default().build().execute_with(|| {
+		assert_noop!(
+			Contracts::upload_code(RuntimeOrigin::root(), wasm, deposit_limit::<Test>()),
+			DispatchError::BadOrigin,
+		);
+	});
+}
 
-			let gas_per_recursion = gas_2.checked_sub(&gas_1).unwrap();
-			assert_eq!(gas_max, gas_0 + gas_per_recursion * limits::CALL_STACK_DEPTH as u64);
-		});
-	}
+#[test]
+fn root_cannot_remove_code() {
+	let (_, code_hash) = compile_module("dummy").unwrap();
 
-	#[test]
-	fn read_only_call_cannot_store() {
-		let (wasm_caller, _code_hash_caller) = compile_module("read_only_call").unwrap();
-		let (wasm_callee, _code_hash_callee) = compile_module("store_call").unwrap();
-		ExtBuilder::default().existential_deposit(200).build().execute_with(|| {
-			let _ = <Test as Config>::Currency::set_balance(&ALICE, 1_000_000);
+	ExtBuilder::default().build().execute_with(|| {
+		assert_noop!(
+			Contracts::remove_code(RuntimeOrigin::root(), code_hash),
+			DispatchError::BadOrigin,
+		);
+	});
+}
 
-			// Create both contracts: Constructors do nothing.
-			let Contract { addr: addr_caller, .. } =
-				builder::bare_instantiate(Code::Upload(wasm_caller)).build_and_unwrap_contract();
-			let Contract { addr: addr_callee, .. } =
-				builder::bare_instantiate(Code::Upload(wasm_callee)).build_and_unwrap_contract();
+#[test]
+fn signed_cannot_set_code() {
+	let (_, code_hash) = compile_module("dummy").unwrap();
 
-			// Read-only call fails when modifying storage.
-			assert_err_ignore_postinfo!(
-				builder::call(addr_caller).data((&addr_callee, 100u32).encode()).build(),
-				<Error<Test>>::ContractTrapped
-			);
-		});
-	}
+	ExtBuilder::default().build().execute_with(|| {
+		assert_noop!(
+			Contracts::set_code(RuntimeOrigin::signed(ALICE), BOB_ADDR, code_hash),
+			DispatchError::BadOrigin,
+		);
+	});
+}
 
-	#[test]
-	fn read_only_call_cannot_transfer() {
-		let (wasm_caller, _code_hash_caller) = compile_module("call_with_flags_and_value").unwrap();
-		let (wasm_callee, _code_hash_callee) = compile_module("dummy").unwrap();
-		ExtBuilder::default().existential_deposit(200).build().execute_with(|| {
-			let _ = <Test as Config>::Currency::set_balance(&ALICE, 1_000_000);
+#[test]
+fn none_cannot_call_code() {
+	ExtBuilder::default().build().execute_with(|| {
+		assert_err_ignore_postinfo!(
+			builder::call(BOB_ADDR).origin(RuntimeOrigin::none()).build(),
+			DispatchError::BadOrigin,
+		);
+	});
+}
 
-			// Create both contracts: Constructors do nothing.
-			let Contract { addr: addr_caller, .. } =
-				builder::bare_instantiate(Code::Upload(wasm_caller)).build_and_unwrap_contract();
-			let Contract { addr: addr_callee, .. } =
-				builder::bare_instantiate(Code::Upload(wasm_callee)).build_and_unwrap_contract();
-
-			// Read-only call fails when a non-zero value is set.
-			assert_err_ignore_postinfo!(
-				builder::call(addr_caller)
-					.data(
-						(addr_callee, pallet_revive_uapi::CallFlags::READ_ONLY.bits(), 100u64)
-							.encode()
-					)
-					.build(),
-				<Error<Test>>::StateChangeDenied
-			);
-		});
-	}
+#[test]
+fn root_can_call() {
+	let (wasm, _) = compile_module("dummy").unwrap();
 
-	#[test]
-	fn read_only_subsequent_call_cannot_store() {
-		let (wasm_read_only_caller, _code_hash_caller) = compile_module("read_only_call").unwrap();
-		let (wasm_caller, _code_hash_caller) = compile_module("call_with_flags_and_value").unwrap();
-		let (wasm_callee, _code_hash_callee) = compile_module("store_call").unwrap();
-		ExtBuilder::default().existential_deposit(200).build().execute_with(|| {
-			let _ = <Test as Config>::Currency::set_balance(&ALICE, 1_000_000);
+	ExtBuilder::default().existential_deposit(100).build().execute_with(|| {
+		let _ = <Test as Config>::Currency::set_balance(&ALICE, 1_000_000);
 
-			// Create contracts: Constructors do nothing.
-			let Contract { addr: addr_caller, .. } =
-				builder::bare_instantiate(Code::Upload(wasm_read_only_caller))
-					.build_and_unwrap_contract();
-			let Contract { addr: addr_subsequent_caller, .. } =
-				builder::bare_instantiate(Code::Upload(wasm_caller)).build_and_unwrap_contract();
-			let Contract { addr: addr_callee, .. } =
-				builder::bare_instantiate(Code::Upload(wasm_callee)).build_and_unwrap_contract();
+		let Contract { addr, .. } =
+			builder::bare_instantiate(Code::Upload(wasm)).build_and_unwrap_contract();
 
-			// Subsequent call input.
-			let input = (&addr_callee, pallet_revive_uapi::CallFlags::empty().bits(), 0u64, 100u32);
+		// Call the contract.
+		assert_ok!(builder::call(addr).origin(RuntimeOrigin::root()).build());
+	});
+}
 
-			// Read-only call fails when modifying storage.
-			assert_err_ignore_postinfo!(
-				builder::call(addr_caller)
-					.data((&addr_subsequent_caller, input).encode())
-					.build(),
-				<Error<Test>>::ContractTrapped
-			);
-		});
-	}
+#[test]
+fn root_cannot_instantiate_with_code() {
+	let (wasm, _) = compile_module("dummy").unwrap();
 
-	#[test]
-	fn read_only_call_works() {
-		let (wasm_caller, _code_hash_caller) = compile_module("read_only_call").unwrap();
-		let (wasm_callee, _code_hash_callee) = compile_module("dummy").unwrap();
-		ExtBuilder::default().existential_deposit(200).build().execute_with(|| {
-			let _ = <Test as Config>::Currency::set_balance(&ALICE, 1_000_000);
+	ExtBuilder::default().build().execute_with(|| {
+		assert_err_ignore_postinfo!(
+			builder::instantiate_with_code(wasm).origin(RuntimeOrigin::root()).build(),
+			DispatchError::BadOrigin
+		);
+	});
+}
 
-			// Create both contracts: Constructors do nothing.
-			let Contract { addr: addr_caller, .. } =
-				builder::bare_instantiate(Code::Upload(wasm_caller)).build_and_unwrap_contract();
-			let Contract { addr: addr_callee, .. } =
-				builder::bare_instantiate(Code::Upload(wasm_callee)).build_and_unwrap_contract();
+#[test]
+fn root_cannot_instantiate() {
+	let (_, code_hash) = compile_module("dummy").unwrap();
 
-			assert_ok!(builder::call(addr_caller).data(addr_callee.encode()).build());
-		});
-	}
+	ExtBuilder::default().build().execute_with(|| {
+		assert_err_ignore_postinfo!(
+			builder::instantiate(code_hash).origin(RuntimeOrigin::root()).build(),
+			DispatchError::BadOrigin
+		);
+	});
+}
 
-	#[test]
-	fn create1_with_value_works() {
-		let (code, code_hash) = compile_module("create1_with_value").unwrap();
-		let value = 42;
-		ExtBuilder::default().existential_deposit(200).build().execute_with(|| {
-			let _ = <Test as Config>::Currency::set_balance(&ALICE, 1_000_000);
+#[test]
+fn only_upload_origin_can_upload() {
+	let (wasm, _) = compile_module("dummy").unwrap();
+	UploadAccount::set(Some(ALICE));
+	ExtBuilder::default().build().execute_with(|| {
+		let _ = Balances::set_balance(&ALICE, 1_000_000);
+		let _ = Balances::set_balance(&BOB, 1_000_000);
+
+		assert_err!(
+			Contracts::upload_code(RuntimeOrigin::root(), wasm.clone(), deposit_limit::<Test>(),),
+			DispatchError::BadOrigin
+		);
 
-			// Create the contract: Constructor does nothing.
-			let Contract { addr, .. } =
-				builder::bare_instantiate(Code::Upload(code)).build_and_unwrap_contract();
+		assert_err!(
+			Contracts::upload_code(
+				RuntimeOrigin::signed(BOB),
+				wasm.clone(),
+				deposit_limit::<Test>(),
+			),
+			DispatchError::BadOrigin
+		);
 
-			// Call the contract: Deploys itself using create1 and the expected value
-			assert_ok!(builder::call(addr).value(value).data(code_hash.encode()).build());
+		// Only alice is allowed to upload contract code.
+		assert_ok!(Contracts::upload_code(
+			RuntimeOrigin::signed(ALICE),
+			wasm.clone(),
+			deposit_limit::<Test>(),
+		));
+	});
+}
 
-			// We should see the expected balance at the expected account
-			let address = crate::address::create1(&addr, 0);
-			let account_id = <Test as Config>::AddressMapper::to_account_id(&address);
-			let usable_balance = <Test as Config>::Currency::usable_balance(&account_id);
-			assert_eq!(usable_balance, value);
-		});
-	}
+#[test]
+fn only_instantiation_origin_can_instantiate() {
+	let (code, code_hash) = compile_module("dummy").unwrap();
+	InstantiateAccount::set(Some(ALICE));
+	ExtBuilder::default().build().execute_with(|| {
+		let _ = Balances::set_balance(&ALICE, 1_000_000);
+		let _ = Balances::set_balance(&BOB, 1_000_000);
+
+		assert_err_ignore_postinfo!(
+			builder::instantiate_with_code(code.clone())
+				.origin(RuntimeOrigin::root())
+				.build(),
+			DispatchError::BadOrigin
+		);
 
-	#[test]
-	fn static_data_limit_is_enforced() {
-		let (oom_rw_trailing, _) = compile_module("oom_rw_trailing").unwrap();
-		let (oom_rw_included, _) = compile_module("oom_rw_included").unwrap();
-		let (oom_ro, _) = compile_module("oom_ro").unwrap();
+		assert_err_ignore_postinfo!(
+			builder::instantiate_with_code(code.clone())
+				.origin(RuntimeOrigin::signed(BOB))
+				.build(),
+			DispatchError::BadOrigin
+		);
 
-		ExtBuilder::default().build().execute_with(|| {
-			let _ = Balances::set_balance(&ALICE, 1_000_000);
+		// Only Alice can instantiate
+		assert_ok!(builder::instantiate_with_code(code).build());
 
-			assert_err!(
-				Contracts::upload_code(
-					RuntimeOrigin::signed(ALICE),
-					oom_rw_trailing,
-					deposit_limit::<Test>(),
-				),
-				<Error<Test>>::StaticMemoryTooLarge
-			);
+		// Bob cannot instantiate with either `instantiate_with_code` or `instantiate`.
+		assert_err_ignore_postinfo!(
+			builder::instantiate(code_hash).origin(RuntimeOrigin::signed(BOB)).build(),
+			DispatchError::BadOrigin
+		);
+	});
+}
 
-			assert_err!(
-				Contracts::upload_code(
-					RuntimeOrigin::signed(ALICE),
-					oom_rw_included,
-					deposit_limit::<Test>(),
-				),
-				<Error<Test>>::BlobTooLarge
-			);
+#[test]
+fn balance_of_api() {
+	let (wasm, _code_hash) = compile_module("balance_of").unwrap();
+	ExtBuilder::default().existential_deposit(200).build().execute_with(|| {
+		let _ = Balances::set_balance(&ALICE, 1_000_000);
+		let _ = Balances::set_balance(&ALICE_FALLBACK, 1_000_000);
+
+		let Contract { addr, .. } =
+			builder::bare_instantiate(Code::Upload(wasm.to_vec())).build_and_unwrap_contract();
+
+		// The fixture asserts a non-zero returned free balance of the account;
+		// The ALICE_FALLBACK account is endowed;
+		// Hence we should not revert
+		assert_ok!(builder::call(addr).data(ALICE_ADDR.0.to_vec()).build());
+
+		// The fixture asserts a non-zero returned free balance of the account;
+		// The ETH_BOB account is not endowed;
+		// Hence we should revert
+		assert_err_ignore_postinfo!(
+			builder::call(addr).data(BOB_ADDR.0.to_vec()).build(),
+			<Error<Test>>::ContractTrapped
+		);
+	});
+}
 
-			assert_err!(
-				Contracts::upload_code(
-					RuntimeOrigin::signed(ALICE),
-					oom_ro,
-					deposit_limit::<Test>(),
-				),
-				<Error<Test>>::BlobTooLarge
-			);
-		});
-	}
+#[test]
+fn balance_api_returns_free_balance() {
+	let (wasm, _code_hash) = compile_module("balance").unwrap();
+	ExtBuilder::default().existential_deposit(200).build().execute_with(|| {
+		let _ = <Test as Config>::Currency::set_balance(&ALICE, 1_000_000);
+
+		// Instantiate the BOB contract without any extra balance.
+		let Contract { addr, .. } =
+			builder::bare_instantiate(Code::Upload(wasm.to_vec())).build_and_unwrap_contract();
+
+		let value = 0;
+		// Call BOB which makes it call the balance runtime API.
+		// The contract code asserts that the returned balance is 0.
+		assert_ok!(builder::call(addr).value(value).build());
+
+		let value = 1;
+		// Calling with value will trap the contract.
+		assert_err_ignore_postinfo!(
+			builder::call(addr).value(value).build(),
+			<Error<Test>>::ContractTrapped
+		);
+	});
+}
 
-	#[test]
-	fn call_diverging_out_len_works() {
-		let (code, _) = compile_module("call_diverging_out_len").unwrap();
+#[test]
+fn gas_consumed_is_linear_for_nested_calls() {
+	let (code, _code_hash) = compile_module("recurse").unwrap();
+	ExtBuilder::default().existential_deposit(200).build().execute_with(|| {
+		let _ = <Test as Config>::Currency::set_balance(&ALICE, 1_000_000);
+
+		let Contract { addr, .. } =
+			builder::bare_instantiate(Code::Upload(code)).build_and_unwrap_contract();
+
+		let [gas_0, gas_1, gas_2, gas_max] = {
+			[0u32, 1u32, 2u32, limits::CALL_STACK_DEPTH]
+				.iter()
+				.map(|i| {
+					let result = builder::bare_call(addr).data(i.encode()).build();
+					assert_ok!(result.result);
+					result.gas_consumed
+				})
+				.collect::<Vec<_>>()
+				.try_into()
+				.unwrap()
+		};
 
-		ExtBuilder::default().existential_deposit(100).build().execute_with(|| {
-			let _ = <Test as Config>::Currency::set_balance(&ALICE, 1_000_000);
+		let gas_per_recursion = gas_2.checked_sub(&gas_1).unwrap();
+		assert_eq!(gas_max, gas_0 + gas_per_recursion * limits::CALL_STACK_DEPTH as u64);
+	});
+}
 
-			// Create the contract: Constructor does nothing
-			let Contract { addr, .. } =
-				builder::bare_instantiate(Code::Upload(code)).build_and_unwrap_contract();
+#[test]
+fn read_only_call_cannot_store() {
+	let (wasm_caller, _code_hash_caller) = compile_module("read_only_call").unwrap();
+	let (wasm_callee, _code_hash_callee) = compile_module("store_call").unwrap();
+	ExtBuilder::default().existential_deposit(200).build().execute_with(|| {
+		let _ = <Test as Config>::Currency::set_balance(&ALICE, 1_000_000);
+
+		// Create both contracts: Constructors do nothing.
+		let Contract { addr: addr_caller, .. } =
+			builder::bare_instantiate(Code::Upload(wasm_caller)).build_and_unwrap_contract();
+		let Contract { addr: addr_callee, .. } =
+			builder::bare_instantiate(Code::Upload(wasm_callee)).build_and_unwrap_contract();
+
+		// Read-only call fails when modifying storage.
+		assert_err_ignore_postinfo!(
+			builder::call(addr_caller).data((&addr_callee, 100u32).encode()).build(),
+			<Error<Test>>::ContractTrapped
+		);
+	});
+}
 
-			// Call the contract: It will issue calls and deploys, asserting on
-			// correct output if the supplied output length was smaller than
-			// than what the callee returned.
-			assert_ok!(builder::call(addr).build());
-		});
-	}
+#[test]
+fn read_only_call_cannot_transfer() {
+	let (wasm_caller, _code_hash_caller) = compile_module("call_with_flags_and_value").unwrap();
+	let (wasm_callee, _code_hash_callee) = compile_module("dummy").unwrap();
+	ExtBuilder::default().existential_deposit(200).build().execute_with(|| {
+		let _ = <Test as Config>::Currency::set_balance(&ALICE, 1_000_000);
+
+		// Create both contracts: Constructors do nothing.
+		let Contract { addr: addr_caller, .. } =
+			builder::bare_instantiate(Code::Upload(wasm_caller)).build_and_unwrap_contract();
+		let Contract { addr: addr_callee, .. } =
+			builder::bare_instantiate(Code::Upload(wasm_callee)).build_and_unwrap_contract();
+
+		// Read-only call fails when a non-zero value is set.
+		assert_err_ignore_postinfo!(
+			builder::call(addr_caller)
+				.data(
+					(addr_callee, pallet_revive_uapi::CallFlags::READ_ONLY.bits(), 100u64).encode()
+				)
+				.build(),
+			<Error<Test>>::StateChangeDenied
+		);
+	});
+}
 
-	#[test]
-	fn chain_id_works() {
-		let (code, _) = compile_module("chain_id").unwrap();
+#[test]
+fn read_only_subsequent_call_cannot_store() {
+	let (wasm_read_only_caller, _code_hash_caller) = compile_module("read_only_call").unwrap();
+	let (wasm_caller, _code_hash_caller) = compile_module("call_with_flags_and_value").unwrap();
+	let (wasm_callee, _code_hash_callee) = compile_module("store_call").unwrap();
+	ExtBuilder::default().existential_deposit(200).build().execute_with(|| {
+		let _ = <Test as Config>::Currency::set_balance(&ALICE, 1_000_000);
+
+		// Create contracts: Constructors do nothing.
+		let Contract { addr: addr_caller, .. } =
+			builder::bare_instantiate(Code::Upload(wasm_read_only_caller))
+				.build_and_unwrap_contract();
+		let Contract { addr: addr_subsequent_caller, .. } =
+			builder::bare_instantiate(Code::Upload(wasm_caller)).build_and_unwrap_contract();
+		let Contract { addr: addr_callee, .. } =
+			builder::bare_instantiate(Code::Upload(wasm_callee)).build_and_unwrap_contract();
+
+		// Subsequent call input.
+		let input = (&addr_callee, pallet_revive_uapi::CallFlags::empty().bits(), 0u64, 100u32);
+
+		// Read-only call fails when modifying storage.
+		assert_err_ignore_postinfo!(
+			builder::call(addr_caller)
+				.data((&addr_subsequent_caller, input).encode())
+				.build(),
+			<Error<Test>>::ContractTrapped
+		);
+	});
+}
 
-		ExtBuilder::default().existential_deposit(100).build().execute_with(|| {
-			let _ = <Test as Config>::Currency::set_balance(&ALICE, 1_000_000);
+#[test]
+fn read_only_call_works() {
+	let (wasm_caller, _code_hash_caller) = compile_module("read_only_call").unwrap();
+	let (wasm_callee, _code_hash_callee) = compile_module("dummy").unwrap();
+	ExtBuilder::default().existential_deposit(200).build().execute_with(|| {
+		let _ = <Test as Config>::Currency::set_balance(&ALICE, 1_000_000);
+
+		// Create both contracts: Constructors do nothing.
+		let Contract { addr: addr_caller, .. } =
+			builder::bare_instantiate(Code::Upload(wasm_caller)).build_and_unwrap_contract();
+		let Contract { addr: addr_callee, .. } =
+			builder::bare_instantiate(Code::Upload(wasm_callee)).build_and_unwrap_contract();
+
+		assert_ok!(builder::call(addr_caller).data(addr_callee.encode()).build());
+	});
+}
 
-			let chain_id = U256::from(<Test as Config>::ChainId::get());
-			let received = builder::bare_instantiate(Code::Upload(code)).build_and_unwrap_result();
-			assert_eq!(received.result.data, chain_id.encode());
-		});
-	}
+#[test]
+fn create1_with_value_works() {
+	let (code, code_hash) = compile_module("create1_with_value").unwrap();
+	let value = 42;
+	ExtBuilder::default().existential_deposit(200).build().execute_with(|| {
+		let _ = <Test as Config>::Currency::set_balance(&ALICE, 1_000_000);
+
+		// Create the contract: Constructor does nothing.
+		let Contract { addr, .. } =
+			builder::bare_instantiate(Code::Upload(code)).build_and_unwrap_contract();
+
+		// Call the contract: Deploys itself using create1 and the expected value
+		assert_ok!(builder::call(addr).value(value).data(code_hash.encode()).build());
+
+		// We should see the expected balance at the expected account
+		let address = crate::address::create1(&addr, 0);
+		let account_id = <Test as Config>::AddressMapper::to_account_id(&address);
+		let usable_balance = <Test as Config>::Currency::usable_balance(&account_id);
+		assert_eq!(usable_balance, value);
+	});
+}
 
-	#[test]
-	fn return_data_api_works() {
-		let (code_return_data_api, _) = compile_module("return_data_api").unwrap();
-		let (code_return_with_data, hash_return_with_data) =
-			compile_module("return_with_data").unwrap();
+#[test]
+fn static_data_limit_is_enforced() {
+	let (oom_rw_trailing, _) = compile_module("oom_rw_trailing").unwrap();
+	let (oom_rw_included, _) = compile_module("oom_rw_included").unwrap();
+	let (oom_ro, _) = compile_module("oom_ro").unwrap();
 
-		ExtBuilder::default().existential_deposit(100).build().execute_with(|| {
-			let _ = <Test as Config>::Currency::set_balance(&ALICE, 1_000_000);
+	ExtBuilder::default().build().execute_with(|| {
+		let _ = Balances::set_balance(&ALICE, 1_000_000);
 
-			// Upload the io echoing fixture for later use
-			assert_ok!(Contracts::upload_code(
+		assert_err!(
+			Contracts::upload_code(
 				RuntimeOrigin::signed(ALICE),
-				code_return_with_data,
+				oom_rw_trailing,
 				deposit_limit::<Test>(),
-			));
+			),
+			<Error<Test>>::StaticMemoryTooLarge
+		);
 
-			// Create fixture: Constructor does nothing
-			let Contract { addr, .. } =
-				builder::bare_instantiate(Code::Upload(code_return_data_api))
-					.build_and_unwrap_contract();
+		assert_err!(
+			Contracts::upload_code(
+				RuntimeOrigin::signed(ALICE),
+				oom_rw_included,
+				deposit_limit::<Test>(),
+			),
+			<Error<Test>>::BlobTooLarge
+		);
 
-			// Call the contract: It will issue calls and deploys, asserting on
-			assert_ok!(builder::call(addr)
-				.value(10 * 1024)
-				.data(hash_return_with_data.encode())
-				.build());
-		});
-	}
+		assert_err!(
+			Contracts::upload_code(RuntimeOrigin::signed(ALICE), oom_ro, deposit_limit::<Test>(),),
+			<Error<Test>>::BlobTooLarge
+		);
+	});
+}
 
-	#[test]
-	fn immutable_data_works() {
-		let (code, _) = compile_module("immutable_data").unwrap();
+#[test]
+fn call_diverging_out_len_works() {
+	let (code, _) = compile_module("call_diverging_out_len").unwrap();
 
-		ExtBuilder::default().existential_deposit(100).build().execute_with(|| {
-			let _ = <Test as Config>::Currency::set_balance(&ALICE, 1_000_000);
+	ExtBuilder::default().existential_deposit(100).build().execute_with(|| {
+		let _ = <Test as Config>::Currency::set_balance(&ALICE, 1_000_000);
 
-			let data = [0xfe; 8];
+		// Create the contract: Constructor does nothing
+		let Contract { addr, .. } =
+			builder::bare_instantiate(Code::Upload(code)).build_and_unwrap_contract();
 
-			// Create fixture: Constructor sets the immtuable data
-			let Contract { addr, .. } = builder::bare_instantiate(Code::Upload(code))
-				.data(data.to_vec())
-				.build_and_unwrap_contract();
+		// Call the contract: It will issue calls and deploys, asserting on
+		// correct output if the supplied output length was smaller than
+		// than what the callee returned.
+		assert_ok!(builder::call(addr).build());
+	});
+}
 
-			// Storing immmutable data charges storage deposit; verify it explicitly.
-			assert_eq!(
-				test_utils::get_balance_on_hold(
-					&HoldReason::StorageDepositReserve.into(),
-					&<Test as Config>::AddressMapper::to_account_id(&addr)
-				),
-				test_utils::contract_info_storage_deposit(&addr)
-			);
-			assert_eq!(test_utils::get_contract(&addr).immutable_data_len(), data.len() as u32);
+#[test]
+fn chain_id_works() {
+	let (code, _) = compile_module("chain_id").unwrap();
 
-			// Call the contract: Asserts the input to equal the immutable data
-			assert_ok!(builder::call(addr).data(data.to_vec()).build());
-		});
-	}
+	ExtBuilder::default().existential_deposit(100).build().execute_with(|| {
+		let _ = <Test as Config>::Currency::set_balance(&ALICE, 1_000_000);
 
-	#[test]
-	fn sbrk_cannot_be_deployed() {
-		let (code, _) = compile_module("sbrk").unwrap();
+		let chain_id = U256::from(<Test as Config>::ChainId::get());
+		let received = builder::bare_instantiate(Code::Upload(code)).build_and_unwrap_result();
+		assert_eq!(received.result.data, chain_id.encode());
+	});
+}
 
-		ExtBuilder::default().build().execute_with(|| {
-			let _ = Balances::set_balance(&ALICE, 1_000_000);
+#[test]
+fn return_data_api_works() {
+	let (code_return_data_api, _) = compile_module("return_data_api").unwrap();
+	let (code_return_with_data, hash_return_with_data) =
+		compile_module("return_with_data").unwrap();
 
-			assert_err!(
-				Contracts::upload_code(
-					RuntimeOrigin::signed(ALICE),
-					code.clone(),
-					deposit_limit::<Test>(),
-				),
-				<Error<Test>>::InvalidInstruction
-			);
+	ExtBuilder::default().existential_deposit(100).build().execute_with(|| {
+		let _ = <Test as Config>::Currency::set_balance(&ALICE, 1_000_000);
 
-			assert_err!(
-				builder::bare_instantiate(Code::Upload(code)).build().result,
-				<Error<Test>>::InvalidInstruction
-			);
-		});
-	}
+		// Upload the io echoing fixture for later use
+		assert_ok!(Contracts::upload_code(
+			RuntimeOrigin::signed(ALICE),
+			code_return_with_data,
+			deposit_limit::<Test>(),
+		));
+
+		// Create fixture: Constructor does nothing
+		let Contract { addr, .. } = builder::bare_instantiate(Code::Upload(code_return_data_api))
+			.build_and_unwrap_contract();
+
+		// Call the contract: It will issue calls and deploys, asserting on
+		assert_ok!(builder::call(addr)
+			.value(10 * 1024)
+			.data(hash_return_with_data.encode())
+			.build());
+	});
+}
 
-	#[test]
-	fn overweight_basic_block_cannot_be_deployed() {
-		let (code, _) = compile_module("basic_block").unwrap();
+#[test]
+fn immutable_data_works() {
+	let (code, _) = compile_module("immutable_data").unwrap();
 
-		ExtBuilder::default().build().execute_with(|| {
-			let _ = Balances::set_balance(&ALICE, 1_000_000);
+	ExtBuilder::default().existential_deposit(100).build().execute_with(|| {
+		let _ = <Test as Config>::Currency::set_balance(&ALICE, 1_000_000);
 
-			assert_err!(
-				Contracts::upload_code(
-					RuntimeOrigin::signed(ALICE),
-					code.clone(),
-					deposit_limit::<Test>(),
-				),
-				<Error<Test>>::BasicBlockTooLarge
-			);
+		let data = [0xfe; 8];
 
-			assert_err!(
-				builder::bare_instantiate(Code::Upload(code)).build().result,
-				<Error<Test>>::BasicBlockTooLarge
-			);
-		});
-	}
+		// Create fixture: Constructor sets the immtuable data
+		let Contract { addr, .. } = builder::bare_instantiate(Code::Upload(code))
+			.data(data.to_vec())
+			.build_and_unwrap_contract();
 
-	#[test]
-	fn origin_api_works() {
-		let (code, _) = compile_module("origin").unwrap();
+		// Storing immmutable data charges storage deposit; verify it explicitly.
+		assert_eq!(
+			test_utils::get_balance_on_hold(
+				&HoldReason::StorageDepositReserve.into(),
+				&<Test as Config>::AddressMapper::to_account_id(&addr)
+			),
+			test_utils::contract_info_storage_deposit(&addr)
+		);
+		assert_eq!(test_utils::get_contract(&addr).immutable_data_len(), data.len() as u32);
 
-		ExtBuilder::default().existential_deposit(100).build().execute_with(|| {
-			let _ = <Test as Config>::Currency::set_balance(&ALICE, 1_000_000);
+		// Call the contract: Asserts the input to equal the immutable data
+		assert_ok!(builder::call(addr).data(data.to_vec()).build());
+	});
+}
 
-			// Create fixture: Constructor does nothing
-			let Contract { addr, .. } =
-				builder::bare_instantiate(Code::Upload(code)).build_and_unwrap_contract();
+#[test]
+fn sbrk_cannot_be_deployed() {
+	let (code, _) = compile_module("sbrk").unwrap();
 
-			// Call the contract: Asserts the origin API to work as expected
-			assert_ok!(builder::call(addr).build());
-		});
-	}
+	ExtBuilder::default().build().execute_with(|| {
+		let _ = Balances::set_balance(&ALICE, 1_000_000);
+
+		assert_err!(
+			Contracts::upload_code(
+				RuntimeOrigin::signed(ALICE),
+				code.clone(),
+				deposit_limit::<Test>(),
+			),
+			<Error<Test>>::InvalidInstruction
+		);
 
-	#[test]
-	fn code_hash_works() {
-		let (code_hash_code, self_code_hash) = compile_module("code_hash").unwrap();
-		let (dummy_code, code_hash) = compile_module("dummy").unwrap();
+		assert_err!(
+			builder::bare_instantiate(Code::Upload(code)).build().result,
+			<Error<Test>>::InvalidInstruction
+		);
+	});
+}
 
-		ExtBuilder::default().existential_deposit(1).build().execute_with(|| {
-			let _ = <Test as Config>::Currency::set_balance(&ALICE, 1_000_000);
+#[test]
+fn overweight_basic_block_cannot_be_deployed() {
+	let (code, _) = compile_module("basic_block").unwrap();
 
-			let Contract { addr, .. } =
-				builder::bare_instantiate(Code::Upload(code_hash_code)).build_and_unwrap_contract();
-			let Contract { addr: dummy_addr, .. } =
-				builder::bare_instantiate(Code::Upload(dummy_code)).build_and_unwrap_contract();
-
-			// code hash of dummy contract
-			assert_ok!(builder::call(addr).data((dummy_addr, code_hash).encode()).build());
-			// code has of itself
-			assert_ok!(builder::call(addr).data((addr, self_code_hash).encode()).build());
-
-			// EOA doesn't exists
-			assert_err!(
-				builder::bare_call(addr)
-					.data((BOB_ADDR, crate::exec::EMPTY_CODE_HASH).encode())
-					.build()
-					.result,
-				Error::<Test>::ContractTrapped
-			);
-			// non-existing will return zero
-			assert_ok!(builder::call(addr).data((BOB_ADDR, H256::zero()).encode()).build());
+	ExtBuilder::default().build().execute_with(|| {
+		let _ = Balances::set_balance(&ALICE, 1_000_000);
 
-			// create EOA
-			let _ = <Test as Config>::Currency::set_balance(
-				&<Test as Config>::AddressMapper::to_account_id(&BOB_ADDR),
-				1_000_000,
-			);
+		assert_err!(
+			Contracts::upload_code(
+				RuntimeOrigin::signed(ALICE),
+				code.clone(),
+				deposit_limit::<Test>(),
+			),
+			<Error<Test>>::BasicBlockTooLarge
+		);
 
-			// EOA returns empty code hash
-			assert_ok!(builder::call(addr)
-				.data((BOB_ADDR, crate::exec::EMPTY_CODE_HASH).encode())
-				.build());
-		});
-	}
+		assert_err!(
+			builder::bare_instantiate(Code::Upload(code)).build().result,
+			<Error<Test>>::BasicBlockTooLarge
+		);
+	});
+}
 
-	#[test]
-	fn code_size_works() {
-		let (tester_code, _) = compile_module("extcodesize").unwrap();
-		let tester_code_len = tester_code.len() as u64;
+#[test]
+fn origin_api_works() {
+	let (code, _) = compile_module("origin").unwrap();
 
-		let (dummy_code, _) = compile_module("dummy").unwrap();
-		let dummy_code_len = dummy_code.len() as u64;
+	ExtBuilder::default().existential_deposit(100).build().execute_with(|| {
+		let _ = <Test as Config>::Currency::set_balance(&ALICE, 1_000_000);
 
-		ExtBuilder::default().existential_deposit(1).build().execute_with(|| {
-			let _ = <Test as Config>::Currency::set_balance(&ALICE, 1_000_000);
+		// Create fixture: Constructor does nothing
+		let Contract { addr, .. } =
+			builder::bare_instantiate(Code::Upload(code)).build_and_unwrap_contract();
 
-			let Contract { addr: tester_addr, .. } =
-				builder::bare_instantiate(Code::Upload(tester_code)).build_and_unwrap_contract();
-			let Contract { addr: dummy_addr, .. } =
-				builder::bare_instantiate(Code::Upload(dummy_code)).build_and_unwrap_contract();
+		// Call the contract: Asserts the origin API to work as expected
+		assert_ok!(builder::call(addr).build());
+	});
+}
 
-			// code size of another contract address
-			assert_ok!(builder::call(tester_addr)
-				.data((dummy_addr, dummy_code_len).encode())
-				.build());
+#[test]
+fn code_hash_works() {
+	let (code_hash_code, self_code_hash) = compile_module("code_hash").unwrap();
+	let (dummy_code, code_hash) = compile_module("dummy").unwrap();
 
-			// code size of own contract address
-			assert_ok!(builder::call(tester_addr)
-				.data((tester_addr, tester_code_len).encode())
-				.build());
+	ExtBuilder::default().existential_deposit(1).build().execute_with(|| {
+		let _ = <Test as Config>::Currency::set_balance(&ALICE, 1_000_000);
 
-			// code size of non contract accounts
-			assert_ok!(builder::call(tester_addr).data(([8u8; 20], 0u64).encode()).build());
-		});
-	}
+		let Contract { addr, .. } =
+			builder::bare_instantiate(Code::Upload(code_hash_code)).build_and_unwrap_contract();
+		let Contract { addr: dummy_addr, .. } =
+			builder::bare_instantiate(Code::Upload(dummy_code)).build_and_unwrap_contract();
 
-	#[test]
-	fn origin_must_be_mapped() {
-		let (code, hash) = compile_module("dummy").unwrap();
+		// code hash of dummy contract
+		assert_ok!(builder::call(addr).data((dummy_addr, code_hash).encode()).build());
+		// code has of itself
+		assert_ok!(builder::call(addr).data((addr, self_code_hash).encode()).build());
 
-		ExtBuilder::default().existential_deposit(100).build().execute_with(|| {
-			<Test as Config>::Currency::set_balance(&ALICE, 1_000_000);
-			<Test as Config>::Currency::set_balance(&EVE, 1_000_000);
+		// EOA doesn't exists
+		assert_err!(
+			builder::bare_call(addr)
+				.data((BOB_ADDR, crate::exec::EMPTY_CODE_HASH).encode())
+				.build()
+				.result,
+			Error::<Test>::ContractTrapped
+		);
+		// non-existing will return zero
+		assert_ok!(builder::call(addr).data((BOB_ADDR, H256::zero()).encode()).build());
 
-			let eve = RuntimeOrigin::signed(EVE);
+		// create EOA
+		let _ = <Test as Config>::Currency::set_balance(
+			&<Test as Config>::AddressMapper::to_account_id(&BOB_ADDR),
+			1_000_000,
+		);
 
-			// alice can instantiate as she doesn't need a mapping
-			let Contract { addr, .. } =
-				builder::bare_instantiate(Code::Upload(code)).build_and_unwrap_contract();
+		// EOA returns empty code hash
+		assert_ok!(builder::call(addr)
+			.data((BOB_ADDR, crate::exec::EMPTY_CODE_HASH).encode())
+			.build());
+	});
+}
 
-			// without a mapping eve can neither call nor instantiate
-			assert_err!(
-				builder::bare_call(addr).origin(eve.clone()).build().result,
-				<Error<Test>>::AccountUnmapped
-			);
-			assert_err!(
-				builder::bare_instantiate(Code::Existing(hash))
-					.origin(eve.clone())
-					.build()
-					.result,
-				<Error<Test>>::AccountUnmapped
-			);
+#[test]
+fn code_size_works() {
+	let (tester_code, _) = compile_module("extcodesize").unwrap();
+	let tester_code_len = tester_code.len() as u64;
 
-			// after mapping eve is usable as an origin
-			<Pallet<Test>>::map_account(eve.clone()).unwrap();
-			assert_ok!(builder::bare_call(addr).origin(eve.clone()).build().result);
-			assert_ok!(builder::bare_instantiate(Code::Existing(hash)).origin(eve).build().result);
-		});
-	}
+	let (dummy_code, _) = compile_module("dummy").unwrap();
+	let dummy_code_len = dummy_code.len() as u64;
 
-	#[test]
-	fn mapped_address_works() {
-		let (code, _) = compile_module("terminate_and_send_to_eve").unwrap();
-
-		ExtBuilder::default().existential_deposit(100).build().execute_with(|| {
-			<Test as Config>::Currency::set_balance(&ALICE, 1_000_000);
-
-			// without a mapping everything will be send to the fallback account
-			let Contract { addr, .. } =
-				builder::bare_instantiate(Code::Upload(code.clone())).build_and_unwrap_contract();
-			assert_eq!(<Test as Config>::Currency::total_balance(&EVE_FALLBACK), 0);
-			builder::bare_call(addr).build_and_unwrap_result();
-			assert_eq!(<Test as Config>::Currency::total_balance(&EVE_FALLBACK), 100);
-
-			// after mapping it will be sent to the real eve account
-			let Contract { addr, .. } =
-				builder::bare_instantiate(Code::Upload(code)).build_and_unwrap_contract();
-			// need some balance to pay for the map deposit
-			<Test as Config>::Currency::set_balance(&EVE, 1_000);
-			<Pallet<Test>>::map_account(RuntimeOrigin::signed(EVE)).unwrap();
-			builder::bare_call(addr).build_and_unwrap_result();
-			assert_eq!(<Test as Config>::Currency::total_balance(&EVE_FALLBACK), 100);
-			assert_eq!(<Test as Config>::Currency::total_balance(&EVE), 1_100);
-		});
-	}
+	ExtBuilder::default().existential_deposit(1).build().execute_with(|| {
+		let _ = <Test as Config>::Currency::set_balance(&ALICE, 1_000_000);
 
-	#[test]
-	fn block_hash_works() {
-		let (code, _) = compile_module("block_hash").unwrap();
+		let Contract { addr: tester_addr, .. } =
+			builder::bare_instantiate(Code::Upload(tester_code)).build_and_unwrap_contract();
+		let Contract { addr: dummy_addr, .. } =
+			builder::bare_instantiate(Code::Upload(dummy_code)).build_and_unwrap_contract();
 
-		ExtBuilder::default().existential_deposit(1).build().execute_with(|| {
-			let _ = <Test as Config>::Currency::set_balance(&ALICE, 1_000_000);
+		// code size of another contract address
+		assert_ok!(builder::call(tester_addr).data((dummy_addr, dummy_code_len).encode()).build());
 
-			let Contract { addr, .. } =
-				builder::bare_instantiate(Code::Upload(code)).build_and_unwrap_contract();
+		// code size of own contract address
+		assert_ok!(builder::call(tester_addr)
+			.data((tester_addr, tester_code_len).encode())
+			.build());
 
-			// The genesis config sets to the block number to 1
-			let block_hash = [1; 32];
-			frame_system::BlockHash::<Test>::insert(
-				&crate::BlockNumberFor::<Test>::from(0u32),
-				<Test as frame_system::Config>::Hash::from(&block_hash),
-			);
-			assert_ok!(builder::call(addr)
-				.data((U256::zero(), H256::from(block_hash)).encode())
-				.build());
+		// code size of non contract accounts
+		assert_ok!(builder::call(tester_addr).data(([8u8; 20], 0u64).encode()).build());
+	});
+}
 
-			// A block number out of range returns the zero value
-			assert_ok!(builder::call(addr).data((U256::from(1), H256::zero()).encode()).build());
-		});
-	}
+#[test]
+fn origin_must_be_mapped() {
+	let (code, hash) = compile_module("dummy").unwrap();
+
+	ExtBuilder::default().existential_deposit(100).build().execute_with(|| {
+		<Test as Config>::Currency::set_balance(&ALICE, 1_000_000);
+		<Test as Config>::Currency::set_balance(&EVE, 1_000_000);
+
+		let eve = RuntimeOrigin::signed(EVE);
+
+		// alice can instantiate as she doesn't need a mapping
+		let Contract { addr, .. } =
+			builder::bare_instantiate(Code::Upload(code)).build_and_unwrap_contract();
+
+		// without a mapping eve can neither call nor instantiate
+		assert_err!(
+			builder::bare_call(addr).origin(eve.clone()).build().result,
+			<Error<Test>>::AccountUnmapped
+		);
+		assert_err!(
+			builder::bare_instantiate(Code::Existing(hash))
+				.origin(eve.clone())
+				.build()
+				.result,
+			<Error<Test>>::AccountUnmapped
+		);
+
+		// after mapping eve is usable as an origin
+		<Pallet<Test>>::map_account(eve.clone()).unwrap();
+		assert_ok!(builder::bare_call(addr).origin(eve.clone()).build().result);
+		assert_ok!(builder::bare_instantiate(Code::Existing(hash)).origin(eve).build().result);
+	});
+}
+
+#[test]
+fn mapped_address_works() {
+	let (code, _) = compile_module("terminate_and_send_to_eve").unwrap();
+
+	ExtBuilder::default().existential_deposit(100).build().execute_with(|| {
+		<Test as Config>::Currency::set_balance(&ALICE, 1_000_000);
+
+		// without a mapping everything will be send to the fallback account
+		let Contract { addr, .. } =
+			builder::bare_instantiate(Code::Upload(code.clone())).build_and_unwrap_contract();
+		assert_eq!(<Test as Config>::Currency::total_balance(&EVE_FALLBACK), 0);
+		builder::bare_call(addr).build_and_unwrap_result();
+		assert_eq!(<Test as Config>::Currency::total_balance(&EVE_FALLBACK), 100);
+
+		// after mapping it will be sent to the real eve account
+		let Contract { addr, .. } =
+			builder::bare_instantiate(Code::Upload(code)).build_and_unwrap_contract();
+		// need some balance to pay for the map deposit
+		<Test as Config>::Currency::set_balance(&EVE, 1_000);
+		<Pallet<Test>>::map_account(RuntimeOrigin::signed(EVE)).unwrap();
+		builder::bare_call(addr).build_and_unwrap_result();
+		assert_eq!(<Test as Config>::Currency::total_balance(&EVE_FALLBACK), 100);
+		assert_eq!(<Test as Config>::Currency::total_balance(&EVE), 1_100);
+	});
 }
diff --git a/substrate/frame/revive/src/tests/test_debug.rs b/substrate/frame/revive/src/tests/test_debug.rs
index 1e94d5cafb8..7c4fbba71f6 100644
--- a/substrate/frame/revive/src/tests/test_debug.rs
+++ b/substrate/frame/revive/src/tests/test_debug.rs
@@ -23,6 +23,7 @@ use crate::{
 	test_utils::*,
 };
 use frame_support::traits::Currency;
+use pretty_assertions::assert_eq;
 use sp_core::H160;
 use std::cell::RefCell;
 
@@ -99,146 +100,139 @@ impl CallSpan for TestCallSpan {
 	}
 }
 
-/// We can only run the tests if we have a riscv toolchain installed
-#[cfg(feature = "riscv")]
-mod run_tests {
-	use super::*;
-	use pretty_assertions::assert_eq;
+#[test]
+fn debugging_works() {
+	let (wasm_caller, _) = compile_module("call").unwrap();
+	let (wasm_callee, _) = compile_module("store_call").unwrap();
 
-	#[test]
-	fn debugging_works() {
-		let (wasm_caller, _) = compile_module("call").unwrap();
-		let (wasm_callee, _) = compile_module("store_call").unwrap();
-
-		fn current_stack() -> Vec<DebugFrame> {
-			DEBUG_EXECUTION_TRACE.with(|stack| stack.borrow().clone())
-		}
+	fn current_stack() -> Vec<DebugFrame> {
+		DEBUG_EXECUTION_TRACE.with(|stack| stack.borrow().clone())
+	}
 
-		fn deploy(wasm: Vec<u8>) -> H160 {
-			Contracts::bare_instantiate(
-				RuntimeOrigin::signed(ALICE),
-				0,
-				GAS_LIMIT,
-				deposit_limit::<Test>(),
-				Code::Upload(wasm),
-				vec![],
-				Some([0u8; 32]),
-				DebugInfo::Skip,
-				CollectEvents::Skip,
-			)
-			.result
-			.unwrap()
-			.addr
-		}
+	fn deploy(wasm: Vec<u8>) -> H160 {
+		Contracts::bare_instantiate(
+			RuntimeOrigin::signed(ALICE),
+			0,
+			GAS_LIMIT,
+			deposit_limit::<Test>(),
+			Code::Upload(wasm),
+			vec![],
+			Some([0u8; 32]),
+			DebugInfo::Skip,
+			CollectEvents::Skip,
+		)
+		.result
+		.unwrap()
+		.addr
+	}
 
-		fn constructor_frame(contract_address: &H160, after: bool) -> DebugFrame {
-			DebugFrame {
-				contract_address: *contract_address,
-				call: ExportedFunction::Constructor,
-				input: vec![],
-				result: if after { Some(vec![]) } else { None },
-			}
+	fn constructor_frame(contract_address: &H160, after: bool) -> DebugFrame {
+		DebugFrame {
+			contract_address: *contract_address,
+			call: ExportedFunction::Constructor,
+			input: vec![],
+			result: if after { Some(vec![]) } else { None },
 		}
+	}
 
-		fn call_frame(contract_address: &H160, args: Vec<u8>, after: bool) -> DebugFrame {
-			DebugFrame {
-				contract_address: *contract_address,
-				call: ExportedFunction::Call,
-				input: args,
-				result: if after { Some(vec![]) } else { None },
-			}
+	fn call_frame(contract_address: &H160, args: Vec<u8>, after: bool) -> DebugFrame {
+		DebugFrame {
+			contract_address: *contract_address,
+			call: ExportedFunction::Call,
+			input: args,
+			result: if after { Some(vec![]) } else { None },
 		}
-
-		ExtBuilder::default().existential_deposit(200).build().execute_with(|| {
-			let _ = Balances::deposit_creating(&ALICE, 1_000_000);
-
-			assert_eq!(current_stack(), vec![]);
-
-			let addr_caller = deploy(wasm_caller);
-			let addr_callee = deploy(wasm_callee);
-
-			assert_eq!(
-				current_stack(),
-				vec![
-					constructor_frame(&addr_caller, false),
-					constructor_frame(&addr_caller, true),
-					constructor_frame(&addr_callee, false),
-					constructor_frame(&addr_callee, true),
-				]
-			);
-
-			let main_args = (100u32, &addr_callee.clone()).encode();
-			let inner_args = (100u32).encode();
-
-			assert_ok!(Contracts::call(
-				RuntimeOrigin::signed(ALICE),
-				addr_caller,
-				0,
-				GAS_LIMIT,
-				deposit_limit::<Test>(),
-				main_args.clone()
-			));
-
-			let stack_top = current_stack()[4..].to_vec();
-			assert_eq!(
-				stack_top,
-				vec![
-					call_frame(&addr_caller, main_args.clone(), false),
-					call_frame(&addr_callee, inner_args.clone(), false),
-					call_frame(&addr_callee, inner_args, true),
-					call_frame(&addr_caller, main_args, true),
-				]
-			);
-		});
 	}
 
-	#[test]
-	fn call_interception_works() {
-		let (wasm, _) = compile_module("dummy").unwrap();
-
-		ExtBuilder::default().existential_deposit(200).build().execute_with(|| {
-			let _ = Balances::deposit_creating(&ALICE, 1_000_000);
+	ExtBuilder::default().existential_deposit(200).build().execute_with(|| {
+		let _ = Balances::deposit_creating(&ALICE, 1_000_000);
+
+		assert_eq!(current_stack(), vec![]);
+
+		let addr_caller = deploy(wasm_caller);
+		let addr_callee = deploy(wasm_callee);
+
+		assert_eq!(
+			current_stack(),
+			vec![
+				constructor_frame(&addr_caller, false),
+				constructor_frame(&addr_caller, true),
+				constructor_frame(&addr_callee, false),
+				constructor_frame(&addr_callee, true),
+			]
+		);
+
+		let main_args = (100u32, &addr_callee.clone()).encode();
+		let inner_args = (100u32).encode();
+
+		assert_ok!(Contracts::call(
+			RuntimeOrigin::signed(ALICE),
+			addr_caller,
+			0,
+			GAS_LIMIT,
+			deposit_limit::<Test>(),
+			main_args.clone()
+		));
+
+		let stack_top = current_stack()[4..].to_vec();
+		assert_eq!(
+			stack_top,
+			vec![
+				call_frame(&addr_caller, main_args.clone(), false),
+				call_frame(&addr_callee, inner_args.clone(), false),
+				call_frame(&addr_callee, inner_args, true),
+				call_frame(&addr_caller, main_args, true),
+			]
+		);
+	});
+}
 
-			let account_id = Contracts::bare_instantiate(
-				RuntimeOrigin::signed(ALICE),
-				0,
-				GAS_LIMIT,
-				deposit_limit::<Test>(),
-				Code::Upload(wasm),
-				vec![],
-				// some salt to ensure that the address of this contract is unique among all tests
-				Some([0x41; 32]),
-				DebugInfo::Skip,
-				CollectEvents::Skip,
-			)
-			.result
-			.unwrap()
-			.addr;
-
-			// no interception yet
-			assert_ok!(Contracts::call(
+#[test]
+fn call_interception_works() {
+	let (wasm, _) = compile_module("dummy").unwrap();
+
+	ExtBuilder::default().existential_deposit(200).build().execute_with(|| {
+		let _ = Balances::deposit_creating(&ALICE, 1_000_000);
+
+		let account_id = Contracts::bare_instantiate(
+			RuntimeOrigin::signed(ALICE),
+			0,
+			GAS_LIMIT,
+			deposit_limit::<Test>(),
+			Code::Upload(wasm),
+			vec![],
+			// some salt to ensure that the address of this contract is unique among all tests
+			Some([0x41; 32]),
+			DebugInfo::Skip,
+			CollectEvents::Skip,
+		)
+		.result
+		.unwrap()
+		.addr;
+
+		// no interception yet
+		assert_ok!(Contracts::call(
+			RuntimeOrigin::signed(ALICE),
+			account_id,
+			0,
+			GAS_LIMIT,
+			deposit_limit::<Test>(),
+			vec![],
+		));
+
+		// intercept calls to this contract
+		INTERCEPTED_ADDRESS.with(|i| *i.borrow_mut() = Some(account_id));
+
+		assert_err_ignore_postinfo!(
+			Contracts::call(
 				RuntimeOrigin::signed(ALICE),
 				account_id,
 				0,
 				GAS_LIMIT,
 				deposit_limit::<Test>(),
 				vec![],
-			));
-
-			// intercept calls to this contract
-			INTERCEPTED_ADDRESS.with(|i| *i.borrow_mut() = Some(account_id));
-
-			assert_err_ignore_postinfo!(
-				Contracts::call(
-					RuntimeOrigin::signed(ALICE),
-					account_id,
-					0,
-					GAS_LIMIT,
-					deposit_limit::<Test>(),
-					vec![],
-				),
-				<Error<Test>>::ContractReverted,
-			);
-		});
-	}
+			),
+			<Error<Test>>::ContractReverted,
+		);
+	});
 }
diff --git a/substrate/frame/revive/src/wasm/mod.rs b/substrate/frame/revive/src/wasm/mod.rs
index 6779f551113..f10c4f5fddf 100644
--- a/substrate/frame/revive/src/wasm/mod.rs
+++ b/substrate/frame/revive/src/wasm/mod.rs
@@ -26,7 +26,7 @@ pub use crate::wasm::runtime::SyscallDoc;
 #[cfg(test)]
 pub use runtime::HIGHEST_API_VERSION;
 
-#[cfg(all(feature = "runtime-benchmarks", feature = "riscv"))]
+#[cfg(feature = "runtime-benchmarks")]
 pub use crate::wasm::runtime::{ReturnData, TrapReason};
 
 pub use crate::wasm::runtime::{ApiVersion, Memory, Runtime, RuntimeCosts};
diff --git a/umbrella/Cargo.toml b/umbrella/Cargo.toml
index 28d6a2c3fb0..7f50658c4e1 100644
--- a/umbrella/Cargo.toml
+++ b/umbrella/Cargo.toml
@@ -610,12 +610,6 @@ tuples-96 = [
 	"frame-support-procedural?/tuples-96",
 	"frame-support?/tuples-96",
 ]
-riscv = [
-	"pallet-revive-eth-rpc?/riscv",
-	"pallet-revive-fixtures?/riscv",
-	"pallet-revive-mock-network?/riscv",
-	"pallet-revive?/riscv",
-]
 
 [package.edition]
 workspace = true
-- 
GitLab