From 0b74812ce8a87a716179b2a52c988d1663232c5e Mon Sep 17 00:00:00 2001
From: Muharem <ismailov.m.h@gmail.com>
Date: Tue, 19 Dec 2023 13:51:05 +0100
Subject: [PATCH] `UnionOf` types for merged `fungible` and `fungibles`
 implementations (#2033)

Introduces `UnionOf` types, crafted to merge `fungible` and `fungibles`
implementations or two `fungibles` implementations into a single type
implementing `fungibles`.

This also addresses an issue where `ItemOf` initiates a double drop for
an imbalance type, leading to inaccurate total issuance accounting.

Find the application of these types in this PR -
[link](https://github.com/paritytech/polkadot-sdk/pull/2031), places in
code -
[1](https://github.com/paritytech/polkadot-sdk/blob/4ec7496fa2632385b08fae860fcf28a523a7b5de/cumulus/parachains/runtimes/assets/asset-hub-kusama/src/lib.rs#L327),
[2](https://github.com/paritytech/polkadot-sdk/blob/4ec7496fa2632385b08fae860fcf28a523a7b5de/cumulus/parachains/runtimes/assets/asset-hub-kusama/src/lib.rs#L343).

---------

Co-authored-by: Liam Aharon <liam.aharon@hotmail.com>
Co-authored-by: joe petrowski <25483142+joepetrowski@users.noreply.github.com>
Co-authored-by: Oliver Tale-Yazdi <oliver.tale-yazdi@parity.io>
Co-authored-by: joepetrowski <joe@parity.io>
---
 .../common/src/local_and_foreign_assets.rs    |  12 +-
 prdoc/pr_2033.prdoc                           |  14 +
 substrate/frame/asset-conversion/src/lib.rs   |   6 +-
 substrate/frame/assets/src/lib.rs             |  16 +-
 substrate/frame/assets/src/tests.rs           |   2 +
 substrate/frame/assets/src/tests/sets.rs      | 346 +++++++
 substrate/frame/balances/src/impl_fungible.rs |  24 +-
 substrate/frame/support/src/traits/misc.rs    |  17 +-
 .../src/traits/tokens/fungible/imbalance.rs   |  29 +-
 .../src/traits/tokens/fungible/item_of.rs     |  41 +-
 .../support/src/traits/tokens/fungible/mod.rs |   4 +-
 .../src/traits/tokens/fungible/union_of.rs    | 924 ++++++++++++++++++
 .../src/traits/tokens/fungibles/imbalance.rs  |  54 +-
 .../src/traits/tokens/fungibles/mod.rs        |   4 +-
 .../src/traits/tokens/fungibles/union_of.rs   | 897 +++++++++++++++++
 15 files changed, 2353 insertions(+), 37 deletions(-)
 create mode 100644 prdoc/pr_2033.prdoc
 create mode 100644 substrate/frame/assets/src/tests/sets.rs
 create mode 100644 substrate/frame/support/src/traits/tokens/fungible/union_of.rs
 create mode 100644 substrate/frame/support/src/traits/tokens/fungibles/union_of.rs

diff --git a/cumulus/parachains/runtimes/assets/common/src/local_and_foreign_assets.rs b/cumulus/parachains/runtimes/assets/common/src/local_and_foreign_assets.rs
index 9f429016f53..ed588798556 100644
--- a/cumulus/parachains/runtimes/assets/common/src/local_and_foreign_assets.rs
+++ b/cumulus/parachains/runtimes/assets/common/src/local_and_foreign_assets.rs
@@ -318,10 +318,18 @@ where
 		}
 	}
 
+	fn should_touch(asset_id: MultiLocation, who: &AccountId) -> bool {
+		if let Some(asset_id) = LocalAssetIdConverter::convert(&asset_id) {
+			Assets::should_touch(asset_id, who)
+		} else {
+			ForeignAssets::should_touch(asset_id, who)
+		}
+	}
+
 	fn touch(
 		asset_id: MultiLocation,
-		who: AccountId,
-		depositor: AccountId,
+		who: &AccountId,
+		depositor: &AccountId,
 	) -> Result<(), DispatchError> {
 		if let Some(asset_id) = LocalAssetIdConverter::convert(&asset_id) {
 			Assets::touch(asset_id, who, depositor)
diff --git a/prdoc/pr_2033.prdoc b/prdoc/pr_2033.prdoc
new file mode 100644
index 00000000000..eeb7ff2b4ee
--- /dev/null
+++ b/prdoc/pr_2033.prdoc
@@ -0,0 +1,14 @@
+# Schema: Polkadot SDK PRDoc Schema (prdoc) v1.0.0
+# See doc at https://raw.githubusercontent.com/paritytech/polkadot-sdk/master/prdoc/schema_user.json
+
+title: "`UnionOf` types for merged `fungible` and `fungibles` implementations"
+
+doc:
+  - audience: Runtime Dev
+    description: |
+      Introduces `UnionOf` types, crafted to merge `fungible` and `fungibles` implementations or two
+      `fungibles` implementations into a single type implementing `fungibles`. This also addresses
+      an issue where `ItemOf` initiates a double drop for an imbalance type, leading to inaccurate
+      total issuance accounting.
+
+crates: [ ]
diff --git a/substrate/frame/asset-conversion/src/lib.rs b/substrate/frame/asset-conversion/src/lib.rs
index 8d811473e86..5cbc2821ce6 100644
--- a/substrate/frame/asset-conversion/src/lib.rs
+++ b/substrate/frame/asset-conversion/src/lib.rs
@@ -417,7 +417,7 @@ pub mod pallet {
 			match T::MultiAssetIdConverter::try_convert(asset1) {
 				MultiAssetIdConversionResult::Converted(asset) =>
 					if !T::Assets::contains(&asset, &pool_account) {
-						T::Assets::touch(asset, pool_account.clone(), sender.clone())?
+						T::Assets::touch(asset, &pool_account, &sender)?
 					},
 				MultiAssetIdConversionResult::Unsupported(_) => Err(Error::<T>::UnsupportedAsset)?,
 				MultiAssetIdConversionResult::Native => (),
@@ -425,7 +425,7 @@ pub mod pallet {
 			match T::MultiAssetIdConverter::try_convert(asset2) {
 				MultiAssetIdConversionResult::Converted(asset) =>
 					if !T::Assets::contains(&asset, &pool_account) {
-						T::Assets::touch(asset, pool_account.clone(), sender.clone())?
+						T::Assets::touch(asset, &pool_account, &sender)?
 					},
 				MultiAssetIdConversionResult::Unsupported(_) => Err(Error::<T>::UnsupportedAsset)?,
 				MultiAssetIdConversionResult::Native => (),
@@ -438,7 +438,7 @@ pub mod pallet {
 			NextPoolAssetId::<T>::set(Some(next_lp_token_id));
 
 			T::PoolAssets::create(lp_token.clone(), pool_account.clone(), false, 1u32.into())?;
-			T::PoolAssets::touch(lp_token.clone(), pool_account.clone(), sender.clone())?;
+			T::PoolAssets::touch(lp_token.clone(), &pool_account, &sender)?;
 
 			let pool_info = PoolInfo { lp_token: lp_token.clone() };
 			Pools::<T>::insert(pool_id.clone(), pool_info);
diff --git a/substrate/frame/assets/src/lib.rs b/substrate/frame/assets/src/lib.rs
index 13aee138ad3..f3ae03d667b 100644
--- a/substrate/frame/assets/src/lib.rs
+++ b/substrate/frame/assets/src/lib.rs
@@ -1648,8 +1648,20 @@ pub mod pallet {
 			T::AssetAccountDeposit::get()
 		}
 
-		fn touch(asset: T::AssetId, who: T::AccountId, depositor: T::AccountId) -> DispatchResult {
-			Self::do_touch(asset, who, depositor, false)
+		fn should_touch(asset: T::AssetId, who: &T::AccountId) -> bool {
+			match Asset::<T, I>::get(&asset) {
+				Some(info) if info.is_sufficient => false,
+				Some(_) => !Account::<T, I>::contains_key(asset, who),
+				_ => true,
+			}
+		}
+
+		fn touch(
+			asset: T::AssetId,
+			who: &T::AccountId,
+			depositor: &T::AccountId,
+		) -> DispatchResult {
+			Self::do_touch(asset, who.clone(), depositor.clone(), false)
 		}
 	}
 
diff --git a/substrate/frame/assets/src/tests.rs b/substrate/frame/assets/src/tests.rs
index f1b116a0f4a..e09648a51ec 100644
--- a/substrate/frame/assets/src/tests.rs
+++ b/substrate/frame/assets/src/tests.rs
@@ -28,6 +28,8 @@ use pallet_balances::Error as BalancesError;
 use sp_io::storage;
 use sp_runtime::{traits::ConvertInto, TokenError};
 
+mod sets;
+
 fn asset_ids() -> Vec<u32> {
 	let mut s: Vec<_> = Assets::asset_ids().collect();
 	s.sort();
diff --git a/substrate/frame/assets/src/tests/sets.rs b/substrate/frame/assets/src/tests/sets.rs
new file mode 100644
index 00000000000..bdff5175185
--- /dev/null
+++ b/substrate/frame/assets/src/tests/sets.rs
@@ -0,0 +1,346 @@
+// This file is part of Substrate.
+
+// Copyright (C) Parity Technologies (UK) Ltd.
+// SPDX-License-Identifier: Apache-2.0
+
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// 	http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+//! Tests for [`ItemOf`], [`fungible::UnionOf`] and [`fungibles::UnionOf`] set types.
+
+use super::*;
+use frame_support::{
+	parameter_types,
+	traits::{
+		fungible,
+		fungible::ItemOf,
+		fungibles,
+		tokens::{
+			fungibles::{
+				Balanced as FungiblesBalanced, Create as FungiblesCreate,
+				Inspect as FungiblesInspect, Mutate as FungiblesMutate,
+			},
+			Fortitude, Precision, Preservation,
+		},
+	},
+};
+use sp_runtime::{traits::ConvertToValue, Either};
+
+const FIRST_ASSET: u32 = 0;
+const UNKNOWN_ASSET: u32 = 10;
+
+parameter_types! {
+	pub const LeftAsset: Either<(), u32> = Either::Left(());
+	pub const RightAsset: Either<u32, ()> = Either::Right(());
+	pub const RightUnitAsset: Either<(), ()> = Either::Right(());
+}
+
+/// Implementation of the `fungible` traits through the [`ItemOf`] type, specifically for a
+/// single asset class from [`T`] identified by [`FIRST_ASSET`].
+type FirstFungible<T> = ItemOf<T, frame_support::traits::ConstU32<{ FIRST_ASSET }>, u64>;
+
+/// Implementation of the `fungible` traits through the [`ItemOf`] type, specifically for a
+/// single asset class from [`T`] identified by [`UNKNOWN_ASSET`].
+type UnknownFungible<T> = ItemOf<T, frame_support::traits::ConstU32<{ UNKNOWN_ASSET }>, u64>;
+
+/// Implementation of `fungibles` traits using [`fungibles::UnionOf`] that exclusively utilizes
+/// the [`FirstFungible`] from the left.
+type LeftFungible<T> = fungible::UnionOf<FirstFungible<T>, T, ConvertToValue<LeftAsset>, (), u64>;
+
+/// Implementation of `fungibles` traits using [`fungibles::UnionOf`] that exclusively utilizes
+/// the [`LeftFungible`] from the right.
+type RightFungible<T> =
+	fungible::UnionOf<UnknownFungible<T>, LeftFungible<T>, ConvertToValue<RightUnitAsset>, (), u64>;
+
+/// Implementation of `fungibles` traits using [`fungibles::UnionOf`] that exclusively utilizes
+/// the [`RightFungible`] from the left.
+type LeftFungibles<T> = fungibles::UnionOf<RightFungible<T>, T, ConvertToValue<LeftAsset>, (), u64>;
+
+/// Implementation of `fungibles` traits using [`fungibles::UnionOf`] that exclusively utilizes
+/// the [`LeftFungibles`] from the right.
+///
+/// By using this type, we can navigate through each branch of [`fungible::UnionOf`],
+/// [`fungibles::UnionOf`], and [`ItemOf`] to access the underlying `fungibles::*`
+/// implementation provided by the pallet.
+type First<T> = fungibles::UnionOf<T, LeftFungibles<T>, ConvertToValue<RightAsset>, (), u64>;
+
+#[test]
+fn deposit_from_set_types_works() {
+	new_test_ext().execute_with(|| {
+		let asset1 = 0;
+		let account1 = 1;
+		let account2 = 2;
+
+		assert_ok!(<Assets as FungiblesCreate<u64>>::create(asset1, account1, true, 1));
+		assert_ok!(Assets::mint_into(asset1, &account1, 100));
+
+		assert_eq!(First::<Assets>::total_issuance(()), 100);
+		assert_eq!(First::<Assets>::total_issuance(()), Assets::total_issuance(asset1));
+
+		let imb = First::<Assets>::deposit((), &account2, 50, Precision::Exact).unwrap();
+		assert_eq!(First::<Assets>::balance((), &account2), 50);
+		assert_eq!(First::<Assets>::total_issuance(()), 100);
+
+		assert_eq!(imb.peek(), 50);
+
+		let (imb1, imb2) = imb.split(30);
+		assert_eq!(imb1.peek(), 30);
+		assert_eq!(imb2.peek(), 20);
+
+		drop(imb2);
+		assert_eq!(First::<Assets>::total_issuance(()), 120);
+
+		assert!(First::<Assets>::settle(&account1, imb1, Preservation::Preserve).is_ok());
+		assert_eq!(First::<Assets>::balance((), &account1), 70);
+		assert_eq!(First::<Assets>::balance((), &account2), 50);
+		assert_eq!(First::<Assets>::total_issuance(()), 120);
+
+		assert_eq!(First::<Assets>::total_issuance(()), Assets::total_issuance(asset1));
+	});
+}
+
+#[test]
+fn issue_from_set_types_works() {
+	new_test_ext().execute_with(|| {
+		let asset1: u32 = 0;
+		let account1: u64 = 1;
+
+		assert_ok!(<Assets as FungiblesCreate<u64>>::create(asset1, account1, true, 1));
+		assert_ok!(Assets::mint_into(asset1, &account1, 100));
+
+		assert_eq!(First::<Assets>::balance((), &account1), 100);
+		assert_eq!(First::<Assets>::total_issuance(()), 100);
+		assert_eq!(First::<Assets>::total_issuance(()), Assets::total_issuance(asset1));
+
+		let imb = First::<Assets>::issue((), 100);
+		assert_eq!(First::<Assets>::total_issuance(()), 200);
+		assert_eq!(imb.peek(), 100);
+
+		let (imb1, imb2) = imb.split(30);
+		assert_eq!(imb1.peek(), 30);
+		assert_eq!(imb2.peek(), 70);
+
+		drop(imb2);
+		assert_eq!(First::<Assets>::total_issuance(()), 130);
+
+		assert!(First::<Assets>::resolve(&account1, imb1).is_ok());
+		assert_eq!(First::<Assets>::balance((), &account1), 130);
+		assert_eq!(First::<Assets>::total_issuance(()), 130);
+
+		assert_eq!(First::<Assets>::total_issuance(()), Assets::total_issuance(asset1));
+	});
+}
+
+#[test]
+fn pair_from_set_types_works() {
+	new_test_ext().execute_with(|| {
+		let asset1: u32 = 0;
+		let account1: u64 = 1;
+
+		assert_ok!(<Assets as FungiblesCreate<u64>>::create(asset1, account1, true, 1));
+		assert_ok!(Assets::mint_into(asset1, &account1, 100));
+
+		assert_eq!(First::<Assets>::balance((), &account1), 100);
+		assert_eq!(First::<Assets>::total_issuance(()), 100);
+		assert_eq!(First::<Assets>::total_issuance(()), Assets::total_issuance(asset1));
+
+		let (debt, credit) = First::<Assets>::pair((), 100);
+		assert_eq!(First::<Assets>::total_issuance(()), 100);
+		assert_eq!(debt.peek(), 100);
+		assert_eq!(credit.peek(), 100);
+
+		let (debt1, debt2) = debt.split(30);
+		assert_eq!(debt1.peek(), 30);
+		assert_eq!(debt2.peek(), 70);
+
+		drop(debt2);
+		assert_eq!(First::<Assets>::total_issuance(()), 170);
+
+		assert!(First::<Assets>::settle(&account1, debt1, Preservation::Preserve).is_ok());
+		assert_eq!(First::<Assets>::balance((), &account1), 70);
+		assert_eq!(First::<Assets>::total_issuance(()), 170);
+
+		let (credit1, credit2) = credit.split(40);
+		assert_eq!(credit1.peek(), 40);
+		assert_eq!(credit2.peek(), 60);
+
+		drop(credit2);
+		assert_eq!(First::<Assets>::total_issuance(()), 110);
+
+		assert!(First::<Assets>::resolve(&account1, credit1).is_ok());
+		assert_eq!(First::<Assets>::balance((), &account1), 110);
+		assert_eq!(First::<Assets>::total_issuance(()), 110);
+
+		assert_eq!(First::<Assets>::total_issuance(()), Assets::total_issuance(asset1));
+	});
+}
+
+#[test]
+fn rescind_from_set_types_works() {
+	new_test_ext().execute_with(|| {
+		let asset1: u32 = 0;
+		let account1: u64 = 1;
+
+		assert_ok!(<Assets as FungiblesCreate<u64>>::create(asset1, account1, true, 1));
+		assert_ok!(Assets::mint_into(asset1, &account1, 100));
+
+		assert_eq!(First::<Assets>::total_issuance(()), 100);
+		assert_eq!(First::<Assets>::total_issuance(()), Assets::total_issuance(asset1));
+
+		let imb = First::<Assets>::rescind((), 20);
+		assert_eq!(First::<Assets>::total_issuance(()), 80);
+
+		assert_eq!(imb.peek(), 20);
+
+		let (imb1, imb2) = imb.split(15);
+		assert_eq!(imb1.peek(), 15);
+		assert_eq!(imb2.peek(), 5);
+
+		drop(imb2);
+		assert_eq!(First::<Assets>::total_issuance(()), 85);
+
+		assert!(First::<Assets>::settle(&account1, imb1, Preservation::Preserve).is_ok());
+		assert_eq!(First::<Assets>::balance((), &account1), 85);
+		assert_eq!(First::<Assets>::total_issuance(()), 85);
+
+		assert_eq!(First::<Assets>::total_issuance(()), Assets::total_issuance(asset1));
+	});
+}
+
+#[test]
+fn resolve_from_set_types_works() {
+	new_test_ext().execute_with(|| {
+		let asset1: u32 = 0;
+		let account1: u64 = 1;
+		let account2: u64 = 2;
+		let ed = 11;
+
+		assert_ok!(<Assets as FungiblesCreate<u64>>::create(asset1, account1, true, ed));
+		assert_ok!(Assets::mint_into(asset1, &account1, 100));
+
+		assert_eq!(First::<Assets>::balance((), &account1), 100);
+		assert_eq!(First::<Assets>::total_issuance(()), 100);
+		assert_eq!(First::<Assets>::total_issuance(()), Assets::total_issuance(asset1));
+
+		let imb = First::<Assets>::issue((), 100);
+		assert_eq!(First::<Assets>::total_issuance(()), 200);
+		assert_eq!(imb.peek(), 100);
+
+		let (imb1, imb2) = imb.split(10);
+		assert_eq!(imb1.peek(), 10);
+		assert_eq!(imb2.peek(), 90);
+		assert_eq!(First::<Assets>::total_issuance(()), 200);
+
+		// ed requirements not met.
+		let imb1 = First::<Assets>::resolve(&account2, imb1).unwrap_err();
+		assert_eq!(imb1.peek(), 10);
+		drop(imb1);
+		assert_eq!(First::<Assets>::total_issuance(()), 190);
+		assert_eq!(First::<Assets>::balance((), &account2), 0);
+
+		// resolve to new account `2`.
+		assert_ok!(First::<Assets>::resolve(&account2, imb2));
+		assert_eq!(First::<Assets>::total_issuance(()), 190);
+		assert_eq!(First::<Assets>::balance((), &account2), 90);
+
+		assert_eq!(First::<Assets>::total_issuance(()), Assets::total_issuance(asset1));
+	});
+}
+
+#[test]
+fn settle_from_set_types_works() {
+	new_test_ext().execute_with(|| {
+		let asset1: u32 = 0;
+		let account1: u64 = 1;
+		let account2: u64 = 2;
+		let ed = 11;
+
+		assert_ok!(<Assets as FungiblesCreate<u64>>::create(asset1, account1, true, ed));
+		assert_ok!(Assets::mint_into(asset1, &account1, 100));
+		assert_ok!(Assets::mint_into(asset1, &account2, 100));
+
+		assert_eq!(First::<Assets>::balance((), &account2), 100);
+		assert_eq!(First::<Assets>::total_issuance(()), 200);
+		assert_eq!(First::<Assets>::total_issuance(()), Assets::total_issuance(asset1));
+
+		let imb = First::<Assets>::rescind((), 100);
+		assert_eq!(First::<Assets>::total_issuance(()), 100);
+		assert_eq!(imb.peek(), 100);
+
+		let (imb1, imb2) = imb.split(10);
+		assert_eq!(imb1.peek(), 10);
+		assert_eq!(imb2.peek(), 90);
+		assert_eq!(First::<Assets>::total_issuance(()), 100);
+
+		// ed requirements not met.
+		let imb2 = First::<Assets>::settle(&account2, imb2, Preservation::Preserve).unwrap_err();
+		assert_eq!(imb2.peek(), 90);
+		drop(imb2);
+		assert_eq!(First::<Assets>::total_issuance(()), 190);
+		assert_eq!(First::<Assets>::balance((), &account2), 100);
+
+		// settle to account `1`.
+		assert_ok!(First::<Assets>::settle(&account2, imb1, Preservation::Preserve));
+		assert_eq!(First::<Assets>::total_issuance(()), 190);
+		assert_eq!(First::<Assets>::balance((), &account2), 90);
+
+		let imb = First::<Assets>::rescind((), 85);
+		assert_eq!(First::<Assets>::total_issuance(()), 105);
+		assert_eq!(imb.peek(), 85);
+
+		// settle to account `1` and expect some dust.
+		let imb = First::<Assets>::settle(&account2, imb, Preservation::Expendable).unwrap();
+		assert_eq!(imb.peek(), 5);
+		assert_eq!(First::<Assets>::total_issuance(()), 105);
+		assert_eq!(First::<Assets>::balance((), &account2), 0);
+
+		drop(imb);
+		assert_eq!(First::<Assets>::total_issuance(()), 100);
+
+		assert_eq!(First::<Assets>::total_issuance(()), Assets::total_issuance(asset1));
+	});
+}
+
+#[test]
+fn withdraw_from_set_types_works() {
+	new_test_ext().execute_with(|| {
+		let asset1 = 0;
+		let account1 = 1;
+		let account2 = 2;
+
+		assert_ok!(<Assets as FungiblesCreate<u64>>::create(asset1, account1, true, 1));
+		assert_ok!(Assets::mint_into(asset1, &account1, 100));
+		assert_ok!(Assets::mint_into(asset1, &account2, 100));
+
+		assert_eq!(First::<Assets>::total_issuance(()), 200);
+		assert_eq!(First::<Assets>::total_issuance(()), Assets::total_issuance(asset1));
+
+		let imb = First::<Assets>::withdraw(
+			(),
+			&account2,
+			50,
+			Precision::Exact,
+			Preservation::Preserve,
+			Fortitude::Polite,
+		)
+		.unwrap();
+		assert_eq!(First::<Assets>::balance((), &account2), 50);
+		assert_eq!(First::<Assets>::total_issuance(()), 200);
+
+		assert_eq!(imb.peek(), 50);
+		drop(imb);
+		assert_eq!(First::<Assets>::total_issuance(()), 150);
+		assert_eq!(First::<Assets>::balance((), &account2), 50);
+
+		assert_eq!(First::<Assets>::total_issuance(()), Assets::total_issuance(asset1));
+	});
+}
diff --git a/substrate/frame/balances/src/impl_fungible.rs b/substrate/frame/balances/src/impl_fungible.rs
index fc8c2d71f25..6737727e0a2 100644
--- a/substrate/frame/balances/src/impl_fungible.rs
+++ b/substrate/frame/balances/src/impl_fungible.rs
@@ -17,10 +17,13 @@
 
 //! Implementation of `fungible` traits for Balances pallet.
 use super::*;
-use frame_support::traits::tokens::{
-	Fortitude,
-	Preservation::{self, Preserve, Protect},
-	Provenance::{self, Minted},
+use frame_support::traits::{
+	tokens::{
+		Fortitude,
+		Preservation::{self, Preserve, Protect},
+		Provenance::{self, Minted},
+	},
+	AccountTouch,
 };
 
 impl<T: Config<I>, I: 'static> fungible::Inspect<T::AccountId> for Pallet<T, I> {
@@ -356,3 +359,16 @@ impl<T: Config<I>, I: 'static> fungible::Balanced<T::AccountId> for Pallet<T, I>
 }
 
 impl<T: Config<I>, I: 'static> fungible::BalancedHold<T::AccountId> for Pallet<T, I> {}
+
+impl<T: Config<I>, I: 'static> AccountTouch<(), T::AccountId> for Pallet<T, I> {
+	type Balance = T::Balance;
+	fn deposit_required(_: ()) -> Self::Balance {
+		Self::Balance::zero()
+	}
+	fn should_touch(_: (), _: &T::AccountId) -> bool {
+		false
+	}
+	fn touch(_: (), _: &T::AccountId, _: &T::AccountId) -> DispatchResult {
+		Ok(())
+	}
+}
diff --git a/substrate/frame/support/src/traits/misc.rs b/substrate/frame/support/src/traits/misc.rs
index 78032cc0a94..bf3053a3f8f 100644
--- a/substrate/frame/support/src/traits/misc.rs
+++ b/substrate/frame/support/src/traits/misc.rs
@@ -1170,17 +1170,26 @@ impl<Hash> PreimageRecipient<Hash> for () {
 	fn unnote_preimage(_: &Hash) {}
 }
 
-/// Trait for creating an asset account with a deposit taken from a designated depositor specified
-/// by the client.
+/// Trait for touching/creating an asset account with a deposit taken from a designated depositor
+/// specified by the client.
+///
+/// Ensures that transfers to the touched account will succeed without being denied by the account
+/// creation requirements. For example, it is useful for the account creation of non-sufficient
+/// assets when its system account may not have the free consumer reference required for it. If
+/// there is no risk of failing to meet those requirements, the touch operation can be a no-op, as
+/// is common for native assets.
 pub trait AccountTouch<AssetId, AccountId> {
 	/// The type for currency units of the deposit.
 	type Balance;
 
-	/// The deposit amount of a native currency required for creating an account of the `asset`.
+	/// The deposit amount of a native currency required for touching an account of the `asset`.
 	fn deposit_required(asset: AssetId) -> Self::Balance;
 
+	/// Check if an account for a given asset should be touched to meet the existence requirements.
+	fn should_touch(asset: AssetId, who: &AccountId) -> bool;
+
 	/// Create an account for `who` of the `asset` with a deposit taken from the `depositor`.
-	fn touch(asset: AssetId, who: AccountId, depositor: AccountId) -> DispatchResult;
+	fn touch(asset: AssetId, who: &AccountId, depositor: &AccountId) -> DispatchResult;
 }
 
 #[cfg(test)]
diff --git a/substrate/frame/support/src/traits/tokens/fungible/imbalance.rs b/substrate/frame/support/src/traits/tokens/fungible/imbalance.rs
index 995797bc8f6..0e251021970 100644
--- a/substrate/frame/support/src/traits/tokens/fungible/imbalance.rs
+++ b/substrate/frame/support/src/traits/tokens/fungible/imbalance.rs
@@ -20,8 +20,9 @@
 
 use super::{super::Imbalance as ImbalanceT, Balanced, *};
 use crate::traits::{
+	fungibles,
 	misc::{SameOrOther, TryDrop},
-	tokens::Balance,
+	tokens::{AssetId, Balance},
 };
 use frame_support_procedural::{EqNoBound, PartialEqNoBound, RuntimeDebugNoBound};
 use sp_runtime::traits::Zero;
@@ -87,6 +88,11 @@ impl<B: Balance, OnDrop: HandleImbalanceDrop<B>, OppositeOnDrop: HandleImbalance
 	pub(crate) fn new(amount: B) -> Self {
 		Self { amount, _phantom: PhantomData }
 	}
+
+	/// Forget the imbalance without invoking the on-drop handler.
+	pub(crate) fn forget(imbalance: Self) {
+		sp_std::mem::forget(imbalance);
+	}
 }
 
 impl<B: Balance, OnDrop: HandleImbalanceDrop<B>, OppositeOnDrop: HandleImbalanceDrop<B>>
@@ -149,6 +155,27 @@ impl<B: Balance, OnDrop: HandleImbalanceDrop<B>, OppositeOnDrop: HandleImbalance
 	}
 }
 
+/// Converts a `fungibles` `imbalance` instance to an instance of a `fungible` imbalance type.
+///
+/// This function facilitates imbalance conversions within the implementations of
+/// [`frame_support::traits::fungibles::UnionOf`], [`frame_support::traits::fungible::UnionOf`], and
+/// [`frame_support::traits::fungible::ItemOf`] adapters. It is intended only for internal use
+/// within the current crate.
+pub(crate) fn from_fungibles<
+	A: AssetId,
+	B: Balance,
+	OnDropIn: fungibles::HandleImbalanceDrop<A, B>,
+	OppositeIn: fungibles::HandleImbalanceDrop<A, B>,
+	OnDropOut: HandleImbalanceDrop<B>,
+	OppositeOut: HandleImbalanceDrop<B>,
+>(
+	imbalance: fungibles::Imbalance<A, B, OnDropIn, OppositeIn>,
+) -> Imbalance<B, OnDropOut, OppositeOut> {
+	let new = Imbalance::new(imbalance.peek());
+	fungibles::Imbalance::forget(imbalance);
+	new
+}
+
 /// Imbalance implying that the total_issuance value is less than the sum of all account balances.
 pub type Debt<AccountId, B> = Imbalance<
 	<B as Inspect<AccountId>>::Balance,
diff --git a/substrate/frame/support/src/traits/tokens/fungible/item_of.rs b/substrate/frame/support/src/traits/tokens/fungible/item_of.rs
index 636866ab93c..fe252c6b089 100644
--- a/substrate/frame/support/src/traits/tokens/fungible/item_of.rs
+++ b/substrate/frame/support/src/traits/tokens/fungible/item_of.rs
@@ -17,14 +17,16 @@
 
 //! Adapter to use `fungibles::*` implementations as `fungible::*`.
 
-use sp_core::Get;
-use sp_runtime::{DispatchError, DispatchResult};
-
 use super::*;
-use crate::traits::tokens::{
-	fungibles, DepositConsequence, Fortitude, Imbalance as ImbalanceT, Precision, Preservation,
-	Provenance, Restriction, WithdrawConsequence,
+use crate::traits::{
+	fungible::imbalance,
+	tokens::{
+		fungibles, DepositConsequence, Fortitude, Precision, Preservation, Provenance, Restriction,
+		WithdrawConsequence,
+	},
 };
+use sp_core::Get;
+use sp_runtime::{DispatchError, DispatchResult};
 
 /// Convert a `fungibles` trait implementation into a `fungible` trait implementation by identifying
 /// a single item.
@@ -381,35 +383,38 @@ impl<
 		precision: Precision,
 	) -> Result<Debt<AccountId, Self>, DispatchError> {
 		<F as fungibles::Balanced<AccountId>>::deposit(A::get(), who, value, precision)
-			.map(|debt| Imbalance::new(debt.peek()))
+			.map(imbalance::from_fungibles)
 	}
 	fn issue(amount: Self::Balance) -> Credit<AccountId, Self> {
-		Imbalance::new(<F as fungibles::Balanced<AccountId>>::issue(A::get(), amount).peek())
+		let credit = <F as fungibles::Balanced<AccountId>>::issue(A::get(), amount);
+		imbalance::from_fungibles(credit)
 	}
 	fn pair(amount: Self::Balance) -> (Debt<AccountId, Self>, Credit<AccountId, Self>) {
 		let (a, b) = <F as fungibles::Balanced<AccountId>>::pair(A::get(), amount);
-		(Imbalance::new(a.peek()), Imbalance::new(b.peek()))
+		(imbalance::from_fungibles(a), imbalance::from_fungibles(b))
 	}
 	fn rescind(amount: Self::Balance) -> Debt<AccountId, Self> {
-		Imbalance::new(<F as fungibles::Balanced<AccountId>>::rescind(A::get(), amount).peek())
+		let debt = <F as fungibles::Balanced<AccountId>>::rescind(A::get(), amount);
+		imbalance::from_fungibles(debt)
 	}
 	fn resolve(
 		who: &AccountId,
 		credit: Credit<AccountId, Self>,
 	) -> Result<(), Credit<AccountId, Self>> {
-		let credit = fungibles::Imbalance::new(A::get(), credit.peek());
+		let credit = fungibles::imbalance::from_fungible(credit, A::get());
 		<F as fungibles::Balanced<AccountId>>::resolve(who, credit)
-			.map_err(|credit| Imbalance::new(credit.peek()))
+			.map_err(imbalance::from_fungibles)
 	}
 	fn settle(
 		who: &AccountId,
 		debt: Debt<AccountId, Self>,
 		preservation: Preservation,
 	) -> Result<Credit<AccountId, Self>, Debt<AccountId, Self>> {
-		let debt = fungibles::Imbalance::new(A::get(), debt.peek());
-		<F as fungibles::Balanced<AccountId>>::settle(who, debt, preservation)
-			.map(|credit| Imbalance::new(credit.peek()))
-			.map_err(|debt| Imbalance::new(debt.peek()))
+		let debt = fungibles::imbalance::from_fungible(debt, A::get());
+		<F as fungibles::Balanced<AccountId>>::settle(who, debt, preservation).map_or_else(
+			|d| Err(imbalance::from_fungibles(d)),
+			|c| Ok(imbalance::from_fungibles(c)),
+		)
 	}
 	fn withdraw(
 		who: &AccountId,
@@ -426,7 +431,7 @@ impl<
 			preservation,
 			force,
 		)
-		.map(|credit| Imbalance::new(credit.peek()))
+		.map(imbalance::from_fungibles)
 	}
 }
 
@@ -443,7 +448,7 @@ impl<
 	) -> (Credit<AccountId, Self>, Self::Balance) {
 		let (credit, amount) =
 			<F as fungibles::BalancedHold<AccountId>>::slash(A::get(), reason, who, amount);
-		(Imbalance::new(credit.peek()), amount)
+		(imbalance::from_fungibles(credit), amount)
 	}
 }
 
diff --git a/substrate/frame/support/src/traits/tokens/fungible/mod.rs b/substrate/frame/support/src/traits/tokens/fungible/mod.rs
index 61b75fd6563..ba4a2e5e21a 100644
--- a/substrate/frame/support/src/traits/tokens/fungible/mod.rs
+++ b/substrate/frame/support/src/traits/tokens/fungible/mod.rs
@@ -41,9 +41,10 @@
 pub mod conformance_tests;
 pub mod freeze;
 pub mod hold;
-mod imbalance;
+pub(crate) mod imbalance;
 mod item_of;
 mod regular;
+mod union_of;
 
 use codec::{Decode, Encode, MaxEncodedLen};
 use frame_support_procedural::{CloneNoBound, EqNoBound, PartialEqNoBound, RuntimeDebugNoBound};
@@ -67,6 +68,7 @@ pub use regular::{
 use sp_arithmetic::traits::Zero;
 use sp_core::Get;
 use sp_runtime::{traits::Convert, DispatchError};
+pub use union_of::{NativeFromLeft, NativeOrWithId, UnionOf};
 
 use crate::{
 	ensure,
diff --git a/substrate/frame/support/src/traits/tokens/fungible/union_of.rs b/substrate/frame/support/src/traits/tokens/fungible/union_of.rs
new file mode 100644
index 00000000000..86505befc05
--- /dev/null
+++ b/substrate/frame/support/src/traits/tokens/fungible/union_of.rs
@@ -0,0 +1,924 @@
+// This file is part of Substrate.
+
+// Copyright (Criterion) Parity Technologies (UK) Ltd.
+// SPDX-License-Identifier: Apache-2.0
+
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// 	http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+//! Types to combine some `fungible::*` and `fungibles::*` implementations into one union
+//! `fungibles::*` implementation.
+
+use codec::{Decode, Encode, MaxEncodedLen};
+use frame_support::traits::{
+	fungible::imbalance,
+	tokens::{
+		fungible, fungibles, AssetId, DepositConsequence, Fortitude, Precision, Preservation,
+		Provenance, Restriction, WithdrawConsequence,
+	},
+	AccountTouch,
+};
+use scale_info::TypeInfo;
+use sp_runtime::{
+	traits::Convert,
+	DispatchError, DispatchResult, Either,
+	Either::{Left, Right},
+	RuntimeDebug,
+};
+use sp_std::cmp::Ordering;
+
+/// The `NativeOrWithId` enum classifies an asset as either `Native` to the current chain or as an
+/// asset with a specific ID.
+#[derive(Decode, Encode, Default, MaxEncodedLen, TypeInfo, Clone, RuntimeDebug, Eq)]
+pub enum NativeOrWithId<AssetId>
+where
+	AssetId: Ord,
+{
+	/// Represents the native asset of the current chain.
+	///
+	/// E.g., DOT for the Polkadot Asset Hub.
+	#[default]
+	Native,
+	/// Represents an asset identified by its underlying `AssetId`.
+	WithId(AssetId),
+}
+impl<AssetId: Ord> From<AssetId> for NativeOrWithId<AssetId> {
+	fn from(asset: AssetId) -> Self {
+		Self::WithId(asset)
+	}
+}
+impl<AssetId: Ord> Ord for NativeOrWithId<AssetId> {
+	fn cmp(&self, other: &Self) -> Ordering {
+		match (self, other) {
+			(Self::Native, Self::Native) => Ordering::Equal,
+			(Self::Native, Self::WithId(_)) => Ordering::Less,
+			(Self::WithId(_), Self::Native) => Ordering::Greater,
+			(Self::WithId(id1), Self::WithId(id2)) => <AssetId as Ord>::cmp(id1, id2),
+		}
+	}
+}
+impl<AssetId: Ord> PartialOrd for NativeOrWithId<AssetId> {
+	fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
+		Some(<Self as Ord>::cmp(self, other))
+	}
+}
+impl<AssetId: Ord> PartialEq for NativeOrWithId<AssetId> {
+	fn eq(&self, other: &Self) -> bool {
+		self.cmp(other) == Ordering::Equal
+	}
+}
+
+/// Criterion for [`UnionOf`] where a set for [`NativeOrWithId::Native`] asset located from the left
+/// and for [`NativeOrWithId::WithId`] from the right.
+pub struct NativeFromLeft;
+impl<AssetId: Ord> Convert<NativeOrWithId<AssetId>, Either<(), AssetId>> for NativeFromLeft {
+	fn convert(asset: NativeOrWithId<AssetId>) -> Either<(), AssetId> {
+		match asset {
+			NativeOrWithId::Native => Either::Left(()),
+			NativeOrWithId::WithId(id) => Either::Right(id),
+		}
+	}
+}
+
+/// Type to combine some `fungible::*` and `fungibles::*` implementations into one union
+/// `fungibles::*` implementation.
+///
+/// ### Parameters:
+/// - `Left` is `fungible::*` implementation that is incorporated into the resulting union.
+/// - `Right` is `fungibles::*` implementation that is incorporated into the resulting union.
+/// - `Criterion` determines whether the `AssetKind` belongs to the `Left` or `Right` set.
+/// - `AssetKind` is a superset type encompassing asset kinds from `Left` and `Right` sets.
+/// - `AccountId` is an account identifier type.
+pub struct UnionOf<Left, Right, Criterion, AssetKind, AccountId>(
+	sp_std::marker::PhantomData<(Left, Right, Criterion, AssetKind, AccountId)>,
+);
+
+impl<
+		Left: fungible::Inspect<AccountId>,
+		Right: fungibles::Inspect<AccountId, Balance = Left::Balance>,
+		Criterion: Convert<AssetKind, Either<(), Right::AssetId>>,
+		AssetKind: AssetId,
+		AccountId,
+	> fungibles::Inspect<AccountId> for UnionOf<Left, Right, Criterion, AssetKind, AccountId>
+{
+	type AssetId = AssetKind;
+	type Balance = Left::Balance;
+
+	fn total_issuance(asset: Self::AssetId) -> Self::Balance {
+		match Criterion::convert(asset) {
+			Left(()) => <Left as fungible::Inspect<AccountId>>::total_issuance(),
+			Right(a) => <Right as fungibles::Inspect<AccountId>>::total_issuance(a),
+		}
+	}
+	fn active_issuance(asset: Self::AssetId) -> Self::Balance {
+		match Criterion::convert(asset) {
+			Left(()) => <Left as fungible::Inspect<AccountId>>::active_issuance(),
+			Right(a) => <Right as fungibles::Inspect<AccountId>>::active_issuance(a),
+		}
+	}
+	fn minimum_balance(asset: Self::AssetId) -> Self::Balance {
+		match Criterion::convert(asset) {
+			Left(()) => <Left as fungible::Inspect<AccountId>>::minimum_balance(),
+			Right(a) => <Right as fungibles::Inspect<AccountId>>::minimum_balance(a),
+		}
+	}
+	fn balance(asset: Self::AssetId, who: &AccountId) -> Self::Balance {
+		match Criterion::convert(asset) {
+			Left(()) => <Left as fungible::Inspect<AccountId>>::balance(who),
+			Right(a) => <Right as fungibles::Inspect<AccountId>>::balance(a, who),
+		}
+	}
+	fn total_balance(asset: Self::AssetId, who: &AccountId) -> Self::Balance {
+		match Criterion::convert(asset) {
+			Left(()) => <Left as fungible::Inspect<AccountId>>::total_balance(who),
+			Right(a) => <Right as fungibles::Inspect<AccountId>>::total_balance(a, who),
+		}
+	}
+	fn reducible_balance(
+		asset: Self::AssetId,
+		who: &AccountId,
+		preservation: Preservation,
+		force: Fortitude,
+	) -> Self::Balance {
+		match Criterion::convert(asset) {
+			Left(()) =>
+				<Left as fungible::Inspect<AccountId>>::reducible_balance(who, preservation, force),
+			Right(a) => <Right as fungibles::Inspect<AccountId>>::reducible_balance(
+				a,
+				who,
+				preservation,
+				force,
+			),
+		}
+	}
+	fn can_deposit(
+		asset: Self::AssetId,
+		who: &AccountId,
+		amount: Self::Balance,
+		provenance: Provenance,
+	) -> DepositConsequence {
+		match Criterion::convert(asset) {
+			Left(()) =>
+				<Left as fungible::Inspect<AccountId>>::can_deposit(who, amount, provenance),
+			Right(a) =>
+				<Right as fungibles::Inspect<AccountId>>::can_deposit(a, who, amount, provenance),
+		}
+	}
+	fn can_withdraw(
+		asset: Self::AssetId,
+		who: &AccountId,
+		amount: Self::Balance,
+	) -> WithdrawConsequence<Self::Balance> {
+		match Criterion::convert(asset) {
+			Left(()) => <Left as fungible::Inspect<AccountId>>::can_withdraw(who, amount),
+			Right(a) => <Right as fungibles::Inspect<AccountId>>::can_withdraw(a, who, amount),
+		}
+	}
+	fn asset_exists(asset: Self::AssetId) -> bool {
+		match Criterion::convert(asset) {
+			Left(()) => true,
+			Right(a) => <Right as fungibles::Inspect<AccountId>>::asset_exists(a),
+		}
+	}
+}
+
+impl<
+		Left: fungible::InspectHold<AccountId>,
+		Right: fungibles::InspectHold<AccountId, Balance = Left::Balance, Reason = Left::Reason>,
+		Criterion: Convert<AssetKind, Either<(), Right::AssetId>>,
+		AssetKind: AssetId,
+		AccountId,
+	> fungibles::InspectHold<AccountId> for UnionOf<Left, Right, Criterion, AssetKind, AccountId>
+{
+	type Reason = Left::Reason;
+
+	fn reducible_total_balance_on_hold(
+		asset: Self::AssetId,
+		who: &AccountId,
+		force: Fortitude,
+	) -> Self::Balance {
+		match Criterion::convert(asset) {
+			Left(()) =>
+				<Left as fungible::InspectHold<AccountId>>::reducible_total_balance_on_hold(
+					who, force,
+				),
+			Right(a) =>
+				<Right as fungibles::InspectHold<AccountId>>::reducible_total_balance_on_hold(
+					a, who, force,
+				),
+		}
+	}
+	fn hold_available(asset: Self::AssetId, reason: &Self::Reason, who: &AccountId) -> bool {
+		match Criterion::convert(asset) {
+			Left(()) => <Left as fungible::InspectHold<AccountId>>::hold_available(reason, who),
+			Right(a) =>
+				<Right as fungibles::InspectHold<AccountId>>::hold_available(a, reason, who),
+		}
+	}
+	fn total_balance_on_hold(asset: Self::AssetId, who: &AccountId) -> Self::Balance {
+		match Criterion::convert(asset) {
+			Left(()) => <Left as fungible::InspectHold<AccountId>>::total_balance_on_hold(who),
+			Right(a) => <Right as fungibles::InspectHold<AccountId>>::total_balance_on_hold(a, who),
+		}
+	}
+	fn balance_on_hold(
+		asset: Self::AssetId,
+		reason: &Self::Reason,
+		who: &AccountId,
+	) -> Self::Balance {
+		match Criterion::convert(asset) {
+			Left(()) => <Left as fungible::InspectHold<AccountId>>::balance_on_hold(reason, who),
+			Right(a) =>
+				<Right as fungibles::InspectHold<AccountId>>::balance_on_hold(a, reason, who),
+		}
+	}
+	fn can_hold(
+		asset: Self::AssetId,
+		reason: &Self::Reason,
+		who: &AccountId,
+		amount: Self::Balance,
+	) -> bool {
+		match Criterion::convert(asset) {
+			Left(()) => <Left as fungible::InspectHold<AccountId>>::can_hold(reason, who, amount),
+			Right(a) =>
+				<Right as fungibles::InspectHold<AccountId>>::can_hold(a, reason, who, amount),
+		}
+	}
+}
+
+impl<
+		Left: fungible::InspectFreeze<AccountId>,
+		Right: fungibles::InspectFreeze<AccountId, Balance = Left::Balance, Id = Left::Id>,
+		Criterion: Convert<AssetKind, Either<(), Right::AssetId>>,
+		AssetKind: AssetId,
+		AccountId,
+	> fungibles::InspectFreeze<AccountId> for UnionOf<Left, Right, Criterion, AssetKind, AccountId>
+{
+	type Id = Left::Id;
+	fn balance_frozen(asset: Self::AssetId, id: &Self::Id, who: &AccountId) -> Self::Balance {
+		match Criterion::convert(asset) {
+			Left(()) => <Left as fungible::InspectFreeze<AccountId>>::balance_frozen(id, who),
+			Right(a) => <Right as fungibles::InspectFreeze<AccountId>>::balance_frozen(a, id, who),
+		}
+	}
+	fn balance_freezable(asset: Self::AssetId, who: &AccountId) -> Self::Balance {
+		match Criterion::convert(asset) {
+			Left(()) => <Left as fungible::InspectFreeze<AccountId>>::balance_freezable(who),
+			Right(a) => <Right as fungibles::InspectFreeze<AccountId>>::balance_freezable(a, who),
+		}
+	}
+	fn can_freeze(asset: Self::AssetId, id: &Self::Id, who: &AccountId) -> bool {
+		match Criterion::convert(asset) {
+			Left(()) => <Left as fungible::InspectFreeze<AccountId>>::can_freeze(id, who),
+			Right(a) => <Right as fungibles::InspectFreeze<AccountId>>::can_freeze(a, id, who),
+		}
+	}
+}
+
+impl<
+		Left: fungible::Unbalanced<AccountId>,
+		Right: fungibles::Unbalanced<AccountId, Balance = Left::Balance>,
+		Criterion: Convert<AssetKind, Either<(), Right::AssetId>>,
+		AssetKind: AssetId,
+		AccountId,
+	> fungibles::Unbalanced<AccountId> for UnionOf<Left, Right, Criterion, AssetKind, AccountId>
+{
+	fn handle_dust(dust: fungibles::Dust<AccountId, Self>)
+	where
+		Self: Sized,
+	{
+		match Criterion::convert(dust.0) {
+			Left(()) =>
+				<Left as fungible::Unbalanced<AccountId>>::handle_dust(fungible::Dust(dust.1)),
+			Right(a) =>
+				<Right as fungibles::Unbalanced<AccountId>>::handle_dust(fungibles::Dust(a, dust.1)),
+		}
+	}
+	fn write_balance(
+		asset: Self::AssetId,
+		who: &AccountId,
+		amount: Self::Balance,
+	) -> Result<Option<Self::Balance>, DispatchError> {
+		match Criterion::convert(asset) {
+			Left(()) => <Left as fungible::Unbalanced<AccountId>>::write_balance(who, amount),
+			Right(a) => <Right as fungibles::Unbalanced<AccountId>>::write_balance(a, who, amount),
+		}
+	}
+	fn set_total_issuance(asset: Self::AssetId, amount: Self::Balance) -> () {
+		match Criterion::convert(asset) {
+			Left(()) => <Left as fungible::Unbalanced<AccountId>>::set_total_issuance(amount),
+			Right(a) => <Right as fungibles::Unbalanced<AccountId>>::set_total_issuance(a, amount),
+		}
+	}
+	fn decrease_balance(
+		asset: Self::AssetId,
+		who: &AccountId,
+		amount: Self::Balance,
+		precision: Precision,
+		preservation: Preservation,
+		force: Fortitude,
+	) -> Result<Self::Balance, DispatchError> {
+		match Criterion::convert(asset) {
+			Left(()) => <Left as fungible::Unbalanced<AccountId>>::decrease_balance(
+				who,
+				amount,
+				precision,
+				preservation,
+				force,
+			),
+			Right(a) => <Right as fungibles::Unbalanced<AccountId>>::decrease_balance(
+				a,
+				who,
+				amount,
+				precision,
+				preservation,
+				force,
+			),
+		}
+	}
+	fn increase_balance(
+		asset: Self::AssetId,
+		who: &AccountId,
+		amount: Self::Balance,
+		precision: Precision,
+	) -> Result<Self::Balance, DispatchError> {
+		match Criterion::convert(asset) {
+			Left(()) =>
+				<Left as fungible::Unbalanced<AccountId>>::increase_balance(who, amount, precision),
+			Right(a) => <Right as fungibles::Unbalanced<AccountId>>::increase_balance(
+				a, who, amount, precision,
+			),
+		}
+	}
+}
+
+impl<
+		Left: fungible::UnbalancedHold<AccountId>,
+		Right: fungibles::UnbalancedHold<AccountId, Balance = Left::Balance, Reason = Left::Reason>,
+		Criterion: Convert<AssetKind, Either<(), Right::AssetId>>,
+		AssetKind: AssetId,
+		AccountId,
+	> fungibles::UnbalancedHold<AccountId> for UnionOf<Left, Right, Criterion, AssetKind, AccountId>
+{
+	fn set_balance_on_hold(
+		asset: Self::AssetId,
+		reason: &Self::Reason,
+		who: &AccountId,
+		amount: Self::Balance,
+	) -> DispatchResult {
+		match Criterion::convert(asset) {
+			Left(()) => <Left as fungible::UnbalancedHold<AccountId>>::set_balance_on_hold(
+				reason, who, amount,
+			),
+			Right(a) => <Right as fungibles::UnbalancedHold<AccountId>>::set_balance_on_hold(
+				a, reason, who, amount,
+			),
+		}
+	}
+	fn decrease_balance_on_hold(
+		asset: Self::AssetId,
+		reason: &Self::Reason,
+		who: &AccountId,
+		amount: Self::Balance,
+		precision: Precision,
+	) -> Result<Self::Balance, DispatchError> {
+		match Criterion::convert(asset) {
+			Left(()) => <Left as fungible::UnbalancedHold<AccountId>>::decrease_balance_on_hold(
+				reason, who, amount, precision,
+			),
+			Right(a) => <Right as fungibles::UnbalancedHold<AccountId>>::decrease_balance_on_hold(
+				a, reason, who, amount, precision,
+			),
+		}
+	}
+	fn increase_balance_on_hold(
+		asset: Self::AssetId,
+		reason: &Self::Reason,
+		who: &AccountId,
+		amount: Self::Balance,
+		precision: Precision,
+	) -> Result<Self::Balance, DispatchError> {
+		match Criterion::convert(asset) {
+			Left(()) => <Left as fungible::UnbalancedHold<AccountId>>::increase_balance_on_hold(
+				reason, who, amount, precision,
+			),
+			Right(a) => <Right as fungibles::UnbalancedHold<AccountId>>::increase_balance_on_hold(
+				a, reason, who, amount, precision,
+			),
+		}
+	}
+}
+
+impl<
+		Left: fungible::Mutate<AccountId>,
+		Right: fungibles::Mutate<AccountId, Balance = Left::Balance>,
+		Criterion: Convert<AssetKind, Either<(), Right::AssetId>>,
+		AssetKind: AssetId,
+		AccountId: Eq,
+	> fungibles::Mutate<AccountId> for UnionOf<Left, Right, Criterion, AssetKind, AccountId>
+{
+	fn mint_into(
+		asset: Self::AssetId,
+		who: &AccountId,
+		amount: Self::Balance,
+	) -> Result<Self::Balance, DispatchError> {
+		match Criterion::convert(asset) {
+			Left(()) => <Left as fungible::Mutate<AccountId>>::mint_into(who, amount),
+			Right(a) => <Right as fungibles::Mutate<AccountId>>::mint_into(a, who, amount),
+		}
+	}
+	fn burn_from(
+		asset: Self::AssetId,
+		who: &AccountId,
+		amount: Self::Balance,
+		precision: Precision,
+		force: Fortitude,
+	) -> Result<Self::Balance, DispatchError> {
+		match Criterion::convert(asset) {
+			Left(()) =>
+				<Left as fungible::Mutate<AccountId>>::burn_from(who, amount, precision, force),
+			Right(a) =>
+				<Right as fungibles::Mutate<AccountId>>::burn_from(a, who, amount, precision, force),
+		}
+	}
+	fn shelve(
+		asset: Self::AssetId,
+		who: &AccountId,
+		amount: Self::Balance,
+	) -> Result<Self::Balance, DispatchError> {
+		match Criterion::convert(asset) {
+			Left(()) => <Left as fungible::Mutate<AccountId>>::shelve(who, amount),
+			Right(a) => <Right as fungibles::Mutate<AccountId>>::shelve(a, who, amount),
+		}
+	}
+	fn restore(
+		asset: Self::AssetId,
+		who: &AccountId,
+		amount: Self::Balance,
+	) -> Result<Self::Balance, DispatchError> {
+		match Criterion::convert(asset) {
+			Left(()) => <Left as fungible::Mutate<AccountId>>::restore(who, amount),
+			Right(a) => <Right as fungibles::Mutate<AccountId>>::restore(a, who, amount),
+		}
+	}
+	fn transfer(
+		asset: Self::AssetId,
+		source: &AccountId,
+		dest: &AccountId,
+		amount: Self::Balance,
+		preservation: Preservation,
+	) -> Result<Self::Balance, DispatchError> {
+		match Criterion::convert(asset) {
+			Left(()) =>
+				<Left as fungible::Mutate<AccountId>>::transfer(source, dest, amount, preservation),
+			Right(a) => <Right as fungibles::Mutate<AccountId>>::transfer(
+				a,
+				source,
+				dest,
+				amount,
+				preservation,
+			),
+		}
+	}
+
+	fn set_balance(asset: Self::AssetId, who: &AccountId, amount: Self::Balance) -> Self::Balance {
+		match Criterion::convert(asset) {
+			Left(()) => <Left as fungible::Mutate<AccountId>>::set_balance(who, amount),
+			Right(a) => <Right as fungibles::Mutate<AccountId>>::set_balance(a, who, amount),
+		}
+	}
+}
+
+impl<
+		Left: fungible::MutateHold<AccountId>,
+		Right: fungibles::MutateHold<AccountId, Balance = Left::Balance, Reason = Left::Reason>,
+		Criterion: Convert<AssetKind, Either<(), Right::AssetId>>,
+		AssetKind: AssetId,
+		AccountId,
+	> fungibles::MutateHold<AccountId> for UnionOf<Left, Right, Criterion, AssetKind, AccountId>
+{
+	fn hold(
+		asset: Self::AssetId,
+		reason: &Self::Reason,
+		who: &AccountId,
+		amount: Self::Balance,
+	) -> DispatchResult {
+		match Criterion::convert(asset) {
+			Left(()) => <Left as fungible::MutateHold<AccountId>>::hold(reason, who, amount),
+			Right(a) => <Right as fungibles::MutateHold<AccountId>>::hold(a, reason, who, amount),
+		}
+	}
+	fn release(
+		asset: Self::AssetId,
+		reason: &Self::Reason,
+		who: &AccountId,
+		amount: Self::Balance,
+		precision: Precision,
+	) -> Result<Self::Balance, DispatchError> {
+		match Criterion::convert(asset) {
+			Left(()) =>
+				<Left as fungible::MutateHold<AccountId>>::release(reason, who, amount, precision),
+			Right(a) => <Right as fungibles::MutateHold<AccountId>>::release(
+				a, reason, who, amount, precision,
+			),
+		}
+	}
+	fn burn_held(
+		asset: Self::AssetId,
+		reason: &Self::Reason,
+		who: &AccountId,
+		amount: Self::Balance,
+		precision: Precision,
+		force: Fortitude,
+	) -> Result<Self::Balance, DispatchError> {
+		match Criterion::convert(asset) {
+			Left(()) => <Left as fungible::MutateHold<AccountId>>::burn_held(
+				reason, who, amount, precision, force,
+			),
+			Right(a) => <Right as fungibles::MutateHold<AccountId>>::burn_held(
+				a, reason, who, amount, precision, force,
+			),
+		}
+	}
+	fn transfer_on_hold(
+		asset: Self::AssetId,
+		reason: &Self::Reason,
+		source: &AccountId,
+		dest: &AccountId,
+		amount: Self::Balance,
+		precision: Precision,
+		mode: Restriction,
+		force: Fortitude,
+	) -> Result<Self::Balance, DispatchError> {
+		match Criterion::convert(asset) {
+			Left(()) => <Left as fungible::MutateHold<AccountId>>::transfer_on_hold(
+				reason, source, dest, amount, precision, mode, force,
+			),
+			Right(a) => <Right as fungibles::MutateHold<AccountId>>::transfer_on_hold(
+				a, reason, source, dest, amount, precision, mode, force,
+			),
+		}
+	}
+	fn transfer_and_hold(
+		asset: Self::AssetId,
+		reason: &Self::Reason,
+		source: &AccountId,
+		dest: &AccountId,
+		amount: Self::Balance,
+		precision: Precision,
+		preservation: Preservation,
+		force: Fortitude,
+	) -> Result<Self::Balance, DispatchError> {
+		match Criterion::convert(asset) {
+			Left(()) => <Left as fungible::MutateHold<AccountId>>::transfer_and_hold(
+				reason,
+				source,
+				dest,
+				amount,
+				precision,
+				preservation,
+				force,
+			),
+			Right(a) => <Right as fungibles::MutateHold<AccountId>>::transfer_and_hold(
+				a,
+				reason,
+				source,
+				dest,
+				amount,
+				precision,
+				preservation,
+				force,
+			),
+		}
+	}
+}
+
+impl<
+		Left: fungible::MutateFreeze<AccountId>,
+		Right: fungibles::MutateFreeze<AccountId, Balance = Left::Balance, Id = Left::Id>,
+		Criterion: Convert<AssetKind, Either<(), Right::AssetId>>,
+		AssetKind: AssetId,
+		AccountId,
+	> fungibles::MutateFreeze<AccountId> for UnionOf<Left, Right, Criterion, AssetKind, AccountId>
+{
+	fn set_freeze(
+		asset: Self::AssetId,
+		id: &Self::Id,
+		who: &AccountId,
+		amount: Self::Balance,
+	) -> DispatchResult {
+		match Criterion::convert(asset) {
+			Left(()) => <Left as fungible::MutateFreeze<AccountId>>::set_freeze(id, who, amount),
+			Right(a) =>
+				<Right as fungibles::MutateFreeze<AccountId>>::set_freeze(a, id, who, amount),
+		}
+	}
+	fn extend_freeze(
+		asset: Self::AssetId,
+		id: &Self::Id,
+		who: &AccountId,
+		amount: Self::Balance,
+	) -> DispatchResult {
+		match Criterion::convert(asset) {
+			Left(()) => <Left as fungible::MutateFreeze<AccountId>>::extend_freeze(id, who, amount),
+			Right(a) =>
+				<Right as fungibles::MutateFreeze<AccountId>>::extend_freeze(a, id, who, amount),
+		}
+	}
+	fn thaw(asset: Self::AssetId, id: &Self::Id, who: &AccountId) -> DispatchResult {
+		match Criterion::convert(asset) {
+			Left(()) => <Left as fungible::MutateFreeze<AccountId>>::thaw(id, who),
+			Right(a) => <Right as fungibles::MutateFreeze<AccountId>>::thaw(a, id, who),
+		}
+	}
+}
+
+pub struct ConvertImbalanceDropHandler<
+	Left,
+	Right,
+	Criterion,
+	AssetKind,
+	Balance,
+	AssetId,
+	AccountId,
+>(sp_std::marker::PhantomData<(Left, Right, Criterion, AssetKind, Balance, AssetId, AccountId)>);
+
+impl<
+		Left: fungible::HandleImbalanceDrop<Balance>,
+		Right: fungibles::HandleImbalanceDrop<AssetId, Balance>,
+		Criterion: Convert<AssetKind, Either<(), AssetId>>,
+		AssetKind,
+		Balance,
+		AssetId,
+		AccountId,
+	> fungibles::HandleImbalanceDrop<AssetKind, Balance>
+	for ConvertImbalanceDropHandler<Left, Right, Criterion, AssetKind, Balance, AssetId, AccountId>
+{
+	fn handle(asset: AssetKind, amount: Balance) {
+		match Criterion::convert(asset) {
+			Left(()) => Left::handle(amount),
+			Right(a) => Right::handle(a, amount),
+		}
+	}
+}
+
+impl<
+		Left: fungible::Balanced<AccountId>,
+		Right: fungibles::Balanced<AccountId, Balance = Left::Balance>,
+		Criterion: Convert<AssetKind, Either<(), Right::AssetId>>,
+		AssetKind: AssetId,
+		AccountId,
+	> fungibles::Balanced<AccountId> for UnionOf<Left, Right, Criterion, AssetKind, AccountId>
+{
+	type OnDropDebt = ConvertImbalanceDropHandler<
+		Left::OnDropDebt,
+		Right::OnDropDebt,
+		Criterion,
+		AssetKind,
+		Left::Balance,
+		Right::AssetId,
+		AccountId,
+	>;
+	type OnDropCredit = ConvertImbalanceDropHandler<
+		Left::OnDropCredit,
+		Right::OnDropCredit,
+		Criterion,
+		AssetKind,
+		Left::Balance,
+		Right::AssetId,
+		AccountId,
+	>;
+
+	fn deposit(
+		asset: Self::AssetId,
+		who: &AccountId,
+		value: Self::Balance,
+		precision: Precision,
+	) -> Result<fungibles::Debt<AccountId, Self>, DispatchError> {
+		match Criterion::convert(asset.clone()) {
+			Left(()) => <Left as fungible::Balanced<AccountId>>::deposit(who, value, precision)
+				.map(|d| fungibles::imbalance::from_fungible(d, asset)),
+			Right(a) =>
+				<Right as fungibles::Balanced<AccountId>>::deposit(a, who, value, precision)
+					.map(|d| fungibles::imbalance::from_fungibles(d, asset)),
+		}
+	}
+	fn issue(asset: Self::AssetId, amount: Self::Balance) -> fungibles::Credit<AccountId, Self> {
+		match Criterion::convert(asset.clone()) {
+			Left(()) => {
+				let credit = <Left as fungible::Balanced<AccountId>>::issue(amount);
+				fungibles::imbalance::from_fungible(credit, asset)
+			},
+			Right(a) => {
+				let credit = <Right as fungibles::Balanced<AccountId>>::issue(a, amount);
+				fungibles::imbalance::from_fungibles(credit, asset)
+			},
+		}
+	}
+	fn pair(
+		asset: Self::AssetId,
+		amount: Self::Balance,
+	) -> (fungibles::Debt<AccountId, Self>, fungibles::Credit<AccountId, Self>) {
+		match Criterion::convert(asset.clone()) {
+			Left(()) => {
+				let (a, b) = <Left as fungible::Balanced<AccountId>>::pair(amount);
+				(
+					fungibles::imbalance::from_fungible(a, asset.clone()),
+					fungibles::imbalance::from_fungible(b, asset),
+				)
+			},
+			Right(a) => {
+				let (a, b) = <Right as fungibles::Balanced<AccountId>>::pair(a, amount);
+				(
+					fungibles::imbalance::from_fungibles(a, asset.clone()),
+					fungibles::imbalance::from_fungibles(b, asset),
+				)
+			},
+		}
+	}
+	fn rescind(asset: Self::AssetId, amount: Self::Balance) -> fungibles::Debt<AccountId, Self> {
+		match Criterion::convert(asset.clone()) {
+			Left(()) => {
+				let debt = <Left as fungible::Balanced<AccountId>>::rescind(amount);
+				fungibles::imbalance::from_fungible(debt, asset)
+			},
+			Right(a) => {
+				let debt = <Right as fungibles::Balanced<AccountId>>::rescind(a, amount);
+				fungibles::imbalance::from_fungibles(debt, asset)
+			},
+		}
+	}
+	fn resolve(
+		who: &AccountId,
+		credit: fungibles::Credit<AccountId, Self>,
+	) -> Result<(), fungibles::Credit<AccountId, Self>> {
+		let asset = credit.asset();
+		match Criterion::convert(asset.clone()) {
+			Left(()) => {
+				let credit = imbalance::from_fungibles(credit);
+				<Left as fungible::Balanced<AccountId>>::resolve(who, credit)
+					.map_err(|credit| fungibles::imbalance::from_fungible(credit, asset))
+			},
+			Right(a) => {
+				let credit = fungibles::imbalance::from_fungibles(credit, a);
+				<Right as fungibles::Balanced<AccountId>>::resolve(who, credit)
+					.map_err(|credit| fungibles::imbalance::from_fungibles(credit, asset))
+			},
+		}
+	}
+	fn settle(
+		who: &AccountId,
+		debt: fungibles::Debt<AccountId, Self>,
+		preservation: Preservation,
+	) -> Result<fungibles::Credit<AccountId, Self>, fungibles::Debt<AccountId, Self>> {
+		let asset = debt.asset();
+		match Criterion::convert(asset.clone()) {
+			Left(()) => {
+				let debt = imbalance::from_fungibles(debt);
+				match <Left as fungible::Balanced<AccountId>>::settle(who, debt, preservation) {
+					Ok(c) => Ok(fungibles::imbalance::from_fungible(c, asset)),
+					Err(d) => Err(fungibles::imbalance::from_fungible(d, asset)),
+				}
+			},
+			Right(a) => {
+				let debt = fungibles::imbalance::from_fungibles(debt, a);
+				match <Right as fungibles::Balanced<AccountId>>::settle(who, debt, preservation) {
+					Ok(c) => Ok(fungibles::imbalance::from_fungibles(c, asset)),
+					Err(d) => Err(fungibles::imbalance::from_fungibles(d, asset)),
+				}
+			},
+		}
+	}
+	fn withdraw(
+		asset: Self::AssetId,
+		who: &AccountId,
+		value: Self::Balance,
+		precision: Precision,
+		preservation: Preservation,
+		force: Fortitude,
+	) -> Result<fungibles::Credit<AccountId, Self>, DispatchError> {
+		match Criterion::convert(asset.clone()) {
+			Left(()) => <Left as fungible::Balanced<AccountId>>::withdraw(
+				who,
+				value,
+				precision,
+				preservation,
+				force,
+			)
+			.map(|c| fungibles::imbalance::from_fungible(c, asset)),
+			Right(a) => <Right as fungibles::Balanced<AccountId>>::withdraw(
+				a,
+				who,
+				value,
+				precision,
+				preservation,
+				force,
+			)
+			.map(|c| fungibles::imbalance::from_fungibles(c, asset)),
+		}
+	}
+}
+
+impl<
+		Left: fungible::BalancedHold<AccountId>,
+		Right: fungibles::BalancedHold<AccountId, Balance = Left::Balance, Reason = Left::Reason>,
+		Criterion: Convert<AssetKind, Either<(), Right::AssetId>>,
+		AssetKind: AssetId,
+		AccountId,
+	> fungibles::BalancedHold<AccountId> for UnionOf<Left, Right, Criterion, AssetKind, AccountId>
+{
+	fn slash(
+		asset: Self::AssetId,
+		reason: &Self::Reason,
+		who: &AccountId,
+		amount: Self::Balance,
+	) -> (fungibles::Credit<AccountId, Self>, Self::Balance) {
+		match Criterion::convert(asset.clone()) {
+			Left(()) => {
+				let (credit, amount) =
+					<Left as fungible::BalancedHold<AccountId>>::slash(reason, who, amount);
+				(fungibles::imbalance::from_fungible(credit, asset), amount)
+			},
+			Right(a) => {
+				let (credit, amount) =
+					<Right as fungibles::BalancedHold<AccountId>>::slash(a, reason, who, amount);
+				(fungibles::imbalance::from_fungibles(credit, asset), amount)
+			},
+		}
+	}
+}
+
+impl<
+		Left: fungible::Inspect<AccountId>,
+		Right: fungibles::Inspect<AccountId, Balance = Left::Balance> + fungibles::Create<AccountId>,
+		Criterion: Convert<AssetKind, Either<(), Right::AssetId>>,
+		AssetKind: AssetId,
+		AccountId,
+	> fungibles::Create<AccountId> for UnionOf<Left, Right, Criterion, AssetKind, AccountId>
+{
+	fn create(
+		asset: AssetKind,
+		admin: AccountId,
+		is_sufficient: bool,
+		min_balance: Self::Balance,
+	) -> DispatchResult {
+		match Criterion::convert(asset) {
+			// no-op for `Left` since `Create` trait is not defined within `fungible::*`.
+			Left(()) => Ok(()),
+			Right(a) => <Right as fungibles::Create<AccountId>>::create(
+				a,
+				admin,
+				is_sufficient,
+				min_balance,
+			),
+		}
+	}
+}
+
+impl<
+		Left: fungible::Inspect<AccountId>
+			+ AccountTouch<(), AccountId, Balance = <Left as fungible::Inspect<AccountId>>::Balance>,
+		Right: fungibles::Inspect<AccountId>
+			+ AccountTouch<
+				Right::AssetId,
+				AccountId,
+				Balance = <Left as fungible::Inspect<AccountId>>::Balance,
+			>,
+		Criterion: Convert<AssetKind, Either<(), Right::AssetId>>,
+		AssetKind: AssetId,
+		AccountId,
+	> AccountTouch<AssetKind, AccountId> for UnionOf<Left, Right, Criterion, AssetKind, AccountId>
+{
+	type Balance = <Left as fungible::Inspect<AccountId>>::Balance;
+
+	fn deposit_required(asset: AssetKind) -> Self::Balance {
+		match Criterion::convert(asset) {
+			Left(()) => <Left as AccountTouch<(), AccountId>>::deposit_required(()),
+			Right(a) => <Right as AccountTouch<Right::AssetId, AccountId>>::deposit_required(a),
+		}
+	}
+
+	fn should_touch(asset: AssetKind, who: &AccountId) -> bool {
+		match Criterion::convert(asset) {
+			Left(()) => <Left as AccountTouch<(), AccountId>>::should_touch((), who),
+			Right(a) => <Right as AccountTouch<Right::AssetId, AccountId>>::should_touch(a, who),
+		}
+	}
+
+	fn touch(asset: AssetKind, who: &AccountId, depositor: &AccountId) -> DispatchResult {
+		match Criterion::convert(asset) {
+			Left(()) => <Left as AccountTouch<(), AccountId>>::touch((), who, depositor),
+			Right(a) =>
+				<Right as AccountTouch<Right::AssetId, AccountId>>::touch(a, who, depositor),
+		}
+	}
+}
diff --git a/substrate/frame/support/src/traits/tokens/fungibles/imbalance.rs b/substrate/frame/support/src/traits/tokens/fungibles/imbalance.rs
index 7c0d7721a2e..54c1e900b6e 100644
--- a/substrate/frame/support/src/traits/tokens/fungibles/imbalance.rs
+++ b/substrate/frame/support/src/traits/tokens/fungibles/imbalance.rs
@@ -20,8 +20,9 @@
 
 use super::*;
 use crate::traits::{
+	fungible,
 	misc::{SameOrOther, TryDrop},
-	tokens::{AssetId, Balance},
+	tokens::{imbalance::Imbalance as ImbalanceT, AssetId, Balance},
 };
 use frame_support_procedural::{EqNoBound, PartialEqNoBound, RuntimeDebugNoBound};
 use sp_runtime::traits::Zero;
@@ -93,6 +94,11 @@ impl<
 		Self { asset, amount, _phantom: PhantomData }
 	}
 
+	/// Forget the imbalance without invoking the on-drop handler.
+	pub(crate) fn forget(imbalance: Self) {
+		sp_std::mem::forget(imbalance);
+	}
+
 	pub fn drop_zero(self) -> Result<(), Self> {
 		if self.amount.is_zero() {
 			sp_std::mem::forget(self);
@@ -168,6 +174,52 @@ impl<
 	}
 }
 
+/// Converts a `fungible` `imbalance` instance to an instance of a `fungibles` imbalance type using
+/// a specified `asset`.
+///
+/// This function facilitates imbalance conversions within the implementations of
+/// [`frame_support::traits::fungibles::UnionOf`], [`frame_support::traits::fungible::UnionOf`], and
+/// [`frame_support::traits::fungible::ItemOf`] adapters. It is intended only for internal use
+/// within the current crate.
+pub(crate) fn from_fungible<
+	A: AssetId,
+	B: Balance,
+	OnDropIn: fungible::HandleImbalanceDrop<B>,
+	OppositeIn: fungible::HandleImbalanceDrop<B>,
+	OnDropOut: HandleImbalanceDrop<A, B>,
+	OppositeOut: HandleImbalanceDrop<A, B>,
+>(
+	imbalance: fungible::Imbalance<B, OnDropIn, OppositeIn>,
+	asset: A,
+) -> Imbalance<A, B, OnDropOut, OppositeOut> {
+	let new = Imbalance::new(asset, imbalance.peek());
+	fungible::Imbalance::forget(imbalance);
+	new
+}
+
+/// Converts a `fungibles` `imbalance` instance of one type to another using a specified `asset`.
+///
+/// This function facilitates imbalance conversions within the implementations of
+/// [`frame_support::traits::fungibles::UnionOf`], [`frame_support::traits::fungible::UnionOf`], and
+/// [`frame_support::traits::fungible::ItemOf`] adapters. It is intended only for internal use
+/// within the current crate.
+pub(crate) fn from_fungibles<
+	A: AssetId,
+	B: Balance,
+	OnDropIn: HandleImbalanceDrop<A, B>,
+	OppositeIn: HandleImbalanceDrop<A, B>,
+	AssetOut: AssetId,
+	OnDropOut: HandleImbalanceDrop<AssetOut, B>,
+	OppositeOut: HandleImbalanceDrop<AssetOut, B>,
+>(
+	imbalance: Imbalance<A, B, OnDropIn, OppositeIn>,
+	asset: AssetOut,
+) -> Imbalance<AssetOut, B, OnDropOut, OppositeOut> {
+	let new = Imbalance::new(asset, imbalance.peek());
+	Imbalance::forget(imbalance);
+	new
+}
+
 /// Imbalance implying that the total_issuance value is less than the sum of all account balances.
 pub type Debt<AccountId, B> = Imbalance<
 	<B as Inspect<AccountId>>::AssetId,
diff --git a/substrate/frame/support/src/traits/tokens/fungibles/mod.rs b/substrate/frame/support/src/traits/tokens/fungibles/mod.rs
index 4fd6ef43a15..1db0706ba4f 100644
--- a/substrate/frame/support/src/traits/tokens/fungibles/mod.rs
+++ b/substrate/frame/support/src/traits/tokens/fungibles/mod.rs
@@ -21,11 +21,12 @@ pub mod approvals;
 mod enumerable;
 pub mod freeze;
 pub mod hold;
-mod imbalance;
+pub(crate) mod imbalance;
 mod lifetime;
 pub mod metadata;
 mod regular;
 pub mod roles;
+mod union_of;
 
 pub use enumerable::Inspect as InspectEnumerable;
 pub use freeze::{Inspect as InspectFreeze, Mutate as MutateFreeze};
@@ -38,3 +39,4 @@ pub use lifetime::{Create, Destroy};
 pub use regular::{
 	Balanced, DecreaseIssuance, Dust, IncreaseIssuance, Inspect, Mutate, Unbalanced,
 };
+pub use union_of::UnionOf;
diff --git a/substrate/frame/support/src/traits/tokens/fungibles/union_of.rs b/substrate/frame/support/src/traits/tokens/fungibles/union_of.rs
new file mode 100644
index 00000000000..3619db3a37b
--- /dev/null
+++ b/substrate/frame/support/src/traits/tokens/fungibles/union_of.rs
@@ -0,0 +1,897 @@
+// This file is part of Substrate.
+
+// Copyright (Criterion) Parity Technologies (UK) Ltd.
+// SPDX-License-Identifier: Apache-2.0
+
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// 	http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+//! Type to combine two `fungibles::*` implementations into one union `fungibles::*` implementation.
+
+use frame_support::traits::{
+	tokens::{
+		fungibles, fungibles::imbalance, AssetId, DepositConsequence, Fortitude, Precision,
+		Preservation, Provenance, Restriction, WithdrawConsequence,
+	},
+	AccountTouch,
+};
+use sp_runtime::{
+	traits::Convert,
+	DispatchError, DispatchResult, Either,
+	Either::{Left, Right},
+};
+
+/// Type to combine two `fungibles::*` implementations into one union `fungibles::*` implementation.
+///
+/// ### Parameters:
+/// - `Left` is `fungibles::*` implementation that is incorporated into the resulting union.
+/// - `Right` is `fungibles::*` implementation that is incorporated into the resulting union.
+/// - `Criterion` determines whether the `AssetKind` belongs to the `Left` or `Right` set.
+/// - `AssetKind` is a superset type encompassing asset kinds from `Left` and `Right` sets.
+/// - `AccountId` is an account identifier type.
+pub struct UnionOf<Left, Right, Criterion, AssetKind, AccountId>(
+	sp_std::marker::PhantomData<(Left, Right, Criterion, AssetKind, AccountId)>,
+);
+
+impl<
+		Left: fungibles::Inspect<AccountId>,
+		Right: fungibles::Inspect<AccountId, Balance = Left::Balance>,
+		Criterion: Convert<AssetKind, Either<Left::AssetId, Right::AssetId>>,
+		AssetKind: AssetId,
+		AccountId,
+	> fungibles::Inspect<AccountId> for UnionOf<Left, Right, Criterion, AssetKind, AccountId>
+{
+	type AssetId = AssetKind;
+	type Balance = Left::Balance;
+
+	fn total_issuance(asset: Self::AssetId) -> Self::Balance {
+		match Criterion::convert(asset) {
+			Left(a) => <Left as fungibles::Inspect<AccountId>>::total_issuance(a),
+			Right(a) => <Right as fungibles::Inspect<AccountId>>::total_issuance(a),
+		}
+	}
+	fn active_issuance(asset: Self::AssetId) -> Self::Balance {
+		match Criterion::convert(asset) {
+			Left(a) => <Left as fungibles::Inspect<AccountId>>::active_issuance(a),
+			Right(a) => <Right as fungibles::Inspect<AccountId>>::active_issuance(a),
+		}
+	}
+	fn minimum_balance(asset: Self::AssetId) -> Self::Balance {
+		match Criterion::convert(asset) {
+			Left(a) => <Left as fungibles::Inspect<AccountId>>::minimum_balance(a),
+			Right(a) => <Right as fungibles::Inspect<AccountId>>::minimum_balance(a),
+		}
+	}
+	fn balance(asset: Self::AssetId, who: &AccountId) -> Self::Balance {
+		match Criterion::convert(asset) {
+			Left(a) => <Left as fungibles::Inspect<AccountId>>::balance(a, who),
+			Right(a) => <Right as fungibles::Inspect<AccountId>>::balance(a, who),
+		}
+	}
+	fn total_balance(asset: Self::AssetId, who: &AccountId) -> Self::Balance {
+		match Criterion::convert(asset) {
+			Left(a) => <Left as fungibles::Inspect<AccountId>>::total_balance(a, who),
+			Right(a) => <Right as fungibles::Inspect<AccountId>>::total_balance(a, who),
+		}
+	}
+	fn reducible_balance(
+		asset: Self::AssetId,
+		who: &AccountId,
+		preservation: Preservation,
+		force: Fortitude,
+	) -> Self::Balance {
+		match Criterion::convert(asset) {
+			Left(a) => <Left as fungibles::Inspect<AccountId>>::reducible_balance(
+				a,
+				who,
+				preservation,
+				force,
+			),
+			Right(a) => <Right as fungibles::Inspect<AccountId>>::reducible_balance(
+				a,
+				who,
+				preservation,
+				force,
+			),
+		}
+	}
+	fn can_deposit(
+		asset: Self::AssetId,
+		who: &AccountId,
+		amount: Self::Balance,
+		provenance: Provenance,
+	) -> DepositConsequence {
+		match Criterion::convert(asset) {
+			Left(a) =>
+				<Left as fungibles::Inspect<AccountId>>::can_deposit(a, who, amount, provenance),
+			Right(a) =>
+				<Right as fungibles::Inspect<AccountId>>::can_deposit(a, who, amount, provenance),
+		}
+	}
+	fn can_withdraw(
+		asset: Self::AssetId,
+		who: &AccountId,
+		amount: Self::Balance,
+	) -> WithdrawConsequence<Self::Balance> {
+		match Criterion::convert(asset) {
+			Left(a) => <Left as fungibles::Inspect<AccountId>>::can_withdraw(a, who, amount),
+			Right(a) => <Right as fungibles::Inspect<AccountId>>::can_withdraw(a, who, amount),
+		}
+	}
+	fn asset_exists(asset: Self::AssetId) -> bool {
+		match Criterion::convert(asset) {
+			Left(a) => <Left as fungibles::Inspect<AccountId>>::asset_exists(a),
+			Right(a) => <Right as fungibles::Inspect<AccountId>>::asset_exists(a),
+		}
+	}
+}
+
+impl<
+		Left: fungibles::InspectHold<AccountId>,
+		Right: fungibles::InspectHold<AccountId, Balance = Left::Balance, Reason = Left::Reason>,
+		Criterion: Convert<AssetKind, Either<Left::AssetId, Right::AssetId>>,
+		AssetKind: AssetId,
+		AccountId,
+	> fungibles::InspectHold<AccountId> for UnionOf<Left, Right, Criterion, AssetKind, AccountId>
+{
+	type Reason = Left::Reason;
+
+	fn reducible_total_balance_on_hold(
+		asset: Self::AssetId,
+		who: &AccountId,
+		force: Fortitude,
+	) -> Self::Balance {
+		match Criterion::convert(asset) {
+			Left(a) =>
+				<Left as fungibles::InspectHold<AccountId>>::reducible_total_balance_on_hold(
+					a, who, force,
+				),
+			Right(a) =>
+				<Right as fungibles::InspectHold<AccountId>>::reducible_total_balance_on_hold(
+					a, who, force,
+				),
+		}
+	}
+	fn hold_available(asset: Self::AssetId, reason: &Self::Reason, who: &AccountId) -> bool {
+		match Criterion::convert(asset) {
+			Left(a) => <Left as fungibles::InspectHold<AccountId>>::hold_available(a, reason, who),
+			Right(a) =>
+				<Right as fungibles::InspectHold<AccountId>>::hold_available(a, reason, who),
+		}
+	}
+	fn total_balance_on_hold(asset: Self::AssetId, who: &AccountId) -> Self::Balance {
+		match Criterion::convert(asset) {
+			Left(a) => <Left as fungibles::InspectHold<AccountId>>::total_balance_on_hold(a, who),
+			Right(a) => <Right as fungibles::InspectHold<AccountId>>::total_balance_on_hold(a, who),
+		}
+	}
+	fn balance_on_hold(
+		asset: Self::AssetId,
+		reason: &Self::Reason,
+		who: &AccountId,
+	) -> Self::Balance {
+		match Criterion::convert(asset) {
+			Left(a) => <Left as fungibles::InspectHold<AccountId>>::balance_on_hold(a, reason, who),
+			Right(a) =>
+				<Right as fungibles::InspectHold<AccountId>>::balance_on_hold(a, reason, who),
+		}
+	}
+	fn can_hold(
+		asset: Self::AssetId,
+		reason: &Self::Reason,
+		who: &AccountId,
+		amount: Self::Balance,
+	) -> bool {
+		match Criterion::convert(asset) {
+			Left(a) =>
+				<Left as fungibles::InspectHold<AccountId>>::can_hold(a, reason, who, amount),
+			Right(a) =>
+				<Right as fungibles::InspectHold<AccountId>>::can_hold(a, reason, who, amount),
+		}
+	}
+}
+
+impl<
+		Left: fungibles::InspectFreeze<AccountId>,
+		Right: fungibles::InspectFreeze<AccountId, Balance = Left::Balance, Id = Left::Id>,
+		Criterion: Convert<AssetKind, Either<Left::AssetId, Right::AssetId>>,
+		AssetKind: AssetId,
+		AccountId,
+	> fungibles::InspectFreeze<AccountId> for UnionOf<Left, Right, Criterion, AssetKind, AccountId>
+{
+	type Id = Left::Id;
+	fn balance_frozen(asset: Self::AssetId, id: &Self::Id, who: &AccountId) -> Self::Balance {
+		match Criterion::convert(asset) {
+			Left(a) => <Left as fungibles::InspectFreeze<AccountId>>::balance_frozen(a, id, who),
+			Right(a) => <Right as fungibles::InspectFreeze<AccountId>>::balance_frozen(a, id, who),
+		}
+	}
+	fn balance_freezable(asset: Self::AssetId, who: &AccountId) -> Self::Balance {
+		match Criterion::convert(asset) {
+			Left(a) => <Left as fungibles::InspectFreeze<AccountId>>::balance_freezable(a, who),
+			Right(a) => <Right as fungibles::InspectFreeze<AccountId>>::balance_freezable(a, who),
+		}
+	}
+	fn can_freeze(asset: Self::AssetId, id: &Self::Id, who: &AccountId) -> bool {
+		match Criterion::convert(asset) {
+			Left(a) => <Left as fungibles::InspectFreeze<AccountId>>::can_freeze(a, id, who),
+			Right(a) => <Right as fungibles::InspectFreeze<AccountId>>::can_freeze(a, id, who),
+		}
+	}
+}
+
+impl<
+		Left: fungibles::Unbalanced<AccountId>,
+		Right: fungibles::Unbalanced<AccountId, Balance = Left::Balance>,
+		Criterion: Convert<AssetKind, Either<Left::AssetId, Right::AssetId>>,
+		AssetKind: AssetId,
+		AccountId,
+	> fungibles::Unbalanced<AccountId> for UnionOf<Left, Right, Criterion, AssetKind, AccountId>
+{
+	fn handle_dust(dust: fungibles::Dust<AccountId, Self>)
+	where
+		Self: Sized,
+	{
+		match Criterion::convert(dust.0) {
+			Left(a) =>
+				<Left as fungibles::Unbalanced<AccountId>>::handle_dust(fungibles::Dust(a, dust.1)),
+			Right(a) =>
+				<Right as fungibles::Unbalanced<AccountId>>::handle_dust(fungibles::Dust(a, dust.1)),
+		}
+	}
+	fn write_balance(
+		asset: Self::AssetId,
+		who: &AccountId,
+		amount: Self::Balance,
+	) -> Result<Option<Self::Balance>, DispatchError> {
+		match Criterion::convert(asset) {
+			Left(a) => <Left as fungibles::Unbalanced<AccountId>>::write_balance(a, who, amount),
+			Right(a) => <Right as fungibles::Unbalanced<AccountId>>::write_balance(a, who, amount),
+		}
+	}
+	fn set_total_issuance(asset: Self::AssetId, amount: Self::Balance) -> () {
+		match Criterion::convert(asset) {
+			Left(a) => <Left as fungibles::Unbalanced<AccountId>>::set_total_issuance(a, amount),
+			Right(a) => <Right as fungibles::Unbalanced<AccountId>>::set_total_issuance(a, amount),
+		}
+	}
+	fn decrease_balance(
+		asset: Self::AssetId,
+		who: &AccountId,
+		amount: Self::Balance,
+		precision: Precision,
+		preservation: Preservation,
+		force: Fortitude,
+	) -> Result<Self::Balance, DispatchError> {
+		match Criterion::convert(asset) {
+			Left(a) => <Left as fungibles::Unbalanced<AccountId>>::decrease_balance(
+				a,
+				who,
+				amount,
+				precision,
+				preservation,
+				force,
+			),
+			Right(a) => <Right as fungibles::Unbalanced<AccountId>>::decrease_balance(
+				a,
+				who,
+				amount,
+				precision,
+				preservation,
+				force,
+			),
+		}
+	}
+	fn increase_balance(
+		asset: Self::AssetId,
+		who: &AccountId,
+		amount: Self::Balance,
+		precision: Precision,
+	) -> Result<Self::Balance, DispatchError> {
+		match Criterion::convert(asset) {
+			Left(a) => <Left as fungibles::Unbalanced<AccountId>>::increase_balance(
+				a, who, amount, precision,
+			),
+			Right(a) => <Right as fungibles::Unbalanced<AccountId>>::increase_balance(
+				a, who, amount, precision,
+			),
+		}
+	}
+}
+
+impl<
+		Left: fungibles::UnbalancedHold<AccountId>,
+		Right: fungibles::UnbalancedHold<AccountId, Balance = Left::Balance, Reason = Left::Reason>,
+		Criterion: Convert<AssetKind, Either<Left::AssetId, Right::AssetId>>,
+		AssetKind: AssetId,
+		AccountId,
+	> fungibles::UnbalancedHold<AccountId> for UnionOf<Left, Right, Criterion, AssetKind, AccountId>
+{
+	fn set_balance_on_hold(
+		asset: Self::AssetId,
+		reason: &Self::Reason,
+		who: &AccountId,
+		amount: Self::Balance,
+	) -> DispatchResult {
+		match Criterion::convert(asset) {
+			Left(a) => <Left as fungibles::UnbalancedHold<AccountId>>::set_balance_on_hold(
+				a, reason, who, amount,
+			),
+			Right(a) => <Right as fungibles::UnbalancedHold<AccountId>>::set_balance_on_hold(
+				a, reason, who, amount,
+			),
+		}
+	}
+	fn decrease_balance_on_hold(
+		asset: Self::AssetId,
+		reason: &Self::Reason,
+		who: &AccountId,
+		amount: Self::Balance,
+		precision: Precision,
+	) -> Result<Self::Balance, DispatchError> {
+		match Criterion::convert(asset) {
+			Left(a) => <Left as fungibles::UnbalancedHold<AccountId>>::decrease_balance_on_hold(
+				a, reason, who, amount, precision,
+			),
+			Right(a) => <Right as fungibles::UnbalancedHold<AccountId>>::decrease_balance_on_hold(
+				a, reason, who, amount, precision,
+			),
+		}
+	}
+	fn increase_balance_on_hold(
+		asset: Self::AssetId,
+		reason: &Self::Reason,
+		who: &AccountId,
+		amount: Self::Balance,
+		precision: Precision,
+	) -> Result<Self::Balance, DispatchError> {
+		match Criterion::convert(asset) {
+			Left(a) => <Left as fungibles::UnbalancedHold<AccountId>>::increase_balance_on_hold(
+				a, reason, who, amount, precision,
+			),
+			Right(a) => <Right as fungibles::UnbalancedHold<AccountId>>::increase_balance_on_hold(
+				a, reason, who, amount, precision,
+			),
+		}
+	}
+}
+
+impl<
+		Left: fungibles::Mutate<AccountId>,
+		Right: fungibles::Mutate<AccountId, Balance = Left::Balance>,
+		Criterion: Convert<AssetKind, Either<Left::AssetId, Right::AssetId>>,
+		AssetKind: AssetId,
+		AccountId: Eq,
+	> fungibles::Mutate<AccountId> for UnionOf<Left, Right, Criterion, AssetKind, AccountId>
+{
+	fn mint_into(
+		asset: Self::AssetId,
+		who: &AccountId,
+		amount: Self::Balance,
+	) -> Result<Self::Balance, DispatchError> {
+		match Criterion::convert(asset) {
+			Left(a) => <Left as fungibles::Mutate<AccountId>>::mint_into(a, who, amount),
+			Right(a) => <Right as fungibles::Mutate<AccountId>>::mint_into(a, who, amount),
+		}
+	}
+	fn burn_from(
+		asset: Self::AssetId,
+		who: &AccountId,
+		amount: Self::Balance,
+		precision: Precision,
+		force: Fortitude,
+	) -> Result<Self::Balance, DispatchError> {
+		match Criterion::convert(asset) {
+			Left(a) =>
+				<Left as fungibles::Mutate<AccountId>>::burn_from(a, who, amount, precision, force),
+			Right(a) =>
+				<Right as fungibles::Mutate<AccountId>>::burn_from(a, who, amount, precision, force),
+		}
+	}
+	fn shelve(
+		asset: Self::AssetId,
+		who: &AccountId,
+		amount: Self::Balance,
+	) -> Result<Self::Balance, DispatchError> {
+		match Criterion::convert(asset) {
+			Left(a) => <Left as fungibles::Mutate<AccountId>>::shelve(a, who, amount),
+			Right(a) => <Right as fungibles::Mutate<AccountId>>::shelve(a, who, amount),
+		}
+	}
+	fn restore(
+		asset: Self::AssetId,
+		who: &AccountId,
+		amount: Self::Balance,
+	) -> Result<Self::Balance, DispatchError> {
+		match Criterion::convert(asset) {
+			Left(a) => <Left as fungibles::Mutate<AccountId>>::restore(a, who, amount),
+			Right(a) => <Right as fungibles::Mutate<AccountId>>::restore(a, who, amount),
+		}
+	}
+	fn transfer(
+		asset: Self::AssetId,
+		source: &AccountId,
+		dest: &AccountId,
+		amount: Self::Balance,
+		preservation: Preservation,
+	) -> Result<Self::Balance, DispatchError> {
+		match Criterion::convert(asset) {
+			Left(a) => <Left as fungibles::Mutate<AccountId>>::transfer(
+				a,
+				source,
+				dest,
+				amount,
+				preservation,
+			),
+			Right(a) => <Right as fungibles::Mutate<AccountId>>::transfer(
+				a,
+				source,
+				dest,
+				amount,
+				preservation,
+			),
+		}
+	}
+
+	fn set_balance(asset: Self::AssetId, who: &AccountId, amount: Self::Balance) -> Self::Balance {
+		match Criterion::convert(asset) {
+			Left(a) => <Left as fungibles::Mutate<AccountId>>::set_balance(a, who, amount),
+			Right(a) => <Right as fungibles::Mutate<AccountId>>::set_balance(a, who, amount),
+		}
+	}
+}
+
+impl<
+		Left: fungibles::MutateHold<AccountId>,
+		Right: fungibles::MutateHold<AccountId, Balance = Left::Balance, Reason = Left::Reason>,
+		Criterion: Convert<AssetKind, Either<Left::AssetId, Right::AssetId>>,
+		AssetKind: AssetId,
+		AccountId,
+	> fungibles::MutateHold<AccountId> for UnionOf<Left, Right, Criterion, AssetKind, AccountId>
+{
+	fn hold(
+		asset: Self::AssetId,
+		reason: &Self::Reason,
+		who: &AccountId,
+		amount: Self::Balance,
+	) -> DispatchResult {
+		match Criterion::convert(asset) {
+			Left(a) => <Left as fungibles::MutateHold<AccountId>>::hold(a, reason, who, amount),
+			Right(a) => <Right as fungibles::MutateHold<AccountId>>::hold(a, reason, who, amount),
+		}
+	}
+	fn release(
+		asset: Self::AssetId,
+		reason: &Self::Reason,
+		who: &AccountId,
+		amount: Self::Balance,
+		precision: Precision,
+	) -> Result<Self::Balance, DispatchError> {
+		match Criterion::convert(asset) {
+			Left(a) => <Left as fungibles::MutateHold<AccountId>>::release(
+				a, reason, who, amount, precision,
+			),
+			Right(a) => <Right as fungibles::MutateHold<AccountId>>::release(
+				a, reason, who, amount, precision,
+			),
+		}
+	}
+	fn burn_held(
+		asset: Self::AssetId,
+		reason: &Self::Reason,
+		who: &AccountId,
+		amount: Self::Balance,
+		precision: Precision,
+		force: Fortitude,
+	) -> Result<Self::Balance, DispatchError> {
+		match Criterion::convert(asset) {
+			Left(a) => <Left as fungibles::MutateHold<AccountId>>::burn_held(
+				a, reason, who, amount, precision, force,
+			),
+			Right(a) => <Right as fungibles::MutateHold<AccountId>>::burn_held(
+				a, reason, who, amount, precision, force,
+			),
+		}
+	}
+	fn transfer_on_hold(
+		asset: Self::AssetId,
+		reason: &Self::Reason,
+		source: &AccountId,
+		dest: &AccountId,
+		amount: Self::Balance,
+		precision: Precision,
+		mode: Restriction,
+		force: Fortitude,
+	) -> Result<Self::Balance, DispatchError> {
+		match Criterion::convert(asset) {
+			Left(a) => <Left as fungibles::MutateHold<AccountId>>::transfer_on_hold(
+				a, reason, source, dest, amount, precision, mode, force,
+			),
+			Right(a) => <Right as fungibles::MutateHold<AccountId>>::transfer_on_hold(
+				a, reason, source, dest, amount, precision, mode, force,
+			),
+		}
+	}
+	fn transfer_and_hold(
+		asset: Self::AssetId,
+		reason: &Self::Reason,
+		source: &AccountId,
+		dest: &AccountId,
+		amount: Self::Balance,
+		precision: Precision,
+		preservation: Preservation,
+		force: Fortitude,
+	) -> Result<Self::Balance, DispatchError> {
+		match Criterion::convert(asset) {
+			Left(a) => <Left as fungibles::MutateHold<AccountId>>::transfer_and_hold(
+				a,
+				reason,
+				source,
+				dest,
+				amount,
+				precision,
+				preservation,
+				force,
+			),
+			Right(a) => <Right as fungibles::MutateHold<AccountId>>::transfer_and_hold(
+				a,
+				reason,
+				source,
+				dest,
+				amount,
+				precision,
+				preservation,
+				force,
+			),
+		}
+	}
+}
+
+impl<
+		Left: fungibles::MutateFreeze<AccountId>,
+		Right: fungibles::MutateFreeze<AccountId, Balance = Left::Balance, Id = Left::Id>,
+		Criterion: Convert<AssetKind, Either<Left::AssetId, Right::AssetId>>,
+		AssetKind: AssetId,
+		AccountId,
+	> fungibles::MutateFreeze<AccountId> for UnionOf<Left, Right, Criterion, AssetKind, AccountId>
+{
+	fn set_freeze(
+		asset: Self::AssetId,
+		id: &Self::Id,
+		who: &AccountId,
+		amount: Self::Balance,
+	) -> DispatchResult {
+		match Criterion::convert(asset) {
+			Left(a) => <Left as fungibles::MutateFreeze<AccountId>>::set_freeze(a, id, who, amount),
+			Right(a) =>
+				<Right as fungibles::MutateFreeze<AccountId>>::set_freeze(a, id, who, amount),
+		}
+	}
+	fn extend_freeze(
+		asset: Self::AssetId,
+		id: &Self::Id,
+		who: &AccountId,
+		amount: Self::Balance,
+	) -> DispatchResult {
+		match Criterion::convert(asset) {
+			Left(a) =>
+				<Left as fungibles::MutateFreeze<AccountId>>::extend_freeze(a, id, who, amount),
+			Right(a) =>
+				<Right as fungibles::MutateFreeze<AccountId>>::extend_freeze(a, id, who, amount),
+		}
+	}
+	fn thaw(asset: Self::AssetId, id: &Self::Id, who: &AccountId) -> DispatchResult {
+		match Criterion::convert(asset) {
+			Left(a) => <Left as fungibles::MutateFreeze<AccountId>>::thaw(a, id, who),
+			Right(a) => <Right as fungibles::MutateFreeze<AccountId>>::thaw(a, id, who),
+		}
+	}
+}
+
+pub struct ConvertImbalanceDropHandler<
+	Left,
+	Right,
+	LeftAssetId,
+	RightAssetId,
+	Criterion,
+	AssetKind,
+	Balance,
+	AccountId,
+>(
+	sp_std::marker::PhantomData<(
+		Left,
+		Right,
+		LeftAssetId,
+		RightAssetId,
+		Criterion,
+		AssetKind,
+		Balance,
+		AccountId,
+	)>,
+);
+
+impl<
+		Left: fungibles::HandleImbalanceDrop<LeftAssetId, Balance>,
+		Right: fungibles::HandleImbalanceDrop<RightAssetId, Balance>,
+		LeftAssetId,
+		RightAssetId,
+		Criterion: Convert<AssetKind, Either<LeftAssetId, RightAssetId>>,
+		AssetKind,
+		Balance,
+		AccountId,
+	> fungibles::HandleImbalanceDrop<AssetKind, Balance>
+	for ConvertImbalanceDropHandler<
+		Left,
+		Right,
+		LeftAssetId,
+		RightAssetId,
+		Criterion,
+		AssetKind,
+		Balance,
+		AccountId,
+	>
+{
+	fn handle(asset: AssetKind, amount: Balance) {
+		match Criterion::convert(asset) {
+			Left(a) => Left::handle(a, amount),
+			Right(a) => Right::handle(a, amount),
+		}
+	}
+}
+
+impl<
+		Left: fungibles::Balanced<AccountId>,
+		Right: fungibles::Balanced<AccountId, Balance = Left::Balance>,
+		Criterion: Convert<AssetKind, Either<Left::AssetId, Right::AssetId>>,
+		AssetKind: AssetId,
+		AccountId,
+	> fungibles::Balanced<AccountId> for UnionOf<Left, Right, Criterion, AssetKind, AccountId>
+{
+	type OnDropDebt = ConvertImbalanceDropHandler<
+		Left::OnDropDebt,
+		Right::OnDropDebt,
+		Left::AssetId,
+		Right::AssetId,
+		Criterion,
+		AssetKind,
+		Left::Balance,
+		AccountId,
+	>;
+	type OnDropCredit = ConvertImbalanceDropHandler<
+		Left::OnDropCredit,
+		Right::OnDropCredit,
+		Left::AssetId,
+		Right::AssetId,
+		Criterion,
+		AssetKind,
+		Left::Balance,
+		AccountId,
+	>;
+
+	fn deposit(
+		asset: Self::AssetId,
+		who: &AccountId,
+		value: Self::Balance,
+		precision: Precision,
+	) -> Result<fungibles::Debt<AccountId, Self>, DispatchError> {
+		match Criterion::convert(asset.clone()) {
+			Left(a) => <Left as fungibles::Balanced<AccountId>>::deposit(a, who, value, precision)
+				.map(|debt| imbalance::from_fungibles(debt, asset)),
+			Right(a) =>
+				<Right as fungibles::Balanced<AccountId>>::deposit(a, who, value, precision)
+					.map(|debt| imbalance::from_fungibles(debt, asset)),
+		}
+	}
+	fn issue(asset: Self::AssetId, amount: Self::Balance) -> fungibles::Credit<AccountId, Self> {
+		match Criterion::convert(asset.clone()) {
+			Left(a) => {
+				let credit = <Left as fungibles::Balanced<AccountId>>::issue(a, amount);
+				imbalance::from_fungibles(credit, asset)
+			},
+			Right(a) => {
+				let credit = <Right as fungibles::Balanced<AccountId>>::issue(a, amount);
+				imbalance::from_fungibles(credit, asset)
+			},
+		}
+	}
+	fn pair(
+		asset: Self::AssetId,
+		amount: Self::Balance,
+	) -> (fungibles::Debt<AccountId, Self>, fungibles::Credit<AccountId, Self>) {
+		match Criterion::convert(asset.clone()) {
+			Left(a) => {
+				let (a, b) = <Left as fungibles::Balanced<AccountId>>::pair(a, amount);
+				(imbalance::from_fungibles(a, asset.clone()), imbalance::from_fungibles(b, asset))
+			},
+			Right(a) => {
+				let (a, b) = <Right as fungibles::Balanced<AccountId>>::pair(a, amount);
+				(imbalance::from_fungibles(a, asset.clone()), imbalance::from_fungibles(b, asset))
+			},
+		}
+	}
+	fn rescind(asset: Self::AssetId, amount: Self::Balance) -> fungibles::Debt<AccountId, Self> {
+		match Criterion::convert(asset.clone()) {
+			Left(a) => {
+				let debt = <Left as fungibles::Balanced<AccountId>>::rescind(a, amount);
+				imbalance::from_fungibles(debt, asset)
+			},
+			Right(a) => {
+				let debt = <Right as fungibles::Balanced<AccountId>>::rescind(a, amount);
+				imbalance::from_fungibles(debt, asset)
+			},
+		}
+	}
+	fn resolve(
+		who: &AccountId,
+		credit: fungibles::Credit<AccountId, Self>,
+	) -> Result<(), fungibles::Credit<AccountId, Self>> {
+		let asset = credit.asset();
+		match Criterion::convert(asset.clone()) {
+			Left(a) => {
+				let credit = imbalance::from_fungibles(credit, a);
+				<Left as fungibles::Balanced<AccountId>>::resolve(who, credit)
+					.map_err(|credit| imbalance::from_fungibles(credit, asset))
+			},
+			Right(a) => {
+				let credit = imbalance::from_fungibles(credit, a);
+				<Right as fungibles::Balanced<AccountId>>::resolve(who, credit)
+					.map_err(|credit| imbalance::from_fungibles(credit, asset))
+			},
+		}
+	}
+	fn settle(
+		who: &AccountId,
+		debt: fungibles::Debt<AccountId, Self>,
+		preservation: Preservation,
+	) -> Result<fungibles::Credit<AccountId, Self>, fungibles::Debt<AccountId, Self>> {
+		let asset = debt.asset();
+		match Criterion::convert(asset.clone()) {
+			Left(a) => {
+				let debt = imbalance::from_fungibles(debt, a);
+				match <Left as fungibles::Balanced<AccountId>>::settle(who, debt, preservation) {
+					Ok(credit) => Ok(imbalance::from_fungibles(credit, asset)),
+					Err(debt) => Err(imbalance::from_fungibles(debt, asset)),
+				}
+			},
+			Right(a) => {
+				let debt = imbalance::from_fungibles(debt, a);
+				match <Right as fungibles::Balanced<AccountId>>::settle(who, debt, preservation) {
+					Ok(credit) => Ok(imbalance::from_fungibles(credit, asset)),
+					Err(debt) => Err(imbalance::from_fungibles(debt, asset)),
+				}
+			},
+		}
+	}
+	fn withdraw(
+		asset: Self::AssetId,
+		who: &AccountId,
+		value: Self::Balance,
+		precision: Precision,
+		preservation: Preservation,
+		force: Fortitude,
+	) -> Result<fungibles::Credit<AccountId, Self>, DispatchError> {
+		match Criterion::convert(asset.clone()) {
+			Left(a) => <Left as fungibles::Balanced<AccountId>>::withdraw(
+				a,
+				who,
+				value,
+				precision,
+				preservation,
+				force,
+			)
+			.map(|credit| imbalance::from_fungibles(credit, asset)),
+			Right(a) => <Right as fungibles::Balanced<AccountId>>::withdraw(
+				a,
+				who,
+				value,
+				precision,
+				preservation,
+				force,
+			)
+			.map(|credit| imbalance::from_fungibles(credit, asset)),
+		}
+	}
+}
+
+impl<
+		Left: fungibles::BalancedHold<AccountId>,
+		Right: fungibles::BalancedHold<AccountId, Balance = Left::Balance, Reason = Left::Reason>,
+		Criterion: Convert<AssetKind, Either<Left::AssetId, Right::AssetId>>,
+		AssetKind: AssetId,
+		AccountId,
+	> fungibles::BalancedHold<AccountId> for UnionOf<Left, Right, Criterion, AssetKind, AccountId>
+{
+	fn slash(
+		asset: Self::AssetId,
+		reason: &Self::Reason,
+		who: &AccountId,
+		amount: Self::Balance,
+	) -> (fungibles::Credit<AccountId, Self>, Self::Balance) {
+		match Criterion::convert(asset.clone()) {
+			Left(a) => {
+				let (credit, amount) =
+					<Left as fungibles::BalancedHold<AccountId>>::slash(a, reason, who, amount);
+				(imbalance::from_fungibles(credit, asset), amount)
+			},
+			Right(a) => {
+				let (credit, amount) =
+					<Right as fungibles::BalancedHold<AccountId>>::slash(a, reason, who, amount);
+				(imbalance::from_fungibles(credit, asset), amount)
+			},
+		}
+	}
+}
+
+impl<
+		Left: fungibles::Inspect<AccountId> + fungibles::Create<AccountId>,
+		Right: fungibles::Inspect<AccountId, Balance = Left::Balance> + fungibles::Create<AccountId>,
+		Criterion: Convert<AssetKind, Either<Left::AssetId, Right::AssetId>>,
+		AssetKind: AssetId,
+		AccountId,
+	> fungibles::Create<AccountId> for UnionOf<Left, Right, Criterion, AssetKind, AccountId>
+{
+	fn create(
+		asset: AssetKind,
+		admin: AccountId,
+		is_sufficient: bool,
+		min_balance: Self::Balance,
+	) -> DispatchResult {
+		match Criterion::convert(asset) {
+			Left(a) =>
+				<Left as fungibles::Create<AccountId>>::create(a, admin, is_sufficient, min_balance),
+			Right(a) => <Right as fungibles::Create<AccountId>>::create(
+				a,
+				admin,
+				is_sufficient,
+				min_balance,
+			),
+		}
+	}
+}
+
+impl<
+		Left: fungibles::Inspect<AccountId> + AccountTouch<Left::AssetId, AccountId>,
+		Right: fungibles::Inspect<AccountId>
+			+ AccountTouch<
+				Right::AssetId,
+				AccountId,
+				Balance = <Left as AccountTouch<Left::AssetId, AccountId>>::Balance,
+			>,
+		Criterion: Convert<AssetKind, Either<Left::AssetId, Right::AssetId>>,
+		AssetKind: AssetId,
+		AccountId,
+	> AccountTouch<AssetKind, AccountId> for UnionOf<Left, Right, Criterion, AssetKind, AccountId>
+{
+	type Balance = <Left as AccountTouch<Left::AssetId, AccountId>>::Balance;
+
+	fn deposit_required(asset: AssetKind) -> Self::Balance {
+		match Criterion::convert(asset) {
+			Left(a) => <Left as AccountTouch<Left::AssetId, AccountId>>::deposit_required(a),
+			Right(a) => <Right as AccountTouch<Right::AssetId, AccountId>>::deposit_required(a),
+		}
+	}
+
+	fn should_touch(asset: AssetKind, who: &AccountId) -> bool {
+		match Criterion::convert(asset) {
+			Left(a) => <Left as AccountTouch<Left::AssetId, AccountId>>::should_touch(a, who),
+			Right(a) => <Right as AccountTouch<Right::AssetId, AccountId>>::should_touch(a, who),
+		}
+	}
+
+	fn touch(asset: AssetKind, who: &AccountId, depositor: &AccountId) -> DispatchResult {
+		match Criterion::convert(asset) {
+			Left(a) => <Left as AccountTouch<Left::AssetId, AccountId>>::touch(a, who, depositor),
+			Right(a) =>
+				<Right as AccountTouch<Right::AssetId, AccountId>>::touch(a, who, depositor),
+		}
+	}
+}
-- 
GitLab