diff --git a/bridges/modules/parachains/src/lib.rs b/bridges/modules/parachains/src/lib.rs
index 3cf0e60e3cdf87e50827592da00e34fac0902f02..33338433e3bfe11a8cdf547f821f9afb64c01072 100644
--- a/bridges/modules/parachains/src/lib.rs
+++ b/bridges/modules/parachains/src/lib.rs
@@ -238,6 +238,7 @@ pub mod pallet {
 				&parachain_heads_proof,
 				parachains.len() as _,
 			);
+
 			pallet_bridge_grandpa::Pallet::<T, T::BridgesGrandpaPalletInstance>::parse_finalized_storage_proof(
 				relay_block_hash,
 				sp_trie::StorageProof::new(parachain_heads_proof.0),
@@ -293,7 +294,7 @@ pub mod pallet {
 							continue;
 						}
 
-						let prune_happened: Result<_, ()> = BestParaHeads::<T, I>::try_mutate(parachain, |stored_best_head| {
+						let update_result: Result<_, ()> = BestParaHeads::<T, I>::try_mutate(parachain, |stored_best_head| {
 							let artifacts = Pallet::<T, I>::update_parachain_head(
 								parachain,
 								stored_best_head.take(),
@@ -305,7 +306,14 @@ pub mod pallet {
 							Ok(artifacts.prune_happened)
 						});
 
-						if matches!(prune_happened, Err(_) | Ok(false)) {
+						// we're refunding weight if update has not happened and if pruning has not happened
+						let is_update_happened = matches!(update_result, Ok(_));
+						if !is_update_happened {
+							actual_weight = actual_weight
+								.saturating_sub(WeightInfoOf::<T, I>::parachain_head_storage_write_weight(T::DbWeight::get()));
+						}
+						let is_prune_happened = matches!(update_result, Ok(true));
+						if !is_prune_happened {
 							actual_weight = actual_weight
 								.saturating_sub(WeightInfoOf::<T, I>::parachain_head_pruning_weight(T::DbWeight::get()));
 						}
@@ -538,6 +546,8 @@ mod tests {
 	};
 
 	type BridgesGrandpaPalletInstance = pallet_bridge_grandpa::Instance1;
+	type WeightInfo = <TestRuntime as Config>::WeightInfo;
+	type DbWeight = <TestRuntime as frame_system::Config>::DbWeight;
 
 	fn initialize(state_root: RelayBlockHash) {
 		pallet_bridge_grandpa::Pallet::<TestRuntime, BridgesGrandpaPalletInstance>::initialize(
@@ -675,12 +685,16 @@ mod tests {
 			initialize(state_root);
 
 			// we're trying to update heads of parachains 1, 2 and 3
-			assert_ok!(Pallet::<TestRuntime>::submit_parachain_heads(
+			let expected_weight =
+				WeightInfo::submit_parachain_heads_weight(DbWeight::get(), &proof, 2);
+			let result = Pallet::<TestRuntime>::submit_parachain_heads(
 				Origin::signed(1),
 				(0, test_relay_header(0, state_root).hash()),
 				parachains,
 				proof,
-			),);
+			);
+			assert_ok!(result);
+			assert_eq!(result.expect("checked above").actual_weight, Some(expected_weight));
 
 			// but only 1 and 2 are updated, because proof is missing head of parachain#2
 			assert_eq!(BestParaHeads::<TestRuntime>::get(ParaId(1)), Some(initial_best_head(1)));
@@ -768,13 +782,20 @@ mod tests {
 		run_test(|| {
 			// start with relay block #0 and try to import head#5 of parachain#1 and untracked
 			// parachain
+			let expected_weight =
+				WeightInfo::submit_parachain_heads_weight(DbWeight::get(), &proof, 3)
+					.saturating_sub(WeightInfo::parachain_head_storage_write_weight(
+						DbWeight::get(),
+					));
 			initialize(state_root);
-			assert_ok!(Pallet::<TestRuntime>::submit_parachain_heads(
+			let result = Pallet::<TestRuntime>::submit_parachain_heads(
 				Origin::signed(1),
 				(0, test_relay_header(0, state_root).hash()),
 				parachains,
 				proof,
-			));
+			);
+			assert_ok!(result);
+			assert_eq!(result.expect("checked above").actual_weight, Some(expected_weight));
 			assert_eq!(
 				BestParaHeads::<TestRuntime>::get(ParaId(1)),
 				Some(BestParaHead {
diff --git a/bridges/modules/parachains/src/weights_ext.rs b/bridges/modules/parachains/src/weights_ext.rs
index f345762dad9c8166500ea0d0872e61847fcdc84b..98655a6ea470212ec460686517044b1d2c1e6c02 100644
--- a/bridges/modules/parachains/src/weights_ext.rs
+++ b/bridges/modules/parachains/src/weights_ext.rs
@@ -68,6 +68,17 @@ pub trait WeightInfoExt: WeightInfo {
 		base_weight.saturating_add(proof_size_overhead).saturating_add(pruning_weight)
 	}
 
+	/// Returns weight of single parachain head storage update.
+	///
+	/// This weight only includes db write operations that happens if parachain head is actually
+	/// updated. All extra weights (weight of storage proof validation, additional checks, ...) is
+	/// not included.
+	fn parachain_head_storage_write_weight(db_weight: RuntimeDbWeight) -> Weight {
+		// it's just a couple of operations - we need to write the hash (`ImportedParaHashes`) and
+		// the head itself (`ImportedParaHeads`. Pruning is not included here
+		db_weight.writes(2)
+	}
+
 	/// Returns weight of single parachain head pruning.
 	fn parachain_head_pruning_weight(db_weight: RuntimeDbWeight) -> Weight {
 		// it's just one write operation, we don't want any benchmarks for that