From 1f8e44831d0a743b116dfb5948ea9f4756955962 Mon Sep 17 00:00:00 2001
From: Branislav Kontur <bkontur@gmail.com>
Date: Fri, 12 Jul 2024 10:20:56 +0200
Subject: [PATCH] Bridges V2 refactoring backport and `pallet_bridge_messages`
 simplifications (#4935)

## Summary

This PR contains migrated code from the Bridges V2
[branch](https://github.com/paritytech/polkadot-sdk/pull/4427) from the
old `parity-bridges-common`
[repo](https://github.com/paritytech/parity-bridges-common/tree/bridges-v2).
Even though the PR looks large, it does not (or should not) contain any
significant changes (also not relevant for audit).
This PR is a requirement for permissionless lanes, as they were
implemented on top of these changes.

## TODO

- [x] generate fresh weights for BridgeHubs
- [x] run `polkadot-fellows` bridges zombienet tests with actual runtime
1.2.5. or 1.2.6 to check compatibility
- :ballot_box_with_check: working, checked with 1.2.8 fellows BridgeHubs
- [x] run `polkadot-sdk` bridges zombienet tests
  - :ballot_box_with_check: with old relayer in CI (1.6.5)
- [x] run `polkadot-sdk` bridges zombienet tests (locally) - with the
relayer based on this branch -
https://github.com/paritytech/parity-bridges-common/pull/3022
- [x] check/fix relayer companion in bridges repo -
https://github.com/paritytech/parity-bridges-common/pull/3022
- [x] extract pruning stuff to separate PR
https://github.com/paritytech/polkadot-sdk/pull/4944

Relates to:
https://github.com/paritytech/parity-bridges-common/issues/2976
Relates to:
https://github.com/paritytech/parity-bridges-common/issues/2451

---------

Signed-off-by: Branislav Kontur <bkontur@gmail.com>
Co-authored-by: Serban Iorga <serban@parity.io>
Co-authored-by: Svyatoslav Nikolsky <svyatonik@gmail.com>
Co-authored-by: command-bot <>
---
 Cargo.lock                                    |   19 +-
 bridges/bin/runtime-common/Cargo.toml         |   24 +-
 .../src/extensions/priority_calculator.rs     |    3 +-
 .../extensions/refund_relayer_extension.rs    |   77 +-
 bridges/bin/runtime-common/src/integrity.rs   |  165 +-
 bridges/bin/runtime-common/src/lib.rs         |    3 +-
 bridges/bin/runtime-common/src/messages.rs    |  704 ---------
 .../src/messages_benchmarking.rs              |  141 +-
 .../runtime-common/src/messages_call_ext.rs   |   68 +-
 bridges/bin/runtime-common/src/mock.rs        |  135 +-
 .../src/parachains_benchmarking.rs            |   13 +-
 .../chains/chain-bridge-hub-kusama/src/lib.rs |    4 +-
 .../chain-bridge-hub-polkadot/src/lib.rs      |    4 +-
 .../chains/chain-bridge-hub-rococo/src/lib.rs |   12 +-
 .../chain-bridge-hub-westend/src/lib.rs       |    4 +-
 bridges/chains/chain-kusama/src/lib.rs        |    4 +-
 .../chains/chain-polkadot-bulletin/src/lib.rs |    6 +-
 bridges/chains/chain-polkadot/src/lib.rs      |    4 +-
 bridges/chains/chain-rococo/src/lib.rs        |    4 +-
 bridges/chains/chain-westend/src/lib.rs       |    4 +-
 bridges/modules/beefy/src/mock.rs             |    3 +
 bridges/modules/grandpa/Cargo.toml            |    5 +-
 bridges/modules/grandpa/src/lib.rs            |   13 +-
 bridges/modules/grandpa/src/mock.rs           |    5 +-
 bridges/modules/messages/Cargo.toml           |   29 +-
 bridges/modules/messages/README.md            |  105 +-
 bridges/modules/messages/src/benchmarking.rs  |  426 ++---
 bridges/modules/messages/src/inbound_lane.rs  |   35 +-
 bridges/modules/messages/src/lib.rs           | 1383 +----------------
 bridges/modules/messages/src/outbound_lane.rs |   71 +-
 bridges/modules/messages/src/proofs.rs        |  562 +++++++
 .../src/tests}/messages_generation.rs         |   95 +-
 .../modules/messages/src/{ => tests}/mock.rs  |  322 ++--
 bridges/modules/messages/src/tests/mod.rs     |   26 +
 .../messages/src/tests/pallet_tests.rs        | 1100 +++++++++++++
 bridges/modules/messages/src/weights.rs       |  311 ++--
 bridges/modules/messages/src/weights_ext.rs   |   64 +-
 bridges/modules/parachains/Cargo.toml         |    2 -
 .../modules/parachains/src/benchmarking.rs    |   10 +-
 bridges/modules/parachains/src/call_ext.rs    |    4 +-
 bridges/modules/parachains/src/lib.rs         |   39 +-
 bridges/modules/parachains/src/mock.rs        |   47 +-
 bridges/modules/parachains/src/proofs.rs      |   81 +
 bridges/modules/parachains/src/weights.rs     |   60 +-
 .../modules/relayers/src/payment_adapter.rs   |    3 +-
 bridges/modules/xcm-bridge-hub/src/mock.rs    |  108 +-
 bridges/primitives/header-chain/src/lib.rs    |   11 +-
 bridges/primitives/messages/src/lib.rs        |   79 +-
 .../primitives/messages/src/source_chain.rs   |   88 +-
 .../primitives/messages/src/target_chain.rs   |   95 +-
 .../polkadot-core/src/parachains.rs           |   10 +-
 bridges/primitives/runtime/Cargo.toml         |    1 +
 bridges/primitives/runtime/src/chain.rs       |    8 +-
 bridges/primitives/runtime/src/lib.rs         |   45 +-
 .../primitives/runtime/src/storage_proof.rs   |  276 +++-
 bridges/primitives/test-utils/Cargo.toml      |    2 +-
 bridges/primitives/test-utils/src/lib.rs      |    4 +-
 bridges/relays/client-substrate/Cargo.toml    |    3 -
 .../client-substrate/src/client/caching.rs    |    6 +-
 .../relays/client-substrate/src/client/rpc.rs |   32 +-
 .../client-substrate/src/client/traits.rs     |    8 +-
 bridges/relays/client-substrate/src/error.rs  |    3 -
 .../relays/client-substrate/src/test_chain.rs |    6 +-
 bridges/relays/finality/src/base.rs           |    4 -
 bridges/relays/lib-substrate-relay/Cargo.toml |    6 +-
 .../lib-substrate-relay/src/cli/bridge.rs     |    2 +-
 .../src/cli/relay_headers_and_messages/mod.rs |   10 +-
 .../src/cli/relay_messages.rs                 |    8 +-
 bridges/relays/lib-substrate-relay/src/lib.rs |   19 +-
 .../metrics.rs}                               |    0
 .../src/{messages_lane.rs => messages/mod.rs} |  421 ++++-
 .../source.rs}                                |   26 +-
 .../target.rs}                                |   32 +-
 .../src/on_demand/parachains.rs               |    4 +-
 .../src/parachains/source.rs                  |   22 +-
 bridges/relays/messages/Cargo.toml            |    1 -
 .../relays/parachains/src/parachains_loop.rs  |    4 +-
 .../weights/pallet_xcm_bridge_hub_router.rs   |   50 +-
 .../weights/pallet_xcm_bridge_hub_router.rs   |   60 +-
 .../bridge-hubs/bridge-hub-rococo/Cargo.toml  |    2 +-
 .../src/bridge_common_config.rs               |   19 +-
 .../src/bridge_to_bulletin_config.rs          |   67 +-
 .../src/bridge_to_westend_config.rs           |  100 +-
 .../bridge-hubs/bridge-hub-rococo/src/lib.rs  |   12 +-
 .../src/weights/pallet_bridge_grandpa.rs      |   20 +-
 ...idge_messages_rococo_to_rococo_bulletin.rs |   78 +-
 ...allet_bridge_messages_rococo_to_westend.rs |   80 +-
 .../src/weights/pallet_bridge_parachains.rs   |   18 +-
 .../src/weights/pallet_bridge_relayers.rs     |   22 +-
 .../xcm/pallet_xcm_benchmarks_generic.rs      |  128 +-
 .../bridge-hub-rococo/tests/tests.rs          |   49 +-
 .../bridge-hubs/bridge-hub-westend/Cargo.toml |    2 +-
 .../src/bridge_to_rococo_config.rs            |  107 +-
 .../bridge-hubs/bridge-hub-westend/src/lib.rs |    8 +-
 .../src/weights/pallet_bridge_grandpa.rs      |   20 +-
 .../src/weights/pallet_bridge_messages.rs     |   76 +-
 .../src/weights/pallet_bridge_parachains.rs   |   18 +-
 .../src/weights/pallet_bridge_relayers.rs     |   22 +-
 .../xcm/pallet_xcm_benchmarks_generic.rs      |  128 +-
 .../bridge-hub-westend/tests/tests.rs         |    5 +-
 .../bridge-hubs/test-utils/Cargo.toml         |    2 +-
 .../src/test_cases/from_grandpa_chain.rs      |  128 +-
 .../src/test_cases/from_parachain.rs          |  135 +-
 .../test-utils/src/test_cases/helpers.rs      |    9 +-
 .../src/test_data/from_grandpa_chain.rs       |  182 +--
 .../src/test_data/from_parachain.rs           |  180 ++-
 ...ridges_zombienet_tests_injected.Dockerfile |    2 +-
 prdoc/pr_4935.prdoc                           |   75 +
 108 files changed, 4708 insertions(+), 4639 deletions(-)
 delete mode 100644 bridges/bin/runtime-common/src/messages.rs
 create mode 100644 bridges/modules/messages/src/proofs.rs
 rename bridges/{bin/runtime-common/src => modules/messages/src/tests}/messages_generation.rs (62%)
 rename bridges/modules/messages/src/{ => tests}/mock.rs (62%)
 create mode 100644 bridges/modules/messages/src/tests/mod.rs
 create mode 100644 bridges/modules/messages/src/tests/pallet_tests.rs
 create mode 100644 bridges/modules/parachains/src/proofs.rs
 rename bridges/relays/lib-substrate-relay/src/{messages_metrics.rs => messages/metrics.rs} (100%)
 rename bridges/relays/lib-substrate-relay/src/{messages_lane.rs => messages/mod.rs} (63%)
 rename bridges/relays/lib-substrate-relay/src/{messages_source.rs => messages/source.rs} (97%)
 rename bridges/relays/lib-substrate-relay/src/{messages_target.rs => messages/target.rs} (94%)
 create mode 100644 prdoc/pr_4935.prdoc

diff --git a/Cargo.lock b/Cargo.lock
index 43601b85ca2..4a7d47eb0f7 100644
--- a/Cargo.lock
+++ b/Cargo.lock
@@ -2359,7 +2359,6 @@ dependencies = [
  "bp-xcm-bridge-hub-router",
  "frame-support",
  "frame-system",
- "hash-db",
  "log",
  "pallet-balances",
  "pallet-bridge-grandpa",
@@ -2370,8 +2369,6 @@ dependencies = [
  "pallet-utility",
  "parity-scale-codec",
  "scale-info",
- "sp-api",
- "sp-core",
  "sp-io",
  "sp-runtime",
  "sp-std 14.0.0",
@@ -8605,7 +8602,6 @@ dependencies = [
  "async-std",
  "async-trait",
  "bp-messages",
- "env_logger 0.11.3",
  "finality-relay",
  "futures",
  "hex",
@@ -10069,7 +10065,6 @@ dependencies = [
  "bp-header-chain",
  "bp-runtime",
  "bp-test-utils",
- "finality-grandpa",
  "frame-benchmarking",
  "frame-support",
  "frame-system",
@@ -10081,13 +10076,13 @@ dependencies = [
  "sp-io",
  "sp-runtime",
  "sp-std 14.0.0",
- "sp-trie",
 ]
 
 [[package]]
 name = "pallet-bridge-messages"
 version = "0.7.0"
 dependencies = [
+ "bp-header-chain",
  "bp-messages",
  "bp-runtime",
  "bp-test-utils",
@@ -10095,13 +10090,15 @@ dependencies = [
  "frame-support",
  "frame-system",
  "log",
- "num-traits",
  "pallet-balances",
+ "pallet-bridge-grandpa",
  "parity-scale-codec",
  "scale-info",
+ "sp-core",
  "sp-io",
  "sp-runtime",
  "sp-std 14.0.0",
+ "sp-trie",
 ]
 
 [[package]]
@@ -10124,7 +10121,6 @@ dependencies = [
  "sp-io",
  "sp-runtime",
  "sp-std 14.0.0",
- "sp-trie",
 ]
 
 [[package]]
@@ -16205,13 +16201,10 @@ dependencies = [
  "bp-runtime",
  "finality-relay",
  "frame-support",
- "frame-system",
  "futures",
  "jsonrpsee",
  "log",
  "num-traits",
- "pallet-balances",
- "pallet-bridge-messages",
  "pallet-transaction-payment",
  "pallet-transaction-payment-rpc-runtime-api",
  "pallet-utility",
@@ -21388,9 +21381,7 @@ dependencies = [
  "bp-polkadot-core",
  "bp-relayers",
  "bp-runtime",
- "bridge-runtime-common",
  "equivocation-detector",
- "finality-grandpa",
  "finality-relay",
  "frame-support",
  "frame-system",
@@ -21410,9 +21401,11 @@ dependencies = [
  "rbtag",
  "relay-substrate-client",
  "relay-utils",
+ "scale-info",
  "sp-consensus-grandpa",
  "sp-core",
  "sp-runtime",
+ "sp-trie",
  "structopt",
  "strum 0.26.2",
  "thiserror",
diff --git a/bridges/bin/runtime-common/Cargo.toml b/bridges/bin/runtime-common/Cargo.toml
index d69a064aab8..36f27b6aa03 100644
--- a/bridges/bin/runtime-common/Cargo.toml
+++ b/bridges/bin/runtime-common/Cargo.toml
@@ -12,14 +12,12 @@ workspace = true
 
 [dependencies]
 codec = { features = ["derive"], workspace = true }
-hash-db = { workspace = true }
 log = { workspace = true }
 scale-info = { features = ["derive"], workspace = true }
 static_assertions = { optional = true, workspace = true, default-features = true }
 tuplex = { workspace = true }
 
 # Bridge dependencies
-
 bp-header-chain = { workspace = true }
 bp-messages = { workspace = true }
 bp-parachains = { workspace = true }
@@ -34,25 +32,23 @@ pallet-bridge-parachains = { workspace = true }
 pallet-bridge-relayers = { workspace = true }
 
 # Substrate dependencies
-
 frame-support = { workspace = true }
 frame-system = { workspace = true }
 pallet-transaction-payment = { workspace = true }
 pallet-utility = { workspace = true }
-sp-api = { workspace = true }
-sp-core = { workspace = true }
 sp-io = { workspace = true }
 sp-runtime = { workspace = true }
 sp-std = { workspace = true }
-sp-trie = { workspace = true }
+sp-trie = { optional = true, workspace = true }
 
 # Polkadot dependencies
 xcm = { workspace = true }
 xcm-builder = { workspace = true }
 
 [dev-dependencies]
-bp-test-utils = { workspace = true, default-features = true }
-pallet-balances = { workspace = true, default-features = true }
+bp-test-utils = { workspace = true }
+pallet-balances = { workspace = true }
+pallet-bridge-messages = { features = ["std", "test-helpers"], workspace = true }
 
 [features]
 default = ["std"]
@@ -63,13 +59,14 @@ std = [
 	"bp-polkadot-core/std",
 	"bp-relayers/std",
 	"bp-runtime/std",
+	"bp-test-utils/std",
 	"bp-xcm-bridge-hub-router/std",
 	"bp-xcm-bridge-hub/std",
 	"codec/std",
 	"frame-support/std",
 	"frame-system/std",
-	"hash-db/std",
 	"log/std",
+	"pallet-balances/std",
 	"pallet-bridge-grandpa/std",
 	"pallet-bridge-messages/std",
 	"pallet-bridge-parachains/std",
@@ -77,8 +74,6 @@ std = [
 	"pallet-transaction-payment/std",
 	"pallet-utility/std",
 	"scale-info/std",
-	"sp-api/std",
-	"sp-core/std",
 	"sp-io/std",
 	"sp-runtime/std",
 	"sp-std/std",
@@ -88,15 +83,22 @@ std = [
 	"xcm/std",
 ]
 runtime-benchmarks = [
+	"bp-runtime/test-helpers",
 	"frame-support/runtime-benchmarks",
 	"frame-system/runtime-benchmarks",
 	"pallet-balances/runtime-benchmarks",
 	"pallet-bridge-grandpa/runtime-benchmarks",
 	"pallet-bridge-messages/runtime-benchmarks",
+	"pallet-bridge-messages/test-helpers",
 	"pallet-bridge-parachains/runtime-benchmarks",
 	"pallet-bridge-relayers/runtime-benchmarks",
 	"pallet-utility/runtime-benchmarks",
 	"sp-runtime/runtime-benchmarks",
+	"sp-trie",
 	"xcm-builder/runtime-benchmarks",
 ]
 integrity-test = ["static_assertions"]
+test-helpers = [
+	"bp-runtime/test-helpers",
+	"sp-trie",
+]
diff --git a/bridges/bin/runtime-common/src/extensions/priority_calculator.rs b/bridges/bin/runtime-common/src/extensions/priority_calculator.rs
index 92810290f95..9f559dc13b6 100644
--- a/bridges/bin/runtime-common/src/extensions/priority_calculator.rs
+++ b/bridges/bin/runtime-common/src/extensions/priority_calculator.rs
@@ -319,6 +319,7 @@ mod integrity_tests {
 	pub mod per_message {
 		use super::*;
 
+		use bp_messages::ChainWithMessages;
 		use pallet_bridge_messages::WeightInfoExt;
 
 		/// Ensures that the value of `PriorityBoostPerMessage` matches the value of
@@ -339,7 +340,7 @@ mod integrity_tests {
 			BalanceOf<Runtime>: Send + Sync + FixedPointOperand,
 		{
 			let maximal_messages_in_delivery_transaction =
-				Runtime::MaxUnconfirmedMessagesAtInboundLane::get();
+				Runtime::BridgedChain::MAX_UNCONFIRMED_MESSAGES_IN_CONFIRMATION_TX;
 			super::ensure_priority_boost_is_sane::<PriorityBoostPerMessage, BalanceOf<Runtime>>(
 				"PriorityBoostPerMessage",
 				maximal_messages_in_delivery_transaction,
diff --git a/bridges/bin/runtime-common/src/extensions/refund_relayer_extension.rs b/bridges/bin/runtime-common/src/extensions/refund_relayer_extension.rs
index 5aa7f1c095d..6ba3506377d 100644
--- a/bridges/bin/runtime-common/src/extensions/refund_relayer_extension.rs
+++ b/bridges/bin/runtime-common/src/extensions/refund_relayer_extension.rs
@@ -22,9 +22,9 @@
 use crate::messages_call_ext::{
 	CallHelper as MessagesCallHelper, CallInfo as MessagesCallInfo, MessagesCallSubType,
 };
-use bp_messages::{LaneId, MessageNonce};
+use bp_messages::{ChainWithMessages, LaneId, MessageNonce};
 use bp_relayers::{ExplicitOrAccountParams, RewardsAccountOwner, RewardsAccountParams};
-use bp_runtime::{Parachain, RangeInclusiveExt, StaticStrProvider};
+use bp_runtime::{Chain, Parachain, RangeInclusiveExt, StaticStrProvider};
 use codec::{Codec, Decode, Encode};
 use frame_support::{
 	dispatch::{CallableCallFor, DispatchInfo, PostDispatchInfo},
@@ -293,7 +293,7 @@ pub trait RefundSignedExtension:
 				<Self::Msgs as RefundableMessagesLaneId>::Id::get(),
 				<Self::Runtime as MessagesConfig<
 					<Self::Msgs as RefundableMessagesLaneId>::Instance,
-				>>::BridgedChainId::get(),
+				>>::BridgedChain::ID,
 				if call_info.is_receive_messages_proof_call() {
 					RewardsAccountOwner::ThisChain
 				} else {
@@ -406,8 +406,7 @@ pub trait RefundSignedExtension:
 		// a quick check to avoid invalid high-priority transactions
 		let max_unconfirmed_messages_in_confirmation_tx = <Self::Runtime as MessagesConfig<
 			<Self::Msgs as RefundableMessagesLaneId>::Instance,
-		>>::MaxUnconfirmedMessagesAtInboundLane::get(
-		);
+		>>::BridgedChain::MAX_UNCONFIRMED_MESSAGES_IN_CONFIRMATION_TX;
 		if bundled_messages > max_unconfirmed_messages_in_confirmation_tx {
 			return None
 		}
@@ -935,9 +934,6 @@ where
 pub(crate) mod tests {
 	use super::*;
 	use crate::{
-		messages::{
-			source::FromBridgedChainMessagesDeliveryProof, target::FromBridgedChainMessagesProof,
-		},
 		messages_call_ext::{
 			BaseMessagesProofInfo, ReceiveMessagesDeliveryProofInfo, ReceiveMessagesProofInfo,
 			UnrewardedRelayerOccupation,
@@ -946,8 +942,10 @@ pub(crate) mod tests {
 	};
 	use bp_header_chain::StoredHeaderDataBuilder;
 	use bp_messages::{
-		DeliveredMessages, InboundLaneData, MessageNonce, MessagesOperatingMode, OutboundLaneData,
-		UnrewardedRelayer, UnrewardedRelayersState,
+		source_chain::FromBridgedChainMessagesDeliveryProof,
+		target_chain::FromBridgedChainMessagesProof, DeliveredMessages, InboundLaneData,
+		MessageNonce, MessagesOperatingMode, OutboundLaneData, UnrewardedRelayer,
+		UnrewardedRelayersState,
 	};
 	use bp_parachains::{BestParaHeadHash, ParaInfo};
 	use bp_polkadot_core::parachains::{ParaHeadsProof, ParaId};
@@ -1123,7 +1121,7 @@ pub(crate) mod tests {
 				ParaId(BridgedUnderlyingParachain::PARACHAIN_ID),
 				[parachain_head_at_relay_header_number as u8; 32].into(),
 			)],
-			parachain_heads_proof: ParaHeadsProof { storage_proof: vec![] },
+			parachain_heads_proof: ParaHeadsProof { storage_proof: Default::default() },
 		})
 	}
 
@@ -1136,7 +1134,7 @@ pub(crate) mod tests {
 				ParaId(BridgedUnderlyingParachain::PARACHAIN_ID),
 				[parachain_head_at_relay_header_number as u8; 32].into(),
 			)],
-			parachain_heads_proof: ParaHeadsProof { storage_proof: vec![] },
+			parachain_heads_proof: ParaHeadsProof { storage_proof: Default::default() },
 			is_free_execution_expected: false,
 		})
 	}
@@ -1144,9 +1142,9 @@ pub(crate) mod tests {
 	fn message_delivery_call(best_message: MessageNonce) -> RuntimeCall {
 		RuntimeCall::BridgeMessages(MessagesCall::receive_messages_proof {
 			relayer_id_at_bridged_chain: relayer_account_at_bridged_chain(),
-			proof: FromBridgedChainMessagesProof {
+			proof: Box::new(FromBridgedChainMessagesProof {
 				bridged_header_hash: Default::default(),
-				storage_proof: vec![],
+				storage_proof: Default::default(),
 				lane: TestLaneId::get(),
 				nonces_start: pallet_bridge_messages::InboundLanes::<TestRuntime>::get(
 					TEST_LANE_ID,
@@ -1154,7 +1152,7 @@ pub(crate) mod tests {
 				.last_delivered_nonce() +
 					1,
 				nonces_end: best_message,
-			},
+			}),
 			messages_count: 1,
 			dispatch_weight: Weight::zero(),
 		})
@@ -1164,7 +1162,7 @@ pub(crate) mod tests {
 		RuntimeCall::BridgeMessages(MessagesCall::receive_messages_delivery_proof {
 			proof: FromBridgedChainMessagesDeliveryProof {
 				bridged_header_hash: Default::default(),
-				storage_proof: vec![],
+				storage_proof: Default::default(),
 				lane: TestLaneId::get(),
 			},
 			relayers_state: UnrewardedRelayersState {
@@ -1327,8 +1325,10 @@ pub(crate) mod tests {
 						best_stored_nonce: 100,
 					},
 					unrewarded_relayers: UnrewardedRelayerOccupation {
-						free_relayer_slots: MaxUnrewardedRelayerEntriesAtInboundLane::get(),
-						free_message_slots: MaxUnconfirmedMessagesAtInboundLane::get(),
+						free_relayer_slots:
+							BridgedUnderlyingChain::MAX_UNREWARDED_RELAYERS_IN_CONFIRMATION_TX,
+						free_message_slots:
+							BridgedUnderlyingChain::MAX_UNCONFIRMED_MESSAGES_IN_CONFIRMATION_TX,
 					},
 				}),
 			),
@@ -1397,8 +1397,10 @@ pub(crate) mod tests {
 						best_stored_nonce: 100,
 					},
 					unrewarded_relayers: UnrewardedRelayerOccupation {
-						free_relayer_slots: MaxUnrewardedRelayerEntriesAtInboundLane::get(),
-						free_message_slots: MaxUnconfirmedMessagesAtInboundLane::get(),
+						free_relayer_slots:
+							BridgedUnderlyingChain::MAX_UNREWARDED_RELAYERS_IN_CONFIRMATION_TX,
+						free_message_slots:
+							BridgedUnderlyingChain::MAX_UNCONFIRMED_MESSAGES_IN_CONFIRMATION_TX,
 					},
 				}),
 			),
@@ -1459,8 +1461,10 @@ pub(crate) mod tests {
 						best_stored_nonce: 100,
 					},
 					unrewarded_relayers: UnrewardedRelayerOccupation {
-						free_relayer_slots: MaxUnrewardedRelayerEntriesAtInboundLane::get(),
-						free_message_slots: MaxUnconfirmedMessagesAtInboundLane::get(),
+						free_relayer_slots:
+							BridgedUnderlyingChain::MAX_UNREWARDED_RELAYERS_IN_CONFIRMATION_TX,
+						free_message_slots:
+							BridgedUnderlyingChain::MAX_UNCONFIRMED_MESSAGES_IN_CONFIRMATION_TX,
 					},
 				}),
 			),
@@ -1499,8 +1503,10 @@ pub(crate) mod tests {
 						best_stored_nonce: 100,
 					},
 					unrewarded_relayers: UnrewardedRelayerOccupation {
-						free_relayer_slots: MaxUnrewardedRelayerEntriesAtInboundLane::get(),
-						free_message_slots: MaxUnconfirmedMessagesAtInboundLane::get(),
+						free_relayer_slots:
+							BridgedUnderlyingChain::MAX_UNREWARDED_RELAYERS_IN_CONFIRMATION_TX,
+						free_message_slots:
+							BridgedUnderlyingChain::MAX_UNCONFIRMED_MESSAGES_IN_CONFIRMATION_TX,
 					},
 				},
 			)),
@@ -1735,14 +1741,16 @@ pub(crate) mod tests {
 
 			let fns = [run_validate, run_grandpa_validate, run_messages_validate];
 			for f in fns {
-				let priority_of_max_messages_delivery =
-					f(message_delivery_call(100 + MaxUnconfirmedMessagesAtInboundLane::get()))
-						.unwrap()
-						.priority;
-				let priority_of_more_than_max_messages_delivery =
-					f(message_delivery_call(100 + MaxUnconfirmedMessagesAtInboundLane::get() + 1))
-						.unwrap()
-						.priority;
+				let priority_of_max_messages_delivery = f(message_delivery_call(
+					100 + BridgedUnderlyingChain::MAX_UNCONFIRMED_MESSAGES_IN_CONFIRMATION_TX,
+				))
+				.unwrap()
+				.priority;
+				let priority_of_more_than_max_messages_delivery = f(message_delivery_call(
+					100 + BridgedUnderlyingChain::MAX_UNCONFIRMED_MESSAGES_IN_CONFIRMATION_TX + 1,
+				))
+				.unwrap()
+				.priority;
 
 				assert!(
 					priority_of_max_messages_delivery > priority_of_more_than_max_messages_delivery,
@@ -2103,7 +2111,7 @@ pub(crate) mod tests {
 								[1u8; 32].into(),
 							),
 						],
-						parachain_heads_proof: ParaHeadsProof { storage_proof: vec![] },
+						parachain_heads_proof: ParaHeadsProof { storage_proof: Default::default() },
 					}),
 					message_delivery_call(200),
 				],
@@ -2865,7 +2873,8 @@ pub(crate) mod tests {
 	#[test]
 	fn does_not_panic_on_boosting_priority_of_empty_message_delivery_transaction() {
 		run_test(|| {
-			let best_delivered_message = MaxUnconfirmedMessagesAtInboundLane::get();
+			let best_delivered_message =
+				BridgedUnderlyingChain::MAX_UNCONFIRMED_MESSAGES_IN_CONFIRMATION_TX;
 			initialize_environment(100, 100, best_delivered_message);
 
 			// register relayer so it gets priority boost
diff --git a/bridges/bin/runtime-common/src/integrity.rs b/bridges/bin/runtime-common/src/integrity.rs
index d3827a14dd6..f661db8a220 100644
--- a/bridges/bin/runtime-common/src/integrity.rs
+++ b/bridges/bin/runtime-common/src/integrity.rs
@@ -19,10 +19,9 @@
 //! Most of the tests in this module assume that the bridge is using standard (see `crate::messages`
 //! module for details) configuration.
 
-use crate::{messages, messages::MessageBridge};
-
-use bp_messages::{InboundLaneData, MessageNonce};
-use bp_runtime::{Chain, ChainId};
+use bp_header_chain::ChainWithGrandpa;
+use bp_messages::{ChainWithMessages, InboundLaneData, MessageNonce};
+use bp_runtime::Chain;
 use codec::Encode;
 use frame_support::{storage::generator::StorageValue, traits::Get, weights::Weight};
 use frame_system::limits;
@@ -50,23 +49,6 @@ macro_rules! assert_chain_types(
 	}
 );
 
-/// Macro that ensures that the bridge GRANDPA pallet is configured properly to bridge with given
-/// chain.
-#[macro_export]
-macro_rules! assert_bridge_grandpa_pallet_types(
-	( runtime: $r:path, with_bridged_chain_grandpa_instance: $i:path, bridged_chain: $bridged:path ) => {
-		{
-			// if one of asserts fail, then either bridge isn't configured properly (or alternatively - non-standard
-			// configuration is used), or something has broke existing configuration (meaning that all bridged chains
-			// and relays will stop functioning)
-			use pallet_bridge_grandpa::Config as GrandpaConfig;
-			use static_assertions::assert_type_eq_all;
-
-			assert_type_eq_all!(<$r as GrandpaConfig<$i>>::BridgedChain, $bridged);
-		}
-	}
-);
-
 /// Macro that ensures that the bridge messages pallet is configured properly to bridge using given
 /// configuration.
 #[macro_export]
@@ -74,32 +56,30 @@ macro_rules! assert_bridge_messages_pallet_types(
 	(
 		runtime: $r:path,
 		with_bridged_chain_messages_instance: $i:path,
-		bridge: $bridge:path
+		this_chain: $this:path,
+		bridged_chain: $bridged:path,
 	) => {
 		{
 			// if one of asserts fail, then either bridge isn't configured properly (or alternatively - non-standard
 			// configuration is used), or something has broke existing configuration (meaning that all bridged chains
 			// and relays will stop functioning)
-			use $crate::messages::{
-				source::{FromThisChainMessagePayload, TargetHeaderChainAdapter},
-				target::{FromBridgedChainMessagePayload, SourceHeaderChainAdapter},
-				AccountIdOf, BalanceOf, BridgedChain, ThisChain,
-			};
+			use $crate::messages_xcm_extension::XcmAsPlainPayload;
+			use bp_messages::ChainWithMessages;
+			use bp_runtime::Chain;
 			use pallet_bridge_messages::Config as MessagesConfig;
 			use static_assertions::assert_type_eq_all;
 
-			assert_type_eq_all!(<$r as MessagesConfig<$i>>::OutboundPayload, FromThisChainMessagePayload);
+			assert_type_eq_all!(<$r as MessagesConfig<$i>>::ThisChain, $this);
+			assert_type_eq_all!(<$r as MessagesConfig<$i>>::BridgedChain, $bridged);
 
-			assert_type_eq_all!(<$r as MessagesConfig<$i>>::InboundRelayer, AccountIdOf<BridgedChain<$bridge>>);
-
-			assert_type_eq_all!(<$r as MessagesConfig<$i>>::TargetHeaderChain, TargetHeaderChainAdapter<$bridge>);
-			assert_type_eq_all!(<$r as MessagesConfig<$i>>::SourceHeaderChain, SourceHeaderChainAdapter<$bridge>);
+			assert_type_eq_all!(<$r as MessagesConfig<$i>>::OutboundPayload, XcmAsPlainPayload);
+			assert_type_eq_all!(<$r as MessagesConfig<$i>>::InboundPayload, XcmAsPlainPayload);
 		}
 	}
 );
 
 /// Macro that combines four other macro calls - `assert_chain_types`, `assert_bridge_types`,
-/// `assert_bridge_grandpa_pallet_types` and `assert_bridge_messages_pallet_types`. It may be used
+/// and `assert_bridge_messages_pallet_types`. It may be used
 /// at the chain that is implementing complete standard messages bridge (i.e. with bridge GRANDPA
 /// and messages pallets deployed).
 #[macro_export]
@@ -108,20 +88,15 @@ macro_rules! assert_complete_bridge_types(
 		runtime: $r:path,
 		with_bridged_chain_grandpa_instance: $gi:path,
 		with_bridged_chain_messages_instance: $mi:path,
-		bridge: $bridge:path,
 		this_chain: $this:path,
 		bridged_chain: $bridged:path,
 	) => {
 		$crate::assert_chain_types!(runtime: $r, this_chain: $this);
-		$crate::assert_bridge_grandpa_pallet_types!(
-			runtime: $r,
-			with_bridged_chain_grandpa_instance: $gi,
-			bridged_chain: $bridged
-		);
 		$crate::assert_bridge_messages_pallet_types!(
 			runtime: $r,
 			with_bridged_chain_messages_instance: $mi,
-			bridge: $bridge
+			this_chain: $this,
+			bridged_chain: $bridged,
 		);
 	}
 );
@@ -184,20 +159,8 @@ where
 	);
 }
 
-/// Parameters for asserting messages pallet constants.
-#[derive(Debug)]
-pub struct AssertBridgeMessagesPalletConstants {
-	/// Maximal number of unrewarded relayer entries in a confirmation transaction at the bridged
-	/// chain.
-	pub max_unrewarded_relayers_in_bridged_confirmation_tx: MessageNonce,
-	/// Maximal number of unconfirmed messages in a confirmation transaction at the bridged chain.
-	pub max_unconfirmed_messages_in_bridged_confirmation_tx: MessageNonce,
-	/// Identifier of the bridged chain.
-	pub bridged_chain_id: ChainId,
-}
-
 /// Test that the constants, used in messages pallet configuration are valid.
-pub fn assert_bridge_messages_pallet_constants<R, MI>(params: AssertBridgeMessagesPalletConstants)
+pub fn assert_bridge_messages_pallet_constants<R, MI>()
 where
 	R: pallet_bridge_messages::Config<MI>,
 	MI: 'static,
@@ -207,27 +170,22 @@ where
 		"ActiveOutboundLanes ({:?}) must not be empty",
 		R::ActiveOutboundLanes::get(),
 	);
+
 	assert!(
-		R::MaxUnrewardedRelayerEntriesAtInboundLane::get() <= params.max_unrewarded_relayers_in_bridged_confirmation_tx,
-		"MaxUnrewardedRelayerEntriesAtInboundLane ({}) must be <= than the hardcoded value for bridged chain: {}",
-		R::MaxUnrewardedRelayerEntriesAtInboundLane::get(),
-		params.max_unrewarded_relayers_in_bridged_confirmation_tx,
-	);
-	assert!(
-		R::MaxUnconfirmedMessagesAtInboundLane::get() <= params.max_unconfirmed_messages_in_bridged_confirmation_tx,
-		"MaxUnrewardedRelayerEntriesAtInboundLane ({}) must be <= than the hardcoded value for bridged chain: {}",
-		R::MaxUnconfirmedMessagesAtInboundLane::get(),
-		params.max_unconfirmed_messages_in_bridged_confirmation_tx,
+		pallet_bridge_messages::BridgedChainOf::<R, MI>::MAX_UNREWARDED_RELAYERS_IN_CONFIRMATION_TX
+			<= pallet_bridge_messages::BridgedChainOf::<R, MI>::MAX_UNCONFIRMED_MESSAGES_IN_CONFIRMATION_TX,
+		"MAX_UNREWARDED_RELAYERS_IN_CONFIRMATION_TX ({}) of {:?} is larger than \
+			its MAX_UNCONFIRMED_MESSAGES_IN_CONFIRMATION_TX ({}). This makes \
+			no sense",
+		pallet_bridge_messages::BridgedChainOf::<R, MI>::MAX_UNREWARDED_RELAYERS_IN_CONFIRMATION_TX,
+		pallet_bridge_messages::BridgedChainOf::<R, MI>::ID,
+		pallet_bridge_messages::BridgedChainOf::<R, MI>::MAX_UNCONFIRMED_MESSAGES_IN_CONFIRMATION_TX,
 	);
-	assert_eq!(R::BridgedChainId::get(), params.bridged_chain_id);
 }
 
 /// Parameters for asserting bridge pallet names.
 #[derive(Debug)]
 pub struct AssertBridgePalletNames<'a> {
-	/// Name of the messages pallet, deployed at the bridged chain and used to bridge with this
-	/// chain.
-	pub with_this_chain_messages_pallet_name: &'a str,
 	/// Name of the GRANDPA pallet, deployed at this chain and used to bridge with the bridged
 	/// chain.
 	pub with_bridged_chain_grandpa_pallet_name: &'a str,
@@ -238,18 +196,22 @@ pub struct AssertBridgePalletNames<'a> {
 
 /// Tests that bridge pallet names used in `construct_runtime!()` macro call are matching constants
 /// from chain primitives crates.
-pub fn assert_bridge_pallet_names<B, R, GI, MI>(params: AssertBridgePalletNames)
+fn assert_bridge_pallet_names<R, GI, MI>(params: AssertBridgePalletNames)
 where
-	B: MessageBridge,
 	R: pallet_bridge_grandpa::Config<GI> + pallet_bridge_messages::Config<MI>,
 	GI: 'static,
 	MI: 'static,
 {
-	assert_eq!(B::BRIDGED_MESSAGES_PALLET_NAME, params.with_this_chain_messages_pallet_name);
+	// check that the bridge GRANDPA pallet has required name
 	assert_eq!(
 		pallet_bridge_grandpa::PalletOwner::<R, GI>::storage_value_final_key().to_vec(),
-		bp_runtime::storage_value_key(params.with_bridged_chain_grandpa_pallet_name, "PalletOwner",).0,
+		bp_runtime::storage_value_key(
+			params.with_bridged_chain_grandpa_pallet_name,
+			"PalletOwner",
+		).0,
 	);
+
+	// check that the bridge messages pallet has required name
 	assert_eq!(
 		pallet_bridge_messages::PalletOwner::<R, MI>::storage_value_final_key().to_vec(),
 		bp_runtime::storage_value_key(
@@ -262,35 +224,58 @@ where
 
 /// Parameters for asserting complete standard messages bridge.
 #[derive(Debug)]
-pub struct AssertCompleteBridgeConstants<'a> {
+pub struct AssertCompleteBridgeConstants {
 	/// Parameters to assert this chain constants.
 	pub this_chain_constants: AssertChainConstants,
-	/// Parameters to assert messages pallet constants.
-	pub messages_pallet_constants: AssertBridgeMessagesPalletConstants,
-	/// Parameters to assert pallet names constants.
-	pub pallet_names: AssertBridgePalletNames<'a>,
 }
 
-/// All bridge-related constants tests for the complete standard messages bridge (i.e. with bridge
-/// GRANDPA and messages pallets deployed).
-pub fn assert_complete_bridge_constants<R, GI, MI, B>(params: AssertCompleteBridgeConstants)
-where
+/// All bridge-related constants tests for the complete standard relay-chain messages bridge
+/// (i.e. with bridge GRANDPA and messages pallets deployed).
+pub fn assert_complete_with_relay_chain_bridge_constants<R, GI, MI>(
+	params: AssertCompleteBridgeConstants,
+) where
+	R: frame_system::Config
+		+ pallet_bridge_grandpa::Config<GI>
+		+ pallet_bridge_messages::Config<MI>,
+	GI: 'static,
+	MI: 'static,
+{
+	assert_chain_constants::<R>(params.this_chain_constants);
+	assert_bridge_grandpa_pallet_constants::<R, GI>();
+	assert_bridge_messages_pallet_constants::<R, MI>();
+	assert_bridge_pallet_names::<R, GI, MI>(AssertBridgePalletNames {
+		with_bridged_chain_grandpa_pallet_name:
+			<R as pallet_bridge_grandpa::Config<GI>>::BridgedChain::WITH_CHAIN_GRANDPA_PALLET_NAME,
+		with_bridged_chain_messages_pallet_name:
+			<R as pallet_bridge_messages::Config<MI>>::BridgedChain::WITH_CHAIN_MESSAGES_PALLET_NAME,
+	});
+}
+
+/// All bridge-related constants tests for the complete standard parachain messages bridge
+/// (i.e. with bridge GRANDPA, parachains and messages pallets deployed).
+pub fn assert_complete_with_parachain_bridge_constants<R, GI, MI, RelayChain>(
+	params: AssertCompleteBridgeConstants,
+) where
 	R: frame_system::Config
 		+ pallet_bridge_grandpa::Config<GI>
 		+ pallet_bridge_messages::Config<MI>,
 	GI: 'static,
 	MI: 'static,
-	B: MessageBridge,
+	RelayChain: ChainWithGrandpa,
 {
 	assert_chain_constants::<R>(params.this_chain_constants);
 	assert_bridge_grandpa_pallet_constants::<R, GI>();
-	assert_bridge_messages_pallet_constants::<R, MI>(params.messages_pallet_constants);
-	assert_bridge_pallet_names::<B, R, GI, MI>(params.pallet_names);
+	assert_bridge_messages_pallet_constants::<R, MI>();
+	assert_bridge_pallet_names::<R, GI, MI>(AssertBridgePalletNames {
+		with_bridged_chain_grandpa_pallet_name: RelayChain::WITH_CHAIN_GRANDPA_PALLET_NAME,
+		with_bridged_chain_messages_pallet_name:
+			<R as pallet_bridge_messages::Config<MI>>::BridgedChain::WITH_CHAIN_MESSAGES_PALLET_NAME,
+	});
 }
 
 /// Check that the message lane weights are correct.
 pub fn check_message_lane_weights<
-	C: Chain,
+	C: ChainWithMessages,
 	T: frame_system::Config + pallet_bridge_messages::Config<MessagesPalletInstance>,
 	MessagesPalletInstance: 'static,
 >(
@@ -308,14 +293,20 @@ pub fn check_message_lane_weights<
 	// check basic weight assumptions
 	pallet_bridge_messages::ensure_weights_are_correct::<Weights<T, MessagesPalletInstance>>();
 
+	// check that the maximal message dispatch weight is below hardcoded limit
+	pallet_bridge_messages::ensure_maximal_message_dispatch::<Weights<T, MessagesPalletInstance>>(
+		C::maximal_incoming_message_size(),
+		C::maximal_incoming_message_dispatch_weight(),
+	);
+
 	// check that weights allow us to receive messages
-	let max_incoming_message_proof_size = bridged_chain_extra_storage_proof_size
-		.saturating_add(messages::target::maximal_incoming_message_size(C::max_extrinsic_size()));
+	let max_incoming_message_proof_size =
+		bridged_chain_extra_storage_proof_size.saturating_add(C::maximal_incoming_message_size());
 	pallet_bridge_messages::ensure_able_to_receive_message::<Weights<T, MessagesPalletInstance>>(
 		C::max_extrinsic_size(),
 		C::max_extrinsic_weight(),
 		max_incoming_message_proof_size,
-		messages::target::maximal_incoming_message_dispatch_weight(C::max_extrinsic_weight()),
+		C::maximal_incoming_message_dispatch_weight(),
 	);
 
 	// check that weights allow us to receive delivery confirmations
diff --git a/bridges/bin/runtime-common/src/lib.rs b/bridges/bin/runtime-common/src/lib.rs
index 5679acd6006..b65bb6041d5 100644
--- a/bridges/bin/runtime-common/src/lib.rs
+++ b/bridges/bin/runtime-common/src/lib.rs
@@ -20,11 +20,10 @@
 #![cfg_attr(not(feature = "std"), no_std)]
 
 pub mod extensions;
-pub mod messages;
+
 pub mod messages_api;
 pub mod messages_benchmarking;
 pub mod messages_call_ext;
-pub mod messages_generation;
 pub mod messages_xcm_extension;
 pub mod parachains_benchmarking;
 
diff --git a/bridges/bin/runtime-common/src/messages.rs b/bridges/bin/runtime-common/src/messages.rs
deleted file mode 100644
index 03801d5279d..00000000000
--- a/bridges/bin/runtime-common/src/messages.rs
+++ /dev/null
@@ -1,704 +0,0 @@
-// 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/>.
-
-//! Types that allow runtime to act as a source/target endpoint of message lanes.
-//!
-//! Messages are assumed to be encoded `Call`s of the target chain. Call-dispatch
-//! pallet is used to dispatch incoming messages. Message identified by a tuple
-//! of to elements - message lane id and message nonce.
-
-pub use bp_runtime::{RangeInclusiveExt, UnderlyingChainOf, UnderlyingChainProvider};
-
-use bp_header_chain::HeaderChain;
-use bp_messages::{
-	source_chain::TargetHeaderChain,
-	target_chain::{ProvedLaneMessages, ProvedMessages, SourceHeaderChain},
-	InboundLaneData, LaneId, Message, MessageKey, MessageNonce, MessagePayload, OutboundLaneData,
-	VerificationError,
-};
-use bp_runtime::{Chain, RawStorageProof, Size, StorageProofChecker};
-use codec::{Decode, Encode};
-use frame_support::{traits::Get, weights::Weight};
-use hash_db::Hasher;
-use scale_info::TypeInfo;
-use sp_runtime::RuntimeDebug;
-use sp_std::{marker::PhantomData, vec::Vec};
-
-/// Bidirectional message bridge.
-pub trait MessageBridge {
-	/// Name of the paired messages pallet instance at the Bridged chain.
-	///
-	/// Should be the name that is used in the `construct_runtime!()` macro.
-	const BRIDGED_MESSAGES_PALLET_NAME: &'static str;
-
-	/// This chain in context of message bridge.
-	type ThisChain: ThisChainWithMessages;
-	/// Bridged chain in context of message bridge.
-	type BridgedChain: BridgedChainWithMessages;
-	/// Bridged header chain.
-	type BridgedHeaderChain: HeaderChain<UnderlyingChainOf<Self::BridgedChain>>;
-}
-
-/// This chain that has `pallet-bridge-messages` module.
-pub trait ThisChainWithMessages: UnderlyingChainProvider {
-	/// Call origin on the chain.
-	type RuntimeOrigin;
-}
-
-/// Bridged chain that has `pallet-bridge-messages` module.
-pub trait BridgedChainWithMessages: UnderlyingChainProvider {}
-
-/// This chain in context of message bridge.
-pub type ThisChain<B> = <B as MessageBridge>::ThisChain;
-/// Bridged chain in context of message bridge.
-pub type BridgedChain<B> = <B as MessageBridge>::BridgedChain;
-/// Hash used on the chain.
-pub type HashOf<C> = bp_runtime::HashOf<<C as UnderlyingChainProvider>::Chain>;
-/// Hasher used on the chain.
-pub type HasherOf<C> = bp_runtime::HasherOf<UnderlyingChainOf<C>>;
-/// Account id used on the chain.
-pub type AccountIdOf<C> = bp_runtime::AccountIdOf<UnderlyingChainOf<C>>;
-/// Type of balances that is used on the chain.
-pub type BalanceOf<C> = bp_runtime::BalanceOf<UnderlyingChainOf<C>>;
-
-/// Sub-module that is declaring types required for processing This -> Bridged chain messages.
-pub mod source {
-	use super::*;
-
-	/// Message payload for This -> Bridged chain messages.
-	pub type FromThisChainMessagePayload = crate::messages_xcm_extension::XcmAsPlainPayload;
-
-	/// Maximal size of outbound message payload.
-	pub struct FromThisChainMaximalOutboundPayloadSize<B>(PhantomData<B>);
-
-	impl<B: MessageBridge> Get<u32> for FromThisChainMaximalOutboundPayloadSize<B> {
-		fn get() -> u32 {
-			maximal_message_size::<B>()
-		}
-	}
-
-	/// Messages delivery proof from bridged chain:
-	///
-	/// - hash of finalized header;
-	/// - storage proof of inbound lane state;
-	/// - lane id.
-	#[derive(Clone, Decode, Encode, Eq, PartialEq, RuntimeDebug, TypeInfo)]
-	pub struct FromBridgedChainMessagesDeliveryProof<BridgedHeaderHash> {
-		/// Hash of the bridge header the proof is for.
-		pub bridged_header_hash: BridgedHeaderHash,
-		/// Storage trie proof generated for [`Self::bridged_header_hash`].
-		pub storage_proof: RawStorageProof,
-		/// Lane id of which messages were delivered and the proof is for.
-		pub lane: LaneId,
-	}
-
-	impl<BridgedHeaderHash> Size for FromBridgedChainMessagesDeliveryProof<BridgedHeaderHash> {
-		fn size(&self) -> u32 {
-			u32::try_from(
-				self.storage_proof
-					.iter()
-					.fold(0usize, |sum, node| sum.saturating_add(node.len())),
-			)
-			.unwrap_or(u32::MAX)
-		}
-	}
-
-	/// 'Parsed' message delivery proof - inbound lane id and its state.
-	pub type ParsedMessagesDeliveryProofFromBridgedChain<B> =
-		(LaneId, InboundLaneData<AccountIdOf<ThisChain<B>>>);
-
-	/// Return maximal message size of This -> Bridged chain message.
-	pub fn maximal_message_size<B: MessageBridge>() -> u32 {
-		super::target::maximal_incoming_message_size(
-			UnderlyingChainOf::<BridgedChain<B>>::max_extrinsic_size(),
-		)
-	}
-
-	/// `TargetHeaderChain` implementation that is using default types and perform default checks.
-	pub struct TargetHeaderChainAdapter<B>(PhantomData<B>);
-
-	impl<B: MessageBridge> TargetHeaderChain<FromThisChainMessagePayload, AccountIdOf<ThisChain<B>>>
-		for TargetHeaderChainAdapter<B>
-	{
-		type MessagesDeliveryProof = FromBridgedChainMessagesDeliveryProof<HashOf<BridgedChain<B>>>;
-
-		fn verify_message(payload: &FromThisChainMessagePayload) -> Result<(), VerificationError> {
-			verify_chain_message::<B>(payload)
-		}
-
-		fn verify_messages_delivery_proof(
-			proof: Self::MessagesDeliveryProof,
-		) -> Result<(LaneId, InboundLaneData<AccountIdOf<ThisChain<B>>>), VerificationError> {
-			verify_messages_delivery_proof::<B>(proof)
-		}
-	}
-
-	/// Do basic Bridged-chain specific verification of This -> Bridged chain message.
-	///
-	/// Ok result from this function means that the delivery transaction with this message
-	/// may be 'mined' by the target chain.
-	pub fn verify_chain_message<B: MessageBridge>(
-		payload: &FromThisChainMessagePayload,
-	) -> Result<(), VerificationError> {
-		// IMPORTANT: any error that is returned here is fatal for the bridge, because
-		// this code is executed at the bridge hub and message sender actually lives
-		// at some sibling parachain. So we are failing **after** the message has been
-		// sent and we can't report it back to sender (unless error report mechanism is
-		// embedded into message and its dispatcher).
-
-		// apart from maximal message size check (see below), we should also check the message
-		// dispatch weight here. But we assume that the bridged chain will just push the message
-		// to some queue (XCMP, UMP, DMP), so the weight is constant and fits the block.
-
-		// The maximal size of extrinsic at Substrate-based chain depends on the
-		// `frame_system::Config::MaximumBlockLength` and
-		// `frame_system::Config::AvailableBlockRatio` constants. This check is here to be sure that
-		// the lane won't stuck because message is too large to fit into delivery transaction.
-		//
-		// **IMPORTANT NOTE**: the delivery transaction contains storage proof of the message, not
-		// the message itself. The proof is always larger than the message. But unless chain state
-		// is enormously large, it should be several dozens/hundreds of bytes. The delivery
-		// transaction also contains signatures and signed extensions. Because of this, we reserve
-		// 1/3 of the the maximal extrinsic size for this data.
-		if payload.len() > maximal_message_size::<B>() as usize {
-			return Err(VerificationError::MessageTooLarge)
-		}
-
-		Ok(())
-	}
-
-	/// Verify proof of This -> Bridged chain messages delivery.
-	///
-	/// This function is used when Bridged chain is directly using GRANDPA finality. For Bridged
-	/// parachains, please use the `verify_messages_delivery_proof_from_parachain`.
-	pub fn verify_messages_delivery_proof<B: MessageBridge>(
-		proof: FromBridgedChainMessagesDeliveryProof<HashOf<BridgedChain<B>>>,
-	) -> Result<ParsedMessagesDeliveryProofFromBridgedChain<B>, VerificationError> {
-		let FromBridgedChainMessagesDeliveryProof { bridged_header_hash, storage_proof, lane } =
-			proof;
-		let mut storage =
-			B::BridgedHeaderChain::storage_proof_checker(bridged_header_hash, storage_proof)
-				.map_err(VerificationError::HeaderChain)?;
-		// Messages delivery proof is just proof of single storage key read => any error
-		// is fatal.
-		let storage_inbound_lane_data_key = bp_messages::storage_keys::inbound_lane_data_key(
-			B::BRIDGED_MESSAGES_PALLET_NAME,
-			&lane,
-		);
-		let inbound_lane_data = storage
-			.read_and_decode_mandatory_value(storage_inbound_lane_data_key.0.as_ref())
-			.map_err(VerificationError::InboundLaneStorage)?;
-
-		// check that the storage proof doesn't have any untouched trie nodes
-		storage.ensure_no_unused_nodes().map_err(VerificationError::StorageProof)?;
-
-		Ok((lane, inbound_lane_data))
-	}
-}
-
-/// Sub-module that is declaring types required for processing Bridged -> This chain messages.
-pub mod target {
-	use super::*;
-
-	/// Decoded Bridged -> This message payload.
-	pub type FromBridgedChainMessagePayload = crate::messages_xcm_extension::XcmAsPlainPayload;
-
-	/// Messages proof from bridged chain:
-	///
-	/// - hash of finalized header;
-	/// - storage proof of messages and (optionally) outbound lane state;
-	/// - lane id;
-	/// - nonces (inclusive range) of messages which are included in this proof.
-	#[derive(Clone, Decode, Encode, Eq, PartialEq, RuntimeDebug, TypeInfo)]
-	pub struct FromBridgedChainMessagesProof<BridgedHeaderHash> {
-		/// 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,
-		/// 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> {
-		fn size(&self) -> u32 {
-			u32::try_from(
-				self.storage_proof
-					.iter()
-					.fold(0usize, |sum, node| sum.saturating_add(node.len())),
-			)
-			.unwrap_or(u32::MAX)
-		}
-	}
-
-	/// Return maximal dispatch weight of the message we're able to receive.
-	pub fn maximal_incoming_message_dispatch_weight(maximal_extrinsic_weight: Weight) -> Weight {
-		maximal_extrinsic_weight / 2
-	}
-
-	/// Return maximal message size given maximal extrinsic size.
-	pub fn maximal_incoming_message_size(maximal_extrinsic_size: u32) -> u32 {
-		maximal_extrinsic_size / 3 * 2
-	}
-
-	/// `SourceHeaderChain` implementation that is using default types and perform default checks.
-	pub struct SourceHeaderChainAdapter<B>(PhantomData<B>);
-
-	impl<B: MessageBridge> SourceHeaderChain for SourceHeaderChainAdapter<B> {
-		type MessagesProof = FromBridgedChainMessagesProof<HashOf<BridgedChain<B>>>;
-
-		fn verify_messages_proof(
-			proof: Self::MessagesProof,
-			messages_count: u32,
-		) -> Result<ProvedMessages<Message>, VerificationError> {
-			verify_messages_proof::<B>(proof, messages_count)
-		}
-	}
-
-	/// Verify proof of Bridged -> This chain messages.
-	///
-	/// This function is used when Bridged chain is directly using GRANDPA finality. For Bridged
-	/// parachains, please use the `verify_messages_proof_from_parachain`.
-	///
-	/// The `messages_count` argument verification (sane limits) is supposed to be made
-	/// outside of this function. This function only verifies that the proof declares exactly
-	/// `messages_count` messages.
-	pub fn verify_messages_proof<B: MessageBridge>(
-		proof: FromBridgedChainMessagesProof<HashOf<BridgedChain<B>>>,
-		messages_count: u32,
-	) -> Result<ProvedMessages<Message>, VerificationError> {
-		let FromBridgedChainMessagesProof {
-			bridged_header_hash,
-			storage_proof,
-			lane,
-			nonces_start,
-			nonces_end,
-		} = proof;
-		let storage =
-			B::BridgedHeaderChain::storage_proof_checker(bridged_header_hash, storage_proof)
-				.map_err(VerificationError::HeaderChain)?;
-		let mut parser = StorageProofCheckerAdapter::<_, B> { storage, _dummy: Default::default() };
-		let nonces_range = nonces_start..=nonces_end;
-
-		// receiving proofs where end < begin is ok (if proof includes outbound lane state)
-		let messages_in_the_proof = nonces_range.checked_len().unwrap_or(0);
-		if messages_in_the_proof != MessageNonce::from(messages_count) {
-			return Err(VerificationError::MessagesCountMismatch)
-		}
-
-		// Read messages first. All messages that are claimed to be in the proof must
-		// be in the proof. So any error in `read_value`, or even missing value is fatal.
-		//
-		// Mind that we allow proofs with no messages if outbound lane state is proved.
-		let mut messages = Vec::with_capacity(messages_in_the_proof as _);
-		for nonce in nonces_range {
-			let message_key = MessageKey { lane_id: lane, nonce };
-			let message_payload = parser.read_and_decode_message_payload(&message_key)?;
-			messages.push(Message { key: message_key, payload: message_payload });
-		}
-
-		// Now let's check if proof contains outbound lane state proof. It is optional, so
-		// we simply ignore `read_value` errors and missing value.
-		let proved_lane_messages = ProvedLaneMessages {
-			lane_state: parser.read_and_decode_outbound_lane_data(&lane)?,
-			messages,
-		};
-
-		// Now we may actually check if the proof is empty or not.
-		if proved_lane_messages.lane_state.is_none() && proved_lane_messages.messages.is_empty() {
-			return Err(VerificationError::EmptyMessageProof)
-		}
-
-		// check that the storage proof doesn't have any untouched trie nodes
-		parser
-			.storage
-			.ensure_no_unused_nodes()
-			.map_err(VerificationError::StorageProof)?;
-
-		// We only support single lane messages in this generated_schema
-		let mut proved_messages = ProvedMessages::new();
-		proved_messages.insert(lane, proved_lane_messages);
-
-		Ok(proved_messages)
-	}
-
-	struct StorageProofCheckerAdapter<H: Hasher, B> {
-		storage: StorageProofChecker<H>,
-		_dummy: sp_std::marker::PhantomData<B>,
-	}
-
-	impl<H: Hasher, B: MessageBridge> StorageProofCheckerAdapter<H, B> {
-		fn read_and_decode_outbound_lane_data(
-			&mut self,
-			lane_id: &LaneId,
-		) -> Result<Option<OutboundLaneData>, VerificationError> {
-			let storage_outbound_lane_data_key = bp_messages::storage_keys::outbound_lane_data_key(
-				B::BRIDGED_MESSAGES_PALLET_NAME,
-				lane_id,
-			);
-
-			self.storage
-				.read_and_decode_opt_value(storage_outbound_lane_data_key.0.as_ref())
-				.map_err(VerificationError::OutboundLaneStorage)
-		}
-
-		fn read_and_decode_message_payload(
-			&mut self,
-			message_key: &MessageKey,
-		) -> Result<MessagePayload, VerificationError> {
-			let storage_message_key = bp_messages::storage_keys::message_key(
-				B::BRIDGED_MESSAGES_PALLET_NAME,
-				&message_key.lane_id,
-				message_key.nonce,
-			);
-			self.storage
-				.read_and_decode_mandatory_value(storage_message_key.0.as_ref())
-				.map_err(VerificationError::MessageStorage)
-		}
-	}
-}
-
-/// The `BridgeMessagesCall` used by a chain.
-pub type BridgeMessagesCallOf<C> = bp_messages::BridgeMessagesCall<
-	bp_runtime::AccountIdOf<C>,
-	target::FromBridgedChainMessagesProof<bp_runtime::HashOf<C>>,
-	source::FromBridgedChainMessagesDeliveryProof<bp_runtime::HashOf<C>>,
->;
-
-#[cfg(test)]
-mod tests {
-	use super::*;
-	use crate::{
-		messages_generation::{
-			encode_all_messages, encode_lane_data, prepare_messages_storage_proof,
-		},
-		mock::*,
-	};
-	use bp_header_chain::{HeaderChainError, StoredHeaderDataBuilder};
-	use bp_runtime::{HeaderId, StorageProofError};
-	use codec::Encode;
-	use sp_core::H256;
-	use sp_runtime::traits::Header as _;
-	use sp_trie::accessed_nodes_tracker::Error as AccessedNodesTrackerError;
-
-	#[test]
-	fn verify_chain_message_rejects_message_with_too_large_declared_weight() {
-		assert!(source::verify_chain_message::<OnThisChainBridge>(&vec![
-			42;
-			BRIDGED_CHAIN_MAX_EXTRINSIC_WEIGHT -
-				1
-		])
-		.is_err());
-	}
-
-	#[test]
-	fn verify_chain_message_rejects_message_too_large_message() {
-		assert!(source::verify_chain_message::<OnThisChainBridge>(&vec![
-			0;
-			source::maximal_message_size::<OnThisChainBridge>()
-				as usize + 1
-		],)
-		.is_err());
-	}
-
-	#[test]
-	fn verify_chain_message_accepts_maximal_message() {
-		assert_eq!(
-			source::verify_chain_message::<OnThisChainBridge>(&vec![
-				0;
-				source::maximal_message_size::<OnThisChainBridge>()
-					as _
-			],),
-			Ok(()),
-		);
-	}
-
-	fn using_messages_proof<R>(
-		nonces_end: MessageNonce,
-		outbound_lane_data: Option<OutboundLaneData>,
-		encode_message: impl Fn(MessageNonce, &MessagePayload) -> Option<Vec<u8>>,
-		encode_outbound_lane_data: impl Fn(&OutboundLaneData) -> Vec<u8>,
-		test: impl Fn(target::FromBridgedChainMessagesProof<H256>) -> R,
-	) -> R {
-		let (state_root, storage_proof) = prepare_messages_storage_proof::<OnThisChainBridge>(
-			TEST_LANE_ID,
-			1..=nonces_end,
-			outbound_lane_data,
-			bp_runtime::StorageProofSize::Minimal(0),
-			vec![42],
-			encode_message,
-			encode_outbound_lane_data,
-		);
-
-		sp_io::TestExternalities::new(Default::default()).execute_with(move || {
-			let bridged_header = BridgedChainHeader::new(
-				0,
-				Default::default(),
-				state_root,
-				Default::default(),
-				Default::default(),
-			);
-			let bridged_header_hash = bridged_header.hash();
-
-			pallet_bridge_grandpa::BestFinalized::<TestRuntime>::put(HeaderId(
-				0,
-				bridged_header_hash,
-			));
-			pallet_bridge_grandpa::ImportedHeaders::<TestRuntime>::insert(
-				bridged_header_hash,
-				bridged_header.build(),
-			);
-			test(target::FromBridgedChainMessagesProof {
-				bridged_header_hash,
-				storage_proof,
-				lane: TEST_LANE_ID,
-				nonces_start: 1,
-				nonces_end,
-			})
-		})
-	}
-
-	#[test]
-	fn messages_proof_is_rejected_if_declared_less_than_actual_number_of_messages() {
-		assert_eq!(
-			using_messages_proof(10, None, encode_all_messages, encode_lane_data, |proof| {
-				target::verify_messages_proof::<OnThisChainBridge>(proof, 5)
-			}),
-			Err(VerificationError::MessagesCountMismatch),
-		);
-	}
-
-	#[test]
-	fn messages_proof_is_rejected_if_declared_more_than_actual_number_of_messages() {
-		assert_eq!(
-			using_messages_proof(10, None, encode_all_messages, encode_lane_data, |proof| {
-				target::verify_messages_proof::<OnThisChainBridge>(proof, 15)
-			}),
-			Err(VerificationError::MessagesCountMismatch),
-		);
-	}
-
-	#[test]
-	fn message_proof_is_rejected_if_header_is_missing_from_the_chain() {
-		assert_eq!(
-			using_messages_proof(10, None, encode_all_messages, encode_lane_data, |proof| {
-				let bridged_header_hash =
-					pallet_bridge_grandpa::BestFinalized::<TestRuntime>::get().unwrap().1;
-				pallet_bridge_grandpa::ImportedHeaders::<TestRuntime>::remove(bridged_header_hash);
-				target::verify_messages_proof::<OnThisChainBridge>(proof, 10)
-			}),
-			Err(VerificationError::HeaderChain(HeaderChainError::UnknownHeader)),
-		);
-	}
-
-	#[test]
-	fn message_proof_is_rejected_if_header_state_root_mismatches() {
-		assert_eq!(
-			using_messages_proof(10, None, encode_all_messages, encode_lane_data, |proof| {
-				let bridged_header_hash =
-					pallet_bridge_grandpa::BestFinalized::<TestRuntime>::get().unwrap().1;
-				pallet_bridge_grandpa::ImportedHeaders::<TestRuntime>::insert(
-					bridged_header_hash,
-					BridgedChainHeader::new(
-						0,
-						Default::default(),
-						Default::default(),
-						Default::default(),
-						Default::default(),
-					)
-					.build(),
-				);
-				target::verify_messages_proof::<OnThisChainBridge>(proof, 10)
-			}),
-			Err(VerificationError::HeaderChain(HeaderChainError::StorageProof(
-				StorageProofError::StorageRootMismatch
-			))),
-		);
-	}
-
-	#[test]
-	fn message_proof_is_rejected_if_it_has_duplicate_trie_nodes() {
-		assert_eq!(
-			using_messages_proof(10, None, encode_all_messages, encode_lane_data, |mut proof| {
-				let node = proof.storage_proof.pop().unwrap();
-				proof.storage_proof.push(node.clone());
-				proof.storage_proof.push(node);
-				target::verify_messages_proof::<OnThisChainBridge>(proof, 10)
-			},),
-			Err(VerificationError::HeaderChain(HeaderChainError::StorageProof(
-				StorageProofError::StorageProof(sp_trie::StorageProofError::DuplicateNodes.into())
-			))),
-		);
-	}
-
-	#[test]
-	fn message_proof_is_rejected_if_it_has_unused_trie_nodes() {
-		assert_eq!(
-			using_messages_proof(10, None, encode_all_messages, encode_lane_data, |mut proof| {
-				proof.storage_proof.push(vec![42]);
-				target::verify_messages_proof::<OnThisChainBridge>(proof, 10)
-			},),
-			Err(VerificationError::StorageProof(StorageProofError::AccessedNodesTracker(
-				AccessedNodesTrackerError::UnusedNodes.into()
-			))),
-		);
-	}
-
-	#[test]
-	fn message_proof_is_rejected_if_required_message_is_missing() {
-		matches!(
-			using_messages_proof(
-				10,
-				None,
-				|n, m| if n != 5 { Some(m.encode()) } else { None },
-				encode_lane_data,
-				|proof| target::verify_messages_proof::<OnThisChainBridge>(proof, 10)
-			),
-			Err(VerificationError::MessageStorage(StorageProofError::StorageValueEmpty)),
-		);
-	}
-
-	#[test]
-	fn message_proof_is_rejected_if_message_decode_fails() {
-		matches!(
-			using_messages_proof(
-				10,
-				None,
-				|n, m| {
-					let mut m = m.encode();
-					if n == 5 {
-						m = vec![42]
-					}
-					Some(m)
-				},
-				encode_lane_data,
-				|proof| target::verify_messages_proof::<OnThisChainBridge>(proof, 10),
-			),
-			Err(VerificationError::MessageStorage(StorageProofError::StorageValueDecodeFailed(_))),
-		);
-	}
-
-	#[test]
-	fn message_proof_is_rejected_if_outbound_lane_state_decode_fails() {
-		matches!(
-			using_messages_proof(
-				10,
-				Some(OutboundLaneData {
-					oldest_unpruned_nonce: 1,
-					latest_received_nonce: 1,
-					latest_generated_nonce: 1,
-				}),
-				encode_all_messages,
-				|d| {
-					let mut d = d.encode();
-					d.truncate(1);
-					d
-				},
-				|proof| target::verify_messages_proof::<OnThisChainBridge>(proof, 10),
-			),
-			Err(VerificationError::OutboundLaneStorage(
-				StorageProofError::StorageValueDecodeFailed(_)
-			)),
-		);
-	}
-
-	#[test]
-	fn message_proof_is_rejected_if_it_is_empty() {
-		assert_eq!(
-			using_messages_proof(0, None, encode_all_messages, encode_lane_data, |proof| {
-				target::verify_messages_proof::<OnThisChainBridge>(proof, 0)
-			},),
-			Err(VerificationError::EmptyMessageProof),
-		);
-	}
-
-	#[test]
-	fn non_empty_message_proof_without_messages_is_accepted() {
-		assert_eq!(
-			using_messages_proof(
-				0,
-				Some(OutboundLaneData {
-					oldest_unpruned_nonce: 1,
-					latest_received_nonce: 1,
-					latest_generated_nonce: 1,
-				}),
-				encode_all_messages,
-				encode_lane_data,
-				|proof| target::verify_messages_proof::<OnThisChainBridge>(proof, 0),
-			),
-			Ok(vec![(
-				TEST_LANE_ID,
-				ProvedLaneMessages {
-					lane_state: Some(OutboundLaneData {
-						oldest_unpruned_nonce: 1,
-						latest_received_nonce: 1,
-						latest_generated_nonce: 1,
-					}),
-					messages: Vec::new(),
-				},
-			)]
-			.into_iter()
-			.collect()),
-		);
-	}
-
-	#[test]
-	fn non_empty_message_proof_is_accepted() {
-		assert_eq!(
-			using_messages_proof(
-				1,
-				Some(OutboundLaneData {
-					oldest_unpruned_nonce: 1,
-					latest_received_nonce: 1,
-					latest_generated_nonce: 1,
-				}),
-				encode_all_messages,
-				encode_lane_data,
-				|proof| target::verify_messages_proof::<OnThisChainBridge>(proof, 1),
-			),
-			Ok(vec![(
-				TEST_LANE_ID,
-				ProvedLaneMessages {
-					lane_state: Some(OutboundLaneData {
-						oldest_unpruned_nonce: 1,
-						latest_received_nonce: 1,
-						latest_generated_nonce: 1,
-					}),
-					messages: vec![Message {
-						key: MessageKey { lane_id: TEST_LANE_ID, nonce: 1 },
-						payload: vec![42],
-					}],
-				},
-			)]
-			.into_iter()
-			.collect()),
-		);
-	}
-
-	#[test]
-	fn verify_messages_proof_does_not_panic_if_messages_count_mismatches() {
-		assert_eq!(
-			using_messages_proof(1, None, encode_all_messages, encode_lane_data, |mut proof| {
-				proof.nonces_end = u64::MAX;
-				target::verify_messages_proof::<OnThisChainBridge>(proof, u32::MAX)
-			},),
-			Err(VerificationError::MessagesCountMismatch),
-		);
-	}
-}
diff --git a/bridges/bin/runtime-common/src/messages_benchmarking.rs b/bridges/bin/runtime-common/src/messages_benchmarking.rs
index 74494f79080..1880e65547f 100644
--- a/bridges/bin/runtime-common/src/messages_benchmarking.rs
+++ b/bridges/bin/runtime-common/src/messages_benchmarking.rs
@@ -19,23 +19,22 @@
 
 #![cfg(feature = "runtime-benchmarks")]
 
-use crate::{
-	messages::{
-		source::FromBridgedChainMessagesDeliveryProof, target::FromBridgedChainMessagesProof,
-		AccountIdOf, BridgedChain, HashOf, MessageBridge, ThisChain,
-	},
+use bp_messages::{
+	source_chain::FromBridgedChainMessagesDeliveryProof,
+	target_chain::FromBridgedChainMessagesProof, MessagePayload,
+};
+use bp_polkadot_core::parachains::ParaHash;
+use bp_runtime::{AccountIdOf, Chain, HashOf, Parachain};
+use codec::Encode;
+use frame_support::weights::Weight;
+use pallet_bridge_messages::{
+	benchmarking::{MessageDeliveryProofParams, MessageProofParams},
 	messages_generation::{
 		encode_all_messages, encode_lane_data, prepare_message_delivery_storage_proof,
 		prepare_messages_storage_proof,
 	},
+	BridgedChainOf, ThisChainOf,
 };
-
-use bp_messages::MessagePayload;
-use bp_polkadot_core::parachains::ParaHash;
-use bp_runtime::{Chain, Parachain, StorageProofSize, UnderlyingChainOf};
-use codec::Encode;
-use frame_support::weights::Weight;
-use pallet_bridge_messages::benchmarking::{MessageDeliveryProofParams, MessageProofParams};
 use sp_runtime::traits::{Header, Zero};
 use sp_std::prelude::*;
 use xcm::latest::prelude::*;
@@ -45,11 +44,7 @@ fn prepare_inbound_message(
 	params: &MessageProofParams,
 	successful_dispatch_message_generator: impl Fn(usize) -> MessagePayload,
 ) -> MessagePayload {
-	// we only care about **this** message size when message proof needs to be `Minimal`
-	let expected_size = match params.size {
-		StorageProofSize::Minimal(size) => size as usize,
-		_ => 0,
-	};
+	let expected_size = params.proof_params.db_size.unwrap_or(0) as usize;
 
 	// if we don't need a correct message, then we may just return some random blob
 	if !params.is_successful_dispatch_expected {
@@ -75,25 +70,32 @@ fn prepare_inbound_message(
 /// This method is intended to be used when benchmarking pallet, linked to the chain that
 /// uses GRANDPA finality. For parachains, please use the `prepare_message_proof_from_parachain`
 /// function.
-pub fn prepare_message_proof_from_grandpa_chain<R, FI, B>(
+pub fn prepare_message_proof_from_grandpa_chain<R, FI, MI>(
 	params: MessageProofParams,
 	message_generator: impl Fn(usize) -> MessagePayload,
-) -> (FromBridgedChainMessagesProof<HashOf<BridgedChain<B>>>, Weight)
+) -> (FromBridgedChainMessagesProof<HashOf<BridgedChainOf<R, MI>>>, Weight)
 where
-	R: pallet_bridge_grandpa::Config<FI, BridgedChain = UnderlyingChainOf<BridgedChain<B>>>,
+	R: pallet_bridge_grandpa::Config<FI, BridgedChain = BridgedChainOf<R, MI>>
+		+ pallet_bridge_messages::Config<
+			MI,
+			BridgedHeaderChain = pallet_bridge_grandpa::Pallet<R, FI>,
+		>,
 	FI: 'static,
-	B: MessageBridge,
+	MI: 'static,
 {
 	// prepare storage proof
-	let (state_root, storage_proof) = prepare_messages_storage_proof::<B>(
-		params.lane,
-		params.message_nonces.clone(),
-		params.outbound_lane_data.clone(),
-		params.size,
-		prepare_inbound_message(&params, message_generator),
-		encode_all_messages,
-		encode_lane_data,
-	);
+	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,
+		);
 
 	// update runtime storage
 	let (_, bridged_header_hash) = insert_header_to_grandpa_pallet::<R, FI>(state_root);
@@ -118,30 +120,33 @@ where
 /// This method is intended to be used when benchmarking pallet, linked to the chain that
 /// 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, B>(
+pub fn prepare_message_proof_from_parachain<R, PI, MI>(
 	params: MessageProofParams,
 	message_generator: impl Fn(usize) -> MessagePayload,
-) -> (FromBridgedChainMessagesProof<HashOf<BridgedChain<B>>>, Weight)
+) -> (FromBridgedChainMessagesProof<HashOf<BridgedChainOf<R, MI>>>, Weight)
 where
-	R: pallet_bridge_parachains::Config<PI>,
+	R: pallet_bridge_parachains::Config<PI> + pallet_bridge_messages::Config<MI>,
 	PI: 'static,
-	B: MessageBridge,
-	UnderlyingChainOf<BridgedChain<B>>: Chain<Hash = ParaHash> + Parachain,
+	MI: 'static,
+	BridgedChainOf<R, MI>: Chain<Hash = ParaHash> + Parachain,
 {
 	// prepare storage proof
-	let (state_root, storage_proof) = prepare_messages_storage_proof::<B>(
-		params.lane,
-		params.message_nonces.clone(),
-		params.outbound_lane_data.clone(),
-		params.size,
-		prepare_inbound_message(&params, message_generator),
-		encode_all_messages,
-		encode_lane_data,
-	);
+	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,
+		);
 
 	// update runtime storage
 	let (_, bridged_header_hash) =
-		insert_header_to_parachains_pallet::<R, PI, UnderlyingChainOf<BridgedChain<B>>>(state_root);
+		insert_header_to_parachains_pallet::<R, PI, BridgedChainOf<R, MI>>(state_root);
 
 	(
 		FromBridgedChainMessagesProof {
@@ -160,21 +165,24 @@ where
 /// This method is intended to be used when benchmarking pallet, linked to the chain that
 /// 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, B>(
-	params: MessageDeliveryProofParams<AccountIdOf<ThisChain<B>>>,
-) -> FromBridgedChainMessagesDeliveryProof<HashOf<BridgedChain<B>>>
+pub fn prepare_message_delivery_proof_from_grandpa_chain<R, FI, MI>(
+	params: MessageDeliveryProofParams<AccountIdOf<ThisChainOf<R, MI>>>,
+) -> FromBridgedChainMessagesDeliveryProof<HashOf<BridgedChainOf<R, MI>>>
 where
-	R: pallet_bridge_grandpa::Config<FI, BridgedChain = UnderlyingChainOf<BridgedChain<B>>>,
+	R: pallet_bridge_grandpa::Config<FI, BridgedChain = BridgedChainOf<R, MI>>
+		+ pallet_bridge_messages::Config<
+			MI,
+			BridgedHeaderChain = pallet_bridge_grandpa::Pallet<R, FI>,
+		>,
 	FI: 'static,
-	B: MessageBridge,
+	MI: 'static,
 {
 	// prepare storage proof
 	let lane = params.lane;
-	let (state_root, storage_proof) = prepare_message_delivery_storage_proof::<B>(
-		params.lane,
-		params.inbound_lane_data,
-		params.size,
-	);
+	let (state_root, storage_proof) = prepare_message_delivery_storage_proof::<
+		BridgedChainOf<R, MI>,
+		ThisChainOf<R, MI>,
+	>(params.lane, params.inbound_lane_data, params.proof_params);
 
 	// update runtime storage
 	let (_, bridged_header_hash) = insert_header_to_grandpa_pallet::<R, FI>(state_root);
@@ -191,26 +199,25 @@ where
 /// This method is intended to be used when benchmarking pallet, linked to the chain that
 /// 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, B>(
-	params: MessageDeliveryProofParams<AccountIdOf<ThisChain<B>>>,
-) -> FromBridgedChainMessagesDeliveryProof<HashOf<BridgedChain<B>>>
+pub fn prepare_message_delivery_proof_from_parachain<R, PI, MI>(
+	params: MessageDeliveryProofParams<AccountIdOf<ThisChainOf<R, MI>>>,
+) -> FromBridgedChainMessagesDeliveryProof<HashOf<BridgedChainOf<R, MI>>>
 where
-	R: pallet_bridge_parachains::Config<PI>,
+	R: pallet_bridge_parachains::Config<PI> + pallet_bridge_messages::Config<MI>,
 	PI: 'static,
-	B: MessageBridge,
-	UnderlyingChainOf<BridgedChain<B>>: Chain<Hash = ParaHash> + Parachain,
+	MI: 'static,
+	BridgedChainOf<R, MI>: Chain<Hash = ParaHash> + Parachain,
 {
 	// prepare storage proof
 	let lane = params.lane;
-	let (state_root, storage_proof) = prepare_message_delivery_storage_proof::<B>(
-		params.lane,
-		params.inbound_lane_data,
-		params.size,
-	);
+	let (state_root, storage_proof) = prepare_message_delivery_storage_proof::<
+		BridgedChainOf<R, MI>,
+		ThisChainOf<R, MI>,
+	>(params.lane, params.inbound_lane_data, params.proof_params);
 
 	// update runtime storage
 	let (_, bridged_header_hash) =
-		insert_header_to_parachains_pallet::<R, PI, UnderlyingChainOf<BridgedChain<B>>>(state_root);
+		insert_header_to_parachains_pallet::<R, PI, BridgedChainOf<R, MI>>(state_root);
 
 	FromBridgedChainMessagesDeliveryProof {
 		bridged_header_hash: bridged_header_hash.into(),
diff --git a/bridges/bin/runtime-common/src/messages_call_ext.rs b/bridges/bin/runtime-common/src/messages_call_ext.rs
index fb07f7b6dd6..a9ee1969ae0 100644
--- a/bridges/bin/runtime-common/src/messages_call_ext.rs
+++ b/bridges/bin/runtime-common/src/messages_call_ext.rs
@@ -14,19 +14,14 @@
 // 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/>.
 
-//! Signed extension for the `pallet-bridge-messages` that is able to reject obsolete
-//! (and some other invalid) transactions.
+//! Helpers for easier manipulation of call processing with signed extensions.
 
-use crate::messages::{
-	source::FromBridgedChainMessagesDeliveryProof, target::FromBridgedChainMessagesProof,
+use bp_messages::{
+	target_chain::MessageDispatch, ChainWithMessages, InboundLaneData, LaneId, MessageNonce,
 };
-use bp_messages::{target_chain::MessageDispatch, InboundLaneData, LaneId, MessageNonce};
-use bp_runtime::OwnedBridgeModule;
-use frame_support::{
-	dispatch::CallableCallFor,
-	traits::{Get, IsSubType},
-};
-use pallet_bridge_messages::{Config, Pallet};
+use bp_runtime::{AccountIdOf, OwnedBridgeModule};
+use frame_support::{dispatch::CallableCallFor, traits::IsSubType};
+use pallet_bridge_messages::{BridgedChainOf, Config, Pallet};
 use sp_runtime::{transaction_validity::TransactionValidity, RuntimeDebug};
 use sp_std::ops::RangeInclusive;
 
@@ -213,18 +208,8 @@ pub trait MessagesCallSubType<T: Config<I, RuntimeCall = Self>, I: 'static>:
 }
 
 impl<
-		BridgedHeaderHash,
-		SourceHeaderChain: bp_messages::target_chain::SourceHeaderChain<
-			MessagesProof = FromBridgedChainMessagesProof<BridgedHeaderHash>,
-		>,
-		TargetHeaderChain: bp_messages::source_chain::TargetHeaderChain<
-			<T as Config<I>>::OutboundPayload,
-			<T as frame_system::Config>::AccountId,
-			MessagesDeliveryProof = FromBridgedChainMessagesDeliveryProof<BridgedHeaderHash>,
-		>,
 		Call: IsSubType<CallableCallFor<Pallet<T, I>, T>>,
-		T: frame_system::Config<RuntimeCall = Call>
-			+ Config<I, SourceHeaderChain = SourceHeaderChain, TargetHeaderChain = TargetHeaderChain>,
+		T: frame_system::Config<RuntimeCall = Call> + Config<I>,
 		I: 'static,
 	> MessagesCallSubType<T, I> for T::RuntimeCall
 {
@@ -340,16 +325,17 @@ impl<
 
 /// Returns occupation state of unrewarded relayers vector.
 fn unrewarded_relayers_occupation<T: Config<I>, I: 'static>(
-	inbound_lane_data: &InboundLaneData<T::InboundRelayer>,
+	inbound_lane_data: &InboundLaneData<AccountIdOf<BridgedChainOf<T, I>>>,
 ) -> UnrewardedRelayerOccupation {
 	UnrewardedRelayerOccupation {
-		free_relayer_slots: T::MaxUnrewardedRelayerEntriesAtInboundLane::get()
+		free_relayer_slots: T::BridgedChain::MAX_UNREWARDED_RELAYERS_IN_CONFIRMATION_TX
 			.saturating_sub(inbound_lane_data.relayers.len() as MessageNonce),
 		free_message_slots: {
 			let unconfirmed_messages = inbound_lane_data
 				.last_delivered_nonce()
 				.saturating_sub(inbound_lane_data.last_confirmed_nonce);
-			T::MaxUnconfirmedMessagesAtInboundLane::get().saturating_sub(unconfirmed_messages)
+			T::BridgedChain::MAX_UNCONFIRMED_MESSAGES_IN_CONFIRMATION_TX
+				.saturating_sub(unconfirmed_messages)
 		},
 	}
 }
@@ -358,22 +344,20 @@ fn unrewarded_relayers_occupation<T: Config<I>, I: 'static>(
 mod tests {
 	use super::*;
 	use crate::{
-		messages::{
-			source::FromBridgedChainMessagesDeliveryProof, target::FromBridgedChainMessagesProof,
-		},
 		messages_call_ext::MessagesCallSubType,
-		mock::{
-			DummyMessageDispatch, MaxUnconfirmedMessagesAtInboundLane,
-			MaxUnrewardedRelayerEntriesAtInboundLane, TestRuntime, ThisChainRuntimeCall,
-		},
+		mock::{BridgedUnderlyingChain, DummyMessageDispatch, TestRuntime, ThisChainRuntimeCall},
+	};
+	use bp_messages::{
+		source_chain::FromBridgedChainMessagesDeliveryProof,
+		target_chain::FromBridgedChainMessagesProof, DeliveredMessages, UnrewardedRelayer,
+		UnrewardedRelayersState,
 	};
-	use bp_messages::{DeliveredMessages, UnrewardedRelayer, UnrewardedRelayersState};
 	use sp_std::ops::RangeInclusive;
 
 	fn fill_unrewarded_relayers() {
 		let mut inbound_lane_state =
 			pallet_bridge_messages::InboundLanes::<TestRuntime>::get(LaneId([0, 0, 0, 0]));
-		for n in 0..MaxUnrewardedRelayerEntriesAtInboundLane::get() {
+		for n in 0..BridgedUnderlyingChain::MAX_UNREWARDED_RELAYERS_IN_CONFIRMATION_TX {
 			inbound_lane_state.relayers.push_back(UnrewardedRelayer {
 				relayer: Default::default(),
 				messages: DeliveredMessages { begin: n + 1, end: n + 1 },
@@ -392,7 +376,7 @@ mod tests {
 			relayer: Default::default(),
 			messages: DeliveredMessages {
 				begin: 1,
-				end: MaxUnconfirmedMessagesAtInboundLane::get(),
+				end: BridgedUnderlyingChain::MAX_UNCONFIRMED_MESSAGES_IN_CONFIRMATION_TX,
 			},
 		});
 		pallet_bridge_messages::InboundLanes::<TestRuntime>::insert(
@@ -418,13 +402,13 @@ mod tests {
 				messages_count: nonces_end.checked_sub(nonces_start).map(|x| x + 1).unwrap_or(0)
 					as u32,
 				dispatch_weight: frame_support::weights::Weight::zero(),
-				proof: FromBridgedChainMessagesProof {
+				proof: Box::new(FromBridgedChainMessagesProof {
 					bridged_header_hash: Default::default(),
-					storage_proof: vec![],
+					storage_proof: Default::default(),
 					lane: LaneId([0, 0, 0, 0]),
 					nonces_start,
 					nonces_end,
-				},
+				}),
 			},
 		)
 		.check_obsolete_call()
@@ -508,8 +492,8 @@ mod tests {
 		sp_io::TestExternalities::new(Default::default()).execute_with(|| {
 			fill_unrewarded_messages();
 			assert!(validate_message_delivery(
-				MaxUnconfirmedMessagesAtInboundLane::get(),
-				MaxUnconfirmedMessagesAtInboundLane::get() - 1
+				BridgedUnderlyingChain::MAX_UNCONFIRMED_MESSAGES_IN_CONFIRMATION_TX,
+				BridgedUnderlyingChain::MAX_UNCONFIRMED_MESSAGES_IN_CONFIRMATION_TX - 1
 			));
 		});
 	}
@@ -540,7 +524,7 @@ mod tests {
 			pallet_bridge_messages::Call::<TestRuntime>::receive_messages_delivery_proof {
 				proof: FromBridgedChainMessagesDeliveryProof {
 					bridged_header_hash: Default::default(),
-					storage_proof: Vec::new(),
+					storage_proof: Default::default(),
 					lane: LaneId([0, 0, 0, 0]),
 				},
 				relayers_state: UnrewardedRelayersState {
@@ -608,7 +592,7 @@ mod tests {
 					free_message_slots: if is_empty {
 						0
 					} else {
-						MaxUnconfirmedMessagesAtInboundLane::get()
+						BridgedUnderlyingChain::MAX_UNCONFIRMED_MESSAGES_IN_CONFIRMATION_TX
 					},
 				},
 			},
diff --git a/bridges/bin/runtime-common/src/mock.rs b/bridges/bin/runtime-common/src/mock.rs
index f4947466789..2f248a7162a 100644
--- a/bridges/bin/runtime-common/src/mock.rs
+++ b/bridges/bin/runtime-common/src/mock.rs
@@ -18,26 +18,16 @@
 
 #![cfg(test)]
 
-use crate::messages::{
-	source::{
-		FromThisChainMaximalOutboundPayloadSize, FromThisChainMessagePayload,
-		TargetHeaderChainAdapter,
-	},
-	target::{FromBridgedChainMessagePayload, SourceHeaderChainAdapter},
-	BridgedChainWithMessages, HashOf, MessageBridge, ThisChainWithMessages,
-};
+use crate::messages_xcm_extension::XcmAsPlainPayload;
 
-use bp_header_chain::{ChainWithGrandpa, HeaderChain};
+use bp_header_chain::ChainWithGrandpa;
 use bp_messages::{
 	target_chain::{DispatchMessage, MessageDispatch},
-	LaneId, MessageNonce,
+	ChainWithMessages, LaneId, MessageNonce,
 };
 use bp_parachains::SingleParaStoredHeaderDataBuilder;
 use bp_relayers::PayRewardFromAccount;
-use bp_runtime::{
-	messages::MessageDispatchResult, Chain, ChainId, Parachain, UnderlyingChainProvider,
-};
-use codec::{Decode, Encode};
+use bp_runtime::{messages::MessageDispatchResult, Chain, ChainId, Parachain};
 use frame_support::{
 	derive_impl, parameter_types,
 	weights::{ConstantMultiplier, IdentityFee, RuntimeDbWeight, Weight},
@@ -46,7 +36,7 @@ use pallet_transaction_payment::Multiplier;
 use sp_runtime::{
 	testing::H256,
 	traits::{BlakeTwo256, ConstU32, ConstU64, ConstU8},
-	FixedPointNumber, Perquintill,
+	FixedPointNumber, Perquintill, StateVersion,
 };
 
 /// Account identifier at `ThisChain`.
@@ -61,8 +51,6 @@ pub type ThisChainHash = H256;
 pub type ThisChainHasher = BlakeTwo256;
 /// Runtime call at `ThisChain`.
 pub type ThisChainRuntimeCall = RuntimeCall;
-/// Runtime call origin at `ThisChain`.
-pub type ThisChainCallOrigin = RuntimeOrigin;
 /// Header of `ThisChain`.
 pub type ThisChainHeader = sp_runtime::generic::Header<ThisChainBlockNumber, ThisChainHasher>;
 /// Block of `ThisChain`.
@@ -100,8 +88,6 @@ pub type TestStakeAndSlash = pallet_bridge_relayers::StakeAndSlashNamed<
 pub const TEST_LANE_ID: LaneId = LaneId([0, 0, 0, 0]);
 /// Bridged chain id used in tests.
 pub const TEST_BRIDGED_CHAIN_ID: ChainId = *b"brdg";
-/// Maximal extrinsic weight at the `BridgedChain`.
-pub const BRIDGED_CHAIN_MAX_EXTRINSIC_WEIGHT: usize = 2048;
 /// Maximal extrinsic size at the `BridgedChain`.
 pub const BRIDGED_CHAIN_MAX_EXTRINSIC_SIZE: u32 = 1024;
 
@@ -126,7 +112,6 @@ crate::generate_bridge_reject_obsolete_headers_and_messages! {
 
 parameter_types! {
 	pub const ActiveOutboundLanes: &'static [LaneId] = &[TEST_LANE_ID];
-	pub const BridgedChainId: ChainId = TEST_BRIDGED_CHAIN_ID;
 	pub const BridgedParasPalletName: &'static str = "Paras";
 	pub const ExistentialDeposit: ThisChainBalance = 500;
 	pub const DbWeight: RuntimeDbWeight = RuntimeDbWeight { read: 1, write: 2 };
@@ -136,8 +121,6 @@ parameter_types! {
 	pub AdjustmentVariable: Multiplier = Multiplier::saturating_from_rational(3, 100_000);
 	pub MinimumMultiplier: Multiplier = Multiplier::saturating_from_rational(1, 1_000_000u128);
 	pub MaximumMultiplier: Multiplier = sp_runtime::traits::Bounded::max_value();
-	pub const MaxUnrewardedRelayerEntriesAtInboundLane: MessageNonce = 16;
-	pub const MaxUnconfirmedMessagesAtInboundLane: MessageNonce = 1_000;
 	pub const ReserveId: [u8; 8] = *b"brdgrlrs";
 }
 
@@ -203,17 +186,12 @@ impl pallet_bridge_messages::Config for TestRuntime {
 	type RuntimeEvent = RuntimeEvent;
 	type WeightInfo = pallet_bridge_messages::weights::BridgeWeight<TestRuntime>;
 	type ActiveOutboundLanes = ActiveOutboundLanes;
-	type MaxUnrewardedRelayerEntriesAtInboundLane = MaxUnrewardedRelayerEntriesAtInboundLane;
-	type MaxUnconfirmedMessagesAtInboundLane = MaxUnconfirmedMessagesAtInboundLane;
 
-	type MaximalOutboundPayloadSize = FromThisChainMaximalOutboundPayloadSize<OnThisChainBridge>;
-	type OutboundPayload = FromThisChainMessagePayload;
+	type OutboundPayload = XcmAsPlainPayload;
 
-	type InboundPayload = FromBridgedChainMessagePayload;
-	type InboundRelayer = BridgedChainAccountId;
+	type InboundPayload = Vec<u8>;
 	type DeliveryPayments = ();
 
-	type TargetHeaderChain = TargetHeaderChainAdapter<OnThisChainBridge>;
 	type DeliveryConfirmationPayments = pallet_bridge_relayers::DeliveryConfirmationPaymentsAdapter<
 		TestRuntime,
 		(),
@@ -221,9 +199,11 @@ impl pallet_bridge_messages::Config for TestRuntime {
 	>;
 	type OnMessagesDelivered = ();
 
-	type SourceHeaderChain = SourceHeaderChainAdapter<OnThisChainBridge>;
 	type MessageDispatch = DummyMessageDispatch;
-	type BridgedChainId = BridgedChainId;
+
+	type ThisChain = ThisUnderlyingChain;
+	type BridgedChain = BridgedUnderlyingChain;
+	type BridgedHeaderChain = BridgeGrandpa;
 }
 
 impl pallet_bridge_relayers::Config for TestRuntime {
@@ -262,55 +242,6 @@ impl MessageDispatch for DummyMessageDispatch {
 	}
 }
 
-/// Bridge that is deployed on `ThisChain` and allows sending/receiving messages to/from
-/// `BridgedChain`.
-#[derive(Debug, PartialEq, Eq)]
-pub struct OnThisChainBridge;
-
-impl MessageBridge for OnThisChainBridge {
-	const BRIDGED_MESSAGES_PALLET_NAME: &'static str = "";
-
-	type ThisChain = ThisChain;
-	type BridgedChain = BridgedChain;
-	type BridgedHeaderChain = pallet_bridge_grandpa::GrandpaChainHeaders<TestRuntime, ()>;
-}
-
-/// Bridge that is deployed on `BridgedChain` and allows sending/receiving messages to/from
-/// `ThisChain`.
-#[derive(Debug, PartialEq, Eq)]
-pub struct OnBridgedChainBridge;
-
-impl MessageBridge for OnBridgedChainBridge {
-	const BRIDGED_MESSAGES_PALLET_NAME: &'static str = "";
-
-	type ThisChain = BridgedChain;
-	type BridgedChain = ThisChain;
-	type BridgedHeaderChain = ThisHeaderChain;
-}
-
-/// Dummy implementation of `HeaderChain` for `ThisChain` at the `BridgedChain`.
-pub struct ThisHeaderChain;
-
-impl HeaderChain<ThisUnderlyingChain> for ThisHeaderChain {
-	fn finalized_header_state_root(_hash: HashOf<ThisChain>) -> Option<HashOf<ThisChain>> {
-		unreachable!()
-	}
-}
-
-/// Call origin at `BridgedChain`.
-#[derive(Clone, Debug)]
-pub struct BridgedChainOrigin;
-
-impl From<BridgedChainOrigin>
-	for Result<frame_system::RawOrigin<BridgedChainAccountId>, BridgedChainOrigin>
-{
-	fn from(
-		_origin: BridgedChainOrigin,
-	) -> Result<frame_system::RawOrigin<BridgedChainAccountId>, BridgedChainOrigin> {
-		unreachable!()
-	}
-}
-
 /// Underlying chain of `ThisChain`.
 pub struct ThisUnderlyingChain;
 
@@ -326,6 +257,8 @@ impl Chain for ThisUnderlyingChain {
 	type Nonce = u32;
 	type Signature = sp_runtime::MultiSignature;
 
+	const STATE_VERSION: StateVersion = StateVersion::V1;
+
 	fn max_extrinsic_size() -> u32 {
 		BRIDGED_CHAIN_MAX_EXTRINSIC_SIZE
 	}
@@ -335,29 +268,20 @@ impl Chain for ThisUnderlyingChain {
 	}
 }
 
-/// The chain where we are in tests.
-pub struct ThisChain;
-
-impl UnderlyingChainProvider for ThisChain {
-	type Chain = ThisUnderlyingChain;
-}
+impl ChainWithMessages for ThisUnderlyingChain {
+	const WITH_CHAIN_MESSAGES_PALLET_NAME: &'static str = "";
 
-impl ThisChainWithMessages for ThisChain {
-	type RuntimeOrigin = ThisChainCallOrigin;
+	const MAX_UNREWARDED_RELAYERS_IN_CONFIRMATION_TX: MessageNonce = 16;
+	const MAX_UNCONFIRMED_MESSAGES_IN_CONFIRMATION_TX: MessageNonce = 1000;
 }
 
-impl BridgedChainWithMessages for ThisChain {}
-
 /// Underlying chain of `BridgedChain`.
 pub struct BridgedUnderlyingChain;
 /// Some parachain under `BridgedChain` consensus.
 pub struct BridgedUnderlyingParachain;
-/// Runtime call of the `BridgedChain`.
-#[derive(Decode, Encode)]
-pub struct BridgedChainCall;
 
 impl Chain for BridgedUnderlyingChain {
-	const ID: ChainId = *b"buch";
+	const ID: ChainId = TEST_BRIDGED_CHAIN_ID;
 
 	type BlockNumber = BridgedChainBlockNumber;
 	type Hash = BridgedChainHash;
@@ -368,6 +292,8 @@ impl Chain for BridgedUnderlyingChain {
 	type Nonce = u32;
 	type Signature = sp_runtime::MultiSignature;
 
+	const STATE_VERSION: StateVersion = StateVersion::V1;
+
 	fn max_extrinsic_size() -> u32 {
 		BRIDGED_CHAIN_MAX_EXTRINSIC_SIZE
 	}
@@ -384,6 +310,12 @@ impl ChainWithGrandpa for BridgedUnderlyingChain {
 	const AVERAGE_HEADER_SIZE: u32 = 64;
 }
 
+impl ChainWithMessages for BridgedUnderlyingChain {
+	const WITH_CHAIN_MESSAGES_PALLET_NAME: &'static str = "";
+	const MAX_UNREWARDED_RELAYERS_IN_CONFIRMATION_TX: MessageNonce = 16;
+	const MAX_UNCONFIRMED_MESSAGES_IN_CONFIRMATION_TX: MessageNonce = 1000;
+}
+
 impl Chain for BridgedUnderlyingParachain {
 	const ID: ChainId = *b"bupc";
 
@@ -396,6 +328,8 @@ impl Chain for BridgedUnderlyingParachain {
 	type Nonce = u32;
 	type Signature = sp_runtime::MultiSignature;
 
+	const STATE_VERSION: StateVersion = StateVersion::V1;
+
 	fn max_extrinsic_size() -> u32 {
 		BRIDGED_CHAIN_MAX_EXTRINSIC_SIZE
 	}
@@ -409,19 +343,6 @@ impl Parachain for BridgedUnderlyingParachain {
 	const MAX_HEADER_SIZE: u32 = 1_024;
 }
 
-/// The other, bridged chain, used in tests.
-pub struct BridgedChain;
-
-impl UnderlyingChainProvider for BridgedChain {
-	type Chain = BridgedUnderlyingChain;
-}
-
-impl ThisChainWithMessages for BridgedChain {
-	type RuntimeOrigin = BridgedChainOrigin;
-}
-
-impl BridgedChainWithMessages for BridgedChain {}
-
 /// Run test within test externalities.
 pub fn run_test(test: impl FnOnce()) {
 	sp_io::TestExternalities::new(Default::default()).execute_with(test)
diff --git a/bridges/bin/runtime-common/src/parachains_benchmarking.rs b/bridges/bin/runtime-common/src/parachains_benchmarking.rs
index b3050b9ac0f..bcbd779b44d 100644
--- a/bridges/bin/runtime-common/src/parachains_benchmarking.rs
+++ b/bridges/bin/runtime-common/src/parachains_benchmarking.rs
@@ -18,14 +18,11 @@
 
 #![cfg(feature = "runtime-benchmarks")]
 
-use crate::{
-	messages_benchmarking::insert_header_to_grandpa_pallet,
-	messages_generation::grow_trie_leaf_value,
-};
+use crate::messages_benchmarking::insert_header_to_grandpa_pallet;
 
 use bp_parachains::parachain_head_storage_key_at_source;
 use bp_polkadot_core::parachains::{ParaHash, ParaHead, ParaHeadsProof, ParaId};
-use bp_runtime::{record_all_trie_keys, StorageProofSize};
+use bp_runtime::{grow_storage_value, record_all_trie_keys, Chain, UnverifiedStorageProofParams};
 use codec::Encode;
 use frame_support::traits::Get;
 use pallet_bridge_parachains::{RelayBlockHash, RelayBlockHasher, RelayBlockNumber};
@@ -39,14 +36,14 @@ use sp_trie::{trie_types::TrieDBMutBuilderV1, LayoutV1, MemoryDB, TrieMut};
 pub fn prepare_parachain_heads_proof<R, PI>(
 	parachains: &[ParaId],
 	parachain_head_size: u32,
-	size: StorageProofSize,
+	proof_params: UnverifiedStorageProofParams,
 ) -> (RelayBlockNumber, RelayBlockHash, ParaHeadsProof, Vec<(ParaId, ParaHash)>)
 where
 	R: pallet_bridge_parachains::Config<PI>
 		+ pallet_bridge_grandpa::Config<R::BridgesGrandpaPalletInstance>,
 	PI: 'static,
 	<R as pallet_bridge_grandpa::Config<R::BridgesGrandpaPalletInstance>>::BridgedChain:
-		bp_runtime::Chain<BlockNumber = RelayBlockNumber, Hash = RelayBlockHash>,
+		Chain<BlockNumber = RelayBlockNumber, Hash = RelayBlockHash>,
 {
 	let parachain_head = ParaHead(vec![0u8; parachain_head_size as usize]);
 
@@ -64,7 +61,7 @@ where
 			let storage_key =
 				parachain_head_storage_key_at_source(R::ParasPalletName::get(), *parachain);
 			let leaf_data = if i == 0 {
-				grow_trie_leaf_value(parachain_head.encode(), size)
+				grow_storage_value(parachain_head.encode(), &proof_params)
 			} else {
 				parachain_head.encode()
 			};
diff --git a/bridges/chains/chain-bridge-hub-kusama/src/lib.rs b/bridges/chains/chain-bridge-hub-kusama/src/lib.rs
index ef3ef4ab7b7..c990e8a12f3 100644
--- a/bridges/chains/chain-bridge-hub-kusama/src/lib.rs
+++ b/bridges/chains/chain-bridge-hub-kusama/src/lib.rs
@@ -29,7 +29,7 @@ use frame_support::{
 	dispatch::DispatchClass,
 	sp_runtime::{MultiAddress, MultiSigner},
 };
-use sp_runtime::RuntimeDebug;
+use sp_runtime::{RuntimeDebug, StateVersion};
 
 /// BridgeHubKusama parachain.
 #[derive(RuntimeDebug)]
@@ -48,6 +48,8 @@ impl Chain for BridgeHubKusama {
 	type Nonce = Nonce;
 	type Signature = Signature;
 
+	const STATE_VERSION: StateVersion = StateVersion::V1;
+
 	fn max_extrinsic_size() -> u32 {
 		*BlockLength::get().max.get(DispatchClass::Normal)
 	}
diff --git a/bridges/chains/chain-bridge-hub-polkadot/src/lib.rs b/bridges/chains/chain-bridge-hub-polkadot/src/lib.rs
index 9db71af928e..7379b8863b1 100644
--- a/bridges/chains/chain-bridge-hub-polkadot/src/lib.rs
+++ b/bridges/chains/chain-bridge-hub-polkadot/src/lib.rs
@@ -26,7 +26,7 @@ use bp_runtime::{
 	decl_bridge_finality_runtime_apis, decl_bridge_messages_runtime_apis, Chain, ChainId, Parachain,
 };
 use frame_support::dispatch::DispatchClass;
-use sp_runtime::RuntimeDebug;
+use sp_runtime::{RuntimeDebug, StateVersion};
 
 /// BridgeHubPolkadot parachain.
 #[derive(RuntimeDebug)]
@@ -45,6 +45,8 @@ impl Chain for BridgeHubPolkadot {
 	type Nonce = Nonce;
 	type Signature = Signature;
 
+	const STATE_VERSION: StateVersion = StateVersion::V1;
+
 	fn max_extrinsic_size() -> u32 {
 		*BlockLength::get().max.get(DispatchClass::Normal)
 	}
diff --git a/bridges/chains/chain-bridge-hub-rococo/src/lib.rs b/bridges/chains/chain-bridge-hub-rococo/src/lib.rs
index d7097f01c53..73af997b995 100644
--- a/bridges/chains/chain-bridge-hub-rococo/src/lib.rs
+++ b/bridges/chains/chain-bridge-hub-rococo/src/lib.rs
@@ -25,8 +25,10 @@ use bp_messages::*;
 use bp_runtime::{
 	decl_bridge_finality_runtime_apis, decl_bridge_messages_runtime_apis, Chain, ChainId, Parachain,
 };
-use frame_support::dispatch::DispatchClass;
-use sp_runtime::{MultiAddress, MultiSigner, RuntimeDebug};
+use frame_support::{
+	dispatch::DispatchClass,
+	sp_runtime::{MultiAddress, MultiSigner, RuntimeDebug, StateVersion},
+};
 
 /// BridgeHubRococo parachain.
 #[derive(RuntimeDebug)]
@@ -45,6 +47,8 @@ impl Chain for BridgeHubRococo {
 	type Nonce = Nonce;
 	type Signature = Signature;
 
+	const STATE_VERSION: StateVersion = StateVersion::V1;
+
 	fn max_extrinsic_size() -> u32 {
 		*BlockLength::get().max.get(DispatchClass::Normal)
 	}
@@ -103,10 +107,10 @@ frame_support::parameter_types! {
 	pub const BridgeHubRococoBaseXcmFeeInRocs: u128 = 59_034_266;
 
 	/// Transaction fee that is paid at the Rococo BridgeHub for delivering single inbound message.
-	/// (initially was calculated by test `BridgeHubRococo::can_calculate_fee_for_complex_message_delivery_transaction` + `33%`)
+	/// (initially was calculated by test `BridgeHubRococo::can_calculate_fee_for_standalone_message_delivery_transaction` + `33%`)
 	pub const BridgeHubRococoBaseDeliveryFeeInRocs: u128 = 314_037_860;
 
 	/// Transaction fee that is paid at the Rococo BridgeHub for delivering single outbound message confirmation.
-	/// (initially was calculated by test `BridgeHubRococo::can_calculate_fee_for_complex_message_confirmation_transaction` + `33%`)
+	/// (initially was calculated by test `BridgeHubRococo::can_calculate_fee_for_standalone_message_confirmation_transaction` + `33%`)
 	pub const BridgeHubRococoBaseConfirmationFeeInRocs: u128 = 57_414_813;
 }
diff --git a/bridges/chains/chain-bridge-hub-westend/src/lib.rs b/bridges/chains/chain-bridge-hub-westend/src/lib.rs
index 800f290d7bf..17ff2c858a1 100644
--- a/bridges/chains/chain-bridge-hub-westend/src/lib.rs
+++ b/bridges/chains/chain-bridge-hub-westend/src/lib.rs
@@ -25,7 +25,7 @@ use bp_runtime::{
 	decl_bridge_finality_runtime_apis, decl_bridge_messages_runtime_apis, Chain, ChainId, Parachain,
 };
 use frame_support::dispatch::DispatchClass;
-use sp_runtime::RuntimeDebug;
+use sp_runtime::{RuntimeDebug, StateVersion};
 
 /// BridgeHubWestend parachain.
 #[derive(RuntimeDebug)]
@@ -44,6 +44,8 @@ impl Chain for BridgeHubWestend {
 	type Nonce = Nonce;
 	type Signature = Signature;
 
+	const STATE_VERSION: StateVersion = StateVersion::V1;
+
 	fn max_extrinsic_size() -> u32 {
 		*BlockLength::get().max.get(DispatchClass::Normal)
 	}
diff --git a/bridges/chains/chain-kusama/src/lib.rs b/bridges/chains/chain-kusama/src/lib.rs
index fd7172c5869..dcd0b23abbb 100644
--- a/bridges/chains/chain-kusama/src/lib.rs
+++ b/bridges/chains/chain-kusama/src/lib.rs
@@ -23,7 +23,7 @@ pub use bp_polkadot_core::*;
 
 use bp_header_chain::ChainWithGrandpa;
 use bp_runtime::{decl_bridge_finality_runtime_apis, Chain, ChainId};
-use frame_support::weights::Weight;
+use frame_support::{sp_runtime::StateVersion, weights::Weight};
 
 /// Kusama Chain
 pub struct Kusama;
@@ -41,6 +41,8 @@ impl Chain for Kusama {
 	type Nonce = Nonce;
 	type Signature = Signature;
 
+	const STATE_VERSION: StateVersion = StateVersion::V0;
+
 	fn max_extrinsic_size() -> u32 {
 		max_extrinsic_size()
 	}
diff --git a/bridges/chains/chain-polkadot-bulletin/src/lib.rs b/bridges/chains/chain-polkadot-bulletin/src/lib.rs
index f3d300567f2..88980a95750 100644
--- a/bridges/chains/chain-polkadot-bulletin/src/lib.rs
+++ b/bridges/chains/chain-polkadot-bulletin/src/lib.rs
@@ -37,7 +37,9 @@ use frame_support::{
 };
 use frame_system::limits;
 use scale_info::TypeInfo;
-use sp_runtime::{traits::DispatchInfoOf, transaction_validity::TransactionValidityError, Perbill};
+use sp_runtime::{
+	traits::DispatchInfoOf, transaction_validity::TransactionValidityError, Perbill, StateVersion,
+};
 
 // This chain reuses most of Polkadot primitives.
 pub use bp_polkadot_core::{
@@ -192,6 +194,8 @@ impl Chain for PolkadotBulletin {
 	type Nonce = Nonce;
 	type Signature = Signature;
 
+	const STATE_VERSION: StateVersion = StateVersion::V1;
+
 	fn max_extrinsic_size() -> u32 {
 		*BlockLength::get().max.get(DispatchClass::Normal)
 	}
diff --git a/bridges/chains/chain-polkadot/src/lib.rs b/bridges/chains/chain-polkadot/src/lib.rs
index a8cac0467d5..f4b262d4073 100644
--- a/bridges/chains/chain-polkadot/src/lib.rs
+++ b/bridges/chains/chain-polkadot/src/lib.rs
@@ -25,7 +25,7 @@ use bp_header_chain::ChainWithGrandpa;
 use bp_runtime::{
 	decl_bridge_finality_runtime_apis, extensions::PrevalidateAttests, Chain, ChainId,
 };
-use frame_support::weights::Weight;
+use frame_support::{sp_runtime::StateVersion, weights::Weight};
 
 /// Polkadot Chain
 pub struct Polkadot;
@@ -43,6 +43,8 @@ impl Chain for Polkadot {
 	type Nonce = Nonce;
 	type Signature = Signature;
 
+	const STATE_VERSION: StateVersion = StateVersion::V0;
+
 	fn max_extrinsic_size() -> u32 {
 		max_extrinsic_size()
 	}
diff --git a/bridges/chains/chain-rococo/src/lib.rs b/bridges/chains/chain-rococo/src/lib.rs
index b290fe71c82..bfcafdf41ea 100644
--- a/bridges/chains/chain-rococo/src/lib.rs
+++ b/bridges/chains/chain-rococo/src/lib.rs
@@ -23,7 +23,7 @@ pub use bp_polkadot_core::*;
 
 use bp_header_chain::ChainWithGrandpa;
 use bp_runtime::{decl_bridge_finality_runtime_apis, Chain, ChainId};
-use frame_support::weights::Weight;
+use frame_support::{sp_runtime::StateVersion, weights::Weight};
 
 /// Rococo Chain
 pub struct Rococo;
@@ -41,6 +41,8 @@ impl Chain for Rococo {
 	type Nonce = Nonce;
 	type Signature = Signature;
 
+	const STATE_VERSION: StateVersion = StateVersion::V1;
+
 	fn max_extrinsic_size() -> u32 {
 		max_extrinsic_size()
 	}
diff --git a/bridges/chains/chain-westend/src/lib.rs b/bridges/chains/chain-westend/src/lib.rs
index ef451f7de0a..2a247e03e59 100644
--- a/bridges/chains/chain-westend/src/lib.rs
+++ b/bridges/chains/chain-westend/src/lib.rs
@@ -23,7 +23,7 @@ pub use bp_polkadot_core::*;
 
 use bp_header_chain::ChainWithGrandpa;
 use bp_runtime::{decl_bridge_finality_runtime_apis, Chain, ChainId};
-use frame_support::weights::Weight;
+use frame_support::{sp_runtime::StateVersion, weights::Weight};
 
 /// Westend Chain
 pub struct Westend;
@@ -41,6 +41,8 @@ impl Chain for Westend {
 	type Nonce = Nonce;
 	type Signature = Signature;
 
+	const STATE_VERSION: StateVersion = StateVersion::V1;
+
 	fn max_extrinsic_size() -> u32 {
 		max_extrinsic_size()
 	}
diff --git a/bridges/modules/beefy/src/mock.rs b/bridges/modules/beefy/src/mock.rs
index 53efd57c29a..3b751ddf066 100644
--- a/bridges/modules/beefy/src/mock.rs
+++ b/bridges/modules/beefy/src/mock.rs
@@ -29,6 +29,7 @@ use sp_core::{sr25519::Signature, Pair};
 use sp_runtime::{
 	testing::{Header, H256},
 	traits::{BlakeTwo256, Hash},
+	StateVersion,
 };
 
 pub use sp_consensus_beefy::ecdsa_crypto::{AuthorityId as BeefyId, Pair as BeefyPair};
@@ -93,6 +94,8 @@ impl Chain for TestBridgedChain {
 	type Nonce = u64;
 	type Signature = Signature;
 
+	const STATE_VERSION: StateVersion = StateVersion::V1;
+
 	fn max_extrinsic_size() -> u32 {
 		unreachable!()
 	}
diff --git a/bridges/modules/grandpa/Cargo.toml b/bridges/modules/grandpa/Cargo.toml
index 307c7ddaaff..6d1419ae5b0 100644
--- a/bridges/modules/grandpa/Cargo.toml
+++ b/bridges/modules/grandpa/Cargo.toml
@@ -14,7 +14,6 @@ workspace = true
 
 [dependencies]
 codec = { workspace = true }
-finality-grandpa = { workspace = true }
 log = { workspace = true }
 scale-info = { features = ["derive"], workspace = true }
 
@@ -30,13 +29,13 @@ frame-system = { workspace = true }
 sp-consensus-grandpa = { features = ["serde"], workspace = true }
 sp-runtime = { features = ["serde"], workspace = true }
 sp-std = { workspace = true }
-sp-trie = { workspace = true }
 
 # Optional Benchmarking Dependencies
 bp-test-utils = { optional = true, workspace = true }
 frame-benchmarking = { optional = true, workspace = true }
 
 [dev-dependencies]
+bp-runtime = { features = ["test-helpers"], workspace = true }
 sp-core = { workspace = true, default-features = true }
 sp-io = { workspace = true, default-features = true }
 
@@ -47,7 +46,6 @@ std = [
 	"bp-runtime/std",
 	"bp-test-utils/std",
 	"codec/std",
-	"finality-grandpa/std",
 	"frame-benchmarking/std",
 	"frame-support/std",
 	"frame-system/std",
@@ -56,7 +54,6 @@ std = [
 	"sp-consensus-grandpa/std",
 	"sp-runtime/std",
 	"sp-std/std",
-	"sp-trie/std",
 ]
 runtime-benchmarks = [
 	"bp-test-utils",
diff --git a/bridges/modules/grandpa/src/lib.rs b/bridges/modules/grandpa/src/lib.rs
index 3b77f676870..c62951b7465 100644
--- a/bridges/modules/grandpa/src/lib.rs
+++ b/bridges/modules/grandpa/src/lib.rs
@@ -1443,11 +1443,14 @@ mod tests {
 	}
 
 	#[test]
-	fn parse_finalized_storage_proof_rejects_proof_on_unknown_header() {
+	fn verify_storage_proof_rejects_unknown_header() {
 		run_test(|| {
 			assert_noop!(
-				Pallet::<TestRuntime>::storage_proof_checker(Default::default(), vec![],)
-					.map(|_| ()),
+				Pallet::<TestRuntime>::verify_storage_proof(
+					Default::default(),
+					Default::default(),
+				)
+				.map(|_| ()),
 				bp_header_chain::HeaderChainError::UnknownHeader,
 			);
 		});
@@ -1465,9 +1468,7 @@ mod tests {
 			<BestFinalized<TestRuntime>>::put(HeaderId(2, hash));
 			<ImportedHeaders<TestRuntime>>::insert(hash, header.build());
 
-			assert_ok!(
-				Pallet::<TestRuntime>::storage_proof_checker(hash, storage_proof).map(|_| ())
-			);
+			assert_ok!(Pallet::<TestRuntime>::verify_storage_proof(hash, storage_proof).map(|_| ()));
 		});
 	}
 
diff --git a/bridges/modules/grandpa/src/mock.rs b/bridges/modules/grandpa/src/mock.rs
index 27df9d9c78f..71af6182e05 100644
--- a/bridges/modules/grandpa/src/mock.rs
+++ b/bridges/modules/grandpa/src/mock.rs
@@ -20,7 +20,8 @@
 use bp_header_chain::ChainWithGrandpa;
 use bp_runtime::{Chain, ChainId};
 use frame_support::{
-	construct_runtime, derive_impl, parameter_types, traits::Hooks, weights::Weight,
+	construct_runtime, derive_impl, parameter_types, sp_runtime::StateVersion, traits::Hooks,
+	weights::Weight,
 };
 use sp_core::sr25519::Signature;
 
@@ -78,6 +79,8 @@ impl Chain for TestBridgedChain {
 	type Nonce = u64;
 	type Signature = Signature;
 
+	const STATE_VERSION: StateVersion = StateVersion::V1;
+
 	fn max_extrinsic_size() -> u32 {
 		unreachable!()
 	}
diff --git a/bridges/modules/messages/Cargo.toml b/bridges/modules/messages/Cargo.toml
index 573d0ba4766..33f524030d2 100644
--- a/bridges/modules/messages/Cargo.toml
+++ b/bridges/modules/messages/Cargo.toml
@@ -13,52 +13,67 @@ workspace = true
 [dependencies]
 codec = { workspace = true }
 log = { workspace = true }
-num-traits = { workspace = true }
 scale-info = { features = ["derive"], workspace = true }
 
 # Bridge dependencies
-
+bp-header-chain = { workspace = true }
 bp-messages = { workspace = true }
 bp-runtime = { workspace = true }
 
 # Substrate Dependencies
-
 frame-benchmarking = { optional = true, workspace = true }
 frame-support = { workspace = true }
 frame-system = { workspace = true }
 sp-runtime = { workspace = true }
 sp-std = { workspace = true }
+sp-trie = { optional = true, workspace = true }
 
 [dev-dependencies]
-bp-test-utils = { workspace = true, default-features = true }
-pallet-balances = { workspace = true, default-features = true }
-sp-io = { workspace = true, default-features = true }
+bp-runtime = { features = ["test-helpers"], workspace = true }
+bp-test-utils = { workspace = true }
+pallet-balances = { workspace = true }
+pallet-bridge-grandpa = { workspace = true }
+sp-io = { workspace = true }
+sp-core = { workspace = true }
 
 [features]
 default = ["std"]
 std = [
+	"bp-header-chain/std",
 	"bp-messages/std",
 	"bp-runtime/std",
+	"bp-test-utils/std",
 	"codec/std",
 	"frame-benchmarking/std",
 	"frame-support/std",
 	"frame-system/std",
 	"log/std",
-	"num-traits/std",
+	"pallet-balances/std",
+	"pallet-bridge-grandpa/std",
 	"scale-info/std",
+	"sp-core/std",
+	"sp-io/std",
 	"sp-runtime/std",
 	"sp-std/std",
+	"sp-trie/std",
 ]
 runtime-benchmarks = [
+	"bp-runtime/test-helpers",
 	"frame-benchmarking/runtime-benchmarks",
 	"frame-support/runtime-benchmarks",
 	"frame-system/runtime-benchmarks",
 	"pallet-balances/runtime-benchmarks",
+	"pallet-bridge-grandpa/runtime-benchmarks",
 	"sp-runtime/runtime-benchmarks",
 ]
 try-runtime = [
 	"frame-support/try-runtime",
 	"frame-system/try-runtime",
 	"pallet-balances/try-runtime",
+	"pallet-bridge-grandpa/try-runtime",
 	"sp-runtime/try-runtime",
 ]
+test-helpers = [
+	"bp-runtime/test-helpers",
+	"sp-trie",
+]
diff --git a/bridges/modules/messages/README.md b/bridges/modules/messages/README.md
index c06b96b857d..80fd92eb0e5 100644
--- a/bridges/modules/messages/README.md
+++ b/bridges/modules/messages/README.md
@@ -104,17 +104,22 @@ the message. When a message is delivered to the target chain, the `MessagesDeliv
 `receive_messages_delivery_proof()` transaction. The `MessagesDelivered` contains the message lane identifier and
 inclusive range of delivered message nonces.
 
-The pallet provides no means to get the result of message dispatch at the target chain. If that is required, it must be
-done outside of the pallet. For example, XCM messages, when dispatched, have special instructions to send some data back
-to the sender. Other dispatchers may use similar mechanism for that.
-### How to plug-in Messages Module to Send Messages to the Bridged Chain?
-
-The `pallet_bridge_messages::Config` trait has 3 main associated types that are used to work with outbound messages. The
-`pallet_bridge_messages::Config::TargetHeaderChain` defines how we see the bridged chain as the target for our outbound
-messages. It must be able to check that the bridged chain may accept our message - like that the message has size below
-maximal possible transaction size of the chain and so on. And when the relayer sends us a confirmation transaction, this
-implementation must be able to parse and verify the proof of messages delivery. Normally, you would reuse the same
-(configurable) type on all chains that are sending messages to the same bridged chain.
+The pallet provides no means to get the result of message dispatch at the target chain. If that is
+required, it must be done outside of the pallet. For example, XCM messages, when dispatched, have
+special instructions to send some data back to the sender. Other dispatchers may use similar
+mechanism for that.
+
+### How to plug-in Messages Module to Send and Receive Messages from the Bridged Chain?
+
+The `pallet_bridge_messages::Config` trait has 2 main associated types that are used to work with
+inbound messages. The `pallet_bridge_messages::BridgedChain` defines basic primitives of the bridged
+chain. The `pallet_bridge_messages::BridgedHeaderChain` defines the way we access the bridged chain
+headers in our runtime. You may use `pallet_bridge_grandpa` if you're bridging with chain that uses
+GRANDPA finality or `pallet_bridge_parachains::ParachainHeaders` if you're bridging with parachain.
+
+The `pallet_bridge_messages::Config::MessageDispatch` defines a way on how to dispatch delivered
+messages. Apart from actually dispatching the message, the implementation must return the correct
+dispatch weight of the message before dispatch is called.
 
 The last type is the `pallet_bridge_messages::Config::DeliveryConfirmationPayments`. When confirmation
 transaction is received, we call the `pay_reward()` method, passing the range of delivered messages.
@@ -129,18 +134,6 @@ You should be looking at the `bp_messages::source_chain::ForbidOutboundMessages`
 [`bp_messages::source_chain`](../../primitives/messages/src/source_chain.rs). It implements all required traits and will
 simply reject all transactions, related to outbound messages.
 
-### How to plug-in Messages Module to Receive Messages from the Bridged Chain?
-
-The `pallet_bridge_messages::Config` trait has 2 main associated types that are used to work with inbound messages. The
-`pallet_bridge_messages::Config::SourceHeaderChain` defines how we see the bridged chain as the source of our inbound
-messages. When relayer sends us a delivery transaction, this implementation must be able to parse and verify the proof
-of messages wrapped in this transaction. Normally, you would reuse the same (configurable) type on all chains that are
-sending messages to the same bridged chain.
-
-The `pallet_bridge_messages::Config::MessageDispatch` defines a way on how to dispatch delivered messages. Apart from
-actually dispatching the message, the implementation must return the correct dispatch weight of the message before
-dispatch is called.
-
 ### I have a Messages Module in my Runtime, but I Want to Reject all Inbound Messages. What shall I do?
 
 You should be looking at the `bp_messages::target_chain::ForbidInboundMessages` structure from the
@@ -150,36 +143,42 @@ and will simply reject all transactions, related to inbound messages.
 ### What about other Constants in the Messages Module Configuration Trait?
 
 Two settings that are used to check messages in the `send_message()` function. The
-`pallet_bridge_messages::Config::ActiveOutboundLanes` is an array of all message lanes, that may be used to send
-messages. All messages sent using other lanes are rejected. All messages that have size above
-`pallet_bridge_messages::Config::MaximalOutboundPayloadSize` will also be rejected.
-
-To be able to reward the relayer for delivering messages, we store a map of message nonces range => identifier of the
-relayer that has delivered this range at the target chain runtime storage. If a relayer delivers multiple consequent
-ranges, they're merged into single entry. So there may be more than one entry for the same relayer. Eventually, this
-whole map must be delivered back to the source chain to confirm delivery and pay rewards. So to make sure we are able to
-craft this confirmation transaction, we need to: (1) keep the size of this map below a certain limit and (2) make sure
-that the weight of processing this map is below a certain limit. Both size and processing weight mostly depend on the
-number of entries. The number of entries is limited with the
-`pallet_bridge_messages::ConfigMaxUnrewardedRelayerEntriesAtInboundLane` parameter. Processing weight also depends on
-the total number of messages that are being confirmed, because every confirmed message needs to be read. So there's
-another `pallet_bridge_messages::Config::MaxUnconfirmedMessagesAtInboundLane` parameter for that.
-
-When choosing values for these parameters, you must also keep in mind that if proof in your scheme is based on finality
-of headers (and it is the most obvious option for Substrate-based chains with finality notion), then choosing too small
-values for these parameters may cause significant delays in message delivery. That's because there are too many actors
-involved in this scheme: 1) authorities that are finalizing headers of the target chain need to finalize header with
-non-empty map; 2) the headers relayer then needs to submit this header and its finality proof to the source chain; 3)
-the messages relayer must then send confirmation transaction (storage proof of this map) to the source chain; 4) when
-the confirmation transaction will be mined at some header, source chain authorities must finalize this header; 5) the
-headers relay then needs to submit this header and its finality proof to the target chain; 6) only now the messages
-relayer may submit new messages from the source to target chain and prune the entry from the map.
-
-Delivery transaction requires the relayer to provide both number of entries and total number of messages in the map.
-This means that the module never charges an extra cost for delivering a map - the relayer would need to pay exactly for
-the number of entries+messages it has delivered. So the best guess for values of these parameters would be the pair that
-would occupy `N` percent of the maximal transaction size and weight of the source chain. The `N` should be large enough
-to process large maps, at the same time keeping reserve for future source chain upgrades.
+`pallet_bridge_messages::Config::ActiveOutboundLanes` is an array of all message lanes, that
+may be used to send messages. All messages sent using other lanes are rejected. All messages that have
+size above `pallet_bridge_messages::Config::MaximalOutboundPayloadSize` will also be rejected.
+
+To be able to reward the relayer for delivering messages, we store a map of message nonces range =>
+identifier of the relayer that has delivered this range at the target chain runtime storage. If a
+relayer delivers multiple consequent ranges, they're merged into single entry. So there may be more
+than one entry for the same relayer. Eventually, this whole map must be delivered back to the source
+chain to confirm delivery and pay rewards. So to make sure we are able to craft this confirmation
+transaction, we need to: (1) keep the size of this map below a certain limit and (2) make sure that
+the weight of processing this map is below a certain limit. Both size and processing weight mostly
+depend on the number of entries. The number of entries is limited with the
+`pallet_bridge_messages::Config::BridgedChain::MAX_UNREWARDED_RELAYERS_IN_CONFIRMATION_TX` parameter.
+Processing weight also depends on the total number of messages that are being confirmed, because every
+confirmed message needs to be read. So there's another
+`pallet_bridge_messages::Config::BridgedChain::MAX_UNCONFIRMED_MESSAGES_IN_CONFIRMATION_TX` parameter
+for that.
+
+When choosing values for these parameters, you must also keep in mind that if proof in your scheme
+is based on finality of headers (and it is the most obvious option for Substrate-based chains with
+finality notion), then choosing too small values for these parameters may cause significant delays
+in message delivery. That's because there are too many actors involved in this scheme: 1) authorities
+that are finalizing headers of the target chain need to finalize header with non-empty map; 2) the
+headers relayer then needs to submit this header and its finality proof to the source chain; 3) the
+messages relayer must then send confirmation transaction (storage proof of this map) to the source
+chain; 4) when the confirmation transaction will be mined at some header, source chain authorities
+must finalize this header; 5) the headers relay then needs to submit this header and its finality
+proof to the target chain; 6) only now the messages relayer may submit new messages from the source
+to target chain and prune the entry from the map.
+
+Delivery transaction requires the relayer to provide both number of entries and total number of
+messages in the map. This means that the module never charges an extra cost for delivering a map -
+the relayer would need to pay exactly for the number of entries+messages it has delivered. So the
+best guess for values of these parameters would be the pair that would occupy `N` percent of the
+maximal transaction size and weight of the source chain. The `N` should be large enough to process
+large maps, at the same time keeping reserve for future source chain upgrades.
 
 ## Non-Essential Functionality
 
diff --git a/bridges/modules/messages/src/benchmarking.rs b/bridges/modules/messages/src/benchmarking.rs
index 4f13c440967..d38aaf32dc9 100644
--- a/bridges/modules/messages/src/benchmarking.rs
+++ b/bridges/modules/messages/src/benchmarking.rs
@@ -16,19 +16,22 @@
 
 //! Messages pallet benchmarking.
 
+#![cfg(feature = "runtime-benchmarks")]
+
 use crate::{
 	inbound_lane::InboundLaneStorage, outbound_lane, weights_ext::EXPECTED_DEFAULT_MESSAGE_LENGTH,
-	Call, OutboundLanes, RuntimeInboundLaneStorage,
+	BridgedChainOf, Call, OutboundLanes, RuntimeInboundLaneStorage,
 };
 
 use bp_messages::{
-	source_chain::TargetHeaderChain, target_chain::SourceHeaderChain, DeliveredMessages,
+	source_chain::FromBridgedChainMessagesDeliveryProof,
+	target_chain::FromBridgedChainMessagesProof, ChainWithMessages, DeliveredMessages,
 	InboundLaneData, LaneId, MessageNonce, OutboundLaneData, UnrewardedRelayer,
 	UnrewardedRelayersState,
 };
-use bp_runtime::StorageProofSize;
+use bp_runtime::{AccountIdOf, HashOf, UnverifiedStorageProofParams};
 use codec::Decode;
-use frame_benchmarking::{account, benchmarks_instance_pallet};
+use frame_benchmarking::{account, v2::*};
 use frame_support::weights::Weight;
 use frame_system::RawOrigin;
 use sp_runtime::{traits::TrailingZeroInput, BoundedVec};
@@ -54,7 +57,7 @@ pub struct MessageProofParams {
 	/// return `true` from the `is_message_successfully_dispatched`.
 	pub is_successful_dispatch_expected: bool,
 	/// Proof size requirements.
-	pub size: StorageProofSize,
+	pub proof_params: UnverifiedStorageProofParams,
 }
 
 /// Benchmark-specific message delivery proof parameters.
@@ -65,7 +68,7 @@ pub struct MessageDeliveryProofParams<ThisChainAccountId> {
 	/// The proof needs to include this inbound lane data.
 	pub inbound_lane_data: InboundLaneData<ThisChainAccountId>,
 	/// Proof size requirements.
-	pub size: StorageProofSize,
+	pub proof_params: UnverifiedStorageProofParams,
 }
 
 /// Trait that must be implemented by runtime.
@@ -80,8 +83,8 @@ pub trait Config<I: 'static>: crate::Config<I> {
 	/// Return id of relayer account at the bridged chain.
 	///
 	/// By default, zero account is returned.
-	fn bridged_relayer_id() -> Self::InboundRelayer {
-		Self::InboundRelayer::decode(&mut TrailingZeroInput::zeroes()).unwrap()
+	fn bridged_relayer_id() -> AccountIdOf<BridgedChainOf<Self, I>> {
+		Decode::decode(&mut TrailingZeroInput::zeroes()).unwrap()
 	}
 
 	/// Create given account and give it enough balance for test purposes. Used to create
@@ -94,11 +97,11 @@ pub trait Config<I: 'static>: crate::Config<I> {
 	/// Prepare messages proof to receive by the module.
 	fn prepare_message_proof(
 		params: MessageProofParams,
-	) -> (<Self::SourceHeaderChain as SourceHeaderChain>::MessagesProof, Weight);
+	) -> (FromBridgedChainMessagesProof<HashOf<BridgedChainOf<Self, I>>>, Weight);
 	/// Prepare messages delivery proof to receive by the module.
 	fn prepare_message_delivery_proof(
 		params: MessageDeliveryProofParams<Self::AccountId>,
-	) -> <Self::TargetHeaderChain as TargetHeaderChain<Self::OutboundPayload, Self::AccountId>>::MessagesDeliveryProof;
+	) -> FromBridgedChainMessagesDeliveryProof<HashOf<BridgedChainOf<Self, I>>>;
 
 	/// Returns true if message has been successfully dispatched or not.
 	fn is_message_successfully_dispatched(_nonce: MessageNonce) -> bool {
@@ -109,174 +112,227 @@ pub trait Config<I: 'static>: crate::Config<I> {
 	fn is_relayer_rewarded(relayer: &Self::AccountId) -> bool;
 }
 
-benchmarks_instance_pallet! {
+fn send_regular_message<T: Config<I>, I: 'static>() {
+	let mut outbound_lane = outbound_lane::<T, I>(T::bench_lane_id());
+	outbound_lane.send_message(BoundedVec::try_from(vec![]).expect("We craft valid messages"));
+}
+
+fn receive_messages<T: Config<I>, I: 'static>(nonce: MessageNonce) {
+	let mut inbound_lane_storage =
+		RuntimeInboundLaneStorage::<T, I>::from_lane_id(T::bench_lane_id());
+	inbound_lane_storage.set_data(InboundLaneData {
+		relayers: vec![UnrewardedRelayer {
+			relayer: T::bridged_relayer_id(),
+			messages: DeliveredMessages::new(nonce),
+		}]
+		.into_iter()
+		.collect(),
+		last_confirmed_nonce: 0,
+	});
+}
+
+struct ReceiveMessagesProofSetup<T: Config<I>, I: 'static> {
+	relayer_id_on_src: AccountIdOf<BridgedChainOf<T, I>>,
+	relayer_id_on_tgt: T::AccountId,
+	msgs_count: u32,
+	_phantom_data: sp_std::marker::PhantomData<I>,
+}
+
+impl<T: Config<I>, I: 'static> ReceiveMessagesProofSetup<T, I> {
+	const LATEST_RECEIVED_NONCE: MessageNonce = 20;
+
+	fn new(msgs_count: u32) -> Self {
+		let setup = Self {
+			relayer_id_on_src: T::bridged_relayer_id(),
+			relayer_id_on_tgt: account("relayer", 0, SEED),
+			msgs_count,
+			_phantom_data: Default::default(),
+		};
+		T::endow_account(&setup.relayer_id_on_tgt);
+		// mark messages 1..=latest_recvd_nonce as delivered
+		receive_messages::<T, I>(Self::LATEST_RECEIVED_NONCE);
+
+		setup
+	}
+
+	fn relayer_id_on_src(&self) -> AccountIdOf<BridgedChainOf<T, I>> {
+		self.relayer_id_on_src.clone()
+	}
+
+	fn relayer_id_on_tgt(&self) -> T::AccountId {
+		self.relayer_id_on_tgt.clone()
+	}
+
+	fn last_nonce(&self) -> MessageNonce {
+		Self::LATEST_RECEIVED_NONCE + self.msgs_count as u64
+	}
+
+	fn nonces(&self) -> RangeInclusive<MessageNonce> {
+		(Self::LATEST_RECEIVED_NONCE + 1)..=self.last_nonce()
+	}
+
+	fn check_last_nonce(&self) {
+		assert_eq!(
+			crate::InboundLanes::<T, I>::get(&T::bench_lane_id()).last_delivered_nonce(),
+			self.last_nonce(),
+		);
+	}
+}
+
+#[instance_benchmarks]
+mod benchmarks {
+	use super::*;
+
 	//
 	// Benchmarks that are used directly by the runtime calls weight formulae.
 	//
 
-	// Benchmark `receive_messages_proof` extrinsic with single minimal-weight message and following conditions:
+	fn max_msgs<T: Config<I>, I: 'static>() -> u32 {
+		T::BridgedChain::MAX_UNCONFIRMED_MESSAGES_IN_CONFIRMATION_TX as u32 -
+			ReceiveMessagesProofSetup::<T, I>::LATEST_RECEIVED_NONCE as u32
+	}
+
+	// Benchmark `receive_messages_proof` extrinsic with single minimal-weight message and following
+	// conditions:
 	// * proof does not include outbound lane state proof;
 	// * inbound lane already has state, so it needs to be read and decoded;
 	// * message is dispatched (reminder: dispatch weight should be minimal);
 	// * message requires all heavy checks done by dispatcher.
-	//
-	// This is base benchmark for all other message delivery benchmarks.
-	receive_single_message_proof {
-		let relayer_id_on_source = T::bridged_relayer_id();
-		let relayer_id_on_target = account("relayer", 0, SEED);
-		T::endow_account(&relayer_id_on_target);
-
-		// mark messages 1..=20 as delivered
-		receive_messages::<T, I>(20);
-
+	#[benchmark]
+	fn receive_single_message_proof() {
+		// setup code
+		let setup = ReceiveMessagesProofSetup::<T, I>::new(1);
 		let (proof, dispatch_weight) = T::prepare_message_proof(MessageProofParams {
 			lane: T::bench_lane_id(),
-			message_nonces: 21..=21,
+			message_nonces: setup.nonces(),
 			outbound_lane_data: None,
 			is_successful_dispatch_expected: false,
-			size: StorageProofSize::Minimal(EXPECTED_DEFAULT_MESSAGE_LENGTH),
+			proof_params: UnverifiedStorageProofParams::from_db_size(
+				EXPECTED_DEFAULT_MESSAGE_LENGTH,
+			),
 		});
-	}: receive_messages_proof(RawOrigin::Signed(relayer_id_on_target), relayer_id_on_source, proof, 1, dispatch_weight)
-	verify {
-		assert_eq!(
-			crate::InboundLanes::<T, I>::get(&T::bench_lane_id()).last_delivered_nonce(),
-			21,
+
+		#[extrinsic_call]
+		receive_messages_proof(
+			RawOrigin::Signed(setup.relayer_id_on_tgt()),
+			setup.relayer_id_on_src(),
+			Box::new(proof),
+			setup.msgs_count,
+			dispatch_weight,
 		);
+
+		// verification code
+		setup.check_last_nonce();
 	}
 
-	// Benchmark `receive_messages_proof` extrinsic with two minimal-weight messages and following conditions:
+	// Benchmark `receive_messages_proof` extrinsic with `n` minimal-weight messages and following
+	// conditions:
 	// * proof does not include outbound lane state proof;
 	// * inbound lane already has state, so it needs to be read and decoded;
 	// * message is dispatched (reminder: dispatch weight should be minimal);
 	// * message requires all heavy checks done by dispatcher.
-	//
-	// The weight of single message delivery could be approximated as
-	// `weight(receive_two_messages_proof) - weight(receive_single_message_proof)`.
-	// This won't be super-accurate if message has non-zero dispatch weight, but estimation should
-	// be close enough to real weight.
-	receive_two_messages_proof {
-		let relayer_id_on_source = T::bridged_relayer_id();
-		let relayer_id_on_target = account("relayer", 0, SEED);
-		T::endow_account(&relayer_id_on_target);
-
-		// mark messages 1..=20 as delivered
-		receive_messages::<T, I>(20);
-
+	#[benchmark]
+	fn receive_n_messages_proof(n: Linear<1, { max_msgs::<T, I>() }>) {
+		// setup code
+		let setup = ReceiveMessagesProofSetup::<T, I>::new(n);
 		let (proof, dispatch_weight) = T::prepare_message_proof(MessageProofParams {
 			lane: T::bench_lane_id(),
-			message_nonces: 21..=22,
+			message_nonces: setup.nonces(),
 			outbound_lane_data: None,
 			is_successful_dispatch_expected: false,
-			size: StorageProofSize::Minimal(EXPECTED_DEFAULT_MESSAGE_LENGTH),
+			proof_params: UnverifiedStorageProofParams::from_db_size(
+				EXPECTED_DEFAULT_MESSAGE_LENGTH,
+			),
 		});
-	}: receive_messages_proof(RawOrigin::Signed(relayer_id_on_target), relayer_id_on_source, proof, 2, dispatch_weight)
-	verify {
-		assert_eq!(
-			crate::InboundLanes::<T, I>::get(&T::bench_lane_id()).last_delivered_nonce(),
-			22,
+
+		#[extrinsic_call]
+		receive_messages_proof(
+			RawOrigin::Signed(setup.relayer_id_on_tgt()),
+			setup.relayer_id_on_src(),
+			Box::new(proof),
+			setup.msgs_count,
+			dispatch_weight,
 		);
+
+		// verification code
+		setup.check_last_nonce();
 	}
 
-	// Benchmark `receive_messages_proof` extrinsic with single minimal-weight message and following conditions:
+	// Benchmark `receive_messages_proof` extrinsic with single minimal-weight message and following
+	// conditions:
 	// * proof includes outbound lane state proof;
 	// * inbound lane already has state, so it needs to be read and decoded;
 	// * message is successfully dispatched (reminder: dispatch weight should be minimal);
 	// * message requires all heavy checks done by dispatcher.
 	//
 	// The weight of outbound lane state delivery would be
-	// `weight(receive_single_message_proof_with_outbound_lane_state) - weight(receive_single_message_proof)`.
-	// This won't be super-accurate if message has non-zero dispatch weight, but estimation should
-	// be close enough to real weight.
-	receive_single_message_proof_with_outbound_lane_state {
-		let relayer_id_on_source = T::bridged_relayer_id();
-		let relayer_id_on_target = account("relayer", 0, SEED);
-		T::endow_account(&relayer_id_on_target);
-
-		// mark messages 1..=20 as delivered
-		receive_messages::<T, I>(20);
-
+	// `weight(receive_single_message_proof_with_outbound_lane_state) -
+	// weight(receive_single_message_proof)`. This won't be super-accurate if message has non-zero
+	// dispatch weight, but estimation should be close enough to real weight.
+	#[benchmark]
+	fn receive_single_message_proof_with_outbound_lane_state() {
+		// setup code
+		let setup = ReceiveMessagesProofSetup::<T, I>::new(1);
 		let (proof, dispatch_weight) = T::prepare_message_proof(MessageProofParams {
 			lane: T::bench_lane_id(),
-			message_nonces: 21..=21,
+			message_nonces: setup.nonces(),
 			outbound_lane_data: Some(OutboundLaneData {
-				oldest_unpruned_nonce: 21,
-				latest_received_nonce: 20,
-				latest_generated_nonce: 21,
+				oldest_unpruned_nonce: setup.last_nonce(),
+				latest_received_nonce: ReceiveMessagesProofSetup::<T, I>::LATEST_RECEIVED_NONCE,
+				latest_generated_nonce: setup.last_nonce(),
 			}),
 			is_successful_dispatch_expected: false,
-			size: StorageProofSize::Minimal(EXPECTED_DEFAULT_MESSAGE_LENGTH),
+			proof_params: UnverifiedStorageProofParams::from_db_size(
+				EXPECTED_DEFAULT_MESSAGE_LENGTH,
+			),
 		});
-	}: receive_messages_proof(RawOrigin::Signed(relayer_id_on_target), relayer_id_on_source, proof, 1, dispatch_weight)
-	verify {
-		let lane_state = crate::InboundLanes::<T, I>::get(&T::bench_lane_id());
-		assert_eq!(lane_state.last_delivered_nonce(), 21);
-		assert_eq!(lane_state.last_confirmed_nonce, 20);
-	}
-
-	// Benchmark `receive_messages_proof` extrinsic with single minimal-weight message and following conditions:
-	// * the proof has large leaf with total size of approximately 1KB;
-	// * proof does not include outbound lane state proof;
-	// * inbound lane already has state, so it needs to be read and decoded;
-	// * message is dispatched (reminder: dispatch weight should be minimal);
-	// * message requires all heavy checks done by dispatcher.
-	//
-	// With single KB of messages proof, the weight of the call is increased (roughly) by
-	// `(receive_single_message_proof_16KB - receive_single_message_proof_1_kb) / 15`.
-	receive_single_message_proof_1_kb {
-		let relayer_id_on_source = T::bridged_relayer_id();
-		let relayer_id_on_target = account("relayer", 0, SEED);
-		T::endow_account(&relayer_id_on_target);
-
-		// mark messages 1..=20 as delivered
-		receive_messages::<T, I>(20);
 
-		let (proof, dispatch_weight) = T::prepare_message_proof(MessageProofParams {
-			lane: T::bench_lane_id(),
-			message_nonces: 21..=21,
-			outbound_lane_data: None,
-			is_successful_dispatch_expected: false,
-			size: StorageProofSize::HasLargeLeaf(1024),
-		});
-	}: receive_messages_proof(RawOrigin::Signed(relayer_id_on_target), relayer_id_on_source, proof, 1, dispatch_weight)
-	verify {
-		assert_eq!(
-			crate::InboundLanes::<T, I>::get(&T::bench_lane_id()).last_delivered_nonce(),
-			21,
+		#[extrinsic_call]
+		receive_messages_proof(
+			RawOrigin::Signed(setup.relayer_id_on_tgt()),
+			setup.relayer_id_on_src(),
+			Box::new(proof),
+			setup.msgs_count,
+			dispatch_weight,
 		);
+
+		// verification code
+		setup.check_last_nonce();
 	}
 
-	// Benchmark `receive_messages_proof` extrinsic with single minimal-weight message and following conditions:
-	// * the proof has large leaf with total size of approximately 16KB;
+	// Benchmark `receive_messages_proof` extrinsic with single minimal-weight message and following
+	// conditions:
+	// * the proof has large leaf with total size ranging between 1KB and 16KB;
 	// * proof does not include outbound lane state proof;
 	// * inbound lane already has state, so it needs to be read and decoded;
 	// * message is dispatched (reminder: dispatch weight should be minimal);
 	// * message requires all heavy checks done by dispatcher.
-	//
-	// Size of proof grows because it contains extra trie nodes in it.
-	//
-	// With single KB of messages proof, the weight of the call is increased (roughly) by
-	// `(receive_single_message_proof_16KB - receive_single_message_proof) / 15`.
-	receive_single_message_proof_16_kb {
-		let relayer_id_on_source = T::bridged_relayer_id();
-		let relayer_id_on_target = account("relayer", 0, SEED);
-		T::endow_account(&relayer_id_on_target);
-
-		// mark messages 1..=20 as delivered
-		receive_messages::<T, I>(20);
-
+	#[benchmark]
+	fn receive_single_n_bytes_message_proof(
+		/// Proof size in KB
+		n: Linear<1, { 16 * 1024 }>,
+	) {
+		// setup code
+		let setup = ReceiveMessagesProofSetup::<T, I>::new(1);
 		let (proof, dispatch_weight) = T::prepare_message_proof(MessageProofParams {
 			lane: T::bench_lane_id(),
-			message_nonces: 21..=21,
+			message_nonces: setup.nonces(),
 			outbound_lane_data: None,
 			is_successful_dispatch_expected: false,
-			size: StorageProofSize::HasLargeLeaf(16 * 1024),
+			proof_params: UnverifiedStorageProofParams::from_db_size(n),
 		});
-	}: receive_messages_proof(RawOrigin::Signed(relayer_id_on_target), relayer_id_on_source, proof, 1, dispatch_weight)
-	verify {
-		assert_eq!(
-			crate::InboundLanes::<T, I>::get(&T::bench_lane_id()).last_delivered_nonce(),
-			21,
+
+		#[extrinsic_call]
+		receive_messages_proof(
+			RawOrigin::Signed(setup.relayer_id_on_tgt()),
+			setup.relayer_id_on_src(),
+			Box::new(proof),
+			setup.msgs_count,
+			dispatch_weight,
 		);
+
+		// verification code
+		setup.check_last_nonce();
 	}
 
 	// Benchmark `receive_messages_delivery_proof` extrinsic with following conditions:
@@ -284,7 +340,8 @@ benchmarks_instance_pallet! {
 	// * relayer account does not exist (in practice it needs to exist in production environment).
 	//
 	// This is base benchmark for all other confirmations delivery benchmarks.
-	receive_delivery_proof_for_single_message {
+	#[benchmark]
+	fn receive_delivery_proof_for_single_message() {
 		let relayer_id: T::AccountId = account("relayer", 0, SEED);
 
 		// send message that we're going to confirm
@@ -302,13 +359,21 @@ benchmarks_instance_pallet! {
 				relayers: vec![UnrewardedRelayer {
 					relayer: relayer_id.clone(),
 					messages: DeliveredMessages::new(1),
-				}].into_iter().collect(),
+				}]
+				.into_iter()
+				.collect(),
 				last_confirmed_nonce: 0,
 			},
-			size: StorageProofSize::Minimal(0),
+			proof_params: UnverifiedStorageProofParams::default(),
 		});
-	}: receive_messages_delivery_proof(RawOrigin::Signed(relayer_id.clone()), proof, relayers_state)
-	verify {
+
+		#[extrinsic_call]
+		receive_messages_delivery_proof(
+			RawOrigin::Signed(relayer_id.clone()),
+			proof,
+			relayers_state,
+		);
+
 		assert_eq!(OutboundLanes::<T, I>::get(T::bench_lane_id()).latest_received_nonce, 1);
 		assert!(T::is_relayer_rewarded(&relayer_id));
 	}
@@ -320,7 +385,8 @@ benchmarks_instance_pallet! {
 	// Additional weight for paying single-message reward to the same relayer could be computed
 	// as `weight(receive_delivery_proof_for_two_messages_by_single_relayer)
 	//   - weight(receive_delivery_proof_for_single_message)`.
-	receive_delivery_proof_for_two_messages_by_single_relayer {
+	#[benchmark]
+	fn receive_delivery_proof_for_two_messages_by_single_relayer() {
 		let relayer_id: T::AccountId = account("relayer", 0, SEED);
 
 		// send message that we're going to confirm
@@ -341,13 +407,21 @@ benchmarks_instance_pallet! {
 				relayers: vec![UnrewardedRelayer {
 					relayer: relayer_id.clone(),
 					messages: delivered_messages,
-				}].into_iter().collect(),
+				}]
+				.into_iter()
+				.collect(),
 				last_confirmed_nonce: 0,
 			},
-			size: StorageProofSize::Minimal(0),
+			proof_params: UnverifiedStorageProofParams::default(),
 		});
-	}: receive_messages_delivery_proof(RawOrigin::Signed(relayer_id.clone()), proof, relayers_state)
-	verify {
+
+		#[extrinsic_call]
+		receive_messages_delivery_proof(
+			RawOrigin::Signed(relayer_id.clone()),
+			proof,
+			relayers_state,
+		);
+
 		assert_eq!(OutboundLanes::<T, I>::get(T::bench_lane_id()).latest_received_nonce, 2);
 		assert!(T::is_relayer_rewarded(&relayer_id));
 	}
@@ -359,7 +433,8 @@ benchmarks_instance_pallet! {
 	// Additional weight for paying reward to the next relayer could be computed
 	// as `weight(receive_delivery_proof_for_two_messages_by_two_relayers)
 	//   - weight(receive_delivery_proof_for_two_messages_by_single_relayer)`.
-	receive_delivery_proof_for_two_messages_by_two_relayers {
+	#[benchmark]
+	fn receive_delivery_proof_for_two_messages_by_two_relayers() {
 		let relayer1_id: T::AccountId = account("relayer1", 1, SEED);
 		let relayer2_id: T::AccountId = account("relayer2", 2, SEED);
 
@@ -385,13 +460,21 @@ benchmarks_instance_pallet! {
 						relayer: relayer2_id.clone(),
 						messages: DeliveredMessages::new(2),
 					},
-				].into_iter().collect(),
+				]
+				.into_iter()
+				.collect(),
 				last_confirmed_nonce: 0,
 			},
-			size: StorageProofSize::Minimal(0),
+			proof_params: UnverifiedStorageProofParams::default(),
 		});
-	}: receive_messages_delivery_proof(RawOrigin::Signed(relayer1_id.clone()), proof, relayers_state)
-	verify {
+
+		#[extrinsic_call]
+		receive_messages_delivery_proof(
+			RawOrigin::Signed(relayer1_id.clone()),
+			proof,
+			relayers_state,
+		);
+
 		assert_eq!(OutboundLanes::<T, I>::get(T::bench_lane_id()).latest_received_nonce, 2);
 		assert!(T::is_relayer_rewarded(&relayer1_id));
 		assert!(T::is_relayer_rewarded(&relayer2_id));
@@ -411,51 +494,38 @@ benchmarks_instance_pallet! {
 	// * inbound lane already has state, so it needs to be read and decoded;
 	// * message is **SUCCESSFULLY** dispatched;
 	// * message requires all heavy checks done by dispatcher.
-	receive_single_message_proof_with_dispatch {
-		// maybe dispatch weight relies on the message size too?
-		let i in EXPECTED_DEFAULT_MESSAGE_LENGTH .. EXPECTED_DEFAULT_MESSAGE_LENGTH * 16;
-
-		let relayer_id_on_source = T::bridged_relayer_id();
-		let relayer_id_on_target = account("relayer", 0, SEED);
-		T::endow_account(&relayer_id_on_target);
-
-		// mark messages 1..=20 as delivered
-		receive_messages::<T, I>(20);
-
+	#[benchmark]
+	fn receive_single_n_bytes_message_proof_with_dispatch(
+		/// Proof size in KB
+		n: Linear<1, { 16 * 1024 }>,
+	) {
+		// setup code
+		let setup = ReceiveMessagesProofSetup::<T, I>::new(1);
 		let (proof, dispatch_weight) = T::prepare_message_proof(MessageProofParams {
 			lane: T::bench_lane_id(),
-			message_nonces: 21..=21,
+			message_nonces: setup.nonces(),
 			outbound_lane_data: None,
 			is_successful_dispatch_expected: true,
-			size: StorageProofSize::Minimal(i),
+			proof_params: UnverifiedStorageProofParams::from_db_size(n),
 		});
-	}: receive_messages_proof(RawOrigin::Signed(relayer_id_on_target), relayer_id_on_source, proof, 1, dispatch_weight)
-	verify {
-		assert_eq!(
-			crate::InboundLanes::<T, I>::get(&T::bench_lane_id()).last_delivered_nonce(),
-			21,
-		);
-		assert!(T::is_message_successfully_dispatched(21));
-	}
 
-	impl_benchmark_test_suite!(Pallet, crate::mock::new_test_ext(), crate::mock::TestRuntime)
-}
+		#[extrinsic_call]
+		receive_messages_proof(
+			RawOrigin::Signed(setup.relayer_id_on_tgt()),
+			setup.relayer_id_on_src(),
+			Box::new(proof),
+			setup.msgs_count,
+			dispatch_weight,
+		);
 
-fn send_regular_message<T: Config<I>, I: 'static>() {
-	let mut outbound_lane = outbound_lane::<T, I>(T::bench_lane_id());
-	outbound_lane.send_message(BoundedVec::try_from(vec![]).expect("We craft valid messages"));
-}
+		// verification code
+		setup.check_last_nonce();
+		assert!(T::is_message_successfully_dispatched(setup.last_nonce()));
+	}
 
-fn receive_messages<T: Config<I>, I: 'static>(nonce: MessageNonce) {
-	let mut inbound_lane_storage =
-		RuntimeInboundLaneStorage::<T, I>::from_lane_id(T::bench_lane_id());
-	inbound_lane_storage.set_data(InboundLaneData {
-		relayers: vec![UnrewardedRelayer {
-			relayer: T::bridged_relayer_id(),
-			messages: DeliveredMessages::new(nonce),
-		}]
-		.into_iter()
-		.collect(),
-		last_confirmed_nonce: 0,
-	});
+	impl_benchmark_test_suite!(
+		Pallet,
+		crate::tests::mock::new_test_ext(),
+		crate::tests::mock::TestRuntime
+	);
 }
diff --git a/bridges/modules/messages/src/inbound_lane.rs b/bridges/modules/messages/src/inbound_lane.rs
index da1698e6e03..7ef4599a93c 100644
--- a/bridges/modules/messages/src/inbound_lane.rs
+++ b/bridges/modules/messages/src/inbound_lane.rs
@@ -16,15 +16,15 @@
 
 //! Everything about incoming messages receival.
 
-use crate::Config;
+use crate::{BridgedChainOf, Config};
 
 use bp_messages::{
 	target_chain::{DispatchMessage, DispatchMessageData, MessageDispatch},
-	DeliveredMessages, InboundLaneData, LaneId, MessageKey, MessageNonce, OutboundLaneData,
-	ReceptionResult, UnrewardedRelayer,
+	ChainWithMessages, DeliveredMessages, InboundLaneData, LaneId, MessageKey, MessageNonce,
+	OutboundLaneData, ReceptionResult, UnrewardedRelayer,
 };
+use bp_runtime::AccountIdOf;
 use codec::{Decode, Encode, EncodeLike, MaxEncodedLen};
-use frame_support::traits::Get;
 use scale_info::{Type, TypeInfo};
 use sp_runtime::RuntimeDebug;
 use sp_std::prelude::PartialEq;
@@ -55,10 +55,12 @@ pub trait InboundLaneStorage {
 ///
 /// The encoding of this type matches encoding of the corresponding `MessageData`.
 #[derive(Encode, Decode, Clone, RuntimeDebug, PartialEq, Eq)]
-pub struct StoredInboundLaneData<T: Config<I>, I: 'static>(pub InboundLaneData<T::InboundRelayer>);
+pub struct StoredInboundLaneData<T: Config<I>, I: 'static>(
+	pub InboundLaneData<AccountIdOf<BridgedChainOf<T, I>>>,
+);
 
 impl<T: Config<I>, I: 'static> sp_std::ops::Deref for StoredInboundLaneData<T, I> {
-	type Target = InboundLaneData<T::InboundRelayer>;
+	type Target = InboundLaneData<AccountIdOf<BridgedChainOf<T, I>>>;
 
 	fn deref(&self) -> &Self::Target {
 		&self.0
@@ -78,7 +80,7 @@ impl<T: Config<I>, I: 'static> Default for StoredInboundLaneData<T, I> {
 }
 
 impl<T: Config<I>, I: 'static> From<StoredInboundLaneData<T, I>>
-	for InboundLaneData<T::InboundRelayer>
+	for InboundLaneData<AccountIdOf<BridgedChainOf<T, I>>>
 {
 	fn from(data: StoredInboundLaneData<T, I>) -> Self {
 		data.0
@@ -86,7 +88,7 @@ impl<T: Config<I>, I: 'static> From<StoredInboundLaneData<T, I>>
 }
 
 impl<T: Config<I>, I: 'static> EncodeLike<StoredInboundLaneData<T, I>>
-	for InboundLaneData<T::InboundRelayer>
+	for InboundLaneData<AccountIdOf<BridgedChainOf<T, I>>>
 {
 }
 
@@ -94,14 +96,14 @@ impl<T: Config<I>, I: 'static> TypeInfo for StoredInboundLaneData<T, I> {
 	type Identity = Self;
 
 	fn type_info() -> Type {
-		InboundLaneData::<T::InboundRelayer>::type_info()
+		InboundLaneData::<AccountIdOf<BridgedChainOf<T, I>>>::type_info()
 	}
 }
 
 impl<T: Config<I>, I: 'static> MaxEncodedLen for StoredInboundLaneData<T, I> {
 	fn max_encoded_len() -> usize {
-		InboundLaneData::<T::InboundRelayer>::encoded_size_hint(
-			T::MaxUnrewardedRelayerEntriesAtInboundLane::get() as usize,
+		InboundLaneData::<AccountIdOf<BridgedChainOf<T, I>>>::encoded_size_hint(
+			BridgedChainOf::<T, I>::MAX_UNREWARDED_RELAYERS_IN_CONFIRMATION_TX as usize,
 		)
 		.unwrap_or(usize::MAX)
 	}
@@ -216,10 +218,10 @@ mod tests {
 	use super::*;
 	use crate::{
 		inbound_lane,
-		mock::{
+		tests::mock::{
 			dispatch_result, inbound_message_data, inbound_unrewarded_relayers_state, run_test,
-			unrewarded_relayer, TestMessageDispatch, TestRuntime, REGULAR_PAYLOAD, TEST_LANE_ID,
-			TEST_RELAYER_A, TEST_RELAYER_B, TEST_RELAYER_C,
+			unrewarded_relayer, BridgedChain, TestMessageDispatch, TestRuntime, REGULAR_PAYLOAD,
+			TEST_LANE_ID, TEST_RELAYER_A, TEST_RELAYER_B, TEST_RELAYER_C,
 		},
 		RuntimeInboundLaneStorage,
 	};
@@ -372,8 +374,7 @@ mod tests {
 	fn fails_to_receive_messages_above_unrewarded_relayer_entries_limit_per_lane() {
 		run_test(|| {
 			let mut lane = inbound_lane::<TestRuntime, _>(TEST_LANE_ID);
-			let max_nonce =
-				<TestRuntime as Config>::MaxUnrewardedRelayerEntriesAtInboundLane::get();
+			let max_nonce = BridgedChain::MAX_UNREWARDED_RELAYERS_IN_CONFIRMATION_TX;
 			for current_nonce in 1..max_nonce + 1 {
 				assert_eq!(
 					lane.receive_message::<TestMessageDispatch>(
@@ -409,7 +410,7 @@ mod tests {
 	fn fails_to_receive_messages_above_unconfirmed_messages_limit_per_lane() {
 		run_test(|| {
 			let mut lane = inbound_lane::<TestRuntime, _>(TEST_LANE_ID);
-			let max_nonce = <TestRuntime as Config>::MaxUnconfirmedMessagesAtInboundLane::get();
+			let max_nonce = BridgedChain::MAX_UNCONFIRMED_MESSAGES_IN_CONFIRMATION_TX;
 			for current_nonce in 1..=max_nonce {
 				assert_eq!(
 					lane.receive_message::<TestMessageDispatch>(
diff --git a/bridges/modules/messages/src/lib.rs b/bridges/modules/messages/src/lib.rs
index e31a4542056..bf105b14040 100644
--- a/bridges/modules/messages/src/lib.rs
+++ b/bridges/modules/messages/src/lib.rs
@@ -41,8 +41,8 @@ pub use outbound_lane::StoredMessagePayload;
 pub use weights::WeightInfo;
 pub use weights_ext::{
 	ensure_able_to_receive_confirmation, ensure_able_to_receive_message,
-	ensure_weights_are_correct, WeightInfoExt, EXPECTED_DEFAULT_MESSAGE_LENGTH,
-	EXTRA_STORAGE_PROOF_SIZE,
+	ensure_maximal_message_dispatch, ensure_weights_are_correct, WeightInfoExt,
+	EXPECTED_DEFAULT_MESSAGE_LENGTH, EXTRA_STORAGE_PROOF_SIZE,
 };
 
 use crate::{
@@ -50,20 +50,23 @@ use crate::{
 	outbound_lane::{OutboundLane, OutboundLaneStorage, ReceptionConfirmationError},
 };
 
+use bp_header_chain::HeaderChain;
 use bp_messages::{
 	source_chain::{
-		DeliveryConfirmationPayments, OnMessagesDelivered, SendMessageArtifacts, TargetHeaderChain,
+		DeliveryConfirmationPayments, FromBridgedChainMessagesDeliveryProof, OnMessagesDelivered,
+		SendMessageArtifacts,
 	},
 	target_chain::{
-		DeliveryPayments, DispatchMessage, MessageDispatch, ProvedLaneMessages, ProvedMessages,
-		SourceHeaderChain,
+		DeliveryPayments, DispatchMessage, FromBridgedChainMessagesProof, MessageDispatch,
+		ProvedLaneMessages, ProvedMessages,
 	},
-	DeliveredMessages, InboundLaneData, InboundMessageDetails, LaneId, MessageKey, MessageNonce,
-	MessagePayload, MessagesOperatingMode, OutboundLaneData, OutboundMessageDetails,
-	UnrewardedRelayersState, VerificationError,
+	ChainWithMessages, DeliveredMessages, InboundLaneData, InboundMessageDetails, LaneId,
+	MessageKey, MessageNonce, MessagePayload, MessagesOperatingMode, OutboundLaneData,
+	OutboundMessageDetails, UnrewardedRelayersState, VerificationError,
 };
 use bp_runtime::{
-	BasicOperatingMode, ChainId, OwnedBridgeModule, PreComputedSize, RangeInclusiveExt, Size,
+	AccountIdOf, BasicOperatingMode, HashOf, OwnedBridgeModule, PreComputedSize, RangeInclusiveExt,
+	Size,
 };
 use codec::{Decode, Encode, MaxEncodedLen};
 use frame_support::{dispatch::PostDispatchInfo, ensure, fail, traits::Get, DefaultNoBound};
@@ -72,6 +75,8 @@ use sp_std::{marker::PhantomData, prelude::*};
 
 mod inbound_lane;
 mod outbound_lane;
+mod proofs;
+mod tests;
 mod weights_ext;
 
 pub mod weights;
@@ -79,10 +84,9 @@ pub mod weights;
 #[cfg(feature = "runtime-benchmarks")]
 pub mod benchmarking;
 
-#[cfg(test)]
-mod mock;
-
 pub use pallet::*;
+#[cfg(feature = "test-helpers")]
+pub use tests::*;
 
 /// The target that will be used when publishing logs related to this pallet.
 pub const LOG_TARGET: &str = "runtime::bridge-messages";
@@ -105,76 +109,39 @@ pub mod pallet {
 		/// Benchmarks results from runtime we're plugged into.
 		type WeightInfo: WeightInfoExt;
 
-		/// Gets the chain id value from the instance.
-		#[pallet::constant]
-		type BridgedChainId: Get<ChainId>;
+		/// This chain type.
+		type ThisChain: ChainWithMessages;
+		/// Bridged chain type.
+		type BridgedChain: ChainWithMessages;
+		/// Bridged chain headers provider.
+		type BridgedHeaderChain: HeaderChain<Self::BridgedChain>;
 
 		/// Get all active outbound lanes that the message pallet is serving.
 		type ActiveOutboundLanes: Get<&'static [LaneId]>;
-		/// Maximal number of unrewarded relayer entries at inbound lane. Unrewarded means that the
-		/// relayer has delivered messages, but either confirmations haven't been delivered back to
-		/// the source chain, or we haven't received reward confirmations yet.
-		///
-		/// This constant limits maximal number of entries in the `InboundLaneData::relayers`. Keep
-		/// in mind that the same relayer account may take several (non-consecutive) entries in this
-		/// set.
-		type MaxUnrewardedRelayerEntriesAtInboundLane: Get<MessageNonce>;
-		/// Maximal number of unconfirmed messages at inbound lane. Unconfirmed means that the
-		/// message has been delivered, but either confirmations haven't been delivered back to the
-		/// source chain, or we haven't received reward confirmations for these messages yet.
-		///
-		/// This constant limits difference between last message from last entry of the
-		/// `InboundLaneData::relayers` and first message at the first entry.
-		///
-		/// There is no point of making this parameter lesser than
-		/// MaxUnrewardedRelayerEntriesAtInboundLane, because then maximal number of relayer entries
-		/// will be limited by maximal number of messages.
-		///
-		/// This value also represents maximal number of messages in single delivery transaction.
-		/// Transaction that is declaring more messages than this value, will be rejected. Even if
-		/// these messages are from different lanes.
-		type MaxUnconfirmedMessagesAtInboundLane: Get<MessageNonce>;
-
-		/// Maximal encoded size of the outbound payload.
-		#[pallet::constant]
-		type MaximalOutboundPayloadSize: Get<u32>;
+
 		/// Payload type of outbound messages. This payload is dispatched on the bridged chain.
 		type OutboundPayload: Parameter + Size;
-
 		/// Payload type of inbound messages. This payload is dispatched on this chain.
 		type InboundPayload: Decode;
-		/// Identifier of relayer that deliver messages to this chain. Relayer reward is paid on the
-		/// bridged chain.
-		type InboundRelayer: Parameter + MaxEncodedLen;
-		/// Delivery payments.
-		type DeliveryPayments: DeliveryPayments<Self::AccountId>;
-
-		// Types that are used by outbound_lane (on source chain).
 
-		/// Target header chain.
-		type TargetHeaderChain: TargetHeaderChain<Self::OutboundPayload, Self::AccountId>;
-		/// Delivery confirmation payments.
+		/// 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>;
 		/// Delivery confirmation callback.
 		type OnMessagesDelivered: OnMessagesDelivered;
 
-		// Types that are used by inbound_lane (on target chain).
-
-		/// Source header chain, as it is represented on target chain.
-		type SourceHeaderChain: SourceHeaderChain;
-		/// Message dispatch.
+		/// Message dispatch handler.
 		type MessageDispatch: MessageDispatch<DispatchPayload = Self::InboundPayload>;
 	}
 
-	/// Shortcut to messages proof type for Config.
-	pub type MessagesProofOf<T, I> =
-		<<T as Config<I>>::SourceHeaderChain as SourceHeaderChain>::MessagesProof;
-	/// Shortcut to messages delivery proof type for Config.
-	pub type MessagesDeliveryProofOf<T, I> =
-		<<T as Config<I>>::TargetHeaderChain as TargetHeaderChain<
-			<T as Config<I>>::OutboundPayload,
-			<T as frame_system::Config>::AccountId,
-		>>::MessagesDeliveryProof;
+	/// Shortcut to this chain type for Config.
+	pub type ThisChainOf<T, I> = <T as Config<I>>::ThisChain;
+	/// Shortcut to bridged chain type for Config.
+	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;
 
 	#[pallet::pallet]
 	pub struct Pallet<T, I = ()>(PhantomData<(T, I)>);
@@ -265,11 +232,11 @@ pub mod pallet {
 		/// The call may succeed, but some messages may not be delivered e.g. if they are not fit
 		/// into the unrewarded relayers vector.
 		#[pallet::call_index(2)]
-		#[pallet::weight(T::WeightInfo::receive_messages_proof_weight(proof, *messages_count, *dispatch_weight))]
+		#[pallet::weight(T::WeightInfo::receive_messages_proof_weight(&**proof, *messages_count, *dispatch_weight))]
 		pub fn receive_messages_proof(
 			origin: OriginFor<T>,
-			relayer_id_at_bridged_chain: T::InboundRelayer,
-			proof: MessagesProofOf<T, I>,
+			relayer_id_at_bridged_chain: AccountIdOf<BridgedChainOf<T, I>>,
+			proof: Box<FromBridgedChainMessagesProof<HashOf<BridgedChainOf<T, I>>>>,
 			messages_count: u32,
 			dispatch_weight: Weight,
 		) -> DispatchResultWithPostInfo {
@@ -278,7 +245,8 @@ pub mod pallet {
 
 			// reject transactions that are declaring too many messages
 			ensure!(
-				MessageNonce::from(messages_count) <= T::MaxUnconfirmedMessagesAtInboundLane::get(),
+				MessageNonce::from(messages_count) <=
+					BridgedChainOf::<T, I>::MAX_UNCONFIRMED_MESSAGES_IN_CONFIRMATION_TX,
 				Error::<T, I>::TooManyMessagesInTheProof
 			);
 
@@ -296,22 +264,19 @@ pub mod pallet {
 			// The DeclaredWeight is exactly what's computed here. Unfortunately it is impossible
 			// to get pre-computed value (and it has been already computed by the executive).
 			let declared_weight = T::WeightInfo::receive_messages_proof_weight(
-				&proof,
+				&*proof,
 				messages_count,
 				dispatch_weight,
 			);
 			let mut actual_weight = declared_weight;
 
 			// verify messages proof && convert proof into messages
-			let messages = verify_and_decode_messages_proof::<
-				T::SourceHeaderChain,
-				T::InboundPayload,
-			>(proof, messages_count)
-			.map_err(|err| {
-				log::trace!(target: LOG_TARGET, "Rejecting invalid messages proof: {:?}", err,);
+			let messages = verify_and_decode_messages_proof::<T, I>(*proof, messages_count)
+				.map_err(|err| {
+					log::trace!(target: LOG_TARGET, "Rejecting invalid messages proof: {:?}", err,);
 
-				Error::<T, I>::InvalidMessagesProof
-			})?;
+					Error::<T, I>::InvalidMessagesProof
+				})?;
 
 			// dispatch messages and (optionally) update lane(s) state(s)
 			let mut total_messages = 0;
@@ -424,14 +389,14 @@ pub mod pallet {
 		))]
 		pub fn receive_messages_delivery_proof(
 			origin: OriginFor<T>,
-			proof: MessagesDeliveryProofOf<T, I>,
+			proof: FromBridgedChainMessagesDeliveryProof<HashOf<BridgedChainOf<T, I>>>,
 			mut relayers_state: UnrewardedRelayersState,
 		) -> DispatchResultWithPostInfo {
 			Self::ensure_not_halted().map_err(Error::<T, I>::BridgeModule)?;
 
 			let proof_size = proof.size();
 			let confirmation_relayer = ensure_signed(origin)?;
-			let (lane_id, lane_data) = T::TargetHeaderChain::verify_messages_delivery_proof(proof)
+			let (lane_id, lane_data) = proofs::verify_messages_delivery_proof::<T, I>(proof)
 				.map_err(|err| {
 					log::trace!(
 						target: LOG_TARGET,
@@ -542,8 +507,6 @@ pub mod pallet {
 		InactiveOutboundLane,
 		/// The inbound message dispatcher is inactive.
 		MessageDispatchInactive,
-		/// Message has been treated as invalid by chain verifier.
-		MessageRejectedByChainVerifier(VerificationError),
 		/// Message has been treated as invalid by the pallet logic.
 		MessageRejectedByPallet(VerificationError),
 		/// Submitter has failed to pay fee for delivering and dispatching messages.
@@ -674,7 +637,9 @@ pub mod pallet {
 		}
 
 		/// Return inbound lane data.
-		pub fn inbound_lane_data(lane: LaneId) -> InboundLaneData<T::InboundRelayer> {
+		pub fn inbound_lane_data(
+			lane: LaneId,
+		) -> InboundLaneData<AccountIdOf<BridgedChainOf<T, I>>> {
 			InboundLanes::<T, I>::get(lane).0
 		}
 	}
@@ -714,18 +679,6 @@ where
 		// let's check if outbound lane is active
 		ensure!(T::ActiveOutboundLanes::get().contains(&lane), Error::<T, I>::InactiveOutboundLane);
 
-		// let's first check if message can be delivered to target chain
-		T::TargetHeaderChain::verify_message(message).map_err(|err| {
-			log::trace!(
-				target: LOG_TARGET,
-				"Message to lane {:?} is rejected by target chain: {:?}",
-				lane,
-				err,
-			);
-
-			Error::<T, I>::MessageRejectedByChainVerifier(err)
-		})?;
-
 		Ok(SendMessageArgs {
 			lane_id: lane,
 			payload: StoredMessagePayload::<T, I>::try_from(message.encode()).map_err(|_| {
@@ -785,7 +738,7 @@ fn outbound_lane<T: Config<I>, I: 'static>(
 /// Runtime inbound lane storage.
 struct RuntimeInboundLaneStorage<T: Config<I>, I: 'static = ()> {
 	lane_id: LaneId,
-	cached_data: Option<InboundLaneData<T::InboundRelayer>>,
+	cached_data: Option<InboundLaneData<AccountIdOf<BridgedChainOf<T, I>>>>,
 	_phantom: PhantomData<I>,
 }
 
@@ -802,39 +755,39 @@ impl<T: Config<I>, I: 'static> RuntimeInboundLaneStorage<T, I> {
 	/// maximal configured.
 	///
 	/// Maximal inbound lane state set size is configured by the
-	/// `MaxUnrewardedRelayerEntriesAtInboundLane` constant from the pallet configuration. The PoV
+	/// `MAX_UNREWARDED_RELAYERS_IN_CONFIRMATION_TX` constant from the pallet configuration. The PoV
 	/// of the call includes the maximal size of inbound lane state. If the actual size is smaller,
 	/// we may subtract extra bytes from this component.
 	pub fn extra_proof_size_bytes(&mut self) -> u64 {
 		let max_encoded_len = StoredInboundLaneData::<T, I>::max_encoded_len();
 		let relayers_count = self.get_or_init_data().relayers.len();
 		let actual_encoded_len =
-			InboundLaneData::<T::InboundRelayer>::encoded_size_hint(relayers_count)
+			InboundLaneData::<AccountIdOf<BridgedChainOf<T, I>>>::encoded_size_hint(relayers_count)
 				.unwrap_or(usize::MAX);
 		max_encoded_len.saturating_sub(actual_encoded_len) as _
 	}
 }
 
 impl<T: Config<I>, I: 'static> InboundLaneStorage for RuntimeInboundLaneStorage<T, I> {
-	type Relayer = T::InboundRelayer;
+	type Relayer = AccountIdOf<BridgedChainOf<T, I>>;
 
 	fn id(&self) -> LaneId {
 		self.lane_id
 	}
 
 	fn max_unrewarded_relayer_entries(&self) -> MessageNonce {
-		T::MaxUnrewardedRelayerEntriesAtInboundLane::get()
+		BridgedChainOf::<T, I>::MAX_UNREWARDED_RELAYERS_IN_CONFIRMATION_TX
 	}
 
 	fn max_unconfirmed_messages(&self) -> MessageNonce {
-		T::MaxUnconfirmedMessagesAtInboundLane::get()
+		BridgedChainOf::<T, I>::MAX_UNCONFIRMED_MESSAGES_IN_CONFIRMATION_TX
 	}
 
-	fn get_or_init_data(&mut self) -> InboundLaneData<T::InboundRelayer> {
+	fn get_or_init_data(&mut self) -> InboundLaneData<AccountIdOf<BridgedChainOf<T, I>>> {
 		match self.cached_data {
 			Some(ref data) => data.clone(),
 			None => {
-				let data: InboundLaneData<T::InboundRelayer> =
+				let data: InboundLaneData<AccountIdOf<BridgedChainOf<T, I>>> =
 					InboundLanes::<T, I>::get(self.lane_id).into();
 				self.cached_data = Some(data.clone());
 				data
@@ -842,7 +795,7 @@ impl<T: Config<I>, I: 'static> InboundLaneStorage for RuntimeInboundLaneStorage<
 		}
 	}
 
-	fn set_data(&mut self, data: InboundLaneData<T::InboundRelayer>) {
+	fn set_data(&mut self, data: InboundLaneData<AccountIdOf<BridgedChainOf<T, I>>>) {
 		self.cached_data = Some(data.clone());
 		InboundLanes::<T, I>::insert(self.lane_id, StoredInboundLaneData::<T, I>(data))
 	}
@@ -887,14 +840,14 @@ impl<T: Config<I>, I: 'static> OutboundLaneStorage for RuntimeOutboundLaneStorag
 }
 
 /// Verify messages proof and return proved messages with decoded payload.
-fn verify_and_decode_messages_proof<Chain: SourceHeaderChain, DispatchPayload: Decode>(
-	proof: Chain::MessagesProof,
+fn verify_and_decode_messages_proof<T: Config<I>, I: 'static>(
+	proof: FromBridgedChainMessagesProof<HashOf<BridgedChainOf<T, I>>>,
 	messages_count: u32,
-) -> Result<ProvedMessages<DispatchMessage<DispatchPayload>>, VerificationError> {
-	// `receive_messages_proof` weight formula and `MaxUnconfirmedMessagesAtInboundLane` check
-	// guarantees that the `message_count` is sane and Vec<Message> may be allocated.
+) -> Result<ProvedMessages<DispatchMessage<T::InboundPayload>>, 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)
-	Chain::verify_messages_proof(proof, messages_count).map(|messages_by_lane| {
+	proofs::verify_messages_proof::<T, I>(proof, messages_count).map(|messages_by_lane| {
 		messages_by_lane
 			.into_iter()
 			.map(|(lane, lane_data)| {
@@ -909,1209 +862,3 @@ fn verify_and_decode_messages_proof<Chain: SourceHeaderChain, DispatchPayload: D
 			.collect()
 	})
 }
-
-#[cfg(test)]
-mod tests {
-	use super::*;
-	use crate::{
-		mock::{
-			inbound_unrewarded_relayers_state, message, message_payload, run_test,
-			unrewarded_relayer, AccountId, DbWeight, RuntimeEvent as TestEvent, RuntimeOrigin,
-			TestDeliveryConfirmationPayments, TestDeliveryPayments, TestMessageDispatch,
-			TestMessagesDeliveryProof, TestMessagesProof, TestOnMessagesDelivered, TestRelayer,
-			TestRuntime, TestWeightInfo, MAX_OUTBOUND_PAYLOAD_SIZE,
-			PAYLOAD_REJECTED_BY_TARGET_CHAIN, REGULAR_PAYLOAD, TEST_LANE_ID, TEST_LANE_ID_2,
-			TEST_LANE_ID_3, TEST_RELAYER_A, TEST_RELAYER_B,
-		},
-		outbound_lane::ReceptionConfirmationError,
-	};
-	use bp_messages::{
-		source_chain::MessagesBridge, BridgeMessagesCall, UnrewardedRelayer,
-		UnrewardedRelayersState,
-	};
-	use bp_test_utils::generate_owned_bridge_module_tests;
-	use frame_support::{
-		assert_noop, assert_ok,
-		dispatch::Pays,
-		storage::generator::{StorageMap, StorageValue},
-		traits::Hooks,
-		weights::Weight,
-	};
-	use frame_system::{EventRecord, Pallet as System, Phase};
-	use sp_runtime::DispatchError;
-
-	fn get_ready_for_events() {
-		System::<TestRuntime>::set_block_number(1);
-		System::<TestRuntime>::reset_events();
-	}
-
-	fn send_regular_message(lane_id: LaneId) {
-		get_ready_for_events();
-
-		let outbound_lane = outbound_lane::<TestRuntime, ()>(lane_id);
-		let message_nonce = outbound_lane.data().latest_generated_nonce + 1;
-		let prev_enqueued_messages = outbound_lane.data().queued_messages().saturating_len();
-		let valid_message = Pallet::<TestRuntime, ()>::validate_message(lane_id, &REGULAR_PAYLOAD)
-			.expect("validate_message has failed");
-		let artifacts = Pallet::<TestRuntime, ()>::send_message(valid_message);
-		assert_eq!(artifacts.enqueued_messages, prev_enqueued_messages + 1);
-
-		// check event with assigned nonce
-		assert_eq!(
-			System::<TestRuntime>::events(),
-			vec![EventRecord {
-				phase: Phase::Initialization,
-				event: TestEvent::Messages(Event::MessageAccepted {
-					lane_id,
-					nonce: message_nonce
-				}),
-				topics: vec![],
-			}],
-		);
-	}
-
-	fn receive_messages_delivery_proof() {
-		System::<TestRuntime>::set_block_number(1);
-		System::<TestRuntime>::reset_events();
-
-		assert_ok!(Pallet::<TestRuntime>::receive_messages_delivery_proof(
-			RuntimeOrigin::signed(1),
-			TestMessagesDeliveryProof(Ok((
-				TEST_LANE_ID,
-				InboundLaneData {
-					last_confirmed_nonce: 1,
-					relayers: vec![UnrewardedRelayer {
-						relayer: 0,
-						messages: DeliveredMessages::new(1),
-					}]
-					.into_iter()
-					.collect(),
-				},
-			))),
-			UnrewardedRelayersState {
-				unrewarded_relayer_entries: 1,
-				messages_in_oldest_entry: 1,
-				total_messages: 1,
-				last_delivered_nonce: 1,
-			},
-		));
-
-		assert_eq!(
-			System::<TestRuntime>::events(),
-			vec![EventRecord {
-				phase: Phase::Initialization,
-				event: TestEvent::Messages(Event::MessagesDelivered {
-					lane_id: TEST_LANE_ID,
-					messages: DeliveredMessages::new(1),
-				}),
-				topics: vec![],
-			}],
-		);
-	}
-
-	#[test]
-	fn pallet_rejects_transactions_if_halted() {
-		run_test(|| {
-			// send message first to be able to check that delivery_proof fails later
-			send_regular_message(TEST_LANE_ID);
-
-			PalletOperatingMode::<TestRuntime, ()>::put(MessagesOperatingMode::Basic(
-				BasicOperatingMode::Halted,
-			));
-
-			assert_noop!(
-				Pallet::<TestRuntime, ()>::validate_message(TEST_LANE_ID, &REGULAR_PAYLOAD),
-				Error::<TestRuntime, ()>::NotOperatingNormally,
-			);
-
-			assert_noop!(
-				Pallet::<TestRuntime>::receive_messages_proof(
-					RuntimeOrigin::signed(1),
-					TEST_RELAYER_A,
-					Ok(vec![message(2, REGULAR_PAYLOAD)]).into(),
-					1,
-					REGULAR_PAYLOAD.declared_weight,
-				),
-				Error::<TestRuntime, ()>::BridgeModule(bp_runtime::OwnedBridgeModuleError::Halted),
-			);
-
-			assert_noop!(
-				Pallet::<TestRuntime>::receive_messages_delivery_proof(
-					RuntimeOrigin::signed(1),
-					TestMessagesDeliveryProof(Ok((
-						TEST_LANE_ID,
-						InboundLaneData {
-							last_confirmed_nonce: 1,
-							relayers: vec![unrewarded_relayer(1, 1, TEST_RELAYER_A)]
-								.into_iter()
-								.collect(),
-						},
-					))),
-					UnrewardedRelayersState {
-						unrewarded_relayer_entries: 1,
-						messages_in_oldest_entry: 1,
-						total_messages: 1,
-						last_delivered_nonce: 1,
-					},
-				),
-				Error::<TestRuntime, ()>::BridgeModule(bp_runtime::OwnedBridgeModuleError::Halted),
-			);
-		});
-	}
-
-	#[test]
-	fn pallet_rejects_new_messages_in_rejecting_outbound_messages_operating_mode() {
-		run_test(|| {
-			// send message first to be able to check that delivery_proof fails later
-			send_regular_message(TEST_LANE_ID);
-
-			PalletOperatingMode::<TestRuntime, ()>::put(
-				MessagesOperatingMode::RejectingOutboundMessages,
-			);
-
-			assert_noop!(
-				Pallet::<TestRuntime, ()>::validate_message(TEST_LANE_ID, &REGULAR_PAYLOAD),
-				Error::<TestRuntime, ()>::NotOperatingNormally,
-			);
-
-			assert_ok!(Pallet::<TestRuntime>::receive_messages_proof(
-				RuntimeOrigin::signed(1),
-				TEST_RELAYER_A,
-				Ok(vec![message(1, REGULAR_PAYLOAD)]).into(),
-				1,
-				REGULAR_PAYLOAD.declared_weight,
-			),);
-
-			assert_ok!(Pallet::<TestRuntime>::receive_messages_delivery_proof(
-				RuntimeOrigin::signed(1),
-				TestMessagesDeliveryProof(Ok((
-					TEST_LANE_ID,
-					InboundLaneData {
-						last_confirmed_nonce: 1,
-						relayers: vec![unrewarded_relayer(1, 1, TEST_RELAYER_A)]
-							.into_iter()
-							.collect(),
-					},
-				))),
-				UnrewardedRelayersState {
-					unrewarded_relayer_entries: 1,
-					messages_in_oldest_entry: 1,
-					total_messages: 1,
-					last_delivered_nonce: 1,
-				},
-			));
-		});
-	}
-
-	#[test]
-	fn send_message_works() {
-		run_test(|| {
-			send_regular_message(TEST_LANE_ID);
-		});
-	}
-
-	#[test]
-	fn send_message_rejects_too_large_message() {
-		run_test(|| {
-			let mut message_payload = message_payload(1, 0);
-			// the payload isn't simply extra, so it'll definitely overflow
-			// `MAX_OUTBOUND_PAYLOAD_SIZE` if we add `MAX_OUTBOUND_PAYLOAD_SIZE` bytes to extra
-			message_payload
-				.extra
-				.extend_from_slice(&[0u8; MAX_OUTBOUND_PAYLOAD_SIZE as usize]);
-			assert_noop!(
-				Pallet::<TestRuntime, ()>::validate_message(TEST_LANE_ID, &message_payload.clone(),),
-				Error::<TestRuntime, ()>::MessageRejectedByPallet(
-					VerificationError::MessageTooLarge
-				),
-			);
-
-			// let's check that we're able to send `MAX_OUTBOUND_PAYLOAD_SIZE` messages
-			while message_payload.encoded_size() as u32 > MAX_OUTBOUND_PAYLOAD_SIZE {
-				message_payload.extra.pop();
-			}
-			assert_eq!(message_payload.encoded_size() as u32, MAX_OUTBOUND_PAYLOAD_SIZE);
-
-			let valid_message =
-				Pallet::<TestRuntime, ()>::validate_message(TEST_LANE_ID, &message_payload)
-					.expect("validate_message has failed");
-			Pallet::<TestRuntime, ()>::send_message(valid_message);
-		})
-	}
-
-	#[test]
-	fn chain_verifier_rejects_invalid_message_in_send_message() {
-		run_test(|| {
-			// messages with this payload are rejected by target chain verifier
-			assert_noop!(
-				Pallet::<TestRuntime, ()>::validate_message(
-					TEST_LANE_ID,
-					&PAYLOAD_REJECTED_BY_TARGET_CHAIN,
-				),
-				Error::<TestRuntime, ()>::MessageRejectedByChainVerifier(VerificationError::Other(
-					mock::TEST_ERROR
-				)),
-			);
-		});
-	}
-
-	#[test]
-	fn receive_messages_proof_works() {
-		run_test(|| {
-			assert_ok!(Pallet::<TestRuntime>::receive_messages_proof(
-				RuntimeOrigin::signed(1),
-				TEST_RELAYER_A,
-				Ok(vec![message(1, REGULAR_PAYLOAD)]).into(),
-				1,
-				REGULAR_PAYLOAD.declared_weight,
-			));
-
-			assert_eq!(InboundLanes::<TestRuntime>::get(TEST_LANE_ID).0.last_delivered_nonce(), 1);
-
-			assert!(TestDeliveryPayments::is_reward_paid(1));
-		});
-	}
-
-	#[test]
-	fn receive_messages_proof_updates_confirmed_message_nonce() {
-		run_test(|| {
-			// say we have received 10 messages && last confirmed message is 8
-			InboundLanes::<TestRuntime, ()>::insert(
-				TEST_LANE_ID,
-				InboundLaneData {
-					last_confirmed_nonce: 8,
-					relayers: vec![
-						unrewarded_relayer(9, 9, TEST_RELAYER_A),
-						unrewarded_relayer(10, 10, TEST_RELAYER_B),
-					]
-					.into_iter()
-					.collect(),
-				},
-			);
-			assert_eq!(
-				inbound_unrewarded_relayers_state(TEST_LANE_ID),
-				UnrewardedRelayersState {
-					unrewarded_relayer_entries: 2,
-					messages_in_oldest_entry: 1,
-					total_messages: 2,
-					last_delivered_nonce: 10,
-				},
-			);
-
-			// message proof includes outbound lane state with latest confirmed message updated to 9
-			let mut message_proof: TestMessagesProof =
-				Ok(vec![message(11, REGULAR_PAYLOAD)]).into();
-			message_proof.result.as_mut().unwrap()[0].1.lane_state =
-				Some(OutboundLaneData { latest_received_nonce: 9, ..Default::default() });
-
-			assert_ok!(Pallet::<TestRuntime>::receive_messages_proof(
-				RuntimeOrigin::signed(1),
-				TEST_RELAYER_A,
-				message_proof,
-				1,
-				REGULAR_PAYLOAD.declared_weight,
-			));
-
-			assert_eq!(
-				InboundLanes::<TestRuntime>::get(TEST_LANE_ID).0,
-				InboundLaneData {
-					last_confirmed_nonce: 9,
-					relayers: vec![
-						unrewarded_relayer(10, 10, TEST_RELAYER_B),
-						unrewarded_relayer(11, 11, TEST_RELAYER_A)
-					]
-					.into_iter()
-					.collect(),
-				},
-			);
-			assert_eq!(
-				inbound_unrewarded_relayers_state(TEST_LANE_ID),
-				UnrewardedRelayersState {
-					unrewarded_relayer_entries: 2,
-					messages_in_oldest_entry: 1,
-					total_messages: 2,
-					last_delivered_nonce: 11,
-				},
-			);
-		});
-	}
-
-	#[test]
-	fn receive_messages_fails_if_dispatcher_is_inactive() {
-		run_test(|| {
-			TestMessageDispatch::deactivate();
-			assert_noop!(
-				Pallet::<TestRuntime>::receive_messages_proof(
-					RuntimeOrigin::signed(1),
-					TEST_RELAYER_A,
-					Ok(vec![message(1, REGULAR_PAYLOAD)]).into(),
-					1,
-					REGULAR_PAYLOAD.declared_weight,
-				),
-				Error::<TestRuntime, ()>::MessageDispatchInactive,
-			);
-		});
-	}
-
-	#[test]
-	fn receive_messages_proof_does_not_accept_message_if_dispatch_weight_is_not_enough() {
-		run_test(|| {
-			let mut declared_weight = REGULAR_PAYLOAD.declared_weight;
-			*declared_weight.ref_time_mut() -= 1;
-			assert_noop!(
-				Pallet::<TestRuntime>::receive_messages_proof(
-					RuntimeOrigin::signed(1),
-					TEST_RELAYER_A,
-					Ok(vec![message(1, REGULAR_PAYLOAD)]).into(),
-					1,
-					declared_weight,
-				),
-				Error::<TestRuntime, ()>::InsufficientDispatchWeight
-			);
-			assert_eq!(InboundLanes::<TestRuntime>::get(TEST_LANE_ID).last_delivered_nonce(), 0);
-		});
-	}
-
-	#[test]
-	fn receive_messages_proof_rejects_invalid_proof() {
-		run_test(|| {
-			assert_noop!(
-				Pallet::<TestRuntime, ()>::receive_messages_proof(
-					RuntimeOrigin::signed(1),
-					TEST_RELAYER_A,
-					Err(()).into(),
-					1,
-					Weight::zero(),
-				),
-				Error::<TestRuntime, ()>::InvalidMessagesProof,
-			);
-		});
-	}
-
-	#[test]
-	fn receive_messages_proof_rejects_proof_with_too_many_messages() {
-		run_test(|| {
-			assert_noop!(
-				Pallet::<TestRuntime, ()>::receive_messages_proof(
-					RuntimeOrigin::signed(1),
-					TEST_RELAYER_A,
-					Ok(vec![message(1, REGULAR_PAYLOAD)]).into(),
-					u32::MAX,
-					Weight::zero(),
-				),
-				Error::<TestRuntime, ()>::TooManyMessagesInTheProof,
-			);
-		});
-	}
-
-	#[test]
-	fn receive_messages_delivery_proof_works() {
-		run_test(|| {
-			send_regular_message(TEST_LANE_ID);
-			receive_messages_delivery_proof();
-
-			assert_eq!(
-				OutboundLanes::<TestRuntime, ()>::get(TEST_LANE_ID).latest_received_nonce,
-				1,
-			);
-		});
-	}
-
-	#[test]
-	fn receive_messages_delivery_proof_rewards_relayers() {
-		run_test(|| {
-			send_regular_message(TEST_LANE_ID);
-			send_regular_message(TEST_LANE_ID);
-
-			// this reports delivery of message 1 => reward is paid to TEST_RELAYER_A
-			let single_message_delivery_proof = TestMessagesDeliveryProof(Ok((
-				TEST_LANE_ID,
-				InboundLaneData {
-					relayers: vec![unrewarded_relayer(1, 1, TEST_RELAYER_A)].into_iter().collect(),
-					..Default::default()
-				},
-			)));
-			let single_message_delivery_proof_size = single_message_delivery_proof.size();
-			let result = Pallet::<TestRuntime>::receive_messages_delivery_proof(
-				RuntimeOrigin::signed(1),
-				single_message_delivery_proof,
-				UnrewardedRelayersState {
-					unrewarded_relayer_entries: 1,
-					messages_in_oldest_entry: 1,
-					total_messages: 1,
-					last_delivered_nonce: 1,
-				},
-			);
-			assert_ok!(result);
-			assert_eq!(
-				result.unwrap().actual_weight.unwrap(),
-				TestWeightInfo::receive_messages_delivery_proof_weight(
-					&PreComputedSize(single_message_delivery_proof_size as _),
-					&UnrewardedRelayersState {
-						unrewarded_relayer_entries: 1,
-						total_messages: 1,
-						..Default::default()
-					},
-				)
-			);
-			assert!(TestDeliveryConfirmationPayments::is_reward_paid(TEST_RELAYER_A, 1));
-			assert!(!TestDeliveryConfirmationPayments::is_reward_paid(TEST_RELAYER_B, 1));
-			assert_eq!(TestOnMessagesDelivered::call_arguments(), Some((TEST_LANE_ID, 1)));
-
-			// this reports delivery of both message 1 and message 2 => reward is paid only to
-			// TEST_RELAYER_B
-			let two_messages_delivery_proof = TestMessagesDeliveryProof(Ok((
-				TEST_LANE_ID,
-				InboundLaneData {
-					relayers: vec![
-						unrewarded_relayer(1, 1, TEST_RELAYER_A),
-						unrewarded_relayer(2, 2, TEST_RELAYER_B),
-					]
-					.into_iter()
-					.collect(),
-					..Default::default()
-				},
-			)));
-			let two_messages_delivery_proof_size = two_messages_delivery_proof.size();
-			let result = Pallet::<TestRuntime>::receive_messages_delivery_proof(
-				RuntimeOrigin::signed(1),
-				two_messages_delivery_proof,
-				UnrewardedRelayersState {
-					unrewarded_relayer_entries: 2,
-					messages_in_oldest_entry: 1,
-					total_messages: 2,
-					last_delivered_nonce: 2,
-				},
-			);
-			assert_ok!(result);
-			// even though the pre-dispatch weight was for two messages, the actual weight is
-			// for single message only
-			assert_eq!(
-				result.unwrap().actual_weight.unwrap(),
-				TestWeightInfo::receive_messages_delivery_proof_weight(
-					&PreComputedSize(two_messages_delivery_proof_size as _),
-					&UnrewardedRelayersState {
-						unrewarded_relayer_entries: 1,
-						total_messages: 1,
-						..Default::default()
-					},
-				)
-			);
-			assert!(!TestDeliveryConfirmationPayments::is_reward_paid(TEST_RELAYER_A, 1));
-			assert!(TestDeliveryConfirmationPayments::is_reward_paid(TEST_RELAYER_B, 1));
-			assert_eq!(TestOnMessagesDelivered::call_arguments(), Some((TEST_LANE_ID, 0)));
-		});
-	}
-
-	#[test]
-	fn receive_messages_delivery_proof_rejects_invalid_proof() {
-		run_test(|| {
-			assert_noop!(
-				Pallet::<TestRuntime>::receive_messages_delivery_proof(
-					RuntimeOrigin::signed(1),
-					TestMessagesDeliveryProof(Err(())),
-					Default::default(),
-				),
-				Error::<TestRuntime, ()>::InvalidMessagesDeliveryProof,
-			);
-		});
-	}
-
-	#[test]
-	fn receive_messages_delivery_proof_rejects_proof_if_declared_relayers_state_is_invalid() {
-		run_test(|| {
-			// when number of relayers entries is invalid
-			assert_noop!(
-				Pallet::<TestRuntime>::receive_messages_delivery_proof(
-					RuntimeOrigin::signed(1),
-					TestMessagesDeliveryProof(Ok((
-						TEST_LANE_ID,
-						InboundLaneData {
-							relayers: vec![
-								unrewarded_relayer(1, 1, TEST_RELAYER_A),
-								unrewarded_relayer(2, 2, TEST_RELAYER_B)
-							]
-							.into_iter()
-							.collect(),
-							..Default::default()
-						}
-					))),
-					UnrewardedRelayersState {
-						unrewarded_relayer_entries: 1,
-						total_messages: 2,
-						last_delivered_nonce: 2,
-						..Default::default()
-					},
-				),
-				Error::<TestRuntime, ()>::InvalidUnrewardedRelayersState,
-			);
-
-			// when number of messages is invalid
-			assert_noop!(
-				Pallet::<TestRuntime>::receive_messages_delivery_proof(
-					RuntimeOrigin::signed(1),
-					TestMessagesDeliveryProof(Ok((
-						TEST_LANE_ID,
-						InboundLaneData {
-							relayers: vec![
-								unrewarded_relayer(1, 1, TEST_RELAYER_A),
-								unrewarded_relayer(2, 2, TEST_RELAYER_B)
-							]
-							.into_iter()
-							.collect(),
-							..Default::default()
-						}
-					))),
-					UnrewardedRelayersState {
-						unrewarded_relayer_entries: 2,
-						total_messages: 1,
-						last_delivered_nonce: 2,
-						..Default::default()
-					},
-				),
-				Error::<TestRuntime, ()>::InvalidUnrewardedRelayersState,
-			);
-
-			// when last delivered nonce is invalid
-			assert_noop!(
-				Pallet::<TestRuntime>::receive_messages_delivery_proof(
-					RuntimeOrigin::signed(1),
-					TestMessagesDeliveryProof(Ok((
-						TEST_LANE_ID,
-						InboundLaneData {
-							relayers: vec![
-								unrewarded_relayer(1, 1, TEST_RELAYER_A),
-								unrewarded_relayer(2, 2, TEST_RELAYER_B)
-							]
-							.into_iter()
-							.collect(),
-							..Default::default()
-						}
-					))),
-					UnrewardedRelayersState {
-						unrewarded_relayer_entries: 2,
-						total_messages: 2,
-						last_delivered_nonce: 8,
-						..Default::default()
-					},
-				),
-				Error::<TestRuntime, ()>::InvalidUnrewardedRelayersState,
-			);
-		});
-	}
-
-	#[test]
-	fn receive_messages_accepts_single_message_with_invalid_payload() {
-		run_test(|| {
-			let mut invalid_message = message(1, REGULAR_PAYLOAD);
-			invalid_message.payload = Vec::new();
-
-			assert_ok!(Pallet::<TestRuntime, ()>::receive_messages_proof(
-				RuntimeOrigin::signed(1),
-				TEST_RELAYER_A,
-				Ok(vec![invalid_message]).into(),
-				1,
-				Weight::zero(), /* weight may be zero in this case (all messages are
-				                 * improperly encoded) */
-			),);
-
-			assert_eq!(InboundLanes::<TestRuntime>::get(TEST_LANE_ID).last_delivered_nonce(), 1,);
-		});
-	}
-
-	#[test]
-	fn receive_messages_accepts_batch_with_message_with_invalid_payload() {
-		run_test(|| {
-			let mut invalid_message = message(2, REGULAR_PAYLOAD);
-			invalid_message.payload = Vec::new();
-
-			assert_ok!(Pallet::<TestRuntime, ()>::receive_messages_proof(
-				RuntimeOrigin::signed(1),
-				TEST_RELAYER_A,
-				Ok(
-					vec![message(1, REGULAR_PAYLOAD), invalid_message, message(3, REGULAR_PAYLOAD),]
-				)
-				.into(),
-				3,
-				REGULAR_PAYLOAD.declared_weight + REGULAR_PAYLOAD.declared_weight,
-			),);
-
-			assert_eq!(InboundLanes::<TestRuntime>::get(TEST_LANE_ID).last_delivered_nonce(), 3,);
-		});
-	}
-
-	#[test]
-	fn actual_dispatch_weight_does_not_overflow() {
-		run_test(|| {
-			let message1 = message(1, message_payload(0, u64::MAX / 2));
-			let message2 = message(2, message_payload(0, u64::MAX / 2));
-			let message3 = message(3, message_payload(0, u64::MAX / 2));
-
-			assert_noop!(
-				Pallet::<TestRuntime, ()>::receive_messages_proof(
-					RuntimeOrigin::signed(1),
-					TEST_RELAYER_A,
-					// this may cause overflow if source chain storage is invalid
-					Ok(vec![message1, message2, message3]).into(),
-					3,
-					Weight::MAX,
-				),
-				Error::<TestRuntime, ()>::InsufficientDispatchWeight
-			);
-			assert_eq!(InboundLanes::<TestRuntime>::get(TEST_LANE_ID).last_delivered_nonce(), 0);
-		});
-	}
-
-	#[test]
-	fn ref_time_refund_from_receive_messages_proof_works() {
-		run_test(|| {
-			fn submit_with_unspent_weight(
-				nonce: MessageNonce,
-				unspent_weight: u64,
-			) -> (Weight, Weight) {
-				let mut payload = REGULAR_PAYLOAD;
-				*payload.dispatch_result.unspent_weight.ref_time_mut() = unspent_weight;
-				let proof = Ok(vec![message(nonce, payload)]).into();
-				let messages_count = 1;
-				let pre_dispatch_weight =
-					<TestRuntime as Config>::WeightInfo::receive_messages_proof_weight(
-						&proof,
-						messages_count,
-						REGULAR_PAYLOAD.declared_weight,
-					);
-				let result = Pallet::<TestRuntime>::receive_messages_proof(
-					RuntimeOrigin::signed(1),
-					TEST_RELAYER_A,
-					proof,
-					messages_count,
-					REGULAR_PAYLOAD.declared_weight,
-				)
-				.expect("delivery has failed");
-				let post_dispatch_weight =
-					result.actual_weight.expect("receive_messages_proof always returns Some");
-
-				// message delivery transactions are never free
-				assert_eq!(result.pays_fee, Pays::Yes);
-
-				(pre_dispatch_weight, post_dispatch_weight)
-			}
-
-			// when dispatch is returning `unspent_weight < declared_weight`
-			let (pre, post) = submit_with_unspent_weight(1, 1);
-			assert_eq!(post.ref_time(), pre.ref_time() - 1);
-
-			// when dispatch is returning `unspent_weight = declared_weight`
-			let (pre, post) =
-				submit_with_unspent_weight(2, REGULAR_PAYLOAD.declared_weight.ref_time());
-			assert_eq!(
-				post.ref_time(),
-				pre.ref_time() - REGULAR_PAYLOAD.declared_weight.ref_time()
-			);
-
-			// when dispatch is returning `unspent_weight > declared_weight`
-			let (pre, post) =
-				submit_with_unspent_weight(3, REGULAR_PAYLOAD.declared_weight.ref_time() + 1);
-			assert_eq!(
-				post.ref_time(),
-				pre.ref_time() - REGULAR_PAYLOAD.declared_weight.ref_time()
-			);
-
-			// when there's no unspent weight
-			let (pre, post) = submit_with_unspent_weight(4, 0);
-			assert_eq!(post.ref_time(), pre.ref_time());
-
-			// when dispatch is returning `unspent_weight < declared_weight`
-			let (pre, post) = submit_with_unspent_weight(5, 1);
-			assert_eq!(post.ref_time(), pre.ref_time() - 1);
-		});
-	}
-
-	#[test]
-	fn proof_size_refund_from_receive_messages_proof_works() {
-		run_test(|| {
-			let max_entries = crate::mock::MaxUnrewardedRelayerEntriesAtInboundLane::get() as usize;
-
-			// if there's maximal number of unrewarded relayer entries at the inbound lane, then
-			// `proof_size` is unchanged in post-dispatch weight
-			let proof: TestMessagesProof = Ok(vec![message(101, REGULAR_PAYLOAD)]).into();
-			let messages_count = 1;
-			let pre_dispatch_weight =
-				<TestRuntime as Config>::WeightInfo::receive_messages_proof_weight(
-					&proof,
-					messages_count,
-					REGULAR_PAYLOAD.declared_weight,
-				);
-			InboundLanes::<TestRuntime>::insert(
-				TEST_LANE_ID,
-				StoredInboundLaneData(InboundLaneData {
-					relayers: vec![
-						UnrewardedRelayer {
-							relayer: 42,
-							messages: DeliveredMessages { begin: 0, end: 100 }
-						};
-						max_entries
-					]
-					.into_iter()
-					.collect(),
-					last_confirmed_nonce: 0,
-				}),
-			);
-			let post_dispatch_weight = Pallet::<TestRuntime>::receive_messages_proof(
-				RuntimeOrigin::signed(1),
-				TEST_RELAYER_A,
-				proof.clone(),
-				messages_count,
-				REGULAR_PAYLOAD.declared_weight,
-			)
-			.unwrap()
-			.actual_weight
-			.unwrap();
-			assert_eq!(post_dispatch_weight.proof_size(), pre_dispatch_weight.proof_size());
-
-			// if count of unrewarded relayer entries is less than maximal, then some `proof_size`
-			// must be refunded
-			InboundLanes::<TestRuntime>::insert(
-				TEST_LANE_ID,
-				StoredInboundLaneData(InboundLaneData {
-					relayers: vec![
-						UnrewardedRelayer {
-							relayer: 42,
-							messages: DeliveredMessages { begin: 0, end: 100 }
-						};
-						max_entries - 1
-					]
-					.into_iter()
-					.collect(),
-					last_confirmed_nonce: 0,
-				}),
-			);
-			let post_dispatch_weight = Pallet::<TestRuntime>::receive_messages_proof(
-				RuntimeOrigin::signed(1),
-				TEST_RELAYER_A,
-				proof,
-				messages_count,
-				REGULAR_PAYLOAD.declared_weight,
-			)
-			.unwrap()
-			.actual_weight
-			.unwrap();
-			assert!(
-				post_dispatch_weight.proof_size() < pre_dispatch_weight.proof_size(),
-				"Expected post-dispatch PoV {} to be less than pre-dispatch PoV {}",
-				post_dispatch_weight.proof_size(),
-				pre_dispatch_weight.proof_size(),
-			);
-		});
-	}
-
-	#[test]
-	fn messages_delivered_callbacks_are_called() {
-		run_test(|| {
-			send_regular_message(TEST_LANE_ID);
-			send_regular_message(TEST_LANE_ID);
-			send_regular_message(TEST_LANE_ID);
-
-			// messages 1+2 are confirmed in 1 tx, message 3 in a separate tx
-			// dispatch of message 2 has failed
-			let mut delivered_messages_1_and_2 = DeliveredMessages::new(1);
-			delivered_messages_1_and_2.note_dispatched_message();
-			let messages_1_and_2_proof = Ok((
-				TEST_LANE_ID,
-				InboundLaneData {
-					last_confirmed_nonce: 0,
-					relayers: vec![UnrewardedRelayer {
-						relayer: 0,
-						messages: delivered_messages_1_and_2.clone(),
-					}]
-					.into_iter()
-					.collect(),
-				},
-			));
-			let delivered_message_3 = DeliveredMessages::new(3);
-			let messages_3_proof = Ok((
-				TEST_LANE_ID,
-				InboundLaneData {
-					last_confirmed_nonce: 0,
-					relayers: vec![UnrewardedRelayer { relayer: 0, messages: delivered_message_3 }]
-						.into_iter()
-						.collect(),
-				},
-			));
-
-			// first tx with messages 1+2
-			assert_ok!(Pallet::<TestRuntime>::receive_messages_delivery_proof(
-				RuntimeOrigin::signed(1),
-				TestMessagesDeliveryProof(messages_1_and_2_proof),
-				UnrewardedRelayersState {
-					unrewarded_relayer_entries: 1,
-					messages_in_oldest_entry: 2,
-					total_messages: 2,
-					last_delivered_nonce: 2,
-				},
-			));
-			// second tx with message 3
-			assert_ok!(Pallet::<TestRuntime>::receive_messages_delivery_proof(
-				RuntimeOrigin::signed(1),
-				TestMessagesDeliveryProof(messages_3_proof),
-				UnrewardedRelayersState {
-					unrewarded_relayer_entries: 1,
-					messages_in_oldest_entry: 1,
-					total_messages: 1,
-					last_delivered_nonce: 3,
-				},
-			));
-		});
-	}
-
-	#[test]
-	fn receive_messages_delivery_proof_rejects_proof_if_trying_to_confirm_more_messages_than_expected(
-	) {
-		run_test(|| {
-			// send message first to be able to check that delivery_proof fails later
-			send_regular_message(TEST_LANE_ID);
-
-			// 1) InboundLaneData declares that the `last_confirmed_nonce` is 1;
-			// 2) InboundLaneData has no entries => `InboundLaneData::last_delivered_nonce()`
-			//    returns `last_confirmed_nonce`;
-			// 3) it means that we're going to confirm delivery of messages 1..=1;
-			// 4) so the number of declared messages (see `UnrewardedRelayersState`) is `0` and
-			//    number of actually confirmed messages is `1`.
-			assert_noop!(
-				Pallet::<TestRuntime>::receive_messages_delivery_proof(
-					RuntimeOrigin::signed(1),
-					TestMessagesDeliveryProof(Ok((
-						TEST_LANE_ID,
-						InboundLaneData { last_confirmed_nonce: 1, relayers: Default::default() },
-					))),
-					UnrewardedRelayersState { last_delivered_nonce: 1, ..Default::default() },
-				),
-				Error::<TestRuntime, ()>::ReceptionConfirmation(
-					ReceptionConfirmationError::TryingToConfirmMoreMessagesThanExpected
-				),
-			);
-		});
-	}
-
-	#[test]
-	fn storage_keys_computed_properly() {
-		assert_eq!(
-			PalletOperatingMode::<TestRuntime>::storage_value_final_key().to_vec(),
-			bp_messages::storage_keys::operating_mode_key("Messages").0,
-		);
-
-		assert_eq!(
-			OutboundMessages::<TestRuntime>::storage_map_final_key(MessageKey {
-				lane_id: TEST_LANE_ID,
-				nonce: 42
-			}),
-			bp_messages::storage_keys::message_key("Messages", &TEST_LANE_ID, 42).0,
-		);
-
-		assert_eq!(
-			OutboundLanes::<TestRuntime>::storage_map_final_key(TEST_LANE_ID),
-			bp_messages::storage_keys::outbound_lane_data_key("Messages", &TEST_LANE_ID).0,
-		);
-
-		assert_eq!(
-			InboundLanes::<TestRuntime>::storage_map_final_key(TEST_LANE_ID),
-			bp_messages::storage_keys::inbound_lane_data_key("Messages", &TEST_LANE_ID).0,
-		);
-	}
-
-	#[test]
-	fn inbound_message_details_works() {
-		run_test(|| {
-			assert_eq!(
-				Pallet::<TestRuntime>::inbound_message_data(
-					TEST_LANE_ID,
-					REGULAR_PAYLOAD.encode(),
-					OutboundMessageDetails { nonce: 0, dispatch_weight: Weight::zero(), size: 0 },
-				),
-				InboundMessageDetails { dispatch_weight: REGULAR_PAYLOAD.declared_weight },
-			);
-		});
-	}
-
-	#[test]
-	fn on_idle_callback_respects_remaining_weight() {
-		run_test(|| {
-			send_regular_message(TEST_LANE_ID);
-			send_regular_message(TEST_LANE_ID);
-			send_regular_message(TEST_LANE_ID);
-			send_regular_message(TEST_LANE_ID);
-
-			assert_ok!(Pallet::<TestRuntime>::receive_messages_delivery_proof(
-				RuntimeOrigin::signed(1),
-				TestMessagesDeliveryProof(Ok((
-					TEST_LANE_ID,
-					InboundLaneData {
-						last_confirmed_nonce: 4,
-						relayers: vec![unrewarded_relayer(1, 4, TEST_RELAYER_A)]
-							.into_iter()
-							.collect(),
-					},
-				))),
-				UnrewardedRelayersState {
-					unrewarded_relayer_entries: 1,
-					messages_in_oldest_entry: 4,
-					total_messages: 4,
-					last_delivered_nonce: 4,
-				},
-			));
-
-			// all 4 messages may be pruned now
-			assert_eq!(
-				outbound_lane::<TestRuntime, ()>(TEST_LANE_ID).data().latest_received_nonce,
-				4
-			);
-			assert_eq!(
-				outbound_lane::<TestRuntime, ()>(TEST_LANE_ID).data().oldest_unpruned_nonce,
-				1
-			);
-			System::<TestRuntime>::set_block_number(2);
-
-			// if passed wight is too low to do anything
-			let dbw = DbWeight::get();
-			assert_eq!(
-				Pallet::<TestRuntime, ()>::on_idle(0, dbw.reads_writes(1, 1)),
-				Weight::zero(),
-			);
-			assert_eq!(
-				outbound_lane::<TestRuntime, ()>(TEST_LANE_ID).data().oldest_unpruned_nonce,
-				1
-			);
-
-			// if passed wight is enough to prune single message
-			assert_eq!(
-				Pallet::<TestRuntime, ()>::on_idle(0, dbw.reads_writes(1, 2)),
-				dbw.reads_writes(1, 2),
-			);
-			assert_eq!(
-				outbound_lane::<TestRuntime, ()>(TEST_LANE_ID).data().oldest_unpruned_nonce,
-				2
-			);
-
-			// if passed wight is enough to prune two more messages
-			assert_eq!(
-				Pallet::<TestRuntime, ()>::on_idle(0, dbw.reads_writes(1, 3)),
-				dbw.reads_writes(1, 3),
-			);
-			assert_eq!(
-				outbound_lane::<TestRuntime, ()>(TEST_LANE_ID).data().oldest_unpruned_nonce,
-				4
-			);
-
-			// if passed wight is enough to prune many messages
-			assert_eq!(
-				Pallet::<TestRuntime, ()>::on_idle(0, dbw.reads_writes(100, 100)),
-				dbw.reads_writes(1, 2),
-			);
-			assert_eq!(
-				outbound_lane::<TestRuntime, ()>(TEST_LANE_ID).data().oldest_unpruned_nonce,
-				5
-			);
-		});
-	}
-
-	#[test]
-	fn on_idle_callback_is_rotating_lanes_to_prune() {
-		run_test(|| {
-			// send + receive confirmation for lane 1
-			send_regular_message(TEST_LANE_ID);
-			receive_messages_delivery_proof();
-			// send + receive confirmation for lane 2
-			send_regular_message(TEST_LANE_ID_2);
-			assert_ok!(Pallet::<TestRuntime>::receive_messages_delivery_proof(
-				RuntimeOrigin::signed(1),
-				TestMessagesDeliveryProof(Ok((
-					TEST_LANE_ID_2,
-					InboundLaneData {
-						last_confirmed_nonce: 1,
-						relayers: vec![unrewarded_relayer(1, 1, TEST_RELAYER_A)]
-							.into_iter()
-							.collect(),
-					},
-				))),
-				UnrewardedRelayersState {
-					unrewarded_relayer_entries: 1,
-					messages_in_oldest_entry: 1,
-					total_messages: 1,
-					last_delivered_nonce: 1,
-				},
-			));
-
-			// nothing is pruned yet
-			assert_eq!(
-				outbound_lane::<TestRuntime, ()>(TEST_LANE_ID).data().latest_received_nonce,
-				1
-			);
-			assert_eq!(
-				outbound_lane::<TestRuntime, ()>(TEST_LANE_ID).data().oldest_unpruned_nonce,
-				1
-			);
-			assert_eq!(
-				outbound_lane::<TestRuntime, ()>(TEST_LANE_ID_2).data().latest_received_nonce,
-				1
-			);
-			assert_eq!(
-				outbound_lane::<TestRuntime, ()>(TEST_LANE_ID_2).data().oldest_unpruned_nonce,
-				1
-			);
-
-			// in block#2.on_idle lane messages of lane 1 are pruned
-			let dbw = DbWeight::get();
-			System::<TestRuntime>::set_block_number(2);
-			assert_eq!(
-				Pallet::<TestRuntime, ()>::on_idle(0, dbw.reads_writes(100, 100)),
-				dbw.reads_writes(1, 2),
-			);
-			assert_eq!(
-				outbound_lane::<TestRuntime, ()>(TEST_LANE_ID).data().oldest_unpruned_nonce,
-				2
-			);
-			assert_eq!(
-				outbound_lane::<TestRuntime, ()>(TEST_LANE_ID_2).data().oldest_unpruned_nonce,
-				1
-			);
-
-			// in block#3.on_idle lane messages of lane 2 are pruned
-			System::<TestRuntime>::set_block_number(3);
-
-			assert_eq!(
-				Pallet::<TestRuntime, ()>::on_idle(0, dbw.reads_writes(100, 100)),
-				dbw.reads_writes(1, 2),
-			);
-			assert_eq!(
-				outbound_lane::<TestRuntime, ()>(TEST_LANE_ID).data().oldest_unpruned_nonce,
-				2
-			);
-			assert_eq!(
-				outbound_lane::<TestRuntime, ()>(TEST_LANE_ID_2).data().oldest_unpruned_nonce,
-				2
-			);
-		});
-	}
-
-	#[test]
-	fn outbound_message_from_unconfigured_lane_is_rejected() {
-		run_test(|| {
-			assert_noop!(
-				Pallet::<TestRuntime, ()>::validate_message(TEST_LANE_ID_3, &REGULAR_PAYLOAD,),
-				Error::<TestRuntime, ()>::InactiveOutboundLane,
-			);
-		});
-	}
-
-	#[test]
-	fn test_bridge_messages_call_is_correctly_defined() {
-		let account_id = 1;
-		let message_proof: TestMessagesProof = Ok(vec![message(1, REGULAR_PAYLOAD)]).into();
-		let message_delivery_proof = TestMessagesDeliveryProof(Ok((
-			TEST_LANE_ID,
-			InboundLaneData {
-				last_confirmed_nonce: 1,
-				relayers: vec![UnrewardedRelayer {
-					relayer: 0,
-					messages: DeliveredMessages::new(1),
-				}]
-				.into_iter()
-				.collect(),
-			},
-		)));
-		let unrewarded_relayer_state = UnrewardedRelayersState {
-			unrewarded_relayer_entries: 1,
-			total_messages: 1,
-			last_delivered_nonce: 1,
-			..Default::default()
-		};
-
-		let direct_receive_messages_proof_call = Call::<TestRuntime>::receive_messages_proof {
-			relayer_id_at_bridged_chain: account_id,
-			proof: message_proof.clone(),
-			messages_count: 1,
-			dispatch_weight: REGULAR_PAYLOAD.declared_weight,
-		};
-		let indirect_receive_messages_proof_call = BridgeMessagesCall::<
-			AccountId,
-			TestMessagesProof,
-			TestMessagesDeliveryProof,
-		>::receive_messages_proof {
-			relayer_id_at_bridged_chain: account_id,
-			proof: message_proof,
-			messages_count: 1,
-			dispatch_weight: REGULAR_PAYLOAD.declared_weight,
-		};
-		assert_eq!(
-			direct_receive_messages_proof_call.encode(),
-			indirect_receive_messages_proof_call.encode()
-		);
-
-		let direct_receive_messages_delivery_proof_call =
-			Call::<TestRuntime>::receive_messages_delivery_proof {
-				proof: message_delivery_proof.clone(),
-				relayers_state: unrewarded_relayer_state.clone(),
-			};
-		let indirect_receive_messages_delivery_proof_call = BridgeMessagesCall::<
-			AccountId,
-			TestMessagesProof,
-			TestMessagesDeliveryProof,
-		>::receive_messages_delivery_proof {
-			proof: message_delivery_proof,
-			relayers_state: unrewarded_relayer_state,
-		};
-		assert_eq!(
-			direct_receive_messages_delivery_proof_call.encode(),
-			indirect_receive_messages_delivery_proof_call.encode()
-		);
-	}
-
-	generate_owned_bridge_module_tests!(
-		MessagesOperatingMode::Basic(BasicOperatingMode::Normal),
-		MessagesOperatingMode::Basic(BasicOperatingMode::Halted)
-	);
-
-	#[test]
-	fn inbound_storage_extra_proof_size_bytes_works() {
-		fn relayer_entry() -> UnrewardedRelayer<TestRelayer> {
-			UnrewardedRelayer { relayer: 42u64, messages: DeliveredMessages { begin: 0, end: 100 } }
-		}
-
-		fn storage(relayer_entries: usize) -> RuntimeInboundLaneStorage<TestRuntime, ()> {
-			RuntimeInboundLaneStorage {
-				lane_id: Default::default(),
-				cached_data: Some(InboundLaneData {
-					relayers: vec![relayer_entry(); relayer_entries].into_iter().collect(),
-					last_confirmed_nonce: 0,
-				}),
-				_phantom: Default::default(),
-			}
-		}
-
-		let max_entries = crate::mock::MaxUnrewardedRelayerEntriesAtInboundLane::get() as usize;
-
-		// when we have exactly `MaxUnrewardedRelayerEntriesAtInboundLane` unrewarded relayers
-		assert_eq!(storage(max_entries).extra_proof_size_bytes(), 0);
-
-		// when we have less than `MaxUnrewardedRelayerEntriesAtInboundLane` unrewarded relayers
-		assert_eq!(
-			storage(max_entries - 1).extra_proof_size_bytes(),
-			relayer_entry().encode().len() as u64
-		);
-		assert_eq!(
-			storage(max_entries - 2).extra_proof_size_bytes(),
-			2 * relayer_entry().encode().len() as u64
-		);
-
-		// when we have more than `MaxUnrewardedRelayerEntriesAtInboundLane` unrewarded relayers
-		// (shall not happen in practice)
-		assert_eq!(storage(max_entries + 1).extra_proof_size_bytes(), 0);
-	}
-
-	#[test]
-	fn maybe_outbound_lanes_count_returns_correct_value() {
-		assert_eq!(
-			MaybeOutboundLanesCount::<TestRuntime, ()>::get(),
-			Some(mock::ActiveOutboundLanes::get().len() as u32)
-		);
-	}
-}
diff --git a/bridges/modules/messages/src/outbound_lane.rs b/bridges/modules/messages/src/outbound_lane.rs
index acef5546d2a..fcdddf199dc 100644
--- a/bridges/modules/messages/src/outbound_lane.rs
+++ b/bridges/modules/messages/src/outbound_lane.rs
@@ -18,16 +18,18 @@
 
 use crate::{Config, LOG_TARGET};
 
-use bp_messages::{DeliveredMessages, LaneId, MessageNonce, OutboundLaneData, UnrewardedRelayer};
+use bp_messages::{
+	ChainWithMessages, DeliveredMessages, LaneId, MessageNonce, OutboundLaneData, UnrewardedRelayer,
+};
 use codec::{Decode, Encode};
 use frame_support::{
+	traits::Get,
 	weights::{RuntimeDbWeight, Weight},
 	BoundedVec, PalletError,
 };
-use num_traits::Zero;
 use scale_info::TypeInfo;
-use sp_runtime::RuntimeDebug;
-use sp_std::collections::vec_deque::VecDeque;
+use sp_runtime::{traits::Zero, RuntimeDebug};
+use sp_std::{collections::vec_deque::VecDeque, marker::PhantomData};
 
 /// Outbound lane storage.
 pub trait OutboundLaneStorage {
@@ -48,8 +50,17 @@ pub trait OutboundLaneStorage {
 	fn remove_message(&mut self, nonce: &MessageNonce);
 }
 
+/// Limit for the `StoredMessagePayload` vector.
+pub struct StoredMessagePayloadLimit<T, I>(PhantomData<(T, I)>);
+
+impl<T: Config<I>, I: 'static> Get<u32> for StoredMessagePayloadLimit<T, I> {
+	fn get() -> u32 {
+		T::BridgedChain::maximal_incoming_message_size()
+	}
+}
+
 /// Outbound message data wrapper that implements `MaxEncodedLen`.
-pub type StoredMessagePayload<T, I> = BoundedVec<u8, <T as Config<I>>::MaximalOutboundPayloadSize>;
+pub type StoredMessagePayload<T, I> = BoundedVec<u8, StoredMessagePayloadLimit<T, I>>;
 
 /// Result of messages receival confirmation.
 #[derive(Encode, Decode, RuntimeDebug, PartialEq, Eq, PalletError, TypeInfo)]
@@ -204,11 +215,11 @@ fn ensure_unrewarded_relayers_are_correct<RelayerId>(
 mod tests {
 	use super::*;
 	use crate::{
-		mock::{
+		outbound_lane,
+		tests::mock::{
 			outbound_message_data, run_test, unrewarded_relayer, TestRelayer, TestRuntime,
 			REGULAR_PAYLOAD, TEST_LANE_ID,
 		},
-		outbound_lane,
 	};
 	use frame_support::weights::constants::RocksDbWeight;
 	use sp_std::ops::RangeInclusive;
@@ -263,12 +274,43 @@ mod tests {
 			assert_eq!(lane.send_message(outbound_message_data(REGULAR_PAYLOAD)), 3);
 			assert_eq!(lane.storage.data().latest_generated_nonce, 3);
 			assert_eq!(lane.storage.data().latest_received_nonce, 0);
+			assert_eq!(lane.storage.data().oldest_unpruned_nonce, 1);
 			assert_eq!(
 				lane.confirm_delivery(3, 3, &unrewarded_relayers(1..=3)),
 				Ok(Some(delivered_messages(1..=3))),
 			);
 			assert_eq!(lane.storage.data().latest_generated_nonce, 3);
 			assert_eq!(lane.storage.data().latest_received_nonce, 3);
+			assert_eq!(lane.storage.data().oldest_unpruned_nonce, 1);
+		});
+	}
+
+	#[test]
+	fn confirm_partial_delivery_works() {
+		run_test(|| {
+			let mut lane = outbound_lane::<TestRuntime, _>(TEST_LANE_ID);
+			assert_eq!(lane.send_message(outbound_message_data(REGULAR_PAYLOAD)), 1);
+			assert_eq!(lane.send_message(outbound_message_data(REGULAR_PAYLOAD)), 2);
+			assert_eq!(lane.send_message(outbound_message_data(REGULAR_PAYLOAD)), 3);
+			assert_eq!(lane.storage.data().latest_generated_nonce, 3);
+			assert_eq!(lane.storage.data().latest_received_nonce, 0);
+			assert_eq!(lane.storage.data().oldest_unpruned_nonce, 1);
+
+			assert_eq!(
+				lane.confirm_delivery(3, 2, &unrewarded_relayers(1..=2)),
+				Ok(Some(delivered_messages(1..=2))),
+			);
+			assert_eq!(lane.storage.data().latest_generated_nonce, 3);
+			assert_eq!(lane.storage.data().latest_received_nonce, 2);
+			assert_eq!(lane.storage.data().oldest_unpruned_nonce, 1);
+
+			assert_eq!(
+				lane.confirm_delivery(3, 3, &unrewarded_relayers(3..=3)),
+				Ok(Some(delivered_messages(3..=3))),
+			);
+			assert_eq!(lane.storage.data().latest_generated_nonce, 3);
+			assert_eq!(lane.storage.data().latest_received_nonce, 3);
+			assert_eq!(lane.storage.data().oldest_unpruned_nonce, 1);
 		});
 	}
 
@@ -281,6 +323,7 @@ mod tests {
 			lane.send_message(outbound_message_data(REGULAR_PAYLOAD));
 			assert_eq!(lane.storage.data().latest_generated_nonce, 3);
 			assert_eq!(lane.storage.data().latest_received_nonce, 0);
+			assert_eq!(lane.storage.data().oldest_unpruned_nonce, 1);
 			assert_eq!(
 				lane.confirm_delivery(3, 3, &unrewarded_relayers(1..=3)),
 				Ok(Some(delivered_messages(1..=3))),
@@ -288,10 +331,12 @@ mod tests {
 			assert_eq!(lane.confirm_delivery(3, 3, &unrewarded_relayers(1..=3)), Ok(None),);
 			assert_eq!(lane.storage.data().latest_generated_nonce, 3);
 			assert_eq!(lane.storage.data().latest_received_nonce, 3);
+			assert_eq!(lane.storage.data().oldest_unpruned_nonce, 1);
 
 			assert_eq!(lane.confirm_delivery(1, 2, &unrewarded_relayers(1..=1)), Ok(None),);
 			assert_eq!(lane.storage.data().latest_generated_nonce, 3);
 			assert_eq!(lane.storage.data().latest_received_nonce, 3);
+			assert_eq!(lane.storage.data().oldest_unpruned_nonce, 1);
 		});
 	}
 
@@ -310,8 +355,8 @@ mod tests {
 				3,
 				&unrewarded_relayers(1..=1)
 					.into_iter()
-					.chain(unrewarded_relayers(2..=30).into_iter())
-					.chain(unrewarded_relayers(3..=3).into_iter())
+					.chain(unrewarded_relayers(2..=30))
+					.chain(unrewarded_relayers(3..=3))
 					.collect(),
 			),
 			Err(ReceptionConfirmationError::FailedToConfirmFutureMessages),
@@ -326,8 +371,8 @@ mod tests {
 				3,
 				&unrewarded_relayers(1..=1)
 					.into_iter()
-					.chain(unrewarded_relayers(2..=1).into_iter())
-					.chain(unrewarded_relayers(2..=3).into_iter())
+					.chain(unrewarded_relayers(2..=1))
+					.chain(unrewarded_relayers(2..=3))
 					.collect(),
 			),
 			Err(ReceptionConfirmationError::EmptyUnrewardedRelayerEntry),
@@ -341,8 +386,8 @@ mod tests {
 				3,
 				&unrewarded_relayers(1..=1)
 					.into_iter()
-					.chain(unrewarded_relayers(3..=3).into_iter())
-					.chain(unrewarded_relayers(2..=2).into_iter())
+					.chain(unrewarded_relayers(3..=3))
+					.chain(unrewarded_relayers(2..=2))
 					.collect(),
 			),
 			Err(ReceptionConfirmationError::NonConsecutiveUnrewardedRelayerEntries),
diff --git a/bridges/modules/messages/src/proofs.rs b/bridges/modules/messages/src/proofs.rs
new file mode 100644
index 00000000000..18367029d72
--- /dev/null
+++ b/bridges/modules/messages/src/proofs.rs
@@ -0,0 +1,562 @@
+// Copyright 2019-2021 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/>.
+
+//! Tools for messages and delivery proof verification.
+
+use crate::{BridgedChainOf, BridgedHeaderChainOf, Config};
+
+use bp_header_chain::{HeaderChain, HeaderChainError};
+use bp_messages::{
+	source_chain::FromBridgedChainMessagesDeliveryProof,
+	target_chain::{FromBridgedChainMessagesProof, ProvedLaneMessages, ProvedMessages},
+	ChainWithMessages, InboundLaneData, LaneId, Message, MessageKey, MessageNonce, MessagePayload,
+	OutboundLaneData, VerificationError,
+};
+use bp_runtime::{
+	HashOf, HasherOf, RangeInclusiveExt, RawStorageProof, StorageProofChecker, StorageProofError,
+};
+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>);
+
+/// Verify proof of Bridged -> This chain messages.
+///
+/// This function is used when Bridged chain is directly using GRANDPA finality. For Bridged
+/// parachains, please use the `verify_messages_proof_from_parachain`.
+///
+/// The `messages_count` argument verification (sane limits) is supposed to be made
+/// 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>>>,
+	messages_count: u32,
+) -> Result<ProvedMessages<Message>, VerificationError> {
+	let FromBridgedChainMessagesProof {
+		bridged_header_hash,
+		storage_proof,
+		lane,
+		nonces_start,
+		nonces_end,
+	} = proof;
+	let mut parser: MessagesStorageProofAdapter<T, I> =
+		MessagesStorageProofAdapter::try_new_with_verified_storage_proof(
+			bridged_header_hash,
+			storage_proof,
+		)
+		.map_err(VerificationError::HeaderChain)?;
+	let nonces_range = nonces_start..=nonces_end;
+
+	// receiving proofs where end < begin is ok (if proof includes outbound lane state)
+	let messages_in_the_proof = nonces_range.checked_len().unwrap_or(0);
+	if messages_in_the_proof != MessageNonce::from(messages_count) {
+		return Err(VerificationError::MessagesCountMismatch)
+	}
+
+	// Read messages first. All messages that are claimed to be in the proof must
+	// be in the proof. So any error in `read_value`, or even missing value is fatal.
+	//
+	// Mind that we allow proofs with no messages if outbound lane state is proved.
+	let mut messages = Vec::with_capacity(messages_in_the_proof as _);
+	for nonce in nonces_range {
+		let message_key = MessageKey { lane_id: lane, nonce };
+		let message_payload = parser
+			.read_and_decode_message_payload(&message_key)
+			.map_err(VerificationError::MessageStorage)?;
+		messages.push(Message { key: message_key, payload: message_payload });
+	}
+
+	// Now let's check if proof contains outbound lane state proof. It is optional, so
+	// we simply ignore `read_value` errors and missing value.
+	let proved_lane_messages = ProvedLaneMessages {
+		lane_state: parser
+			.read_and_decode_outbound_lane_data(&lane)
+			.map_err(VerificationError::OutboundLaneStorage)?,
+		messages,
+	};
+
+	// Now we may actually check if the proof is empty or not.
+	if proved_lane_messages.lane_state.is_none() && proved_lane_messages.messages.is_empty() {
+		return Err(VerificationError::EmptyMessageProof)
+	}
+
+	// Check that the storage proof doesn't have any untouched keys.
+	parser.ensure_no_unused_keys().map_err(VerificationError::StorageProof)?;
+
+	// We only support single lane messages in this generated_schema
+	let mut proved_messages = ProvedMessages::new();
+	proved_messages.insert(lane, proved_lane_messages);
+
+	Ok(proved_messages)
+}
+
+/// 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> {
+	let FromBridgedChainMessagesDeliveryProof { bridged_header_hash, storage_proof, lane } = proof;
+	let mut parser: MessagesStorageProofAdapter<T, I> =
+		MessagesStorageProofAdapter::try_new_with_verified_storage_proof(
+			bridged_header_hash,
+			storage_proof,
+		)
+		.map_err(VerificationError::HeaderChain)?;
+	// Messages delivery proof is just proof of single storage key read => any error
+	// is fatal.
+	let storage_inbound_lane_data_key = bp_messages::storage_keys::inbound_lane_data_key(
+		T::ThisChain::WITH_CHAIN_MESSAGES_PALLET_NAME,
+		&lane,
+	);
+	let inbound_lane_data = parser
+		.read_and_decode_mandatory_value(&storage_inbound_lane_data_key)
+		.map_err(VerificationError::InboundLaneStorage)?;
+
+	// check that the storage proof doesn't have any untouched trie nodes
+	parser.ensure_no_unused_keys().map_err(VerificationError::StorageProof)?;
+
+	Ok((lane, inbound_lane_data))
+}
+
+/// Abstraction over storage proof manipulation, hiding implementation details of actual storage
+/// proofs.
+trait StorageProofAdapter<T: Config<I>, I: 'static> {
+	fn read_and_decode_mandatory_value<D: Decode>(
+		&mut self,
+		key: &impl AsRef<[u8]>,
+	) -> Result<D, StorageProofError>;
+	fn read_and_decode_optional_value<D: Decode>(
+		&mut self,
+		key: &impl AsRef<[u8]>,
+	) -> Result<Option<D>, StorageProofError>;
+	fn ensure_no_unused_keys(self) -> Result<(), StorageProofError>;
+
+	fn read_and_decode_outbound_lane_data(
+		&mut self,
+		lane_id: &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,
+			lane_id,
+		);
+		self.read_and_decode_optional_value(&storage_outbound_lane_data_key)
+	}
+
+	fn read_and_decode_message_payload(
+		&mut self,
+		message_key: &MessageKey,
+	) -> Result<MessagePayload, StorageProofError> {
+		let storage_message_key = bp_messages::storage_keys::message_key(
+			T::ThisChain::WITH_CHAIN_MESSAGES_PALLET_NAME,
+			&message_key.lane_id,
+			message_key.nonce,
+		);
+		self.read_and_decode_mandatory_value(&storage_message_key)
+	}
+}
+
+/// Actual storage proof adapter for messages proofs.
+type MessagesStorageProofAdapter<T, I> = StorageProofCheckerAdapter<T, I>;
+
+/// A `StorageProofAdapter` implementation for raw storage proofs.
+struct StorageProofCheckerAdapter<T: Config<I>, I: 'static> {
+	storage: StorageProofChecker<HasherOf<BridgedChainOf<T, I>>>,
+	_dummy: sp_std::marker::PhantomData<(T, I)>,
+}
+
+impl<T: Config<I>, I: 'static> StorageProofCheckerAdapter<T, I> {
+	fn try_new_with_verified_storage_proof(
+		bridged_header_hash: HashOf<BridgedChainOf<T, I>>,
+		storage_proof: RawStorageProof,
+	) -> Result<Self, HeaderChainError> {
+		BridgedHeaderChainOf::<T, I>::verify_storage_proof(bridged_header_hash, storage_proof).map(
+			|storage| StorageProofCheckerAdapter::<T, I> { storage, _dummy: Default::default() },
+		)
+	}
+}
+
+impl<T: Config<I>, I: 'static> StorageProofAdapter<T, I> for StorageProofCheckerAdapter<T, I> {
+	fn read_and_decode_optional_value<D: Decode>(
+		&mut self,
+		key: &impl AsRef<[u8]>,
+	) -> Result<Option<D>, StorageProofError> {
+		self.storage.read_and_decode_opt_value(key.as_ref())
+	}
+
+	fn read_and_decode_mandatory_value<D: Decode>(
+		&mut self,
+		key: &impl AsRef<[u8]>,
+	) -> Result<D, StorageProofError> {
+		self.storage.read_and_decode_mandatory_value(key.as_ref())
+	}
+
+	fn ensure_no_unused_keys(self) -> Result<(), StorageProofError> {
+		self.storage.ensure_no_unused_nodes()
+	}
+}
+
+#[cfg(test)]
+mod tests {
+	use super::*;
+	use crate::tests::{
+		messages_generation::{
+			encode_all_messages, encode_lane_data, generate_dummy_message,
+			prepare_messages_storage_proof,
+		},
+		mock::*,
+	};
+
+	use bp_header_chain::StoredHeaderDataBuilder;
+	use bp_runtime::{HeaderId, StorageProofError};
+	use codec::Encode;
+	use sp_runtime::traits::Header;
+
+	fn using_messages_proof<R>(
+		nonces_end: MessageNonce,
+		outbound_lane_data: Option<OutboundLaneData>,
+		encode_message: impl Fn(MessageNonce, &MessagePayload) -> Option<Vec<u8>>,
+		encode_outbound_lane_data: impl Fn(&OutboundLaneData) -> Vec<u8>,
+		add_duplicate_key: bool,
+		add_unused_key: bool,
+		test: impl Fn(FromBridgedChainMessagesProof<BridgedHeaderHash>) -> 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,
+		);
+
+		sp_io::TestExternalities::new(Default::default()).execute_with(move || {
+			let bridged_header = BridgedChainHeader::new(
+				0,
+				Default::default(),
+				state_root,
+				Default::default(),
+				Default::default(),
+			);
+			let bridged_header_hash = bridged_header.hash();
+
+			pallet_bridge_grandpa::BestFinalized::<TestRuntime>::put(HeaderId(
+				0,
+				bridged_header_hash,
+			));
+			pallet_bridge_grandpa::ImportedHeaders::<TestRuntime>::insert(
+				bridged_header_hash,
+				bridged_header.build(),
+			);
+			test(FromBridgedChainMessagesProof {
+				bridged_header_hash,
+				storage_proof,
+				lane: TEST_LANE_ID,
+				nonces_start: 1,
+				nonces_end,
+			})
+		})
+	}
+
+	#[test]
+	fn messages_proof_is_rejected_if_declared_less_than_actual_number_of_messages() {
+		assert_eq!(
+			using_messages_proof(
+				10,
+				None,
+				encode_all_messages,
+				encode_lane_data,
+				false,
+				false,
+				|proof| { verify_messages_proof::<TestRuntime, ()>(proof, 5) }
+			),
+			Err(VerificationError::MessagesCountMismatch),
+		);
+	}
+
+	#[test]
+	fn messages_proof_is_rejected_if_declared_more_than_actual_number_of_messages() {
+		assert_eq!(
+			using_messages_proof(
+				10,
+				None,
+				encode_all_messages,
+				encode_lane_data,
+				false,
+				false,
+				|proof| { verify_messages_proof::<TestRuntime, ()>(proof, 15) }
+			),
+			Err(VerificationError::MessagesCountMismatch),
+		);
+	}
+
+	#[test]
+	fn message_proof_is_rejected_if_header_is_missing_from_the_chain() {
+		assert_eq!(
+			using_messages_proof(
+				10,
+				None,
+				encode_all_messages,
+				encode_lane_data,
+				false,
+				false,
+				|proof| {
+					let bridged_header_hash =
+						pallet_bridge_grandpa::BestFinalized::<TestRuntime>::get().unwrap().1;
+					pallet_bridge_grandpa::ImportedHeaders::<TestRuntime>::remove(
+						bridged_header_hash,
+					);
+					verify_messages_proof::<TestRuntime, ()>(proof, 10)
+				}
+			),
+			Err(VerificationError::HeaderChain(HeaderChainError::UnknownHeader)),
+		);
+	}
+
+	#[test]
+	fn message_proof_is_rejected_if_header_state_root_mismatches() {
+		assert_eq!(
+			using_messages_proof(
+				10,
+				None,
+				encode_all_messages,
+				encode_lane_data,
+				false,
+				false,
+				|proof| {
+					let bridged_header_hash =
+						pallet_bridge_grandpa::BestFinalized::<TestRuntime>::get().unwrap().1;
+					pallet_bridge_grandpa::ImportedHeaders::<TestRuntime>::insert(
+						bridged_header_hash,
+						BridgedChainHeader::new(
+							0,
+							Default::default(),
+							Default::default(),
+							Default::default(),
+							Default::default(),
+						)
+						.build(),
+					);
+					verify_messages_proof::<TestRuntime, ()>(proof, 10)
+				}
+			),
+			Err(VerificationError::HeaderChain(HeaderChainError::StorageProof(
+				StorageProofError::StorageRootMismatch
+			))),
+		);
+	}
+
+	#[test]
+	fn message_proof_is_rejected_if_it_has_duplicate_trie_nodes() {
+		assert_eq!(
+			using_messages_proof(
+				10,
+				None,
+				encode_all_messages,
+				encode_lane_data,
+				true,
+				false,
+				|proof| { verify_messages_proof::<TestRuntime, ()>(proof, 10) },
+			),
+			Err(VerificationError::HeaderChain(HeaderChainError::StorageProof(
+				StorageProofError::DuplicateNodes
+			))),
+		);
+	}
+
+	#[test]
+	fn message_proof_is_rejected_if_it_has_unused_trie_nodes() {
+		assert_eq!(
+			using_messages_proof(
+				10,
+				None,
+				encode_all_messages,
+				encode_lane_data,
+				false,
+				true,
+				|proof| { verify_messages_proof::<TestRuntime, ()>(proof, 10) },
+			),
+			Err(VerificationError::StorageProof(StorageProofError::UnusedKey)),
+		);
+	}
+
+	#[test]
+	fn message_proof_is_rejected_if_required_message_is_missing() {
+		matches!(
+			using_messages_proof(
+				10,
+				None,
+				|n, m| if n != 5 { Some(m.encode()) } else { None },
+				encode_lane_data,
+				false,
+				false,
+				|proof| verify_messages_proof::<TestRuntime, ()>(proof, 10)
+			),
+			Err(VerificationError::MessageStorage(StorageProofError::EmptyVal)),
+		);
+	}
+
+	#[test]
+	fn message_proof_is_rejected_if_message_decode_fails() {
+		matches!(
+			using_messages_proof(
+				10,
+				None,
+				|n, m| {
+					let mut m = m.encode();
+					if n == 5 {
+						m = vec![42]
+					}
+					Some(m)
+				},
+				encode_lane_data,
+				false,
+				false,
+				|proof| verify_messages_proof::<TestRuntime, ()>(proof, 10),
+			),
+			Err(VerificationError::MessageStorage(StorageProofError::DecodeError)),
+		);
+	}
+
+	#[test]
+	fn message_proof_is_rejected_if_outbound_lane_state_decode_fails() {
+		matches!(
+			using_messages_proof(
+				10,
+				Some(OutboundLaneData {
+					oldest_unpruned_nonce: 1,
+					latest_received_nonce: 1,
+					latest_generated_nonce: 1,
+				}),
+				encode_all_messages,
+				|d| {
+					let mut d = d.encode();
+					d.truncate(1);
+					d
+				},
+				false,
+				false,
+				|proof| verify_messages_proof::<TestRuntime, ()>(proof, 10),
+			),
+			Err(VerificationError::OutboundLaneStorage(StorageProofError::DecodeError)),
+		);
+	}
+
+	#[test]
+	fn message_proof_is_rejected_if_it_is_empty() {
+		assert_eq!(
+			using_messages_proof(
+				0,
+				None,
+				encode_all_messages,
+				encode_lane_data,
+				false,
+				false,
+				|proof| { verify_messages_proof::<TestRuntime, ()>(proof, 0) },
+			),
+			Err(VerificationError::EmptyMessageProof),
+		);
+	}
+
+	#[test]
+	fn non_empty_message_proof_without_messages_is_accepted() {
+		assert_eq!(
+			using_messages_proof(
+				0,
+				Some(OutboundLaneData {
+					oldest_unpruned_nonce: 1,
+					latest_received_nonce: 1,
+					latest_generated_nonce: 1,
+				}),
+				encode_all_messages,
+				encode_lane_data,
+				false,
+				false,
+				|proof| verify_messages_proof::<TestRuntime, ()>(proof, 0),
+			),
+			Ok(vec![(
+				TEST_LANE_ID,
+				ProvedLaneMessages {
+					lane_state: Some(OutboundLaneData {
+						oldest_unpruned_nonce: 1,
+						latest_received_nonce: 1,
+						latest_generated_nonce: 1,
+					}),
+					messages: Vec::new(),
+				},
+			)]
+			.into_iter()
+			.collect()),
+		);
+	}
+
+	#[test]
+	fn non_empty_message_proof_is_accepted() {
+		assert_eq!(
+			using_messages_proof(
+				1,
+				Some(OutboundLaneData {
+					oldest_unpruned_nonce: 1,
+					latest_received_nonce: 1,
+					latest_generated_nonce: 1,
+				}),
+				encode_all_messages,
+				encode_lane_data,
+				false,
+				false,
+				|proof| verify_messages_proof::<TestRuntime, ()>(proof, 1),
+			),
+			Ok(vec![(
+				TEST_LANE_ID,
+				ProvedLaneMessages {
+					lane_state: Some(OutboundLaneData {
+						oldest_unpruned_nonce: 1,
+						latest_received_nonce: 1,
+						latest_generated_nonce: 1,
+					}),
+					messages: vec![Message {
+						key: MessageKey { lane_id: TEST_LANE_ID, nonce: 1 },
+						payload: vec![42],
+					}],
+				},
+			)]
+			.into_iter()
+			.collect()),
+		);
+	}
+
+	#[test]
+	fn verify_messages_proof_does_not_panic_if_messages_count_mismatches() {
+		assert_eq!(
+			using_messages_proof(
+				1,
+				None,
+				encode_all_messages,
+				encode_lane_data,
+				false,
+				false,
+				|mut proof| {
+					proof.nonces_end = u64::MAX;
+					verify_messages_proof::<TestRuntime, ()>(proof, u32::MAX)
+				},
+			),
+			Err(VerificationError::MessagesCountMismatch),
+		);
+	}
+}
diff --git a/bridges/bin/runtime-common/src/messages_generation.rs b/bridges/modules/messages/src/tests/messages_generation.rs
similarity index 62%
rename from bridges/bin/runtime-common/src/messages_generation.rs
rename to bridges/modules/messages/src/tests/messages_generation.rs
index c37aaa5d4d5..6c4867fa6de 100644
--- a/bridges/bin/runtime-common/src/messages_generation.rs
+++ b/bridges/modules/messages/src/tests/messages_generation.rs
@@ -16,17 +16,23 @@
 
 //! Helpers for generating message storage proofs, that are used by tests and by benchmarks.
 
-use crate::messages::{AccountIdOf, BridgedChain, HashOf, HasherOf, MessageBridge, ThisChain};
-
 use bp_messages::{
-	storage_keys, InboundLaneData, LaneId, MessageKey, MessageNonce, MessagePayload,
-	OutboundLaneData,
+	storage_keys, ChainWithMessages, InboundLaneData, LaneId, MessageKey, MessageNonce,
+	MessagePayload, OutboundLaneData,
+};
+use bp_runtime::{
+	grow_storage_value, record_all_trie_keys, AccountIdOf, Chain, HashOf, HasherOf,
+	RawStorageProof, UnverifiedStorageProofParams,
 };
-use bp_runtime::{record_all_trie_keys, RawStorageProof, StorageProofSize};
 use codec::Encode;
 use sp_std::{ops::RangeInclusive, prelude::*};
 use sp_trie::{trie_types::TrieDBMutBuilderV1, LayoutV1, MemoryDB, TrieMut};
 
+/// Dummy message generation function.
+pub fn generate_dummy_message(_: MessageNonce) -> MessagePayload {
+	vec![42]
+}
+
 /// Simple and correct message data encode function.
 pub fn encode_all_messages(_: MessageNonce, m: &MessagePayload) -> Option<Vec<u8>> {
 	Some(m.encode())
@@ -40,18 +46,20 @@ pub fn encode_lane_data(d: &OutboundLaneData) -> Vec<u8> {
 /// Prepare storage proof of given messages.
 ///
 /// Returns state trie root and nodes with prepared messages.
-pub fn prepare_messages_storage_proof<B>(
+#[allow(clippy::too_many_arguments)]
+pub fn prepare_messages_storage_proof<BridgedChain: Chain, ThisChain: ChainWithMessages>(
 	lane: LaneId,
 	message_nonces: RangeInclusive<MessageNonce>,
 	outbound_lane_data: Option<OutboundLaneData>,
-	size: StorageProofSize,
-	message_payload: MessagePayload,
+	proof_params: UnverifiedStorageProofParams,
+	generate_message: impl Fn(MessageNonce) -> MessagePayload,
 	encode_message: impl Fn(MessageNonce, &MessagePayload) -> Option<Vec<u8>>,
 	encode_outbound_lane_data: impl Fn(&OutboundLaneData) -> Vec<u8>,
-) -> (HashOf<BridgedChain<B>>, RawStorageProof)
+	add_duplicate_key: bool,
+	add_unused_key: bool,
+) -> (HashOf<BridgedChain>, RawStorageProof)
 where
-	B: MessageBridge,
-	HashOf<BridgedChain<B>>: Copy + Default,
+	HashOf<BridgedChain>: Copy + Default,
 {
 	// prepare Bridged chain storage with messages and (optionally) outbound lane state
 	let message_count = message_nonces.end().saturating_sub(*message_nonces.start()) + 1;
@@ -60,22 +68,22 @@ where
 	let mut mdb = MemoryDB::default();
 	{
 		let mut trie =
-			TrieDBMutBuilderV1::<HasherOf<BridgedChain<B>>>::new(&mut mdb, &mut root).build();
+			TrieDBMutBuilderV1::<HasherOf<BridgedChain>>::new(&mut mdb, &mut root).build();
 
 		// insert messages
 		for (i, nonce) in message_nonces.into_iter().enumerate() {
 			let message_key = MessageKey { lane_id: lane, nonce };
-			let message_payload = match encode_message(nonce, &message_payload) {
+			let message_payload = match encode_message(nonce, &generate_message(nonce)) {
 				Some(message_payload) =>
 					if i == 0 {
-						grow_trie_leaf_value(message_payload, size)
+						grow_storage_value(message_payload, &proof_params)
 					} else {
 						message_payload
 					},
 				None => continue,
 			};
 			let storage_key = storage_keys::message_key(
-				B::BRIDGED_MESSAGES_PALLET_NAME,
+				ThisChain::WITH_CHAIN_MESSAGES_PALLET_NAME,
 				&message_key.lane_id,
 				message_key.nonce,
 			)
@@ -89,8 +97,11 @@ where
 		// insert outbound lane state
 		if let Some(outbound_lane_data) = outbound_lane_data.as_ref().map(encode_outbound_lane_data)
 		{
-			let storage_key =
-				storage_keys::outbound_lane_data_key(B::BRIDGED_MESSAGES_PALLET_NAME, &lane).0;
+			let storage_key = storage_keys::outbound_lane_data_key(
+				ThisChain::WITH_CHAIN_MESSAGES_PALLET_NAME,
+				&lane,
+			)
+			.0;
 			trie.insert(&storage_key, &outbound_lane_data)
 				.map_err(|_| "TrieMut::insert has failed")
 				.expect("TrieMut::insert should not fail in benchmarks");
@@ -99,52 +110,54 @@ where
 	}
 
 	// generate storage proof to be delivered to This chain
-	let storage_proof = record_all_trie_keys::<LayoutV1<HasherOf<BridgedChain<B>>>, _>(&mdb, &root)
-		.map_err(|_| "record_all_trie_keys has failed")
-		.expect("record_all_trie_keys should not fail in benchmarks");
+	let mut storage_proof =
+		record_all_trie_keys::<LayoutV1<HasherOf<BridgedChain>>, _>(&mdb, &root)
+			.map_err(|_| "record_all_trie_keys has failed")
+			.expect("record_all_trie_keys should not fail in benchmarks");
+
+	if add_duplicate_key {
+		assert!(!storage_proof.is_empty());
+		let node = storage_proof.pop().unwrap();
+		storage_proof.push(node.clone());
+		storage_proof.push(node);
+	}
+
+	if add_unused_key {
+		storage_proof.push(b"unused_value".to_vec());
+	}
+
 	(root, storage_proof)
 }
 
 /// Prepare storage proof of given messages delivery.
 ///
 /// Returns state trie root and nodes with prepared messages.
-pub fn prepare_message_delivery_storage_proof<B>(
+pub fn prepare_message_delivery_storage_proof<BridgedChain: Chain, ThisChain: ChainWithMessages>(
 	lane: LaneId,
-	inbound_lane_data: InboundLaneData<AccountIdOf<ThisChain<B>>>,
-	size: StorageProofSize,
-) -> (HashOf<BridgedChain<B>>, RawStorageProof)
+	inbound_lane_data: InboundLaneData<AccountIdOf<ThisChain>>,
+	proof_params: UnverifiedStorageProofParams,
+) -> (HashOf<BridgedChain>, RawStorageProof)
 where
-	B: MessageBridge,
+	HashOf<BridgedChain>: Copy + Default,
 {
 	// prepare Bridged chain storage with inbound lane state
-	let storage_key = storage_keys::inbound_lane_data_key(B::BRIDGED_MESSAGES_PALLET_NAME, &lane).0;
+	let storage_key =
+		storage_keys::inbound_lane_data_key(ThisChain::WITH_CHAIN_MESSAGES_PALLET_NAME, &lane).0;
 	let mut root = Default::default();
 	let mut mdb = MemoryDB::default();
 	{
 		let mut trie =
-			TrieDBMutBuilderV1::<HasherOf<BridgedChain<B>>>::new(&mut mdb, &mut root).build();
-		let inbound_lane_data = grow_trie_leaf_value(inbound_lane_data.encode(), size);
+			TrieDBMutBuilderV1::<HasherOf<BridgedChain>>::new(&mut mdb, &mut root).build();
+		let inbound_lane_data = grow_storage_value(inbound_lane_data.encode(), &proof_params);
 		trie.insert(&storage_key, &inbound_lane_data)
 			.map_err(|_| "TrieMut::insert has failed")
 			.expect("TrieMut::insert should not fail in benchmarks");
 	}
 
 	// generate storage proof to be delivered to This chain
-	let storage_proof = record_all_trie_keys::<LayoutV1<HasherOf<BridgedChain<B>>>, _>(&mdb, &root)
+	let storage_proof = record_all_trie_keys::<LayoutV1<HasherOf<BridgedChain>>, _>(&mdb, &root)
 		.map_err(|_| "record_all_trie_keys has failed")
 		.expect("record_all_trie_keys should not fail in benchmarks");
 
 	(root, storage_proof)
 }
-
-/// Add extra data to the trie leaf value so that it'll be of given size.
-pub fn grow_trie_leaf_value(mut value: Vec<u8>, size: StorageProofSize) -> Vec<u8> {
-	match size {
-		StorageProofSize::Minimal(_) => (),
-		StorageProofSize::HasLargeLeaf(size) if size as usize > value.len() => {
-			value.extend(sp_std::iter::repeat(42u8).take(size as usize - value.len()));
-		},
-		StorageProofSize::HasLargeLeaf(_) => (),
-	}
-	value
-}
diff --git a/bridges/modules/messages/src/mock.rs b/bridges/modules/messages/src/tests/mock.rs
similarity index 62%
rename from bridges/modules/messages/src/mock.rs
rename to bridges/modules/messages/src/tests/mock.rs
index 3a1e0063d53..ffdd536830b 100644
--- a/bridges/modules/messages/src/mock.rs
+++ b/bridges/modules/messages/src/tests/mock.rs
@@ -17,30 +17,43 @@
 // From construct_runtime macro
 #![allow(clippy::from_over_into)]
 
-use crate::{Config, StoredMessagePayload};
+use crate::{
+	tests::messages_generation::{
+		encode_all_messages, encode_lane_data, prepare_message_delivery_storage_proof,
+		prepare_messages_storage_proof,
+	},
+	Config, StoredMessagePayload,
+};
 
+use bp_header_chain::{ChainWithGrandpa, StoredHeaderData};
 use bp_messages::{
 	calc_relayers_rewards,
-	source_chain::{DeliveryConfirmationPayments, OnMessagesDelivered, TargetHeaderChain},
+	source_chain::{
+		DeliveryConfirmationPayments, FromBridgedChainMessagesDeliveryProof, OnMessagesDelivered,
+	},
 	target_chain::{
-		DeliveryPayments, DispatchMessage, DispatchMessageData, MessageDispatch,
-		ProvedLaneMessages, ProvedMessages, SourceHeaderChain,
+		DeliveryPayments, DispatchMessage, DispatchMessageData, FromBridgedChainMessagesProof,
+		MessageDispatch,
 	},
-	DeliveredMessages, InboundLaneData, LaneId, Message, MessageKey, MessageNonce,
-	UnrewardedRelayer, UnrewardedRelayersState, VerificationError,
+	ChainWithMessages, DeliveredMessages, InboundLaneData, LaneId, Message, MessageKey,
+	MessageNonce, OutboundLaneData, UnrewardedRelayer, UnrewardedRelayersState,
+};
+use bp_runtime::{
+	messages::MessageDispatchResult, Chain, ChainId, Size, UnverifiedStorageProofParams,
 };
-use bp_runtime::{messages::MessageDispatchResult, Size};
 use codec::{Decode, Encode};
 use frame_support::{
 	derive_impl, parameter_types,
 	weights::{constants::RocksDbWeight, Weight},
 };
 use scale_info::TypeInfo;
-use sp_runtime::BuildStorage;
-use std::{
-	collections::{BTreeMap, VecDeque},
-	ops::RangeInclusive,
+use sp_core::H256;
+use sp_runtime::{
+	testing::Header as SubstrateHeader,
+	traits::{BlakeTwo256, ConstU32},
+	BuildStorage, StateVersion,
 };
+use std::{collections::VecDeque, ops::RangeInclusive};
 
 pub type AccountId = u64;
 pub type Balance = u64;
@@ -62,6 +75,77 @@ pub type TestMessageFee = u64;
 pub type TestRelayer = u64;
 pub type TestDispatchLevelResult = ();
 
+pub struct ThisChain;
+
+impl Chain for ThisChain {
+	const ID: ChainId = *b"ttch";
+
+	type BlockNumber = u64;
+	type Hash = H256;
+	type Hasher = BlakeTwo256;
+	type Header = SubstrateHeader;
+	type AccountId = AccountId;
+	type Balance = Balance;
+	type Nonce = u64;
+	type Signature = sp_runtime::MultiSignature;
+	const STATE_VERSION: StateVersion = StateVersion::V1;
+
+	fn max_extrinsic_size() -> u32 {
+		u32::MAX
+	}
+
+	fn max_extrinsic_weight() -> Weight {
+		Weight::MAX
+	}
+}
+
+impl ChainWithMessages for ThisChain {
+	const WITH_CHAIN_MESSAGES_PALLET_NAME: &'static str = "WithThisChainBridgeMessages";
+	const MAX_UNREWARDED_RELAYERS_IN_CONFIRMATION_TX: MessageNonce = 16;
+	const MAX_UNCONFIRMED_MESSAGES_IN_CONFIRMATION_TX: MessageNonce = 128;
+}
+
+pub struct BridgedChain;
+
+pub type BridgedHeaderHash = H256;
+pub type BridgedChainHeader = SubstrateHeader;
+
+impl Chain for BridgedChain {
+	const ID: ChainId = *b"tbch";
+
+	type BlockNumber = u64;
+	type Hash = BridgedHeaderHash;
+	type Hasher = BlakeTwo256;
+	type Header = BridgedChainHeader;
+	type AccountId = TestRelayer;
+	type Balance = Balance;
+	type Nonce = u64;
+	type Signature = sp_runtime::MultiSignature;
+	const STATE_VERSION: StateVersion = StateVersion::V1;
+
+	fn max_extrinsic_size() -> u32 {
+		4096
+	}
+
+	fn max_extrinsic_weight() -> Weight {
+		Weight::MAX
+	}
+}
+
+impl ChainWithGrandpa for BridgedChain {
+	const WITH_CHAIN_GRANDPA_PALLET_NAME: &'static str = "WithBridgedChainBridgeGrandpa";
+	const MAX_AUTHORITIES_COUNT: u32 = 16;
+	const REASONABLE_HEADERS_IN_JUSTIFICATION_ANCESTRY: u32 = 4;
+	const MAX_MANDATORY_HEADER_SIZE: u32 = 4096;
+	const AVERAGE_HEADER_SIZE: u32 = 4096;
+}
+
+impl ChainWithMessages for BridgedChain {
+	const WITH_CHAIN_MESSAGES_PALLET_NAME: &'static str = "WithBridgedChainBridgeMessages";
+	const MAX_UNREWARDED_RELAYERS_IN_CONFIRMATION_TX: MessageNonce = 16;
+	const MAX_UNCONFIRMED_MESSAGES_IN_CONFIRMATION_TX: MessageNonce = 128;
+}
+
 type Block = frame_system::mocking::MockBlock<TestRuntime>;
 
 use crate as pallet_bridge_messages;
@@ -71,6 +155,7 @@ frame_support::construct_runtime! {
 	{
 		System: frame_system::{Pallet, Call, Config<T>, Storage, Event<T>},
 		Balances: pallet_balances::{Pallet, Call, Event<T>},
+		BridgedChainGrandpa: pallet_bridge_grandpa::{Pallet, Call, Event<T>},
 		Messages: pallet_bridge_messages::{Pallet, Call, Event<T>},
 	}
 }
@@ -89,10 +174,17 @@ impl pallet_balances::Config for TestRuntime {
 	type AccountStore = System;
 }
 
+impl pallet_bridge_grandpa::Config for TestRuntime {
+	type RuntimeEvent = RuntimeEvent;
+	type BridgedChain = BridgedChain;
+	type MaxFreeHeadersPerBlock = ConstU32<4>;
+	type FreeHeadersInterval = ConstU32<1_024>;
+	type HeadersToKeep = ConstU32<8>;
+	type WeightInfo = pallet_bridge_grandpa::weights::BridgeWeight<TestRuntime>;
+}
+
 parameter_types! {
 	pub const MaxMessagesToPruneAtOnce: u64 = 10;
-	pub const MaxUnrewardedRelayerEntriesAtInboundLane: u64 = 16;
-	pub const MaxUnconfirmedMessagesAtInboundLane: u64 = 128;
 	pub const TestBridgedChainId: bp_runtime::ChainId = *b"test";
 	pub const ActiveOutboundLanes: &'static [LaneId] = &[TEST_LANE_ID, TEST_LANE_ID_2];
 }
@@ -103,24 +195,22 @@ pub type TestWeightInfo = ();
 impl Config for TestRuntime {
 	type RuntimeEvent = RuntimeEvent;
 	type WeightInfo = TestWeightInfo;
+
+	type ThisChain = ThisChain;
+	type BridgedChain = BridgedChain;
+	type BridgedHeaderChain = BridgedChainGrandpa;
+
 	type ActiveOutboundLanes = ActiveOutboundLanes;
-	type MaxUnrewardedRelayerEntriesAtInboundLane = MaxUnrewardedRelayerEntriesAtInboundLane;
-	type MaxUnconfirmedMessagesAtInboundLane = MaxUnconfirmedMessagesAtInboundLane;
 
-	type MaximalOutboundPayloadSize = frame_support::traits::ConstU32<MAX_OUTBOUND_PAYLOAD_SIZE>;
 	type OutboundPayload = TestPayload;
 
 	type InboundPayload = TestPayload;
-	type InboundRelayer = TestRelayer;
 	type DeliveryPayments = TestDeliveryPayments;
 
-	type TargetHeaderChain = TestTargetHeaderChain;
 	type DeliveryConfirmationPayments = TestDeliveryConfirmationPayments;
 	type OnMessagesDelivered = TestOnMessagesDelivered;
 
-	type SourceHeaderChain = TestSourceHeaderChain;
 	type MessageDispatch = TestMessageDispatch;
-	type BridgedChainId = TestBridgedChainId;
 }
 
 #[cfg(feature = "runtime-benchmarks")]
@@ -131,29 +221,26 @@ impl crate::benchmarking::Config<()> for TestRuntime {
 
 	fn prepare_message_proof(
 		params: crate::benchmarking::MessageProofParams,
-	) -> (TestMessagesProof, Weight) {
-		// in mock run we only care about benchmarks correctness, not the benchmark results
-		// => ignore size related arguments
-		let (messages, total_dispatch_weight) =
-			params.message_nonces.into_iter().map(|n| message(n, REGULAR_PAYLOAD)).fold(
-				(Vec::new(), Weight::zero()),
-				|(mut messages, total_dispatch_weight), message| {
-					let weight = REGULAR_PAYLOAD.declared_weight;
-					messages.push(message);
-					(messages, total_dispatch_weight.saturating_add(weight))
-				},
-			);
-		let mut proof: TestMessagesProof = Ok(messages).into();
-		proof.result.as_mut().unwrap().get_mut(0).unwrap().1.lane_state = params.outbound_lane_data;
-		(proof, total_dispatch_weight)
+	) -> (FromBridgedChainMessagesProof<BridgedHeaderHash>, Weight) {
+		use bp_runtime::RangeInclusiveExt;
+
+		let dispatch_weight =
+			REGULAR_PAYLOAD.declared_weight * params.message_nonces.checked_len().unwrap_or(0);
+		(
+			*prepare_messages_proof(
+				params.message_nonces.into_iter().map(|n| message(n, REGULAR_PAYLOAD)).collect(),
+				params.outbound_lane_data,
+			),
+			dispatch_weight,
+		)
 	}
 
 	fn prepare_message_delivery_proof(
 		params: crate::benchmarking::MessageDeliveryProofParams<AccountId>,
-	) -> TestMessagesDeliveryProof {
+	) -> FromBridgedChainMessagesDeliveryProof<BridgedHeaderHash> {
 		// in mock run we only care about benchmarks correctness, not the benchmark results
 		// => ignore size related arguments
-		TestMessagesDeliveryProof(Ok((params.lane, params.inbound_lane_data)))
+		prepare_messages_delivery_proof(params.lane, params.inbound_lane_data)
 	}
 
 	fn is_relayer_rewarded(_relayer: &AccountId) -> bool {
@@ -167,9 +254,6 @@ impl Size for TestPayload {
 	}
 }
 
-/// Maximal outbound payload size.
-pub const MAX_OUTBOUND_PAYLOAD_SIZE: u32 = 4096;
-
 /// Account that has balance to use in tests.
 pub const ENDOWED_ACCOUNT: AccountId = 0xDEAD;
 
@@ -182,9 +266,6 @@ pub const TEST_RELAYER_B: AccountId = 101;
 /// Account id of additional test relayer - C.
 pub const TEST_RELAYER_C: AccountId = 102;
 
-/// Error that is returned by all test implementations.
-pub const TEST_ERROR: &str = "Test error";
-
 /// Lane that we're using in tests.
 pub const TEST_LANE_ID: LaneId = LaneId([0, 0, 0, 1]);
 
@@ -197,71 +278,6 @@ pub const TEST_LANE_ID_3: LaneId = LaneId([0, 0, 0, 3]);
 /// Regular message payload.
 pub const REGULAR_PAYLOAD: TestPayload = message_payload(0, 50);
 
-/// Payload that is rejected by `TestTargetHeaderChain`.
-pub const PAYLOAD_REJECTED_BY_TARGET_CHAIN: TestPayload = message_payload(1, 50);
-
-/// Vec of proved messages, grouped by lane.
-pub type MessagesByLaneVec = Vec<(LaneId, ProvedLaneMessages<Message>)>;
-
-/// Test messages proof.
-#[derive(Debug, Encode, Decode, Clone, PartialEq, Eq, TypeInfo)]
-pub struct TestMessagesProof {
-	pub result: Result<MessagesByLaneVec, ()>,
-}
-
-impl Size for TestMessagesProof {
-	fn size(&self) -> u32 {
-		0
-	}
-}
-
-impl From<Result<Vec<Message>, ()>> for TestMessagesProof {
-	fn from(result: Result<Vec<Message>, ()>) -> Self {
-		Self {
-			result: result.map(|messages| {
-				let mut messages_by_lane: BTreeMap<LaneId, ProvedLaneMessages<Message>> =
-					BTreeMap::new();
-				for message in messages {
-					messages_by_lane.entry(message.key.lane_id).or_default().messages.push(message);
-				}
-				messages_by_lane.into_iter().collect()
-			}),
-		}
-	}
-}
-
-/// Messages delivery proof used in tests.
-#[derive(Debug, Encode, Decode, Eq, Clone, PartialEq, TypeInfo)]
-pub struct TestMessagesDeliveryProof(pub Result<(LaneId, InboundLaneData<TestRelayer>), ()>);
-
-impl Size for TestMessagesDeliveryProof {
-	fn size(&self) -> u32 {
-		0
-	}
-}
-
-/// Target header chain that is used in tests.
-#[derive(Debug, Default)]
-pub struct TestTargetHeaderChain;
-
-impl TargetHeaderChain<TestPayload, TestRelayer> for TestTargetHeaderChain {
-	type MessagesDeliveryProof = TestMessagesDeliveryProof;
-
-	fn verify_message(payload: &TestPayload) -> Result<(), VerificationError> {
-		if *payload == PAYLOAD_REJECTED_BY_TARGET_CHAIN {
-			Err(VerificationError::Other(TEST_ERROR))
-		} else {
-			Ok(())
-		}
-	}
-
-	fn verify_messages_delivery_proof(
-		proof: Self::MessagesDeliveryProof,
-	) -> Result<(LaneId, InboundLaneData<TestRelayer>), VerificationError> {
-		proof.0.map_err(|_| VerificationError::Other(TEST_ERROR))
-	}
-}
-
 /// Reward payments at the target chain during delivery transaction.
 #[derive(Debug, Default)]
 pub struct TestDeliveryPayments;
@@ -322,24 +338,6 @@ impl DeliveryConfirmationPayments<AccountId> for TestDeliveryConfirmationPayment
 	}
 }
 
-/// Source header chain that is used in tests.
-#[derive(Debug)]
-pub struct TestSourceHeaderChain;
-
-impl SourceHeaderChain for TestSourceHeaderChain {
-	type MessagesProof = TestMessagesProof;
-
-	fn verify_messages_proof(
-		proof: Self::MessagesProof,
-		_messages_count: u32,
-	) -> Result<ProvedMessages<Message>, VerificationError> {
-		proof
-			.result
-			.map(|proof| proof.into_iter().collect())
-			.map_err(|_| VerificationError::Other(TEST_ERROR))
-	}
-}
-
 /// Test message dispatcher.
 #[derive(Debug)]
 pub struct TestMessageDispatch;
@@ -458,3 +456,75 @@ pub fn new_test_ext() -> sp_io::TestExternalities {
 pub fn run_test<T>(test: impl FnOnce() -> T) -> T {
 	new_test_ext().execute_with(test)
 }
+
+/// Prepare valid storage proof for given messages and insert appropriate header to the
+/// bridged header chain.
+///
+/// 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>,
+	outbound_lane_data: Option<OutboundLaneData>,
+) -> Box<FromBridgedChainMessagesProof<BridgedHeaderHash>> {
+	// 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>(
+		TEST_LANE_ID,
+		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();
+	pallet_bridge_grandpa::ImportedHeaders::<TestRuntime>::insert(
+		bridged_header_hash,
+		StoredHeaderData { number: 0, state_root: storage_root },
+	);
+
+	Box::new(FromBridgedChainMessagesProof::<BridgedHeaderHash> {
+		bridged_header_hash,
+		storage_proof,
+		lane,
+		nonces_start,
+		nonces_end,
+	})
+}
+
+/// Prepare valid storage proof for given messages and insert appropriate header to the
+/// bridged header chain.
+///
+/// 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,
+	inbound_lane_data: InboundLaneData<AccountId>,
+) -> FromBridgedChainMessagesDeliveryProof<BridgedHeaderHash> {
+	// first - let's generate storage proof
+	let (storage_root, storage_proof) =
+		prepare_message_delivery_storage_proof::<BridgedChain, ThisChain>(
+			lane,
+			inbound_lane_data,
+			UnverifiedStorageProofParams::default(),
+		);
+
+	// let's now insert bridged chain header into the storage
+	let bridged_header_hash = Default::default();
+	pallet_bridge_grandpa::ImportedHeaders::<TestRuntime>::insert(
+		bridged_header_hash,
+		StoredHeaderData { number: 0, state_root: storage_root },
+	);
+
+	FromBridgedChainMessagesDeliveryProof::<BridgedHeaderHash> {
+		bridged_header_hash,
+		storage_proof,
+		lane,
+	}
+}
diff --git a/bridges/modules/messages/src/tests/mod.rs b/bridges/modules/messages/src/tests/mod.rs
new file mode 100644
index 00000000000..c3bde5fc275
--- /dev/null
+++ b/bridges/modules/messages/src/tests/mod.rs
@@ -0,0 +1,26 @@
+// Copyright 2019-2021 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/>.
+
+//! Tests and test helpers for messages pallet.
+
+#![cfg(any(feature = "test-helpers", test))]
+
+#[cfg(test)]
+pub(crate) mod mock;
+#[cfg(test)]
+mod pallet_tests;
+
+pub mod messages_generation;
diff --git a/bridges/modules/messages/src/tests/pallet_tests.rs b/bridges/modules/messages/src/tests/pallet_tests.rs
new file mode 100644
index 00000000000..42e1042717d
--- /dev/null
+++ b/bridges/modules/messages/src/tests/pallet_tests.rs
@@ -0,0 +1,1100 @@
+// Copyright 2019-2021 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/>.
+
+//! Pallet-level tests.
+
+use crate::{
+	outbound_lane,
+	outbound_lane::ReceptionConfirmationError,
+	tests::mock::{self, RuntimeEvent as TestEvent, *},
+	weights_ext::WeightInfoExt,
+	Call, Config, Error, Event, InboundLanes, MaybeOutboundLanesCount, OutboundLanes,
+	OutboundMessages, Pallet, PalletOperatingMode, PalletOwner, RuntimeInboundLaneStorage,
+	StoredInboundLaneData,
+};
+
+use bp_messages::{
+	source_chain::{FromBridgedChainMessagesDeliveryProof, MessagesBridge},
+	target_chain::FromBridgedChainMessagesProof,
+	BridgeMessagesCall, ChainWithMessages, DeliveredMessages, InboundLaneData,
+	InboundMessageDetails, LaneId, MessageKey, MessageNonce, MessagesOperatingMode,
+	OutboundLaneData, OutboundMessageDetails, UnrewardedRelayer, UnrewardedRelayersState,
+	VerificationError,
+};
+use bp_runtime::{BasicOperatingMode, PreComputedSize, RangeInclusiveExt, Size};
+use bp_test_utils::generate_owned_bridge_module_tests;
+use codec::Encode;
+use frame_support::{
+	assert_noop, assert_ok,
+	dispatch::Pays,
+	storage::generator::{StorageMap, StorageValue},
+	traits::Hooks,
+	weights::Weight,
+};
+use frame_system::{EventRecord, Pallet as System, Phase};
+use sp_core::Get;
+use sp_runtime::DispatchError;
+
+fn get_ready_for_events() {
+	System::<TestRuntime>::set_block_number(1);
+	System::<TestRuntime>::reset_events();
+}
+
+fn send_regular_message(lane_id: LaneId) {
+	get_ready_for_events();
+
+	let outbound_lane = outbound_lane::<TestRuntime, ()>(lane_id);
+	let message_nonce = outbound_lane.data().latest_generated_nonce + 1;
+	let prev_enqueued_messages = outbound_lane.data().queued_messages().saturating_len();
+	let valid_message = Pallet::<TestRuntime, ()>::validate_message(lane_id, &REGULAR_PAYLOAD)
+		.expect("validate_message has failed");
+	let artifacts = Pallet::<TestRuntime, ()>::send_message(valid_message);
+	assert_eq!(artifacts.enqueued_messages, prev_enqueued_messages + 1);
+
+	// check event with assigned nonce
+	assert_eq!(
+		System::<TestRuntime>::events(),
+		vec![EventRecord {
+			phase: Phase::Initialization,
+			event: TestEvent::Messages(Event::MessageAccepted { lane_id, nonce: message_nonce }),
+			topics: vec![],
+		}],
+	);
+}
+
+fn receive_messages_delivery_proof() {
+	System::<TestRuntime>::set_block_number(1);
+	System::<TestRuntime>::reset_events();
+
+	assert_ok!(Pallet::<TestRuntime>::receive_messages_delivery_proof(
+		RuntimeOrigin::signed(1),
+		prepare_messages_delivery_proof(
+			TEST_LANE_ID,
+			InboundLaneData {
+				last_confirmed_nonce: 1,
+				relayers: vec![UnrewardedRelayer {
+					relayer: 0,
+					messages: DeliveredMessages::new(1),
+				}]
+				.into(),
+			},
+		),
+		UnrewardedRelayersState {
+			unrewarded_relayer_entries: 1,
+			messages_in_oldest_entry: 1,
+			total_messages: 1,
+			last_delivered_nonce: 1,
+		},
+	));
+
+	assert_eq!(
+		System::<TestRuntime>::events(),
+		vec![EventRecord {
+			phase: Phase::Initialization,
+			event: TestEvent::Messages(Event::MessagesDelivered {
+				lane_id: TEST_LANE_ID,
+				messages: DeliveredMessages::new(1),
+			}),
+			topics: vec![],
+		}],
+	);
+}
+
+#[test]
+fn pallet_rejects_transactions_if_halted() {
+	run_test(|| {
+		// send message first to be able to check that delivery_proof fails later
+		send_regular_message(TEST_LANE_ID);
+
+		PalletOperatingMode::<TestRuntime, ()>::put(MessagesOperatingMode::Basic(
+			BasicOperatingMode::Halted,
+		));
+
+		assert_noop!(
+			Pallet::<TestRuntime, ()>::validate_message(TEST_LANE_ID, &REGULAR_PAYLOAD),
+			Error::<TestRuntime, ()>::NotOperatingNormally,
+		);
+
+		let messages_proof = prepare_messages_proof(vec![message(2, REGULAR_PAYLOAD)], None);
+		assert_noop!(
+			Pallet::<TestRuntime>::receive_messages_proof(
+				RuntimeOrigin::signed(1),
+				TEST_RELAYER_A,
+				messages_proof,
+				1,
+				REGULAR_PAYLOAD.declared_weight,
+			),
+			Error::<TestRuntime, ()>::BridgeModule(bp_runtime::OwnedBridgeModuleError::Halted),
+		);
+
+		let delivery_proof = prepare_messages_delivery_proof(
+			TEST_LANE_ID,
+			InboundLaneData {
+				last_confirmed_nonce: 1,
+				relayers: vec![unrewarded_relayer(1, 1, TEST_RELAYER_A)].into(),
+			},
+		);
+		assert_noop!(
+			Pallet::<TestRuntime>::receive_messages_delivery_proof(
+				RuntimeOrigin::signed(1),
+				delivery_proof,
+				UnrewardedRelayersState {
+					unrewarded_relayer_entries: 1,
+					messages_in_oldest_entry: 1,
+					total_messages: 1,
+					last_delivered_nonce: 1,
+				},
+			),
+			Error::<TestRuntime, ()>::BridgeModule(bp_runtime::OwnedBridgeModuleError::Halted),
+		);
+	});
+}
+
+#[test]
+fn receive_messages_fails_if_dispatcher_is_inactive() {
+	run_test(|| {
+		TestMessageDispatch::deactivate();
+		let proof = prepare_messages_proof(vec![message(1, REGULAR_PAYLOAD)], None);
+		assert_noop!(
+			Pallet::<TestRuntime>::receive_messages_proof(
+				RuntimeOrigin::signed(1),
+				TEST_RELAYER_A,
+				proof,
+				1,
+				REGULAR_PAYLOAD.declared_weight,
+			),
+			Error::<TestRuntime, ()>::MessageDispatchInactive,
+		);
+	});
+}
+
+#[test]
+fn pallet_rejects_new_messages_in_rejecting_outbound_messages_operating_mode() {
+	run_test(|| {
+		// send message first to be able to check that delivery_proof fails later
+		send_regular_message(TEST_LANE_ID);
+
+		PalletOperatingMode::<TestRuntime, ()>::put(
+			MessagesOperatingMode::RejectingOutboundMessages,
+		);
+
+		assert_noop!(
+			Pallet::<TestRuntime, ()>::validate_message(TEST_LANE_ID, &REGULAR_PAYLOAD),
+			Error::<TestRuntime, ()>::NotOperatingNormally,
+		);
+
+		assert_ok!(Pallet::<TestRuntime>::receive_messages_proof(
+			RuntimeOrigin::signed(1),
+			TEST_RELAYER_A,
+			prepare_messages_proof(vec![message(1, REGULAR_PAYLOAD)], None),
+			1,
+			REGULAR_PAYLOAD.declared_weight,
+		),);
+
+		assert_ok!(Pallet::<TestRuntime>::receive_messages_delivery_proof(
+			RuntimeOrigin::signed(1),
+			prepare_messages_delivery_proof(
+				TEST_LANE_ID,
+				InboundLaneData {
+					last_confirmed_nonce: 1,
+					relayers: vec![unrewarded_relayer(1, 1, TEST_RELAYER_A)].into(),
+				},
+			),
+			UnrewardedRelayersState {
+				unrewarded_relayer_entries: 1,
+				messages_in_oldest_entry: 1,
+				total_messages: 1,
+				last_delivered_nonce: 1,
+			},
+		));
+	});
+}
+
+#[test]
+fn send_message_works() {
+	run_test(|| {
+		send_regular_message(TEST_LANE_ID);
+	});
+}
+
+#[test]
+fn send_message_rejects_too_large_message() {
+	run_test(|| {
+		let mut message_payload = message_payload(1, 0);
+		// the payload isn't simply extra, so it'll definitely overflow
+		// `max_outbound_payload_size` if we add `max_outbound_payload_size` bytes to extra
+		let max_outbound_payload_size = BridgedChain::maximal_incoming_message_size();
+		message_payload
+			.extra
+			.extend_from_slice(&vec![0u8; max_outbound_payload_size as usize]);
+		assert_noop!(
+			Pallet::<TestRuntime, ()>::validate_message(TEST_LANE_ID, &message_payload.clone(),),
+			Error::<TestRuntime, ()>::MessageRejectedByPallet(VerificationError::MessageTooLarge),
+		);
+
+		// let's check that we're able to send `max_outbound_payload_size` messages
+		while message_payload.encoded_size() as u32 > max_outbound_payload_size {
+			message_payload.extra.pop();
+		}
+		assert_eq!(message_payload.encoded_size() as u32, max_outbound_payload_size);
+
+		let valid_message =
+			Pallet::<TestRuntime, ()>::validate_message(TEST_LANE_ID, &message_payload)
+				.expect("validate_message has failed");
+		Pallet::<TestRuntime, ()>::send_message(valid_message);
+	})
+}
+
+#[test]
+fn receive_messages_proof_works() {
+	run_test(|| {
+		assert_ok!(Pallet::<TestRuntime>::receive_messages_proof(
+			RuntimeOrigin::signed(1),
+			TEST_RELAYER_A,
+			prepare_messages_proof(vec![message(1, REGULAR_PAYLOAD)], None),
+			1,
+			REGULAR_PAYLOAD.declared_weight,
+		));
+
+		assert_eq!(InboundLanes::<TestRuntime>::get(TEST_LANE_ID).0.last_delivered_nonce(), 1);
+
+		assert!(TestDeliveryPayments::is_reward_paid(1));
+	});
+}
+
+#[test]
+fn receive_messages_proof_updates_confirmed_message_nonce() {
+	run_test(|| {
+		// say we have received 10 messages && last confirmed message is 8
+		InboundLanes::<TestRuntime, ()>::insert(
+			TEST_LANE_ID,
+			InboundLaneData {
+				last_confirmed_nonce: 8,
+				relayers: vec![
+					unrewarded_relayer(9, 9, TEST_RELAYER_A),
+					unrewarded_relayer(10, 10, TEST_RELAYER_B),
+				]
+				.into(),
+			},
+		);
+		assert_eq!(
+			inbound_unrewarded_relayers_state(TEST_LANE_ID),
+			UnrewardedRelayersState {
+				unrewarded_relayer_entries: 2,
+				messages_in_oldest_entry: 1,
+				total_messages: 2,
+				last_delivered_nonce: 10,
+			},
+		);
+
+		// message proof includes outbound lane state with latest confirmed message updated to 9
+		assert_ok!(Pallet::<TestRuntime>::receive_messages_proof(
+			RuntimeOrigin::signed(1),
+			TEST_RELAYER_A,
+			prepare_messages_proof(
+				vec![message(11, REGULAR_PAYLOAD)],
+				Some(OutboundLaneData { latest_received_nonce: 9, ..Default::default() }),
+			),
+			1,
+			REGULAR_PAYLOAD.declared_weight,
+		));
+
+		assert_eq!(
+			InboundLanes::<TestRuntime>::get(TEST_LANE_ID).0,
+			InboundLaneData {
+				last_confirmed_nonce: 9,
+				relayers: vec![
+					unrewarded_relayer(10, 10, TEST_RELAYER_B),
+					unrewarded_relayer(11, 11, TEST_RELAYER_A)
+				]
+				.into(),
+			},
+		);
+		assert_eq!(
+			inbound_unrewarded_relayers_state(TEST_LANE_ID),
+			UnrewardedRelayersState {
+				unrewarded_relayer_entries: 2,
+				messages_in_oldest_entry: 1,
+				total_messages: 2,
+				last_delivered_nonce: 11,
+			},
+		);
+	});
+}
+
+#[test]
+fn receive_messages_proof_does_not_accept_message_if_dispatch_weight_is_not_enough() {
+	run_test(|| {
+		let proof = prepare_messages_proof(vec![message(1, REGULAR_PAYLOAD)], None);
+		let mut declared_weight = REGULAR_PAYLOAD.declared_weight;
+		*declared_weight.ref_time_mut() -= 1;
+
+		assert_noop!(
+			Pallet::<TestRuntime>::receive_messages_proof(
+				RuntimeOrigin::signed(1),
+				TEST_RELAYER_A,
+				proof,
+				1,
+				declared_weight,
+			),
+			Error::<TestRuntime, ()>::InsufficientDispatchWeight
+		);
+		assert_eq!(InboundLanes::<TestRuntime>::get(TEST_LANE_ID).last_delivered_nonce(), 0);
+	});
+}
+
+#[test]
+fn receive_messages_proof_rejects_invalid_proof() {
+	run_test(|| {
+		let mut proof = prepare_messages_proof(vec![message(1, REGULAR_PAYLOAD)], None);
+		proof.nonces_end += 1;
+
+		assert_noop!(
+			Pallet::<TestRuntime, ()>::receive_messages_proof(
+				RuntimeOrigin::signed(1),
+				TEST_RELAYER_A,
+				proof,
+				1,
+				Weight::zero(),
+			),
+			Error::<TestRuntime, ()>::InvalidMessagesProof,
+		);
+	});
+}
+
+#[test]
+fn receive_messages_proof_rejects_proof_with_too_many_messages() {
+	run_test(|| {
+		let proof = prepare_messages_proof(vec![message(1, REGULAR_PAYLOAD)], None);
+		assert_noop!(
+			Pallet::<TestRuntime, ()>::receive_messages_proof(
+				RuntimeOrigin::signed(1),
+				TEST_RELAYER_A,
+				proof,
+				u32::MAX,
+				Weight::zero(),
+			),
+			Error::<TestRuntime, ()>::TooManyMessagesInTheProof,
+		);
+	});
+}
+
+#[test]
+fn receive_messages_delivery_proof_works() {
+	run_test(|| {
+		send_regular_message(TEST_LANE_ID);
+		receive_messages_delivery_proof();
+
+		assert_eq!(OutboundLanes::<TestRuntime, ()>::get(TEST_LANE_ID).latest_received_nonce, 1,);
+	});
+}
+
+#[test]
+fn receive_messages_delivery_proof_rewards_relayers() {
+	run_test(|| {
+		send_regular_message(TEST_LANE_ID);
+		send_regular_message(TEST_LANE_ID);
+
+		// this reports delivery of message 1 => reward is paid to TEST_RELAYER_A
+		let single_message_delivery_proof = prepare_messages_delivery_proof(
+			TEST_LANE_ID,
+			InboundLaneData {
+				relayers: vec![unrewarded_relayer(1, 1, TEST_RELAYER_A)].into(),
+				..Default::default()
+			},
+		);
+		let single_message_delivery_proof_size = single_message_delivery_proof.size();
+		let result = Pallet::<TestRuntime>::receive_messages_delivery_proof(
+			RuntimeOrigin::signed(1),
+			single_message_delivery_proof,
+			UnrewardedRelayersState {
+				unrewarded_relayer_entries: 1,
+				messages_in_oldest_entry: 1,
+				total_messages: 1,
+				last_delivered_nonce: 1,
+			},
+		);
+		assert_ok!(result);
+		assert_eq!(
+			result.unwrap().actual_weight.unwrap(),
+			TestWeightInfo::receive_messages_delivery_proof_weight(
+				&PreComputedSize(single_message_delivery_proof_size as _),
+				&UnrewardedRelayersState {
+					unrewarded_relayer_entries: 1,
+					total_messages: 1,
+					..Default::default()
+				},
+			)
+		);
+		assert!(TestDeliveryConfirmationPayments::is_reward_paid(TEST_RELAYER_A, 1));
+		assert!(!TestDeliveryConfirmationPayments::is_reward_paid(TEST_RELAYER_B, 1));
+
+		// this reports delivery of both message 1 and message 2 => reward is paid only to
+		// TEST_RELAYER_B
+		let two_messages_delivery_proof = prepare_messages_delivery_proof(
+			TEST_LANE_ID,
+			InboundLaneData {
+				relayers: vec![
+					unrewarded_relayer(1, 1, TEST_RELAYER_A),
+					unrewarded_relayer(2, 2, TEST_RELAYER_B),
+				]
+				.into(),
+				..Default::default()
+			},
+		);
+		let two_messages_delivery_proof_size = two_messages_delivery_proof.size();
+		let result = Pallet::<TestRuntime>::receive_messages_delivery_proof(
+			RuntimeOrigin::signed(1),
+			two_messages_delivery_proof,
+			UnrewardedRelayersState {
+				unrewarded_relayer_entries: 2,
+				messages_in_oldest_entry: 1,
+				total_messages: 2,
+				last_delivered_nonce: 2,
+			},
+		);
+		assert_ok!(result);
+		// even though the pre-dispatch weight was for two messages, the actual weight is
+		// for single message only
+		assert_eq!(
+			result.unwrap().actual_weight.unwrap(),
+			TestWeightInfo::receive_messages_delivery_proof_weight(
+				&PreComputedSize(two_messages_delivery_proof_size as _),
+				&UnrewardedRelayersState {
+					unrewarded_relayer_entries: 1,
+					total_messages: 1,
+					..Default::default()
+				},
+			)
+		);
+		assert!(!TestDeliveryConfirmationPayments::is_reward_paid(TEST_RELAYER_A, 1));
+		assert!(TestDeliveryConfirmationPayments::is_reward_paid(TEST_RELAYER_B, 1));
+		assert_eq!(TestOnMessagesDelivered::call_arguments(), Some((TEST_LANE_ID, 0)));
+	});
+}
+
+#[test]
+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([42, 42, 42, 42]);
+
+		assert_noop!(
+			Pallet::<TestRuntime>::receive_messages_delivery_proof(
+				RuntimeOrigin::signed(1),
+				proof,
+				Default::default(),
+			),
+			Error::<TestRuntime, ()>::InvalidMessagesDeliveryProof,
+		);
+	});
+}
+
+#[test]
+fn receive_messages_delivery_proof_rejects_proof_if_declared_relayers_state_is_invalid() {
+	run_test(|| {
+		// when number of relayers entries is invalid
+		let proof = prepare_messages_delivery_proof(
+			TEST_LANE_ID,
+			InboundLaneData {
+				relayers: vec![
+					unrewarded_relayer(1, 1, TEST_RELAYER_A),
+					unrewarded_relayer(2, 2, TEST_RELAYER_B),
+				]
+				.into(),
+				..Default::default()
+			},
+		);
+		assert_noop!(
+			Pallet::<TestRuntime>::receive_messages_delivery_proof(
+				RuntimeOrigin::signed(1),
+				proof,
+				UnrewardedRelayersState {
+					unrewarded_relayer_entries: 1,
+					total_messages: 2,
+					last_delivered_nonce: 2,
+					..Default::default()
+				},
+			),
+			Error::<TestRuntime, ()>::InvalidUnrewardedRelayersState,
+		);
+
+		// when number of messages is invalid
+		let proof = prepare_messages_delivery_proof(
+			TEST_LANE_ID,
+			InboundLaneData {
+				relayers: vec![
+					unrewarded_relayer(1, 1, TEST_RELAYER_A),
+					unrewarded_relayer(2, 2, TEST_RELAYER_B),
+				]
+				.into(),
+				..Default::default()
+			},
+		);
+		assert_noop!(
+			Pallet::<TestRuntime>::receive_messages_delivery_proof(
+				RuntimeOrigin::signed(1),
+				proof,
+				UnrewardedRelayersState {
+					unrewarded_relayer_entries: 2,
+					total_messages: 1,
+					last_delivered_nonce: 2,
+					..Default::default()
+				},
+			),
+			Error::<TestRuntime, ()>::InvalidUnrewardedRelayersState,
+		);
+
+		// when last delivered nonce is invalid
+		let proof = prepare_messages_delivery_proof(
+			TEST_LANE_ID,
+			InboundLaneData {
+				relayers: vec![
+					unrewarded_relayer(1, 1, TEST_RELAYER_A),
+					unrewarded_relayer(2, 2, TEST_RELAYER_B),
+				]
+				.into(),
+				..Default::default()
+			},
+		);
+		assert_noop!(
+			Pallet::<TestRuntime>::receive_messages_delivery_proof(
+				RuntimeOrigin::signed(1),
+				proof,
+				UnrewardedRelayersState {
+					unrewarded_relayer_entries: 2,
+					total_messages: 2,
+					last_delivered_nonce: 8,
+					..Default::default()
+				},
+			),
+			Error::<TestRuntime, ()>::InvalidUnrewardedRelayersState,
+		);
+	});
+}
+
+#[test]
+fn receive_messages_accepts_single_message_with_invalid_payload() {
+	run_test(|| {
+		let mut invalid_message = message(1, REGULAR_PAYLOAD);
+		invalid_message.payload = Vec::new();
+
+		assert_ok!(Pallet::<TestRuntime, ()>::receive_messages_proof(
+			RuntimeOrigin::signed(1),
+			TEST_RELAYER_A,
+			prepare_messages_proof(vec![invalid_message], None),
+			1,
+			Weight::zero(), /* weight may be zero in this case (all messages are
+			                 * improperly encoded) */
+		),);
+
+		assert_eq!(InboundLanes::<TestRuntime>::get(TEST_LANE_ID).last_delivered_nonce(), 1,);
+	});
+}
+
+#[test]
+fn receive_messages_accepts_batch_with_message_with_invalid_payload() {
+	run_test(|| {
+		let mut invalid_message = message(2, REGULAR_PAYLOAD);
+		invalid_message.payload = Vec::new();
+
+		assert_ok!(Pallet::<TestRuntime, ()>::receive_messages_proof(
+			RuntimeOrigin::signed(1),
+			TEST_RELAYER_A,
+			prepare_messages_proof(
+				vec![message(1, REGULAR_PAYLOAD), invalid_message, message(3, REGULAR_PAYLOAD),],
+				None
+			),
+			3,
+			REGULAR_PAYLOAD.declared_weight + REGULAR_PAYLOAD.declared_weight,
+		),);
+
+		assert_eq!(InboundLanes::<TestRuntime>::get(TEST_LANE_ID).last_delivered_nonce(), 3,);
+	});
+}
+
+#[test]
+fn actual_dispatch_weight_does_not_overflow() {
+	run_test(|| {
+		let message1 = message(1, message_payload(0, u64::MAX / 2));
+		let message2 = message(2, message_payload(0, u64::MAX / 2));
+		let message3 = message(3, message_payload(0, u64::MAX / 2));
+
+		let proof = prepare_messages_proof(vec![message1, message2, message3], None);
+		assert_noop!(
+			Pallet::<TestRuntime, ()>::receive_messages_proof(
+				RuntimeOrigin::signed(1),
+				TEST_RELAYER_A,
+				// this may cause overflow if source chain storage is invalid
+				proof,
+				3,
+				Weight::MAX,
+			),
+			Error::<TestRuntime, ()>::InsufficientDispatchWeight
+		);
+		assert_eq!(InboundLanes::<TestRuntime>::get(TEST_LANE_ID).last_delivered_nonce(), 0);
+	});
+}
+
+#[test]
+fn ref_time_refund_from_receive_messages_proof_works() {
+	run_test(|| {
+		fn submit_with_unspent_weight(
+			nonce: MessageNonce,
+			unspent_weight: u64,
+		) -> (Weight, Weight) {
+			let mut payload = REGULAR_PAYLOAD;
+			*payload.dispatch_result.unspent_weight.ref_time_mut() = unspent_weight;
+			let proof = prepare_messages_proof(vec![message(nonce, payload)], None);
+			let messages_count = 1;
+			let pre_dispatch_weight =
+				<TestRuntime as Config>::WeightInfo::receive_messages_proof_weight(
+					&*proof,
+					messages_count,
+					REGULAR_PAYLOAD.declared_weight,
+				);
+			let result = Pallet::<TestRuntime>::receive_messages_proof(
+				RuntimeOrigin::signed(1),
+				TEST_RELAYER_A,
+				proof,
+				messages_count,
+				REGULAR_PAYLOAD.declared_weight,
+			)
+			.expect("delivery has failed");
+			let post_dispatch_weight =
+				result.actual_weight.expect("receive_messages_proof always returns Some");
+
+			// message delivery transactions are never free
+			assert_eq!(result.pays_fee, Pays::Yes);
+
+			(pre_dispatch_weight, post_dispatch_weight)
+		}
+
+		// when dispatch is returning `unspent_weight < declared_weight`
+		let (pre, post) = submit_with_unspent_weight(1, 1);
+		assert_eq!(post.ref_time(), pre.ref_time() - 1);
+
+		// when dispatch is returning `unspent_weight = declared_weight`
+		let (pre, post) = submit_with_unspent_weight(2, REGULAR_PAYLOAD.declared_weight.ref_time());
+		assert_eq!(post.ref_time(), pre.ref_time() - REGULAR_PAYLOAD.declared_weight.ref_time());
+
+		// when dispatch is returning `unspent_weight > declared_weight`
+		let (pre, post) =
+			submit_with_unspent_weight(3, REGULAR_PAYLOAD.declared_weight.ref_time() + 1);
+		assert_eq!(post.ref_time(), pre.ref_time() - REGULAR_PAYLOAD.declared_weight.ref_time());
+
+		// when there's no unspent weight
+		let (pre, post) = submit_with_unspent_weight(4, 0);
+		assert_eq!(post.ref_time(), pre.ref_time());
+
+		// when dispatch is returning `unspent_weight < declared_weight`
+		let (pre, post) = submit_with_unspent_weight(5, 1);
+		assert_eq!(post.ref_time(), pre.ref_time() - 1);
+	});
+}
+
+#[test]
+fn proof_size_refund_from_receive_messages_proof_works() {
+	run_test(|| {
+		let max_entries = BridgedChain::MAX_UNREWARDED_RELAYERS_IN_CONFIRMATION_TX as usize;
+
+		// if there's maximal number of unrewarded relayer entries at the inbound lane, then
+		// `proof_size` is unchanged in post-dispatch weight
+		let proof = prepare_messages_proof(vec![message(101, REGULAR_PAYLOAD)], None);
+		let messages_count = 1;
+		let pre_dispatch_weight =
+			<TestRuntime as Config>::WeightInfo::receive_messages_proof_weight(
+				&*proof,
+				messages_count,
+				REGULAR_PAYLOAD.declared_weight,
+			);
+		InboundLanes::<TestRuntime>::insert(
+			TEST_LANE_ID,
+			StoredInboundLaneData(InboundLaneData {
+				relayers: vec![
+					UnrewardedRelayer {
+						relayer: 42,
+						messages: DeliveredMessages { begin: 0, end: 100 }
+					};
+					max_entries
+				]
+				.into(),
+				last_confirmed_nonce: 0,
+			}),
+		);
+		let post_dispatch_weight = Pallet::<TestRuntime>::receive_messages_proof(
+			RuntimeOrigin::signed(1),
+			TEST_RELAYER_A,
+			proof.clone(),
+			messages_count,
+			REGULAR_PAYLOAD.declared_weight,
+		)
+		.unwrap()
+		.actual_weight
+		.unwrap();
+		assert_eq!(post_dispatch_weight.proof_size(), pre_dispatch_weight.proof_size());
+
+		// if count of unrewarded relayer entries is less than maximal, then some `proof_size`
+		// must be refunded
+		InboundLanes::<TestRuntime>::insert(
+			TEST_LANE_ID,
+			StoredInboundLaneData(InboundLaneData {
+				relayers: vec![
+					UnrewardedRelayer {
+						relayer: 42,
+						messages: DeliveredMessages { begin: 0, end: 100 }
+					};
+					max_entries - 1
+				]
+				.into(),
+				last_confirmed_nonce: 0,
+			}),
+		);
+		let post_dispatch_weight = Pallet::<TestRuntime>::receive_messages_proof(
+			RuntimeOrigin::signed(1),
+			TEST_RELAYER_A,
+			proof,
+			messages_count,
+			REGULAR_PAYLOAD.declared_weight,
+		)
+		.unwrap()
+		.actual_weight
+		.unwrap();
+		assert!(
+			post_dispatch_weight.proof_size() < pre_dispatch_weight.proof_size(),
+			"Expected post-dispatch PoV {} to be less than pre-dispatch PoV {}",
+			post_dispatch_weight.proof_size(),
+			pre_dispatch_weight.proof_size(),
+		);
+	});
+}
+
+#[test]
+fn receive_messages_delivery_proof_rejects_proof_if_trying_to_confirm_more_messages_than_expected()
+{
+	run_test(|| {
+		// send message first to be able to check that delivery_proof fails later
+		send_regular_message(TEST_LANE_ID);
+
+		// 1) InboundLaneData declares that the `last_confirmed_nonce` is 1;
+		// 2) InboundLaneData has no entries => `InboundLaneData::last_delivered_nonce()` returns
+		//    `last_confirmed_nonce`;
+		// 3) it means that we're going to confirm delivery of messages 1..=1;
+		// 4) so the number of declared messages (see `UnrewardedRelayersState`) is `0` and numer of
+		//    actually confirmed messages is `1`.
+		let proof = prepare_messages_delivery_proof(
+			TEST_LANE_ID,
+			InboundLaneData { last_confirmed_nonce: 1, relayers: Default::default() },
+		);
+		assert_noop!(
+			Pallet::<TestRuntime>::receive_messages_delivery_proof(
+				RuntimeOrigin::signed(1),
+				proof,
+				UnrewardedRelayersState { last_delivered_nonce: 1, ..Default::default() },
+			),
+			Error::<TestRuntime, ()>::ReceptionConfirmation(
+				ReceptionConfirmationError::TryingToConfirmMoreMessagesThanExpected
+			),
+		);
+	});
+}
+
+#[test]
+fn storage_keys_computed_properly() {
+	assert_eq!(
+		PalletOperatingMode::<TestRuntime>::storage_value_final_key().to_vec(),
+		bp_messages::storage_keys::operating_mode_key("Messages").0,
+	);
+
+	assert_eq!(
+		OutboundMessages::<TestRuntime>::storage_map_final_key(MessageKey {
+			lane_id: TEST_LANE_ID,
+			nonce: 42
+		}),
+		bp_messages::storage_keys::message_key("Messages", &TEST_LANE_ID, 42).0,
+	);
+
+	assert_eq!(
+		OutboundLanes::<TestRuntime>::storage_map_final_key(TEST_LANE_ID),
+		bp_messages::storage_keys::outbound_lane_data_key("Messages", &TEST_LANE_ID).0,
+	);
+
+	assert_eq!(
+		InboundLanes::<TestRuntime>::storage_map_final_key(TEST_LANE_ID),
+		bp_messages::storage_keys::inbound_lane_data_key("Messages", &TEST_LANE_ID).0,
+	);
+}
+
+#[test]
+fn inbound_message_details_works() {
+	run_test(|| {
+		assert_eq!(
+			Pallet::<TestRuntime>::inbound_message_data(
+				TEST_LANE_ID,
+				REGULAR_PAYLOAD.encode(),
+				OutboundMessageDetails { nonce: 0, dispatch_weight: Weight::zero(), size: 0 },
+			),
+			InboundMessageDetails { dispatch_weight: REGULAR_PAYLOAD.declared_weight },
+		);
+	});
+}
+
+#[test]
+fn on_idle_callback_respects_remaining_weight() {
+	run_test(|| {
+		send_regular_message(TEST_LANE_ID);
+		send_regular_message(TEST_LANE_ID);
+		send_regular_message(TEST_LANE_ID);
+		send_regular_message(TEST_LANE_ID);
+
+		assert_ok!(Pallet::<TestRuntime>::receive_messages_delivery_proof(
+			RuntimeOrigin::signed(1),
+			prepare_messages_delivery_proof(
+				TEST_LANE_ID,
+				InboundLaneData {
+					last_confirmed_nonce: 4,
+					relayers: vec![unrewarded_relayer(1, 4, TEST_RELAYER_A)].into(),
+				},
+			),
+			UnrewardedRelayersState {
+				unrewarded_relayer_entries: 1,
+				messages_in_oldest_entry: 4,
+				total_messages: 4,
+				last_delivered_nonce: 4,
+			},
+		));
+
+		// all 4 messages may be pruned now
+		assert_eq!(outbound_lane::<TestRuntime, ()>(TEST_LANE_ID).data().latest_received_nonce, 4);
+		assert_eq!(outbound_lane::<TestRuntime, ()>(TEST_LANE_ID).data().oldest_unpruned_nonce, 1);
+		System::<TestRuntime>::set_block_number(2);
+
+		// if passed wight is too low to do anything
+		let dbw = DbWeight::get();
+		assert_eq!(Pallet::<TestRuntime, ()>::on_idle(0, dbw.reads_writes(1, 1)), Weight::zero(),);
+		assert_eq!(outbound_lane::<TestRuntime, ()>(TEST_LANE_ID).data().oldest_unpruned_nonce, 1);
+
+		// if passed wight is enough to prune single message
+		assert_eq!(
+			Pallet::<TestRuntime, ()>::on_idle(0, dbw.reads_writes(1, 2)),
+			dbw.reads_writes(1, 2),
+		);
+		assert_eq!(outbound_lane::<TestRuntime, ()>(TEST_LANE_ID).data().oldest_unpruned_nonce, 2);
+
+		// if passed wight is enough to prune two more messages
+		assert_eq!(
+			Pallet::<TestRuntime, ()>::on_idle(0, dbw.reads_writes(1, 3)),
+			dbw.reads_writes(1, 3),
+		);
+		assert_eq!(outbound_lane::<TestRuntime, ()>(TEST_LANE_ID).data().oldest_unpruned_nonce, 4);
+
+		// if passed wight is enough to prune many messages
+		assert_eq!(
+			Pallet::<TestRuntime, ()>::on_idle(0, dbw.reads_writes(100, 100)),
+			dbw.reads_writes(1, 2),
+		);
+		assert_eq!(outbound_lane::<TestRuntime, ()>(TEST_LANE_ID).data().oldest_unpruned_nonce, 5);
+	});
+}
+
+#[test]
+fn on_idle_callback_is_rotating_lanes_to_prune() {
+	run_test(|| {
+		// send + receive confirmation for lane 1
+		send_regular_message(TEST_LANE_ID);
+		receive_messages_delivery_proof();
+		// send + receive confirmation for lane 2
+		send_regular_message(TEST_LANE_ID_2);
+		assert_ok!(Pallet::<TestRuntime>::receive_messages_delivery_proof(
+			RuntimeOrigin::signed(1),
+			prepare_messages_delivery_proof(
+				TEST_LANE_ID_2,
+				InboundLaneData {
+					last_confirmed_nonce: 1,
+					relayers: vec![unrewarded_relayer(1, 1, TEST_RELAYER_A)].into(),
+				},
+			),
+			UnrewardedRelayersState {
+				unrewarded_relayer_entries: 1,
+				messages_in_oldest_entry: 1,
+				total_messages: 1,
+				last_delivered_nonce: 1,
+			},
+		));
+
+		// nothing is pruned yet
+		assert_eq!(outbound_lane::<TestRuntime, ()>(TEST_LANE_ID).data().latest_received_nonce, 1);
+		assert_eq!(outbound_lane::<TestRuntime, ()>(TEST_LANE_ID).data().oldest_unpruned_nonce, 1);
+		assert_eq!(
+			outbound_lane::<TestRuntime, ()>(TEST_LANE_ID_2).data().latest_received_nonce,
+			1
+		);
+		assert_eq!(
+			outbound_lane::<TestRuntime, ()>(TEST_LANE_ID_2).data().oldest_unpruned_nonce,
+			1
+		);
+
+		// in block#2.on_idle lane messages of lane 1 are pruned
+		let dbw = DbWeight::get();
+		System::<TestRuntime>::set_block_number(2);
+		assert_eq!(
+			Pallet::<TestRuntime, ()>::on_idle(0, dbw.reads_writes(100, 100)),
+			dbw.reads_writes(1, 2),
+		);
+		assert_eq!(outbound_lane::<TestRuntime, ()>(TEST_LANE_ID).data().oldest_unpruned_nonce, 2);
+		assert_eq!(
+			outbound_lane::<TestRuntime, ()>(TEST_LANE_ID_2).data().oldest_unpruned_nonce,
+			1
+		);
+
+		// in block#3.on_idle lane messages of lane 2 are pruned
+		System::<TestRuntime>::set_block_number(3);
+
+		assert_eq!(
+			Pallet::<TestRuntime, ()>::on_idle(0, dbw.reads_writes(100, 100)),
+			dbw.reads_writes(1, 2),
+		);
+		assert_eq!(outbound_lane::<TestRuntime, ()>(TEST_LANE_ID).data().oldest_unpruned_nonce, 2);
+		assert_eq!(
+			outbound_lane::<TestRuntime, ()>(TEST_LANE_ID_2).data().oldest_unpruned_nonce,
+			2
+		);
+	});
+}
+
+#[test]
+fn outbound_message_from_unconfigured_lane_is_rejected() {
+	run_test(|| {
+		assert_noop!(
+			Pallet::<TestRuntime, ()>::validate_message(TEST_LANE_ID_3, &REGULAR_PAYLOAD,),
+			Error::<TestRuntime, ()>::InactiveOutboundLane,
+		);
+	});
+}
+
+#[test]
+fn test_bridge_messages_call_is_correctly_defined() {
+	run_test(|| {
+		let account_id = 1;
+		let message_proof = prepare_messages_proof(vec![message(1, REGULAR_PAYLOAD)], None);
+		let message_delivery_proof = prepare_messages_delivery_proof(
+			TEST_LANE_ID,
+			InboundLaneData {
+				last_confirmed_nonce: 1,
+				relayers: vec![UnrewardedRelayer {
+					relayer: 0,
+					messages: DeliveredMessages::new(1),
+				}]
+				.into(),
+			},
+		);
+		let unrewarded_relayer_state = UnrewardedRelayersState {
+			unrewarded_relayer_entries: 1,
+			total_messages: 1,
+			last_delivered_nonce: 1,
+			..Default::default()
+		};
+
+		let direct_receive_messages_proof_call = Call::<TestRuntime>::receive_messages_proof {
+			relayer_id_at_bridged_chain: account_id,
+			proof: message_proof.clone(),
+			messages_count: 1,
+			dispatch_weight: REGULAR_PAYLOAD.declared_weight,
+		};
+		let indirect_receive_messages_proof_call = BridgeMessagesCall::<
+			AccountId,
+			FromBridgedChainMessagesProof<BridgedHeaderHash>,
+			FromBridgedChainMessagesDeliveryProof<BridgedHeaderHash>,
+		>::receive_messages_proof {
+			relayer_id_at_bridged_chain: account_id,
+			proof: *message_proof,
+			messages_count: 1,
+			dispatch_weight: REGULAR_PAYLOAD.declared_weight,
+		};
+		assert_eq!(
+			direct_receive_messages_proof_call.encode(),
+			indirect_receive_messages_proof_call.encode()
+		);
+
+		let direct_receive_messages_delivery_proof_call =
+			Call::<TestRuntime>::receive_messages_delivery_proof {
+				proof: message_delivery_proof.clone(),
+				relayers_state: unrewarded_relayer_state.clone(),
+			};
+		let indirect_receive_messages_delivery_proof_call = BridgeMessagesCall::<
+			AccountId,
+			FromBridgedChainMessagesProof<BridgedHeaderHash>,
+			FromBridgedChainMessagesDeliveryProof<BridgedHeaderHash>,
+		>::receive_messages_delivery_proof {
+			proof: message_delivery_proof,
+			relayers_state: unrewarded_relayer_state,
+		};
+		assert_eq!(
+			direct_receive_messages_delivery_proof_call.encode(),
+			indirect_receive_messages_delivery_proof_call.encode()
+		);
+	});
+}
+
+generate_owned_bridge_module_tests!(
+	MessagesOperatingMode::Basic(BasicOperatingMode::Normal),
+	MessagesOperatingMode::Basic(BasicOperatingMode::Halted)
+);
+
+#[test]
+fn inbound_storage_extra_proof_size_bytes_works() {
+	fn relayer_entry() -> UnrewardedRelayer<TestRelayer> {
+		UnrewardedRelayer { relayer: 42u64, messages: DeliveredMessages { begin: 0, end: 100 } }
+	}
+
+	fn storage(relayer_entries: usize) -> RuntimeInboundLaneStorage<TestRuntime, ()> {
+		RuntimeInboundLaneStorage {
+			lane_id: Default::default(),
+			cached_data: Some(InboundLaneData {
+				relayers: vec![relayer_entry(); relayer_entries].into(),
+				last_confirmed_nonce: 0,
+			}),
+			_phantom: Default::default(),
+		}
+	}
+
+	let max_entries = BridgedChain::MAX_UNREWARDED_RELAYERS_IN_CONFIRMATION_TX as usize;
+
+	// when we have exactly `MAX_UNREWARDED_RELAYERS_IN_CONFIRMATION_TX` unrewarded relayers
+	assert_eq!(storage(max_entries).extra_proof_size_bytes(), 0);
+
+	// when we have less than `MAX_UNREWARDED_RELAYERS_IN_CONFIRMATION_TX` unrewarded relayers
+	assert_eq!(
+		storage(max_entries - 1).extra_proof_size_bytes(),
+		relayer_entry().encode().len() as u64
+	);
+	assert_eq!(
+		storage(max_entries - 2).extra_proof_size_bytes(),
+		2 * relayer_entry().encode().len() as u64
+	);
+
+	// when we have more than `MAX_UNREWARDED_RELAYERS_IN_CONFIRMATION_TX` unrewarded relayers
+	// (shall not happen in practice)
+	assert_eq!(storage(max_entries + 1).extra_proof_size_bytes(), 0);
+}
+
+#[test]
+fn maybe_outbound_lanes_count_returns_correct_value() {
+	assert_eq!(
+		MaybeOutboundLanesCount::<TestRuntime, ()>::get(),
+		Some(mock::ActiveOutboundLanes::get().len() as u32)
+	);
+}
diff --git a/bridges/modules/messages/src/weights.rs b/bridges/modules/messages/src/weights.rs
index 5bf7d567560..72a06599b16 100644
--- a/bridges/modules/messages/src/weights.rs
+++ b/bridges/modules/messages/src/weights.rs
@@ -17,9 +17,9 @@
 //! Autogenerated weights for pallet_bridge_messages
 //!
 //! THIS FILE WAS AUTO-GENERATED USING THE SUBSTRATE BENCHMARK CLI VERSION 4.0.0-dev
-//! DATE: 2023-03-23, STEPS: `50`, REPEAT: `20`, LOW RANGE: `[]`, HIGH RANGE: `[]`
+//! DATE: 2023-06-22, STEPS: `50`, REPEAT: `20`, LOW RANGE: `[]`, HIGH RANGE: `[]`
 //! WORST CASE MAP SIZE: `1000000`
-//! HOSTNAME: `covid`, CPU: `11th Gen Intel(R) Core(TM) i7-11800H @ 2.30GHz`
+//! HOSTNAME: `serban-ROG-Zephyrus`, CPU: `12th Gen Intel(R) Core(TM) i7-12700H`
 //! EXECUTION: Some(Wasm), WASM-EXECUTION: Compiled, CHAIN: Some("dev"), DB CACHE: 1024
 
 // Executed Command:
@@ -51,14 +51,13 @@ use sp_std::marker::PhantomData;
 /// Weight functions needed for pallet_bridge_messages.
 pub trait WeightInfo {
 	fn receive_single_message_proof() -> Weight;
-	fn receive_two_messages_proof() -> Weight;
+	fn receive_n_messages_proof(n: u32) -> Weight;
 	fn receive_single_message_proof_with_outbound_lane_state() -> Weight;
-	fn receive_single_message_proof_1_kb() -> Weight;
-	fn receive_single_message_proof_16_kb() -> Weight;
+	fn receive_single_n_bytes_message_proof(n: u32) -> Weight;
 	fn receive_delivery_proof_for_single_message() -> Weight;
 	fn receive_delivery_proof_for_two_messages_by_single_relayer() -> Weight;
 	fn receive_delivery_proof_for_two_messages_by_two_relayers() -> Weight;
-	fn receive_single_message_proof_with_dispatch(i: u32) -> Weight;
+	fn receive_single_n_bytes_message_proof_with_dispatch(n: u32) -> Weight;
 }
 
 /// Weights for `pallet_bridge_messages` that are generated using one of the Bridge testnets.
@@ -82,56 +81,39 @@ impl<T: frame_system::Config> WeightInfo for BridgeWeight<T> {
 	/// 51655, mode: MaxEncodedLen)
 	fn receive_single_message_proof() -> Weight {
 		// Proof Size summary in bytes:
-		//  Measured:  `618`
-		//  Estimated: `57170`
-		// Minimum execution time: 52_321 nanoseconds.
-		Weight::from_parts(54_478_000, 57170)
+		//  Measured:  `653`
+		//  Estimated: `52673`
+		// Minimum execution time: 38_724 nanoseconds.
+		Weight::from_parts(40_650_000, 52673)
 			.saturating_add(T::DbWeight::get().reads(3_u64))
 			.saturating_add(T::DbWeight::get().writes(1_u64))
 	}
-	/// Storage: BridgeUnknownMessages PalletOperatingMode (r:1 w:0)
+	/// Storage: BridgeRialtoMessages PalletOperatingMode (r:1 w:0)
 	///
-	/// Proof: BridgeUnknownMessages PalletOperatingMode (max_values: Some(1), max_size: Some(2),
+	/// Proof: BridgeRialtoMessages PalletOperatingMode (max_values: Some(1), max_size: Some(2),
 	/// added: 497, mode: MaxEncodedLen)
 	///
-	/// Storage: BridgeUnknownGrandpa ImportedHeaders (r:1 w:0)
+	/// Storage: BridgeRialtoGrandpa ImportedHeaders (r:1 w:0)
 	///
-	/// Proof: BridgeUnknownGrandpa ImportedHeaders (max_values: Some(14400), max_size: Some(68),
+	/// Proof: BridgeRialtoGrandpa ImportedHeaders (max_values: Some(14400), max_size: Some(68),
 	/// added: 2048, mode: MaxEncodedLen)
 	///
-	/// Storage: BridgeUnknownMessages InboundLanes (r:1 w:1)
-	///
-	/// Proof: BridgeUnknownMessages InboundLanes (max_values: None, max_size: Some(49180), added:
-	/// 51655, mode: MaxEncodedLen)
-	fn receive_two_messages_proof() -> Weight {
-		// Proof Size summary in bytes:
-		//  Measured:  `618`
-		//  Estimated: `57170`
-		// Minimum execution time: 64_597 nanoseconds.
-		Weight::from_parts(69_267_000, 57170)
-			.saturating_add(T::DbWeight::get().reads(3_u64))
-			.saturating_add(T::DbWeight::get().writes(1_u64))
-	}
-	/// Storage: BridgeUnknownMessages PalletOperatingMode (r:1 w:0)
+	/// Storage: BridgeRialtoMessages InboundLanes (r:1 w:1)
 	///
-	/// Proof: BridgeUnknownMessages PalletOperatingMode (max_values: Some(1), max_size: Some(2),
-	/// added: 497, mode: MaxEncodedLen)
+	/// Proof: BridgeRialtoMessages InboundLanes (max_values: None, max_size: Some(49208), added:
+	/// 51683, mode: MaxEncodedLen)
 	///
-	/// Storage: BridgeUnknownGrandpa ImportedHeaders (r:1 w:0)
+	/// The range of component `n` is `[1, 1004]`.
 	///
-	/// Proof: BridgeUnknownGrandpa ImportedHeaders (max_values: Some(14400), max_size: Some(68),
-	/// added: 2048, mode: MaxEncodedLen)
-	///
-	/// Storage: BridgeUnknownMessages InboundLanes (r:1 w:1)
-	///
-	/// Proof: BridgeUnknownMessages InboundLanes (max_values: None, max_size: Some(49180), added:
-	/// 51655, mode: MaxEncodedLen)
-	fn receive_single_message_proof_with_outbound_lane_state() -> Weight {
+	/// The range of component `n` is `[1, 1004]`.
+	fn receive_n_messages_proof(n: u32) -> Weight {
 		// Proof Size summary in bytes:
-		//  Measured:  `618`
-		//  Estimated: `57170`
-		// Minimum execution time: 64_079 nanoseconds.
-		Weight::from_parts(65_905_000, 57170)
+		//  Measured:  `653`
+		//  Estimated: `52673`
+		// Minimum execution time: 39_354 nanoseconds.
+		Weight::from_parts(29_708_543, 52673)
+			// Standard Error: 1_185
+			.saturating_add(Weight::from_parts(7_648_787, 0).saturating_mul(n.into()))
 			.saturating_add(T::DbWeight::get().reads(3_u64))
 			.saturating_add(T::DbWeight::get().writes(1_u64))
 	}
@@ -149,12 +131,12 @@ impl<T: frame_system::Config> WeightInfo for BridgeWeight<T> {
 	///
 	/// Proof: BridgeUnknownMessages InboundLanes (max_values: None, max_size: Some(49180), added:
 	/// 51655, mode: MaxEncodedLen)
-	fn receive_single_message_proof_1_kb() -> Weight {
+	fn receive_single_message_proof_with_outbound_lane_state() -> Weight {
 		// Proof Size summary in bytes:
-		//  Measured:  `618`
-		//  Estimated: `57170`
-		// Minimum execution time: 50_588 nanoseconds.
-		Weight::from_parts(53_544_000, 57170)
+		//  Measured:  `653`
+		//  Estimated: `52673`
+		// Minimum execution time: 45_578 nanoseconds.
+		Weight::from_parts(47_161_000, 52673)
 			.saturating_add(T::DbWeight::get().reads(3_u64))
 			.saturating_add(T::DbWeight::get().writes(1_u64))
 	}
@@ -172,12 +154,16 @@ impl<T: frame_system::Config> WeightInfo for BridgeWeight<T> {
 	///
 	/// Proof: BridgeUnknownMessages InboundLanes (max_values: None, max_size: Some(49180), added:
 	/// 51655, mode: MaxEncodedLen)
-	fn receive_single_message_proof_16_kb() -> Weight {
+	///
+	/// The range of component `n` is `[1, 16384]`.
+	fn receive_single_n_bytes_message_proof(n: u32) -> Weight {
 		// Proof Size summary in bytes:
-		//  Measured:  `618`
-		//  Estimated: `57170`
-		// Minimum execution time: 78_269 nanoseconds.
-		Weight::from_parts(81_748_000, 57170)
+		//  Measured:  `653`
+		//  Estimated: `52673`
+		// Minimum execution time: 38_702 nanoseconds.
+		Weight::from_parts(41_040_143, 52673)
+			// Standard Error: 5
+			.saturating_add(Weight::from_parts(1_174, 0).saturating_mul(n.into()))
 			.saturating_add(T::DbWeight::get().reads(3_u64))
 			.saturating_add(T::DbWeight::get().writes(1_u64))
 	}
@@ -198,16 +184,21 @@ impl<T: frame_system::Config> WeightInfo for BridgeWeight<T> {
 	///
 	/// Storage: BridgeRelayers RelayerRewards (r:1 w:1)
 	///
-	/// Proof: BridgeRelayers RelayerRewards (max_values: None, max_size: Some(65), added: 2540,
+	/// Proof: BridgeRelayers RelayerRewards (max_values: None, max_size: Some(93), added: 2568,
 	/// mode: MaxEncodedLen)
+	///
+	/// Storage: BridgeRialtoMessages OutboundMessages (r:0 w:1)
+	///
+	/// Proof: BridgeRialtoMessages OutboundMessages (max_values: None, max_size: Some(65596),
+	/// added: 68071, mode: MaxEncodedLen)
 	fn receive_delivery_proof_for_single_message() -> Weight {
 		// Proof Size summary in bytes:
-		//  Measured:  `579`
-		//  Estimated: `9584`
-		// Minimum execution time: 45_786 nanoseconds.
-		Weight::from_parts(47_382_000, 9584)
+		//  Measured:  `701`
+		//  Estimated: `3558`
+		// Minimum execution time: 37_197 nanoseconds.
+		Weight::from_parts(38_371_000, 3558)
 			.saturating_add(T::DbWeight::get().reads(4_u64))
-			.saturating_add(T::DbWeight::get().writes(2_u64))
+			.saturating_add(T::DbWeight::get().writes(3_u64))
 	}
 	/// Storage: BridgeUnknownMessages PalletOperatingMode (r:1 w:0)
 	///
@@ -226,16 +217,21 @@ impl<T: frame_system::Config> WeightInfo for BridgeWeight<T> {
 	///
 	/// Storage: BridgeRelayers RelayerRewards (r:1 w:1)
 	///
-	/// Proof: BridgeRelayers RelayerRewards (max_values: None, max_size: Some(65), added: 2540,
+	/// Proof: BridgeRelayers RelayerRewards (max_values: None, max_size: Some(93), added: 2568,
 	/// mode: MaxEncodedLen)
+	///
+	/// Storage: BridgeRialtoMessages OutboundMessages (r:0 w:2)
+	///
+	/// Proof: BridgeRialtoMessages OutboundMessages (max_values: None, max_size: Some(65596),
+	/// added: 68071, mode: MaxEncodedLen)
 	fn receive_delivery_proof_for_two_messages_by_single_relayer() -> Weight {
 		// Proof Size summary in bytes:
-		//  Measured:  `596`
-		//  Estimated: `9584`
-		// Minimum execution time: 44_544 nanoseconds.
-		Weight::from_parts(45_451_000, 9584)
+		//  Measured:  `701`
+		//  Estimated: `3558`
+		// Minimum execution time: 38_684 nanoseconds.
+		Weight::from_parts(39_929_000, 3558)
 			.saturating_add(T::DbWeight::get().reads(4_u64))
-			.saturating_add(T::DbWeight::get().writes(2_u64))
+			.saturating_add(T::DbWeight::get().writes(4_u64))
 	}
 	/// Storage: BridgeUnknownMessages PalletOperatingMode (r:1 w:0)
 	///
@@ -254,16 +250,21 @@ impl<T: frame_system::Config> WeightInfo for BridgeWeight<T> {
 	///
 	/// Storage: BridgeRelayers RelayerRewards (r:2 w:2)
 	///
-	/// Proof: BridgeRelayers RelayerRewards (max_values: None, max_size: Some(65), added: 2540,
+	/// Proof: BridgeRelayers RelayerRewards (max_values: None, max_size: Some(93), added: 2568,
 	/// mode: MaxEncodedLen)
+	///
+	/// Storage: BridgeRialtoMessages OutboundMessages (r:0 w:2)
+	///
+	/// Proof: BridgeRialtoMessages OutboundMessages (max_values: None, max_size: Some(65596),
+	/// added: 68071, mode: MaxEncodedLen)
 	fn receive_delivery_proof_for_two_messages_by_two_relayers() -> Weight {
 		// Proof Size summary in bytes:
-		//  Measured:  `596`
-		//  Estimated: `12124`
-		// Minimum execution time: 47_344 nanoseconds.
-		Weight::from_parts(48_311_000, 12124)
+		//  Measured:  `701`
+		//  Estimated: `6126`
+		// Minimum execution time: 41_363 nanoseconds.
+		Weight::from_parts(42_621_000, 6126)
 			.saturating_add(T::DbWeight::get().reads(5_u64))
-			.saturating_add(T::DbWeight::get().writes(3_u64))
+			.saturating_add(T::DbWeight::get().writes(5_u64))
 	}
 	/// Storage: BridgeUnknownMessages PalletOperatingMode (r:1 w:0)
 	///
@@ -280,15 +281,15 @@ impl<T: frame_system::Config> WeightInfo for BridgeWeight<T> {
 	/// Proof: BridgeUnknownMessages InboundLanes (max_values: None, max_size: Some(49180), added:
 	/// 51655, mode: MaxEncodedLen)
 	///
-	/// The range of component `i` is `[128, 2048]`.
-	fn receive_single_message_proof_with_dispatch(i: u32) -> Weight {
+	/// The range of component `n` is `[1, 16384]`.
+	fn receive_single_n_bytes_message_proof_with_dispatch(n: u32) -> Weight {
 		// Proof Size summary in bytes:
-		//  Measured:  `618`
-		//  Estimated: `57170`
-		// Minimum execution time: 52_385 nanoseconds.
-		Weight::from_parts(54_919_468, 57170)
-			// Standard Error: 108
-			.saturating_add(Weight::from_parts(3_286, 0).saturating_mul(i.into()))
+		//  Measured:  `653`
+		//  Estimated: `52673`
+		// Minimum execution time: 38_925 nanoseconds.
+		Weight::from_parts(39_617_000, 52673)
+			// Standard Error: 612
+			.saturating_add(Weight::from_parts(372_813, 0).saturating_mul(n.into()))
 			.saturating_add(T::DbWeight::get().reads(3_u64))
 			.saturating_add(T::DbWeight::get().writes(1_u64))
 	}
@@ -312,33 +313,39 @@ impl WeightInfo for () {
 	/// 51655, mode: MaxEncodedLen)
 	fn receive_single_message_proof() -> Weight {
 		// Proof Size summary in bytes:
-		//  Measured:  `618`
-		//  Estimated: `57170`
-		// Minimum execution time: 52_321 nanoseconds.
-		Weight::from_parts(54_478_000, 57170)
+		//  Measured:  `653`
+		//  Estimated: `52673`
+		// Minimum execution time: 38_724 nanoseconds.
+		Weight::from_parts(40_650_000, 52673)
 			.saturating_add(RocksDbWeight::get().reads(3_u64))
 			.saturating_add(RocksDbWeight::get().writes(1_u64))
 	}
-	/// Storage: BridgeUnknownMessages PalletOperatingMode (r:1 w:0)
+	/// Storage: BridgeRialtoMessages PalletOperatingMode (r:1 w:0)
 	///
-	/// Proof: BridgeUnknownMessages PalletOperatingMode (max_values: Some(1), max_size: Some(2),
+	/// Proof: BridgeRialtoMessages PalletOperatingMode (max_values: Some(1), max_size: Some(2),
 	/// added: 497, mode: MaxEncodedLen)
 	///
-	/// Storage: BridgeUnknownGrandpa ImportedHeaders (r:1 w:0)
+	/// Storage: BridgeRialtoGrandpa ImportedHeaders (r:1 w:0)
 	///
-	/// Proof: BridgeUnknownGrandpa ImportedHeaders (max_values: Some(14400), max_size: Some(68),
+	/// Proof: BridgeRialtoGrandpa ImportedHeaders (max_values: Some(14400), max_size: Some(68),
 	/// added: 2048, mode: MaxEncodedLen)
 	///
-	/// Storage: BridgeUnknownMessages InboundLanes (r:1 w:1)
+	/// Storage: BridgeRialtoMessages InboundLanes (r:1 w:1)
 	///
-	/// Proof: BridgeUnknownMessages InboundLanes (max_values: None, max_size: Some(49180), added:
-	/// 51655, mode: MaxEncodedLen)
-	fn receive_two_messages_proof() -> Weight {
+	/// Proof: BridgeRialtoMessages InboundLanes (max_values: None, max_size: Some(49208), added:
+	/// 51683, mode: MaxEncodedLen)
+	///
+	/// The range of component `n` is `[1, 1004]`.
+	///
+	/// The range of component `n` is `[1, 1004]`.
+	fn receive_n_messages_proof(n: u32) -> Weight {
 		// Proof Size summary in bytes:
-		//  Measured:  `618`
-		//  Estimated: `57170`
-		// Minimum execution time: 64_597 nanoseconds.
-		Weight::from_parts(69_267_000, 57170)
+		//  Measured:  `653`
+		//  Estimated: `52673`
+		// Minimum execution time: 39_354 nanoseconds.
+		Weight::from_parts(29_708_543, 52673)
+			// Standard Error: 1_185
+			.saturating_add(Weight::from_parts(7_648_787, 0).saturating_mul(n.into()))
 			.saturating_add(RocksDbWeight::get().reads(3_u64))
 			.saturating_add(RocksDbWeight::get().writes(1_u64))
 	}
@@ -358,10 +365,10 @@ impl WeightInfo for () {
 	/// 51655, mode: MaxEncodedLen)
 	fn receive_single_message_proof_with_outbound_lane_state() -> Weight {
 		// Proof Size summary in bytes:
-		//  Measured:  `618`
-		//  Estimated: `57170`
-		// Minimum execution time: 64_079 nanoseconds.
-		Weight::from_parts(65_905_000, 57170)
+		//  Measured:  `653`
+		//  Estimated: `52673`
+		// Minimum execution time: 45_578 nanoseconds.
+		Weight::from_parts(47_161_000, 52673)
 			.saturating_add(RocksDbWeight::get().reads(3_u64))
 			.saturating_add(RocksDbWeight::get().writes(1_u64))
 	}
@@ -377,37 +384,20 @@ impl WeightInfo for () {
 	///
 	/// Storage: BridgeUnknownMessages InboundLanes (r:1 w:1)
 	///
-	/// Proof: BridgeUnknownMessages InboundLanes (max_values: None, max_size: Some(49180), added:
-	/// 51655, mode: MaxEncodedLen)
-	fn receive_single_message_proof_1_kb() -> Weight {
-		// Proof Size summary in bytes:
-		//  Measured:  `618`
-		//  Estimated: `57170`
-		// Minimum execution time: 50_588 nanoseconds.
-		Weight::from_parts(53_544_000, 57170)
-			.saturating_add(RocksDbWeight::get().reads(3_u64))
-			.saturating_add(RocksDbWeight::get().writes(1_u64))
-	}
-	/// Storage: BridgeUnknownMessages PalletOperatingMode (r:1 w:0)
-	///
-	/// Proof: BridgeUnknownMessages PalletOperatingMode (max_values: Some(1), max_size: Some(2),
-	/// added: 497, mode: MaxEncodedLen)
-	///
-	/// Storage: BridgeUnknownGrandpa ImportedHeaders (r:1 w:0)
+	/// Proof: BridgeRialtoMessages InboundLanes (max_values: None, max_size: Some(49208), added:
+	/// 51683, mode: MaxEncodedLen)
 	///
-	/// Proof: BridgeUnknownGrandpa ImportedHeaders (max_values: Some(14400), max_size: Some(68),
-	/// added: 2048, mode: MaxEncodedLen)
+	/// The range of component `n` is `[1, 16384]`.
 	///
-	/// Storage: BridgeUnknownMessages InboundLanes (r:1 w:1)
-	///
-	/// Proof: BridgeUnknownMessages InboundLanes (max_values: None, max_size: Some(49180), added:
-	/// 51655, mode: MaxEncodedLen)
-	fn receive_single_message_proof_16_kb() -> Weight {
+	/// The range of component `n` is `[1, 16384]`.
+	fn receive_single_n_bytes_message_proof(n: u32) -> Weight {
 		// Proof Size summary in bytes:
-		//  Measured:  `618`
-		//  Estimated: `57170`
-		// Minimum execution time: 78_269 nanoseconds.
-		Weight::from_parts(81_748_000, 57170)
+		//  Measured:  `653`
+		//  Estimated: `52673`
+		// Minimum execution time: 38_702 nanoseconds.
+		Weight::from_parts(41_040_143, 52673)
+			// Standard Error: 5
+			.saturating_add(Weight::from_parts(1_174, 0).saturating_mul(n.into()))
 			.saturating_add(RocksDbWeight::get().reads(3_u64))
 			.saturating_add(RocksDbWeight::get().writes(1_u64))
 	}
@@ -428,16 +418,21 @@ impl WeightInfo for () {
 	///
 	/// Storage: BridgeRelayers RelayerRewards (r:1 w:1)
 	///
-	/// Proof: BridgeRelayers RelayerRewards (max_values: None, max_size: Some(65), added: 2540,
+	/// Proof: BridgeRelayers RelayerRewards (max_values: None, max_size: Some(93), added: 2568,
 	/// mode: MaxEncodedLen)
+	///
+	/// Storage: BridgeRialtoMessages OutboundMessages (r:0 w:1)
+	///
+	/// Proof: BridgeRialtoMessages OutboundMessages (max_values: None, max_size: Some(65596),
+	/// added: 68071, mode: MaxEncodedLen)
 	fn receive_delivery_proof_for_single_message() -> Weight {
 		// Proof Size summary in bytes:
-		//  Measured:  `579`
-		//  Estimated: `9584`
-		// Minimum execution time: 45_786 nanoseconds.
-		Weight::from_parts(47_382_000, 9584)
+		//  Measured:  `701`
+		//  Estimated: `3558`
+		// Minimum execution time: 37_197 nanoseconds.
+		Weight::from_parts(38_371_000, 3558)
 			.saturating_add(RocksDbWeight::get().reads(4_u64))
-			.saturating_add(RocksDbWeight::get().writes(2_u64))
+			.saturating_add(RocksDbWeight::get().writes(3_u64))
 	}
 	/// Storage: BridgeUnknownMessages PalletOperatingMode (r:1 w:0)
 	///
@@ -456,16 +451,21 @@ impl WeightInfo for () {
 	///
 	/// Storage: BridgeRelayers RelayerRewards (r:1 w:1)
 	///
-	/// Proof: BridgeRelayers RelayerRewards (max_values: None, max_size: Some(65), added: 2540,
+	/// Proof: BridgeRelayers RelayerRewards (max_values: None, max_size: Some(93), added: 2568,
 	/// mode: MaxEncodedLen)
+	///
+	/// Storage: BridgeRialtoMessages OutboundMessages (r:0 w:2)
+	///
+	/// Proof: BridgeRialtoMessages OutboundMessages (max_values: None, max_size: Some(65596),
+	/// added: 68071, mode: MaxEncodedLen)
 	fn receive_delivery_proof_for_two_messages_by_single_relayer() -> Weight {
 		// Proof Size summary in bytes:
-		//  Measured:  `596`
-		//  Estimated: `9584`
-		// Minimum execution time: 44_544 nanoseconds.
-		Weight::from_parts(45_451_000, 9584)
+		//  Measured:  `701`
+		//  Estimated: `3558`
+		// Minimum execution time: 38_684 nanoseconds.
+		Weight::from_parts(39_929_000, 3558)
 			.saturating_add(RocksDbWeight::get().reads(4_u64))
-			.saturating_add(RocksDbWeight::get().writes(2_u64))
+			.saturating_add(RocksDbWeight::get().writes(4_u64))
 	}
 	/// Storage: BridgeUnknownMessages PalletOperatingMode (r:1 w:0)
 	///
@@ -484,16 +484,21 @@ impl WeightInfo for () {
 	///
 	/// Storage: BridgeRelayers RelayerRewards (r:2 w:2)
 	///
-	/// Proof: BridgeRelayers RelayerRewards (max_values: None, max_size: Some(65), added: 2540,
+	/// Proof: BridgeRelayers RelayerRewards (max_values: None, max_size: Some(93), added: 2568,
 	/// mode: MaxEncodedLen)
+	///
+	/// Storage: BridgeRialtoMessages OutboundMessages (r:0 w:2)
+	///
+	/// Proof: BridgeRialtoMessages OutboundMessages (max_values: None, max_size: Some(65596),
+	/// added: 68071, mode: MaxEncodedLen)
 	fn receive_delivery_proof_for_two_messages_by_two_relayers() -> Weight {
 		// Proof Size summary in bytes:
-		//  Measured:  `596`
-		//  Estimated: `12124`
-		// Minimum execution time: 47_344 nanoseconds.
-		Weight::from_parts(48_311_000, 12124)
+		//  Measured:  `701`
+		//  Estimated: `6126`
+		// Minimum execution time: 41_363 nanoseconds.
+		Weight::from_parts(42_621_000, 6126)
 			.saturating_add(RocksDbWeight::get().reads(5_u64))
-			.saturating_add(RocksDbWeight::get().writes(3_u64))
+			.saturating_add(RocksDbWeight::get().writes(5_u64))
 	}
 	/// Storage: BridgeUnknownMessages PalletOperatingMode (r:1 w:0)
 	///
@@ -510,15 +515,15 @@ impl WeightInfo for () {
 	/// Proof: BridgeUnknownMessages InboundLanes (max_values: None, max_size: Some(49180), added:
 	/// 51655, mode: MaxEncodedLen)
 	///
-	/// The range of component `i` is `[128, 2048]`.
-	fn receive_single_message_proof_with_dispatch(i: u32) -> Weight {
+	/// The range of component `n` is `[1, 16384]`.
+	fn receive_single_n_bytes_message_proof_with_dispatch(n: u32) -> Weight {
 		// Proof Size summary in bytes:
-		//  Measured:  `618`
-		//  Estimated: `57170`
-		// Minimum execution time: 52_385 nanoseconds.
-		Weight::from_parts(54_919_468, 57170)
-			// Standard Error: 108
-			.saturating_add(Weight::from_parts(3_286, 0).saturating_mul(i.into()))
+		//  Measured:  `653`
+		//  Estimated: `52673`
+		// Minimum execution time: 38_925 nanoseconds.
+		Weight::from_parts(39_617_000, 52673)
+			// Standard Error: 612
+			.saturating_add(Weight::from_parts(372_813, 0).saturating_mul(n.into()))
 			.saturating_add(RocksDbWeight::get().reads(3_u64))
 			.saturating_add(RocksDbWeight::get().writes(1_u64))
 	}
diff --git a/bridges/modules/messages/src/weights_ext.rs b/bridges/modules/messages/src/weights_ext.rs
index c12e04f692b..7711e212efb 100644
--- a/bridges/modules/messages/src/weights_ext.rs
+++ b/bridges/modules/messages/src/weights_ext.rs
@@ -40,13 +40,6 @@ pub fn ensure_weights_are_correct<W: WeightInfoExt>() {
 	// benchmarked using `MaxEncodedLen` approach and there are no components that cause additional
 	// db reads
 
-	// verify `receive_messages_proof` weight components
-	assert_ne!(W::receive_messages_proof_overhead().ref_time(), 0);
-	assert_ne!(W::receive_messages_proof_overhead().proof_size(), 0);
-	// W::receive_messages_proof_messages_overhead(1).ref_time() may be zero because:
-	// the message processing code (`InboundLane::receive_message`) is minimal and may not be
-	// accounted by our benchmarks
-	assert_eq!(W::receive_messages_proof_messages_overhead(1).proof_size(), 0);
 	// W::receive_messages_proof_outbound_lane_state_overhead().ref_time() may be zero because:
 	// the outbound lane state processing code (`InboundLane::receive_state_update`) is minimal and
 	// may not be accounted by our benchmarks
@@ -86,6 +79,19 @@ pub fn ensure_weights_are_correct<W: WeightInfoExt>() {
 	total_messages_in_delivery_proof_does_not_affect_proof_size::<W>();
 }
 
+/// Ensure that we are able to dispatch maximal size messages.
+pub fn ensure_maximal_message_dispatch<W: WeightInfoExt>(
+	max_incoming_message_size: u32,
+	max_incoming_message_dispatch_weight: Weight,
+) {
+	let message_dispatch_weight = W::message_dispatch_weight(max_incoming_message_size);
+	assert!(
+		message_dispatch_weight.all_lte(max_incoming_message_dispatch_weight),
+		"Dispatch weight of maximal message {message_dispatch_weight:?} must be lower \
+		than the hardcoded {max_incoming_message_dispatch_weight:?}",
+	);
+}
+
 /// Ensure that we're able to receive maximal (by-size and by-weight) message from other chain.
 pub fn ensure_able_to_receive_message<W: WeightInfoExt>(
 	max_extrinsic_size: u32,
@@ -98,7 +104,8 @@ pub fn ensure_able_to_receive_message<W: WeightInfoExt>(
 		max_incoming_message_proof_size.saturating_add(SIGNED_EXTENSIONS_SIZE);
 	assert!(
 		max_delivery_transaction_size <= max_extrinsic_size,
-		"Size of maximal message delivery transaction {max_incoming_message_proof_size} + {SIGNED_EXTENSIONS_SIZE} is larger than maximal possible transaction size {max_extrinsic_size}",
+		"Size of maximal message delivery transaction {max_incoming_message_proof_size} + \
+		{SIGNED_EXTENSIONS_SIZE} is larger than maximal possible transaction size {max_extrinsic_size}",
 	);
 
 	// verify that we're able to receive proof of maximal-size message with maximal dispatch weight
@@ -297,13 +304,11 @@ pub trait WeightInfoExt: WeightInfo {
 		dispatch_weight: Weight,
 	) -> Weight {
 		// basic components of extrinsic weight
-		let transaction_overhead = Self::receive_messages_proof_overhead();
+		let base_weight = Self::receive_n_messages_proof(messages_count);
 		let transaction_overhead_from_runtime =
 			Self::receive_messages_proof_overhead_from_runtime();
 		let outbound_state_delivery_weight =
 			Self::receive_messages_proof_outbound_lane_state_overhead();
-		let messages_delivery_weight =
-			Self::receive_messages_proof_messages_overhead(MessageNonce::from(messages_count));
 		let messages_dispatch_weight = dispatch_weight;
 
 		// proof size overhead weight
@@ -315,10 +320,9 @@ pub trait WeightInfoExt: WeightInfo {
 			actual_proof_size.saturating_sub(expected_proof_size),
 		);
 
-		transaction_overhead
+		base_weight
 			.saturating_add(transaction_overhead_from_runtime)
 			.saturating_add(outbound_state_delivery_weight)
-			.saturating_add(messages_delivery_weight)
 			.saturating_add(messages_dispatch_weight)
 			.saturating_add(proof_size_overhead)
 	}
@@ -354,25 +358,6 @@ pub trait WeightInfoExt: WeightInfo {
 
 	// Functions that are used by extrinsics weights formulas.
 
-	/// Returns weight overhead of message delivery transaction (`receive_messages_proof`).
-	fn receive_messages_proof_overhead() -> Weight {
-		let weight_of_two_messages_and_two_tx_overheads =
-			Self::receive_single_message_proof().saturating_mul(2);
-		let weight_of_two_messages_and_single_tx_overhead = Self::receive_two_messages_proof();
-		weight_of_two_messages_and_two_tx_overheads
-			.saturating_sub(weight_of_two_messages_and_single_tx_overhead)
-	}
-
-	/// Returns weight that needs to be accounted when receiving given a number of messages with
-	/// message delivery transaction (`receive_messages_proof`).
-	fn receive_messages_proof_messages_overhead(messages: MessageNonce) -> Weight {
-		let weight_of_two_messages_and_single_tx_overhead = Self::receive_two_messages_proof();
-		let weight_of_single_message_and_single_tx_overhead = Self::receive_single_message_proof();
-		weight_of_two_messages_and_single_tx_overhead
-			.saturating_sub(weight_of_single_message_and_single_tx_overhead)
-			.saturating_mul(messages as _)
-	}
-
 	/// Returns weight that needs to be accounted when message delivery transaction
 	/// (`receive_messages_proof`) is carrying outbound lane state proof.
 	fn receive_messages_proof_outbound_lane_state_overhead() -> Weight {
@@ -426,9 +411,8 @@ pub trait WeightInfoExt: WeightInfo {
 	/// is less than that cost).
 	fn storage_proof_size_overhead(proof_size: u32) -> Weight {
 		let proof_size_in_bytes = proof_size;
-		let byte_weight = (Self::receive_single_message_proof_16_kb() -
-			Self::receive_single_message_proof_1_kb()) /
-			(15 * 1024);
+		let byte_weight = Self::receive_single_n_bytes_message_proof(2) -
+			Self::receive_single_n_bytes_message_proof(1);
 		proof_size_in_bytes * byte_weight
 	}
 
@@ -440,11 +424,9 @@ pub trait WeightInfoExt: WeightInfo {
 	/// `receive_single_message_proof_with_dispatch` benchmark. See its requirements for
 	/// details.
 	fn message_dispatch_weight(message_size: u32) -> Weight {
-		// There may be a tiny overweight/underweight here, because we don't account how message
-		// size affects all steps before dispatch. But the effect should be small enough and we
-		// may ignore it.
-		Self::receive_single_message_proof_with_dispatch(message_size)
-			.saturating_sub(Self::receive_single_message_proof())
+		let message_size_in_bytes = message_size;
+		Self::receive_single_n_bytes_message_proof_with_dispatch(message_size_in_bytes)
+			.saturating_sub(Self::receive_single_n_bytes_message_proof(message_size_in_bytes))
 	}
 }
 
@@ -479,7 +461,7 @@ impl<T: frame_system::Config> WeightInfoExt for crate::weights::BridgeWeight<T>
 #[cfg(test)]
 mod tests {
 	use super::*;
-	use crate::{mock::TestRuntime, weights::BridgeWeight};
+	use crate::{tests::mock::TestRuntime, weights::BridgeWeight};
 
 	#[test]
 	fn ensure_default_weights_are_correct() {
diff --git a/bridges/modules/parachains/Cargo.toml b/bridges/modules/parachains/Cargo.toml
index 97bad724a78..cda0ee8106d 100644
--- a/bridges/modules/parachains/Cargo.toml
+++ b/bridges/modules/parachains/Cargo.toml
@@ -30,7 +30,6 @@ frame-support = { workspace = true }
 frame-system = { workspace = true }
 sp-runtime = { workspace = true }
 sp-std = { workspace = true }
-sp-trie = { workspace = true }
 
 [dev-dependencies]
 bp-header-chain = { workspace = true, default-features = true }
@@ -54,7 +53,6 @@ std = [
 	"scale-info/std",
 	"sp-runtime/std",
 	"sp-std/std",
-	"sp-trie/std",
 ]
 runtime-benchmarks = [
 	"frame-benchmarking/runtime-benchmarks",
diff --git a/bridges/modules/parachains/src/benchmarking.rs b/bridges/modules/parachains/src/benchmarking.rs
index 27e06a12a1d..92ece6d688c 100644
--- a/bridges/modules/parachains/src/benchmarking.rs
+++ b/bridges/modules/parachains/src/benchmarking.rs
@@ -22,7 +22,7 @@ use crate::{
 };
 
 use bp_polkadot_core::parachains::{ParaHash, ParaHeadsProof, ParaId};
-use bp_runtime::StorageProofSize;
+use bp_runtime::UnverifiedStorageProofParams;
 use frame_benchmarking::{account, benchmarks_instance_pallet};
 use frame_system::RawOrigin;
 use sp_std::prelude::*;
@@ -38,7 +38,7 @@ pub trait Config<I: 'static>: crate::Config<I> {
 	fn prepare_parachain_heads_proof(
 		parachains: &[ParaId],
 		parachain_head_size: u32,
-		proof_size: StorageProofSize,
+		proof_params: UnverifiedStorageProofParams,
 	) -> (RelayBlockNumber, RelayBlockHash, ParaHeadsProof, Vec<(ParaId, ParaHash)>);
 }
 
@@ -68,7 +68,7 @@ benchmarks_instance_pallet! {
 		let (relay_block_number, relay_block_hash, parachain_heads_proof, parachains_heads) = T::prepare_parachain_heads_proof(
 			&parachains,
 			DEFAULT_PARACHAIN_HEAD_SIZE,
-			StorageProofSize::Minimal(0),
+			UnverifiedStorageProofParams::default(),
 		);
 		let at_relay_block = (relay_block_number, relay_block_hash);
 	}: submit_parachain_heads(RawOrigin::Signed(sender), at_relay_block, parachains_heads, parachain_heads_proof)
@@ -85,7 +85,7 @@ benchmarks_instance_pallet! {
 		let (relay_block_number, relay_block_hash, parachain_heads_proof, parachains_heads) = T::prepare_parachain_heads_proof(
 			&parachains,
 			DEFAULT_PARACHAIN_HEAD_SIZE,
-			StorageProofSize::HasLargeLeaf(1024),
+			UnverifiedStorageProofParams::from_db_size(1024),
 		);
 		let at_relay_block = (relay_block_number, relay_block_hash);
 	}: submit_parachain_heads(RawOrigin::Signed(sender), at_relay_block, parachains_heads, parachain_heads_proof)
@@ -102,7 +102,7 @@ benchmarks_instance_pallet! {
 		let (relay_block_number, relay_block_hash, parachain_heads_proof, parachains_heads) = T::prepare_parachain_heads_proof(
 			&parachains,
 			DEFAULT_PARACHAIN_HEAD_SIZE,
-			StorageProofSize::HasLargeLeaf(16 * 1024),
+			UnverifiedStorageProofParams::from_db_size(16 * 1024),
 		);
 		let at_relay_block = (relay_block_number, relay_block_hash);
 	}: submit_parachain_heads(RawOrigin::Signed(sender), at_relay_block, parachains_heads, parachain_heads_proof)
diff --git a/bridges/modules/parachains/src/call_ext.rs b/bridges/modules/parachains/src/call_ext.rs
index fe6b319205d..0f77eaf2c5a 100644
--- a/bridges/modules/parachains/src/call_ext.rs
+++ b/bridges/modules/parachains/src/call_ext.rs
@@ -289,7 +289,7 @@ mod tests {
 		RuntimeCall::Parachains(crate::Call::<TestRuntime, ()>::submit_parachain_heads_ex {
 			at_relay_block: (num, [num as u8; 32].into()),
 			parachains,
-			parachain_heads_proof: ParaHeadsProof { storage_proof: Vec::new() },
+			parachain_heads_proof: ParaHeadsProof { storage_proof: Default::default() },
 			is_free_execution_expected: false,
 		})
 		.check_obsolete_submit_parachain_heads()
@@ -303,7 +303,7 @@ mod tests {
 		RuntimeCall::Parachains(crate::Call::<TestRuntime, ()>::submit_parachain_heads_ex {
 			at_relay_block: (num, [num as u8; 32].into()),
 			parachains,
-			parachain_heads_proof: ParaHeadsProof { storage_proof: Vec::new() },
+			parachain_heads_proof: ParaHeadsProof { storage_proof: Default::default() },
 			is_free_execution_expected: true,
 		})
 		.check_obsolete_submit_parachain_heads()
diff --git a/bridges/modules/parachains/src/lib.rs b/bridges/modules/parachains/src/lib.rs
index d323aef3b22..e2c30ce9aec 100644
--- a/bridges/modules/parachains/src/lib.rs
+++ b/bridges/modules/parachains/src/lib.rs
@@ -28,11 +28,12 @@ pub use weights::WeightInfo;
 pub use weights_ext::WeightInfoExt;
 
 use bp_header_chain::{HeaderChain, HeaderChainError};
-use bp_parachains::{parachain_head_storage_key_at_source, ParaInfo, ParaStoredHeaderData};
-use bp_polkadot_core::parachains::{ParaHash, ParaHead, ParaHeadsProof, ParaId};
-use bp_runtime::{Chain, HashOf, HeaderId, HeaderIdOf, Parachain, StorageProofError};
+use bp_parachains::{ParaInfo, ParaStoredHeaderData};
+use bp_polkadot_core::parachains::{ParaHash, ParaHeadsProof, ParaId};
+use bp_runtime::{Chain, HashOf, HeaderId, HeaderIdOf, Parachain};
 use frame_support::{dispatch::PostDispatchInfo, DefaultNoBound};
 use pallet_bridge_grandpa::SubmitFinalityProofHelper;
+use proofs::{ParachainsStorageProofAdapter, StorageProofAdapter};
 use sp_std::{marker::PhantomData, vec::Vec};
 
 #[cfg(feature = "runtime-benchmarks")]
@@ -55,6 +56,7 @@ pub mod benchmarking;
 mod call_ext;
 #[cfg(test)]
 mod mock;
+mod proofs;
 
 /// The target that will be used when publishing logs related to this pallet.
 pub const LOG_TARGET: &str = "runtime::bridge-parachains";
@@ -448,15 +450,15 @@ pub mod pallet {
 				parachains.len() as _,
 			);
 
-			let mut is_updated_something = false;
-			let mut storage = GrandpaPalletOf::<T, I>::storage_proof_checker(
-				relay_block_hash,
-				parachain_heads_proof.storage_proof,
-			)
-			.map_err(Error::<T, I>::HeaderChainStorageProof)?;
+			let mut storage: ParachainsStorageProofAdapter<T, I> =
+				ParachainsStorageProofAdapter::try_new_with_verified_storage_proof(
+					relay_block_hash,
+					parachain_heads_proof.storage_proof,
+				)
+				.map_err(Error::<T, I>::HeaderChainStorageProof)?;
 
 			for (parachain, parachain_head_hash) in parachains {
-				let parachain_head = match Self::read_parachain_head(&mut storage, parachain) {
+				let parachain_head = match storage.read_parachain_head(parachain) {
 					Ok(Some(parachain_head)) => parachain_head,
 					Ok(None) => {
 						log::trace!(
@@ -541,7 +543,6 @@ pub mod pallet {
 							parachain_head_hash,
 						)?;
 
-						is_updated_something = true;
 						if is_free {
 							free_parachain_heads = free_parachain_heads + 1;
 						}
@@ -572,7 +573,7 @@ pub mod pallet {
 			// => treat this as an error
 			//
 			// (we can throw error here, because now all our calls are transactional)
-			storage.ensure_no_unused_nodes().map_err(|e| {
+			storage.ensure_no_unused_keys().map_err(|e| {
 				Error::<T, I>::HeaderChainStorageProof(HeaderChainError::StorageProof(e))
 			})?;
 
@@ -633,16 +634,6 @@ pub mod pallet {
 			ImportedParaHeads::<T, I>::get(parachain, hash).map(|h| h.into_inner())
 		}
 
-		/// Read parachain head from storage proof.
-		fn read_parachain_head(
-			storage: &mut bp_runtime::StorageProofChecker<RelayBlockHasher>,
-			parachain: ParaId,
-		) -> Result<Option<ParaHead>, StorageProofError> {
-			let parachain_head_key =
-				parachain_head_storage_key_at_source(T::ParasPalletName::get(), parachain);
-			storage.read_and_decode_value(parachain_head_key.0.as_ref())
-		}
-
 		/// Try to update parachain head.
 		pub(super) fn update_parachain_head(
 			parachain: ParaId,
@@ -801,6 +792,7 @@ impl<T: Config<I>, I: 'static, C: Parachain<Hash = ParaHash>> HeaderChain<C>
 pub fn initialize_for_benchmarks<T: Config<I>, I: 'static, PC: Parachain<Hash = ParaHash>>(
 	header: HeaderOf<PC>,
 ) {
+	use bp_polkadot_core::parachains::ParaHead;
 	use bp_runtime::HeaderIdProvider;
 	use sp_runtime::traits::Header;
 
@@ -844,9 +836,10 @@ pub(crate) mod tests {
 	use bp_parachains::{
 		BestParaHeadHash, BridgeParachainCall, ImportedParaHeadsKeyProvider, ParasInfoKeyProvider,
 	};
+	use bp_polkadot_core::parachains::ParaHead;
 	use bp_runtime::{
 		BasicOperatingMode, OwnedBridgeModuleError, StorageDoubleMapKeyProvider,
-		StorageMapKeyProvider,
+		StorageMapKeyProvider, StorageProofError,
 	};
 	use bp_test_utils::{
 		authority_list, generate_owned_bridge_module_tests, make_default_justification,
diff --git a/bridges/modules/parachains/src/mock.rs b/bridges/modules/parachains/src/mock.rs
index dbb62845392..c49b5939093 100644
--- a/bridges/modules/parachains/src/mock.rs
+++ b/bridges/modules/parachains/src/mock.rs
@@ -23,7 +23,7 @@ use frame_support::{
 use sp_runtime::{
 	testing::H256,
 	traits::{BlakeTwo256, Header as HeaderT},
-	MultiSignature,
+	MultiSignature, StateVersion,
 };
 
 use crate as pallet_bridge_parachains;
@@ -60,6 +60,8 @@ impl Chain for Parachain1 {
 	type Nonce = u64;
 	type Signature = MultiSignature;
 
+	const STATE_VERSION: StateVersion = StateVersion::V1;
+
 	fn max_extrinsic_size() -> u32 {
 		0
 	}
@@ -87,6 +89,8 @@ impl Chain for Parachain2 {
 	type Nonce = u64;
 	type Signature = MultiSignature;
 
+	const STATE_VERSION: StateVersion = StateVersion::V1;
+
 	fn max_extrinsic_size() -> u32 {
 		0
 	}
@@ -114,6 +118,8 @@ impl Chain for Parachain3 {
 	type Nonce = u64;
 	type Signature = MultiSignature;
 
+	const STATE_VERSION: StateVersion = StateVersion::V1;
+
 	fn max_extrinsic_size() -> u32 {
 		0
 	}
@@ -142,6 +148,8 @@ impl Chain for BigParachain {
 	type Nonce = u64;
 	type Signature = MultiSignature;
 
+	const STATE_VERSION: StateVersion = StateVersion::V1;
+
 	fn max_extrinsic_size() -> u32 {
 		0
 	}
@@ -222,7 +230,7 @@ impl pallet_bridge_parachains::benchmarking::Config<()> for TestRuntime {
 	fn prepare_parachain_heads_proof(
 		parachains: &[ParaId],
 		_parachain_head_size: u32,
-		_proof_size: bp_runtime::StorageProofSize,
+		_proof_params: bp_runtime::UnverifiedStorageProofParams,
 	) -> (
 		crate::RelayBlockNumber,
 		crate::RelayBlockHash,
@@ -256,38 +264,7 @@ impl Chain for TestBridgedChain {
 	type Nonce = u32;
 	type Signature = sp_runtime::testing::TestSignature;
 
-	fn max_extrinsic_size() -> u32 {
-		unreachable!()
-	}
-
-	fn max_extrinsic_weight() -> Weight {
-		unreachable!()
-	}
-}
-
-impl ChainWithGrandpa for TestBridgedChain {
-	const WITH_CHAIN_GRANDPA_PALLET_NAME: &'static str = "";
-	const MAX_AUTHORITIES_COUNT: u32 = 16;
-	const REASONABLE_HEADERS_IN_JUSTIFICATION_ANCESTRY: u32 = 8;
-	const MAX_MANDATORY_HEADER_SIZE: u32 = 256;
-	const AVERAGE_HEADER_SIZE: u32 = 64;
-}
-
-#[derive(Debug)]
-pub struct OtherBridgedChain;
-
-impl Chain for OtherBridgedChain {
-	const ID: ChainId = *b"obch";
-
-	type BlockNumber = u64;
-	type Hash = crate::RelayBlockHash;
-	type Hasher = crate::RelayBlockHasher;
-	type Header = sp_runtime::generic::Header<u64, crate::RelayBlockHasher>;
-
-	type AccountId = AccountId;
-	type Balance = u32;
-	type Nonce = u32;
-	type Signature = sp_runtime::testing::TestSignature;
+	const STATE_VERSION: StateVersion = StateVersion::V1;
 
 	fn max_extrinsic_size() -> u32 {
 		unreachable!()
@@ -298,7 +275,7 @@ impl Chain for OtherBridgedChain {
 	}
 }
 
-impl ChainWithGrandpa for OtherBridgedChain {
+impl ChainWithGrandpa for TestBridgedChain {
 	const WITH_CHAIN_GRANDPA_PALLET_NAME: &'static str = "";
 	const MAX_AUTHORITIES_COUNT: u32 = 16;
 	const REASONABLE_HEADERS_IN_JUSTIFICATION_ANCESTRY: u32 = 8;
diff --git a/bridges/modules/parachains/src/proofs.rs b/bridges/modules/parachains/src/proofs.rs
new file mode 100644
index 00000000000..dcf22229f34
--- /dev/null
+++ b/bridges/modules/parachains/src/proofs.rs
@@ -0,0 +1,81 @@
+// Copyright 2019-2021 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/>.
+
+//! Tools for parachain head proof verification.
+
+use crate::{Config, GrandpaPalletOf, RelayBlockHash, RelayBlockHasher};
+use bp_header_chain::{HeaderChain, HeaderChainError};
+use bp_parachains::parachain_head_storage_key_at_source;
+use bp_polkadot_core::parachains::{ParaHead, ParaId};
+use bp_runtime::{RawStorageProof, StorageProofChecker, StorageProofError};
+use codec::Decode;
+use frame_support::traits::Get;
+
+/// Abstraction over storage proof manipulation, hiding implementation details of actual storage
+/// proofs.
+pub trait StorageProofAdapter<T: Config<I>, I: 'static> {
+	/// Read and decode optional value from the proof.
+	fn read_and_decode_optional_value<D: Decode>(
+		&mut self,
+		key: &impl AsRef<[u8]>,
+	) -> Result<Option<D>, StorageProofError>;
+
+	/// Checks if each key was read.
+	fn ensure_no_unused_keys(self) -> Result<(), StorageProofError>;
+
+	/// Read parachain head from storage proof.
+	fn read_parachain_head(
+		&mut self,
+		parachain: ParaId,
+	) -> Result<Option<ParaHead>, StorageProofError> {
+		let parachain_head_key =
+			parachain_head_storage_key_at_source(T::ParasPalletName::get(), parachain);
+		self.read_and_decode_optional_value(&parachain_head_key)
+	}
+}
+
+/// Actual storage proof adapter for parachain proofs.
+pub type ParachainsStorageProofAdapter<T, I> = RawStorageProofAdapter<T, I>;
+
+/// A `StorageProofAdapter` implementation for raw storage proofs.
+pub struct RawStorageProofAdapter<T: Config<I>, I: 'static> {
+	storage: StorageProofChecker<RelayBlockHasher>,
+	_dummy: sp_std::marker::PhantomData<(T, I)>,
+}
+
+impl<T: Config<I>, I: 'static> RawStorageProofAdapter<T, I> {
+	/// Try to create a new instance of `RawStorageProofAdapter`.
+	pub fn try_new_with_verified_storage_proof(
+		relay_block_hash: RelayBlockHash,
+		storage_proof: RawStorageProof,
+	) -> Result<Self, HeaderChainError> {
+		GrandpaPalletOf::<T, I>::verify_storage_proof(relay_block_hash, storage_proof)
+			.map(|storage| RawStorageProofAdapter::<T, I> { storage, _dummy: Default::default() })
+	}
+}
+
+impl<T: Config<I>, I: 'static> StorageProofAdapter<T, I> for RawStorageProofAdapter<T, I> {
+	fn read_and_decode_optional_value<D: Decode>(
+		&mut self,
+		key: &impl AsRef<[u8]>,
+	) -> Result<Option<D>, StorageProofError> {
+		self.storage.read_and_decode_opt_value(key.as_ref())
+	}
+
+	fn ensure_no_unused_keys(self) -> Result<(), StorageProofError> {
+		self.storage.ensure_no_unused_nodes()
+	}
+}
diff --git a/bridges/modules/parachains/src/weights.rs b/bridges/modules/parachains/src/weights.rs
index abddc876894..1f92b7ff763 100644
--- a/bridges/modules/parachains/src/weights.rs
+++ b/bridges/modules/parachains/src/weights.rs
@@ -17,9 +17,9 @@
 //! Autogenerated weights for pallet_bridge_parachains
 //!
 //! THIS FILE WAS AUTO-GENERATED USING THE SUBSTRATE BENCHMARK CLI VERSION 4.0.0-dev
-//! DATE: 2023-03-02, STEPS: `50`, REPEAT: `20`, LOW RANGE: `[]`, HIGH RANGE: `[]`
+//! DATE: 2023-06-22, STEPS: `50`, REPEAT: `20`, LOW RANGE: `[]`, HIGH RANGE: `[]`
 //! WORST CASE MAP SIZE: `1000000`
-//! HOSTNAME: `covid`, CPU: `11th Gen Intel(R) Core(TM) i7-11800H @ 2.30GHz`
+//! HOSTNAME: `serban-ROG-Zephyrus`, CPU: `12th Gen Intel(R) Core(TM) i7-12700H`
 //! EXECUTION: Some(Wasm), WASM-EXECUTION: Compiled, CHAIN: Some("dev"), DB CACHE: 1024
 
 // Executed Command:
@@ -86,14 +86,12 @@ impl<T: frame_system::Config> WeightInfo for BridgeWeight<T> {
 	/// Some(196), added: 1681, mode: MaxEncodedLen)
 	///
 	/// The range of component `p` is `[1, 2]`.
-	fn submit_parachain_heads_with_n_parachains(p: u32) -> Weight {
+	fn submit_parachain_heads_with_n_parachains(_p: u32) -> Weight {
 		// Proof Size summary in bytes:
-		//  Measured:  `366`
-		//  Estimated: `4648`
-		// Minimum execution time: 36_701 nanoseconds.
-		Weight::from_parts(38_597_828, 4648)
-			// Standard Error: 190_859
-			.saturating_add(Weight::from_parts(60_685, 0).saturating_mul(p.into()))
+		//  Measured:  `302`
+		//  Estimated: `3038`
+		// Minimum execution time: 30_211 nanoseconds.
+		Weight::from_parts(32_633_893, 3038)
 			.saturating_add(T::DbWeight::get().reads(4_u64))
 			.saturating_add(T::DbWeight::get().writes(3_u64))
 	}
@@ -123,10 +121,10 @@ impl<T: frame_system::Config> WeightInfo for BridgeWeight<T> {
 	/// Some(196), added: 1681, mode: MaxEncodedLen)
 	fn submit_parachain_heads_with_1kb_proof() -> Weight {
 		// Proof Size summary in bytes:
-		//  Measured:  `366`
-		//  Estimated: `4648`
-		// Minimum execution time: 38_189 nanoseconds.
-		Weight::from_parts(39_252_000, 4648)
+		//  Measured:  `302`
+		//  Estimated: `3038`
+		// Minimum execution time: 30_830 nanoseconds.
+		Weight::from_parts(31_801_000, 3038)
 			.saturating_add(T::DbWeight::get().reads(4_u64))
 			.saturating_add(T::DbWeight::get().writes(3_u64))
 	}
@@ -156,10 +154,10 @@ impl<T: frame_system::Config> WeightInfo for BridgeWeight<T> {
 	/// Some(196), added: 1681, mode: MaxEncodedLen)
 	fn submit_parachain_heads_with_16kb_proof() -> Weight {
 		// Proof Size summary in bytes:
-		//  Measured:  `366`
-		//  Estimated: `4648`
-		// Minimum execution time: 62_868 nanoseconds.
-		Weight::from_parts(63_581_000, 4648)
+		//  Measured:  `302`
+		//  Estimated: `3038`
+		// Minimum execution time: 44_736 nanoseconds.
+		Weight::from_parts(45_296_000, 3038)
 			.saturating_add(T::DbWeight::get().reads(4_u64))
 			.saturating_add(T::DbWeight::get().writes(3_u64))
 	}
@@ -193,14 +191,12 @@ impl WeightInfo for () {
 	/// Some(196), added: 1681, mode: MaxEncodedLen)
 	///
 	/// The range of component `p` is `[1, 2]`.
-	fn submit_parachain_heads_with_n_parachains(p: u32) -> Weight {
+	fn submit_parachain_heads_with_n_parachains(_p: u32) -> Weight {
 		// Proof Size summary in bytes:
-		//  Measured:  `366`
-		//  Estimated: `4648`
-		// Minimum execution time: 36_701 nanoseconds.
-		Weight::from_parts(38_597_828, 4648)
-			// Standard Error: 190_859
-			.saturating_add(Weight::from_parts(60_685, 0).saturating_mul(p.into()))
+		//  Measured:  `302`
+		//  Estimated: `3038`
+		// Minimum execution time: 30_211 nanoseconds.
+		Weight::from_parts(32_633_893, 3038)
 			.saturating_add(RocksDbWeight::get().reads(4_u64))
 			.saturating_add(RocksDbWeight::get().writes(3_u64))
 	}
@@ -230,10 +226,10 @@ impl WeightInfo for () {
 	/// Some(196), added: 1681, mode: MaxEncodedLen)
 	fn submit_parachain_heads_with_1kb_proof() -> Weight {
 		// Proof Size summary in bytes:
-		//  Measured:  `366`
-		//  Estimated: `4648`
-		// Minimum execution time: 38_189 nanoseconds.
-		Weight::from_parts(39_252_000, 4648)
+		//  Measured:  `302`
+		//  Estimated: `3038`
+		// Minimum execution time: 30_830 nanoseconds.
+		Weight::from_parts(31_801_000, 3038)
 			.saturating_add(RocksDbWeight::get().reads(4_u64))
 			.saturating_add(RocksDbWeight::get().writes(3_u64))
 	}
@@ -263,10 +259,10 @@ impl WeightInfo for () {
 	/// Some(196), added: 1681, mode: MaxEncodedLen)
 	fn submit_parachain_heads_with_16kb_proof() -> Weight {
 		// Proof Size summary in bytes:
-		//  Measured:  `366`
-		//  Estimated: `4648`
-		// Minimum execution time: 62_868 nanoseconds.
-		Weight::from_parts(63_581_000, 4648)
+		//  Measured:  `302`
+		//  Estimated: `3038`
+		// Minimum execution time: 44_736 nanoseconds.
+		Weight::from_parts(45_296_000, 3038)
 			.saturating_add(RocksDbWeight::get().reads(4_u64))
 			.saturating_add(RocksDbWeight::get().writes(3_u64))
 	}
diff --git a/bridges/modules/relayers/src/payment_adapter.rs b/bridges/modules/relayers/src/payment_adapter.rs
index b2d9c676bdd..f75c409aca4 100644
--- a/bridges/modules/relayers/src/payment_adapter.rs
+++ b/bridges/modules/relayers/src/payment_adapter.rs
@@ -23,6 +23,7 @@ use bp_messages::{
 	LaneId, MessageNonce,
 };
 use bp_relayers::{RewardsAccountOwner, RewardsAccountParams};
+use bp_runtime::Chain;
 use frame_support::{sp_runtime::SaturatedConversion, traits::Get};
 use sp_arithmetic::traits::{Saturating, Zero};
 use sp_std::{collections::vec_deque::VecDeque, marker::PhantomData, ops::RangeInclusive};
@@ -57,7 +58,7 @@ where
 			relayers_rewards,
 			RewardsAccountParams::new(
 				lane_id,
-				T::BridgedChainId::get(),
+				T::BridgedChain::ID,
 				RewardsAccountOwner::BridgedChain,
 			),
 			DeliveryReward::get(),
diff --git a/bridges/modules/xcm-bridge-hub/src/mock.rs b/bridges/modules/xcm-bridge-hub/src/mock.rs
index 4c09bce56d7..df72e7a3c4f 100644
--- a/bridges/modules/xcm-bridge-hub/src/mock.rs
+++ b/bridges/modules/xcm-bridge-hub/src/mock.rs
@@ -20,23 +20,17 @@ use crate as pallet_xcm_bridge_hub;
 
 use bp_messages::{
 	target_chain::{DispatchMessage, MessageDispatch},
-	LaneId,
-};
-use bp_runtime::{messages::MessageDispatchResult, Chain, ChainId, UnderlyingChainProvider};
-use bridge_runtime_common::{
-	messages::{
-		source::TargetHeaderChainAdapter, target::SourceHeaderChainAdapter,
-		BridgedChainWithMessages, HashOf, MessageBridge, ThisChainWithMessages,
-	},
-	messages_xcm_extension::{SenderAndLane, XcmBlobHauler},
+	ChainWithMessages, LaneId, MessageNonce,
 };
+use bp_runtime::{messages::MessageDispatchResult, Chain, ChainId, HashOf};
+use bridge_runtime_common::messages_xcm_extension::{SenderAndLane, XcmBlobHauler};
 use codec::Encode;
-use frame_support::{derive_impl, parameter_types, traits::ConstU32, weights::RuntimeDbWeight};
+use frame_support::{derive_impl, parameter_types, weights::RuntimeDbWeight};
 use sp_core::H256;
 use sp_runtime::{
 	testing::Header as SubstrateHeader,
 	traits::{BlakeTwo256, IdentityLookup},
-	AccountId32, BuildStorage,
+	AccountId32, BuildStorage, StateVersion,
 };
 use xcm::prelude::*;
 
@@ -85,20 +79,17 @@ impl pallet_bridge_messages::Config for TestRuntime {
 	type RuntimeEvent = RuntimeEvent;
 	type WeightInfo = TestMessagesWeights;
 
-	type BridgedChainId = ();
 	type ActiveOutboundLanes = ActiveOutboundLanes;
-	type MaxUnrewardedRelayerEntriesAtInboundLane = ();
-	type MaxUnconfirmedMessagesAtInboundLane = ();
-	type MaximalOutboundPayloadSize = ConstU32<2048>;
 	type OutboundPayload = Vec<u8>;
 	type InboundPayload = Vec<u8>;
-	type InboundRelayer = ();
 	type DeliveryPayments = ();
-	type TargetHeaderChain = TargetHeaderChainAdapter<OnThisChainBridge>;
 	type DeliveryConfirmationPayments = ();
 	type OnMessagesDelivered = ();
-	type SourceHeaderChain = SourceHeaderChainAdapter<OnThisChainBridge>;
 	type MessageDispatch = TestMessageDispatch;
+
+	type ThisChain = ThisUnderlyingChain;
+	type BridgedChain = BridgedUnderlyingChain;
+	type BridgedHeaderChain = BridgedHeaderChain;
 }
 
 pub struct TestMessagesWeights;
@@ -107,34 +98,27 @@ impl pallet_bridge_messages::WeightInfo for TestMessagesWeights {
 	fn receive_single_message_proof() -> Weight {
 		Weight::zero()
 	}
-	fn receive_single_message_proof_with_outbound_lane_state() -> Weight {
+	fn receive_n_messages_proof(_: u32) -> Weight {
 		Weight::zero()
 	}
-	fn receive_delivery_proof_for_single_message() -> Weight {
+	fn receive_single_message_proof_with_outbound_lane_state() -> Weight {
 		Weight::zero()
 	}
-	fn receive_delivery_proof_for_two_messages_by_single_relayer() -> Weight {
+	fn receive_single_n_bytes_message_proof(_: u32) -> Weight {
 		Weight::zero()
 	}
-	fn receive_delivery_proof_for_two_messages_by_two_relayers() -> Weight {
+	fn receive_delivery_proof_for_single_message() -> Weight {
 		Weight::zero()
 	}
-
-	fn receive_two_messages_proof() -> Weight {
+	fn receive_delivery_proof_for_two_messages_by_single_relayer() -> Weight {
 		Weight::zero()
 	}
-
-	fn receive_single_message_proof_1_kb() -> Weight {
+	fn receive_delivery_proof_for_two_messages_by_two_relayers() -> Weight {
 		Weight::zero()
 	}
-
-	fn receive_single_message_proof_16_kb() -> Weight {
+	fn receive_single_n_bytes_message_proof_with_dispatch(_: u32) -> Weight {
 		Weight::zero()
 	}
-
-	fn receive_single_message_proof_with_dispatch(_: u32) -> Weight {
-		Weight::from_parts(1, 0)
-	}
 }
 
 impl pallet_bridge_messages::WeightInfoExt for TestMessagesWeights {
@@ -198,9 +182,9 @@ impl XcmBlobHauler for TestXcmBlobHauler {
 	type UncongestedMessage = ();
 }
 
-pub struct ThisChain;
+pub struct ThisUnderlyingChain;
 
-impl Chain for ThisChain {
+impl Chain for ThisUnderlyingChain {
 	const ID: ChainId = *b"tuch";
 	type BlockNumber = u64;
 	type Hash = H256;
@@ -211,6 +195,8 @@ impl Chain for ThisChain {
 	type Nonce = u64;
 	type Signature = sp_runtime::MultiSignature;
 
+	const STATE_VERSION: StateVersion = StateVersion::V1;
+
 	fn max_extrinsic_size() -> u32 {
 		u32::MAX
 	}
@@ -220,12 +206,19 @@ impl Chain for ThisChain {
 	}
 }
 
-pub struct BridgedChain;
+impl ChainWithMessages for ThisUnderlyingChain {
+	const WITH_CHAIN_MESSAGES_PALLET_NAME: &'static str = "";
+
+	const MAX_UNREWARDED_RELAYERS_IN_CONFIRMATION_TX: MessageNonce = 16;
+	const MAX_UNCONFIRMED_MESSAGES_IN_CONFIRMATION_TX: MessageNonce = 1000;
+}
+
+pub struct BridgedUnderlyingChain;
 pub type BridgedHeaderHash = H256;
 pub type BridgedChainHeader = SubstrateHeader;
 
-impl Chain for BridgedChain {
-	const ID: ChainId = *b"tuch";
+impl Chain for BridgedUnderlyingChain {
+	const ID: ChainId = *b"bgdc";
 	type BlockNumber = u64;
 	type Hash = BridgedHeaderHash;
 	type Hasher = BlakeTwo256;
@@ -235,6 +228,8 @@ impl Chain for BridgedChain {
 	type Nonce = u64;
 	type Signature = sp_runtime::MultiSignature;
 
+	const STATE_VERSION: StateVersion = StateVersion::V1;
+
 	fn max_extrinsic_size() -> u32 {
 		4096
 	}
@@ -244,6 +239,12 @@ impl Chain for BridgedChain {
 	}
 }
 
+impl ChainWithMessages for BridgedUnderlyingChain {
+	const WITH_CHAIN_MESSAGES_PALLET_NAME: &'static str = "";
+	const MAX_UNREWARDED_RELAYERS_IN_CONFIRMATION_TX: MessageNonce = 16;
+	const MAX_UNCONFIRMED_MESSAGES_IN_CONFIRMATION_TX: MessageNonce = 1000;
+}
+
 /// Test message dispatcher.
 pub struct TestMessageDispatch;
 
@@ -272,42 +273,15 @@ impl MessageDispatch for TestMessageDispatch {
 	}
 }
 
-pub struct WrappedThisChain;
-impl UnderlyingChainProvider for WrappedThisChain {
-	type Chain = ThisChain;
-}
-impl ThisChainWithMessages for WrappedThisChain {
-	type RuntimeOrigin = RuntimeOrigin;
-}
-
-pub struct WrappedBridgedChain;
-impl UnderlyingChainProvider for WrappedBridgedChain {
-	type Chain = BridgedChain;
-}
-impl BridgedChainWithMessages for WrappedBridgedChain {}
-
 pub struct BridgedHeaderChain;
-impl bp_header_chain::HeaderChain<BridgedChain> for BridgedHeaderChain {
+impl bp_header_chain::HeaderChain<BridgedUnderlyingChain> for BridgedHeaderChain {
 	fn finalized_header_state_root(
-		_hash: HashOf<WrappedBridgedChain>,
-	) -> Option<HashOf<WrappedBridgedChain>> {
+		_hash: HashOf<BridgedUnderlyingChain>,
+	) -> Option<HashOf<BridgedUnderlyingChain>> {
 		unreachable!()
 	}
 }
 
-/// Bridge that is deployed on `ThisChain` and allows sending/receiving messages to/from
-/// `BridgedChain`.
-#[derive(Debug, PartialEq, Eq)]
-pub struct OnThisChainBridge;
-
-impl MessageBridge for OnThisChainBridge {
-	const BRIDGED_MESSAGES_PALLET_NAME: &'static str = "";
-
-	type ThisChain = WrappedThisChain;
-	type BridgedChain = WrappedBridgedChain;
-	type BridgedHeaderChain = BridgedHeaderChain;
-}
-
 /// Run pallet test.
 pub fn run_test<T>(test: impl FnOnce() -> T) -> T {
 	sp_io::TestExternalities::new(
diff --git a/bridges/primitives/header-chain/src/lib.rs b/bridges/primitives/header-chain/src/lib.rs
index af2afb65a26..26295dee180 100644
--- a/bridges/primitives/header-chain/src/lib.rs
+++ b/bridges/primitives/header-chain/src/lib.rs
@@ -46,7 +46,7 @@ pub mod storage_keys;
 pub enum HeaderChainError {
 	/// Header with given hash is missing from the chain.
 	UnknownHeader,
-	/// Storage proof related error.
+	/// Error generated by the `storage_proof` module.
 	StorageProof(StorageProofError),
 }
 
@@ -78,8 +78,9 @@ impl<H: HeaderT> StoredHeaderDataBuilder<H::Number, H::Hash> for H {
 pub trait HeaderChain<C: Chain> {
 	/// Returns state (storage) root of given finalized header.
 	fn finalized_header_state_root(header_hash: HashOf<C>) -> Option<HashOf<C>>;
+
 	/// Get storage proof checker using finalized header.
-	fn storage_proof_checker(
+	fn verify_storage_proof(
 		header_hash: HashOf<C>,
 		storage_proof: RawStorageProof,
 	) -> Result<StorageProofChecker<HasherOf<C>>, HeaderChainError> {
@@ -409,7 +410,9 @@ mod tests {
 	use super::*;
 	use bp_runtime::ChainId;
 	use frame_support::weights::Weight;
-	use sp_runtime::{testing::H256, traits::BlakeTwo256, DigestItem, MultiSignature};
+	use sp_runtime::{
+		testing::H256, traits::BlakeTwo256, DigestItem, MultiSignature, StateVersion,
+	};
 
 	struct TestChain;
 
@@ -425,6 +428,8 @@ mod tests {
 		type Nonce = u64;
 		type Signature = MultiSignature;
 
+		const STATE_VERSION: StateVersion = StateVersion::V1;
+
 		fn max_extrinsic_size() -> u32 {
 			0
 		}
diff --git a/bridges/primitives/messages/src/lib.rs b/bridges/primitives/messages/src/lib.rs
index c3f79b3ee38..9984f8ac322 100644
--- a/bridges/primitives/messages/src/lib.rs
+++ b/bridges/primitives/messages/src/lib.rs
@@ -38,6 +38,9 @@ pub mod source_chain;
 pub mod storage_keys;
 pub mod target_chain;
 
+/// Hard limit on message size that can be sent over the bridge.
+pub const HARD_MESSAGE_SIZE_LIMIT: u32 = 64 * 1024;
+
 /// Substrate-based chain with messaging support.
 pub trait ChainWithMessages: Chain {
 	/// Name of the bridge messages pallet (used in `construct_runtime` macro call) that is
@@ -48,11 +51,63 @@ pub trait ChainWithMessages: Chain {
 	const WITH_CHAIN_MESSAGES_PALLET_NAME: &'static str;
 
 	/// Maximal number of unrewarded relayers in a single confirmation transaction at this
-	/// `ChainWithMessages`.
+	/// `ChainWithMessages`. Unrewarded means that the relayer has delivered messages, but
+	/// either confirmations haven't been delivered back to the source chain, or we haven't
+	/// received reward confirmations yet.
+	///
+	/// This constant limits maximal number of entries in the `InboundLaneData::relayers`. Keep
+	/// in mind that the same relayer account may take several (non-consecutive) entries in this
+	/// set.
 	const MAX_UNREWARDED_RELAYERS_IN_CONFIRMATION_TX: MessageNonce;
 	/// Maximal number of unconfirmed messages in a single confirmation transaction at this
-	/// `ChainWithMessages`.
+	/// `ChainWithMessages`. Unconfirmed means that the
+	/// message has been delivered, but either confirmations haven't been delivered back to the
+	/// source chain, or we haven't received reward confirmations for these messages yet.
+	///
+	/// This constant limits difference between last message from last entry of the
+	/// `InboundLaneData::relayers` and first message at the first entry.
+	///
+	/// There is no point of making this parameter lesser than
+	/// `MAX_UNREWARDED_RELAYERS_IN_CONFIRMATION_TX`, because then maximal number of relayer entries
+	/// will be limited by maximal number of messages.
+	///
+	/// This value also represents maximal number of messages in single delivery transaction.
+	/// Transaction that is declaring more messages than this value, will be rejected. Even if
+	/// these messages are from different lanes.
 	const MAX_UNCONFIRMED_MESSAGES_IN_CONFIRMATION_TX: MessageNonce;
+
+	/// Return maximal dispatch weight of the message we're able to receive.
+	fn maximal_incoming_message_dispatch_weight() -> Weight {
+		// we leave 1/2 of `max_extrinsic_weight` for the delivery transaction itself
+		Self::max_extrinsic_weight() / 2
+	}
+
+	/// Return maximal size of the message we're able to receive.
+	fn maximal_incoming_message_size() -> u32 {
+		maximal_incoming_message_size(Self::max_extrinsic_size())
+	}
+}
+
+/// Return maximal size of the message the chain with `max_extrinsic_size` is able to receive.
+pub fn maximal_incoming_message_size(max_extrinsic_size: u32) -> u32 {
+	// The maximal size of extrinsic at Substrate-based chain depends on the
+	// `frame_system::Config::MaximumBlockLength` and
+	// `frame_system::Config::AvailableBlockRatio` constants. This check is here to be sure that
+	// the lane won't stuck because message is too large to fit into delivery transaction.
+	//
+	// **IMPORTANT NOTE**: the delivery transaction contains storage proof of the message, not
+	// the message itself. The proof is always larger than the message. But unless chain state
+	// is enormously large, it should be several dozens/hundreds of bytes. The delivery
+	// transaction also contains signatures and signed extensions. Because of this, we reserve
+	// 1/3 of the the maximal extrinsic size for this data.
+	//
+	// **ANOTHER IMPORTANT NOTE**: large message means not only larger proofs and heavier
+	// proof verification, but also heavier message decoding and dispatch. So we have a hard
+	// limit of `64Kb`, which in practice limits the message size on all chains. Without this
+	// limit the **weight** (not the size) of the message will be higher than the
+	// `Self::maximal_incoming_message_dispatch_weight()`.
+
+	sp_std::cmp::min(max_extrinsic_size / 3 * 2, HARD_MESSAGE_SIZE_LIMIT)
 }
 
 impl<T> ChainWithMessages for T
@@ -112,7 +167,19 @@ impl OperatingMode for MessagesOperatingMode {
 
 /// Lane id which implements `TypeId`.
 #[derive(
-	Clone, Copy, Decode, Default, Encode, Eq, Ord, PartialOrd, PartialEq, TypeInfo, MaxEncodedLen,
+	Clone,
+	Copy,
+	Decode,
+	Default,
+	Encode,
+	Eq,
+	Ord,
+	PartialOrd,
+	PartialEq,
+	TypeInfo,
+	MaxEncodedLen,
+	Serialize,
+	Deserialize,
 )]
 pub struct LaneId(pub [u8; 4]);
 
@@ -435,7 +502,7 @@ where
 	AccountId: sp_std::cmp::Ord,
 {
 	// remember to reward relayers that have delivered messages
-	// this loop is bounded by `T::MaxUnrewardedRelayerEntriesAtInboundLane` on the bridged chain
+	// this loop is bounded by `T::MAX_UNREWARDED_RELAYERS_IN_CONFIRMATION_TX` on the bridged chain
 	let mut relayers_rewards = RelayersRewards::new();
 	for entry in messages_relayers {
 		let nonce_begin = sp_std::cmp::max(entry.messages.begin, *received_range.start());
@@ -486,11 +553,11 @@ pub enum VerificationError {
 	InvalidMessageWeight,
 	/// Declared messages count doesn't match actual value.
 	MessagesCountMismatch,
-	/// Error returned while reading/decoding message data from the storage proof.
+	/// Error returned while reading/decoding message data from the `VerifiedStorageProof`.
 	MessageStorage(StorageProofError),
 	/// The message is too large.
 	MessageTooLarge,
-	/// Error returned while reading/decoding outbound lane data from the storage proof.
+	/// Error returned while reading/decoding outbound lane data from the `VerifiedStorageProof`.
 	OutboundLaneStorage(StorageProofError),
 	/// Storage proof related error.
 	StorageProof(StorageProofError),
diff --git a/bridges/primitives/messages/src/source_chain.rs b/bridges/primitives/messages/src/source_chain.rs
index f4aefd97355..64f015bdb82 100644
--- a/bridges/primitives/messages/src/source_chain.rs
+++ b/bridges/primitives/messages/src/source_chain.rs
@@ -16,11 +16,11 @@
 
 //! Primitives of messages module, that are used on the source chain.
 
-use crate::{InboundLaneData, LaneId, MessageNonce, VerificationError};
+use crate::{LaneId, MessageNonce, UnrewardedRelayer};
 
-use crate::UnrewardedRelayer;
-use bp_runtime::Size;
-use frame_support::Parameter;
+use bp_runtime::{raw_storage_proof_size, RawStorageProof, Size};
+use codec::{Decode, Encode};
+use scale_info::TypeInfo;
 use sp_core::RuntimeDebug;
 use sp_std::{
 	collections::{btree_map::BTreeMap, vec_deque::VecDeque},
@@ -28,42 +28,36 @@ use sp_std::{
 	ops::RangeInclusive,
 };
 
-/// Number of messages, delivered by relayers.
-pub type RelayersRewards<AccountId> = BTreeMap<AccountId, MessageNonce>;
-
-/// Target chain API. Used by source chain to verify target chain proofs.
+/// Messages delivery proof from the bridged chain.
 ///
-/// All implementations of this trait should only work with finalized data that
-/// can't change. Wrong implementation may lead to invalid lane states (i.e. lane
-/// that's stuck) and/or processing messages without paying fees.
+/// It contains everything required to prove that our (this chain) messages have been
+/// delivered to the bridged (target) chain:
 ///
-/// The `Payload` type here means the payload of the message that is sent from the
-/// source chain to the target chain. The `AccountId` type here means the account
-/// type used by the source chain.
-pub trait TargetHeaderChain<Payload, AccountId> {
-	/// Proof that messages have been received by target chain.
-	type MessagesDeliveryProof: Parameter + Size;
-
-	/// Verify message payload before we accept it.
-	///
-	/// **CAUTION**: this is very important function. Incorrect implementation may lead
-	/// to stuck lanes and/or relayers loses.
-	///
-	/// The proper implementation must ensure that the delivery-transaction with this
-	/// payload would (at least) be accepted into target chain transaction pool AND
-	/// eventually will be successfully mined. The most obvious incorrect implementation
-	/// example would be implementation for BTC chain that accepts payloads larger than
-	/// 1MB. BTC nodes aren't accepting transactions that are larger than 1MB, so relayer
-	/// will be unable to craft valid transaction => this (and all subsequent) messages will
-	/// never be delivered.
-	fn verify_message(payload: &Payload) -> Result<(), VerificationError>;
-
-	/// Verify messages delivery proof and return lane && nonce of the latest received message.
-	fn verify_messages_delivery_proof(
-		proof: Self::MessagesDeliveryProof,
-	) -> Result<(LaneId, InboundLaneData<AccountId>), VerificationError>;
+/// - hash of finalized header;
+///
+/// - storage proof of the inbound lane state;
+///
+/// - lane id.
+#[derive(Clone, Decode, Encode, Eq, PartialEq, RuntimeDebug, TypeInfo)]
+pub struct FromBridgedChainMessagesDeliveryProof<BridgedHeaderHash> {
+	/// Hash of the bridge header the proof is for.
+	pub bridged_header_hash: BridgedHeaderHash,
+	/// Storage trie proof generated for [`Self::bridged_header_hash`].
+	pub storage_proof: RawStorageProof,
+	/// Lane id of which messages were delivered and the proof is for.
+	pub lane: LaneId,
+}
+
+impl<BridgedHeaderHash> Size for FromBridgedChainMessagesDeliveryProof<BridgedHeaderHash> {
+	fn size(&self) -> u32 {
+		use frame_support::sp_runtime::SaturatedConversion;
+		raw_storage_proof_size(&self.storage_proof).saturated_into()
+	}
 }
 
+/// Number of messages, delivered by relayers.
+pub type RelayersRewards<AccountId> = BTreeMap<AccountId, MessageNonce>;
+
 /// Manages payments that are happening at the source chain during delivery confirmation
 /// transaction.
 pub trait DeliveryConfirmationPayments<AccountId> {
@@ -143,28 +137,10 @@ pub trait MessagesBridge<Payload> {
 	fn send_message(message: Self::SendMessageArgs) -> SendMessageArtifacts;
 }
 
-/// Structure that may be used in place of `TargetHeaderChain` and
-/// `MessageDeliveryAndDispatchPayment` on chains, where outbound messages are forbidden.
+/// Structure that may be used in place `MessageDeliveryAndDispatchPayment` on chains,
+/// where outbound messages are forbidden.
 pub struct ForbidOutboundMessages;
 
-/// Error message that is used in `ForbidOutboundMessages` implementation.
-const ALL_OUTBOUND_MESSAGES_REJECTED: &str =
-	"This chain is configured to reject all outbound messages";
-
-impl<Payload, AccountId> TargetHeaderChain<Payload, AccountId> for ForbidOutboundMessages {
-	type MessagesDeliveryProof = ();
-
-	fn verify_message(_payload: &Payload) -> Result<(), VerificationError> {
-		Err(VerificationError::Other(ALL_OUTBOUND_MESSAGES_REJECTED))
-	}
-
-	fn verify_messages_delivery_proof(
-		_proof: Self::MessagesDeliveryProof,
-	) -> Result<(LaneId, InboundLaneData<AccountId>), VerificationError> {
-		Err(VerificationError::Other(ALL_OUTBOUND_MESSAGES_REJECTED))
-	}
-}
-
 impl<AccountId> DeliveryConfirmationPayments<AccountId> for ForbidOutboundMessages {
 	type Error = &'static str;
 
diff --git a/bridges/primitives/messages/src/target_chain.rs b/bridges/primitives/messages/src/target_chain.rs
index 388ce16ccdc..74fecb9d9f0 100644
--- a/bridges/primitives/messages/src/target_chain.rs
+++ b/bridges/primitives/messages/src/target_chain.rs
@@ -16,17 +16,48 @@
 
 //! Primitives of messages module, that are used on the target chain.
 
-use crate::{
-	LaneId, Message, MessageKey, MessageNonce, MessagePayload, OutboundLaneData, VerificationError,
-};
+use crate::{LaneId, Message, MessageKey, MessageNonce, MessagePayload, OutboundLaneData};
 
-use bp_runtime::{messages::MessageDispatchResult, Size};
+use bp_runtime::{messages::MessageDispatchResult, raw_storage_proof_size, RawStorageProof, Size};
 use codec::{Decode, Encode, Error as CodecError};
-use frame_support::{weights::Weight, Parameter};
+use frame_support::weights::Weight;
 use scale_info::TypeInfo;
 use sp_core::RuntimeDebug;
 use sp_std::{collections::btree_map::BTreeMap, fmt::Debug, marker::PhantomData, prelude::*};
 
+/// Messages proof from bridged chain.
+///
+/// It contains everything required to prove that bridged (source) chain has
+/// sent us some messages:
+///
+/// - hash of finalized header;
+///
+/// - storage proof of messages and (optionally) outbound lane state;
+///
+/// - lane id;
+///
+/// - nonces (inclusive range) of messages which are included in this proof.
+#[derive(Clone, Decode, Encode, Eq, PartialEq, RuntimeDebug, TypeInfo)]
+pub struct FromBridgedChainMessagesProof<BridgedHeaderHash> {
+	/// 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,
+	/// 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> {
+	fn size(&self) -> u32 {
+		use frame_support::sp_runtime::SaturatedConversion;
+		raw_storage_proof_size(&self.storage_proof).saturated_into()
+	}
+}
+
 /// Proved messages from the source chain.
 pub type ProvedMessages<Message> = BTreeMap<LaneId, ProvedLaneMessages<Message>>;
 
@@ -55,33 +86,6 @@ pub struct DispatchMessage<DispatchPayload> {
 	pub data: DispatchMessageData<DispatchPayload>,
 }
 
-/// Source chain API. Used by target chain, to verify source chain proofs.
-///
-/// All implementations of this trait should only work with finalized data that
-/// can't change. Wrong implementation may lead to invalid lane states (i.e. lane
-/// that's stuck) and/or processing messages without paying fees.
-pub trait SourceHeaderChain {
-	/// Proof that messages are sent from source chain. This may also include proof
-	/// of corresponding outbound lane states.
-	type MessagesProof: Parameter + Size;
-
-	/// Verify messages proof and return proved messages.
-	///
-	/// Returns error if either proof is incorrect, or the number of messages in the proof
-	/// is not matching the `messages_count`.
-	///
-	/// Messages vector is required to be sorted by nonce within each lane. Out-of-order
-	/// messages will be rejected.
-	///
-	/// The `messages_count` argument verification (sane limits) is supposed to be made
-	/// outside this function. This function only verifies that the proof declares exactly
-	/// `messages_count` messages.
-	fn verify_messages_proof(
-		proof: Self::MessagesProof,
-		messages_count: u32,
-	) -> Result<ProvedMessages<Message>, VerificationError>;
-}
-
 /// Called when inbound message is received.
 pub trait MessageDispatch {
 	/// Decoded message payload type. Valid message may contain invalid payload. In this case
@@ -167,32 +171,11 @@ impl<AccountId> DeliveryPayments<AccountId> for () {
 	}
 }
 
-/// Structure that may be used in place of `SourceHeaderChain` and `MessageDispatch` on chains,
+/// Structure that may be used in place of  `MessageDispatch` on chains,
 /// where inbound messages are forbidden.
-pub struct ForbidInboundMessages<MessagesProof, DispatchPayload>(
-	PhantomData<(MessagesProof, DispatchPayload)>,
-);
-
-/// Error message that is used in `ForbidInboundMessages` implementation.
-const ALL_INBOUND_MESSAGES_REJECTED: &str =
-	"This chain is configured to reject all inbound messages";
-
-impl<MessagesProof: Parameter + Size, DispatchPayload> SourceHeaderChain
-	for ForbidInboundMessages<MessagesProof, DispatchPayload>
-{
-	type MessagesProof = MessagesProof;
-
-	fn verify_messages_proof(
-		_proof: Self::MessagesProof,
-		_messages_count: u32,
-	) -> Result<ProvedMessages<Message>, VerificationError> {
-		Err(VerificationError::Other(ALL_INBOUND_MESSAGES_REJECTED))
-	}
-}
+pub struct ForbidInboundMessages<DispatchPayload>(PhantomData<DispatchPayload>);
 
-impl<MessagesProof, DispatchPayload: Decode> MessageDispatch
-	for ForbidInboundMessages<MessagesProof, DispatchPayload>
-{
+impl<DispatchPayload: Decode> MessageDispatch for ForbidInboundMessages<DispatchPayload> {
 	type DispatchPayload = DispatchPayload;
 	type DispatchLevelResult = ();
 
diff --git a/bridges/primitives/polkadot-core/src/parachains.rs b/bridges/primitives/polkadot-core/src/parachains.rs
index 433cd2845ab..d54ee108386 100644
--- a/bridges/primitives/polkadot-core/src/parachains.rs
+++ b/bridges/primitives/polkadot-core/src/parachains.rs
@@ -22,7 +22,7 @@
 //! parachains. Having pallets that are referencing polkadot, would mean that there may
 //! be two versions of polkadot crates included in the runtime. Which is bad.
 
-use bp_runtime::{RawStorageProof, Size};
+use bp_runtime::{raw_storage_proof_size, RawStorageProof, Size};
 use codec::{CompactAs, Decode, Encode, MaxEncodedLen};
 use scale_info::TypeInfo;
 use sp_core::Hasher;
@@ -96,11 +96,7 @@ pub struct ParaHeadsProof {
 
 impl Size for ParaHeadsProof {
 	fn size(&self) -> u32 {
-		u32::try_from(
-			self.storage_proof
-				.iter()
-				.fold(0usize, |sum, node| sum.saturating_add(node.len())),
-		)
-		.unwrap_or(u32::MAX)
+		use frame_support::sp_runtime::SaturatedConversion;
+		raw_storage_proof_size(&self.storage_proof).saturated_into()
 	}
 }
diff --git a/bridges/primitives/runtime/Cargo.toml b/bridges/primitives/runtime/Cargo.toml
index 5fa35e68899..117409b37b9 100644
--- a/bridges/primitives/runtime/Cargo.toml
+++ b/bridges/primitives/runtime/Cargo.toml
@@ -53,3 +53,4 @@ std = [
 	"sp-trie/std",
 	"trie-db/std",
 ]
+test-helpers = []
diff --git a/bridges/primitives/runtime/src/chain.rs b/bridges/primitives/runtime/src/chain.rs
index 369386e41b0..0db4eac79a7 100644
--- a/bridges/primitives/runtime/src/chain.rs
+++ b/bridges/primitives/runtime/src/chain.rs
@@ -24,7 +24,7 @@ use sp_runtime::{
 		AtLeast32Bit, AtLeast32BitUnsigned, Hash as HashT, Header as HeaderT, MaybeDisplay,
 		MaybeSerialize, MaybeSerializeDeserialize, Member, SimpleBitOps, Verify,
 	},
-	FixedPointOperand,
+	FixedPointOperand, StateVersion,
 };
 use sp_std::{fmt::Debug, hash::Hash, str::FromStr, vec, vec::Vec};
 
@@ -196,6 +196,10 @@ pub trait Chain: Send + Sync + 'static {
 	/// Signature type, used on this chain.
 	type Signature: Parameter + Verify;
 
+	/// Version of the state implementation used by this chain. This is directly related with the
+	/// `TrieLayout` configuration used by the storage.
+	const STATE_VERSION: StateVersion;
+
 	/// Get the maximum size (in bytes) of a Normal extrinsic at this chain.
 	fn max_extrinsic_size() -> u32;
 	/// Get the maximum weight (compute time) that a Normal extrinsic at this chain can use.
@@ -223,6 +227,8 @@ where
 	type Nonce = <T::Chain as Chain>::Nonce;
 	type Signature = <T::Chain as Chain>::Signature;
 
+	const STATE_VERSION: StateVersion = <T::Chain as Chain>::STATE_VERSION;
+
 	fn max_extrinsic_size() -> u32 {
 		<T::Chain as Chain>::max_extrinsic_size()
 	}
diff --git a/bridges/primitives/runtime/src/lib.rs b/bridges/primitives/runtime/src/lib.rs
index d13c9b40efa..8f5040ad9a1 100644
--- a/bridges/primitives/runtime/src/lib.rs
+++ b/bridges/primitives/runtime/src/lib.rs
@@ -40,15 +40,18 @@ pub use chain::{
 };
 pub use frame_support::storage::storage_prefix as storage_value_final_key;
 use num_traits::{CheckedAdd, CheckedSub, One, SaturatingAdd, Zero};
+#[cfg(feature = "std")]
+pub use storage_proof::craft_valid_storage_proof;
+#[cfg(feature = "test-helpers")]
 pub use storage_proof::{
-	record_all_keys as record_all_trie_keys, Error as StorageProofError,
-	ProofSize as StorageProofSize, RawStorageProof, StorageProofChecker,
+	grow_storage_proof, grow_storage_value, record_all_keys as record_all_trie_keys,
+	UnverifiedStorageProofParams,
+};
+pub use storage_proof::{
+	raw_storage_proof_size, RawStorageProof, StorageProofChecker, StorageProofError,
 };
 pub use storage_types::BoundedStorageValue;
 
-#[cfg(feature = "std")]
-pub use storage_proof::craft_valid_storage_proof;
-
 pub mod extensions;
 pub mod messages;
 
@@ -461,38 +464,6 @@ macro_rules! generate_static_str_provider {
 	};
 }
 
-/// Error message that is only displayable in `std` environment.
-#[derive(Encode, Decode, Clone, Eq, PartialEq, PalletError, TypeInfo)]
-#[scale_info(skip_type_params(T))]
-pub struct StrippableError<T> {
-	_phantom_data: sp_std::marker::PhantomData<T>,
-	#[codec(skip)]
-	#[cfg(feature = "std")]
-	message: String,
-}
-
-impl<T: Debug> From<T> for StrippableError<T> {
-	fn from(_err: T) -> Self {
-		Self {
-			_phantom_data: Default::default(),
-			#[cfg(feature = "std")]
-			message: format!("{:?}", _err),
-		}
-	}
-}
-
-impl<T> Debug for StrippableError<T> {
-	#[cfg(feature = "std")]
-	fn fmt(&self, f: &mut sp_std::fmt::Formatter<'_>) -> sp_std::fmt::Result {
-		f.write_str(&self.message)
-	}
-
-	#[cfg(not(feature = "std"))]
-	fn fmt(&self, f: &mut sp_std::fmt::Formatter<'_>) -> sp_std::fmt::Result {
-		f.write_str("Stripped error")
-	}
-}
-
 /// A trait defining helper methods for `RangeInclusive` (start..=end)
 pub trait RangeInclusiveExt<Idx> {
 	/// Computes the length of the `RangeInclusive`, checking for underflow and overflow.
diff --git a/bridges/primitives/runtime/src/storage_proof.rs b/bridges/primitives/runtime/src/storage_proof.rs
index 251ceec5a9e..7bfa0d6fde0 100644
--- a/bridges/primitives/runtime/src/storage_proof.rs
+++ b/bridges/primitives/runtime/src/storage_proof.rs
@@ -14,34 +14,91 @@
 // 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/>.
 
-//! Logic for checking Substrate storage proofs.
+//! Logic for working with storage proofs.
 
-use crate::StrippableError;
-use codec::{Decode, Encode};
 use frame_support::PalletError;
-use hash_db::{HashDB, Hasher, EMPTY_PREFIX};
-use scale_info::TypeInfo;
-use sp_std::{boxed::Box, vec::Vec};
-pub use sp_trie::RawStorageProof;
+use sp_core::RuntimeDebug;
+use sp_std::{default::Default, vec::Vec};
 use sp_trie::{
-	accessed_nodes_tracker::{AccessedNodesTracker, Error as AccessedNodesTrackerError},
-	read_trie_value,
-	recorder_ext::RecorderExt,
-	LayoutV1, MemoryDB, Recorder, StorageProof, StorageProofError, Trie, TrieConfiguration,
-	TrieDBBuilder, TrieError, TrieHash,
+	accessed_nodes_tracker::AccessedNodesTracker, read_trie_value, LayoutV1, MemoryDB, StorageProof,
 };
 
-/// Storage proof size requirements.
+use codec::{Decode, Encode};
+use hash_db::{HashDB, Hasher, EMPTY_PREFIX};
+use scale_info::TypeInfo;
+#[cfg(feature = "test-helpers")]
+use sp_trie::{recorder_ext::RecorderExt, Recorder, TrieDBBuilder, TrieError, TrieHash};
+#[cfg(feature = "test-helpers")]
+use trie_db::{Trie, TrieConfiguration, TrieDBMut};
+
+/// Errors that can occur when interacting with `UnverifiedStorageProof` and `VerifiedStorageProof`.
+#[derive(Clone, Encode, Decode, RuntimeDebug, PartialEq, Eq, PalletError, TypeInfo)]
+pub enum StorageProofError {
+	/// Call to `generate_trie_proof()` failed.
+	UnableToGenerateTrieProof,
+	/// Call to `verify_trie_proof()` failed.
+	InvalidProof,
+	/// The `Vec` entries weren't sorted as expected.
+	UnsortedEntries,
+	/// The provided key wasn't found.
+	UnavailableKey,
+	/// The value associated to the provided key is `None`.
+	EmptyVal,
+	/// Error decoding value associated to a provided key.
+	DecodeError,
+	/// At least one key or node wasn't read.
+	UnusedKey,
+
+	/// Expected storage root is missing from the proof. (for non-compact proofs)
+	StorageRootMismatch,
+	/// Unable to reach expected storage value using provided trie nodes. (for non-compact proofs)
+	StorageValueUnavailable,
+	/// The proof contains duplicate nodes. (for non-compact proofs)
+	DuplicateNodes,
+}
+
+impl From<sp_trie::StorageProofError> for StorageProofError {
+	fn from(e: sp_trie::StorageProofError) -> Self {
+		match e {
+			sp_trie::StorageProofError::DuplicateNodes => StorageProofError::DuplicateNodes,
+		}
+	}
+}
+
+impl From<sp_trie::accessed_nodes_tracker::Error> for StorageProofError {
+	fn from(e: sp_trie::accessed_nodes_tracker::Error) -> Self {
+		match e {
+			sp_trie::accessed_nodes_tracker::Error::UnusedNodes => StorageProofError::UnusedKey,
+		}
+	}
+}
+
+/// Raw storage proof type (just raw trie nodes).
+pub type RawStorageProof = sp_trie::RawStorageProof;
+
+/// Calculates size for `RawStorageProof`.
+pub fn raw_storage_proof_size(raw_storage_proof: &RawStorageProof) -> usize {
+	raw_storage_proof
+		.iter()
+		.fold(0usize, |sum, node| sum.saturating_add(node.len()))
+}
+
+/// Storage values size requirements.
 ///
 /// This is currently used by benchmarks when generating storage proofs.
-#[derive(Clone, Copy, Debug)]
-pub enum ProofSize {
-	/// The proof is expected to be minimal. If value size may be changed, then it is expected to
-	/// have given size.
-	Minimal(u32),
-	/// The proof is expected to have at least given size and grow by increasing value that is
-	/// stored in the trie.
-	HasLargeLeaf(u32),
+#[cfg(feature = "test-helpers")]
+#[derive(Clone, Copy, Debug, Default)]
+pub struct UnverifiedStorageProofParams {
+	/// Expected storage proof size in bytes.
+	pub db_size: Option<u32>,
+}
+
+#[cfg(feature = "test-helpers")]
+impl UnverifiedStorageProofParams {
+	/// Make storage proof parameters that require proof of at least `db_size` bytes.
+	pub fn from_db_size(db_size: u32) -> Self {
+		Self { db_size: Some(db_size) }
+	}
 }
 
 /// This struct is used to read storage values from a subset of a Merklized database. The "proof"
@@ -64,15 +121,14 @@ where
 	/// Constructs a new storage proof checker.
 	///
 	/// This returns an error if the given proof is invalid with respect to the given root.
-	pub fn new(root: H::Out, proof: RawStorageProof) -> Result<Self, Error> {
-		let proof = StorageProof::new_with_duplicate_nodes_check(proof)
-			.map_err(|e| Error::StorageProof(e.into()))?;
+	pub fn new(root: H::Out, proof: RawStorageProof) -> Result<Self, StorageProofError> {
+		let proof = StorageProof::new_with_duplicate_nodes_check(proof)?;
 
 		let recorder = AccessedNodesTracker::new(proof.len());
 
 		let db = proof.into_memory_db();
 		if !db.contains(&root, EMPTY_PREFIX) {
-			return Err(Error::StorageRootMismatch)
+			return Err(StorageProofError::StorageRootMismatch)
 		}
 
 		Ok(StorageProofChecker { root, db, accessed_nodes_tracker: recorder })
@@ -80,15 +136,13 @@ where
 
 	/// Returns error if the proof has some nodes that are left intact by previous `read_value`
 	/// calls.
-	pub fn ensure_no_unused_nodes(self) -> Result<(), Error> {
-		self.accessed_nodes_tracker
-			.ensure_no_unused_nodes()
-			.map_err(|e| Error::AccessedNodesTracker(e.into()))
+	pub fn ensure_no_unused_nodes(self) -> Result<(), StorageProofError> {
+		self.accessed_nodes_tracker.ensure_no_unused_nodes().map_err(Into::into)
 	}
 
 	/// Reads a value from the available subset of storage. If the value cannot be read due to an
 	/// incomplete or otherwise invalid proof, this function returns an error.
-	pub fn read_value(&mut self, key: &[u8]) -> Result<Option<Vec<u8>>, Error> {
+	pub fn read_value(&mut self, key: &[u8]) -> Result<Option<Vec<u8>>, StorageProofError> {
 		// LayoutV1 or LayoutV0 is identical for proof that only read values.
 		read_trie_value::<LayoutV1<H>, _>(
 			&self.db,
@@ -97,53 +151,131 @@ where
 			Some(&mut self.accessed_nodes_tracker),
 			None,
 		)
-		.map_err(|_| Error::StorageValueUnavailable)
+		.map_err(|_| StorageProofError::StorageValueUnavailable)
 	}
 
 	/// Reads and decodes a value from the available subset of storage. If the value cannot be read
 	/// due to an incomplete or otherwise invalid proof, this function returns an error. If value is
 	/// read, but decoding fails, this function returns an error.
-	pub fn read_and_decode_value<T: Decode>(&mut self, key: &[u8]) -> Result<Option<T>, Error> {
+	pub fn read_and_decode_value<T: Decode>(
+		&mut self,
+		key: &[u8],
+	) -> Result<Option<T>, StorageProofError> {
 		self.read_value(key).and_then(|v| {
-			v.map(|v| T::decode(&mut &v[..]).map_err(|e| Error::StorageValueDecodeFailed(e.into())))
-				.transpose()
+			v.map(|v| {
+				T::decode(&mut &v[..]).map_err(|e| {
+					log::warn!(target: "bridge-storage-proofs", "read_and_decode_value error: {e:?}");
+					StorageProofError::DecodeError
+				})
+			})
+			.transpose()
 		})
 	}
 
 	/// Reads and decodes a value from the available subset of storage. If the value cannot be read
 	/// due to an incomplete or otherwise invalid proof, or if the value is `None`, this function
 	/// returns an error. If value is read, but decoding fails, this function returns an error.
-	pub fn read_and_decode_mandatory_value<T: Decode>(&mut self, key: &[u8]) -> Result<T, Error> {
-		self.read_and_decode_value(key)?.ok_or(Error::StorageValueEmpty)
+	pub fn read_and_decode_mandatory_value<T: Decode>(
+		&mut self,
+		key: &[u8],
+	) -> Result<T, StorageProofError> {
+		self.read_and_decode_value(key)?.ok_or(StorageProofError::EmptyVal)
 	}
 
 	/// Reads and decodes a value from the available subset of storage. If the value cannot be read
 	/// due to an incomplete or otherwise invalid proof, this function returns `Ok(None)`.
 	/// If value is read, but decoding fails, this function returns an error.
-	pub fn read_and_decode_opt_value<T: Decode>(&mut self, key: &[u8]) -> Result<Option<T>, Error> {
+	pub fn read_and_decode_opt_value<T: Decode>(
+		&mut self,
+		key: &[u8],
+	) -> Result<Option<T>, StorageProofError> {
 		match self.read_and_decode_value(key) {
 			Ok(outbound_lane_data) => Ok(outbound_lane_data),
-			Err(Error::StorageValueUnavailable) => Ok(None),
+			Err(StorageProofError::StorageValueUnavailable) => Ok(None),
 			Err(e) => Err(e),
 		}
 	}
 }
 
-/// Storage proof related errors.
-#[derive(Encode, Decode, Clone, Eq, PartialEq, PalletError, Debug, TypeInfo)]
-pub enum Error {
-	/// Error generated by the `AccessedNodesTrackerError`.
-	AccessedNodesTracker(StrippableError<AccessedNodesTrackerError>),
-	/// Error originating in the `storage_proof` module.
-	StorageProof(StrippableError<StorageProofError>),
-	/// Expected storage root is missing from the proof.
-	StorageRootMismatch,
-	/// Unable to reach expected storage value using provided trie nodes.
-	StorageValueUnavailable,
-	/// The storage value is `None`.
-	StorageValueEmpty,
-	/// Failed to decode storage value.
-	StorageValueDecodeFailed(StrippableError<codec::Error>),
+/// Add extra data to the storage value so that it'll be of given size.
+#[cfg(feature = "test-helpers")]
+pub fn grow_storage_value(mut value: Vec<u8>, params: &UnverifiedStorageProofParams) -> Vec<u8> {
+	if let Some(db_size) = params.db_size {
+		if db_size as usize > value.len() {
+			value.extend(sp_std::iter::repeat(42u8).take(db_size as usize - value.len()));
+		}
+	}
+	value
+}
+
+/// Insert values in the provided trie at common-prefix keys in order to inflate the resulting
+/// storage proof.
+///
+/// This function can add at most 15 common-prefix keys per prefix nibble (4 bits).
+/// Each such key adds about 33 bytes (a node) to the proof.
+#[cfg(feature = "test-helpers")]
+pub fn grow_storage_proof<L: TrieConfiguration>(
+	trie: &mut TrieDBMut<L>,
+	prefix: Vec<u8>,
+	num_extra_nodes: usize,
+) {
+	use sp_trie::TrieMut;
+
+	let mut added_nodes = 0;
+	for i in 0..prefix.len() {
+		let mut prefix = prefix[0..=i].to_vec();
+		// 1 byte has 2 nibbles (4 bits each)
+		let first_nibble = (prefix[i] & 0xf0) >> 4;
+		let second_nibble = prefix[i] & 0x0f;
+
+		// create branches at the 1st nibble
+		for branch in 1..=15 {
+			if added_nodes >= num_extra_nodes {
+				return
+			}
+
+			// create branches at the 1st nibble
+			prefix[i] = (first_nibble.wrapping_add(branch) % 16) << 4;
+			trie.insert(&prefix, &[0; 32])
+				.map_err(|_| "TrieMut::insert has failed")
+				.expect("TrieMut::insert should not fail in benchmarks");
+			added_nodes += 1;
+		}
+
+		// create branches at the 2nd nibble
+		for branch in 1..=15 {
+			if added_nodes >= num_extra_nodes {
+				return
+			}
+
+			prefix[i] = (first_nibble << 4) | (second_nibble.wrapping_add(branch) % 16);
+			trie.insert(&prefix, &[0; 32])
+				.map_err(|_| "TrieMut::insert has failed")
+				.expect("TrieMut::insert should not fail in benchmarks");
+			added_nodes += 1;
+		}
+	}
+
+	assert_eq!(added_nodes, num_extra_nodes)
+}
+
+/// Record all keys for a given root.
+#[cfg(feature = "test-helpers")]
+pub fn record_all_keys<L: TrieConfiguration, DB>(
+	db: &DB,
+	root: &TrieHash<L>,
+) -> Result<RawStorageProof, sp_std::boxed::Box<TrieError<L>>>
+where
+	DB: hash_db::HashDBRef<L::Hash, trie_db::DBValue>,
+{
+	let mut recorder = Recorder::<L>::new();
+	let trie = TrieDBBuilder::<L>::new(db, root).with_recorder(&mut recorder).build();
+	for x in trie.iter()? {
+		let (key, _) = x?;
+		trie.get(&key)?;
+	}
+
+	Ok(recorder.into_raw_storage_proof())
 }
 
 /// Return valid storage proof and state root.
@@ -157,7 +289,7 @@ pub fn craft_valid_storage_proof() -> (sp_core::H256, RawStorageProof) {
 
 	// construct storage proof
 	let backend = <InMemoryBackend<sp_core::Blake2Hasher>>::from((
-		vec![
+		sp_std::vec![
 			(None, vec![(b"key1".to_vec(), Some(b"value1".to_vec()))]),
 			(None, vec![(b"key2".to_vec(), Some(b"value2".to_vec()))]),
 			(None, vec![(b"key3".to_vec(), Some(b"value3".to_vec()))]),
@@ -167,33 +299,15 @@ pub fn craft_valid_storage_proof() -> (sp_core::H256, RawStorageProof) {
 		],
 		state_version,
 	));
-	let root = backend.storage_root(std::iter::empty(), state_version).0;
+	let root = backend.storage_root(sp_std::iter::empty(), state_version).0;
 	let proof =
 		prove_read(backend, &[&b"key1"[..], &b"key2"[..], &b"key4"[..], &b"key22"[..]]).unwrap();
 
 	(root, proof.into_nodes().into_iter().collect())
 }
 
-/// Record all keys for a given root.
-pub fn record_all_keys<L: TrieConfiguration, DB>(
-	db: &DB,
-	root: &TrieHash<L>,
-) -> Result<RawStorageProof, Box<TrieError<L>>>
-where
-	DB: hash_db::HashDBRef<L::Hash, trie_db::DBValue>,
-{
-	let mut recorder = Recorder::<L>::new();
-	let trie = TrieDBBuilder::<L>::new(db, root).with_recorder(&mut recorder).build();
-	for x in trie.iter()? {
-		let (key, _) = x?;
-		trie.get(&key)?;
-	}
-
-	Ok(recorder.into_raw_storage_proof())
-}
-
 #[cfg(test)]
-pub mod tests {
+pub mod tests_for_storage_proof_checker {
 	use super::*;
 	use codec::Encode;
 
@@ -207,18 +321,21 @@ pub mod tests {
 		assert_eq!(checker.read_value(b"key1"), Ok(Some(b"value1".to_vec())));
 		assert_eq!(checker.read_value(b"key2"), Ok(Some(b"value2".to_vec())));
 		assert_eq!(checker.read_value(b"key4"), Ok(Some((42u64, 42u32, 42u16, 42u8).encode())));
-		assert_eq!(checker.read_value(b"key11111"), Err(Error::StorageValueUnavailable));
+		assert_eq!(
+			checker.read_value(b"key11111"),
+			Err(StorageProofError::StorageValueUnavailable)
+		);
 		assert_eq!(checker.read_value(b"key22"), Ok(None));
 		assert_eq!(checker.read_and_decode_value(b"key4"), Ok(Some((42u64, 42u32, 42u16, 42u8))),);
 		assert!(matches!(
 			checker.read_and_decode_value::<[u8; 64]>(b"key4"),
-			Err(Error::StorageValueDecodeFailed(_)),
+			Err(StorageProofError::DecodeError),
 		));
 
 		// checking proof against invalid commitment fails
 		assert_eq!(
 			<StorageProofChecker<sp_core::Blake2Hasher>>::new(sp_core::H256::random(), proof).err(),
-			Some(Error::StorageRootMismatch)
+			Some(StorageProofError::StorageRootMismatch)
 		);
 	}
 
@@ -235,9 +352,6 @@ pub mod tests {
 		assert_eq!(checker.ensure_no_unused_nodes(), Ok(()));
 
 		let checker = StorageProofChecker::<sp_core::Blake2Hasher>::new(root, proof).unwrap();
-		assert_eq!(
-			checker.ensure_no_unused_nodes(),
-			Err(Error::AccessedNodesTracker(AccessedNodesTrackerError::UnusedNodes.into()))
-		);
+		assert_eq!(checker.ensure_no_unused_nodes(), Err(StorageProofError::UnusedKey));
 	}
 }
diff --git a/bridges/primitives/test-utils/Cargo.toml b/bridges/primitives/test-utils/Cargo.toml
index 0b7fb3fec07..5e6e3893393 100644
--- a/bridges/primitives/test-utils/Cargo.toml
+++ b/bridges/primitives/test-utils/Cargo.toml
@@ -14,7 +14,7 @@ workspace = true
 bp-header-chain = { workspace = true }
 bp-parachains = { workspace = true }
 bp-polkadot-core = { workspace = true }
-bp-runtime = { workspace = true }
+bp-runtime = { features = ["test-helpers"], workspace = true }
 codec = { workspace = true }
 ed25519-dalek = { workspace = true }
 finality-grandpa = { workspace = true }
diff --git a/bridges/primitives/test-utils/src/lib.rs b/bridges/primitives/test-utils/src/lib.rs
index f4fe4a242e7..9855c32a468 100644
--- a/bridges/primitives/test-utils/src/lib.rs
+++ b/bridges/primitives/test-utils/src/lib.rs
@@ -177,6 +177,7 @@ pub fn prepare_parachain_heads_proof<H: HeaderT>(
 	let mut parachains = Vec::with_capacity(heads.len());
 	let mut root = Default::default();
 	let mut mdb = MemoryDB::default();
+	let mut storage_keys = vec![];
 	{
 		let mut trie = TrieDBMutBuilderV1::<H::Hashing>::new(&mut mdb, &mut root).build();
 		for (parachain, head) in heads {
@@ -185,11 +186,12 @@ pub fn prepare_parachain_heads_proof<H: HeaderT>(
 			trie.insert(&storage_key.0, &head.encode())
 				.map_err(|_| "TrieMut::insert has failed")
 				.expect("TrieMut::insert should not fail in tests");
+			storage_keys.push(storage_key.0);
 			parachains.push((ParaId(parachain), head.hash()));
 		}
 	}
 
-	// generate storage proof to be delivered to This chain
+	// generate storage proof to be delivered to this chain
 	let storage_proof = record_all_trie_keys::<LayoutV1<H::Hashing>, _>(&mdb, &root)
 		.map_err(|_| "record_all_trie_keys has failed")
 		.expect("record_all_trie_keys should not fail in benchmarks");
diff --git a/bridges/relays/client-substrate/Cargo.toml b/bridges/relays/client-substrate/Cargo.toml
index 66501d03691..969cd73d619 100644
--- a/bridges/relays/client-substrate/Cargo.toml
+++ b/bridges/relays/client-substrate/Cargo.toml
@@ -31,15 +31,12 @@ bp-header-chain = { workspace = true, default-features = true }
 bp-messages = { workspace = true, default-features = true }
 bp-polkadot-core = { workspace = true, default-features = true }
 bp-runtime = { workspace = true, default-features = true }
-pallet-bridge-messages = { workspace = true, default-features = true }
 finality-relay = { workspace = true }
 relay-utils = { workspace = true }
 
 # Substrate Dependencies
 
 frame-support = { workspace = true, default-features = true }
-frame-system = { workspace = true, default-features = true }
-pallet-balances = { workspace = true, default-features = true }
 pallet-transaction-payment = { workspace = true, default-features = true }
 pallet-transaction-payment-rpc-runtime-api = { workspace = true, default-features = true }
 pallet-utility = { workspace = true, default-features = true }
diff --git a/bridges/relays/client-substrate/src/client/caching.rs b/bridges/relays/client-substrate/src/client/caching.rs
index cb898cf5172..a574e5985bc 100644
--- a/bridges/relays/client-substrate/src/client/caching.rs
+++ b/bridges/relays/client-substrate/src/client/caching.rs
@@ -462,7 +462,11 @@ impl<C: Chain, B: Client<C>> Client<C> for CachingClient<C, B> {
 		.await
 	}
 
-	async fn prove_storage(&self, at: HashOf<C>, keys: Vec<StorageKey>) -> Result<StorageProof> {
+	async fn prove_storage(
+		&self,
+		at: HashOf<C>,
+		keys: Vec<StorageKey>,
+	) -> Result<(StorageProof, HashOf<C>)> {
 		self.backend.prove_storage(at, keys).await
 	}
 }
diff --git a/bridges/relays/client-substrate/src/client/rpc.rs b/bridges/relays/client-substrate/src/client/rpc.rs
index bf7442a9514..9c7f769462e 100644
--- a/bridges/relays/client-substrate/src/client/rpc.rs
+++ b/bridges/relays/client-substrate/src/client/rpc.rs
@@ -52,7 +52,10 @@ use sp_core::{
 	storage::{StorageData, StorageKey},
 	Bytes, Hasher, Pair,
 };
-use sp_runtime::transaction_validity::{TransactionSource, TransactionValidity};
+use sp_runtime::{
+	traits::Header,
+	transaction_validity::{TransactionSource, TransactionValidity},
+};
 use sp_trie::StorageProof;
 use sp_version::RuntimeVersion;
 use std::{cmp::Ordering, future::Future, marker::PhantomData};
@@ -635,16 +638,25 @@ impl<C: Chain> Client<C> for RpcClient<C> {
 		.map_err(|e| Error::failed_state_call::<C>(at, method_clone, arguments_clone, e))
 	}
 
-	async fn prove_storage(&self, at: HashOf<C>, keys: Vec<StorageKey>) -> Result<StorageProof> {
+	async fn prove_storage(
+		&self,
+		at: HashOf<C>,
+		keys: Vec<StorageKey>,
+	) -> Result<(StorageProof, HashOf<C>)> {
+		let state_root = *self.header_by_hash(at).await?.state_root();
+
 		let keys_clone = keys.clone();
-		self.jsonrpsee_execute(move |client| async move {
-			SubstrateStateClient::<C>::prove_storage(&*client, keys, Some(at))
-				.await
-				.map(|proof| StorageProof::new(proof.proof.into_iter().map(|b| b.0)))
-				.map_err(Into::into)
-		})
-		.await
-		.map_err(|e| Error::failed_to_prove_storage::<C>(at, keys_clone, e))
+		let read_proof = self
+			.jsonrpsee_execute(move |client| async move {
+				SubstrateStateClient::<C>::prove_storage(&*client, keys_clone, Some(at))
+					.await
+					.map(|proof| StorageProof::new(proof.proof.into_iter().map(|b| b.0)))
+					.map_err(Into::into)
+			})
+			.await
+			.map_err(|e| Error::failed_to_prove_storage::<C>(at, keys.clone(), e))?;
+
+		Ok((read_proof, state_root))
 	}
 }
 
diff --git a/bridges/relays/client-substrate/src/client/traits.rs b/bridges/relays/client-substrate/src/client/traits.rs
index 49f5c001c3f..6f4ef5aa951 100644
--- a/bridges/relays/client-substrate/src/client/traits.rs
+++ b/bridges/relays/client-substrate/src/client/traits.rs
@@ -225,6 +225,10 @@ pub trait Client<C: Chain>: 'static + Send + Sync + Clone + Debug {
 		})
 	}
 
-	/// Returns storage proof of given storage keys.
-	async fn prove_storage(&self, at: HashOf<C>, keys: Vec<StorageKey>) -> Result<StorageProof>;
+	/// Returns storage proof of given storage keys and state root.
+	async fn prove_storage(
+		&self,
+		at: HashOf<C>,
+		keys: Vec<StorageKey>,
+	) -> Result<(StorageProof, HashOf<C>)>;
 }
diff --git a/bridges/relays/client-substrate/src/error.rs b/bridges/relays/client-substrate/src/error.rs
index b09e2c7abdc..ee3c73f806e 100644
--- a/bridges/relays/client-substrate/src/error.rs
+++ b/bridges/relays/client-substrate/src/error.rs
@@ -213,9 +213,6 @@ pub enum Error {
 	/// The bridge pallet is not yet initialized and all transactions will be rejected.
 	#[error("Bridge pallet is not initialized.")]
 	BridgePalletIsNotInitialized,
-	/// An error has happened when we have tried to parse storage proof.
-	#[error("Error when parsing storage proof: {0:?}.")]
-	StorageProofError(bp_runtime::StorageProofError),
 	/// The Substrate transaction is invalid.
 	#[error("Substrate transaction is invalid: {0:?}")]
 	TransactionInvalid(#[from] TransactionValidityError),
diff --git a/bridges/relays/client-substrate/src/test_chain.rs b/bridges/relays/client-substrate/src/test_chain.rs
index cfd241c022a..991202e9874 100644
--- a/bridges/relays/client-substrate/src/test_chain.rs
+++ b/bridges/relays/client-substrate/src/test_chain.rs
@@ -24,7 +24,7 @@
 use crate::{Chain, ChainWithBalances, ChainWithMessages};
 use bp_messages::{ChainWithMessages as ChainWithMessagesBase, MessageNonce};
 use bp_runtime::ChainId;
-use frame_support::weights::Weight;
+use frame_support::{sp_runtime::StateVersion, weights::Weight};
 use std::time::Duration;
 
 /// Chain that may be used in tests.
@@ -44,6 +44,8 @@ impl bp_runtime::Chain for TestChain {
 	type Nonce = u32;
 	type Signature = sp_runtime::testing::TestSignature;
 
+	const STATE_VERSION: StateVersion = StateVersion::V1;
+
 	fn max_extrinsic_size() -> u32 {
 		100000
 	}
@@ -100,6 +102,8 @@ impl bp_runtime::Chain for TestParachainBase {
 	type Nonce = u32;
 	type Signature = sp_runtime::testing::TestSignature;
 
+	const STATE_VERSION: StateVersion = StateVersion::V1;
+
 	fn max_extrinsic_size() -> u32 {
 		unreachable!()
 	}
diff --git a/bridges/relays/finality/src/base.rs b/bridges/relays/finality/src/base.rs
index 4253468eaac..8704bff9549 100644
--- a/bridges/relays/finality/src/base.rs
+++ b/bridges/relays/finality/src/base.rs
@@ -45,7 +45,3 @@ pub trait SourceClientBase<P: FinalityPipeline>: RelayClient {
 	/// Subscribe to new finality proofs.
 	async fn finality_proofs(&self) -> Result<Self::FinalityProofsStream, Self::Error>;
 }
-
-/// Target client used in finality related loops.
-#[async_trait]
-pub trait TargetClientBase<P: FinalityPipeline>: RelayClient {}
diff --git a/bridges/relays/lib-substrate-relay/Cargo.toml b/bridges/relays/lib-substrate-relay/Cargo.toml
index 28fee5b167f..b0f93e5b548 100644
--- a/bridges/relays/lib-substrate-relay/Cargo.toml
+++ b/bridges/relays/lib-substrate-relay/Cargo.toml
@@ -25,15 +25,12 @@ strum = { features = ["derive"], workspace = true, default-features = true }
 thiserror = { workspace = true }
 
 # Bridge dependencies
-
 bp-header-chain = { workspace = true, default-features = true }
 bp-parachains = { workspace = true, default-features = true }
 bp-polkadot-core = { workspace = true, default-features = true }
 bp-relayers = { workspace = true, default-features = true }
-bridge-runtime-common = { workspace = true, default-features = true }
 
 equivocation-detector = { workspace = true }
-finality-grandpa = { workspace = true, default-features = true }
 finality-relay = { workspace = true }
 parachains-relay = { workspace = true }
 relay-utils = { workspace = true }
@@ -48,7 +45,6 @@ bp-runtime = { workspace = true, default-features = true }
 bp-messages = { workspace = true, default-features = true }
 
 # Substrate Dependencies
-
 frame-support = { workspace = true, default-features = true }
 frame-system = { workspace = true, default-features = true }
 pallet-balances = { workspace = true, default-features = true }
@@ -56,7 +52,9 @@ pallet-grandpa = { workspace = true, default-features = true }
 sp-core = { workspace = true, default-features = true }
 sp-consensus-grandpa = { workspace = true, default-features = true }
 sp-runtime = { workspace = true, default-features = true }
+sp-trie = { workspace = true }
 
 [dev-dependencies]
+scale-info = { features = ["derive"], workspace = true }
 pallet-transaction-payment = { workspace = true, default-features = true }
 relay-substrate-client = { features = ["test-helpers"], workspace = true }
diff --git a/bridges/relays/lib-substrate-relay/src/cli/bridge.rs b/bridges/relays/lib-substrate-relay/src/cli/bridge.rs
index 316f59a2b0c..5631285b3c5 100644
--- a/bridges/relays/lib-substrate-relay/src/cli/bridge.rs
+++ b/bridges/relays/lib-substrate-relay/src/cli/bridge.rs
@@ -19,7 +19,7 @@
 use crate::{
 	equivocation::SubstrateEquivocationDetectionPipeline,
 	finality::SubstrateFinalitySyncPipeline,
-	messages_lane::{MessagesRelayLimits, SubstrateMessageLane},
+	messages::{MessagesRelayLimits, SubstrateMessageLane},
 	parachains::SubstrateParachainsPipeline,
 };
 use pallet_bridge_parachains::{RelayBlockHash, RelayBlockHasher, RelayBlockNumber};
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 05a061c2ea6..338dda3c633 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
@@ -38,7 +38,7 @@ use futures::{FutureExt, TryFutureExt};
 
 use crate::{
 	cli::{bridge::MessagesCliBridge, DefaultClient, HexLaneId, PrometheusParams},
-	messages_lane::{MessagesRelayLimits, MessagesRelayParams},
+	messages::{MessagesRelayLimits, MessagesRelayParams},
 	on_demand::OnDemandRelay,
 	HeadersToRelay, TaggedAccount, TransactionParams,
 };
@@ -298,14 +298,14 @@ where
 			.collect::<Vec<_>>();
 		{
 			let common = self.mut_base().mut_common();
-			crate::messages_metrics::add_relay_balances_metrics::<_, Self::Right>(
+			crate::messages::metrics::add_relay_balances_metrics::<_, Self::Right>(
 				common.left.client.clone(),
 				&common.metrics_params,
 				&common.left.accounts,
 				&lanes,
 			)
 			.await?;
-			crate::messages_metrics::add_relay_balances_metrics::<_, Self::Left>(
+			crate::messages::metrics::add_relay_balances_metrics::<_, Self::Left>(
 				common.right.client.clone(),
 				&common.metrics_params,
 				&common.right.accounts,
@@ -318,7 +318,7 @@ where
 		let mut message_relays = Vec::with_capacity(lanes.len() * 2);
 		for lane in lanes {
 			let left_to_right_messages =
-				crate::messages_lane::run::<<Self::L2R as MessagesCliBridge>::MessagesLane, _, _>(
+				crate::messages::run::<<Self::L2R as MessagesCliBridge>::MessagesLane, _, _>(
 					self.left_to_right().messages_relay_params(
 						left_to_right_on_demand_headers.clone(),
 						right_to_left_on_demand_headers.clone(),
@@ -331,7 +331,7 @@ where
 			message_relays.push(left_to_right_messages);
 
 			let right_to_left_messages =
-				crate::messages_lane::run::<<Self::R2L as MessagesCliBridge>::MessagesLane, _, _>(
+				crate::messages::run::<<Self::R2L as MessagesCliBridge>::MessagesLane, _, _>(
 					self.right_to_left().messages_relay_params(
 						right_to_left_on_demand_headers.clone(),
 						left_to_right_on_demand_headers.clone(),
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 a17ae7c0c01..68bbe71ae59 100644
--- a/bridges/relays/lib-substrate-relay/src/cli/relay_messages.rs
+++ b/bridges/relays/lib-substrate-relay/src/cli/relay_messages.rs
@@ -18,7 +18,7 @@
 
 use crate::{
 	cli::{bridge::*, chain_schema::*, HexLaneId, PrometheusParams},
-	messages_lane::MessagesRelayParams,
+	messages::MessagesRelayParams,
 	TransactionParams,
 };
 
@@ -117,7 +117,7 @@ where
 		let target_sign = data.target_sign.to_keypair::<Self::Target>()?;
 		let target_transactions_mortality = data.target_sign.transactions_mortality()?;
 
-		crate::messages_lane::run::<Self::MessagesLane, _, _>(MessagesRelayParams {
+		crate::messages::run::<Self::MessagesLane, _, _>(MessagesRelayParams {
 			source_client,
 			source_transaction_params: TransactionParams {
 				signer: source_sign,
@@ -161,7 +161,7 @@ where
 			})?
 			.id();
 
-		crate::messages_lane::relay_messages_range::<Self::MessagesLane>(
+		crate::messages::relay_messages_range::<Self::MessagesLane>(
 			source_client,
 			target_client,
 			TransactionParams { signer: source_sign, mortality: source_transactions_mortality },
@@ -197,7 +197,7 @@ where
 			})?
 			.id();
 
-		crate::messages_lane::relay_messages_delivery_confirmation::<Self::MessagesLane>(
+		crate::messages::relay_messages_delivery_confirmation::<Self::MessagesLane>(
 			source_client,
 			target_client,
 			TransactionParams { signer: source_sign, mortality: source_transactions_mortality },
diff --git a/bridges/relays/lib-substrate-relay/src/lib.rs b/bridges/relays/lib-substrate-relay/src/lib.rs
index b3e8e7ed9a2..c004540a9f4 100644
--- a/bridges/relays/lib-substrate-relay/src/lib.rs
+++ b/bridges/relays/lib-substrate-relay/src/lib.rs
@@ -30,10 +30,7 @@ pub mod equivocation;
 pub mod error;
 pub mod finality;
 pub mod finality_base;
-pub mod messages_lane;
-pub mod messages_metrics;
-pub mod messages_source;
-pub mod messages_target;
+pub mod messages;
 pub mod on_demand;
 pub mod parachains;
 
@@ -130,3 +127,17 @@ impl<Call> BatchCallBuilder<Call> for () {
 		unreachable!("never called, because ()::new_builder() returns None; qed")
 	}
 }
+
+/// Module for handling storage proofs compatibility.
+pub mod proofs {
+	use bp_runtime::{HashOf, RawStorageProof};
+	use relay_substrate_client::Chain;
+	use sp_trie::StorageProof;
+
+	/// Converts proof to `RawStorageProof` type.
+	pub fn to_raw_storage_proof<SourceChain: Chain>(
+		proof: (StorageProof, HashOf<SourceChain>),
+	) -> RawStorageProof {
+		proof.0.into_iter_nodes().collect()
+	}
+}
diff --git a/bridges/relays/lib-substrate-relay/src/messages_metrics.rs b/bridges/relays/lib-substrate-relay/src/messages/metrics.rs
similarity index 100%
rename from bridges/relays/lib-substrate-relay/src/messages_metrics.rs
rename to bridges/relays/lib-substrate-relay/src/messages/metrics.rs
diff --git a/bridges/relays/lib-substrate-relay/src/messages_lane.rs b/bridges/relays/lib-substrate-relay/src/messages/mod.rs
similarity index 63%
rename from bridges/relays/lib-substrate-relay/src/messages_lane.rs
rename to bridges/relays/lib-substrate-relay/src/messages/mod.rs
index e3786dcdc5e..e52b7020666 100644
--- a/bridges/relays/lib-substrate-relay/src/messages_lane.rs
+++ b/bridges/relays/lib-substrate-relay/src/messages/mod.rs
@@ -17,20 +17,21 @@
 //! Tools for supporting message lanes between two Substrate-based chains.
 
 use crate::{
-	messages_source::{SubstrateMessagesProof, SubstrateMessagesSource},
-	messages_target::{SubstrateMessagesDeliveryProof, SubstrateMessagesTarget},
+	messages::{
+		source::{SubstrateMessagesProof, SubstrateMessagesSource},
+		target::{SubstrateMessagesDeliveryProof, SubstrateMessagesTarget},
+	},
 	on_demand::OnDemandRelay,
 	BatchCallBuilder, BatchCallBuilderConstructor, TransactionParams,
 };
 
 use async_std::sync::Arc;
-use bp_messages::{ChainWithMessages as _, LaneId, MessageNonce};
+use bp_messages::{
+	target_chain::FromBridgedChainMessagesProof, ChainWithMessages as _, LaneId, MessageNonce,
+};
 use bp_runtime::{
 	AccountIdOf, Chain as _, EncodedOrDecodedCall, HeaderIdOf, TransactionEra, WeightExtraOps,
 };
-use bridge_runtime_common::messages::{
-	source::FromBridgedChainMessagesDeliveryProof, target::FromBridgedChainMessagesProof,
-};
 use codec::Encode;
 use frame_support::{dispatch::GetDispatchInfo, weights::Weight};
 use messages_relay::{message_lane::MessageLane, message_lane_loop::BatchTransaction};
@@ -48,6 +49,10 @@ use sp_core::Pair;
 use sp_runtime::traits::Zero;
 use std::{fmt::Debug, marker::PhantomData, ops::RangeInclusive};
 
+pub mod metrics;
+pub mod source;
+pub mod target;
+
 /// Substrate -> Substrate messages synchronization pipeline.
 pub trait SubstrateMessageLane: 'static + Clone + Debug + Send + Sync {
 	/// Messages of this chain are relayed to the `TargetChain`.
@@ -383,11 +388,10 @@ pub struct DirectReceiveMessagesProofCallBuilder<P, R, I> {
 impl<P, R, I> ReceiveMessagesProofCallBuilder<P> for DirectReceiveMessagesProofCallBuilder<P, R, I>
 where
 	P: SubstrateMessageLane,
-	R: BridgeMessagesConfig<I, InboundRelayer = AccountIdOf<P::SourceChain>>,
+	R: BridgeMessagesConfig<I>,
 	I: 'static,
-	R::SourceHeaderChain: bp_messages::target_chain::SourceHeaderChain<
-		MessagesProof = FromBridgedChainMessagesProof<HashOf<P::SourceChain>>,
-	>,
+	R::BridgedChain:
+		bp_runtime::Chain<AccountId = AccountIdOf<P::SourceChain>, Hash = HashOf<P::SourceChain>>,
 	CallOf<P::TargetChain>: From<BridgeMessagesCall<R, I>> + GetDispatchInfo,
 {
 	fn build_receive_messages_proof_call(
@@ -399,7 +403,7 @@ where
 	) -> CallOf<P::TargetChain> {
 		let call: CallOf<P::TargetChain> = BridgeMessagesCall::<R, I>::receive_messages_proof {
 			relayer_id_at_bridged_chain: relayer_id_at_source,
-			proof: proof.1,
+			proof: proof.1.into(),
 			messages_count,
 			dispatch_weight,
 		}
@@ -432,26 +436,26 @@ macro_rules! generate_receive_message_proof_call_builder {
 	($pipeline:ident, $mocked_builder:ident, $bridge_messages:path, $receive_messages_proof:path) => {
 		pub struct $mocked_builder;
 
-		impl $crate::messages_lane::ReceiveMessagesProofCallBuilder<$pipeline>
+		impl $crate::messages::ReceiveMessagesProofCallBuilder<$pipeline>
 			for $mocked_builder
 		{
 			fn build_receive_messages_proof_call(
 				relayer_id_at_source: relay_substrate_client::AccountIdOf<
-					<$pipeline as $crate::messages_lane::SubstrateMessageLane>::SourceChain
+					<$pipeline as $crate::messages::SubstrateMessageLane>::SourceChain
 				>,
-				proof: $crate::messages_source::SubstrateMessagesProof<
-					<$pipeline as $crate::messages_lane::SubstrateMessageLane>::SourceChain
+				proof: $crate::messages::source::SubstrateMessagesProof<
+					<$pipeline as $crate::messages::SubstrateMessageLane>::SourceChain
 				>,
 				messages_count: u32,
 				dispatch_weight: bp_messages::Weight,
 				_trace_call: bool,
 			) -> relay_substrate_client::CallOf<
-				<$pipeline as $crate::messages_lane::SubstrateMessageLane>::TargetChain
+				<$pipeline as $crate::messages::SubstrateMessageLane>::TargetChain
 			> {
 				bp_runtime::paste::item! {
 					$bridge_messages($receive_messages_proof {
 						relayer_id_at_bridged_chain: relayer_id_at_source,
-						proof: proof.1,
+						proof: proof.1.into(),
 						messages_count: messages_count,
 						dispatch_weight: dispatch_weight,
 					})
@@ -483,11 +487,7 @@ where
 	P: SubstrateMessageLane,
 	R: BridgeMessagesConfig<I>,
 	I: 'static,
-	R::TargetHeaderChain: bp_messages::source_chain::TargetHeaderChain<
-		R::OutboundPayload,
-		R::AccountId,
-		MessagesDeliveryProof = FromBridgedChainMessagesDeliveryProof<HashOf<P::TargetChain>>,
-	>,
+	R::BridgedChain: bp_runtime::Chain<Hash = HashOf<P::TargetChain>>,
 	CallOf<P::SourceChain>: From<BridgeMessagesCall<R, I>> + GetDispatchInfo,
 {
 	fn build_receive_messages_delivery_proof_call(
@@ -496,7 +496,7 @@ where
 	) -> CallOf<P::SourceChain> {
 		let call: CallOf<P::SourceChain> =
 			BridgeMessagesCall::<R, I>::receive_messages_delivery_proof {
-				proof: proof.1,
+				proof: proof.1.into(),
 				relayers_state: proof.0,
 			}
 			.into();
@@ -528,16 +528,16 @@ macro_rules! generate_receive_message_delivery_proof_call_builder {
 	($pipeline:ident, $mocked_builder:ident, $bridge_messages:path, $receive_messages_delivery_proof:path) => {
 		pub struct $mocked_builder;
 
-		impl $crate::messages_lane::ReceiveMessagesDeliveryProofCallBuilder<$pipeline>
+		impl $crate::messages::ReceiveMessagesDeliveryProofCallBuilder<$pipeline>
 			for $mocked_builder
 		{
 			fn build_receive_messages_delivery_proof_call(
-				proof: $crate::messages_target::SubstrateMessagesDeliveryProof<
-					<$pipeline as $crate::messages_lane::SubstrateMessageLane>::TargetChain
+				proof: $crate::messages::target::SubstrateMessagesDeliveryProof<
+					<$pipeline as $crate::messages::SubstrateMessageLane>::TargetChain
 				>,
 				_trace_call: bool,
 			) -> relay_substrate_client::CallOf<
-				<$pipeline as $crate::messages_lane::SubstrateMessageLane>::SourceChain
+				<$pipeline as $crate::messages::SubstrateMessageLane>::SourceChain
 			> {
 				bp_runtime::paste::item! {
 					$bridge_messages($receive_messages_delivery_proof {
@@ -643,13 +643,7 @@ where
 				Weight::zero(),
 				FromBridgedChainMessagesProof {
 					bridged_header_hash: Default::default(),
-					// we may use per-chain `EXTRA_STORAGE_PROOF_SIZE`, but since we don't need
-					// exact values, this global estimation is fine
-					storage_proof: vec![vec![
-						42u8;
-						pallet_bridge_messages::EXTRA_STORAGE_PROOF_SIZE
-							as usize
-					]],
+					storage_proof: Default::default(),
 					lane: Default::default(),
 					nonces_start: 1,
 					nonces_end: messages as u64,
@@ -675,3 +669,362 @@ where
 	)
 	.map_err(Into::into)
 }
+
+#[cfg(test)]
+mod tests {
+	use super::*;
+	use bp_messages::{
+		source_chain::FromBridgedChainMessagesDeliveryProof, UnrewardedRelayersState,
+	};
+	use relay_substrate_client::calls::{UtilityCall as MockUtilityCall, UtilityCall};
+
+	#[derive(codec::Decode, codec::Encode, Clone, Debug, PartialEq)]
+	pub enum RuntimeCall {
+		#[codec(index = 53)]
+		BridgeMessages(CodegenBridgeMessagesCall),
+		#[codec(index = 123)]
+		Utility(UtilityCall<RuntimeCall>),
+	}
+	pub type CodegenBridgeMessagesCall = bp_messages::BridgeMessagesCall<
+		u64,
+		Box<FromBridgedChainMessagesProof<mock::BridgedHeaderHash>>,
+		FromBridgedChainMessagesDeliveryProof<mock::BridgedHeaderHash>,
+	>;
+
+	impl From<MockUtilityCall<RuntimeCall>> for RuntimeCall {
+		fn from(value: MockUtilityCall<RuntimeCall>) -> RuntimeCall {
+			match value {
+				MockUtilityCall::batch_all(calls) =>
+					RuntimeCall::Utility(UtilityCall::<RuntimeCall>::batch_all(calls)),
+			}
+		}
+	}
+
+	#[test]
+	fn ensure_macro_compatibility_for_generate_receive_message_proof_call_builder() {
+		// data
+		let receive_messages_proof = FromBridgedChainMessagesProof {
+			bridged_header_hash: Default::default(),
+			storage_proof: Default::default(),
+			lane: LaneId([0, 0, 0, 0]),
+			nonces_start: 0,
+			nonces_end: 0,
+		};
+		let account = 1234;
+		let messages_count = 0;
+		let dispatch_weight = Default::default();
+
+		// construct pallet Call directly
+		let pallet_receive_messages_proof =
+			pallet_bridge_messages::Call::<mock::TestRuntime>::receive_messages_proof {
+				relayer_id_at_bridged_chain: account,
+				proof: receive_messages_proof.clone().into(),
+				messages_count,
+				dispatch_weight,
+			};
+
+		// construct mock enum Call
+		let mock_enum_receive_messages_proof = CodegenBridgeMessagesCall::receive_messages_proof {
+			relayer_id_at_bridged_chain: account,
+			proof: receive_messages_proof.clone().into(),
+			messages_count,
+			dispatch_weight,
+		};
+
+		// now we should be able to use macro `generate_receive_message_proof_call_builder`
+		let relayer_call_builder_receive_messages_proof = relayer::ThisChainToBridgedChainMessageLaneReceiveMessagesProofCallBuilder::build_receive_messages_proof_call(
+			account,
+			(Default::default(), receive_messages_proof),
+			messages_count,
+			dispatch_weight,
+			false,
+		);
+
+		// ensure they are all equal
+		assert_eq!(
+			pallet_receive_messages_proof.encode(),
+			mock_enum_receive_messages_proof.encode()
+		);
+		match relayer_call_builder_receive_messages_proof {
+			RuntimeCall::BridgeMessages(call) => match call {
+				call @ CodegenBridgeMessagesCall::receive_messages_proof { .. } =>
+					assert_eq!(pallet_receive_messages_proof.encode(), call.encode()),
+				_ => panic!("Unexpected CodegenBridgeMessagesCall type"),
+			},
+			_ => panic!("Unexpected RuntimeCall type"),
+		};
+	}
+
+	#[test]
+	fn ensure_macro_compatibility_for_generate_receive_message_delivery_proof_call_builder() {
+		// data
+		let receive_messages_delivery_proof = FromBridgedChainMessagesDeliveryProof {
+			bridged_header_hash: Default::default(),
+			storage_proof: Default::default(),
+			lane: LaneId([0, 0, 0, 0]),
+		};
+		let relayers_state = UnrewardedRelayersState {
+			unrewarded_relayer_entries: 0,
+			messages_in_oldest_entry: 0,
+			total_messages: 0,
+			last_delivered_nonce: 0,
+		};
+
+		// construct pallet Call directly
+		let pallet_receive_messages_delivery_proof =
+			pallet_bridge_messages::Call::<mock::TestRuntime>::receive_messages_delivery_proof {
+				proof: receive_messages_delivery_proof.clone(),
+				relayers_state: relayers_state.clone(),
+			};
+
+		// construct mock enum Call
+		let mock_enum_receive_messages_delivery_proof =
+			CodegenBridgeMessagesCall::receive_messages_delivery_proof {
+				proof: receive_messages_delivery_proof.clone(),
+				relayers_state: relayers_state.clone(),
+			};
+
+		// now we should be able to use macro `generate_receive_message_proof_call_builder`
+		let relayer_call_builder_receive_messages_delivery_proof = relayer::ThisChainToBridgedChainMessageLaneReceiveMessagesDeliveryProofCallBuilder::build_receive_messages_delivery_proof_call(
+			(relayers_state, receive_messages_delivery_proof),
+			false,
+		);
+
+		// ensure they are all equal
+		assert_eq!(
+			pallet_receive_messages_delivery_proof.encode(),
+			mock_enum_receive_messages_delivery_proof.encode()
+		);
+		match relayer_call_builder_receive_messages_delivery_proof {
+			RuntimeCall::BridgeMessages(call) => match call {
+				call @ CodegenBridgeMessagesCall::receive_messages_delivery_proof { .. } =>
+					assert_eq!(pallet_receive_messages_delivery_proof.encode(), call.encode()),
+				_ => panic!("Unexpected CodegenBridgeMessagesCall type"),
+			},
+			_ => panic!("Unexpected RuntimeCall type"),
+		};
+	}
+
+	// mock runtime with `pallet_bridge_messages`
+	mod mock {
+		use super::super::*;
+		use bp_messages::target_chain::ForbidInboundMessages;
+		use bp_runtime::ChainId;
+		use frame_support::derive_impl;
+		use sp_core::H256;
+		use sp_runtime::{
+			generic, testing::Header as SubstrateHeader, traits::BlakeTwo256, StateVersion,
+		};
+
+		type Block = frame_system::mocking::MockBlock<TestRuntime>;
+		pub type SignedBlock = generic::SignedBlock<Block>;
+
+		frame_support::construct_runtime! {
+			pub enum TestRuntime
+			{
+				System: frame_system,
+				Messages: pallet_bridge_messages,
+			}
+		}
+
+		#[derive_impl(frame_system::config_preludes::TestDefaultConfig)]
+		impl frame_system::Config for TestRuntime {
+			type Block = Block;
+		}
+
+		impl pallet_bridge_messages::Config for TestRuntime {
+			type RuntimeEvent = RuntimeEvent;
+			type WeightInfo = ();
+			type ThisChain = ThisUnderlyingChain;
+			type BridgedChain = BridgedUnderlyingChain;
+			type BridgedHeaderChain = BridgedHeaderChain;
+			type ActiveOutboundLanes = ();
+			type OutboundPayload = Vec<u8>;
+			type InboundPayload = Vec<u8>;
+			type DeliveryPayments = ();
+			type DeliveryConfirmationPayments = ();
+			type OnMessagesDelivered = ();
+			type MessageDispatch = ForbidInboundMessages<Vec<u8>>;
+		}
+
+		pub struct ThisUnderlyingChain;
+
+		impl bp_runtime::Chain for ThisUnderlyingChain {
+			const ID: ChainId = *b"tuch";
+			type BlockNumber = u64;
+			type Hash = H256;
+			type Hasher = BlakeTwo256;
+			type Header = SubstrateHeader;
+			type AccountId = u64;
+			type Balance = u64;
+			type Nonce = u64;
+			type Signature = sp_runtime::MultiSignature;
+			const STATE_VERSION: StateVersion = StateVersion::V1;
+			fn max_extrinsic_size() -> u32 {
+				u32::MAX
+			}
+			fn max_extrinsic_weight() -> Weight {
+				Weight::MAX
+			}
+		}
+
+		impl bp_messages::ChainWithMessages for ThisUnderlyingChain {
+			const WITH_CHAIN_MESSAGES_PALLET_NAME: &'static str = "";
+			const MAX_UNREWARDED_RELAYERS_IN_CONFIRMATION_TX: MessageNonce = 16;
+			const MAX_UNCONFIRMED_MESSAGES_IN_CONFIRMATION_TX: MessageNonce = 1000;
+		}
+
+		pub struct BridgedUnderlyingChain;
+
+		pub type BridgedHeaderHash = H256;
+		pub type BridgedChainHeader = SubstrateHeader;
+
+		impl bp_runtime::Chain for BridgedUnderlyingChain {
+			const ID: ChainId = *b"bgdc";
+			type BlockNumber = u64;
+			type Hash = BridgedHeaderHash;
+			type Hasher = BlakeTwo256;
+			type Header = BridgedChainHeader;
+			type AccountId = u64;
+			type Balance = u64;
+			type Nonce = u64;
+			type Signature = sp_runtime::MultiSignature;
+			const STATE_VERSION: StateVersion = StateVersion::V1;
+			fn max_extrinsic_size() -> u32 {
+				4096
+			}
+			fn max_extrinsic_weight() -> Weight {
+				Weight::MAX
+			}
+		}
+
+		impl bp_messages::ChainWithMessages for BridgedUnderlyingChain {
+			const WITH_CHAIN_MESSAGES_PALLET_NAME: &'static str = "";
+			const MAX_UNREWARDED_RELAYERS_IN_CONFIRMATION_TX: MessageNonce = 16;
+			const MAX_UNCONFIRMED_MESSAGES_IN_CONFIRMATION_TX: MessageNonce = 1000;
+		}
+
+		pub struct BridgedHeaderChain;
+
+		impl bp_header_chain::HeaderChain<BridgedUnderlyingChain> for BridgedHeaderChain {
+			fn finalized_header_state_root(
+				_hash: HashOf<BridgedUnderlyingChain>,
+			) -> Option<HashOf<BridgedUnderlyingChain>> {
+				unreachable!()
+			}
+		}
+	}
+
+	// relayer configuration
+	mod relayer {
+		use super::*;
+		use crate::{
+			messages::{
+				tests::{mock, RuntimeCall},
+				SubstrateMessageLane,
+			},
+			UtilityPalletBatchCallBuilder,
+		};
+		use bp_runtime::UnderlyingChainProvider;
+		use relay_substrate_client::{MockedRuntimeUtilityPallet, SignParam, UnsignedTransaction};
+		use std::time::Duration;
+
+		#[derive(Clone)]
+		pub struct ThisChain;
+		impl UnderlyingChainProvider for ThisChain {
+			type Chain = mock::ThisUnderlyingChain;
+		}
+		impl relay_substrate_client::Chain for ThisChain {
+			const NAME: &'static str = "";
+			const BEST_FINALIZED_HEADER_ID_METHOD: &'static str = "";
+			const FREE_HEADERS_INTERVAL_METHOD: &'static str = "";
+			const AVERAGE_BLOCK_INTERVAL: Duration = Duration::from_millis(0);
+			type SignedBlock = mock::SignedBlock;
+			type Call = RuntimeCall;
+		}
+		impl relay_substrate_client::ChainWithTransactions for ThisChain {
+			type AccountKeyPair = sp_core::sr25519::Pair;
+			type SignedTransaction = ();
+
+			fn sign_transaction(
+				_: SignParam<Self>,
+				_: UnsignedTransaction<Self>,
+			) -> Result<Self::SignedTransaction, SubstrateError>
+			where
+				Self: Sized,
+			{
+				todo!()
+			}
+		}
+		impl relay_substrate_client::ChainWithMessages for ThisChain {
+			const WITH_CHAIN_RELAYERS_PALLET_NAME: Option<&'static str> = None;
+			const TO_CHAIN_MESSAGE_DETAILS_METHOD: &'static str = "";
+			const FROM_CHAIN_MESSAGE_DETAILS_METHOD: &'static str = "";
+		}
+		impl relay_substrate_client::ChainWithUtilityPallet for ThisChain {
+			type UtilityPallet = MockedRuntimeUtilityPallet<RuntimeCall>;
+		}
+
+		#[derive(Clone)]
+		pub struct BridgedChain;
+		impl UnderlyingChainProvider for BridgedChain {
+			type Chain = mock::BridgedUnderlyingChain;
+		}
+		impl relay_substrate_client::Chain for BridgedChain {
+			const NAME: &'static str = "";
+			const BEST_FINALIZED_HEADER_ID_METHOD: &'static str = "";
+			const FREE_HEADERS_INTERVAL_METHOD: &'static str = "";
+			const AVERAGE_BLOCK_INTERVAL: Duration = Duration::from_millis(0);
+			type SignedBlock = mock::SignedBlock;
+			type Call = RuntimeCall;
+		}
+		impl relay_substrate_client::ChainWithTransactions for BridgedChain {
+			type AccountKeyPair = sp_core::sr25519::Pair;
+			type SignedTransaction = ();
+
+			fn sign_transaction(
+				_: SignParam<Self>,
+				_: UnsignedTransaction<Self>,
+			) -> Result<Self::SignedTransaction, SubstrateError>
+			where
+				Self: Sized,
+			{
+				todo!()
+			}
+		}
+		impl relay_substrate_client::ChainWithMessages for BridgedChain {
+			const WITH_CHAIN_RELAYERS_PALLET_NAME: Option<&'static str> = None;
+			const TO_CHAIN_MESSAGE_DETAILS_METHOD: &'static str = "";
+			const FROM_CHAIN_MESSAGE_DETAILS_METHOD: &'static str = "";
+		}
+		impl relay_substrate_client::ChainWithUtilityPallet for BridgedChain {
+			type UtilityPallet = MockedRuntimeUtilityPallet<RuntimeCall>;
+		}
+
+		#[derive(Clone, Debug)]
+		pub struct ThisChainToBridgedChainMessageLane;
+		impl SubstrateMessageLane for ThisChainToBridgedChainMessageLane {
+			type SourceChain = ThisChain;
+			type TargetChain = BridgedChain;
+			type ReceiveMessagesProofCallBuilder =
+				ThisChainToBridgedChainMessageLaneReceiveMessagesProofCallBuilder;
+			type ReceiveMessagesDeliveryProofCallBuilder =
+				ThisChainToBridgedChainMessageLaneReceiveMessagesDeliveryProofCallBuilder;
+			type SourceBatchCallBuilder = UtilityPalletBatchCallBuilder<ThisChain>;
+			type TargetBatchCallBuilder = UtilityPalletBatchCallBuilder<BridgedChain>;
+		}
+
+		generate_receive_message_proof_call_builder!(
+			ThisChainToBridgedChainMessageLane,
+			ThisChainToBridgedChainMessageLaneReceiveMessagesProofCallBuilder,
+			RuntimeCall::BridgeMessages,
+			CodegenBridgeMessagesCall::receive_messages_proof
+		);
+		generate_receive_message_delivery_proof_call_builder!(
+			ThisChainToBridgedChainMessageLane,
+			ThisChainToBridgedChainMessageLaneReceiveMessagesDeliveryProofCallBuilder,
+			RuntimeCall::BridgeMessages,
+			CodegenBridgeMessagesCall::receive_messages_delivery_proof
+		);
+	}
+}
diff --git a/bridges/relays/lib-substrate-relay/src/messages_source.rs b/bridges/relays/lib-substrate-relay/src/messages/source.rs
similarity index 97%
rename from bridges/relays/lib-substrate-relay/src/messages_source.rs
rename to bridges/relays/lib-substrate-relay/src/messages/source.rs
index 1f597e278da..b75fc86d5ee 100644
--- a/bridges/relays/lib-substrate-relay/src/messages_source.rs
+++ b/bridges/relays/lib-substrate-relay/src/messages/source.rs
@@ -20,11 +20,12 @@
 
 use crate::{
 	finality_base::best_synced_header_id,
-	messages_lane::{
+	messages::{
 		BatchProofTransaction, MessageLaneAdapter, ReceiveMessagesDeliveryProofCallBuilder,
 		SubstrateMessageLane,
 	},
 	on_demand::OnDemandRelay,
+	proofs::to_raw_storage_proof,
 	TransactionParams,
 };
 
@@ -32,11 +33,11 @@ use async_std::sync::Arc;
 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,
 	MessagesOperatingMode, OutboundLaneData, OutboundMessageDetails,
 };
-use bp_runtime::{BasicOperatingMode, HeaderIdProvider};
-use bridge_runtime_common::messages::target::FromBridgedChainMessagesProof;
+use bp_runtime::{BasicOperatingMode, HeaderIdProvider, RangeInclusiveExt};
 use codec::Encode;
 use frame_support::weights::Weight;
 use messages_relay::{
@@ -320,34 +321,27 @@ where
 		),
 		SubstrateError,
 	> {
-		let mut storage_keys =
-			Vec::with_capacity(nonces.end().saturating_sub(*nonces.start()) as usize + 1);
-		let mut message_nonce = *nonces.start();
-		while message_nonce <= *nonces.end() {
+		let mut storage_keys = Vec::with_capacity(nonces.saturating_len() as usize);
+		for message_nonce in nonces.clone() {
 			let message_key = bp_messages::storage_keys::message_key(
 				P::TargetChain::WITH_CHAIN_MESSAGES_PALLET_NAME,
 				&self.lane_id,
 				message_nonce,
 			);
 			storage_keys.push(message_key);
-			message_nonce += 1;
 		}
 		if proof_parameters.outbound_state_proof_required {
-			storage_keys.push(bp_messages::storage_keys::outbound_lane_data_key(
+			storage_keys.push(outbound_lane_data_key(
 				P::TargetChain::WITH_CHAIN_MESSAGES_PALLET_NAME,
 				&self.lane_id,
 			));
 		}
 
-		let proof = self
-			.source_client
-			.prove_storage(id.1, storage_keys)
-			.await?
-			.into_iter_nodes()
-			.collect();
+		let storage_proof =
+			self.source_client.prove_storage(id.hash(), storage_keys.clone()).await?;
 		let proof = FromBridgedChainMessagesProof {
 			bridged_header_hash: id.1,
-			storage_proof: proof,
+			storage_proof: to_raw_storage_proof::<P::SourceChain>(storage_proof),
 			lane: self.lane_id,
 			nonces_start: *nonces.start(),
 			nonces_end: *nonces.end(),
diff --git a/bridges/relays/lib-substrate-relay/src/messages_target.rs b/bridges/relays/lib-substrate-relay/src/messages/target.rs
similarity index 94%
rename from bridges/relays/lib-substrate-relay/src/messages_target.rs
rename to bridges/relays/lib-substrate-relay/src/messages/target.rs
index e1c7645eac6..a6bf169cffb 100644
--- a/bridges/relays/lib-substrate-relay/src/messages_target.rs
+++ b/bridges/relays/lib-substrate-relay/src/messages/target.rs
@@ -19,24 +19,25 @@
 //! `<BridgedName>` chain.
 
 use crate::{
-	messages_lane::{
+	messages::{
+		source::{
+			ensure_messages_pallet_active, read_client_state_from_both_chains,
+			SubstrateMessagesProof,
+		},
 		BatchProofTransaction, MessageLaneAdapter, ReceiveMessagesProofCallBuilder,
 		SubstrateMessageLane,
 	},
-	messages_source::{
-		ensure_messages_pallet_active, read_client_state_from_both_chains, SubstrateMessagesProof,
-	},
 	on_demand::OnDemandRelay,
+	proofs::to_raw_storage_proof,
 	TransactionParams,
 };
 
 use async_std::sync::Arc;
 use async_trait::async_trait;
 use bp_messages::{
-	storage_keys::inbound_lane_data_key, ChainWithMessages as _, InboundLaneData, LaneId,
-	MessageNonce, UnrewardedRelayersState,
+	source_chain::FromBridgedChainMessagesDeliveryProof, storage_keys::inbound_lane_data_key,
+	ChainWithMessages as _, InboundLaneData, LaneId, MessageNonce, UnrewardedRelayersState,
 };
-use bridge_runtime_common::messages::source::FromBridgedChainMessagesDeliveryProof;
 use messages_relay::{
 	message_lane::{MessageLane, SourceHeaderIdOf, TargetHeaderIdOf},
 	message_lane_loop::{NoncesSubmitArtifacts, TargetClient, TargetClientState},
@@ -47,7 +48,7 @@ use relay_substrate_client::{
 };
 use relay_utils::relay_loop::Client as RelayClient;
 use sp_core::Pair;
-use std::ops::RangeInclusive;
+use std::{convert::TryFrom, ops::RangeInclusive};
 
 /// Message receiving proof returned by the target Substrate node.
 pub type SubstrateMessagesDeliveryProof<C> =
@@ -231,19 +232,16 @@ where
 		SubstrateError,
 	> {
 		let (id, relayers_state) = self.unrewarded_relayers_state(id).await?;
-		let inbound_data_key = bp_messages::storage_keys::inbound_lane_data_key(
+		let storage_keys = vec![inbound_lane_data_key(
 			P::SourceChain::WITH_CHAIN_MESSAGES_PALLET_NAME,
 			&self.lane_id,
-		);
-		let proof = self
-			.target_client
-			.prove_storage(id.hash(), vec![inbound_data_key])
-			.await?
-			.into_iter_nodes()
-			.collect();
+		)];
+
+		let storage_proof =
+			self.target_client.prove_storage(id.hash(), storage_keys.clone()).await?;
 		let proof = FromBridgedChainMessagesDeliveryProof {
 			bridged_header_hash: id.1,
-			storage_proof: proof,
+			storage_proof: to_raw_storage_proof::<P::TargetChain>(storage_proof),
 			lane: self.lane_id,
 		};
 		Ok((id, (relayers_state, proof)))
diff --git a/bridges/relays/lib-substrate-relay/src/on_demand/parachains.rs b/bridges/relays/lib-substrate-relay/src/on_demand/parachains.rs
index 654cb6628d5..4579222a2c6 100644
--- a/bridges/relays/lib-substrate-relay/src/on_demand/parachains.rs
+++ b/bridges/relays/lib-substrate-relay/src/on_demand/parachains.rs
@@ -17,7 +17,7 @@
 //! On-demand Substrate -> Substrate parachain finality relay.
 
 use crate::{
-	messages_source::best_finalized_peer_header_at_self,
+	messages::source::best_finalized_peer_header_at_self,
 	on_demand::OnDemandRelay,
 	parachains::{
 		source::ParachainsSource, target::ParachainsTarget, ParachainsPipelineAdapter,
@@ -681,7 +681,7 @@ impl<'a, P: SubstrateParachainsPipeline, SourceRelayClnt, TargetClnt>
 	async fn best_finalized_relay_block_at_target(
 		&self,
 	) -> Result<HeaderIdOf<P::SourceRelayChain>, SubstrateError> {
-		Ok(crate::messages_source::read_client_state::<P::TargetChain, P::SourceRelayChain>(
+		Ok(crate::messages::source::read_client_state::<P::TargetChain, P::SourceRelayChain>(
 			&self.0.target_client,
 		)
 		.await?
diff --git a/bridges/relays/lib-substrate-relay/src/parachains/source.rs b/bridges/relays/lib-substrate-relay/src/parachains/source.rs
index 11b9d6dbf5b..1aa12d1c913 100644
--- a/bridges/relays/lib-substrate-relay/src/parachains/source.rs
+++ b/bridges/relays/lib-substrate-relay/src/parachains/source.rs
@@ -16,8 +16,10 @@
 
 //! Parachain heads source.
 
-use crate::parachains::{ParachainsPipelineAdapter, SubstrateParachainsPipeline};
-
+use crate::{
+	parachains::{ParachainsPipelineAdapter, SubstrateParachainsPipeline},
+	proofs::to_raw_storage_proof,
+};
 use async_std::sync::{Arc, Mutex};
 use async_trait::async_trait;
 use bp_parachains::parachain_head_storage_key_at_source;
@@ -153,12 +155,9 @@ where
 		let parachain = ParaId(P::SourceParachain::PARACHAIN_ID);
 		let storage_key =
 			parachain_head_storage_key_at_source(P::SourceRelayChain::PARAS_PALLET_NAME, parachain);
-		let parachain_heads_proof = self
-			.client
-			.prove_storage(at_block.hash(), vec![storage_key.clone()])
-			.await?
-			.into_iter_nodes()
-			.collect();
+
+		let storage_proof =
+			self.client.prove_storage(at_block.hash(), vec![storage_key.clone()]).await?;
 
 		// why we're reading parachain head here once again (it has already been read at the
 		// `parachain_head`)? that's because `parachain_head` sometimes returns obsolete parachain
@@ -178,6 +177,11 @@ where
 			})?;
 		let parachain_head_hash = parachain_head.hash();
 
-		Ok((ParaHeadsProof { storage_proof: parachain_heads_proof }, parachain_head_hash))
+		Ok((
+			ParaHeadsProof {
+				storage_proof: to_raw_storage_proof::<P::SourceRelayChain>(storage_proof),
+			},
+			parachain_head_hash,
+		))
 	}
 }
diff --git a/bridges/relays/messages/Cargo.toml b/bridges/relays/messages/Cargo.toml
index 96e441fc673..c7a132bb3ba 100644
--- a/bridges/relays/messages/Cargo.toml
+++ b/bridges/relays/messages/Cargo.toml
@@ -13,7 +13,6 @@ workspace = true
 [dependencies]
 async-std = { features = ["attributes"], workspace = true }
 async-trait = { workspace = true }
-env_logger = { workspace = true }
 futures = { workspace = true }
 hex = { workspace = true, default-features = true }
 log = { workspace = true }
diff --git a/bridges/relays/parachains/src/parachains_loop.rs b/bridges/relays/parachains/src/parachains_loop.rs
index fd73ca2d46c..0fd1d72c707 100644
--- a/bridges/relays/parachains/src/parachains_loop.rs
+++ b/bridges/relays/parachains/src/parachains_loop.rs
@@ -680,7 +680,6 @@ impl<P: ParachainsPipeline> SubmittedHeadsTracker<P> {
 mod tests {
 	use super::*;
 	use async_std::sync::{Arc, Mutex};
-	use codec::Encode;
 	use futures::{SinkExt, StreamExt};
 	use relay_substrate_client::test_chain::{TestChain, TestParachain};
 	use relay_utils::{HeaderId, MaybeConnectionError};
@@ -821,8 +820,7 @@ mod tests {
 			let head_result =
 				SourceClient::<TestParachainsPipeline>::parachain_head(self, at_block).await?;
 			let head = head_result.as_available().unwrap();
-			let storage_proof = vec![head.hash().encode()];
-			let proof = (ParaHeadsProof { storage_proof }, head.hash());
+			let proof = (ParaHeadsProof { storage_proof: Default::default() }, head.hash());
 			self.data.lock().await.source_proof.clone().map(|_| proof)
 		}
 	}
diff --git a/cumulus/parachains/runtimes/assets/asset-hub-rococo/src/weights/pallet_xcm_bridge_hub_router.rs b/cumulus/parachains/runtimes/assets/asset-hub-rococo/src/weights/pallet_xcm_bridge_hub_router.rs
index 775bc3bdb80..0a86037391b 100644
--- a/cumulus/parachains/runtimes/assets/asset-hub-rococo/src/weights/pallet_xcm_bridge_hub_router.rs
+++ b/cumulus/parachains/runtimes/assets/asset-hub-rococo/src/weights/pallet_xcm_bridge_hub_router.rs
@@ -16,10 +16,10 @@
 
 //! Autogenerated weights for `pallet_xcm_bridge_hub_router`
 //!
-//! THIS FILE WAS AUTO-GENERATED USING THE SUBSTRATE BENCHMARK CLI VERSION 4.0.0-dev
-//! DATE: 2023-12-12, STEPS: `50`, REPEAT: `20`, LOW RANGE: `[]`, HIGH RANGE: `[]`
+//! THIS FILE WAS AUTO-GENERATED USING THE SUBSTRATE BENCHMARK CLI VERSION 32.0.0
+//! DATE: 2024-07-03, STEPS: `50`, REPEAT: `20`, LOW RANGE: `[]`, HIGH RANGE: `[]`
 //! WORST CASE MAP SIZE: `1000000`
-//! HOSTNAME: `runner-itmxxexx-project-674-concurrent-0`, CPU: `Intel(R) Xeon(R) CPU @ 2.60GHz`
+//! HOSTNAME: `runner-7wrmsoux-project-674-concurrent-0`, CPU: `Intel(R) Xeon(R) CPU @ 2.60GHz`
 //! WASM-EXECUTION: `Compiled`, CHAIN: `Some("asset-hub-rococo-dev")`, DB CACHE: 1024
 
 // Executed Command:
@@ -49,32 +49,32 @@ use core::marker::PhantomData;
 pub struct WeightInfo<T>(PhantomData<T>);
 impl<T: frame_system::Config> pallet_xcm_bridge_hub_router::WeightInfo for WeightInfo<T> {
 	/// Storage: `XcmpQueue::InboundXcmpSuspended` (r:1 w:0)
-	/// Proof: `XcmpQueue::InboundXcmpSuspended` (`max_values`: Some(1), `max_size`: None, mode: `Measured`)
+	/// Proof: `XcmpQueue::InboundXcmpSuspended` (`max_values`: Some(1), `max_size`: Some(4002), added: 4497, mode: `MaxEncodedLen`)
 	/// Storage: `XcmpQueue::OutboundXcmpStatus` (r:1 w:0)
-	/// Proof: `XcmpQueue::OutboundXcmpStatus` (`max_values`: Some(1), `max_size`: None, mode: `Measured`)
+	/// Proof: `XcmpQueue::OutboundXcmpStatus` (`max_values`: Some(1), `max_size`: Some(1282), added: 1777, mode: `MaxEncodedLen`)
 	/// Storage: `ToWestendXcmRouter::Bridge` (r:1 w:1)
 	/// Proof: `ToWestendXcmRouter::Bridge` (`max_values`: Some(1), `max_size`: Some(17), added: 512, mode: `MaxEncodedLen`)
 	fn on_initialize_when_non_congested() -> Weight {
 		// Proof Size summary in bytes:
 		//  Measured:  `154`
-		//  Estimated: `1639`
-		// Minimum execution time: 7_853_000 picoseconds.
-		Weight::from_parts(8_443_000, 0)
-			.saturating_add(Weight::from_parts(0, 1639))
+		//  Estimated: `5487`
+		// Minimum execution time: 8_078_000 picoseconds.
+		Weight::from_parts(8_455_000, 0)
+			.saturating_add(Weight::from_parts(0, 5487))
 			.saturating_add(T::DbWeight::get().reads(3))
 			.saturating_add(T::DbWeight::get().writes(1))
 	}
 	/// Storage: `XcmpQueue::InboundXcmpSuspended` (r:1 w:0)
-	/// Proof: `XcmpQueue::InboundXcmpSuspended` (`max_values`: Some(1), `max_size`: None, mode: `Measured`)
+	/// Proof: `XcmpQueue::InboundXcmpSuspended` (`max_values`: Some(1), `max_size`: Some(4002), added: 4497, mode: `MaxEncodedLen`)
 	/// Storage: `XcmpQueue::OutboundXcmpStatus` (r:1 w:0)
-	/// Proof: `XcmpQueue::OutboundXcmpStatus` (`max_values`: Some(1), `max_size`: None, mode: `Measured`)
+	/// Proof: `XcmpQueue::OutboundXcmpStatus` (`max_values`: Some(1), `max_size`: Some(1282), added: 1777, mode: `MaxEncodedLen`)
 	fn on_initialize_when_congested() -> Weight {
 		// Proof Size summary in bytes:
 		//  Measured:  `144`
-		//  Estimated: `1629`
-		// Minimum execution time: 4_333_000 picoseconds.
-		Weight::from_parts(4_501_000, 0)
-			.saturating_add(Weight::from_parts(0, 1629))
+		//  Estimated: `5487`
+		// Minimum execution time: 4_291_000 picoseconds.
+		Weight::from_parts(4_548_000, 0)
+			.saturating_add(Weight::from_parts(0, 5487))
 			.saturating_add(T::DbWeight::get().reads(2))
 	}
 	/// Storage: `ToWestendXcmRouter::Bridge` (r:1 w:1)
@@ -83,14 +83,12 @@ impl<T: frame_system::Config> pallet_xcm_bridge_hub_router::WeightInfo for Weigh
 		// Proof Size summary in bytes:
 		//  Measured:  `150`
 		//  Estimated: `1502`
-		// Minimum execution time: 10_167_000 picoseconds.
-		Weight::from_parts(10_667_000, 0)
+		// Minimum execution time: 9_959_000 picoseconds.
+		Weight::from_parts(10_372_000, 0)
 			.saturating_add(Weight::from_parts(0, 1502))
 			.saturating_add(T::DbWeight::get().reads(1))
 			.saturating_add(T::DbWeight::get().writes(1))
 	}
-	/// Storage: `PolkadotXcm::SupportedVersion` (r:2 w:0)
-	/// Proof: `PolkadotXcm::SupportedVersion` (`max_values`: None, `max_size`: None, mode: `Measured`)
 	/// Storage: `ParachainInfo::ParachainId` (r:1 w:0)
 	/// Proof: `ParachainInfo::ParachainId` (`max_values`: Some(1), `max_size`: Some(4), added: 499, mode: `MaxEncodedLen`)
 	/// Storage: UNKNOWN KEY `0x3302afcb67e838a3f960251b417b9a4f` (r:1 w:0)
@@ -100,7 +98,9 @@ impl<T: frame_system::Config> pallet_xcm_bridge_hub_router::WeightInfo for Weigh
 	/// Storage: `ToWestendXcmRouter::Bridge` (r:1 w:1)
 	/// Proof: `ToWestendXcmRouter::Bridge` (`max_values`: Some(1), `max_size`: Some(17), added: 512, mode: `MaxEncodedLen`)
 	/// Storage: `XcmpQueue::DeliveryFeeFactor` (r:1 w:0)
-	/// Proof: `XcmpQueue::DeliveryFeeFactor` (`max_values`: None, `max_size`: None, mode: `Measured`)
+	/// Proof: `XcmpQueue::DeliveryFeeFactor` (`max_values`: None, `max_size`: Some(28), added: 2503, mode: `MaxEncodedLen`)
+	/// Storage: `PolkadotXcm::SupportedVersion` (r:2 w:0)
+	/// Proof: `PolkadotXcm::SupportedVersion` (`max_values`: None, `max_size`: None, mode: `Measured`)
 	/// Storage: `PolkadotXcm::VersionDiscoveryQueue` (r:1 w:1)
 	/// Proof: `PolkadotXcm::VersionDiscoveryQueue` (`max_values`: Some(1), `max_size`: None, mode: `Measured`)
 	/// Storage: `PolkadotXcm::SafeXcmVersion` (r:1 w:0)
@@ -108,17 +108,17 @@ impl<T: frame_system::Config> pallet_xcm_bridge_hub_router::WeightInfo for Weigh
 	/// Storage: `ParachainSystem::RelevantMessagingState` (r:1 w:0)
 	/// Proof: `ParachainSystem::RelevantMessagingState` (`max_values`: Some(1), `max_size`: None, mode: `Measured`)
 	/// Storage: `XcmpQueue::OutboundXcmpStatus` (r:1 w:1)
-	/// Proof: `XcmpQueue::OutboundXcmpStatus` (`max_values`: Some(1), `max_size`: None, mode: `Measured`)
+	/// Proof: `XcmpQueue::OutboundXcmpStatus` (`max_values`: Some(1), `max_size`: Some(1282), added: 1777, mode: `MaxEncodedLen`)
 	/// Storage: `XcmpQueue::InboundXcmpSuspended` (r:1 w:0)
-	/// Proof: `XcmpQueue::InboundXcmpSuspended` (`max_values`: Some(1), `max_size`: None, mode: `Measured`)
+	/// Proof: `XcmpQueue::InboundXcmpSuspended` (`max_values`: Some(1), `max_size`: Some(4002), added: 4497, mode: `MaxEncodedLen`)
 	/// Storage: `XcmpQueue::OutboundXcmpMessages` (r:0 w:1)
-	/// Proof: `XcmpQueue::OutboundXcmpMessages` (`max_values`: None, `max_size`: None, mode: `Measured`)
+	/// Proof: `XcmpQueue::OutboundXcmpMessages` (`max_values`: None, `max_size`: Some(105506), added: 107981, mode: `MaxEncodedLen`)
 	fn send_message() -> Weight {
 		// Proof Size summary in bytes:
 		//  Measured:  `448`
 		//  Estimated: `6388`
-		// Minimum execution time: 60_584_000 picoseconds.
-		Weight::from_parts(62_467_000, 0)
+		// Minimum execution time: 45_888_000 picoseconds.
+		Weight::from_parts(47_022_000, 0)
 			.saturating_add(Weight::from_parts(0, 6388))
 			.saturating_add(T::DbWeight::get().reads(12))
 			.saturating_add(T::DbWeight::get().writes(4))
diff --git a/cumulus/parachains/runtimes/assets/asset-hub-westend/src/weights/pallet_xcm_bridge_hub_router.rs b/cumulus/parachains/runtimes/assets/asset-hub-westend/src/weights/pallet_xcm_bridge_hub_router.rs
index 84d717b0283..21d15c75af5 100644
--- a/cumulus/parachains/runtimes/assets/asset-hub-westend/src/weights/pallet_xcm_bridge_hub_router.rs
+++ b/cumulus/parachains/runtimes/assets/asset-hub-westend/src/weights/pallet_xcm_bridge_hub_router.rs
@@ -16,10 +16,10 @@
 
 //! Autogenerated weights for `pallet_xcm_bridge_hub_router`
 //!
-//! THIS FILE WAS AUTO-GENERATED USING THE SUBSTRATE BENCHMARK CLI VERSION 4.0.0-dev
-//! DATE: 2023-12-12, STEPS: `50`, REPEAT: `20`, LOW RANGE: `[]`, HIGH RANGE: `[]`
+//! THIS FILE WAS AUTO-GENERATED USING THE SUBSTRATE BENCHMARK CLI VERSION 32.0.0
+//! DATE: 2024-07-03, STEPS: `50`, REPEAT: `20`, LOW RANGE: `[]`, HIGH RANGE: `[]`
 //! WORST CASE MAP SIZE: `1000000`
-//! HOSTNAME: `runner-itmxxexx-project-674-concurrent-0`, CPU: `Intel(R) Xeon(R) CPU @ 2.60GHz`
+//! HOSTNAME: `runner-7wrmsoux-project-674-concurrent-0`, CPU: `Intel(R) Xeon(R) CPU @ 2.60GHz`
 //! WASM-EXECUTION: `Compiled`, CHAIN: `Some("asset-hub-westend-dev")`, DB CACHE: 1024
 
 // Executed Command:
@@ -49,48 +49,46 @@ use core::marker::PhantomData;
 pub struct WeightInfo<T>(PhantomData<T>);
 impl<T: frame_system::Config> pallet_xcm_bridge_hub_router::WeightInfo for WeightInfo<T> {
 	/// Storage: `XcmpQueue::InboundXcmpSuspended` (r:1 w:0)
-	/// Proof: `XcmpQueue::InboundXcmpSuspended` (`max_values`: Some(1), `max_size`: None, mode: `Measured`)
+	/// Proof: `XcmpQueue::InboundXcmpSuspended` (`max_values`: Some(1), `max_size`: Some(4002), added: 4497, mode: `MaxEncodedLen`)
 	/// Storage: `XcmpQueue::OutboundXcmpStatus` (r:1 w:0)
-	/// Proof: `XcmpQueue::OutboundXcmpStatus` (`max_values`: Some(1), `max_size`: None, mode: `Measured`)
+	/// Proof: `XcmpQueue::OutboundXcmpStatus` (`max_values`: Some(1), `max_size`: Some(1282), added: 1777, mode: `MaxEncodedLen`)
 	/// Storage: `ToRococoXcmRouter::Bridge` (r:1 w:1)
 	/// Proof: `ToRococoXcmRouter::Bridge` (`max_values`: Some(1), `max_size`: Some(17), added: 512, mode: `MaxEncodedLen`)
 	fn on_initialize_when_non_congested() -> Weight {
 		// Proof Size summary in bytes:
-		//  Measured:  `193`
-		//  Estimated: `1678`
-		// Minimum execution time: 8_095_000 picoseconds.
-		Weight::from_parts(8_393_000, 0)
-			.saturating_add(Weight::from_parts(0, 1678))
+		//  Measured:  `226`
+		//  Estimated: `5487`
+		// Minimum execution time: 8_363_000 picoseconds.
+		Weight::from_parts(8_620_000, 0)
+			.saturating_add(Weight::from_parts(0, 5487))
 			.saturating_add(T::DbWeight::get().reads(3))
 			.saturating_add(T::DbWeight::get().writes(1))
 	}
 	/// Storage: `XcmpQueue::InboundXcmpSuspended` (r:1 w:0)
-	/// Proof: `XcmpQueue::InboundXcmpSuspended` (`max_values`: Some(1), `max_size`: None, mode: `Measured`)
+	/// Proof: `XcmpQueue::InboundXcmpSuspended` (`max_values`: Some(1), `max_size`: Some(4002), added: 4497, mode: `MaxEncodedLen`)
 	/// Storage: `XcmpQueue::OutboundXcmpStatus` (r:1 w:0)
-	/// Proof: `XcmpQueue::OutboundXcmpStatus` (`max_values`: Some(1), `max_size`: None, mode: `Measured`)
+	/// Proof: `XcmpQueue::OutboundXcmpStatus` (`max_values`: Some(1), `max_size`: Some(1282), added: 1777, mode: `MaxEncodedLen`)
 	fn on_initialize_when_congested() -> Weight {
 		// Proof Size summary in bytes:
 		//  Measured:  `111`
-		//  Estimated: `1596`
-		// Minimum execution time: 3_417_000 picoseconds.
-		Weight::from_parts(3_583_000, 0)
-			.saturating_add(Weight::from_parts(0, 1596))
+		//  Estimated: `5487`
+		// Minimum execution time: 3_436_000 picoseconds.
+		Weight::from_parts(3_586_000, 0)
+			.saturating_add(Weight::from_parts(0, 5487))
 			.saturating_add(T::DbWeight::get().reads(2))
 	}
 	/// Storage: `ToRococoXcmRouter::Bridge` (r:1 w:1)
 	/// Proof: `ToRococoXcmRouter::Bridge` (`max_values`: Some(1), `max_size`: Some(17), added: 512, mode: `MaxEncodedLen`)
 	fn report_bridge_status() -> Weight {
 		// Proof Size summary in bytes:
-		//  Measured:  `117`
+		//  Measured:  `150`
 		//  Estimated: `1502`
-		// Minimum execution time: 10_280_000 picoseconds.
-		Weight::from_parts(10_703_000, 0)
+		// Minimum execution time: 9_706_000 picoseconds.
+		Weight::from_parts(10_139_000, 0)
 			.saturating_add(Weight::from_parts(0, 1502))
 			.saturating_add(T::DbWeight::get().reads(1))
 			.saturating_add(T::DbWeight::get().writes(1))
 	}
-	/// Storage: `PolkadotXcm::SupportedVersion` (r:2 w:0)
-	/// Proof: `PolkadotXcm::SupportedVersion` (`max_values`: None, `max_size`: None, mode: `Measured`)
 	/// Storage: `ParachainInfo::ParachainId` (r:1 w:0)
 	/// Proof: `ParachainInfo::ParachainId` (`max_values`: Some(1), `max_size`: Some(4), added: 499, mode: `MaxEncodedLen`)
 	/// Storage: UNKNOWN KEY `0x3302afcb67e838a3f960251b417b9a4f` (r:1 w:0)
@@ -100,7 +98,9 @@ impl<T: frame_system::Config> pallet_xcm_bridge_hub_router::WeightInfo for Weigh
 	/// Storage: `ToRococoXcmRouter::Bridge` (r:1 w:1)
 	/// Proof: `ToRococoXcmRouter::Bridge` (`max_values`: Some(1), `max_size`: Some(17), added: 512, mode: `MaxEncodedLen`)
 	/// Storage: `XcmpQueue::DeliveryFeeFactor` (r:1 w:0)
-	/// Proof: `XcmpQueue::DeliveryFeeFactor` (`max_values`: None, `max_size`: None, mode: `Measured`)
+	/// Proof: `XcmpQueue::DeliveryFeeFactor` (`max_values`: None, `max_size`: Some(28), added: 2503, mode: `MaxEncodedLen`)
+	/// Storage: `PolkadotXcm::SupportedVersion` (r:2 w:0)
+	/// Proof: `PolkadotXcm::SupportedVersion` (`max_values`: None, `max_size`: None, mode: `Measured`)
 	/// Storage: `PolkadotXcm::VersionDiscoveryQueue` (r:1 w:1)
 	/// Proof: `PolkadotXcm::VersionDiscoveryQueue` (`max_values`: Some(1), `max_size`: None, mode: `Measured`)
 	/// Storage: `PolkadotXcm::SafeXcmVersion` (r:1 w:0)
@@ -108,18 +108,18 @@ impl<T: frame_system::Config> pallet_xcm_bridge_hub_router::WeightInfo for Weigh
 	/// Storage: `ParachainSystem::RelevantMessagingState` (r:1 w:0)
 	/// Proof: `ParachainSystem::RelevantMessagingState` (`max_values`: Some(1), `max_size`: None, mode: `Measured`)
 	/// Storage: `XcmpQueue::OutboundXcmpStatus` (r:1 w:1)
-	/// Proof: `XcmpQueue::OutboundXcmpStatus` (`max_values`: Some(1), `max_size`: None, mode: `Measured`)
+	/// Proof: `XcmpQueue::OutboundXcmpStatus` (`max_values`: Some(1), `max_size`: Some(1282), added: 1777, mode: `MaxEncodedLen`)
 	/// Storage: `XcmpQueue::InboundXcmpSuspended` (r:1 w:0)
-	/// Proof: `XcmpQueue::InboundXcmpSuspended` (`max_values`: Some(1), `max_size`: None, mode: `Measured`)
+	/// Proof: `XcmpQueue::InboundXcmpSuspended` (`max_values`: Some(1), `max_size`: Some(4002), added: 4497, mode: `MaxEncodedLen`)
 	/// Storage: `XcmpQueue::OutboundXcmpMessages` (r:0 w:1)
-	/// Proof: `XcmpQueue::OutboundXcmpMessages` (`max_values`: None, `max_size`: None, mode: `Measured`)
+	/// Proof: `XcmpQueue::OutboundXcmpMessages` (`max_values`: None, `max_size`: Some(105506), added: 107981, mode: `MaxEncodedLen`)
 	fn send_message() -> Weight {
 		// Proof Size summary in bytes:
-		//  Measured:  `487`
-		//  Estimated: `6427`
-		// Minimum execution time: 63_624_000 picoseconds.
-		Weight::from_parts(66_071_000, 0)
-			.saturating_add(Weight::from_parts(0, 6427))
+		//  Measured:  `520`
+		//  Estimated: `6460`
+		// Minimum execution time: 46_250_000 picoseconds.
+		Weight::from_parts(47_801_000, 0)
+			.saturating_add(Weight::from_parts(0, 6460))
 			.saturating_add(T::DbWeight::get().reads(12))
 			.saturating_add(T::DbWeight::get().writes(4))
 	}
diff --git a/cumulus/parachains/runtimes/bridge-hubs/bridge-hub-rococo/Cargo.toml b/cumulus/parachains/runtimes/bridge-hubs/bridge-hub-rococo/Cargo.toml
index 1ac31efaf91..98737298468 100644
--- a/cumulus/parachains/runtimes/bridge-hubs/bridge-hub-rococo/Cargo.toml
+++ b/cumulus/parachains/runtimes/bridge-hubs/bridge-hub-rococo/Cargo.toml
@@ -96,7 +96,7 @@ bp-parachains = { workspace = true }
 bp-polkadot-bulletin = { workspace = true }
 bp-polkadot-core = { workspace = true }
 bp-relayers = { workspace = true }
-bp-runtime = { workspace = true }
+bp-runtime = { features = ["test-helpers"], workspace = true }
 bp-rococo = { workspace = true }
 bp-westend = { workspace = true }
 pallet-bridge-grandpa = { workspace = true }
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 5551b05e202..779cc537ee9 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
@@ -21,14 +21,9 @@
 //! For example, the messaging pallet needs to know the sending and receiving chains, but the
 //! GRANDPA tracking pallet only needs to be aware of one chain.
 
-use super::{
-	weights, AccountId, Balance, Balances, BlockNumber, Runtime, RuntimeEvent, RuntimeOrigin,
-};
+use super::{weights, AccountId, Balance, Balances, BlockNumber, Runtime, RuntimeEvent};
 use bp_parachains::SingleParaStoredHeaderDataBuilder;
-use bp_runtime::UnderlyingChainProvider;
-use bridge_runtime_common::messages::ThisChainWithMessages;
 use frame_support::{parameter_types, traits::ConstU32};
-use sp_runtime::RuntimeDebug;
 
 parameter_types! {
 	pub const RelayChainHeadersToKeep: u32 = 1024;
@@ -103,15 +98,3 @@ impl pallet_bridge_grandpa::Config<BridgeGrandpaRococoBulletinInstance> for Runt
 	// weights are also the same for both bridges.
 	type WeightInfo = weights::pallet_bridge_grandpa::WeightInfo<Runtime>;
 }
-
-/// BridgeHubRococo chain from message lane point of view.
-#[derive(RuntimeDebug, Clone, Copy)]
-pub struct BridgeHubRococo;
-
-impl UnderlyingChainProvider for BridgeHubRococo {
-	type Chain = bp_bridge_hub_rococo::BridgeHubRococo;
-}
-
-impl ThisChainWithMessages for BridgeHubRococo {
-	type RuntimeOrigin = RuntimeOrigin;
-}
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 94b936889b7..39ea6369255 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,23 +20,20 @@
 //! are reusing Polkadot Bulletin chain primitives everywhere here.
 
 use crate::{
-	bridge_common_config::BridgeHubRococo, weights, xcm_config::UniversalLocation, AccountId,
-	BridgeRococoBulletinGrandpa, BridgeRococoBulletinMessages, PolkadotXcm, Runtime, RuntimeEvent,
-	XcmOverRococoBulletin, XcmRouter,
+	weights, xcm_config::UniversalLocation, BridgeRococoBulletinGrandpa,
+	BridgeRococoBulletinMessages, PolkadotXcm, Runtime, RuntimeEvent, XcmOverRococoBulletin,
+	XcmRouter,
+};
+use bp_messages::{
+	source_chain::FromBridgedChainMessagesDeliveryProof,
+	target_chain::FromBridgedChainMessagesProof, LaneId,
 };
-use bp_messages::LaneId;
 use bp_runtime::Chain;
 use bridge_runtime_common::{
 	extensions::refund_relayer_extension::{
 		ActualFeeRefund, RefundBridgedMessages, RefundSignedExtensionAdapter,
 		RefundableMessagesLane,
 	},
-	messages,
-	messages::{
-		source::{FromBridgedChainMessagesDeliveryProof, TargetHeaderChainAdapter},
-		target::{FromBridgedChainMessagesProof, SourceHeaderChainAdapter},
-		MessageBridge, UnderlyingChainProvider,
-	},
 	messages_xcm_extension::{
 		SenderAndLane, XcmAsPlainPayload, XcmBlobHauler, XcmBlobHaulerAdapter,
 		XcmBlobMessageDispatch, XcmVersionOfDestAndRemoteBridge,
@@ -44,7 +41,6 @@ use bridge_runtime_common::{
 };
 
 use frame_support::{parameter_types, traits::PalletInfoAccess};
-use sp_runtime::RuntimeDebug;
 use xcm::{
 	latest::prelude::*,
 	prelude::{InteriorLocation, NetworkId},
@@ -52,15 +48,6 @@ use xcm::{
 use xcm_builder::BridgeBlobDispatcher;
 
 parameter_types! {
-	/// Maximal number of entries in the unrewarded relayers vector at the Rococo Bridge Hub. It matches the
-	/// maximal number of unrewarded relayers that the single confirmation transaction at Rococo Bulletin Chain
-	/// may process.
-	pub const MaxUnrewardedRelayerEntriesAtInboundLane: bp_messages::MessageNonce =
-		bp_polkadot_bulletin::MAX_UNREWARDED_RELAYERS_IN_CONFIRMATION_TX;
-	/// Maximal number of unconfirmed messages at the Rococo Bridge Hub. It matches the maximal number of
-	/// unconfirmed messages that the single confirmation transaction at Rococo Bulletin Chain may process.
-	pub const MaxUnconfirmedMessagesAtInboundLane: bp_messages::MessageNonce =
-		bp_polkadot_bulletin::MAX_UNCONFIRMED_MESSAGES_IN_CONFIRMATION_TX;
 	/// Bridge specific chain (network) identifier of the Rococo Bulletin Chain.
 	pub const RococoBulletinChainId: bp_runtime::ChainId = bp_polkadot_bulletin::PolkadotBulletin::ID;
 	/// Interior location (relative to this runtime) of the with-RococoBulletin messages pallet.
@@ -142,31 +129,6 @@ impl XcmBlobHauler for ToRococoBulletinXcmBlobHauler {
 type OnMessagesDeliveredFromRococoBulletin =
 	XcmBlobHaulerAdapter<ToRococoBulletinXcmBlobHauler, ActiveLanes>;
 
-/// Messaging Bridge configuration for BridgeHubRococo -> Rococo Bulletin.
-pub struct WithRococoBulletinMessageBridge;
-impl MessageBridge for WithRococoBulletinMessageBridge {
-	// Bulletin chain assumes it is bridged with Polkadot Bridge Hub
-	const BRIDGED_MESSAGES_PALLET_NAME: &'static str =
-		bp_bridge_hub_polkadot::WITH_BRIDGE_HUB_POLKADOT_MESSAGES_PALLET_NAME;
-	type ThisChain = BridgeHubRococo;
-	type BridgedChain = RococoBulletin;
-	type BridgedHeaderChain = BridgeRococoBulletinGrandpa;
-}
-
-/// Maximal outbound payload size of BridgeHubRococo -> RococoBulletin messages.
-pub type ToRococoBulletinMaximalOutboundPayloadSize =
-	messages::source::FromThisChainMaximalOutboundPayloadSize<WithRococoBulletinMessageBridge>;
-
-/// RococoBulletin chain from message lane point of view.
-#[derive(RuntimeDebug, Clone, Copy)]
-pub struct RococoBulletin;
-
-impl UnderlyingChainProvider for RococoBulletin {
-	type Chain = bp_polkadot_bulletin::PolkadotBulletin;
-}
-
-impl messages::BridgedChainWithMessages for RococoBulletin {}
-
 /// Signed extension that refunds relayers that are delivering messages from the Rococo Bulletin
 /// chain.
 pub type OnBridgeHubRococoRefundRococoBulletinMessages = RefundSignedExtensionAdapter<
@@ -189,22 +151,20 @@ impl pallet_bridge_messages::Config<WithRococoBulletinMessagesInstance> for Runt
 	type RuntimeEvent = RuntimeEvent;
 	type WeightInfo =
 		weights::pallet_bridge_messages_rococo_to_rococo_bulletin::WeightInfo<Runtime>;
-	type BridgedChainId = RococoBulletinChainId;
+
+	type ThisChain = bp_bridge_hub_rococo::BridgeHubRococo;
+	type BridgedChain = bp_polkadot_bulletin::PolkadotBulletin;
+	type BridgedHeaderChain = BridgeRococoBulletinGrandpa;
+
 	type ActiveOutboundLanes = ActiveOutboundLanesToRococoBulletin;
-	type MaxUnrewardedRelayerEntriesAtInboundLane = MaxUnrewardedRelayerEntriesAtInboundLane;
-	type MaxUnconfirmedMessagesAtInboundLane = MaxUnconfirmedMessagesAtInboundLane;
 
-	type MaximalOutboundPayloadSize = ToRococoBulletinMaximalOutboundPayloadSize;
 	type OutboundPayload = XcmAsPlainPayload;
 
 	type InboundPayload = XcmAsPlainPayload;
-	type InboundRelayer = AccountId;
 	type DeliveryPayments = ();
 
-	type TargetHeaderChain = TargetHeaderChainAdapter<WithRococoBulletinMessageBridge>;
 	type DeliveryConfirmationPayments = ();
 
-	type SourceHeaderChain = SourceHeaderChainAdapter<WithRococoBulletinMessageBridge>;
 	type MessageDispatch =
 		XcmBlobMessageDispatch<FromRococoBulletinMessageBlobDispatcher, Self::WeightInfo, ()>;
 	type OnMessagesDelivered = OnMessagesDeliveredFromRococoBulletin;
@@ -267,8 +227,7 @@ mod tests {
 			runtime: Runtime,
 			with_bridged_chain_grandpa_instance: BridgeGrandpaRococoBulletinInstance,
 			with_bridged_chain_messages_instance: WithRococoBulletinMessagesInstance,
-			bridge: WithRococoBulletinMessageBridge,
-			this_chain: bp_rococo::Rococo,
+			this_chain: bp_bridge_hub_rococo::BridgeHubRococo,
 			bridged_chain: bp_polkadot_bulletin::PolkadotBulletin,
 		);
 
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 1681ac7f468..07bb718bd13 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,27 +17,21 @@
 //! Bridge definitions used on BridgeHubRococo for bridging to BridgeHubWestend.
 
 use crate::{
-	bridge_common_config::{
-		BridgeHubRococo, BridgeParachainWestendInstance, DeliveryRewardInBalance,
-	},
+	bridge_common_config::{BridgeParachainWestendInstance, DeliveryRewardInBalance},
 	weights,
 	xcm_config::UniversalLocation,
-	AccountId, BridgeWestendMessages, PolkadotXcm, Runtime, RuntimeEvent, XcmOverBridgeHubWestend,
-	XcmRouter,
+	BridgeWestendMessages, PolkadotXcm, Runtime, RuntimeEvent, XcmOverBridgeHubWestend, XcmRouter,
+};
+use bp_messages::{
+	source_chain::FromBridgedChainMessagesDeliveryProof,
+	target_chain::FromBridgedChainMessagesProof, LaneId,
 };
-use bp_messages::LaneId;
 use bp_runtime::Chain;
 use bridge_runtime_common::{
 	extensions::refund_relayer_extension::{
 		ActualFeeRefund, RefundBridgedMessages, RefundSignedExtensionAdapter,
 		RefundableMessagesLane,
 	},
-	messages,
-	messages::{
-		source::{FromBridgedChainMessagesDeliveryProof, TargetHeaderChainAdapter},
-		target::{FromBridgedChainMessagesProof, SourceHeaderChainAdapter},
-		MessageBridge, UnderlyingChainProvider,
-	},
 	messages_xcm_extension::{
 		SenderAndLane, XcmAsPlainPayload, XcmBlobHauler, XcmBlobHaulerAdapter,
 		XcmBlobMessageDispatch, XcmVersionOfDestAndRemoteBridge,
@@ -46,7 +40,6 @@ use bridge_runtime_common::{
 
 use codec::Encode;
 use frame_support::{parameter_types, traits::PalletInfoAccess};
-use sp_runtime::RuntimeDebug;
 use xcm::{
 	latest::prelude::*,
 	prelude::{InteriorLocation, NetworkId},
@@ -54,11 +47,7 @@ use xcm::{
 use xcm_builder::BridgeBlobDispatcher;
 
 parameter_types! {
-	pub const MaxUnrewardedRelayerEntriesAtInboundLane: bp_messages::MessageNonce =
-		bp_bridge_hub_rococo::MAX_UNREWARDED_RELAYERS_IN_CONFIRMATION_TX;
-	pub const MaxUnconfirmedMessagesAtInboundLane: bp_messages::MessageNonce =
-		bp_bridge_hub_rococo::MAX_UNCONFIRMED_MESSAGES_IN_CONFIRMATION_TX;
-	pub const BridgeHubWestendChainId: bp_runtime::ChainId = BridgeHubWestend::ID;
+	pub const BridgeHubWestendChainId: bp_runtime::ChainId = bp_bridge_hub_westend::BridgeHubWestend::ID;
 	pub BridgeRococoToWestendMessagesPalletInstance: InteriorLocation = [PalletInstance(<BridgeWestendMessages as PalletInfoAccess>::index() as u8)].into();
 	pub WestendGlobalConsensusNetwork: NetworkId = NetworkId::Westend;
 	pub WestendGlobalConsensusNetworkLocation: Location = Location::new(
@@ -148,34 +137,6 @@ impl XcmBlobHauler for ToBridgeHubWestendXcmBlobHauler {
 type OnMessagesDeliveredFromWestend =
 	XcmBlobHaulerAdapter<ToBridgeHubWestendXcmBlobHauler, ActiveLanes>;
 
-/// Messaging Bridge configuration for BridgeHubRococo -> BridgeHubWestend
-pub struct WithBridgeHubWestendMessageBridge;
-impl MessageBridge for WithBridgeHubWestendMessageBridge {
-	const BRIDGED_MESSAGES_PALLET_NAME: &'static str =
-		bp_bridge_hub_rococo::WITH_BRIDGE_HUB_ROCOCO_MESSAGES_PALLET_NAME;
-	type ThisChain = BridgeHubRococo;
-	type BridgedChain = BridgeHubWestend;
-	type BridgedHeaderChain = pallet_bridge_parachains::ParachainHeaders<
-		Runtime,
-		BridgeParachainWestendInstance,
-		bp_bridge_hub_westend::BridgeHubWestend,
-	>;
-}
-
-/// Maximal outbound payload size of BridgeHubRococo -> BridgeHubWestend messages.
-pub type ToBridgeHubWestendMaximalOutboundPayloadSize =
-	messages::source::FromThisChainMaximalOutboundPayloadSize<WithBridgeHubWestendMessageBridge>;
-
-/// BridgeHubWestend chain from message lane point of view.
-#[derive(RuntimeDebug, Clone, Copy)]
-pub struct BridgeHubWestend;
-
-impl UnderlyingChainProvider for BridgeHubWestend {
-	type Chain = bp_bridge_hub_westend::BridgeHubWestend;
-}
-
-impl messages::BridgedChainWithMessages for BridgeHubWestend {}
-
 /// Signed extension that refunds relayers that are delivering messages from the Westend parachain.
 pub type OnBridgeHubRococoRefundBridgeHubWestendMessages = RefundSignedExtensionAdapter<
 	RefundBridgedMessages<
@@ -196,26 +157,28 @@ pub type WithBridgeHubWestendMessagesInstance = pallet_bridge_messages::Instance
 impl pallet_bridge_messages::Config<WithBridgeHubWestendMessagesInstance> for Runtime {
 	type RuntimeEvent = RuntimeEvent;
 	type WeightInfo = weights::pallet_bridge_messages_rococo_to_westend::WeightInfo<Runtime>;
-	type BridgedChainId = BridgeHubWestendChainId;
+
+	type ThisChain = bp_bridge_hub_rococo::BridgeHubRococo;
+	type BridgedChain = bp_bridge_hub_westend::BridgeHubWestend;
+	type BridgedHeaderChain = pallet_bridge_parachains::ParachainHeaders<
+		Runtime,
+		BridgeParachainWestendInstance,
+		bp_bridge_hub_westend::BridgeHubWestend,
+	>;
+
 	type ActiveOutboundLanes = ActiveOutboundLanesToBridgeHubWestend;
-	type MaxUnrewardedRelayerEntriesAtInboundLane = MaxUnrewardedRelayerEntriesAtInboundLane;
-	type MaxUnconfirmedMessagesAtInboundLane = MaxUnconfirmedMessagesAtInboundLane;
 
-	type MaximalOutboundPayloadSize = ToBridgeHubWestendMaximalOutboundPayloadSize;
 	type OutboundPayload = XcmAsPlainPayload;
 
 	type InboundPayload = XcmAsPlainPayload;
-	type InboundRelayer = AccountId;
 	type DeliveryPayments = ();
 
-	type TargetHeaderChain = TargetHeaderChainAdapter<WithBridgeHubWestendMessageBridge>;
 	type DeliveryConfirmationPayments = pallet_bridge_relayers::DeliveryConfirmationPaymentsAdapter<
 		Runtime,
 		WithBridgeHubWestendMessagesInstance,
 		DeliveryRewardInBalance,
 	>;
 
-	type SourceHeaderChain = SourceHeaderChainAdapter<WithBridgeHubWestendMessageBridge>;
 	type MessageDispatch = XcmBlobMessageDispatch<
 		FromWestendMessageBlobDispatcher,
 		Self::WeightInfo,
@@ -248,9 +211,8 @@ mod tests {
 		assert_complete_bridge_types,
 		extensions::refund_relayer_extension::RefundableParachain,
 		integrity::{
-			assert_complete_bridge_constants, check_message_lane_weights,
-			AssertBridgeMessagesPalletConstants, AssertBridgePalletNames, AssertChainConstants,
-			AssertCompleteBridgeConstants,
+			assert_complete_with_parachain_bridge_constants, check_message_lane_weights,
+			AssertChainConstants, AssertCompleteBridgeConstants,
 		},
 	};
 	use parachains_common::Balance;
@@ -292,36 +254,20 @@ mod tests {
 			runtime: Runtime,
 			with_bridged_chain_grandpa_instance: BridgeGrandpaWestendInstance,
 			with_bridged_chain_messages_instance: WithBridgeHubWestendMessagesInstance,
-			bridge: WithBridgeHubWestendMessageBridge,
-			this_chain: bp_rococo::Rococo,
-			bridged_chain: bp_westend::Westend,
+			this_chain: bp_bridge_hub_rococo::BridgeHubRococo,
+			bridged_chain: bp_bridge_hub_westend::BridgeHubWestend,
 		);
 
-		assert_complete_bridge_constants::<
+		assert_complete_with_parachain_bridge_constants::<
 			Runtime,
 			BridgeGrandpaWestendInstance,
 			WithBridgeHubWestendMessagesInstance,
-			WithBridgeHubWestendMessageBridge,
+			bp_westend::Westend,
 		>(AssertCompleteBridgeConstants {
 			this_chain_constants: AssertChainConstants {
 				block_length: bp_bridge_hub_rococo::BlockLength::get(),
 				block_weights: bp_bridge_hub_rococo::BlockWeightsForAsyncBacking::get(),
 			},
-			messages_pallet_constants: AssertBridgeMessagesPalletConstants {
-				max_unrewarded_relayers_in_bridged_confirmation_tx:
-					bp_bridge_hub_westend::MAX_UNREWARDED_RELAYERS_IN_CONFIRMATION_TX,
-				max_unconfirmed_messages_in_bridged_confirmation_tx:
-					bp_bridge_hub_westend::MAX_UNCONFIRMED_MESSAGES_IN_CONFIRMATION_TX,
-				bridged_chain_id: BridgeHubWestend::ID,
-			},
-			pallet_names: AssertBridgePalletNames {
-				with_this_chain_messages_pallet_name:
-					bp_bridge_hub_rococo::WITH_BRIDGE_HUB_ROCOCO_MESSAGES_PALLET_NAME,
-				with_bridged_chain_grandpa_pallet_name:
-					bp_westend::WITH_WESTEND_GRANDPA_PALLET_NAME,
-				with_bridged_chain_messages_pallet_name:
-					bp_bridge_hub_westend::WITH_BRIDGE_HUB_WESTEND_MESSAGES_PALLET_NAME,
-			},
 		});
 
 		bridge_runtime_common::extensions::priority_calculator::per_relay_header::ensure_priority_boost_is_sane::<
@@ -332,7 +278,7 @@ mod tests {
 
 		bridge_runtime_common::extensions::priority_calculator::per_parachain_header::ensure_priority_boost_is_sane::<
 			Runtime,
-			RefundableParachain<WithBridgeHubWestendMessagesInstance, BridgeHubWestend>,
+			RefundableParachain<WithBridgeHubWestendMessagesInstance, bp_bridge_hub_westend::BridgeHubWestend>,
 			PriorityBoostPerParachainHeader,
 		>(FEE_BOOST_PER_PARACHAIN_HEADER);
 
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 57ed5ec258d..8ca5898d1a1 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
@@ -1432,7 +1432,7 @@ impl_runtime_apis! {
 					prepare_message_proof_from_parachain::<
 						Runtime,
 						bridge_common_config::BridgeGrandpaWestendInstance,
-						bridge_to_westend_config::WithBridgeHubWestendMessageBridge,
+						bridge_to_westend_config::WithBridgeHubWestendMessagesInstance,
 					>(params, generate_xcm_builder_bridge_message_sample([GlobalConsensus(Rococo), Parachain(42)].into()))
 				}
 
@@ -1442,7 +1442,7 @@ impl_runtime_apis! {
 					prepare_message_delivery_proof_from_parachain::<
 						Runtime,
 						bridge_common_config::BridgeGrandpaWestendInstance,
-						bridge_to_westend_config::WithBridgeHubWestendMessageBridge,
+						bridge_to_westend_config::WithBridgeHubWestendMessagesInstance,
 					>(params)
 				}
 
@@ -1467,7 +1467,7 @@ impl_runtime_apis! {
 					prepare_message_proof_from_grandpa_chain::<
 						Runtime,
 						bridge_common_config::BridgeGrandpaRococoBulletinInstance,
-						bridge_to_bulletin_config::WithRococoBulletinMessageBridge,
+						bridge_to_bulletin_config::WithRococoBulletinMessagesInstance,
 					>(params, generate_xcm_builder_bridge_message_sample([GlobalConsensus(Rococo), Parachain(42)].into()))
 				}
 
@@ -1477,7 +1477,7 @@ impl_runtime_apis! {
 					prepare_message_delivery_proof_from_grandpa_chain::<
 						Runtime,
 						bridge_common_config::BridgeGrandpaRococoBulletinInstance,
-						bridge_to_bulletin_config::WithRococoBulletinMessageBridge,
+						bridge_to_bulletin_config::WithRococoBulletinMessagesInstance,
 					>(params)
 				}
 
@@ -1503,7 +1503,7 @@ impl_runtime_apis! {
 				fn prepare_parachain_heads_proof(
 					parachains: &[bp_polkadot_core::parachains::ParaId],
 					parachain_head_size: u32,
-					proof_size: bp_runtime::StorageProofSize,
+					proof_params: bp_runtime::UnverifiedStorageProofParams,
 				) -> (
 					pallet_bridge_parachains::RelayBlockNumber,
 					pallet_bridge_parachains::RelayBlockHash,
@@ -1513,7 +1513,7 @@ impl_runtime_apis! {
 					prepare_parachain_heads_proof::<Runtime, bridge_common_config::BridgeParachainWestendInstance>(
 						parachains,
 						parachain_head_size,
-						proof_size,
+						proof_params,
 					)
 				}
 			}
diff --git a/cumulus/parachains/runtimes/bridge-hubs/bridge-hub-rococo/src/weights/pallet_bridge_grandpa.rs b/cumulus/parachains/runtimes/bridge-hubs/bridge-hub-rococo/src/weights/pallet_bridge_grandpa.rs
index bbd27bbb1d7..4ce57b2e501 100644
--- a/cumulus/parachains/runtimes/bridge-hubs/bridge-hub-rococo/src/weights/pallet_bridge_grandpa.rs
+++ b/cumulus/parachains/runtimes/bridge-hubs/bridge-hub-rococo/src/weights/pallet_bridge_grandpa.rs
@@ -17,9 +17,9 @@
 //! Autogenerated weights for `pallet_bridge_grandpa`
 //!
 //! THIS FILE WAS AUTO-GENERATED USING THE SUBSTRATE BENCHMARK CLI VERSION 32.0.0
-//! DATE: 2024-06-26, STEPS: `50`, REPEAT: `20`, LOW RANGE: `[]`, HIGH RANGE: `[]`
+//! DATE: 2024-07-11, STEPS: `50`, REPEAT: `20`, LOW RANGE: `[]`, HIGH RANGE: `[]`
 //! WORST CASE MAP SIZE: `1000000`
-//! HOSTNAME: `runner-7wrmsoux-project-674-concurrent-0`, CPU: `Intel(R) Xeon(R) CPU @ 2.60GHz`
+//! HOSTNAME: `runner-yaoqqom-project-674-concurrent-0`, CPU: `Intel(R) Xeon(R) CPU @ 2.60GHz`
 //! WASM-EXECUTION: `Compiled`, CHAIN: `Some("bridge-hub-rococo-dev")`, DB CACHE: 1024
 
 // Executed Command:
@@ -68,13 +68,13 @@ impl<T: frame_system::Config> pallet_bridge_grandpa::WeightInfo for WeightInfo<T
 		// Proof Size summary in bytes:
 		//  Measured:  `438 + p * (60 ±0)`
 		//  Estimated: `51735`
-		// Minimum execution time: 302_299_000 picoseconds.
-		Weight::from_parts(39_997_741, 0)
+		// Minimum execution time: 325_365_000 picoseconds.
+		Weight::from_parts(14_958_535, 0)
 			.saturating_add(Weight::from_parts(0, 51735))
-			// Standard Error: 22_953
-			.saturating_add(Weight::from_parts(44_095_182, 0).saturating_mul(p.into()))
-			// Standard Error: 76_592
-			.saturating_add(Weight::from_parts(2_262_037, 0).saturating_mul(v.into()))
+			// Standard Error: 15_085
+			.saturating_add(Weight::from_parts(41_227_904, 0).saturating_mul(p.into()))
+			// Standard Error: 50_338
+			.saturating_add(Weight::from_parts(2_664_555, 0).saturating_mul(v.into()))
 			.saturating_add(T::DbWeight::get().reads(6))
 			.saturating_add(T::DbWeight::get().writes(5))
 	}
@@ -92,8 +92,8 @@ impl<T: frame_system::Config> pallet_bridge_grandpa::WeightInfo for WeightInfo<T
 		// Proof Size summary in bytes:
 		//  Measured:  `452`
 		//  Estimated: `51735`
-		// Minimum execution time: 82_597_000 picoseconds.
-		Weight::from_parts(88_310_000, 0)
+		// Minimum execution time: 79_562_000 picoseconds.
+		Weight::from_parts(82_479_000, 0)
 			.saturating_add(Weight::from_parts(0, 51735))
 			.saturating_add(T::DbWeight::get().reads(3))
 			.saturating_add(T::DbWeight::get().writes(6))
diff --git a/cumulus/parachains/runtimes/bridge-hubs/bridge-hub-rococo/src/weights/pallet_bridge_messages_rococo_to_rococo_bulletin.rs b/cumulus/parachains/runtimes/bridge-hubs/bridge-hub-rococo/src/weights/pallet_bridge_messages_rococo_to_rococo_bulletin.rs
index 814716c08b6..5522a325f19 100644
--- a/cumulus/parachains/runtimes/bridge-hubs/bridge-hub-rococo/src/weights/pallet_bridge_messages_rococo_to_rococo_bulletin.rs
+++ b/cumulus/parachains/runtimes/bridge-hubs/bridge-hub-rococo/src/weights/pallet_bridge_messages_rococo_to_rococo_bulletin.rs
@@ -17,7 +17,7 @@
 //! Autogenerated weights for `pallet_bridge_messages`
 //!
 //! THIS FILE WAS AUTO-GENERATED USING THE SUBSTRATE BENCHMARK CLI VERSION 32.0.0
-//! DATE: 2024-06-26, STEPS: `50`, REPEAT: `20`, LOW RANGE: `[]`, HIGH RANGE: `[]`
+//! DATE: 2024-07-03, STEPS: `50`, REPEAT: `20`, LOW RANGE: `[]`, HIGH RANGE: `[]`
 //! WORST CASE MAP SIZE: `1000000`
 //! HOSTNAME: `runner-7wrmsoux-project-674-concurrent-0`, CPU: `Intel(R) Xeon(R) CPU @ 2.60GHz`
 //! WASM-EXECUTION: `Compiled`, CHAIN: `Some("bridge-hub-rococo-dev")`, DB CACHE: 1024
@@ -60,8 +60,8 @@ impl<T: frame_system::Config> pallet_bridge_messages::WeightInfo for WeightInfo<
 		// Proof Size summary in bytes:
 		//  Measured:  `654`
 		//  Estimated: `52645`
-		// Minimum execution time: 37_405_000 picoseconds.
-		Weight::from_parts(38_397_000, 0)
+		// Minimum execution time: 37_206_000 picoseconds.
+		Weight::from_parts(38_545_000, 0)
 			.saturating_add(Weight::from_parts(0, 52645))
 			.saturating_add(T::DbWeight::get().reads(4))
 			.saturating_add(T::DbWeight::get().writes(1))
@@ -74,13 +74,17 @@ impl<T: frame_system::Config> pallet_bridge_messages::WeightInfo for WeightInfo<
 	/// Proof: `BridgePolkadotBulletinMessages::InboundLanes` (`max_values`: None, `max_size`: Some(49180), added: 51655, mode: `MaxEncodedLen`)
 	/// Storage: `ParachainInfo::ParachainId` (r:1 w:0)
 	/// Proof: `ParachainInfo::ParachainId` (`max_values`: Some(1), `max_size`: Some(4), added: 499, mode: `MaxEncodedLen`)
-	fn receive_two_messages_proof() -> Weight {
+	/// The range of component `n` is `[1, 4076]`.
+	/// The range of component `n` is `[1, 4076]`.
+	fn receive_n_messages_proof(n: u32, ) -> Weight {
 		// Proof Size summary in bytes:
 		//  Measured:  `654`
 		//  Estimated: `52645`
-		// Minimum execution time: 48_076_000 picoseconds.
-		Weight::from_parts(49_374_000, 0)
+		// Minimum execution time: 37_075_000 picoseconds.
+		Weight::from_parts(37_757_000, 0)
 			.saturating_add(Weight::from_parts(0, 52645))
+			// Standard Error: 5_776
+			.saturating_add(Weight::from_parts(11_586_768, 0).saturating_mul(n.into()))
 			.saturating_add(T::DbWeight::get().reads(4))
 			.saturating_add(T::DbWeight::get().writes(1))
 	}
@@ -96,8 +100,8 @@ impl<T: frame_system::Config> pallet_bridge_messages::WeightInfo for WeightInfo<
 		// Proof Size summary in bytes:
 		//  Measured:  `654`
 		//  Estimated: `52645`
-		// Minimum execution time: 42_529_000 picoseconds.
-		Weight::from_parts(44_536_000, 0)
+		// Minimum execution time: 42_087_000 picoseconds.
+		Weight::from_parts(42_970_000, 0)
 			.saturating_add(Weight::from_parts(0, 52645))
 			.saturating_add(T::DbWeight::get().reads(4))
 			.saturating_add(T::DbWeight::get().writes(1))
@@ -108,30 +112,20 @@ impl<T: frame_system::Config> pallet_bridge_messages::WeightInfo for WeightInfo<
 	/// Proof: `BridgePolkadotBulletinGrandpa::ImportedHeaders` (`max_values`: Some(1024), `max_size`: Some(68), added: 1553, mode: `MaxEncodedLen`)
 	/// Storage: `BridgePolkadotBulletinMessages::InboundLanes` (r:1 w:1)
 	/// Proof: `BridgePolkadotBulletinMessages::InboundLanes` (`max_values`: None, `max_size`: Some(49180), added: 51655, mode: `MaxEncodedLen`)
-	fn receive_single_message_proof_1_kb() -> Weight {
-		// Proof Size summary in bytes:
-		//  Measured:  `622`
-		//  Estimated: `52645`
-		// Minimum execution time: 36_101_000 picoseconds.
-		Weight::from_parts(37_356_000, 0)
-			.saturating_add(Weight::from_parts(0, 52645))
-			.saturating_add(T::DbWeight::get().reads(3))
-			.saturating_add(T::DbWeight::get().writes(1))
-	}
-	/// Storage: `BridgePolkadotBulletinMessages::PalletOperatingMode` (r:1 w:0)
-	/// Proof: `BridgePolkadotBulletinMessages::PalletOperatingMode` (`max_values`: Some(1), `max_size`: Some(2), added: 497, mode: `MaxEncodedLen`)
-	/// Storage: `BridgePolkadotBulletinGrandpa::ImportedHeaders` (r:1 w:0)
-	/// Proof: `BridgePolkadotBulletinGrandpa::ImportedHeaders` (`max_values`: Some(1024), `max_size`: Some(68), added: 1553, mode: `MaxEncodedLen`)
-	/// Storage: `BridgePolkadotBulletinMessages::InboundLanes` (r:1 w:1)
-	/// Proof: `BridgePolkadotBulletinMessages::InboundLanes` (`max_values`: None, `max_size`: Some(49180), added: 51655, mode: `MaxEncodedLen`)
-	fn receive_single_message_proof_16_kb() -> Weight {
+	/// Storage: `ParachainInfo::ParachainId` (r:1 w:0)
+	/// Proof: `ParachainInfo::ParachainId` (`max_values`: Some(1), `max_size`: Some(4), added: 499, mode: `MaxEncodedLen`)
+	/// The range of component `n` is `[1, 16384]`.
+	/// The range of component `n` is `[1, 16384]`.
+	fn receive_single_n_bytes_message_proof(n: u32, ) -> Weight {
 		// Proof Size summary in bytes:
-		//  Measured:  `622`
+		//  Measured:  `654`
 		//  Estimated: `52645`
-		// Minimum execution time: 70_370_000 picoseconds.
-		Weight::from_parts(72_054_000, 0)
+		// Minimum execution time: 35_055_000 picoseconds.
+		Weight::from_parts(36_987_740, 0)
 			.saturating_add(Weight::from_parts(0, 52645))
-			.saturating_add(T::DbWeight::get().reads(3))
+			// Standard Error: 4
+			.saturating_add(Weight::from_parts(2_316, 0).saturating_mul(n.into()))
+			.saturating_add(T::DbWeight::get().reads(4))
 			.saturating_add(T::DbWeight::get().writes(1))
 	}
 	/// Storage: `BridgePolkadotBulletinMessages::PalletOperatingMode` (r:1 w:0)
@@ -144,8 +138,8 @@ impl<T: frame_system::Config> pallet_bridge_messages::WeightInfo for WeightInfo<
 		// Proof Size summary in bytes:
 		//  Measured:  `621`
 		//  Estimated: `2543`
-		// Minimum execution time: 25_144_000 picoseconds.
-		Weight::from_parts(25_876_000, 0)
+		// Minimum execution time: 24_326_000 picoseconds.
+		Weight::from_parts(25_169_000, 0)
 			.saturating_add(Weight::from_parts(0, 2543))
 			.saturating_add(T::DbWeight::get().reads(3))
 			.saturating_add(T::DbWeight::get().writes(1))
@@ -160,8 +154,8 @@ impl<T: frame_system::Config> pallet_bridge_messages::WeightInfo for WeightInfo<
 		// Proof Size summary in bytes:
 		//  Measured:  `621`
 		//  Estimated: `2543`
-		// Minimum execution time: 25_085_000 picoseconds.
-		Weight::from_parts(25_872_000, 0)
+		// Minimum execution time: 24_484_000 picoseconds.
+		Weight::from_parts(25_130_000, 0)
 			.saturating_add(Weight::from_parts(0, 2543))
 			.saturating_add(T::DbWeight::get().reads(3))
 			.saturating_add(T::DbWeight::get().writes(1))
@@ -176,8 +170,8 @@ impl<T: frame_system::Config> pallet_bridge_messages::WeightInfo for WeightInfo<
 		// Proof Size summary in bytes:
 		//  Measured:  `621`
 		//  Estimated: `2543`
-		// Minimum execution time: 25_181_000 picoseconds.
-		Weight::from_parts(25_920_000, 0)
+		// Minimum execution time: 24_450_000 picoseconds.
+		Weight::from_parts(25_164_000, 0)
 			.saturating_add(Weight::from_parts(0, 2543))
 			.saturating_add(T::DbWeight::get().reads(3))
 			.saturating_add(T::DbWeight::get().writes(1))
@@ -204,17 +198,17 @@ impl<T: frame_system::Config> pallet_bridge_messages::WeightInfo for WeightInfo<
 	/// Proof: `XcmpQueue::OutboundXcmpStatus` (`max_values`: Some(1), `max_size`: Some(1282), added: 1777, mode: `MaxEncodedLen`)
 	/// Storage: `XcmpQueue::OutboundXcmpMessages` (r:0 w:1)
 	/// Proof: `XcmpQueue::OutboundXcmpMessages` (`max_values`: None, `max_size`: Some(105506), added: 107981, mode: `MaxEncodedLen`)
-	/// The range of component `i` is `[128, 2048]`.
-	/// The range of component `i` is `[128, 2048]`.
-	fn receive_single_message_proof_with_dispatch(i: u32, ) -> Weight {
+	/// The range of component `n` is `[1, 16384]`.
+	/// The range of component `n` is `[1, 16384]`.
+	fn receive_single_n_bytes_message_proof_with_dispatch(n: u32, ) -> Weight {
 		// Proof Size summary in bytes:
 		//  Measured:  `813`
 		//  Estimated: `52645`
-		// Minimum execution time: 57_941_000 picoseconds.
-		Weight::from_parts(59_000_115, 0)
+		// Minimum execution time: 54_317_000 picoseconds.
+		Weight::from_parts(59_171_547, 0)
 			.saturating_add(Weight::from_parts(0, 52645))
-			// Standard Error: 46
-			.saturating_add(Weight::from_parts(8_660, 0).saturating_mul(i.into()))
+			// Standard Error: 7
+			.saturating_add(Weight::from_parts(7_566, 0).saturating_mul(n.into()))
 			.saturating_add(T::DbWeight::get().reads(10))
 			.saturating_add(T::DbWeight::get().writes(4))
 	}
diff --git a/cumulus/parachains/runtimes/bridge-hubs/bridge-hub-rococo/src/weights/pallet_bridge_messages_rococo_to_westend.rs b/cumulus/parachains/runtimes/bridge-hubs/bridge-hub-rococo/src/weights/pallet_bridge_messages_rococo_to_westend.rs
index e096c40d284..9c05dae979d 100644
--- a/cumulus/parachains/runtimes/bridge-hubs/bridge-hub-rococo/src/weights/pallet_bridge_messages_rococo_to_westend.rs
+++ b/cumulus/parachains/runtimes/bridge-hubs/bridge-hub-rococo/src/weights/pallet_bridge_messages_rococo_to_westend.rs
@@ -17,7 +17,7 @@
 //! Autogenerated weights for `pallet_bridge_messages`
 //!
 //! THIS FILE WAS AUTO-GENERATED USING THE SUBSTRATE BENCHMARK CLI VERSION 32.0.0
-//! DATE: 2024-06-26, STEPS: `50`, REPEAT: `20`, LOW RANGE: `[]`, HIGH RANGE: `[]`
+//! DATE: 2024-07-03, STEPS: `50`, REPEAT: `20`, LOW RANGE: `[]`, HIGH RANGE: `[]`
 //! WORST CASE MAP SIZE: `1000000`
 //! HOSTNAME: `runner-7wrmsoux-project-674-concurrent-0`, CPU: `Intel(R) Xeon(R) CPU @ 2.60GHz`
 //! WASM-EXECUTION: `Compiled`, CHAIN: `Some("bridge-hub-rococo-dev")`, DB CACHE: 1024
@@ -62,8 +62,8 @@ impl<T: frame_system::Config> pallet_bridge_messages::WeightInfo for WeightInfo<
 		// Proof Size summary in bytes:
 		//  Measured:  `658`
 		//  Estimated: `52645`
-		// Minimum execution time: 41_848_000 picoseconds.
-		Weight::from_parts(43_363_000, 0)
+		// Minimum execution time: 41_396_000 picoseconds.
+		Weight::from_parts(43_141_000, 0)
 			.saturating_add(Weight::from_parts(0, 52645))
 			.saturating_add(T::DbWeight::get().reads(5))
 			.saturating_add(T::DbWeight::get().writes(1))
@@ -78,13 +78,17 @@ impl<T: frame_system::Config> pallet_bridge_messages::WeightInfo for WeightInfo<
 	/// Proof: `BridgeWestendMessages::InboundLanes` (`max_values`: None, `max_size`: Some(49180), added: 51655, mode: `MaxEncodedLen`)
 	/// Storage: `ParachainInfo::ParachainId` (r:1 w:0)
 	/// Proof: `ParachainInfo::ParachainId` (`max_values`: Some(1), `max_size`: Some(4), added: 499, mode: `MaxEncodedLen`)
-	fn receive_two_messages_proof() -> Weight {
+	/// The range of component `n` is `[1, 4076]`.
+	/// The range of component `n` is `[1, 4076]`.
+	fn receive_n_messages_proof(n: u32, ) -> Weight {
 		// Proof Size summary in bytes:
 		//  Measured:  `658`
 		//  Estimated: `52645`
-		// Minimum execution time: 52_674_000 picoseconds.
-		Weight::from_parts(55_080_000, 0)
+		// Minimum execution time: 41_095_000 picoseconds.
+		Weight::from_parts(42_030_000, 0)
 			.saturating_add(Weight::from_parts(0, 52645))
+			// Standard Error: 5_702
+			.saturating_add(Weight::from_parts(11_627_951, 0).saturating_mul(n.into()))
 			.saturating_add(T::DbWeight::get().reads(5))
 			.saturating_add(T::DbWeight::get().writes(1))
 	}
@@ -102,8 +106,8 @@ impl<T: frame_system::Config> pallet_bridge_messages::WeightInfo for WeightInfo<
 		// Proof Size summary in bytes:
 		//  Measured:  `658`
 		//  Estimated: `52645`
-		// Minimum execution time: 46_892_000 picoseconds.
-		Weight::from_parts(49_441_000, 0)
+		// Minimum execution time: 45_912_000 picoseconds.
+		Weight::from_parts(47_564_000, 0)
 			.saturating_add(Weight::from_parts(0, 52645))
 			.saturating_add(T::DbWeight::get().reads(5))
 			.saturating_add(T::DbWeight::get().writes(1))
@@ -116,32 +120,20 @@ impl<T: frame_system::Config> pallet_bridge_messages::WeightInfo for WeightInfo<
 	/// Proof: `BridgeWestendParachains::ImportedParaHeads` (`max_values`: Some(64), `max_size`: Some(196), added: 1186, mode: `MaxEncodedLen`)
 	/// Storage: `BridgeWestendMessages::InboundLanes` (r:1 w:1)
 	/// Proof: `BridgeWestendMessages::InboundLanes` (`max_values`: None, `max_size`: Some(49180), added: 51655, mode: `MaxEncodedLen`)
-	fn receive_single_message_proof_1_kb() -> Weight {
-		// Proof Size summary in bytes:
-		//  Measured:  `626`
-		//  Estimated: `52645`
-		// Minimum execution time: 41_262_000 picoseconds.
-		Weight::from_parts(42_734_000, 0)
-			.saturating_add(Weight::from_parts(0, 52645))
-			.saturating_add(T::DbWeight::get().reads(4))
-			.saturating_add(T::DbWeight::get().writes(1))
-	}
-	/// Storage: `BridgeWestendMessages::PalletOperatingMode` (r:1 w:0)
-	/// Proof: `BridgeWestendMessages::PalletOperatingMode` (`max_values`: Some(1), `max_size`: Some(2), added: 497, mode: `MaxEncodedLen`)
-	/// Storage: `XcmpQueue::OutboundXcmpStatus` (r:1 w:0)
-	/// Proof: `XcmpQueue::OutboundXcmpStatus` (`max_values`: Some(1), `max_size`: Some(1282), added: 1777, mode: `MaxEncodedLen`)
-	/// Storage: `BridgeWestendParachains::ImportedParaHeads` (r:1 w:0)
-	/// Proof: `BridgeWestendParachains::ImportedParaHeads` (`max_values`: Some(64), `max_size`: Some(196), added: 1186, mode: `MaxEncodedLen`)
-	/// Storage: `BridgeWestendMessages::InboundLanes` (r:1 w:1)
-	/// Proof: `BridgeWestendMessages::InboundLanes` (`max_values`: None, `max_size`: Some(49180), added: 51655, mode: `MaxEncodedLen`)
-	fn receive_single_message_proof_16_kb() -> Weight {
+	/// Storage: `ParachainInfo::ParachainId` (r:1 w:0)
+	/// Proof: `ParachainInfo::ParachainId` (`max_values`: Some(1), `max_size`: Some(4), added: 499, mode: `MaxEncodedLen`)
+	/// The range of component `n` is `[1, 16384]`.
+	/// The range of component `n` is `[1, 16384]`.
+	fn receive_single_n_bytes_message_proof(n: u32, ) -> Weight {
 		// Proof Size summary in bytes:
-		//  Measured:  `626`
+		//  Measured:  `658`
 		//  Estimated: `52645`
-		// Minimum execution time: 75_654_000 picoseconds.
-		Weight::from_parts(76_866_000, 0)
+		// Minimum execution time: 39_175_000 picoseconds.
+		Weight::from_parts(41_674_095, 0)
 			.saturating_add(Weight::from_parts(0, 52645))
-			.saturating_add(T::DbWeight::get().reads(4))
+			// Standard Error: 4
+			.saturating_add(Weight::from_parts(2_305, 0).saturating_mul(n.into()))
+			.saturating_add(T::DbWeight::get().reads(5))
 			.saturating_add(T::DbWeight::get().writes(1))
 	}
 	/// Storage: `BridgeWestendMessages::PalletOperatingMode` (r:1 w:0)
@@ -158,8 +150,8 @@ impl<T: frame_system::Config> pallet_bridge_messages::WeightInfo for WeightInfo<
 		// Proof Size summary in bytes:
 		//  Measured:  `501`
 		//  Estimated: `3966`
-		// Minimum execution time: 32_911_000 picoseconds.
-		Weight::from_parts(33_644_000, 0)
+		// Minimum execution time: 32_033_000 picoseconds.
+		Weight::from_parts(33_131_000, 0)
 			.saturating_add(Weight::from_parts(0, 3966))
 			.saturating_add(T::DbWeight::get().reads(5))
 			.saturating_add(T::DbWeight::get().writes(2))
@@ -178,8 +170,8 @@ impl<T: frame_system::Config> pallet_bridge_messages::WeightInfo for WeightInfo<
 		// Proof Size summary in bytes:
 		//  Measured:  `501`
 		//  Estimated: `3966`
-		// Minimum execution time: 32_830_000 picoseconds.
-		Weight::from_parts(33_675_000, 0)
+		// Minimum execution time: 32_153_000 picoseconds.
+		Weight::from_parts(33_126_000, 0)
 			.saturating_add(Weight::from_parts(0, 3966))
 			.saturating_add(T::DbWeight::get().reads(5))
 			.saturating_add(T::DbWeight::get().writes(2))
@@ -198,8 +190,8 @@ impl<T: frame_system::Config> pallet_bridge_messages::WeightInfo for WeightInfo<
 		// Proof Size summary in bytes:
 		//  Measured:  `501`
 		//  Estimated: `6086`
-		// Minimum execution time: 37_024_000 picoseconds.
-		Weight::from_parts(38_112_000, 0)
+		// Minimum execution time: 36_387_000 picoseconds.
+		Weight::from_parts(37_396_000, 0)
 			.saturating_add(Weight::from_parts(0, 6086))
 			.saturating_add(T::DbWeight::get().reads(6))
 			.saturating_add(T::DbWeight::get().writes(3))
@@ -226,17 +218,17 @@ impl<T: frame_system::Config> pallet_bridge_messages::WeightInfo for WeightInfo<
 	/// Proof: `ParachainSystem::RelevantMessagingState` (`max_values`: Some(1), `max_size`: None, mode: `Measured`)
 	/// Storage: `XcmpQueue::OutboundXcmpMessages` (r:0 w:1)
 	/// Proof: `XcmpQueue::OutboundXcmpMessages` (`max_values`: None, `max_size`: Some(105506), added: 107981, mode: `MaxEncodedLen`)
-	/// The range of component `i` is `[128, 2048]`.
-	/// The range of component `i` is `[128, 2048]`.
-	fn receive_single_message_proof_with_dispatch(i: u32, ) -> Weight {
+	/// The range of component `n` is `[1, 16384]`.
+	/// The range of component `n` is `[1, 16384]`.
+	fn receive_single_n_bytes_message_proof_with_dispatch(n: u32, ) -> Weight {
 		// Proof Size summary in bytes:
 		//  Measured:  `789`
 		//  Estimated: `52645`
-		// Minimum execution time: 60_653_000 picoseconds.
-		Weight::from_parts(62_358_212, 0)
+		// Minimum execution time: 56_562_000 picoseconds.
+		Weight::from_parts(61_452_871, 0)
 			.saturating_add(Weight::from_parts(0, 52645))
-			// Standard Error: 57
-			.saturating_add(Weight::from_parts(8_660, 0).saturating_mul(i.into()))
+			// Standard Error: 9
+			.saturating_add(Weight::from_parts(7_587, 0).saturating_mul(n.into()))
 			.saturating_add(T::DbWeight::get().reads(10))
 			.saturating_add(T::DbWeight::get().writes(4))
 	}
diff --git a/cumulus/parachains/runtimes/bridge-hubs/bridge-hub-rococo/src/weights/pallet_bridge_parachains.rs b/cumulus/parachains/runtimes/bridge-hubs/bridge-hub-rococo/src/weights/pallet_bridge_parachains.rs
index af216cd997c..8eb291ea145 100644
--- a/cumulus/parachains/runtimes/bridge-hubs/bridge-hub-rococo/src/weights/pallet_bridge_parachains.rs
+++ b/cumulus/parachains/runtimes/bridge-hubs/bridge-hub-rococo/src/weights/pallet_bridge_parachains.rs
@@ -17,7 +17,7 @@
 //! Autogenerated weights for `pallet_bridge_parachains`
 //!
 //! THIS FILE WAS AUTO-GENERATED USING THE SUBSTRATE BENCHMARK CLI VERSION 32.0.0
-//! DATE: 2024-06-26, STEPS: `50`, REPEAT: `20`, LOW RANGE: `[]`, HIGH RANGE: `[]`
+//! DATE: 2024-07-03, STEPS: `50`, REPEAT: `20`, LOW RANGE: `[]`, HIGH RANGE: `[]`
 //! WORST CASE MAP SIZE: `1000000`
 //! HOSTNAME: `runner-7wrmsoux-project-674-concurrent-0`, CPU: `Intel(R) Xeon(R) CPU @ 2.60GHz`
 //! WASM-EXECUTION: `Compiled`, CHAIN: `Some("bridge-hub-rococo-dev")`, DB CACHE: 1024
@@ -61,13 +61,15 @@ impl<T: frame_system::Config> pallet_bridge_parachains::WeightInfo for WeightInf
 	/// Storage: `BridgeWestendParachains::ImportedParaHeads` (r:0 w:1)
 	/// Proof: `BridgeWestendParachains::ImportedParaHeads` (`max_values`: Some(64), `max_size`: Some(196), added: 1186, mode: `MaxEncodedLen`)
 	/// The range of component `p` is `[1, 2]`.
-	fn submit_parachain_heads_with_n_parachains(_p: u32, ) -> Weight {
+	fn submit_parachain_heads_with_n_parachains(p: u32, ) -> Weight {
 		// Proof Size summary in bytes:
 		//  Measured:  `558`
 		//  Estimated: `2543`
-		// Minimum execution time: 35_711_000 picoseconds.
-		Weight::from_parts(37_344_514, 0)
+		// Minimum execution time: 34_889_000 picoseconds.
+		Weight::from_parts(36_100_759, 0)
 			.saturating_add(Weight::from_parts(0, 2543))
+			// Standard Error: 102_466
+			.saturating_add(Weight::from_parts(178_820, 0).saturating_mul(p.into()))
 			.saturating_add(T::DbWeight::get().reads(5))
 			.saturating_add(T::DbWeight::get().writes(4))
 	}
@@ -87,8 +89,8 @@ impl<T: frame_system::Config> pallet_bridge_parachains::WeightInfo for WeightInf
 		// Proof Size summary in bytes:
 		//  Measured:  `558`
 		//  Estimated: `2543`
-		// Minimum execution time: 37_717_000 picoseconds.
-		Weight::from_parts(38_374_000, 0)
+		// Minimum execution time: 36_501_000 picoseconds.
+		Weight::from_parts(37_266_000, 0)
 			.saturating_add(Weight::from_parts(0, 2543))
 			.saturating_add(T::DbWeight::get().reads(5))
 			.saturating_add(T::DbWeight::get().writes(4))
@@ -109,8 +111,8 @@ impl<T: frame_system::Config> pallet_bridge_parachains::WeightInfo for WeightInf
 		// Proof Size summary in bytes:
 		//  Measured:  `558`
 		//  Estimated: `2543`
-		// Minimum execution time: 70_203_000 picoseconds.
-		Weight::from_parts(70_994_000, 0)
+		// Minimum execution time: 66_059_000 picoseconds.
+		Weight::from_parts(67_139_000, 0)
 			.saturating_add(Weight::from_parts(0, 2543))
 			.saturating_add(T::DbWeight::get().reads(5))
 			.saturating_add(T::DbWeight::get().writes(4))
diff --git a/cumulus/parachains/runtimes/bridge-hubs/bridge-hub-rococo/src/weights/pallet_bridge_relayers.rs b/cumulus/parachains/runtimes/bridge-hubs/bridge-hub-rococo/src/weights/pallet_bridge_relayers.rs
index a66a7fb08aa..f8bb983e80a 100644
--- a/cumulus/parachains/runtimes/bridge-hubs/bridge-hub-rococo/src/weights/pallet_bridge_relayers.rs
+++ b/cumulus/parachains/runtimes/bridge-hubs/bridge-hub-rococo/src/weights/pallet_bridge_relayers.rs
@@ -17,7 +17,7 @@
 //! Autogenerated weights for `pallet_bridge_relayers`
 //!
 //! THIS FILE WAS AUTO-GENERATED USING THE SUBSTRATE BENCHMARK CLI VERSION 32.0.0
-//! DATE: 2024-06-26, STEPS: `50`, REPEAT: `20`, LOW RANGE: `[]`, HIGH RANGE: `[]`
+//! DATE: 2024-07-03, STEPS: `50`, REPEAT: `20`, LOW RANGE: `[]`, HIGH RANGE: `[]`
 //! WORST CASE MAP SIZE: `1000000`
 //! HOSTNAME: `runner-7wrmsoux-project-674-concurrent-0`, CPU: `Intel(R) Xeon(R) CPU @ 2.60GHz`
 //! WASM-EXECUTION: `Compiled`, CHAIN: `Some("bridge-hub-rococo-dev")`, DB CACHE: 1024
@@ -56,8 +56,8 @@ impl<T: frame_system::Config> pallet_bridge_relayers::WeightInfo for WeightInfo<
 		// Proof Size summary in bytes:
 		//  Measured:  `278`
 		//  Estimated: `3593`
-		// Minimum execution time: 43_669_000 picoseconds.
-		Weight::from_parts(44_907_000, 0)
+		// Minimum execution time: 44_224_000 picoseconds.
+		Weight::from_parts(44_905_000, 0)
 			.saturating_add(Weight::from_parts(0, 3593))
 			.saturating_add(T::DbWeight::get().reads(2))
 			.saturating_add(T::DbWeight::get().writes(2))
@@ -72,8 +72,8 @@ impl<T: frame_system::Config> pallet_bridge_relayers::WeightInfo for WeightInfo<
 		// Proof Size summary in bytes:
 		//  Measured:  `131`
 		//  Estimated: `4714`
-		// Minimum execution time: 24_024_000 picoseconds.
-		Weight::from_parts(24_582_000, 0)
+		// Minimum execution time: 23_902_000 picoseconds.
+		Weight::from_parts(24_702_000, 0)
 			.saturating_add(Weight::from_parts(0, 4714))
 			.saturating_add(T::DbWeight::get().reads(3))
 			.saturating_add(T::DbWeight::get().writes(2))
@@ -86,8 +86,8 @@ impl<T: frame_system::Config> pallet_bridge_relayers::WeightInfo for WeightInfo<
 		// Proof Size summary in bytes:
 		//  Measured:  `231`
 		//  Estimated: `4714`
-		// Minimum execution time: 24_522_000 picoseconds.
-		Weight::from_parts(25_362_000, 0)
+		// Minimum execution time: 24_469_000 picoseconds.
+		Weight::from_parts(25_176_000, 0)
 			.saturating_add(Weight::from_parts(0, 4714))
 			.saturating_add(T::DbWeight::get().reads(2))
 			.saturating_add(T::DbWeight::get().writes(2))
@@ -102,8 +102,8 @@ impl<T: frame_system::Config> pallet_bridge_relayers::WeightInfo for WeightInfo<
 		// Proof Size summary in bytes:
 		//  Measured:  `334`
 		//  Estimated: `4714`
-		// Minimum execution time: 26_963_000 picoseconds.
-		Weight::from_parts(27_686_000, 0)
+		// Minimum execution time: 27_518_000 picoseconds.
+		Weight::from_parts(28_068_000, 0)
 			.saturating_add(Weight::from_parts(0, 4714))
 			.saturating_add(T::DbWeight::get().reads(3))
 			.saturating_add(T::DbWeight::get().writes(3))
@@ -114,8 +114,8 @@ impl<T: frame_system::Config> pallet_bridge_relayers::WeightInfo for WeightInfo<
 		// Proof Size summary in bytes:
 		//  Measured:  `76`
 		//  Estimated: `3538`
-		// Minimum execution time: 5_401_000 picoseconds.
-		Weight::from_parts(5_563_000, 0)
+		// Minimum execution time: 5_484_000 picoseconds.
+		Weight::from_parts(5_718_000, 0)
 			.saturating_add(Weight::from_parts(0, 3538))
 			.saturating_add(T::DbWeight::get().reads(1))
 			.saturating_add(T::DbWeight::get().writes(1))
diff --git a/cumulus/parachains/runtimes/bridge-hubs/bridge-hub-rococo/src/weights/xcm/pallet_xcm_benchmarks_generic.rs b/cumulus/parachains/runtimes/bridge-hubs/bridge-hub-rococo/src/weights/xcm/pallet_xcm_benchmarks_generic.rs
index abd84f8e89b..bafc973bdac 100644
--- a/cumulus/parachains/runtimes/bridge-hubs/bridge-hub-rococo/src/weights/xcm/pallet_xcm_benchmarks_generic.rs
+++ b/cumulus/parachains/runtimes/bridge-hubs/bridge-hub-rococo/src/weights/xcm/pallet_xcm_benchmarks_generic.rs
@@ -16,10 +16,10 @@
 
 //! Autogenerated weights for `pallet_xcm_benchmarks::generic`
 //!
-//! THIS FILE WAS AUTO-GENERATED USING THE SUBSTRATE BENCHMARK CLI VERSION 4.0.0-dev
-//! DATE: 2023-12-12, STEPS: `50`, REPEAT: `20`, LOW RANGE: `[]`, HIGH RANGE: `[]`
+//! THIS FILE WAS AUTO-GENERATED USING THE SUBSTRATE BENCHMARK CLI VERSION 32.0.0
+//! DATE: 2024-07-03, STEPS: `50`, REPEAT: `20`, LOW RANGE: `[]`, HIGH RANGE: `[]`
 //! WORST CASE MAP SIZE: `1000000`
-//! HOSTNAME: `runner-itmxxexx-project-674-concurrent-0`, CPU: `Intel(R) Xeon(R) CPU @ 2.60GHz`
+//! HOSTNAME: `runner-7wrmsoux-project-674-concurrent-0`, CPU: `Intel(R) Xeon(R) CPU @ 2.60GHz`
 //! WASM-EXECUTION: Compiled, CHAIN: Some("bridge-hub-rococo-dev"), DB CACHE: 1024
 
 // Executed Command:
@@ -68,8 +68,8 @@ impl<T: frame_system::Config> WeightInfo<T> {
 		// Proof Size summary in bytes:
 		//  Measured:  `171`
 		//  Estimated: `6196`
-		// Minimum execution time: 61_813_000 picoseconds.
-		Weight::from_parts(62_996_000, 6196)
+		// Minimum execution time: 60_119_000 picoseconds.
+		Weight::from_parts(61_871_000, 6196)
 			.saturating_add(T::DbWeight::get().reads(9))
 			.saturating_add(T::DbWeight::get().writes(4))
 	}
@@ -77,8 +77,8 @@ impl<T: frame_system::Config> WeightInfo<T> {
 		// Proof Size summary in bytes:
 		//  Measured:  `0`
 		//  Estimated: `0`
-		// Minimum execution time: 2_044_000 picoseconds.
-		Weight::from_parts(2_112_000, 0)
+		// Minimum execution time: 998_000 picoseconds.
+		Weight::from_parts(1_038_000, 0)
 	}
 	// Storage: `PolkadotXcm::Queries` (r:1 w:0)
 	// Proof: `PolkadotXcm::Queries` (`max_values`: None, `max_size`: None, mode: `Measured`)
@@ -86,58 +86,58 @@ impl<T: frame_system::Config> WeightInfo<T> {
 		// Proof Size summary in bytes:
 		//  Measured:  `32`
 		//  Estimated: `3497`
-		// Minimum execution time: 7_472_000 picoseconds.
-		Weight::from_parts(7_723_000, 3497)
+		// Minimum execution time: 6_327_000 picoseconds.
+		Weight::from_parts(6_520_000, 3497)
 			.saturating_add(T::DbWeight::get().reads(1))
 	}
 	pub fn transact() -> Weight {
 		// Proof Size summary in bytes:
 		//  Measured:  `0`
 		//  Estimated: `0`
-		// Minimum execution time: 8_414_000 picoseconds.
-		Weight::from_parts(8_765_000, 0)
+		// Minimum execution time: 6_783_000 picoseconds.
+		Weight::from_parts(7_117_000, 0)
 	}
 	pub fn refund_surplus() -> Weight {
 		// Proof Size summary in bytes:
 		//  Measured:  `0`
 		//  Estimated: `0`
-		// Minimum execution time: 2_192_000 picoseconds.
-		Weight::from_parts(2_243_000, 0)
+		// Minimum execution time: 1_589_000 picoseconds.
+		Weight::from_parts(1_655_000, 0)
 	}
 	pub fn set_error_handler() -> Weight {
 		// Proof Size summary in bytes:
 		//  Measured:  `0`
 		//  Estimated: `0`
-		// Minimum execution time: 1_866_000 picoseconds.
-		Weight::from_parts(1_931_000, 0)
+		// Minimum execution time: 1_013_000 picoseconds.
+		Weight::from_parts(1_045_000, 0)
 	}
 	pub fn set_appendix() -> Weight {
 		// Proof Size summary in bytes:
 		//  Measured:  `0`
 		//  Estimated: `0`
-		// Minimum execution time: 1_847_000 picoseconds.
-		Weight::from_parts(1_921_000, 0)
+		// Minimum execution time: 1_005_000 picoseconds.
+		Weight::from_parts(1_044_000, 0)
 	}
 	pub fn clear_error() -> Weight {
 		// Proof Size summary in bytes:
 		//  Measured:  `0`
 		//  Estimated: `0`
-		// Minimum execution time: 1_797_000 picoseconds.
-		Weight::from_parts(1_880_000, 0)
+		// Minimum execution time: 964_000 picoseconds.
+		Weight::from_parts(1_011_000, 0)
 	}
 	pub fn descend_origin() -> Weight {
 		// Proof Size summary in bytes:
 		//  Measured:  `0`
 		//  Estimated: `0`
-		// Minimum execution time: 2_458_000 picoseconds.
-		Weight::from_parts(2_523_000, 0)
+		// Minimum execution time: 1_005_000 picoseconds.
+		Weight::from_parts(1_027_000, 0)
 	}
 	pub fn clear_origin() -> Weight {
 		// Proof Size summary in bytes:
 		//  Measured:  `0`
 		//  Estimated: `0`
-		// Minimum execution time: 1_833_000 picoseconds.
-		Weight::from_parts(1_906_000, 0)
+		// Minimum execution time: 980_000 picoseconds.
+		Weight::from_parts(1_009_000, 0)
 	}
 	// Storage: `ParachainInfo::ParachainId` (r:1 w:0)
 	// Proof: `ParachainInfo::ParachainId` (`max_values`: Some(1), `max_size`: Some(4), added: 499, mode: `MaxEncodedLen`)
@@ -159,8 +159,8 @@ impl<T: frame_system::Config> WeightInfo<T> {
 		// Proof Size summary in bytes:
 		//  Measured:  `171`
 		//  Estimated: `6196`
-		// Minimum execution time: 54_659_000 picoseconds.
-		Weight::from_parts(56_025_000, 6196)
+		// Minimum execution time: 56_726_000 picoseconds.
+		Weight::from_parts(59_300_000, 6196)
 			.saturating_add(T::DbWeight::get().reads(9))
 			.saturating_add(T::DbWeight::get().writes(4))
 	}
@@ -170,8 +170,8 @@ impl<T: frame_system::Config> WeightInfo<T> {
 		// Proof Size summary in bytes:
 		//  Measured:  `90`
 		//  Estimated: `3555`
-		// Minimum execution time: 10_953_000 picoseconds.
-		Weight::from_parts(11_220_000, 3555)
+		// Minimum execution time: 8_962_000 picoseconds.
+		Weight::from_parts(9_519_000, 3555)
 			.saturating_add(T::DbWeight::get().reads(1))
 			.saturating_add(T::DbWeight::get().writes(1))
 	}
@@ -179,8 +179,8 @@ impl<T: frame_system::Config> WeightInfo<T> {
 		// Proof Size summary in bytes:
 		//  Measured:  `0`
 		//  Estimated: `0`
-		// Minimum execution time: 1_834_000 picoseconds.
-		Weight::from_parts(1_892_000, 0)
+		// Minimum execution time: 999_000 picoseconds.
+		Weight::from_parts(1_035_000, 0)
 	}
 	// Storage: `PolkadotXcm::VersionNotifyTargets` (r:1 w:1)
 	// Proof: `PolkadotXcm::VersionNotifyTargets` (`max_values`: None, `max_size`: None, mode: `Measured`)
@@ -200,8 +200,8 @@ impl<T: frame_system::Config> WeightInfo<T> {
 		// Proof Size summary in bytes:
 		//  Measured:  `38`
 		//  Estimated: `3503`
-		// Minimum execution time: 22_238_000 picoseconds.
-		Weight::from_parts(22_690_000, 3503)
+		// Minimum execution time: 20_313_000 picoseconds.
+		Weight::from_parts(21_000_000, 3503)
 			.saturating_add(T::DbWeight::get().reads(7))
 			.saturating_add(T::DbWeight::get().writes(3))
 	}
@@ -211,44 +211,44 @@ impl<T: frame_system::Config> WeightInfo<T> {
 		// Proof Size summary in bytes:
 		//  Measured:  `0`
 		//  Estimated: `0`
-		// Minimum execution time: 3_798_000 picoseconds.
-		Weight::from_parts(3_936_000, 0)
+		// Minimum execution time: 2_820_000 picoseconds.
+		Weight::from_parts(2_949_000, 0)
 			.saturating_add(T::DbWeight::get().writes(1))
 	}
 	pub fn burn_asset() -> Weight {
 		// Proof Size summary in bytes:
 		//  Measured:  `0`
 		//  Estimated: `0`
-		// Minimum execution time: 2_985_000 picoseconds.
-		Weight::from_parts(3_099_000, 0)
+		// Minimum execution time: 1_293_000 picoseconds.
+		Weight::from_parts(1_354_000, 0)
 	}
 	pub fn expect_asset() -> Weight {
 		// Proof Size summary in bytes:
 		//  Measured:  `0`
 		//  Estimated: `0`
-		// Minimum execution time: 1_955_000 picoseconds.
-		Weight::from_parts(2_050_000, 0)
+		// Minimum execution time: 1_076_000 picoseconds.
+		Weight::from_parts(1_114_000, 0)
 	}
 	pub fn expect_origin() -> Weight {
 		// Proof Size summary in bytes:
 		//  Measured:  `0`
 		//  Estimated: `0`
-		// Minimum execution time: 1_939_000 picoseconds.
-		Weight::from_parts(1_990_000, 0)
+		// Minimum execution time: 1_014_000 picoseconds.
+		Weight::from_parts(1_055_000, 0)
 	}
 	pub fn expect_error() -> Weight {
 		// Proof Size summary in bytes:
 		//  Measured:  `0`
 		//  Estimated: `0`
-		// Minimum execution time: 1_841_000 picoseconds.
-		Weight::from_parts(1_900_000, 0)
+		// Minimum execution time: 979_000 picoseconds.
+		Weight::from_parts(1_019_000, 0)
 	}
 	pub fn expect_transact_status() -> Weight {
 		// Proof Size summary in bytes:
 		//  Measured:  `0`
 		//  Estimated: `0`
-		// Minimum execution time: 2_081_000 picoseconds.
-		Weight::from_parts(2_145_000, 0)
+		// Minimum execution time: 1_161_000 picoseconds.
+		Weight::from_parts(1_208_000, 0)
 	}
 	// Storage: `ParachainInfo::ParachainId` (r:1 w:0)
 	// Proof: `ParachainInfo::ParachainId` (`max_values`: Some(1), `max_size`: Some(4), added: 499, mode: `MaxEncodedLen`)
@@ -270,8 +270,8 @@ impl<T: frame_system::Config> WeightInfo<T> {
 		// Proof Size summary in bytes:
 		//  Measured:  `171`
 		//  Estimated: `6196`
-		// Minimum execution time: 59_600_000 picoseconds.
-		Weight::from_parts(61_572_000, 6196)
+		// Minimum execution time: 62_250_000 picoseconds.
+		Weight::from_parts(64_477_000, 6196)
 			.saturating_add(T::DbWeight::get().reads(9))
 			.saturating_add(T::DbWeight::get().writes(4))
 	}
@@ -279,8 +279,8 @@ impl<T: frame_system::Config> WeightInfo<T> {
 		// Proof Size summary in bytes:
 		//  Measured:  `0`
 		//  Estimated: `0`
-		// Minimum execution time: 4_390_000 picoseconds.
-		Weight::from_parts(4_517_000, 0)
+		// Minimum execution time: 4_286_000 picoseconds.
+		Weight::from_parts(4_476_000, 0)
 	}
 	// Storage: `ParachainInfo::ParachainId` (r:1 w:0)
 	// Proof: `ParachainInfo::ParachainId` (`max_values`: Some(1), `max_size`: Some(4), added: 499, mode: `MaxEncodedLen`)
@@ -302,8 +302,8 @@ impl<T: frame_system::Config> WeightInfo<T> {
 		// Proof Size summary in bytes:
 		//  Measured:  `171`
 		//  Estimated: `6196`
-		// Minimum execution time: 53_864_000 picoseconds.
-		Weight::from_parts(55_527_000, 6196)
+		// Minimum execution time: 58_253_000 picoseconds.
+		Weight::from_parts(59_360_000, 6196)
 			.saturating_add(T::DbWeight::get().reads(9))
 			.saturating_add(T::DbWeight::get().writes(4))
 	}
@@ -311,22 +311,22 @@ impl<T: frame_system::Config> WeightInfo<T> {
 		// Proof Size summary in bytes:
 		//  Measured:  `0`
 		//  Estimated: `0`
-		// Minimum execution time: 1_879_000 picoseconds.
-		Weight::from_parts(1_947_000, 0)
+		// Minimum execution time: 1_026_000 picoseconds.
+		Weight::from_parts(1_065_000, 0)
 	}
 	pub fn set_topic() -> Weight {
 		// Proof Size summary in bytes:
 		//  Measured:  `0`
 		//  Estimated: `0`
-		// Minimum execution time: 1_827_000 picoseconds.
-		Weight::from_parts(1_900_000, 0)
+		// Minimum execution time: 993_000 picoseconds.
+		Weight::from_parts(1_015_000, 0)
 	}
 	pub fn clear_topic() -> Weight {
 		// Proof Size summary in bytes:
 		//  Measured:  `0`
 		//  Estimated: `0`
-		// Minimum execution time: 1_824_000 picoseconds.
-		Weight::from_parts(1_898_000, 0)
+		// Minimum execution time: 966_000 picoseconds.
+		Weight::from_parts(999_000, 0)
 	}
 	// Storage: `ParachainInfo::ParachainId` (r:1 w:0)
 	// Proof: `ParachainInfo::ParachainId` (`max_values`: Some(1), `max_size`: Some(4), added: 499, mode: `MaxEncodedLen`)
@@ -339,16 +339,16 @@ impl<T: frame_system::Config> WeightInfo<T> {
 	// Storage: `BridgeWestendMessages::OutboundLanesCongestedSignals` (r:1 w:0)
 	// Proof: `BridgeWestendMessages::OutboundLanesCongestedSignals` (`max_values`: Some(1), `max_size`: Some(21), added: 516, mode: `MaxEncodedLen`)
 	// Storage: `BridgeWestendMessages::OutboundMessages` (r:0 w:1)
-	// Proof: `BridgeWestendMessages::OutboundMessages` (`max_values`: None, `max_size`: Some(2621472), added: 2623947, mode: `MaxEncodedLen`)
+	// Proof: `BridgeWestendMessages::OutboundMessages` (`max_values`: None, `max_size`: Some(65568), added: 68043, mode: `MaxEncodedLen`)
 	/// The range of component `x` is `[1, 1000]`.
 	pub fn export_message(x: u32, ) -> Weight {
 		// Proof Size summary in bytes:
 		//  Measured:  `190`
 		//  Estimated: `6130`
-		// Minimum execution time: 41_598_000 picoseconds.
-		Weight::from_parts(42_219_173, 6130)
-			// Standard Error: 426
-			.saturating_add(Weight::from_parts(452_460, 0).saturating_mul(x.into()))
+		// Minimum execution time: 37_014_000 picoseconds.
+		Weight::from_parts(38_096_655, 6130)
+			// Standard Error: 61
+			.saturating_add(Weight::from_parts(45_146, 0).saturating_mul(x.into()))
 			.saturating_add(T::DbWeight::get().reads(6))
 			.saturating_add(T::DbWeight::get().writes(2))
 	}
@@ -356,14 +356,14 @@ impl<T: frame_system::Config> WeightInfo<T> {
 		// Proof Size summary in bytes:
 		//  Measured:  `0`
 		//  Estimated: `0`
-		// Minimum execution time: 1_812_000 picoseconds.
-		Weight::from_parts(1_898_000, 0)
+		// Minimum execution time: 996_000 picoseconds.
+		Weight::from_parts(1_025_000, 0)
 	}
 	pub fn unpaid_execution() -> Weight {
 		// Proof Size summary in bytes:
 		//  Measured:  `0`
 		//  Estimated: `0`
-		// Minimum execution time: 1_915_000 picoseconds.
-		Weight::from_parts(1_976_000, 0)
+		// Minimum execution time: 1_001_000 picoseconds.
+		Weight::from_parts(1_044_000, 0)
 	}
 }
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 b309232825d..1d3d9e55f7e 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
@@ -148,8 +148,7 @@ mod bridge_hub_westend_tests {
 	use bridge_hub_test_utils::test_cases::from_parachain;
 	use bridge_to_westend_config::{
 		BridgeHubWestendChainId, BridgeHubWestendLocation, WestendGlobalConsensusNetwork,
-		WithBridgeHubWestendMessageBridge, WithBridgeHubWestendMessagesInstance,
-		XCM_LANE_FOR_ASSET_HUB_ROCOCO_TO_ASSET_HUB_WESTEND,
+		WithBridgeHubWestendMessagesInstance, XCM_LANE_FOR_ASSET_HUB_ROCOCO_TO_ASSET_HUB_WESTEND,
 	};
 
 	// Para id of sibling chain used in tests.
@@ -162,7 +161,6 @@ mod bridge_hub_westend_tests {
 		BridgeGrandpaWestendInstance,
 		BridgeParachainWestendInstance,
 		WithBridgeHubWestendMessagesInstance,
-		WithBridgeHubWestendMessageBridge,
 	>;
 
 	#[test]
@@ -457,8 +455,8 @@ mod bridge_hub_bulletin_tests {
 	use bridge_hub_test_utils::test_cases::from_grandpa_chain;
 	use bridge_to_bulletin_config::{
 		RococoBulletinChainId, RococoBulletinGlobalConsensusNetwork,
-		RococoBulletinGlobalConsensusNetworkLocation, WithRococoBulletinMessageBridge,
-		WithRococoBulletinMessagesInstance, XCM_LANE_FOR_ROCOCO_PEOPLE_TO_ROCOCO_BULLETIN,
+		RococoBulletinGlobalConsensusNetworkLocation, WithRococoBulletinMessagesInstance,
+		XCM_LANE_FOR_ROCOCO_PEOPLE_TO_ROCOCO_BULLETIN,
 	};
 
 	// Para id of sibling chain used in tests.
@@ -470,7 +468,6 @@ mod bridge_hub_bulletin_tests {
 		AllPalletsWithoutSystem,
 		BridgeGrandpaRococoBulletinInstance,
 		WithRococoBulletinMessagesInstance,
-		WithRococoBulletinMessageBridge,
 	>;
 
 	#[test]
@@ -594,44 +591,4 @@ mod bridge_hub_bulletin_tests {
 			construct_and_apply_extrinsic,
 		)
 	}
-
-	#[test]
-	pub fn can_calculate_fee_for_standalone_message_delivery_transaction() {
-		bridge_hub_test_utils::check_sane_fees_values(
-			"bp_bridge_hub_rococo::BridgeHubRococoBaseDeliveryFeeInRocs",
-			bp_bridge_hub_rococo::BridgeHubRococoBaseDeliveryFeeInRocs::get(),
-			|| {
-				from_grandpa_chain::can_calculate_fee_for_standalone_message_delivery_transaction::<
-					RuntimeTestsAdapter,
-				>(collator_session_keys(), construct_and_estimate_extrinsic_fee)
-			},
-			Perbill::from_percent(33),
-			None, /* we don't want lowering according to the Bulletin setup, because
-			       * `from_grandpa_chain` is cheaper then `from_parachain_chain` */
-			&format!(
-				"Estimate fee for `single message delivery` for runtime: {:?}",
-				<Runtime as frame_system::Config>::Version::get()
-			),
-		)
-	}
-
-	#[test]
-	pub fn can_calculate_fee_for_standalone_message_confirmation_transaction() {
-		bridge_hub_test_utils::check_sane_fees_values(
-			"bp_bridge_hub_rococo::BridgeHubRococoBaseConfirmationFeeInRocs",
-			bp_bridge_hub_rococo::BridgeHubRococoBaseConfirmationFeeInRocs::get(),
-			|| {
-				from_grandpa_chain::can_calculate_fee_for_standalone_message_confirmation_transaction::<
-					RuntimeTestsAdapter,
-				>(collator_session_keys(), construct_and_estimate_extrinsic_fee)
-			},
-			Perbill::from_percent(33),
-			None, /* we don't want lowering according to the Bulletin setup, because
-			       * `from_grandpa_chain` is cheaper then `from_parachain_chain` */
-			&format!(
-				"Estimate fee for `single message confirmation` for runtime: {:?}",
-				<Runtime as frame_system::Config>::Version::get()
-			),
-		)
-	}
 }
diff --git a/cumulus/parachains/runtimes/bridge-hubs/bridge-hub-westend/Cargo.toml b/cumulus/parachains/runtimes/bridge-hubs/bridge-hub-westend/Cargo.toml
index c18d5036e06..e2671d3d606 100644
--- a/cumulus/parachains/runtimes/bridge-hubs/bridge-hub-westend/Cargo.toml
+++ b/cumulus/parachains/runtimes/bridge-hubs/bridge-hub-westend/Cargo.toml
@@ -89,7 +89,7 @@ bp-messages = { workspace = true }
 bp-parachains = { workspace = true }
 bp-polkadot-core = { workspace = true }
 bp-relayers = { workspace = true }
-bp-runtime = { workspace = true }
+bp-runtime = { features = ["test-helpers"], workspace = true }
 bp-rococo = { workspace = true }
 bp-westend = { workspace = true }
 pallet-bridge-grandpa = { workspace = true }
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 425b53da30f..09d55f4323a 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
@@ -18,10 +18,12 @@
 
 use crate::{
 	bridge_common_config::DeliveryRewardInBalance, weights, xcm_config::UniversalLocation,
-	AccountId, BridgeRococoMessages, PolkadotXcm, Runtime, RuntimeEvent, RuntimeOrigin,
-	XcmOverBridgeHubRococo, XcmRouter,
+	BridgeRococoMessages, PolkadotXcm, Runtime, RuntimeEvent, XcmOverBridgeHubRococo, XcmRouter,
+};
+use bp_messages::{
+	source_chain::FromBridgedChainMessagesDeliveryProof,
+	target_chain::FromBridgedChainMessagesProof, LaneId,
 };
-use bp_messages::LaneId;
 use bp_parachains::SingleParaStoredHeaderDataBuilder;
 use bp_runtime::Chain;
 use bridge_runtime_common::{
@@ -29,12 +31,6 @@ use bridge_runtime_common::{
 		ActualFeeRefund, RefundBridgedMessages, RefundSignedExtensionAdapter,
 		RefundableMessagesLane,
 	},
-	messages,
-	messages::{
-		source::{FromBridgedChainMessagesDeliveryProof, TargetHeaderChainAdapter},
-		target::{FromBridgedChainMessagesProof, SourceHeaderChainAdapter},
-		MessageBridge, ThisChainWithMessages, UnderlyingChainProvider,
-	},
 	messages_xcm_extension::{
 		SenderAndLane, XcmAsPlainPayload, XcmBlobHauler, XcmBlobHaulerAdapter,
 		XcmBlobMessageDispatch, XcmVersionOfDestAndRemoteBridge,
@@ -45,7 +41,6 @@ use frame_support::{
 	parameter_types,
 	traits::{ConstU32, PalletInfoAccess},
 };
-use sp_runtime::RuntimeDebug;
 use xcm::{
 	latest::prelude::*,
 	prelude::{InteriorLocation, NetworkId},
@@ -59,11 +54,7 @@ parameter_types! {
 	pub const RococoBridgeParachainPalletName: &'static str = "Paras";
 	pub const MaxRococoParaHeadDataSize: u32 = bp_rococo::MAX_NESTED_PARACHAIN_HEAD_DATA_SIZE;
 
-	pub const MaxUnrewardedRelayerEntriesAtInboundLane: bp_messages::MessageNonce =
-		bp_bridge_hub_westend::MAX_UNREWARDED_RELAYERS_IN_CONFIRMATION_TX;
-	pub const MaxUnconfirmedMessagesAtInboundLane: bp_messages::MessageNonce =
-		bp_bridge_hub_westend::MAX_UNCONFIRMED_MESSAGES_IN_CONFIRMATION_TX;
-	pub const BridgeHubRococoChainId: bp_runtime::ChainId = BridgeHubRococo::ID;
+	pub const BridgeHubRococoChainId: bp_runtime::ChainId = bp_bridge_hub_rococo::BridgeHubRococo::ID;
 	pub BridgeWestendToRococoMessagesPalletInstance: InteriorLocation = [PalletInstance(<BridgeRococoMessages as PalletInfoAccess>::index() as u8)].into();
 	pub RococoGlobalConsensusNetwork: NetworkId = NetworkId::Rococo;
 	pub RococoGlobalConsensusNetworkLocation: Location = Location::new(
@@ -153,46 +144,6 @@ impl XcmBlobHauler for ToBridgeHubRococoXcmBlobHauler {
 /// On messages delivered callback.
 type OnMessagesDelivered = XcmBlobHaulerAdapter<ToBridgeHubRococoXcmBlobHauler, ActiveLanes>;
 
-/// Messaging Bridge configuration for BridgeHubWestend -> BridgeHubRococo
-pub struct WithBridgeHubRococoMessageBridge;
-impl MessageBridge for WithBridgeHubRococoMessageBridge {
-	const BRIDGED_MESSAGES_PALLET_NAME: &'static str =
-		bp_bridge_hub_westend::WITH_BRIDGE_HUB_WESTEND_MESSAGES_PALLET_NAME;
-	type ThisChain = BridgeHubWestend;
-	type BridgedChain = BridgeHubRococo;
-	type BridgedHeaderChain = pallet_bridge_parachains::ParachainHeaders<
-		Runtime,
-		BridgeParachainRococoInstance,
-		bp_bridge_hub_rococo::BridgeHubRococo,
-	>;
-}
-
-/// Maximal outbound payload size of BridgeHubWestend -> BridgeHubRococo messages.
-type ToBridgeHubRococoMaximalOutboundPayloadSize =
-	messages::source::FromThisChainMaximalOutboundPayloadSize<WithBridgeHubRococoMessageBridge>;
-
-/// BridgeHubRococo chain from message lane point of view.
-#[derive(RuntimeDebug, Clone, Copy)]
-pub struct BridgeHubRococo;
-
-impl UnderlyingChainProvider for BridgeHubRococo {
-	type Chain = bp_bridge_hub_rococo::BridgeHubRococo;
-}
-
-impl messages::BridgedChainWithMessages for BridgeHubRococo {}
-
-/// BridgeHubWestend chain from message lane point of view.
-#[derive(RuntimeDebug, Clone, Copy)]
-pub struct BridgeHubWestend;
-
-impl UnderlyingChainProvider for BridgeHubWestend {
-	type Chain = bp_bridge_hub_westend::BridgeHubWestend;
-}
-
-impl ThisChainWithMessages for BridgeHubWestend {
-	type RuntimeOrigin = RuntimeOrigin;
-}
-
 /// Signed extension that refunds relayers that are delivering messages from the Rococo parachain.
 pub type OnBridgeHubWestendRefundBridgeHubRococoMessages = RefundSignedExtensionAdapter<
 	RefundBridgedMessages<
@@ -237,26 +188,28 @@ pub type WithBridgeHubRococoMessagesInstance = pallet_bridge_messages::Instance1
 impl pallet_bridge_messages::Config<WithBridgeHubRococoMessagesInstance> for Runtime {
 	type RuntimeEvent = RuntimeEvent;
 	type WeightInfo = weights::pallet_bridge_messages::WeightInfo<Runtime>;
-	type BridgedChainId = BridgeHubRococoChainId;
+
+	type ThisChain = bp_bridge_hub_westend::BridgeHubWestend;
+	type BridgedChain = bp_bridge_hub_rococo::BridgeHubRococo;
+	type BridgedHeaderChain = pallet_bridge_parachains::ParachainHeaders<
+		Runtime,
+		BridgeParachainRococoInstance,
+		bp_bridge_hub_rococo::BridgeHubRococo,
+	>;
+
 	type ActiveOutboundLanes = ActiveOutboundLanesToBridgeHubRococo;
-	type MaxUnrewardedRelayerEntriesAtInboundLane = MaxUnrewardedRelayerEntriesAtInboundLane;
-	type MaxUnconfirmedMessagesAtInboundLane = MaxUnconfirmedMessagesAtInboundLane;
 
-	type MaximalOutboundPayloadSize = ToBridgeHubRococoMaximalOutboundPayloadSize;
 	type OutboundPayload = XcmAsPlainPayload;
 
 	type InboundPayload = XcmAsPlainPayload;
-	type InboundRelayer = AccountId;
 	type DeliveryPayments = ();
 
-	type TargetHeaderChain = TargetHeaderChainAdapter<WithBridgeHubRococoMessageBridge>;
 	type DeliveryConfirmationPayments = pallet_bridge_relayers::DeliveryConfirmationPaymentsAdapter<
 		Runtime,
 		WithBridgeHubRococoMessagesInstance,
 		DeliveryRewardInBalance,
 	>;
 
-	type SourceHeaderChain = SourceHeaderChainAdapter<WithBridgeHubRococoMessageBridge>;
 	type MessageDispatch = XcmBlobMessageDispatch<
 		FromRococoMessageBlobDispatcher,
 		Self::WeightInfo,
@@ -287,9 +240,8 @@ mod tests {
 		assert_complete_bridge_types,
 		extensions::refund_relayer_extension::RefundableParachain,
 		integrity::{
-			assert_complete_bridge_constants, check_message_lane_weights,
-			AssertBridgeMessagesPalletConstants, AssertBridgePalletNames, AssertChainConstants,
-			AssertCompleteBridgeConstants,
+			assert_complete_with_parachain_bridge_constants, check_message_lane_weights,
+			AssertChainConstants, AssertCompleteBridgeConstants,
 		},
 	};
 	use parachains_common::Balance;
@@ -331,35 +283,20 @@ mod tests {
 			runtime: Runtime,
 			with_bridged_chain_grandpa_instance: BridgeGrandpaRococoInstance,
 			with_bridged_chain_messages_instance: WithBridgeHubRococoMessagesInstance,
-			bridge: WithBridgeHubRococoMessageBridge,
-			this_chain: bp_westend::Westend,
-			bridged_chain: bp_rococo::Rococo,
+			this_chain: bp_bridge_hub_westend::BridgeHubWestend,
+			bridged_chain: bp_bridge_hub_rococo::BridgeHubRococo,
 		);
 
-		assert_complete_bridge_constants::<
+		assert_complete_with_parachain_bridge_constants::<
 			Runtime,
 			BridgeGrandpaRococoInstance,
 			WithBridgeHubRococoMessagesInstance,
-			WithBridgeHubRococoMessageBridge,
+			bp_rococo::Rococo,
 		>(AssertCompleteBridgeConstants {
 			this_chain_constants: AssertChainConstants {
 				block_length: bp_bridge_hub_westend::BlockLength::get(),
 				block_weights: bp_bridge_hub_westend::BlockWeightsForAsyncBacking::get(),
 			},
-			messages_pallet_constants: AssertBridgeMessagesPalletConstants {
-				max_unrewarded_relayers_in_bridged_confirmation_tx:
-					bp_bridge_hub_rococo::MAX_UNREWARDED_RELAYERS_IN_CONFIRMATION_TX,
-				max_unconfirmed_messages_in_bridged_confirmation_tx:
-					bp_bridge_hub_rococo::MAX_UNCONFIRMED_MESSAGES_IN_CONFIRMATION_TX,
-				bridged_chain_id: BridgeHubRococo::ID,
-			},
-			pallet_names: AssertBridgePalletNames {
-				with_this_chain_messages_pallet_name:
-					bp_bridge_hub_westend::WITH_BRIDGE_HUB_WESTEND_MESSAGES_PALLET_NAME,
-				with_bridged_chain_grandpa_pallet_name: bp_rococo::WITH_ROCOCO_GRANDPA_PALLET_NAME,
-				with_bridged_chain_messages_pallet_name:
-					bp_bridge_hub_rococo::WITH_BRIDGE_HUB_ROCOCO_MESSAGES_PALLET_NAME,
-			},
 		});
 
 		bridge_runtime_common::extensions::priority_calculator::per_relay_header::ensure_priority_boost_is_sane::<
@@ -370,7 +307,7 @@ mod tests {
 
 		bridge_runtime_common::extensions::priority_calculator::per_parachain_header::ensure_priority_boost_is_sane::<
 			Runtime,
-			RefundableParachain<WithBridgeHubRococoMessagesInstance, BridgeHubRococo>,
+			RefundableParachain<WithBridgeHubRococoMessagesInstance, bp_bridge_hub_rococo::BridgeHubRococo>,
 			PriorityBoostPerParachainHeader,
 		>(FEE_BOOST_PER_PARACHAIN_HEADER);
 
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 05d6cdfd691..993f6043245 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
@@ -1118,7 +1118,7 @@ impl_runtime_apis! {
 					prepare_message_proof_from_parachain::<
 						Runtime,
 						bridge_to_rococo_config::BridgeGrandpaRococoInstance,
-						bridge_to_rococo_config::WithBridgeHubRococoMessageBridge,
+						bridge_to_rococo_config::WithBridgeHubRococoMessagesInstance,
 					>(params, generate_xcm_builder_bridge_message_sample([GlobalConsensus(Westend), Parachain(42)].into()))
 				}
 
@@ -1128,7 +1128,7 @@ impl_runtime_apis! {
 					prepare_message_delivery_proof_from_parachain::<
 						Runtime,
 						bridge_to_rococo_config::BridgeGrandpaRococoInstance,
-						bridge_to_rococo_config::WithBridgeHubRococoMessageBridge,
+						bridge_to_rococo_config::WithBridgeHubRococoMessagesInstance,
 					>(params)
 				}
 
@@ -1154,7 +1154,7 @@ impl_runtime_apis! {
 				fn prepare_parachain_heads_proof(
 					parachains: &[bp_polkadot_core::parachains::ParaId],
 					parachain_head_size: u32,
-					proof_size: bp_runtime::StorageProofSize,
+					proof_params: bp_runtime::UnverifiedStorageProofParams,
 				) -> (
 					pallet_bridge_parachains::RelayBlockNumber,
 					pallet_bridge_parachains::RelayBlockHash,
@@ -1164,7 +1164,7 @@ impl_runtime_apis! {
 					prepare_parachain_heads_proof::<Runtime, bridge_to_rococo_config::BridgeParachainRococoInstance>(
 						parachains,
 						parachain_head_size,
-						proof_size,
+						proof_params,
 					)
 				}
 			}
diff --git a/cumulus/parachains/runtimes/bridge-hubs/bridge-hub-westend/src/weights/pallet_bridge_grandpa.rs b/cumulus/parachains/runtimes/bridge-hubs/bridge-hub-westend/src/weights/pallet_bridge_grandpa.rs
index a9ccb00b1a1..fa7efc26048 100644
--- a/cumulus/parachains/runtimes/bridge-hubs/bridge-hub-westend/src/weights/pallet_bridge_grandpa.rs
+++ b/cumulus/parachains/runtimes/bridge-hubs/bridge-hub-westend/src/weights/pallet_bridge_grandpa.rs
@@ -17,9 +17,9 @@
 //! Autogenerated weights for `pallet_bridge_grandpa`
 //!
 //! THIS FILE WAS AUTO-GENERATED USING THE SUBSTRATE BENCHMARK CLI VERSION 32.0.0
-//! DATE: 2024-06-26, STEPS: `50`, REPEAT: `20`, LOW RANGE: `[]`, HIGH RANGE: `[]`
+//! DATE: 2024-07-11, STEPS: `50`, REPEAT: `20`, LOW RANGE: `[]`, HIGH RANGE: `[]`
 //! WORST CASE MAP SIZE: `1000000`
-//! HOSTNAME: `runner-7wrmsoux-project-674-concurrent-0`, CPU: `Intel(R) Xeon(R) CPU @ 2.60GHz`
+//! HOSTNAME: `runner-yaoqqom-project-674-concurrent-0`, CPU: `Intel(R) Xeon(R) CPU @ 2.60GHz`
 //! WASM-EXECUTION: `Compiled`, CHAIN: `Some("bridge-hub-westend-dev")`, DB CACHE: 1024
 
 // Executed Command:
@@ -68,13 +68,13 @@ impl<T: frame_system::Config> pallet_bridge_grandpa::WeightInfo for WeightInfo<T
 		// Proof Size summary in bytes:
 		//  Measured:  `268 + p * (60 ±0)`
 		//  Estimated: `51735`
-		// Minimum execution time: 290_999_000 picoseconds.
-		Weight::from_parts(8_417_887, 0)
+		// Minimum execution time: 294_381_000 picoseconds.
+		Weight::from_parts(21_868_057, 0)
 			.saturating_add(Weight::from_parts(0, 51735))
-			// Standard Error: 15_256
-			.saturating_add(Weight::from_parts(41_159_168, 0).saturating_mul(p.into()))
-			// Standard Error: 50_907
-			.saturating_add(Weight::from_parts(2_560_033, 0).saturating_mul(v.into()))
+			// Standard Error: 14_649
+			.saturating_add(Weight::from_parts(40_681_012, 0).saturating_mul(p.into()))
+			// Standard Error: 48_883
+			.saturating_add(Weight::from_parts(2_466_672, 0).saturating_mul(v.into()))
 			.saturating_add(T::DbWeight::get().reads(6))
 			.saturating_add(T::DbWeight::get().writes(5))
 	}
@@ -92,8 +92,8 @@ impl<T: frame_system::Config> pallet_bridge_grandpa::WeightInfo for WeightInfo<T
 		// Proof Size summary in bytes:
 		//  Measured:  `282`
 		//  Estimated: `51735`
-		// Minimum execution time: 116_445_000 picoseconds.
-		Weight::from_parts(129_204_000, 0)
+		// Minimum execution time: 96_441_000 picoseconds.
+		Weight::from_parts(110_957_000, 0)
 			.saturating_add(Weight::from_parts(0, 51735))
 			.saturating_add(T::DbWeight::get().reads(3))
 			.saturating_add(T::DbWeight::get().writes(6))
diff --git a/cumulus/parachains/runtimes/bridge-hubs/bridge-hub-westend/src/weights/pallet_bridge_messages.rs b/cumulus/parachains/runtimes/bridge-hubs/bridge-hub-westend/src/weights/pallet_bridge_messages.rs
index 2fbce4231f3..386342d7ea5 100644
--- a/cumulus/parachains/runtimes/bridge-hubs/bridge-hub-westend/src/weights/pallet_bridge_messages.rs
+++ b/cumulus/parachains/runtimes/bridge-hubs/bridge-hub-westend/src/weights/pallet_bridge_messages.rs
@@ -17,7 +17,7 @@
 //! Autogenerated weights for `pallet_bridge_messages`
 //!
 //! THIS FILE WAS AUTO-GENERATED USING THE SUBSTRATE BENCHMARK CLI VERSION 32.0.0
-//! DATE: 2024-06-26, STEPS: `50`, REPEAT: `20`, LOW RANGE: `[]`, HIGH RANGE: `[]`
+//! DATE: 2024-07-03, STEPS: `50`, REPEAT: `20`, LOW RANGE: `[]`, HIGH RANGE: `[]`
 //! WORST CASE MAP SIZE: `1000000`
 //! HOSTNAME: `runner-7wrmsoux-project-674-concurrent-0`, CPU: `Intel(R) Xeon(R) CPU @ 2.60GHz`
 //! WASM-EXECUTION: `Compiled`, CHAIN: `Some("bridge-hub-westend-dev")`, DB CACHE: 1024
@@ -62,8 +62,8 @@ impl<T: frame_system::Config> pallet_bridge_messages::WeightInfo for WeightInfo<
 		// Proof Size summary in bytes:
 		//  Measured:  `522`
 		//  Estimated: `52645`
-		// Minimum execution time: 41_095_000 picoseconds.
-		Weight::from_parts(42_438_000, 0)
+		// Minimum execution time: 40_748_000 picoseconds.
+		Weight::from_parts(41_836_000, 0)
 			.saturating_add(Weight::from_parts(0, 52645))
 			.saturating_add(T::DbWeight::get().reads(5))
 			.saturating_add(T::DbWeight::get().writes(1))
@@ -78,13 +78,16 @@ impl<T: frame_system::Config> pallet_bridge_messages::WeightInfo for WeightInfo<
 	/// Proof: `BridgeRococoMessages::InboundLanes` (`max_values`: None, `max_size`: Some(49180), added: 51655, mode: `MaxEncodedLen`)
 	/// Storage: `ParachainInfo::ParachainId` (r:1 w:0)
 	/// Proof: `ParachainInfo::ParachainId` (`max_values`: Some(1), `max_size`: Some(4), added: 499, mode: `MaxEncodedLen`)
-	fn receive_two_messages_proof() -> Weight {
+	/// The range of component `n` is `[1, 4076]`.
+	fn receive_n_messages_proof(n: u32, ) -> Weight {
 		// Proof Size summary in bytes:
 		//  Measured:  `522`
 		//  Estimated: `52645`
-		// Minimum execution time: 51_974_000 picoseconds.
-		Weight::from_parts(53_444_000, 0)
+		// Minimum execution time: 40_923_000 picoseconds.
+		Weight::from_parts(41_287_000, 0)
 			.saturating_add(Weight::from_parts(0, 52645))
+			// Standard Error: 9_774
+			.saturating_add(Weight::from_parts(11_469_207, 0).saturating_mul(n.into()))
 			.saturating_add(T::DbWeight::get().reads(5))
 			.saturating_add(T::DbWeight::get().writes(1))
 	}
@@ -102,8 +105,8 @@ impl<T: frame_system::Config> pallet_bridge_messages::WeightInfo for WeightInfo<
 		// Proof Size summary in bytes:
 		//  Measured:  `522`
 		//  Estimated: `52645`
-		// Minimum execution time: 45_949_000 picoseconds.
-		Weight::from_parts(47_912_000, 0)
+		// Minimum execution time: 45_946_000 picoseconds.
+		Weight::from_parts(47_547_000, 0)
 			.saturating_add(Weight::from_parts(0, 52645))
 			.saturating_add(T::DbWeight::get().reads(5))
 			.saturating_add(T::DbWeight::get().writes(1))
@@ -116,32 +119,19 @@ impl<T: frame_system::Config> pallet_bridge_messages::WeightInfo for WeightInfo<
 	/// Proof: `BridgeRococoParachains::ImportedParaHeads` (`max_values`: Some(64), `max_size`: Some(196), added: 1186, mode: `MaxEncodedLen`)
 	/// Storage: `BridgeRococoMessages::InboundLanes` (r:1 w:1)
 	/// Proof: `BridgeRococoMessages::InboundLanes` (`max_values`: None, `max_size`: Some(49180), added: 51655, mode: `MaxEncodedLen`)
-	fn receive_single_message_proof_1_kb() -> Weight {
-		// Proof Size summary in bytes:
-		//  Measured:  `453`
-		//  Estimated: `52645`
-		// Minimum execution time: 39_349_000 picoseconds.
-		Weight::from_parts(40_723_000, 0)
-			.saturating_add(Weight::from_parts(0, 52645))
-			.saturating_add(T::DbWeight::get().reads(4))
-			.saturating_add(T::DbWeight::get().writes(1))
-	}
-	/// Storage: `BridgeRococoMessages::PalletOperatingMode` (r:1 w:0)
-	/// Proof: `BridgeRococoMessages::PalletOperatingMode` (`max_values`: Some(1), `max_size`: Some(2), added: 497, mode: `MaxEncodedLen`)
-	/// Storage: `XcmpQueue::OutboundXcmpStatus` (r:1 w:0)
-	/// Proof: `XcmpQueue::OutboundXcmpStatus` (`max_values`: Some(1), `max_size`: Some(1282), added: 1777, mode: `MaxEncodedLen`)
-	/// Storage: `BridgeRococoParachains::ImportedParaHeads` (r:1 w:0)
-	/// Proof: `BridgeRococoParachains::ImportedParaHeads` (`max_values`: Some(64), `max_size`: Some(196), added: 1186, mode: `MaxEncodedLen`)
-	/// Storage: `BridgeRococoMessages::InboundLanes` (r:1 w:1)
-	/// Proof: `BridgeRococoMessages::InboundLanes` (`max_values`: None, `max_size`: Some(49180), added: 51655, mode: `MaxEncodedLen`)
-	fn receive_single_message_proof_16_kb() -> Weight {
+	/// Storage: `ParachainInfo::ParachainId` (r:1 w:0)
+	/// Proof: `ParachainInfo::ParachainId` (`max_values`: Some(1), `max_size`: Some(4), added: 499, mode: `MaxEncodedLen`)
+	/// The range of component `n` is `[1, 16384]`.
+	fn receive_single_n_bytes_message_proof(n: u32, ) -> Weight {
 		// Proof Size summary in bytes:
-		//  Measured:  `453`
+		//  Measured:  `522`
 		//  Estimated: `52645`
-		// Minimum execution time: 70_566_000 picoseconds.
-		Weight::from_parts(73_240_000, 0)
+		// Minimum execution time: 39_668_000 picoseconds.
+		Weight::from_parts(41_908_980, 0)
 			.saturating_add(Weight::from_parts(0, 52645))
-			.saturating_add(T::DbWeight::get().reads(4))
+			// Standard Error: 11
+			.saturating_add(Weight::from_parts(2_209, 0).saturating_mul(n.into()))
+			.saturating_add(T::DbWeight::get().reads(5))
 			.saturating_add(T::DbWeight::get().writes(1))
 	}
 	/// Storage: `BridgeRococoMessages::PalletOperatingMode` (r:1 w:0)
@@ -158,8 +148,8 @@ impl<T: frame_system::Config> pallet_bridge_messages::WeightInfo for WeightInfo<
 		// Proof Size summary in bytes:
 		//  Measured:  `357`
 		//  Estimated: `3822`
-		// Minimum execution time: 31_381_000 picoseconds.
-		Weight::from_parts(31_980_000, 0)
+		// Minimum execution time: 30_544_000 picoseconds.
+		Weight::from_parts(31_171_000, 0)
 			.saturating_add(Weight::from_parts(0, 3822))
 			.saturating_add(T::DbWeight::get().reads(5))
 			.saturating_add(T::DbWeight::get().writes(2))
@@ -178,8 +168,8 @@ impl<T: frame_system::Config> pallet_bridge_messages::WeightInfo for WeightInfo<
 		// Proof Size summary in bytes:
 		//  Measured:  `357`
 		//  Estimated: `3822`
-		// Minimum execution time: 31_479_000 picoseconds.
-		Weight::from_parts(32_220_000, 0)
+		// Minimum execution time: 30_593_000 picoseconds.
+		Weight::from_parts(31_261_000, 0)
 			.saturating_add(Weight::from_parts(0, 3822))
 			.saturating_add(T::DbWeight::get().reads(5))
 			.saturating_add(T::DbWeight::get().writes(2))
@@ -198,8 +188,8 @@ impl<T: frame_system::Config> pallet_bridge_messages::WeightInfo for WeightInfo<
 		// Proof Size summary in bytes:
 		//  Measured:  `357`
 		//  Estimated: `6086`
-		// Minimum execution time: 35_724_000 picoseconds.
-		Weight::from_parts(36_460_000, 0)
+		// Minimum execution time: 34_682_000 picoseconds.
+		Weight::from_parts(35_277_000, 0)
 			.saturating_add(Weight::from_parts(0, 6086))
 			.saturating_add(T::DbWeight::get().reads(6))
 			.saturating_add(T::DbWeight::get().writes(3))
@@ -226,16 +216,16 @@ impl<T: frame_system::Config> pallet_bridge_messages::WeightInfo for WeightInfo<
 	/// Proof: `ParachainSystem::RelevantMessagingState` (`max_values`: Some(1), `max_size`: None, mode: `Measured`)
 	/// Storage: `XcmpQueue::OutboundXcmpMessages` (r:0 w:1)
 	/// Proof: `XcmpQueue::OutboundXcmpMessages` (`max_values`: None, `max_size`: Some(105506), added: 107981, mode: `MaxEncodedLen`)
-	/// The range of component `i` is `[128, 2048]`.
-	fn receive_single_message_proof_with_dispatch(i: u32, ) -> Weight {
+	/// The range of component `n` is `[1, 16384]`.
+	fn receive_single_n_bytes_message_proof_with_dispatch(n: u32, ) -> Weight {
 		// Proof Size summary in bytes:
 		//  Measured:  `653`
 		//  Estimated: `52645`
-		// Minimum execution time: 60_476_000 picoseconds.
-		Weight::from_parts(61_832_928, 0)
+		// Minimum execution time: 56_465_000 picoseconds.
+		Weight::from_parts(61_575_775, 0)
 			.saturating_add(Weight::from_parts(0, 52645))
-			// Standard Error: 73
-			.saturating_add(Weight::from_parts(7_951, 0).saturating_mul(i.into()))
+			// Standard Error: 15
+			.saturating_add(Weight::from_parts(7_197, 0).saturating_mul(n.into()))
 			.saturating_add(T::DbWeight::get().reads(10))
 			.saturating_add(T::DbWeight::get().writes(4))
 	}
diff --git a/cumulus/parachains/runtimes/bridge-hubs/bridge-hub-westend/src/weights/pallet_bridge_parachains.rs b/cumulus/parachains/runtimes/bridge-hubs/bridge-hub-westend/src/weights/pallet_bridge_parachains.rs
index 82764a48b48..b4748f14170 100644
--- a/cumulus/parachains/runtimes/bridge-hubs/bridge-hub-westend/src/weights/pallet_bridge_parachains.rs
+++ b/cumulus/parachains/runtimes/bridge-hubs/bridge-hub-westend/src/weights/pallet_bridge_parachains.rs
@@ -17,7 +17,7 @@
 //! Autogenerated weights for `pallet_bridge_parachains`
 //!
 //! THIS FILE WAS AUTO-GENERATED USING THE SUBSTRATE BENCHMARK CLI VERSION 32.0.0
-//! DATE: 2024-06-26, STEPS: `50`, REPEAT: `20`, LOW RANGE: `[]`, HIGH RANGE: `[]`
+//! DATE: 2024-07-03, STEPS: `50`, REPEAT: `20`, LOW RANGE: `[]`, HIGH RANGE: `[]`
 //! WORST CASE MAP SIZE: `1000000`
 //! HOSTNAME: `runner-7wrmsoux-project-674-concurrent-0`, CPU: `Intel(R) Xeon(R) CPU @ 2.60GHz`
 //! WASM-EXECUTION: `Compiled`, CHAIN: `Some("bridge-hub-westend-dev")`, DB CACHE: 1024
@@ -61,15 +61,13 @@ impl<T: frame_system::Config> pallet_bridge_parachains::WeightInfo for WeightInf
 	/// Storage: `BridgeRococoParachains::ImportedParaHeads` (r:0 w:1)
 	/// Proof: `BridgeRococoParachains::ImportedParaHeads` (`max_values`: Some(64), `max_size`: Some(196), added: 1186, mode: `MaxEncodedLen`)
 	/// The range of component `p` is `[1, 2]`.
-	fn submit_parachain_heads_with_n_parachains(p: u32, ) -> Weight {
+	fn submit_parachain_heads_with_n_parachains(_p: u32, ) -> Weight {
 		// Proof Size summary in bytes:
 		//  Measured:  `315`
 		//  Estimated: `2543`
-		// Minimum execution time: 34_408_000 picoseconds.
-		Weight::from_parts(35_467_881, 0)
+		// Minimum execution time: 34_177_000 picoseconds.
+		Weight::from_parts(35_662_308, 0)
 			.saturating_add(Weight::from_parts(0, 2543))
-			// Standard Error: 100_493
-			.saturating_add(Weight::from_parts(63_859, 0).saturating_mul(p.into()))
 			.saturating_add(T::DbWeight::get().reads(5))
 			.saturating_add(T::DbWeight::get().writes(4))
 	}
@@ -89,8 +87,8 @@ impl<T: frame_system::Config> pallet_bridge_parachains::WeightInfo for WeightInf
 		// Proof Size summary in bytes:
 		//  Measured:  `315`
 		//  Estimated: `2543`
-		// Minimum execution time: 36_125_000 picoseconds.
-		Weight::from_parts(36_703_000, 0)
+		// Minimum execution time: 35_975_000 picoseconds.
+		Weight::from_parts(36_510_000, 0)
 			.saturating_add(Weight::from_parts(0, 2543))
 			.saturating_add(T::DbWeight::get().reads(5))
 			.saturating_add(T::DbWeight::get().writes(4))
@@ -111,8 +109,8 @@ impl<T: frame_system::Config> pallet_bridge_parachains::WeightInfo for WeightInf
 		// Proof Size summary in bytes:
 		//  Measured:  `315`
 		//  Estimated: `2543`
-		// Minimum execution time: 64_697_000 picoseconds.
-		Weight::from_parts(68_015_000, 0)
+		// Minimum execution time: 62_837_000 picoseconds.
+		Weight::from_parts(63_562_000, 0)
 			.saturating_add(Weight::from_parts(0, 2543))
 			.saturating_add(T::DbWeight::get().reads(5))
 			.saturating_add(T::DbWeight::get().writes(4))
diff --git a/cumulus/parachains/runtimes/bridge-hubs/bridge-hub-westend/src/weights/pallet_bridge_relayers.rs b/cumulus/parachains/runtimes/bridge-hubs/bridge-hub-westend/src/weights/pallet_bridge_relayers.rs
index 7e3f3f3d723..60d81dc3082 100644
--- a/cumulus/parachains/runtimes/bridge-hubs/bridge-hub-westend/src/weights/pallet_bridge_relayers.rs
+++ b/cumulus/parachains/runtimes/bridge-hubs/bridge-hub-westend/src/weights/pallet_bridge_relayers.rs
@@ -17,7 +17,7 @@
 //! Autogenerated weights for `pallet_bridge_relayers`
 //!
 //! THIS FILE WAS AUTO-GENERATED USING THE SUBSTRATE BENCHMARK CLI VERSION 32.0.0
-//! DATE: 2024-06-26, STEPS: `50`, REPEAT: `20`, LOW RANGE: `[]`, HIGH RANGE: `[]`
+//! DATE: 2024-07-03, STEPS: `50`, REPEAT: `20`, LOW RANGE: `[]`, HIGH RANGE: `[]`
 //! WORST CASE MAP SIZE: `1000000`
 //! HOSTNAME: `runner-7wrmsoux-project-674-concurrent-0`, CPU: `Intel(R) Xeon(R) CPU @ 2.60GHz`
 //! WASM-EXECUTION: `Compiled`, CHAIN: `Some("bridge-hub-westend-dev")`, DB CACHE: 1024
@@ -56,8 +56,8 @@ impl<T: frame_system::Config> pallet_bridge_relayers::WeightInfo for WeightInfo<
 		// Proof Size summary in bytes:
 		//  Measured:  `207`
 		//  Estimated: `3593`
-		// Minimum execution time: 43_291_000 picoseconds.
-		Weight::from_parts(44_118_000, 0)
+		// Minimum execution time: 43_132_000 picoseconds.
+		Weight::from_parts(43_923_000, 0)
 			.saturating_add(Weight::from_parts(0, 3593))
 			.saturating_add(T::DbWeight::get().reads(2))
 			.saturating_add(T::DbWeight::get().writes(2))
@@ -72,8 +72,8 @@ impl<T: frame_system::Config> pallet_bridge_relayers::WeightInfo for WeightInfo<
 		// Proof Size summary in bytes:
 		//  Measured:  `61`
 		//  Estimated: `4714`
-		// Minimum execution time: 23_286_000 picoseconds.
-		Weight::from_parts(23_823_000, 0)
+		// Minimum execution time: 22_765_000 picoseconds.
+		Weight::from_parts(23_576_000, 0)
 			.saturating_add(Weight::from_parts(0, 4714))
 			.saturating_add(T::DbWeight::get().reads(3))
 			.saturating_add(T::DbWeight::get().writes(2))
@@ -86,8 +86,8 @@ impl<T: frame_system::Config> pallet_bridge_relayers::WeightInfo for WeightInfo<
 		// Proof Size summary in bytes:
 		//  Measured:  `160`
 		//  Estimated: `4714`
-		// Minimum execution time: 24_145_000 picoseconds.
-		Weight::from_parts(24_541_000, 0)
+		// Minimum execution time: 24_013_000 picoseconds.
+		Weight::from_parts(24_460_000, 0)
 			.saturating_add(Weight::from_parts(0, 4714))
 			.saturating_add(T::DbWeight::get().reads(2))
 			.saturating_add(T::DbWeight::get().writes(2))
@@ -102,8 +102,8 @@ impl<T: frame_system::Config> pallet_bridge_relayers::WeightInfo for WeightInfo<
 		// Proof Size summary in bytes:
 		//  Measured:  `263`
 		//  Estimated: `4714`
-		// Minimum execution time: 26_707_000 picoseconds.
-		Weight::from_parts(27_266_000, 0)
+		// Minimum execution time: 26_946_000 picoseconds.
+		Weight::from_parts(27_485_000, 0)
 			.saturating_add(Weight::from_parts(0, 4714))
 			.saturating_add(T::DbWeight::get().reads(3))
 			.saturating_add(T::DbWeight::get().writes(3))
@@ -114,8 +114,8 @@ impl<T: frame_system::Config> pallet_bridge_relayers::WeightInfo for WeightInfo<
 		// Proof Size summary in bytes:
 		//  Measured:  `6`
 		//  Estimated: `3538`
-		// Minimum execution time: 4_850_000 picoseconds.
-		Weight::from_parts(5_116_000, 0)
+		// Minimum execution time: 4_658_000 picoseconds.
+		Weight::from_parts(4_902_000, 0)
 			.saturating_add(Weight::from_parts(0, 3538))
 			.saturating_add(T::DbWeight::get().reads(1))
 			.saturating_add(T::DbWeight::get().writes(1))
diff --git a/cumulus/parachains/runtimes/bridge-hubs/bridge-hub-westend/src/weights/xcm/pallet_xcm_benchmarks_generic.rs b/cumulus/parachains/runtimes/bridge-hubs/bridge-hub-westend/src/weights/xcm/pallet_xcm_benchmarks_generic.rs
index 9281a880c7e..73bea66bf71 100644
--- a/cumulus/parachains/runtimes/bridge-hubs/bridge-hub-westend/src/weights/xcm/pallet_xcm_benchmarks_generic.rs
+++ b/cumulus/parachains/runtimes/bridge-hubs/bridge-hub-westend/src/weights/xcm/pallet_xcm_benchmarks_generic.rs
@@ -16,10 +16,10 @@
 
 //! Autogenerated weights for `pallet_xcm_benchmarks::generic`
 //!
-//! THIS FILE WAS AUTO-GENERATED USING THE SUBSTRATE BENCHMARK CLI VERSION 4.0.0-dev
-//! DATE: 2023-12-12, STEPS: `50`, REPEAT: `20`, LOW RANGE: `[]`, HIGH RANGE: `[]`
+//! THIS FILE WAS AUTO-GENERATED USING THE SUBSTRATE BENCHMARK CLI VERSION 32.0.0
+//! DATE: 2024-07-03, STEPS: `50`, REPEAT: `20`, LOW RANGE: `[]`, HIGH RANGE: `[]`
 //! WORST CASE MAP SIZE: `1000000`
-//! HOSTNAME: `runner-itmxxexx-project-674-concurrent-0`, CPU: `Intel(R) Xeon(R) CPU @ 2.60GHz`
+//! HOSTNAME: `runner-7wrmsoux-project-674-concurrent-0`, CPU: `Intel(R) Xeon(R) CPU @ 2.60GHz`
 //! WASM-EXECUTION: Compiled, CHAIN: Some("bridge-hub-westend-dev"), DB CACHE: 1024
 
 // Executed Command:
@@ -68,8 +68,8 @@ impl<T: frame_system::Config> WeightInfo<T> {
 		// Proof Size summary in bytes:
 		//  Measured:  `208`
 		//  Estimated: `6196`
-		// Minimum execution time: 61_577_000 picoseconds.
-		Weight::from_parts(63_216_000, 6196)
+		// Minimum execution time: 58_505_000 picoseconds.
+		Weight::from_parts(60_437_000, 6196)
 			.saturating_add(T::DbWeight::get().reads(9))
 			.saturating_add(T::DbWeight::get().writes(4))
 	}
@@ -77,8 +77,8 @@ impl<T: frame_system::Config> WeightInfo<T> {
 		// Proof Size summary in bytes:
 		//  Measured:  `0`
 		//  Estimated: `0`
-		// Minimum execution time: 2_019_000 picoseconds.
-		Weight::from_parts(2_146_000, 0)
+		// Minimum execution time: 510_000 picoseconds.
+		Weight::from_parts(569_000, 0)
 	}
 	// Storage: `PolkadotXcm::Queries` (r:1 w:0)
 	// Proof: `PolkadotXcm::Queries` (`max_values`: None, `max_size`: None, mode: `Measured`)
@@ -86,58 +86,58 @@ impl<T: frame_system::Config> WeightInfo<T> {
 		// Proof Size summary in bytes:
 		//  Measured:  `32`
 		//  Estimated: `3497`
-		// Minimum execution time: 7_473_000 picoseconds.
-		Weight::from_parts(7_784_000, 3497)
+		// Minimum execution time: 5_597_000 picoseconds.
+		Weight::from_parts(5_884_000, 3497)
 			.saturating_add(T::DbWeight::get().reads(1))
 	}
 	pub fn transact() -> Weight {
 		// Proof Size summary in bytes:
 		//  Measured:  `0`
 		//  Estimated: `0`
-		// Minimum execution time: 8_385_000 picoseconds.
-		Weight::from_parts(8_768_000, 0)
+		// Minimum execution time: 5_320_000 picoseconds.
+		Weight::from_parts(5_594_000, 0)
 	}
 	pub fn refund_surplus() -> Weight {
 		// Proof Size summary in bytes:
 		//  Measured:  `0`
 		//  Estimated: `0`
-		// Minimum execution time: 2_181_000 picoseconds.
-		Weight::from_parts(2_304_000, 0)
+		// Minimum execution time: 1_164_000 picoseconds.
+		Weight::from_parts(1_227_000, 0)
 	}
 	pub fn set_error_handler() -> Weight {
 		// Proof Size summary in bytes:
 		//  Measured:  `0`
 		//  Estimated: `0`
-		// Minimum execution time: 1_858_000 picoseconds.
-		Weight::from_parts(1_919_000, 0)
+		// Minimum execution time: 528_000 picoseconds.
+		Weight::from_parts(586_000, 0)
 	}
 	pub fn set_appendix() -> Weight {
 		// Proof Size summary in bytes:
 		//  Measured:  `0`
 		//  Estimated: `0`
-		// Minimum execution time: 1_855_000 picoseconds.
-		Weight::from_parts(1_979_000, 0)
+		// Minimum execution time: 509_000 picoseconds.
+		Weight::from_parts(571_000, 0)
 	}
 	pub fn clear_error() -> Weight {
 		// Proof Size summary in bytes:
 		//  Measured:  `0`
 		//  Estimated: `0`
-		// Minimum execution time: 1_823_000 picoseconds.
-		Weight::from_parts(1_890_000, 0)
+		// Minimum execution time: 511_000 picoseconds.
+		Weight::from_parts(546_000, 0)
 	}
 	pub fn descend_origin() -> Weight {
 		// Proof Size summary in bytes:
 		//  Measured:  `0`
 		//  Estimated: `0`
-		// Minimum execution time: 2_407_000 picoseconds.
-		Weight::from_parts(2_507_000, 0)
+		// Minimum execution time: 560_000 picoseconds.
+		Weight::from_parts(600_000, 0)
 	}
 	pub fn clear_origin() -> Weight {
 		// Proof Size summary in bytes:
 		//  Measured:  `0`
 		//  Estimated: `0`
-		// Minimum execution time: 1_838_000 picoseconds.
-		Weight::from_parts(1_894_000, 0)
+		// Minimum execution time: 514_000 picoseconds.
+		Weight::from_parts(558_000, 0)
 	}
 	// Storage: `ParachainInfo::ParachainId` (r:1 w:0)
 	// Proof: `ParachainInfo::ParachainId` (`max_values`: Some(1), `max_size`: Some(4), added: 499, mode: `MaxEncodedLen`)
@@ -159,8 +159,8 @@ impl<T: frame_system::Config> WeightInfo<T> {
 		// Proof Size summary in bytes:
 		//  Measured:  `208`
 		//  Estimated: `6196`
-		// Minimum execution time: 54_847_000 picoseconds.
-		Weight::from_parts(55_742_000, 6196)
+		// Minimum execution time: 55_871_000 picoseconds.
+		Weight::from_parts(57_172_000, 6196)
 			.saturating_add(T::DbWeight::get().reads(9))
 			.saturating_add(T::DbWeight::get().writes(4))
 	}
@@ -170,8 +170,8 @@ impl<T: frame_system::Config> WeightInfo<T> {
 		// Proof Size summary in bytes:
 		//  Measured:  `90`
 		//  Estimated: `3555`
-		// Minimum execution time: 10_614_000 picoseconds.
-		Weight::from_parts(11_344_000, 3555)
+		// Minimum execution time: 8_487_000 picoseconds.
+		Weight::from_parts(8_800_000, 3555)
 			.saturating_add(T::DbWeight::get().reads(1))
 			.saturating_add(T::DbWeight::get().writes(1))
 	}
@@ -179,8 +179,8 @@ impl<T: frame_system::Config> WeightInfo<T> {
 		// Proof Size summary in bytes:
 		//  Measured:  `0`
 		//  Estimated: `0`
-		// Minimum execution time: 1_826_000 picoseconds.
-		Weight::from_parts(1_899_000, 0)
+		// Minimum execution time: 528_000 picoseconds.
+		Weight::from_parts(569_000, 0)
 	}
 	// Storage: `PolkadotXcm::VersionNotifyTargets` (r:1 w:1)
 	// Proof: `PolkadotXcm::VersionNotifyTargets` (`max_values`: None, `max_size`: None, mode: `Measured`)
@@ -200,8 +200,8 @@ impl<T: frame_system::Config> WeightInfo<T> {
 		// Proof Size summary in bytes:
 		//  Measured:  `38`
 		//  Estimated: `3503`
-		// Minimum execution time: 22_312_000 picoseconds.
-		Weight::from_parts(22_607_000, 3503)
+		// Minimum execution time: 19_803_000 picoseconds.
+		Weight::from_parts(20_368_000, 3503)
 			.saturating_add(T::DbWeight::get().reads(7))
 			.saturating_add(T::DbWeight::get().writes(3))
 	}
@@ -211,44 +211,44 @@ impl<T: frame_system::Config> WeightInfo<T> {
 		// Proof Size summary in bytes:
 		//  Measured:  `0`
 		//  Estimated: `0`
-		// Minimum execution time: 3_728_000 picoseconds.
-		Weight::from_parts(3_914_000, 0)
+		// Minimum execution time: 2_185_000 picoseconds.
+		Weight::from_parts(2_332_000, 0)
 			.saturating_add(T::DbWeight::get().writes(1))
 	}
 	pub fn burn_asset() -> Weight {
 		// Proof Size summary in bytes:
 		//  Measured:  `0`
 		//  Estimated: `0`
-		// Minimum execution time: 3_054_000 picoseconds.
-		Weight::from_parts(3_140_000, 0)
+		// Minimum execution time: 822_000 picoseconds.
+		Weight::from_parts(928_000, 0)
 	}
 	pub fn expect_asset() -> Weight {
 		// Proof Size summary in bytes:
 		//  Measured:  `0`
 		//  Estimated: `0`
-		// Minimum execution time: 1_996_000 picoseconds.
-		Weight::from_parts(2_148_000, 0)
+		// Minimum execution time: 603_000 picoseconds.
+		Weight::from_parts(643_000, 0)
 	}
 	pub fn expect_origin() -> Weight {
 		// Proof Size summary in bytes:
 		//  Measured:  `0`
 		//  Estimated: `0`
-		// Minimum execution time: 2_008_000 picoseconds.
-		Weight::from_parts(2_077_000, 0)
+		// Minimum execution time: 503_000 picoseconds.
+		Weight::from_parts(580_000, 0)
 	}
 	pub fn expect_error() -> Weight {
 		// Proof Size summary in bytes:
 		//  Measured:  `0`
 		//  Estimated: `0`
-		// Minimum execution time: 1_837_000 picoseconds.
-		Weight::from_parts(1_913_000, 0)
+		// Minimum execution time: 534_000 picoseconds.
+		Weight::from_parts(577_000, 0)
 	}
 	pub fn expect_transact_status() -> Weight {
 		// Proof Size summary in bytes:
 		//  Measured:  `0`
 		//  Estimated: `0`
-		// Minimum execution time: 2_052_000 picoseconds.
-		Weight::from_parts(2_120_000, 0)
+		// Minimum execution time: 694_000 picoseconds.
+		Weight::from_parts(745_000, 0)
 	}
 	// Storage: `ParachainInfo::ParachainId` (r:1 w:0)
 	// Proof: `ParachainInfo::ParachainId` (`max_values`: Some(1), `max_size`: Some(4), added: 499, mode: `MaxEncodedLen`)
@@ -270,8 +270,8 @@ impl<T: frame_system::Config> WeightInfo<T> {
 		// Proof Size summary in bytes:
 		//  Measured:  `208`
 		//  Estimated: `6196`
-		// Minimum execution time: 58_725_000 picoseconds.
-		Weight::from_parts(60_271_000, 6196)
+		// Minimum execution time: 61_083_000 picoseconds.
+		Weight::from_parts(62_214_000, 6196)
 			.saturating_add(T::DbWeight::get().reads(9))
 			.saturating_add(T::DbWeight::get().writes(4))
 	}
@@ -279,8 +279,8 @@ impl<T: frame_system::Config> WeightInfo<T> {
 		// Proof Size summary in bytes:
 		//  Measured:  `0`
 		//  Estimated: `0`
-		// Minimum execution time: 4_570_000 picoseconds.
-		Weight::from_parts(4_707_000, 0)
+		// Minimum execution time: 3_261_000 picoseconds.
+		Weight::from_parts(3_483_000, 0)
 	}
 	// Storage: `ParachainInfo::ParachainId` (r:1 w:0)
 	// Proof: `ParachainInfo::ParachainId` (`max_values`: Some(1), `max_size`: Some(4), added: 499, mode: `MaxEncodedLen`)
@@ -302,8 +302,8 @@ impl<T: frame_system::Config> WeightInfo<T> {
 		// Proof Size summary in bytes:
 		//  Measured:  `208`
 		//  Estimated: `6196`
-		// Minimum execution time: 54_903_000 picoseconds.
-		Weight::from_parts(55_711_000, 6196)
+		// Minimum execution time: 56_270_000 picoseconds.
+		Weight::from_parts(57_443_000, 6196)
 			.saturating_add(T::DbWeight::get().reads(9))
 			.saturating_add(T::DbWeight::get().writes(4))
 	}
@@ -311,22 +311,22 @@ impl<T: frame_system::Config> WeightInfo<T> {
 		// Proof Size summary in bytes:
 		//  Measured:  `0`
 		//  Estimated: `0`
-		// Minimum execution time: 1_872_000 picoseconds.
-		Weight::from_parts(1_938_000, 0)
+		// Minimum execution time: 565_000 picoseconds.
+		Weight::from_parts(628_000, 0)
 	}
 	pub fn set_topic() -> Weight {
 		// Proof Size summary in bytes:
 		//  Measured:  `0`
 		//  Estimated: `0`
-		// Minimum execution time: 1_836_000 picoseconds.
-		Weight::from_parts(1_903_000, 0)
+		// Minimum execution time: 496_000 picoseconds.
+		Weight::from_parts(563_000, 0)
 	}
 	pub fn clear_topic() -> Weight {
 		// Proof Size summary in bytes:
 		//  Measured:  `0`
 		//  Estimated: `0`
-		// Minimum execution time: 1_847_000 picoseconds.
-		Weight::from_parts(1_900_000, 0)
+		// Minimum execution time: 518_000 picoseconds.
+		Weight::from_parts(557_000, 0)
 	}
 	// Storage: `ParachainInfo::ParachainId` (r:1 w:0)
 	// Proof: `ParachainInfo::ParachainId` (`max_values`: Some(1), `max_size`: Some(4), added: 499, mode: `MaxEncodedLen`)
@@ -339,16 +339,16 @@ impl<T: frame_system::Config> WeightInfo<T> {
 	// Storage: `BridgeRococoMessages::OutboundLanesCongestedSignals` (r:1 w:0)
 	// Proof: `BridgeRococoMessages::OutboundLanesCongestedSignals` (`max_values`: Some(1), `max_size`: Some(21), added: 516, mode: `MaxEncodedLen`)
 	// Storage: `BridgeRococoMessages::OutboundMessages` (r:0 w:1)
-	// Proof: `BridgeRococoMessages::OutboundMessages` (`max_values`: None, `max_size`: Some(2621472), added: 2623947, mode: `MaxEncodedLen`)
+	// Proof: `BridgeRococoMessages::OutboundMessages` (`max_values`: None, `max_size`: Some(65568), added: 68043, mode: `MaxEncodedLen`)
 	/// The range of component `x` is `[1, 1000]`.
 	pub fn export_message(x: u32, ) -> Weight {
 		// Proof Size summary in bytes:
 		//  Measured:  `225`
 		//  Estimated: `6165`
-		// Minimum execution time: 41_750_000 picoseconds.
-		Weight::from_parts(43_496_915, 6165)
-			// Standard Error: 623
-			.saturating_add(Weight::from_parts(457_907, 0).saturating_mul(x.into()))
+		// Minimum execution time: 36_288_000 picoseconds.
+		Weight::from_parts(37_707_751, 6165)
+			// Standard Error: 124
+			.saturating_add(Weight::from_parts(51_290, 0).saturating_mul(x.into()))
 			.saturating_add(T::DbWeight::get().reads(6))
 			.saturating_add(T::DbWeight::get().writes(2))
 	}
@@ -356,14 +356,14 @@ impl<T: frame_system::Config> WeightInfo<T> {
 		// Proof Size summary in bytes:
 		//  Measured:  `0`
 		//  Estimated: `0`
-		// Minimum execution time: 1_826_000 picoseconds.
-		Weight::from_parts(1_911_000, 0)
+		// Minimum execution time: 485_000 picoseconds.
+		Weight::from_parts(540_000, 0)
 	}
 	pub fn unpaid_execution() -> Weight {
 		// Proof Size summary in bytes:
 		//  Measured:  `0`
 		//  Estimated: `0`
-		// Minimum execution time: 1_967_000 picoseconds.
-		Weight::from_parts(2_096_000, 0)
+		// Minimum execution time: 542_000 picoseconds.
+		Weight::from_parts(586_000, 0)
 	}
 }
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 836594140b2..763271fd7af 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
@@ -28,8 +28,8 @@ use bridge_hub_westend_runtime::{
 };
 use bridge_to_rococo_config::{
 	BridgeGrandpaRococoInstance, BridgeHubRococoChainId, BridgeHubRococoLocation,
-	BridgeParachainRococoInstance, WithBridgeHubRococoMessageBridge,
-	WithBridgeHubRococoMessagesInstance, XCM_LANE_FOR_ASSET_HUB_WESTEND_TO_ASSET_HUB_ROCOCO,
+	BridgeParachainRococoInstance, WithBridgeHubRococoMessagesInstance,
+	XCM_LANE_FOR_ASSET_HUB_WESTEND_TO_ASSET_HUB_ROCOCO,
 };
 use codec::{Decode, Encode};
 use frame_support::{dispatch::GetDispatchInfo, parameter_types, traits::ConstU8};
@@ -53,7 +53,6 @@ type RuntimeTestsAdapter = from_parachain::WithRemoteParachainHelperAdapter<
 	BridgeGrandpaRococoInstance,
 	BridgeParachainRococoInstance,
 	WithBridgeHubRococoMessagesInstance,
-	WithBridgeHubRococoMessageBridge,
 >;
 
 parameter_types! {
diff --git a/cumulus/parachains/runtimes/bridge-hubs/test-utils/Cargo.toml b/cumulus/parachains/runtimes/bridge-hubs/test-utils/Cargo.toml
index 90af4dd8f3e..fb96d29a497 100644
--- a/cumulus/parachains/runtimes/bridge-hubs/test-utils/Cargo.toml
+++ b/cumulus/parachains/runtimes/bridge-hubs/test-utils/Cargo.toml
@@ -48,7 +48,7 @@ bp-runtime = { workspace = true }
 bp-test-utils = { workspace = true }
 pallet-bridge-grandpa = { workspace = true }
 pallet-bridge-parachains = { workspace = true }
-pallet-bridge-messages = { workspace = true }
+pallet-bridge-messages = { features = ["test-helpers"], workspace = true }
 pallet-bridge-relayers = { workspace = true }
 bridge-runtime-common = { workspace = true }
 
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 bfa2f0f50f9..8f3c7de61f8 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
@@ -23,21 +23,12 @@ use crate::{
 };
 
 use bp_header_chain::ChainWithGrandpa;
-use bp_messages::{
-	source_chain::TargetHeaderChain, target_chain::SourceHeaderChain, LaneId,
-	UnrewardedRelayersState,
-};
+use bp_messages::{LaneId, UnrewardedRelayersState};
 use bp_relayers::{RewardsAccountOwner, RewardsAccountParams};
-use bp_runtime::{HashOf, UnderlyingChainOf};
-use bridge_runtime_common::{
-	messages::{
-		source::FromBridgedChainMessagesDeliveryProof, target::FromBridgedChainMessagesProof,
-		BridgedChain as MessageBridgedChain, MessageBridge, ThisChain as MessageThisChain,
-	},
-	messages_xcm_extension::XcmAsPlainPayload,
-};
+use bridge_runtime_common::messages_xcm_extension::XcmAsPlainPayload;
 use frame_support::traits::{OnFinalize, OnInitialize};
 use frame_system::pallet_prelude::BlockNumberFor;
+use pallet_bridge_messages::{BridgedChainOf, ThisChainOf};
 use parachains_runtimes_test_utils::{
 	AccountIdOf, BasicParachainRuntime, CollatorSessionKeys, RuntimeCallOf, SlotDurations,
 };
@@ -53,13 +44,10 @@ pub trait WithRemoteGrandpaChainHelper {
 	/// This chain runtime.
 	type Runtime: BasicParachainRuntime
 		+ cumulus_pallet_xcmp_queue::Config
-		+ BridgeGrandpaConfig<
-			Self::GPI,
-			BridgedChain = UnderlyingChainOf<MessageBridgedChain<Self::MB>>,
-		> + BridgeMessagesConfig<
+		+ BridgeGrandpaConfig<Self::GPI, BridgedChain = BridgedChainOf<Self::Runtime, Self::MPI>>
+		+ BridgeMessagesConfig<
 			Self::MPI,
 			InboundPayload = XcmAsPlainPayload,
-			InboundRelayer = bp_runtime::AccountIdOf<MessageBridgedChain<Self::MB>>,
 			OutboundPayload = XcmAsPlainPayload,
 		> + pallet_bridge_relayers::Config;
 	/// All pallets of this chain, excluding system pallet.
@@ -69,38 +57,33 @@ pub trait WithRemoteGrandpaChainHelper {
 	type GPI: 'static;
 	/// Instance of the `pallet-bridge-messages`, used to bridge with remote GRANDPA chain.
 	type MPI: 'static;
-	/// Messages bridge definition.
-	type MB: MessageBridge;
 }
 
 /// Adapter struct that implements [`WithRemoteGrandpaChainHelper`].
-pub struct WithRemoteGrandpaChainHelperAdapter<Runtime, AllPalletsWithoutSystem, GPI, MPI, MB>(
-	sp_std::marker::PhantomData<(Runtime, AllPalletsWithoutSystem, GPI, MPI, MB)>,
+pub struct WithRemoteGrandpaChainHelperAdapter<Runtime, AllPalletsWithoutSystem, GPI, MPI>(
+	sp_std::marker::PhantomData<(Runtime, AllPalletsWithoutSystem, GPI, MPI)>,
 );
 
-impl<Runtime, AllPalletsWithoutSystem, GPI, MPI, MB> WithRemoteGrandpaChainHelper
-	for WithRemoteGrandpaChainHelperAdapter<Runtime, AllPalletsWithoutSystem, GPI, MPI, MB>
+impl<Runtime, AllPalletsWithoutSystem, GPI, MPI> WithRemoteGrandpaChainHelper
+	for WithRemoteGrandpaChainHelperAdapter<Runtime, AllPalletsWithoutSystem, GPI, MPI>
 where
 	Runtime: BasicParachainRuntime
 		+ cumulus_pallet_xcmp_queue::Config
-		+ BridgeGrandpaConfig<GPI, BridgedChain = UnderlyingChainOf<MessageBridgedChain<MB>>>
+		+ BridgeGrandpaConfig<GPI, BridgedChain = BridgedChainOf<Runtime, MPI>>
 		+ BridgeMessagesConfig<
 			MPI,
 			InboundPayload = XcmAsPlainPayload,
-			InboundRelayer = bp_runtime::AccountIdOf<MessageBridgedChain<MB>>,
 			OutboundPayload = XcmAsPlainPayload,
 		> + pallet_bridge_relayers::Config,
 	AllPalletsWithoutSystem:
 		OnInitialize<BlockNumberFor<Runtime>> + OnFinalize<BlockNumberFor<Runtime>>,
 	GPI: 'static,
 	MPI: 'static,
-	MB: MessageBridge,
 {
 	type Runtime = Runtime;
 	type AllPalletsWithoutSystem = AllPalletsWithoutSystem;
 	type GPI = GPI;
 	type MPI = MPI;
-	type MB = MB;
 }
 
 /// Test-case makes sure that Runtime can dispatch XCM messages submitted by relayer,
@@ -124,13 +107,7 @@ pub fn relayed_incoming_message_works<RuntimeHelper>(
 	AccountIdOf<RuntimeHelper::Runtime>: From<AccountId32>,
 	RuntimeCallOf<RuntimeHelper::Runtime>: From<BridgeGrandpaCall<RuntimeHelper::Runtime, RuntimeHelper::GPI>>
 		+ From<BridgeMessagesCall<RuntimeHelper::Runtime, RuntimeHelper::MPI>>,
-	UnderlyingChainOf<MessageBridgedChain<RuntimeHelper::MB>>: ChainWithGrandpa,
-	<RuntimeHelper::Runtime as BridgeMessagesConfig<RuntimeHelper::MPI>>::SourceHeaderChain:
-		SourceHeaderChain<
-			MessagesProof = FromBridgedChainMessagesProof<
-				HashOf<MessageBridgedChain<RuntimeHelper::MB>>,
-			>,
-		>,
+	BridgedChainOf<RuntimeHelper::Runtime, RuntimeHelper::MPI>: ChainWithGrandpa,
 {
 	helpers::relayed_incoming_message_works::<
 		RuntimeHelper::Runtime,
@@ -161,7 +138,8 @@ pub fn relayed_incoming_message_works<RuntimeHelper>(
 			// to be submitted by relayer to this chain.
 			let (relay_chain_header, grandpa_justification, message_proof) =
 				test_data::from_grandpa_chain::make_complex_relayer_delivery_proofs::<
-					RuntimeHelper::MB,
+					BridgedChainOf<RuntimeHelper::Runtime, RuntimeHelper::MPI>,
+					ThisChainOf<RuntimeHelper::Runtime, RuntimeHelper::MPI>,
 					(),
 				>(
 					lane_id,
@@ -186,7 +164,7 @@ pub fn relayed_incoming_message_works<RuntimeHelper>(
 				(
 					BridgeMessagesCall::<RuntimeHelper::Runtime, RuntimeHelper::MPI>::receive_messages_proof {
 						relayer_id_at_bridged_chain,
-						proof: message_proof,
+						proof: Box::new(message_proof),
 						messages_count: 1,
 						dispatch_weight: Weight::from_parts(1000000000, 0),
 					}.into(),
@@ -233,13 +211,7 @@ pub fn free_relay_extrinsic_works<RuntimeHelper>(
 	AccountIdOf<RuntimeHelper::Runtime>: From<AccountId32>,
 	RuntimeCallOf<RuntimeHelper::Runtime>: From<BridgeGrandpaCall<RuntimeHelper::Runtime, RuntimeHelper::GPI>>
 		+ From<BridgeMessagesCall<RuntimeHelper::Runtime, RuntimeHelper::MPI>>,
-	UnderlyingChainOf<MessageBridgedChain<RuntimeHelper::MB>>: ChainWithGrandpa,
-	<RuntimeHelper::Runtime as BridgeMessagesConfig<RuntimeHelper::MPI>>::SourceHeaderChain:
-		SourceHeaderChain<
-			MessagesProof = FromBridgedChainMessagesProof<
-				HashOf<MessageBridgedChain<RuntimeHelper::MB>>,
-			>,
-		>,
+	BridgedChainOf<RuntimeHelper::Runtime, RuntimeHelper::MPI>: ChainWithGrandpa,
 {
 	// ensure that the runtime allows free header submissions
 	let free_headers_interval = <RuntimeHelper::Runtime as BridgeGrandpaConfig<
@@ -291,7 +263,8 @@ pub fn free_relay_extrinsic_works<RuntimeHelper>(
 			// to be submitted by relayer to this chain.
 			let (relay_chain_header, grandpa_justification, message_proof) =
 				test_data::from_grandpa_chain::make_complex_relayer_delivery_proofs::<
-					RuntimeHelper::MB,
+					BridgedChainOf<RuntimeHelper::Runtime, RuntimeHelper::MPI>,
+					ThisChainOf<RuntimeHelper::Runtime, RuntimeHelper::MPI>,
 					(),
 				>(
 					lane_id,
@@ -322,7 +295,7 @@ pub fn free_relay_extrinsic_works<RuntimeHelper>(
 				(
 					BridgeMessagesCall::<RuntimeHelper::Runtime, RuntimeHelper::MPI>::receive_messages_proof {
 						relayer_id_at_bridged_chain,
-						proof: message_proof,
+						proof: Box::new(message_proof),
 						messages_count: 1,
 						dispatch_weight: Weight::from_parts(1000000000, 0),
 					}.into(),
@@ -370,13 +343,7 @@ pub fn complex_relay_extrinsic_works<RuntimeHelper>(
 	RuntimeCallOf<RuntimeHelper::Runtime>: From<BridgeGrandpaCall<RuntimeHelper::Runtime, RuntimeHelper::GPI>>
 		+ From<BridgeMessagesCall<RuntimeHelper::Runtime, RuntimeHelper::MPI>>
 		+ From<pallet_utility::Call<RuntimeHelper::Runtime>>,
-	UnderlyingChainOf<MessageBridgedChain<RuntimeHelper::MB>>: ChainWithGrandpa,
-	<RuntimeHelper::Runtime as BridgeMessagesConfig<RuntimeHelper::MPI>>::SourceHeaderChain:
-		SourceHeaderChain<
-			MessagesProof = FromBridgedChainMessagesProof<
-				HashOf<MessageBridgedChain<RuntimeHelper::MB>>,
-			>,
-		>,
+	BridgedChainOf<RuntimeHelper::Runtime, RuntimeHelper::MPI>: ChainWithGrandpa,
 {
 	helpers::relayed_incoming_message_works::<
 		RuntimeHelper::Runtime,
@@ -407,7 +374,8 @@ pub fn complex_relay_extrinsic_works<RuntimeHelper>(
 			// to be submitted by relayer to this chain.
 			let (relay_chain_header, grandpa_justification, message_proof) =
 				test_data::from_grandpa_chain::make_complex_relayer_delivery_proofs::<
-					RuntimeHelper::MB,
+					BridgedChainOf<RuntimeHelper::Runtime, RuntimeHelper::MPI>,
+					ThisChainOf<RuntimeHelper::Runtime, RuntimeHelper::MPI>,
 					(),
 				>(
 					lane_id,
@@ -428,7 +396,7 @@ pub fn complex_relay_extrinsic_works<RuntimeHelper>(
 						}.into(),
 						BridgeMessagesCall::<RuntimeHelper::Runtime, RuntimeHelper::MPI>::receive_messages_proof {
 							relayer_id_at_bridged_chain,
-							proof: message_proof,
+							proof: Box::new(message_proof),
 							messages_count: 1,
 							dispatch_weight: Weight::from_parts(1000000000, 0),
 						}.into(),
@@ -470,13 +438,7 @@ where
 		pallet_utility::Config<RuntimeCall = RuntimeCallOf<RuntimeHelper::Runtime>>,
 	RuntimeCallOf<RuntimeHelper::Runtime>: From<BridgeGrandpaCall<RuntimeHelper::Runtime, RuntimeHelper::GPI>>
 		+ From<BridgeMessagesCall<RuntimeHelper::Runtime, RuntimeHelper::MPI>>,
-	UnderlyingChainOf<MessageBridgedChain<RuntimeHelper::MB>>: ChainWithGrandpa,
-	<RuntimeHelper::Runtime as BridgeMessagesConfig<RuntimeHelper::MPI>>::SourceHeaderChain:
-		SourceHeaderChain<
-			MessagesProof = FromBridgedChainMessagesProof<
-				HashOf<MessageBridgedChain<RuntimeHelper::MB>>,
-			>,
-		>,
+	BridgedChainOf<RuntimeHelper::Runtime, RuntimeHelper::MPI>: ChainWithGrandpa,
 {
 	run_test::<RuntimeHelper::Runtime, _>(collator_session_key, 1000, vec![], || {
 		// generate bridged relay chain finality, parachain heads and message proofs,
@@ -487,7 +449,8 @@ where
 		// the message additionally
 		let (relay_chain_header, grandpa_justification, message_proof) =
 			test_data::from_grandpa_chain::make_complex_relayer_delivery_proofs::<
-				RuntimeHelper::MB,
+				BridgedChainOf<RuntimeHelper::Runtime, RuntimeHelper::MPI>,
+				ThisChainOf<RuntimeHelper::Runtime, RuntimeHelper::MPI>,
 				(),
 			>(
 				LaneId::default(),
@@ -526,19 +489,11 @@ where
 	AccountIdOf<RuntimeHelper::Runtime>: From<AccountId32>,
 	RuntimeHelper::Runtime:
 		pallet_utility::Config<RuntimeCall = RuntimeCallOf<RuntimeHelper::Runtime>>,
-	MessageThisChain<RuntimeHelper::MB>:
+	ThisChainOf<RuntimeHelper::Runtime, RuntimeHelper::MPI>:
 		bp_runtime::Chain<AccountId = AccountIdOf<RuntimeHelper::Runtime>>,
 	RuntimeCallOf<RuntimeHelper::Runtime>: From<BridgeGrandpaCall<RuntimeHelper::Runtime, RuntimeHelper::GPI>>
 		+ From<BridgeMessagesCall<RuntimeHelper::Runtime, RuntimeHelper::MPI>>,
-	UnderlyingChainOf<MessageBridgedChain<RuntimeHelper::MB>>: ChainWithGrandpa,
-	<RuntimeHelper::Runtime as BridgeMessagesConfig<RuntimeHelper::MPI>>::TargetHeaderChain:
-		TargetHeaderChain<
-			XcmAsPlainPayload,
-			AccountIdOf<RuntimeHelper::Runtime>,
-			MessagesDeliveryProof = FromBridgedChainMessagesDeliveryProof<
-				HashOf<UnderlyingChainOf<MessageBridgedChain<RuntimeHelper::MB>>>,
-			>,
-		>,
+	BridgedChainOf<RuntimeHelper::Runtime, RuntimeHelper::MPI>: ChainWithGrandpa,
 {
 	run_test::<RuntimeHelper::Runtime, _>(collator_session_key, 1000, vec![], || {
 		// generate bridged relay chain finality, parachain heads and message proofs,
@@ -550,7 +505,8 @@ where
 		};
 		let (relay_chain_header, grandpa_justification, message_delivery_proof) =
 			test_data::from_grandpa_chain::make_complex_relayer_confirmation_proofs::<
-				RuntimeHelper::MB,
+				BridgedChainOf<RuntimeHelper::Runtime, RuntimeHelper::MPI>,
+				ThisChainOf<RuntimeHelper::Runtime, RuntimeHelper::MPI>,
 				(),
 			>(
 				LaneId::default(),
@@ -587,13 +543,7 @@ where
 	RuntimeHelper: WithRemoteGrandpaChainHelper,
 	RuntimeCallOf<RuntimeHelper::Runtime>:
 		From<BridgeMessagesCall<RuntimeHelper::Runtime, RuntimeHelper::MPI>>,
-	UnderlyingChainOf<MessageBridgedChain<RuntimeHelper::MB>>: ChainWithGrandpa,
-	<RuntimeHelper::Runtime as BridgeMessagesConfig<RuntimeHelper::MPI>>::SourceHeaderChain:
-		SourceHeaderChain<
-			MessagesProof = FromBridgedChainMessagesProof<
-				HashOf<MessageBridgedChain<RuntimeHelper::MB>>,
-			>,
-		>,
+	BridgedChainOf<RuntimeHelper::Runtime, RuntimeHelper::MPI>: ChainWithGrandpa,
 {
 	run_test::<RuntimeHelper::Runtime, _>(collator_session_key, 1000, vec![], || {
 		// generate bridged relay chain finality, parachain heads and message proofs,
@@ -604,7 +554,8 @@ where
 		// the message additionally
 		let (_, _, message_proof) =
 			test_data::from_grandpa_chain::make_complex_relayer_delivery_proofs::<
-				RuntimeHelper::MB,
+				BridgedChainOf<RuntimeHelper::Runtime, RuntimeHelper::MPI>,
+				ThisChainOf<RuntimeHelper::Runtime, RuntimeHelper::MPI>,
 				(),
 			>(
 				LaneId::default(),
@@ -639,19 +590,11 @@ pub fn can_calculate_fee_for_standalone_message_confirmation_transaction<Runtime
 where
 	RuntimeHelper: WithRemoteGrandpaChainHelper,
 	AccountIdOf<RuntimeHelper::Runtime>: From<AccountId32>,
-	MessageThisChain<RuntimeHelper::MB>:
+	ThisChainOf<RuntimeHelper::Runtime, RuntimeHelper::MPI>:
 		bp_runtime::Chain<AccountId = AccountIdOf<RuntimeHelper::Runtime>>,
 	RuntimeCallOf<RuntimeHelper::Runtime>:
 		From<BridgeMessagesCall<RuntimeHelper::Runtime, RuntimeHelper::MPI>>,
-	UnderlyingChainOf<MessageBridgedChain<RuntimeHelper::MB>>: ChainWithGrandpa,
-	<RuntimeHelper::Runtime as BridgeMessagesConfig<RuntimeHelper::MPI>>::TargetHeaderChain:
-		TargetHeaderChain<
-			XcmAsPlainPayload,
-			AccountIdOf<RuntimeHelper::Runtime>,
-			MessagesDeliveryProof = FromBridgedChainMessagesDeliveryProof<
-				HashOf<UnderlyingChainOf<MessageBridgedChain<RuntimeHelper::MB>>>,
-			>,
-		>,
+	BridgedChainOf<RuntimeHelper::Runtime, RuntimeHelper::MPI>: ChainWithGrandpa,
 {
 	run_test::<RuntimeHelper::Runtime, _>(collator_session_key, 1000, vec![], || {
 		// generate bridged relay chain finality, parachain heads and message proofs,
@@ -663,7 +606,8 @@ where
 		};
 		let (_, _, message_delivery_proof) =
 			test_data::from_grandpa_chain::make_complex_relayer_confirmation_proofs::<
-				RuntimeHelper::MB,
+				BridgedChainOf<RuntimeHelper::Runtime, RuntimeHelper::MPI>,
+				ThisChainOf<RuntimeHelper::Runtime, RuntimeHelper::MPI>,
 				(),
 			>(
 				LaneId::default(),
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 12ab382d9e0..6580648e660 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
@@ -23,22 +23,14 @@ use crate::{
 };
 
 use bp_header_chain::ChainWithGrandpa;
-use bp_messages::{
-	source_chain::TargetHeaderChain, target_chain::SourceHeaderChain, LaneId,
-	UnrewardedRelayersState,
-};
+use bp_messages::{LaneId, UnrewardedRelayersState};
 use bp_polkadot_core::parachains::ParaHash;
 use bp_relayers::{RewardsAccountOwner, RewardsAccountParams};
-use bp_runtime::{HashOf, Parachain, UnderlyingChainOf};
-use bridge_runtime_common::{
-	messages::{
-		source::FromBridgedChainMessagesDeliveryProof, target::FromBridgedChainMessagesProof,
-		BridgedChain as MessageBridgedChain, MessageBridge, ThisChain as MessageThisChain,
-	},
-	messages_xcm_extension::XcmAsPlainPayload,
-};
+use bp_runtime::{Chain, Parachain};
+use bridge_runtime_common::messages_xcm_extension::XcmAsPlainPayload;
 use frame_support::traits::{OnFinalize, OnInitialize};
 use frame_system::pallet_prelude::BlockNumberFor;
+use pallet_bridge_messages::{BridgedChainOf, ThisChainOf};
 use parachains_runtimes_test_utils::{
 	AccountIdOf, BasicParachainRuntime, CollatorSessionKeys, RuntimeCallOf, SlotDurations,
 };
@@ -59,7 +51,6 @@ pub trait WithRemoteParachainHelper {
 		+ BridgeMessagesConfig<
 			Self::MPI,
 			InboundPayload = XcmAsPlainPayload,
-			InboundRelayer = bp_runtime::AccountIdOf<MessageBridgedChain<Self::MB>>,
 			OutboundPayload = XcmAsPlainPayload,
 		> + pallet_bridge_relayers::Config;
 	/// All pallets of this chain, excluding system pallet.
@@ -71,17 +62,15 @@ pub trait WithRemoteParachainHelper {
 	type PPI: 'static;
 	/// Instance of the `pallet-bridge-messages`, used to bridge with remote parachain.
 	type MPI: 'static;
-	/// Messages bridge definition.
-	type MB: MessageBridge;
 }
 
 /// Adapter struct that implements `WithRemoteParachainHelper`.
-pub struct WithRemoteParachainHelperAdapter<Runtime, AllPalletsWithoutSystem, GPI, PPI, MPI, MB>(
-	sp_std::marker::PhantomData<(Runtime, AllPalletsWithoutSystem, GPI, PPI, MPI, MB)>,
+pub struct WithRemoteParachainHelperAdapter<Runtime, AllPalletsWithoutSystem, GPI, PPI, MPI>(
+	sp_std::marker::PhantomData<(Runtime, AllPalletsWithoutSystem, GPI, PPI, MPI)>,
 );
 
-impl<Runtime, AllPalletsWithoutSystem, GPI, PPI, MPI, MB> WithRemoteParachainHelper
-	for WithRemoteParachainHelperAdapter<Runtime, AllPalletsWithoutSystem, GPI, PPI, MPI, MB>
+impl<Runtime, AllPalletsWithoutSystem, GPI, PPI, MPI> WithRemoteParachainHelper
+	for WithRemoteParachainHelperAdapter<Runtime, AllPalletsWithoutSystem, GPI, PPI, MPI>
 where
 	Runtime: BasicParachainRuntime
 		+ cumulus_pallet_xcmp_queue::Config
@@ -90,7 +79,6 @@ where
 		+ BridgeMessagesConfig<
 			MPI,
 			InboundPayload = XcmAsPlainPayload,
-			InboundRelayer = bp_runtime::AccountIdOf<MessageBridgedChain<MB>>,
 			OutboundPayload = XcmAsPlainPayload,
 		> + pallet_bridge_relayers::Config,
 	AllPalletsWithoutSystem:
@@ -98,14 +86,13 @@ where
 	GPI: 'static,
 	PPI: 'static,
 	MPI: 'static,
-	MB: MessageBridge,
+	// MB: MessageBridge,
 {
 	type Runtime = Runtime;
 	type AllPalletsWithoutSystem = AllPalletsWithoutSystem;
 	type GPI = GPI;
 	type PPI = PPI;
 	type MPI = MPI;
-	type MB = MB;
 }
 
 /// Test-case makes sure that Runtime can dispatch XCM messages submitted by relayer,
@@ -131,16 +118,9 @@ pub fn relayed_incoming_message_works<RuntimeHelper>(
 	RuntimeCallOf<RuntimeHelper::Runtime>: From<BridgeGrandpaCall<RuntimeHelper::Runtime, RuntimeHelper::GPI>>
 		+ From<BridgeParachainsCall<RuntimeHelper::Runtime, RuntimeHelper::PPI>>
 		+ From<BridgeMessagesCall<RuntimeHelper::Runtime, RuntimeHelper::MPI>>,
-	UnderlyingChainOf<MessageBridgedChain<RuntimeHelper::MB>>:
-		bp_runtime::Chain<Hash = ParaHash> + Parachain,
+	BridgedChainOf<RuntimeHelper::Runtime, RuntimeHelper::MPI>: Chain<Hash = ParaHash> + Parachain,
 	<RuntimeHelper::Runtime as BridgeGrandpaConfig<RuntimeHelper::GPI>>::BridgedChain:
 		bp_runtime::Chain<Hash = RelayBlockHash, BlockNumber = RelayBlockNumber> + ChainWithGrandpa,
-	<RuntimeHelper::Runtime as BridgeMessagesConfig<RuntimeHelper::MPI>>::SourceHeaderChain:
-		SourceHeaderChain<
-			MessagesProof = FromBridgedChainMessagesProof<
-				HashOf<MessageBridgedChain<RuntimeHelper::MB>>,
-			>,
-		>,
 {
 	helpers::relayed_incoming_message_works::<
 		RuntimeHelper::Runtime,
@@ -179,7 +159,8 @@ pub fn relayed_incoming_message_works<RuntimeHelper>(
 				message_proof,
 			) = test_data::from_parachain::make_complex_relayer_delivery_proofs::<
 				<RuntimeHelper::Runtime as BridgeGrandpaConfig<RuntimeHelper::GPI>>::BridgedChain,
-				RuntimeHelper::MB,
+				BridgedChainOf<RuntimeHelper::Runtime, RuntimeHelper::MPI>,
+				ThisChainOf<RuntimeHelper::Runtime, RuntimeHelper::MPI>,
 				(),
 			>(
 				lane_id,
@@ -219,7 +200,7 @@ pub fn relayed_incoming_message_works<RuntimeHelper>(
 				(
 					BridgeMessagesCall::<RuntimeHelper::Runtime, RuntimeHelper::MPI>::receive_messages_proof {
 						relayer_id_at_bridged_chain,
-						proof: message_proof,
+						proof: Box::new(message_proof),
 						messages_count: 1,
 						dispatch_weight: Weight::from_parts(1000000000, 0),
 					}.into(),
@@ -268,16 +249,9 @@ pub fn free_relay_extrinsic_works<RuntimeHelper>(
 	RuntimeCallOf<RuntimeHelper::Runtime>: From<BridgeGrandpaCall<RuntimeHelper::Runtime, RuntimeHelper::GPI>>
 		+ From<BridgeParachainsCall<RuntimeHelper::Runtime, RuntimeHelper::PPI>>
 		+ From<BridgeMessagesCall<RuntimeHelper::Runtime, RuntimeHelper::MPI>>,
-	UnderlyingChainOf<MessageBridgedChain<RuntimeHelper::MB>>:
-		bp_runtime::Chain<Hash = ParaHash> + Parachain,
+	BridgedChainOf<RuntimeHelper::Runtime, RuntimeHelper::MPI>: Chain<Hash = ParaHash> + Parachain,
 	<RuntimeHelper::Runtime as BridgeGrandpaConfig<RuntimeHelper::GPI>>::BridgedChain:
 		bp_runtime::Chain<Hash = RelayBlockHash, BlockNumber = RelayBlockNumber> + ChainWithGrandpa,
-	<RuntimeHelper::Runtime as BridgeMessagesConfig<RuntimeHelper::MPI>>::SourceHeaderChain:
-		SourceHeaderChain<
-			MessagesProof = FromBridgedChainMessagesProof<
-				HashOf<MessageBridgedChain<RuntimeHelper::MB>>,
-			>,
-		>,
 {
 	// ensure that the runtime allows free header submissions
 	let free_headers_interval = <RuntimeHelper::Runtime as BridgeGrandpaConfig<
@@ -338,7 +312,8 @@ pub fn free_relay_extrinsic_works<RuntimeHelper>(
 				message_proof,
 			) = test_data::from_parachain::make_complex_relayer_delivery_proofs::<
 				<RuntimeHelper::Runtime as BridgeGrandpaConfig<RuntimeHelper::GPI>>::BridgedChain,
-				RuntimeHelper::MB,
+				BridgedChainOf<RuntimeHelper::Runtime, RuntimeHelper::MPI>,
+				ThisChainOf<RuntimeHelper::Runtime, RuntimeHelper::MPI>,
 				(),
 			>(
 				lane_id,
@@ -390,7 +365,7 @@ pub fn free_relay_extrinsic_works<RuntimeHelper>(
 				(
 					BridgeMessagesCall::<RuntimeHelper::Runtime, RuntimeHelper::MPI>::receive_messages_proof {
 						relayer_id_at_bridged_chain,
-						proof: message_proof,
+						proof: Box::new(message_proof),
 						messages_count: 1,
 						dispatch_weight: Weight::from_parts(1000000000, 0),
 					}.into(),
@@ -440,16 +415,9 @@ pub fn complex_relay_extrinsic_works<RuntimeHelper>(
 		+ From<BridgeParachainsCall<RuntimeHelper::Runtime, RuntimeHelper::PPI>>
 		+ From<BridgeMessagesCall<RuntimeHelper::Runtime, RuntimeHelper::MPI>>
 		+ From<pallet_utility::Call<RuntimeHelper::Runtime>>,
-	UnderlyingChainOf<MessageBridgedChain<RuntimeHelper::MB>>:
-		bp_runtime::Chain<Hash = ParaHash> + Parachain,
+	BridgedChainOf<RuntimeHelper::Runtime, RuntimeHelper::MPI>: Chain<Hash = ParaHash> + Parachain,
 	<RuntimeHelper::Runtime as BridgeGrandpaConfig<RuntimeHelper::GPI>>::BridgedChain:
 		bp_runtime::Chain<Hash = RelayBlockHash, BlockNumber = RelayBlockNumber> + ChainWithGrandpa,
-	<RuntimeHelper::Runtime as BridgeMessagesConfig<RuntimeHelper::MPI>>::SourceHeaderChain:
-		SourceHeaderChain<
-			MessagesProof = FromBridgedChainMessagesProof<
-				HashOf<MessageBridgedChain<RuntimeHelper::MB>>,
-			>,
-		>,
 {
 	helpers::relayed_incoming_message_works::<
 		RuntimeHelper::Runtime,
@@ -488,7 +456,8 @@ pub fn complex_relay_extrinsic_works<RuntimeHelper>(
 				message_proof,
 			) = test_data::from_parachain::make_complex_relayer_delivery_proofs::<
 				<RuntimeHelper::Runtime as BridgeGrandpaConfig<RuntimeHelper::GPI>>::BridgedChain,
-				RuntimeHelper::MB,
+				BridgedChainOf<RuntimeHelper::Runtime, RuntimeHelper::MPI>,
+				ThisChainOf<RuntimeHelper::Runtime, RuntimeHelper::MPI>,
 				(),
 			>(
 				lane_id,
@@ -518,7 +487,7 @@ pub fn complex_relay_extrinsic_works<RuntimeHelper>(
 						}.into(),
 						BridgeMessagesCall::<RuntimeHelper::Runtime, RuntimeHelper::MPI>::receive_messages_proof {
 							relayer_id_at_bridged_chain,
-							proof: message_proof,
+							proof: Box::new(message_proof),
 							messages_count: 1,
 							dispatch_weight: Weight::from_parts(1000000000, 0),
 						}.into(),
@@ -565,16 +534,9 @@ where
 	RuntimeCallOf<RuntimeHelper::Runtime>: From<BridgeGrandpaCall<RuntimeHelper::Runtime, RuntimeHelper::GPI>>
 		+ From<BridgeParachainsCall<RuntimeHelper::Runtime, RuntimeHelper::PPI>>
 		+ From<BridgeMessagesCall<RuntimeHelper::Runtime, RuntimeHelper::MPI>>,
-	UnderlyingChainOf<MessageBridgedChain<RuntimeHelper::MB>>:
-		bp_runtime::Chain<Hash = ParaHash> + Parachain,
+	BridgedChainOf<RuntimeHelper::Runtime, RuntimeHelper::MPI>: Chain<Hash = ParaHash> + Parachain,
 	<RuntimeHelper::Runtime as BridgeGrandpaConfig<RuntimeHelper::GPI>>::BridgedChain:
 		bp_runtime::Chain<Hash = RelayBlockHash, BlockNumber = RelayBlockNumber> + ChainWithGrandpa,
-	<RuntimeHelper::Runtime as BridgeMessagesConfig<RuntimeHelper::MPI>>::SourceHeaderChain:
-		SourceHeaderChain<
-			MessagesProof = FromBridgedChainMessagesProof<
-				HashOf<MessageBridgedChain<RuntimeHelper::MB>>,
-			>,
-		>,
 {
 	run_test::<RuntimeHelper::Runtime, _>(collator_session_key, 1000, vec![], || {
 		// generate bridged relay chain finality, parachain heads and message proofs,
@@ -592,7 +554,8 @@ where
 			message_proof,
 		) = test_data::from_parachain::make_complex_relayer_delivery_proofs::<
 			<RuntimeHelper::Runtime as pallet_bridge_grandpa::Config<RuntimeHelper::GPI>>::BridgedChain,
-			RuntimeHelper::MB,
+			BridgedChainOf<RuntimeHelper::Runtime, RuntimeHelper::MPI>,
+			ThisChainOf<RuntimeHelper::Runtime, RuntimeHelper::MPI>,
 			(),
 		>(
 			LaneId::default(),
@@ -612,7 +575,6 @@ where
 			RuntimeHelper::GPI,
 			RuntimeHelper::PPI,
 			RuntimeHelper::MPI,
-			_,
 		>(
 			relay_chain_header,
 			grandpa_justification,
@@ -637,23 +599,14 @@ where
 	AccountIdOf<RuntimeHelper::Runtime>: From<AccountId32>,
 	RuntimeHelper::Runtime:
 		pallet_utility::Config<RuntimeCall = RuntimeCallOf<RuntimeHelper::Runtime>>,
-	MessageThisChain<RuntimeHelper::MB>:
-		bp_runtime::Chain<AccountId = AccountIdOf<RuntimeHelper::Runtime>>,
+	ThisChainOf<RuntimeHelper::Runtime, RuntimeHelper::MPI>:
+		Chain<AccountId = AccountIdOf<RuntimeHelper::Runtime>>,
 	RuntimeCallOf<RuntimeHelper::Runtime>: From<BridgeGrandpaCall<RuntimeHelper::Runtime, RuntimeHelper::GPI>>
 		+ From<BridgeParachainsCall<RuntimeHelper::Runtime, RuntimeHelper::PPI>>
 		+ From<BridgeMessagesCall<RuntimeHelper::Runtime, RuntimeHelper::MPI>>,
-	UnderlyingChainOf<MessageBridgedChain<RuntimeHelper::MB>>:
-		bp_runtime::Chain<Hash = ParaHash> + Parachain,
+	BridgedChainOf<RuntimeHelper::Runtime, RuntimeHelper::MPI>: Chain<Hash = ParaHash> + Parachain,
 	<RuntimeHelper::Runtime as BridgeGrandpaConfig<RuntimeHelper::GPI>>::BridgedChain:
 		bp_runtime::Chain<Hash = RelayBlockHash, BlockNumber = RelayBlockNumber> + ChainWithGrandpa,
-	<RuntimeHelper::Runtime as BridgeMessagesConfig<RuntimeHelper::MPI>>::TargetHeaderChain:
-		TargetHeaderChain<
-			XcmAsPlainPayload,
-			AccountIdOf<RuntimeHelper::Runtime>,
-			MessagesDeliveryProof = FromBridgedChainMessagesDeliveryProof<
-				HashOf<UnderlyingChainOf<MessageBridgedChain<RuntimeHelper::MB>>>,
-			>,
-		>,
 {
 	run_test::<RuntimeHelper::Runtime, _>(collator_session_key, 1000, vec![], || {
 		// generate bridged relay chain finality, parachain heads and message proofs,
@@ -672,7 +625,8 @@ where
 			message_delivery_proof,
 		) = test_data::from_parachain::make_complex_relayer_confirmation_proofs::<
 			<RuntimeHelper::Runtime as BridgeGrandpaConfig<RuntimeHelper::GPI>>::BridgedChain,
-			RuntimeHelper::MB,
+			BridgedChainOf<RuntimeHelper::Runtime, RuntimeHelper::MPI>,
+			ThisChainOf<RuntimeHelper::Runtime, RuntimeHelper::MPI>,
 			(),
 		>(
 			LaneId::default(),
@@ -714,16 +668,9 @@ where
 	RuntimeHelper: WithRemoteParachainHelper,
 	RuntimeCallOf<RuntimeHelper::Runtime>:
 		From<BridgeMessagesCall<RuntimeHelper::Runtime, RuntimeHelper::MPI>>,
-	UnderlyingChainOf<MessageBridgedChain<RuntimeHelper::MB>>:
-		bp_runtime::Chain<Hash = ParaHash> + Parachain,
+	BridgedChainOf<RuntimeHelper::Runtime, RuntimeHelper::MPI>: Chain<Hash = ParaHash> + Parachain,
 	<RuntimeHelper::Runtime as BridgeGrandpaConfig<RuntimeHelper::GPI>>::BridgedChain:
 		bp_runtime::Chain<Hash = RelayBlockHash, BlockNumber = RelayBlockNumber> + ChainWithGrandpa,
-	<RuntimeHelper::Runtime as BridgeMessagesConfig<RuntimeHelper::MPI>>::SourceHeaderChain:
-		SourceHeaderChain<
-			MessagesProof = FromBridgedChainMessagesProof<
-				HashOf<MessageBridgedChain<RuntimeHelper::MB>>,
-			>,
-		>,
 {
 	run_test::<RuntimeHelper::Runtime, _>(collator_session_key, 1000, vec![], || {
 		// generate bridged relay chain finality, parachain heads and message proofs,
@@ -741,7 +688,8 @@ where
 			message_proof,
 		) = test_data::from_parachain::make_complex_relayer_delivery_proofs::<
 			<RuntimeHelper::Runtime as pallet_bridge_grandpa::Config<RuntimeHelper::GPI>>::BridgedChain,
-			RuntimeHelper::MB,
+			BridgedChainOf<RuntimeHelper::Runtime, RuntimeHelper::MPI>,
+			ThisChainOf<RuntimeHelper::Runtime, RuntimeHelper::MPI>,
 			(),
 		>(
 			LaneId::default(),
@@ -757,7 +705,6 @@ where
 		let call = test_data::from_parachain::make_standalone_relayer_delivery_call::<
 			RuntimeHelper::Runtime,
 			RuntimeHelper::MPI,
-			_,
 		>(
 			message_proof,
 			helpers::relayer_id_at_bridged_chain::<RuntimeHelper::Runtime, RuntimeHelper::MPI>(),
@@ -778,22 +725,13 @@ pub fn can_calculate_fee_for_standalone_message_confirmation_transaction<Runtime
 where
 	RuntimeHelper: WithRemoteParachainHelper,
 	AccountIdOf<RuntimeHelper::Runtime>: From<AccountId32>,
-	MessageThisChain<RuntimeHelper::MB>:
-		bp_runtime::Chain<AccountId = AccountIdOf<RuntimeHelper::Runtime>>,
+	ThisChainOf<RuntimeHelper::Runtime, RuntimeHelper::MPI>:
+		Chain<AccountId = AccountIdOf<RuntimeHelper::Runtime>>,
 	RuntimeCallOf<RuntimeHelper::Runtime>:
 		From<BridgeMessagesCall<RuntimeHelper::Runtime, RuntimeHelper::MPI>>,
-	UnderlyingChainOf<MessageBridgedChain<RuntimeHelper::MB>>:
-		bp_runtime::Chain<Hash = ParaHash> + Parachain,
+	BridgedChainOf<RuntimeHelper::Runtime, RuntimeHelper::MPI>: Chain<Hash = ParaHash> + Parachain,
 	<RuntimeHelper::Runtime as BridgeGrandpaConfig<RuntimeHelper::GPI>>::BridgedChain:
 		bp_runtime::Chain<Hash = RelayBlockHash, BlockNumber = RelayBlockNumber> + ChainWithGrandpa,
-	<RuntimeHelper::Runtime as BridgeMessagesConfig<RuntimeHelper::MPI>>::TargetHeaderChain:
-		TargetHeaderChain<
-			XcmAsPlainPayload,
-			AccountIdOf<RuntimeHelper::Runtime>,
-			MessagesDeliveryProof = FromBridgedChainMessagesDeliveryProof<
-				HashOf<UnderlyingChainOf<MessageBridgedChain<RuntimeHelper::MB>>>,
-			>,
-		>,
 {
 	run_test::<RuntimeHelper::Runtime, _>(collator_session_key, 1000, vec![], || {
 		// generate bridged relay chain finality, parachain heads and message proofs,
@@ -806,7 +744,8 @@ where
 		let (_, _, _, _, _, message_delivery_proof) =
 			test_data::from_parachain::make_complex_relayer_confirmation_proofs::<
 				<RuntimeHelper::Runtime as BridgeGrandpaConfig<RuntimeHelper::GPI>>::BridgedChain,
-				RuntimeHelper::MB,
+				BridgedChainOf<RuntimeHelper::Runtime, RuntimeHelper::MPI>,
+				ThisChainOf<RuntimeHelper::Runtime, RuntimeHelper::MPI>,
 				(),
 			>(
 				LaneId::default(),
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 0ce049cd1c4..c990c6e5307 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
@@ -29,6 +29,7 @@ use frame_support::{
 };
 use frame_system::pallet_prelude::BlockNumberFor;
 use pallet_bridge_grandpa::{BridgedBlockHash, BridgedHeader};
+use pallet_bridge_messages::BridgedChainOf;
 use parachains_common::AccountId;
 use parachains_runtimes_test_utils::{
 	mock_open_hrmp_channel, AccountIdOf, CollatorSessionKeys, RuntimeCallOf, SlotDurations,
@@ -240,10 +241,12 @@ pub(crate) fn initialize_bridge_grandpa_pallet<Runtime, GPI>(
 pub type CallsAndVerifiers<Runtime> =
 	Vec<(RuntimeCallOf<Runtime>, Box<dyn VerifyTransactionOutcome>)>;
 
+pub type InboundRelayerId<Runtime, MPI> = bp_runtime::AccountIdOf<BridgedChainOf<Runtime, MPI>>;
+
 /// Returns relayer id at the bridged chain.
 pub fn relayer_id_at_bridged_chain<Runtime: pallet_bridge_messages::Config<MPI>, MPI>(
-) -> Runtime::InboundRelayer {
-	Runtime::InboundRelayer::decode(&mut TrailingZeroInput::zeroes()).unwrap()
+) -> InboundRelayerId<Runtime, MPI> {
+	Decode::decode(&mut TrailingZeroInput::zeroes()).unwrap()
 }
 
 /// Test-case makes sure that Runtime can dispatch XCM messages submitted by relayer,
@@ -260,7 +263,7 @@ pub fn relayed_incoming_message_works<Runtime, AllPalletsWithoutSystem, MPI>(
 	) -> sp_runtime::DispatchOutcome,
 	prepare_message_proof_import: impl FnOnce(
 		Runtime::AccountId,
-		Runtime::InboundRelayer,
+		InboundRelayerId<Runtime, MPI>,
 		InteriorLocation,
 		MessageNonce,
 		Xcm<()>,
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 e5d5e7cac96..c61a31e5454 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
@@ -19,30 +19,29 @@
 use crate::test_data::prepare_inbound_xcm;
 
 use bp_messages::{
-	source_chain::TargetHeaderChain, target_chain::SourceHeaderChain, LaneId, MessageNonce,
+	source_chain::FromBridgedChainMessagesDeliveryProof,
+	target_chain::FromBridgedChainMessagesProof, ChainWithMessages, LaneId, MessageNonce,
 	UnrewardedRelayersState,
 };
-use bp_runtime::{AccountIdOf, BlockNumberOf, HeaderOf, StorageProofSize, UnderlyingChainOf};
+use bp_runtime::{AccountIdOf, BlockNumberOf, Chain, HeaderOf, UnverifiedStorageProofParams};
 use bp_test_utils::make_default_justification;
-use bridge_runtime_common::{
-	messages::{
-		source::FromBridgedChainMessagesDeliveryProof, target::FromBridgedChainMessagesProof,
-		BridgedChain as MessageBridgedChain, MessageBridge, ThisChain as MessageThisChain,
-	},
-	messages_generation::{
-		encode_all_messages, encode_lane_data, prepare_message_delivery_storage_proof,
-		prepare_messages_storage_proof,
-	},
-	messages_xcm_extension::XcmAsPlainPayload,
-};
+use bridge_runtime_common::messages_xcm_extension::XcmAsPlainPayload;
 use codec::Encode;
 use pallet_bridge_grandpa::{BridgedChain, BridgedHeader};
 use sp_runtime::traits::Header as HeaderT;
 use xcm::latest::prelude::*;
 
+use crate::test_cases::helpers::InboundRelayerId;
 use bp_header_chain::{justification::GrandpaJustification, ChainWithGrandpa};
 use bp_messages::{DeliveredMessages, InboundLaneData, UnrewardedRelayer};
 use bp_runtime::HashOf;
+use pallet_bridge_messages::{
+	messages_generation::{
+		encode_all_messages, encode_lane_data, prepare_message_delivery_storage_proof,
+		prepare_messages_storage_proof,
+	},
+	BridgedChainOf,
+};
 use sp_runtime::DigestItem;
 
 /// Prepare a batch call with bridged GRANDPA finality and message proof.
@@ -50,22 +49,17 @@ 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>>>,
-	relayer_id_at_bridged_chain: AccountIdOf<BridgedChain<Runtime, GPI>>,
+	relayer_id_at_bridged_chain: InboundRelayerId<Runtime, MPI>,
 ) -> pallet_utility::Call<Runtime>
 where
 	Runtime: pallet_bridge_grandpa::Config<GPI>
-		+ pallet_bridge_messages::Config<
-			MPI,
-			InboundPayload = XcmAsPlainPayload,
-			InboundRelayer = AccountIdOf<BridgedChain<Runtime, GPI>>,
-		> + pallet_utility::Config,
+		+ pallet_bridge_messages::Config<MPI, InboundPayload = XcmAsPlainPayload>
+		+ pallet_utility::Config,
 	GPI: 'static,
 	MPI: 'static,
-	<Runtime as pallet_bridge_messages::Config<MPI>>::SourceHeaderChain: SourceHeaderChain<
-		MessagesProof = FromBridgedChainMessagesProof<HashOf<BridgedChain<Runtime, GPI>>>,
-	>,
 	<Runtime as pallet_utility::Config>::RuntimeCall: From<pallet_bridge_grandpa::Call<Runtime, GPI>>
 		+ From<pallet_bridge_messages::Call<Runtime, MPI>>,
+	BridgedChainOf<Runtime, MPI>: Chain<Hash = HashOf<BridgedChain<Runtime, GPI>>>,
 {
 	let submit_grandpa = pallet_bridge_grandpa::Call::<Runtime, GPI>::submit_finality_proof {
 		finality_target: Box::new(bridged_header),
@@ -73,7 +67,7 @@ where
 	};
 	let submit_message = pallet_bridge_messages::Call::<Runtime, MPI>::receive_messages_proof {
 		relayer_id_at_bridged_chain,
-		proof: message_proof,
+		proof: Box::new(message_proof),
 		messages_count: 1,
 		dispatch_weight: Weight::from_parts(1000000000, 0),
 	};
@@ -97,15 +91,9 @@ where
 		+ pallet_utility::Config,
 	GPI: 'static,
 	MPI: 'static,
-	<Runtime as pallet_bridge_messages::Config<MPI>>::TargetHeaderChain: TargetHeaderChain<
-		XcmAsPlainPayload,
-		Runtime::AccountId,
-		MessagesDeliveryProof = FromBridgedChainMessagesDeliveryProof<
-			HashOf<BridgedChain<Runtime, GPI>>,
-		>,
-	>,
 	<Runtime as pallet_utility::Config>::RuntimeCall: From<pallet_bridge_grandpa::Call<Runtime, GPI>>
 		+ From<pallet_bridge_messages::Call<Runtime, MPI>>,
+	BridgedChainOf<Runtime, MPI>: Chain<Hash = HashOf<BridgedChain<Runtime, GPI>>>,
 {
 	let submit_grandpa = pallet_bridge_grandpa::Call::<Runtime, GPI>::submit_finality_proof {
 		finality_target: Box::new(bridged_header),
@@ -124,24 +112,18 @@ where
 /// Prepare a call with message proof.
 pub fn make_standalone_relayer_delivery_call<Runtime, GPI, MPI>(
 	message_proof: FromBridgedChainMessagesProof<HashOf<BridgedChain<Runtime, GPI>>>,
-	relayer_id_at_bridged_chain: AccountIdOf<BridgedChain<Runtime, GPI>>,
+	relayer_id_at_bridged_chain: InboundRelayerId<Runtime, MPI>,
 ) -> Runtime::RuntimeCall
 where
 	Runtime: pallet_bridge_grandpa::Config<GPI>
-		+ pallet_bridge_messages::Config<
-			MPI,
-			InboundPayload = XcmAsPlainPayload,
-			InboundRelayer = AccountIdOf<BridgedChain<Runtime, GPI>>,
-		>,
+		+ pallet_bridge_messages::Config<MPI, InboundPayload = XcmAsPlainPayload>,
 	MPI: 'static,
-	<Runtime as pallet_bridge_messages::Config<MPI>>::SourceHeaderChain: SourceHeaderChain<
-		MessagesProof = FromBridgedChainMessagesProof<HashOf<BridgedChain<Runtime, GPI>>>,
-	>,
 	Runtime::RuntimeCall: From<pallet_bridge_messages::Call<Runtime, MPI>>,
+	BridgedChainOf<Runtime, MPI>: Chain<Hash = HashOf<BridgedChain<Runtime, GPI>>>,
 {
 	pallet_bridge_messages::Call::<Runtime, MPI>::receive_messages_proof {
 		relayer_id_at_bridged_chain,
-		proof: message_proof,
+		proof: Box::new(message_proof),
 		messages_count: 1,
 		dispatch_weight: Weight::from_parts(1000000000, 0),
 	}
@@ -159,14 +141,8 @@ where
 	Runtime: pallet_bridge_grandpa::Config<GPI>
 		+ pallet_bridge_messages::Config<MPI, OutboundPayload = XcmAsPlainPayload>,
 	MPI: 'static,
-	<Runtime as pallet_bridge_messages::Config<MPI>>::TargetHeaderChain: TargetHeaderChain<
-		XcmAsPlainPayload,
-		Runtime::AccountId,
-		MessagesDeliveryProof = FromBridgedChainMessagesDeliveryProof<
-			HashOf<BridgedChain<Runtime, GPI>>,
-		>,
-	>,
 	Runtime::RuntimeCall: From<pallet_bridge_messages::Call<Runtime, MPI>>,
+	BridgedChainOf<Runtime, MPI>: Chain<Hash = HashOf<BridgedChain<Runtime, GPI>>>,
 {
 	pallet_bridge_messages::Call::<Runtime, MPI>::receive_messages_delivery_proof {
 		proof: message_delivery_proof,
@@ -176,39 +152,47 @@ where
 }
 
 /// Prepare storage proofs of messages, stored at the (bridged) source GRANDPA chain.
-pub fn make_complex_relayer_delivery_proofs<MB, InnerXcmRuntimeCall>(
+pub fn make_complex_relayer_delivery_proofs<
+	BridgedChain,
+	ThisChainWithMessages,
+	InnerXcmRuntimeCall,
+>(
 	lane_id: LaneId,
 	xcm_message: Xcm<InnerXcmRuntimeCall>,
 	message_nonce: MessageNonce,
 	message_destination: Junctions,
-	header_number: BlockNumberOf<MessageBridgedChain<MB>>,
+	header_number: BlockNumberOf<BridgedChain>,
 	is_minimal_call: bool,
 ) -> (
-	HeaderOf<MessageBridgedChain<MB>>,
-	GrandpaJustification<HeaderOf<MessageBridgedChain<MB>>>,
-	FromBridgedChainMessagesProof<HashOf<MessageBridgedChain<MB>>>,
+	HeaderOf<BridgedChain>,
+	GrandpaJustification<HeaderOf<BridgedChain>>,
+	FromBridgedChainMessagesProof<HashOf<BridgedChain>>,
 )
 where
-	MB: MessageBridge,
-	MessageBridgedChain<MB>: Send + Sync + 'static,
-	UnderlyingChainOf<MessageBridgedChain<MB>>: ChainWithGrandpa,
+	BridgedChain: ChainWithGrandpa,
+	ThisChainWithMessages: ChainWithMessages,
 {
+	// prepare message
 	let message_payload = prepare_inbound_xcm(xcm_message, message_destination);
-	let message_size = StorageProofSize::Minimal(message_payload.len() as u32);
-	// prepare para storage proof containing message
-	let (state_root, storage_proof) = prepare_messages_storage_proof::<MB>(
-		lane_id,
-		message_nonce..=message_nonce,
-		None,
-		message_size,
-		message_payload,
-		encode_all_messages,
-		encode_lane_data,
-	);
+	// prepare storage proof containing message
+	let (state_root, storage_proof) =
+		prepare_messages_storage_proof::<BridgedChain, ThisChainWithMessages>(
+			lane_id,
+			message_nonce..=message_nonce,
+			None,
+			UnverifiedStorageProofParams::from_db_size(message_payload.len() as u32),
+			|_| message_payload.clone(),
+			encode_all_messages,
+			encode_lane_data,
+			false,
+			false,
+		);
 
-	let (header, justification) = make_complex_bridged_grandpa_header_proof::<
-		MessageBridgedChain<MB>,
-	>(state_root, header_number, is_minimal_call);
+	let (header, justification) = make_complex_bridged_grandpa_header_proof::<BridgedChain>(
+		state_root,
+		header_number,
+		is_minimal_call,
+	);
 
 	let message_proof = FromBridgedChainMessagesProof {
 		bridged_header_hash: header.hash(),
@@ -222,44 +206,44 @@ where
 }
 
 /// Prepare storage proofs of message confirmations, stored at the (bridged) target GRANDPA chain.
-pub fn make_complex_relayer_confirmation_proofs<MB, InnerXcmRuntimeCall>(
+pub fn make_complex_relayer_confirmation_proofs<
+	BridgedChain,
+	ThisChainWithMessages,
+	InnerXcmRuntimeCall,
+>(
 	lane_id: LaneId,
-	header_number: BlockNumberOf<MessageBridgedChain<MB>>,
-	relayer_id_at_this_chain: AccountIdOf<MessageThisChain<MB>>,
+	header_number: BlockNumberOf<BridgedChain>,
+	relayer_id_at_this_chain: AccountIdOf<ThisChainWithMessages>,
 	relayers_state: UnrewardedRelayersState,
 ) -> (
-	HeaderOf<MessageBridgedChain<MB>>,
-	GrandpaJustification<HeaderOf<MessageBridgedChain<MB>>>,
-	FromBridgedChainMessagesDeliveryProof<HashOf<MessageBridgedChain<MB>>>,
+	HeaderOf<BridgedChain>,
+	GrandpaJustification<HeaderOf<BridgedChain>>,
+	FromBridgedChainMessagesDeliveryProof<HashOf<BridgedChain>>,
 )
 where
-	MB: MessageBridge,
-	MessageBridgedChain<MB>: Send + Sync + 'static,
-	MessageThisChain<MB>: Send + Sync + 'static,
-	UnderlyingChainOf<MessageBridgedChain<MB>>: ChainWithGrandpa,
+	BridgedChain: ChainWithGrandpa,
+	ThisChainWithMessages: ChainWithMessages,
 {
 	// prepare storage proof containing message delivery proof
-	let (state_root, storage_proof) = prepare_message_delivery_storage_proof::<MB>(
-		lane_id,
-		InboundLaneData {
-			relayers: vec![
-				UnrewardedRelayer {
-					relayer: relayer_id_at_this_chain,
-					messages: DeliveredMessages::new(1)
-				};
-				relayers_state.unrewarded_relayer_entries as usize
-			]
-			.into(),
-			last_confirmed_nonce: 1,
-		},
-		StorageProofSize::Minimal(0),
-	);
+	let (state_root, storage_proof) =
+		prepare_message_delivery_storage_proof::<BridgedChain, ThisChainWithMessages>(
+			lane_id,
+			InboundLaneData {
+				relayers: vec![
+					UnrewardedRelayer {
+						relayer: relayer_id_at_this_chain,
+						messages: DeliveredMessages::new(1)
+					};
+					relayers_state.unrewarded_relayer_entries as usize
+				]
+				.into(),
+				last_confirmed_nonce: 1,
+			},
+			UnverifiedStorageProofParams::default(),
+		);
 
-	let (header, justification) = make_complex_bridged_grandpa_header_proof::<MB::BridgedChain>(
-		state_root,
-		header_number,
-		false,
-	);
+	let (header, justification) =
+		make_complex_bridged_grandpa_header_proof::<BridgedChain>(state_root, header_number, false);
 
 	let message_delivery_proof = FromBridgedChainMessagesDeliveryProof {
 		bridged_header_hash: header.hash(),
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 5d3cba4e53b..897fe0d0b0f 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
@@ -19,61 +19,58 @@
 use super::{from_grandpa_chain::make_complex_bridged_grandpa_header_proof, prepare_inbound_xcm};
 
 use bp_messages::{
-	source_chain::TargetHeaderChain, target_chain::SourceHeaderChain, LaneId,
+	source_chain::FromBridgedChainMessagesDeliveryProof,
+	target_chain::FromBridgedChainMessagesProof, ChainWithMessages, LaneId,
 	UnrewardedRelayersState, Weight,
 };
 use bp_runtime::{
-	AccountIdOf, BlockNumberOf, HeaderOf, Parachain, StorageProofSize, UnderlyingChainOf,
+	AccountIdOf, BlockNumberOf, Chain, HeaderOf, Parachain, UnverifiedStorageProofParams,
 };
 use bp_test_utils::prepare_parachain_heads_proof;
-use bridge_runtime_common::{
-	messages::{
-		source::FromBridgedChainMessagesDeliveryProof, target::FromBridgedChainMessagesProof,
-		BridgedChain as MessageBridgedChain, MessageBridge, ThisChain as MessageThisChain,
-	},
-	messages_generation::{
-		encode_all_messages, encode_lane_data, prepare_message_delivery_storage_proof,
-		prepare_messages_storage_proof,
-	},
-	messages_xcm_extension::XcmAsPlainPayload,
-};
+use bridge_runtime_common::messages_xcm_extension::XcmAsPlainPayload;
 use codec::Encode;
 use pallet_bridge_grandpa::BridgedHeader;
 use pallet_bridge_parachains::{RelayBlockHash, RelayBlockNumber};
 use sp_runtime::traits::Header as HeaderT;
 use xcm::latest::prelude::*;
 
+use crate::test_cases::helpers::InboundRelayerId;
 use bp_header_chain::{justification::GrandpaJustification, ChainWithGrandpa};
 use bp_messages::{DeliveredMessages, InboundLaneData, MessageNonce, UnrewardedRelayer};
 use bp_polkadot_core::parachains::{ParaHash, ParaHead, ParaHeadsProof, ParaId};
+use pallet_bridge_messages::{
+	messages_generation::{
+		encode_all_messages, encode_lane_data, prepare_message_delivery_storage_proof,
+		prepare_messages_storage_proof,
+	},
+	BridgedChainOf,
+};
 use sp_runtime::SaturatedConversion;
 
 /// Prepare a batch call with relay finality proof, parachain head proof and message proof.
-pub fn make_complex_relayer_delivery_batch<Runtime, GPI, PPI, MPI, InboundRelayer>(
+pub fn make_complex_relayer_delivery_batch<Runtime, GPI, PPI, MPI>(
 	relay_chain_header: BridgedHeader<Runtime, GPI>,
 	grandpa_justification: GrandpaJustification<BridgedHeader<Runtime, GPI>>,
 	parachain_heads: Vec<(ParaId, ParaHash)>,
 	para_heads_proof: ParaHeadsProof,
 	message_proof: FromBridgedChainMessagesProof<ParaHash>,
-	relayer_id_at_bridged_chain: InboundRelayer,
-) -> pallet_utility::Call<Runtime> where
-	Runtime:pallet_bridge_grandpa::Config<GPI>
+	relayer_id_at_bridged_chain: InboundRelayerId<Runtime, MPI>,
+) -> pallet_utility::Call<Runtime>
+where
+	Runtime: pallet_bridge_grandpa::Config<GPI>
 		+ pallet_bridge_parachains::Config<PPI>
-		+ pallet_bridge_messages::Config<
-			MPI,
-			InboundPayload = XcmAsPlainPayload,
-			InboundRelayer = InboundRelayer,
-		>
+		+ pallet_bridge_messages::Config<MPI, InboundPayload = XcmAsPlainPayload>
 		+ pallet_utility::Config,
 	GPI: 'static,
 	PPI: 'static,
 	MPI: 'static,
-	ParaHash: From<<<Runtime as pallet_bridge_grandpa::Config<GPI>>::BridgedChain as bp_runtime::Chain>::Hash>,
-	<<Runtime as pallet_bridge_grandpa::Config<GPI>>::BridgedChain as bp_runtime::Chain>::Hash: From<ParaHash>,
-	<<Runtime as pallet_bridge_messages::Config<MPI>>::SourceHeaderChain as SourceHeaderChain>::MessagesProof:
-		From<FromBridgedChainMessagesProof<ParaHash>>,
-	<Runtime as pallet_utility::Config>::RuntimeCall:
-		From<pallet_bridge_grandpa::Call<Runtime, GPI>>
+	ParaHash: From<
+		<<Runtime as pallet_bridge_grandpa::Config<GPI>>::BridgedChain as bp_runtime::Chain>::Hash,
+	>,
+	<<Runtime as pallet_bridge_grandpa::Config<GPI>>::BridgedChain as bp_runtime::Chain>::Hash:
+		From<ParaHash>,
+	BridgedChainOf<Runtime, MPI>: Chain<Hash = ParaHash> + Parachain,
+	<Runtime as pallet_utility::Config>::RuntimeCall: From<pallet_bridge_grandpa::Call<Runtime, GPI>>
 		+ From<pallet_bridge_parachains::Call<Runtime, PPI>>
 		+ From<pallet_bridge_messages::Call<Runtime, MPI>>,
 {
@@ -93,7 +90,7 @@ pub fn make_complex_relayer_delivery_batch<Runtime, GPI, PPI, MPI, InboundRelaye
 	};
 	let submit_message = pallet_bridge_messages::Call::<Runtime, MPI>::receive_messages_proof {
 		relayer_id_at_bridged_chain: relayer_id_at_bridged_chain.into(),
-		proof: message_proof.into(),
+		proof: Box::new(message_proof),
 		messages_count: 1,
 		dispatch_weight: Weight::from_parts(1000000000, 0),
 	};
@@ -122,11 +119,7 @@ where
 	MPI: 'static,
 	<Runtime as pallet_bridge_grandpa::Config<GPI>>::BridgedChain:
 		bp_runtime::Chain<Hash = RelayBlockHash, BlockNumber = RelayBlockNumber> + ChainWithGrandpa,
-	<Runtime as pallet_bridge_messages::Config<MPI>>::TargetHeaderChain: TargetHeaderChain<
-		XcmAsPlainPayload,
-		Runtime::AccountId,
-		MessagesDeliveryProof = FromBridgedChainMessagesDeliveryProof<ParaHash>,
-	>,
+	BridgedChainOf<Runtime, MPI>: Chain<Hash = ParaHash> + Parachain,
 	<Runtime as pallet_utility::Config>::RuntimeCall: From<pallet_bridge_grandpa::Call<Runtime, GPI>>
 		+ From<pallet_bridge_parachains::Call<Runtime, PPI>>
 		+ From<pallet_bridge_messages::Call<Runtime, MPI>>,
@@ -160,23 +153,19 @@ where
 }
 
 /// Prepare a call with message proof.
-pub fn make_standalone_relayer_delivery_call<Runtime, MPI, InboundRelayer>(
+pub fn make_standalone_relayer_delivery_call<Runtime, MPI>(
 	message_proof: FromBridgedChainMessagesProof<ParaHash>,
-	relayer_id_at_bridged_chain: InboundRelayer,
-) -> Runtime::RuntimeCall where
-	Runtime: pallet_bridge_messages::Config<
-		MPI,
-		InboundPayload = XcmAsPlainPayload,
-		InboundRelayer = InboundRelayer,
-	>,
+	relayer_id_at_bridged_chain: InboundRelayerId<Runtime, MPI>,
+) -> Runtime::RuntimeCall
+where
+	Runtime: pallet_bridge_messages::Config<MPI, InboundPayload = XcmAsPlainPayload>,
 	MPI: 'static,
-	Runtime::RuntimeCall: From<pallet_bridge_messages::Call::<Runtime, MPI>>,
-	<<Runtime as pallet_bridge_messages::Config<MPI>>::SourceHeaderChain as SourceHeaderChain>::MessagesProof:
-		From<FromBridgedChainMessagesProof<ParaHash>>,
+	Runtime::RuntimeCall: From<pallet_bridge_messages::Call<Runtime, MPI>>,
+	BridgedChainOf<Runtime, MPI>: Chain<Hash = ParaHash> + Parachain,
 {
 	pallet_bridge_messages::Call::<Runtime, MPI>::receive_messages_proof {
 		relayer_id_at_bridged_chain: relayer_id_at_bridged_chain.into(),
-		proof: message_proof.into(),
+		proof: Box::new(message_proof),
 		messages_count: 1,
 		dispatch_weight: Weight::from_parts(1000000000, 0),
 	}
@@ -192,11 +181,7 @@ where
 	Runtime: pallet_bridge_messages::Config<MPI, OutboundPayload = XcmAsPlainPayload>,
 	MPI: 'static,
 	Runtime::RuntimeCall: From<pallet_bridge_messages::Call<Runtime, MPI>>,
-	<Runtime as pallet_bridge_messages::Config<MPI>>::TargetHeaderChain: TargetHeaderChain<
-		XcmAsPlainPayload,
-		Runtime::AccountId,
-		MessagesDeliveryProof = FromBridgedChainMessagesDeliveryProof<ParaHash>,
-	>,
+	BridgedChainOf<Runtime, MPI>: Chain<Hash = ParaHash> + Parachain,
 {
 	pallet_bridge_messages::Call::<Runtime, MPI>::receive_messages_delivery_proof {
 		proof: message_delivery_proof,
@@ -206,7 +191,12 @@ where
 }
 
 /// Prepare storage proofs of messages, stored at the source chain.
-pub fn make_complex_relayer_delivery_proofs<BridgedRelayChain, MB, InnerXcmRuntimeCall>(
+pub fn make_complex_relayer_delivery_proofs<
+	BridgedRelayChain,
+	BridgedParachain,
+	ThisChainWithMessages,
+	InnerXcmRuntimeCall,
+>(
 	lane_id: LaneId,
 	xcm_message: Xcm<InnerXcmRuntimeCall>,
 	message_nonce: MessageNonce,
@@ -226,24 +216,27 @@ pub fn make_complex_relayer_delivery_proofs<BridgedRelayChain, MB, InnerXcmRunti
 where
 	BridgedRelayChain:
 		bp_runtime::Chain<Hash = RelayBlockHash, BlockNumber = RelayBlockNumber> + ChainWithGrandpa,
-	MB: MessageBridge,
-	UnderlyingChainOf<MessageBridgedChain<MB>>: bp_runtime::Chain<Hash = ParaHash> + Parachain,
+	BridgedParachain: bp_runtime::Chain<Hash = ParaHash> + Parachain,
+	ThisChainWithMessages: ChainWithMessages,
 {
+	// prepare message
 	let message_payload = prepare_inbound_xcm(xcm_message, message_destination);
-	let message_size = StorageProofSize::Minimal(message_payload.len() as u32);
 	// prepare para storage proof containing message
-	let (para_state_root, para_storage_proof) = prepare_messages_storage_proof::<MB>(
-		lane_id,
-		message_nonce..=message_nonce,
-		None,
-		message_size,
-		message_payload,
-		encode_all_messages,
-		encode_lane_data,
-	);
+	let (para_state_root, para_storage_proof) =
+		prepare_messages_storage_proof::<BridgedParachain, ThisChainWithMessages>(
+			lane_id,
+			message_nonce..=message_nonce,
+			None,
+			UnverifiedStorageProofParams::from_db_size(message_payload.len() as u32),
+			|_| message_payload.clone(),
+			encode_all_messages,
+			encode_lane_data,
+			false,
+			false,
+		);
 
 	let (relay_chain_header, justification, bridged_para_head, parachain_heads, para_heads_proof) =
-		make_complex_bridged_parachain_heads_proof::<BridgedRelayChain, MB>(
+		make_complex_bridged_parachain_heads_proof::<BridgedRelayChain, BridgedParachain>(
 			para_state_root,
 			para_header_number,
 			relay_header_number,
@@ -270,12 +263,17 @@ where
 }
 
 /// Prepare storage proofs of message confirmations, stored at the target parachain.
-pub fn make_complex_relayer_confirmation_proofs<BridgedRelayChain, MB, InnerXcmRuntimeCall>(
+pub fn make_complex_relayer_confirmation_proofs<
+	BridgedRelayChain,
+	BridgedParachain,
+	ThisChainWithMessages,
+	InnerXcmRuntimeCall,
+>(
 	lane_id: LaneId,
 	para_header_number: u32,
 	relay_header_number: u32,
 	bridged_para_id: u32,
-	relayer_id_at_this_chain: AccountIdOf<MessageThisChain<MB>>,
+	relayer_id_at_this_chain: AccountIdOf<ThisChainWithMessages>,
 	relayers_state: UnrewardedRelayersState,
 ) -> (
 	HeaderOf<BridgedRelayChain>,
@@ -288,28 +286,29 @@ pub fn make_complex_relayer_confirmation_proofs<BridgedRelayChain, MB, InnerXcmR
 where
 	BridgedRelayChain:
 		bp_runtime::Chain<Hash = RelayBlockHash, BlockNumber = RelayBlockNumber> + ChainWithGrandpa,
-	MB: MessageBridge,
-	UnderlyingChainOf<MessageBridgedChain<MB>>: bp_runtime::Chain<Hash = ParaHash> + Parachain,
+	BridgedParachain: bp_runtime::Chain<Hash = ParaHash> + Parachain,
+	ThisChainWithMessages: ChainWithMessages,
 {
 	// prepare para storage proof containing message delivery proof
-	let (para_state_root, para_storage_proof) = prepare_message_delivery_storage_proof::<MB>(
-		lane_id,
-		InboundLaneData {
-			relayers: vec![
-				UnrewardedRelayer {
-					relayer: relayer_id_at_this_chain.into(),
-					messages: DeliveredMessages::new(1)
-				};
-				relayers_state.unrewarded_relayer_entries as usize
-			]
-			.into(),
-			last_confirmed_nonce: 1,
-		},
-		StorageProofSize::Minimal(0),
-	);
+	let (para_state_root, para_storage_proof) =
+		prepare_message_delivery_storage_proof::<BridgedParachain, ThisChainWithMessages>(
+			lane_id,
+			InboundLaneData {
+				relayers: vec![
+					UnrewardedRelayer {
+						relayer: relayer_id_at_this_chain.into(),
+						messages: DeliveredMessages::new(1)
+					};
+					relayers_state.unrewarded_relayer_entries as usize
+				]
+				.into(),
+				last_confirmed_nonce: 1,
+			},
+			UnverifiedStorageProofParams::default(),
+		);
 
 	let (relay_chain_header, justification, bridged_para_head, parachain_heads, para_heads_proof) =
-		make_complex_bridged_parachain_heads_proof::<BridgedRelayChain, MB>(
+		make_complex_bridged_parachain_heads_proof::<BridgedRelayChain, BridgedParachain>(
 			para_state_root,
 			para_header_number,
 			relay_header_number,
@@ -334,7 +333,7 @@ where
 }
 
 /// Make bridged parachain header with given state root and relay header that is finalizing it.
-pub fn make_complex_bridged_parachain_heads_proof<BridgedRelayChain, MB>(
+pub fn make_complex_bridged_parachain_heads_proof<BridgedRelayChain, BridgedParachain>(
 	para_state_root: ParaHash,
 	para_header_number: u32,
 	relay_header_number: BlockNumberOf<BridgedRelayChain>,
@@ -350,20 +349,17 @@ pub fn make_complex_bridged_parachain_heads_proof<BridgedRelayChain, MB>(
 where
 	BridgedRelayChain:
 		bp_runtime::Chain<Hash = RelayBlockHash, BlockNumber = RelayBlockNumber> + ChainWithGrandpa,
-	MB: MessageBridge,
-	<MB as MessageBridge>::BridgedChain: Send + Sync + 'static,
-	<MB as MessageBridge>::ThisChain: Send + Sync + 'static,
-	UnderlyingChainOf<MessageBridgedChain<MB>>: bp_runtime::Chain<Hash = ParaHash> + Parachain,
+	BridgedParachain: bp_runtime::Chain<Hash = ParaHash> + Parachain,
 {
 	let bridged_para_head = ParaHead(
-		bp_test_utils::test_header_with_root::<HeaderOf<MB::BridgedChain>>(
+		bp_test_utils::test_header_with_root::<HeaderOf<BridgedParachain>>(
 			para_header_number.into(),
 			para_state_root,
 		)
 		.encode(),
 	);
 	let (relay_state_root, para_heads_proof, parachain_heads) =
-		prepare_parachain_heads_proof::<HeaderOf<MB::BridgedChain>>(vec![(
+		prepare_parachain_heads_proof::<HeaderOf<BridgedParachain>>(vec![(
 			bridged_para_id,
 			bridged_para_head.clone(),
 		)]);
diff --git a/docker/dockerfiles/bridges_zombienet_tests_injected.Dockerfile b/docker/dockerfiles/bridges_zombienet_tests_injected.Dockerfile
index e17952ccee8..634b9f18829 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.4
+ARG SUBSTRATE_RELAY_IMAGE=docker.io/paritytech/substrate-relay:v1.6.5
 
 # metadata
 ARG VCS_REF
diff --git a/prdoc/pr_4935.prdoc b/prdoc/pr_4935.prdoc
new file mode 100644
index 00000000000..2b06899b633
--- /dev/null
+++ b/prdoc/pr_4935.prdoc
@@ -0,0 +1,75 @@
+# Schema: Polkadot SDK PRDoc Schema (prdoc) v1.0.0
+# See doc at https://raw.githubusercontent.com/paritytech/polkadot-sdk/master/prdoc/schema_user.json
+
+title: "Bridges V2 refactoring backport and `pallet_bridge_messages` simplifications"
+
+doc:
+  - audience: Runtime Dev
+    description: |
+      This introduces several simplifications to the pallet_bridge_messages::Config configuration.
+      Types like `BridgedChainId`, `MaxUnrewardedRelayerEntriesAtInboundLane`, `MaxUnconfirmedMessagesAtInboundLane`, `MaximalOutboundPayloadSize`,
+      `InboundRelayer`, `TargetHeaderChain`, and `SourceHeaderChain` were removed.
+      Now, you only need to provide specific bridging chain configurations for `ThisChain`, `BridgedChain`, and `BridgedHeaderChain`.
+
+      If you previously specified implementations for the bp_runtime::Chain* traits, those will fit here exactly, for example:
+      ```
+      type ThisChain = bp_bridge_hub_rococo::BridgeHubRococo;
+      type BridgedChain = bp_bridge_hub_westend::BridgeHubWestend;
+      type BridgedHeaderChain = pallet_bridge_parachains::ParachainHeaders<
+            Runtime,
+            BridgeParachainWestendInstance,
+            bp_bridge_hub_westend::BridgeHubWestend,
+      >;
+      ```
+
+crates:
+  - name: pallet-bridge-messages
+    bump: major
+  - name: bridge-runtime-common
+    bump: major
+  - name: bp-header-chain
+    bump: major
+  - name: bp-runtime
+    bump: major
+  - name: bp-messages
+    bump: major
+  - name: bp-polkadot-core
+    bump: patch
+  - name: bp-bridge-hub-kusama
+    bump: minor
+  - name: bp-bridge-hub-polkadot
+    bump: minor
+  - name: bp-bridge-hub-rococo
+    bump: minor
+  - name: bp-bridge-hub-westend
+    bump: minor
+  - name: bp-kusama
+    bump: minor
+  - name: bp-polkadot
+    bump: minor
+  - name: bp-polkadot-bulletin
+    bump: minor
+  - name: bp-rococo
+    bump: minor
+  - name: bp-test-utils
+    bump: patch
+  - name: bp-westend
+    bump: minor
+  - name: bridge-hub-test-utils
+    bump: major
+  - name: pallet-bridge-grandpa
+    bump: patch
+  - name: pallet-bridge-parachains
+    bump: patch
+  - name: pallet-bridge-relayers
+    bump: patch
+  - name: pallet-xcm-bridge-hub
+    bump: patch
+  - name: asset-hub-rococo-runtime
+    bump: patch
+  - name: asset-hub-westend-runtime
+    bump: patch
+  - name: bridge-hub-rococo-runtime
+    bump: major
+  - name: bridge-hub-westend-runtime
+    bump: major
-- 
GitLab