diff --git a/cumulus/parachains/runtimes/assets/asset-hub-rococo/src/lib.rs b/cumulus/parachains/runtimes/assets/asset-hub-rococo/src/lib.rs
index f09647854cd0132751889ef2b33bca1c5ec16e74..83bc12cf9b41fc887451fe672ca65f6bf056257f 100644
--- a/cumulus/parachains/runtimes/assets/asset-hub-rococo/src/lib.rs
+++ b/cumulus/parachains/runtimes/assets/asset-hub-rococo/src/lib.rs
@@ -71,7 +71,7 @@ use frame_system::{
 	limits::{BlockLength, BlockWeights},
 	EnsureRoot, EnsureSigned, EnsureSignedBy,
 };
-use pallet_asset_conversion_tx_payment::AssetConversionAdapter;
+use pallet_asset_conversion_tx_payment::SwapAssetAdapter;
 use pallet_nfts::PalletFeatures;
 use parachains_common::{
 	impls::DealWithFees,
@@ -798,11 +798,19 @@ impl pallet_collator_selection::Config for Runtime {
 	type WeightInfo = weights::pallet_collator_selection::WeightInfo<Runtime>;
 }
 
+parameter_types! {
+	pub StakingPot: AccountId = CollatorSelection::account_id();
+}
+
 impl pallet_asset_conversion_tx_payment::Config for Runtime {
 	type RuntimeEvent = RuntimeEvent;
-	type Fungibles = LocalAndForeignAssets;
-	type OnChargeAssetTransaction =
-		AssetConversionAdapter<Balances, AssetConversion, TokenLocationV3>;
+	type AssetId = xcm::v3::Location;
+	type OnChargeAssetTransaction = SwapAssetAdapter<
+		TokenLocationV3,
+		NativeAndAssets,
+		AssetConversion,
+		ResolveAssetTo<StakingPot, NativeAndAssets>,
+	>;
 }
 
 parameter_types! {
diff --git a/cumulus/parachains/runtimes/assets/asset-hub-westend/src/lib.rs b/cumulus/parachains/runtimes/assets/asset-hub-westend/src/lib.rs
index 178b886fc3e84b741eb6721fec1b4e493626795b..2d9de07f251f9b157f01cb3c05c63b7bb55ba5f6 100644
--- a/cumulus/parachains/runtimes/assets/asset-hub-westend/src/lib.rs
+++ b/cumulus/parachains/runtimes/assets/asset-hub-westend/src/lib.rs
@@ -55,7 +55,7 @@ use frame_system::{
 	limits::{BlockLength, BlockWeights},
 	EnsureRoot, EnsureSigned, EnsureSignedBy,
 };
-use pallet_asset_conversion_tx_payment::AssetConversionAdapter;
+use pallet_asset_conversion_tx_payment::SwapAssetAdapter;
 use pallet_nfts::{DestroyWitness, PalletFeatures};
 use pallet_xcm::EnsureXcm;
 use parachains_common::{
@@ -787,11 +787,19 @@ impl pallet_collator_selection::Config for Runtime {
 	type WeightInfo = weights::pallet_collator_selection::WeightInfo<Runtime>;
 }
 
+parameter_types! {
+	pub StakingPot: AccountId = CollatorSelection::account_id();
+}
+
 impl pallet_asset_conversion_tx_payment::Config for Runtime {
 	type RuntimeEvent = RuntimeEvent;
-	type Fungibles = LocalAndForeignAssets;
-	type OnChargeAssetTransaction =
-		AssetConversionAdapter<Balances, AssetConversion, WestendLocationV3>;
+	type AssetId = xcm::v3::Location;
+	type OnChargeAssetTransaction = SwapAssetAdapter<
+		WestendLocationV3,
+		NativeAndAssets,
+		AssetConversion,
+		ResolveAssetTo<StakingPot, NativeAndAssets>,
+	>;
 }
 
 parameter_types! {
diff --git a/prdoc/pr_4488.prdoc b/prdoc/pr_4488.prdoc
new file mode 100644
index 0000000000000000000000000000000000000000..d0b6a877be6b1ecbdfbf94e364cc9c836cb8dd32
--- /dev/null
+++ b/prdoc/pr_4488.prdoc
@@ -0,0 +1,25 @@
+title: "Tx Payment: drop ED requirements for tx payments with exchangeable asset"
+
+doc:
+  - audience: Runtime Dev
+    description: |
+        Drop the Existential Deposit requirement for the asset amount exchangeable for the fee asset
+        (eg. DOT/KSM) during transaction payments.
+
+        This achieved by using `SwapCredit` implementation of asset conversion, which works with
+        imbalances and does not require a temporary balance account within the transaction payment.
+
+        This is a breaking change for the `pallet-asset-conversion-tx-payment` pallet, use examples 
+        from PR for the migration.
+
+crates:
+  - name: pallet-asset-conversion-tx-payment
+    bump: major
+  - name: pallet-transaction-payment
+    bump: patch
+  - name: pallet-asset-conversion
+    bump: patch
+  - name: asset-hub-rococo-runtime
+    bump: patch
+  - name: asset-hub-westend-runtime
+    bump: patch
diff --git a/substrate/bin/node/runtime/src/lib.rs b/substrate/bin/node/runtime/src/lib.rs
index cad2cc119f0d5dcb2d9b7bc6fa66250c971d42af..5d046539b0362b003b8d3aec23e17207a0b8dc84 100644
--- a/substrate/bin/node/runtime/src/lib.rs
+++ b/substrate/bin/node/runtime/src/lib.rs
@@ -69,6 +69,7 @@ use frame_system::{
 pub use node_primitives::{AccountId, Signature};
 use node_primitives::{AccountIndex, Balance, BlockNumber, Hash, Moment, Nonce};
 use pallet_asset_conversion::{AccountIdConverter, Ascending, Chain, WithFirstAsset};
+use pallet_asset_conversion_tx_payment::SwapAssetAdapter;
 use pallet_broker::{CoreAssignment, CoreIndex, CoretimeInterface, PartsOf57600};
 use pallet_election_provider_multi_phase::{GeometricDepositBase, SolutionAccuracyOf};
 use pallet_identity::legacy::IdentityInfo;
@@ -123,7 +124,7 @@ pub use sp_runtime::BuildStorage;
 pub mod impls;
 #[cfg(not(feature = "runtime-benchmarks"))]
 use impls::AllianceIdentityVerifier;
-use impls::{AllianceProposalProvider, Author, CreditToBlockAuthor};
+use impls::{AllianceProposalProvider, Author};
 
 /// Constant values used within the runtime.
 pub mod constants;
@@ -574,22 +575,14 @@ impl pallet_transaction_payment::Config for Runtime {
 	>;
 }
 
-impl pallet_asset_tx_payment::Config for Runtime {
-	type RuntimeEvent = RuntimeEvent;
-	type Fungibles = Assets;
-	type OnChargeAssetTransaction = pallet_asset_tx_payment::FungiblesAdapter<
-		pallet_assets::BalanceToAssetBalance<Balances, Runtime, ConvertInto, Instance1>,
-		CreditToBlockAuthor,
-	>;
-}
-
 impl pallet_asset_conversion_tx_payment::Config for Runtime {
 	type RuntimeEvent = RuntimeEvent;
-	type Fungibles = Assets;
-	type OnChargeAssetTransaction = pallet_asset_conversion_tx_payment::AssetConversionAdapter<
-		Balances,
-		AssetConversion,
+	type AssetId = NativeOrWithId<u32>;
+	type OnChargeAssetTransaction = SwapAssetAdapter<
 		Native,
+		NativeAndAssets,
+		AssetConversion,
+		ResolveAssetTo<TreasuryAccount, NativeAndAssets>,
 	>;
 }
 
@@ -1705,12 +1698,15 @@ parameter_types! {
 	pub const Native: NativeOrWithId<u32> = NativeOrWithId::Native;
 }
 
+pub type NativeAndAssets =
+	UnionOf<Balances, Assets, NativeFromLeft, NativeOrWithId<u32>, AccountId>;
+
 impl pallet_asset_conversion::Config for Runtime {
 	type RuntimeEvent = RuntimeEvent;
 	type Balance = u128;
 	type HigherPrecisionBalance = sp_core::U256;
 	type AssetKind = NativeOrWithId<u32>;
-	type Assets = UnionOf<Balances, Assets, NativeFromLeft, NativeOrWithId<u32>, AccountId>;
+	type Assets = NativeAndAssets;
 	type PoolId = (Self::AssetKind, Self::AssetKind);
 	type PoolLocator = Chain<
 		WithFirstAsset<
@@ -2259,9 +2255,6 @@ mod runtime {
 	#[runtime::pallet_index(7)]
 	pub type TransactionPayment = pallet_transaction_payment::Pallet<Runtime>;
 
-	#[runtime::pallet_index(8)]
-	pub type AssetTxPayment = pallet_asset_tx_payment::Pallet<Runtime>;
-
 	#[runtime::pallet_index(9)]
 	pub type AssetConversionTxPayment = pallet_asset_conversion_tx_payment::Pallet<Runtime>;
 
diff --git a/substrate/frame/asset-conversion/src/swap.rs b/substrate/frame/asset-conversion/src/swap.rs
index a6154e29414767550106544585b592903c3a6f2a..1485e7166f30eda340139ea5b889b359ca904230 100644
--- a/substrate/frame/asset-conversion/src/swap.rs
+++ b/substrate/frame/asset-conversion/src/swap.rs
@@ -112,6 +112,37 @@ pub trait SwapCredit<AccountId> {
 	) -> Result<(Self::Credit, Self::Credit), (Self::Credit, DispatchError)>;
 }
 
+/// Trait providing methods to quote swap prices between asset classes.
+///
+/// The quoted price is only guaranteed if no other swaps are made after the price is quoted and
+/// before the target swap (e.g., the swap is made immediately within the same transaction).
+pub trait QuotePrice {
+	/// Measurement units of the asset classes for pricing.
+	type Balance: Balance;
+	/// Type representing the kind of assets for which the price is being quoted.
+	type AssetKind;
+	/// Quotes the amount of `asset1` required to obtain the exact `amount` of `asset2`.
+	///
+	/// If `include_fee` is set to `true`, the price will include the pool's fee.
+	/// If the pool does not exist or the swap cannot be made, `None` is returned.
+	fn quote_price_tokens_for_exact_tokens(
+		asset1: Self::AssetKind,
+		asset2: Self::AssetKind,
+		amount: Self::Balance,
+		include_fee: bool,
+	) -> Option<Self::Balance>;
+	/// Quotes the amount of `asset2` resulting from swapping the exact `amount` of `asset1`.
+	///
+	/// If `include_fee` is set to `true`, the price will include the pool's fee.
+	/// If the pool does not exist or the swap cannot be made, `None` is returned.
+	fn quote_price_exact_tokens_for_tokens(
+		asset1: Self::AssetKind,
+		asset2: Self::AssetKind,
+		amount: Self::Balance,
+		include_fee: bool,
+	) -> Option<Self::Balance>;
+}
+
 impl<T: Config> Swap<T::AccountId> for Pallet<T> {
 	type Balance = T::Balance;
 	type AssetKind = T::AssetKind;
@@ -210,3 +241,24 @@ impl<T: Config> SwapCredit<T::AccountId> for Pallet<T> {
 		.map_err(|_| (Self::Credit::zero(credit_asset), DispatchError::Corruption))?
 	}
 }
+
+impl<T: Config> QuotePrice for Pallet<T> {
+	type Balance = T::Balance;
+	type AssetKind = T::AssetKind;
+	fn quote_price_exact_tokens_for_tokens(
+		asset1: Self::AssetKind,
+		asset2: Self::AssetKind,
+		amount: Self::Balance,
+		include_fee: bool,
+	) -> Option<Self::Balance> {
+		Self::quote_price_exact_tokens_for_tokens(asset1, asset2, amount, include_fee)
+	}
+	fn quote_price_tokens_for_exact_tokens(
+		asset1: Self::AssetKind,
+		asset2: Self::AssetKind,
+		amount: Self::Balance,
+		include_fee: bool,
+	) -> Option<Self::Balance> {
+		Self::quote_price_tokens_for_exact_tokens(asset1, asset2, amount, include_fee)
+	}
+}
diff --git a/substrate/frame/transaction-payment/asset-conversion-tx-payment/src/lib.rs b/substrate/frame/transaction-payment/asset-conversion-tx-payment/src/lib.rs
index 538d88bfacfaa6d5138ff7fca33e9b45cf1ee56c..825a35e621382d65ba60c804b258b866be6b8f1a 100644
--- a/substrate/frame/transaction-payment/asset-conversion-tx-payment/src/lib.rs
+++ b/substrate/frame/transaction-payment/asset-conversion-tx-payment/src/lib.rs
@@ -23,7 +23,7 @@
 //! This pallet provides a `SignedExtension` with an optional `AssetId` that specifies the asset
 //! to be used for payment (defaulting to the native token on `None`). It expects an
 //! [`OnChargeAssetTransaction`] implementation analogous to [`pallet-transaction-payment`]. The
-//! included [`AssetConversionAdapter`] (implementing [`OnChargeAssetTransaction`]) determines the
+//! included [`SwapAssetAdapter`] (implementing [`OnChargeAssetTransaction`]) determines the
 //! fee amount by converting the fee calculated by [`pallet-transaction-payment`] in the native
 //! asset into the amount required of the specified asset.
 //!
@@ -47,19 +47,14 @@ extern crate alloc;
 use codec::{Decode, Encode};
 use frame_support::{
 	dispatch::{DispatchInfo, DispatchResult, PostDispatchInfo},
-	traits::{
-		fungibles::{Balanced, Inspect},
-		IsType,
-	},
+	traits::IsType,
 	DefaultNoBound,
 };
 use pallet_transaction_payment::OnChargeTransaction;
 use scale_info::TypeInfo;
 use sp_runtime::{
 	traits::{DispatchInfoOf, Dispatchable, PostDispatchInfoOf, SignedExtension, Zero},
-	transaction_validity::{
-		InvalidTransaction, TransactionValidity, TransactionValidityError, ValidTransaction,
-	},
+	transaction_validity::{TransactionValidity, TransactionValidityError, ValidTransaction},
 };
 
 #[cfg(test)]
@@ -71,30 +66,19 @@ mod payment;
 use frame_support::traits::tokens::AssetId;
 pub use payment::*;
 
+/// Balance type alias for balances of the chain's native asset.
+pub(crate) type BalanceOf<T> = <OnChargeTransactionOf<T> as OnChargeTransaction<T>>::Balance;
+
 /// Type aliases used for interaction with `OnChargeTransaction`.
 pub(crate) type OnChargeTransactionOf<T> =
 	<T as pallet_transaction_payment::Config>::OnChargeTransaction;
-/// Balance type alias for balances of the chain's native asset.
-pub(crate) type BalanceOf<T> = <OnChargeTransactionOf<T> as OnChargeTransaction<T>>::Balance;
-/// Liquidity info type alias.
-pub(crate) type LiquidityInfoOf<T> =
-	<OnChargeTransactionOf<T> as OnChargeTransaction<T>>::LiquidityInfo;
 
-/// Balance type alias for balances of assets that implement the `fungibles` trait.
-pub(crate) type AssetBalanceOf<T> =
-	<<T as Config>::Fungibles as Inspect<<T as frame_system::Config>::AccountId>>::Balance;
-/// Type alias for Asset IDs.
-pub(crate) type AssetIdOf<T> =
-	<<T as Config>::Fungibles as Inspect<<T as frame_system::Config>::AccountId>>::AssetId;
+/// Liquidity info type alias for the chain's native asset.
+pub(crate) type NativeLiquidityInfoOf<T> =
+	<OnChargeTransactionOf<T> as OnChargeTransaction<T>>::LiquidityInfo;
 
-/// Type alias for the interaction of balances with `OnChargeAssetTransaction`.
-pub(crate) type ChargeAssetBalanceOf<T> =
-	<<T as Config>::OnChargeAssetTransaction as OnChargeAssetTransaction<T>>::Balance;
-/// Type alias for Asset IDs in their interaction with `OnChargeAssetTransaction`.
-pub(crate) type ChargeAssetIdOf<T> =
-	<<T as Config>::OnChargeAssetTransaction as OnChargeAssetTransaction<T>>::AssetId;
-/// Liquidity info type alias for interaction with `OnChargeAssetTransaction`.
-pub(crate) type ChargeAssetLiquidityOf<T> =
+/// Liquidity info type alias for the chain's assets.
+pub(crate) type AssetLiquidityInfoOf<T> =
 	<<T as Config>::OnChargeAssetTransaction as OnChargeAssetTransaction<T>>::LiquidityInfo;
 
 /// Used to pass the initial payment info from pre- to post-dispatch.
@@ -104,9 +88,9 @@ pub enum InitialPayment<T: Config> {
 	#[default]
 	Nothing,
 	/// The initial fee was paid in the native currency.
-	Native(LiquidityInfoOf<T>),
+	Native(NativeLiquidityInfoOf<T>),
 	/// The initial fee was paid in an asset.
-	Asset((LiquidityInfoOf<T>, BalanceOf<T>, AssetBalanceOf<T>)),
+	Asset((T::AssetId, AssetLiquidityInfoOf<T>)),
 }
 
 pub use pallet::*;
@@ -116,15 +100,18 @@ pub mod pallet {
 	use super::*;
 
 	#[pallet::config]
-	pub trait Config:
-		frame_system::Config + pallet_transaction_payment::Config + pallet_asset_conversion::Config
-	{
+	pub trait Config: frame_system::Config + pallet_transaction_payment::Config {
 		/// The overarching event type.
 		type RuntimeEvent: From<Event<Self>> + IsType<<Self as frame_system::Config>::RuntimeEvent>;
-		/// The fungibles instance used to pay for transactions in assets.
-		type Fungibles: Balanced<Self::AccountId>;
+		/// The asset ID type that can be used for transaction payments in addition to a
+		/// native asset.
+		type AssetId: AssetId;
 		/// The actual transaction charging logic that charges the fees.
-		type OnChargeAssetTransaction: OnChargeAssetTransaction<Self>;
+		type OnChargeAssetTransaction: OnChargeAssetTransaction<
+			Self,
+			Balance = BalanceOf<Self>,
+			AssetId = Self::AssetId,
+		>;
 	}
 
 	#[pallet::pallet]
@@ -137,9 +124,9 @@ pub mod pallet {
 		/// has been paid by `who` in an asset `asset_id`.
 		AssetTxFeePaid {
 			who: T::AccountId,
-			actual_fee: AssetBalanceOf<T>,
+			actual_fee: BalanceOf<T>,
 			tip: BalanceOf<T>,
-			asset_id: ChargeAssetIdOf<T>,
+			asset_id: T::AssetId,
 		},
 		/// A swap of the refund in native currency back to asset failed.
 		AssetRefundFailed { native_amount_kept: BalanceOf<T> },
@@ -147,33 +134,35 @@ pub mod pallet {
 }
 
 /// Require payment for transaction inclusion and optionally include a tip to gain additional
-/// priority in the queue. Allows paying via both `Currency` as well as `fungibles::Balanced`.
+/// priority in the queue.
 ///
 /// Wraps the transaction logic in [`pallet_transaction_payment`] and extends it with assets.
 /// An asset ID of `None` falls back to the underlying transaction payment logic via the native
 /// currency.
+///
+/// Transaction payments are processed using different handlers based on the asset type:
+/// - Payments with a native asset are charged by
+///   [pallet_transaction_payment::Config::OnChargeTransaction].
+/// - Payments with other assets are charged by [Config::OnChargeAssetTransaction].
 #[derive(Encode, Decode, Clone, Eq, PartialEq, TypeInfo)]
 #[scale_info(skip_type_params(T))]
 pub struct ChargeAssetTxPayment<T: Config> {
 	#[codec(compact)]
 	tip: BalanceOf<T>,
-	asset_id: Option<ChargeAssetIdOf<T>>,
+	asset_id: Option<T::AssetId>,
 }
 
 impl<T: Config> ChargeAssetTxPayment<T>
 where
 	T::RuntimeCall: Dispatchable<Info = DispatchInfo, PostInfo = PostDispatchInfo>,
-	AssetBalanceOf<T>: Send + Sync,
-	BalanceOf<T>: Send + Sync + Into<ChargeAssetBalanceOf<T>> + From<ChargeAssetLiquidityOf<T>>,
-	ChargeAssetIdOf<T>: Send + Sync,
 {
 	/// Utility constructor. Used only in client/factory code.
-	pub fn from(tip: BalanceOf<T>, asset_id: Option<ChargeAssetIdOf<T>>) -> Self {
+	pub fn from(tip: BalanceOf<T>, asset_id: Option<T::AssetId>) -> Self {
 		Self { tip, asset_id }
 	}
 
-	/// Fee withdrawal logic that dispatches to either `OnChargeAssetTransaction` or
-	/// `OnChargeTransaction`.
+	/// Fee withdrawal logic that dispatches to either [`Config::OnChargeAssetTransaction`] or
+	/// [`pallet_transaction_payment::Config::OnChargeTransaction`].
 	fn withdraw_fee(
 		&self,
 		who: &T::AccountId,
@@ -191,25 +180,13 @@ where
 				call,
 				info,
 				asset_id.clone(),
-				fee.into(),
-				self.tip.into(),
+				fee,
+				self.tip,
 			)
-			.map(|(used_for_fee, received_exchanged, asset_consumed)| {
-				(
-					fee,
-					InitialPayment::Asset((
-						used_for_fee.into(),
-						received_exchanged.into(),
-						asset_consumed.into(),
-					)),
-				)
-			})
+			.map(|payment| (fee, InitialPayment::Asset((asset_id.clone(), payment))))
 		} else {
-			<OnChargeTransactionOf<T> as OnChargeTransaction<T>>::withdraw_fee(
-				who, call, info, fee, self.tip,
-			)
-			.map(|i| (fee, InitialPayment::Native(i)))
-			.map_err(|_| -> TransactionValidityError { InvalidTransaction::Payment.into() })
+			T::OnChargeTransaction::withdraw_fee(who, call, info, fee, self.tip)
+				.map(|payment| (fee, InitialPayment::Native(payment)))
 		}
 	}
 }
@@ -228,14 +205,8 @@ impl<T: Config> core::fmt::Debug for ChargeAssetTxPayment<T> {
 impl<T: Config> SignedExtension for ChargeAssetTxPayment<T>
 where
 	T::RuntimeCall: Dispatchable<Info = DispatchInfo, PostInfo = PostDispatchInfo>,
-	AssetBalanceOf<T>: Send + Sync,
-	BalanceOf<T>: Send
-		+ Sync
-		+ From<u64>
-		+ Into<ChargeAssetBalanceOf<T>>
-		+ Into<ChargeAssetLiquidityOf<T>>
-		+ From<ChargeAssetLiquidityOf<T>>,
-	ChargeAssetIdOf<T>: Send + Sync,
+	BalanceOf<T>: Send + Sync,
+	T::AssetId: Send + Sync,
 {
 	const IDENTIFIER: &'static str = "ChargeAssetTxPayment";
 	type AccountId = T::AccountId;
@@ -248,8 +219,6 @@ where
 		Self::AccountId,
 		// imbalance resulting from withdrawing the fee
 		InitialPayment<T>,
-		// asset_id for the transaction payment
-		Option<ChargeAssetIdOf<T>>,
 	);
 
 	fn additional_signed(&self) -> core::result::Result<(), TransactionValidityError> {
@@ -277,7 +246,7 @@ where
 		len: usize,
 	) -> Result<Self::Pre, TransactionValidityError> {
 		let (_fee, initial_payment) = self.withdraw_fee(who, call, info, len)?;
-		Ok((self.tip, who.clone(), initial_payment, self.asset_id))
+		Ok((self.tip, who.clone(), initial_payment))
 	}
 
 	fn post_dispatch(
@@ -285,53 +254,45 @@ where
 		info: &DispatchInfoOf<Self::Call>,
 		post_info: &PostDispatchInfoOf<Self::Call>,
 		len: usize,
-		result: &DispatchResult,
+		_result: &DispatchResult,
 	) -> Result<(), TransactionValidityError> {
-		if let Some((tip, who, initial_payment, asset_id)) = pre {
+		if let Some((tip, who, initial_payment)) = pre {
 			match initial_payment {
 				InitialPayment::Native(already_withdrawn) => {
-					debug_assert!(
-						asset_id.is_none(),
-						"For that payment type the `asset_id` should be None"
+					let actual_fee = pallet_transaction_payment::Pallet::<T>::compute_actual_fee(
+						len as u32, info, post_info, tip,
 					);
-					pallet_transaction_payment::ChargeTransactionPayment::<T>::post_dispatch(
-						Some((tip, who, already_withdrawn)),
+					T::OnChargeTransaction::correct_and_deposit_fee(
+						&who,
 						info,
 						post_info,
-						len,
-						result,
+						actual_fee,
+						tip,
+						already_withdrawn,
 					)?;
-				},
-				InitialPayment::Asset(already_withdrawn) => {
-					debug_assert!(
-						asset_id.is_some(),
-						"For that payment type the `asset_id` should be set"
+					pallet_transaction_payment::Pallet::<T>::deposit_fee_paid_event(
+						who, actual_fee, tip,
 					);
+				},
+				InitialPayment::Asset((asset_id, already_withdrawn)) => {
 					let actual_fee = pallet_transaction_payment::Pallet::<T>::compute_actual_fee(
 						len as u32, info, post_info, tip,
 					);
-
-					if let Some(asset_id) = asset_id {
-						let (used_for_fee, received_exchanged, asset_consumed) = already_withdrawn;
-						let converted_fee = T::OnChargeAssetTransaction::correct_and_deposit_fee(
-							&who,
-							info,
-							post_info,
-							actual_fee.into(),
-							tip.into(),
-							used_for_fee.into(),
-							received_exchanged.into(),
-							asset_id.clone(),
-							asset_consumed.into(),
-						)?;
-
-						Pallet::<T>::deposit_event(Event::<T>::AssetTxFeePaid {
-							who,
-							actual_fee: converted_fee,
-							tip,
-							asset_id,
-						});
-					}
+					let converted_fee = T::OnChargeAssetTransaction::correct_and_deposit_fee(
+						&who,
+						info,
+						post_info,
+						actual_fee,
+						tip,
+						asset_id.clone(),
+						already_withdrawn,
+					)?;
+					Pallet::<T>::deposit_event(Event::<T>::AssetTxFeePaid {
+						who,
+						actual_fee: converted_fee,
+						tip,
+						asset_id,
+					});
 				},
 				InitialPayment::Nothing => {
 					// `actual_fee` should be zero here for any signed extrinsic. It would be
diff --git a/substrate/frame/transaction-payment/asset-conversion-tx-payment/src/mock.rs b/substrate/frame/transaction-payment/asset-conversion-tx-payment/src/mock.rs
index 245900760de9d31f5540b83b5b3a5789b72d31cc..acfd43d0a7cb9520ac14974f439eca7530e7b3a8 100644
--- a/substrate/frame/transaction-payment/asset-conversion-tx-payment/src/mock.rs
+++ b/substrate/frame/transaction-payment/asset-conversion-tx-payment/src/mock.rs
@@ -24,7 +24,7 @@ use frame_support::{
 	pallet_prelude::*,
 	parameter_types,
 	traits::{
-		fungible,
+		fungible, fungibles,
 		tokens::{
 			fungible::{NativeFromLeft, NativeOrWithId, UnionOf},
 			imbalance::ResolveAssetTo,
@@ -145,6 +145,22 @@ impl OnUnbalanced<fungible::Credit<<Runtime as frame_system::Config>::AccountId,
 	}
 }
 
+pub struct DealWithFungiblesFees;
+impl OnUnbalanced<fungibles::Credit<AccountId, NativeAndAssets>> for DealWithFungiblesFees {
+	fn on_unbalanceds(
+		mut fees_then_tips: impl Iterator<
+			Item = fungibles::Credit<<Runtime as frame_system::Config>::AccountId, NativeAndAssets>,
+		>,
+	) {
+		if let Some(fees) = fees_then_tips.next() {
+			FeeUnbalancedAmount::mutate(|a| *a += fees.peek());
+			if let Some(tips) = fees_then_tips.next() {
+				TipUnbalancedAmount::mutate(|a| *a += tips.peek());
+			}
+		}
+	}
+}
+
 #[derive_impl(pallet_transaction_payment::config_preludes::TestDefaultConfig)]
 impl pallet_transaction_payment::Config for Runtime {
 	type RuntimeEvent = RuntimeEvent;
@@ -221,12 +237,14 @@ pub type PoolIdToAccountId = pallet_asset_conversion::AccountIdConverter<
 	(NativeOrWithId<u32>, NativeOrWithId<u32>),
 >;
 
+type NativeAndAssets = UnionOf<Balances, Assets, NativeFromLeft, NativeOrWithId<u32>, AccountId>;
+
 impl pallet_asset_conversion::Config for Runtime {
 	type RuntimeEvent = RuntimeEvent;
 	type Balance = Balance;
 	type HigherPrecisionBalance = u128;
 	type AssetKind = NativeOrWithId<u32>;
-	type Assets = UnionOf<Balances, Assets, NativeFromLeft, NativeOrWithId<u32>, AccountId>;
+	type Assets = NativeAndAssets;
 	type PoolId = (Self::AssetKind, Self::AssetKind);
 	type PoolLocator = Chain<
 		WithFirstAsset<Native, AccountId, NativeOrWithId<u32>, PoolIdToAccountId>,
@@ -250,6 +268,7 @@ impl pallet_asset_conversion::Config for Runtime {
 
 impl Config for Runtime {
 	type RuntimeEvent = RuntimeEvent;
-	type Fungibles = Assets;
-	type OnChargeAssetTransaction = AssetConversionAdapter<Balances, AssetConversion, Native>;
+	type AssetId = NativeOrWithId<u32>;
+	type OnChargeAssetTransaction =
+		SwapAssetAdapter<Native, NativeAndAssets, AssetConversion, DealWithFungiblesFees>;
 }
diff --git a/substrate/frame/transaction-payment/asset-conversion-tx-payment/src/payment.rs b/substrate/frame/transaction-payment/asset-conversion-tx-payment/src/payment.rs
index 0ef3fb1111439e0020bc4797fb0cd85ed71521b7..dc7faecd56084d924c6e0a57fa0c6052acb4bd38 100644
--- a/substrate/frame/transaction-payment/asset-conversion-tx-payment/src/payment.rs
+++ b/substrate/frame/transaction-payment/asset-conversion-tx-payment/src/payment.rs
@@ -20,11 +20,15 @@ use crate::Config;
 use alloc::vec;
 use core::marker::PhantomData;
 use frame_support::{
-	ensure,
-	traits::{fungible::Inspect, tokens::Balance},
+	defensive, ensure,
+	traits::{
+		fungibles,
+		tokens::{Balance, Fortitude, Precision, Preservation},
+		Defensive, OnUnbalanced, SameOrOther,
+	},
 	unsigned::TransactionValidityError,
 };
-use pallet_asset_conversion::Swap;
+use pallet_asset_conversion::{QuotePrice, SwapCredit};
 use sp_runtime::{
 	traits::{DispatchInfoOf, Get, PostDispatchInfoOf, Zero},
 	transaction_validity::InvalidTransaction,
@@ -50,150 +54,216 @@ pub trait OnChargeAssetTransaction<T: Config> {
 		asset_id: Self::AssetId,
 		fee: Self::Balance,
 		tip: Self::Balance,
-	) -> Result<
-		(LiquidityInfoOf<T>, Self::LiquidityInfo, AssetBalanceOf<T>),
-		TransactionValidityError,
-	>;
+	) -> Result<Self::LiquidityInfo, TransactionValidityError>;
 
 	/// Refund any overpaid fees and deposit the corrected amount.
 	/// The actual fee gets calculated once the transaction is executed.
 	///
 	/// Note: The `fee` already includes the `tip`.
 	///
-	/// Returns the fee and tip in the asset used for payment as (fee, tip).
+	/// Returns the amount of `asset_id` that was used for the payment.
 	fn correct_and_deposit_fee(
 		who: &T::AccountId,
 		dispatch_info: &DispatchInfoOf<T::RuntimeCall>,
 		post_info: &PostDispatchInfoOf<T::RuntimeCall>,
 		corrected_fee: Self::Balance,
 		tip: Self::Balance,
-		fee_paid: LiquidityInfoOf<T>,
-		received_exchanged: Self::LiquidityInfo,
 		asset_id: Self::AssetId,
-		initial_asset_consumed: AssetBalanceOf<T>,
-	) -> Result<AssetBalanceOf<T>, TransactionValidityError>;
+		already_withdraw: Self::LiquidityInfo,
+	) -> Result<BalanceOf<T>, TransactionValidityError>;
 }
 
-/// Implements the asset transaction for a balance to asset converter (implementing [`Swap`]).
+/// Means to withdraw, correct and deposit fees in the asset accepted by the system.
 ///
-/// The converter is given the complete fee in terms of the asset used for the transaction.
-pub struct AssetConversionAdapter<C, CON, N>(PhantomData<(C, CON, N)>);
+/// The type uses the [`SwapCredit`] implementation to swap the asset used by a user for the fee
+/// payment for the asset accepted as a fee payment be the system.
+///
+/// Parameters:
+/// - `A`: The asset identifier that system accepts as a fee payment (eg. native asset).
+/// - `F`: The fungibles registry that can handle assets provided by user and the `A` asset.
+/// - `S`: The swap implementation that can swap assets provided by user for the `A` asset.
+/// - OU: The handler for withdrawn `fee` and `tip`, passed in the respective order to
+///   [OnUnbalanced::on_unbalanceds].
+/// - `T`: The pallet's configuration.
+pub struct SwapAssetAdapter<A, F, S, OU>(PhantomData<(A, F, S, OU)>);
 
-/// Default implementation for a runtime instantiating this pallet, an asset to native swapper.
-impl<T, C, CON, N> OnChargeAssetTransaction<T> for AssetConversionAdapter<C, CON, N>
+impl<A, F, S, OU, T> OnChargeAssetTransaction<T> for SwapAssetAdapter<A, F, S, OU>
 where
-	N: Get<CON::AssetKind>,
+	A: Get<T::AssetId>,
+	F: fungibles::Balanced<T::AccountId, Balance = BalanceOf<T>, AssetId = T::AssetId>,
+	S: SwapCredit<
+			T::AccountId,
+			Balance = BalanceOf<T>,
+			AssetKind = T::AssetId,
+			Credit = fungibles::Credit<T::AccountId, F>,
+		> + QuotePrice<Balance = BalanceOf<T>, AssetKind = T::AssetId>,
+	OU: OnUnbalanced<fungibles::Credit<T::AccountId, F>>,
 	T: Config,
-	C: Inspect<<T as frame_system::Config>::AccountId>,
-	CON: Swap<T::AccountId, Balance = BalanceOf<T>, AssetKind = T::AssetKind>,
-	BalanceOf<T>: Into<AssetBalanceOf<T>>,
-	T::AssetKind: From<AssetIdOf<T>>,
-	BalanceOf<T>: IsType<<C as Inspect<<T as frame_system::Config>::AccountId>>::Balance>,
 {
+	type AssetId = T::AssetId;
 	type Balance = BalanceOf<T>;
-	type AssetId = AssetIdOf<T>;
-	type LiquidityInfo = BalanceOf<T>;
+	type LiquidityInfo = (fungibles::Credit<T::AccountId, F>, BalanceOf<T>);
 
-	/// Swap & withdraw the predicted fee from the transaction origin.
-	///
-	/// Note: The `fee` already includes the `tip`.
-	///
-	/// Returns the total amount in native currency received by exchanging the `asset_id` and the
-	/// amount in native currency used to pay the fee.
 	fn withdraw_fee(
 		who: &T::AccountId,
-		call: &T::RuntimeCall,
-		info: &DispatchInfoOf<T::RuntimeCall>,
+		_call: &T::RuntimeCall,
+		_dispatch_info: &DispatchInfoOf<<T>::RuntimeCall>,
 		asset_id: Self::AssetId,
-		fee: BalanceOf<T>,
-		tip: BalanceOf<T>,
-	) -> Result<
-		(LiquidityInfoOf<T>, Self::LiquidityInfo, AssetBalanceOf<T>),
-		TransactionValidityError,
-	> {
-		// convert the asset into native currency
-		let ed = C::minimum_balance();
-		let native_asset_required =
-			if C::balance(&who) >= ed.saturating_add(fee.into()) { fee } else { fee + ed.into() };
-
-		let asset_consumed = CON::swap_tokens_for_exact_tokens(
-			who.clone(),
-			vec![asset_id.into(), N::get()],
-			native_asset_required,
-			None,
-			who.clone(),
-			true,
+		fee: Self::Balance,
+		_tip: Self::Balance,
+	) -> Result<Self::LiquidityInfo, TransactionValidityError> {
+		if asset_id == A::get() {
+			// The `asset_id` is the target asset, we do not need to swap.
+			let fee_credit = F::withdraw(
+				asset_id.clone(),
+				who,
+				fee,
+				Precision::Exact,
+				Preservation::Preserve,
+				Fortitude::Polite,
+			)
+			.map_err(|_| InvalidTransaction::Payment)?;
+
+			return Ok((fee_credit, fee));
+		}
+
+		// Quote the amount of the `asset_id` needed to pay the fee in the asset `A`.
+		let asset_fee =
+			S::quote_price_tokens_for_exact_tokens(asset_id.clone(), A::get(), fee, true)
+				.ok_or(InvalidTransaction::Payment)?;
+
+		// Withdraw the `asset_id` credit for the swap.
+		let asset_fee_credit = F::withdraw(
+			asset_id.clone(),
+			who,
+			asset_fee,
+			Precision::Exact,
+			Preservation::Preserve,
+			Fortitude::Polite,
 		)
-		.map_err(|_| TransactionValidityError::from(InvalidTransaction::Payment))?;
+		.map_err(|_| InvalidTransaction::Payment)?;
 
-		ensure!(asset_consumed > Zero::zero(), InvalidTransaction::Payment);
+		let (fee_credit, change) = match S::swap_tokens_for_exact_tokens(
+			vec![asset_id, A::get()],
+			asset_fee_credit,
+			fee,
+		) {
+			Ok((fee_credit, change)) => (fee_credit, change),
+			Err((credit_in, _)) => {
+				defensive!("Fee swap should pass for the quoted amount");
+				let _ = F::resolve(who, credit_in).defensive_proof("Should resolve the credit");
+				return Err(InvalidTransaction::Payment.into())
+			},
+		};
 
-		// charge the fee in native currency
-		<T::OnChargeTransaction>::withdraw_fee(who, call, info, fee, tip)
-			.map(|r| (r, native_asset_required, asset_consumed.into()))
+		// Since the exact price for `fee` has been quoted, the change should be zero.
+		ensure!(change.peek().is_zero(), InvalidTransaction::Payment);
+
+		Ok((fee_credit, asset_fee))
 	}
 
-	/// Correct the fee and swap the refund back to asset.
-	///
-	/// Note: The `corrected_fee` already includes the `tip`.
-	/// Note: Is the ED wasn't needed, the `received_exchanged` will be equal to `fee_paid`, or
-	/// `fee_paid + ed` otherwise.
 	fn correct_and_deposit_fee(
 		who: &T::AccountId,
-		dispatch_info: &DispatchInfoOf<T::RuntimeCall>,
-		post_info: &PostDispatchInfoOf<T::RuntimeCall>,
-		corrected_fee: BalanceOf<T>,
-		tip: BalanceOf<T>,
-		fee_paid: LiquidityInfoOf<T>,
-		received_exchanged: Self::LiquidityInfo,
+		_dispatch_info: &DispatchInfoOf<<T>::RuntimeCall>,
+		_post_info: &PostDispatchInfoOf<<T>::RuntimeCall>,
+		corrected_fee: Self::Balance,
+		tip: Self::Balance,
 		asset_id: Self::AssetId,
-		initial_asset_consumed: AssetBalanceOf<T>,
-	) -> Result<AssetBalanceOf<T>, TransactionValidityError> {
-		// Refund the native asset to the account that paid the fees (`who`).
-		// The `who` account will receive the "fee_paid - corrected_fee" refund.
-		<T::OnChargeTransaction>::correct_and_deposit_fee(
-			who,
-			dispatch_info,
-			post_info,
-			corrected_fee,
-			tip,
-			fee_paid,
-		)?;
-
-		// calculate the refund in native asset, to swap back to the desired `asset_id`
-		let swap_back = received_exchanged.saturating_sub(corrected_fee);
-		let mut asset_refund = Zero::zero();
-		if !swap_back.is_zero() {
-			// If this fails, the account might have dropped below the existential balance or there
-			// is not enough liquidity left in the pool. In that case we don't throw an error and
-			// the account will keep the native currency.
-			match CON::swap_exact_tokens_for_tokens(
-				who.clone(), // we already deposited the native to `who`
-				vec![
-					N::get(),        // we provide the native
-					asset_id.into(), // we want asset_id back
-				],
-				swap_back,   // amount of the native asset to convert to `asset_id`
-				None,        // no minimum amount back
-				who.clone(), // we will refund to `who`
-				false,       // no need to keep alive
+		already_withdrawn: Self::LiquidityInfo,
+	) -> Result<BalanceOf<T>, TransactionValidityError> {
+		let (fee_paid, initial_asset_consumed) = already_withdrawn;
+		let refund_amount = fee_paid.peek().saturating_sub(corrected_fee);
+		let (fee_in_asset, adjusted_paid) = if refund_amount.is_zero() ||
+			F::total_balance(asset_id.clone(), who).is_zero()
+		{
+			// Nothing to refund or the account was removed be the dispatched function.
+			(initial_asset_consumed, fee_paid)
+		} else if asset_id == A::get() {
+			// The `asset_id` is the target asset, we do not need to swap.
+			let (refund, fee_paid) = fee_paid.split(refund_amount);
+			if let Err(refund) = F::resolve(who, refund) {
+				let fee_paid = fee_paid.merge(refund).map_err(|_| {
+					defensive!("`fee_paid` and `refund` are credits of the same asset.");
+					InvalidTransaction::Payment
+				})?;
+				(initial_asset_consumed, fee_paid)
+			} else {
+				(fee_paid.peek().saturating_sub(refund_amount), fee_paid)
+			}
+		} else {
+			// Check if the refund amount can be swapped back into the asset used by `who` for fee
+			// payment.
+			let refund_asset_amount = S::quote_price_exact_tokens_for_tokens(
+				A::get(),
+				asset_id.clone(),
+				refund_amount,
+				true,
 			)
-			.ok()
-			{
-				Some(acquired) => {
-					asset_refund = acquired
-						.try_into()
-						.map_err(|_| TransactionValidityError::from(InvalidTransaction::Payment))?;
-				},
-				None => {
-					Pallet::<T>::deposit_event(Event::<T>::AssetRefundFailed {
-						native_amount_kept: swap_back,
-					});
-				},
+			// No refund given if it cannot be swapped back.
+			.unwrap_or(Zero::zero());
+
+			let debt = if refund_asset_amount.is_zero() {
+				fungibles::Debt::<T::AccountId, F>::zero(asset_id.clone())
+			} else {
+				// Deposit the refund before the swap to ensure it can be processed.
+				match F::deposit(asset_id.clone(), &who, refund_asset_amount, Precision::BestEffort)
+				{
+					Ok(debt) => debt,
+					// No refund given since it cannot be deposited.
+					Err(_) => fungibles::Debt::<T::AccountId, F>::zero(asset_id.clone()),
+				}
+			};
+
+			if debt.peek().is_zero() {
+				// No refund given.
+				(initial_asset_consumed, fee_paid)
+			} else {
+				let (refund, adjusted_paid) = fee_paid.split(refund_amount);
+				match S::swap_exact_tokens_for_tokens(
+					vec![A::get(), asset_id],
+					refund,
+					Some(refund_asset_amount),
+				) {
+					Ok(refund_asset) => {
+						match refund_asset.offset(debt) {
+							Ok(SameOrOther::None) => {},
+							// This arm should never be reached, as the  amount of `debt` is
+							// expected to be exactly equal to the amount of `refund_asset` credit.
+							_ => {
+								defensive!("Debt should be equal to the refund credit");
+								return Err(InvalidTransaction::Payment.into())
+							},
+						};
+						(
+							initial_asset_consumed.saturating_sub(refund_asset_amount.into()),
+							adjusted_paid,
+						)
+					},
+					// The error should not occur since swap was quoted before.
+					Err((refund, _)) => {
+						defensive!("Refund swap should pass for the quoted amount");
+						match F::settle(who, debt, Preservation::Expendable) {
+							Ok(dust) => ensure!(dust.peek().is_zero(), InvalidTransaction::Payment),
+							// The error should not occur as the `debt` was just withdrawn above.
+							Err(_) => {
+								defensive!("Should settle the debt");
+								return Err(InvalidTransaction::Payment.into())
+							},
+						};
+						let adjusted_paid = adjusted_paid.merge(refund).map_err(|_| {
+							// The error should never occur since `adjusted_paid` and `refund` are
+							// credits of the same asset.
+							InvalidTransaction::Payment
+						})?;
+						(initial_asset_consumed, adjusted_paid)
+					},
+				}
 			}
-		}
+		};
 
-		let actual_paid = initial_asset_consumed.saturating_sub(asset_refund);
-		Ok(actual_paid)
+		// Handle the imbalance (fee and tip separately).
+		let (tip, fee) = adjusted_paid.split(tip);
+		OU::on_unbalanceds(Some(fee).into_iter().chain(Some(tip)));
+		Ok(fee_in_asset)
 	}
 }
diff --git a/substrate/frame/transaction-payment/asset-conversion-tx-payment/src/tests.rs b/substrate/frame/transaction-payment/asset-conversion-tx-payment/src/tests.rs
index aa2f26f3a6a8d5ce0edc45e1694c51a2c12da44c..aab657199533ec2714ee2b14813044e02e83d205 100644
--- a/substrate/frame/transaction-payment/asset-conversion-tx-payment/src/tests.rs
+++ b/substrate/frame/transaction-payment/asset-conversion-tx-payment/src/tests.rs
@@ -22,6 +22,7 @@ use frame_support::{
 	traits::{
 		fungible::{Inspect, NativeOrWithId},
 		fungibles::{Inspect as FungiblesInspect, Mutate},
+		tokens::{Fortitude, Precision, Preservation},
 	},
 	weights::Weight,
 };
@@ -239,7 +240,7 @@ fn transaction_payment_in_asset_possible() {
 			let fee_in_asset = input_quote.unwrap();
 			assert_eq!(Assets::balance(asset_id, caller), balance);
 
-			let pre = ChargeAssetTxPayment::<Runtime>::from(0, Some(asset_id))
+			let pre = ChargeAssetTxPayment::<Runtime>::from(0, Some(asset_id.into()))
 				.pre_dispatch(&caller, CALL, &info_from_weight(WEIGHT_5), len)
 				.unwrap();
 			// assert that native balance is not used
@@ -297,7 +298,7 @@ fn transaction_payment_in_asset_fails_if_no_pool_for_that_asset() {
 			assert_eq!(Assets::balance(asset_id, caller), balance);
 
 			let len = 10;
-			let pre = ChargeAssetTxPayment::<Runtime>::from(0, Some(asset_id)).pre_dispatch(
+			let pre = ChargeAssetTxPayment::<Runtime>::from(0, Some(asset_id.into())).pre_dispatch(
 				&caller,
 				CALL,
 				&info_from_weight(WEIGHT_5),
@@ -352,7 +353,7 @@ fn transaction_payment_without_fee() {
 			assert_eq!(input_quote, Some(201));
 
 			let fee_in_asset = input_quote.unwrap();
-			let pre = ChargeAssetTxPayment::<Runtime>::from(0, Some(asset_id))
+			let pre = ChargeAssetTxPayment::<Runtime>::from(0, Some(asset_id.into()))
 				.pre_dispatch(&caller, CALL, &info_from_weight(WEIGHT_5), len)
 				.unwrap();
 
@@ -429,7 +430,7 @@ fn asset_transaction_payment_with_tip_and_refund() {
 			assert_eq!(input_quote, Some(1206));
 
 			let fee_in_asset = input_quote.unwrap();
-			let pre = ChargeAssetTxPayment::<Runtime>::from(tip, Some(asset_id))
+			let pre = ChargeAssetTxPayment::<Runtime>::from(tip, Some(asset_id.into()))
 				.pre_dispatch(&caller, CALL, &info_from_weight(WEIGHT_100), len)
 				.unwrap();
 			assert_eq!(Assets::balance(asset_id, caller), balance - fee_in_asset);
@@ -512,31 +513,21 @@ fn payment_from_account_with_only_assets() {
 			let len = 10;
 
 			let fee_in_native = base_weight + weight + len as u64;
-			let ed = Balances::minimum_balance();
 			let fee_in_asset = AssetConversion::quote_price_tokens_for_exact_tokens(
 				NativeOrWithId::WithId(asset_id),
 				NativeOrWithId::Native,
-				fee_in_native + ed,
+				fee_in_native,
 				true,
 			)
 			.unwrap();
-			assert_eq!(fee_in_asset, 301);
+			assert_eq!(fee_in_asset, 201);
 
-			let pre = ChargeAssetTxPayment::<Runtime>::from(0, Some(asset_id))
+			let pre = ChargeAssetTxPayment::<Runtime>::from(0, Some(asset_id.into()))
 				.pre_dispatch(&caller, CALL, &info_from_weight(WEIGHT_5), len)
 				.unwrap();
-			assert_eq!(Balances::free_balance(caller), ed);
 			// check that fee was charged in the given asset
 			assert_eq!(Assets::balance(asset_id, caller), balance - fee_in_asset);
 
-			let refund = AssetConversion::quote_price_exact_tokens_for_tokens(
-				NativeOrWithId::Native,
-				NativeOrWithId::WithId(asset_id),
-				ed,
-				true,
-			)
-			.unwrap();
-
 			assert_ok!(ChargeAssetTxPayment::<Runtime>::post_dispatch(
 				Some(pre),
 				&info_from_weight(WEIGHT_5),
@@ -544,7 +535,7 @@ fn payment_from_account_with_only_assets() {
 				len,
 				&Ok(())
 			));
-			assert_eq!(Assets::balance(asset_id, caller), balance - fee_in_asset + refund);
+			assert_eq!(Assets::balance(asset_id, caller), balance - fee_in_asset);
 			assert_eq!(Balances::free_balance(caller), 0);
 
 			assert_eq!(TipUnbalancedAmount::get(), 0);
@@ -587,7 +578,7 @@ fn converted_fee_is_never_zero_if_input_fee_is_not() {
 
 			// there will be no conversion when the fee is zero
 			{
-				let pre = ChargeAssetTxPayment::<Runtime>::from(0, Some(asset_id))
+				let pre = ChargeAssetTxPayment::<Runtime>::from(0, Some(asset_id.into()))
 					.pre_dispatch(&caller, CALL, &info_from_pays(Pays::No), len)
 					.unwrap();
 				// `Pays::No` implies there are no fees
@@ -613,7 +604,7 @@ fn converted_fee_is_never_zero_if_input_fee_is_not() {
 			)
 			.unwrap();
 
-			let pre = ChargeAssetTxPayment::<Runtime>::from(0, Some(asset_id))
+			let pre = ChargeAssetTxPayment::<Runtime>::from(0, Some(asset_id.into()))
 				.pre_dispatch(&caller, CALL, &info_from_weight(Weight::from_parts(weight, 0)), len)
 				.unwrap();
 			assert_eq!(Assets::balance(asset_id, caller), balance - fee_in_asset);
@@ -663,14 +654,14 @@ fn post_dispatch_fee_is_zero_if_pre_dispatch_fee_is_zero() {
 			// calculated fee is greater than 0
 			assert!(fee > 0);
 
-			let pre = ChargeAssetTxPayment::<Runtime>::from(0, Some(asset_id))
+			let pre = ChargeAssetTxPayment::<Runtime>::from(0, Some(asset_id.into()))
 				.pre_dispatch(&caller, CALL, &info_from_pays(Pays::No), len)
 				.unwrap();
 			// `Pays::No` implies no pre-dispatch fees
 
 			assert_eq!(Assets::balance(asset_id, caller), balance);
 
-			let (_tip, _who, initial_payment, _asset_id) = &pre;
+			let (_tip, _who, initial_payment) = &pre;
 			let not_paying = match initial_payment {
 				&InitialPayment::Nothing => true,
 				_ => false,
@@ -740,3 +731,139 @@ fn post_dispatch_fee_is_zero_if_unsigned_pre_dispatch_fee_is_zero() {
 			assert_eq!(Assets::balance(asset_id, caller), balance);
 		});
 }
+
+#[test]
+fn fee_with_native_asset_passed_with_id() {
+	let base_weight = 5;
+	let balance_factor = 100;
+	ExtBuilder::default()
+		.balance_factor(balance_factor)
+		.base_weight(Weight::from_parts(base_weight, 0))
+		.build()
+		.execute_with(|| {
+			let caller = 1;
+			let caller_balance = 1000;
+			// native asset
+			let asset_id = NativeOrWithId::Native;
+			// assert that native balance is not necessary
+			assert_eq!(Balances::free_balance(caller), caller_balance);
+
+			let tip = 10;
+			let weight = 100;
+			let len = 5;
+			let initial_fee = base_weight + weight + len as u64 + tip;
+
+			let pre = ChargeAssetTxPayment::<Runtime>::from(tip, Some(asset_id.into()))
+				.pre_dispatch(&caller, CALL, &info_from_weight(WEIGHT_100), len)
+				.unwrap();
+			assert_eq!(Balances::free_balance(caller), caller_balance - initial_fee);
+
+			let final_weight = 50;
+			let expected_fee = initial_fee - final_weight;
+
+			assert_ok!(ChargeAssetTxPayment::<Runtime>::post_dispatch(
+				Some(pre),
+				&info_from_weight(WEIGHT_100),
+				&post_info_from_weight(WEIGHT_50),
+				len,
+				&Ok(())
+			));
+
+			assert_eq!(Balances::free_balance(caller), caller_balance - expected_fee);
+
+			assert_eq!(TipUnbalancedAmount::get(), tip);
+			assert_eq!(FeeUnbalancedAmount::get(), expected_fee - tip);
+		});
+}
+
+#[test]
+fn transfer_add_and_remove_account() {
+	let base_weight = 5;
+	let balance_factor = 100;
+	ExtBuilder::default()
+		.balance_factor(balance_factor)
+		.base_weight(Weight::from_parts(base_weight, 0))
+		.build()
+		.execute_with(|| {
+			System::set_block_number(1);
+
+			// create the asset
+			let asset_id = 1;
+			let min_balance = 2;
+			assert_ok!(Assets::force_create(
+				RuntimeOrigin::root(),
+				asset_id.into(),
+				42,   /* owner */
+				true, /* is_sufficient */
+				min_balance,
+			));
+
+			setup_lp(asset_id, balance_factor);
+
+			// mint into the caller account
+			let caller = 222;
+			let beneficiary = <Runtime as system::Config>::Lookup::unlookup(caller);
+			let balance = 10000;
+
+			assert_eq!(Balances::free_balance(caller), 0);
+			assert_ok!(Assets::mint_into(asset_id.into(), &beneficiary, balance));
+			assert_eq!(Assets::balance(asset_id, caller), balance);
+
+			let weight = 100;
+			let tip = 5;
+			let len = 10;
+			let fee_in_native = base_weight + weight + len as u64 + tip;
+			let input_quote = AssetConversion::quote_price_tokens_for_exact_tokens(
+				NativeOrWithId::WithId(asset_id),
+				NativeOrWithId::Native,
+				fee_in_native,
+				true,
+			);
+			assert!(!input_quote.unwrap().is_zero());
+
+			let fee_in_asset = input_quote.unwrap();
+			let pre = ChargeAssetTxPayment::<Runtime>::from(tip, Some(asset_id.into()))
+				.pre_dispatch(&caller, CALL, &info_from_weight(WEIGHT_100), len)
+				.unwrap();
+
+			assert_eq!(Assets::balance(asset_id, &caller), balance - fee_in_asset);
+
+			// remove caller account.
+			assert_ok!(Assets::burn_from(
+				asset_id,
+				&caller,
+				Assets::balance(asset_id, &caller),
+				Preservation::Expendable,
+				Precision::Exact,
+				Fortitude::Force
+			));
+
+			let final_weight = 50;
+			let final_fee_in_native = fee_in_native - final_weight - tip;
+			let token_refund = AssetConversion::quote_price_exact_tokens_for_tokens(
+				NativeOrWithId::Native,
+				NativeOrWithId::WithId(asset_id),
+				fee_in_native - final_fee_in_native - tip,
+				true,
+			)
+			.unwrap();
+
+			// make sure the refund amount is enough to create the account.
+			assert!(token_refund >= min_balance);
+
+			assert_ok!(ChargeAssetTxPayment::<Runtime>::post_dispatch(
+				Some(pre),
+				&info_from_weight(WEIGHT_100),
+				&post_info_from_weight(WEIGHT_50),
+				len,
+				&Ok(())
+			));
+
+			// fee paid with no refund.
+			assert_eq!(TipUnbalancedAmount::get(), tip);
+			assert_eq!(FeeUnbalancedAmount::get(), fee_in_native - tip);
+
+			// caller account removed.
+			assert_eq!(Assets::balance(asset_id, caller), 0);
+		});
+}
diff --git a/substrate/frame/transaction-payment/src/lib.rs b/substrate/frame/transaction-payment/src/lib.rs
index 7df658a4732e65dfaf2a89dfba7ac08cb75c68a6..c17ab393b5d3b2a05cc3a52745a83183e164205f 100644
--- a/substrate/frame/transaction-payment/src/lib.rs
+++ b/substrate/frame/transaction-payment/src/lib.rs
@@ -667,6 +667,11 @@ impl<T: Config> Pallet<T> {
 		let capped_weight = weight.min(T::BlockWeights::get().max_block);
 		T::WeightToFee::weight_to_fee(&capped_weight)
 	}
+
+	/// Deposit the [`Event::TransactionFeePaid`] event.
+	pub fn deposit_fee_paid_event(who: T::AccountId, actual_fee: BalanceOf<T>, tip: BalanceOf<T>) {
+		Self::deposit_event(Event::TransactionFeePaid { who, actual_fee, tip });
+	}
 }
 
 impl<T> Convert<Weight, BalanceOf<T>> for Pallet<T>