diff --git a/polkadot/xcm/xcm-builder/src/lib.rs b/polkadot/xcm/xcm-builder/src/lib.rs
index 3d68d8ed16ae1459f7ac2dd39f1df80ecb44131e..e23412a97ebcb3e6f2999d0f48d555e53d02503e 100644
--- a/polkadot/xcm/xcm-builder/src/lib.rs
+++ b/polkadot/xcm/xcm-builder/src/lib.rs
@@ -132,11 +132,13 @@ pub use routing::{
 mod transactional;
 pub use transactional::FrameTransactionalProcessor;
 
+#[allow(deprecated)]
+pub use universal_exports::UnpaidLocalExporter;
 mod universal_exports;
 pub use universal_exports::{
 	ensure_is_remote, BridgeBlobDispatcher, BridgeMessage, DispatchBlob, DispatchBlobError,
-	ExporterFor, HaulBlob, HaulBlobError, HaulBlobExporter, NetworkExportTable,
-	NetworkExportTableItem, SovereignPaidRemoteExporter, UnpaidLocalExporter, UnpaidRemoteExporter,
+	ExporterFor, HaulBlob, HaulBlobError, HaulBlobExporter, LocalExporter, NetworkExportTable,
+	NetworkExportTableItem, SovereignPaidRemoteExporter, UnpaidRemoteExporter,
 };
 
 mod weight;
diff --git a/polkadot/xcm/xcm-builder/src/tests/bridging/local_para_para.rs b/polkadot/xcm/xcm-builder/src/tests/bridging/local_para_para.rs
index ea584bf9d485a25c38ce0b673259b6ed8df7368f..5e930fe575c2b07c3c8d172f478d7f20ff519e76 100644
--- a/polkadot/xcm/xcm-builder/src/tests/bridging/local_para_para.rs
+++ b/polkadot/xcm/xcm-builder/src/tests/bridging/local_para_para.rs
@@ -28,7 +28,7 @@ parameter_types! {
 type TheBridge =
 	TestBridge<BridgeBlobDispatcher<TestRemoteIncomingRouter, RemoteUniversalLocation, ()>>;
 type Router = TestTopic<
-	UnpaidLocalExporter<
+	LocalExporter<
 		HaulBlobExporter<TheBridge, RemoteNetwork, AlwaysLatest, Price>,
 		UniversalLocation,
 	>,
diff --git a/polkadot/xcm/xcm-builder/src/tests/bridging/local_relay_relay.rs b/polkadot/xcm/xcm-builder/src/tests/bridging/local_relay_relay.rs
index 38ffe2532d580a017efc21043e7313028700c046..a41f09721812a99f71fdcb58345e265472b163c6 100644
--- a/polkadot/xcm/xcm-builder/src/tests/bridging/local_relay_relay.rs
+++ b/polkadot/xcm/xcm-builder/src/tests/bridging/local_relay_relay.rs
@@ -28,7 +28,7 @@ parameter_types! {
 type TheBridge =
 	TestBridge<BridgeBlobDispatcher<TestRemoteIncomingRouter, RemoteUniversalLocation, ()>>;
 type Router = TestTopic<
-	UnpaidLocalExporter<
+	LocalExporter<
 		HaulBlobExporter<TheBridge, RemoteNetwork, AlwaysLatest, Price>,
 		UniversalLocation,
 	>,
diff --git a/polkadot/xcm/xcm-builder/src/tests/bridging/mod.rs b/polkadot/xcm/xcm-builder/src/tests/bridging/mod.rs
index 767575e7f2dd9efa29cc1441a8cc2bf2cdaf3d19..90ad9921d65a13b8b5590507d0a7c36ba79b063a 100644
--- a/polkadot/xcm/xcm-builder/src/tests/bridging/mod.rs
+++ b/polkadot/xcm/xcm-builder/src/tests/bridging/mod.rs
@@ -209,7 +209,7 @@ impl<Local: Get<Junctions>, Remote: Get<Junctions>, RemoteExporter: ExportXcm> S
 		let origin = Local::get().relative_to(&Remote::get());
 		AllowUnpaidFrom::set(vec![origin.clone()]);
 		set_exporter_override(price::<RemoteExporter>, deliver::<RemoteExporter>);
-		// The we execute it:
+		// Then we execute it:
 		let mut id = fake_id();
 		let outcome = XcmExecutor::<TestConfig>::prepare_and_execute(
 			origin,
diff --git a/polkadot/xcm/xcm-builder/src/universal_exports.rs b/polkadot/xcm/xcm-builder/src/universal_exports.rs
index 6b3c3adf737dbb6e938d59383c5100041c493d52..e215aea3ab6858a0bcecd5b952cb4fd1384f52a9 100644
--- a/polkadot/xcm/xcm-builder/src/universal_exports.rs
+++ b/polkadot/xcm/xcm-builder/src/universal_exports.rs
@@ -16,6 +16,8 @@
 
 //! Traits and utilities to help with origin mutation and bridging.
 
+#![allow(deprecated)]
+
 use crate::InspectMessageQueues;
 use alloc::{vec, vec::Vec};
 use codec::{Decode, Encode};
@@ -58,6 +60,8 @@ pub fn ensure_is_remote(
 /// that the message sending cannot be abused in any way.
 ///
 /// This is only useful when the local chain has bridging capabilities.
+#[deprecated(note = "Will be removed after July 2025; It uses hard-coded channel `0`, \
+	use `xcm_builder::LocalExporter` directly instead.")]
 pub struct UnpaidLocalExporter<Exporter, UniversalLocation>(
 	PhantomData<(Exporter, UniversalLocation)>,
 );
@@ -100,6 +104,54 @@ impl<Exporter: ExportXcm, UniversalLocation: Get<InteriorLocation>> SendXcm
 	fn ensure_successful_delivery(_: Option<Location>) {}
 }
 
+/// Implementation of `SendXcm` which uses the given `ExportXcm` implementation in order to forward
+/// the message over a bridge.
+///
+/// This is only useful when the local chain has bridging capabilities.
+pub struct LocalExporter<Exporter, UniversalLocation>(PhantomData<(Exporter, UniversalLocation)>);
+impl<Exporter: ExportXcm, UniversalLocation: Get<InteriorLocation>> SendXcm
+	for LocalExporter<Exporter, UniversalLocation>
+{
+	type Ticket = Exporter::Ticket;
+
+	fn validate(
+		dest: &mut Option<Location>,
+		msg: &mut Option<Xcm<()>>,
+	) -> SendResult<Exporter::Ticket> {
+		// This `clone` ensures that `dest` is not consumed in any case.
+		let d = dest.clone().take().ok_or(MissingArgument)?;
+		let universal_source = UniversalLocation::get();
+		let devolved = ensure_is_remote(universal_source.clone(), d).map_err(|_| NotApplicable)?;
+		let (remote_network, remote_location) = devolved;
+		let xcm = msg.take().ok_or(MissingArgument)?;
+
+		let hash =
+			(Some(Location::here()), &remote_location).using_encoded(sp_io::hashing::blake2_128);
+		let channel = u32::decode(&mut hash.as_ref()).unwrap_or(0);
+
+		validate_export::<Exporter>(
+			remote_network,
+			channel,
+			universal_source,
+			remote_location,
+			xcm.clone(),
+		)
+		.inspect_err(|err| {
+			if let NotApplicable = err {
+				// We need to make sure that msg is not consumed in case of `NotApplicable`.
+				*msg = Some(xcm);
+			}
+		})
+	}
+
+	fn deliver(ticket: Exporter::Ticket) -> Result<XcmHash, SendError> {
+		Exporter::deliver(ticket)
+	}
+
+	#[cfg(feature = "runtime-benchmarks")]
+	fn ensure_successful_delivery(_: Option<Location>) {}
+}
+
 pub trait ExporterFor {
 	/// Return the locally-routable bridge (if any) capable of forwarding `message` to the
 	/// `remote_location` on the remote `network`, together with the payment which is required.
@@ -703,9 +755,9 @@ mod tests {
 		let local_dest: Location = (Parent, Parachain(5678)).into();
 		assert!(ensure_is_remote(UniversalLocation::get(), local_dest.clone()).is_err());
 
-		// UnpaidLocalExporter
+		// LocalExporter
 		ensure_validate_does_not_consume_dest_or_msg::<
-			UnpaidLocalExporter<RoutableBridgeExporter, UniversalLocation>,
+			LocalExporter<RoutableBridgeExporter, UniversalLocation>,
 		>(local_dest.clone(), |result| assert_eq!(Err(NotApplicable), result));
 
 		// 2. check with not applicable from the inner router (using `NotApplicableBridgeSender`)
@@ -713,14 +765,14 @@ mod tests {
 			(Parent, Parent, DifferentRemote::get(), RemoteDestination::get()).into();
 		assert!(ensure_is_remote(UniversalLocation::get(), remote_dest.clone()).is_ok());
 
-		// UnpaidLocalExporter
+		// LocalExporter
 		ensure_validate_does_not_consume_dest_or_msg::<
-			UnpaidLocalExporter<NotApplicableBridgeExporter, UniversalLocation>,
+			LocalExporter<NotApplicableBridgeExporter, UniversalLocation>,
 		>(remote_dest.clone(), |result| assert_eq!(Err(NotApplicable), result));
 
 		// 3. Ok - deliver
 		// UnpaidRemoteExporter
-		assert_ok!(send_xcm::<UnpaidLocalExporter<RoutableBridgeExporter, UniversalLocation>>(
+		assert_ok!(send_xcm::<LocalExporter<RoutableBridgeExporter, UniversalLocation>>(
 			remote_dest,
 			Xcm::default()
 		));
diff --git a/prdoc/pr_7126.prdoc b/prdoc/pr_7126.prdoc
new file mode 100644
index 0000000000000000000000000000000000000000..1a86af1b2d1da8ade29829a01f83753637e915dd
--- /dev/null
+++ b/prdoc/pr_7126.prdoc
@@ -0,0 +1,7 @@
+title: 'xcm: Fixes for `UnpaidLocalExporter`'
+doc:
+- audience: Runtime Dev
+  description: This PR deprecates `UnpaidLocalExporter` in favor of the new `LocalExporter`. First, the name is misleading, as it can be used in both paid and unpaid scenarios. Second, it contains a hard-coded channel 0, whereas `LocalExporter` uses the same algorithm as `xcm-exporter`.
+crates:
+- name: staging-xcm-builder
+  bump: minor