diff --git a/tokens/src/lib.rs b/tokens/src/lib.rs
index 4b1f3bfc7ea4d54e0abb907053940a3513334d43..f80d14ad4ad695f63023a7daa7f70802e697a32a 100644
--- a/tokens/src/lib.rs
+++ b/tokens/src/lib.rs
@@ -43,6 +43,7 @@ use frame_support::{
 	ensure, log,
 	pallet_prelude::*,
 	traits::{
+		tokens::{fungible, fungibles, DepositConsequence, WithdrawConsequence},
 		BalanceStatus as Status, Currency as PalletCurrency, ExistenceRequirement, Get, Imbalance,
 		LockableCurrency as PalletLockableCurrency, MaxEncodedLen, ReservableCurrency as PalletReservableCurrency,
 		SignedImbalance, WithdrawReasons,
@@ -177,7 +178,8 @@ pub mod module {
 			+ MaybeSerializeDeserialize;
 
 		/// The currency ID type
-		type CurrencyId: Parameter + Member + Copy + MaybeSerializeDeserialize + Ord;
+		// TODO: remove `Default` after https://github.com/paritytech/substrate/pull/9062
+		type CurrencyId: Parameter + Member + Copy + MaybeSerializeDeserialize + Ord + Default;
 
 		/// Weight information for extrinsics in this module.
 		type WeightInfo: WeightInfo;
@@ -201,6 +203,8 @@ pub mod module {
 		LiquidityRestrictions,
 		/// Failed because the maximum locks was exceeded
 		MaxLocksExceeded,
+		/// Transfer/payment would kill account
+		KeepAlive,
 	}
 
 	#[pallet::event]
@@ -359,6 +363,83 @@ impl<T: Config> Pallet<T> {
 		PalletId::try_from_account(account_id).is_some()
 	}
 
+	pub(crate) fn deposit_consequence(
+		_who: &T::AccountId,
+		currency_id: T::CurrencyId,
+		amount: T::Balance,
+		account: &AccountData<T::Balance>,
+	) -> DepositConsequence {
+		if amount.is_zero() {
+			return DepositConsequence::Success;
+		}
+
+		if TotalIssuance::<T>::get(currency_id).checked_add(&amount).is_none() {
+			return DepositConsequence::Overflow;
+		}
+
+		let new_total_balance = match account.total().checked_add(&amount) {
+			Some(x) => x,
+			None => return DepositConsequence::Overflow,
+		};
+
+		if new_total_balance < T::ExistentialDeposits::get(&currency_id) {
+			return DepositConsequence::BelowMinimum;
+		}
+
+		// NOTE: We assume that we are a provider, so don't need to do any checks in the
+		// case of account creation.
+
+		DepositConsequence::Success
+	}
+
+	pub(crate) fn withdraw_consequence(
+		who: &T::AccountId,
+		currency_id: T::CurrencyId,
+		amount: T::Balance,
+		account: &AccountData<T::Balance>,
+	) -> WithdrawConsequence<T::Balance> {
+		if amount.is_zero() {
+			return WithdrawConsequence::Success;
+		}
+
+		if TotalIssuance::<T>::get(currency_id).checked_sub(&amount).is_none() {
+			return WithdrawConsequence::Underflow;
+		}
+
+		let new_total_balance = match account.total().checked_sub(&amount) {
+			Some(x) => x,
+			None => return WithdrawConsequence::NoFunds,
+		};
+
+		// Provider restriction - total account balance cannot be reduced to zero if it
+		// cannot sustain the loss of a provider reference.
+		// NOTE: This assumes that the pallet is a provider (which is true). Is this
+		// ever changes, then this will need to adapt accordingly.
+		let ed = T::ExistentialDeposits::get(&currency_id);
+		let success = if new_total_balance < ed {
+			if frame_system::Pallet::<T>::can_dec_provider(who) {
+				WithdrawConsequence::ReducedToZero(new_total_balance)
+			} else {
+				return WithdrawConsequence::WouldDie;
+			}
+		} else {
+			WithdrawConsequence::Success
+		};
+
+		// Enough free funds to have them be reduced.
+		let new_free_balance = match account.free.checked_sub(&amount) {
+			Some(b) => b,
+			None => return WithdrawConsequence::NoFunds,
+		};
+
+		// Eventual free funds must be no less than the frozen balance.
+		if new_free_balance < account.frozen() {
+			return WithdrawConsequence::Frozen;
+		}
+
+		success
+	}
+
 	pub(crate) fn try_mutate_account<R, E>(
 		who: &T::AccountId,
 		currency_id: T::CurrencyId,
@@ -485,6 +566,41 @@ impl<T: Config> Pallet<T> {
 
 		Ok(())
 	}
+
+	/// Transfer some free balance from `from` to `to`.
+	/// Is a no-op if value to be transferred is zero or the `from` is the
+	/// same as `to`.
+	pub(crate) fn do_transfer(
+		currency_id: T::CurrencyId,
+		from: &T::AccountId,
+		to: &T::AccountId,
+		amount: T::Balance,
+		existence_requirement: ExistenceRequirement,
+	) -> DispatchResult {
+		if amount.is_zero() || from == to {
+			return Ok(());
+		}
+
+		Pallet::<T>::try_mutate_account(to, currency_id, |to_account, _is_new| -> DispatchResult {
+			Pallet::<T>::try_mutate_account(from, currency_id, |from_account, _is_new| -> DispatchResult {
+				from_account.free = from_account
+					.free
+					.checked_sub(&amount)
+					.ok_or(Error::<T>::BalanceTooLow)?;
+				to_account.free = to_account.free.checked_add(&amount).ok_or(ArithmeticError::Overflow)?;
+
+				Self::ensure_can_withdraw(currency_id, from, amount)?;
+
+				let ed = T::ExistentialDeposits::get(&currency_id);
+				let allow_death = existence_requirement == ExistenceRequirement::AllowDeath;
+				let allow_death = allow_death && !frame_system::Pallet::<T>::is_provider_required(from);
+				ensure!(allow_death || from_account.total() >= ed, Error::<T>::KeepAlive);
+
+				Ok(())
+			})?;
+			Ok(())
+		})
+	}
 }
 
 impl<T: Config> MultiCurrency<T::AccountId> for Pallet<T> {
@@ -534,20 +650,7 @@ impl<T: Config> MultiCurrency<T::AccountId> for Pallet<T> {
 		to: &T::AccountId,
 		amount: Self::Balance,
 	) -> DispatchResult {
-		if amount.is_zero() || from == to {
-			return Ok(());
-		}
-		Self::ensure_can_withdraw(currency_id, from, amount)?;
-
-		let from_balance = Self::free_balance(currency_id, from);
-		let to_balance = Self::free_balance(currency_id, to)
-			.checked_add(&amount)
-			.ok_or(ArithmeticError::Overflow)?;
-		// Cannot underflow because ensure_can_withdraw check
-		Self::set_free_balance(currency_id, from, from_balance - amount);
-		Self::set_free_balance(currency_id, to, to_balance);
-
-		Ok(())
+		Self::do_transfer(currency_id, from, to, amount, ExistenceRequirement::AllowDeath)
 	}
 
 	/// Deposit some `amount` into the free balance of account `who`.
@@ -833,6 +936,183 @@ impl<T: Config> MultiReservableCurrency<T::AccountId> for Pallet<T> {
 	}
 }
 
+impl<T: Config> fungibles::Inspect<T::AccountId> for Pallet<T> {
+	type AssetId = T::CurrencyId;
+	type Balance = T::Balance;
+
+	fn total_issuance(asset_id: Self::AssetId) -> Self::Balance {
+		Pallet::<T>::total_issuance(asset_id)
+	}
+	fn minimum_balance(asset_id: Self::AssetId) -> Self::Balance {
+		<Self as MultiCurrency<_>>::minimum_balance(asset_id)
+	}
+	fn balance(asset_id: Self::AssetId, who: &T::AccountId) -> Self::Balance {
+		Pallet::<T>::total_balance(asset_id, who)
+	}
+	fn reducible_balance(asset_id: Self::AssetId, who: &T::AccountId, keep_alive: bool) -> Self::Balance {
+		let a = Pallet::<T>::accounts(who, asset_id);
+		// Liquid balance is what is neither reserved nor locked/frozen.
+		let liquid = a.free.saturating_sub(a.frozen);
+		if frame_system::Pallet::<T>::can_dec_provider(who) && !keep_alive {
+			liquid
+		} else {
+			// `must_remain_to_exist` is the part of liquid balance which must remain to
+			// keep total over ED.
+			let must_remain_to_exist = T::ExistentialDeposits::get(&asset_id).saturating_sub(a.total() - liquid);
+			liquid.saturating_sub(must_remain_to_exist)
+		}
+	}
+	fn can_deposit(asset_id: Self::AssetId, who: &T::AccountId, amount: Self::Balance) -> DepositConsequence {
+		Pallet::<T>::deposit_consequence(who, asset_id, amount, &Pallet::<T>::accounts(who, asset_id))
+	}
+	fn can_withdraw(
+		asset_id: Self::AssetId,
+		who: &T::AccountId,
+		amount: Self::Balance,
+	) -> WithdrawConsequence<Self::Balance> {
+		Pallet::<T>::withdraw_consequence(who, asset_id, amount, &Pallet::<T>::accounts(who, asset_id))
+	}
+}
+
+impl<T: Config> fungibles::Mutate<T::AccountId> for Pallet<T> {
+	fn mint_into(asset_id: Self::AssetId, who: &T::AccountId, amount: Self::Balance) -> DispatchResult {
+		if amount.is_zero() {
+			return Ok(());
+		}
+		Pallet::<T>::try_mutate_account(who, asset_id, |account, _is_new| -> DispatchResult {
+			Pallet::<T>::deposit_consequence(who, asset_id, amount, &account).into_result()?;
+			// deposit_consequence already did overflow checking
+			account.free += amount;
+			Ok(())
+		})?;
+		// deposit_consequence already did overflow checking
+		<TotalIssuance<T>>::mutate(asset_id, |t| *t += amount);
+		Ok(())
+	}
+
+	fn burn_from(
+		asset_id: Self::AssetId,
+		who: &T::AccountId,
+		amount: Self::Balance,
+	) -> Result<Self::Balance, DispatchError> {
+		if amount.is_zero() {
+			return Ok(Self::Balance::zero());
+		}
+		let actual =
+			Pallet::<T>::try_mutate_account(who, asset_id, |account, _is_new| -> Result<T::Balance, DispatchError> {
+				let extra = Pallet::<T>::withdraw_consequence(who, asset_id, amount, &account).into_result()?;
+				// withdraw_consequence already did underflow checking
+				let actual = amount + extra;
+				account.free -= actual;
+				Ok(actual)
+			})?;
+		// withdraw_consequence already did underflow checking
+		<TotalIssuance<T>>::mutate(asset_id, |t| *t -= actual);
+		Ok(actual)
+	}
+}
+
+impl<T: Config> fungibles::Transfer<T::AccountId> for Pallet<T> {
+	fn transfer(
+		asset_id: Self::AssetId,
+		source: &T::AccountId,
+		dest: &T::AccountId,
+		amount: T::Balance,
+		keep_alive: bool,
+	) -> Result<T::Balance, DispatchError> {
+		let er = if keep_alive {
+			ExistenceRequirement::KeepAlive
+		} else {
+			ExistenceRequirement::AllowDeath
+		};
+		Self::do_transfer(asset_id, source, dest, amount, er).map(|_| amount)
+	}
+}
+
+impl<T: Config> fungibles::Unbalanced<T::AccountId> for Pallet<T> {
+	fn set_balance(asset_id: Self::AssetId, who: &T::AccountId, amount: Self::Balance) -> DispatchResult {
+		// Balance is the same type and will not overflow
+		Pallet::<T>::mutate_account(who, asset_id, |account, _| account.free = amount);
+		Ok(())
+	}
+
+	fn set_total_issuance(asset_id: Self::AssetId, amount: Self::Balance) {
+		// Balance is the same type and will not overflow
+		<TotalIssuance<T>>::mutate(asset_id, |t| *t = amount);
+	}
+}
+
+impl<T: Config> fungibles::InspectHold<T::AccountId> for Pallet<T> {
+	fn balance_on_hold(asset_id: Self::AssetId, who: &T::AccountId) -> T::Balance {
+		Pallet::<T>::accounts(who, asset_id).reserved
+	}
+	fn can_hold(asset_id: Self::AssetId, who: &T::AccountId, amount: T::Balance) -> bool {
+		let a = Pallet::<T>::accounts(who, asset_id);
+		let min_balance = T::ExistentialDeposits::get(&asset_id).max(a.frozen);
+		if a.reserved.checked_add(&amount).is_none() {
+			return false;
+		}
+		// We require it to be min_balance + amount to ensure that the full reserved
+		// funds may be slashed without compromising locked funds or destroying the
+		// account.
+		let required_free = match min_balance.checked_add(&amount) {
+			Some(x) => x,
+			None => return false,
+		};
+		a.free >= required_free
+	}
+}
+
+impl<T: Config> fungibles::MutateHold<T::AccountId> for Pallet<T> {
+	fn hold(asset_id: Self::AssetId, who: &T::AccountId, amount: Self::Balance) -> DispatchResult {
+		if amount.is_zero() {
+			return Ok(());
+		}
+		ensure!(
+			Pallet::<T>::can_reserve(asset_id, who, amount),
+			Error::<T>::BalanceTooLow
+		);
+		Pallet::<T>::mutate_account(who, asset_id, |a, _| {
+			// `can_reserve` has did underflow checking
+			a.free -= amount;
+			// Cannot overflow as `amount` is from `a.free`
+			a.reserved += amount;
+		});
+		Ok(())
+	}
+	fn release(
+		asset_id: Self::AssetId,
+		who: &T::AccountId,
+		amount: Self::Balance,
+		best_effort: bool,
+	) -> Result<T::Balance, DispatchError> {
+		if amount.is_zero() {
+			return Ok(amount);
+		}
+		// Done on a best-effort basis.
+		Pallet::<T>::try_mutate_account(who, asset_id, |a, _| {
+			let new_free = a.free.saturating_add(amount.min(a.reserved));
+			let actual = new_free - a.free;
+			// Guaranteed to be <= amount and <= a.reserved
+			ensure!(best_effort || actual == amount, Error::<T>::BalanceTooLow);
+			a.free = new_free;
+			a.reserved = a.reserved.saturating_sub(actual);
+			Ok(actual)
+		})
+	}
+	fn transfer_held(
+		asset_id: Self::AssetId,
+		source: &T::AccountId,
+		dest: &T::AccountId,
+		amount: Self::Balance,
+		_best_effort: bool,
+		on_hold: bool,
+	) -> Result<Self::Balance, DispatchError> {
+		let status = if on_hold { Status::Reserved } else { Status::Free };
+		Pallet::<T>::repatriate_reserved(asset_id, source, dest, amount, status)
+	}
+}
+
 pub struct CurrencyAdapter<T, GetCurrencyId>(marker::PhantomData<(T, GetCurrencyId)>);
 
 impl<T, GetCurrencyId> PalletCurrency<T::AccountId> for CurrencyAdapter<T, GetCurrencyId>
@@ -903,9 +1183,9 @@ where
 		source: &T::AccountId,
 		dest: &T::AccountId,
 		value: Self::Balance,
-		_existence_requirement: ExistenceRequirement,
+		existence_requirement: ExistenceRequirement,
 	) -> DispatchResult {
-		<Pallet<T> as MultiCurrency<T::AccountId>>::transfer(GetCurrencyId::get(), &source, &dest, value)
+		Pallet::<T>::do_transfer(GetCurrencyId::get(), &source, &dest, value, existence_requirement)
 	}
 
 	fn slash(who: &T::AccountId, value: Self::Balance) -> (Self::NegativeImbalance, Self::Balance) {
@@ -961,14 +1241,25 @@ where
 		who: &T::AccountId,
 		value: Self::Balance,
 		_reasons: WithdrawReasons,
-		_liveness: ExistenceRequirement,
+		liveness: ExistenceRequirement,
 	) -> sp_std::result::Result<Self::NegativeImbalance, DispatchError> {
 		if value.is_zero() {
 			return Ok(Self::NegativeImbalance::zero());
 		}
+
 		let currency_id = GetCurrencyId::get();
-		Pallet::<T>::ensure_can_withdraw(currency_id, who, value)?;
-		Pallet::<T>::set_free_balance(currency_id, who, Pallet::<T>::free_balance(currency_id, who) - value);
+		Pallet::<T>::try_mutate_account(who, currency_id, |account, _is_new| -> DispatchResult {
+			account.free = account.free.checked_sub(&value).ok_or(Error::<T>::BalanceTooLow)?;
+
+			Pallet::<T>::ensure_can_withdraw(currency_id, who, value)?;
+
+			let ed = T::ExistentialDeposits::get(&currency_id);
+			let allow_death = liveness == ExistenceRequirement::AllowDeath;
+			let allow_death = allow_death && !frame_system::Pallet::<T>::is_provider_required(who);
+			ensure!(allow_death || account.total() >= ed, Error::<T>::KeepAlive);
+
+			Ok(())
+		})?;
 
 		Ok(Self::NegativeImbalance::new(value))
 	}
@@ -1070,3 +1361,113 @@ impl<T: Config> TransferAll<T::AccountId> for Pallet<T> {
 		})
 	}
 }
+
+impl<T, GetCurrencyId> fungible::Inspect<T::AccountId> for CurrencyAdapter<T, GetCurrencyId>
+where
+	T: Config,
+	GetCurrencyId: Get<T::CurrencyId>,
+{
+	type Balance = T::Balance;
+
+	fn total_issuance() -> Self::Balance {
+		<Pallet<T> as fungibles::Inspect<_>>::total_issuance(GetCurrencyId::get())
+	}
+	fn minimum_balance() -> Self::Balance {
+		<Pallet<T> as fungibles::Inspect<_>>::minimum_balance(GetCurrencyId::get())
+	}
+	fn balance(who: &T::AccountId) -> Self::Balance {
+		<Pallet<T> as fungibles::Inspect<_>>::balance(GetCurrencyId::get(), who)
+	}
+	fn reducible_balance(who: &T::AccountId, keep_alive: bool) -> Self::Balance {
+		<Pallet<T> as fungibles::Inspect<_>>::reducible_balance(GetCurrencyId::get(), who, keep_alive)
+	}
+	fn can_deposit(who: &T::AccountId, amount: Self::Balance) -> DepositConsequence {
+		<Pallet<T> as fungibles::Inspect<_>>::can_deposit(GetCurrencyId::get(), who, amount)
+	}
+	fn can_withdraw(who: &T::AccountId, amount: Self::Balance) -> WithdrawConsequence<Self::Balance> {
+		<Pallet<T> as fungibles::Inspect<_>>::can_withdraw(GetCurrencyId::get(), who, amount)
+	}
+}
+
+impl<T, GetCurrencyId> fungible::Mutate<T::AccountId> for CurrencyAdapter<T, GetCurrencyId>
+where
+	T: Config,
+	GetCurrencyId: Get<T::CurrencyId>,
+{
+	fn mint_into(who: &T::AccountId, amount: Self::Balance) -> DispatchResult {
+		<Pallet<T> as fungibles::Mutate<_>>::mint_into(GetCurrencyId::get(), who, amount)
+	}
+	fn burn_from(who: &T::AccountId, amount: Self::Balance) -> Result<Self::Balance, DispatchError> {
+		<Pallet<T> as fungibles::Mutate<_>>::burn_from(GetCurrencyId::get(), who, amount)
+	}
+}
+
+impl<T, GetCurrencyId> fungible::Transfer<T::AccountId> for CurrencyAdapter<T, GetCurrencyId>
+where
+	T: Config,
+	GetCurrencyId: Get<T::CurrencyId>,
+{
+	fn transfer(
+		source: &T::AccountId,
+		dest: &T::AccountId,
+		amount: T::Balance,
+		keep_alive: bool,
+	) -> Result<T::Balance, DispatchError> {
+		<Pallet<T> as fungibles::Transfer<_>>::transfer(GetCurrencyId::get(), source, dest, amount, keep_alive)
+	}
+}
+
+impl<T, GetCurrencyId> fungible::Unbalanced<T::AccountId> for CurrencyAdapter<T, GetCurrencyId>
+where
+	T: Config,
+	GetCurrencyId: Get<T::CurrencyId>,
+{
+	fn set_balance(who: &T::AccountId, amount: Self::Balance) -> DispatchResult {
+		<Pallet<T> as fungibles::Unbalanced<_>>::set_balance(GetCurrencyId::get(), who, amount)
+	}
+	fn set_total_issuance(amount: Self::Balance) {
+		<Pallet<T> as fungibles::Unbalanced<_>>::set_total_issuance(GetCurrencyId::get(), amount)
+	}
+}
+
+impl<T, GetCurrencyId> fungible::InspectHold<T::AccountId> for CurrencyAdapter<T, GetCurrencyId>
+where
+	T: Config,
+	GetCurrencyId: Get<T::CurrencyId>,
+{
+	fn balance_on_hold(who: &T::AccountId) -> T::Balance {
+		<Pallet<T> as fungibles::InspectHold<_>>::balance_on_hold(GetCurrencyId::get(), who)
+	}
+	fn can_hold(who: &T::AccountId, amount: T::Balance) -> bool {
+		<Pallet<T> as fungibles::InspectHold<_>>::can_hold(GetCurrencyId::get(), who, amount)
+	}
+}
+
+impl<T, GetCurrencyId> fungible::MutateHold<T::AccountId> for CurrencyAdapter<T, GetCurrencyId>
+where
+	T: Config,
+	GetCurrencyId: Get<T::CurrencyId>,
+{
+	fn hold(who: &T::AccountId, amount: Self::Balance) -> DispatchResult {
+		<Pallet<T> as fungibles::MutateHold<_>>::hold(GetCurrencyId::get(), who, amount)
+	}
+	fn release(who: &T::AccountId, amount: Self::Balance, best_effort: bool) -> Result<T::Balance, DispatchError> {
+		<Pallet<T> as fungibles::MutateHold<_>>::release(GetCurrencyId::get(), who, amount, best_effort)
+	}
+	fn transfer_held(
+		source: &T::AccountId,
+		dest: &T::AccountId,
+		amount: Self::Balance,
+		best_effort: bool,
+		on_hold: bool,
+	) -> Result<Self::Balance, DispatchError> {
+		<Pallet<T> as fungibles::MutateHold<_>>::transfer_held(
+			GetCurrencyId::get(),
+			source,
+			dest,
+			amount,
+			best_effort,
+			on_hold,
+		)
+	}
+}
diff --git a/tokens/src/tests.rs b/tokens/src/tests.rs
index 297da4d173b3398520ad52ff7279ab21ead28424..a11562559092a14a7fc33d21f701218d3f1b3ac2 100644
--- a/tokens/src/tests.rs
+++ b/tokens/src/tests.rs
@@ -137,7 +137,7 @@ fn frozen_can_limit_liquidity() {
 				Error::<Runtime>::LiquidityRestrictions,
 			);
 			assert_ok!(Tokens::set_lock(ID_1, DOT, &ALICE, 10));
-			assert_ok!(<Tokens as MultiCurrency<_>>::transfer(DOT, &ALICE, &BOB, 11),);
+			assert_ok!(<Tokens as MultiCurrency<_>>::transfer(DOT, &ALICE, &BOB, 11));
 		});
 }
 
@@ -159,7 +159,7 @@ fn reserve_should_work() {
 		.one_hundred_for_alice_n_bob()
 		.build()
 		.execute_with(|| {
-			assert_noop!(Tokens::reserve(DOT, &ALICE, 101), Error::<Runtime>::BalanceTooLow,);
+			assert_noop!(Tokens::reserve(DOT, &ALICE, 101), Error::<Runtime>::BalanceTooLow);
 			assert_ok!(Tokens::reserve(DOT, &ALICE, 0));
 			assert_eq!(Tokens::free_balance(DOT, &ALICE), 100);
 			assert_eq!(Tokens::reserved_balance(DOT, &ALICE), 0);
@@ -999,3 +999,98 @@ fn exceeding_max_locks_should_fail() {
 			assert_eq!(Tokens::locks(ALICE, DOT).len(), 2);
 		});
 }
+
+#[test]
+fn fungibles_inspect_trait_should_work() {
+	ExtBuilder::default()
+		.one_hundred_for_alice_n_bob()
+		.build()
+		.execute_with(|| {
+			assert_eq!(<Tokens as fungibles::Inspect<_>>::total_issuance(DOT), 200);
+			assert_eq!(<Tokens as fungibles::Inspect<_>>::minimum_balance(DOT), 2);
+			assert_eq!(<Tokens as fungibles::Inspect<_>>::balance(DOT, &ALICE), 100);
+			assert_eq!(
+				<Tokens as fungibles::Inspect<_>>::reducible_balance(DOT, &ALICE, true),
+				98
+			);
+			assert_ok!(<Tokens as fungibles::Inspect<_>>::can_deposit(DOT, &ALICE, 1).into_result());
+			assert_ok!(<Tokens as fungibles::Inspect<_>>::can_withdraw(DOT, &ALICE, 1).into_result());
+		});
+}
+
+#[test]
+fn fungibles_mutate_trait_should_work() {
+	ExtBuilder::default()
+		.one_hundred_for_alice_n_bob()
+		.build()
+		.execute_with(|| {
+			assert_ok!(<Tokens as fungibles::Mutate<_>>::mint_into(DOT, &ALICE, 10));
+			assert_eq!(<Tokens as fungibles::Mutate<_>>::burn_from(DOT, &ALICE, 8), Ok(8));
+		});
+}
+
+#[test]
+fn fungibles_transfer_trait_should_work() {
+	ExtBuilder::default()
+		.one_hundred_for_alice_n_bob()
+		.build()
+		.execute_with(|| {
+			assert_eq!(<Tokens as fungibles::Inspect<_>>::balance(DOT, &ALICE), 100);
+			assert_eq!(<Tokens as fungibles::Inspect<_>>::balance(DOT, &BOB), 100);
+			assert_ok!(<Tokens as fungibles::Transfer<_>>::transfer(
+				DOT, &ALICE, &BOB, 10, true
+			));
+			assert_eq!(<Tokens as fungibles::Inspect<_>>::balance(DOT, &ALICE), 90);
+			assert_eq!(<Tokens as fungibles::Inspect<_>>::balance(DOT, &BOB), 110);
+		});
+}
+
+#[test]
+fn fungibles_unbalanced_trait_should_work() {
+	ExtBuilder::default()
+		.one_hundred_for_alice_n_bob()
+		.build()
+		.execute_with(|| {
+			assert_eq!(<Tokens as fungibles::Inspect<_>>::balance(DOT, &ALICE), 100);
+			assert_ok!(<Tokens as fungibles::Unbalanced<_>>::set_balance(DOT, &ALICE, 10));
+			assert_eq!(<Tokens as fungibles::Inspect<_>>::balance(DOT, &ALICE), 10);
+
+			assert_eq!(<Tokens as fungibles::Inspect<_>>::total_issuance(DOT), 200);
+			<Tokens as fungibles::Unbalanced<_>>::set_total_issuance(DOT, 10);
+			assert_eq!(<Tokens as fungibles::Inspect<_>>::total_issuance(DOT), 10);
+		});
+}
+
+#[test]
+fn fungibles_inspect_hold_trait_should_work() {
+	ExtBuilder::default()
+		.one_hundred_for_alice_n_bob()
+		.build()
+		.execute_with(|| {
+			assert_eq!(<Tokens as fungibles::InspectHold<_>>::balance_on_hold(DOT, &ALICE), 0);
+			assert_eq!(<Tokens as fungibles::InspectHold<_>>::can_hold(DOT, &ALICE, 50), true);
+			assert_eq!(<Tokens as fungibles::InspectHold<_>>::can_hold(DOT, &ALICE, 100), false);
+		});
+}
+
+#[test]
+fn fungibles_mutate_hold_trait_should_work() {
+	ExtBuilder::default()
+		.one_hundred_for_alice_n_bob()
+		.build()
+		.execute_with(|| {
+			assert_noop!(
+				<Tokens as fungibles::MutateHold<_>>::hold(DOT, &ALICE, 200),
+				Error::<Runtime>::BalanceTooLow
+			);
+			assert_ok!(<Tokens as fungibles::MutateHold<_>>::hold(DOT, &ALICE, 100));
+			assert_eq!(
+				<Tokens as fungibles::MutateHold<_>>::release(DOT, &ALICE, 50, true),
+				Ok(50)
+			);
+			assert_eq!(
+				<Tokens as fungibles::MutateHold<_>>::transfer_held(DOT, &ALICE, &BOB, 100, true, true),
+				Ok(50)
+			);
+		});
+}