diff --git a/polkadot/runtime/common/src/xcm_sender.rs b/polkadot/runtime/common/src/xcm_sender.rs
index 03120ccf704a7e58ad63240b22f44c03ca998f3e..3654c44010e9130773cb3d926f4e226a2759008a 100644
--- a/polkadot/runtime/common/src/xcm_sender.rs
+++ b/polkadot/runtime/common/src/xcm_sender.rs
@@ -21,8 +21,9 @@ use parity_scale_codec::Encode;
 use primitives::Id as ParaId;
 use runtime_parachains::{
 	configuration::{self, HostConfiguration},
-	dmp,
+	dmp, FeeTracker,
 };
+use sp_runtime::FixedPointNumber;
 use sp_std::{marker::PhantomData, prelude::*};
 use xcm::prelude::*;
 use SendError::*;
@@ -47,6 +48,24 @@ impl<T: Get<MultiAssets>> PriceForParachainDelivery for ConstantPrice<T> {
 	}
 }
 
+/// Implementation of `PriceForParachainDelivery` which returns an exponentially increasing price.
+/// The `A` type parameter is used to denote the asset ID that will be used for paying the delivery
+/// fee.
+///
+/// The formula for the fee is based on the sum of a base fee plus a message length fee, multiplied
+/// by a specified factor. In mathematical form, it is `F * (B + encoded_msg_len * M)`.
+pub struct ExponentialPrice<A, B, M, F>(sp_std::marker::PhantomData<(A, B, M, F)>);
+impl<A: Get<AssetId>, B: Get<u128>, M: Get<u128>, F: FeeTracker> PriceForParachainDelivery
+	for ExponentialPrice<A, B, M, F>
+{
+	fn price_for_parachain_delivery(para: ParaId, msg: &Xcm<()>) -> MultiAssets {
+		let msg_fee = (msg.encoded_size() as u128).saturating_mul(M::get());
+		let fee_sum = B::get().saturating_add(msg_fee);
+		let amount = F::get_fee_factor(para).saturating_mul_int(fee_sum);
+		(A::get(), amount).into()
+	}
+}
+
 /// XCM sender for relay chain. It only sends downward message.
 pub struct ChildParachainRouter<T, W, P>(PhantomData<(T, W, P)>);
 
@@ -88,3 +107,61 @@ impl<T: configuration::Config + dmp::Config, W: xcm::WrapVersion, P: PriceForPar
 			.map_err(|_| SendError::Transport(&"Error placing into DMP queue"))
 	}
 }
+
+#[cfg(test)]
+mod tests {
+	use super::*;
+	use frame_support::parameter_types;
+	use runtime_parachains::FeeTracker;
+	use sp_runtime::FixedU128;
+
+	parameter_types! {
+		pub const BaseDeliveryFee: u128 = 300_000_000;
+		pub const TransactionByteFee: u128 = 1_000_000;
+		pub FeeAssetId: AssetId = Concrete(Here.into());
+	}
+
+	struct TestFeeTracker;
+	impl FeeTracker for TestFeeTracker {
+		fn get_fee_factor(_: ParaId) -> FixedU128 {
+			FixedU128::from_rational(101, 100)
+		}
+	}
+
+	type TestExponentialPrice =
+		ExponentialPrice<FeeAssetId, BaseDeliveryFee, TransactionByteFee, TestFeeTracker>;
+
+	#[test]
+	fn exponential_price_correct_price_calculation() {
+		let id: ParaId = 123.into();
+		let b: u128 = BaseDeliveryFee::get();
+		let m: u128 = TransactionByteFee::get();
+
+		// F * (B + msg_length * M)
+		// message_length = 1
+		let result: u128 = TestFeeTracker::get_fee_factor(id.clone()).saturating_mul_int(b + m);
+		assert_eq!(
+			TestExponentialPrice::price_for_parachain_delivery(id.clone(), &Xcm(vec![])),
+			(FeeAssetId::get(), result).into()
+		);
+
+		// message size = 2
+		let result: u128 =
+			TestFeeTracker::get_fee_factor(id.clone()).saturating_mul_int(b + (2 * m));
+		assert_eq!(
+			TestExponentialPrice::price_for_parachain_delivery(id.clone(), &Xcm(vec![ClearOrigin])),
+			(FeeAssetId::get(), result).into()
+		);
+
+		// message size = 4
+		let result: u128 =
+			TestFeeTracker::get_fee_factor(id.clone()).saturating_mul_int(b + (4 * m));
+		assert_eq!(
+			TestExponentialPrice::price_for_parachain_delivery(
+				id.clone(),
+				&Xcm(vec![SetAppendix(Xcm(vec![ClearOrigin]))])
+			),
+			(FeeAssetId::get(), result).into()
+		);
+	}
+}
diff --git a/polkadot/runtime/kusama/Cargo.toml b/polkadot/runtime/kusama/Cargo.toml
index 668cf04dae4d5acab6798eb33bec659fac784567..4e082eb5acd7c11b7755d2dd2fd94894beee41ee 100644
--- a/polkadot/runtime/kusama/Cargo.toml
+++ b/polkadot/runtime/kusama/Cargo.toml
@@ -24,7 +24,7 @@ sp-api = { git = "https://github.com/paritytech/substrate", branch = "master", d
 inherents = { package = "sp-inherents", git = "https://github.com/paritytech/substrate", branch = "master", default-features = false }
 offchain-primitives = { package = "sp-offchain", git = "https://github.com/paritytech/substrate", branch = "master", default-features = false }
 sp-std = { package = "sp-std", git = "https://github.com/paritytech/substrate", branch = "master", default-features = false }
-sp-arithmetic = { package = "sp-arithmetic", git = "https://github.com/paritytech/substrate", branch = "master", default-features = false }
+sp-arithmetic = { git = "https://github.com/paritytech/substrate", branch = "master", default-features = false }
 sp-io = { git = "https://github.com/paritytech/substrate", branch = "master", default-features = false }
 sp-mmr-primitives = { git = "https://github.com/paritytech/substrate", branch = "master", default-features = false }
 sp-runtime = { git = "https://github.com/paritytech/substrate", branch = "master", default-features = false }
@@ -128,6 +128,7 @@ std = [
 	"parity-scale-codec/std",
 	"scale-info/std",
 	"inherents/std",
+	"sp-arithmetic/std",
 	"sp-core/std",
 	"sp-api/std",
 	"tx-pool-api/std",
diff --git a/polkadot/runtime/kusama/src/lib.rs b/polkadot/runtime/kusama/src/lib.rs
index b62f72cc42248d4684cc0e45acf079d7d45785d0..79a5ce75f7ae78921dde275038019e8041ed7af3 100644
--- a/polkadot/runtime/kusama/src/lib.rs
+++ b/polkadot/runtime/kusama/src/lib.rs
@@ -1410,7 +1410,7 @@ construct_runtime! {
 		ParaScheduler: parachains_scheduler::{Pallet, Storage} = 55,
 		Paras: parachains_paras::{Pallet, Call, Storage, Event, Config, ValidateUnsigned} = 56,
 		Initializer: parachains_initializer::{Pallet, Call, Storage} = 57,
-		Dmp: parachains_dmp::{Pallet, Call, Storage} = 58,
+		Dmp: parachains_dmp::{Pallet, Storage} = 58,
 		Ump: parachains_ump::{Pallet, Call, Storage, Event} = 59,
 		Hrmp: parachains_hrmp::{Pallet, Call, Storage, Event<T>, Config} = 60,
 		ParaSessionInfo: parachains_session_info::{Pallet, Storage} = 61,
diff --git a/polkadot/runtime/kusama/src/xcm_config.rs b/polkadot/runtime/kusama/src/xcm_config.rs
index 289ea118d7bc4f30b0407a474ea8f666d400c4f0..b3da8f4d2a9a02c8cc81ea15d3ba8471cb3e7310 100644
--- a/polkadot/runtime/kusama/src/xcm_config.rs
+++ b/polkadot/runtime/kusama/src/xcm_config.rs
@@ -17,8 +17,9 @@
 //! XCM configurations for the Kusama runtime.
 
 use super::{
-	parachains_origin, AccountId, AllPalletsWithSystem, Balances, Fellows, ParaId, Runtime,
-	RuntimeCall, RuntimeEvent, RuntimeOrigin, StakingAdmin, WeightToFee, XcmPallet,
+	parachains_origin, AccountId, AllPalletsWithSystem, Balances, Dmp, Fellows, ParaId, Runtime,
+	RuntimeCall, RuntimeEvent, RuntimeOrigin, StakingAdmin, TransactionByteFee, WeightToFee,
+	XcmPallet,
 };
 use frame_support::{
 	match_types, parameter_types,
@@ -26,7 +27,12 @@ use frame_support::{
 	weights::Weight,
 };
 use frame_system::EnsureRoot;
-use runtime_common::{crowdloan, paras_registrar, xcm_sender, ToAuthor};
+use kusama_runtime_constants::currency::CENTS;
+use runtime_common::{
+	crowdloan, paras_registrar,
+	xcm_sender::{ChildParachainRouter, ExponentialPrice},
+	ToAuthor,
+};
 use sp_core::ConstU32;
 use xcm::latest::prelude::*;
 use xcm_builder::{
@@ -101,13 +107,21 @@ parameter_types! {
 	/// Maximum number of instructions in a single XCM fragment. A sanity check against weight
 	/// calculations getting too crazy.
 	pub const MaxInstructions: u32 = 100;
+	/// The asset ID for the asset that we use to pay for message delivery fees.
+	pub FeeAssetId: AssetId = Concrete(TokenLocation::get());
+	/// The base fee for the message delivery fees.
+	pub const BaseDeliveryFee: u128 = CENTS.saturating_mul(3);
 }
 
 /// The XCM router. When we want to send an XCM message, we use this type. It amalgamates all of our
 /// individual routers.
 pub type XcmRouter = (
 	// Only one router so far - use DMP to communicate with child parachains.
-	xcm_sender::ChildParachainRouter<Runtime, XcmPallet, ()>,
+	ChildParachainRouter<
+		Runtime,
+		XcmPallet,
+		ExponentialPrice<FeeAssetId, BaseDeliveryFee, TransactionByteFee, Dmp>,
+	>,
 );
 
 parameter_types! {
diff --git a/polkadot/runtime/parachains/src/dmp.rs b/polkadot/runtime/parachains/src/dmp.rs
index e5ccac2647fe1959063305c78a4aa0a4c4ac563b..5244406fcce44e4ca65bc05b0542ec14a04187b0 100644
--- a/polkadot/runtime/parachains/src/dmp.rs
+++ b/polkadot/runtime/parachains/src/dmp.rs
@@ -14,13 +14,45 @@
 // You should have received a copy of the GNU General Public License
 // along with Polkadot.  If not, see <http://www.gnu.org/licenses/>.
 
+//! To prevent Out of Memory errors on the `DownwardMessageQueue`, an
+//! exponential fee factor (`DeliveryFeeFactor`) is set. The fee factor
+//! increments exponentially after the number of messages in the
+//! `DownwardMessageQueue` pass a threshold. This threshold is set as:
+//!
+//! ```ignore
+//! // Maximum max sized messages that can be send to
+//! // the DownwardMessageQueue before it runs out of memory
+//! max_messsages = MAX_POSSIBLE_ALLOCATION / max_downward_message_size
+//! threshold = max_messages / THRESHOLD_FACTOR
+//! ```
+//! Based on the THRESHOLD_FACTOR, the threshold is set as a fraction of the
+//! total messages. The `DeliveryFeeFactor` increases for a message over the
+//! threshold by:
+//!
+//! `DeliveryFeeFactor = DeliveryFeeFactor *
+//! (EXPONENTIAL_FEE_BASE + MESSAGE_SIZE_FEE_BASE * encoded_message_size_in_KB)`
+//!
+//! And decreases when the number of messages in the `DownwardMessageQueue` fall
+//! below the threshold by:
+//!
+//! `DeliveryFeeFactor = DeliveryFeeFactor / EXPONENTIAL_FEE_BASE`
+//!
+//! As an extra defensive measure, a `max_messages` hard
+//! limit is set to the number of messages in the DownwardMessageQueue. Messages
+//! that would increase the number of messages in the queue above this hard
+//! limit are dropped.
+
 use crate::{
 	configuration::{self, HostConfiguration},
-	initializer,
+	initializer, FeeTracker,
 };
 use frame_support::pallet_prelude::*;
 use primitives::{DownwardMessage, Hash, Id as ParaId, InboundDownwardMessage};
-use sp_runtime::traits::{BlakeTwo256, Hash as HashT, SaturatedConversion};
+use sp_core::MAX_POSSIBLE_ALLOCATION;
+use sp_runtime::{
+	traits::{BlakeTwo256, Hash as HashT, SaturatedConversion},
+	FixedU128, Saturating,
+};
 use sp_std::{fmt, prelude::*};
 use xcm::latest::SendError;
 
@@ -29,7 +61,9 @@ pub use pallet::*;
 #[cfg(test)]
 mod tests;
 
-pub const MAX_MESSAGE_QUEUE_SIZE: usize = 1024;
+const THRESHOLD_FACTOR: u32 = 2;
+const EXPONENTIAL_FEE_BASE: FixedU128 = FixedU128::from_rational(105, 100); // 1.05
+const MESSAGE_SIZE_FEE_BASE: FixedU128 = FixedU128::from_rational(1, 1000); // 0.001
 
 /// An error sending a downward message.
 #[cfg_attr(test, derive(Debug))]
@@ -102,10 +136,17 @@ pub mod pallet {
 	pub(crate) type DownwardMessageQueueHeads<T: Config> =
 		StorageMap<_, Twox64Concat, ParaId, Hash, ValueQuery>;
 
-	#[pallet::call]
-	impl<T: Config> Pallet<T> {}
-}
+	/// Initialization value for the DeliveryFee factor.
+	#[pallet::type_value]
+	pub fn InitialFactor() -> FixedU128 {
+		FixedU128::from_u32(1)
+	}
 
+	/// The number to multiply the base delivery fee by.
+	#[pallet::storage]
+	pub(crate) type DeliveryFeeFactor<T: Config> =
+		StorageMap<_, Twox64Concat, ParaId, FixedU128, ValueQuery, InitialFactor>;
+}
 /// Routines and getters related to downward message passing.
 impl<T: Config> Pallet<T> {
 	/// Block initialization logic, called by initializer.
@@ -151,7 +192,8 @@ impl<T: Config> Pallet<T> {
 			return Err(QueueDownwardMessageError::ExceedsMaxMessageSize)
 		}
 
-		if DownwardMessageQueues::<T>::decode_len(para).unwrap_or(0) > MAX_MESSAGE_QUEUE_SIZE {
+		// Hard limit on Queue size
+		if Self::dmq_length(*para) > Self::dmq_max_length(config.max_downward_message_size) {
 			return Err(QueueDownwardMessageError::ExceedsMaxMessageSize)
 		}
 
@@ -176,7 +218,8 @@ impl<T: Config> Pallet<T> {
 			return Err(QueueDownwardMessageError::ExceedsMaxMessageSize)
 		}
 
-		if DownwardMessageQueues::<T>::decode_len(para).unwrap_or(0) > MAX_MESSAGE_QUEUE_SIZE {
+		// Hard limit on Queue size
+		if Self::dmq_length(para) > Self::dmq_max_length(config.max_downward_message_size) {
 			return Err(QueueDownwardMessageError::ExceedsMaxMessageSize)
 		}
 
@@ -190,10 +233,20 @@ impl<T: Config> Pallet<T> {
 			*head = new_head;
 		});
 
-		DownwardMessageQueues::<T>::mutate(para, |v| {
+		let q_len = DownwardMessageQueues::<T>::mutate(para, |v| {
 			v.push(inbound);
+			v.len()
 		});
 
+		let threshold =
+			Self::dmq_max_length(config.max_downward_message_size).saturating_div(THRESHOLD_FACTOR);
+		if q_len > (threshold as usize) {
+			let message_size_factor =
+				FixedU128::from_u32(serialized_len.saturating_div(1024) as u32)
+					.saturating_mul(MESSAGE_SIZE_FEE_BASE);
+			Self::increment_fee_factor(para, message_size_factor);
+		}
+
 		Ok(())
 	}
 
@@ -219,7 +272,7 @@ impl<T: Config> Pallet<T> {
 
 	/// Prunes the specified number of messages from the downward message queue of the given para.
 	pub(crate) fn prune_dmq(para: ParaId, processed_downward_messages: u32) -> Weight {
-		DownwardMessageQueues::<T>::mutate(para, |q| {
+		let q_len = DownwardMessageQueues::<T>::mutate(para, |q| {
 			let processed_downward_messages = processed_downward_messages as usize;
 			if processed_downward_messages > q.len() {
 				// reaching this branch is unexpected due to the constraint established by
@@ -228,7 +281,15 @@ impl<T: Config> Pallet<T> {
 			} else {
 				*q = q.split_off(processed_downward_messages);
 			}
+			q.len()
 		});
+
+		let config = configuration::ActiveConfig::<T>::get();
+		let threshold =
+			Self::dmq_max_length(config.max_downward_message_size).saturating_div(THRESHOLD_FACTOR);
+		if q_len <= (threshold as usize) {
+			Self::decrement_fee_factor(para);
+		}
 		T::DbWeight::get().reads_writes(1, 1)
 	}
 
@@ -248,10 +309,42 @@ impl<T: Config> Pallet<T> {
 			.saturated_into::<u32>()
 	}
 
+	fn dmq_max_length(max_downward_message_size: u32) -> u32 {
+		MAX_POSSIBLE_ALLOCATION.checked_div(max_downward_message_size).unwrap_or(0)
+	}
+
 	/// Returns the downward message queue contents for the given para.
 	///
 	/// The most recent messages are the latest in the vector.
 	pub(crate) fn dmq_contents(recipient: ParaId) -> Vec<InboundDownwardMessage<T::BlockNumber>> {
 		DownwardMessageQueues::<T>::get(&recipient)
 	}
+
+	/// Raise the delivery fee factor by a multiplicative factor and stores the resulting value.
+	///
+	/// Returns the new delivery fee factor after the increment.
+	pub(crate) fn increment_fee_factor(para: ParaId, message_size_factor: FixedU128) -> FixedU128 {
+		<DeliveryFeeFactor<T>>::mutate(para, |f| {
+			*f = f.saturating_mul(EXPONENTIAL_FEE_BASE + message_size_factor);
+			*f
+		})
+	}
+
+	/// Reduce the delivery fee factor by a multiplicative factor and stores the resulting value.
+	///
+	/// Does not reduce the fee factor below the initial value, which is currently set as 1.
+	///
+	/// Returns the new delivery fee factor after the decrement.
+	pub(crate) fn decrement_fee_factor(para: ParaId) -> FixedU128 {
+		<DeliveryFeeFactor<T>>::mutate(para, |f| {
+			*f = InitialFactor::get().max(*f / EXPONENTIAL_FEE_BASE);
+			*f
+		})
+	}
+}
+
+impl<T: Config> FeeTracker for Pallet<T> {
+	fn get_fee_factor(para: ParaId) -> FixedU128 {
+		DeliveryFeeFactor::<T>::get(para)
+	}
 }
diff --git a/polkadot/runtime/parachains/src/dmp/tests.rs b/polkadot/runtime/parachains/src/dmp/tests.rs
index 6e05f6c735ed54de051a71ac84e2dd71834c54f5..234f5f7e43c73aefe6fee4efb832d7a8855723bd 100644
--- a/polkadot/runtime/parachains/src/dmp/tests.rs
+++ b/polkadot/runtime/parachains/src/dmp/tests.rs
@@ -15,7 +15,11 @@
 // along with Polkadot.  If not, see <http://www.gnu.org/licenses/>.
 
 use super::*;
-use crate::mock::{new_test_ext, Configuration, Dmp, MockGenesisConfig, Paras, System, Test};
+use crate::{
+	configuration::ActiveConfig,
+	mock::{new_test_ext, Configuration, Dmp, MockGenesisConfig, Paras, System, Test},
+};
+use frame_support::assert_ok;
 use hex_literal::hex;
 use parity_scale_codec::Encode;
 use primitives::BlockNumber;
@@ -205,3 +209,69 @@ fn verify_dmq_mqc_head_is_externally_accessible() {
 		);
 	});
 }
+
+#[test]
+fn verify_fee_increment_and_decrement() {
+	let a = ParaId::from(123);
+	let mut genesis = default_genesis_config();
+	genesis.configuration.config.max_downward_message_size = 16777216;
+	new_test_ext(genesis).execute_with(|| {
+		let initial = InitialFactor::get();
+		assert_eq!(DeliveryFeeFactor::<Test>::get(a), initial);
+
+		// Under fee limit
+		queue_downward_message(a, vec![1]).unwrap();
+		assert_eq!(DeliveryFeeFactor::<Test>::get(a), initial);
+
+		// Limit reached so fee is increased
+		queue_downward_message(a, vec![1]).unwrap();
+		let result = InitialFactor::get().saturating_mul(EXPONENTIAL_FEE_BASE);
+		assert_eq!(DeliveryFeeFactor::<Test>::get(a), result);
+
+		Dmp::prune_dmq(a, 1);
+		assert_eq!(DeliveryFeeFactor::<Test>::get(a), initial);
+
+		// 10 Kb message adds additional 0.001 per KB fee factor
+		let big_message = [0; 10240].to_vec();
+		let msg_len_in_kb = big_message.len().saturating_div(1024) as u32;
+		let result = initial.saturating_mul(
+			EXPONENTIAL_FEE_BASE +
+				MESSAGE_SIZE_FEE_BASE.saturating_mul(FixedU128::from_u32(msg_len_in_kb)),
+		);
+		queue_downward_message(a, big_message).unwrap();
+		assert_eq!(DeliveryFeeFactor::<Test>::get(a), result);
+
+		queue_downward_message(a, vec![1]).unwrap();
+		let result = result.saturating_mul(EXPONENTIAL_FEE_BASE);
+		assert_eq!(DeliveryFeeFactor::<Test>::get(a), result);
+
+		Dmp::prune_dmq(a, 3);
+		let result = result / EXPONENTIAL_FEE_BASE;
+		assert_eq!(DeliveryFeeFactor::<Test>::get(a), result);
+		assert_eq!(Dmp::dmq_length(a), 0);
+
+		// Messages under limit will keep decreasing fee factor until base fee factor is reached
+		queue_downward_message(a, vec![1]).unwrap();
+		Dmp::prune_dmq(a, 1);
+		queue_downward_message(a, vec![1]).unwrap();
+		Dmp::prune_dmq(a, 1);
+		assert_eq!(DeliveryFeeFactor::<Test>::get(a), initial);
+	});
+}
+
+#[test]
+fn verify_fee_factor_reaches_high_value() {
+	let a = ParaId::from(123);
+	let mut genesis = default_genesis_config();
+	genesis.configuration.config.max_downward_message_size = 51200;
+	new_test_ext(genesis).execute_with(|| {
+		let max_messages =
+			Dmp::dmq_max_length(ActiveConfig::<Test>::get().max_downward_message_size);
+		let mut total_fee_factor = FixedU128::from_float(1.0);
+		for _ in 1..max_messages {
+			assert_ok!(queue_downward_message(a, vec![]));
+			total_fee_factor = total_fee_factor + (DeliveryFeeFactor::<Test>::get(a));
+		}
+		assert!(total_fee_factor > FixedU128::from_u32(100_000_000));
+	});
+}
diff --git a/polkadot/runtime/parachains/src/lib.rs b/polkadot/runtime/parachains/src/lib.rs
index ad884802ff190e717bafd43ebd18e85bda7f6cd3..9b2a49d349867834f410f92d46ea9c6f17e97358 100644
--- a/polkadot/runtime/parachains/src/lib.rs
+++ b/polkadot/runtime/parachains/src/lib.rs
@@ -51,7 +51,12 @@ mod mock;
 pub use origin::{ensure_parachain, Origin};
 pub use paras::ParaLifecycle;
 use primitives::{HeadData, Id as ParaId, ValidationCode};
-use sp_runtime::DispatchResult;
+use sp_runtime::{DispatchResult, FixedU128};
+
+/// Trait for tracking message delivery fees on a transport protocol.
+pub trait FeeTracker {
+	fn get_fee_factor(para: ParaId) -> FixedU128;
+}
 
 /// Schedule a para to be initialized at the start of the next session with the given genesis data.
 ///
diff --git a/polkadot/runtime/polkadot/Cargo.toml b/polkadot/runtime/polkadot/Cargo.toml
index 6163ef12e390ff57c235c3f0144741c7e50433c2..56f835ea5a47d4f846b063ed865329e8171c5d57 100644
--- a/polkadot/runtime/polkadot/Cargo.toml
+++ b/polkadot/runtime/polkadot/Cargo.toml
@@ -23,11 +23,11 @@ block-builder-api = { package = "sp-block-builder", git = "https://github.com/pa
 inherents = { package = "sp-inherents", git = "https://github.com/paritytech/substrate", branch = "master", default-features = false }
 offchain-primitives = { package = "sp-offchain", git = "https://github.com/paritytech/substrate", branch = "master", default-features = false }
 tx-pool-api = { package = "sp-transaction-pool", git = "https://github.com/paritytech/substrate", branch = "master", default-features = false }
+sp-arithmetic = { git = "https://github.com/paritytech/substrate", branch = "master", default-features = false }
 sp-api = { git = "https://github.com/paritytech/substrate", branch = "master", default-features = false }
 sp-std = { git = "https://github.com/paritytech/substrate", branch = "master", default-features = false }
 sp-io = { git = "https://github.com/paritytech/substrate", branch = "master", default-features = false }
 sp-mmr-primitives = { git = "https://github.com/paritytech/substrate", branch = "master", default-features = false }
-sp-arithmetic = { git = "https://github.com/paritytech/substrate", branch = "master", default-features = false }
 sp-runtime = { git = "https://github.com/paritytech/substrate", branch = "master", default-features = false }
 sp-staking = { git = "https://github.com/paritytech/substrate", branch = "master", default-features = false }
 sp-core = { git = "https://github.com/paritytech/substrate", branch = "master", default-features = false }
@@ -124,6 +124,7 @@ std = [
 	"parity-scale-codec/std",
 	"scale-info/std",
 	"inherents/std",
+	"sp-arithmetic/std",
 	"sp-core/std",
 	"sp-api/std",
 	"tx-pool-api/std",
diff --git a/polkadot/runtime/polkadot/src/lib.rs b/polkadot/runtime/polkadot/src/lib.rs
index fe5cafd0ee15f3148e1e0ae6928d27cdf36b4c9f..ec68400dca864c8eb34d86b120910e0214b945fd 100644
--- a/polkadot/runtime/polkadot/src/lib.rs
+++ b/polkadot/runtime/polkadot/src/lib.rs
@@ -1367,7 +1367,7 @@ construct_runtime! {
 		ParaScheduler: parachains_scheduler::{Pallet, Storage} = 55,
 		Paras: parachains_paras::{Pallet, Call, Storage, Event, Config, ValidateUnsigned} = 56,
 		Initializer: parachains_initializer::{Pallet, Call, Storage} = 57,
-		Dmp: parachains_dmp::{Pallet, Call, Storage} = 58,
+		Dmp: parachains_dmp::{Pallet, Storage} = 58,
 		Ump: parachains_ump::{Pallet, Call, Storage, Event} = 59,
 		Hrmp: parachains_hrmp::{Pallet, Call, Storage, Event<T>, Config} = 60,
 		ParaSessionInfo: parachains_session_info::{Pallet, Storage} = 61,
diff --git a/polkadot/runtime/polkadot/src/xcm_config.rs b/polkadot/runtime/polkadot/src/xcm_config.rs
index 5aa841564bf5cd5ab91803ce3708584a5bb39391..573798a873d29e3697bccdb500bfbe8236dfd949 100644
--- a/polkadot/runtime/polkadot/src/xcm_config.rs
+++ b/polkadot/runtime/polkadot/src/xcm_config.rs
@@ -17,9 +17,9 @@
 //! XCM configuration for Polkadot.
 
 use super::{
-	parachains_origin, AccountId, AllPalletsWithSystem, Balances, CouncilCollective,
+	parachains_origin, AccountId, AllPalletsWithSystem, Balances, CouncilCollective, Dmp,
 	FellowshipAdmin, ParaId, Runtime, RuntimeCall, RuntimeEvent, RuntimeOrigin, StakingAdmin,
-	WeightToFee, XcmPallet,
+	TransactionByteFee, WeightToFee, XcmPallet,
 };
 use frame_support::{
 	match_types, parameter_types,
@@ -28,8 +28,14 @@ use frame_support::{
 };
 use frame_system::EnsureRoot;
 use pallet_xcm::XcmPassthrough;
-use polkadot_runtime_constants::{system_parachain::*, xcm::body::FELLOWSHIP_ADMIN_INDEX};
-use runtime_common::{crowdloan, paras_registrar, xcm_sender, ToAuthor};
+use polkadot_runtime_constants::{
+	currency::CENTS, system_parachain::*, xcm::body::FELLOWSHIP_ADMIN_INDEX,
+};
+use runtime_common::{
+	crowdloan, paras_registrar,
+	xcm_sender::{ChildParachainRouter, ExponentialPrice},
+	ToAuthor,
+};
 use sp_core::ConstU32;
 use xcm::latest::prelude::*;
 use xcm_builder::{
@@ -106,13 +112,21 @@ parameter_types! {
 	/// Maximum number of instructions in a single XCM fragment. A sanity check against weight
 	/// calculations getting too crazy.
 	pub const MaxInstructions: u32 = 100;
+	/// The asset ID for the asset that we use to pay for message delivery fees.
+	pub FeeAssetId: AssetId = Concrete(TokenLocation::get());
+	/// The base fee for the message delivery fees.
+	pub const BaseDeliveryFee: u128 = CENTS.saturating_mul(3);
 }
 
 /// The XCM router. When we want to send an XCM message, we use this type. It amalgamates all of our
 /// individual routers.
 pub type XcmRouter = (
 	// Only one router so far - use DMP to communicate with child parachains.
-	xcm_sender::ChildParachainRouter<Runtime, XcmPallet, ()>,
+	ChildParachainRouter<
+		Runtime,
+		XcmPallet,
+		ExponentialPrice<FeeAssetId, BaseDeliveryFee, TransactionByteFee, Dmp>,
+	>,
 );
 
 parameter_types! {
diff --git a/polkadot/runtime/rococo/src/lib.rs b/polkadot/runtime/rococo/src/lib.rs
index 253f3ba476400a6dabd4e40458429a2a30ae9c95..a3a32e48d39db9e61e5a419923007d68db669512 100644
--- a/polkadot/runtime/rococo/src/lib.rs
+++ b/polkadot/runtime/rococo/src/lib.rs
@@ -1412,7 +1412,7 @@ construct_runtime! {
 		ParaScheduler: parachains_scheduler::{Pallet, Storage} = 55,
 		Paras: parachains_paras::{Pallet, Call, Storage, Event, Config, ValidateUnsigned} = 56,
 		Initializer: parachains_initializer::{Pallet, Call, Storage} = 57,
-		Dmp: parachains_dmp::{Pallet, Call, Storage} = 58,
+		Dmp: parachains_dmp::{Pallet, Storage} = 58,
 		Ump: parachains_ump::{Pallet, Call, Storage, Event} = 59,
 		Hrmp: parachains_hrmp::{Pallet, Call, Storage, Event<T>, Config} = 60,
 		ParaSessionInfo: parachains_session_info::{Pallet, Storage} = 61,
diff --git a/polkadot/runtime/rococo/src/xcm_config.rs b/polkadot/runtime/rococo/src/xcm_config.rs
index 872bbbe1a035f72f02c27d3d1d9bd5f2d730e7cd..de3bdfde4c0b13bce0396e42904ca1232e251dd9 100644
--- a/polkadot/runtime/rococo/src/xcm_config.rs
+++ b/polkadot/runtime/rococo/src/xcm_config.rs
@@ -17,8 +17,8 @@
 //! XCM configuration for Rococo.
 
 use super::{
-	parachains_origin, AccountId, AllPalletsWithSystem, Balances, CouncilCollective, ParaId,
-	Runtime, RuntimeCall, RuntimeEvent, RuntimeOrigin, WeightToFee, XcmPallet,
+	parachains_origin, AccountId, AllPalletsWithSystem, Balances, CouncilCollective, Dmp, ParaId,
+	Runtime, RuntimeCall, RuntimeEvent, RuntimeOrigin, TransactionByteFee, WeightToFee, XcmPallet,
 };
 use frame_support::{
 	match_types, parameter_types,
@@ -26,7 +26,12 @@ use frame_support::{
 	weights::Weight,
 };
 use frame_system::EnsureRoot;
-use runtime_common::{crowdloan, paras_registrar, xcm_sender, ToAuthor};
+use rococo_runtime_constants::currency::CENTS;
+use runtime_common::{
+	crowdloan, paras_registrar,
+	xcm_sender::{ChildParachainRouter, ExponentialPrice},
+	ToAuthor,
+};
 use sp_core::ConstU32;
 use xcm::latest::prelude::*;
 use xcm_builder::{
@@ -82,12 +87,21 @@ type LocalOriginConverter = (
 parameter_types! {
 	/// The amount of weight an XCM operation takes. This is a safe overestimate.
 	pub const BaseXcmWeight: Weight = Weight::from_parts(1_000_000_000, 64 * 1024);
+	/// The asset ID for the asset that we use to pay for message delivery fees.
+	pub FeeAssetId: AssetId = Concrete(TokenLocation::get());
+	/// The base fee for the message delivery fees.
+	pub const BaseDeliveryFee: u128 = CENTS.saturating_mul(3);
 }
+
 /// The XCM router. When we want to send an XCM message, we use this type. It amalgamates all of our
 /// individual routers.
 pub type XcmRouter = (
 	// Only one router so far - use DMP to communicate with child parachains.
-	xcm_sender::ChildParachainRouter<Runtime, XcmPallet, ()>,
+	ChildParachainRouter<
+		Runtime,
+		XcmPallet,
+		ExponentialPrice<FeeAssetId, BaseDeliveryFee, TransactionByteFee, Dmp>,
+	>,
 );
 
 parameter_types! {
diff --git a/polkadot/runtime/test-runtime/src/lib.rs b/polkadot/runtime/test-runtime/src/lib.rs
index 616f1c955dc92f0be1bf7a61b3a692be0f71c768..fb114df2ee46d6b91583cf429ac8f5fb6295e61c 100644
--- a/polkadot/runtime/test-runtime/src/lib.rs
+++ b/polkadot/runtime/test-runtime/src/lib.rs
@@ -671,7 +671,7 @@ construct_runtime! {
 		ParaSessionInfo: parachains_session_info::{Pallet, Storage},
 		Hrmp: parachains_hrmp::{Pallet, Call, Storage, Event<T>},
 		Ump: parachains_ump::{Pallet, Call, Storage, Event},
-		Dmp: parachains_dmp::{Pallet, Call, Storage},
+		Dmp: parachains_dmp::{Pallet, Storage},
 		Xcm: pallet_xcm::{Pallet, Call, Event<T>, Origin},
 		ParasDisputes: parachains_disputes::{Pallet, Storage, Event<T>},
 
diff --git a/polkadot/runtime/westend/src/lib.rs b/polkadot/runtime/westend/src/lib.rs
index 9374d5a8c8efab883dc61f567e12762c493f1eef..e3d917a43a43dca496bedef7b819c014ef81a04f 100644
--- a/polkadot/runtime/westend/src/lib.rs
+++ b/polkadot/runtime/westend/src/lib.rs
@@ -1160,7 +1160,7 @@ construct_runtime! {
 		ParaScheduler: parachains_scheduler::{Pallet, Storage} = 46,
 		Paras: parachains_paras::{Pallet, Call, Storage, Event, Config, ValidateUnsigned} = 47,
 		Initializer: parachains_initializer::{Pallet, Call, Storage} = 48,
-		Dmp: parachains_dmp::{Pallet, Call, Storage} = 49,
+		Dmp: parachains_dmp::{Pallet, Storage} = 49,
 		Ump: parachains_ump::{Pallet, Call, Storage, Event} = 50,
 		Hrmp: parachains_hrmp::{Pallet, Call, Storage, Event<T>, Config} = 51,
 		ParaSessionInfo: parachains_session_info::{Pallet, Storage} = 52,
diff --git a/polkadot/runtime/westend/src/xcm_config.rs b/polkadot/runtime/westend/src/xcm_config.rs
index 24b161e42a017b7142af282daf6189ca17c47f86..148405c7e4e7c48b1975b9549262f8e072eea5c6 100644
--- a/polkadot/runtime/westend/src/xcm_config.rs
+++ b/polkadot/runtime/westend/src/xcm_config.rs
@@ -17,16 +17,21 @@
 //! XCM configurations for Westend.
 
 use super::{
-	parachains_origin, weights, AccountId, AllPalletsWithSystem, Balances, ParaId, Runtime,
-	RuntimeCall, RuntimeEvent, RuntimeOrigin, WeightToFee, XcmPallet,
+	parachains_origin, weights, AccountId, AllPalletsWithSystem, Balances, Dmp, ParaId, Runtime,
+	RuntimeCall, RuntimeEvent, RuntimeOrigin, TransactionByteFee, WeightToFee, XcmPallet,
 };
 use frame_support::{
 	parameter_types,
 	traits::{Contains, Everything, Nothing},
 };
 use frame_system::EnsureRoot;
-use runtime_common::{crowdloan, paras_registrar, xcm_sender, ToAuthor};
+use runtime_common::{
+	crowdloan, paras_registrar,
+	xcm_sender::{ChildParachainRouter, ExponentialPrice},
+	ToAuthor,
+};
 use sp_core::ConstU32;
+use westend_runtime_constants::currency::CENTS;
 use xcm::latest::prelude::*;
 use xcm_builder::{
 	AccountId32Aliases, AllowExplicitUnpaidExecutionFrom, AllowKnownQueryResponses,
@@ -44,6 +49,10 @@ parameter_types! {
 	pub UniversalLocation: InteriorMultiLocation = ThisNetwork::get().into();
 	pub CheckAccount: AccountId = XcmPallet::check_account();
 	pub LocalCheckAccount: (AccountId, MintLocation) = (CheckAccount::get(), MintLocation::Local);
+	/// The asset ID for the asset that we use to pay for message delivery fees.
+	pub FeeAssetId: AssetId = Concrete(TokenLocation::get());
+	/// The base fee for the message delivery fees.
+	pub const BaseDeliveryFee: u128 = CENTS.saturating_mul(3);
 }
 
 pub type LocationConverter =
@@ -73,7 +82,11 @@ type LocalOriginConverter = (
 /// individual routers.
 pub type XcmRouter = (
 	// Only one router so far - use DMP to communicate with child parachains.
-	xcm_sender::ChildParachainRouter<Runtime, XcmPallet, ()>,
+	ChildParachainRouter<
+		Runtime,
+		XcmPallet,
+		ExponentialPrice<FeeAssetId, BaseDeliveryFee, TransactionByteFee, Dmp>,
+	>,
 );
 
 parameter_types! {
diff --git a/polkadot/xcm/pallet-xcm/src/lib.rs b/polkadot/xcm/pallet-xcm/src/lib.rs
index 5af903ca37b37f0390b4e14cca13930cf5475e2a..cd6fb6321a8e7e6ecba30bbd7a247033abaf3dbf 100644
--- a/polkadot/xcm/pallet-xcm/src/lib.rs
+++ b/polkadot/xcm/pallet-xcm/src/lib.rs
@@ -1146,7 +1146,10 @@ impl<T: Config> Pallet<T> {
 			BuyExecution { fees, weight_limit },
 			DepositAsset { assets: Wild(AllCounted(max_assets)), beneficiary },
 		]);
-		let mut message = Xcm(vec![TransferReserveAsset { assets, dest, xcm }]);
+		let mut message = Xcm(vec![
+			SetFeesMode { jit_withdraw: true },
+			TransferReserveAsset { assets, dest, xcm },
+		]);
 		let weight =
 			T::Weigher::weight(&mut message).map_err(|()| Error::<T>::UnweighableMessage)?;
 		let hash = message.using_encoded(sp_io::hashing::blake2_256);
@@ -1205,6 +1208,7 @@ impl<T: Config> Pallet<T> {
 		]);
 		let mut message = Xcm(vec![
 			WithdrawAsset(assets),
+			SetFeesMode { jit_withdraw: true },
 			InitiateTeleport { assets: Wild(AllCounted(max_assets)), dest, xcm },
 		]);
 		let weight =
diff --git a/polkadot/xcm/pallet-xcm/src/tests.rs b/polkadot/xcm/pallet-xcm/src/tests.rs
index 919eede0fe4ada437f96697848a953fd2f31ec2e..ae359116e02316a97a0443607eadccfe156a3a7a 100644
--- a/polkadot/xcm/pallet-xcm/src/tests.rs
+++ b/polkadot/xcm/pallet-xcm/src/tests.rs
@@ -349,7 +349,7 @@ fn teleport_assets_works() {
 		(ParaId::from(PARA_ID).into_account_truncating(), INITIAL_BALANCE),
 	];
 	new_test_ext_with_balances(balances).execute_with(|| {
-		let weight = BaseXcmWeight::get() * 2;
+		let weight = BaseXcmWeight::get() * 3;
 		assert_eq!(Balances::total_balance(&ALICE), INITIAL_BALANCE);
 		let dest: MultiLocation = AccountId32 { network: None, id: BOB.into() }.into();
 		assert_ok!(XcmPallet::teleport_assets(
@@ -392,7 +392,7 @@ fn limited_teleport_assets_works() {
 		(ParaId::from(PARA_ID).into_account_truncating(), INITIAL_BALANCE),
 	];
 	new_test_ext_with_balances(balances).execute_with(|| {
-		let weight = BaseXcmWeight::get() * 2;
+		let weight = BaseXcmWeight::get() * 3;
 		assert_eq!(Balances::total_balance(&ALICE), INITIAL_BALANCE);
 		let dest: MultiLocation = AccountId32 { network: None, id: BOB.into() }.into();
 		assert_ok!(XcmPallet::limited_teleport_assets(
@@ -436,7 +436,7 @@ fn unlimited_teleport_assets_works() {
 		(ParaId::from(PARA_ID).into_account_truncating(), INITIAL_BALANCE),
 	];
 	new_test_ext_with_balances(balances).execute_with(|| {
-		let weight = BaseXcmWeight::get() * 2;
+		let weight = BaseXcmWeight::get() * 3;
 		assert_eq!(Balances::total_balance(&ALICE), INITIAL_BALANCE);
 		let dest: MultiLocation = AccountId32 { network: None, id: BOB.into() }.into();
 		assert_ok!(XcmPallet::limited_teleport_assets(
@@ -478,7 +478,7 @@ fn reserve_transfer_assets_works() {
 		(ParaId::from(PARA_ID).into_account_truncating(), INITIAL_BALANCE),
 	];
 	new_test_ext_with_balances(balances).execute_with(|| {
-		let weight = BaseXcmWeight::get();
+		let weight = BaseXcmWeight::get() * 2;
 		let dest: MultiLocation = Junction::AccountId32 { network: None, id: ALICE.into() }.into();
 		assert_eq!(Balances::total_balance(&ALICE), INITIAL_BALANCE);
 		assert_ok!(XcmPallet::reserve_transfer_assets(
@@ -525,7 +525,7 @@ fn limited_reserve_transfer_assets_works() {
 		(ParaId::from(PARA_ID).into_account_truncating(), INITIAL_BALANCE),
 	];
 	new_test_ext_with_balances(balances).execute_with(|| {
-		let weight = BaseXcmWeight::get();
+		let weight = BaseXcmWeight::get() * 2;
 		let dest: MultiLocation = Junction::AccountId32 { network: None, id: ALICE.into() }.into();
 		assert_eq!(Balances::total_balance(&ALICE), INITIAL_BALANCE);
 		assert_ok!(XcmPallet::limited_reserve_transfer_assets(
@@ -573,7 +573,7 @@ fn unlimited_reserve_transfer_assets_works() {
 		(ParaId::from(PARA_ID).into_account_truncating(), INITIAL_BALANCE),
 	];
 	new_test_ext_with_balances(balances).execute_with(|| {
-		let weight = BaseXcmWeight::get();
+		let weight = BaseXcmWeight::get() * 2;
 		let dest: MultiLocation = Junction::AccountId32 { network: None, id: ALICE.into() }.into();
 		assert_eq!(Balances::total_balance(&ALICE), INITIAL_BALANCE);
 		assert_ok!(XcmPallet::limited_reserve_transfer_assets(
diff --git a/polkadot/xcm/src/v3/mod.rs b/polkadot/xcm/src/v3/mod.rs
index 39a7ae32ee1ea05b6fbe21535b925afbd42fb999..ff94c1392fea71ffd080215e55268a3d62396599 100644
--- a/polkadot/xcm/src/v3/mod.rs
+++ b/polkadot/xcm/src/v3/mod.rs
@@ -925,7 +925,7 @@ pub enum Instruction<Call> {
 	/// asset to be transferred.
 	///
 	/// - `asset`: The asset to be unlocked.
-	/// - `owner`: The owner of the asset on the local chain.
+	/// - `target`: The owner of the asset on the local chain.
 	///
 	/// Safety: No concerns.
 	///