diff --git a/bridges/relays/lib-substrate-relay/src/finality/mod.rs b/bridges/relays/lib-substrate-relay/src/finality/mod.rs
index b8cf27ea78fde9704820a30373dc80e6d4e317d5..ea7c4e0362b8583204e508a80036bb9445eac538 100644
--- a/bridges/relays/lib-substrate-relay/src/finality/mod.rs
+++ b/bridges/relays/lib-substrate-relay/src/finality/mod.rs
@@ -24,7 +24,7 @@ use crate::{
 };
 
 use async_trait::async_trait;
-use bp_header_chain::justification::GrandpaJustification;
+use bp_header_chain::justification::{GrandpaJustification, JustificationVerificationContext};
 use finality_relay::{FinalityPipeline, FinalitySyncPipeline};
 use pallet_bridge_grandpa::{Call as BridgeGrandpaCall, Config as BridgeGrandpaConfig};
 use relay_substrate_client::{
@@ -110,11 +110,12 @@ impl<P: SubstrateFinalitySyncPipeline> FinalitySyncPipeline for FinalitySyncPipe
 
 /// Different ways of building `submit_finality_proof` calls.
 pub trait SubmitFinalityProofCallBuilder<P: SubstrateFinalitySyncPipeline> {
-	/// Given source chain header and its finality proofs, build call of `submit_finality_proof`
-	/// function of bridge GRANDPA module at the target chain.
+	/// Given source chain header, its finality proof and the current authority set id, build call
+	/// of `submit_finality_proof` function of bridge GRANDPA module at the target chain.
 	fn build_submit_finality_proof_call(
 		header: SyncHeader<HeaderOf<P::SourceChain>>,
 		proof: SubstrateFinalityProof<P>,
+		context: <<P as SubstrateFinalityPipeline>::FinalityEngine as Engine<P::SourceChain>>::FinalityVerificationContext,
 	) -> CallOf<P::TargetChain>;
 }
 
@@ -132,12 +133,16 @@ where
 	I: 'static,
 	R::BridgedChain: bp_runtime::Chain<Header = HeaderOf<P::SourceChain>>,
 	CallOf<P::TargetChain>: From<BridgeGrandpaCall<R, I>>,
-	P::FinalityEngine:
-		Engine<P::SourceChain, FinalityProof = GrandpaJustification<HeaderOf<P::SourceChain>>>,
+	P::FinalityEngine: Engine<
+		P::SourceChain,
+		FinalityProof = GrandpaJustification<HeaderOf<P::SourceChain>>,
+		FinalityVerificationContext = JustificationVerificationContext,
+	>,
 {
 	fn build_submit_finality_proof_call(
 		header: SyncHeader<HeaderOf<P::SourceChain>>,
 		proof: GrandpaJustification<HeaderOf<P::SourceChain>>,
+		_context: JustificationVerificationContext,
 	) -> CallOf<P::TargetChain> {
 		BridgeGrandpaCall::<R, I>::submit_finality_proof {
 			finality_target: Box::new(header.into_inner()),
@@ -171,6 +176,7 @@ macro_rules! generate_submit_finality_proof_call_builder {
 						<$pipeline as $crate::finality_base::SubstrateFinalityPipeline>::SourceChain
 					>
 				>,
+				_context: bp_header_chain::justification::JustificationVerificationContext,
 			) -> relay_substrate_client::CallOf<
 				<$pipeline as $crate::finality_base::SubstrateFinalityPipeline>::TargetChain
 			> {
diff --git a/bridges/relays/lib-substrate-relay/src/finality/target.rs b/bridges/relays/lib-substrate-relay/src/finality/target.rs
index 930f0360311c37e08955b5cfece2b62e1a3f2f6b..18464d523f4f6cf4c9c165af82f6b7c2c2504070 100644
--- a/bridges/relays/lib-substrate-relay/src/finality/target.rs
+++ b/bridges/relays/lib-substrate-relay/src/finality/target.rs
@@ -108,13 +108,15 @@ impl<P: SubstrateFinalitySyncPipeline> TargetClient<FinalitySyncPipelineAdapter<
 		header: SyncHeader<HeaderOf<P::SourceChain>>,
 		mut proof: SubstrateFinalityProof<P>,
 	) -> Result<Self::TransactionTracker, Error> {
-		// runtime module at target chain may require optimized finality proof
-		P::FinalityEngine::optimize_proof(&self.client, &header, &mut proof).await?;
+		// verify and runtime module at target chain may require optimized finality proof
+		let context =
+			P::FinalityEngine::verify_and_optimize_proof(&self.client, &header, &mut proof).await?;
 
 		// now we may submit optimized finality proof
 		let mortality = self.transaction_params.mortality;
-		let call =
-			P::SubmitFinalityProofCallBuilder::build_submit_finality_proof_call(header, proof);
+		let call = P::SubmitFinalityProofCallBuilder::build_submit_finality_proof_call(
+			header, proof, context,
+		);
 		self.client
 			.submit_and_watch_signed_extrinsic(
 				&self.transaction_params.signer,
diff --git a/bridges/relays/lib-substrate-relay/src/finality_base/engine.rs b/bridges/relays/lib-substrate-relay/src/finality_base/engine.rs
index 831c1e7ad5b50009f167f49827f8418cd2ba8d3f..e517b0fd9b9abd50d6445e7222ef24ed946554bf 100644
--- a/bridges/relays/lib-substrate-relay/src/finality_base/engine.rs
+++ b/bridges/relays/lib-substrate-relay/src/finality_base/engine.rs
@@ -118,12 +118,15 @@ pub trait Engine<C: Chain>: Send {
 		source_client.subscribe_finality_justifications::<Self::FinalityClient>().await
 	}
 
-	/// Optimize finality proof before sending it to the target node.
-	async fn optimize_proof<TargetChain: Chain>(
+	/// Verify and optimize finality proof before sending it to the target node.
+	///
+	/// Apart from optimization, we expect this method to perform all required checks
+	/// that the `header` and `proof` are valid at the current state of the target chain.
+	async fn verify_and_optimize_proof<TargetChain: Chain>(
 		target_client: &Client<TargetChain>,
 		header: &C::Header,
 		proof: &mut Self::FinalityProof,
-	) -> Result<(), SubstrateError>;
+	) -> Result<Self::FinalityVerificationContext, SubstrateError>;
 
 	/// Checks whether the given `header` and its finality `proof` fit the maximal expected
 	/// call size limit. If result is `MaxExpectedCallSizeCheck::Exceeds { .. }`, this
@@ -212,11 +215,11 @@ impl<C: ChainWithGrandpa> Engine<C> for Grandpa<C> {
 		bp_header_chain::storage_keys::pallet_operating_mode_key(C::WITH_CHAIN_GRANDPA_PALLET_NAME)
 	}
 
-	async fn optimize_proof<TargetChain: Chain>(
+	async fn verify_and_optimize_proof<TargetChain: Chain>(
 		target_client: &Client<TargetChain>,
 		header: &C::Header,
 		proof: &mut Self::FinalityProof,
-	) -> Result<(), SubstrateError> {
+	) -> Result<Self::FinalityVerificationContext, SubstrateError> {
 		let verification_context = Grandpa::<C>::finality_verification_context(
 			target_client,
 			target_client.best_header().await?.hash(),
@@ -231,6 +234,7 @@ impl<C: ChainWithGrandpa> Engine<C> for Grandpa<C> {
 			&verification_context,
 			proof,
 		)
+		.map(|_| verification_context)
 		.map_err(|e| {
 			SubstrateError::Custom(format!(
 				"Failed to optimize {} GRANDPA jutification for header {:?}: {:?}",
diff --git a/bridges/relays/lib-substrate-relay/src/on_demand/headers.rs b/bridges/relays/lib-substrate-relay/src/on_demand/headers.rs
index 0090dee3a03c8119a45fe2b1086303f02a08fc8b..8b58552d292c4c56c1c6fbd6fb0d9334bbcdeb3f 100644
--- a/bridges/relays/lib-substrate-relay/src/on_demand/headers.rs
+++ b/bridges/relays/lib-substrate-relay/src/on_demand/headers.rs
@@ -146,8 +146,13 @@ impl<P: SubstrateFinalitySyncPipeline> OnDemandRelay<P::SourceChain, P::TargetCh
 				finality_source.prove_block_finality(current_required_header).await?;
 			let header_id = header.id();
 
-			// optimize justification before including it into the call
-			P::FinalityEngine::optimize_proof(&self.target_client, &header, &mut proof).await?;
+			// verify and optimize justification before including it into the call
+			let context = P::FinalityEngine::verify_and_optimize_proof(
+				&self.target_client,
+				&header,
+				&mut proof,
+			)
+			.await?;
 
 			// now we have the header and its proof, but we want to minimize our losses, so let's
 			// check if we'll get the full refund for submitting this header
@@ -185,8 +190,9 @@ impl<P: SubstrateFinalitySyncPipeline> OnDemandRelay<P::SourceChain, P::TargetCh
 			);
 
 			// and then craft the submit-proof call
-			let call =
-				P::SubmitFinalityProofCallBuilder::build_submit_finality_proof_call(header, proof);
+			let call = P::SubmitFinalityProofCallBuilder::build_submit_finality_proof_call(
+				header, proof, context,
+			);
 
 			return Ok((header_id, vec![call]));
 		}