diff --git a/bridges/bin/millau/runtime/src/lib.rs b/bridges/bin/millau/runtime/src/lib.rs
index 8f82e645f9e028ccb3e7235ff23a8057ec30c3b3..5a18f8b12c039b701589930f5c030aefb1a3835c 100644
--- a/bridges/bin/millau/runtime/src/lib.rs
+++ b/bridges/bin/millau/runtime/src/lib.rs
@@ -387,21 +387,6 @@ impl pallet_bridge_relayers::Config for Runtime {
 	type WeightInfo = ();
 }
 
-parameter_types! {
-	/// Number of headers to keep.
-	///
-	/// Assuming the worst case of every header being finalized, we will keep headers at least for a
-	/// day.
-	pub const HeadersToKeep: u32 = bp_rialto::DAYS;
-	/// Maximal number of authorities at Rialto.
-	pub const MaxAuthoritiesAtRialto: u32 = bp_rialto::MAX_AUTHORITIES_COUNT;
-}
-
-parameter_types! {
-	/// Maximal number of authorities at Westend.
-	pub const MaxAuthoritiesAtWestend: u32 = bp_westend::MAX_AUTHORITIES_COUNT;
-}
-
 pub type RialtoGrandpaInstance = ();
 impl pallet_bridge_grandpa::Config for Runtime {
 	type BridgedChain = bp_rialto::Rialto;
@@ -410,9 +395,7 @@ impl pallet_bridge_grandpa::Config for Runtime {
 	// Note that once this is hit the pallet will essentially throttle incoming requests down to one
 	// call per block.
 	type MaxRequests = ConstU32<50>;
-	type HeadersToKeep = HeadersToKeep;
-	type MaxBridgedAuthorities = MaxAuthoritiesAtRialto;
-
+	type HeadersToKeep = ConstU32<{ bp_rialto::DAYS }>;
 	type WeightInfo = pallet_bridge_grandpa::weights::BridgeWeight<Runtime>;
 }
 
@@ -420,9 +403,7 @@ pub type WestendGrandpaInstance = pallet_bridge_grandpa::Instance1;
 impl pallet_bridge_grandpa::Config<WestendGrandpaInstance> for Runtime {
 	type BridgedChain = bp_westend::Westend;
 	type MaxRequests = ConstU32<50>;
-	type HeadersToKeep = HeadersToKeep;
-	type MaxBridgedAuthorities = MaxAuthoritiesAtWestend;
-
+	type HeadersToKeep = ConstU32<{ bp_westend::DAYS }>;
 	type WeightInfo = pallet_bridge_grandpa::weights::BridgeWeight<Runtime>;
 }
 
@@ -521,7 +502,7 @@ impl pallet_bridge_parachains::Config<WithRialtoParachainsInstance> for Runtime
 	type ParasPalletName = RialtoParasPalletName;
 	type ParaStoredHeaderDataBuilder =
 		SingleParaStoredHeaderDataBuilder<bp_rialto_parachain::RialtoParachain>;
-	type HeadsToKeep = HeadersToKeep;
+	type HeadsToKeep = ConstU32<1024>;
 	type MaxParaHeadDataSize = MaxRialtoParaHeadDataSize;
 }
 
@@ -534,7 +515,7 @@ impl pallet_bridge_parachains::Config<WithWestendParachainsInstance> for Runtime
 	type BridgesGrandpaPalletInstance = WestendGrandpaInstance;
 	type ParasPalletName = WestendParasPalletName;
 	type ParaStoredHeaderDataBuilder = SingleParaStoredHeaderDataBuilder<bp_westend::Westmint>;
-	type HeadsToKeep = HeadersToKeep;
+	type HeadsToKeep = ConstU32<1024>;
 	type MaxParaHeadDataSize = MaxWestendParaHeadDataSize;
 }
 
diff --git a/bridges/bin/rialto-parachain/runtime/src/lib.rs b/bridges/bin/rialto-parachain/runtime/src/lib.rs
index ba1aa05463b1ca213fde9be4e6cf8311b4ed48a7..5eeddf41e52471f71f0b53337db0ab4b9b8d1eb8 100644
--- a/bridges/bin/rialto-parachain/runtime/src/lib.rs
+++ b/bridges/bin/rialto-parachain/runtime/src/lib.rs
@@ -525,17 +525,6 @@ impl pallet_bridge_relayers::Config for Runtime {
 	type WeightInfo = ();
 }
 
-parameter_types! {
-	/// Number of headers to keep.
-	///
-	/// Assuming the worst case of every header being finalized, we will keep headers at least for a
-	/// day.
-	pub const HeadersToKeep: u32 = bp_millau::DAYS as u32;
-
-	/// Maximal number of authorities at Millau.
-	pub const MaxAuthoritiesAtMillau: u32 = bp_millau::MAX_AUTHORITIES_COUNT;
-}
-
 pub type MillauGrandpaInstance = ();
 impl pallet_bridge_grandpa::Config for Runtime {
 	type BridgedChain = bp_millau::Millau;
@@ -544,8 +533,7 @@ impl pallet_bridge_grandpa::Config for Runtime {
 	/// Note that once this is hit the pallet will essentially throttle incoming requests down to
 	/// one call per block.
 	type MaxRequests = ConstU32<50>;
-	type HeadersToKeep = HeadersToKeep;
-	type MaxBridgedAuthorities = MaxAuthoritiesAtMillau;
+	type HeadersToKeep = ConstU32<{ bp_millau::DAYS as u32 }>;
 	type WeightInfo = pallet_bridge_grandpa::weights::BridgeWeight<Runtime>;
 }
 
diff --git a/bridges/bin/rialto/runtime/src/lib.rs b/bridges/bin/rialto/runtime/src/lib.rs
index cb3b6d604866b76b93732a8a51fa97fc11e136eb..3959dbea1aae3913664af02ee2c8431d97e4556d 100644
--- a/bridges/bin/rialto/runtime/src/lib.rs
+++ b/bridges/bin/rialto/runtime/src/lib.rs
@@ -397,17 +397,6 @@ impl pallet_bridge_relayers::Config for Runtime {
 	type WeightInfo = ();
 }
 
-parameter_types! {
-	/// Number of headers to keep.
-	///
-	/// Assuming the worst case of every header being finalized, we will keep headers at least for a
-	/// day.
-	pub const HeadersToKeep: u32 = bp_rialto::DAYS;
-
-	/// Maximal number of authorities at Millau.
-	pub const MaxAuthoritiesAtMillau: u32 = bp_millau::MAX_AUTHORITIES_COUNT;
-}
-
 pub type MillauGrandpaInstance = ();
 impl pallet_bridge_grandpa::Config for Runtime {
 	type BridgedChain = bp_millau::Millau;
@@ -416,8 +405,7 @@ impl pallet_bridge_grandpa::Config for Runtime {
 	/// Note that once this is hit the pallet will essentially throttle incoming requests down to
 	/// one call per block.
 	type MaxRequests = ConstU32<50>;
-	type HeadersToKeep = HeadersToKeep;
-	type MaxBridgedAuthorities = MaxAuthoritiesAtMillau;
+	type HeadersToKeep = ConstU32<{ bp_millau::DAYS as u32 }>;
 	type WeightInfo = pallet_bridge_grandpa::weights::BridgeWeight<Runtime>;
 }
 
diff --git a/bridges/bin/runtime-common/src/mock.rs b/bridges/bin/runtime-common/src/mock.rs
index 51b03447c63833303b24885ca7aab8fd6563c21e..a5ae9131f031f822dd18108b22de7c2dda3ccf65 100644
--- a/bridges/bin/runtime-common/src/mock.rs
+++ b/bridges/bin/runtime-common/src/mock.rs
@@ -32,7 +32,7 @@ use crate::messages::{
 	BridgedChainWithMessages, HashOf, MessageBridge, ThisChainWithMessages,
 };
 
-use bp_header_chain::HeaderChain;
+use bp_header_chain::{ChainWithGrandpa, HeaderChain};
 use bp_messages::{target_chain::ForbidInboundMessages, LaneId, MessageNonce};
 use bp_parachains::SingleParaStoredHeaderDataBuilder;
 use bp_runtime::{Chain, ChainId, Parachain, UnderlyingChainProvider};
@@ -195,7 +195,6 @@ impl pallet_bridge_grandpa::Config for TestRuntime {
 	type BridgedChain = BridgedUnderlyingChain;
 	type MaxRequests = ConstU32<50>;
 	type HeadersToKeep = ConstU32<8>;
-	type MaxBridgedAuthorities = ConstU32<1024>;
 	type WeightInfo = pallet_bridge_grandpa::weights::BridgeWeight<TestRuntime>;
 }
 
@@ -372,6 +371,14 @@ impl Chain for BridgedUnderlyingChain {
 	}
 }
 
+impl ChainWithGrandpa for BridgedUnderlyingChain {
+	const WITH_CHAIN_GRANDPA_PALLET_NAME: &'static str = "";
+	const MAX_AUTHORITIES_COUNT: u32 = 16;
+	const REASONABLE_HEADERS_IN_JUSTIFICATON_ANCESTRY: u32 = 8;
+	const MAX_HEADER_SIZE: u32 = 256;
+	const AVERAGE_HEADER_SIZE_IN_JUSTIFICATION: u32 = 64;
+}
+
 impl Chain for BridgedUnderlyingParachain {
 	type BlockNumber = BridgedChainBlockNumber;
 	type Hash = BridgedChainHash;
diff --git a/bridges/modules/grandpa/src/benchmarking.rs b/bridges/modules/grandpa/src/benchmarking.rs
index 710a7e0952c611695b45c3dc07c891942f601b96..337943bee4edcd9a45973a018bfd52fd8aa6858b 100644
--- a/bridges/modules/grandpa/src/benchmarking.rs
+++ b/bridges/modules/grandpa/src/benchmarking.rs
@@ -47,7 +47,6 @@ use bp_test_utils::{
 	TEST_GRANDPA_SET_ID,
 };
 use frame_benchmarking::{benchmarks_instance_pallet, whitelisted_caller};
-use frame_support::traits::Get;
 use frame_system::RawOrigin;
 use sp_finality_grandpa::AuthorityId;
 use sp_runtime::traits::{One, Zero};
@@ -68,7 +67,7 @@ const MAX_VOTE_ANCESTRIES_RANGE_END: u32 =
 
 // the same with validators - if there are too much validators, let's run benchmarks on subrange
 fn validator_set_range_end<T: Config<I>, I: 'static>() -> u32 {
-	let max_bridged_authorities = T::MaxBridgedAuthorities::get();
+	let max_bridged_authorities = T::BridgedChain::MAX_AUTHORITIES_COUNT;
 	if max_bridged_authorities > 128 {
 		sp_std::cmp::max(128, max_bridged_authorities / 5)
 	} else {
diff --git a/bridges/modules/grandpa/src/lib.rs b/bridges/modules/grandpa/src/lib.rs
index fca0cb1b758c754816bc95080dd3bd7ccd02aff6..197726dd47a5ec15da463a7ec838ad28174c747b 100644
--- a/bridges/modules/grandpa/src/lib.rs
+++ b/bridges/modules/grandpa/src/lib.rs
@@ -39,10 +39,10 @@
 use storage_types::StoredAuthoritySet;
 
 use bp_header_chain::{
-	justification::GrandpaJustification, HeaderChain, InitializationData, StoredHeaderData,
-	StoredHeaderDataBuilder,
+	justification::GrandpaJustification, ChainWithGrandpa, HeaderChain, InitializationData,
+	StoredHeaderData, StoredHeaderDataBuilder,
 };
-use bp_runtime::{BlockNumberOf, Chain, HashOf, HasherOf, HeaderId, HeaderOf, OwnedBridgeModule};
+use bp_runtime::{BlockNumberOf, HashOf, HasherOf, HeaderId, HeaderOf, OwnedBridgeModule};
 use finality_grandpa::voter_set::VoterSet;
 use frame_support::{dispatch::PostDispatchInfo, ensure};
 use sp_finality_grandpa::{ConsensusLog, GRANDPA_ENGINE_ID};
@@ -97,7 +97,7 @@ pub mod pallet {
 	#[pallet::config]
 	pub trait Config<I: 'static = ()>: frame_system::Config {
 		/// The chain we are bridging to here.
-		type BridgedChain: Chain;
+		type BridgedChain: ChainWithGrandpa;
 
 		/// The upper bound on the number of requests allowed by the pallet.
 		///
@@ -118,10 +118,6 @@ pub mod pallet {
 		#[pallet::constant]
 		type HeadersToKeep: Get<u32>;
 
-		/// Max number of authorities at the bridged chain.
-		#[pallet::constant]
-		type MaxBridgedAuthorities: Get<u32>;
-
 		/// Weights gathered through benchmarking.
 		type WeightInfo: WeightInfo;
 	}
@@ -513,7 +509,7 @@ pub mod pallet {
 					target: LOG_TARGET,
 					"Failed to initialize bridge. Number of authorities in the set {} is larger than the configured value {}",
 					authority_set_length,
-					T::MaxBridgedAuthorities::get(),
+					T::BridgedChain::MAX_AUTHORITIES_COUNT,
 				);
 
 				Error::TooManyAuthoritiesInSet
diff --git a/bridges/modules/grandpa/src/mock.rs b/bridges/modules/grandpa/src/mock.rs
index 8757093ee682eed3f6cf3e3da90eb9e8b0afa89d..f0ae2583529d56d06b0ba3eeff8ef4bfb3ac5efc 100644
--- a/bridges/modules/grandpa/src/mock.rs
+++ b/bridges/modules/grandpa/src/mock.rs
@@ -17,8 +17,13 @@
 // From construct_runtime macro
 #![allow(clippy::from_over_into)]
 
+use bp_header_chain::ChainWithGrandpa;
 use bp_runtime::Chain;
-use frame_support::{construct_runtime, parameter_types, traits::ConstU64, weights::Weight};
+use frame_support::{
+	construct_runtime, parameter_types,
+	traits::{ConstU32, ConstU64},
+	weights::Weight,
+};
 use sp_core::sr25519::Signature;
 use sp_runtime::{
 	testing::{Header, H256},
@@ -78,7 +83,7 @@ impl frame_system::Config for TestRuntime {
 	type BlockLength = ();
 	type SS58Prefix = ();
 	type OnSetCode = ();
-	type MaxConsumers = frame_support::traits::ConstU32<16>;
+	type MaxConsumers = ConstU32<16>;
 }
 
 parameter_types! {
@@ -92,7 +97,6 @@ impl grandpa::Config for TestRuntime {
 	type BridgedChain = TestBridgedChain;
 	type MaxRequests = MaxRequests;
 	type HeadersToKeep = HeadersToKeep;
-	type MaxBridgedAuthorities = frame_support::traits::ConstU32<MAX_BRIDGED_AUTHORITIES>;
 	type WeightInfo = ();
 }
 
@@ -118,6 +122,14 @@ impl Chain for TestBridgedChain {
 	}
 }
 
+impl ChainWithGrandpa for TestBridgedChain {
+	const WITH_CHAIN_GRANDPA_PALLET_NAME: &'static str = "";
+	const MAX_AUTHORITIES_COUNT: u32 = MAX_BRIDGED_AUTHORITIES;
+	const REASONABLE_HEADERS_IN_JUSTIFICATON_ANCESTRY: u32 = 8;
+	const MAX_HEADER_SIZE: u32 = 256;
+	const AVERAGE_HEADER_SIZE_IN_JUSTIFICATION: u32 = 64;
+}
+
 pub fn run_test<T>(test: impl FnOnce() -> T) -> T {
 	sp_io::TestExternalities::new(Default::default()).execute_with(test)
 }
diff --git a/bridges/modules/grandpa/src/storage_types.rs b/bridges/modules/grandpa/src/storage_types.rs
index 732e72e2c0d6220f9d800881db0ce54b39ade234..918c131289c5bb7a6c98613c5ecc61099e9f9788 100644
--- a/bridges/modules/grandpa/src/storage_types.rs
+++ b/bridges/modules/grandpa/src/storage_types.rs
@@ -18,22 +18,32 @@
 
 use crate::Config;
 
-use bp_header_chain::AuthoritySet;
+use bp_header_chain::{AuthoritySet, ChainWithGrandpa};
 use codec::{Decode, Encode, MaxEncodedLen};
 use frame_support::{traits::Get, BoundedVec, RuntimeDebugNoBound};
 use scale_info::TypeInfo;
 use sp_finality_grandpa::{AuthorityId, AuthorityList, AuthorityWeight, SetId};
+use sp_std::marker::PhantomData;
 
 /// A bounded list of Grandpa authorities with associated weights.
 pub type StoredAuthorityList<MaxBridgedAuthorities> =
 	BoundedVec<(AuthorityId, AuthorityWeight), MaxBridgedAuthorities>;
 
+/// Adapter for using `T::BridgedChain::MAX_BRIDGED_AUTHORITIES` in `BoundedVec`.
+pub struct StoredAuthorityListLimit<T, I>(PhantomData<(T, I)>);
+
+impl<T: Config<I>, I: 'static> Get<u32> for StoredAuthorityListLimit<T, I> {
+	fn get() -> u32 {
+		T::BridgedChain::MAX_AUTHORITIES_COUNT
+	}
+}
+
 /// A bounded GRANDPA Authority List and ID.
 #[derive(Clone, Decode, Encode, Eq, TypeInfo, MaxEncodedLen, RuntimeDebugNoBound)]
 #[scale_info(skip_type_params(T, I))]
 pub struct StoredAuthoritySet<T: Config<I>, I: 'static> {
 	/// List of GRANDPA authorities for the current round.
-	pub authorities: StoredAuthorityList<<T as Config<I>>::MaxBridgedAuthorities>,
+	pub authorities: StoredAuthorityList<StoredAuthorityListLimit<T, I>>,
 	/// Monotonic identifier of the current GRANDPA authority set.
 	pub set_id: SetId,
 }
@@ -60,7 +70,7 @@ impl<T: Config<I>, I: 'static> StoredAuthoritySet<T, I> {
 		let single_authority_max_encoded_len =
 			<(AuthorityId, AuthorityWeight)>::max_encoded_len() as u64;
 		let extra_authorities =
-			T::MaxBridgedAuthorities::get().saturating_sub(self.authorities.len() as _);
+			T::BridgedChain::MAX_AUTHORITIES_COUNT.saturating_sub(self.authorities.len() as _);
 		single_authority_max_encoded_len.saturating_mul(extra_authorities as u64)
 	}
 }
diff --git a/bridges/modules/parachains/src/mock.rs b/bridges/modules/parachains/src/mock.rs
index 8248964263c06f45bfa3fc202313297e55420aef..0e8261f689915dd06c62f4add1d82c97a4aeef67 100644
--- a/bridges/modules/parachains/src/mock.rs
+++ b/bridges/modules/parachains/src/mock.rs
@@ -14,6 +14,7 @@
 // You should have received a copy of the GNU General Public License
 // along with Parity Bridges Common.  If not, see <http://www.gnu.org/licenses/>.
 
+use bp_header_chain::ChainWithGrandpa;
 use bp_polkadot_core::parachains::ParaId;
 use bp_runtime::{Chain, Parachain};
 use frame_support::{construct_runtime, parameter_types, traits::ConstU32, weights::Weight};
@@ -199,7 +200,6 @@ impl pallet_bridge_grandpa::Config<pallet_bridge_grandpa::Instance1> for TestRun
 	type BridgedChain = TestBridgedChain;
 	type MaxRequests = ConstU32<2>;
 	type HeadersToKeep = HeadersToKeep;
-	type MaxBridgedAuthorities = frame_support::traits::ConstU32<5>;
 	type WeightInfo = ();
 }
 
@@ -207,7 +207,6 @@ impl pallet_bridge_grandpa::Config<pallet_bridge_grandpa::Instance2> for TestRun
 	type BridgedChain = TestBridgedChain;
 	type MaxRequests = ConstU32<2>;
 	type HeadersToKeep = HeadersToKeep;
-	type MaxBridgedAuthorities = frame_support::traits::ConstU32<5>;
 	type WeightInfo = ();
 }
 
@@ -250,6 +249,14 @@ impl Chain for TestBridgedChain {
 	}
 }
 
+impl ChainWithGrandpa for TestBridgedChain {
+	const WITH_CHAIN_GRANDPA_PALLET_NAME: &'static str = "";
+	const MAX_AUTHORITIES_COUNT: u32 = 16;
+	const REASONABLE_HEADERS_IN_JUSTIFICATON_ANCESTRY: u32 = 8;
+	const MAX_HEADER_SIZE: u32 = 256;
+	const AVERAGE_HEADER_SIZE_IN_JUSTIFICATION: u32 = 64;
+}
+
 #[derive(Debug)]
 pub struct OtherBridgedChain;
 
@@ -273,6 +280,14 @@ impl Chain for OtherBridgedChain {
 	}
 }
 
+impl ChainWithGrandpa for OtherBridgedChain {
+	const WITH_CHAIN_GRANDPA_PALLET_NAME: &'static str = "";
+	const MAX_AUTHORITIES_COUNT: u32 = 16;
+	const REASONABLE_HEADERS_IN_JUSTIFICATON_ANCESTRY: u32 = 8;
+	const MAX_HEADER_SIZE: u32 = 256;
+	const AVERAGE_HEADER_SIZE_IN_JUSTIFICATION: u32 = 64;
+}
+
 pub fn run_test<T>(test: impl FnOnce() -> T) -> T {
 	sp_io::TestExternalities::new(Default::default()).execute_with(|| {
 		System::set_block_number(1);
diff --git a/bridges/primitives/chain-kusama/Cargo.toml b/bridges/primitives/chain-kusama/Cargo.toml
index 3bca5f4c3f0772f99a0e3fe450e413554efccd47..7f48ded1a37e603b69dc67a7deb4856e2e7e9ca1 100644
--- a/bridges/primitives/chain-kusama/Cargo.toml
+++ b/bridges/primitives/chain-kusama/Cargo.toml
@@ -10,17 +10,21 @@ license = "GPL-3.0-or-later WITH Classpath-exception-2.0"
 
 # Bridge Dependencies
 
+bp-header-chain = { path = "../header-chain", default-features = false }
 bp-polkadot-core = { path = "../polkadot-core", default-features = false }
 bp-runtime = { path = "../runtime", default-features = false }
 
 # Substrate Based Dependencies
 
+frame-support = { git = "https://github.com/paritytech/substrate", branch = "master", default-features = false }
 sp-api = { git = "https://github.com/paritytech/substrate", branch = "master", default-features = false }
 
 [features]
 default = ["std"]
 std = [
+	"bp-header-chain/std",
 	"bp-polkadot-core/std",
 	"bp-runtime/std",
+	"frame-support/std",
 	"sp-api/std",
 ]
diff --git a/bridges/primitives/chain-kusama/src/lib.rs b/bridges/primitives/chain-kusama/src/lib.rs
index 27d5f125ee2666b30fe526ac364bede89867bee0..9ada688bf11b5b9c0eff2fd61950db0185e06805 100644
--- a/bridges/primitives/chain-kusama/src/lib.rs
+++ b/bridges/primitives/chain-kusama/src/lib.rs
@@ -19,10 +19,42 @@
 #![allow(clippy::too_many_arguments)]
 
 pub use bp_polkadot_core::*;
-use bp_runtime::decl_bridge_finality_runtime_apis;
+
+use bp_header_chain::ChainWithGrandpa;
+use bp_runtime::{decl_bridge_finality_runtime_apis, Chain};
+use frame_support::weights::Weight;
 
 /// Kusama Chain
-pub type Kusama = PolkadotLike;
+pub struct Kusama;
+
+impl Chain for Kusama {
+	type BlockNumber = <PolkadotLike as Chain>::BlockNumber;
+	type Hash = <PolkadotLike as Chain>::Hash;
+	type Hasher = <PolkadotLike as Chain>::Hasher;
+	type Header = <PolkadotLike as Chain>::Header;
+
+	type AccountId = <PolkadotLike as Chain>::AccountId;
+	type Balance = <PolkadotLike as Chain>::Balance;
+	type Index = <PolkadotLike as Chain>::Index;
+	type Signature = <PolkadotLike as Chain>::Signature;
+
+	fn max_extrinsic_size() -> u32 {
+		PolkadotLike::max_extrinsic_size()
+	}
+
+	fn max_extrinsic_weight() -> Weight {
+		PolkadotLike::max_extrinsic_weight()
+	}
+}
+
+impl ChainWithGrandpa for Kusama {
+	const WITH_CHAIN_GRANDPA_PALLET_NAME: &'static str = WITH_KUSAMA_GRANDPA_PALLET_NAME;
+	const MAX_AUTHORITIES_COUNT: u32 = MAX_AUTHORITIES_COUNT;
+	const REASONABLE_HEADERS_IN_JUSTIFICATON_ANCESTRY: u32 =
+		REASONABLE_HEADERS_IN_JUSTIFICATON_ANCESTRY;
+	const MAX_HEADER_SIZE: u32 = MAX_HEADER_SIZE;
+	const AVERAGE_HEADER_SIZE_IN_JUSTIFICATION: u32 = AVERAGE_HEADER_SIZE_IN_JUSTIFICATION;
+}
 
 /// Name of the With-Kusama GRANDPA pallet instance that is deployed at bridged chains.
 pub const WITH_KUSAMA_GRANDPA_PALLET_NAME: &str = "BridgeKusamaGrandpa";
diff --git a/bridges/primitives/chain-millau/Cargo.toml b/bridges/primitives/chain-millau/Cargo.toml
index 7600781d891690e24e5930dde61153af7483392d..00d5a02d47c49b3dbd3cd772974c433a3dded951 100644
--- a/bridges/primitives/chain-millau/Cargo.toml
+++ b/bridges/primitives/chain-millau/Cargo.toml
@@ -8,11 +8,6 @@ license = "GPL-3.0-or-later WITH Classpath-exception-2.0"
 
 [dependencies]
 
-# Bridge Dependencies
-
-bp-beefy = { path = "../beefy", default-features = false }
-bp-messages = { path = "../messages", default-features = false }
-bp-runtime = { path = "../runtime", default-features = false }
 fixed-hash = { version = "0.8.0", default-features = false }
 hash256-std-hasher = { version = "0.15.2", default-features = false }
 impl-codec = { version = "0.6", default-features = false }
@@ -21,6 +16,13 @@ parity-util-mem = { version = "0.12.0", default-features = false, features = ["p
 scale-info = { version = "2.1.1", default-features = false, features = ["derive"] }
 serde = { version = "1.0", optional = true, features = ["derive"] }
 
+# Bridge Dependencies
+
+bp-beefy = { path = "../beefy", default-features = false }
+bp-header-chain = { path = "../header-chain", default-features = false }
+bp-messages = { path = "../messages", default-features = false }
+bp-runtime = { path = "../runtime", default-features = false }
+
 # Substrate Based Dependencies
 
 frame-support = { git = "https://github.com/paritytech/substrate", branch = "master", default-features = false }
@@ -36,6 +38,7 @@ sp-trie = { git = "https://github.com/paritytech/substrate", branch = "master",
 default = ["std"]
 std = [
 	"bp-beefy/std",
+	"bp-header-chain/std",
 	"bp-messages/std",
 	"bp-runtime/std",
 	"fixed-hash/std",
diff --git a/bridges/primitives/chain-millau/src/lib.rs b/bridges/primitives/chain-millau/src/lib.rs
index 73515a7f4097ba315bd2f1ff759844ca07eef2b5..81ed54362631b8bb380c12d2c0e43b5f7fcf5962 100644
--- a/bridges/primitives/chain-millau/src/lib.rs
+++ b/bridges/primitives/chain-millau/src/lib.rs
@@ -21,6 +21,7 @@
 mod millau_hash;
 
 use bp_beefy::ChainWithBeefy;
+use bp_header_chain::ChainWithGrandpa;
 use bp_messages::{
 	InboundMessageDetails, LaneId, MessageNonce, MessagePayload, OutboundMessageDetails,
 };
@@ -83,6 +84,27 @@ pub const SESSION_LENGTH: BlockNumber = 5 * time_units::MINUTES;
 /// Maximal number of GRANDPA authorities at Millau.
 pub const MAX_AUTHORITIES_COUNT: u32 = 5;
 
+/// Reasonable number of headers in the `votes_ancestries` on Millau chain.
+///
+/// See [`bp_header_chain::ChainWithGrandpa`] for more details.
+pub const REASONABLE_HEADERS_IN_JUSTIFICATON_ANCESTRY: u32 = 8;
+
+/// Approximate average header size in `votes_ancestries` field of justification on Millau chain.
+///
+/// See [`bp_header_chain::ChainWithGrandpa`] for more details.
+pub const AVERAGE_HEADER_SIZE_IN_JUSTIFICATION: u32 = 256;
+
+/// Approximate maximal header size on Millau chain.
+///
+/// We expect maximal header to have digest item with the new authorities set for every consensus
+/// engine (GRANDPA, Babe, BEEFY, ...) - so we multiply it by 3. And also
+/// `AVERAGE_HEADER_SIZE_IN_JUSTIFICATION` bytes for other stuff.
+///
+/// See [`bp_header_chain::ChainWithGrandpa`] for more details.
+pub const MAX_HEADER_SIZE: u32 = MAX_AUTHORITIES_COUNT
+	.saturating_mul(3)
+	.saturating_add(AVERAGE_HEADER_SIZE_IN_JUSTIFICATION);
+
 /// Re-export `time_units` to make usage easier.
 pub use time_units::*;
 
@@ -156,6 +178,15 @@ impl Chain for Millau {
 	}
 }
 
+impl ChainWithGrandpa for Millau {
+	const WITH_CHAIN_GRANDPA_PALLET_NAME: &'static str = WITH_MILLAU_GRANDPA_PALLET_NAME;
+	const MAX_AUTHORITIES_COUNT: u32 = MAX_AUTHORITIES_COUNT;
+	const REASONABLE_HEADERS_IN_JUSTIFICATON_ANCESTRY: u32 =
+		REASONABLE_HEADERS_IN_JUSTIFICATON_ANCESTRY;
+	const MAX_HEADER_SIZE: u32 = MAX_HEADER_SIZE;
+	const AVERAGE_HEADER_SIZE_IN_JUSTIFICATION: u32 = AVERAGE_HEADER_SIZE_IN_JUSTIFICATION;
+}
+
 impl ChainWithBeefy for Millau {
 	type CommitmentHasher = Keccak256;
 	type MmrHashing = Keccak256;
diff --git a/bridges/primitives/chain-polkadot/Cargo.toml b/bridges/primitives/chain-polkadot/Cargo.toml
index b26093a05706e28c0089e9c3f12d2de7151dc6ce..def26bdda1c88041ce961ae2601b5a478810fdb8 100644
--- a/bridges/primitives/chain-polkadot/Cargo.toml
+++ b/bridges/primitives/chain-polkadot/Cargo.toml
@@ -7,19 +7,24 @@ edition = "2021"
 license = "GPL-3.0-or-later WITH Classpath-exception-2.0"
 
 [dependencies]
+
 # Bridge Dependencies
 
+bp-header-chain = { path = "../header-chain", default-features = false }
 bp-polkadot-core = { path = "../polkadot-core", default-features = false }
 bp-runtime = { path = "../runtime", default-features = false }
 
 # Substrate Based Dependencies
 
+frame-support = { git = "https://github.com/paritytech/substrate", branch = "master", default-features = false }
 sp-api = { git = "https://github.com/paritytech/substrate", branch = "master", default-features = false }
 
 [features]
 default = ["std"]
 std = [
+	"bp-header-chain/std",
 	"bp-polkadot-core/std",
 	"bp-runtime/std",
+	"frame-support/std",
 	"sp-api/std",
 ]
diff --git a/bridges/primitives/chain-polkadot/src/lib.rs b/bridges/primitives/chain-polkadot/src/lib.rs
index 0cada4e49a9be996f79a782cb7eb8938feaf8f01..e1600102fcd46bf2952172dfec56e5db9d561a17 100644
--- a/bridges/primitives/chain-polkadot/src/lib.rs
+++ b/bridges/primitives/chain-polkadot/src/lib.rs
@@ -19,10 +19,42 @@
 #![allow(clippy::too_many_arguments)]
 
 pub use bp_polkadot_core::*;
-use bp_runtime::decl_bridge_finality_runtime_apis;
+
+use bp_header_chain::ChainWithGrandpa;
+use bp_runtime::{decl_bridge_finality_runtime_apis, Chain};
+use frame_support::weights::Weight;
 
 /// Polkadot Chain
-pub type Polkadot = PolkadotLike;
+pub struct Polkadot;
+
+impl Chain for Polkadot {
+	type BlockNumber = <PolkadotLike as Chain>::BlockNumber;
+	type Hash = <PolkadotLike as Chain>::Hash;
+	type Hasher = <PolkadotLike as Chain>::Hasher;
+	type Header = <PolkadotLike as Chain>::Header;
+
+	type AccountId = <PolkadotLike as Chain>::AccountId;
+	type Balance = <PolkadotLike as Chain>::Balance;
+	type Index = <PolkadotLike as Chain>::Index;
+	type Signature = <PolkadotLike as Chain>::Signature;
+
+	fn max_extrinsic_size() -> u32 {
+		PolkadotLike::max_extrinsic_size()
+	}
+
+	fn max_extrinsic_weight() -> Weight {
+		PolkadotLike::max_extrinsic_weight()
+	}
+}
+
+impl ChainWithGrandpa for Polkadot {
+	const WITH_CHAIN_GRANDPA_PALLET_NAME: &'static str = WITH_POLKADOT_GRANDPA_PALLET_NAME;
+	const MAX_AUTHORITIES_COUNT: u32 = MAX_AUTHORITIES_COUNT;
+	const REASONABLE_HEADERS_IN_JUSTIFICATON_ANCESTRY: u32 =
+		REASONABLE_HEADERS_IN_JUSTIFICATON_ANCESTRY;
+	const MAX_HEADER_SIZE: u32 = MAX_HEADER_SIZE;
+	const AVERAGE_HEADER_SIZE_IN_JUSTIFICATION: u32 = AVERAGE_HEADER_SIZE_IN_JUSTIFICATION;
+}
 
 /// Name of the With-Polkadot GRANDPA pallet instance that is deployed at bridged chains.
 pub const WITH_POLKADOT_GRANDPA_PALLET_NAME: &str = "BridgePolkadotGrandpa";
diff --git a/bridges/primitives/chain-rialto/Cargo.toml b/bridges/primitives/chain-rialto/Cargo.toml
index 663f9076657dd5e4d8dad11b77eb0ad7d5acdfc5..0a70e0504c97ade2fbbf2ffdcb29a3fa3f2e7c0a 100644
--- a/bridges/primitives/chain-rialto/Cargo.toml
+++ b/bridges/primitives/chain-rialto/Cargo.toml
@@ -10,6 +10,7 @@ license = "GPL-3.0-or-later WITH Classpath-exception-2.0"
 
 # Bridge Dependencies
 
+bp-header-chain = { path = "../header-chain", default-features = false }
 bp-messages = { path = "../messages", default-features = false }
 bp-runtime = { path = "../runtime", default-features = false }
 
@@ -25,6 +26,7 @@ sp-std = { git = "https://github.com/paritytech/substrate", branch = "master", d
 [features]
 default = ["std"]
 std = [
+	"bp-header-chain/std",
 	"bp-messages/std",
 	"bp-runtime/std",
 	"frame-support/std",
diff --git a/bridges/primitives/chain-rialto/src/lib.rs b/bridges/primitives/chain-rialto/src/lib.rs
index ca0a3dfee88d614ee01a60a66d949e1a1ea9604f..a1c4a7267a7bdff6f1a5ee3abe3044842bfb6148 100644
--- a/bridges/primitives/chain-rialto/src/lib.rs
+++ b/bridges/primitives/chain-rialto/src/lib.rs
@@ -18,6 +18,7 @@
 // RuntimeApi generated functions
 #![allow(clippy::too_many_arguments)]
 
+use bp_header_chain::ChainWithGrandpa;
 use bp_messages::{
 	InboundMessageDetails, LaneId, MessageNonce, MessagePayload, OutboundMessageDetails,
 };
@@ -72,6 +73,27 @@ pub const SESSION_LENGTH: BlockNumber = 4;
 /// Maximal number of GRANDPA authorities at Rialto.
 pub const MAX_AUTHORITIES_COUNT: u32 = 5;
 
+/// Reasonable number of headers in the `votes_ancestries` on Rialto chain.
+///
+/// See [`bp_header_chain::ChainWithGrandpa`] for more details.
+pub const REASONABLE_HEADERS_IN_JUSTIFICATON_ANCESTRY: u32 = 8;
+
+/// Approximate average header size in `votes_ancestries` field of justification on Rialto chain.
+///
+/// See [`bp_header_chain::ChainWithGrandpa`] for more details.
+pub const AVERAGE_HEADER_SIZE_IN_JUSTIFICATION: u32 = 256;
+
+/// Approximate maximal header size on Rialto chain.
+///
+/// We expect maximal header to have digest item with the new authorities set for every consensus
+/// engine (GRANDPA, Babe, BEEFY, ...) - so we multiply it by 3. And also
+/// `AVERAGE_HEADER_SIZE_IN_JUSTIFICATION` bytes for other stuff.
+///
+/// See [`bp_header_chain::ChainWithGrandpa`] for more details.
+pub const MAX_HEADER_SIZE: u32 = MAX_AUTHORITIES_COUNT
+	.saturating_mul(3)
+	.saturating_add(AVERAGE_HEADER_SIZE_IN_JUSTIFICATION);
+
 /// Maximal size of encoded `bp_parachains::ParaStoredHeaderData` structure among all Rialto
 /// parachains.
 ///
@@ -160,6 +182,15 @@ impl Chain for Rialto {
 	}
 }
 
+impl ChainWithGrandpa for Rialto {
+	const WITH_CHAIN_GRANDPA_PALLET_NAME: &'static str = WITH_RIALTO_GRANDPA_PALLET_NAME;
+	const MAX_AUTHORITIES_COUNT: u32 = MAX_AUTHORITIES_COUNT;
+	const REASONABLE_HEADERS_IN_JUSTIFICATON_ANCESTRY: u32 =
+		REASONABLE_HEADERS_IN_JUSTIFICATON_ANCESTRY;
+	const MAX_HEADER_SIZE: u32 = MAX_HEADER_SIZE;
+	const AVERAGE_HEADER_SIZE_IN_JUSTIFICATION: u32 = AVERAGE_HEADER_SIZE_IN_JUSTIFICATION;
+}
+
 frame_support::parameter_types! {
 	pub BlockLength: limits::BlockLength =
 		limits::BlockLength::max_with_normal_ratio(5 * 1024 * 1024, NORMAL_DISPATCH_RATIO);
diff --git a/bridges/primitives/chain-rococo/Cargo.toml b/bridges/primitives/chain-rococo/Cargo.toml
index 73a2450cd17d7c2cdcf0ee06799232bb5cb7a66f..4e21bd38b7acbfe24236e9a79dc7f669634aff2c 100644
--- a/bridges/primitives/chain-rococo/Cargo.toml
+++ b/bridges/primitives/chain-rococo/Cargo.toml
@@ -9,18 +9,22 @@ license = "GPL-3.0-or-later WITH Classpath-exception-2.0"
 [dependencies]
 
 # Bridge Dependencies
+
+bp-header-chain = { path = "../header-chain", default-features = false }
 bp-polkadot-core = { path = "../polkadot-core", default-features = false }
 bp-runtime = { path = "../runtime", default-features = false }
 
 # Substrate Based Dependencies
+
 sp-api = { git = "https://github.com/paritytech/substrate", branch = "master", default-features = false }
 frame-support = { git = "https://github.com/paritytech/substrate", branch = "master", default-features = false }
 
 [features]
 default = ["std"]
 std = [
+	"bp-header-chain/std",
 	"bp-polkadot-core/std",
 	"bp-runtime/std",
-	"sp-api/std",
 	"frame-support/std",
+	"sp-api/std",
 ]
diff --git a/bridges/primitives/chain-rococo/src/lib.rs b/bridges/primitives/chain-rococo/src/lib.rs
index 41fc65e0e93d1f3a90731899e392f03dfa384585..0cb0b1d41e6dc42642709d7779f96535f1f35fdb 100644
--- a/bridges/primitives/chain-rococo/src/lib.rs
+++ b/bridges/primitives/chain-rococo/src/lib.rs
@@ -19,11 +19,42 @@
 #![allow(clippy::too_many_arguments)]
 
 pub use bp_polkadot_core::*;
-use bp_runtime::decl_bridge_finality_runtime_apis;
-use frame_support::parameter_types;
+
+use bp_header_chain::ChainWithGrandpa;
+use bp_runtime::{decl_bridge_finality_runtime_apis, Chain};
+use frame_support::{parameter_types, weights::Weight};
 
 /// Rococo Chain
-pub type Rococo = PolkadotLike;
+pub struct Rococo;
+
+impl Chain for Rococo {
+	type BlockNumber = <PolkadotLike as Chain>::BlockNumber;
+	type Hash = <PolkadotLike as Chain>::Hash;
+	type Hasher = <PolkadotLike as Chain>::Hasher;
+	type Header = <PolkadotLike as Chain>::Header;
+
+	type AccountId = <PolkadotLike as Chain>::AccountId;
+	type Balance = <PolkadotLike as Chain>::Balance;
+	type Index = <PolkadotLike as Chain>::Index;
+	type Signature = <PolkadotLike as Chain>::Signature;
+
+	fn max_extrinsic_size() -> u32 {
+		PolkadotLike::max_extrinsic_size()
+	}
+
+	fn max_extrinsic_weight() -> Weight {
+		PolkadotLike::max_extrinsic_weight()
+	}
+}
+
+impl ChainWithGrandpa for Rococo {
+	const WITH_CHAIN_GRANDPA_PALLET_NAME: &'static str = WITH_ROCOCO_GRANDPA_PALLET_NAME;
+	const MAX_AUTHORITIES_COUNT: u32 = MAX_AUTHORITIES_COUNT;
+	const REASONABLE_HEADERS_IN_JUSTIFICATON_ANCESTRY: u32 =
+		REASONABLE_HEADERS_IN_JUSTIFICATON_ANCESTRY;
+	const MAX_HEADER_SIZE: u32 = MAX_HEADER_SIZE;
+	const AVERAGE_HEADER_SIZE_IN_JUSTIFICATION: u32 = AVERAGE_HEADER_SIZE_IN_JUSTIFICATION;
+}
 
 parameter_types! {
 	pub const SS58Prefix: u8 = 42;
diff --git a/bridges/primitives/chain-westend/Cargo.toml b/bridges/primitives/chain-westend/Cargo.toml
index 75f6727d764fd6503bedfc3c8ad51eae7065b08f..13a2e597f9d861880e7c2d95413168f7e532d361 100644
--- a/bridges/primitives/chain-westend/Cargo.toml
+++ b/bridges/primitives/chain-westend/Cargo.toml
@@ -10,6 +10,7 @@ license = "GPL-3.0-or-later WITH Classpath-exception-2.0"
 
 # Bridge Dependencies
 
+bp-header-chain = { path = "../header-chain", default-features = false }
 bp-polkadot-core = { path = "../polkadot-core", default-features = false }
 bp-runtime = { path = "../runtime", default-features = false }
 
@@ -21,6 +22,7 @@ sp-api = { git = "https://github.com/paritytech/substrate", branch = "master", d
 [features]
 default = ["std"]
 std = [
+	"bp-header-chain/std",
 	"bp-polkadot-core/std",
 	"bp-runtime/std",
 	"frame-support/std",
diff --git a/bridges/primitives/chain-westend/src/lib.rs b/bridges/primitives/chain-westend/src/lib.rs
index 910a6c4acbd014ef03b0aed687dd7b1891e1d234..b6d41ece2868d73f5db5a73e81d57c467379c2de 100644
--- a/bridges/primitives/chain-westend/src/lib.rs
+++ b/bridges/primitives/chain-westend/src/lib.rs
@@ -19,11 +19,42 @@
 #![allow(clippy::too_many_arguments)]
 
 pub use bp_polkadot_core::*;
+
+use bp_header_chain::ChainWithGrandpa;
 use bp_runtime::{decl_bridge_finality_runtime_apis, Chain, Parachain};
 use frame_support::weights::Weight;
 
 /// Westend Chain
-pub type Westend = PolkadotLike;
+pub struct Westend;
+
+impl Chain for Westend {
+	type BlockNumber = <PolkadotLike as Chain>::BlockNumber;
+	type Hash = <PolkadotLike as Chain>::Hash;
+	type Hasher = <PolkadotLike as Chain>::Hasher;
+	type Header = <PolkadotLike as Chain>::Header;
+
+	type AccountId = <PolkadotLike as Chain>::AccountId;
+	type Balance = <PolkadotLike as Chain>::Balance;
+	type Index = <PolkadotLike as Chain>::Index;
+	type Signature = <PolkadotLike as Chain>::Signature;
+
+	fn max_extrinsic_size() -> u32 {
+		PolkadotLike::max_extrinsic_size()
+	}
+
+	fn max_extrinsic_weight() -> Weight {
+		PolkadotLike::max_extrinsic_weight()
+	}
+}
+
+impl ChainWithGrandpa for Westend {
+	const WITH_CHAIN_GRANDPA_PALLET_NAME: &'static str = WITH_WESTEND_GRANDPA_PALLET_NAME;
+	const MAX_AUTHORITIES_COUNT: u32 = MAX_AUTHORITIES_COUNT;
+	const REASONABLE_HEADERS_IN_JUSTIFICATON_ANCESTRY: u32 =
+		REASONABLE_HEADERS_IN_JUSTIFICATON_ANCESTRY;
+	const MAX_HEADER_SIZE: u32 = MAX_HEADER_SIZE;
+	const AVERAGE_HEADER_SIZE_IN_JUSTIFICATION: u32 = AVERAGE_HEADER_SIZE_IN_JUSTIFICATION;
+}
 
 /// Westmint parachain definition
 #[derive(Debug, Clone, Copy)]
diff --git a/bridges/primitives/chain-wococo/Cargo.toml b/bridges/primitives/chain-wococo/Cargo.toml
index 6b741cd4b73c6f01b62bd7215047b7be7c30b65f..25fd7b9fd947adb76f41bc86ebfafc437cda9283 100644
--- a/bridges/primitives/chain-wococo/Cargo.toml
+++ b/bridges/primitives/chain-wococo/Cargo.toml
@@ -9,18 +9,24 @@ license = "GPL-3.0-or-later WITH Classpath-exception-2.0"
 [dependencies]
 
 # Bridge Dependencies
+
+bp-header-chain = { path = "../header-chain", default-features = false }
 bp-polkadot-core = { path = "../polkadot-core", default-features = false }
 bp-runtime = { path = "../runtime", default-features = false }
 bp-rococo = { path = "../chain-rococo", default-features = false }
 
 # Substrate Based Dependencies
+
+frame-support = { git = "https://github.com/paritytech/substrate", branch = "master", default-features = false }
 sp-api = { git = "https://github.com/paritytech/substrate", branch = "master", default-features = false }
 
 [features]
 default = ["std"]
 std = [
+	"bp-header-chain/std",
 	"bp-polkadot-core/std",
 	"bp-runtime/std",
 	"bp-rococo/std",
+	"frame-support/std",
 	"sp-api/std",
 ]
diff --git a/bridges/primitives/chain-wococo/src/lib.rs b/bridges/primitives/chain-wococo/src/lib.rs
index 1cf666c7f9612a78e43af8d9bc3243663bac9a40..2df019496ab14206426c106eebaf877e9be68e8e 100644
--- a/bridges/primitives/chain-wococo/src/lib.rs
+++ b/bridges/primitives/chain-wococo/src/lib.rs
@@ -22,10 +22,42 @@ pub use bp_polkadot_core::*;
 pub use bp_rococo::{
 	SS58Prefix, MAX_AUTHORITIES_COUNT, MAX_NESTED_PARACHAIN_HEAD_DATA_SIZE, PARAS_PALLET_NAME,
 };
-use bp_runtime::decl_bridge_finality_runtime_apis;
+
+use bp_header_chain::ChainWithGrandpa;
+use bp_runtime::{decl_bridge_finality_runtime_apis, Chain};
+use frame_support::weights::Weight;
 
 /// Wococo Chain
-pub type Wococo = PolkadotLike;
+pub struct Wococo;
+
+impl Chain for Wococo {
+	type BlockNumber = <PolkadotLike as Chain>::BlockNumber;
+	type Hash = <PolkadotLike as Chain>::Hash;
+	type Hasher = <PolkadotLike as Chain>::Hasher;
+	type Header = <PolkadotLike as Chain>::Header;
+
+	type AccountId = <PolkadotLike as Chain>::AccountId;
+	type Balance = <PolkadotLike as Chain>::Balance;
+	type Index = <PolkadotLike as Chain>::Index;
+	type Signature = <PolkadotLike as Chain>::Signature;
+
+	fn max_extrinsic_size() -> u32 {
+		PolkadotLike::max_extrinsic_size()
+	}
+
+	fn max_extrinsic_weight() -> Weight {
+		PolkadotLike::max_extrinsic_weight()
+	}
+}
+
+impl ChainWithGrandpa for Wococo {
+	const WITH_CHAIN_GRANDPA_PALLET_NAME: &'static str = WITH_WOCOCO_GRANDPA_PALLET_NAME;
+	const MAX_AUTHORITIES_COUNT: u32 = MAX_AUTHORITIES_COUNT;
+	const REASONABLE_HEADERS_IN_JUSTIFICATON_ANCESTRY: u32 =
+		REASONABLE_HEADERS_IN_JUSTIFICATON_ANCESTRY;
+	const MAX_HEADER_SIZE: u32 = MAX_HEADER_SIZE;
+	const AVERAGE_HEADER_SIZE_IN_JUSTIFICATION: u32 = AVERAGE_HEADER_SIZE_IN_JUSTIFICATION;
+}
 
 /// Name of the With-Wococo GRANDPA pallet instance that is deployed at bridged chains.
 pub const WITH_WOCOCO_GRANDPA_PALLET_NAME: &str = "BridgeWococoGrandpa";
diff --git a/bridges/primitives/header-chain/src/lib.rs b/bridges/primitives/header-chain/src/lib.rs
index 49d1ae894068f84bada51ae516a918576e75eb8f..cafb8e7a3c86f98658699717421df986b20d65e1 100644
--- a/bridges/primitives/header-chain/src/lib.rs
+++ b/bridges/primitives/header-chain/src/lib.rs
@@ -119,7 +119,7 @@ impl AuthoritySet {
 	}
 }
 
-/// Data required for initializing the bridge pallet.
+/// Data required for initializing the GRANDPA bridge pallet.
 ///
 /// The bridge needs to know where to start its sync from, and this provides that initial context.
 #[derive(Default, Encode, Decode, RuntimeDebug, PartialEq, Eq, Clone, TypeInfo)]
@@ -188,3 +188,48 @@ pub enum BridgeGrandpaCall<Header: HeaderT> {
 
 /// The `BridgeGrandpaCall` used by a chain.
 pub type BridgeGrandpaCallOf<C> = BridgeGrandpaCall<HeaderOf<C>>;
+
+/// Substrate-based chain that is using direct GRANDPA finality.
+///
+/// Keep in mind that parachains are relying on relay chain GRANDPA, so they should not implement
+/// this trait.
+pub trait ChainWithGrandpa: Chain {
+	/// Name of the bridge GRANDPA pallet (used in `construct_runtime` macro call) that is deployed
+	/// at some other chain to bridge with this `ChainWithGrandpa`.
+	///
+	/// We assume that all chains that are bridging with this `ChainWithGrandpa` are using
+	/// the same name.
+	const WITH_CHAIN_GRANDPA_PALLET_NAME: &'static str;
+
+	/// Max number of GRANDPA authorities at the chain.
+	///
+	/// This is a strict constant. If bridged chain will have more authorities than that,
+	/// the GRANDPA bridge pallet may halt.
+	const MAX_AUTHORITIES_COUNT: u32;
+
+	/// Max reasonable number of headers in `votes_ancestries` vector of the GRANDPA justification.
+	///
+	/// This isn't a strict limit. The relay may submit justifications with more headers in its
+	/// ancestry and the pallet will accept such justification. The limit is only used to compute
+	/// maximal refund amount and submitting justifications which exceed the limit, may be costly
+	/// to submitter.
+	const REASONABLE_HEADERS_IN_JUSTIFICATON_ANCESTRY: u32;
+
+	/// Maximal size of the chain header. The header may be the header that enacts new GRANDPA
+	/// authorities set (so it has large digest inside).
+	///
+	/// This isn't a strict limit. The relay may submit larger headers and the pallet will accept
+	/// the call. The limit is only used to compute maximal refund amount and doing calls which
+	/// exceed the limit, may be costly to submitter.
+	const MAX_HEADER_SIZE: u32;
+
+	/// Average size of the chain header from justification ancestry. We don't expect to see there
+	/// headers that change GRANDPA authorities set (GRANDPA will probably be able to finalize at
+	/// least one additional header per session on non test chains), so this is average size of
+	/// headers that aren't changing the set.
+	///
+	/// This isn't a strict limit. The relay may submit justifications with larger headers in its
+	/// ancestry and the pallet will accept the call. The limit is only used to compute maximal
+	/// refund amount and doing calls which exceed the limit, may be costly to submitter.
+	const AVERAGE_HEADER_SIZE_IN_JUSTIFICATION: u32;
+}
diff --git a/bridges/primitives/polkadot-core/src/lib.rs b/bridges/primitives/polkadot-core/src/lib.rs
index b13ceb5df50d45ae61f1ddffd77b238d10c2cae0..3b1712042d80896b3de4a8d27a9351fa070ba6fe 100644
--- a/bridges/primitives/polkadot-core/src/lib.rs
+++ b/bridges/primitives/polkadot-core/src/lib.rs
@@ -53,6 +53,28 @@ pub mod parachains;
 /// take twice as much here.
 pub const MAX_AUTHORITIES_COUNT: u32 = 2_048;
 
+/// Reasonable number of headers in the `votes_ancestries` on Polkadot-like chains.
+///
+/// See [`bp_header_chain::ChainWithGrandpa`] for more details.
+pub const REASONABLE_HEADERS_IN_JUSTIFICATON_ANCESTRY: u32 = 8;
+
+/// Approximate average header size in `votes_ancestries` field of justification on Polkadot-like
+/// chains.
+///
+/// See [`bp_header_chain::ChainWithGrandpa`] for more details.
+pub const AVERAGE_HEADER_SIZE_IN_JUSTIFICATION: u32 = 256;
+
+/// Approximate maximal header size on Polkadot-like chains.
+///
+/// We expect maximal header to have digest item with the new authorities set for every consensus
+/// engine (GRANDPA, Babe, BEEFY, ...) - so we multiply it by 3. And also
+/// `AVERAGE_HEADER_SIZE_IN_JUSTIFICATION` bytes for other stuff.
+///
+/// See [`bp_header_chain::ChainWithGrandpa`] for more details.
+pub const MAX_HEADER_SIZE: u32 = MAX_AUTHORITIES_COUNT
+	.saturating_mul(3)
+	.saturating_add(AVERAGE_HEADER_SIZE_IN_JUSTIFICATION);
+
 /// Number of extra bytes (excluding size of storage value itself) of storage proof, built at
 /// Polkadot-like chain. This mostly depends on number of entries in the storage trie.
 /// Some reserve is reserved to account future chain growth.
diff --git a/bridges/relays/client-kusama/src/lib.rs b/bridges/relays/client-kusama/src/lib.rs
index cd6e23a46079529701c631cbac6a7485bd3c0ba8..83f6b30f4cb29cb6a5fe26fd04d8077804a00cee 100644
--- a/bridges/relays/client-kusama/src/lib.rs
+++ b/bridges/relays/client-kusama/src/lib.rs
@@ -17,7 +17,7 @@
 //! Types used to connect to the Kusama chain.
 
 use bp_kusama::AccountInfoStorageMapKeyProvider;
-use relay_substrate_client::{Chain, ChainWithBalances, ChainWithGrandpa, UnderlyingChainProvider};
+use relay_substrate_client::{Chain, ChainWithBalances, UnderlyingChainProvider};
 use sp_core::storage::StorageKey;
 use std::time::Duration;
 
@@ -43,10 +43,6 @@ impl Chain for Kusama {
 	type Call = ();
 }
 
-impl ChainWithGrandpa for Kusama {
-	const WITH_CHAIN_GRANDPA_PALLET_NAME: &'static str = bp_kusama::WITH_KUSAMA_GRANDPA_PALLET_NAME;
-}
-
 impl ChainWithBalances for Kusama {
 	fn account_info_storage_key(account_id: &Self::AccountId) -> StorageKey {
 		AccountInfoStorageMapKeyProvider::final_key(account_id)
diff --git a/bridges/relays/client-millau/src/lib.rs b/bridges/relays/client-millau/src/lib.rs
index 34bbea92d57e99d054c6606a46dcf552ea4a88ca..711b4f32cc632652b5d77bd7696c3f4b2b24fc6c 100644
--- a/bridges/relays/client-millau/src/lib.rs
+++ b/bridges/relays/client-millau/src/lib.rs
@@ -19,9 +19,9 @@
 use bp_messages::MessageNonce;
 use codec::{Compact, Decode, Encode};
 use relay_substrate_client::{
-	BalanceOf, Chain, ChainWithBalances, ChainWithGrandpa, ChainWithMessages,
-	ChainWithTransactions, ChainWithUtilityPallet, Error as SubstrateError,
-	FullRuntimeUtilityPallet, IndexOf, SignParam, UnderlyingChainProvider, UnsignedTransaction,
+	BalanceOf, Chain, ChainWithBalances, ChainWithMessages, ChainWithTransactions,
+	ChainWithUtilityPallet, Error as SubstrateError, FullRuntimeUtilityPallet, IndexOf, SignParam,
+	UnderlyingChainProvider, UnsignedTransaction,
 };
 use sp_core::{storage::StorageKey, Pair};
 use sp_runtime::{generic::SignedPayload, traits::IdentifyAccount};
@@ -38,10 +38,6 @@ impl UnderlyingChainProvider for Millau {
 	type Chain = bp_millau::Millau;
 }
 
-impl ChainWithGrandpa for Millau {
-	const WITH_CHAIN_GRANDPA_PALLET_NAME: &'static str = bp_millau::WITH_MILLAU_GRANDPA_PALLET_NAME;
-}
-
 impl ChainWithMessages for Millau {
 	const WITH_CHAIN_MESSAGES_PALLET_NAME: &'static str =
 		bp_millau::WITH_MILLAU_MESSAGES_PALLET_NAME;
diff --git a/bridges/relays/client-polkadot/src/lib.rs b/bridges/relays/client-polkadot/src/lib.rs
index ac67d55ab5418eedf154b5eb9b5ab0364b26c39d..19326dd4c7303a5ff700e8e56abff4dd52ebd462 100644
--- a/bridges/relays/client-polkadot/src/lib.rs
+++ b/bridges/relays/client-polkadot/src/lib.rs
@@ -17,7 +17,7 @@
 //! Types used to connect to the Polkadot chain.
 
 use bp_polkadot::AccountInfoStorageMapKeyProvider;
-use relay_substrate_client::{Chain, ChainWithBalances, ChainWithGrandpa, UnderlyingChainProvider};
+use relay_substrate_client::{Chain, ChainWithBalances, UnderlyingChainProvider};
 use sp_core::storage::StorageKey;
 use std::time::Duration;
 
@@ -43,11 +43,6 @@ impl Chain for Polkadot {
 	type Call = ();
 }
 
-impl ChainWithGrandpa for Polkadot {
-	const WITH_CHAIN_GRANDPA_PALLET_NAME: &'static str =
-		bp_polkadot::WITH_POLKADOT_GRANDPA_PALLET_NAME;
-}
-
 impl ChainWithBalances for Polkadot {
 	fn account_info_storage_key(account_id: &Self::AccountId) -> StorageKey {
 		AccountInfoStorageMapKeyProvider::final_key(account_id)
diff --git a/bridges/relays/client-rialto/src/lib.rs b/bridges/relays/client-rialto/src/lib.rs
index 8ad31de4d583a73267d33e9fe9d0a1bda7169a01..39cc2721ddbf5329d8f1627b02c3b21db19106d5 100644
--- a/bridges/relays/client-rialto/src/lib.rs
+++ b/bridges/relays/client-rialto/src/lib.rs
@@ -19,9 +19,9 @@
 use bp_messages::MessageNonce;
 use codec::{Compact, Decode, Encode};
 use relay_substrate_client::{
-	BalanceOf, Chain, ChainWithBalances, ChainWithGrandpa, ChainWithMessages,
-	ChainWithTransactions, Error as SubstrateError, IndexOf, RelayChain, SignParam,
-	UnderlyingChainProvider, UnsignedTransaction,
+	BalanceOf, Chain, ChainWithBalances, ChainWithMessages, ChainWithTransactions,
+	Error as SubstrateError, IndexOf, RelayChain, SignParam, UnderlyingChainProvider,
+	UnsignedTransaction,
 };
 use sp_core::{storage::StorageKey, Pair};
 use sp_runtime::{generic::SignedPayload, traits::IdentifyAccount};
@@ -56,10 +56,6 @@ impl RelayChain for Rialto {
 		bp_rialto::WITH_RIALTO_BRIDGE_PARAS_PALLET_NAME;
 }
 
-impl ChainWithGrandpa for Rialto {
-	const WITH_CHAIN_GRANDPA_PALLET_NAME: &'static str = bp_rialto::WITH_RIALTO_GRANDPA_PALLET_NAME;
-}
-
 impl ChainWithMessages for Rialto {
 	const WITH_CHAIN_MESSAGES_PALLET_NAME: &'static str =
 		bp_rialto::WITH_RIALTO_MESSAGES_PALLET_NAME;
diff --git a/bridges/relays/client-rococo/src/lib.rs b/bridges/relays/client-rococo/src/lib.rs
index a764759906ac4406128b4b13dba11fe3f990b6f1..a0730026e04c65c08637c5c21ec3bf13771e0f47 100644
--- a/bridges/relays/client-rococo/src/lib.rs
+++ b/bridges/relays/client-rococo/src/lib.rs
@@ -16,9 +16,7 @@
 
 //! Types used to connect to the Rococo-Substrate chain.
 
-use relay_substrate_client::{
-	Chain, ChainWithBalances, ChainWithGrandpa, RelayChain, UnderlyingChainProvider,
-};
+use relay_substrate_client::{Chain, ChainWithBalances, RelayChain, UnderlyingChainProvider};
 use sp_core::storage::StorageKey;
 use std::time::Duration;
 
@@ -47,10 +45,6 @@ impl Chain for Rococo {
 	type Call = ();
 }
 
-impl ChainWithGrandpa for Rococo {
-	const WITH_CHAIN_GRANDPA_PALLET_NAME: &'static str = bp_rococo::WITH_ROCOCO_GRANDPA_PALLET_NAME;
-}
-
 impl ChainWithBalances for Rococo {
 	fn account_info_storage_key(account_id: &Self::AccountId) -> StorageKey {
 		bp_rococo::AccountInfoStorageMapKeyProvider::final_key(account_id)
diff --git a/bridges/relays/client-substrate/src/chain.rs b/bridges/relays/client-substrate/src/chain.rs
index 4ec5edfc41bcf83d70f8cc57fcef7d60ad3504b2..b9c5793842efd23eb092a189c17d2ae951d73ca0 100644
--- a/bridges/relays/client-substrate/src/chain.rs
+++ b/bridges/relays/client-substrate/src/chain.rs
@@ -73,11 +73,6 @@ pub trait RelayChain: Chain {
 	const PARACHAINS_FINALITY_PALLET_NAME: &'static str;
 }
 
-/// Substrate-based parachain from minimal relay-client point of view.
-pub trait Parachain: Chain + ParachainBase {}
-
-impl<T> Parachain for T where T: UnderlyingChainProvider + Chain + ParachainBase {}
-
 /// Substrate-based chain that is using direct GRANDPA finality from minimal relay-client point of
 /// view.
 ///
@@ -92,6 +87,20 @@ pub trait ChainWithGrandpa: Chain {
 	const WITH_CHAIN_GRANDPA_PALLET_NAME: &'static str;
 }
 
+impl<T> ChainWithGrandpa for T
+where
+	T: Chain + UnderlyingChainProvider,
+	T::Chain: bp_header_chain::ChainWithGrandpa,
+{
+	const WITH_CHAIN_GRANDPA_PALLET_NAME: &'static str =
+		<T::Chain as bp_header_chain::ChainWithGrandpa>::WITH_CHAIN_GRANDPA_PALLET_NAME;
+}
+
+/// Substrate-based parachain from minimal relay-client point of view.
+pub trait Parachain: Chain + ParachainBase {}
+
+impl<T> Parachain for T where T: UnderlyingChainProvider + Chain + ParachainBase {}
+
 /// Substrate-based chain with messaging support from minimal relay-client point of view.
 pub trait ChainWithMessages: Chain {
 	/// Name of the bridge messages pallet (used in `construct_runtime` macro call) that is deployed
diff --git a/bridges/relays/client-westend/src/lib.rs b/bridges/relays/client-westend/src/lib.rs
index 6467f431335cfe1be130672ce0e457692c06a962..a8a8df36b8bf96d88d6d31b4dc6a4c1f03723579 100644
--- a/bridges/relays/client-westend/src/lib.rs
+++ b/bridges/relays/client-westend/src/lib.rs
@@ -16,9 +16,7 @@
 
 //! Types used to connect to the Westend chain.
 
-use relay_substrate_client::{
-	Chain, ChainWithBalances, ChainWithGrandpa, RelayChain, UnderlyingChainProvider,
-};
+use relay_substrate_client::{Chain, ChainWithBalances, RelayChain, UnderlyingChainProvider};
 use sp_core::storage::StorageKey;
 use std::time::Duration;
 
@@ -53,11 +51,6 @@ impl RelayChain for Westend {
 		bp_westend::WITH_WESTEND_BRIDGE_PARAS_PALLET_NAME;
 }
 
-impl ChainWithGrandpa for Westend {
-	const WITH_CHAIN_GRANDPA_PALLET_NAME: &'static str =
-		bp_westend::WITH_WESTEND_GRANDPA_PALLET_NAME;
-}
-
 impl ChainWithBalances for Westend {
 	fn account_info_storage_key(account_id: &Self::AccountId) -> StorageKey {
 		bp_westend::AccountInfoStorageMapKeyProvider::final_key(account_id)
diff --git a/bridges/relays/client-wococo/src/lib.rs b/bridges/relays/client-wococo/src/lib.rs
index a3e10729ba0935bf9edb47c940fad8991937b81e..8f30572db63af24df317075c0be1393ae5d2e0f9 100644
--- a/bridges/relays/client-wococo/src/lib.rs
+++ b/bridges/relays/client-wococo/src/lib.rs
@@ -16,9 +16,7 @@
 
 //! Types used to connect to the Wococo-Substrate chain.
 
-use relay_substrate_client::{
-	Chain, ChainWithBalances, ChainWithGrandpa, RelayChain, UnderlyingChainProvider,
-};
+use relay_substrate_client::{Chain, ChainWithBalances, RelayChain, UnderlyingChainProvider};
 use sp_core::storage::StorageKey;
 use std::time::Duration;
 
@@ -47,10 +45,6 @@ impl Chain for Wococo {
 	type Call = ();
 }
 
-impl ChainWithGrandpa for Wococo {
-	const WITH_CHAIN_GRANDPA_PALLET_NAME: &'static str = bp_wococo::WITH_WOCOCO_GRANDPA_PALLET_NAME;
-}
-
 impl ChainWithBalances for Wococo {
 	fn account_info_storage_key(account_id: &Self::AccountId) -> StorageKey {
 		bp_wococo::AccountInfoStorageMapKeyProvider::final_key(account_id)