From 78b1cab9e8064557b2ebab9d3d039ab448aacd72 Mon Sep 17 00:00:00 2001
From: PG Herveou <pgherveou@gmail.com>
Date: Thu, 28 Mar 2024 11:40:06 +0100
Subject: [PATCH] Contracts: add test builders (#3796)

Cleanup tests (-2.7k lines !) using some builder patterns to build
pallet_contracts api calls
---
 Cargo.lock                                    |    1 +
 substrate/frame/contracts/Cargo.toml          |    1 +
 substrate/frame/contracts/src/tests.rs        | 2729 ++++-------------
 .../frame/contracts/src/tests/builder.rs      |  219 ++
 4 files changed, 733 insertions(+), 2217 deletions(-)
 create mode 100644 substrate/frame/contracts/src/tests/builder.rs

diff --git a/Cargo.lock b/Cargo.lock
index 2382dc8d162..8188e571fcd 100644
--- a/Cargo.lock
+++ b/Cargo.lock
@@ -9553,6 +9553,7 @@ dependencies = [
  "pallet-timestamp",
  "pallet-utility",
  "parity-scale-codec",
+ "paste",
  "pretty_assertions",
  "rand",
  "rand_pcg",
diff --git a/substrate/frame/contracts/Cargo.toml b/substrate/frame/contracts/Cargo.toml
index be3bafcd23f..2aa37a2bf21 100644
--- a/substrate/frame/contracts/Cargo.toml
+++ b/substrate/frame/contracts/Cargo.toml
@@ -18,6 +18,7 @@ workspace = true
 targets = ["x86_64-unknown-linux-gnu"]
 
 [dependencies]
+paste = { version = "1.0", default-features = false }
 bitflags = "1.3"
 codec = { package = "parity-scale-codec", version = "3.6.1", default-features = false, features = [
 	"derive",
diff --git a/substrate/frame/contracts/src/tests.rs b/substrate/frame/contracts/src/tests.rs
index ed486fc4a67..a51faa88c41 100644
--- a/substrate/frame/contracts/src/tests.rs
+++ b/substrate/frame/contracts/src/tests.rs
@@ -15,6 +15,7 @@
 // See the License for the specific language governing permissions and
 // limitations under the License.
 
+mod builder;
 mod pallet_dummy;
 mod test_debug;
 
@@ -610,7 +611,7 @@ fn calling_plain_account_fails() {
 		let base_cost = <<Test as Config>::WeightInfo as WeightInfo>::call();
 
 		assert_eq!(
-			Contracts::call(RuntimeOrigin::signed(ALICE), BOB, 0, GAS_LIMIT, None, Vec::new()),
+			builder::call(BOB).build(),
 			Err(DispatchErrorWithPostInfo {
 				error: Error::<Test>::ContractNotFound.into(),
 				post_info: PostDispatchInfo {
@@ -668,32 +669,13 @@ fn migration_in_progress_works() {
 			Contracts::set_code(RuntimeOrigin::signed(ALICE), BOB.clone(), code_hash),
 			Error::<Test>::MigrationInProgress,
 		);
+		assert_err_ignore_postinfo!(builder::call(BOB).build(), Error::<Test>::MigrationInProgress);
 		assert_err_ignore_postinfo!(
-			Contracts::call(RuntimeOrigin::signed(ALICE), BOB, 0, GAS_LIMIT, None, vec![],),
+			builder::instantiate_with_code(wasm).value(100_000).build(),
 			Error::<Test>::MigrationInProgress,
 		);
 		assert_err_ignore_postinfo!(
-			Contracts::instantiate_with_code(
-				RuntimeOrigin::signed(ALICE),
-				100_000,
-				GAS_LIMIT,
-				None,
-				wasm,
-				vec![],
-				vec![],
-			),
-			Error::<Test>::MigrationInProgress,
-		);
-		assert_err_ignore_postinfo!(
-			Contracts::instantiate(
-				RuntimeOrigin::signed(ALICE),
-				100_000,
-				GAS_LIMIT,
-				None,
-				code_hash,
-				vec![],
-				vec![],
-			),
+			builder::instantiate(code_hash).value(100_000).build(),
 			Error::<Test>::MigrationInProgress,
 		);
 	});
@@ -721,20 +703,9 @@ fn instantiate_and_call_and_deposit_event() {
 		initialize_block(2);
 
 		// Check at the end to get hash on error easily
-		let addr = Contracts::bare_instantiate(
-			ALICE,
-			value,
-			GAS_LIMIT,
-			None,
-			Code::Existing(code_hash),
-			vec![],
-			vec![],
-			DebugInfo::Skip,
-			CollectEvents::Skip,
-		)
-		.result
-		.unwrap()
-		.account_id;
+		let addr = builder::bare_instantiate(Code::Existing(code_hash))
+			.value(value)
+			.build_and_unwrap_account_id();
 		assert!(ContractInfoOf::<Test>::contains_key(&addr));
 
 		assert_eq!(
@@ -812,41 +783,21 @@ fn deposit_event_max_value_limit() {
 	ExtBuilder::default().existential_deposit(50).build().execute_with(|| {
 		// Create
 		let _ = <Test as Config>::Currency::set_balance(&ALICE, 1_000_000);
-		let addr = Contracts::bare_instantiate(
-			ALICE,
-			30_000,
-			GAS_LIMIT,
-			None,
-			Code::Upload(wasm),
-			vec![],
-			vec![],
-			DebugInfo::Skip,
-			CollectEvents::Skip,
-		)
-		.result
-		.unwrap()
-		.account_id;
+		let addr = builder::bare_instantiate(Code::Upload(wasm))
+			.value(30_000)
+			.build_and_unwrap_account_id();
 
 		// Call contract with allowed storage value.
-		assert_ok!(Contracts::call(
-			RuntimeOrigin::signed(ALICE),
-			addr.clone(),
-			0,
-			GAS_LIMIT.set_ref_time(GAS_LIMIT.ref_time() * 2), // we are copying a huge buffer,
-			None,
-			<Test as Config>::Schedule::get().limits.payload_len.encode(),
-		));
+		assert_ok!(builder::call(addr.clone())
+			.gas_limit(GAS_LIMIT.set_ref_time(GAS_LIMIT.ref_time() * 2)) // we are copying a huge buffer,
+			.data(<Test as Config>::Schedule::get().limits.payload_len.encode())
+			.build());
 
 		// Call contract with too large a storage value.
 		assert_err_ignore_postinfo!(
-			Contracts::call(
-				RuntimeOrigin::signed(ALICE),
-				addr,
-				0,
-				GAS_LIMIT,
-				None,
-				(<Test as Config>::Schedule::get().limits.payload_len + 1).encode(),
-			),
+			builder::call(addr)
+				.data((<Test as Config>::Schedule::get().limits.payload_len + 1).encode())
+				.build(),
 			Error::<Test>::ValueTooLarge,
 		);
 	});
@@ -860,32 +811,16 @@ fn run_out_of_fuel_engine() {
 		let min_balance = Contracts::min_balance();
 		let _ = <Test as Config>::Currency::set_balance(&ALICE, 1_000_000);
 
-		let addr = Contracts::bare_instantiate(
-			ALICE,
-			100 * min_balance,
-			GAS_LIMIT,
-			None,
-			Code::Upload(wasm),
-			vec![],
-			vec![],
-			DebugInfo::Skip,
-			CollectEvents::Skip,
-		)
-		.result
-		.unwrap()
-		.account_id;
+		let addr = builder::bare_instantiate(Code::Upload(wasm))
+			.value(100 * min_balance)
+			.build_and_unwrap_account_id();
 
 		// Call the contract with a fixed gas limit. It must run out of gas because it just
 		// loops forever.
 		assert_err_ignore_postinfo!(
-			Contracts::call(
-				RuntimeOrigin::signed(ALICE),
-				addr, // newly created account
-				0,
-				Weight::from_parts(1_000_000_000_000, u64::MAX),
-				None,
-				vec![],
-			),
+			builder::call(addr)
+				.gas_limit(Weight::from_parts(1_000_000_000_000, u64::MAX))
+				.build(),
 			Error::<Test>::OutOfGas,
 		);
 	});
@@ -899,36 +834,18 @@ fn run_out_of_fuel_host() {
 		let min_balance = Contracts::min_balance();
 		let _ = <Test as Config>::Currency::set_balance(&ALICE, 1000 * min_balance);
 
-		let addr = Contracts::bare_instantiate(
-			ALICE,
-			min_balance * 100,
-			GAS_LIMIT,
-			None,
-			Code::Upload(code),
-			vec![],
-			vec![],
-			DebugInfo::Skip,
-			CollectEvents::Skip,
-		)
-		.result
-		.unwrap()
-		.account_id;
+		let addr = builder::bare_instantiate(Code::Upload(code))
+			.value(min_balance * 100)
+			.build_and_unwrap_account_id();
 
 		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 = Contracts::bare_call(
-			ALICE,
-			addr.clone(),
-			0,
-			gas_limit,
-			None,
-			ExtensionInput { extension_id: 0, func_id: 2, extra: &u32::MAX.encode() }.into(),
-			DebugInfo::Skip,
-			CollectEvents::Skip,
-			Determinism::Enforced,
-		)
-		.result;
+		let result = builder::bare_call(addr.clone())
+			.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);
 	});
 }
@@ -938,63 +855,20 @@ fn gas_syncs_work() {
 	let (code, _code_hash) = compile_module::<Test>("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 addr = Contracts::bare_instantiate(
-			ALICE,
-			0,
-			GAS_LIMIT,
-			None,
-			Code::Upload(code),
-			vec![],
-			vec![],
-			DebugInfo::Skip,
-			CollectEvents::Skip,
-		)
-		.result
-		.unwrap()
-		.account_id;
+		let addr = builder::bare_instantiate(Code::Upload(code)).build_and_unwrap_account_id();
 
-		let result = Contracts::bare_call(
-			ALICE,
-			addr.clone(),
-			0,
-			GAS_LIMIT,
-			None,
-			0u32.encode(),
-			DebugInfo::Skip,
-			CollectEvents::Skip,
-			Determinism::Enforced,
-		);
+		let result = builder::bare_call(addr.clone()).data(0u32.encode()).build();
 		assert_ok!(result.result);
 		let engine_consumed_noop = result.gas_consumed.ref_time();
 
-		let result = Contracts::bare_call(
-			ALICE,
-			addr.clone(),
-			0,
-			GAS_LIMIT,
-			None,
-			1u32.encode(),
-			DebugInfo::Skip,
-			CollectEvents::Skip,
-			Determinism::Enforced,
-		);
+		let result = builder::bare_call(addr.clone()).data(1u32.encode()).build();
 		assert_ok!(result.result);
 		let gas_consumed_once = result.gas_consumed.ref_time();
 		let host_consumed_once =
 			<Test as Config>::Schedule::get().host_fn_weights.caller_is_origin.ref_time();
 		let engine_consumed_once = gas_consumed_once - host_consumed_once - engine_consumed_noop;
 
-		let result = Contracts::bare_call(
-			ALICE,
-			addr,
-			0,
-			GAS_LIMIT,
-			None,
-			2u32.encode(),
-			DebugInfo::Skip,
-			CollectEvents::Skip,
-			Determinism::Enforced,
-		);
+		let result = builder::bare_call(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;
@@ -1018,56 +892,21 @@ fn instantiate_unique_trie_id() {
 			.unwrap();
 
 		// Instantiate the contract and store its trie id for later comparison.
-		let addr = Contracts::bare_instantiate(
-			ALICE,
-			0,
-			GAS_LIMIT,
-			None,
-			Code::Existing(code_hash),
-			vec![],
-			vec![],
-			DebugInfo::Skip,
-			CollectEvents::Skip,
-		)
-		.result
-		.unwrap()
-		.account_id;
+		let addr =
+			builder::bare_instantiate(Code::Existing(code_hash)).build_and_unwrap_account_id();
 		let trie_id = get_contract(&addr).trie_id;
 
 		// Try to instantiate it again without termination should yield an error.
 		assert_err_ignore_postinfo!(
-			Contracts::instantiate(
-				RuntimeOrigin::signed(ALICE),
-				0,
-				GAS_LIMIT,
-				None,
-				code_hash,
-				vec![],
-				vec![],
-			),
+			builder::instantiate(code_hash).build(),
 			<Error<Test>>::DuplicateContract,
 		);
 
 		// Terminate the contract.
-		assert_ok!(Contracts::call(
-			RuntimeOrigin::signed(ALICE),
-			addr.clone(),
-			0,
-			GAS_LIMIT,
-			None,
-			vec![]
-		));
+		assert_ok!(builder::call(addr.clone()).build());
 
 		// Re-Instantiate after termination.
-		assert_ok!(Contracts::instantiate(
-			RuntimeOrigin::signed(ALICE),
-			0,
-			GAS_LIMIT,
-			None,
-			code_hash,
-			vec![],
-			vec![],
-		));
+		assert_ok!(builder::instantiate(code_hash).build());
 
 		// Trie ids shouldn't match or we might have a collision
 		assert_ne!(trie_id, get_contract(&addr).trie_id);
@@ -1081,42 +920,22 @@ fn storage_max_value_limit() {
 	ExtBuilder::default().existential_deposit(50).build().execute_with(|| {
 		// Create
 		let _ = <Test as Config>::Currency::set_balance(&ALICE, 1_000_000);
-		let addr = Contracts::bare_instantiate(
-			ALICE,
-			30_000,
-			GAS_LIMIT,
-			None,
-			Code::Upload(wasm),
-			vec![],
-			vec![],
-			DebugInfo::Skip,
-			CollectEvents::Skip,
-		)
-		.result
-		.unwrap()
-		.account_id;
+		let addr = builder::bare_instantiate(Code::Upload(wasm))
+			.value(30_000)
+			.build_and_unwrap_account_id();
 		get_contract(&addr);
 
 		// Call contract with allowed storage value.
-		assert_ok!(Contracts::call(
-			RuntimeOrigin::signed(ALICE),
-			addr.clone(),
-			0,
-			GAS_LIMIT.set_ref_time(GAS_LIMIT.ref_time() * 2), // we are copying a huge buffer
-			None,
-			<Test as Config>::Schedule::get().limits.payload_len.encode(),
-		));
+		assert_ok!(builder::call(addr.clone())
+			.gas_limit(GAS_LIMIT.set_ref_time(GAS_LIMIT.ref_time() * 2)) // we are copying a huge buffer
+			.data(<Test as Config>::Schedule::get().limits.payload_len.encode())
+			.build());
 
 		// Call contract with too large a storage value.
 		assert_err_ignore_postinfo!(
-			Contracts::call(
-				RuntimeOrigin::signed(ALICE),
-				addr,
-				0,
-				GAS_LIMIT,
-				None,
-				(<Test as Config>::Schedule::get().limits.payload_len + 1).encode(),
-			),
+			builder::call(addr)
+				.data((<Test as Config>::Schedule::get().limits.payload_len + 1).encode())
+				.build(),
 			Error::<Test>::ValueTooLarge,
 		);
 	});
@@ -1132,20 +951,9 @@ fn deploy_and_call_other_contract() {
 
 		// Create
 		let _ = <Test as Config>::Currency::set_balance(&ALICE, 1_000_000);
-		let caller_addr = Contracts::bare_instantiate(
-			ALICE,
-			100_000,
-			GAS_LIMIT,
-			None,
-			Code::Upload(caller_wasm),
-			vec![],
-			vec![],
-			DebugInfo::Skip,
-			CollectEvents::Skip,
-		)
-		.result
-		.unwrap()
-		.account_id;
+		let caller_addr = builder::bare_instantiate(Code::Upload(caller_wasm))
+			.value(100_000)
+			.build_and_unwrap_account_id();
 		Contracts::bare_upload_code(ALICE, callee_wasm, None, Determinism::Enforced).unwrap();
 
 		let callee_addr = Contracts::contract_address(
@@ -1160,14 +968,9 @@ fn deploy_and_call_other_contract() {
 
 		// Call BOB contract, which attempts to instantiate and call the callee contract and
 		// makes various assertions on the results from those calls.
-		assert_ok!(Contracts::call(
-			RuntimeOrigin::signed(ALICE),
-			caller_addr.clone(),
-			0,
-			GAS_LIMIT,
-			None,
-			callee_code_hash.as_ref().to_vec(),
-		));
+		assert_ok!(builder::call(caller_addr.clone())
+			.data(callee_code_hash.as_ref().to_vec())
+			.build());
 
 		assert_eq!(
 			System::events(),
@@ -1266,20 +1069,9 @@ fn delegate_call() {
 		let _ = <Test as Config>::Currency::set_balance(&ALICE, 1_000_000);
 
 		// Instantiate the 'caller'
-		let caller_addr = Contracts::bare_instantiate(
-			ALICE,
-			300_000,
-			GAS_LIMIT,
-			None,
-			Code::Upload(caller_wasm),
-			vec![],
-			vec![],
-			DebugInfo::Skip,
-			CollectEvents::Skip,
-		)
-		.result
-		.unwrap()
-		.account_id;
+		let caller_addr = builder::bare_instantiate(Code::Upload(caller_wasm))
+			.value(300_000)
+			.build_and_unwrap_account_id();
 		// Only upload 'callee' code
 		assert_ok!(Contracts::upload_code(
 			RuntimeOrigin::signed(ALICE),
@@ -1288,14 +1080,10 @@ fn delegate_call() {
 			Determinism::Enforced,
 		));
 
-		assert_ok!(Contracts::call(
-			RuntimeOrigin::signed(ALICE),
-			caller_addr.clone(),
-			1337,
-			GAS_LIMIT,
-			None,
-			callee_code_hash.as_ref().to_vec(),
-		));
+		assert_ok!(builder::call(caller_addr.clone())
+			.value(1337)
+			.data(callee_code_hash.as_ref().to_vec())
+			.build());
 	});
 }
 
@@ -1306,20 +1094,9 @@ fn transfer_expendable_cannot_kill_account() {
 		let _ = <Test as Config>::Currency::set_balance(&ALICE, 1_000_000);
 
 		// Instantiate the BOB contract.
-		let addr = Contracts::bare_instantiate(
-			ALICE,
-			1_000,
-			GAS_LIMIT,
-			None,
-			Code::Upload(wasm),
-			vec![],
-			vec![],
-			DebugInfo::Skip,
-			CollectEvents::Skip,
-		)
-		.result
-		.unwrap()
-		.account_id;
+		let addr = builder::bare_instantiate(Code::Upload(wasm))
+			.value(1_000)
+			.build_and_unwrap_account_id();
 
 		// Check that the BOB contract has been instantiated.
 		get_contract(&addr);
@@ -1355,34 +1132,16 @@ fn cannot_self_destruct_through_draining() {
 		let min_balance = Contracts::min_balance();
 
 		// Instantiate the BOB contract.
-		let addr = Contracts::bare_instantiate(
-			ALICE,
-			value,
-			GAS_LIMIT,
-			None,
-			Code::Upload(wasm),
-			vec![],
-			vec![],
-			DebugInfo::Skip,
-			CollectEvents::Skip,
-		)
-		.result
-		.unwrap()
-		.account_id;
+		let addr = builder::bare_instantiate(Code::Upload(wasm))
+			.value(value)
+			.build_and_unwrap_account_id();
 
 		// 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!(Contracts::call(
-			RuntimeOrigin::signed(ALICE),
-			addr.clone(),
-			0,
-			GAS_LIMIT,
-			None,
-			vec![]
-		));
+		assert_ok!(builder::call(addr.clone()).build());
 
 		// Make sure the account wasn't remove by sending all free balance away.
 		assert_eq!(
@@ -1400,20 +1159,7 @@ fn cannot_self_destruct_through_storage_refund_after_price_change() {
 		let min_balance = Contracts::min_balance();
 
 		// Instantiate the BOB contract.
-		let addr = Contracts::bare_instantiate(
-			ALICE,
-			0,
-			GAS_LIMIT,
-			None,
-			Code::Upload(wasm),
-			vec![],
-			vec![],
-			DebugInfo::Skip,
-			CollectEvents::Skip,
-		)
-		.result
-		.unwrap()
-		.account_id;
+		let addr = builder::bare_instantiate(Code::Upload(wasm)).build_and_unwrap_account_id();
 
 		let info_deposit = test_utils::contract_info_storage_deposit(&addr);
 
@@ -1423,27 +1169,13 @@ fn cannot_self_destruct_through_storage_refund_after_price_change() {
 		assert_eq!(<Test as Config>::Currency::total_balance(&addr), info_deposit + min_balance);
 
 		// Create 100 bytes of storage with a price of per byte and a single storage item of price 2
-		assert_ok!(Contracts::call(
-			RuntimeOrigin::signed(ALICE),
-			addr.clone(),
-			0,
-			GAS_LIMIT,
-			None,
-			100u32.to_le_bytes().to_vec()
-		));
+		assert_ok!(builder::call(addr.clone()).data(100u32.to_le_bytes().to_vec()).build());
 		assert_eq!(get_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!(Contracts::call(
-			RuntimeOrigin::signed(ALICE),
-			addr.clone(),
-			0,
-			GAS_LIMIT,
-			None,
-			0u32.to_le_bytes().to_vec()
-		));
+		assert_ok!(builder::call(addr.clone()).data(0u32.to_le_bytes().to_vec()).build());
 
 		// Make sure the account wasn't removed by the refund
 		assert_eq!(
@@ -1461,20 +1193,9 @@ fn cannot_self_destruct_while_live() {
 		let _ = <Test as Config>::Currency::set_balance(&ALICE, 1_000_000);
 
 		// Instantiate the BOB contract.
-		let addr = Contracts::bare_instantiate(
-			ALICE,
-			100_000,
-			GAS_LIMIT,
-			None,
-			Code::Upload(wasm),
-			vec![],
-			vec![],
-			DebugInfo::Skip,
-			CollectEvents::Skip,
-		)
-		.result
-		.unwrap()
-		.account_id;
+		let addr = builder::bare_instantiate(Code::Upload(wasm))
+			.value(100_000)
+			.build_and_unwrap_account_id();
 
 		// Check that the BOB contract has been instantiated.
 		get_contract(&addr);
@@ -1482,14 +1203,7 @@ fn cannot_self_destruct_while_live() {
 		// Call BOB with input data, forcing it make a recursive call to itself to
 		// self-destruct, resulting in a trap.
 		assert_err_ignore_postinfo!(
-			Contracts::call(
-				RuntimeOrigin::signed(ALICE),
-				addr.clone(),
-				0,
-				GAS_LIMIT,
-				None,
-				vec![0],
-			),
+			builder::call(addr.clone()).data(vec![0]).build(),
 			Error::<Test>::ContractTrapped,
 		);
 
@@ -1507,20 +1221,9 @@ fn self_destruct_works() {
 		let min_balance = Contracts::min_balance();
 
 		// Instantiate the BOB contract.
-		let addr = Contracts::bare_instantiate(
-			ALICE,
-			100_000,
-			GAS_LIMIT,
-			None,
-			Code::Upload(wasm),
-			vec![],
-			vec![],
-			DebugInfo::Skip,
-			CollectEvents::Skip,
-		)
-		.result
-		.unwrap()
-		.account_id;
+		let addr = builder::bare_instantiate(Code::Upload(wasm))
+			.value(100_000)
+			.build_and_unwrap_account_id();
 
 		// Check that the BOB contract has been instantiated.
 		let _ = get_contract(&addr);
@@ -1531,10 +1234,7 @@ fn self_destruct_works() {
 		initialize_block(2);
 
 		// Call BOB without input data which triggers termination.
-		assert_matches!(
-			Contracts::call(RuntimeOrigin::signed(ALICE), addr.clone(), 0, GAS_LIMIT, None, vec![],),
-			Ok(_)
-		);
+		assert_matches!(builder::call(addr.clone()).build(), Ok(_));
 
 		// Check that code is still there but refcount dropped to zero.
 		assert_refcount!(&code_hash, 0);
@@ -1621,20 +1321,10 @@ fn destroy_contract_and_transfer_funds() {
 
 		// This deploys the BOB contract, which in turn deploys the CHARLIE contract during
 		// construction.
-		let addr_bob = Contracts::bare_instantiate(
-			ALICE,
-			200_000,
-			GAS_LIMIT,
-			None,
-			Code::Upload(caller_wasm),
-			callee_code_hash.as_ref().to_vec(),
-			vec![],
-			DebugInfo::Skip,
-			CollectEvents::Skip,
-		)
-		.result
-		.unwrap()
-		.account_id;
+		let addr_bob = builder::bare_instantiate(Code::Upload(caller_wasm))
+			.value(200_000)
+			.data(callee_code_hash.as_ref().to_vec())
+			.build_and_unwrap_account_id();
 
 		// Check that the CHARLIE contract has been instantiated.
 		let addr_charlie =
@@ -1642,14 +1332,7 @@ fn destroy_contract_and_transfer_funds() {
 		get_contract(&addr_charlie);
 
 		// Call BOB, which calls CHARLIE, forcing CHARLIE to self-destruct.
-		assert_ok!(Contracts::call(
-			RuntimeOrigin::signed(ALICE),
-			addr_bob,
-			0,
-			GAS_LIMIT,
-			None,
-			addr_charlie.encode(),
-		));
+		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());
@@ -1664,15 +1347,7 @@ fn cannot_self_destruct_in_constructor() {
 
 		// Fail to instantiate the BOB because the constructor calls seal_terminate.
 		assert_err_ignore_postinfo!(
-			Contracts::instantiate_with_code(
-				RuntimeOrigin::signed(ALICE),
-				100_000,
-				GAS_LIMIT,
-				None,
-				wasm,
-				vec![],
-				vec![],
-			),
+			builder::instantiate_with_code(wasm).value(100_000).build(),
 			Error::<Test>::TerminatedInConstructor,
 		);
 	});
@@ -1686,20 +1361,9 @@ fn crypto_hashes() {
 		let _ = <Test as Config>::Currency::set_balance(&ALICE, 1_000_000);
 
 		// Instantiate the CRYPTO_HASHES contract.
-		let addr = Contracts::bare_instantiate(
-			ALICE,
-			100_000,
-			GAS_LIMIT,
-			None,
-			Code::Upload(wasm),
-			vec![],
-			vec![],
-			DebugInfo::Skip,
-			CollectEvents::Skip,
-		)
-		.result
-		.unwrap()
-		.account_id;
+		let addr = builder::bare_instantiate(Code::Upload(wasm))
+			.value(100_000)
+			.build_and_unwrap_account_id();
 		// Perform the call.
 		let input = b"_DEAD_BEEF";
 		use sp_io::hashing::*;
@@ -1748,36 +1412,13 @@ fn transfer_return_code() {
 		let min_balance = Contracts::min_balance();
 		let _ = <Test as Config>::Currency::set_balance(&ALICE, 1000 * min_balance);
 
-		let addr = Contracts::bare_instantiate(
-			ALICE,
-			min_balance * 100,
-			GAS_LIMIT,
-			None,
-			Code::Upload(wasm),
-			vec![],
-			vec![],
-			DebugInfo::Skip,
-			CollectEvents::Skip,
-		)
-		.result
-		.unwrap()
-		.account_id;
+		let addr = builder::bare_instantiate(Code::Upload(wasm))
+			.value(min_balance * 100)
+			.build_and_unwrap_account_id();
 
 		// Contract has only the minimal balance so any transfer will fail.
 		<Test as Config>::Currency::set_balance(&addr, min_balance);
-		let result = Contracts::bare_call(
-			ALICE,
-			addr.clone(),
-			0,
-			GAS_LIMIT,
-			None,
-			vec![],
-			DebugInfo::Skip,
-			CollectEvents::Skip,
-			Determinism::Enforced,
-		)
-		.result
-		.unwrap();
+		let result = builder::bare_call(addr.clone()).build_and_unwrap_result();
 		assert_return_code!(result, RuntimeReturnCode::TransferFailed);
 	});
 }
@@ -1791,113 +1432,60 @@ fn call_return_code() {
 		let _ = <Test as Config>::Currency::set_balance(&ALICE, 1000 * min_balance);
 		let _ = <Test as Config>::Currency::set_balance(&CHARLIE, 1000 * min_balance);
 
-		let addr_bob = Contracts::bare_instantiate(
-			ALICE,
-			min_balance * 100,
-			GAS_LIMIT,
-			None,
-			Code::Upload(caller_code),
-			vec![0],
-			vec![],
-			DebugInfo::Skip,
-			CollectEvents::Skip,
-		)
-		.result
-		.unwrap()
-		.account_id;
+		let addr_bob = builder::bare_instantiate(Code::Upload(caller_code))
+			.value(min_balance * 100)
+			.data(vec![0])
+			.build_and_unwrap_account_id();
 		<Test as Config>::Currency::set_balance(&addr_bob, min_balance);
 
 		// Contract calls into Django which is no valid contract
-		let result = Contracts::bare_call(
-			ALICE,
-			addr_bob.clone(),
-			0,
-			GAS_LIMIT,
-			None,
-			AsRef::<[u8]>::as_ref(&DJANGO).to_vec(),
-			DebugInfo::Skip,
-			CollectEvents::Skip,
-			Determinism::Enforced,
-		)
-		.result
-		.unwrap();
+		let result = builder::bare_call(addr_bob.clone())
+			.data(AsRef::<[u8]>::as_ref(&DJANGO).to_vec())
+			.build_and_unwrap_result();
 		assert_return_code!(result, RuntimeReturnCode::NotCallable);
 
-		let addr_django = Contracts::bare_instantiate(
-			CHARLIE,
-			min_balance * 100,
-			GAS_LIMIT,
-			None,
-			Code::Upload(callee_code),
-			vec![0],
-			vec![],
-			DebugInfo::Skip,
-			CollectEvents::Skip,
-		)
-		.result
-		.unwrap()
-		.account_id;
+		let addr_django = builder::bare_instantiate(Code::Upload(callee_code))
+			.origin(CHARLIE)
+			.value(min_balance * 100)
+			.data(vec![0])
+			.build_and_unwrap_account_id();
 		<Test as Config>::Currency::set_balance(&addr_django, min_balance);
 
 		// Contract has only the minimal balance so any transfer will fail.
-		let result = Contracts::bare_call(
-			ALICE,
-			addr_bob.clone(),
-			0,
-			GAS_LIMIT,
-			None,
-			AsRef::<[u8]>::as_ref(&addr_django)
-				.iter()
-				.chain(&0u32.to_le_bytes())
-				.cloned()
-				.collect(),
-			DebugInfo::Skip,
-			CollectEvents::Skip,
-			Determinism::Enforced,
-		)
-		.result
-		.unwrap();
+		let result = builder::bare_call(addr_bob.clone())
+			.data(
+				AsRef::<[u8]>::as_ref(&addr_django)
+					.iter()
+					.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(&addr_bob, min_balance + 1000);
-		let result = Contracts::bare_call(
-			ALICE,
-			addr_bob.clone(),
-			0,
-			GAS_LIMIT,
-			None,
-			AsRef::<[u8]>::as_ref(&addr_django)
-				.iter()
-				.chain(&1u32.to_le_bytes())
-				.cloned()
-				.collect(),
-			DebugInfo::Skip,
-			CollectEvents::Skip,
-			Determinism::Enforced,
-		)
-		.result
-		.unwrap();
+		let result = builder::bare_call(addr_bob.clone())
+			.data(
+				AsRef::<[u8]>::as_ref(&addr_django)
+					.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 = Contracts::bare_call(
-			ALICE,
-			addr_bob,
-			0,
-			GAS_LIMIT,
-			None,
-			AsRef::<[u8]>::as_ref(&addr_django)
-				.iter()
-				.chain(&2u32.to_le_bytes())
-				.cloned()
-				.collect(),
-			DebugInfo::Skip,
-			CollectEvents::Skip,
-			Determinism::Enforced,
-		)
-		.result
-		.unwrap();
+		let result = builder::bare_call(addr_bob)
+			.data(
+				AsRef::<[u8]>::as_ref(&addr_django)
+					.iter()
+					.chain(&2u32.to_le_bytes())
+					.cloned()
+					.collect(),
+			)
+			.build_and_unwrap_result();
 		assert_return_code!(result, RuntimeReturnCode::CalleeTrapped);
 	});
 }
@@ -1912,95 +1500,34 @@ fn instantiate_return_code() {
 		let _ = <Test as Config>::Currency::set_balance(&CHARLIE, 1000 * min_balance);
 		let callee_hash = callee_hash.as_ref().to_vec();
 
-		assert_ok!(Contracts::instantiate_with_code(
-			RuntimeOrigin::signed(ALICE),
-			min_balance * 100,
-			GAS_LIMIT,
-			None,
-			callee_code,
-			vec![],
-			vec![],
-		));
+		assert_ok!(builder::instantiate_with_code(callee_code).value(min_balance * 100).build());
 
-		let addr = Contracts::bare_instantiate(
-			ALICE,
-			min_balance * 100,
-			GAS_LIMIT,
-			None,
-			Code::Upload(caller_code),
-			vec![],
-			vec![],
-			DebugInfo::Skip,
-			CollectEvents::Skip,
-		)
-		.result
-		.unwrap()
-		.account_id;
+		let addr = builder::bare_instantiate(Code::Upload(caller_code))
+			.value(min_balance * 100)
+			.build_and_unwrap_account_id();
 
 		// Contract has only the minimal balance so any transfer will fail.
 		<Test as Config>::Currency::set_balance(&addr, min_balance);
-		let result = Contracts::bare_call(
-			ALICE,
-			addr.clone(),
-			0,
-			GAS_LIMIT,
-			None,
-			callee_hash.clone(),
-			DebugInfo::Skip,
-			CollectEvents::Skip,
-			Determinism::Enforced,
-		)
-		.result
-		.unwrap();
+		let result = builder::bare_call(addr.clone())
+			.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(&addr, min_balance + 10_000);
-		let result = Contracts::bare_call(
-			ALICE,
-			addr.clone(),
-			0,
-			GAS_LIMIT,
-			None,
-			vec![0; 33],
-			DebugInfo::Skip,
-			CollectEvents::Skip,
-			Determinism::Enforced,
-		)
-		.result
-		.unwrap();
+		let result = builder::bare_call(addr.clone()).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 = Contracts::bare_call(
-			ALICE,
-			addr.clone(),
-			0,
-			GAS_LIMIT,
-			None,
-			callee_hash.iter().chain(&1u32.to_le_bytes()).cloned().collect(),
-			DebugInfo::Skip,
-			CollectEvents::Skip,
-			Determinism::Enforced,
-		)
-		.result
-		.unwrap();
+		let result = builder::bare_call(addr.clone())
+			.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 = Contracts::bare_call(
-			ALICE,
-			addr,
-			0,
-			GAS_LIMIT,
-			None,
-			callee_hash.iter().chain(&2u32.to_le_bytes()).cloned().collect(),
-			DebugInfo::Skip,
-			CollectEvents::Skip,
-			Determinism::Enforced,
-		)
-		.result
-		.unwrap();
+		let result = builder::bare_call(addr)
+			.data(callee_hash.iter().chain(&2u32.to_le_bytes()).cloned().collect())
+			.build_and_unwrap_result();
 		assert_return_code!(result, RuntimeReturnCode::CalleeTrapped);
 	});
 }
@@ -2013,15 +1540,7 @@ fn disabled_chain_extension_wont_deploy() {
 		let _ = <Test as Config>::Currency::set_balance(&ALICE, 1000 * min_balance);
 		TestExtension::disable();
 		assert_err_ignore_postinfo!(
-			Contracts::instantiate_with_code(
-				RuntimeOrigin::signed(ALICE),
-				3 * min_balance,
-				GAS_LIMIT,
-				None,
-				code,
-				vec![],
-				vec![],
-			),
+			builder::instantiate_with_code(code).value(3 * min_balance).build(),
 			<Error<Test>>::CodeRejected,
 		);
 	});
@@ -2033,23 +1552,12 @@ fn disabled_chain_extension_errors_on_call() {
 	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 addr = Contracts::bare_instantiate(
-			ALICE,
-			min_balance * 100,
-			GAS_LIMIT,
-			None,
-			Code::Upload(code),
-			vec![],
-			vec![],
-			DebugInfo::Skip,
-			CollectEvents::Skip,
-		)
-		.result
-		.unwrap()
-		.account_id;
+		let addr = builder::bare_instantiate(Code::Upload(code))
+			.value(min_balance * 100)
+			.build_and_unwrap_account_id();
 		TestExtension::disable();
 		assert_err_ignore_postinfo!(
-			Contracts::call(RuntimeOrigin::signed(ALICE), addr.clone(), 0, GAS_LIMIT, None, vec![],),
+			builder::call(addr.clone()).build(),
 			Error::<Test>::CodeRejected,
 		);
 	});
@@ -2061,141 +1569,61 @@ fn chain_extension_works() {
 	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 addr = Contracts::bare_instantiate(
-			ALICE,
-			min_balance * 100,
-			GAS_LIMIT,
-			None,
-			Code::Upload(code),
-			vec![],
-			vec![],
-			DebugInfo::Skip,
-			CollectEvents::Skip,
-		)
-		.result
-		.unwrap()
-		.account_id;
+		let addr = builder::bare_instantiate(Code::Upload(code))
+			.value(min_balance * 100)
+			.build_and_unwrap_account_id();
 
 		// 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 = Contracts::bare_call(
-			ALICE,
-			addr.clone(),
-			0,
-			GAS_LIMIT,
-			None,
-			input.clone(),
-			DebugInfo::Skip,
-			CollectEvents::Skip,
-			Determinism::Enforced,
-		);
+		let result = builder::bare_call(addr.clone()).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
-		Contracts::bare_call(
-			ALICE,
-			addr.clone(),
-			0,
-			GAS_LIMIT,
-			None,
-			ExtensionInput { extension_id: 0, func_id: 1, extra: &[] }.into(),
-			DebugInfo::Skip,
-			CollectEvents::Skip,
-			Determinism::Enforced,
-		)
-		.result
-		.unwrap();
+		builder::bare_call(addr.clone())
+			.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 = Contracts::bare_call(
-			ALICE,
-			addr.clone(),
-			0,
-			GAS_LIMIT,
-			None,
-			ExtensionInput { extension_id: 0, func_id: 2, extra: &0u32.encode() }.into(),
-			DebugInfo::Skip,
-			CollectEvents::Skip,
-			Determinism::Enforced,
-		);
+		let result = builder::bare_call(addr.clone())
+			.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 = Contracts::bare_call(
-			ALICE,
-			addr.clone(),
-			0,
-			GAS_LIMIT,
-			None,
-			ExtensionInput { extension_id: 0, func_id: 2, extra: &42u32.encode() }.into(),
-			DebugInfo::Skip,
-			CollectEvents::Skip,
-			Determinism::Enforced,
-		);
+		let result = builder::bare_call(addr.clone())
+			.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 = Contracts::bare_call(
-			ALICE,
-			addr.clone(),
-			0,
-			GAS_LIMIT,
-			None,
-			ExtensionInput { extension_id: 0, func_id: 2, extra: &95u32.encode() }.into(),
-			DebugInfo::Skip,
-			CollectEvents::Skip,
-			Determinism::Enforced,
-		);
+		let result = builder::bare_call(addr.clone())
+			.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 = Contracts::bare_call(
-			ALICE,
-			addr.clone(),
-			0,
-			GAS_LIMIT,
-			None,
-			ExtensionInput { extension_id: 0, func_id: 3, extra: &[] }.into(),
-			DebugInfo::Skip,
-			CollectEvents::Skip,
-			Determinism::Enforced,
-		)
-		.result
-		.unwrap();
+		let result = builder::bare_call(addr.clone())
+			.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 = Contracts::bare_call(
-			ALICE,
-			addr.clone(),
-			0,
-			GAS_LIMIT,
-			None,
-			ExtensionInput { extension_id: 1, func_id: 0, extra: &[] }.into(),
-			DebugInfo::Skip,
-			CollectEvents::Skip,
-			Determinism::Enforced,
-		)
-		.result
-		.unwrap();
+		let result = builder::bare_call(addr.clone())
+			.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!(
-			Contracts::call(
-				RuntimeOrigin::signed(ALICE),
-				addr.clone(),
-				0,
-				GAS_LIMIT,
-				None,
-				ExtensionInput { extension_id: 2, func_id: 0, extra: &[] }.into(),
-			),
+			builder::call(addr.clone())
+				.data(ExtensionInput { extension_id: 2, func_id: 0, extra: &[] }.into())
+				.build(),
 			Error::<Test>::NoChainExtension,
 		);
 	});
@@ -2207,20 +1635,9 @@ fn chain_extension_temp_storage_works() {
 	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 addr = Contracts::bare_instantiate(
-			ALICE,
-			min_balance * 100,
-			GAS_LIMIT,
-			None,
-			Code::Upload(code),
-			vec![],
-			vec![],
-			DebugInfo::Skip,
-			CollectEvents::Skip,
-		)
-		.result
-		.unwrap()
-		.account_id;
+		let addr = builder::bare_instantiate(Code::Upload(code))
+			.value(min_balance * 100)
+			.build_and_unwrap_account_id();
 
 		// Call func 0 and func 1 back to back.
 		let stop_recursion = 0u8;
@@ -2231,20 +1648,7 @@ fn chain_extension_temp_storage_works() {
 				.as_ref(),
 		);
 
-		assert_ok!(
-			Contracts::bare_call(
-				ALICE,
-				addr.clone(),
-				0,
-				GAS_LIMIT,
-				None,
-				input.clone(),
-				DebugInfo::Skip,
-				CollectEvents::Skip,
-				Determinism::Enforced,
-			)
-			.result
-		);
+		assert_ok!(builder::bare_call(addr.clone()).data(input.clone()).build().result);
 	})
 }
 
@@ -2255,20 +1659,9 @@ fn lazy_removal_works() {
 		let min_balance = Contracts::min_balance();
 		let _ = <Test as Config>::Currency::set_balance(&ALICE, 1000 * min_balance);
 
-		let addr = Contracts::bare_instantiate(
-			ALICE,
-			min_balance * 100,
-			GAS_LIMIT,
-			None,
-			Code::Upload(code),
-			vec![],
-			vec![],
-			DebugInfo::Skip,
-			CollectEvents::Skip,
-		)
-		.result
-		.unwrap()
-		.account_id;
+		let addr = builder::bare_instantiate(Code::Upload(code))
+			.value(min_balance * 100)
+			.build_and_unwrap_account_id();
 
 		let info = get_contract(&addr);
 		let trie = &info.child_trie_info();
@@ -2277,14 +1670,7 @@ fn lazy_removal_works() {
 		child::put(trie, &[99], &42);
 
 		// Terminate the contract
-		assert_ok!(Contracts::call(
-			RuntimeOrigin::signed(ALICE),
-			addr.clone(),
-			0,
-			GAS_LIMIT,
-			None,
-			vec![]
-		));
+		assert_ok!(builder::call(addr.clone()).build());
 
 		// Contract info should be gone
 		assert!(!<ContractInfoOf::<Test>>::contains_key(&addr));
@@ -2309,20 +1695,10 @@ fn lazy_batch_removal_works() {
 		let mut tries: Vec<child::ChildInfo> = vec![];
 
 		for i in 0..3u8 {
-			let addr = Contracts::bare_instantiate(
-				ALICE,
-				min_balance * 100,
-				GAS_LIMIT,
-				None,
-				Code::Upload(code.clone()),
-				vec![],
-				vec![i],
-				DebugInfo::Skip,
-				CollectEvents::Skip,
-			)
-			.result
-			.unwrap()
-			.account_id;
+			let addr = builder::bare_instantiate(Code::Upload(code.clone()))
+				.value(min_balance * 100)
+				.salt(vec![i])
+				.build_and_unwrap_account_id();
 
 			let info = get_contract(&addr);
 			let trie = &info.child_trie_info();
@@ -2332,14 +1708,7 @@ fn lazy_batch_removal_works() {
 
 			// Terminate the contract. Contract info should be gone, but value should be still there
 			// as the lazy removal did not run, yet.
-			assert_ok!(Contracts::call(
-				RuntimeOrigin::signed(ALICE),
-				addr.clone(),
-				0,
-				GAS_LIMIT,
-				None,
-				vec![]
-			));
+			assert_ok!(builder::call(addr.clone()).build());
 
 			assert!(!<ContractInfoOf::<Test>>::contains_key(&addr));
 			assert_matches!(child::get(trie, &[99]), Some(42));
@@ -2375,20 +1744,9 @@ fn lazy_removal_partial_remove_works() {
 		let min_balance = Contracts::min_balance();
 		let _ = <Test as Config>::Currency::set_balance(&ALICE, 1000 * min_balance);
 
-		let addr = Contracts::bare_instantiate(
-			ALICE,
-			min_balance * 100,
-			GAS_LIMIT,
-			None,
-			Code::Upload(code),
-			vec![],
-			vec![],
-			DebugInfo::Skip,
-			CollectEvents::Skip,
-		)
-		.result
-		.unwrap()
-		.account_id;
+		let addr = builder::bare_instantiate(Code::Upload(code))
+			.value(min_balance * 100)
+			.build_and_unwrap_account_id();
 
 		let info = get_contract(&addr);
 
@@ -2399,14 +1757,7 @@ fn lazy_removal_partial_remove_works() {
 		<ContractInfoOf<Test>>::insert(&addr, info.clone());
 
 		// Terminate the contract
-		assert_ok!(Contracts::call(
-			RuntimeOrigin::signed(ALICE),
-			addr.clone(),
-			0,
-			GAS_LIMIT,
-			None,
-			vec![]
-		));
+		assert_ok!(builder::call(addr.clone()).build());
 
 		// Contract info should be gone
 		assert!(!<ContractInfoOf::<Test>>::contains_key(&addr));
@@ -2457,20 +1808,9 @@ fn lazy_removal_does_no_run_on_low_remaining_weight() {
 		let min_balance = Contracts::min_balance();
 		let _ = <Test as Config>::Currency::set_balance(&ALICE, 1000 * min_balance);
 
-		let addr = Contracts::bare_instantiate(
-			ALICE,
-			min_balance * 100,
-			GAS_LIMIT,
-			None,
-			Code::Upload(code),
-			vec![],
-			vec![],
-			DebugInfo::Skip,
-			CollectEvents::Skip,
-		)
-		.result
-		.unwrap()
-		.account_id;
+		let addr = builder::bare_instantiate(Code::Upload(code))
+			.value(min_balance * 100)
+			.build_and_unwrap_account_id();
 
 		let info = get_contract(&addr);
 		let trie = &info.child_trie_info();
@@ -2479,14 +1819,7 @@ fn lazy_removal_does_no_run_on_low_remaining_weight() {
 		child::put(trie, &[99], &42);
 
 		// Terminate the contract
-		assert_ok!(Contracts::call(
-			RuntimeOrigin::signed(ALICE),
-			addr.clone(),
-			0,
-			GAS_LIMIT,
-			None,
-			vec![]
-		));
+		assert_ok!(builder::call(addr.clone()).build());
 
 		// Contract info should be gone
 		assert!(!<ContractInfoOf::<Test>>::contains_key(&addr));
@@ -2529,20 +1862,9 @@ fn lazy_removal_does_not_use_all_weight() {
 		let min_balance = Contracts::min_balance();
 		let _ = <Test as Config>::Currency::set_balance(&ALICE, 1000 * min_balance);
 
-		let addr = Contracts::bare_instantiate(
-			ALICE,
-			min_balance * 100,
-			GAS_LIMIT,
-			None,
-			Code::Upload(code),
-			vec![],
-			vec![],
-			DebugInfo::Skip,
-			CollectEvents::Skip,
-		)
-		.result
-		.unwrap()
-		.account_id;
+		let addr = builder::bare_instantiate(Code::Upload(code))
+			.value(min_balance * 100)
+			.build_and_unwrap_account_id();
 
 		let info = get_contract(&addr);
 		let (weight_per_key, max_keys) = ContractInfo::<Test>::deletion_budget(weight_limit);
@@ -2559,14 +1881,7 @@ fn lazy_removal_does_not_use_all_weight() {
 		<ContractInfoOf<Test>>::insert(&addr, info.clone());
 
 		// Terminate the contract
-		assert_ok!(Contracts::call(
-			RuntimeOrigin::signed(ALICE),
-			addr.clone(),
-			0,
-			GAS_LIMIT,
-			None,
-			vec![]
-		));
+		assert_ok!(builder::call(addr.clone()).build());
 
 		// Contract info should be gone
 		assert!(!<ContractInfoOf::<Test>>::contains_key(&addr));
@@ -2620,20 +1935,10 @@ fn deletion_queue_ring_buffer_overflow() {
 
 		// add 3 contracts to the deletion queue
 		for i in 0..3u8 {
-			let addr = Contracts::bare_instantiate(
-				ALICE,
-				min_balance * 100,
-				GAS_LIMIT,
-				None,
-				Code::Upload(code.clone()),
-				vec![],
-				vec![i],
-				DebugInfo::Skip,
-				CollectEvents::Skip,
-			)
-			.result
-			.unwrap()
-			.account_id;
+			let addr = builder::bare_instantiate(Code::Upload(code.clone()))
+				.value(min_balance * 100)
+				.salt(vec![i])
+				.build_and_unwrap_account_id();
 
 			let info = get_contract(&addr);
 			let trie = &info.child_trie_info();
@@ -2643,14 +1948,7 @@ fn deletion_queue_ring_buffer_overflow() {
 
 			// Terminate the contract. Contract info should be gone, but value should be still
 			// there as the lazy removal did not run, yet.
-			assert_ok!(Contracts::call(
-				RuntimeOrigin::signed(ALICE),
-				addr.clone(),
-				0,
-				GAS_LIMIT,
-				None,
-				vec![]
-			));
+			assert_ok!(builder::call(addr.clone()).build());
 
 			assert!(!<ContractInfoOf::<Test>>::contains_key(&addr));
 			assert_matches!(child::get(trie, &[99]), Some(42));
@@ -2678,87 +1976,36 @@ fn refcounter() {
 		let min_balance = Contracts::min_balance();
 
 		// Create two contracts with the same code and check that they do in fact share it.
-		let addr0 = Contracts::bare_instantiate(
-			ALICE,
-			min_balance * 100,
-			GAS_LIMIT,
-			None,
-			Code::Upload(wasm.clone()),
-			vec![],
-			vec![0],
-			DebugInfo::Skip,
-			CollectEvents::Skip,
-		)
-		.result
-		.unwrap()
-		.account_id;
-		let addr1 = Contracts::bare_instantiate(
-			ALICE,
-			min_balance * 100,
-			GAS_LIMIT,
-			None,
-			Code::Upload(wasm.clone()),
-			vec![],
-			vec![1],
-			DebugInfo::Skip,
-			CollectEvents::Skip,
-		)
-		.result
-		.unwrap()
-		.account_id;
+		let addr0 = builder::bare_instantiate(Code::Upload(wasm.clone()))
+			.value(min_balance * 100)
+			.salt(vec![0])
+			.build_and_unwrap_account_id();
+		let addr1 = builder::bare_instantiate(Code::Upload(wasm.clone()))
+			.value(min_balance * 100)
+			.salt(vec![1])
+			.build_and_unwrap_account_id();
 		assert_refcount!(code_hash, 2);
 
 		// Sharing should also work with the usual instantiate call
-		let addr2 = Contracts::bare_instantiate(
-			ALICE,
-			min_balance * 100,
-			GAS_LIMIT,
-			None,
-			Code::Existing(code_hash),
-			vec![],
-			vec![2],
-			DebugInfo::Skip,
-			CollectEvents::Skip,
-		)
-		.result
-		.unwrap()
-		.account_id;
+		let addr2 = builder::bare_instantiate(Code::Existing(code_hash))
+			.value(min_balance * 100)
+			.salt(vec![2])
+			.build_and_unwrap_account_id();
 		assert_refcount!(code_hash, 3);
-
-		// Terminating one contract should decrement the refcount
-		assert_ok!(Contracts::call(
-			RuntimeOrigin::signed(ALICE),
-			addr0,
-			0,
-			GAS_LIMIT,
-			None,
-			vec![]
-		));
+
+		// Terminating one contract should decrement the refcount
+		assert_ok!(builder::call(addr0).build());
 		assert_refcount!(code_hash, 2);
 
 		// remove another one
-		assert_ok!(Contracts::call(
-			RuntimeOrigin::signed(ALICE),
-			addr1,
-			0,
-			GAS_LIMIT,
-			None,
-			vec![]
-		));
+		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!(Contracts::call(
-			RuntimeOrigin::signed(ALICE),
-			addr2,
-			0,
-			GAS_LIMIT,
-			None,
-			vec![]
-		));
+		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
@@ -2772,31 +2019,10 @@ fn debug_message_works() {
 
 	ExtBuilder::default().existential_deposit(50).build().execute_with(|| {
 		let _ = <Test as Config>::Currency::set_balance(&ALICE, 1_000_000);
-		let addr = Contracts::bare_instantiate(
-			ALICE,
-			30_000,
-			GAS_LIMIT,
-			None,
-			Code::Upload(wasm),
-			vec![],
-			vec![],
-			DebugInfo::Skip,
-			CollectEvents::Skip,
-		)
-		.result
-		.unwrap()
-		.account_id;
-		let result = Contracts::bare_call(
-			ALICE,
-			addr,
-			0,
-			GAS_LIMIT,
-			None,
-			vec![],
-			DebugInfo::UnsafeDebug,
-			CollectEvents::Skip,
-			Determinism::Enforced,
-		);
+		let addr = builder::bare_instantiate(Code::Upload(wasm))
+			.value(30_000)
+			.build_and_unwrap_account_id();
+		let result = builder::bare_call(addr).debug(DebugInfo::UnsafeDebug).build();
 
 		assert_matches!(result.result, Ok(_));
 		assert_eq!(std::str::from_utf8(&result.debug_message).unwrap(), "Hello World!");
@@ -2809,32 +2035,11 @@ fn debug_message_logging_disabled() {
 
 	ExtBuilder::default().existential_deposit(50).build().execute_with(|| {
 		let _ = <Test as Config>::Currency::set_balance(&ALICE, 1_000_000);
-		let addr = Contracts::bare_instantiate(
-			ALICE,
-			30_000,
-			GAS_LIMIT,
-			None,
-			Code::Upload(wasm),
-			vec![],
-			vec![],
-			DebugInfo::Skip,
-			CollectEvents::Skip,
-		)
-		.result
-		.unwrap()
-		.account_id;
+		let addr = builder::bare_instantiate(Code::Upload(wasm))
+			.value(30_000)
+			.build_and_unwrap_account_id();
 		// disable logging by passing `false`
-		let result = Contracts::bare_call(
-			ALICE,
-			addr.clone(),
-			0,
-			GAS_LIMIT,
-			None,
-			vec![],
-			DebugInfo::Skip,
-			CollectEvents::Skip,
-			Determinism::Enforced,
-		);
+		let result = builder::bare_call(addr.clone()).build();
 		assert_matches!(result.result, Ok(_));
 		// the dispatchables always run without debugging
 		assert_ok!(Contracts::call(RuntimeOrigin::signed(ALICE), addr, 0, GAS_LIMIT, None, vec![]));
@@ -2848,31 +2053,10 @@ fn debug_message_invalid_utf8() {
 
 	ExtBuilder::default().existential_deposit(50).build().execute_with(|| {
 		let _ = <Test as Config>::Currency::set_balance(&ALICE, 1_000_000);
-		let addr = Contracts::bare_instantiate(
-			ALICE,
-			30_000,
-			GAS_LIMIT,
-			None,
-			Code::Upload(wasm),
-			vec![],
-			vec![],
-			DebugInfo::Skip,
-			CollectEvents::Skip,
-		)
-		.result
-		.unwrap()
-		.account_id;
-		let result = Contracts::bare_call(
-			ALICE,
-			addr,
-			0,
-			GAS_LIMIT,
-			None,
-			vec![],
-			DebugInfo::UnsafeDebug,
-			CollectEvents::Skip,
-			Determinism::Enforced,
-		);
+		let addr = builder::bare_instantiate(Code::Upload(wasm))
+			.value(30_000)
+			.build_and_unwrap_account_id();
+		let result = builder::bare_call(addr).debug(DebugInfo::UnsafeDebug).build();
 		assert_ok!(result.result);
 		assert!(result.debug_message.is_empty());
 	});
@@ -2887,50 +2071,17 @@ fn gas_estimation_for_subcalls() {
 		let min_balance = Contracts::min_balance();
 		let _ = <Test as Config>::Currency::set_balance(&ALICE, 2_000 * min_balance);
 
-		let addr_caller = Contracts::bare_instantiate(
-			ALICE,
-			min_balance * 100,
-			GAS_LIMIT,
-			None,
-			Code::Upload(caller_code),
-			vec![],
-			vec![],
-			DebugInfo::Skip,
-			CollectEvents::Skip,
-		)
-		.result
-		.unwrap()
-		.account_id;
+		let addr_caller = builder::bare_instantiate(Code::Upload(caller_code))
+			.value(min_balance * 100)
+			.build_and_unwrap_account_id();
 
-		let addr_dummy = Contracts::bare_instantiate(
-			ALICE,
-			min_balance * 100,
-			GAS_LIMIT,
-			None,
-			Code::Upload(dummy_code),
-			vec![],
-			vec![],
-			DebugInfo::Skip,
-			CollectEvents::Skip,
-		)
-		.result
-		.unwrap()
-		.account_id;
+		let addr_dummy = builder::bare_instantiate(Code::Upload(dummy_code))
+			.value(min_balance * 100)
+			.build_and_unwrap_account_id();
 
-		let addr_call_runtime = Contracts::bare_instantiate(
-			ALICE,
-			min_balance * 100,
-			GAS_LIMIT,
-			None,
-			Code::Upload(call_runtime_code),
-			vec![],
-			vec![],
-			DebugInfo::Skip,
-			CollectEvents::Skip,
-		)
-		.result
-		.unwrap()
-		.account_id;
+		let addr_call_runtime = builder::bare_instantiate(Code::Upload(call_runtime_code))
+			.value(min_balance * 100)
+			.build_and_unwrap_account_id();
 
 		// Run the test for all of those weight limits for the subcall
 		let weights = [
@@ -2966,17 +2117,7 @@ fn gas_estimation_for_subcalls() {
 					.collect();
 
 				// Call in order to determine the gas that is required for this call
-				let result = Contracts::bare_call(
-					ALICE,
-					addr_caller.clone(),
-					0,
-					GAS_LIMIT,
-					None,
-					input.clone(),
-					DebugInfo::Skip,
-					CollectEvents::Skip,
-					Determinism::Enforced,
-				);
+				let result = builder::bare_call(addr_caller.clone()).data(input.clone()).build();
 				assert_ok!(&result.result);
 
 				// If the out of gas happens in the subcall the caller contract
@@ -2992,51 +2133,33 @@ fn gas_estimation_for_subcalls() {
 
 				// Make the same call using the estimated gas. Should succeed.
 				assert_ok!(
-					Contracts::bare_call(
-						ALICE,
-						addr_caller.clone(),
-						0,
-						result.gas_required,
-						Some(result.storage_deposit.charge_or_zero()),
-						input.clone(),
-						DebugInfo::Skip,
-						CollectEvents::Skip,
-						Determinism::Enforced,
-					)
-					.result
+					builder::bare_call(addr_caller.clone())
+						.gas_limit(result.gas_required)
+						.storage_deposit_limit(Some(result.storage_deposit.charge_or_zero()))
+						.data(input.clone())
+						.build()
+						.result
 				);
 
 				// Check that it fails with too little ref_time
 				assert_err!(
-					Contracts::bare_call(
-						ALICE,
-						addr_caller.clone(),
-						0,
-						result.gas_required.sub_ref_time(1),
-						Some(result.storage_deposit.charge_or_zero()),
-						input.clone(),
-						DebugInfo::Skip,
-						CollectEvents::Skip,
-						Determinism::Enforced,
-					)
-					.result,
+					builder::bare_call(addr_caller.clone())
+						.gas_limit(result.gas_required.sub_ref_time(1))
+						.storage_deposit_limit(Some(result.storage_deposit.charge_or_zero()))
+						.data(input.clone())
+						.build()
+						.result,
 					error,
 				);
 
 				// Check that it fails with too little proof_size
 				assert_err!(
-					Contracts::bare_call(
-						ALICE,
-						addr_caller.clone(),
-						0,
-						result.gas_required.sub_proof_size(1),
-						Some(result.storage_deposit.charge_or_zero()),
-						input,
-						DebugInfo::Skip,
-						CollectEvents::Skip,
-						Determinism::Enforced,
-					)
-					.result,
+					builder::bare_call(addr_caller.clone())
+						.gas_limit(result.gas_required.sub_proof_size(1))
+						.storage_deposit_limit(Some(result.storage_deposit.charge_or_zero()))
+						.data(input)
+						.build()
+						.result,
 					error,
 				);
 			}
@@ -3052,20 +2175,10 @@ fn gas_estimation_call_runtime() {
 		let _ = <Test as Config>::Currency::set_balance(&ALICE, 1000 * min_balance);
 		let _ = <Test as Config>::Currency::set_balance(&CHARLIE, 1000 * min_balance);
 
-		let addr_caller = Contracts::bare_instantiate(
-			ALICE,
-			min_balance * 100,
-			GAS_LIMIT,
-			None,
-			Code::Upload(caller_code),
-			vec![],
-			vec![0],
-			DebugInfo::Skip,
-			CollectEvents::Skip,
-		)
-		.result
-		.unwrap()
-		.account_id;
+		let addr_caller = builder::bare_instantiate(Code::Upload(caller_code))
+			.value(min_balance * 100)
+			.salt(vec![0])
+			.build_and_unwrap_account_id();
 
 		// 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.
@@ -3073,17 +2186,7 @@ fn gas_estimation_call_runtime() {
 			pre_charge: Weight::from_parts(10_000_000, 1_000),
 			actual_weight: Weight::from_parts(100, 100),
 		});
-		let result = Contracts::bare_call(
-			ALICE,
-			addr_caller.clone(),
-			0,
-			GAS_LIMIT,
-			None,
-			call.encode(),
-			DebugInfo::Skip,
-			CollectEvents::Skip,
-			Determinism::Enforced,
-		);
+		let result = builder::bare_call(addr_caller.clone()).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);
@@ -3091,18 +2194,11 @@ fn gas_estimation_call_runtime() {
 
 		// Make the same call using the required gas. Should succeed.
 		assert_ok!(
-			Contracts::bare_call(
-				ALICE,
-				addr_caller,
-				0,
-				result.gas_required,
-				None,
-				call.encode(),
-				DebugInfo::Skip,
-				CollectEvents::Skip,
-				Determinism::Enforced,
-			)
-			.result
+			builder::bare_call(addr_caller)
+				.gas_limit(result.gas_required)
+				.data(call.encode())
+				.build()
+				.result
 		);
 	});
 }
@@ -3116,35 +2212,15 @@ fn call_runtime_reentrancy_guarded() {
 		let _ = <Test as Config>::Currency::set_balance(&ALICE, 1000 * min_balance);
 		let _ = <Test as Config>::Currency::set_balance(&CHARLIE, 1000 * min_balance);
 
-		let addr_caller = Contracts::bare_instantiate(
-			ALICE,
-			min_balance * 100,
-			GAS_LIMIT,
-			None,
-			Code::Upload(caller_code),
-			vec![],
-			vec![0],
-			DebugInfo::Skip,
-			CollectEvents::Skip,
-		)
-		.result
-		.unwrap()
-		.account_id;
+		let addr_caller = builder::bare_instantiate(Code::Upload(caller_code))
+			.value(min_balance * 100)
+			.salt(vec![0])
+			.build_and_unwrap_account_id();
 
-		let addr_callee = Contracts::bare_instantiate(
-			ALICE,
-			min_balance * 100,
-			GAS_LIMIT,
-			None,
-			Code::Upload(callee_code),
-			vec![],
-			vec![1],
-			DebugInfo::Skip,
-			CollectEvents::Skip,
-		)
-		.result
-		.unwrap()
-		.account_id;
+		let addr_callee = builder::bare_instantiate(Code::Upload(callee_code))
+			.value(min_balance * 100)
+			.salt(vec![1])
+			.build_and_unwrap_account_id();
 
 		// Call pallet_contracts call() dispatchable
 		let call = RuntimeCall::Contracts(crate::Call::call {
@@ -3157,19 +2233,9 @@ fn call_runtime_reentrancy_guarded() {
 
 		// Call runtime to re-enter back to contracts engine by
 		// calling dummy contract
-		let result = Contracts::bare_call(
-			ALICE,
-			addr_caller.clone(),
-			0,
-			GAS_LIMIT,
-			None,
-			call.encode(),
-			DebugInfo::Skip,
-			CollectEvents::Skip,
-			Determinism::Enforced,
-		)
-		.result
-		.unwrap();
+		let result = builder::bare_call(addr_caller.clone())
+			.data(call.encode())
+			.build_and_unwrap_result();
 		// Call to runtime should fail because of the re-entrancy guard
 		assert_return_code!(result, RuntimeReturnCode::CallRuntimeFailed);
 	});
@@ -3183,20 +2249,9 @@ fn ecdsa_recover() {
 		let _ = <Test as Config>::Currency::set_balance(&ALICE, 1_000_000);
 
 		// Instantiate the ecdsa_recover contract.
-		let addr = Contracts::bare_instantiate(
-			ALICE,
-			100_000,
-			GAS_LIMIT,
-			None,
-			Code::Upload(wasm),
-			vec![],
-			vec![],
-			DebugInfo::Skip,
-			CollectEvents::Skip,
-		)
-		.result
-		.unwrap()
-		.account_id;
+		let addr = builder::bare_instantiate(Code::Upload(wasm))
+			.value(100_000)
+			.build_and_unwrap_account_id();
 
 		#[rustfmt::skip]
 		let signature: [u8; 65] = [
@@ -3246,17 +2301,10 @@ fn bare_instantiate_returns_events() {
 		let min_balance = Contracts::min_balance();
 		let _ = <Test as Config>::Currency::set_balance(&ALICE, 1000 * min_balance);
 
-		let result = Contracts::bare_instantiate(
-			ALICE,
-			min_balance * 100,
-			GAS_LIMIT,
-			None,
-			Code::Upload(wasm),
-			vec![],
-			vec![],
-			DebugInfo::Skip,
-			CollectEvents::UnsafeCollect,
-		);
+		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());
@@ -3271,17 +2319,7 @@ fn bare_instantiate_does_not_return_events() {
 		let min_balance = Contracts::min_balance();
 		let _ = <Test as Config>::Currency::set_balance(&ALICE, 1000 * min_balance);
 
-		let result = Contracts::bare_instantiate(
-			ALICE,
-			min_balance * 100,
-			GAS_LIMIT,
-			None,
-			Code::Upload(wasm),
-			vec![],
-			vec![],
-			DebugInfo::Skip,
-			CollectEvents::Skip,
-		);
+		let result = builder::bare_instantiate(Code::Upload(wasm)).value(min_balance * 100).build();
 
 		let events = result.events;
 		assert!(!System::events().is_empty());
@@ -3296,32 +2334,13 @@ fn bare_call_returns_events() {
 		let min_balance = Contracts::min_balance();
 		let _ = <Test as Config>::Currency::set_balance(&ALICE, 1000 * min_balance);
 
-		let addr = Contracts::bare_instantiate(
-			ALICE,
-			min_balance * 100,
-			GAS_LIMIT,
-			None,
-			Code::Upload(wasm),
-			vec![],
-			vec![],
-			DebugInfo::Skip,
-			CollectEvents::Skip,
-		)
-		.result
-		.unwrap()
-		.account_id;
+		let addr = builder::bare_instantiate(Code::Upload(wasm))
+			.value(min_balance * 100)
+			.build_and_unwrap_account_id();
 
-		let result = Contracts::bare_call(
-			ALICE,
-			addr.clone(),
-			0,
-			GAS_LIMIT,
-			None,
-			vec![],
-			DebugInfo::Skip,
-			CollectEvents::UnsafeCollect,
-			Determinism::Enforced,
-		);
+		let result = builder::bare_call(addr.clone())
+			.collect_events(CollectEvents::UnsafeCollect)
+			.build();
 
 		let events = result.events.unwrap();
 		assert_return_code!(&result.result.unwrap(), RuntimeReturnCode::Success);
@@ -3337,32 +2356,11 @@ fn bare_call_does_not_return_events() {
 		let min_balance = Contracts::min_balance();
 		let _ = <Test as Config>::Currency::set_balance(&ALICE, 1000 * min_balance);
 
-		let addr = Contracts::bare_instantiate(
-			ALICE,
-			min_balance * 100,
-			GAS_LIMIT,
-			None,
-			Code::Upload(wasm),
-			vec![],
-			vec![],
-			DebugInfo::Skip,
-			CollectEvents::Skip,
-		)
-		.result
-		.unwrap()
-		.account_id;
+		let addr = builder::bare_instantiate(Code::Upload(wasm))
+			.value(min_balance * 100)
+			.build_and_unwrap_account_id();
 
-		let result = Contracts::bare_call(
-			ALICE,
-			addr.clone(),
-			0,
-			GAS_LIMIT,
-			None,
-			vec![],
-			DebugInfo::Skip,
-			CollectEvents::Skip,
-			Determinism::Enforced,
-		);
+		let result = builder::bare_call(addr.clone()).build();
 
 		let events = result.events;
 		assert_return_code!(&result.result.unwrap(), RuntimeReturnCode::Success);
@@ -3379,20 +2377,9 @@ fn sr25519_verify() {
 		let _ = <Test as Config>::Currency::set_balance(&ALICE, 1_000_000);
 
 		// Instantiate the sr25519_verify contract.
-		let addr = Contracts::bare_instantiate(
-			ALICE,
-			100_000,
-			GAS_LIMIT,
-			None,
-			Code::Upload(wasm),
-			vec![],
-			vec![],
-			DebugInfo::Skip,
-			CollectEvents::Skip,
-		)
-		.result
-		.unwrap()
-		.account_id;
+		let addr = builder::bare_instantiate(Code::Upload(wasm))
+			.value(100_000)
+			.build_and_unwrap_account_id();
 
 		let call_with = |message: &[u8; 11]| {
 			// Alice's signature for "hello world"
@@ -3450,34 +2437,10 @@ fn failed_deposit_charge_should_roll_back_call() {
 			let _ = <Test as Config>::Currency::set_balance(&ALICE, 1_000_000);
 
 			// Instantiate both contracts.
-			let addr_caller = Contracts::bare_instantiate(
-				ALICE,
-				0,
-				GAS_LIMIT,
-				None,
-				Code::Upload(wasm_caller.clone()),
-				vec![],
-				vec![],
-				DebugInfo::Skip,
-				CollectEvents::Skip,
-			)
-			.result
-			.unwrap()
-			.account_id;
-			let addr_callee = Contracts::bare_instantiate(
-				ALICE,
-				0,
-				GAS_LIMIT,
-				None,
-				Code::Upload(wasm_callee.clone()),
-				vec![],
-				vec![],
-				DebugInfo::Skip,
-				CollectEvents::Skip,
-			)
-			.result
-			.unwrap()
-			.account_id;
+			let addr_caller = builder::bare_instantiate(Code::Upload(wasm_caller.clone()))
+				.build_and_unwrap_account_id();
+			let addr_callee = builder::bare_instantiate(Code::Upload(wasm_callee.clone()))
+				.build_and_unwrap_account_id();
 
 			// Give caller proxy access to Alice.
 			assert_ok!(Proxy::add_proxy(RuntimeOrigin::signed(ALICE), addr_caller.clone(), (), 0));
@@ -3701,15 +2664,7 @@ fn remove_code_in_use() {
 	ExtBuilder::default().existential_deposit(100).build().execute_with(|| {
 		let _ = <Test as Config>::Currency::set_balance(&ALICE, 1_000_000);
 
-		assert_ok!(Contracts::instantiate_with_code(
-			RuntimeOrigin::signed(ALICE),
-			0,
-			GAS_LIMIT,
-			None,
-			wasm,
-			vec![],
-			vec![],
-		));
+		assert_ok!(builder::instantiate_with_code(wasm).build());
 
 		// Drop previous events
 		initialize_block(2);
@@ -3753,20 +2708,7 @@ fn instantiate_with_zero_balance_works() {
 		initialize_block(2);
 
 		// Instantiate the BOB contract.
-		let addr = Contracts::bare_instantiate(
-			ALICE,
-			0,
-			GAS_LIMIT,
-			None,
-			Code::Upload(wasm),
-			vec![],
-			vec![],
-			DebugInfo::Skip,
-			CollectEvents::Skip,
-		)
-		.result
-		.unwrap()
-		.account_id;
+		let addr = builder::bare_instantiate(Code::Upload(wasm)).build_and_unwrap_account_id();
 
 		// Ensure the contract was stored and get expected deposit amount to be reserved.
 		let deposit_expected = expected_deposit(ensure_stored(code_hash));
@@ -3850,20 +2792,9 @@ fn instantiate_with_below_existential_deposit_works() {
 		initialize_block(2);
 
 		// Instantiate the BOB contract.
-		let addr = Contracts::bare_instantiate(
-			ALICE,
-			value,
-			GAS_LIMIT,
-			None,
-			Code::Upload(wasm),
-			vec![],
-			vec![],
-			DebugInfo::Skip,
-			CollectEvents::Skip,
-		)
-		.result
-		.unwrap()
-		.account_id;
+		let addr = builder::bare_instantiate(Code::Upload(wasm))
+			.value(value)
+			.build_and_unwrap_account_id();
 
 		// Ensure the contract was stored and get expected deposit amount to be reserved.
 		let deposit_expected = expected_deposit(ensure_stored(code_hash));
@@ -3949,20 +2880,7 @@ fn storage_deposit_works() {
 	ExtBuilder::default().existential_deposit(200).build().execute_with(|| {
 		let _ = <Test as Config>::Currency::set_balance(&ALICE, 1_000_000);
 
-		let addr = Contracts::bare_instantiate(
-			ALICE,
-			0,
-			GAS_LIMIT,
-			None,
-			Code::Upload(wasm),
-			vec![],
-			vec![],
-			DebugInfo::Skip,
-			CollectEvents::Skip,
-		)
-		.result
-		.unwrap()
-		.account_id;
+		let addr = builder::bare_instantiate(Code::Upload(wasm)).build_and_unwrap_account_id();
 
 		let mut deposit = test_utils::contract_info_storage_deposit(&addr);
 
@@ -3970,41 +2888,23 @@ fn storage_deposit_works() {
 		initialize_block(2);
 
 		// Create storage
-		assert_ok!(Contracts::call(
-			RuntimeOrigin::signed(ALICE),
-			addr.clone(),
-			42,
-			GAS_LIMIT,
-			None,
-			(1_000u32, 5_000u32).encode(),
-		));
+		assert_ok!(builder::call(addr.clone())
+			.value(42)
+			.data((1_000u32, 5_000u32).encode())
+			.build());
 		// 4 is for creating 2 storage items
 		let charged0 = 4 + 1_000 + 5_000;
 		deposit += charged0;
 		assert_eq!(get_contract(&addr).total_deposit(), deposit);
-
-		// Add more storage (but also remove some)
-		assert_ok!(Contracts::call(
-			RuntimeOrigin::signed(ALICE),
-			addr.clone(),
-			0,
-			GAS_LIMIT,
-			None,
-			(2_000u32, 4_900u32).encode(),
-		));
+
+		// Add more storage (but also remove some)
+		assert_ok!(builder::call(addr.clone()).data((2_000u32, 4_900u32).encode()).build());
 		let charged1 = 1_000 - 100;
 		deposit += charged1;
 		assert_eq!(get_contract(&addr).total_deposit(), deposit);
 
 		// Remove more storage (but also add some)
-		assert_ok!(Contracts::call(
-			RuntimeOrigin::signed(ALICE),
-			addr.clone(),
-			0,
-			GAS_LIMIT,
-			None,
-			(2_100u32, 900u32).encode(),
-		));
+		assert_ok!(builder::call(addr.clone()).data((2_100u32, 900u32).encode()).build());
 		// -1 for numeric instability
 		let refunded0 = 4_000 - 100 - 1;
 		deposit -= refunded0;
@@ -4093,43 +2993,12 @@ fn storage_deposit_callee_works() {
 		let min_balance = Contracts::min_balance();
 
 		// Create both contracts: Constructors do nothing.
-		let addr_caller = Contracts::bare_instantiate(
-			ALICE,
-			0,
-			GAS_LIMIT,
-			None,
-			Code::Upload(wasm_caller),
-			vec![],
-			vec![],
-			DebugInfo::Skip,
-			CollectEvents::Skip,
-		)
-		.result
-		.unwrap()
-		.account_id;
-		let addr_callee = Contracts::bare_instantiate(
-			ALICE,
-			0,
-			GAS_LIMIT,
-			None,
-			Code::Upload(wasm_callee),
-			vec![],
-			vec![],
-			DebugInfo::Skip,
-			CollectEvents::Skip,
-		)
-		.result
-		.unwrap()
-		.account_id;
+		let addr_caller =
+			builder::bare_instantiate(Code::Upload(wasm_caller)).build_and_unwrap_account_id();
+		let addr_callee =
+			builder::bare_instantiate(Code::Upload(wasm_callee)).build_and_unwrap_account_id();
 
-		assert_ok!(Contracts::call(
-			RuntimeOrigin::signed(ALICE),
-			addr_caller,
-			0,
-			GAS_LIMIT,
-			None,
-			(100u32, &addr_callee).encode()
-		));
+		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;
@@ -4152,20 +3021,7 @@ fn set_code_extrinsic() {
 	ExtBuilder::default().existential_deposit(100).build().execute_with(|| {
 		let _ = <Test as Config>::Currency::set_balance(&ALICE, 1_000_000);
 
-		let addr = Contracts::bare_instantiate(
-			ALICE,
-			0,
-			GAS_LIMIT,
-			None,
-			Code::Upload(wasm),
-			vec![],
-			vec![],
-			DebugInfo::Skip,
-			CollectEvents::Skip,
-		)
-		.result
-		.unwrap()
-		.account_id;
+		let addr = builder::bare_instantiate(Code::Upload(wasm)).build_and_unwrap_account_id();
 
 		assert_ok!(Contracts::upload_code(
 			RuntimeOrigin::signed(ALICE),
@@ -4189,7 +3045,7 @@ fn set_code_extrinsic() {
 		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![],);
+		assert_eq!(System::events(), vec![]);
 
 		// contract must exist
 		assert_noop!(
@@ -4199,7 +3055,7 @@ fn set_code_extrinsic() {
 		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![],);
+		assert_eq!(System::events(), vec![]);
 
 		// new code hash must exist
 		assert_noop!(
@@ -4209,7 +3065,7 @@ fn set_code_extrinsic() {
 		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![],);
+		assert_eq!(System::events(), vec![]);
 
 		// successful call
 		assert_ok!(Contracts::set_code(RuntimeOrigin::root(), addr.clone(), new_code_hash));
@@ -4239,20 +3095,9 @@ fn slash_cannot_kill_account() {
 		let _ = <Test as Config>::Currency::set_balance(&ALICE, 1_000_000);
 		let min_balance = Contracts::min_balance();
 
-		let addr = Contracts::bare_instantiate(
-			ALICE,
-			value,
-			GAS_LIMIT,
-			None,
-			Code::Upload(wasm),
-			vec![],
-			vec![],
-			DebugInfo::Skip,
-			CollectEvents::Skip,
-		)
-		.result
-		.unwrap()
-		.account_id;
+		let addr = builder::bare_instantiate(Code::Upload(wasm))
+			.value(value)
+			.build_and_unwrap_account_id();
 
 		// Drop previous events
 		initialize_block(2);
@@ -4303,29 +3148,13 @@ fn contract_reverted() {
 
 		// Calling extrinsic: revert leads to an error
 		assert_err_ignore_postinfo!(
-			Contracts::instantiate(
-				RuntimeOrigin::signed(ALICE),
-				0,
-				GAS_LIMIT,
-				None,
-				code_hash,
-				input.clone(),
-				vec![],
-			),
+			builder::instantiate(code_hash).data(input.clone()).build(),
 			<Error<Test>>::ContractReverted,
 		);
 
 		// Calling extrinsic: revert leads to an error
 		assert_err_ignore_postinfo!(
-			Contracts::instantiate_with_code(
-				RuntimeOrigin::signed(ALICE),
-				0,
-				GAS_LIMIT,
-				None,
-				wasm,
-				input.clone(),
-				vec![],
-			),
+			builder::instantiate_with_code(wasm).data(input.clone()).build(),
 			<Error<Test>>::ContractReverted,
 		);
 
@@ -4333,66 +3162,26 @@ fn contract_reverted() {
 		// 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 = Contracts::bare_instantiate(
-			ALICE,
-			0,
-			GAS_LIMIT,
-			None,
-			Code::Existing(code_hash),
-			input.clone(),
-			vec![],
-			DebugInfo::Skip,
-			CollectEvents::Skip,
-		)
-		.result
-		.unwrap();
+		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.account_id));
 
 		// Pass empty flags and therefore successfully instantiate the contract for later use.
-		let addr = Contracts::bare_instantiate(
-			ALICE,
-			0,
-			GAS_LIMIT,
-			None,
-			Code::Existing(code_hash),
-			ReturnFlags::empty().bits().encode(),
-			vec![],
-			DebugInfo::Skip,
-			CollectEvents::Skip,
-		)
-		.result
-		.unwrap()
-		.account_id;
+		let addr = builder::bare_instantiate(Code::Existing(code_hash))
+			.data(ReturnFlags::empty().bits().encode())
+			.build_and_unwrap_account_id();
 
 		// Calling extrinsic: revert leads to an error
 		assert_err_ignore_postinfo!(
-			Contracts::call(
-				RuntimeOrigin::signed(ALICE),
-				addr.clone(),
-				0,
-				GAS_LIMIT,
-				None,
-				input.clone()
-			),
+			builder::call(addr.clone()).data(input.clone()).build(),
 			<Error<Test>>::ContractReverted,
 		);
 
 		// Calling directly: revert leads to success but the flags indicate the error
-		let result = Contracts::bare_call(
-			ALICE,
-			addr.clone(),
-			0,
-			GAS_LIMIT,
-			None,
-			input,
-			DebugInfo::Skip,
-			CollectEvents::Skip,
-			Determinism::Enforced,
-		)
-		.result
-		.unwrap();
+		let result = builder::bare_call(addr.clone()).data(input).build_and_unwrap_result();
 		assert_eq!(result.flags, flags);
 		assert_eq!(result.data, buffer);
 	});
@@ -4407,20 +3196,9 @@ fn set_code_hash() {
 		let _ = <Test as Config>::Currency::set_balance(&ALICE, 1_000_000);
 
 		// Instantiate the 'caller'
-		let contract_addr = Contracts::bare_instantiate(
-			ALICE,
-			300_000,
-			GAS_LIMIT,
-			None,
-			Code::Upload(wasm),
-			vec![],
-			vec![],
-			DebugInfo::Skip,
-			CollectEvents::Skip,
-		)
-		.result
-		.unwrap()
-		.account_id;
+		let contract_addr = builder::bare_instantiate(Code::Upload(wasm))
+			.value(300_000)
+			.build_and_unwrap_account_id();
 		// upload new code
 		assert_ok!(Contracts::upload_code(
 			RuntimeOrigin::signed(ALICE),
@@ -4432,35 +3210,16 @@ fn set_code_hash() {
 		System::reset_events();
 
 		// First call sets new code_hash and returns 1
-		let result = Contracts::bare_call(
-			ALICE,
-			contract_addr.clone(),
-			0,
-			GAS_LIMIT,
-			None,
-			new_code_hash.as_ref().to_vec(),
-			DebugInfo::UnsafeDebug,
-			CollectEvents::Skip,
-			Determinism::Enforced,
-		)
-		.result
-		.unwrap();
+		let result = builder::bare_call(contract_addr.clone())
+			.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 = Contracts::bare_call(
-			ALICE,
-			contract_addr.clone(),
-			0,
-			GAS_LIMIT,
-			None,
-			vec![],
-			DebugInfo::UnsafeDebug,
-			CollectEvents::Skip,
-			Determinism::Enforced,
-		)
-		.result
-		.unwrap();
+		let result = builder::bare_call(contract_addr.clone())
+			.debug(DebugInfo::UnsafeDebug)
+			.build_and_unwrap_result();
 		assert_return_code!(result, 2);
 
 		// Checking for the last event only
@@ -4512,37 +3271,16 @@ fn storage_deposit_limit_is_enforced() {
 
 		// Setting insufficient storage_deposit should fail.
 		assert_err!(
-			Contracts::bare_instantiate(
-				ALICE,
-				0,
-				GAS_LIMIT,
-				Some((2 * min_balance + 3 - 1).into()), /* expected deposit is 2 * ed + 3 for
-				                                         * the call */
-				Code::Upload(wasm.clone()),
-				vec![],
-				vec![],
-				DebugInfo::Skip,
-				CollectEvents::Skip,
-			)
-			.result,
+			builder::bare_instantiate(Code::Upload(wasm.clone()))
+				// expected deposit is 2 * ed + 3 for the call
+				.storage_deposit_limit(Some((2 * min_balance + 3 - 1).into()))
+				.build()
+				.result,
 			<Error<Test>>::StorageDepositLimitExhausted,
 		);
 
 		// Instantiate the BOB contract.
-		let addr = Contracts::bare_instantiate(
-			ALICE,
-			0,
-			GAS_LIMIT,
-			None,
-			Code::Upload(wasm),
-			vec![],
-			vec![],
-			DebugInfo::Skip,
-			CollectEvents::Skip,
-		)
-		.result
-		.unwrap()
-		.account_id;
+		let addr = builder::bare_instantiate(Code::Upload(wasm)).build_and_unwrap_account_id();
 
 		let info_deposit = test_utils::contract_info_storage_deposit(&addr);
 		// Check that the BOB contract has been instantiated and has the minimum balance
@@ -4553,14 +3291,10 @@ fn storage_deposit_limit_is_enforced() {
 		// setting insufficient deposit limit, as it requires 3 Balance:
 		// 2 for the item added + 1 for the new storage item.
 		assert_err_ignore_postinfo!(
-			Contracts::call(
-				RuntimeOrigin::signed(ALICE),
-				addr.clone(),
-				0,
-				GAS_LIMIT,
-				Some(codec::Compact(2)),
-				1u32.to_le_bytes().to_vec()
-			),
+			builder::call(addr.clone())
+				.storage_deposit_limit(Some(codec::Compact(2)))
+				.data(1u32.to_le_bytes().to_vec())
+				.build(),
 			<Error<Test>>::StorageDepositLimitExhausted,
 		);
 
@@ -4571,26 +3305,12 @@ fn storage_deposit_limit_is_enforced() {
 		// 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!(Contracts::call(
-			RuntimeOrigin::signed(ALICE),
-			addr.clone(),
-			0,
-			GAS_LIMIT,
-			None,
-			1u32.to_le_bytes().to_vec()
-		));
+		assert_ok!(builder::call(addr.clone()).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!(
-			Contracts::call(
-				RuntimeOrigin::signed(ALICE),
-				addr.clone(),
-				0,
-				GAS_LIMIT,
-				None,
-				5u32.to_le_bytes().to_vec()
-			),
+			builder::call(addr.clone()).data(5u32.to_le_bytes().to_vec()).build(),
 			<Error<Test>>::StorageDepositLimitExhausted,
 		);
 	});
@@ -4605,45 +3325,17 @@ fn deposit_limit_in_nested_calls() {
 		let _ = <Test as Config>::Currency::set_balance(&ALICE, 1_000_000);
 
 		// Create both contracts: Constructors do nothing.
-		let addr_caller = Contracts::bare_instantiate(
-			ALICE,
-			0,
-			GAS_LIMIT,
-			None,
-			Code::Upload(wasm_caller),
-			vec![],
-			vec![],
-			DebugInfo::Skip,
-			CollectEvents::Skip,
-		)
-		.result
-		.unwrap()
-		.account_id;
-		let addr_callee = Contracts::bare_instantiate(
-			ALICE,
-			0,
-			GAS_LIMIT,
-			None,
-			Code::Upload(wasm_callee),
-			vec![],
-			vec![],
-			DebugInfo::Skip,
-			CollectEvents::Skip,
-		)
-		.result
-		.unwrap()
-		.account_id;
+		let addr_caller =
+			builder::bare_instantiate(Code::Upload(wasm_caller)).build_and_unwrap_account_id();
+		let addr_callee =
+			builder::bare_instantiate(Code::Upload(wasm_callee)).build_and_unwrap_account_id();
 
 		// Create 100 bytes of storage with a price of per byte
 		// This is 100 Balance + 2 Balance for the item
-		assert_ok!(Contracts::call(
-			RuntimeOrigin::signed(ALICE),
-			addr_callee.clone(),
-			0,
-			GAS_LIMIT,
-			Some(codec::Compact(102)),
-			100u32.to_le_bytes().to_vec()
-		));
+		assert_ok!(builder::call(addr_callee.clone())
+			.storage_deposit_limit(Some(codec::Compact(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.
@@ -4651,14 +3343,10 @@ fn deposit_limit_in_nested_calls() {
 		// This should fail as the specified parent's limit is less than the cost: 13 <
 		// 14.
 		assert_err_ignore_postinfo!(
-			Contracts::call(
-				RuntimeOrigin::signed(ALICE),
-				addr_caller.clone(),
-				0,
-				GAS_LIMIT,
-				Some(codec::Compact(13)),
-				(100u32, &addr_callee, 0u64).encode(),
-			),
+			builder::call(addr_caller.clone())
+				.storage_deposit_limit(Some(codec::Compact(13)))
+				.data((100u32, &addr_callee, 0u64).encode())
+				.build(),
 			<Error<Test>>::StorageDepositLimitExhausted,
 		);
 		// Now we specify the parent's limit high enough to cover the caller's storage additions.
@@ -4668,14 +3356,10 @@ fn deposit_limit_in_nested_calls() {
 		// This should fail as the specified parent's limit is less than the cost: 14
 		// < 15.
 		assert_err_ignore_postinfo!(
-			Contracts::call(
-				RuntimeOrigin::signed(ALICE),
-				addr_caller.clone(),
-				0,
-				GAS_LIMIT,
-				Some(codec::Compact(14)),
-				(101u32, &addr_callee, 0u64).encode(),
-			),
+			builder::call(addr_caller.clone())
+				.storage_deposit_limit(Some(codec::Compact(14)))
+				.data((101u32, &addr_callee, 0u64).encode())
+				.build(),
 			<Error<Test>>::StorageDepositLimitExhausted,
 		);
 
@@ -4685,14 +3369,10 @@ fn deposit_limit_in_nested_calls() {
 		// 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!(
-			Contracts::call(
-				RuntimeOrigin::signed(ALICE),
-				addr_caller.clone(),
-				0,
-				GAS_LIMIT,
-				Some(codec::Compact(16)),
-				(102u32, &addr_callee, 1u64).encode(),
-			),
+			builder::call(addr_caller.clone())
+				.storage_deposit_limit(Some(codec::Compact(16)))
+				.data((102u32, &addr_callee, 1u64).encode())
+				.build(),
 			<Error<Test>>::StorageDepositLimitExhausted,
 		);
 
@@ -4700,14 +3380,10 @@ fn deposit_limit_in_nested_calls() {
 		// 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!(
-			Contracts::call(
-				RuntimeOrigin::signed(ALICE),
-				addr_caller.clone(),
-				0,
-				GAS_LIMIT,
-				Some(codec::Compact(0)),
-				(87u32, &addr_callee, 0u64).encode(),
-			),
+			builder::call(addr_caller.clone())
+				.storage_deposit_limit(Some(codec::Compact(0)))
+				.data((87u32, &addr_callee, 0u64).encode())
+				.build(),
 			<Error<Test>>::StorageDepositLimitExhausted,
 		);
 
@@ -4716,28 +3392,19 @@ fn deposit_limit_in_nested_calls() {
 		// Require more than the sender's balance.
 		// We don't set a special limit for the nested call.
 		assert_err_ignore_postinfo!(
-			Contracts::call(
-				RuntimeOrigin::signed(ALICE),
-				addr_caller.clone(),
-				0,
-				GAS_LIMIT,
-				None,
-				(1200u32, &addr_callee, 1u64).encode(),
-			),
+			builder::call(addr_caller.clone())
+				.data((1200u32, &addr_callee, 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!(Contracts::call(
-			RuntimeOrigin::signed(ALICE),
-			addr_caller.clone(),
-			0,
-			GAS_LIMIT,
-			Some(codec::Compact(1)),
-			(87u32, &addr_callee, 1u64).encode(),
-		));
+		assert_ok!(builder::call(addr_caller.clone())
+			.storage_deposit_limit(Some(codec::Compact(1)))
+			.data((87u32, &addr_callee, 1u64).encode())
+			.build());
 	});
 }
 
@@ -4751,35 +3418,13 @@ fn deposit_limit_in_nested_instantiate() {
 		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 addr_caller = Contracts::bare_instantiate(
-			ALICE,
-			10_000u64, // this balance is later passed to the deployed contract
-			GAS_LIMIT,
-			None,
-			Code::Upload(wasm_caller),
-			vec![],
-			vec![],
-			DebugInfo::Skip,
-			CollectEvents::Skip,
-		)
-		.result
-		.unwrap()
-		.account_id;
+		let addr_caller = builder::bare_instantiate(Code::Upload(wasm_caller))
+			.value(10_000u64) // this balance is later passed to the deployed contract
+			.build_and_unwrap_account_id();
 		// Deploy a contract to get its occupied storage size
-		let addr = Contracts::bare_instantiate(
-			ALICE,
-			0,
-			GAS_LIMIT,
-			None,
-			Code::Upload(wasm_callee),
-			vec![0, 0, 0, 0],
-			vec![],
-			DebugInfo::Skip,
-			CollectEvents::Skip,
-		)
-		.result
-		.unwrap()
-		.account_id;
+		let addr = builder::bare_instantiate(Code::Upload(wasm_callee))
+			.data(vec![0, 0, 0, 0])
+			.build_and_unwrap_account_id();
 
 		let callee_info_len = ContractInfoOf::<Test>::get(&addr).unwrap().encoded_size() as u64;
 
@@ -4795,14 +3440,11 @@ fn deposit_limit_in_nested_instantiate() {
 		// 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!(
-			Contracts::call(
-				RuntimeOrigin::signed(BOB),
-				addr_caller.clone(),
-				0,
-				GAS_LIMIT,
-				Some(codec::Compact(callee_info_len + 2 + ED + 1)),
-				(0u32, &code_hash_callee, 0u64).encode(),
-			),
+			builder::call(addr_caller.clone())
+				.origin(RuntimeOrigin::signed(BOB))
+				.storage_deposit_limit(Some(codec::Compact(callee_info_len + 2 + ED + 1)))
+				.data((0u32, &code_hash_callee, 0u64).encode())
+				.build(),
 			<Error<Test>>::StorageDepositLimitExhausted,
 		);
 		// The charges made on instantiation should be rolled back.
@@ -4812,14 +3454,11 @@ fn deposit_limit_in_nested_instantiate() {
 		// byte in the constructor. Hence +1 Balance to the limit is needed. This should fail on the
 		// return from constructor.
 		assert_err_ignore_postinfo!(
-			Contracts::call(
-				RuntimeOrigin::signed(BOB),
-				addr_caller.clone(),
-				0,
-				GAS_LIMIT,
-				Some(codec::Compact(callee_info_len + 2 + ED + 2)),
-				(1u32, &code_hash_callee, 0u64).encode(),
-			),
+			builder::call(addr_caller.clone())
+				.origin(RuntimeOrigin::signed(BOB))
+				.storage_deposit_limit(Some(codec::Compact(callee_info_len + 2 + ED + 2)))
+				.data((1u32, &code_hash_callee, 0u64).encode())
+				.build(),
 			<Error<Test>>::StorageDepositLimitExhausted,
 		);
 		// The charges made on the instantiation should be rolled back.
@@ -4829,14 +3468,11 @@ fn deposit_limit_in_nested_instantiate() {
 		// This should fail during the charging for the instantiation in
 		// `RawMeter::charge_instantiate()`
 		assert_err_ignore_postinfo!(
-			Contracts::call(
-				RuntimeOrigin::signed(BOB),
-				addr_caller.clone(),
-				0,
-				GAS_LIMIT,
-				Some(codec::Compact(callee_info_len + 2 + ED + 2)),
-				(0u32, &code_hash_callee, callee_info_len + 2 + ED + 1).encode(),
-			),
+			builder::call(addr_caller.clone())
+				.origin(RuntimeOrigin::signed(BOB))
+				.storage_deposit_limit(Some(codec::Compact(callee_info_len + 2 + ED + 2)))
+				.data((0u32, &code_hash_callee, callee_info_len + 2 + ED + 1).encode())
+				.build(),
 			<Error<Test>>::StorageDepositLimitExhausted,
 		);
 		// The charges made on the instantiation should be rolled back.
@@ -4847,31 +3483,22 @@ fn deposit_limit_in_nested_instantiate() {
 		// 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!(
-			Contracts::call(
-				RuntimeOrigin::signed(BOB),
-				addr_caller.clone(),
-				0,
-				GAS_LIMIT,
-				Some(codec::Compact(callee_info_len + 2 + ED + 3)), // enough parent limit
-				(1u32, &code_hash_callee, callee_info_len + 2 + ED + 2).encode(),
-			),
+			builder::call(addr_caller.clone())
+				.origin(RuntimeOrigin::signed(BOB))
+				.storage_deposit_limit(Some(codec::Compact(callee_info_len + 2 + ED + 3))) // enough parent limit
+				.data((1u32, &code_hash_callee, 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 = Contracts::bare_call(
-			BOB,
-			addr_caller.clone(),
-			0,
-			GAS_LIMIT,
-			Some(codec::Compact(callee_info_len + 2 + ED + 4).into()),
-			(1u32, &code_hash_callee, callee_info_len + 2 + ED + 3).encode(),
-			DebugInfo::Skip,
-			CollectEvents::Skip,
-			Determinism::Enforced,
-		);
+		let result = builder::bare_call(addr_caller.clone())
+			.origin(BOB)
+			.storage_deposit_limit(Some(codec::Compact(callee_info_len + 2 + ED + 4).into()))
+			.data((1u32, &code_hash_callee, callee_info_len + 2 + ED + 3).encode())
+			.build();
 
 		let returned = result.result.unwrap();
 		// All balance of the caller except ED has been transferred to the callee.
@@ -4891,7 +3518,7 @@ fn deposit_limit_in_nested_instantiate() {
 			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),)
+		assert_eq!(result.storage_deposit.charge_or_zero(), (callee_info_len + 2 + ED + 3))
 	});
 }
 
@@ -4905,20 +3532,7 @@ fn deposit_limit_honors_liquidity_restrictions() {
 		let min_balance = Contracts::min_balance();
 
 		// Instantiate the BOB contract.
-		let addr = Contracts::bare_instantiate(
-			ALICE,
-			0,
-			GAS_LIMIT,
-			None,
-			Code::Upload(wasm),
-			vec![],
-			vec![],
-			DebugInfo::Skip,
-			CollectEvents::Skip,
-		)
-		.result
-		.unwrap()
-		.account_id;
+		let addr = builder::bare_instantiate(Code::Upload(wasm)).build_and_unwrap_account_id();
 
 		let info_deposit = test_utils::contract_info_storage_deposit(&addr);
 		// Check that the contract has been instantiated and has the minimum balance
@@ -4933,14 +3547,11 @@ fn deposit_limit_honors_liquidity_restrictions() {
 		)
 		.unwrap();
 		assert_err_ignore_postinfo!(
-			Contracts::call(
-				RuntimeOrigin::signed(BOB),
-				addr.clone(),
-				0,
-				GAS_LIMIT,
-				Some(codec::Compact(200)),
-				100u32.to_le_bytes().to_vec()
-			),
+			builder::call(addr.clone())
+				.origin(RuntimeOrigin::signed(BOB))
+				.storage_deposit_limit(Some(codec::Compact(200)))
+				.data(100u32.to_le_bytes().to_vec())
+				.build(),
 			<Error<Test>>::StorageDepositNotEnoughFunds,
 		);
 		assert_eq!(<Test as Config>::Currency::free_balance(&BOB), min_balance);
@@ -4956,20 +3567,7 @@ fn deposit_limit_honors_existential_deposit() {
 		let min_balance = Contracts::min_balance();
 
 		// Instantiate the BOB contract.
-		let addr = Contracts::bare_instantiate(
-			ALICE,
-			0,
-			GAS_LIMIT,
-			None,
-			Code::Upload(wasm),
-			vec![],
-			vec![],
-			DebugInfo::Skip,
-			CollectEvents::Skip,
-		)
-		.result
-		.unwrap()
-		.account_id;
+		let addr = builder::bare_instantiate(Code::Upload(wasm)).build_and_unwrap_account_id();
 
 		let info_deposit = test_utils::contract_info_storage_deposit(&addr);
 
@@ -4979,14 +3577,11 @@ fn deposit_limit_honors_existential_deposit() {
 
 		// check that the deposit can't bring the account below the existential deposit
 		assert_err_ignore_postinfo!(
-			Contracts::call(
-				RuntimeOrigin::signed(BOB),
-				addr.clone(),
-				0,
-				GAS_LIMIT,
-				Some(codec::Compact(900)),
-				100u32.to_le_bytes().to_vec()
-			),
+			builder::call(addr.clone())
+				.origin(RuntimeOrigin::signed(BOB))
+				.storage_deposit_limit(Some(codec::Compact(900)))
+				.data(100u32.to_le_bytes().to_vec())
+				.build(),
 			<Error<Test>>::StorageDepositNotEnoughFunds,
 		);
 		assert_eq!(<Test as Config>::Currency::free_balance(&BOB), 1_000);
@@ -5002,20 +3597,7 @@ fn deposit_limit_honors_min_leftover() {
 		let min_balance = Contracts::min_balance();
 
 		// Instantiate the BOB contract.
-		let addr = Contracts::bare_instantiate(
-			ALICE,
-			0,
-			GAS_LIMIT,
-			None,
-			Code::Upload(wasm),
-			vec![],
-			vec![],
-			DebugInfo::Skip,
-			CollectEvents::Skip,
-		)
-		.result
-		.unwrap()
-		.account_id;
+		let addr = builder::bare_instantiate(Code::Upload(wasm)).build_and_unwrap_account_id();
 
 		let info_deposit = test_utils::contract_info_storage_deposit(&addr);
 
@@ -5026,14 +3608,12 @@ fn deposit_limit_honors_min_leftover() {
 
 		// check that the minimum leftover (value send) is considered
 		assert_err_ignore_postinfo!(
-			Contracts::call(
-				RuntimeOrigin::signed(BOB),
-				addr.clone(),
-				400,
-				GAS_LIMIT,
-				Some(codec::Compact(500)),
-				100u32.to_le_bytes().to_vec()
-			),
+			builder::call(addr.clone())
+				.origin(RuntimeOrigin::signed(BOB))
+				.value(400)
+				.storage_deposit_limit(Some(codec::Compact(500)))
+				.data(100u32.to_le_bytes().to_vec())
+				.build(),
 			<Error<Test>>::StorageDepositNotEnoughFunds,
 		);
 		assert_eq!(<Test as Config>::Currency::free_balance(&BOB), 1_000);
@@ -5069,30 +3649,11 @@ fn cannot_instantiate_indeterministic_code() {
 
 		// Try to instantiate directly from code
 		assert_err_ignore_postinfo!(
-			Contracts::instantiate_with_code(
-				RuntimeOrigin::signed(ALICE),
-				0,
-				GAS_LIMIT,
-				None,
-				wasm.clone(),
-				vec![],
-				vec![],
-			),
+			builder::instantiate_with_code(wasm.clone()).build(),
 			<Error<Test>>::CodeRejected,
 		);
 		assert_err!(
-			Contracts::bare_instantiate(
-				ALICE,
-				0,
-				GAS_LIMIT,
-				None,
-				Code::Upload(wasm.clone()),
-				vec![],
-				vec![],
-				DebugInfo::Skip,
-				CollectEvents::Skip,
-			)
-			.result,
+			builder::bare_instantiate(Code::Upload(wasm.clone())).build().result,
 			<Error<Test>>::CodeRejected,
 		);
 
@@ -5116,48 +3677,17 @@ fn cannot_instantiate_indeterministic_code() {
 		));
 
 		assert_err_ignore_postinfo!(
-			Contracts::instantiate(
-				RuntimeOrigin::signed(ALICE),
-				0,
-				GAS_LIMIT,
-				None,
-				code_hash,
-				vec![],
-				vec![],
-			),
+			builder::instantiate(code_hash).build(),
 			<Error<Test>>::Indeterministic,
 		);
 		assert_err!(
-			Contracts::bare_instantiate(
-				ALICE,
-				0,
-				GAS_LIMIT,
-				None,
-				Code::Existing(code_hash),
-				vec![],
-				vec![],
-				DebugInfo::Skip,
-				CollectEvents::Skip,
-			)
-			.result,
+			builder::bare_instantiate(Code::Existing(code_hash)).build().result,
 			<Error<Test>>::Indeterministic,
 		);
 
 		// Deploy contract which instantiates another contract
-		let addr = Contracts::bare_instantiate(
-			ALICE,
-			0,
-			GAS_LIMIT,
-			None,
-			Code::Upload(caller_wasm),
-			vec![],
-			vec![],
-			DebugInfo::Skip,
-			CollectEvents::Skip,
-		)
-		.result
-		.unwrap()
-		.account_id;
+		let addr =
+			builder::bare_instantiate(Code::Upload(caller_wasm)).build_and_unwrap_account_id();
 
 		// Try to instantiate `code_hash` from another contract in deterministic mode
 		assert_err!(
@@ -5211,20 +3741,8 @@ fn cannot_set_code_indeterministic_code() {
 		));
 
 		// Create the contract that will call `seal_set_code_hash`
-		let caller_addr = Contracts::bare_instantiate(
-			ALICE,
-			0,
-			GAS_LIMIT,
-			None,
-			Code::Upload(caller_wasm),
-			vec![],
-			vec![],
-			DebugInfo::Skip,
-			CollectEvents::Skip,
-		)
-		.result
-		.unwrap()
-		.account_id;
+		let caller_addr =
+			builder::bare_instantiate(Code::Upload(caller_wasm)).build_and_unwrap_account_id();
 
 		// We do not allow to set the code hash to a non-deterministic wasm
 		assert_err!(
@@ -5261,20 +3779,8 @@ fn delegate_call_indeterministic_code() {
 		));
 
 		// Create the contract that will call `seal_delegate_call`
-		let caller_addr = Contracts::bare_instantiate(
-			ALICE,
-			0,
-			GAS_LIMIT,
-			None,
-			Code::Upload(caller_wasm),
-			vec![],
-			vec![],
-			DebugInfo::Skip,
-			CollectEvents::Skip,
-		)
-		.result
-		.unwrap()
-		.account_id;
+		let caller_addr =
+			builder::bare_instantiate(Code::Upload(caller_wasm)).build_and_unwrap_account_id();
 
 		// The delegate call will fail in deterministic mode
 		assert_err!(
@@ -5331,17 +3837,9 @@ fn locking_delegate_dependency_works() {
 
 	// Instantiate the caller contract with the given input.
 	let instantiate = |input: &(u32, H256)| {
-		Contracts::bare_instantiate(
-			ALICE,
-			0,
-			GAS_LIMIT,
-			None,
-			Code::Upload(wasm_caller.clone()),
-			input.encode(),
-			vec![],
-			DebugInfo::Skip,
-			CollectEvents::Skip,
-		)
+		builder::bare_instantiate(Code::Upload(wasm_caller.clone()))
+			.data(input.encode())
+			.build()
 	};
 
 	// Call contract with the given input.
@@ -5509,17 +4007,7 @@ fn native_dependency_deposit_works() {
 			};
 
 			// Instantiate the set_code_hash contract.
-			let res = Contracts::bare_instantiate(
-				ALICE,
-				0,
-				GAS_LIMIT,
-				None,
-				code,
-				vec![],
-				vec![],
-				DebugInfo::Skip,
-				CollectEvents::Skip,
-			);
+			let res = builder::bare_instantiate(code).build();
 
 			let addr = res.result.unwrap().account_id;
 			let base_deposit = ED + test_utils::contract_info_storage_deposit(&addr);
@@ -5567,37 +4055,17 @@ fn reentrance_count_works_with_call() {
 	ExtBuilder::default().existential_deposit(100).build().execute_with(|| {
 		let _ = <Test as Config>::Currency::set_balance(&ALICE, 1_000_000);
 
-		let contract_addr = Contracts::bare_instantiate(
-			ALICE,
-			300_000,
-			GAS_LIMIT,
-			None,
-			Code::Upload(wasm),
-			vec![],
-			vec![],
-			DebugInfo::Skip,
-			CollectEvents::Skip,
-		)
-		.result
-		.unwrap()
-		.account_id;
+		let contract_addr = builder::bare_instantiate(Code::Upload(wasm))
+			.value(300_000)
+			.build_and_unwrap_account_id();
 
 		// passing reentrant count to the input
 		let input = 0.encode();
 
-		Contracts::bare_call(
-			ALICE,
-			contract_addr,
-			0,
-			GAS_LIMIT,
-			None,
-			input,
-			DebugInfo::UnsafeDebug,
-			CollectEvents::Skip,
-			Determinism::Enforced,
-		)
-		.result
-		.unwrap();
+		builder::bare_call(contract_addr)
+			.data(input)
+			.debug(DebugInfo::UnsafeDebug)
+			.build_and_unwrap_result();
 	});
 }
 
@@ -5608,37 +4076,17 @@ fn reentrance_count_works_with_delegated_call() {
 	ExtBuilder::default().existential_deposit(100).build().execute_with(|| {
 		let _ = <Test as Config>::Currency::set_balance(&ALICE, 1_000_000);
 
-		let contract_addr = Contracts::bare_instantiate(
-			ALICE,
-			300_000,
-			GAS_LIMIT,
-			None,
-			Code::Upload(wasm),
-			vec![],
-			vec![],
-			DebugInfo::Skip,
-			CollectEvents::Skip,
-		)
-		.result
-		.unwrap()
-		.account_id;
+		let contract_addr = builder::bare_instantiate(Code::Upload(wasm))
+			.value(300_000)
+			.build_and_unwrap_account_id();
 
 		// adding a callstack height to the input
 		let input = (code_hash, 1).encode();
 
-		Contracts::bare_call(
-			ALICE,
-			contract_addr.clone(),
-			0,
-			GAS_LIMIT,
-			None,
-			input,
-			DebugInfo::UnsafeDebug,
-			CollectEvents::Skip,
-			Determinism::Enforced,
-		)
-		.result
-		.unwrap();
+		builder::bare_call(contract_addr.clone())
+			.data(input)
+			.debug(DebugInfo::UnsafeDebug)
+			.build_and_unwrap_result();
 	});
 }
 
@@ -5651,63 +4099,23 @@ fn account_reentrance_count_works() {
 	ExtBuilder::default().existential_deposit(100).build().execute_with(|| {
 		let _ = <Test as Config>::Currency::set_balance(&ALICE, 1_000_000);
 
-		let contract_addr = Contracts::bare_instantiate(
-			ALICE,
-			300_000,
-			GAS_LIMIT,
-			None,
-			Code::Upload(wasm),
-			vec![],
-			vec![],
-			DebugInfo::Skip,
-			CollectEvents::Skip,
-		)
-		.result
-		.unwrap()
-		.account_id;
+		let contract_addr = builder::bare_instantiate(Code::Upload(wasm))
+			.value(300_000)
+			.build_and_unwrap_account_id();
 
-		let another_contract_addr = Contracts::bare_instantiate(
-			ALICE,
-			300_000,
-			GAS_LIMIT,
-			None,
-			Code::Upload(wasm_reentrance_count),
-			vec![],
-			vec![],
-			DebugInfo::Skip,
-			CollectEvents::Skip,
-		)
-		.result
-		.unwrap()
-		.account_id;
+		let another_contract_addr = builder::bare_instantiate(Code::Upload(wasm_reentrance_count))
+			.value(300_000)
+			.build_and_unwrap_account_id();
 
-		let result1 = Contracts::bare_call(
-			ALICE,
-			contract_addr.clone(),
-			0,
-			GAS_LIMIT,
-			None,
-			contract_addr.encode(),
-			DebugInfo::UnsafeDebug,
-			CollectEvents::Skip,
-			Determinism::Enforced,
-		)
-		.result
-		.unwrap();
+		let result1 = builder::bare_call(contract_addr.clone())
+			.data(contract_addr.encode())
+			.debug(DebugInfo::UnsafeDebug)
+			.build_and_unwrap_result();
 
-		let result2 = Contracts::bare_call(
-			ALICE,
-			contract_addr.clone(),
-			0,
-			GAS_LIMIT,
-			None,
-			another_contract_addr.encode(),
-			DebugInfo::UnsafeDebug,
-			CollectEvents::Skip,
-			Determinism::Enforced,
-		)
-		.result
-		.unwrap();
+		let result2 = builder::bare_call(contract_addr.clone())
+			.data(another_contract_addr.encode())
+			.debug(DebugInfo::UnsafeDebug)
+			.build_and_unwrap_result();
 
 		assert_eq!(result1.data, 1.encode());
 		assert_eq!(result2.data, 0.encode());
@@ -5754,7 +4162,7 @@ fn signed_cannot_set_code() {
 fn none_cannot_call_code() {
 	ExtBuilder::default().build().execute_with(|| {
 		assert_noop!(
-			Contracts::call(RuntimeOrigin::none(), BOB, 0, GAS_LIMIT, None, Vec::new()),
+			builder::call(BOB).origin(RuntimeOrigin::none()).build(),
 			DispatchError::BadOrigin,
 		);
 	});
@@ -5767,30 +4175,10 @@ fn root_can_call() {
 	ExtBuilder::default().existential_deposit(100).build().execute_with(|| {
 		let _ = <Test as Config>::Currency::set_balance(&ALICE, 1_000_000);
 
-		let addr = Contracts::bare_instantiate(
-			ALICE,
-			0,
-			GAS_LIMIT,
-			None,
-			Code::Upload(wasm),
-			vec![],
-			vec![],
-			DebugInfo::Skip,
-			CollectEvents::Skip,
-		)
-		.result
-		.unwrap()
-		.account_id;
+		let addr = builder::bare_instantiate(Code::Upload(wasm)).build_and_unwrap_account_id();
 
 		// Call the contract.
-		assert_ok!(Contracts::call(
-			RuntimeOrigin::root(),
-			addr.clone(),
-			0,
-			GAS_LIMIT,
-			None,
-			vec![]
-		));
+		assert_ok!(builder::call(addr.clone()).origin(RuntimeOrigin::root()).build());
 	});
 }
 
@@ -5800,15 +4188,7 @@ fn root_cannot_instantiate_with_code() {
 
 	ExtBuilder::default().build().execute_with(|| {
 		assert_err_ignore_postinfo!(
-			Contracts::instantiate_with_code(
-				RuntimeOrigin::root(),
-				0,
-				GAS_LIMIT,
-				None,
-				wasm,
-				vec![],
-				vec![],
-			),
+			builder::instantiate_with_code(wasm).origin(RuntimeOrigin::root()).build(),
 			DispatchError::BadOrigin
 		);
 	});
@@ -5820,15 +4200,7 @@ fn root_cannot_instantiate() {
 
 	ExtBuilder::default().build().execute_with(|| {
 		assert_err_ignore_postinfo!(
-			Contracts::instantiate(
-				RuntimeOrigin::root(),
-				0,
-				GAS_LIMIT,
-				None,
-				code_hash,
-				vec![],
-				vec![],
-			),
+			builder::instantiate(code_hash).origin(RuntimeOrigin::root()).build(),
 			DispatchError::BadOrigin
 		);
 	});
@@ -5881,53 +4253,25 @@ fn only_instantiation_origin_can_instantiate() {
 		let _ = Balances::set_balance(&BOB, 1_000_000);
 
 		assert_err_ignore_postinfo!(
-			Contracts::instantiate_with_code(
-				RuntimeOrigin::root(),
-				0,
-				GAS_LIMIT,
-				None,
-				code.clone(),
-				vec![],
-				vec![],
-			),
+			builder::instantiate_with_code(code.clone())
+				.origin(RuntimeOrigin::root())
+				.build(),
 			DispatchError::BadOrigin
 		);
 
 		assert_err_ignore_postinfo!(
-			Contracts::instantiate_with_code(
-				RuntimeOrigin::signed(BOB),
-				0,
-				GAS_LIMIT,
-				None,
-				code.clone(),
-				vec![],
-				vec![],
-			),
+			builder::instantiate_with_code(code.clone())
+				.origin(RuntimeOrigin::signed(BOB))
+				.build(),
 			DispatchError::BadOrigin
 		);
 
 		// Only Alice can instantiate
-		assert_ok!(Contracts::instantiate_with_code(
-			RuntimeOrigin::signed(ALICE),
-			0,
-			GAS_LIMIT,
-			None,
-			code,
-			vec![],
-			vec![],
-		),);
+		assert_ok!(builder::instantiate_with_code(code).build());
 
 		// Bob cannot instantiate with either `instantiate_with_code` or `instantiate`.
 		assert_err_ignore_postinfo!(
-			Contracts::instantiate(
-				RuntimeOrigin::signed(BOB),
-				0,
-				GAS_LIMIT,
-				None,
-				code_hash,
-				vec![],
-				vec![],
-			),
+			builder::instantiate(code_hash).origin(RuntimeOrigin::signed(BOB)).build(),
 			DispatchError::BadOrigin
 		);
 	});
@@ -5940,44 +4284,18 @@ fn balance_api_returns_free_balance() {
 		let _ = <Test as Config>::Currency::set_balance(&ALICE, 1_000_000);
 
 		// Instantiate the BOB contract without any extra balance.
-		let addr = Contracts::bare_instantiate(
-			ALICE,
-			0,
-			GAS_LIMIT,
-			None,
-			Code::Upload(wasm.to_vec()),
-			vec![],
-			vec![],
-			DebugInfo::Skip,
-			CollectEvents::Skip,
-		)
-		.result
-		.unwrap()
-		.account_id;
+		let addr =
+			builder::bare_instantiate(Code::Upload(wasm.to_vec())).build_and_unwrap_account_id();
 
 		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!(Contracts::call(
-			RuntimeOrigin::signed(ALICE),
-			addr.clone(),
-			value,
-			GAS_LIMIT,
-			None,
-			vec![]
-		));
+		assert_ok!(builder::call(addr.clone()).value(value).build());
 
 		let value = 1;
 		// Calling with value will trap the contract.
 		assert_err_ignore_postinfo!(
-			Contracts::call(
-				RuntimeOrigin::signed(ALICE),
-				addr.clone(),
-				value,
-				GAS_LIMIT,
-				None,
-				vec![]
-			),
+			builder::call(addr.clone()).value(value).build(),
 			<Error<Test>>::ContractTrapped
 		);
 	});
@@ -5989,37 +4307,14 @@ fn gas_consumed_is_linear_for_nested_calls() {
 	ExtBuilder::default().existential_deposit(200).build().execute_with(|| {
 		let _ = <Test as Config>::Currency::set_balance(&ALICE, 1_000_000);
 
-		let addr = Contracts::bare_instantiate(
-			ALICE,
-			0,
-			GAS_LIMIT,
-			None,
-			Code::Upload(code),
-			vec![],
-			vec![],
-			DebugInfo::Skip,
-			CollectEvents::Skip,
-		)
-		.result
-		.unwrap()
-		.account_id;
+		let addr = builder::bare_instantiate(Code::Upload(code)).build_and_unwrap_account_id();
 
 		let max_call_depth = <Test as Config>::CallStack::size() as u32;
 		let [gas_0, gas_1, gas_2, gas_max] = {
 			[0u32, 1u32, 2u32, max_call_depth]
 				.iter()
 				.map(|i| {
-					let result = Contracts::bare_call(
-						ALICE,
-						addr.clone(),
-						0,
-						GAS_LIMIT,
-						None,
-						i.encode(),
-						DebugInfo::Skip,
-						CollectEvents::Skip,
-						Determinism::Enforced,
-					);
+					let result = builder::bare_call(addr.clone()).data(i.encode()).build();
 					assert_ok!(result.result);
 					result.gas_consumed
 				})
diff --git a/substrate/frame/contracts/src/tests/builder.rs b/substrate/frame/contracts/src/tests/builder.rs
new file mode 100644
index 00000000000..08d12503a29
--- /dev/null
+++ b/substrate/frame/contracts/src/tests/builder.rs
@@ -0,0 +1,219 @@
+// 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.
+
+use super::{AccountId32, Test, ALICE, GAS_LIMIT};
+use crate::{
+	tests::RuntimeOrigin, AccountIdLookupOf, AccountIdOf, BalanceOf, Code, CodeHash, CollectEvents,
+	ContractExecResult, ContractInstantiateResult, DebugInfo, Determinism, EventRecordOf,
+	ExecReturnValue, OriginFor, Pallet, Weight,
+};
+use codec::Compact;
+use frame_support::pallet_prelude::DispatchResultWithPostInfo;
+use paste::paste;
+
+/// Helper macro to generate a builder for contract API calls.
+macro_rules! builder {
+	// Entry point to generate a builder for the given method.
+	(
+		$method:ident($($field:ident: $type:ty,)*) -> $result:ty
+	) => {
+		paste!{
+			builder!([< $method:camel Builder >], $method($($field: $type,)* ) -> $result);
+		}
+	};
+	// Generate the builder struct and its methods.
+	(
+		$name:ident,
+		$method:ident(
+			$($field:ident: $type:ty,)*
+		) -> $result:ty
+	) => {
+		#[doc = concat!("A builder to construct a ", stringify!($method), " call")]
+		pub struct $name {
+			$($field: $type,)*
+		}
+
+		#[allow(dead_code)]
+		impl $name
+		{
+			$(
+				#[doc = concat!("Set the ", stringify!($field))]
+				pub fn $field(mut self, value: $type) -> Self {
+					self.$field = value;
+					self
+				}
+			)*
+
+			#[doc = concat!("Build the ", stringify!($method), " call")]
+			pub fn build(self) -> $result {
+				Pallet::<Test>::$method(
+					$(self.$field,)*
+				)
+			}
+		}
+	}
+}
+
+builder!(
+	instantiate_with_code(
+		origin: OriginFor<Test>,
+		value: BalanceOf<Test>,
+		gas_limit: Weight,
+		storage_deposit_limit: Option<Compact<BalanceOf<Test>>>,
+		code: Vec<u8>,
+		data: Vec<u8>,
+		salt: Vec<u8>,
+	) -> DispatchResultWithPostInfo
+);
+
+builder!(
+	instantiate(
+		origin: OriginFor<Test>,
+		value: BalanceOf<Test>,
+		gas_limit: Weight,
+		storage_deposit_limit: Option<Compact<BalanceOf<Test>>>,
+		code_hash: CodeHash<Test>,
+		data: Vec<u8>,
+		salt: Vec<u8>,
+	) -> DispatchResultWithPostInfo
+);
+
+builder!(
+	bare_instantiate(
+		origin: AccountIdOf<Test>,
+		value: BalanceOf<Test>,
+		gas_limit: Weight,
+		storage_deposit_limit: Option<BalanceOf<Test>>,
+		code: Code<CodeHash<Test>>,
+		data: Vec<u8>,
+		salt: Vec<u8>,
+		debug: DebugInfo,
+		collect_events: CollectEvents,
+	) -> ContractInstantiateResult<AccountIdOf<Test>, BalanceOf<Test>, EventRecordOf<Test>>
+);
+
+builder!(
+	call(
+		origin: OriginFor<Test>,
+		dest: AccountIdLookupOf<Test>,
+		value: BalanceOf<Test>,
+		gas_limit: Weight,
+		storage_deposit_limit: Option<Compact<BalanceOf<Test>>>,
+		data: Vec<u8>,
+	) -> DispatchResultWithPostInfo
+);
+
+builder!(
+	bare_call(
+		origin: AccountIdOf<Test>,
+		dest: AccountIdOf<Test>,
+		value: BalanceOf<Test>,
+		gas_limit: Weight,
+		storage_deposit_limit: Option<BalanceOf<Test>>,
+		data: Vec<u8>,
+		debug: DebugInfo,
+		collect_events: CollectEvents,
+		determinism: Determinism,
+	) -> ContractExecResult<BalanceOf<Test>, EventRecordOf<Test>>
+);
+
+/// Create a [`BareInstantiateBuilder`] with default values.
+pub fn bare_instantiate(code: Code<CodeHash<Test>>) -> BareInstantiateBuilder {
+	BareInstantiateBuilder {
+		origin: ALICE,
+		value: 0,
+		gas_limit: GAS_LIMIT,
+		storage_deposit_limit: None,
+		code,
+		data: vec![],
+		salt: vec![],
+		debug: DebugInfo::Skip,
+		collect_events: CollectEvents::Skip,
+	}
+}
+
+impl BareInstantiateBuilder {
+	/// Build the instantiate call and unwrap the result.
+	pub fn build_and_unwrap_result(self) -> crate::InstantiateReturnValue<AccountIdOf<Test>> {
+		self.build().result.unwrap()
+	}
+
+	/// Build the instantiate call and unwrap the account id.
+	pub fn build_and_unwrap_account_id(self) -> AccountIdOf<Test> {
+		self.build().result.unwrap().account_id
+	}
+}
+
+/// Create a [`BareCallBuilder`] with default values.
+pub fn bare_call(dest: AccountId32) -> BareCallBuilder {
+	BareCallBuilder {
+		origin: ALICE,
+		dest,
+		value: 0,
+		gas_limit: GAS_LIMIT,
+		storage_deposit_limit: None,
+		data: vec![],
+		debug: DebugInfo::Skip,
+		collect_events: CollectEvents::Skip,
+		determinism: Determinism::Enforced,
+	}
+}
+
+impl BareCallBuilder {
+	/// Build the call and unwrap the result.
+	pub fn build_and_unwrap_result(self) -> ExecReturnValue {
+		self.build().result.unwrap()
+	}
+}
+
+/// Create an [`InstantiateWithCodeBuilder`] with default values.
+pub fn instantiate_with_code(code: Vec<u8>) -> InstantiateWithCodeBuilder {
+	InstantiateWithCodeBuilder {
+		origin: RuntimeOrigin::signed(ALICE),
+		value: 0,
+		gas_limit: GAS_LIMIT,
+		storage_deposit_limit: None,
+		code,
+		data: vec![],
+		salt: vec![],
+	}
+}
+
+/// Create an [`InstantiateBuilder`] with default values.
+pub fn instantiate(code_hash: CodeHash<Test>) -> InstantiateBuilder {
+	InstantiateBuilder {
+		origin: RuntimeOrigin::signed(ALICE),
+		value: 0,
+		gas_limit: GAS_LIMIT,
+		storage_deposit_limit: None,
+		code_hash,
+		data: vec![],
+		salt: vec![],
+	}
+}
+
+/// Create a [`CallBuilder`] with default values.
+pub fn call(dest: AccountIdLookupOf<Test>) -> CallBuilder {
+	CallBuilder {
+		origin: RuntimeOrigin::signed(ALICE),
+		dest,
+		value: 0,
+		gas_limit: GAS_LIMIT,
+		storage_deposit_limit: None,
+		data: vec![],
+	}
+}
-- 
GitLab