From 2a0e53d11a497cd426977e537d727be7569e4df7 Mon Sep 17 00:00:00 2001
From: Gavin Wood <gavin@parity.io>
Date: Mon, 5 Dec 2022 13:37:52 +0000
Subject: [PATCH] Non-Interactive Staking (#12610)

* Improve naming.

* More improvements to naming

* Fungible counterpart

* Shared pot instead of reserve

* Transferable receipts

* Better naming

* Use u128 for counterpart

* Partial thawing

* Docs

* Remove AdminOrigin

* Integrate into Kitchen Sink

* Thaw throttling

* Remove todo

* Docs

* Fix benchmarks

* Building

* Tests work

* New benchmarks

* Benchmarking tests

* Test new defensive_saturating_* functions

Signed-off-by: Oliver Tale-Yazdi <oliver.tale-yazdi@parity.io>

* fmt

Signed-off-by: Oliver Tale-Yazdi <oliver.tale-yazdi@parity.io>

* Formatting

* Update frame/nis/src/lib.rs

Co-authored-by: Oliver Tale-Yazdi <oliver.tale-yazdi@parity.io>

* Apply suggestions from code review

Co-authored-by: Oliver Tale-Yazdi <oliver.tale-yazdi@parity.io>

* Events added

* Fix kitchensink

* Update frame/nis/src/lib.rs

Co-authored-by: Xiliang Chen <xlchen1291@gmail.com>

* Review niggles

* Remove genesis build requirements

* Grumbles

* Fixes

* Fixes

* Fixes

* Update frame/nis/src/lib.rs

Co-authored-by: Kian Paimani <5588131+kianenigma@users.noreply.github.com>

* Update primitives/runtime/src/traits.rs

Co-authored-by: Kian Paimani <5588131+kianenigma@users.noreply.github.com>

* Formatting

* Fixes

* Fix node genesis config

Signed-off-by: Oliver Tale-Yazdi <oliver.tale-yazdi@parity.io>

* Fix node chain specs

Signed-off-by: Oliver Tale-Yazdi <oliver.tale-yazdi@parity.io>

* Use free asset ID as counterpart

Signed-off-by: Oliver Tale-Yazdi <oliver.tale-yazdi@parity.io>

* Account for rounding errors in fund_deficit bench

Relaxes the check for the NIS account balance in the fund_deficit bench
from equality from to checking for 99.999% equality. The exact deviation
for the kitchensink runtime config is 1.24e-10 percent but could vary if
the config is changed.

Signed-off-by: Oliver Tale-Yazdi <oliver.tale-yazdi@parity.io>

* clippy

Signed-off-by: Oliver Tale-Yazdi <oliver.tale-yazdi@parity.io>

* fmt

Signed-off-by: Oliver Tale-Yazdi <oliver.tale-yazdi@parity.io>

* Fix

* Rename

* Fixes

* Fixes

* Formatting

Signed-off-by: Oliver Tale-Yazdi <oliver.tale-yazdi@parity.io>
Co-authored-by: Oliver Tale-Yazdi <oliver.tale-yazdi@parity.io>
Co-authored-by: Xiliang Chen <xlchen1291@gmail.com>
Co-authored-by: Kian Paimani <5588131+kianenigma@users.noreply.github.com>
---
 substrate/Cargo.lock                          |  38 +-
 substrate/Cargo.toml                          |   2 +-
 substrate/bin/node/cli/Cargo.toml             |   1 +
 substrate/bin/node/cli/src/chain_spec.rs      |   7 +-
 substrate/bin/node/runtime/Cargo.toml         |   8 +-
 substrate/bin/node/runtime/src/lib.rs         |  41 +-
 substrate/bin/node/testing/Cargo.toml         |   1 +
 substrate/bin/node/testing/src/genesis.rs     |   9 +-
 substrate/frame/gilt/src/benchmarking.rs      | 131 ---
 substrate/frame/gilt/src/lib.rs               | 662 -------------
 substrate/frame/gilt/src/tests.rs             | 573 -----------
 substrate/frame/{gilt => nis}/Cargo.toml      |   5 +-
 substrate/frame/{gilt => nis}/README.md       |   0
 substrate/frame/nis/src/benchmarking.rs       | 182 ++++
 substrate/frame/nis/src/lib.rs                | 936 ++++++++++++++++++
 substrate/frame/{gilt => nis}/src/mock.rs     |  71 +-
 substrate/frame/nis/src/tests.rs              | 654 ++++++++++++
 substrate/frame/{gilt => nis}/src/weights.rs  | 176 ++--
 substrate/frame/support/src/traits.rs         |   2 +-
 substrate/frame/support/src/traits/misc.rs    | 118 ++-
 .../src/traits/tokens/currency/reservable.rs  | 151 ++-
 .../support/src/traits/tokens/fungible.rs     |   2 +-
 substrate/primitives/arithmetic/src/lib.rs    |   4 +-
 .../primitives/arithmetic/src/per_things.rs   | 194 ++--
 substrate/primitives/runtime/src/traits.rs    |  11 +
 25 files changed, 2313 insertions(+), 1666 deletions(-)
 delete mode 100644 substrate/frame/gilt/src/benchmarking.rs
 delete mode 100644 substrate/frame/gilt/src/lib.rs
 delete mode 100644 substrate/frame/gilt/src/tests.rs
 rename substrate/frame/{gilt => nis}/Cargo.toml (93%)
 rename substrate/frame/{gilt => nis}/README.md (100%)
 create mode 100644 substrate/frame/nis/src/benchmarking.rs
 create mode 100644 substrate/frame/nis/src/lib.rs
 rename substrate/frame/{gilt => nis}/src/mock.rs (63%)
 create mode 100644 substrate/frame/nis/src/tests.rs
 rename substrate/frame/{gilt => nis}/src/weights.rs (51%)

diff --git a/substrate/Cargo.lock b/substrate/Cargo.lock
index 64e8cfa82d2..1caffc1d824 100644
--- a/substrate/Cargo.lock
+++ b/substrate/Cargo.lock
@@ -3314,7 +3314,6 @@ dependencies = [
  "pallet-election-provider-support-benchmarking",
  "pallet-elections-phragmen",
  "pallet-fast-unstake",
- "pallet-gilt",
  "pallet-grandpa",
  "pallet-identity",
  "pallet-im-online",
@@ -3323,6 +3322,7 @@ dependencies = [
  "pallet-membership",
  "pallet-mmr",
  "pallet-multisig",
+ "pallet-nis",
  "pallet-nomination-pools",
  "pallet-nomination-pools-benchmarking",
  "pallet-nomination-pools-runtime-api",
@@ -4473,6 +4473,7 @@ dependencies = [
  "node-primitives",
  "node-rpc",
  "pallet-asset-tx-payment",
+ "pallet-assets",
  "pallet-balances",
  "pallet-im-online",
  "pallet-timestamp",
@@ -4731,6 +4732,7 @@ dependencies = [
  "node-executor",
  "node-primitives",
  "pallet-asset-tx-payment",
+ "pallet-assets",
  "pallet-transaction-payment",
  "parity-scale-codec",
  "sc-block-builder",
@@ -5450,23 +5452,6 @@ dependencies = [
  "substrate-test-utils",
 ]
 
-[[package]]
-name = "pallet-gilt"
-version = "4.0.0-dev"
-dependencies = [
- "frame-benchmarking",
- "frame-support",
- "frame-system",
- "pallet-balances",
- "parity-scale-codec",
- "scale-info",
- "sp-arithmetic",
- "sp-core",
- "sp-io",
- "sp-runtime",
- "sp-std",
-]
-
 [[package]]
 name = "pallet-grandpa"
 version = "4.0.0-dev"
@@ -5635,6 +5620,23 @@ dependencies = [
  "sp-std",
 ]
 
+[[package]]
+name = "pallet-nis"
+version = "4.0.0-dev"
+dependencies = [
+ "frame-benchmarking",
+ "frame-support",
+ "frame-system",
+ "pallet-balances",
+ "parity-scale-codec",
+ "scale-info",
+ "sp-arithmetic",
+ "sp-core",
+ "sp-io",
+ "sp-runtime",
+ "sp-std",
+]
+
 [[package]]
 name = "pallet-node-authorization"
 version = "4.0.0-dev"
diff --git a/substrate/Cargo.toml b/substrate/Cargo.toml
index fbe57e03caa..e885f0916ca 100644
--- a/substrate/Cargo.toml
+++ b/substrate/Cargo.toml
@@ -101,7 +101,7 @@ members = [
 	"frame/examples/basic",
 	"frame/examples/offchain-worker",
 	"frame/executive",
-	"frame/gilt",
+	"frame/nis",
 	"frame/grandpa",
 	"frame/identity",
 	"frame/im-online",
diff --git a/substrate/bin/node/cli/Cargo.toml b/substrate/bin/node/cli/Cargo.toml
index 114d324aa15..108923265fb 100644
--- a/substrate/bin/node/cli/Cargo.toml
+++ b/substrate/bin/node/cli/Cargo.toml
@@ -84,6 +84,7 @@ sc-sysinfo = { version = "6.0.0-dev", path = "../../../client/sysinfo" }
 frame-system = { version = "4.0.0-dev", path = "../../../frame/system" }
 frame-system-rpc-runtime-api = { version = "4.0.0-dev", path = "../../../frame/system/rpc/runtime-api" }
 pallet-transaction-payment = { version = "4.0.0-dev", path = "../../../frame/transaction-payment" }
+pallet-assets = { version = "4.0.0-dev", path = "../../../frame/assets/" }
 pallet-asset-tx-payment = { version = "4.0.0-dev", path = "../../../frame/transaction-payment/asset-tx-payment/" }
 pallet-im-online = { version = "4.0.0-dev", default-features = false, path = "../../../frame/im-online" }
 
diff --git a/substrate/bin/node/cli/src/chain_spec.rs b/substrate/bin/node/cli/src/chain_spec.rs
index 8d74f2bde0f..1e4e806fd27 100644
--- a/substrate/bin/node/cli/src/chain_spec.rs
+++ b/substrate/bin/node/cli/src/chain_spec.rs
@@ -358,8 +358,11 @@ pub fn testnet_genesis(
 			max_members: 999,
 		},
 		vesting: Default::default(),
-		assets: Default::default(),
-		gilt: Default::default(),
+		assets: pallet_assets::GenesisConfig {
+			// This asset is used by the NIS pallet as counterpart currency.
+			assets: vec![(9, get_account_id_from_seed::<sr25519::Public>("Alice"), true, 1)],
+			..Default::default()
+		},
 		transaction_storage: Default::default(),
 		transaction_payment: Default::default(),
 		alliance: Default::default(),
diff --git a/substrate/bin/node/runtime/Cargo.toml b/substrate/bin/node/runtime/Cargo.toml
index f812cbe030c..dfddf6a8949 100644
--- a/substrate/bin/node/runtime/Cargo.toml
+++ b/substrate/bin/node/runtime/Cargo.toml
@@ -68,7 +68,7 @@ pallet-election-provider-multi-phase = { version = "4.0.0-dev", default-features
 pallet-election-provider-support-benchmarking = { version = "4.0.0-dev", default-features = false, path = "../../../frame/election-provider-support/benchmarking", optional = true }
 pallet-elections-phragmen = { version = "5.0.0-dev", default-features = false, path = "../../../frame/elections-phragmen" }
 pallet-fast-unstake = { version = "4.0.0-dev", default-features = false, path = "../../../frame/fast-unstake" }
-pallet-gilt = { version = "4.0.0-dev", default-features = false, path = "../../../frame/gilt" }
+pallet-nis = { version = "4.0.0-dev", default-features = false, path = "../../../frame/nis" }
 pallet-grandpa = { version = "4.0.0-dev", default-features = false, path = "../../../frame/grandpa" }
 pallet-im-online = { version = "4.0.0-dev", default-features = false, path = "../../../frame/im-online" }
 pallet-indices = { version = "4.0.0-dev", default-features = false, path = "../../../frame/indices" }
@@ -144,7 +144,7 @@ std = [
 	"pallet-elections-phragmen/std",
 	"pallet-fast-unstake/std",
 	"frame-executive/std",
-	"pallet-gilt/std",
+	"pallet-nis/std",
 	"pallet-grandpa/std",
 	"pallet-im-online/std",
 	"pallet-indices/std",
@@ -223,7 +223,7 @@ runtime-benchmarks = [
 	"pallet-election-provider-support-benchmarking/runtime-benchmarks",
 	"pallet-elections-phragmen/runtime-benchmarks",
 	"pallet-fast-unstake/runtime-benchmarks",
-	"pallet-gilt/runtime-benchmarks",
+	"pallet-nis/runtime-benchmarks",
 	"pallet-grandpa/runtime-benchmarks",
 	"pallet-identity/runtime-benchmarks",
 	"pallet-im-online/runtime-benchmarks",
@@ -276,7 +276,7 @@ try-runtime = [
 	"pallet-election-provider-multi-phase/try-runtime",
 	"pallet-elections-phragmen/try-runtime",
 	"pallet-fast-unstake/try-runtime",
-	"pallet-gilt/try-runtime",
+	"pallet-nis/try-runtime",
 	"pallet-grandpa/try-runtime",
 	"pallet-im-online/try-runtime",
 	"pallet-indices/try-runtime",
diff --git a/substrate/bin/node/runtime/src/lib.rs b/substrate/bin/node/runtime/src/lib.rs
index 215b02bcca9..633106e10b6 100644
--- a/substrate/bin/node/runtime/src/lib.rs
+++ b/substrate/bin/node/runtime/src/lib.rs
@@ -32,9 +32,10 @@ use frame_support::{
 	pallet_prelude::Get,
 	parameter_types,
 	traits::{
-		AsEnsureOriginWithArg, ConstBool, ConstU128, ConstU16, ConstU32, Currency, EitherOfDiverse,
-		EqualPrivilegeOnly, Everything, Imbalance, InstanceFilter, KeyOwnerProofSystem,
-		LockIdentifier, Nothing, OnUnbalanced, U128CurrencyToVote, WithdrawReasons,
+		fungible::ItemOf, AsEnsureOriginWithArg, ConstBool, ConstU128, ConstU16, ConstU32,
+		Currency, EitherOfDiverse, EqualPrivilegeOnly, Everything, Imbalance, InstanceFilter,
+		KeyOwnerProofSystem, LockIdentifier, Nothing, OnUnbalanced, U128CurrencyToVote,
+		WithdrawReasons,
 	},
 	weights::{
 		constants::{BlockExecutionWeight, ExtrinsicBaseWeight, RocksDbWeight, WEIGHT_PER_SECOND},
@@ -53,6 +54,7 @@ use pallet_grandpa::{
 	fg_primitives, AuthorityId as GrandpaId, AuthorityList as GrandpaAuthorityList,
 };
 use pallet_im_online::sr25519::AuthorityId as ImOnlineId;
+use pallet_nis::WithMaximumOf;
 use pallet_session::historical::{self as pallet_session_historical};
 pub use pallet_transaction_payment::{CurrencyAdapter, Multiplier, TargetedFeeAdjustment};
 use pallet_transaction_payment::{FeeDetails, RuntimeDispatchInfo};
@@ -1464,28 +1466,37 @@ parameter_types! {
 	pub const QueueCount: u32 = 300;
 	pub const MaxQueueLen: u32 = 1000;
 	pub const FifoQueueLen: u32 = 500;
-	pub const Period: BlockNumber = 30 * DAYS;
-	pub const MinFreeze: Balance = 100 * DOLLARS;
+	pub const NisBasePeriod: BlockNumber = 30 * DAYS;
+	pub const MinBid: Balance = 100 * DOLLARS;
+	pub const MinReceipt: Perquintill = Perquintill::from_percent(1);
 	pub const IntakePeriod: BlockNumber = 10;
-	pub const MaxIntakeBids: u32 = 10;
+	pub MaxIntakeWeight: Weight = MAXIMUM_BLOCK_WEIGHT / 10;
+	pub const ThawThrottle: (Perquintill, BlockNumber) = (Perquintill::from_percent(25), 5);
+	pub Target: Perquintill = Perquintill::zero();
+	pub const NisPalletId: PalletId = PalletId(*b"py/nis  ");
 }
 
-impl pallet_gilt::Config for Runtime {
+impl pallet_nis::Config for Runtime {
+	type WeightInfo = pallet_nis::weights::SubstrateWeight<Runtime>;
 	type RuntimeEvent = RuntimeEvent;
 	type Currency = Balances;
 	type CurrencyBalance = Balance;
-	type AdminOrigin = frame_system::EnsureRoot<AccountId>;
+	type FundOrigin = frame_system::EnsureSigned<AccountId>;
+	type Counterpart = ItemOf<Assets, ConstU32<9u32>, AccountId>;
+	type CounterpartAmount = WithMaximumOf<ConstU128<21_000_000_000_000_000_000u128>>;
 	type Deficit = ();
-	type Surplus = ();
 	type IgnoredIssuance = IgnoredIssuance;
+	type Target = Target;
+	type PalletId = NisPalletId;
 	type QueueCount = QueueCount;
 	type MaxQueueLen = MaxQueueLen;
 	type FifoQueueLen = FifoQueueLen;
-	type Period = Period;
-	type MinFreeze = MinFreeze;
+	type BasePeriod = NisBasePeriod;
+	type MinBid = MinBid;
+	type MinReceipt = MinReceipt;
 	type IntakePeriod = IntakePeriod;
-	type MaxIntakeBids = MaxIntakeBids;
-	type WeightInfo = pallet_gilt::weights::SubstrateWeight<Runtime>;
+	type MaxIntakeWeight = MaxIntakeWeight;
+	type ThawThrottle = ThawThrottle;
 }
 
 parameter_types! {
@@ -1668,7 +1679,7 @@ construct_runtime!(
 		Assets: pallet_assets,
 		Mmr: pallet_mmr,
 		Lottery: pallet_lottery,
-		Gilt: pallet_gilt,
+		Nis: pallet_nis,
 		Uniques: pallet_uniques,
 		TransactionStorage: pallet_transaction_storage,
 		VoterList: pallet_bags_list::<Instance1>,
@@ -1772,7 +1783,7 @@ mod benches {
 		[pallet_election_provider_support_benchmarking, EPSBench::<Runtime>]
 		[pallet_elections_phragmen, Elections]
 		[pallet_fast_unstake, FastUnstake]
-		[pallet_gilt, Gilt]
+		[pallet_nis, Nis]
 		[pallet_grandpa, Grandpa]
 		[pallet_identity, Identity]
 		[pallet_im_online, ImOnline]
diff --git a/substrate/bin/node/testing/Cargo.toml b/substrate/bin/node/testing/Cargo.toml
index a2b34cf59b1..69447212364 100644
--- a/substrate/bin/node/testing/Cargo.toml
+++ b/substrate/bin/node/testing/Cargo.toml
@@ -22,6 +22,7 @@ frame-system = { version = "4.0.0-dev", path = "../../../frame/system" }
 node-executor = { version = "3.0.0-dev", path = "../executor" }
 node-primitives = { version = "2.0.0", path = "../primitives" }
 kitchensink-runtime = { version = "3.0.0-dev", path = "../runtime" }
+pallet-assets = { version = "4.0.0-dev", path = "../../../frame/assets" }
 pallet-asset-tx-payment = { version = "4.0.0-dev", path = "../../../frame/transaction-payment/asset-tx-payment" }
 pallet-transaction-payment = { version = "4.0.0-dev", path = "../../../frame/transaction-payment" }
 sc-block-builder = { version = "0.10.0-dev", path = "../../../client/block-builder" }
diff --git a/substrate/bin/node/testing/src/genesis.rs b/substrate/bin/node/testing/src/genesis.rs
index 1eb7318db52..b207fc7f98a 100644
--- a/substrate/bin/node/testing/src/genesis.rs
+++ b/substrate/bin/node/testing/src/genesis.rs
@@ -20,9 +20,9 @@
 
 use crate::keyring::*;
 use kitchensink_runtime::{
-	constants::currency::*, wasm_binary_unwrap, AccountId, BabeConfig, BalancesConfig,
-	GenesisConfig, GrandpaConfig, IndicesConfig, SessionConfig, SocietyConfig, StakerStatus,
-	StakingConfig, SystemConfig, BABE_GENESIS_EPOCH_CONFIG,
+	constants::currency::*, wasm_binary_unwrap, AccountId, AssetsConfig, BabeConfig,
+	BalancesConfig, GenesisConfig, GrandpaConfig, IndicesConfig, SessionConfig, SocietyConfig,
+	StakerStatus, StakingConfig, SystemConfig, BABE_GENESIS_EPOCH_CONFIG,
 };
 use sp_keyring::{Ed25519Keyring, Sr25519Keyring};
 use sp_runtime::Perbill;
@@ -88,8 +88,7 @@ pub fn config_endowed(code: Option<&[u8]>, extra_endowed: Vec<AccountId>) -> Gen
 		treasury: Default::default(),
 		society: SocietyConfig { members: vec![alice(), bob()], pot: 0, max_members: 999 },
 		vesting: Default::default(),
-		assets: Default::default(),
-		gilt: Default::default(),
+		assets: AssetsConfig { assets: vec![(9, alice(), true, 1)], ..Default::default() },
 		transaction_storage: Default::default(),
 		transaction_payment: Default::default(),
 		alliance: Default::default(),
diff --git a/substrate/frame/gilt/src/benchmarking.rs b/substrate/frame/gilt/src/benchmarking.rs
deleted file mode 100644
index 92ebf81854f..00000000000
--- a/substrate/frame/gilt/src/benchmarking.rs
+++ /dev/null
@@ -1,131 +0,0 @@
-// This file is part of Substrate.
-
-// Copyright (C) 2021-2022 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.
-
-//! Benchmarks for Gilt Pallet
-
-#![cfg(feature = "runtime-benchmarks")]
-
-use super::*;
-use frame_benchmarking::{benchmarks, whitelisted_caller};
-use frame_support::{
-	dispatch::UnfilteredDispatchable,
-	traits::{Currency, EnsureOrigin, Get},
-};
-use frame_system::RawOrigin;
-use sp_arithmetic::Perquintill;
-use sp_runtime::traits::{Bounded, Zero};
-use sp_std::prelude::*;
-
-use crate::Pallet as Gilt;
-
-type BalanceOf<T> =
-	<<T as Config>::Currency as Currency<<T as frame_system::Config>::AccountId>>::Balance;
-
-benchmarks! {
-	place_bid {
-		let l in 0..(T::MaxQueueLen::get() - 1);
-		let caller: T::AccountId = whitelisted_caller();
-		T::Currency::make_free_balance_be(&caller, BalanceOf::<T>::max_value());
-		for i in 0..l {
-			Gilt::<T>::place_bid(RawOrigin::Signed(caller.clone()).into(), T::MinFreeze::get(), 1)?;
-		}
-	}: _(RawOrigin::Signed(caller.clone()), T::MinFreeze::get() * BalanceOf::<T>::from(2u32), 1)
-	verify {
-		assert_eq!(QueueTotals::<T>::get()[0], (l + 1, T::MinFreeze::get() * BalanceOf::<T>::from(l + 2)));
-	}
-
-	place_bid_max {
-		let caller: T::AccountId = whitelisted_caller();
-		let origin = RawOrigin::Signed(caller.clone());
-		T::Currency::make_free_balance_be(&caller, BalanceOf::<T>::max_value());
-		for i in 0..T::MaxQueueLen::get() {
-			Gilt::<T>::place_bid(origin.clone().into(), T::MinFreeze::get(), 1)?;
-		}
-	}: place_bid(origin, T::MinFreeze::get() * BalanceOf::<T>::from(2u32), 1)
-	verify {
-		assert_eq!(QueueTotals::<T>::get()[0], (
-			T::MaxQueueLen::get(),
-			T::MinFreeze::get() * BalanceOf::<T>::from(T::MaxQueueLen::get() + 1),
-		));
-	}
-
-	retract_bid {
-		let l in 1..T::MaxQueueLen::get();
-		let caller: T::AccountId = whitelisted_caller();
-		T::Currency::make_free_balance_be(&caller, BalanceOf::<T>::max_value());
-		for i in 0..l {
-			Gilt::<T>::place_bid(RawOrigin::Signed(caller.clone()).into(), T::MinFreeze::get(), 1)?;
-		}
-	}: _(RawOrigin::Signed(caller.clone()), T::MinFreeze::get(), 1)
-	verify {
-		assert_eq!(QueueTotals::<T>::get()[0], (l - 1, T::MinFreeze::get() * BalanceOf::<T>::from(l - 1)));
-	}
-
-	set_target {
-		let origin = T::AdminOrigin::successful_origin();
-	}: _<T::RuntimeOrigin>(origin, Default::default())
-	verify {}
-
-	thaw {
-		let caller: T::AccountId = whitelisted_caller();
-		T::Currency::make_free_balance_be(&caller, T::MinFreeze::get() * BalanceOf::<T>::from(3u32));
-		Gilt::<T>::place_bid(RawOrigin::Signed(caller.clone()).into(), T::MinFreeze::get(), 1)?;
-		Gilt::<T>::place_bid(RawOrigin::Signed(caller.clone()).into(), T::MinFreeze::get(), 1)?;
-		Gilt::<T>::enlarge(T::MinFreeze::get() * BalanceOf::<T>::from(2u32), 2);
-		Active::<T>::mutate(0, |m_g| if let Some(ref mut g) = m_g { g.expiry = Zero::zero() });
-	}: _(RawOrigin::Signed(caller.clone()), 0)
-	verify {
-		assert!(Active::<T>::get(0).is_none());
-	}
-
-	pursue_target_noop {
-	}: { Gilt::<T>::pursue_target(0) }
-
-	pursue_target_per_item {
-		// bids taken
-		let b in 0..T::MaxQueueLen::get();
-
-		let caller: T::AccountId = whitelisted_caller();
-		T::Currency::make_free_balance_be(&caller, T::MinFreeze::get() * BalanceOf::<T>::from(b + 1));
-
-		for _ in 0..b {
-			Gilt::<T>::place_bid(RawOrigin::Signed(caller.clone()).into(), T::MinFreeze::get(), 1)?;
-		}
-
-		Call::<T>::set_target { target: Perquintill::from_percent(100) }
-			.dispatch_bypass_filter(T::AdminOrigin::successful_origin())?;
-
-	}: { Gilt::<T>::pursue_target(b) }
-
-	pursue_target_per_queue {
-		// total queues hit
-		let q in 0..T::QueueCount::get();
-
-		let caller: T::AccountId = whitelisted_caller();
-		T::Currency::make_free_balance_be(&caller, T::MinFreeze::get() * BalanceOf::<T>::from(q + 1));
-
-		for i in 0..q {
-			Gilt::<T>::place_bid(RawOrigin::Signed(caller.clone()).into(), T::MinFreeze::get(), i + 1)?;
-		}
-
-		Call::<T>::set_target { target: Perquintill::from_percent(100) }
-			.dispatch_bypass_filter(T::AdminOrigin::successful_origin())?;
-
-	}: { Gilt::<T>::pursue_target(q) }
-
-	impl_benchmark_test_suite!(Gilt, crate::mock::new_test_ext(), crate::mock::Test);
-}
diff --git a/substrate/frame/gilt/src/lib.rs b/substrate/frame/gilt/src/lib.rs
deleted file mode 100644
index 28a0f5fd56e..00000000000
--- a/substrate/frame/gilt/src/lib.rs
+++ /dev/null
@@ -1,662 +0,0 @@
-// This file is part of Substrate.
-
-// Copyright (C) 2019-2022 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.
-
-//! # Gilt Pallet
-//! A pallet allowing accounts to auction for being frozen and receive open-ended
-//! inflation-protection in return.
-//!
-//! ## Overview
-//!
-//! Lock up tokens, for at least as long as you offer, and be free from both inflation and
-//! intermediate reward or exchange until the tokens become unlocked.
-//!
-//! ## Design
-//!
-//! Queues for each of 1-`QueueCount` periods, given in blocks (`Period`). Queues are limited in
-//! size to something sensible, `MaxQueueLen`. A secondary storage item with `QueueCount` x `u32`
-//! elements with the number of items in each queue.
-//!
-//! Queues are split into two parts. The first part is a priority queue based on bid size. The
-//! second part is just a FIFO (the size of the second part is set with `FifoQueueLen`). Items are
-//! always prepended so that removal is always O(1) since removal often happens many times under a
-//! single weighed function (`on_initialize`) yet placing bids only ever happens once per weighed
-//! function (`place_bid`). If the queue has a priority portion, then it remains sorted in order of
-//! bid size so that smaller bids fall off as it gets too large.
-//!
-//! Account may enqueue a balance with some number of `Period`s lock up, up to a maximum of
-//! `QueueCount`. The balance gets reserved. There's a minimum of `MinFreeze` to avoid dust.
-//!
-//! Until your bid is turned into an issued gilt you can retract it instantly and the funds are
-//! unreserved.
-//!
-//! There's a target proportion of effective total issuance (i.e. accounting for existing gilts)
-//! which the we attempt to have frozen at any one time. It will likely be gradually increased over
-//! time by governance.
-//!
-//! As the total funds frozen under gilts drops below `FrozenFraction` of the total effective
-//! issuance, then bids are taken from queues, with the queue of the greatest period taking
-//! priority. If the item in the queue's locked amount is greater than the amount left to be
-//! frozen, then it is split up into multiple bids and becomes partially frozen under gilt.
-//!
-//! Once an account's balance is frozen, it remains frozen until the owner thaws the balance of the
-//! account. This may happen no earlier than queue's period after the point at which the gilt is
-//! issued.
-//!
-//! ## Suggested Values
-//!
-//! - `QueueCount`: 300
-//! - `Period`: 432,000
-//! - `MaxQueueLen`: 1000
-//! - `MinFreeze`: Around CHF 100 in value.
-
-#![cfg_attr(not(feature = "std"), no_std)]
-
-pub use pallet::*;
-
-mod benchmarking;
-#[cfg(test)]
-mod mock;
-#[cfg(test)]
-mod tests;
-pub mod weights;
-
-#[frame_support::pallet]
-pub mod pallet {
-	pub use crate::weights::WeightInfo;
-	use frame_support::{
-		pallet_prelude::*,
-		traits::{Currency, DefensiveSaturating, OnUnbalanced, ReservableCurrency},
-	};
-	use frame_system::pallet_prelude::*;
-	use sp_arithmetic::{PerThing, Perquintill};
-	use sp_runtime::traits::{Saturating, Zero};
-	use sp_std::prelude::*;
-
-	type BalanceOf<T> =
-		<<T as Config>::Currency as Currency<<T as frame_system::Config>::AccountId>>::Balance;
-	type PositiveImbalanceOf<T> = <<T as Config>::Currency as Currency<
-		<T as frame_system::Config>::AccountId,
-	>>::PositiveImbalance;
-	type NegativeImbalanceOf<T> = <<T as Config>::Currency as Currency<
-		<T as frame_system::Config>::AccountId,
-	>>::NegativeImbalance;
-
-	#[pallet::config]
-	pub trait Config: frame_system::Config {
-		/// Overarching event type.
-		type RuntimeEvent: From<Event<Self>> + IsType<<Self as frame_system::Config>::RuntimeEvent>;
-
-		/// Currency type that this works on.
-		type Currency: ReservableCurrency<Self::AccountId, Balance = Self::CurrencyBalance>;
-
-		/// Just the `Currency::Balance` type; we have this item to allow us to constrain it to
-		/// `From<u64>`.
-		type CurrencyBalance: sp_runtime::traits::AtLeast32BitUnsigned
-			+ codec::FullCodec
-			+ Copy
-			+ MaybeSerializeDeserialize
-			+ sp_std::fmt::Debug
-			+ Default
-			+ From<u64>
-			+ TypeInfo
-			+ MaxEncodedLen;
-
-		/// Origin required for setting the target proportion to be under gilt.
-		type AdminOrigin: EnsureOrigin<Self::RuntimeOrigin>;
-
-		/// Unbalanced handler to account for funds created (in case of a higher total issuance over
-		/// freezing period).
-		type Deficit: OnUnbalanced<PositiveImbalanceOf<Self>>;
-
-		/// Unbalanced handler to account for funds destroyed (in case of a lower total issuance
-		/// over freezing period).
-		type Surplus: OnUnbalanced<NegativeImbalanceOf<Self>>;
-
-		/// The issuance to ignore. This is subtracted from the `Currency`'s `total_issuance` to get
-		/// the issuance by which we inflate or deflate the gilt.
-		type IgnoredIssuance: Get<BalanceOf<Self>>;
-
-		/// Number of duration queues in total. This sets the maximum duration supported, which is
-		/// this value multiplied by `Period`.
-		#[pallet::constant]
-		type QueueCount: Get<u32>;
-
-		/// Maximum number of items that may be in each duration queue.
-		#[pallet::constant]
-		type MaxQueueLen: Get<u32>;
-
-		/// Portion of the queue which is free from ordering and just a FIFO.
-		///
-		/// Must be no greater than `MaxQueueLen`.
-		#[pallet::constant]
-		type FifoQueueLen: Get<u32>;
-
-		/// The base period for the duration queues. This is the common multiple across all
-		/// supported freezing durations that can be bid upon.
-		#[pallet::constant]
-		type Period: Get<Self::BlockNumber>;
-
-		/// The minimum amount of funds that may be offered to freeze for a gilt. Note that this
-		/// does not actually limit the amount which may be frozen in a gilt since gilts may be
-		/// split up in order to satisfy the desired amount of funds under gilts.
-		///
-		/// It should be at least big enough to ensure that there is no possible storage spam attack
-		/// or queue-filling attack.
-		#[pallet::constant]
-		type MinFreeze: Get<BalanceOf<Self>>;
-
-		/// The number of blocks between consecutive attempts to issue more gilts in an effort to
-		/// get to the target amount to be frozen.
-		///
-		/// A larger value results in fewer storage hits each block, but a slower period to get to
-		/// the target.
-		#[pallet::constant]
-		type IntakePeriod: Get<Self::BlockNumber>;
-
-		/// The maximum amount of bids that can be turned into issued gilts each block. A larger
-		/// value here means less of the block available for transactions should there be a glut of
-		/// bids to make into gilts to reach the target.
-		#[pallet::constant]
-		type MaxIntakeBids: Get<u32>;
-
-		/// Information on runtime weights.
-		type WeightInfo: WeightInfo;
-	}
-
-	#[pallet::pallet]
-	#[pallet::generate_store(pub(super) trait Store)]
-	pub struct Pallet<T>(_);
-
-	/// A single bid on a gilt, an item of a *queue* in `Queues`.
-	#[derive(
-		Clone, Eq, PartialEq, Default, Encode, Decode, RuntimeDebug, TypeInfo, MaxEncodedLen,
-	)]
-	pub struct GiltBid<Balance, AccountId> {
-		/// The amount bid.
-		pub amount: Balance,
-		/// The owner of the bid.
-		pub who: AccountId,
-	}
-
-	/// Information representing an active gilt.
-	#[derive(
-		Clone, Eq, PartialEq, Default, Encode, Decode, RuntimeDebug, TypeInfo, MaxEncodedLen,
-	)]
-	pub struct ActiveGilt<Balance, AccountId, BlockNumber> {
-		/// The proportion of the effective total issuance (i.e. accounting for any eventual gilt
-		/// expansion or contraction that may eventually be claimed).
-		pub proportion: Perquintill,
-		/// The amount reserved under this gilt.
-		pub amount: Balance,
-		/// The account to whom this gilt belongs.
-		pub who: AccountId,
-		/// The time after which this gilt can be redeemed for the proportional amount of balance.
-		pub expiry: BlockNumber,
-	}
-
-	/// An index for a gilt.
-	pub type ActiveIndex = u32;
-
-	/// Overall information package on the active gilts.
-	///
-	/// The way of determining the net issuance (i.e. after factoring in all maturing frozen funds)
-	/// is:
-	///
-	/// `issuance - frozen + proportion * issuance`
-	///
-	/// where `issuance = total_issuance - IgnoredIssuance`
-	#[derive(
-		Clone, Eq, PartialEq, Default, Encode, Decode, RuntimeDebug, TypeInfo, MaxEncodedLen,
-	)]
-	pub struct ActiveGiltsTotal<Balance> {
-		/// The total amount of funds held in reserve for all active gilts.
-		pub frozen: Balance,
-		/// The proportion of funds that the `frozen` balance represents to total issuance.
-		pub proportion: Perquintill,
-		/// The total number of gilts issued so far.
-		pub index: ActiveIndex,
-		/// The target proportion of gilts within total issuance.
-		pub target: Perquintill,
-	}
-
-	/// The totals of items and balances within each queue. Saves a lot of storage reads in the
-	/// case of sparsely packed queues.
-	///
-	/// The vector is indexed by duration in `Period`s, offset by one, so information on the queue
-	/// whose duration is one `Period` would be storage `0`.
-	#[pallet::storage]
-	pub type QueueTotals<T: Config> =
-		StorageValue<_, BoundedVec<(u32, BalanceOf<T>), T::QueueCount>, ValueQuery>;
-
-	/// The queues of bids ready to become gilts. Indexed by duration (in `Period`s).
-	#[pallet::storage]
-	pub type Queues<T: Config> = StorageMap<
-		_,
-		Blake2_128Concat,
-		u32,
-		BoundedVec<GiltBid<BalanceOf<T>, T::AccountId>, T::MaxQueueLen>,
-		ValueQuery,
-	>;
-
-	/// Information relating to the gilts currently active.
-	#[pallet::storage]
-	pub type ActiveTotal<T> = StorageValue<_, ActiveGiltsTotal<BalanceOf<T>>, ValueQuery>;
-
-	/// The currently active gilts, indexed according to the order of creation.
-	#[pallet::storage]
-	pub type Active<T> = StorageMap<
-		_,
-		Blake2_128Concat,
-		ActiveIndex,
-		ActiveGilt<
-			BalanceOf<T>,
-			<T as frame_system::Config>::AccountId,
-			<T as frame_system::Config>::BlockNumber,
-		>,
-		OptionQuery,
-	>;
-
-	#[pallet::genesis_config]
-	#[derive(Default)]
-	pub struct GenesisConfig;
-
-	#[pallet::genesis_build]
-	impl<T: Config> GenesisBuild<T> for GenesisConfig {
-		fn build(&self) {
-			let unbounded = vec![(0, BalanceOf::<T>::zero()); T::QueueCount::get() as usize];
-			let bounded: BoundedVec<_, _> = unbounded
-				.try_into()
-				.expect("QueueTotals should support up to QueueCount items. qed");
-			QueueTotals::<T>::put(bounded);
-		}
-	}
-
-	#[pallet::event]
-	#[pallet::generate_deposit(pub(super) fn deposit_event)]
-	pub enum Event<T: Config> {
-		/// A bid was successfully placed.
-		BidPlaced { who: T::AccountId, amount: BalanceOf<T>, duration: u32 },
-		/// A bid was successfully removed (before being accepted as a gilt).
-		BidRetracted { who: T::AccountId, amount: BalanceOf<T>, duration: u32 },
-		/// A bid was accepted as a gilt. The balance may not be released until expiry.
-		GiltIssued {
-			index: ActiveIndex,
-			expiry: T::BlockNumber,
-			who: T::AccountId,
-			amount: BalanceOf<T>,
-		},
-		/// An expired gilt has been thawed.
-		GiltThawed {
-			index: ActiveIndex,
-			who: T::AccountId,
-			original_amount: BalanceOf<T>,
-			additional_amount: BalanceOf<T>,
-		},
-	}
-
-	#[pallet::error]
-	pub enum Error<T> {
-		/// The duration of the bid is less than one.
-		DurationTooSmall,
-		/// The duration is the bid is greater than the number of queues.
-		DurationTooBig,
-		/// The amount of the bid is less than the minimum allowed.
-		AmountTooSmall,
-		/// The queue for the bid's duration is full and the amount bid is too low to get in
-		/// through replacing an existing bid.
-		BidTooLow,
-		/// Gilt index is unknown.
-		Unknown,
-		/// Not the owner of the gilt.
-		NotOwner,
-		/// Gilt not yet at expiry date.
-		NotExpired,
-		/// The given bid for retraction is not found.
-		NotFound,
-	}
-
-	#[pallet::hooks]
-	impl<T: Config> Hooks<BlockNumberFor<T>> for Pallet<T> {
-		fn on_initialize(n: T::BlockNumber) -> Weight {
-			if (n % T::IntakePeriod::get()).is_zero() {
-				Self::pursue_target(T::MaxIntakeBids::get())
-			} else {
-				Weight::zero()
-			}
-		}
-	}
-
-	#[pallet::call]
-	impl<T: Config> Pallet<T> {
-		/// Place a bid for a gilt to be issued.
-		///
-		/// Origin must be Signed, and account must have at least `amount` in free balance.
-		///
-		/// - `amount`: The amount of the bid; these funds will be reserved. If the bid is
-		/// successfully elevated into an issued gilt, then these funds will continue to be
-		/// reserved until the gilt expires. Must be at least `MinFreeze`.
-		/// - `duration`: The number of periods for which the funds will be locked if the gilt is
-		/// issued. It will expire only after this period has elapsed after the point of issuance.
-		/// Must be greater than 1 and no more than `QueueCount`.
-		///
-		/// Complexities:
-		/// - `Queues[duration].len()` (just take max).
-		#[pallet::weight(T::WeightInfo::place_bid_max())]
-		pub fn place_bid(
-			origin: OriginFor<T>,
-			#[pallet::compact] amount: BalanceOf<T>,
-			duration: u32,
-		) -> DispatchResultWithPostInfo {
-			let who = ensure_signed(origin)?;
-
-			ensure!(amount >= T::MinFreeze::get(), Error::<T>::AmountTooSmall);
-			let queue_count = T::QueueCount::get() as usize;
-			let queue_index = duration.checked_sub(1).ok_or(Error::<T>::DurationTooSmall)? as usize;
-			ensure!(queue_index < queue_count, Error::<T>::DurationTooBig);
-
-			let net = Queues::<T>::try_mutate(
-				duration,
-				|q| -> Result<(u32, BalanceOf<T>), DispatchError> {
-					let queue_full = q.len() == T::MaxQueueLen::get() as usize;
-					ensure!(!queue_full || q[0].amount < amount, Error::<T>::BidTooLow);
-					T::Currency::reserve(&who, amount)?;
-
-					// queue is <Ordered: Lowest ... Highest><Fifo: Last ... First>
-					let mut bid = GiltBid { amount, who: who.clone() };
-					let net = if queue_full {
-						sp_std::mem::swap(&mut q[0], &mut bid);
-						T::Currency::unreserve(&bid.who, bid.amount);
-						(0, amount - bid.amount)
-					} else {
-						q.try_insert(0, bid).expect("verified queue was not full above. qed.");
-						(1, amount)
-					};
-
-					let sorted_item_count = q.len().saturating_sub(T::FifoQueueLen::get() as usize);
-					if sorted_item_count > 1 {
-						q[0..sorted_item_count].sort_by_key(|x| x.amount);
-					}
-
-					Ok(net)
-				},
-			)?;
-			QueueTotals::<T>::mutate(|qs| {
-				qs.bounded_resize(queue_count, (0, Zero::zero()));
-				qs[queue_index].0 += net.0;
-				qs[queue_index].1 = qs[queue_index].1.saturating_add(net.1);
-			});
-			Self::deposit_event(Event::BidPlaced { who, amount, duration });
-
-			Ok(().into())
-		}
-
-		/// Retract a previously placed bid.
-		///
-		/// Origin must be Signed, and the account should have previously issued a still-active bid
-		/// of `amount` for `duration`.
-		///
-		/// - `amount`: The amount of the previous bid.
-		/// - `duration`: The duration of the previous bid.
-		#[pallet::weight(T::WeightInfo::place_bid(T::MaxQueueLen::get()))]
-		pub fn retract_bid(
-			origin: OriginFor<T>,
-			#[pallet::compact] amount: BalanceOf<T>,
-			duration: u32,
-		) -> DispatchResultWithPostInfo {
-			let who = ensure_signed(origin)?;
-
-			let queue_count = T::QueueCount::get() as usize;
-			let queue_index = duration.checked_sub(1).ok_or(Error::<T>::DurationTooSmall)? as usize;
-			ensure!(queue_index < queue_count, Error::<T>::DurationTooBig);
-
-			let bid = GiltBid { amount, who };
-			let new_len = Queues::<T>::try_mutate(duration, |q| -> Result<u32, DispatchError> {
-				let pos = q.iter().position(|i| i == &bid).ok_or(Error::<T>::NotFound)?;
-				q.remove(pos);
-				Ok(q.len() as u32)
-			})?;
-
-			QueueTotals::<T>::mutate(|qs| {
-				qs.bounded_resize(queue_count, (0, Zero::zero()));
-				qs[queue_index].0 = new_len;
-				qs[queue_index].1 = qs[queue_index].1.saturating_sub(bid.amount);
-			});
-
-			T::Currency::unreserve(&bid.who, bid.amount);
-			Self::deposit_event(Event::BidRetracted { who: bid.who, amount: bid.amount, duration });
-
-			Ok(().into())
-		}
-
-		/// Set target proportion of gilt-funds.
-		///
-		/// Origin must be `AdminOrigin`.
-		///
-		/// - `target`: The target proportion of effective issued funds that should be under gilts
-		/// at any one time.
-		#[pallet::weight(T::WeightInfo::set_target())]
-		pub fn set_target(
-			origin: OriginFor<T>,
-			#[pallet::compact] target: Perquintill,
-		) -> DispatchResultWithPostInfo {
-			T::AdminOrigin::ensure_origin(origin)?;
-			ActiveTotal::<T>::mutate(|totals| totals.target = target);
-			Ok(().into())
-		}
-
-		/// Remove an active but expired gilt. Reserved funds under gilt are freed and balance is
-		/// adjusted to ensure that the funds grow or shrink to maintain the equivalent proportion
-		/// of effective total issued funds.
-		///
-		/// Origin must be Signed and the account must be the owner of the gilt of the given index.
-		///
-		/// - `index`: The index of the gilt to be thawed.
-		#[pallet::weight(T::WeightInfo::thaw())]
-		pub fn thaw(
-			origin: OriginFor<T>,
-			#[pallet::compact] index: ActiveIndex,
-		) -> DispatchResultWithPostInfo {
-			let who = ensure_signed(origin)?;
-
-			// Look for `index`
-			let gilt = Active::<T>::get(index).ok_or(Error::<T>::Unknown)?;
-			// If found, check the owner is `who`.
-			ensure!(gilt.who == who, Error::<T>::NotOwner);
-			let now = frame_system::Pallet::<T>::block_number();
-			ensure!(now >= gilt.expiry, Error::<T>::NotExpired);
-			// Remove it
-			Active::<T>::remove(index);
-
-			// Multiply the proportion it is by the total issued.
-			let total_issuance =
-				T::Currency::total_issuance().saturating_sub(T::IgnoredIssuance::get());
-			ActiveTotal::<T>::mutate(|totals| {
-				let nongilt_issuance = total_issuance.saturating_sub(totals.frozen);
-				let effective_issuance =
-					totals.proportion.left_from_one().saturating_reciprocal_mul(nongilt_issuance);
-				let gilt_value = gilt.proportion * effective_issuance;
-
-				totals.frozen = totals.frozen.saturating_sub(gilt.amount);
-				totals.proportion = totals.proportion.saturating_sub(gilt.proportion);
-
-				// Remove or mint the additional to the amount using `Deficit`/`Surplus`.
-				if gilt_value > gilt.amount {
-					// Unreserve full amount.
-					T::Currency::unreserve(&gilt.who, gilt.amount);
-					let amount = gilt_value - gilt.amount;
-					let deficit = T::Currency::deposit_creating(&gilt.who, amount);
-					T::Deficit::on_unbalanced(deficit);
-				} else {
-					if gilt_value < gilt.amount {
-						// We take anything reserved beyond the gilt's final value.
-						let rest = gilt.amount - gilt_value;
-						// `slash` might seem a little aggressive, but it's the only way to do it
-						// in case it's locked into the staking system.
-						let surplus = T::Currency::slash_reserved(&gilt.who, rest).0;
-						T::Surplus::on_unbalanced(surplus);
-					}
-					// Unreserve only its new value (less than the amount reserved). Everything
-					// should add up, but (defensive) in case it doesn't, unreserve takes lower
-					// priority over the funds.
-					let err_amt = T::Currency::unreserve(&gilt.who, gilt_value);
-					debug_assert!(err_amt.is_zero());
-				}
-
-				let e = Event::GiltThawed {
-					index,
-					who: gilt.who,
-					original_amount: gilt.amount,
-					additional_amount: gilt_value,
-				};
-				Self::deposit_event(e);
-			});
-
-			Ok(().into())
-		}
-	}
-
-	/// Issuance information returned by `issuance()`.
-	pub struct IssuanceInfo<Balance> {
-		/// The balance held in reserve over all active gilts.
-		pub reserved: Balance,
-		/// The issuance not held in reserve for active gilts. Together with `reserved` this sums
-		/// to `Currency::total_issuance`.
-		pub non_gilt: Balance,
-		/// The balance that `reserved` is effectively worth, at present. This is not issued funds
-		/// and could be less than `reserved` (though in most cases should be greater).
-		pub effective: Balance,
-	}
-
-	impl<T: Config> Pallet<T> {
-		/// Get the target amount of Gilts that we're aiming for.
-		pub fn target() -> Perquintill {
-			ActiveTotal::<T>::get().target
-		}
-
-		/// Returns information on the issuance of gilts.
-		pub fn issuance() -> IssuanceInfo<BalanceOf<T>> {
-			let totals = ActiveTotal::<T>::get();
-
-			let total_issuance = T::Currency::total_issuance();
-			let non_gilt = total_issuance.saturating_sub(totals.frozen);
-			let effective = totals.proportion.left_from_one().saturating_reciprocal_mul(non_gilt);
-
-			IssuanceInfo { reserved: totals.frozen, non_gilt, effective }
-		}
-
-		/// Attempt to enlarge our gilt-set from bids in order to satisfy our desired target amount
-		/// of funds frozen into gilts.
-		pub fn pursue_target(max_bids: u32) -> Weight {
-			let totals = ActiveTotal::<T>::get();
-			if totals.proportion < totals.target {
-				let missing = totals.target.saturating_sub(totals.proportion);
-
-				let total_issuance =
-					T::Currency::total_issuance().saturating_sub(T::IgnoredIssuance::get());
-				let nongilt_issuance = total_issuance.saturating_sub(totals.frozen);
-				let effective_issuance =
-					totals.proportion.left_from_one().saturating_reciprocal_mul(nongilt_issuance);
-				let intake = missing * effective_issuance;
-
-				let (bids_taken, queues_hit) = Self::enlarge(intake, max_bids);
-				let first_from_each_queue = T::WeightInfo::pursue_target_per_queue(queues_hit);
-				let rest_from_each_queue = T::WeightInfo::pursue_target_per_item(bids_taken)
-					.saturating_sub(T::WeightInfo::pursue_target_per_item(queues_hit));
-				first_from_each_queue + rest_from_each_queue
-			} else {
-				T::WeightInfo::pursue_target_noop()
-			}
-		}
-
-		/// Freeze additional funds from queue of bids up to `amount`. Use at most `max_bids`
-		/// from the queue.
-		///
-		/// Return the number of bids taken and the number of distinct queues taken from.
-		pub fn enlarge(amount: BalanceOf<T>, max_bids: u32) -> (u32, u32) {
-			let total_issuance =
-				T::Currency::total_issuance().saturating_sub(T::IgnoredIssuance::get());
-			let mut remaining = amount;
-			let mut bids_taken = 0;
-			let mut queues_hit = 0;
-			let now = frame_system::Pallet::<T>::block_number();
-
-			ActiveTotal::<T>::mutate(|totals| {
-				QueueTotals::<T>::mutate(|qs| {
-					for duration in (1..=T::QueueCount::get()).rev() {
-						if qs[duration as usize - 1].0 == 0 {
-							continue
-						}
-						let queue_index = duration as usize - 1;
-						let expiry =
-							now.saturating_add(T::Period::get().saturating_mul(duration.into()));
-						Queues::<T>::mutate(duration, |q| {
-							while let Some(mut bid) = q.pop() {
-								if remaining < bid.amount {
-									let overflow = bid.amount - remaining;
-									bid.amount = remaining;
-									q.try_push(GiltBid { amount: overflow, who: bid.who.clone() })
-										.expect("just popped, so there must be space. qed");
-								}
-								let amount = bid.amount;
-								// Can never overflow due to block above.
-								remaining -= amount;
-								// Should never underflow since it should track the total of the
-								// bids exactly, but we'll be defensive.
-								qs[queue_index].1 =
-									qs[queue_index].1.defensive_saturating_sub(bid.amount);
-
-								// Now to activate the bid...
-								let nongilt_issuance =
-									total_issuance.defensive_saturating_sub(totals.frozen);
-								let effective_issuance = totals
-									.proportion
-									.left_from_one()
-									.saturating_reciprocal_mul(nongilt_issuance);
-								let n = amount;
-								let d = effective_issuance;
-								let proportion = Perquintill::from_rational(n, d);
-								let who = bid.who;
-								let index = totals.index;
-								totals.frozen += bid.amount;
-								totals.proportion =
-									totals.proportion.defensive_saturating_add(proportion);
-								totals.index += 1;
-								let e =
-									Event::GiltIssued { index, expiry, who: who.clone(), amount };
-								Self::deposit_event(e);
-								let gilt = ActiveGilt { amount, proportion, who, expiry };
-								Active::<T>::insert(index, gilt);
-
-								bids_taken += 1;
-
-								if remaining.is_zero() || bids_taken == max_bids {
-									break
-								}
-							}
-							queues_hit += 1;
-							qs[queue_index].0 = q.len() as u32;
-						});
-						if remaining.is_zero() || bids_taken == max_bids {
-							break
-						}
-					}
-				});
-			});
-			(bids_taken, queues_hit)
-		}
-	}
-}
diff --git a/substrate/frame/gilt/src/tests.rs b/substrate/frame/gilt/src/tests.rs
deleted file mode 100644
index 2ac369dd3b8..00000000000
--- a/substrate/frame/gilt/src/tests.rs
+++ /dev/null
@@ -1,573 +0,0 @@
-// This file is part of Substrate.
-
-// Copyright (C) 2019-2022 Parity Technologies (UK) Ltd.
-// SPDX-License-Identifier: Apache-2.0
-
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-// 	http://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-
-//! Tests for Gilt pallet.
-
-use super::*;
-use crate::{mock::*, Error};
-use frame_support::{assert_noop, assert_ok, dispatch::DispatchError, traits::Currency};
-use pallet_balances::Error as BalancesError;
-use sp_arithmetic::Perquintill;
-
-#[test]
-fn basic_setup_works() {
-	new_test_ext().execute_with(|| {
-		run_to_block(1);
-
-		for q in 0..3 {
-			assert!(Queues::<Test>::get(q).is_empty());
-		}
-		assert_eq!(
-			ActiveTotal::<Test>::get(),
-			ActiveGiltsTotal {
-				frozen: 0,
-				proportion: Perquintill::zero(),
-				index: 0,
-				target: Perquintill::zero(),
-			}
-		);
-		assert_eq!(QueueTotals::<Test>::get(), vec![(0, 0); 3]);
-	});
-}
-
-#[test]
-fn set_target_works() {
-	new_test_ext().execute_with(|| {
-		run_to_block(1);
-		let e = DispatchError::BadOrigin;
-		assert_noop!(Gilt::set_target(RuntimeOrigin::signed(2), Perquintill::from_percent(50)), e);
-		assert_ok!(Gilt::set_target(RuntimeOrigin::signed(1), Perquintill::from_percent(50)));
-
-		assert_eq!(
-			ActiveTotal::<Test>::get(),
-			ActiveGiltsTotal {
-				frozen: 0,
-				proportion: Perquintill::zero(),
-				index: 0,
-				target: Perquintill::from_percent(50),
-			}
-		);
-	});
-}
-
-#[test]
-fn place_bid_works() {
-	new_test_ext().execute_with(|| {
-		run_to_block(1);
-		assert_noop!(
-			Gilt::place_bid(RuntimeOrigin::signed(1), 1, 2),
-			Error::<Test>::AmountTooSmall
-		);
-		assert_noop!(
-			Gilt::place_bid(RuntimeOrigin::signed(1), 101, 2),
-			BalancesError::<Test>::InsufficientBalance
-		);
-		assert_noop!(
-			Gilt::place_bid(RuntimeOrigin::signed(1), 10, 4),
-			Error::<Test>::DurationTooBig
-		);
-		assert_ok!(Gilt::place_bid(RuntimeOrigin::signed(1), 10, 2));
-		assert_eq!(Balances::reserved_balance(1), 10);
-		assert_eq!(Queues::<Test>::get(2), vec![GiltBid { amount: 10, who: 1 }]);
-		assert_eq!(QueueTotals::<Test>::get(), vec![(0, 0), (1, 10), (0, 0)]);
-	});
-}
-
-#[test]
-fn place_bid_queuing_works() {
-	new_test_ext().execute_with(|| {
-		run_to_block(1);
-		assert_ok!(Gilt::place_bid(RuntimeOrigin::signed(1), 20, 2));
-		assert_ok!(Gilt::place_bid(RuntimeOrigin::signed(1), 10, 2));
-		assert_ok!(Gilt::place_bid(RuntimeOrigin::signed(1), 5, 2));
-		assert_noop!(Gilt::place_bid(RuntimeOrigin::signed(1), 5, 2), Error::<Test>::BidTooLow);
-		assert_ok!(Gilt::place_bid(RuntimeOrigin::signed(1), 15, 2));
-		assert_eq!(Balances::reserved_balance(1), 45);
-
-		assert_ok!(Gilt::place_bid(RuntimeOrigin::signed(1), 25, 2));
-		assert_eq!(Balances::reserved_balance(1), 60);
-		assert_noop!(Gilt::place_bid(RuntimeOrigin::signed(1), 10, 2), Error::<Test>::BidTooLow);
-		assert_eq!(
-			Queues::<Test>::get(2),
-			vec![
-				GiltBid { amount: 15, who: 1 },
-				GiltBid { amount: 25, who: 1 },
-				GiltBid { amount: 20, who: 1 },
-			]
-		);
-		assert_eq!(QueueTotals::<Test>::get(), vec![(0, 0), (3, 60), (0, 0)]);
-	});
-}
-
-#[test]
-fn place_bid_fails_when_queue_full() {
-	new_test_ext().execute_with(|| {
-		run_to_block(1);
-		assert_ok!(Gilt::place_bid(RuntimeOrigin::signed(1), 10, 2));
-		assert_ok!(Gilt::place_bid(RuntimeOrigin::signed(2), 10, 2));
-		assert_ok!(Gilt::place_bid(RuntimeOrigin::signed(3), 10, 2));
-		assert_noop!(Gilt::place_bid(RuntimeOrigin::signed(4), 10, 2), Error::<Test>::BidTooLow);
-		assert_ok!(Gilt::place_bid(RuntimeOrigin::signed(4), 10, 3));
-	});
-}
-
-#[test]
-fn multiple_place_bids_works() {
-	new_test_ext().execute_with(|| {
-		run_to_block(1);
-		assert_ok!(Gilt::place_bid(RuntimeOrigin::signed(1), 10, 1));
-		assert_ok!(Gilt::place_bid(RuntimeOrigin::signed(1), 10, 2));
-		assert_ok!(Gilt::place_bid(RuntimeOrigin::signed(1), 10, 2));
-		assert_ok!(Gilt::place_bid(RuntimeOrigin::signed(1), 10, 3));
-		assert_ok!(Gilt::place_bid(RuntimeOrigin::signed(2), 10, 2));
-
-		assert_eq!(Balances::reserved_balance(1), 40);
-		assert_eq!(Balances::reserved_balance(2), 10);
-		assert_eq!(Queues::<Test>::get(1), vec![GiltBid { amount: 10, who: 1 },]);
-		assert_eq!(
-			Queues::<Test>::get(2),
-			vec![
-				GiltBid { amount: 10, who: 2 },
-				GiltBid { amount: 10, who: 1 },
-				GiltBid { amount: 10, who: 1 },
-			]
-		);
-		assert_eq!(Queues::<Test>::get(3), vec![GiltBid { amount: 10, who: 1 },]);
-		assert_eq!(QueueTotals::<Test>::get(), vec![(1, 10), (3, 30), (1, 10)]);
-	});
-}
-
-#[test]
-fn retract_single_item_queue_works() {
-	new_test_ext().execute_with(|| {
-		run_to_block(1);
-		assert_ok!(Gilt::place_bid(RuntimeOrigin::signed(1), 10, 1));
-		assert_ok!(Gilt::place_bid(RuntimeOrigin::signed(1), 10, 2));
-		assert_ok!(Gilt::retract_bid(RuntimeOrigin::signed(1), 10, 1));
-
-		assert_eq!(Balances::reserved_balance(1), 10);
-		assert_eq!(Queues::<Test>::get(1), vec![]);
-		assert_eq!(Queues::<Test>::get(2), vec![GiltBid { amount: 10, who: 1 }]);
-		assert_eq!(QueueTotals::<Test>::get(), vec![(0, 0), (1, 10), (0, 0)]);
-	});
-}
-
-#[test]
-fn retract_with_other_and_duplicate_works() {
-	new_test_ext().execute_with(|| {
-		run_to_block(1);
-		assert_ok!(Gilt::place_bid(RuntimeOrigin::signed(1), 10, 1));
-		assert_ok!(Gilt::place_bid(RuntimeOrigin::signed(1), 10, 2));
-		assert_ok!(Gilt::place_bid(RuntimeOrigin::signed(1), 10, 2));
-		assert_ok!(Gilt::place_bid(RuntimeOrigin::signed(2), 10, 2));
-
-		assert_ok!(Gilt::retract_bid(RuntimeOrigin::signed(1), 10, 2));
-		assert_eq!(Balances::reserved_balance(1), 20);
-		assert_eq!(Balances::reserved_balance(2), 10);
-		assert_eq!(Queues::<Test>::get(1), vec![GiltBid { amount: 10, who: 1 },]);
-		assert_eq!(
-			Queues::<Test>::get(2),
-			vec![GiltBid { amount: 10, who: 2 }, GiltBid { amount: 10, who: 1 },]
-		);
-		assert_eq!(QueueTotals::<Test>::get(), vec![(1, 10), (2, 20), (0, 0)]);
-	});
-}
-
-#[test]
-fn retract_non_existent_item_fails() {
-	new_test_ext().execute_with(|| {
-		run_to_block(1);
-		assert_noop!(Gilt::retract_bid(RuntimeOrigin::signed(1), 10, 1), Error::<Test>::NotFound);
-		assert_ok!(Gilt::place_bid(RuntimeOrigin::signed(1), 10, 1));
-		assert_noop!(Gilt::retract_bid(RuntimeOrigin::signed(1), 20, 1), Error::<Test>::NotFound);
-		assert_noop!(Gilt::retract_bid(RuntimeOrigin::signed(1), 10, 2), Error::<Test>::NotFound);
-		assert_noop!(Gilt::retract_bid(RuntimeOrigin::signed(2), 10, 1), Error::<Test>::NotFound);
-	});
-}
-
-#[test]
-fn basic_enlarge_works() {
-	new_test_ext().execute_with(|| {
-		run_to_block(1);
-		assert_ok!(Gilt::place_bid(RuntimeOrigin::signed(1), 40, 1));
-		assert_ok!(Gilt::place_bid(RuntimeOrigin::signed(2), 40, 2));
-		Gilt::enlarge(40, 2);
-
-		// Takes 2/2, then stopped because it reaches its max amount
-		assert_eq!(Balances::reserved_balance(1), 40);
-		assert_eq!(Balances::reserved_balance(2), 40);
-		assert_eq!(Queues::<Test>::get(1), vec![GiltBid { amount: 40, who: 1 }]);
-		assert_eq!(Queues::<Test>::get(2), vec![]);
-		assert_eq!(QueueTotals::<Test>::get(), vec![(1, 40), (0, 0), (0, 0)]);
-
-		assert_eq!(
-			ActiveTotal::<Test>::get(),
-			ActiveGiltsTotal {
-				frozen: 40,
-				proportion: Perquintill::from_percent(10),
-				index: 1,
-				target: Perquintill::zero(),
-			}
-		);
-		assert_eq!(
-			Active::<Test>::get(0).unwrap(),
-			ActiveGilt { proportion: Perquintill::from_percent(10), amount: 40, who: 2, expiry: 7 }
-		);
-	});
-}
-
-#[test]
-fn enlarge_respects_bids_limit() {
-	new_test_ext().execute_with(|| {
-		run_to_block(1);
-		assert_ok!(Gilt::place_bid(RuntimeOrigin::signed(1), 40, 1));
-		assert_ok!(Gilt::place_bid(RuntimeOrigin::signed(2), 40, 2));
-		assert_ok!(Gilt::place_bid(RuntimeOrigin::signed(3), 40, 2));
-		assert_ok!(Gilt::place_bid(RuntimeOrigin::signed(4), 40, 3));
-		Gilt::enlarge(100, 2);
-
-		// Should have taken 4/3 and 2/2, then stopped because it's only allowed 2.
-		assert_eq!(Queues::<Test>::get(1), vec![GiltBid { amount: 40, who: 1 }]);
-		assert_eq!(Queues::<Test>::get(2), vec![GiltBid { amount: 40, who: 3 }]);
-		assert_eq!(Queues::<Test>::get(3), vec![]);
-		assert_eq!(QueueTotals::<Test>::get(), vec![(1, 40), (1, 40), (0, 0)]);
-
-		assert_eq!(
-			Active::<Test>::get(0).unwrap(),
-			ActiveGilt {
-				proportion: Perquintill::from_percent(10),
-				amount: 40,
-				who: 4,
-				expiry: 10,
-			}
-		);
-		assert_eq!(
-			Active::<Test>::get(1).unwrap(),
-			ActiveGilt { proportion: Perquintill::from_percent(10), amount: 40, who: 2, expiry: 7 }
-		);
-		assert_eq!(
-			ActiveTotal::<Test>::get(),
-			ActiveGiltsTotal {
-				frozen: 80,
-				proportion: Perquintill::from_percent(20),
-				index: 2,
-				target: Perquintill::zero(),
-			}
-		);
-	});
-}
-
-#[test]
-fn enlarge_respects_amount_limit_and_will_split() {
-	new_test_ext().execute_with(|| {
-		run_to_block(1);
-		assert_ok!(Gilt::place_bid(RuntimeOrigin::signed(1), 80, 1));
-		Gilt::enlarge(40, 2);
-
-		// Takes 2/2, then stopped because it reaches its max amount
-		assert_eq!(Queues::<Test>::get(1), vec![GiltBid { amount: 40, who: 1 }]);
-		assert_eq!(QueueTotals::<Test>::get(), vec![(1, 40), (0, 0), (0, 0)]);
-
-		assert_eq!(
-			Active::<Test>::get(0).unwrap(),
-			ActiveGilt { proportion: Perquintill::from_percent(10), amount: 40, who: 1, expiry: 4 }
-		);
-		assert_eq!(
-			ActiveTotal::<Test>::get(),
-			ActiveGiltsTotal {
-				frozen: 40,
-				proportion: Perquintill::from_percent(10),
-				index: 1,
-				target: Perquintill::zero(),
-			}
-		);
-	});
-}
-
-#[test]
-fn basic_thaw_works() {
-	new_test_ext().execute_with(|| {
-		run_to_block(1);
-		assert_ok!(Gilt::place_bid(RuntimeOrigin::signed(1), 40, 1));
-		Gilt::enlarge(40, 1);
-		run_to_block(3);
-		assert_noop!(Gilt::thaw(RuntimeOrigin::signed(1), 0), Error::<Test>::NotExpired);
-		run_to_block(4);
-		assert_noop!(Gilt::thaw(RuntimeOrigin::signed(1), 1), Error::<Test>::Unknown);
-		assert_noop!(Gilt::thaw(RuntimeOrigin::signed(2), 0), Error::<Test>::NotOwner);
-		assert_ok!(Gilt::thaw(RuntimeOrigin::signed(1), 0));
-
-		assert_eq!(
-			ActiveTotal::<Test>::get(),
-			ActiveGiltsTotal {
-				frozen: 0,
-				proportion: Perquintill::zero(),
-				index: 1,
-				target: Perquintill::zero(),
-			}
-		);
-		assert_eq!(Active::<Test>::get(0), None);
-		assert_eq!(Balances::free_balance(1), 100);
-		assert_eq!(Balances::reserved_balance(1), 0);
-	});
-}
-
-#[test]
-fn thaw_when_issuance_higher_works() {
-	new_test_ext().execute_with(|| {
-		run_to_block(1);
-		assert_ok!(Gilt::place_bid(RuntimeOrigin::signed(1), 100, 1));
-		Gilt::enlarge(100, 1);
-
-		// Everybody else's balances goes up by 50%
-		Balances::make_free_balance_be(&2, 150);
-		Balances::make_free_balance_be(&3, 150);
-		Balances::make_free_balance_be(&4, 150);
-
-		run_to_block(4);
-		assert_ok!(Gilt::thaw(RuntimeOrigin::signed(1), 0));
-
-		assert_eq!(Balances::free_balance(1), 150);
-		assert_eq!(Balances::reserved_balance(1), 0);
-	});
-}
-
-#[test]
-fn thaw_with_ignored_issuance_works() {
-	new_test_ext().execute_with(|| {
-		run_to_block(1);
-		// Give account zero some balance.
-		Balances::make_free_balance_be(&0, 200);
-
-		assert_ok!(Gilt::place_bid(RuntimeOrigin::signed(1), 100, 1));
-		Gilt::enlarge(100, 1);
-
-		// Account zero transfers 50 into everyone else's accounts.
-		assert_ok!(Balances::transfer(RuntimeOrigin::signed(0), 2, 50));
-		assert_ok!(Balances::transfer(RuntimeOrigin::signed(0), 3, 50));
-		assert_ok!(Balances::transfer(RuntimeOrigin::signed(0), 4, 50));
-
-		run_to_block(4);
-		assert_ok!(Gilt::thaw(RuntimeOrigin::signed(1), 0));
-
-		// Account zero changes have been ignored.
-		assert_eq!(Balances::free_balance(1), 150);
-		assert_eq!(Balances::reserved_balance(1), 0);
-	});
-}
-
-#[test]
-fn thaw_when_issuance_lower_works() {
-	new_test_ext().execute_with(|| {
-		run_to_block(1);
-		assert_ok!(Gilt::place_bid(RuntimeOrigin::signed(1), 100, 1));
-		Gilt::enlarge(100, 1);
-
-		// Everybody else's balances goes down by 25%
-		Balances::make_free_balance_be(&2, 75);
-		Balances::make_free_balance_be(&3, 75);
-		Balances::make_free_balance_be(&4, 75);
-
-		run_to_block(4);
-		assert_ok!(Gilt::thaw(RuntimeOrigin::signed(1), 0));
-
-		assert_eq!(Balances::free_balance(1), 75);
-		assert_eq!(Balances::reserved_balance(1), 0);
-	});
-}
-
-#[test]
-fn multiple_thaws_works() {
-	new_test_ext().execute_with(|| {
-		run_to_block(1);
-		assert_ok!(Gilt::place_bid(RuntimeOrigin::signed(1), 40, 1));
-		assert_ok!(Gilt::place_bid(RuntimeOrigin::signed(1), 60, 1));
-		assert_ok!(Gilt::place_bid(RuntimeOrigin::signed(2), 50, 1));
-		Gilt::enlarge(200, 3);
-
-		// Double everyone's free balances.
-		Balances::make_free_balance_be(&2, 100);
-		Balances::make_free_balance_be(&3, 200);
-		Balances::make_free_balance_be(&4, 200);
-
-		run_to_block(4);
-		assert_ok!(Gilt::thaw(RuntimeOrigin::signed(1), 0));
-		assert_ok!(Gilt::thaw(RuntimeOrigin::signed(1), 1));
-		assert_ok!(Gilt::thaw(RuntimeOrigin::signed(2), 2));
-
-		assert_eq!(Balances::free_balance(1), 200);
-		assert_eq!(Balances::free_balance(2), 200);
-	});
-}
-
-#[test]
-fn multiple_thaws_works_in_alternative_thaw_order() {
-	new_test_ext().execute_with(|| {
-		run_to_block(1);
-		assert_ok!(Gilt::place_bid(RuntimeOrigin::signed(1), 40, 1));
-		assert_ok!(Gilt::place_bid(RuntimeOrigin::signed(1), 60, 1));
-		assert_ok!(Gilt::place_bid(RuntimeOrigin::signed(2), 50, 1));
-		Gilt::enlarge(200, 3);
-
-		// Double everyone's free balances.
-		Balances::make_free_balance_be(&2, 100);
-		Balances::make_free_balance_be(&3, 200);
-		Balances::make_free_balance_be(&4, 200);
-
-		run_to_block(4);
-		assert_ok!(Gilt::thaw(RuntimeOrigin::signed(2), 2));
-		assert_ok!(Gilt::thaw(RuntimeOrigin::signed(1), 1));
-		assert_ok!(Gilt::thaw(RuntimeOrigin::signed(1), 0));
-
-		assert_eq!(Balances::free_balance(1), 200);
-		assert_eq!(Balances::free_balance(2), 200);
-	});
-}
-
-#[test]
-fn enlargement_to_target_works() {
-	new_test_ext().execute_with(|| {
-		run_to_block(2);
-		assert_ok!(Gilt::place_bid(RuntimeOrigin::signed(1), 40, 1));
-		assert_ok!(Gilt::place_bid(RuntimeOrigin::signed(1), 40, 2));
-		assert_ok!(Gilt::place_bid(RuntimeOrigin::signed(2), 40, 2));
-		assert_ok!(Gilt::place_bid(RuntimeOrigin::signed(2), 40, 3));
-		assert_ok!(Gilt::place_bid(RuntimeOrigin::signed(3), 40, 3));
-		assert_ok!(Gilt::set_target(RuntimeOrigin::signed(1), Perquintill::from_percent(40)));
-
-		run_to_block(3);
-		assert_eq!(Queues::<Test>::get(1), vec![GiltBid { amount: 40, who: 1 },]);
-		assert_eq!(
-			Queues::<Test>::get(2),
-			vec![GiltBid { amount: 40, who: 2 }, GiltBid { amount: 40, who: 1 },]
-		);
-		assert_eq!(
-			Queues::<Test>::get(3),
-			vec![GiltBid { amount: 40, who: 3 }, GiltBid { amount: 40, who: 2 },]
-		);
-		assert_eq!(QueueTotals::<Test>::get(), vec![(1, 40), (2, 80), (2, 80)]);
-
-		run_to_block(4);
-		// Two new gilts should have been issued to 2 & 3 for 40 each & duration of 3.
-		assert_eq!(
-			Active::<Test>::get(0).unwrap(),
-			ActiveGilt {
-				proportion: Perquintill::from_percent(10),
-				amount: 40,
-				who: 2,
-				expiry: 13,
-			}
-		);
-		assert_eq!(
-			Active::<Test>::get(1).unwrap(),
-			ActiveGilt {
-				proportion: Perquintill::from_percent(10),
-				amount: 40,
-				who: 3,
-				expiry: 13,
-			}
-		);
-		assert_eq!(
-			ActiveTotal::<Test>::get(),
-			ActiveGiltsTotal {
-				frozen: 80,
-				proportion: Perquintill::from_percent(20),
-				index: 2,
-				target: Perquintill::from_percent(40),
-			}
-		);
-
-		run_to_block(5);
-		// No change
-		assert_eq!(
-			ActiveTotal::<Test>::get(),
-			ActiveGiltsTotal {
-				frozen: 80,
-				proportion: Perquintill::from_percent(20),
-				index: 2,
-				target: Perquintill::from_percent(40),
-			}
-		);
-
-		run_to_block(6);
-		// Two new gilts should have been issued to 1 & 2 for 40 each & duration of 2.
-		assert_eq!(
-			Active::<Test>::get(2).unwrap(),
-			ActiveGilt {
-				proportion: Perquintill::from_percent(10),
-				amount: 40,
-				who: 1,
-				expiry: 12,
-			}
-		);
-		assert_eq!(
-			Active::<Test>::get(3).unwrap(),
-			ActiveGilt {
-				proportion: Perquintill::from_percent(10),
-				amount: 40,
-				who: 2,
-				expiry: 12,
-			}
-		);
-		assert_eq!(
-			ActiveTotal::<Test>::get(),
-			ActiveGiltsTotal {
-				frozen: 160,
-				proportion: Perquintill::from_percent(40),
-				index: 4,
-				target: Perquintill::from_percent(40),
-			}
-		);
-
-		run_to_block(8);
-		// No change now.
-		assert_eq!(
-			ActiveTotal::<Test>::get(),
-			ActiveGiltsTotal {
-				frozen: 160,
-				proportion: Perquintill::from_percent(40),
-				index: 4,
-				target: Perquintill::from_percent(40),
-			}
-		);
-
-		// Set target a bit higher to use up the remaining bid.
-		assert_ok!(Gilt::set_target(RuntimeOrigin::signed(1), Perquintill::from_percent(60)));
-		run_to_block(10);
-
-		// Two new gilts should have been issued to 1 & 2 for 40 each & duration of 2.
-		assert_eq!(
-			Active::<Test>::get(4).unwrap(),
-			ActiveGilt {
-				proportion: Perquintill::from_percent(10),
-				amount: 40,
-				who: 1,
-				expiry: 13,
-			}
-		);
-
-		assert_eq!(
-			ActiveTotal::<Test>::get(),
-			ActiveGiltsTotal {
-				frozen: 200,
-				proportion: Perquintill::from_percent(50),
-				index: 5,
-				target: Perquintill::from_percent(60),
-			}
-		);
-	});
-}
diff --git a/substrate/frame/gilt/Cargo.toml b/substrate/frame/nis/Cargo.toml
similarity index 93%
rename from substrate/frame/gilt/Cargo.toml
rename to substrate/frame/nis/Cargo.toml
index f7bd98999f7..be12d97dd87 100644
--- a/substrate/frame/gilt/Cargo.toml
+++ b/substrate/frame/nis/Cargo.toml
@@ -1,5 +1,5 @@
 [package]
-name = "pallet-gilt"
+name = "pallet-nis"
 version = "4.0.0-dev"
 authors = ["Parity Technologies <admin@parity.io>"]
 edition = "2021"
@@ -19,12 +19,12 @@ frame-benchmarking = { version = "4.0.0-dev", default-features = false, optional
 frame-support = { version = "4.0.0-dev", default-features = false, path = "../support" }
 frame-system = { version = "4.0.0-dev", default-features = false, path = "../system" }
 sp-arithmetic = { version = "6.0.0", default-features = false, path = "../../primitives/arithmetic" }
+sp-core = { version = "7.0.0", default-features = false, path = "../../primitives/core" }
 sp-runtime = { version = "7.0.0", default-features = false, path = "../../primitives/runtime" }
 sp-std = { version = "5.0.0", default-features = false, path = "../../primitives/std" }
 
 [dev-dependencies]
 pallet-balances = { version = "4.0.0-dev", path = "../balances" }
-sp-core = { version = "7.0.0", path = "../../primitives/core" }
 sp-io = { version = "7.0.0", path = "../../primitives/io" }
 
 [features]
@@ -36,6 +36,7 @@ std = [
 	"frame-system/std",
 	"scale-info/std",
 	"sp-arithmetic/std",
+	"sp-core/std",
 	"sp-runtime/std",
 	"sp-std/std",
 ]
diff --git a/substrate/frame/gilt/README.md b/substrate/frame/nis/README.md
similarity index 100%
rename from substrate/frame/gilt/README.md
rename to substrate/frame/nis/README.md
diff --git a/substrate/frame/nis/src/benchmarking.rs b/substrate/frame/nis/src/benchmarking.rs
new file mode 100644
index 00000000000..606b1c484b1
--- /dev/null
+++ b/substrate/frame/nis/src/benchmarking.rs
@@ -0,0 +1,182 @@
+// This file is part of Substrate.
+
+// Copyright (C) 2021-2022 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.
+
+//! Benchmarks for NIS Pallet
+
+#![cfg(feature = "runtime-benchmarks")]
+
+use super::*;
+use frame_benchmarking::{account, benchmarks, whitelisted_caller};
+use frame_support::traits::{Currency, EnsureOrigin, Get};
+use frame_system::RawOrigin;
+use sp_arithmetic::Perquintill;
+use sp_runtime::{
+	traits::{Bounded, One, Zero},
+	DispatchError, PerThing,
+};
+use sp_std::prelude::*;
+
+use crate::Pallet as Nis;
+
+const SEED: u32 = 0;
+
+type BalanceOf<T> =
+	<<T as Config>::Currency as Currency<<T as frame_system::Config>::AccountId>>::Balance;
+
+fn fill_queues<T: Config>() -> Result<(), DispatchError> {
+	// filling queues involves filling the first queue entirely and placing a single item in all
+	// other queues.
+
+	let queues = T::QueueCount::get();
+	let bids = T::MaxQueueLen::get();
+
+	let caller: T::AccountId = whitelisted_caller();
+	T::Currency::make_free_balance_be(
+		&caller,
+		T::MinBid::get() * BalanceOf::<T>::from(queues + bids),
+	);
+
+	for _ in 0..bids {
+		Nis::<T>::place_bid(RawOrigin::Signed(caller.clone()).into(), T::MinBid::get(), 1)?;
+	}
+	for d in 1..queues {
+		Nis::<T>::place_bid(RawOrigin::Signed(caller.clone()).into(), T::MinBid::get(), 1 + d)?;
+	}
+	Ok(())
+}
+
+benchmarks! {
+	place_bid {
+		let l in 0..(T::MaxQueueLen::get() - 1);
+		let caller: T::AccountId = whitelisted_caller();
+		T::Currency::make_free_balance_be(&caller, BalanceOf::<T>::max_value());
+		for i in 0..l {
+			Nis::<T>::place_bid(RawOrigin::Signed(caller.clone()).into(), T::MinBid::get(), 1)?;
+		}
+	}: _(RawOrigin::Signed(caller.clone()), T::MinBid::get() * BalanceOf::<T>::from(2u32), 1)
+	verify {
+		assert_eq!(QueueTotals::<T>::get()[0], (l + 1, T::MinBid::get() * BalanceOf::<T>::from(l + 2)));
+	}
+
+	place_bid_max {
+		let caller: T::AccountId = whitelisted_caller();
+		let origin = RawOrigin::Signed(caller.clone());
+		T::Currency::make_free_balance_be(&caller, BalanceOf::<T>::max_value());
+		for i in 0..T::MaxQueueLen::get() {
+			Nis::<T>::place_bid(origin.clone().into(), T::MinBid::get(), 1)?;
+		}
+	}: place_bid(origin, T::MinBid::get() * BalanceOf::<T>::from(2u32), 1)
+	verify {
+		assert_eq!(QueueTotals::<T>::get()[0], (
+			T::MaxQueueLen::get(),
+			T::MinBid::get() * BalanceOf::<T>::from(T::MaxQueueLen::get() + 1),
+		));
+	}
+
+	retract_bid {
+		let l in 1..T::MaxQueueLen::get();
+		let caller: T::AccountId = whitelisted_caller();
+		T::Currency::make_free_balance_be(&caller, BalanceOf::<T>::max_value());
+		for i in 0..l {
+			Nis::<T>::place_bid(RawOrigin::Signed(caller.clone()).into(), T::MinBid::get(), 1)?;
+		}
+	}: _(RawOrigin::Signed(caller.clone()), T::MinBid::get(), 1)
+	verify {
+		assert_eq!(QueueTotals::<T>::get()[0], (l - 1, T::MinBid::get() * BalanceOf::<T>::from(l - 1)));
+	}
+
+	fund_deficit {
+		let origin = T::FundOrigin::successful_origin();
+		let caller: T::AccountId = whitelisted_caller();
+		let bid = T::MinBid::get().max(One::one());
+		T::Currency::make_free_balance_be(&caller, bid);
+		Nis::<T>::place_bid(RawOrigin::Signed(caller.clone()).into(), bid, 1)?;
+		Nis::<T>::process_queues(Perquintill::one(), 1, 1, &mut WeightCounter::unlimited());
+		let original = T::Currency::free_balance(&Nis::<T>::account_id());
+		T::Currency::make_free_balance_be(&Nis::<T>::account_id(), BalanceOf::<T>::min_value());
+	}: _<T::RuntimeOrigin>(origin)
+	verify {
+		// Must fund at least 99.999% of the required amount.
+		let missing = Perquintill::from_rational(
+			T::Currency::free_balance(&Nis::<T>::account_id()), original).left_from_one();
+		assert!(missing <= Perquintill::one() / 100_000);
+	}
+
+	thaw {
+		let caller: T::AccountId = whitelisted_caller();
+		T::Currency::make_free_balance_be(&caller, T::MinBid::get() * BalanceOf::<T>::from(3u32));
+		Nis::<T>::place_bid(RawOrigin::Signed(caller.clone()).into(), T::MinBid::get(), 1)?;
+		Nis::<T>::place_bid(RawOrigin::Signed(caller.clone()).into(), T::MinBid::get(), 1)?;
+		Nis::<T>::process_queues(Perquintill::one(), 1, 2, &mut WeightCounter::unlimited());
+		Receipts::<T>::mutate(0, |m_g| if let Some(ref mut g) = m_g { g.expiry = Zero::zero() });
+	}: _(RawOrigin::Signed(caller.clone()), 0, None)
+	verify {
+		assert!(Receipts::<T>::get(0).is_none());
+	}
+
+	process_queues {
+		fill_queues::<T>()?;
+	}: {
+		Nis::<T>::process_queues(
+			Perquintill::one(),
+			Zero::zero(),
+			u32::max_value(),
+			&mut WeightCounter::unlimited(),
+		)
+	}
+
+	process_queue {
+		let our_account = Nis::<T>::account_id();
+		let issuance = Nis::<T>::issuance();
+		let mut summary = Summary::<T>::get();
+	}: {
+		Nis::<T>::process_queue(
+			1u32,
+			1u32.into(),
+			&our_account,
+			&issuance,
+			0,
+			&mut Bounded::max_value(),
+			&mut (T::MaxQueueLen::get(), Bounded::max_value()),
+			&mut summary,
+			&mut WeightCounter::unlimited(),
+		)
+	}
+
+	process_bid {
+		let who = account::<T::AccountId>("bidder", 0, SEED);
+		let bid = Bid {
+			amount: T::MinBid::get(),
+			who,
+		};
+		let our_account = Nis::<T>::account_id();
+		let issuance = Nis::<T>::issuance();
+		let mut summary = Summary::<T>::get();
+	}: {
+		Nis::<T>::process_bid(
+			bid,
+			2u32.into(),
+			&our_account,
+			&issuance,
+			&mut Bounded::max_value(),
+			&mut Bounded::max_value(),
+			&mut summary,
+		)
+	}
+
+	impl_benchmark_test_suite!(Nis, crate::mock::new_test_ext(), crate::mock::Test);
+}
diff --git a/substrate/frame/nis/src/lib.rs b/substrate/frame/nis/src/lib.rs
new file mode 100644
index 00000000000..97f727c2414
--- /dev/null
+++ b/substrate/frame/nis/src/lib.rs
@@ -0,0 +1,936 @@
+// This file is part of Substrate.
+
+// Copyright (C) 2019-2022 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.
+
+//! # Non-Interactive Staking (NIS) Pallet
+//! A pallet allowing accounts to auction for being frozen and receive open-ended
+//! inflation-protection in return.
+//!
+//! ## Overview
+//!
+//! Lock up tokens, for at least as long as you offer, and be free from both inflation and
+//! intermediate reward or exchange until the tokens become unlocked.
+//!
+//! ## Design
+//!
+//! Queues for each of `1..QueueCount` periods, given in blocks (`Period`). Queues are limited in
+//! size to something sensible, `MaxQueueLen`. A secondary storage item with `QueueCount` x `u32`
+//! elements with the number of items in each queue.
+//!
+//! Queues are split into two parts. The first part is a priority queue based on bid size. The
+//! second part is just a FIFO (the size of the second part is set with `FifoQueueLen`). Items are
+//! always prepended so that removal is always O(1) since removal often happens many times under a
+//! single weighed function (`on_initialize`) yet placing bids only ever happens once per weighed
+//! function (`place_bid`). If the queue has a priority portion, then it remains sorted in order of
+//! bid size so that smaller bids fall off as it gets too large.
+//!
+//! Account may enqueue a balance with some number of `Period`s lock up, up to a maximum of
+//! `QueueCount`. The balance gets reserved. There's a minimum of `MinBid` to avoid dust.
+//!
+//! Until your bid is consolidated and you receive a receipt, you can retract it instantly and the
+//! funds are unreserved.
+//!
+//! There's a target proportion of effective total issuance (i.e. accounting for existing receipts)
+//! which the pallet attempts to have frozen at any one time. It will likely be gradually increased
+//! over time by governance.
+//!
+//! As the proportion of effective total issuance represented by outstanding receipts drops below
+//! `FrozenFraction`, then bids are taken from queues and consolidated into receipts, with the queue
+//! of the greatest period taking priority. If the item in the queue's locked amount is greater than
+//! the amount remaining to achieve `FrozenFraction`, then it is split up into multiple bids and
+//! becomes partially consolidated.
+//!
+//! With the consolidation of a bid, the bid amount is taken from the owner and a receipt is issued.
+//! The receipt records the proportion of the bid compared to effective total issuance at the time
+//! of consolidation. The receipt has two independent elements: a "main" non-fungible receipt and
+//! a second set of fungible "counterpart" tokens. The accounting functionality of the latter must
+//! be provided through the `Counterpart` trait item. The main non-fungible receipt may have its
+//! owner transferred through the pallet's implementation of `nonfungible::Transfer`.
+//!
+//! A later `thaw` function may be called in order to reduce the recorded proportion or entirely
+//! remove the receipt in return for the appropriate proportion of the effective total issuance.
+//! This may happen no earlier than queue's period after the point at which the receipt was issued.
+//! The call must be made by the owner of both the "main" non-fungible receipt and the appropriate
+//! amount of counterpart tokens.
+//!
+//! `NoCounterpart` may be provided as an implementation for the counterpart token system in which
+//! case they are completely disregarded from the thawing logic.
+//!
+//! ## Terms
+//!
+//! - *Effective total issuance*: The total issuance of balances in the system, including all claims
+//!   of all outstanding receipts but excluding `IgnoredIssuance`.
+
+#![cfg_attr(not(feature = "std"), no_std)]
+
+use frame_support::{
+	dispatch::{DispatchError, DispatchResult},
+	traits::fungible::{Inspect as FungibleInspect, Mutate as FungibleMutate},
+};
+pub use pallet::*;
+use sp_arithmetic::{traits::Unsigned, RationalArg};
+use sp_core::TypedGet;
+use sp_runtime::{
+	traits::{Convert, ConvertBack},
+	Perquintill,
+};
+
+mod benchmarking;
+#[cfg(test)]
+mod mock;
+#[cfg(test)]
+mod tests;
+pub mod weights;
+
+pub struct WithMaximumOf<A: TypedGet>(sp_std::marker::PhantomData<A>);
+impl<A: TypedGet> Convert<Perquintill, A::Type> for WithMaximumOf<A>
+where
+	A::Type: Clone + Unsigned + From<u64>,
+	u64: TryFrom<A::Type>,
+{
+	fn convert(a: Perquintill) -> A::Type {
+		a * A::get()
+	}
+}
+impl<A: TypedGet> ConvertBack<Perquintill, A::Type> for WithMaximumOf<A>
+where
+	A::Type: RationalArg + From<u64>,
+	u64: TryFrom<A::Type>,
+	u128: TryFrom<A::Type>,
+{
+	fn convert_back(a: A::Type) -> Perquintill {
+		Perquintill::from_rational(a, A::get())
+	}
+}
+
+pub struct NoCounterpart<T>(sp_std::marker::PhantomData<T>);
+impl<T> FungibleInspect<T> for NoCounterpart<T> {
+	type Balance = u32;
+	fn total_issuance() -> u32 {
+		0
+	}
+	fn minimum_balance() -> u32 {
+		0
+	}
+	fn balance(_who: &T) -> u32 {
+		0
+	}
+	fn reducible_balance(_who: &T, _keep_alive: bool) -> u32 {
+		0
+	}
+	fn can_deposit(
+		_who: &T,
+		_amount: u32,
+		_mint: bool,
+	) -> frame_support::traits::tokens::DepositConsequence {
+		frame_support::traits::tokens::DepositConsequence::Success
+	}
+	fn can_withdraw(
+		_who: &T,
+		_amount: u32,
+	) -> frame_support::traits::tokens::WithdrawConsequence<u32> {
+		frame_support::traits::tokens::WithdrawConsequence::Success
+	}
+}
+impl<T> FungibleMutate<T> for NoCounterpart<T> {
+	fn mint_into(_who: &T, _amount: u32) -> DispatchResult {
+		Ok(())
+	}
+	fn burn_from(_who: &T, _amount: u32) -> Result<u32, DispatchError> {
+		Ok(0)
+	}
+}
+impl<T> Convert<Perquintill, u32> for NoCounterpart<T> {
+	fn convert(_: Perquintill) -> u32 {
+		0
+	}
+}
+
+#[frame_support::pallet]
+pub mod pallet {
+	use super::{FungibleInspect, FungibleMutate};
+	pub use crate::weights::WeightInfo;
+	use frame_support::{
+		pallet_prelude::*,
+		traits::{
+			nonfungible::{Inspect as NonfungibleInspect, Transfer as NonfungibleTransfer},
+			Currency, Defensive, DefensiveSaturating,
+			ExistenceRequirement::AllowDeath,
+			OnUnbalanced, ReservableCurrency,
+		},
+		PalletId,
+	};
+	use frame_system::pallet_prelude::*;
+	use sp_arithmetic::{PerThing, Perquintill};
+	use sp_runtime::{
+		traits::{AccountIdConversion, Bounded, Convert, ConvertBack, Saturating, Zero},
+		TokenError,
+	};
+	use sp_std::prelude::*;
+
+	type BalanceOf<T> =
+		<<T as Config>::Currency as Currency<<T as frame_system::Config>::AccountId>>::Balance;
+	type PositiveImbalanceOf<T> = <<T as Config>::Currency as Currency<
+		<T as frame_system::Config>::AccountId,
+	>>::PositiveImbalance;
+	type ReceiptRecordOf<T> = ReceiptRecord<
+		<T as frame_system::Config>::AccountId,
+		<T as frame_system::Config>::BlockNumber,
+	>;
+	type IssuanceInfoOf<T> = IssuanceInfo<BalanceOf<T>>;
+	type SummaryRecordOf<T> = SummaryRecord<<T as frame_system::Config>::BlockNumber>;
+	type BidOf<T> = Bid<BalanceOf<T>, <T as frame_system::Config>::AccountId>;
+	type QueueTotalsTypeOf<T> = BoundedVec<(u32, BalanceOf<T>), <T as Config>::QueueCount>;
+
+	#[pallet::config]
+	pub trait Config: frame_system::Config {
+		/// Information on runtime weights.
+		type WeightInfo: WeightInfo;
+
+		/// Overarching event type.
+		type RuntimeEvent: From<Event<Self>> + IsType<<Self as frame_system::Config>::RuntimeEvent>;
+
+		/// The treasury's pallet id, used for deriving its sovereign account ID.
+		#[pallet::constant]
+		type PalletId: Get<PalletId>;
+
+		/// Currency type that this works on.
+		type Currency: ReservableCurrency<Self::AccountId, Balance = Self::CurrencyBalance>;
+
+		/// Just the `Currency::Balance` type; we have this item to allow us to constrain it to
+		/// `From<u64>`.
+		type CurrencyBalance: sp_runtime::traits::AtLeast32BitUnsigned
+			+ codec::FullCodec
+			+ Copy
+			+ MaybeSerializeDeserialize
+			+ sp_std::fmt::Debug
+			+ Default
+			+ From<u64>
+			+ TypeInfo
+			+ MaxEncodedLen;
+
+		/// Origin required for auto-funding the deficit.
+		type FundOrigin: EnsureOrigin<Self::RuntimeOrigin>;
+
+		/// The issuance to ignore. This is subtracted from the `Currency`'s `total_issuance` to get
+		/// the issuance with which we determine the thawed value of a given proportion.
+		type IgnoredIssuance: Get<BalanceOf<Self>>;
+
+		/// The accounting system for the fungible counterpart tokens.
+		type Counterpart: FungibleMutate<Self::AccountId>;
+
+		/// The system to convert an overall proportion of issuance into a number of fungible
+		/// counterpart tokens.
+		///
+		/// In general it's best to use `WithMaximumOf`.
+		type CounterpartAmount: ConvertBack<
+			Perquintill,
+			<Self::Counterpart as FungibleInspect<Self::AccountId>>::Balance,
+		>;
+
+		/// Unbalanced handler to account for funds created (in case of a higher total issuance over
+		/// freezing period).
+		type Deficit: OnUnbalanced<PositiveImbalanceOf<Self>>;
+
+		/// The target sum of all receipts' proportions.
+		type Target: Get<Perquintill>;
+
+		/// Number of duration queues in total. This sets the maximum duration supported, which is
+		/// this value multiplied by `Period`.
+		#[pallet::constant]
+		type QueueCount: Get<u32>;
+
+		/// Maximum number of items that may be in each duration queue.
+		///
+		/// Must be larger than zero.
+		#[pallet::constant]
+		type MaxQueueLen: Get<u32>;
+
+		/// Portion of the queue which is free from ordering and just a FIFO.
+		///
+		/// Must be no greater than `MaxQueueLen`.
+		#[pallet::constant]
+		type FifoQueueLen: Get<u32>;
+
+		/// The base period for the duration queues. This is the common multiple across all
+		/// supported freezing durations that can be bid upon.
+		#[pallet::constant]
+		type BasePeriod: Get<Self::BlockNumber>;
+
+		/// The minimum amount of funds that may be placed in a bid. Note that this
+		/// does not actually limit the amount which may be represented in a receipt since bids may
+		/// be split up by the system.
+		///
+		/// It should be at least big enough to ensure that there is no possible storage spam attack
+		/// or queue-filling attack.
+		#[pallet::constant]
+		type MinBid: Get<BalanceOf<Self>>;
+
+		/// The minimum amount of funds which may intentionally be left remaining under a single
+		/// receipt.
+		#[pallet::constant]
+		type MinReceipt: Get<Perquintill>;
+
+		/// The number of blocks between consecutive attempts to dequeue bids and create receipts.
+		///
+		/// A larger value results in fewer storage hits each block, but a slower period to get to
+		/// the target.
+		#[pallet::constant]
+		type IntakePeriod: Get<Self::BlockNumber>;
+
+		/// The maximum amount of bids that can consolidated into receipts in a single intake. A
+		/// larger value here means less of the block available for transactions should there be a
+		/// glut of bids.
+		#[pallet::constant]
+		type MaxIntakeWeight: Get<Weight>;
+
+		/// The maximum proportion which may be thawed and the period over which it is reset.
+		#[pallet::constant]
+		type ThawThrottle: Get<(Perquintill, Self::BlockNumber)>;
+	}
+
+	#[pallet::pallet]
+	#[pallet::generate_store(pub(super) trait Store)]
+	pub struct Pallet<T>(_);
+
+	/// A single bid, an item of a *queue* in `Queues`.
+	#[derive(
+		Clone, Eq, PartialEq, Default, Encode, Decode, RuntimeDebug, TypeInfo, MaxEncodedLen,
+	)]
+	pub struct Bid<Balance, AccountId> {
+		/// The amount bid.
+		pub amount: Balance,
+		/// The owner of the bid.
+		pub who: AccountId,
+	}
+
+	/// Information representing a receipt.
+	#[derive(
+		Clone, Eq, PartialEq, Default, Encode, Decode, RuntimeDebug, TypeInfo, MaxEncodedLen,
+	)]
+	pub struct ReceiptRecord<AccountId, BlockNumber> {
+		/// The proportion of the effective total issuance.
+		pub proportion: Perquintill,
+		/// The account to whom this receipt belongs.
+		pub who: AccountId,
+		/// The time after which this receipt can be thawed.
+		pub expiry: BlockNumber,
+	}
+
+	/// An index for a receipt.
+	pub type ReceiptIndex = u32;
+
+	/// Overall information package on the outstanding receipts.
+	///
+	/// The way of determining the net issuance (i.e. after factoring in all maturing frozen funds)
+	/// is:
+	///
+	/// `issuance - frozen + proportion * issuance`
+	///
+	/// where `issuance = total_issuance - IgnoredIssuance`
+	#[derive(
+		Clone, Eq, PartialEq, Default, Encode, Decode, RuntimeDebug, TypeInfo, MaxEncodedLen,
+	)]
+	pub struct SummaryRecord<BlockNumber> {
+		/// The total proportion over all outstanding receipts.
+		pub proportion_owed: Perquintill,
+		/// The total number of receipts created so far.
+		pub index: ReceiptIndex,
+		/// The amount (as a proportion of ETI) which has been thawed in this period so far.
+		pub thawed: Perquintill,
+		/// The current thaw period's beginning.
+		pub last_period: BlockNumber,
+	}
+
+	pub struct OnEmptyQueueTotals<T>(sp_std::marker::PhantomData<T>);
+	impl<T: Config> Get<QueueTotalsTypeOf<T>> for OnEmptyQueueTotals<T> {
+		fn get() -> QueueTotalsTypeOf<T> {
+			BoundedVec::truncate_from(vec![
+				(0, Zero::zero());
+				<T as Config>::QueueCount::get() as usize
+			])
+		}
+	}
+
+	/// The totals of items and balances within each queue. Saves a lot of storage reads in the
+	/// case of sparsely packed queues.
+	///
+	/// The vector is indexed by duration in `Period`s, offset by one, so information on the queue
+	/// whose duration is one `Period` would be storage `0`.
+	#[pallet::storage]
+	pub type QueueTotals<T: Config> =
+		StorageValue<_, QueueTotalsTypeOf<T>, ValueQuery, OnEmptyQueueTotals<T>>;
+
+	/// The queues of bids. Indexed by duration (in `Period`s).
+	#[pallet::storage]
+	pub type Queues<T: Config> =
+		StorageMap<_, Blake2_128Concat, u32, BoundedVec<BidOf<T>, T::MaxQueueLen>, ValueQuery>;
+
+	/// Summary information over the general state.
+	#[pallet::storage]
+	pub type Summary<T> = StorageValue<_, SummaryRecordOf<T>, ValueQuery>;
+
+	/// The currently outstanding receipts, indexed according to the order of creation.
+	#[pallet::storage]
+	pub type Receipts<T> =
+		StorageMap<_, Blake2_128Concat, ReceiptIndex, ReceiptRecordOf<T>, OptionQuery>;
+
+	#[pallet::event]
+	#[pallet::generate_deposit(pub(super) fn deposit_event)]
+	pub enum Event<T: Config> {
+		/// A bid was successfully placed.
+		BidPlaced { who: T::AccountId, amount: BalanceOf<T>, duration: u32 },
+		/// A bid was successfully removed (before being accepted).
+		BidRetracted { who: T::AccountId, amount: BalanceOf<T>, duration: u32 },
+		/// A bid was dropped from a queue because of another, more substantial, bid was present.
+		BidDropped { who: T::AccountId, amount: BalanceOf<T>, duration: u32 },
+		/// A bid was accepted. The balance may not be released until expiry.
+		Issued {
+			/// The identity of the receipt.
+			index: ReceiptIndex,
+			/// The block number at which the receipt may be thawed.
+			expiry: T::BlockNumber,
+			/// The owner of the receipt.
+			who: T::AccountId,
+			/// The proportion of the effective total issuance which the receipt represents.
+			proportion: Perquintill,
+			/// The amount of funds which were debited from the owner.
+			amount: BalanceOf<T>,
+		},
+		/// An receipt has been (at least partially) thawed.
+		Thawed {
+			/// The identity of the receipt.
+			index: ReceiptIndex,
+			/// The owner.
+			who: T::AccountId,
+			/// The proportion of the effective total issuance by which the owner was debited.
+			proportion: Perquintill,
+			/// The amount by which the owner was credited.
+			amount: BalanceOf<T>,
+			/// If `true` then the receipt is done.
+			dropped: bool,
+		},
+		/// An automatic funding of the deficit was made.
+		Funded { deficit: BalanceOf<T> },
+		/// A receipt was transfered.
+		Transferred { from: T::AccountId, to: T::AccountId, index: ReceiptIndex },
+	}
+
+	#[pallet::error]
+	pub enum Error<T> {
+		/// The duration of the bid is less than one.
+		DurationTooSmall,
+		/// The duration is the bid is greater than the number of queues.
+		DurationTooBig,
+		/// The amount of the bid is less than the minimum allowed.
+		AmountTooSmall,
+		/// The queue for the bid's duration is full and the amount bid is too low to get in
+		/// through replacing an existing bid.
+		BidTooLow,
+		/// Bond index is unknown.
+		Unknown,
+		/// Not the owner of the receipt.
+		NotOwner,
+		/// Bond not yet at expiry date.
+		NotExpired,
+		/// The given bid for retraction is not found.
+		NotFound,
+		/// The portion supplied is beyond the value of the receipt.
+		TooMuch,
+		/// Not enough funds are held to pay out.
+		Unfunded,
+		/// There are enough funds for what is required.
+		Funded,
+		/// The thaw throttle has been reached for this period.
+		Throttled,
+		/// The operation would result in a receipt worth an insignficant value.
+		MakesDust,
+	}
+
+	pub(crate) struct WeightCounter {
+		pub(crate) used: Weight,
+		pub(crate) limit: Weight,
+	}
+	impl WeightCounter {
+		#[allow(dead_code)]
+		pub(crate) fn unlimited() -> Self {
+			WeightCounter { used: Weight::zero(), limit: Weight::max_value() }
+		}
+		fn check_accrue(&mut self, w: Weight) -> bool {
+			let test = self.used.saturating_add(w);
+			if test.any_gt(self.limit) {
+				false
+			} else {
+				self.used = test;
+				true
+			}
+		}
+		fn can_accrue(&mut self, w: Weight) -> bool {
+			self.used.saturating_add(w).all_lte(self.limit)
+		}
+	}
+
+	#[pallet::hooks]
+	impl<T: Config> Hooks<BlockNumberFor<T>> for Pallet<T> {
+		fn on_initialize(n: T::BlockNumber) -> Weight {
+			let mut weight_counter =
+				WeightCounter { used: Weight::zero(), limit: T::MaxIntakeWeight::get() };
+			if T::IntakePeriod::get().is_zero() || (n % T::IntakePeriod::get()).is_zero() {
+				if weight_counter.check_accrue(T::WeightInfo::process_queues()) {
+					Self::process_queues(
+						T::Target::get(),
+						T::QueueCount::get(),
+						u32::max_value(),
+						&mut weight_counter,
+					)
+				}
+			}
+			weight_counter.used
+		}
+
+		fn integrity_test() {
+			assert!(!T::IntakePeriod::get().is_zero());
+			assert!(!T::MaxQueueLen::get().is_zero());
+		}
+	}
+
+	#[pallet::call]
+	impl<T: Config> Pallet<T> {
+		/// Place a bid.
+		///
+		/// Origin must be Signed, and account must have at least `amount` in free balance.
+		///
+		/// - `amount`: The amount of the bid; these funds will be reserved, and if/when
+		///   consolidated, removed. Must be at least `MinBid`.
+		/// - `duration`: The number of periods before which the newly consolidated bid may be
+		///   thawed. Must be greater than 1 and no more than `QueueCount`.
+		///
+		/// Complexities:
+		/// - `Queues[duration].len()` (just take max).
+		#[pallet::weight(T::WeightInfo::place_bid_max())]
+		pub fn place_bid(
+			origin: OriginFor<T>,
+			#[pallet::compact] amount: BalanceOf<T>,
+			duration: u32,
+		) -> DispatchResult {
+			let who = ensure_signed(origin)?;
+
+			ensure!(amount >= T::MinBid::get(), Error::<T>::AmountTooSmall);
+			let queue_count = T::QueueCount::get() as usize;
+			let queue_index = duration.checked_sub(1).ok_or(Error::<T>::DurationTooSmall)? as usize;
+			ensure!(queue_index < queue_count, Error::<T>::DurationTooBig);
+
+			let net = Queues::<T>::try_mutate(
+				duration,
+				|q| -> Result<(u32, BalanceOf<T>), DispatchError> {
+					let queue_full = q.len() == T::MaxQueueLen::get() as usize;
+					ensure!(!queue_full || q[0].amount < amount, Error::<T>::BidTooLow);
+					T::Currency::reserve(&who, amount)?;
+
+					// queue is <Ordered: Lowest ... Highest><Fifo: Last ... First>
+					let mut bid = Bid { amount, who: who.clone() };
+					let net = if queue_full {
+						sp_std::mem::swap(&mut q[0], &mut bid);
+						let _ = T::Currency::unreserve(&bid.who, bid.amount);
+						Self::deposit_event(Event::<T>::BidDropped {
+							who: bid.who,
+							amount: bid.amount,
+							duration,
+						});
+						(0, amount - bid.amount)
+					} else {
+						q.try_insert(0, bid).expect("verified queue was not full above. qed.");
+						(1, amount)
+					};
+
+					let sorted_item_count = q.len().saturating_sub(T::FifoQueueLen::get() as usize);
+					if sorted_item_count > 1 {
+						q[0..sorted_item_count].sort_by_key(|x| x.amount);
+					}
+
+					Ok(net)
+				},
+			)?;
+			QueueTotals::<T>::mutate(|qs| {
+				qs.bounded_resize(queue_count, (0, Zero::zero()));
+				qs[queue_index].0 += net.0;
+				qs[queue_index].1.saturating_accrue(net.1);
+			});
+			Self::deposit_event(Event::BidPlaced { who, amount, duration });
+
+			Ok(())
+		}
+
+		/// Retract a previously placed bid.
+		///
+		/// Origin must be Signed, and the account should have previously issued a still-active bid
+		/// of `amount` for `duration`.
+		///
+		/// - `amount`: The amount of the previous bid.
+		/// - `duration`: The duration of the previous bid.
+		#[pallet::weight(T::WeightInfo::retract_bid(T::MaxQueueLen::get()))]
+		pub fn retract_bid(
+			origin: OriginFor<T>,
+			#[pallet::compact] amount: BalanceOf<T>,
+			duration: u32,
+		) -> DispatchResult {
+			let who = ensure_signed(origin)?;
+
+			let queue_count = T::QueueCount::get() as usize;
+			let queue_index = duration.checked_sub(1).ok_or(Error::<T>::DurationTooSmall)? as usize;
+			ensure!(queue_index < queue_count, Error::<T>::DurationTooBig);
+
+			let bid = Bid { amount, who };
+			let new_len = Queues::<T>::try_mutate(duration, |q| -> Result<u32, DispatchError> {
+				let pos = q.iter().position(|i| i == &bid).ok_or(Error::<T>::NotFound)?;
+				q.remove(pos);
+				Ok(q.len() as u32)
+			})?;
+
+			QueueTotals::<T>::mutate(|qs| {
+				qs.bounded_resize(queue_count, (0, Zero::zero()));
+				qs[queue_index].0 = new_len;
+				qs[queue_index].1.saturating_reduce(bid.amount);
+			});
+
+			T::Currency::unreserve(&bid.who, bid.amount);
+			Self::deposit_event(Event::BidRetracted { who: bid.who, amount: bid.amount, duration });
+
+			Ok(())
+		}
+
+		/// Ensure we have sufficient funding for all potential payouts.
+		///
+		/// - `origin`: Must be accepted by `FundOrigin`.
+		#[pallet::weight(T::WeightInfo::fund_deficit())]
+		pub fn fund_deficit(origin: OriginFor<T>) -> DispatchResult {
+			T::FundOrigin::ensure_origin(origin)?;
+			let summary: SummaryRecordOf<T> = Summary::<T>::get();
+			let our_account = Self::account_id();
+			let issuance = Self::issuance_with(&our_account, &summary);
+			let deficit = issuance.required.saturating_sub(issuance.holdings);
+			ensure!(!deficit.is_zero(), Error::<T>::Funded);
+			T::Deficit::on_unbalanced(T::Currency::deposit_creating(&our_account, deficit));
+			Self::deposit_event(Event::<T>::Funded { deficit });
+			Ok(())
+		}
+
+		/// Reduce or remove an outstanding receipt, placing the according proportion of funds into
+		/// the account of the owner.
+		///
+		/// - `origin`: Must be Signed and the account must be the owner of the receipt `index` as
+		///   well as any fungible counterpart.
+		/// - `index`: The index of the receipt.
+		/// - `portion`: If `Some`, then only the given portion of the receipt should be thawed. If
+		///   `None`, then all of it should be.
+		#[pallet::weight(T::WeightInfo::thaw())]
+		pub fn thaw(
+			origin: OriginFor<T>,
+			#[pallet::compact] index: ReceiptIndex,
+			portion: Option<<T::Counterpart as FungibleInspect<T::AccountId>>::Balance>,
+		) -> DispatchResult {
+			let who = ensure_signed(origin)?;
+
+			// Look for `index`
+			let mut receipt: ReceiptRecordOf<T> =
+				Receipts::<T>::get(index).ok_or(Error::<T>::Unknown)?;
+			// If found, check the owner is `who`.
+			ensure!(receipt.who == who, Error::<T>::NotOwner);
+			let now = frame_system::Pallet::<T>::block_number();
+			ensure!(now >= receipt.expiry, Error::<T>::NotExpired);
+
+			let mut summary: SummaryRecordOf<T> = Summary::<T>::get();
+
+			let proportion = if let Some(counterpart) = portion {
+				let proportion = T::CounterpartAmount::convert_back(counterpart);
+				ensure!(proportion <= receipt.proportion, Error::<T>::TooMuch);
+				let remaining = receipt.proportion.saturating_sub(proportion);
+				ensure!(
+					remaining.is_zero() || remaining >= T::MinReceipt::get(),
+					Error::<T>::MakesDust
+				);
+				proportion
+			} else {
+				receipt.proportion
+			};
+
+			let (throttle, throttle_period) = T::ThawThrottle::get();
+			if now.saturating_sub(summary.last_period) >= throttle_period {
+				summary.thawed = Zero::zero();
+				summary.last_period = now;
+			}
+			summary.thawed.saturating_accrue(proportion);
+			ensure!(summary.thawed <= throttle, Error::<T>::Throttled);
+
+			T::Counterpart::burn_from(&who, T::CounterpartAmount::convert(proportion))?;
+
+			// Multiply the proportion it is by the total issued.
+			let our_account = Self::account_id();
+			let effective_issuance = Self::issuance_with(&our_account, &summary).effective;
+			let amount = proportion * effective_issuance;
+
+			receipt.proportion.saturating_reduce(proportion);
+			summary.proportion_owed.saturating_reduce(proportion);
+
+			T::Currency::transfer(&our_account, &who, amount, AllowDeath)
+				.map_err(|_| Error::<T>::Unfunded)?;
+
+			let dropped = receipt.proportion.is_zero();
+			if dropped {
+				Receipts::<T>::remove(index);
+			} else {
+				Receipts::<T>::insert(index, &receipt);
+			}
+			Summary::<T>::put(&summary);
+
+			Self::deposit_event(Event::Thawed { index, who, amount, proportion, dropped });
+
+			Ok(())
+		}
+	}
+
+	/// Issuance information returned by `issuance()`.
+	#[derive(RuntimeDebug)]
+	pub struct IssuanceInfo<Balance> {
+		/// The balance held in reserve by this pallet instance.
+		pub holdings: Balance,
+		/// The (non-ignored) issuance in the system, not including this pallet's account.
+		pub other: Balance,
+		/// The effective total issuance, hypothetically if all outstanding receipts were thawed at
+		/// present.
+		pub effective: Balance,
+		/// The amount needed to be the pallet instance's account in case all outstanding receipts
+		/// were thawed at present.
+		pub required: Balance,
+	}
+
+	impl<T: Config> NonfungibleInspect<T::AccountId> for Pallet<T> {
+		type ItemId = ReceiptIndex;
+
+		fn owner(item: &ReceiptIndex) -> Option<T::AccountId> {
+			Receipts::<T>::get(item).map(|r| r.who)
+		}
+
+		fn attribute(item: &Self::ItemId, key: &[u8]) -> Option<Vec<u8>> {
+			let item = Receipts::<T>::get(item)?;
+			match key {
+				b"proportion" => Some(item.proportion.encode()),
+				b"expiry" => Some(item.expiry.encode()),
+				_ => None,
+			}
+		}
+	}
+
+	impl<T: Config> NonfungibleTransfer<T::AccountId> for Pallet<T> {
+		fn transfer(index: &ReceiptIndex, destination: &T::AccountId) -> DispatchResult {
+			let mut item = Receipts::<T>::get(index).ok_or(TokenError::UnknownAsset)?;
+			let from = item.who;
+			item.who = destination.clone();
+			Receipts::<T>::insert(&index, &item);
+			Pallet::<T>::deposit_event(Event::<T>::Transferred {
+				from,
+				to: item.who,
+				index: *index,
+			});
+			Ok(())
+		}
+	}
+
+	impl<T: Config> Pallet<T> {
+		/// The account ID of the reserves.
+		///
+		/// This actually does computation. If you need to keep using it, then make sure you cache
+		/// the value and only call this once.
+		pub fn account_id() -> T::AccountId {
+			T::PalletId::get().into_account_truncating()
+		}
+
+		/// Returns information on the issuance within the system.
+		pub fn issuance() -> IssuanceInfo<BalanceOf<T>> {
+			Self::issuance_with(&Self::account_id(), &Summary::<T>::get())
+		}
+
+		/// Returns information on the issuance within the system
+		///
+		/// This function is equivalent to `issuance`, except that it accepts arguments rather than
+		/// queries state. If the arguments are already known, then this may be slightly more
+		/// performant.
+		///
+		/// - `our_account`: The value returned by `Self::account_id()`.
+		/// - `summary`: The value returned by `Summary::<T>::get()`.
+		pub fn issuance_with(
+			our_account: &T::AccountId,
+			summary: &SummaryRecordOf<T>,
+		) -> IssuanceInfo<BalanceOf<T>> {
+			let total_issuance =
+				T::Currency::total_issuance().saturating_sub(T::IgnoredIssuance::get());
+			let holdings = T::Currency::free_balance(our_account);
+			let other = total_issuance.saturating_sub(holdings);
+			let effective =
+				summary.proportion_owed.left_from_one().saturating_reciprocal_mul(other);
+			let required = summary.proportion_owed * effective;
+			IssuanceInfo { holdings, other, effective, required }
+		}
+
+		/// Process some bids into receipts up to a `target` total of all receipts.
+		///
+		/// Touch at most `max_queues`.
+		///
+		/// Return the weight used.
+		pub(crate) fn process_queues(
+			target: Perquintill,
+			max_queues: u32,
+			max_bids: u32,
+			weight: &mut WeightCounter,
+		) {
+			let mut summary: SummaryRecordOf<T> = Summary::<T>::get();
+			if summary.proportion_owed >= target {
+				return
+			}
+
+			let now = frame_system::Pallet::<T>::block_number();
+			let our_account = Self::account_id();
+			let issuance: IssuanceInfoOf<T> = Self::issuance_with(&our_account, &summary);
+			let mut remaining = target.saturating_sub(summary.proportion_owed) * issuance.effective;
+
+			let mut queues_hit = 0;
+			let mut bids_hit = 0;
+			let mut totals = QueueTotals::<T>::get();
+			let queue_count = T::QueueCount::get();
+			totals.bounded_resize(queue_count as usize, (0, Zero::zero()));
+			for duration in (1..=queue_count).rev() {
+				if totals[duration as usize - 1].0.is_zero() {
+					continue
+				}
+				if remaining.is_zero() || queues_hit >= max_queues
+					|| !weight.check_accrue(T::WeightInfo::process_queue())
+					// No point trying to process a queue if we can't process a single bid.
+					|| !weight.can_accrue(T::WeightInfo::process_bid())
+				{
+					break
+				}
+
+				let b = Self::process_queue(
+					duration,
+					now,
+					&our_account,
+					&issuance,
+					max_bids.saturating_sub(bids_hit),
+					&mut remaining,
+					&mut totals[duration as usize - 1],
+					&mut summary,
+					weight,
+				);
+
+				bids_hit.saturating_accrue(b);
+				queues_hit.saturating_inc();
+			}
+			QueueTotals::<T>::put(&totals);
+			Summary::<T>::put(&summary);
+		}
+
+		pub(crate) fn process_queue(
+			duration: u32,
+			now: T::BlockNumber,
+			our_account: &T::AccountId,
+			issuance: &IssuanceInfo<BalanceOf<T>>,
+			max_bids: u32,
+			remaining: &mut BalanceOf<T>,
+			queue_total: &mut (u32, BalanceOf<T>),
+			summary: &mut SummaryRecordOf<T>,
+			weight: &mut WeightCounter,
+		) -> u32 {
+			let mut queue: BoundedVec<BidOf<T>, _> = Queues::<T>::get(&duration);
+			let expiry = now.saturating_add(T::BasePeriod::get().saturating_mul(duration.into()));
+			let mut count = 0;
+
+			while count < max_bids &&
+				!queue.is_empty() &&
+				!remaining.is_zero() &&
+				weight.check_accrue(T::WeightInfo::process_bid())
+			{
+				let bid = match queue.pop() {
+					Some(b) => b,
+					None => break,
+				};
+				if let Some(bid) = Self::process_bid(
+					bid,
+					expiry,
+					our_account,
+					issuance,
+					remaining,
+					&mut queue_total.1,
+					summary,
+				) {
+					queue.try_push(bid).expect("just popped, so there must be space. qed");
+					// This should exit at the next iteration (though nothing will break if it
+					// doesn't).
+				}
+				count.saturating_inc();
+			}
+			queue_total.0 = queue.len() as u32;
+			Queues::<T>::insert(&duration, &queue);
+			count
+		}
+
+		pub(crate) fn process_bid(
+			mut bid: BidOf<T>,
+			expiry: T::BlockNumber,
+			our_account: &T::AccountId,
+			issuance: &IssuanceInfo<BalanceOf<T>>,
+			remaining: &mut BalanceOf<T>,
+			queue_amount: &mut BalanceOf<T>,
+			summary: &mut SummaryRecordOf<T>,
+		) -> Option<BidOf<T>> {
+			let result = if *remaining < bid.amount {
+				let overflow = bid.amount - *remaining;
+				bid.amount = *remaining;
+				Some(Bid { amount: overflow, who: bid.who.clone() })
+			} else {
+				None
+			};
+			let amount = bid.amount.saturating_sub(T::Currency::unreserve(&bid.who, bid.amount));
+			if T::Currency::transfer(&bid.who, &our_account, amount, AllowDeath).is_err() {
+				return result
+			}
+
+			// Can never overflow due to block above.
+			remaining.saturating_reduce(amount);
+			// Should never underflow since it should track the total of the
+			// bids exactly, but we'll be defensive.
+			queue_amount.defensive_saturating_reduce(amount);
+
+			// Now to activate the bid...
+			let n = amount;
+			let d = issuance.effective;
+			let proportion = Perquintill::from_rational(n, d);
+			let who = bid.who;
+			let index = summary.index;
+			summary.proportion_owed.defensive_saturating_accrue(proportion);
+			summary.index += 1;
+
+			let e = Event::Issued { index, expiry, who: who.clone(), amount, proportion };
+			Self::deposit_event(e);
+			let receipt = ReceiptRecord { proportion, who: who.clone(), expiry };
+			Receipts::<T>::insert(index, receipt);
+
+			// issue the fungible counterpart
+			let fung_eq = T::CounterpartAmount::convert(proportion);
+			let _ = T::Counterpart::mint_into(&who, fung_eq).defensive();
+			result
+		}
+	}
+}
diff --git a/substrate/frame/gilt/src/mock.rs b/substrate/frame/nis/src/mock.rs
similarity index 63%
rename from substrate/frame/gilt/src/mock.rs
rename to substrate/frame/nis/src/mock.rs
index e1cdf6507ef..ebe073d6833 100644
--- a/substrate/frame/gilt/src/mock.rs
+++ b/substrate/frame/nis/src/mock.rs
@@ -15,15 +15,18 @@
 // See the License for the specific language governing permissions and
 // limitations under the License.
 
-//! Test environment for Gilt pallet.
+//! Test environment for NIS pallet.
 
-use crate as pallet_gilt;
+use crate::{self as pallet_nis, Perquintill, WithMaximumOf};
 
 use frame_support::{
 	ord_parameter_types, parameter_types,
-	traits::{ConstU16, ConstU32, ConstU64, Currency, GenesisBuild, OnFinalize, OnInitialize},
+	traits::{ConstU16, ConstU32, ConstU64, Currency, OnFinalize, OnInitialize, StorageMapShim},
+	weights::Weight,
+	PalletId,
 };
-use sp_core::H256;
+use pallet_balances::{Instance1, Instance2};
+use sp_core::{ConstU128, H256};
 use sp_runtime::{
 	testing::Header,
 	traits::{BlakeTwo256, IdentityLookup},
@@ -39,9 +42,10 @@ frame_support::construct_runtime!(
 		NodeBlock = Block,
 		UncheckedExtrinsic = UncheckedExtrinsic,
 	{
-		System: frame_system::{Pallet, Call, Config, Storage, Event<T>},
-		Balances: pallet_balances::{Pallet, Call, Config<T>, Storage, Event<T>},
-		Gilt: pallet_gilt::{Pallet, Call, Config, Storage, Event<T>},
+		System: frame_system,
+		Balances: pallet_balances::<Instance1>,
+		NisBalances: pallet_balances::<Instance2>,
+		Nis: pallet_nis,
 	}
 );
 
@@ -72,7 +76,7 @@ impl frame_system::Config for Test {
 	type MaxConsumers = frame_support::traits::ConstU32<16>;
 }
 
-impl pallet_balances::Config for Test {
+impl pallet_balances::Config<Instance1> for Test {
 	type Balance = u64;
 	type DustRemoval = ();
 	type RuntimeEvent = RuntimeEvent;
@@ -84,52 +88,79 @@ impl pallet_balances::Config for Test {
 	type ReserveIdentifier = [u8; 8];
 }
 
+impl pallet_balances::Config<Instance2> for Test {
+	type Balance = u128;
+	type DustRemoval = ();
+	type RuntimeEvent = RuntimeEvent;
+	type ExistentialDeposit = frame_support::traits::ConstU128<1>;
+	type AccountStore = StorageMapShim<
+		pallet_balances::Account<Test, Instance2>,
+		frame_system::Provider<Test>,
+		u64,
+		pallet_balances::AccountData<u128>,
+	>;
+	type WeightInfo = ();
+	type MaxLocks = ();
+	type MaxReserves = ();
+	type ReserveIdentifier = [u8; 8];
+}
+
 parameter_types! {
 	pub IgnoredIssuance: u64 = Balances::total_balance(&0); // Account zero is ignored.
+	pub const NisPalletId: PalletId = PalletId(*b"py/nis  ");
+	pub static Target: Perquintill = Perquintill::zero();
+	pub const MinReceipt: Perquintill = Perquintill::from_percent(1);
+	pub const ThawThrottle: (Perquintill, u64) = (Perquintill::from_percent(25), 5);
+	pub static MaxIntakeWeight: Weight = Weight::from_ref_time(2_000_000_000_000);
 }
+
 ord_parameter_types! {
 	pub const One: u64 = 1;
 }
 
-impl pallet_gilt::Config for Test {
+impl pallet_nis::Config for Test {
+	type WeightInfo = ();
 	type RuntimeEvent = RuntimeEvent;
+	type PalletId = NisPalletId;
 	type Currency = Balances;
-	type CurrencyBalance = <Self as pallet_balances::Config>::Balance;
-	type AdminOrigin = frame_system::EnsureSignedBy<One, Self::AccountId>;
+	type CurrencyBalance = <Self as pallet_balances::Config<Instance1>>::Balance;
+	type FundOrigin = frame_system::EnsureSigned<Self::AccountId>;
 	type Deficit = ();
-	type Surplus = ();
 	type IgnoredIssuance = IgnoredIssuance;
+	type Counterpart = NisBalances;
+	type CounterpartAmount = WithMaximumOf<ConstU128<21_000_000u128>>;
+	type Target = Target;
 	type QueueCount = ConstU32<3>;
 	type MaxQueueLen = ConstU32<3>;
 	type FifoQueueLen = ConstU32<1>;
-	type Period = ConstU64<3>;
-	type MinFreeze = ConstU64<2>;
+	type BasePeriod = ConstU64<3>;
+	type MinBid = ConstU64<2>;
 	type IntakePeriod = ConstU64<2>;
-	type MaxIntakeBids = ConstU32<2>;
-	type WeightInfo = ();
+	type MaxIntakeWeight = MaxIntakeWeight;
+	type MinReceipt = MinReceipt;
+	type ThawThrottle = ThawThrottle;
 }
 
 // This function basically just builds a genesis storage key/value store according to
 // our desired mockup.
 pub fn new_test_ext() -> sp_io::TestExternalities {
 	let mut t = frame_system::GenesisConfig::default().build_storage::<Test>().unwrap();
-	pallet_balances::GenesisConfig::<Test> {
+	pallet_balances::GenesisConfig::<Test, Instance1> {
 		balances: vec![(1, 100), (2, 100), (3, 100), (4, 100)],
 	}
 	.assimilate_storage(&mut t)
 	.unwrap();
-	GenesisBuild::<Test>::assimilate_storage(&crate::GenesisConfig, &mut t).unwrap();
 	t.into()
 }
 
 pub fn run_to_block(n: u64) {
 	while System::block_number() < n {
-		Gilt::on_finalize(System::block_number());
+		Nis::on_finalize(System::block_number());
 		Balances::on_finalize(System::block_number());
 		System::on_finalize(System::block_number());
 		System::set_block_number(System::block_number() + 1);
 		System::on_initialize(System::block_number());
 		Balances::on_initialize(System::block_number());
-		Gilt::on_initialize(System::block_number());
+		Nis::on_initialize(System::block_number());
 	}
 }
diff --git a/substrate/frame/nis/src/tests.rs b/substrate/frame/nis/src/tests.rs
new file mode 100644
index 00000000000..f0c45cc80b0
--- /dev/null
+++ b/substrate/frame/nis/src/tests.rs
@@ -0,0 +1,654 @@
+// This file is part of Substrate.
+
+// Copyright (C) 2019-2022 Parity Technologies (UK) Ltd.
+// SPDX-License-Identifier: Apache-2.0
+
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// 	http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+//! Tests for NIS pallet.
+
+use super::*;
+use crate::{mock::*, Error};
+use frame_support::{
+	assert_noop, assert_ok,
+	traits::{
+		nonfungible::{Inspect, Transfer},
+		Currency,
+	},
+};
+use pallet_balances::{Error as BalancesError, Instance1};
+use sp_arithmetic::Perquintill;
+use sp_runtime::{Saturating, TokenError};
+
+fn pot() -> u64 {
+	Balances::free_balance(&Nis::account_id())
+}
+
+fn enlarge(amount: u64, max_bids: u32) {
+	let summary: SummaryRecord<u64> = Summary::<Test>::get();
+	let increase_in_proportion_owed = Perquintill::from_rational(amount, Nis::issuance().effective);
+	let target = summary.proportion_owed.saturating_add(increase_in_proportion_owed);
+	Nis::process_queues(target, u32::max_value(), max_bids, &mut WeightCounter::unlimited());
+}
+
+#[test]
+fn basic_setup_works() {
+	new_test_ext().execute_with(|| {
+		run_to_block(1);
+
+		for q in 0..3 {
+			assert!(Queues::<Test>::get(q).is_empty());
+		}
+		assert_eq!(
+			Summary::<Test>::get(),
+			SummaryRecord {
+				proportion_owed: Perquintill::zero(),
+				index: 0,
+				last_period: 0,
+				thawed: Perquintill::zero()
+			}
+		);
+	});
+}
+
+#[test]
+fn place_bid_works() {
+	new_test_ext().execute_with(|| {
+		run_to_block(1);
+		assert_noop!(Nis::place_bid(RuntimeOrigin::signed(1), 1, 2), Error::<Test>::AmountTooSmall);
+		assert_noop!(
+			Nis::place_bid(RuntimeOrigin::signed(1), 101, 2),
+			BalancesError::<Test, Instance1>::InsufficientBalance
+		);
+		assert_noop!(
+			Nis::place_bid(RuntimeOrigin::signed(1), 10, 4),
+			Error::<Test>::DurationTooBig
+		);
+		assert_ok!(Nis::place_bid(RuntimeOrigin::signed(1), 10, 2));
+		assert_eq!(Balances::reserved_balance(1), 10);
+		assert_eq!(Queues::<Test>::get(2), vec![Bid { amount: 10, who: 1 }]);
+		assert_eq!(QueueTotals::<Test>::get(), vec![(0, 0), (1, 10), (0, 0)]);
+	});
+}
+
+#[test]
+fn place_bid_queuing_works() {
+	new_test_ext().execute_with(|| {
+		run_to_block(1);
+		assert_ok!(Nis::place_bid(RuntimeOrigin::signed(1), 20, 2));
+		assert_ok!(Nis::place_bid(RuntimeOrigin::signed(1), 10, 2));
+		assert_ok!(Nis::place_bid(RuntimeOrigin::signed(1), 5, 2));
+		assert_noop!(Nis::place_bid(RuntimeOrigin::signed(1), 5, 2), Error::<Test>::BidTooLow);
+		assert_ok!(Nis::place_bid(RuntimeOrigin::signed(1), 15, 2));
+		assert_eq!(Balances::reserved_balance(1), 45);
+
+		assert_ok!(Nis::place_bid(RuntimeOrigin::signed(1), 25, 2));
+		assert_eq!(Balances::reserved_balance(1), 60);
+		assert_noop!(Nis::place_bid(RuntimeOrigin::signed(1), 10, 2), Error::<Test>::BidTooLow);
+		assert_eq!(
+			Queues::<Test>::get(2),
+			vec![
+				Bid { amount: 15, who: 1 },
+				Bid { amount: 25, who: 1 },
+				Bid { amount: 20, who: 1 },
+			]
+		);
+		assert_eq!(QueueTotals::<Test>::get(), vec![(0, 0), (3, 60), (0, 0)]);
+	});
+}
+
+#[test]
+fn place_bid_fails_when_queue_full() {
+	new_test_ext().execute_with(|| {
+		run_to_block(1);
+		assert_ok!(Nis::place_bid(RuntimeOrigin::signed(1), 10, 2));
+		assert_ok!(Nis::place_bid(RuntimeOrigin::signed(2), 10, 2));
+		assert_ok!(Nis::place_bid(RuntimeOrigin::signed(3), 10, 2));
+		assert_noop!(Nis::place_bid(RuntimeOrigin::signed(4), 10, 2), Error::<Test>::BidTooLow);
+		assert_ok!(Nis::place_bid(RuntimeOrigin::signed(4), 10, 3));
+	});
+}
+
+#[test]
+fn multiple_place_bids_works() {
+	new_test_ext().execute_with(|| {
+		run_to_block(1);
+		assert_ok!(Nis::place_bid(RuntimeOrigin::signed(1), 10, 1));
+		assert_ok!(Nis::place_bid(RuntimeOrigin::signed(1), 10, 2));
+		assert_ok!(Nis::place_bid(RuntimeOrigin::signed(1), 10, 2));
+		assert_ok!(Nis::place_bid(RuntimeOrigin::signed(1), 10, 3));
+		assert_ok!(Nis::place_bid(RuntimeOrigin::signed(2), 10, 2));
+
+		assert_eq!(Balances::reserved_balance(1), 40);
+		assert_eq!(Balances::reserved_balance(2), 10);
+		assert_eq!(Queues::<Test>::get(1), vec![Bid { amount: 10, who: 1 },]);
+		assert_eq!(
+			Queues::<Test>::get(2),
+			vec![
+				Bid { amount: 10, who: 2 },
+				Bid { amount: 10, who: 1 },
+				Bid { amount: 10, who: 1 },
+			]
+		);
+		assert_eq!(Queues::<Test>::get(3), vec![Bid { amount: 10, who: 1 },]);
+		assert_eq!(QueueTotals::<Test>::get(), vec![(1, 10), (3, 30), (1, 10)]);
+	});
+}
+
+#[test]
+fn retract_single_item_queue_works() {
+	new_test_ext().execute_with(|| {
+		run_to_block(1);
+		assert_ok!(Nis::place_bid(RuntimeOrigin::signed(1), 10, 1));
+		assert_ok!(Nis::place_bid(RuntimeOrigin::signed(1), 10, 2));
+		assert_ok!(Nis::retract_bid(RuntimeOrigin::signed(1), 10, 1));
+
+		assert_eq!(Balances::reserved_balance(1), 10);
+		assert_eq!(Queues::<Test>::get(1), vec![]);
+		assert_eq!(Queues::<Test>::get(2), vec![Bid { amount: 10, who: 1 }]);
+		assert_eq!(QueueTotals::<Test>::get(), vec![(0, 0), (1, 10), (0, 0)]);
+	});
+}
+
+#[test]
+fn retract_with_other_and_duplicate_works() {
+	new_test_ext().execute_with(|| {
+		run_to_block(1);
+		assert_ok!(Nis::place_bid(RuntimeOrigin::signed(1), 10, 1));
+		assert_ok!(Nis::place_bid(RuntimeOrigin::signed(1), 10, 2));
+		assert_ok!(Nis::place_bid(RuntimeOrigin::signed(1), 10, 2));
+		assert_ok!(Nis::place_bid(RuntimeOrigin::signed(2), 10, 2));
+
+		assert_ok!(Nis::retract_bid(RuntimeOrigin::signed(1), 10, 2));
+		assert_eq!(Balances::reserved_balance(1), 20);
+		assert_eq!(Balances::reserved_balance(2), 10);
+		assert_eq!(Queues::<Test>::get(1), vec![Bid { amount: 10, who: 1 },]);
+		assert_eq!(
+			Queues::<Test>::get(2),
+			vec![Bid { amount: 10, who: 2 }, Bid { amount: 10, who: 1 },]
+		);
+		assert_eq!(QueueTotals::<Test>::get(), vec![(1, 10), (2, 20), (0, 0)]);
+	});
+}
+
+#[test]
+fn retract_non_existent_item_fails() {
+	new_test_ext().execute_with(|| {
+		run_to_block(1);
+		assert_noop!(Nis::retract_bid(RuntimeOrigin::signed(1), 10, 1), Error::<Test>::NotFound);
+		assert_ok!(Nis::place_bid(RuntimeOrigin::signed(1), 10, 1));
+		assert_noop!(Nis::retract_bid(RuntimeOrigin::signed(1), 20, 1), Error::<Test>::NotFound);
+		assert_noop!(Nis::retract_bid(RuntimeOrigin::signed(1), 10, 2), Error::<Test>::NotFound);
+		assert_noop!(Nis::retract_bid(RuntimeOrigin::signed(2), 10, 1), Error::<Test>::NotFound);
+	});
+}
+
+#[test]
+fn basic_enlarge_works() {
+	new_test_ext().execute_with(|| {
+		run_to_block(1);
+		assert_ok!(Nis::place_bid(RuntimeOrigin::signed(1), 40, 1));
+		assert_ok!(Nis::place_bid(RuntimeOrigin::signed(2), 40, 2));
+		enlarge(40, 2);
+
+		// Takes 2/2, then stopped because it reaches its max amount
+		assert_eq!(Balances::reserved_balance(1), 40);
+		assert_eq!(Balances::reserved_balance(2), 0);
+		assert_eq!(pot(), 40);
+
+		assert_eq!(Queues::<Test>::get(1), vec![Bid { amount: 40, who: 1 }]);
+		assert_eq!(Queues::<Test>::get(2), vec![]);
+		assert_eq!(QueueTotals::<Test>::get(), vec![(1, 40), (0, 0), (0, 0)]);
+
+		assert_eq!(
+			Summary::<Test>::get(),
+			SummaryRecord {
+				proportion_owed: Perquintill::from_percent(10),
+				index: 1,
+				last_period: 0,
+				thawed: Perquintill::zero()
+			}
+		);
+		assert_eq!(
+			Receipts::<Test>::get(0).unwrap(),
+			ReceiptRecord { proportion: Perquintill::from_percent(10), who: 2, expiry: 7 }
+		);
+	});
+}
+
+#[test]
+fn enlarge_respects_bids_limit() {
+	new_test_ext().execute_with(|| {
+		run_to_block(1);
+		assert_ok!(Nis::place_bid(RuntimeOrigin::signed(1), 40, 1));
+		assert_ok!(Nis::place_bid(RuntimeOrigin::signed(2), 40, 2));
+		assert_ok!(Nis::place_bid(RuntimeOrigin::signed(3), 40, 2));
+		assert_ok!(Nis::place_bid(RuntimeOrigin::signed(4), 40, 3));
+		enlarge(100, 2);
+
+		// Should have taken 4/3 and 2/2, then stopped because it's only allowed 2.
+		assert_eq!(Queues::<Test>::get(1), vec![Bid { amount: 40, who: 1 }]);
+		assert_eq!(Queues::<Test>::get(2), vec![Bid { amount: 40, who: 3 }]);
+		assert_eq!(Queues::<Test>::get(3), vec![]);
+		assert_eq!(QueueTotals::<Test>::get(), vec![(1, 40), (1, 40), (0, 0)]);
+
+		assert_eq!(
+			Receipts::<Test>::get(0).unwrap(),
+			ReceiptRecord { proportion: Perquintill::from_percent(10), who: 4, expiry: 10 }
+		);
+		assert_eq!(
+			Receipts::<Test>::get(1).unwrap(),
+			ReceiptRecord { proportion: Perquintill::from_percent(10), who: 2, expiry: 7 }
+		);
+		assert_eq!(
+			Summary::<Test>::get(),
+			SummaryRecord {
+				proportion_owed: Perquintill::from_percent(20),
+				index: 2,
+				last_period: 0,
+				thawed: Perquintill::zero()
+			}
+		);
+	});
+}
+
+#[test]
+fn enlarge_respects_amount_limit_and_will_split() {
+	new_test_ext().execute_with(|| {
+		run_to_block(1);
+		assert_ok!(Nis::place_bid(RuntimeOrigin::signed(1), 80, 1));
+		enlarge(40, 2);
+
+		// Takes 2/2, then stopped because it reaches its max amount
+		assert_eq!(Queues::<Test>::get(1), vec![Bid { amount: 40, who: 1 }]);
+		assert_eq!(QueueTotals::<Test>::get(), vec![(1, 40), (0, 0), (0, 0)]);
+
+		assert_eq!(
+			Receipts::<Test>::get(0).unwrap(),
+			ReceiptRecord { proportion: Perquintill::from_percent(10), who: 1, expiry: 4 }
+		);
+		assert_eq!(
+			Summary::<Test>::get(),
+			SummaryRecord {
+				proportion_owed: Perquintill::from_percent(10),
+				index: 1,
+				last_period: 0,
+				thawed: Perquintill::zero()
+			}
+		);
+	});
+}
+
+#[test]
+fn basic_thaw_works() {
+	new_test_ext().execute_with(|| {
+		run_to_block(1);
+		assert_ok!(Nis::place_bid(RuntimeOrigin::signed(1), 40, 1));
+		assert_eq!(Nis::issuance().effective, 400);
+		assert_eq!(Balances::free_balance(1), 60);
+		assert_eq!(Balances::reserved_balance(1), 40);
+		assert_eq!(pot(), 0);
+
+		enlarge(40, 1);
+		assert_eq!(Nis::issuance().effective, 400);
+		assert_eq!(Balances::free_balance(1), 60);
+		assert_eq!(Balances::reserved_balance(1), 0);
+		assert_eq!(pot(), 40);
+
+		run_to_block(3);
+		assert_noop!(Nis::thaw(RuntimeOrigin::signed(1), 0, None), Error::<Test>::NotExpired);
+		run_to_block(4);
+		assert_noop!(Nis::thaw(RuntimeOrigin::signed(1), 1, None), Error::<Test>::Unknown);
+		assert_noop!(Nis::thaw(RuntimeOrigin::signed(2), 0, None), Error::<Test>::NotOwner);
+
+		assert_ok!(Nis::thaw(RuntimeOrigin::signed(1), 0, None));
+		assert_eq!(NisBalances::free_balance(1), 0);
+		assert_eq!(Nis::typed_attribute::<_, Perquintill>(&0, b"proportion"), None);
+		assert_eq!(Nis::issuance().effective, 400);
+		assert_eq!(Balances::free_balance(1), 100);
+		assert_eq!(pot(), 0);
+		assert_eq!(
+			Summary::<Test>::get(),
+			SummaryRecord {
+				proportion_owed: Perquintill::zero(),
+				index: 1,
+				last_period: 0,
+				thawed: Perquintill::from_percent(10)
+			}
+		);
+		assert_eq!(Receipts::<Test>::get(0), None);
+	});
+}
+
+#[test]
+fn partial_thaw_works() {
+	new_test_ext().execute_with(|| {
+		run_to_block(1);
+		assert_ok!(Nis::place_bid(RuntimeOrigin::signed(1), 80, 1));
+		enlarge(80, 1);
+		assert_eq!(pot(), 80);
+
+		run_to_block(4);
+		assert_noop!(
+			Nis::thaw(RuntimeOrigin::signed(1), 0, Some(4_100_000)),
+			Error::<Test>::MakesDust
+		);
+		assert_ok!(Nis::thaw(RuntimeOrigin::signed(1), 0, Some(1_050_000)));
+
+		assert_eq!(NisBalances::free_balance(1), 3_150_000);
+		assert_eq!(
+			Nis::typed_attribute::<_, Perquintill>(&0, b"proportion"),
+			Some(Perquintill::from_rational(3_150_000u64, 21_000_000u64)),
+		);
+
+		assert_eq!(Nis::issuance().effective, 400);
+		assert_eq!(Balances::free_balance(1), 40);
+		assert_eq!(pot(), 60);
+
+		assert_ok!(Nis::thaw(RuntimeOrigin::signed(1), 0, None));
+
+		assert_eq!(Nis::issuance().effective, 400);
+		assert_eq!(Balances::free_balance(1), 100);
+		assert_eq!(pot(), 0);
+
+		assert_eq!(
+			Summary::<Test>::get(),
+			SummaryRecord {
+				proportion_owed: Perquintill::zero(),
+				index: 1,
+				last_period: 0,
+				thawed: Perquintill::from_percent(20)
+			}
+		);
+		assert_eq!(Receipts::<Test>::get(0), None);
+	});
+}
+
+#[test]
+fn thaw_respects_transfers() {
+	new_test_ext().execute_with(|| {
+		run_to_block(1);
+		assert_ok!(Nis::place_bid(RuntimeOrigin::signed(1), 40, 1));
+		enlarge(40, 1);
+		run_to_block(4);
+
+		assert_eq!(Nis::owner(&0), Some(1));
+		assert_ok!(Nis::transfer(&0, &2));
+
+		// Transfering the receipt...
+		assert_noop!(Nis::thaw(RuntimeOrigin::signed(1), 0, None), Error::<Test>::NotOwner);
+		// ...can't be thawed due to missing counterpart
+		assert_noop!(Nis::thaw(RuntimeOrigin::signed(2), 0, None), TokenError::NoFunds);
+
+		// Transfer the counterpart also...
+		assert_ok!(NisBalances::transfer(RuntimeOrigin::signed(1), 2, 2100000));
+		// ...and thawing is possible.
+		assert_ok!(Nis::thaw(RuntimeOrigin::signed(2), 0, None));
+
+		assert_eq!(Balances::free_balance(2), 140);
+		assert_eq!(Balances::free_balance(1), 60);
+	});
+}
+
+#[test]
+fn thaw_when_issuance_higher_works() {
+	new_test_ext().execute_with(|| {
+		run_to_block(1);
+		assert_ok!(Nis::place_bid(RuntimeOrigin::signed(1), 100, 1));
+		enlarge(100, 1);
+
+		assert_eq!(NisBalances::free_balance(1), 5_250_000); // (25% of 21m)
+
+		// Everybody else's balances goes up by 50%
+		Balances::make_free_balance_be(&2, 150);
+		Balances::make_free_balance_be(&3, 150);
+		Balances::make_free_balance_be(&4, 150);
+
+		run_to_block(4);
+
+		// Unfunded initially...
+		assert_noop!(Nis::thaw(RuntimeOrigin::signed(1), 0, None), Error::<Test>::Unfunded);
+		// ...so we fund.
+		assert_ok!(Nis::fund_deficit(RuntimeOrigin::signed(1)));
+
+		// Transfer counterpart away...
+		assert_ok!(NisBalances::transfer(RuntimeOrigin::signed(1), 2, 250_000));
+		// ...and it's not thawable.
+		assert_noop!(Nis::thaw(RuntimeOrigin::signed(1), 0, None), TokenError::NoFunds);
+
+		// Transfer counterpart back...
+		assert_ok!(NisBalances::transfer(RuntimeOrigin::signed(2), 1, 250_000));
+		// ...and it is.
+		assert_ok!(Nis::thaw(RuntimeOrigin::signed(1), 0, None));
+
+		assert_eq!(Balances::free_balance(1), 150);
+		assert_eq!(Balances::reserved_balance(1), 0);
+	});
+}
+
+#[test]
+fn thaw_with_ignored_issuance_works() {
+	new_test_ext().execute_with(|| {
+		run_to_block(1);
+		// Give account zero some balance.
+		Balances::make_free_balance_be(&0, 200);
+
+		assert_ok!(Nis::place_bid(RuntimeOrigin::signed(1), 100, 1));
+		enlarge(100, 1);
+
+		// Account zero transfers 50 into everyone else's accounts.
+		assert_ok!(Balances::transfer(RuntimeOrigin::signed(0), 2, 50));
+		assert_ok!(Balances::transfer(RuntimeOrigin::signed(0), 3, 50));
+		assert_ok!(Balances::transfer(RuntimeOrigin::signed(0), 4, 50));
+
+		run_to_block(4);
+		// Unfunded initially...
+		assert_noop!(Nis::thaw(RuntimeOrigin::signed(1), 0, None), Error::<Test>::Unfunded);
+		// ...so we fund...
+		assert_ok!(Nis::fund_deficit(RuntimeOrigin::signed(1)));
+		// ...and then it's ok.
+		assert_ok!(Nis::thaw(RuntimeOrigin::signed(1), 0, None));
+
+		// Account zero changes have been ignored.
+		assert_eq!(Balances::free_balance(1), 150);
+		assert_eq!(Balances::reserved_balance(1), 0);
+	});
+}
+
+#[test]
+fn thaw_when_issuance_lower_works() {
+	new_test_ext().execute_with(|| {
+		run_to_block(1);
+		assert_ok!(Nis::place_bid(RuntimeOrigin::signed(1), 100, 1));
+		enlarge(100, 1);
+
+		// Everybody else's balances goes down by 25%
+		Balances::make_free_balance_be(&2, 75);
+		Balances::make_free_balance_be(&3, 75);
+		Balances::make_free_balance_be(&4, 75);
+
+		run_to_block(4);
+		assert_ok!(Nis::thaw(RuntimeOrigin::signed(1), 0, None));
+
+		assert_eq!(Balances::free_balance(1), 75);
+		assert_eq!(Balances::reserved_balance(1), 0);
+	});
+}
+
+#[test]
+fn multiple_thaws_works() {
+	new_test_ext().execute_with(|| {
+		run_to_block(1);
+		assert_ok!(Nis::place_bid(RuntimeOrigin::signed(1), 40, 1));
+		assert_ok!(Nis::place_bid(RuntimeOrigin::signed(1), 60, 1));
+		assert_ok!(Nis::place_bid(RuntimeOrigin::signed(2), 50, 1));
+		enlarge(200, 3);
+
+		// Double everyone's free balances.
+		Balances::make_free_balance_be(&2, 100);
+		Balances::make_free_balance_be(&3, 200);
+		Balances::make_free_balance_be(&4, 200);
+		assert_ok!(Nis::fund_deficit(RuntimeOrigin::signed(1)));
+
+		run_to_block(4);
+		assert_ok!(Nis::thaw(RuntimeOrigin::signed(1), 0, None));
+		assert_ok!(Nis::thaw(RuntimeOrigin::signed(1), 1, None));
+		assert_noop!(Nis::thaw(RuntimeOrigin::signed(2), 2, None), Error::<Test>::Throttled);
+		run_to_block(5);
+		assert_ok!(Nis::thaw(RuntimeOrigin::signed(2), 2, None));
+
+		assert_eq!(Balances::free_balance(1), 200);
+		assert_eq!(Balances::free_balance(2), 200);
+	});
+}
+
+#[test]
+fn multiple_thaws_works_in_alternative_thaw_order() {
+	new_test_ext().execute_with(|| {
+		run_to_block(1);
+		assert_ok!(Nis::place_bid(RuntimeOrigin::signed(1), 40, 1));
+		assert_ok!(Nis::place_bid(RuntimeOrigin::signed(1), 60, 1));
+		assert_ok!(Nis::place_bid(RuntimeOrigin::signed(2), 50, 1));
+		enlarge(200, 3);
+
+		// Double everyone's free balances.
+		Balances::make_free_balance_be(&2, 100);
+		Balances::make_free_balance_be(&3, 200);
+		Balances::make_free_balance_be(&4, 200);
+		assert_ok!(Nis::fund_deficit(RuntimeOrigin::signed(1)));
+
+		run_to_block(4);
+		assert_ok!(Nis::thaw(RuntimeOrigin::signed(2), 2, None));
+		assert_noop!(Nis::thaw(RuntimeOrigin::signed(1), 1, None), Error::<Test>::Throttled);
+		assert_ok!(Nis::thaw(RuntimeOrigin::signed(1), 0, None));
+
+		run_to_block(5);
+		assert_ok!(Nis::thaw(RuntimeOrigin::signed(1), 1, None));
+
+		assert_eq!(Balances::free_balance(1), 200);
+		assert_eq!(Balances::free_balance(2), 200);
+	});
+}
+
+#[test]
+fn enlargement_to_target_works() {
+	new_test_ext().execute_with(|| {
+		run_to_block(2);
+		let w = <() as WeightInfo>::process_queues() +
+			<() as WeightInfo>::process_queue() +
+			(<() as WeightInfo>::process_bid() * 2);
+		super::mock::MaxIntakeWeight::set(w);
+		assert_ok!(Nis::place_bid(RuntimeOrigin::signed(1), 40, 1));
+		assert_ok!(Nis::place_bid(RuntimeOrigin::signed(1), 40, 2));
+		assert_ok!(Nis::place_bid(RuntimeOrigin::signed(2), 40, 2));
+		assert_ok!(Nis::place_bid(RuntimeOrigin::signed(2), 40, 3));
+		assert_ok!(Nis::place_bid(RuntimeOrigin::signed(3), 40, 3));
+		Target::set(Perquintill::from_percent(40));
+
+		run_to_block(3);
+		assert_eq!(Queues::<Test>::get(1), vec![Bid { amount: 40, who: 1 },]);
+		assert_eq!(
+			Queues::<Test>::get(2),
+			vec![Bid { amount: 40, who: 2 }, Bid { amount: 40, who: 1 },]
+		);
+		assert_eq!(
+			Queues::<Test>::get(3),
+			vec![Bid { amount: 40, who: 3 }, Bid { amount: 40, who: 2 },]
+		);
+		assert_eq!(QueueTotals::<Test>::get(), vec![(1, 40), (2, 80), (2, 80)]);
+
+		run_to_block(4);
+		// Two new items should have been issued to 2 & 3 for 40 each & duration of 3.
+		assert_eq!(
+			Receipts::<Test>::get(0).unwrap(),
+			ReceiptRecord { proportion: Perquintill::from_percent(10), who: 2, expiry: 13 }
+		);
+		assert_eq!(
+			Receipts::<Test>::get(1).unwrap(),
+			ReceiptRecord { proportion: Perquintill::from_percent(10), who: 3, expiry: 13 }
+		);
+		assert_eq!(
+			Summary::<Test>::get(),
+			SummaryRecord {
+				proportion_owed: Perquintill::from_percent(20),
+				index: 2,
+				last_period: 0,
+				thawed: Perquintill::zero(),
+			}
+		);
+
+		run_to_block(5);
+		// No change
+		assert_eq!(
+			Summary::<Test>::get(),
+			SummaryRecord {
+				proportion_owed: Perquintill::from_percent(20),
+				index: 2,
+				last_period: 0,
+				thawed: Perquintill::zero()
+			}
+		);
+
+		run_to_block(6);
+		// Two new items should have been issued to 1 & 2 for 40 each & duration of 2.
+		assert_eq!(
+			Receipts::<Test>::get(2).unwrap(),
+			ReceiptRecord { proportion: Perquintill::from_percent(10), who: 1, expiry: 12 }
+		);
+		assert_eq!(
+			Receipts::<Test>::get(3).unwrap(),
+			ReceiptRecord { proportion: Perquintill::from_percent(10), who: 2, expiry: 12 }
+		);
+		assert_eq!(
+			Summary::<Test>::get(),
+			SummaryRecord {
+				proportion_owed: Perquintill::from_percent(40),
+				index: 4,
+				last_period: 0,
+				thawed: Perquintill::zero()
+			}
+		);
+
+		run_to_block(8);
+		// No change now.
+		assert_eq!(
+			Summary::<Test>::get(),
+			SummaryRecord {
+				proportion_owed: Perquintill::from_percent(40),
+				index: 4,
+				last_period: 0,
+				thawed: Perquintill::zero()
+			}
+		);
+
+		// Set target a bit higher to use up the remaining bid.
+		Target::set(Perquintill::from_percent(60));
+		run_to_block(10);
+
+		// Two new items should have been issued to 1 & 2 for 40 each & duration of 2.
+		assert_eq!(
+			Receipts::<Test>::get(4).unwrap(),
+			ReceiptRecord { proportion: Perquintill::from_percent(10), who: 1, expiry: 13 }
+		);
+
+		assert_eq!(
+			Summary::<Test>::get(),
+			SummaryRecord {
+				proportion_owed: Perquintill::from_percent(50),
+				index: 5,
+				last_period: 0,
+				thawed: Perquintill::zero()
+			}
+		);
+	});
+}
diff --git a/substrate/frame/gilt/src/weights.rs b/substrate/frame/nis/src/weights.rs
similarity index 51%
rename from substrate/frame/gilt/src/weights.rs
rename to substrate/frame/nis/src/weights.rs
index 82b199b1a66..71577075ada 100644
--- a/substrate/frame/gilt/src/weights.rs
+++ b/substrate/frame/nis/src/weights.rs
@@ -15,7 +15,7 @@
 // See the License for the specific language governing permissions and
 // limitations under the License.
 
-//! Autogenerated weights for pallet_gilt
+//! Autogenerated weights for pallet_nis
 //!
 //! THIS FILE WAS AUTO-GENERATED USING THE SUBSTRATE BENCHMARK CLI VERSION 4.0.0-dev
 //! DATE: 2022-11-07, STEPS: `50`, REPEAT: 20, LOW RANGE: `[]`, HIGH RANGE: `[]`
@@ -29,14 +29,12 @@
 // --chain=dev
 // --steps=50
 // --repeat=20
-// --pallet=pallet_gilt
+// --pallet=pallet_nis
 // --extrinsic=*
 // --execution=wasm
 // --wasm-execution=compiled
-// --heap-pages=4096
-// --output=./frame/gilt/src/weights.rs
-// --header=./HEADER-APACHE2
 // --template=./.maintain/frame-weight-template.hbs
+// --output=./frame/nis/src/weights.rs
 
 #![cfg_attr(rustfmt, rustfmt_skip)]
 #![allow(unused_parens)]
@@ -45,24 +43,23 @@
 use frame_support::{traits::Get, weights::{Weight, constants::RocksDbWeight}};
 use sp_std::marker::PhantomData;
 
-/// Weight functions needed for pallet_gilt.
+/// Weight functions needed for pallet_nis.
 pub trait WeightInfo {
 	fn place_bid(l: u32, ) -> Weight;
 	fn place_bid_max() -> Weight;
 	fn retract_bid(l: u32, ) -> Weight;
-	fn set_target() -> Weight;
 	fn thaw() -> Weight;
-	fn pursue_target_noop() -> Weight;
-	fn pursue_target_per_item(b: u32, ) -> Weight;
-	fn pursue_target_per_queue(q: u32, ) -> Weight;
+	fn fund_deficit() -> Weight;
+	fn process_queues() -> Weight;
+	fn process_queue() -> Weight;
+	fn process_bid() -> Weight;
 }
 
-/// Weights for pallet_gilt using the Substrate node and recommended hardware.
+/// Weights for pallet_nis using the Substrate node and recommended hardware.
 pub struct SubstrateWeight<T>(PhantomData<T>);
 impl<T: frame_system::Config> WeightInfo for SubstrateWeight<T> {
-	// Storage: Gilt Queues (r:1 w:1)
-	// Storage: Gilt QueueTotals (r:1 w:1)
-	/// The range of component `l` is `[0, 999]`.
+	// Storage: Nis Queues (r:1 w:1)
+	// Storage: Nis QueueTotals (r:1 w:1)
 	fn place_bid(l: u32, ) -> Weight {
 		// Minimum execution time: 42_332 nanoseconds.
 		Weight::from_ref_time(45_584_514 as u64)
@@ -71,17 +68,16 @@ impl<T: frame_system::Config> WeightInfo for SubstrateWeight<T> {
 			.saturating_add(T::DbWeight::get().reads(2 as u64))
 			.saturating_add(T::DbWeight::get().writes(2 as u64))
 	}
-	// Storage: Gilt Queues (r:1 w:1)
-	// Storage: Gilt QueueTotals (r:1 w:1)
+	// Storage: Nis Queues (r:1 w:1)
+	// Storage: Nis QueueTotals (r:1 w:1)
 	fn place_bid_max() -> Weight {
 		// Minimum execution time: 85_866 nanoseconds.
 		Weight::from_ref_time(87_171_000 as u64)
 			.saturating_add(T::DbWeight::get().reads(2 as u64))
 			.saturating_add(T::DbWeight::get().writes(2 as u64))
 	}
-	// Storage: Gilt Queues (r:1 w:1)
-	// Storage: Gilt QueueTotals (r:1 w:1)
-	/// The range of component `l` is `[1, 1000]`.
+	// Storage: Nis Queues (r:1 w:1)
+	// Storage: Nis QueueTotals (r:1 w:1)
 	fn retract_bid(l: u32, ) -> Weight {
 		// Minimum execution time: 44_605 nanoseconds.
 		Weight::from_ref_time(46_850_108 as u64)
@@ -90,63 +86,52 @@ impl<T: frame_system::Config> WeightInfo for SubstrateWeight<T> {
 			.saturating_add(T::DbWeight::get().reads(2 as u64))
 			.saturating_add(T::DbWeight::get().writes(2 as u64))
 	}
-	// Storage: Gilt ActiveTotal (r:1 w:1)
-	fn set_target() -> Weight {
-		// Minimum execution time: 7_331 nanoseconds.
-		Weight::from_ref_time(7_619_000 as u64)
-			.saturating_add(T::DbWeight::get().reads(1 as u64))
-			.saturating_add(T::DbWeight::get().writes(1 as u64))
-	}
-	// Storage: Gilt Active (r:1 w:1)
-	// Storage: Gilt ActiveTotal (r:1 w:1)
+	// Storage: Nis Active (r:1 w:1)
+	// Storage: Nis ActiveTotal (r:1 w:1)
 	fn thaw() -> Weight {
 		// Minimum execution time: 55_143 nanoseconds.
 		Weight::from_ref_time(55_845_000 as u64)
 			.saturating_add(T::DbWeight::get().reads(2 as u64))
 			.saturating_add(T::DbWeight::get().writes(2 as u64))
 	}
-	// Storage: Gilt ActiveTotal (r:1 w:0)
-	fn pursue_target_noop() -> Weight {
-		// Minimum execution time: 3_386 nanoseconds.
-		Weight::from_ref_time(3_461_000 as u64)
+	// Storage: Nis Active (r:1 w:1)
+	// Storage: Nis ActiveTotal (r:1 w:1)
+	fn fund_deficit() -> Weight {
+		Weight::from_ref_time(47_753_000 as u64)
+			.saturating_add(T::DbWeight::get().reads(2 as u64))
+			.saturating_add(T::DbWeight::get().writes(2 as u64))
+	}
+	// Storage: Nis ActiveTotal (r:1 w:0)
+	fn process_queues() -> Weight {
+		Weight::from_ref_time(1_663_000 as u64)
 			.saturating_add(T::DbWeight::get().reads(1 as u64))
 	}
-	// Storage: Gilt ActiveTotal (r:1 w:1)
-	// Storage: Gilt QueueTotals (r:1 w:1)
-	// Storage: Gilt Queues (r:1 w:1)
-	// Storage: Gilt Active (r:0 w:20)
-	/// The range of component `b` is `[0, 1000]`.
-	fn pursue_target_per_item(b: u32, ) -> Weight {
-		// Minimum execution time: 34_156 nanoseconds.
-		Weight::from_ref_time(45_262_859 as u64)
-			// Standard Error: 1_529
-			.saturating_add(Weight::from_ref_time(4_181_654 as u64).saturating_mul(b as u64))
+	// Storage: Nis ActiveTotal (r:1 w:1)
+	// Storage: Nis QueueTotals (r:1 w:1)
+	// Storage: Nis Queues (r:1 w:1)
+	// Storage: Nis Active (r:0 w:1)
+	fn process_queue() -> Weight {
+		Weight::from_ref_time(40_797_000 as u64)
+			// Standard Error: 1_000
 			.saturating_add(T::DbWeight::get().reads(3 as u64))
 			.saturating_add(T::DbWeight::get().writes(3 as u64))
-			.saturating_add(T::DbWeight::get().writes((1 as u64).saturating_mul(b as u64)))
-	}
-	// Storage: Gilt ActiveTotal (r:1 w:1)
-	// Storage: Gilt QueueTotals (r:1 w:1)
-	// Storage: Gilt Queues (r:6 w:6)
-	// Storage: Gilt Active (r:0 w:6)
-	/// The range of component `q` is `[0, 300]`.
-	fn pursue_target_per_queue(q: u32, ) -> Weight {
-		// Minimum execution time: 33_526 nanoseconds.
-		Weight::from_ref_time(37_255_562 as u64)
-			// Standard Error: 3_611
-			.saturating_add(Weight::from_ref_time(7_193_128 as u64).saturating_mul(q as u64))
+	}
+	// Storage: Nis ActiveTotal (r:1 w:1)
+	// Storage: Nis QueueTotals (r:1 w:1)
+	// Storage: Nis Queues (r:1 w:1)
+	// Storage: Nis Active (r:0 w:1)
+	fn process_bid() -> Weight {
+		Weight::from_ref_time(14_944_000 as u64)
+			// Standard Error: 6_000
 			.saturating_add(T::DbWeight::get().reads(2 as u64))
-			.saturating_add(T::DbWeight::get().reads((1 as u64).saturating_mul(q as u64)))
 			.saturating_add(T::DbWeight::get().writes(2 as u64))
-			.saturating_add(T::DbWeight::get().writes((2 as u64).saturating_mul(q as u64)))
 	}
 }
 
 // For backwards compatibility and tests
 impl WeightInfo for () {
-	// Storage: Gilt Queues (r:1 w:1)
-	// Storage: Gilt QueueTotals (r:1 w:1)
-	/// The range of component `l` is `[0, 999]`.
+	// Storage: Nis Queues (r:1 w:1)
+	// Storage: Nis QueueTotals (r:1 w:1)
 	fn place_bid(l: u32, ) -> Weight {
 		// Minimum execution time: 42_332 nanoseconds.
 		Weight::from_ref_time(45_584_514 as u64)
@@ -155,17 +140,16 @@ impl WeightInfo for () {
 			.saturating_add(RocksDbWeight::get().reads(2 as u64))
 			.saturating_add(RocksDbWeight::get().writes(2 as u64))
 	}
-	// Storage: Gilt Queues (r:1 w:1)
-	// Storage: Gilt QueueTotals (r:1 w:1)
+	// Storage: Nis Queues (r:1 w:1)
+	// Storage: Nis QueueTotals (r:1 w:1)
 	fn place_bid_max() -> Weight {
 		// Minimum execution time: 85_866 nanoseconds.
 		Weight::from_ref_time(87_171_000 as u64)
 			.saturating_add(RocksDbWeight::get().reads(2 as u64))
 			.saturating_add(RocksDbWeight::get().writes(2 as u64))
 	}
-	// Storage: Gilt Queues (r:1 w:1)
-	// Storage: Gilt QueueTotals (r:1 w:1)
-	/// The range of component `l` is `[1, 1000]`.
+	// Storage: Nis Queues (r:1 w:1)
+	// Storage: Nis QueueTotals (r:1 w:1)
 	fn retract_bid(l: u32, ) -> Weight {
 		// Minimum execution time: 44_605 nanoseconds.
 		Weight::from_ref_time(46_850_108 as u64)
@@ -174,54 +158,44 @@ impl WeightInfo for () {
 			.saturating_add(RocksDbWeight::get().reads(2 as u64))
 			.saturating_add(RocksDbWeight::get().writes(2 as u64))
 	}
-	// Storage: Gilt ActiveTotal (r:1 w:1)
-	fn set_target() -> Weight {
-		// Minimum execution time: 7_331 nanoseconds.
-		Weight::from_ref_time(7_619_000 as u64)
-			.saturating_add(RocksDbWeight::get().reads(1 as u64))
-			.saturating_add(RocksDbWeight::get().writes(1 as u64))
-	}
-	// Storage: Gilt Active (r:1 w:1)
-	// Storage: Gilt ActiveTotal (r:1 w:1)
+	// Storage: Nis Active (r:1 w:1)
+	// Storage: Nis ActiveTotal (r:1 w:1)
 	fn thaw() -> Weight {
 		// Minimum execution time: 55_143 nanoseconds.
 		Weight::from_ref_time(55_845_000 as u64)
 			.saturating_add(RocksDbWeight::get().reads(2 as u64))
 			.saturating_add(RocksDbWeight::get().writes(2 as u64))
 	}
-	// Storage: Gilt ActiveTotal (r:1 w:0)
-	fn pursue_target_noop() -> Weight {
-		// Minimum execution time: 3_386 nanoseconds.
-		Weight::from_ref_time(3_461_000 as u64)
+	// Storage: Nis Active (r:1 w:1)
+	// Storage: Nis ActiveTotal (r:1 w:1)
+	fn fund_deficit() -> Weight {
+		Weight::from_ref_time(47_753_000 as u64)
+			.saturating_add(RocksDbWeight::get().reads(2 as u64))
+			.saturating_add(RocksDbWeight::get().writes(2 as u64))
+	}
+	// Storage: Nis ActiveTotal (r:1 w:0)
+	fn process_queues() -> Weight {
+		Weight::from_ref_time(1_663_000 as u64)
 			.saturating_add(RocksDbWeight::get().reads(1 as u64))
 	}
-	// Storage: Gilt ActiveTotal (r:1 w:1)
-	// Storage: Gilt QueueTotals (r:1 w:1)
-	// Storage: Gilt Queues (r:1 w:1)
-	// Storage: Gilt Active (r:0 w:20)
-	/// The range of component `b` is `[0, 1000]`.
-	fn pursue_target_per_item(b: u32, ) -> Weight {
-		// Minimum execution time: 34_156 nanoseconds.
-		Weight::from_ref_time(45_262_859 as u64)
-			// Standard Error: 1_529
-			.saturating_add(Weight::from_ref_time(4_181_654 as u64).saturating_mul(b as u64))
+	// Storage: Nis ActiveTotal (r:1 w:1)
+	// Storage: Nis QueueTotals (r:1 w:1)
+	// Storage: Nis Queues (r:1 w:1)
+	// Storage: Nis Active (r:0 w:1)
+	fn process_queue() -> Weight {
+		Weight::from_ref_time(40_797_000 as u64)
+			// Standard Error: 1_000
 			.saturating_add(RocksDbWeight::get().reads(3 as u64))
 			.saturating_add(RocksDbWeight::get().writes(3 as u64))
-			.saturating_add(RocksDbWeight::get().writes((1 as u64).saturating_mul(b as u64)))
-	}
-	// Storage: Gilt ActiveTotal (r:1 w:1)
-	// Storage: Gilt QueueTotals (r:1 w:1)
-	// Storage: Gilt Queues (r:6 w:6)
-	// Storage: Gilt Active (r:0 w:6)
-	/// The range of component `q` is `[0, 300]`.
-	fn pursue_target_per_queue(q: u32, ) -> Weight {
-		// Minimum execution time: 33_526 nanoseconds.
-		Weight::from_ref_time(37_255_562 as u64)
-			// Standard Error: 3_611
-			.saturating_add(Weight::from_ref_time(7_193_128 as u64).saturating_mul(q as u64))
+	}
+	// Storage: Nis ActiveTotal (r:1 w:1)
+	// Storage: Nis QueueTotals (r:1 w:1)
+	// Storage: Nis Queues (r:1 w:1)
+	// Storage: Nis Active (r:0 w:1)
+	fn process_bid() -> Weight {
+		Weight::from_ref_time(14_944_000 as u64)
+			// Standard Error: 6_000
 			.saturating_add(RocksDbWeight::get().reads(2 as u64))
-			.saturating_add(RocksDbWeight::get().reads((1 as u64).saturating_mul(q as u64)))
 			.saturating_add(RocksDbWeight::get().writes(2 as u64))
-			.saturating_add(RocksDbWeight::get().writes((2 as u64).saturating_mul(q as u64)))
 	}
 }
diff --git a/substrate/frame/support/src/traits.rs b/substrate/frame/support/src/traits.rs
index 9b5300dfc57..1b1d5d68cb5 100644
--- a/substrate/frame/support/src/traits.rs
+++ b/substrate/frame/support/src/traits.rs
@@ -27,7 +27,7 @@ pub use tokens::{
 	},
 	fungible, fungibles,
 	imbalance::{Imbalance, OnUnbalanced, SignedImbalance},
-	BalanceStatus, ExistenceRequirement, Locker, WithdrawReasons,
+	nonfungible, nonfungibles, BalanceStatus, ExistenceRequirement, Locker, WithdrawReasons,
 };
 
 mod members;
diff --git a/substrate/frame/support/src/traits/misc.rs b/substrate/frame/support/src/traits/misc.rs
index ce8faaaf37c..33e48faa7ef 100644
--- a/substrate/frame/support/src/traits/misc.rs
+++ b/substrate/frame/support/src/traits/misc.rs
@@ -21,7 +21,7 @@ use crate::dispatch::Parameter;
 use codec::{CompactLen, Decode, DecodeLimit, Encode, EncodeLike, Input, MaxEncodedLen};
 use impl_trait_for_tuples::impl_for_tuples;
 use scale_info::{build::Fields, meta_type, Path, Type, TypeInfo, TypeParameter};
-use sp_arithmetic::traits::{CheckedAdd, CheckedMul, CheckedSub, Saturating};
+use sp_arithmetic::traits::{CheckedAdd, CheckedMul, CheckedSub, One, Saturating};
 use sp_core::bounded::bounded_vec::TruncateFrom;
 #[doc(hidden)]
 pub use sp_runtime::traits::{
@@ -348,17 +348,25 @@ impl<T> DefensiveOption<T> for Option<T> {
 /// A variant of [`Defensive`] with the same rationale, for the arithmetic operations where in
 /// case an infallible operation fails, it saturates.
 pub trait DefensiveSaturating {
-	/// Add `self` and `other` defensively.
+	/// Return `self` plus `other` defensively.
 	fn defensive_saturating_add(self, other: Self) -> Self;
-	/// Subtract `other` from `self` defensively.
+	/// Return `self` minus `other` defensively.
 	fn defensive_saturating_sub(self, other: Self) -> Self;
-	/// Multiply `self` and `other` defensively.
+	/// Return the product of `self` and `other` defensively.
 	fn defensive_saturating_mul(self, other: Self) -> Self;
+	/// Increase `self` by `other` defensively.
+	fn defensive_saturating_accrue(&mut self, other: Self);
+	/// Reduce `self` by `other` defensively.
+	fn defensive_saturating_reduce(&mut self, other: Self);
+	/// Increment `self` by one defensively.
+	fn defensive_saturating_inc(&mut self);
+	/// Decrement `self` by one defensively.
+	fn defensive_saturating_dec(&mut self);
 }
 
 // NOTE: A bit unfortunate, since T has to be bound by all the traits needed. Could make it
 // `DefensiveSaturating<T>` to mitigate.
-impl<T: Saturating + CheckedAdd + CheckedMul + CheckedSub> DefensiveSaturating for T {
+impl<T: Saturating + CheckedAdd + CheckedMul + CheckedSub + One> DefensiveSaturating for T {
 	fn defensive_saturating_add(self, other: Self) -> Self {
 		self.checked_add(&other).defensive_unwrap_or_else(|| self.saturating_add(other))
 	}
@@ -368,6 +376,20 @@ impl<T: Saturating + CheckedAdd + CheckedMul + CheckedSub> DefensiveSaturating f
 	fn defensive_saturating_mul(self, other: Self) -> Self {
 		self.checked_mul(&other).defensive_unwrap_or_else(|| self.saturating_mul(other))
 	}
+	fn defensive_saturating_accrue(&mut self, other: Self) {
+		// Use `replace` here since `take` would require `T: Default`.
+		*self = sp_std::mem::replace(self, One::one()).defensive_saturating_add(other);
+	}
+	fn defensive_saturating_reduce(&mut self, other: Self) {
+		// Use `replace` here since `take` would require `T: Default`.
+		*self = sp_std::mem::replace(self, One::one()).defensive_saturating_sub(other);
+	}
+	fn defensive_saturating_inc(&mut self) {
+		self.defensive_saturating_accrue(One::one());
+	}
+	fn defensive_saturating_dec(&mut self) {
+		self.defensive_saturating_reduce(One::one());
+	}
 }
 
 /// Construct an object by defensively truncating an input if the `TryFrom` conversion fails.
@@ -1119,6 +1141,92 @@ mod test {
 	use sp_core::bounded::{BoundedSlice, BoundedVec};
 	use sp_std::marker::PhantomData;
 
+	#[test]
+	#[cfg(not(debug_assertions))]
+	fn defensive_saturating_accrue_works() {
+		let mut v = 1_u32;
+		v.defensive_saturating_accrue(2);
+		assert_eq!(v, 3);
+		v.defensive_saturating_accrue(u32::MAX);
+		assert_eq!(v, u32::MAX);
+		v.defensive_saturating_accrue(1);
+		assert_eq!(v, u32::MAX);
+	}
+
+	#[test]
+	#[cfg(debug_assertions)]
+	#[should_panic(expected = "Defensive")]
+	fn defensive_saturating_accrue_panics() {
+		let mut v = u32::MAX;
+		v.defensive_saturating_accrue(1); // defensive failure
+	}
+
+	#[test]
+	#[cfg(not(debug_assertions))]
+	fn defensive_saturating_reduce_works() {
+		let mut v = u32::MAX;
+		v.defensive_saturating_reduce(3);
+		assert_eq!(v, u32::MAX - 3);
+		v.defensive_saturating_reduce(u32::MAX);
+		assert_eq!(v, 0);
+		v.defensive_saturating_reduce(1);
+		assert_eq!(v, 0);
+	}
+
+	#[test]
+	#[cfg(debug_assertions)]
+	#[should_panic(expected = "Defensive")]
+	fn defensive_saturating_reduce_panics() {
+		let mut v = 0_u32;
+		v.defensive_saturating_reduce(1); // defensive failure
+	}
+
+	#[test]
+	#[cfg(not(debug_assertions))]
+	fn defensive_saturating_inc_works() {
+		let mut v = 0_u32;
+		for i in 1..10 {
+			v.defensive_saturating_inc();
+			assert_eq!(v, i);
+		}
+		v += u32::MAX - 10;
+		v.defensive_saturating_inc();
+		assert_eq!(v, u32::MAX);
+		v.defensive_saturating_inc();
+		assert_eq!(v, u32::MAX);
+	}
+
+	#[test]
+	#[cfg(debug_assertions)]
+	#[should_panic(expected = "Defensive")]
+	fn defensive_saturating_inc_panics() {
+		let mut v = u32::MAX;
+		v.defensive_saturating_inc(); // defensive failure
+	}
+
+	#[test]
+	#[cfg(not(debug_assertions))]
+	fn defensive_saturating_dec_works() {
+		let mut v = u32::MAX;
+		for i in 1..10 {
+			v.defensive_saturating_dec();
+			assert_eq!(v, u32::MAX - i);
+		}
+		v -= u32::MAX - 10;
+		v.defensive_saturating_dec();
+		assert_eq!(v, 0);
+		v.defensive_saturating_dec();
+		assert_eq!(v, 0);
+	}
+
+	#[test]
+	#[cfg(debug_assertions)]
+	#[should_panic(expected = "Defensive")]
+	fn defensive_saturating_dec_panics() {
+		let mut v = 0_u32;
+		v.defensive_saturating_dec(); // defensive failure
+	}
+
 	#[test]
 	#[cfg(not(debug_assertions))]
 	fn defensive_truncating_from_vec_defensive_works() {
diff --git a/substrate/frame/support/src/traits/tokens/currency/reservable.rs b/substrate/frame/support/src/traits/tokens/currency/reservable.rs
index 35455aaecdb..53f6764c3a1 100644
--- a/substrate/frame/support/src/traits/tokens/currency/reservable.rs
+++ b/substrate/frame/support/src/traits/tokens/currency/reservable.rs
@@ -17,8 +17,14 @@
 
 //! The reservable currency trait.
 
+use scale_info::TypeInfo;
+use sp_core::Get;
+
 use super::{super::misc::BalanceStatus, Currency};
-use crate::dispatch::{DispatchError, DispatchResult};
+use crate::{
+	dispatch::{DispatchError, DispatchResult},
+	traits::{ExistenceRequirement, SignedImbalance, WithdrawReasons},
+};
 
 /// A currency where funds can be reserved from the user.
 pub trait ReservableCurrency<AccountId>: Currency<AccountId> {
@@ -111,7 +117,7 @@ impl<AccountId> ReservableCurrency<AccountId> for () {
 pub trait NamedReservableCurrency<AccountId>: ReservableCurrency<AccountId> {
 	/// An identifier for a reserve. Used for disambiguating different reserves so that
 	/// they can be individually replaced or removed.
-	type ReserveIdentifier;
+	type ReserveIdentifier: codec::Encode + TypeInfo + 'static;
 
 	/// Deducts up to `value` from reserved balance of `who`. This function cannot fail.
 	///
@@ -236,3 +242,144 @@ pub trait NamedReservableCurrency<AccountId>: ReservableCurrency<AccountId> {
 		Self::repatriate_reserved_named(id, slashed, beneficiary, value, status).map(|_| ())
 	}
 }
+
+/// Adapter to allow a `NamedReservableCurrency` to be passed as regular `ReservableCurrency`
+/// together with an `Id`.
+///
+/// All "anonymous" operations are then implemented as their named counterparts with the given `Id`.
+pub struct WithName<NamedReservable, Id, AccountId>(
+	sp_std::marker::PhantomData<(NamedReservable, Id, AccountId)>,
+);
+impl<
+		NamedReservable: NamedReservableCurrency<AccountId>,
+		Id: Get<NamedReservable::ReserveIdentifier>,
+		AccountId,
+	> Currency<AccountId> for WithName<NamedReservable, Id, AccountId>
+{
+	type Balance = <NamedReservable as Currency<AccountId>>::Balance;
+	type PositiveImbalance = <NamedReservable as Currency<AccountId>>::PositiveImbalance;
+	type NegativeImbalance = <NamedReservable as Currency<AccountId>>::NegativeImbalance;
+
+	fn total_balance(who: &AccountId) -> Self::Balance {
+		NamedReservable::total_balance(who)
+	}
+	fn can_slash(who: &AccountId, value: Self::Balance) -> bool {
+		NamedReservable::can_slash(who, value)
+	}
+	fn total_issuance() -> Self::Balance {
+		NamedReservable::total_issuance()
+	}
+	fn minimum_balance() -> Self::Balance {
+		NamedReservable::minimum_balance()
+	}
+	fn burn(amount: Self::Balance) -> Self::PositiveImbalance {
+		NamedReservable::burn(amount)
+	}
+	fn issue(amount: Self::Balance) -> Self::NegativeImbalance {
+		NamedReservable::issue(amount)
+	}
+	fn pair(amount: Self::Balance) -> (Self::PositiveImbalance, Self::NegativeImbalance) {
+		NamedReservable::pair(amount)
+	}
+	fn free_balance(who: &AccountId) -> Self::Balance {
+		NamedReservable::free_balance(who)
+	}
+	fn ensure_can_withdraw(
+		who: &AccountId,
+		amount: Self::Balance,
+		reasons: WithdrawReasons,
+		new_balance: Self::Balance,
+	) -> DispatchResult {
+		NamedReservable::ensure_can_withdraw(who, amount, reasons, new_balance)
+	}
+
+	fn transfer(
+		source: &AccountId,
+		dest: &AccountId,
+		value: Self::Balance,
+		existence_requirement: ExistenceRequirement,
+	) -> DispatchResult {
+		NamedReservable::transfer(source, dest, value, existence_requirement)
+	}
+	fn slash(who: &AccountId, value: Self::Balance) -> (Self::NegativeImbalance, Self::Balance) {
+		NamedReservable::slash(who, value)
+	}
+	fn deposit_into_existing(
+		who: &AccountId,
+		value: Self::Balance,
+	) -> Result<Self::PositiveImbalance, DispatchError> {
+		NamedReservable::deposit_into_existing(who, value)
+	}
+	fn resolve_into_existing(
+		who: &AccountId,
+		value: Self::NegativeImbalance,
+	) -> Result<(), Self::NegativeImbalance> {
+		NamedReservable::resolve_into_existing(who, value)
+	}
+	fn deposit_creating(who: &AccountId, value: Self::Balance) -> Self::PositiveImbalance {
+		NamedReservable::deposit_creating(who, value)
+	}
+	fn resolve_creating(who: &AccountId, value: Self::NegativeImbalance) {
+		NamedReservable::resolve_creating(who, value)
+	}
+	fn withdraw(
+		who: &AccountId,
+		value: Self::Balance,
+		reasons: WithdrawReasons,
+		liveness: ExistenceRequirement,
+	) -> Result<Self::NegativeImbalance, DispatchError> {
+		NamedReservable::withdraw(who, value, reasons, liveness)
+	}
+	fn settle(
+		who: &AccountId,
+		value: Self::PositiveImbalance,
+		reasons: WithdrawReasons,
+		liveness: ExistenceRequirement,
+	) -> Result<(), Self::PositiveImbalance> {
+		NamedReservable::settle(who, value, reasons, liveness)
+	}
+	fn make_free_balance_be(
+		who: &AccountId,
+		balance: Self::Balance,
+	) -> SignedImbalance<Self::Balance, Self::PositiveImbalance> {
+		NamedReservable::make_free_balance_be(who, balance)
+	}
+}
+impl<
+		NamedReservable: NamedReservableCurrency<AccountId>,
+		Id: Get<NamedReservable::ReserveIdentifier>,
+		AccountId,
+	> ReservableCurrency<AccountId> for WithName<NamedReservable, Id, AccountId>
+{
+	fn can_reserve(who: &AccountId, value: Self::Balance) -> bool {
+		NamedReservable::can_reserve(who, value)
+	}
+
+	fn slash_reserved(
+		who: &AccountId,
+		value: Self::Balance,
+	) -> (Self::NegativeImbalance, Self::Balance) {
+		NamedReservable::slash_reserved_named(&Id::get(), who, value)
+	}
+
+	fn reserved_balance(who: &AccountId) -> Self::Balance {
+		NamedReservable::reserved_balance_named(&Id::get(), who)
+	}
+
+	fn reserve(who: &AccountId, value: Self::Balance) -> DispatchResult {
+		NamedReservable::reserve_named(&Id::get(), who, value)
+	}
+
+	fn unreserve(who: &AccountId, value: Self::Balance) -> Self::Balance {
+		NamedReservable::unreserve_named(&Id::get(), who, value)
+	}
+
+	fn repatriate_reserved(
+		slashed: &AccountId,
+		beneficiary: &AccountId,
+		value: Self::Balance,
+		status: BalanceStatus,
+	) -> Result<Self::Balance, DispatchError> {
+		NamedReservable::repatriate_reserved_named(&Id::get(), slashed, beneficiary, value, status)
+	}
+}
diff --git a/substrate/frame/support/src/traits/tokens/fungible.rs b/substrate/frame/support/src/traits/tokens/fungible.rs
index 7b1ec0f4343..05e109b870e 100644
--- a/substrate/frame/support/src/traits/tokens/fungible.rs
+++ b/substrate/frame/support/src/traits/tokens/fungible.rs
@@ -83,7 +83,7 @@ pub trait Mutate<AccountId>: Inspect<AccountId> {
 	/// is returned and nothing is changed. If successful, the amount of tokens reduced is returned.
 	///
 	/// The default implementation just uses `withdraw` along with `reducible_balance` to ensure
-	/// that is doesn't fail.
+	/// that it doesn't fail.
 	fn slash(who: &AccountId, amount: Self::Balance) -> Result<Self::Balance, DispatchError> {
 		Self::burn_from(who, Self::reducible_balance(who, false).min(amount))
 	}
diff --git a/substrate/primitives/arithmetic/src/lib.rs b/substrate/primitives/arithmetic/src/lib.rs
index 244242c0f75..d7b326164b7 100644
--- a/substrate/primitives/arithmetic/src/lib.rs
+++ b/substrate/primitives/arithmetic/src/lib.rs
@@ -42,8 +42,8 @@ pub mod traits;
 
 pub use fixed_point::{FixedI128, FixedI64, FixedPointNumber, FixedPointOperand, FixedU128};
 pub use per_things::{
-	InnerOf, PerThing, PerU16, Perbill, Percent, Permill, Perquintill, Rounding, SignedRounding,
-	UpperOf,
+	InnerOf, MultiplyArg, PerThing, PerU16, Perbill, Percent, Permill, Perquintill, RationalArg,
+	ReciprocalArg, Rounding, SignedRounding, UpperOf,
 };
 pub use rational::{Rational128, RationalInfinite};
 
diff --git a/substrate/primitives/arithmetic/src/per_things.rs b/substrate/primitives/arithmetic/src/per_things.rs
index 2932a742063..fc376776117 100644
--- a/substrate/primitives/arithmetic/src/per_things.rs
+++ b/substrate/primitives/arithmetic/src/per_things.rs
@@ -36,6 +36,57 @@ pub type InnerOf<P> = <P as PerThing>::Inner;
 /// Get the upper type of a `PerThing`.
 pub type UpperOf<P> = <P as PerThing>::Upper;
 
+pub trait RationalArg:
+	Clone
+	+ Ord
+	+ ops::Div<Self, Output = Self>
+	+ ops::Rem<Self, Output = Self>
+	+ ops::Add<Self, Output = Self>
+	+ ops::AddAssign<Self>
+	+ Unsigned
+	+ Zero
+	+ One
+{
+}
+
+impl<
+		T: Clone
+			+ Ord
+			+ ops::Div<Self, Output = Self>
+			+ ops::Rem<Self, Output = Self>
+			+ ops::Add<Self, Output = Self>
+			+ ops::AddAssign<Self>
+			+ Unsigned
+			+ Zero
+			+ One,
+	> RationalArg for T
+{
+}
+
+pub trait MultiplyArg:
+	Clone
+	+ ops::Rem<Self, Output = Self>
+	+ ops::Div<Self, Output = Self>
+	+ ops::Mul<Self, Output = Self>
+	+ ops::Add<Self, Output = Self>
+	+ Unsigned
+{
+}
+
+impl<
+		T: Clone
+			+ ops::Rem<Self, Output = Self>
+			+ ops::Div<Self, Output = Self>
+			+ ops::Mul<Self, Output = Self>
+			+ ops::Add<Self, Output = Self>
+			+ Unsigned,
+	> MultiplyArg for T
+{
+}
+
+pub trait ReciprocalArg: MultiplyArg + Saturating {}
+impl<T: MultiplyArg + Saturating> ReciprocalArg for T {}
+
 /// Something that implements a fixed point ration with an arbitrary granularity `X`, as _parts per
 /// `X`_.
 pub trait PerThing:
@@ -160,13 +211,7 @@ pub trait PerThing:
 	/// ```
 	fn mul_floor<N>(self, b: N) -> N
 	where
-		N: Clone
-			+ UniqueSaturatedInto<Self::Inner>
-			+ ops::Rem<N, Output = N>
-			+ ops::Div<N, Output = N>
-			+ ops::Mul<N, Output = N>
-			+ ops::Add<N, Output = N>
-			+ Unsigned,
+		N: MultiplyArg + UniqueSaturatedInto<Self::Inner>,
 		Self::Inner: Into<N>,
 	{
 		overflow_prune_mul::<N, Self>(b, self.deconstruct(), Rounding::Down)
@@ -189,13 +234,7 @@ pub trait PerThing:
 	/// ```
 	fn mul_ceil<N>(self, b: N) -> N
 	where
-		N: Clone
-			+ UniqueSaturatedInto<Self::Inner>
-			+ ops::Rem<N, Output = N>
-			+ ops::Div<N, Output = N>
-			+ ops::Mul<N, Output = N>
-			+ ops::Add<N, Output = N>
-			+ Unsigned,
+		N: MultiplyArg + UniqueSaturatedInto<Self::Inner>,
 		Self::Inner: Into<N>,
 	{
 		overflow_prune_mul::<N, Self>(b, self.deconstruct(), Rounding::Up)
@@ -212,14 +251,7 @@ pub trait PerThing:
 	/// ```
 	fn saturating_reciprocal_mul<N>(self, b: N) -> N
 	where
-		N: Clone
-			+ UniqueSaturatedInto<Self::Inner>
-			+ ops::Rem<N, Output = N>
-			+ ops::Div<N, Output = N>
-			+ ops::Mul<N, Output = N>
-			+ ops::Add<N, Output = N>
-			+ Saturating
-			+ Unsigned,
+		N: ReciprocalArg + UniqueSaturatedInto<Self::Inner>,
 		Self::Inner: Into<N>,
 	{
 		saturating_reciprocal_mul::<N, Self>(b, self.deconstruct(), Rounding::NearestPrefUp)
@@ -239,14 +271,7 @@ pub trait PerThing:
 	/// ```
 	fn saturating_reciprocal_mul_floor<N>(self, b: N) -> N
 	where
-		N: Clone
-			+ UniqueSaturatedInto<Self::Inner>
-			+ ops::Rem<N, Output = N>
-			+ ops::Div<N, Output = N>
-			+ ops::Mul<N, Output = N>
-			+ ops::Add<N, Output = N>
-			+ Saturating
-			+ Unsigned,
+		N: ReciprocalArg + UniqueSaturatedInto<Self::Inner>,
 		Self::Inner: Into<N>,
 	{
 		saturating_reciprocal_mul::<N, Self>(b, self.deconstruct(), Rounding::Down)
@@ -266,14 +291,7 @@ pub trait PerThing:
 	/// ```
 	fn saturating_reciprocal_mul_ceil<N>(self, b: N) -> N
 	where
-		N: Clone
-			+ UniqueSaturatedInto<Self::Inner>
-			+ ops::Rem<N, Output = N>
-			+ ops::Div<N, Output = N>
-			+ ops::Mul<N, Output = N>
-			+ ops::Add<N, Output = N>
-			+ Saturating
-			+ Unsigned,
+		N: ReciprocalArg + UniqueSaturatedInto<Self::Inner>,
 		Self::Inner: Into<N>,
 	{
 		saturating_reciprocal_mul::<N, Self>(b, self.deconstruct(), Rounding::Up)
@@ -316,17 +334,7 @@ pub trait PerThing:
 	/// ```
 	fn from_rational<N>(p: N, q: N) -> Self
 	where
-		N: Clone
-			+ Ord
-			+ TryInto<Self::Inner>
-			+ TryInto<Self::Upper>
-			+ ops::Div<N, Output = N>
-			+ ops::Rem<N, Output = N>
-			+ ops::Add<N, Output = N>
-			+ ops::AddAssign<N>
-			+ Unsigned
-			+ Zero
-			+ One,
+		N: RationalArg + TryInto<Self::Inner> + TryInto<Self::Upper>,
 		Self::Inner: Into<N>,
 	{
 		Self::from_rational_with_rounding(p, q, Rounding::Down).unwrap_or_else(|_| Self::one())
@@ -388,34 +396,14 @@ pub trait PerThing:
 	/// ```
 	fn from_rational_with_rounding<N>(p: N, q: N, rounding: Rounding) -> Result<Self, ()>
 	where
-		N: Clone
-			+ Ord
-			+ TryInto<Self::Inner>
-			+ TryInto<Self::Upper>
-			+ ops::Div<N, Output = N>
-			+ ops::Rem<N, Output = N>
-			+ ops::Add<N, Output = N>
-			+ ops::AddAssign<N>
-			+ Unsigned
-			+ Zero
-			+ One,
+		N: RationalArg + TryInto<Self::Inner> + TryInto<Self::Upper>,
 		Self::Inner: Into<N>;
 
 	/// Same as `Self::from_rational`.
 	#[deprecated = "Use from_rational instead"]
 	fn from_rational_approximation<N>(p: N, q: N) -> Self
 	where
-		N: Clone
-			+ Ord
-			+ TryInto<Self::Inner>
-			+ TryInto<Self::Upper>
-			+ ops::Div<N, Output = N>
-			+ ops::Rem<N, Output = N>
-			+ ops::Add<N, Output = N>
-			+ ops::AddAssign<N>
-			+ Unsigned
-			+ Zero
-			+ One,
+		N: RationalArg + TryInto<Self::Inner> + TryInto<Self::Upper>,
 		Self::Inner: Into<N>,
 	{
 		Self::from_rational(p, q)
@@ -495,13 +483,7 @@ where
 /// Overflow-prune multiplication. Accurately multiply a value by `self` without overflowing.
 fn overflow_prune_mul<N, P>(x: N, part: P::Inner, rounding: Rounding) -> N
 where
-	N: Clone
-		+ UniqueSaturatedInto<P::Inner>
-		+ ops::Div<N, Output = N>
-		+ ops::Mul<N, Output = N>
-		+ ops::Add<N, Output = N>
-		+ ops::Rem<N, Output = N>
-		+ Unsigned,
+	N: MultiplyArg + UniqueSaturatedInto<P::Inner>,
 	P: PerThing,
 	P::Inner: Into<N>,
 {
@@ -517,12 +499,7 @@ where
 /// to `x / denom * numer` for an accurate result.
 fn rational_mul_correction<N, P>(x: N, numer: P::Inner, denom: P::Inner, rounding: Rounding) -> N
 where
-	N: UniqueSaturatedInto<P::Inner>
-		+ ops::Div<N, Output = N>
-		+ ops::Mul<N, Output = N>
-		+ ops::Add<N, Output = N>
-		+ ops::Rem<N, Output = N>
-		+ Unsigned,
+	N: MultiplyArg + UniqueSaturatedInto<P::Inner>,
 	P: PerThing,
 	P::Inner: Into<N>,
 {
@@ -803,17 +780,7 @@ macro_rules! implement_per_thing {
 			#[deprecated = "Use `PerThing::from_rational` instead"]
 			pub fn from_rational_approximation<N>(p: N, q: N) -> Self
 			where
-				N: Clone
-					+ Ord
-					+ TryInto<$type>
-					+ TryInto<$upper_type>
-					+ ops::Div<N, Output = N>
-					+ ops::Rem<N, Output = N>
-					+ ops::Add<N, Output = N>
-					+ ops::AddAssign<N>
-					+ Unsigned
-					+ Zero
-					+ One,
+				N: RationalArg+ TryInto<$type> + TryInto<$upper_type>,
 				$type: Into<N>
 			{
 				<Self as PerThing>::from_rational(p, q)
@@ -822,17 +789,7 @@ macro_rules! implement_per_thing {
 			/// See [`PerThing::from_rational`].
 			pub fn from_rational<N>(p: N, q: N) -> Self
 			where
-				N: Clone
-					+ Ord
-					+ TryInto<$type>
-					+ TryInto<$upper_type>
-					+ ops::Div<N, Output = N>
-					+ ops::Rem<N, Output = N>
-					+ ops::Add<N, Output = N>
-					+ ops::AddAssign<N>
-					+ Unsigned
-					+ Zero
-					+ One,
+				N: RationalArg+ TryInto<$type> + TryInto<$upper_type>,
 				$type: Into<N>
 			{
 				<Self as PerThing>::from_rational(p, q)
@@ -851,9 +808,7 @@ macro_rules! implement_per_thing {
 			/// See [`PerThing::mul_floor`].
 			pub fn mul_floor<N>(self, b: N) -> N
 				where
-					N: Clone + UniqueSaturatedInto<$type> +
-						ops::Rem<N, Output=N> + ops::Div<N, Output=N> + ops::Mul<N, Output=N> +
-						ops::Add<N, Output=N> + Unsigned,
+					N: MultiplyArg + UniqueSaturatedInto<$type>,
 					$type: Into<N>,
 
 			{
@@ -863,9 +818,7 @@ macro_rules! implement_per_thing {
 			/// See [`PerThing::mul_ceil`].
 			pub fn mul_ceil<N>(self, b: N) -> N
 				where
-					N: Clone + UniqueSaturatedInto<$type> +
-						ops::Rem<N, Output=N> + ops::Div<N, Output=N> + ops::Mul<N, Output=N> +
-						ops::Add<N, Output=N> + Unsigned,
+					N: MultiplyArg + UniqueSaturatedInto<$type>,
 					$type: Into<N>,
 			{
 				PerThing::mul_ceil(self, b)
@@ -874,9 +827,7 @@ macro_rules! implement_per_thing {
 			/// See [`PerThing::saturating_reciprocal_mul`].
 			pub fn saturating_reciprocal_mul<N>(self, b: N) -> N
 				where
-					N: Clone + UniqueSaturatedInto<$type> + ops::Rem<N, Output=N> +
-						ops::Div<N, Output=N> + ops::Mul<N, Output=N> + ops::Add<N, Output=N> +
-						Saturating + Unsigned,
+					N: ReciprocalArg + UniqueSaturatedInto<$type>,
 					$type: Into<N>,
 			{
 				PerThing::saturating_reciprocal_mul(self, b)
@@ -885,9 +836,7 @@ macro_rules! implement_per_thing {
 			/// See [`PerThing::saturating_reciprocal_mul_floor`].
 			pub fn saturating_reciprocal_mul_floor<N>(self, b: N) -> N
 				where
-					N: Clone + UniqueSaturatedInto<$type> + ops::Rem<N, Output=N> +
-						ops::Div<N, Output=N> + ops::Mul<N, Output=N> + ops::Add<N, Output=N> +
-						Saturating + Unsigned,
+					N: ReciprocalArg + UniqueSaturatedInto<$type>,
 					$type: Into<N>,
 			{
 				PerThing::saturating_reciprocal_mul_floor(self, b)
@@ -896,9 +845,7 @@ macro_rules! implement_per_thing {
 			/// See [`PerThing::saturating_reciprocal_mul_ceil`].
 			pub fn saturating_reciprocal_mul_ceil<N>(self, b: N) -> N
 				where
-					N: Clone + UniqueSaturatedInto<$type> + ops::Rem<N, Output=N> +
-						ops::Div<N, Output=N> + ops::Mul<N, Output=N> + ops::Add<N, Output=N> +
-						Saturating + Unsigned,
+					N: ReciprocalArg + UniqueSaturatedInto<$type>,
 					$type: Into<N>,
 			{
 				PerThing::saturating_reciprocal_mul_ceil(self, b)
@@ -1133,6 +1080,11 @@ macro_rules! implement_per_thing {
 			}
 		}
 
+		impl $crate::traits::One for $name {
+			fn one() -> Self {
+				Self::one()
+			}
+		}
 
 		#[cfg(test)]
 		mod $test_mod {
diff --git a/substrate/primitives/runtime/src/traits.rs b/substrate/primitives/runtime/src/traits.rs
index 1c48b193343..276a62349a1 100644
--- a/substrate/primitives/runtime/src/traits.rs
+++ b/substrate/primitives/runtime/src/traits.rs
@@ -515,6 +515,11 @@ impl<T> Convert<T, T> for Identity {
 		a
 	}
 }
+impl<T> ConvertBack<T, T> for Identity {
+	fn convert_back(a: T) -> T {
+		a
+	}
+}
 
 /// A structure that performs standard conversion using the standard Rust conversion traits.
 pub struct ConvertInto;
@@ -524,6 +529,12 @@ impl<A, B: From<A>> Convert<A, B> for ConvertInto {
 	}
 }
 
+/// Extensible conversion trait. Generic over both source and destination types.
+pub trait ConvertBack<A, B>: Convert<A, B> {
+	/// Make conversion back.
+	fn convert_back(b: B) -> A;
+}
+
 /// Convenience type to work around the highly unergonomic syntax needed
 /// to invoke the functions of overloaded generic traits, in this case
 /// `TryFrom` and `TryInto`.
-- 
GitLab