diff --git a/substrate/frame/contracts/src/lib.rs b/substrate/frame/contracts/src/lib.rs
index 77efcc6986e64e4c4ac00fbab69f3cff33097d93..0d7e4cbf56474f84d749c5a561c3bed3fe93803c 100644
--- a/substrate/frame/contracts/src/lib.rs
+++ b/substrate/frame/contracts/src/lib.rs
@@ -104,7 +104,7 @@ pub use crate::{
 	schedule::{HostFnWeights, InstructionWeights, Limits, Schedule},
 };
 use crate::{
-	exec::{Executable, Stack as ExecStack},
+	exec::{AccountIdOf, ExecError, Executable, Stack as ExecStack},
 	gas::GasMeter,
 	storage::{ContractInfo, DeletedContract, Storage},
 	wasm::PrefabWasmModule,
@@ -112,13 +112,14 @@ use crate::{
 };
 use frame_support::{
 	dispatch::Dispatchable,
+	ensure,
 	traits::{Contains, Currency, Get, Randomness, StorageVersion, Time},
 	weights::{GetDispatchInfo, PostDispatchInfo, Weight},
 };
 use frame_system::Pallet as System;
 use pallet_contracts_primitives::{
-	Code, ContractAccessError, ContractExecResult, ContractInstantiateResult, GetStorageResult,
-	InstantiateReturnValue,
+	Code, ContractAccessError, ContractExecResult, ContractInstantiateResult, ExecReturnValue,
+	GetStorageResult, InstantiateReturnValue,
 };
 use sp_core::{crypto::UncheckedFrom, Bytes};
 use sp_runtime::traits::{Convert, Hash, Saturating, StaticLookup};
@@ -272,18 +273,8 @@ pub mod pallet {
 		) -> DispatchResultWithPostInfo {
 			let origin = ensure_signed(origin)?;
 			let dest = T::Lookup::lookup(dest)?;
-			let mut gas_meter = GasMeter::new(gas_limit);
-			let schedule = T::Schedule::get();
-			let result = ExecStack::<T, PrefabWasmModule<T>>::run_call(
-				origin,
-				dest,
-				&mut gas_meter,
-				&schedule,
-				value,
-				data,
-				None,
-			);
-			gas_meter.into_dispatch_result(result, T::WeightInfo::call())
+			let output = Self::internal_call(origin, dest, value, gas_limit, data, None);
+			output.gas_meter.into_dispatch_result(output.result, T::WeightInfo::call())
 		}
 
 		/// Instantiates a new contract from the supplied `code` optionally transferring
@@ -325,26 +316,19 @@ pub mod pallet {
 		) -> DispatchResultWithPostInfo {
 			let origin = ensure_signed(origin)?;
 			let code_len = code.len() as u32;
-			ensure!(code_len <= T::Schedule::get().limits.code_len, Error::<T>::CodeTooLarge);
-			let mut gas_meter = GasMeter::new(gas_limit);
-			let schedule = T::Schedule::get();
-			let executable = PrefabWasmModule::from_code(code, &schedule)?;
-			let code_len = executable.code_len();
-			ensure!(code_len <= T::Schedule::get().limits.code_len, Error::<T>::CodeTooLarge);
-			let result = ExecStack::<T, PrefabWasmModule<T>>::run_instantiate(
+			let salt_len = salt.len() as u32;
+			let output = Self::internal_instantiate(
 				origin,
-				executable,
-				&mut gas_meter,
-				&schedule,
 				endowment,
+				gas_limit,
+				Code::Upload(Bytes(code)),
 				data,
-				&salt,
+				salt,
 				None,
-			)
-			.map(|(_address, output)| output);
-			gas_meter.into_dispatch_result(
-				result,
-				T::WeightInfo::instantiate_with_code(code_len / 1024, salt.len() as u32 / 1024),
+			);
+			output.gas_meter.into_dispatch_result(
+				output.result.map(|(_address, result)| result),
+				T::WeightInfo::instantiate_with_code(code_len / 1024, salt_len / 1024),
 			)
 		}
 
@@ -365,22 +349,20 @@ pub mod pallet {
 			salt: Vec<u8>,
 		) -> DispatchResultWithPostInfo {
 			let origin = ensure_signed(origin)?;
-			let mut gas_meter = GasMeter::new(gas_limit);
-			let schedule = T::Schedule::get();
-			let executable = PrefabWasmModule::from_storage(code_hash, &schedule, &mut gas_meter)?;
-			let result = ExecStack::<T, PrefabWasmModule<T>>::run_instantiate(
+			let salt_len = salt.len() as u32;
+			let output = Self::internal_instantiate(
 				origin,
-				executable,
-				&mut gas_meter,
-				&schedule,
 				endowment,
+				gas_limit,
+				Code::Existing(code_hash),
 				data,
-				&salt,
+				salt,
 				None,
+			);
+			output.gas_meter.into_dispatch_result(
+				output.result.map(|(_address, output)| output),
+				T::WeightInfo::instantiate(salt_len / 1024),
 			)
-			.map(|(_address, output)| output);
-			gas_meter
-				.into_dispatch_result(result, T::WeightInfo::instantiate(salt.len() as u32 / 1024))
 		}
 	}
 
@@ -535,6 +517,20 @@ pub mod pallet {
 	pub(crate) type DeletionQueue<T: Config> = StorageValue<_, Vec<DeletedContract>, ValueQuery>;
 }
 
+/// Return type of the private [`Pallet::internal_call`] function.
+type InternalCallOutput<T> = InternalOutput<T, ExecReturnValue>;
+
+/// Return type of the private [`Pallet::internal_instantiate`] function.
+type InternalInstantiateOutput<T> = InternalOutput<T, (AccountIdOf<T>, ExecReturnValue)>;
+
+/// Return type of private helper functions.
+struct InternalOutput<T: Config, O> {
+	/// The gas meter that was used to execute the call.
+	gas_meter: GasMeter<T>,
+	/// The result of the call.
+	result: Result<O, ExecError>,
+}
+
 impl<T: Config> Pallet<T>
 where
 	T::AccountId: UncheckedFrom<T::Hash> + AsRef<[u8]>,
@@ -556,25 +552,16 @@ where
 		dest: T::AccountId,
 		value: BalanceOf<T>,
 		gas_limit: Weight,
-		input_data: Vec<u8>,
+		data: Vec<u8>,
 		debug: bool,
 	) -> ContractExecResult {
-		let mut gas_meter = GasMeter::new(gas_limit);
-		let schedule = T::Schedule::get();
 		let mut debug_message = if debug { Some(Vec::new()) } else { None };
-		let result = ExecStack::<T, PrefabWasmModule<T>>::run_call(
-			origin,
-			dest,
-			&mut gas_meter,
-			&schedule,
-			value,
-			input_data,
-			debug_message.as_mut(),
-		);
+		let output =
+			Self::internal_call(origin, dest, value, gas_limit, data, debug_message.as_mut());
 		ContractExecResult {
-			result: result.map_err(|r| r.error),
-			gas_consumed: gas_meter.gas_consumed(),
-			gas_required: gas_meter.gas_required(),
+			result: output.result.map_err(|r| r.error),
+			gas_consumed: output.gas_meter.gas_consumed(),
+			gas_required: output.gas_meter.gas_required(),
 			debug_message: debug_message.unwrap_or_default(),
 		}
 	}
@@ -601,38 +588,23 @@ where
 		salt: Vec<u8>,
 		debug: bool,
 	) -> ContractInstantiateResult<T::AccountId> {
-		let mut gas_meter = GasMeter::new(gas_limit);
-		let schedule = T::Schedule::get();
-		let executable = match code {
-			Code::Upload(Bytes(binary)) => PrefabWasmModule::from_code(binary, &schedule),
-			Code::Existing(hash) => PrefabWasmModule::from_storage(hash, &schedule, &mut gas_meter),
-		};
-		let executable = match executable {
-			Ok(executable) => executable,
-			Err(error) =>
-				return ContractInstantiateResult {
-					result: Err(error.into()),
-					gas_consumed: gas_meter.gas_consumed(),
-					gas_required: gas_meter.gas_required(),
-					debug_message: Vec::new(),
-				},
-		};
 		let mut debug_message = if debug { Some(Vec::new()) } else { None };
-		let result = ExecStack::<T, PrefabWasmModule<T>>::run_instantiate(
+		let output = Self::internal_instantiate(
 			origin,
-			executable,
-			&mut gas_meter,
-			&schedule,
 			endowment,
+			gas_limit,
+			code,
 			data,
-			&salt,
+			salt,
 			debug_message.as_mut(),
-		)
-		.and_then(|(account_id, result)| Ok(InstantiateReturnValue { result, account_id }));
+		);
 		ContractInstantiateResult {
-			result: result.map_err(|e| e.error),
-			gas_consumed: gas_meter.gas_consumed(),
-			gas_required: gas_meter.gas_required(),
+			result: output
+				.result
+				.map(|(account_id, result)| InstantiateReturnValue { result, account_id })
+				.map_err(|e| e.error),
+			gas_consumed: output.gas_meter.gas_consumed(),
+			gas_required: output.gas_meter.gas_required(),
 			debug_message: debug_message.unwrap_or_default(),
 		}
 	}
@@ -709,4 +681,74 @@ where
 	) -> frame_support::dispatch::DispatchResult {
 		self::wasm::reinstrument(module, schedule)
 	}
+
+	/// Internal function that does the actual call.
+	///
+	/// Called by dispatchables and public functions.
+	fn internal_call(
+		origin: T::AccountId,
+		dest: T::AccountId,
+		value: BalanceOf<T>,
+		gas_limit: Weight,
+		data: Vec<u8>,
+		debug_message: Option<&mut Vec<u8>>,
+	) -> InternalCallOutput<T> {
+		let mut gas_meter = GasMeter::new(gas_limit);
+		let schedule = T::Schedule::get();
+		let result = ExecStack::<T, PrefabWasmModule<T>>::run_call(
+			origin,
+			dest,
+			&mut gas_meter,
+			&schedule,
+			value,
+			data,
+			debug_message,
+		);
+		InternalCallOutput { gas_meter, result }
+	}
+
+	/// Internal function that does the actual instantiation.
+	///
+	/// Called by dispatchables and public functions.
+	fn internal_instantiate(
+		origin: T::AccountId,
+		endowment: BalanceOf<T>,
+		gas_limit: Weight,
+		code: Code<CodeHash<T>>,
+		data: Vec<u8>,
+		salt: Vec<u8>,
+		debug_message: Option<&mut Vec<u8>>,
+	) -> InternalInstantiateOutput<T> {
+		let mut gas_meter = GasMeter::new(gas_limit);
+		let schedule = T::Schedule::get();
+		let try_exec = || {
+			let executable = match code {
+				Code::Upload(Bytes(binary)) => {
+					ensure!(
+						binary.len() as u32 <= schedule.limits.code_len,
+						<Error<T>>::CodeTooLarge
+					);
+					let executable = PrefabWasmModule::from_code(binary, &schedule)?;
+					ensure!(
+						executable.code_len() <= schedule.limits.code_len,
+						<Error<T>>::CodeTooLarge
+					);
+					executable
+				},
+				Code::Existing(hash) =>
+					PrefabWasmModule::from_storage(hash, &schedule, &mut gas_meter)?,
+			};
+			ExecStack::<T, PrefabWasmModule<T>>::run_instantiate(
+				origin,
+				executable,
+				&mut gas_meter,
+				&schedule,
+				endowment,
+				data,
+				&salt,
+				debug_message,
+			)
+		};
+		InternalInstantiateOutput { result: try_exec(), gas_meter }
+	}
 }