diff --git a/prdoc/pr_1296.prdoc b/prdoc/pr_1296.prdoc
new file mode 100644
index 0000000000000000000000000000000000000000..b7ef4288a57a6bdcad6150a98f7ddc3a0d538038
--- /dev/null
+++ b/prdoc/pr_1296.prdoc
@@ -0,0 +1,16 @@
+title: fungible fixes and more conformance tests
+
+doc:
+  - audience: Runtime Dev
+    description: |
+      Adds conformance tests for the Balanced and Unbalanced fungible traits
+      Fixes Unbalanced::decrease_balance not respecting preservation
+      Fixes Balanced::pair possibly returning pairs of imbalances which do not cancel each other out. Method now returns a Result instead (breaking change).
+      Fixes Balances pallet active_issuance possible 'underflow'
+      Refactors the conformance test file structure to match the fungible file structure: tests for traits in regular.rs go into a test file named regular.rs, tests for traits in freezes.rs go into a test file named freezes.rs, etc.
+      Improve doc comments
+      Simplify macros
+
+crates:
+  - name: pallet-balances
+  - name: frame-support
diff --git a/substrate/frame/assets/src/tests/sets.rs b/substrate/frame/assets/src/tests/sets.rs
index bdff5175185f940eaa9bd29b6195f31088925e18..f85a736c08320c22df3ec3ad9d0aeaa74cc1706c 100644
--- a/substrate/frame/assets/src/tests/sets.rs
+++ b/substrate/frame/assets/src/tests/sets.rs
@@ -153,7 +153,7 @@ fn pair_from_set_types_works() {
 		assert_eq!(First::<Assets>::total_issuance(()), 100);
 		assert_eq!(First::<Assets>::total_issuance(()), Assets::total_issuance(asset1));
 
-		let (debt, credit) = First::<Assets>::pair((), 100);
+		let (debt, credit) = First::<Assets>::pair((), 100).unwrap();
 		assert_eq!(First::<Assets>::total_issuance(()), 100);
 		assert_eq!(debt.peek(), 100);
 		assert_eq!(credit.peek(), 100);
diff --git a/substrate/frame/balances/src/impl_fungible.rs b/substrate/frame/balances/src/impl_fungible.rs
index 6737727e0a2977c7a186f4cfd845d994e4e82bc2..0f4e51f35012a23a0d229b4ad6ab3efe2e7e83d7 100644
--- a/substrate/frame/balances/src/impl_fungible.rs
+++ b/substrate/frame/balances/src/impl_fungible.rs
@@ -177,7 +177,10 @@ impl<T: Config<I>, I: 'static> fungible::Unbalanced<T::AccountId> for Pallet<T,
 	}
 
 	fn deactivate(amount: Self::Balance) {
-		InactiveIssuance::<T, I>::mutate(|b| b.saturating_accrue(amount));
+		InactiveIssuance::<T, I>::mutate(|b| {
+			// InactiveIssuance cannot be greater than TotalIssuance.
+			*b = b.saturating_add(amount).min(TotalIssuance::<T, I>::get());
+		});
 	}
 
 	fn reactivate(amount: Self::Balance) {
diff --git a/substrate/frame/balances/src/tests/fungible_conformance_tests.rs b/substrate/frame/balances/src/tests/fungible_conformance_tests.rs
index 6262aa04dc0882c52cd6ef5dc0924971d08826e2..5c0c19a554a1d326e870023799323b93716d6067 100644
--- a/substrate/frame/balances/src/tests/fungible_conformance_tests.rs
+++ b/substrate/frame/balances/src/tests/fungible_conformance_tests.rs
@@ -19,17 +19,19 @@ use super::*;
 use frame_support::traits::fungible::{conformance_tests, Inspect, Mutate};
 use paste::paste;
 
-macro_rules! run_tests {
-    ($path:path, $ext_deposit:expr, $($name:ident),*) => {
+macro_rules! generate_tests {
+	// Handle a conformance test that requires special testing with and without a dust trap.
+    (dust_trap_variation, $base_path:path, $scope:expr, $trait:ident, $ext_deposit:expr, $($test_name:ident),*) => {
 		$(
 			paste! {
 				#[test]
-				fn [< $name _existential_deposit_ $ext_deposit _dust_trap_on >]() {
+				fn [<$trait _ $scope _ $test_name _existential_deposit_ $ext_deposit _dust_trap_on >]() {
+					// Some random trap account.
 					let trap_account = <Test as frame_system::Config>::AccountId::from(65174286u64);
 					let builder = ExtBuilder::default().existential_deposit($ext_deposit).dust_trap(trap_account);
 					builder.build_and_execute_with(|| {
 						Balances::set_balance(&trap_account, Balances::minimum_balance());
-						$path::$name::<
+						$base_path::$scope::$trait::$test_name::<
 							Balances,
 							<Test as frame_system::Config>::AccountId,
 						>(Some(trap_account));
@@ -37,10 +39,10 @@ macro_rules! run_tests {
 				}
 
 				#[test]
-				fn [< $name _existential_deposit_ $ext_deposit _dust_trap_off >]() {
+				fn [< $trait _ $scope _ $test_name _existential_deposit_ $ext_deposit _dust_trap_off >]() {
 					let builder = ExtBuilder::default().existential_deposit($ext_deposit);
 					builder.build_and_execute_with(|| {
-						$path::$name::<
+						$base_path::$scope::$trait::$test_name::<
 							Balances,
 							<Test as frame_system::Config>::AccountId,
 						>(None);
@@ -49,9 +51,37 @@ macro_rules! run_tests {
 			}
 		)*
 	};
-	($path:path, $ext_deposit:expr) => {
-		run_tests!(
-			$path,
+	// Regular conformance test
+    ($base_path:path, $scope:expr, $trait:ident, $ext_deposit:expr, $($test_name:ident),*) => {
+		$(
+			paste! {
+				#[test]
+				fn [< $trait _ $scope _ $test_name _existential_deposit_ $ext_deposit>]() {
+					let builder = ExtBuilder::default().existential_deposit($ext_deposit);
+					builder.build_and_execute_with(|| {
+						$base_path::$scope::$trait::$test_name::<
+							Balances,
+							<Test as frame_system::Config>::AccountId,
+						>();
+					});
+				}
+			}
+		)*
+	};
+	($base_path:path, $ext_deposit:expr) => {
+		// regular::mutate
+		generate_tests!(
+			dust_trap_variation,
+			$base_path,
+			regular,
+			mutate,
+			$ext_deposit,
+			transfer_expendable_dust
+		);
+		generate_tests!(
+			$base_path,
+			regular,
+			mutate,
 			$ext_deposit,
 			mint_into_success,
 			mint_into_overflow,
@@ -66,7 +96,6 @@ macro_rules! run_tests {
 			shelve_insufficient_funds,
 			transfer_success,
 			transfer_expendable_all,
-			transfer_expendable_dust,
 			transfer_protect_preserve,
 			set_balance_mint_success,
 			set_balance_burn_success,
@@ -79,10 +108,34 @@ macro_rules! run_tests {
 			reducible_balance_expendable,
 			reducible_balance_protect_preserve
 		);
+		// regular::unbalanced
+		generate_tests!(
+			$base_path,
+			regular,
+			unbalanced,
+			$ext_deposit,
+			write_balance,
+			decrease_balance_expendable,
+			decrease_balance_preserve,
+			increase_balance,
+			set_total_issuance,
+			deactivate_and_reactivate
+		);
+		// regular::balanced
+		generate_tests!(
+			$base_path,
+			regular,
+			balanced,
+			$ext_deposit,
+			issue_and_resolve_credit,
+			rescind_and_settle_debt,
+			deposit,
+			withdraw,
+			pair
+		);
 	};
 }
 
-run_tests!(conformance_tests::inspect_mutate, 1);
-run_tests!(conformance_tests::inspect_mutate, 2);
-run_tests!(conformance_tests::inspect_mutate, 5);
-run_tests!(conformance_tests::inspect_mutate, 1000);
+generate_tests!(conformance_tests, 1);
+generate_tests!(conformance_tests, 5);
+generate_tests!(conformance_tests, 1000);
diff --git a/substrate/frame/support/src/traits/tokens/fungible/conformance_tests/mod.rs b/substrate/frame/support/src/traits/tokens/fungible/conformance_tests/mod.rs
index 56166436003f3ad24e342166d67f38ff3ebe6edd..005674088dd350d4f240078dedafc0a7007e5a57 100644
--- a/substrate/frame/support/src/traits/tokens/fungible/conformance_tests/mod.rs
+++ b/substrate/frame/support/src/traits/tokens/fungible/conformance_tests/mod.rs
@@ -16,3 +16,4 @@
 // limitations under the License.
 
 pub mod inspect_mutate;
+pub mod regular;
diff --git a/substrate/frame/support/src/traits/tokens/fungible/conformance_tests/regular/balanced.rs b/substrate/frame/support/src/traits/tokens/fungible/conformance_tests/regular/balanced.rs
new file mode 100644
index 0000000000000000000000000000000000000000..d8d20543e3d603c589c8ae2644774280e3f8dda1
--- /dev/null
+++ b/substrate/frame/support/src/traits/tokens/fungible/conformance_tests/regular/balanced.rs
@@ -0,0 +1,292 @@
+// 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.
+
+use crate::traits::{
+	fungible::{Balanced, Inspect},
+	tokens::{imbalance::Imbalance as ImbalanceT, Fortitude, Precision, Preservation},
+};
+use core::fmt::Debug;
+use frame_support::traits::tokens::fungible::imbalance::{Credit, Debt};
+use sp_arithmetic::{traits::AtLeast8BitUnsigned, ArithmeticError};
+use sp_runtime::{traits::Bounded, TokenError};
+
+/// Tests issuing and resolving [`Credit`] imbalances with [`Balanced::issue`] and
+/// [`Balanced::resolve`].
+pub fn issue_and_resolve_credit<T, AccountId>()
+where
+	T: Balanced<AccountId>,
+	<T as Inspect<AccountId>>::Balance: AtLeast8BitUnsigned + Debug,
+	AccountId: AtLeast8BitUnsigned,
+{
+	let account = AccountId::from(0);
+	assert_eq!(T::total_issuance(), 0.into());
+	assert_eq!(T::balance(&account), 0.into());
+
+	// Account that doesn't exist yet can't be credited below the minimum balance
+	let credit: Credit<AccountId, T> = T::issue(T::minimum_balance() - 1.into());
+	// issue temporarily increases total issuance
+	assert_eq!(T::total_issuance(), credit.peek());
+	match T::resolve(&account, credit) {
+		Ok(_) => panic!("Balanced::resolve should have failed"),
+		Err(c) => assert_eq!(c.peek(), T::minimum_balance() - 1.into()),
+	};
+	// Credit was unused and dropped from total issuance
+	assert_eq!(T::total_issuance(), 0.into());
+	assert_eq!(T::balance(&account), 0.into());
+
+	// Credit account with minimum balance
+	let credit: Credit<AccountId, T> = T::issue(T::minimum_balance());
+	match T::resolve(&account, credit) {
+		Ok(()) => {},
+		Err(_) => panic!("resolve failed"),
+	};
+	assert_eq!(T::total_issuance(), T::minimum_balance());
+	assert_eq!(T::balance(&account), T::minimum_balance());
+
+	// Now that account has been created, it can be credited with an amount below the minimum
+	// balance.
+	let total_issuance_before = T::total_issuance();
+	let balance_before = T::balance(&account);
+	let amount = T::minimum_balance() - 1.into();
+	let credit: Credit<AccountId, T> = T::issue(amount);
+	match T::resolve(&account, credit) {
+		Ok(()) => {},
+		Err(_) => panic!("resolve failed"),
+	};
+	assert_eq!(T::total_issuance(), total_issuance_before + amount);
+	assert_eq!(T::balance(&account), balance_before + amount);
+
+	// Unhandled issuance is dropped from total issuance
+	// `let _ = ...` immediately drops the issuance, so everything should be unchanged when
+	// logic gets to the assertions.
+	let total_issuance_before = T::total_issuance();
+	let balance_before = T::balance(&account);
+	let _ = T::issue(5.into());
+	assert_eq!(T::total_issuance(), total_issuance_before);
+	assert_eq!(T::balance(&account), balance_before);
+}
+
+/// Tests issuing and resolving [`Debt`] imbalances with [`Balanced::rescind`] and
+/// [`Balanced::settle`].
+pub fn rescind_and_settle_debt<T, AccountId>()
+where
+	T: Balanced<AccountId>,
+	<T as Inspect<AccountId>>::Balance: AtLeast8BitUnsigned + Debug,
+	AccountId: AtLeast8BitUnsigned,
+{
+	// Credit account with some balance
+	let account = AccountId::from(0);
+	let initial_bal = T::minimum_balance() + 10.into();
+	let credit = T::issue(initial_bal);
+	match T::resolve(&account, credit) {
+		Ok(()) => {},
+		Err(_) => panic!("resolve failed"),
+	};
+	assert_eq!(T::total_issuance(), initial_bal);
+	assert_eq!(T::balance(&account), initial_bal);
+
+	// Rescind some balance
+	let rescind_amount = 2.into();
+	let debt: Debt<AccountId, T> = T::rescind(rescind_amount);
+	assert_eq!(debt.peek(), rescind_amount);
+	match T::settle(&account, debt, Preservation::Expendable) {
+		Ok(c) => {
+			// We settled the full debt and account was not dusted, so there is no left over
+			// credit.
+			assert_eq!(c.peek(), 0.into());
+		},
+		Err(_) => panic!("settle failed"),
+	};
+	assert_eq!(T::total_issuance(), initial_bal - rescind_amount);
+	assert_eq!(T::balance(&account), initial_bal - rescind_amount);
+
+	// Unhandled debt is added from total issuance
+	// `let _ = ...` immediately drops the debt, so everything should be unchanged when
+	// logic gets to the assertions.
+	let _ = T::rescind(T::minimum_balance());
+	assert_eq!(T::total_issuance(), initial_bal - rescind_amount);
+	assert_eq!(T::balance(&account), initial_bal - rescind_amount);
+
+	// Preservation::Preserve will not allow the account to be dusted on settle
+	let balance_before = T::balance(&account);
+	let total_issuance_before = T::total_issuance();
+	let rescind_amount = balance_before - T::minimum_balance() + 1.into();
+	let debt: Debt<AccountId, T> = T::rescind(rescind_amount);
+	assert_eq!(debt.peek(), rescind_amount);
+	// The new debt is temporarily removed from total_issuance
+	assert_eq!(T::total_issuance(), total_issuance_before - debt.peek().into());
+	match T::settle(&account, debt, Preservation::Preserve) {
+		Ok(_) => panic!("Balanced::settle should have failed"),
+		Err(d) => assert_eq!(d.peek(), rescind_amount),
+	};
+	// The debt is added back to total_issuance because it was dropped, leaving the operation a
+	// noop.
+	assert_eq!(T::total_issuance(), total_issuance_before);
+	assert_eq!(T::balance(&account), balance_before);
+
+	// Preservation::Expendable allows the account to be dusted on settle
+	let debt: Debt<AccountId, T> = T::rescind(rescind_amount);
+	match T::settle(&account, debt, Preservation::Expendable) {
+		Ok(c) => {
+			// Dusting happens internally, there is no left over credit.
+			assert_eq!(c.peek(), 0.into());
+		},
+		Err(_) => panic!("settle failed"),
+	};
+	// The account is dusted and debt dropped from total_issuance
+	assert_eq!(T::total_issuance(), 0.into());
+	assert_eq!(T::balance(&account), 0.into());
+}
+
+/// Tests [`Balanced::deposit`].
+pub fn deposit<T, AccountId>()
+where
+	T: Balanced<AccountId>,
+	<T as Inspect<AccountId>>::Balance: AtLeast8BitUnsigned + Debug,
+	AccountId: AtLeast8BitUnsigned,
+{
+	// Cannot deposit < minimum balance into non-existent account
+	let account = AccountId::from(0);
+	let amount = T::minimum_balance() - 1.into();
+	match T::deposit(&account, amount, Precision::Exact) {
+		Ok(_) => panic!("Balanced::deposit should have failed"),
+		Err(e) => assert_eq!(e, TokenError::BelowMinimum.into()),
+	};
+	assert_eq!(T::total_issuance(), 0.into());
+	assert_eq!(T::balance(&account), 0.into());
+
+	// Can deposit minimum balance into non-existent account
+	let amount = T::minimum_balance();
+	match T::deposit(&account, amount, Precision::Exact) {
+		Ok(d) => assert_eq!(d.peek(), amount),
+		Err(_) => panic!("Balanced::deposit failed"),
+	};
+	assert_eq!(T::total_issuance(), amount);
+	assert_eq!(T::balance(&account), amount);
+
+	// Depositing amount that would overflow when Precision::Exact fails and is a noop
+	let amount = T::Balance::max_value();
+	let balance_before = T::balance(&account);
+	let total_issuance_before = T::total_issuance();
+	match T::deposit(&account, amount, Precision::Exact) {
+		Ok(_) => panic!("Balanced::deposit should have failed"),
+		Err(e) => assert_eq!(e, ArithmeticError::Overflow.into()),
+	};
+	assert_eq!(T::total_issuance(), total_issuance_before);
+	assert_eq!(T::balance(&account), balance_before);
+
+	// Depositing amount that would overflow when Precision::BestEffort saturates
+	match T::deposit(&account, amount, Precision::BestEffort) {
+		Ok(d) => assert_eq!(d.peek(), T::Balance::max_value() - balance_before),
+		Err(_) => panic!("Balanced::deposit failed"),
+	};
+	assert_eq!(T::total_issuance(), T::Balance::max_value());
+	assert_eq!(T::balance(&account), T::Balance::max_value());
+}
+
+/// Tests [`Balanced::withdraw`].
+pub fn withdraw<T, AccountId>()
+where
+	T: Balanced<AccountId>,
+	<T as Inspect<AccountId>>::Balance: AtLeast8BitUnsigned + Debug,
+	AccountId: AtLeast8BitUnsigned,
+{
+	let account = AccountId::from(0);
+
+	// Init an account with some balance
+	let initial_balance = T::minimum_balance() + 10.into();
+	match T::deposit(&account, initial_balance, Precision::Exact) {
+		Ok(_) => {},
+		Err(_) => panic!("Balanced::deposit failed"),
+	};
+	assert_eq!(T::total_issuance(), initial_balance);
+	assert_eq!(T::balance(&account), initial_balance);
+
+	// Withdrawing an amount smaller than the balance works when Precision::Exact
+	let amount = 1.into();
+	match T::withdraw(
+		&account,
+		amount,
+		Precision::Exact,
+		Preservation::Expendable,
+		Fortitude::Polite,
+	) {
+		Ok(c) => assert_eq!(c.peek(), amount),
+		Err(_) => panic!("withdraw failed"),
+	};
+	assert_eq!(T::total_issuance(), initial_balance - amount);
+	assert_eq!(T::balance(&account), initial_balance - amount);
+
+	// Withdrawing an amount greater than the balance fails when Precision::Exact
+	let balance_before = T::balance(&account);
+	let amount = balance_before + 1.into();
+	match T::withdraw(
+		&account,
+		amount,
+		Precision::Exact,
+		Preservation::Expendable,
+		Fortitude::Polite,
+	) {
+		Ok(_) => panic!("should have failed"),
+		Err(e) => assert_eq!(e, TokenError::FundsUnavailable.into()),
+	};
+	assert_eq!(T::total_issuance(), balance_before);
+	assert_eq!(T::balance(&account), balance_before);
+
+	// Withdrawing an amount greater than the balance works when Precision::BestEffort
+	let balance_before = T::balance(&account);
+	let amount = balance_before + 1.into();
+	match T::withdraw(
+		&account,
+		amount,
+		Precision::BestEffort,
+		Preservation::Expendable,
+		Fortitude::Polite,
+	) {
+		Ok(c) => assert_eq!(c.peek(), balance_before),
+		Err(_) => panic!("withdraw failed"),
+	};
+	assert_eq!(T::total_issuance(), 0.into());
+	assert_eq!(T::balance(&account), 0.into());
+}
+
+/// Tests [`Balanced::pair`].
+pub fn pair<T, AccountId>()
+where
+	T: Balanced<AccountId>,
+	<T as Inspect<AccountId>>::Balance: AtLeast8BitUnsigned + Debug,
+	AccountId: AtLeast8BitUnsigned,
+{
+	T::set_total_issuance(50.into());
+
+	// Pair zero balance works
+	let (credit, debt) = T::pair(0.into()).unwrap();
+	assert_eq!(debt.peek(), 0.into());
+	assert_eq!(credit.peek(), 0.into());
+
+	// Pair with non-zero balance: the credit and debt cancel each other out
+	let balance = 10.into();
+	let (credit, debt) = T::pair(balance).unwrap();
+	assert_eq!(credit.peek(), balance);
+	assert_eq!(debt.peek(), balance);
+
+	// Creating a pair that could increase total_issuance beyond the max value returns an error
+	let max_value = T::Balance::max_value();
+	let distance_from_max_value = 5.into();
+	T::set_total_issuance(max_value - distance_from_max_value);
+	T::pair(distance_from_max_value + 5.into()).unwrap_err();
+}
diff --git a/substrate/frame/support/src/traits/tokens/fungible/conformance_tests/regular/mod.rs b/substrate/frame/support/src/traits/tokens/fungible/conformance_tests/regular/mod.rs
new file mode 100644
index 0000000000000000000000000000000000000000..85acbcf2fcd3ea6c04f9929db38d42ebf2149b58
--- /dev/null
+++ b/substrate/frame/support/src/traits/tokens/fungible/conformance_tests/regular/mod.rs
@@ -0,0 +1,20 @@
+// 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.
+
+pub mod balanced;
+pub mod mutate;
+pub mod unbalanced;
diff --git a/substrate/frame/support/src/traits/tokens/fungible/conformance_tests/regular/mutate.rs b/substrate/frame/support/src/traits/tokens/fungible/conformance_tests/regular/mutate.rs
new file mode 100644
index 0000000000000000000000000000000000000000..95b5256bb4912269b9ad7c40ba637f966035c56b
--- /dev/null
+++ b/substrate/frame/support/src/traits/tokens/fungible/conformance_tests/regular/mutate.rs
@@ -0,0 +1,783 @@
+// 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.
+
+use crate::traits::{
+	fungible::{Inspect, Mutate},
+	tokens::{
+		DepositConsequence, Fortitude, Precision, Preservation, Provenance, WithdrawConsequence,
+	},
+};
+use core::fmt::Debug;
+use sp_arithmetic::traits::AtLeast8BitUnsigned;
+use sp_runtime::traits::{Bounded, Zero};
+
+/// Test [`Mutate::mint_into`] for successful token minting.
+///
+/// It ensures that account balances and total issuance values are updated correctly after
+/// minting tokens into two distinct accounts.
+pub fn mint_into_success<T, AccountId>()
+where
+	T: Mutate<AccountId>,
+	<T as Inspect<AccountId>>::Balance: AtLeast8BitUnsigned + Debug,
+	AccountId: AtLeast8BitUnsigned,
+{
+	let initial_total_issuance = T::total_issuance();
+	let initial_active_issuance = T::active_issuance();
+	let account_0 = AccountId::from(0);
+	let account_1 = AccountId::from(1);
+
+	// Test: Mint an amount into each account
+	let amount_0 = T::minimum_balance();
+	let amount_1 = T::minimum_balance() + 5.into();
+	T::mint_into(&account_0, amount_0).unwrap();
+	T::mint_into(&account_1, amount_1).unwrap();
+
+	// Verify: Account balances are updated correctly
+	assert_eq!(T::total_balance(&account_0), amount_0);
+	assert_eq!(T::total_balance(&account_1), amount_1);
+	assert_eq!(T::balance(&account_0), amount_0);
+	assert_eq!(T::balance(&account_1), amount_1);
+
+	// Verify: Total issuance is updated correctly
+	assert_eq!(T::total_issuance(), initial_total_issuance + amount_0 + amount_1);
+	assert_eq!(T::active_issuance(), initial_active_issuance + amount_0 + amount_1);
+}
+
+/// Test [`Mutate::mint_into`] for overflow prevention.
+///
+/// This test ensures that minting tokens beyond the maximum balance value for an account
+/// returns an error and does not change the account balance or total issuance values.
+pub fn mint_into_overflow<T, AccountId>()
+where
+	T: Mutate<AccountId>,
+	<T as Inspect<AccountId>>::Balance: AtLeast8BitUnsigned + Debug,
+	AccountId: AtLeast8BitUnsigned,
+{
+	let initial_total_issuance = T::total_issuance();
+	let initial_active_issuance = T::active_issuance();
+	let account = AccountId::from(10);
+	let amount = T::Balance::max_value() - 5.into() - initial_total_issuance;
+
+	// Mint just below the maximum balance
+	T::mint_into(&account, amount).unwrap();
+
+	// Verify: Minting beyond the maximum balance value returns an Err
+	T::mint_into(&account, 10.into()).unwrap_err();
+
+	// Verify: The balance did not change
+	assert_eq!(T::total_balance(&account), amount);
+	assert_eq!(T::balance(&account), amount);
+
+	// Verify: The total issuance did not change
+	assert_eq!(T::total_issuance(), initial_total_issuance + amount);
+	assert_eq!(T::active_issuance(), initial_active_issuance + amount);
+}
+
+/// Test [`Mutate::mint_into`] for handling balances below the minimum value.
+///
+/// This test verifies that minting tokens below the minimum balance for an account
+/// returns an error and has no impact on the account balance or total issuance values.
+pub fn mint_into_below_minimum<T, AccountId>()
+where
+	T: Mutate<AccountId>,
+	<T as Inspect<AccountId>>::Balance: AtLeast8BitUnsigned + Debug,
+	AccountId: AtLeast8BitUnsigned,
+{
+	// Skip if there is no minimum balance
+	if T::minimum_balance() == T::Balance::zero() {
+		return
+	}
+
+	let initial_total_issuance = T::total_issuance();
+	let initial_active_issuance = T::active_issuance();
+	let account = AccountId::from(10);
+	let amount = T::minimum_balance() - 1.into();
+
+	// Verify: Minting below the minimum balance returns Err
+	T::mint_into(&account, amount).unwrap_err();
+
+	// Verify: noop
+	assert_eq!(T::total_balance(&account), T::Balance::zero());
+	assert_eq!(T::balance(&account), T::Balance::zero());
+	assert_eq!(T::total_issuance(), initial_total_issuance);
+	assert_eq!(T::active_issuance(), initial_active_issuance);
+}
+
+/// Test [`Mutate::burn_from`] for successfully burning an exact amount of tokens.
+///
+/// This test checks that burning tokens with [`Precision::Exact`] correctly reduces the account
+/// balance and total issuance values by the burned amount.
+pub fn burn_from_exact_success<T, AccountId>()
+where
+	T: Mutate<AccountId>,
+	<T as Inspect<AccountId>>::Balance: AtLeast8BitUnsigned + Debug,
+	AccountId: AtLeast8BitUnsigned,
+{
+	let initial_total_issuance = T::total_issuance();
+	let initial_active_issuance = T::active_issuance();
+
+	// Setup account
+	let account = AccountId::from(5);
+	let initial_balance = T::minimum_balance() + 10.into();
+	T::mint_into(&account, initial_balance).unwrap();
+
+	// Test: Burn an exact amount from the account
+	let amount_to_burn = T::Balance::from(5);
+	let precision = Precision::Exact;
+	let force = Fortitude::Polite;
+	T::burn_from(&account, amount_to_burn, precision, force).unwrap();
+
+	// Verify: The balance and total issuance should be reduced by the burned amount
+	assert_eq!(T::balance(&account), initial_balance - amount_to_burn);
+	assert_eq!(T::total_balance(&account), initial_balance - amount_to_burn);
+	assert_eq!(T::total_issuance(), initial_total_issuance + initial_balance - amount_to_burn);
+	assert_eq!(T::active_issuance(), initial_active_issuance + initial_balance - amount_to_burn);
+}
+
+/// Test [`Mutate::burn_from`] for successfully burning tokens with [`Precision::BestEffort`].
+///
+/// This test verifies that the burning tokens with best-effort precision correctly reduces the
+/// account balance and total issuance values by the reducible balance when attempting to burn
+/// an amount greater than the reducible balance.
+pub fn burn_from_best_effort_success<T, AccountId>()
+where
+	T: Mutate<AccountId>,
+	<T as Inspect<AccountId>>::Balance: AtLeast8BitUnsigned + Debug,
+	AccountId: AtLeast8BitUnsigned,
+{
+	let initial_total_issuance = T::total_issuance();
+	let initial_active_issuance = T::active_issuance();
+
+	// Setup account
+	let account = AccountId::from(5);
+	let initial_balance = T::minimum_balance() + 10.into();
+	T::mint_into(&account, initial_balance).unwrap();
+
+	// Get reducible balance
+	let force = Fortitude::Polite;
+	let reducible_balance = T::reducible_balance(&account, Preservation::Expendable, force);
+
+	// Test: Burn a best effort amount from the account that is greater than the reducible
+	// balance
+	let amount_to_burn = reducible_balance + 5.into();
+	let precision = Precision::BestEffort;
+	assert!(amount_to_burn > reducible_balance);
+	assert!(amount_to_burn > T::balance(&account));
+	T::burn_from(&account, amount_to_burn, precision, force).unwrap();
+
+	// Verify: The balance and total issuance should be reduced by the reducible_balance
+	assert_eq!(T::balance(&account), initial_balance - reducible_balance);
+	assert_eq!(T::total_balance(&account), initial_balance - reducible_balance);
+	assert_eq!(T::total_issuance(), initial_total_issuance + initial_balance - reducible_balance);
+	assert_eq!(T::active_issuance(), initial_active_issuance + initial_balance - reducible_balance);
+}
+
+/// Test [`Mutate::burn_from`] handling of insufficient funds when called with
+/// [`Precision::Exact`].
+///
+/// This test verifies that burning an amount greater than the account's balance with exact
+/// precision returns an error and does not change the account balance or total issuance values.
+pub fn burn_from_exact_insufficient_funds<T, AccountId>()
+where
+	T: Mutate<AccountId>,
+	<T as Inspect<AccountId>>::Balance: AtLeast8BitUnsigned + Debug,
+	AccountId: AtLeast8BitUnsigned,
+{
+	// Set up the initial conditions and parameters for the test
+	let account = AccountId::from(5);
+	let initial_balance = T::minimum_balance() + 10.into();
+	T::mint_into(&account, initial_balance).unwrap();
+	let initial_total_issuance = T::total_issuance();
+	let initial_active_issuance = T::active_issuance();
+
+	// Verify: Burn an amount greater than the account's balance with Exact precision returns
+	// Err
+	let amount_to_burn = initial_balance + 10.into();
+	let precision = Precision::Exact;
+	let force = Fortitude::Polite;
+	T::burn_from(&account, amount_to_burn, precision, force).unwrap_err();
+
+	// Verify: The balance and total issuance should remain unchanged
+	assert_eq!(T::balance(&account), initial_balance);
+	assert_eq!(T::total_balance(&account), initial_balance);
+	assert_eq!(T::total_issuance(), initial_total_issuance);
+	assert_eq!(T::active_issuance(), initial_active_issuance);
+}
+
+/// Test [`Mutate::restore`] for successful restoration.
+///
+/// This test verifies that restoring an amount into each account updates their balances and the
+/// total issuance values correctly.
+pub fn restore_success<T, AccountId>()
+where
+	T: Mutate<AccountId>,
+	<T as Inspect<AccountId>>::Balance: AtLeast8BitUnsigned + Debug,
+	AccountId: AtLeast8BitUnsigned,
+{
+	let account_0 = AccountId::from(0);
+	let account_1 = AccountId::from(1);
+
+	// Test: Restore an amount into each account
+	let amount_0 = T::minimum_balance();
+	let amount_1 = T::minimum_balance() + 5.into();
+	let initial_total_issuance = T::total_issuance();
+	let initial_active_issuance = T::active_issuance();
+	T::restore(&account_0, amount_0).unwrap();
+	T::restore(&account_1, amount_1).unwrap();
+
+	// Verify: Account balances are updated correctly
+	assert_eq!(T::total_balance(&account_0), amount_0);
+	assert_eq!(T::total_balance(&account_1), amount_1);
+	assert_eq!(T::balance(&account_0), amount_0);
+	assert_eq!(T::balance(&account_1), amount_1);
+
+	// Verify: Total issuance is updated correctly
+	assert_eq!(T::total_issuance(), initial_total_issuance + amount_0 + amount_1);
+	assert_eq!(T::active_issuance(), initial_active_issuance + amount_0 + amount_1);
+}
+
+/// Test [`Mutate::restore`] handles balance overflow.
+///
+/// This test verifies that restoring an amount beyond the maximum balance returns an error and
+/// does not change the account balance or total issuance values.
+pub fn restore_overflow<T, AccountId>()
+where
+	T: Mutate<AccountId>,
+	<T as Inspect<AccountId>>::Balance: AtLeast8BitUnsigned + Debug,
+	AccountId: AtLeast8BitUnsigned,
+{
+	let initial_total_issuance = T::total_issuance();
+	let initial_active_issuance = T::active_issuance();
+	let account = AccountId::from(10);
+	let amount = T::Balance::max_value() - 5.into() - initial_total_issuance;
+
+	// Restore just below the maximum balance
+	T::restore(&account, amount).unwrap();
+
+	// Verify: Restoring beyond the maximum balance returns an Err
+	T::restore(&account, 10.into()).unwrap_err();
+
+	// Verify: The balance and total issuance did not change
+	assert_eq!(T::total_balance(&account), amount);
+	assert_eq!(T::balance(&account), amount);
+	assert_eq!(T::total_issuance(), initial_total_issuance + amount);
+	assert_eq!(T::active_issuance(), initial_active_issuance + amount);
+}
+
+/// Test [`Mutate::restore`] handles restoration below the minimum balance.
+///
+/// This test verifies that restoring an amount below the minimum balance returns an error and
+/// does not change the account balance or total issuance values.
+pub fn restore_below_minimum<T, AccountId>()
+where
+	T: Mutate<AccountId>,
+	<T as Inspect<AccountId>>::Balance: AtLeast8BitUnsigned + Debug,
+	AccountId: AtLeast8BitUnsigned,
+{
+	// Skip if there is no minimum balance
+	if T::minimum_balance() == T::Balance::zero() {
+		return
+	}
+
+	let account = AccountId::from(10);
+	let amount = T::minimum_balance() - 1.into();
+	let initial_total_issuance = T::total_issuance();
+	let initial_active_issuance = T::active_issuance();
+
+	// Verify: Restoring below the minimum balance returns Err
+	T::restore(&account, amount).unwrap_err();
+
+	// Verify: noop
+	assert_eq!(T::total_balance(&account), T::Balance::zero());
+	assert_eq!(T::balance(&account), T::Balance::zero());
+	assert_eq!(T::total_issuance(), initial_total_issuance);
+	assert_eq!(T::active_issuance(), initial_active_issuance);
+}
+
+/// Test [`Mutate::shelve`] for successful shelving.
+///
+/// This test verifies that shelving an amount from an account reduces the account balance and
+/// total issuance values by the shelved amount.
+pub fn shelve_success<T, AccountId>()
+where
+	T: Mutate<AccountId>,
+	<T as Inspect<AccountId>>::Balance: AtLeast8BitUnsigned + Debug,
+	AccountId: AtLeast8BitUnsigned,
+{
+	let initial_total_issuance = T::total_issuance();
+	let initial_active_issuance = T::active_issuance();
+
+	// Setup account
+	let account = AccountId::from(5);
+	let initial_balance = T::minimum_balance() + 10.into();
+
+	T::restore(&account, initial_balance).unwrap();
+
+	// Test: Shelve an amount from the account
+	let amount_to_shelve = T::Balance::from(5);
+	T::shelve(&account, amount_to_shelve).unwrap();
+
+	// Verify: The balance and total issuance should be reduced by the shelved amount
+	assert_eq!(T::balance(&account), initial_balance - amount_to_shelve);
+	assert_eq!(T::total_balance(&account), initial_balance - amount_to_shelve);
+	assert_eq!(T::total_issuance(), initial_total_issuance + initial_balance - amount_to_shelve);
+	assert_eq!(T::active_issuance(), initial_active_issuance + initial_balance - amount_to_shelve);
+}
+
+/// Test [`Mutate::shelve`] handles insufficient funds correctly.
+///
+/// This test verifies that attempting to shelve an amount greater than the account's balance
+/// returns an error and does not change the account balance or total issuance values.
+pub fn shelve_insufficient_funds<T, AccountId>()
+where
+	T: Mutate<AccountId>,
+	<T as Inspect<AccountId>>::Balance: AtLeast8BitUnsigned + Debug,
+	AccountId: AtLeast8BitUnsigned,
+{
+	let initial_total_issuance = T::total_issuance();
+	let initial_active_issuance = T::active_issuance();
+
+	// Set up the initial conditions and parameters for the test
+	let account = AccountId::from(5);
+	let initial_balance = T::minimum_balance() + 10.into();
+	T::restore(&account, initial_balance).unwrap();
+
+	// Verify: Shelving greater than the balance with Exact precision returns Err
+	let amount_to_shelve = initial_balance + 10.into();
+	T::shelve(&account, amount_to_shelve).unwrap_err();
+
+	// Verify: The balance and total issuance should remain unchanged
+	assert_eq!(T::balance(&account), initial_balance);
+	assert_eq!(T::total_balance(&account), initial_balance);
+	assert_eq!(T::total_issuance(), initial_total_issuance + initial_balance);
+	assert_eq!(T::active_issuance(), initial_active_issuance + initial_balance);
+}
+
+/// Test [`Mutate::transfer`] for a successful transfer.
+///
+/// This test verifies that transferring an amount between two accounts with updates the account
+/// balances and maintains correct total issuance and active issuance values.
+pub fn transfer_success<T, AccountId>()
+where
+	T: Mutate<AccountId>,
+	<T as Inspect<AccountId>>::Balance: AtLeast8BitUnsigned + Debug,
+	AccountId: AtLeast8BitUnsigned,
+{
+	let initial_total_issuance = T::total_issuance();
+	let initial_active_issuance = T::active_issuance();
+	let account_0 = AccountId::from(0);
+	let account_1 = AccountId::from(1);
+	let initial_balance = T::minimum_balance() + 10.into();
+	T::set_balance(&account_0, initial_balance);
+	T::set_balance(&account_1, initial_balance);
+
+	// Test: Transfer an amount from account_0 to account_1
+	let transfer_amount = T::Balance::from(3);
+	T::transfer(&account_0, &account_1, transfer_amount, Preservation::Expendable).unwrap();
+
+	// Verify: Account balances are updated correctly
+	assert_eq!(T::total_balance(&account_0), initial_balance - transfer_amount);
+	assert_eq!(T::total_balance(&account_1), initial_balance + transfer_amount);
+	assert_eq!(T::balance(&account_0), initial_balance - transfer_amount);
+	assert_eq!(T::balance(&account_1), initial_balance + transfer_amount);
+
+	// Verify: Total issuance doesn't change
+	assert_eq!(T::total_issuance(), initial_total_issuance + initial_balance * 2.into());
+	assert_eq!(T::active_issuance(), initial_active_issuance + initial_balance * 2.into());
+}
+
+/// Test calling [`Mutate::transfer`] with [`Preservation::Expendable`] correctly transfers the
+/// entire balance.
+///
+/// This test verifies that transferring the entire balance from one account to another with
+/// when preservation is expendable updates the account balances and maintains the total
+/// issuance and active issuance values.
+pub fn transfer_expendable_all<T, AccountId>()
+where
+	T: Mutate<AccountId>,
+	<T as Inspect<AccountId>>::Balance: AtLeast8BitUnsigned + Debug,
+	AccountId: AtLeast8BitUnsigned,
+{
+	let initial_total_issuance = T::total_issuance();
+	let initial_active_issuance = T::active_issuance();
+	let account_0 = AccountId::from(0);
+	let account_1 = AccountId::from(1);
+	let initial_balance = T::minimum_balance() + 10.into();
+	T::set_balance(&account_0, initial_balance);
+	T::set_balance(&account_1, initial_balance);
+
+	// Test: Transfer entire balance from account_0 to account_1
+	let preservation = Preservation::Expendable;
+	let transfer_amount = initial_balance;
+	T::transfer(&account_0, &account_1, transfer_amount, preservation).unwrap();
+
+	// Verify: Account balances are updated correctly
+	assert_eq!(T::total_balance(&account_0), T::Balance::zero());
+	assert_eq!(T::total_balance(&account_1), initial_balance * 2.into());
+	assert_eq!(T::balance(&account_0), T::Balance::zero());
+	assert_eq!(T::balance(&account_1), initial_balance * 2.into());
+
+	// Verify: Total issuance doesn't change
+	assert_eq!(T::total_issuance(), initial_total_issuance + initial_balance * 2.into());
+	assert_eq!(T::active_issuance(), initial_active_issuance + initial_balance * 2.into());
+}
+
+/// Test calling [`Mutate::transfer`] function with [`Preservation::Expendable`] and an amount
+/// that results in some dust.
+///
+/// This test verifies that dust is handled correctly when an account is reaped, with and
+/// without a dust trap.
+///
+/// # Parameters
+///
+/// - dust_trap: An optional account identifier to which dust will be collected. If `None`, dust is
+///   expected to be removed from the total and active issuance.
+pub fn transfer_expendable_dust<T, AccountId>(dust_trap: Option<AccountId>)
+where
+	T: Mutate<AccountId>,
+	<T as Inspect<AccountId>>::Balance: AtLeast8BitUnsigned + Debug,
+	AccountId: AtLeast8BitUnsigned,
+{
+	if T::minimum_balance() == T::Balance::zero() {
+		return
+	}
+
+	let account_0 = AccountId::from(10);
+	let account_1 = AccountId::from(20);
+	let initial_balance = T::minimum_balance() + 10.into();
+	T::set_balance(&account_0, initial_balance);
+	T::set_balance(&account_1, initial_balance);
+
+	let initial_total_issuance = T::total_issuance();
+	let initial_active_issuance = T::active_issuance();
+	let initial_dust_trap_balance = match dust_trap.clone() {
+		Some(dust_trap) => T::total_balance(&dust_trap),
+		None => T::Balance::zero(),
+	};
+
+	// Test: Transfer balance
+	let preservation = Preservation::Expendable;
+	let transfer_amount = T::Balance::from(11);
+	T::transfer(&account_0, &account_1, transfer_amount, preservation).unwrap();
+
+	// Verify: Account balances are updated correctly
+	assert_eq!(T::total_balance(&account_0), T::Balance::zero());
+	assert_eq!(T::total_balance(&account_1), initial_balance + transfer_amount);
+	assert_eq!(T::balance(&account_0), T::Balance::zero());
+	assert_eq!(T::balance(&account_1), initial_balance + transfer_amount);
+
+	match dust_trap {
+		Some(dust_trap) => {
+			// Verify: Total issuance and active issuance don't change
+			assert_eq!(T::total_issuance(), initial_total_issuance);
+			assert_eq!(T::active_issuance(), initial_active_issuance);
+			// Verify: Dust is collected into dust trap
+			assert_eq!(
+				T::total_balance(&dust_trap),
+				initial_dust_trap_balance + T::minimum_balance() - 1.into()
+			);
+			assert_eq!(
+				T::balance(&dust_trap),
+				initial_dust_trap_balance + T::minimum_balance() - 1.into()
+			);
+		},
+		None => {
+			// Verify: Total issuance and active issuance are reduced by the dust amount
+			assert_eq!(
+				T::total_issuance(),
+				initial_total_issuance - T::minimum_balance() + 1.into()
+			);
+			assert_eq!(
+				T::active_issuance(),
+				initial_active_issuance - T::minimum_balance() + 1.into()
+			);
+		},
+	}
+}
+
+/// Test [`Mutate::transfer`] with [`Preservation::Protect`] and [`Preservation::Preserve`]
+/// transferring the entire balance.
+///
+/// This test verifies that attempting to transfer the entire balance with returns an error when
+/// preservation should not allow it, and the account balances, total issuance, and active
+/// issuance values remain unchanged.
+pub fn transfer_protect_preserve<T, AccountId>()
+where
+	T: Mutate<AccountId>,
+	<T as Inspect<AccountId>>::Balance: AtLeast8BitUnsigned + Debug,
+	AccountId: AtLeast8BitUnsigned,
+{
+	// This test means nothing if there is no minimum balance
+	if T::minimum_balance() == T::Balance::zero() {
+		return
+	}
+
+	let initial_total_issuance = T::total_issuance();
+	let initial_active_issuance = T::active_issuance();
+	let account_0 = AccountId::from(0);
+	let account_1 = AccountId::from(1);
+	let initial_balance = T::minimum_balance() + 10.into();
+	T::set_balance(&account_0, initial_balance);
+	T::set_balance(&account_1, initial_balance);
+
+	// Verify: Transfer Protect entire balance from account_0 to account_1 should Err
+	let preservation = Preservation::Protect;
+	let transfer_amount = initial_balance;
+	T::transfer(&account_0, &account_1, transfer_amount, preservation).unwrap_err();
+
+	// Verify: Noop
+	assert_eq!(T::total_balance(&account_0), initial_balance);
+	assert_eq!(T::total_balance(&account_1), initial_balance);
+	assert_eq!(T::balance(&account_0), initial_balance);
+	assert_eq!(T::balance(&account_1), initial_balance);
+	assert_eq!(T::total_issuance(), initial_total_issuance + initial_balance * 2.into());
+	assert_eq!(T::active_issuance(), initial_active_issuance + initial_balance * 2.into());
+
+	// Verify: Transfer Preserve entire balance from account_0 to account_1 should Err
+	let preservation = Preservation::Preserve;
+	T::transfer(&account_0, &account_1, transfer_amount, preservation).unwrap_err();
+
+	// Verify: Noop
+	assert_eq!(T::total_balance(&account_0), initial_balance);
+	assert_eq!(T::total_balance(&account_1), initial_balance);
+	assert_eq!(T::balance(&account_0), initial_balance);
+	assert_eq!(T::balance(&account_1), initial_balance);
+	assert_eq!(T::total_issuance(), initial_total_issuance + initial_balance * 2.into());
+	assert_eq!(T::active_issuance(), initial_active_issuance + initial_balance * 2.into());
+}
+
+/// Test [`Mutate::set_balance`] mints balances correctly.
+///
+/// This test verifies that minting a balance using `set_balance` updates the account balance,
+/// total issuance, and active issuance correctly.
+pub fn set_balance_mint_success<T, AccountId>()
+where
+	T: Mutate<AccountId>,
+	<T as Inspect<AccountId>>::Balance: AtLeast8BitUnsigned + Debug,
+	AccountId: AtLeast8BitUnsigned,
+{
+	let initial_total_issuance = T::total_issuance();
+	let initial_active_issuance = T::active_issuance();
+	let account = AccountId::from(10);
+	let initial_balance = T::minimum_balance() + 10.into();
+	T::mint_into(&account, initial_balance).unwrap();
+
+	// Test: Increase the account balance with set_balance
+	let increase_amount: T::Balance = 5.into();
+	let new = T::set_balance(&account, initial_balance + increase_amount);
+
+	// Verify: set_balance returned the new balance
+	let expected_new = initial_balance + increase_amount;
+	assert_eq!(new, expected_new);
+
+	// Verify: Balance and issuance is updated correctly
+	assert_eq!(T::total_balance(&account), expected_new);
+	assert_eq!(T::balance(&account), expected_new);
+	assert_eq!(T::total_issuance(), initial_total_issuance + expected_new);
+	assert_eq!(T::active_issuance(), initial_active_issuance + expected_new);
+}
+
+/// Test [`Mutate::set_balance`] burns balances correctly.
+///
+/// This test verifies that burning a balance using `set_balance` updates the account balance,
+/// total issuance, and active issuance correctly.
+pub fn set_balance_burn_success<T, AccountId>()
+where
+	T: Mutate<AccountId>,
+	<T as Inspect<AccountId>>::Balance: AtLeast8BitUnsigned + Debug,
+	AccountId: AtLeast8BitUnsigned,
+{
+	let initial_total_issuance = T::total_issuance();
+	let initial_active_issuance = T::active_issuance();
+	let account = AccountId::from(10);
+	let initial_balance = T::minimum_balance() + 10.into();
+	T::mint_into(&account, initial_balance).unwrap();
+
+	// Test: Increase the account balance with set_balance
+	let burn_amount: T::Balance = 5.into();
+	let new = T::set_balance(&account, initial_balance - burn_amount);
+
+	// Verify: set_balance returned the new balance
+	let expected_new = initial_balance - burn_amount;
+	assert_eq!(new, expected_new);
+
+	// Verify: Balance and issuance is updated correctly
+	assert_eq!(T::total_balance(&account), expected_new);
+	assert_eq!(T::balance(&account), expected_new);
+	assert_eq!(T::total_issuance(), initial_total_issuance + expected_new);
+	assert_eq!(T::active_issuance(), initial_active_issuance + expected_new);
+}
+
+/// Test [`Inspect::can_deposit`] works correctly returns [`DepositConsequence::Success`]
+/// when depositing an amount that should succeed.
+pub fn can_deposit_success<T, AccountId>()
+where
+	T: Mutate<AccountId>,
+	<T as Inspect<AccountId>>::Balance: AtLeast8BitUnsigned + Debug,
+	AccountId: AtLeast8BitUnsigned,
+{
+	let account = AccountId::from(10);
+	let initial_balance = T::minimum_balance() + 10.into();
+	T::mint_into(&account, initial_balance).unwrap();
+
+	// Test: can_deposit a reasonable amount
+	let ret = T::can_deposit(&account, 5.into(), Provenance::Minted);
+
+	// Verify: Returns success
+	assert_eq!(ret, DepositConsequence::Success);
+}
+
+/// Test [`Inspect::can_deposit`] returns [`DepositConsequence::BelowMinimum`] when depositing
+/// below the minimum balance.
+pub fn can_deposit_below_minimum<T, AccountId>()
+where
+	T: Mutate<AccountId>,
+	<T as Inspect<AccountId>>::Balance: AtLeast8BitUnsigned + Debug,
+	AccountId: AtLeast8BitUnsigned,
+{
+	// can_deposit always returns Success for amount 0
+	if T::minimum_balance() < 2.into() {
+		return
+	}
+
+	let account = AccountId::from(10);
+
+	// Test: can_deposit below the minimum
+	let ret = T::can_deposit(&account, T::minimum_balance() - 1.into(), Provenance::Minted);
+
+	// Verify: Returns success
+	assert_eq!(ret, DepositConsequence::BelowMinimum);
+}
+
+/// Test [`Inspect::can_deposit`] returns [`DepositConsequence::Overflow`] when
+/// depositing an amount that would overflow.
+pub fn can_deposit_overflow<T, AccountId>()
+where
+	T: Mutate<AccountId>,
+	<T as Inspect<AccountId>>::Balance: AtLeast8BitUnsigned + Debug,
+	AccountId: AtLeast8BitUnsigned,
+{
+	let account = AccountId::from(10);
+
+	// Test: Try deposit over the max balance
+	let initial_balance = T::Balance::max_value() - 5.into() - T::total_issuance();
+	T::mint_into(&account, initial_balance).unwrap();
+	let ret = T::can_deposit(&account, 10.into(), Provenance::Minted);
+
+	// Verify: Returns success
+	assert_eq!(ret, DepositConsequence::Overflow);
+}
+
+/// Test [`Inspect::can_withdraw`] returns [`WithdrawConsequence::Success`] when withdrawing an
+/// amount that should succeed.
+pub fn can_withdraw_success<T, AccountId>()
+where
+	T: Mutate<AccountId>,
+	<T as Inspect<AccountId>>::Balance: AtLeast8BitUnsigned + Debug,
+	AccountId: AtLeast8BitUnsigned,
+{
+	let account = AccountId::from(10);
+	let initial_balance = T::minimum_balance() + 10.into();
+	T::mint_into(&account, initial_balance).unwrap();
+
+	// Test: can_withdraw a reasonable amount
+	let ret = T::can_withdraw(&account, 5.into());
+
+	// Verify: Returns success
+	assert_eq!(ret, WithdrawConsequence::Success);
+}
+
+/// Test [`Inspect::can_withdraw`] returns [`WithdrawConsequence::ReducedToZero`] when
+/// withdrawing an amount that would reduce the account balance below the minimum balance.
+pub fn can_withdraw_reduced_to_zero<T, AccountId>()
+where
+	T: Mutate<AccountId>,
+	<T as Inspect<AccountId>>::Balance: AtLeast8BitUnsigned + Debug,
+	AccountId: AtLeast8BitUnsigned,
+{
+	if T::minimum_balance() == T::Balance::zero() {
+		return
+	}
+
+	let account = AccountId::from(10);
+	let initial_balance = T::minimum_balance();
+	T::mint_into(&account, initial_balance).unwrap();
+
+	// Verify: can_withdraw below the minimum balance returns ReducedToZero
+	let ret = T::can_withdraw(&account, 1.into());
+	assert_eq!(ret, WithdrawConsequence::ReducedToZero(T::minimum_balance() - 1.into()));
+}
+
+/// Test [`Inspect::can_withdraw`] returns [`WithdrawConsequence::BalanceLow`] when withdrawing
+/// an amount that would result in an account balance below the current balance.
+pub fn can_withdraw_balance_low<T, AccountId>()
+where
+	T: Mutate<AccountId>,
+	<T as Inspect<AccountId>>::Balance: AtLeast8BitUnsigned + Debug,
+	AccountId: AtLeast8BitUnsigned,
+{
+	if T::minimum_balance() == T::Balance::zero() {
+		return
+	}
+
+	let account = AccountId::from(10);
+	let other_account = AccountId::from(100);
+	let initial_balance = T::minimum_balance() + 5.into();
+	T::mint_into(&account, initial_balance).unwrap();
+	T::mint_into(&other_account, initial_balance * 2.into()).unwrap();
+
+	// Verify: can_withdraw below the account balance returns BalanceLow
+	let ret = T::can_withdraw(&account, initial_balance + 1.into());
+	assert_eq!(ret, WithdrawConsequence::BalanceLow);
+}
+
+/// Test [`Inspect::reducible_balance`] returns the full account balance when called with
+/// [`Preservation::Expendable`].
+pub fn reducible_balance_expendable<T, AccountId>()
+where
+	T: Mutate<AccountId>,
+	<T as Inspect<AccountId>>::Balance: AtLeast8BitUnsigned + Debug,
+	AccountId: AtLeast8BitUnsigned,
+{
+	let account = AccountId::from(10);
+	let initial_balance = T::minimum_balance() + 10.into();
+	T::mint_into(&account, initial_balance).unwrap();
+
+	// Verify: reducible_balance returns the full balance
+	let ret = T::reducible_balance(&account, Preservation::Expendable, Fortitude::Polite);
+	assert_eq!(ret, initial_balance);
+}
+
+/// Tests [`Inspect::reducible_balance`] returns [`Inspect::balance`] -
+/// [`Inspect::minimum_balance`] when called with either [`Preservation::Protect`] or
+/// [`Preservation::Preserve`].
+pub fn reducible_balance_protect_preserve<T, AccountId>()
+where
+	T: Mutate<AccountId>,
+	<T as Inspect<AccountId>>::Balance: AtLeast8BitUnsigned + Debug,
+	AccountId: AtLeast8BitUnsigned,
+{
+	let account = AccountId::from(10);
+	let initial_balance = T::minimum_balance() + 10.into();
+	T::mint_into(&account, initial_balance).unwrap();
+
+	// Verify: reducible_balance returns the full balance - min balance
+	let ret = T::reducible_balance(&account, Preservation::Protect, Fortitude::Polite);
+	assert_eq!(ret, initial_balance - T::minimum_balance());
+	let ret = T::reducible_balance(&account, Preservation::Preserve, Fortitude::Polite);
+	assert_eq!(ret, initial_balance - T::minimum_balance());
+}
diff --git a/substrate/frame/support/src/traits/tokens/fungible/conformance_tests/regular/unbalanced.rs b/substrate/frame/support/src/traits/tokens/fungible/conformance_tests/regular/unbalanced.rs
new file mode 100644
index 0000000000000000000000000000000000000000..e7fcc15472e05b0600f5bfda2f6a1d524a8afbc3
--- /dev/null
+++ b/substrate/frame/support/src/traits/tokens/fungible/conformance_tests/regular/unbalanced.rs
@@ -0,0 +1,281 @@
+// 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.
+
+use crate::traits::{
+	fungible::{Inspect, Unbalanced},
+	tokens::{Fortitude, Precision, Preservation},
+};
+use core::fmt::Debug;
+use sp_arithmetic::{traits::AtLeast8BitUnsigned, ArithmeticError};
+use sp_runtime::{traits::Bounded, TokenError};
+
+/// Tests [`Unbalanced::write_balance`].
+///
+/// We don't need to test the Error case for this function, because the trait makes no
+/// assumptions about the ways it can fail. That is completely an implementation detail.
+pub fn write_balance<T, AccountId>()
+where
+	T: Unbalanced<AccountId>,
+	<T as Inspect<AccountId>>::Balance: AtLeast8BitUnsigned + Debug,
+	AccountId: AtLeast8BitUnsigned,
+{
+	// Setup some accounts to test varying initial balances
+	let account_0_ed = AccountId::from(0);
+	let account_1_gt_ed = AccountId::from(1);
+	let account_2_empty = AccountId::from(2);
+	T::increase_balance(&account_0_ed, T::minimum_balance(), Precision::Exact).unwrap();
+	T::increase_balance(&account_1_gt_ed, T::minimum_balance() + 5.into(), Precision::Exact)
+		.unwrap();
+
+	// Test setting the balances of each account by gt the minimum balance succeeds with no
+	// dust.
+	let amount = T::minimum_balance() + 10.into();
+	assert_eq!(T::write_balance(&account_0_ed, amount), Ok(None));
+	assert_eq!(T::write_balance(&account_1_gt_ed, amount), Ok(None));
+	assert_eq!(T::write_balance(&account_2_empty, amount), Ok(None));
+	assert_eq!(T::balance(&account_0_ed), amount);
+	assert_eq!(T::balance(&account_1_gt_ed), amount);
+	assert_eq!(T::balance(&account_2_empty), amount);
+
+	// Test setting the balances of each account to below the minimum balance succeeds with
+	// the expected dust.
+	// If the minimum balance is 1, then the dust is 0, represented as None.
+	// If the minimum balance is >1, then the dust is the remaining balance that will be wiped
+	// as the account is reaped.
+	let amount = T::minimum_balance() - 1.into();
+	if T::minimum_balance() == 1.into() {
+		assert_eq!(T::write_balance(&account_0_ed, amount), Ok(None));
+		assert_eq!(T::write_balance(&account_1_gt_ed, amount), Ok(None));
+		assert_eq!(T::write_balance(&account_2_empty, amount), Ok(None));
+	} else if T::minimum_balance() > 1.into() {
+		assert_eq!(T::write_balance(&account_0_ed, amount), Ok(Some(amount)));
+		assert_eq!(T::write_balance(&account_1_gt_ed, amount), Ok(Some(amount)));
+		assert_eq!(T::write_balance(&account_2_empty, amount), Ok(Some(amount)));
+	}
+}
+
+/// Tests [`Unbalanced::decrease_balance`] called with [`Preservation::Expendable`].
+pub fn decrease_balance_expendable<T, AccountId>()
+where
+	T: Unbalanced<AccountId>,
+	<T as Inspect<AccountId>>::Balance: AtLeast8BitUnsigned + Debug,
+	AccountId: AtLeast8BitUnsigned,
+{
+	// Setup account with some balance
+	let account_0 = AccountId::from(0);
+	let account_0_initial_balance = T::minimum_balance() + 10.into();
+	T::increase_balance(&account_0, account_0_initial_balance, Precision::Exact).unwrap();
+
+	// Decreasing the balance still above the minimum balance should not reap the account.
+	let amount = 1.into();
+	assert_eq!(
+		T::decrease_balance(
+			&account_0,
+			amount,
+			Precision::Exact,
+			Preservation::Expendable,
+			Fortitude::Polite,
+		),
+		Ok(amount),
+	);
+	assert_eq!(T::balance(&account_0), account_0_initial_balance - amount);
+
+	// Decreasing the balance below funds avalibale should fail when Precision::Exact
+	let balance_before = T::balance(&account_0);
+	assert_eq!(
+		T::decrease_balance(
+			&account_0,
+			account_0_initial_balance,
+			Precision::Exact,
+			Preservation::Expendable,
+			Fortitude::Polite,
+		),
+		Err(TokenError::FundsUnavailable.into())
+	);
+	// Balance unchanged
+	assert_eq!(T::balance(&account_0), balance_before);
+
+	// And reap the account when Precision::BestEffort
+	assert_eq!(
+		T::decrease_balance(
+			&account_0,
+			account_0_initial_balance,
+			Precision::BestEffort,
+			Preservation::Expendable,
+			Fortitude::Polite,
+		),
+		Ok(balance_before),
+	);
+	// Account reaped
+	assert_eq!(T::balance(&account_0), 0.into());
+}
+
+/// Tests [`Unbalanced::decrease_balance`] called with [`Preservation::Preserve`].
+pub fn decrease_balance_preserve<T, AccountId>()
+where
+	T: Unbalanced<AccountId>,
+	<T as Inspect<AccountId>>::Balance: AtLeast8BitUnsigned + Debug,
+	AccountId: AtLeast8BitUnsigned,
+{
+	// Setup account with some balance
+	let account_0 = AccountId::from(0);
+	let account_0_initial_balance = T::minimum_balance() + 10.into();
+	T::increase_balance(&account_0, account_0_initial_balance, Precision::Exact).unwrap();
+
+	// Decreasing the balance below the minimum when Precision::Exact should fail.
+	let amount = 11.into();
+	assert_eq!(
+		T::decrease_balance(
+			&account_0,
+			amount,
+			Precision::Exact,
+			Preservation::Preserve,
+			Fortitude::Polite,
+		),
+		Err(TokenError::FundsUnavailable.into()),
+	);
+	// Balance should not have changed.
+	assert_eq!(T::balance(&account_0), account_0_initial_balance);
+
+	// Decreasing the balance below the minimum when Precision::BestEffort should reduce to
+	// minimum balance.
+	let amount = 11.into();
+	assert_eq!(
+		T::decrease_balance(
+			&account_0,
+			amount,
+			Precision::BestEffort,
+			Preservation::Preserve,
+			Fortitude::Polite,
+		),
+		Ok(account_0_initial_balance - T::minimum_balance()),
+	);
+	assert_eq!(T::balance(&account_0), T::minimum_balance());
+}
+
+/// Tests [`Unbalanced::increase_balance`].
+pub fn increase_balance<T, AccountId>()
+where
+	T: Unbalanced<AccountId>,
+	<T as Inspect<AccountId>>::Balance: AtLeast8BitUnsigned + Debug,
+	AccountId: AtLeast8BitUnsigned,
+{
+	let account_0 = AccountId::from(0);
+	assert_eq!(T::balance(&account_0), 0.into());
+
+	// Increasing the bal below the ED errors when precision is Exact
+	if T::minimum_balance() > 0.into() {
+		assert_eq!(
+			T::increase_balance(&account_0, T::minimum_balance() - 1.into(), Precision::Exact),
+			Err(TokenError::BelowMinimum.into()),
+		);
+	}
+	assert_eq!(T::balance(&account_0), 0.into());
+
+	// Increasing the bal below the ED leaves the balance at zero when precision is BestEffort
+	if T::minimum_balance() > 0.into() {
+		assert_eq!(
+			T::increase_balance(&account_0, T::minimum_balance() - 1.into(), Precision::BestEffort),
+			Ok(0.into()),
+		);
+	}
+	assert_eq!(T::balance(&account_0), 0.into());
+
+	// Can increase if new bal is >= ED
+	assert_eq!(
+		T::increase_balance(&account_0, T::minimum_balance(), Precision::Exact),
+		Ok(T::minimum_balance()),
+	);
+	assert_eq!(T::balance(&account_0), T::minimum_balance());
+	assert_eq!(T::increase_balance(&account_0, 5.into(), Precision::Exact), Ok(5.into()),);
+	assert_eq!(T::balance(&account_0), T::minimum_balance() + 5.into());
+
+	// Increasing by amount that would overflow fails when precision is Exact
+	assert_eq!(
+		T::increase_balance(&account_0, T::Balance::max_value(), Precision::Exact),
+		Err(ArithmeticError::Overflow.into()),
+	);
+
+	// Increasing by amount that would overflow saturates when precision is BestEffort
+	let balance_before = T::balance(&account_0);
+	assert_eq!(
+		T::increase_balance(&account_0, T::Balance::max_value(), Precision::BestEffort),
+		Ok(T::Balance::max_value() - balance_before),
+	);
+	assert_eq!(T::balance(&account_0), T::Balance::max_value());
+}
+
+/// Tests [`Unbalanced::set_total_issuance`].
+pub fn set_total_issuance<T, AccountId>()
+where
+	T: Unbalanced<AccountId>,
+	<T as Inspect<AccountId>>::Balance: AtLeast8BitUnsigned + Debug,
+	AccountId: AtLeast8BitUnsigned,
+{
+	T::set_total_issuance(1.into());
+	assert_eq!(T::total_issuance(), 1.into());
+
+	T::set_total_issuance(0.into());
+	assert_eq!(T::total_issuance(), 0.into());
+
+	T::set_total_issuance(T::minimum_balance());
+	assert_eq!(T::total_issuance(), T::minimum_balance());
+
+	T::set_total_issuance(T::minimum_balance() + 5.into());
+	assert_eq!(T::total_issuance(), T::minimum_balance() + 5.into());
+
+	if T::minimum_balance() > 0.into() {
+		T::set_total_issuance(T::minimum_balance() - 1.into());
+		assert_eq!(T::total_issuance(), T::minimum_balance() - 1.into());
+	}
+}
+
+/// Tests [`Unbalanced::deactivate`] and [`Unbalanced::reactivate`].
+pub fn deactivate_and_reactivate<T, AccountId>()
+where
+	T: Unbalanced<AccountId>,
+	<T as Inspect<AccountId>>::Balance: AtLeast8BitUnsigned + Debug,
+	AccountId: AtLeast8BitUnsigned,
+{
+	T::set_total_issuance(10.into());
+	assert_eq!(T::total_issuance(), 10.into());
+	assert_eq!(T::active_issuance(), 10.into());
+
+	T::deactivate(2.into());
+	assert_eq!(T::total_issuance(), 10.into());
+	assert_eq!(T::active_issuance(), 8.into());
+
+	// Saturates at total_issuance
+	T::reactivate(4.into());
+	assert_eq!(T::total_issuance(), 10.into());
+	assert_eq!(T::active_issuance(), 10.into());
+
+	// Decrements correctly after saturating at total_issuance
+	T::deactivate(1.into());
+	assert_eq!(T::total_issuance(), 10.into());
+	assert_eq!(T::active_issuance(), 9.into());
+
+	// Saturates at zero
+	T::deactivate(15.into());
+	assert_eq!(T::total_issuance(), 10.into());
+	assert_eq!(T::active_issuance(), 0.into());
+
+	// Increments correctly after saturating at zero
+	T::reactivate(1.into());
+	assert_eq!(T::total_issuance(), 10.into());
+	assert_eq!(T::active_issuance(), 1.into());
+}
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 fe252c6b0893d05ddbdd33d561e0388daddee307..37749d396009d8a11629d3c1f94c6ce5efbe6f21 100644
--- a/substrate/frame/support/src/traits/tokens/fungible/item_of.rs
+++ b/substrate/frame/support/src/traits/tokens/fungible/item_of.rs
@@ -389,9 +389,11 @@ impl<
 		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::from_fungibles(a), imbalance::from_fungibles(b))
+	fn pair(
+		amount: Self::Balance,
+	) -> Result<(Debt<AccountId, Self>, Credit<AccountId, Self>), DispatchError> {
+		let (a, b) = <F as fungibles::Balanced<AccountId>>::pair(A::get(), amount)?;
+		Ok((imbalance::from_fungibles(a), imbalance::from_fungibles(b)))
 	}
 	fn rescind(amount: Self::Balance) -> Debt<AccountId, Self> {
 		let debt = <F as fungibles::Balanced<AccountId>>::rescind(A::get(), amount);
diff --git a/substrate/frame/support/src/traits/tokens/fungible/regular.rs b/substrate/frame/support/src/traits/tokens/fungible/regular.rs
index 87c6ef68d77d6c8ca0b88d650126c7e472126b85..f15c3418d0ac1247690a488ec3972bce2a3fd592 100644
--- a/substrate/frame/support/src/traits/tokens/fungible/regular.rs
+++ b/substrate/frame/support/src/traits/tokens/fungible/regular.rs
@@ -64,7 +64,7 @@ pub trait Inspect<AccountId>: Sized {
 	/// indefinitely.
 	///
 	/// For the amount of the balance which is currently free to be removed from the account without
-	/// error, use `reducible_balance`.
+	/// error, use [`Inspect::reducible_balance`].
 	///
 	/// For the amount of the balance which may eventually be free to be removed from the account,
 	/// use `balance()`.
@@ -74,7 +74,7 @@ pub trait Inspect<AccountId>: Sized {
 	/// subsystems of the chain ("on hold" or "reserved").
 	///
 	/// In general this isn't especially useful outside of tests, and for practical purposes, you'll
-	/// want to use `reducible_balance()`.
+	/// want to use [`Inspect::reducible_balance`].
 	fn balance(who: &AccountId) -> Self::Balance;
 
 	/// Get the maximum amount that `who` can withdraw/transfer successfully based on whether the
@@ -82,7 +82,7 @@ pub trait Inspect<AccountId>: Sized {
 	/// reduction and potentially go below user-level restrictions on the minimum amount of the
 	/// account.
 	///
-	/// Always less than or equal to `balance()`.
+	/// Always less than or equal to [`Inspect::balance`].
 	fn reducible_balance(
 		who: &AccountId,
 		preservation: Preservation,
@@ -106,7 +106,7 @@ pub trait Inspect<AccountId>: Sized {
 	fn can_withdraw(who: &AccountId, amount: Self::Balance) -> WithdrawConsequence<Self::Balance>;
 }
 
-/// Special dust type which can be type-safely converted into a `Credit`.
+/// Special dust type which can be type-safely converted into a [`Credit`].
 #[must_use]
 pub struct Dust<A, T: Inspect<A>>(pub T::Balance);
 
@@ -123,20 +123,20 @@ impl<A, T: Balanced<A>> Dust<A, T> {
 /// Do not use this directly unless you want trouble, since it allows you to alter account balances
 /// without keeping the issuance up to date. It has no safeguards against accidentally creating
 /// token imbalances in your system leading to accidental inflation or deflation. It's really just
-/// for the underlying datatype to implement so the user gets the much safer `Balanced` trait to
+/// for the underlying datatype to implement so the user gets the much safer [`Balanced`] trait to
 /// use.
 pub trait Unbalanced<AccountId>: Inspect<AccountId> {
-	/// Create some dust and handle it with `Self::handle_dust`. This is an unbalanced operation
-	/// and it must only be used when an account is modified in a raw fashion, outside of the entire
-	/// fungibles API. The `amount` is capped at `Self::minimum_balance() - 1`.
+	/// Create some dust and handle it with [`Unbalanced::handle_dust`]. This is an unbalanced
+	/// operation and it must only be used when an account is modified in a raw fashion, outside of
+	/// the entire fungibles API. The `amount` is capped at [`Inspect::minimum_balance()`] - 1`.
 	///
 	/// This should not be reimplemented.
 	fn handle_raw_dust(amount: Self::Balance) {
 		Self::handle_dust(Dust(amount.min(Self::minimum_balance().saturating_sub(One::one()))))
 	}
 
-	/// Do something with the dust which has been destroyed from the system. `Dust` can be converted
-	/// into a `Credit` with the `Balanced` trait impl.
+	/// Do something with the dust which has been destroyed from the system. [`Dust`] can be
+	/// converted into a [`Credit`] with the [`Balanced`] trait impl.
 	fn handle_dust(dust: Dust<AccountId, Self>);
 
 	/// Forcefully set the balance of `who` to `amount`.
@@ -151,9 +151,10 @@ pub trait Unbalanced<AccountId>: Inspect<AccountId> {
 	/// If this cannot be done for some reason (e.g. because the account cannot be created, deleted
 	/// or would overflow) then an `Err` is returned.
 	///
-	/// If `Ok` is returned then its inner, if `Some` is the amount which was discarded as dust due
-	/// to existential deposit requirements. The default implementation of `decrease_balance` and
-	/// `increase_balance` converts this into an `Imbalance` and then passes it into `handle_dust`.
+	/// If `Ok` is returned then its inner, then `Some` is the amount which was discarded as dust
+	/// due to existential deposit requirements. The default implementation of
+	/// [`Unbalanced::decrease_balance`] and [`Unbalanced::increase_balance`] converts this into an
+	/// [`Imbalance`] and then passes it into [`Unbalanced::handle_dust`].
 	fn write_balance(
 		who: &AccountId,
 		amount: Self::Balance,
@@ -164,14 +165,14 @@ pub trait Unbalanced<AccountId>: Inspect<AccountId> {
 
 	/// Reduce the balance of `who` by `amount`.
 	///
-	/// If `precision` is `Exact` and it cannot be reduced by that amount for
-	/// some reason, return `Err` and don't reduce it at all. If `precision` is `BestEffort`, then
+	/// If `precision` is [`Exact`] and it cannot be reduced by that amount for
+	/// some reason, return `Err` and don't reduce it at all. If `precision` is [`BestEffort`], then
 	/// reduce the balance of `who` by the most that is possible, up to `amount`.
 	///
 	/// In either case, if `Ok` is returned then the inner is the amount by which is was reduced.
 	/// Minimum balance will be respected and thus the returned amount may be up to
-	/// `Self::minimum_balance() - 1` greater than `amount` in the case that the reduction caused
-	/// the account to be deleted.
+	/// [`Inspect::minimum_balance()`] - 1` greater than `amount` in the case that the reduction
+	/// caused the account to be deleted.
 	fn decrease_balance(
 		who: &AccountId,
 		mut amount: Self::Balance,
@@ -180,15 +181,10 @@ pub trait Unbalanced<AccountId>: Inspect<AccountId> {
 		force: Fortitude,
 	) -> Result<Self::Balance, DispatchError> {
 		let old_balance = Self::balance(who);
-		let free = Self::reducible_balance(who, preservation, force);
+		let reducible = Self::reducible_balance(who, preservation, force);
 		match precision {
-			BestEffort => {
-				amount = amount.min(free);
-			},
-			Exact =>
-				if free < amount {
-					return Err(TokenError::FundsUnavailable.into())
-				},
+			BestEffort => amount = amount.min(reducible),
+			Exact => ensure!(reducible >= amount, TokenError::FundsUnavailable),
 		}
 
 		let new_balance = old_balance.checked_sub(&amount).ok_or(TokenError::FundsUnavailable)?;
@@ -203,7 +199,7 @@ pub trait Unbalanced<AccountId>: Inspect<AccountId> {
 	/// If it cannot be increased by that amount for some reason, return `Err` and don't increase
 	/// it at all. If Ok, return the imbalance.
 	/// Minimum balance will be respected and an error will be returned if
-	/// `amount < Self::minimum_balance()` when the account of `who` is zero.
+	/// amount < [`Inspect::minimum_balance()`] when the account of `who` is zero.
 	fn increase_balance(
 		who: &AccountId,
 		amount: Self::Balance,
@@ -276,8 +272,8 @@ where
 
 	/// Attempt to decrease the `asset` balance of `who` by `amount`.
 	///
-	/// Equivalent to `burn_from`, except with an expectation that within the bounds of some
-	/// universal issuance, the total assets `suspend`ed and `resume`d will be equivalent. The
+	/// Equivalent to [`Mutate::burn_from`], except with an expectation that within the bounds of
+	/// some universal issuance, the total assets `suspend`ed and `resume`d will be equivalent. The
 	/// implementation may be configured such that the total assets suspended may never be less than
 	/// the total assets resumed (which is the invariant for an issuing system), or the reverse
 	/// (which the invariant in a non-issuing system).
@@ -296,8 +292,8 @@ where
 
 	/// Attempt to increase the `asset` balance of `who` by `amount`.
 	///
-	/// Equivalent to `mint_into`, except with an expectation that within the bounds of some
-	/// universal issuance, the total assets `suspend`ed and `resume`d will be equivalent. The
+	/// Equivalent to [`Mutate::mint_into`], except with an expectation that within the bounds of
+	/// some universal issuance, the total assets `suspend`ed and `resume`d will be equivalent. The
 	/// implementation may be configured such that the total assets suspended may never be less than
 	/// the total assets resumed (which is the invariant for an issuing system), or the reverse
 	/// (which the invariant in a non-issuing system).
@@ -325,7 +321,7 @@ where
 		let _extra = Self::can_withdraw(source, amount).into_result(preservation != Expendable)?;
 		Self::can_deposit(dest, amount, Extant).into_result()?;
 		if source == dest {
-			return Ok(amount)
+			return Ok(amount);
 		}
 
 		Self::decrease_balance(source, amount, BestEffort, preservation, Polite)?;
@@ -383,7 +379,7 @@ impl<AccountId, U: Unbalanced<AccountId>> HandleImbalanceDrop<U::Balance>
 /// A fungible token class where any creation and deletion of tokens is semi-explicit and where the
 /// total supply is maintained automatically.
 ///
-/// This is auto-implemented when a token class has `Unbalanced` implemented.
+/// This is auto-implemented when a token class has [`Unbalanced`] implemented.
 pub trait Balanced<AccountId>: Inspect<AccountId> + Unbalanced<AccountId> {
 	/// The type for managing what happens when an instance of `Debt` is dropped without being used.
 	type OnDropDebt: HandleImbalanceDrop<Self::Balance>;
@@ -392,7 +388,7 @@ pub trait Balanced<AccountId>: Inspect<AccountId> + Unbalanced<AccountId> {
 	type OnDropCredit: HandleImbalanceDrop<Self::Balance>;
 
 	/// Reduce the total issuance by `amount` and return the according imbalance. The imbalance will
-	/// typically be used to reduce an account by the same amount with e.g. `settle`.
+	/// typically be used to reduce an account by the same amount with e.g. [`Balanced::settle`].
 	///
 	/// This is infallible, but doesn't guarantee that the entire `amount` is burnt, for example
 	/// in the case of underflow.
@@ -407,7 +403,7 @@ pub trait Balanced<AccountId>: Inspect<AccountId> + Unbalanced<AccountId> {
 
 	/// Increase the total issuance by `amount` and return the according imbalance. The imbalance
 	/// will typically be used to increase an account by the same amount with e.g.
-	/// `resolve_into_existing` or `resolve_creating`.
+	/// [`Balanced::resolve`].
 	///
 	/// This is infallible, but doesn't guarantee that the entire `amount` is issued, for example
 	/// in the case of overflow.
@@ -424,18 +420,33 @@ pub trait Balanced<AccountId>: Inspect<AccountId> + Unbalanced<AccountId> {
 	///
 	/// This is just the same as burning and issuing the same amount and has no effect on the
 	/// total issuance.
-	fn pair(amount: Self::Balance) -> (Debt<AccountId, Self>, Credit<AccountId, Self>) {
-		(Self::rescind(amount), Self::issue(amount))
+	///
+	/// This could fail when we cannot issue and redeem the entire `amount`, for example in the
+	/// case where the amount would cause overflow or underflow in [`Balanced::issue`] or
+	/// [`Balanced::rescind`].
+	fn pair(
+		amount: Self::Balance,
+	) -> Result<(Debt<AccountId, Self>, Credit<AccountId, Self>), DispatchError> {
+		let issued = Self::issue(amount);
+		let rescinded = Self::rescind(amount);
+		// Need to check amount in case by some edge case both issued and rescinded are below
+		// `amount` by the exact same value
+		if issued.peek() != rescinded.peek() || issued.peek() != amount {
+			// Issued and rescinded will be dropped automatically
+			Err("Failed to issue and rescind equal amounts".into())
+		} else {
+			Ok((rescinded, issued))
+		}
 	}
 
 	/// Mints `value` into the account of `who`, creating it as needed.
 	///
 	/// If `precision` is `BestEffort` and `value` in full could not be minted (e.g. due to
-	/// overflow), then the maximum is minted, up to `value`. If `precision` is `Exact`, then
+	/// overflow), then the maximum is minted, up to `value`. If `precision` is [`Exact`], then
 	/// exactly `value` must be minted into the account of `who` or the operation will fail with an
 	/// `Err` and nothing will change.
 	///
-	/// If the operation is successful, this will return `Ok` with a `Debt` of the total value
+	/// If the operation is successful, this will return `Ok` with a [`Debt`] of the total value
 	/// added to the account.
 	fn deposit(
 		who: &AccountId,
@@ -449,8 +460,8 @@ pub trait Balanced<AccountId>: Inspect<AccountId> + Unbalanced<AccountId> {
 
 	/// Removes `value` balance from `who` account if possible.
 	///
-	/// If `precision` is `BestEffort` and `value` in full could not be removed (e.g. due to
-	/// underflow), then the maximum is removed, up to `value`. If `precision` is `Exact`, then
+	/// If `precision` is [`BestEffort`] and `value` in full could not be removed (e.g. due to
+	/// underflow), then the maximum is removed, up to `value`. If `precision` is [`Exact`], then
 	/// exactly `value` must be removed from the account of `who` or the operation will fail with an
 	/// `Err` and nothing will change.
 	///
@@ -458,7 +469,7 @@ pub trait Balanced<AccountId>: Inspect<AccountId> + Unbalanced<AccountId> {
 	/// If the account needed to be deleted, then slightly more than `value` may be removed from the
 	/// account owning since up to (but not including) minimum balance may also need to be removed.
 	///
-	/// If the operation is successful, this will return `Ok` with a `Credit` of the total value
+	/// If the operation is successful, this will return `Ok` with a [`Credit`] of the total value
 	/// removed from the account.
 	fn withdraw(
 		who: &AccountId,
@@ -476,7 +487,7 @@ pub trait Balanced<AccountId>: Inspect<AccountId> + Unbalanced<AccountId> {
 	/// cannot be countered, then nothing is changed and the original `credit` is returned in an
 	/// `Err`.
 	///
-	/// Please note: If `credit.peek()` is less than `Self::minimum_balance()`, then `who` must
+	/// Please note: If `credit.peek()` is less than [`Inspect::minimum_balance()`], then `who` must
 	/// already exist for this to succeed.
 	fn resolve(
 		who: &AccountId,
@@ -503,7 +514,7 @@ pub trait Balanced<AccountId>: Inspect<AccountId> + Unbalanced<AccountId> {
 		let amount = debt.peek();
 		let credit = match Self::withdraw(who, amount, Exact, preservation, Polite) {
 			Err(_) => return Err(debt),
-			Ok(d) => d,
+			Ok(c) => c,
 		};
 
 		match credit.offset(debt) {
diff --git a/substrate/frame/support/src/traits/tokens/fungible/union_of.rs b/substrate/frame/support/src/traits/tokens/fungible/union_of.rs
index 86505befc05f75c052f8d33f0f495ba6768d71ba..33711d7a16cc6e7fe40eb619f5b4aba4703163df 100644
--- a/substrate/frame/support/src/traits/tokens/fungible/union_of.rs
+++ b/substrate/frame/support/src/traits/tokens/fungible/union_of.rs
@@ -727,21 +727,22 @@ impl<
 	fn pair(
 		asset: Self::AssetId,
 		amount: Self::Balance,
-	) -> (fungibles::Debt<AccountId, Self>, fungibles::Credit<AccountId, Self>) {
+	) -> Result<(fungibles::Debt<AccountId, Self>, fungibles::Credit<AccountId, Self>), DispatchError>
+	{
 		match Criterion::convert(asset.clone()) {
 			Left(()) => {
-				let (a, b) = <Left as fungible::Balanced<AccountId>>::pair(amount);
-				(
+				let (a, b) = <Left as fungible::Balanced<AccountId>>::pair(amount)?;
+				Ok((
 					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);
-				(
+				let (a, b) = <Right as fungibles::Balanced<AccountId>>::pair(a, amount)?;
+				Ok((
 					fungibles::imbalance::from_fungibles(a, asset.clone()),
 					fungibles::imbalance::from_fungibles(b, asset),
-				)
+				))
 			},
 		}
 	}
diff --git a/substrate/frame/support/src/traits/tokens/fungibles/regular.rs b/substrate/frame/support/src/traits/tokens/fungibles/regular.rs
index 41ef4b40c75b389971db43d8013072b269a30a39..8cc97802da66e5fa192a3720cfc6381c8930818f 100644
--- a/substrate/frame/support/src/traits/tokens/fungibles/regular.rs
+++ b/substrate/frame/support/src/traits/tokens/fungibles/regular.rs
@@ -194,9 +194,10 @@ pub trait Unbalanced<AccountId>: Inspect<AccountId> {
 		force: Fortitude,
 	) -> Result<Self::Balance, DispatchError> {
 		let old_balance = Self::balance(asset.clone(), who);
-		let free = Self::reducible_balance(asset.clone(), who, preservation, force);
-		if let BestEffort = precision {
-			amount = amount.min(free);
+		let reducible = Self::reducible_balance(asset.clone(), who, preservation, force);
+		match precision {
+			BestEffort => amount = amount.min(reducible),
+			Exact => ensure!(reducible >= amount, TokenError::FundsUnavailable),
 		}
 		let new_balance = old_balance.checked_sub(&amount).ok_or(TokenError::FundsUnavailable)?;
 		if let Some(dust) = Self::write_balance(asset.clone(), who, new_balance)? {
@@ -478,11 +479,24 @@ pub trait Balanced<AccountId>: Inspect<AccountId> + Unbalanced<AccountId> {
 	///
 	/// This is just the same as burning and issuing the same amount and has no effect on the
 	/// total issuance.
+	///
+	/// This is infallible, but doesn't guarantee that the entire `amount` is used to create the
+	/// pair, for example in the case where the amounts would cause overflow or underflow in
+	/// [`Balanced::issue`] or [`Balanced::rescind`].
 	fn pair(
 		asset: Self::AssetId,
 		amount: Self::Balance,
-	) -> (Debt<AccountId, Self>, Credit<AccountId, Self>) {
-		(Self::rescind(asset.clone(), amount), Self::issue(asset, amount))
+	) -> Result<(Debt<AccountId, Self>, Credit<AccountId, Self>), DispatchError> {
+		let issued = Self::issue(asset.clone(), amount);
+		let rescinded = Self::rescind(asset, amount);
+		// Need to check amount in case by some edge case both issued and rescinded are below
+		// `amount` by the exact same value
+		if issued.peek() != rescinded.peek() || issued.peek() != amount {
+			// Issued and rescinded will be dropped automatically
+			Err("Failed to issue and rescind equal amounts".into())
+		} else {
+			Ok((rescinded, issued))
+		}
 	}
 
 	/// Mints `value` into the account of `who`, creating it as needed.
diff --git a/substrate/frame/support/src/traits/tokens/fungibles/union_of.rs b/substrate/frame/support/src/traits/tokens/fungibles/union_of.rs
index 3619db3a37b63ac527a7038b4c99bbb2c4bed7c5..9d2a783df2a4af0c593c784d4c4d794b0311c392 100644
--- a/substrate/frame/support/src/traits/tokens/fungibles/union_of.rs
+++ b/substrate/frame/support/src/traits/tokens/fungibles/union_of.rs
@@ -706,15 +706,22 @@ impl<
 	fn pair(
 		asset: Self::AssetId,
 		amount: Self::Balance,
-	) -> (fungibles::Debt<AccountId, Self>, fungibles::Credit<AccountId, Self>) {
+	) -> Result<(fungibles::Debt<AccountId, Self>, fungibles::Credit<AccountId, Self>), DispatchError>
+	{
 		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))
+				let (a, b) = <Left as fungibles::Balanced<AccountId>>::pair(a, amount)?;
+				Ok((
+					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))
+				let (a, b) = <Right as fungibles::Balanced<AccountId>>::pair(a, amount)?;
+				Ok((
+					imbalance::from_fungibles(a, asset.clone()),
+					imbalance::from_fungibles(b, asset),
+				))
 			},
 		}
 	}