diff --git a/Cargo.lock b/Cargo.lock
index f97d29c56c86aab57906fb358a611d3b1f5f8e99..69eabeb04e20c6c5c116f800e73a411eee370f5a 100644
--- a/Cargo.lock
+++ b/Cargo.lock
@@ -2188,12 +2188,14 @@ dependencies = [
 name = "bridge-hub-rococo-emulated-chain"
 version = "0.0.0"
 dependencies = [
+ "bp-messages",
  "bridge-hub-common",
  "bridge-hub-rococo-runtime",
  "emulated-integration-tests-common",
  "frame-support",
  "parachains-common",
  "sp-core 28.0.0",
+ "staging-xcm",
  "testnet-parachains-constants",
 ]
 
@@ -2357,6 +2359,7 @@ dependencies = [
  "pallet-bridge-relayers",
  "pallet-timestamp",
  "pallet-utility",
+ "pallet-xcm",
  "pallet-xcm-bridge-hub",
  "parachains-common",
  "parachains-runtimes-test-utils",
@@ -2375,12 +2378,14 @@ dependencies = [
 name = "bridge-hub-westend-emulated-chain"
 version = "0.0.0"
 dependencies = [
+ "bp-messages",
  "bridge-hub-common",
  "bridge-hub-westend-runtime",
  "emulated-integration-tests-common",
  "frame-support",
  "parachains-common",
  "sp-core 28.0.0",
+ "staging-xcm",
  "testnet-parachains-constants",
 ]
 
@@ -9560,6 +9565,7 @@ dependencies = [
  "parking_lot 0.12.3",
  "relay-utils",
  "sp-arithmetic 23.0.0",
+ "sp-core 28.0.0",
 ]
 
 [[package]]
@@ -14876,6 +14882,7 @@ version = "4.0.0"
 dependencies = [
  "asset-hub-rococo-runtime",
  "asset-hub-westend-runtime",
+ "bp-messages",
  "bridge-hub-rococo-runtime",
  "bridge-hub-westend-runtime",
  "collectives-westend-runtime",
diff --git a/bridges/bin/runtime-common/src/extensions.rs b/bridges/bin/runtime-common/src/extensions.rs
index dc7e14de28f311817b7bced78e62ff1b5b037b1d..dced502394717411654d270b335d57b94feda31a 100644
--- a/bridges/bin/runtime-common/src/extensions.rs
+++ b/bridges/bin/runtime-common/src/extensions.rs
@@ -374,7 +374,7 @@ mod tests {
 	use super::*;
 	use crate::mock::*;
 	use bp_header_chain::StoredHeaderDataBuilder;
-	use bp_messages::{InboundLaneData, LaneId, MessageNonce, OutboundLaneData};
+	use bp_messages::{InboundLaneData, MessageNonce, OutboundLaneData};
 	use bp_parachains::{BestParaHeadHash, ParaInfo};
 	use bp_polkadot_core::parachains::{ParaHeadsProof, ParaId};
 	use bp_relayers::{RewardsAccountOwner, RewardsAccountParams};
@@ -390,17 +390,16 @@ mod tests {
 	};
 
 	parameter_types! {
-		pub MsgProofsRewardsAccount: RewardsAccountParams = RewardsAccountParams::new(
+		pub MsgProofsRewardsAccount: RewardsAccountParams<TestLaneIdType> = RewardsAccountParams::new(
 			test_lane_id(),
 			TEST_BRIDGED_CHAIN_ID,
 			RewardsAccountOwner::ThisChain,
 		);
-		pub MsgDeliveryProofsRewardsAccount: RewardsAccountParams = RewardsAccountParams::new(
+		pub MsgDeliveryProofsRewardsAccount: RewardsAccountParams<TestLaneIdType> = RewardsAccountParams::new(
 			test_lane_id(),
 			TEST_BRIDGED_CHAIN_ID,
 			RewardsAccountOwner::BridgedChain,
 		);
-		pub TestLaneId: LaneId = test_lane_id();
 	}
 
 	pub struct MockCall {
@@ -476,10 +475,6 @@ mod tests {
 		}
 	}
 
-	fn test_lane_id() -> LaneId {
-		LaneId::new(1, 2)
-	}
-
 	fn initial_balance_of_relayer_account_at_this_chain() -> ThisChainBalance {
 		let test_stake: ThisChainBalance = TestStake::get();
 		ExistentialDeposit::get().saturating_add(test_stake * 100)
diff --git a/bridges/bin/runtime-common/src/messages_api.rs b/bridges/bin/runtime-common/src/messages_api.rs
index 7fbdeb366124778b36c77725be8ca8778020be1b..c8522d4d1f276aa945682de04edbe032a20e8a93 100644
--- a/bridges/bin/runtime-common/src/messages_api.rs
+++ b/bridges/bin/runtime-common/src/messages_api.rs
@@ -16,14 +16,12 @@
 
 //! Helpers for implementing various message-related runtime API methods.
 
-use bp_messages::{
-	InboundMessageDetails, LaneId, MessageNonce, MessagePayload, OutboundMessageDetails,
-};
+use bp_messages::{InboundMessageDetails, MessageNonce, MessagePayload, OutboundMessageDetails};
 use sp_std::vec::Vec;
 
 /// Implementation of the `To*OutboundLaneApi::message_details`.
 pub fn outbound_message_details<Runtime, MessagesPalletInstance>(
-	lane: LaneId,
+	lane: Runtime::LaneId,
 	begin: MessageNonce,
 	end: MessageNonce,
 ) -> Vec<OutboundMessageDetails>
@@ -48,7 +46,7 @@ where
 
 /// Implementation of the `To*InboundLaneApi::message_details`.
 pub fn inbound_message_details<Runtime, MessagesPalletInstance>(
-	lane: LaneId,
+	lane: Runtime::LaneId,
 	messages: Vec<(MessagePayload, OutboundMessageDetails)>,
 ) -> Vec<InboundMessageDetails>
 where
diff --git a/bridges/bin/runtime-common/src/messages_benchmarking.rs b/bridges/bin/runtime-common/src/messages_benchmarking.rs
index 1880e65547fe6d5e0af71e6ec7c6e0e214f20866..acbdbcda8deafcf64430b1cabe0ca9eaf6eddfb1 100644
--- a/bridges/bin/runtime-common/src/messages_benchmarking.rs
+++ b/bridges/bin/runtime-common/src/messages_benchmarking.rs
@@ -33,15 +33,15 @@ use pallet_bridge_messages::{
 		encode_all_messages, encode_lane_data, prepare_message_delivery_storage_proof,
 		prepare_messages_storage_proof,
 	},
-	BridgedChainOf, ThisChainOf,
+	BridgedChainOf, LaneIdOf, ThisChainOf,
 };
 use sp_runtime::traits::{Header, Zero};
 use sp_std::prelude::*;
 use xcm::latest::prelude::*;
 
 /// Prepare inbound bridge message according to given message proof parameters.
-fn prepare_inbound_message(
-	params: &MessageProofParams,
+fn prepare_inbound_message<LaneId>(
+	params: &MessageProofParams<LaneId>,
 	successful_dispatch_message_generator: impl Fn(usize) -> MessagePayload,
 ) -> MessagePayload {
 	let expected_size = params.proof_params.db_size.unwrap_or(0) as usize;
@@ -71,9 +71,9 @@ fn prepare_inbound_message(
 /// uses GRANDPA finality. For parachains, please use the `prepare_message_proof_from_parachain`
 /// function.
 pub fn prepare_message_proof_from_grandpa_chain<R, FI, MI>(
-	params: MessageProofParams,
+	params: MessageProofParams<LaneIdOf<R, MI>>,
 	message_generator: impl Fn(usize) -> MessagePayload,
-) -> (FromBridgedChainMessagesProof<HashOf<BridgedChainOf<R, MI>>>, Weight)
+) -> (FromBridgedChainMessagesProof<HashOf<BridgedChainOf<R, MI>>, LaneIdOf<R, MI>>, Weight)
 where
 	R: pallet_bridge_grandpa::Config<FI, BridgedChain = BridgedChainOf<R, MI>>
 		+ pallet_bridge_messages::Config<
@@ -84,18 +84,21 @@ where
 	MI: 'static,
 {
 	// prepare storage proof
-	let (state_root, storage_proof) =
-		prepare_messages_storage_proof::<BridgedChainOf<R, MI>, ThisChainOf<R, MI>>(
-			params.lane,
-			params.message_nonces.clone(),
-			params.outbound_lane_data.clone(),
-			params.proof_params,
-			|_| prepare_inbound_message(&params, &message_generator),
-			encode_all_messages,
-			encode_lane_data,
-			false,
-			false,
-		);
+	let (state_root, storage_proof) = prepare_messages_storage_proof::<
+		BridgedChainOf<R, MI>,
+		ThisChainOf<R, MI>,
+		LaneIdOf<R, MI>,
+	>(
+		params.lane,
+		params.message_nonces.clone(),
+		params.outbound_lane_data.clone(),
+		params.proof_params,
+		|_| prepare_inbound_message(&params, &message_generator),
+		encode_all_messages,
+		encode_lane_data,
+		false,
+		false,
+	);
 
 	// update runtime storage
 	let (_, bridged_header_hash) = insert_header_to_grandpa_pallet::<R, FI>(state_root);
@@ -121,9 +124,9 @@ where
 /// uses parachain finality. For GRANDPA chains, please use the
 /// `prepare_message_proof_from_grandpa_chain` function.
 pub fn prepare_message_proof_from_parachain<R, PI, MI>(
-	params: MessageProofParams,
+	params: MessageProofParams<LaneIdOf<R, MI>>,
 	message_generator: impl Fn(usize) -> MessagePayload,
-) -> (FromBridgedChainMessagesProof<HashOf<BridgedChainOf<R, MI>>>, Weight)
+) -> (FromBridgedChainMessagesProof<HashOf<BridgedChainOf<R, MI>>, LaneIdOf<R, MI>>, Weight)
 where
 	R: pallet_bridge_parachains::Config<PI> + pallet_bridge_messages::Config<MI>,
 	PI: 'static,
@@ -131,18 +134,21 @@ where
 	BridgedChainOf<R, MI>: Chain<Hash = ParaHash> + Parachain,
 {
 	// prepare storage proof
-	let (state_root, storage_proof) =
-		prepare_messages_storage_proof::<BridgedChainOf<R, MI>, ThisChainOf<R, MI>>(
-			params.lane,
-			params.message_nonces.clone(),
-			params.outbound_lane_data.clone(),
-			params.proof_params,
-			|_| prepare_inbound_message(&params, &message_generator),
-			encode_all_messages,
-			encode_lane_data,
-			false,
-			false,
-		);
+	let (state_root, storage_proof) = prepare_messages_storage_proof::<
+		BridgedChainOf<R, MI>,
+		ThisChainOf<R, MI>,
+		LaneIdOf<R, MI>,
+	>(
+		params.lane,
+		params.message_nonces.clone(),
+		params.outbound_lane_data.clone(),
+		params.proof_params,
+		|_| prepare_inbound_message(&params, &message_generator),
+		encode_all_messages,
+		encode_lane_data,
+		false,
+		false,
+	);
 
 	// update runtime storage
 	let (_, bridged_header_hash) =
@@ -166,8 +172,8 @@ where
 /// uses GRANDPA finality. For parachains, please use the
 /// `prepare_message_delivery_proof_from_parachain` function.
 pub fn prepare_message_delivery_proof_from_grandpa_chain<R, FI, MI>(
-	params: MessageDeliveryProofParams<AccountIdOf<ThisChainOf<R, MI>>>,
-) -> FromBridgedChainMessagesDeliveryProof<HashOf<BridgedChainOf<R, MI>>>
+	params: MessageDeliveryProofParams<AccountIdOf<ThisChainOf<R, MI>>, LaneIdOf<R, MI>>,
+) -> FromBridgedChainMessagesDeliveryProof<HashOf<BridgedChainOf<R, MI>>, LaneIdOf<R, MI>>
 where
 	R: pallet_bridge_grandpa::Config<FI, BridgedChain = BridgedChainOf<R, MI>>
 		+ pallet_bridge_messages::Config<
@@ -182,6 +188,7 @@ where
 	let (state_root, storage_proof) = prepare_message_delivery_storage_proof::<
 		BridgedChainOf<R, MI>,
 		ThisChainOf<R, MI>,
+		LaneIdOf<R, MI>,
 	>(params.lane, params.inbound_lane_data, params.proof_params);
 
 	// update runtime storage
@@ -200,8 +207,8 @@ where
 /// uses parachain finality. For GRANDPA chains, please use the
 /// `prepare_message_delivery_proof_from_grandpa_chain` function.
 pub fn prepare_message_delivery_proof_from_parachain<R, PI, MI>(
-	params: MessageDeliveryProofParams<AccountIdOf<ThisChainOf<R, MI>>>,
-) -> FromBridgedChainMessagesDeliveryProof<HashOf<BridgedChainOf<R, MI>>>
+	params: MessageDeliveryProofParams<AccountIdOf<ThisChainOf<R, MI>>, LaneIdOf<R, MI>>,
+) -> FromBridgedChainMessagesDeliveryProof<HashOf<BridgedChainOf<R, MI>>, LaneIdOf<R, MI>>
 where
 	R: pallet_bridge_parachains::Config<PI> + pallet_bridge_messages::Config<MI>,
 	PI: 'static,
@@ -213,6 +220,7 @@ where
 	let (state_root, storage_proof) = prepare_message_delivery_storage_proof::<
 		BridgedChainOf<R, MI>,
 		ThisChainOf<R, MI>,
+		LaneIdOf<R, MI>,
 	>(params.lane, params.inbound_lane_data, params.proof_params);
 
 	// update runtime storage
diff --git a/bridges/bin/runtime-common/src/mock.rs b/bridges/bin/runtime-common/src/mock.rs
index fed0d15cc080b78381d3e2ad6bfe8a768139101d..1d4043fc4b61b257ca559b3454eeed63b882ca62 100644
--- a/bridges/bin/runtime-common/src/mock.rs
+++ b/bridges/bin/runtime-common/src/mock.rs
@@ -21,7 +21,7 @@
 use bp_header_chain::ChainWithGrandpa;
 use bp_messages::{
 	target_chain::{DispatchMessage, MessageDispatch},
-	ChainWithMessages, LaneId, MessageNonce,
+	ChainWithMessages, HashedLaneId, LaneIdType, MessageNonce,
 };
 use bp_parachains::SingleParaStoredHeaderDataBuilder;
 use bp_relayers::PayRewardFromAccount;
@@ -70,7 +70,7 @@ pub type BridgedChainHeader =
 	sp_runtime::generic::Header<BridgedChainBlockNumber, BridgedChainHasher>;
 
 /// Rewards payment procedure.
-pub type TestPaymentProcedure = PayRewardFromAccount<Balances, ThisChainAccountId>;
+pub type TestPaymentProcedure = PayRewardFromAccount<Balances, ThisChainAccountId, TestLaneIdType>;
 /// Stake that we are using in tests.
 pub type TestStake = ConstU64<5_000>;
 /// Stake and slash mechanism to use in tests.
@@ -83,10 +83,11 @@ pub type TestStakeAndSlash = pallet_bridge_relayers::StakeAndSlashNamed<
 	ConstU32<8>,
 >;
 
-/// Message lane used in tests.
-#[allow(unused)]
-pub fn test_lane_id() -> LaneId {
-	LaneId::new(1, 2)
+/// Lane identifier type used for tests.
+pub type TestLaneIdType = HashedLaneId;
+/// Lane that we're using in tests.
+pub fn test_lane_id() -> TestLaneIdType {
+	TestLaneIdType::try_new(1, 2).unwrap()
 }
 
 /// Bridged chain id used in tests.
@@ -189,10 +190,10 @@ impl pallet_bridge_messages::Config for TestRuntime {
 	type WeightInfo = pallet_bridge_messages::weights::BridgeWeight<TestRuntime>;
 
 	type OutboundPayload = Vec<u8>;
-
 	type InboundPayload = Vec<u8>;
-	type DeliveryPayments = ();
+	type LaneId = TestLaneIdType;
 
+	type DeliveryPayments = ();
 	type DeliveryConfirmationPayments = pallet_bridge_relayers::DeliveryConfirmationPaymentsAdapter<
 		TestRuntime,
 		(),
@@ -213,13 +214,14 @@ impl pallet_bridge_relayers::Config for TestRuntime {
 	type PaymentProcedure = TestPaymentProcedure;
 	type StakeAndSlash = TestStakeAndSlash;
 	type WeightInfo = ();
+	type LaneId = TestLaneIdType;
 }
 
 /// Dummy message dispatcher.
 pub struct DummyMessageDispatch;
 
 impl DummyMessageDispatch {
-	pub fn deactivate(lane: LaneId) {
+	pub fn deactivate(lane: TestLaneIdType) {
 		frame_support::storage::unhashed::put(&(b"inactive", lane).encode()[..], &false);
 	}
 }
@@ -227,18 +229,21 @@ impl DummyMessageDispatch {
 impl MessageDispatch for DummyMessageDispatch {
 	type DispatchPayload = Vec<u8>;
 	type DispatchLevelResult = ();
+	type LaneId = TestLaneIdType;
 
-	fn is_active(lane: LaneId) -> bool {
+	fn is_active(lane: Self::LaneId) -> bool {
 		frame_support::storage::unhashed::take::<bool>(&(b"inactive", lane).encode()[..]) !=
 			Some(false)
 	}
 
-	fn dispatch_weight(_message: &mut DispatchMessage<Self::DispatchPayload>) -> Weight {
+	fn dispatch_weight(
+		_message: &mut DispatchMessage<Self::DispatchPayload, Self::LaneId>,
+	) -> Weight {
 		Weight::zero()
 	}
 
 	fn dispatch(
-		_: DispatchMessage<Self::DispatchPayload>,
+		_: DispatchMessage<Self::DispatchPayload, Self::LaneId>,
 	) -> MessageDispatchResult<Self::DispatchLevelResult> {
 		MessageDispatchResult { unspent_weight: Weight::zero(), dispatch_level_result: () }
 	}
diff --git a/bridges/chains/chain-bridge-hub-kusama/src/lib.rs b/bridges/chains/chain-bridge-hub-kusama/src/lib.rs
index c990e8a12f367cafbd35b0693b323a6ec5fb5e96..485fb3d31f2001a0303c8aa7165c50bab7acbbbf 100644
--- a/bridges/chains/chain-bridge-hub-kusama/src/lib.rs
+++ b/bridges/chains/chain-bridge-hub-kusama/src/lib.rs
@@ -93,4 +93,4 @@ pub const WITH_BRIDGE_HUB_KUSAMA_MESSAGES_PALLET_NAME: &str = "BridgeKusamaMessa
 pub const WITH_BRIDGE_HUB_KUSAMA_RELAYERS_PALLET_NAME: &str = "BridgeRelayers";
 
 decl_bridge_finality_runtime_apis!(bridge_hub_kusama);
-decl_bridge_messages_runtime_apis!(bridge_hub_kusama);
+decl_bridge_messages_runtime_apis!(bridge_hub_kusama, LegacyLaneId);
diff --git a/bridges/chains/chain-bridge-hub-polkadot/src/lib.rs b/bridges/chains/chain-bridge-hub-polkadot/src/lib.rs
index 7379b8863b1de5c1a1482db90077e958f0a33366..7a1793b4da4a46680fe5b54c3b4c311635a47786 100644
--- a/bridges/chains/chain-bridge-hub-polkadot/src/lib.rs
+++ b/bridges/chains/chain-bridge-hub-polkadot/src/lib.rs
@@ -85,4 +85,4 @@ pub const WITH_BRIDGE_HUB_POLKADOT_MESSAGES_PALLET_NAME: &str = "BridgePolkadotM
 pub const WITH_BRIDGE_HUB_POLKADOT_RELAYERS_PALLET_NAME: &str = "BridgeRelayers";
 
 decl_bridge_finality_runtime_apis!(bridge_hub_polkadot);
-decl_bridge_messages_runtime_apis!(bridge_hub_polkadot);
+decl_bridge_messages_runtime_apis!(bridge_hub_polkadot, LegacyLaneId);
diff --git a/bridges/chains/chain-bridge-hub-rococo/src/lib.rs b/bridges/chains/chain-bridge-hub-rococo/src/lib.rs
index 7920eb93403376159c7520dcca31295116193d08..538bc44019f5226f56b2f07b8cca47451b93e678 100644
--- a/bridges/chains/chain-bridge-hub-rococo/src/lib.rs
+++ b/bridges/chains/chain-bridge-hub-rococo/src/lib.rs
@@ -99,7 +99,7 @@ pub const WITH_BRIDGE_ROCOCO_TO_WESTEND_MESSAGES_PALLET_INDEX: u8 = 51;
 pub const WITH_BRIDGE_ROCOCO_TO_BULLETIN_MESSAGES_PALLET_INDEX: u8 = 61;
 
 decl_bridge_finality_runtime_apis!(bridge_hub_rococo);
-decl_bridge_messages_runtime_apis!(bridge_hub_rococo);
+decl_bridge_messages_runtime_apis!(bridge_hub_rococo, LegacyLaneId);
 
 frame_support::parameter_types! {
 	/// The XCM fee that is paid for executing XCM program (with `ExportMessage` instruction) at the Rococo
diff --git a/bridges/chains/chain-bridge-hub-westend/src/lib.rs b/bridges/chains/chain-bridge-hub-westend/src/lib.rs
index 644fa64c687b3162f1380cf3e666373226c0a16d..7a213fdb28c82df5212a86669b480fe371cf8837 100644
--- a/bridges/chains/chain-bridge-hub-westend/src/lib.rs
+++ b/bridges/chains/chain-bridge-hub-westend/src/lib.rs
@@ -88,7 +88,7 @@ pub const WITH_BRIDGE_HUB_WESTEND_RELAYERS_PALLET_NAME: &str = "BridgeRelayers";
 pub const WITH_BRIDGE_WESTEND_TO_ROCOCO_MESSAGES_PALLET_INDEX: u8 = 44;
 
 decl_bridge_finality_runtime_apis!(bridge_hub_westend);
-decl_bridge_messages_runtime_apis!(bridge_hub_westend);
+decl_bridge_messages_runtime_apis!(bridge_hub_westend, LegacyLaneId);
 
 frame_support::parameter_types! {
 	/// The XCM fee that is paid for executing XCM program (with `ExportMessage` instruction) at the Westend
diff --git a/bridges/chains/chain-polkadot-bulletin/src/lib.rs b/bridges/chains/chain-polkadot-bulletin/src/lib.rs
index 88980a9575016bd5c5e1428329454e8131a2075d..d009369197213da84aca80b9263f82ae4407ca0f 100644
--- a/bridges/chains/chain-polkadot-bulletin/src/lib.rs
+++ b/bridges/chains/chain-polkadot-bulletin/src/lib.rs
@@ -228,4 +228,4 @@ impl ChainWithMessages for PolkadotBulletin {
 }
 
 decl_bridge_finality_runtime_apis!(polkadot_bulletin, grandpa);
-decl_bridge_messages_runtime_apis!(polkadot_bulletin);
+decl_bridge_messages_runtime_apis!(polkadot_bulletin, bp_messages::HashedLaneId);
diff --git a/bridges/modules/messages/src/benchmarking.rs b/bridges/modules/messages/src/benchmarking.rs
index b3a4447fb0211737cc5410a11c87dca9e12d8d80..355fb08ab28ae4eb063ee8bc1a826b5cf8a2654b 100644
--- a/bridges/modules/messages/src/benchmarking.rs
+++ b/bridges/modules/messages/src/benchmarking.rs
@@ -26,7 +26,7 @@ use crate::{
 use bp_messages::{
 	source_chain::FromBridgedChainMessagesDeliveryProof,
 	target_chain::FromBridgedChainMessagesProof, ChainWithMessages, DeliveredMessages,
-	InboundLaneData, LaneId, LaneState, MessageNonce, OutboundLaneData, UnrewardedRelayer,
+	InboundLaneData, LaneState, MessageNonce, OutboundLaneData, UnrewardedRelayer,
 	UnrewardedRelayersState,
 };
 use bp_runtime::{AccountIdOf, HashOf, UnverifiedStorageProofParams};
@@ -44,7 +44,7 @@ pub struct Pallet<T: Config<I>, I: 'static = ()>(crate::Pallet<T, I>);
 
 /// Benchmark-specific message proof parameters.
 #[derive(Debug)]
-pub struct MessageProofParams {
+pub struct MessageProofParams<LaneId> {
 	/// Id of the lane.
 	pub lane: LaneId,
 	/// Range of messages to include in the proof.
@@ -62,7 +62,7 @@ pub struct MessageProofParams {
 
 /// Benchmark-specific message delivery proof parameters.
 #[derive(Debug)]
-pub struct MessageDeliveryProofParams<ThisChainAccountId> {
+pub struct MessageDeliveryProofParams<ThisChainAccountId, LaneId> {
 	/// Id of the lane.
 	pub lane: LaneId,
 	/// The proof needs to include this inbound lane data.
@@ -74,8 +74,8 @@ pub struct MessageDeliveryProofParams<ThisChainAccountId> {
 /// Trait that must be implemented by runtime.
 pub trait Config<I: 'static>: crate::Config<I> {
 	/// Lane id to use in benchmarks.
-	fn bench_lane_id() -> LaneId {
-		LaneId::new(1, 2)
+	fn bench_lane_id() -> Self::LaneId {
+		Self::LaneId::default()
 	}
 
 	/// Return id of relayer account at the bridged chain.
@@ -94,12 +94,12 @@ pub trait Config<I: 'static>: crate::Config<I> {
 
 	/// Prepare messages proof to receive by the module.
 	fn prepare_message_proof(
-		params: MessageProofParams,
-	) -> (FromBridgedChainMessagesProof<HashOf<BridgedChainOf<Self, I>>>, Weight);
+		params: MessageProofParams<Self::LaneId>,
+	) -> (FromBridgedChainMessagesProof<HashOf<BridgedChainOf<Self, I>>, Self::LaneId>, Weight);
 	/// Prepare messages delivery proof to receive by the module.
 	fn prepare_message_delivery_proof(
-		params: MessageDeliveryProofParams<Self::AccountId>,
-	) -> FromBridgedChainMessagesDeliveryProof<HashOf<BridgedChainOf<Self, I>>>;
+		params: MessageDeliveryProofParams<Self::AccountId, Self::LaneId>,
+	) -> FromBridgedChainMessagesDeliveryProof<HashOf<BridgedChainOf<Self, I>>, Self::LaneId>;
 
 	/// Returns true if message has been successfully dispatched or not.
 	fn is_message_successfully_dispatched(_nonce: MessageNonce) -> bool {
diff --git a/bridges/modules/messages/src/call_ext.rs b/bridges/modules/messages/src/call_ext.rs
index 8e021c8e5e242a6b89873b6178bdee6279d3b626..9e5f5f8d1129ed249b485f6c7662890deb1e8a4c 100644
--- a/bridges/modules/messages/src/call_ext.rs
+++ b/bridges/modules/messages/src/call_ext.rs
@@ -20,8 +20,8 @@ use crate::{BridgedChainOf, Config, InboundLanes, OutboundLanes, Pallet, LOG_TAR
 
 use bp_messages::{
 	target_chain::MessageDispatch, BaseMessagesProofInfo, ChainWithMessages, InboundLaneData,
-	LaneId, MessageNonce, MessagesCallInfo, ReceiveMessagesDeliveryProofInfo,
-	ReceiveMessagesProofInfo, UnrewardedRelayerOccupation,
+	MessageNonce, MessagesCallInfo, ReceiveMessagesDeliveryProofInfo, ReceiveMessagesProofInfo,
+	UnrewardedRelayerOccupation,
 };
 use bp_runtime::{AccountIdOf, OwnedBridgeModule};
 use frame_support::{dispatch::CallableCallFor, traits::IsSubType};
@@ -39,7 +39,7 @@ impl<T: Config<I>, I: 'static> CallHelper<T, I> {
 	///
 	/// - call is `receive_messages_delivery_proof` and all messages confirmations have been
 	///   received.
-	pub fn was_successful(info: &MessagesCallInfo) -> bool {
+	pub fn was_successful(info: &MessagesCallInfo<T::LaneId>) -> bool {
 		match info {
 			MessagesCallInfo::ReceiveMessagesProof(info) => {
 				let inbound_lane_data = match InboundLanes::<T, I>::get(info.base.lane_id) {
@@ -75,19 +75,21 @@ pub trait CallSubType<T: Config<I, RuntimeCall = Self>, I: 'static>:
 	IsSubType<CallableCallFor<Pallet<T, I>, T>>
 {
 	/// Create a new instance of `ReceiveMessagesProofInfo` from a `ReceiveMessagesProof` call.
-	fn receive_messages_proof_info(&self) -> Option<ReceiveMessagesProofInfo>;
+	fn receive_messages_proof_info(&self) -> Option<ReceiveMessagesProofInfo<T::LaneId>>;
 
 	/// Create a new instance of `ReceiveMessagesDeliveryProofInfo` from
 	/// a `ReceiveMessagesDeliveryProof` call.
-	fn receive_messages_delivery_proof_info(&self) -> Option<ReceiveMessagesDeliveryProofInfo>;
+	fn receive_messages_delivery_proof_info(
+		&self,
+	) -> Option<ReceiveMessagesDeliveryProofInfo<T::LaneId>>;
 
 	/// Create a new instance of `MessagesCallInfo` from a `ReceiveMessagesProof`
 	/// or a `ReceiveMessagesDeliveryProof` call.
-	fn call_info(&self) -> Option<MessagesCallInfo>;
+	fn call_info(&self) -> Option<MessagesCallInfo<T::LaneId>>;
 
 	/// Create a new instance of `MessagesCallInfo` from a `ReceiveMessagesProof`
 	/// or a `ReceiveMessagesDeliveryProof` call, if the call is for the provided lane.
-	fn call_info_for(&self, lane_id: LaneId) -> Option<MessagesCallInfo>;
+	fn call_info_for(&self, lane_id: T::LaneId) -> Option<MessagesCallInfo<T::LaneId>>;
 
 	/// Ensures that a `ReceiveMessagesProof` or a `ReceiveMessagesDeliveryProof` call:
 	///
@@ -114,7 +116,7 @@ impl<
 		I: 'static,
 	> CallSubType<T, I> for T::RuntimeCall
 {
-	fn receive_messages_proof_info(&self) -> Option<ReceiveMessagesProofInfo> {
+	fn receive_messages_proof_info(&self) -> Option<ReceiveMessagesProofInfo<T::LaneId>> {
 		if let Some(crate::Call::<T, I>::receive_messages_proof { ref proof, .. }) =
 			self.is_sub_type()
 		{
@@ -135,7 +137,9 @@ impl<
 		None
 	}
 
-	fn receive_messages_delivery_proof_info(&self) -> Option<ReceiveMessagesDeliveryProofInfo> {
+	fn receive_messages_delivery_proof_info(
+		&self,
+	) -> Option<ReceiveMessagesDeliveryProofInfo<T::LaneId>> {
 		if let Some(crate::Call::<T, I>::receive_messages_delivery_proof {
 			ref proof,
 			ref relayers_state,
@@ -159,7 +163,7 @@ impl<
 		None
 	}
 
-	fn call_info(&self) -> Option<MessagesCallInfo> {
+	fn call_info(&self) -> Option<MessagesCallInfo<T::LaneId>> {
 		if let Some(info) = self.receive_messages_proof_info() {
 			return Some(MessagesCallInfo::ReceiveMessagesProof(info))
 		}
@@ -171,7 +175,7 @@ impl<
 		None
 	}
 
-	fn call_info_for(&self, lane_id: LaneId) -> Option<MessagesCallInfo> {
+	fn call_info_for(&self, lane_id: T::LaneId) -> Option<MessagesCallInfo<T::LaneId>> {
 		self.call_info().filter(|info| {
 			let actual_lane_id = match info {
 				MessagesCallInfo::ReceiveMessagesProof(info) => info.base.lane_id,
@@ -251,10 +255,6 @@ mod tests {
 	};
 	use sp_std::ops::RangeInclusive;
 
-	fn test_lane_id() -> LaneId {
-		LaneId::new(1, 2)
-	}
-
 	fn fill_unrewarded_relayers() {
 		let mut inbound_lane_state = InboundLanes::<TestRuntime>::get(test_lane_id()).unwrap();
 		for n in 0..BridgedChain::MAX_UNREWARDED_RELAYERS_IN_CONFIRMATION_TX {
diff --git a/bridges/modules/messages/src/inbound_lane.rs b/bridges/modules/messages/src/inbound_lane.rs
index 65240feb7194aaef83ed5e2268f3fa44a4f739a6..91f1159f8f9152e0ba7025762eeafb06999b5acb 100644
--- a/bridges/modules/messages/src/inbound_lane.rs
+++ b/bridges/modules/messages/src/inbound_lane.rs
@@ -20,8 +20,8 @@ use crate::{BridgedChainOf, Config};
 
 use bp_messages::{
 	target_chain::{DispatchMessage, DispatchMessageData, MessageDispatch},
-	ChainWithMessages, DeliveredMessages, InboundLaneData, LaneId, LaneState, MessageKey,
-	MessageNonce, OutboundLaneData, ReceptionResult, UnrewardedRelayer,
+	ChainWithMessages, DeliveredMessages, InboundLaneData, LaneState, MessageKey, MessageNonce,
+	OutboundLaneData, ReceptionResult, UnrewardedRelayer,
 };
 use bp_runtime::AccountIdOf;
 use codec::{Decode, Encode, EncodeLike, MaxEncodedLen};
@@ -33,9 +33,11 @@ use sp_std::prelude::PartialEq;
 pub trait InboundLaneStorage {
 	/// Id of relayer on source chain.
 	type Relayer: Clone + PartialEq;
+	/// Lane identifier type.
+	type LaneId: Encode;
 
 	/// Lane id.
-	fn id(&self) -> LaneId;
+	fn id(&self) -> Self::LaneId;
 	/// Return maximal number of unrewarded relayer entries in inbound lane.
 	fn max_unrewarded_relayer_entries(&self) -> MessageNonce;
 	/// Return maximal number of unconfirmed messages in inbound lane.
@@ -181,7 +183,7 @@ impl<S: InboundLaneStorage> InboundLane<S> {
 	}
 
 	/// Receive new message.
-	pub fn receive_message<Dispatch: MessageDispatch>(
+	pub fn receive_message<Dispatch: MessageDispatch<LaneId = S::LaneId>>(
 		&mut self,
 		relayer_at_bridged_chain: &S::Relayer,
 		nonce: MessageNonce,
diff --git a/bridges/modules/messages/src/lanes_manager.rs b/bridges/modules/messages/src/lanes_manager.rs
index 4f5ac1c0a40325d9f15279bc84a3753e185b92fc..27cab48535d7bc354d31a7f11ac616edf7e879d1 100644
--- a/bridges/modules/messages/src/lanes_manager.rs
+++ b/bridges/modules/messages/src/lanes_manager.rs
@@ -21,8 +21,8 @@ use crate::{
 };
 
 use bp_messages::{
-	target_chain::MessageDispatch, ChainWithMessages, InboundLaneData, LaneId, LaneState,
-	MessageKey, MessageNonce, OutboundLaneData,
+	target_chain::MessageDispatch, ChainWithMessages, InboundLaneData, LaneState, MessageKey,
+	MessageNonce, OutboundLaneData,
 };
 use bp_runtime::AccountIdOf;
 use codec::{Decode, Encode, MaxEncodedLen};
@@ -68,7 +68,7 @@ impl<T: Config<I>, I: 'static> LanesManager<T, I> {
 	/// Create new inbound lane in `Opened` state.
 	pub fn create_inbound_lane(
 		&self,
-		lane_id: LaneId,
+		lane_id: T::LaneId,
 	) -> Result<InboundLane<RuntimeInboundLaneStorage<T, I>>, LanesManagerError> {
 		InboundLanes::<T, I>::try_mutate(lane_id, |lane| match lane {
 			Some(_) => Err(LanesManagerError::InboundLaneAlreadyExists),
@@ -87,7 +87,7 @@ impl<T: Config<I>, I: 'static> LanesManager<T, I> {
 	/// Create new outbound lane in `Opened` state.
 	pub fn create_outbound_lane(
 		&self,
-		lane_id: LaneId,
+		lane_id: T::LaneId,
 	) -> Result<OutboundLane<RuntimeOutboundLaneStorage<T, I>>, LanesManagerError> {
 		OutboundLanes::<T, I>::try_mutate(lane_id, |lane| match lane {
 			Some(_) => Err(LanesManagerError::OutboundLaneAlreadyExists),
@@ -103,7 +103,7 @@ impl<T: Config<I>, I: 'static> LanesManager<T, I> {
 	/// Get existing inbound lane, checking that it is in usable state.
 	pub fn active_inbound_lane(
 		&self,
-		lane_id: LaneId,
+		lane_id: T::LaneId,
 	) -> Result<InboundLane<RuntimeInboundLaneStorage<T, I>>, LanesManagerError> {
 		Ok(InboundLane::new(RuntimeInboundLaneStorage::from_lane_id(lane_id, true)?))
 	}
@@ -111,7 +111,7 @@ impl<T: Config<I>, I: 'static> LanesManager<T, I> {
 	/// Get existing outbound lane, checking that it is in usable state.
 	pub fn active_outbound_lane(
 		&self,
-		lane_id: LaneId,
+		lane_id: T::LaneId,
 	) -> Result<OutboundLane<RuntimeOutboundLaneStorage<T, I>>, LanesManagerError> {
 		Ok(OutboundLane::new(RuntimeOutboundLaneStorage::from_lane_id(lane_id, true)?))
 	}
@@ -119,7 +119,7 @@ impl<T: Config<I>, I: 'static> LanesManager<T, I> {
 	/// Get existing inbound lane without any additional state checks.
 	pub fn any_state_inbound_lane(
 		&self,
-		lane_id: LaneId,
+		lane_id: T::LaneId,
 	) -> Result<InboundLane<RuntimeInboundLaneStorage<T, I>>, LanesManagerError> {
 		Ok(InboundLane::new(RuntimeInboundLaneStorage::from_lane_id(lane_id, false)?))
 	}
@@ -127,7 +127,7 @@ impl<T: Config<I>, I: 'static> LanesManager<T, I> {
 	/// Get existing outbound lane without any additional state checks.
 	pub fn any_state_outbound_lane(
 		&self,
-		lane_id: LaneId,
+		lane_id: T::LaneId,
 	) -> Result<OutboundLane<RuntimeOutboundLaneStorage<T, I>>, LanesManagerError> {
 		Ok(OutboundLane::new(RuntimeOutboundLaneStorage::from_lane_id(lane_id, false)?))
 	}
@@ -135,14 +135,14 @@ impl<T: Config<I>, I: 'static> LanesManager<T, I> {
 
 /// Runtime inbound lane storage.
 pub struct RuntimeInboundLaneStorage<T: Config<I>, I: 'static = ()> {
-	pub(crate) lane_id: LaneId,
+	pub(crate) lane_id: T::LaneId,
 	pub(crate) cached_data: InboundLaneData<AccountIdOf<BridgedChainOf<T, I>>>,
 }
 
 impl<T: Config<I>, I: 'static> RuntimeInboundLaneStorage<T, I> {
 	/// Creates new runtime inbound lane storage for given **existing** lane.
 	fn from_lane_id(
-		lane_id: LaneId,
+		lane_id: T::LaneId,
 		check_active: bool,
 	) -> Result<RuntimeInboundLaneStorage<T, I>, LanesManagerError> {
 		let cached_data =
@@ -196,8 +196,9 @@ impl<T: Config<I>, I: 'static> RuntimeInboundLaneStorage<T, I> {
 
 impl<T: Config<I>, I: 'static> InboundLaneStorage for RuntimeInboundLaneStorage<T, I> {
 	type Relayer = AccountIdOf<BridgedChainOf<T, I>>;
+	type LaneId = T::LaneId;
 
-	fn id(&self) -> LaneId {
+	fn id(&self) -> Self::LaneId {
 		self.lane_id
 	}
 
@@ -225,15 +226,15 @@ impl<T: Config<I>, I: 'static> InboundLaneStorage for RuntimeInboundLaneStorage<
 
 /// Runtime outbound lane storage.
 #[derive(Debug, PartialEq, Eq)]
-pub struct RuntimeOutboundLaneStorage<T, I = ()> {
-	pub(crate) lane_id: LaneId,
+pub struct RuntimeOutboundLaneStorage<T: Config<I>, I: 'static> {
+	pub(crate) lane_id: T::LaneId,
 	pub(crate) cached_data: OutboundLaneData,
 	pub(crate) _phantom: PhantomData<(T, I)>,
 }
 
 impl<T: Config<I>, I: 'static> RuntimeOutboundLaneStorage<T, I> {
 	/// Creates new runtime outbound lane storage for given **existing** lane.
-	fn from_lane_id(lane_id: LaneId, check_active: bool) -> Result<Self, LanesManagerError> {
+	fn from_lane_id(lane_id: T::LaneId, check_active: bool) -> Result<Self, LanesManagerError> {
 		let cached_data =
 			OutboundLanes::<T, I>::get(lane_id).ok_or(LanesManagerError::UnknownOutboundLane)?;
 		ensure!(
@@ -246,8 +247,9 @@ impl<T: Config<I>, I: 'static> RuntimeOutboundLaneStorage<T, I> {
 
 impl<T: Config<I>, I: 'static> OutboundLaneStorage for RuntimeOutboundLaneStorage<T, I> {
 	type StoredMessagePayload = StoredMessagePayload<T, I>;
+	type LaneId = T::LaneId;
 
-	fn id(&self) -> LaneId {
+	fn id(&self) -> Self::LaneId {
 		self.lane_id
 	}
 
diff --git a/bridges/modules/messages/src/lib.rs b/bridges/modules/messages/src/lib.rs
index b7fe1c7dbb19f029de81bd2cd42548a612e1980a..af14257db99c1cf0882f4f1a5a94329b194d1977 100644
--- a/bridges/modules/messages/src/lib.rs
+++ b/bridges/modules/messages/src/lib.rs
@@ -60,9 +60,9 @@ use bp_messages::{
 		DeliveryPayments, DispatchMessage, FromBridgedChainMessagesProof, MessageDispatch,
 		ProvedLaneMessages, ProvedMessages,
 	},
-	ChainWithMessages, DeliveredMessages, InboundLaneData, InboundMessageDetails, LaneId,
-	MessageKey, MessageNonce, MessagePayload, MessagesOperatingMode, OutboundLaneData,
-	OutboundMessageDetails, UnrewardedRelayersState, VerificationError,
+	ChainWithMessages, DeliveredMessages, InboundLaneData, InboundMessageDetails, MessageKey,
+	MessageNonce, MessagePayload, MessagesOperatingMode, OutboundLaneData, OutboundMessageDetails,
+	UnrewardedRelayersState, VerificationError,
 };
 use bp_runtime::{
 	AccountIdOf, BasicOperatingMode, HashOf, OwnedBridgeModule, PreComputedSize, RangeInclusiveExt,
@@ -97,7 +97,7 @@ pub const LOG_TARGET: &str = "runtime::bridge-messages";
 #[frame_support::pallet]
 pub mod pallet {
 	use super::*;
-	use bp_messages::{ReceivedMessages, ReceptionResult};
+	use bp_messages::{LaneIdType, ReceivedMessages, ReceptionResult};
 	use bp_runtime::RangeInclusiveExt;
 	use frame_support::pallet_prelude::*;
 	use frame_system::pallet_prelude::*;
@@ -123,17 +123,25 @@ pub mod pallet {
 		type OutboundPayload: Parameter + Size;
 		/// Payload type of inbound messages. This payload is dispatched on this chain.
 		type InboundPayload: Decode;
+		/// Lane identifier type.
+		type LaneId: LaneIdType;
 
 		/// Handler for relayer payments that happen during message delivery transaction.
 		type DeliveryPayments: DeliveryPayments<Self::AccountId>;
 		/// Handler for relayer payments that happen during message delivery confirmation
 		/// transaction.
-		type DeliveryConfirmationPayments: DeliveryConfirmationPayments<Self::AccountId>;
+		type DeliveryConfirmationPayments: DeliveryConfirmationPayments<
+			Self::AccountId,
+			Self::LaneId,
+		>;
 		/// Delivery confirmation callback.
-		type OnMessagesDelivered: OnMessagesDelivered;
+		type OnMessagesDelivered: OnMessagesDelivered<Self::LaneId>;
 
 		/// Message dispatch handler.
-		type MessageDispatch: MessageDispatch<DispatchPayload = Self::InboundPayload>;
+		type MessageDispatch: MessageDispatch<
+			DispatchPayload = Self::InboundPayload,
+			LaneId = Self::LaneId,
+		>;
 	}
 
 	/// Shortcut to this chain type for Config.
@@ -142,6 +150,8 @@ pub mod pallet {
 	pub type BridgedChainOf<T, I> = <T as Config<I>>::BridgedChain;
 	/// Shortcut to bridged header chain type for Config.
 	pub type BridgedHeaderChainOf<T, I> = <T as Config<I>>::BridgedHeaderChain;
+	/// Shortcut to lane identifier type for Config.
+	pub type LaneIdOf<T, I> = <T as Config<I>>::LaneId;
 
 	#[pallet::pallet]
 	#[pallet::storage_version(migration::STORAGE_VERSION)]
@@ -203,7 +213,7 @@ pub mod pallet {
 		pub fn receive_messages_proof(
 			origin: OriginFor<T>,
 			relayer_id_at_bridged_chain: AccountIdOf<BridgedChainOf<T, I>>,
-			proof: Box<FromBridgedChainMessagesProof<HashOf<BridgedChainOf<T, I>>>>,
+			proof: Box<FromBridgedChainMessagesProof<HashOf<BridgedChainOf<T, I>>, T::LaneId>>,
 			messages_count: u32,
 			dispatch_weight: Weight,
 		) -> DispatchResultWithPostInfo {
@@ -350,7 +360,7 @@ pub mod pallet {
 		))]
 		pub fn receive_messages_delivery_proof(
 			origin: OriginFor<T>,
-			proof: FromBridgedChainMessagesDeliveryProof<HashOf<BridgedChainOf<T, I>>>,
+			proof: FromBridgedChainMessagesDeliveryProof<HashOf<BridgedChainOf<T, I>>, T::LaneId>,
 			mut relayers_state: UnrewardedRelayersState,
 		) -> DispatchResultWithPostInfo {
 			Self::ensure_not_halted().map_err(Error::<T, I>::BridgeModule)?;
@@ -387,7 +397,7 @@ pub mod pallet {
 				// emit 'delivered' event
 				let received_range = confirmed_messages.begin..=confirmed_messages.end;
 				Self::deposit_event(Event::MessagesDelivered {
-					lane_id,
+					lane_id: lane_id.into(),
 					messages: confirmed_messages,
 				});
 
@@ -441,19 +451,22 @@ pub mod pallet {
 		/// Message has been accepted and is waiting to be delivered.
 		MessageAccepted {
 			/// Lane, which has accepted the message.
-			lane_id: LaneId,
+			lane_id: T::LaneId,
 			/// Nonce of accepted message.
 			nonce: MessageNonce,
 		},
 		/// Messages have been received from the bridged chain.
 		MessagesReceived(
 			/// Result of received messages dispatch.
-			ReceivedMessages<<T::MessageDispatch as MessageDispatch>::DispatchLevelResult>,
+			ReceivedMessages<
+				<T::MessageDispatch as MessageDispatch>::DispatchLevelResult,
+				T::LaneId,
+			>,
 		),
 		/// Messages in the inclusive range have been delivered to the bridged chain.
 		MessagesDelivered {
 			/// Lane for which the delivery has been confirmed.
-			lane_id: LaneId,
+			lane_id: T::LaneId,
 			/// Delivered messages.
 			messages: DeliveredMessages,
 		},
@@ -510,13 +523,13 @@ pub mod pallet {
 	/// Map of lane id => inbound lane data.
 	#[pallet::storage]
 	pub type InboundLanes<T: Config<I>, I: 'static = ()> =
-		StorageMap<_, Blake2_128Concat, LaneId, StoredInboundLaneData<T, I>, OptionQuery>;
+		StorageMap<_, Blake2_128Concat, T::LaneId, StoredInboundLaneData<T, I>, OptionQuery>;
 
 	/// Map of lane id => outbound lane data.
 	#[pallet::storage]
 	pub type OutboundLanes<T: Config<I>, I: 'static = ()> = StorageMap<
 		Hasher = Blake2_128Concat,
-		Key = LaneId,
+		Key = T::LaneId,
 		Value = OutboundLaneData,
 		QueryKind = OptionQuery,
 	>;
@@ -524,7 +537,7 @@ pub mod pallet {
 	/// All queued outbound messages.
 	#[pallet::storage]
 	pub type OutboundMessages<T: Config<I>, I: 'static = ()> =
-		StorageMap<_, Blake2_128Concat, MessageKey, StoredMessagePayload<T, I>>;
+		StorageMap<_, Blake2_128Concat, MessageKey<T::LaneId>, StoredMessagePayload<T, I>>;
 
 	#[pallet::genesis_config]
 	#[derive(DefaultNoBound)]
@@ -534,7 +547,7 @@ pub mod pallet {
 		/// Initial pallet owner.
 		pub owner: Option<T::AccountId>,
 		/// Opened lanes.
-		pub opened_lanes: Vec<LaneId>,
+		pub opened_lanes: Vec<T::LaneId>,
 		/// Dummy marker.
 		#[serde(skip)]
 		pub _phantom: sp_std::marker::PhantomData<I>,
@@ -565,13 +578,16 @@ pub mod pallet {
 
 	impl<T: Config<I>, I: 'static> Pallet<T, I> {
 		/// Get stored data of the outbound message with given nonce.
-		pub fn outbound_message_data(lane: LaneId, nonce: MessageNonce) -> Option<MessagePayload> {
+		pub fn outbound_message_data(
+			lane: T::LaneId,
+			nonce: MessageNonce,
+		) -> Option<MessagePayload> {
 			OutboundMessages::<T, I>::get(MessageKey { lane_id: lane, nonce }).map(Into::into)
 		}
 
 		/// Prepare data, related to given inbound message.
 		pub fn inbound_message_data(
-			lane: LaneId,
+			lane: T::LaneId,
 			payload: MessagePayload,
 			outbound_details: OutboundMessageDetails,
 		) -> InboundMessageDetails {
@@ -585,13 +601,13 @@ pub mod pallet {
 		}
 
 		/// Return outbound lane data.
-		pub fn outbound_lane_data(lane: LaneId) -> Option<OutboundLaneData> {
+		pub fn outbound_lane_data(lane: T::LaneId) -> Option<OutboundLaneData> {
 			OutboundLanes::<T, I>::get(lane)
 		}
 
 		/// Return inbound lane data.
 		pub fn inbound_lane_data(
-			lane: LaneId,
+			lane: T::LaneId,
 		) -> Option<InboundLaneData<AccountIdOf<BridgedChainOf<T, I>>>> {
 			InboundLanes::<T, I>::get(lane).map(|lane| lane.0)
 		}
@@ -654,12 +670,12 @@ pub mod pallet {
 /// to send it on the bridge.
 #[derive(Debug, PartialEq, Eq)]
 pub struct SendMessageArgs<T: Config<I>, I: 'static> {
-	lane_id: LaneId,
+	lane_id: T::LaneId,
 	lane: OutboundLane<RuntimeOutboundLaneStorage<T, I>>,
 	payload: StoredMessagePayload<T, I>,
 }
 
-impl<T, I> bp_messages::source_chain::MessagesBridge<T::OutboundPayload> for Pallet<T, I>
+impl<T, I> bp_messages::source_chain::MessagesBridge<T::OutboundPayload, T::LaneId> for Pallet<T, I>
 where
 	T: Config<I>,
 	I: 'static,
@@ -668,7 +684,7 @@ where
 	type SendMessageArgs = SendMessageArgs<T, I>;
 
 	fn validate_message(
-		lane_id: LaneId,
+		lane_id: T::LaneId,
 		message: &T::OutboundPayload,
 	) -> Result<SendMessageArgs<T, I>, Self::Error> {
 		// we can't accept any messages if the pallet is halted
@@ -703,7 +719,10 @@ where
 			message_len,
 		);
 
-		Pallet::<T, I>::deposit_event(Event::MessageAccepted { lane_id: args.lane_id, nonce });
+		Pallet::<T, I>::deposit_event(Event::MessageAccepted {
+			lane_id: args.lane_id.into(),
+			nonce,
+		});
 
 		SendMessageArtifacts { nonce, enqueued_messages }
 	}
@@ -722,7 +741,7 @@ fn ensure_normal_operating_mode<T: Config<I>, I: 'static>() -> Result<(), Error<
 
 /// Creates new inbound lane object, backed by runtime storage. Lane must be active.
 fn active_inbound_lane<T: Config<I>, I: 'static>(
-	lane_id: LaneId,
+	lane_id: T::LaneId,
 ) -> Result<InboundLane<RuntimeInboundLaneStorage<T, I>>, Error<T, I>> {
 	LanesManager::<T, I>::new()
 		.active_inbound_lane(lane_id)
@@ -731,7 +750,7 @@ fn active_inbound_lane<T: Config<I>, I: 'static>(
 
 /// Creates new outbound lane object, backed by runtime storage. Lane must be active.
 fn active_outbound_lane<T: Config<I>, I: 'static>(
-	lane_id: LaneId,
+	lane_id: T::LaneId,
 ) -> Result<OutboundLane<RuntimeOutboundLaneStorage<T, I>>, Error<T, I>> {
 	LanesManager::<T, I>::new()
 		.active_outbound_lane(lane_id)
@@ -740,7 +759,7 @@ fn active_outbound_lane<T: Config<I>, I: 'static>(
 
 /// Creates new outbound lane object, backed by runtime storage.
 fn any_state_outbound_lane<T: Config<I>, I: 'static>(
-	lane_id: LaneId,
+	lane_id: T::LaneId,
 ) -> Result<OutboundLane<RuntimeOutboundLaneStorage<T, I>>, Error<T, I>> {
 	LanesManager::<T, I>::new()
 		.any_state_outbound_lane(lane_id)
@@ -749,9 +768,12 @@ fn any_state_outbound_lane<T: Config<I>, I: 'static>(
 
 /// Verify messages proof and return proved messages with decoded payload.
 fn verify_and_decode_messages_proof<T: Config<I>, I: 'static>(
-	proof: FromBridgedChainMessagesProof<HashOf<BridgedChainOf<T, I>>>,
+	proof: FromBridgedChainMessagesProof<HashOf<BridgedChainOf<T, I>>, T::LaneId>,
 	messages_count: u32,
-) -> Result<ProvedMessages<DispatchMessage<T::InboundPayload>>, VerificationError> {
+) -> Result<
+	ProvedMessages<T::LaneId, DispatchMessage<T::InboundPayload, T::LaneId>>,
+	VerificationError,
+> {
 	// `receive_messages_proof` weight formula and `MAX_UNCONFIRMED_MESSAGES_IN_CONFIRMATION_TX`
 	// check guarantees that the `message_count` is sane and Vec<Message> may be allocated.
 	// (tx with too many messages will either be rejected from the pool, or will fail earlier)
diff --git a/bridges/modules/messages/src/outbound_lane.rs b/bridges/modules/messages/src/outbound_lane.rs
index f71240ab7c703203d97294e1a0639059413f25cb..c72713e7455a648622e9432a5c0c7bbd29b118b7 100644
--- a/bridges/modules/messages/src/outbound_lane.rs
+++ b/bridges/modules/messages/src/outbound_lane.rs
@@ -19,7 +19,7 @@
 use crate::{Config, LOG_TARGET};
 
 use bp_messages::{
-	ChainWithMessages, DeliveredMessages, LaneId, LaneState, MessageNonce, OutboundLaneData,
+	ChainWithMessages, DeliveredMessages, LaneState, MessageNonce, OutboundLaneData,
 	UnrewardedRelayer,
 };
 use codec::{Decode, Encode};
@@ -32,9 +32,11 @@ use sp_std::{collections::vec_deque::VecDeque, marker::PhantomData, ops::RangeIn
 pub trait OutboundLaneStorage {
 	/// Stored message payload type.
 	type StoredMessagePayload;
+	/// Lane identifier type.
+	type LaneId: Encode;
 
 	/// Lane id.
-	fn id(&self) -> LaneId;
+	fn id(&self) -> Self::LaneId;
 	/// Get lane data from the storage.
 	fn data(&self) -> OutboundLaneData;
 	/// Update lane data in the storage.
diff --git a/bridges/modules/messages/src/proofs.rs b/bridges/modules/messages/src/proofs.rs
index f35eb24d98c5d7ad178c4d2e6791c98c044b7600..dcd642341d7763d52bba1f1023b91ec241aa3c24 100644
--- a/bridges/modules/messages/src/proofs.rs
+++ b/bridges/modules/messages/src/proofs.rs
@@ -22,7 +22,7 @@ use bp_header_chain::{HeaderChain, HeaderChainError};
 use bp_messages::{
 	source_chain::FromBridgedChainMessagesDeliveryProof,
 	target_chain::{FromBridgedChainMessagesProof, ProvedLaneMessages, ProvedMessages},
-	ChainWithMessages, InboundLaneData, LaneId, Message, MessageKey, MessageNonce, MessagePayload,
+	ChainWithMessages, InboundLaneData, Message, MessageKey, MessageNonce, MessagePayload,
 	OutboundLaneData, VerificationError,
 };
 use bp_runtime::{
@@ -32,8 +32,8 @@ use codec::Decode;
 use sp_std::vec::Vec;
 
 /// 'Parsed' message delivery proof - inbound lane id and its state.
-pub(crate) type ParsedMessagesDeliveryProofFromBridgedChain<T> =
-	(LaneId, InboundLaneData<<T as frame_system::Config>::AccountId>);
+pub(crate) type ParsedMessagesDeliveryProofFromBridgedChain<T, I> =
+	(<T as Config<I>>::LaneId, InboundLaneData<<T as frame_system::Config>::AccountId>);
 
 /// Verify proof of Bridged -> This chain messages.
 ///
@@ -44,9 +44,9 @@ pub(crate) type ParsedMessagesDeliveryProofFromBridgedChain<T> =
 /// outside of this function. This function only verifies that the proof declares exactly
 /// `messages_count` messages.
 pub fn verify_messages_proof<T: Config<I>, I: 'static>(
-	proof: FromBridgedChainMessagesProof<HashOf<BridgedChainOf<T, I>>>,
+	proof: FromBridgedChainMessagesProof<HashOf<BridgedChainOf<T, I>>, T::LaneId>,
 	messages_count: u32,
-) -> Result<ProvedMessages<Message>, VerificationError> {
+) -> Result<ProvedMessages<T::LaneId, Message<T::LaneId>>, VerificationError> {
 	let FromBridgedChainMessagesProof {
 		bridged_header_hash,
 		storage_proof,
@@ -103,8 +103,8 @@ pub fn verify_messages_proof<T: Config<I>, I: 'static>(
 
 /// Verify proof of This -> Bridged chain messages delivery.
 pub fn verify_messages_delivery_proof<T: Config<I>, I: 'static>(
-	proof: FromBridgedChainMessagesDeliveryProof<HashOf<BridgedChainOf<T, I>>>,
-) -> Result<ParsedMessagesDeliveryProofFromBridgedChain<T>, VerificationError> {
+	proof: FromBridgedChainMessagesDeliveryProof<HashOf<BridgedChainOf<T, I>>, T::LaneId>,
+) -> Result<ParsedMessagesDeliveryProofFromBridgedChain<T, I>, VerificationError> {
 	let FromBridgedChainMessagesDeliveryProof { bridged_header_hash, storage_proof, lane } = proof;
 	let mut parser: MessagesStorageProofAdapter<T, I> =
 		MessagesStorageProofAdapter::try_new_with_verified_storage_proof(
@@ -143,7 +143,7 @@ trait StorageProofAdapter<T: Config<I>, I: 'static> {
 
 	fn read_and_decode_outbound_lane_data(
 		&mut self,
-		lane_id: &LaneId,
+		lane_id: &T::LaneId,
 	) -> Result<Option<OutboundLaneData>, StorageProofError> {
 		let storage_outbound_lane_data_key = bp_messages::storage_keys::outbound_lane_data_key(
 			T::ThisChain::WITH_CHAIN_MESSAGES_PALLET_NAME,
@@ -154,7 +154,7 @@ trait StorageProofAdapter<T: Config<I>, I: 'static> {
 
 	fn read_and_decode_message_payload(
 		&mut self,
-		message_key: &MessageKey,
+		message_key: &MessageKey<T::LaneId>,
 	) -> Result<MessagePayload, StorageProofError> {
 		let storage_message_key = bp_messages::storage_keys::message_key(
 			T::ThisChain::WITH_CHAIN_MESSAGES_PALLET_NAME,
@@ -229,19 +229,20 @@ mod tests {
 		encode_outbound_lane_data: impl Fn(&OutboundLaneData) -> Vec<u8>,
 		add_duplicate_key: bool,
 		add_unused_key: bool,
-		test: impl Fn(FromBridgedChainMessagesProof<BridgedHeaderHash>) -> R,
+		test: impl Fn(FromBridgedChainMessagesProof<BridgedHeaderHash, TestLaneIdType>) -> R,
 	) -> R {
-		let (state_root, storage_proof) = prepare_messages_storage_proof::<BridgedChain, ThisChain>(
-			test_lane_id(),
-			1..=nonces_end,
-			outbound_lane_data,
-			bp_runtime::UnverifiedStorageProofParams::default(),
-			generate_dummy_message,
-			encode_message,
-			encode_outbound_lane_data,
-			add_duplicate_key,
-			add_unused_key,
-		);
+		let (state_root, storage_proof) =
+			prepare_messages_storage_proof::<BridgedChain, ThisChain, TestLaneIdType>(
+				test_lane_id(),
+				1..=nonces_end,
+				outbound_lane_data,
+				bp_runtime::UnverifiedStorageProofParams::default(),
+				generate_dummy_message,
+				encode_message,
+				encode_outbound_lane_data,
+				add_duplicate_key,
+				add_unused_key,
+			);
 
 		sp_io::TestExternalities::new(Default::default()).execute_with(move || {
 			let bridged_header = BridgedChainHeader::new(
diff --git a/bridges/modules/messages/src/tests/messages_generation.rs b/bridges/modules/messages/src/tests/messages_generation.rs
index 6c4867fa6de39fa97b35cbd1f270bdeff4c76dff..00b1d3eefe43b176b31bf4b13e426a6bcb5319e3 100644
--- a/bridges/modules/messages/src/tests/messages_generation.rs
+++ b/bridges/modules/messages/src/tests/messages_generation.rs
@@ -17,8 +17,8 @@
 //! Helpers for generating message storage proofs, that are used by tests and by benchmarks.
 
 use bp_messages::{
-	storage_keys, ChainWithMessages, InboundLaneData, LaneId, MessageKey, MessageNonce,
-	MessagePayload, OutboundLaneData,
+	storage_keys, ChainWithMessages, InboundLaneData, MessageKey, MessageNonce, MessagePayload,
+	OutboundLaneData,
 };
 use bp_runtime::{
 	grow_storage_value, record_all_trie_keys, AccountIdOf, Chain, HashOf, HasherOf,
@@ -47,7 +47,11 @@ pub fn encode_lane_data(d: &OutboundLaneData) -> Vec<u8> {
 ///
 /// Returns state trie root and nodes with prepared messages.
 #[allow(clippy::too_many_arguments)]
-pub fn prepare_messages_storage_proof<BridgedChain: Chain, ThisChain: ChainWithMessages>(
+pub fn prepare_messages_storage_proof<
+	BridgedChain: Chain,
+	ThisChain: ChainWithMessages,
+	LaneId: Encode + Copy,
+>(
 	lane: LaneId,
 	message_nonces: RangeInclusive<MessageNonce>,
 	outbound_lane_data: Option<OutboundLaneData>,
@@ -132,7 +136,11 @@ where
 /// Prepare storage proof of given messages delivery.
 ///
 /// Returns state trie root and nodes with prepared messages.
-pub fn prepare_message_delivery_storage_proof<BridgedChain: Chain, ThisChain: ChainWithMessages>(
+pub fn prepare_message_delivery_storage_proof<
+	BridgedChain: Chain,
+	ThisChain: ChainWithMessages,
+	LaneId: Encode,
+>(
 	lane: LaneId,
 	inbound_lane_data: InboundLaneData<AccountIdOf<ThisChain>>,
 	proof_params: UnverifiedStorageProofParams,
diff --git a/bridges/modules/messages/src/tests/mock.rs b/bridges/modules/messages/src/tests/mock.rs
index 2caea9813e8277c6989e8815aae280cde993c33c..2935ebd69610f6138c18c22d2c548e2f670487a2 100644
--- a/bridges/modules/messages/src/tests/mock.rs
+++ b/bridges/modules/messages/src/tests/mock.rs
@@ -35,8 +35,9 @@ use bp_messages::{
 		DeliveryPayments, DispatchMessage, DispatchMessageData, FromBridgedChainMessagesProof,
 		MessageDispatch,
 	},
-	ChainWithMessages, DeliveredMessages, InboundLaneData, LaneId, LaneState, Message, MessageKey,
-	MessageNonce, OutboundLaneData, UnrewardedRelayer, UnrewardedRelayersState,
+	ChainWithMessages, DeliveredMessages, HashedLaneId, InboundLaneData, LaneIdType, LaneState,
+	Message, MessageKey, MessageNonce, OutboundLaneData, UnrewardedRelayer,
+	UnrewardedRelayersState,
 };
 use bp_runtime::{
 	messages::MessageDispatchResult, Chain, ChainId, Size, UnverifiedStorageProofParams,
@@ -195,10 +196,10 @@ impl Config for TestRuntime {
 	type BridgedHeaderChain = BridgedChainGrandpa;
 
 	type OutboundPayload = TestPayload;
-
 	type InboundPayload = TestPayload;
-	type DeliveryPayments = TestDeliveryPayments;
+	type LaneId = TestLaneIdType;
 
+	type DeliveryPayments = TestDeliveryPayments;
 	type DeliveryConfirmationPayments = TestDeliveryConfirmationPayments;
 	type OnMessagesDelivered = TestOnMessagesDelivered;
 
@@ -207,13 +208,13 @@ impl Config for TestRuntime {
 
 #[cfg(feature = "runtime-benchmarks")]
 impl crate::benchmarking::Config<()> for TestRuntime {
-	fn bench_lane_id() -> LaneId {
+	fn bench_lane_id() -> Self::LaneId {
 		test_lane_id()
 	}
 
 	fn prepare_message_proof(
-		params: crate::benchmarking::MessageProofParams,
-	) -> (FromBridgedChainMessagesProof<BridgedHeaderHash>, Weight) {
+		params: crate::benchmarking::MessageProofParams<Self::LaneId>,
+	) -> (FromBridgedChainMessagesProof<BridgedHeaderHash, Self::LaneId>, Weight) {
 		use bp_runtime::RangeInclusiveExt;
 
 		let dispatch_weight =
@@ -228,8 +229,8 @@ impl crate::benchmarking::Config<()> for TestRuntime {
 	}
 
 	fn prepare_message_delivery_proof(
-		params: crate::benchmarking::MessageDeliveryProofParams<AccountId>,
-	) -> FromBridgedChainMessagesDeliveryProof<BridgedHeaderHash> {
+		params: crate::benchmarking::MessageDeliveryProofParams<AccountId, Self::LaneId>,
+	) -> FromBridgedChainMessagesDeliveryProof<BridgedHeaderHash, Self::LaneId> {
 		// in mock run we only care about benchmarks correctness, not the benchmark results
 		// => ignore size related arguments
 		prepare_messages_delivery_proof(params.lane, params.inbound_lane_data)
@@ -258,19 +259,21 @@ pub const TEST_RELAYER_B: AccountId = 101;
 /// Account id of additional test relayer - C.
 pub const TEST_RELAYER_C: AccountId = 102;
 
+/// Lane identifier type used for tests.
+pub type TestLaneIdType = HashedLaneId;
 /// Lane that we're using in tests.
-pub fn test_lane_id() -> LaneId {
-	LaneId::new(1, 2)
+pub fn test_lane_id() -> TestLaneIdType {
+	TestLaneIdType::try_new(1, 2).unwrap()
 }
 
 /// Lane that is completely unknown to our runtime.
-pub fn unknown_lane_id() -> LaneId {
-	LaneId::new(1, 3)
+pub fn unknown_lane_id() -> TestLaneIdType {
+	TestLaneIdType::try_new(1, 3).unwrap()
 }
 
 /// Lane that is registered, but it is closed.
-pub fn closed_lane_id() -> LaneId {
-	LaneId::new(1, 4)
+pub fn closed_lane_id() -> TestLaneIdType {
+	TestLaneIdType::try_new(1, 4).unwrap()
 }
 
 /// Regular message payload.
@@ -316,11 +319,11 @@ impl TestDeliveryConfirmationPayments {
 	}
 }
 
-impl DeliveryConfirmationPayments<AccountId> for TestDeliveryConfirmationPayments {
+impl DeliveryConfirmationPayments<AccountId, TestLaneIdType> for TestDeliveryConfirmationPayments {
 	type Error = &'static str;
 
 	fn pay_reward(
-		_lane_id: LaneId,
+		_lane_id: TestLaneIdType,
 		messages_relayers: VecDeque<UnrewardedRelayer<AccountId>>,
 		_confirmation_relayer: &AccountId,
 		received_range: &RangeInclusive<MessageNonce>,
@@ -341,7 +344,7 @@ impl DeliveryConfirmationPayments<AccountId> for TestDeliveryConfirmationPayment
 pub struct TestMessageDispatch;
 
 impl TestMessageDispatch {
-	pub fn deactivate(lane: LaneId) {
+	pub fn deactivate(lane: TestLaneIdType) {
 		// "enqueue" enough (to deactivate dispatcher) messages at dispatcher
 		let latest_received_nonce = BridgedChain::MAX_UNCONFIRMED_MESSAGES_IN_CONFIRMATION_TX + 1;
 		for _ in 1..=latest_received_nonce {
@@ -349,7 +352,7 @@ impl TestMessageDispatch {
 		}
 	}
 
-	pub fn emulate_enqueued_message(lane: LaneId) {
+	pub fn emulate_enqueued_message(lane: TestLaneIdType) {
 		let key = (b"dispatched", lane).encode();
 		let dispatched = frame_support::storage::unhashed::get_or_default::<MessageNonce>(&key[..]);
 		frame_support::storage::unhashed::put(&key[..], &(dispatched + 1));
@@ -359,14 +362,15 @@ impl TestMessageDispatch {
 impl MessageDispatch for TestMessageDispatch {
 	type DispatchPayload = TestPayload;
 	type DispatchLevelResult = TestDispatchLevelResult;
+	type LaneId = TestLaneIdType;
 
-	fn is_active(lane: LaneId) -> bool {
+	fn is_active(lane: Self::LaneId) -> bool {
 		frame_support::storage::unhashed::get_or_default::<MessageNonce>(
 			&(b"dispatched", lane).encode()[..],
 		) <= BridgedChain::MAX_UNCONFIRMED_MESSAGES_IN_CONFIRMATION_TX
 	}
 
-	fn dispatch_weight(message: &mut DispatchMessage<TestPayload>) -> Weight {
+	fn dispatch_weight(message: &mut DispatchMessage<TestPayload, Self::LaneId>) -> Weight {
 		match message.data.payload.as_ref() {
 			Ok(payload) => payload.declared_weight,
 			Err(_) => Weight::zero(),
@@ -374,7 +378,7 @@ impl MessageDispatch for TestMessageDispatch {
 	}
 
 	fn dispatch(
-		message: DispatchMessage<TestPayload>,
+		message: DispatchMessage<TestPayload, Self::LaneId>,
 	) -> MessageDispatchResult<TestDispatchLevelResult> {
 		match message.data.payload.as_ref() {
 			Ok(payload) => {
@@ -390,13 +394,13 @@ impl MessageDispatch for TestMessageDispatch {
 pub struct TestOnMessagesDelivered;
 
 impl TestOnMessagesDelivered {
-	pub fn call_arguments() -> Option<(LaneId, MessageNonce)> {
+	pub fn call_arguments() -> Option<(TestLaneIdType, MessageNonce)> {
 		frame_support::storage::unhashed::get(b"TestOnMessagesDelivered.OnMessagesDelivered")
 	}
 }
 
-impl OnMessagesDelivered for TestOnMessagesDelivered {
-	fn on_messages_delivered(lane: LaneId, enqueued_messages: MessageNonce) {
+impl OnMessagesDelivered<TestLaneIdType> for TestOnMessagesDelivered {
+	fn on_messages_delivered(lane: TestLaneIdType, enqueued_messages: MessageNonce) {
 		frame_support::storage::unhashed::put(
 			b"TestOnMessagesDelivered.OnMessagesDelivered",
 			&(lane, enqueued_messages),
@@ -405,7 +409,7 @@ impl OnMessagesDelivered for TestOnMessagesDelivered {
 }
 
 /// Return test lane message with given nonce and payload.
-pub fn message(nonce: MessageNonce, payload: TestPayload) -> Message {
+pub fn message(nonce: MessageNonce, payload: TestPayload) -> Message<TestLaneIdType> {
 	Message { key: MessageKey { lane_id: test_lane_id(), nonce }, payload: payload.encode() }
 }
 
@@ -449,7 +453,7 @@ pub fn unrewarded_relayer(
 }
 
 /// Returns unrewarded relayers state at given lane.
-pub fn inbound_unrewarded_relayers_state(lane: bp_messages::LaneId) -> UnrewardedRelayersState {
+pub fn inbound_unrewarded_relayers_state(lane: TestLaneIdType) -> UnrewardedRelayersState {
 	let inbound_lane_data = crate::InboundLanes::<TestRuntime, ()>::get(lane).unwrap().0;
 	UnrewardedRelayersState::from(&inbound_lane_data)
 }
@@ -486,24 +490,25 @@ pub fn run_test<T>(test: impl FnOnce() -> T) -> T {
 /// Since this function changes the runtime storage, you can't "inline" it in the
 /// `asset_noop` macro calls.
 pub fn prepare_messages_proof(
-	messages: Vec<Message>,
+	messages: Vec<Message<TestLaneIdType>>,
 	outbound_lane_data: Option<OutboundLaneData>,
-) -> Box<FromBridgedChainMessagesProof<BridgedHeaderHash>> {
+) -> Box<FromBridgedChainMessagesProof<BridgedHeaderHash, TestLaneIdType>> {
 	// first - let's generate storage proof
 	let lane = messages.first().unwrap().key.lane_id;
 	let nonces_start = messages.first().unwrap().key.nonce;
 	let nonces_end = messages.last().unwrap().key.nonce;
-	let (storage_root, storage_proof) = prepare_messages_storage_proof::<BridgedChain, ThisChain>(
-		lane,
-		nonces_start..=nonces_end,
-		outbound_lane_data,
-		UnverifiedStorageProofParams::default(),
-		|nonce| messages[(nonce - nonces_start) as usize].payload.clone(),
-		encode_all_messages,
-		encode_lane_data,
-		false,
-		false,
-	);
+	let (storage_root, storage_proof) =
+		prepare_messages_storage_proof::<BridgedChain, ThisChain, TestLaneIdType>(
+			lane,
+			nonces_start..=nonces_end,
+			outbound_lane_data,
+			UnverifiedStorageProofParams::default(),
+			|nonce| messages[(nonce - nonces_start) as usize].payload.clone(),
+			encode_all_messages,
+			encode_lane_data,
+			false,
+			false,
+		);
 
 	// let's now insert bridged chain header into the storage
 	let bridged_header_hash = Default::default();
@@ -512,7 +517,7 @@ pub fn prepare_messages_proof(
 		StoredHeaderData { number: 0, state_root: storage_root },
 	);
 
-	Box::new(FromBridgedChainMessagesProof::<BridgedHeaderHash> {
+	Box::new(FromBridgedChainMessagesProof::<BridgedHeaderHash, TestLaneIdType> {
 		bridged_header_hash,
 		storage_proof,
 		lane,
@@ -527,12 +532,12 @@ pub fn prepare_messages_proof(
 /// Since this function changes the runtime storage, you can't "inline" it in the
 /// `asset_noop` macro calls.
 pub fn prepare_messages_delivery_proof(
-	lane: LaneId,
+	lane: TestLaneIdType,
 	inbound_lane_data: InboundLaneData<AccountId>,
-) -> FromBridgedChainMessagesDeliveryProof<BridgedHeaderHash> {
+) -> FromBridgedChainMessagesDeliveryProof<BridgedHeaderHash, TestLaneIdType> {
 	// first - let's generate storage proof
 	let (storage_root, storage_proof) =
-		prepare_message_delivery_storage_proof::<BridgedChain, ThisChain>(
+		prepare_message_delivery_storage_proof::<BridgedChain, ThisChain, TestLaneIdType>(
 			lane,
 			inbound_lane_data,
 			UnverifiedStorageProofParams::default(),
@@ -545,7 +550,7 @@ pub fn prepare_messages_delivery_proof(
 		StoredHeaderData { number: 0, state_root: storage_root },
 	);
 
-	FromBridgedChainMessagesDeliveryProof::<BridgedHeaderHash> {
+	FromBridgedChainMessagesDeliveryProof::<BridgedHeaderHash, TestLaneIdType> {
 		bridged_header_hash,
 		storage_proof,
 		lane,
diff --git a/bridges/modules/messages/src/tests/pallet_tests.rs b/bridges/modules/messages/src/tests/pallet_tests.rs
index ceb1744c066588cf41ac6db429cc6bb8503dc7f1..9df103a7cf6f1b6173dd264733d298d4a68ab7df 100644
--- a/bridges/modules/messages/src/tests/pallet_tests.rs
+++ b/bridges/modules/messages/src/tests/pallet_tests.rs
@@ -30,7 +30,7 @@ use bp_messages::{
 	source_chain::{FromBridgedChainMessagesDeliveryProof, MessagesBridge},
 	target_chain::{FromBridgedChainMessagesProof, MessageDispatch},
 	BridgeMessagesCall, ChainWithMessages, DeliveredMessages, InboundLaneData,
-	InboundMessageDetails, LaneId, LaneState, MessageKey, MessageNonce, MessagesOperatingMode,
+	InboundMessageDetails, LaneIdType, LaneState, MessageKey, MessageNonce, MessagesOperatingMode,
 	OutboundLaneData, OutboundMessageDetails, UnrewardedRelayer, UnrewardedRelayersState,
 	VerificationError,
 };
@@ -51,7 +51,7 @@ fn get_ready_for_events() {
 	System::<TestRuntime>::reset_events();
 }
 
-fn send_regular_message(lane_id: LaneId) {
+fn send_regular_message(lane_id: TestLaneIdType) {
 	get_ready_for_events();
 
 	let outbound_lane = active_outbound_lane::<TestRuntime, ()>(lane_id).unwrap();
@@ -67,7 +67,10 @@ fn send_regular_message(lane_id: LaneId) {
 		System::<TestRuntime>::events(),
 		vec![EventRecord {
 			phase: Phase::Initialization,
-			event: TestEvent::Messages(Event::MessageAccepted { lane_id, nonce: message_nonce }),
+			event: TestEvent::Messages(Event::MessageAccepted {
+				lane_id: lane_id.into(),
+				nonce: message_nonce
+			}),
 			topics: vec![],
 		}],
 	);
@@ -105,7 +108,7 @@ fn receive_messages_delivery_proof() {
 		vec![EventRecord {
 			phase: Phase::Initialization,
 			event: TestEvent::Messages(Event::MessagesDelivered {
-				lane_id: test_lane_id(),
+				lane_id: test_lane_id().into(),
 				messages: DeliveredMessages::new(1),
 			}),
 			topics: vec![],
@@ -629,7 +632,7 @@ fn receive_messages_delivery_proof_rewards_relayers() {
 fn receive_messages_delivery_proof_rejects_invalid_proof() {
 	run_test(|| {
 		let mut proof = prepare_messages_delivery_proof(test_lane_id(), Default::default());
-		proof.lane = bp_messages::LaneId::new(42, 84);
+		proof.lane = TestLaneIdType::try_new(42, 84).unwrap();
 
 		assert_noop!(
 			Pallet::<TestRuntime>::receive_messages_delivery_proof(
@@ -1038,8 +1041,8 @@ fn test_bridge_messages_call_is_correctly_defined() {
 		};
 		let indirect_receive_messages_proof_call = BridgeMessagesCall::<
 			AccountId,
-			FromBridgedChainMessagesProof<BridgedHeaderHash>,
-			FromBridgedChainMessagesDeliveryProof<BridgedHeaderHash>,
+			FromBridgedChainMessagesProof<BridgedHeaderHash, TestLaneIdType>,
+			FromBridgedChainMessagesDeliveryProof<BridgedHeaderHash, TestLaneIdType>,
 		>::receive_messages_proof {
 			relayer_id_at_bridged_chain: account_id,
 			proof: *message_proof,
@@ -1058,8 +1061,8 @@ fn test_bridge_messages_call_is_correctly_defined() {
 			};
 		let indirect_receive_messages_delivery_proof_call = BridgeMessagesCall::<
 			AccountId,
-			FromBridgedChainMessagesProof<BridgedHeaderHash>,
-			FromBridgedChainMessagesDeliveryProof<BridgedHeaderHash>,
+			FromBridgedChainMessagesProof<BridgedHeaderHash, TestLaneIdType>,
+			FromBridgedChainMessagesDeliveryProof<BridgedHeaderHash, TestLaneIdType>,
 		>::receive_messages_delivery_proof {
 			proof: message_delivery_proof,
 			relayers_state: unrewarded_relayer_state,
@@ -1084,7 +1087,7 @@ fn inbound_storage_extra_proof_size_bytes_works() {
 
 	fn storage(relayer_entries: usize) -> RuntimeInboundLaneStorage<TestRuntime, ()> {
 		RuntimeInboundLaneStorage {
-			lane_id: LaneId::new(1, 2),
+			lane_id: TestLaneIdType::try_new(1, 2).unwrap(),
 			cached_data: InboundLaneData {
 				state: LaneState::Opened,
 				relayers: vec![relayer_entry(); relayer_entries].into(),
@@ -1165,7 +1168,7 @@ fn receive_messages_proof_fails_if_inbound_lane_is_not_opened() {
 #[test]
 fn receive_messages_delivery_proof_fails_if_outbound_lane_is_unknown() {
 	run_test(|| {
-		let make_proof = |lane: LaneId| {
+		let make_proof = |lane: TestLaneIdType| {
 			prepare_messages_delivery_proof(
 				lane,
 				InboundLaneData {
diff --git a/bridges/modules/relayers/src/benchmarking.rs b/bridges/modules/relayers/src/benchmarking.rs
index 8a3f905a8f291f98ee5832da8817c8e9dd5df1d9..8fe3fc11d6ae67c151119e585a3048149a81ea36 100644
--- a/bridges/modules/relayers/src/benchmarking.rs
+++ b/bridges/modules/relayers/src/benchmarking.rs
@@ -20,9 +20,8 @@
 
 use crate::*;
 
-use bp_messages::LaneId;
 use bp_relayers::RewardsAccountOwner;
-use frame_benchmarking::{benchmarks, whitelisted_caller};
+use frame_benchmarking::{benchmarks_instance_pallet, whitelisted_caller};
 use frame_system::RawOrigin;
 use sp_runtime::traits::One;
 
@@ -30,27 +29,34 @@ use sp_runtime::traits::One;
 const REWARD_AMOUNT: u32 = u32::MAX;
 
 /// Pallet we're benchmarking here.
-pub struct Pallet<T: Config>(crate::Pallet<T>);
+pub struct Pallet<T: Config<I>, I: 'static = ()>(crate::Pallet<T, I>);
 
 /// Trait that must be implemented by runtime.
-pub trait Config: crate::Config {
+pub trait Config<I: 'static = ()>: crate::Config<I> {
+	/// Lane id to use in benchmarks.
+	fn bench_lane_id() -> Self::LaneId {
+		Self::LaneId::default()
+	}
 	/// Prepare environment for paying given reward for serving given lane.
-	fn prepare_rewards_account(account_params: RewardsAccountParams, reward: Self::Reward);
+	fn prepare_rewards_account(
+		account_params: RewardsAccountParams<Self::LaneId>,
+		reward: Self::Reward,
+	);
 	/// Give enough balance to given account.
 	fn deposit_account(account: Self::AccountId, balance: Self::Reward);
 }
 
-benchmarks! {
+benchmarks_instance_pallet! {
 	// Benchmark `claim_rewards` call.
 	claim_rewards {
-		let lane = LaneId::new(1, 2);
+		let lane = T::bench_lane_id();
 		let account_params =
 			RewardsAccountParams::new(lane, *b"test", RewardsAccountOwner::ThisChain);
 		let relayer: T::AccountId = whitelisted_caller();
 		let reward = T::Reward::from(REWARD_AMOUNT);
 
 		T::prepare_rewards_account(account_params, reward);
-		RelayerRewards::<T>::insert(&relayer, account_params, reward);
+		RelayerRewards::<T, I>::insert(&relayer, account_params, reward);
 	}: _(RawOrigin::Signed(relayer), account_params)
 	verify {
 		// we can't check anything here, because `PaymentProcedure` is responsible for
@@ -62,30 +68,30 @@ benchmarks! {
 	register {
 		let relayer: T::AccountId = whitelisted_caller();
 		let valid_till = frame_system::Pallet::<T>::block_number()
-			.saturating_add(crate::Pallet::<T>::required_registration_lease())
+			.saturating_add(crate::Pallet::<T, I>::required_registration_lease())
 			.saturating_add(One::one())
 			.saturating_add(One::one());
 
-		T::deposit_account(relayer.clone(), crate::Pallet::<T>::required_stake());
+		T::deposit_account(relayer.clone(), crate::Pallet::<T, I>::required_stake());
 	}: _(RawOrigin::Signed(relayer.clone()), valid_till)
 	verify {
-		assert!(crate::Pallet::<T>::is_registration_active(&relayer));
+		assert!(crate::Pallet::<T, I>::is_registration_active(&relayer));
 	}
 
 	// Benchmark `deregister` call.
 	deregister {
 		let relayer: T::AccountId = whitelisted_caller();
 		let valid_till = frame_system::Pallet::<T>::block_number()
-			.saturating_add(crate::Pallet::<T>::required_registration_lease())
+			.saturating_add(crate::Pallet::<T, I>::required_registration_lease())
 			.saturating_add(One::one())
 			.saturating_add(One::one());
-		T::deposit_account(relayer.clone(), crate::Pallet::<T>::required_stake());
-		crate::Pallet::<T>::register(RawOrigin::Signed(relayer.clone()).into(), valid_till).unwrap();
+		T::deposit_account(relayer.clone(), crate::Pallet::<T, I>::required_stake());
+		crate::Pallet::<T, I>::register(RawOrigin::Signed(relayer.clone()).into(), valid_till).unwrap();
 
 		frame_system::Pallet::<T>::set_block_number(valid_till.saturating_add(One::one()));
 	}: _(RawOrigin::Signed(relayer.clone()))
 	verify {
-		assert!(!crate::Pallet::<T>::is_registration_active(&relayer));
+		assert!(!crate::Pallet::<T, I>::is_registration_active(&relayer));
 	}
 
 	// Benchmark `slash_and_deregister` method of the pallet. We are adding this weight to
@@ -95,36 +101,36 @@ benchmarks! {
 		// prepare and register relayer account
 		let relayer: T::AccountId = whitelisted_caller();
 		let valid_till = frame_system::Pallet::<T>::block_number()
-			.saturating_add(crate::Pallet::<T>::required_registration_lease())
+			.saturating_add(crate::Pallet::<T, I>::required_registration_lease())
 			.saturating_add(One::one())
 			.saturating_add(One::one());
-		T::deposit_account(relayer.clone(), crate::Pallet::<T>::required_stake());
-		crate::Pallet::<T>::register(RawOrigin::Signed(relayer.clone()).into(), valid_till).unwrap();
+		T::deposit_account(relayer.clone(), crate::Pallet::<T, I>::required_stake());
+		crate::Pallet::<T, I>::register(RawOrigin::Signed(relayer.clone()).into(), valid_till).unwrap();
 
 		// create slash destination account
-		let lane = LaneId::new(1, 2);
+		let lane = T::bench_lane_id();
 		let slash_destination = RewardsAccountParams::new(lane, *b"test", RewardsAccountOwner::ThisChain);
 		T::prepare_rewards_account(slash_destination, Zero::zero());
 	}: {
-		crate::Pallet::<T>::slash_and_deregister(&relayer, slash_destination.into())
+		crate::Pallet::<T, I>::slash_and_deregister(&relayer, slash_destination.into())
 	}
 	verify {
-		assert!(!crate::Pallet::<T>::is_registration_active(&relayer));
+		assert!(!crate::Pallet::<T, I>::is_registration_active(&relayer));
 	}
 
 	// Benchmark `register_relayer_reward` method of the pallet. We are adding this weight to
 	// the weight of message delivery call if `RefundBridgedParachainMessages` signed extension
 	// is deployed at runtime level.
 	register_relayer_reward {
-		let lane = LaneId::new(1, 2);
+		let lane = T::bench_lane_id();
 		let relayer: T::AccountId = whitelisted_caller();
 		let account_params =
 			RewardsAccountParams::new(lane, *b"test", RewardsAccountOwner::ThisChain);
 	}: {
-		crate::Pallet::<T>::register_relayer_reward(account_params, &relayer, One::one());
+		crate::Pallet::<T, I>::register_relayer_reward(account_params, &relayer, One::one());
 	}
 	verify {
-		assert_eq!(RelayerRewards::<T>::get(relayer, &account_params), Some(One::one()));
+		assert_eq!(RelayerRewards::<T, I>::get(relayer, &account_params), Some(One::one()));
 	}
 
 	impl_benchmark_test_suite!(Pallet, crate::mock::new_test_ext(), crate::mock::TestRuntime)
diff --git a/bridges/modules/relayers/src/extension/grandpa_adapter.rs b/bridges/modules/relayers/src/extension/grandpa_adapter.rs
index 6c9ae1c2968c0a3f63db463d720ccdcfe6a356a3..2a8a6e78ef9c732b868f1293f313b35a044ad81a 100644
--- a/bridges/modules/relayers/src/extension/grandpa_adapter.rs
+++ b/bridges/modules/relayers/src/extension/grandpa_adapter.rs
@@ -30,7 +30,7 @@ use pallet_bridge_grandpa::{
 	SubmitFinalityProofHelper,
 };
 use pallet_bridge_messages::{
-	CallSubType as BridgeMessagesCallSubType, Config as BridgeMessagesConfig,
+	CallSubType as BridgeMessagesCallSubType, Config as BridgeMessagesConfig, LaneIdOf,
 };
 use sp_runtime::{
 	traits::{Dispatchable, Get},
@@ -54,6 +54,8 @@ pub struct WithGrandpaChainExtensionConfig<
 	BridgeGrandpaPalletInstance,
 	// instance of BridgedChain `pallet-bridge-messages`, tracked by this extension
 	BridgeMessagesPalletInstance,
+	// instance of `pallet-bridge-relayers`, tracked by this extension
+	BridgeRelayersPalletInstance,
 	// message delivery transaction priority boost for every additional message
 	PriorityBoostPerMessage,
 >(
@@ -63,20 +65,22 @@ pub struct WithGrandpaChainExtensionConfig<
 		BatchCallUnpacker,
 		BridgeGrandpaPalletInstance,
 		BridgeMessagesPalletInstance,
+		BridgeRelayersPalletInstance,
 		PriorityBoostPerMessage,
 	)>,
 );
 
-impl<ID, R, BCU, GI, MI, P> ExtensionConfig
-	for WithGrandpaChainExtensionConfig<ID, R, BCU, GI, MI, P>
+impl<ID, R, BCU, GI, MI, RI, P> ExtensionConfig
+	for WithGrandpaChainExtensionConfig<ID, R, BCU, GI, MI, RI, P>
 where
 	ID: StaticStrProvider,
-	R: BridgeRelayersConfig
+	R: BridgeRelayersConfig<RI>
 		+ BridgeMessagesConfig<MI, BridgedChain = pallet_bridge_grandpa::BridgedChain<R, GI>>
 		+ BridgeGrandpaConfig<GI>,
 	BCU: BatchCallUnpacker<R>,
 	GI: 'static,
 	MI: 'static,
+	RI: 'static,
 	P: Get<TransactionPriority>,
 	R::RuntimeCall: Dispatchable<Info = DispatchInfo, PostInfo = PostDispatchInfo>
 		+ BridgeGrandpaCallSubtype<R, GI>
@@ -85,14 +89,15 @@ where
 	type IdProvider = ID;
 	type Runtime = R;
 	type BridgeMessagesPalletInstance = MI;
+	type BridgeRelayersPalletInstance = RI;
 	type PriorityBoostPerMessage = P;
-	type Reward = R::Reward;
 	type RemoteGrandpaChainBlockNumber = pallet_bridge_grandpa::BridgedBlockNumber<R, GI>;
+	type LaneId = LaneIdOf<R, Self::BridgeMessagesPalletInstance>;
 
 	fn parse_and_check_for_obsolete_call(
 		call: &R::RuntimeCall,
 	) -> Result<
-		Option<ExtensionCallInfo<Self::RemoteGrandpaChainBlockNumber>>,
+		Option<ExtensionCallInfo<Self::RemoteGrandpaChainBlockNumber, Self::LaneId>>,
 		TransactionValidityError,
 	> {
 		let calls = BCU::unpack(call, 2);
@@ -120,12 +125,12 @@ where
 	}
 
 	fn check_call_result(
-		call_info: &ExtensionCallInfo<Self::RemoteGrandpaChainBlockNumber>,
+		call_info: &ExtensionCallInfo<Self::RemoteGrandpaChainBlockNumber, Self::LaneId>,
 		call_data: &mut ExtensionCallData,
 		relayer: &R::AccountId,
 	) -> bool {
 		verify_submit_finality_proof_succeeded::<Self, GI>(call_info, call_data, relayer) &&
-			verify_messages_call_succeeded::<Self, MI>(call_info, call_data, relayer)
+			verify_messages_call_succeeded::<Self>(call_info, call_data, relayer)
 	}
 }
 
@@ -134,7 +139,7 @@ where
 ///
 /// Only returns false when GRANDPA chain state update call has failed.
 pub(crate) fn verify_submit_finality_proof_succeeded<C, GI>(
-	call_info: &ExtensionCallInfo<C::RemoteGrandpaChainBlockNumber>,
+	call_info: &ExtensionCallInfo<C::RemoteGrandpaChainBlockNumber, C::LaneId>,
 	call_data: &mut ExtensionCallData,
 	relayer: &<C::Runtime as SystemConfig>::AccountId,
 ) -> bool
diff --git a/bridges/modules/relayers/src/extension/messages_adapter.rs b/bridges/modules/relayers/src/extension/messages_adapter.rs
index ecb575524bb020db2e3a31383bf890b4418aaa5a..e8c2088b7f2d3e6a08bfd45550bf0723132824d6 100644
--- a/bridges/modules/relayers/src/extension/messages_adapter.rs
+++ b/bridges/modules/relayers/src/extension/messages_adapter.rs
@@ -23,7 +23,7 @@ use bp_relayers::{ExtensionCallData, ExtensionCallInfo, ExtensionConfig};
 use bp_runtime::StaticStrProvider;
 use frame_support::dispatch::{DispatchInfo, PostDispatchInfo};
 use pallet_bridge_messages::{
-	CallSubType as BridgeMessagesCallSubType, Config as BridgeMessagesConfig,
+	CallSubType as BridgeMessagesCallSubType, Config as BridgeMessagesConfig, LaneIdOf,
 };
 use sp_runtime::{
 	traits::{Dispatchable, Get},
@@ -37,6 +37,7 @@ pub struct WithMessagesExtensionConfig<
 	IdProvider,
 	Runtime,
 	BridgeMessagesPalletInstance,
+	BridgeRelayersPalletInstance,
 	PriorityBoostPerMessage,
 >(
 	PhantomData<(
@@ -46,16 +47,19 @@ pub struct WithMessagesExtensionConfig<
 		Runtime,
 		// instance of BridgedChain `pallet-bridge-messages`, tracked by this extension
 		BridgeMessagesPalletInstance,
+		// instance of `pallet-bridge-relayers`, tracked by this extension
+		BridgeRelayersPalletInstance,
 		// message delivery transaction priority boost for every additional message
 		PriorityBoostPerMessage,
 	)>,
 );
 
-impl<ID, R, MI, P> ExtensionConfig for WithMessagesExtensionConfig<ID, R, MI, P>
+impl<ID, R, MI, RI, P> ExtensionConfig for WithMessagesExtensionConfig<ID, R, MI, RI, P>
 where
 	ID: StaticStrProvider,
-	R: BridgeRelayersConfig + BridgeMessagesConfig<MI>,
+	R: BridgeRelayersConfig<RI> + BridgeMessagesConfig<MI>,
 	MI: 'static,
+	RI: 'static,
 	P: Get<TransactionPriority>,
 	R::RuntimeCall: Dispatchable<Info = DispatchInfo, PostInfo = PostDispatchInfo>
 		+ BridgeMessagesCallSubType<R, MI>,
@@ -63,14 +67,15 @@ where
 	type IdProvider = ID;
 	type Runtime = R;
 	type BridgeMessagesPalletInstance = MI;
+	type BridgeRelayersPalletInstance = RI;
 	type PriorityBoostPerMessage = P;
-	type Reward = R::Reward;
 	type RemoteGrandpaChainBlockNumber = ();
+	type LaneId = LaneIdOf<R, Self::BridgeMessagesPalletInstance>;
 
 	fn parse_and_check_for_obsolete_call(
 		call: &R::RuntimeCall,
 	) -> Result<
-		Option<ExtensionCallInfo<Self::RemoteGrandpaChainBlockNumber>>,
+		Option<ExtensionCallInfo<Self::RemoteGrandpaChainBlockNumber, Self::LaneId>>,
 		TransactionValidityError,
 	> {
 		let call = Self::check_obsolete_parsed_call(call)?;
@@ -85,10 +90,10 @@ where
 	}
 
 	fn check_call_result(
-		call_info: &ExtensionCallInfo<Self::RemoteGrandpaChainBlockNumber>,
+		call_info: &ExtensionCallInfo<Self::RemoteGrandpaChainBlockNumber, Self::LaneId>,
 		call_data: &mut ExtensionCallData,
 		relayer: &R::AccountId,
 	) -> bool {
-		verify_messages_call_succeeded::<Self, MI>(call_info, call_data, relayer)
+		verify_messages_call_succeeded::<Self>(call_info, call_data, relayer)
 	}
 }
diff --git a/bridges/modules/relayers/src/extension/mod.rs b/bridges/modules/relayers/src/extension/mod.rs
index e1a7abd0ad1c207ac71e60f87074a826e0b6dd0b..9a248eb8e7983c09934396e7ccae824dd83b296c 100644
--- a/bridges/modules/relayers/src/extension/mod.rs
+++ b/bridges/modules/relayers/src/extension/mod.rs
@@ -36,7 +36,9 @@ use frame_support::{
 	CloneNoBound, DefaultNoBound, EqNoBound, PartialEqNoBound, RuntimeDebugNoBound,
 };
 use frame_system::Config as SystemConfig;
-use pallet_bridge_messages::{CallHelper as MessagesCallHelper, Config as BridgeMessagesConfig};
+use pallet_bridge_messages::{
+	CallHelper as MessagesCallHelper, Config as BridgeMessagesConfig, LaneIdOf,
+};
 use pallet_transaction_payment::{
 	Config as TransactionPaymentConfig, OnChargeTransaction, Pallet as TransactionPaymentPallet,
 };
@@ -62,15 +64,19 @@ mod priority;
 
 /// Data that is crafted in `pre_dispatch` method and used at `post_dispatch`.
 #[cfg_attr(test, derive(Debug, PartialEq))]
-pub struct PreDispatchData<AccountId, RemoteGrandpaChainBlockNumber: Debug> {
+pub struct PreDispatchData<
+	AccountId,
+	RemoteGrandpaChainBlockNumber: Debug,
+	LaneId: Clone + Copy + Debug,
+> {
 	/// Transaction submitter (relayer) account.
 	relayer: AccountId,
 	/// Type of the call.
-	call_info: ExtensionCallInfo<RemoteGrandpaChainBlockNumber>,
+	call_info: ExtensionCallInfo<RemoteGrandpaChainBlockNumber, LaneId>,
 }
 
-impl<AccountId, RemoteGrandpaChainBlockNumber: Debug>
-	PreDispatchData<AccountId, RemoteGrandpaChainBlockNumber>
+impl<AccountId, RemoteGrandpaChainBlockNumber: Debug, LaneId: Clone + Copy + Debug>
+	PreDispatchData<AccountId, RemoteGrandpaChainBlockNumber, LaneId>
 {
 	/// Returns mutable reference to pre-dispatch `finality_target` sent to the
 	/// `SubmitFinalityProof` call.
@@ -88,13 +94,13 @@ impl<AccountId, RemoteGrandpaChainBlockNumber: Debug>
 
 /// The actions on relayer account that need to be performed because of his actions.
 #[derive(RuntimeDebug, PartialEq)]
-pub enum RelayerAccountAction<AccountId, Reward> {
+pub enum RelayerAccountAction<AccountId, Reward, LaneId> {
 	/// Do nothing with relayer account.
 	None,
 	/// Reward the relayer.
-	Reward(AccountId, RewardsAccountParams, Reward),
+	Reward(AccountId, RewardsAccountParams<LaneId>, Reward),
 	/// Slash the relayer.
-	Slash(AccountId, RewardsAccountParams),
+	Slash(AccountId, RewardsAccountParams<LaneId>),
 }
 
 /// A signed extension, built around `pallet-bridge-relayers`.
@@ -112,19 +118,22 @@ pub enum RelayerAccountAction<AccountId, Reward> {
 	RuntimeDebugNoBound,
 	TypeInfo,
 )]
-#[scale_info(skip_type_params(Runtime, Config))]
-pub struct BridgeRelayersSignedExtension<Runtime, Config>(PhantomData<(Runtime, Config)>);
+#[scale_info(skip_type_params(Runtime, Config, LaneId))]
+pub struct BridgeRelayersSignedExtension<Runtime, Config, LaneId>(
+	PhantomData<(Runtime, Config, LaneId)>,
+);
 
-impl<R, C> BridgeRelayersSignedExtension<R, C>
+impl<R, C, LaneId> BridgeRelayersSignedExtension<R, C, LaneId>
 where
 	Self: 'static + Send + Sync,
-	R: RelayersConfig
-		+ BridgeMessagesConfig<C::BridgeMessagesPalletInstance>
+	R: RelayersConfig<LaneId = LaneId>
+		+ BridgeMessagesConfig<C::BridgeMessagesPalletInstance, LaneId = LaneId>
 		+ TransactionPaymentConfig,
-	C: ExtensionConfig<Runtime = R, Reward = R::Reward>,
+	C: ExtensionConfig<Runtime = R, LaneId = LaneId>,
 	R::RuntimeCall: Dispatchable<Info = DispatchInfo, PostInfo = PostDispatchInfo>,
 	<R as TransactionPaymentConfig>::OnChargeTransaction:
 		OnChargeTransaction<R, Balance = R::Reward>,
+	LaneId: Clone + Copy + Decode + Encode + Debug + TypeInfo,
 {
 	/// Returns number of bundled messages `Some(_)`, if the given call info is a:
 	///
@@ -136,7 +145,7 @@ where
 	/// virtually boosted. The relayer registration (we only boost priority for registered
 	/// relayer transactions) must be checked outside.
 	fn bundled_messages_for_priority_boost(
-		call_info: Option<&ExtensionCallInfo<C::RemoteGrandpaChainBlockNumber>>,
+		call_info: Option<&ExtensionCallInfo<C::RemoteGrandpaChainBlockNumber, LaneId>>,
 	) -> Option<MessageNonce> {
 		// we only boost priority of message delivery transactions
 		let parsed_call = match call_info {
@@ -160,12 +169,14 @@ where
 	/// Given post-dispatch information, analyze the outcome of relayer call and return
 	/// actions that need to be performed on relayer account.
 	fn analyze_call_result(
-		pre: Option<Option<PreDispatchData<R::AccountId, C::RemoteGrandpaChainBlockNumber>>>,
+		pre: Option<
+			Option<PreDispatchData<R::AccountId, C::RemoteGrandpaChainBlockNumber, LaneId>>,
+		>,
 		info: &DispatchInfo,
 		post_info: &PostDispatchInfo,
 		len: usize,
 		result: &DispatchResult,
-	) -> RelayerAccountAction<R::AccountId, R::Reward> {
+	) -> RelayerAccountAction<R::AccountId, R::Reward, LaneId> {
 		// We don't refund anything for transactions that we don't support.
 		let (relayer, call_info) = match pre {
 			Some(Some(pre)) => (pre.relayer, pre.call_info),
@@ -263,22 +274,23 @@ where
 	}
 }
 
-impl<R, C> SignedExtension for BridgeRelayersSignedExtension<R, C>
+impl<R, C, LaneId> SignedExtension for BridgeRelayersSignedExtension<R, C, LaneId>
 where
 	Self: 'static + Send + Sync,
-	R: RelayersConfig
-		+ BridgeMessagesConfig<C::BridgeMessagesPalletInstance>
+	R: RelayersConfig<LaneId = LaneId>
+		+ BridgeMessagesConfig<C::BridgeMessagesPalletInstance, LaneId = LaneId>
 		+ TransactionPaymentConfig,
-	C: ExtensionConfig<Runtime = R, Reward = R::Reward>,
+	C: ExtensionConfig<Runtime = R, LaneId = LaneId>,
 	R::RuntimeCall: Dispatchable<Info = DispatchInfo, PostInfo = PostDispatchInfo>,
 	<R as TransactionPaymentConfig>::OnChargeTransaction:
 		OnChargeTransaction<R, Balance = R::Reward>,
+	LaneId: Clone + Copy + Decode + Encode + Debug + TypeInfo,
 {
 	const IDENTIFIER: &'static str = C::IdProvider::STR;
 	type AccountId = R::AccountId;
 	type Call = R::RuntimeCall;
 	type AdditionalSigned = ();
-	type Pre = Option<PreDispatchData<R::AccountId, C::RemoteGrandpaChainBlockNumber>>;
+	type Pre = Option<PreDispatchData<R::AccountId, C::RemoteGrandpaChainBlockNumber, LaneId>>;
 
 	fn additional_signed(&self) -> Result<(), TransactionValidityError> {
 		Ok(())
@@ -392,19 +404,23 @@ where
 }
 
 /// Verify that the messages pallet call, supported by extension has succeeded.
-pub(crate) fn verify_messages_call_succeeded<C, MI>(
-	call_info: &ExtensionCallInfo<C::RemoteGrandpaChainBlockNumber>,
+pub(crate) fn verify_messages_call_succeeded<C>(
+	call_info: &ExtensionCallInfo<
+		C::RemoteGrandpaChainBlockNumber,
+		LaneIdOf<C::Runtime, C::BridgeMessagesPalletInstance>,
+	>,
 	_call_data: &mut ExtensionCallData,
 	relayer: &<C::Runtime as SystemConfig>::AccountId,
 ) -> bool
 where
 	C: ExtensionConfig,
-	MI: 'static,
-	C::Runtime: BridgeMessagesConfig<MI>,
+	C::Runtime: BridgeMessagesConfig<C::BridgeMessagesPalletInstance>,
 {
 	let messages_call = call_info.messages_call_info();
 
-	if !MessagesCallHelper::<C::Runtime, MI>::was_successful(messages_call) {
+	if !MessagesCallHelper::<C::Runtime, C::BridgeMessagesPalletInstance>::was_successful(
+		messages_call,
+	) {
 		log::trace!(
 			target: LOG_TARGET,
 			"{}.{:?}: relayer {:?} has submitted invalid messages call",
@@ -427,9 +443,9 @@ mod tests {
 	use bp_messages::{
 		source_chain::FromBridgedChainMessagesDeliveryProof,
 		target_chain::FromBridgedChainMessagesProof, BaseMessagesProofInfo, DeliveredMessages,
-		InboundLaneData, LaneId, MessageNonce, MessagesCallInfo, MessagesOperatingMode,
-		OutboundLaneData, ReceiveMessagesDeliveryProofInfo, ReceiveMessagesProofInfo,
-		UnrewardedRelayer, UnrewardedRelayerOccupation, UnrewardedRelayersState,
+		InboundLaneData, MessageNonce, MessagesCallInfo, MessagesOperatingMode, OutboundLaneData,
+		ReceiveMessagesDeliveryProofInfo, ReceiveMessagesProofInfo, UnrewardedRelayer,
+		UnrewardedRelayerOccupation, UnrewardedRelayersState,
 	};
 	use bp_parachains::{BestParaHeadHash, ParaInfo, SubmitParachainHeadsInfo};
 	use bp_polkadot_core::parachains::{ParaHeadsProof, ParaId};
@@ -454,17 +470,16 @@ mod tests {
 
 	parameter_types! {
 		TestParachain: u32 = BridgedUnderlyingParachain::PARACHAIN_ID;
-		pub MsgProofsRewardsAccount: RewardsAccountParams = RewardsAccountParams::new(
+		pub MsgProofsRewardsAccount: RewardsAccountParams<TestLaneIdType> = RewardsAccountParams::new(
 			test_lane_id(),
 			TEST_BRIDGED_CHAIN_ID,
 			RewardsAccountOwner::ThisChain,
 		);
-		pub MsgDeliveryProofsRewardsAccount: RewardsAccountParams = RewardsAccountParams::new(
+		pub MsgDeliveryProofsRewardsAccount: RewardsAccountParams<TestLaneIdType> = RewardsAccountParams::new(
 			test_lane_id(),
 			TEST_BRIDGED_CHAIN_ID,
 			RewardsAccountOwner::BridgedChain,
 		);
-		pub TestLaneId: LaneId = test_lane_id();
 	}
 
 	bp_runtime::generate_static_str_provider!(TestGrandpaExtension);
@@ -477,31 +492,31 @@ mod tests {
 		RuntimeWithUtilityPallet<TestRuntime>,
 		(),
 		(),
+		(),
 		ConstU64<1>,
 	>;
 	type TestGrandpaExtension =
-		BridgeRelayersSignedExtension<TestRuntime, TestGrandpaExtensionConfig>;
+		BridgeRelayersSignedExtension<TestRuntime, TestGrandpaExtensionConfig, TestLaneIdType>;
 	type TestExtensionConfig = parachain_adapter::WithParachainExtensionConfig<
 		StrTestExtension,
 		TestRuntime,
 		RuntimeWithUtilityPallet<TestRuntime>,
 		(),
 		(),
+		(),
 		ConstU64<1>,
 	>;
-	type TestExtension = BridgeRelayersSignedExtension<TestRuntime, TestExtensionConfig>;
+	type TestExtension =
+		BridgeRelayersSignedExtension<TestRuntime, TestExtensionConfig, TestLaneIdType>;
 	type TestMessagesExtensionConfig = messages_adapter::WithMessagesExtensionConfig<
 		StrTestMessagesExtension,
 		TestRuntime,
 		(),
+		(),
 		ConstU64<1>,
 	>;
 	type TestMessagesExtension =
-		BridgeRelayersSignedExtension<TestRuntime, TestMessagesExtensionConfig>;
-
-	fn test_lane_id() -> LaneId {
-		LaneId::new(1, 2)
-	}
+		BridgeRelayersSignedExtension<TestRuntime, TestMessagesExtensionConfig, TestLaneIdType>;
 
 	fn initial_balance_of_relayer_account_at_this_chain() -> ThisChainBalance {
 		let test_stake: ThisChainBalance = Stake::get();
@@ -795,7 +810,7 @@ mod tests {
 	}
 
 	fn all_finality_pre_dispatch_data(
-	) -> PreDispatchData<ThisChainAccountId, BridgedChainBlockNumber> {
+	) -> PreDispatchData<ThisChainAccountId, BridgedChainBlockNumber, TestLaneIdType> {
 		PreDispatchData {
 			relayer: relayer_account_at_this_chain(),
 			call_info: ExtensionCallInfo::AllFinalityAndMsgs(
@@ -832,14 +847,14 @@ mod tests {
 
 	#[cfg(test)]
 	fn all_finality_pre_dispatch_data_ex(
-	) -> PreDispatchData<ThisChainAccountId, BridgedChainBlockNumber> {
+	) -> PreDispatchData<ThisChainAccountId, BridgedChainBlockNumber, TestLaneIdType> {
 		let mut data = all_finality_pre_dispatch_data();
 		data.submit_finality_proof_info_mut().unwrap().current_set_id = Some(TEST_GRANDPA_SET_ID);
 		data
 	}
 
 	fn all_finality_confirmation_pre_dispatch_data(
-	) -> PreDispatchData<ThisChainAccountId, BridgedChainBlockNumber> {
+	) -> PreDispatchData<ThisChainAccountId, BridgedChainBlockNumber, TestLaneIdType> {
 		PreDispatchData {
 			relayer: relayer_account_at_this_chain(),
 			call_info: ExtensionCallInfo::AllFinalityAndMsgs(
@@ -869,14 +884,14 @@ mod tests {
 	}
 
 	fn all_finality_confirmation_pre_dispatch_data_ex(
-	) -> PreDispatchData<ThisChainAccountId, BridgedChainBlockNumber> {
+	) -> PreDispatchData<ThisChainAccountId, BridgedChainBlockNumber, TestLaneIdType> {
 		let mut data = all_finality_confirmation_pre_dispatch_data();
 		data.submit_finality_proof_info_mut().unwrap().current_set_id = Some(TEST_GRANDPA_SET_ID);
 		data
 	}
 
 	fn relay_finality_pre_dispatch_data(
-	) -> PreDispatchData<ThisChainAccountId, BridgedChainBlockNumber> {
+	) -> PreDispatchData<ThisChainAccountId, BridgedChainBlockNumber, TestLaneIdType> {
 		PreDispatchData {
 			relayer: relayer_account_at_this_chain(),
 			call_info: ExtensionCallInfo::RelayFinalityAndMsgs(
@@ -906,14 +921,14 @@ mod tests {
 	}
 
 	fn relay_finality_pre_dispatch_data_ex(
-	) -> PreDispatchData<ThisChainAccountId, BridgedChainBlockNumber> {
+	) -> PreDispatchData<ThisChainAccountId, BridgedChainBlockNumber, TestLaneIdType> {
 		let mut data = relay_finality_pre_dispatch_data();
 		data.submit_finality_proof_info_mut().unwrap().current_set_id = Some(TEST_GRANDPA_SET_ID);
 		data
 	}
 
 	fn relay_finality_confirmation_pre_dispatch_data(
-	) -> PreDispatchData<ThisChainAccountId, BridgedChainBlockNumber> {
+	) -> PreDispatchData<ThisChainAccountId, BridgedChainBlockNumber, TestLaneIdType> {
 		PreDispatchData {
 			relayer: relayer_account_at_this_chain(),
 			call_info: ExtensionCallInfo::RelayFinalityAndMsgs(
@@ -937,14 +952,14 @@ mod tests {
 	}
 
 	fn relay_finality_confirmation_pre_dispatch_data_ex(
-	) -> PreDispatchData<ThisChainAccountId, BridgedChainBlockNumber> {
+	) -> PreDispatchData<ThisChainAccountId, BridgedChainBlockNumber, TestLaneIdType> {
 		let mut data = relay_finality_confirmation_pre_dispatch_data();
 		data.submit_finality_proof_info_mut().unwrap().current_set_id = Some(TEST_GRANDPA_SET_ID);
 		data
 	}
 
 	fn parachain_finality_pre_dispatch_data(
-	) -> PreDispatchData<ThisChainAccountId, BridgedChainBlockNumber> {
+	) -> PreDispatchData<ThisChainAccountId, BridgedChainBlockNumber, TestLaneIdType> {
 		PreDispatchData {
 			relayer: relayer_account_at_this_chain(),
 			call_info: ExtensionCallInfo::ParachainFinalityAndMsgs(
@@ -972,7 +987,7 @@ mod tests {
 	}
 
 	fn parachain_finality_confirmation_pre_dispatch_data(
-	) -> PreDispatchData<ThisChainAccountId, BridgedChainBlockNumber> {
+	) -> PreDispatchData<ThisChainAccountId, BridgedChainBlockNumber, TestLaneIdType> {
 		PreDispatchData {
 			relayer: relayer_account_at_this_chain(),
 			call_info: ExtensionCallInfo::ParachainFinalityAndMsgs(
@@ -994,7 +1009,7 @@ mod tests {
 	}
 
 	fn delivery_pre_dispatch_data<RemoteGrandpaChainBlockNumber: Debug>(
-	) -> PreDispatchData<ThisChainAccountId, RemoteGrandpaChainBlockNumber> {
+	) -> PreDispatchData<ThisChainAccountId, RemoteGrandpaChainBlockNumber, TestLaneIdType> {
 		PreDispatchData {
 			relayer: relayer_account_at_this_chain(),
 			call_info: ExtensionCallInfo::Msgs(MessagesCallInfo::ReceiveMessagesProof(
@@ -1016,7 +1031,7 @@ mod tests {
 	}
 
 	fn confirmation_pre_dispatch_data<RemoteGrandpaChainBlockNumber: Debug>(
-	) -> PreDispatchData<ThisChainAccountId, RemoteGrandpaChainBlockNumber> {
+	) -> PreDispatchData<ThisChainAccountId, RemoteGrandpaChainBlockNumber, TestLaneIdType> {
 		PreDispatchData {
 			relayer: relayer_account_at_this_chain(),
 			call_info: ExtensionCallInfo::Msgs(MessagesCallInfo::ReceiveMessagesDeliveryProof(
@@ -1030,9 +1045,13 @@ mod tests {
 	}
 
 	fn set_bundled_range_end(
-		mut pre_dispatch_data: PreDispatchData<ThisChainAccountId, BridgedChainBlockNumber>,
+		mut pre_dispatch_data: PreDispatchData<
+			ThisChainAccountId,
+			BridgedChainBlockNumber,
+			TestLaneIdType,
+		>,
 		end: MessageNonce,
-	) -> PreDispatchData<ThisChainAccountId, BridgedChainBlockNumber> {
+	) -> PreDispatchData<ThisChainAccountId, BridgedChainBlockNumber, TestLaneIdType> {
 		let msg_info = match pre_dispatch_data.call_info {
 			ExtensionCallInfo::AllFinalityAndMsgs(_, _, ref mut info) => info,
 			ExtensionCallInfo::RelayFinalityAndMsgs(_, ref mut info) => info,
@@ -1072,7 +1091,7 @@ mod tests {
 	fn run_pre_dispatch(
 		call: RuntimeCall,
 	) -> Result<
-		Option<PreDispatchData<ThisChainAccountId, BridgedChainBlockNumber>>,
+		Option<PreDispatchData<ThisChainAccountId, BridgedChainBlockNumber, TestLaneIdType>>,
 		TransactionValidityError,
 	> {
 		sp_tracing::try_init_simple();
@@ -1083,7 +1102,7 @@ mod tests {
 	fn run_grandpa_pre_dispatch(
 		call: RuntimeCall,
 	) -> Result<
-		Option<PreDispatchData<ThisChainAccountId, BridgedChainBlockNumber>>,
+		Option<PreDispatchData<ThisChainAccountId, BridgedChainBlockNumber, TestLaneIdType>>,
 		TransactionValidityError,
 	> {
 		let extension: TestGrandpaExtension = BridgeRelayersSignedExtension(PhantomData);
@@ -1092,7 +1111,10 @@ mod tests {
 
 	fn run_messages_pre_dispatch(
 		call: RuntimeCall,
-	) -> Result<Option<PreDispatchData<ThisChainAccountId, ()>>, TransactionValidityError> {
+	) -> Result<
+		Option<PreDispatchData<ThisChainAccountId, (), TestLaneIdType>>,
+		TransactionValidityError,
+	> {
 		let extension: TestMessagesExtension = BridgeRelayersSignedExtension(PhantomData);
 		extension.pre_dispatch(&relayer_account_at_this_chain(), &call, &DispatchInfo::default(), 0)
 	}
@@ -1113,7 +1135,9 @@ mod tests {
 	}
 
 	fn run_post_dispatch(
-		pre_dispatch_data: Option<PreDispatchData<ThisChainAccountId, BridgedChainBlockNumber>>,
+		pre_dispatch_data: Option<
+			PreDispatchData<ThisChainAccountId, BridgedChainBlockNumber, TestLaneIdType>,
+		>,
 		dispatch_result: DispatchResult,
 	) {
 		let post_dispatch_result = TestExtension::post_dispatch(
@@ -1886,9 +1910,13 @@ mod tests {
 	}
 
 	fn run_analyze_call_result(
-		pre_dispatch_data: PreDispatchData<ThisChainAccountId, BridgedChainBlockNumber>,
+		pre_dispatch_data: PreDispatchData<
+			ThisChainAccountId,
+			BridgedChainBlockNumber,
+			TestLaneIdType,
+		>,
 		dispatch_result: DispatchResult,
-	) -> RelayerAccountAction<ThisChainAccountId, ThisChainBalance> {
+	) -> RelayerAccountAction<ThisChainAccountId, ThisChainBalance, TestLaneIdType> {
 		TestExtension::analyze_call_result(
 			Some(Some(pre_dispatch_data)),
 			&dispatch_info(),
@@ -2318,7 +2346,7 @@ mod tests {
 				.unwrap();
 
 			// allow empty message delivery transactions
-			let lane_id = TestLaneId::get();
+			let lane_id = test_lane_id();
 			let in_lane_data = InboundLaneData {
 				last_confirmed_nonce: 0,
 				relayers: vec![UnrewardedRelayer {
diff --git a/bridges/modules/relayers/src/extension/parachain_adapter.rs b/bridges/modules/relayers/src/extension/parachain_adapter.rs
index b6f57cebc309dcf1d5e5c952395879f01933d188..69cf766dd674de6d68d20531dec27f7b2424e419 100644
--- a/bridges/modules/relayers/src/extension/parachain_adapter.rs
+++ b/bridges/modules/relayers/src/extension/parachain_adapter.rs
@@ -32,7 +32,7 @@ use pallet_bridge_grandpa::{
 	CallSubType as BridgeGrandpaCallSubtype, Config as BridgeGrandpaConfig,
 };
 use pallet_bridge_messages::{
-	CallSubType as BridgeMessagesCallSubType, Config as BridgeMessagesConfig,
+	CallSubType as BridgeMessagesCallSubType, Config as BridgeMessagesConfig, LaneIdOf,
 };
 use pallet_bridge_parachains::{
 	CallSubType as BridgeParachainsCallSubtype, Config as BridgeParachainsConfig,
@@ -58,6 +58,8 @@ pub struct WithParachainExtensionConfig<
 	BridgeParachainsPalletInstance,
 	// instance of BridgedChain `pallet-bridge-messages`, tracked by this extension
 	BridgeMessagesPalletInstance,
+	// instance of `pallet-bridge-relayers`, tracked by this extension
+	BridgeRelayersPalletInstance,
 	// message delivery transaction priority boost for every additional message
 	PriorityBoostPerMessage,
 >(
@@ -67,20 +69,23 @@ pub struct WithParachainExtensionConfig<
 		BatchCallUnpacker,
 		BridgeParachainsPalletInstance,
 		BridgeMessagesPalletInstance,
+		BridgeRelayersPalletInstance,
 		PriorityBoostPerMessage,
 	)>,
 );
 
-impl<ID, R, BCU, PI, MI, P> ExtensionConfig for WithParachainExtensionConfig<ID, R, BCU, PI, MI, P>
+impl<ID, R, BCU, PI, MI, RI, P> ExtensionConfig
+	for WithParachainExtensionConfig<ID, R, BCU, PI, MI, RI, P>
 where
 	ID: StaticStrProvider,
-	R: BridgeRelayersConfig
+	R: BridgeRelayersConfig<RI>
 		+ BridgeMessagesConfig<MI>
 		+ BridgeParachainsConfig<PI>
 		+ BridgeGrandpaConfig<R::BridgesGrandpaPalletInstance>,
 	BCU: BatchCallUnpacker<R>,
 	PI: 'static,
 	MI: 'static,
+	RI: 'static,
 	P: Get<TransactionPriority>,
 	R::RuntimeCall: Dispatchable<Info = DispatchInfo, PostInfo = PostDispatchInfo>
 		+ BridgeGrandpaCallSubtype<R, R::BridgesGrandpaPalletInstance>
@@ -91,15 +96,16 @@ where
 	type IdProvider = ID;
 	type Runtime = R;
 	type BridgeMessagesPalletInstance = MI;
+	type BridgeRelayersPalletInstance = RI;
 	type PriorityBoostPerMessage = P;
-	type Reward = R::Reward;
 	type RemoteGrandpaChainBlockNumber =
 		pallet_bridge_grandpa::BridgedBlockNumber<R, R::BridgesGrandpaPalletInstance>;
+	type LaneId = LaneIdOf<R, Self::BridgeMessagesPalletInstance>;
 
 	fn parse_and_check_for_obsolete_call(
 		call: &R::RuntimeCall,
 	) -> Result<
-		Option<ExtensionCallInfo<Self::RemoteGrandpaChainBlockNumber>>,
+		Option<ExtensionCallInfo<Self::RemoteGrandpaChainBlockNumber, Self::LaneId>>,
 		TransactionValidityError,
 	> {
 		let calls = BCU::unpack(call, 3);
@@ -109,7 +115,7 @@ where
 		let msgs_call = calls.next().transpose()?.and_then(|c| c.call_info());
 		let para_finality_call = calls.next().transpose()?.and_then(|c| {
 			let r = c.submit_parachain_heads_info_for(
-				<R as BridgeMessagesConfig<MI>>::BridgedChain::PARACHAIN_ID,
+				<R as BridgeMessagesConfig<Self::BridgeMessagesPalletInstance>>::BridgedChain::PARACHAIN_ID,
 			);
 			r
 		});
@@ -139,14 +145,14 @@ where
 	}
 
 	fn check_call_result(
-		call_info: &ExtensionCallInfo<Self::RemoteGrandpaChainBlockNumber>,
+		call_info: &ExtensionCallInfo<Self::RemoteGrandpaChainBlockNumber, Self::LaneId>,
 		call_data: &mut ExtensionCallData,
 		relayer: &R::AccountId,
 	) -> bool {
 		verify_submit_finality_proof_succeeded::<Self, R::BridgesGrandpaPalletInstance>(
 			call_info, call_data, relayer,
 		) && verify_submit_parachain_head_succeeded::<Self, PI>(call_info, call_data, relayer) &&
-			verify_messages_call_succeeded::<Self, MI>(call_info, call_data, relayer)
+			verify_messages_call_succeeded::<Self>(call_info, call_data, relayer)
 	}
 }
 
@@ -155,7 +161,7 @@ where
 ///
 /// Only returns false when parachain state update call has failed.
 pub(crate) fn verify_submit_parachain_head_succeeded<C, PI>(
-	call_info: &ExtensionCallInfo<C::RemoteGrandpaChainBlockNumber>,
+	call_info: &ExtensionCallInfo<C::RemoteGrandpaChainBlockNumber, C::LaneId>,
 	_call_data: &mut ExtensionCallData,
 	relayer: &<C::Runtime as SystemConfig>::AccountId,
 ) -> bool
diff --git a/bridges/modules/relayers/src/lib.rs b/bridges/modules/relayers/src/lib.rs
index b9627774db1ec9fcc828713904e1e60e24a7ba1d..f06c2e16ac248513b449b98038049995a1d61302 100644
--- a/bridges/modules/relayers/src/lib.rs
+++ b/bridges/modules/relayers/src/lib.rs
@@ -43,6 +43,7 @@ mod weights_ext;
 
 pub mod benchmarking;
 pub mod extension;
+pub mod migration;
 pub mod weights;
 
 /// The target that will be used when publishing logs related to this pallet.
@@ -51,46 +52,58 @@ pub const LOG_TARGET: &str = "runtime::bridge-relayers";
 #[frame_support::pallet]
 pub mod pallet {
 	use super::*;
+	use bp_messages::LaneIdType;
 	use frame_support::pallet_prelude::*;
 	use frame_system::pallet_prelude::*;
 
 	/// `RelayerRewardsKeyProvider` for given configuration.
-	type RelayerRewardsKeyProviderOf<T> =
-		RelayerRewardsKeyProvider<<T as frame_system::Config>::AccountId, <T as Config>::Reward>;
+	type RelayerRewardsKeyProviderOf<T, I> = RelayerRewardsKeyProvider<
+		<T as frame_system::Config>::AccountId,
+		<T as Config<I>>::Reward,
+		<T as Config<I>>::LaneId,
+	>;
 
 	#[pallet::config]
-	pub trait Config: frame_system::Config {
+	pub trait Config<I: 'static = ()>: frame_system::Config {
 		/// The overarching event type.
-		type RuntimeEvent: From<Event<Self>> + IsType<<Self as frame_system::Config>::RuntimeEvent>;
+		type RuntimeEvent: From<Event<Self, I>>
+			+ IsType<<Self as frame_system::Config>::RuntimeEvent>;
 		/// Type of relayer reward.
 		type Reward: AtLeast32BitUnsigned + Copy + Member + Parameter + MaxEncodedLen;
 		/// Pay rewards scheme.
-		type PaymentProcedure: PaymentProcedure<Self::AccountId, Self::Reward>;
+		type PaymentProcedure: PaymentProcedure<
+			Self::AccountId,
+			Self::Reward,
+			LaneId = Self::LaneId,
+		>;
 		/// Stake and slash scheme.
 		type StakeAndSlash: StakeAndSlash<Self::AccountId, BlockNumberFor<Self>, Self::Reward>;
 		/// Pallet call weights.
 		type WeightInfo: WeightInfoExt;
+		/// Lane identifier type.
+		type LaneId: LaneIdType + Send + Sync;
 	}
 
 	#[pallet::pallet]
-	pub struct Pallet<T>(PhantomData<T>);
+	#[pallet::storage_version(migration::STORAGE_VERSION)]
+	pub struct Pallet<T, I = ()>(PhantomData<(T, I)>);
 
 	#[pallet::call]
-	impl<T: Config> Pallet<T> {
+	impl<T: Config<I>, I: 'static> Pallet<T, I> {
 		/// Claim accumulated rewards.
 		#[pallet::call_index(0)]
 		#[pallet::weight(T::WeightInfo::claim_rewards())]
 		pub fn claim_rewards(
 			origin: OriginFor<T>,
-			rewards_account_params: RewardsAccountParams,
+			rewards_account_params: RewardsAccountParams<T::LaneId>,
 		) -> DispatchResult {
 			let relayer = ensure_signed(origin)?;
 
-			RelayerRewards::<T>::try_mutate_exists(
+			RelayerRewards::<T, I>::try_mutate_exists(
 				&relayer,
 				rewards_account_params,
 				|maybe_reward| -> DispatchResult {
-					let reward = maybe_reward.take().ok_or(Error::<T>::NoRewardForRelayer)?;
+					let reward = maybe_reward.take().ok_or(Error::<T, I>::NoRewardForRelayer)?;
 					T::PaymentProcedure::pay_reward(&relayer, rewards_account_params, reward)
 						.map_err(|e| {
 							log::trace!(
@@ -100,10 +113,10 @@ pub mod pallet {
 								relayer,
 								e,
 							);
-							Error::<T>::FailedToPayReward
+							Error::<T, I>::FailedToPayReward
 						})?;
 
-					Self::deposit_event(Event::<T>::RewardPaid {
+					Self::deposit_event(Event::<T, I>::RewardPaid {
 						relayer: relayer.clone(),
 						rewards_account_params,
 						reward,
@@ -125,53 +138,57 @@ pub mod pallet {
 			// than the `RequiredRegistrationLease`
 			let lease = valid_till.saturating_sub(frame_system::Pallet::<T>::block_number());
 			ensure!(
-				lease > Pallet::<T>::required_registration_lease(),
-				Error::<T>::InvalidRegistrationLease
+				lease > Self::required_registration_lease(),
+				Error::<T, I>::InvalidRegistrationLease
 			);
 
-			RegisteredRelayers::<T>::try_mutate(&relayer, |maybe_registration| -> DispatchResult {
-				let mut registration = maybe_registration
-					.unwrap_or_else(|| Registration { valid_till, stake: Zero::zero() });
+			RegisteredRelayers::<T, I>::try_mutate(
+				&relayer,
+				|maybe_registration| -> DispatchResult {
+					let mut registration = maybe_registration
+						.unwrap_or_else(|| Registration { valid_till, stake: Zero::zero() });
+
+					// new `valid_till` must be larger (or equal) than the old one
+					ensure!(
+						valid_till >= registration.valid_till,
+						Error::<T, I>::CannotReduceRegistrationLease,
+					);
+					registration.valid_till = valid_till;
+
+					// regarding stake, there are three options:
+					// - if relayer stake is larger than required stake, we may do unreserve
+					// - if relayer stake equals to required stake, we do nothing
+					// - if relayer stake is smaller than required stake, we do additional reserve
+					let required_stake = Self::required_stake();
+					if let Some(to_unreserve) = registration.stake.checked_sub(&required_stake) {
+						Self::do_unreserve(&relayer, to_unreserve)?;
+					} else if let Some(to_reserve) = required_stake.checked_sub(&registration.stake)
+					{
+						T::StakeAndSlash::reserve(&relayer, to_reserve).map_err(|e| {
+							log::trace!(
+								target: LOG_TARGET,
+								"Failed to reserve {:?} on relayer {:?} account: {:?}",
+								to_reserve,
+								relayer,
+								e,
+							);
 
-				// new `valid_till` must be larger (or equal) than the old one
-				ensure!(
-					valid_till >= registration.valid_till,
-					Error::<T>::CannotReduceRegistrationLease,
-				);
-				registration.valid_till = valid_till;
-
-				// regarding stake, there are three options:
-				// - if relayer stake is larger than required stake, we may do unreserve
-				// - if relayer stake equals to required stake, we do nothing
-				// - if relayer stake is smaller than required stake, we do additional reserve
-				let required_stake = Pallet::<T>::required_stake();
-				if let Some(to_unreserve) = registration.stake.checked_sub(&required_stake) {
-					Self::do_unreserve(&relayer, to_unreserve)?;
-				} else if let Some(to_reserve) = required_stake.checked_sub(&registration.stake) {
-					T::StakeAndSlash::reserve(&relayer, to_reserve).map_err(|e| {
-						log::trace!(
-							target: LOG_TARGET,
-							"Failed to reserve {:?} on relayer {:?} account: {:?}",
-							to_reserve,
-							relayer,
-							e,
-						);
-
-						Error::<T>::FailedToReserve
-					})?;
-				}
-				registration.stake = required_stake;
-
-				log::trace!(target: LOG_TARGET, "Successfully registered relayer: {:?}", relayer);
-				Self::deposit_event(Event::<T>::RegistrationUpdated {
-					relayer: relayer.clone(),
-					registration,
-				});
-
-				*maybe_registration = Some(registration);
-
-				Ok(())
-			})
+							Error::<T, I>::FailedToReserve
+						})?;
+					}
+					registration.stake = required_stake;
+
+					log::trace!(target: LOG_TARGET, "Successfully registered relayer: {:?}", relayer);
+					Self::deposit_event(Event::<T, I>::RegistrationUpdated {
+						relayer: relayer.clone(),
+						registration,
+					});
+
+					*maybe_registration = Some(registration);
+
+					Ok(())
+				},
+			)
 		}
 
 		/// `Deregister` relayer.
@@ -183,34 +200,37 @@ pub mod pallet {
 		pub fn deregister(origin: OriginFor<T>) -> DispatchResult {
 			let relayer = ensure_signed(origin)?;
 
-			RegisteredRelayers::<T>::try_mutate(&relayer, |maybe_registration| -> DispatchResult {
-				let registration = match maybe_registration.take() {
-					Some(registration) => registration,
-					None => fail!(Error::<T>::NotRegistered),
-				};
-
-				// we can't deregister until `valid_till + 1`
-				ensure!(
-					registration.valid_till < frame_system::Pallet::<T>::block_number(),
-					Error::<T>::RegistrationIsStillActive,
-				);
+			RegisteredRelayers::<T, I>::try_mutate(
+				&relayer,
+				|maybe_registration| -> DispatchResult {
+					let registration = match maybe_registration.take() {
+						Some(registration) => registration,
+						None => fail!(Error::<T, I>::NotRegistered),
+					};
+
+					// we can't deregister until `valid_till + 1`
+					ensure!(
+						registration.valid_till < frame_system::Pallet::<T>::block_number(),
+						Error::<T, I>::RegistrationIsStillActive,
+					);
 
-				// if stake is non-zero, we should do unreserve
-				if !registration.stake.is_zero() {
-					Self::do_unreserve(&relayer, registration.stake)?;
-				}
+					// if stake is non-zero, we should do unreserve
+					if !registration.stake.is_zero() {
+						Self::do_unreserve(&relayer, registration.stake)?;
+					}
 
-				log::trace!(target: LOG_TARGET, "Successfully deregistered relayer: {:?}", relayer);
-				Self::deposit_event(Event::<T>::Deregistered { relayer: relayer.clone() });
+					log::trace!(target: LOG_TARGET, "Successfully deregistered relayer: {:?}", relayer);
+					Self::deposit_event(Event::<T, I>::Deregistered { relayer: relayer.clone() });
 
-				*maybe_registration = None;
+					*maybe_registration = None;
 
-				Ok(())
-			})
+					Ok(())
+				},
+			)
 		}
 	}
 
-	impl<T: Config> Pallet<T> {
+	impl<T: Config<I>, I: 'static> Pallet<T, I> {
 		/// Returns true if given relayer registration is active at current block.
 		///
 		/// This call respects both `RequiredStake` and `RequiredRegistrationLease`, meaning that
@@ -243,9 +263,9 @@ pub mod pallet {
 		/// It may fail inside, but error is swallowed and we only log it.
 		pub fn slash_and_deregister(
 			relayer: &T::AccountId,
-			slash_destination: ExplicitOrAccountParams<T::AccountId>,
+			slash_destination: ExplicitOrAccountParams<T::AccountId, T::LaneId>,
 		) {
-			let registration = match RegisteredRelayers::<T>::take(relayer) {
+			let registration = match RegisteredRelayers::<T, I>::take(relayer) {
 				Some(registration) => registration,
 				None => {
 					log::trace!(
@@ -304,7 +324,7 @@ pub mod pallet {
 
 		/// Register reward for given relayer.
 		pub fn register_relayer_reward(
-			rewards_account_params: RewardsAccountParams,
+			rewards_account_params: RewardsAccountParams<T::LaneId>,
 			relayer: &T::AccountId,
 			reward: T::Reward,
 		) {
@@ -312,7 +332,7 @@ pub mod pallet {
 				return
 			}
 
-			RelayerRewards::<T>::mutate(
+			RelayerRewards::<T, I>::mutate(
 				relayer,
 				rewards_account_params,
 				|old_reward: &mut Option<T::Reward>| {
@@ -327,7 +347,7 @@ pub mod pallet {
 						new_reward,
 					);
 
-					Self::deposit_event(Event::<T>::RewardRegistered {
+					Self::deposit_event(Event::<T, I>::RewardRegistered {
 						relayer: relayer.clone(),
 						rewards_account_params,
 						reward,
@@ -366,7 +386,7 @@ pub mod pallet {
 					relayer,
 				);
 
-				fail!(Error::<T>::FailedToUnreserve)
+				fail!(Error::<T, I>::FailedToUnreserve)
 			}
 
 			Ok(())
@@ -375,13 +395,13 @@ pub mod pallet {
 
 	#[pallet::event]
 	#[pallet::generate_deposit(pub(super) fn deposit_event)]
-	pub enum Event<T: Config> {
+	pub enum Event<T: Config<I>, I: 'static = ()> {
 		/// Relayer reward has been registered and may be claimed later.
 		RewardRegistered {
 			/// Relayer account that can claim reward.
 			relayer: T::AccountId,
 			/// Relayer can claim reward from this account.
-			rewards_account_params: RewardsAccountParams,
+			rewards_account_params: RewardsAccountParams<T::LaneId>,
 			/// Reward amount.
 			reward: T::Reward,
 		},
@@ -390,7 +410,7 @@ pub mod pallet {
 			/// Relayer account that has been rewarded.
 			relayer: T::AccountId,
 			/// Relayer has received reward from this account.
-			rewards_account_params: RewardsAccountParams,
+			rewards_account_params: RewardsAccountParams<T::LaneId>,
 			/// Reward amount.
 			reward: T::Reward,
 		},
@@ -416,7 +436,7 @@ pub mod pallet {
 	}
 
 	#[pallet::error]
-	pub enum Error<T> {
+	pub enum Error<T, I = ()> {
 		/// No reward can be claimed by given relayer.
 		NoRewardForRelayer,
 		/// Reward payment procedure has failed.
@@ -439,13 +459,13 @@ pub mod pallet {
 	/// Map of the relayer => accumulated reward.
 	#[pallet::storage]
 	#[pallet::getter(fn relayer_reward)]
-	pub type RelayerRewards<T: Config> = StorageDoubleMap<
+	pub type RelayerRewards<T: Config<I>, I: 'static = ()> = StorageDoubleMap<
 		_,
-		<RelayerRewardsKeyProviderOf<T> as StorageDoubleMapKeyProvider>::Hasher1,
-		<RelayerRewardsKeyProviderOf<T> as StorageDoubleMapKeyProvider>::Key1,
-		<RelayerRewardsKeyProviderOf<T> as StorageDoubleMapKeyProvider>::Hasher2,
-		<RelayerRewardsKeyProviderOf<T> as StorageDoubleMapKeyProvider>::Key2,
-		<RelayerRewardsKeyProviderOf<T> as StorageDoubleMapKeyProvider>::Value,
+		<RelayerRewardsKeyProviderOf<T, I> as StorageDoubleMapKeyProvider>::Hasher1,
+		<RelayerRewardsKeyProviderOf<T, I> as StorageDoubleMapKeyProvider>::Key1,
+		<RelayerRewardsKeyProviderOf<T, I> as StorageDoubleMapKeyProvider>::Hasher2,
+		<RelayerRewardsKeyProviderOf<T, I> as StorageDoubleMapKeyProvider>::Key2,
+		<RelayerRewardsKeyProviderOf<T, I> as StorageDoubleMapKeyProvider>::Value,
 		OptionQuery,
 	>;
 
@@ -457,7 +477,7 @@ pub mod pallet {
 	/// relayer is present.
 	#[pallet::storage]
 	#[pallet::getter(fn registered_relayer)]
-	pub type RegisteredRelayers<T: Config> = StorageMap<
+	pub type RegisteredRelayers<T: Config<I>, I: 'static = ()> = StorageMap<
 		_,
 		Blake2_128Concat,
 		T::AccountId,
@@ -469,10 +489,10 @@ pub mod pallet {
 #[cfg(test)]
 mod tests {
 	use super::*;
+	use bp_messages::LaneIdType;
 	use mock::{RuntimeEvent as TestEvent, *};
 
 	use crate::Event::{RewardPaid, RewardRegistered};
-	use bp_messages::LaneId;
 	use bp_relayers::RewardsAccountOwner;
 	use frame_support::{
 		assert_noop, assert_ok,
@@ -596,16 +616,16 @@ mod tests {
 	fn pay_reward_from_account_actually_pays_reward() {
 		type Balances = pallet_balances::Pallet<TestRuntime>;
 		type PayLaneRewardFromAccount =
-			bp_relayers::PayRewardFromAccount<Balances, ThisChainAccountId>;
+			bp_relayers::PayRewardFromAccount<Balances, ThisChainAccountId, TestLaneIdType>;
 
 		run_test(|| {
 			let in_lane_0 = RewardsAccountParams::new(
-				LaneId::new(1, 2),
+				TestLaneIdType::try_new(1, 2).unwrap(),
 				*b"test",
 				RewardsAccountOwner::ThisChain,
 			);
 			let out_lane_1 = RewardsAccountParams::new(
-				LaneId::new(1, 3),
+				TestLaneIdType::try_new(1, 3).unwrap(),
 				*b"test",
 				RewardsAccountOwner::BridgedChain,
 			);
diff --git a/bridges/modules/relayers/src/migration.rs b/bridges/modules/relayers/src/migration.rs
new file mode 100644
index 0000000000000000000000000000000000000000..8bf473b300c2a15eb983fb3e4c6f4c4c5dad0105
--- /dev/null
+++ b/bridges/modules/relayers/src/migration.rs
@@ -0,0 +1,243 @@
+// Copyright (C) Parity Technologies (UK) Ltd.
+// This file is part of Parity Bridges Common.
+
+// Parity Bridges Common is free software: you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+
+// Parity Bridges Common is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+// GNU General Public License for more details.
+
+// 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/>.
+
+//! A module that is responsible for migration of storage.
+
+use frame_support::{
+	traits::{Get, StorageVersion},
+	weights::Weight,
+};
+
+/// The in-code storage version.
+pub const STORAGE_VERSION: StorageVersion = StorageVersion::new(1);
+
+/// This module contains data structures that are valid for the initial state of `0`.
+/// (used with v1 migration).
+pub mod v0 {
+	use crate::{Config, Pallet};
+	use bp_relayers::RewardsAccountOwner;
+	use bp_runtime::{ChainId, StorageDoubleMapKeyProvider};
+	use codec::{Codec, Decode, Encode, EncodeLike, MaxEncodedLen};
+	use frame_support::{pallet_prelude::OptionQuery, Blake2_128Concat, Identity};
+	use scale_info::TypeInfo;
+	use sp_runtime::traits::AccountIdConversion;
+	use sp_std::marker::PhantomData;
+
+	/// Structure used to identify the account that pays a reward to the relayer.
+	#[derive(Copy, Clone, Debug, Decode, Encode, Eq, PartialEq, TypeInfo, MaxEncodedLen)]
+	pub struct RewardsAccountParams<LaneId> {
+		/// lane_id
+		pub lane_id: LaneId,
+		/// bridged_chain_id
+		pub bridged_chain_id: ChainId,
+		/// owner
+		pub owner: RewardsAccountOwner,
+	}
+
+	impl<LaneId: Decode + Encode> RewardsAccountParams<LaneId> {
+		/// Create a new instance of `RewardsAccountParams`.
+		pub const fn new(
+			lane_id: LaneId,
+			bridged_chain_id: ChainId,
+			owner: RewardsAccountOwner,
+		) -> Self {
+			Self { lane_id, bridged_chain_id, owner }
+		}
+	}
+
+	impl<LaneId> sp_runtime::TypeId for RewardsAccountParams<LaneId> {
+		const TYPE_ID: [u8; 4] = *b"brap";
+	}
+
+	pub(crate) struct RelayerRewardsKeyProvider<AccountId, Reward, LaneId>(
+		PhantomData<(AccountId, Reward, LaneId)>,
+	);
+
+	impl<AccountId, Reward, LaneId> StorageDoubleMapKeyProvider
+		for RelayerRewardsKeyProvider<AccountId, Reward, LaneId>
+	where
+		AccountId: 'static + Codec + EncodeLike + Send + Sync,
+		Reward: 'static + Codec + EncodeLike + Send + Sync,
+		LaneId: Codec + EncodeLike + Send + Sync,
+	{
+		const MAP_NAME: &'static str = "RelayerRewards";
+
+		type Hasher1 = Blake2_128Concat;
+		type Key1 = AccountId;
+		type Hasher2 = Identity;
+		type Key2 = RewardsAccountParams<LaneId>;
+		type Value = Reward;
+	}
+
+	pub(crate) type RelayerRewardsKeyProviderOf<T, I> = RelayerRewardsKeyProvider<
+		<T as frame_system::Config>::AccountId,
+		<T as Config<I>>::Reward,
+		<T as Config<I>>::LaneId,
+	>;
+
+	#[frame_support::storage_alias]
+	pub(crate) type RelayerRewards<T: Config<I>, I: 'static> = StorageDoubleMap<
+		Pallet<T, I>,
+		<RelayerRewardsKeyProviderOf<T, I> as StorageDoubleMapKeyProvider>::Hasher1,
+		<RelayerRewardsKeyProviderOf<T, I> as StorageDoubleMapKeyProvider>::Key1,
+		<RelayerRewardsKeyProviderOf<T, I> as StorageDoubleMapKeyProvider>::Hasher2,
+		<RelayerRewardsKeyProviderOf<T, I> as StorageDoubleMapKeyProvider>::Key2,
+		<RelayerRewardsKeyProviderOf<T, I> as StorageDoubleMapKeyProvider>::Value,
+		OptionQuery,
+	>;
+
+	/// Reward account generator for `v0`.
+	pub struct PayRewardFromAccount<Account, LaneId>(PhantomData<(Account, LaneId)>);
+	impl<Account, LaneId> PayRewardFromAccount<Account, LaneId>
+	where
+		Account: Decode + Encode,
+		LaneId: Decode + Encode,
+	{
+		/// Return account that pays rewards based on the provided parameters.
+		pub fn rewards_account(params: RewardsAccountParams<LaneId>) -> Account {
+			params.into_sub_account_truncating(b"rewards-account")
+		}
+	}
+}
+
+/// This migration updates `RelayerRewards` where `RewardsAccountParams` was used as the key with
+/// `lane_id` as the first attribute, which affects `into_sub_account_truncating`. We are migrating
+/// this key to use the new `RewardsAccountParams` where `lane_id` is the last attribute.
+pub mod v1 {
+	use super::*;
+	use crate::{Config, Pallet};
+	use bp_relayers::RewardsAccountParams;
+	use frame_support::traits::UncheckedOnRuntimeUpgrade;
+	use sp_std::marker::PhantomData;
+
+	#[cfg(feature = "try-runtime")]
+	use crate::RelayerRewards;
+
+	/// Migrates the pallet storage to v1.
+	pub struct UncheckedMigrationV0ToV1<T, I>(PhantomData<(T, I)>);
+
+	#[cfg(feature = "try-runtime")]
+	const LOG_TARGET: &str = "runtime::bridge-relayers-migration";
+
+	impl<T: Config<I>, I: 'static> UncheckedOnRuntimeUpgrade for UncheckedMigrationV0ToV1<T, I> {
+		fn on_runtime_upgrade() -> Weight {
+			let mut weight = T::DbWeight::get().reads(1);
+
+			// list all rewards (we cannot do this as one step because of `drain` limitation)
+			let mut rewards_to_migrate =
+				sp_std::vec::Vec::with_capacity(v0::RelayerRewards::<T, I>::iter().count());
+			for (key1, key2, reward) in v0::RelayerRewards::<T, I>::drain() {
+				rewards_to_migrate.push((key1, key2, reward));
+				weight.saturating_accrue(T::DbWeight::get().reads_writes(1, 1));
+			}
+
+			// re-register rewards with new format of `RewardsAccountParams`.
+			for (key1, key2, reward) in rewards_to_migrate {
+				// expand old key
+				let v0::RewardsAccountParams { owner, lane_id, bridged_chain_id } = key2;
+
+				// re-register reward
+				Pallet::<T, I>::register_relayer_reward(
+					v1::RewardsAccountParams::new(lane_id, bridged_chain_id, owner),
+					&key1,
+					reward,
+				);
+				weight.saturating_accrue(T::DbWeight::get().reads_writes(1, 1));
+			}
+
+			weight
+		}
+
+		#[cfg(feature = "try-runtime")]
+		fn pre_upgrade() -> Result<sp_std::vec::Vec<u8>, sp_runtime::DispatchError> {
+			use codec::Encode;
+			use frame_support::BoundedBTreeMap;
+			use sp_runtime::traits::ConstU32;
+
+			// collect actual rewards
+			let mut rewards: BoundedBTreeMap<
+				(T::AccountId, T::LaneId),
+				T::Reward,
+				ConstU32<{ u32::MAX }>,
+			> = BoundedBTreeMap::new();
+			for (key1, key2, reward) in v0::RelayerRewards::<T, I>::iter() {
+				log::info!(target: LOG_TARGET, "Reward to migrate: {key1:?}::{key2:?} - {reward:?}");
+				rewards = rewards
+					.try_mutate(|inner| {
+						inner
+							.entry((key1.clone(), key2.lane_id))
+							.and_modify(|value| *value += reward)
+							.or_insert(reward);
+					})
+					.unwrap();
+			}
+			log::info!(target: LOG_TARGET, "Found total rewards to migrate: {rewards:?}");
+
+			Ok(rewards.encode())
+		}
+
+		#[cfg(feature = "try-runtime")]
+		fn post_upgrade(state: sp_std::vec::Vec<u8>) -> Result<(), sp_runtime::DispatchError> {
+			use codec::Decode;
+			use frame_support::BoundedBTreeMap;
+			use sp_runtime::traits::ConstU32;
+
+			let rewards_before: BoundedBTreeMap<
+				(T::AccountId, T::LaneId),
+				T::Reward,
+				ConstU32<{ u32::MAX }>,
+			> = Decode::decode(&mut &state[..]).unwrap();
+
+			// collect migrated rewards
+			let mut rewards_after: BoundedBTreeMap<
+				(T::AccountId, T::LaneId),
+				T::Reward,
+				ConstU32<{ u32::MAX }>,
+			> = BoundedBTreeMap::new();
+			for (key1, key2, reward) in v1::RelayerRewards::<T, I>::iter() {
+				log::info!(target: LOG_TARGET, "Migrated rewards: {key1:?}::{key2:?} - {reward:?}");
+				rewards_after = rewards_after
+					.try_mutate(|inner| {
+						inner
+							.entry((key1.clone(), *key2.lane_id()))
+							.and_modify(|value| *value += reward)
+							.or_insert(reward);
+					})
+					.unwrap();
+			}
+			log::info!(target: LOG_TARGET, "Found total migrated rewards: {rewards_after:?}");
+
+			frame_support::ensure!(
+				rewards_before == rewards_after,
+				"The rewards were not migrated correctly!."
+			);
+
+			log::info!(target: LOG_TARGET, "migrated all.");
+			Ok(())
+		}
+	}
+
+	/// [`UncheckedMigrationV0ToV1`] wrapped in a
+	/// [`VersionedMigration`](frame_support::migrations::VersionedMigration), ensuring the
+	/// migration is only performed when on-chain version is 0.
+	pub type MigrationToV1<T, I> = frame_support::migrations::VersionedMigration<
+		0,
+		1,
+		UncheckedMigrationV0ToV1<T, I>,
+		Pallet<T, I>,
+		<T as frame_system::Config>::DbWeight,
+	>;
+}
diff --git a/bridges/modules/relayers/src/mock.rs b/bridges/modules/relayers/src/mock.rs
index de1d292b7c0f022d9b94213dd3a8ecea814f409a..d186e968e648264a81e465ff01c4bf1d6082b048 100644
--- a/bridges/modules/relayers/src/mock.rs
+++ b/bridges/modules/relayers/src/mock.rs
@@ -21,7 +21,7 @@ use crate as pallet_bridge_relayers;
 use bp_header_chain::ChainWithGrandpa;
 use bp_messages::{
 	target_chain::{DispatchMessage, MessageDispatch},
-	ChainWithMessages, LaneId, MessageNonce,
+	ChainWithMessages, HashedLaneId, LaneIdType, MessageNonce,
 };
 use bp_parachains::SingleParaStoredHeaderDataBuilder;
 use bp_relayers::{
@@ -75,6 +75,13 @@ pub const TEST_BRIDGED_CHAIN_ID: ChainId = *b"brdg";
 /// Maximal extrinsic size at the `BridgedChain`.
 pub const BRIDGED_CHAIN_MAX_EXTRINSIC_SIZE: u32 = 1024;
 
+/// Lane identifier type used for tests.
+pub type TestLaneIdType = HashedLaneId;
+/// Lane that we're using in tests.
+pub fn test_lane_id() -> TestLaneIdType {
+	TestLaneIdType::try_new(1, 2).unwrap()
+}
+
 /// Underlying chain of `ThisChain`.
 pub struct ThisUnderlyingChain;
 
@@ -253,10 +260,10 @@ impl pallet_bridge_messages::Config for TestRuntime {
 	type WeightInfo = pallet_bridge_messages::weights::BridgeWeight<TestRuntime>;
 
 	type OutboundPayload = Vec<u8>;
-
 	type InboundPayload = Vec<u8>;
-	type DeliveryPayments = ();
+	type LaneId = TestLaneIdType;
 
+	type DeliveryPayments = ();
 	type DeliveryConfirmationPayments = pallet_bridge_relayers::DeliveryConfirmationPaymentsAdapter<
 		TestRuntime,
 		(),
@@ -276,15 +283,20 @@ impl pallet_bridge_relayers::Config for TestRuntime {
 	type PaymentProcedure = TestPaymentProcedure;
 	type StakeAndSlash = TestStakeAndSlash;
 	type WeightInfo = ();
+	type LaneId = TestLaneIdType;
 }
 
 #[cfg(feature = "runtime-benchmarks")]
 impl pallet_bridge_relayers::benchmarking::Config for TestRuntime {
-	fn prepare_rewards_account(account_params: RewardsAccountParams, reward: ThisChainBalance) {
-		let rewards_account =
-			bp_relayers::PayRewardFromAccount::<Balances, ThisChainAccountId>::rewards_account(
-				account_params,
-			);
+	fn prepare_rewards_account(
+		account_params: RewardsAccountParams<Self::LaneId>,
+		reward: Self::Reward,
+	) {
+		let rewards_account = bp_relayers::PayRewardFromAccount::<
+			Balances,
+			ThisChainAccountId,
+			Self::LaneId,
+		>::rewards_account(account_params);
 		Self::deposit_account(rewards_account, reward);
 	}
 
@@ -306,17 +318,18 @@ pub const REGISTER_RELAYER: ThisChainAccountId = 42;
 pub struct TestPaymentProcedure;
 
 impl TestPaymentProcedure {
-	pub fn rewards_account(params: RewardsAccountParams) -> ThisChainAccountId {
-		PayRewardFromAccount::<(), ThisChainAccountId>::rewards_account(params)
+	pub fn rewards_account(params: RewardsAccountParams<TestLaneIdType>) -> ThisChainAccountId {
+		PayRewardFromAccount::<(), ThisChainAccountId, TestLaneIdType>::rewards_account(params)
 	}
 }
 
 impl PaymentProcedure<ThisChainAccountId, ThisChainBalance> for TestPaymentProcedure {
 	type Error = ();
+	type LaneId = TestLaneIdType;
 
 	fn pay_reward(
 		relayer: &ThisChainAccountId,
-		_lane_id: RewardsAccountParams,
+		_lane_id: RewardsAccountParams<Self::LaneId>,
 		_reward: ThisChainBalance,
 	) -> Result<(), Self::Error> {
 		match *relayer {
@@ -330,7 +343,7 @@ impl PaymentProcedure<ThisChainAccountId, ThisChainBalance> for TestPaymentProce
 pub struct DummyMessageDispatch;
 
 impl DummyMessageDispatch {
-	pub fn deactivate(lane: LaneId) {
+	pub fn deactivate(lane: TestLaneIdType) {
 		frame_support::storage::unhashed::put(&(b"inactive", lane).encode()[..], &false);
 	}
 }
@@ -338,26 +351,33 @@ impl DummyMessageDispatch {
 impl MessageDispatch for DummyMessageDispatch {
 	type DispatchPayload = Vec<u8>;
 	type DispatchLevelResult = ();
+	type LaneId = TestLaneIdType;
 
-	fn is_active(lane: LaneId) -> bool {
+	fn is_active(lane: Self::LaneId) -> bool {
 		frame_support::storage::unhashed::take::<bool>(&(b"inactive", lane).encode()[..]) !=
 			Some(false)
 	}
 
-	fn dispatch_weight(_message: &mut DispatchMessage<Self::DispatchPayload>) -> Weight {
+	fn dispatch_weight(
+		_message: &mut DispatchMessage<Self::DispatchPayload, Self::LaneId>,
+	) -> Weight {
 		Weight::zero()
 	}
 
 	fn dispatch(
-		_: DispatchMessage<Self::DispatchPayload>,
+		_: DispatchMessage<Self::DispatchPayload, Self::LaneId>,
 	) -> MessageDispatchResult<Self::DispatchLevelResult> {
 		MessageDispatchResult { unspent_weight: Weight::zero(), dispatch_level_result: () }
 	}
 }
 
 /// Reward account params that we are using in tests.
-pub fn test_reward_account_param() -> RewardsAccountParams {
-	RewardsAccountParams::new(LaneId::new(1, 2), *b"test", RewardsAccountOwner::ThisChain)
+pub fn test_reward_account_param() -> RewardsAccountParams<TestLaneIdType> {
+	RewardsAccountParams::new(
+		TestLaneIdType::try_new(1, 2).unwrap(),
+		*b"test",
+		RewardsAccountOwner::ThisChain,
+	)
 }
 
 /// Return test externalities to use in tests.
diff --git a/bridges/modules/relayers/src/payment_adapter.rs b/bridges/modules/relayers/src/payment_adapter.rs
index 3693793a3e5cac36d7fc46574abf2153249e4467..5383cba5ecbddcf565aaabcb6852d970c6fcdcb8 100644
--- a/bridges/modules/relayers/src/payment_adapter.rs
+++ b/bridges/modules/relayers/src/payment_adapter.rs
@@ -20,11 +20,12 @@ use crate::{Config, Pallet};
 
 use bp_messages::{
 	source_chain::{DeliveryConfirmationPayments, RelayersRewards},
-	LaneId, MessageNonce,
+	MessageNonce,
 };
 use bp_relayers::{RewardsAccountOwner, RewardsAccountParams};
 use bp_runtime::Chain;
 use frame_support::{sp_runtime::SaturatedConversion, traits::Get};
+use pallet_bridge_messages::LaneIdOf;
 use sp_arithmetic::traits::{Saturating, Zero};
 use sp_std::{collections::vec_deque::VecDeque, marker::PhantomData, ops::RangeInclusive};
 
@@ -34,17 +35,17 @@ pub struct DeliveryConfirmationPaymentsAdapter<T, MI, DeliveryReward>(
 	PhantomData<(T, MI, DeliveryReward)>,
 );
 
-impl<T, MI, DeliveryReward> DeliveryConfirmationPayments<T::AccountId>
+impl<T, MI, DeliveryReward> DeliveryConfirmationPayments<T::AccountId, LaneIdOf<T, MI>>
 	for DeliveryConfirmationPaymentsAdapter<T, MI, DeliveryReward>
 where
-	T: Config + pallet_bridge_messages::Config<MI>,
+	T: Config + pallet_bridge_messages::Config<MI, LaneId = <T as Config>::LaneId>,
 	MI: 'static,
 	DeliveryReward: Get<T::Reward>,
 {
 	type Error = &'static str;
 
 	fn pay_reward(
-		lane_id: LaneId,
+		lane_id: LaneIdOf<T, MI>,
 		messages_relayers: VecDeque<bp_messages::UnrewardedRelayer<T::AccountId>>,
 		confirmation_relayer: &T::AccountId,
 		received_range: &RangeInclusive<bp_messages::MessageNonce>,
@@ -72,7 +73,7 @@ where
 fn register_relayers_rewards<T: Config>(
 	confirmation_relayer: &T::AccountId,
 	relayers_rewards: RelayersRewards<T::AccountId>,
-	lane_id: RewardsAccountParams,
+	lane_id: RewardsAccountParams<T::LaneId>,
 	delivery_fee: T::Reward,
 ) {
 	// reward every relayer except `confirmation_relayer`
diff --git a/bridges/modules/relayers/src/stake_adapter.rs b/bridges/modules/relayers/src/stake_adapter.rs
index 0c965e9e6bff8182e9c45498faa3cb4535a87b62..1792f0be8316acfc9b32508198654c035cc51289 100644
--- a/bridges/modules/relayers/src/stake_adapter.rs
+++ b/bridges/modules/relayers/src/stake_adapter.rs
@@ -18,7 +18,7 @@
 //! mechanism of the relayers pallet.
 
 use bp_relayers::{ExplicitOrAccountParams, PayRewardFromAccount, StakeAndSlash};
-use codec::Codec;
+use codec::{Codec, Decode, Encode};
 use frame_support::traits::{tokens::BalanceStatus, NamedReservableCurrency};
 use sp_runtime::{traits::Get, DispatchError, DispatchResult};
 use sp_std::{fmt::Debug, marker::PhantomData};
@@ -53,15 +53,15 @@ where
 		Currency::unreserve_named(&ReserveId::get(), relayer, amount)
 	}
 
-	fn repatriate_reserved(
+	fn repatriate_reserved<LaneId: Decode + Encode>(
 		relayer: &AccountId,
-		beneficiary: ExplicitOrAccountParams<AccountId>,
+		beneficiary: ExplicitOrAccountParams<AccountId, LaneId>,
 		amount: Currency::Balance,
 	) -> Result<Currency::Balance, DispatchError> {
 		let beneficiary_account = match beneficiary {
 			ExplicitOrAccountParams::Explicit(account) => account,
 			ExplicitOrAccountParams::Params(params) =>
-				PayRewardFromAccount::<(), AccountId>::rewards_account(params),
+				PayRewardFromAccount::<(), AccountId, LaneId>::rewards_account(params),
 		};
 		Currency::repatriate_reserved_named(
 			&ReserveId::get(),
diff --git a/bridges/modules/xcm-bridge-hub/src/dispatcher.rs b/bridges/modules/xcm-bridge-hub/src/dispatcher.rs
index 2412bb0f3bb0cb14ea00c702d701e92e5286cb43..dd855c7069aadf1fc95840c462530cd7bb7bcb00 100644
--- a/bridges/modules/xcm-bridge-hub/src/dispatcher.rs
+++ b/bridges/modules/xcm-bridge-hub/src/dispatcher.rs
@@ -23,10 +23,7 @@
 
 use crate::{Config, Pallet, LOG_TARGET};
 
-use bp_messages::{
-	target_chain::{DispatchMessage, MessageDispatch},
-	LaneId,
-};
+use bp_messages::target_chain::{DispatchMessage, MessageDispatch};
 use bp_runtime::messages::MessageDispatchResult;
 use bp_xcm_bridge_hub::{LocalXcmChannelManager, XcmAsPlainPayload};
 use codec::{Decode, Encode};
@@ -58,15 +55,18 @@ where
 {
 	type DispatchPayload = XcmAsPlainPayload;
 	type DispatchLevelResult = XcmBlobMessageDispatchResult;
+	type LaneId = T::LaneId;
 
-	fn is_active(lane: LaneId) -> bool {
+	fn is_active(lane: Self::LaneId) -> bool {
 		Pallet::<T, I>::bridge_by_lane_id(&lane)
 			.and_then(|(_, bridge)| bridge.bridge_origin_relative_location.try_as().cloned().ok())
 			.map(|recipient: Location| !T::LocalXcmChannelManager::is_congested(&recipient))
 			.unwrap_or(false)
 	}
 
-	fn dispatch_weight(message: &mut DispatchMessage<Self::DispatchPayload>) -> Weight {
+	fn dispatch_weight(
+		message: &mut DispatchMessage<Self::DispatchPayload, Self::LaneId>,
+	) -> Weight {
 		match message.data.payload {
 			Ok(ref payload) => {
 				let payload_size = payload.encoded_size().saturated_into();
@@ -77,14 +77,14 @@ where
 	}
 
 	fn dispatch(
-		message: DispatchMessage<Self::DispatchPayload>,
+		message: DispatchMessage<Self::DispatchPayload, Self::LaneId>,
 	) -> MessageDispatchResult<Self::DispatchLevelResult> {
 		let payload = match message.data.payload {
 			Ok(payload) => payload,
 			Err(e) => {
 				log::error!(
 					target: LOG_TARGET,
-					"dispatch - payload error: {e:?} for lane_id: {} and message_nonce: {:?}",
+					"dispatch - payload error: {e:?} for lane_id: {:?} and message_nonce: {:?}",
 					message.key.lane_id,
 					message.key.nonce
 				);
@@ -98,7 +98,7 @@ where
 			Ok(_) => {
 				log::debug!(
 					target: LOG_TARGET,
-					"dispatch - `DispatchBlob::dispatch_blob` was ok for lane_id: {} and message_nonce: {:?}",
+					"dispatch - `DispatchBlob::dispatch_blob` was ok for lane_id: {:?} and message_nonce: {:?}",
 					message.key.lane_id,
 					message.key.nonce
 				);
@@ -107,7 +107,7 @@ where
 			Err(e) => {
 				log::error!(
 					target: LOG_TARGET,
-					"dispatch - `DispatchBlob::dispatch_blob` failed with error: {e:?} for lane_id: {} and message_nonce: {:?}",
+					"dispatch - `DispatchBlob::dispatch_blob` failed with error: {e:?} for lane_id: {:?} and message_nonce: {:?}",
 					message.key.lane_id,
 					message.key.nonce
 				);
@@ -123,13 +123,13 @@ mod tests {
 	use super::*;
 	use crate::{mock::*, Bridges, LaneToBridge, LanesManagerOf};
 
-	use bp_messages::{target_chain::DispatchMessageData, MessageKey};
+	use bp_messages::{target_chain::DispatchMessageData, LaneIdType, MessageKey};
 	use bp_xcm_bridge_hub::{Bridge, BridgeLocations, BridgeState};
 	use frame_support::assert_ok;
 	use pallet_bridge_messages::InboundLaneStorage;
 	use xcm_executor::traits::ConvertLocation;
 
-	fn bridge() -> (Box<BridgeLocations>, LaneId) {
+	fn bridge() -> (Box<BridgeLocations>, TestLaneIdType) {
 		let origin = OpenBridgeOrigin::sibling_parachain_origin();
 		let with = bridged_asset_hub_universal_location();
 		let locations =
@@ -194,16 +194,16 @@ mod tests {
 		});
 	}
 
-	fn invalid_message() -> DispatchMessage<Vec<u8>> {
+	fn invalid_message() -> DispatchMessage<Vec<u8>, TestLaneIdType> {
 		DispatchMessage {
-			key: MessageKey { lane_id: LaneId::new(1, 2), nonce: 1 },
+			key: MessageKey { lane_id: TestLaneIdType::try_new(1, 2).unwrap(), nonce: 1 },
 			data: DispatchMessageData { payload: Err(codec::Error::from("test")) },
 		}
 	}
 
-	fn valid_message() -> DispatchMessage<Vec<u8>> {
+	fn valid_message() -> DispatchMessage<Vec<u8>, TestLaneIdType> {
 		DispatchMessage {
-			key: MessageKey { lane_id: LaneId::new(1, 2), nonce: 1 },
+			key: MessageKey { lane_id: TestLaneIdType::try_new(1, 2).unwrap(), nonce: 1 },
 			data: DispatchMessageData { payload: Ok(vec![42]) },
 		}
 	}
diff --git a/bridges/modules/xcm-bridge-hub/src/exporter.rs b/bridges/modules/xcm-bridge-hub/src/exporter.rs
index b42ae1e267f4be55d5fd0efff7e39cd95bc19cc0..5afb9f36bc9414bde920eec3d3e84bc7487f711d 100644
--- a/bridges/modules/xcm-bridge-hub/src/exporter.rs
+++ b/bridges/modules/xcm-bridge-hub/src/exporter.rs
@@ -26,7 +26,7 @@ use crate::{BridgeOf, Bridges};
 
 use bp_messages::{
 	source_chain::{MessagesBridge, OnMessagesDelivered},
-	LaneId, MessageNonce,
+	MessageNonce,
 };
 use bp_xcm_bridge_hub::{BridgeId, BridgeState, LocalXcmChannelManager, XcmAsPlainPayload};
 use frame_support::{ensure, traits::Get};
@@ -62,7 +62,7 @@ where
 	type Ticket = (
 		BridgeId,
 		BridgeOf<T, I>,
-		<MessagesPallet<T, I> as MessagesBridge<T::OutboundPayload>>::SendMessageArgs,
+		<MessagesPallet<T, I> as MessagesBridge<T::OutboundPayload, T::LaneId>>::SendMessageArgs,
 		XcmHash,
 	);
 
@@ -94,7 +94,7 @@ where
 						"Destination: {dest:?} is already universal, checking dest_network: {dest_network:?} and network: {network:?} if matches: {:?}",
 						dest_network == network
 					);
-					ensure!(dest_network == network, SendError::Unroutable);
+					ensure!(dest_network == network, SendError::NotApplicable);
 					// ok, `dest` looks like a universal location, so let's use it
 					dest
 				},
@@ -108,23 +108,12 @@ where
 							error_data.0,
 							error_data.1,
 						);
-						SendError::Unroutable
+						SendError::NotApplicable
 					})?
 				},
 			}
 		};
 
-		// check if we are able to route the message. We use existing `HaulBlobExporter` for that.
-		// It will make all required changes and will encode message properly, so that the
-		// `DispatchBlob` at the bridged bridge hub will be able to decode it
-		let ((blob, id), price) = PalletAsHaulBlobExporter::<T, I>::validate(
-			network,
-			channel,
-			universal_source,
-			destination,
-			message,
-		)?;
-
 		// prepare the origin relative location
 		let bridge_origin_relative_location =
 			bridge_origin_universal_location.relative_to(&T::UniversalLocation::get());
@@ -139,9 +128,28 @@ where
 				target: LOG_TARGET,
 				"Validate `bridge_locations` with error: {e:?}",
 			);
-			SendError::Unroutable
+			SendError::NotApplicable
+		})?;
+		let bridge = Self::bridge(locations.bridge_id()).ok_or_else(|| {
+			log::error!(
+				target: LOG_TARGET,
+				"No opened bridge for requested bridge_origin_relative_location: {:?} and bridge_destination_universal_location: {:?}",
+				locations.bridge_origin_relative_location(),
+				locations.bridge_destination_universal_location(),
+			);
+			SendError::NotApplicable
 		})?;
-		let bridge = Self::bridge(locations.bridge_id()).ok_or(SendError::Unroutable)?;
+
+		// check if we are able to route the message. We use existing `HaulBlobExporter` for that.
+		// It will make all required changes and will encode message properly, so that the
+		// `DispatchBlob` at the bridged bridge hub will be able to decode it
+		let ((blob, id), price) = PalletAsHaulBlobExporter::<T, I>::validate(
+			network,
+			channel,
+			universal_source,
+			destination,
+			message,
+		)?;
 
 		let bridge_message = MessagesPallet::<T, I>::validate_message(bridge.lane_id, &blob)
 			.map_err(|e| {
@@ -190,8 +198,8 @@ where
 	}
 }
 
-impl<T: Config<I>, I: 'static> OnMessagesDelivered for Pallet<T, I> {
-	fn on_messages_delivered(lane_id: LaneId, enqueued_messages: MessageNonce) {
+impl<T: Config<I>, I: 'static> OnMessagesDelivered<T::LaneId> for Pallet<T, I> {
+	fn on_messages_delivered(lane_id: T::LaneId, enqueued_messages: MessageNonce) {
 		Self::on_bridge_messages_delivered(lane_id, enqueued_messages);
 	}
 }
@@ -265,7 +273,7 @@ impl<T: Config<I>, I: 'static> Pallet<T, I> {
 	}
 
 	/// Must be called whenever we receive a message delivery confirmation.
-	fn on_bridge_messages_delivered(lane_id: LaneId, enqueued_messages: MessageNonce) {
+	fn on_bridge_messages_delivered(lane_id: T::LaneId, enqueued_messages: MessageNonce) {
 		// if the bridge queue is still congested, we don't want to do anything
 		let is_congested = enqueued_messages > OUTBOUND_LANE_UNCONGESTED_THRESHOLD;
 		if is_congested {
@@ -373,7 +381,7 @@ mod tests {
 		BridgedUniversalDestination::get()
 	}
 
-	fn open_lane() -> (BridgeLocations, LaneId) {
+	fn open_lane() -> (BridgeLocations, TestLaneIdType) {
 		// open expected outbound lane
 		let origin = OpenBridgeOrigin::sibling_parachain_origin();
 		let with = bridged_asset_hub_universal_location();
@@ -430,7 +438,7 @@ mod tests {
 		(*locations, lane_id)
 	}
 
-	fn open_lane_and_send_regular_message() -> (BridgeId, LaneId) {
+	fn open_lane_and_send_regular_message() -> (BridgeId, TestLaneIdType) {
 		let (locations, lane_id) = open_lane();
 
 		// now let's try to enqueue message using our `ExportXcm` implementation
@@ -466,7 +474,7 @@ mod tests {
 		run_test(|| {
 			let (bridge_id, _) = open_lane_and_send_regular_message();
 			assert!(!TestLocalXcmChannelManager::is_bridge_suspened());
-			assert_eq!(XcmOverBridge::bridge(bridge_id).unwrap().state, BridgeState::Opened);
+			assert_eq!(XcmOverBridge::bridge(&bridge_id).unwrap().state, BridgeState::Opened);
 		});
 	}
 
@@ -495,11 +503,11 @@ mod tests {
 			}
 
 			assert!(!TestLocalXcmChannelManager::is_bridge_suspened());
-			assert_eq!(XcmOverBridge::bridge(bridge_id).unwrap().state, BridgeState::Opened);
+			assert_eq!(XcmOverBridge::bridge(&bridge_id).unwrap().state, BridgeState::Opened);
 
 			open_lane_and_send_regular_message();
 			assert!(TestLocalXcmChannelManager::is_bridge_suspened());
-			assert_eq!(XcmOverBridge::bridge(bridge_id).unwrap().state, BridgeState::Suspended);
+			assert_eq!(XcmOverBridge::bridge(&bridge_id).unwrap().state, BridgeState::Suspended);
 		});
 	}
 
@@ -516,7 +524,7 @@ mod tests {
 			);
 
 			assert!(!TestLocalXcmChannelManager::is_bridge_resumed());
-			assert_eq!(XcmOverBridge::bridge(bridge_id).unwrap().state, BridgeState::Suspended);
+			assert_eq!(XcmOverBridge::bridge(&bridge_id).unwrap().state, BridgeState::Suspended);
 		});
 	}
 
@@ -530,7 +538,7 @@ mod tests {
 			);
 
 			assert!(!TestLocalXcmChannelManager::is_bridge_resumed());
-			assert_eq!(XcmOverBridge::bridge(bridge_id).unwrap().state, BridgeState::Opened);
+			assert_eq!(XcmOverBridge::bridge(&bridge_id).unwrap().state, BridgeState::Opened);
 		});
 	}
 
@@ -547,7 +555,7 @@ mod tests {
 			);
 
 			assert!(TestLocalXcmChannelManager::is_bridge_resumed());
-			assert_eq!(XcmOverBridge::bridge(bridge_id).unwrap().state, BridgeState::Opened);
+			assert_eq!(XcmOverBridge::bridge(&bridge_id).unwrap().state, BridgeState::Opened);
 		});
 	}
 
@@ -679,4 +687,97 @@ mod tests {
 			);
 		})
 	}
+
+	#[test]
+	fn validate_works() {
+		run_test(|| {
+			let xcm: Xcm<()> = vec![ClearOrigin].into();
+
+			// check that router does not consume when `NotApplicable`
+			let mut xcm_wrapper = Some(xcm.clone());
+			let mut universal_source_wrapper = Some(universal_source());
+
+			// wrong `NetworkId`
+			let mut dest_wrapper = Some(bridged_relative_destination());
+			assert_eq!(
+				XcmOverBridge::validate(
+					NetworkId::ByGenesis([0; 32]),
+					0,
+					&mut universal_source_wrapper,
+					&mut dest_wrapper,
+					&mut xcm_wrapper,
+				),
+				Err(SendError::NotApplicable),
+			);
+			// dest and xcm is NOT consumed and untouched
+			assert_eq!(&Some(xcm.clone()), &xcm_wrapper);
+			assert_eq!(&Some(universal_source()), &universal_source_wrapper);
+			assert_eq!(&Some(bridged_relative_destination()), &dest_wrapper);
+
+			// dest starts with wrong `NetworkId`
+			let mut invalid_dest_wrapper = Some(
+				[GlobalConsensus(NetworkId::ByGenesis([0; 32])), Parachain(BRIDGED_ASSET_HUB_ID)]
+					.into(),
+			);
+			assert_eq!(
+				XcmOverBridge::validate(
+					BridgedRelayNetwork::get(),
+					0,
+					&mut Some(universal_source()),
+					&mut invalid_dest_wrapper,
+					&mut xcm_wrapper,
+				),
+				Err(SendError::NotApplicable),
+			);
+			// dest and xcm is NOT consumed and untouched
+			assert_eq!(&Some(xcm.clone()), &xcm_wrapper);
+			assert_eq!(&Some(universal_source()), &universal_source_wrapper);
+			assert_eq!(
+				&Some(
+					[
+						GlobalConsensus(NetworkId::ByGenesis([0; 32]),),
+						Parachain(BRIDGED_ASSET_HUB_ID)
+					]
+					.into()
+				),
+				&invalid_dest_wrapper
+			);
+
+			// no opened lane for dest
+			let mut dest_without_lane_wrapper =
+				Some([GlobalConsensus(BridgedRelayNetwork::get()), Parachain(5679)].into());
+			assert_eq!(
+				XcmOverBridge::validate(
+					BridgedRelayNetwork::get(),
+					0,
+					&mut Some(universal_source()),
+					&mut dest_without_lane_wrapper,
+					&mut xcm_wrapper,
+				),
+				Err(SendError::NotApplicable),
+			);
+			// dest and xcm is NOT consumed and untouched
+			assert_eq!(&Some(xcm.clone()), &xcm_wrapper);
+			assert_eq!(&Some(universal_source()), &universal_source_wrapper);
+			assert_eq!(
+				&Some([GlobalConsensus(BridgedRelayNetwork::get(),), Parachain(5679)].into()),
+				&dest_without_lane_wrapper
+			);
+
+			// ok
+			let _ = open_lane();
+			let mut dest_wrapper = Some(bridged_relative_destination());
+			assert_ok!(XcmOverBridge::validate(
+				BridgedRelayNetwork::get(),
+				0,
+				&mut Some(universal_source()),
+				&mut dest_wrapper,
+				&mut xcm_wrapper,
+			));
+			// dest and xcm IS consumed
+			assert_eq!(None, xcm_wrapper);
+			assert_eq!(&Some(universal_source()), &universal_source_wrapper);
+			assert_eq!(None, dest_wrapper);
+		});
+	}
 }
diff --git a/bridges/modules/xcm-bridge-hub/src/lib.rs b/bridges/modules/xcm-bridge-hub/src/lib.rs
index 02d578386a7501894ac2b96400578ecf22f2ff77..22c60fb4ad60bb56a1a7748b497c640a5775adac 100644
--- a/bridges/modules/xcm-bridge-hub/src/lib.rs
+++ b/bridges/modules/xcm-bridge-hub/src/lib.rs
@@ -143,7 +143,7 @@
 #![warn(missing_docs)]
 #![cfg_attr(not(feature = "std"), no_std)]
 
-use bp_messages::{LaneId, LaneState, MessageNonce};
+use bp_messages::{LaneState, MessageNonce};
 use bp_runtime::{AccountIdOf, BalanceOf, RangeInclusiveExt};
 pub use bp_xcm_bridge_hub::{Bridge, BridgeId, BridgeState};
 use bp_xcm_bridge_hub::{BridgeLocations, BridgeLocationsError, LocalXcmChannelManager};
@@ -213,9 +213,8 @@ pub mod pallet {
 		type DestinationVersion: GetVersion;
 
 		/// The origin that is allowed to call privileged operations on the pallet, e.g. open/close
-		/// bridge for location that coresponds to `Self::BridgeOriginAccountIdConverter` and
-		/// `Self::BridgedNetwork`.
-		type AdminOrigin: EnsureOrigin<<Self as SystemConfig>::RuntimeOrigin>;
+		/// bridge for locations.
+		type ForceOrigin: EnsureOrigin<<Self as SystemConfig>::RuntimeOrigin>;
 		/// A set of XCM locations within local consensus system that are allowed to open
 		/// bridges with remote destinations.
 		type OpenBridgeOrigin: EnsureOrigin<
@@ -248,10 +247,13 @@ pub mod pallet {
 	}
 
 	/// An alias for the bridge metadata.
-	pub type BridgeOf<T, I> = Bridge<ThisChainOf<T, I>>;
+	pub type BridgeOf<T, I> = Bridge<ThisChainOf<T, I>, LaneIdOf<T, I>>;
 	/// An alias for this chain.
 	pub type ThisChainOf<T, I> =
 		pallet_bridge_messages::ThisChainOf<T, <T as Config<I>>::BridgeMessagesPalletInstance>;
+	/// An alias for lane identifier type.
+	pub type LaneIdOf<T, I> =
+		<T as BridgeMessagesConfig<<T as Config<I>>::BridgeMessagesPalletInstance>>::LaneId;
 	/// An alias for the associated lanes manager.
 	pub type LanesManagerOf<T, I> =
 		pallet_bridge_messages::LanesManager<T, <T as Config<I>>::BridgeMessagesPalletInstance>;
@@ -392,7 +394,7 @@ pub mod pallet {
 				// deposit the `ClosingBridge` event
 				Self::deposit_event(Event::<T, I>::ClosingBridge {
 					bridge_id: *locations.bridge_id(),
-					lane_id: bridge.lane_id,
+					lane_id: bridge.lane_id.into(),
 					pruned_messages,
 					enqueued_messages,
 				});
@@ -439,7 +441,7 @@ pub mod pallet {
 			// deposit the `BridgePruned` event
 			Self::deposit_event(Event::<T, I>::BridgePruned {
 				bridge_id: *locations.bridge_id(),
-				lane_id: bridge.lane_id,
+				lane_id: bridge.lane_id.into(),
 				bridge_deposit: released_deposit,
 				pruned_messages,
 			});
@@ -449,9 +451,10 @@ pub mod pallet {
 	}
 
 	impl<T: Config<I>, I: 'static> Pallet<T, I> {
-		pub(crate) fn do_open_bridge(
+		/// Open bridge for lane.
+		pub fn do_open_bridge(
 			locations: Box<BridgeLocations>,
-			lane_id: LaneId,
+			lane_id: T::LaneId,
 			create_lanes: bool,
 		) -> Result<(), DispatchError> {
 			// reserve balance on the origin's sovereign account (if needed)
@@ -542,7 +545,7 @@ pub mod pallet {
 				remote_endpoint: Box::new(
 					locations.bridge_destination_universal_location().clone(),
 				),
-				lane_id,
+				lane_id: lane_id.into(),
 			});
 
 			Ok(())
@@ -585,10 +588,15 @@ pub mod pallet {
 			})
 		}
 
+		/// Return bridge metadata by bridge_id
+		pub fn bridge(bridge_id: &BridgeId) -> Option<BridgeOf<T, I>> {
+			Bridges::<T, I>::get(bridge_id)
+		}
+
 		/// Return bridge metadata by lane_id
-		pub fn bridge_by_lane_id(lane_id: &LaneId) -> Option<(BridgeId, BridgeOf<T, I>)> {
+		pub fn bridge_by_lane_id(lane_id: &T::LaneId) -> Option<(BridgeId, BridgeOf<T, I>)> {
 			LaneToBridge::<T, I>::get(lane_id)
-				.and_then(|bridge_id| Self::bridge(bridge_id).map(|bridge| (bridge_id, bridge)))
+				.and_then(|bridge_id| Self::bridge(&bridge_id).map(|bridge| (bridge_id, bridge)))
 		}
 	}
 
@@ -634,7 +642,7 @@ pub mod pallet {
 		pub fn do_try_state_for_bridge(
 			bridge_id: BridgeId,
 			bridge: BridgeOf<T, I>,
-		) -> Result<LaneId, sp_runtime::TryRuntimeError> {
+		) -> Result<T::LaneId, sp_runtime::TryRuntimeError> {
 			log::info!(target: LOG_TARGET, "Checking `do_try_state_for_bridge` for bridge_id: {bridge_id:?} and bridge: {bridge:?}");
 
 			// check `BridgeId` points to the same `LaneId` and vice versa.
@@ -707,13 +715,12 @@ pub mod pallet {
 
 	/// All registered bridges.
 	#[pallet::storage]
-	#[pallet::getter(fn bridge)]
 	pub type Bridges<T: Config<I>, I: 'static = ()> =
 		StorageMap<_, Identity, BridgeId, BridgeOf<T, I>>;
 	/// All registered `lane_id` and `bridge_id` mappings.
 	#[pallet::storage]
 	pub type LaneToBridge<T: Config<I>, I: 'static = ()> =
-		StorageMap<_, Identity, LaneId, BridgeId>;
+		StorageMap<_, Identity, T::LaneId, BridgeId>;
 
 	#[pallet::genesis_config]
 	#[derive(DefaultNoBound)]
@@ -723,7 +730,7 @@ pub mod pallet {
 		/// Keep in mind that we are **NOT** reserving any amount for the bridges opened at
 		/// genesis. We are **NOT** opening lanes, used by this bridge. It all must be done using
 		/// other pallets genesis configuration or some other means.
-		pub opened_bridges: Vec<(Location, InteriorLocation)>,
+		pub opened_bridges: Vec<(Location, InteriorLocation, Option<T::LaneId>)>,
 		/// Dummy marker.
 		#[serde(skip)]
 		pub _phantom: sp_std::marker::PhantomData<(T, I)>,
@@ -735,48 +742,26 @@ pub mod pallet {
 		T: frame_system::Config<AccountId = AccountIdOf<ThisChainOf<T, I>>>,
 	{
 		fn build(&self) {
-			for (bridge_origin_relative_location, bridge_destination_universal_location) in
-				&self.opened_bridges
+			for (
+				bridge_origin_relative_location,
+				bridge_destination_universal_location,
+				maybe_lane_id,
+			) in &self.opened_bridges
 			{
 				let locations = Pallet::<T, I>::bridge_locations(
 					bridge_origin_relative_location.clone(),
 					bridge_destination_universal_location.clone().into(),
 				)
 				.expect("Invalid genesis configuration");
-				let lane_id =
-					locations.calculate_lane_id(xcm::latest::VERSION).expect("Valid locations");
-				let bridge_owner_account = T::BridgeOriginAccountIdConverter::convert_location(
-					locations.bridge_origin_relative_location(),
-				)
-				.expect("Invalid genesis configuration");
 
-				Bridges::<T, I>::insert(
-					locations.bridge_id(),
-					Bridge {
-						bridge_origin_relative_location: Box::new(
-							locations.bridge_origin_relative_location().clone().into(),
-						),
-						bridge_origin_universal_location: Box::new(
-							locations.bridge_origin_universal_location().clone().into(),
-						),
-						bridge_destination_universal_location: Box::new(
-							locations.bridge_destination_universal_location().clone().into(),
-						),
-						state: BridgeState::Opened,
-						bridge_owner_account,
-						deposit: Zero::zero(),
-						lane_id,
-					},
-				);
-				LaneToBridge::<T, I>::insert(lane_id, locations.bridge_id());
+				let lane_id = match maybe_lane_id {
+					Some(lane_id) => *lane_id,
+					None =>
+						locations.calculate_lane_id(xcm::latest::VERSION).expect("Valid locations"),
+				};
 
-				let lanes_manager = LanesManagerOf::<T, I>::new();
-				lanes_manager
-					.create_inbound_lane(lane_id)
-					.expect("Invalid genesis configuration");
-				lanes_manager
-					.create_outbound_lane(lane_id)
-					.expect("Invalid genesis configuration");
+				Pallet::<T, I>::do_open_bridge(locations, lane_id, true)
+					.expect("Valid opened bridge!");
 			}
 		}
 	}
@@ -796,14 +781,14 @@ pub mod pallet {
 			/// Universal location of remote bridge endpoint.
 			remote_endpoint: Box<InteriorLocation>,
 			/// Lane identifier.
-			lane_id: LaneId,
+			lane_id: T::LaneId,
 		},
 		/// Bridge is going to be closed, but not yet fully pruned from the runtime storage.
 		ClosingBridge {
 			/// Bridge identifier.
 			bridge_id: BridgeId,
 			/// Lane identifier.
-			lane_id: LaneId,
+			lane_id: T::LaneId,
 			/// Number of pruned messages during the close call.
 			pruned_messages: MessageNonce,
 			/// Number of enqueued messages that need to be pruned in follow up calls.
@@ -815,7 +800,7 @@ pub mod pallet {
 			/// Bridge identifier.
 			bridge_id: BridgeId,
 			/// Lane identifier.
-			lane_id: LaneId,
+			lane_id: T::LaneId,
 			/// Amount of deposit released.
 			bridge_deposit: BalanceOf<ThisChainOf<T, I>>,
 			/// Number of pruned messages during the close call.
@@ -849,12 +834,11 @@ pub mod pallet {
 #[cfg(test)]
 mod tests {
 	use super::*;
+	use bp_messages::LaneIdType;
 	use mock::*;
 
-	use bp_messages::LaneId;
 	use frame_support::{assert_err, assert_noop, assert_ok, traits::fungible::Mutate, BoundedVec};
 	use frame_system::{EventRecord, Phase};
-	use sp_core::H256;
 	use sp_runtime::TryRuntimeError;
 
 	fn fund_origin_sovereign_account(locations: &BridgeLocations, balance: Balance) -> AccountId {
@@ -911,7 +895,7 @@ mod tests {
 		mock_open_bridge_from_with(origin, deposit, bridged_asset_hub_universal_location())
 	}
 
-	fn enqueue_message(lane: LaneId) {
+	fn enqueue_message(lane: TestLaneIdType) {
 		let lanes_manager = LanesManagerOf::<TestRuntime, ()>::new();
 		lanes_manager
 			.active_outbound_lane(lane)
@@ -1212,7 +1196,7 @@ mod tests {
 							remote_endpoint: Box::new(
 								locations.bridge_destination_universal_location().clone()
 							),
-							lane_id
+							lane_id: lane_id.into()
 						}),
 						topics: vec![],
 					}),
@@ -1355,7 +1339,7 @@ mod tests {
 					phase: Phase::Initialization,
 					event: RuntimeEvent::XcmOverBridge(Event::ClosingBridge {
 						bridge_id: *locations.bridge_id(),
-						lane_id: bridge.lane_id,
+						lane_id: bridge.lane_id.into(),
 						pruned_messages: 16,
 						enqueued_messages: 16,
 					}),
@@ -1403,7 +1387,7 @@ mod tests {
 					phase: Phase::Initialization,
 					event: RuntimeEvent::XcmOverBridge(Event::ClosingBridge {
 						bridge_id: *locations.bridge_id(),
-						lane_id: bridge.lane_id,
+						lane_id: bridge.lane_id.into(),
 						pruned_messages: 8,
 						enqueued_messages: 8,
 					}),
@@ -1444,7 +1428,7 @@ mod tests {
 					phase: Phase::Initialization,
 					event: RuntimeEvent::XcmOverBridge(Event::BridgePruned {
 						bridge_id: *locations.bridge_id(),
-						lane_id: bridge.lane_id,
+						lane_id: bridge.lane_id.into(),
 						bridge_deposit: expected_deposit,
 						pruned_messages: 8,
 					}),
@@ -1456,8 +1440,6 @@ mod tests {
 
 	#[test]
 	fn do_try_state_works() {
-		use sp_runtime::Either;
-
 		let bridge_origin_relative_location = SiblingLocation::get();
 		let bridge_origin_universal_location = SiblingUniversalLocation::get();
 		let bridge_destination_universal_location = BridgedUniversalDestination::get();
@@ -1471,8 +1453,8 @@ mod tests {
 			&bridge_destination_universal_location,
 		);
 		let bridge_id_mismatch = BridgeId::new(&InteriorLocation::Here, &InteriorLocation::Here);
-		let lane_id = LaneId::from_inner(Either::Left(H256::default()));
-		let lane_id_mismatch = LaneId::from_inner(Either::Left(H256::from([1u8; 32])));
+		let lane_id = TestLaneIdType::try_new(1, 2).unwrap();
+		let lane_id_mismatch = TestLaneIdType::try_new(3, 4).unwrap();
 
 		let test_bridge_state = |id,
 		                         bridge,
diff --git a/bridges/modules/xcm-bridge-hub/src/migration.rs b/bridges/modules/xcm-bridge-hub/src/migration.rs
index c9d8b67176a5114668f2a7a2ac9e2aaaa88f0fdc..ffd5233a917b1c8d7c838313a2c5ae095be27593 100644
--- a/bridges/modules/xcm-bridge-hub/src/migration.rs
+++ b/bridges/modules/xcm-bridge-hub/src/migration.rs
@@ -17,7 +17,6 @@
 //! A module that is responsible for migration of storage.
 
 use crate::{Config, Pallet, LOG_TARGET};
-use bp_messages::LaneId;
 use frame_support::{
 	traits::{Get, OnRuntimeUpgrade, StorageVersion},
 	weights::Weight,
@@ -52,7 +51,7 @@ pub struct OpenBridgeForLane<
 impl<
 		T: Config<I>,
 		I: 'static,
-		Lane: Get<LaneId>,
+		Lane: Get<T::LaneId>,
 		CreateLane: Get<bool>,
 		SourceRelativeLocation: Get<Location>,
 		BridgedUniversalLocation: Get<InteriorLocation>,
diff --git a/bridges/modules/xcm-bridge-hub/src/mock.rs b/bridges/modules/xcm-bridge-hub/src/mock.rs
index aff3526b5589cfff395525cdfb5a4b58bcec0a26..6511b9fc5b04e6da2052853c49df716ed3b13ac9 100644
--- a/bridges/modules/xcm-bridge-hub/src/mock.rs
+++ b/bridges/modules/xcm-bridge-hub/src/mock.rs
@@ -20,7 +20,7 @@ use crate as pallet_xcm_bridge_hub;
 
 use bp_messages::{
 	target_chain::{DispatchMessage, MessageDispatch},
-	ChainWithMessages, LaneId, MessageNonce,
+	ChainWithMessages, HashedLaneId, MessageNonce,
 };
 use bp_runtime::{messages::MessageDispatchResult, Chain, ChainId, HashOf};
 use bp_xcm_bridge_hub::{BridgeId, LocalXcmChannelManager};
@@ -50,6 +50,9 @@ pub type AccountId = AccountId32;
 pub type Balance = u64;
 type Block = frame_system::mocking::MockBlock<TestRuntime>;
 
+/// Lane identifier type used for tests.
+pub type TestLaneIdType = HashedLaneId;
+
 pub const SIBLING_ASSET_HUB_ID: u32 = 2001;
 pub const THIS_BRIDGE_HUB_ID: u32 = 2002;
 pub const BRIDGED_ASSET_HUB_ID: u32 = 1001;
@@ -92,6 +95,7 @@ impl pallet_bridge_messages::Config for TestRuntime {
 
 	type OutboundPayload = Vec<u8>;
 	type InboundPayload = Vec<u8>;
+	type LaneId = TestLaneIdType;
 
 	type DeliveryPayments = ();
 	type DeliveryConfirmationPayments = ();
@@ -152,7 +156,7 @@ parameter_types! {
 	pub SiblingLocation: Location = Location::new(1, [Parachain(SIBLING_ASSET_HUB_ID)]);
 	pub SiblingUniversalLocation: InteriorLocation = [GlobalConsensus(RelayNetwork::get()), Parachain(SIBLING_ASSET_HUB_ID)].into();
 
-	pub const BridgedRelayNetwork: NetworkId = NetworkId::Polkadot;
+	pub const BridgedRelayNetwork: NetworkId = NetworkId::ByGenesis([1; 32]);
 	pub BridgedRelayNetworkLocation: Location = (Parent, GlobalConsensus(BridgedRelayNetwork::get())).into();
 	pub BridgedRelativeDestination: InteriorLocation = [Parachain(BRIDGED_ASSET_HUB_ID)].into();
 	pub BridgedUniversalDestination: InteriorLocation = [GlobalConsensus(BridgedRelayNetwork::get()), Parachain(BRIDGED_ASSET_HUB_ID)].into();
@@ -190,7 +194,7 @@ impl pallet_xcm_bridge_hub::Config for TestRuntime {
 	type MessageExportPrice = ();
 	type DestinationVersion = AlwaysLatest;
 
-	type AdminOrigin = frame_system::EnsureNever<()>;
+	type ForceOrigin = frame_system::EnsureNever<()>;
 	type OpenBridgeOrigin = OpenBridgeOrigin;
 	type BridgeOriginAccountIdConverter = LocationToAccountId;
 
@@ -523,7 +527,7 @@ impl bp_header_chain::HeaderChain<BridgedUnderlyingChain> for BridgedHeaderChain
 pub struct TestMessageDispatch;
 
 impl TestMessageDispatch {
-	pub fn deactivate(lane: LaneId) {
+	pub fn deactivate(lane: TestLaneIdType) {
 		frame_support::storage::unhashed::put(&(b"inactive", lane).encode()[..], &false);
 	}
 }
@@ -531,18 +535,21 @@ impl TestMessageDispatch {
 impl MessageDispatch for TestMessageDispatch {
 	type DispatchPayload = Vec<u8>;
 	type DispatchLevelResult = ();
+	type LaneId = TestLaneIdType;
 
-	fn is_active(lane: LaneId) -> bool {
+	fn is_active(lane: Self::LaneId) -> bool {
 		frame_support::storage::unhashed::take::<bool>(&(b"inactive", lane).encode()[..]) !=
 			Some(false)
 	}
 
-	fn dispatch_weight(_message: &mut DispatchMessage<Self::DispatchPayload>) -> Weight {
+	fn dispatch_weight(
+		_message: &mut DispatchMessage<Self::DispatchPayload, Self::LaneId>,
+	) -> Weight {
 		Weight::zero()
 	}
 
 	fn dispatch(
-		_: DispatchMessage<Self::DispatchPayload>,
+		_: DispatchMessage<Self::DispatchPayload, Self::LaneId>,
 	) -> MessageDispatchResult<Self::DispatchLevelResult> {
 		MessageDispatchResult { unspent_weight: Weight::zero(), dispatch_level_result: () }
 	}
diff --git a/bridges/primitives/messages/src/call_info.rs b/bridges/primitives/messages/src/call_info.rs
index c8f06ed8cb7c34e11a37fb7613e3dfad6bc45538..dfd076f029b471349c3fcc02880aa76db8b53d2c 100644
--- a/bridges/primitives/messages/src/call_info.rs
+++ b/bridges/primitives/messages/src/call_info.rs
@@ -16,22 +16,14 @@
 
 //! Defines structures related to calls of the `pallet-bridge-messages` pallet.
 
-use crate::{source_chain, target_chain, LaneId, MessageNonce, UnrewardedRelayersState};
+use crate::{MessageNonce, UnrewardedRelayersState};
 
-use bp_runtime::{AccountIdOf, HashOf};
 use codec::{Decode, Encode};
 use frame_support::weights::Weight;
 use scale_info::TypeInfo;
 use sp_core::RuntimeDebug;
 use sp_std::ops::RangeInclusive;
 
-/// The `BridgeMessagesCall` used to bridge with a given chain.
-pub type BridgeMessagesCallOf<C> = BridgeMessagesCall<
-	AccountIdOf<C>,
-	target_chain::FromBridgedChainMessagesProof<HashOf<C>>,
-	source_chain::FromBridgedChainMessagesDeliveryProof<HashOf<C>>,
->;
-
 /// A minimized version of `pallet-bridge-messages::Call` that can be used without a runtime.
 #[derive(Encode, Decode, Debug, PartialEq, Eq, Clone, TypeInfo)]
 #[allow(non_camel_case_types)]
@@ -60,7 +52,7 @@ pub enum BridgeMessagesCall<AccountId, MessagesProof, MessagesDeliveryProof> {
 
 /// Generic info about a messages delivery/confirmation proof.
 #[derive(PartialEq, RuntimeDebug)]
-pub struct BaseMessagesProofInfo {
+pub struct BaseMessagesProofInfo<LaneId> {
 	/// Message lane, used by the call.
 	pub lane_id: LaneId,
 	/// Nonces of messages, included in the call.
@@ -75,7 +67,7 @@ pub struct BaseMessagesProofInfo {
 	pub best_stored_nonce: MessageNonce,
 }
 
-impl BaseMessagesProofInfo {
+impl<LaneId> BaseMessagesProofInfo<LaneId> {
 	/// Returns true if `bundled_range` continues the `0..=best_stored_nonce` range.
 	pub fn appends_to_stored_nonce(&self) -> bool {
 		Some(*self.bundled_range.start()) == self.best_stored_nonce.checked_add(1)
@@ -94,14 +86,14 @@ pub struct UnrewardedRelayerOccupation {
 
 /// Info about a `ReceiveMessagesProof` call which tries to update a single lane.
 #[derive(PartialEq, RuntimeDebug)]
-pub struct ReceiveMessagesProofInfo {
+pub struct ReceiveMessagesProofInfo<LaneId> {
 	/// Base messages proof info
-	pub base: BaseMessagesProofInfo,
+	pub base: BaseMessagesProofInfo<LaneId>,
 	/// State of unrewarded relayers vector.
 	pub unrewarded_relayers: UnrewardedRelayerOccupation,
 }
 
-impl ReceiveMessagesProofInfo {
+impl<LaneId> ReceiveMessagesProofInfo<LaneId> {
 	/// Returns true if:
 	///
 	/// - either inbound lane is ready to accept bundled messages;
@@ -134,9 +126,9 @@ impl ReceiveMessagesProofInfo {
 
 /// Info about a `ReceiveMessagesDeliveryProof` call which tries to update a single lane.
 #[derive(PartialEq, RuntimeDebug)]
-pub struct ReceiveMessagesDeliveryProofInfo(pub BaseMessagesProofInfo);
+pub struct ReceiveMessagesDeliveryProofInfo<LaneId>(pub BaseMessagesProofInfo<LaneId>);
 
-impl ReceiveMessagesDeliveryProofInfo {
+impl<LaneId> ReceiveMessagesDeliveryProofInfo<LaneId> {
 	/// Returns true if outbound lane is ready to accept confirmations of bundled messages.
 	pub fn is_obsolete(&self) -> bool {
 		self.0.bundled_range.is_empty() || !self.0.appends_to_stored_nonce()
@@ -146,14 +138,14 @@ impl ReceiveMessagesDeliveryProofInfo {
 /// Info about a `ReceiveMessagesProof` or a `ReceiveMessagesDeliveryProof` call
 /// which tries to update a single lane.
 #[derive(PartialEq, RuntimeDebug)]
-pub enum MessagesCallInfo {
+pub enum MessagesCallInfo<LaneId: Clone + Copy> {
 	/// Messages delivery call info.
-	ReceiveMessagesProof(ReceiveMessagesProofInfo),
+	ReceiveMessagesProof(ReceiveMessagesProofInfo<LaneId>),
 	/// Messages delivery confirmation call info.
-	ReceiveMessagesDeliveryProof(ReceiveMessagesDeliveryProofInfo),
+	ReceiveMessagesDeliveryProof(ReceiveMessagesDeliveryProofInfo<LaneId>),
 }
 
-impl MessagesCallInfo {
+impl<LaneId: Clone + Copy> MessagesCallInfo<LaneId> {
 	/// Returns lane, used by the call.
 	pub fn lane_id(&self) -> LaneId {
 		match *self {
diff --git a/bridges/primitives/messages/src/lane.rs b/bridges/primitives/messages/src/lane.rs
index 6d4ca402eb345302d2a3072b0ddb30011f2a0015..c835449db27d5c7ef05052932fcbe958892a9dec 100644
--- a/bridges/primitives/messages/src/lane.rs
+++ b/bridges/primitives/messages/src/lane.rs
@@ -16,12 +16,88 @@
 
 //! Primitives of messages module, that represents lane id.
 
-use codec::{Decode, Encode, Error as CodecError, Input, MaxEncodedLen};
-use frame_support::sp_runtime::Either;
+use codec::{Codec, Decode, Encode, EncodeLike, MaxEncodedLen};
 use scale_info::TypeInfo;
-use serde::{Deserialize, Serialize};
+use serde::{de::DeserializeOwned, Deserialize, Serialize};
 use sp_core::{RuntimeDebug, TypeId, H256};
 use sp_io::hashing::blake2_256;
+use sp_std::fmt::Debug;
+
+/// Trait representing a generic `LaneId` type.
+pub trait LaneIdType:
+	Clone
+	+ Copy
+	+ Codec
+	+ EncodeLike
+	+ Debug
+	+ Default
+	+ PartialEq
+	+ Eq
+	+ Ord
+	+ TypeInfo
+	+ MaxEncodedLen
+	+ Serialize
+	+ DeserializeOwned
+{
+	/// Creates a new `LaneId` type (if supported).
+	fn try_new<E: Ord + Encode>(endpoint1: E, endpoint2: E) -> Result<Self, ()>;
+}
+
+/// Bridge lane identifier (legacy).
+///
+/// Note: For backwards compatibility reasons, we also handle the older format `[u8; 4]`.
+#[derive(
+	Clone,
+	Copy,
+	Decode,
+	Default,
+	Encode,
+	Eq,
+	Ord,
+	PartialOrd,
+	PartialEq,
+	TypeInfo,
+	MaxEncodedLen,
+	Serialize,
+	Deserialize,
+)]
+pub struct LegacyLaneId(pub [u8; 4]);
+
+impl LaneIdType for LegacyLaneId {
+	/// Create lane identifier from two locations.
+	fn try_new<T: Ord + Encode>(_endpoint1: T, _endpoint2: T) -> Result<Self, ()> {
+		// we don't support this for `LegacyLaneId`, because it was hard-coded before
+		Err(())
+	}
+}
+
+#[cfg(any(feature = "std", feature = "runtime-benchmarks", test))]
+impl TryFrom<Vec<u8>> for LegacyLaneId {
+	type Error = ();
+
+	fn try_from(value: Vec<u8>) -> Result<Self, Self::Error> {
+		if value.len() == 4 {
+			return <[u8; 4]>::try_from(value).map(Self).map_err(|_| ());
+		}
+		Err(())
+	}
+}
+
+impl core::fmt::Debug for LegacyLaneId {
+	fn fmt(&self, fmt: &mut core::fmt::Formatter) -> core::fmt::Result {
+		self.0.fmt(fmt)
+	}
+}
+
+impl AsRef<[u8]> for LegacyLaneId {
+	fn as_ref(&self) -> &[u8] {
+		&self.0
+	}
+}
+
+impl TypeId for LegacyLaneId {
+	const TYPE_ID: [u8; 4] = *b"blan";
+}
 
 /// Bridge lane identifier.
 ///
@@ -41,12 +117,11 @@ use sp_io::hashing::blake2_256;
 ///     (endpoint2, VALUES_SEPARATOR, endpoint1)
 /// }.using_encoded(blake2_256);
 /// ```
-///
-/// Note: For backwards compatibility reasons, we also handle the older format `[u8; 4]`.
 #[derive(
 	Clone,
 	Copy,
 	Decode,
+	Default,
 	Encode,
 	Eq,
 	Ord,
@@ -57,115 +132,67 @@ use sp_io::hashing::blake2_256;
 	Serialize,
 	Deserialize,
 )]
-pub struct LaneId(InnerLaneId);
-
-impl LaneId {
-	/// Create lane identifier from two locations.
-	pub fn new<T: Ord + Encode>(endpoint1: T, endpoint2: T) -> Self {
-		const VALUES_SEPARATOR: [u8; 31] = *b"bridges-lane-id-value-separator";
-
-		LaneId(InnerLaneId::Hash(
-			if endpoint1 < endpoint2 {
-				(endpoint1, VALUES_SEPARATOR, endpoint2)
-			} else {
-				(endpoint2, VALUES_SEPARATOR, endpoint1)
-			}
-			.using_encoded(blake2_256)
-			.into(),
-		))
-	}
+pub struct HashedLaneId(H256);
 
+impl HashedLaneId {
 	/// Create lane identifier from given hash.
 	///
 	/// There's no `From<H256>` implementation for the `LaneId`, because using this conversion
 	/// in a wrong way (i.e. computing hash of endpoints manually) may lead to issues. So we
 	/// want the call to be explicit.
-	pub const fn from_inner(inner: Either<H256, [u8; 4]>) -> Self {
-		LaneId(match inner {
-			Either::Left(hash) => InnerLaneId::Hash(hash),
-			Either::Right(array) => InnerLaneId::Array(array),
-		})
+	#[cfg(any(feature = "std", feature = "runtime-benchmarks", test))]
+	pub const fn from_inner(inner: H256) -> Self {
+		Self(inner)
+	}
+
+	/// Access the inner lane representation.
+	pub fn inner(&self) -> &H256 {
+		&self.0
 	}
 }
 
-impl core::fmt::Display for LaneId {
+impl core::fmt::Display for HashedLaneId {
 	fn fmt(&self, f: &mut core::fmt::Formatter) -> core::fmt::Result {
 		core::fmt::Display::fmt(&self.0, f)
 	}
 }
 
-impl core::fmt::Debug for LaneId {
+impl core::fmt::Debug for HashedLaneId {
 	fn fmt(&self, f: &mut core::fmt::Formatter) -> core::fmt::Result {
 		core::fmt::Debug::fmt(&self.0, f)
 	}
 }
 
-impl AsRef<[u8]> for LaneId {
-	fn as_ref(&self) -> &[u8] {
-		self.0.as_ref()
-	}
+impl TypeId for HashedLaneId {
+	const TYPE_ID: [u8; 4] = *b"hlan";
 }
 
-impl TypeId for LaneId {
-	const TYPE_ID: [u8; 4] = *b"blan";
-}
-
-#[derive(
-	Clone, Copy, Eq, Ord, PartialOrd, PartialEq, TypeInfo, MaxEncodedLen, Serialize, Deserialize,
-)]
-enum InnerLaneId {
-	/// Old format (for backwards compatibility).
-	Array([u8; 4]),
-	/// New format 32-byte hash generated by `blake2_256`.
-	Hash(H256),
-}
-
-impl Encode for InnerLaneId {
-	fn encode(&self) -> sp_std::vec::Vec<u8> {
-		match self {
-			InnerLaneId::Array(array) => array.encode(),
-			InnerLaneId::Hash(hash) => hash.encode(),
-		}
-	}
-}
-
-impl Decode for InnerLaneId {
-	fn decode<I: Input>(input: &mut I) -> Result<Self, CodecError> {
-		// check backwards compatibly first
-		if input.remaining_len() == Ok(Some(4)) {
-			let array: [u8; 4] = Decode::decode(input)?;
-			return Ok(InnerLaneId::Array(array))
-		}
-
-		// else check new format
-		H256::decode(input).map(InnerLaneId::Hash)
-	}
-}
+impl LaneIdType for HashedLaneId {
+	/// Create lane identifier from two locations.
+	fn try_new<T: Ord + Encode>(endpoint1: T, endpoint2: T) -> Result<Self, ()> {
+		const VALUES_SEPARATOR: [u8; 31] = *b"bridges-lane-id-value-separator";
 
-impl core::fmt::Display for InnerLaneId {
-	fn fmt(&self, f: &mut core::fmt::Formatter) -> core::fmt::Result {
-		match self {
-			InnerLaneId::Array(array) => write!(f, "Array({:?})", array),
-			InnerLaneId::Hash(hash) => write!(f, "Hash({:?})", hash),
-		}
+		Ok(Self(
+			if endpoint1 < endpoint2 {
+				(endpoint1, VALUES_SEPARATOR, endpoint2)
+			} else {
+				(endpoint2, VALUES_SEPARATOR, endpoint1)
+			}
+			.using_encoded(blake2_256)
+			.into(),
+		))
 	}
 }
 
-impl core::fmt::Debug for InnerLaneId {
-	fn fmt(&self, fmt: &mut core::fmt::Formatter) -> core::fmt::Result {
-		match self {
-			InnerLaneId::Array(array) => array.fmt(fmt),
-			InnerLaneId::Hash(hash) => hash.fmt(fmt),
-		}
-	}
-}
+#[cfg(any(feature = "std", feature = "runtime-benchmarks", test))]
+impl TryFrom<Vec<u8>> for HashedLaneId {
+	type Error = ();
 
-impl AsRef<[u8]> for InnerLaneId {
-	fn as_ref(&self) -> &[u8] {
-		match self {
-			InnerLaneId::Array(array) => array.as_ref(),
-			InnerLaneId::Hash(hash) => hash.as_ref(),
+	fn try_from(value: Vec<u8>) -> Result<Self, Self::Error> {
+		if value.len() == 32 {
+			return <[u8; 32]>::try_from(value).map(|v| Self(H256::from(v))).map_err(|_| ());
 		}
+		Err(())
 	}
 }
 
@@ -194,63 +221,89 @@ impl LaneState {
 #[cfg(test)]
 mod tests {
 	use super::*;
+	use crate::MessageNonce;
 
 	#[test]
 	fn lane_id_debug_format_matches_inner_hash_format() {
 		assert_eq!(
-			format!("{:?}", LaneId(InnerLaneId::Hash(H256::from([1u8; 32])))),
+			format!("{:?}", HashedLaneId(H256::from([1u8; 32]))),
 			format!("{:?}", H256::from([1u8; 32])),
 		);
-		assert_eq!(
-			format!("{:?}", LaneId(InnerLaneId::Array([0, 0, 0, 1]))),
-			format!("{:?}", [0, 0, 0, 1]),
-		);
+		assert_eq!(format!("{:?}", LegacyLaneId([0, 0, 0, 1])), format!("{:?}", [0, 0, 0, 1]),);
 	}
 
 	#[test]
-	fn lane_id_as_ref_works() {
+	fn hashed_encode_decode_works() {
+		// simple encode/decode - new format
+		let lane_id = HashedLaneId(H256::from([1u8; 32]));
+		let encoded_lane_id = lane_id.encode();
+		let decoded_lane_id = HashedLaneId::decode(&mut &encoded_lane_id[..]).expect("decodable");
+		assert_eq!(lane_id, decoded_lane_id);
 		assert_eq!(
 			"0101010101010101010101010101010101010101010101010101010101010101",
-			hex::encode(LaneId(InnerLaneId::Hash(H256::from([1u8; 32]))).as_ref()),
+			hex::encode(encoded_lane_id)
 		);
-		assert_eq!("00000001", hex::encode(LaneId(InnerLaneId::Array([0, 0, 0, 1])).as_ref()),);
 	}
 
 	#[test]
-	fn lane_id_encode_decode_works() {
-		let test_encode_decode = |expected_hex, lane_id: LaneId| {
-			let enc = lane_id.encode();
-			let decoded_lane_id = LaneId::decode(&mut &enc[..]).expect("decodable");
-			assert_eq!(lane_id, decoded_lane_id);
-
-			assert_eq!(expected_hex, hex::encode(lane_id.as_ref()),);
-			assert_eq!(expected_hex, hex::encode(decoded_lane_id.as_ref()),);
-
-			let hex_bytes = hex::decode(expected_hex).expect("valid hex");
-			let hex_decoded_lane_id = LaneId::decode(&mut &hex_bytes[..]).expect("decodable");
-			assert_eq!(hex_decoded_lane_id, lane_id);
-			assert_eq!(hex_decoded_lane_id, decoded_lane_id);
-		};
-
-		test_encode_decode(
-			"0101010101010101010101010101010101010101010101010101010101010101",
-			LaneId(InnerLaneId::Hash(H256::from([1u8; 32]))),
-		);
-		test_encode_decode("00000001", LaneId(InnerLaneId::Array([0, 0, 0, 1])));
+	fn legacy_encode_decode_works() {
+		// simple encode/decode - old format
+		let lane_id = LegacyLaneId([0, 0, 0, 1]);
+		let encoded_lane_id = lane_id.encode();
+		let decoded_lane_id = LegacyLaneId::decode(&mut &encoded_lane_id[..]).expect("decodable");
+		assert_eq!(lane_id, decoded_lane_id);
+		assert_eq!("00000001", hex::encode(encoded_lane_id));
+
+		// decode sample
+		let bytes = vec![0, 0, 0, 2, 1, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0];
+		let (lane, nonce_start, nonce_end): (LegacyLaneId, MessageNonce, MessageNonce) =
+			Decode::decode(&mut &bytes[..]).unwrap();
+		assert_eq!(lane, LegacyLaneId([0, 0, 0, 2]));
+		assert_eq!(nonce_start, 1);
+		assert_eq!(nonce_end, 1);
+
+		// run encode/decode for `LaneId` with different positions
+		let expected_lane = LegacyLaneId([0, 0, 0, 1]);
+		let expected_nonce_start = 1088_u64;
+		let expected_nonce_end = 9185_u64;
+
+		// decode: LaneId,Nonce,Nonce
+		let bytes = (expected_lane, expected_nonce_start, expected_nonce_end).encode();
+		let (lane, nonce_start, nonce_end): (LegacyLaneId, MessageNonce, MessageNonce) =
+			Decode::decode(&mut &bytes[..]).unwrap();
+		assert_eq!(lane, expected_lane);
+		assert_eq!(nonce_start, expected_nonce_start);
+		assert_eq!(nonce_end, expected_nonce_end);
+
+		// decode: Nonce,LaneId,Nonce
+		let bytes = (expected_nonce_start, expected_lane, expected_nonce_end).encode();
+		let (nonce_start, lane, nonce_end): (MessageNonce, LegacyLaneId, MessageNonce) =
+			Decode::decode(&mut &bytes[..]).unwrap();
+		assert_eq!(lane, expected_lane);
+		assert_eq!(nonce_start, expected_nonce_start);
+		assert_eq!(nonce_end, expected_nonce_end);
+
+		// decode: Nonce,Nonce,LaneId
+		let bytes = (expected_nonce_start, expected_nonce_end, expected_lane).encode();
+		let (nonce_start, nonce_end, lane): (MessageNonce, MessageNonce, LegacyLaneId) =
+			Decode::decode(&mut &bytes[..]).unwrap();
+		assert_eq!(lane, expected_lane);
+		assert_eq!(nonce_start, expected_nonce_start);
+		assert_eq!(nonce_end, expected_nonce_end);
 	}
 
 	#[test]
-	fn lane_id_is_generated_using_ordered_endpoints() {
-		assert_eq!(LaneId::new(1, 2), LaneId::new(2, 1));
+	fn hashed_lane_id_is_generated_using_ordered_endpoints() {
+		assert_eq!(HashedLaneId::try_new(1, 2).unwrap(), HashedLaneId::try_new(2, 1).unwrap());
 	}
 
 	#[test]
-	fn lane_id_is_different_for_different_endpoints() {
-		assert_ne!(LaneId::new(1, 2), LaneId::new(1, 3));
+	fn hashed_lane_id_is_different_for_different_endpoints() {
+		assert_ne!(HashedLaneId::try_new(1, 2).unwrap(), HashedLaneId::try_new(1, 3).unwrap());
 	}
 
 	#[test]
-	fn lane_id_is_different_even_if_arguments_has_partial_matching_encoding() {
+	fn hashed_lane_id_is_different_even_if_arguments_has_partial_matching_encoding() {
 		/// Some artificial type that generates the same encoding for different values
 		/// concatenations. I.e. the encoding for `(Either::Two(1, 2), Either::Two(3, 4))`
 		/// is the same as encoding of `(Either::Three(1, 2, 3), Either::One(4))`.
@@ -274,8 +327,8 @@ mod tests {
 		}
 
 		assert_ne!(
-			LaneId::new(Either::Two(1, 2), Either::Two(3, 4)),
-			LaneId::new(Either::Three(1, 2, 3), Either::One(4)),
+			HashedLaneId::try_new(Either::Two(1, 2), Either::Two(3, 4)).unwrap(),
+			HashedLaneId::try_new(Either::Three(1, 2, 3), Either::One(4)).unwrap(),
 		);
 	}
 }
diff --git a/bridges/primitives/messages/src/lib.rs b/bridges/primitives/messages/src/lib.rs
index 7eb0c562939531a9762e8864babf5f7843d070ab..2776b806cc16bf30fb09c06d904c05f6bcea21d0 100644
--- a/bridges/primitives/messages/src/lib.rs
+++ b/bridges/primitives/messages/src/lib.rs
@@ -35,10 +35,10 @@ use sp_core::RuntimeDebug;
 use sp_std::{collections::vec_deque::VecDeque, ops::RangeInclusive, prelude::*};
 
 pub use call_info::{
-	BaseMessagesProofInfo, BridgeMessagesCall, BridgeMessagesCallOf, MessagesCallInfo,
-	ReceiveMessagesDeliveryProofInfo, ReceiveMessagesProofInfo, UnrewardedRelayerOccupation,
+	BaseMessagesProofInfo, BridgeMessagesCall, MessagesCallInfo, ReceiveMessagesDeliveryProofInfo,
+	ReceiveMessagesProofInfo, UnrewardedRelayerOccupation,
 };
-pub use lane::{LaneId, LaneState};
+pub use lane::{HashedLaneId, LaneIdType, LaneState, LegacyLaneId};
 
 mod call_info;
 mod lane;
@@ -181,7 +181,7 @@ pub type MessagePayload = Vec<u8>;
 
 /// Message key (unique message identifier) as it is stored in the storage.
 #[derive(Encode, Decode, Clone, PartialEq, Eq, RuntimeDebug, TypeInfo, MaxEncodedLen)]
-pub struct MessageKey {
+pub struct MessageKey<LaneId: Encode> {
 	/// ID of the message lane.
 	pub lane_id: LaneId,
 	/// Message nonce.
@@ -190,9 +190,9 @@ pub struct MessageKey {
 
 /// Message as it is stored in the storage.
 #[derive(Encode, Decode, Clone, PartialEq, Eq, RuntimeDebug, TypeInfo)]
-pub struct Message {
+pub struct Message<LaneId: Encode> {
 	/// Message key.
-	pub key: MessageKey,
+	pub key: MessageKey<LaneId>,
 	/// Message payload.
 	pub payload: MessagePayload,
 }
@@ -200,11 +200,6 @@ pub struct Message {
 /// Inbound lane data.
 #[derive(Encode, Decode, Clone, RuntimeDebug, PartialEq, Eq, TypeInfo)]
 pub struct InboundLaneData<RelayerId> {
-	/// Inbound lane state.
-	///
-	/// If state is `Closed`, then all attempts to deliver messages to this end will fail.
-	pub state: LaneState,
-
 	/// Identifiers of relayers and messages that they have delivered to this lane (ordered by
 	/// message nonce).
 	///
@@ -233,6 +228,11 @@ pub struct InboundLaneData<RelayerId> {
 	/// This value is updated indirectly when an `OutboundLane` state of the source
 	/// chain is received alongside with new messages delivery.
 	pub last_confirmed_nonce: MessageNonce,
+
+	/// Inbound lane state.
+	///
+	/// If state is `Closed`, then all attempts to deliver messages to this end will fail.
+	pub state: LaneState,
 }
 
 impl<RelayerId> Default for InboundLaneData<RelayerId> {
@@ -337,20 +337,20 @@ pub struct UnrewardedRelayer<RelayerId> {
 
 /// Received messages with their dispatch result.
 #[derive(Clone, Encode, Decode, RuntimeDebug, PartialEq, Eq, TypeInfo)]
-pub struct ReceivedMessages<DispatchLevelResult> {
+pub struct ReceivedMessages<DispatchLevelResult, LaneId> {
 	/// Id of the lane which is receiving messages.
 	pub lane: LaneId,
 	/// Result of messages which we tried to dispatch
 	pub receive_results: Vec<(MessageNonce, ReceptionResult<DispatchLevelResult>)>,
 }
 
-impl<DispatchLevelResult> ReceivedMessages<DispatchLevelResult> {
+impl<DispatchLevelResult, LaneId> ReceivedMessages<DispatchLevelResult, LaneId> {
 	/// Creates new `ReceivedMessages` structure from given results.
 	pub fn new(
 		lane: LaneId,
 		receive_results: Vec<(MessageNonce, ReceptionResult<DispatchLevelResult>)>,
 	) -> Self {
-		ReceivedMessages { lane, receive_results }
+		ReceivedMessages { lane: lane.into(), receive_results }
 	}
 
 	/// Push `result` of the `message` delivery onto `receive_results` vector.
@@ -449,10 +449,6 @@ impl<RelayerId> From<&InboundLaneData<RelayerId>> for UnrewardedRelayersState {
 /// Outbound lane data.
 #[derive(Encode, Decode, Clone, RuntimeDebug, PartialEq, Eq, TypeInfo, MaxEncodedLen)]
 pub struct OutboundLaneData {
-	/// Lane state.
-	///
-	/// If state is `Closed`, then all attempts to send messages messages at this end will fail.
-	pub state: LaneState,
 	/// Nonce of the oldest message that we haven't yet pruned. May point to not-yet-generated
 	/// message if all sent messages are already pruned.
 	pub oldest_unpruned_nonce: MessageNonce,
@@ -460,6 +456,10 @@ pub struct OutboundLaneData {
 	pub latest_received_nonce: MessageNonce,
 	/// Nonce of the latest message, generated by us.
 	pub latest_generated_nonce: MessageNonce,
+	/// Lane state.
+	///
+	/// If state is `Closed`, then all attempts to send messages at this end will fail.
+	pub state: LaneState,
 }
 
 impl OutboundLaneData {
diff --git a/bridges/primitives/messages/src/source_chain.rs b/bridges/primitives/messages/src/source_chain.rs
index 64f015bdb822eb25ff42a3fdb52bf6405a5424bf..1d4a513035c744bf4044156858bcdf9d756e94fa 100644
--- a/bridges/primitives/messages/src/source_chain.rs
+++ b/bridges/primitives/messages/src/source_chain.rs
@@ -16,7 +16,7 @@
 
 //! Primitives of messages module, that are used on the source chain.
 
-use crate::{LaneId, MessageNonce, UnrewardedRelayer};
+use crate::{MessageNonce, UnrewardedRelayer};
 
 use bp_runtime::{raw_storage_proof_size, RawStorageProof, Size};
 use codec::{Decode, Encode};
@@ -39,7 +39,7 @@ use sp_std::{
 ///
 /// - lane id.
 #[derive(Clone, Decode, Encode, Eq, PartialEq, RuntimeDebug, TypeInfo)]
-pub struct FromBridgedChainMessagesDeliveryProof<BridgedHeaderHash> {
+pub struct FromBridgedChainMessagesDeliveryProof<BridgedHeaderHash, LaneId> {
 	/// Hash of the bridge header the proof is for.
 	pub bridged_header_hash: BridgedHeaderHash,
 	/// Storage trie proof generated for [`Self::bridged_header_hash`].
@@ -48,7 +48,9 @@ pub struct FromBridgedChainMessagesDeliveryProof<BridgedHeaderHash> {
 	pub lane: LaneId,
 }
 
-impl<BridgedHeaderHash> Size for FromBridgedChainMessagesDeliveryProof<BridgedHeaderHash> {
+impl<BridgedHeaderHash, LaneId> Size
+	for FromBridgedChainMessagesDeliveryProof<BridgedHeaderHash, LaneId>
+{
 	fn size(&self) -> u32 {
 		use frame_support::sp_runtime::SaturatedConversion;
 		raw_storage_proof_size(&self.storage_proof).saturated_into()
@@ -60,7 +62,7 @@ pub type RelayersRewards<AccountId> = BTreeMap<AccountId, MessageNonce>;
 
 /// Manages payments that are happening at the source chain during delivery confirmation
 /// transaction.
-pub trait DeliveryConfirmationPayments<AccountId> {
+pub trait DeliveryConfirmationPayments<AccountId, LaneId> {
 	/// Error type.
 	type Error: Debug + Into<&'static str>;
 
@@ -78,7 +80,7 @@ pub trait DeliveryConfirmationPayments<AccountId> {
 	) -> MessageNonce;
 }
 
-impl<AccountId> DeliveryConfirmationPayments<AccountId> for () {
+impl<AccountId, LaneId> DeliveryConfirmationPayments<AccountId, LaneId> for () {
 	type Error = &'static str;
 
 	fn pay_reward(
@@ -94,14 +96,14 @@ impl<AccountId> DeliveryConfirmationPayments<AccountId> for () {
 
 /// Callback that is called at the source chain (bridge hub) when we get delivery confirmation
 /// for new messages.
-pub trait OnMessagesDelivered {
+pub trait OnMessagesDelivered<LaneId> {
 	/// New messages delivery has been confirmed.
 	///
 	/// The only argument of the function is the number of yet undelivered messages
 	fn on_messages_delivered(lane: LaneId, enqueued_messages: MessageNonce);
 }
 
-impl OnMessagesDelivered for () {
+impl<LaneId> OnMessagesDelivered<LaneId> for () {
 	fn on_messages_delivered(_lane: LaneId, _enqueued_messages: MessageNonce) {}
 }
 
@@ -115,7 +117,7 @@ pub struct SendMessageArtifacts {
 }
 
 /// Messages bridge API to be used from other pallets.
-pub trait MessagesBridge<Payload> {
+pub trait MessagesBridge<Payload, LaneId> {
 	/// Error type.
 	type Error: Debug;
 
@@ -141,7 +143,7 @@ pub trait MessagesBridge<Payload> {
 /// where outbound messages are forbidden.
 pub struct ForbidOutboundMessages;
 
-impl<AccountId> DeliveryConfirmationPayments<AccountId> for ForbidOutboundMessages {
+impl<AccountId, LaneId> DeliveryConfirmationPayments<AccountId, LaneId> for ForbidOutboundMessages {
 	type Error = &'static str;
 
 	fn pay_reward(
diff --git a/bridges/primitives/messages/src/storage_keys.rs b/bridges/primitives/messages/src/storage_keys.rs
index ff62dab078e748db0827fc0c1e16c6508dfeb813..fb3371cb830c4b3b94a2f60c1f0939329fa815c4 100644
--- a/bridges/primitives/messages/src/storage_keys.rs
+++ b/bridges/primitives/messages/src/storage_keys.rs
@@ -25,7 +25,7 @@ pub const OUTBOUND_LANES_MAP_NAME: &str = "OutboundLanes";
 /// Name of the `InboundLanes` storage map.
 pub const INBOUND_LANES_MAP_NAME: &str = "InboundLanes";
 
-use crate::{LaneId, MessageKey, MessageNonce};
+use crate::{MessageKey, MessageNonce};
 
 use codec::Encode;
 use frame_support::Blake2_128Concat;
@@ -43,16 +43,20 @@ pub fn operating_mode_key(pallet_prefix: &str) -> StorageKey {
 }
 
 /// Storage key of the outbound message in the runtime storage.
-pub fn message_key(pallet_prefix: &str, lane: &LaneId, nonce: MessageNonce) -> StorageKey {
+pub fn message_key<LaneId: Encode>(
+	pallet_prefix: &str,
+	lane: LaneId,
+	nonce: MessageNonce,
+) -> StorageKey {
 	bp_runtime::storage_map_final_key::<Blake2_128Concat>(
 		pallet_prefix,
 		OUTBOUND_MESSAGES_MAP_NAME,
-		&MessageKey { lane_id: *lane, nonce }.encode(),
+		&MessageKey { lane_id: lane, nonce }.encode(),
 	)
 }
 
 /// Storage key of the outbound message lane state in the runtime storage.
-pub fn outbound_lane_data_key(pallet_prefix: &str, lane: &LaneId) -> StorageKey {
+pub fn outbound_lane_data_key<LaneId: Encode>(pallet_prefix: &str, lane: &LaneId) -> StorageKey {
 	bp_runtime::storage_map_final_key::<Blake2_128Concat>(
 		pallet_prefix,
 		OUTBOUND_LANES_MAP_NAME,
@@ -61,7 +65,7 @@ pub fn outbound_lane_data_key(pallet_prefix: &str, lane: &LaneId) -> StorageKey
 }
 
 /// Storage key of the inbound message lane state in the runtime storage.
-pub fn inbound_lane_data_key(pallet_prefix: &str, lane: &LaneId) -> StorageKey {
+pub fn inbound_lane_data_key<LaneId: Encode>(pallet_prefix: &str, lane: &LaneId) -> StorageKey {
 	bp_runtime::storage_map_final_key::<Blake2_128Concat>(
 		pallet_prefix,
 		INBOUND_LANES_MAP_NAME,
@@ -72,7 +76,10 @@ pub fn inbound_lane_data_key(pallet_prefix: &str, lane: &LaneId) -> StorageKey {
 #[cfg(test)]
 mod tests {
 	use super::*;
-	use frame_support::sp_runtime::Either;
+	use crate::{
+		lane::{HashedLaneId, LegacyLaneId},
+		LaneIdType,
+	};
 	use hex_literal::hex;
 
 	#[test]
@@ -92,7 +99,8 @@ mod tests {
 	fn storage_message_key_computed_properly() {
 		// If this test fails, then something has been changed in module storage that is breaking
 		// all previously crafted messages proofs.
-		let storage_key = message_key("BridgeMessages", &LaneId::new(1, 2), 42).0;
+		let storage_key =
+			message_key("BridgeMessages", &HashedLaneId::try_new(1, 2).unwrap(), 42).0;
 		assert_eq!(
 			storage_key,
 			hex!("dd16c784ebd3390a9bc0357c7511ed018a395e6242c6813b196ca31ed0547ea70e9bdb8f50c68d12f06eabb57759ee5eb1d3dccd8b3c3a012afe265f3e3c4432129b8aee50c9dcf87f9793be208e5ea02a00000000000000").to_vec(),
@@ -101,8 +109,7 @@ mod tests {
 		);
 
 		// check backwards compatibility
-		let storage_key =
-			message_key("BridgeMessages", &LaneId::from_inner(Either::Right(*b"test")), 42).0;
+		let storage_key = message_key("BridgeMessages", &LegacyLaneId(*b"test"), 42).0;
 		assert_eq!(
 			storage_key,
 			hex!("dd16c784ebd3390a9bc0357c7511ed018a395e6242c6813b196ca31ed0547ea79446af0e09063bd4a7874aef8a997cec746573742a00000000000000").to_vec(),
@@ -115,7 +122,8 @@ mod tests {
 	fn outbound_lane_data_key_computed_properly() {
 		// If this test fails, then something has been changed in module storage that is breaking
 		// all previously crafted outbound lane state proofs.
-		let storage_key = outbound_lane_data_key("BridgeMessages", &LaneId::new(1, 2)).0;
+		let storage_key =
+			outbound_lane_data_key("BridgeMessages", &HashedLaneId::try_new(1, 2).unwrap()).0;
 		assert_eq!(
 			storage_key,
 			hex!("dd16c784ebd3390a9bc0357c7511ed0196c246acb9b55077390e3ca723a0ca1fd3bef8b00df8ca7b01813b5e2741950db1d3dccd8b3c3a012afe265f3e3c4432129b8aee50c9dcf87f9793be208e5ea0").to_vec(),
@@ -124,9 +132,7 @@ mod tests {
 		);
 
 		// check backwards compatibility
-		let storage_key =
-			outbound_lane_data_key("BridgeMessages", &LaneId::from_inner(Either::Right(*b"test")))
-				.0;
+		let storage_key = outbound_lane_data_key("BridgeMessages", &LegacyLaneId(*b"test")).0;
 		assert_eq!(
 			storage_key,
 			hex!("dd16c784ebd3390a9bc0357c7511ed0196c246acb9b55077390e3ca723a0ca1f44a8995dd50b6657a037a7839304535b74657374").to_vec(),
@@ -139,7 +145,8 @@ mod tests {
 	fn inbound_lane_data_key_computed_properly() {
 		// If this test fails, then something has been changed in module storage that is breaking
 		// all previously crafted inbound lane state proofs.
-		let storage_key = inbound_lane_data_key("BridgeMessages", &LaneId::new(1, 2)).0;
+		let storage_key =
+			inbound_lane_data_key("BridgeMessages", &HashedLaneId::try_new(1, 2).unwrap()).0;
 		assert_eq!(
 			storage_key,
 			hex!("dd16c784ebd3390a9bc0357c7511ed01e5f83cf83f2127eb47afdc35d6e43fabd3bef8b00df8ca7b01813b5e2741950db1d3dccd8b3c3a012afe265f3e3c4432129b8aee50c9dcf87f9793be208e5ea0").to_vec(),
@@ -148,8 +155,7 @@ mod tests {
 		);
 
 		// check backwards compatibility
-		let storage_key =
-			inbound_lane_data_key("BridgeMessages", &LaneId::from_inner(Either::Right(*b"test"))).0;
+		let storage_key = inbound_lane_data_key("BridgeMessages", &LegacyLaneId(*b"test")).0;
 		assert_eq!(
 			storage_key,
 			hex!("dd16c784ebd3390a9bc0357c7511ed01e5f83cf83f2127eb47afdc35d6e43fab44a8995dd50b6657a037a7839304535b74657374").to_vec(),
diff --git a/bridges/primitives/messages/src/target_chain.rs b/bridges/primitives/messages/src/target_chain.rs
index 67868ff7c7cd3745dc3e44b1b6b7d8f45f5605f8..cf07a400933a965f5097d4654f6e405a1d11b33b 100644
--- a/bridges/primitives/messages/src/target_chain.rs
+++ b/bridges/primitives/messages/src/target_chain.rs
@@ -16,7 +16,7 @@
 
 //! Primitives of messages module, that are used on the target chain.
 
-use crate::{LaneId, Message, MessageKey, MessageNonce, MessagePayload, OutboundLaneData};
+use crate::{Message, MessageKey, MessageNonce, MessagePayload, OutboundLaneData};
 
 use bp_runtime::{messages::MessageDispatchResult, raw_storage_proof_size, RawStorageProof, Size};
 use codec::{Decode, Encode, Error as CodecError};
@@ -38,20 +38,20 @@ use sp_std::{fmt::Debug, marker::PhantomData, prelude::*};
 ///
 /// - nonces (inclusive range) of messages which are included in this proof.
 #[derive(Clone, Decode, Encode, Eq, PartialEq, RuntimeDebug, TypeInfo)]
-pub struct FromBridgedChainMessagesProof<BridgedHeaderHash> {
+pub struct FromBridgedChainMessagesProof<BridgedHeaderHash, Lane> {
 	/// Hash of the finalized bridged header the proof is for.
 	pub bridged_header_hash: BridgedHeaderHash,
 	/// A storage trie proof of messages being delivered.
 	pub storage_proof: RawStorageProof,
 	/// Messages in this proof are sent over this lane.
-	pub lane: LaneId,
+	pub lane: Lane,
 	/// Nonce of the first message being delivered.
 	pub nonces_start: MessageNonce,
 	/// Nonce of the last message being delivered.
 	pub nonces_end: MessageNonce,
 }
 
-impl<BridgedHeaderHash> Size for FromBridgedChainMessagesProof<BridgedHeaderHash> {
+impl<BridgedHeaderHash, Lane> Size for FromBridgedChainMessagesProof<BridgedHeaderHash, Lane> {
 	fn size(&self) -> u32 {
 		use frame_support::sp_runtime::SaturatedConversion;
 		raw_storage_proof_size(&self.storage_proof).saturated_into()
@@ -59,7 +59,7 @@ impl<BridgedHeaderHash> Size for FromBridgedChainMessagesProof<BridgedHeaderHash
 }
 
 /// Proved messages from the source chain.
-pub type ProvedMessages<Message> = (LaneId, ProvedLaneMessages<Message>);
+pub type ProvedMessages<LaneId, Message> = (LaneId, ProvedLaneMessages<Message>);
 
 /// Proved messages from single lane of the source chain.
 #[derive(RuntimeDebug, Encode, Decode, Clone, PartialEq, Eq, TypeInfo)]
@@ -79,9 +79,9 @@ pub struct DispatchMessageData<DispatchPayload> {
 
 /// Message with decoded dispatch payload.
 #[derive(RuntimeDebug)]
-pub struct DispatchMessage<DispatchPayload> {
+pub struct DispatchMessage<DispatchPayload, LaneId: Encode> {
 	/// Message key.
-	pub key: MessageKey,
+	pub key: MessageKey<LaneId>,
 	/// Message data with decoded dispatch payload.
 	pub data: DispatchMessageData<DispatchPayload>,
 }
@@ -96,6 +96,9 @@ pub trait MessageDispatch {
 	/// Fine-grained result of single message dispatch (for better diagnostic purposes)
 	type DispatchLevelResult: Clone + sp_std::fmt::Debug + Eq;
 
+	/// Lane identifier type.
+	type LaneId: Encode;
+
 	/// Returns `true` if dispatcher is ready to accept additional messages. The `false` should
 	/// be treated as a hint by both dispatcher and its consumers - i.e. dispatcher shall not
 	/// simply drop messages if it returns `false`. The consumer may still call the `dispatch`
@@ -103,21 +106,23 @@ pub trait MessageDispatch {
 	///
 	/// We check it in the messages delivery transaction prologue. So if it becomes `false`
 	/// after some portion of messages is already dispatched, it doesn't fail the whole transaction.
-	fn is_active(lane: LaneId) -> bool;
+	fn is_active(lane: Self::LaneId) -> bool;
 
 	/// Estimate dispatch weight.
 	///
 	/// This function must return correct upper bound of dispatch weight. The return value
 	/// of this function is expected to match return value of the corresponding
 	/// `From<Chain>InboundLaneApi::message_details().dispatch_weight` call.
-	fn dispatch_weight(message: &mut DispatchMessage<Self::DispatchPayload>) -> Weight;
+	fn dispatch_weight(
+		message: &mut DispatchMessage<Self::DispatchPayload, Self::LaneId>,
+	) -> Weight;
 
 	/// Called when inbound message is received.
 	///
 	/// It is up to the implementers of this trait to determine whether the message
 	/// is invalid (i.e. improperly encoded, has too large weight, ...) or not.
 	fn dispatch(
-		message: DispatchMessage<Self::DispatchPayload>,
+		message: DispatchMessage<Self::DispatchPayload, Self::LaneId>,
 	) -> MessageDispatchResult<Self::DispatchLevelResult>;
 }
 
@@ -146,8 +151,10 @@ impl<Message> Default for ProvedLaneMessages<Message> {
 	}
 }
 
-impl<DispatchPayload: Decode> From<Message> for DispatchMessage<DispatchPayload> {
-	fn from(message: Message) -> Self {
+impl<DispatchPayload: Decode, LaneId: Encode> From<Message<LaneId>>
+	for DispatchMessage<DispatchPayload, LaneId>
+{
+	fn from(message: Message<LaneId>) -> Self {
 		DispatchMessage { key: message.key, data: message.payload.into() }
 	}
 }
@@ -173,22 +180,27 @@ impl<AccountId> DeliveryPayments<AccountId> for () {
 
 /// Structure that may be used in place of  `MessageDispatch` on chains,
 /// where inbound messages are forbidden.
-pub struct ForbidInboundMessages<DispatchPayload>(PhantomData<DispatchPayload>);
+pub struct ForbidInboundMessages<DispatchPayload, LaneId>(PhantomData<(DispatchPayload, LaneId)>);
 
-impl<DispatchPayload: Decode> MessageDispatch for ForbidInboundMessages<DispatchPayload> {
+impl<DispatchPayload: Decode, LaneId: Encode> MessageDispatch
+	for ForbidInboundMessages<DispatchPayload, LaneId>
+{
 	type DispatchPayload = DispatchPayload;
 	type DispatchLevelResult = ();
+	type LaneId = LaneId;
 
 	fn is_active(_: LaneId) -> bool {
 		false
 	}
 
-	fn dispatch_weight(_message: &mut DispatchMessage<Self::DispatchPayload>) -> Weight {
+	fn dispatch_weight(
+		_message: &mut DispatchMessage<Self::DispatchPayload, Self::LaneId>,
+	) -> Weight {
 		Weight::MAX
 	}
 
 	fn dispatch(
-		_: DispatchMessage<Self::DispatchPayload>,
+		_: DispatchMessage<Self::DispatchPayload, Self::LaneId>,
 	) -> MessageDispatchResult<Self::DispatchLevelResult> {
 		MessageDispatchResult { unspent_weight: Weight::zero(), dispatch_level_result: () }
 	}
diff --git a/bridges/primitives/relayers/src/extension.rs b/bridges/primitives/relayers/src/extension.rs
index 5ab8e6cde96b408f6415768053eebf14c2b82ae4..a9fa4df27ead317d6a34babe36dd289b3db568b0 100644
--- a/bridges/primitives/relayers/src/extension.rs
+++ b/bridges/primitives/relayers/src/extension.rs
@@ -21,6 +21,7 @@ use bp_header_chain::SubmitFinalityProofInfo;
 use bp_messages::MessagesCallInfo;
 use bp_parachains::SubmitParachainHeadsInfo;
 use bp_runtime::StaticStrProvider;
+use codec::{Decode, Encode};
 use frame_support::{
 	dispatch::CallableCallFor, traits::IsSubType, weights::Weight, RuntimeDebugNoBound,
 };
@@ -35,25 +36,28 @@ use sp_std::{fmt::Debug, marker::PhantomData, vec, vec::Vec};
 
 /// Type of the call that the signed extension recognizes.
 #[derive(PartialEq, RuntimeDebugNoBound)]
-pub enum ExtensionCallInfo<RemoteGrandpaChainBlockNumber: Debug> {
+pub enum ExtensionCallInfo<RemoteGrandpaChainBlockNumber: Debug, LaneId: Clone + Copy + Debug> {
 	/// Relay chain finality + parachain finality + message delivery/confirmation calls.
 	AllFinalityAndMsgs(
 		SubmitFinalityProofInfo<RemoteGrandpaChainBlockNumber>,
 		SubmitParachainHeadsInfo,
-		MessagesCallInfo,
+		MessagesCallInfo<LaneId>,
 	),
 	/// Relay chain finality + message delivery/confirmation calls.
-	RelayFinalityAndMsgs(SubmitFinalityProofInfo<RemoteGrandpaChainBlockNumber>, MessagesCallInfo),
+	RelayFinalityAndMsgs(
+		SubmitFinalityProofInfo<RemoteGrandpaChainBlockNumber>,
+		MessagesCallInfo<LaneId>,
+	),
 	/// Parachain finality + message delivery/confirmation calls.
 	///
 	/// This variant is used only when bridging with parachain.
-	ParachainFinalityAndMsgs(SubmitParachainHeadsInfo, MessagesCallInfo),
+	ParachainFinalityAndMsgs(SubmitParachainHeadsInfo, MessagesCallInfo<LaneId>),
 	/// Standalone message delivery/confirmation call.
-	Msgs(MessagesCallInfo),
+	Msgs(MessagesCallInfo<LaneId>),
 }
 
-impl<RemoteGrandpaChainBlockNumber: Clone + Copy + Debug>
-	ExtensionCallInfo<RemoteGrandpaChainBlockNumber>
+impl<RemoteGrandpaChainBlockNumber: Clone + Copy + Debug, LaneId: Clone + Copy + Debug>
+	ExtensionCallInfo<RemoteGrandpaChainBlockNumber, LaneId>
 {
 	/// Returns true if call is a message delivery call (with optional finality calls).
 	pub fn is_receive_messages_proof_call(&self) -> bool {
@@ -84,7 +88,7 @@ impl<RemoteGrandpaChainBlockNumber: Clone + Copy + Debug>
 	}
 
 	/// Returns the pre-dispatch `ReceiveMessagesProofInfo`.
-	pub fn messages_call_info(&self) -> &MessagesCallInfo {
+	pub fn messages_call_info(&self) -> &MessagesCallInfo<LaneId> {
 		match self {
 			Self::AllFinalityAndMsgs(_, _, info) => info,
 			Self::RelayFinalityAndMsgs(_, info) => info,
@@ -119,25 +123,27 @@ pub trait ExtensionConfig {
 	/// Runtime that optionally supports batched calls. We assume that batched call
 	/// succeeds if and only if all of its nested calls succeed.
 	type Runtime: frame_system::Config;
+	/// Relayers pallet instance.
+	type BridgeRelayersPalletInstance: 'static;
 	/// Messages pallet instance.
 	type BridgeMessagesPalletInstance: 'static;
 	/// Additional priority that is added to base message delivery transaction priority
 	/// for every additional bundled message.
 	type PriorityBoostPerMessage: Get<TransactionPriority>;
-	/// Type of reward, that the `pallet-bridge-relayers` is using.
-	type Reward;
 	/// Block number for the remote **GRANDPA chain**. Mind that this chain is not
 	/// necessarily the chain that we are bridging with. If we are bridging with
 	/// parachain, it must be its parent relay chain. If we are bridging with the
 	/// GRANDPA chain, it must be it.
 	type RemoteGrandpaChainBlockNumber: Clone + Copy + Debug;
+	/// Lane identifier type.
+	type LaneId: Clone + Copy + Decode + Encode + Debug;
 
 	/// Given runtime call, check if it is supported by the signed extension. Additionally,
 	/// check if call (or any of batched calls) are obsolete.
 	fn parse_and_check_for_obsolete_call(
 		call: &<Self::Runtime as SystemConfig>::RuntimeCall,
 	) -> Result<
-		Option<ExtensionCallInfo<Self::RemoteGrandpaChainBlockNumber>>,
+		Option<ExtensionCallInfo<Self::RemoteGrandpaChainBlockNumber, Self::LaneId>>,
 		TransactionValidityError,
 	>;
 
@@ -149,7 +155,7 @@ pub trait ExtensionConfig {
 	/// Given runtime call info, check that this call has been successful and has updated
 	/// runtime storage accordingly.
 	fn check_call_result(
-		call_info: &ExtensionCallInfo<Self::RemoteGrandpaChainBlockNumber>,
+		call_info: &ExtensionCallInfo<Self::RemoteGrandpaChainBlockNumber, Self::LaneId>,
 		call_data: &mut ExtensionCallData,
 		relayer: &<Self::Runtime as SystemConfig>::AccountId,
 	) -> bool;
diff --git a/bridges/primitives/relayers/src/lib.rs b/bridges/primitives/relayers/src/lib.rs
index 1e63c89ecd70434de754e1442c193d65bb5ec182..faa4cb17762940ff0dc6d3fbf322dbf99bb92e49 100644
--- a/bridges/primitives/relayers/src/lib.rs
+++ b/bridges/primitives/relayers/src/lib.rs
@@ -25,7 +25,6 @@ pub use extension::{
 };
 pub use registration::{ExplicitOrAccountParams, Registration, StakeAndSlash};
 
-use bp_messages::LaneId;
 use bp_runtime::{ChainId, StorageDoubleMapKeyProvider};
 use frame_support::{traits::tokens::Preservation, Blake2_128Concat, Identity};
 use scale_info::TypeInfo;
@@ -61,7 +60,7 @@ pub enum RewardsAccountOwner {
 /// of the sovereign accounts will pay rewards for different operations. So we need multiple
 /// parameters to identify the account that pays a reward to the relayer.
 #[derive(Copy, Clone, Debug, Decode, Encode, Eq, PartialEq, TypeInfo, MaxEncodedLen)]
-pub struct RewardsAccountParams {
+pub struct RewardsAccountParams<LaneId> {
 	// **IMPORTANT NOTE**: the order of fields here matters - we are using
 	// `into_account_truncating` and lane id is already `32` byte, so if other fields are encoded
 	// after it, they're simply dropped. So lane id shall be the last field.
@@ -70,7 +69,7 @@ pub struct RewardsAccountParams {
 	lane_id: LaneId,
 }
 
-impl RewardsAccountParams {
+impl<LaneId: Decode + Encode> RewardsAccountParams<LaneId> {
 	/// Create a new instance of `RewardsAccountParams`.
 	pub const fn new(
 		lane_id: LaneId,
@@ -79,9 +78,14 @@ impl RewardsAccountParams {
 	) -> Self {
 		Self { lane_id, bridged_chain_id, owner }
 	}
+
+	/// Getter for `lane_id`.
+	pub const fn lane_id(&self) -> &LaneId {
+		&self.lane_id
+	}
 }
 
-impl TypeId for RewardsAccountParams {
+impl<LaneId: Decode + Encode> TypeId for RewardsAccountParams<LaneId> {
 	const TYPE_ID: [u8; 4] = *b"brap";
 }
 
@@ -89,47 +93,58 @@ impl TypeId for RewardsAccountParams {
 pub trait PaymentProcedure<Relayer, Reward> {
 	/// Error that may be returned by the procedure.
 	type Error: Debug;
+	/// Lane identifier type.
+	type LaneId: Decode + Encode;
 
 	/// Pay reward to the relayer from the account with provided params.
 	fn pay_reward(
 		relayer: &Relayer,
-		rewards_account_params: RewardsAccountParams,
+		rewards_account_params: RewardsAccountParams<Self::LaneId>,
 		reward: Reward,
 	) -> Result<(), Self::Error>;
 }
 
 impl<Relayer, Reward> PaymentProcedure<Relayer, Reward> for () {
 	type Error = &'static str;
+	type LaneId = ();
 
-	fn pay_reward(_: &Relayer, _: RewardsAccountParams, _: Reward) -> Result<(), Self::Error> {
+	fn pay_reward(
+		_: &Relayer,
+		_: RewardsAccountParams<Self::LaneId>,
+		_: Reward,
+	) -> Result<(), Self::Error> {
 		Ok(())
 	}
 }
 
 /// Reward payment procedure that does `balances::transfer` call from the account, derived from
 /// given params.
-pub struct PayRewardFromAccount<T, Relayer>(PhantomData<(T, Relayer)>);
+pub struct PayRewardFromAccount<T, Relayer, LaneId>(PhantomData<(T, Relayer, LaneId)>);
 
-impl<T, Relayer> PayRewardFromAccount<T, Relayer>
+impl<T, Relayer, LaneId> PayRewardFromAccount<T, Relayer, LaneId>
 where
 	Relayer: Decode + Encode,
+	LaneId: Decode + Encode,
 {
 	/// Return account that pays rewards based on the provided parameters.
-	pub fn rewards_account(params: RewardsAccountParams) -> Relayer {
+	pub fn rewards_account(params: RewardsAccountParams<LaneId>) -> Relayer {
 		params.into_sub_account_truncating(b"rewards-account")
 	}
 }
 
-impl<T, Relayer> PaymentProcedure<Relayer, T::Balance> for PayRewardFromAccount<T, Relayer>
+impl<T, Relayer, LaneId> PaymentProcedure<Relayer, T::Balance>
+	for PayRewardFromAccount<T, Relayer, LaneId>
 where
 	T: frame_support::traits::fungible::Mutate<Relayer>,
 	Relayer: Decode + Encode + Eq,
+	LaneId: Decode + Encode,
 {
 	type Error = sp_runtime::DispatchError;
+	type LaneId = LaneId;
 
 	fn pay_reward(
 		relayer: &Relayer,
-		rewards_account_params: RewardsAccountParams,
+		rewards_account_params: RewardsAccountParams<Self::LaneId>,
 		reward: T::Balance,
 	) -> Result<(), Self::Error> {
 		T::transfer(
@@ -142,48 +157,56 @@ where
 	}
 }
 
-/// Can be use to access the runtime storage key within the `RelayerRewards` map of the relayers
+/// Can be used to access the runtime storage key within the `RelayerRewards` map of the relayers
 /// pallet.
-pub struct RelayerRewardsKeyProvider<AccountId, Reward>(PhantomData<(AccountId, Reward)>);
+pub struct RelayerRewardsKeyProvider<AccountId, Reward, LaneId>(
+	PhantomData<(AccountId, Reward, LaneId)>,
+);
 
-impl<AccountId, Reward> StorageDoubleMapKeyProvider for RelayerRewardsKeyProvider<AccountId, Reward>
+impl<AccountId, Reward, LaneId> StorageDoubleMapKeyProvider
+	for RelayerRewardsKeyProvider<AccountId, Reward, LaneId>
 where
 	AccountId: 'static + Codec + EncodeLike + Send + Sync,
 	Reward: 'static + Codec + EncodeLike + Send + Sync,
+	LaneId: Codec + EncodeLike + Send + Sync,
 {
 	const MAP_NAME: &'static str = "RelayerRewards";
 
 	type Hasher1 = Blake2_128Concat;
 	type Key1 = AccountId;
 	type Hasher2 = Identity;
-	type Key2 = RewardsAccountParams;
+	type Key2 = RewardsAccountParams<LaneId>;
 	type Value = Reward;
 }
 
 #[cfg(test)]
 mod tests {
 	use super::*;
-	use bp_messages::LaneId;
-	use sp_runtime::testing::H256;
+	use bp_messages::{HashedLaneId, LaneIdType, LegacyLaneId};
+	use sp_runtime::{app_crypto::Ss58Codec, testing::H256};
 
 	#[test]
 	fn different_lanes_are_using_different_accounts() {
 		assert_eq!(
-			PayRewardFromAccount::<(), H256>::rewards_account(RewardsAccountParams::new(
-				LaneId::new(1, 2),
-				*b"test",
-				RewardsAccountOwner::ThisChain
-			)),
+			PayRewardFromAccount::<(), H256, HashedLaneId>::rewards_account(
+				RewardsAccountParams::new(
+					HashedLaneId::try_new(1, 2).unwrap(),
+					*b"test",
+					RewardsAccountOwner::ThisChain
+				)
+			),
 			hex_literal::hex!("627261700074657374b1d3dccd8b3c3a012afe265f3e3c4432129b8aee50c9dc")
 				.into(),
 		);
 
 		assert_eq!(
-			PayRewardFromAccount::<(), H256>::rewards_account(RewardsAccountParams::new(
-				LaneId::new(1, 3),
-				*b"test",
-				RewardsAccountOwner::ThisChain
-			)),
+			PayRewardFromAccount::<(), H256, HashedLaneId>::rewards_account(
+				RewardsAccountParams::new(
+					HashedLaneId::try_new(1, 3).unwrap(),
+					*b"test",
+					RewardsAccountOwner::ThisChain
+				)
+			),
 			hex_literal::hex!("627261700074657374a43e8951aa302c133beb5f85821a21645f07b487270ef3")
 				.into(),
 		);
@@ -192,23 +215,101 @@ mod tests {
 	#[test]
 	fn different_directions_are_using_different_accounts() {
 		assert_eq!(
-			PayRewardFromAccount::<(), H256>::rewards_account(RewardsAccountParams::new(
-				LaneId::new(1, 2),
-				*b"test",
-				RewardsAccountOwner::ThisChain
-			)),
+			PayRewardFromAccount::<(), H256, HashedLaneId>::rewards_account(
+				RewardsAccountParams::new(
+					HashedLaneId::try_new(1, 2).unwrap(),
+					*b"test",
+					RewardsAccountOwner::ThisChain
+				)
+			),
 			hex_literal::hex!("627261700074657374b1d3dccd8b3c3a012afe265f3e3c4432129b8aee50c9dc")
 				.into(),
 		);
 
 		assert_eq!(
-			PayRewardFromAccount::<(), H256>::rewards_account(RewardsAccountParams::new(
-				LaneId::new(1, 2),
-				*b"test",
-				RewardsAccountOwner::BridgedChain
-			)),
+			PayRewardFromAccount::<(), H256, HashedLaneId>::rewards_account(
+				RewardsAccountParams::new(
+					HashedLaneId::try_new(1, 2).unwrap(),
+					*b"test",
+					RewardsAccountOwner::BridgedChain
+				)
+			),
 			hex_literal::hex!("627261700174657374b1d3dccd8b3c3a012afe265f3e3c4432129b8aee50c9dc")
 				.into(),
 		);
 	}
+
+	#[test]
+	fn pay_reward_from_account_for_legacy_lane_id_works() {
+		let test_data = vec![
+			// Note: these accounts are used for integration tests within
+			// `bridges_rococo_westend.sh`
+			(
+				LegacyLaneId([0, 0, 0, 1]),
+				b"bhks",
+				RewardsAccountOwner::ThisChain,
+				(0_u16, "13E5fui97x6KTwNnSjaEKZ8s7kJNot5F3aUsy3jUtuoMyUec"),
+			),
+			(
+				LegacyLaneId([0, 0, 0, 1]),
+				b"bhks",
+				RewardsAccountOwner::BridgedChain,
+				(0_u16, "13E5fui9Ka9Vz4JbGN3xWjmwDNxnxF1N9Hhhbeu3VCqLChuj"),
+			),
+			(
+				LegacyLaneId([0, 0, 0, 1]),
+				b"bhpd",
+				RewardsAccountOwner::ThisChain,
+				(2_u16, "EoQBtnwtXqnSnr9cgBEJpKU7NjeC9EnR4D1VjgcvHz9ZYmS"),
+			),
+			(
+				LegacyLaneId([0, 0, 0, 1]),
+				b"bhpd",
+				RewardsAccountOwner::BridgedChain,
+				(2_u16, "EoQBtnx69txxumxSJexVzxYD1Q4LWAuWmRq8LrBWb27nhYN"),
+			),
+			// Note: these accounts are used for integration tests within
+			// `bridges_polkadot_kusama.sh` from fellows.
+			(
+				LegacyLaneId([0, 0, 0, 2]),
+				b"bhwd",
+				RewardsAccountOwner::ThisChain,
+				(4_u16, "SNihsskf7bFhnHH9HJFMjWD3FJ96ESdAQTFZUAtXudRQbaH"),
+			),
+			(
+				LegacyLaneId([0, 0, 0, 2]),
+				b"bhwd",
+				RewardsAccountOwner::BridgedChain,
+				(4_u16, "SNihsskrjeSDuD5xumyYv9H8sxZEbNkG7g5C5LT8CfPdaSE"),
+			),
+			(
+				LegacyLaneId([0, 0, 0, 2]),
+				b"bhro",
+				RewardsAccountOwner::ThisChain,
+				(4_u16, "SNihsskf7bF2vWogkC6uFoiqPhd3dUX6TGzYZ1ocJdo3xHp"),
+			),
+			(
+				LegacyLaneId([0, 0, 0, 2]),
+				b"bhro",
+				RewardsAccountOwner::BridgedChain,
+				(4_u16, "SNihsskrjeRZ3ScWNfq6SSnw2N3BzQeCAVpBABNCbfmHENB"),
+			),
+		];
+
+		for (lane_id, bridged_chain_id, owner, (expected_ss58, expected_account)) in test_data {
+			assert_eq!(
+				expected_account,
+				sp_runtime::AccountId32::new(PayRewardFromAccount::<
+					[u8; 32],
+					[u8; 32],
+					LegacyLaneId,
+				>::rewards_account(RewardsAccountParams::new(
+					lane_id,
+					*bridged_chain_id,
+					owner
+				)))
+				.to_ss58check_with_version(expected_ss58.into())
+			);
+		}
+	}
 }
diff --git a/bridges/primitives/relayers/src/registration.rs b/bridges/primitives/relayers/src/registration.rs
index 9d9b7e4812201390c6831ff020c12c8cc995c17a..d74ef18cf706bdee134dc1157a150a3132b36a2e 100644
--- a/bridges/primitives/relayers/src/registration.rs
+++ b/bridges/primitives/relayers/src/registration.rs
@@ -48,15 +48,17 @@ use sp_runtime::{
 
 /// Either explicit account reference or `RewardsAccountParams`.
 #[derive(Clone, Debug)]
-pub enum ExplicitOrAccountParams<AccountId> {
+pub enum ExplicitOrAccountParams<AccountId, LaneId: Decode + Encode> {
 	/// Explicit account reference.
 	Explicit(AccountId),
 	/// Account, referenced using `RewardsAccountParams`.
-	Params(RewardsAccountParams),
+	Params(RewardsAccountParams<LaneId>),
 }
 
-impl<AccountId> From<RewardsAccountParams> for ExplicitOrAccountParams<AccountId> {
-	fn from(params: RewardsAccountParams) -> Self {
+impl<AccountId, LaneId: Decode + Encode> From<RewardsAccountParams<LaneId>>
+	for ExplicitOrAccountParams<AccountId, LaneId>
+{
+	fn from(params: RewardsAccountParams<LaneId>) -> Self {
 		ExplicitOrAccountParams::Params(params)
 	}
 }
@@ -103,9 +105,9 @@ pub trait StakeAndSlash<AccountId, BlockNumber, Balance> {
 	/// `beneficiary`.
 	///
 	/// Returns `Ok(_)` with non-zero balance if we have failed to repatriate some portion of stake.
-	fn repatriate_reserved(
+	fn repatriate_reserved<LaneId: Decode + Encode>(
 		relayer: &AccountId,
-		beneficiary: ExplicitOrAccountParams<AccountId>,
+		beneficiary: ExplicitOrAccountParams<AccountId, LaneId>,
 		amount: Balance,
 	) -> Result<Balance, DispatchError>;
 }
@@ -126,9 +128,9 @@ where
 		Zero::zero()
 	}
 
-	fn repatriate_reserved(
+	fn repatriate_reserved<LaneId: Decode + Encode>(
 		_relayer: &AccountId,
-		_beneficiary: ExplicitOrAccountParams<AccountId>,
+		_beneficiary: ExplicitOrAccountParams<AccountId, LaneId>,
 		_amount: Balance,
 	) -> Result<Balance, DispatchError> {
 		Ok(Zero::zero())
diff --git a/bridges/primitives/runtime/src/chain.rs b/bridges/primitives/runtime/src/chain.rs
index 0db4eac79a7500bf295756efaef514f5a4429e6c..eba3bcadfead084878e456ebe8d69cc69d015da0 100644
--- a/bridges/primitives/runtime/src/chain.rs
+++ b/bridges/primitives/runtime/src/chain.rs
@@ -365,17 +365,23 @@ macro_rules! decl_bridge_finality_runtime_apis {
 	};
 }
 
+// Re-export to avoid include tuplex dependency everywhere.
+#[doc(hidden)]
+pub mod __private {
+	pub use codec;
+}
+
 /// Convenience macro that declares bridge messages runtime apis and related constants for a chain.
 /// This includes:
 /// - chain-specific bridge runtime APIs:
-///     - `To<ThisChain>OutboundLaneApi`
-///     - `From<ThisChain>InboundLaneApi`
+///     - `To<ThisChain>OutboundLaneApi<LaneIdType>`
+///     - `From<ThisChain>InboundLaneApi<LaneIdType>`
 /// - constants that are stringified names of runtime API methods:
 ///     - `FROM_<THIS_CHAIN>_MESSAGE_DETAILS_METHOD`,
 /// The name of the chain has to be specified in snake case (e.g. `bridge_hub_polkadot`).
 #[macro_export]
 macro_rules! decl_bridge_messages_runtime_apis {
-	($chain: ident) => {
+	($chain: ident, $lane_id_type:ty) => {
 		bp_runtime::paste::item! {
 			mod [<$chain _messages_api>] {
 				use super::*;
@@ -400,7 +406,7 @@ macro_rules! decl_bridge_messages_runtime_apis {
 						/// If some (or all) messages are missing from the storage, they'll also will
 						/// be missing from the resulting vector. The vector is ordered by the nonce.
 						fn message_details(
-							lane: bp_messages::LaneId,
+							lane: $lane_id_type,
 							begin: bp_messages::MessageNonce,
 							end: bp_messages::MessageNonce,
 						) -> sp_std::vec::Vec<bp_messages::OutboundMessageDetails>;
@@ -416,7 +422,7 @@ macro_rules! decl_bridge_messages_runtime_apis {
 					pub trait [<From $chain:camel InboundLaneApi>] {
 						/// Return details of given inbound messages.
 						fn message_details(
-							lane: bp_messages::LaneId,
+							lane: $lane_id_type,
 							messages: sp_std::vec::Vec<(bp_messages::MessagePayload, bp_messages::OutboundMessageDetails)>,
 						) -> sp_std::vec::Vec<bp_messages::InboundMessageDetails>;
 					}
@@ -433,8 +439,8 @@ macro_rules! decl_bridge_messages_runtime_apis {
 /// The name of the chain has to be specified in snake case (e.g. `bridge_hub_polkadot`).
 #[macro_export]
 macro_rules! decl_bridge_runtime_apis {
-	($chain: ident $(, $consensus: ident)?) => {
+	($chain: ident $(, $consensus: ident, $lane_id_type:ident)?) => {
 		bp_runtime::decl_bridge_finality_runtime_apis!($chain $(, $consensus)?);
-		bp_runtime::decl_bridge_messages_runtime_apis!($chain);
+		bp_runtime::decl_bridge_messages_runtime_apis!($chain, $lane_id_type);
 	};
 }
diff --git a/bridges/primitives/runtime/src/lib.rs b/bridges/primitives/runtime/src/lib.rs
index 8f5040ad9a1bee5efad995b4e62d5e883dfb0c9f..90eb72922beaa3bc490c879eacb43a7db521f9a0 100644
--- a/bridges/primitives/runtime/src/lib.rs
+++ b/bridges/primitives/runtime/src/lib.rs
@@ -36,7 +36,7 @@ use sp_std::{fmt::Debug, ops::RangeInclusive, vec, vec::Vec};
 pub use chain::{
 	AccountIdOf, AccountPublicOf, BalanceOf, BlockNumberOf, Chain, EncodedOrDecodedCall, HashOf,
 	HasherOf, HeaderOf, NonceOf, Parachain, ParachainIdOf, SignatureOf, TransactionEraOf,
-	UnderlyingChainOf, UnderlyingChainProvider,
+	UnderlyingChainOf, UnderlyingChainProvider, __private,
 };
 pub use frame_support::storage::storage_prefix as storage_value_final_key;
 use num_traits::{CheckedAdd, CheckedSub, One, SaturatingAdd, Zero};
@@ -272,7 +272,7 @@ pub trait StorageMapKeyProvider {
 	}
 }
 
-/// Can be use to access the runtime storage key of a `StorageDoubleMap`.
+/// Can be used to access the runtime storage key of a `StorageDoubleMap`.
 pub trait StorageDoubleMapKeyProvider {
 	/// The name of the variable that holds the `StorageDoubleMap`.
 	const MAP_NAME: &'static str;
diff --git a/bridges/primitives/xcm-bridge-hub/src/lib.rs b/bridges/primitives/xcm-bridge-hub/src/lib.rs
index 44a90a57d4fbc6c423973ab89d6756b5832da1ff..061e7a27506329029da1e62f7ddd56f516b5dd80 100644
--- a/bridges/primitives/xcm-bridge-hub/src/lib.rs
+++ b/bridges/primitives/xcm-bridge-hub/src/lib.rs
@@ -19,7 +19,7 @@
 #![warn(missing_docs)]
 #![cfg_attr(not(feature = "std"), no_std)]
 
-use bp_messages::LaneId;
+use bp_messages::LaneIdType;
 use bp_runtime::{AccountIdOf, BalanceOf, Chain};
 pub use call_info::XcmBridgeHubCall;
 use codec::{Decode, Encode, MaxEncodedLen};
@@ -63,7 +63,6 @@ pub type XcmAsPlainPayload = sp_std::vec::Vec<u8>;
 	Ord,
 	PartialOrd,
 	PartialEq,
-	RuntimeDebug,
 	TypeInfo,
 	MaxEncodedLen,
 	Serialize,
@@ -90,6 +89,12 @@ impl BridgeId {
 	}
 }
 
+impl core::fmt::Debug for BridgeId {
+	fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
+		core::fmt::Debug::fmt(&self.0, f)
+	}
+}
+
 /// Local XCM channel manager.
 pub trait LocalXcmChannelManager {
 	/// Error that may be returned when suspending/resuming the bridge.
@@ -149,8 +154,8 @@ pub enum BridgeState {
 #[derive(
 	CloneNoBound, Decode, Encode, Eq, PartialEqNoBound, TypeInfo, MaxEncodedLen, RuntimeDebugNoBound,
 )]
-#[scale_info(skip_type_params(ThisChain))]
-pub struct Bridge<ThisChain: Chain> {
+#[scale_info(skip_type_params(ThisChain, LaneId))]
+pub struct Bridge<ThisChain: Chain, LaneId: LaneIdType> {
 	/// Relative location of the bridge origin chain. This is expected to be **convertible** to the
 	/// `latest` XCM, so the check and migration needs to be ensured.
 	pub bridge_origin_relative_location: Box<VersionedLocation>,
@@ -204,6 +209,8 @@ pub enum BridgeLocationsError {
 	UnsupportedDestinationLocation,
 	/// The version of XCM location argument is unsupported.
 	UnsupportedXcmVersion,
+	/// The `LaneIdType` generator is not supported.
+	UnsupportedLaneIdType,
 }
 
 impl BridgeLocations {
@@ -318,7 +325,7 @@ impl BridgeLocations {
 	/// Generates the exact same `LaneId` on the both bridge hubs.
 	///
 	/// Note: Use this **only** when opening a new bridge.
-	pub fn calculate_lane_id(
+	pub fn calculate_lane_id<LaneId: LaneIdType>(
 		&self,
 		xcm_version: XcmVersion,
 	) -> Result<LaneId, BridgeLocationsError> {
@@ -341,10 +348,11 @@ impl BridgeLocations {
 				.into_version(xcm_version)
 				.map_err(|_| BridgeLocationsError::UnsupportedXcmVersion);
 
-		Ok(LaneId::new(
+		LaneId::try_new(
 			EncodedVersionedInteriorLocation(universal_location1.encode()),
 			EncodedVersionedInteriorLocation(universal_location2.encode()),
-		))
+		)
+		.map_err(|_| BridgeLocationsError::UnsupportedLaneIdType)
 	}
 }
 
@@ -590,6 +598,8 @@ mod tests {
 
 	#[test]
 	fn calculate_lane_id_works() {
+		type TestLaneId = bp_messages::HashedLaneId;
+
 		let from_local_to_remote = run_successful_test(SuccessfulTest {
 			here_universal_location: [GlobalConsensus(LOCAL_NETWORK), Parachain(LOCAL_BRIDGE_HUB)]
 				.into(),
@@ -631,12 +641,12 @@ mod tests {
 		});
 
 		assert_ne!(
-			from_local_to_remote.calculate_lane_id(xcm::latest::VERSION),
-			from_remote_to_local.calculate_lane_id(xcm::latest::VERSION - 1),
+			from_local_to_remote.calculate_lane_id::<TestLaneId>(xcm::latest::VERSION),
+			from_remote_to_local.calculate_lane_id::<TestLaneId>(xcm::latest::VERSION - 1),
 		);
 		assert_eq!(
-			from_local_to_remote.calculate_lane_id(xcm::latest::VERSION),
-			from_remote_to_local.calculate_lane_id(xcm::latest::VERSION),
+			from_local_to_remote.calculate_lane_id::<TestLaneId>(xcm::latest::VERSION),
+			from_remote_to_local.calculate_lane_id::<TestLaneId>(xcm::latest::VERSION),
 		);
 	}
 
diff --git a/bridges/relays/client-substrate/src/chain.rs b/bridges/relays/client-substrate/src/chain.rs
index 227e9c31c5bfc6a93df88bb117f29bdb1b7c61d3..9856f0d0237ea63997ffc24baf0f1f099df9ef6b 100644
--- a/bridges/relays/client-substrate/src/chain.rs
+++ b/bridges/relays/client-substrate/src/chain.rs
@@ -113,9 +113,6 @@ 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 + ChainWithMessagesBase {
-	// TODO (https://github.com/paritytech/parity-bridges-common/issues/1692): check all the names
-	// after the issue is fixed - all names must be changed
-
 	/// Name of the bridge relayers pallet (used in `construct_runtime` macro call) that is deployed
 	/// at some other chain to bridge with this `ChainWithMessages`.
 	///
diff --git a/bridges/relays/lib-substrate-relay/src/cli/bridge.rs b/bridges/relays/lib-substrate-relay/src/cli/bridge.rs
index 28b0eb0ad526789d44ec6936e01e269f4e93a816..2e15562f6c2ea21d778f3ea53ee9cc8814203771 100644
--- a/bridges/relays/lib-substrate-relay/src/cli/bridge.rs
+++ b/bridges/relays/lib-substrate-relay/src/cli/bridge.rs
@@ -23,9 +23,12 @@ use crate::{
 	parachains::SubstrateParachainsPipeline,
 };
 use bp_parachains::{RelayBlockHash, RelayBlockHasher, RelayBlockNumber};
+use codec::{Codec, EncodeLike};
+use messages_relay::Labeled;
 use relay_substrate_client::{
 	Chain, ChainWithRuntimeVersion, ChainWithTransactions, Parachain, RelayChain,
 };
+use std::fmt::Debug;
 
 /// Minimal bridge representation that can be used from the CLI.
 /// It connects a source chain to a target chain.
@@ -99,7 +102,22 @@ where
 /// Bridge representation that can be used from the CLI for relaying messages.
 pub trait MessagesCliBridge: CliBridgeBase {
 	/// The Source -> Destination messages synchronization pipeline.
-	type MessagesLane: SubstrateMessageLane<SourceChain = Self::Source, TargetChain = Self::Target>;
+	type MessagesLane: SubstrateMessageLane<
+		SourceChain = Self::Source,
+		TargetChain = Self::Target,
+		LaneId = Self::LaneId,
+	>;
+	/// Lane identifier type.
+	type LaneId: Clone
+		+ Copy
+		+ Debug
+		+ Codec
+		+ EncodeLike
+		+ Send
+		+ Sync
+		+ Labeled
+		+ TryFrom<Vec<u8>>
+		+ Default;
 
 	/// Optional messages delivery transaction limits that the messages relay is going
 	/// to use. If it returns `None`, limits are estimated using `TransactionPayment` API
@@ -108,3 +126,6 @@ pub trait MessagesCliBridge: CliBridgeBase {
 		None
 	}
 }
+
+/// An alias for lane identifier type.
+pub type MessagesLaneIdOf<B> = <B as MessagesCliBridge>::LaneId;
diff --git a/bridges/relays/lib-substrate-relay/src/cli/mod.rs b/bridges/relays/lib-substrate-relay/src/cli/mod.rs
index ef8403ff68ee1ea2d849e5f221c439ef537e8d35..be64866fc14bce70184efb8bd0d7cda656ae6a87 100644
--- a/bridges/relays/lib-substrate-relay/src/cli/mod.rs
+++ b/bridges/relays/lib-substrate-relay/src/cli/mod.rs
@@ -16,10 +16,8 @@
 
 //! Deal with CLI args of substrate-to-substrate relay.
 
-use bp_messages::LaneId;
 use rbtag::BuildInfo;
-use sp_core::H256;
-use sp_runtime::Either;
+use sp_runtime::traits::TryConvert;
 use std::str::FromStr;
 use structopt::StructOpt;
 use strum::{EnumString, VariantNames};
@@ -43,36 +41,19 @@ pub type DefaultClient<C> = relay_substrate_client::RpcWithCachingClient<C>;
 
 /// Lane id.
 #[derive(Debug, Clone, PartialEq, Eq)]
-pub struct HexLaneId(Either<H256, [u8; 4]>);
+pub struct HexLaneId(Vec<u8>);
 
-impl From<HexLaneId> for LaneId {
-	fn from(lane_id: HexLaneId) -> LaneId {
-		LaneId::from_inner(lane_id.0)
+impl<T: TryFrom<Vec<u8>>> TryConvert<HexLaneId, T> for HexLaneId {
+	fn try_convert(lane_id: HexLaneId) -> Result<T, HexLaneId> {
+		T::try_from(lane_id.0.clone()).map_err(|_| lane_id)
 	}
 }
 
 impl FromStr for HexLaneId {
-	type Err = rustc_hex::FromHexError;
+	type Err = hex::FromHexError;
 
 	fn from_str(s: &str) -> Result<Self, Self::Err> {
-		// check `H256` variant at first
-		match H256::from_str(s) {
-			Ok(hash) => Ok(HexLaneId(Either::Left(hash))),
-			Err(hash_error) => {
-				// check backwards compatible
-				let mut lane_id = [0u8; 4];
-				match hex::decode_to_slice(s, &mut lane_id) {
-					Ok(_) => Ok(HexLaneId(Either::Right(lane_id))),
-					Err(array_error) => {
-						log::error!(
-							target: "bridge",
-							"Failed to parse `HexLaneId` as hex string: {s:?} - hash_error: {hash_error:?}, array_error: {array_error:?}",
-						);
-						Err(hash_error)
-					},
-				}
-			},
-		}
+		hex::decode(s).map(Self)
 	}
 }
 
@@ -172,6 +153,8 @@ pub enum RuntimeVersionType {
 #[cfg(test)]
 mod tests {
 	use super::*;
+	use bp_messages::{HashedLaneId, LegacyLaneId};
+	use sp_core::H256;
 
 	#[test]
 	fn hex_lane_id_from_str_works() {
@@ -185,21 +168,21 @@ mod tests {
 		)
 		.is_err());
 		assert_eq!(
-			LaneId::from(
+			HexLaneId::try_convert(
 				HexLaneId::from_str(
 					"0101010101010101010101010101010101010101010101010101010101010101"
 				)
 				.unwrap()
 			),
-			LaneId::from_inner(Either::Left(H256::from([1u8; 32])))
+			Ok(HashedLaneId::from_inner(H256::from([1u8; 32])))
 		);
 
 		// array variant
 		assert!(HexLaneId::from_str("0000001").is_err());
 		assert!(HexLaneId::from_str("000000001").is_err());
 		assert_eq!(
-			LaneId::from(HexLaneId::from_str("00000001").unwrap()),
-			LaneId::from_inner(Either::Right([0, 0, 0, 1]))
+			HexLaneId::try_convert(HexLaneId::from_str("00000001").unwrap()),
+			Ok(LegacyLaneId([0, 0, 0, 1]))
 		);
 	}
 }
diff --git a/bridges/relays/lib-substrate-relay/src/cli/relay_headers_and_messages/mod.rs b/bridges/relays/lib-substrate-relay/src/cli/relay_headers_and_messages/mod.rs
index 3786976bed9b0723af47b6f81da1b813405fa95c..9261dc4375363543857c47c5dcf0cf81bf9a891e 100644
--- a/bridges/relays/lib-substrate-relay/src/cli/relay_headers_and_messages/mod.rs
+++ b/bridges/relays/lib-substrate-relay/src/cli/relay_headers_and_messages/mod.rs
@@ -31,25 +31,30 @@ pub mod relay_to_relay;
 pub mod relay_to_parachain;
 
 use async_trait::async_trait;
-use std::{marker::PhantomData, sync::Arc};
+use codec::{Codec, EncodeLike};
+use std::{fmt::Debug, marker::PhantomData, sync::Arc};
 use structopt::StructOpt;
 
 use futures::{FutureExt, TryFutureExt};
 
 use crate::{
-	cli::{bridge::MessagesCliBridge, DefaultClient, HexLaneId, PrometheusParams},
+	cli::{
+		bridge::{MessagesCliBridge, MessagesLaneIdOf},
+		DefaultClient, HexLaneId, PrometheusParams,
+	},
 	messages::{MessagesRelayLimits, MessagesRelayParams},
 	on_demand::OnDemandRelay,
 	HeadersToRelay, TaggedAccount, TransactionParams,
 };
-use bp_messages::LaneId;
 use bp_runtime::BalanceOf;
+use messages_relay::Labeled;
 use relay_substrate_client::{
 	AccountIdOf, AccountKeyPairOf, Chain, ChainWithBalances, ChainWithMessages,
 	ChainWithRuntimeVersion, ChainWithTransactions,
 };
 use relay_utils::metrics::MetricsParams;
 use sp_core::Pair;
+use sp_runtime::traits::TryConvert;
 
 /// Parameters that have the same names across all bridges.
 #[derive(Debug, PartialEq, StructOpt)]
@@ -163,7 +168,7 @@ where
 		&self,
 		source_to_target_headers_relay: Arc<dyn OnDemandRelay<Source, Target>>,
 		target_to_source_headers_relay: Arc<dyn OnDemandRelay<Target, Source>>,
-		lane_id: LaneId,
+		lane_id: MessagesLaneIdOf<Bridge>,
 		maybe_limits: Option<MessagesRelayLimits>,
 	) -> MessagesRelayParams<Bridge::MessagesLane, DefaultClient<Source>, DefaultClient<Target>> {
 		MessagesRelayParams {
@@ -234,9 +239,20 @@ where
 		+ ChainWithRuntimeVersion;
 
 	/// Left to Right bridge.
-	type L2R: MessagesCliBridge<Source = Self::Left, Target = Self::Right>;
+	type L2R: MessagesCliBridge<Source = Self::Left, Target = Self::Right, LaneId = Self::LaneId>;
 	/// Right to Left bridge
-	type R2L: MessagesCliBridge<Source = Self::Right, Target = Self::Left>;
+	type R2L: MessagesCliBridge<Source = Self::Right, Target = Self::Left, LaneId = Self::LaneId>;
+	/// Lane identifier type.
+	type LaneId: Clone
+		+ Copy
+		+ Debug
+		+ Codec
+		+ EncodeLike
+		+ Send
+		+ Sync
+		+ Labeled
+		+ TryFrom<Vec<u8>>
+		+ Default;
 
 	/// Construct new bridge.
 	fn new(params: <Self::Base as Full2WayBridgeBase>::Params) -> anyhow::Result<Self>;
@@ -287,30 +303,29 @@ where
 			self.mut_base().start_on_demand_headers_relayers().await?;
 
 		// add balance-related metrics
-		let lanes = self
+		let lanes: Vec<Self::LaneId> = self
 			.base()
 			.common()
 			.shared
 			.lane
 			.iter()
 			.cloned()
-			.map(Into::into)
-			.collect::<Vec<_>>();
+			.map(HexLaneId::try_convert)
+			.collect::<Result<Vec<_>, HexLaneId>>()
+			.expect("");
 		{
 			let common = self.mut_base().mut_common();
-			crate::messages::metrics::add_relay_balances_metrics::<_, Self::Right>(
-				common.left.client.clone(),
-				&common.metrics_params,
-				&common.left.accounts,
-				&lanes,
-			)
+			crate::messages::metrics::add_relay_balances_metrics::<
+				_,
+				Self::Right,
+				MessagesLaneIdOf<Self::L2R>,
+			>(common.left.client.clone(), &common.metrics_params, &common.left.accounts, &lanes)
 			.await?;
-			crate::messages::metrics::add_relay_balances_metrics::<_, Self::Left>(
-				common.right.client.clone(),
-				&common.metrics_params,
-				&common.right.accounts,
-				&lanes,
-			)
+			crate::messages::metrics::add_relay_balances_metrics::<
+				_,
+				Self::Left,
+				MessagesLaneIdOf<Self::R2L>,
+			>(common.right.client.clone(), &common.metrics_params, &common.right.accounts, &lanes)
 			.await?;
 		}
 
@@ -359,8 +374,6 @@ mod tests {
 	use crate::{cli::chain_schema::RuntimeVersionType, declare_chain_cli_schema};
 
 	use relay_substrate_client::{ChainRuntimeVersion, Parachain, SimpleRuntimeVersion};
-	use sp_core::H256;
-	use sp_runtime::Either;
 
 	#[test]
 	// We need `#[allow(dead_code)]` because some of the methods generated by the macros
@@ -434,7 +447,7 @@ mod tests {
 			res,
 			BridgeHubKusamaBridgeHubPolkadotHeadersAndMessages {
 				shared: HeadersAndMessagesSharedParams {
-					lane: vec![HexLaneId(Either::Left(H256::from([0x00u8; 32])))],
+					lane: vec![HexLaneId(vec![0x00u8; 32])],
 					only_mandatory_headers: false,
 					only_free_headers: false,
 					prometheus_params: PrometheusParams {
diff --git a/bridges/relays/lib-substrate-relay/src/cli/relay_messages.rs b/bridges/relays/lib-substrate-relay/src/cli/relay_messages.rs
index 34d5226e90c59981ffca14f7bafafc3eb64a266c..3878b081d6c355a713122f481df9e0ceea91a299 100644
--- a/bridges/relays/lib-substrate-relay/src/cli/relay_messages.rs
+++ b/bridges/relays/lib-substrate-relay/src/cli/relay_messages.rs
@@ -33,6 +33,7 @@ use relay_substrate_client::{
 	ChainWithTransactions, Client,
 };
 use relay_utils::UniqueSaturatedInto;
+use sp_runtime::traits::TryConvert;
 
 /// Messages relaying params.
 #[derive(StructOpt)]
@@ -116,6 +117,9 @@ where
 		let target_client = data.target.into_client::<Self::Target>().await?;
 		let target_sign = data.target_sign.to_keypair::<Self::Target>()?;
 		let target_transactions_mortality = data.target_sign.transactions_mortality()?;
+		let lane_id = HexLaneId::try_convert(data.lane).map_err(|invalid_lane_id| {
+			anyhow::format_err!("Invalid laneId: {:?}!", invalid_lane_id)
+		})?;
 
 		crate::messages::run::<Self::MessagesLane, _, _>(MessagesRelayParams {
 			source_client,
@@ -130,7 +134,7 @@ where
 			},
 			source_to_target_headers_relay: None,
 			target_to_source_headers_relay: None,
-			lane_id: data.lane.into(),
+			lane_id,
 			limits: Self::maybe_messages_limits(),
 			metrics_params: data.prometheus_params.into_metrics_params()?,
 		})
@@ -146,6 +150,9 @@ where
 		let source_transactions_mortality = data.source_sign.transactions_mortality()?;
 		let target_sign = data.target_sign.to_keypair::<Self::Target>()?;
 		let target_transactions_mortality = data.target_sign.transactions_mortality()?;
+		let lane_id = HexLaneId::try_convert(data.lane).map_err(|invalid_lane_id| {
+			anyhow::format_err!("Invalid laneId: {:?}!", invalid_lane_id)
+		})?;
 
 		let at_source_block = source_client
 			.header_by_number(data.at_source_block.unique_saturated_into())
@@ -167,7 +174,7 @@ where
 			TransactionParams { signer: source_sign, mortality: source_transactions_mortality },
 			TransactionParams { signer: target_sign, mortality: target_transactions_mortality },
 			at_source_block,
-			data.lane.into(),
+			lane_id,
 			data.messages_start..=data.messages_end,
 			data.outbound_state_proof_required,
 		)
@@ -182,6 +189,9 @@ where
 		let target_client = data.target.into_client::<Self::Target>().await?;
 		let source_sign = data.source_sign.to_keypair::<Self::Source>()?;
 		let source_transactions_mortality = data.source_sign.transactions_mortality()?;
+		let lane_id = HexLaneId::try_convert(data.lane).map_err(|invalid_lane_id| {
+			anyhow::format_err!("Invalid laneId: {:?}!", invalid_lane_id)
+		})?;
 
 		let at_target_block = target_client
 			.header_by_number(data.at_target_block.unique_saturated_into())
@@ -202,7 +212,7 @@ where
 			target_client,
 			TransactionParams { signer: source_sign, mortality: source_transactions_mortality },
 			at_target_block,
-			data.lane.into(),
+			lane_id,
 		)
 		.await
 	}
diff --git a/bridges/relays/lib-substrate-relay/src/messages/metrics.rs b/bridges/relays/lib-substrate-relay/src/messages/metrics.rs
index 8845f43dcb62aa778b50354141381290fc0b897d..9d45a4b3d6682430bc112a3ce3b1d98585367725 100644
--- a/bridges/relays/lib-substrate-relay/src/messages/metrics.rs
+++ b/bridges/relays/lib-substrate-relay/src/messages/metrics.rs
@@ -18,11 +18,11 @@
 
 use crate::TaggedAccount;
 
-use bp_messages::LaneId;
 use bp_relayers::{RewardsAccountOwner, RewardsAccountParams};
 use bp_runtime::StorageDoubleMapKeyProvider;
-use codec::Decode;
+use codec::{Decode, EncodeLike};
 use frame_system::AccountInfo;
+use messages_relay::Labeled;
 use pallet_balances::AccountData;
 use relay_substrate_client::{
 	metrics::{FloatStorageValue, FloatStorageValueMetric},
@@ -35,7 +35,7 @@ use sp_runtime::{FixedPointNumber, FixedU128};
 use std::{fmt::Debug, marker::PhantomData};
 
 /// Add relay accounts balance metrics.
-pub async fn add_relay_balances_metrics<C: ChainWithBalances, BC: ChainWithMessages>(
+pub async fn add_relay_balances_metrics<C: ChainWithBalances, BC: ChainWithMessages, LaneId>(
 	client: impl Client<C>,
 	metrics: &MetricsParams,
 	relay_accounts: &Vec<TaggedAccount<AccountIdOf<C>>>,
@@ -43,6 +43,7 @@ pub async fn add_relay_balances_metrics<C: ChainWithBalances, BC: ChainWithMessa
 ) -> anyhow::Result<()>
 where
 	BalanceOf<C>: Into<u128> + std::fmt::Debug,
+	LaneId: Clone + Copy + Decode + EncodeLike + Send + Sync + Labeled,
 {
 	if relay_accounts.is_empty() {
 		return Ok(())
@@ -86,25 +87,25 @@ where
 				FloatStorageValueMetric::new(
 					AccountBalance::<C> { token_decimals, _phantom: Default::default() },
 					client.clone(),
-					bp_relayers::RelayerRewardsKeyProvider::<AccountIdOf<C>, BalanceOf<C>>::final_key(
+					bp_relayers::RelayerRewardsKeyProvider::<AccountIdOf<C>, BalanceOf<C>, LaneId>::final_key(
 						relayers_pallet_name,
 						account.id(),
 						&RewardsAccountParams::new(*lane, BC::ID, RewardsAccountOwner::ThisChain),
 					),
-					format!("at_{}_relay_{}_reward_for_msgs_from_{}_on_lane_{}", C::NAME, account.tag(), BC::NAME, hex::encode(lane.as_ref())),
-					format!("Reward of the {} relay account at {} for delivering messages from {} on lane {:?}", account.tag(), C::NAME, BC::NAME, lane),
+					format!("at_{}_relay_{}_reward_for_msgs_from_{}_on_lane_{}", C::NAME, account.tag(), BC::NAME, lane.label()),
+					format!("Reward of the {} relay account at {} for delivering messages from {} on lane {:?}", account.tag(), C::NAME, BC::NAME, lane.label()),
 				)?.register_and_spawn(&metrics.registry)?;
 
 				FloatStorageValueMetric::new(
 					AccountBalance::<C> { token_decimals, _phantom: Default::default() },
 					client.clone(),
-					bp_relayers::RelayerRewardsKeyProvider::<AccountIdOf<C>, BalanceOf<C>>::final_key(
+					bp_relayers::RelayerRewardsKeyProvider::<AccountIdOf<C>, BalanceOf<C>, LaneId>::final_key(
 						relayers_pallet_name,
 						account.id(),
 						&RewardsAccountParams::new(*lane, BC::ID, RewardsAccountOwner::BridgedChain),
 					),
-					format!("at_{}_relay_{}_reward_for_msgs_to_{}_on_lane_{}", C::NAME, account.tag(), BC::NAME, hex::encode(lane.as_ref())),
-					format!("Reward of the {} relay account at {} for delivering messages confirmations from {} on lane {:?}", account.tag(), C::NAME, BC::NAME, lane),
+					format!("at_{}_relay_{}_reward_for_msgs_to_{}_on_lane_{}", C::NAME, account.tag(), BC::NAME, lane.label()),
+					format!("Reward of the {} relay account at {} for delivering messages confirmations from {} on lane {:?}", account.tag(), C::NAME, BC::NAME, lane.label()),
 				)?.register_and_spawn(&metrics.registry)?;
 			}
 		}
diff --git a/bridges/relays/lib-substrate-relay/src/messages/mod.rs b/bridges/relays/lib-substrate-relay/src/messages/mod.rs
index 28bc5c7f5e8e60875ac5e294225a0b9285c5f7ae..f7031648bc35d3b0dc249ae25888409eb368d0d9 100644
--- a/bridges/relays/lib-substrate-relay/src/messages/mod.rs
+++ b/bridges/relays/lib-substrate-relay/src/messages/mod.rs
@@ -27,19 +27,17 @@ use crate::{
 
 use async_std::sync::Arc;
 use bp_messages::{
-	target_chain::FromBridgedChainMessagesProof, ChainWithMessages as _, LaneId, MessageNonce,
+	target_chain::FromBridgedChainMessagesProof, ChainWithMessages as _, MessageNonce,
 };
-use bp_runtime::{
-	AccountIdOf, Chain as _, EncodedOrDecodedCall, HeaderIdOf, TransactionEra, WeightExtraOps,
-};
-use codec::Encode;
+use bp_runtime::{AccountIdOf, EncodedOrDecodedCall, HeaderIdOf, TransactionEra, WeightExtraOps};
+use codec::{Codec, Encode, EncodeLike};
 use frame_support::{dispatch::GetDispatchInfo, weights::Weight};
-use messages_relay::{message_lane::MessageLane, message_lane_loop::BatchTransaction};
+use messages_relay::{message_lane::MessageLane, message_lane_loop::BatchTransaction, Labeled};
 use pallet_bridge_messages::{Call as BridgeMessagesCall, Config as BridgeMessagesConfig};
 use relay_substrate_client::{
 	transaction_stall_timeout, AccountKeyPairOf, BalanceOf, BlockNumberOf, CallOf, Chain,
-	ChainWithMessages, ChainWithTransactions, Client, Error as SubstrateError, HashOf, SignParam,
-	UnsignedTransaction,
+	ChainBase, ChainWithMessages, ChainWithTransactions, Client, Error as SubstrateError, HashOf,
+	SignParam, UnsignedTransaction,
 };
 use relay_utils::{
 	metrics::{GlobalMetrics, MetricsParams, StandaloneMetric},
@@ -60,6 +58,18 @@ pub trait SubstrateMessageLane: 'static + Clone + Debug + Send + Sync {
 	/// Messages from the `SourceChain` are dispatched on this chain.
 	type TargetChain: ChainWithMessages + ChainWithTransactions;
 
+	/// Lane identifier type.
+	type LaneId: Clone
+		+ Copy
+		+ Debug
+		+ Codec
+		+ EncodeLike
+		+ Send
+		+ Sync
+		+ Labeled
+		+ TryFrom<Vec<u8>>
+		+ Default;
+
 	/// How receive messages proof call is built?
 	type ReceiveMessagesProofCallBuilder: ReceiveMessagesProofCallBuilder<Self>;
 	/// How receive messages delivery proof call is built?
@@ -81,8 +91,10 @@ impl<P: SubstrateMessageLane> MessageLane for MessageLaneAdapter<P> {
 	const SOURCE_NAME: &'static str = P::SourceChain::NAME;
 	const TARGET_NAME: &'static str = P::TargetChain::NAME;
 
-	type MessagesProof = SubstrateMessagesProof<P::SourceChain>;
-	type MessagesReceivingProof = SubstrateMessagesDeliveryProof<P::TargetChain>;
+	type LaneId = P::LaneId;
+
+	type MessagesProof = SubstrateMessagesProof<P::SourceChain, P::LaneId>;
+	type MessagesReceivingProof = SubstrateMessagesDeliveryProof<P::TargetChain, P::LaneId>;
 
 	type SourceChainBalance = BalanceOf<P::SourceChain>;
 	type SourceHeaderNumber = BlockNumberOf<P::SourceChain>;
@@ -109,7 +121,7 @@ pub struct MessagesRelayParams<P: SubstrateMessageLane, SourceClnt, TargetClnt>
 	pub target_to_source_headers_relay:
 		Option<Arc<dyn OnDemandRelay<P::TargetChain, P::SourceChain>>>,
 	/// Identifier of lane that needs to be served.
-	pub lane_id: LaneId,
+	pub lane_id: P::LaneId,
 	/// Messages relay limits. If not provided, the relay tries to determine it automatically,
 	/// using `TransactionPayment` pallet runtime API.
 	pub limits: Option<MessagesRelayLimits>,
@@ -293,7 +305,7 @@ pub async fn relay_messages_range<P: SubstrateMessageLane>(
 	source_transaction_params: TransactionParams<AccountKeyPairOf<P::SourceChain>>,
 	target_transaction_params: TransactionParams<AccountKeyPairOf<P::TargetChain>>,
 	at_source_block: HeaderIdOf<P::SourceChain>,
-	lane_id: LaneId,
+	lane_id: P::LaneId,
 	range: RangeInclusive<MessageNonce>,
 	outbound_state_proof_required: bool,
 ) -> anyhow::Result<()>
@@ -335,7 +347,7 @@ pub async fn relay_messages_delivery_confirmation<P: SubstrateMessageLane>(
 	target_client: impl Client<P::TargetChain>,
 	source_transaction_params: TransactionParams<AccountKeyPairOf<P::SourceChain>>,
 	at_target_block: HeaderIdOf<P::TargetChain>,
-	lane_id: LaneId,
+	lane_id: P::LaneId,
 ) -> anyhow::Result<()>
 where
 	AccountIdOf<P::SourceChain>: From<<AccountKeyPairOf<P::SourceChain> as Pair>::Public>,
@@ -372,7 +384,7 @@ pub trait ReceiveMessagesProofCallBuilder<P: SubstrateMessageLane> {
 	/// messages module at the target chain.
 	fn build_receive_messages_proof_call(
 		relayer_id_at_source: AccountIdOf<P::SourceChain>,
-		proof: SubstrateMessagesProof<P::SourceChain>,
+		proof: SubstrateMessagesProof<P::SourceChain, P::LaneId>,
 		messages_count: u32,
 		dispatch_weight: Weight,
 		trace_call: bool,
@@ -388,7 +400,7 @@ pub struct DirectReceiveMessagesProofCallBuilder<P, R, I> {
 impl<P, R, I> ReceiveMessagesProofCallBuilder<P> for DirectReceiveMessagesProofCallBuilder<P, R, I>
 where
 	P: SubstrateMessageLane,
-	R: BridgeMessagesConfig<I>,
+	R: BridgeMessagesConfig<I, LaneId = P::LaneId>,
 	I: 'static,
 	R::BridgedChain:
 		bp_runtime::Chain<AccountId = AccountIdOf<P::SourceChain>, Hash = HashOf<P::SourceChain>>,
@@ -396,7 +408,7 @@ where
 {
 	fn build_receive_messages_proof_call(
 		relayer_id_at_source: AccountIdOf<P::SourceChain>,
-		proof: SubstrateMessagesProof<P::SourceChain>,
+		proof: SubstrateMessagesProof<P::SourceChain, P::LaneId>,
 		messages_count: u32,
 		dispatch_weight: Weight,
 		trace_call: bool,
@@ -444,7 +456,8 @@ macro_rules! generate_receive_message_proof_call_builder {
 					<$pipeline as $crate::messages::SubstrateMessageLane>::SourceChain
 				>,
 				proof: $crate::messages::source::SubstrateMessagesProof<
-					<$pipeline as $crate::messages::SubstrateMessageLane>::SourceChain
+					<$pipeline as $crate::messages::SubstrateMessageLane>::SourceChain,
+					<$pipeline as $crate::messages::SubstrateMessageLane>::LaneId
 				>,
 				messages_count: u32,
 				dispatch_weight: bp_messages::Weight,
@@ -470,7 +483,7 @@ pub trait ReceiveMessagesDeliveryProofCallBuilder<P: SubstrateMessageLane> {
 	/// Given messages delivery proof, build call of `receive_messages_delivery_proof` function of
 	/// bridge messages module at the source chain.
 	fn build_receive_messages_delivery_proof_call(
-		proof: SubstrateMessagesDeliveryProof<P::TargetChain>,
+		proof: SubstrateMessagesDeliveryProof<P::TargetChain, P::LaneId>,
 		trace_call: bool,
 	) -> CallOf<P::SourceChain>;
 }
@@ -485,13 +498,13 @@ impl<P, R, I> ReceiveMessagesDeliveryProofCallBuilder<P>
 	for DirectReceiveMessagesDeliveryProofCallBuilder<P, R, I>
 where
 	P: SubstrateMessageLane,
-	R: BridgeMessagesConfig<I>,
+	R: BridgeMessagesConfig<I, LaneId = P::LaneId>,
 	I: 'static,
 	R::BridgedChain: bp_runtime::Chain<Hash = HashOf<P::TargetChain>>,
 	CallOf<P::SourceChain>: From<BridgeMessagesCall<R, I>> + GetDispatchInfo,
 {
 	fn build_receive_messages_delivery_proof_call(
-		proof: SubstrateMessagesDeliveryProof<P::TargetChain>,
+		proof: SubstrateMessagesDeliveryProof<P::TargetChain, P::LaneId>,
 		trace_call: bool,
 	) -> CallOf<P::SourceChain> {
 		let call: CallOf<P::SourceChain> =
@@ -533,7 +546,8 @@ macro_rules! generate_receive_message_delivery_proof_call_builder {
 		{
 			fn build_receive_messages_delivery_proof_call(
 				proof: $crate::messages::target::SubstrateMessagesDeliveryProof<
-					<$pipeline as $crate::messages::SubstrateMessageLane>::TargetChain
+					<$pipeline as $crate::messages::SubstrateMessageLane>::TargetChain,
+					<$pipeline as $crate::messages::SubstrateMessageLane>::LaneId
 				>,
 				_trace_call: bool,
 			) -> relay_substrate_client::CallOf<
@@ -644,7 +658,7 @@ where
 				FromBridgedChainMessagesProof {
 					bridged_header_hash: Default::default(),
 					storage_proof: Default::default(),
-					lane: LaneId::new(1, 2),
+					lane: P::LaneId::default(),
 					nonces_start: 1,
 					nonces_end: messages as u64,
 				},
@@ -674,7 +688,7 @@ where
 mod tests {
 	use super::*;
 	use bp_messages::{
-		source_chain::FromBridgedChainMessagesDeliveryProof, UnrewardedRelayersState,
+		source_chain::FromBridgedChainMessagesDeliveryProof, LaneIdType, UnrewardedRelayersState,
 	};
 	use relay_substrate_client::calls::{UtilityCall as MockUtilityCall, UtilityCall};
 
@@ -687,8 +701,8 @@ mod tests {
 	}
 	pub type CodegenBridgeMessagesCall = bp_messages::BridgeMessagesCall<
 		u64,
-		Box<FromBridgedChainMessagesProof<mock::BridgedHeaderHash>>,
-		FromBridgedChainMessagesDeliveryProof<mock::BridgedHeaderHash>,
+		Box<FromBridgedChainMessagesProof<mock::BridgedHeaderHash, mock::TestLaneIdType>>,
+		FromBridgedChainMessagesDeliveryProof<mock::BridgedHeaderHash, mock::TestLaneIdType>,
 	>;
 
 	impl From<MockUtilityCall<RuntimeCall>> for RuntimeCall {
@@ -706,7 +720,7 @@ mod tests {
 		let receive_messages_proof = FromBridgedChainMessagesProof {
 			bridged_header_hash: Default::default(),
 			storage_proof: Default::default(),
-			lane: LaneId::new(1, 2),
+			lane: mock::TestLaneIdType::try_new(1, 2).unwrap(),
 			nonces_start: 0,
 			nonces_end: 0,
 		};
@@ -761,7 +775,7 @@ mod tests {
 		let receive_messages_delivery_proof = FromBridgedChainMessagesDeliveryProof {
 			bridged_header_hash: Default::default(),
 			storage_proof: Default::default(),
-			lane: LaneId::new(1, 2),
+			lane: mock::TestLaneIdType::try_new(1, 2).unwrap(),
 		};
 		let relayers_state = UnrewardedRelayersState {
 			unrewarded_relayer_entries: 0,
@@ -808,7 +822,7 @@ mod tests {
 	// mock runtime with `pallet_bridge_messages`
 	mod mock {
 		use super::super::*;
-		use bp_messages::target_chain::ForbidInboundMessages;
+		use bp_messages::{target_chain::ForbidInboundMessages, HashedLaneId};
 		use bp_runtime::ChainId;
 		use frame_support::derive_impl;
 		use sp_core::H256;
@@ -819,6 +833,9 @@ mod tests {
 		type Block = frame_system::mocking::MockBlock<TestRuntime>;
 		pub type SignedBlock = generic::SignedBlock<Block>;
 
+		/// Lane identifier type used for tests.
+		pub type TestLaneIdType = HashedLaneId;
+
 		frame_support::construct_runtime! {
 			pub enum TestRuntime
 			{
@@ -840,10 +857,11 @@ mod tests {
 			type BridgedHeaderChain = BridgedHeaderChain;
 			type OutboundPayload = Vec<u8>;
 			type InboundPayload = Vec<u8>;
+			type LaneId = TestLaneIdType;
 			type DeliveryPayments = ();
 			type DeliveryConfirmationPayments = ();
 			type OnMessagesDelivered = ();
-			type MessageDispatch = ForbidInboundMessages<Vec<u8>>;
+			type MessageDispatch = ForbidInboundMessages<Vec<u8>, Self::LaneId>;
 		}
 
 		pub struct ThisUnderlyingChain;
@@ -1005,6 +1023,7 @@ mod tests {
 		impl SubstrateMessageLane for ThisChainToBridgedChainMessageLane {
 			type SourceChain = ThisChain;
 			type TargetChain = BridgedChain;
+			type LaneId = mock::TestLaneIdType;
 			type ReceiveMessagesProofCallBuilder =
 				ThisChainToBridgedChainMessageLaneReceiveMessagesProofCallBuilder;
 			type ReceiveMessagesDeliveryProofCallBuilder =
diff --git a/bridges/relays/lib-substrate-relay/src/messages/source.rs b/bridges/relays/lib-substrate-relay/src/messages/source.rs
index 2c49df3452abbaa60f40c313b0860729570216fb..b560867a235b015f5546df6be842db50bced4f36 100644
--- a/bridges/relays/lib-substrate-relay/src/messages/source.rs
+++ b/bridges/relays/lib-substrate-relay/src/messages/source.rs
@@ -34,7 +34,7 @@ use async_trait::async_trait;
 use bp_messages::{
 	storage_keys::{operating_mode_key, outbound_lane_data_key},
 	target_chain::FromBridgedChainMessagesProof,
-	ChainWithMessages as _, InboundMessageDetails, LaneId, MessageNonce, MessagePayload,
+	ChainWithMessages as _, InboundMessageDetails, MessageNonce, MessagePayload,
 	MessagesOperatingMode, OutboundLaneData, OutboundMessageDetails,
 };
 use bp_runtime::{BasicOperatingMode, HeaderIdProvider, RangeInclusiveExt};
@@ -60,14 +60,14 @@ use std::ops::RangeInclusive;
 /// Intermediate message proof returned by the source Substrate node. Includes everything
 /// required to submit to the target node: cumulative dispatch weight of bundled messages and
 /// the proof itself.
-pub type SubstrateMessagesProof<C> = (Weight, FromBridgedChainMessagesProof<HashOf<C>>);
+pub type SubstrateMessagesProof<C, L> = (Weight, FromBridgedChainMessagesProof<HashOf<C>, L>);
 type MessagesToRefine<'a> = Vec<(MessagePayload, &'a mut OutboundMessageDetails)>;
 
 /// Substrate client as Substrate messages source.
 pub struct SubstrateMessagesSource<P: SubstrateMessageLane, SourceClnt, TargetClnt> {
 	source_client: SourceClnt,
 	target_client: TargetClnt,
-	lane_id: LaneId,
+	lane_id: P::LaneId,
 	transaction_params: TransactionParams<AccountKeyPairOf<P::SourceChain>>,
 	target_to_source_headers_relay: Option<Arc<dyn OnDemandRelay<P::TargetChain, P::SourceChain>>>,
 }
@@ -79,7 +79,7 @@ impl<P: SubstrateMessageLane, SourceClnt: Client<P::SourceChain>, TargetClnt>
 	pub fn new(
 		source_client: SourceClnt,
 		target_client: TargetClnt,
-		lane_id: LaneId,
+		lane_id: P::LaneId,
 		transaction_params: TransactionParams<AccountKeyPairOf<P::SourceChain>>,
 		target_to_source_headers_relay: Option<
 			Arc<dyn OnDemandRelay<P::TargetChain, P::SourceChain>>,
@@ -256,8 +256,11 @@ where
 		}
 
 		let best_target_header_hash = self.target_client.best_header_hash().await?;
-		for mut msgs_to_refine_batch in
-			split_msgs_to_refine::<P::SourceChain, P::TargetChain>(self.lane_id, msgs_to_refine)?
+		for mut msgs_to_refine_batch in split_msgs_to_refine::<
+			P::SourceChain,
+			P::TargetChain,
+			P::LaneId,
+		>(self.lane_id, msgs_to_refine)?
 		{
 			let in_msgs_details = self
 				.target_client
@@ -542,7 +545,7 @@ fn validate_out_msgs_details<C: Chain>(
 	Ok(())
 }
 
-fn split_msgs_to_refine<Source: Chain + ChainWithMessages, Target: Chain>(
+fn split_msgs_to_refine<Source: Chain + ChainWithMessages, Target: Chain, LaneId: Encode + Copy>(
 	lane_id: LaneId,
 	msgs_to_refine: MessagesToRefine,
 ) -> Result<Vec<MessagesToRefine>, SubstrateError> {
@@ -578,8 +581,12 @@ fn split_msgs_to_refine<Source: Chain + ChainWithMessages, Target: Chain>(
 #[cfg(test)]
 mod tests {
 	use super::*;
+	use bp_messages::{HashedLaneId, LaneIdType};
 	use relay_substrate_client::test_chain::TestChain;
 
+	/// Lane identifier type used for tests.
+	type TestLaneIdType = HashedLaneId;
+
 	fn message_details_from_rpc(
 		nonces: RangeInclusive<MessageNonce>,
 	) -> Vec<OutboundMessageDetails> {
@@ -660,8 +667,10 @@ mod tests {
 			msgs_to_refine.push((payload, out_msg_details));
 		}
 
-		let maybe_batches =
-			split_msgs_to_refine::<TestChain, TestChain>(LaneId::new(1, 2), msgs_to_refine);
+		let maybe_batches = split_msgs_to_refine::<TestChain, TestChain, TestLaneIdType>(
+			TestLaneIdType::try_new(1, 2).unwrap(),
+			msgs_to_refine,
+		);
 		match expected_batches {
 			Ok(expected_batches) => {
 				let batches = maybe_batches.unwrap();
diff --git a/bridges/relays/lib-substrate-relay/src/messages/target.rs b/bridges/relays/lib-substrate-relay/src/messages/target.rs
index a6bf169cffb67ae149d9d4c8c5a2348dc18f5b39..0d1aac88a32aee638f3d44225f25972aaad4d011 100644
--- a/bridges/relays/lib-substrate-relay/src/messages/target.rs
+++ b/bridges/relays/lib-substrate-relay/src/messages/target.rs
@@ -36,7 +36,7 @@ use async_std::sync::Arc;
 use async_trait::async_trait;
 use bp_messages::{
 	source_chain::FromBridgedChainMessagesDeliveryProof, storage_keys::inbound_lane_data_key,
-	ChainWithMessages as _, InboundLaneData, LaneId, MessageNonce, UnrewardedRelayersState,
+	ChainWithMessages as _, InboundLaneData, MessageNonce, UnrewardedRelayersState,
 };
 use messages_relay::{
 	message_lane::{MessageLane, SourceHeaderIdOf, TargetHeaderIdOf},
@@ -51,14 +51,14 @@ use sp_core::Pair;
 use std::{convert::TryFrom, ops::RangeInclusive};
 
 /// Message receiving proof returned by the target Substrate node.
-pub type SubstrateMessagesDeliveryProof<C> =
-	(UnrewardedRelayersState, FromBridgedChainMessagesDeliveryProof<HashOf<C>>);
+pub type SubstrateMessagesDeliveryProof<C, L> =
+	(UnrewardedRelayersState, FromBridgedChainMessagesDeliveryProof<HashOf<C>, L>);
 
 /// Substrate client as Substrate messages target.
 pub struct SubstrateMessagesTarget<P: SubstrateMessageLane, SourceClnt, TargetClnt> {
 	target_client: TargetClnt,
 	source_client: SourceClnt,
-	lane_id: LaneId,
+	lane_id: P::LaneId,
 	relayer_id_at_source: AccountIdOf<P::SourceChain>,
 	transaction_params: Option<TransactionParams<AccountKeyPairOf<P::TargetChain>>>,
 	source_to_target_headers_relay: Option<Arc<dyn OnDemandRelay<P::SourceChain, P::TargetChain>>>,
@@ -73,7 +73,7 @@ where
 	pub fn new(
 		target_client: TargetClnt,
 		source_client: SourceClnt,
-		lane_id: LaneId,
+		lane_id: P::LaneId,
 		relayer_id_at_source: AccountIdOf<P::SourceChain>,
 		transaction_params: Option<TransactionParams<AccountKeyPairOf<P::TargetChain>>>,
 		source_to_target_headers_relay: Option<
@@ -308,7 +308,7 @@ where
 fn make_messages_delivery_call<P: SubstrateMessageLane>(
 	relayer_id_at_source: AccountIdOf<P::SourceChain>,
 	nonces: RangeInclusive<MessageNonce>,
-	proof: SubstrateMessagesProof<P::SourceChain>,
+	proof: SubstrateMessagesProof<P::SourceChain, P::LaneId>,
 	trace_call: bool,
 ) -> CallOf<P::TargetChain> {
 	let messages_count = nonces.end() - nonces.start() + 1;
diff --git a/bridges/relays/messages/Cargo.toml b/bridges/relays/messages/Cargo.toml
index c7a132bb3bae7ebc34728de8c94c41fb39c89751..f9df73507c753564fb85a88ba7382b00b4a98e57 100644
--- a/bridges/relays/messages/Cargo.toml
+++ b/bridges/relays/messages/Cargo.toml
@@ -26,3 +26,6 @@ finality-relay = { workspace = true }
 relay-utils = { workspace = true }
 
 sp-arithmetic = { workspace = true, default-features = true }
+
+[dev-dependencies]
+sp-core = { workspace = true }
diff --git a/bridges/relays/messages/src/lib.rs b/bridges/relays/messages/src/lib.rs
index 78a3237ba4fe03851412b86e08867fcba07e8451..f5e09f4d4684fbea2aea838abb0dbb1c57d4327e 100644
--- a/bridges/relays/messages/src/lib.rs
+++ b/bridges/relays/messages/src/lib.rs
@@ -38,3 +38,4 @@ mod message_race_strategy;
 
 pub use message_race_delivery::relay_messages_range;
 pub use message_race_receiving::relay_messages_delivery_confirmation;
+pub use metrics::Labeled;
diff --git a/bridges/relays/messages/src/message_lane.rs b/bridges/relays/messages/src/message_lane.rs
index 5c9728ad93abd5aa1ea9b2fc77b2a6f9968539f6..84c1e57ba4eb8ef5ec5bff12a9a338c7ab4f2cbe 100644
--- a/bridges/relays/messages/src/message_lane.rs
+++ b/bridges/relays/messages/src/message_lane.rs
@@ -19,6 +19,7 @@
 //! 1) relay new messages from source to target node;
 //! 2) relay proof-of-delivery from target to source node.
 
+use crate::metrics::Labeled;
 use num_traits::{SaturatingAdd, Zero};
 use relay_utils::{BlockNumberBase, HeaderId};
 use sp_arithmetic::traits::AtLeast32BitUnsigned;
@@ -31,6 +32,9 @@ pub trait MessageLane: 'static + Clone + Send + Sync {
 	/// Name of the messages target.
 	const TARGET_NAME: &'static str;
 
+	/// Lane identifier type.
+	type LaneId: Clone + Send + Sync + Labeled;
+
 	/// Messages proof.
 	type MessagesProof: Clone + Debug + Send + Sync;
 	/// Messages receiving proof.
diff --git a/bridges/relays/messages/src/message_lane_loop.rs b/bridges/relays/messages/src/message_lane_loop.rs
index 995499092c3ee2f51fdac54340767e39d8d218d9..36de637f04c437450e9054dd5088c3da5eb329cd 100644
--- a/bridges/relays/messages/src/message_lane_loop.rs
+++ b/bridges/relays/messages/src/message_lane_loop.rs
@@ -29,7 +29,7 @@ use std::{collections::BTreeMap, fmt::Debug, future::Future, ops::RangeInclusive
 use async_trait::async_trait;
 use futures::{channel::mpsc::unbounded, future::FutureExt, stream::StreamExt};
 
-use bp_messages::{LaneId, MessageNonce, UnrewardedRelayersState, Weight};
+use bp_messages::{MessageNonce, UnrewardedRelayersState, Weight};
 use relay_utils::{
 	interval, metrics::MetricsParams, process_future_result, relay_loop::Client as RelayClient,
 	retry_backoff, FailedClient, TransactionTracker,
@@ -39,12 +39,12 @@ use crate::{
 	message_lane::{MessageLane, SourceHeaderIdOf, TargetHeaderIdOf},
 	message_race_delivery::run as run_message_delivery_race,
 	message_race_receiving::run as run_message_receiving_race,
-	metrics::MessageLaneLoopMetrics,
+	metrics::{Labeled, MessageLaneLoopMetrics},
 };
 
 /// Message lane loop configuration params.
 #[derive(Debug, Clone)]
-pub struct Params {
+pub struct Params<LaneId> {
 	/// Id of lane this loop is servicing.
 	pub lane: LaneId,
 	/// Interval at which we ask target node about its updates.
@@ -275,13 +275,13 @@ pub struct ClientsState<P: MessageLane> {
 
 /// Return prefix that will be used by default to expose Prometheus metrics of the finality proofs
 /// sync loop.
-pub fn metrics_prefix<P: MessageLane>(lane: &LaneId) -> String {
-	format!("{}_to_{}_MessageLane_{:?}", P::SOURCE_NAME, P::TARGET_NAME, lane)
+pub fn metrics_prefix<P: MessageLane>(lane: &P::LaneId) -> String {
+	format!("{}_to_{}_MessageLane_{}", P::SOURCE_NAME, P::TARGET_NAME, lane.label())
 }
 
 /// Run message lane service loop.
 pub async fn run<P: MessageLane>(
-	params: Params,
+	params: Params<P::LaneId>,
 	source_client: impl SourceClient<P>,
 	target_client: impl TargetClient<P>,
 	metrics_params: MetricsParams,
@@ -309,7 +309,7 @@ pub async fn run<P: MessageLane>(
 /// Run one-way message delivery loop until connection with target or source node is lost, or exit
 /// signal is received.
 async fn run_until_connection_lost<P: MessageLane, SC: SourceClient<P>, TC: TargetClient<P>>(
-	params: Params,
+	params: Params<P::LaneId>,
 	source_client: SC,
 	target_client: TC,
 	metrics_msg: Option<MessageLaneLoopMetrics>,
@@ -471,9 +471,9 @@ async fn run_until_connection_lost<P: MessageLane, SC: SourceClient<P>, TC: Targ
 pub(crate) mod tests {
 	use std::sync::Arc;
 
+	use bp_messages::{HashedLaneId, LaneIdType, LegacyLaneId};
 	use futures::stream::StreamExt;
 	use parking_lot::Mutex;
-
 	use relay_utils::{HeaderId, MaybeConnectionError, TrackedTransactionStatus};
 
 	use super::*;
@@ -504,6 +504,9 @@ pub(crate) mod tests {
 		}
 	}
 
+	/// Lane identifier type used for tests.
+	pub type TestLaneIdType = HashedLaneId;
+
 	#[derive(Clone)]
 	pub struct TestMessageLane;
 
@@ -520,6 +523,8 @@ pub(crate) mod tests {
 
 		type TargetHeaderNumber = TestTargetHeaderNumber;
 		type TargetHeaderHash = TestTargetHeaderHash;
+
+		type LaneId = TestLaneIdType;
 	}
 
 	#[derive(Clone, Debug)]
@@ -957,7 +962,7 @@ pub(crate) mod tests {
 			};
 			let _ = run(
 				Params {
-					lane: LaneId::new(1, 2),
+					lane: TestLaneIdType::try_new(1, 2).unwrap(),
 					source_tick: Duration::from_millis(100),
 					target_tick: Duration::from_millis(100),
 					reconnect_delay: Duration::from_millis(0),
@@ -1278,7 +1283,31 @@ pub(crate) mod tests {
 	#[test]
 	fn metrics_prefix_is_valid() {
 		assert!(MessageLaneLoopMetrics::new(Some(&metrics_prefix::<TestMessageLane>(
-			&LaneId::new(1, 2)
+			&HashedLaneId::try_new(1, 2).unwrap()
+		)))
+		.is_ok());
+
+		// with LegacyLaneId
+		#[derive(Clone)]
+		pub struct LegacyTestMessageLane;
+		impl MessageLane for LegacyTestMessageLane {
+			const SOURCE_NAME: &'static str = "LegacyTestSource";
+			const TARGET_NAME: &'static str = "LegacyTestTarget";
+
+			type MessagesProof = TestMessagesProof;
+			type MessagesReceivingProof = TestMessagesReceivingProof;
+
+			type SourceChainBalance = TestSourceChainBalance;
+			type SourceHeaderNumber = TestSourceHeaderNumber;
+			type SourceHeaderHash = TestSourceHeaderHash;
+
+			type TargetHeaderNumber = TestTargetHeaderNumber;
+			type TargetHeaderHash = TestTargetHeaderHash;
+
+			type LaneId = LegacyLaneId;
+		}
+		assert!(MessageLaneLoopMetrics::new(Some(&metrics_prefix::<LegacyTestMessageLane>(
+			&LegacyLaneId([0, 0, 0, 1])
 		)))
 		.is_ok());
 	}
diff --git a/bridges/relays/messages/src/metrics.rs b/bridges/relays/messages/src/metrics.rs
index 69d80d178de809211b1874965adcf02fd76a66b8..2ca10e56d74aa603794f471308ba4dfb489309df 100644
--- a/bridges/relays/messages/src/metrics.rs
+++ b/bridges/relays/messages/src/metrics.rs
@@ -21,7 +21,7 @@ use crate::{
 	message_lane_loop::{SourceClientState, TargetClientState},
 };
 
-use bp_messages::MessageNonce;
+use bp_messages::{HashedLaneId, LegacyLaneId, MessageNonce};
 use finality_relay::SyncLoopMetrics;
 use relay_utils::metrics::{
 	metric_name, register, GaugeVec, Metric, Opts, PrometheusError, Registry, U64,
@@ -146,3 +146,32 @@ impl Metric for MessageLaneLoopMetrics {
 		Ok(())
 	}
 }
+
+/// Provides a label for metrics.
+pub trait Labeled {
+	/// Returns a label.
+	fn label(&self) -> String;
+}
+
+/// `Labeled` implementation for `LegacyLaneId`.
+impl Labeled for LegacyLaneId {
+	fn label(&self) -> String {
+		hex::encode(self.0)
+	}
+}
+
+/// `Labeled` implementation for `HashedLaneId`.
+impl Labeled for HashedLaneId {
+	fn label(&self) -> String {
+		format!("{:?}", self.inner())
+	}
+}
+
+#[test]
+fn lane_to_label_works() {
+	assert_eq!(
+		"0x0101010101010101010101010101010101010101010101010101010101010101",
+		HashedLaneId::from_inner(sp_core::H256::from([1u8; 32])).label(),
+	);
+	assert_eq!("00000001", LegacyLaneId([0, 0, 0, 1]).label());
+}
diff --git a/bridges/testing/README.md b/bridges/testing/README.md
index 158dfd73b1ad8d6b67891b661bc215c290017cd0..89a07c421e3e279b04ef3813d35a9db72cdc3576 100644
--- a/bridges/testing/README.md
+++ b/bridges/testing/README.md
@@ -22,7 +22,7 @@ Prerequisites for running the tests locally:
 - copy the `substrate-relay` binary, built in the previous step, to `~/local_bridge_testing/bin/substrate-relay`;
 
 After that, any test can be run using the `run-test.sh` command.
-Example: `./run-new-test.sh 0001-asset-transfer`
+Example: `./run-test.sh 0001-asset-transfer`
 
 Hopefully, it'll show the
 "All tests have completed successfully" message in the end. Otherwise, it'll print paths to zombienet
diff --git a/bridges/testing/environments/rococo-westend/bridges_rococo_westend.sh b/bridges/testing/environments/rococo-westend/bridges_rococo_westend.sh
index 54633449134b4ce85ff124d034cd6f58061409a1..e7848fe7163c7419d01e76b1cd5c60f271b6ae98 100755
--- a/bridges/testing/environments/rococo-westend/bridges_rococo_westend.sh
+++ b/bridges/testing/environments/rococo-westend/bridges_rococo_westend.sh
@@ -53,66 +53,66 @@ ASSET_HUB_ROCOCO_SOVEREIGN_ACCOUNT_AT_BRIDGE_HUB_ROCOCO="5Eg2fntNprdN3FgH4sfEaaZ
 # Expected sovereign accounts for rewards on BridgeHubs.
 #
 # Generated by:
-#	#[test]
-#	fn generate_sovereign_accounts_for_rewards() {
-#		use bp_messages::LaneId;
-#		use bp_relayers::{PayRewardFromAccount, RewardsAccountOwner, RewardsAccountParams};
-#		use sp_core::crypto::Ss58Codec;
+##[test]
+#fn generate_sovereign_accounts_for_rewards() {
+#	use bp_messages::LegacyLaneId;
+#	use bp_relayers::{PayRewardFromAccount, RewardsAccountOwner, RewardsAccountParams};
+#	use sp_core::crypto::Ss58Codec;
 #
-#		// SS58=42
-#		println!(
-#			"ON_BRIDGE_HUB_ROCOCO_SOVEREIGN_ACCOUNT_FOR_LANE_00000002_bhwd_ThisChain=\"{}\"",
-#			frame_support::sp_runtime::AccountId32::new(
-#				PayRewardFromAccount::<[u8; 32], [u8; 32]>::rewards_account(RewardsAccountParams::new(
-#					LaneId([0, 0, 0, 2]),
-#					*b"bhwd",
-#					RewardsAccountOwner::ThisChain
-#				))
-#			)
-#				.to_ss58check_with_version(42_u16.into())
-#		);
-#		// SS58=42
-#		println!(
-#			"ON_BRIDGE_HUB_ROCOCO_SOVEREIGN_ACCOUNT_FOR_LANE_00000002_bhwd_BridgedChain=\"{}\"",
-#			frame_support::sp_runtime::AccountId32::new(
-#				PayRewardFromAccount::<[u8; 32], [u8; 32]>::rewards_account(RewardsAccountParams::new(
-#					LaneId([0, 0, 0, 2]),
-#					*b"bhwd",
-#					RewardsAccountOwner::BridgedChain
-#				))
-#			)
-#				.to_ss58check_with_version(42_u16.into())
-#		);
+#	// SS58=42
+#	println!(
+#		"ON_BRIDGE_HUB_ROCOCO_SOVEREIGN_ACCOUNT_FOR_LANE_00000002_bhwd_ThisChain=\"{}\"",
+#		frame_support::sp_runtime::AccountId32::new(
+#			PayRewardFromAccount::<[u8; 32], [u8; 32], LegacyLaneId>::rewards_account(RewardsAccountParams::new(
+#				LegacyLaneId([0, 0, 0, 2]),
+#				*b"bhwd",
+#				RewardsAccountOwner::ThisChain
+#			))
+#		)
+#			.to_ss58check_with_version(42_u16.into())
+#	);
+#	// SS58=42
+#	println!(
+#		"ON_BRIDGE_HUB_ROCOCO_SOVEREIGN_ACCOUNT_FOR_LANE_00000002_bhwd_BridgedChain=\"{}\"",
+#		frame_support::sp_runtime::AccountId32::new(
+#			PayRewardFromAccount::<[u8; 32], [u8; 32], LegacyLaneId>::rewards_account(RewardsAccountParams::new(
+#				LegacyLaneId([0, 0, 0, 2]),
+#				*b"bhwd",
+#				RewardsAccountOwner::BridgedChain
+#			))
+#		)
+#			.to_ss58check_with_version(42_u16.into())
+#	);
 #
-#		// SS58=42
-#		println!(
-#			"ON_BRIDGE_HUB_WESTEND_SOVEREIGN_ACCOUNT_FOR_LANE_00000002_bhro_ThisChain=\"{}\"",
-#			frame_support::sp_runtime::AccountId32::new(
-#				PayRewardFromAccount::<[u8; 32], [u8; 32]>::rewards_account(RewardsAccountParams::new(
-#					LaneId([0, 0, 0, 2]),
-#					*b"bhro",
-#					RewardsAccountOwner::ThisChain
-#				))
-#			)
-#				.to_ss58check_with_version(42_u16.into())
-#		);
-#		// SS58=42
-#		println!(
-#			"ON_BRIDGE_HUB_WESTEND_SOVEREIGN_ACCOUNT_FOR_LANE_00000002_bhro_BridgedChain=\"{}\"",
-#			frame_support::sp_runtime::AccountId32::new(
-#				PayRewardFromAccount::<[u8; 32], [u8; 32]>::rewards_account(RewardsAccountParams::new(
-#					LaneId([0, 0, 0, 2]),
-#					*b"bhro",
-#					RewardsAccountOwner::BridgedChain
-#				))
-#			)
-#				.to_ss58check_with_version(42_u16.into())
-#		);
-#	}
-ON_BRIDGE_HUB_ROCOCO_SOVEREIGN_ACCOUNT_FOR_LANE_00000002_bhwd_ThisChain="5EHnXaT5BhiSGP5hbdsoVGtzi2sQVgpDNToTxLYeQvKoMPEm"
-ON_BRIDGE_HUB_ROCOCO_SOVEREIGN_ACCOUNT_FOR_LANE_00000002_bhwd_BridgedChain="5EHnXaT5BhiSGP5hbdt5EJSapXYbxEv678jyWHEUskCXcjqo"
-ON_BRIDGE_HUB_WESTEND_SOVEREIGN_ACCOUNT_FOR_LANE_00000002_bhro_ThisChain="5EHnXaT5BhiSGP5h9Rg8sgUJqoLym3iEaWUiboT8S9AT5xFh"
-ON_BRIDGE_HUB_WESTEND_SOVEREIGN_ACCOUNT_FOR_LANE_00000002_bhro_BridgedChain="5EHnXaT5BhiSGP5h9RgQci1txJ2BDbp7KBRE9k8xty3BMUSi"
+#	// SS58=42
+#	println!(
+#		"ON_BRIDGE_HUB_WESTEND_SOVEREIGN_ACCOUNT_FOR_LANE_00000002_bhro_ThisChain=\"{}\"",
+#		frame_support::sp_runtime::AccountId32::new(
+#			PayRewardFromAccount::<[u8; 32], [u8; 32], LegacyLaneId>::rewards_account(RewardsAccountParams::new(
+#				LegacyLaneId([0, 0, 0, 2]),
+#				*b"bhro",
+#				RewardsAccountOwner::ThisChain
+#			))
+#		)
+#			.to_ss58check_with_version(42_u16.into())
+#	);
+#	// SS58=42
+#	println!(
+#		"ON_BRIDGE_HUB_WESTEND_SOVEREIGN_ACCOUNT_FOR_LANE_00000002_bhro_BridgedChain=\"{}\"",
+#		frame_support::sp_runtime::AccountId32::new(
+#			PayRewardFromAccount::<[u8; 32], [u8; 32], LegacyLaneId>::rewards_account(RewardsAccountParams::new(
+#				LegacyLaneId([0, 0, 0, 2]),
+#				*b"bhro",
+#				RewardsAccountOwner::BridgedChain
+#			))
+#		)
+#			.to_ss58check_with_version(42_u16.into())
+#	);
+#}
+ON_BRIDGE_HUB_ROCOCO_SOVEREIGN_ACCOUNT_FOR_LANE_00000002_bhwd_ThisChain="5EHnXaT5GApse1euZWj9hycMbgjKBCNQL9WEwScL8QDx6mhK"
+ON_BRIDGE_HUB_ROCOCO_SOVEREIGN_ACCOUNT_FOR_LANE_00000002_bhwd_BridgedChain="5EHnXaT5Tnt4A8aiP9CsuAFRhKPjKZJXRrj4a3mtihFvKpTi"
+ON_BRIDGE_HUB_WESTEND_SOVEREIGN_ACCOUNT_FOR_LANE_00000002_bhro_ThisChain="5EHnXaT5GApry9tS6yd1FVusPq8o8bQJGCKyvXTFCoEKk5Z9"
+ON_BRIDGE_HUB_WESTEND_SOVEREIGN_ACCOUNT_FOR_LANE_00000002_bhro_BridgedChain="5EHnXaT5Tnt3VGpEvc6jSgYwVToDGxLRMuYoZ8coo6GHyWbR"
 
 LANE_ID="00000002"
 XCM_VERSION=3
diff --git a/cumulus/parachains/integration-tests/emulated/chains/parachains/bridges/bridge-hub-rococo/Cargo.toml b/cumulus/parachains/integration-tests/emulated/chains/parachains/bridges/bridge-hub-rococo/Cargo.toml
index f3c0799ad0f6acf14b3e99d4e27e46921c4e9cb9..266d743ca0c22c53160f877836eb3273f8735e2f 100644
--- a/cumulus/parachains/integration-tests/emulated/chains/parachains/bridges/bridge-hub-rococo/Cargo.toml
+++ b/cumulus/parachains/integration-tests/emulated/chains/parachains/bridges/bridge-hub-rococo/Cargo.toml
@@ -16,6 +16,12 @@ workspace = true
 sp-core = { workspace = true }
 frame-support = { workspace = true }
 
+# Polkadot Dependencies
+xcm = { workspace = true }
+
+# Bridge dependencies
+bp-messages = { workspace = true }
+
 # Cumulus
 parachains-common = { workspace = true, default-features = true }
 emulated-integration-tests-common = { workspace = true }
diff --git a/cumulus/parachains/integration-tests/emulated/chains/parachains/bridges/bridge-hub-rococo/src/genesis.rs b/cumulus/parachains/integration-tests/emulated/chains/parachains/bridges/bridge-hub-rococo/src/genesis.rs
index 3786d529ea6502988f92c18955722a7a0f9ebded..b9c0c01101c6e7432956aad334b56a1cb27def01 100644
--- a/cumulus/parachains/integration-tests/emulated/chains/parachains/bridges/bridge-hub-rococo/src/genesis.rs
+++ b/cumulus/parachains/integration-tests/emulated/chains/parachains/bridges/bridge-hub-rococo/src/genesis.rs
@@ -21,6 +21,7 @@ use emulated_integration_tests_common::{
 	accounts, build_genesis_storage, collators, get_account_id_from_seed, SAFE_XCM_VERSION,
 };
 use parachains_common::Balance;
+use xcm::latest::prelude::*;
 
 pub const ASSETHUB_PARA_ID: u32 = 1000;
 pub const PARA_ID: u32 = 1013;
@@ -66,6 +67,17 @@ pub fn genesis() -> Storage {
 			owner: Some(get_account_id_from_seed::<sr25519::Public>(accounts::BOB)),
 			..Default::default()
 		},
+		xcm_over_bridge_hub_westend: bridge_hub_rococo_runtime::XcmOverBridgeHubWestendConfig {
+			opened_bridges: vec![
+				// open AHR -> AHW bridge
+				(
+					Location::new(1, [Parachain(1000)]),
+					Junctions::from([Westend.into(), Parachain(1000)]),
+					Some(bp_messages::LegacyLaneId([0, 0, 0, 2])),
+				),
+			],
+			..Default::default()
+		},
 		ethereum_system: bridge_hub_rococo_runtime::EthereumSystemConfig {
 			para_id: PARA_ID.into(),
 			asset_hub_para_id: ASSETHUB_PARA_ID.into(),
diff --git a/cumulus/parachains/integration-tests/emulated/chains/parachains/bridges/bridge-hub-westend/Cargo.toml b/cumulus/parachains/integration-tests/emulated/chains/parachains/bridges/bridge-hub-westend/Cargo.toml
index ebcec9641e7d9f122f5b6737769bc140d9c877a5..88d7348f50f28f7376acc9a1d73f068d1397f4c0 100644
--- a/cumulus/parachains/integration-tests/emulated/chains/parachains/bridges/bridge-hub-westend/Cargo.toml
+++ b/cumulus/parachains/integration-tests/emulated/chains/parachains/bridges/bridge-hub-westend/Cargo.toml
@@ -16,6 +16,12 @@ workspace = true
 sp-core = { workspace = true }
 frame-support = { workspace = true }
 
+# Polkadot Dependencies
+xcm = { workspace = true }
+
+# Bridge dependencies
+bp-messages = { workspace = true }
+
 # Cumulus
 parachains-common = { workspace = true, default-features = true }
 emulated-integration-tests-common = { workspace = true }
diff --git a/cumulus/parachains/integration-tests/emulated/chains/parachains/bridges/bridge-hub-westend/src/genesis.rs b/cumulus/parachains/integration-tests/emulated/chains/parachains/bridges/bridge-hub-westend/src/genesis.rs
index f38f385db65068928b0f83de4cdef998813c0dba..3ffe3d86b2acda13e5bbf76f1f0052bc3da58237 100644
--- a/cumulus/parachains/integration-tests/emulated/chains/parachains/bridges/bridge-hub-westend/src/genesis.rs
+++ b/cumulus/parachains/integration-tests/emulated/chains/parachains/bridges/bridge-hub-westend/src/genesis.rs
@@ -21,6 +21,7 @@ use emulated_integration_tests_common::{
 	accounts, build_genesis_storage, collators, get_account_id_from_seed, SAFE_XCM_VERSION,
 };
 use parachains_common::Balance;
+use xcm::latest::prelude::*;
 
 pub const PARA_ID: u32 = 1002;
 pub const ASSETHUB_PARA_ID: u32 = 1000;
@@ -66,6 +67,17 @@ pub fn genesis() -> Storage {
 			owner: Some(get_account_id_from_seed::<sr25519::Public>(accounts::BOB)),
 			..Default::default()
 		},
+		xcm_over_bridge_hub_rococo: bridge_hub_westend_runtime::XcmOverBridgeHubRococoConfig {
+			opened_bridges: vec![
+				// open AHW -> AHR bridge
+				(
+					Location::new(1, [Parachain(1000)]),
+					Junctions::from([Rococo.into(), Parachain(1000)]),
+					Some(bp_messages::LegacyLaneId([0, 0, 0, 2])),
+				),
+			],
+			..Default::default()
+		},
 		ethereum_system: bridge_hub_westend_runtime::EthereumSystemConfig {
 			para_id: PARA_ID.into(),
 			asset_hub_para_id: ASSETHUB_PARA_ID.into(),
diff --git a/cumulus/parachains/integration-tests/emulated/common/src/impls.rs b/cumulus/parachains/integration-tests/emulated/common/src/impls.rs
index 559a16379bb410150cbf569bfa60a4e99be45cb2..c0d42cf2758e9431c87e4187eb42b9ab703f7f54 100644
--- a/cumulus/parachains/integration-tests/emulated/common/src/impls.rs
+++ b/cumulus/parachains/integration-tests/emulated/common/src/impls.rs
@@ -61,10 +61,10 @@ pub use xcm_emulator::{
 // Bridges
 use bp_messages::{
 	target_chain::{DispatchMessage, DispatchMessageData, MessageDispatch},
-	LaneId, MessageKey, OutboundLaneData,
+	MessageKey, OutboundLaneData,
 };
 pub use bp_xcm_bridge_hub::XcmBridgeHubCall;
-use pallet_bridge_messages::{Config as BridgeMessagesConfig, OutboundLanes, Pallet};
+use pallet_bridge_messages::{Config as BridgeMessagesConfig, LaneIdOf, OutboundLanes, Pallet};
 pub use pallet_bridge_messages::{
 	Instance1 as BridgeMessagesInstance1, Instance2 as BridgeMessagesInstance2,
 	Instance3 as BridgeMessagesInstance3,
@@ -75,14 +75,14 @@ pub struct BridgeHubMessageHandler<S, SI, T, TI> {
 	_marker: std::marker::PhantomData<(S, SI, T, TI)>,
 }
 
-struct LaneIdWrapper(LaneId);
-impl From<LaneIdWrapper> for BridgeLaneId {
-	fn from(lane_id: LaneIdWrapper) -> BridgeLaneId {
+struct LaneIdWrapper<LaneId>(LaneId);
+impl<LaneId: Encode> From<LaneIdWrapper<LaneId>> for BridgeLaneId {
+	fn from(lane_id: LaneIdWrapper<LaneId>) -> BridgeLaneId {
 		lane_id.0.encode()
 	}
 }
-impl From<BridgeLaneId> for LaneIdWrapper {
-	fn from(id: BridgeLaneId) -> LaneIdWrapper {
+impl<LaneId: Decode> From<BridgeLaneId> for LaneIdWrapper<LaneId> {
+	fn from(id: BridgeLaneId) -> LaneIdWrapper<LaneId> {
 		LaneIdWrapper(LaneId::decode(&mut &id[..]).expect("decodable"))
 	}
 }
@@ -154,7 +154,7 @@ where
 	}
 
 	fn notify_source_message_delivery(lane_id: BridgeLaneId) {
-		let lane_id = LaneIdWrapper::from(lane_id).0;
+		let lane_id: LaneIdOf<S, SI> = LaneIdWrapper::from(lane_id).0;
 		let data = OutboundLanes::<S, SI>::get(lane_id).unwrap();
 		let new_data = OutboundLaneData {
 			oldest_unpruned_nonce: data.oldest_unpruned_nonce + 1,
diff --git a/cumulus/parachains/integration-tests/emulated/tests/bridges/bridge-hub-rococo/src/tests/mod.rs b/cumulus/parachains/integration-tests/emulated/tests/bridges/bridge-hub-rococo/src/tests/mod.rs
index b540f55642a5f13fcdceba1594f7c849bcbc3df8..a989881fef09f45648a26f733158c9363cefe852 100644
--- a/cumulus/parachains/integration-tests/emulated/tests/bridges/bridge-hub-rococo/src/tests/mod.rs
+++ b/cumulus/parachains/integration-tests/emulated/tests/bridges/bridge-hub-rococo/src/tests/mod.rs
@@ -231,17 +231,6 @@ pub(crate) fn open_bridge_between_asset_hub_rococo_and_asset_hub_westend() {
 			)),
 		)),
 	);
-	BridgeHubRococo::execute_with(|| {
-		type RuntimeEvent = <BridgeHubRococo as Chain>::RuntimeEvent;
-		assert_expected_events!(
-			BridgeHubRococo,
-			vec![
-				RuntimeEvent::XcmOverBridgeHubWestend(
-					pallet_xcm_bridge_hub::Event::BridgeOpened { .. }
-				) => {},
-			]
-		);
-	});
 
 	// open AHW -> AHR
 	BridgeHubWestend::fund_para_sovereign(AssetHubWestend::para_id(), WND * 5);
@@ -255,15 +244,4 @@ pub(crate) fn open_bridge_between_asset_hub_rococo_and_asset_hub_westend() {
 			)),
 		)),
 	);
-	BridgeHubWestend::execute_with(|| {
-		type RuntimeEvent = <BridgeHubWestend as Chain>::RuntimeEvent;
-		assert_expected_events!(
-			BridgeHubWestend,
-			vec![
-				RuntimeEvent::XcmOverBridgeHubRococo(
-					pallet_xcm_bridge_hub::Event::BridgeOpened { .. }
-				) => {},
-			]
-		);
-	});
 }
diff --git a/cumulus/parachains/integration-tests/emulated/tests/bridges/bridge-hub-westend/src/tests/mod.rs b/cumulus/parachains/integration-tests/emulated/tests/bridges/bridge-hub-westend/src/tests/mod.rs
index 699641d3328fc416ae6ef0c550eb1961084c50e1..f037a05a82766f50bbaae06459a30eb068aa15ea 100644
--- a/cumulus/parachains/integration-tests/emulated/tests/bridges/bridge-hub-westend/src/tests/mod.rs
+++ b/cumulus/parachains/integration-tests/emulated/tests/bridges/bridge-hub-westend/src/tests/mod.rs
@@ -246,17 +246,6 @@ pub(crate) fn open_bridge_between_asset_hub_rococo_and_asset_hub_westend() {
 			)),
 		)),
 	);
-	BridgeHubRococo::execute_with(|| {
-		type RuntimeEvent = <BridgeHubRococo as Chain>::RuntimeEvent;
-		assert_expected_events!(
-			BridgeHubRococo,
-			vec![
-				RuntimeEvent::XcmOverBridgeHubWestend(
-					pallet_xcm_bridge_hub::Event::BridgeOpened { .. }
-				) => {},
-			]
-		);
-	});
 
 	// open AHW -> AHR
 	BridgeHubWestend::fund_para_sovereign(AssetHubWestend::para_id(), WND * 5);
@@ -270,15 +259,4 @@ pub(crate) fn open_bridge_between_asset_hub_rococo_and_asset_hub_westend() {
 			)),
 		)),
 	);
-	BridgeHubWestend::execute_with(|| {
-		type RuntimeEvent = <BridgeHubWestend as Chain>::RuntimeEvent;
-		assert_expected_events!(
-			BridgeHubWestend,
-			vec![
-				RuntimeEvent::XcmOverBridgeHubRococo(
-					pallet_xcm_bridge_hub::Event::BridgeOpened { .. }
-				) => {},
-			]
-		);
-	});
 }
diff --git a/cumulus/parachains/runtimes/bridge-hubs/bridge-hub-rococo/src/bridge_common_config.rs b/cumulus/parachains/runtimes/bridge-hubs/bridge-hub-rococo/src/bridge_common_config.rs
index 779cc537ee96dcc2fadc38452dfcd01c4c320789..5dca45d326b83fb188a261d1c09737b828159ebf 100644
--- a/cumulus/parachains/runtimes/bridge-hubs/bridge-hub-rococo/src/bridge_common_config.rs
+++ b/cumulus/parachains/runtimes/bridge-hubs/bridge-hub-rococo/src/bridge_common_config.rs
@@ -64,11 +64,37 @@ impl pallet_bridge_parachains::Config<BridgeParachainWestendInstance> for Runtim
 }
 
 /// Allows collect and claim rewards for relayers
-impl pallet_bridge_relayers::Config for Runtime {
+pub type RelayersForLegacyLaneIdsMessagesInstance = ();
+impl pallet_bridge_relayers::Config<RelayersForLegacyLaneIdsMessagesInstance> for Runtime {
 	type RuntimeEvent = RuntimeEvent;
 	type Reward = Balance;
-	type PaymentProcedure =
-		bp_relayers::PayRewardFromAccount<pallet_balances::Pallet<Runtime>, AccountId>;
+	type PaymentProcedure = bp_relayers::PayRewardFromAccount<
+		pallet_balances::Pallet<Runtime>,
+		AccountId,
+		Self::LaneId,
+	>;
+	type StakeAndSlash = pallet_bridge_relayers::StakeAndSlashNamed<
+		AccountId,
+		BlockNumber,
+		Balances,
+		RelayerStakeReserveId,
+		RequiredStakeForStakeAndSlash,
+		RelayerStakeLease,
+	>;
+	type WeightInfo = weights::pallet_bridge_relayers::WeightInfo<Runtime>;
+	type LaneId = bp_messages::LegacyLaneId;
+}
+
+/// Allows collect and claim rewards for relayers
+pub type RelayersForPermissionlessLanesInstance = pallet_bridge_relayers::Instance2;
+impl pallet_bridge_relayers::Config<RelayersForPermissionlessLanesInstance> for Runtime {
+	type RuntimeEvent = RuntimeEvent;
+	type Reward = Balance;
+	type PaymentProcedure = bp_relayers::PayRewardFromAccount<
+		pallet_balances::Pallet<Runtime>,
+		AccountId,
+		Self::LaneId,
+	>;
 	type StakeAndSlash = pallet_bridge_relayers::StakeAndSlashNamed<
 		AccountId,
 		BlockNumber,
@@ -78,6 +104,7 @@ impl pallet_bridge_relayers::Config for Runtime {
 		RelayerStakeLease,
 	>;
 	type WeightInfo = weights::pallet_bridge_relayers::WeightInfo<Runtime>;
+	type LaneId = bp_messages::HashedLaneId;
 }
 
 /// Add GRANDPA bridge pallet to track Rococo Bulletin chain.
diff --git a/cumulus/parachains/runtimes/bridge-hubs/bridge-hub-rococo/src/bridge_to_bulletin_config.rs b/cumulus/parachains/runtimes/bridge-hubs/bridge-hub-rococo/src/bridge_to_bulletin_config.rs
index 00d902486c850f0fc99219beb81bce3c9c830c78..c971fa59c68d7d4a3b6558274d61ad631246d1f6 100644
--- a/cumulus/parachains/runtimes/bridge-hubs/bridge-hub-rococo/src/bridge_to_bulletin_config.rs
+++ b/cumulus/parachains/runtimes/bridge-hubs/bridge-hub-rococo/src/bridge_to_bulletin_config.rs
@@ -20,13 +20,14 @@
 //! are reusing Polkadot Bulletin chain primitives everywhere here.
 
 use crate::{
-	weights, xcm_config::UniversalLocation, AccountId, Balance, Balances,
-	BridgeRococoBulletinGrandpa, BridgeRococoBulletinMessages, PolkadotXcm, Runtime, RuntimeEvent,
-	RuntimeHoldReason, XcmOverRococoBulletin, XcmRouter,
+	bridge_common_config::RelayersForPermissionlessLanesInstance, weights,
+	xcm_config::UniversalLocation, AccountId, Balance, Balances, BridgeRococoBulletinGrandpa,
+	BridgeRococoBulletinMessages, PolkadotXcm, Runtime, RuntimeEvent, RuntimeHoldReason,
+	XcmOverRococoBulletin, XcmRouter,
 };
 use bp_messages::{
 	source_chain::FromBridgedChainMessagesDeliveryProof,
-	target_chain::FromBridgedChainMessagesProof,
+	target_chain::FromBridgedChainMessagesProof, HashedLaneId,
 };
 use bridge_hub_common::xcm_version::XcmVersionOfDestAndRemoteBridge;
 
@@ -34,11 +35,11 @@ use frame_support::{
 	parameter_types,
 	traits::{Equals, PalletInfoAccess},
 };
-use frame_system::EnsureRoot;
+use frame_system::{EnsureNever, EnsureRoot};
+use pallet_bridge_messages::LaneIdOf;
 use pallet_bridge_relayers::extension::{
 	BridgeRelayersSignedExtension, WithMessagesExtensionConfig,
 };
-use pallet_xcm::EnsureXcm;
 use pallet_xcm_bridge_hub::XcmAsPlainPayload;
 use polkadot_parachain_primitives::primitives::Sibling;
 use testnet_parachains_constants::rococo::currency::UNITS as ROC;
@@ -78,11 +79,11 @@ parameter_types! {
 }
 
 /// Proof of messages, coming from Rococo Bulletin chain.
-pub type FromRococoBulletinMessagesProof =
-	FromBridgedChainMessagesProof<bp_polkadot_bulletin::Hash>;
+pub type FromRococoBulletinMessagesProof<MI> =
+	FromBridgedChainMessagesProof<bp_polkadot_bulletin::Hash, LaneIdOf<Runtime, MI>>;
 /// Messages delivery proof for Rococo Bridge Hub -> Rococo Bulletin messages.
-pub type ToRococoBulletinMessagesDeliveryProof =
-	FromBridgedChainMessagesDeliveryProof<bp_polkadot_bulletin::Hash>;
+pub type ToRococoBulletinMessagesDeliveryProof<MI> =
+	FromBridgedChainMessagesDeliveryProof<bp_polkadot_bulletin::Hash, LaneIdOf<Runtime, MI>>;
 
 /// Dispatches received XCM messages from other bridge.
 type FromRococoBulletinMessageBlobDispatcher = BridgeBlobDispatcher<
@@ -99,8 +100,10 @@ pub type OnBridgeHubRococoRefundRococoBulletinMessages = BridgeRelayersSignedExt
 		StrOnBridgeHubRococoRefundRococoBulletinMessages,
 		Runtime,
 		WithRococoBulletinMessagesInstance,
+		RelayersForPermissionlessLanesInstance,
 		PriorityBoostPerMessage,
 	>,
+	LaneIdOf<Runtime, WithRococoBulletinMessagesInstance>,
 >;
 bp_runtime::generate_static_str_provider!(OnBridgeHubRococoRefundRococoBulletinMessages);
 
@@ -116,10 +119,10 @@ impl pallet_bridge_messages::Config<WithRococoBulletinMessagesInstance> for Runt
 	type BridgedHeaderChain = BridgeRococoBulletinGrandpa;
 
 	type OutboundPayload = XcmAsPlainPayload;
-
 	type InboundPayload = XcmAsPlainPayload;
-	type DeliveryPayments = ();
+	type LaneId = HashedLaneId;
 
+	type DeliveryPayments = ();
 	type DeliveryConfirmationPayments = ();
 
 	type MessageDispatch = XcmOverRococoBulletin;
@@ -139,9 +142,9 @@ impl pallet_xcm_bridge_hub::Config<XcmOverPolkadotBulletinInstance> for Runtime
 	type DestinationVersion =
 		XcmVersionOfDestAndRemoteBridge<PolkadotXcm, RococoBulletinGlobalConsensusNetworkLocation>;
 
-	type AdminOrigin = EnsureRoot<AccountId>;
-	// Only allow calls from sibling People parachain to directly open the bridge.
-	type OpenBridgeOrigin = EnsureXcm<Equals<PeopleRococoLocation>>;
+	type ForceOrigin = EnsureRoot<AccountId>;
+	// We don't want to allow creating bridges for this instance.
+	type OpenBridgeOrigin = EnsureNever<Location>;
 	// Converter aligned with `OpenBridgeOrigin`.
 	type BridgeOriginAccountIdConverter =
 		(ParentIsPreset<AccountId>, SiblingParachainConvertsVia<Sibling, AccountId>);
@@ -230,14 +233,20 @@ mod tests {
 }
 
 #[cfg(feature = "runtime-benchmarks")]
-pub(crate) fn open_bridge_for_benchmarks(
-	with: bp_messages::LaneId,
+pub(crate) fn open_bridge_for_benchmarks<R, XBHI, C>(
+	with: pallet_xcm_bridge_hub::LaneIdOf<R, XBHI>,
 	sibling_para_id: u32,
-) -> InteriorLocation {
+) -> InteriorLocation
+where
+	R: pallet_xcm_bridge_hub::Config<XBHI>,
+	XBHI: 'static,
+	C: xcm_executor::traits::ConvertLocation<
+		bp_runtime::AccountIdOf<pallet_xcm_bridge_hub::ThisChainOf<R, XBHI>>,
+	>,
+{
 	use pallet_xcm_bridge_hub::{Bridge, BridgeId, BridgeState};
 	use sp_runtime::traits::Zero;
 	use xcm::VersionedInteriorLocation;
-	use xcm_executor::traits::ConvertLocation;
 
 	// insert bridge metadata
 	let lane_id = with;
@@ -248,7 +257,7 @@ pub(crate) fn open_bridge_for_benchmarks(
 	let bridge_id = BridgeId::new(&universal_source, &universal_destination);
 
 	// insert only bridge metadata, because the benchmarks create lanes
-	pallet_xcm_bridge_hub::Bridges::<Runtime, XcmOverPolkadotBulletinInstance>::insert(
+	pallet_xcm_bridge_hub::Bridges::<R, XBHI>::insert(
 		bridge_id,
 		Bridge {
 			bridge_origin_relative_location: alloc::boxed::Box::new(
@@ -261,17 +270,12 @@ pub(crate) fn open_bridge_for_benchmarks(
 				VersionedInteriorLocation::from(universal_destination),
 			),
 			state: BridgeState::Opened,
-			bridge_owner_account: crate::xcm_config::LocationToAccountId::convert_location(
-				&sibling_parachain,
-			)
-			.expect("valid AccountId"),
-			deposit: Balance::zero(),
+			bridge_owner_account: C::convert_location(&sibling_parachain).expect("valid AccountId"),
+			deposit: Zero::zero(),
 			lane_id,
 		},
 	);
-	pallet_xcm_bridge_hub::LaneToBridge::<Runtime, XcmOverPolkadotBulletinInstance>::insert(
-		lane_id, bridge_id,
-	);
+	pallet_xcm_bridge_hub::LaneToBridge::<R, XBHI>::insert(lane_id, bridge_id);
 
 	universal_source
 }
@@ -279,13 +283,16 @@ pub(crate) fn open_bridge_for_benchmarks(
 /// Contains the migration for the PeopleRococo<>RococoBulletin bridge.
 pub mod migration {
 	use super::*;
-	use bp_messages::LaneId;
 	use frame_support::traits::ConstBool;
-	use sp_runtime::Either;
 
 	parameter_types! {
-		pub RococoPeopleToRococoBulletinMessagesLane: LaneId = LaneId::from_inner(Either::Right([0, 0, 0, 0]));
 		pub BulletinRococoLocation: InteriorLocation = [GlobalConsensus(RococoBulletinGlobalConsensusNetwork::get())].into();
+		pub RococoPeopleToRococoBulletinMessagesLane: HashedLaneId = pallet_xcm_bridge_hub::Pallet::< Runtime, XcmOverPolkadotBulletinInstance >::bridge_locations(
+				PeopleRococoLocation::get(),
+				BulletinRococoLocation::get()
+			)
+			.unwrap()
+			.calculate_lane_id(xcm::latest::VERSION).expect("Valid locations");
 	}
 
 	/// Ensure that the existing lanes for the People<>Bulletin bridge are correctly configured.
diff --git a/cumulus/parachains/runtimes/bridge-hubs/bridge-hub-rococo/src/bridge_to_westend_config.rs b/cumulus/parachains/runtimes/bridge-hubs/bridge-hub-rococo/src/bridge_to_westend_config.rs
index fc52413a909fe63894cc1d2f94729f85abdfaa26..8fe045723107c58974c696ad9edf18a424b090b4 100644
--- a/cumulus/parachains/runtimes/bridge-hubs/bridge-hub-rococo/src/bridge_to_westend_config.rs
+++ b/cumulus/parachains/runtimes/bridge-hubs/bridge-hub-rococo/src/bridge_to_westend_config.rs
@@ -17,7 +17,10 @@
 //! Bridge definitions used on BridgeHubRococo for bridging to BridgeHubWestend.
 
 use crate::{
-	bridge_common_config::{BridgeParachainWestendInstance, DeliveryRewardInBalance},
+	bridge_common_config::{
+		BridgeParachainWestendInstance, DeliveryRewardInBalance,
+		RelayersForLegacyLaneIdsMessagesInstance,
+	},
 	weights,
 	xcm_config::UniversalLocation,
 	AccountId, Balance, Balances, BridgeWestendMessages, PolkadotXcm, Runtime, RuntimeEvent,
@@ -25,20 +28,18 @@ use crate::{
 };
 use bp_messages::{
 	source_chain::FromBridgedChainMessagesDeliveryProof,
-	target_chain::FromBridgedChainMessagesProof,
+	target_chain::FromBridgedChainMessagesProof, LegacyLaneId,
 };
 use bridge_hub_common::xcm_version::XcmVersionOfDestAndRemoteBridge;
 use pallet_xcm_bridge_hub::XcmAsPlainPayload;
 
 use frame_support::{parameter_types, traits::PalletInfoAccess};
-use frame_system::EnsureRoot;
+use frame_system::{EnsureNever, EnsureRoot};
+use pallet_bridge_messages::LaneIdOf;
 use pallet_bridge_relayers::extension::{
 	BridgeRelayersSignedExtension, WithMessagesExtensionConfig,
 };
-use pallet_xcm::EnsureXcm;
-use parachains_common::xcm_config::{
-	AllSiblingSystemParachains, ParentRelayOrSiblingParachains, RelayOrOtherSystemParachains,
-};
+use parachains_common::xcm_config::{AllSiblingSystemParachains, RelayOrOtherSystemParachains};
 use polkadot_parachain_primitives::primitives::Sibling;
 use testnet_parachains_constants::rococo::currency::UNITS as ROC;
 use xcm::{
@@ -73,11 +74,11 @@ parameter_types! {
 }
 
 /// Proof of messages, coming from Westend.
-pub type FromWestendBridgeHubMessagesProof =
-	FromBridgedChainMessagesProof<bp_bridge_hub_westend::Hash>;
+pub type FromWestendBridgeHubMessagesProof<MI> =
+	FromBridgedChainMessagesProof<bp_bridge_hub_westend::Hash, LaneIdOf<Runtime, MI>>;
 /// Messages delivery proof for Rococo Bridge Hub -> Westend Bridge Hub messages.
-pub type ToWestendBridgeHubMessagesDeliveryProof =
-	FromBridgedChainMessagesDeliveryProof<bp_bridge_hub_westend::Hash>;
+pub type ToWestendBridgeHubMessagesDeliveryProof<MI> =
+	FromBridgedChainMessagesDeliveryProof<bp_bridge_hub_westend::Hash, LaneIdOf<Runtime, MI>>;
 
 /// Dispatches received XCM messages from other bridge
 type FromWestendMessageBlobDispatcher =
@@ -90,8 +91,10 @@ pub type OnBridgeHubRococoRefundBridgeHubWestendMessages = BridgeRelayersSignedE
 		StrOnBridgeHubRococoRefundBridgeHubWestendMessages,
 		Runtime,
 		WithBridgeHubWestendMessagesInstance,
+		RelayersForLegacyLaneIdsMessagesInstance,
 		PriorityBoostPerMessage,
 	>,
+	LaneIdOf<Runtime, WithBridgeHubWestendMessagesInstance>,
 >;
 bp_runtime::generate_static_str_provider!(OnBridgeHubRococoRefundBridgeHubWestendMessages);
 
@@ -110,10 +113,10 @@ impl pallet_bridge_messages::Config<WithBridgeHubWestendMessagesInstance> for Ru
 	>;
 
 	type OutboundPayload = XcmAsPlainPayload;
-
 	type InboundPayload = XcmAsPlainPayload;
-	type DeliveryPayments = ();
+	type LaneId = LegacyLaneId;
 
+	type DeliveryPayments = ();
 	type DeliveryConfirmationPayments = pallet_bridge_relayers::DeliveryConfirmationPaymentsAdapter<
 		Runtime,
 		WithBridgeHubWestendMessagesInstance,
@@ -124,7 +127,8 @@ impl pallet_bridge_messages::Config<WithBridgeHubWestendMessagesInstance> for Ru
 	type OnMessagesDelivered = XcmOverBridgeHubWestend;
 }
 
-/// Add support for the export and dispatch of XCM programs.
+/// Add support for the export and dispatch of XCM programs withing
+/// `WithBridgeHubWestendMessagesInstance`.
 pub type XcmOverBridgeHubWestendInstance = pallet_xcm_bridge_hub::Instance1;
 impl pallet_xcm_bridge_hub::Config<XcmOverBridgeHubWestendInstance> for Runtime {
 	type RuntimeEvent = RuntimeEvent;
@@ -137,9 +141,9 @@ impl pallet_xcm_bridge_hub::Config<XcmOverBridgeHubWestendInstance> for Runtime
 	type DestinationVersion =
 		XcmVersionOfDestAndRemoteBridge<PolkadotXcm, BridgeHubWestendLocation>;
 
-	type AdminOrigin = EnsureRoot<AccountId>;
-	// Only allow calls from relay chains and sibling parachains to directly open the bridge.
-	type OpenBridgeOrigin = EnsureXcm<ParentRelayOrSiblingParachains>;
+	type ForceOrigin = EnsureRoot<AccountId>;
+	// We don't want to allow creating bridges for this instance with `LegacyLaneId`.
+	type OpenBridgeOrigin = EnsureNever<Location>;
 	// Converter aligned with `OpenBridgeOrigin`.
 	type BridgeOriginAccountIdConverter =
 		(ParentIsPreset<AccountId>, SiblingParachainConvertsVia<Sibling, AccountId>);
@@ -157,14 +161,20 @@ impl pallet_xcm_bridge_hub::Config<XcmOverBridgeHubWestendInstance> for Runtime
 }
 
 #[cfg(feature = "runtime-benchmarks")]
-pub(crate) fn open_bridge_for_benchmarks(
-	with: bp_messages::LaneId,
+pub(crate) fn open_bridge_for_benchmarks<R, XBHI, C>(
+	with: pallet_xcm_bridge_hub::LaneIdOf<R, XBHI>,
 	sibling_para_id: u32,
-) -> InteriorLocation {
+) -> InteriorLocation
+where
+	R: pallet_xcm_bridge_hub::Config<XBHI>,
+	XBHI: 'static,
+	C: xcm_executor::traits::ConvertLocation<
+		bp_runtime::AccountIdOf<pallet_xcm_bridge_hub::ThisChainOf<R, XBHI>>,
+	>,
+{
 	use pallet_xcm_bridge_hub::{Bridge, BridgeId, BridgeState};
 	use sp_runtime::traits::Zero;
 	use xcm::VersionedInteriorLocation;
-	use xcm_executor::traits::ConvertLocation;
 
 	// insert bridge metadata
 	let lane_id = with;
@@ -174,7 +184,7 @@ pub(crate) fn open_bridge_for_benchmarks(
 	let bridge_id = BridgeId::new(&universal_source, &universal_destination);
 
 	// insert only bridge metadata, because the benchmarks create lanes
-	pallet_xcm_bridge_hub::Bridges::<Runtime, XcmOverBridgeHubWestendInstance>::insert(
+	pallet_xcm_bridge_hub::Bridges::<R, XBHI>::insert(
 		bridge_id,
 		Bridge {
 			bridge_origin_relative_location: alloc::boxed::Box::new(
@@ -187,17 +197,12 @@ pub(crate) fn open_bridge_for_benchmarks(
 				VersionedInteriorLocation::from(universal_destination),
 			),
 			state: BridgeState::Opened,
-			bridge_owner_account: crate::xcm_config::LocationToAccountId::convert_location(
-				&sibling_parachain,
-			)
-			.expect("valid AccountId"),
-			deposit: Balance::zero(),
+			bridge_owner_account: C::convert_location(&sibling_parachain).expect("valid AccountId"),
+			deposit: Zero::zero(),
 			lane_id,
 		},
 	);
-	pallet_xcm_bridge_hub::LaneToBridge::<Runtime, XcmOverBridgeHubWestendInstance>::insert(
-		lane_id, bridge_id,
-	);
+	pallet_xcm_bridge_hub::LaneToBridge::<R, XBHI>::insert(lane_id, bridge_id);
 
 	universal_source
 }
@@ -297,12 +302,10 @@ mod tests {
 /// Contains the migration for the AssetHubRococo<>AssetHubWestend bridge.
 pub mod migration {
 	use super::*;
-	use bp_messages::LaneId;
 	use frame_support::traits::ConstBool;
-	use sp_runtime::Either;
 
 	parameter_types! {
-		pub AssetHubRococoToAssetHubWestendMessagesLane: LaneId = LaneId::from_inner(Either::Right([0, 0, 0, 2]));
+		pub AssetHubRococoToAssetHubWestendMessagesLane: LegacyLaneId = LegacyLaneId([0, 0, 0, 2]);
 		pub AssetHubRococoLocation: Location = Location::new(1, [Parachain(bp_asset_hub_rococo::ASSET_HUB_ROCOCO_PARACHAIN_ID)]);
 		pub AssetHubWestendUniversalLocation: InteriorLocation = [GlobalConsensus(WestendGlobalConsensusNetwork::get()), Parachain(bp_asset_hub_westend::ASSET_HUB_WESTEND_PARACHAIN_ID)].into();
 	}
@@ -318,4 +321,75 @@ pub mod migration {
 		AssetHubRococoLocation,
 		AssetHubWestendUniversalLocation,
 	>;
+
+	mod v1_wrong {
+		use bp_messages::{LaneState, MessageNonce, UnrewardedRelayer};
+		use bp_runtime::AccountIdOf;
+		use codec::{Decode, Encode};
+		use pallet_bridge_messages::BridgedChainOf;
+		use sp_std::collections::vec_deque::VecDeque;
+
+		#[derive(Encode, Decode, Clone, PartialEq, Eq)]
+		pub(crate) struct StoredInboundLaneData<T: pallet_bridge_messages::Config<I>, I: 'static>(
+			pub(crate) InboundLaneData<AccountIdOf<BridgedChainOf<T, I>>>,
+		);
+		#[derive(Encode, Decode, Clone, PartialEq, Eq)]
+		pub(crate) struct InboundLaneData<RelayerId> {
+			pub state: LaneState,
+			pub(crate) relayers: VecDeque<UnrewardedRelayer<RelayerId>>,
+			pub(crate) last_confirmed_nonce: MessageNonce,
+		}
+		#[derive(Encode, Decode, Clone, PartialEq, Eq)]
+		pub(crate) struct OutboundLaneData {
+			pub state: LaneState,
+			pub(crate) oldest_unpruned_nonce: MessageNonce,
+			pub(crate) latest_received_nonce: MessageNonce,
+			pub(crate) latest_generated_nonce: MessageNonce,
+		}
+	}
+
+	mod v1 {
+		pub use bp_messages::{InboundLaneData, LaneState, OutboundLaneData};
+		pub use pallet_bridge_messages::{InboundLanes, OutboundLanes, StoredInboundLaneData};
+	}
+
+	/// Fix for v1 migration - corrects data for OutboundLaneData/InboundLaneData (it is needed only
+	/// for Rococo/Westend).
+	pub struct FixMessagesV1Migration<T, I>(sp_std::marker::PhantomData<(T, I)>);
+
+	impl<T: pallet_bridge_messages::Config<I>, I: 'static> frame_support::traits::OnRuntimeUpgrade
+		for FixMessagesV1Migration<T, I>
+	{
+		fn on_runtime_upgrade() -> Weight {
+			use sp_core::Get;
+			let mut weight = T::DbWeight::get().reads(1);
+
+			// `InboundLanes` - add state to the old structs
+			let translate_inbound =
+				|pre: v1_wrong::StoredInboundLaneData<T, I>| -> Option<v1::StoredInboundLaneData<T, I>> {
+					weight.saturating_accrue(T::DbWeight::get().reads_writes(1, 1));
+					Some(v1::StoredInboundLaneData(v1::InboundLaneData {
+						state: v1::LaneState::Opened,
+						relayers: pre.0.relayers,
+						last_confirmed_nonce: pre.0.last_confirmed_nonce,
+					}))
+				};
+			v1::InboundLanes::<T, I>::translate_values(translate_inbound);
+
+			// `OutboundLanes` - add state to the old structs
+			let translate_outbound =
+				|pre: v1_wrong::OutboundLaneData| -> Option<v1::OutboundLaneData> {
+					weight.saturating_accrue(T::DbWeight::get().reads_writes(1, 1));
+					Some(v1::OutboundLaneData {
+						state: v1::LaneState::Opened,
+						oldest_unpruned_nonce: pre.oldest_unpruned_nonce,
+						latest_received_nonce: pre.latest_received_nonce,
+						latest_generated_nonce: pre.latest_generated_nonce,
+					})
+				};
+			v1::OutboundLanes::<T, I>::translate_values(translate_outbound);
+
+			weight
+		}
+	}
 }
diff --git a/cumulus/parachains/runtimes/bridge-hubs/bridge-hub-rococo/src/genesis_config_presets.rs b/cumulus/parachains/runtimes/bridge-hubs/bridge-hub-rococo/src/genesis_config_presets.rs
index e5d615985646b8011ccf755200de1b16ee58ca4b..07048d54ab1ba96acbe3fd9c32f17ab686da04cd 100644
--- a/cumulus/parachains/runtimes/bridge-hubs/bridge-hub-rococo/src/genesis_config_presets.rs
+++ b/cumulus/parachains/runtimes/bridge-hubs/bridge-hub-rococo/src/genesis_config_presets.rs
@@ -31,6 +31,7 @@ fn bridge_hub_rococo_genesis(
 	id: ParaId,
 	bridges_pallet_owner: Option<AccountId>,
 	asset_hub_para_id: ParaId,
+	opened_bridges: Vec<(Location, InteriorLocation, Option<bp_messages::LegacyLaneId>)>,
 ) -> serde_json::Value {
 	let config = RuntimeGenesisConfig {
 		balances: BalancesConfig {
@@ -71,6 +72,10 @@ fn bridge_hub_rococo_genesis(
 			owner: bridges_pallet_owner.clone(),
 			..Default::default()
 		},
+		xcm_over_bridge_hub_westend: XcmOverBridgeHubWestendConfig {
+			opened_bridges,
+			..Default::default()
+		},
 		ethereum_system: EthereumSystemConfig {
 			para_id: id,
 			asset_hub_para_id,
@@ -114,6 +119,11 @@ pub fn get_preset(id: &sp_genesis_builder::PresetId) -> Option<sp_std::vec::Vec<
 			1013.into(),
 			Some(get_account_id_from_seed::<sr25519::Public>("Bob")),
 			rococo_runtime_constants::system_parachain::ASSET_HUB_ID.into(),
+			vec![(
+				Location::new(1, [Parachain(1000)]),
+				Junctions::from([Westend.into(), Parachain(1000)]),
+				Some(bp_messages::LegacyLaneId([0, 0, 0, 2])),
+			)],
 		),
 		Ok(sp_genesis_builder::DEV_RUNTIME_PRESET) => bridge_hub_rococo_genesis(
 			// initial collators.
@@ -144,6 +154,7 @@ pub fn get_preset(id: &sp_genesis_builder::PresetId) -> Option<sp_std::vec::Vec<
 			1013.into(),
 			Some(get_account_id_from_seed::<sr25519::Public>("Bob")),
 			rococo_runtime_constants::system_parachain::ASSET_HUB_ID.into(),
+			vec![],
 		),
 		_ => return None,
 	};
diff --git a/cumulus/parachains/runtimes/bridge-hubs/bridge-hub-rococo/src/lib.rs b/cumulus/parachains/runtimes/bridge-hubs/bridge-hub-rococo/src/lib.rs
index 177978cee10bd83bf543b006fb7a2cfbc8295ac3..bf9bd58669b4651b9be5ff051bf2478e71761bf4 100644
--- a/cumulus/parachains/runtimes/bridge-hubs/bridge-hub-rococo/src/lib.rs
+++ b/cumulus/parachains/runtimes/bridge-hubs/bridge-hub-rococo/src/lib.rs
@@ -43,6 +43,7 @@ use bridge_runtime_common::extensions::{
 	CheckAndBoostBridgeGrandpaTransactions, CheckAndBoostBridgeParachainsTransactions,
 };
 use cumulus_pallet_parachain_system::RelayNumberMonotonicallyIncreases;
+use pallet_bridge_messages::LaneIdOf;
 use sp_api::impl_runtime_apis;
 use sp_core::{crypto::KeyTypeId, OpaqueMetadata};
 use sp_runtime::{
@@ -130,10 +131,7 @@ pub type SignedExtra = (
 	frame_system::CheckWeight<Runtime>,
 	pallet_transaction_payment::ChargeTransactionPayment<Runtime>,
 	BridgeRejectObsoleteHeadersAndMessages,
-	(
-		bridge_to_westend_config::OnBridgeHubRococoRefundBridgeHubWestendMessages,
-		bridge_to_bulletin_config::OnBridgeHubRococoRefundRococoBulletinMessages,
-	),
+	(bridge_to_westend_config::OnBridgeHubRococoRefundBridgeHubWestendMessages,),
 	cumulus_primitives_storage_weight_reclaim::StorageWeightReclaim<Runtime>,
 	frame_metadata_hash_extension::CheckMetadataHash<Runtime>,
 );
@@ -163,6 +161,10 @@ pub type Migrations = (
 		Runtime,
 		bridge_to_bulletin_config::WithRococoBulletinMessagesInstance,
 	>,
+	bridge_to_westend_config::migration::FixMessagesV1Migration<
+		Runtime,
+		bridge_to_westend_config::WithBridgeHubWestendMessagesInstance,
+	>,
 	bridge_to_westend_config::migration::StaticToDynamicLanes,
 	bridge_to_bulletin_config::migration::StaticToDynamicLanes,
 	frame_support::migrations::RemoveStorage<
@@ -175,6 +177,7 @@ pub type Migrations = (
 		OutboundLanesCongestedSignalsKey,
 		RocksDbWeight,
 	>,
+	pallet_bridge_relayers::migration::v1::MigrationToV1<Runtime, ()>,
 	// permanent
 	pallet_xcm::migration::MigrateToLatestXcmVersion<Runtime>,
 );
@@ -593,6 +596,9 @@ construct_runtime!(
 		// With-Rococo Bulletin bridge hub pallet.
 		XcmOverPolkadotBulletin: pallet_xcm_bridge_hub::<Instance2> = 62,
 
+		// Bridge relayers pallet, used by several bridges here (another instance).
+		BridgeRelayersForPermissionlessLanes: pallet_bridge_relayers::<Instance2> = 63,
+
 		EthereumInboundQueue: snowbridge_pallet_inbound_queue = 80,
 		EthereumOutboundQueue: snowbridge_pallet_outbound_queue = 81,
 		EthereumBeaconClient: snowbridge_pallet_ethereum_client = 82,
@@ -662,7 +668,8 @@ mod benches {
 		[pallet_bridge_parachains, WithinWestend]
 		[pallet_bridge_messages, RococoToWestend]
 		[pallet_bridge_messages, RococoToRococoBulletin]
-		[pallet_bridge_relayers, BridgeRelayersBench::<Runtime>]
+		[pallet_bridge_relayers, Legacy]
+		[pallet_bridge_relayers, PermissionlessLanes]
 		// Ethereum Bridge
 		[snowbridge_pallet_inbound_queue, EthereumInboundQueue]
 		[snowbridge_pallet_outbound_queue, EthereumOutboundQueue]
@@ -671,6 +678,11 @@ mod benches {
 	);
 }
 
+cumulus_pallet_parachain_system::register_validate_block! {
+	Runtime = Runtime,
+	BlockExecutor = cumulus_pallet_aura_ext::BlockExecutor::<Runtime, Executive>,
+}
+
 impl_runtime_apis! {
 	impl sp_consensus_aura::AuraApi<Block, AuraId> for Runtime {
 		fn slot_duration() -> sp_consensus_aura::SlotDuration {
@@ -914,7 +926,7 @@ impl_runtime_apis! {
 	// This is exposed by BridgeHubRococo
 	impl bp_bridge_hub_westend::FromBridgeHubWestendInboundLaneApi<Block> for Runtime {
 		fn message_details(
-			lane: bp_messages::LaneId,
+			lane: LaneIdOf<Runtime, bridge_to_westend_config::WithBridgeHubWestendMessagesInstance>,
 			messages: Vec<(bp_messages::MessagePayload, bp_messages::OutboundMessageDetails)>,
 		) -> Vec<bp_messages::InboundMessageDetails> {
 			bridge_runtime_common::messages_api::inbound_message_details::<
@@ -927,7 +939,7 @@ impl_runtime_apis! {
 	// This is exposed by BridgeHubRococo
 	impl bp_bridge_hub_westend::ToBridgeHubWestendOutboundLaneApi<Block> for Runtime {
 		fn message_details(
-			lane: bp_messages::LaneId,
+			lane: LaneIdOf<Runtime, bridge_to_westend_config::WithBridgeHubWestendMessagesInstance>,
 			begin: bp_messages::MessageNonce,
 			end: bp_messages::MessageNonce,
 		) -> Vec<bp_messages::OutboundMessageDetails> {
@@ -957,7 +969,7 @@ impl_runtime_apis! {
 
 	impl bp_polkadot_bulletin::FromPolkadotBulletinInboundLaneApi<Block> for Runtime {
 		fn message_details(
-			lane: bp_messages::LaneId,
+			lane: LaneIdOf<Runtime, bridge_to_bulletin_config::WithRococoBulletinMessagesInstance>,
 			messages: Vec<(bp_messages::MessagePayload, bp_messages::OutboundMessageDetails)>,
 		) -> Vec<bp_messages::InboundMessageDetails> {
 			bridge_runtime_common::messages_api::inbound_message_details::<
@@ -969,7 +981,7 @@ impl_runtime_apis! {
 
 	impl bp_polkadot_bulletin::ToPolkadotBulletinOutboundLaneApi<Block> for Runtime {
 		fn message_details(
-			lane: bp_messages::LaneId,
+			lane: LaneIdOf<Runtime, bridge_to_bulletin_config::WithRococoBulletinMessagesInstance>,
 			begin: bp_messages::MessageNonce,
 			end: bp_messages::MessageNonce,
 		) -> Vec<bp_messages::OutboundMessageDetails> {
@@ -1039,6 +1051,8 @@ impl_runtime_apis! {
 			type WithinWestend = pallet_bridge_parachains::benchmarking::Pallet::<Runtime, bridge_common_config::BridgeParachainWestendInstance>;
 			type RococoToWestend = pallet_bridge_messages::benchmarking::Pallet ::<Runtime, bridge_to_westend_config::WithBridgeHubWestendMessagesInstance>;
 			type RococoToRococoBulletin = pallet_bridge_messages::benchmarking::Pallet ::<Runtime, bridge_to_bulletin_config::WithRococoBulletinMessagesInstance>;
+			type Legacy = BridgeRelayersBench::<Runtime, bridge_common_config::RelayersForLegacyLaneIdsMessagesInstance>;
+			type PermissionlessLanes = BridgeRelayersBench::<Runtime, bridge_common_config::RelayersForPermissionlessLanesInstance>;
 
 			let mut list = Vec::<BenchmarkList>::new();
 			list_benchmarks!(list, extra);
@@ -1248,15 +1262,20 @@ impl_runtime_apis! {
 					);
 
 					// open bridge
-					let origin = RuntimeOrigin::from(pallet_xcm::Origin::Xcm(sibling_parachain_location.clone()));
-					XcmOverBridgeHubWestend::open_bridge(
-						origin.clone(),
-						Box::new(VersionedInteriorLocation::from([GlobalConsensus(NetworkId::Westend), Parachain(8765)])),
+					let bridge_destination_universal_location: InteriorLocation = [GlobalConsensus(NetworkId::Westend), Parachain(8765)].into();
+					let locations = XcmOverBridgeHubWestend::bridge_locations(
+						sibling_parachain_location.clone(),
+						bridge_destination_universal_location.clone(),
+					)?;
+					XcmOverBridgeHubWestend::do_open_bridge(
+						locations,
+						bp_messages::LegacyLaneId([1, 2, 3, 4]),
+						true,
 					).map_err(|e| {
 						log::error!(
 							"Failed to `XcmOverBridgeHubWestend::open_bridge`({:?}, {:?})`, error: {:?}",
-							origin,
-							[GlobalConsensus(NetworkId::Westend), Parachain(8765)],
+							sibling_parachain_location,
+							bridge_destination_universal_location,
 							e
 						);
 						BenchmarkError::Stop("Bridge was not opened!")
@@ -1283,6 +1302,8 @@ impl_runtime_apis! {
 			type WithinWestend = pallet_bridge_parachains::benchmarking::Pallet::<Runtime, bridge_common_config::BridgeParachainWestendInstance>;
 			type RococoToWestend = pallet_bridge_messages::benchmarking::Pallet ::<Runtime, bridge_to_westend_config::WithBridgeHubWestendMessagesInstance>;
 			type RococoToRococoBulletin = pallet_bridge_messages::benchmarking::Pallet ::<Runtime, bridge_to_bulletin_config::WithRococoBulletinMessagesInstance>;
+			type Legacy = BridgeRelayersBench::<Runtime, bridge_common_config::RelayersForLegacyLaneIdsMessagesInstance>;
+			type PermissionlessLanes = BridgeRelayersBench::<Runtime, bridge_common_config::RelayersForPermissionlessLanesInstance>;
 
 			use bridge_runtime_common::messages_benchmarking::{
 				prepare_message_delivery_proof_from_grandpa_chain,
@@ -1313,12 +1334,16 @@ impl_runtime_apis! {
 				}
 
 				fn prepare_message_proof(
-					params: MessageProofParams,
-				) -> (bridge_to_westend_config::FromWestendBridgeHubMessagesProof, Weight) {
+					params: MessageProofParams<LaneIdOf<Runtime, bridge_to_westend_config::WithBridgeHubWestendMessagesInstance>>,
+				) -> (bridge_to_westend_config::FromWestendBridgeHubMessagesProof<bridge_to_westend_config::WithBridgeHubWestendMessagesInstance>, Weight) {
 					use cumulus_primitives_core::XcmpMessageSource;
 					assert!(XcmpQueue::take_outbound_messages(usize::MAX).is_empty());
 					ParachainSystem::open_outbound_hrmp_channel_for_benchmarks_or_tests(42.into());
-					let universal_source = bridge_to_westend_config::open_bridge_for_benchmarks(params.lane, 42);
+					let universal_source = bridge_to_westend_config::open_bridge_for_benchmarks::<
+						Runtime,
+						bridge_to_westend_config::XcmOverBridgeHubWestendInstance,
+						xcm_config::LocationToAccountId,
+					>(params.lane, 42);
 					prepare_message_proof_from_parachain::<
 						Runtime,
 						bridge_common_config::BridgeGrandpaWestendInstance,
@@ -1327,9 +1352,13 @@ impl_runtime_apis! {
 				}
 
 				fn prepare_message_delivery_proof(
-					params: MessageDeliveryProofParams<AccountId>,
-				) -> bridge_to_westend_config::ToWestendBridgeHubMessagesDeliveryProof {
-					let _ = bridge_to_westend_config::open_bridge_for_benchmarks(params.lane, 42);
+					params: MessageDeliveryProofParams<AccountId, LaneIdOf<Runtime, bridge_to_westend_config::WithBridgeHubWestendMessagesInstance>>,
+				) -> bridge_to_westend_config::ToWestendBridgeHubMessagesDeliveryProof<bridge_to_westend_config::WithBridgeHubWestendMessagesInstance> {
+					let _ = bridge_to_westend_config::open_bridge_for_benchmarks::<
+						Runtime,
+						bridge_to_westend_config::XcmOverBridgeHubWestendInstance,
+						xcm_config::LocationToAccountId,
+					>(params.lane, 42);
 					prepare_message_delivery_proof_from_parachain::<
 						Runtime,
 						bridge_common_config::BridgeGrandpaWestendInstance,
@@ -1350,12 +1379,16 @@ impl_runtime_apis! {
 				}
 
 				fn prepare_message_proof(
-					params: MessageProofParams,
-				) -> (bridge_to_bulletin_config::FromRococoBulletinMessagesProof, Weight) {
+					params: MessageProofParams<LaneIdOf<Runtime, bridge_to_bulletin_config::WithRococoBulletinMessagesInstance>>,
+				) -> (bridge_to_bulletin_config::FromRococoBulletinMessagesProof<bridge_to_bulletin_config::WithRococoBulletinMessagesInstance>, Weight) {
 					use cumulus_primitives_core::XcmpMessageSource;
 					assert!(XcmpQueue::take_outbound_messages(usize::MAX).is_empty());
 					ParachainSystem::open_outbound_hrmp_channel_for_benchmarks_or_tests(42.into());
-					let universal_source = bridge_to_bulletin_config::open_bridge_for_benchmarks(params.lane, 42);
+					let universal_source = bridge_to_bulletin_config::open_bridge_for_benchmarks::<
+						Runtime,
+						bridge_to_bulletin_config::XcmOverPolkadotBulletinInstance,
+						xcm_config::LocationToAccountId,
+					>(params.lane, 42);
 					prepare_message_proof_from_grandpa_chain::<
 						Runtime,
 						bridge_common_config::BridgeGrandpaRococoBulletinInstance,
@@ -1364,9 +1397,13 @@ impl_runtime_apis! {
 				}
 
 				fn prepare_message_delivery_proof(
-					params: MessageDeliveryProofParams<AccountId>,
-				) -> bridge_to_bulletin_config::ToRococoBulletinMessagesDeliveryProof {
-					let _ = bridge_to_bulletin_config::open_bridge_for_benchmarks(params.lane, 42);
+					params: MessageDeliveryProofParams<AccountId, LaneIdOf<Runtime, bridge_to_bulletin_config::WithRococoBulletinMessagesInstance>>,
+				) -> bridge_to_bulletin_config::ToRococoBulletinMessagesDeliveryProof<bridge_to_bulletin_config::WithRococoBulletinMessagesInstance> {
+					let _ = bridge_to_bulletin_config::open_bridge_for_benchmarks::<
+						Runtime,
+						bridge_to_bulletin_config::XcmOverPolkadotBulletinInstance,
+						xcm_config::LocationToAccountId,
+					>(params.lane, 42);
 					prepare_message_delivery_proof_from_grandpa_chain::<
 						Runtime,
 						bridge_common_config::BridgeGrandpaRococoBulletinInstance,
@@ -1411,16 +1448,36 @@ impl_runtime_apis! {
 				}
 			}
 
-			impl BridgeRelayersConfig for Runtime {
+			impl BridgeRelayersConfig<bridge_common_config::RelayersForLegacyLaneIdsMessagesInstance> for Runtime {
 				fn prepare_rewards_account(
-					account_params: bp_relayers::RewardsAccountParams,
+					account_params: bp_relayers::RewardsAccountParams<<Self as pallet_bridge_relayers::Config<bridge_common_config::RelayersForLegacyLaneIdsMessagesInstance>>::LaneId>,
 					reward: Balance,
 				) {
 					let rewards_account = bp_relayers::PayRewardFromAccount::<
 						Balances,
-						AccountId
+						AccountId,
+						<Self as pallet_bridge_relayers::Config<bridge_common_config::RelayersForLegacyLaneIdsMessagesInstance>>::LaneId,
 					>::rewards_account(account_params);
-					Self::deposit_account(rewards_account, reward);
+					<Runtime as BridgeRelayersConfig<bridge_common_config::RelayersForLegacyLaneIdsMessagesInstance>>::deposit_account(rewards_account, reward);
+				}
+
+				fn deposit_account(account: AccountId, balance: Balance) {
+					use frame_support::traits::fungible::Mutate;
+					Balances::mint_into(&account, balance.saturating_add(ExistentialDeposit::get())).unwrap();
+				}
+			}
+
+			impl BridgeRelayersConfig<bridge_common_config::RelayersForPermissionlessLanesInstance> for Runtime {
+				fn prepare_rewards_account(
+					account_params: bp_relayers::RewardsAccountParams<<Self as pallet_bridge_relayers::Config<bridge_common_config::RelayersForPermissionlessLanesInstance>>::LaneId>,
+					reward: Balance,
+				) {
+					let rewards_account = bp_relayers::PayRewardFromAccount::<
+						Balances,
+						AccountId,
+						<Self as pallet_bridge_relayers::Config<bridge_common_config::RelayersForPermissionlessLanesInstance>>::LaneId,
+					>::rewards_account(account_params);
+					<Runtime as BridgeRelayersConfig<bridge_common_config::RelayersForPermissionlessLanesInstance>>::deposit_account(rewards_account, reward);
 				}
 
 				fn deposit_account(account: AccountId, balance: Balance) {
@@ -1465,11 +1522,6 @@ impl_runtime_apis! {
 	}
 }
 
-cumulus_pallet_parachain_system::register_validate_block! {
-	Runtime = Runtime,
-	BlockExecutor = cumulus_pallet_aura_ext::BlockExecutor::<Runtime, Executive>,
-}
-
 #[cfg(test)]
 mod tests {
 	use super::*;
@@ -1497,7 +1549,6 @@ mod tests {
                 BridgeRejectObsoleteHeadersAndMessages,
                 (
                     bridge_to_westend_config::OnBridgeHubRococoRefundBridgeHubWestendMessages::default(),
-                    bridge_to_bulletin_config::OnBridgeHubRococoRefundRococoBulletinMessages::default(),
                 ),
                 cumulus_primitives_storage_weight_reclaim::StorageWeightReclaim::new(),
                 frame_metadata_hash_extension::CheckMetadataHash::new(false),
diff --git a/cumulus/parachains/runtimes/bridge-hubs/bridge-hub-rococo/tests/snowbridge.rs b/cumulus/parachains/runtimes/bridge-hubs/bridge-hub-rococo/tests/snowbridge.rs
index c7b5850f9ffe56620e26e0751a6cb04c07311bb3..7a0f1462e7a7d150c1a4bb5c016bd2af410c9d55 100644
--- a/cumulus/parachains/runtimes/bridge-hubs/bridge-hub-rococo/tests/snowbridge.rs
+++ b/cumulus/parachains/runtimes/bridge-hubs/bridge-hub-rococo/tests/snowbridge.rs
@@ -18,7 +18,6 @@
 
 use bp_polkadot_core::Signature;
 use bridge_hub_rococo_runtime::{
-	bridge_to_bulletin_config::OnBridgeHubRococoRefundRococoBulletinMessages,
 	bridge_to_westend_config::OnBridgeHubRococoRefundBridgeHubWestendMessages,
 	xcm_config::XcmConfig, AllPalletsWithoutSystem, BridgeRejectObsoleteHeadersAndMessages,
 	Executive, MessageQueueServiceWeight, Runtime, RuntimeCall, RuntimeEvent, SessionKeys,
@@ -183,10 +182,7 @@ fn construct_extrinsic(
 		frame_system::CheckWeight::<Runtime>::new(),
 		pallet_transaction_payment::ChargeTransactionPayment::<Runtime>::from(0),
 		BridgeRejectObsoleteHeadersAndMessages::default(),
-		(
-			OnBridgeHubRococoRefundBridgeHubWestendMessages::default(),
-			OnBridgeHubRococoRefundRococoBulletinMessages::default(),
-		),
+		(OnBridgeHubRococoRefundBridgeHubWestendMessages::default(),),
 		cumulus_primitives_storage_weight_reclaim::StorageWeightReclaim::new(),
 		frame_metadata_hash_extension::CheckMetadataHash::<Runtime>::new(false),
 	);
diff --git a/cumulus/parachains/runtimes/bridge-hubs/bridge-hub-rococo/tests/tests.rs b/cumulus/parachains/runtimes/bridge-hubs/bridge-hub-rococo/tests/tests.rs
index 982c9fec6634496bd079b16177003ba9a5cf27cc..002e31174cb14c48f55180b22d5716340cd7c08b 100644
--- a/cumulus/parachains/runtimes/bridge-hubs/bridge-hub-rococo/tests/tests.rs
+++ b/cumulus/parachains/runtimes/bridge-hubs/bridge-hub-rococo/tests/tests.rs
@@ -62,10 +62,7 @@ fn construct_extrinsic(
 		frame_system::CheckWeight::<Runtime>::new(),
 		pallet_transaction_payment::ChargeTransactionPayment::<Runtime>::from(0),
 		BridgeRejectObsoleteHeadersAndMessages::default(),
-		(
-			bridge_to_westend_config::OnBridgeHubRococoRefundBridgeHubWestendMessages::default(),
-			bridge_to_bulletin_config::OnBridgeHubRococoRefundRococoBulletinMessages::default(),
-		),
+		(bridge_to_westend_config::OnBridgeHubRococoRefundBridgeHubWestendMessages::default(),),
 		cumulus_primitives_storage_weight_reclaim::StorageWeightReclaim::new(),
 		frame_metadata_hash_extension::CheckMetadataHash::new(false),
 	);
@@ -145,8 +142,10 @@ fn change_required_stake_by_governance_works() {
 
 mod bridge_hub_westend_tests {
 	use super::*;
+	use bp_messages::LegacyLaneId;
 	use bridge_common_config::{
 		BridgeGrandpaWestendInstance, BridgeParachainWestendInstance, DeliveryRewardInBalance,
+		RelayersForLegacyLaneIdsMessagesInstance,
 	};
 	use bridge_hub_test_utils::test_cases::from_parachain;
 	use bridge_to_westend_config::{
@@ -174,6 +173,7 @@ mod bridge_hub_westend_tests {
 		BridgeGrandpaWestendInstance,
 		BridgeParachainWestendInstance,
 		WithBridgeHubWestendMessagesInstance,
+		RelayersForLegacyLaneIdsMessagesInstance,
 	>;
 
 	#[test]
@@ -338,7 +338,16 @@ mod bridge_hub_westend_tests {
 					XcmOverBridgeHubWestendInstance,
 					LocationToAccountId,
 					TokenLocation,
-				>(SiblingParachainLocation::get(), BridgedUniversalLocation::get()).1
+				>(
+					SiblingParachainLocation::get(),
+					BridgedUniversalLocation::get(),
+					|locations, fee| {
+						bridge_hub_test_utils::open_bridge_with_storage::<
+							Runtime,
+							XcmOverBridgeHubWestendInstance
+						>(locations, fee, LegacyLaneId([0, 0, 0, 1]))
+					}
+				).1
 			},
 		)
 	}
@@ -393,10 +402,20 @@ mod bridge_hub_westend_tests {
 					XcmOverBridgeHubWestendInstance,
 					LocationToAccountId,
 					TokenLocation,
-				>(SiblingParachainLocation::get(), BridgedUniversalLocation::get())
+				>(
+					SiblingParachainLocation::get(),
+					BridgedUniversalLocation::get(),
+					|locations, fee| {
+						bridge_hub_test_utils::open_bridge_with_storage::<
+							Runtime,
+							XcmOverBridgeHubWestendInstance,
+						>(locations, fee, LegacyLaneId([0, 0, 0, 1]))
+					},
+				)
 				.1
 			},
 			construct_and_apply_extrinsic,
+			true,
 		)
 	}
 
@@ -417,10 +436,20 @@ mod bridge_hub_westend_tests {
 					XcmOverBridgeHubWestendInstance,
 					LocationToAccountId,
 					TokenLocation,
-				>(SiblingParachainLocation::get(), BridgedUniversalLocation::get())
+				>(
+					SiblingParachainLocation::get(),
+					BridgedUniversalLocation::get(),
+					|locations, fee| {
+						bridge_hub_test_utils::open_bridge_with_storage::<
+							Runtime,
+							XcmOverBridgeHubWestendInstance,
+						>(locations, fee, LegacyLaneId([0, 0, 0, 1]))
+					},
+				)
 				.1
 			},
 			construct_and_apply_extrinsic,
+			false,
 		)
 	}
 
@@ -482,30 +511,13 @@ mod bridge_hub_westend_tests {
 			),
 		)
 	}
-
-	#[test]
-	fn open_and_close_bridge_works() {
-		let origins = [SiblingParachainLocation::get(), SiblingSystemParachainLocation::get()];
-
-		for origin in origins {
-			bridge_hub_test_utils::test_cases::open_and_close_bridge_works::<
-				Runtime,
-				XcmOverBridgeHubWestendInstance,
-				LocationToAccountId,
-				TokenLocation,
-			>(
-				collator_session_keys(),
-				bp_bridge_hub_rococo::BRIDGE_HUB_ROCOCO_PARACHAIN_ID,
-				origin,
-				BridgedUniversalLocation::get(),
-			)
-		}
-	}
 }
 
 mod bridge_hub_bulletin_tests {
 	use super::*;
+	use bp_messages::{HashedLaneId, LaneIdType};
 	use bridge_common_config::BridgeGrandpaRococoBulletinInstance;
+	use bridge_hub_rococo_runtime::bridge_common_config::RelayersForPermissionlessLanesInstance;
 	use bridge_hub_test_utils::test_cases::from_grandpa_chain;
 	use bridge_to_bulletin_config::{
 		RococoBulletinGlobalConsensusNetwork, RococoBulletinGlobalConsensusNetworkLocation,
@@ -527,6 +539,7 @@ mod bridge_hub_bulletin_tests {
 		AllPalletsWithoutSystem,
 		BridgeGrandpaRococoBulletinInstance,
 		WithRococoBulletinMessagesInstance,
+		RelayersForPermissionlessLanesInstance,
 	>;
 
 	#[test]
@@ -589,7 +602,16 @@ mod bridge_hub_bulletin_tests {
 					XcmOverPolkadotBulletinInstance,
 					LocationToAccountId,
 					TokenLocation,
-				>(SiblingPeopleParachainLocation::get(), BridgedBulletinLocation::get()).1
+				>(
+					SiblingPeopleParachainLocation::get(),
+					BridgedBulletinLocation::get(),
+					|locations, fee| {
+						bridge_hub_test_utils::open_bridge_with_storage::<
+							Runtime,
+							XcmOverPolkadotBulletinInstance
+						>(locations, fee, HashedLaneId::try_new(1, 2).unwrap())
+					}
+				).1
 			},
 		)
 	}
@@ -643,10 +665,20 @@ mod bridge_hub_bulletin_tests {
 					XcmOverPolkadotBulletinInstance,
 					LocationToAccountId,
 					TokenLocation,
-				>(SiblingPeopleParachainLocation::get(), BridgedBulletinLocation::get())
+				>(
+					SiblingPeopleParachainLocation::get(),
+					BridgedBulletinLocation::get(),
+					|locations, fee| {
+						bridge_hub_test_utils::open_bridge_with_storage::<
+							Runtime,
+							XcmOverPolkadotBulletinInstance,
+						>(locations, fee, HashedLaneId::try_new(1, 2).unwrap())
+					},
+				)
 				.1
 			},
 			construct_and_apply_extrinsic,
+			false,
 		)
 	}
 
@@ -666,29 +698,20 @@ mod bridge_hub_bulletin_tests {
 					XcmOverPolkadotBulletinInstance,
 					LocationToAccountId,
 					TokenLocation,
-				>(SiblingPeopleParachainLocation::get(), BridgedBulletinLocation::get())
+				>(
+					SiblingPeopleParachainLocation::get(),
+					BridgedBulletinLocation::get(),
+					|locations, fee| {
+						bridge_hub_test_utils::open_bridge_with_storage::<
+							Runtime,
+							XcmOverPolkadotBulletinInstance,
+						>(locations, fee, HashedLaneId::try_new(1, 2).unwrap())
+					},
+				)
 				.1
 			},
 			construct_and_apply_extrinsic,
+			false,
 		)
 	}
-
-	#[test]
-	fn open_and_close_bridge_works() {
-		let origins = [SiblingPeopleParachainLocation::get()];
-
-		for origin in origins {
-			bridge_hub_test_utils::test_cases::open_and_close_bridge_works::<
-				Runtime,
-				XcmOverPolkadotBulletinInstance,
-				LocationToAccountId,
-				TokenLocation,
-			>(
-				collator_session_keys(),
-				bp_bridge_hub_rococo::BRIDGE_HUB_ROCOCO_PARACHAIN_ID,
-				origin,
-				BridgedBulletinLocation::get(),
-			)
-		}
-	}
 }
diff --git a/cumulus/parachains/runtimes/bridge-hubs/bridge-hub-westend/src/bridge_common_config.rs b/cumulus/parachains/runtimes/bridge-hubs/bridge-hub-westend/src/bridge_common_config.rs
index 9bae106395a62a9d1cb6861ed3099b5f3bf963cc..0872d0498f85789edb4c105831754f6e0f406bf1 100644
--- a/cumulus/parachains/runtimes/bridge-hubs/bridge-hub-westend/src/bridge_common_config.rs
+++ b/cumulus/parachains/runtimes/bridge-hubs/bridge-hub-westend/src/bridge_common_config.rs
@@ -22,6 +22,7 @@
 //! GRANDPA tracking pallet only needs to be aware of one chain.
 
 use super::{weights, AccountId, Balance, Balances, BlockNumber, Runtime, RuntimeEvent};
+use bp_messages::LegacyLaneId;
 use frame_support::parameter_types;
 
 parameter_types! {
@@ -33,11 +34,15 @@ parameter_types! {
 }
 
 /// Allows collect and claim rewards for relayers
-impl pallet_bridge_relayers::Config for Runtime {
+pub type RelayersForLegacyLaneIdsMessagesInstance = ();
+impl pallet_bridge_relayers::Config<RelayersForLegacyLaneIdsMessagesInstance> for Runtime {
 	type RuntimeEvent = RuntimeEvent;
 	type Reward = Balance;
-	type PaymentProcedure =
-		bp_relayers::PayRewardFromAccount<pallet_balances::Pallet<Runtime>, AccountId>;
+	type PaymentProcedure = bp_relayers::PayRewardFromAccount<
+		pallet_balances::Pallet<Runtime>,
+		AccountId,
+		Self::LaneId,
+	>;
 	type StakeAndSlash = pallet_bridge_relayers::StakeAndSlashNamed<
 		AccountId,
 		BlockNumber,
@@ -47,4 +52,5 @@ impl pallet_bridge_relayers::Config for Runtime {
 		RelayerStakeLease,
 	>;
 	type WeightInfo = weights::pallet_bridge_relayers::WeightInfo<Runtime>;
+	type LaneId = LegacyLaneId;
 }
diff --git a/cumulus/parachains/runtimes/bridge-hubs/bridge-hub-westend/src/bridge_to_rococo_config.rs b/cumulus/parachains/runtimes/bridge-hubs/bridge-hub-westend/src/bridge_to_rococo_config.rs
index 2d9e8f6642768a934be275a9b5602f13d81513f4..e45654bc62bd23f32f03c342ee3eb37f4c8ad1a5 100644
--- a/cumulus/parachains/runtimes/bridge-hubs/bridge-hub-westend/src/bridge_to_rococo_config.rs
+++ b/cumulus/parachains/runtimes/bridge-hubs/bridge-hub-westend/src/bridge_to_rococo_config.rs
@@ -17,13 +17,15 @@
 //! Bridge definitions used on BridgeHub with the Westend flavor.
 
 use crate::{
-	bridge_common_config::DeliveryRewardInBalance, weights, xcm_config::UniversalLocation,
+	bridge_common_config::{DeliveryRewardInBalance, RelayersForLegacyLaneIdsMessagesInstance},
+	weights,
+	xcm_config::UniversalLocation,
 	AccountId, Balance, Balances, BridgeRococoMessages, PolkadotXcm, Runtime, RuntimeEvent,
 	RuntimeHoldReason, XcmOverBridgeHubRococo, XcmRouter,
 };
 use bp_messages::{
 	source_chain::FromBridgedChainMessagesDeliveryProof,
-	target_chain::FromBridgedChainMessagesProof,
+	target_chain::FromBridgedChainMessagesProof, LegacyLaneId,
 };
 use bp_parachains::SingleParaStoredHeaderDataBuilder;
 use bridge_hub_common::xcm_version::XcmVersionOfDestAndRemoteBridge;
@@ -33,14 +35,12 @@ use frame_support::{
 	parameter_types,
 	traits::{ConstU32, PalletInfoAccess},
 };
-use frame_system::EnsureRoot;
+use frame_system::{EnsureNever, EnsureRoot};
+use pallet_bridge_messages::LaneIdOf;
 use pallet_bridge_relayers::extension::{
 	BridgeRelayersSignedExtension, WithMessagesExtensionConfig,
 };
-use pallet_xcm::EnsureXcm;
-use parachains_common::xcm_config::{
-	AllSiblingSystemParachains, ParentRelayOrSiblingParachains, RelayOrOtherSystemParachains,
-};
+use parachains_common::xcm_config::{AllSiblingSystemParachains, RelayOrOtherSystemParachains};
 use polkadot_parachain_primitives::primitives::Sibling;
 use testnet_parachains_constants::westend::currency::UNITS as WND;
 use xcm::{
@@ -81,11 +81,11 @@ parameter_types! {
 }
 
 /// Proof of messages, coming from Rococo.
-pub type FromRococoBridgeHubMessagesProof =
-	FromBridgedChainMessagesProof<bp_bridge_hub_rococo::Hash>;
+pub type FromRococoBridgeHubMessagesProof<MI> =
+	FromBridgedChainMessagesProof<bp_bridge_hub_rococo::Hash, LaneIdOf<Runtime, MI>>;
 /// Messages delivery proof for Rococo Bridge Hub -> Westend Bridge Hub messages.
-pub type ToRococoBridgeHubMessagesDeliveryProof =
-	FromBridgedChainMessagesDeliveryProof<bp_bridge_hub_rococo::Hash>;
+pub type ToRococoBridgeHubMessagesDeliveryProof<MI> =
+	FromBridgedChainMessagesDeliveryProof<bp_bridge_hub_rococo::Hash, LaneIdOf<Runtime, MI>>;
 
 /// Dispatches received XCM messages from other bridge
 type FromRococoMessageBlobDispatcher =
@@ -98,8 +98,10 @@ pub type OnBridgeHubWestendRefundBridgeHubRococoMessages = BridgeRelayersSignedE
 		StrOnBridgeHubWestendRefundBridgeHubRococoMessages,
 		Runtime,
 		WithBridgeHubRococoMessagesInstance,
+		RelayersForLegacyLaneIdsMessagesInstance,
 		PriorityBoostPerMessage,
 	>,
+	LaneIdOf<Runtime, WithBridgeHubRococoMessagesInstance>,
 >;
 bp_runtime::generate_static_str_provider!(OnBridgeHubWestendRefundBridgeHubRococoMessages);
 
@@ -142,10 +144,10 @@ impl pallet_bridge_messages::Config<WithBridgeHubRococoMessagesInstance> for Run
 	>;
 
 	type OutboundPayload = XcmAsPlainPayload;
-
 	type InboundPayload = XcmAsPlainPayload;
-	type DeliveryPayments = ();
+	type LaneId = LegacyLaneId;
 
+	type DeliveryPayments = ();
 	type DeliveryConfirmationPayments = pallet_bridge_relayers::DeliveryConfirmationPaymentsAdapter<
 		Runtime,
 		WithBridgeHubRococoMessagesInstance,
@@ -168,9 +170,9 @@ impl pallet_xcm_bridge_hub::Config<XcmOverBridgeHubRococoInstance> for Runtime {
 	type MessageExportPrice = ();
 	type DestinationVersion = XcmVersionOfDestAndRemoteBridge<PolkadotXcm, BridgeHubRococoLocation>;
 
-	type AdminOrigin = EnsureRoot<AccountId>;
-	// Only allow calls from relay chains and sibling parachains to directly open the bridge.
-	type OpenBridgeOrigin = EnsureXcm<ParentRelayOrSiblingParachains>;
+	type ForceOrigin = EnsureRoot<AccountId>;
+	// We don't want to allow creating bridges for this instance with `LegacyLaneId`.
+	type OpenBridgeOrigin = EnsureNever<Location>;
 	// Converter aligned with `OpenBridgeOrigin`.
 	type BridgeOriginAccountIdConverter =
 		(ParentIsPreset<AccountId>, SiblingParachainConvertsVia<Sibling, AccountId>);
@@ -188,14 +190,20 @@ impl pallet_xcm_bridge_hub::Config<XcmOverBridgeHubRococoInstance> for Runtime {
 }
 
 #[cfg(feature = "runtime-benchmarks")]
-pub(crate) fn open_bridge_for_benchmarks(
-	with: bp_messages::LaneId,
+pub(crate) fn open_bridge_for_benchmarks<R, XBHI, C>(
+	with: pallet_xcm_bridge_hub::LaneIdOf<R, XBHI>,
 	sibling_para_id: u32,
-) -> InteriorLocation {
+) -> InteriorLocation
+where
+	R: pallet_xcm_bridge_hub::Config<XBHI>,
+	XBHI: 'static,
+	C: xcm_executor::traits::ConvertLocation<
+		bp_runtime::AccountIdOf<pallet_xcm_bridge_hub::ThisChainOf<R, XBHI>>,
+	>,
+{
 	use pallet_xcm_bridge_hub::{Bridge, BridgeId, BridgeState};
 	use sp_runtime::traits::Zero;
 	use xcm::VersionedInteriorLocation;
-	use xcm_executor::traits::ConvertLocation;
 
 	// insert bridge metadata
 	let lane_id = with;
@@ -205,7 +213,7 @@ pub(crate) fn open_bridge_for_benchmarks(
 	let bridge_id = BridgeId::new(&universal_source, &universal_destination);
 
 	// insert only bridge metadata, because the benchmarks create lanes
-	pallet_xcm_bridge_hub::Bridges::<Runtime, XcmOverBridgeHubRococoInstance>::insert(
+	pallet_xcm_bridge_hub::Bridges::<R, XBHI>::insert(
 		bridge_id,
 		Bridge {
 			bridge_origin_relative_location: alloc::boxed::Box::new(
@@ -218,17 +226,12 @@ pub(crate) fn open_bridge_for_benchmarks(
 				VersionedInteriorLocation::from(universal_destination),
 			),
 			state: BridgeState::Opened,
-			bridge_owner_account: crate::xcm_config::LocationToAccountId::convert_location(
-				&sibling_parachain,
-			)
-			.expect("valid AccountId"),
-			deposit: Balance::zero(),
+			bridge_owner_account: C::convert_location(&sibling_parachain).expect("valid AccountId"),
+			deposit: Zero::zero(),
 			lane_id,
 		},
 	);
-	pallet_xcm_bridge_hub::LaneToBridge::<Runtime, XcmOverBridgeHubRococoInstance>::insert(
-		lane_id, bridge_id,
-	);
+	pallet_xcm_bridge_hub::LaneToBridge::<R, XBHI>::insert(lane_id, bridge_id);
 
 	universal_source
 }
@@ -327,12 +330,11 @@ mod tests {
 /// Contains the migration for the AssetHubWestend<>AssetHubRococo bridge.
 pub mod migration {
 	use super::*;
-	use bp_messages::LaneId;
+	use bp_messages::LegacyLaneId;
 	use frame_support::traits::ConstBool;
-	use sp_runtime::Either;
 
 	parameter_types! {
-		pub AssetHubWestendToAssetHubRococoMessagesLane: LaneId = LaneId::from_inner(Either::Right([0, 0, 0, 2]));
+		pub AssetHubWestendToAssetHubRococoMessagesLane: LegacyLaneId = LegacyLaneId([0, 0, 0, 2]);
 		pub AssetHubWestendLocation: Location = Location::new(1, [Parachain(bp_asset_hub_westend::ASSET_HUB_WESTEND_PARACHAIN_ID)]);
 		pub AssetHubRococoUniversalLocation: InteriorLocation = [GlobalConsensus(RococoGlobalConsensusNetwork::get()), Parachain(bp_asset_hub_rococo::ASSET_HUB_ROCOCO_PARACHAIN_ID)].into();
 	}
@@ -348,4 +350,75 @@ pub mod migration {
 		AssetHubWestendLocation,
 		AssetHubRococoUniversalLocation,
 	>;
+
+	mod v1_wrong {
+		use bp_messages::{LaneState, MessageNonce, UnrewardedRelayer};
+		use bp_runtime::AccountIdOf;
+		use codec::{Decode, Encode};
+		use pallet_bridge_messages::BridgedChainOf;
+		use sp_std::collections::vec_deque::VecDeque;
+
+		#[derive(Encode, Decode, Clone, PartialEq, Eq)]
+		pub(crate) struct StoredInboundLaneData<T: pallet_bridge_messages::Config<I>, I: 'static>(
+			pub(crate) InboundLaneData<AccountIdOf<BridgedChainOf<T, I>>>,
+		);
+		#[derive(Encode, Decode, Clone, PartialEq, Eq)]
+		pub(crate) struct InboundLaneData<RelayerId> {
+			pub state: LaneState,
+			pub(crate) relayers: VecDeque<UnrewardedRelayer<RelayerId>>,
+			pub(crate) last_confirmed_nonce: MessageNonce,
+		}
+		#[derive(Encode, Decode, Clone, PartialEq, Eq)]
+		pub(crate) struct OutboundLaneData {
+			pub state: LaneState,
+			pub(crate) oldest_unpruned_nonce: MessageNonce,
+			pub(crate) latest_received_nonce: MessageNonce,
+			pub(crate) latest_generated_nonce: MessageNonce,
+		}
+	}
+
+	mod v1 {
+		pub use bp_messages::{InboundLaneData, LaneState, OutboundLaneData};
+		pub use pallet_bridge_messages::{InboundLanes, OutboundLanes, StoredInboundLaneData};
+	}
+
+	/// Fix for v1 migration - corrects data for OutboundLaneData/InboundLaneData (it is needed only
+	/// for Rococo/Westend).
+	pub struct FixMessagesV1Migration<T, I>(sp_std::marker::PhantomData<(T, I)>);
+
+	impl<T: pallet_bridge_messages::Config<I>, I: 'static> frame_support::traits::OnRuntimeUpgrade
+		for FixMessagesV1Migration<T, I>
+	{
+		fn on_runtime_upgrade() -> Weight {
+			use sp_core::Get;
+			let mut weight = T::DbWeight::get().reads(1);
+
+			// `InboundLanes` - add state to the old structs
+			let translate_inbound =
+				|pre: v1_wrong::StoredInboundLaneData<T, I>| -> Option<v1::StoredInboundLaneData<T, I>> {
+					weight.saturating_accrue(T::DbWeight::get().reads_writes(1, 1));
+					Some(v1::StoredInboundLaneData(v1::InboundLaneData {
+						state: v1::LaneState::Opened,
+						relayers: pre.0.relayers,
+						last_confirmed_nonce: pre.0.last_confirmed_nonce,
+					}))
+				};
+			v1::InboundLanes::<T, I>::translate_values(translate_inbound);
+
+			// `OutboundLanes` - add state to the old structs
+			let translate_outbound =
+				|pre: v1_wrong::OutboundLaneData| -> Option<v1::OutboundLaneData> {
+					weight.saturating_accrue(T::DbWeight::get().reads_writes(1, 1));
+					Some(v1::OutboundLaneData {
+						state: v1::LaneState::Opened,
+						oldest_unpruned_nonce: pre.oldest_unpruned_nonce,
+						latest_received_nonce: pre.latest_received_nonce,
+						latest_generated_nonce: pre.latest_generated_nonce,
+					})
+				};
+			v1::OutboundLanes::<T, I>::translate_values(translate_outbound);
+
+			weight
+		}
+	}
 }
diff --git a/cumulus/parachains/runtimes/bridge-hubs/bridge-hub-westend/src/genesis_config_presets.rs b/cumulus/parachains/runtimes/bridge-hubs/bridge-hub-westend/src/genesis_config_presets.rs
index 4c948656d9f80871013d55f8e536e244b65409dc..0b270e5843391f7bba071ccbf1b40c96fff78ed5 100644
--- a/cumulus/parachains/runtimes/bridge-hubs/bridge-hub-westend/src/genesis_config_presets.rs
+++ b/cumulus/parachains/runtimes/bridge-hubs/bridge-hub-westend/src/genesis_config_presets.rs
@@ -31,6 +31,7 @@ fn bridge_hub_westend_genesis(
 	id: ParaId,
 	bridges_pallet_owner: Option<AccountId>,
 	asset_hub_para_id: ParaId,
+	opened_bridges: Vec<(Location, InteriorLocation, Option<bp_messages::LegacyLaneId>)>,
 ) -> serde_json::Value {
 	let config = RuntimeGenesisConfig {
 		balances: BalancesConfig {
@@ -71,6 +72,10 @@ fn bridge_hub_westend_genesis(
 			owner: bridges_pallet_owner.clone(),
 			..Default::default()
 		},
+		xcm_over_bridge_hub_rococo: XcmOverBridgeHubRococoConfig {
+			opened_bridges,
+			..Default::default()
+		},
 		ethereum_system: EthereumSystemConfig {
 			para_id: id,
 			asset_hub_para_id,
@@ -114,6 +119,11 @@ pub fn get_preset(id: &sp_genesis_builder::PresetId) -> Option<sp_std::vec::Vec<
 			1002.into(),
 			Some(get_account_id_from_seed::<sr25519::Public>("Bob")),
 			westend_runtime_constants::system_parachain::ASSET_HUB_ID.into(),
+			vec![(
+				Location::new(1, [Parachain(1000)]),
+				Junctions::from([Rococo.into(), Parachain(1000)]),
+				Some(bp_messages::LegacyLaneId([0, 0, 0, 2])),
+			)],
 		),
 		Ok(sp_genesis_builder::DEV_RUNTIME_PRESET) => bridge_hub_westend_genesis(
 			// initial collators.
@@ -144,6 +154,7 @@ pub fn get_preset(id: &sp_genesis_builder::PresetId) -> Option<sp_std::vec::Vec<
 			1002.into(),
 			Some(get_account_id_from_seed::<sr25519::Public>("Bob")),
 			westend_runtime_constants::system_parachain::ASSET_HUB_ID.into(),
+			vec![],
 		),
 		_ => return None,
 	};
diff --git a/cumulus/parachains/runtimes/bridge-hubs/bridge-hub-westend/src/lib.rs b/cumulus/parachains/runtimes/bridge-hubs/bridge-hub-westend/src/lib.rs
index 5c40506442dfa29b9490a21dd05666683ada766c..87152d30977fb6f203af30463abbd8abcb4f836a 100644
--- a/cumulus/parachains/runtimes/bridge-hubs/bridge-hub-westend/src/lib.rs
+++ b/cumulus/parachains/runtimes/bridge-hubs/bridge-hub-westend/src/lib.rs
@@ -82,6 +82,7 @@ use xcm_runtime_apis::{
 };
 
 use bp_runtime::HeaderId;
+use pallet_bridge_messages::LaneIdOf;
 #[cfg(any(feature = "std", test))]
 pub use sp_runtime::BuildStorage;
 
@@ -147,12 +148,17 @@ pub type Migrations = (
 		Runtime,
 		bridge_to_rococo_config::WithBridgeHubRococoMessagesInstance,
 	>,
+	bridge_to_rococo_config::migration::FixMessagesV1Migration<
+		Runtime,
+		bridge_to_rococo_config::WithBridgeHubRococoMessagesInstance,
+	>,
 	bridge_to_rococo_config::migration::StaticToDynamicLanes,
 	frame_support::migrations::RemoveStorage<
 		BridgeRococoMessagesPalletName,
 		OutboundLanesCongestedSignalsKey,
 		RocksDbWeight,
 	>,
+	pallet_bridge_relayers::migration::v1::MigrationToV1<Runtime, ()>,
 	// permanent
 	pallet_xcm::migration::MigrateToLatestXcmVersion<Runtime>,
 	snowbridge_pallet_system::migration::v0::InitializeOnUpgrade<
@@ -851,7 +857,7 @@ impl_runtime_apis! {
 
 	impl bp_bridge_hub_rococo::FromBridgeHubRococoInboundLaneApi<Block> for Runtime {
 		fn message_details(
-			lane: bp_messages::LaneId,
+			lane: LaneIdOf<Runtime, bridge_to_rococo_config::WithBridgeHubRococoMessagesInstance>,
 			messages: Vec<(bp_messages::MessagePayload, bp_messages::OutboundMessageDetails)>,
 		) -> Vec<bp_messages::InboundMessageDetails> {
 			bridge_runtime_common::messages_api::inbound_message_details::<
@@ -863,7 +869,7 @@ impl_runtime_apis! {
 
 	impl bp_bridge_hub_rococo::ToBridgeHubRococoOutboundLaneApi<Block> for Runtime {
 		fn message_details(
-			lane: bp_messages::LaneId,
+			lane: LaneIdOf<Runtime, bridge_to_rococo_config::WithBridgeHubRococoMessagesInstance>,
 			begin: bp_messages::MessageNonce,
 			end: bp_messages::MessageNonce,
 		) -> Vec<bp_messages::OutboundMessageDetails> {
@@ -1141,15 +1147,20 @@ impl_runtime_apis! {
 					);
 
 					// open bridge
-					let origin = RuntimeOrigin::from(pallet_xcm::Origin::Xcm(sibling_parachain_location.clone()));
-					XcmOverBridgeHubRococo::open_bridge(
-						origin.clone(),
-						alloc::boxed::Box::new(VersionedInteriorLocation::from([GlobalConsensus(NetworkId::Rococo), Parachain(8765)])),
+					let bridge_destination_universal_location: InteriorLocation = [GlobalConsensus(NetworkId::Rococo), Parachain(8765)].into();
+					let locations = XcmOverBridgeHubRococo::bridge_locations(
+						sibling_parachain_location.clone(),
+						bridge_destination_universal_location.clone(),
+					)?;
+					XcmOverBridgeHubRococo::do_open_bridge(
+						locations,
+						bp_messages::LegacyLaneId([1, 2, 3, 4]),
+						true,
 					).map_err(|e| {
 						log::error!(
 							"Failed to `XcmOverBridgeHubRococo::open_bridge`({:?}, {:?})`, error: {:?}",
-							origin,
-							[GlobalConsensus(NetworkId::Rococo), Parachain(8765)],
+							sibling_parachain_location,
+							bridge_destination_universal_location,
 							e
 						);
 						BenchmarkError::Stop("Bridge was not opened!")
@@ -1203,12 +1214,16 @@ impl_runtime_apis! {
 				}
 
 				fn prepare_message_proof(
-					params: MessageProofParams,
-				) -> (bridge_to_rococo_config::FromRococoBridgeHubMessagesProof, Weight) {
+					params: MessageProofParams<LaneIdOf<Runtime, bridge_to_rococo_config::WithBridgeHubRococoMessagesInstance>>,
+				) -> (bridge_to_rococo_config::FromRococoBridgeHubMessagesProof<bridge_to_rococo_config::WithBridgeHubRococoMessagesInstance>, Weight) {
 					use cumulus_primitives_core::XcmpMessageSource;
 					assert!(XcmpQueue::take_outbound_messages(usize::MAX).is_empty());
 					ParachainSystem::open_outbound_hrmp_channel_for_benchmarks_or_tests(42.into());
-					let universal_source = bridge_to_rococo_config::open_bridge_for_benchmarks(params.lane, 42);
+					let universal_source = bridge_to_rococo_config::open_bridge_for_benchmarks::<
+						Runtime,
+						bridge_to_rococo_config::XcmOverBridgeHubRococoInstance,
+						xcm_config::LocationToAccountId,
+					>(params.lane, 42);
 					prepare_message_proof_from_parachain::<
 						Runtime,
 						bridge_to_rococo_config::BridgeGrandpaRococoInstance,
@@ -1217,9 +1232,13 @@ impl_runtime_apis! {
 				}
 
 				fn prepare_message_delivery_proof(
-					params: MessageDeliveryProofParams<AccountId>,
-				) -> bridge_to_rococo_config::ToRococoBridgeHubMessagesDeliveryProof {
-					let _ = bridge_to_rococo_config::open_bridge_for_benchmarks(params.lane, 42);
+					params: MessageDeliveryProofParams<AccountId, LaneIdOf<Runtime, bridge_to_rococo_config::WithBridgeHubRococoMessagesInstance>>,
+				) -> bridge_to_rococo_config::ToRococoBridgeHubMessagesDeliveryProof<bridge_to_rococo_config::WithBridgeHubRococoMessagesInstance> {
+					let _ = bridge_to_rococo_config::open_bridge_for_benchmarks::<
+						Runtime,
+						bridge_to_rococo_config::XcmOverBridgeHubRococoInstance,
+						xcm_config::LocationToAccountId,
+					>(params.lane, 42);
 					prepare_message_delivery_proof_from_parachain::<
 						Runtime,
 						bridge_to_rococo_config::BridgeGrandpaRococoInstance,
@@ -1264,14 +1283,15 @@ impl_runtime_apis! {
 				}
 			}
 
-			impl BridgeRelayersConfig for Runtime {
+			impl BridgeRelayersConfig<bridge_common_config::RelayersForLegacyLaneIdsMessagesInstance> for Runtime {
 				fn prepare_rewards_account(
-					account_params: bp_relayers::RewardsAccountParams,
+					account_params: bp_relayers::RewardsAccountParams<<Self as pallet_bridge_relayers::Config<bridge_common_config::RelayersForLegacyLaneIdsMessagesInstance>>::LaneId>,
 					reward: Balance,
 				) {
 					let rewards_account = bp_relayers::PayRewardFromAccount::<
 						Balances,
-						AccountId
+						AccountId,
+						<Self as pallet_bridge_relayers::Config<bridge_common_config::RelayersForLegacyLaneIdsMessagesInstance>>::LaneId,
 					>::rewards_account(account_params);
 					Self::deposit_account(rewards_account, reward);
 				}
diff --git a/cumulus/parachains/runtimes/bridge-hubs/bridge-hub-westend/tests/tests.rs b/cumulus/parachains/runtimes/bridge-hubs/bridge-hub-westend/tests/tests.rs
index 4391b069cf093fe6d2347b081d50fa7d898ffa2f..4ff388f4ba2b87909e48aa2dabfe3dc37f1ad29c 100644
--- a/cumulus/parachains/runtimes/bridge-hubs/bridge-hub-westend/tests/tests.rs
+++ b/cumulus/parachains/runtimes/bridge-hubs/bridge-hub-westend/tests/tests.rs
@@ -16,8 +16,12 @@
 
 #![cfg(test)]
 
+use bp_messages::LegacyLaneId;
 use bp_polkadot_core::Signature;
-use bridge_common_config::{DeliveryRewardInBalance, RequiredStakeForStakeAndSlash};
+use bridge_common_config::{
+	DeliveryRewardInBalance, RelayersForLegacyLaneIdsMessagesInstance,
+	RequiredStakeForStakeAndSlash,
+};
 use bridge_hub_test_utils::{test_cases::from_parachain, SlotDurations};
 use bridge_hub_westend_runtime::{
 	bridge_common_config, bridge_to_rococo_config,
@@ -63,6 +67,7 @@ type RuntimeTestsAdapter = from_parachain::WithRemoteParachainHelperAdapter<
 	BridgeGrandpaRococoInstance,
 	BridgeParachainRococoInstance,
 	WithBridgeHubRococoMessagesInstance,
+	RelayersForLegacyLaneIdsMessagesInstance,
 >;
 
 parameter_types! {
@@ -235,7 +240,15 @@ fn handle_export_message_from_system_parachain_add_to_outbound_queue_works() {
 					XcmOverBridgeHubRococoInstance,
 					LocationToAccountId,
 					WestendLocation,
-				>(SiblingParachainLocation::get(), BridgedUniversalLocation::get()).1
+				>(
+					SiblingParachainLocation::get(),
+					BridgedUniversalLocation::get(),
+					|locations, fee| {
+						bridge_hub_test_utils::open_bridge_with_storage::<
+							Runtime, XcmOverBridgeHubRococoInstance
+						>(locations, fee, LegacyLaneId([0, 0, 0, 1]))
+					}
+				).1
 			},
 		)
 }
@@ -288,10 +301,16 @@ fn relayed_incoming_message_works() {
 				XcmOverBridgeHubRococoInstance,
 				LocationToAccountId,
 				WestendLocation,
-			>(SiblingParachainLocation::get(), BridgedUniversalLocation::get())
+			>(SiblingParachainLocation::get(), BridgedUniversalLocation::get(), |locations, fee| {
+				bridge_hub_test_utils::open_bridge_with_storage::<
+					Runtime,
+					XcmOverBridgeHubRococoInstance,
+				>(locations, fee, LegacyLaneId([0, 0, 0, 1]))
+			})
 			.1
 		},
 		construct_and_apply_extrinsic,
+		true,
 	)
 }
 
@@ -312,10 +331,16 @@ fn free_relay_extrinsic_works() {
 				XcmOverBridgeHubRococoInstance,
 				LocationToAccountId,
 				WestendLocation,
-			>(SiblingParachainLocation::get(), BridgedUniversalLocation::get())
+			>(SiblingParachainLocation::get(), BridgedUniversalLocation::get(), |locations, fee| {
+				bridge_hub_test_utils::open_bridge_with_storage::<
+					Runtime,
+					XcmOverBridgeHubRococoInstance,
+				>(locations, fee, LegacyLaneId([0, 0, 0, 1]))
+			})
 			.1
 		},
 		construct_and_apply_extrinsic,
+		true,
 	)
 }
 
@@ -377,22 +402,3 @@ pub fn can_calculate_fee_for_standalone_message_confirmation_transaction() {
 		),
 	)
 }
-
-#[test]
-fn open_and_close_bridge_works() {
-	let origins = [SiblingParachainLocation::get(), SiblingSystemParachainLocation::get()];
-
-	for origin in origins {
-		bridge_hub_test_utils::test_cases::open_and_close_bridge_works::<
-			Runtime,
-			XcmOverBridgeHubRococoInstance,
-			LocationToAccountId,
-			WestendLocation,
-		>(
-			collator_session_keys(),
-			bp_bridge_hub_westend::BRIDGE_HUB_WESTEND_PARACHAIN_ID,
-			origin,
-			BridgedUniversalLocation::get(),
-		)
-	}
-}
diff --git a/cumulus/parachains/runtimes/bridge-hubs/test-utils/Cargo.toml b/cumulus/parachains/runtimes/bridge-hubs/test-utils/Cargo.toml
index 8c048a0d2dbd8b033ad9b3a1cef93eb199e3d62f..915b3090092f3c16f126ca6f86e700bd8e6ebdc1 100644
--- a/cumulus/parachains/runtimes/bridge-hubs/test-utils/Cargo.toml
+++ b/cumulus/parachains/runtimes/bridge-hubs/test-utils/Cargo.toml
@@ -37,6 +37,7 @@ parachains-runtimes-test-utils = { workspace = true }
 xcm = { workspace = true }
 xcm-builder = { workspace = true }
 xcm-executor = { workspace = true }
+pallet-xcm = { workspace = true }
 
 # Bridges
 bp-header-chain = { workspace = true }
@@ -81,6 +82,7 @@ std = [
 	"pallet-timestamp/std",
 	"pallet-utility/std",
 	"pallet-xcm-bridge-hub/std",
+	"pallet-xcm/std",
 	"parachains-common/std",
 	"parachains-runtimes-test-utils/std",
 	"sp-core/std",
diff --git a/cumulus/parachains/runtimes/bridge-hubs/test-utils/src/lib.rs b/cumulus/parachains/runtimes/bridge-hubs/test-utils/src/lib.rs
index b8d6d87051c76f346bf24d98ef6e8ccf85e5a7db..bc28df0eb829cc02ba455b363ce6bea51d702950 100644
--- a/cumulus/parachains/runtimes/bridge-hubs/test-utils/src/lib.rs
+++ b/cumulus/parachains/runtimes/bridge-hubs/test-utils/src/lib.rs
@@ -24,7 +24,9 @@ extern crate alloc;
 pub use bp_test_utils::test_header;
 pub use parachains_runtimes_test_utils::*;
 use sp_runtime::Perbill;
-pub use test_cases::helpers::ensure_opened_bridge;
+pub use test_cases::helpers::{
+	ensure_opened_bridge, open_bridge_with_extrinsic, open_bridge_with_storage,
+};
 
 /// A helper function for comparing the actual value of a fee constant with its estimated value. The
 /// estimated value can be overestimated (`overestimate_in_percent`), and if the difference to the
diff --git a/cumulus/parachains/runtimes/bridge-hubs/test-utils/src/test_cases/from_grandpa_chain.rs b/cumulus/parachains/runtimes/bridge-hubs/test-utils/src/test_cases/from_grandpa_chain.rs
index 72743eaa41dbcaad20c1f22b33e644e68ac6cc7b..320f3030b60a63e362bf88f467ac09afb78ff64b 100644
--- a/cumulus/parachains/runtimes/bridge-hubs/test-utils/src/test_cases/from_grandpa_chain.rs
+++ b/cumulus/parachains/runtimes/bridge-hubs/test-utils/src/test_cases/from_grandpa_chain.rs
@@ -24,12 +24,12 @@ use crate::{
 
 use alloc::{boxed::Box, vec};
 use bp_header_chain::ChainWithGrandpa;
-use bp_messages::{LaneId, UnrewardedRelayersState};
+use bp_messages::UnrewardedRelayersState;
 use bp_relayers::{RewardsAccountOwner, RewardsAccountParams};
 use bp_xcm_bridge_hub::XcmAsPlainPayload;
 use frame_support::traits::{OnFinalize, OnInitialize};
 use frame_system::pallet_prelude::BlockNumberFor;
-use pallet_bridge_messages::{BridgedChainOf, ThisChainOf};
+use pallet_bridge_messages::{BridgedChainOf, LaneIdOf, ThisChainOf};
 use parachains_runtimes_test_utils::{
 	AccountIdOf, BasicParachainRuntime, CollatorSessionKeys, RuntimeCallOf, SlotDurations,
 };
@@ -50,7 +50,7 @@ pub trait WithRemoteGrandpaChainHelper {
 			Self::MPI,
 			InboundPayload = XcmAsPlainPayload,
 			OutboundPayload = XcmAsPlainPayload,
-		> + pallet_bridge_relayers::Config;
+		> + pallet_bridge_relayers::Config<Self::RPI, LaneId = LaneIdOf<Self::Runtime, Self::MPI>>;
 	/// All pallets of this chain, excluding system pallet.
 	type AllPalletsWithoutSystem: OnInitialize<BlockNumberFor<Self::Runtime>>
 		+ OnFinalize<BlockNumberFor<Self::Runtime>>;
@@ -58,15 +58,18 @@ pub trait WithRemoteGrandpaChainHelper {
 	type GPI: 'static;
 	/// Instance of the `pallet-bridge-messages`, used to bridge with remote GRANDPA chain.
 	type MPI: 'static;
+	/// Instance of the `pallet-bridge-relayers`, used to collect rewards from messages `MPI`
+	/// instance.
+	type RPI: 'static;
 }
 
 /// Adapter struct that implements [`WithRemoteGrandpaChainHelper`].
-pub struct WithRemoteGrandpaChainHelperAdapter<Runtime, AllPalletsWithoutSystem, GPI, MPI>(
-	core::marker::PhantomData<(Runtime, AllPalletsWithoutSystem, GPI, MPI)>,
+pub struct WithRemoteGrandpaChainHelperAdapter<Runtime, AllPalletsWithoutSystem, GPI, MPI, RPI>(
+	core::marker::PhantomData<(Runtime, AllPalletsWithoutSystem, GPI, MPI, RPI)>,
 );
 
-impl<Runtime, AllPalletsWithoutSystem, GPI, MPI> WithRemoteGrandpaChainHelper
-	for WithRemoteGrandpaChainHelperAdapter<Runtime, AllPalletsWithoutSystem, GPI, MPI>
+impl<Runtime, AllPalletsWithoutSystem, GPI, MPI, RPI> WithRemoteGrandpaChainHelper
+	for WithRemoteGrandpaChainHelperAdapter<Runtime, AllPalletsWithoutSystem, GPI, MPI, RPI>
 where
 	Runtime: BasicParachainRuntime
 		+ cumulus_pallet_xcmp_queue::Config
@@ -75,16 +78,18 @@ where
 			MPI,
 			InboundPayload = XcmAsPlainPayload,
 			OutboundPayload = XcmAsPlainPayload,
-		> + pallet_bridge_relayers::Config,
+		> + pallet_bridge_relayers::Config<RPI, LaneId = LaneIdOf<Runtime, MPI>>,
 	AllPalletsWithoutSystem:
 		OnInitialize<BlockNumberFor<Runtime>> + OnFinalize<BlockNumberFor<Runtime>>,
 	GPI: 'static,
 	MPI: 'static,
+	RPI: 'static,
 {
 	type Runtime = Runtime;
 	type AllPalletsWithoutSystem = AllPalletsWithoutSystem;
 	type GPI = GPI;
 	type MPI = MPI;
+	type RPI = RPI;
 }
 
 /// Test-case makes sure that Runtime can dispatch XCM messages submitted by relayer,
@@ -96,11 +101,12 @@ pub fn relayed_incoming_message_works<RuntimeHelper>(
 	runtime_para_id: u32,
 	sibling_parachain_id: u32,
 	local_relay_chain_id: NetworkId,
-	prepare_configuration: impl Fn() -> LaneId,
+	prepare_configuration: impl Fn() -> LaneIdOf<RuntimeHelper::Runtime, RuntimeHelper::MPI>,
 	construct_and_apply_extrinsic: fn(
 		sp_keyring::AccountKeyring,
 		RuntimeCallOf<RuntimeHelper::Runtime>,
 	) -> sp_runtime::DispatchOutcome,
+	expect_rewards: bool,
 ) where
 	RuntimeHelper: WithRemoteGrandpaChainHelper,
 	AccountIdOf<RuntimeHelper::Runtime>: From<AccountId32>,
@@ -140,6 +146,7 @@ pub fn relayed_incoming_message_works<RuntimeHelper>(
 				test_data::from_grandpa_chain::make_complex_relayer_delivery_proofs::<
 					BridgedChainOf<RuntimeHelper::Runtime, RuntimeHelper::MPI>,
 					ThisChainOf<RuntimeHelper::Runtime, RuntimeHelper::MPI>,
+					LaneIdOf<RuntimeHelper::Runtime, RuntimeHelper::MPI>,
 				>(
 					lane_id,
 					xcm.into(),
@@ -172,14 +179,18 @@ pub fn relayed_incoming_message_works<RuntimeHelper>(
 							lane_id,
 							1,
 						),
-						helpers::VerifyRelayerRewarded::<RuntimeHelper::Runtime>::expect_relayer_reward(
-							relayer_id_at_this_chain,
-							RewardsAccountParams::new(
-								lane_id,
-								bridged_chain_id,
-								RewardsAccountOwner::ThisChain,
-							),
-						),
+						if expect_rewards {
+							helpers::VerifyRelayerRewarded::<RuntimeHelper::Runtime, RuntimeHelper::RPI>::expect_relayer_reward(
+								relayer_id_at_this_chain,
+								RewardsAccountParams::new(
+									lane_id,
+									bridged_chain_id,
+									RewardsAccountOwner::ThisChain,
+								),
+							)
+						} else {
+							Box::new(())
+						}
 					)),
 				),
 			]
@@ -197,11 +208,12 @@ pub fn free_relay_extrinsic_works<RuntimeHelper>(
 	runtime_para_id: u32,
 	sibling_parachain_id: u32,
 	local_relay_chain_id: NetworkId,
-	prepare_configuration: impl Fn() -> LaneId,
+	prepare_configuration: impl Fn() -> LaneIdOf<RuntimeHelper::Runtime, RuntimeHelper::MPI>,
 	construct_and_apply_extrinsic: fn(
 		sp_keyring::AccountKeyring,
 		RuntimeCallOf<RuntimeHelper::Runtime>,
 	) -> sp_runtime::DispatchOutcome,
+	expect_rewards: bool,
 ) where
 	RuntimeHelper: WithRemoteGrandpaChainHelper,
 	RuntimeHelper::Runtime: pallet_balances::Config,
@@ -263,6 +275,7 @@ pub fn free_relay_extrinsic_works<RuntimeHelper>(
 				test_data::from_grandpa_chain::make_complex_relayer_delivery_proofs::<
 					BridgedChainOf<RuntimeHelper::Runtime, RuntimeHelper::MPI>,
 					ThisChainOf<RuntimeHelper::Runtime, RuntimeHelper::MPI>,
+					LaneIdOf<RuntimeHelper::Runtime, RuntimeHelper::MPI>,
 				>(
 					lane_id,
 					xcm.into(),
@@ -301,14 +314,18 @@ pub fn free_relay_extrinsic_works<RuntimeHelper>(
 							lane_id,
 							1,
 						),
-						helpers::VerifyRelayerRewarded::<RuntimeHelper::Runtime>::expect_relayer_reward(
-							relayer_id_at_this_chain,
-							RewardsAccountParams::new(
-								lane_id,
-								bridged_chain_id,
-								RewardsAccountOwner::ThisChain,
-							),
-						),
+						if expect_rewards {
+							helpers::VerifyRelayerRewarded::<RuntimeHelper::Runtime, RuntimeHelper::RPI>::expect_relayer_reward(
+								relayer_id_at_this_chain,
+								RewardsAccountParams::new(
+									lane_id,
+									bridged_chain_id,
+									RewardsAccountOwner::ThisChain,
+								),
+							)
+						} else {
+							Box::new(())
+						}
 					)),
 				),
 			]
@@ -325,7 +342,7 @@ pub fn complex_relay_extrinsic_works<RuntimeHelper>(
 	runtime_para_id: u32,
 	sibling_parachain_id: u32,
 	local_relay_chain_id: NetworkId,
-	prepare_configuration: impl Fn() -> LaneId,
+	prepare_configuration: impl Fn() -> LaneIdOf<RuntimeHelper::Runtime, RuntimeHelper::MPI>,
 	construct_and_apply_extrinsic: fn(
 		sp_keyring::AccountKeyring,
 		RuntimeCallOf<RuntimeHelper::Runtime>,
@@ -372,6 +389,7 @@ pub fn complex_relay_extrinsic_works<RuntimeHelper>(
 				test_data::from_grandpa_chain::make_complex_relayer_delivery_proofs::<
 					BridgedChainOf<RuntimeHelper::Runtime, RuntimeHelper::MPI>,
 					ThisChainOf<RuntimeHelper::Runtime, RuntimeHelper::MPI>,
+					LaneIdOf<RuntimeHelper::Runtime, RuntimeHelper::MPI>,
 				>(
 					lane_id,
 					xcm.into(),
@@ -382,9 +400,10 @@ pub fn complex_relay_extrinsic_works<RuntimeHelper>(
 				);
 
 			let relay_chain_header_hash = relay_chain_header.hash();
-			vec![(
-				pallet_utility::Call::<RuntimeHelper::Runtime>::batch_all {
-					calls: vec![
+			vec![
+				(
+					pallet_utility::Call::<RuntimeHelper::Runtime>::batch_all {
+						calls: vec![
 						BridgeGrandpaCall::<RuntimeHelper::Runtime, RuntimeHelper::GPI>::submit_finality_proof {
 							finality_target: Box::new(relay_chain_header),
 							justification: grandpa_justification,
@@ -396,27 +415,33 @@ pub fn complex_relay_extrinsic_works<RuntimeHelper>(
 							dispatch_weight: Weight::from_parts(1000000000, 0),
 						}.into(),
 					],
-				}
-				.into(),
-				Box::new((
-					helpers::VerifySubmitGrandpaFinalityProofOutcome::<
-						RuntimeHelper::Runtime,
-						RuntimeHelper::GPI,
-					>::expect_best_header_hash(relay_chain_header_hash),
-					helpers::VerifySubmitMessagesProofOutcome::<
-						RuntimeHelper::Runtime,
-						RuntimeHelper::MPI,
-					>::expect_last_delivered_nonce(lane_id, 1),
-					helpers::VerifyRelayerRewarded::<RuntimeHelper::Runtime>::expect_relayer_reward(
-						relayer_id_at_this_chain,
-						RewardsAccountParams::new(
-							lane_id,
-							bridged_chain_id,
-							RewardsAccountOwner::ThisChain,
+					}
+					.into(),
+					Box::new(
+						(
+							helpers::VerifySubmitGrandpaFinalityProofOutcome::<
+								RuntimeHelper::Runtime,
+								RuntimeHelper::GPI,
+							>::expect_best_header_hash(relay_chain_header_hash),
+							helpers::VerifySubmitMessagesProofOutcome::<
+								RuntimeHelper::Runtime,
+								RuntimeHelper::MPI,
+							>::expect_last_delivered_nonce(lane_id, 1),
+							helpers::VerifyRelayerRewarded::<
+								RuntimeHelper::Runtime,
+								RuntimeHelper::RPI,
+							>::expect_relayer_reward(
+								relayer_id_at_this_chain,
+								RewardsAccountParams::new(
+									lane_id,
+									bridged_chain_id,
+									RewardsAccountOwner::ThisChain,
+								),
+							),
 						),
 					),
-				)),
-			)]
+				),
+			]
 		},
 	);
 }
@@ -446,8 +471,9 @@ where
 			test_data::from_grandpa_chain::make_complex_relayer_delivery_proofs::<
 				BridgedChainOf<RuntimeHelper::Runtime, RuntimeHelper::MPI>,
 				ThisChainOf<RuntimeHelper::Runtime, RuntimeHelper::MPI>,
+				LaneIdOf<RuntimeHelper::Runtime, RuntimeHelper::MPI>,
 			>(
-				LaneId::new(1, 2),
+				LaneIdOf::<RuntimeHelper::Runtime, RuntimeHelper::MPI>::default(),
 				vec![Instruction::<()>::ClearOrigin; 1_024].into(),
 				1,
 				[GlobalConsensus(Polkadot), Parachain(1_000)].into(),
@@ -502,8 +528,9 @@ where
 				BridgedChainOf<RuntimeHelper::Runtime, RuntimeHelper::MPI>,
 				ThisChainOf<RuntimeHelper::Runtime, RuntimeHelper::MPI>,
 				(),
+				LaneIdOf<RuntimeHelper::Runtime, RuntimeHelper::MPI>,
 			>(
-				LaneId::new(1, 2),
+				LaneIdOf::<RuntimeHelper::Runtime, RuntimeHelper::MPI>::default(),
 				1u32.into(),
 				AccountId32::from(Alice.public()).into(),
 				unrewarded_relayers.clone(),
@@ -550,8 +577,9 @@ where
 			test_data::from_grandpa_chain::make_complex_relayer_delivery_proofs::<
 				BridgedChainOf<RuntimeHelper::Runtime, RuntimeHelper::MPI>,
 				ThisChainOf<RuntimeHelper::Runtime, RuntimeHelper::MPI>,
+				LaneIdOf<RuntimeHelper::Runtime, RuntimeHelper::MPI>,
 			>(
-				LaneId::new(1, 2),
+				LaneIdOf::<RuntimeHelper::Runtime, RuntimeHelper::MPI>::default(),
 				vec![Instruction::<()>::ClearOrigin; 1_024].into(),
 				1,
 				[GlobalConsensus(Polkadot), Parachain(1_000)].into(),
@@ -602,8 +630,9 @@ where
 				BridgedChainOf<RuntimeHelper::Runtime, RuntimeHelper::MPI>,
 				ThisChainOf<RuntimeHelper::Runtime, RuntimeHelper::MPI>,
 				(),
+				LaneIdOf<RuntimeHelper::Runtime, RuntimeHelper::MPI>,
 			>(
-				LaneId::new(1, 2),
+				LaneIdOf::<RuntimeHelper::Runtime, RuntimeHelper::MPI>::default(),
 				1u32.into(),
 				AccountId32::from(Alice.public()).into(),
 				unrewarded_relayers.clone(),
diff --git a/cumulus/parachains/runtimes/bridge-hubs/test-utils/src/test_cases/from_parachain.rs b/cumulus/parachains/runtimes/bridge-hubs/test-utils/src/test_cases/from_parachain.rs
index 82edcacdcab57d4cc33ca8b9fea312f2e7596327..1da901e0bcdf9e8da142fba80ad07d053f3fabe2 100644
--- a/cumulus/parachains/runtimes/bridge-hubs/test-utils/src/test_cases/from_parachain.rs
+++ b/cumulus/parachains/runtimes/bridge-hubs/test-utils/src/test_cases/from_parachain.rs
@@ -24,14 +24,14 @@ use crate::{
 
 use alloc::{boxed::Box, vec};
 use bp_header_chain::ChainWithGrandpa;
-use bp_messages::{LaneId, UnrewardedRelayersState};
+use bp_messages::UnrewardedRelayersState;
 use bp_polkadot_core::parachains::ParaHash;
 use bp_relayers::{RewardsAccountOwner, RewardsAccountParams};
 use bp_runtime::{Chain, Parachain};
 use bp_xcm_bridge_hub::XcmAsPlainPayload;
 use frame_support::traits::{OnFinalize, OnInitialize};
 use frame_system::pallet_prelude::BlockNumberFor;
-use pallet_bridge_messages::{BridgedChainOf, ThisChainOf};
+use pallet_bridge_messages::{BridgedChainOf, LaneIdOf, ThisChainOf};
 use parachains_runtimes_test_utils::{
 	AccountIdOf, BasicParachainRuntime, CollatorSessionKeys, RuntimeCallOf, SlotDurations,
 };
@@ -53,7 +53,7 @@ pub trait WithRemoteParachainHelper {
 			Self::MPI,
 			InboundPayload = XcmAsPlainPayload,
 			OutboundPayload = XcmAsPlainPayload,
-		> + pallet_bridge_relayers::Config;
+		> + pallet_bridge_relayers::Config<Self::RPI, LaneId = LaneIdOf<Self::Runtime, Self::MPI>>;
 	/// All pallets of this chain, excluding system pallet.
 	type AllPalletsWithoutSystem: OnInitialize<BlockNumberFor<Self::Runtime>>
 		+ OnFinalize<BlockNumberFor<Self::Runtime>>;
@@ -63,15 +63,18 @@ pub trait WithRemoteParachainHelper {
 	type PPI: 'static;
 	/// Instance of the `pallet-bridge-messages`, used to bridge with remote parachain.
 	type MPI: 'static;
+	/// Instance of the `pallet-bridge-relayers`, used to collect rewards from messages `MPI`
+	/// instance.
+	type RPI: 'static;
 }
 
 /// Adapter struct that implements `WithRemoteParachainHelper`.
-pub struct WithRemoteParachainHelperAdapter<Runtime, AllPalletsWithoutSystem, GPI, PPI, MPI>(
-	core::marker::PhantomData<(Runtime, AllPalletsWithoutSystem, GPI, PPI, MPI)>,
+pub struct WithRemoteParachainHelperAdapter<Runtime, AllPalletsWithoutSystem, GPI, PPI, MPI, RPI>(
+	core::marker::PhantomData<(Runtime, AllPalletsWithoutSystem, GPI, PPI, MPI, RPI)>,
 );
 
-impl<Runtime, AllPalletsWithoutSystem, GPI, PPI, MPI> WithRemoteParachainHelper
-	for WithRemoteParachainHelperAdapter<Runtime, AllPalletsWithoutSystem, GPI, PPI, MPI>
+impl<Runtime, AllPalletsWithoutSystem, GPI, PPI, MPI, RPI> WithRemoteParachainHelper
+	for WithRemoteParachainHelperAdapter<Runtime, AllPalletsWithoutSystem, GPI, PPI, MPI, RPI>
 where
 	Runtime: BasicParachainRuntime
 		+ cumulus_pallet_xcmp_queue::Config
@@ -81,19 +84,20 @@ where
 			MPI,
 			InboundPayload = XcmAsPlainPayload,
 			OutboundPayload = XcmAsPlainPayload,
-		> + pallet_bridge_relayers::Config,
+		> + pallet_bridge_relayers::Config<RPI, LaneId = LaneIdOf<Runtime, MPI>>,
 	AllPalletsWithoutSystem:
 		OnInitialize<BlockNumberFor<Runtime>> + OnFinalize<BlockNumberFor<Runtime>>,
 	GPI: 'static,
 	PPI: 'static,
 	MPI: 'static,
-	// MB: MessageBridge,
+	RPI: 'static,
 {
 	type Runtime = Runtime;
 	type AllPalletsWithoutSystem = AllPalletsWithoutSystem;
 	type GPI = GPI;
 	type PPI = PPI;
 	type MPI = MPI;
+	type RPI = RPI;
 }
 
 /// Test-case makes sure that Runtime can dispatch XCM messages submitted by relayer,
@@ -106,11 +110,12 @@ pub fn relayed_incoming_message_works<RuntimeHelper>(
 	bridged_para_id: u32,
 	sibling_parachain_id: u32,
 	local_relay_chain_id: NetworkId,
-	prepare_configuration: impl Fn() -> LaneId,
+	prepare_configuration: impl Fn() -> LaneIdOf<RuntimeHelper::Runtime, RuntimeHelper::MPI>,
 	construct_and_apply_extrinsic: fn(
 		sp_keyring::AccountKeyring,
 		<RuntimeHelper::Runtime as frame_system::Config>::RuntimeCall,
 	) -> sp_runtime::DispatchOutcome,
+	expect_rewards: bool,
 ) where
 	RuntimeHelper: WithRemoteParachainHelper,
 	AccountIdOf<RuntimeHelper::Runtime>: From<AccountId32>,
@@ -161,6 +166,7 @@ pub fn relayed_incoming_message_works<RuntimeHelper>(
 				<RuntimeHelper::Runtime as BridgeGrandpaConfig<RuntimeHelper::GPI>>::BridgedChain,
 				BridgedChainOf<RuntimeHelper::Runtime, RuntimeHelper::MPI>,
 				ThisChainOf<RuntimeHelper::Runtime, RuntimeHelper::MPI>,
+				LaneIdOf<RuntimeHelper::Runtime, RuntimeHelper::MPI>,
 			>(
 				lane_id,
 				xcm.into(),
@@ -208,14 +214,18 @@ pub fn relayed_incoming_message_works<RuntimeHelper>(
 							lane_id,
 							1,
 						),
-						helpers::VerifyRelayerRewarded::<RuntimeHelper::Runtime>::expect_relayer_reward(
-							relayer_id_at_this_chain,
-							RewardsAccountParams::new(
-								lane_id,
-								bridged_chain_id,
-								RewardsAccountOwner::ThisChain,
-							),
-						),
+						if expect_rewards {
+                            helpers::VerifyRelayerRewarded::<RuntimeHelper::Runtime, RuntimeHelper::RPI>::expect_relayer_reward(
+                                relayer_id_at_this_chain,
+                                RewardsAccountParams::new(
+                                    lane_id,
+                                    bridged_chain_id,
+                                    RewardsAccountOwner::ThisChain,
+                                ),
+                            )
+						} else {
+							Box::new(())
+						}
 					)),
 				),
 			]
@@ -234,11 +244,12 @@ pub fn free_relay_extrinsic_works<RuntimeHelper>(
 	bridged_para_id: u32,
 	sibling_parachain_id: u32,
 	local_relay_chain_id: NetworkId,
-	prepare_configuration: impl Fn() -> LaneId,
+	prepare_configuration: impl Fn() -> LaneIdOf<RuntimeHelper::Runtime, RuntimeHelper::MPI>,
 	construct_and_apply_extrinsic: fn(
 		sp_keyring::AccountKeyring,
 		<RuntimeHelper::Runtime as frame_system::Config>::RuntimeCall,
 	) -> sp_runtime::DispatchOutcome,
+	expect_rewards: bool,
 ) where
 	RuntimeHelper: WithRemoteParachainHelper,
 	RuntimeHelper::Runtime: pallet_balances::Config,
@@ -312,6 +323,7 @@ pub fn free_relay_extrinsic_works<RuntimeHelper>(
 				<RuntimeHelper::Runtime as BridgeGrandpaConfig<RuntimeHelper::GPI>>::BridgedChain,
 				BridgedChainOf<RuntimeHelper::Runtime, RuntimeHelper::MPI>,
 				ThisChainOf<RuntimeHelper::Runtime, RuntimeHelper::MPI>,
+				LaneIdOf<RuntimeHelper::Runtime, RuntimeHelper::MPI>,
 			>(
 				lane_id,
 				xcm.into(),
@@ -353,10 +365,10 @@ pub fn free_relay_extrinsic_works<RuntimeHelper>(
 							bridged_para_id,
 							parachain_head_hash,
 						),
-						/*helpers::VerifyRelayerBalance::<RuntimeHelper::Runtime>::expect_relayer_balance(
+						helpers::VerifyRelayerBalance::<RuntimeHelper::Runtime>::expect_relayer_balance(
 							relayer_id_at_this_chain.clone(),
 							initial_relayer_balance,
-						),*/
+						),
 					)),
 				),
 				(
@@ -371,14 +383,18 @@ pub fn free_relay_extrinsic_works<RuntimeHelper>(
 							lane_id,
 							1,
 						),
-						helpers::VerifyRelayerRewarded::<RuntimeHelper::Runtime>::expect_relayer_reward(
-							relayer_id_at_this_chain,
-							RewardsAccountParams::new(
-								lane_id,
-								bridged_chain_id,
-								RewardsAccountOwner::ThisChain,
-							),
-						),
+						if expect_rewards {
+                            helpers::VerifyRelayerRewarded::<RuntimeHelper::Runtime, RuntimeHelper::RPI>::expect_relayer_reward(
+                                relayer_id_at_this_chain,
+                                RewardsAccountParams::new(
+                                    lane_id,
+                                    bridged_chain_id,
+                                    RewardsAccountOwner::ThisChain,
+                                ),
+                            )
+						} else {
+							Box::new(())
+						}
 					)),
 				),
 			]
@@ -396,7 +412,7 @@ pub fn complex_relay_extrinsic_works<RuntimeHelper>(
 	bridged_para_id: u32,
 	sibling_parachain_id: u32,
 	local_relay_chain_id: NetworkId,
-	prepare_configuration: impl Fn() -> LaneId,
+	prepare_configuration: impl Fn() -> LaneIdOf<RuntimeHelper::Runtime, RuntimeHelper::MPI>,
 	construct_and_apply_extrinsic: fn(
 		sp_keyring::AccountKeyring,
 		<RuntimeHelper::Runtime as frame_system::Config>::RuntimeCall,
@@ -454,6 +470,7 @@ pub fn complex_relay_extrinsic_works<RuntimeHelper>(
 				<RuntimeHelper::Runtime as BridgeGrandpaConfig<RuntimeHelper::GPI>>::BridgedChain,
 				BridgedChainOf<RuntimeHelper::Runtime, RuntimeHelper::MPI>,
 				ThisChainOf<RuntimeHelper::Runtime, RuntimeHelper::MPI>,
+				LaneIdOf<RuntimeHelper::Runtime, RuntimeHelper::MPI>,
 			>(
 				lane_id,
 				xcm.into(),
@@ -468,9 +485,10 @@ pub fn complex_relay_extrinsic_works<RuntimeHelper>(
 			let parachain_head_hash = parachain_head.hash();
 			let relay_chain_header_hash = relay_chain_header.hash();
 			let relay_chain_header_number = *relay_chain_header.number();
-			vec![(
-				pallet_utility::Call::<RuntimeHelper::Runtime>::batch_all {
-					calls: vec![
+			vec![
+				(
+					pallet_utility::Call::<RuntimeHelper::Runtime>::batch_all {
+						calls: vec![
 						BridgeGrandpaCall::<RuntimeHelper::Runtime, RuntimeHelper::GPI>::submit_finality_proof {
 							finality_target: Box::new(relay_chain_header),
 							justification: grandpa_justification,
@@ -487,31 +505,37 @@ pub fn complex_relay_extrinsic_works<RuntimeHelper>(
 							dispatch_weight: Weight::from_parts(1000000000, 0),
 						}.into(),
 					],
-				}
-				.into(),
-				Box::new((
-					helpers::VerifySubmitGrandpaFinalityProofOutcome::<
-						RuntimeHelper::Runtime,
-						RuntimeHelper::GPI,
-					>::expect_best_header_hash(relay_chain_header_hash),
-					helpers::VerifySubmitParachainHeaderProofOutcome::<
-						RuntimeHelper::Runtime,
-						RuntimeHelper::PPI,
-					>::expect_best_header_hash(bridged_para_id, parachain_head_hash),
-					helpers::VerifySubmitMessagesProofOutcome::<
-						RuntimeHelper::Runtime,
-						RuntimeHelper::MPI,
-					>::expect_last_delivered_nonce(lane_id, 1),
-					helpers::VerifyRelayerRewarded::<RuntimeHelper::Runtime>::expect_relayer_reward(
-						relayer_id_at_this_chain,
-						RewardsAccountParams::new(
-							lane_id,
-							bridged_chain_id,
-							RewardsAccountOwner::ThisChain,
+					}
+					.into(),
+					Box::new(
+						(
+							helpers::VerifySubmitGrandpaFinalityProofOutcome::<
+								RuntimeHelper::Runtime,
+								RuntimeHelper::GPI,
+							>::expect_best_header_hash(relay_chain_header_hash),
+							helpers::VerifySubmitParachainHeaderProofOutcome::<
+								RuntimeHelper::Runtime,
+								RuntimeHelper::PPI,
+							>::expect_best_header_hash(bridged_para_id, parachain_head_hash),
+							helpers::VerifySubmitMessagesProofOutcome::<
+								RuntimeHelper::Runtime,
+								RuntimeHelper::MPI,
+							>::expect_last_delivered_nonce(lane_id, 1),
+							helpers::VerifyRelayerRewarded::<
+								RuntimeHelper::Runtime,
+								RuntimeHelper::RPI,
+							>::expect_relayer_reward(
+								relayer_id_at_this_chain,
+								RewardsAccountParams::new(
+									lane_id,
+									bridged_chain_id,
+									RewardsAccountOwner::ThisChain,
+								),
+							),
 						),
 					),
-				)),
-			)]
+				),
+			]
 		},
 	);
 }
@@ -551,8 +575,9 @@ where
 			<RuntimeHelper::Runtime as pallet_bridge_grandpa::Config<RuntimeHelper::GPI>>::BridgedChain,
 			BridgedChainOf<RuntimeHelper::Runtime, RuntimeHelper::MPI>,
 			ThisChainOf<RuntimeHelper::Runtime, RuntimeHelper::MPI>,
+			LaneIdOf<RuntimeHelper::Runtime, RuntimeHelper::MPI>
 		>(
-			LaneId::new(1, 2),
+			LaneIdOf::<RuntimeHelper::Runtime, RuntimeHelper::MPI>::default(),
 			vec![Instruction::<()>::ClearOrigin; 1_024].into(),
 			1,
 			[GlobalConsensus(Polkadot), Parachain(1_000)].into(),
@@ -621,8 +646,9 @@ where
 			<RuntimeHelper::Runtime as BridgeGrandpaConfig<RuntimeHelper::GPI>>::BridgedChain,
 			BridgedChainOf<RuntimeHelper::Runtime, RuntimeHelper::MPI>,
 			ThisChainOf<RuntimeHelper::Runtime, RuntimeHelper::MPI>,
+			LaneIdOf<RuntimeHelper::Runtime, RuntimeHelper::MPI>,
 		>(
-			LaneId::new(1, 2),
+			LaneIdOf::<RuntimeHelper::Runtime, RuntimeHelper::MPI>::default(),
 			1,
 			5,
 			1_000,
@@ -683,8 +709,9 @@ where
 			<RuntimeHelper::Runtime as pallet_bridge_grandpa::Config<RuntimeHelper::GPI>>::BridgedChain,
 			BridgedChainOf<RuntimeHelper::Runtime, RuntimeHelper::MPI>,
 			ThisChainOf<RuntimeHelper::Runtime, RuntimeHelper::MPI>,
+			LaneIdOf<RuntimeHelper::Runtime, RuntimeHelper::MPI>,
 		>(
-			LaneId::new(1, 2),
+			LaneIdOf::<RuntimeHelper::Runtime, RuntimeHelper::MPI>::default(),
 			vec![Instruction::<()>::ClearOrigin; 1_024].into(),
 			1,
 			[GlobalConsensus(Polkadot), Parachain(1_000)].into(),
@@ -738,8 +765,9 @@ where
 				<RuntimeHelper::Runtime as BridgeGrandpaConfig<RuntimeHelper::GPI>>::BridgedChain,
 				BridgedChainOf<RuntimeHelper::Runtime, RuntimeHelper::MPI>,
 				ThisChainOf<RuntimeHelper::Runtime, RuntimeHelper::MPI>,
+				LaneIdOf<RuntimeHelper::Runtime, RuntimeHelper::MPI>,
 			>(
-				LaneId::new(1, 2),
+				LaneIdOf::<RuntimeHelper::Runtime, RuntimeHelper::MPI>::default(),
 				1,
 				5,
 				1_000,
diff --git a/cumulus/parachains/runtimes/bridge-hubs/test-utils/src/test_cases/helpers.rs b/cumulus/parachains/runtimes/bridge-hubs/test-utils/src/test_cases/helpers.rs
index c343e9b3e09a382065e7aed1be275abf9c029f43..aac60bba0b53978fc72f7562a1e52480205be3b6 100644
--- a/cumulus/parachains/runtimes/bridge-hubs/test-utils/src/test_cases/helpers.rs
+++ b/cumulus/parachains/runtimes/bridge-hubs/test-utils/src/test_cases/helpers.rs
@@ -19,7 +19,7 @@
 use crate::test_cases::{bridges_prelude::*, run_test, RuntimeHelper};
 
 use asset_test_utils::BasicParachainRuntime;
-use bp_messages::{LaneId, MessageNonce};
+use bp_messages::MessageNonce;
 use bp_polkadot_core::parachains::{ParaHash, ParaId};
 use bp_relayers::RewardsAccountParams;
 use bp_runtime::Chain;
@@ -33,7 +33,7 @@ use frame_support::{
 };
 use frame_system::pallet_prelude::BlockNumberFor;
 use pallet_bridge_grandpa::{BridgedBlockHash, BridgedHeader};
-use pallet_bridge_messages::BridgedChainOf;
+use pallet_bridge_messages::{BridgedChainOf, LaneIdOf};
 use parachains_common::AccountId;
 use parachains_runtimes_test_utils::{
 	mock_open_hrmp_channel, AccountIdOf, CollatorSessionKeys, RuntimeCallOf, SlotDurations,
@@ -132,8 +132,8 @@ where
 }
 
 /// Checks that the latest delivered nonce in the bridge messages pallet equals to given one.
-pub struct VerifySubmitMessagesProofOutcome<Runtime, MPI> {
-	lane: LaneId,
+pub struct VerifySubmitMessagesProofOutcome<Runtime: BridgeMessagesConfig<MPI>, MPI: 'static> {
+	lane: LaneIdOf<Runtime, MPI>,
 	expected_nonce: MessageNonce,
 	_marker: PhantomData<(Runtime, MPI)>,
 }
@@ -145,7 +145,7 @@ where
 {
 	/// Expect given delivered nonce to be the latest after transaction.
 	pub fn expect_last_delivered_nonce(
-		lane: LaneId,
+		lane: LaneIdOf<Runtime, MPI>,
 		expected_nonce: MessageNonce,
 	) -> Box<dyn VerifyTransactionOutcome> {
 		Box::new(Self { lane, expected_nonce, _marker: PhantomData })
@@ -167,30 +167,32 @@ where
 }
 
 /// Verifies that relayer is rewarded at this chain.
-pub struct VerifyRelayerRewarded<Runtime: frame_system::Config> {
+pub struct VerifyRelayerRewarded<Runtime: pallet_bridge_relayers::Config<RPI>, RPI: 'static> {
 	relayer: Runtime::AccountId,
-	reward_params: RewardsAccountParams,
+	reward_params: RewardsAccountParams<Runtime::LaneId>,
 }
 
-impl<Runtime> VerifyRelayerRewarded<Runtime>
+impl<Runtime, RPI> VerifyRelayerRewarded<Runtime, RPI>
 where
-	Runtime: pallet_bridge_relayers::Config,
+	Runtime: pallet_bridge_relayers::Config<RPI>,
+	RPI: 'static,
 {
 	/// Expect given delivered nonce to be the latest after transaction.
 	pub fn expect_relayer_reward(
 		relayer: Runtime::AccountId,
-		reward_params: RewardsAccountParams,
+		reward_params: RewardsAccountParams<Runtime::LaneId>,
 	) -> Box<dyn VerifyTransactionOutcome> {
 		Box::new(Self { relayer, reward_params })
 	}
 }
 
-impl<Runtime> VerifyTransactionOutcome for VerifyRelayerRewarded<Runtime>
+impl<Runtime, RPI> VerifyTransactionOutcome for VerifyRelayerRewarded<Runtime, RPI>
 where
-	Runtime: pallet_bridge_relayers::Config,
+	Runtime: pallet_bridge_relayers::Config<RPI>,
+	RPI: 'static,
 {
 	fn verify_outcome(&self) {
-		assert!(pallet_bridge_relayers::RelayerRewards::<Runtime>::get(
+		assert!(pallet_bridge_relayers::RelayerRewards::<Runtime, RPI>::get(
 			&self.relayer,
 			&self.reward_params,
 		)
@@ -388,7 +390,12 @@ fn execute_and_verify_calls<Runtime: frame_system::Config>(
 
 /// Helper function to open the bridge/lane for `source` and `destination` while ensuring all
 /// required balances are placed into the SA of the source.
-pub fn ensure_opened_bridge<Runtime, XcmOverBridgePalletInstance, LocationToAccountId, TokenLocation>(source: Location, destination: InteriorLocation) -> (BridgeLocations, LaneId)
+pub fn ensure_opened_bridge<
+	Runtime,
+	XcmOverBridgePalletInstance,
+	LocationToAccountId,
+	TokenLocation>
+(source: Location, destination: InteriorLocation, bridge_opener: impl Fn(BridgeLocations, Asset)) -> (BridgeLocations, pallet_xcm_bridge_hub::LaneIdOf<Runtime, XcmOverBridgePalletInstance>)
 where
 	Runtime: BasicParachainRuntime + BridgeXcmOverBridgeConfig<XcmOverBridgePalletInstance>,
 	XcmOverBridgePalletInstance: 'static,
@@ -425,34 +432,74 @@ TokenLocation: Get<Location>{
 	let _ = <pallet_balances::Pallet<Runtime>>::mint_into(&source_account_id, balance_needed)
 		.expect("mint_into passes");
 
+	// call the bridge opener
+	bridge_opener(*locations.clone(), buy_execution_fee);
+
+	// check opened bridge
+	let bridge = pallet_xcm_bridge_hub::Bridges::<Runtime, XcmOverBridgePalletInstance>::get(
+		locations.bridge_id(),
+	)
+	.expect("opened bridge");
+
+	// check state
+	assert_ok!(
+		pallet_xcm_bridge_hub::Pallet::<Runtime, XcmOverBridgePalletInstance>::do_try_state()
+	);
+
+	// return locations
+	(*locations, bridge.lane_id)
+}
+
+/// Utility for opening bridge with dedicated `pallet_xcm_bridge_hub`'s extrinsic.
+pub fn open_bridge_with_extrinsic<Runtime, XcmOverBridgePalletInstance>(
+	locations: BridgeLocations,
+	buy_execution_fee: Asset,
+) where
+	Runtime: frame_system::Config
+		+ pallet_xcm_bridge_hub::Config<XcmOverBridgePalletInstance>
+		+ cumulus_pallet_parachain_system::Config
+		+ pallet_xcm::Config,
+	XcmOverBridgePalletInstance: 'static,
+	<Runtime as frame_system::Config>::RuntimeCall:
+		GetDispatchInfo + From<BridgeXcmOverBridgeCall<Runtime, XcmOverBridgePalletInstance>>,
+{
 	// open bridge with `Transact` call
 	let open_bridge_call = RuntimeCallOf::<Runtime>::from(BridgeXcmOverBridgeCall::<
 		Runtime,
 		XcmOverBridgePalletInstance,
 	>::open_bridge {
-		bridge_destination_universal_location: Box::new(destination.into()),
+		bridge_destination_universal_location: Box::new(
+			locations.bridge_destination_universal_location().clone().into(),
+		),
 	});
 
 	// execute XCM as source origin would do with `Transact -> Origin::Xcm`
 	assert_ok!(RuntimeHelper::<Runtime>::execute_as_origin_xcm(
+		locations.bridge_origin_relative_location().clone(),
 		open_bridge_call,
-		source.clone(),
 		buy_execution_fee
 	)
 	.ensure_complete());
+}
 
-	let bridge = pallet_xcm_bridge_hub::Bridges::<Runtime, XcmOverBridgePalletInstance>::get(
-		locations.bridge_id(),
-	)
-	.expect("opened bridge");
-
-	// check state
+/// Utility for opening bridge directly inserting data to the storage (used only for legacy
+/// purposes).
+pub fn open_bridge_with_storage<Runtime, XcmOverBridgePalletInstance>(
+	locations: BridgeLocations,
+	_buy_execution_fee: Asset,
+	lane_id: pallet_xcm_bridge_hub::LaneIdOf<Runtime, XcmOverBridgePalletInstance>,
+) where
+	Runtime: pallet_xcm_bridge_hub::Config<XcmOverBridgePalletInstance>,
+	XcmOverBridgePalletInstance: 'static,
+{
+	// insert bridge data directly to the storage
 	assert_ok!(
-		pallet_xcm_bridge_hub::Pallet::<Runtime, XcmOverBridgePalletInstance>::do_try_state()
+		pallet_xcm_bridge_hub::Pallet::<Runtime, XcmOverBridgePalletInstance>::do_open_bridge(
+			Box::new(locations),
+			lane_id,
+			true
+		)
 	);
-
-	// return locations
-	(*locations, bridge.lane_id)
 }
 
 /// Helper function to close the bridge/lane for `source` and `destination`.
@@ -504,8 +551,8 @@ TokenLocation: Get<Location>{
 
 	// execute XCM as source origin would do with `Transact -> Origin::Xcm`
 	assert_ok!(RuntimeHelper::<Runtime>::execute_as_origin_xcm(
-		close_bridge_call,
 		source.clone(),
+		close_bridge_call,
 		buy_execution_fee
 	)
 	.ensure_complete());
diff --git a/cumulus/parachains/runtimes/bridge-hubs/test-utils/src/test_cases/mod.rs b/cumulus/parachains/runtimes/bridge-hubs/test-utils/src/test_cases/mod.rs
index de117982b26fbd9696620d9c4ccdb726a17ebede..663558f5fd5cd2921d15406fdc70d58f1f0170cc 100644
--- a/cumulus/parachains/runtimes/bridge-hubs/test-utils/src/test_cases/mod.rs
+++ b/cumulus/parachains/runtimes/bridge-hubs/test-utils/src/test_cases/mod.rs
@@ -29,7 +29,7 @@ use crate::{test_cases::bridges_prelude::*, test_data};
 use asset_test_utils::BasicParachainRuntime;
 use bp_messages::{
 	target_chain::{DispatchMessage, DispatchMessageData, MessageDispatch},
-	LaneId, LaneState, MessageKey, MessagesOperatingMode, OutboundLaneData,
+	LaneState, MessageKey, MessagesOperatingMode, OutboundLaneData,
 };
 use bp_runtime::BasicOperatingMode;
 use bp_xcm_bridge_hub::{Bridge, BridgeState, XcmAsPlainPayload};
@@ -71,11 +71,13 @@ pub(crate) mod bridges_prelude {
 
 // Re-export test_case from assets
 pub use asset_test_utils::include_teleports_for_native_asset_works;
+use pallet_bridge_messages::LaneIdOf;
 
 pub type RuntimeHelper<Runtime, AllPalletsWithoutSystem = ()> =
 	parachains_runtimes_test_utils::RuntimeHelper<Runtime, AllPalletsWithoutSystem>;
 
 // Re-export test_case from `parachains-runtimes-test-utils`
+use crate::test_cases::helpers::open_bridge_with_extrinsic;
 pub use parachains_runtimes_test_utils::test_cases::{
 	change_storage_constant_by_governance_works, set_storage_keys_by_governance_works,
 };
@@ -326,7 +328,7 @@ pub fn handle_export_message_from_system_parachain_to_outbound_queue_works<
 	export_message_instruction: fn() -> Instruction<XcmConfig::RuntimeCall>,
 	existential_deposit: Option<Asset>,
 	maybe_paid_export_message: Option<Asset>,
-	prepare_configuration: impl Fn() -> LaneId,
+	prepare_configuration: impl Fn() -> LaneIdOf<Runtime, MessagesPalletInstance>,
 ) where
 	Runtime: BasicParachainRuntime + BridgeMessagesConfig<MessagesPalletInstance>,
 	XcmConfig: xcm_executor::Config,
@@ -469,7 +471,7 @@ pub fn message_dispatch_routing_works<
 	run_test::<Runtime, _>(collator_session_key, runtime_para_id, vec![], || {
 		prepare_configuration();
 
-		let dummy_lane_id = LaneId::new(1, 2);
+		let dummy_lane_id = LaneIdOf::<Runtime, MessagesPalletInstance>::default();
 		let mut alice = [0u8; 32];
 		alice[0] = 1;
 
@@ -714,7 +716,11 @@ pub fn open_and_close_bridge_works<Runtime, XcmOverBridgePalletInstance, Locatio
 				XcmOverBridgePalletInstance,
 				LocationToAccountId,
 				TokenLocation,
-			>(source.clone(), destination.clone())
+			>(
+				source.clone(),
+				destination.clone(),
+				open_bridge_with_extrinsic::<Runtime, XcmOverBridgePalletInstance>
+			)
 			.0
 			.bridge_id(),
 			locations.bridge_id()
diff --git a/cumulus/parachains/runtimes/bridge-hubs/test-utils/src/test_data/from_grandpa_chain.rs b/cumulus/parachains/runtimes/bridge-hubs/test-utils/src/test_data/from_grandpa_chain.rs
index 2940c4e00f4258818b2e6c960a0e89c758f7afab..7461085330f27691b4da39e425aebcf64046641d 100644
--- a/cumulus/parachains/runtimes/bridge-hubs/test-utils/src/test_data/from_grandpa_chain.rs
+++ b/cumulus/parachains/runtimes/bridge-hubs/test-utils/src/test_data/from_grandpa_chain.rs
@@ -20,8 +20,8 @@ use crate::test_data::prepare_inbound_xcm;
 
 use bp_messages::{
 	source_chain::FromBridgedChainMessagesDeliveryProof,
-	target_chain::FromBridgedChainMessagesProof, ChainWithMessages, LaneId, LaneState,
-	MessageNonce, UnrewardedRelayersState,
+	target_chain::FromBridgedChainMessagesProof, ChainWithMessages, LaneState, MessageNonce,
+	UnrewardedRelayersState,
 };
 use bp_runtime::{AccountIdOf, BlockNumberOf, Chain, HeaderOf, UnverifiedStorageProofParams};
 use bp_test_utils::make_default_justification;
@@ -40,7 +40,7 @@ use pallet_bridge_messages::{
 		encode_all_messages, encode_lane_data, prepare_message_delivery_storage_proof,
 		prepare_messages_storage_proof,
 	},
-	BridgedChainOf,
+	BridgedChainOf, LaneIdOf,
 };
 use sp_runtime::DigestItem;
 
@@ -48,7 +48,10 @@ use sp_runtime::DigestItem;
 pub fn make_complex_relayer_delivery_batch<Runtime, GPI, MPI>(
 	bridged_header: BridgedHeader<Runtime, GPI>,
 	bridged_justification: GrandpaJustification<BridgedHeader<Runtime, GPI>>,
-	message_proof: FromBridgedChainMessagesProof<HashOf<BridgedChain<Runtime, GPI>>>,
+	message_proof: FromBridgedChainMessagesProof<
+		HashOf<BridgedChain<Runtime, GPI>>,
+		LaneIdOf<Runtime, MPI>,
+	>,
 	relayer_id_at_bridged_chain: InboundRelayerId<Runtime, MPI>,
 ) -> pallet_utility::Call<Runtime>
 where
@@ -82,6 +85,7 @@ pub fn make_complex_relayer_confirmation_batch<Runtime, GPI, MPI>(
 	bridged_justification: GrandpaJustification<BridgedHeader<Runtime, GPI>>,
 	message_delivery_proof: FromBridgedChainMessagesDeliveryProof<
 		HashOf<BridgedChain<Runtime, GPI>>,
+		LaneIdOf<Runtime, MPI>,
 	>,
 	relayers_state: UnrewardedRelayersState,
 ) -> pallet_utility::Call<Runtime>
@@ -111,7 +115,10 @@ where
 
 /// Prepare a call with message proof.
 pub fn make_standalone_relayer_delivery_call<Runtime, GPI, MPI>(
-	message_proof: FromBridgedChainMessagesProof<HashOf<BridgedChain<Runtime, GPI>>>,
+	message_proof: FromBridgedChainMessagesProof<
+		HashOf<BridgedChain<Runtime, GPI>>,
+		LaneIdOf<Runtime, MPI>,
+	>,
 	relayer_id_at_bridged_chain: InboundRelayerId<Runtime, MPI>,
 ) -> Runtime::RuntimeCall
 where
@@ -134,6 +141,7 @@ where
 pub fn make_standalone_relayer_confirmation_call<Runtime, GPI, MPI>(
 	message_delivery_proof: FromBridgedChainMessagesDeliveryProof<
 		HashOf<BridgedChain<Runtime, GPI>>,
+		LaneIdOf<Runtime, MPI>,
 	>,
 	relayers_state: UnrewardedRelayersState,
 ) -> Runtime::RuntimeCall
@@ -152,7 +160,7 @@ where
 }
 
 /// Prepare storage proofs of messages, stored at the (bridged) source GRANDPA chain.
-pub fn make_complex_relayer_delivery_proofs<BridgedChain, ThisChainWithMessages>(
+pub fn make_complex_relayer_delivery_proofs<BridgedChain, ThisChainWithMessages, LaneId>(
 	lane_id: LaneId,
 	xcm_message: Xcm<()>,
 	message_nonce: MessageNonce,
@@ -162,17 +170,18 @@ pub fn make_complex_relayer_delivery_proofs<BridgedChain, ThisChainWithMessages>
 ) -> (
 	HeaderOf<BridgedChain>,
 	GrandpaJustification<HeaderOf<BridgedChain>>,
-	FromBridgedChainMessagesProof<HashOf<BridgedChain>>,
+	FromBridgedChainMessagesProof<HashOf<BridgedChain>, LaneId>,
 )
 where
 	BridgedChain: ChainWithGrandpa,
 	ThisChainWithMessages: ChainWithMessages,
+	LaneId: Copy + Encode,
 {
 	// prepare message
 	let message_payload = prepare_inbound_xcm(xcm_message, message_destination);
 	// prepare storage proof containing message
 	let (state_root, storage_proof) =
-		prepare_messages_storage_proof::<BridgedChain, ThisChainWithMessages>(
+		prepare_messages_storage_proof::<BridgedChain, ThisChainWithMessages, LaneId>(
 			lane_id,
 			message_nonce..=message_nonce,
 			None,
@@ -206,6 +215,7 @@ pub fn make_complex_relayer_confirmation_proofs<
 	BridgedChain,
 	ThisChainWithMessages,
 	InnerXcmRuntimeCall,
+	LaneId,
 >(
 	lane_id: LaneId,
 	header_number: BlockNumberOf<BridgedChain>,
@@ -214,15 +224,16 @@ pub fn make_complex_relayer_confirmation_proofs<
 ) -> (
 	HeaderOf<BridgedChain>,
 	GrandpaJustification<HeaderOf<BridgedChain>>,
-	FromBridgedChainMessagesDeliveryProof<HashOf<BridgedChain>>,
+	FromBridgedChainMessagesDeliveryProof<HashOf<BridgedChain>, LaneId>,
 )
 where
 	BridgedChain: ChainWithGrandpa,
 	ThisChainWithMessages: ChainWithMessages,
+	LaneId: Copy + Encode,
 {
 	// prepare storage proof containing message delivery proof
 	let (state_root, storage_proof) =
-		prepare_message_delivery_storage_proof::<BridgedChain, ThisChainWithMessages>(
+		prepare_message_delivery_storage_proof::<BridgedChain, ThisChainWithMessages, LaneId>(
 			lane_id,
 			InboundLaneData {
 				state: LaneState::Opened,
diff --git a/cumulus/parachains/runtimes/bridge-hubs/test-utils/src/test_data/from_parachain.rs b/cumulus/parachains/runtimes/bridge-hubs/test-utils/src/test_data/from_parachain.rs
index aefbc0dbd0a786dfc3873445fdf53a2c1991fc3c..a6659b8241dfd50aedbfe14ac2672756fa0a03af 100644
--- a/cumulus/parachains/runtimes/bridge-hubs/test-utils/src/test_data/from_parachain.rs
+++ b/cumulus/parachains/runtimes/bridge-hubs/test-utils/src/test_data/from_parachain.rs
@@ -20,7 +20,7 @@ use super::{from_grandpa_chain::make_complex_bridged_grandpa_header_proof, prepa
 
 use bp_messages::{
 	source_chain::FromBridgedChainMessagesDeliveryProof,
-	target_chain::FromBridgedChainMessagesProof, ChainWithMessages, LaneId, LaneState,
+	target_chain::FromBridgedChainMessagesProof, ChainWithMessages, LaneState,
 	UnrewardedRelayersState, Weight,
 };
 use bp_parachains::{RelayBlockHash, RelayBlockNumber};
@@ -43,7 +43,7 @@ use pallet_bridge_messages::{
 		encode_all_messages, encode_lane_data, prepare_message_delivery_storage_proof,
 		prepare_messages_storage_proof,
 	},
-	BridgedChainOf,
+	BridgedChainOf, LaneIdOf,
 };
 use sp_runtime::SaturatedConversion;
 
@@ -53,7 +53,7 @@ pub fn make_complex_relayer_delivery_batch<Runtime, GPI, PPI, MPI>(
 	grandpa_justification: GrandpaJustification<BridgedHeader<Runtime, GPI>>,
 	parachain_heads: Vec<(ParaId, ParaHash)>,
 	para_heads_proof: ParaHeadsProof,
-	message_proof: FromBridgedChainMessagesProof<ParaHash>,
+	message_proof: FromBridgedChainMessagesProof<ParaHash, LaneIdOf<Runtime, MPI>>,
 	relayer_id_at_bridged_chain: InboundRelayerId<Runtime, MPI>,
 ) -> pallet_utility::Call<Runtime>
 where
@@ -106,7 +106,7 @@ pub fn make_complex_relayer_confirmation_batch<Runtime, GPI, PPI, MPI>(
 	grandpa_justification: GrandpaJustification<BridgedHeader<Runtime, GPI>>,
 	parachain_heads: Vec<(ParaId, ParaHash)>,
 	para_heads_proof: ParaHeadsProof,
-	message_delivery_proof: FromBridgedChainMessagesDeliveryProof<ParaHash>,
+	message_delivery_proof: FromBridgedChainMessagesDeliveryProof<ParaHash, LaneIdOf<Runtime, MPI>>,
 	relayers_state: UnrewardedRelayersState,
 ) -> pallet_utility::Call<Runtime>
 where
@@ -154,7 +154,7 @@ where
 
 /// Prepare a call with message proof.
 pub fn make_standalone_relayer_delivery_call<Runtime, MPI>(
-	message_proof: FromBridgedChainMessagesProof<ParaHash>,
+	message_proof: FromBridgedChainMessagesProof<ParaHash, LaneIdOf<Runtime, MPI>>,
 	relayer_id_at_bridged_chain: InboundRelayerId<Runtime, MPI>,
 ) -> Runtime::RuntimeCall
 where
@@ -174,7 +174,7 @@ where
 
 /// Prepare a call with message delivery proof.
 pub fn make_standalone_relayer_confirmation_call<Runtime, MPI>(
-	message_delivery_proof: FromBridgedChainMessagesDeliveryProof<ParaHash>,
+	message_delivery_proof: FromBridgedChainMessagesDeliveryProof<ParaHash, LaneIdOf<Runtime, MPI>>,
 	relayers_state: UnrewardedRelayersState,
 ) -> Runtime::RuntimeCall
 where
@@ -195,6 +195,7 @@ pub fn make_complex_relayer_delivery_proofs<
 	BridgedRelayChain,
 	BridgedParachain,
 	ThisChainWithMessages,
+	LaneId,
 >(
 	lane_id: LaneId,
 	xcm_message: Xcm<()>,
@@ -210,19 +211,20 @@ pub fn make_complex_relayer_delivery_proofs<
 	ParaHead,
 	Vec<(ParaId, ParaHash)>,
 	ParaHeadsProof,
-	FromBridgedChainMessagesProof<ParaHash>,
+	FromBridgedChainMessagesProof<ParaHash, LaneId>,
 )
 where
 	BridgedRelayChain:
 		bp_runtime::Chain<Hash = RelayBlockHash, BlockNumber = RelayBlockNumber> + ChainWithGrandpa,
 	BridgedParachain: bp_runtime::Chain<Hash = ParaHash> + Parachain,
 	ThisChainWithMessages: ChainWithMessages,
+	LaneId: Copy + Encode,
 {
 	// prepare message
 	let message_payload = prepare_inbound_xcm(xcm_message, message_destination);
 	// prepare para storage proof containing message
 	let (para_state_root, para_storage_proof) =
-		prepare_messages_storage_proof::<BridgedParachain, ThisChainWithMessages>(
+		prepare_messages_storage_proof::<BridgedParachain, ThisChainWithMessages, LaneId>(
 			lane_id,
 			message_nonce..=message_nonce,
 			None,
@@ -266,6 +268,7 @@ pub fn make_complex_relayer_confirmation_proofs<
 	BridgedRelayChain,
 	BridgedParachain,
 	ThisChainWithMessages,
+	LaneId,
 >(
 	lane_id: LaneId,
 	para_header_number: u32,
@@ -279,17 +282,18 @@ pub fn make_complex_relayer_confirmation_proofs<
 	ParaHead,
 	Vec<(ParaId, ParaHash)>,
 	ParaHeadsProof,
-	FromBridgedChainMessagesDeliveryProof<ParaHash>,
+	FromBridgedChainMessagesDeliveryProof<ParaHash, LaneId>,
 )
 where
 	BridgedRelayChain:
 		bp_runtime::Chain<Hash = RelayBlockHash, BlockNumber = RelayBlockNumber> + ChainWithGrandpa,
 	BridgedParachain: bp_runtime::Chain<Hash = ParaHash> + Parachain,
 	ThisChainWithMessages: ChainWithMessages,
+	LaneId: Copy + Encode,
 {
 	// prepare para storage proof containing message delivery proof
 	let (para_state_root, para_storage_proof) =
-		prepare_message_delivery_storage_proof::<BridgedParachain, ThisChainWithMessages>(
+		prepare_message_delivery_storage_proof::<BridgedParachain, ThisChainWithMessages, LaneId>(
 			lane_id,
 			InboundLaneData {
 				state: LaneState::Opened,
diff --git a/cumulus/parachains/runtimes/bridge-hubs/test-utils/src/test_data/mod.rs b/cumulus/parachains/runtimes/bridge-hubs/test-utils/src/test_data/mod.rs
index 106eacd799ca3aee1daea2769423976154d50337..c34188af506896d9fb4e18781b09d804077c3941 100644
--- a/cumulus/parachains/runtimes/bridge-hubs/test-utils/src/test_data/mod.rs
+++ b/cumulus/parachains/runtimes/bridge-hubs/test-utils/src/test_data/mod.rs
@@ -21,7 +21,7 @@ pub mod from_parachain;
 
 use bp_messages::{
 	target_chain::{DispatchMessage, DispatchMessageData},
-	LaneId, MessageKey,
+	MessageKey,
 };
 use codec::Encode;
 use frame_support::traits::Get;
@@ -65,11 +65,11 @@ pub(crate) fn dummy_xcm() -> Xcm<()> {
 	vec![Trap(42)].into()
 }
 
-pub(crate) fn dispatch_message(
+pub(crate) fn dispatch_message<LaneId: Encode>(
 	lane_id: LaneId,
 	nonce: MessageNonce,
 	payload: Vec<u8>,
-) -> DispatchMessage<Vec<u8>> {
+) -> DispatchMessage<Vec<u8>, LaneId> {
 	DispatchMessage {
 		key: MessageKey { lane_id, nonce },
 		data: DispatchMessageData { payload: Ok(payload) },
diff --git a/cumulus/parachains/runtimes/test-utils/src/lib.rs b/cumulus/parachains/runtimes/test-utils/src/lib.rs
index fe75b2b6e72f2254867c94253c38f2ebef718a92..3b38eee244f10931bdd7703cf5a59f88a363efaa 100644
--- a/cumulus/parachains/runtimes/test-utils/src/lib.rs
+++ b/cumulus/parachains/runtimes/test-utils/src/lib.rs
@@ -465,8 +465,8 @@ impl<
 	}
 
 	pub fn execute_as_origin_xcm<Call: GetDispatchInfo + Encode>(
-		call: Call,
 		origin: Location,
+		call: Call,
 		buy_execution_fee: Asset,
 	) -> Outcome {
 		// prepare `Transact` xcm
diff --git a/cumulus/polkadot-parachain/Cargo.toml b/cumulus/polkadot-parachain/Cargo.toml
index ad88be60d747be9136ae7e5bde9c47c1a54d41bf..d52bc639b3bc9730c08bffd05438dacfb41c67a5 100644
--- a/cumulus/polkadot-parachain/Cargo.toml
+++ b/cumulus/polkadot-parachain/Cargo.toml
@@ -57,6 +57,9 @@ xcm = { workspace = true, default-features = true }
 # Cumulus
 cumulus-primitives-core = { workspace = true, default-features = true }
 
+# Bridges
+bp-messages = { workspace = true, default-features = true }
+
 [build-dependencies]
 substrate-build-script-utils = { workspace = true, default-features = true }
 
diff --git a/docker/dockerfiles/bridges_zombienet_tests_injected.Dockerfile b/docker/dockerfiles/bridges_zombienet_tests_injected.Dockerfile
index 55b9156e6a0ae811c8ef1d2bf8761452ae8c561a..2198da13a4b7e81773e8314afbaa1467e8fc0aef 100644
--- a/docker/dockerfiles/bridges_zombienet_tests_injected.Dockerfile
+++ b/docker/dockerfiles/bridges_zombienet_tests_injected.Dockerfile
@@ -1,7 +1,7 @@
 # this image is built on top of existing Zombienet image
 ARG ZOMBIENET_IMAGE
 # this image uses substrate-relay image built elsewhere
-ARG SUBSTRATE_RELAY_IMAGE=docker.io/paritytech/substrate-relay:v1.6.8
+ARG SUBSTRATE_RELAY_IMAGE=docker.io/paritytech/substrate-relay:v1.6.10
 
 # metadata
 ARG VCS_REF
diff --git a/polkadot/xcm/xcm-executor/src/traits/export.rs b/polkadot/xcm/xcm-executor/src/traits/export.rs
index 78aa68ce2644a8bd6d4ab1075d9d93923b31c3cc..b356e0da7df7801b52cb63652fa321507223a824 100644
--- a/polkadot/xcm/xcm-executor/src/traits/export.rs
+++ b/polkadot/xcm/xcm-executor/src/traits/export.rs
@@ -20,7 +20,7 @@ use xcm::latest::prelude::*;
 /// spoofed origin. This essentially defines the behaviour of the `ExportMessage` XCM instruction.
 ///
 /// This is quite different to `SendXcm`; `SendXcm` assumes that the local side's location will be
-/// preserved to be represented as the value of the Origin register in the messages execution.
+/// preserved to be represented as the value of the Origin register during the message's execution.
 ///
 /// This trait on the other hand assumes that we do not necessarily want the Origin register to
 /// contain the local (i.e. the caller chain's) location, since it will generally be exporting a
@@ -44,8 +44,8 @@ pub trait ExportXcm {
 	/// The `destination` and `message` must be `Some` (or else an error will be returned) and they
 	/// may only be consumed if the `Err` is not `NotApplicable`.
 	///
-	/// If it is not a destination which can be reached with this type but possibly could by others,
-	/// then this *MUST* return `NotApplicable`. Any other error will cause the tuple
+	/// If it is not a destination that can be reached with this type, but possibly could be with
+	/// others, then this *MUST* return `NotApplicable`. Any other error will cause the tuple
 	/// implementation (used to compose routing systems from different delivery agents) to exit
 	/// early without trying alternative means of delivery.
 	fn validate(
diff --git a/prdoc/pr_5649.prdoc b/prdoc/pr_5649.prdoc
new file mode 100644
index 0000000000000000000000000000000000000000..1f4c97aa1753a924c87050d8d08a904042f66ac5
--- /dev/null
+++ b/prdoc/pr_5649.prdoc
@@ -0,0 +1,49 @@
+title: "Bridges lane id agnostic for backwards compatibility"
+
+doc:
+- audience: Runtime Dev
+  description: |
+    This PR improves support for handling `LaneId` backwards compatibility with the previously merged [PR](https://github.com/paritytech/polkadot-sdk/pull/4949).
+    If `pallet_bridge_messages` or `pallet_bridge_relayers` used `LaneId([u8; 4])` previously, they should now set `type LaneId = LegacyLaneId;`.
+
+crates:
+- name: bridge-runtime-common
+  bump: patch
+- name: bp-runtime
+  bump: patch
+- name: staging-xcm-executor
+  bump: none
+- name: parachains-runtimes-test-utils
+  bump: patch
+- name: bp-messages
+  bump: major
+- name: bp-relayers
+  bump: major
+- name: bp-xcm-bridge-hub
+  bump: major
+- name: pallet-bridge-messages
+  bump: patch
+- name: pallet-bridge-relayers
+  bump: patch
+- name: pallet-xcm-bridge-hub
+  bump: major
+- name: emulated-integration-tests-common
+  bump: patch
+- name: bp-bridge-hub-kusama
+  bump: patch
+- name: bp-bridge-hub-polkadot
+  bump: patch
+- name: bp-bridge-hub-rococo
+  bump: patch
+- name: bp-bridge-hub-westend
+  bump: patch
+- name: bp-polkadot-bulletin
+  bump: patch
+- name: bridge-hub-rococo-runtime
+  bump: major
+- name: bridge-hub-westend-runtime
+  bump: patch
+- name: polkadot-parachain-bin
+  bump: none
+- name: bridge-hub-test-utils
+  bump: major